kimiflare 0.68.1 → 0.68.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -2703,6 +2703,10 @@ function deleteOrphanedSkills(db, existingPaths) {
2703
2703
  const result = db.prepare(`DELETE FROM skill_index WHERE file_path NOT IN (${placeholders})`).run(...existingPaths);
2704
2704
  return Number(result.changes);
2705
2705
  }
2706
+ function hasAnySections(db) {
2707
+ const row = db.prepare("SELECT 1 FROM skill_sections LIMIT 1").get();
2708
+ return row !== void 0;
2709
+ }
2706
2710
  function listAllSectionRows(db) {
2707
2711
  return db.prepare(
2708
2712
  `SELECT s.id, s.heading, s.body, s.embedding, i.name, i.description, i.file_path
@@ -2728,6 +2732,7 @@ var init_db = __esm({
2728
2732
 
2729
2733
  // src/skills/search.ts
2730
2734
  async function searchSections(query, db, opts2) {
2735
+ if (!hasAnySections(db)) return [];
2731
2736
  const embeddings = await fetchEmbeddings({
2732
2737
  accountId: opts2.accountId,
2733
2738
  apiToken: opts2.apiToken,
@@ -3162,6 +3167,15 @@ function getSessionWebFetchHistory(sessionId) {
3162
3167
  function isHighSignalMemory(memory) {
3163
3168
  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;
3164
3169
  }
3170
+ function extractLastUserText(messages) {
3171
+ const lastUser = [...messages].reverse().find((m) => m.role === "user");
3172
+ if (!lastUser) return "";
3173
+ if (typeof lastUser.content === "string") return lastUser.content;
3174
+ if (Array.isArray(lastUser.content)) {
3175
+ return lastUser.content.filter((p) => p.type === "text").map((p) => p.text).join(" ");
3176
+ }
3177
+ return "";
3178
+ }
3165
3179
  function raceWithSignal(promise, signal) {
3166
3180
  return Promise.race([
3167
3181
  promise,
@@ -3181,80 +3195,74 @@ async function runAgentTurn(opts2) {
3181
3195
  const codeMode = opts2.codeMode ?? false;
3182
3196
  let memoryRecalledCount = 0;
3183
3197
  let skillResult;
3184
- if (opts2.sessionStartRecall) {
3185
- try {
3186
- const results = await raceWithSignal(opts2.sessionStartRecall, opts2.signal);
3187
- if (results.length > 0 && opts2.memoryManager) {
3188
- const text = await raceWithSignal(
3189
- opts2.memoryManager.synthesizeRecalled(results, opts2.signal),
3190
- opts2.signal
3191
- );
3192
- memoryRecalledCount = results.length;
3193
- const lastSystemIdx = opts2.messages.findLastIndex((m) => m.role === "system");
3194
- const insertIdx = lastSystemIdx >= 0 ? lastSystemIdx + 1 : opts2.messages.length;
3195
- opts2.messages.splice(insertIdx, 0, { role: "system", content: text });
3196
- opts2.callbacks.onMemoryRecalled?.(results.length);
3197
- }
3198
- } catch (err) {
3199
- if (err instanceof DOMException && err.name === "AbortError") throw err;
3200
- }
3201
- }
3202
- if (opts2.signal.aborted) {
3203
- throw new DOMException("aborted", "AbortError");
3204
- }
3205
- if (opts2.skillsDb && opts2.skillRoutingConfig && opts2.intentClassification) {
3206
- try {
3207
- const lastUserMsg = [...opts2.messages].reverse().find((m) => m.role === "user");
3208
- const prompt = typeof lastUserMsg?.content === "string" ? lastUserMsg.content : Array.isArray(lastUserMsg?.content) ? lastUserMsg.content.filter((p) => p.type === "text").map((p) => p.text).join(" ") : "";
3209
- if (prompt) {
3210
- skillResult = await raceWithSignal(
3211
- selectSkills(
3212
- {
3213
- prompt,
3214
- tier: opts2.intentClassification.tier,
3215
- maxSkillTokens: opts2.skillRoutingConfig.maxSkillTokens ?? 25e4 - 1e4
3216
- },
3217
- {
3218
- db: opts2.skillsDb,
3219
- accountId: opts2.skillRoutingConfig.accountId,
3220
- apiToken: opts2.skillRoutingConfig.apiToken,
3221
- embeddingModel: opts2.skillRoutingConfig.embeddingModel,
3222
- gateway: opts2.skillRoutingConfig.gateway,
3223
- cloudMode: opts2.skillRoutingConfig.cloudMode,
3224
- cloudToken: opts2.skillRoutingConfig.cloudToken,
3225
- cloudDeviceId: opts2.skillRoutingConfig.cloudDeviceId
3226
- }
3227
- ),
3228
- opts2.signal
3229
- );
3230
- opts2.callbacks.onSkillsSelected?.(skillResult);
3231
- const allTools = opts2.tools;
3232
- if (opts2.cacheStable) {
3233
- opts2.messages[1] = {
3234
- role: "system",
3235
- content: buildSessionPrefix({
3236
- cwd: opts2.cwd,
3237
- tools: allTools,
3238
- model: opts2.model,
3239
- mode: opts2.mode,
3240
- skillContext: skillResult.skillContext
3241
- })
3242
- };
3243
- } else {
3244
- opts2.messages[0] = {
3245
- role: "system",
3246
- content: buildSystemPrompt({
3247
- cwd: opts2.cwd,
3248
- tools: allTools,
3249
- model: opts2.model,
3250
- mode: opts2.mode,
3251
- skillContext: skillResult.skillContext
3252
- })
3253
- };
3254
- }
3255
- }
3256
- } catch (err) {
3257
- if (err instanceof DOMException && err.name === "AbortError") throw err;
3198
+ const lastUserPrompt = extractLastUserText(opts2.messages);
3199
+ const skipSkillRouting = opts2.intentClassification?.tier === "light" && lastUserPrompt.length < 40;
3200
+ const recallPromise = opts2.sessionStartRecall && opts2.memoryManager ? (async () => {
3201
+ const results = await opts2.sessionStartRecall;
3202
+ if (results.length === 0 || !opts2.memoryManager) return null;
3203
+ const text = await opts2.memoryManager.synthesizeRecalled(results, opts2.signal);
3204
+ return { text, count: results.length };
3205
+ })() : Promise.resolve(null);
3206
+ const skillsPromise = opts2.skillsDb && opts2.skillRoutingConfig && opts2.intentClassification && lastUserPrompt && !skipSkillRouting ? selectSkills(
3207
+ {
3208
+ prompt: lastUserPrompt,
3209
+ tier: opts2.intentClassification.tier,
3210
+ maxSkillTokens: opts2.skillRoutingConfig.maxSkillTokens ?? 25e4 - 1e4
3211
+ },
3212
+ {
3213
+ db: opts2.skillsDb,
3214
+ accountId: opts2.skillRoutingConfig.accountId,
3215
+ apiToken: opts2.skillRoutingConfig.apiToken,
3216
+ embeddingModel: opts2.skillRoutingConfig.embeddingModel,
3217
+ gateway: opts2.skillRoutingConfig.gateway,
3218
+ cloudMode: opts2.skillRoutingConfig.cloudMode,
3219
+ cloudToken: opts2.skillRoutingConfig.cloudToken,
3220
+ cloudDeviceId: opts2.skillRoutingConfig.cloudDeviceId
3221
+ }
3222
+ ) : Promise.resolve(void 0);
3223
+ const [recallSettled, skillsSettled] = await Promise.allSettled([
3224
+ raceWithSignal(recallPromise, opts2.signal),
3225
+ raceWithSignal(skillsPromise, opts2.signal)
3226
+ ]);
3227
+ for (const settled of [recallSettled, skillsSettled]) {
3228
+ if (settled.status === "rejected" && settled.reason instanceof DOMException && settled.reason.name === "AbortError") {
3229
+ throw settled.reason;
3230
+ }
3231
+ }
3232
+ if (recallSettled.status === "fulfilled" && recallSettled.value) {
3233
+ const { text, count } = recallSettled.value;
3234
+ const lastSystemIdx = opts2.messages.findLastIndex((m) => m.role === "system");
3235
+ const insertIdx = lastSystemIdx >= 0 ? lastSystemIdx + 1 : opts2.messages.length;
3236
+ opts2.messages.splice(insertIdx, 0, { role: "system", content: text });
3237
+ memoryRecalledCount = count;
3238
+ opts2.callbacks.onMemoryRecalled?.(count);
3239
+ }
3240
+ if (skillsSettled.status === "fulfilled" && skillsSettled.value) {
3241
+ skillResult = skillsSettled.value;
3242
+ opts2.callbacks.onSkillsSelected?.(skillResult);
3243
+ const allTools = opts2.tools;
3244
+ if (opts2.cacheStable) {
3245
+ opts2.messages[1] = {
3246
+ role: "system",
3247
+ content: buildSessionPrefix({
3248
+ cwd: opts2.cwd,
3249
+ tools: allTools,
3250
+ model: opts2.model,
3251
+ mode: opts2.mode,
3252
+ skillContext: skillResult.skillContext
3253
+ })
3254
+ };
3255
+ } else {
3256
+ opts2.messages[0] = {
3257
+ role: "system",
3258
+ content: buildSystemPrompt({
3259
+ cwd: opts2.cwd,
3260
+ tools: allTools,
3261
+ model: opts2.model,
3262
+ mode: opts2.mode,
3263
+ skillContext: skillResult.skillContext
3264
+ })
3265
+ };
3258
3266
  }
3259
3267
  }
3260
3268
  if (opts2.signal.aborted) {
@@ -10318,69 +10326,6 @@ var init_rpc = __esm({
10318
10326
  }
10319
10327
  });
10320
10328
 
10321
- // src/agent/supervisor.ts
10322
- var TurnSupervisor;
10323
- var init_supervisor = __esm({
10324
- "src/agent/supervisor.ts"() {
10325
- "use strict";
10326
- init_loop();
10327
- init_logger();
10328
- TurnSupervisor = class {
10329
- currentTurn = null;
10330
- _phase = "idle";
10331
- _killRequested = false;
10332
- get phase() {
10333
- return this._phase;
10334
- }
10335
- get isRunning() {
10336
- return this._phase !== "idle";
10337
- }
10338
- get killRequested() {
10339
- return this._killRequested;
10340
- }
10341
- startTurn(opts2, callbacks) {
10342
- if (this.isRunning) {
10343
- logger.warn("supervisor:start_rejected", { reason: "turn_already_running", phase: this._phase });
10344
- throw new Error("TurnSupervisor: turn already in progress");
10345
- }
10346
- this._phase = "preparing";
10347
- this._killRequested = false;
10348
- logger.debug("supervisor:turn_start", { sessionId: opts2.sessionId });
10349
- this.currentTurn = runAgentTurn(opts2).then(async () => {
10350
- this._phase = "idle";
10351
- if (this._killRequested) {
10352
- logger.debug("supervisor:turn_killed", { sessionId: opts2.sessionId });
10353
- } else {
10354
- logger.debug("supervisor:turn_done", { sessionId: opts2.sessionId });
10355
- }
10356
- await callbacks?.onDone?.();
10357
- }).catch(async (error) => {
10358
- this._phase = "idle";
10359
- const err = error;
10360
- logger.warn("supervisor:turn_error", {
10361
- sessionId: opts2.sessionId,
10362
- error: err.message ?? String(err),
10363
- name: err.name
10364
- });
10365
- await callbacks?.onError?.(err);
10366
- }).finally(() => {
10367
- this.currentTurn = null;
10368
- this._killRequested = false;
10369
- });
10370
- }
10371
- /** Request that the current turn be killed. This does NOT directly abort
10372
- * the turn — the caller must abort the AbortScope that was passed to
10373
- * `startTurn`. This method only records the intent so the supervisor
10374
- * knows the turn was intentionally killed rather than failing. */
10375
- killTurn() {
10376
- if (!this.isRunning) return;
10377
- this._killRequested = true;
10378
- logger.debug("supervisor:kill_requested", { phase: this._phase });
10379
- }
10380
- };
10381
- }
10382
- });
10383
-
10384
10329
  // src/agent/llm-summarize.ts
10385
10330
  function indexOfNthUserFromEnd(messages, n) {
10386
10331
  let seen = 0;
@@ -18537,6 +18482,346 @@ var init_modal_host = __esm({
18537
18482
  }
18538
18483
  });
18539
18484
 
18485
+ // src/ui/use-session-manager.ts
18486
+ import { useCallback as useCallback6, useRef as useRef5, useState as useState16 } from "react";
18487
+ function extractFirstUserText(messages) {
18488
+ const firstUser = messages.find((m) => m.role === "user");
18489
+ if (!firstUser) return "session";
18490
+ if (typeof firstUser.content === "string") {
18491
+ return firstUser.content || "session";
18492
+ }
18493
+ if (Array.isArray(firstUser.content)) {
18494
+ const textPart = firstUser.content.find((p) => p.type === "text");
18495
+ if (textPart?.text) return textPart.text;
18496
+ }
18497
+ return "session";
18498
+ }
18499
+ function useSessionManager(deps) {
18500
+ const sessionIdRef = useRef5(null);
18501
+ const sessionCreatedAtRef = useRef5(null);
18502
+ const sessionTitleRef = useRef5(null);
18503
+ const [resumeSessions, setResumeSessions] = useState16(null);
18504
+ const [checkpointSession, setCheckpointSession] = useState16(null);
18505
+ const [checkpointList, setCheckpointList] = useState16([]);
18506
+ const depsRef = useRef5(deps);
18507
+ depsRef.current = deps;
18508
+ const ensureSessionId = useCallback6(() => {
18509
+ if (sessionIdRef.current) return sessionIdRef.current;
18510
+ const text = extractFirstUserText(depsRef.current.messagesRef.current);
18511
+ sessionIdRef.current = makeSessionId(text);
18512
+ return sessionIdRef.current;
18513
+ }, []);
18514
+ const saveSessionSafe = useCallback6(async () => {
18515
+ const d = depsRef.current;
18516
+ if (!d.cfg) return;
18517
+ ensureSessionId();
18518
+ const now2 = (/* @__PURE__ */ new Date()).toISOString();
18519
+ if (!sessionCreatedAtRef.current) {
18520
+ sessionCreatedAtRef.current = now2;
18521
+ }
18522
+ try {
18523
+ await saveSession({
18524
+ id: sessionIdRef.current,
18525
+ cwd: process.cwd(),
18526
+ model: d.cfg.model,
18527
+ createdAt: sessionCreatedAtRef.current,
18528
+ updatedAt: now2,
18529
+ title: sessionTitleRef.current ?? void 0,
18530
+ messages: d.messagesRef.current,
18531
+ sessionState: d.compiledContextRef.current ? d.sessionStateRef.current : void 0,
18532
+ artifactStore: serializeArtifactStore(d.artifactStoreRef.current)
18533
+ });
18534
+ } catch (e) {
18535
+ d.setEvents((es) => [
18536
+ ...es,
18537
+ { kind: "error", key: d.mkKey(), text: `session save failed: ${e.message}` }
18538
+ ]);
18539
+ }
18540
+ }, [ensureSessionId]);
18541
+ const openResumePicker = useCallback6(async () => {
18542
+ const sessions = await listSessions(200, process.cwd());
18543
+ setResumeSessions(sessions);
18544
+ }, []);
18545
+ const doResumeSession = useCallback6(
18546
+ async (filePath, checkpointId) => {
18547
+ const d = depsRef.current;
18548
+ try {
18549
+ const file = checkpointId ? (await loadSessionFromCheckpoint(filePath, checkpointId)).file : await loadSession(filePath);
18550
+ d.messagesRef.current = file.messages;
18551
+ sessionIdRef.current = file.id;
18552
+ sessionCreatedAtRef.current = file.createdAt;
18553
+ if (file.sessionState && d.compiledContextRef.current) {
18554
+ d.sessionStateRef.current = file.sessionState;
18555
+ }
18556
+ if (file.artifactStore) {
18557
+ d.artifactStoreRef.current = deserializeArtifactStore(file.artifactStore);
18558
+ } else {
18559
+ d.artifactStoreRef.current = new ArtifactStore();
18560
+ }
18561
+ const manager = d.memoryManagerRef.current;
18562
+ if (manager) {
18563
+ try {
18564
+ const cwd = process.cwd();
18565
+ const results = await manager.recall({ text: cwd, repoPath: cwd, limit: 5 });
18566
+ if (results.length > 0) {
18567
+ const text = await manager.synthesizeRecalled(results);
18568
+ const lastSystemIdx = d.messagesRef.current.findLastIndex((m) => m.role === "system");
18569
+ const insertIdx = lastSystemIdx >= 0 ? lastSystemIdx + 1 : d.messagesRef.current.length;
18570
+ d.messagesRef.current.splice(insertIdx, 0, { role: "system", content: text });
18571
+ }
18572
+ } catch {
18573
+ }
18574
+ }
18575
+ const msg = checkpointId ? `resumed session ${file.id} from checkpoint` : `resumed session ${file.id} (${file.messages.filter((m) => m.role !== "system").length} msgs)`;
18576
+ d.setEvents([{ kind: "info", key: d.mkKey(), text: msg }]);
18577
+ const userMsgs = file.messages.filter((m) => m.role === "user" && m.content).map((m) => {
18578
+ if (!m.content) return "";
18579
+ if (typeof m.content === "string") return m.content;
18580
+ const textPart = m.content.find((p) => p.type === "text");
18581
+ return textPart?.text ?? "";
18582
+ }).filter((text) => text.length > 0);
18583
+ if (userMsgs.length > 0) d.setHistory(userMsgs);
18584
+ d.setUsage(null);
18585
+ d.setSessionUsage(null);
18586
+ d.gatewayMetaRef.current = null;
18587
+ d.setGatewayMeta(null);
18588
+ void getCostReport(file.id).then((report) => d.setSessionUsage(report.session));
18589
+ } catch (e) {
18590
+ d.setEvents((es) => [
18591
+ ...es,
18592
+ { kind: "error", key: d.mkKey(), text: `failed to load session: ${e.message}` }
18593
+ ]);
18594
+ }
18595
+ },
18596
+ []
18597
+ );
18598
+ const handleResumePick = useCallback6(
18599
+ async (picked) => {
18600
+ setResumeSessions(null);
18601
+ if (!picked) return;
18602
+ if (picked.checkpointCount > 0) {
18603
+ try {
18604
+ const file = await loadSession(picked.filePath);
18605
+ setCheckpointList(file.checkpoints ?? []);
18606
+ setCheckpointSession(picked);
18607
+ } catch (e) {
18608
+ depsRef.current.setEvents((es) => [
18609
+ ...es,
18610
+ { kind: "error", key: depsRef.current.mkKey(), text: `failed to load checkpoints: ${e.message}` }
18611
+ ]);
18612
+ await doResumeSession(picked.filePath);
18613
+ }
18614
+ return;
18615
+ }
18616
+ await doResumeSession(picked.filePath);
18617
+ },
18618
+ [doResumeSession]
18619
+ );
18620
+ const handleCheckpointPick = useCallback6(
18621
+ async (checkpointId) => {
18622
+ const session = checkpointSession;
18623
+ setCheckpointSession(null);
18624
+ setCheckpointList([]);
18625
+ if (!session || !checkpointId) {
18626
+ if (session) {
18627
+ setResumeSessions(await listSessions(200, process.cwd()));
18628
+ }
18629
+ return;
18630
+ }
18631
+ if (checkpointId === "__start__") {
18632
+ await doResumeSession(session.filePath);
18633
+ return;
18634
+ }
18635
+ await doResumeSession(session.filePath, checkpointId);
18636
+ },
18637
+ [checkpointSession, doResumeSession]
18638
+ );
18639
+ const resetSession = useCallback6(() => {
18640
+ sessionIdRef.current = null;
18641
+ sessionCreatedAtRef.current = null;
18642
+ sessionTitleRef.current = null;
18643
+ const d = depsRef.current;
18644
+ d.sessionStateRef.current = emptySessionState();
18645
+ d.artifactStoreRef.current = new ArtifactStore();
18646
+ }, []);
18647
+ return {
18648
+ sessionIdRef,
18649
+ sessionCreatedAtRef,
18650
+ sessionTitleRef,
18651
+ resumeSessions,
18652
+ setResumeSessions,
18653
+ checkpointSession,
18654
+ setCheckpointSession,
18655
+ checkpointList,
18656
+ setCheckpointList,
18657
+ hasPickerOpen: resumeSessions !== null || checkpointSession !== null,
18658
+ ensureSessionId,
18659
+ saveSessionSafe,
18660
+ openResumePicker,
18661
+ doResumeSession,
18662
+ handleResumePick,
18663
+ handleCheckpointPick,
18664
+ resetSession
18665
+ };
18666
+ }
18667
+ var init_use_session_manager = __esm({
18668
+ "src/ui/use-session-manager.ts"() {
18669
+ "use strict";
18670
+ init_sessions();
18671
+ init_session_state();
18672
+ init_usage_tracker();
18673
+ }
18674
+ });
18675
+
18676
+ // src/agent/supervisor.ts
18677
+ var TurnSupervisor;
18678
+ var init_supervisor = __esm({
18679
+ "src/agent/supervisor.ts"() {
18680
+ "use strict";
18681
+ init_loop();
18682
+ init_logger();
18683
+ TurnSupervisor = class {
18684
+ currentTurn = null;
18685
+ _phase = "idle";
18686
+ _killRequested = false;
18687
+ get phase() {
18688
+ return this._phase;
18689
+ }
18690
+ get isRunning() {
18691
+ return this._phase !== "idle";
18692
+ }
18693
+ get killRequested() {
18694
+ return this._killRequested;
18695
+ }
18696
+ startTurn(opts2, callbacks) {
18697
+ if (this.isRunning) {
18698
+ logger.warn("supervisor:start_rejected", { reason: "turn_already_running", phase: this._phase });
18699
+ throw new Error("TurnSupervisor: turn already in progress");
18700
+ }
18701
+ this._phase = "preparing";
18702
+ this._killRequested = false;
18703
+ logger.debug("supervisor:turn_start", { sessionId: opts2.sessionId });
18704
+ this.currentTurn = runAgentTurn(opts2).then(async () => {
18705
+ this._phase = "idle";
18706
+ if (this._killRequested) {
18707
+ logger.debug("supervisor:turn_killed", { sessionId: opts2.sessionId });
18708
+ } else {
18709
+ logger.debug("supervisor:turn_done", { sessionId: opts2.sessionId });
18710
+ }
18711
+ await callbacks?.onDone?.();
18712
+ }).catch(async (error) => {
18713
+ this._phase = "idle";
18714
+ const err = error;
18715
+ logger.warn("supervisor:turn_error", {
18716
+ sessionId: opts2.sessionId,
18717
+ error: err.message ?? String(err),
18718
+ name: err.name
18719
+ });
18720
+ await callbacks?.onError?.(err);
18721
+ }).finally(() => {
18722
+ this.currentTurn = null;
18723
+ this._killRequested = false;
18724
+ });
18725
+ }
18726
+ /** Request that the current turn be killed. This does NOT directly abort
18727
+ * the turn — the caller must abort the AbortScope that was passed to
18728
+ * `startTurn`. This method only records the intent so the supervisor
18729
+ * knows the turn was intentionally killed rather than failing. */
18730
+ killTurn() {
18731
+ if (!this.isRunning) return;
18732
+ this._killRequested = true;
18733
+ logger.debug("supervisor:kill_requested", { phase: this._phase });
18734
+ }
18735
+ };
18736
+ }
18737
+ });
18738
+
18739
+ // src/ui/use-turn-controller.ts
18740
+ import { useCallback as useCallback7, useRef as useRef6, useState as useState17 } from "react";
18741
+ function useTurnController() {
18742
+ const [busy, setBusy] = useState17(false);
18743
+ const busyRef = useRef6(false);
18744
+ const isAbortingRef = useRef6(false);
18745
+ const lastEscapeAtRef = useRef6(0);
18746
+ const supervisorRef = useRef6(new TurnSupervisor());
18747
+ const [turnPhase, setTurnPhase] = useState17("waiting");
18748
+ const [turnStartedAt, setTurnStartedAt] = useState17(null);
18749
+ const [currentToolName, setCurrentToolName] = useState17(null);
18750
+ const [lastActivityAt, setLastActivityAt] = useState17(null);
18751
+ const turnCounterRef = useRef6(0);
18752
+ const [showReasoning, setShowReasoning] = useState17(false);
18753
+ const toggleReasoning = useCallback7(() => {
18754
+ setShowReasoning((s) => !s);
18755
+ }, []);
18756
+ const [tasks, setTasks] = useState17([]);
18757
+ const tasksRef = useRef6([]);
18758
+ const [tasksStartedAt, setTasksStartedAt] = useState17(null);
18759
+ const [tasksStartTokens, setTasksStartTokens] = useState17(0);
18760
+ const beginTurn = useCallback7(() => {
18761
+ setBusy(true);
18762
+ busyRef.current = true;
18763
+ setTurnStartedAt(Date.now());
18764
+ }, []);
18765
+ const endTurn = useCallback7(() => {
18766
+ setBusy(false);
18767
+ busyRef.current = false;
18768
+ setTurnStartedAt(null);
18769
+ setTurnPhase("waiting");
18770
+ setCurrentToolName(null);
18771
+ setLastActivityAt(null);
18772
+ isAbortingRef.current = false;
18773
+ }, []);
18774
+ const clearTaskTracking = useCallback7(() => {
18775
+ setTasks([]);
18776
+ setTasksStartedAt(null);
18777
+ setTasksStartTokens(0);
18778
+ tasksRef.current = [];
18779
+ }, []);
18780
+ const markAborting = useCallback7(() => {
18781
+ if (isAbortingRef.current) return false;
18782
+ isAbortingRef.current = true;
18783
+ supervisorRef.current.killTurn();
18784
+ return true;
18785
+ }, []);
18786
+ return {
18787
+ busy,
18788
+ setBusy,
18789
+ busyRef,
18790
+ isAbortingRef,
18791
+ lastEscapeAtRef,
18792
+ supervisorRef,
18793
+ turnPhase,
18794
+ setTurnPhase,
18795
+ turnStartedAt,
18796
+ setTurnStartedAt,
18797
+ currentToolName,
18798
+ setCurrentToolName,
18799
+ lastActivityAt,
18800
+ setLastActivityAt,
18801
+ turnCounterRef,
18802
+ showReasoning,
18803
+ setShowReasoning,
18804
+ toggleReasoning,
18805
+ tasks,
18806
+ setTasks,
18807
+ tasksRef,
18808
+ tasksStartedAt,
18809
+ setTasksStartedAt,
18810
+ tasksStartTokens,
18811
+ setTasksStartTokens,
18812
+ beginTurn,
18813
+ endTurn,
18814
+ clearTaskTracking,
18815
+ markAborting
18816
+ };
18817
+ }
18818
+ var init_use_turn_controller = __esm({
18819
+ "src/ui/use-turn-controller.ts"() {
18820
+ "use strict";
18821
+ init_supervisor();
18822
+ }
18823
+ });
18824
+
18540
18825
  // src/cost-attribution/tui-report.ts
18541
18826
  var tui_report_exports = {};
18542
18827
  __export(tui_report_exports, {
@@ -18624,7 +18909,7 @@ __export(app_exports, {
18624
18909
  buildFilePickerIgnoreList: () => buildFilePickerIgnoreList,
18625
18910
  renderApp: () => renderApp
18626
18911
  });
18627
- import React15, { useState as useState16, useRef as useRef5, useEffect as useEffect8, useCallback as useCallback6 } from "react";
18912
+ import React17, { useState as useState18, useRef as useRef7, useEffect as useEffect8, useCallback as useCallback8 } from "react";
18628
18913
  import { Box as Box26, Text as Text27, useApp, useInput as useInput10, render } from "ink";
18629
18914
  import { existsSync as existsSync4, statSync as statSync4 } from "fs";
18630
18915
  import { join as join28 } from "path";
@@ -18863,13 +19148,13 @@ function App({
18863
19148
  initialCloudDeviceId
18864
19149
  }) {
18865
19150
  const { exit } = useApp();
18866
- const [cfg, setCfg] = useState16(initialCfg);
18867
- const [lspScope, setLspScope] = useState16(initialLspScope);
18868
- const [lspProjectPath, setLspProjectPath] = useState16(initialLspProjectPath);
18869
- const [cloudToken, setCloudToken] = useState16(initialCloudToken);
18870
- const [cloudDeviceId, setCloudDeviceId] = useState16(initialCloudDeviceId);
18871
- const [events, setRawEvents] = useState16([]);
18872
- const setEvents = useCallback6(
19151
+ const [cfg, setCfg] = useState18(initialCfg);
19152
+ const [lspScope, setLspScope] = useState18(initialLspScope);
19153
+ const [lspProjectPath, setLspProjectPath] = useState18(initialLspProjectPath);
19154
+ const [cloudToken, setCloudToken] = useState18(initialCloudToken);
19155
+ const [cloudDeviceId, setCloudDeviceId] = useState18(initialCloudDeviceId);
19156
+ const [events, setRawEvents] = useState18([]);
19157
+ const setEvents = useCallback8(
18873
19158
  (updater) => {
18874
19159
  setRawEvents((prev) => {
18875
19160
  const next = typeof updater === "function" ? updater(prev) : updater;
@@ -18878,10 +19163,9 @@ function App({
18878
19163
  },
18879
19164
  []
18880
19165
  );
18881
- const [input, setInput] = useState16("");
18882
- const [busy, setBusy] = useState16(false);
18883
- const [usage, setUsage] = useState16(null);
18884
- const [sessionUsage, setSessionUsage] = useState16(null);
19166
+ const [input, setInput] = useState18("");
19167
+ const [usage, setUsage] = useState18(null);
19168
+ const [sessionUsage, setSessionUsage] = useState18(null);
18885
19169
  useEffect8(() => {
18886
19170
  const handler = (sid) => {
18887
19171
  if (sessionIdRef.current && sid === sessionIdRef.current) {
@@ -18893,9 +19177,35 @@ function App({
18893
19177
  usageEvents.off("update", handler);
18894
19178
  };
18895
19179
  }, []);
18896
- const [gatewayMeta, setGatewayMeta] = useState16(null);
18897
- const [cloudBudget, setCloudBudget] = useState16(null);
18898
- const [showReasoning, setShowReasoning] = useState16(false);
19180
+ const [gatewayMeta, setGatewayMeta] = useState18(null);
19181
+ const [cloudBudget, setCloudBudget] = useState18(null);
19182
+ const turn = useTurnController();
19183
+ const {
19184
+ busy,
19185
+ busyRef,
19186
+ isAbortingRef,
19187
+ lastEscapeAtRef,
19188
+ supervisorRef,
19189
+ turnPhase,
19190
+ setTurnPhase,
19191
+ turnStartedAt,
19192
+ currentToolName,
19193
+ setCurrentToolName,
19194
+ lastActivityAt,
19195
+ setLastActivityAt,
19196
+ turnCounterRef,
19197
+ showReasoning,
19198
+ tasks,
19199
+ setTasks,
19200
+ tasksRef,
19201
+ tasksStartedAt,
19202
+ setTasksStartedAt,
19203
+ tasksStartTokens,
19204
+ setTasksStartTokens,
19205
+ beginTurn,
19206
+ endTurn,
19207
+ clearTaskTracking
19208
+ } = turn;
18899
19209
  const {
18900
19210
  pending: perm,
18901
19211
  askPermission: askForPermission,
@@ -18941,38 +19251,28 @@ function App({
18941
19251
  hasFullscreenModal,
18942
19252
  hasAnyModal
18943
19253
  } = modals;
18944
- const [queue, setQueue] = useState16([]);
18945
- const [history, setHistory] = useState16([]);
18946
- const [historyIndex, setHistoryIndex] = useState16(-1);
18947
- const [draftInput, setDraftInput] = useState16("");
18948
- const [mode, setMode] = useState16("edit");
18949
- const [codeMode, setCodeMode] = useState16(false);
19254
+ const [queue, setQueue] = useState18([]);
19255
+ const [history, setHistory] = useState18([]);
19256
+ const [historyIndex, setHistoryIndex] = useState18(-1);
19257
+ const [draftInput, setDraftInput] = useState18("");
19258
+ const [mode, setMode] = useState18("edit");
19259
+ const [codeMode, setCodeMode] = useState18(false);
18950
19260
  const filePickerEnabled = initialCfg?.filePicker ?? true;
18951
- const [effort, setEffort] = useState16(
19261
+ const [effort, setEffort] = useState18(
18952
19262
  initialCfg?.reasoningEffort ?? DEFAULT_REASONING_EFFORT
18953
19263
  );
18954
- const [resumeSessions, setResumeSessions] = useState16(null);
18955
- const [checkpointSession, setCheckpointSession] = useState16(null);
18956
- const [checkpointList, setCheckpointList] = useState16([]);
18957
- const [selectedRemoteSession, setSelectedRemoteSession] = useState16(null);
18958
- const [tasks, setTasks] = useState16([]);
18959
- const [tasksStartedAt, setTasksStartedAt] = useState16(null);
18960
- const [tasksStartTokens, setTasksStartTokens] = useState16(0);
18961
- const [turnStartedAt, setTurnStartedAt] = useState16(null);
18962
- const [turnPhase, setTurnPhase] = useState16("waiting");
18963
- const [currentToolName, setCurrentToolName] = useState16(null);
18964
- const [lastActivityAt, setLastActivityAt] = useState16(null);
18965
- const [verbose, setVerbose] = useState16(false);
18966
- const [hasUpdate, setHasUpdate] = useState16(initialUpdateResult?.hasUpdate ?? false);
18967
- const [latestVersion, setLatestVersion] = useState16(initialUpdateResult?.latestVersion ?? null);
18968
- const [theme, setTheme] = useState16(resolveTheme(initialCfg?.theme));
18969
- const [originalTheme, setOriginalTheme] = useState16(null);
18970
- const [skillsActive, setSkillsActive] = useState16(0);
18971
- const [memoryRecalled, setMemoryRecalled] = useState16(false);
18972
- const [intentTier, setIntentTier] = useState16(null);
18973
- const [kimiMdStale, setKimiMdStale] = useState16(false);
18974
- const [gitBranch, setGitBranch] = useState16(null);
18975
- const [lastSessionTopic, setLastSessionTopic] = useState16(null);
19264
+ const [selectedRemoteSession, setSelectedRemoteSession] = useState18(null);
19265
+ const [verbose, setVerbose] = useState18(false);
19266
+ const [hasUpdate, setHasUpdate] = useState18(initialUpdateResult?.hasUpdate ?? false);
19267
+ const [latestVersion, setLatestVersion] = useState18(initialUpdateResult?.latestVersion ?? null);
19268
+ const [theme, setTheme] = useState18(resolveTheme(initialCfg?.theme));
19269
+ const [originalTheme, setOriginalTheme] = useState18(null);
19270
+ const [skillsActive, setSkillsActive] = useState18(0);
19271
+ const [memoryRecalled, setMemoryRecalled] = useState18(false);
19272
+ const [intentTier, setIntentTier] = useState18(null);
19273
+ const [kimiMdStale, setKimiMdStale] = useState18(false);
19274
+ const [gitBranch, setGitBranch] = useState18(null);
19275
+ const [lastSessionTopic, setLastSessionTopic] = useState18(null);
18976
19276
  useEffect8(() => {
18977
19277
  setGitBranch(detectGitBranch());
18978
19278
  }, []);
@@ -19048,57 +19348,80 @@ ${wcagWarnings.join("\n")}` }
19048
19348
  cancelled = true;
19049
19349
  };
