kimiflare 0.46.0 → 0.47.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 +1585 -1499
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1928,6 +1928,447 @@ var init_code_mode = __esm({
|
|
|
1928
1928
|
}
|
|
1929
1929
|
});
|
|
1930
1930
|
|
|
1931
|
+
// src/agent/session-state.ts
|
|
1932
|
+
function emptySessionState(task = "") {
|
|
1933
|
+
return {
|
|
1934
|
+
task,
|
|
1935
|
+
user_constraints: [],
|
|
1936
|
+
repo_facts: [],
|
|
1937
|
+
files_touched: [],
|
|
1938
|
+
files_modified: [],
|
|
1939
|
+
confirmed_findings: [],
|
|
1940
|
+
open_questions: [],
|
|
1941
|
+
recent_failures: [],
|
|
1942
|
+
decisions: [],
|
|
1943
|
+
next_actions: [],
|
|
1944
|
+
artifact_index: {}
|
|
1945
|
+
};
|
|
1946
|
+
}
|
|
1947
|
+
function serializeArtifactStore(store) {
|
|
1948
|
+
const MAX_ARTIFACT_CHARS = 5e4;
|
|
1949
|
+
const out = [];
|
|
1950
|
+
for (const a of store.list()) {
|
|
1951
|
+
out.push({
|
|
1952
|
+
id: a.id,
|
|
1953
|
+
type: a.type,
|
|
1954
|
+
summary: a.summary,
|
|
1955
|
+
raw: a.raw.slice(0, MAX_ARTIFACT_CHARS),
|
|
1956
|
+
source: a.source,
|
|
1957
|
+
path: a.path,
|
|
1958
|
+
lineRange: a.lineRange,
|
|
1959
|
+
ts: a.ts
|
|
1960
|
+
});
|
|
1961
|
+
}
|
|
1962
|
+
return out;
|
|
1963
|
+
}
|
|
1964
|
+
function deserializeArtifactStore(data) {
|
|
1965
|
+
const store = new ArtifactStore();
|
|
1966
|
+
for (const a of data) {
|
|
1967
|
+
store.add({
|
|
1968
|
+
id: a.id,
|
|
1969
|
+
type: a.type,
|
|
1970
|
+
summary: a.summary,
|
|
1971
|
+
raw: a.raw,
|
|
1972
|
+
source: a.source,
|
|
1973
|
+
path: a.path,
|
|
1974
|
+
lineRange: a.lineRange,
|
|
1975
|
+
ts: a.ts
|
|
1976
|
+
});
|
|
1977
|
+
}
|
|
1978
|
+
return store;
|
|
1979
|
+
}
|
|
1980
|
+
function formatRecalledArtifacts(recalled) {
|
|
1981
|
+
if (recalled.length === 0) return "";
|
|
1982
|
+
const lines = ["[recalled artifacts]"];
|
|
1983
|
+
for (const { id, artifact } of recalled) {
|
|
1984
|
+
lines.push(`--- artifact:${id} (${artifact.type} from ${artifact.source}) ---`);
|
|
1985
|
+
lines.push(artifact.raw);
|
|
1986
|
+
}
|
|
1987
|
+
return lines.join("\n");
|
|
1988
|
+
}
|
|
1989
|
+
function serializeSessionState(state) {
|
|
1990
|
+
const lines = [];
|
|
1991
|
+
lines.push(`task: ${state.task || "(none)"}`);
|
|
1992
|
+
if (state.user_constraints.length) lines.push(`constraints:
|
|
1993
|
+
${state.user_constraints.map((c) => " - " + c).join("\n")}`);
|
|
1994
|
+
if (state.repo_facts.length) lines.push(`repo_facts:
|
|
1995
|
+
${state.repo_facts.map((f) => " - " + f).join("\n")}`);
|
|
1996
|
+
if (state.files_touched.length) lines.push(`files_touched: ${state.files_touched.join(", ")}`);
|
|
1997
|
+
if (state.files_modified.length) lines.push(`files_modified: ${state.files_modified.join(", ")}`);
|
|
1998
|
+
if (state.confirmed_findings.length) lines.push(`findings:
|
|
1999
|
+
${state.confirmed_findings.map((f) => " - " + f).join("\n")}`);
|
|
2000
|
+
if (state.open_questions.length) lines.push(`open_questions:
|
|
2001
|
+
${state.open_questions.map((q) => " - " + q).join("\n")}`);
|
|
2002
|
+
if (state.recent_failures.length) lines.push(`recent_failures:
|
|
2003
|
+
${state.recent_failures.map((f) => " - " + f).join("\n")}`);
|
|
2004
|
+
if (state.decisions.length) lines.push(`decisions:
|
|
2005
|
+
${state.decisions.map((d) => " - " + d).join("\n")}`);
|
|
2006
|
+
if (state.next_actions.length) lines.push(`next_actions:
|
|
2007
|
+
${state.next_actions.map((a) => " - " + a).join("\n")}`);
|
|
2008
|
+
if (Object.keys(state.artifact_index).length) {
|
|
2009
|
+
lines.push("artifact_index:");
|
|
2010
|
+
for (const [id, meta] of Object.entries(state.artifact_index)) {
|
|
2011
|
+
lines.push(` ${id}: [${meta.type}] ${meta.summary}`);
|
|
2012
|
+
}
|
|
2013
|
+
}
|
|
2014
|
+
return lines.join("\n");
|
|
2015
|
+
}
|
|
2016
|
+
function buildSessionStateMessage(state) {
|
|
2017
|
+
return {
|
|
2018
|
+
role: "system",
|
|
2019
|
+
content: `[compiled session state]
|
|
2020
|
+
${serializeSessionState(state)}`
|
|
2021
|
+
};
|
|
2022
|
+
}
|
|
2023
|
+
var ArtifactStore;
|
|
2024
|
+
var init_session_state = __esm({
|
|
2025
|
+
"src/agent/session-state.ts"() {
|
|
2026
|
+
"use strict";
|
|
2027
|
+
ArtifactStore = class {
|
|
2028
|
+
artifacts = /* @__PURE__ */ new Map();
|
|
2029
|
+
maxArtifacts;
|
|
2030
|
+
maxTotalChars;
|
|
2031
|
+
constructor(opts2) {
|
|
2032
|
+
this.maxArtifacts = opts2?.maxArtifacts ?? 200;
|
|
2033
|
+
this.maxTotalChars = opts2?.maxTotalChars ?? 5e5;
|
|
2034
|
+
}
|
|
2035
|
+
add(a) {
|
|
2036
|
+
while (this.totalChars() + a.raw.length > this.maxTotalChars && this.artifacts.size > 0) {
|
|
2037
|
+
this.evictOldest();
|
|
2038
|
+
}
|
|
2039
|
+
while (this.artifacts.size >= this.maxArtifacts) {
|
|
2040
|
+
this.evictOldest();
|
|
2041
|
+
}
|
|
2042
|
+
this.artifacts.set(a.id, a);
|
|
2043
|
+
}
|
|
2044
|
+
get(id) {
|
|
2045
|
+
return this.artifacts.get(id);
|
|
2046
|
+
}
|
|
2047
|
+
has(id) {
|
|
2048
|
+
return this.artifacts.has(id);
|
|
2049
|
+
}
|
|
2050
|
+
list() {
|
|
2051
|
+
return [...this.artifacts.values()].sort((a, b) => a.ts < b.ts ? -1 : 1);
|
|
2052
|
+
}
|
|
2053
|
+
recall(ids) {
|
|
2054
|
+
const out = [];
|
|
2055
|
+
for (const id of ids) {
|
|
2056
|
+
const a = this.artifacts.get(id);
|
|
2057
|
+
if (a) out.push({ id, artifact: a });
|
|
2058
|
+
}
|
|
2059
|
+
return out;
|
|
2060
|
+
}
|
|
2061
|
+
size() {
|
|
2062
|
+
return this.artifacts.size;
|
|
2063
|
+
}
|
|
2064
|
+
totalChars() {
|
|
2065
|
+
let sum = 0;
|
|
2066
|
+
for (const a of this.artifacts.values()) {
|
|
2067
|
+
sum += a.raw.length;
|
|
2068
|
+
}
|
|
2069
|
+
return sum;
|
|
2070
|
+
}
|
|
2071
|
+
evictOldest() {
|
|
2072
|
+
let oldest;
|
|
2073
|
+
for (const a of this.artifacts.values()) {
|
|
2074
|
+
if (!oldest || a.ts < oldest.ts) oldest = a;
|
|
2075
|
+
}
|
|
2076
|
+
if (oldest) this.artifacts.delete(oldest.id);
|
|
2077
|
+
}
|
|
2078
|
+
};
|
|
2079
|
+
}
|
|
2080
|
+
});
|
|
2081
|
+
|
|
2082
|
+
// src/agent/compaction.ts
|
|
2083
|
+
function approxTokens2(n) {
|
|
2084
|
+
return Math.round(n / 4);
|
|
2085
|
+
}
|
|
2086
|
+
function estimateMessageTokens(m) {
|
|
2087
|
+
let chars = 0;
|
|
2088
|
+
if (typeof m.content === "string") {
|
|
2089
|
+
chars = m.content.length;
|
|
2090
|
+
} else if (Array.isArray(m.content)) {
|
|
2091
|
+
chars = m.content.map((p) => p.type === "text" ? p.text.length : 0).reduce((a, b) => a + b, 0);
|
|
2092
|
+
}
|
|
2093
|
+
if (m.reasoning_content) chars += m.reasoning_content.length;
|
|
2094
|
+
if (m.tool_calls) {
|
|
2095
|
+
for (const tc of m.tool_calls) {
|
|
2096
|
+
chars += tc.function.name.length + tc.function.arguments.length;
|
|
2097
|
+
}
|
|
2098
|
+
}
|
|
2099
|
+
return approxTokens2(chars);
|
|
2100
|
+
}
|
|
2101
|
+
function estimatePromptTokens(messages) {
|
|
2102
|
+
return messages.reduce((sum, m) => sum + estimateMessageTokens(m), 0);
|
|
2103
|
+
}
|
|
2104
|
+
function groupIntoTurns(messages) {
|
|
2105
|
+
const prefix = [];
|
|
2106
|
+
let i = 0;
|
|
2107
|
+
while (i < messages.length && messages[i].role === "system") {
|
|
2108
|
+
prefix.push(messages[i]);
|
|
2109
|
+
i++;
|
|
2110
|
+
}
|
|
2111
|
+
const turns = [];
|
|
2112
|
+
while (i < messages.length) {
|
|
2113
|
+
if (messages[i].role !== "user") {
|
|
2114
|
+
i++;
|
|
2115
|
+
continue;
|
|
2116
|
+
}
|
|
2117
|
+
const user = messages[i];
|
|
2118
|
+
i++;
|
|
2119
|
+
if (i >= messages.length || messages[i].role !== "assistant") {
|
|
2120
|
+
continue;
|
|
2121
|
+
}
|
|
2122
|
+
const assistant = messages[i];
|
|
2123
|
+
i++;
|
|
2124
|
+
const tools = [];
|
|
2125
|
+
while (i < messages.length && messages[i].role === "tool") {
|
|
2126
|
+
tools.push(messages[i]);
|
|
2127
|
+
i++;
|
|
2128
|
+
}
|
|
2129
|
+
turns.push({ user, assistant, tools });
|
|
2130
|
+
}
|
|
2131
|
+
return { prefix, turns };
|
|
2132
|
+
}
|
|
2133
|
+
function makeArtifactId(type, index) {
|
|
2134
|
+
return `${type}_${Date.now()}_${index}`;
|
|
2135
|
+
}
|
|
2136
|
+
function extractArtifactsFromTurn(turn, startIndex, store) {
|
|
2137
|
+
const artifacts = [];
|
|
2138
|
+
const stateDelta = {
|
|
2139
|
+
files_touched: [],
|
|
2140
|
+
files_modified: [],
|
|
2141
|
+
confirmed_findings: [],
|
|
2142
|
+
recent_failures: [],
|
|
2143
|
+
decisions: [],
|
|
2144
|
+
next_actions: []
|
|
2145
|
+
};
|
|
2146
|
+
const toolCalls = turn.assistant.tool_calls ?? [];
|
|
2147
|
+
for (let ti = 0; ti < turn.tools.length; ti++) {
|
|
2148
|
+
const tm = turn.tools[ti];
|
|
2149
|
+
const tc = toolCalls[ti];
|
|
2150
|
+
const name = tm.name ?? tc?.function.name ?? "unknown";
|
|
2151
|
+
const content = typeof tm.content === "string" ? tm.content : "";
|
|
2152
|
+
let type = "tool_result";
|
|
2153
|
+
let summary = `${name} result`;
|
|
2154
|
+
let path;
|
|
2155
|
+
if (name === "read") {
|
|
2156
|
+
type = "read_slice";
|
|
2157
|
+
try {
|
|
2158
|
+
const args = tc ? JSON.parse(tc.function.arguments) : {};
|
|
2159
|
+
path = args.path;
|
|
2160
|
+
summary = `read ${path ?? "file"}`;
|
|
2161
|
+
if (path && !stateDelta.files_touched.includes(path)) stateDelta.files_touched.push(path);
|
|
2162
|
+
} catch {
|
|
2163
|
+
summary = "read file";
|
|
2164
|
+
}
|
|
2165
|
+
} else if (name === "bash") {
|
|
2166
|
+
type = "bash_log";
|
|
2167
|
+
try {
|
|
2168
|
+
const args = tc ? JSON.parse(tc.function.arguments) : {};
|
|
2169
|
+
const cmd = args.command ?? "";
|
|
2170
|
+
summary = `bash: ${cmd.slice(0, 60)}`;
|
|
2171
|
+
if (content.includes("Error") || content.includes("error") || content.includes("FAIL")) {
|
|
2172
|
+
stateDelta.recent_failures.push(`bash failed: ${cmd.slice(0, 80)}`);
|
|
2173
|
+
}
|
|
2174
|
+
} catch {
|
|
2175
|
+
summary = "bash command";
|
|
2176
|
+
}
|
|
2177
|
+
} else if (name === "grep") {
|
|
2178
|
+
type = "grep_result";
|
|
2179
|
+
summary = `grep results (${content.split("\n").length} lines)`;
|
|
2180
|
+
} else if (name === "web_fetch") {
|
|
2181
|
+
type = "web_fetch";
|
|
2182
|
+
try {
|
|
2183
|
+
const args = tc ? JSON.parse(tc.function.arguments) : {};
|
|
2184
|
+
summary = `web_fetch: ${args.url ?? "url"}`;
|
|
2185
|
+
} catch {
|
|
2186
|
+
summary = "web_fetch";
|
|
2187
|
+
}
|
|
2188
|
+
} else if (name === "write" || name === "edit") {
|
|
2189
|
+
try {
|
|
2190
|
+
const args = tc ? JSON.parse(tc.function.arguments) : {};
|
|
2191
|
+
path = args.path;
|
|
2192
|
+
if (path && !stateDelta.files_modified.includes(path)) stateDelta.files_modified.push(path);
|
|
2193
|
+
if (path && !stateDelta.files_touched.includes(path)) stateDelta.files_touched.push(path);
|
|
2194
|
+
} catch {
|
|
2195
|
+
}
|
|
2196
|
+
continue;
|
|
2197
|
+
} else if (name === "glob") {
|
|
2198
|
+
try {
|
|
2199
|
+
const args = tc ? JSON.parse(tc.function.arguments) : {};
|
|
2200
|
+
summary = `glob: ${args.pattern ?? ""}`;
|
|
2201
|
+
} catch {
|
|
2202
|
+
summary = "glob";
|
|
2203
|
+
}
|
|
2204
|
+
} else if (name === "tasks_set") {
|
|
2205
|
+
try {
|
|
2206
|
+
const args = tc ? JSON.parse(tc.function.arguments) : {};
|
|
2207
|
+
const tasks = args.tasks ?? [];
|
|
2208
|
+
const inProgress = tasks.filter((t) => t.status === "in_progress").map((t) => t.title);
|
|
2209
|
+
const pending = tasks.filter((t) => t.status === "pending").map((t) => t.title);
|
|
2210
|
+
if (inProgress.length) stateDelta.next_actions.push(...inProgress);
|
|
2211
|
+
if (pending.length) stateDelta.next_actions.push(...pending);
|
|
2212
|
+
summary = `tasks_set: ${tasks.length} tasks`;
|
|
2213
|
+
} catch {
|
|
2214
|
+
summary = "tasks_set";
|
|
2215
|
+
}
|
|
2216
|
+
}
|
|
2217
|
+
const maxRaw = 5e4;
|
|
2218
|
+
const raw = content.length > maxRaw ? content.slice(0, maxRaw) + `
|
|
2219
|
+
...[${content.length - maxRaw} chars truncated]` : content;
|
|
2220
|
+
const artifact = {
|
|
2221
|
+
id: makeArtifactId(type, startIndex + ti),
|
|
2222
|
+
type,
|
|
2223
|
+
summary,
|
|
2224
|
+
raw,
|
|
2225
|
+
source: name,
|
|
2226
|
+
path,
|
|
2227
|
+
ts: (/* @__PURE__ */ new Date()).toISOString()
|
|
2228
|
+
};
|
|
2229
|
+
artifacts.push(artifact);
|
|
2230
|
+
if (!content.includes("Error") && !content.includes("error") && content.length > 0 && content.length < 2e3) {
|
|
2231
|
+
stateDelta.confirmed_findings.push(`${name}: ${content.slice(0, 200)}`);
|
|
2232
|
+
}
|
|
2233
|
+
}
|
|
2234
|
+
const assistantText = typeof turn.assistant.content === "string" ? turn.assistant.content : "";
|
|
2235
|
+
if (assistantText.length > 0) {
|
|
2236
|
+
const decisionPatterns = [
|
|
2237
|
+
/(?:decided?|will|plan to|going to|should|need to)\s+(.{10,200})/gi,
|
|
2238
|
+
/(?:let's|let us)\s+(.{10,200})/gi
|
|
2239
|
+
];
|
|
2240
|
+
for (const pattern of decisionPatterns) {
|
|
2241
|
+
let match;
|
|
2242
|
+
while ((match = pattern.exec(assistantText)) !== null) {
|
|
2243
|
+
const decision = match[1].trim().replace(/\.$/, "");
|
|
2244
|
+
if (decision.length > 10 && !stateDelta.decisions.includes(decision)) {
|
|
2245
|
+
stateDelta.decisions.push(decision);
|
|
2246
|
+
}
|
|
2247
|
+
}
|
|
2248
|
+
}
|
|
2249
|
+
}
|
|
2250
|
+
return { artifacts, stateDelta };
|
|
2251
|
+
}
|
|
2252
|
+
function mergeState(state, delta) {
|
|
2253
|
+
const mergeArr = (a, b) => {
|
|
2254
|
+
if (!b) return a;
|
|
2255
|
+
const set = new Set(a);
|
|
2256
|
+
for (const item of b) set.add(item);
|
|
2257
|
+
return [...set];
|
|
2258
|
+
};
|
|
2259
|
+
return {
|
|
2260
|
+
...state,
|
|
2261
|
+
task: state.task || delta.task || "",
|
|
2262
|
+
user_constraints: mergeArr(state.user_constraints, delta.user_constraints),
|
|
2263
|
+
repo_facts: mergeArr(state.repo_facts, delta.repo_facts),
|
|
2264
|
+
files_touched: mergeArr(state.files_touched, delta.files_touched),
|
|
2265
|
+
files_modified: mergeArr(state.files_modified, delta.files_modified),
|
|
2266
|
+
confirmed_findings: mergeArr(state.confirmed_findings, delta.confirmed_findings),
|
|
2267
|
+
open_questions: mergeArr(state.open_questions, delta.open_questions),
|
|
2268
|
+
recent_failures: mergeArr(state.recent_failures, delta.recent_failures),
|
|
2269
|
+
decisions: mergeArr(state.decisions, delta.decisions),
|
|
2270
|
+
next_actions: mergeArr(state.next_actions, delta.next_actions),
|
|
2271
|
+
artifact_index: { ...state.artifact_index, ...delta.artifact_index }
|
|
2272
|
+
};
|
|
2273
|
+
}
|
|
2274
|
+
function shouldCompact(opts2) {
|
|
2275
|
+
const tokenThreshold = opts2.tokenThreshold ?? 8e4;
|
|
2276
|
+
const turnThreshold = opts2.turnThreshold ?? 12;
|
|
2277
|
+
const tokens = estimatePromptTokens(opts2.messages);
|
|
2278
|
+
const { turns } = groupIntoTurns(opts2.messages);
|
|
2279
|
+
return tokens > tokenThreshold || turns.length > turnThreshold;
|
|
2280
|
+
}
|
|
2281
|
+
function compactMessages(opts2) {
|
|
2282
|
+
const keepLastTurns = opts2.keepLastTurns ?? 4;
|
|
2283
|
+
const { prefix, turns } = groupIntoTurns(opts2.messages);
|
|
2284
|
+
const tokensBefore = estimatePromptTokens(opts2.messages);
|
|
2285
|
+
if (turns.length <= keepLastTurns) {
|
|
2286
|
+
return {
|
|
2287
|
+
newMessages: opts2.messages,
|
|
2288
|
+
newState: opts2.state,
|
|
2289
|
+
metrics: {
|
|
2290
|
+
estimatedTokensBefore: tokensBefore,
|
|
2291
|
+
estimatedTokensAfter: tokensBefore,
|
|
2292
|
+
archivedArtifacts: 0,
|
|
2293
|
+
recalledArtifacts: 0,
|
|
2294
|
+
rawTurnsRemoved: 0,
|
|
2295
|
+
rawTurnsKept: turns.length
|
|
2296
|
+
}
|
|
2297
|
+
};
|
|
2298
|
+
}
|
|
2299
|
+
const toCompact = turns.slice(0, turns.length - keepLastTurns);
|
|
2300
|
+
const toKeep = turns.slice(turns.length - keepLastTurns);
|
|
2301
|
+
let newState = { ...opts2.state };
|
|
2302
|
+
let archivedCount = 0;
|
|
2303
|
+
for (let i = 0; i < toCompact.length; i++) {
|
|
2304
|
+
const turn = toCompact[i];
|
|
2305
|
+
const { artifacts, stateDelta } = extractArtifactsFromTurn(turn, i, opts2.store);
|
|
2306
|
+
for (const artifact of artifacts) {
|
|
2307
|
+
opts2.store.add(artifact);
|
|
2308
|
+
archivedCount++;
|
|
2309
|
+
newState.artifact_index[artifact.id] = {
|
|
2310
|
+
type: artifact.type,
|
|
2311
|
+
summary: artifact.summary,
|
|
2312
|
+
source: artifact.source,
|
|
2313
|
+
path: artifact.path
|
|
2314
|
+
};
|
|
2315
|
+
}
|
|
2316
|
+
newState = mergeState(newState, stateDelta);
|
|
2317
|
+
if (!newState.task && typeof turn.user.content === "string") {
|
|
2318
|
+
newState.task = turn.user.content.slice(0, 200);
|
|
2319
|
+
}
|
|
2320
|
+
}
|
|
2321
|
+
const workingMemory = [];
|
|
2322
|
+
for (const turn of toKeep) {
|
|
2323
|
+
workingMemory.push(turn.user);
|
|
2324
|
+
workingMemory.push(turn.assistant);
|
|
2325
|
+
for (const tm of turn.tools) {
|
|
2326
|
+
workingMemory.push(tm);
|
|
2327
|
+
}
|
|
2328
|
+
}
|
|
2329
|
+
const stateMsg = buildSessionStateMessage(newState);
|
|
2330
|
+
const newMessages = [...prefix, stateMsg, ...workingMemory];
|
|
2331
|
+
const tokensAfter = estimatePromptTokens(newMessages);
|
|
2332
|
+
const metrics = {
|
|
2333
|
+
estimatedTokensBefore: tokensBefore,
|
|
2334
|
+
estimatedTokensAfter: tokensAfter,
|
|
2335
|
+
archivedArtifacts: archivedCount,
|
|
2336
|
+
recalledArtifacts: 0,
|
|
2337
|
+
rawTurnsRemoved: toCompact.length,
|
|
2338
|
+
rawTurnsKept: toKeep.length
|
|
2339
|
+
};
|
|
2340
|
+
return { newMessages, newState, metrics };
|
|
2341
|
+
}
|
|
2342
|
+
function recallArtifacts(messages, store, state) {
|
|
2343
|
+
const text = messages.map((m) => typeof m.content === "string" ? m.content : "").join(" ");
|
|
2344
|
+
const ids = [];
|
|
2345
|
+
for (const [id, meta] of Object.entries(state.artifact_index)) {
|
|
2346
|
+
if (meta.path && text.includes(meta.path)) {
|
|
2347
|
+
ids.push(id);
|
|
2348
|
+
}
|
|
2349
|
+
}
|
|
2350
|
+
for (const failure of state.recent_failures) {
|
|
2351
|
+
const keyword = failure.split(":")[0];
|
|
2352
|
+
if (!keyword) continue;
|
|
2353
|
+
const lowerKeyword = keyword.toLowerCase();
|
|
2354
|
+
if (!text.toLowerCase().includes(lowerKeyword)) continue;
|
|
2355
|
+
for (const [id, meta] of Object.entries(state.artifact_index)) {
|
|
2356
|
+
if (meta.source === "bash" && !ids.includes(id) && meta.summary.toLowerCase().includes(lowerKeyword)) {
|
|
2357
|
+
ids.push(id);
|
|
2358
|
+
}
|
|
2359
|
+
}
|
|
2360
|
+
}
|
|
2361
|
+
const uniqueIds = [...new Set(ids)].slice(0, 5);
|
|
2362
|
+
const recalled = store.recall(uniqueIds);
|
|
2363
|
+
return { ids: uniqueIds, recalled };
|
|
2364
|
+
}
|
|
2365
|
+
var init_compaction = __esm({
|
|
2366
|
+
"src/agent/compaction.ts"() {
|
|
2367
|
+
"use strict";
|
|
2368
|
+
init_session_state();
|
|
2369
|
+
}
|
|
2370
|
+
});
|
|
2371
|
+
|
|
1931
2372
|
// src/agent/loop.ts
|
|
1932
2373
|
function isHighSignalMemory(memory) {
|
|
1933
2374
|
return memory.topicKey === "project_dependencies" || memory.topicKey === "project_tsconfig" || memory.topicKey === "project_entry_point" || memory.category === "instruction" || memory.category === "preference" || memory.category === "event" && memory.importance >= 3;
|
|
@@ -2065,6 +2506,12 @@ Use console.log() to return results. Only console.log output will be sent back t
|
|
|
2065
2506
|
if (opts2.keepLastImageTurns !== void 0) {
|
|
2066
2507
|
apiMessages = stripOldImages(apiMessages, opts2.keepLastImageTurns);
|
|
2067
2508
|
}
|
|
2509
|
+
const promptTokens = estimatePromptTokens(apiMessages);
|
|
2510
|
+
if (promptTokens > MAX_PROMPT_TOKENS) {
|
|
2511
|
+
throw new Error(
|
|
2512
|
+
`kimiflare: context window exceeded (~${promptTokens.toLocaleString()} tokens). Run /compact to summarize older turns, or /clear to start fresh.`
|
|
2513
|
+
);
|
|
2514
|
+
}
|
|
2068
2515
|
logger.debug("turn:api_request", { sessionId: opts2.sessionId, messageCount: apiMessages.length });
|
|
2069
2516
|
const events = runKimi({
|
|
2070
2517
|
accountId: opts2.accountId,
|
|
@@ -2269,10 +2716,15 @@ Use console.log() to return results. Only console.log output will be sent back t
|
|
|
2269
2716
|
const warningPrefix = sandboxResult.warnings?.length ? `Warning: ${sandboxResult.warnings.join(" ")}
|
|
2270
2717
|
|
|
2271
2718
|
` : "";
|
|
2272
|
-
|
|
2719
|
+
let resultContent = sandboxResult.error ? `${warningPrefix}Error: ${sandboxResult.error}
|
|
2273
2720
|
|
|
2274
2721
|
Output:
|
|
2275
2722
|
${sandboxResult.output}` : `${warningPrefix}${sandboxResult.output}`;
|
|
2723
|
+
if (resultContent.length > MAX_TOOL_CONTENT_CHARS) {
|
|
2724
|
+
resultContent = resultContent.slice(0, MAX_TOOL_CONTENT_CHARS) + `
|
|
2725
|
+
|
|
2726
|
+
[truncated: ${resultContent.length - MAX_TOOL_CONTENT_CHARS} chars omitted]`;
|
|
2727
|
+
}
|
|
2276
2728
|
const result = {
|
|
2277
2729
|
tool_call_id: tc.id,
|
|
2278
2730
|
name: "execute_code",
|
|
@@ -2297,12 +2749,18 @@ ${sandboxResult.output}` : `${warningPrefix}${sandboxResult.output}`;
|
|
|
2297
2749
|
{ cwd: opts2.cwd, signal: opts2.signal, onTasks: opts2.callbacks.onTasks, coauthor: opts2.coauthor, memoryManager: opts2.memoryManager, sessionId: opts2.sessionId, githubToken: opts2.githubToken },
|
|
2298
2750
|
opts2.onFileChange
|
|
2299
2751
|
);
|
|
2752
|
+
let content2 = result.content;
|
|
2753
|
+
if (content2.length > MAX_TOOL_CONTENT_CHARS) {
|
|
2754
|
+
content2 = content2.slice(0, MAX_TOOL_CONTENT_CHARS) + `
|
|
2755
|
+
|
|
2756
|
+
[truncated: ${content2.length - MAX_TOOL_CONTENT_CHARS} chars omitted]`;
|
|
2757
|
+
}
|
|
2300
2758
|
logger.debug("turn:tool_end", { sessionId: opts2.sessionId, tool: tc.function.name, toolCallId: tc.id, ok: result.ok });
|
|
2301
2759
|
toolResults.push(result);
|
|
2302
2760
|
opts2.messages.push({
|
|
2303
2761
|
role: "tool",
|
|
2304
2762
|
tool_call_id: result.tool_call_id,
|
|
2305
|
-
content: sanitizeString(
|
|
2763
|
+
content: sanitizeString(content2),
|
|
2306
2764
|
name: result.name
|
|
2307
2765
|
});
|
|
2308
2766
|
opts2.callbacks.onToolResult?.(result);
|
|
@@ -2401,7 +2859,7 @@ function validateToolArguments(raw) {
|
|
|
2401
2859
|
return "{}";
|
|
2402
2860
|
}
|
|
2403
2861
|
}
|
|
2404
|
-
var BudgetExhaustedError, codeModeApiCache, driftAccumulator, DRIFT_THRESHOLD;
|
|
2862
|
+
var BudgetExhaustedError, codeModeApiCache, driftAccumulator, DRIFT_THRESHOLD, MAX_PROMPT_TOKENS, MAX_TOOL_CONTENT_CHARS;
|
|
2405
2863
|
var init_loop = __esm({
|
|
2406
2864
|
"src/agent/loop.ts"() {
|
|
2407
2865
|
"use strict";
|
|
@@ -2412,6 +2870,7 @@ var init_loop = __esm({
|
|
|
2412
2870
|
init_extractors();
|
|
2413
2871
|
init_strip_reasoning();
|
|
2414
2872
|
init_code_mode();
|
|
2873
|
+
init_compaction();
|
|
2415
2874
|
init_logger();
|
|
2416
2875
|
BudgetExhaustedError = class extends Error {
|
|
2417
2876
|
constructor(message2 = "Cumulative input token budget exhausted") {
|
|
@@ -2422,6 +2881,8 @@ var init_loop = __esm({
|
|
|
2422
2881
|
codeModeApiCache = /* @__PURE__ */ new Map();
|
|
2423
2882
|
driftAccumulator = /* @__PURE__ */ new Map();
|
|
2424
2883
|
DRIFT_THRESHOLD = 5;
|
|
2884
|
+
MAX_PROMPT_TOKENS = 24e4;
|
|
2885
|
+
MAX_TOOL_CONTENT_CHARS = 1e4;
|
|
2425
2886
|
}
|
|
2426
2887
|
});
|
|
2427
2888
|
|
|
@@ -3977,6 +4438,13 @@ function reduceToolOutput(toolName, raw, args, store, config = DEFAULT_REDUCER_C
|
|
|
3977
4438
|
hint = r.hint;
|
|
3978
4439
|
break;
|
|
3979
4440
|
}
|
|
4441
|
+
case "glob": {
|
|
4442
|
+
const r = reduceGlob(raw, config.glob);
|
|
4443
|
+
reduced = r.body;
|
|
4444
|
+
wasReduced = r.wasReduced;
|
|
4445
|
+
hint = r.hint;
|
|
4446
|
+
break;
|
|
4447
|
+
}
|
|
3980
4448
|
case "web_fetch": {
|
|
3981
4449
|
const r = reduceWebFetch(raw, args, config.webFetch);
|
|
3982
4450
|
reduced = r.body;
|
|
@@ -4022,9 +4490,13 @@ function reduceToolOutput(toolName, raw, args, store, config = DEFAULT_REDUCER_C
|
|
|
4022
4490
|
hint = r.hint;
|
|
4023
4491
|
break;
|
|
4024
4492
|
}
|
|
4025
|
-
default:
|
|
4026
|
-
|
|
4493
|
+
default: {
|
|
4494
|
+
const r = reduceDefault(raw, config.defaultMaxChars);
|
|
4495
|
+
reduced = r.body;
|
|
4496
|
+
wasReduced = r.wasReduced;
|
|
4497
|
+
hint = r.hint;
|
|
4027
4498
|
break;
|
|
4499
|
+
}
|
|
4028
4500
|
}
|
|
4029
4501
|
if (!wasReduced) {
|
|
4030
4502
|
return { content: reduced, rawBytes, reducedBytes: rawBytes, artifactId };
|
|
@@ -4326,6 +4798,34 @@ function reduceBrowser(raw, cfg) {
|
|
|
4326
4798
|
hint: "Browser page content truncated. Use a more specific selector or narrower URL scope."
|
|
4327
4799
|
};
|
|
4328
4800
|
}
|
|
4801
|
+
function reduceGlob(raw, cfg) {
|
|
4802
|
+
const lines = raw.split("\n").filter((l) => l.trim() !== "");
|
|
4803
|
+
if (lines.length <= cfg.maxFiles && raw.length <= cfg.maxOutputChars) {
|
|
4804
|
+
return { body: raw, wasReduced: false };
|
|
4805
|
+
}
|
|
4806
|
+
let result = raw;
|
|
4807
|
+
if (lines.length > cfg.maxFiles) {
|
|
4808
|
+
result = lines.slice(0, cfg.maxFiles).join("\n");
|
|
4809
|
+
}
|
|
4810
|
+
if (result.length > cfg.maxOutputChars) {
|
|
4811
|
+
result = result.slice(0, cfg.maxOutputChars);
|
|
4812
|
+
}
|
|
4813
|
+
return {
|
|
4814
|
+
body: result,
|
|
4815
|
+
wasReduced: true,
|
|
4816
|
+
hint: `Glob output truncated (${lines.length} files total). Use a more specific pattern (e.g., "src/**/*.ts") to narrow results.`
|
|
4817
|
+
};
|
|
4818
|
+
}
|
|
4819
|
+
function reduceDefault(raw, maxChars) {
|
|
4820
|
+
if (raw.length <= maxChars) {
|
|
4821
|
+
return { body: raw, wasReduced: false };
|
|
4822
|
+
}
|
|
4823
|
+
return {
|
|
4824
|
+
body: raw.slice(0, maxChars),
|
|
4825
|
+
wasReduced: true,
|
|
4826
|
+
hint: "Output truncated. Use a more specific query or call expand_artifact if you need the full content."
|
|
4827
|
+
};
|
|
4828
|
+
}
|
|
4329
4829
|
var DEFAULT_REDUCER_CONFIG;
|
|
4330
4830
|
var init_reducer = __esm({
|
|
4331
4831
|
"src/tools/reducer.ts"() {
|
|
@@ -4351,6 +4851,10 @@ var init_reducer = __esm({
|
|
|
4351
4851
|
maxOutputChars: 4e3,
|
|
4352
4852
|
dedupeConsecutiveLines: true
|
|
4353
4853
|
},
|
|
4854
|
+
glob: {
|
|
4855
|
+
maxFiles: 1e3,
|
|
4856
|
+
maxOutputChars: 2e4
|
|
4857
|
+
},
|
|
4354
4858
|
webFetch: {
|
|
4355
4859
|
maxChars: 2e3,
|
|
4356
4860
|
maxHeadingChars: 500
|
|
@@ -4368,7 +4872,8 @@ var init_reducer = __esm({
|
|
|
4368
4872
|
lsp: {
|
|
4369
4873
|
maxLines: 50,
|
|
4370
4874
|
maxOutputChars: 3e3
|
|
4371
|
-
}
|
|
4875
|
+
},
|
|
4876
|
+
defaultMaxChars: 1e4
|
|
4372
4877
|
};
|
|
4373
4878
|
}
|
|
4374
4879
|
});
|
|
@@ -4396,7 +4901,13 @@ function makeExpandArtifactTool(store) {
|
|
|
4396
4901
|
if (!raw) {
|
|
4397
4902
|
return `Artifact "${args.artifact_id}" not found. It may have been evicted from memory. Re-run the original tool to regenerate the output.`;
|
|
4398
4903
|
}
|
|
4399
|
-
|
|
4904
|
+
const MAX_EXPAND_CHARS = 2e4;
|
|
4905
|
+
if (raw.length <= MAX_EXPAND_CHARS) {
|
|
4906
|
+
return raw;
|
|
4907
|
+
}
|
|
4908
|
+
return raw.slice(0, MAX_EXPAND_CHARS) + `
|
|
4909
|
+
|
|
4910
|
+
[truncated: ${raw.length - MAX_EXPAND_CHARS} chars omitted. The artifact is very large; consider using a more specific tool query instead of expanding the full content.]`;
|
|
4400
4911
|
}
|
|
4401
4912
|
};
|
|
4402
4913
|
}
|
|
@@ -4672,1634 +5183,1193 @@ async function checkForUpdate(force = false) {
|
|
|
4672
5183
|
}
|
|
4673
5184
|
}
|
|
4674
5185
|
const latestVersion = await fetchLatestVersion();
|
|
4675
|
-
if (!latestVersion) {
|
|
4676
|
-
return { hasUpdate: false, localVersion, latestVersion: null };
|
|
4677
|
-
}
|
|
4678
|
-
const hasUpdate = isNewer(localVersion, latestVersion);
|
|
4679
|
-
await writeCache({ checkedAt: Date.now(), latestVersion });
|
|
4680
|
-
return { hasUpdate, localVersion, latestVersion };
|
|
4681
|
-
}
|
|
4682
|
-
var CACHE_TTL_MS, NPM_REGISTRY;
|
|
4683
|
-
var init_update_check = __esm({
|
|
4684
|
-
"src/util/update-check.ts"() {
|
|
4685
|
-
"use strict";
|
|
4686
|
-
init_version();
|
|
4687
|
-
CACHE_TTL_MS = 60 * 60 * 1e3;
|
|
4688
|
-
NPM_REGISTRY = "https://registry.npmjs.org/kimiflare/latest";
|
|
4689
|
-
}
|
|
4690
|
-
});
|
|
4691
|
-
|
|
4692
|
-
// src/remote/session-store.ts
|
|
4693
|
-
import { readFile as readFile8, writeFile as writeFile7, mkdir as mkdir7, readdir as readdir2 } from "fs/promises";
|
|
4694
|
-
import { homedir as homedir6 } from "os";
|
|
4695
|
-
import { join as join11 } from "path";
|
|
4696
|
-
function remoteDir() {
|
|
4697
|
-
const xdg = process.env.XDG_DATA_HOME || join11(homedir6(), ".config");
|
|
4698
|
-
return join11(xdg, "kimiflare", "remote");
|
|
4699
|
-
}
|
|
4700
|
-
async function saveRemoteSession(session) {
|
|
4701
|
-
const dir = remoteDir();
|
|
4702
|
-
await mkdir7(dir, { recursive: true });
|
|
4703
|
-
const path = join11(dir, `${session.sessionId}.json`);
|
|
4704
|
-
await writeFile7(path, JSON.stringify(session, null, 2) + "\n", "utf8");
|
|
4705
|
-
}
|
|
4706
|
-
async function loadRemoteSession(sessionId) {
|
|
4707
|
-
try {
|
|
4708
|
-
const path = join11(remoteDir(), `${sessionId}.json`);
|
|
4709
|
-
const raw = await readFile8(path, "utf8");
|
|
4710
|
-
return JSON.parse(raw);
|
|
4711
|
-
} catch {
|
|
4712
|
-
return null;
|
|
4713
|
-
}
|
|
4714
|
-
}
|
|
4715
|
-
async function listRemoteSessions() {
|
|
4716
|
-
const dir = remoteDir();
|
|
4717
|
-
try {
|
|
4718
|
-
const files = await readdir2(dir);
|
|
4719
|
-
const sessions = [];
|
|
4720
|
-
for (const file of files) {
|
|
4721
|
-
if (!file.endsWith(".json")) continue;
|
|
4722
|
-
try {
|
|
4723
|
-
const raw = await readFile8(join11(dir, file), "utf8");
|
|
4724
|
-
sessions.push(JSON.parse(raw));
|
|
4725
|
-
} catch {
|
|
4726
|
-
}
|
|
4727
|
-
}
|
|
4728
|
-
return sessions.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
|
|
4729
|
-
} catch {
|
|
4730
|
-
return [];
|
|
4731
|
-
}
|
|
4732
|
-
}
|
|
4733
|
-
async function getMostRecentRemoteSession() {
|
|
4734
|
-
const sessions = await listRemoteSessions();
|
|
4735
|
-
return sessions[0] ?? null;
|
|
4736
|
-
}
|
|
4737
|
-
var init_session_store = __esm({
|
|
4738
|
-
"src/remote/session-store.ts"() {
|
|
4739
|
-
"use strict";
|
|
4740
|
-
}
|
|
4741
|
-
});
|
|
4742
|
-
|
|
4743
|
-
// src/remote/deploy.ts
|
|
4744
|
-
import { execSync } from "child_process";
|
|
4745
|
-
import { join as join12, dirname as dirname7 } from "path";
|
|
4746
|
-
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
4747
|
-
import { randomBytes } from "crypto";
|
|
4748
|
-
function generateSecret() {
|
|
4749
|
-
return randomBytes(32).toString("hex");
|
|
4750
|
-
}
|
|
4751
|
-
function runCapture(cmd, cwd) {
|
|
4752
|
-
return execSync(cmd, { cwd, encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
4753
|
-
}
|
|
4754
|
-
async function* deployForTui() {
|
|
4755
|
-
yield { message: "Checking prerequisites..." };
|
|
4756
|
-
try {
|
|
4757
|
-
runCapture("wrangler --version");
|
|
4758
|
-
} catch {
|
|
4759
|
-
yield { message: "wrangler not found. Install: npm install -g wrangler", error: true };
|
|
4760
|
-
yield { message: "Then run: wrangler login", error: true };
|
|
4761
|
-
throw new Error("wrangler not installed");
|
|
4762
|
-
}
|
|
4763
|
-
yield { message: "wrangler OK" };
|
|
4764
|
-
try {
|
|
4765
|
-
runCapture("wrangler whoami");
|
|
4766
|
-
} catch {
|
|
4767
|
-
yield { message: "wrangler not authenticated. Run: wrangler login", error: true };
|
|
4768
|
-
throw new Error("wrangler not authenticated");
|
|
4769
|
-
}
|
|
4770
|
-
yield { message: "wrangler authenticated" };
|
|
4771
|
-
try {
|
|
4772
|
-
runCapture("docker --version");
|
|
4773
|
-
} catch {
|
|
4774
|
-
yield { message: "Docker not found. Install: https://docs.docker.com/get-docker/", error: true };
|
|
4775
|
-
throw new Error("docker not installed");
|
|
4776
|
-
}
|
|
4777
|
-
yield { message: "Docker OK" };
|
|
4778
|
-
yield { message: "Building remote agent bundle..." };
|
|
4779
|
-
try {
|
|
4780
|
-
runCapture("npm run build:remote-agent", join12(REMOTE_DIR, ".."));
|
|
4781
|
-
yield { message: "Agent bundle built" };
|
|
4782
|
-
} catch (err) {
|
|
4783
|
-
yield { message: `Build failed: ${err instanceof Error ? err.message : String(err)}`, error: true };
|
|
4784
|
-
throw err;
|
|
4785
|
-
}
|
|
4786
|
-
yield { message: "Deploying Worker to Cloudflare..." };
|
|
4787
|
-
try {
|
|
4788
|
-
runCapture("wrangler deploy", WORKER_DIR);
|
|
4789
|
-
yield { message: "Worker deployed" };
|
|
4790
|
-
} catch (err) {
|
|
4791
|
-
yield { message: `Deploy failed: ${err instanceof Error ? err.message : String(err)}`, error: true };
|
|
4792
|
-
throw err;
|
|
4793
|
-
}
|
|
4794
|
-
let workerUrl;
|
|
4795
|
-
try {
|
|
4796
|
-
const info = runCapture("wrangler info", WORKER_DIR);
|
|
4797
|
-
const match = info.match(/https:\/\/[^\s]+\.workers\.dev/);
|
|
4798
|
-
if (match) workerUrl = match[0];
|
|
4799
|
-
} catch {
|
|
4800
|
-
}
|
|
4801
|
-
if (!workerUrl) {
|
|
4802
|
-
yield { message: "Could not auto-detect Worker URL", error: true };
|
|
4803
|
-
throw new Error("Worker URL not found");
|
|
4804
|
-
}
|
|
4805
|
-
yield { message: `Worker URL: ${workerUrl}` };
|
|
4806
|
-
const authSecret = generateSecret();
|
|
4807
|
-
const cfg = await loadConfig();
|
|
4808
|
-
const cfToken = process.env.CF_API_TOKEN ?? cfg?.apiToken;
|
|
4809
|
-
if (!cfToken) {
|
|
4810
|
-
yield { message: "CF_API_TOKEN not found. Set CF_API_TOKEN env var or apiToken in config", error: true };
|
|
4811
|
-
throw new Error("CF_API_TOKEN missing");
|
|
4812
|
-
}
|
|
4813
|
-
yield { message: "Setting Worker secrets..." };
|
|
4814
|
-
try {
|
|
4815
|
-
execSync(`wrangler secret put REMOTE_AUTH_SECRET`, {
|
|
4816
|
-
cwd: WORKER_DIR,
|
|
4817
|
-
input: authSecret,
|
|
4818
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
4819
|
-
});
|
|
4820
|
-
execSync(`wrangler secret put CF_API_TOKEN`, {
|
|
4821
|
-
cwd: WORKER_DIR,
|
|
4822
|
-
input: cfToken,
|
|
4823
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
4824
|
-
});
|
|
4825
|
-
yield { message: "Secrets set" };
|
|
4826
|
-
} catch (err) {
|
|
4827
|
-
yield { message: `Secret setup failed: ${err instanceof Error ? err.message : String(err)}`, error: true };
|
|
4828
|
-
throw err;
|
|
4829
|
-
}
|
|
4830
|
-
const imageTag = "ghcr.io/sinameraji/kimiflare-remote-agent:latest";
|
|
4831
|
-
yield { message: "Building container image..." };
|
|
4832
|
-
try {
|
|
4833
|
-
runCapture(`docker build -t ${imageTag} .`, REMOTE_DIR);
|
|
4834
|
-
yield { message: "Image built" };
|
|
4835
|
-
} catch (err) {
|
|
4836
|
-
yield { message: `Image build failed: ${err instanceof Error ? err.message : String(err)}`, error: true };
|
|
4837
|
-
throw err;
|
|
4838
|
-
}
|
|
4839
|
-
yield { message: `Pushing ${imageTag}...` };
|
|
4840
|
-
try {
|
|
4841
|
-
runCapture(`docker push ${imageTag}`, REMOTE_DIR);
|
|
4842
|
-
yield { message: "Image pushed" };
|
|
4843
|
-
} catch (err) {
|
|
4844
|
-
yield { message: `Push failed: ${err instanceof Error ? err.message : String(err)}`, error: true };
|
|
4845
|
-
yield { message: "Make sure you're logged into ghcr.io: docker login ghcr.io -u USERNAME -p GITHUB_TOKEN", error: true };
|
|
4846
|
-
throw err;
|
|
4847
|
-
}
|
|
4848
|
-
const nextCfg = {
|
|
4849
|
-
...cfg ?? { accountId: "", apiToken: "", model: "@cf/moonshotai/kimi-k2.6" },
|
|
4850
|
-
remoteWorkerUrl: workerUrl,
|
|
4851
|
-
remoteAuthSecret: authSecret
|
|
4852
|
-
};
|
|
4853
|
-
await saveConfig(nextCfg);
|
|
4854
|
-
yield { message: "Config saved" };
|
|
4855
|
-
yield { message: "Remote infrastructure ready!", done: true };
|
|
4856
|
-
return { workerUrl, authSecret };
|
|
4857
|
-
}
|
|
4858
|
-
async function runDeploy() {
|
|
4859
|
-
console.log("kimiflare remote deploy\n");
|
|
4860
|
-
try {
|
|
4861
|
-
for await (const step of deployForTui()) {
|
|
4862
|
-
console.log(step.message);
|
|
4863
|
-
if (step.done) break;
|
|
4864
|
-
if (step.error) process.exit(1);
|
|
4865
|
-
}
|
|
4866
|
-
console.log("\nDeploy complete!");
|
|
4867
|
-
} catch (err) {
|
|
4868
|
-
console.error(`Deploy failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
4869
|
-
process.exit(1);
|
|
4870
|
-
}
|
|
4871
|
-
}
|
|
4872
|
-
async function checkDeployStatus() {
|
|
4873
|
-
let wrangler = false;
|
|
4874
|
-
let wranglerAuth = false;
|
|
4875
|
-
let docker = false;
|
|
4876
|
-
let workerUrl;
|
|
4877
|
-
try {
|
|
4878
|
-
execSync("wrangler --version", { stdio: "pipe" });
|
|
4879
|
-
wrangler = true;
|
|
4880
|
-
} catch {
|
|
4881
|
-
}
|
|
4882
|
-
if (wrangler) {
|
|
4883
|
-
try {
|
|
4884
|
-
execSync("wrangler whoami", { stdio: "pipe" });
|
|
4885
|
-
wranglerAuth = true;
|
|
4886
|
-
} catch {
|
|
4887
|
-
}
|
|
4888
|
-
}
|
|
4889
|
-
try {
|
|
4890
|
-
execSync("docker --version", { stdio: "pipe" });
|
|
4891
|
-
docker = true;
|
|
4892
|
-
} catch {
|
|
4893
|
-
}
|
|
4894
|
-
const cfg = await loadConfig();
|
|
4895
|
-
if (cfg?.remoteWorkerUrl) {
|
|
4896
|
-
try {
|
|
4897
|
-
const res = await fetch(`${cfg.remoteWorkerUrl}/health`, { signal: AbortSignal.timeout(5e3) });
|
|
4898
|
-
if (res.ok) workerUrl = cfg.remoteWorkerUrl;
|
|
4899
|
-
} catch {
|
|
4900
|
-
}
|
|
5186
|
+
if (!latestVersion) {
|
|
5187
|
+
return { hasUpdate: false, localVersion, latestVersion: null };
|
|
4901
5188
|
}
|
|
4902
|
-
|
|
5189
|
+
const hasUpdate = isNewer(localVersion, latestVersion);
|
|
5190
|
+
await writeCache({ checkedAt: Date.now(), latestVersion });
|
|
5191
|
+
return { hasUpdate, localVersion, latestVersion };
|
|
4903
5192
|
}
|
|
4904
|
-
var
|
|
4905
|
-
var
|
|
4906
|
-
"src/
|
|
4907
|
-
"use strict";
|
|
4908
|
-
init_config();
|
|
4909
|
-
__dirname2 = dirname7(fileURLToPath3(import.meta.url));
|
|
4910
|
-
REMOTE_DIR = join12(__dirname2, "..", "..", "..", "remote");
|
|
4911
|
-
WORKER_DIR = join12(REMOTE_DIR, "worker");
|
|
4912
|
-
}
|
|
4913
|
-
});
|
|
4914
|
-
|
|
4915
|
-
// src/cost-attribution/types.ts
|
|
4916
|
-
var ALL_CATEGORIES;
|
|
4917
|
-
var init_types = __esm({
|
|
4918
|
-
"src/cost-attribution/types.ts"() {
|
|
5193
|
+
var CACHE_TTL_MS, NPM_REGISTRY;
|
|
5194
|
+
var init_update_check = __esm({
|
|
5195
|
+
"src/util/update-check.ts"() {
|
|
4919
5196
|
"use strict";
|
|
4920
|
-
|
|
4921
|
-
|
|
4922
|
-
|
|
4923
|
-
"reading-configuration",
|
|
4924
|
-
"reading-web-content",
|
|
4925
|
-
"reading-data",
|
|
4926
|
-
"reading-logs-output",
|
|
4927
|
-
"writing-source-code",
|
|
4928
|
-
"writing-documentation",
|
|
4929
|
-
"writing-configuration",
|
|
4930
|
-
"writing-tests",
|
|
4931
|
-
"editing-source-code",
|
|
4932
|
-
"editing-documentation",
|
|
4933
|
-
"editing-configuration",
|
|
4934
|
-
"running-tests",
|
|
4935
|
-
"running-git-commands",
|
|
4936
|
-
"running-build-scripts",
|
|
4937
|
-
"running-deploy-commands",
|
|
4938
|
-
"running-shell-commands",
|
|
4939
|
-
"searching-code",
|
|
4940
|
-
"searching-web",
|
|
4941
|
-
"exploring-codebase",
|
|
4942
|
-
"other"
|
|
4943
|
-
];
|
|
5197
|
+
init_version();
|
|
5198
|
+
CACHE_TTL_MS = 60 * 60 * 1e3;
|
|
5199
|
+
NPM_REGISTRY = "https://registry.npmjs.org/kimiflare/latest";
|
|
4944
5200
|
}
|
|
4945
5201
|
});
|
|
4946
5202
|
|
|
4947
|
-
// src/
|
|
4948
|
-
|
|
4949
|
-
|
|
4950
|
-
|
|
4951
|
-
|
|
4952
|
-
|
|
4953
|
-
|
|
4954
|
-
|
|
4955
|
-
|
|
4956
|
-
|
|
4957
|
-
|
|
5203
|
+
// src/remote/session-store.ts
|
|
5204
|
+
import { readFile as readFile8, writeFile as writeFile7, mkdir as mkdir7, readdir as readdir2 } from "fs/promises";
|
|
5205
|
+
import { homedir as homedir6 } from "os";
|
|
5206
|
+
import { join as join11 } from "path";
|
|
5207
|
+
function remoteDir() {
|
|
5208
|
+
const xdg = process.env.XDG_DATA_HOME || join11(homedir6(), ".config");
|
|
5209
|
+
return join11(xdg, "kimiflare", "remote");
|
|
5210
|
+
}
|
|
5211
|
+
async function saveRemoteSession(session) {
|
|
5212
|
+
const dir = remoteDir();
|
|
5213
|
+
await mkdir7(dir, { recursive: true });
|
|
5214
|
+
const path = join11(dir, `${session.sessionId}.json`);
|
|
5215
|
+
await writeFile7(path, JSON.stringify(session, null, 2) + "\n", "utf8");
|
|
5216
|
+
}
|
|
5217
|
+
async function loadRemoteSession(sessionId) {
|
|
5218
|
+
try {
|
|
5219
|
+
const path = join11(remoteDir(), `${sessionId}.json`);
|
|
5220
|
+
const raw = await readFile8(path, "utf8");
|
|
5221
|
+
return JSON.parse(raw);
|
|
5222
|
+
} catch {
|
|
5223
|
+
return null;
|
|
4958
5224
|
}
|
|
4959
|
-
return map;
|
|
4960
5225
|
}
|
|
4961
|
-
function
|
|
4962
|
-
const
|
|
4963
|
-
|
|
4964
|
-
|
|
4965
|
-
|
|
4966
|
-
const
|
|
4967
|
-
|
|
4968
|
-
|
|
4969
|
-
|
|
4970
|
-
|
|
4971
|
-
|
|
4972
|
-
|
|
4973
|
-
|
|
4974
|
-
|
|
4975
|
-
|
|
5226
|
+
async function listRemoteSessions() {
|
|
5227
|
+
const dir = remoteDir();
|
|
5228
|
+
try {
|
|
5229
|
+
const files = await readdir2(dir);
|
|
5230
|
+
const sessions = [];
|
|
5231
|
+
for (const file of files) {
|
|
5232
|
+
if (!file.endsWith(".json")) continue;
|
|
5233
|
+
try {
|
|
5234
|
+
const raw = await readFile8(join11(dir, file), "utf8");
|
|
5235
|
+
sessions.push(JSON.parse(raw));
|
|
5236
|
+
} catch {
|
|
5237
|
+
}
|
|
5238
|
+
}
|
|
5239
|
+
return sessions.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
|
|
5240
|
+
} catch {
|
|
5241
|
+
return [];
|
|
4976
5242
|
}
|
|
4977
|
-
categories.sort((a, b) => b.thisPeriod.cost - a.thisPeriod.cost);
|
|
4978
|
-
const topSessions = opts2.sessions.filter((s) => !opts2.categoryFilter || s.category === opts2.categoryFilter).sort((a, b) => b.cost - a.cost).slice(0, 5).map((s) => ({
|
|
4979
|
-
sessionId: s.id,
|
|
4980
|
-
date: s.date,
|
|
4981
|
-
cost: s.cost,
|
|
4982
|
-
category: s.category ?? "other",
|
|
4983
|
-
summary: s.summary,
|
|
4984
|
-
isCurrentSession: opts2.currentSessionId ? s.id === opts2.currentSessionId : void 0
|
|
4985
|
-
}));
|
|
4986
|
-
return {
|
|
4987
|
-
period: { start: opts2.startDate, end: opts2.endDate },
|
|
4988
|
-
categories,
|
|
4989
|
-
topSessions,
|
|
4990
|
-
reconciliation: opts2.reconciliation ?? { status: "local-only", localCost: 0 }
|
|
4991
|
-
};
|
|
4992
5243
|
}
|
|
4993
|
-
|
|
4994
|
-
|
|
5244
|
+
async function getMostRecentRemoteSession() {
|
|
5245
|
+
const sessions = await listRemoteSessions();
|
|
5246
|
+
return sessions[0] ?? null;
|
|
5247
|
+
}
|
|
5248
|
+
var init_session_store = __esm({
|
|
5249
|
+
"src/remote/session-store.ts"() {
|
|
4995
5250
|
"use strict";
|
|
4996
|
-
init_types();
|
|
4997
5251
|
}
|
|
4998
5252
|
});
|
|
4999
5253
|
|
|
5000
|
-
// src/
|
|
5001
|
-
|
|
5002
|
-
|
|
5003
|
-
}
|
|
5004
|
-
|
|
5005
|
-
|
|
5006
|
-
|
|
5007
|
-
return "\u2192";
|
|
5008
|
-
}
|
|
5009
|
-
function pad(str, width) {
|
|
5010
|
-
return str.padEnd(width).slice(0, width);
|
|
5011
|
-
}
|
|
5012
|
-
function padLeft(str, width) {
|
|
5013
|
-
return str.padStart(width).slice(-width);
|
|
5254
|
+
// src/remote/deploy.ts
|
|
5255
|
+
import { execSync } from "child_process";
|
|
5256
|
+
import { join as join12, dirname as dirname7 } from "path";
|
|
5257
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
5258
|
+
import { randomBytes } from "crypto";
|
|
5259
|
+
function generateSecret() {
|
|
5260
|
+
return randomBytes(32).toString("hex");
|
|
5014
5261
|
}
|
|
5015
|
-
function
|
|
5016
|
-
return
|
|
5262
|
+
function runCapture(cmd, cwd) {
|
|
5263
|
+
return execSync(cmd, { cwd, encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
5017
5264
|
}
|
|
5018
|
-
function
|
|
5019
|
-
|
|
5020
|
-
|
|
5021
|
-
|
|
5022
|
-
|
|
5023
|
-
|
|
5024
|
-
|
|
5025
|
-
|
|
5026
|
-
lines.push(
|
|
5027
|
-
`${pad("Category", catWidth)} ${padLeft("This period", numWidth)} ${padLeft("Last period", numWidth)} ${pad("", arrWidth)}`
|
|
5028
|
-
);
|
|
5029
|
-
lines.push("\u2500".repeat(totalWidth));
|
|
5030
|
-
let totalThis = 0;
|
|
5031
|
-
let totalLast = 0;
|
|
5032
|
-
for (const entry of report.categories) {
|
|
5033
|
-
totalThis += entry.thisPeriod.cost;
|
|
5034
|
-
totalLast += entry.lastPeriod.cost;
|
|
5035
|
-
const label = pad(categoryLabel(entry.category), catWidth);
|
|
5036
|
-
const thisStr = padLeft(fmtCost(entry.thisPeriod.cost), numWidth);
|
|
5037
|
-
const lastStr = padLeft(fmtCost(entry.lastPeriod.cost), numWidth);
|
|
5038
|
-
const arr = pad(arrow(entry.changePct), arrWidth);
|
|
5039
|
-
lines.push(`${label} ${thisStr} ${lastStr} ${arr}`);
|
|
5265
|
+
async function* deployForTui() {
|
|
5266
|
+
yield { message: "Checking prerequisites..." };
|
|
5267
|
+
try {
|
|
5268
|
+
runCapture("wrangler --version");
|
|
5269
|
+
} catch {
|
|
5270
|
+
yield { message: "wrangler not found. Install: npm install -g wrangler", error: true };
|
|
5271
|
+
yield { message: "Then run: wrangler login", error: true };
|
|
5272
|
+
throw new Error("wrangler not installed");
|
|
5040
5273
|
}
|
|
5041
|
-
|
|
5042
|
-
|
|
5043
|
-
|
|
5044
|
-
|
|
5045
|
-
|
|
5046
|
-
|
|
5047
|
-
lines.push("Top sessions this period:");
|
|
5048
|
-
for (const s of report.topSessions) {
|
|
5049
|
-
const day = new Date(s.date).toLocaleDateString("en-US", { weekday: "short" });
|
|
5050
|
-
const cat = s.category;
|
|
5051
|
-
const sum = s.summary ? ` \u2014 ${s.summary}` : "";
|
|
5052
|
-
const cur = s.isCurrentSession ? " (current)" : "";
|
|
5053
|
-
lines.push(` ${fmtCost(s.cost).padStart(6)} ${day} ${cat}${sum}${cur}`);
|
|
5054
|
-
}
|
|
5274
|
+
yield { message: "wrangler OK" };
|
|
5275
|
+
try {
|
|
5276
|
+
runCapture("wrangler whoami");
|
|
5277
|
+
} catch {
|
|
5278
|
+
yield { message: "wrangler not authenticated. Run: wrangler login", error: true };
|
|
5279
|
+
throw new Error("wrangler not authenticated");
|
|
5055
5280
|
}
|
|
5056
|
-
|
|
5057
|
-
|
|
5058
|
-
|
|
5059
|
-
|
|
5060
|
-
|
|
5061
|
-
|
|
5062
|
-
|
|
5063
|
-
|
|
5064
|
-
|
|
5065
|
-
|
|
5066
|
-
|
|
5067
|
-
|
|
5068
|
-
|
|
5069
|
-
|
|
5070
|
-
|
|
5281
|
+
yield { message: "wrangler authenticated" };
|
|
5282
|
+
try {
|
|
5283
|
+
runCapture("docker --version");
|
|
5284
|
+
} catch {
|
|
5285
|
+
yield { message: "Docker not found. Install: https://docs.docker.com/get-docker/", error: true };
|
|
5286
|
+
throw new Error("docker not installed");
|
|
5287
|
+
}
|
|
5288
|
+
yield { message: "Docker OK" };
|
|
5289
|
+
yield { message: "Building remote agent bundle..." };
|
|
5290
|
+
try {
|
|
5291
|
+
runCapture("npm run build:remote-agent", join12(REMOTE_DIR, ".."));
|
|
5292
|
+
yield { message: "Agent bundle built" };
|
|
5293
|
+
} catch (err) {
|
|
5294
|
+
yield { message: `Build failed: ${err instanceof Error ? err.message : String(err)}`, error: true };
|
|
5295
|
+
throw err;
|
|
5296
|
+
}
|
|
5297
|
+
yield { message: "Deploying Worker to Cloudflare..." };
|
|
5298
|
+
try {
|
|
5299
|
+
runCapture("wrangler deploy", WORKER_DIR);
|
|
5300
|
+
yield { message: "Worker deployed" };
|
|
5301
|
+
} catch (err) {
|
|
5302
|
+
yield { message: `Deploy failed: ${err instanceof Error ? err.message : String(err)}`, error: true };
|
|
5303
|
+
throw err;
|
|
5304
|
+
}
|
|
5305
|
+
let workerUrl;
|
|
5306
|
+
try {
|
|
5307
|
+
const info = runCapture("wrangler info", WORKER_DIR);
|
|
5308
|
+
const match = info.match(/https:\/\/[^\s]+\.workers\.dev/);
|
|
5309
|
+
if (match) workerUrl = match[0];
|
|
5310
|
+
} catch {
|
|
5071
5311
|
}
|
|
5072
|
-
|
|
5073
|
-
}
|
|
5074
|
-
|
|
5075
|
-
return JSON.stringify(report, null, 2);
|
|
5076
|
-
}
|
|
5077
|
-
var init_renderer = __esm({
|
|
5078
|
-
"src/cost-attribution/renderer.ts"() {
|
|
5079
|
-
"use strict";
|
|
5312
|
+
if (!workerUrl) {
|
|
5313
|
+
yield { message: "Could not auto-detect Worker URL", error: true };
|
|
5314
|
+
throw new Error("Worker URL not found");
|
|
5080
5315
|
}
|
|
5081
|
-
}
|
|
5082
|
-
|
|
5083
|
-
|
|
5084
|
-
|
|
5085
|
-
|
|
5086
|
-
}
|
|
5087
|
-
|
|
5088
|
-
if (!opts2.accountId || !opts2.apiToken) {
|
|
5089
|
-
return { status: "local-only", localCost: opts2.localCost, message: "Missing Cloudflare credentials" };
|
|
5316
|
+
yield { message: `Worker URL: ${workerUrl}` };
|
|
5317
|
+
const authSecret = generateSecret();
|
|
5318
|
+
const cfg = await loadConfig();
|
|
5319
|
+
const cfToken = process.env.CF_API_TOKEN ?? cfg?.apiToken;
|
|
5320
|
+
if (!cfToken) {
|
|
5321
|
+
yield { message: "CF_API_TOKEN not found. Set CF_API_TOKEN env var or apiToken in config", error: true };
|
|
5322
|
+
throw new Error("CF_API_TOKEN missing");
|
|
5090
5323
|
}
|
|
5091
|
-
|
|
5092
|
-
|
|
5093
|
-
|
|
5094
|
-
|
|
5324
|
+
yield { message: "Setting Worker secrets..." };
|
|
5325
|
+
try {
|
|
5326
|
+
execSync(`wrangler secret put REMOTE_AUTH_SECRET`, {
|
|
5327
|
+
cwd: WORKER_DIR,
|
|
5328
|
+
input: authSecret,
|
|
5329
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
5330
|
+
});
|
|
5331
|
+
execSync(`wrangler secret put CF_API_TOKEN`, {
|
|
5332
|
+
cwd: WORKER_DIR,
|
|
5333
|
+
input: cfToken,
|
|
5334
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
5335
|
+
});
|
|
5336
|
+
yield { message: "Secrets set" };
|
|
5337
|
+
} catch (err) {
|
|
5338
|
+
yield { message: `Secret setup failed: ${err instanceof Error ? err.message : String(err)}`, error: true };
|
|
5339
|
+
throw err;
|
|
5095
5340
|
}
|
|
5341
|
+
const imageTag = "ghcr.io/sinameraji/kimiflare-remote-agent:latest";
|
|
5342
|
+
yield { message: "Building container image..." };
|
|
5096
5343
|
try {
|
|
5097
|
-
|
|
5098
|
-
|
|
5099
|
-
localCost: opts2.localCost,
|
|
5100
|
-
message: "Cloudflare reconciliation not yet implemented"
|
|
5101
|
-
};
|
|
5102
|
-
cache.set(key, { result, expires: Date.now() + 60 * 60 * 1e3 });
|
|
5103
|
-
return result;
|
|
5344
|
+
runCapture(`docker build -t ${imageTag} .`, REMOTE_DIR);
|
|
5345
|
+
yield { message: "Image built" };
|
|
5104
5346
|
} catch (err) {
|
|
5105
|
-
|
|
5106
|
-
|
|
5347
|
+
yield { message: `Image build failed: ${err instanceof Error ? err.message : String(err)}`, error: true };
|
|
5348
|
+
throw err;
|
|
5107
5349
|
}
|
|
5108
|
-
}
|
|
5109
|
-
|
|
5110
|
-
|
|
5111
|
-
|
|
5112
|
-
|
|
5113
|
-
|
|
5350
|
+
yield { message: `Pushing ${imageTag}...` };
|
|
5351
|
+
try {
|
|
5352
|
+
runCapture(`docker push ${imageTag}`, REMOTE_DIR);
|
|
5353
|
+
yield { message: "Image pushed" };
|
|
5354
|
+
} catch (err) {
|
|
5355
|
+
yield { message: `Push failed: ${err instanceof Error ? err.message : String(err)}`, error: true };
|
|
5356
|
+
yield { message: "Make sure you're logged into ghcr.io: docker login ghcr.io -u USERNAME -p GITHUB_TOKEN", error: true };
|
|
5357
|
+
throw err;
|
|
5114
5358
|
}
|
|
5115
|
-
|
|
5116
|
-
|
|
5117
|
-
|
|
5118
|
-
|
|
5119
|
-
|
|
5120
|
-
|
|
5121
|
-
}
|
|
5122
|
-
|
|
5123
|
-
|
|
5124
|
-
return idx >= 0 ? path.slice(idx + 1) : path;
|
|
5125
|
-
}
|
|
5126
|
-
function classifyFile(path) {
|
|
5127
|
-
const base = basename2(path);
|
|
5128
|
-
const ext = extOf(path);
|
|
5129
|
-
if (TEST_PATTERNS.test(base)) return "writing-tests";
|
|
5130
|
-
if (base.toLowerCase().startsWith("readme")) return "reading-documentation";
|
|
5131
|
-
if (SOURCE_EXTS.has(ext)) return null;
|
|
5132
|
-
if (DOC_EXTS.has(ext)) return "reading-documentation";
|
|
5133
|
-
if (CONFIG_EXTS.has(ext)) return "reading-configuration";
|
|
5134
|
-
if (DATA_EXTS.has(ext)) return "reading-data";
|
|
5135
|
-
return null;
|
|
5136
|
-
}
|
|
5137
|
-
function classifyWriteFile(path) {
|
|
5138
|
-
const base = basename2(path);
|
|
5139
|
-
const ext = extOf(path);
|
|
5140
|
-
if (TEST_PATTERNS.test(base)) return "writing-tests";
|
|
5141
|
-
if (base.toLowerCase().startsWith("readme") || DOC_EXTS.has(ext)) return "writing-documentation";
|
|
5142
|
-
if (CONFIG_EXTS.has(ext)) return "writing-configuration";
|
|
5143
|
-
if (SOURCE_EXTS.has(ext)) return "writing-source-code";
|
|
5144
|
-
return null;
|
|
5145
|
-
}
|
|
5146
|
-
function classifyEditFile(path) {
|
|
5147
|
-
const base = basename2(path);
|
|
5148
|
-
const ext = extOf(path);
|
|
5149
|
-
if (base.toLowerCase().startsWith("readme") || DOC_EXTS.has(ext)) return "editing-documentation";
|
|
5150
|
-
if (CONFIG_EXTS.has(ext)) return "editing-configuration";
|
|
5151
|
-
if (SOURCE_EXTS.has(ext)) return "editing-source-code";
|
|
5152
|
-
return null;
|
|
5153
|
-
}
|
|
5154
|
-
function classifyBash(command) {
|
|
5155
|
-
if (TEST_COMMANDS.test(command)) return "running-tests";
|
|
5156
|
-
if (GIT_COMMANDS.test(command)) return "running-git-commands";
|
|
5157
|
-
if (BUILD_COMMANDS.test(command)) return "running-build-scripts";
|
|
5158
|
-
if (DEPLOY_COMMANDS.test(command)) return "running-deploy-commands";
|
|
5159
|
-
return "running-shell-commands";
|
|
5359
|
+
const nextCfg = {
|
|
5360
|
+
...cfg ?? { accountId: "", apiToken: "", model: "@cf/moonshotai/kimi-k2.6" },
|
|
5361
|
+
remoteWorkerUrl: workerUrl,
|
|
5362
|
+
remoteAuthSecret: authSecret
|
|
5363
|
+
};
|
|
5364
|
+
await saveConfig(nextCfg);
|
|
5365
|
+
yield { message: "Config saved" };
|
|
5366
|
+
yield { message: "Remote infrastructure ready!", done: true };
|
|
5367
|
+
return { workerUrl, authSecret };
|
|
5160
5368
|
}
|
|
5161
|
-
function
|
|
5162
|
-
|
|
5163
|
-
|
|
5164
|
-
|
|
5165
|
-
|
|
5166
|
-
|
|
5167
|
-
|
|
5168
|
-
const cat = classifyFile(path);
|
|
5169
|
-
if (cat) return { category: cat, confidence: 0.8 };
|
|
5170
|
-
if (SOURCE_EXTS.has(extOf(path))) return { category: "reading-source-code", confidence: 0.8 };
|
|
5171
|
-
return null;
|
|
5172
|
-
}
|
|
5173
|
-
case "create_file":
|
|
5174
|
-
case "write_file":
|
|
5175
|
-
case "write": {
|
|
5176
|
-
const cat = classifyWriteFile(path);
|
|
5177
|
-
if (cat) return { category: cat, confidence: 0.9 };
|
|
5178
|
-
return null;
|
|
5179
|
-
}
|
|
5180
|
-
case "str_replace":
|
|
5181
|
-
case "edit": {
|
|
5182
|
-
const cat = classifyEditFile(path);
|
|
5183
|
-
if (cat) return { category: cat, confidence: 0.85 };
|
|
5184
|
-
return null;
|
|
5185
|
-
}
|
|
5186
|
-
case "bash": {
|
|
5187
|
-
const cat = classifyBash(command);
|
|
5188
|
-
if (cat) return { category: cat, confidence: 0.9 };
|
|
5189
|
-
return null;
|
|
5190
|
-
}
|
|
5191
|
-
case "web_fetch":
|
|
5192
|
-
return { category: "reading-web-content", confidence: 0.85 };
|
|
5193
|
-
case "grep":
|
|
5194
|
-
case "glob": {
|
|
5195
|
-
const pattern = typeof args.pattern === "string" ? args.pattern : path;
|
|
5196
|
-
const isSource = SOURCE_EXTS.has(extOf(pattern)) || /\.(ts|js|py|go|rs)\b/.test(pattern);
|
|
5197
|
-
return { category: isSource ? "searching-code" : "searching-code", confidence: 0.75 };
|
|
5369
|
+
async function runDeploy() {
|
|
5370
|
+
console.log("kimiflare remote deploy\n");
|
|
5371
|
+
try {
|
|
5372
|
+
for await (const step of deployForTui()) {
|
|
5373
|
+
console.log(step.message);
|
|
5374
|
+
if (step.done) break;
|
|
5375
|
+
if (step.error) process.exit(1);
|
|
5198
5376
|
}
|
|
5199
|
-
|
|
5200
|
-
|
|
5201
|
-
|
|
5202
|
-
|
|
5377
|
+
console.log("\nDeploy complete!");
|
|
5378
|
+
} catch (err) {
|
|
5379
|
+
console.error(`Deploy failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
5380
|
+
process.exit(1);
|
|
5203
5381
|
}
|
|
5204
5382
|
}
|
|
5205
|
-
function
|
|
5206
|
-
|
|
5207
|
-
|
|
5208
|
-
|
|
5209
|
-
|
|
5210
|
-
|
|
5211
|
-
|
|
5212
|
-
|
|
5213
|
-
|
|
5214
|
-
|
|
5383
|
+
async function checkDeployStatus() {
|
|
5384
|
+
let wrangler = false;
|
|
5385
|
+
let wranglerAuth = false;
|
|
5386
|
+
let docker = false;
|
|
5387
|
+
let workerUrl;
|
|
5388
|
+
try {
|
|
5389
|
+
execSync("wrangler --version", { stdio: "pipe" });
|
|
5390
|
+
wrangler = true;
|
|
5391
|
+
} catch {
|
|
5392
|
+
}
|
|
5393
|
+
if (wrangler) {
|
|
5394
|
+
try {
|
|
5395
|
+
execSync("wrangler whoami", { stdio: "pipe" });
|
|
5396
|
+
wranglerAuth = true;
|
|
5397
|
+
} catch {
|
|
5215
5398
|
}
|
|
5216
5399
|
}
|
|
5217
|
-
|
|
5218
|
-
}
|
|
5219
|
-
|
|
5220
|
-
|
|
5221
|
-
|
|
5222
|
-
|
|
5223
|
-
|
|
5224
|
-
|
|
5400
|
+
try {
|
|
5401
|
+
execSync("docker --version", { stdio: "pipe" });
|
|
5402
|
+
docker = true;
|
|
5403
|
+
} catch {
|
|
5404
|
+
}
|
|
5405
|
+
const cfg = await loadConfig();
|
|
5406
|
+
if (cfg?.remoteWorkerUrl) {
|
|
5407
|
+
try {
|
|
5408
|
+
const res = await fetch(`${cfg.remoteWorkerUrl}/health`, { signal: AbortSignal.timeout(5e3) });
|
|
5409
|
+
if (res.ok) workerUrl = cfg.remoteWorkerUrl;
|
|
5410
|
+
} catch {
|
|
5225
5411
|
}
|
|
5226
5412
|
}
|
|
5227
|
-
|
|
5228
|
-
|
|
5229
|
-
|
|
5230
|
-
|
|
5413
|
+
return { wrangler, wranglerAuth, docker, workerUrl };
|
|
5414
|
+
}
|
|
5415
|
+
var __dirname2, REMOTE_DIR, WORKER_DIR;
|
|
5416
|
+
var init_deploy = __esm({
|
|
5417
|
+
"src/remote/deploy.ts"() {
|
|
5418
|
+
"use strict";
|
|
5419
|
+
init_config();
|
|
5420
|
+
__dirname2 = dirname7(fileURLToPath3(import.meta.url));
|
|
5421
|
+
REMOTE_DIR = join12(__dirname2, "..", "..", "..", "remote");
|
|
5422
|
+
WORKER_DIR = join12(REMOTE_DIR, "worker");
|
|
5423
|
+
}
|
|
5424
|
+
});
|
|
5425
|
+
|
|
5426
|
+
// src/cost-attribution/types.ts
|
|
5427
|
+
var ALL_CATEGORIES;
|
|
5428
|
+
var init_types = __esm({
|
|
5429
|
+
"src/cost-attribution/types.ts"() {
|
|
5430
|
+
"use strict";
|
|
5431
|
+
ALL_CATEGORIES = [
|
|
5432
|
+
"reading-source-code",
|
|
5433
|
+
"reading-documentation",
|
|
5434
|
+
"reading-configuration",
|
|
5435
|
+
"reading-web-content",
|
|
5436
|
+
"reading-data",
|
|
5437
|
+
"reading-logs-output",
|
|
5438
|
+
"writing-source-code",
|
|
5439
|
+
"writing-documentation",
|
|
5440
|
+
"writing-configuration",
|
|
5441
|
+
"writing-tests",
|
|
5442
|
+
"editing-source-code",
|
|
5443
|
+
"editing-documentation",
|
|
5444
|
+
"editing-configuration",
|
|
5445
|
+
"running-tests",
|
|
5446
|
+
"running-git-commands",
|
|
5447
|
+
"running-build-scripts",
|
|
5448
|
+
"running-deploy-commands",
|
|
5449
|
+
"running-shell-commands",
|
|
5450
|
+
"searching-code",
|
|
5451
|
+
"searching-web",
|
|
5452
|
+
"exploring-codebase",
|
|
5453
|
+
"other"
|
|
5454
|
+
];
|
|
5231
5455
|
}
|
|
5232
|
-
|
|
5233
|
-
|
|
5456
|
+
});
|
|
5457
|
+
|
|
5458
|
+
// src/cost-attribution/report.ts
|
|
5459
|
+
function aggregate(sessions, filter) {
|
|
5460
|
+
const map = /* @__PURE__ */ new Map();
|
|
5461
|
+
for (const s of sessions) {
|
|
5462
|
+
const cat = s.category ?? "other";
|
|
5463
|
+
if (filter && cat !== filter) continue;
|
|
5464
|
+
const entry = map.get(cat) ?? { cost: 0, tokens: 0, sessions: 0 };
|
|
5465
|
+
entry.cost += s.cost;
|
|
5466
|
+
entry.tokens += s.promptTokens + s.completionTokens;
|
|
5467
|
+
entry.sessions += 1;
|
|
5468
|
+
map.set(cat, entry);
|
|
5234
5469
|
}
|
|
5235
|
-
|
|
5236
|
-
|
|
5237
|
-
|
|
5238
|
-
|
|
5239
|
-
|
|
5240
|
-
|
|
5241
|
-
|
|
5242
|
-
|
|
5243
|
-
}
|
|
5470
|
+
return map;
|
|
5471
|
+
}
|
|
5472
|
+
function buildReport(opts2) {
|
|
5473
|
+
const thisMap = aggregate(opts2.sessions, opts2.categoryFilter);
|
|
5474
|
+
const prevMap = opts2.previousSessions ? aggregate(opts2.previousSessions, opts2.categoryFilter) : /* @__PURE__ */ new Map();
|
|
5475
|
+
const categories = [];
|
|
5476
|
+
for (const cat of ALL_CATEGORIES) {
|
|
5477
|
+
const thisPeriod = thisMap.get(cat) ?? { cost: 0, tokens: 0, sessions: 0 };
|
|
5478
|
+
const lastPeriod = prevMap.get(cat) ?? { cost: 0, tokens: 0, sessions: 0 };
|
|
5479
|
+
if (thisPeriod.sessions === 0 && lastPeriod.sessions === 0) continue;
|
|
5480
|
+
const changePct = lastPeriod.cost > 0 ? Math.round((thisPeriod.cost - lastPeriod.cost) / lastPeriod.cost * 1e3) / 10 : thisPeriod.cost > 0 ? 100 : 0;
|
|
5481
|
+
categories.push({
|
|
5482
|
+
category: cat,
|
|
5483
|
+
thisPeriod,
|
|
5484
|
+
lastPeriod,
|
|
5485
|
+
changePct
|
|
5486
|
+
});
|
|
5244
5487
|
}
|
|
5245
|
-
|
|
5488
|
+
categories.sort((a, b) => b.thisPeriod.cost - a.thisPeriod.cost);
|
|
5489
|
+
const topSessions = opts2.sessions.filter((s) => !opts2.categoryFilter || s.category === opts2.categoryFilter).sort((a, b) => b.cost - a.cost).slice(0, 5).map((s) => ({
|
|
5490
|
+
sessionId: s.id,
|
|
5491
|
+
date: s.date,
|
|
5492
|
+
cost: s.cost,
|
|
5493
|
+
category: s.category ?? "other",
|
|
5494
|
+
summary: s.summary,
|
|
5495
|
+
isCurrentSession: opts2.currentSessionId ? s.id === opts2.currentSessionId : void 0
|
|
5496
|
+
}));
|
|
5246
5497
|
return {
|
|
5247
|
-
|
|
5248
|
-
|
|
5249
|
-
|
|
5498
|
+
period: { start: opts2.startDate, end: opts2.endDate },
|
|
5499
|
+
categories,
|
|
5500
|
+
topSessions,
|
|
5501
|
+
reconciliation: opts2.reconciliation ?? { status: "local-only", localCost: 0 }
|
|
5250
5502
|
};
|
|
5251
5503
|
}
|
|
5252
|
-
var
|
|
5253
|
-
|
|
5254
|
-
"src/cost-attribution/heuristic.ts"() {
|
|
5504
|
+
var init_report = __esm({
|
|
5505
|
+
"src/cost-attribution/report.ts"() {
|
|
5255
5506
|
"use strict";
|
|
5256
|
-
|
|
5257
|
-
".ts",
|
|
5258
|
-
".tsx",
|
|
5259
|
-
".js",
|
|
5260
|
-
".jsx",
|
|
5261
|
-
".py",
|
|
5262
|
-
".go",
|
|
5263
|
-
".rs",
|
|
5264
|
-
".java",
|
|
5265
|
-
".kt",
|
|
5266
|
-
".swift",
|
|
5267
|
-
".cpp",
|
|
5268
|
-
".c",
|
|
5269
|
-
".h",
|
|
5270
|
-
".hpp",
|
|
5271
|
-
".cs",
|
|
5272
|
-
".rb",
|
|
5273
|
-
".php",
|
|
5274
|
-
".scala",
|
|
5275
|
-
".clj",
|
|
5276
|
-
".erl",
|
|
5277
|
-
".ex",
|
|
5278
|
-
".exs",
|
|
5279
|
-
".elm",
|
|
5280
|
-
".hs",
|
|
5281
|
-
".lua",
|
|
5282
|
-
".r",
|
|
5283
|
-
".m",
|
|
5284
|
-
".mm",
|
|
5285
|
-
".sh",
|
|
5286
|
-
".bash",
|
|
5287
|
-
".zsh",
|
|
5288
|
-
".fish",
|
|
5289
|
-
".ps1",
|
|
5290
|
-
".vim",
|
|
5291
|
-
".el"
|
|
5292
|
-
]);
|
|
5293
|
-
DOC_EXTS = /* @__PURE__ */ new Set([
|
|
5294
|
-
".md",
|
|
5295
|
-
".txt",
|
|
5296
|
-
".rst",
|
|
5297
|
-
".adoc",
|
|
5298
|
-
".org"
|
|
5299
|
-
]);
|
|
5300
|
-
CONFIG_EXTS = /* @__PURE__ */ new Set([
|
|
5301
|
-
".json",
|
|
5302
|
-
".yaml",
|
|
5303
|
-
".yml",
|
|
5304
|
-
".toml",
|
|
5305
|
-
".ini",
|
|
5306
|
-
".conf",
|
|
5307
|
-
".cfg",
|
|
5308
|
-
".env",
|
|
5309
|
-
".envrc",
|
|
5310
|
-
".properties",
|
|
5311
|
-
".xml",
|
|
5312
|
-
".plist"
|
|
5313
|
-
]);
|
|
5314
|
-
DATA_EXTS = /* @__PURE__ */ new Set([
|
|
5315
|
-
".csv",
|
|
5316
|
-
".sql",
|
|
5317
|
-
".db",
|
|
5318
|
-
".sqlite",
|
|
5319
|
-
".parquet",
|
|
5320
|
-
".jsonl",
|
|
5321
|
-
".ndjson",
|
|
5322
|
-
".tsv",
|
|
5323
|
-
".psv"
|
|
5324
|
-
]);
|
|
5325
|
-
TEST_PATTERNS = /\.(test|spec)\./;
|
|
5326
|
-
TEST_COMMANDS = /\b(npm test|pytest|jest|vitest|mocha|ava|tap|cargo test|go test|dotnet test|gradle test|mvn test|rake test|bundle exec rspec)\b/;
|
|
5327
|
-
GIT_COMMANDS = /\b(git commit|git merge|git rebase|git diff|git log|git blame|git cherry-pick|git revert)\b/;
|
|
5328
|
-
BUILD_COMMANDS = /\b(npm run build|make|cargo build|go build|gradle build|mvn compile|dotnet build|yarn build|pnpm build|webpack|vite build|tsc|esbuild|rollup)\b/;
|
|
5329
|
-
DEPLOY_COMMANDS = /\b(docker|kubectl|helm|wrangler deploy|terraform apply|pulumi up|serverless deploy|aws deploy|gcloud deploy|fly deploy|vercel deploy|netlify deploy)\b/;
|
|
5507
|
+
init_types();
|
|
5330
5508
|
}
|
|
5331
5509
|
});
|
|
5332
5510
|
|
|
5333
|
-
// src/cost-attribution/
|
|
5334
|
-
|
|
5335
|
-
|
|
5336
|
-
import { homedir as homedir7 } from "os";
|
|
5337
|
-
function sessionsDir() {
|
|
5338
|
-
const xdg = process.env.XDG_DATA_HOME || join13(homedir7(), ".local", "share");
|
|
5339
|
-
return join13(xdg, "kimiflare", "sessions");
|
|
5511
|
+
// src/cost-attribution/renderer.ts
|
|
5512
|
+
function fmtCost(n) {
|
|
5513
|
+
return n === 0 ? "$0.00" : `$${n.toFixed(2)}`;
|
|
5340
5514
|
}
|
|
5341
|
-
function
|
|
5342
|
-
|
|
5343
|
-
|
|
5344
|
-
|
|
5345
|
-
args = JSON.parse(c.function.arguments);
|
|
5346
|
-
} catch {
|
|
5347
|
-
}
|
|
5348
|
-
return { name: c.function.name, arguments: args };
|
|
5349
|
-
});
|
|
5515
|
+
function arrow(changePct) {
|
|
5516
|
+
if (changePct > 5) return "\u2191";
|
|
5517
|
+
if (changePct < -5) return "\u2193";
|
|
5518
|
+
return "\u2192";
|
|
5350
5519
|
}
|
|
5351
|
-
|
|
5352
|
-
|
|
5353
|
-
|
|
5354
|
-
|
|
5355
|
-
|
|
5356
|
-
|
|
5357
|
-
|
|
5358
|
-
|
|
5359
|
-
|
|
5360
|
-
|
|
5520
|
+
function pad(str, width) {
|
|
5521
|
+
return str.padEnd(width).slice(0, width);
|
|
5522
|
+
}
|
|
5523
|
+
function padLeft(str, width) {
|
|
5524
|
+
return str.padStart(width).slice(-width);
|
|
5525
|
+
}
|
|
5526
|
+
function categoryLabel(cat) {
|
|
5527
|
+
return cat;
|
|
5528
|
+
}
|
|
5529
|
+
function renderTerminal(report) {
|
|
5530
|
+
const lines = [];
|
|
5531
|
+
const catWidth = 22;
|
|
5532
|
+
const numWidth = 12;
|
|
5533
|
+
const arrWidth = 3;
|
|
5534
|
+
const totalWidth = catWidth + 1 + numWidth + 1 + numWidth + 1 + arrWidth;
|
|
5535
|
+
lines.push(`Period: ${report.period.start} \u2192 ${report.period.end}`);
|
|
5536
|
+
lines.push("");
|
|
5537
|
+
lines.push(
|
|
5538
|
+
`${pad("Category", catWidth)} ${padLeft("This period", numWidth)} ${padLeft("Last period", numWidth)} ${pad("", arrWidth)}`
|
|
5539
|
+
);
|
|
5540
|
+
lines.push("\u2500".repeat(totalWidth));
|
|
5541
|
+
let totalThis = 0;
|
|
5542
|
+
let totalLast = 0;
|
|
5543
|
+
for (const entry of report.categories) {
|
|
5544
|
+
totalThis += entry.thisPeriod.cost;
|
|
5545
|
+
totalLast += entry.lastPeriod.cost;
|
|
5546
|
+
const label = pad(categoryLabel(entry.category), catWidth);
|
|
5547
|
+
const thisStr = padLeft(fmtCost(entry.thisPeriod.cost), numWidth);
|
|
5548
|
+
const lastStr = padLeft(fmtCost(entry.lastPeriod.cost), numWidth);
|
|
5549
|
+
const arr = pad(arrow(entry.changePct), arrWidth);
|
|
5550
|
+
lines.push(`${label} ${thisStr} ${lastStr} ${arr}`);
|
|
5551
|
+
}
|
|
5552
|
+
lines.push("\u2500".repeat(totalWidth));
|
|
5553
|
+
lines.push(
|
|
5554
|
+
`${pad("Total", catWidth)} ${padLeft(fmtCost(totalThis), numWidth)} ${padLeft(fmtCost(totalLast), numWidth)} ${pad("", arrWidth)}`
|
|
5555
|
+
);
|
|
5556
|
+
if (report.topSessions.length > 0) {
|
|
5557
|
+
lines.push("");
|
|
5558
|
+
lines.push("Top sessions this period:");
|
|
5559
|
+
for (const s of report.topSessions) {
|
|
5560
|
+
const day = new Date(s.date).toLocaleDateString("en-US", { weekday: "short" });
|
|
5561
|
+
const cat = s.category;
|
|
5562
|
+
const sum = s.summary ? ` \u2014 ${s.summary}` : "";
|
|
5563
|
+
const cur = s.isCurrentSession ? " (current)" : "";
|
|
5564
|
+
lines.push(` ${fmtCost(s.cost).padStart(6)} ${day} ${cat}${sum}${cur}`);
|
|
5361
5565
|
}
|
|
5362
|
-
const totalToolCalls = turns.reduce((sum, t) => sum + t.toolCalls.length, 0);
|
|
5363
|
-
const result = classifySession(turns, { totalTurns: turns.length, totalToolCalls });
|
|
5364
|
-
return {
|
|
5365
|
-
category: result.category,
|
|
5366
|
-
confidence: result.confidence,
|
|
5367
|
-
classifiedBy: result.classifiedBy,
|
|
5368
|
-
summary: result.summary
|
|
5369
|
-
};
|
|
5370
|
-
} catch {
|
|
5371
|
-
return { category: "other", confidence: 0.5, classifiedBy: "heuristic", summary: "Session file unavailable" };
|
|
5372
5566
|
}
|
|
5567
|
+
lines.push("");
|
|
5568
|
+
const rec = report.reconciliation;
|
|
5569
|
+
switch (rec.status) {
|
|
5570
|
+
case "verified":
|
|
5571
|
+
lines.push(`Verified against Cloudflare: \u2713 (within ${rec.driftPct?.toFixed(1) ?? "0"}%)`);
|
|
5572
|
+
break;
|
|
5573
|
+
case "drift":
|
|
5574
|
+
lines.push(`Verified against Cloudflare: \u2717 (drift ${rec.driftPct?.toFixed(1) ?? "?"}%)`);
|
|
5575
|
+
break;
|
|
5576
|
+
case "error":
|
|
5577
|
+
lines.push(`Cloudflare reconciliation: \u26A0 ${rec.message ?? "API error"}`);
|
|
5578
|
+
break;
|
|
5579
|
+
case "local-only":
|
|
5580
|
+
lines.push("Local-only report (Cloudflare reconciliation skipped).");
|
|
5581
|
+
break;
|
|
5582
|
+
}
|
|
5583
|
+
return lines.join("\n");
|
|
5373
5584
|
}
|
|
5374
|
-
|
|
5375
|
-
|
|
5585
|
+
function renderJson(report) {
|
|
5586
|
+
return JSON.stringify(report, null, 2);
|
|
5587
|
+
}
|
|
5588
|
+
var init_renderer = __esm({
|
|
5589
|
+
"src/cost-attribution/renderer.ts"() {
|
|
5376
5590
|
"use strict";
|
|
5377
|
-
init_heuristic();
|
|
5378
5591
|
}
|
|
5379
5592
|
});
|
|
5380
5593
|
|
|
5381
|
-
// src/cost-attribution/
|
|
5382
|
-
|
|
5383
|
-
|
|
5384
|
-
runCostCommand: () => runCostCommand
|
|
5385
|
-
});
|
|
5386
|
-
import { readFile as readFile10 } from "fs/promises";
|
|
5387
|
-
import { join as join14 } from "path";
|
|
5388
|
-
import { homedir as homedir8 } from "os";
|
|
5389
|
-
function usageDir() {
|
|
5390
|
-
const xdg = process.env.XDG_DATA_HOME || join14(homedir8(), ".local", "share");
|
|
5391
|
-
return join14(xdg, "kimiflare");
|
|
5392
|
-
}
|
|
5393
|
-
function usagePath() {
|
|
5394
|
-
return join14(usageDir(), "usage.json");
|
|
5395
|
-
}
|
|
5396
|
-
function today() {
|
|
5397
|
-
return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
5398
|
-
}
|
|
5399
|
-
function daysAgo(n) {
|
|
5400
|
-
const d = /* @__PURE__ */ new Date();
|
|
5401
|
-
d.setDate(d.getDate() - n);
|
|
5402
|
-
return d.toISOString().slice(0, 10);
|
|
5403
|
-
}
|
|
5404
|
-
async function loadLog() {
|
|
5405
|
-
try {
|
|
5406
|
-
const raw = await readFile10(usagePath(), "utf8");
|
|
5407
|
-
return JSON.parse(raw);
|
|
5408
|
-
} catch {
|
|
5409
|
-
return { version: 1, days: [], sessions: [] };
|
|
5410
|
-
}
|
|
5411
|
-
}
|
|
5412
|
-
function filterSessions(sessions, start, end) {
|
|
5413
|
-
return sessions.filter((s) => s.date >= start && s.date <= end);
|
|
5594
|
+
// src/cost-attribution/reconcile.ts
|
|
5595
|
+
function cacheKey(opts2) {
|
|
5596
|
+
return `${opts2.gatewayId ?? "none"}:${opts2.startDate}:${opts2.endDate}`;
|
|
5414
5597
|
}
|
|
5415
|
-
async function
|
|
5416
|
-
|
|
5417
|
-
|
|
5418
|
-
let endDate;
|
|
5419
|
-
let prevStart;
|
|
5420
|
-
let prevEnd;
|
|
5421
|
-
if (opts2.month) {
|
|
5422
|
-
startDate = daysAgo(30);
|
|
5423
|
-
endDate = today();
|
|
5424
|
-
prevStart = daysAgo(60);
|
|
5425
|
-
prevEnd = daysAgo(31);
|
|
5426
|
-
} else if (opts2.day) {
|
|
5427
|
-
startDate = today();
|
|
5428
|
-
endDate = today();
|
|
5429
|
-
prevStart = daysAgo(1);
|
|
5430
|
-
prevEnd = daysAgo(1);
|
|
5431
|
-
} else {
|
|
5432
|
-
startDate = daysAgo(7);
|
|
5433
|
-
endDate = today();
|
|
5434
|
-
prevStart = daysAgo(14);
|
|
5435
|
-
prevEnd = daysAgo(8);
|
|
5436
|
-
}
|
|
5437
|
-
if (opts2.session) {
|
|
5438
|
-
const session = log2.sessions.find((s) => s.id === opts2.session);
|
|
5439
|
-
if (!session) {
|
|
5440
|
-
console.error(`Session ${opts2.session} not found.`);
|
|
5441
|
-
process.exit(1);
|
|
5442
|
-
}
|
|
5443
|
-
console.log(JSON.stringify(session, null, 2));
|
|
5444
|
-
return;
|
|
5598
|
+
async function reconcileWithCloudflare(opts2) {
|
|
5599
|
+
if (!opts2.accountId || !opts2.apiToken) {
|
|
5600
|
+
return { status: "local-only", localCost: opts2.localCost, message: "Missing Cloudflare credentials" };
|
|
5445
5601
|
}
|
|
5446
|
-
const
|
|
5447
|
-
const
|
|
5448
|
-
|
|
5449
|
-
|
|
5450
|
-
const result = await classifyFromSessionFile(s.id);
|
|
5451
|
-
s.category = result.category;
|
|
5452
|
-
s.confidence = result.confidence;
|
|
5453
|
-
s.classifiedBy = result.classifiedBy;
|
|
5454
|
-
s.summary = result.summary;
|
|
5455
|
-
s.classifiedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
5456
|
-
}
|
|
5602
|
+
const key = cacheKey(opts2);
|
|
5603
|
+
const cached = cache.get(key);
|
|
5604
|
+
if (cached && cached.expires > Date.now()) {
|
|
5605
|
+
return { ...cached.result, localCost: opts2.localCost };
|
|
5457
5606
|
}
|
|
5458
|
-
|
|
5459
|
-
|
|
5460
|
-
|
|
5461
|
-
|
|
5462
|
-
|
|
5463
|
-
|
|
5464
|
-
|
|
5465
|
-
|
|
5466
|
-
|
|
5467
|
-
|
|
5468
|
-
|
|
5469
|
-
startDate,
|
|
5470
|
-
endDate,
|
|
5471
|
-
sessions,
|
|
5472
|
-
previousSessions: prevSessions,
|
|
5473
|
-
reconciliation,
|
|
5474
|
-
categoryFilter
|
|
5475
|
-
});
|
|
5476
|
-
if (opts2.json) {
|
|
5477
|
-
console.log(renderJson(report));
|
|
5478
|
-
} else {
|
|
5479
|
-
console.log(renderTerminal(report));
|
|
5607
|
+
try {
|
|
5608
|
+
const result = {
|
|
5609
|
+
status: "local-only",
|
|
5610
|
+
localCost: opts2.localCost,
|
|
5611
|
+
message: "Cloudflare reconciliation not yet implemented"
|
|
5612
|
+
};
|
|
5613
|
+
cache.set(key, { result, expires: Date.now() + 60 * 60 * 1e3 });
|
|
5614
|
+
return result;
|
|
5615
|
+
} catch (err) {
|
|
5616
|
+
const message2 = err instanceof Error ? err.message : "Unknown error";
|
|
5617
|
+
return { status: "error", localCost: opts2.localCost, message: message2 };
|
|
5480
5618
|
}
|
|
5481
5619
|
}
|
|
5482
|
-
var
|
|
5483
|
-
|
|
5620
|
+
var cache;
|
|
5621
|
+
var init_reconcile = __esm({
|
|
5622
|
+
"src/cost-attribution/reconcile.ts"() {
|
|
5484
5623
|
"use strict";
|
|
5485
|
-
|
|
5486
|
-
init_renderer();
|
|
5487
|
-
init_reconcile();
|
|
5488
|
-
init_classify_from_session();
|
|
5624
|
+
cache = /* @__PURE__ */ new Map();
|
|
5489
5625
|
}
|
|
5490
5626
|
});
|
|
5491
5627
|
|
|
5492
|
-
// src/
|
|
5493
|
-
|
|
5494
|
-
|
|
5495
|
-
|
|
5496
|
-
POLL_INTERVAL_MS: () => POLL_INTERVAL_MS,
|
|
5497
|
-
POLL_TIMEOUT_MS: () => POLL_TIMEOUT_MS,
|
|
5498
|
-
authenticateDevice: () => authenticateDevice,
|
|
5499
|
-
clearCloudCredentials: () => clearCloudCredentials,
|
|
5500
|
-
fetchCloudUsage: () => fetchCloudUsage,
|
|
5501
|
-
generateDeviceCodes: () => generateDeviceCodes,
|
|
5502
|
-
loadCloudCredentials: () => loadCloudCredentials,
|
|
5503
|
-
pollForToken: () => pollForToken,
|
|
5504
|
-
registerDevice: () => registerDevice,
|
|
5505
|
-
saveCloudCredentials: () => saveCloudCredentials
|
|
5506
|
-
});
|
|
5507
|
-
import { readFile as readFile11, writeFile as writeFile8 } from "fs/promises";
|
|
5508
|
-
import { homedir as homedir9 } from "os";
|
|
5509
|
-
import { join as join15 } from "path";
|
|
5510
|
-
function cloudCredPath() {
|
|
5511
|
-
const xdg = process.env.XDG_CONFIG_HOME || join15(homedir9(), ".config");
|
|
5512
|
-
return join15(xdg, "kimiflare", "cloud.json");
|
|
5628
|
+
// src/cost-attribution/heuristic.ts
|
|
5629
|
+
function extOf(path) {
|
|
5630
|
+
const idx = path.lastIndexOf(".");
|
|
5631
|
+
return idx >= 0 ? path.slice(idx).toLowerCase() : "";
|
|
5513
5632
|
}
|
|
5514
|
-
function
|
|
5515
|
-
const
|
|
5516
|
-
|
|
5517
|
-
for (let i = 0; i < 8; i++) {
|
|
5518
|
-
out += chars[Math.floor(Math.random() * chars.length)];
|
|
5519
|
-
}
|
|
5520
|
-
return out;
|
|
5633
|
+
function basename2(path) {
|
|
5634
|
+
const idx = Math.max(path.lastIndexOf("/"), path.lastIndexOf("\\"));
|
|
5635
|
+
return idx >= 0 ? path.slice(idx + 1) : path;
|
|
5521
5636
|
}
|
|
5522
|
-
function
|
|
5523
|
-
const
|
|
5524
|
-
|
|
5525
|
-
|
|
5637
|
+
function classifyFile(path) {
|
|
5638
|
+
const base = basename2(path);
|
|
5639
|
+
const ext = extOf(path);
|
|
5640
|
+
if (TEST_PATTERNS.test(base)) return "writing-tests";
|
|
5641
|
+
if (base.toLowerCase().startsWith("readme")) return "reading-documentation";
|
|
5642
|
+
if (SOURCE_EXTS.has(ext)) return null;
|
|
5643
|
+
if (DOC_EXTS.has(ext)) return "reading-documentation";
|
|
5644
|
+
if (CONFIG_EXTS.has(ext)) return "reading-configuration";
|
|
5645
|
+
if (DATA_EXTS.has(ext)) return "reading-data";
|
|
5646
|
+
return null;
|
|
5526
5647
|
}
|
|
5527
|
-
function
|
|
5528
|
-
const
|
|
5529
|
-
const
|
|
5530
|
-
|
|
5531
|
-
|
|
5532
|
-
|
|
5648
|
+
function classifyWriteFile(path) {
|
|
5649
|
+
const base = basename2(path);
|
|
5650
|
+
const ext = extOf(path);
|
|
5651
|
+
if (TEST_PATTERNS.test(base)) return "writing-tests";
|
|
5652
|
+
if (base.toLowerCase().startsWith("readme") || DOC_EXTS.has(ext)) return "writing-documentation";
|
|
5653
|
+
if (CONFIG_EXTS.has(ext)) return "writing-configuration";
|
|
5654
|
+
if (SOURCE_EXTS.has(ext)) return "writing-source-code";
|
|
5655
|
+
return null;
|
|
5533
5656
|
}
|
|
5534
|
-
|
|
5535
|
-
const
|
|
5536
|
-
|
|
5537
|
-
|
|
5538
|
-
|
|
5539
|
-
|
|
5540
|
-
|
|
5541
|
-
const err = await registerRes.json().catch(() => ({}));
|
|
5542
|
-
throw new Error(`Failed to register device: ${err.error || registerRes.statusText}`);
|
|
5543
|
-
}
|
|
5657
|
+
function classifyEditFile(path) {
|
|
5658
|
+
const base = basename2(path);
|
|
5659
|
+
const ext = extOf(path);
|
|
5660
|
+
if (base.toLowerCase().startsWith("readme") || DOC_EXTS.has(ext)) return "editing-documentation";
|
|
5661
|
+
if (CONFIG_EXTS.has(ext)) return "editing-configuration";
|
|
5662
|
+
if (SOURCE_EXTS.has(ext)) return "editing-source-code";
|
|
5663
|
+
return null;
|
|
5544
5664
|
}
|
|
5545
|
-
|
|
5546
|
-
|
|
5547
|
-
|
|
5548
|
-
|
|
5549
|
-
|
|
5550
|
-
|
|
5551
|
-
|
|
5552
|
-
|
|
5553
|
-
|
|
5554
|
-
|
|
5555
|
-
|
|
5556
|
-
|
|
5557
|
-
|
|
5558
|
-
|
|
5559
|
-
|
|
5560
|
-
|
|
5561
|
-
|
|
5665
|
+
function classifyBash(command) {
|
|
5666
|
+
if (TEST_COMMANDS.test(command)) return "running-tests";
|
|
5667
|
+
if (GIT_COMMANDS.test(command)) return "running-git-commands";
|
|
5668
|
+
if (BUILD_COMMANDS.test(command)) return "running-build-scripts";
|
|
5669
|
+
if (DEPLOY_COMMANDS.test(command)) return "running-deploy-commands";
|
|
5670
|
+
return "running-shell-commands";
|
|
5671
|
+
}
|
|
5672
|
+
function classifyToolCall(tool) {
|
|
5673
|
+
const args = tool.arguments ?? {};
|
|
5674
|
+
const path = typeof args.path === "string" ? args.path : typeof args.file_path === "string" ? args.file_path : "";
|
|
5675
|
+
const command = typeof args.command === "string" ? args.command : "";
|
|
5676
|
+
switch (tool.name) {
|
|
5677
|
+
case "read_file":
|
|
5678
|
+
case "read": {
|
|
5679
|
+
const cat = classifyFile(path);
|
|
5680
|
+
if (cat) return { category: cat, confidence: 0.8 };
|
|
5681
|
+
if (SOURCE_EXTS.has(extOf(path))) return { category: "reading-source-code", confidence: 0.8 };
|
|
5682
|
+
return null;
|
|
5683
|
+
}
|
|
5684
|
+
case "create_file":
|
|
5685
|
+
case "write_file":
|
|
5686
|
+
case "write": {
|
|
5687
|
+
const cat = classifyWriteFile(path);
|
|
5688
|
+
if (cat) return { category: cat, confidence: 0.9 };
|
|
5689
|
+
return null;
|
|
5690
|
+
}
|
|
5691
|
+
case "str_replace":
|
|
5692
|
+
case "edit": {
|
|
5693
|
+
const cat = classifyEditFile(path);
|
|
5694
|
+
if (cat) return { category: cat, confidence: 0.85 };
|
|
5695
|
+
return null;
|
|
5696
|
+
}
|
|
5697
|
+
case "bash": {
|
|
5698
|
+
const cat = classifyBash(command);
|
|
5699
|
+
if (cat) return { category: cat, confidence: 0.9 };
|
|
5700
|
+
return null;
|
|
5701
|
+
}
|
|
5702
|
+
case "web_fetch":
|
|
5703
|
+
return { category: "reading-web-content", confidence: 0.85 };
|
|
5704
|
+
case "grep":
|
|
5705
|
+
case "glob": {
|
|
5706
|
+
const pattern = typeof args.pattern === "string" ? args.pattern : path;
|
|
5707
|
+
const isSource = SOURCE_EXTS.has(extOf(pattern)) || /\.(ts|js|py|go|rs)\b/.test(pattern);
|
|
5708
|
+
return { category: isSource ? "searching-code" : "searching-code", confidence: 0.75 };
|
|
5709
|
+
}
|
|
5710
|
+
case "execute_code":
|
|
5711
|
+
return { category: "other", confidence: 0.6 };
|
|
5712
|
+
default:
|
|
5713
|
+
return null;
|
|
5562
5714
|
}
|
|
5563
|
-
return null;
|
|
5564
5715
|
}
|
|
5565
|
-
|
|
5566
|
-
const
|
|
5567
|
-
|
|
5568
|
-
|
|
5569
|
-
|
|
5570
|
-
|
|
5571
|
-
|
|
5572
|
-
|
|
5716
|
+
function classifyTurn(turn) {
|
|
5717
|
+
const signals = [];
|
|
5718
|
+
for (const tool of turn.toolCalls) {
|
|
5719
|
+
const result = classifyToolCall(tool);
|
|
5720
|
+
if (result) {
|
|
5721
|
+
signals.push({
|
|
5722
|
+
category: result.category,
|
|
5723
|
+
weight: turn.tokens ?? 1,
|
|
5724
|
+
confidence: result.confidence
|
|
5725
|
+
});
|
|
5726
|
+
}
|
|
5573
5727
|
}
|
|
5574
|
-
return
|
|
5575
|
-
input_token_limit: data.input_token_limit,
|
|
5576
|
-
input_tokens_used: data.input_tokens_used,
|
|
5577
|
-
remaining: data.remaining,
|
|
5578
|
-
expires_at: data.expires_at
|
|
5579
|
-
};
|
|
5728
|
+
return signals;
|
|
5580
5729
|
}
|
|
5581
|
-
|
|
5582
|
-
|
|
5583
|
-
|
|
5584
|
-
const
|
|
5585
|
-
|
|
5586
|
-
|
|
5730
|
+
function classifySession(turns, opts2) {
|
|
5731
|
+
const scores = /* @__PURE__ */ new Map();
|
|
5732
|
+
for (const turn of turns) {
|
|
5733
|
+
const signals = classifyTurn(turn);
|
|
5734
|
+
for (const s of signals) {
|
|
5735
|
+
scores.set(s.category, (scores.get(s.category) ?? 0) + s.weight * s.confidence);
|
|
5587
5736
|
}
|
|
5588
|
-
} catch {
|
|
5589
5737
|
}
|
|
5590
|
-
|
|
5591
|
-
|
|
5592
|
-
|
|
5593
|
-
|
|
5594
|
-
await writeFile8(p, JSON.stringify(creds, null, 2), "utf8");
|
|
5595
|
-
}
|
|
5596
|
-
async function clearCloudCredentials() {
|
|
5597
|
-
try {
|
|
5598
|
-
const { unlink: unlink5 } = await import("fs/promises");
|
|
5599
|
-
await unlink5(cloudCredPath());
|
|
5600
|
-
} catch {
|
|
5738
|
+
const totalTurns = opts2?.totalTurns ?? turns.length;
|
|
5739
|
+
const totalToolCalls = opts2?.totalToolCalls ?? turns.reduce((sum, t) => sum + t.toolCalls.length, 0);
|
|
5740
|
+
if (totalTurns < 3 && totalToolCalls < 5) {
|
|
5741
|
+
scores.set("other", (scores.get("other") ?? 0) + 0.6);
|
|
5601
5742
|
}
|
|
5602
|
-
|
|
5603
|
-
|
|
5604
|
-
const codes = generateDeviceCodes();
|
|
5605
|
-
await registerDevice(codes);
|
|
5606
|
-
onStatus({ url: codes.authUrl, userCode: codes.userCode, polling: false });
|
|
5607
|
-
const startTime = Date.now();
|
|
5608
|
-
while (Date.now() - startTime < POLL_TIMEOUT_MS) {
|
|
5609
|
-
await new Promise((r) => setTimeout(r, POLL_INTERVAL_MS));
|
|
5610
|
-
onStatus({ url: codes.authUrl, userCode: codes.userCode, polling: true });
|
|
5611
|
-
const creds = await pollForToken(codes.deviceCode, codes.deviceId);
|
|
5612
|
-
if (creds) return creds;
|
|
5743
|
+
if (scores.size === 0) {
|
|
5744
|
+
return { category: "other", confidence: 0.6, classifiedBy: "heuristic", summary: "Short or ambiguous session" };
|
|
5613
5745
|
}
|
|
5614
|
-
|
|
5746
|
+
let bestCategory = "other";
|
|
5747
|
+
let bestScore = -1;
|
|
5748
|
+
let totalScore = 0;
|
|
5749
|
+
for (const [cat, score] of scores) {
|
|
5750
|
+
totalScore += score;
|
|
5751
|
+
if (score > bestScore) {
|
|
5752
|
+
bestScore = score;
|
|
5753
|
+
bestCategory = cat;
|
|
5754
|
+
}
|
|
5755
|
+
}
|
|
5756
|
+
const confidence = totalScore > 0 ? bestScore / totalScore : 0;
|
|
5757
|
+
return {
|
|
5758
|
+
category: bestCategory,
|
|
5759
|
+
confidence: Math.round(confidence * 100) / 100,
|
|
5760
|
+
classifiedBy: "heuristic"
|
|
5761
|
+
};
|
|
5615
5762
|
}
|
|
5616
|
-
var
|
|
5617
|
-
var
|
|
5618
|
-
"src/
|
|
5763
|
+
var SOURCE_EXTS, DOC_EXTS, CONFIG_EXTS, DATA_EXTS, TEST_PATTERNS, TEST_COMMANDS, GIT_COMMANDS, BUILD_COMMANDS, DEPLOY_COMMANDS;
|
|
5764
|
+
var init_heuristic = __esm({
|
|
5765
|
+
"src/cost-attribution/heuristic.ts"() {
|
|
5619
5766
|
"use strict";
|
|
5620
|
-
|
|
5621
|
-
|
|
5622
|
-
|
|
5767
|
+
SOURCE_EXTS = /* @__PURE__ */ new Set([
|
|
5768
|
+
".ts",
|
|
5769
|
+
".tsx",
|
|
5770
|
+
".js",
|
|
5771
|
+
".jsx",
|
|
5772
|
+
".py",
|
|
5773
|
+
".go",
|
|
5774
|
+
".rs",
|
|
5775
|
+
".java",
|
|
5776
|
+
".kt",
|
|
5777
|
+
".swift",
|
|
5778
|
+
".cpp",
|
|
5779
|
+
".c",
|
|
5780
|
+
".h",
|
|
5781
|
+
".hpp",
|
|
5782
|
+
".cs",
|
|
5783
|
+
".rb",
|
|
5784
|
+
".php",
|
|
5785
|
+
".scala",
|
|
5786
|
+
".clj",
|
|
5787
|
+
".erl",
|
|
5788
|
+
".ex",
|
|
5789
|
+
".exs",
|
|
5790
|
+
".elm",
|
|
5791
|
+
".hs",
|
|
5792
|
+
".lua",
|
|
5793
|
+
".r",
|
|
5794
|
+
".m",
|
|
5795
|
+
".mm",
|
|
5796
|
+
".sh",
|
|
5797
|
+
".bash",
|
|
5798
|
+
".zsh",
|
|
5799
|
+
".fish",
|
|
5800
|
+
".ps1",
|
|
5801
|
+
".vim",
|
|
5802
|
+
".el"
|
|
5803
|
+
]);
|
|
5804
|
+
DOC_EXTS = /* @__PURE__ */ new Set([
|
|
5805
|
+
".md",
|
|
5806
|
+
".txt",
|
|
5807
|
+
".rst",
|
|
5808
|
+
".adoc",
|
|
5809
|
+
".org"
|
|
5810
|
+
]);
|
|
5811
|
+
CONFIG_EXTS = /* @__PURE__ */ new Set([
|
|
5812
|
+
".json",
|
|
5813
|
+
".yaml",
|
|
5814
|
+
".yml",
|
|
5815
|
+
".toml",
|
|
5816
|
+
".ini",
|
|
5817
|
+
".conf",
|
|
5818
|
+
".cfg",
|
|
5819
|
+
".env",
|
|
5820
|
+
".envrc",
|
|
5821
|
+
".properties",
|
|
5822
|
+
".xml",
|
|
5823
|
+
".plist"
|
|
5824
|
+
]);
|
|
5825
|
+
DATA_EXTS = /* @__PURE__ */ new Set([
|
|
5826
|
+
".csv",
|
|
5827
|
+
".sql",
|
|
5828
|
+
".db",
|
|
5829
|
+
".sqlite",
|
|
5830
|
+
".parquet",
|
|
5831
|
+
".jsonl",
|
|
5832
|
+
".ndjson",
|
|
5833
|
+
".tsv",
|
|
5834
|
+
".psv"
|
|
5835
|
+
]);
|
|
5836
|
+
TEST_PATTERNS = /\.(test|spec)\./;
|
|
5837
|
+
TEST_COMMANDS = /\b(npm test|pytest|jest|vitest|mocha|ava|tap|cargo test|go test|dotnet test|gradle test|mvn test|rake test|bundle exec rspec)\b/;
|
|
5838
|
+
GIT_COMMANDS = /\b(git commit|git merge|git rebase|git diff|git log|git blame|git cherry-pick|git revert)\b/;
|
|
5839
|
+
BUILD_COMMANDS = /\b(npm run build|make|cargo build|go build|gradle build|mvn compile|dotnet build|yarn build|pnpm build|webpack|vite build|tsc|esbuild|rollup)\b/;
|
|
5840
|
+
DEPLOY_COMMANDS = /\b(docker|kubectl|helm|wrangler deploy|terraform apply|pulumi up|serverless deploy|aws deploy|gcloud deploy|fly deploy|vercel deploy|netlify deploy)\b/;
|
|
5623
5841
|
}
|
|
5624
5842
|
});
|
|
5625
5843
|
|
|
5626
|
-
// src/
|
|
5627
|
-
|
|
5628
|
-
|
|
5629
|
-
|
|
5630
|
-
|
|
5631
|
-
|
|
5632
|
-
return
|
|
5633
|
-
}
|
|
5634
|
-
|
|
5635
|
-
|
|
5636
|
-
|
|
5637
|
-
|
|
5638
|
-
|
|
5639
|
-
|
|
5640
|
-
"Content-Type": "application/x-www-form-urlencoded"
|
|
5641
|
-
},
|
|
5642
|
-
body: new URLSearchParams({ client_id: CLIENT_ID, scope: "repo" })
|
|
5643
|
-
});
|
|
5644
|
-
if (!deviceRes.ok) {
|
|
5645
|
-
yield { message: `Failed to request device code: ${deviceRes.status}`, error: true };
|
|
5646
|
-
throw new Error("Device code request failed");
|
|
5647
|
-
}
|
|
5648
|
-
const deviceData = await deviceRes.json();
|
|
5649
|
-
yield {
|
|
5650
|
-
message: `Open ${deviceData.verification_uri} and enter code: ${deviceData.user_code}`,
|
|
5651
|
-
url: deviceData.verification_uri,
|
|
5652
|
-
code: deviceData.user_code
|
|
5653
|
-
};
|
|
5654
|
-
const startTime = Date.now();
|
|
5655
|
-
const expiresIn = deviceData.expires_in * 1e3;
|
|
5656
|
-
const interval = deviceData.interval * 1e3;
|
|
5657
|
-
while (Date.now() - startTime < expiresIn) {
|
|
5658
|
-
await sleep2(interval);
|
|
5659
|
-
const tokenRes = await fetch(GITHUB_ACCESS_TOKEN_URL, {
|
|
5660
|
-
method: "POST",
|
|
5661
|
-
headers: {
|
|
5662
|
-
Accept: "application/json",
|
|
5663
|
-
"Content-Type": "application/x-www-form-urlencoded"
|
|
5664
|
-
},
|
|
5665
|
-
body: new URLSearchParams({
|
|
5666
|
-
client_id: CLIENT_ID,
|
|
5667
|
-
device_code: deviceData.device_code,
|
|
5668
|
-
grant_type: "urn:ietf:params:oauth:grant-type:device_code"
|
|
5669
|
-
})
|
|
5670
|
-
});
|
|
5671
|
-
if (!tokenRes.ok) continue;
|
|
5672
|
-
const tokenData = await tokenRes.json();
|
|
5673
|
-
if (tokenData.error === "authorization_pending") {
|
|
5674
|
-
continue;
|
|
5675
|
-
}
|
|
5676
|
-
if (tokenData.error === "slow_down") {
|
|
5677
|
-
await sleep2(interval * 2);
|
|
5678
|
-
continue;
|
|
5679
|
-
}
|
|
5680
|
-
if (tokenData.error) {
|
|
5681
|
-
yield { message: `OAuth error: ${tokenData.error}`, error: true };
|
|
5682
|
-
throw new Error(tokenData.error);
|
|
5844
|
+
// src/cost-attribution/classify-from-session.ts
|
|
5845
|
+
import { readFile as readFile9 } from "fs/promises";
|
|
5846
|
+
import { join as join13 } from "path";
|
|
5847
|
+
import { homedir as homedir7 } from "os";
|
|
5848
|
+
function sessionsDir() {
|
|
5849
|
+
const xdg = process.env.XDG_DATA_HOME || join13(homedir7(), ".local", "share");
|
|
5850
|
+
return join13(xdg, "kimiflare", "sessions");
|
|
5851
|
+
}
|
|
5852
|
+
function parseToolCalls(calls) {
|
|
5853
|
+
return calls.map((c) => {
|
|
5854
|
+
let args = {};
|
|
5855
|
+
try {
|
|
5856
|
+
args = JSON.parse(c.function.arguments);
|
|
5857
|
+
} catch {
|
|
5683
5858
|
}
|
|
5684
|
-
|
|
5685
|
-
|
|
5686
|
-
|
|
5687
|
-
|
|
5688
|
-
|
|
5689
|
-
|
|
5690
|
-
|
|
5691
|
-
|
|
5692
|
-
|
|
5693
|
-
|
|
5694
|
-
|
|
5695
|
-
|
|
5696
|
-
|
|
5697
|
-
return;
|
|
5859
|
+
return { name: c.function.name, arguments: args };
|
|
5860
|
+
});
|
|
5861
|
+
}
|
|
5862
|
+
async function classifyFromSessionFile(sessionId) {
|
|
5863
|
+
try {
|
|
5864
|
+
const raw = await readFile9(join13(sessionsDir(), `${sessionId}.json`), "utf8");
|
|
5865
|
+
const session = JSON.parse(raw);
|
|
5866
|
+
const messages = session.messages ?? [];
|
|
5867
|
+
const turns = [];
|
|
5868
|
+
for (const m of messages) {
|
|
5869
|
+
if (m.role === "assistant" && m.tool_calls && m.tool_calls.length > 0) {
|
|
5870
|
+
turns.push({ toolCalls: parseToolCalls(m.tool_calls), tokens: 100 });
|
|
5871
|
+
}
|
|
5698
5872
|
}
|
|
5873
|
+
const totalToolCalls = turns.reduce((sum, t) => sum + t.toolCalls.length, 0);
|
|
5874
|
+
const result = classifySession(turns, { totalTurns: turns.length, totalToolCalls });
|
|
5875
|
+
return {
|
|
5876
|
+
category: result.category,
|
|
5877
|
+
confidence: result.confidence,
|
|
5878
|
+
classifiedBy: result.classifiedBy,
|
|
5879
|
+
summary: result.summary
|
|
5880
|
+
};
|
|
5881
|
+
} catch {
|
|
5882
|
+
return { category: "other", confidence: 0.5, classifiedBy: "heuristic", summary: "Session file unavailable" };
|
|
5699
5883
|
}
|
|
5700
|
-
yield { message: "Device flow expired. Please try again.", error: true };
|
|
5701
|
-
throw new Error("Device flow expired");
|
|
5702
5884
|
}
|
|
5703
|
-
var
|
|
5704
|
-
|
|
5705
|
-
"src/remote/tui-auth.ts"() {
|
|
5885
|
+
var init_classify_from_session = __esm({
|
|
5886
|
+
"src/cost-attribution/classify-from-session.ts"() {
|
|
5706
5887
|
"use strict";
|
|
5707
|
-
|
|
5708
|
-
GITHUB_DEVICE_AUTH_URL = "https://github.com/login/device/code";
|
|
5709
|
-
GITHUB_ACCESS_TOKEN_URL = "https://github.com/login/oauth/access_token";
|
|
5710
|
-
CLIENT_ID = process.env.KIMIFLARE_GITHUB_CLIENT_ID ?? "Ov23liM7lJX1xE2V1sVK";
|
|
5888
|
+
init_heuristic();
|
|
5711
5889
|
}
|
|
5712
5890
|
});
|
|
5713
5891
|
|
|
5714
|
-
// src/
|
|
5715
|
-
var
|
|
5716
|
-
|
|
5717
|
-
|
|
5718
|
-
"use strict";
|
|
5719
|
-
init_loop();
|
|
5720
|
-
init_logger();
|
|
5721
|
-
TurnSupervisor = class {
|
|
5722
|
-
currentTurn = null;
|
|
5723
|
-
_phase = "idle";
|
|
5724
|
-
_killRequested = false;
|
|
5725
|
-
get phase() {
|
|
5726
|
-
return this._phase;
|
|
5727
|
-
}
|
|
5728
|
-
get isRunning() {
|
|
5729
|
-
return this._phase !== "idle";
|
|
5730
|
-
}
|
|
5731
|
-
get killRequested() {
|
|
5732
|
-
return this._killRequested;
|
|
5733
|
-
}
|
|
5734
|
-
startTurn(opts2, callbacks) {
|
|
5735
|
-
if (this.isRunning) {
|
|
5736
|
-
logger.warn("supervisor:start_rejected", { reason: "turn_already_running", phase: this._phase });
|
|
5737
|
-
throw new Error("TurnSupervisor: turn already in progress");
|
|
5738
|
-
}
|
|
5739
|
-
this._phase = "streaming";
|
|
5740
|
-
this._killRequested = false;
|
|
5741
|
-
logger.debug("supervisor:turn_start", { sessionId: opts2.sessionId });
|
|
5742
|
-
this.currentTurn = runAgentTurn(opts2).then(async () => {
|
|
5743
|
-
this._phase = "idle";
|
|
5744
|
-
if (this._killRequested) {
|
|
5745
|
-
logger.debug("supervisor:turn_killed", { sessionId: opts2.sessionId });
|
|
5746
|
-
} else {
|
|
5747
|
-
logger.debug("supervisor:turn_done", { sessionId: opts2.sessionId });
|
|
5748
|
-
}
|
|
5749
|
-
await callbacks?.onDone?.();
|
|
5750
|
-
}).catch(async (error) => {
|
|
5751
|
-
this._phase = "idle";
|
|
5752
|
-
const err = error;
|
|
5753
|
-
logger.warn("supervisor:turn_error", {
|
|
5754
|
-
sessionId: opts2.sessionId,
|
|
5755
|
-
error: err.message ?? String(err),
|
|
5756
|
-
name: err.name
|
|
5757
|
-
});
|
|
5758
|
-
await callbacks?.onError?.(err);
|
|
5759
|
-
}).finally(() => {
|
|
5760
|
-
this.currentTurn = null;
|
|
5761
|
-
this._killRequested = false;
|
|
5762
|
-
});
|
|
5763
|
-
}
|
|
5764
|
-
/** Request that the current turn be killed. This does NOT directly abort
|
|
5765
|
-
* the turn — the caller must abort the AbortScope that was passed to
|
|
5766
|
-
* `startTurn`. This method only records the intent so the supervisor
|
|
5767
|
-
* knows the turn was intentionally killed rather than failing. */
|
|
5768
|
-
killTurn() {
|
|
5769
|
-
if (!this.isRunning) return;
|
|
5770
|
-
this._killRequested = true;
|
|
5771
|
-
logger.debug("supervisor:kill_requested", { phase: this._phase });
|
|
5772
|
-
}
|
|
5773
|
-
};
|
|
5774
|
-
}
|
|
5892
|
+
// src/cost-attribution/cli.ts
|
|
5893
|
+
var cli_exports = {};
|
|
5894
|
+
__export(cli_exports, {
|
|
5895
|
+
runCostCommand: () => runCostCommand
|
|
5775
5896
|
});
|
|
5776
|
-
|
|
5777
|
-
|
|
5778
|
-
|
|
5779
|
-
|
|
5780
|
-
|
|
5781
|
-
|
|
5782
|
-
seen++;
|
|
5783
|
-
if (seen === n) return i;
|
|
5784
|
-
}
|
|
5785
|
-
}
|
|
5786
|
-
return -1;
|
|
5897
|
+
import { readFile as readFile10 } from "fs/promises";
|
|
5898
|
+
import { join as join14 } from "path";
|
|
5899
|
+
import { homedir as homedir8 } from "os";
|
|
5900
|
+
function usageDir() {
|
|
5901
|
+
const xdg = process.env.XDG_DATA_HOME || join14(homedir8(), ".local", "share");
|
|
5902
|
+
return join14(xdg, "kimiflare");
|
|
5787
5903
|
}
|
|
5788
|
-
|
|
5789
|
-
|
|
5790
|
-
|
|
5791
|
-
|
|
5792
|
-
|
|
5793
|
-
|
|
5794
|
-
|
|
5795
|
-
const
|
|
5796
|
-
|
|
5797
|
-
|
|
5904
|
+
function usagePath() {
|
|
5905
|
+
return join14(usageDir(), "usage.json");
|
|
5906
|
+
}
|
|
5907
|
+
function today() {
|
|
5908
|
+
return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
5909
|
+
}
|
|
5910
|
+
function daysAgo(n) {
|
|
5911
|
+
const d = /* @__PURE__ */ new Date();
|
|
5912
|
+
d.setDate(d.getDate() - n);
|
|
5913
|
+
return d.toISOString().slice(0, 10);
|
|
5914
|
+
}
|
|
5915
|
+
async function loadLog() {
|
|
5916
|
+
try {
|
|
5917
|
+
const raw = await readFile10(usagePath(), "utf8");
|
|
5918
|
+
return JSON.parse(raw);
|
|
5919
|
+
} catch {
|
|
5920
|
+
return { version: 1, days: [], sessions: [] };
|
|
5798
5921
|
}
|
|
5799
|
-
|
|
5800
|
-
|
|
5801
|
-
|
|
5802
|
-
|
|
5803
|
-
|
|
5804
|
-
|
|
5922
|
+
}
|
|
5923
|
+
function filterSessions(sessions, start, end) {
|
|
5924
|
+
return sessions.filter((s) => s.date >= start && s.date <= end);
|
|
5925
|
+
}
|
|
5926
|
+
async function runCostCommand(opts2) {
|
|
5927
|
+
const log2 = await loadLog();
|
|
5928
|
+
let startDate;
|
|
5929
|
+
let endDate;
|
|
5930
|
+
let prevStart;
|
|
5931
|
+
let prevEnd;
|
|
5932
|
+
if (opts2.month) {
|
|
5933
|
+
startDate = daysAgo(30);
|
|
5934
|
+
endDate = today();
|
|
5935
|
+
prevStart = daysAgo(60);
|
|
5936
|
+
prevEnd = daysAgo(31);
|
|
5937
|
+
} else if (opts2.day) {
|
|
5938
|
+
startDate = today();
|
|
5939
|
+
endDate = today();
|
|
5940
|
+
prevStart = daysAgo(1);
|
|
5941
|
+
prevEnd = daysAgo(1);
|
|
5942
|
+
} else {
|
|
5943
|
+
startDate = daysAgo(7);
|
|
5944
|
+
endDate = today();
|
|
5945
|
+
prevStart = daysAgo(14);
|
|
5946
|
+
prevEnd = daysAgo(8);
|
|
5805
5947
|
}
|
|
5806
|
-
|
|
5807
|
-
const
|
|
5808
|
-
if (
|
|
5809
|
-
|
|
5810
|
-
|
|
5948
|
+
if (opts2.session) {
|
|
5949
|
+
const session = log2.sessions.find((s) => s.id === opts2.session);
|
|
5950
|
+
if (!session) {
|
|
5951
|
+
console.error(`Session ${opts2.session} not found.`);
|
|
5952
|
+
process.exit(1);
|
|
5811
5953
|
}
|
|
5812
|
-
|
|
5813
|
-
|
|
5814
|
-
|
|
5954
|
+
console.log(JSON.stringify(session, null, 2));
|
|
5955
|
+
return;
|
|
5956
|
+
}
|
|
5957
|
+
const sessions = filterSessions(log2.sessions, startDate, endDate);
|
|
5958
|
+
const prevSessions = filterSessions(log2.sessions, prevStart, prevEnd);
|
|
5959
|
+
for (const s of sessions) {
|
|
5960
|
+
if (!s.category || opts2.reclassify) {
|
|
5961
|
+
const result = await classifyFromSessionFile(s.id);
|
|
5962
|
+
s.category = result.category;
|
|
5963
|
+
s.confidence = result.confidence;
|
|
5964
|
+
s.classifiedBy = result.classifiedBy;
|
|
5965
|
+
s.summary = result.summary;
|
|
5966
|
+
s.classifiedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
5815
5967
|
}
|
|
5816
|
-
|
|
5817
|
-
|
|
5818
|
-
|
|
5819
|
-
const
|
|
5820
|
-
|
|
5821
|
-
|
|
5822
|
-
|
|
5823
|
-
|
|
5824
|
-
|
|
5825
|
-
|
|
5826
|
-
|
|
5827
|
-
${transcript}` }
|
|
5828
|
-
],
|
|
5829
|
-
signal: opts2.signal,
|
|
5830
|
-
temperature: 0.1,
|
|
5831
|
-
reasoningEffort: "low",
|
|
5832
|
-
gateway: opts2.gateway,
|
|
5833
|
-
idleTimeoutMs: 6e4
|
|
5968
|
+
}
|
|
5969
|
+
const categoryFilter = opts2.category;
|
|
5970
|
+
const localCost = sessions.reduce((sum, s) => sum + s.cost, 0);
|
|
5971
|
+
const reconciliation = opts2.localOnly ? { status: "local-only", localCost } : await reconcileWithCloudflare({
|
|
5972
|
+
localCost,
|
|
5973
|
+
accountId: opts2.config.accountId,
|
|
5974
|
+
apiToken: opts2.config.apiToken,
|
|
5975
|
+
gatewayId: opts2.config.aiGatewayId,
|
|
5976
|
+
startDate,
|
|
5977
|
+
endDate
|
|
5834
5978
|
});
|
|
5835
|
-
|
|
5836
|
-
|
|
5979
|
+
const report = buildReport({
|
|
5980
|
+
startDate,
|
|
5981
|
+
endDate,
|
|
5982
|
+
sessions,
|
|
5983
|
+
previousSessions: prevSessions,
|
|
5984
|
+
reconciliation,
|
|
5985
|
+
categoryFilter
|
|
5986
|
+
});
|
|
5987
|
+
if (opts2.json) {
|
|
5988
|
+
console.log(renderJson(report));
|
|
5989
|
+
} else {
|
|
5990
|
+
console.log(renderTerminal(report));
|
|
5837
5991
|
}
|
|
5838
|
-
const summaryMsg = {
|
|
5839
|
-
role: "user",
|
|
5840
|
-
content: `[compacted summary of earlier turns]
|
|
5841
|
-
${summary.trim()}`
|
|
5842
|
-
};
|
|
5843
|
-
return {
|
|
5844
|
-
summary: summary.trim(),
|
|
5845
|
-
newMessages: [...prefix, summaryMsg, ...toKeep],
|
|
5846
|
-
replacedCount: toSummarize.length
|
|
5847
|
-
};
|
|
5848
5992
|
}
|
|
5849
|
-
var
|
|
5850
|
-
|
|
5851
|
-
"src/agent/compact.ts"() {
|
|
5993
|
+
var init_cli = __esm({
|
|
5994
|
+
"src/cost-attribution/cli.ts"() {
|
|
5852
5995
|
"use strict";
|
|
5853
|
-
|
|
5854
|
-
|
|
5855
|
-
|
|
5856
|
-
|
|
5857
|
-
- Tools run (bash commands, edits) and the outcome of each.
|
|
5858
|
-
- Decisions made and open questions.
|
|
5859
|
-
- Any constraints or preferences the user has stated.
|
|
5860
|
-
|
|
5861
|
-
Do not include speculation. Do not include chat-style pleasantries. Use short bullet form. Aim for ~400-800 tokens.`;
|
|
5996
|
+
init_report();
|
|
5997
|
+
init_renderer();
|
|
5998
|
+
init_reconcile();
|
|
5999
|
+
init_classify_from_session();
|
|
5862
6000
|
}
|
|
5863
6001
|
});
|
|
5864
6002
|
|
|
5865
|
-
// src/
|
|
5866
|
-
|
|
5867
|
-
|
|
5868
|
-
|
|
5869
|
-
|
|
5870
|
-
|
|
5871
|
-
|
|
5872
|
-
|
|
5873
|
-
|
|
5874
|
-
|
|
5875
|
-
|
|
5876
|
-
|
|
5877
|
-
|
|
5878
|
-
|
|
5879
|
-
|
|
6003
|
+
// src/cloud/auth.ts
|
|
6004
|
+
var auth_exports = {};
|
|
6005
|
+
__export(auth_exports, {
|
|
6006
|
+
CLOUD_API_URL: () => CLOUD_API_URL,
|
|
6007
|
+
POLL_INTERVAL_MS: () => POLL_INTERVAL_MS,
|
|
6008
|
+
POLL_TIMEOUT_MS: () => POLL_TIMEOUT_MS,
|
|
6009
|
+
authenticateDevice: () => authenticateDevice,
|
|
6010
|
+
clearCloudCredentials: () => clearCloudCredentials,
|
|
6011
|
+
fetchCloudUsage: () => fetchCloudUsage,
|
|
6012
|
+
generateDeviceCodes: () => generateDeviceCodes,
|
|
6013
|
+
loadCloudCredentials: () => loadCloudCredentials,
|
|
6014
|
+
pollForToken: () => pollForToken,
|
|
6015
|
+
registerDevice: () => registerDevice,
|
|
6016
|
+
saveCloudCredentials: () => saveCloudCredentials
|
|
6017
|
+
});
|
|
6018
|
+
import { readFile as readFile11, writeFile as writeFile8 } from "fs/promises";
|
|
6019
|
+
import { homedir as homedir9 } from "os";
|
|
6020
|
+
import { join as join15 } from "path";
|
|
6021
|
+
function cloudCredPath() {
|
|
6022
|
+
const xdg = process.env.XDG_CONFIG_HOME || join15(homedir9(), ".config");
|
|
6023
|
+
return join15(xdg, "kimiflare", "cloud.json");
|
|
5880
6024
|
}
|
|
5881
|
-
function
|
|
5882
|
-
const
|
|
5883
|
-
|
|
5884
|
-
for (
|
|
5885
|
-
out.
|
|
5886
|
-
id: a.id,
|
|
5887
|
-
type: a.type,
|
|
5888
|
-
summary: a.summary,
|
|
5889
|
-
raw: a.raw.slice(0, MAX_ARTIFACT_CHARS),
|
|
5890
|
-
source: a.source,
|
|
5891
|
-
path: a.path,
|
|
5892
|
-
lineRange: a.lineRange,
|
|
5893
|
-
ts: a.ts
|
|
5894
|
-
});
|
|
6025
|
+
function generateCode() {
|
|
6026
|
+
const chars = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789";
|
|
6027
|
+
let out = "";
|
|
6028
|
+
for (let i = 0; i < 8; i++) {
|
|
6029
|
+
out += chars[Math.floor(Math.random() * chars.length)];
|
|
5895
6030
|
}
|
|
5896
6031
|
return out;
|
|
5897
6032
|
}
|
|
5898
|
-
function
|
|
5899
|
-
const
|
|
5900
|
-
|
|
5901
|
-
|
|
5902
|
-
id: a.id,
|
|
5903
|
-
type: a.type,
|
|
5904
|
-
summary: a.summary,
|
|
5905
|
-
raw: a.raw,
|
|
5906
|
-
source: a.source,
|
|
5907
|
-
path: a.path,
|
|
5908
|
-
lineRange: a.lineRange,
|
|
5909
|
-
ts: a.ts
|
|
5910
|
-
});
|
|
5911
|
-
}
|
|
5912
|
-
return store;
|
|
6033
|
+
function generateDeviceId() {
|
|
6034
|
+
const arr = new Uint8Array(16);
|
|
6035
|
+
crypto.getRandomValues(arr);
|
|
6036
|
+
return Array.from(arr, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
5913
6037
|
}
|
|
5914
|
-
function
|
|
5915
|
-
|
|
5916
|
-
const
|
|
5917
|
-
|
|
5918
|
-
|
|
5919
|
-
|
|
6038
|
+
function generateDeviceCodes() {
|
|
6039
|
+
const deviceCode = `device-${generateCode()}-${Date.now()}`;
|
|
6040
|
+
const userCode = `${generateCode()}-${generateCode()}`;
|
|
6041
|
+
const authUrl = `${CLOUD_API_URL}/auth?code=${encodeURIComponent(userCode)}`;
|
|
6042
|
+
const deviceId = generateDeviceId();
|
|
6043
|
+
return { deviceCode, userCode, authUrl, deviceId };
|
|
6044
|
+
}
|
|
6045
|
+
async function registerDevice(codes) {
|
|
6046
|
+
const registerRes = await fetch(`${CLOUD_API_URL}/auth/device`, {
|
|
6047
|
+
method: "POST",
|
|
6048
|
+
headers: { "Content-Type": "application/json" },
|
|
6049
|
+
body: JSON.stringify({ device_code: codes.deviceCode, user_code: codes.userCode, device_id: codes.deviceId })
|
|
6050
|
+
});
|
|
6051
|
+
if (!registerRes.ok) {
|
|
6052
|
+
const err = await registerRes.json().catch(() => ({}));
|
|
6053
|
+
throw new Error(`Failed to register device: ${err.error || registerRes.statusText}`);
|
|
5920
6054
|
}
|
|
5921
|
-
return lines.join("\n");
|
|
5922
6055
|
}
|
|
5923
|
-
function
|
|
5924
|
-
const
|
|
5925
|
-
|
|
5926
|
-
|
|
5927
|
-
|
|
5928
|
-
|
|
5929
|
-
|
|
5930
|
-
|
|
5931
|
-
if (
|
|
5932
|
-
|
|
5933
|
-
|
|
5934
|
-
|
|
5935
|
-
|
|
5936
|
-
|
|
5937
|
-
|
|
5938
|
-
|
|
5939
|
-
|
|
5940
|
-
if (state.next_actions.length) lines.push(`next_actions:
|
|
5941
|
-
${state.next_actions.map((a) => " - " + a).join("\n")}`);
|
|
5942
|
-
if (Object.keys(state.artifact_index).length) {
|
|
5943
|
-
lines.push("artifact_index:");
|
|
5944
|
-
for (const [id, meta] of Object.entries(state.artifact_index)) {
|
|
5945
|
-
lines.push(` ${id}: [${meta.type}] ${meta.summary}`);
|
|
5946
|
-
}
|
|
6056
|
+
async function pollForToken(deviceCode, deviceId) {
|
|
6057
|
+
const pollRes = await fetch(`${CLOUD_API_URL}/auth/poll`, {
|
|
6058
|
+
method: "POST",
|
|
6059
|
+
headers: { "Content-Type": "application/json" },
|
|
6060
|
+
body: JSON.stringify({ device_code: deviceCode })
|
|
6061
|
+
});
|
|
6062
|
+
if (!pollRes.ok) return null;
|
|
6063
|
+
const pollData = await pollRes.json();
|
|
6064
|
+
if (pollData.status === "approved" && pollData.access_token) {
|
|
6065
|
+
const creds = {
|
|
6066
|
+
accessToken: pollData.access_token,
|
|
6067
|
+
expiresAt: Math.floor(Date.now() / 1e3) + 7 * 24 * 60 * 60,
|
|
6068
|
+
// 7 days
|
|
6069
|
+
deviceId
|
|
6070
|
+
};
|
|
6071
|
+
await saveCloudCredentials(creds);
|
|
6072
|
+
return creds;
|
|
5947
6073
|
}
|
|
5948
|
-
return
|
|
6074
|
+
return null;
|
|
5949
6075
|
}
|
|
5950
|
-
function
|
|
6076
|
+
async function fetchCloudUsage(token, deviceId) {
|
|
6077
|
+
const headers = { Authorization: `Bearer ${token}` };
|
|
6078
|
+
if (deviceId) headers["X-Device-ID"] = deviceId;
|
|
6079
|
+
const res = await fetch(`${CLOUD_API_URL}/v1/usage`, { headers });
|
|
6080
|
+
if (!res.ok) return null;
|
|
6081
|
+
const data = await res.json();
|
|
6082
|
+
if (typeof data.remaining !== "number" || typeof data.input_token_limit !== "number" || typeof data.input_tokens_used !== "number" || typeof data.expires_at !== "string") {
|
|
6083
|
+
return null;
|
|
6084
|
+
}
|
|
5951
6085
|
return {
|
|
5952
|
-
|
|
5953
|
-
|
|
5954
|
-
|
|
6086
|
+
input_token_limit: data.input_token_limit,
|
|
6087
|
+
input_tokens_used: data.input_tokens_used,
|
|
6088
|
+
remaining: data.remaining,
|
|
6089
|
+
expires_at: data.expires_at
|
|
5955
6090
|
};
|
|
5956
|
-
}
|
|
5957
|
-
|
|
5958
|
-
|
|
5959
|
-
|
|
5960
|
-
|
|
5961
|
-
|
|
5962
|
-
|
|
5963
|
-
|
|
5964
|
-
|
|
5965
|
-
constructor(opts2) {
|
|
5966
|
-
this.maxArtifacts = opts2?.maxArtifacts ?? 200;
|
|
5967
|
-
this.maxTotalChars = opts2?.maxTotalChars ?? 5e5;
|
|
5968
|
-
}
|
|
5969
|
-
add(a) {
|
|
5970
|
-
while (this.totalChars() + a.raw.length > this.maxTotalChars && this.artifacts.size > 0) {
|
|
5971
|
-
this.evictOldest();
|
|
5972
|
-
}
|
|
5973
|
-
while (this.artifacts.size >= this.maxArtifacts) {
|
|
5974
|
-
this.evictOldest();
|
|
5975
|
-
}
|
|
5976
|
-
this.artifacts.set(a.id, a);
|
|
5977
|
-
}
|
|
5978
|
-
get(id) {
|
|
5979
|
-
return this.artifacts.get(id);
|
|
5980
|
-
}
|
|
5981
|
-
has(id) {
|
|
5982
|
-
return this.artifacts.has(id);
|
|
5983
|
-
}
|
|
5984
|
-
list() {
|
|
5985
|
-
return [...this.artifacts.values()].sort((a, b) => a.ts < b.ts ? -1 : 1);
|
|
5986
|
-
}
|
|
5987
|
-
recall(ids) {
|
|
5988
|
-
const out = [];
|
|
5989
|
-
for (const id of ids) {
|
|
5990
|
-
const a = this.artifacts.get(id);
|
|
5991
|
-
if (a) out.push({ id, artifact: a });
|
|
5992
|
-
}
|
|
5993
|
-
return out;
|
|
5994
|
-
}
|
|
5995
|
-
size() {
|
|
5996
|
-
return this.artifacts.size;
|
|
5997
|
-
}
|
|
5998
|
-
totalChars() {
|
|
5999
|
-
let sum = 0;
|
|
6000
|
-
for (const a of this.artifacts.values()) {
|
|
6001
|
-
sum += a.raw.length;
|
|
6002
|
-
}
|
|
6003
|
-
return sum;
|
|
6004
|
-
}
|
|
6005
|
-
evictOldest() {
|
|
6006
|
-
let oldest;
|
|
6007
|
-
for (const a of this.artifacts.values()) {
|
|
6008
|
-
if (!oldest || a.ts < oldest.ts) oldest = a;
|
|
6009
|
-
}
|
|
6010
|
-
if (oldest) this.artifacts.delete(oldest.id);
|
|
6011
|
-
}
|
|
6012
|
-
};
|
|
6091
|
+
}
|
|
6092
|
+
async function loadCloudCredentials() {
|
|
6093
|
+
try {
|
|
6094
|
+
const raw = await readFile11(cloudCredPath(), "utf8");
|
|
6095
|
+
const parsed = JSON.parse(raw);
|
|
6096
|
+
if (parsed.expiresAt && parsed.expiresAt > Date.now() / 1e3 && parsed.accessToken) {
|
|
6097
|
+
return parsed;
|
|
6098
|
+
}
|
|
6099
|
+
} catch {
|
|
6013
6100
|
}
|
|
6014
|
-
|
|
6015
|
-
|
|
6016
|
-
// src/agent/compaction.ts
|
|
6017
|
-
function approxTokens2(n) {
|
|
6018
|
-
return Math.round(n / 4);
|
|
6101
|
+
return null;
|
|
6019
6102
|
}
|
|
6020
|
-
function
|
|
6021
|
-
|
|
6022
|
-
|
|
6023
|
-
|
|
6024
|
-
|
|
6025
|
-
|
|
6103
|
+
async function saveCloudCredentials(creds) {
|
|
6104
|
+
const p = cloudCredPath();
|
|
6105
|
+
await writeFile8(p, JSON.stringify(creds, null, 2), "utf8");
|
|
6106
|
+
}
|
|
6107
|
+
async function clearCloudCredentials() {
|
|
6108
|
+
try {
|
|
6109
|
+
const { unlink: unlink5 } = await import("fs/promises");
|
|
6110
|
+
await unlink5(cloudCredPath());
|
|
6111
|
+
} catch {
|
|
6026
6112
|
}
|
|
6027
|
-
|
|
6028
|
-
|
|
6029
|
-
|
|
6030
|
-
|
|
6031
|
-
|
|
6113
|
+
}
|
|
6114
|
+
async function authenticateDevice(onStatus) {
|
|
6115
|
+
const codes = generateDeviceCodes();
|
|
6116
|
+
await registerDevice(codes);
|
|
6117
|
+
onStatus({ url: codes.authUrl, userCode: codes.userCode, polling: false });
|
|
6118
|
+
const startTime = Date.now();
|
|
6119
|
+
while (Date.now() - startTime < POLL_TIMEOUT_MS) {
|
|
6120
|
+
await new Promise((r) => setTimeout(r, POLL_INTERVAL_MS));
|
|
6121
|
+
onStatus({ url: codes.authUrl, userCode: codes.userCode, polling: true });
|
|
6122
|
+
const creds = await pollForToken(codes.deviceCode, codes.deviceId);
|
|
6123
|
+
if (creds) return creds;
|
|
6032
6124
|
}
|
|
6033
|
-
|
|
6125
|
+
throw new Error("Authentication timed out. Please try again.");
|
|
6034
6126
|
}
|
|
6035
|
-
|
|
6036
|
-
|
|
6127
|
+
var CLOUD_API_URL, POLL_INTERVAL_MS, POLL_TIMEOUT_MS;
|
|
6128
|
+
var init_auth = __esm({
|
|
6129
|
+
"src/cloud/auth.ts"() {
|
|
6130
|
+
"use strict";
|
|
6131
|
+
CLOUD_API_URL = "https://api.kimiflare.com";
|
|
6132
|
+
POLL_INTERVAL_MS = 5e3;
|
|
6133
|
+
POLL_TIMEOUT_MS = 15 * 60 * 1e3;
|
|
6134
|
+
}
|
|
6135
|
+
});
|
|
6136
|
+
|
|
6137
|
+
// src/remote/tui-auth.ts
|
|
6138
|
+
var tui_auth_exports = {};
|
|
6139
|
+
__export(tui_auth_exports, {
|
|
6140
|
+
authGitHubForTui: () => authGitHubForTui
|
|
6141
|
+
});
|
|
6142
|
+
function sleep2(ms) {
|
|
6143
|
+
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
6037
6144
|
}
|
|
6038
|
-
function
|
|
6039
|
-
|
|
6040
|
-
|
|
6041
|
-
|
|
6042
|
-
|
|
6043
|
-
|
|
6145
|
+
async function* authGitHubForTui() {
|
|
6146
|
+
yield { message: "Starting GitHub OAuth device flow..." };
|
|
6147
|
+
const deviceRes = await fetch(GITHUB_DEVICE_AUTH_URL, {
|
|
6148
|
+
method: "POST",
|
|
6149
|
+
headers: {
|
|
6150
|
+
Accept: "application/json",
|
|
6151
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
6152
|
+
},
|
|
6153
|
+
body: new URLSearchParams({ client_id: CLIENT_ID, scope: "repo" })
|
|
6154
|
+
});
|
|
6155
|
+
if (!deviceRes.ok) {
|
|
6156
|
+
yield { message: `Failed to request device code: ${deviceRes.status}`, error: true };
|
|
6157
|
+
throw new Error("Device code request failed");
|
|
6044
6158
|
}
|
|
6045
|
-
const
|
|
6046
|
-
|
|
6047
|
-
|
|
6048
|
-
|
|
6159
|
+
const deviceData = await deviceRes.json();
|
|
6160
|
+
yield {
|
|
6161
|
+
message: `Open ${deviceData.verification_uri} and enter code: ${deviceData.user_code}`,
|
|
6162
|
+
url: deviceData.verification_uri,
|
|
6163
|
+
code: deviceData.user_code
|
|
6164
|
+
};
|
|
6165
|
+
const startTime = Date.now();
|
|
6166
|
+
const expiresIn = deviceData.expires_in * 1e3;
|
|
6167
|
+
const interval = deviceData.interval * 1e3;
|
|
6168
|
+
while (Date.now() - startTime < expiresIn) {
|
|
6169
|
+
await sleep2(interval);
|
|
6170
|
+
const tokenRes = await fetch(GITHUB_ACCESS_TOKEN_URL, {
|
|
6171
|
+
method: "POST",
|
|
6172
|
+
headers: {
|
|
6173
|
+
Accept: "application/json",
|
|
6174
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
6175
|
+
},
|
|
6176
|
+
body: new URLSearchParams({
|
|
6177
|
+
client_id: CLIENT_ID,
|
|
6178
|
+
device_code: deviceData.device_code,
|
|
6179
|
+
grant_type: "urn:ietf:params:oauth:grant-type:device_code"
|
|
6180
|
+
})
|
|
6181
|
+
});
|
|
6182
|
+
if (!tokenRes.ok) continue;
|
|
6183
|
+
const tokenData = await tokenRes.json();
|
|
6184
|
+
if (tokenData.error === "authorization_pending") {
|
|
6049
6185
|
continue;
|
|
6050
6186
|
}
|
|
6051
|
-
|
|
6052
|
-
|
|
6053
|
-
if (i >= messages.length || messages[i].role !== "assistant") {
|
|
6187
|
+
if (tokenData.error === "slow_down") {
|
|
6188
|
+
await sleep2(interval * 2);
|
|
6054
6189
|
continue;
|
|
6055
6190
|
}
|
|
6056
|
-
|
|
6057
|
-
|
|
6058
|
-
|
|
6059
|
-
|
|
6060
|
-
|
|
6061
|
-
|
|
6191
|
+
if (tokenData.error) {
|
|
6192
|
+
yield { message: `OAuth error: ${tokenData.error}`, error: true };
|
|
6193
|
+
throw new Error(tokenData.error);
|
|
6194
|
+
}
|
|
6195
|
+
if (tokenData.access_token) {
|
|
6196
|
+
const cfg = await loadConfig() ?? {
|
|
6197
|
+
accountId: "",
|
|
6198
|
+
apiToken: "",
|
|
6199
|
+
model: "@cf/moonshotai/kimi-k2.6"
|
|
6200
|
+
};
|
|
6201
|
+
await saveConfig({
|
|
6202
|
+
...cfg,
|
|
6203
|
+
githubOAuthToken: tokenData.access_token,
|
|
6204
|
+
githubRefreshToken: tokenData.refresh_token,
|
|
6205
|
+
githubTokenExpiry: tokenData.expires_in ? Date.now() + tokenData.expires_in * 1e3 : void 0
|
|
6206
|
+
});
|
|
6207
|
+
yield { message: "GitHub authentication successful!", done: true };
|
|
6208
|
+
return;
|
|
6062
6209
|
}
|
|
6063
|
-
turns.push({ user, assistant, tools });
|
|
6064
6210
|
}
|
|
6065
|
-
|
|
6066
|
-
|
|
6067
|
-
function makeArtifactId(type, index) {
|
|
6068
|
-
return `${type}_${Date.now()}_${index}`;
|
|
6211
|
+
yield { message: "Device flow expired. Please try again.", error: true };
|
|
6212
|
+
throw new Error("Device flow expired");
|
|
6069
6213
|
}
|
|
6070
|
-
|
|
6071
|
-
|
|
6072
|
-
|
|
6073
|
-
|
|
6074
|
-
|
|
6075
|
-
|
|
6076
|
-
|
|
6077
|
-
|
|
6078
|
-
|
|
6079
|
-
|
|
6080
|
-
|
|
6081
|
-
|
|
6082
|
-
|
|
6083
|
-
|
|
6084
|
-
|
|
6085
|
-
|
|
6086
|
-
|
|
6087
|
-
|
|
6088
|
-
|
|
6089
|
-
|
|
6090
|
-
|
|
6091
|
-
|
|
6092
|
-
|
|
6093
|
-
|
|
6094
|
-
summary = `read ${path ?? "file"}`;
|
|
6095
|
-
if (path && !stateDelta.files_touched.includes(path)) stateDelta.files_touched.push(path);
|
|
6096
|
-
} catch {
|
|
6097
|
-
summary = "read file";
|
|
6098
|
-
}
|
|
6099
|
-
} else if (name === "bash") {
|
|
6100
|
-
type = "bash_log";
|
|
6101
|
-
try {
|
|
6102
|
-
const args = tc ? JSON.parse(tc.function.arguments) : {};
|
|
6103
|
-
const cmd = args.command ?? "";
|
|
6104
|
-
summary = `bash: ${cmd.slice(0, 60)}`;
|
|
6105
|
-
if (content.includes("Error") || content.includes("error") || content.includes("FAIL")) {
|
|
6106
|
-
stateDelta.recent_failures.push(`bash failed: ${cmd.slice(0, 80)}`);
|
|
6107
|
-
}
|
|
6108
|
-
} catch {
|
|
6109
|
-
summary = "bash command";
|
|
6214
|
+
var GITHUB_DEVICE_AUTH_URL, GITHUB_ACCESS_TOKEN_URL, CLIENT_ID;
|
|
6215
|
+
var init_tui_auth = __esm({
|
|
6216
|
+
"src/remote/tui-auth.ts"() {
|
|
6217
|
+
"use strict";
|
|
6218
|
+
init_config();
|
|
6219
|
+
GITHUB_DEVICE_AUTH_URL = "https://github.com/login/device/code";
|
|
6220
|
+
GITHUB_ACCESS_TOKEN_URL = "https://github.com/login/oauth/access_token";
|
|
6221
|
+
CLIENT_ID = process.env.KIMIFLARE_GITHUB_CLIENT_ID ?? "Ov23liM7lJX1xE2V1sVK";
|
|
6222
|
+
}
|
|
6223
|
+
});
|
|
6224
|
+
|
|
6225
|
+
// src/agent/supervisor.ts
|
|
6226
|
+
var TurnSupervisor;
|
|
6227
|
+
var init_supervisor = __esm({
|
|
6228
|
+
"src/agent/supervisor.ts"() {
|
|
6229
|
+
"use strict";
|
|
6230
|
+
init_loop();
|
|
6231
|
+
init_logger();
|
|
6232
|
+
TurnSupervisor = class {
|
|
6233
|
+
currentTurn = null;
|
|
6234
|
+
_phase = "idle";
|
|
6235
|
+
_killRequested = false;
|
|
6236
|
+
get phase() {
|
|
6237
|
+
return this._phase;
|
|
6110
6238
|
}
|
|
6111
|
-
|
|
6112
|
-
|
|
6113
|
-
summary = `grep results (${content.split("\n").length} lines)`;
|
|
6114
|
-
} else if (name === "web_fetch") {
|
|
6115
|
-
type = "web_fetch";
|
|
6116
|
-
try {
|
|
6117
|
-
const args = tc ? JSON.parse(tc.function.arguments) : {};
|
|
6118
|
-
summary = `web_fetch: ${args.url ?? "url"}`;
|
|
6119
|
-
} catch {
|
|
6120
|
-
summary = "web_fetch";
|
|
6239
|
+
get isRunning() {
|
|
6240
|
+
return this._phase !== "idle";
|
|
6121
6241
|
}
|
|
6122
|
-
|
|
6123
|
-
|
|
6124
|
-
const args = tc ? JSON.parse(tc.function.arguments) : {};
|
|
6125
|
-
path = args.path;
|
|
6126
|
-
if (path && !stateDelta.files_modified.includes(path)) stateDelta.files_modified.push(path);
|
|
6127
|
-
if (path && !stateDelta.files_touched.includes(path)) stateDelta.files_touched.push(path);
|
|
6128
|
-
} catch {
|
|
6242
|
+
get killRequested() {
|
|
6243
|
+
return this._killRequested;
|
|
6129
6244
|
}
|
|
6130
|
-
|
|
6131
|
-
|
|
6132
|
-
|
|
6133
|
-
|
|
6134
|
-
|
|
6135
|
-
|
|
6136
|
-
|
|
6245
|
+
startTurn(opts2, callbacks) {
|
|
6246
|
+
if (this.isRunning) {
|
|
6247
|
+
logger.warn("supervisor:start_rejected", { reason: "turn_already_running", phase: this._phase });
|
|
6248
|
+
throw new Error("TurnSupervisor: turn already in progress");
|
|
6249
|
+
}
|
|
6250
|
+
this._phase = "streaming";
|
|
6251
|
+
this._killRequested = false;
|
|
6252
|
+
logger.debug("supervisor:turn_start", { sessionId: opts2.sessionId });
|
|
6253
|
+
this.currentTurn = runAgentTurn(opts2).then(async () => {
|
|
6254
|
+
this._phase = "idle";
|
|
6255
|
+
if (this._killRequested) {
|
|
6256
|
+
logger.debug("supervisor:turn_killed", { sessionId: opts2.sessionId });
|
|
6257
|
+
} else {
|
|
6258
|
+
logger.debug("supervisor:turn_done", { sessionId: opts2.sessionId });
|
|
6259
|
+
}
|
|
6260
|
+
await callbacks?.onDone?.();
|
|
6261
|
+
}).catch(async (error) => {
|
|
6262
|
+
this._phase = "idle";
|
|
6263
|
+
const err = error;
|
|
6264
|
+
logger.warn("supervisor:turn_error", {
|
|
6265
|
+
sessionId: opts2.sessionId,
|
|
6266
|
+
error: err.message ?? String(err),
|
|
6267
|
+
name: err.name
|
|
6268
|
+
});
|
|
6269
|
+
await callbacks?.onError?.(err);
|
|
6270
|
+
}).finally(() => {
|
|
6271
|
+
this.currentTurn = null;
|
|
6272
|
+
this._killRequested = false;
|
|
6273
|
+
});
|
|
6137
6274
|
}
|
|
6138
|
-
|
|
6139
|
-
|
|
6140
|
-
|
|
6141
|
-
|
|
6142
|
-
|
|
6143
|
-
|
|
6144
|
-
|
|
6145
|
-
|
|
6146
|
-
summary = `tasks_set: ${tasks.length} tasks`;
|
|
6147
|
-
} catch {
|
|
6148
|
-
summary = "tasks_set";
|
|
6275
|
+
/** Request that the current turn be killed. This does NOT directly abort
|
|
6276
|
+
* the turn — the caller must abort the AbortScope that was passed to
|
|
6277
|
+
* `startTurn`. This method only records the intent so the supervisor
|
|
6278
|
+
* knows the turn was intentionally killed rather than failing. */
|
|
6279
|
+
killTurn() {
|
|
6280
|
+
if (!this.isRunning) return;
|
|
6281
|
+
this._killRequested = true;
|
|
6282
|
+
logger.debug("supervisor:kill_requested", { phase: this._phase });
|
|
6149
6283
|
}
|
|
6150
|
-
}
|
|
6151
|
-
const maxRaw = 5e4;
|
|
6152
|
-
const raw = content.length > maxRaw ? content.slice(0, maxRaw) + `
|
|
6153
|
-
...[${content.length - maxRaw} chars truncated]` : content;
|
|
6154
|
-
const artifact = {
|
|
6155
|
-
id: makeArtifactId(type, startIndex + ti),
|
|
6156
|
-
type,
|
|
6157
|
-
summary,
|
|
6158
|
-
raw,
|
|
6159
|
-
source: name,
|
|
6160
|
-
path,
|
|
6161
|
-
ts: (/* @__PURE__ */ new Date()).toISOString()
|
|
6162
6284
|
};
|
|
6163
|
-
artifacts.push(artifact);
|
|
6164
|
-
if (!content.includes("Error") && !content.includes("error") && content.length > 0 && content.length < 2e3) {
|
|
6165
|
-
stateDelta.confirmed_findings.push(`${name}: ${content.slice(0, 200)}`);
|
|
6166
|
-
}
|
|
6167
6285
|
}
|
|
6168
|
-
|
|
6169
|
-
|
|
6170
|
-
|
|
6171
|
-
|
|
6172
|
-
|
|
6173
|
-
|
|
6174
|
-
|
|
6175
|
-
|
|
6176
|
-
|
|
6177
|
-
const decision = match[1].trim().replace(/\.$/, "");
|
|
6178
|
-
if (decision.length > 10 && !stateDelta.decisions.includes(decision)) {
|
|
6179
|
-
stateDelta.decisions.push(decision);
|
|
6180
|
-
}
|
|
6181
|
-
}
|
|
6286
|
+
});
|
|
6287
|
+
|
|
6288
|
+
// src/agent/compact.ts
|
|
6289
|
+
function indexOfNthUserFromEnd(messages, n) {
|
|
6290
|
+
let seen = 0;
|
|
6291
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
6292
|
+
if (messages[i].role === "user") {
|
|
6293
|
+
seen++;
|
|
6294
|
+
if (seen === n) return i;
|
|
6182
6295
|
}
|
|
6183
6296
|
}
|
|
6184
|
-
return
|
|
6185
|
-
}
|
|
6186
|
-
function mergeState(state, delta) {
|
|
6187
|
-
const mergeArr = (a, b) => {
|
|
6188
|
-
if (!b) return a;
|
|
6189
|
-
const set = new Set(a);
|
|
6190
|
-
for (const item of b) set.add(item);
|
|
6191
|
-
return [...set];
|
|
6192
|
-
};
|
|
6193
|
-
return {
|
|
6194
|
-
...state,
|
|
6195
|
-
task: state.task || delta.task || "",
|
|
6196
|
-
user_constraints: mergeArr(state.user_constraints, delta.user_constraints),
|
|
6197
|
-
repo_facts: mergeArr(state.repo_facts, delta.repo_facts),
|
|
6198
|
-
files_touched: mergeArr(state.files_touched, delta.files_touched),
|
|
6199
|
-
files_modified: mergeArr(state.files_modified, delta.files_modified),
|
|
6200
|
-
confirmed_findings: mergeArr(state.confirmed_findings, delta.confirmed_findings),
|
|
6201
|
-
open_questions: mergeArr(state.open_questions, delta.open_questions),
|
|
6202
|
-
recent_failures: mergeArr(state.recent_failures, delta.recent_failures),
|
|
6203
|
-
decisions: mergeArr(state.decisions, delta.decisions),
|
|
6204
|
-
next_actions: mergeArr(state.next_actions, delta.next_actions),
|
|
6205
|
-
artifact_index: { ...state.artifact_index, ...delta.artifact_index }
|
|
6206
|
-
};
|
|
6207
|
-
}
|
|
6208
|
-
function shouldCompact(opts2) {
|
|
6209
|
-
const tokenThreshold = opts2.tokenThreshold ?? 8e4;
|
|
6210
|
-
const turnThreshold = opts2.turnThreshold ?? 12;
|
|
6211
|
-
const tokens = estimatePromptTokens(opts2.messages);
|
|
6212
|
-
const { turns } = groupIntoTurns(opts2.messages);
|
|
6213
|
-
return tokens > tokenThreshold || turns.length > turnThreshold;
|
|
6297
|
+
return -1;
|
|
6214
6298
|
}
|
|
6215
|
-
function compactMessages2(opts2) {
|
|
6216
|
-
const
|
|
6217
|
-
const
|
|
6218
|
-
|
|
6219
|
-
|
|
6220
|
-
|
|
6221
|
-
newMessages: opts2.messages,
|
|
6222
|
-
newState: opts2.state,
|
|
6223
|
-
metrics: {
|
|
6224
|
-
estimatedTokensBefore: tokensBefore,
|
|
6225
|
-
estimatedTokensAfter: tokensBefore,
|
|
6226
|
-
archivedArtifacts: 0,
|
|
6227
|
-
recalledArtifacts: 0,
|
|
6228
|
-
rawTurnsRemoved: 0,
|
|
6229
|
-
rawTurnsKept: turns.length
|
|
6230
|
-
}
|
|
6231
|
-
};
|
|
6299
|
+
async function compactMessages2(opts2) {
|
|
6300
|
+
const keep = opts2.keepLastTurns ?? 4;
|
|
6301
|
+
const messages = opts2.messages;
|
|
6302
|
+
let prefixEnd = 0;
|
|
6303
|
+
while (prefixEnd < messages.length && messages[prefixEnd].role === "system") {
|
|
6304
|
+
prefixEnd++;
|
|
6232
6305
|
}
|
|
6233
|
-
const
|
|
6234
|
-
|
|
6235
|
-
|
|
6236
|
-
let archivedCount = 0;
|
|
6237
|
-
for (let i = 0; i < toCompact.length; i++) {
|
|
6238
|
-
const turn = toCompact[i];
|
|
6239
|
-
const { artifacts, stateDelta } = extractArtifactsFromTurn(turn, i, opts2.store);
|
|
6240
|
-
for (const artifact of artifacts) {
|
|
6241
|
-
opts2.store.add(artifact);
|
|
6242
|
-
archivedCount++;
|
|
6243
|
-
newState.artifact_index[artifact.id] = {
|
|
6244
|
-
type: artifact.type,
|
|
6245
|
-
summary: artifact.summary,
|
|
6246
|
-
source: artifact.source,
|
|
6247
|
-
path: artifact.path
|
|
6248
|
-
};
|
|
6249
|
-
}
|
|
6250
|
-
newState = mergeState(newState, stateDelta);
|
|
6251
|
-
if (!newState.task && typeof turn.user.content === "string") {
|
|
6252
|
-
newState.task = turn.user.content.slice(0, 200);
|
|
6253
|
-
}
|
|
6306
|
+
const prefix = messages.slice(0, prefixEnd);
|
|
6307
|
+
if (prefix.length === 0) {
|
|
6308
|
+
return { summary: "", newMessages: messages, replacedCount: 0 };
|
|
6254
6309
|
}
|
|
6255
|
-
const
|
|
6256
|
-
|
|
6257
|
-
|
|
6258
|
-
|
|
6259
|
-
|
|
6260
|
-
|
|
6261
|
-
}
|
|
6310
|
+
const cutoffUserIdx = indexOfNthUserFromEnd(messages, keep);
|
|
6311
|
+
const firstKeepIdx = cutoffUserIdx >= 0 ? cutoffUserIdx : messages.length;
|
|
6312
|
+
const toSummarize = messages.slice(prefixEnd, firstKeepIdx);
|
|
6313
|
+
const toKeep = messages.slice(firstKeepIdx);
|
|
6314
|
+
if (toSummarize.length === 0) {
|
|
6315
|
+
return { summary: "", newMessages: messages, replacedCount: 0 };
|
|
6262
6316
|
}
|
|
6263
|
-
const
|
|
6264
|
-
|
|
6265
|
-
|
|
6266
|
-
|
|
6267
|
-
|
|
6268
|
-
estimatedTokensAfter: tokensAfter,
|
|
6269
|
-
archivedArtifacts: archivedCount,
|
|
6270
|
-
recalledArtifacts: 0,
|
|
6271
|
-
rawTurnsRemoved: toCompact.length,
|
|
6272
|
-
rawTurnsKept: toKeep.length
|
|
6273
|
-
};
|
|
6274
|
-
return { newMessages, newState, metrics };
|
|
6275
|
-
}
|
|
6276
|
-
function recallArtifacts(messages, store, state) {
|
|
6277
|
-
const text = messages.map((m) => typeof m.content === "string" ? m.content : "").join(" ");
|
|
6278
|
-
const ids = [];
|
|
6279
|
-
for (const [id, meta] of Object.entries(state.artifact_index)) {
|
|
6280
|
-
if (meta.path && text.includes(meta.path)) {
|
|
6281
|
-
ids.push(id);
|
|
6317
|
+
const transcript = toSummarize.map((m) => {
|
|
6318
|
+
const contentStr = typeof m.content === "string" ? m.content : m.content?.map((p) => p.type === "text" ? p.text : "[image]").join(" ") ?? "";
|
|
6319
|
+
if (m.role === "tool") {
|
|
6320
|
+
const snippet = contentStr.slice(0, 500);
|
|
6321
|
+
return `[tool ${m.name ?? ""}] ${snippet}`;
|
|
6282
6322
|
}
|
|
6283
|
-
|
|
6284
|
-
|
|
6285
|
-
|
|
6286
|
-
if (!keyword) continue;
|
|
6287
|
-
const lowerKeyword = keyword.toLowerCase();
|
|
6288
|
-
if (!text.toLowerCase().includes(lowerKeyword)) continue;
|
|
6289
|
-
for (const [id, meta] of Object.entries(state.artifact_index)) {
|
|
6290
|
-
if (meta.source === "bash" && !ids.includes(id) && meta.summary.toLowerCase().includes(lowerKeyword)) {
|
|
6291
|
-
ids.push(id);
|
|
6292
|
-
}
|
|
6323
|
+
if (m.role === "assistant") {
|
|
6324
|
+
const calls = m.tool_calls ? ` (tool_calls: ${m.tool_calls.map((c) => c.function.name).join(", ")})` : "";
|
|
6325
|
+
return `[assistant]${calls} ${contentStr}`;
|
|
6293
6326
|
}
|
|
6327
|
+
return `[${m.role}] ${contentStr}`;
|
|
6328
|
+
}).join("\n");
|
|
6329
|
+
let summary = "";
|
|
6330
|
+
const events = runKimi({
|
|
6331
|
+
accountId: opts2.accountId,
|
|
6332
|
+
apiToken: opts2.apiToken,
|
|
6333
|
+
model: opts2.model,
|
|
6334
|
+
messages: [
|
|
6335
|
+
{ role: "system", content: SUMMARY_SYSTEM },
|
|
6336
|
+
{ role: "user", content: `Summarize this session so it can be replaced by your summary:
|
|
6337
|
+
|
|
6338
|
+
${transcript}` }
|
|
6339
|
+
],
|
|
6340
|
+
signal: opts2.signal,
|
|
6341
|
+
temperature: 0.1,
|
|
6342
|
+
reasoningEffort: "low",
|
|
6343
|
+
gateway: opts2.gateway,
|
|
6344
|
+
idleTimeoutMs: 6e4
|
|
6345
|
+
});
|
|
6346
|
+
for await (const ev of events) {
|
|
6347
|
+
if (ev.type === "text") summary += ev.delta;
|
|
6294
6348
|
}
|
|
6295
|
-
const
|
|
6296
|
-
|
|
6297
|
-
|
|
6349
|
+
const summaryMsg = {
|
|
6350
|
+
role: "user",
|
|
6351
|
+
content: `[compacted summary of earlier turns]
|
|
6352
|
+
${summary.trim()}`
|
|
6353
|
+
};
|
|
6354
|
+
return {
|
|
6355
|
+
summary: summary.trim(),
|
|
6356
|
+
newMessages: [...prefix, summaryMsg, ...toKeep],
|
|
6357
|
+
replacedCount: toSummarize.length
|
|
6358
|
+
};
|
|
6298
6359
|
}
|
|
6299
|
-
var
|
|
6300
|
-
|
|
6360
|
+
var SUMMARY_SYSTEM;
|
|
6361
|
+
var init_compact = __esm({
|
|
6362
|
+
"src/agent/compact.ts"() {
|
|
6301
6363
|
"use strict";
|
|
6302
|
-
|
|
6364
|
+
init_client();
|
|
6365
|
+
SUMMARY_SYSTEM = `You are summarizing a terminal coding session so it can fit back into a short context window. Produce a dense summary that captures:
|
|
6366
|
+
- The user's goal(s) and what they've asked for.
|
|
6367
|
+
- Files read or modified, with paths.
|
|
6368
|
+
- Tools run (bash commands, edits) and the outcome of each.
|
|
6369
|
+
- Decisions made and open questions.
|
|
6370
|
+
- Any constraints or preferences the user has stated.
|
|
6371
|
+
|
|
6372
|
+
Do not include speculation. Do not include chat-style pleasantries. Use short bullet form. Aim for ~400-800 tokens.`;
|
|
6303
6373
|
}
|
|
6304
6374
|
});
|
|
6305
6375
|
|
|
@@ -7936,6 +8006,9 @@ function parseBlocks(src) {
|
|
|
7936
8006
|
}
|
|
7937
8007
|
out.push({ kind: "paragraph", text: paraLines.join("\n") });
|
|
7938
8008
|
}
|
|
8009
|
+
while (out.length > 0 && out[out.length - 1].kind === "blank") {
|
|
8010
|
+
out.pop();
|
|
8011
|
+
}
|
|
7939
8012
|
return out;
|
|
7940
8013
|
}
|
|
7941
8014
|
function renderInline(src, theme) {
|
|
@@ -8382,7 +8455,7 @@ function StatusBar({ model, usage, sessionUsage, thinking, turnStartedAt, mode,
|
|
|
8382
8455
|
] }),
|
|
8383
8456
|
/* @__PURE__ */ jsx7(Text6, { children: " " }),
|
|
8384
8457
|
thinking ? /* @__PURE__ */ jsxs6(Text6, { color: theme.spinner, children: [
|
|
8385
|
-
/* @__PURE__ */ jsx7(Spinner3, { type: "
|
|
8458
|
+
/* @__PURE__ */ jsx7(Spinner3, { type: "dots2" }),
|
|
8386
8459
|
" ",
|
|
8387
8460
|
phaseLabel,
|
|
8388
8461
|
elapsed ? ` \xB7 ${elapsed}` : "",
|
|
@@ -8700,7 +8773,7 @@ function TaskRow({ task }) {
|
|
|
8700
8773
|
if (task.status === "in_progress") {
|
|
8701
8774
|
return /* @__PURE__ */ jsxs10(Text10, { color: theme.accent, bold: true, children: [
|
|
8702
8775
|
" ",
|
|
8703
|
-
/* @__PURE__ */ jsx11(Spinner4, { type: "
|
|
8776
|
+
/* @__PURE__ */ jsx11(Spinner4, { type: "line" }),
|
|
8704
8777
|
" ",
|
|
8705
8778
|
task.title
|
|
8706
8779
|
] });
|
|
@@ -16373,7 +16446,7 @@ ${wcagWarnings.join("\n")}` }
|
|
|
16373
16446
|
if (!shouldCompact({ messages })) return messages;
|
|
16374
16447
|
if (compiledContextRef.current) {
|
|
16375
16448
|
const store = artifactStoreRef.current;
|
|
16376
|
-
const result =
|
|
16449
|
+
const result = compactMessages({
|
|
16377
16450
|
messages,
|
|
16378
16451
|
state: sessionStateRef.current,
|
|
16379
16452
|
store
|
|
@@ -16418,7 +16491,7 @@ ${wcagWarnings.join("\n")}` }
|
|
|
16418
16491
|
}
|
|
16419
16492
|
if (cfg && !signal.aborted) {
|
|
16420
16493
|
try {
|
|
16421
|
-
const result = await
|
|
16494
|
+
const result = await compactMessages2({
|
|
16422
16495
|
accountId: cfg.accountId,
|
|
16423
16496
|
apiToken: cfg.apiToken,
|
|
16424
16497
|
model: cfg.model,
|
|
@@ -16589,7 +16662,7 @@ ${wcagWarnings.join("\n")}` }
|
|
|
16589
16662
|
try {
|
|
16590
16663
|
if (compiledContextRef.current) {
|
|
16591
16664
|
const store = artifactStoreRef.current;
|
|
16592
|
-
const result =
|
|
16665
|
+
const result = compactMessages({
|
|
16593
16666
|
messages: messagesRef.current,
|
|
16594
16667
|
state: sessionStateRef.current,
|
|
16595
16668
|
store
|
|
@@ -16618,7 +16691,7 @@ ${wcagWarnings.join("\n")}` }
|
|
|
16618
16691
|
await saveSessionSafe();
|
|
16619
16692
|
}
|
|
16620
16693
|
} else {
|
|
16621
|
-
const result = await
|
|
16694
|
+
const result = await compactMessages2({
|
|
16622
16695
|
accountId: cfg.accountId,
|
|
16623
16696
|
apiToken: cfg.apiToken,
|
|
16624
16697
|
model: cfg.model,
|
|
@@ -17021,6 +17094,10 @@ ${wcagWarnings.join("\n")}` }
|
|
|
17021
17094
|
return true;
|
|
17022
17095
|
}
|
|
17023
17096
|
if (c === "/clear") {
|
|
17097
|
+
if (busy) {
|
|
17098
|
+
setEvents((e) => [...e, { kind: "info", key: mkKey(), text: "can't /clear while model is running \u2014 press Esc to interrupt first" }]);
|
|
17099
|
+
return true;
|
|
17100
|
+
}
|
|
17024
17101
|
if (cacheStableRef.current && messagesRef.current.length >= 2) {
|
|
17025
17102
|
messagesRef.current = [messagesRef.current[0], messagesRef.current[1]];
|
|
17026
17103
|
} else {
|
|
@@ -17030,6 +17107,15 @@ ${wcagWarnings.join("\n")}` }
|
|
|
17030
17107
|
sessionStateRef.current = emptySessionState();
|
|
17031
17108
|
artifactStoreRef.current = new ArtifactStore();
|
|
17032
17109
|
executorRef.current.clearArtifacts();
|
|
17110
|
+
if (flushTimeoutRef.current) {
|
|
17111
|
+
clearTimeout(flushTimeoutRef.current);
|
|
17112
|
+
flushTimeoutRef.current = null;
|
|
17113
|
+
}
|
|
17114
|
+
pendingTextRef.current.clear();
|
|
17115
|
+
activeAsstIdRef.current = null;
|
|
17116
|
+
pendingToolCallsRef.current.clear();
|
|
17117
|
+
usageRef.current = null;
|
|
17118
|
+
turnCounterRef.current = 0;
|
|
17033
17119
|
setEvents([]);
|
|
17034
17120
|
setUsage(null);
|
|
17035
17121
|
setSessionUsage(null);
|
|
@@ -18254,7 +18340,7 @@ ${lines.join("\n")}` }]);
|
|
|
18254
18340
|
if (shouldCompact({ messages: messagesRef.current })) {
|
|
18255
18341
|
if (compiledContextRef.current) {
|
|
18256
18342
|
const store = artifactStoreRef.current;
|
|
18257
|
-
const result =
|
|
18343
|
+
const result = compactMessages({
|
|
18258
18344
|
messages: messagesRef.current,
|
|
18259
18345
|
state: sessionStateRef.current,
|
|
18260
18346
|
store
|
|
@@ -18274,7 +18360,7 @@ ${lines.join("\n")}` }]);
|
|
|
18274
18360
|
}
|
|
18275
18361
|
} else {
|
|
18276
18362
|
try {
|
|
18277
|
-
const result = await
|
|
18363
|
+
const result = await compactMessages2({
|
|
18278
18364
|
accountId: cfg.accountId,
|
|
18279
18365
|
apiToken: cfg.apiToken,
|
|
18280
18366
|
model: cfg.model,
|