kfc-code-cli 0.0.1-alpha.16 → 0.0.1-alpha.17

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.
Files changed (2) hide show
  1. package/dist/main.mjs +966 -955
  2. package/package.json +1 -1
package/dist/main.mjs CHANGED
@@ -198,7 +198,7 @@ const agentTypeEnum = z.enum([
198
198
  "sub",
199
199
  "independent"
200
200
  ]);
201
- const _rawWireProducerSchema = z.object({
201
+ const WireProducerSchema = z.object({
202
202
  kind: z.enum(["python", "typescript"]),
203
203
  name: z.string(),
204
204
  version: z.string()
@@ -208,9 +208,9 @@ const WireFileMetadataSchema = z.object({
208
208
  protocol_version: z.string(),
209
209
  created_at: z.number(),
210
210
  kimi_version: z.string().optional(),
211
- producer: _rawWireProducerSchema.optional()
211
+ producer: WireProducerSchema.optional()
212
212
  });
213
- const _rawTurnBeginRecordSchema = z.object({
213
+ const TurnBeginRecordSchema = z.object({
214
214
  type: z.literal("turn_begin"),
215
215
  seq: z.number(),
216
216
  time: z.number(),
@@ -220,7 +220,7 @@ const _rawTurnBeginRecordSchema = z.object({
220
220
  input_kind: z.enum(["user", "system_trigger"]),
221
221
  trigger_source: z.string().optional()
222
222
  });
223
- const _rawTurnEndRecordSchema = z.object({
223
+ const TurnEndRecordSchema = z.object({
224
224
  type: z.literal("turn_end"),
225
225
  seq: z.number(),
226
226
  time: z.number(),
@@ -256,7 +256,7 @@ const _userInputPartSchema = z.discriminatedUnion("type", [
256
256
  video_url: z.object({ url: z.string() })
257
257
  })
258
258
  ]);
259
- const _rawUserMessageRecordSchema = z.object({
259
+ const UserMessageRecordSchema = z.object({
260
260
  type: z.literal("user_message"),
261
261
  seq: z.number(),
262
262
  time: z.number(),
@@ -264,7 +264,7 @@ const _rawUserMessageRecordSchema = z.object({
264
264
  content: z.union([z.string(), z.array(_userInputPartSchema).readonly()]),
265
265
  uuid: z.string().optional()
266
266
  });
267
- const _rawToolResultRecordSchema = z.object({
267
+ const ToolResultRecordSchema = z.object({
268
268
  type: z.literal("tool_result"),
269
269
  seq: z.number(),
270
270
  time: z.number(),
@@ -276,29 +276,22 @@ const _rawToolResultRecordSchema = z.object({
276
276
  uuid: z.string().optional(),
277
277
  parent_uuid: z.string().optional()
278
278
  });
279
- const _rawCompactionRecordSchema = z.object({
279
+ const CompactionRecordSchema = z.object({
280
280
  type: z.literal("compaction"),
281
281
  seq: z.number(),
282
282
  time: z.number(),
283
283
  summary: z.string(),
284
- compacted_range: z.object({
285
- from_turn: z.number(),
286
- to_turn: z.number(),
287
- message_count: z.number()
288
- }),
289
284
  pre_compact_tokens: z.number(),
290
285
  post_compact_tokens: z.number(),
291
- trigger: z.enum(["auto", "manual"]),
292
- archive_file: z.string().optional(),
293
286
  uuid: z.string().optional()
294
287
  });
295
- const _rawSystemPromptChangedRecordSchema = z.object({
288
+ const SystemPromptChangedRecordSchema = z.object({
296
289
  type: z.literal("system_prompt_changed"),
297
290
  seq: z.number(),
298
291
  time: z.number(),
299
292
  new_prompt: z.string()
300
293
  });
301
- const _rawToolsChangedRecordSchema = z.object({
294
+ const ToolsChangedRecordSchema = z.object({
302
295
  type: z.literal("tools_changed"),
303
296
  seq: z.number(),
304
297
  time: z.number(),
@@ -309,14 +302,14 @@ const _rawToolsChangedRecordSchema = z.object({
309
302
  ]),
310
303
  tools: z.array(z.string())
311
304
  });
312
- const _rawSystemReminderRecordSchema = z.object({
305
+ const SystemReminderRecordSchema = z.object({
313
306
  type: z.literal("system_reminder"),
314
307
  seq: z.number(),
315
308
  time: z.number(),
316
309
  content: z.string(),
317
310
  consumed_at_turn: z.number().optional()
318
311
  });
319
- const _rawNotificationRecordSchema = z.object({
312
+ const NotificationRecordSchema = z.object({
320
313
  type: z.literal("notification"),
321
314
  seq: z.number(),
322
315
  time: z.number(),
@@ -354,7 +347,7 @@ const _rawNotificationRecordSchema = z.object({
354
347
  envelope_id: z.string().optional()
355
348
  })
356
349
  });
357
- const _rawToolDeniedRecordSchema = z.object({
350
+ const ToolDeniedRecordSchema = z.object({
358
351
  type: z.literal("tool_denied"),
359
352
  seq: z.number(),
360
353
  time: z.number(),
@@ -367,7 +360,7 @@ const _rawToolDeniedRecordSchema = z.object({
367
360
  reason: z.string()
368
361
  })
369
362
  });
370
- const _rawStepBeginRecordSchema = z.object({
363
+ const StepBeginRecordSchema = z.object({
371
364
  type: z.literal("step_begin"),
372
365
  seq: z.number(),
373
366
  time: z.number(),
@@ -375,7 +368,7 @@ const _rawStepBeginRecordSchema = z.object({
375
368
  turn_id: z.string(),
376
369
  step: z.number()
377
370
  });
378
- const _rawStepEndRecordSchema = z.object({
371
+ const StepEndRecordSchema = z.object({
379
372
  type: z.literal("step_end"),
380
373
  seq: z.number(),
381
374
  time: z.number(),
@@ -398,7 +391,7 @@ const _contentPartBodySchema = z.discriminatedUnion("kind", [z.object({
398
391
  think: z.string(),
399
392
  encrypted: z.string().optional()
400
393
  })]);
401
- const _rawContentPartRecordSchema = z.object({
394
+ const ContentPartRecordSchema = z.object({
402
395
  type: z.literal("content_part"),
403
396
  seq: z.number(),
404
397
  time: z.number(),
@@ -409,7 +402,7 @@ const _rawContentPartRecordSchema = z.object({
409
402
  role: z.literal("assistant"),
410
403
  part: _contentPartBodySchema
411
404
  });
412
- const _rawToolCallRecordSchema = z.object({
405
+ const ToolCallRecordSchema = z.object({
413
406
  type: z.literal("tool_call"),
414
407
  seq: z.number(),
415
408
  time: z.number(),
@@ -466,7 +459,7 @@ const skillInvocationTriggerEnum = z.enum([
466
459
  "claude-proactive",
467
460
  "nested-skill"
468
461
  ]);
469
- const _rawSkillInvokedRecordSchema = z.object({
462
+ const SkillInvokedRecordSchema = z.object({
470
463
  type: z.literal("skill_invoked"),
471
464
  seq: z.number(),
472
465
  time: z.number(),
@@ -481,7 +474,7 @@ const _rawSkillInvokedRecordSchema = z.object({
481
474
  query_depth: z.number().optional()
482
475
  })
483
476
  });
484
- const _rawSkillCompletedRecordSchema = z.object({
477
+ const SkillCompletedRecordSchema = z.object({
485
478
  type: z.literal("skill_completed"),
486
479
  seq: z.number(),
487
480
  time: z.number(),
@@ -497,7 +490,7 @@ const _rawSkillCompletedRecordSchema = z.object({
497
490
  query_depth: z.number().optional()
498
491
  })
499
492
  });
500
- const _rawApprovalRequestRecordSchema = z.object({
493
+ const ApprovalRequestRecordSchema = z.object({
501
494
  type: z.literal("approval_request"),
502
495
  seq: z.number(),
503
496
  time: z.number(),
@@ -512,7 +505,7 @@ const _rawApprovalRequestRecordSchema = z.object({
512
505
  source: ApprovalSourceSchema
513
506
  })
514
507
  });
515
- const _rawApprovalResponseRecordSchema = z.object({
508
+ const ApprovalResponseRecordSchema = z.object({
516
509
  type: z.literal("approval_response"),
517
510
  seq: z.number(),
518
511
  time: z.number(),
@@ -530,7 +523,7 @@ const _rawApprovalResponseRecordSchema = z.object({
530
523
  synthetic: z.boolean().optional()
531
524
  })
532
525
  });
533
- const _rawTeamMailRecordSchema = z.object({
526
+ const TeamMailRecordSchema = z.object({
534
527
  type: z.literal("team_mail"),
535
528
  seq: z.number(),
536
529
  time: z.number(),
@@ -543,13 +536,13 @@ const _rawTeamMailRecordSchema = z.object({
543
536
  summary: z.string().optional()
544
537
  })
545
538
  });
546
- const _rawTokenUsageSchema = z.object({
539
+ const TokenUsageSchema = z.object({
547
540
  input: z.number().int().nonnegative(),
548
541
  output: z.number().int().nonnegative(),
549
542
  cache_read: z.number().int().nonnegative().optional(),
550
543
  cache_write: z.number().int().nonnegative().optional()
551
544
  });
552
- const _rawSubagentSpawnedRecordSchema = z.object({
545
+ const SubagentSpawnedRecordSchema = z.object({
553
546
  type: z.literal("subagent_spawned"),
554
547
  seq: z.number(),
555
548
  time: z.number(),
@@ -563,7 +556,7 @@ const _rawSubagentSpawnedRecordSchema = z.object({
563
556
  run_in_background: z.boolean()
564
557
  })
565
558
  });
566
- const _rawSubagentCompletedRecordSchema = z.object({
559
+ const SubagentCompletedRecordSchema = z.object({
567
560
  type: z.literal("subagent_completed"),
568
561
  seq: z.number(),
569
562
  time: z.number(),
@@ -573,10 +566,10 @@ const _rawSubagentCompletedRecordSchema = z.object({
573
566
  agent_id: z.string(),
574
567
  parent_tool_call_id: z.string(),
575
568
  result_summary: z.string(),
576
- usage: _rawTokenUsageSchema.optional()
569
+ usage: TokenUsageSchema.optional()
577
570
  })
578
571
  });
579
- const _rawSubagentFailedRecordSchema = z.object({
572
+ const SubagentFailedRecordSchema = z.object({
580
573
  type: z.literal("subagent_failed"),
581
574
  seq: z.number(),
582
575
  time: z.number(),
@@ -588,14 +581,14 @@ const _rawSubagentFailedRecordSchema = z.object({
588
581
  error: z.string()
589
582
  })
590
583
  });
591
- const _rawOwnershipChangedRecordSchema = z.object({
584
+ const OwnershipChangedRecordSchema = z.object({
592
585
  type: z.literal("ownership_changed"),
593
586
  seq: z.number(),
594
587
  time: z.number(),
595
588
  old_owner: z.string().nullable(),
596
589
  new_owner: z.string()
597
590
  });
598
- const _rawContextEditRecordSchema = z.object({
591
+ const ContextEditRecordSchema = z.object({
599
592
  type: z.literal("context_edit"),
600
593
  seq: z.number(),
601
594
  time: z.number(),
@@ -617,7 +610,7 @@ const _rawContextEditRecordSchema = z.object({
617
610
  ]).optional(),
618
611
  cascade: z.boolean().optional()
619
612
  });
620
- const _rawContextClearedRecordSchema = z.object({
613
+ const ContextClearedRecordSchema = z.object({
621
614
  type: z.literal("context_cleared"),
622
615
  seq: z.number(),
623
616
  time: z.number()
@@ -638,12 +631,12 @@ const _sessionInitializedCommonShape = {
638
631
  workspace_dir: z.string().optional(),
639
632
  thinking_level: z.string().optional()
640
633
  };
641
- const _rawSessionInitializedMainSchema = z.object({
634
+ const SessionInitializedMainSchema = z.object({
642
635
  ..._sessionInitializedCommonShape,
643
636
  agent_type: z.literal("main"),
644
637
  session_id: z.string()
645
638
  });
646
- const _rawSessionInitializedSubSchema = z.object({
639
+ const SessionInitializedSubSchema = z.object({
647
640
  ..._sessionInitializedCommonShape,
648
641
  agent_type: z.literal("sub"),
649
642
  agent_id: z.string(),
@@ -653,45 +646,44 @@ const _rawSessionInitializedSubSchema = z.object({
653
646
  parent_tool_call_id: z.string(),
654
647
  run_in_background: z.boolean()
655
648
  });
656
- const _rawSessionInitializedIndependentSchema = z.object({
649
+ const SessionInitializedIndependentSchema = z.object({
657
650
  ..._sessionInitializedCommonShape,
658
651
  agent_type: z.literal("independent"),
659
652
  agent_id: z.string(),
660
653
  agent_name: z.string().optional()
661
654
  });
662
- const _rawSessionInitializedRecordSchema = z.discriminatedUnion("agent_type", [
663
- _rawSessionInitializedMainSchema,
664
- _rawSessionInitializedSubSchema,
665
- _rawSessionInitializedIndependentSchema
655
+ const SessionInitializedRecordSchema = z.discriminatedUnion("agent_type", [
656
+ SessionInitializedMainSchema,
657
+ SessionInitializedSubSchema,
658
+ SessionInitializedIndependentSchema
666
659
  ]);
667
- const SessionInitializedRecordSchema = _rawSessionInitializedRecordSchema;
668
660
  const WireRecordSchema = z.discriminatedUnion("type", [
669
- _rawTurnBeginRecordSchema,
670
- _rawTurnEndRecordSchema,
671
- _rawUserMessageRecordSchema,
672
- _rawToolResultRecordSchema,
673
- _rawCompactionRecordSchema,
674
- _rawSystemPromptChangedRecordSchema,
675
- _rawToolsChangedRecordSchema,
676
- _rawSystemReminderRecordSchema,
677
- _rawNotificationRecordSchema,
678
- _rawToolDeniedRecordSchema,
679
- _rawStepBeginRecordSchema,
680
- _rawStepEndRecordSchema,
681
- _rawContentPartRecordSchema,
682
- _rawToolCallRecordSchema,
683
- _rawSkillInvokedRecordSchema,
684
- _rawSkillCompletedRecordSchema,
685
- _rawApprovalRequestRecordSchema,
686
- _rawApprovalResponseRecordSchema,
687
- _rawTeamMailRecordSchema,
688
- _rawSubagentSpawnedRecordSchema,
689
- _rawSubagentCompletedRecordSchema,
690
- _rawSubagentFailedRecordSchema,
691
- _rawOwnershipChangedRecordSchema,
692
- _rawContextEditRecordSchema,
693
- _rawContextClearedRecordSchema,
694
- _rawSessionInitializedRecordSchema
661
+ TurnBeginRecordSchema,
662
+ TurnEndRecordSchema,
663
+ UserMessageRecordSchema,
664
+ ToolResultRecordSchema,
665
+ CompactionRecordSchema,
666
+ SystemPromptChangedRecordSchema,
667
+ ToolsChangedRecordSchema,
668
+ SystemReminderRecordSchema,
669
+ NotificationRecordSchema,
670
+ ToolDeniedRecordSchema,
671
+ StepBeginRecordSchema,
672
+ StepEndRecordSchema,
673
+ ContentPartRecordSchema,
674
+ ToolCallRecordSchema,
675
+ SkillInvokedRecordSchema,
676
+ SkillCompletedRecordSchema,
677
+ ApprovalRequestRecordSchema,
678
+ ApprovalResponseRecordSchema,
679
+ TeamMailRecordSchema,
680
+ SubagentSpawnedRecordSchema,
681
+ SubagentCompletedRecordSchema,
682
+ SubagentFailedRecordSchema,
683
+ OwnershipChangedRecordSchema,
684
+ ContextEditRecordSchema,
685
+ ContextClearedRecordSchema,
686
+ SessionInitializedRecordSchema
695
687
  ]);
696
688
  //#endregion
697
689
  //#region ../../packages/kimi-core/src/storage/journal/reader.ts
@@ -759,6 +751,7 @@ async function replayWire(path, options) {
759
751
  }
760
752
  const reason = `wire.jsonl mid-file corruption at line ${physicalLineNo}: ${String(error)}`;
761
753
  return {
754
+ meta,
762
755
  records,
763
756
  protocolVersion: meta.protocol_version,
764
757
  health: "broken",
@@ -783,6 +776,7 @@ async function replayWire(path, options) {
783
776
  }
784
777
  const reason = `wire.jsonl schema violation at line ${physicalLineNo}: ${parsed.error.message}`;
785
778
  return {
779
+ meta,
786
780
  records,
787
781
  protocolVersion: meta.protocol_version,
788
782
  health: "broken",
@@ -795,6 +789,7 @@ async function replayWire(path, options) {
795
789
  records.push(parsed.data);
796
790
  }
797
791
  return {
792
+ meta,
798
793
  records,
799
794
  protocolVersion: meta.protocol_version,
800
795
  health: "ok",
@@ -1100,7 +1095,7 @@ var WiredJournalWriter = class {
1100
1095
  if (!Number.isInteger(opts.initialSeq) || opts.initialSeq < 0) throw new RangeError(`WiredJournalWriter.initialSeq must be a non-negative integer, got ${String(opts.initialSeq)}`);
1101
1096
  this.seq = opts.initialSeq;
1102
1097
  }
1103
- if (opts.metadataAlreadyWritten === true) {
1098
+ if (opts.metadataAlreadyWritten) {
1104
1099
  this.metadataWritten = true;
1105
1100
  this.directorySynced = true;
1106
1101
  }
@@ -1118,44 +1113,14 @@ var WiredJournalWriter = class {
1118
1113
  get sessionDir() {
1119
1114
  return dirname(this.filePath);
1120
1115
  }
1121
- get rotateCapability() {
1122
- return {
1123
- sessionDir: this.sessionDir,
1124
- rotate: async () => {
1125
- await this.flush();
1126
- const result = await rotateJournal(this.sessionDir, { producer: this.producer });
1127
- this.resetForRotation();
1128
- return result;
1129
- },
1130
- readSessionInitialized: async () => {
1131
- const lines = (await readFile(this.filePath, "utf-8")).split("\n").filter((l) => l.length > 0);
1132
- if (lines.length < 2) throw new Error(`readSessionInitialized: wire.jsonl at ${this.filePath} has fewer than 2 lines`);
1133
- const parsed = JSON.parse(lines[1]);
1134
- const result = SessionInitializedRecordSchema.safeParse(parsed);
1135
- if (!result.success) throw new Error(`readSessionInitialized: wire.jsonl line 2 at ${this.filePath} is not a valid session_initialized record: ${result.error.message}`);
1136
- return result.data;
1137
- },
1138
- appendBoundary: async (sessionInitialized) => {
1139
- const { seq: _seq, time: _time, ...input } = sessionInitialized;
1140
- await this.append(input);
1141
- }
1142
- };
1143
- }
1144
- /**
1145
- * Reset writer state after a compaction rotation (Slice 3.3 / M04).
1146
- *
1147
- * After `rotateJournal` renames the old `wire.jsonl` → `wire.N.jsonl`
1148
- * and creates a fresh `wire.jsonl` with a metadata header, the writer
1149
- * must restart its monotonic seq from 0 (so the first record in the
1150
- * new file gets seq=1). The metadata header is already on disk, so we
1151
- * mark it as written. The new file's directory entry was durably
1152
- * committed by `rotateJournal`'s `syncDir`, so `directorySynced` is
1153
- * also set.
1154
- */
1155
- resetForRotation() {
1116
+ async rotate(initializeRecord) {
1117
+ await this.flush();
1118
+ const result = await rotateJournal(this.sessionDir, { producer: this.producer });
1156
1119
  this.seq = 0;
1157
1120
  this.metadataWritten = true;
1158
1121
  this.directorySynced = true;
1122
+ await this.append(initializeRecord);
1123
+ return result;
1159
1124
  }
1160
1125
  async append(input) {
1161
1126
  if (this.closed) throw new Error("JournalWriter: append on closed writer");
@@ -1360,6 +1325,7 @@ var NoopJournalWriter = class {
1360
1325
  //#region ../../packages/kimi-core/src/storage/state/memory-store.ts
1361
1326
  var ContextStateMemory = class {
1362
1327
  history = [];
1328
+ initializeRecord;
1363
1329
  _systemPrompt;
1364
1330
  _model;
1365
1331
  _activeTools;
@@ -1372,6 +1338,7 @@ var ContextStateMemory = class {
1372
1338
  this._model = opts.initialModel;
1373
1339
  this._systemPrompt = opts.initialSystemPrompt ?? "";
1374
1340
  this._activeTools = new Set(opts.initialActiveTools ?? []);
1341
+ this.initializeRecord = opts.initializeRecord;
1375
1342
  if (opts.initialHistory !== void 0) this.history = [...opts.initialHistory];
1376
1343
  if (opts.initialTokenCount !== void 0) this._tokenCountWithPending = opts.initialTokenCount;
1377
1344
  if (opts.initialThinkingLevel !== void 0) this._thinkingLevel = opts.initialThinkingLevel;
@@ -1478,7 +1445,7 @@ var ContextStateMemory = class {
1478
1445
  text: summary.summary
1479
1446
  }],
1480
1447
  toolCalls: []
1481
- }];
1448
+ }, ...summary.preservedRecent];
1482
1449
  this.openSteps.clear();
1483
1450
  this._tokenCountWithPending = summary.postCompactTokens;
1484
1451
  }
@@ -1820,15 +1787,8 @@ function buildCompactionRecord(summary) {
1820
1787
  return {
1821
1788
  type: "compaction",
1822
1789
  summary: summary.summary,
1823
- compacted_range: {
1824
- from_turn: summary.compactedRange.fromTurn,
1825
- to_turn: summary.compactedRange.toTurn,
1826
- message_count: summary.compactedRange.messageCount
1827
- },
1828
1790
  pre_compact_tokens: summary.preCompactTokens,
1829
- post_compact_tokens: summary.postCompactTokens,
1830
- trigger: summary.trigger,
1831
- archive_file: summary.archiveFile
1791
+ post_compact_tokens: summary.postCompactTokens
1832
1792
  };
1833
1793
  }
1834
1794
  //#endregion
@@ -1990,6 +1950,12 @@ var BaseContextState = class {
1990
1950
  this.memory.setThinkingLevel(level);
1991
1951
  }
1992
1952
  async resetToSummary(summary) {
1953
+ this.assertNotBroken();
1954
+ await this.journalWriter.rotate?.({
1955
+ ...this.memory.initializeRecord,
1956
+ system_prompt: this.systemPrompt,
1957
+ active_tools: [...this.activeTools]
1958
+ });
1993
1959
  this.assertNotBroken();
1994
1960
  await this.journalWriter.append(buildCompactionRecord(summary));
1995
1961
  this.memory.resetToSummary(summary);
@@ -2131,36 +2097,8 @@ function createSessionRuntimeSlot(initial) {
2131
2097
  }
2132
2098
  };
2133
2099
  }
2134
- function withCompactionConfigFallback(bundle, fallback) {
2135
- if (bundle.compactionConfig !== void 0 || fallback === void 0) return bundle;
2136
- return {
2137
- ...bundle,
2138
- compactionConfig: fallback
2139
- };
2140
- }
2141
2100
  function normalizeSessionRuntimeSlot(slot, fallback) {
2142
- const runtimeSlot = slot ?? createSessionRuntimeSlot(fallback);
2143
- const current = runtimeSlot.current();
2144
- if (current.compactionConfig === void 0 && fallback.compactionConfig !== void 0) runtimeSlot.replace({
2145
- ...current,
2146
- compactionConfig: fallback.compactionConfig
2147
- });
2148
- return runtimeSlot;
2149
- }
2150
- const DEFAULT_RESERVED_CONTEXT_SIZE$1 = 5e4;
2151
- const DEFAULT_RESERVED_CONTEXT_FRACTION$1 = .25;
2152
- function defaultReservedContextSize$1(maxContextSize) {
2153
- return Math.max(0, Math.min(DEFAULT_RESERVED_CONTEXT_SIZE$1, Math.floor(maxContextSize * DEFAULT_RESERVED_CONTEXT_FRACTION$1)));
2154
- }
2155
- /** Returns true when either trigger condition fires. */
2156
- function shouldCompact(context, config) {
2157
- if (config === void 0) return false;
2158
- const triggerRatio = config.triggerRatio ?? .85;
2159
- const reservedContextSize = config.reservedContextSize ?? defaultReservedContextSize$1(config.maxContextSize);
2160
- const tokens = context.tokenCountWithPending;
2161
- if (tokens >= config.maxContextSize * triggerRatio) return true;
2162
- if (tokens + reservedContextSize >= config.maxContextSize) return true;
2163
- return false;
2101
+ return slot ?? createSessionRuntimeSlot(fallback);
2164
2102
  }
2165
2103
  //#endregion
2166
2104
  //#region ../../packages/kimi-core/src/soul/error-utils.ts
@@ -2222,9 +2160,9 @@ function zodToSchema(schema) {
2222
2160
  return { type: "object" };
2223
2161
  }
2224
2162
  }
2225
- function buildLLMVisibleTools(tools, activeTools) {
2163
+ function buildLLMVisibleTools(tools) {
2226
2164
  if (tools === void 0) return [];
2227
- return (activeTools === void 0 ? tools : tools.filter((t) => activeTools.includes(t.name))).map((t) => ({
2165
+ return tools.map((t) => ({
2228
2166
  name: t.name,
2229
2167
  description: t.description,
2230
2168
  input_schema: zodToSchema(t.inputSchema)
@@ -2743,12 +2681,13 @@ async function raceExecuteWithGraceTimeout(executePromise, signal, toolName) {
2743
2681
  * already spent.
2744
2682
  */
2745
2683
  async function executeSoulStep(deps) {
2746
- const { config, context, turnId, runtime, signal, overrides, currentStep, emit, recordUsage } = deps;
2684
+ const { config, context, turnId, runtime, signal, currentStep, emit, recordUsage } = deps;
2747
2685
  if (config.beforeStep !== void 0) {
2748
2686
  const beforeStep = await config.beforeStep({
2749
2687
  turnId,
2750
2688
  stepNumber: currentStep,
2751
- context
2689
+ context,
2690
+ signal
2752
2691
  }, signal);
2753
2692
  if (beforeStep?.block === true) throw new Error(beforeStep.reason ?? `Step ${String(currentStep)} was blocked`);
2754
2693
  }
@@ -2757,8 +2696,6 @@ async function executeSoulStep(deps) {
2757
2696
  step: currentStep
2758
2697
  });
2759
2698
  signal.throwIfAborted();
2760
- const model = overrides?.model ?? context.model;
2761
- const visibleTools = buildLLMVisibleTools(config.tools, overrides?.activeTools);
2762
2699
  const messages = context.buildMessages();
2763
2700
  const stepUuid = randomUUID();
2764
2701
  const toolCallByProviderId = /* @__PURE__ */ new Map();
@@ -2786,10 +2723,9 @@ async function executeSoulStep(deps) {
2786
2723
  try {
2787
2724
  response = await runtime.kosong.chat({
2788
2725
  messages,
2789
- tools: visibleTools,
2790
- model,
2726
+ tools: buildLLMVisibleTools(config.tools ?? []),
2727
+ model: context.model,
2791
2728
  systemPrompt: context.systemPrompt,
2792
- effort: overrides?.effort,
2793
2729
  signal,
2794
2730
  ...createChatStreamingCallbacks({
2795
2731
  emit,
@@ -2801,19 +2737,19 @@ async function executeSoulStep(deps) {
2801
2737
  }),
2802
2738
  onToolCallReady: (toolCall, info) => {
2803
2739
  streamingScheduler.markReady(toolCall, info);
2804
- },
2805
- contextWindow: config.contextWindow
2740
+ }
2806
2741
  });
2807
2742
  } finally {
2808
2743
  streamingScheduler.finish();
2809
2744
  }
2810
- recordUsage(response.usage);
2745
+ const usage = response.usage;
2746
+ recordUsage(usage);
2811
2747
  const pending = await classifyToolCalls(step, response);
2812
2748
  await context.appendStepEnd({
2813
2749
  uuid: stepUuid,
2814
2750
  turnId,
2815
2751
  step: currentStep,
2816
- usage: toStepEndUsage(response.usage),
2752
+ usage: toStepEndUsage(usage),
2817
2753
  finishReason: response.stopReason
2818
2754
  });
2819
2755
  await executePendingCalls(step, pending);
@@ -2828,10 +2764,15 @@ async function executeSoulStep(deps) {
2828
2764
  turnId,
2829
2765
  stepNumber: currentStep,
2830
2766
  context,
2831
- stopReason
2767
+ usage,
2768
+ stopReason,
2769
+ signal
2832
2770
  }, signal);
2833
2771
  } catch {}
2834
- return { stopReason };
2772
+ return {
2773
+ usage,
2774
+ stopReason
2775
+ };
2835
2776
  }
2836
2777
  function createStreamingToolCallScheduler(step, streamingPreparations) {
2837
2778
  let nextOrdinal = 0;
@@ -2925,7 +2866,7 @@ function toStepEndUsage(usage) {
2925
2866
  //#region ../../packages/kimi-core/src/soul/run-turn.ts
2926
2867
  const DEFAULT_MAX_STEPS = 100;
2927
2868
  const UNKNOWN_TURN_ID = "unknown-turn";
2928
- async function runSoulTurn(config, context, runtime, sink, signal, overrides) {
2869
+ async function runSoulTurn(config, context, runtime, sink, signal) {
2929
2870
  const maxSteps = config.maxSteps ?? DEFAULT_MAX_STEPS;
2930
2871
  const usage = {
2931
2872
  input: 0,
@@ -2939,10 +2880,6 @@ async function runSoulTurn(config, context, runtime, sink, signal, overrides) {
2939
2880
  try {
2940
2881
  while (true) {
2941
2882
  signal.throwIfAborted();
2942
- if (shouldCompact(context, config.compactionConfig)) {
2943
- stopReason = "needs_compaction";
2944
- break;
2945
- }
2946
2883
  if (steps >= maxSteps) throw new MaxStepsExceededError(maxSteps);
2947
2884
  steps += 1;
2948
2885
  const stepResult = await executeSoulStep({
@@ -2951,7 +2888,6 @@ async function runSoulTurn(config, context, runtime, sink, signal, overrides) {
2951
2888
  context,
2952
2889
  runtime,
2953
2890
  signal,
2954
- overrides,
2955
2891
  currentStep: steps,
2956
2892
  emit: (event) => {
2957
2893
  safeEmit(sink, event);
@@ -2966,7 +2902,8 @@ async function runSoulTurn(config, context, runtime, sink, signal, overrides) {
2966
2902
  ...stepResult,
2967
2903
  turnId,
2968
2904
  stepNumber: steps,
2969
- context
2905
+ context,
2906
+ signal
2970
2907
  }))?.continue) break;
2971
2908
  }
2972
2909
  } catch (error) {
@@ -3338,11 +3275,15 @@ var SoulRegistry = class {
3338
3275
  runSubagentTurnFn;
3339
3276
  parentSessionJournal;
3340
3277
  eventBus;
3278
+ notificationManager;
3279
+ wakeScheduler;
3341
3280
  constructor(deps) {
3342
3281
  this.createHandle = deps.createHandle;
3343
3282
  this.runSubagentTurnFn = deps.runSubagentTurn;
3344
3283
  this.parentSessionJournal = deps.parentSessionJournal;
3345
3284
  this.eventBus = deps.eventBus;
3285
+ this.notificationManager = deps.notificationManager;
3286
+ this.wakeScheduler = deps.wakeScheduler;
3346
3287
  }
3347
3288
  getOrCreate(key, agentDepth = 0) {
3348
3289
  const existing = this.handles.get(key);
@@ -3456,6 +3397,47 @@ var SoulRegistry = class {
3456
3397
  data: failedPayload
3457
3398
  });
3458
3399
  }
3400
+ if (walOk && this.notificationManager !== void 0 && request.runInBackground === true) try {
3401
+ if (completedPayload !== void 0) await this.notificationManager.emit({
3402
+ category: "agent",
3403
+ type: "subagent.completed",
3404
+ source_kind: "subagent",
3405
+ source_id: agentId,
3406
+ title: `Subagent ${agentId} completed`,
3407
+ body: completedPayload.result_summary,
3408
+ severity: "success",
3409
+ payload: {
3410
+ agent_id: agentId,
3411
+ parent_tool_call_id: request.parentToolCallId
3412
+ },
3413
+ targets: ["llm", "wire"],
3414
+ dedupe_key: `subagent_completed:${agentId}`
3415
+ });
3416
+ else if (failedPayload !== void 0) await this.notificationManager.emit({
3417
+ category: "agent",
3418
+ type: "subagent.failed",
3419
+ source_kind: "subagent",
3420
+ source_id: agentId,
3421
+ title: `Subagent ${agentId} failed`,
3422
+ body: failedPayload.error,
3423
+ severity: "error",
3424
+ payload: {
3425
+ agent_id: agentId,
3426
+ parent_tool_call_id: request.parentToolCallId
3427
+ },
3428
+ targets: ["llm", "wire"],
3429
+ dedupe_key: `subagent_failed:${agentId}`
3430
+ });
3431
+ this.wakeScheduler?.enqueue({
3432
+ kind: "system_trigger",
3433
+ input: {
3434
+ text: "",
3435
+ parts: []
3436
+ },
3437
+ reason: completedPayload !== void 0 ? "subagent_completed" : "subagent_failed",
3438
+ source: agentId
3439
+ });
3440
+ } catch {}
3459
3441
  }
3460
3442
  queueMicrotask(() => {
3461
3443
  this.destroy(soulKey);
@@ -3484,8 +3466,6 @@ function mergeSoulConfig(target, source) {
3484
3466
  if (source.afterToolCall) target.afterToolCall = mergeCallback(target.afterToolCall, source.afterToolCall);
3485
3467
  if (source.beforeStep) target.beforeStep = mergeCallback(target.beforeStep, source.beforeStep);
3486
3468
  if (source.afterStep) target.afterStep = mergeCallback(target.afterStep, source.afterStep);
3487
- if (source.compactionConfig !== void 0) target.compactionConfig = source.compactionConfig;
3488
- if (source.contextWindow !== void 0) target.contextWindow = source.contextWindow;
3489
3469
  function mergeCallback(f1, f2) {
3490
3470
  if (!f1) return f2;
3491
3471
  if (!f2) return f1;
@@ -3737,6 +3717,19 @@ async function runSubagentTurn(deps, agentId, request, signal) {
3737
3717
  }
3738
3718
  const childSystemPrompt = typeDef.systemPromptSuffix || void 0;
3739
3719
  const childModel = request.model ?? typeDef.defaultModel ?? parentModel;
3720
+ const childActiveTools = childTools.map((t) => t.name);
3721
+ const subInitInput = {
3722
+ type: "session_initialized",
3723
+ agent_type: "sub",
3724
+ agent_id: agentId,
3725
+ agent_name: request.agentName,
3726
+ parent_session_id: parentSessionId,
3727
+ ...request.parentAgentId !== void 0 && request.parentAgentId !== "" && request.parentAgentId !== "agent_main" ? { parent_agent_id: request.parentAgentId } : {},
3728
+ parent_tool_call_id: request.parentToolCallId,
3729
+ run_in_background: request.runInBackground ?? false,
3730
+ system_prompt: childSystemPrompt ?? "",
3731
+ active_tools: childActiveTools
3732
+ };
3740
3733
  let childContext;
3741
3734
  let childJournalWriter;
3742
3735
  if (pathConfig !== void 0 && sessionId !== void 0 || sessionDir !== "") {
@@ -3755,21 +3748,9 @@ async function runSubagentTurn(deps, agentId, request, signal) {
3755
3748
  }
3756
3749
  }
3757
3750
  });
3758
- const childActiveTools = childTools.map((t) => t.name);
3759
- const subInitInput = {
3760
- type: "session_initialized",
3761
- agent_type: "sub",
3762
- agent_id: agentId,
3763
- agent_name: request.agentName,
3764
- parent_session_id: parentSessionId,
3765
- ...request.parentAgentId !== void 0 && request.parentAgentId !== "" && request.parentAgentId !== "agent_main" ? { parent_agent_id: request.parentAgentId } : {},
3766
- parent_tool_call_id: request.parentToolCallId,
3767
- run_in_background: request.runInBackground ?? false,
3768
- system_prompt: childSystemPrompt ?? "",
3769
- active_tools: childActiveTools
3770
- };
3771
3751
  await childJournalWriter.append(subInitInput);
3772
3752
  childContext = new WiredContextState({
3753
+ initializeRecord: subInitInput,
3773
3754
  journalWriter: childJournalWriter,
3774
3755
  initialModel: childModel,
3775
3756
  initialSystemPrompt: childSystemPrompt,
@@ -3777,6 +3758,7 @@ async function runSubagentTurn(deps, agentId, request, signal) {
3777
3758
  });
3778
3759
  childContextRef = childContext;
3779
3760
  } else childContext = new InMemoryContextState({
3761
+ initializeRecord: subInitInput,
3780
3762
  initialModel: childModel,
3781
3763
  initialSystemPrompt: childSystemPrompt
3782
3764
  });
@@ -5150,6 +5132,8 @@ function createSoulRegistryForSession(deps) {
5150
5132
  }),
5151
5133
  parentSessionJournal: deps.sessionJournal,
5152
5134
  eventBus: deps.eventBus,
5135
+ notificationManager: deps.notificationManager,
5136
+ wakeScheduler: deps.wakeScheduler,
5153
5137
  ...hasSubagentInfra ? { runSubagentTurn: (agentId, request, signal) => runSubagentTurn({
5154
5138
  store: deps.subagentStore,
5155
5139
  typeRegistry: deps.agentTypeRegistry,
@@ -5170,6 +5154,25 @@ function createSoulRegistryForSession(deps) {
5170
5154
  hasSubagentInfra
5171
5155
  };
5172
5156
  }
5157
+ //#endregion
5158
+ //#region ../../packages/kimi-core/src/tools/builtin/collaboration/agent.ts
5159
+ /**
5160
+ * AgentTool —— 用于派生任务子代理的协作工具(v2 §7.2)。
5161
+ *
5162
+ * 这是一个“协作工具”(v2 §9-F.5),与内建工具
5163
+ * (Read/Write/Edit/Bash/Grep/Glob)不同。它使用 `SubagentHost`
5164
+ * (通过构造函数注入,而不是通过 Runtime)来创建同进程的
5165
+ * 子代理 Soul 实例。
5166
+ *
5167
+ * 两种模式:
5168
+ * - **Foreground**(默认):阻塞父回合,`await handle.completion`
5169
+ * - **Background**:立即返回 agent id,结果通过通知投递
5170
+ *
5171
+ * Slice 5.3 —— 已实现前台与后台执行路径。
5172
+ * 与 Python 实现对齐:`kimi_cli.tools.agent.__init__.AgentTool`
5173
+ * (文本形式的 `ToolResult.content`;通过 `AgentToolOutputSchema`
5174
+ * 提供的结构化输出仅用于 drift-guard,不在运行时消费——见 TD1)。
5175
+ */
5173
5176
  const AgentToolInputSchema = z.preprocess((input) => {
5174
5177
  if (typeof input !== "object" || input === null || Array.isArray(input)) return input;
5175
5178
  const record = input;
@@ -5194,15 +5197,30 @@ z.object({
5194
5197
  cache_write: z.number().int().nonnegative().optional()
5195
5198
  }).describe("Cumulative token usage")
5196
5199
  });
5200
+ const AGENT_DESCRIPTION_BASE = [
5201
+ "Launch a subagent to handle a task. The subagent runs as a same-process Soul instance with its own context and wire file.",
5202
+ "",
5203
+ "Writing the prompt:",
5204
+ "- The subagent starts with zero context — it has not seen this conversation. Brief it like a colleague who just walked into the room: state the goal, list what you already know, hand over the specifics.",
5205
+ "- Lookups (read this file, run that test): put the exact path or command in the prompt. The subagent should not have to search for things you already know.",
5206
+ "- Investigations (figure out X, find why Y): give the question, not prescribed steps — fixed steps become dead weight when the premise is wrong.",
5207
+ "- Do not delegate understanding. If the task hinges on a file path or line number, find it yourself first and write it into the prompt.",
5208
+ "",
5209
+ "Once a subagent is running, leave that scope to it: do not redo its searches or reads in parallel, and do not abandon it midway and finish the job manually. Both undo the context savings the delegation was meant to buy.",
5210
+ "",
5211
+ "When `runInBackground=true`, the subagent runs detached from this turn. The completion arrives in a later turn as a synthetic user-role message containing its result — you do not need to poll, sleep, or check on its progress. Continue with other work or respond to the user. Never fabricate or predict what the result will say."
5212
+ ].join("\n");
5197
5213
  var AgentTool = class {
5198
5214
  name = "Agent";
5199
- description = "Launch a subagent to handle a task. The subagent runs as a same-process Soul instance with its own context and wire file.";
5215
+ description;
5200
5216
  inputSchema = AgentToolInputSchema;
5201
5217
  constructor(subagentHost, parentAgentId, backgroundManager, typeRegistry) {
5202
5218
  this.subagentHost = subagentHost;
5203
5219
  this.parentAgentId = parentAgentId;
5204
5220
  this.backgroundManager = backgroundManager;
5205
5221
  this.typeRegistry = typeRegistry;
5222
+ const typeLines = typeRegistry?.list().length ? typeRegistry.buildTypeDescriptions() : "";
5223
+ this.description = typeLines ? `${AGENT_DESCRIPTION_BASE}\n\nAvailable agent types (pass via subagent_type):\n${typeLines}` : AGENT_DESCRIPTION_BASE;
5206
5224
  }
5207
5225
  async execute({ toolCallId, args, signal, wireUuid }) {
5208
5226
  try {
@@ -5988,202 +6006,6 @@ function registerInteractiveTools(options) {
5988
6006
  if (options.isToolEnabled("AskUserQuestion")) options.toolRegistry.push(new AskUserQuestionTool(questionRuntime, () => options.turnManager.getPermissionMode()));
5989
6007
  }
5990
6008
  //#endregion
5991
- //#region ../../packages/kimi-core/src/soul-plus/compaction/tokens.ts
5992
- /**
5993
- * Token-estimation helper used by `CompactionOrchestrator` to seed
5994
- * `postCompactTokens` after a summary is produced.
5995
- *
5996
- * Lives on the SoulPlus side because Soul itself never calls it —
5997
- * Soul's only interest in tokens is `shouldCompact`, which reads the
5998
- * precomputed `tokenCountWithPending` off `SoulContextState`.
5999
- */
6000
- /**
6001
- * Estimate token count from text using a character-based heuristic.
6002
- * - ASCII (~4 chars per token)
6003
- * - CJK and other non-ASCII (~1 char per token)
6004
- * The estimate is transient — the next LLM call returns the real count
6005
- * and supersedes this value. Used to keep `tokenCountWithPending`
6006
- * monotonic between LLM round-trips without paying for a tokenizer.
6007
- */
6008
- function estimateTokens(text) {
6009
- let asciiCount = 0;
6010
- let nonAsciiCount = 0;
6011
- for (const char of text) if (char.codePointAt(0) <= 127) asciiCount++;
6012
- else nonAsciiCount++;
6013
- return Math.ceil(asciiCount / 4) + nonAsciiCount;
6014
- }
6015
- //#endregion
6016
- //#region ../../packages/kimi-core/src/soul-plus/compaction/compaction-orchestrator.ts
6017
- var CompactionOrchestrator = class {
6018
- constructor(deps) {
6019
- this.deps = deps;
6020
- }
6021
- /**
6022
- * Core compaction pipeline — assumes the lifecycle machine has already
6023
- * landed in `active`. Drives `active → compacting → active`:
6024
- *
6025
- * 1. transitionTo('compacting')
6026
- * 2. emit compaction.begin
6027
- * 3. provider.run(messages, signal, {userInstructions?})
6028
- * 4. journalWriter.flush() (Phase 3 铁律 — before rotate)
6029
- * 5. journalWriter.rotate(...)
6030
- * 6. contextState.resetToSummary(storageSummary)
6031
- * 7. emit compaction.end
6032
- * 8. finally: transitionTo('active')
6033
- *
6034
- * `signal.throwIfAborted()` is checked at two points: immediately after
6035
- * begin (so a pre-aborted signal never invokes the provider) and
6036
- * immediately after provider.run (so a slow provider doesn't produce a
6037
- * summary that races with concurrent cancel). There is no abort check
6038
- * between rotate and resetToSummary — once rotate has renamed the old
6039
- * wire.jsonl, aborting would leave the fresh wire.jsonl without its
6040
- * CompactionRecord.
6041
- */
6042
- async executeCompaction(signal, customInstruction, trigger = "auto") {
6043
- const rotateCapability = this.deps.journalWriter.rotateCapability;
6044
- if (!rotateCapability) throw new Error("JournalWriter missing rotate capability");
6045
- const machine = this.deps.lifecycleStateMachine;
6046
- machine.transitionTo("compacting");
6047
- let tailUserText;
6048
- try {
6049
- this.deps.sink.emit({ type: "compaction.begin" });
6050
- signal.throwIfAborted();
6051
- const messages = this.deps.contextState.buildMessages();
6052
- const preCompactTokens = this.deps.contextState.tokenCountWithPending;
6053
- const summary = await (this.deps.runtimeSlot?.current().compactionProvider ?? this.deps.compactionProvider).run(messages, signal, customInstruction !== void 0 ? { userInstructions: customInstruction } : void 0);
6054
- signal.throwIfAborted();
6055
- const baselineInit = await rotateCapability.readSessionInitialized();
6056
- const cs = this.deps.contextState;
6057
- const sessionInitialized = applyRuntimeOverlay(baselineInit, {
6058
- system_prompt: cs.systemPrompt,
6059
- active_tools: [...cs.activeTools]
6060
- });
6061
- const rotateResult = await rotateCapability.rotate();
6062
- await rotateCapability.appendBoundary(sessionInitialized);
6063
- tailUserText = extractUnpairedTailUserText(messages);
6064
- const storageSummary = bridgeSummaryMessage(summary, messages.length, preCompactTokens, trigger, rotateResult.archivePath, this.deps.contextState.systemPrompt, tailUserText);
6065
- await this.deps.contextState.resetToSummary(storageSummary);
6066
- this.deps.sink.emit({
6067
- type: "compaction.end",
6068
- tokensBefore: preCompactTokens,
6069
- tokensAfter: storageSummary.postCompactTokens
6070
- });
6071
- } finally {
6072
- machine.transitionTo("active");
6073
- }
6074
- if (tailUserText !== void 0) await this.deps.contextState.appendUserMessage({ text: tailUserText });
6075
- }
6076
- /**
6077
- * Manual `/compact` slash-command entry point. Drives the full lifecycle
6078
- * dance from `idle`:
6079
- *
6080
- * idle → active → (executeCompaction: active → compacting → active) →
6081
- * completing → idle
6082
- *
6083
- * Throws if the machine is not `idle` at entry — `Cannot compact while
6084
- * a turn is active`. PreCompact / PostCompact hooks fire here (not in
6085
- * `executeCompaction`) because the hook scope covers the *entire*
6086
- * compaction request, including the idle-drain bracketing.
6087
- */
6088
- async triggerCompaction(customInstruction) {
6089
- if (!this.deps.lifecycleStateMachine.isIdle()) throw new Error("Cannot compact while a turn is active");
6090
- if (this.deps.getPendingTurnId() !== void 0) throw new Error("Cannot compact while a prompt launch is in flight");
6091
- const controller = new AbortController();
6092
- this.deps.lifecycleStateMachine.transitionTo("active");
6093
- const sessionId = this.deps.sessionId ?? "unknown";
6094
- const agentId = this.deps.agentId ?? "agent_main";
6095
- if (this.deps.hookEngine !== void 0) this.deps.hookEngine.executeHooks("PreCompact", {
6096
- event: "PreCompact",
6097
- sessionId,
6098
- turnId: "compact",
6099
- agentId
6100
- }, controller.signal).catch(() => {});
6101
- const tokensBefore = this.deps.contextState.tokenCountWithPending;
6102
- try {
6103
- await this.executeCompaction(controller.signal, customInstruction, "manual");
6104
- } finally {
6105
- const tokensAfter = this.deps.contextState.tokenCountWithPending;
6106
- if (this.deps.hookEngine !== void 0) this.deps.hookEngine.executeHooks("PostCompact", {
6107
- event: "PostCompact",
6108
- sessionId,
6109
- turnId: "compact",
6110
- agentId,
6111
- tokensBefore,
6112
- tokensAfter
6113
- }, controller.signal).catch(() => {});
6114
- if (this.deps.lifecycleStateMachine.isActive()) this.deps.lifecycleStateMachine.transitionTo("completing");
6115
- if (this.deps.lifecycleStateMachine.isCompleting()) this.deps.lifecycleStateMachine.transitionTo("idle");
6116
- }
6117
- }
6118
- };
6119
- /**
6120
- * Phase 23 fix — overlay the runtime-mutable subset of a baseline
6121
- * `session_initialized` with the live wire-owned ContextState baseline.
6122
- *
6123
- * Identity-class fields (`type`, `seq`, `time`, `agent_type`, `session_id`,
6124
- * `agent_id`, parent lineage) are preserved verbatim
6125
- * because they cannot legally mutate at runtime — see the discriminated-union
6126
- * shape in `wire-record.ts`. The mutable wire-owned fields
6127
- * (`system_prompt`, `active_tools`) are overwritten so the post-rotate
6128
- * baseline reflects the compaction-time snapshot rather than the original
6129
- * startup config. Runtime/session fields live in state.json.
6130
- *
6131
- * The generic preserves the discriminated-union narrowing so each branch
6132
- * (main / sub / independent) returns its own concrete type.
6133
- */
6134
- function applyRuntimeOverlay(baseline, overlay) {
6135
- return {
6136
- ...baseline,
6137
- system_prompt: overlay.system_prompt,
6138
- active_tools: [...overlay.active_tools]
6139
- };
6140
- }
6141
- /**
6142
- * Translate the provider's Soul-facing `RuntimeSummaryMessage` into the
6143
- * storage layer's `StorageSummaryMessage`. Ported verbatim from the old
6144
- * `turn-manager.ts:bridgeSummaryMessage`.
6145
- */
6146
- /**
6147
- * If the input message array ends on an unpaired user message (no following
6148
- * assistant response), return that user's text so the caller can re-append it
6149
- * to the post-compaction conversation state (决策 #101).
6150
- *
6151
- * Returns `undefined` when the tail is not an unpaired user message.
6152
- */
6153
- function extractUnpairedTailUserText(messages) {
6154
- if (messages.length === 0) return void 0;
6155
- const tail = messages.at(-1);
6156
- if (tail === void 0) return void 0;
6157
- if (tail.role !== "user") return void 0;
6158
- const content = tail.content;
6159
- if (typeof content === "string") return content;
6160
- if (Array.isArray(content)) {
6161
- const parts = [];
6162
- for (const part of content) if (typeof part === "object" && part !== null && "type" in part) {
6163
- const typed = part;
6164
- if (typed.type === "text" && typeof typed.text === "string") parts.push(typed.text);
6165
- }
6166
- return parts.length > 0 ? parts.join("") : void 0;
6167
- }
6168
- }
6169
- function bridgeSummaryMessage(providerSummary, messagesCount, preCompactTokens, trigger, archivePath, systemPrompt, tailUserText) {
6170
- const summaryTokens = estimateTokens(providerSummary.content);
6171
- const systemTokens = systemPrompt ? estimateTokens(systemPrompt) : 0;
6172
- const tailTokens = tailUserText ? estimateTokens(tailUserText) : 0;
6173
- return {
6174
- summary: providerSummary.content,
6175
- compactedRange: {
6176
- fromTurn: 1,
6177
- toTurn: providerSummary.original_turn_count ?? messagesCount,
6178
- messageCount: messagesCount
6179
- },
6180
- preCompactTokens,
6181
- postCompactTokens: summaryTokens + systemTokens + tailTokens,
6182
- trigger,
6183
- archivePath
6184
- };
6185
- }
6186
- //#endregion
6187
6009
  //#region ../../packages/kimi-core/src/soul-plus/assembly/turn-runtime.ts
6188
6010
  function createTurnManagerRef() {
6189
6011
  let current;
@@ -6200,27 +6022,6 @@ function createTurnManagerRef() {
6200
6022
  }
6201
6023
  };
6202
6024
  }
6203
- function createSoulPlusCompactionOrchestrator(deps) {
6204
- return new CompactionOrchestrator({
6205
- contextState: deps.contextState,
6206
- compactionProvider: deps.compactionProvider,
6207
- runtimeSlot: deps.runtimeSlot,
6208
- lifecycleStateMachine: deps.lifecycleStateMachine,
6209
- sink: deps.sink,
6210
- journalWriter: deps.journalWriter,
6211
- sessionId: deps.sessionId,
6212
- agentId: "agent_main",
6213
- runtimeStateProvider: () => {
6214
- const turnManager = deps.turnManagerRef.require("CompactionOrchestrator.runtimeStateProvider");
6215
- return {
6216
- permissionMode: turnManager.getPermissionMode(),
6217
- planMode: turnManager.getPlanMode(),
6218
- thinkingLevel: turnManager.getThinkingLevel()
6219
- };
6220
- },
6221
- getPendingTurnId: () => deps.turnManagerRef.require("CompactionOrchestrator.getPendingTurnId").getCurrentTurnId()
6222
- });
6223
- }
6224
6025
  function createSoulPlusToolExecutionScopeFactory(deps) {
6225
6026
  if (deps.providedFactory !== void 0) return deps.providedFactory;
6226
6027
  if (deps.approvalRuntime !== void 0 && deps.hookEngine !== void 0) return new DefaultToolExecutionScopeFactory({
@@ -6987,48 +6788,6 @@ var NotificationManager = class {
6987
6788
  }
6988
6789
  };
6989
6790
  //#endregion
6990
- //#region ../../packages/kimi-core/src/soul-plus/session/session-control.ts
6991
- var DefaultSessionControl = class {
6992
- turnManager;
6993
- contextState;
6994
- setPlanModeOverride;
6995
- setYoloOverride;
6996
- constructor(deps) {
6997
- this.turnManager = deps.turnManager;
6998
- this.contextState = deps.contextState;
6999
- this.setPlanModeOverride = deps.setPlanModeOverride;
7000
- this.setYoloOverride = deps.setYoloOverride;
7001
- }
7002
- async compact(customInstruction) {
7003
- await this.turnManager.triggerCompaction(customInstruction);
7004
- }
7005
- async clear() {
7006
- if (!this.turnManager.tryReserveForMaintenance()) throw new Error(`Cannot clear while session is ${this.turnManager.getLifecycleState()} — cancel the current turn or wait for compaction to finish first.`);
7007
- try {
7008
- await this.contextState.clear();
7009
- } finally {
7010
- this.turnManager.releaseMaintenance();
7011
- }
7012
- }
7013
- async setPlanMode(enabled) {
7014
- if (this.setPlanModeOverride !== void 0) {
7015
- await this.setPlanModeOverride(enabled);
7016
- return;
7017
- }
7018
- this.turnManager.setPlanMode(enabled);
7019
- }
7020
- async setYolo(enabled) {
7021
- if (this.setYoloOverride !== void 0) {
7022
- await this.setYoloOverride(enabled);
7023
- return;
7024
- }
7025
- const previousMode = this.turnManager.getPermissionMode();
7026
- const newMode = enabled ? "bypassPermissions" : "default";
7027
- if (previousMode === newMode) return;
7028
- this.turnManager.setPermissionMode(newMode);
7029
- }
7030
- };
7031
- //#endregion
7032
6791
  //#region ../../packages/kimi-core/src/soul-plus/session/session-meta-service.ts
7033
6792
  const DEFAULT_FLUSH_DEBOUNCE_MS = 200;
7034
6793
  var SessionMetaService = class {
@@ -20806,6 +20565,252 @@ function checkLLMCapabilities(opts) {
20806
20565
  if (inputContainsAudio && !capability.audio_in) return new LLMCapabilityMismatchError(`Model "${model}" does not accept audio input`);
20807
20566
  }
20808
20567
  //#endregion
20568
+ //#region ../../packages/kimi-core/src/soul-plus/compaction/tokens.ts
20569
+ /**
20570
+ * Estimate token count from text using a character-based heuristic.
20571
+ * - ASCII (~4 chars per token)
20572
+ * - CJK and other non-ASCII (~1 char per token)
20573
+ * The estimate is transient — the next LLM call returns the real count
20574
+ * and supersedes this value. Used to keep `tokenCountWithPending`
20575
+ * monotonic between LLM round-trips without paying for a tokenizer.
20576
+ */
20577
+ function estimateTokens(text) {
20578
+ let asciiCount = 0;
20579
+ let nonAsciiCount = 0;
20580
+ for (const char of text) if (char.codePointAt(0) <= 127) asciiCount++;
20581
+ else nonAsciiCount++;
20582
+ return Math.ceil(asciiCount / 4) + nonAsciiCount;
20583
+ }
20584
+ function estimateTokensForMessages(messages) {
20585
+ let total = 0;
20586
+ for (const message of messages) total += estimateTokensForMessage(message);
20587
+ return total;
20588
+ }
20589
+ function estimateTokensForMessage(message) {
20590
+ let total = estimateTokens(message.role);
20591
+ for (const part of message.content) total += estimateTokensForContentPart(part);
20592
+ if (message.toolCalls !== void 0) for (const call of message.toolCalls) {
20593
+ total += estimateTokens(call.function.name);
20594
+ total += estimateTokens(JSON.stringify(call.function.arguments));
20595
+ }
20596
+ return total;
20597
+ }
20598
+ function estimateTokensForContentPart(part) {
20599
+ if (part.type === "text") return estimateTokens(part.text);
20600
+ else if (part.type === "think") return estimateTokens(part.think);
20601
+ else return 0;
20602
+ }
20603
+ //#endregion
20604
+ //#region ../../packages/kimi-core/src/soul-plus/compaction/full.ts
20605
+ var DefaultCompactionStrategy = class {
20606
+ triggerRatio = .8;
20607
+ blockRatio = .9;
20608
+ maxCompactionPerTurn = 3;
20609
+ maxRecentSteps = 3;
20610
+ maxRecentUserMessages = Infinity;
20611
+ maxRecentSizeRatio = .2;
20612
+ constructor(maxContextSize) {
20613
+ this.maxContextSize = maxContextSize;
20614
+ }
20615
+ shouldCompact(usedSize) {
20616
+ return usedSize >= this.maxContextSize * this.triggerRatio;
20617
+ }
20618
+ shouldBlock(usedSize) {
20619
+ return usedSize >= this.maxContextSize * this.blockRatio;
20620
+ }
20621
+ splitCompactAndRecent(messages) {
20622
+ let splitAt = messages.length;
20623
+ let recentSize = 0;
20624
+ let userMessageCount = 0;
20625
+ for (let i = messages.length - 1; i >= 0; i--) {
20626
+ if (messages.length - i >= this.maxRecentSteps) break;
20627
+ const m1 = messages[i - 1];
20628
+ const m2 = messages[i];
20629
+ if (m2.role === "user") {
20630
+ userMessageCount++;
20631
+ if (userMessageCount > this.maxRecentUserMessages) break;
20632
+ }
20633
+ recentSize += estimateTokensForMessage(m2);
20634
+ if (recentSize > this.maxContextSize * this.maxRecentSizeRatio) break;
20635
+ if (m1?.role === m2.role) continue;
20636
+ if (m1?.role === "user" && m2.role === "assistant") continue;
20637
+ if (m2.role === "tool") continue;
20638
+ splitAt = i;
20639
+ }
20640
+ return [messages.slice(0, splitAt), messages.slice(splitAt)];
20641
+ }
20642
+ };
20643
+ var FullCompactionProvider = class {
20644
+ deps;
20645
+ compactionCountInTurn = 0;
20646
+ compacting = null;
20647
+ lastSoulConfig = null;
20648
+ constructor(deps) {
20649
+ this.deps = deps;
20650
+ }
20651
+ shouldCompact() {
20652
+ return this.deps.strategy.shouldCompact(this.deps.context.tokenCountWithPending);
20653
+ }
20654
+ shouldBlock() {
20655
+ return this.deps.strategy.shouldBlock?.(this.deps.context.tokenCountWithPending) ?? true;
20656
+ }
20657
+ beforeTurn() {
20658
+ this.compactionCountInTurn = 0;
20659
+ }
20660
+ async handleOverflowError(signal) {
20661
+ this.checkAutoCompaction();
20662
+ await this.block(signal);
20663
+ }
20664
+ async beforeStep(signal) {
20665
+ this.checkAutoCompaction();
20666
+ if (this.shouldBlock()) await this.block(signal);
20667
+ }
20668
+ async afterStep() {
20669
+ this.checkAutoCompaction();
20670
+ }
20671
+ triggerCompaction(customInstruction) {
20672
+ this.compactionCountInTurn = 0;
20673
+ if (!this.compacting) this.startCompaction(customInstruction);
20674
+ }
20675
+ checkAutoCompaction() {
20676
+ if (this.compacting || !this.shouldCompact()) return;
20677
+ const maxCompactions = this.deps.strategy.maxCompactionPerTurn;
20678
+ this.compactionCountInTurn += 1;
20679
+ if (this.compactionCountInTurn > maxCompactions) {
20680
+ this.deps.sink.emit({
20681
+ type: "session.error",
20682
+ error: `Compaction limit exceeded (${maxCompactions})`,
20683
+ error_type: "context_overflow"
20684
+ });
20685
+ throw new Error(`Compaction limit exceeded (${maxCompactions})`);
20686
+ }
20687
+ this.triggerCompaction();
20688
+ }
20689
+ async block(signal) {
20690
+ if (this.compacting) {
20691
+ signal.addEventListener("abort", () => {
20692
+ this.compacting?.abortController.abort();
20693
+ });
20694
+ this.deps.sink.emit({ type: "compaction.blocked" });
20695
+ await this.compacting.promise;
20696
+ }
20697
+ }
20698
+ startCompaction(customInstruction) {
20699
+ const abortController = new AbortController();
20700
+ this.compacting = {
20701
+ abortController,
20702
+ promise: this.compactionWorker(abortController.signal, customInstruction).finally(() => this.compacting = null)
20703
+ };
20704
+ }
20705
+ async compactionWorker(signal, customInstruction) {
20706
+ this.deps.sink.emit({ type: "compaction.begin" });
20707
+ const history = this.deps.context.getHistory();
20708
+ const messages = this.deps.context.buildMessages();
20709
+ const preCompactTokens = this.deps.context.tokenCountWithPending;
20710
+ const [toCompact, recent] = this.deps.strategy.splitCompactAndRecent(messages);
20711
+ const result = await this.deps.runtime.kosong.chat({
20712
+ messages: [...toCompact, {
20713
+ role: "user",
20714
+ content: [{
20715
+ type: "text",
20716
+ text: COMPACTION_INSTRUCTION(customInstruction)
20717
+ }],
20718
+ toolCalls: []
20719
+ }],
20720
+ tools: buildLLMVisibleTools(this.lastSoulConfig?.tools),
20721
+ model: this.deps.context.model,
20722
+ systemPrompt: this.deps.context.systemPrompt,
20723
+ signal
20724
+ });
20725
+ const summary = typeof result.message.content === "string" ? result.message.content : result.message.content.map((part) => part.type === "text" ? part.text : "").join("");
20726
+ const newHistory = this.deps.context.getHistory();
20727
+ for (let i = 0; i < history.length; i++) if (newHistory[i] !== history[i]) {
20728
+ this.deps.sink.emit({ type: "compaction.canceled" });
20729
+ return;
20730
+ }
20731
+ const postCompactTokens = estimateTokens(summary) + estimateTokensForMessages(recent);
20732
+ await this.deps.context.resetToSummary({
20733
+ summary,
20734
+ preCompactTokens,
20735
+ postCompactTokens,
20736
+ preservedRecent: recent
20737
+ });
20738
+ this.deps.sink.emit({
20739
+ type: "compaction.end",
20740
+ tokensBefore: preCompactTokens,
20741
+ tokensAfter: postCompactTokens
20742
+ });
20743
+ }
20744
+ };
20745
+ const COMPACTION_INSTRUCTION = (customInstruction = "") => `
20746
+ --- This message is a direct task, not part of the above conversation ---
20747
+
20748
+ You are now given a task to compact this conversation context according to specific priorities and output requirements.
20749
+
20750
+ Output text only. DO NOT CALL ANY TOOLS. Calling tools will be rejected and fails the task. You already have all the information you need in the conversation history. You have only one chance.
20751
+
20752
+ The goal of compaction is to keep essential code patterns, technical details, and architectural decisions for continuing development without losing context after the above messages are cleared work.
20753
+
20754
+ ${customInstruction}
20755
+
20756
+ <!-- Compression Priorities (in order) -->
20757
+
20758
+ 1. **Current Task State**: What is being worked on RIGHT NOW
20759
+ 2. **Errors & Solutions**: All encountered errors and their resolutions
20760
+ 3. **Code Evolution**: Final working versions only (remove intermediate attempts)
20761
+ 4. **System Context**: Project structure, dependencies, environment setup
20762
+ 5. **Design Decisions**: Architectural choices and their rationale
20763
+ 6. **TODO Items**: Unfinished tasks and known issues
20764
+
20765
+ <!-- Required Output Structure -->
20766
+
20767
+ ## Current Focus
20768
+
20769
+ [What we're working on now]
20770
+
20771
+ ## Environment
20772
+
20773
+ - [Key setup/config points]
20774
+ - ...
20775
+
20776
+ ## Completed Tasks
20777
+
20778
+ - [Task]: [Brief outcome]
20779
+ - ...
20780
+
20781
+ ## Active Issues
20782
+
20783
+ - [Issue]: [Status/Next steps]
20784
+ - ...
20785
+
20786
+ ## Code State
20787
+
20788
+ ### [Critical file name]
20789
+
20790
+ [Brief description of the file's purpose and current state]
20791
+
20792
+ \`\`\`
20793
+ [The latest version of critical code snippets in this file, <20 lines]
20794
+ \`\`\`
20795
+
20796
+ ### [Critical file name]
20797
+
20798
+ - [Useful classes/methods/functions]: [Brief description/usage]
20799
+ - ...
20800
+
20801
+ <!-- Omit non-critical code, intermediate attempts, and resolved errors -->
20802
+
20803
+ ## Important Context
20804
+
20805
+ - [Any crucial information not covered above]
20806
+ - ...
20807
+
20808
+ ## All User Messages
20809
+
20810
+ - [Detailed non tool use user message]
20811
+ - ...
20812
+ `;
20813
+ //#endregion
20809
20814
  //#region ../../packages/kimi-core/src/soul-plus/turn/turn-manager.ts
20810
20815
  /**
20811
20816
  * TurnManager — Phase 4 coordinator (v2 §6.4 / 决策 #109).
@@ -20842,37 +20847,12 @@ function checkLLMCapabilities(opts) {
20842
20847
  * 3. await lifecycle.cancelTurn(turnId) — drain
20843
20848
  */
20844
20849
  /**
20845
- * Phase 2 (todo Step 7): hard cap on how many compaction round-trips a
20846
- * single turn is allowed. After the cap is exceeded the turn is
20847
- * terminated with a `session.error` / `stopReason='error'` so a
20848
- * misbehaving provider cannot lock the session in an infinite
20849
- * compaction loop.
20850
- */
20851
- const MAX_COMPACTIONS_PER_TURN = 3;
20852
- /**
20853
20850
  * Default context window used for `status.update.context_usage.total`
20854
20851
  * when the session has no `compactionConfig.maxContextSize`. Matches
20855
20852
  * the fallback used by existing config schema (200k-token window of
20856
20853
  * typical GPT-4/Claude models).
20857
20854
  */
20858
20855
  const DEFAULT_CONTEXT_WINDOW = 2e5;
20859
- function zeroUsage() {
20860
- return {
20861
- input: 0,
20862
- output: 0
20863
- };
20864
- }
20865
- function configuredCapabilitiesToModelCapability(capabilities, maxContextTokens) {
20866
- const caps = new Set(capabilities.map((cap) => cap.toLowerCase()));
20867
- return {
20868
- image_in: caps.has("image_in"),
20869
- video_in: caps.has("video_in"),
20870
- audio_in: caps.has("audio_in"),
20871
- thinking: caps.has("thinking") || caps.has("always_thinking"),
20872
- tool_use: caps.has("tool_use"),
20873
- max_context_tokens: maxContextTokens
20874
- };
20875
- }
20876
20856
  function mergeTurnRules(sessionRules, pendingOverrides) {
20877
20857
  if (pendingOverrides === void 0) return sessionRules;
20878
20858
  const turnRules = [];
@@ -20902,9 +20882,9 @@ var TurnManager = class {
20902
20882
  thinkingLevel;
20903
20883
  runtime;
20904
20884
  runtimeSlot;
20905
- compactionConfig;
20906
20885
  sessionId;
20907
20886
  executionHooks;
20887
+ fullCompactionProvider;
20908
20888
  activeToolExecutionScope;
20909
20889
  pendingLaunchController;
20910
20890
  pendingLaunchDrain;
@@ -20933,9 +20913,17 @@ var TurnManager = class {
20933
20913
  this.planMode = deps.planMode ?? false;
20934
20914
  this.runtime = deps.runtime;
20935
20915
  this.runtimeSlot = deps.runtimeSlot;
20936
- this.compactionConfig = deps.compactionConfig;
20937
20916
  this.sessionId = deps.sessionId ?? "unknown";
20938
20917
  this.executionHooks = new ExecutionHookPipeline({ hookEngine: deps.hookEngine });
20918
+ this.fullCompactionProvider = deps.compactionConfig ? new FullCompactionProvider({
20919
+ strategy: new DefaultCompactionStrategy(1e6),
20920
+ runtime: deps.runtime,
20921
+ context: deps.contextState,
20922
+ sink: deps.sink
20923
+ }) : void 0;
20924
+ if (deps.wakeScheduler !== void 0) deps.wakeScheduler.setOnEnqueue(() => {
20925
+ this.processWakeQueue();
20926
+ });
20939
20927
  }
20940
20928
  setPermissionMode(mode) {
20941
20929
  this.permissionMode = mode;
@@ -20959,16 +20947,6 @@ var TurnManager = class {
20959
20947
  getThinkingLevel() {
20960
20948
  return this.thinkingLevel;
20961
20949
  }
20962
- setCompactionConfig(config) {
20963
- if (this.runtimeSlot !== void 0) {
20964
- const current = this.runtimeSlot.current();
20965
- this.runtimeSlot.replace({
20966
- ...current,
20967
- ...config !== void 0 ? { compactionConfig: config } : { compactionConfig: void 0 }
20968
- });
20969
- }
20970
- this.compactionConfig = config;
20971
- }
20972
20950
  setRuntime(runtime) {
20973
20951
  if (this.runtimeSlot !== void 0) {
20974
20952
  const current = this.runtimeSlot.current();
@@ -21076,7 +21054,7 @@ var TurnManager = class {
21076
21054
  * facade.
21077
21055
  */
21078
21056
  async triggerCompaction(customInstruction) {
21079
- return this.deps.compaction.triggerCompaction(customInstruction);
21057
+ return this.fullCompactionProvider?.triggerCompaction(customInstruction);
21080
21058
  }
21081
21059
  /**
21082
21060
  * Cancel an in-flight turn per v2 §7.2 three-step contract (决策 #102):
@@ -21133,12 +21111,8 @@ var TurnManager = class {
21133
21111
  async handlePrompt(req) {
21134
21112
  if (!this.deps.lifecycleStateMachine.isIdle() || this.getCurrentTurnId() !== void 0) return { error: "agent_busy" };
21135
21113
  const input = req.data.input;
21136
- const runtimeBundle = this.runtimeSlot?.current();
21137
- const runtime = runtimeBundle?.runtime ?? this.runtime;
21138
- const configuredCapabilities = runtimeBundle !== void 0 ? runtimeBundle.modelCapabilities : this.deps.modelCapabilities;
21139
- const capability = configuredCapabilities !== void 0 ? configuredCapabilitiesToModelCapability(configuredCapabilities, runtimeBundle?.compactionConfig?.maxContextSize ?? this.compactionConfig?.maxContextSize ?? 0) : runtime.kosong.getCapability?.();
21140
- const capabilityIsAuthoritative = configuredCapabilities !== void 0 || capability !== void 0 && !isUnknownCapability(capability);
21141
- if (capability !== void 0 && capabilityIsAuthoritative) {
21114
+ const capability = ((this.runtimeSlot?.current())?.runtime ?? this.runtime).kosong.getCapability?.();
21115
+ if (capability !== void 0 && !isUnknownCapability(capability)) {
21142
21116
  let inputContainsImage = false;
21143
21117
  let inputContainsVideo = false;
21144
21118
  for (const part of input.parts ?? []) if (part.type === "image_url") inputContainsImage = true;
@@ -21204,6 +21178,96 @@ var TurnManager = class {
21204
21178
  throw error;
21205
21179
  }
21206
21180
  }
21181
+ /**
21182
+ * Auto-wake entry — start a turn from a `TurnTrigger` produced by
21183
+ * something other than user input (currently: subagent completion fan
21184
+ * out via SoulRegistry → NotificationManager → WakeQueueScheduler).
21185
+ *
21186
+ * Mirrors `handlePrompt` for the lifecycle / launch machinery but with
21187
+ * three deliberate differences:
21188
+ *
21189
+ * - `input_kind: 'system_trigger'` on the `turn_begin` WAL record so
21190
+ * wire consumers can distinguish auto-wake from user-driven turns.
21191
+ * - Skips `appendUserMessage` — the wake's payload (e.g. the
21192
+ * `<notification>` XML for a completed subagent) is already in
21193
+ * `contextState` because NotificationManager.appendNotification
21194
+ * ran *before* the wake was enqueued. Appending the trigger's
21195
+ * (typically empty) UserInput would drop a stray user message
21196
+ * ahead of those notifications and confuse the model.
21197
+ * - Skips capability checks and the `UserPromptSubmit` hook — those
21198
+ * are user-input concerns, not auto-wake concerns.
21199
+ */
21200
+ async handleSystemTrigger(req) {
21201
+ if (!this.deps.lifecycleStateMachine.isIdle() || this.getCurrentTurnId() !== void 0) return { error: "agent_busy" };
21202
+ const trigger = req.trigger;
21203
+ const input = trigger.input;
21204
+ const inputKind = trigger.kind === "user_prompt" ? "user" : "system_trigger";
21205
+ const turnId = this.deps.lifecycle.allocateTurnId();
21206
+ const pendingLaunchController = new AbortController();
21207
+ this.pendingLaunchTurnId = turnId;
21208
+ this.pendingLaunchController = pendingLaunchController;
21209
+ let releasePendingLaunch;
21210
+ this.pendingLaunchDrain = new Promise((resolve) => {
21211
+ releasePendingLaunch = resolve;
21212
+ });
21213
+ try {
21214
+ await this.executePreTurnHook(turnId, input, pendingLaunchController.signal);
21215
+ pendingLaunchController.signal.throwIfAborted();
21216
+ await this.deps.sessionJournal.appendTurnBegin({
21217
+ type: "turn_begin",
21218
+ turn_id: turnId,
21219
+ agent_type: this.agentType,
21220
+ user_input: input.text,
21221
+ input_kind: inputKind
21222
+ });
21223
+ this.deps.lifecycleStateMachine.transitionTo("active");
21224
+ this.deps.lifecycle.fireLifecycleEvent({
21225
+ kind: "begin",
21226
+ turnId,
21227
+ userInput: input.text,
21228
+ userInputParts: input.parts,
21229
+ inputKind,
21230
+ agentType: this.agentType
21231
+ });
21232
+ this.launchTurn(turnId);
21233
+ this.pendingLaunchTurnId = void 0;
21234
+ this.pendingLaunchController = void 0;
21235
+ this.pendingLaunchDrain = void 0;
21236
+ releasePendingLaunch();
21237
+ return {
21238
+ turn_id: turnId,
21239
+ status: "started"
21240
+ };
21241
+ } catch (error) {
21242
+ this.pendingLaunchTurnId = void 0;
21243
+ this.pendingLaunchController = void 0;
21244
+ this.pendingLaunchDrain = void 0;
21245
+ releasePendingLaunch();
21246
+ throw error;
21247
+ }
21248
+ }
21249
+ /**
21250
+ * Drain the WakeQueueScheduler and start a turn if the lifecycle is
21251
+ * idle. Called synchronously from WakeQueueScheduler's onEnqueue
21252
+ * listener — see constructor wiring. Multiple queued triggers collapse
21253
+ * into a single turn: the *contextState* already carries every queued
21254
+ * notification's payload, so one turn-start is enough to surface them
21255
+ * all at once.
21256
+ *
21257
+ * Race tolerance: when called while a turn is already running we leave
21258
+ * the queue alone and accept a small drop window (a wake that arrives
21259
+ * mid-turn won't auto-fire after that turn ends). The notifications
21260
+ * themselves are durable in contextState, so any subsequent turn —
21261
+ * including a user-driven one — still sees them via `buildMessages`.
21262
+ */
21263
+ processWakeQueue() {
21264
+ const scheduler = this.deps.wakeScheduler;
21265
+ if (scheduler === void 0) return;
21266
+ if (!this.deps.lifecycleStateMachine.isIdle() || this.getCurrentTurnId() !== void 0) return;
21267
+ const head = scheduler.drain()[0];
21268
+ if (head === void 0) return;
21269
+ this.handleSystemTrigger({ trigger: head }).catch(() => {});
21270
+ }
21207
21271
  async handleCancel(req) {
21208
21272
  const requestedId = req.data.turn_id ?? this.getCurrentTurnId();
21209
21273
  if (requestedId === void 0) return { ok: true };
@@ -21278,38 +21342,38 @@ var TurnManager = class {
21278
21342
  agent_id: this.agentId
21279
21343
  };
21280
21344
  const soulConfig = {
21281
- beforeStep: async () => {
21345
+ beforeStep: async ({ signal }) => {
21282
21346
  await this.deps.contextState.applySteerMessages();
21283
21347
  this.deps.contextState.beforeStep?.();
21348
+ await this.fullCompactionProvider?.beforeStep(signal);
21349
+ },
21350
+ afterStep: async () => {
21351
+ await this.fullCompactionProvider?.afterStep();
21284
21352
  },
21285
21353
  beforeTurnCompletes: async () => {
21286
21354
  return { continue: await this.deps.contextState.applySteerMessages() };
21287
21355
  }
21288
21356
  };
21357
+ if (this.fullCompactionProvider) this.fullCompactionProvider.lastSoulConfig = soulConfig;
21289
21358
  const scope = this.deps.toolExecutionScopeFactory?.create({
21290
21359
  sessionId: this.sessionId,
21291
21360
  agentId: this.agentId,
21292
21361
  kind: this.agentType === "sub" ? "subagent" : this.agentType === "independent" ? "independent" : "main",
21293
21362
  subagentType: this.subagentType
21294
21363
  });
21295
- const runtimeBundle = this.runtimeSlot?.current();
21296
- const baseRuntime = runtimeBundle?.runtime ?? this.runtime;
21364
+ const baseRuntime = (this.runtimeSlot?.current())?.runtime ?? this.runtime;
21297
21365
  const runtimeForTurn = scope?.wrapRuntime(baseRuntime) ?? baseRuntime;
21366
+ const tools = this.deps.tools;
21298
21367
  if (scope !== void 0) {
21299
21368
  this.activeToolExecutionScope = scope;
21300
21369
  mergeSoulConfig(soulConfig, scope.buildSoulConfig({
21301
- tools: this.deps.tools,
21370
+ tools,
21302
21371
  turnId,
21303
21372
  permissionRules: () => mergeTurnRules(this.sessionRules, turnOverrides),
21304
21373
  permissionMode: () => this.permissionMode,
21305
21374
  approvalSource
21306
21375
  }));
21307
- } else mergeSoulConfig(soulConfig, { tools: this.deps.tools });
21308
- const compactionConfig = this.runtimeSlot !== void 0 ? runtimeBundle?.compactionConfig : this.compactionConfig;
21309
- if (compactionConfig !== void 0) mergeSoulConfig(soulConfig, {
21310
- compactionConfig,
21311
- contextWindow: compactionConfig.maxContextSize
21312
- });
21376
+ } else mergeSoulConfig(soulConfig, { tools });
21313
21377
  if (this.deps.hookEngine !== void 0) mergeSoulConfig(soulConfig, {
21314
21378
  beforeStep: async (ctx, signal) => {
21315
21379
  const result = await this.executionHooks.run({
@@ -21345,7 +21409,6 @@ var TurnManager = class {
21345
21409
  let result;
21346
21410
  let reason;
21347
21411
  try {
21348
- let compactionCount = 0;
21349
21412
  while (true) {
21350
21413
  signal.throwIfAborted();
21351
21414
  let soulResult;
@@ -21353,44 +21416,13 @@ var TurnManager = class {
21353
21416
  soulResult = await runSoulTurn(soulConfig, this.deps.contextState, runtime, this.createStepStatusSink(), signal);
21354
21417
  } catch (error) {
21355
21418
  if (error instanceof ContextOverflowError) {
21356
- compactionCount += 1;
21357
- if (compactionCount > MAX_COMPACTIONS_PER_TURN) {
21358
- this.deps.sink.emit({
21359
- type: "session.error",
21360
- error: `Compaction limit exceeded (${MAX_COMPACTIONS_PER_TURN})`,
21361
- error_type: "context_overflow"
21362
- });
21363
- result = {
21364
- stopReason: "error",
21365
- steps: 0,
21366
- usage: zeroUsage()
21367
- };
21368
- break;
21369
- }
21370
- await this.deps.compaction.executeCompaction(signal);
21419
+ await this.fullCompactionProvider?.handleOverflowError(signal);
21371
21420
  continue;
21372
21421
  }
21373
21422
  throw error;
21374
21423
  }
21375
- if (soulResult.stopReason !== "needs_compaction") {
21376
- result = soulResult;
21377
- break;
21378
- }
21379
- compactionCount += 1;
21380
- if (compactionCount > MAX_COMPACTIONS_PER_TURN) {
21381
- this.deps.sink.emit({
21382
- type: "session.error",
21383
- error: `Compaction limit exceeded (${MAX_COMPACTIONS_PER_TURN})`,
21384
- error_type: "context_overflow"
21385
- });
21386
- result = {
21387
- stopReason: "error",
21388
- steps: soulResult.steps,
21389
- usage: soulResult.usage
21390
- };
21391
- break;
21392
- }
21393
- await this.deps.compaction.executeCompaction(signal);
21424
+ result = soulResult;
21425
+ break;
21394
21426
  }
21395
21427
  reason = result !== void 0 && result.stopReason === "aborted" ? "cancelled" : "done";
21396
21428
  if (result !== void 0 && result.stopReason === "error") reason = "error";
@@ -21482,6 +21514,7 @@ var TurnManager = class {
21482
21514
  input: 0,
21483
21515
  output: 0
21484
21516
  });
21517
+ this.processWakeQueue();
21485
21518
  }
21486
21519
  }
21487
21520
  /**
@@ -21496,7 +21529,7 @@ var TurnManager = class {
21496
21529
  */
21497
21530
  getStatusSnapshot(tokenUsage) {
21498
21531
  const used = this.deps.contextState.tokenCountWithPending;
21499
- const total = (this.runtimeSlot !== void 0 ? this.runtimeSlot.current().compactionConfig : this.compactionConfig)?.maxContextSize ?? DEFAULT_CONTEXT_WINDOW;
21532
+ const total = DEFAULT_CONTEXT_WINDOW;
21500
21533
  return {
21501
21534
  context_usage: {
21502
21535
  used,
@@ -21520,8 +21553,20 @@ var TurnManager = class {
21520
21553
  //#region ../../packages/kimi-core/src/soul-plus/turn/wake-queue-scheduler.ts
21521
21554
  var WakeQueueScheduler = class {
21522
21555
  queue = [];
21556
+ onEnqueueListener;
21523
21557
  enqueue(trigger) {
21524
21558
  this.queue.push(trigger);
21559
+ if (this.onEnqueueListener !== void 0) try {
21560
+ this.onEnqueueListener();
21561
+ } catch {}
21562
+ }
21563
+ /**
21564
+ * Register a single listener fired synchronously after each `enqueue`.
21565
+ * Last writer wins — the canonical wiring is TurnManager registering
21566
+ * exactly once at construction. Pass `undefined` to clear.
21567
+ */
21568
+ setOnEnqueue(listener) {
21569
+ this.onEnqueueListener = listener;
21525
21570
  }
21526
21571
  /**
21527
21572
  * Remove and return every buffered trigger in FIFO order. Idempotent
@@ -21553,15 +21598,13 @@ var SoulPlus = class {
21553
21598
  sessionId;
21554
21599
  lifecycle;
21555
21600
  journal;
21556
- services;
21601
+ sessionMeta;
21557
21602
  components;
21558
21603
  infra;
21559
21604
  hostToolNames;
21560
21605
  planController;
21561
21606
  setPlanModeImpl;
21562
21607
  stateCache;
21563
- /** Lazily materialized config-channel handler used by wire/session control callers. */
21564
- sessionControlInstance;
21565
21608
  constructor(deps) {
21566
21609
  this.sessionId = deps.sessionId;
21567
21610
  this.stateCache = deps.stateCache;
@@ -21579,13 +21622,11 @@ var SoulPlus = class {
21579
21622
  const journalWriter = contextState.journalWriter;
21580
21623
  const runtimeSlot = deps.runtimeSlot ?? createSessionRuntimeSlot({
21581
21624
  model: contextState.model,
21582
- runtime: deps.runtime,
21583
- compactionProvider: deps.compactionProvider,
21584
- compactionConfig: deps.compactionConfig
21625
+ runtime: deps.runtime
21585
21626
  });
21586
21627
  const runtime = runtimeSlot.current().runtime;
21587
21628
  const approvalRuntime = deps.approvalRuntime;
21588
- const sessionMeta = deps.stateCache !== void 0 && deps.initialMeta !== void 0 ? new SessionMetaService({
21629
+ this.sessionMeta = deps.stateCache !== void 0 && deps.initialMeta !== void 0 ? new SessionMetaService({
21589
21630
  sessionId: deps.sessionId,
21590
21631
  eventBus,
21591
21632
  stateCache: deps.stateCache,
@@ -21594,21 +21635,10 @@ var SoulPlus = class {
21594
21635
  const planController = new PlanSessionController({
21595
21636
  sessionId: deps.sessionId,
21596
21637
  paths: deps.pathConfig,
21597
- sessionMeta
21638
+ sessionMeta: this.sessionMeta
21598
21639
  });
21599
21640
  this.planController = planController;
21600
- const compactionProvider = deps.compactionProvider;
21601
21641
  const turnManagerRef = createTurnManagerRef();
21602
- const compaction = createSoulPlusCompactionOrchestrator({
21603
- contextState,
21604
- compactionProvider,
21605
- runtimeSlot,
21606
- lifecycleStateMachine: stateMachine,
21607
- sink: eventBus,
21608
- journalWriter,
21609
- sessionId: deps.sessionId,
21610
- turnManagerRef
21611
- });
21612
21642
  const effectiveHookEngine = deps.hookEngine ?? (approvalRuntime !== void 0 ? new HookEngine({
21613
21643
  executors: /* @__PURE__ */ new Map(),
21614
21644
  sink: eventBus
@@ -21621,6 +21651,14 @@ var SoulPlus = class {
21621
21651
  planController,
21622
21652
  turnManagerRef
21623
21653
  });
21654
+ const notificationManager = new NotificationManager({
21655
+ sessionJournal,
21656
+ sessionEventBus: eventBus,
21657
+ contextState,
21658
+ onShellDeliver: deps.onShellDeliver,
21659
+ logger: deps.logger
21660
+ });
21661
+ const wakeScheduler = new WakeQueueScheduler();
21624
21662
  const { soulRegistry, hasSubagentInfra } = createSoulRegistryForSession({
21625
21663
  sessionId: deps.sessionId,
21626
21664
  sessionJournal,
@@ -21634,7 +21672,9 @@ var SoulPlus = class {
21634
21672
  workDir: deps.workDir,
21635
21673
  pathConfig: deps.pathConfig,
21636
21674
  toolExecutionScopeFactory,
21637
- producer: deps.producer
21675
+ producer: deps.producer,
21676
+ notificationManager,
21677
+ wakeScheduler
21638
21678
  });
21639
21679
  registerSubagentTool({
21640
21680
  toolRegistry,
@@ -21650,7 +21690,6 @@ var SoulPlus = class {
21650
21690
  sessionJournal
21651
21691
  });
21652
21692
  const dynamicInjectionManager = createDefaultDynamicInjectionManager();
21653
- const wakeScheduler = new WakeQueueScheduler();
21654
21693
  const turnLifecycle = new TurnLifecycleTracker();
21655
21694
  const turnManager = new TurnManager({
21656
21695
  contextState,
@@ -21661,7 +21700,6 @@ var SoulPlus = class {
21661
21700
  lifecycleStateMachine: stateMachine,
21662
21701
  soulRegistry,
21663
21702
  tools: toolRegistry,
21664
- compaction,
21665
21703
  lifecycle: turnLifecycle,
21666
21704
  wakeScheduler,
21667
21705
  dynamicInjectionManager,
@@ -21672,8 +21710,7 @@ var SoulPlus = class {
21672
21710
  toolExecutionScopeFactory,
21673
21711
  approvalRuntime,
21674
21712
  hookEngine: deps.hookEngine,
21675
- compactionConfig: deps.compactionConfig,
21676
- modelCapabilities: deps.modelCapabilities
21713
+ compactionConfig: deps.compactionConfig
21677
21714
  });
21678
21715
  turnManagerRef.bind(turnManager);
21679
21716
  const setPlanMode = createPlanModeSetter({
@@ -21692,13 +21729,6 @@ var SoulPlus = class {
21692
21729
  setPlanMode,
21693
21730
  planController
21694
21731
  });
21695
- const notificationManager = new NotificationManager({
21696
- sessionJournal,
21697
- sessionEventBus: eventBus,
21698
- contextState,
21699
- onShellDeliver: deps.onShellDeliver,
21700
- logger: deps.logger
21701
- });
21702
21732
  this.lifecycle = {
21703
21733
  stateMachine,
21704
21734
  gate
@@ -21708,11 +21738,6 @@ var SoulPlus = class {
21708
21738
  contextState,
21709
21739
  sessionJournal
21710
21740
  };
21711
- this.services = {
21712
- approvalRuntime,
21713
- compaction,
21714
- sessionMeta
21715
- };
21716
21741
  this.components = {
21717
21742
  turnManager,
21718
21743
  soulRegistry,
@@ -21835,7 +21860,7 @@ var SoulPlus = class {
21835
21860
  * create/resume paths; direct tests may omit it.
21836
21861
  */
21837
21862
  getSessionMeta() {
21838
- const svc = this.services.sessionMeta;
21863
+ const svc = this.sessionMeta;
21839
21864
  if (svc === void 0) throw new Error("SoulPlus.getSessionMeta: SessionMetaService was not wired (missing stateCache / initialMeta in SoulPlusDeps)");
21840
21865
  return svc;
21841
21866
  }
@@ -21844,7 +21869,7 @@ var SoulPlus = class {
21844
21869
  * direct tests may not include state plumbing.
21845
21870
  */
21846
21871
  tryGetSessionMeta() {
21847
- return this.services.sessionMeta;
21872
+ return this.sessionMeta;
21848
21873
  }
21849
21874
  async setPlanMode(enabled) {
21850
21875
  await this.setPlanModeImpl(enabled);
@@ -21881,36 +21906,6 @@ var SoulPlus = class {
21881
21906
  return this.components.skillManager;
21882
21907
  }
21883
21908
  /**
21884
- * Access the config-channel session control handler. The handler is
21885
- * lazy but stable for this SoulPlus instance.
21886
- */
21887
- getSessionControl() {
21888
- if (this.sessionControlInstance !== void 0) return this.sessionControlInstance;
21889
- this.sessionControlInstance = new DefaultSessionControl({
21890
- turnManager: this.components.turnManager,
21891
- contextState: this.journal.contextState,
21892
- setPlanModeOverride: (enabled) => this.setPlanMode(enabled),
21893
- setYoloOverride: async (enabled) => {
21894
- const permissionMode = enabled ? "bypassPermissions" : "default";
21895
- await this.stateCache?.update((current) => {
21896
- const now = Date.now();
21897
- return {
21898
- ...current ?? {
21899
- session_id: this.sessionId,
21900
- created_at: now,
21901
- updated_at: now
21902
- },
21903
- permission_mode: permissionMode,
21904
- updated_at: now,
21905
- last_exit_code: "dirty"
21906
- };
21907
- });
21908
- this.components.turnManager.setPermissionMode(permissionMode);
21909
- }
21910
- });
21911
- return this.sessionControlInstance;
21912
- }
21913
- /**
21914
21909
  * Apply a programmatic model change and notify observers immediately.
21915
21910
  *
21916
21911
  * state.json is the durable truth source (updated via `stateCache.update`);
@@ -21982,6 +21977,48 @@ var SoulPlus = class {
21982
21977
  }
21983
21978
  };
21984
21979
  //#endregion
21980
+ //#region ../../packages/kimi-core/src/soul-plus/session/session-control.ts
21981
+ var DefaultSessionControl = class {
21982
+ turnManager;
21983
+ contextState;
21984
+ setPlanModeOverride;
21985
+ setYoloOverride;
21986
+ constructor(deps) {
21987
+ this.turnManager = deps.turnManager;
21988
+ this.contextState = deps.contextState;
21989
+ this.setPlanModeOverride = deps.setPlanModeOverride;
21990
+ this.setYoloOverride = deps.setYoloOverride;
21991
+ }
21992
+ async compact(customInstruction) {
21993
+ return this.turnManager.triggerCompaction(customInstruction);
21994
+ }
21995
+ async clear() {
21996
+ if (!this.turnManager.tryReserveForMaintenance()) throw new Error(`Cannot clear while session is ${this.turnManager.getLifecycleState()} — cancel the current turn or wait for compaction to finish first.`);
21997
+ try {
21998
+ await this.contextState.clear();
21999
+ } finally {
22000
+ this.turnManager.releaseMaintenance();
22001
+ }
22002
+ }
22003
+ async setPlanMode(enabled) {
22004
+ if (this.setPlanModeOverride !== void 0) {
22005
+ await this.setPlanModeOverride(enabled);
22006
+ return;
22007
+ }
22008
+ this.turnManager.setPlanMode(enabled);
22009
+ }
22010
+ async setYolo(enabled) {
22011
+ if (this.setYoloOverride !== void 0) {
22012
+ await this.setYoloOverride(enabled);
22013
+ return;
22014
+ }
22015
+ const previousMode = this.turnManager.getPermissionMode();
22016
+ const newMode = enabled ? "bypassPermissions" : "default";
22017
+ if (previousMode === newMode) return;
22018
+ this.turnManager.setPermissionMode(newMode);
22019
+ }
22020
+ };
22021
+ //#endregion
21985
22022
  //#region ../../packages/kimi-core/src/soul-plus/kosong/kosong-adapter.ts
21986
22023
  /**
21987
22024
  * KosongAdapter — wraps a `ChatProvider` from `@moonshot-ai/kosong` so it
@@ -22042,15 +22079,6 @@ var KosongAdapter = class {
22042
22079
  this.maxRetries = options.maxRetries ?? 3;
22043
22080
  this.baseRetryDelayMs = options.baseRetryDelayMs ?? 1e3;
22044
22081
  }
22045
- /**
22046
- * Phase 19 Slice B — proxy the provider's declared capability matrix.
22047
- * Returns `undefined` when the provider does not expose a capability
22048
- * table (legacy providers / mock harnesses); callers treat `undefined`
22049
- * as "no constraint" and skip the gate.
22050
- */
22051
- getCapability(model) {
22052
- return this.provider.getCapability?.(model);
22053
- }
22054
22082
  async chat(params) {
22055
22083
  params.signal.throwIfAborted();
22056
22084
  try {
@@ -22184,7 +22212,7 @@ var KosongAdapter = class {
22184
22212
  }));
22185
22213
  const usage = mapUsage(result.usage);
22186
22214
  const stopReason = mapFinishReason(result.finishReason);
22187
- const response = {
22215
+ return {
22188
22216
  message: {
22189
22217
  role: "assistant",
22190
22218
  content: contentBlocks,
@@ -22196,8 +22224,6 @@ var KosongAdapter = class {
22196
22224
  actualModel: activeProvider.modelName,
22197
22225
  stopReason
22198
22226
  };
22199
- if (params.contextWindow !== void 0 && usage.input > params.contextWindow) throw new ContextOverflowError(`Implicit context overflow: input=${String(usage.input)} exceeds contextWindow=${String(params.contextWindow)}`, usage);
22200
- return response;
22201
22227
  }
22202
22228
  };
22203
22229
  const PTL_MESSAGE_PATTERNS = [
@@ -22324,88 +22350,6 @@ function createRuntime(deps) {
22324
22350
  return { kosong: deps.kosong };
22325
22351
  }
22326
22352
  //#endregion
22327
- //#region ../../packages/kimi-core/src/soul-plus/compaction/compaction-provider.ts
22328
- /**
22329
- * KosongCompactionProvider — real CompactionProvider that uses kosong's
22330
- * `generate()` to produce a conversation summary (Slice 3.3 / M05).
22331
- *
22332
- * Replaces `createStubCompactionProvider()` from Slice 3 for production
22333
- * use. The stub remains available for tests that do not need real LLM
22334
- * interaction.
22335
- *
22336
- * The implementation mirrors Python's `SimpleCompaction.compact()`:
22337
- * 1. Format the input messages into a single compaction prompt
22338
- * 2. Call the LLM (via kosong generate) with a system prompt
22339
- * 3. Extract the summary text from the response
22340
- * 4. Return a `SummaryMessage` with the summary and metadata
22341
- */
22342
- const COMPACTION_SYSTEM_PROMPT = "You are a helpful assistant that compacts conversation context. Summarize the conversation concisely while preserving all important details, decisions, code snippets, file paths, and action items.";
22343
- /**
22344
- * Build the compaction prompt from conversation messages.
22345
- *
22346
- * Mirrors Python's `SimpleCompaction.prepare()` serialization:
22347
- * - Each message becomes a block: `## Message N\nRole: X\nContent:\n<text>`
22348
- * - Only TextPart is retained; think / image / audio / video are dropped.
22349
- * - Tool results are folded into the assistant message that issued the
22350
- * call, so the summary sees both the invocation and its result.
22351
- * - Message blocks are concatenated without separators, matching Python's
22352
- * `compact_message.content.extend()` behaviour.
22353
- * - The instruction suffix is joined with a single `\n` to match Python's
22354
- * `prompt_text = "\n" + prompts.COMPACT`.
22355
- */
22356
- function buildCompactionPrompt(messages, userInstructions) {
22357
- const toolResultsById = /* @__PURE__ */ new Map();
22358
- for (const msg of messages) if (msg.role === "tool" && msg.toolCallId) {
22359
- const text = msg.content.filter((p) => p.type === "text").map((p) => p.text).join("");
22360
- toolResultsById.set(msg.toolCallId, text);
22361
- }
22362
- const parts = [];
22363
- let seq = 1;
22364
- for (const msg of messages) {
22365
- if (msg.role === "tool") continue;
22366
- const body = msg.content.filter((p) => p.type === "text").map((p) => p.text).join("");
22367
- let block = `## Message ${seq}\nRole: ${msg.role}\nContent:\n${body}`;
22368
- if (msg.role === "assistant" && msg.toolCalls && msg.toolCalls.length > 0) for (const tc of msg.toolCalls) {
22369
- let argsDisplay = tc.function.arguments ?? "";
22370
- try {
22371
- const parsed = JSON.parse(argsDisplay);
22372
- argsDisplay = JSON.stringify(parsed);
22373
- } catch {}
22374
- const result = toolResultsById.get(tc.id);
22375
- const resultStr = result !== void 0 ? ` ->\n${result}` : "";
22376
- block += `\n[Tool: ${tc.function.name}(${argsDisplay})]${resultStr}`;
22377
- }
22378
- parts.push((block.endsWith("\n") ? block : block + "\n") + "\n");
22379
- seq++;
22380
- }
22381
- let prompt = parts.join("") + "\nSummarize the above conversation. Preserve:\n- Key decisions and their reasoning\n- Important code snippets, file paths, and technical details\n- Action items and their status\n- Any constraints or requirements mentioned\n\nBe concise but thorough. Do not lose critical context.";
22382
- if (userInstructions !== void 0 && userInstructions.length > 0) prompt += "\n\n**User's Custom Compaction Instruction:**\nThe user has specifically requested the following focus during compaction. You MUST prioritize this instruction above the default compression priorities:\n" + userInstructions;
22383
- return prompt;
22384
- }
22385
- var KosongCompactionProvider = class {
22386
- provider;
22387
- constructor(provider) {
22388
- this.provider = provider;
22389
- }
22390
- async run(messages, signal, options) {
22391
- const compactionMessage = {
22392
- role: "user",
22393
- content: [{
22394
- type: "text",
22395
- text: buildCompactionPrompt(messages, options?.userInstructions)
22396
- }],
22397
- toolCalls: []
22398
- };
22399
- return {
22400
- content: (await generate$1(this.provider, COMPACTION_SYSTEM_PROMPT, [], [compactionMessage], void 0, { signal })).message.content.filter((p) => p.type === "text").map((p) => p.text).join(""),
22401
- original_turn_count: messages.length
22402
- };
22403
- }
22404
- };
22405
- function createKosongCompactionProvider(provider) {
22406
- return new KosongCompactionProvider(provider);
22407
- }
22408
- //#endregion
22409
22353
  //#region ../../packages/kimi-core/src/soul-plus/approval/wired-approval-runtime.ts
22410
22354
  /**
22411
22355
  * WiredApprovalRuntime — the production `ApprovalRuntime` (v2 §9-G).
@@ -22912,6 +22856,16 @@ z.discriminatedUnion("kind", [
22912
22856
  detail: z.unknown().optional()
22913
22857
  })
22914
22858
  ]);
22859
+ //#endregion
22860
+ //#region ../../packages/kimi-core/src/tools/builtin/cognition/think.ts
22861
+ /**
22862
+ * ThinkTool — no-op reasoning tool (Slice 3.5).
22863
+ *
22864
+ * Ports Python `kimi_cli/tools/think/__init__.py`. Gives the LLM a
22865
+ * structured place to record intermediate reasoning steps — acts as an
22866
+ * extended-thinking workaround for models that lack native thinking
22867
+ * tokens. The tool produces no side effects and always returns success.
22868
+ */
22915
22869
  const ThinkInputSchema = z.object({ thought: z.string().describe("Your thought process.") });
22916
22870
  const DESCRIPTION$3 = "Use this tool to think about something. It will not produce any output or take any action. Use it when you need to reason through a problem step by step before deciding what to do next.";
22917
22871
  var ThinkTool = class {
@@ -23560,9 +23514,16 @@ function infoToPersisted(info) {
23560
23514
  stop_reason: info.stopReason
23561
23515
  };
23562
23516
  }
23517
+ //#endregion
23518
+ //#region ../../packages/kimi-core/src/tools/background/task-list.ts
23519
+ /**
23520
+ * TaskListTool — list background tasks (Slice 3.5).
23521
+ *
23522
+ * Ports Python `kimi_cli/tools/background/__init__.py:TaskList`.
23523
+ */
23563
23524
  const TaskListInputSchema = z.object({
23564
23525
  active_only: z.boolean().optional().default(true).describe("Whether to list only non-terminal background tasks."),
23565
- limit: z.number().int().min(1).max(100).optional().default(20).describe("Maximum number of tasks to return.")
23526
+ limit: z.number().int().min(1).max(100).default(20).describe("Maximum number of tasks to return.").optional()
23566
23527
  });
23567
23528
  function formatTaskList(tasks) {
23568
23529
  if (tasks.length === 0) return "No background tasks found.";
@@ -23585,10 +23546,17 @@ var TaskListTool = class {
23585
23546
  return "Listing background tasks";
23586
23547
  }
23587
23548
  };
23549
+ //#endregion
23550
+ //#region ../../packages/kimi-core/src/tools/background/task-output.ts
23551
+ /**
23552
+ * TaskOutputTool — read output from a background task (Slice 3.5).
23553
+ *
23554
+ * Ports Python `kimi_cli/tools/background/__init__.py:TaskOutput`.
23555
+ */
23588
23556
  const TaskOutputInputSchema = z.object({
23589
23557
  task_id: z.string().describe("The background task ID to inspect."),
23590
- block: z.boolean().optional().default(false).describe("Whether to wait for the task to finish before returning."),
23591
- timeout: z.number().int().min(0).max(3600).optional().default(30).describe("Maximum number of seconds to wait when block=true.")
23558
+ block: z.boolean().default(false).describe("Whether to wait for the task to finish before returning.").optional(),
23559
+ timeout: z.number().int().min(0).max(3600).default(30).describe("Maximum number of seconds to wait when block=true.").optional()
23592
23560
  });
23593
23561
  function retrievalStatus(status, block) {
23594
23562
  if (isBackgroundTaskTerminal(status)) return "success";
@@ -23634,9 +23602,16 @@ var TaskOutputTool = class {
23634
23602
  return `Reading output of task ${args.task_id}`;
23635
23603
  }
23636
23604
  };
23605
+ //#endregion
23606
+ //#region ../../packages/kimi-core/src/tools/background/task-stop.ts
23607
+ /**
23608
+ * TaskStopTool — stop a running background task (Slice 3.5).
23609
+ *
23610
+ * Ports Python `kimi_cli/tools/background/__init__.py:TaskStop`.
23611
+ */
23637
23612
  const TaskStopInputSchema = z.object({
23638
23613
  task_id: z.string().describe("The background task ID to stop."),
23639
- reason: z.string().optional().default("Stopped by TaskStop").describe("Short reason recorded when the task is stopped.")
23614
+ reason: z.string().default("Stopped by TaskStop").describe("Short reason recorded when the task is stopped.").optional()
23640
23615
  });
23641
23616
  var TaskStopTool = class {
23642
23617
  name = "TaskStop";
@@ -23670,10 +23645,20 @@ var TaskStopTool = class {
23670
23645
  return `Stopping task ${args.task_id}`;
23671
23646
  }
23672
23647
  };
23648
+ //#endregion
23649
+ //#region ../../packages/kimi-core/src/tools/builtin/web/web-search.ts
23650
+ /**
23651
+ * WebSearchTool — host-injected web search (Slice 3.5).
23652
+ *
23653
+ * Ports Python `kimi_cli/tools/web/search.py`. kimi-core defines the
23654
+ * interface; the host provides the real search implementation via
23655
+ * `WebSearchProvider`. If no provider is supplied, the tool should not
23656
+ * be registered (not exposed to the LLM).
23657
+ */
23673
23658
  const WebSearchInputSchema = z.object({
23674
23659
  query: z.string().describe("The query text to search for."),
23675
- limit: z.number().int().min(1).max(20).optional().default(5).describe("The number of results to return."),
23676
- include_content: z.boolean().optional().default(false).describe("Whether to include the content of the web pages in the results.")
23660
+ limit: z.number().int().min(1).max(20).default(5).describe("The number of results to return.").optional(),
23661
+ include_content: z.boolean().default(false).describe("Whether to include the content of the web pages in the results.").optional()
23677
23662
  });
23678
23663
  const DESCRIPTION$2 = "Search the web for information. Returns a list of search results with title, URL, and snippet. Use this when you need up-to-date information from the internet.";
23679
23664
  var WebSearchTool = class {
@@ -23719,9 +23704,19 @@ var WebSearchTool = class {
23719
23704
  return `Searching: ${args.query.length > 40 ? `${args.query.slice(0, 40)}…` : args.query}`;
23720
23705
  }
23721
23706
  };
23707
+ //#endregion
23708
+ //#region ../../packages/kimi-core/src/tools/builtin/web/fetch-url.ts
23709
+ /**
23710
+ * FetchURLTool — host-injected URL fetcher (Slice 3.5).
23711
+ *
23712
+ * Ports Python `kimi_cli/tools/web/fetch.py`. kimi-core defines the
23713
+ * interface; the host provides the real fetch implementation via
23714
+ * `UrlFetcher`. If no fetcher is supplied, the tool should not be
23715
+ * registered (not exposed to the LLM).
23716
+ */
23722
23717
  const FetchURLInputSchema = z.object({
23723
23718
  url: z.string().describe("The URL to fetch content from."),
23724
- format: z.enum(["text", "markdown"]).optional().default("text").describe("The format of the returned content.")
23719
+ format: z.enum(["text", "markdown"]).default("text").describe("The format of the returned content.").optional()
23725
23720
  });
23726
23721
  const DESCRIPTION$1 = "Fetch content from a URL. Returns the main text content extracted from the page. Use this when you need to read a specific web page.";
23727
23722
  var FetchURLTool = class {
@@ -24254,6 +24249,7 @@ var ReadMediaFileTool = class {
24254
24249
  metadata = { source: "builtin" };
24255
24250
  description;
24256
24251
  inputSchema = ReadMediaFileInputSchema;
24252
+ isConcurrencySafe = (_input) => true;
24257
24253
  capabilities;
24258
24254
  constructor(kaos, workspace, capabilities, imageSizeExtractor, videoUploader) {
24259
24255
  this.kaos = kaos;
@@ -24468,7 +24464,7 @@ async function detectEnvironmentFromNode() {
24468
24464
  * 2. Public `*Schema: z.ZodType<T>` — explicit interface as type param
24469
24465
  * 3. `AssertEqual` drift guard — compile-time proof that (1) and (2) agree
24470
24466
  */
24471
- const _rawReadInputSchema = z.object({
24467
+ const ReadInputSchema = z.object({
24472
24468
  path: z.string(),
24473
24469
  offset: z.number().int().nonnegative().optional(),
24474
24470
  limit: z.number().int().positive().optional()
@@ -24477,24 +24473,21 @@ z.object({
24477
24473
  content: z.string(),
24478
24474
  lineCount: z.number().int().nonnegative()
24479
24475
  });
24480
- const ReadInputSchema = _rawReadInputSchema;
24481
- const _rawWriteInputSchema = z.object({
24476
+ const WriteInputSchema = z.object({
24482
24477
  path: z.string(),
24483
24478
  content: z.string()
24484
24479
  });
24485
24480
  z.object({ bytesWritten: z.number().int().nonnegative() });
24486
- const WriteInputSchema = _rawWriteInputSchema;
24487
- const _rawEditInputSchema = z.object({
24481
+ const EditInputSchema = z.object({
24488
24482
  path: z.string(),
24489
24483
  old_string: z.string().min(1),
24490
24484
  new_string: z.string(),
24491
24485
  replace_all: z.boolean().optional()
24492
24486
  });
24493
24487
  z.object({ replacementCount: z.number().int().nonnegative() });
24494
- const EditInputSchema = _rawEditInputSchema;
24495
24488
  const MAX_FG_TIMEOUT_MS = 300 * 1e3;
24496
24489
  const MAX_BG_TIMEOUT_MS = 1440 * 60 * 1e3;
24497
- const _rawBashInputSchema = z.object({
24490
+ const BashInputSchema = z.object({
24498
24491
  command: z.string().min(1, "Command cannot be empty."),
24499
24492
  cwd: z.string().optional(),
24500
24493
  timeout: z.number().int().positive().optional().describe(`Optional timeout in milliseconds for the command to execute. Foreground max ${String(MAX_FG_TIMEOUT_MS)}ms; background max ${String(MAX_BG_TIMEOUT_MS)}ms.`),
@@ -24514,8 +24507,7 @@ z.object({
24514
24507
  stdout: z.string(),
24515
24508
  stderr: z.string()
24516
24509
  });
24517
- const BashInputSchema = _rawBashInputSchema;
24518
- const _rawGrepInputSchema = z.object({
24510
+ const GrepInputSchema = z.object({
24519
24511
  pattern: z.string(),
24520
24512
  path: z.string().optional(),
24521
24513
  glob: z.string().optional(),
@@ -24548,14 +24540,12 @@ z.object({
24548
24540
  numMatches: z.number().int().nonnegative().optional(),
24549
24541
  appliedLimit: z.number().int().nonnegative().optional()
24550
24542
  });
24551
- const GrepInputSchema = _rawGrepInputSchema;
24552
- const _rawGlobInputSchema = z.object({
24543
+ const GlobInputSchema = z.object({
24553
24544
  pattern: z.string().describe("Glob pattern to match files/directories."),
24554
24545
  path: z.string().optional().describe("Absolute path to the directory to search in. Defaults to every allowed workspace root (primary + additional dirs)."),
24555
24546
  include_dirs: z.boolean().optional().describe("Whether to include directories in results. Defaults to true. Set false to return only files.")
24556
24547
  });
24557
24548
  z.object({ paths: z.array(z.string()) });
24558
- const GlobInputSchema = _rawGlobInputSchema;
24559
24549
  //#endregion
24560
24550
  //#region ../../packages/kimi-core/src/tools/builtin/file/read.ts
24561
24551
  const MAX_LINES = 1e3;
@@ -24567,10 +24557,21 @@ function truncateLine(line, maxLength) {
24567
24557
  const target = Math.max(maxLength, 3);
24568
24558
  return line.slice(0, target - 3) + marker;
24569
24559
  }
24560
+ const READ_DESCRIPTION = [
24561
+ "Read a text file from the local filesystem.",
24562
+ "",
24563
+ "Trust the path the caller gives you — do not `Glob`/`ls` first to confirm it exists. A missing file returns an error you can react to; pre-checking just adds round-trips.",
24564
+ "",
24565
+ "- `path` must be absolute.",
24566
+ `- Returns up to ${String(MAX_LINES)} lines or ${String(MAX_BYTES / 1024)} KB per call, whichever comes first; lines longer than ${String(MAX_LINE_LENGTH)} chars are truncated mid-line.`,
24567
+ "- Page larger files with `offset` (0-based start line) and `limit`.",
24568
+ "- Output format: `<line-number>\\t<content>` per line.",
24569
+ "- After a successful `Edit`/`Write`, skip the verification re-read — those tools error on failure, so a clean return means the change landed."
24570
+ ].join("\n");
24570
24571
  var ReadTool = class {
24571
24572
  name = "Read";
24572
24573
  metadata = { source: "builtin" };
24573
- description = "Read the contents of a file from the local filesystem.";
24574
+ description = READ_DESCRIPTION;
24574
24575
  inputSchema = ReadInputSchema;
24575
24576
  maxResultSizeChars = Number.POSITIVE_INFINITY;
24576
24577
  isConcurrencySafe = (_input) => true;
@@ -24831,7 +24832,17 @@ const MAX_TIMEOUT_MS = 300 * 1e3;
24831
24832
  const MAX_BACKGROUND_TIMEOUT_MS = 1440 * 60 * 1e3;
24832
24833
  const SIGTERM_GRACE_MS$1 = 5e3;
24833
24834
  const MAX_OUTPUT_BYTES$1 = 10 * 1024 * 1024;
24834
- const BASH_DESCRIPTION = `Execute a bash command. Use this tool to explore the filesystem, edit files, run scripts, get system information, etc.
24835
+ const BASH_DESCRIPTION = `Execute a bash command. Use this for shell semantics pipes, env, processes, git, package managers, build/test runners, anything genuinely interactive or multi-step.
24836
+
24837
+ **Translate these to a dedicated tool instead:**
24838
+ - \`cat\` / \`head\` / \`tail\` (known path) → \`Read\`
24839
+ - \`sed\` / \`awk\` (in-place edit) → \`Edit\`
24840
+ - \`echo > file\` / \`cat <<EOF\` → \`Write\`
24841
+ - \`find\` / \`ls\` (locate by pattern) → \`Glob\`
24842
+ - \`grep\` / \`rg\` (search file contents) → \`Grep\`
24843
+ - \`echo\` / \`printf\` (talk to the user) → just output text directly
24844
+
24845
+ The dedicated tools render in the per-tool permission UI and keep raw stdout out of the conversation; that is why they are worth reaching for whenever one fits.
24835
24846
 
24836
24847
  **Output:**
24837
24848
  The stdout and stderr will be combined and returned as a string. The output may be truncated if it is too long. If the command failed, the exit code will be provided in a system tag.
@@ -24852,9 +24863,17 @@ If \`run_in_background=true\`, the command will be started as a background task
24852
24863
  - Use pipe operations (\`|\`) and redirections (\`>\`, \`>>\`) to chain input and output between commands
24853
24864
  - Always quote file paths containing spaces with double quotes (e.g., cd "/path with spaces/")
24854
24865
  - Prefer \`run_in_background=true\` for long-running builds, tests, watchers, or servers when you need the conversation to continue before the command finishes.`;
24855
- const POWERSHELL_DESCRIPTION = `Execute a Windows PowerShell command. Use this tool to explore the filesystem, inspect or edit files, run Windows scripts, collect system information, etc., whenever the agent is running on Windows.
24866
+ const POWERSHELL_DESCRIPTION = `Execute a Windows PowerShell command. Use this for shell semantics pipes, env, processes, git, package managers, build/test runners, anything genuinely interactive or multi-step. You are on Windows, so use Windows commands, paths, and conventions.
24856
24867
 
24857
- Note that you are running on Windows, so make sure to use Windows commands, paths, and conventions.
24868
+ **Translate these to a dedicated tool instead:**
24869
+ - \`Get-Content\` / \`type\` (known path) → \`Read\`
24870
+ - \`Set-Content\` / \`Add-Content\` (in-place edit) → \`Edit\`
24871
+ - \`Out-File\` / \`> file\` → \`Write\`
24872
+ - \`Get-ChildItem -Recurse\` (locate by pattern) → \`Glob\`
24873
+ - \`Select-String\` (search file contents) → \`Grep\`
24874
+ - \`Write-Host\` / \`echo\` (talk to the user) → just output text directly
24875
+
24876
+ The dedicated tools render in the per-tool permission UI and keep raw stdout out of the conversation; that is why they are worth reaching for whenever one fits.
24858
24877
 
24859
24878
  **Output:**
24860
24879
  The stdout and stderr streams are combined and returned as a single string. Extremely long output may be truncated. When a command fails, the exit code is provided in a system tag.
@@ -30241,7 +30260,7 @@ async function runHookWithTimeout(options) {
30241
30260
  } catch {}
30242
30261
  return FAIL_OPEN_HOOK_RESULT;
30243
30262
  }
30244
- const _rawWireErrorSchema = z.object({
30263
+ const WireErrorSchema = z.object({
30245
30264
  code: z.number(),
30246
30265
  message: z.string(),
30247
30266
  details: z.unknown().optional()
@@ -30260,7 +30279,7 @@ const WireMessageSchema = z.object({
30260
30279
  method: z.string().min(1).optional(),
30261
30280
  request_id: z.string().min(1).optional(),
30262
30281
  data: z.unknown().optional(),
30263
- error: _rawWireErrorSchema.optional(),
30282
+ error: WireErrorSchema.optional(),
30264
30283
  turn_id: z.string().optional(),
30265
30284
  agent_type: z.enum([
30266
30285
  "main",
@@ -30606,6 +30625,12 @@ const BUS_EVENT_TRANSLATORS = {
30606
30625
  "compaction.begin": (event, ctx) => {
30607
30626
  return draft(event, ctx, "compaction.begin", {});
30608
30627
  },
30628
+ "compaction.blocked": (event, ctx) => {
30629
+ return draft(event, ctx, "compaction.blocked", {});
30630
+ },
30631
+ "compaction.canceled": (event, ctx) => {
30632
+ return draft(event, ctx, "compaction.canceled", {});
30633
+ },
30609
30634
  "compaction.end": (event, ctx) => {
30610
30635
  return draft(event, ctx, "compaction.end", {
30611
30636
  tokens_before: event.tokensBefore,
@@ -31232,13 +31257,12 @@ var DefaultSessionApplicationService = class {
31232
31257
  sessionId,
31233
31258
  model
31234
31259
  });
31235
- const compactionConfig = runtimeBundle?.compactionConfig ?? this.deps.compactionConfigProvider?.({
31260
+ const compactionConfig = this.deps.compactionConfigProvider?.({
31236
31261
  sessionId,
31237
31262
  model
31238
31263
  });
31239
- const runtimeSlotBundle = runtimeBundle !== void 0 ? withCompactionConfigFallback(runtimeBundle, compactionConfig) : void 0;
31264
+ const runtimeSlotBundle = runtimeBundle;
31240
31265
  const runtime = runtimeSlotBundle?.runtime ?? this.deps.runtimeProvider();
31241
- const compactionProvider = runtimeSlotBundle?.compactionProvider ?? this.deps.compactionProviderProvider();
31242
31266
  const runtimeSlot = runtimeSlotBundle !== void 0 ? createSessionRuntimeSlot(runtimeSlotBundle) : void 0;
31243
31267
  const tools = await this.deps.toolsProvider({
31244
31268
  sessionId,
@@ -31256,7 +31280,6 @@ var DefaultSessionApplicationService = class {
31256
31280
  systemPrompt,
31257
31281
  eventBus,
31258
31282
  workspaceDir: this.deps.workspaceDir,
31259
- compactionProvider,
31260
31283
  compactionConfig,
31261
31284
  approvalRuntime: this.deps.approvalRuntime,
31262
31285
  approvalRuntimeFactory: this.deps.approvalRuntimeFactory,
@@ -31291,17 +31314,11 @@ var DefaultSessionApplicationService = class {
31291
31314
  hookEngine
31292
31315
  });
31293
31316
  const initialModel = (await new StateCache(this.deps.pathConfig.statePath(sessionId)).read())?.model ?? this.deps.defaultModelProvider();
31294
- const runtimeBundle = await this.deps.runtimeBundleProvider?.({
31295
- sessionId,
31296
- model: initialModel
31297
- });
31298
- const compactionConfig = runtimeBundle?.compactionConfig ?? this.deps.compactionConfigProvider?.({
31317
+ const runtimeSlotBundle = await this.deps.runtimeBundleProvider?.({
31299
31318
  sessionId,
31300
31319
  model: initialModel
31301
31320
  });
31302
- const runtimeSlotBundle = runtimeBundle !== void 0 ? withCompactionConfigFallback(runtimeBundle, compactionConfig) : void 0;
31303
31321
  const runtime = runtimeSlotBundle?.runtime ?? this.deps.runtimeProvider();
31304
- const compactionProvider = runtimeSlotBundle?.compactionProvider ?? this.deps.compactionProviderProvider();
31305
31322
  const runtimeSlot = runtimeSlotBundle !== void 0 ? createSessionRuntimeSlot(runtimeSlotBundle) : void 0;
31306
31323
  const tools = await this.deps.toolsProvider({
31307
31324
  sessionId,
@@ -31314,8 +31331,6 @@ var DefaultSessionApplicationService = class {
31314
31331
  runtimeSlot,
31315
31332
  tools,
31316
31333
  eventBus,
31317
- compactionProvider,
31318
- compactionConfig,
31319
31334
  approvalRuntime: this.deps.approvalRuntime,
31320
31335
  approvalRuntimeFactory: this.deps.approvalRuntimeFactory,
31321
31336
  hookEngine,
@@ -31330,15 +31345,8 @@ var DefaultSessionApplicationService = class {
31330
31345
  sessionId,
31331
31346
  model: managed.contextState.model
31332
31347
  });
31333
- const nextConfig = next.compactionConfig ?? this.deps.compactionConfigProvider?.({
31334
- sessionId,
31335
- model: managed.contextState.model
31336
- });
31337
- runtimeSlot.replace(withCompactionConfigFallback(next, nextConfig));
31338
- } else if (runtimeSlot === void 0) managed.soulPlus.getTurnManager().setCompactionConfig(this.deps.compactionConfigProvider?.({
31339
- sessionId,
31340
- model: managed.contextState.model
31341
- }));
31348
+ runtimeSlot.replace(next);
31349
+ }
31342
31350
  this.deps.sessionLifecycle?.onSessionCreated?.(managed);
31343
31351
  return {
31344
31352
  sessionId: managed.sessionId,
@@ -31408,7 +31416,7 @@ var DefaultSessionApplicationService = class {
31408
31416
  return this.deps.sessionManager.setSessionTags(sessionId, tags);
31409
31417
  }
31410
31418
  async compact(sessionId, customInstruction) {
31411
- await this.getManaged(sessionId).sessionControl.compact(customInstruction);
31419
+ return this.getManaged(sessionId).sessionControl.compact(customInstruction);
31412
31420
  }
31413
31421
  async clear(sessionId) {
31414
31422
  await this.getManaged(sessionId).sessionControl.clear();
@@ -31455,30 +31463,23 @@ var DefaultSessionApplicationService = class {
31455
31463
  }
31456
31464
  async setModel(sessionId, model) {
31457
31465
  const managed = this.getManaged(sessionId);
31458
- const compactionConfig = this.deps.compactionConfigProvider?.({
31459
- sessionId,
31460
- model
31461
- });
31462
31466
  if (this.deps.runtimeBundleProvider !== void 0) {
31463
31467
  const next = await this.deps.runtimeBundleProvider({
31464
31468
  sessionId,
31465
31469
  model
31466
31470
  });
31467
- managed.runtimeSlot.replace(withCompactionConfigFallback(next, compactionConfig));
31471
+ managed.runtimeSlot.replace(next);
31468
31472
  } else if (this.deps.rebuildRuntimeForModel !== void 0) {
31469
31473
  await this.deps.rebuildRuntimeForModel(sessionId, model);
31470
31474
  managed.runtimeSlot.replace({
31471
31475
  model,
31472
- runtime: this.deps.runtimeProvider(),
31473
- compactionProvider: this.deps.compactionProviderProvider(),
31474
- compactionConfig
31476
+ runtime: this.deps.runtimeProvider()
31475
31477
  });
31476
31478
  } else {
31477
31479
  const current = managed.runtimeSlot.current();
31478
31480
  managed.runtimeSlot.replace({
31479
31481
  ...current,
31480
- model,
31481
- compactionConfig
31482
+ model
31482
31483
  });
31483
31484
  }
31484
31485
  await managed.stateCache.update(patchDirtySessionState(sessionId, () => ({ model })));
@@ -32759,7 +32760,7 @@ function createWireApprovalRuntimeFactory(reverse, runtimesBySession) {
32759
32760
  };
32760
32761
  }
32761
32762
  function registerDefaultWireHandlers(deps) {
32762
- const { router, sessionManager, runtime, tools, eventBus, workspaceDir, defaultModel, approval, pathConfig, backgroundProcessManager, server, hookEngine, approvalStateStore, rebuildRuntimeForModel, hookTimeoutMs, mcpRegistry, authService, compactionProvider, modelsProvider, configProvider, eventBusProvider, hookEngineProvider } = deps;
32763
+ const { router, sessionManager, runtime, tools, eventBus, workspaceDir, defaultModel, approval, pathConfig, backgroundProcessManager, server, hookEngine, approvalStateStore, rebuildRuntimeForModel, hookTimeoutMs, mcpRegistry, authService, modelsProvider, configProvider, eventBusProvider, hookEngineProvider } = deps;
32763
32764
  const sessionState = new MapWireSessionStateStore();
32764
32765
  const reverse = server !== void 0 ? createReverseRpcClient({
32765
32766
  server,
@@ -32776,7 +32777,6 @@ function registerDefaultWireHandlers(deps) {
32776
32777
  ...deps.enabledToolNames !== void 0 || deps.enabledToolNamesProvider !== void 0 ? { enabledToolNamesProvider: async () => deps.enabledToolNamesProvider !== void 0 ? deps.enabledToolNamesProvider() : deps.enabledToolNames } : {},
32777
32778
  defaultModelProvider: () => deps.defaultModelProvider?.() ?? defaultModel,
32778
32779
  ...deps.defaultSystemPromptProvider !== void 0 ? { defaultSystemPromptProvider: deps.defaultSystemPromptProvider } : {},
32779
- compactionProviderProvider: () => deps.compactionProviderProvider?.() ?? compactionProvider,
32780
32780
  ...deps.compactionConfigProvider !== void 0 ? { compactionConfigProvider: deps.compactionConfigProvider } : {},
32781
32781
  ...deps.runtimeBundleProvider !== void 0 ? { runtimeBundleProvider: deps.runtimeBundleProvider } : {},
32782
32782
  eventBusProvider: () => eventBusProvider?.() ?? eventBus,
@@ -33876,10 +33876,7 @@ var SessionManager = class {
33876
33876
  const sessionTools = bindSessionTodoStore(options.tools, todoStore);
33877
33877
  const runtimeSlot = normalizeSessionRuntimeSlot(options.runtimeSlot, {
33878
33878
  model: options.model,
33879
- runtime: options.runtime,
33880
- compactionProvider: options.compactionProvider,
33881
- ...options.compactionConfig !== void 0 ? { compactionConfig: options.compactionConfig } : {},
33882
- ...options.modelCapabilities !== void 0 ? { modelCapabilities: options.modelCapabilities } : {}
33879
+ runtime: options.runtime
33883
33880
  });
33884
33881
  const runtimeBundle = runtimeSlot.current();
33885
33882
  const mainInitInput = {
@@ -33893,6 +33890,7 @@ var SessionManager = class {
33893
33890
  const sessionJournal = new WiredSessionJournalImpl(journalWriter);
33894
33891
  let turnManagerRef;
33895
33892
  const contextState = new WiredContextState({
33893
+ initializeRecord: mainInitInput,
33896
33894
  journalWriter,
33897
33895
  initialModel: options.model,
33898
33896
  initialSystemPrompt: options.systemPrompt,
@@ -33922,10 +33920,7 @@ var SessionManager = class {
33922
33920
  last_exit_code: "dirty",
33923
33921
  producer: this.producer
33924
33922
  };
33925
- const approvalRuntime = this.buildApprovalRuntime(sessionId, sessionJournal, stateCache, {
33926
- ...options.approvalRuntime !== void 0 ? { approvalRuntime: options.approvalRuntime } : {},
33927
- ...options.approvalRuntimeFactory !== void 0 ? { approvalRuntimeFactory: options.approvalRuntimeFactory } : {}
33928
- });
33923
+ const approvalRuntime = this.buildApprovalRuntime(sessionId, sessionJournal, stateCache, options);
33929
33924
  const soulPlus = new SoulPlus({
33930
33925
  sessionId,
33931
33926
  contextState,
@@ -33940,9 +33935,7 @@ var SessionManager = class {
33940
33935
  onShellDeliver: options.onShellDeliver,
33941
33936
  skillManager: options.skillManager,
33942
33937
  questionRuntime: options.questionRuntime,
33943
- compactionConfig: runtimeBundle.compactionConfig,
33944
- modelCapabilities: runtimeBundle.modelCapabilities,
33945
- compactionProvider: runtimeBundle.compactionProvider,
33938
+ compactionConfig: options.compactionConfig,
33946
33939
  approvalRuntime,
33947
33940
  hookEngine: options.hookEngine,
33948
33941
  toolExecutionScopeFactory: options.toolExecutionScopeFactory,
@@ -34045,14 +34038,15 @@ var SessionManager = class {
34045
34038
  const eventBus = options.eventBus ?? new EventBusCtor();
34046
34039
  let turnManagerRef;
34047
34040
  const contextState = new WiredContextState({
34041
+ initializeRecord: replayResult.sessionInitialized,
34048
34042
  journalWriter,
34049
34043
  initialModel: projected.model,
34050
34044
  initialSystemPrompt: projected.systemPrompt,
34051
- ...projected.activeTools.size > 0 ? { initialActiveTools: projected.activeTools } : {},
34045
+ initialActiveTools: projected.activeTools,
34052
34046
  currentTurnId: () => turnManagerRef?.getCurrentTurnId() ?? "no_turn",
34053
34047
  initialHistory: projected.messages,
34054
34048
  initialTokenCount: projected.tokenCount,
34055
- ...projected.thinkingLevel !== void 0 ? { initialThinkingLevel: projected.thinkingLevel } : {}
34049
+ initialThinkingLevel: projected.thinkingLevel
34056
34050
  });
34057
34051
  contextStateRef = contextState;
34058
34052
  await repairJournal({
@@ -34066,10 +34060,7 @@ var SessionManager = class {
34066
34060
  const sessionTools = bindSessionTodoStore(options.tools, todoStore);
34067
34061
  const runtimeSlot = normalizeSessionRuntimeSlot(options.runtimeSlot, {
34068
34062
  model: projected.model,
34069
- runtime: options.runtime,
34070
- compactionProvider: options.compactionProvider,
34071
- compactionConfig: options.compactionConfig,
34072
- ...options.modelCapabilities !== void 0 ? { modelCapabilities: options.modelCapabilities } : {}
34063
+ runtime: options.runtime
34073
34064
  });
34074
34065
  const runtimeBundle = runtimeSlot.current();
34075
34066
  await stateCache.write({
@@ -34110,24 +34101,21 @@ var SessionManager = class {
34110
34101
  runtime: runtimeBundle.runtime,
34111
34102
  eventBus,
34112
34103
  tools: sessionTools,
34113
- ...options.enabledToolNames !== void 0 ? { enabledToolNames: options.enabledToolNames } : {},
34104
+ enabledToolNames: options.enabledToolNames,
34114
34105
  lifecycleStateMachine,
34115
34106
  producer: this.producer,
34116
34107
  onShellDeliver: options.onShellDeliver,
34117
34108
  skillManager: options.skillManager,
34118
- ...options.questionRuntime !== void 0 ? { questionRuntime: options.questionRuntime } : {},
34119
- ...runtimeBundle.compactionConfig !== void 0 ? { compactionConfig: runtimeBundle.compactionConfig } : {},
34120
- ...runtimeBundle.modelCapabilities !== void 0 ? { modelCapabilities: runtimeBundle.modelCapabilities } : {},
34121
- compactionProvider: runtimeBundle.compactionProvider,
34109
+ questionRuntime: options.questionRuntime,
34122
34110
  approvalRuntime,
34123
34111
  hookEngine: options.hookEngine,
34124
- ...options.toolExecutionScopeFactory !== void 0 ? { toolExecutionScopeFactory: options.toolExecutionScopeFactory } : {},
34112
+ toolExecutionScopeFactory: options.toolExecutionScopeFactory,
34125
34113
  sessionRules: options.sessionRules,
34126
34114
  permissionMode: effectivePermissionMode,
34127
34115
  stateCache,
34128
34116
  initialMeta,
34129
34117
  pathConfig: this.paths,
34130
- ...options.approvalStateStore !== void 0 ? { approvalStateStore: options.approvalStateStore } : {},
34118
+ approvalStateStore: options.approvalStateStore,
34131
34119
  ...options.agentTypeRegistry !== void 0 && subagentStore !== void 0 ? {
34132
34120
  subagentStore,
34133
34121
  agentTypeRegistry: options.agentTypeRegistry,
@@ -42206,26 +42194,26 @@ function buildInlinePrompt(content, args) {
42206
42194
  * failures into {@link MCPConfigError} for a consistent error surface
42207
42195
  * with the Python runtime.
42208
42196
  *
42209
- * The zod schemas are declared as non-exported `_raw...Schema`
42197
+ * The zod schemas are declared as non-exported `...Schema`
42210
42198
  * constants and re-exported as `z.ZodType<TsInterface>` values. This
42211
42199
  * is the same idiom `storage/wire-record.ts` uses to keep
42212
42200
  * `isolatedDeclarations` happy — without it every exported schema
42213
42201
  * would need a gigantic generated zod type annotation.
42214
42202
  */
42215
- const _rawStdioServerConfigSchema = z.object({
42203
+ const StdioServerConfigSchema = z.object({
42216
42204
  command: z.string().min(1, "stdio server \"command\" must be a non-empty string"),
42217
42205
  args: z.array(z.string()).optional(),
42218
42206
  env: z.record(z.string(), z.string()).optional(),
42219
42207
  cwd: z.string().optional()
42220
42208
  }).strict();
42221
- const _rawHttpServerConfigSchema = z.object({
42209
+ const HttpServerConfigSchema = z.object({
42222
42210
  url: z.string().url("http server \"url\" must be a valid URL"),
42223
42211
  transport: z.union([z.literal("http"), z.literal("sse")]),
42224
42212
  headers: z.record(z.string(), z.string()).optional(),
42225
42213
  auth: z.literal("oauth").optional()
42226
42214
  }).strict();
42227
- const _rawMcpServerConfigSchema = z.union([_rawStdioServerConfigSchema, _rawHttpServerConfigSchema]);
42228
- z.object({ mcpServers: z.record(z.string().min(1), _rawMcpServerConfigSchema) }).strict();
42215
+ const McpServerConfigSchema = z.union([StdioServerConfigSchema, HttpServerConfigSchema]);
42216
+ z.object({ mcpServers: z.record(z.string().min(1), McpServerConfigSchema) }).strict();
42229
42217
  [
42230
42218
  "<!doctype html>",
42231
42219
  "<html><head><meta charset=\"utf-8\"><title>Authentication complete</title></head>",
@@ -42241,11 +42229,6 @@ z.object({ mcpServers: z.record(z.string().min(1), _rawMcpServerConfigSchema) })
42241
42229
  *
42242
42230
  * Mirrors the Python `Config` / `LLMProvider` / `LLMModel` structure with
42243
42231
  * a simplified shape suitable for the TS rewrite.
42244
- *
42245
- * Each exported schema is annotated with `z.ZodType<T>`
42246
- * so the file compiles under `--isolatedDeclarations`. Interfaces are
42247
- * hand-declared rather than `z.infer`'d to avoid the circular type reference
42248
- * that isolatedDeclarations forbids.
42249
42232
  */
42250
42233
  const ProviderType = z.enum([
42251
42234
  "anthropic",
@@ -78777,30 +78760,16 @@ function resolveDefaultSoulPlusDefaultModel(options) {
78777
78760
  if (defaultModel === void 0 || defaultModel.length === 0) return "";
78778
78761
  return defaultModel;
78779
78762
  }
78780
- function getDefaultSoulPlusMaxContextSize(options, defaultModel) {
78781
- return options.kimiConfig?.models?.[defaultModel]?.maxContextSize ?? 2e5;
78782
- }
78783
- function getDefaultSoulPlusModelCapabilities(options, defaultModel) {
78784
- return options.kimiConfig?.models?.[defaultModel]?.capabilities;
78785
- }
78786
78763
  async function createDefaultSoulPlusRuntimeBundle(options) {
78787
78764
  const defaultModel = resolveDefaultSoulPlusDefaultModel(options);
78788
- const maxContextSize = getDefaultSoulPlusMaxContextSize(options, defaultModel);
78789
- const modelCapabilities = getDefaultSoulPlusModelCapabilities(options, defaultModel);
78790
- if (options.runtime !== void 0 && options.compactionProvider !== void 0) return {
78765
+ if (options.runtime !== void 0) return {
78791
78766
  runtime: options.runtime,
78792
- compactionProvider: options.compactionProvider,
78793
- defaultModel,
78794
- maxContextSize,
78795
- ...modelCapabilities !== void 0 ? { modelCapabilities } : {}
78767
+ defaultModel
78796
78768
  };
78797
- if (options.runtime !== void 0 || options.compactionProvider !== void 0) throw new Error("createDefaultSoulPlusWireClient: runtime and compactionProvider must be provided together");
78769
+ if (options.runtime !== void 0) throw new Error("createDefaultSoulPlusWireClient: runtime and compactionProvider must be provided together");
78798
78770
  if (defaultModel.length === 0) return {
78799
78771
  runtime: createRuntime({ kosong: new UnconfiguredKosongAdapter() }),
78800
- compactionProvider: new UnconfiguredCompactionProvider(),
78801
- defaultModel,
78802
- maxContextSize,
78803
- ...modelCapabilities !== void 0 ? { modelCapabilities } : {}
78772
+ defaultModel
78804
78773
  };
78805
78774
  if (options.kimiConfig === void 0) throw new Error("createDefaultSoulPlusWireClient: provide either runtime+compactionProvider or kimiConfig");
78806
78775
  const provider = await createProviderFromConfig(options.kimiConfig, defaultModel, {
@@ -78814,10 +78783,7 @@ async function createDefaultSoulPlusRuntimeBundle(options) {
78814
78783
  provider,
78815
78784
  tokenRefresher: options.tokenRefresher
78816
78785
  }) }),
78817
- compactionProvider: createKosongCompactionProvider(provider),
78818
78786
  defaultModel,
78819
- maxContextSize,
78820
- ...modelCapabilities !== void 0 ? { modelCapabilities } : {},
78821
78787
  videoUploader
78822
78788
  };
78823
78789
  }
@@ -78827,11 +78793,6 @@ var UnconfiguredKosongAdapter = class {
78827
78793
  throw new LLMNotSetError(LLM_NOT_CONFIGURED_MESSAGE);
78828
78794
  }
78829
78795
  };
78830
- var UnconfiguredCompactionProvider = class {
78831
- async run() {
78832
- throw new LLMNotSetError(LLM_NOT_CONFIGURED_MESSAGE);
78833
- }
78834
- };
78835
78796
  //#endregion
78836
78797
  //#region ../../packages/kaos/src/errors.ts
78837
78798
  /**
@@ -95170,7 +95131,6 @@ async function startCoreWireServer(options) {
95170
95131
  sessionManager,
95171
95132
  router,
95172
95133
  runtime: options.runtime,
95173
- compactionProvider: options.compactionProvider,
95174
95134
  kosong: options.runtime.kosong,
95175
95135
  tools: options.tools ?? [],
95176
95136
  ...options.toolsProvider !== void 0 ? { toolsProvider: options.toolsProvider } : {},
@@ -95188,7 +95148,6 @@ async function startCoreWireServer(options) {
95188
95148
  defaultModel: options.defaultModel,
95189
95149
  runtimeProvider: options.runtimeProvider,
95190
95150
  ...options.defaultModelProvider !== void 0 ? { defaultModelProvider: options.defaultModelProvider } : {},
95191
- ...options.compactionProviderProvider !== void 0 ? { compactionProviderProvider: options.compactionProviderProvider } : {},
95192
95151
  ...options.compactionConfigProvider !== void 0 ? { compactionConfigProvider: options.compactionConfigProvider } : {},
95193
95152
  ...options.runtimeBundleProvider !== void 0 ? { runtimeBundleProvider: options.runtimeBundleProvider } : {},
95194
95153
  ...options.rebuildRuntimeForModel !== void 0 ? { rebuildRuntimeForModel: options.rebuildRuntimeForModel } : {},
@@ -95276,8 +95235,6 @@ async function createInProcessWireClient(options) {
95276
95235
  }
95277
95236
  //#endregion
95278
95237
  //#region ../../packages/kimi-sdk/src/default-soul-plus-wire-client.ts
95279
- const DEFAULT_RESERVED_CONTEXT_SIZE = 5e4;
95280
- const DEFAULT_RESERVED_CONTEXT_FRACTION = .25;
95281
95238
  async function createDefaultSoulPlusWireClient(options) {
95282
95239
  const pathConfig = options.pathConfig ?? new PathConfig();
95283
95240
  const configPath = options.configPath ?? join(pathConfig.home, "config.toml");
@@ -95301,10 +95258,6 @@ async function createDefaultSoulPlusWireClient(options) {
95301
95258
  let authService;
95302
95259
  let activeVideoUploader;
95303
95260
  let activeDefaultModel = defaultModel;
95304
- let activeMaxContextSize = getDefaultSoulPlusMaxContextSize({
95305
- kimiConfig: effectiveConfig,
95306
- defaultModel
95307
- }, defaultModel);
95308
95261
  const backgroundManager = new BackgroundProcessManager();
95309
95262
  let defaultsPromise;
95310
95263
  const builtinByModel = /* @__PURE__ */ new Map();
@@ -95352,10 +95305,7 @@ async function createDefaultSoulPlusWireClient(options) {
95352
95305
  activeVideoUploader = next.videoUploader;
95353
95306
  return {
95354
95307
  model: next.defaultModel,
95355
- runtime: next.runtime,
95356
- compactionProvider: next.compactionProvider,
95357
- compactionConfig: buildCompactionConfig(effectiveConfig, next.defaultModel, next.maxContextSize),
95358
- ...next.modelCapabilities !== void 0 ? { modelCapabilities: next.modelCapabilities } : {}
95308
+ runtime: next.runtime
95359
95309
  };
95360
95310
  };
95361
95311
  const reloadConfigFromDisk = async () => {
@@ -95365,10 +95315,6 @@ async function createDefaultSoulPlusWireClient(options) {
95365
95315
  defaultModel = resolved.defaultModel;
95366
95316
  activeDefaultModel = defaultModel;
95367
95317
  builtinByModel.clear();
95368
- activeMaxContextSize = getDefaultSoulPlusMaxContextSize({
95369
- kimiConfig: effectiveConfig,
95370
- defaultModel
95371
- }, defaultModel);
95372
95318
  };
95373
95319
  authService = createDefaultSoulPlusOAuthService({
95374
95320
  kimiConfig: effectiveConfig,
@@ -95378,15 +95324,14 @@ async function createDefaultSoulPlusWireClient(options) {
95378
95324
  forceManagedKimiCodeOAuth: defaultModel.length === 0,
95379
95325
  onConfigProvisioned: reloadConfigFromDisk
95380
95326
  });
95381
- const fallbackRuntimeBundle = await createDefaultSoulPlusRuntimeBundle({
95382
- defaultModel: "",
95383
- kimiConfig: effectiveConfig
95384
- });
95385
95327
  const handle = await createInProcessWireClient({
95386
95328
  pathConfig,
95387
- runtime: fallbackRuntimeBundle.runtime,
95388
- compactionProvider: fallbackRuntimeBundle.compactionProvider,
95329
+ runtime: (await createDefaultSoulPlusRuntimeBundle({
95330
+ defaultModel: "",
95331
+ kimiConfig: effectiveConfig
95332
+ })).runtime,
95389
95333
  runtimeBundleProvider: ({ model }) => buildRuntimeBundle(model),
95334
+ compactionConfigProvider: () => {},
95390
95335
  workspaceDir: options.workspaceDir,
95391
95336
  defaultModel: activeDefaultModel,
95392
95337
  defaultModelProvider: () => activeDefaultModel,
@@ -95415,9 +95360,6 @@ async function createDefaultSoulPlusWireClient(options) {
95415
95360
  get defaultModel() {
95416
95361
  return activeDefaultModel;
95417
95362
  },
95418
- get maxContextSize() {
95419
- return activeMaxContextSize;
95420
- },
95421
95363
  get defaultThinking() {
95422
95364
  return effectiveConfig.defaultThinking ?? false;
95423
95365
  },
@@ -95441,21 +95383,6 @@ async function createDefaultSoulPlusWireClient(options) {
95441
95383
  }
95442
95384
  };
95443
95385
  }
95444
- function buildCompactionConfig(config, model, maxContextSize) {
95445
- const loop = config.loopControl;
95446
- const resolvedMaxContextSize = config.models?.[model] !== void 0 ? getDefaultSoulPlusMaxContextSize({
95447
- kimiConfig: config,
95448
- defaultModel: model
95449
- }, model) : maxContextSize;
95450
- return {
95451
- maxContextSize: resolvedMaxContextSize,
95452
- ...loop?.compactionTriggerRatio !== void 0 ? { triggerRatio: loop.compactionTriggerRatio } : {},
95453
- reservedContextSize: loop?.reservedContextSize ?? defaultReservedContextSize(resolvedMaxContextSize)
95454
- };
95455
- }
95456
- function defaultReservedContextSize(maxContextSize) {
95457
- return Math.max(0, Math.min(DEFAULT_RESERVED_CONTEXT_SIZE, Math.floor(maxContextSize * DEFAULT_RESERVED_CONTEXT_FRACTION)));
95458
- }
95459
95386
  function inferUserAgent(defaultHeaders) {
95460
95387
  return defaultHeaders?.["User-Agent"] ?? "KimiCLI/unknown";
95461
95388
  }
@@ -95605,7 +95532,7 @@ function pinoToLogger(p) {
95605
95532
  async function createKimiAgent() {
95606
95533
  const workDir = process.cwd();
95607
95534
  const version = getVersion();
95608
- const { client, dispose, maxContextSize, defaultModel, defaultThinking, defaultYolo, defaultPlanMode, availableModels, syncSessionRuntime } = await createDefaultSoulPlusWireClient({
95535
+ const { client, dispose, defaultModel, defaultThinking, defaultYolo, defaultPlanMode, availableModels, syncSessionRuntime } = await createDefaultSoulPlusWireClient({
95609
95536
  workspaceDir: workDir,
95610
95537
  userAgent: `KimiCLI/${version}`,
95611
95538
  defaultHeaders: buildKimiDefaultHeaders(version),
@@ -95621,7 +95548,7 @@ async function createKimiAgent() {
95621
95548
  planMode: defaultPlanMode
95622
95549
  },
95623
95550
  availableModels,
95624
- maxContextSize,
95551
+ maxContextSize: 1e6,
95625
95552
  syncSessionRuntime,
95626
95553
  dispose
95627
95554
  };
@@ -95854,6 +95781,28 @@ function shellQuote(path) {
95854
95781
  return `'${path.replace(/'/g, "'\\''")}'`;
95855
95782
  }
95856
95783
  //#endregion
95784
+ //#region src/tui/commands/parser.ts
95785
+ /**
95786
+ * Slash command input parser.
95787
+ *
95788
+ * Splits a `/<name> [args]` line into the command name + the trimmed
95789
+ * argument tail. Returns null when the input is not a slash command
95790
+ * (no leading `/` or only whitespace after `/`).
95791
+ */
95792
+ function parseSlashInput(input) {
95793
+ if (!input.startsWith("/")) return null;
95794
+ const trimmed = input.slice(1).trim();
95795
+ if (trimmed.length === 0) return null;
95796
+ const spaceIdx = trimmed.indexOf(" ");
95797
+ const name = spaceIdx === -1 ? trimmed : trimmed.slice(0, spaceIdx);
95798
+ const args = spaceIdx === -1 ? "" : trimmed.slice(spaceIdx + 1).trim();
95799
+ if (name.includes("/")) return null;
95800
+ return {
95801
+ name,
95802
+ args
95803
+ };
95804
+ }
95805
+ //#endregion
95857
95806
  //#region src/tui/components/media/image-thumbnail.ts
95858
95807
  /**
95859
95808
  * Transcript-side rendering of a pasted image.
@@ -95888,8 +95837,10 @@ var ImageThumbnail = class extends Container {
95888
95837
  }
95889
95838
  };
95890
95839
  //#endregion
95840
+ //#region src/tui/symbols.ts
95841
+ const STATUS_BULLET = "⏺︎ ";
95842
+ //#endregion
95891
95843
  //#region src/tui/components/messages/assistant-message.ts
95892
- const BULLET$1 = "⏺ ";
95893
95844
  const INDENT$1 = " ";
95894
95845
  var AssistantMessageComponent = class {
95895
95846
  contentContainer;
@@ -95917,12 +95868,12 @@ var AssistantMessageComponent = class {
95917
95868
  }
95918
95869
  render(width) {
95919
95870
  if (this.lastText.trim().length === 0) return [];
95920
- const prefix = this.showBullet ? BULLET$1 : INDENT$1;
95921
- const contentWidth = Math.max(1, width - prefix.length);
95871
+ const prefix = this.showBullet ? STATUS_BULLET : INDENT$1;
95872
+ const contentWidth = Math.max(1, width - visibleWidth(prefix));
95922
95873
  const contentLines = this.contentContainer.render(contentWidth);
95923
95874
  const lines = [""];
95924
95875
  for (let i = 0; i < contentLines.length; i++) {
95925
- const p = i === 0 && this.showBullet ? chalk.hex(this.bulletColor)(BULLET$1) : INDENT$1;
95876
+ const p = i === 0 && this.showBullet ? chalk.hex(this.bulletColor)(STATUS_BULLET) : INDENT$1;
95926
95877
  lines.push(p + contentLines[i]);
95927
95878
  }
95928
95879
  return lines;
@@ -95961,7 +95912,6 @@ var SkillActivationComponent = class extends Container {
95961
95912
  };
95962
95913
  //#endregion
95963
95914
  //#region src/tui/components/messages/thinking.ts
95964
- const BULLET = "⏺ ";
95965
95915
  const INDENT = " ";
95966
95916
  const PREVIEW_LINES = 3;
95967
95917
  const SPINNER_FRAMES = [
@@ -96023,7 +95973,7 @@ var ThinkingComponent = class {
96023
95973
  }
96024
95974
  const rendered = [""];
96025
95975
  for (let i = 0; i < contentLines.length; i++) {
96026
- const p = i === 0 && this.showMarker ? chalk.hex(this.color)(BULLET) : INDENT;
95976
+ const p = i === 0 && this.showMarker ? chalk.hex(this.color)(STATUS_BULLET) : INDENT;
96027
95977
  rendered.push(p + contentLines[i]);
96028
95978
  }
96029
95979
  if (this.expanded || contentLines.length <= PREVIEW_LINES) return rendered;
@@ -97074,8 +97024,8 @@ var ToolCallComponent = class extends Container {
97074
97024
  const isFinished = result !== void 0;
97075
97025
  const isError = result?.is_error ?? false;
97076
97026
  let bullet;
97077
- if (isFinished) bullet = isError ? chalk.hex(colors.error)("✗ ") : chalk.hex(colors.success)("⏺ ");
97078
- else bullet = chalk.hex(colors.roleAssistant)("⏺ ");
97027
+ if (isFinished) bullet = isError ? chalk.hex(colors.error)("✗ ") : chalk.hex(colors.success)(STATUS_BULLET);
97028
+ else bullet = chalk.hex(colors.roleAssistant)(STATUS_BULLET);
97079
97029
  if (toolCall.name === "ExitPlanMode") {
97080
97030
  const label = chalk.hex(colors.primary).bold("Current plan");
97081
97031
  if (!isFinished || result === void 0 || result.is_error === true) return label;
@@ -97509,7 +97459,7 @@ function finalizeTurn(ectx, sendQueued) {
97509
97459
  streamingPhase: "idle"
97510
97460
  });
97511
97461
  ectx.resetLivePane();
97512
- setTimeout(() => sendQueued(next.text), 0);
97462
+ setTimeout(() => sendQueued(next), 0);
97513
97463
  return;
97514
97464
  }
97515
97465
  ectx.setAppState({
@@ -98075,11 +98025,8 @@ function clearTranscriptAndRedraw(state) {
98075
98025
  /** Use primary color for slash commands or while plan mode is active. */
98076
98026
  function updateEditorBorderHighlight(state, text) {
98077
98027
  const trimmed = (text ?? state.editor.getText()).trimStart();
98078
- if (state.appState.planMode || trimmed.startsWith("/")) {
98079
- const primary = state.colors.primary;
98080
- state.editor.borderColor = (s) => chalk.hex(primary)(s);
98081
- } else state.editor.borderColor = state.editorTheme.borderColor;
98082
- state.editor.slashHighlightHex = state.colors.primary;
98028
+ const colorToken = state.appState.planMode || trimmed.startsWith("/") ? state.colors.primary : state.colors.border;
98029
+ state.editor.borderColor = (s) => chalk.hex(colorToken)(s);
98083
98030
  state.ui.requestRender();
98084
98031
  }
98085
98032
  /**
@@ -98095,7 +98042,6 @@ function applyTheme(state, theme, hooks, resolved) {
98095
98042
  state.resolvedTheme = resolvedTheme;
98096
98043
  state.styles = createThemeStyles(state.colors);
98097
98044
  state.markdownTheme = createMarkdownTheme(state.colors);
98098
- state.editorTheme = createEditorTheme(state.colors);
98099
98045
  updateEditorBorderHighlight(state);
98100
98046
  hooks.syncFooter();
98101
98047
  hooks.refreshActivityPane();
@@ -98417,6 +98363,7 @@ function createAuthCommands(deps = {}) {
98417
98363
  aliases: [],
98418
98364
  description: "Clear OAuth credentials",
98419
98365
  mode: "both",
98366
+ availability: "idle-only",
98420
98367
  priority: 40,
98421
98368
  async execute(_args, _ctx) {
98422
98369
  if (deps.client === void 0) return ok$2("OAuth is unavailable: no wire client is attached.");
@@ -98429,6 +98376,7 @@ function createAuthCommands(deps = {}) {
98429
98376
  aliases: [],
98430
98377
  description: "Start OAuth device code login flow",
98431
98378
  mode: "both",
98379
+ availability: "idle-only",
98432
98380
  priority: 40,
98433
98381
  async execute(_args, ctx) {
98434
98382
  if (deps.client === void 0) return ok$2("OAuth is unavailable: no wire client is attached.");
@@ -98488,6 +98436,7 @@ const shellCommands = [
98488
98436
  aliases: ["h", "?"],
98489
98437
  description: "Show available commands and shortcuts",
98490
98438
  mode: "both",
98439
+ availability: "always",
98491
98440
  priority: 80,
98492
98441
  async execute(_args, _ctx) {
98493
98442
  return ok$1("__show_help__");
@@ -98498,6 +98447,7 @@ const shellCommands = [
98498
98447
  aliases: [],
98499
98448
  description: "Show version information",
98500
98449
  mode: "both",
98450
+ availability: "always",
98501
98451
  priority: 20,
98502
98452
  async execute(_args, ctx) {
98503
98453
  return ok$1(`Kimi Code v${ctx.appState.version}`);
@@ -98521,6 +98471,7 @@ const shellCommands = [
98521
98471
  aliases: ["resume"],
98522
98472
  description: "Browse and resume sessions",
98523
98473
  mode: "both",
98474
+ availability: "always",
98524
98475
  priority: 80,
98525
98476
  async execute(_args, _ctx) {
98526
98477
  return ok$1("__show_sessions__");
@@ -98531,6 +98482,7 @@ const shellCommands = [
98531
98482
  aliases: ["rename"],
98532
98483
  description: "Set or show session title",
98533
98484
  mode: "both",
98485
+ availability: "always",
98534
98486
  priority: 60,
98535
98487
  async execute(args, ctx) {
98536
98488
  const trimmed = args.trim();
@@ -98552,6 +98504,7 @@ const shellCommands = [
98552
98504
  aliases: ["yes"],
98553
98505
  description: "Toggle auto-approve mode",
98554
98506
  mode: "both",
98507
+ availability: "always",
98555
98508
  priority: 100,
98556
98509
  async execute(args, ctx) {
98557
98510
  let enabled;
@@ -98569,6 +98522,7 @@ const shellCommands = [
98569
98522
  aliases: [],
98570
98523
  description: "Toggle plan mode",
98571
98524
  mode: "both",
98525
+ availability: (args) => args.trim().toLowerCase() === "clear" ? "idle-only" : "always",
98572
98526
  priority: 100,
98573
98527
  async execute(args, ctx) {
98574
98528
  const subcmd = args.trim().toLowerCase();
@@ -98597,6 +98551,7 @@ const shellCommands = [
98597
98551
  aliases: [],
98598
98552
  description: "Switch LLM model",
98599
98553
  mode: "both",
98554
+ availability: "always",
98600
98555
  priority: 100,
98601
98556
  async execute(args, ctx) {
98602
98557
  const trimmed = args.trim();
@@ -98610,6 +98565,7 @@ const shellCommands = [
98610
98565
  aliases: ["status"],
98611
98566
  description: "Show session tokens + context window + plan quotas",
98612
98567
  mode: "both",
98568
+ availability: "always",
98613
98569
  priority: 60,
98614
98570
  async execute() {
98615
98571
  return ok$1("__show_usage__");
@@ -98620,6 +98576,7 @@ const shellCommands = [
98620
98576
  aliases: [],
98621
98577
  description: "Set the external editor for Ctrl-G",
98622
98578
  mode: "both",
98579
+ availability: "always",
98623
98580
  priority: 60,
98624
98581
  async execute(args, _ctx) {
98625
98582
  const trimmed = args.trim();
@@ -98632,6 +98589,7 @@ const shellCommands = [
98632
98589
  aliases: [],
98633
98590
  description: "Set the terminal UI theme",
98634
98591
  mode: "both",
98592
+ availability: "always",
98635
98593
  priority: 60,
98636
98594
  async execute(args) {
98637
98595
  const trimmed = args.trim();
@@ -98663,28 +98621,6 @@ const soulCommands = [{
98663
98621
  }
98664
98622
  }];
98665
98623
  //#endregion
98666
- //#region src/tui/commands/parser.ts
98667
- /**
98668
- * Slash command input parser.
98669
- *
98670
- * Splits a `/<name> [args]` line into the command name + the trimmed
98671
- * argument tail. Returns null when the input is not a slash command
98672
- * (no leading `/` or only whitespace after `/`).
98673
- */
98674
- function parseSlashInput(input) {
98675
- if (!input.startsWith("/")) return null;
98676
- const trimmed = input.slice(1).trim();
98677
- if (trimmed.length === 0) return null;
98678
- const spaceIdx = trimmed.indexOf(" ");
98679
- const name = spaceIdx === -1 ? trimmed : trimmed.slice(0, spaceIdx);
98680
- const args = spaceIdx === -1 ? "" : trimmed.slice(spaceIdx + 1).trim();
98681
- if (name.includes("/")) return null;
98682
- return {
98683
- name,
98684
- args
98685
- };
98686
- }
98687
- //#endregion
98688
98624
  //#region src/tui/commands/index.ts
98689
98625
  function fuzzyScore(query, target) {
98690
98626
  if (query.length === 0) return 1;
@@ -98782,8 +98718,8 @@ function createDefaultRegistry(authDeps) {
98782
98718
  /**
98783
98719
  * Custom editor extending pi-tui Editor with app-level keybindings.
98784
98720
  */
98785
- const ANSI_SGR = /\x1B\[[0-9;]*m/g;
98786
- const KITTY_CSI_U = /^\x1B\[(\d+);(\d+)((?::\d+)*)u$/;
98721
+ const ANSI_SGR = /\u001B\[[0-9;]*m/g;
98722
+ const KITTY_CSI_U = /^\u001B\[(\d+);(\d+)((?::\d+)*)u$/;
98787
98723
  const CAPS_LOCK_BIT = 64;
98788
98724
  const CTRL_BIT = 4;
98789
98725
  const SHIFT_BIT = 1;
@@ -98815,7 +98751,7 @@ function normalizeCapsLockedCtrl(data) {
98815
98751
  if (codepoint < 65 || codepoint > 90) return data;
98816
98752
  const loweredCodepoint = codepoint + 32;
98817
98753
  const strippedModifier = (modifier & ~CAPS_LOCK_BIT) + 1;
98818
- return `\x1B[${String(loweredCodepoint)};${String(strippedModifier)}${tail}u`;
98754
+ return `\u001B[${String(loweredCodepoint)};${String(strippedModifier)}${tail}u`;
98819
98755
  }
98820
98756
  /** Convert a visible-char index (ANSI-stripped) back to an index into the raw ANSI-bearing string. */
98821
98757
  function mapVisibleIdxToRaw(line, visibleIdx) {
@@ -98859,15 +98795,23 @@ var CustomEditor = class extends Editor {
98859
98795
  * the next keystroke.
98860
98796
  */
98861
98797
  onPasteImage;
98862
- /**
98863
- * Hex colour used to highlight a leading `/slash-command` token in
98864
- * the input. Host sets this on theme change; undefined disables the
98865
- * highlight entirely.
98866
- */
98867
- slashHighlightHex;
98868
98798
  autocompleteVisibleLastRender = false;
98869
98799
  hasRenderedOnce = false;
98870
98800
  autocompleteViewportRefreshQueued = false;
98801
+ /**
98802
+ * `colors` is the live `ColorPalette` reference — the host mutates it
98803
+ * in place on theme switch (`Object.assign(state.colors, ...)`), so
98804
+ * reading `this.colors.<token>` at render time always sees the
98805
+ * current theme without any setter plumbing. The `EditorTheme` that
98806
+ * pi-tui's `Editor` requires is derived from the same palette, and
98807
+ * `paddingX: 2` reserves the two leading columns where `render()`
98808
+ * paints the terminal-style `> ` prompt — both are implementation
98809
+ * details, not caller knobs.
98810
+ */
98811
+ constructor(tui, colors) {
98812
+ super(tui, createEditorTheme(colors), { paddingX: 2 });
98813
+ this.colors = colors;
98814
+ }
98871
98815
  render(width) {
98872
98816
  const lines = super.render(width);
98873
98817
  const autocompleteVisible = this.isShowingAutocomplete();
@@ -98876,14 +98820,19 @@ var CustomEditor = class extends Editor {
98876
98820
  lines,
98877
98821
  firstContentIdx: 1
98878
98822
  };
98879
- const hex = this.slashHighlightHex;
98880
- if (hex === void 0) return arrangedLines;
98881
- if (!this.getText().trimStart().startsWith("/")) return arrangedLines;
98882
98823
  if (arrangedLines.length < 3) return arrangedLines;
98883
- const original = arrangedLines[firstContentIdx];
98884
- if (original === void 0) return arrangedLines;
98885
- const highlighted = highlightFirstSlashToken(original, hex);
98886
- if (highlighted !== void 0) arrangedLines[firstContentIdx] = highlighted;
98824
+ if (this.getText().trimStart().startsWith("/")) {
98825
+ const original = arrangedLines[firstContentIdx];
98826
+ if (original !== void 0) {
98827
+ const highlighted = highlightFirstSlashToken(original, this.colors.primary);
98828
+ if (highlighted !== void 0) arrangedLines[firstContentIdx] = highlighted;
98829
+ }
98830
+ }
98831
+ const firstContent = arrangedLines[firstContentIdx];
98832
+ if (firstContent !== void 0) {
98833
+ const withPrompt = injectPromptSymbol(firstContent);
98834
+ if (withPrompt !== void 0) arrangedLines[firstContentIdx] = withPrompt;
98835
+ }
98887
98836
  return arrangedLines;
98888
98837
  }
98889
98838
  handleInput(data) {
@@ -98966,6 +98915,7 @@ function highlightFirstSlashToken(line, hex) {
98966
98915
  if (ch === " " || ch === " ") break;
98967
98916
  endVisible++;
98968
98917
  }
98918
+ if (visible.slice(slashIdx, endVisible).slice(1).includes("/")) return void 0;
98969
98919
  const rawStart = mapVisibleIdxToRaw(line, slashIdx);
98970
98920
  const rawEnd = mapVisibleIdxToRaw(line, endVisible);
98971
98921
  const before = line.slice(0, rawStart);
@@ -98973,6 +98923,21 @@ function highlightFirstSlashToken(line, hex) {
98973
98923
  const after = line.slice(rawEnd);
98974
98924
  return before + chalk.hex(hex).bold(token) + after;
98975
98925
  }
98926
+ /**
98927
+ * Overlay a terminal-style `> ` prompt symbol on the first content line.
98928
+ * Relies on the editor being configured with `paddingX >= 2` so the line
98929
+ * starts with at least two literal spaces — those slots are replaced with
98930
+ * the prompt token, preserving overall line width and cursor positioning.
98931
+ * Emits no SGR so the terminal's default foreground colour renders the
98932
+ * symbol (white-on-dark / black-on-light, matching the user's typed text).
98933
+ * Returns `undefined` if the line is too short or doesn't begin with the
98934
+ * expected padding.
98935
+ */
98936
+ function injectPromptSymbol(line) {
98937
+ if (line.length < 2) return void 0;
98938
+ if (line[0] !== " " || line[1] !== " ") return void 0;
98939
+ return "> " + line.slice(2);
98940
+ }
98976
98941
  function moveAutocompleteAboveEditor(lines) {
98977
98942
  const bottomBorderIdx = findLastEditorBorderIndex(lines);
98978
98943
  if (bottomBorderIdx < 1 || bottomBorderIdx >= lines.length - 1) return {
@@ -99465,7 +99430,6 @@ function createTUIState(options) {
99465
99430
  const colors = { ...getColorPalette(resolvedTheme) };
99466
99431
  const styles = createThemeStyles(colors);
99467
99432
  const markdownTheme = createMarkdownTheme(colors);
99468
- const editorTheme = createEditorTheme(colors);
99469
99433
  const terminal = new ProcessTerminal();
99470
99434
  const ui = new TUI(terminal);
99471
99435
  const transcriptContainer = new Container();
@@ -99474,8 +99438,7 @@ function createTUIState(options) {
99474
99438
  const todoPanel = new TodoPanelComponent(colors);
99475
99439
  const queueContainer = new Container();
99476
99440
  const editorContainer = new Container();
99477
- const editor = new CustomEditor(ui, editorTheme);
99478
- editor.slashHighlightHex = colors.primary;
99441
+ const editor = new CustomEditor(ui, colors);
99479
99442
  const footer = new FooterComponent({ ...initialAppState }, colors);
99480
99443
  const registry = createDefaultRegistry({ client: options.client });
99481
99444
  const approvalController = new ApprovalController();
@@ -99498,7 +99461,6 @@ function createTUIState(options) {
99498
99461
  colors,
99499
99462
  styles,
99500
99463
  markdownTheme,
99501
- editorTheme,
99502
99464
  resolvedTheme,
99503
99465
  appState: { ...initialAppState },
99504
99466
  startupState: "pending",
@@ -99663,27 +99625,33 @@ function addTranscriptEntry(state, entry) {
99663
99625
  }
99664
99626
  //#endregion
99665
99627
  //#region src/tui/actions/queue-ops.ts
99666
- function enqueueMessage(state, text) {
99628
+ function nextQueueId(state) {
99667
99629
  state.queueIdCounter += 1;
99630
+ return `q-${String(state.queueIdCounter)}`;
99631
+ }
99632
+ function enqueueMessage(state, text) {
99668
99633
  state.queuedMessages.push({
99669
- id: `q-${String(state.queueIdCounter)}`,
99634
+ id: nextQueueId(state),
99635
+ kind: "message",
99670
99636
  text
99671
99637
  });
99672
99638
  }
99639
+ function queuedMessageText(item) {
99640
+ if (item.kind === "message") return item.text;
99641
+ const args = item.skillArgs.trim();
99642
+ return args.length > 0 ? `/${item.skillName} ${args}` : `/${item.skillName}`;
99643
+ }
99673
99644
  function recallLastQueued(state) {
99674
99645
  if (state.queuedMessages.length === 0) return void 0;
99675
99646
  const last = state.queuedMessages.at(-1);
99676
99647
  state.queuedMessages = state.queuedMessages.slice(0, -1);
99677
- return last.text;
99648
+ return queuedMessageText(last);
99678
99649
  }
99679
99650
  function dequeueFirst(state) {
99680
99651
  if (state.queuedMessages.length === 0) return void 0;
99681
99652
  const first = state.queuedMessages[0];
99682
99653
  state.queuedMessages = state.queuedMessages.slice(1);
99683
- return first.text;
99684
- }
99685
- function clearQueue(state) {
99686
- state.queuedMessages.length = 0;
99654
+ return first;
99687
99655
  }
99688
99656
  //#endregion
99689
99657
  //#region src/tui/actions/wire-ops.ts
@@ -99861,10 +99829,13 @@ function handleUserInput(state, text, hooks, onSlash) {
99861
99829
  return;
99862
99830
  }
99863
99831
  persistInputHistory(state, text);
99864
- if (text.startsWith("/")) {
99832
+ if (parseSlashInput(text) !== null) {
99865
99833
  onSlash(text);
99866
99834
  return;
99867
99835
  }
99836
+ sendNormalUserInput(state, text, hooks);
99837
+ }
99838
+ function sendNormalUserInput(state, text, hooks) {
99868
99839
  const extraction = extractImageAttachments(text, state.imageStore);
99869
99840
  const addEntry = (e) => addTranscriptEntry(state, e);
99870
99841
  if (extraction.hasImages) sendMessage(state, addEntry, text, {
@@ -100035,7 +100006,7 @@ var AgentGroupComponent = class extends Container {
100035
100006
  const failed = snapshots.filter((s) => s.phase === "failed").length;
100036
100007
  const finished = done + failed;
100037
100008
  const allDone = finished === total;
100038
- const bullet = allDone ? chalk.hex(colors.success)("⏺ ") : chalk.hex(colors.roleAssistant)("⏺ ");
100009
+ const bullet = allDone ? chalk.hex(colors.success)(STATUS_BULLET) : chalk.hex(colors.roleAssistant)(STATUS_BULLET);
100039
100010
  if (allDone) {
100040
100011
  const types = new Set(snapshots.map((s) => s.agentName).filter((n) => n !== void 0));
100041
100012
  const headerLabel = types.size === 1 ? `${String(total)} ${[...types][0]} agents finished` : `${String(total)} agents finished`;
@@ -100494,11 +100465,9 @@ function isRecord$1(value) {
100494
100465
  function applyContextUsage(patch, value) {
100495
100466
  if (!isRecord$1(value)) return;
100496
100467
  const used = value["used"];
100497
- const total = value["total"];
100498
100468
  const percent = value["percent"];
100499
100469
  if (typeof percent === "number") patch.contextUsage = Number.isFinite(percent) ? percent / 100 : 0;
100500
100470
  if (typeof used === "number" && Number.isFinite(used)) patch.contextTokens = used;
100501
- if (typeof total === "number" && Number.isFinite(total) && total > 0) patch.maxContextTokens = total;
100502
100471
  }
100503
100472
  function applySessionResumeSnapshot(state, hooks, sessionId, snapshot) {
100504
100473
  const patch = { sessionId };
@@ -100653,8 +100622,8 @@ var CompactionComponent = class extends Container {
100653
100622
  this.stopBlink();
100654
100623
  }
100655
100624
  buildHeader() {
100656
- if (this.done) return `${chalk.hex(this.colors.success)("⏺ ")}${chalk.hex(this.colors.success).bold("Compaction complete")}${this.tokensBefore !== void 0 && this.tokensAfter !== void 0 ? chalk.dim(` (${String(this.tokensBefore)} → ${String(this.tokensAfter)} tokens)`) : ""}`;
100657
- return `${this.blinkOn ? chalk.hex(this.colors.roleAssistant)("⏺ ") : " "}${chalk.hex(this.colors.primary).bold("Compacting context...")}`;
100625
+ if (this.done) return `${chalk.hex(this.colors.success)(STATUS_BULLET)}${chalk.hex(this.colors.success).bold("Compaction complete")}${this.tokensBefore !== void 0 && this.tokensAfter !== void 0 ? chalk.dim(` (${String(this.tokensBefore)} → ${String(this.tokensAfter)} tokens)`) : ""}`;
100626
+ return `${this.blinkOn ? chalk.hex(this.colors.roleAssistant)(STATUS_BULLET) : " "}${chalk.hex(this.colors.primary).bold("Compacting context...")}`;
100658
100627
  }
100659
100628
  startBlink() {
100660
100629
  this.blinkTimer = setInterval(() => {
@@ -100861,9 +100830,9 @@ var ReadGroupComponent = class extends Container {
100861
100830
  buildHeader(total, pending, failed, totalLines) {
100862
100831
  const colors = this.colors;
100863
100832
  const dim = chalk.dim;
100864
- if (pending > 0) return `${chalk.hex(colors.roleAssistant)("⏺ ")}${chalk.hex(colors.primary).bold(`Reading ${String(total)} files…`)}`;
100833
+ if (pending > 0) return `${chalk.hex(colors.roleAssistant)(STATUS_BULLET)}${chalk.hex(colors.primary).bold(`Reading ${String(total)} files…`)}`;
100865
100834
  if (failed === total) return `${chalk.hex(colors.error)("✗ ")}${chalk.hex(colors.error).bold(`Read ${String(total)} files`)}${chalk.hex(colors.error)(" · failed")}`;
100866
- return `${chalk.hex(colors.success)("⏺ ")}${chalk.hex(colors.primary).bold(`Read ${String(total)} files`)}${dim(` · ${String(totalLines)} ${totalLines === 1 ? "line" : "lines"}`)}${failed > 0 ? chalk.hex(colors.error)(` · ${String(failed)} failed`) : ""}`;
100835
+ return `${chalk.hex(colors.success)(STATUS_BULLET)}${chalk.hex(colors.primary).bold(`Read ${String(total)} files`)}${dim(` · ${String(totalLines)} ${totalLines === 1 ? "line" : "lines"}`)}${failed > 0 ? chalk.hex(colors.error)(` · ${String(failed)} failed`) : ""}`;
100867
100836
  }
100868
100837
  buildBodyLine(snap, isLast) {
100869
100838
  const colors = this.colors;
@@ -101215,6 +101184,24 @@ async function applyThemeChoice(state, theme, hooks) {
101215
101184
  emitStatus(state, `Theme set to "${theme}"${theme === "auto" ? ` (detected: ${resolved})` : ""}.`);
101216
101185
  }
101217
101186
  //#endregion
101187
+ //#region src/tui/panes/queue-pane.ts
101188
+ /**
101189
+ * Queue pane — renders `state.queuedMessages` in the queue container
101190
+ * above the input. No-op when the queue is empty.
101191
+ *
101192
+ * Extracted from `KimiTUI.updateQueueDisplay`.
101193
+ */
101194
+ function updateQueueDisplay(state) {
101195
+ state.queueContainer.clear();
101196
+ const queued = state.queuedMessages;
101197
+ if (queued.length === 0) return;
101198
+ const accent = chalk.hex(state.colors.accent);
101199
+ const dim = chalk.hex(state.colors.textDim);
101200
+ for (const item of queued) state.queueContainer.addChild(new Text(accent(` ❯ ${queuedMessageText(item)}`), 0, 0));
101201
+ const hint = state.appState.isCompacting && !state.appState.isStreaming ? " ↑ to edit · will send after compaction" : " ↑ to edit · ctrl-s to steer immediately";
101202
+ state.queueContainer.addChild(new Text(dim(hint), 0, 0));
101203
+ }
101204
+ //#endregion
101218
101205
  //#region src/tui/commands/skill-commands.ts
101219
101206
  const SKILL_ACTIVATION_SENTINEL_PREFIX = "__activate_skill__:";
101220
101207
  async function fetchSkills(client, sessionId) {
@@ -101237,6 +101224,7 @@ function buildSkillCommand(skill) {
101237
101224
  aliases: [],
101238
101225
  description: skill.description ?? "",
101239
101226
  mode: "both",
101227
+ availability: "idle-only",
101240
101228
  async execute(args) {
101241
101229
  const trimmed = args.trim();
101242
101230
  const prompt = trimmed.length > 0 ? `${skill.content}\n\nUser request:\n${trimmed}` : skill.content;
@@ -101312,18 +101300,24 @@ async function dispatchSlashCommand(input, state, buildCtx, addEntry) {
101312
101300
  const ctx = buildCtx();
101313
101301
  const result = await tryDispatchSkill(state.client, state.appState.sessionId, parsed.name, parsed.args);
101314
101302
  if (result.kind === "activate") {
101315
- ctx.activateSkill(result.name, result.args, result.prompt);
101303
+ activateSkillIfIdle(state, ctx, addEntry, parsed.name, result.name, result.args, result.prompt);
101316
101304
  return true;
101317
101305
  }
101306
+ ctx.sendAsMessage(input);
101307
+ refreshQueueIfBusy(state);
101308
+ return true;
101309
+ }
101310
+ const ctx = buildCtx();
101311
+ if (isBusy(state) && resolveAvailability(def, parsed.args) === "idle-only") {
101318
101312
  addEntry({
101319
- id: `slash-${Date.now()}`,
101313
+ id: `slash-blocked-${Date.now()}`,
101320
101314
  kind: "status",
101321
101315
  renderMode: "plain",
101322
- content: result.message
101316
+ content: busyMessage(parsed.name, state),
101317
+ color: state.colors.error
101323
101318
  });
101324
101319
  return true;
101325
101320
  }
101326
- const ctx = buildCtx();
101327
101321
  const baseCtx = {
101328
101322
  client: ctx.client,
101329
101323
  appState: ctx.appState,
@@ -101396,6 +101390,7 @@ async function dispatchSlashCommand(input, state, buildCtx, addEntry) {
101396
101390
  if (result.message.startsWith("__send_as_message__:")) {
101397
101391
  const msg = result.message.slice(20);
101398
101392
  ctx.sendAsMessage(msg);
101393
+ refreshQueueIfBusy(state);
101399
101394
  return true;
101400
101395
  }
101401
101396
  if (result.message.startsWith("__activate_skill__:")) {
@@ -101413,7 +101408,7 @@ async function dispatchSlashCommand(input, state, buildCtx, addEntry) {
101413
101408
  });
101414
101409
  return true;
101415
101410
  }
101416
- ctx.activateSkill(payload.name, payload.args, payload.prompt);
101411
+ activateSkillIfIdle(state, ctx, addEntry, parsed.name, payload.name, payload.args, payload.prompt);
101417
101412
  return true;
101418
101413
  }
101419
101414
  addEntry({
@@ -101427,6 +101422,35 @@ async function dispatchSlashCommand(input, state, buildCtx, addEntry) {
101427
101422
  }
101428
101423
  return true;
101429
101424
  }
101425
+ function resolveAvailability(def, args) {
101426
+ const availability = def.availability ?? "idle-only";
101427
+ return typeof availability === "function" ? availability(args) : availability;
101428
+ }
101429
+ function isBusy(state) {
101430
+ return state.appState.isStreaming || state.appState.isCompacting;
101431
+ }
101432
+ function busyMessage(commandName, state) {
101433
+ if (state.appState.isStreaming) return `Cannot /${commandName} while streaming — press Esc or Ctrl-C first.`;
101434
+ return `Cannot /${commandName} while compacting — wait for compaction to finish first.`;
101435
+ }
101436
+ function activateSkillIfIdle(state, ctx, addEntry, commandName, name, args, prompt) {
101437
+ if (isBusy(state)) {
101438
+ addEntry({
101439
+ id: `slash-blocked-${Date.now()}`,
101440
+ kind: "status",
101441
+ renderMode: "plain",
101442
+ content: busyMessage(commandName, state),
101443
+ color: state.colors.error
101444
+ });
101445
+ return;
101446
+ }
101447
+ ctx.activateSkill(name, args, prompt);
101448
+ }
101449
+ function refreshQueueIfBusy(state) {
101450
+ if (!isBusy(state)) return;
101451
+ updateQueueDisplay(state);
101452
+ state.ui.requestRender();
101453
+ }
101430
101454
  //#endregion
101431
101455
  //#region src/tui/handlers/turn-lifecycle.ts
101432
101456
  function handleTurnBegin(ectx, _data) {
@@ -101707,7 +101731,7 @@ function handleCompactionEnd(ectx, data, sendQueued) {
101707
101731
  if (ectx.state.queuedMessages.length > 0) {
101708
101732
  const [next, ...rest] = ectx.state.queuedMessages;
101709
101733
  ectx.state.queuedMessages = rest;
101710
- setTimeout(() => sendQueued(next.text), 0);
101734
+ setTimeout(() => sendQueued(next), 0);
101711
101735
  }
101712
101736
  } else ectx.setAppState({ isCompacting: false });
101713
101737
  }
@@ -102539,24 +102563,6 @@ function stopPhaseSpinner(state) {
102539
102563
  }
102540
102564
  }
102541
102565
  //#endregion
102542
- //#region src/tui/panes/queue-pane.ts
102543
- /**
102544
- * Queue pane — renders `state.queuedMessages` in the queue container
102545
- * above the input. No-op when the queue is empty.
102546
- *
102547
- * Extracted from `KimiTUI.updateQueueDisplay`.
102548
- */
102549
- function updateQueueDisplay(state) {
102550
- state.queueContainer.clear();
102551
- const queued = state.queuedMessages;
102552
- if (queued.length === 0) return;
102553
- const accent = chalk.hex(state.colors.accent);
102554
- const dim = chalk.hex(state.colors.textDim);
102555
- for (const item of queued) state.queueContainer.addChild(new Text(accent(` ❯ ${item.text}`), 0, 0));
102556
- const hint = state.appState.isCompacting && !state.appState.isStreaming ? " ↑ to edit · will send after compaction" : " ↑ to edit · ctrl-s to steer immediately";
102557
- state.queueContainer.addChild(new Text(dim(hint), 0, 0));
102558
- }
102559
- //#endregion
102560
102566
  //#region src/tui/reverse-rpc/approval/adapter.ts
102561
102567
  const DEFAULT_APPROVAL_CHOICES = [
102562
102568
  {
@@ -104280,6 +104286,7 @@ var BottomPinnedLayout = class {
104280
104286
  render(width) {
104281
104287
  const bodyLines = this.body.render(width);
104282
104288
  const bottomLines = this.bottom.flatMap((component) => component.render(width));
104289
+ if (bodyLines.length + bottomLines.length < this.terminal.rows) return [...bodyLines, ...bottomLines];
104283
104290
  const spacerHeight = Math.max(0, this.terminal.rows - bodyLines.length - bottomLines.length);
104284
104291
  return [
104285
104292
  ...bodyLines,
@@ -104351,8 +104358,8 @@ function setupEditor(state, cb) {
104351
104358
  editor.onCtrlS = () => {
104352
104359
  if (!state.appState.isStreaming || state.appState.isCompacting) return;
104353
104360
  const text = editor.getText().trim();
104354
- const queuedTexts = state.queuedMessages.map((m) => m.text);
104355
- clearQueue(state);
104361
+ const queuedTexts = state.queuedMessages.filter((m) => m.kind === "message").map((m) => m.text);
104362
+ state.queuedMessages = state.queuedMessages.filter((m) => m.kind !== "message");
104356
104363
  const parts = [];
104357
104364
  for (const q of queuedTexts) {
104358
104365
  const trimmed = q.trim();
@@ -104665,8 +104672,12 @@ var KimiTUI = class {
104665
104672
  }
104666
104673
  startWireSubscription() {
104667
104674
  const ectx = buildEventCtx(this.state, this.stateHooks, this.streamCallbacks, (entry) => this.addEntry(entry));
104668
- const sendQueued = (text) => {
104669
- sendMessageInternal(this.state, (e) => this.addEntry(e), text);
104675
+ const sendQueued = (item) => {
104676
+ if (item.kind === "skill") {
104677
+ sendSkillActivation(this.state, (e) => this.addEntry(e), item.skillName, item.skillArgs, item.fullPrompt);
104678
+ return;
104679
+ }
104680
+ sendMessageInternal(this.state, (e) => this.addEntry(e), item.text);
104670
104681
  };
104671
104682
  subscribeToWire(this.state, (event) => {
104672
104683
  dispatchEvent(event, ectx, sendQueued);