19050
19350
  }, [cfg?.cloudMode, initialCloudToken]);
19051
- const [cursorOffset, setCursorOffset] = useState16(0);
19052
- const [customCommandsVersion, setCustomCommandsVersion] = useState16(0);
19053
- const cacheStableRef = useRef5(initialCfg?.cacheStablePrompts !== false);
19054
- const messagesRef = useRef5(
19351
+ const [cursorOffset, setCursorOffset] = useState18(0);
19352
+ const [customCommandsVersion, setCustomCommandsVersion] = useState18(0);
19353
+ const cacheStableRef = useRef7(initialCfg?.cacheStablePrompts !== false);
19354
+ const messagesRef = useRef7(
19055
19355
  makePrefixMessages(cacheStableRef.current, cfg?.model ?? DEFAULT_MODEL, "edit", ALL_TOOLS)
19056
19356
  );
19057
- const executorRef = useRef5(new ToolExecutor(ALL_TOOLS));
19058
- const activeAsstIdRef = useRef5(null);
19059
- const sessionScopeRef = useRef5(new AbortScope());
19060
- const activeScopeRef = useRef5(null);
19061
- const supervisorRef = useRef5(new TurnSupervisor());
19062
- const isAbortingRef = useRef5(false);
19063
- const lastEscapeAtRef = useRef5(0);
19064
- const sigintHandlerRef = useRef5(null);
19065
- const limitResolveRef = useRef5(null);
19066
- const loopResolveRef = useRef5(null);
19067
- const pendingToolCallsRef = useRef5(/* @__PURE__ */ new Map());
19068
- const sessionIdRef = useRef5(null);
19069
- const sessionCreatedAtRef = useRef5(null);
19070
- const sessionTitleRef = useRef5(null);
19071
- const modeRef = useRef5(mode);
19072
- const effortRef = useRef5(effort);
19073
- const tasksRef = useRef5([]);
19074
- const usageRef = useRef5(null);
19075
- const gatewayMetaRef = useRef5(null);
19076
- const lastApiErrorRef = useRef5(null);
19077
- const updateCheckedRef = useRef5(false);
19078
- const sessionStateRef = useRef5(emptySessionState());
19079
- const artifactStoreRef = useRef5(new ArtifactStore());
19080
- const compiledContextRef = useRef5(initialCfg?.compiledContext === true);
19081
- const updateNudgedRef = useRef5(false);
19082
- const compactSuggestedRef = useRef5(false);
19083
- const mcpManagerRef = useRef5(new McpManager());
19084
- const mcpToolsRef = useRef5([]);
19085
- const mcpInitRef = useRef5(false);
19086
- const submitRef = useRef5(() => {
19357
+ const executorRef = useRef7(new ToolExecutor(ALL_TOOLS));
19358
+ const activeAsstIdRef = useRef7(null);
19359
+ const sessionScopeRef = useRef7(new AbortScope());
19360
+ const activeScopeRef = useRef7(null);
19361
+ const sigintHandlerRef = useRef7(null);
19362
+ const limitResolveRef = useRef7(null);
19363
+ const loopResolveRef = useRef7(null);
19364
+ const pendingToolCallsRef = useRef7(/* @__PURE__ */ new Map());
19365
+ const modeRef = useRef7(mode);
19366
+ const effortRef = useRef7(effort);
19367
+ const usageRef = useRef7(null);
19368
+ const gatewayMetaRef = useRef7(null);
19369
+ const lastApiErrorRef = useRef7(null);
19370
+ const updateCheckedRef = useRef7(false);
19371
+ const sessionStateRef = useRef7(emptySessionState());
19372
+ const artifactStoreRef = useRef7(new ArtifactStore());
19373
+ const compiledContextRef = useRef7(initialCfg?.compiledContext === true);
19374
+ const updateNudgedRef = useRef7(false);
19375
+ const compactSuggestedRef = useRef7(false);
19376
+ const mcpManagerRef = useRef7(new McpManager());
19377
+ const mcpToolsRef = useRef7([]);
19378
+ const mcpInitRef = useRef7(false);
19379
+ const submitRef = useRef7(() => {
19380
+ });
19381
+ const lspManagerRef = useRef7(new LspManager());
19382
+ const lspToolsRef = useRef7([]);
19383
+ const lspInitRef = useRef7(false);
19384
+ const memoryManagerRef = useRef7(null);
19385
+ const sessionStartRecallRef = useRef7(null);
19386
+ const kimiMdStaleNudgedRef = useRef7(false);
19387
+ const sessionMgr = useSessionManager({
19388
+ cfg,
19389
+ messagesRef,
19390
+ sessionStateRef,
19391
+ artifactStoreRef,
19392
+ compiledContextRef,
19393
+ gatewayMetaRef,
19394
+ memoryManagerRef,
19395
+ setEvents,
19396
+ setHistory,
19397
+ setUsage,
19398
+ setSessionUsage,
19399
+ setGatewayMeta,
19400
+ mkKey
19087
19401
  });
19088
- const lspManagerRef = useRef5(new LspManager());
19089
- const lspToolsRef = useRef5([]);
19090
- const lspInitRef = useRef5(false);
19091
- const busyRef = useRef5(busy);
19092
- const memoryManagerRef = useRef5(null);
19093
- const sessionStartRecallRef = useRef5(null);
19094
- const kimiMdStaleNudgedRef = useRef5(false);
19095
- const turnCounterRef = useRef5(0);
19096
- const pendingTextRef = useRef5(/* @__PURE__ */ new Map());
19097
- const flushTimeoutRef = useRef5(null);
19098
- const customCommandsRef = useRef5([]);
19099
- const recentFilesRef = useRef5(/* @__PURE__ */ new Map());
19402
+ const {
19403
+ sessionIdRef,
19404
+ sessionCreatedAtRef,
19405
+ sessionTitleRef,
19406
+ resumeSessions,
19407
+ setResumeSessions,
19408
+ checkpointSession,
19409
+ setCheckpointSession,
19410
+ checkpointList,
19411
+ ensureSessionId,
19412
+ saveSessionSafe,
19413
+ openResumePicker,
19414
+ doResumeSession,
19415
+ handleResumePick,
19416
+ handleCheckpointPick,
19417
+ resetSession
19418
+ } = sessionMgr;
19419
+ const pendingTextRef = useRef7(/* @__PURE__ */ new Map());
19420
+ const flushTimeoutRef = useRef7(null);
19421
+ const customCommandsRef = useRef7([]);
19422
+ const recentFilesRef = useRef7(/* @__PURE__ */ new Map());
19100
19423
  const MAX_RECENT_FILES = 10;
19101
- const allSlashCommands = React15.useMemo(() => {
19424
+ const allSlashCommands = React17.useMemo(() => {
19102
19425
  const customs = customCommandsRef.current.filter((c) => !BUILTIN_COMMAND_NAMES.has(c.name.toLowerCase())).map((c) => ({
19103
19426
  name: c.name,
19104
19427
  description: c.description ?? "",
@@ -19107,7 +19430,7 @@ ${wcagWarnings.join("\n")}` }
19107
19430
  return [...BUILTIN_COMMANDS, ...customs];
19108
19431
  }, [customCommandsVersion]);
19109
19432
  const modalActive = commandWizard !== null || commandPicker !== null || commandToDelete !== null || showCommandList || showLspWizard || resumeSessions !== null || checkpointSession !== null || perm !== null || limitModal !== null || loopModal !== null || showInboxModal;
19110
- const loadFilePickerItems = useCallback6(async () => {
19433
+ const loadFilePickerItems = useCallback8(async () => {
19111
19434
  const cwd = process.cwd();
19112
19435
  const entries = await fg4("**/*", {
19113
19436
  cwd,
@@ -19264,7 +19587,7 @@ ${wcagWarnings.join("\n")}` }
19264
19587
  }, 3e5);
19265
19588
  return () => clearInterval(id);
19266
19589
  }, []);
19267
- const reloadCustomCommands = useCallback6(async () => {
19590
+ const reloadCustomCommands = useCallback8(async () => {
19268
19591
  const { commands, warnings } = await loadCustomCommands(process.cwd());
19269
19592
  customCommandsRef.current = commands;
19270
19593
  setCustomCommandsVersion((v) => v + 1);
@@ -19391,7 +19714,7 @@ ${wcagWarnings.join("\n")}` }
19391
19714
  }, 30 * 60 * 1e3);
19392
19715
  return () => clearInterval(id);
19393
19716
  }, [cfg]);
19394
- const initMcp = useCallback6(async () => {
19717
+ const initMcp = useCallback8(async () => {
19395
19718
  if (!cfg?.mcpServers || mcpInitRef.current) return;
19396
19719
  mcpInitRef.current = true;
19397
19720
  const manager = mcpManagerRef.current;
@@ -19456,7 +19779,7 @@ ${wcagWarnings.join("\n")}` }
19456
19779
  ]);
19457
19780
  }
19458
19781
  }, [cfg]);
19459
- const initLsp = useCallback6(async () => {
19782
+ const initLsp = useCallback8(async () => {
19460
19783
  if (!cfg?.lspEnabled || !cfg?.lspServers || lspInitRef.current) {
19461
19784
  if (lspInitRef.current) return;
19462
19785
  if (!cfg?.lspEnabled) {
@@ -19527,46 +19850,7 @@ ${wcagWarnings.join("\n")}` }
19527
19850
  void initLsp();
19528
19851
  }
19529
19852
  }, [cfg, initMcp, initLsp]);
19530
- const ensureSessionId = useCallback6(() => {
19531
- if (sessionIdRef.current) return sessionIdRef.current;
19532
- const firstUser = messagesRef.current.find((m) => m.role === "user");
19533
- let firstText = "session";
19534
- if (typeof firstUser?.content === "string") {
19535
- firstText = firstUser.content;
19536
- } else if (Array.isArray(firstUser?.content)) {
19537
- const textPart = firstUser.content.find((p) => p.type === "text");
19538
- if (textPart?.text) firstText = textPart.text;
19539
- }
19540
- sessionIdRef.current = makeSessionId(firstText);
19541
- return sessionIdRef.current;
19542
- }, []);
19543
- const saveSessionSafe = useCallback6(async () => {
19544
- if (!cfg) return;
19545
- ensureSessionId();
19546
- const now2 = (/* @__PURE__ */ new Date()).toISOString();
19547
- if (!sessionCreatedAtRef.current) {
19548
- sessionCreatedAtRef.current = now2;
19549
- }
19550
- try {
19551
- await saveSession({
19552
- id: sessionIdRef.current,
19553
- cwd: process.cwd(),
19554
- model: cfg.model,
19555
- createdAt: sessionCreatedAtRef.current,
19556
- updatedAt: now2,
19557
- title: sessionTitleRef.current ?? void 0,
19558
- messages: messagesRef.current,
19559
- sessionState: compiledContextRef.current ? sessionStateRef.current : void 0,
19560
- artifactStore: serializeArtifactStore(artifactStoreRef.current)
19561
- });
19562
- } catch (e) {
19563
- setEvents((es) => [
19564
- ...es,
19565
- { kind: "error", key: mkKey(), text: `session save failed: ${e.message}` }
19566
- ]);
19567
- }
19568
- }, [cfg, ensureSessionId]);
19569
- const onIterationEnd = useCallback6(
19853
+ const onIterationEnd = useCallback8(
19570
19854
  async (messages, signal) => {
19571
19855
  if (signal.aborted) return messages;
19572
19856
  if (!shouldCompact({ messages })) return messages;
@@ -19676,10 +19960,7 @@ ${wcagWarnings.join("\n")}` }
19676
19960
  }
19677
19961
  pendingToolCallsRef.current.clear();
19678
19962
  void saveSessionSafe();
19679
- setTasks([]);
19680
- setTasksStartedAt(null);
19681
- setTasksStartTokens(0);
19682
- tasksRef.current = [];
19963
+ clearTaskTracking();
19683
19964
  } else if (!hadPerm && !hadLimit && !hadLoop) {
19684
19965
  logger.info("input:ctrl+c:exiting");
19685
19966
  void lspManagerRef.current.stopAll().finally(() => exit());
@@ -19710,15 +19991,12 @@ ${wcagWarnings.join("\n")}` }
19710
19991
  updateTool(toolId, { status: "cancelled" });
19711
19992
  }
19712
19993
  pendingToolCallsRef.current.clear();
19713
- setTasks([]);
19714
- setTasksStartedAt(null);
19715
- setTasksStartTokens(0);
19716
- tasksRef.current = [];
19994
+ clearTaskTracking();
19717
19995
  return;
19718
19996
  }
19719
19997
  }
19720
19998
  if (key.ctrl && inputChar === "r") {
19721
- setShowReasoning((s) => !s);
19999
+ turn.toggleReasoning();
19722
20000
  return;
19723
20001
  }
19724
20002
  if (key.shift && key.tab) {
@@ -19758,16 +20036,13 @@ ${wcagWarnings.join("\n")}` }
19758
20036
  activeScopeRef.current.abort("user_stopped");
19759
20037
  setEvents((e) => [...e, { kind: "info", key: mkKey(), text: "(interrupted)" }]);
19760
20038
  void saveSessionSafe();
19761
- setTasks([]);
19762
- setTasksStartedAt(null);
19763
- setTasksStartTokens(0);
19764
- tasksRef.current = [];
20039
+ clearTaskTracking();
19765
20040
  } else if (!hadPerm && !hadLimit) {
19766
20041
  logger.info("sigint:handler:exiting");
19767
20042
  void lspManagerRef.current.stopAll().finally(() => exit());
19768
20043
  }
19769
20044
  };
19770
- const flushAssistantUpdates = useCallback6(() => {
20045
+ const flushAssistantUpdates = useCallback8(() => {
19771
20046
  flushTimeoutRef.current = null;
19772
20047
  const pending = pendingTextRef.current;
19773
20048
  if (pending.size === 0) return;
@@ -19785,7 +20060,7 @@ ${wcagWarnings.join("\n")}` }
19785
20060
  })
19786
20061
  );
19787
20062
  }, []);
19788
- const updateAssistant = useCallback6(
20063
+ const updateAssistant = useCallback8(
19789
20064
  (id, patch) => {
19790
20065
  const result = patch({ text: "", reasoning: "" });
19791
20066
  const assistantResult = result;
@@ -19814,7 +20089,7 @@ ${wcagWarnings.join("\n")}` }
19814
20089
  },
19815
20090
  [flushAssistantUpdates]
19816
20091
  );
19817
- const updateTool = useCallback6(
20092
+ const updateTool = useCallback8(
19818
20093
  (id, patch) => {
19819
20094
  setEvents(
19820
20095
  (evts) => evts.map(
@@ -19824,19 +20099,17 @@ ${wcagWarnings.join("\n")}` }
19824
20099
  },
19825
20100
  []
19826
20101
  );
19827
- const updateGatewayMeta = useCallback6((meta) => {
20102
+ const updateGatewayMeta = useCallback8((meta) => {
19828
20103
  gatewayMetaRef.current = meta;
19829
20104
  setGatewayMeta(meta);
19830
20105
  }, []);
19831
- const runCompact = useCallback6(async () => {
20106
+ const runCompact = useCallback8(async () => {
19832
20107
  if (!cfg) return;
19833
20108
  if (busy) {
19834
20109
  setEvents((e) => [...e, { kind: "info", key: mkKey(), text: "can't compact while model is running" }]);
19835
20110
  return;
19836
20111
  }
19837
- setBusy(true);
19838
- busyRef.current = true;
19839
- setTurnStartedAt(Date.now());
20112
+ beginTurn();
19840
20113
  const turnScope = sessionScopeRef.current.createChild();
19841
20114
  activeScopeRef.current = turnScope;
19842
20115
  try {
@@ -19911,24 +20184,14 @@ ${wcagWarnings.join("\n")}` }
19911
20184
  }
19912
20185
  } finally {
19913
20186
  logger.info("runCompact:finally");
19914
- setBusy(false);
19915
- busyRef.current = false;
19916
- setTurnStartedAt(null);
19917
- setTurnPhase("waiting");
19918
- setCurrentToolName(null);
19919
- setLastActivityAt(null);
20187
+ endTurn();
19920
20188
  activeScopeRef.current = null;
19921
- isAbortingRef.current = false;
19922
20189
  clearPermissionResolveRef();
19923
20190
  limitResolveRef.current = null;
19924
20191
  pendingToolCallsRef.current.clear();
19925
20192
  }
19926
20193
  }, [cfg, busy, saveSessionSafe]);
19927
- const openResumePicker = useCallback6(async () => {
19928
- const sessions = await listSessions(200, process.cwd());
19929
- setResumeSessions(sessions);
19930
- }, []);
19931
- const runInit = useCallback6(async () => {
20194
+ const runInit = useCallback8(async () => {
19932
20195
  if (!cfg) return;
19933
20196
  if (busy) {
19934
20197
  setEvents((e) => [...e, { kind: "info", key: mkKey(), text: "can't /init while model is running" }]);
@@ -19938,9 +20201,7 @@ ${wcagWarnings.join("\n")}` }
19938
20201
  const { prompt, targetFilename, isRefresh } = buildInitPrompt(cwd);
19939
20202
  setEvents((e) => [...e, { kind: "user", key: mkKey(), text: isRefresh ? `/init (refreshing ${targetFilename})` : "/init" }]);
19940
20203
  messagesRef.current.push({ role: "user", content: sanitizeString(prompt) });
19941
- setBusy(true);
19942
- busyRef.current = true;
19943
- setTurnStartedAt(Date.now());
20204
+ beginTurn();
19944
20205
  const turnScope = sessionScopeRef.current.createChild();
19945
20206
  activeScopeRef.current = turnScope;
19946
20207
  const initClassification = classifyIntent(prompt);
@@ -20207,15 +20468,9 @@ ${wcagWarnings.join("\n")}` }
20207
20468
  setCodeMode(false);
20208
20469
  const asstId = activeAsstIdRef.current;
20209
20470
  if (asstId !== null) updateAssistant(asstId, () => ({ streaming: false }));
20210
- setBusy(false);
20211
- busyRef.current = false;
20212
- setTurnStartedAt(null);
20213
- setTurnPhase("waiting");
20214
- setCurrentToolName(null);
20215
- setLastActivityAt(null);
20471
+ endTurn();
20216
20472
  activeAsstIdRef.current = null;
20217
20473
  activeScopeRef.current = null;
20218
- isAbortingRef.current = false;
20219
20474
  clearPermissionResolveRef();
20220
20475
  limitResolveRef.current = null;
20221
20476
  loopResolveRef.current = null;
@@ -20223,7 +20478,7 @@ ${wcagWarnings.join("\n")}` }
20223
20478
  pendingToolCallsRef.current.clear();
20224
20479
  }
20225
20480
  }, [cfg, busy, updateAssistant, updateTool, updateGatewayMeta]);
20226
- const handleThemePick = useCallback6(
20481
+ const handleThemePick = useCallback8(
20227
20482
  (picked) => {
20228
20483
  setShowThemePicker(false);
20229
20484
  if (!picked) return;
@@ -20241,100 +20496,7 @@ ${wcagWarnings.join("\n")}` }
20241
20496
  },
20242
20497
  []
20243
20498
  );
20244
- const doResumeSession = useCallback6(
20245
- async (filePath, checkpointId) => {
20246
- try {
20247
- const file = checkpointId ? (await loadSessionFromCheckpoint(filePath, checkpointId)).file : await loadSession(filePath);
20248
- messagesRef.current = file.messages;
20249
- sessionIdRef.current = file.id;
20250
- sessionCreatedAtRef.current = file.createdAt;
20251
- if (file.sessionState && compiledContextRef.current) {
20252
- sessionStateRef.current = file.sessionState;
20253
- }
20254
- if (file.artifactStore) {
20255
- artifactStoreRef.current = deserializeArtifactStore(file.artifactStore);
20256
- } else {
20257
- artifactStoreRef.current = new ArtifactStore();
20258
- }
20259
- const manager = memoryManagerRef.current;
20260
- if (manager) {
20261
- try {
20262
- const cwd = process.cwd();
20263
- const results = await manager.recall({ text: cwd, repoPath: cwd, limit: 5 });
20264
- if (results.length > 0) {
20265
- const text = await manager.synthesizeRecalled(results);
20266
- const lastSystemIdx = messagesRef.current.findLastIndex((m) => m.role === "system");
20267
- const insertIdx = lastSystemIdx >= 0 ? lastSystemIdx + 1 : messagesRef.current.length;
20268
- messagesRef.current.splice(insertIdx, 0, { role: "system", content: text });
20269
- }
20270
- } catch {
20271
- }
20272
- }
20273
- const msg = checkpointId ? `resumed session ${file.id} from checkpoint` : `resumed session ${file.id} (${file.messages.filter((m) => m.role !== "system").length} msgs)`;
20274
- setEvents([{ kind: "info", key: mkKey(), text: msg }]);
20275
- const userMsgs = file.messages.filter((m) => m.role === "user" && m.content).map((m) => {
20276
- if (!m.content) return "";
20277
- if (typeof m.content === "string") return m.content;
20278
- const textPart = m.content.find((p) => p.type === "text");
20279
- return textPart?.text ?? "";
20280
- }).filter((text) => text.length > 0);
20281
- if (userMsgs.length > 0) setHistory(userMsgs);
20282
- setUsage(null);
20283
- setSessionUsage(null);
20284
- gatewayMetaRef.current = null;
20285
- setGatewayMeta(null);
20286
- void getCostReport(file.id).then((report) => setSessionUsage(report.session));
20287
- } catch (e) {
20288
- setEvents((es) => [
20289
- ...es,
20290
- { kind: "error", key: mkKey(), text: `failed to load session: ${e.message}` }
20291
- ]);
20292
- }
20293
- },
20294
- []
20295
- );
20296
- const handleResumePick = useCallback6(
20297
- async (picked) => {
20298
- setResumeSessions(null);
20299
- if (!picked) return;
20300
- if (picked.checkpointCount > 0) {
20301
- try {
20302
- const file = await loadSession(picked.filePath);
20303
- setCheckpointList(file.checkpoints ?? []);
20304
- setCheckpointSession(picked);
20305
- } catch (e) {
20306
- setEvents((es) => [
20307
- ...es,
20308
- { kind: "error", key: mkKey(), text: `failed to load checkpoints: ${e.message}` }
20309
- ]);
20310
- await doResumeSession(picked.filePath);
20311
- }
20312
- return;
20313
- }
20314
- await doResumeSession(picked.filePath);
20315
- },
20316
- [doResumeSession]
20317
- );
20318
- const handleCheckpointPick = useCallback6(
20319
- async (checkpointId) => {
20320
- const session = checkpointSession;
20321
- setCheckpointSession(null);
20322
- setCheckpointList([]);
20323
- if (!session || !checkpointId) {
20324
- if (session) {
20325
- setResumeSessions(await listSessions(200, process.cwd()));
20326
- }
20327
- return;
20328
- }
20329
- if (checkpointId === "__start__") {
20330
- await doResumeSession(session.filePath);
20331
- return;
20332
- }
20333
- await doResumeSession(session.filePath, checkpointId);
20334
- },
20335
- [checkpointSession, doResumeSession]
20336
- );
20337
- const handleSlash = useCallback6(
20499
+ const handleSlash = useCallback8(
20338
20500
  (cmd) => {
20339
20501
  const raw = cmd.trim();
20340
20502
  const [head, ...rest] = raw.split(/\s+/);
@@ -20354,11 +20516,7 @@ ${wcagWarnings.join("\n")}` }
20354
20516
  } else {
20355
20517
  messagesRef.current = [messagesRef.current[0]];
20356
20518
  }
20357
- sessionIdRef.current = null;
20358
- sessionCreatedAtRef.current = null;
20359
- sessionTitleRef.current = null;
20360
- sessionStateRef.current = emptySessionState();
20361
- artifactStoreRef.current = new ArtifactStore();
20519
+ resetSession();
20362
20520
  executorRef.current.clearArtifacts();
20363
20521
  if (flushTimeoutRef.current) {
20364
20522
  clearTimeout(flushTimeoutRef.current);
@@ -20374,15 +20532,13 @@ ${wcagWarnings.join("\n")}` }
20374
20532
  setSessionUsage(null);
20375
20533
  gatewayMetaRef.current = null;
20376
20534
  setGatewayMeta(null);
20377
- setTasks([]);
20378
- setTasksStartedAt(null);
20379
- setTasksStartTokens(0);
20535
+ clearTaskTracking();
20380
20536
  compactSuggestedRef.current = false;
20381
20537
  updateNudgedRef.current = false;
20382
20538
  return true;
20383
20539
  }
20384
20540
  if (c === "/reasoning") {
20385
- setShowReasoning((s) => {
20541
+ turn.setShowReasoning((s) => {
20386
20542
  const next = !s;
20387
20543
  setEvents((e) => [
20388
20544
  ...e,
@@ -21341,7 +21497,7 @@ ${lines.join("\n")}` }]);
21341
21497
  },
21342
21498
  [cfg, exit, usage, theme, mode, openResumePicker, runCompact, runInit, initMcp, setCfg, setShowRemoteDashboard, setSelectedRemoteSession]
21343
21499
  );
21344
- const handleCommandSave = useCallback6(
21500
+ const handleCommandSave = useCallback8(
21345
21501
  async (opts2) => {
21346
21502
  setCommandWizard(null);
21347
21503
  try {
@@ -21363,7 +21519,7 @@ ${lines.join("\n")}` }]);
21363
21519
  },
21364
21520
  [commandWizard, reloadCustomCommands, setEvents]
21365
21521
  );
21366
- const handleCommandDelete = useCallback6(
21522
+ const handleCommandDelete = useCallback8(
21367
21523
  async (cmd) => {
21368
21524
  setCommandToDelete(null);
21369
21525
  try {
@@ -21382,7 +21538,7 @@ ${lines.join("\n")}` }]);
21382
21538
  },
21383
21539
  [reloadCustomCommands, setEvents, setCommandToDelete]
21384
21540
  );
21385
- const handleLspSave = useCallback6(
21541
+ const handleLspSave = useCallback8(
21386
21542
  (servers, enabled, scope) => {
21387
21543
  setCfg((c) => c ? { ...c, lspEnabled: enabled, lspServers: servers } : c);
21388
21544
  setLspScope(scope);
@@ -21411,7 +21567,7 @@ ${lines.join("\n")}` }]);
21411
21567
  },
21412
21568
  [cfg, setCfg, setEvents, setShowLspWizard]
21413
21569
  );
21414
- const handleRemoteCancel = useCallback6(
21570
+ const handleRemoteCancel = useCallback8(
21415
21571
  async (session) => {
21416
21572
  try {
21417
21573
  const { cancelRemoteSession: cancelRemoteSession2 } = await Promise.resolve().then(() => (init_worker_client(), worker_client_exports));
@@ -21431,7 +21587,7 @@ ${lines.join("\n")}` }]);
21431
21587
  },
21432
21588
  [setEvents, setShowRemoteDashboard]
21433
21589
  );
21434
- const processMessage = useCallback6(
21590
+ const processMessage = useCallback8(
21435
21591
  async (text, displayText, opts2) => {
21436
21592
  if (!cfg) return;
21437
21593
  let trimmed = text.trim();
@@ -21534,11 +21690,9 @@ ${lines.join("\n")}` }]);
21534
21690
  { kind: "info", key: mkKey(), text: "Tip: Rerunning /init occasionally helps KimiFlare stay accurate as your project evolves." }
21535
21691
  ]);
21536
21692
  }
21537
- setBusy(true);
21538
- busyRef.current = true;
21693
+ beginTurn();
21539
21694
  gatewayMetaRef.current = null;
21540
21695
  setGatewayMeta(null);
21541
- setTurnStartedAt(Date.now());
21542
21696
  const classification = classifyIntent(trimmed);
21543
21697
  setIntentTier(classification.tier);
21544
21698
  if (!sessionTitleRef.current) {
@@ -21716,24 +21870,15 @@ ${lines.join("\n")}` }]);
21716
21870
  setCodeMode(false);
21717
21871
  const asstId = activeAsstIdRef.current;
21718
21872
  if (asstId !== null) updateAssistant(asstId, () => ({ streaming: false }));
21719
- setBusy(false);
21720
- busyRef.current = false;
21721
- setTurnStartedAt(null);
21722
- setTurnPhase("waiting");
21723
- setCurrentToolName(null);
21724
- setLastActivityAt(null);
21873
+ endTurn();
21725
21874
  activeAsstIdRef.current = null;
21726
21875
  activeScopeRef.current = null;
21727
- isAbortingRef.current = false;
21728
21876
  clearPermissionResolveRef();
21729
21877
  limitResolveRef.current = null;
21730
21878
  loopResolveRef.current = null;
21731
21879
  setLoopModal(null);
21732
21880
  pendingToolCallsRef.current.clear();
21733
- setTasks([]);
21734
- setTasksStartedAt(null);
21735
- setTasksStartTokens(0);
21736
- tasksRef.current = [];
21881
+ clearTaskTracking();
21737
21882
  setEvents(
21738
21883
  (evts) => evts.map((e) => e.kind === "tool" && e.status === "running" ? { ...e, status: "error", result: "(stopped)" } : e)
21739
21884
  );
@@ -21955,7 +22100,7 @@ ${lines.join("\n")}` }]);
21955
22100
  processMessage(next.full, next.display, { queuedKey: next.key });
21956
22101
  }
21957
22102
  }, [busy, queue, processMessage]);
21958
- const submit = useCallback6(
22103
+ const submit = useCallback8(
21959
22104
  (full, display) => {
21960
22105
  const trimmedFull = full.trim();
21961
22106
  if (!trimmedFull) return;
@@ -22210,7 +22355,6 @@ var init_app = __esm({
22210
22355
  "src/app.tsx"() {
22211
22356
  "use strict";
22212
22357
  init_loop();
22213
- init_supervisor();
22214
22358
  init_system_prompt();
22215
22359
  init_llm_summarize();
22216
22360
  init_artifact_compaction();
@@ -22268,6 +22412,8 @@ var init_app = __esm({
22268
22412
  init_use_picker_controller();
22269
22413
  init_use_modal_host();
22270
22414
  init_modal_host();
22415
+ init_use_session_manager();
22416
+ init_use_turn_controller();
22271
22417
  MAX_GITIGNORE_SIZE = 1 * 1024 * 1024;
22272
22418
  FEEDBACK_WORKER_URL2 = "https://hello.kimiflare.com";
22273
22419
  CONTEXT_LIMIT = 262e3;