kfc-code-cli 0.0.1-alpha.13 → 0.0.1-alpha.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/main.mjs +2142 -1827
- package/package.json +1 -1
package/dist/main.mjs
CHANGED
|
@@ -183,75 +183,894 @@ var ContextStateBrokenError = class extends Error {
|
|
|
183
183
|
}
|
|
184
184
|
};
|
|
185
185
|
//#endregion
|
|
186
|
-
//#region ../../packages/kimi-core/src/storage/
|
|
187
|
-
/**
|
|
188
|
-
* Record types the compaction path itself is allowed to write while the
|
|
189
|
-
* lifecycle gate is in `compacting` state (§5.1.7 / v2 L2663-L2690).
|
|
190
|
-
*
|
|
191
|
-
* Background (Slice 6 audit M02): every non-compaction write is drained
|
|
192
|
-
* during `compacting` so the in-flight compaction is not racing against
|
|
193
|
-
* new Soul output. But compaction still needs to persist its own
|
|
194
|
-
* `CompactionRecord` via `context.resetToSummary()`, which goes through
|
|
195
|
-
* the same `JournalWriter.append()` entry point. Blanket-rejecting all
|
|
196
|
-
* writes in `compacting` would deadlock compaction against itself, so
|
|
197
|
-
* this whitelist names the record types that are considered
|
|
198
|
-
* "compaction's own writes" and allowed through the gate.
|
|
199
|
-
*
|
|
200
|
-
* Keep this list narrow: only record types that are *only* emitted from
|
|
201
|
-
* inside `TurnManager.executeCompaction` (Phase 2; previously
|
|
202
|
-
* `runCompaction`) belong here. Any future compaction-path record
|
|
203
|
-
* must be added explicitly.
|
|
204
|
-
*/
|
|
205
|
-
const COMPACTION_OWN_WRITE_TYPES = new Set(["compaction", "session_initialized"]);
|
|
186
|
+
//#region ../../packages/kimi-core/src/storage/schema/records.ts
|
|
206
187
|
/**
|
|
207
|
-
*
|
|
208
|
-
*
|
|
209
|
-
* here: their absence at replay time breaks the contracts §9.x relies on.
|
|
210
|
-
*
|
|
211
|
-
* Declared as `ReadonlySet<string>` so we can include future union-member
|
|
212
|
-
* strings (e.g. `subagent_completed` / `subagent_failed` from the
|
|
213
|
-
* subagent slice) without forcing an out-of-slice edit to `WireRecord`.
|
|
188
|
+
* Fallback producer identity used when the host hasn't supplied one.
|
|
189
|
+
* Version marker makes unset state easy to spot in field reports.
|
|
214
190
|
*/
|
|
215
|
-
const
|
|
216
|
-
"
|
|
217
|
-
"
|
|
218
|
-
"
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
"
|
|
222
|
-
"
|
|
191
|
+
const DEFAULT_PRODUCER = {
|
|
192
|
+
kind: "typescript",
|
|
193
|
+
name: "@moonshot-ai/core",
|
|
194
|
+
version: "0.0.0-unset"
|
|
195
|
+
};
|
|
196
|
+
const agentTypeEnum = z.enum([
|
|
197
|
+
"main",
|
|
198
|
+
"sub",
|
|
199
|
+
"independent"
|
|
223
200
|
]);
|
|
224
|
-
const
|
|
201
|
+
const _rawWireProducerSchema = z.object({
|
|
202
|
+
kind: z.enum(["python", "typescript"]),
|
|
203
|
+
name: z.string(),
|
|
204
|
+
version: z.string()
|
|
205
|
+
});
|
|
206
|
+
const WireFileMetadataSchema = z.object({
|
|
207
|
+
type: z.literal("metadata"),
|
|
208
|
+
protocol_version: z.string(),
|
|
209
|
+
created_at: z.number(),
|
|
210
|
+
kimi_version: z.string().optional(),
|
|
211
|
+
producer: _rawWireProducerSchema.optional()
|
|
212
|
+
});
|
|
213
|
+
const _rawTurnBeginRecordSchema = z.object({
|
|
214
|
+
type: z.literal("turn_begin"),
|
|
215
|
+
seq: z.number(),
|
|
216
|
+
time: z.number(),
|
|
217
|
+
turn_id: z.string(),
|
|
218
|
+
agent_type: agentTypeEnum,
|
|
219
|
+
user_input: z.string().optional(),
|
|
220
|
+
input_kind: z.enum(["user", "system_trigger"]),
|
|
221
|
+
trigger_source: z.string().optional()
|
|
222
|
+
});
|
|
223
|
+
const _rawTurnEndRecordSchema = z.object({
|
|
224
|
+
type: z.literal("turn_end"),
|
|
225
|
+
seq: z.number(),
|
|
226
|
+
time: z.number(),
|
|
227
|
+
turn_id: z.string(),
|
|
228
|
+
agent_type: agentTypeEnum,
|
|
229
|
+
success: z.boolean(),
|
|
230
|
+
reason: z.enum([
|
|
231
|
+
"done",
|
|
232
|
+
"cancelled",
|
|
233
|
+
"error",
|
|
234
|
+
"interrupted"
|
|
235
|
+
]),
|
|
236
|
+
usage: z.object({
|
|
237
|
+
input_tokens: z.number(),
|
|
238
|
+
output_tokens: z.number(),
|
|
239
|
+
cache_read_tokens: z.number().optional(),
|
|
240
|
+
cache_write_tokens: z.number().optional(),
|
|
241
|
+
cost_usd: z.number().optional()
|
|
242
|
+
}).optional(),
|
|
243
|
+
synthetic: z.boolean().optional()
|
|
244
|
+
});
|
|
245
|
+
const _userInputPartSchema = z.discriminatedUnion("type", [
|
|
246
|
+
z.object({
|
|
247
|
+
type: z.literal("text"),
|
|
248
|
+
text: z.string()
|
|
249
|
+
}),
|
|
250
|
+
z.object({
|
|
251
|
+
type: z.literal("image_url"),
|
|
252
|
+
image_url: z.object({ url: z.string() })
|
|
253
|
+
}),
|
|
254
|
+
z.object({
|
|
255
|
+
type: z.literal("video_url"),
|
|
256
|
+
video_url: z.object({ url: z.string() })
|
|
257
|
+
})
|
|
258
|
+
]);
|
|
259
|
+
const _rawUserMessageRecordSchema = z.object({
|
|
260
|
+
type: z.literal("user_message"),
|
|
261
|
+
seq: z.number(),
|
|
262
|
+
time: z.number(),
|
|
263
|
+
turn_id: z.string(),
|
|
264
|
+
content: z.union([z.string(), z.array(_userInputPartSchema).readonly()]),
|
|
265
|
+
uuid: z.string().optional()
|
|
266
|
+
});
|
|
267
|
+
const _rawToolResultRecordSchema = z.object({
|
|
268
|
+
type: z.literal("tool_result"),
|
|
269
|
+
seq: z.number(),
|
|
270
|
+
time: z.number(),
|
|
271
|
+
turn_id: z.string(),
|
|
272
|
+
tool_call_id: z.string(),
|
|
273
|
+
output: z.unknown(),
|
|
274
|
+
is_error: z.boolean().optional(),
|
|
275
|
+
synthetic: z.boolean().optional(),
|
|
276
|
+
uuid: z.string().optional(),
|
|
277
|
+
parent_uuid: z.string().optional()
|
|
278
|
+
});
|
|
279
|
+
const _rawCompactionRecordSchema = z.object({
|
|
280
|
+
type: z.literal("compaction"),
|
|
281
|
+
seq: z.number(),
|
|
282
|
+
time: z.number(),
|
|
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
|
+
pre_compact_tokens: z.number(),
|
|
290
|
+
post_compact_tokens: z.number(),
|
|
291
|
+
trigger: z.enum(["auto", "manual"]),
|
|
292
|
+
archive_file: z.string().optional(),
|
|
293
|
+
uuid: z.string().optional()
|
|
294
|
+
});
|
|
295
|
+
const _rawSystemPromptChangedRecordSchema = z.object({
|
|
296
|
+
type: z.literal("system_prompt_changed"),
|
|
297
|
+
seq: z.number(),
|
|
298
|
+
time: z.number(),
|
|
299
|
+
new_prompt: z.string()
|
|
300
|
+
});
|
|
301
|
+
const _rawToolsChangedRecordSchema = z.object({
|
|
302
|
+
type: z.literal("tools_changed"),
|
|
303
|
+
seq: z.number(),
|
|
304
|
+
time: z.number(),
|
|
305
|
+
operation: z.enum([
|
|
306
|
+
"register",
|
|
307
|
+
"remove",
|
|
308
|
+
"set_active"
|
|
309
|
+
]),
|
|
310
|
+
tools: z.array(z.string())
|
|
311
|
+
});
|
|
312
|
+
const _rawSystemReminderRecordSchema = z.object({
|
|
313
|
+
type: z.literal("system_reminder"),
|
|
314
|
+
seq: z.number(),
|
|
315
|
+
time: z.number(),
|
|
316
|
+
content: z.string(),
|
|
317
|
+
consumed_at_turn: z.number().optional()
|
|
318
|
+
});
|
|
319
|
+
const _rawNotificationRecordSchema = z.object({
|
|
320
|
+
type: z.literal("notification"),
|
|
321
|
+
seq: z.number(),
|
|
322
|
+
time: z.number(),
|
|
323
|
+
data: z.object({
|
|
324
|
+
id: z.string(),
|
|
325
|
+
category: z.enum([
|
|
326
|
+
"task",
|
|
327
|
+
"agent",
|
|
328
|
+
"system",
|
|
329
|
+
"team"
|
|
330
|
+
]),
|
|
331
|
+
type: z.string(),
|
|
332
|
+
source_kind: z.string(),
|
|
333
|
+
source_id: z.string(),
|
|
334
|
+
title: z.string(),
|
|
335
|
+
body: z.string(),
|
|
336
|
+
severity: z.enum([
|
|
337
|
+
"info",
|
|
338
|
+
"success",
|
|
339
|
+
"warning",
|
|
340
|
+
"error"
|
|
341
|
+
]),
|
|
342
|
+
payload: z.record(z.string(), z.unknown()).optional(),
|
|
343
|
+
targets: z.array(z.enum([
|
|
344
|
+
"llm",
|
|
345
|
+
"wire",
|
|
346
|
+
"shell"
|
|
347
|
+
])),
|
|
348
|
+
dedupe_key: z.string().optional(),
|
|
349
|
+
delivered_at: z.object({
|
|
350
|
+
llm: z.number().optional(),
|
|
351
|
+
wire: z.number().optional(),
|
|
352
|
+
shell: z.number().optional()
|
|
353
|
+
}).optional(),
|
|
354
|
+
envelope_id: z.string().optional()
|
|
355
|
+
})
|
|
356
|
+
});
|
|
357
|
+
const _rawToolDeniedRecordSchema = z.object({
|
|
358
|
+
type: z.literal("tool_denied"),
|
|
359
|
+
seq: z.number(),
|
|
360
|
+
time: z.number(),
|
|
361
|
+
turn_id: z.string(),
|
|
362
|
+
step: z.number(),
|
|
363
|
+
data: z.object({
|
|
364
|
+
tool_call_id: z.string(),
|
|
365
|
+
tool_name: z.string(),
|
|
366
|
+
rule_id: z.string(),
|
|
367
|
+
reason: z.string()
|
|
368
|
+
})
|
|
369
|
+
});
|
|
370
|
+
const _rawStepBeginRecordSchema = z.object({
|
|
371
|
+
type: z.literal("step_begin"),
|
|
372
|
+
seq: z.number(),
|
|
373
|
+
time: z.number(),
|
|
374
|
+
uuid: z.string(),
|
|
375
|
+
turn_id: z.string(),
|
|
376
|
+
step: z.number()
|
|
377
|
+
});
|
|
378
|
+
const _rawStepEndRecordSchema = z.object({
|
|
379
|
+
type: z.literal("step_end"),
|
|
380
|
+
seq: z.number(),
|
|
381
|
+
time: z.number(),
|
|
382
|
+
uuid: z.string(),
|
|
383
|
+
turn_id: z.string(),
|
|
384
|
+
step: z.number(),
|
|
385
|
+
usage: z.object({
|
|
386
|
+
input_tokens: z.number(),
|
|
387
|
+
output_tokens: z.number(),
|
|
388
|
+
cache_read_tokens: z.number().optional(),
|
|
389
|
+
cache_write_tokens: z.number().optional()
|
|
390
|
+
}).optional(),
|
|
391
|
+
finish_reason: z.string().optional()
|
|
392
|
+
});
|
|
393
|
+
const _contentPartBodySchema = z.discriminatedUnion("kind", [z.object({
|
|
394
|
+
kind: z.literal("text"),
|
|
395
|
+
text: z.string()
|
|
396
|
+
}), z.object({
|
|
397
|
+
kind: z.literal("think"),
|
|
398
|
+
think: z.string(),
|
|
399
|
+
encrypted: z.string().optional()
|
|
400
|
+
})]);
|
|
401
|
+
const _rawContentPartRecordSchema = z.object({
|
|
402
|
+
type: z.literal("content_part"),
|
|
403
|
+
seq: z.number(),
|
|
404
|
+
time: z.number(),
|
|
405
|
+
uuid: z.string(),
|
|
406
|
+
turn_id: z.string(),
|
|
407
|
+
step: z.number(),
|
|
408
|
+
step_uuid: z.string(),
|
|
409
|
+
role: z.literal("assistant"),
|
|
410
|
+
part: _contentPartBodySchema
|
|
411
|
+
});
|
|
412
|
+
const _rawToolCallRecordSchema = z.object({
|
|
413
|
+
type: z.literal("tool_call"),
|
|
414
|
+
seq: z.number(),
|
|
415
|
+
time: z.number(),
|
|
416
|
+
uuid: z.string(),
|
|
417
|
+
turn_id: z.string(),
|
|
418
|
+
step: z.number(),
|
|
419
|
+
step_uuid: z.string(),
|
|
420
|
+
data: z.object({
|
|
421
|
+
tool_call_id: z.string(),
|
|
422
|
+
tool_name: z.string(),
|
|
423
|
+
args: z.unknown(),
|
|
424
|
+
activity_description: z.string().optional(),
|
|
425
|
+
user_facing_name: z.string().optional(),
|
|
426
|
+
input_display: z.unknown().optional()
|
|
427
|
+
})
|
|
428
|
+
});
|
|
225
429
|
/**
|
|
226
|
-
*
|
|
227
|
-
*
|
|
430
|
+
* Storage persists the approval display as an opaque blob. Write-trust
|
|
431
|
+
* / read-passthrough: the concrete `ToolInputDisplay` union lives in
|
|
432
|
+
* `display/` and is the responsibility of the host layer to validate
|
|
433
|
+
* at render time.
|
|
228
434
|
*/
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
435
|
+
const ApprovalDisplaySchema = z.unknown();
|
|
436
|
+
const ApprovalSourceSchema = z.discriminatedUnion("kind", [
|
|
437
|
+
z.object({
|
|
438
|
+
kind: z.literal("soul"),
|
|
439
|
+
agent_id: z.string()
|
|
440
|
+
}),
|
|
441
|
+
z.object({
|
|
442
|
+
kind: z.literal("subagent"),
|
|
443
|
+
agent_id: z.string(),
|
|
444
|
+
subagent_type: z.string().optional()
|
|
445
|
+
}),
|
|
446
|
+
z.object({
|
|
447
|
+
kind: z.literal("turn"),
|
|
448
|
+
turn_id: z.string()
|
|
449
|
+
}),
|
|
450
|
+
z.object({
|
|
451
|
+
kind: z.literal("session"),
|
|
452
|
+
session_id: z.string()
|
|
453
|
+
}),
|
|
454
|
+
z.object({
|
|
455
|
+
kind: z.literal("mcp"),
|
|
456
|
+
server_id: z.string(),
|
|
457
|
+
reason: z.enum([
|
|
458
|
+
"elicitation",
|
|
459
|
+
"auth",
|
|
460
|
+
"tool_call"
|
|
461
|
+
])
|
|
462
|
+
})
|
|
463
|
+
]);
|
|
464
|
+
const skillInvocationTriggerEnum = z.enum([
|
|
465
|
+
"user-slash",
|
|
466
|
+
"claude-proactive",
|
|
467
|
+
"nested-skill"
|
|
468
|
+
]);
|
|
469
|
+
const _rawSkillInvokedRecordSchema = z.object({
|
|
470
|
+
type: z.literal("skill_invoked"),
|
|
471
|
+
seq: z.number(),
|
|
472
|
+
time: z.number(),
|
|
473
|
+
turn_id: z.string(),
|
|
474
|
+
agent_type: agentTypeEnum.optional(),
|
|
475
|
+
data: z.object({
|
|
476
|
+
skill_name: z.string(),
|
|
477
|
+
execution_mode: z.enum(["inline", "fork"]),
|
|
478
|
+
original_input: z.string(),
|
|
479
|
+
sub_agent_id: z.string().optional(),
|
|
480
|
+
invocation_trigger: skillInvocationTriggerEnum.optional(),
|
|
481
|
+
query_depth: z.number().optional()
|
|
482
|
+
})
|
|
483
|
+
});
|
|
484
|
+
const _rawSkillCompletedRecordSchema = z.object({
|
|
485
|
+
type: z.literal("skill_completed"),
|
|
486
|
+
seq: z.number(),
|
|
487
|
+
time: z.number(),
|
|
488
|
+
turn_id: z.string(),
|
|
489
|
+
agent_type: agentTypeEnum.optional(),
|
|
490
|
+
data: z.object({
|
|
491
|
+
skill_name: z.string(),
|
|
492
|
+
execution_mode: z.enum(["inline", "fork"]),
|
|
493
|
+
success: z.boolean(),
|
|
494
|
+
error: z.string().optional(),
|
|
495
|
+
sub_agent_id: z.string().optional(),
|
|
496
|
+
invocation_trigger: skillInvocationTriggerEnum.optional(),
|
|
497
|
+
query_depth: z.number().optional()
|
|
498
|
+
})
|
|
499
|
+
});
|
|
500
|
+
const _rawApprovalRequestRecordSchema = z.object({
|
|
501
|
+
type: z.literal("approval_request"),
|
|
502
|
+
seq: z.number(),
|
|
503
|
+
time: z.number(),
|
|
504
|
+
turn_id: z.string(),
|
|
505
|
+
step: z.number(),
|
|
506
|
+
data: z.object({
|
|
507
|
+
request_id: z.string(),
|
|
508
|
+
tool_call_id: z.string(),
|
|
509
|
+
tool_name: z.string(),
|
|
510
|
+
action: z.string(),
|
|
511
|
+
display: ApprovalDisplaySchema,
|
|
512
|
+
source: ApprovalSourceSchema
|
|
513
|
+
})
|
|
514
|
+
});
|
|
515
|
+
const _rawApprovalResponseRecordSchema = z.object({
|
|
516
|
+
type: z.literal("approval_response"),
|
|
517
|
+
seq: z.number(),
|
|
518
|
+
time: z.number(),
|
|
519
|
+
turn_id: z.string(),
|
|
520
|
+
step: z.number(),
|
|
521
|
+
data: z.object({
|
|
522
|
+
request_id: z.string(),
|
|
523
|
+
response: z.enum([
|
|
524
|
+
"approved",
|
|
525
|
+
"rejected",
|
|
526
|
+
"cancelled"
|
|
527
|
+
]),
|
|
528
|
+
feedback: z.string().optional(),
|
|
529
|
+
selected_label: z.string().optional(),
|
|
530
|
+
synthetic: z.boolean().optional()
|
|
531
|
+
})
|
|
532
|
+
});
|
|
533
|
+
const _rawTeamMailRecordSchema = z.object({
|
|
534
|
+
type: z.literal("team_mail"),
|
|
535
|
+
seq: z.number(),
|
|
536
|
+
time: z.number(),
|
|
537
|
+
data: z.object({
|
|
538
|
+
mail_id: z.string(),
|
|
539
|
+
reply_to: z.string().optional(),
|
|
540
|
+
from_agent: z.string(),
|
|
541
|
+
to_agent: z.string(),
|
|
542
|
+
content: z.string(),
|
|
543
|
+
summary: z.string().optional()
|
|
544
|
+
})
|
|
545
|
+
});
|
|
546
|
+
const _rawTokenUsageSchema = z.object({
|
|
547
|
+
input: z.number().int().nonnegative(),
|
|
548
|
+
output: z.number().int().nonnegative(),
|
|
549
|
+
cache_read: z.number().int().nonnegative().optional(),
|
|
550
|
+
cache_write: z.number().int().nonnegative().optional()
|
|
551
|
+
});
|
|
552
|
+
const _rawSubagentSpawnedRecordSchema = z.object({
|
|
553
|
+
type: z.literal("subagent_spawned"),
|
|
554
|
+
seq: z.number(),
|
|
555
|
+
time: z.number(),
|
|
556
|
+
uuid: z.string().optional(),
|
|
557
|
+
data: z.object({
|
|
558
|
+
agent_id: z.string(),
|
|
559
|
+
agent_name: z.string().optional(),
|
|
560
|
+
parent_tool_call_id: z.string(),
|
|
561
|
+
parent_agent_id: z.string().optional(),
|
|
562
|
+
parent_tool_call_uuid: z.string().optional(),
|
|
563
|
+
run_in_background: z.boolean()
|
|
564
|
+
})
|
|
565
|
+
});
|
|
566
|
+
const _rawSubagentCompletedRecordSchema = z.object({
|
|
567
|
+
type: z.literal("subagent_completed"),
|
|
568
|
+
seq: z.number(),
|
|
569
|
+
time: z.number(),
|
|
570
|
+
uuid: z.string().optional(),
|
|
571
|
+
parent_uuid: z.string().optional(),
|
|
572
|
+
data: z.object({
|
|
573
|
+
agent_id: z.string(),
|
|
574
|
+
parent_tool_call_id: z.string(),
|
|
575
|
+
result_summary: z.string(),
|
|
576
|
+
usage: _rawTokenUsageSchema.optional()
|
|
577
|
+
})
|
|
578
|
+
});
|
|
579
|
+
const _rawSubagentFailedRecordSchema = z.object({
|
|
580
|
+
type: z.literal("subagent_failed"),
|
|
581
|
+
seq: z.number(),
|
|
582
|
+
time: z.number(),
|
|
583
|
+
uuid: z.string().optional(),
|
|
584
|
+
parent_uuid: z.string().optional(),
|
|
585
|
+
data: z.object({
|
|
586
|
+
agent_id: z.string(),
|
|
587
|
+
parent_tool_call_id: z.string(),
|
|
588
|
+
error: z.string()
|
|
589
|
+
})
|
|
590
|
+
});
|
|
591
|
+
const _rawOwnershipChangedRecordSchema = z.object({
|
|
592
|
+
type: z.literal("ownership_changed"),
|
|
593
|
+
seq: z.number(),
|
|
594
|
+
time: z.number(),
|
|
595
|
+
old_owner: z.string().nullable(),
|
|
596
|
+
new_owner: z.string()
|
|
597
|
+
});
|
|
598
|
+
const _rawContextEditRecordSchema = z.object({
|
|
599
|
+
type: z.literal("context_edit"),
|
|
600
|
+
seq: z.number(),
|
|
601
|
+
time: z.number(),
|
|
602
|
+
operation: z.enum([
|
|
603
|
+
"edit_message",
|
|
604
|
+
"delete_message",
|
|
605
|
+
"rewind",
|
|
606
|
+
"insert_message",
|
|
607
|
+
"replace_message"
|
|
608
|
+
]),
|
|
609
|
+
target_seq: z.number().optional(),
|
|
610
|
+
to_turn: z.number().optional(),
|
|
611
|
+
after_seq: z.number().optional(),
|
|
612
|
+
new_content: z.string().optional(),
|
|
613
|
+
new_role: z.enum([
|
|
614
|
+
"user",
|
|
615
|
+
"assistant",
|
|
616
|
+
"system"
|
|
617
|
+
]).optional(),
|
|
618
|
+
cascade: z.boolean().optional()
|
|
619
|
+
});
|
|
620
|
+
const _rawContextClearedRecordSchema = z.object({
|
|
621
|
+
type: z.literal("context_cleared"),
|
|
622
|
+
seq: z.number(),
|
|
623
|
+
time: z.number()
|
|
624
|
+
});
|
|
625
|
+
const _sessionInitializedCommonShape = {
|
|
626
|
+
type: z.literal("session_initialized"),
|
|
627
|
+
seq: z.number(),
|
|
628
|
+
time: z.number(),
|
|
629
|
+
system_prompt: z.string(),
|
|
630
|
+
active_tools: z.array(z.string()),
|
|
631
|
+
model: z.string().optional(),
|
|
632
|
+
permission_mode: z.enum([
|
|
633
|
+
"default",
|
|
634
|
+
"acceptEdits",
|
|
635
|
+
"bypassPermissions"
|
|
636
|
+
]).optional(),
|
|
637
|
+
plan_mode: z.boolean().optional(),
|
|
638
|
+
workspace_dir: z.string().optional(),
|
|
639
|
+
thinking_level: z.string().optional()
|
|
236
640
|
};
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
641
|
+
const _rawSessionInitializedMainSchema = z.object({
|
|
642
|
+
..._sessionInitializedCommonShape,
|
|
643
|
+
agent_type: z.literal("main"),
|
|
644
|
+
session_id: z.string()
|
|
645
|
+
});
|
|
646
|
+
const _rawSessionInitializedSubSchema = z.object({
|
|
647
|
+
..._sessionInitializedCommonShape,
|
|
648
|
+
agent_type: z.literal("sub"),
|
|
649
|
+
agent_id: z.string(),
|
|
650
|
+
agent_name: z.string().optional(),
|
|
651
|
+
parent_session_id: z.string(),
|
|
652
|
+
parent_agent_id: z.string().optional(),
|
|
653
|
+
parent_tool_call_id: z.string(),
|
|
654
|
+
run_in_background: z.boolean()
|
|
655
|
+
});
|
|
656
|
+
const _rawSessionInitializedIndependentSchema = z.object({
|
|
657
|
+
..._sessionInitializedCommonShape,
|
|
658
|
+
agent_type: z.literal("independent"),
|
|
659
|
+
agent_id: z.string(),
|
|
660
|
+
agent_name: z.string().optional()
|
|
661
|
+
});
|
|
662
|
+
const _rawSessionInitializedRecordSchema = z.discriminatedUnion("agent_type", [
|
|
663
|
+
_rawSessionInitializedMainSchema,
|
|
664
|
+
_rawSessionInitializedSubSchema,
|
|
665
|
+
_rawSessionInitializedIndependentSchema
|
|
666
|
+
]);
|
|
667
|
+
const SessionInitializedRecordSchema = _rawSessionInitializedRecordSchema;
|
|
668
|
+
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
|
|
695
|
+
]);
|
|
696
|
+
//#endregion
|
|
697
|
+
//#region ../../packages/kimi-core/src/storage/journal/reader.ts
|
|
698
|
+
async function defaultReadLines(path) {
|
|
699
|
+
const parts = (await readFile(path, "utf8")).split("\n");
|
|
700
|
+
if (parts.length > 0 && parts.at(-1) === "") parts.pop();
|
|
701
|
+
return parts;
|
|
702
|
+
}
|
|
703
|
+
/**
|
|
704
|
+
* Replay a wire.jsonl into an ordered list of valid WireRecords plus a
|
|
705
|
+
* health signal. Does not construct a ContextState — that concern lives in
|
|
706
|
+
* WiredContextState which drives its internal mirror from these records.
|
|
707
|
+
*
|
|
708
|
+
* Error policy (§4.1.1 / §8.4):
|
|
709
|
+
* - unknown record type at any line → skip + warn
|
|
710
|
+
* - JSON.parse failure on the LAST body line → skip + warn (tail truncation)
|
|
711
|
+
* - JSON.parse failure on any earlier line → health = 'broken'
|
|
712
|
+
* - major version higher than supportedMajor → throw IncompatibleVersionError
|
|
713
|
+
*/
|
|
714
|
+
async function replayWire(path, options) {
|
|
715
|
+
const lines = await (options.readLines ?? defaultReadLines)(path);
|
|
716
|
+
if (lines.length === 0) throw new WireJournalCorruptError(`wire.jsonl is empty at ${path}`);
|
|
717
|
+
const firstLine = lines[0];
|
|
718
|
+
if (firstLine === void 0) throw new WireJournalCorruptError(`wire.jsonl is empty at ${path}`);
|
|
719
|
+
let meta;
|
|
720
|
+
try {
|
|
721
|
+
meta = WireFileMetadataSchema.parse(JSON.parse(firstLine));
|
|
722
|
+
} catch (error) {
|
|
723
|
+
throw new WireJournalCorruptError(`wire.jsonl metadata header (line 1) is invalid: ${String(error)}`);
|
|
724
|
+
}
|
|
725
|
+
const majorStr = meta.protocol_version.split(".")[0] ?? "0";
|
|
726
|
+
const major = Number.parseInt(majorStr, 10);
|
|
727
|
+
if (!Number.isFinite(major)) throw new IncompatibleVersionError(`wire.jsonl metadata.protocol_version "${meta.protocol_version}" is not a valid version string`);
|
|
728
|
+
if (major > options.supportedMajor) throw new IncompatibleVersionError(`wire.jsonl version ${meta.protocol_version} is not supported (max major: ${options.supportedMajor}). Please upgrade Kimi CLI.`);
|
|
729
|
+
if (meta.producer === void 0) throw new UnsupportedProducerError("legacy", "metadata-missing-producer");
|
|
730
|
+
if (meta.producer.kind !== "typescript") throw new UnsupportedProducerError(meta.producer.kind === "python" ? "python" : "unknown", "cross-producer-not-supported");
|
|
731
|
+
const producer = meta.producer;
|
|
732
|
+
const initLine = lines[1];
|
|
733
|
+
if (initLine === void 0) throw new MalformedWireError("session-initialized-missing", `wire.jsonl has no line 2 (session_initialized) at ${path}`);
|
|
734
|
+
let initRaw;
|
|
735
|
+
try {
|
|
736
|
+
initRaw = JSON.parse(initLine);
|
|
737
|
+
} catch (error) {
|
|
738
|
+
throw new MalformedWireError("session-initialized-missing", `wire.jsonl line 2 failed JSON.parse: ${String(error)}`);
|
|
739
|
+
}
|
|
740
|
+
const initTypeField = initRaw?.type;
|
|
741
|
+
if (initTypeField !== "session_initialized") throw new MalformedWireError("session-initialized-missing", `wire.jsonl line 2 has type="${String(initTypeField)}"; expected "session_initialized"`);
|
|
742
|
+
const initParsed = SessionInitializedRecordSchema.safeParse(initRaw);
|
|
743
|
+
if (!initParsed.success) throw new MalformedWireError("session-initialized-missing", `wire.jsonl line 2 failed zod parse: ${initParsed.error.message}`);
|
|
744
|
+
const sessionInitialized = initParsed.data;
|
|
745
|
+
const records = [];
|
|
746
|
+
const warnings = [];
|
|
747
|
+
const bodyLines = lines.slice(2);
|
|
748
|
+
for (const [i, line] of bodyLines.entries()) {
|
|
749
|
+
const physicalLineNo = i + 3;
|
|
750
|
+
const isLastLine = i === bodyLines.length - 1;
|
|
751
|
+
const snippet = line.slice(0, 100);
|
|
752
|
+
let raw;
|
|
753
|
+
try {
|
|
754
|
+
raw = JSON.parse(line);
|
|
755
|
+
} catch (error) {
|
|
756
|
+
if (isLastLine) {
|
|
757
|
+
warnings.push(`Tail line truncated at line ${physicalLineNo}, skipping: ${snippet}`);
|
|
758
|
+
continue;
|
|
759
|
+
}
|
|
760
|
+
const reason = `wire.jsonl mid-file corruption at line ${physicalLineNo}: ${String(error)}`;
|
|
761
|
+
return {
|
|
762
|
+
records,
|
|
763
|
+
protocolVersion: meta.protocol_version,
|
|
764
|
+
health: "broken",
|
|
765
|
+
brokenReason: reason,
|
|
766
|
+
warnings,
|
|
767
|
+
producer,
|
|
768
|
+
sessionInitialized
|
|
769
|
+
};
|
|
770
|
+
}
|
|
771
|
+
const typeField = raw?.type;
|
|
772
|
+
if (typeField === "session_initialized") throw new MalformedWireError("session-initialized-position-wrong", `wire.jsonl has a session_initialized record at line ${physicalLineNo}; only line 2 is permitted`);
|
|
773
|
+
if (typeof typeField !== "string" || !KNOWN_RECORD_TYPES.has(typeField)) {
|
|
774
|
+
if (typeof typeField === "string" && LEGACY_RECORD_TYPES.has(typeField)) warnings.push(`Skipping legacy record type "${typeField}" at line ${physicalLineNo} (migrated to state.json)`);
|
|
775
|
+
else warnings.push(`Skipping unrecognized record type "${String(typeField)}" at line ${physicalLineNo}: ${snippet}`);
|
|
776
|
+
continue;
|
|
777
|
+
}
|
|
778
|
+
const parsed = WireRecordSchema.safeParse(raw);
|
|
779
|
+
if (!parsed.success) {
|
|
780
|
+
if (isLastLine) {
|
|
781
|
+
warnings.push(`Tail record failed schema validation at line ${physicalLineNo}, skipping: ${snippet}`);
|
|
782
|
+
continue;
|
|
783
|
+
}
|
|
784
|
+
const reason = `wire.jsonl schema violation at line ${physicalLineNo}: ${parsed.error.message}`;
|
|
785
|
+
return {
|
|
786
|
+
records,
|
|
787
|
+
protocolVersion: meta.protocol_version,
|
|
788
|
+
health: "broken",
|
|
789
|
+
brokenReason: reason,
|
|
790
|
+
warnings,
|
|
791
|
+
producer,
|
|
792
|
+
sessionInitialized
|
|
793
|
+
};
|
|
794
|
+
}
|
|
795
|
+
records.push(parsed.data);
|
|
796
|
+
}
|
|
797
|
+
return {
|
|
798
|
+
records,
|
|
799
|
+
protocolVersion: meta.protocol_version,
|
|
800
|
+
health: "ok",
|
|
801
|
+
warnings,
|
|
802
|
+
producer,
|
|
803
|
+
sessionInitialized
|
|
804
|
+
};
|
|
805
|
+
}
|
|
806
|
+
const KNOWN_RECORD_TYPES = new Set([
|
|
807
|
+
"turn_begin",
|
|
808
|
+
"turn_end",
|
|
809
|
+
"user_message",
|
|
810
|
+
"tool_result",
|
|
811
|
+
"compaction",
|
|
812
|
+
"system_prompt_changed",
|
|
813
|
+
"tools_changed",
|
|
814
|
+
"system_reminder",
|
|
815
|
+
"notification",
|
|
816
|
+
"tool_denied",
|
|
817
|
+
"step_begin",
|
|
818
|
+
"step_end",
|
|
819
|
+
"content_part",
|
|
820
|
+
"tool_call",
|
|
821
|
+
"skill_invoked",
|
|
822
|
+
"skill_completed",
|
|
823
|
+
"approval_request",
|
|
824
|
+
"approval_response",
|
|
825
|
+
"team_mail",
|
|
826
|
+
"subagent_spawned",
|
|
827
|
+
"subagent_completed",
|
|
828
|
+
"subagent_failed",
|
|
829
|
+
"ownership_changed",
|
|
830
|
+
"context_edit",
|
|
831
|
+
"context_cleared"
|
|
832
|
+
]);
|
|
833
|
+
/**
|
|
834
|
+
* Record types that earlier schema versions persisted to `wire.jsonl`
|
|
835
|
+
* but have since been migrated to `state.json` as the truth source.
|
|
836
|
+
*
|
|
837
|
+
* Sessions written by older binaries still carry rows of these types.
|
|
838
|
+
* Keeping them in a dedicated set lets the replay loop emit an
|
|
839
|
+
* informational "legacy" warning instead of the forward-compat
|
|
840
|
+
* "unrecognized" warning, so it is clear these rows are expected to be
|
|
841
|
+
* dropped rather than being an unknown future-version type.
|
|
842
|
+
*/
|
|
843
|
+
const LEGACY_RECORD_TYPES = new Set([
|
|
844
|
+
"permission_mode_changed",
|
|
845
|
+
"model_changed",
|
|
846
|
+
"thinking_changed",
|
|
847
|
+
"plan_mode_changed",
|
|
848
|
+
"session_meta_changed"
|
|
849
|
+
]);
|
|
850
|
+
//#endregion
|
|
851
|
+
//#region ../../packages/kimi-core/src/storage/journal/rotation.ts
|
|
852
|
+
/**
|
|
853
|
+
* Storage-layer compaction utilities — file rotation and cross-file replay
|
|
854
|
+
* (§4.7 / §4.1.1).
|
|
855
|
+
*
|
|
856
|
+
* File rotation is the physical counterpart to TurnManager's
|
|
857
|
+
* `executeCompaction` (Phase 2; previously Soul's `runCompaction`):
|
|
858
|
+
* 1. Rename `wire.jsonl` → `wire.N.jsonl` (frozen archive)
|
|
859
|
+
* 2. Create new `wire.jsonl` with metadata header + CompactionRecord
|
|
860
|
+
*
|
|
861
|
+
* Cross-file replay reads all wire files in a session directory
|
|
862
|
+
* (wire.N.jsonl → ... → wire.1.jsonl → wire.jsonl) and produces a
|
|
863
|
+
* unified record stream.
|
|
864
|
+
*
|
|
865
|
+
* Crash recovery detects "wire.jsonl missing but wire.N.jsonl exists"
|
|
866
|
+
* and rolls back the lowest-numbered archive to `wire.jsonl`.
|
|
867
|
+
*/
|
|
868
|
+
const ARCHIVE_PATTERN = /^wire\.(\d+)\.jsonl$/;
|
|
869
|
+
function extractArchiveNumber(name) {
|
|
870
|
+
const captured = ARCHIVE_PATTERN.exec(name)?.[1];
|
|
871
|
+
return captured !== void 0 ? Number.parseInt(captured, 10) : 0;
|
|
872
|
+
}
|
|
873
|
+
/**
|
|
874
|
+
* Compute the next archive filename for a rotation:
|
|
875
|
+
* - No archives exist → `wire.1.jsonl`
|
|
876
|
+
* - Highest existing is `wire.3.jsonl` → `wire.4.jsonl`
|
|
877
|
+
*/
|
|
878
|
+
function nextArchiveName(sessionDir, existingArchives) {
|
|
879
|
+
let maxN = 0;
|
|
880
|
+
for (const archive of existingArchives) {
|
|
881
|
+
const n = extractArchiveNumber(archive);
|
|
882
|
+
if (n > maxN) maxN = n;
|
|
883
|
+
}
|
|
884
|
+
return join(sessionDir, `wire.${maxN + 1}.jsonl`);
|
|
885
|
+
}
|
|
886
|
+
async function rotateJournal(sessionDir, options) {
|
|
887
|
+
const { producer, protocolVersion, ...deps } = options;
|
|
888
|
+
const version = protocolVersion ?? "2.1";
|
|
889
|
+
const currentPath = join(sessionDir, "wire.jsonl");
|
|
890
|
+
const archivePath = nextArchiveName(sessionDir, (await readdir(sessionDir)).filter((e) => ARCHIVE_PATTERN.test(e)));
|
|
891
|
+
const renameFn = deps.renameFn ?? rename;
|
|
892
|
+
const retryDelayMs = deps.retryDelayMs ?? 500;
|
|
893
|
+
try {
|
|
894
|
+
await renameFn(currentPath, archivePath);
|
|
895
|
+
} catch (error) {
|
|
896
|
+
const code = error.code;
|
|
897
|
+
if (process.platform === "win32" && code === "EPERM") {
|
|
898
|
+
await new Promise((resolve, reject) => {
|
|
899
|
+
const timer = setTimeout(resolve, retryDelayMs);
|
|
900
|
+
const abortSignal = deps.signal;
|
|
901
|
+
if (abortSignal !== void 0) {
|
|
902
|
+
const onAbort = () => {
|
|
903
|
+
clearTimeout(timer);
|
|
904
|
+
reject(abortSignal.reason ?? /* @__PURE__ */ new Error("aborted"));
|
|
905
|
+
};
|
|
906
|
+
if (abortSignal.aborted) onAbort();
|
|
907
|
+
else abortSignal.addEventListener("abort", onAbort, { once: true });
|
|
908
|
+
}
|
|
909
|
+
});
|
|
910
|
+
await renameFn(currentPath, archivePath);
|
|
911
|
+
} else throw error;
|
|
912
|
+
}
|
|
913
|
+
await writeFileAtomicDurable(currentPath, JSON.stringify({
|
|
914
|
+
type: "metadata",
|
|
915
|
+
protocol_version: version,
|
|
916
|
+
created_at: Date.now(),
|
|
917
|
+
producer,
|
|
918
|
+
kimi_version: producer.version
|
|
919
|
+
}) + "\n");
|
|
920
|
+
await syncDir(sessionDir);
|
|
921
|
+
return {
|
|
922
|
+
archivePath,
|
|
923
|
+
newCurrentPath: currentPath
|
|
924
|
+
};
|
|
925
|
+
}
|
|
926
|
+
/**
|
|
927
|
+
* Find the highest-numbered archive in a list of archive filenames.
|
|
928
|
+
* Returns the filename and its number, or `undefined` if the list is empty.
|
|
929
|
+
*/
|
|
930
|
+
function findHighestArchive(archives) {
|
|
931
|
+
let highestN = 0;
|
|
932
|
+
let result;
|
|
933
|
+
for (const name of archives) {
|
|
934
|
+
const n = extractArchiveNumber(name);
|
|
935
|
+
if (n > highestN) {
|
|
936
|
+
highestN = n;
|
|
937
|
+
result = name;
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
return result !== void 0 ? {
|
|
941
|
+
name: result,
|
|
942
|
+
n: highestN
|
|
943
|
+
} : void 0;
|
|
944
|
+
}
|
|
945
|
+
/**
|
|
946
|
+
* Detect and recover from a crash between file rotation steps.
|
|
947
|
+
*
|
|
948
|
+
* Three scenarios are handled:
|
|
949
|
+
*
|
|
950
|
+
* 1. **wire.jsonl missing**: `wire.jsonl` was renamed to `wire.N.jsonl`
|
|
951
|
+
* but the process crashed before the new `wire.jsonl` was created.
|
|
952
|
+
* Recovery: roll back the highest-numbered archive to `wire.jsonl`.
|
|
953
|
+
*
|
|
954
|
+
* 2. **wire.jsonl is metadata-only** (Slice 3.3 / M04): the rename
|
|
955
|
+
* succeeded AND the new `wire.jsonl` was created with a metadata
|
|
956
|
+
* header, but the process crashed before `appendBoundary` ran. The
|
|
957
|
+
* new file has only the metadata line — no session_initialized, no
|
|
958
|
+
* compaction record. Recovery: remove the half-complete file and
|
|
959
|
+
* roll back the highest archive.
|
|
960
|
+
*
|
|
961
|
+
* 3. **wire.jsonl has metadata + session_initialized only** (Phase 23 /
|
|
962
|
+
* T7.7): `appendBoundary` copied `session_initialized` through as the
|
|
963
|
+
* second line, but the process crashed before the compaction record
|
|
964
|
+
* landed. Without the compaction record, the archived conversation is
|
|
965
|
+
* orphaned — replay of the new wire would show an empty post-boundary
|
|
966
|
+
* window and the archive would never be re-read. Recovery: same as
|
|
967
|
+
* case 2 — remove the half-complete file and restore the archive.
|
|
968
|
+
*
|
|
969
|
+
* Returns `true` if recovery was performed, `false` if no recovery was needed.
|
|
970
|
+
*/
|
|
971
|
+
async function recoverRotation(sessionDir) {
|
|
972
|
+
const entries = await readdir(sessionDir);
|
|
973
|
+
const archives = entries.filter((e) => ARCHIVE_PATTERN.test(e));
|
|
974
|
+
if (!entries.includes("wire.jsonl")) {
|
|
975
|
+
const highest = findHighestArchive(archives);
|
|
976
|
+
if (highest === void 0) return false;
|
|
977
|
+
await rename(join(sessionDir, highest.name), join(sessionDir, "wire.jsonl"));
|
|
978
|
+
return true;
|
|
979
|
+
}
|
|
980
|
+
if (archives.length > 0) {
|
|
981
|
+
const currentPath = join(sessionDir, "wire.jsonl");
|
|
982
|
+
const lines = (await readFile(currentPath, "utf8")).trim().split("\n").filter((l) => l.length > 0);
|
|
983
|
+
if (lines.length === 1 || lines.length === 2) {
|
|
984
|
+
const parsedLines = lines.map((l) => {
|
|
985
|
+
try {
|
|
986
|
+
return JSON.parse(l);
|
|
987
|
+
} catch {
|
|
988
|
+
return null;
|
|
989
|
+
}
|
|
990
|
+
});
|
|
991
|
+
const isMetadata = parsedLines[0]?.["type"] === "metadata";
|
|
992
|
+
if (lines.length === 1 ? isMetadata : isMetadata && parsedLines[1]?.["type"] === "session_initialized") {
|
|
993
|
+
const highest = findHighestArchive(archives);
|
|
994
|
+
if (highest !== void 0) {
|
|
995
|
+
await unlink(currentPath);
|
|
996
|
+
await rename(join(sessionDir, highest.name), join(sessionDir, "wire.jsonl"));
|
|
997
|
+
return true;
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
return false;
|
|
1003
|
+
}
|
|
1004
|
+
//#endregion
|
|
1005
|
+
//#region ../../packages/kimi-core/src/storage/journal/writer.ts
|
|
1006
|
+
/**
|
|
1007
|
+
* Record types the compaction path itself is allowed to write while the
|
|
1008
|
+
* lifecycle gate is in `compacting` state (§5.1.7 / v2 L2663-L2690).
|
|
1009
|
+
*
|
|
1010
|
+
* Background (Slice 6 audit M02): every non-compaction write is drained
|
|
1011
|
+
* during `compacting` so the in-flight compaction is not racing against
|
|
1012
|
+
* new Soul output. But compaction still needs to persist its own
|
|
1013
|
+
* `CompactionRecord` via `context.resetToSummary()`, which goes through
|
|
1014
|
+
* the same `JournalWriter.append()` entry point. Blanket-rejecting all
|
|
1015
|
+
* writes in `compacting` would deadlock compaction against itself, so
|
|
1016
|
+
* this whitelist names the record types that are considered
|
|
1017
|
+
* "compaction's own writes" and allowed through the gate.
|
|
1018
|
+
*
|
|
1019
|
+
* Keep this list narrow: only record types that are *only* emitted from
|
|
1020
|
+
* inside `TurnManager.executeCompaction` (Phase 2; previously
|
|
1021
|
+
* `runCompaction`) belong here. Any future compaction-path record
|
|
1022
|
+
* must be added explicitly.
|
|
1023
|
+
*/
|
|
1024
|
+
const COMPACTION_OWN_WRITE_TYPES = new Set(["compaction", "session_initialized"]);
|
|
1025
|
+
/**
|
|
1026
|
+
* Record types that must be durable on disk before `append()` resolves
|
|
1027
|
+
* (§4.5.4 — force-flush kinds). Recovery-critical boundary markers live
|
|
1028
|
+
* here: their absence at replay time breaks the contracts §9.x relies on.
|
|
1029
|
+
*
|
|
1030
|
+
* Declared as `ReadonlySet<string>` so we can include future union-member
|
|
1031
|
+
* strings (e.g. `subagent_completed` / `subagent_failed` from the
|
|
1032
|
+
* subagent slice) without forcing an out-of-slice edit to `WireRecord`.
|
|
1033
|
+
*/
|
|
1034
|
+
const FORCE_FLUSH_KINDS = new Set([
|
|
1035
|
+
"approval_response",
|
|
1036
|
+
"turn_end",
|
|
1037
|
+
"subagent_completed",
|
|
1038
|
+
"subagent_failed",
|
|
1039
|
+
"session_initialized",
|
|
1040
|
+
"context_cleared",
|
|
1041
|
+
"tools_changed"
|
|
1042
|
+
]);
|
|
1043
|
+
const DEFAULT_PROTOCOL_VERSION = "2.1";
|
|
1044
|
+
/**
|
|
1045
|
+
* Chains work onto a single promise so all callers are serialised in the
|
|
1046
|
+
* order `run` was invoked, regardless of when each task resolves.
|
|
1047
|
+
*/
|
|
1048
|
+
var AsyncSerialQueue = class {
|
|
1049
|
+
tail = Promise.resolve();
|
|
1050
|
+
run(task) {
|
|
1051
|
+
const next = this.tail.then(task, task);
|
|
1052
|
+
this.tail = next.catch(() => {});
|
|
1053
|
+
return next;
|
|
1054
|
+
}
|
|
1055
|
+
};
|
|
1056
|
+
/** Real fs-backed JournalWriter. */
|
|
1057
|
+
var WiredJournalWriter = class {
|
|
1058
|
+
filePath;
|
|
1059
|
+
lifecycle;
|
|
1060
|
+
producer;
|
|
1061
|
+
protocolVersion;
|
|
1062
|
+
kimiVersion;
|
|
1063
|
+
now;
|
|
1064
|
+
queue = new AsyncSerialQueue();
|
|
1065
|
+
seq = 0;
|
|
1066
|
+
metadataWritten = false;
|
|
1067
|
+
/**
|
|
1068
|
+
* Tracks whether the parent directory entry for `filePath` has been
|
|
1069
|
+
* fsynced. Under POSIX semantics, `fh.sync()` flushes file *contents* but
|
|
1070
|
+
* does not guarantee the directory entry for a freshly created file has
|
|
1071
|
+
* been durably committed — a crash between the first append and the next
|
|
1072
|
+
* parent-directory fsync can leave the file's contents on disk with no
|
|
1073
|
+
* visible dirent. We fsync the parent directory once, after the first
|
|
255
1074
|
* successful write, and never again for the lifetime of this writer.
|
|
256
1075
|
*/
|
|
257
1076
|
directorySynced = false;
|
|
@@ -296,6 +1115,32 @@ var WiredJournalWriter = class {
|
|
|
296
1115
|
get pendingRecords() {
|
|
297
1116
|
return this.pending;
|
|
298
1117
|
}
|
|
1118
|
+
get sessionDir() {
|
|
1119
|
+
return dirname(this.filePath);
|
|
1120
|
+
}
|
|
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
|
+
}
|
|
299
1144
|
/**
|
|
300
1145
|
* Reset writer state after a compaction rotation (Slice 3.3 / M04).
|
|
301
1146
|
*
|
|
@@ -306,9 +1151,6 @@ var WiredJournalWriter = class {
|
|
|
306
1151
|
* mark it as written. The new file's directory entry was durably
|
|
307
1152
|
* committed by `rotateJournal`'s `syncDir`, so `directorySynced` is
|
|
308
1153
|
* also set.
|
|
309
|
-
*
|
|
310
|
-
* Only the compaction path (via `WiredJournalCapability`) should call
|
|
311
|
-
* this method — normal appends must never touch the seq counter.
|
|
312
1154
|
*/
|
|
313
1155
|
resetForRotation() {
|
|
314
1156
|
this.seq = 0;
|
|
@@ -453,7 +1295,7 @@ var WiredJournalWriter = class {
|
|
|
453
1295
|
}
|
|
454
1296
|
}
|
|
455
1297
|
async ensureDir() {
|
|
456
|
-
await mkdir(
|
|
1298
|
+
await mkdir(this.sessionDir, { recursive: true });
|
|
457
1299
|
}
|
|
458
1300
|
async writeAndSync(line) {
|
|
459
1301
|
const fh = await open(this.filePath, "a");
|
|
@@ -487,7 +1329,7 @@ var WiredJournalWriter = class {
|
|
|
487
1329
|
}
|
|
488
1330
|
/** Async parent-directory fsync. Tests may spy on this method by name. */
|
|
489
1331
|
async syncParentDir() {
|
|
490
|
-
await syncDir(
|
|
1332
|
+
await syncDir(this.sessionDir);
|
|
491
1333
|
}
|
|
492
1334
|
};
|
|
493
1335
|
/** No-op writer used by InMemory state implementations. */
|
|
@@ -546,1492 +1388,673 @@ var ContextStateMemory = class {
|
|
|
546
1388
|
get tokenCountWithPending() {
|
|
547
1389
|
return this._tokenCountWithPending;
|
|
548
1390
|
}
|
|
549
|
-
get thinkingLevel() {
|
|
550
|
-
return this._thinkingLevel;
|
|
551
|
-
}
|
|
552
|
-
get beforeStep() {
|
|
553
|
-
return this._beforeStep;
|
|
554
|
-
}
|
|
555
|
-
setBeforeStepHook(fn) {
|
|
556
|
-
this._beforeStep = fn;
|
|
557
|
-
}
|
|
558
|
-
getHistory() {
|
|
559
|
-
return this.history;
|
|
560
|
-
}
|
|
561
|
-
drainSteerMessages() {
|
|
562
|
-
const drained = this.steerBuffer;
|
|
563
|
-
this.steerBuffer = [];
|
|
564
|
-
return drained;
|
|
565
|
-
}
|
|
566
|
-
pushSteer(input) {
|
|
567
|
-
this.steerBuffer.push({ ...input });
|
|
568
|
-
}
|
|
569
|
-
appendUserContent(content) {
|
|
570
|
-
this.history.push({
|
|
571
|
-
role: "user",
|
|
572
|
-
content,
|
|
573
|
-
toolCalls: []
|
|
574
|
-
});
|
|
575
|
-
}
|
|
576
|
-
appendToolResult(toolCallId, content) {
|
|
577
|
-
this.history.push({
|
|
578
|
-
role: "tool",
|
|
579
|
-
content,
|
|
580
|
-
toolCalls: [],
|
|
581
|
-
toolCallId
|
|
582
|
-
});
|
|
583
|
-
}
|
|
584
|
-
openStep(uuid) {
|
|
585
|
-
const message = {
|
|
586
|
-
role: "assistant",
|
|
587
|
-
content: [],
|
|
588
|
-
toolCalls: []
|
|
589
|
-
};
|
|
590
|
-
this.history.push(message);
|
|
591
|
-
this.openSteps.set(uuid, message);
|
|
592
|
-
}
|
|
593
|
-
closeStep(uuid, usage) {
|
|
594
|
-
this.openSteps.delete(uuid);
|
|
595
|
-
if (usage !== void 0) this._tokenCountWithPending = usage.input_tokens + usage.output_tokens;
|
|
596
|
-
}
|
|
597
|
-
getOpenStep(uuid) {
|
|
598
|
-
return this.openSteps.get(uuid);
|
|
599
|
-
}
|
|
600
|
-
appendOpenStepContent(stepUuid, part) {
|
|
601
|
-
this.openSteps.get(stepUuid)?.content.push(part);
|
|
602
|
-
}
|
|
603
|
-
appendOpenStepToolCall(stepUuid, toolCall) {
|
|
604
|
-
this.openSteps.get(stepUuid)?.toolCalls.push(toolCall);
|
|
605
|
-
}
|
|
606
|
-
clearConversation() {
|
|
607
|
-
this.history = [];
|
|
608
|
-
this.openSteps.clear();
|
|
609
|
-
this._tokenCountWithPending = 0;
|
|
610
|
-
}
|
|
611
|
-
setSystemPrompt(systemPrompt) {
|
|
612
|
-
this._systemPrompt = systemPrompt;
|
|
613
|
-
}
|
|
614
|
-
setModel(model) {
|
|
615
|
-
this._model = model;
|
|
616
|
-
}
|
|
617
|
-
setThinkingLevel(level) {
|
|
618
|
-
this._thinkingLevel = level;
|
|
619
|
-
}
|
|
620
|
-
applyToolsChanged(operation, tools) {
|
|
621
|
-
if (operation === "set_active") {
|
|
622
|
-
this._activeTools = new Set(tools);
|
|
623
|
-
return;
|
|
624
|
-
}
|
|
625
|
-
if (operation === "register") {
|
|
626
|
-
for (const tool of tools) this._activeTools.add(tool);
|
|
627
|
-
return;
|
|
628
|
-
}
|
|
629
|
-
for (const tool of tools) this._activeTools.delete(tool);
|
|
630
|
-
}
|
|
631
|
-
resetToSummary(summary) {
|
|
632
|
-
this.history = [{
|
|
633
|
-
role: "assistant",
|
|
634
|
-
content: [{
|
|
635
|
-
type: "text",
|
|
636
|
-
text: summary.summary
|
|
637
|
-
}],
|
|
638
|
-
toolCalls: []
|
|
639
|
-
}];
|
|
640
|
-
this.openSteps.clear();
|
|
641
|
-
this._tokenCountWithPending = summary.postCompactTokens;
|
|
642
|
-
}
|
|
643
|
-
};
|
|
644
|
-
//#endregion
|
|
645
|
-
//#region ../../packages/kimi-core/src/storage/state/message-mirror.ts
|
|
646
|
-
function userInputToContentParts(input) {
|
|
647
|
-
if (input.parts !== void 0 && input.parts.some((part) => part.type !== "text") && input.parts !== void 0) return input.parts.map(userInputPartToContentPart);
|
|
648
|
-
return [{
|
|
649
|
-
type: "text",
|
|
650
|
-
text: input.parts !== void 0 ? input.parts.filter((part) => part.type === "text").map((part) => part.text).join("") : input.text
|
|
651
|
-
}];
|
|
652
|
-
}
|
|
653
|
-
function toolResultOutputToContentParts(output) {
|
|
654
|
-
if (isContentPartArray$1(output)) return output.map((part) => cloneContentPart$1(part));
|
|
655
|
-
return [{
|
|
656
|
-
type: "text",
|
|
657
|
-
text: typeof output === "string" ? output : JSON.stringify(output)
|
|
658
|
-
}];
|
|
659
|
-
}
|
|
660
|
-
function atomicPartToContentPart(part) {
|
|
661
|
-
if (part.kind === "text") return {
|
|
662
|
-
type: "text",
|
|
663
|
-
text: part.text
|
|
664
|
-
};
|
|
665
|
-
const thinkPart = {
|
|
666
|
-
type: "think",
|
|
667
|
-
think: part.think
|
|
668
|
-
};
|
|
669
|
-
if (part.encrypted !== void 0) thinkPart.encrypted = part.encrypted;
|
|
670
|
-
return thinkPart;
|
|
671
|
-
}
|
|
672
|
-
function sanitizeToolCallData(data) {
|
|
673
|
-
return {
|
|
674
|
-
tool_call_id: data.tool_call_id,
|
|
675
|
-
tool_name: data.tool_name,
|
|
676
|
-
args: data.args,
|
|
677
|
-
...data.activity_description !== void 0 ? { activity_description: data.activity_description } : {},
|
|
678
|
-
user_facing_name: data.user_facing_name,
|
|
679
|
-
input_display: data.input_display
|
|
680
|
-
};
|
|
681
|
-
}
|
|
682
|
-
function toolCallDataToKosongToolCall(data) {
|
|
683
|
-
return {
|
|
684
|
-
type: "function",
|
|
685
|
-
id: data.tool_call_id,
|
|
686
|
-
function: {
|
|
687
|
-
name: data.tool_name,
|
|
688
|
-
arguments: data.args === void 0 ? null : JSON.stringify(data.args)
|
|
689
|
-
}
|
|
690
|
-
};
|
|
691
|
-
}
|
|
692
|
-
function userInputPartToContentPart(part) {
|
|
693
|
-
if (part.type === "text") return {
|
|
694
|
-
type: "text",
|
|
695
|
-
text: part.text
|
|
696
|
-
};
|
|
697
|
-
if (part.type === "image_url") return {
|
|
698
|
-
type: "image_url",
|
|
699
|
-
imageUrl: part.image_url
|
|
700
|
-
};
|
|
701
|
-
return {
|
|
702
|
-
type: "video_url",
|
|
703
|
-
videoUrl: part.video_url
|
|
704
|
-
};
|
|
705
|
-
}
|
|
706
|
-
function isContentPart$2(value) {
|
|
707
|
-
if (typeof value !== "object" || value === null) return false;
|
|
708
|
-
const part = value;
|
|
709
|
-
return part.type === "text" || part.type === "think" || part.type === "image_url" || part.type === "audio_url" || part.type === "video_url";
|
|
710
|
-
}
|
|
711
|
-
function isContentPartArray$1(value) {
|
|
712
|
-
return Array.isArray(value) && value.every((part) => isContentPart$2(part));
|
|
713
|
-
}
|
|
714
|
-
function cloneContentPart$1(part) {
|
|
715
|
-
return { ...part };
|
|
716
|
-
}
|
|
717
|
-
//#endregion
|
|
718
|
-
//#region ../../packages/kimi-core/src/storage/state/notification-xml.ts
|
|
719
|
-
/**
|
|
720
|
-
* Notification XML rendering — shared between ContextState and
|
|
721
|
-
* ConversationProjector so both produce byte-identical chat-history
|
|
722
|
-
* injection text.
|
|
723
|
-
*
|
|
724
|
-
* Output shape:
|
|
725
|
-
* <notification id="..." category="..." type="..." source_kind="..." source_id="...">
|
|
726
|
-
* Title: ...
|
|
727
|
-
* Severity: ...
|
|
728
|
-
* <body>
|
|
729
|
-
* <task-notification> (only when source_kind === 'background_task' and tail_output is non-empty)
|
|
730
|
-
* <truncated tail>
|
|
731
|
-
* </task-notification>
|
|
732
|
-
* </notification>
|
|
733
|
-
*
|
|
734
|
-
* The opening-tag names (`<notification ` / `<task-notification>`) are
|
|
735
|
-
* load-bearing for the projector's `mergeAdjacentUserMessages` detector
|
|
736
|
-
* — rename requires updating the detector too.
|
|
737
|
-
*/
|
|
738
|
-
function renderNotificationXml(data) {
|
|
739
|
-
const id = stringAttr(data["id"], "unknown");
|
|
740
|
-
const category = stringAttr(data["category"], "unknown");
|
|
741
|
-
const type = stringAttr(data["type"], "unknown");
|
|
742
|
-
const sourceKind = stringAttr(data["source_kind"], "unknown");
|
|
743
|
-
const sourceId = stringAttr(data["source_id"], "unknown");
|
|
744
|
-
const title = typeof data["title"] === "string" ? data["title"] : "";
|
|
745
|
-
const severity = typeof data["severity"] === "string" ? data["severity"] : "";
|
|
746
|
-
const body = typeof data["body"] === "string" ? data["body"] : "";
|
|
747
|
-
const lines = [`<notification id="${id}" category="${category}" type="${type}" source_kind="${sourceKind}" source_id="${sourceId}">`];
|
|
748
|
-
if (title.length > 0) lines.push(`Title: ${title}`);
|
|
749
|
-
if (severity.length > 0) lines.push(`Severity: ${severity}`);
|
|
750
|
-
if (body.length > 0) lines.push(body);
|
|
751
|
-
if (data["source_kind"] === "background_task") {
|
|
752
|
-
const tailRaw = typeof data["tail_output"] === "string" ? data["tail_output"] : "";
|
|
753
|
-
if (tailRaw.length > 0) {
|
|
754
|
-
const truncated = truncateTailOutput(tailRaw, 20, 3e3);
|
|
755
|
-
lines.push("<task-notification>");
|
|
756
|
-
lines.push(truncated);
|
|
757
|
-
lines.push("</task-notification>");
|
|
758
|
-
}
|
|
759
|
-
}
|
|
760
|
-
lines.push("</notification>");
|
|
761
|
-
return lines.join("\n");
|
|
762
|
-
}
|
|
763
|
-
/**
|
|
764
|
-
* Truncate tail output to at most `maxLines` lines and `maxChars`
|
|
765
|
-
* characters. Takes the *last* N lines, then trims from the front if
|
|
766
|
-
* the character budget is exceeded.
|
|
767
|
-
*/
|
|
768
|
-
function truncateTailOutput(raw, maxLines, maxChars) {
|
|
769
|
-
const allLines = raw.split("\n");
|
|
770
|
-
let result = (allLines.length > maxLines ? allLines.slice(-maxLines) : allLines).join("\n");
|
|
771
|
-
if (result.length > maxChars) result = result.slice(-maxChars);
|
|
772
|
-
return result;
|
|
773
|
-
}
|
|
774
|
-
function stringAttr(value, fallback) {
|
|
775
|
-
if (typeof value !== "string" || value.length === 0) return fallback;
|
|
776
|
-
return value.replaceAll("&", "&").replaceAll("\"", """);
|
|
777
|
-
}
|
|
778
|
-
//#endregion
|
|
779
|
-
//#region ../../packages/kimi-core/src/storage/state/projector.ts
|
|
780
|
-
/**
|
|
781
|
-
* Pure read-side projector for provider-visible messages. It passes through
|
|
782
|
-
* persisted history, merges adjacent user messages, filters incomplete
|
|
783
|
-
* assistant placeholders, and injects ephemeral annotations without writing
|
|
784
|
-
* anything back to the journal.
|
|
785
|
-
*
|
|
786
|
-
* The system prompt is not injected here; it is forwarded separately through
|
|
787
|
-
* provider chat parameters to avoid duplicate system messages.
|
|
788
|
-
*/
|
|
789
|
-
var DefaultConversationProjector = class {
|
|
790
|
-
project(snapshot, ephemeralInjectionsOrOptions, options) {
|
|
791
|
-
const ephemeralInjections = Array.isArray(ephemeralInjectionsOrOptions) ? ephemeralInjectionsOrOptions : [];
|
|
792
|
-
const merged = mergeAdjacentUserMessages(snapshot.history.filter((m) => m.partial !== true && !(m.role === "assistant" && m.content.length === 0 && m.toolCalls.length === 0)));
|
|
793
|
-
return [...ephemeralInjections.length === 0 ? [] : ephemeralInjections.map((injection) => renderInjection(injection)), ...merged];
|
|
794
|
-
}
|
|
795
|
-
};
|
|
796
|
-
/**
|
|
797
|
-
* Render an EphemeralInjection into a synthetic user message. System
|
|
798
|
-
* reminders and pending notifications use XML wrappers so the model can
|
|
799
|
-
* distinguish host annotations from genuine user text. `memory_recall`
|
|
800
|
-
* stays as free text.
|
|
801
|
-
*
|
|
802
|
-
* The merge-guard logic downstream (`mergeAdjacentUserMessages`) uses
|
|
803
|
-
* the `<notification ` / `<system-reminder>` opening tag to detect
|
|
804
|
-
* these messages, so the exact tag names are load-bearing for
|
|
805
|
-
* projector correctness — do not rename without also updating
|
|
806
|
-
* `isInjectionUserMessage` below.
|
|
807
|
-
*/
|
|
808
|
-
function renderInjection(injection) {
|
|
809
|
-
return {
|
|
810
|
-
role: "user",
|
|
811
|
-
content: [{
|
|
812
|
-
type: "text",
|
|
813
|
-
text: renderInjectionText(injection)
|
|
814
|
-
}],
|
|
815
|
-
toolCalls: []
|
|
816
|
-
};
|
|
817
|
-
}
|
|
818
|
-
function renderInjectionText(injection) {
|
|
819
|
-
const { kind, content } = injection;
|
|
820
|
-
if (kind === "pending_notification") {
|
|
821
|
-
if (typeof content === "string") return `<notification>\n${content}\n</notification>`;
|
|
822
|
-
return renderNotificationXml(content);
|
|
823
|
-
}
|
|
824
|
-
if (kind === "system_reminder") return `<system-reminder>\n${typeof content === "string" ? content : JSON.stringify(content)}\n</system-reminder>`;
|
|
825
|
-
return typeof content === "string" ? content : JSON.stringify(content);
|
|
826
|
-
}
|
|
827
|
-
/**
|
|
828
|
-
* Detect whether a user message was produced by the ephemeral injection
|
|
829
|
-
* pipeline (system_reminder or notification XML tag). Such messages
|
|
830
|
-
* must never be merged with an adjacent real user turn — doing so would
|
|
831
|
-
* smear the injection's XML wrapper into the user's actual prompt and
|
|
832
|
-
* confuse the LLM about where the system annotation ends.
|
|
833
|
-
*
|
|
834
|
-
*/
|
|
835
|
-
function isInjectionUserMessage(message) {
|
|
836
|
-
if (message.role !== "user") return false;
|
|
837
|
-
const trimmed = extractTextOnly(message).trimStart();
|
|
838
|
-
if (trimmed.startsWith("<notification ")) return true;
|
|
839
|
-
if (trimmed.startsWith("<system-reminder>")) return true;
|
|
840
|
-
return false;
|
|
841
|
-
}
|
|
842
|
-
function mergeAdjacentUserMessages(history) {
|
|
843
|
-
const out = [];
|
|
844
|
-
for (const message of history) {
|
|
845
|
-
const previous = out.at(-1);
|
|
846
|
-
if (message.role === "user" && previous !== void 0 && previous.role === "user" && !isInjectionUserMessage(message) && !isInjectionUserMessage(previous)) {
|
|
847
|
-
out[out.length - 1] = mergeTwoUserMessages(previous, message);
|
|
848
|
-
continue;
|
|
849
|
-
}
|
|
850
|
-
out.push(cloneMessage(message));
|
|
851
|
-
}
|
|
852
|
-
return out;
|
|
853
|
-
}
|
|
854
|
-
function mergeTwoUserMessages(a, b) {
|
|
855
|
-
const aText = extractTextOnly(a);
|
|
856
|
-
const bText = extractTextOnly(b);
|
|
857
|
-
const nonTextParts = [...a.content.filter((p) => p.type !== "text"), ...b.content.filter((p) => p.type !== "text")];
|
|
858
|
-
return {
|
|
859
|
-
role: "user",
|
|
860
|
-
content: [{
|
|
861
|
-
type: "text",
|
|
862
|
-
text: `${aText}\n\n${bText}`
|
|
863
|
-
}, ...nonTextParts],
|
|
864
|
-
toolCalls: []
|
|
865
|
-
};
|
|
866
|
-
}
|
|
867
|
-
function extractTextOnly(message) {
|
|
868
|
-
return message.content.filter((p) => p.type === "text").map((p) => p.text).join("");
|
|
869
|
-
}
|
|
870
|
-
function cloneMessage(message) {
|
|
871
|
-
return {
|
|
872
|
-
role: message.role,
|
|
873
|
-
name: message.name,
|
|
874
|
-
content: message.content.map((p) => ({ ...p })),
|
|
875
|
-
toolCalls: message.toolCalls.map((tc) => ({ ...tc })),
|
|
876
|
-
toolCallId: message.toolCallId,
|
|
877
|
-
partial: message.partial
|
|
878
|
-
};
|
|
879
|
-
}
|
|
880
|
-
//#endregion
|
|
881
|
-
//#region ../../packages/kimi-core/src/storage/state/record-builders.ts
|
|
882
|
-
function buildUserMessageRecord(turnId, input) {
|
|
883
|
-
return {
|
|
884
|
-
type: "user_message",
|
|
885
|
-
turn_id: turnId,
|
|
886
|
-
content: input.parts !== void 0 ? input.parts : input.text
|
|
887
|
-
};
|
|
888
|
-
}
|
|
889
|
-
function buildToolResultRecord(turnId, parentUuid, toolCallId, normalisedOutput, result) {
|
|
890
|
-
return {
|
|
891
|
-
type: "tool_result",
|
|
892
|
-
turn_id: turnId,
|
|
893
|
-
tool_call_id: toolCallId,
|
|
894
|
-
output: normalisedOutput,
|
|
895
|
-
parent_uuid: parentUuid,
|
|
896
|
-
is_error: result.isError,
|
|
897
|
-
synthetic: result.synthetic
|
|
898
|
-
};
|
|
899
|
-
}
|
|
900
|
-
function buildStepBeginRecord(input) {
|
|
901
|
-
return {
|
|
902
|
-
type: "step_begin",
|
|
903
|
-
uuid: input.uuid,
|
|
904
|
-
turn_id: input.turnId,
|
|
905
|
-
step: input.step
|
|
906
|
-
};
|
|
907
|
-
}
|
|
908
|
-
function buildStepEndRecord(input) {
|
|
909
|
-
return {
|
|
910
|
-
type: "step_end",
|
|
911
|
-
uuid: input.uuid,
|
|
912
|
-
turn_id: input.turnId,
|
|
913
|
-
step: input.step,
|
|
914
|
-
usage: input.usage,
|
|
915
|
-
finish_reason: input.finishReason
|
|
916
|
-
};
|
|
917
|
-
}
|
|
918
|
-
function buildContentPartRecord(input) {
|
|
919
|
-
return {
|
|
920
|
-
type: "content_part",
|
|
921
|
-
uuid: input.uuid,
|
|
922
|
-
turn_id: input.turnId,
|
|
923
|
-
step: input.step,
|
|
924
|
-
step_uuid: input.stepUuid,
|
|
925
|
-
role: "assistant",
|
|
926
|
-
part: input.part.kind === "text" ? {
|
|
927
|
-
kind: "text",
|
|
928
|
-
text: input.part.text
|
|
929
|
-
} : input.part.encrypted !== void 0 ? {
|
|
930
|
-
kind: "think",
|
|
931
|
-
think: input.part.think,
|
|
932
|
-
encrypted: input.part.encrypted
|
|
933
|
-
} : {
|
|
934
|
-
kind: "think",
|
|
935
|
-
think: input.part.think
|
|
936
|
-
}
|
|
937
|
-
};
|
|
938
|
-
}
|
|
939
|
-
function buildToolCallRecord(input, data) {
|
|
940
|
-
return {
|
|
941
|
-
type: "tool_call",
|
|
942
|
-
uuid: input.uuid,
|
|
943
|
-
turn_id: input.turnId,
|
|
944
|
-
step: input.step,
|
|
945
|
-
step_uuid: input.stepUuid,
|
|
946
|
-
data
|
|
947
|
-
};
|
|
948
|
-
}
|
|
949
|
-
function buildNotificationRecord(data) {
|
|
950
|
-
return {
|
|
951
|
-
type: "notification",
|
|
952
|
-
data
|
|
953
|
-
};
|
|
954
|
-
}
|
|
955
|
-
function buildSystemReminderRecord(data) {
|
|
956
|
-
return {
|
|
957
|
-
type: "system_reminder",
|
|
958
|
-
content: data.content
|
|
959
|
-
};
|
|
960
|
-
}
|
|
961
|
-
function buildClearRecord() {
|
|
962
|
-
return { type: "context_cleared" };
|
|
963
|
-
}
|
|
964
|
-
function buildConfigChangeRecord(event) {
|
|
965
|
-
switch (event.type) {
|
|
966
|
-
case "system_prompt_changed": return {
|
|
967
|
-
type: "system_prompt_changed",
|
|
968
|
-
new_prompt: event.new_prompt
|
|
969
|
-
};
|
|
970
|
-
case "tools_changed": return {
|
|
971
|
-
type: "tools_changed",
|
|
972
|
-
operation: event.operation,
|
|
973
|
-
tools: event.tools
|
|
974
|
-
};
|
|
975
|
-
}
|
|
976
|
-
}
|
|
977
|
-
function buildCompactionRecord(summary) {
|
|
978
|
-
return {
|
|
979
|
-
type: "compaction",
|
|
980
|
-
summary: summary.summary,
|
|
981
|
-
compacted_range: {
|
|
982
|
-
from_turn: summary.compactedRange.fromTurn,
|
|
983
|
-
to_turn: summary.compactedRange.toTurn,
|
|
984
|
-
message_count: summary.compactedRange.messageCount
|
|
985
|
-
},
|
|
986
|
-
pre_compact_tokens: summary.preCompactTokens,
|
|
987
|
-
post_compact_tokens: summary.postCompactTokens,
|
|
988
|
-
trigger: summary.trigger,
|
|
989
|
-
archive_file: summary.archiveFile
|
|
990
|
-
};
|
|
991
|
-
}
|
|
992
|
-
//#endregion
|
|
993
|
-
//#region ../../packages/kimi-core/src/storage/state/context-state.ts
|
|
994
|
-
/**
|
|
995
|
-
* Core ContextState logic. Both `WiredContextState` and
|
|
996
|
-
* `InMemoryContextState` are thin wrappers that differ only in the journal
|
|
997
|
-
* writer they supply and the constructor shape they expose.
|
|
998
|
-
*
|
|
999
|
-
* WAL-then-mirror atomicity:
|
|
1000
|
-
* 1. build the WireRecord
|
|
1001
|
-
* 2. await journalWriter.append(...)
|
|
1002
|
-
* 3. on success only, update the in-memory projection
|
|
1003
|
-
*
|
|
1004
|
-
* If step 2 throws, the in-memory state is unchanged.
|
|
1005
|
-
*/
|
|
1006
|
-
var BaseContextState = class {
|
|
1007
|
-
journalWriter;
|
|
1008
|
-
projector;
|
|
1009
|
-
currentTurnId;
|
|
1010
|
-
memory;
|
|
1011
|
-
_broken = false;
|
|
1012
|
-
_brokenError;
|
|
1013
|
-
/**
|
|
1014
|
-
* Marks all future write entry points as failed. Read methods remain
|
|
1015
|
-
* callable so callers can still inspect the last valid in-memory state.
|
|
1016
|
-
*/
|
|
1017
|
-
markBroken(error) {
|
|
1018
|
-
if (this._broken) return;
|
|
1019
|
-
this._broken = true;
|
|
1020
|
-
this._brokenError = error;
|
|
1021
|
-
}
|
|
1022
|
-
assertNotBroken() {
|
|
1023
|
-
if (this._broken) throw new ContextStateBrokenError("ContextState is broken due to a prior persist error", this._brokenError);
|
|
1024
|
-
}
|
|
1025
|
-
constructor(opts) {
|
|
1026
|
-
this.journalWriter = opts.journalWriter;
|
|
1027
|
-
this.projector = opts.projector ?? new DefaultConversationProjector();
|
|
1028
|
-
this.currentTurnId = opts.currentTurnId;
|
|
1029
|
-
this.memory = new ContextStateMemory(opts);
|
|
1030
|
-
}
|
|
1031
|
-
get model() {
|
|
1032
|
-
return this.memory.model;
|
|
1033
|
-
}
|
|
1034
|
-
get systemPrompt() {
|
|
1035
|
-
return this.memory.systemPrompt;
|
|
1036
|
-
}
|
|
1037
|
-
get activeTools() {
|
|
1038
|
-
return this.memory.activeTools;
|
|
1039
|
-
}
|
|
1040
|
-
get tokenCountWithPending() {
|
|
1041
|
-
return this.memory.tokenCountWithPending;
|
|
1042
|
-
}
|
|
1043
|
-
get thinkingLevel() {
|
|
1044
|
-
return this.memory.thinkingLevel;
|
|
1045
|
-
}
|
|
1046
|
-
get beforeStep() {
|
|
1047
|
-
return this.memory.beforeStep;
|
|
1048
|
-
}
|
|
1049
|
-
buildMessages() {
|
|
1050
|
-
return this.projector.project({
|
|
1051
|
-
history: this.memory.getHistory(),
|
|
1052
|
-
systemPrompt: this.memory.systemPrompt,
|
|
1053
|
-
model: this.memory.model,
|
|
1054
|
-
activeTools: this.memory.activeTools
|
|
1055
|
-
});
|
|
1056
|
-
}
|
|
1057
|
-
async applySteerMessages() {
|
|
1058
|
-
const steers = this.memory.drainSteerMessages();
|
|
1059
|
-
if (steers.length === 0) return false;
|
|
1060
|
-
this.assertNotBroken();
|
|
1061
|
-
for (const steer of steers) await this.appendUserMessage(steer);
|
|
1062
|
-
return true;
|
|
1063
|
-
}
|
|
1064
|
-
pushSteer(input) {
|
|
1065
|
-
this.memory.pushSteer(input);
|
|
1066
|
-
}
|
|
1067
|
-
setBeforeStepHook(fn) {
|
|
1068
|
-
this.memory.setBeforeStepHook(fn);
|
|
1069
|
-
}
|
|
1070
|
-
getHistory() {
|
|
1071
|
-
return this.memory.getHistory();
|
|
1072
|
-
}
|
|
1073
|
-
async appendUserMessage(input, turnIdOverride) {
|
|
1074
|
-
this.assertNotBroken();
|
|
1075
|
-
const turnId = turnIdOverride ?? this.currentTurnId();
|
|
1076
|
-
await this.journalWriter.append(buildUserMessageRecord(turnId, input));
|
|
1077
|
-
this.memory.appendUserContent(userInputToContentParts(input));
|
|
1078
|
-
}
|
|
1079
|
-
async appendToolResult(parentUuid, toolCallId, result) {
|
|
1080
|
-
this.assertNotBroken();
|
|
1081
|
-
const normalisedOutput = result.output === void 0 ? null : result.output;
|
|
1082
|
-
const turnId = this.currentTurnId();
|
|
1083
|
-
await this.journalWriter.append(buildToolResultRecord(turnId, parentUuid, toolCallId, normalisedOutput, result));
|
|
1084
|
-
this.memory.appendToolResult(toolCallId, toolResultOutputToContentParts(normalisedOutput));
|
|
1085
|
-
}
|
|
1086
|
-
async appendStepBegin(input) {
|
|
1087
|
-
this.assertNotBroken();
|
|
1088
|
-
await this.journalWriter.append(buildStepBeginRecord(input));
|
|
1089
|
-
this.memory.openStep(input.uuid);
|
|
1090
|
-
}
|
|
1091
|
-
async appendStepEnd(input) {
|
|
1092
|
-
this.assertNotBroken();
|
|
1093
|
-
await this.journalWriter.append(buildStepEndRecord(input));
|
|
1094
|
-
this.memory.closeStep(input.uuid, input.usage);
|
|
1095
|
-
}
|
|
1096
|
-
async appendContentPart(input) {
|
|
1097
|
-
this.assertNotBroken();
|
|
1098
|
-
if (this.memory.getOpenStep(input.stepUuid) === void 0) throw new Error(`appendContentPart: unknown stepUuid '${input.stepUuid}' (no open step_begin)`);
|
|
1099
|
-
await this.journalWriter.append(buildContentPartRecord(input));
|
|
1100
|
-
this.memory.appendOpenStepContent(input.stepUuid, atomicPartToContentPart(input.part));
|
|
1101
|
-
}
|
|
1102
|
-
async appendToolCall(input) {
|
|
1103
|
-
this.assertNotBroken();
|
|
1104
|
-
if (this.memory.getOpenStep(input.stepUuid) === void 0) throw new Error(`appendToolCall: unknown stepUuid '${input.stepUuid}' (no open step_begin)`);
|
|
1105
|
-
const data = sanitizeToolCallData(input.data);
|
|
1106
|
-
await this.journalWriter.append(buildToolCallRecord(input, data));
|
|
1107
|
-
this.memory.appendOpenStepToolCall(input.stepUuid, toolCallDataToKosongToolCall(input.data));
|
|
1108
|
-
}
|
|
1109
|
-
async appendNotification(data) {
|
|
1110
|
-
this.assertNotBroken();
|
|
1111
|
-
await this.journalWriter.append(buildNotificationRecord(data));
|
|
1112
|
-
const text = renderNotificationXml(data);
|
|
1113
|
-
this.memory.appendUserContent([{
|
|
1114
|
-
type: "text",
|
|
1115
|
-
text
|
|
1116
|
-
}]);
|
|
1117
|
-
}
|
|
1118
|
-
async appendSystemReminder(data) {
|
|
1119
|
-
this.assertNotBroken();
|
|
1120
|
-
await this.journalWriter.append(buildSystemReminderRecord(data));
|
|
1121
|
-
const text = `<system-reminder>\n${data.content}\n</system-reminder>`;
|
|
1122
|
-
this.memory.appendUserContent([{
|
|
1123
|
-
type: "text",
|
|
1124
|
-
text
|
|
1125
|
-
}]);
|
|
1126
|
-
}
|
|
1127
|
-
async clear() {
|
|
1128
|
-
this.assertNotBroken();
|
|
1129
|
-
await this.journalWriter.append(buildClearRecord());
|
|
1130
|
-
this.memory.clearConversation();
|
|
1131
|
-
}
|
|
1132
|
-
async applyConfigChange(event) {
|
|
1133
|
-
this.assertNotBroken();
|
|
1134
|
-
switch (event.type) {
|
|
1135
|
-
case "system_prompt_changed":
|
|
1136
|
-
await this.journalWriter.append(buildConfigChangeRecord(event));
|
|
1137
|
-
this.memory.setSystemPrompt(event.new_prompt);
|
|
1138
|
-
return;
|
|
1139
|
-
case "tools_changed":
|
|
1140
|
-
await this.journalWriter.append(buildConfigChangeRecord(event));
|
|
1141
|
-
this.memory.applyToolsChanged(event.operation, event.tools);
|
|
1142
|
-
}
|
|
1143
|
-
}
|
|
1144
|
-
setModel(model) {
|
|
1145
|
-
this.memory.setModel(model);
|
|
1146
|
-
}
|
|
1147
|
-
setThinkingLevel(level) {
|
|
1148
|
-
this.memory.setThinkingLevel(level);
|
|
1149
|
-
}
|
|
1150
|
-
async resetToSummary(summary) {
|
|
1151
|
-
this.assertNotBroken();
|
|
1152
|
-
await this.journalWriter.append(buildCompactionRecord(summary));
|
|
1153
|
-
this.memory.resetToSummary(summary);
|
|
1154
|
-
}
|
|
1155
|
-
};
|
|
1156
|
-
var WiredContextState = class extends BaseContextState {};
|
|
1157
|
-
var InMemoryContextState = class extends BaseContextState {
|
|
1158
|
-
constructor(opts) {
|
|
1159
|
-
super({
|
|
1160
|
-
...opts,
|
|
1161
|
-
currentTurnId: opts.currentTurnId ?? (() => "embedded"),
|
|
1162
|
-
journalWriter: new NoopJournalWriter()
|
|
1163
|
-
});
|
|
1164
|
-
}
|
|
1165
|
-
};
|
|
1166
|
-
//#endregion
|
|
1167
|
-
//#region ../../packages/kimi-core/src/storage/state/session-journal.ts
|
|
1168
|
-
/**
|
|
1169
|
-
* Production `SessionJournal` implementation — delegates every append to the
|
|
1170
|
-
* shared `JournalWriter`, which in turn serialises to wire.jsonl.
|
|
1171
|
-
*
|
|
1172
|
-
* Does not mutate any conversation projection state; management records do
|
|
1173
|
-
* not change buildMessages().
|
|
1174
|
-
*/
|
|
1175
|
-
var WiredSessionJournalImpl = class {
|
|
1176
|
-
constructor(journalWriter) {
|
|
1177
|
-
this.journalWriter = journalWriter;
|
|
1178
|
-
}
|
|
1179
|
-
async appendTurnBegin(data) {
|
|
1180
|
-
await this.journalWriter.append(data);
|
|
1181
|
-
}
|
|
1182
|
-
async appendTurnEnd(data) {
|
|
1183
|
-
await this.journalWriter.append(data);
|
|
1184
|
-
}
|
|
1185
|
-
async appendSkillInvoked(data) {
|
|
1186
|
-
await this.journalWriter.append(data);
|
|
1187
|
-
}
|
|
1188
|
-
async appendSkillCompleted(data) {
|
|
1189
|
-
await this.journalWriter.append(data);
|
|
1190
|
-
}
|
|
1191
|
-
async appendApprovalRequest(data) {
|
|
1192
|
-
await this.journalWriter.append(data);
|
|
1193
|
-
}
|
|
1194
|
-
async appendApprovalResponse(data) {
|
|
1195
|
-
await this.journalWriter.append(data);
|
|
1196
|
-
}
|
|
1197
|
-
async appendTeamMail(data) {
|
|
1198
|
-
await this.journalWriter.append(data);
|
|
1199
|
-
}
|
|
1200
|
-
async appendToolDenied(data) {
|
|
1201
|
-
await this.journalWriter.append(data);
|
|
1202
|
-
}
|
|
1203
|
-
async appendSubagentSpawned(data) {
|
|
1204
|
-
await this.journalWriter.append(data);
|
|
1205
|
-
}
|
|
1206
|
-
async appendSubagentCompleted(data) {
|
|
1207
|
-
await this.journalWriter.append(data);
|
|
1208
|
-
}
|
|
1209
|
-
async appendSubagentFailed(data) {
|
|
1210
|
-
await this.journalWriter.append(data);
|
|
1211
|
-
}
|
|
1212
|
-
async appendOwnershipChanged(data) {
|
|
1213
|
-
await this.journalWriter.append(data);
|
|
1214
|
-
}
|
|
1215
|
-
};
|
|
1216
|
-
//#endregion
|
|
1217
|
-
//#region ../../packages/kimi-core/src/storage/schema/records.ts
|
|
1218
|
-
/**
|
|
1219
|
-
* Fallback producer identity used when the host hasn't supplied one.
|
|
1220
|
-
* Version marker makes unset state easy to spot in field reports.
|
|
1221
|
-
*/
|
|
1222
|
-
const DEFAULT_PRODUCER = {
|
|
1223
|
-
kind: "typescript",
|
|
1224
|
-
name: "@moonshot-ai/core",
|
|
1225
|
-
version: "0.0.0-unset"
|
|
1226
|
-
};
|
|
1227
|
-
const agentTypeEnum = z.enum([
|
|
1228
|
-
"main",
|
|
1229
|
-
"sub",
|
|
1230
|
-
"independent"
|
|
1231
|
-
]);
|
|
1232
|
-
const _rawWireProducerSchema = z.object({
|
|
1233
|
-
kind: z.enum(["python", "typescript"]),
|
|
1234
|
-
name: z.string(),
|
|
1235
|
-
version: z.string()
|
|
1236
|
-
});
|
|
1237
|
-
const WireFileMetadataSchema = z.object({
|
|
1238
|
-
type: z.literal("metadata"),
|
|
1239
|
-
protocol_version: z.string(),
|
|
1240
|
-
created_at: z.number(),
|
|
1241
|
-
kimi_version: z.string().optional(),
|
|
1242
|
-
producer: _rawWireProducerSchema.optional()
|
|
1243
|
-
});
|
|
1244
|
-
const _rawTurnBeginRecordSchema = z.object({
|
|
1245
|
-
type: z.literal("turn_begin"),
|
|
1246
|
-
seq: z.number(),
|
|
1247
|
-
time: z.number(),
|
|
1248
|
-
turn_id: z.string(),
|
|
1249
|
-
agent_type: agentTypeEnum,
|
|
1250
|
-
user_input: z.string().optional(),
|
|
1251
|
-
input_kind: z.enum(["user", "system_trigger"]),
|
|
1252
|
-
trigger_source: z.string().optional()
|
|
1253
|
-
});
|
|
1254
|
-
const _rawTurnEndRecordSchema = z.object({
|
|
1255
|
-
type: z.literal("turn_end"),
|
|
1256
|
-
seq: z.number(),
|
|
1257
|
-
time: z.number(),
|
|
1258
|
-
turn_id: z.string(),
|
|
1259
|
-
agent_type: agentTypeEnum,
|
|
1260
|
-
success: z.boolean(),
|
|
1261
|
-
reason: z.enum([
|
|
1262
|
-
"done",
|
|
1263
|
-
"cancelled",
|
|
1264
|
-
"error",
|
|
1265
|
-
"interrupted"
|
|
1266
|
-
]),
|
|
1267
|
-
usage: z.object({
|
|
1268
|
-
input_tokens: z.number(),
|
|
1269
|
-
output_tokens: z.number(),
|
|
1270
|
-
cache_read_tokens: z.number().optional(),
|
|
1271
|
-
cache_write_tokens: z.number().optional(),
|
|
1272
|
-
cost_usd: z.number().optional()
|
|
1273
|
-
}).optional(),
|
|
1274
|
-
synthetic: z.boolean().optional()
|
|
1275
|
-
});
|
|
1276
|
-
const _userInputPartSchema = z.discriminatedUnion("type", [
|
|
1277
|
-
z.object({
|
|
1278
|
-
type: z.literal("text"),
|
|
1279
|
-
text: z.string()
|
|
1280
|
-
}),
|
|
1281
|
-
z.object({
|
|
1282
|
-
type: z.literal("image_url"),
|
|
1283
|
-
image_url: z.object({ url: z.string() })
|
|
1284
|
-
}),
|
|
1285
|
-
z.object({
|
|
1286
|
-
type: z.literal("video_url"),
|
|
1287
|
-
video_url: z.object({ url: z.string() })
|
|
1288
|
-
})
|
|
1289
|
-
]);
|
|
1290
|
-
const _rawUserMessageRecordSchema = z.object({
|
|
1291
|
-
type: z.literal("user_message"),
|
|
1292
|
-
seq: z.number(),
|
|
1293
|
-
time: z.number(),
|
|
1294
|
-
turn_id: z.string(),
|
|
1295
|
-
content: z.union([z.string(), z.array(_userInputPartSchema).readonly()]),
|
|
1296
|
-
uuid: z.string().optional()
|
|
1297
|
-
});
|
|
1298
|
-
const _rawToolResultRecordSchema = z.object({
|
|
1299
|
-
type: z.literal("tool_result"),
|
|
1300
|
-
seq: z.number(),
|
|
1301
|
-
time: z.number(),
|
|
1302
|
-
turn_id: z.string(),
|
|
1303
|
-
tool_call_id: z.string(),
|
|
1304
|
-
output: z.unknown(),
|
|
1305
|
-
is_error: z.boolean().optional(),
|
|
1306
|
-
synthetic: z.boolean().optional(),
|
|
1307
|
-
uuid: z.string().optional(),
|
|
1308
|
-
parent_uuid: z.string().optional()
|
|
1309
|
-
});
|
|
1310
|
-
const _rawCompactionRecordSchema = z.object({
|
|
1311
|
-
type: z.literal("compaction"),
|
|
1312
|
-
seq: z.number(),
|
|
1313
|
-
time: z.number(),
|
|
1314
|
-
summary: z.string(),
|
|
1315
|
-
compacted_range: z.object({
|
|
1316
|
-
from_turn: z.number(),
|
|
1317
|
-
to_turn: z.number(),
|
|
1318
|
-
message_count: z.number()
|
|
1319
|
-
}),
|
|
1320
|
-
pre_compact_tokens: z.number(),
|
|
1321
|
-
post_compact_tokens: z.number(),
|
|
1322
|
-
trigger: z.enum(["auto", "manual"]),
|
|
1323
|
-
archive_file: z.string().optional(),
|
|
1324
|
-
uuid: z.string().optional()
|
|
1325
|
-
});
|
|
1326
|
-
const _rawSystemPromptChangedRecordSchema = z.object({
|
|
1327
|
-
type: z.literal("system_prompt_changed"),
|
|
1328
|
-
seq: z.number(),
|
|
1329
|
-
time: z.number(),
|
|
1330
|
-
new_prompt: z.string()
|
|
1331
|
-
});
|
|
1332
|
-
const _rawToolsChangedRecordSchema = z.object({
|
|
1333
|
-
type: z.literal("tools_changed"),
|
|
1334
|
-
seq: z.number(),
|
|
1335
|
-
time: z.number(),
|
|
1336
|
-
operation: z.enum([
|
|
1337
|
-
"register",
|
|
1338
|
-
"remove",
|
|
1339
|
-
"set_active"
|
|
1340
|
-
]),
|
|
1341
|
-
tools: z.array(z.string())
|
|
1342
|
-
});
|
|
1343
|
-
const _rawSystemReminderRecordSchema = z.object({
|
|
1344
|
-
type: z.literal("system_reminder"),
|
|
1345
|
-
seq: z.number(),
|
|
1346
|
-
time: z.number(),
|
|
1347
|
-
content: z.string(),
|
|
1348
|
-
consumed_at_turn: z.number().optional()
|
|
1349
|
-
});
|
|
1350
|
-
const _rawNotificationRecordSchema = z.object({
|
|
1351
|
-
type: z.literal("notification"),
|
|
1352
|
-
seq: z.number(),
|
|
1353
|
-
time: z.number(),
|
|
1354
|
-
data: z.object({
|
|
1355
|
-
id: z.string(),
|
|
1356
|
-
category: z.enum([
|
|
1357
|
-
"task",
|
|
1358
|
-
"agent",
|
|
1359
|
-
"system",
|
|
1360
|
-
"team"
|
|
1361
|
-
]),
|
|
1362
|
-
type: z.string(),
|
|
1363
|
-
source_kind: z.string(),
|
|
1364
|
-
source_id: z.string(),
|
|
1365
|
-
title: z.string(),
|
|
1366
|
-
body: z.string(),
|
|
1367
|
-
severity: z.enum([
|
|
1368
|
-
"info",
|
|
1369
|
-
"success",
|
|
1370
|
-
"warning",
|
|
1371
|
-
"error"
|
|
1372
|
-
]),
|
|
1373
|
-
payload: z.record(z.string(), z.unknown()).optional(),
|
|
1374
|
-
targets: z.array(z.enum([
|
|
1375
|
-
"llm",
|
|
1376
|
-
"wire",
|
|
1377
|
-
"shell"
|
|
1378
|
-
])),
|
|
1379
|
-
dedupe_key: z.string().optional(),
|
|
1380
|
-
delivered_at: z.object({
|
|
1381
|
-
llm: z.number().optional(),
|
|
1382
|
-
wire: z.number().optional(),
|
|
1383
|
-
shell: z.number().optional()
|
|
1384
|
-
}).optional(),
|
|
1385
|
-
envelope_id: z.string().optional()
|
|
1386
|
-
})
|
|
1387
|
-
});
|
|
1388
|
-
const _rawToolDeniedRecordSchema = z.object({
|
|
1389
|
-
type: z.literal("tool_denied"),
|
|
1390
|
-
seq: z.number(),
|
|
1391
|
-
time: z.number(),
|
|
1392
|
-
turn_id: z.string(),
|
|
1393
|
-
step: z.number(),
|
|
1394
|
-
data: z.object({
|
|
1395
|
-
tool_call_id: z.string(),
|
|
1396
|
-
tool_name: z.string(),
|
|
1397
|
-
rule_id: z.string(),
|
|
1398
|
-
reason: z.string()
|
|
1399
|
-
})
|
|
1400
|
-
});
|
|
1401
|
-
const _rawStepBeginRecordSchema = z.object({
|
|
1402
|
-
type: z.literal("step_begin"),
|
|
1403
|
-
seq: z.number(),
|
|
1404
|
-
time: z.number(),
|
|
1405
|
-
uuid: z.string(),
|
|
1406
|
-
turn_id: z.string(),
|
|
1407
|
-
step: z.number()
|
|
1408
|
-
});
|
|
1409
|
-
const _rawStepEndRecordSchema = z.object({
|
|
1410
|
-
type: z.literal("step_end"),
|
|
1411
|
-
seq: z.number(),
|
|
1412
|
-
time: z.number(),
|
|
1413
|
-
uuid: z.string(),
|
|
1414
|
-
turn_id: z.string(),
|
|
1415
|
-
step: z.number(),
|
|
1416
|
-
usage: z.object({
|
|
1417
|
-
input_tokens: z.number(),
|
|
1418
|
-
output_tokens: z.number(),
|
|
1419
|
-
cache_read_tokens: z.number().optional(),
|
|
1420
|
-
cache_write_tokens: z.number().optional()
|
|
1421
|
-
}).optional(),
|
|
1422
|
-
finish_reason: z.string().optional()
|
|
1423
|
-
});
|
|
1424
|
-
const _contentPartBodySchema = z.discriminatedUnion("kind", [z.object({
|
|
1425
|
-
kind: z.literal("text"),
|
|
1426
|
-
text: z.string()
|
|
1427
|
-
}), z.object({
|
|
1428
|
-
kind: z.literal("think"),
|
|
1429
|
-
think: z.string(),
|
|
1430
|
-
encrypted: z.string().optional()
|
|
1431
|
-
})]);
|
|
1432
|
-
const _rawContentPartRecordSchema = z.object({
|
|
1433
|
-
type: z.literal("content_part"),
|
|
1434
|
-
seq: z.number(),
|
|
1435
|
-
time: z.number(),
|
|
1436
|
-
uuid: z.string(),
|
|
1437
|
-
turn_id: z.string(),
|
|
1438
|
-
step: z.number(),
|
|
1439
|
-
step_uuid: z.string(),
|
|
1440
|
-
role: z.literal("assistant"),
|
|
1441
|
-
part: _contentPartBodySchema
|
|
1442
|
-
});
|
|
1443
|
-
const _rawToolCallRecordSchema = z.object({
|
|
1444
|
-
type: z.literal("tool_call"),
|
|
1445
|
-
seq: z.number(),
|
|
1446
|
-
time: z.number(),
|
|
1447
|
-
uuid: z.string(),
|
|
1448
|
-
turn_id: z.string(),
|
|
1449
|
-
step: z.number(),
|
|
1450
|
-
step_uuid: z.string(),
|
|
1451
|
-
data: z.object({
|
|
1452
|
-
tool_call_id: z.string(),
|
|
1453
|
-
tool_name: z.string(),
|
|
1454
|
-
args: z.unknown(),
|
|
1455
|
-
activity_description: z.string().optional(),
|
|
1456
|
-
user_facing_name: z.string().optional(),
|
|
1457
|
-
input_display: z.unknown().optional()
|
|
1458
|
-
})
|
|
1459
|
-
});
|
|
1460
|
-
/**
|
|
1461
|
-
* Storage persists the approval display as an opaque blob. Write-trust
|
|
1462
|
-
* / read-passthrough: the concrete `ToolInputDisplay` union lives in
|
|
1463
|
-
* `display/` and is the responsibility of the host layer to validate
|
|
1464
|
-
* at render time.
|
|
1465
|
-
*/
|
|
1466
|
-
const ApprovalDisplaySchema = z.unknown();
|
|
1467
|
-
const ApprovalSourceSchema = z.discriminatedUnion("kind", [
|
|
1468
|
-
z.object({
|
|
1469
|
-
kind: z.literal("soul"),
|
|
1470
|
-
agent_id: z.string()
|
|
1471
|
-
}),
|
|
1472
|
-
z.object({
|
|
1473
|
-
kind: z.literal("subagent"),
|
|
1474
|
-
agent_id: z.string(),
|
|
1475
|
-
subagent_type: z.string().optional()
|
|
1476
|
-
}),
|
|
1477
|
-
z.object({
|
|
1478
|
-
kind: z.literal("turn"),
|
|
1479
|
-
turn_id: z.string()
|
|
1480
|
-
}),
|
|
1481
|
-
z.object({
|
|
1482
|
-
kind: z.literal("session"),
|
|
1483
|
-
session_id: z.string()
|
|
1484
|
-
}),
|
|
1485
|
-
z.object({
|
|
1486
|
-
kind: z.literal("mcp"),
|
|
1487
|
-
server_id: z.string(),
|
|
1488
|
-
reason: z.enum([
|
|
1489
|
-
"elicitation",
|
|
1490
|
-
"auth",
|
|
1491
|
-
"tool_call"
|
|
1492
|
-
])
|
|
1493
|
-
})
|
|
1494
|
-
]);
|
|
1495
|
-
const skillInvocationTriggerEnum = z.enum([
|
|
1496
|
-
"user-slash",
|
|
1497
|
-
"claude-proactive",
|
|
1498
|
-
"nested-skill"
|
|
1499
|
-
]);
|
|
1500
|
-
const _rawSkillInvokedRecordSchema = z.object({
|
|
1501
|
-
type: z.literal("skill_invoked"),
|
|
1502
|
-
seq: z.number(),
|
|
1503
|
-
time: z.number(),
|
|
1504
|
-
turn_id: z.string(),
|
|
1505
|
-
agent_type: agentTypeEnum.optional(),
|
|
1506
|
-
data: z.object({
|
|
1507
|
-
skill_name: z.string(),
|
|
1508
|
-
execution_mode: z.enum(["inline", "fork"]),
|
|
1509
|
-
original_input: z.string(),
|
|
1510
|
-
sub_agent_id: z.string().optional(),
|
|
1511
|
-
invocation_trigger: skillInvocationTriggerEnum.optional(),
|
|
1512
|
-
query_depth: z.number().optional()
|
|
1513
|
-
})
|
|
1514
|
-
});
|
|
1515
|
-
const _rawSkillCompletedRecordSchema = z.object({
|
|
1516
|
-
type: z.literal("skill_completed"),
|
|
1517
|
-
seq: z.number(),
|
|
1518
|
-
time: z.number(),
|
|
1519
|
-
turn_id: z.string(),
|
|
1520
|
-
agent_type: agentTypeEnum.optional(),
|
|
1521
|
-
data: z.object({
|
|
1522
|
-
skill_name: z.string(),
|
|
1523
|
-
execution_mode: z.enum(["inline", "fork"]),
|
|
1524
|
-
success: z.boolean(),
|
|
1525
|
-
error: z.string().optional(),
|
|
1526
|
-
sub_agent_id: z.string().optional(),
|
|
1527
|
-
invocation_trigger: skillInvocationTriggerEnum.optional(),
|
|
1528
|
-
query_depth: z.number().optional()
|
|
1529
|
-
})
|
|
1530
|
-
});
|
|
1531
|
-
const _rawApprovalRequestRecordSchema = z.object({
|
|
1532
|
-
type: z.literal("approval_request"),
|
|
1533
|
-
seq: z.number(),
|
|
1534
|
-
time: z.number(),
|
|
1535
|
-
turn_id: z.string(),
|
|
1536
|
-
step: z.number(),
|
|
1537
|
-
data: z.object({
|
|
1538
|
-
request_id: z.string(),
|
|
1539
|
-
tool_call_id: z.string(),
|
|
1540
|
-
tool_name: z.string(),
|
|
1541
|
-
action: z.string(),
|
|
1542
|
-
display: ApprovalDisplaySchema,
|
|
1543
|
-
source: ApprovalSourceSchema
|
|
1544
|
-
})
|
|
1545
|
-
});
|
|
1546
|
-
const _rawApprovalResponseRecordSchema = z.object({
|
|
1547
|
-
type: z.literal("approval_response"),
|
|
1548
|
-
seq: z.number(),
|
|
1549
|
-
time: z.number(),
|
|
1550
|
-
turn_id: z.string(),
|
|
1551
|
-
step: z.number(),
|
|
1552
|
-
data: z.object({
|
|
1553
|
-
request_id: z.string(),
|
|
1554
|
-
response: z.enum([
|
|
1555
|
-
"approved",
|
|
1556
|
-
"rejected",
|
|
1557
|
-
"cancelled"
|
|
1558
|
-
]),
|
|
1559
|
-
feedback: z.string().optional(),
|
|
1560
|
-
selected_label: z.string().optional(),
|
|
1561
|
-
synthetic: z.boolean().optional()
|
|
1562
|
-
})
|
|
1563
|
-
});
|
|
1564
|
-
const _rawTeamMailRecordSchema = z.object({
|
|
1565
|
-
type: z.literal("team_mail"),
|
|
1566
|
-
seq: z.number(),
|
|
1567
|
-
time: z.number(),
|
|
1568
|
-
data: z.object({
|
|
1569
|
-
mail_id: z.string(),
|
|
1570
|
-
reply_to: z.string().optional(),
|
|
1571
|
-
from_agent: z.string(),
|
|
1572
|
-
to_agent: z.string(),
|
|
1573
|
-
content: z.string(),
|
|
1574
|
-
summary: z.string().optional()
|
|
1575
|
-
})
|
|
1576
|
-
});
|
|
1577
|
-
const _rawTokenUsageSchema = z.object({
|
|
1578
|
-
input: z.number().int().nonnegative(),
|
|
1579
|
-
output: z.number().int().nonnegative(),
|
|
1580
|
-
cache_read: z.number().int().nonnegative().optional(),
|
|
1581
|
-
cache_write: z.number().int().nonnegative().optional()
|
|
1582
|
-
});
|
|
1583
|
-
const _rawSubagentSpawnedRecordSchema = z.object({
|
|
1584
|
-
type: z.literal("subagent_spawned"),
|
|
1585
|
-
seq: z.number(),
|
|
1586
|
-
time: z.number(),
|
|
1587
|
-
uuid: z.string().optional(),
|
|
1588
|
-
data: z.object({
|
|
1589
|
-
agent_id: z.string(),
|
|
1590
|
-
agent_name: z.string().optional(),
|
|
1591
|
-
parent_tool_call_id: z.string(),
|
|
1592
|
-
parent_agent_id: z.string().optional(),
|
|
1593
|
-
parent_tool_call_uuid: z.string().optional(),
|
|
1594
|
-
run_in_background: z.boolean()
|
|
1595
|
-
})
|
|
1596
|
-
});
|
|
1597
|
-
const _rawSubagentCompletedRecordSchema = z.object({
|
|
1598
|
-
type: z.literal("subagent_completed"),
|
|
1599
|
-
seq: z.number(),
|
|
1600
|
-
time: z.number(),
|
|
1601
|
-
uuid: z.string().optional(),
|
|
1602
|
-
parent_uuid: z.string().optional(),
|
|
1603
|
-
data: z.object({
|
|
1604
|
-
agent_id: z.string(),
|
|
1605
|
-
parent_tool_call_id: z.string(),
|
|
1606
|
-
result_summary: z.string(),
|
|
1607
|
-
usage: _rawTokenUsageSchema.optional()
|
|
1608
|
-
})
|
|
1609
|
-
});
|
|
1610
|
-
const _rawSubagentFailedRecordSchema = z.object({
|
|
1611
|
-
type: z.literal("subagent_failed"),
|
|
1612
|
-
seq: z.number(),
|
|
1613
|
-
time: z.number(),
|
|
1614
|
-
uuid: z.string().optional(),
|
|
1615
|
-
parent_uuid: z.string().optional(),
|
|
1616
|
-
data: z.object({
|
|
1617
|
-
agent_id: z.string(),
|
|
1618
|
-
parent_tool_call_id: z.string(),
|
|
1619
|
-
error: z.string()
|
|
1620
|
-
})
|
|
1621
|
-
});
|
|
1622
|
-
const _rawOwnershipChangedRecordSchema = z.object({
|
|
1623
|
-
type: z.literal("ownership_changed"),
|
|
1624
|
-
seq: z.number(),
|
|
1625
|
-
time: z.number(),
|
|
1626
|
-
old_owner: z.string().nullable(),
|
|
1627
|
-
new_owner: z.string()
|
|
1628
|
-
});
|
|
1629
|
-
const _rawContextEditRecordSchema = z.object({
|
|
1630
|
-
type: z.literal("context_edit"),
|
|
1631
|
-
seq: z.number(),
|
|
1632
|
-
time: z.number(),
|
|
1633
|
-
operation: z.enum([
|
|
1634
|
-
"edit_message",
|
|
1635
|
-
"delete_message",
|
|
1636
|
-
"rewind",
|
|
1637
|
-
"insert_message",
|
|
1638
|
-
"replace_message"
|
|
1639
|
-
]),
|
|
1640
|
-
target_seq: z.number().optional(),
|
|
1641
|
-
to_turn: z.number().optional(),
|
|
1642
|
-
after_seq: z.number().optional(),
|
|
1643
|
-
new_content: z.string().optional(),
|
|
1644
|
-
new_role: z.enum([
|
|
1645
|
-
"user",
|
|
1646
|
-
"assistant",
|
|
1647
|
-
"system"
|
|
1648
|
-
]).optional(),
|
|
1649
|
-
cascade: z.boolean().optional()
|
|
1650
|
-
});
|
|
1651
|
-
const _rawContextClearedRecordSchema = z.object({
|
|
1652
|
-
type: z.literal("context_cleared"),
|
|
1653
|
-
seq: z.number(),
|
|
1654
|
-
time: z.number()
|
|
1655
|
-
});
|
|
1656
|
-
const _sessionInitializedCommonShape = {
|
|
1657
|
-
type: z.literal("session_initialized"),
|
|
1658
|
-
seq: z.number(),
|
|
1659
|
-
time: z.number(),
|
|
1660
|
-
system_prompt: z.string(),
|
|
1661
|
-
active_tools: z.array(z.string()),
|
|
1662
|
-
model: z.string().optional(),
|
|
1663
|
-
permission_mode: z.enum([
|
|
1664
|
-
"default",
|
|
1665
|
-
"acceptEdits",
|
|
1666
|
-
"bypassPermissions"
|
|
1667
|
-
]).optional(),
|
|
1668
|
-
plan_mode: z.boolean().optional(),
|
|
1669
|
-
workspace_dir: z.string().optional(),
|
|
1670
|
-
thinking_level: z.string().optional()
|
|
1671
|
-
};
|
|
1672
|
-
const _rawSessionInitializedMainSchema = z.object({
|
|
1673
|
-
..._sessionInitializedCommonShape,
|
|
1674
|
-
agent_type: z.literal("main"),
|
|
1675
|
-
session_id: z.string()
|
|
1676
|
-
});
|
|
1677
|
-
const _rawSessionInitializedSubSchema = z.object({
|
|
1678
|
-
..._sessionInitializedCommonShape,
|
|
1679
|
-
agent_type: z.literal("sub"),
|
|
1680
|
-
agent_id: z.string(),
|
|
1681
|
-
agent_name: z.string().optional(),
|
|
1682
|
-
parent_session_id: z.string(),
|
|
1683
|
-
parent_agent_id: z.string().optional(),
|
|
1684
|
-
parent_tool_call_id: z.string(),
|
|
1685
|
-
run_in_background: z.boolean()
|
|
1686
|
-
});
|
|
1687
|
-
const _rawSessionInitializedIndependentSchema = z.object({
|
|
1688
|
-
..._sessionInitializedCommonShape,
|
|
1689
|
-
agent_type: z.literal("independent"),
|
|
1690
|
-
agent_id: z.string(),
|
|
1691
|
-
agent_name: z.string().optional()
|
|
1692
|
-
});
|
|
1693
|
-
const _rawSessionInitializedRecordSchema = z.discriminatedUnion("agent_type", [
|
|
1694
|
-
_rawSessionInitializedMainSchema,
|
|
1695
|
-
_rawSessionInitializedSubSchema,
|
|
1696
|
-
_rawSessionInitializedIndependentSchema
|
|
1697
|
-
]);
|
|
1698
|
-
const SessionInitializedRecordSchema = _rawSessionInitializedRecordSchema;
|
|
1699
|
-
const WireRecordSchema = z.discriminatedUnion("type", [
|
|
1700
|
-
_rawTurnBeginRecordSchema,
|
|
1701
|
-
_rawTurnEndRecordSchema,
|
|
1702
|
-
_rawUserMessageRecordSchema,
|
|
1703
|
-
_rawToolResultRecordSchema,
|
|
1704
|
-
_rawCompactionRecordSchema,
|
|
1705
|
-
_rawSystemPromptChangedRecordSchema,
|
|
1706
|
-
_rawToolsChangedRecordSchema,
|
|
1707
|
-
_rawSystemReminderRecordSchema,
|
|
1708
|
-
_rawNotificationRecordSchema,
|
|
1709
|
-
_rawToolDeniedRecordSchema,
|
|
1710
|
-
_rawStepBeginRecordSchema,
|
|
1711
|
-
_rawStepEndRecordSchema,
|
|
1712
|
-
_rawContentPartRecordSchema,
|
|
1713
|
-
_rawToolCallRecordSchema,
|
|
1714
|
-
_rawSkillInvokedRecordSchema,
|
|
1715
|
-
_rawSkillCompletedRecordSchema,
|
|
1716
|
-
_rawApprovalRequestRecordSchema,
|
|
1717
|
-
_rawApprovalResponseRecordSchema,
|
|
1718
|
-
_rawTeamMailRecordSchema,
|
|
1719
|
-
_rawSubagentSpawnedRecordSchema,
|
|
1720
|
-
_rawSubagentCompletedRecordSchema,
|
|
1721
|
-
_rawSubagentFailedRecordSchema,
|
|
1722
|
-
_rawOwnershipChangedRecordSchema,
|
|
1723
|
-
_rawContextEditRecordSchema,
|
|
1724
|
-
_rawContextClearedRecordSchema,
|
|
1725
|
-
_rawSessionInitializedRecordSchema
|
|
1726
|
-
]);
|
|
1727
|
-
//#endregion
|
|
1728
|
-
//#region ../../packages/kimi-core/src/storage/journal/reader.ts
|
|
1729
|
-
async function defaultReadLines(path) {
|
|
1730
|
-
const parts = (await readFile(path, "utf8")).split("\n");
|
|
1731
|
-
if (parts.length > 0 && parts.at(-1) === "") parts.pop();
|
|
1732
|
-
return parts;
|
|
1733
|
-
}
|
|
1734
|
-
/**
|
|
1735
|
-
* Replay a wire.jsonl into an ordered list of valid WireRecords plus a
|
|
1736
|
-
* health signal. Does not construct a ContextState — that concern lives in
|
|
1737
|
-
* WiredContextState which drives its internal mirror from these records.
|
|
1738
|
-
*
|
|
1739
|
-
* Error policy (§4.1.1 / §8.4):
|
|
1740
|
-
* - unknown record type at any line → skip + warn
|
|
1741
|
-
* - JSON.parse failure on the LAST body line → skip + warn (tail truncation)
|
|
1742
|
-
* - JSON.parse failure on any earlier line → health = 'broken'
|
|
1743
|
-
* - major version higher than supportedMajor → throw IncompatibleVersionError
|
|
1744
|
-
*/
|
|
1745
|
-
async function replayWire(path, options) {
|
|
1746
|
-
const lines = await (options.readLines ?? defaultReadLines)(path);
|
|
1747
|
-
if (lines.length === 0) throw new WireJournalCorruptError(`wire.jsonl is empty at ${path}`);
|
|
1748
|
-
const firstLine = lines[0];
|
|
1749
|
-
if (firstLine === void 0) throw new WireJournalCorruptError(`wire.jsonl is empty at ${path}`);
|
|
1750
|
-
let meta;
|
|
1751
|
-
try {
|
|
1752
|
-
meta = WireFileMetadataSchema.parse(JSON.parse(firstLine));
|
|
1753
|
-
} catch (error) {
|
|
1754
|
-
throw new WireJournalCorruptError(`wire.jsonl metadata header (line 1) is invalid: ${String(error)}`);
|
|
1755
|
-
}
|
|
1756
|
-
const majorStr = meta.protocol_version.split(".")[0] ?? "0";
|
|
1757
|
-
const major = Number.parseInt(majorStr, 10);
|
|
1758
|
-
if (!Number.isFinite(major)) throw new IncompatibleVersionError(`wire.jsonl metadata.protocol_version "${meta.protocol_version}" is not a valid version string`);
|
|
1759
|
-
if (major > options.supportedMajor) throw new IncompatibleVersionError(`wire.jsonl version ${meta.protocol_version} is not supported (max major: ${options.supportedMajor}). Please upgrade Kimi CLI.`);
|
|
1760
|
-
if (meta.producer === void 0) throw new UnsupportedProducerError("legacy", "metadata-missing-producer");
|
|
1761
|
-
if (meta.producer.kind !== "typescript") throw new UnsupportedProducerError(meta.producer.kind === "python" ? "python" : "unknown", "cross-producer-not-supported");
|
|
1762
|
-
const producer = meta.producer;
|
|
1763
|
-
const initLine = lines[1];
|
|
1764
|
-
if (initLine === void 0) throw new MalformedWireError("session-initialized-missing", `wire.jsonl has no line 2 (session_initialized) at ${path}`);
|
|
1765
|
-
let initRaw;
|
|
1766
|
-
try {
|
|
1767
|
-
initRaw = JSON.parse(initLine);
|
|
1768
|
-
} catch (error) {
|
|
1769
|
-
throw new MalformedWireError("session-initialized-missing", `wire.jsonl line 2 failed JSON.parse: ${String(error)}`);
|
|
1391
|
+
get thinkingLevel() {
|
|
1392
|
+
return this._thinkingLevel;
|
|
1770
1393
|
}
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
const
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
}
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1394
|
+
get beforeStep() {
|
|
1395
|
+
return this._beforeStep;
|
|
1396
|
+
}
|
|
1397
|
+
setBeforeStepHook(fn) {
|
|
1398
|
+
this._beforeStep = fn;
|
|
1399
|
+
}
|
|
1400
|
+
getHistory() {
|
|
1401
|
+
return this.history;
|
|
1402
|
+
}
|
|
1403
|
+
drainSteerMessages() {
|
|
1404
|
+
const drained = this.steerBuffer;
|
|
1405
|
+
this.steerBuffer = [];
|
|
1406
|
+
return drained;
|
|
1407
|
+
}
|
|
1408
|
+
pushSteer(input) {
|
|
1409
|
+
this.steerBuffer.push({ ...input });
|
|
1410
|
+
}
|
|
1411
|
+
appendUserContent(content) {
|
|
1412
|
+
this.history.push({
|
|
1413
|
+
role: "user",
|
|
1414
|
+
content,
|
|
1415
|
+
toolCalls: []
|
|
1416
|
+
});
|
|
1417
|
+
}
|
|
1418
|
+
appendToolResult(toolCallId, content) {
|
|
1419
|
+
this.history.push({
|
|
1420
|
+
role: "tool",
|
|
1421
|
+
content,
|
|
1422
|
+
toolCalls: [],
|
|
1423
|
+
toolCallId
|
|
1424
|
+
});
|
|
1425
|
+
}
|
|
1426
|
+
openStep(uuid) {
|
|
1427
|
+
const message = {
|
|
1428
|
+
role: "assistant",
|
|
1429
|
+
content: [],
|
|
1430
|
+
toolCalls: []
|
|
1431
|
+
};
|
|
1432
|
+
this.history.push(message);
|
|
1433
|
+
this.openSteps.set(uuid, message);
|
|
1434
|
+
}
|
|
1435
|
+
closeStep(uuid, usage) {
|
|
1436
|
+
this.openSteps.delete(uuid);
|
|
1437
|
+
if (usage !== void 0) this._tokenCountWithPending = usage.input_tokens + usage.output_tokens;
|
|
1438
|
+
}
|
|
1439
|
+
getOpenStep(uuid) {
|
|
1440
|
+
return this.openSteps.get(uuid);
|
|
1441
|
+
}
|
|
1442
|
+
appendOpenStepContent(stepUuid, part) {
|
|
1443
|
+
this.openSteps.get(stepUuid)?.content.push(part);
|
|
1444
|
+
}
|
|
1445
|
+
appendOpenStepToolCall(stepUuid, toolCall) {
|
|
1446
|
+
this.openSteps.get(stepUuid)?.toolCalls.push(toolCall);
|
|
1447
|
+
}
|
|
1448
|
+
clearConversation() {
|
|
1449
|
+
this.history = [];
|
|
1450
|
+
this.openSteps.clear();
|
|
1451
|
+
this._tokenCountWithPending = 0;
|
|
1452
|
+
}
|
|
1453
|
+
setSystemPrompt(systemPrompt) {
|
|
1454
|
+
this._systemPrompt = systemPrompt;
|
|
1455
|
+
}
|
|
1456
|
+
setModel(model) {
|
|
1457
|
+
this._model = model;
|
|
1458
|
+
}
|
|
1459
|
+
setThinkingLevel(level) {
|
|
1460
|
+
this._thinkingLevel = level;
|
|
1461
|
+
}
|
|
1462
|
+
applyToolsChanged(operation, tools) {
|
|
1463
|
+
if (operation === "set_active") {
|
|
1464
|
+
this._activeTools = new Set(tools);
|
|
1465
|
+
return;
|
|
1808
1466
|
}
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
warnings.push(`Tail record failed schema validation at line ${physicalLineNo}, skipping: ${snippet}`);
|
|
1813
|
-
continue;
|
|
1814
|
-
}
|
|
1815
|
-
const reason = `wire.jsonl schema violation at line ${physicalLineNo}: ${parsed.error.message}`;
|
|
1816
|
-
return {
|
|
1817
|
-
records,
|
|
1818
|
-
protocolVersion: meta.protocol_version,
|
|
1819
|
-
health: "broken",
|
|
1820
|
-
brokenReason: reason,
|
|
1821
|
-
warnings,
|
|
1822
|
-
producer,
|
|
1823
|
-
sessionInitialized
|
|
1824
|
-
};
|
|
1467
|
+
if (operation === "register") {
|
|
1468
|
+
for (const tool of tools) this._activeTools.add(tool);
|
|
1469
|
+
return;
|
|
1825
1470
|
}
|
|
1826
|
-
|
|
1471
|
+
for (const tool of tools) this._activeTools.delete(tool);
|
|
1472
|
+
}
|
|
1473
|
+
resetToSummary(summary) {
|
|
1474
|
+
this.history = [{
|
|
1475
|
+
role: "assistant",
|
|
1476
|
+
content: [{
|
|
1477
|
+
type: "text",
|
|
1478
|
+
text: summary.summary
|
|
1479
|
+
}],
|
|
1480
|
+
toolCalls: []
|
|
1481
|
+
}];
|
|
1482
|
+
this.openSteps.clear();
|
|
1483
|
+
this._tokenCountWithPending = summary.postCompactTokens;
|
|
1827
1484
|
}
|
|
1485
|
+
};
|
|
1486
|
+
//#endregion
|
|
1487
|
+
//#region ../../packages/kimi-core/src/storage/state/message-mirror.ts
|
|
1488
|
+
function userInputToContentParts(input) {
|
|
1489
|
+
if (input.parts !== void 0 && input.parts.some((part) => part.type !== "text") && input.parts !== void 0) return input.parts.map(userInputPartToContentPart);
|
|
1490
|
+
return [{
|
|
1491
|
+
type: "text",
|
|
1492
|
+
text: input.parts !== void 0 ? input.parts.filter((part) => part.type === "text").map((part) => part.text).join("") : input.text
|
|
1493
|
+
}];
|
|
1494
|
+
}
|
|
1495
|
+
function toolResultOutputToContentParts(output) {
|
|
1496
|
+
if (isContentPartArray$1(output)) return output.map((part) => cloneContentPart$1(part));
|
|
1497
|
+
return [{
|
|
1498
|
+
type: "text",
|
|
1499
|
+
text: typeof output === "string" ? output : JSON.stringify(output)
|
|
1500
|
+
}];
|
|
1501
|
+
}
|
|
1502
|
+
function atomicPartToContentPart(part) {
|
|
1503
|
+
if (part.kind === "text") return {
|
|
1504
|
+
type: "text",
|
|
1505
|
+
text: part.text
|
|
1506
|
+
};
|
|
1507
|
+
const thinkPart = {
|
|
1508
|
+
type: "think",
|
|
1509
|
+
think: part.think
|
|
1510
|
+
};
|
|
1511
|
+
if (part.encrypted !== void 0) thinkPart.encrypted = part.encrypted;
|
|
1512
|
+
return thinkPart;
|
|
1513
|
+
}
|
|
1514
|
+
function sanitizeToolCallData(data) {
|
|
1828
1515
|
return {
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1516
|
+
tool_call_id: data.tool_call_id,
|
|
1517
|
+
tool_name: data.tool_name,
|
|
1518
|
+
args: data.args,
|
|
1519
|
+
...data.activity_description !== void 0 ? { activity_description: data.activity_description } : {},
|
|
1520
|
+
user_facing_name: data.user_facing_name,
|
|
1521
|
+
input_display: data.input_display
|
|
1835
1522
|
};
|
|
1836
1523
|
}
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
"
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
"
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
"
|
|
1863
|
-
|
|
1524
|
+
function toolCallDataToKosongToolCall(data) {
|
|
1525
|
+
return {
|
|
1526
|
+
type: "function",
|
|
1527
|
+
id: data.tool_call_id,
|
|
1528
|
+
function: {
|
|
1529
|
+
name: data.tool_name,
|
|
1530
|
+
arguments: data.args === void 0 ? null : JSON.stringify(data.args)
|
|
1531
|
+
}
|
|
1532
|
+
};
|
|
1533
|
+
}
|
|
1534
|
+
function userInputPartToContentPart(part) {
|
|
1535
|
+
if (part.type === "text") return {
|
|
1536
|
+
type: "text",
|
|
1537
|
+
text: part.text
|
|
1538
|
+
};
|
|
1539
|
+
if (part.type === "image_url") return {
|
|
1540
|
+
type: "image_url",
|
|
1541
|
+
imageUrl: part.image_url
|
|
1542
|
+
};
|
|
1543
|
+
return {
|
|
1544
|
+
type: "video_url",
|
|
1545
|
+
videoUrl: part.video_url
|
|
1546
|
+
};
|
|
1547
|
+
}
|
|
1548
|
+
function isContentPart$2(value) {
|
|
1549
|
+
if (typeof value !== "object" || value === null) return false;
|
|
1550
|
+
const part = value;
|
|
1551
|
+
return part.type === "text" || part.type === "think" || part.type === "image_url" || part.type === "audio_url" || part.type === "video_url";
|
|
1552
|
+
}
|
|
1553
|
+
function isContentPartArray$1(value) {
|
|
1554
|
+
return Array.isArray(value) && value.every((part) => isContentPart$2(part));
|
|
1555
|
+
}
|
|
1556
|
+
function cloneContentPart$1(part) {
|
|
1557
|
+
return { ...part };
|
|
1558
|
+
}
|
|
1559
|
+
//#endregion
|
|
1560
|
+
//#region ../../packages/kimi-core/src/storage/state/notification-xml.ts
|
|
1864
1561
|
/**
|
|
1865
|
-
*
|
|
1866
|
-
*
|
|
1562
|
+
* Notification XML rendering — shared between ContextState and
|
|
1563
|
+
* ConversationProjector so both produce byte-identical chat-history
|
|
1564
|
+
* injection text.
|
|
1867
1565
|
*
|
|
1868
|
-
*
|
|
1869
|
-
*
|
|
1870
|
-
*
|
|
1871
|
-
*
|
|
1872
|
-
*
|
|
1566
|
+
* Output shape:
|
|
1567
|
+
* <notification id="..." category="..." type="..." source_kind="..." source_id="...">
|
|
1568
|
+
* Title: ...
|
|
1569
|
+
* Severity: ...
|
|
1570
|
+
* <body>
|
|
1571
|
+
* <task-notification> (only when source_kind === 'background_task' and tail_output is non-empty)
|
|
1572
|
+
* <truncated tail>
|
|
1573
|
+
* </task-notification>
|
|
1574
|
+
* </notification>
|
|
1575
|
+
*
|
|
1576
|
+
* The opening-tag names (`<notification ` / `<task-notification>`) are
|
|
1577
|
+
* load-bearing for the projector's `mergeAdjacentUserMessages` detector
|
|
1578
|
+
* — rename requires updating the detector too.
|
|
1873
1579
|
*/
|
|
1874
|
-
|
|
1875
|
-
"
|
|
1876
|
-
"
|
|
1877
|
-
"
|
|
1878
|
-
"
|
|
1879
|
-
"
|
|
1880
|
-
]
|
|
1580
|
+
function renderNotificationXml(data) {
|
|
1581
|
+
const id = stringAttr(data["id"], "unknown");
|
|
1582
|
+
const category = stringAttr(data["category"], "unknown");
|
|
1583
|
+
const type = stringAttr(data["type"], "unknown");
|
|
1584
|
+
const sourceKind = stringAttr(data["source_kind"], "unknown");
|
|
1585
|
+
const sourceId = stringAttr(data["source_id"], "unknown");
|
|
1586
|
+
const title = typeof data["title"] === "string" ? data["title"] : "";
|
|
1587
|
+
const severity = typeof data["severity"] === "string" ? data["severity"] : "";
|
|
1588
|
+
const body = typeof data["body"] === "string" ? data["body"] : "";
|
|
1589
|
+
const lines = [`<notification id="${id}" category="${category}" type="${type}" source_kind="${sourceKind}" source_id="${sourceId}">`];
|
|
1590
|
+
if (title.length > 0) lines.push(`Title: ${title}`);
|
|
1591
|
+
if (severity.length > 0) lines.push(`Severity: ${severity}`);
|
|
1592
|
+
if (body.length > 0) lines.push(body);
|
|
1593
|
+
if (data["source_kind"] === "background_task") {
|
|
1594
|
+
const tailRaw = typeof data["tail_output"] === "string" ? data["tail_output"] : "";
|
|
1595
|
+
if (tailRaw.length > 0) {
|
|
1596
|
+
const truncated = truncateTailOutput(tailRaw, 20, 3e3);
|
|
1597
|
+
lines.push("<task-notification>");
|
|
1598
|
+
lines.push(truncated);
|
|
1599
|
+
lines.push("</task-notification>");
|
|
1600
|
+
}
|
|
1601
|
+
}
|
|
1602
|
+
lines.push("</notification>");
|
|
1603
|
+
return lines.join("\n");
|
|
1604
|
+
}
|
|
1605
|
+
/**
|
|
1606
|
+
* Truncate tail output to at most `maxLines` lines and `maxChars`
|
|
1607
|
+
* characters. Takes the *last* N lines, then trims from the front if
|
|
1608
|
+
* the character budget is exceeded.
|
|
1609
|
+
*/
|
|
1610
|
+
function truncateTailOutput(raw, maxLines, maxChars) {
|
|
1611
|
+
const allLines = raw.split("\n");
|
|
1612
|
+
let result = (allLines.length > maxLines ? allLines.slice(-maxLines) : allLines).join("\n");
|
|
1613
|
+
if (result.length > maxChars) result = result.slice(-maxChars);
|
|
1614
|
+
return result;
|
|
1615
|
+
}
|
|
1616
|
+
function stringAttr(value, fallback) {
|
|
1617
|
+
if (typeof value !== "string" || value.length === 0) return fallback;
|
|
1618
|
+
return value.replaceAll("&", "&").replaceAll("\"", """);
|
|
1619
|
+
}
|
|
1881
1620
|
//#endregion
|
|
1882
|
-
//#region ../../packages/kimi-core/src/storage/
|
|
1621
|
+
//#region ../../packages/kimi-core/src/storage/state/projector.ts
|
|
1883
1622
|
/**
|
|
1884
|
-
*
|
|
1885
|
-
*
|
|
1623
|
+
* Pure read-side projector for provider-visible messages. It passes through
|
|
1624
|
+
* persisted history, merges adjacent user messages, filters incomplete
|
|
1625
|
+
* assistant placeholders, and injects ephemeral annotations without writing
|
|
1626
|
+
* anything back to the journal.
|
|
1886
1627
|
*
|
|
1887
|
-
*
|
|
1888
|
-
*
|
|
1889
|
-
|
|
1890
|
-
|
|
1628
|
+
* The system prompt is not injected here; it is forwarded separately through
|
|
1629
|
+
* provider chat parameters to avoid duplicate system messages.
|
|
1630
|
+
*/
|
|
1631
|
+
var DefaultConversationProjector = class {
|
|
1632
|
+
project(snapshot, ephemeralInjectionsOrOptions, options) {
|
|
1633
|
+
const ephemeralInjections = Array.isArray(ephemeralInjectionsOrOptions) ? ephemeralInjectionsOrOptions : [];
|
|
1634
|
+
const merged = mergeAdjacentUserMessages(snapshot.history.filter((m) => m.partial !== true && !(m.role === "assistant" && m.content.length === 0 && m.toolCalls.length === 0)));
|
|
1635
|
+
return [...ephemeralInjections.length === 0 ? [] : ephemeralInjections.map((injection) => renderInjection(injection)), ...merged];
|
|
1636
|
+
}
|
|
1637
|
+
};
|
|
1638
|
+
/**
|
|
1639
|
+
* Render an EphemeralInjection into a synthetic user message. System
|
|
1640
|
+
* reminders and pending notifications use XML wrappers so the model can
|
|
1641
|
+
* distinguish host annotations from genuine user text. `memory_recall`
|
|
1642
|
+
* stays as free text.
|
|
1891
1643
|
*
|
|
1892
|
-
*
|
|
1893
|
-
*
|
|
1894
|
-
*
|
|
1644
|
+
* The merge-guard logic downstream (`mergeAdjacentUserMessages`) uses
|
|
1645
|
+
* the `<notification ` / `<system-reminder>` opening tag to detect
|
|
1646
|
+
* these messages, so the exact tag names are load-bearing for
|
|
1647
|
+
* projector correctness — do not rename without also updating
|
|
1648
|
+
* `isInjectionUserMessage` below.
|
|
1649
|
+
*/
|
|
1650
|
+
function renderInjection(injection) {
|
|
1651
|
+
return {
|
|
1652
|
+
role: "user",
|
|
1653
|
+
content: [{
|
|
1654
|
+
type: "text",
|
|
1655
|
+
text: renderInjectionText(injection)
|
|
1656
|
+
}],
|
|
1657
|
+
toolCalls: []
|
|
1658
|
+
};
|
|
1659
|
+
}
|
|
1660
|
+
function renderInjectionText(injection) {
|
|
1661
|
+
const { kind, content } = injection;
|
|
1662
|
+
if (kind === "pending_notification") {
|
|
1663
|
+
if (typeof content === "string") return `<notification>\n${content}\n</notification>`;
|
|
1664
|
+
return renderNotificationXml(content);
|
|
1665
|
+
}
|
|
1666
|
+
if (kind === "system_reminder") return `<system-reminder>\n${typeof content === "string" ? content : JSON.stringify(content)}\n</system-reminder>`;
|
|
1667
|
+
return typeof content === "string" ? content : JSON.stringify(content);
|
|
1668
|
+
}
|
|
1669
|
+
/**
|
|
1670
|
+
* Detect whether a user message was produced by the ephemeral injection
|
|
1671
|
+
* pipeline (system_reminder or notification XML tag). Such messages
|
|
1672
|
+
* must never be merged with an adjacent real user turn — doing so would
|
|
1673
|
+
* smear the injection's XML wrapper into the user's actual prompt and
|
|
1674
|
+
* confuse the LLM about where the system annotation ends.
|
|
1895
1675
|
*
|
|
1896
|
-
* Crash recovery detects "wire.jsonl missing but wire.N.jsonl exists"
|
|
1897
|
-
* and rolls back the lowest-numbered archive to `wire.jsonl`.
|
|
1898
1676
|
*/
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
const
|
|
1902
|
-
|
|
1677
|
+
function isInjectionUserMessage(message) {
|
|
1678
|
+
if (message.role !== "user") return false;
|
|
1679
|
+
const trimmed = extractTextOnly(message).trimStart();
|
|
1680
|
+
if (trimmed.startsWith("<notification ")) return true;
|
|
1681
|
+
if (trimmed.startsWith("<system-reminder>")) return true;
|
|
1682
|
+
return false;
|
|
1683
|
+
}
|
|
1684
|
+
function mergeAdjacentUserMessages(history) {
|
|
1685
|
+
const out = [];
|
|
1686
|
+
for (const message of history) {
|
|
1687
|
+
const previous = out.at(-1);
|
|
1688
|
+
if (message.role === "user" && previous !== void 0 && previous.role === "user" && !isInjectionUserMessage(message) && !isInjectionUserMessage(previous)) {
|
|
1689
|
+
out[out.length - 1] = mergeTwoUserMessages(previous, message);
|
|
1690
|
+
continue;
|
|
1691
|
+
}
|
|
1692
|
+
out.push(cloneMessage(message));
|
|
1693
|
+
}
|
|
1694
|
+
return out;
|
|
1695
|
+
}
|
|
1696
|
+
function mergeTwoUserMessages(a, b) {
|
|
1697
|
+
const aText = extractTextOnly(a);
|
|
1698
|
+
const bText = extractTextOnly(b);
|
|
1699
|
+
const nonTextParts = [...a.content.filter((p) => p.type !== "text"), ...b.content.filter((p) => p.type !== "text")];
|
|
1700
|
+
return {
|
|
1701
|
+
role: "user",
|
|
1702
|
+
content: [{
|
|
1703
|
+
type: "text",
|
|
1704
|
+
text: `${aText}\n\n${bText}`
|
|
1705
|
+
}, ...nonTextParts],
|
|
1706
|
+
toolCalls: []
|
|
1707
|
+
};
|
|
1708
|
+
}
|
|
1709
|
+
function extractTextOnly(message) {
|
|
1710
|
+
return message.content.filter((p) => p.type === "text").map((p) => p.text).join("");
|
|
1711
|
+
}
|
|
1712
|
+
function cloneMessage(message) {
|
|
1713
|
+
return {
|
|
1714
|
+
role: message.role,
|
|
1715
|
+
name: message.name,
|
|
1716
|
+
content: message.content.map((p) => ({ ...p })),
|
|
1717
|
+
toolCalls: message.toolCalls.map((tc) => ({ ...tc })),
|
|
1718
|
+
toolCallId: message.toolCallId,
|
|
1719
|
+
partial: message.partial
|
|
1720
|
+
};
|
|
1721
|
+
}
|
|
1722
|
+
//#endregion
|
|
1723
|
+
//#region ../../packages/kimi-core/src/storage/state/record-builders.ts
|
|
1724
|
+
function buildUserMessageRecord(turnId, input) {
|
|
1725
|
+
return {
|
|
1726
|
+
type: "user_message",
|
|
1727
|
+
turn_id: turnId,
|
|
1728
|
+
content: input.parts !== void 0 ? input.parts : input.text
|
|
1729
|
+
};
|
|
1730
|
+
}
|
|
1731
|
+
function buildToolResultRecord(turnId, parentUuid, toolCallId, normalisedOutput, result) {
|
|
1732
|
+
return {
|
|
1733
|
+
type: "tool_result",
|
|
1734
|
+
turn_id: turnId,
|
|
1735
|
+
tool_call_id: toolCallId,
|
|
1736
|
+
output: normalisedOutput,
|
|
1737
|
+
parent_uuid: parentUuid,
|
|
1738
|
+
is_error: result.isError,
|
|
1739
|
+
synthetic: result.synthetic
|
|
1740
|
+
};
|
|
1741
|
+
}
|
|
1742
|
+
function buildStepBeginRecord(input) {
|
|
1743
|
+
return {
|
|
1744
|
+
type: "step_begin",
|
|
1745
|
+
uuid: input.uuid,
|
|
1746
|
+
turn_id: input.turnId,
|
|
1747
|
+
step: input.step
|
|
1748
|
+
};
|
|
1749
|
+
}
|
|
1750
|
+
function buildStepEndRecord(input) {
|
|
1751
|
+
return {
|
|
1752
|
+
type: "step_end",
|
|
1753
|
+
uuid: input.uuid,
|
|
1754
|
+
turn_id: input.turnId,
|
|
1755
|
+
step: input.step,
|
|
1756
|
+
usage: input.usage,
|
|
1757
|
+
finish_reason: input.finishReason
|
|
1758
|
+
};
|
|
1759
|
+
}
|
|
1760
|
+
function buildContentPartRecord(input) {
|
|
1761
|
+
return {
|
|
1762
|
+
type: "content_part",
|
|
1763
|
+
uuid: input.uuid,
|
|
1764
|
+
turn_id: input.turnId,
|
|
1765
|
+
step: input.step,
|
|
1766
|
+
step_uuid: input.stepUuid,
|
|
1767
|
+
role: "assistant",
|
|
1768
|
+
part: input.part.kind === "text" ? {
|
|
1769
|
+
kind: "text",
|
|
1770
|
+
text: input.part.text
|
|
1771
|
+
} : input.part.encrypted !== void 0 ? {
|
|
1772
|
+
kind: "think",
|
|
1773
|
+
think: input.part.think,
|
|
1774
|
+
encrypted: input.part.encrypted
|
|
1775
|
+
} : {
|
|
1776
|
+
kind: "think",
|
|
1777
|
+
think: input.part.think
|
|
1778
|
+
}
|
|
1779
|
+
};
|
|
1780
|
+
}
|
|
1781
|
+
function buildToolCallRecord(input, data) {
|
|
1782
|
+
return {
|
|
1783
|
+
type: "tool_call",
|
|
1784
|
+
uuid: input.uuid,
|
|
1785
|
+
turn_id: input.turnId,
|
|
1786
|
+
step: input.step,
|
|
1787
|
+
step_uuid: input.stepUuid,
|
|
1788
|
+
data
|
|
1789
|
+
};
|
|
1903
1790
|
}
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
function nextArchiveName(sessionDir, existingArchives) {
|
|
1910
|
-
let maxN = 0;
|
|
1911
|
-
for (const archive of existingArchives) {
|
|
1912
|
-
const n = extractArchiveNumber(archive);
|
|
1913
|
-
if (n > maxN) maxN = n;
|
|
1914
|
-
}
|
|
1915
|
-
return join(sessionDir, `wire.${maxN + 1}.jsonl`);
|
|
1791
|
+
function buildNotificationRecord(data) {
|
|
1792
|
+
return {
|
|
1793
|
+
type: "notification",
|
|
1794
|
+
data
|
|
1795
|
+
};
|
|
1916
1796
|
}
|
|
1917
|
-
|
|
1918
|
-
const { producer, protocolVersion, ...deps } = options;
|
|
1919
|
-
const version = protocolVersion ?? "2.1";
|
|
1920
|
-
const currentPath = join(sessionDir, "wire.jsonl");
|
|
1921
|
-
const archivePath = nextArchiveName(sessionDir, (await readdir(sessionDir)).filter((e) => ARCHIVE_PATTERN.test(e)));
|
|
1922
|
-
const renameFn = deps.renameFn ?? rename;
|
|
1923
|
-
const retryDelayMs = deps.retryDelayMs ?? 500;
|
|
1924
|
-
try {
|
|
1925
|
-
await renameFn(currentPath, archivePath);
|
|
1926
|
-
} catch (error) {
|
|
1927
|
-
const code = error.code;
|
|
1928
|
-
if (process.platform === "win32" && code === "EPERM") {
|
|
1929
|
-
await new Promise((resolve, reject) => {
|
|
1930
|
-
const timer = setTimeout(resolve, retryDelayMs);
|
|
1931
|
-
const abortSignal = deps.signal;
|
|
1932
|
-
if (abortSignal !== void 0) {
|
|
1933
|
-
const onAbort = () => {
|
|
1934
|
-
clearTimeout(timer);
|
|
1935
|
-
reject(abortSignal.reason ?? /* @__PURE__ */ new Error("aborted"));
|
|
1936
|
-
};
|
|
1937
|
-
if (abortSignal.aborted) onAbort();
|
|
1938
|
-
else abortSignal.addEventListener("abort", onAbort, { once: true });
|
|
1939
|
-
}
|
|
1940
|
-
});
|
|
1941
|
-
await renameFn(currentPath, archivePath);
|
|
1942
|
-
} else throw error;
|
|
1943
|
-
}
|
|
1944
|
-
await writeFileAtomicDurable(currentPath, JSON.stringify({
|
|
1945
|
-
type: "metadata",
|
|
1946
|
-
protocol_version: version,
|
|
1947
|
-
created_at: Date.now(),
|
|
1948
|
-
producer,
|
|
1949
|
-
kimi_version: producer.version
|
|
1950
|
-
}) + "\n");
|
|
1951
|
-
await syncDir(sessionDir);
|
|
1797
|
+
function buildSystemReminderRecord(data) {
|
|
1952
1798
|
return {
|
|
1953
|
-
|
|
1954
|
-
|
|
1799
|
+
type: "system_reminder",
|
|
1800
|
+
content: data.content
|
|
1955
1801
|
};
|
|
1956
1802
|
}
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1803
|
+
function buildClearRecord() {
|
|
1804
|
+
return { type: "context_cleared" };
|
|
1805
|
+
}
|
|
1806
|
+
function buildConfigChangeRecord(event) {
|
|
1807
|
+
switch (event.type) {
|
|
1808
|
+
case "system_prompt_changed": return {
|
|
1809
|
+
type: "system_prompt_changed",
|
|
1810
|
+
new_prompt: event.new_prompt
|
|
1811
|
+
};
|
|
1812
|
+
case "tools_changed": return {
|
|
1813
|
+
type: "tools_changed",
|
|
1814
|
+
operation: event.operation,
|
|
1815
|
+
tools: event.tools
|
|
1816
|
+
};
|
|
1970
1817
|
}
|
|
1971
|
-
return result !== void 0 ? {
|
|
1972
|
-
name: result,
|
|
1973
|
-
n: highestN
|
|
1974
|
-
} : void 0;
|
|
1975
1818
|
}
|
|
1819
|
+
function buildCompactionRecord(summary) {
|
|
1820
|
+
return {
|
|
1821
|
+
type: "compaction",
|
|
1822
|
+
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
|
+
pre_compact_tokens: summary.preCompactTokens,
|
|
1829
|
+
post_compact_tokens: summary.postCompactTokens,
|
|
1830
|
+
trigger: summary.trigger,
|
|
1831
|
+
archive_file: summary.archiveFile
|
|
1832
|
+
};
|
|
1833
|
+
}
|
|
1834
|
+
//#endregion
|
|
1835
|
+
//#region ../../packages/kimi-core/src/storage/state/context-state.ts
|
|
1976
1836
|
/**
|
|
1977
|
-
*
|
|
1978
|
-
*
|
|
1979
|
-
*
|
|
1980
|
-
*
|
|
1981
|
-
* 1. **wire.jsonl missing**: `wire.jsonl` was renamed to `wire.N.jsonl`
|
|
1982
|
-
* but the process crashed before the new `wire.jsonl` was created.
|
|
1983
|
-
* Recovery: roll back the highest-numbered archive to `wire.jsonl`.
|
|
1984
|
-
*
|
|
1985
|
-
* 2. **wire.jsonl is metadata-only** (Slice 3.3 / M04): the rename
|
|
1986
|
-
* succeeded AND the new `wire.jsonl` was created with a metadata
|
|
1987
|
-
* header, but the process crashed before `appendBoundary` ran. The
|
|
1988
|
-
* new file has only the metadata line — no session_initialized, no
|
|
1989
|
-
* compaction record. Recovery: remove the half-complete file and
|
|
1990
|
-
* roll back the highest archive.
|
|
1837
|
+
* Core ContextState logic. Both `WiredContextState` and
|
|
1838
|
+
* `InMemoryContextState` are thin wrappers that differ only in the journal
|
|
1839
|
+
* writer they supply and the constructor shape they expose.
|
|
1991
1840
|
*
|
|
1992
|
-
*
|
|
1993
|
-
*
|
|
1994
|
-
*
|
|
1995
|
-
*
|
|
1996
|
-
* orphaned — replay of the new wire would show an empty post-boundary
|
|
1997
|
-
* window and the archive would never be re-read. Recovery: same as
|
|
1998
|
-
* case 2 — remove the half-complete file and restore the archive.
|
|
1841
|
+
* WAL-then-mirror atomicity:
|
|
1842
|
+
* 1. build the WireRecord
|
|
1843
|
+
* 2. await journalWriter.append(...)
|
|
1844
|
+
* 3. on success only, update the in-memory projection
|
|
1999
1845
|
*
|
|
2000
|
-
*
|
|
1846
|
+
* If step 2 throws, the in-memory state is unchanged.
|
|
2001
1847
|
*/
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
1848
|
+
var BaseContextState = class {
|
|
1849
|
+
journalWriter;
|
|
1850
|
+
projector;
|
|
1851
|
+
currentTurnId;
|
|
1852
|
+
memory;
|
|
1853
|
+
_broken = false;
|
|
1854
|
+
_brokenError;
|
|
1855
|
+
/**
|
|
1856
|
+
* Marks all future write entry points as failed. Read methods remain
|
|
1857
|
+
* callable so callers can still inspect the last valid in-memory state.
|
|
1858
|
+
*/
|
|
1859
|
+
markBroken(error) {
|
|
1860
|
+
if (this._broken) return;
|
|
1861
|
+
this._broken = true;
|
|
1862
|
+
this._brokenError = error;
|
|
1863
|
+
}
|
|
1864
|
+
assertNotBroken() {
|
|
1865
|
+
if (this._broken) throw new ContextStateBrokenError("ContextState is broken due to a prior persist error", this._brokenError);
|
|
1866
|
+
}
|
|
1867
|
+
constructor(opts) {
|
|
1868
|
+
this.journalWriter = opts.journalWriter;
|
|
1869
|
+
this.projector = opts.projector ?? new DefaultConversationProjector();
|
|
1870
|
+
this.currentTurnId = opts.currentTurnId;
|
|
1871
|
+
this.memory = new ContextStateMemory(opts);
|
|
1872
|
+
}
|
|
1873
|
+
get model() {
|
|
1874
|
+
return this.memory.model;
|
|
1875
|
+
}
|
|
1876
|
+
get systemPrompt() {
|
|
1877
|
+
return this.memory.systemPrompt;
|
|
1878
|
+
}
|
|
1879
|
+
get activeTools() {
|
|
1880
|
+
return this.memory.activeTools;
|
|
1881
|
+
}
|
|
1882
|
+
get tokenCountWithPending() {
|
|
1883
|
+
return this.memory.tokenCountWithPending;
|
|
1884
|
+
}
|
|
1885
|
+
get thinkingLevel() {
|
|
1886
|
+
return this.memory.thinkingLevel;
|
|
1887
|
+
}
|
|
1888
|
+
get beforeStep() {
|
|
1889
|
+
return this.memory.beforeStep;
|
|
1890
|
+
}
|
|
1891
|
+
buildMessages() {
|
|
1892
|
+
return this.projector.project({
|
|
1893
|
+
history: this.memory.getHistory(),
|
|
1894
|
+
systemPrompt: this.memory.systemPrompt,
|
|
1895
|
+
model: this.memory.model,
|
|
1896
|
+
activeTools: this.memory.activeTools
|
|
1897
|
+
});
|
|
1898
|
+
}
|
|
1899
|
+
async applySteerMessages() {
|
|
1900
|
+
const steers = this.memory.drainSteerMessages();
|
|
1901
|
+
if (steers.length === 0) return false;
|
|
1902
|
+
this.assertNotBroken();
|
|
1903
|
+
for (const steer of steers) await this.appendUserMessage(steer);
|
|
2009
1904
|
return true;
|
|
2010
1905
|
}
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
1906
|
+
pushSteer(input) {
|
|
1907
|
+
this.memory.pushSteer(input);
|
|
1908
|
+
}
|
|
1909
|
+
setBeforeStepHook(fn) {
|
|
1910
|
+
this.memory.setBeforeStepHook(fn);
|
|
1911
|
+
}
|
|
1912
|
+
getHistory() {
|
|
1913
|
+
return this.memory.getHistory();
|
|
1914
|
+
}
|
|
1915
|
+
async appendUserMessage(input, turnIdOverride) {
|
|
1916
|
+
this.assertNotBroken();
|
|
1917
|
+
const turnId = turnIdOverride ?? this.currentTurnId();
|
|
1918
|
+
await this.journalWriter.append(buildUserMessageRecord(turnId, input));
|
|
1919
|
+
this.memory.appendUserContent(userInputToContentParts(input));
|
|
1920
|
+
}
|
|
1921
|
+
async appendToolResult(parentUuid, toolCallId, result) {
|
|
1922
|
+
this.assertNotBroken();
|
|
1923
|
+
const normalisedOutput = result.output === void 0 ? null : result.output;
|
|
1924
|
+
const turnId = this.currentTurnId();
|
|
1925
|
+
await this.journalWriter.append(buildToolResultRecord(turnId, parentUuid, toolCallId, normalisedOutput, result));
|
|
1926
|
+
this.memory.appendToolResult(toolCallId, toolResultOutputToContentParts(normalisedOutput));
|
|
1927
|
+
}
|
|
1928
|
+
async appendStepBegin(input) {
|
|
1929
|
+
this.assertNotBroken();
|
|
1930
|
+
await this.journalWriter.append(buildStepBeginRecord(input));
|
|
1931
|
+
this.memory.openStep(input.uuid);
|
|
1932
|
+
}
|
|
1933
|
+
async appendStepEnd(input) {
|
|
1934
|
+
this.assertNotBroken();
|
|
1935
|
+
await this.journalWriter.append(buildStepEndRecord(input));
|
|
1936
|
+
this.memory.closeStep(input.uuid, input.usage);
|
|
1937
|
+
}
|
|
1938
|
+
async appendContentPart(input) {
|
|
1939
|
+
this.assertNotBroken();
|
|
1940
|
+
if (this.memory.getOpenStep(input.stepUuid) === void 0) throw new Error(`appendContentPart: unknown stepUuid '${input.stepUuid}' (no open step_begin)`);
|
|
1941
|
+
await this.journalWriter.append(buildContentPartRecord(input));
|
|
1942
|
+
this.memory.appendOpenStepContent(input.stepUuid, atomicPartToContentPart(input.part));
|
|
1943
|
+
}
|
|
1944
|
+
async appendToolCall(input) {
|
|
1945
|
+
this.assertNotBroken();
|
|
1946
|
+
if (this.memory.getOpenStep(input.stepUuid) === void 0) throw new Error(`appendToolCall: unknown stepUuid '${input.stepUuid}' (no open step_begin)`);
|
|
1947
|
+
const data = sanitizeToolCallData(input.data);
|
|
1948
|
+
await this.journalWriter.append(buildToolCallRecord(input, data));
|
|
1949
|
+
this.memory.appendOpenStepToolCall(input.stepUuid, toolCallDataToKosongToolCall(input.data));
|
|
1950
|
+
}
|
|
1951
|
+
async appendNotification(data) {
|
|
1952
|
+
this.assertNotBroken();
|
|
1953
|
+
await this.journalWriter.append(buildNotificationRecord(data));
|
|
1954
|
+
const text = renderNotificationXml(data);
|
|
1955
|
+
this.memory.appendUserContent([{
|
|
1956
|
+
type: "text",
|
|
1957
|
+
text
|
|
1958
|
+
}]);
|
|
1959
|
+
}
|
|
1960
|
+
async appendSystemReminder(data) {
|
|
1961
|
+
this.assertNotBroken();
|
|
1962
|
+
await this.journalWriter.append(buildSystemReminderRecord(data));
|
|
1963
|
+
const text = `<system-reminder>\n${data.content}\n</system-reminder>`;
|
|
1964
|
+
this.memory.appendUserContent([{
|
|
1965
|
+
type: "text",
|
|
1966
|
+
text
|
|
1967
|
+
}]);
|
|
1968
|
+
}
|
|
1969
|
+
async clear() {
|
|
1970
|
+
this.assertNotBroken();
|
|
1971
|
+
await this.journalWriter.append(buildClearRecord());
|
|
1972
|
+
this.memory.clearConversation();
|
|
1973
|
+
}
|
|
1974
|
+
async applyConfigChange(event) {
|
|
1975
|
+
this.assertNotBroken();
|
|
1976
|
+
switch (event.type) {
|
|
1977
|
+
case "system_prompt_changed":
|
|
1978
|
+
await this.journalWriter.append(buildConfigChangeRecord(event));
|
|
1979
|
+
this.memory.setSystemPrompt(event.new_prompt);
|
|
1980
|
+
return;
|
|
1981
|
+
case "tools_changed":
|
|
1982
|
+
await this.journalWriter.append(buildConfigChangeRecord(event));
|
|
1983
|
+
this.memory.applyToolsChanged(event.operation, event.tools);
|
|
2031
1984
|
}
|
|
2032
1985
|
}
|
|
2033
|
-
|
|
2034
|
-
|
|
1986
|
+
setModel(model) {
|
|
1987
|
+
this.memory.setModel(model);
|
|
1988
|
+
}
|
|
1989
|
+
setThinkingLevel(level) {
|
|
1990
|
+
this.memory.setThinkingLevel(level);
|
|
1991
|
+
}
|
|
1992
|
+
async resetToSummary(summary) {
|
|
1993
|
+
this.assertNotBroken();
|
|
1994
|
+
await this.journalWriter.append(buildCompactionRecord(summary));
|
|
1995
|
+
this.memory.resetToSummary(summary);
|
|
1996
|
+
}
|
|
1997
|
+
};
|
|
1998
|
+
var WiredContextState = class extends BaseContextState {};
|
|
1999
|
+
var InMemoryContextState = class extends BaseContextState {
|
|
2000
|
+
constructor(opts) {
|
|
2001
|
+
super({
|
|
2002
|
+
...opts,
|
|
2003
|
+
currentTurnId: opts.currentTurnId ?? (() => "embedded"),
|
|
2004
|
+
journalWriter: new NoopJournalWriter()
|
|
2005
|
+
});
|
|
2006
|
+
}
|
|
2007
|
+
};
|
|
2008
|
+
//#endregion
|
|
2009
|
+
//#region ../../packages/kimi-core/src/storage/state/session-journal.ts
|
|
2010
|
+
/**
|
|
2011
|
+
* Production `SessionJournal` implementation — delegates every append to the
|
|
2012
|
+
* shared `JournalWriter`, which in turn serialises to wire.jsonl.
|
|
2013
|
+
*
|
|
2014
|
+
* Does not mutate any conversation projection state; management records do
|
|
2015
|
+
* not change buildMessages().
|
|
2016
|
+
*/
|
|
2017
|
+
var WiredSessionJournalImpl = class {
|
|
2018
|
+
constructor(journalWriter) {
|
|
2019
|
+
this.journalWriter = journalWriter;
|
|
2020
|
+
}
|
|
2021
|
+
async appendTurnBegin(data) {
|
|
2022
|
+
await this.journalWriter.append(data);
|
|
2023
|
+
}
|
|
2024
|
+
async appendTurnEnd(data) {
|
|
2025
|
+
await this.journalWriter.append(data);
|
|
2026
|
+
}
|
|
2027
|
+
async appendSkillInvoked(data) {
|
|
2028
|
+
await this.journalWriter.append(data);
|
|
2029
|
+
}
|
|
2030
|
+
async appendSkillCompleted(data) {
|
|
2031
|
+
await this.journalWriter.append(data);
|
|
2032
|
+
}
|
|
2033
|
+
async appendApprovalRequest(data) {
|
|
2034
|
+
await this.journalWriter.append(data);
|
|
2035
|
+
}
|
|
2036
|
+
async appendApprovalResponse(data) {
|
|
2037
|
+
await this.journalWriter.append(data);
|
|
2038
|
+
}
|
|
2039
|
+
async appendTeamMail(data) {
|
|
2040
|
+
await this.journalWriter.append(data);
|
|
2041
|
+
}
|
|
2042
|
+
async appendToolDenied(data) {
|
|
2043
|
+
await this.journalWriter.append(data);
|
|
2044
|
+
}
|
|
2045
|
+
async appendSubagentSpawned(data) {
|
|
2046
|
+
await this.journalWriter.append(data);
|
|
2047
|
+
}
|
|
2048
|
+
async appendSubagentCompleted(data) {
|
|
2049
|
+
await this.journalWriter.append(data);
|
|
2050
|
+
}
|
|
2051
|
+
async appendSubagentFailed(data) {
|
|
2052
|
+
await this.journalWriter.append(data);
|
|
2053
|
+
}
|
|
2054
|
+
async appendOwnershipChanged(data) {
|
|
2055
|
+
await this.journalWriter.append(data);
|
|
2056
|
+
}
|
|
2057
|
+
};
|
|
2035
2058
|
//#endregion
|
|
2036
2059
|
//#region ../../packages/kimi-core/src/storage/fs/atomic-write.ts
|
|
2037
2060
|
/**
|
|
@@ -2599,10 +2622,8 @@ async function raceExecuteWithGraceTimeout(executePromise, signal, toolName) {
|
|
|
2599
2622
|
* later abort during tool execution does not lose the LLM usage that was
|
|
2600
2623
|
* already spent.
|
|
2601
2624
|
*/
|
|
2602
|
-
const UNKNOWN_TURN_ID = "unknown-turn";
|
|
2603
2625
|
async function executeSoulStep(deps) {
|
|
2604
|
-
const { config, context, runtime, signal, overrides, currentStep, emit, recordUsage } = deps;
|
|
2605
|
-
const turnId = context.currentTurnId?.() ?? UNKNOWN_TURN_ID;
|
|
2626
|
+
const { config, context, turnId, runtime, signal, overrides, currentStep, emit, recordUsage } = deps;
|
|
2606
2627
|
if (config.beforeStep !== void 0) {
|
|
2607
2628
|
const beforeStep = await config.beforeStep({
|
|
2608
2629
|
turnId,
|
|
@@ -2700,6 +2721,7 @@ function toStepEndUsage(usage) {
|
|
|
2700
2721
|
//#endregion
|
|
2701
2722
|
//#region ../../packages/kimi-core/src/soul/run-turn.ts
|
|
2702
2723
|
const DEFAULT_MAX_STEPS = 100;
|
|
2724
|
+
const UNKNOWN_TURN_ID = "unknown-turn";
|
|
2703
2725
|
async function runSoulTurn(config, context, runtime, sink, signal, overrides) {
|
|
2704
2726
|
const maxSteps = config.maxSteps ?? DEFAULT_MAX_STEPS;
|
|
2705
2727
|
const usage = {
|
|
@@ -2710,6 +2732,7 @@ async function runSoulTurn(config, context, runtime, sink, signal, overrides) {
|
|
|
2710
2732
|
};
|
|
2711
2733
|
let steps = 0;
|
|
2712
2734
|
let stopReason = "end_turn";
|
|
2735
|
+
const turnId = context.currentTurnId?.() ?? UNKNOWN_TURN_ID;
|
|
2713
2736
|
try {
|
|
2714
2737
|
while (true) {
|
|
2715
2738
|
signal.throwIfAborted();
|
|
@@ -2721,6 +2744,7 @@ async function runSoulTurn(config, context, runtime, sink, signal, overrides) {
|
|
|
2721
2744
|
steps += 1;
|
|
2722
2745
|
const stepResult = await executeSoulStep({
|
|
2723
2746
|
config,
|
|
2747
|
+
turnId,
|
|
2724
2748
|
context,
|
|
2725
2749
|
runtime,
|
|
2726
2750
|
signal,
|
|
@@ -2735,7 +2759,12 @@ async function runSoulTurn(config, context, runtime, sink, signal, overrides) {
|
|
|
2735
2759
|
});
|
|
2736
2760
|
if (stepResult.stopReason === "tool_use") continue;
|
|
2737
2761
|
stopReason = stepResult.stopReason;
|
|
2738
|
-
if (!(await config.beforeTurnCompletes?.(
|
|
2762
|
+
if (!(await config.beforeTurnCompletes?.({
|
|
2763
|
+
...stepResult,
|
|
2764
|
+
turnId,
|
|
2765
|
+
stepNumber: steps,
|
|
2766
|
+
context
|
|
2767
|
+
}))?.continue) break;
|
|
2739
2768
|
}
|
|
2740
2769
|
} catch (error) {
|
|
2741
2770
|
if (isAbortError$3(error) || signal.aborted) {
|
|
@@ -3065,6 +3094,25 @@ var SubagentTooDeepError = class extends Error {
|
|
|
3065
3094
|
this.limit = limit;
|
|
3066
3095
|
}
|
|
3067
3096
|
};
|
|
3097
|
+
function classifyBusinessError(error) {
|
|
3098
|
+
if (error instanceof LLMNotSetError) return {
|
|
3099
|
+
code: -32001,
|
|
3100
|
+
message: error.message
|
|
3101
|
+
};
|
|
3102
|
+
if (error instanceof LLMCapabilityMismatchError) return {
|
|
3103
|
+
code: -32002,
|
|
3104
|
+
message: error.message
|
|
3105
|
+
};
|
|
3106
|
+
if (error instanceof ProviderError) return {
|
|
3107
|
+
code: -32003,
|
|
3108
|
+
message: error.message
|
|
3109
|
+
};
|
|
3110
|
+
if (error instanceof Error && /provider|backend|upstream/i.test(error.message)) return {
|
|
3111
|
+
code: -32003,
|
|
3112
|
+
message: error.message
|
|
3113
|
+
};
|
|
3114
|
+
return null;
|
|
3115
|
+
}
|
|
3068
3116
|
//#endregion
|
|
3069
3117
|
//#region ../../packages/kimi-core/src/soul-plus/soul-registry.ts
|
|
3070
3118
|
/**
|
|
@@ -5721,14 +5769,19 @@ function registerInteractiveTools(options) {
|
|
|
5721
5769
|
* precomputed `tokenCountWithPending` off `SoulContextState`.
|
|
5722
5770
|
*/
|
|
5723
5771
|
/**
|
|
5724
|
-
* Estimate token count from text using a character-based heuristic
|
|
5725
|
-
* (~4 chars per token
|
|
5726
|
-
*
|
|
5772
|
+
* Estimate token count from text using a character-based heuristic.
|
|
5773
|
+
* - ASCII (~4 chars per token)
|
|
5774
|
+
* - CJK and other non-ASCII (~1 char per token)
|
|
5775
|
+
* The estimate is transient — the next LLM call returns the real count
|
|
5727
5776
|
* and supersedes this value. Used to keep `tokenCountWithPending`
|
|
5728
5777
|
* monotonic between LLM round-trips without paying for a tokenizer.
|
|
5729
5778
|
*/
|
|
5730
5779
|
function estimateTokens(text) {
|
|
5731
|
-
|
|
5780
|
+
let asciiCount = 0;
|
|
5781
|
+
let nonAsciiCount = 0;
|
|
5782
|
+
for (const char of text) if (char.codePointAt(0) <= 127) asciiCount++;
|
|
5783
|
+
else nonAsciiCount++;
|
|
5784
|
+
return Math.ceil(asciiCount / 4) + nonAsciiCount;
|
|
5732
5785
|
}
|
|
5733
5786
|
//#endregion
|
|
5734
5787
|
//#region ../../packages/kimi-core/src/soul-plus/compaction/compaction-orchestrator.ts
|
|
@@ -5744,7 +5797,7 @@ var CompactionOrchestrator = class {
|
|
|
5744
5797
|
* 2. emit compaction.begin
|
|
5745
5798
|
* 3. provider.run(messages, signal, {userInstructions?})
|
|
5746
5799
|
* 4. journalWriter.flush() (Phase 3 铁律 — before rotate)
|
|
5747
|
-
* 5.
|
|
5800
|
+
* 5. journalWriter.rotate(...)
|
|
5748
5801
|
* 6. contextState.resetToSummary(storageSummary)
|
|
5749
5802
|
* 7. emit compaction.end
|
|
5750
5803
|
* 8. finally: transitionTo('active')
|
|
@@ -5758,6 +5811,8 @@ var CompactionOrchestrator = class {
|
|
|
5758
5811
|
* CompactionRecord.
|
|
5759
5812
|
*/
|
|
5760
5813
|
async executeCompaction(signal, customInstruction, trigger = "auto") {
|
|
5814
|
+
const rotateCapability = this.deps.journalWriter.rotateCapability;
|
|
5815
|
+
if (!rotateCapability) throw new Error("JournalWriter missing rotate capability");
|
|
5761
5816
|
const machine = this.deps.lifecycleStateMachine;
|
|
5762
5817
|
machine.transitionTo("compacting");
|
|
5763
5818
|
let tailUserText;
|
|
@@ -5768,22 +5823,17 @@ var CompactionOrchestrator = class {
|
|
|
5768
5823
|
const preCompactTokens = this.deps.contextState.tokenCountWithPending;
|
|
5769
5824
|
const summary = await (this.deps.runtimeSlot?.current().compactionProvider ?? this.deps.compactionProvider).run(messages, signal, customInstruction !== void 0 ? { userInstructions: customInstruction } : void 0);
|
|
5770
5825
|
signal.throwIfAborted();
|
|
5771
|
-
const baselineInit = await
|
|
5826
|
+
const baselineInit = await rotateCapability.readSessionInitialized();
|
|
5772
5827
|
const cs = this.deps.contextState;
|
|
5773
5828
|
const sessionInitialized = applyRuntimeOverlay(baselineInit, {
|
|
5774
5829
|
system_prompt: cs.systemPrompt,
|
|
5775
5830
|
active_tools: [...cs.activeTools]
|
|
5776
5831
|
});
|
|
5777
|
-
await
|
|
5778
|
-
|
|
5779
|
-
type: "compaction_boundary",
|
|
5780
|
-
summary,
|
|
5781
|
-
parent_file: ""
|
|
5782
|
-
});
|
|
5783
|
-
await this.deps.journalCapability.appendBoundary(sessionInitialized);
|
|
5784
|
-
const storageSummary = bridgeSummaryMessage(summary, messages.length, preCompactTokens, trigger, rotateResult.archiveFile);
|
|
5785
|
-
await this.deps.contextState.resetToSummary(storageSummary);
|
|
5832
|
+
const rotateResult = await rotateCapability.rotate();
|
|
5833
|
+
await rotateCapability.appendBoundary(sessionInitialized);
|
|
5786
5834
|
tailUserText = extractUnpairedTailUserText(messages);
|
|
5835
|
+
const storageSummary = bridgeSummaryMessage(summary, messages.length, preCompactTokens, trigger, rotateResult.archivePath, this.deps.contextState.systemPrompt, tailUserText);
|
|
5836
|
+
await this.deps.contextState.resetToSummary(storageSummary);
|
|
5787
5837
|
this.deps.sink.emit({
|
|
5788
5838
|
type: "compaction.end",
|
|
5789
5839
|
tokensBefore: preCompactTokens,
|
|
@@ -5887,7 +5937,10 @@ function extractUnpairedTailUserText(messages) {
|
|
|
5887
5937
|
return parts.length > 0 ? parts.join("") : void 0;
|
|
5888
5938
|
}
|
|
5889
5939
|
}
|
|
5890
|
-
function bridgeSummaryMessage(providerSummary, messagesCount, preCompactTokens, trigger,
|
|
5940
|
+
function bridgeSummaryMessage(providerSummary, messagesCount, preCompactTokens, trigger, archivePath, systemPrompt, tailUserText) {
|
|
5941
|
+
const summaryTokens = estimateTokens(providerSummary.content);
|
|
5942
|
+
const systemTokens = systemPrompt ? estimateTokens(systemPrompt) : 0;
|
|
5943
|
+
const tailTokens = tailUserText ? estimateTokens(tailUserText) : 0;
|
|
5891
5944
|
return {
|
|
5892
5945
|
summary: providerSummary.content,
|
|
5893
5946
|
compactedRange: {
|
|
@@ -5896,9 +5949,9 @@ function bridgeSummaryMessage(providerSummary, messagesCount, preCompactTokens,
|
|
|
5896
5949
|
messageCount: messagesCount
|
|
5897
5950
|
},
|
|
5898
5951
|
preCompactTokens,
|
|
5899
|
-
postCompactTokens:
|
|
5952
|
+
postCompactTokens: summaryTokens + systemTokens + tailTokens,
|
|
5900
5953
|
trigger,
|
|
5901
|
-
|
|
5954
|
+
archivePath
|
|
5902
5955
|
};
|
|
5903
5956
|
}
|
|
5904
5957
|
//#endregion
|
|
@@ -5924,7 +5977,6 @@ function createSoulPlusCompactionOrchestrator(deps) {
|
|
|
5924
5977
|
compactionProvider: deps.compactionProvider,
|
|
5925
5978
|
runtimeSlot: deps.runtimeSlot,
|
|
5926
5979
|
lifecycleStateMachine: deps.lifecycleStateMachine,
|
|
5927
|
-
journalCapability: deps.journalCapability,
|
|
5928
5980
|
sink: deps.sink,
|
|
5929
5981
|
journalWriter: deps.journalWriter,
|
|
5930
5982
|
sessionId: deps.sessionId,
|
|
@@ -6922,53 +6974,84 @@ function toWirePatch(p) {
|
|
|
6922
6974
|
return out;
|
|
6923
6975
|
}
|
|
6924
6976
|
//#endregion
|
|
6925
|
-
//#region ../../packages/kimi-core/src/soul-plus/
|
|
6926
|
-
|
|
6927
|
-
|
|
6928
|
-
|
|
6929
|
-
|
|
6930
|
-
|
|
6931
|
-
|
|
6932
|
-
|
|
6933
|
-
|
|
6934
|
-
|
|
6935
|
-
|
|
6936
|
-
|
|
6937
|
-
|
|
6938
|
-
|
|
6939
|
-
|
|
6940
|
-
|
|
6941
|
-
|
|
6942
|
-
|
|
6943
|
-
|
|
6977
|
+
//#region ../../packages/kimi-core/src/soul-plus/injection/types.ts
|
|
6978
|
+
/**
|
|
6979
|
+
* Extract the text content from a history message.
|
|
6980
|
+
*/
|
|
6981
|
+
function historyMessageText(msg) {
|
|
6982
|
+
return msg.content.filter((p) => p.type === "text" && typeof p.text === "string").map((p) => p.text).join("");
|
|
6983
|
+
}
|
|
6984
|
+
/**
|
|
6985
|
+
* Scan history backwards and classify the plan-mode dedup state.
|
|
6986
|
+
*
|
|
6987
|
+
* The scan stops at the first `user` message it encounters going
|
|
6988
|
+
* backwards:
|
|
6989
|
+
* - If that user message is a plan-mode reminder → returns
|
|
6990
|
+
* `{found:true, newUserSinceReminder:false, assistantTurnsSince}`.
|
|
6991
|
+
* The caller decides dedup vs sparse vs full by consulting the
|
|
6992
|
+
* assistant-turn counter.
|
|
6993
|
+
* - If that user message is a real user prompt (not a reminder) →
|
|
6994
|
+
* returns `{found:false, newUserSinceReminder:true, …}`. The caller
|
|
6995
|
+
* treats this as a fresh turn and emits the full reminder.
|
|
6996
|
+
* - If no user message exists at all → returns
|
|
6997
|
+
* `{found:false, newUserSinceReminder:false, …}`. First-time
|
|
6998
|
+
* injection.
|
|
6999
|
+
*
|
|
7000
|
+
* Only counts ASSISTANT messages between the tail and the reminder.
|
|
7001
|
+
* Non-reminder user messages never appear inside that window because
|
|
7002
|
+
* the scan stops at them.
|
|
7003
|
+
*/
|
|
7004
|
+
function scanPlanReminderHistory(history) {
|
|
7005
|
+
let assistantTurnsSince = 0;
|
|
7006
|
+
for (let i = history.length - 1; i >= 0; i--) {
|
|
7007
|
+
const msg = history[i];
|
|
7008
|
+
if (msg === void 0) continue;
|
|
7009
|
+
if (msg.role === "assistant") {
|
|
7010
|
+
assistantTurnsSince += 1;
|
|
7011
|
+
continue;
|
|
6944
7012
|
}
|
|
6945
|
-
|
|
6946
|
-
|
|
6947
|
-
|
|
6948
|
-
|
|
6949
|
-
|
|
6950
|
-
|
|
6951
|
-
|
|
6952
|
-
|
|
6953
|
-
|
|
6954
|
-
|
|
6955
|
-
|
|
6956
|
-
* Collect every active injection for the upcoming LLM step. Providers
|
|
6957
|
-
* are iterated in registration order; a provider that throws is
|
|
6958
|
-
* isolated (error forwarded to `onProviderError`, remaining providers
|
|
6959
|
-
* still run). Same error-isolation pattern as HookEngine / SessionEventBus.
|
|
6960
|
-
*/
|
|
6961
|
-
computeInjections(ctx, contextState) {
|
|
6962
|
-
const out = [];
|
|
6963
|
-
for (const provider of this.providers) try {
|
|
6964
|
-
const injections = provider.getInjections(ctx, contextState);
|
|
6965
|
-
if (injections !== void 0 && injections !== null && Array.isArray(injections) && injections.length > 0) out.push(...injections);
|
|
6966
|
-
} catch (error) {
|
|
6967
|
-
this.onProviderError?.(provider, error instanceof Error ? error : new Error(String(error)));
|
|
7013
|
+
if (msg.role === "user") {
|
|
7014
|
+
if (isPlanReminderMessage(msg)) return {
|
|
7015
|
+
found: true,
|
|
7016
|
+
newUserSinceReminder: false,
|
|
7017
|
+
assistantTurnsSince
|
|
7018
|
+
};
|
|
7019
|
+
return {
|
|
7020
|
+
found: false,
|
|
7021
|
+
newUserSinceReminder: true,
|
|
7022
|
+
assistantTurnsSince
|
|
7023
|
+
};
|
|
6968
7024
|
}
|
|
6969
|
-
return out;
|
|
6970
7025
|
}
|
|
6971
|
-
|
|
7026
|
+
return {
|
|
7027
|
+
found: false,
|
|
7028
|
+
newUserSinceReminder: false,
|
|
7029
|
+
assistantTurnsSince
|
|
7030
|
+
};
|
|
7031
|
+
}
|
|
7032
|
+
function isPlanReminderMessage(msg) {
|
|
7033
|
+
const text = historyMessageText(msg);
|
|
7034
|
+
if (!text.trimStart().startsWith("<system-reminder>")) return false;
|
|
7035
|
+
return text.includes("Plan mode is active") || text.includes("Plan mode still active") || text.includes("Re-entering Plan Mode");
|
|
7036
|
+
}
|
|
7037
|
+
/**
|
|
7038
|
+
* Same as above but for yolo mode reminder.
|
|
7039
|
+
*/
|
|
7040
|
+
function hasYoloReminderWithoutNewUser(history) {
|
|
7041
|
+
return hasReminderWithoutNewUser(history, "yolo");
|
|
7042
|
+
}
|
|
7043
|
+
function hasReminderWithoutNewUser(history, fingerprint) {
|
|
7044
|
+
for (let i = history.length - 1; i >= 0; i--) {
|
|
7045
|
+
const msg = history[i];
|
|
7046
|
+
if (msg === void 0) continue;
|
|
7047
|
+
const text = historyMessageText(msg);
|
|
7048
|
+
if (msg.role === "user" && text.trimStart().startsWith("<system-reminder>") && text.toLowerCase().includes(fingerprint.toLowerCase())) return true;
|
|
7049
|
+
if (msg.role === "user" && !text.trimStart().startsWith("<system-reminder>") && !text.trimStart().startsWith("<notification")) return false;
|
|
7050
|
+
}
|
|
7051
|
+
return false;
|
|
7052
|
+
}
|
|
7053
|
+
//#endregion
|
|
7054
|
+
//#region ../../packages/kimi-core/src/soul-plus/injection/plan-mode.ts
|
|
6972
7055
|
/**
|
|
6973
7056
|
* Phase 18 §D.6 — reminder cadence constants.
|
|
6974
7057
|
*
|
|
@@ -7033,7 +7116,7 @@ var PlanModeInjectionProvider = class {
|
|
|
7033
7116
|
if (scan.assistantTurnsSince >= PLAN_MODE_DEDUP_MIN_TURNS) return "sparse";
|
|
7034
7117
|
return null;
|
|
7035
7118
|
}
|
|
7036
|
-
getInjections(ctx
|
|
7119
|
+
getInjections(ctx) {
|
|
7037
7120
|
if (!ctx.planMode) {
|
|
7038
7121
|
this.pendingReentry = false;
|
|
7039
7122
|
return [];
|
|
@@ -7041,23 +7124,19 @@ var PlanModeInjectionProvider = class {
|
|
|
7041
7124
|
const planFilePath = ctx.planFilePath;
|
|
7042
7125
|
if (this.pendingReentry) {
|
|
7043
7126
|
this.pendingReentry = false;
|
|
7044
|
-
return
|
|
7127
|
+
return [{
|
|
7128
|
+
kind: "system_reminder",
|
|
7129
|
+
content: reentryReminder(planFilePath)
|
|
7130
|
+
}];
|
|
7045
7131
|
}
|
|
7046
7132
|
const variant = this.getVariant(ctx);
|
|
7047
7133
|
if (variant === null) return [];
|
|
7048
|
-
return
|
|
7134
|
+
return [{
|
|
7135
|
+
kind: "system_reminder",
|
|
7136
|
+
content: variant === "full" ? fullReminder(planFilePath) : variant === "sparse" ? sparseReminder(planFilePath) : reentryReminder(planFilePath)
|
|
7137
|
+
}];
|
|
7049
7138
|
}
|
|
7050
7139
|
};
|
|
7051
|
-
function emit$1(contextState, content) {
|
|
7052
|
-
if (contextState !== void 0) {
|
|
7053
|
-
contextState.appendSystemReminder({ content });
|
|
7054
|
-
return;
|
|
7055
|
-
}
|
|
7056
|
-
return [{
|
|
7057
|
-
kind: "system_reminder",
|
|
7058
|
-
content
|
|
7059
|
-
}];
|
|
7060
|
-
}
|
|
7061
7140
|
/**
|
|
7062
7141
|
* Phase 18 §D.7 — when the host has resolved the session's plan-file
|
|
7063
7142
|
* path, append a `Plan file: {path}` footer so the LLM can target
|
|
@@ -7166,6 +7245,13 @@ function inlineReentryReminder() {
|
|
|
7166
7245
|
"Your turn must end with either AskUserQuestion (to clarify requirements) or ExitPlanMode (to request plan approval)."
|
|
7167
7246
|
].join("\n");
|
|
7168
7247
|
}
|
|
7248
|
+
//#endregion
|
|
7249
|
+
//#region ../../packages/kimi-core/src/soul-plus/injection/yolo-mode.ts
|
|
7250
|
+
const YOLO_MODE_REMINDER = [
|
|
7251
|
+
"You are running in non-interactive (yolo) mode. The user cannot answer questions or provide feedback during execution.",
|
|
7252
|
+
" - Do NOT call AskUserQuestion. If you need to make a decision, make your best judgment and proceed.",
|
|
7253
|
+
" - For ExitPlanMode, it will be auto-approved. You can use it normally but expect no user feedback."
|
|
7254
|
+
].join("\n");
|
|
7169
7255
|
/**
|
|
7170
7256
|
* Yolo-mode reminder ported from Python `yolo_mode.py`. One-shot per
|
|
7171
7257
|
* activation: the first turn after `permissionMode` flips to
|
|
@@ -7176,7 +7262,7 @@ function inlineReentryReminder() {
|
|
|
7176
7262
|
var YoloModeInjectionProvider = class {
|
|
7177
7263
|
id = "yolo_mode";
|
|
7178
7264
|
injected = false;
|
|
7179
|
-
getInjections(ctx
|
|
7265
|
+
getInjections(ctx) {
|
|
7180
7266
|
if (ctx.permissionMode !== "bypassPermissions") {
|
|
7181
7267
|
this.injected = false;
|
|
7182
7268
|
return [];
|
|
@@ -7184,101 +7270,62 @@ var YoloModeInjectionProvider = class {
|
|
|
7184
7270
|
if (ctx.history !== void 0 && hasYoloReminderWithoutNewUser(ctx.history)) return [];
|
|
7185
7271
|
if (this.injected) return [];
|
|
7186
7272
|
this.injected = true;
|
|
7187
|
-
if (contextState !== void 0) {
|
|
7188
|
-
contextState.appendSystemReminder({ content: YOLO_MODE_REMINDER });
|
|
7189
|
-
return;
|
|
7190
|
-
}
|
|
7191
7273
|
return [{
|
|
7192
7274
|
kind: "system_reminder",
|
|
7193
7275
|
content: YOLO_MODE_REMINDER
|
|
7194
7276
|
}];
|
|
7195
7277
|
}
|
|
7196
7278
|
};
|
|
7197
|
-
|
|
7198
|
-
|
|
7199
|
-
|
|
7200
|
-
|
|
7201
|
-
|
|
7202
|
-
|
|
7203
|
-
|
|
7204
|
-
|
|
7205
|
-
|
|
7206
|
-
|
|
7207
|
-
|
|
7208
|
-
|
|
7209
|
-
*
|
|
7210
|
-
*
|
|
7211
|
-
|
|
7212
|
-
|
|
7213
|
-
|
|
7214
|
-
|
|
7215
|
-
|
|
7216
|
-
|
|
7217
|
-
* backwards:
|
|
7218
|
-
* - If that user message is a plan-mode reminder → returns
|
|
7219
|
-
* `{found:true, newUserSinceReminder:false, assistantTurnsSince}`.
|
|
7220
|
-
* The caller decides dedup vs sparse vs full by consulting the
|
|
7221
|
-
* assistant-turn counter.
|
|
7222
|
-
* - If that user message is a real user prompt (not a reminder) →
|
|
7223
|
-
* returns `{found:false, newUserSinceReminder:true, …}`. The caller
|
|
7224
|
-
* treats this as a fresh turn and emits the full reminder.
|
|
7225
|
-
* - If no user message exists at all → returns
|
|
7226
|
-
* `{found:false, newUserSinceReminder:false, …}`. First-time
|
|
7227
|
-
* injection.
|
|
7228
|
-
*
|
|
7229
|
-
* Only counts ASSISTANT messages between the tail and the reminder.
|
|
7230
|
-
* Non-reminder user messages never appear inside that window because
|
|
7231
|
-
* the scan stops at them.
|
|
7232
|
-
*/
|
|
7233
|
-
function scanPlanReminderHistory(history) {
|
|
7234
|
-
let assistantTurnsSince = 0;
|
|
7235
|
-
for (let i = history.length - 1; i >= 0; i--) {
|
|
7236
|
-
const msg = history[i];
|
|
7237
|
-
if (msg === void 0) continue;
|
|
7238
|
-
if (msg.role === "assistant") {
|
|
7239
|
-
assistantTurnsSince += 1;
|
|
7240
|
-
continue;
|
|
7241
|
-
}
|
|
7242
|
-
if (msg.role === "user") {
|
|
7243
|
-
if (isPlanReminderMessage(msg)) return {
|
|
7244
|
-
found: true,
|
|
7245
|
-
newUserSinceReminder: false,
|
|
7246
|
-
assistantTurnsSince
|
|
7247
|
-
};
|
|
7248
|
-
return {
|
|
7249
|
-
found: false,
|
|
7250
|
-
newUserSinceReminder: true,
|
|
7251
|
-
assistantTurnsSince
|
|
7252
|
-
};
|
|
7279
|
+
//#endregion
|
|
7280
|
+
//#region ../../packages/kimi-core/src/soul-plus/injection/manager.ts
|
|
7281
|
+
var DynamicInjectionManager = class {
|
|
7282
|
+
providers = [];
|
|
7283
|
+
onProviderError;
|
|
7284
|
+
constructor(options = {}) {
|
|
7285
|
+
if (options.initialProviders !== void 0) for (const provider of options.initialProviders) this.register(provider);
|
|
7286
|
+
this.onProviderError = options.onProviderError;
|
|
7287
|
+
}
|
|
7288
|
+
/**
|
|
7289
|
+
* Register a provider. Registration is idempotent on `id`: a second
|
|
7290
|
+
* register with the same id replaces the previous entry in place.
|
|
7291
|
+
* This keeps host-side re-initialisation (e.g. SessionManager resume)
|
|
7292
|
+
* from double-injecting a built-in reminder.
|
|
7293
|
+
*/
|
|
7294
|
+
register(provider) {
|
|
7295
|
+
const existing = this.providers.findIndex((p) => p.id === provider.id);
|
|
7296
|
+
if (existing === -1) {
|
|
7297
|
+
this.providers.push(provider);
|
|
7298
|
+
return;
|
|
7253
7299
|
}
|
|
7300
|
+
this.providers[existing] = provider;
|
|
7254
7301
|
}
|
|
7255
|
-
|
|
7256
|
-
|
|
7257
|
-
|
|
7258
|
-
assistantTurnsSince
|
|
7259
|
-
};
|
|
7260
|
-
}
|
|
7261
|
-
function isPlanReminderMessage(msg) {
|
|
7262
|
-
const text = historyMessageText(msg);
|
|
7263
|
-
if (!text.trimStart().startsWith("<system-reminder>")) return false;
|
|
7264
|
-
return text.includes("Plan mode is active") || text.includes("Plan mode still active") || text.includes("Re-entering Plan Mode");
|
|
7265
|
-
}
|
|
7266
|
-
/**
|
|
7267
|
-
* Same as above but for yolo mode reminder.
|
|
7268
|
-
*/
|
|
7269
|
-
function hasYoloReminderWithoutNewUser(history) {
|
|
7270
|
-
return hasReminderWithoutNewUser(history, "yolo");
|
|
7271
|
-
}
|
|
7272
|
-
function hasReminderWithoutNewUser(history, fingerprint) {
|
|
7273
|
-
for (let i = history.length - 1; i >= 0; i--) {
|
|
7274
|
-
const msg = history[i];
|
|
7275
|
-
if (msg === void 0) continue;
|
|
7276
|
-
const text = historyMessageText(msg);
|
|
7277
|
-
if (msg.role === "user" && text.trimStart().startsWith("<system-reminder>") && text.toLowerCase().includes(fingerprint.toLowerCase())) return true;
|
|
7278
|
-
if (msg.role === "user" && !text.trimStart().startsWith("<system-reminder>") && !text.trimStart().startsWith("<notification")) return false;
|
|
7302
|
+
unregister(id) {
|
|
7303
|
+
const idx = this.providers.findIndex((p) => p.id === id);
|
|
7304
|
+
if (idx !== -1) this.providers.splice(idx, 1);
|
|
7279
7305
|
}
|
|
7280
|
-
|
|
7281
|
-
|
|
7306
|
+
/** Read-only view of registered providers. Primarily for tests. */
|
|
7307
|
+
list() {
|
|
7308
|
+
return this.providers;
|
|
7309
|
+
}
|
|
7310
|
+
/**
|
|
7311
|
+
* Collect every active injection for the upcoming LLM step. Providers
|
|
7312
|
+
* are iterated in registration order; a provider that throws is
|
|
7313
|
+
* isolated (error forwarded to `onProviderError`, remaining providers
|
|
7314
|
+
* still run). Same error-isolation pattern as HookEngine / SessionEventBus.
|
|
7315
|
+
*/
|
|
7316
|
+
computeInjections(ctx, contextState) {
|
|
7317
|
+
const out = [];
|
|
7318
|
+
for (const provider of this.providers) try {
|
|
7319
|
+
const injections = provider.getInjections(ctx);
|
|
7320
|
+
if (injections !== void 0 && injections !== null && Array.isArray(injections) && injections.length > 0) if (contextState !== void 0) for (const injection of injections) if (injection.kind === "system_reminder" && typeof injection.content === "string") contextState.appendSystemReminder({ content: injection.content });
|
|
7321
|
+
else throw new Error(`Unsupported injection kind "${injection.kind}" from provider "${provider.id}"`);
|
|
7322
|
+
else out.push(...injections);
|
|
7323
|
+
} catch (error) {
|
|
7324
|
+
this.onProviderError?.(provider, error instanceof Error ? error : new Error(String(error)));
|
|
7325
|
+
}
|
|
7326
|
+
return out;
|
|
7327
|
+
}
|
|
7328
|
+
};
|
|
7282
7329
|
/**
|
|
7283
7330
|
* Build a DynamicInjectionManager pre-populated with the two Phase 3
|
|
7284
7331
|
* built-in providers (plan-mode + yolo-mode). Hosts that want additional
|
|
@@ -7442,19 +7489,25 @@ function extractText(message, sep = "") {
|
|
|
7442
7489
|
}
|
|
7443
7490
|
//#endregion
|
|
7444
7491
|
//#region ../../packages/kosong/src/capability.ts
|
|
7492
|
+
const UNKNOWN_CAPABILITY_MARKER = Symbol.for("moonshot-ai.kosong.UNKNOWN_CAPABILITY");
|
|
7445
7493
|
/**
|
|
7446
7494
|
* Shared read-only default returned when a provider has not catalogued a
|
|
7447
7495
|
* given model. Frozen so accidental mutation at one call site cannot leak
|
|
7448
7496
|
* into another.
|
|
7449
7497
|
*/
|
|
7450
|
-
const UNKNOWN_CAPABILITY = Object.freeze({
|
|
7498
|
+
const UNKNOWN_CAPABILITY = Object.freeze(Object.defineProperty({
|
|
7451
7499
|
image_in: false,
|
|
7452
7500
|
video_in: false,
|
|
7453
7501
|
audio_in: false,
|
|
7454
7502
|
thinking: false,
|
|
7455
7503
|
tool_use: false,
|
|
7456
7504
|
max_context_tokens: 0
|
|
7457
|
-
});
|
|
7505
|
+
}, UNKNOWN_CAPABILITY_MARKER, { value: true }));
|
|
7506
|
+
function isUnknownCapability(capability) {
|
|
7507
|
+
if (capability === UNKNOWN_CAPABILITY) return true;
|
|
7508
|
+
if (capability[UNKNOWN_CAPABILITY_MARKER] === true) return true;
|
|
7509
|
+
return capability.image_in === false && capability.video_in === false && capability.audio_in === false && capability.thinking === false && capability.tool_use === false && capability.max_context_tokens === 0;
|
|
7510
|
+
}
|
|
7458
7511
|
//#endregion
|
|
7459
7512
|
//#region ../../packages/kosong/src/errors.ts
|
|
7460
7513
|
/**
|
|
@@ -20511,6 +20564,17 @@ function zeroUsage() {
|
|
|
20511
20564
|
output: 0
|
|
20512
20565
|
};
|
|
20513
20566
|
}
|
|
20567
|
+
function configuredCapabilitiesToModelCapability(capabilities, maxContextTokens) {
|
|
20568
|
+
const caps = new Set(capabilities.map((cap) => cap.toLowerCase()));
|
|
20569
|
+
return {
|
|
20570
|
+
image_in: caps.has("image_in"),
|
|
20571
|
+
video_in: caps.has("video_in"),
|
|
20572
|
+
audio_in: caps.has("audio_in"),
|
|
20573
|
+
thinking: caps.has("thinking") || caps.has("always_thinking"),
|
|
20574
|
+
tool_use: caps.has("tool_use"),
|
|
20575
|
+
max_context_tokens: maxContextTokens
|
|
20576
|
+
};
|
|
20577
|
+
}
|
|
20514
20578
|
function mergeTurnRules(sessionRules, pendingOverrides) {
|
|
20515
20579
|
if (pendingOverrides === void 0) return sessionRules;
|
|
20516
20580
|
const turnRules = [];
|
|
@@ -20771,8 +20835,12 @@ var TurnManager = class {
|
|
|
20771
20835
|
async handlePrompt(req) {
|
|
20772
20836
|
if (!this.deps.lifecycleStateMachine.isIdle() || this.getCurrentTurnId() !== void 0) return { error: "agent_busy" };
|
|
20773
20837
|
const input = req.data.input;
|
|
20774
|
-
const
|
|
20775
|
-
|
|
20838
|
+
const runtimeBundle = this.runtimeSlot?.current();
|
|
20839
|
+
const runtime = runtimeBundle?.runtime ?? this.runtime;
|
|
20840
|
+
const configuredCapabilities = runtimeBundle !== void 0 ? runtimeBundle.modelCapabilities : this.deps.modelCapabilities;
|
|
20841
|
+
const capability = configuredCapabilities !== void 0 ? configuredCapabilitiesToModelCapability(configuredCapabilities, runtimeBundle?.compactionConfig?.maxContextSize ?? this.compactionConfig?.maxContextSize ?? 0) : runtime.kosong.getCapability?.();
|
|
20842
|
+
const capabilityIsAuthoritative = configuredCapabilities !== void 0 || capability !== void 0 && !isUnknownCapability(capability);
|
|
20843
|
+
if (capability !== void 0 && capabilityIsAuthoritative) {
|
|
20776
20844
|
let inputContainsImage = false;
|
|
20777
20845
|
let inputContainsVideo = false;
|
|
20778
20846
|
for (const part of input.parts ?? []) if (part.type === "image_url") inputContainsImage = true;
|
|
@@ -21211,7 +21279,6 @@ var SoulPlus = class {
|
|
|
21211
21279
|
const contextState = deps.contextState;
|
|
21212
21280
|
const sessionJournal = deps.sessionJournal;
|
|
21213
21281
|
const journalWriter = contextState.journalWriter;
|
|
21214
|
-
const journalCapability = deps.journalCapability;
|
|
21215
21282
|
const runtimeSlot = deps.runtimeSlot ?? createSessionRuntimeSlot({
|
|
21216
21283
|
model: contextState.model,
|
|
21217
21284
|
runtime: deps.runtime,
|
|
@@ -21239,7 +21306,6 @@ var SoulPlus = class {
|
|
|
21239
21306
|
compactionProvider,
|
|
21240
21307
|
runtimeSlot,
|
|
21241
21308
|
lifecycleStateMachine: stateMachine,
|
|
21242
|
-
journalCapability,
|
|
21243
21309
|
sink: eventBus,
|
|
21244
21310
|
journalWriter,
|
|
21245
21311
|
sessionId: deps.sessionId,
|
|
@@ -21307,7 +21373,8 @@ var SoulPlus = class {
|
|
|
21307
21373
|
toolExecutionScopeFactory,
|
|
21308
21374
|
approvalRuntime,
|
|
21309
21375
|
hookEngine: deps.hookEngine,
|
|
21310
|
-
compactionConfig: deps.compactionConfig
|
|
21376
|
+
compactionConfig: deps.compactionConfig,
|
|
21377
|
+
modelCapabilities: deps.modelCapabilities
|
|
21311
21378
|
});
|
|
21312
21379
|
turnManagerRef.bind(turnManager);
|
|
21313
21380
|
const setPlanMode = createPlanModeSetter({
|
|
@@ -21340,8 +21407,7 @@ var SoulPlus = class {
|
|
|
21340
21407
|
this.journal = {
|
|
21341
21408
|
writer: journalWriter,
|
|
21342
21409
|
contextState,
|
|
21343
|
-
sessionJournal
|
|
21344
|
-
capability: journalCapability
|
|
21410
|
+
sessionJournal
|
|
21345
21411
|
};
|
|
21346
21412
|
this.services = {
|
|
21347
21413
|
approvalRuntime,
|
|
@@ -21964,17 +22030,42 @@ const COMPACTION_SYSTEM_PROMPT = "You are a helpful assistant that compacts conv
|
|
|
21964
22030
|
/**
|
|
21965
22031
|
* Build the compaction prompt from conversation messages.
|
|
21966
22032
|
*
|
|
21967
|
-
*
|
|
21968
|
-
*
|
|
21969
|
-
*
|
|
22033
|
+
* Mirrors Python's `SimpleCompaction.prepare()` serialization:
|
|
22034
|
+
* - Each message becomes a block: `## Message N\nRole: X\nContent:\n<text>`
|
|
22035
|
+
* - Only TextPart is retained; think / image / audio / video are dropped.
|
|
22036
|
+
* - Tool results are folded into the assistant message that issued the
|
|
22037
|
+
* call, so the summary sees both the invocation and its result.
|
|
22038
|
+
* - Message blocks are concatenated without separators, matching Python's
|
|
22039
|
+
* `compact_message.content.extend()` behaviour.
|
|
22040
|
+
* - The instruction suffix is joined with a single `\n` to match Python's
|
|
22041
|
+
* `prompt_text = "\n" + prompts.COMPACT`.
|
|
21970
22042
|
*/
|
|
21971
22043
|
function buildCompactionPrompt(messages, userInstructions) {
|
|
22044
|
+
const toolResultsById = /* @__PURE__ */ new Map();
|
|
22045
|
+
for (const msg of messages) if (msg.role === "tool" && msg.toolCallId) {
|
|
22046
|
+
const text = msg.content.filter((p) => p.type === "text").map((p) => p.text).join("");
|
|
22047
|
+
toolResultsById.set(msg.toolCallId, text);
|
|
22048
|
+
}
|
|
21972
22049
|
const parts = [];
|
|
21973
|
-
|
|
21974
|
-
|
|
21975
|
-
if (
|
|
22050
|
+
let seq = 1;
|
|
22051
|
+
for (const msg of messages) {
|
|
22052
|
+
if (msg.role === "tool") continue;
|
|
22053
|
+
const body = msg.content.filter((p) => p.type === "text").map((p) => p.text).join("");
|
|
22054
|
+
let block = `## Message ${seq}\nRole: ${msg.role}\nContent:\n${body}`;
|
|
22055
|
+
if (msg.role === "assistant" && msg.toolCalls && msg.toolCalls.length > 0) for (const tc of msg.toolCalls) {
|
|
22056
|
+
let argsDisplay = tc.function.arguments ?? "";
|
|
22057
|
+
try {
|
|
22058
|
+
const parsed = JSON.parse(argsDisplay);
|
|
22059
|
+
argsDisplay = JSON.stringify(parsed);
|
|
22060
|
+
} catch {}
|
|
22061
|
+
const result = toolResultsById.get(tc.id);
|
|
22062
|
+
const resultStr = result !== void 0 ? ` ->\n${result}` : "";
|
|
22063
|
+
block += `\n[Tool: ${tc.function.name}(${argsDisplay})]${resultStr}`;
|
|
22064
|
+
}
|
|
22065
|
+
parts.push((block.endsWith("\n") ? block : block + "\n") + "\n");
|
|
22066
|
+
seq++;
|
|
21976
22067
|
}
|
|
21977
|
-
let prompt = parts.join("
|
|
22068
|
+
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.";
|
|
21978
22069
|
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;
|
|
21979
22070
|
return prompt;
|
|
21980
22071
|
}
|
|
@@ -21993,7 +22084,7 @@ var KosongCompactionProvider = class {
|
|
|
21993
22084
|
toolCalls: []
|
|
21994
22085
|
};
|
|
21995
22086
|
return {
|
|
21996
|
-
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("
|
|
22087
|
+
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(""),
|
|
21997
22088
|
original_turn_count: messages.length
|
|
21998
22089
|
};
|
|
21999
22090
|
}
|
|
@@ -22002,48 +22093,6 @@ function createKosongCompactionProvider(provider) {
|
|
|
22002
22093
|
return new KosongCompactionProvider(provider);
|
|
22003
22094
|
}
|
|
22004
22095
|
//#endregion
|
|
22005
|
-
//#region ../../packages/kimi-core/src/soul-plus/compaction/journal-capability.ts
|
|
22006
|
-
/**
|
|
22007
|
-
* WiredJournalCapability — real JournalCapability backed by physical
|
|
22008
|
-
* wire.jsonl rotation (Slice 3.3 / M05).
|
|
22009
|
-
*
|
|
22010
|
-
* Replaces `createStubJournalCapability()` from Slice 3 for production
|
|
22011
|
-
* use. Wraps `rotateJournal()` from `src/storage/compaction.ts` and
|
|
22012
|
-
* coordinates with `WiredJournalWriter.resetForRotation()` so the
|
|
22013
|
-
* journal's monotonic seq counter restarts at 0 in the new file.
|
|
22014
|
-
*/
|
|
22015
|
-
var WiredJournalCapability = class {
|
|
22016
|
-
sessionDir;
|
|
22017
|
-
journalWriter;
|
|
22018
|
-
producer;
|
|
22019
|
-
constructor(deps) {
|
|
22020
|
-
this.sessionDir = deps.sessionDir;
|
|
22021
|
-
this.journalWriter = deps.journalWriter;
|
|
22022
|
-
this.producer = deps.producer;
|
|
22023
|
-
}
|
|
22024
|
-
async rotate(_boundaryRecord) {
|
|
22025
|
-
const result = await rotateJournal(this.sessionDir, { producer: this.producer });
|
|
22026
|
-
this.journalWriter.resetForRotation();
|
|
22027
|
-
return { archiveFile: basename(result.archivePath) };
|
|
22028
|
-
}
|
|
22029
|
-
async readSessionInitialized() {
|
|
22030
|
-
const wirePath = join(this.sessionDir, "wire.jsonl");
|
|
22031
|
-
const lines = (await readFile(wirePath, "utf-8")).split("\n").filter((l) => l.length > 0);
|
|
22032
|
-
if (lines.length < 2) throw new Error(`readSessionInitialized: wire.jsonl at ${wirePath} has fewer than 2 lines`);
|
|
22033
|
-
const parsed = JSON.parse(lines[1]);
|
|
22034
|
-
const result = SessionInitializedRecordSchema.safeParse(parsed);
|
|
22035
|
-
if (!result.success) throw new Error(`readSessionInitialized: wire.jsonl line 2 at ${wirePath} is not a valid session_initialized record: ${result.error.message}`);
|
|
22036
|
-
return result.data;
|
|
22037
|
-
}
|
|
22038
|
-
async appendBoundary(sessionInitialized) {
|
|
22039
|
-
const { seq: _seq, time: _time, ...input } = sessionInitialized;
|
|
22040
|
-
await this.journalWriter.append(input);
|
|
22041
|
-
}
|
|
22042
|
-
};
|
|
22043
|
-
function createWiredJournalCapability(deps) {
|
|
22044
|
-
return new WiredJournalCapability(deps);
|
|
22045
|
-
}
|
|
22046
|
-
//#endregion
|
|
22047
22096
|
//#region ../../packages/kimi-core/src/soul-plus/approval/wired-approval-runtime.ts
|
|
22048
22097
|
/**
|
|
22049
22098
|
* WiredApprovalRuntime — the production `ApprovalRuntime` (v2 §9-G).
|
|
@@ -30264,6 +30313,10 @@ const BUS_EVENT_TRANSLATORS = {
|
|
|
30264
30313
|
hook_id: event.hook_id,
|
|
30265
30314
|
outcome: event.outcome
|
|
30266
30315
|
}),
|
|
30316
|
+
"session.created": (event, ctx) => {
|
|
30317
|
+
const data = event.data;
|
|
30318
|
+
return draft(event, ctx, "session.created", data);
|
|
30319
|
+
},
|
|
30267
30320
|
"status.update": (event, ctx) => draft(event, ctx, "status.update", event.data),
|
|
30268
30321
|
"session_meta.changed": (event, ctx) => draft(event, ctx, "session_meta.changed", event.data),
|
|
30269
30322
|
"thinking.changed": (event, ctx) => draft(event, ctx, "thinking.changed", { level: event.level }),
|
|
@@ -30374,6 +30427,9 @@ function installWireEventBridge(opts) {
|
|
|
30374
30427
|
* -32700 Parse error — codec decode failure (unparseable frame)
|
|
30375
30428
|
* -32600 Invalid request — envelope schema rejection
|
|
30376
30429
|
* -32602 Invalid params — zod-validated method params failure
|
|
30430
|
+
* -32001 LLM not configured
|
|
30431
|
+
* -32002 LLM capability mismatch
|
|
30432
|
+
* -32003 Provider-level failure
|
|
30377
30433
|
* -32603 Internal error — fallback
|
|
30378
30434
|
*/
|
|
30379
30435
|
function mapToWireError(err) {
|
|
@@ -30417,6 +30473,11 @@ function mapToWireError(err) {
|
|
|
30417
30473
|
details: { session_id: err.sessionId }
|
|
30418
30474
|
}
|
|
30419
30475
|
};
|
|
30476
|
+
const businessError = classifyBusinessError(err);
|
|
30477
|
+
if (businessError !== null) return {
|
|
30478
|
+
request_id: null,
|
|
30479
|
+
error: businessError
|
|
30480
|
+
};
|
|
30420
30481
|
return {
|
|
30421
30482
|
request_id: null,
|
|
30422
30483
|
error: {
|
|
@@ -30848,8 +30909,8 @@ var DefaultSessionApplicationService = class {
|
|
|
30848
30909
|
eventBus,
|
|
30849
30910
|
hookEngine
|
|
30850
30911
|
});
|
|
30851
|
-
const systemPrompt = input.systemPrompt ?? this.deps.defaultSystemPromptProvider?.();
|
|
30852
|
-
const enabledToolNames = this.deps.enabledToolNamesProvider?.();
|
|
30912
|
+
const systemPrompt = input.systemPrompt ?? await this.deps.defaultSystemPromptProvider?.();
|
|
30913
|
+
const enabledToolNames = await this.deps.enabledToolNamesProvider?.();
|
|
30853
30914
|
const model = input.model ?? this.deps.defaultModelProvider();
|
|
30854
30915
|
const runtimeBundle = await this.deps.runtimeBundleProvider?.({
|
|
30855
30916
|
sessionId,
|
|
@@ -30863,11 +30924,17 @@ var DefaultSessionApplicationService = class {
|
|
|
30863
30924
|
const runtime = runtimeSlotBundle?.runtime ?? this.deps.runtimeProvider();
|
|
30864
30925
|
const compactionProvider = runtimeSlotBundle?.compactionProvider ?? this.deps.compactionProviderProvider();
|
|
30865
30926
|
const runtimeSlot = runtimeSlotBundle !== void 0 ? createSessionRuntimeSlot(runtimeSlotBundle) : void 0;
|
|
30927
|
+
const tools = await this.deps.toolsProvider({
|
|
30928
|
+
sessionId,
|
|
30929
|
+
model
|
|
30930
|
+
});
|
|
30931
|
+
const skillManager = await this.deps.skillManagerProvider?.() ?? this.deps.skillManager;
|
|
30932
|
+
const agentTypeRegistry = await this.deps.agentTypeRegistryProvider?.() ?? this.deps.agentTypeRegistry;
|
|
30866
30933
|
const managed = await this.deps.sessionManager.createSession({
|
|
30867
30934
|
sessionId,
|
|
30868
30935
|
runtime,
|
|
30869
30936
|
runtimeSlot,
|
|
30870
|
-
tools
|
|
30937
|
+
tools,
|
|
30871
30938
|
enabledToolNames,
|
|
30872
30939
|
model,
|
|
30873
30940
|
systemPrompt,
|
|
@@ -30878,12 +30945,19 @@ var DefaultSessionApplicationService = class {
|
|
|
30878
30945
|
approvalRuntime: this.deps.approvalRuntime,
|
|
30879
30946
|
approvalRuntimeFactory: this.deps.approvalRuntimeFactory,
|
|
30880
30947
|
hookEngine,
|
|
30881
|
-
skillManager
|
|
30882
|
-
agentTypeRegistry
|
|
30948
|
+
skillManager,
|
|
30949
|
+
agentTypeRegistry,
|
|
30883
30950
|
questionRuntime: this.deps.questionRuntimeProvider?.({ sessionId }),
|
|
30884
30951
|
logger: this.deps.logger
|
|
30885
30952
|
});
|
|
30886
30953
|
this.deps.sessionLifecycle?.onSessionCreated?.(managed);
|
|
30954
|
+
managed.eventBus?.emit({
|
|
30955
|
+
type: "session.created",
|
|
30956
|
+
data: {
|
|
30957
|
+
session_id: managed.sessionId,
|
|
30958
|
+
model
|
|
30959
|
+
}
|
|
30960
|
+
});
|
|
30887
30961
|
return {
|
|
30888
30962
|
sessionId: managed.sessionId,
|
|
30889
30963
|
managed
|
|
@@ -30913,18 +30987,24 @@ var DefaultSessionApplicationService = class {
|
|
|
30913
30987
|
const runtime = runtimeSlotBundle?.runtime ?? this.deps.runtimeProvider();
|
|
30914
30988
|
const compactionProvider = runtimeSlotBundle?.compactionProvider ?? this.deps.compactionProviderProvider();
|
|
30915
30989
|
const runtimeSlot = runtimeSlotBundle !== void 0 ? createSessionRuntimeSlot(runtimeSlotBundle) : void 0;
|
|
30990
|
+
const tools = await this.deps.toolsProvider({
|
|
30991
|
+
sessionId,
|
|
30992
|
+
model: initialModel
|
|
30993
|
+
});
|
|
30994
|
+
const skillManager = await this.deps.skillManagerProvider?.() ?? this.deps.skillManager;
|
|
30995
|
+
const agentTypeRegistry = await this.deps.agentTypeRegistryProvider?.() ?? this.deps.agentTypeRegistry;
|
|
30916
30996
|
const managed = await this.deps.sessionManager.resumeSession(sessionId, {
|
|
30917
30997
|
runtime,
|
|
30918
30998
|
runtimeSlot,
|
|
30919
|
-
tools
|
|
30999
|
+
tools,
|
|
30920
31000
|
eventBus,
|
|
30921
31001
|
compactionProvider,
|
|
30922
31002
|
compactionConfig,
|
|
30923
31003
|
approvalRuntime: this.deps.approvalRuntime,
|
|
30924
31004
|
approvalRuntimeFactory: this.deps.approvalRuntimeFactory,
|
|
30925
31005
|
hookEngine,
|
|
30926
|
-
skillManager
|
|
30927
|
-
agentTypeRegistry
|
|
31006
|
+
skillManager,
|
|
31007
|
+
agentTypeRegistry,
|
|
30928
31008
|
questionRuntime: this.deps.questionRuntimeProvider?.({ sessionId }),
|
|
30929
31009
|
logger: this.deps.logger
|
|
30930
31010
|
});
|
|
@@ -31116,17 +31196,20 @@ var DefaultSessionApplicationService = class {
|
|
|
31116
31196
|
}
|
|
31117
31197
|
async getBackgroundTasks(sessionId) {
|
|
31118
31198
|
return {
|
|
31119
|
-
background_tasks: this.
|
|
31199
|
+
background_tasks: this.backgroundProcessManager()?.list() ?? [],
|
|
31120
31200
|
agent_instances: await new SubagentStore(this.deps.pathConfig.sessionDir(sessionId)).listInstances()
|
|
31121
31201
|
};
|
|
31122
31202
|
}
|
|
31123
31203
|
async stopBackgroundTask(taskId) {
|
|
31124
|
-
const manager = this.
|
|
31204
|
+
const manager = this.backgroundProcessManager();
|
|
31125
31205
|
if (manager === void 0) throw new Error(`session.stopBackgroundTask: no BackgroundProcessManager wired (task ${taskId})`);
|
|
31126
31206
|
if (await manager.stop(taskId) === void 0) throw new Error(`Background task not found: ${taskId}`);
|
|
31127
31207
|
}
|
|
31128
31208
|
getBackgroundTaskOutput(taskId, tail) {
|
|
31129
|
-
return this.
|
|
31209
|
+
return this.backgroundProcessManager()?.getOutput(taskId, tail) ?? "";
|
|
31210
|
+
}
|
|
31211
|
+
backgroundProcessManager() {
|
|
31212
|
+
return this.deps.backgroundProcessManagerProvider?.() ?? this.deps.backgroundProcessManager;
|
|
31130
31213
|
}
|
|
31131
31214
|
getManaged(sessionId) {
|
|
31132
31215
|
const managed = this.deps.sessionManager.get(sessionId);
|
|
@@ -31603,15 +31686,25 @@ const CONVERSATION_HANDLER_DESCRIPTORS = [
|
|
|
31603
31686
|
}).join("");
|
|
31604
31687
|
inputParts = payload.input;
|
|
31605
31688
|
}
|
|
31606
|
-
|
|
31607
|
-
|
|
31608
|
-
|
|
31609
|
-
|
|
31610
|
-
|
|
31611
|
-
|
|
31612
|
-
|
|
31613
|
-
|
|
31614
|
-
|
|
31689
|
+
try {
|
|
31690
|
+
const dispatch = await ctx.sessionApplication.prompt(msg.session_id, {
|
|
31691
|
+
text: inputText,
|
|
31692
|
+
parts: inputParts
|
|
31693
|
+
});
|
|
31694
|
+
return createWireResponse({
|
|
31695
|
+
requestId: msg.id,
|
|
31696
|
+
sessionId: msg.session_id,
|
|
31697
|
+
data: dispatch
|
|
31698
|
+
});
|
|
31699
|
+
} catch (error) {
|
|
31700
|
+
const classified = classifyBusinessError(error);
|
|
31701
|
+
if (classified !== null) return createWireResponse({
|
|
31702
|
+
requestId: msg.id,
|
|
31703
|
+
sessionId: msg.session_id,
|
|
31704
|
+
error: classified
|
|
31705
|
+
});
|
|
31706
|
+
throw error;
|
|
31707
|
+
}
|
|
31615
31708
|
});
|
|
31616
31709
|
}),
|
|
31617
31710
|
defineWireMethodDescriptor("session.steer", "conversation", (ctx) => {
|
|
@@ -32260,6 +32353,7 @@ const SUPPORTED_WIRE_EVENTS = [
|
|
|
32260
32353
|
"notification",
|
|
32261
32354
|
"hook.triggered",
|
|
32262
32355
|
"hook.resolved",
|
|
32356
|
+
"session.created",
|
|
32263
32357
|
"session.error",
|
|
32264
32358
|
"session.replay.chunk",
|
|
32265
32359
|
"session.replay.end",
|
|
@@ -32358,8 +32452,8 @@ function registerDefaultWireHandlers(deps) {
|
|
|
32358
32452
|
sessionManager,
|
|
32359
32453
|
pathConfig,
|
|
32360
32454
|
runtimeProvider: () => deps.runtimeProvider?.() ?? runtime,
|
|
32361
|
-
toolsProvider: () => tools,
|
|
32362
|
-
...deps.enabledToolNames !== void 0 ? { enabledToolNamesProvider: () => deps.enabledToolNames } : {},
|
|
32455
|
+
toolsProvider: (ctx) => deps.toolsProvider?.(ctx) ?? tools,
|
|
32456
|
+
...deps.enabledToolNames !== void 0 || deps.enabledToolNamesProvider !== void 0 ? { enabledToolNamesProvider: async () => deps.enabledToolNamesProvider !== void 0 ? await deps.enabledToolNamesProvider() : deps.enabledToolNames } : {},
|
|
32363
32457
|
defaultModelProvider: () => deps.defaultModelProvider?.() ?? defaultModel,
|
|
32364
32458
|
...deps.defaultSystemPromptProvider !== void 0 ? { defaultSystemPromptProvider: deps.defaultSystemPromptProvider } : {},
|
|
32365
32459
|
compactionProviderProvider: () => deps.compactionProviderProvider?.() ?? compactionProvider,
|
|
@@ -32371,10 +32465,13 @@ function registerDefaultWireHandlers(deps) {
|
|
|
32371
32465
|
workspaceDir,
|
|
32372
32466
|
approvalStateStore,
|
|
32373
32467
|
skillManager: deps.skillManager,
|
|
32468
|
+
...deps.skillManagerProvider !== void 0 ? { skillManagerProvider: deps.skillManagerProvider } : {},
|
|
32374
32469
|
agentTypeRegistry: deps.agentTypeRegistry,
|
|
32470
|
+
...deps.agentTypeRegistryProvider !== void 0 ? { agentTypeRegistryProvider: deps.agentTypeRegistryProvider } : {},
|
|
32375
32471
|
questionRuntimeProvider: ({ sessionId }) => reverse !== void 0 ? createWireQuestionRuntime(reverse, sessionId) : void 0,
|
|
32376
32472
|
rebuildRuntimeForModel,
|
|
32377
32473
|
backgroundProcessManager,
|
|
32474
|
+
...deps.backgroundProcessManagerProvider !== void 0 ? { backgroundProcessManagerProvider: deps.backgroundProcessManagerProvider } : {},
|
|
32378
32475
|
sessionLifecycle: {
|
|
32379
32476
|
onSessionCreated: (managed) => deps.sessionLifecycle?.onSessionCreated?.(managed),
|
|
32380
32477
|
onSessionDestroyed: (sessionId) => {
|
|
@@ -33461,7 +33558,8 @@ var SessionManager = class {
|
|
|
33461
33558
|
model: options.model,
|
|
33462
33559
|
runtime: options.runtime,
|
|
33463
33560
|
compactionProvider: options.compactionProvider,
|
|
33464
|
-
...options.compactionConfig !== void 0 ? { compactionConfig: options.compactionConfig } : {}
|
|
33561
|
+
...options.compactionConfig !== void 0 ? { compactionConfig: options.compactionConfig } : {},
|
|
33562
|
+
...options.modelCapabilities !== void 0 ? { modelCapabilities: options.modelCapabilities } : {}
|
|
33465
33563
|
});
|
|
33466
33564
|
const runtimeBundle = runtimeSlot.current();
|
|
33467
33565
|
const mainInitInput = {
|
|
@@ -33523,12 +33621,8 @@ var SessionManager = class {
|
|
|
33523
33621
|
skillManager: options.skillManager,
|
|
33524
33622
|
questionRuntime: options.questionRuntime,
|
|
33525
33623
|
compactionConfig: runtimeBundle.compactionConfig,
|
|
33624
|
+
modelCapabilities: runtimeBundle.modelCapabilities,
|
|
33526
33625
|
compactionProvider: runtimeBundle.compactionProvider,
|
|
33527
|
-
journalCapability: options.journalCapability ?? createWiredJournalCapability({
|
|
33528
|
-
sessionDir,
|
|
33529
|
-
journalWriter,
|
|
33530
|
-
producer: this.producer
|
|
33531
|
-
}),
|
|
33532
33626
|
approvalRuntime,
|
|
33533
33627
|
hookEngine: options.hookEngine,
|
|
33534
33628
|
toolExecutionScopeFactory: options.toolExecutionScopeFactory,
|
|
@@ -33654,7 +33748,8 @@ var SessionManager = class {
|
|
|
33654
33748
|
model: projected.model,
|
|
33655
33749
|
runtime: options.runtime,
|
|
33656
33750
|
compactionProvider: options.compactionProvider,
|
|
33657
|
-
compactionConfig: options.compactionConfig
|
|
33751
|
+
compactionConfig: options.compactionConfig,
|
|
33752
|
+
...options.modelCapabilities !== void 0 ? { modelCapabilities: options.modelCapabilities } : {}
|
|
33658
33753
|
});
|
|
33659
33754
|
const runtimeBundle = runtimeSlot.current();
|
|
33660
33755
|
await stateCache.write({
|
|
@@ -33702,12 +33797,8 @@ var SessionManager = class {
|
|
|
33702
33797
|
skillManager: options.skillManager,
|
|
33703
33798
|
...options.questionRuntime !== void 0 ? { questionRuntime: options.questionRuntime } : {},
|
|
33704
33799
|
...runtimeBundle.compactionConfig !== void 0 ? { compactionConfig: runtimeBundle.compactionConfig } : {},
|
|
33800
|
+
...runtimeBundle.modelCapabilities !== void 0 ? { modelCapabilities: runtimeBundle.modelCapabilities } : {},
|
|
33705
33801
|
compactionProvider: runtimeBundle.compactionProvider,
|
|
33706
|
-
journalCapability: options.journalCapability ?? createWiredJournalCapability({
|
|
33707
|
-
sessionDir,
|
|
33708
|
-
journalWriter,
|
|
33709
|
-
producer: this.producer
|
|
33710
|
-
}),
|
|
33711
33802
|
approvalRuntime,
|
|
33712
33803
|
hookEngine: options.hookEngine,
|
|
33713
33804
|
...options.toolExecutionScopeFactory !== void 0 ? { toolExecutionScopeFactory: options.toolExecutionScopeFactory } : {},
|
|
@@ -42000,6 +42091,16 @@ function injectEnvVars(config) {
|
|
|
42000
42091
|
yolo
|
|
42001
42092
|
};
|
|
42002
42093
|
}
|
|
42094
|
+
function parseTomlData(data) {
|
|
42095
|
+
const raw = JSON.parse(JSON.stringify(data));
|
|
42096
|
+
const transformed = transformTomlData(data);
|
|
42097
|
+
transformed["raw"] = raw;
|
|
42098
|
+
try {
|
|
42099
|
+
return KimiConfigSchema.parse(transformed);
|
|
42100
|
+
} catch (error) {
|
|
42101
|
+
throw new ConfigError(`Invalid configuration: ${error instanceof Error ? error.message : String(error)}`);
|
|
42102
|
+
}
|
|
42103
|
+
}
|
|
42003
42104
|
/**
|
|
42004
42105
|
* Load and merge configuration from global, project, and override layers.
|
|
42005
42106
|
*
|
|
@@ -42027,6 +42128,16 @@ function loadConfig(options) {
|
|
|
42027
42128
|
return config;
|
|
42028
42129
|
}
|
|
42029
42130
|
/**
|
|
42131
|
+
* Load configuration from exactly one TOML file.
|
|
42132
|
+
*
|
|
42133
|
+
* Missing files are treated as an empty config. This is the preferred entry
|
|
42134
|
+
* point for default-host assembly, where the caller owns the config path and
|
|
42135
|
+
* no global/project layering should be inferred.
|
|
42136
|
+
*/
|
|
42137
|
+
function loadConfigFile(filePath) {
|
|
42138
|
+
return injectEnvVars(parseTomlData(readTomlFile(filePath) ?? {}));
|
|
42139
|
+
}
|
|
42140
|
+
/**
|
|
42030
42141
|
* Save a validated {@link KimiConfig} to TOML. Existing raw keys loaded from
|
|
42031
42142
|
* disk are preserved when `config.raw` is present; typed fields are overlaid
|
|
42032
42143
|
* in snake_case so the file remains compatible with the user-facing format.
|
|
@@ -78233,6 +78344,50 @@ async function loadBundledAgentTypeRegistry(defaultAgentYamlPath, onWarning) {
|
|
|
78233
78344
|
return { agentTypeRegistry: registry };
|
|
78234
78345
|
}
|
|
78235
78346
|
//#endregion
|
|
78347
|
+
//#region ../../packages/kimi-core/src/soul-plus/host/config.ts
|
|
78348
|
+
const DEFAULT_SOUL_PLUS_CONFIG = {
|
|
78349
|
+
providers: {},
|
|
78350
|
+
defaultProvider: void 0,
|
|
78351
|
+
defaultModel: void 0,
|
|
78352
|
+
models: void 0,
|
|
78353
|
+
thinking: void 0,
|
|
78354
|
+
planMode: void 0,
|
|
78355
|
+
yolo: void 0,
|
|
78356
|
+
defaultThinking: false,
|
|
78357
|
+
defaultYolo: false,
|
|
78358
|
+
defaultPlanMode: false,
|
|
78359
|
+
hooks: void 0,
|
|
78360
|
+
services: void 0,
|
|
78361
|
+
mergeAllAvailableSkills: false,
|
|
78362
|
+
loopControl: {
|
|
78363
|
+
reservedContextSize: 5e4,
|
|
78364
|
+
compactionTriggerRatio: .85
|
|
78365
|
+
},
|
|
78366
|
+
raw: void 0
|
|
78367
|
+
};
|
|
78368
|
+
function normalizeDefaultSoulPlusConfig(config) {
|
|
78369
|
+
return {
|
|
78370
|
+
...DEFAULT_SOUL_PLUS_CONFIG,
|
|
78371
|
+
...config,
|
|
78372
|
+
providers: {
|
|
78373
|
+
...DEFAULT_SOUL_PLUS_CONFIG.providers,
|
|
78374
|
+
...config.providers
|
|
78375
|
+
},
|
|
78376
|
+
...DEFAULT_SOUL_PLUS_CONFIG.services !== void 0 || config.services !== void 0 ? { services: {
|
|
78377
|
+
...DEFAULT_SOUL_PLUS_CONFIG.services,
|
|
78378
|
+
...config.services
|
|
78379
|
+
} } : {},
|
|
78380
|
+
loopControl: {
|
|
78381
|
+
...DEFAULT_SOUL_PLUS_CONFIG.loopControl,
|
|
78382
|
+
...config.loopControl
|
|
78383
|
+
},
|
|
78384
|
+
raw: config.raw
|
|
78385
|
+
};
|
|
78386
|
+
}
|
|
78387
|
+
function loadDefaultSoulPlusConfig(configPath) {
|
|
78388
|
+
return normalizeDefaultSoulPlusConfig(loadConfigFile(configPath));
|
|
78389
|
+
}
|
|
78390
|
+
//#endregion
|
|
78236
78391
|
//#region ../../packages/kimi-core/src/soul-plus/host/runtime.ts
|
|
78237
78392
|
function isVideoUploadCapableProvider(value) {
|
|
78238
78393
|
return typeof value === "object" && value !== null && typeof value.uploadVideo === "function";
|
|
@@ -78265,21 +78420,27 @@ function resolveDefaultSoulPlusDefaultModel(options) {
|
|
|
78265
78420
|
function getDefaultSoulPlusMaxContextSize(options, defaultModel) {
|
|
78266
78421
|
return options.kimiConfig?.models?.[defaultModel]?.maxContextSize ?? 2e5;
|
|
78267
78422
|
}
|
|
78423
|
+
function getDefaultSoulPlusModelCapabilities(options, defaultModel) {
|
|
78424
|
+
return options.kimiConfig?.models?.[defaultModel]?.capabilities;
|
|
78425
|
+
}
|
|
78268
78426
|
async function createDefaultSoulPlusRuntimeBundle(options) {
|
|
78269
78427
|
const defaultModel = resolveDefaultSoulPlusDefaultModel(options);
|
|
78270
78428
|
const maxContextSize = getDefaultSoulPlusMaxContextSize(options, defaultModel);
|
|
78429
|
+
const modelCapabilities = getDefaultSoulPlusModelCapabilities(options, defaultModel);
|
|
78271
78430
|
if (options.runtime !== void 0 && options.compactionProvider !== void 0) return {
|
|
78272
78431
|
runtime: options.runtime,
|
|
78273
78432
|
compactionProvider: options.compactionProvider,
|
|
78274
78433
|
defaultModel,
|
|
78275
|
-
maxContextSize
|
|
78434
|
+
maxContextSize,
|
|
78435
|
+
...modelCapabilities !== void 0 ? { modelCapabilities } : {}
|
|
78276
78436
|
};
|
|
78277
78437
|
if (options.runtime !== void 0 || options.compactionProvider !== void 0) throw new Error("createDefaultSoulPlusWireClient: runtime and compactionProvider must be provided together");
|
|
78278
78438
|
if (defaultModel.length === 0) return {
|
|
78279
78439
|
runtime: createRuntime({ kosong: new UnconfiguredKosongAdapter() }),
|
|
78280
78440
|
compactionProvider: new UnconfiguredCompactionProvider(),
|
|
78281
78441
|
defaultModel,
|
|
78282
|
-
maxContextSize
|
|
78442
|
+
maxContextSize,
|
|
78443
|
+
...modelCapabilities !== void 0 ? { modelCapabilities } : {}
|
|
78283
78444
|
};
|
|
78284
78445
|
if (options.kimiConfig === void 0) throw new Error("createDefaultSoulPlusWireClient: provide either runtime+compactionProvider or kimiConfig");
|
|
78285
78446
|
const provider = await createProviderFromConfig(options.kimiConfig, defaultModel, {
|
|
@@ -78296,6 +78457,7 @@ async function createDefaultSoulPlusRuntimeBundle(options) {
|
|
|
78296
78457
|
compactionProvider: createKosongCompactionProvider(provider),
|
|
78297
78458
|
defaultModel,
|
|
78298
78459
|
maxContextSize,
|
|
78460
|
+
...modelCapabilities !== void 0 ? { modelCapabilities } : {},
|
|
78299
78461
|
videoUploader
|
|
78300
78462
|
};
|
|
78301
78463
|
}
|
|
@@ -78783,7 +78945,7 @@ new AsyncLocalStorage();
|
|
|
78783
78945
|
//#endregion
|
|
78784
78946
|
//#region ../../packages/kimi-core/src/soul-plus/host/tools.ts
|
|
78785
78947
|
async function createDefaultSoulPlusBuiltinTools(options) {
|
|
78786
|
-
const backgroundManager = new BackgroundProcessManager();
|
|
78948
|
+
const backgroundManager = options.backgroundManager ?? new BackgroundProcessManager();
|
|
78787
78949
|
const todoStore = new InMemoryTodoStore();
|
|
78788
78950
|
const hostEnvironment = await detectEnvironmentFromNode();
|
|
78789
78951
|
const defaultCapabilities = new Set(resolveModelCapabilities(options));
|
|
@@ -93460,18 +93622,21 @@ function applyManagedKimiCodeConfig(config, options) {
|
|
|
93460
93622
|
}
|
|
93461
93623
|
async function provisionManagedKimiCodeConfigAfterLogin(options) {
|
|
93462
93624
|
const models = await fetchManagedKimiCodeModels(options);
|
|
93463
|
-
const config = loadConfig({ pathConfig: options.pathConfig });
|
|
93625
|
+
const config = options.configPath !== void 0 ? loadConfigFile(options.configPath) : loadConfig({ pathConfig: options.pathConfig });
|
|
93464
93626
|
const applied = applyManagedKimiCodeConfig(config, {
|
|
93465
93627
|
models,
|
|
93466
93628
|
baseUrl: options.baseUrl
|
|
93467
93629
|
});
|
|
93468
|
-
await saveConfig(config, {
|
|
93630
|
+
await saveConfig(config, {
|
|
93631
|
+
pathConfig: options.pathConfig,
|
|
93632
|
+
filePath: options.configPath
|
|
93633
|
+
});
|
|
93469
93634
|
return {
|
|
93470
93635
|
providerName: KIMI_CODE_PROVIDER_NAME,
|
|
93471
93636
|
defaultModel: applied.defaultModel,
|
|
93472
93637
|
defaultThinking: applied.defaultThinking,
|
|
93473
93638
|
models,
|
|
93474
|
-
configPath: join(options.pathConfig.home, "config.toml")
|
|
93639
|
+
configPath: options.configPath ?? join(options.pathConfig.home, "config.toml")
|
|
93475
93640
|
};
|
|
93476
93641
|
}
|
|
93477
93642
|
//#endregion
|
|
@@ -93750,9 +93915,11 @@ var DefaultSoulPlusOAuthServiceImpl = class {
|
|
|
93750
93915
|
})).accessToken;
|
|
93751
93916
|
await provisionManagedKimiCodeConfigAfterLogin({
|
|
93752
93917
|
pathConfig: this.options.pathConfig,
|
|
93918
|
+
configPath: this.options.configPath,
|
|
93753
93919
|
accessToken
|
|
93754
93920
|
});
|
|
93755
|
-
|
|
93921
|
+
const config = this.options.configPath !== void 0 ? loadConfigFile(this.options.configPath) : loadConfig({ pathConfig: this.options.pathConfig });
|
|
93922
|
+
await this.options.onConfigProvisioned?.(config);
|
|
93756
93923
|
return {
|
|
93757
93924
|
provider_name: name,
|
|
93758
93925
|
ok: true
|
|
@@ -94646,7 +94813,9 @@ async function startCoreWireServer(options) {
|
|
|
94646
94813
|
compactionProvider: options.compactionProvider,
|
|
94647
94814
|
kosong: options.runtime.kosong,
|
|
94648
94815
|
tools: options.tools ?? [],
|
|
94816
|
+
...options.toolsProvider !== void 0 ? { toolsProvider: options.toolsProvider } : {},
|
|
94649
94817
|
...options.enabledToolNames !== void 0 ? { enabledToolNames: options.enabledToolNames } : {},
|
|
94818
|
+
...options.enabledToolNamesProvider !== void 0 ? { enabledToolNamesProvider: options.enabledToolNamesProvider } : {},
|
|
94650
94819
|
approval: options.approval,
|
|
94651
94820
|
...options.enableWiredApprovals !== void 0 ? { enableWiredApprovals: options.enableWiredApprovals } : {},
|
|
94652
94821
|
eventBus,
|
|
@@ -94668,10 +94837,13 @@ async function startCoreWireServer(options) {
|
|
|
94668
94837
|
server: options.transport,
|
|
94669
94838
|
hookEngine,
|
|
94670
94839
|
skillManager: options.skillManager,
|
|
94840
|
+
...options.skillManagerProvider !== void 0 ? { skillManagerProvider: options.skillManagerProvider } : {},
|
|
94671
94841
|
...options.agentTypeRegistry !== void 0 ? { agentTypeRegistry: options.agentTypeRegistry } : {},
|
|
94842
|
+
...options.agentTypeRegistryProvider !== void 0 ? { agentTypeRegistryProvider: options.agentTypeRegistryProvider } : {},
|
|
94672
94843
|
mcpRegistry: options.mcpRegistry,
|
|
94673
94844
|
authService: options.authService,
|
|
94674
94845
|
...options.backgroundProcessManager !== void 0 ? { backgroundProcessManager: options.backgroundProcessManager } : {},
|
|
94846
|
+
...options.backgroundProcessManagerProvider !== void 0 ? { backgroundProcessManagerProvider: options.backgroundProcessManagerProvider } : {},
|
|
94675
94847
|
sessionLifecycle: {
|
|
94676
94848
|
onSessionCreated: installBridge,
|
|
94677
94849
|
onSessionDestroyed: disposeBridge
|
|
@@ -94748,10 +94920,8 @@ const DEFAULT_RESERVED_CONTEXT_SIZE = 5e4;
|
|
|
94748
94920
|
const DEFAULT_RESERVED_CONTEXT_FRACTION = .25;
|
|
94749
94921
|
async function createDefaultSoulPlusWireClient(options) {
|
|
94750
94922
|
const pathConfig = options.pathConfig ?? new PathConfig();
|
|
94751
|
-
const
|
|
94752
|
-
|
|
94753
|
-
workspaceDir: options.workspaceDir
|
|
94754
|
-
});
|
|
94923
|
+
const configPath = options.configPath ?? join(pathConfig.home, "config.toml");
|
|
94924
|
+
const loadHostConfig = () => options.kimiConfig !== void 0 ? normalizeDefaultSoulPlusConfig(options.kimiConfig) : loadDefaultSoulPlusConfig(configPath);
|
|
94755
94925
|
const resolveHostConfig = (config) => {
|
|
94756
94926
|
const requestedDefaultModel = resolveDefaultSoulPlusDefaultModel({
|
|
94757
94927
|
...options,
|
|
@@ -94771,7 +94941,45 @@ async function createDefaultSoulPlusWireClient(options) {
|
|
|
94771
94941
|
let authService;
|
|
94772
94942
|
let activeVideoUploader;
|
|
94773
94943
|
let activeDefaultModel = defaultModel;
|
|
94774
|
-
let activeMaxContextSize =
|
|
94944
|
+
let activeMaxContextSize = getDefaultSoulPlusMaxContextSize({
|
|
94945
|
+
kimiConfig: effectiveConfig,
|
|
94946
|
+
defaultModel
|
|
94947
|
+
}, defaultModel);
|
|
94948
|
+
const backgroundManager = new BackgroundProcessManager();
|
|
94949
|
+
let defaultsPromise;
|
|
94950
|
+
const builtinByModel = /* @__PURE__ */ new Map();
|
|
94951
|
+
const ensureDefaults = () => {
|
|
94952
|
+
defaultsPromise ??= prepareDefaultSoulPlusSessionDefaults({
|
|
94953
|
+
workspaceDir: options.workspaceDir,
|
|
94954
|
+
pathConfig
|
|
94955
|
+
});
|
|
94956
|
+
return defaultsPromise;
|
|
94957
|
+
};
|
|
94958
|
+
const ensureBuiltin = (model) => {
|
|
94959
|
+
let existing = builtinByModel.get(model);
|
|
94960
|
+
if (existing !== void 0) return existing;
|
|
94961
|
+
existing = (async () => {
|
|
94962
|
+
const defaults = await ensureDefaults();
|
|
94963
|
+
const webProviders = createDefaultSoulPlusWebProviders({
|
|
94964
|
+
kimiConfig: effectiveConfig,
|
|
94965
|
+
oauthManagers: authService.oauthManagers,
|
|
94966
|
+
userAgent: options.userAgent ?? inferUserAgent(options.defaultHeaders)
|
|
94967
|
+
});
|
|
94968
|
+
return createDefaultSoulPlusBuiltinTools({
|
|
94969
|
+
workspaceDir: options.workspaceDir,
|
|
94970
|
+
workspace: defaults.workspace,
|
|
94971
|
+
kimiConfig: effectiveConfig,
|
|
94972
|
+
modelAlias: model,
|
|
94973
|
+
enabledToolNames: defaults.enabledToolNames,
|
|
94974
|
+
webSearchProvider: webProviders.webSearchProvider,
|
|
94975
|
+
urlFetcher: webProviders.urlFetcher,
|
|
94976
|
+
videoUploader: activeVideoUploader,
|
|
94977
|
+
backgroundManager
|
|
94978
|
+
});
|
|
94979
|
+
})();
|
|
94980
|
+
builtinByModel.set(model, existing);
|
|
94981
|
+
return existing;
|
|
94982
|
+
};
|
|
94775
94983
|
const buildRuntimeBundle = async (model) => {
|
|
94776
94984
|
const next = await createDefaultSoulPlusRuntimeBundle({
|
|
94777
94985
|
...options,
|
|
@@ -94786,7 +94994,8 @@ async function createDefaultSoulPlusWireClient(options) {
|
|
|
94786
94994
|
model: next.defaultModel,
|
|
94787
94995
|
runtime: next.runtime,
|
|
94788
94996
|
compactionProvider: next.compactionProvider,
|
|
94789
|
-
compactionConfig: buildCompactionConfig(effectiveConfig, next.defaultModel, next.maxContextSize)
|
|
94997
|
+
compactionConfig: buildCompactionConfig(effectiveConfig, next.defaultModel, next.maxContextSize),
|
|
94998
|
+
...next.modelCapabilities !== void 0 ? { modelCapabilities: next.modelCapabilities } : {}
|
|
94790
94999
|
};
|
|
94791
95000
|
};
|
|
94792
95001
|
const reloadConfigFromDisk = async () => {
|
|
@@ -94795,6 +95004,7 @@ async function createDefaultSoulPlusWireClient(options) {
|
|
|
94795
95004
|
effectiveConfig = resolved.effectiveConfig;
|
|
94796
95005
|
defaultModel = resolved.defaultModel;
|
|
94797
95006
|
activeDefaultModel = defaultModel;
|
|
95007
|
+
builtinByModel.clear();
|
|
94798
95008
|
activeMaxContextSize = getDefaultSoulPlusMaxContextSize({
|
|
94799
95009
|
kimiConfig: effectiveConfig,
|
|
94800
95010
|
defaultModel
|
|
@@ -94804,45 +95014,28 @@ async function createDefaultSoulPlusWireClient(options) {
|
|
|
94804
95014
|
kimiConfig: effectiveConfig,
|
|
94805
95015
|
modelAlias: defaultModel,
|
|
94806
95016
|
pathConfig,
|
|
95017
|
+
configPath,
|
|
94807
95018
|
forceManagedKimiCodeOAuth: defaultModel.length === 0,
|
|
94808
95019
|
onConfigProvisioned: reloadConfigFromDisk
|
|
94809
95020
|
});
|
|
94810
|
-
const
|
|
94811
|
-
|
|
94812
|
-
|
|
94813
|
-
workspaceDir: options.workspaceDir,
|
|
94814
|
-
pathConfig
|
|
94815
|
-
});
|
|
94816
|
-
const webProviders = createDefaultSoulPlusWebProviders({
|
|
94817
|
-
kimiConfig: effectiveConfig,
|
|
94818
|
-
oauthManagers: authService.oauthManagers,
|
|
94819
|
-
userAgent: options.userAgent ?? inferUserAgent(options.defaultHeaders)
|
|
94820
|
-
});
|
|
94821
|
-
const builtin = await createDefaultSoulPlusBuiltinTools({
|
|
94822
|
-
workspaceDir: options.workspaceDir,
|
|
94823
|
-
workspace: defaults.workspace,
|
|
94824
|
-
kimiConfig: effectiveConfig,
|
|
94825
|
-
modelAlias: defaultModel,
|
|
94826
|
-
enabledToolNames: defaults.enabledToolNames,
|
|
94827
|
-
webSearchProvider: webProviders.webSearchProvider,
|
|
94828
|
-
urlFetcher: webProviders.urlFetcher,
|
|
94829
|
-
videoUploader: activeVideoUploader
|
|
95021
|
+
const fallbackRuntimeBundle = await createDefaultSoulPlusRuntimeBundle({
|
|
95022
|
+
defaultModel: "",
|
|
95023
|
+
kimiConfig: effectiveConfig
|
|
94830
95024
|
});
|
|
94831
|
-
const backgroundManager = builtin.backgroundManager;
|
|
94832
95025
|
const handle = await createInProcessWireClient({
|
|
94833
95026
|
pathConfig,
|
|
94834
|
-
runtime:
|
|
94835
|
-
compactionProvider:
|
|
95027
|
+
runtime: fallbackRuntimeBundle.runtime,
|
|
95028
|
+
compactionProvider: fallbackRuntimeBundle.compactionProvider,
|
|
94836
95029
|
runtimeBundleProvider: ({ model }) => buildRuntimeBundle(model),
|
|
94837
95030
|
workspaceDir: options.workspaceDir,
|
|
94838
95031
|
defaultModel: activeDefaultModel,
|
|
94839
95032
|
defaultModelProvider: () => activeDefaultModel,
|
|
94840
|
-
|
|
95033
|
+
enabledToolNamesProvider: async () => (await ensureDefaults()).enabledToolNames,
|
|
94841
95034
|
enableWiredApprovals: true,
|
|
94842
|
-
defaultSystemPromptProvider: () =>
|
|
94843
|
-
|
|
94844
|
-
|
|
94845
|
-
|
|
95035
|
+
defaultSystemPromptProvider: async () => (await ensureDefaults()).systemPrompt,
|
|
95036
|
+
toolsProvider: async ({ model }) => (await ensureBuiltin(model)).tools,
|
|
95037
|
+
skillManagerProvider: async () => (await ensureDefaults()).skillManager,
|
|
95038
|
+
agentTypeRegistryProvider: async () => (await ensureDefaults()).agentTypeRegistry,
|
|
94846
95039
|
authService,
|
|
94847
95040
|
modelsProvider: () => ({
|
|
94848
95041
|
models: Object.keys(effectiveConfig.models ?? {}),
|
|
@@ -96076,6 +96269,12 @@ const APPROVED_PLAN_MARKER = "## Approved Plan:";
|
|
|
96076
96269
|
function str(v) {
|
|
96077
96270
|
return typeof v === "string" ? v : "";
|
|
96078
96271
|
}
|
|
96272
|
+
function formatSubagentTokens(usage) {
|
|
96273
|
+
if (usage === void 0) return void 0;
|
|
96274
|
+
const total = usage.input + usage.output;
|
|
96275
|
+
if (total <= 0) return void 0;
|
|
96276
|
+
return `${total >= 1e3 ? `${(total / 1e3).toFixed(1)}k` : String(total)} tok`;
|
|
96277
|
+
}
|
|
96079
96278
|
function extractApprovedPlan(output) {
|
|
96080
96279
|
const markerIndex = output.indexOf(APPROVED_PLAN_MARKER);
|
|
96081
96280
|
if (markerIndex < 0) return "";
|
|
@@ -96262,6 +96461,11 @@ var ToolCallComponent = class extends Container {
|
|
|
96262
96461
|
* 最后几行,避免占用过多屏幕。
|
|
96263
96462
|
*/
|
|
96264
96463
|
subagentText = "";
|
|
96464
|
+
subagentPhase;
|
|
96465
|
+
subagentRunInBackground = false;
|
|
96466
|
+
subagentUsage;
|
|
96467
|
+
subagentResultSummary;
|
|
96468
|
+
subagentError;
|
|
96265
96469
|
constructor(toolCall, result, colors, ui, markdownTheme) {
|
|
96266
96470
|
super();
|
|
96267
96471
|
this.toolCall = toolCall;
|
|
@@ -96346,8 +96550,42 @@ var ToolCallComponent = class extends Container {
|
|
|
96346
96550
|
this.rebuildContent();
|
|
96347
96551
|
this.ui?.requestRender();
|
|
96348
96552
|
}
|
|
96553
|
+
/**
|
|
96554
|
+
* 来自 wire 'subagent.spawned'。子 agent 已被注册,但内部活动事件
|
|
96555
|
+
* (content.delta / tool.call) 可能尚未到达——把 UI 切到 'spawning'
|
|
96556
|
+
* 占位状态(除非 runInBackground,这种情况直接进 'backgrounded')。
|
|
96557
|
+
*/
|
|
96558
|
+
onSubagentSpawned(meta) {
|
|
96559
|
+
this.subagentAgentId = meta.agentId;
|
|
96560
|
+
this.subagentAgentName = meta.agentName;
|
|
96561
|
+
this.subagentRunInBackground = meta.runInBackground;
|
|
96562
|
+
this.subagentPhase = meta.runInBackground ? "backgrounded" : "spawning";
|
|
96563
|
+
this.rebuildContent();
|
|
96564
|
+
this.ui?.requestRender();
|
|
96565
|
+
}
|
|
96566
|
+
/**
|
|
96567
|
+
* 来自 wire 'subagent.completed'。把 phase 切到 'done',记录 token
|
|
96568
|
+
* usage 与 result summary(用于头部右侧 chip + 尾部摘要行)。
|
|
96569
|
+
*/
|
|
96570
|
+
onSubagentCompleted(payload) {
|
|
96571
|
+
this.subagentPhase = "done";
|
|
96572
|
+
this.subagentUsage = payload.usage;
|
|
96573
|
+
this.subagentResultSummary = payload.resultSummary.length > 0 ? payload.resultSummary : void 0;
|
|
96574
|
+
this.rebuildContent();
|
|
96575
|
+
this.ui?.requestRender();
|
|
96576
|
+
}
|
|
96577
|
+
/**
|
|
96578
|
+
* 来自 wire 'subagent.failed'。
|
|
96579
|
+
*/
|
|
96580
|
+
onSubagentFailed(payload) {
|
|
96581
|
+
this.subagentPhase = "failed";
|
|
96582
|
+
this.subagentError = payload.error;
|
|
96583
|
+
this.rebuildContent();
|
|
96584
|
+
this.ui?.requestRender();
|
|
96585
|
+
}
|
|
96349
96586
|
appendSubagentText(text) {
|
|
96350
96587
|
this.subagentText += text;
|
|
96588
|
+
if (this.subagentPhase === void 0 || this.subagentPhase === "spawning") this.subagentPhase = "running";
|
|
96351
96589
|
this.rebuildContent();
|
|
96352
96590
|
this.ui?.requestRender();
|
|
96353
96591
|
}
|
|
@@ -96358,6 +96596,7 @@ var ToolCallComponent = class extends Container {
|
|
|
96358
96596
|
args: call.args,
|
|
96359
96597
|
...existing?.streamingArguments !== void 0 ? { streamingArguments: existing.streamingArguments } : {}
|
|
96360
96598
|
});
|
|
96599
|
+
if (this.subagentPhase === void 0 || this.subagentPhase === "spawning") this.subagentPhase = "running";
|
|
96361
96600
|
this.rebuildContent();
|
|
96362
96601
|
this.ui?.requestRender();
|
|
96363
96602
|
}
|
|
@@ -96440,10 +96679,11 @@ var ToolCallComponent = class extends Container {
|
|
|
96440
96679
|
this.buildSubagentBlock();
|
|
96441
96680
|
}
|
|
96442
96681
|
buildSubagentBlock() {
|
|
96443
|
-
if (this.subagentAgentId === void 0 && this.ongoingSubCalls.size === 0 && this.finishedSubCalls.length === 0 && this.subagentText.length === 0) return;
|
|
96682
|
+
if (this.subagentAgentId === void 0 && this.ongoingSubCalls.size === 0 && this.finishedSubCalls.length === 0 && this.subagentText.length === 0 && this.subagentPhase === void 0) return;
|
|
96444
96683
|
const dim = chalk.dim;
|
|
96445
|
-
const
|
|
96446
|
-
this.
|
|
96684
|
+
const phaseChip = this.formatPhaseChip();
|
|
96685
|
+
const headerLabel = this.subagentAgentName !== void 0 ? `subagent ${this.subagentAgentName} (${this.formatAgentId()})` : `subagent (${this.formatAgentId()})`;
|
|
96686
|
+
this.addChild(new Text(` ${dim(`↳ ${headerLabel}`)}${phaseChip}`, 0, 0));
|
|
96447
96687
|
if (this.hiddenSubCallCount > 0) {
|
|
96448
96688
|
const suffix = this.hiddenSubCallCount > 1 ? "s" : "";
|
|
96449
96689
|
this.addChild(new Text(dim.italic(` ${String(this.hiddenSubCallCount)} more tool call${suffix} ...`), 0, 0));
|
|
@@ -96465,6 +96705,50 @@ var ToolCallComponent = class extends Container {
|
|
|
96465
96705
|
const tailLines = this.subagentText.split("\n").slice(-3);
|
|
96466
96706
|
for (const line of tailLines) this.addChild(new Text(` ${dim(line)}`, 0, 0));
|
|
96467
96707
|
}
|
|
96708
|
+
if (this.subagentPhase === "done" && this.subagentResultSummary !== void 0) {
|
|
96709
|
+
const summaryLines = this.subagentResultSummary.split("\n").slice(0, 2);
|
|
96710
|
+
for (const line of summaryLines) this.addChild(new Text(` ${dim("└")} ${line}`, 0, 0));
|
|
96711
|
+
}
|
|
96712
|
+
if (this.subagentPhase === "failed" && this.subagentError !== void 0) {
|
|
96713
|
+
const errLines = this.subagentError.split("\n");
|
|
96714
|
+
for (const line of errLines) this.addChild(new Text(` ${chalk.hex(this.colors.error)("└")} ${line}`, 0, 0));
|
|
96715
|
+
}
|
|
96716
|
+
}
|
|
96717
|
+
/**
|
|
96718
|
+
* 头部右侧 phase / token chip。phase=undefined 时无 chip。
|
|
96719
|
+
* spawning → ↻ starting…
|
|
96720
|
+
* running → ↻ running
|
|
96721
|
+
* done → ✓ N tools · 8.4k tok
|
|
96722
|
+
* failed → ✗ failed
|
|
96723
|
+
* backgrounded → ◐ backgrounded
|
|
96724
|
+
*/
|
|
96725
|
+
formatPhaseChip() {
|
|
96726
|
+
if (this.subagentPhase === void 0) return "";
|
|
96727
|
+
const dim = chalk.dim;
|
|
96728
|
+
const parts = [];
|
|
96729
|
+
switch (this.subagentPhase) {
|
|
96730
|
+
case "spawning":
|
|
96731
|
+
parts.push("↻ starting…");
|
|
96732
|
+
break;
|
|
96733
|
+
case "running":
|
|
96734
|
+
parts.push("↻ running");
|
|
96735
|
+
break;
|
|
96736
|
+
case "done": {
|
|
96737
|
+
parts.push(chalk.hex(this.colors.success)("✓ done"));
|
|
96738
|
+
const toolCount = this.finishedSubCalls.length + this.hiddenSubCallCount;
|
|
96739
|
+
if (toolCount > 0) parts.push(`${String(toolCount)} tool${toolCount > 1 ? "s" : ""}`);
|
|
96740
|
+
const tokens = formatSubagentTokens(this.subagentUsage);
|
|
96741
|
+
if (tokens !== void 0) parts.push(tokens);
|
|
96742
|
+
break;
|
|
96743
|
+
}
|
|
96744
|
+
case "failed":
|
|
96745
|
+
parts.push(chalk.hex(this.colors.error)("✗ failed"));
|
|
96746
|
+
break;
|
|
96747
|
+
case "backgrounded":
|
|
96748
|
+
parts.push("◐ backgrounded");
|
|
96749
|
+
break;
|
|
96750
|
+
}
|
|
96751
|
+
return parts.length > 0 ? dim(` · ${parts.join(" · ")}`) : "";
|
|
96468
96752
|
}
|
|
96469
96753
|
formatAgentId() {
|
|
96470
96754
|
const id = this.subagentAgentId ?? "";
|
|
@@ -97392,6 +97676,28 @@ function handleSubagentSourceEvent(ectx, payload) {
|
|
|
97392
97676
|
});
|
|
97393
97677
|
}
|
|
97394
97678
|
}
|
|
97679
|
+
function handleSubagentSpawned(ectx, data) {
|
|
97680
|
+
const tc = ectx.state.pendingToolComponents.get(data.parent_tool_call_id);
|
|
97681
|
+
if (tc === void 0) return;
|
|
97682
|
+
tc.onSubagentSpawned({
|
|
97683
|
+
agentId: data.agent_id,
|
|
97684
|
+
agentName: data.agent_name,
|
|
97685
|
+
runInBackground: data.run_in_background
|
|
97686
|
+
});
|
|
97687
|
+
}
|
|
97688
|
+
function handleSubagentCompleted(ectx, data) {
|
|
97689
|
+
const tc = ectx.state.pendingToolComponents.get(data.parent_tool_call_id);
|
|
97690
|
+
if (tc === void 0) return;
|
|
97691
|
+
tc.onSubagentCompleted({
|
|
97692
|
+
usage: data.usage,
|
|
97693
|
+
resultSummary: data.result_summary
|
|
97694
|
+
});
|
|
97695
|
+
}
|
|
97696
|
+
function handleSubagentFailed(ectx, data) {
|
|
97697
|
+
const tc = ectx.state.pendingToolComponents.get(data.parent_tool_call_id);
|
|
97698
|
+
if (tc === void 0) return;
|
|
97699
|
+
tc.onSubagentFailed({ error: data.error });
|
|
97700
|
+
}
|
|
97395
97701
|
//#endregion
|
|
97396
97702
|
//#region src/utils/git/git-ls-files.ts
|
|
97397
97703
|
/**
|
|
@@ -100372,6 +100678,15 @@ function dispatchEvent(event, ectx, sendQueued) {
|
|
|
100372
100678
|
case "notification":
|
|
100373
100679
|
handleNotification(ectx, event.data);
|
|
100374
100680
|
break;
|
|
100681
|
+
case "subagent.spawned":
|
|
100682
|
+
handleSubagentSpawned(ectx, event.data);
|
|
100683
|
+
break;
|
|
100684
|
+
case "subagent.completed":
|
|
100685
|
+
handleSubagentCompleted(ectx, event.data);
|
|
100686
|
+
break;
|
|
100687
|
+
case "subagent.failed":
|
|
100688
|
+
handleSubagentFailed(ectx, event.data);
|
|
100689
|
+
break;
|
|
100375
100690
|
default: break;
|
|
100376
100691
|
}
|
|
100377
100692
|
}
|