@ycodium-ai/agent-kimi 0.2.3598 → 0.2.3614

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -49,7 +49,9 @@ var KIMI_CARD = {
49
49
  // `nativeModeSwitch`。kimi `acp` 的 `session/set_mode plan` 实测回 `{}` 并推
50
50
  // `current_mode_update`,不存在的 modeId 被 `-32602 Unknown modeId` 拒掉。
51
51
  runtimeModeSwitch: "in-session",
52
- nativeModeSwitch: true
52
+ nativeModeSwitch: true,
53
+ // 内置 `Agent` 工具调用经 `task.*` 报(见 `subagents.ts`);后台子 agent 的结束 ACP 不报。
54
+ reportsSpawnedAgents: true
53
55
  },
54
56
  credentialSources: [],
55
57
  diagnostics: [],
@@ -302,8 +304,8 @@ function asTrimmedString(value) {
302
304
  if (typeof value !== "string") {
303
305
  return void 0;
304
306
  }
305
- const trimmed = value.trim();
306
- return trimmed.length > 0 ? trimmed : void 0;
307
+ const trimmed2 = value.trim();
308
+ return trimmed2.length > 0 ? trimmed2 : void 0;
307
309
  }
308
310
  function readAcpText(source, key) {
309
311
  return asTrimmedString(source?.[key]);
@@ -334,12 +336,12 @@ function normalizeCommandValue(value) {
334
336
  return parts.length > 0 ? parts.join(" ") : void 0;
335
337
  }
336
338
  function stripTrailingExitCode(value) {
337
- const trimmed = value?.trim();
338
- if (!trimmed) {
339
+ const trimmed2 = value?.trim();
340
+ if (!trimmed2) {
339
341
  return void 0;
340
342
  }
341
- const match = /^(?<output>[\s\S]*?)(?:\s*<exited with exit code \d+>)\s*$/iu.exec(trimmed);
342
- const output2 = match?.groups?.output?.trim() ?? trimmed;
343
+ const match = /^(?<output>[\s\S]*?)(?:\s*<exited with exit code \d+>)\s*$/iu.exec(trimmed2);
344
+ const output2 = match?.groups?.output?.trim() ?? trimmed2;
343
345
  return output2.length > 0 ? output2 : void 0;
344
346
  }
345
347
  function extractCommandFromTitle(title) {
@@ -429,11 +431,11 @@ function extractPrimaryPath(data) {
429
431
  return paths[0];
430
432
  }
431
433
  function normalizeEquivalentValue(value) {
432
- const trimmed = asTrimmedString(value);
433
- if (!trimmed) {
434
+ const trimmed2 = asTrimmedString(value);
435
+ if (!trimmed2) {
434
436
  return void 0;
435
437
  }
436
- return trimmed.replace(/\s+/gu, " ").replace(/\s+(?:complete|completed|started)\s*$/iu, "").trim();
438
+ return trimmed2.replace(/\s+/gu, " ").replace(/\s+(?:complete|completed|started)\s*$/iu, "").trim();
437
439
  }
438
440
  function isEquivalent(left, right) {
439
441
  const normalizedLeft = normalizeEquivalentValue(left)?.toLowerCase();
@@ -587,13 +589,14 @@ function extractToolCallCommand(rawInput, title) {
587
589
  }
588
590
  var TOOL_CALL_CONTENT_MAX_CHARS = 8e3;
589
591
  var TOOL_CALL_CONTENT_TRUNCATION_MARKER = "[Earlier output truncated]\n\n";
590
- function boundToolCallOutputText(text) {
591
- if (text.length <= TOOL_CALL_CONTENT_MAX_CHARS) {
592
+ function boundTailText(text, limit = TOOL_CALL_CONTENT_MAX_CHARS) {
593
+ if (text.length <= limit) {
592
594
  return text;
593
595
  }
594
- const tail = text.slice(text.length - TOOL_CALL_CONTENT_MAX_CHARS);
596
+ const tail = text.slice(text.length - limit);
595
597
  return `${TOOL_CALL_CONTENT_TRUNCATION_MARKER}${tail}`;
596
598
  }
599
+ var boundToolCallOutputText = (text) => boundTailText(text);
597
600
  var RAW_OUTPUT_TEXT_FIELDS = ["content", "stdout", "stderr", "output"];
598
601
  function boundToolCallRawOutput(rawOutput) {
599
602
  if (!isRecord(rawOutput)) {
@@ -705,6 +708,7 @@ function makeToolCallState(input2, options) {
705
708
  ...status ? { status } : {},
706
709
  ...command ? { command } : {},
707
710
  ...presentation?.detail ? { detail: presentation.detail } : {},
711
+ ...textContent ? { contentText: textContent } : {},
708
712
  data
709
713
  };
710
714
  }
@@ -715,6 +719,7 @@ function mergeToolCallState(previous, next) {
715
719
  const status = next.status ?? previous?.status;
716
720
  const command = next.command ?? previous?.command;
717
721
  const detail = next.detail ?? previous?.detail;
722
+ const contentText = "content" in next.data ? next.contentText : previous?.contentText;
718
723
  return {
719
724
  toolCallId: next.toolCallId,
720
725
  ...kind ? { kind } : {},
@@ -722,6 +727,7 @@ function mergeToolCallState(previous, next) {
722
727
  ...status ? { status } : {},
723
728
  ...command ? { command } : {},
724
729
  ...detail ? { detail } : {},
730
+ ...contentText ? { contentText } : {},
725
731
  data: {
726
732
  ...previous?.data,
727
733
  ...next.data
@@ -1268,6 +1274,54 @@ function retainToolCallUpdate(toolCalls, next, seen) {
1268
1274
  return { nextToolCall, decision };
1269
1275
  }
1270
1276
 
1277
+ // ../../packages/ycodium-acp-executor/src/executor/child-sessions.ts
1278
+ var CHILD_LIMIT = 256;
1279
+ var active = (status) => status === "running" || status === "waiting";
1280
+ function createChildSessionRegistry(limit = CHILD_LIMIT) {
1281
+ const records = /* @__PURE__ */ new Map();
1282
+ const evictOne = (keep) => {
1283
+ const protectedIds = /* @__PURE__ */ new Set();
1284
+ for (const [id, record2] of records) {
1285
+ if (!active(record2.observed)) continue;
1286
+ protectedIds.add(id);
1287
+ let parent = record2.parentAgentId;
1288
+ while (parent !== void 0 && !protectedIds.has(parent)) {
1289
+ protectedIds.add(parent);
1290
+ parent = records.get(parent)?.parentAgentId;
1291
+ }
1292
+ }
1293
+ for (const id of records.keys()) {
1294
+ if (id === keep || protectedIds.has(id)) continue;
1295
+ records.delete(id);
1296
+ return;
1297
+ }
1298
+ };
1299
+ const add = (sessionId, record2) => {
1300
+ records.set(sessionId, record2);
1301
+ if (records.size > limit) evictOne(sessionId);
1302
+ };
1303
+ return {
1304
+ has: (sessionId) => records.has(sessionId),
1305
+ get: (sessionId) => records.get(sessionId),
1306
+ mark: (sessionId) => {
1307
+ if (records.has(sessionId)) return;
1308
+ add(sessionId, {
1309
+ turnId: void 0,
1310
+ title: void 0,
1311
+ description: void 0,
1312
+ parentAgentId: void 0,
1313
+ observed: void 0,
1314
+ reported: void 0,
1315
+ reportedError: void 0,
1316
+ announced: false,
1317
+ pendingMetadata: false
1318
+ });
1319
+ },
1320
+ add,
1321
+ entries: () => records.entries()
1322
+ };
1323
+ }
1324
+
1271
1325
  // ../../node_modules/.pnpm/@agentclientprotocol+sdk@1.4.0_zod@4.6.5/node_modules/@agentclientprotocol/sdk/dist/schema/index.js
1272
1326
  var AGENT_METHODS = {
1273
1327
  initialize: "initialize",
@@ -4108,23 +4162,23 @@ function canParseURL(input2) {
4108
4162
  return false;
4109
4163
  }
4110
4164
  }
4111
- function validateURL(trimmed, def) {
4165
+ function validateURL(trimmed2, def) {
4112
4166
  if (!("normalize" in def) && !("hostname" in def) && !("protocol" in def)) {
4113
- return canParseURL(trimmed) || URL_UNPARSEABLE;
4167
+ return canParseURL(trimmed2) || URL_UNPARSEABLE;
4114
4168
  }
4115
- return parseURLObject(trimmed, def);
4169
+ return parseURLObject(trimmed2, def);
4116
4170
  }
4117
- function parseURLObject(trimmed, def) {
4118
- if (!def.normalize && def.protocol?.source === httpProtocol.source && !/^https?:\/\//i.test(trimmed)) {
4171
+ function parseURLObject(trimmed2, def) {
4172
+ if (!def.normalize && def.protocol?.source === httpProtocol.source && !/^https?:\/\//i.test(trimmed2)) {
4119
4173
  return URL_BAD_FORMAT;
4120
4174
  }
4121
4175
  try {
4122
4176
  if (typeof URL !== "undefined") {
4123
4177
  const URLStatic = URL;
4124
4178
  if (typeof URLStatic.parse === "function")
4125
- return URLStatic.parse(trimmed) ?? URL_UNPARSEABLE;
4179
+ return URLStatic.parse(trimmed2) ?? URL_UNPARSEABLE;
4126
4180
  }
4127
- return new URL(trimmed);
4181
+ return new URL(trimmed2);
4128
4182
  } catch {
4129
4183
  return URL_UNPARSEABLE;
4130
4184
  }
@@ -4145,8 +4199,8 @@ var $ZodURL = /* @__PURE__ */ $constructor("$ZodURL", (inst, def) => {
4145
4199
  $ZodStringFormat.init(inst, def);
4146
4200
  inst._zod.check = (payload) => {
4147
4201
  try {
4148
- const trimmed = payload.value.trim();
4149
- const url2 = validateURL(trimmed, def);
4202
+ const trimmed2 = payload.value.trim();
4203
+ const url2 = validateURL(trimmed2, def);
4150
4204
  if (url2 === URL_BAD_FORMAT) {
4151
4205
  payload.issues.push({
4152
4206
  code: "invalid_format",
@@ -4169,7 +4223,7 @@ var $ZodURL = /* @__PURE__ */ $constructor("$ZodURL", (inst, def) => {
4169
4223
  return;
4170
4224
  }
4171
4225
  if (url2 === true) {
4172
- payload.value = stripTabAndNewline(trimmed);
4226
+ payload.value = stripTabAndNewline(trimmed2);
4173
4227
  return;
4174
4228
  }
4175
4229
  if (def.hostname && !urlHostnameOk(url2, def.hostname)) {
@@ -4194,7 +4248,7 @@ var $ZodURL = /* @__PURE__ */ $constructor("$ZodURL", (inst, def) => {
4194
4248
  continue: !def.abort
4195
4249
  });
4196
4250
  }
4197
- payload.value = def.normalize ? url2.href : stripTabAndNewline(trimmed);
4251
+ payload.value = def.normalize ? url2.href : stripTabAndNewline(trimmed2);
4198
4252
  return;
4199
4253
  } catch (_) {
4200
4254
  payload.issues.push({
@@ -24796,12 +24850,12 @@ function asBytes(chunk) {
24796
24850
  throw new TypeError("ACP readable produced a non-byte chunk");
24797
24851
  }
24798
24852
  function isEnqueueableLine(lineBytes) {
24799
- const trimmed = textDecoder.decode(lineBytes).trim();
24800
- if (!trimmed) {
24853
+ const trimmed2 = textDecoder.decode(lineBytes).trim();
24854
+ if (!trimmed2) {
24801
24855
  return false;
24802
24856
  }
24803
24857
  try {
24804
- const parsed = JSON.parse(trimmed);
24858
+ const parsed = JSON.parse(trimmed2);
24805
24859
  return typeof parsed === "object" && parsed !== null;
24806
24860
  } catch {
24807
24861
  return false;
@@ -25002,9 +25056,9 @@ function createAcpConnection(options) {
25002
25056
  const handleChunk = async (bytes) => {
25003
25057
  const lines = lineBuffer.push(bytes);
25004
25058
  for (const line of lines) {
25005
- const trimmed = textDecoder.decode(line).trim();
25006
- if (trimmed) {
25007
- log({ direction: "incoming", stage: "raw", payload: trimmed });
25059
+ const trimmed2 = textDecoder.decode(line).trim();
25060
+ if (trimmed2) {
25061
+ log({ direction: "incoming", stage: "raw", payload: trimmed2 });
25008
25062
  }
25009
25063
  }
25010
25064
  const expected = lines.filter(isEnqueueableLine).length;
@@ -25041,9 +25095,9 @@ function createAcpConnection(options) {
25041
25095
  enqueueInbound(async () => {
25042
25096
  const last = lineBuffer.flush();
25043
25097
  if (last) {
25044
- const trimmed = textDecoder.decode(last).trim();
25045
- if (trimmed) {
25046
- log({ direction: "incoming", stage: "raw", payload: trimmed });
25098
+ const trimmed2 = textDecoder.decode(last).trim();
25099
+ if (trimmed2) {
25100
+ log({ direction: "incoming", stage: "raw", payload: trimmed2 });
25047
25101
  }
25048
25102
  }
25049
25103
  const expected = last && isEnqueueableLine(last) ? 1 : 0;
@@ -25250,7 +25304,9 @@ function createExecutorLifecycle({
25250
25304
  eventStream,
25251
25305
  onInputDead,
25252
25306
  shared,
25253
- promptResults
25307
+ promptResults,
25308
+ settleToolSubagents,
25309
+ settleSessionEnded
25254
25310
  }) {
25255
25311
  const cancel = async (turnId) => {
25256
25312
  failIfDisposed();
@@ -25281,6 +25337,7 @@ function createExecutorLifecycle({
25281
25337
  shared.promptsInFlight = 0;
25282
25338
  shared.activeTurnId = void 0;
25283
25339
  shared.status = "ready";
25340
+ settleToolSubagents("turn-interrupted", targetTurnId);
25284
25341
  emit({
25285
25342
  type: "turn.completed",
25286
25343
  ...stamp(),
@@ -25392,6 +25449,7 @@ function createExecutorLifecycle({
25392
25449
  shared.abortLocalPrompt?.();
25393
25450
  promptResults.finishUnreceived();
25394
25451
  await promptResults.wait();
25452
+ settleSessionEnded();
25395
25453
  emit({
25396
25454
  type: "session.exited",
25397
25455
  ...stamp(),
@@ -25495,6 +25553,304 @@ var acpPromptFailureCategory = (error62, input2) => {
25495
25553
  return error62 instanceof AcpConnectionClosedError ? acpTransportIfEstablished(input2.sessionEstablished, input2.disposed) : void 0;
25496
25554
  };
25497
25555
 
25556
+ // ../../packages/ycodium-acp-executor/src/tool-subagents.ts
25557
+ var EXCLUDED = /* @__PURE__ */ Symbol("excluded");
25558
+ var sealedHidden = (entry) => ({
25559
+ sealed: true,
25560
+ keepToolRow: false,
25561
+ turnId: entry.turnId,
25562
+ kind: entry.kind,
25563
+ taskId: entry.taskId,
25564
+ facts: entry.facts
25565
+ });
25566
+ function toolSubagentSettlementOfTurn(state, failureCategory) {
25567
+ if (state === "cancelled") return "turn-interrupted";
25568
+ return state === "failed" && failureCategory === "transport" ? "session-ended" : "turn-ended";
25569
+ }
25570
+ var sameFacts = (a, b) => a.title === b.title && a.description === b.description && a.summary === b.summary && a.role === b.role && a.model === b.model && a.effort === b.effort && a.rosterKind === b.rosterKind;
25571
+ function linkage(taskId, toolCallId, facts) {
25572
+ return {
25573
+ taskId,
25574
+ taskType: "subagent",
25575
+ toolUseId: toolCallId,
25576
+ title: facts.title,
25577
+ ...facts.role === void 0 ? {} : { role: facts.role },
25578
+ ...facts.model === void 0 ? {} : { model: facts.model },
25579
+ ...facts.effort === void 0 ? {} : { effort: facts.effort },
25580
+ ...facts.rosterKind === void 0 ? {} : { rosterKind: facts.rosterKind }
25581
+ };
25582
+ }
25583
+ var isEntry = (value) => value !== EXCLUDED && !("sealed" in value);
25584
+ function createToolSubagentTracker(spec) {
25585
+ const calls = /* @__PURE__ */ new Map();
25586
+ const agentTasks = /* @__PURE__ */ new Map();
25587
+ const rememberAgent = (ref, taskId, facts) => {
25588
+ agentTasks.set(ref, { taskId, facts });
25589
+ };
25590
+ const turnOf = (turnId) => turnId === void 0 ? {} : { turnId };
25591
+ const onToolCallUpdated = (call) => {
25592
+ const tracked = calls.get(call.toolCallId);
25593
+ if (tracked === EXCLUDED) return void 0;
25594
+ if (tracked !== void 0 && !isEntry(tracked)) {
25595
+ const late = spec.resolve?.(call, tracked.kind, { known: true, background: false });
25596
+ if (late !== void 0 && late.type !== "hold" && late.agentRef !== void 0) {
25597
+ rememberAgent(late.agentRef, tracked.taskId, tracked.facts);
25598
+ }
25599
+ const repeatedTerminal = call.status === "completed" || call.status === "failed";
25600
+ const events2 = [];
25601
+ const final = tracked.final;
25602
+ if (repeatedTerminal && final !== void 0 && final.summary === void 0 && late?.type === "final" && late.status === final.status && late.summary !== void 0) {
25603
+ final.summary = late.summary;
25604
+ events2.push({
25605
+ type: "task.completed",
25606
+ ...turnOf(tracked.turnId),
25607
+ payload: {
25608
+ ...linkage(tracked.taskId, call.toolCallId, tracked.facts),
25609
+ status: final.status,
25610
+ summary: late.summary
25611
+ }
25612
+ });
25613
+ }
25614
+ return {
25615
+ events: events2,
25616
+ handled: !tracked.keepToolRow || repeatedTerminal,
25617
+ ...turnOf(tracked.turnId)
25618
+ };
25619
+ }
25620
+ let known = tracked;
25621
+ let kind;
25622
+ if (known === void 0) {
25623
+ const recognized = spec.recognize(call);
25624
+ if (recognized === void 0) return void 0;
25625
+ if (recognized === "exclude") {
25626
+ calls.set(call.toolCallId, EXCLUDED);
25627
+ return void 0;
25628
+ }
25629
+ kind = recognized;
25630
+ } else {
25631
+ kind = known.kind;
25632
+ }
25633
+ const resolution = spec.resolve?.(call, kind, {
25634
+ known: known?.announced === true,
25635
+ background: known?.background ?? false
25636
+ });
25637
+ if (known?.phase === "idle" && resolution?.type !== "final") {
25638
+ if (resolution?.type === "background" && resolution.agentRef !== void 0) {
25639
+ rememberAgent(resolution.agentRef, known.taskId, known.facts);
25640
+ }
25641
+ return { events: [], handled: true };
25642
+ }
25643
+ let carriedTurn;
25644
+ if (known === void 0 || !known.announced) {
25645
+ if (resolution === void 0 && !(spec.ready?.(call, kind) ?? true)) {
25646
+ if (known === void 0) {
25647
+ calls.set(call.toolCallId, {
25648
+ kind,
25649
+ taskId: call.toolCallId,
25650
+ turnId: call.turnId,
25651
+ facts: spec.facts(call, kind, void 0),
25652
+ phase: "open",
25653
+ status: void 0,
25654
+ background: false,
25655
+ announced: false
25656
+ });
25657
+ }
25658
+ return { events: [], handled: true };
25659
+ }
25660
+ carriedTurn = known?.turnId;
25661
+ calls.delete(call.toolCallId);
25662
+ known = void 0;
25663
+ }
25664
+ const events = [];
25665
+ const ref = known === void 0 ? spec.resumes?.(call, kind) : void 0;
25666
+ const prior = ref === void 0 ? void 0 : agentTasks.get(ref);
25667
+ const facts = spec.facts(call, kind, prior?.facts ?? known?.facts);
25668
+ const turnId = known?.turnId ?? carriedTurn ?? call.turnId;
25669
+ const turn = turnOf(turnId);
25670
+ let entry;
25671
+ if (known !== void 0) {
25672
+ entry = known;
25673
+ } else if (prior !== void 0) {
25674
+ for (const [id, other] of calls) {
25675
+ if (other === EXCLUDED || other.taskId !== prior.taskId) continue;
25676
+ if (isEntry(other)) calls.set(id, sealedHidden(other));
25677
+ else if (other.final !== void 0) {
25678
+ const { final: _superseded, ...rest } = other;
25679
+ calls.set(id, rest);
25680
+ }
25681
+ }
25682
+ entry = {
25683
+ kind,
25684
+ taskId: prior.taskId,
25685
+ turnId,
25686
+ facts,
25687
+ phase: "open",
25688
+ status: "running",
25689
+ background: false,
25690
+ announced: true
25691
+ };
25692
+ calls.set(call.toolCallId, entry);
25693
+ events.push({
25694
+ type: "task.updated",
25695
+ ...turn,
25696
+ payload: {
25697
+ ...linkage(entry.taskId, call.toolCallId, facts),
25698
+ description: facts.description,
25699
+ status: "running"
25700
+ }
25701
+ });
25702
+ } else {
25703
+ entry = {
25704
+ kind,
25705
+ taskId: call.toolCallId,
25706
+ turnId,
25707
+ facts,
25708
+ phase: "open",
25709
+ status: void 0,
25710
+ background: false,
25711
+ announced: true
25712
+ };
25713
+ calls.set(call.toolCallId, entry);
25714
+ if (spec.announce === "started") {
25715
+ events.push({
25716
+ type: "task.started",
25717
+ ...turn,
25718
+ payload: {
25719
+ ...linkage(entry.taskId, call.toolCallId, facts),
25720
+ description: facts.description
25721
+ }
25722
+ });
25723
+ }
25724
+ }
25725
+ const seen = known !== void 0 || prior !== void 0;
25726
+ const wasStatus = entry.status;
25727
+ entry.turnId = turnId;
25728
+ if (resolution?.type === "hold") {
25729
+ entry.facts = facts;
25730
+ return { events, handled: true };
25731
+ }
25732
+ if (resolution?.type === "background") {
25733
+ entry.facts = resolution.role === void 0 ? facts : { ...facts, role: resolution.role };
25734
+ if (!entry.background) {
25735
+ events.push({
25736
+ type: "task.updated",
25737
+ ...turn,
25738
+ payload: {
25739
+ ...linkage(entry.taskId, call.toolCallId, entry.facts),
25740
+ status: "running",
25741
+ isBackgrounded: true
25742
+ }
25743
+ });
25744
+ }
25745
+ entry.background = true;
25746
+ entry.status = "running";
25747
+ if (resolution.agentRef !== void 0)
25748
+ rememberAgent(resolution.agentRef, entry.taskId, entry.facts);
25749
+ return { events, handled: true };
25750
+ }
25751
+ if (resolution?.type === "final") {
25752
+ const finalFacts = resolution.role === void 0 ? facts : { ...facts, role: resolution.role };
25753
+ const keepToolRow = resolution.keepToolRow === true;
25754
+ const base = linkage(entry.taskId, call.toolCallId, finalFacts);
25755
+ if (resolution.agentRef !== void 0)
25756
+ rememberAgent(resolution.agentRef, entry.taskId, finalFacts);
25757
+ calls.set(call.toolCallId, {
25758
+ sealed: true,
25759
+ keepToolRow,
25760
+ ...keepToolRow && (resolution.status === "completed" || resolution.status === "failed") ? { final: { status: resolution.status, summary: resolution.summary } } : {},
25761
+ turnId,
25762
+ kind,
25763
+ taskId: entry.taskId,
25764
+ facts: finalFacts
25765
+ });
25766
+ if (resolution.status === "completed" || resolution.status === "failed") {
25767
+ events.push({
25768
+ type: "task.completed",
25769
+ ...turn,
25770
+ payload: {
25771
+ ...base,
25772
+ status: resolution.status,
25773
+ ...resolution.summary === void 0 ? {} : { summary: resolution.summary }
25774
+ }
25775
+ });
25776
+ } else {
25777
+ events.push({
25778
+ type: "task.updated",
25779
+ ...turn,
25780
+ payload: {
25781
+ ...base,
25782
+ status: resolution.status,
25783
+ ...resolution.status === "idle" ? { outcomeUnknown: true } : {},
25784
+ ...resolution.description === void 0 ? {} : { description: resolution.description },
25785
+ ...resolution.timelineBypass === true ? { timelineBypass: true } : {}
25786
+ }
25787
+ });
25788
+ }
25789
+ return { events, handled: !keepToolRow, ...turn };
25790
+ }
25791
+ const status = call.status === "pending" && wasStatus !== "running" ? "pending" : "running";
25792
+ const changed = !seen && spec.announce === "progress" ? true : seen && (wasStatus !== status || !sameFacts(entry.facts, facts));
25793
+ entry.facts = facts;
25794
+ entry.status = status;
25795
+ if (changed) {
25796
+ events.push({
25797
+ type: "task.progress",
25798
+ ...turn,
25799
+ payload: {
25800
+ ...linkage(entry.taskId, call.toolCallId, facts),
25801
+ description: facts.description,
25802
+ ...facts.summary === void 0 ? {} : { summary: facts.summary },
25803
+ status
25804
+ }
25805
+ });
25806
+ }
25807
+ return { events, handled: true };
25808
+ };
25809
+ const settle2 = (settlement, turnId) => {
25810
+ const events = [];
25811
+ for (const [toolCallId, entry] of calls) {
25812
+ if (!isEntry(entry)) continue;
25813
+ if (settlement !== "session-ended" && turnId !== void 0 && entry.turnId !== void 0 && entry.turnId !== turnId) {
25814
+ continue;
25815
+ }
25816
+ const interrupted = settlement === "turn-interrupted";
25817
+ const ended = settlement === "session-ended" || interrupted && spec.interruptedMayStillRun !== true;
25818
+ if (!entry.announced) {
25819
+ if (ended) calls.set(toolCallId, sealedHidden(entry));
25820
+ else entry.phase = "idle";
25821
+ continue;
25822
+ }
25823
+ const base = linkage(entry.taskId, toolCallId, entry.facts);
25824
+ const turn = turnOf(entry.turnId);
25825
+ if (ended) {
25826
+ events.push({ type: "task.updated", ...turn, payload: { ...base, status: "cancelled" } });
25827
+ calls.set(toolCallId, sealedHidden(entry));
25828
+ continue;
25829
+ }
25830
+ if (entry.phase === "idle") continue;
25831
+ const note = spec.endNote({ interrupted, background: entry.background });
25832
+ events.push({
25833
+ type: "task.updated",
25834
+ ...turn,
25835
+ payload: {
25836
+ ...base,
25837
+ status: "idle",
25838
+ outcomeUnknown: true,
25839
+ description: note.description,
25840
+ ...note.timelineBypass === true ? { timelineBypass: true } : {}
25841
+ }
25842
+ });
25843
+ entry.phase = "idle";
25844
+ }
25845
+ return events;
25846
+ };
25847
+ const mayStillRun = () => {
25848
+ for (const entry of calls.values()) if (isEntry(entry)) return true;
25849
+ return false;
25850
+ };
25851
+ return { onToolCallUpdated, settle: settle2, mayStillRun };
25852
+ }
25853
+
25498
25854
  // ../../packages/ycodium-acp-executor/src/executor/prompt.ts
25499
25855
  function createExecutorPrompt({
25500
25856
  isLive,
@@ -25514,7 +25870,8 @@ function createExecutorPrompt({
25514
25870
  applyRequestedModel,
25515
25871
  notifyPromptSettled,
25516
25872
  shared,
25517
- promptResults
25873
+ promptResults,
25874
+ settleToolSubagents
25518
25875
  }) {
25519
25876
  const settleTurn = (input2) => {
25520
25877
  if (!isLive(input2.expectedGeneration) || input2.record.finished) {
@@ -25538,6 +25895,10 @@ function createExecutorPrompt({
25538
25895
  shared.status = "ready";
25539
25896
  }
25540
25897
  const interrupted = promptResults.hasInterrupted(input2.turnId);
25898
+ settleToolSubagents(
25899
+ toolSubagentSettlementOfTurn(interrupted ? "cancelled" : input2.state, input2.failureCategory),
25900
+ input2.turnId
25901
+ );
25541
25902
  emit({
25542
25903
  type: "turn.completed",
25543
25904
  ...stamp(),
@@ -26426,11 +26787,19 @@ function acpInitializePayload(clientInfo, overrides) {
26426
26787
  writeTextFile: false,
26427
26788
  ...overrides?.fs
26428
26789
  },
26429
- terminal: overrides?.terminal ?? false
26790
+ terminal: overrides?.terminal ?? false,
26791
+ ...overrides?.subagents === true ? { subagents: {} } : {}
26430
26792
  },
26431
26793
  clientInfo
26432
26794
  };
26433
26795
  }
26796
+ function acpCapabilityOverrides(input2) {
26797
+ const rest = {
26798
+ ...input2.clientCapabilities
26799
+ };
26800
+ delete rest.subagents;
26801
+ return input2.subagentSessions === true ? { ...rest, subagents: true } : rest;
26802
+ }
26434
26803
 
26435
26804
  // ../../packages/ycodium-acp-executor/src/refreshDeadline.ts
26436
26805
  var MODEL_CATALOG_REFRESH_TIMEOUT_MS = 9e4;
@@ -26530,6 +26899,20 @@ function createExecutorSession({
26530
26899
  failIfDisposed,
26531
26900
  shared
26532
26901
  }) {
26902
+ const requestSession = async (method, params) => {
26903
+ const knownSessionId = params["sessionId"];
26904
+ if (typeof knownSessionId === "string") shared.ownSessionIds.add(knownSessionId);
26905
+ shared.sessionOpensInFlight += 1;
26906
+ try {
26907
+ const result = await connection.request(method, params);
26908
+ const sessionId = result?.sessionId;
26909
+ if (typeof sessionId === "string" && sessionId.length > 0)
26910
+ shared.ownSessionIds.add(sessionId);
26911
+ return result;
26912
+ } finally {
26913
+ shared.sessionOpensInFlight -= 1;
26914
+ }
26915
+ };
26533
26916
  const openFreshSession = async (expectedGeneration) => {
26534
26917
  const initialized = shared.initializeResult;
26535
26918
  if (initialized === void 0) {
@@ -26540,7 +26923,7 @@ function createExecutorSession({
26540
26923
  initialized.agentCapabilities
26541
26924
  );
26542
26925
  const additionalDirectories = options.additionalDirectories !== void 0 && options.additionalDirectories.length > 0 ? { additionalDirectories: options.additionalDirectories } : {};
26543
- const created = await connection.request("session/new", {
26926
+ const created = await requestSession("session/new", {
26544
26927
  cwd: options.cwd,
26545
26928
  mcpServers,
26546
26929
  ...additionalDirectories
@@ -26657,7 +27040,13 @@ function createExecutorSession({
26657
27040
  return applyOnce(input2.state, input2.acpSessionId, input2.setup, false);
26658
27041
  };
26659
27042
  const startOnce = async (expectedGeneration) => {
26660
- const initializePayload = acpInitializePayload(options.clientInfo, options.clientCapabilities);
27043
+ const initializePayload = acpInitializePayload(
27044
+ options.clientInfo,
27045
+ acpCapabilityOverrides({
27046
+ clientCapabilities: options.clientCapabilities,
27047
+ subagentSessions: options.subagentSessions
27048
+ })
27049
+ );
26661
27050
  const initialized = await connection.request(
26662
27051
  "initialize",
26663
27052
  initializePayload
@@ -26681,7 +27070,7 @@ function createExecutorSession({
26681
27070
  let setup;
26682
27071
  let nextSessionId;
26683
27072
  if (options.resumeSessionId && options.resumeMethod === "session/resume") {
26684
- setup = await connection.request("session/resume", {
27073
+ setup = await requestSession("session/resume", {
26685
27074
  sessionId: options.resumeSessionId,
26686
27075
  cwd: options.cwd,
26687
27076
  mcpServers,
@@ -26691,7 +27080,7 @@ function createExecutorSession({
26691
27080
  } else if (options.resumeSessionId) {
26692
27081
  shared.sessionLoadInFlight = true;
26693
27082
  try {
26694
- setup = await connection.request("session/load", {
27083
+ setup = await requestSession("session/load", {
26695
27084
  sessionId: options.resumeSessionId,
26696
27085
  cwd: options.cwd,
26697
27086
  mcpServers,
@@ -26706,7 +27095,7 @@ function createExecutorSession({
26706
27095
  });
26707
27096
  nextSessionId = options.resumeSessionId;
26708
27097
  } else {
26709
- const created = await connection.request("session/new", {
27098
+ const created = await requestSession("session/new", {
26710
27099
  cwd: options.cwd,
26711
27100
  mcpServers,
26712
27101
  ...additionalDirectories
@@ -26891,6 +27280,127 @@ function createExecutorSettlement({
26891
27280
  return { rememberSettledPromptKey, abortPendingSettlement, settleFromNotification };
26892
27281
  }
26893
27282
 
27283
+ // ../../packages/ycodium-acp-executor/src/executor/subagent-sessions.ts
27284
+ function parseState(raw) {
27285
+ if (!isRecord(raw)) return { status: void 0, error: void 0 };
27286
+ const state = raw["state"];
27287
+ if (state === "running") return { status: "running", error: void 0 };
27288
+ if (state === "requires_action") return { status: "waiting", error: void 0 };
27289
+ if (state !== "idle") return { status: void 0, error: void 0 };
27290
+ const stopReason = raw["stopReason"];
27291
+ const error62 = isRecord(raw["error"]) ? asTrimmedString(raw["error"]["message"]) : void 0;
27292
+ if (stopReason === "error" || isRecord(raw["error"])) return { status: "failed", error: error62 };
27293
+ if (stopReason === "cancelled") return { status: "cancelled", error: void 0 };
27294
+ return { status: "idle", error: void 0 };
27295
+ }
27296
+ function childLinkage(childId, record2) {
27297
+ return {
27298
+ taskType: "subagent",
27299
+ rosterKind: "subagent",
27300
+ nativeSessionId: childId,
27301
+ timelineBypass: true,
27302
+ ...record2.title === void 0 ? {} : { title: record2.title }
27303
+ };
27304
+ }
27305
+ function createSubagentSessionTracker(registry2) {
27306
+ const observe = (notification, ctx) => {
27307
+ const update = notification.update;
27308
+ if (!isRecord(update) || update["sessionUpdate"] !== "subagent_update") return [];
27309
+ const childId = asTrimmedString(update["sessionId"]);
27310
+ if (childId === void 0 || ctx.ownSessionIds.has(childId)) return [];
27311
+ const outerId = notification.sessionId;
27312
+ const nested = registry2.has(outerId);
27313
+ if (ctx.foreign && !nested) return [];
27314
+ if (outerId === childId) return [];
27315
+ const title = asTrimmedString(update["title"]);
27316
+ const description = asTrimmedString(update["description"]);
27317
+ const state = parseState(update["state"]);
27318
+ let record2 = registry2.get(childId);
27319
+ if (record2 === void 0) {
27320
+ record2 = {
27321
+ turnId: ctx.turnId,
27322
+ title,
27323
+ description,
27324
+ parentAgentId: nested ? outerId : void 0,
27325
+ observed: state.status,
27326
+ reported: void 0,
27327
+ reportedError: void 0,
27328
+ // 重放里恢复的身份:宿主早有这一行,不再发 started。
27329
+ announced: ctx.silent,
27330
+ pendingMetadata: false
27331
+ };
27332
+ registry2.add(childId, record2);
27333
+ }
27334
+ record2.turnId ??= ctx.turnId;
27335
+ if (!ctx.silent) {
27336
+ if (title !== void 0 && title !== record2.title || description !== void 0 && description !== record2.description) {
27337
+ record2.pendingMetadata = true;
27338
+ }
27339
+ }
27340
+ if (title !== void 0) record2.title = title;
27341
+ if (description !== void 0) record2.description = description;
27342
+ if (state.status !== void 0) record2.observed = state.status;
27343
+ if (ctx.silent) return [];
27344
+ const linkage2 = childLinkage(childId, record2);
27345
+ const turn = record2.turnId === void 0 ? {} : { turnId: record2.turnId };
27346
+ const events = [];
27347
+ if (!record2.announced && state.status === void 0) return events;
27348
+ if (!record2.announced) {
27349
+ record2.announced = true;
27350
+ record2.pendingMetadata = false;
27351
+ events.push({
27352
+ type: "task.started",
27353
+ ...turn,
27354
+ payload: {
27355
+ taskId: childId,
27356
+ ...linkage2,
27357
+ ...record2.description === void 0 ? {} : { description: record2.description }
27358
+ }
27359
+ });
27360
+ }
27361
+ const statusChanged = state.status !== void 0 && (state.status !== record2.reported || record2.pendingMetadata || state.error !== void 0 && state.error !== record2.reportedError);
27362
+ if (statusChanged) {
27363
+ record2.pendingMetadata = false;
27364
+ record2.reported = state.status;
27365
+ record2.reportedError = state.status === "failed" ? state.error ?? record2.reportedError : void 0;
27366
+ events.push({
27367
+ type: "task.updated",
27368
+ ...turn,
27369
+ payload: {
27370
+ taskId: childId,
27371
+ status: state.status,
27372
+ ...state.error === void 0 ? {} : { error: state.error },
27373
+ ...record2.description === void 0 ? {} : { description: record2.description },
27374
+ ...linkage2
27375
+ }
27376
+ });
27377
+ }
27378
+ return events;
27379
+ };
27380
+ const settleSessionEnded = () => {
27381
+ const events = [];
27382
+ for (const [childId, record2] of registry2.entries()) {
27383
+ const last = record2.reported ?? record2.observed;
27384
+ if (!record2.announced || last !== "running" && last !== "waiting") continue;
27385
+ record2.reported = "idle";
27386
+ record2.observed = "idle";
27387
+ events.push({
27388
+ type: "task.updated",
27389
+ ...record2.turnId === void 0 ? {} : { turnId: record2.turnId },
27390
+ payload: {
27391
+ taskId: childId,
27392
+ status: "idle",
27393
+ outcomeUnknown: true,
27394
+ ...record2.description === void 0 ? {} : { description: record2.description },
27395
+ ...childLinkage(childId, record2)
27396
+ }
27397
+ });
27398
+ }
27399
+ return events;
27400
+ };
27401
+ return { observe, settleSessionEnded };
27402
+ }
27403
+
26894
27404
  // ../../packages/ycodium-acp-executor/src/executor/updates.ts
26895
27405
  var COMPACTION_SUMMARY_MAX_CHARS = 8e3;
26896
27406
  var SHOWN_TOOL_CALL_LIMIT = 256;
@@ -26909,6 +27419,8 @@ function createExecutorUpdates({
26909
27419
  shared
26910
27420
  }) {
26911
27421
  const shownToolCallIds = /* @__PURE__ */ new Set();
27422
+ const markChildSession = shared.childSessions.mark;
27423
+ const subagentSessions = options.subagentSessions === true ? createSubagentSessionTracker(shared.childSessions) : void 0;
26912
27424
  const emitCompactionState = (record2, state) => {
26913
27425
  const summary = record2.summary?.trim() ?? "";
26914
27426
  const truncated = summary.length > COMPACTION_SUMMARY_MAX_CHARS;
@@ -26994,6 +27506,30 @@ function createExecutorUpdates({
26994
27506
  return;
26995
27507
  }
26996
27508
  };
27509
+ const emitTaskEvents = (taskEvents) => {
27510
+ for (const taskEvent of taskEvents ?? []) {
27511
+ emit({
27512
+ type: taskEvent.type,
27513
+ ...stamp(),
27514
+ provider: options.provider,
27515
+ threadId: options.threadId,
27516
+ ...taskEvent.turnId !== void 0 ? { turnId: taskEvent.turnId } : {},
27517
+ payload: taskEvent.payload
27518
+ });
27519
+ }
27520
+ };
27521
+ const toolSubagents = hooks.toolSubagents === void 0 ? void 0 : createToolSubagentTracker(hooks.toolSubagents);
27522
+ const settleToolSubagents = (settlement, turnId) => emitTaskEvents(toolSubagents?.settle(settlement, turnId));
27523
+ const settleSessionEnded = () => {
27524
+ settleToolSubagents("session-ended");
27525
+ emitTaskEvents(subagentSessions?.settleSessionEnded());
27526
+ emitTaskEvents(hooks.onSessionEnded?.());
27527
+ };
27528
+ const observeContext = () => ({
27529
+ turnId: shared.activeTurnId,
27530
+ sessionId: shared.sessionId,
27531
+ markChildSession
27532
+ });
26997
27533
  const handleSessionUpdate = (params) => {
26998
27534
  const parsed = parseSessionUpdateEvent(params);
26999
27535
  if (parsed.modeId && shared.modeState) {
@@ -27020,43 +27556,39 @@ function createExecutorUpdates({
27020
27556
  rawTitle: params.update.title,
27021
27557
  nowMs
27022
27558
  });
27023
- const hookResult = hooks.onToolCallUpdated?.({
27559
+ const hasToolHooks = toolSubagents !== void 0 || hooks.onToolCallUpdated !== void 0;
27560
+ const call = !hasToolHooks ? void 0 : {
27024
27561
  toolCallId: nextToolCall.toolCallId,
27025
27562
  status: nextToolCall.status,
27026
27563
  title: nextToolCall.title,
27027
27564
  kind: nextToolCall.kind,
27028
27565
  rawInput: nextToolCall.data["rawInput"],
27029
27566
  rawOutput: nextToolCall.data["rawOutput"],
27567
+ contentText: nextToolCall.contentText,
27030
27568
  meta: params.update._meta,
27031
27569
  turnId: shared.activeTurnId
27032
- });
27033
- const taskEvents = hookResult === void 0 ? [] : "events" in hookResult ? hookResult.events : hookResult;
27570
+ };
27571
+ const hookResult = call === void 0 ? void 0 : toolSubagents?.onToolCallUpdated(call) ?? hooks.onToolCallUpdated?.(call);
27034
27572
  const handledByHook = hookResult !== void 0 && "events" in hookResult && hookResult.handled === true;
27035
- for (const taskEvent of taskEvents) {
27036
- emit({
27037
- type: taskEvent.type,
27038
- ...stamp(),
27039
- provider: options.provider,
27040
- threadId: options.threadId,
27041
- ...taskEvent.turnId !== void 0 ? { turnId: taskEvent.turnId } : {},
27042
- payload: taskEvent.payload
27043
- });
27044
- }
27573
+ emitTaskEvents(
27574
+ hookResult === void 0 ? [] : "events" in hookResult ? hookResult.events : hookResult
27575
+ );
27045
27576
  if (nextToolCall.status === "completed" && hookResult && "artifacts" in hookResult)
27046
27577
  emitArtifacts(hookResult.artifacts ?? [], nextToolCall.toolCallId);
27047
27578
  if (handledByHook || !decision.emit) {
27048
27579
  continue;
27049
27580
  }
27581
+ const toolTurnId = (hookResult !== void 0 && "events" in hookResult ? hookResult.turnId : void 0) ?? shared.activeTurnId;
27050
27582
  if (!shownToolCallIds.has(nextToolCall.toolCallId)) {
27051
27583
  rememberShownToolCall(nextToolCall.toolCallId);
27052
- closeActiveAssistantSegment(shared.activeTurnId);
27584
+ if (toolTurnId === shared.activeTurnId) closeActiveAssistantSegment(shared.activeTurnId);
27053
27585
  }
27054
27586
  emit(
27055
27587
  makeAcpToolCallEvent({
27056
27588
  stamp: stamp(),
27057
27589
  provider: options.provider,
27058
27590
  threadId: options.threadId,
27059
- turnId: shared.activeTurnId,
27591
+ turnId: toolTurnId,
27060
27592
  toolCall: nextToolCall,
27061
27593
  rawPayload: event.rawPayload
27062
27594
  })
@@ -27133,7 +27665,36 @@ function createExecutorUpdates({
27133
27665
  }
27134
27666
  }
27135
27667
  };
27136
- return { emitCompactionState, handleSessionUpdate };
27668
+ const receiveSessionUpdate = (params) => {
27669
+ const foreign = !shared.ownSessionIds.has(params.sessionId) && (shared.childSessions.has(params.sessionId) || shared.sessionOpensInFlight === 0 && shared.ownSessionIds.size > 0);
27670
+ emitTaskEvents(
27671
+ subagentSessions?.observe(params, {
27672
+ turnId: shared.activeTurnId,
27673
+ foreign,
27674
+ ownSessionIds: shared.ownSessionIds,
27675
+ silent: shared.sessionLoadInFlight
27676
+ })
27677
+ );
27678
+ if (!shared.sessionLoadInFlight) {
27679
+ if (hooks.onSessionUpdate) {
27680
+ emitTaskEvents(hooks.onSessionUpdate(params, { ...observeContext(), foreign }));
27681
+ }
27682
+ }
27683
+ if (!foreign) handleSessionUpdate(params);
27684
+ };
27685
+ const receiveExtensionNotification = (method, params) => {
27686
+ if (shared.sessionLoadInFlight || !hooks.onExtensionNotification) return;
27687
+ const result = hooks.onExtensionNotification(method, params, observeContext());
27688
+ if (Array.isArray(result)) emitTaskEvents(result);
27689
+ };
27690
+ return {
27691
+ emitCompactionState,
27692
+ receiveSessionUpdate,
27693
+ receiveExtensionNotification,
27694
+ settleToolSubagents,
27695
+ settleSessionEnded,
27696
+ toolSubagentsMayRun: () => toolSubagents?.mayStillRun() ?? false
27697
+ };
27137
27698
  }
27138
27699
 
27139
27700
  // ../../packages/ycodium-acp-executor/src/openCommands.ts
@@ -27282,7 +27843,10 @@ function createAcpExecutor(options) {
27282
27843
  abortLocalPrompt: void 0,
27283
27844
  connectionClosed: false,
27284
27845
  sessionLoadInFlight: false,
27285
- droppedReplayUpdates: 0
27846
+ droppedReplayUpdates: 0,
27847
+ ownSessionIds: /* @__PURE__ */ new Set(),
27848
+ childSessions: createChildSessionRegistry(),
27849
+ sessionOpensInFlight: 0
27286
27850
  };
27287
27851
  const interruptedTurnIds = /* @__PURE__ */ new Set();
27288
27852
  const approvals = createApprovalGate({
@@ -27312,7 +27876,14 @@ function createAcpExecutor(options) {
27312
27876
  promptMutex,
27313
27877
  shared
27314
27878
  });
27315
- const { emitCompactionState, handleSessionUpdate } = createExecutorUpdates({
27879
+ const {
27880
+ emitCompactionState,
27881
+ receiveSessionUpdate,
27882
+ receiveExtensionNotification,
27883
+ settleToolSubagents,
27884
+ settleSessionEnded,
27885
+ toolSubagentsMayRun
27886
+ } = createExecutorUpdates({
27316
27887
  emit,
27317
27888
  stamp,
27318
27889
  options,
@@ -27375,11 +27946,11 @@ function createAcpExecutor(options) {
27375
27946
  if (notification.method === "session/update") {
27376
27947
  const params = notification.params;
27377
27948
  const transformed = hooks.transformSessionUpdate ? hooks.transformSessionUpdate(params) : params;
27378
- if (transformed !== void 0) handleSessionUpdate(transformed);
27949
+ if (transformed !== void 0) receiveSessionUpdate(transformed);
27379
27950
  return;
27380
27951
  }
27381
27952
  if (settleFromNotification(notification.method, notification.params)) return;
27382
- void hooks.onExtensionNotification?.(notification.method, notification.params);
27953
+ receiveExtensionNotification(notification.method, notification.params);
27383
27954
  },
27384
27955
  onRequest: async (method, params) => {
27385
27956
  if (method === CLIENT_METHODS.session_request_permission) {
@@ -27435,6 +28006,7 @@ function createAcpExecutor(options) {
27435
28006
  });
27436
28007
  const { sendPromptRpc, prompt } = createExecutorPrompt({
27437
28008
  promptResults,
28009
+ settleToolSubagents,
27438
28010
  isLive,
27439
28011
  closeActiveAssistantSegment,
27440
28012
  emit,
@@ -27459,6 +28031,8 @@ function createAcpExecutor(options) {
27459
28031
  dispose: disposeLifecycle
27460
28032
  } = createExecutorLifecycle({
27461
28033
  promptResults,
28034
+ settleToolSubagents,
28035
+ settleSessionEnded,
27462
28036
  failIfDisposed,
27463
28037
  requireStarted,
27464
28038
  interruptedTurnIds,
@@ -27507,6 +28081,7 @@ function createAcpExecutor(options) {
27507
28081
  setNativeMode,
27508
28082
  compact,
27509
28083
  openToolCalls: () => readOpenToolCalls(toolCalls, shared.activeTurnId),
28084
+ toolSubagentsMayRun,
27510
28085
  dispose: (exitCode) => {
27511
28086
  disposeOnce ??= (async () => {
27512
28087
  try {
@@ -27849,11 +28424,11 @@ function parseKimiContextWindow(contents) {
27849
28424
  let usedTokens;
27850
28425
  let maxTokens;
27851
28426
  for (const line of contents.split("\n")) {
27852
- const trimmed = line.trim();
27853
- if (trimmed.length === 0) continue;
28427
+ const trimmed2 = line.trim();
28428
+ if (trimmed2.length === 0) continue;
27854
28429
  let record2;
27855
28430
  try {
27856
- record2 = JSON.parse(trimmed);
28431
+ record2 = JSON.parse(trimmed2);
27857
28432
  } catch {
27858
28433
  continue;
27859
28434
  }
@@ -28399,13 +28974,13 @@ function kimiProbeModels(stdout) {
28399
28974
  if (!isRecord9(models)) return void 0;
28400
28975
  const out = [];
28401
28976
  for (const [slug, entry] of Object.entries(models)) {
28402
- const trimmed = slug.trim();
28403
- if (trimmed.length === 0 || !isRecord9(entry)) continue;
28977
+ const trimmed2 = slug.trim();
28978
+ if (trimmed2.length === 0 || !isRecord9(entry)) continue;
28404
28979
  const name = asString4(entry["displayName"]);
28405
28980
  const contextWindow = positiveInt2(entry["maxContextSize"]);
28406
28981
  const effort = kimiEffortDescriptor(entry);
28407
28982
  out.push({
28408
- slug: trimmed,
28983
+ slug: trimmed2,
28409
28984
  ...name === void 0 ? {} : { name },
28410
28985
  ...contextWindow === void 0 ? {} : { contextWindow },
28411
28986
  ...effort === void 0 ? {} : { capabilities: { optionDescriptors: [effort] } }
@@ -28549,6 +29124,134 @@ function acpSessionModeToApply(requestedNativeModeId, startedModeId) {
28549
29124
  return requestedNativeModeId !== void 0 && requestedNativeModeId !== startedModeId ? requestedNativeModeId : void 0;
28550
29125
  }
28551
29126
 
29127
+ // src/subagents.ts
29128
+ var SUBAGENT_TITLE = "Kimi subagent";
29129
+ var SWARM_TITLE = "Kimi agent swarm";
29130
+ var FOREGROUND_ENDED_DESCRIPTION = "Turn ended before Kimi reported this agent's result.";
29131
+ var BACKGROUND_ENDED_DESCRIPTION = "Running in the background. Kimi does not report when it finishes.";
29132
+ var SINGLE_TITLE = /^Launching (background )?(\S+) agent: ([\s\S]*)$/u;
29133
+ var SWARM_TITLE_PREFIX = "Launching agent swarm: ";
29134
+ var isRecord10 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
29135
+ var trimmed = (value) => typeof value === "string" && value.trim() !== "" ? value.trim() : void 0;
29136
+ function classify(input2) {
29137
+ if (input2.kind !== void 0 && input2.kind !== "other") return void 0;
29138
+ const title = input2.title;
29139
+ if (title === void 0) return void 0;
29140
+ if (title === "AgentSwarm" || title.startsWith(SWARM_TITLE_PREFIX)) return "swarm";
29141
+ return title === "Agent" || SINGLE_TITLE.test(title) ? "subagent" : void 0;
29142
+ }
29143
+ function requestedModel(value) {
29144
+ const model = trimmed(value);
29145
+ return model === "primary" ? void 0 : model;
29146
+ }
29147
+ function describe3(kind, input2, previous) {
29148
+ const raw = isRecord10(input2.rawInput) ? input2.rawInput : {};
29149
+ if (kind === "swarm") {
29150
+ const fromTitle = input2.title?.startsWith(SWARM_TITLE_PREFIX) ? trimmed(input2.title.slice(SWARM_TITLE_PREFIX.length)) : void 0;
29151
+ return {
29152
+ title: SWARM_TITLE,
29153
+ description: trimmed(raw["description"]) ?? fromTitle ?? previous?.description ?? SWARM_TITLE
29154
+ };
29155
+ }
29156
+ const match = input2.title === void 0 ? null : SINGLE_TITLE.exec(input2.title);
29157
+ const title = trimmed(raw["description"]) ?? trimmed(match?.[3]) ?? previous?.title;
29158
+ const role = match?.[2] ?? trimmed(raw["subagent_type"]) ?? previous?.role;
29159
+ const model = requestedModel(raw["model"]) ?? previous?.model;
29160
+ return {
29161
+ title: title ?? SUBAGENT_TITLE,
29162
+ description: title ?? SUBAGENT_TITLE,
29163
+ ...role === void 0 ? {} : { role },
29164
+ ...model === void 0 ? {} : { model }
29165
+ };
29166
+ }
29167
+ function splitResult(text) {
29168
+ const lines = text.split(/\r?\n/u);
29169
+ const header = /* @__PURE__ */ new Map();
29170
+ let index = 0;
29171
+ for (; index < lines.length; index += 1) {
29172
+ const match = /^([a-z_]+): ?(.*)$/u.exec(lines[index] ?? "");
29173
+ if (match === null) break;
29174
+ header.set(match[1] ?? "", match[2] ?? "");
29175
+ }
29176
+ return { header, body: lines.slice(index).join("\n").trim() };
29177
+ }
29178
+ function stripTrailingHints(body) {
29179
+ const cuts = ["\nresume_hint: ", "\nnext_step: "].map((marker) => body.lastIndexOf(marker)).filter((position) => position >= 0);
29180
+ return (cuts.length === 0 ? body : body.slice(0, Math.min(...cuts))).trim();
29181
+ }
29182
+ function parseAgentResult(text, isError) {
29183
+ if (text === void 0)
29184
+ return {
29185
+ kind: isError ? "failed" : "completed",
29186
+ summary: void 0,
29187
+ role: void 0,
29188
+ agentId: void 0
29189
+ };
29190
+ const { header, body } = splitResult(text);
29191
+ const role = trimmed(header.get("actual_subagent_type"));
29192
+ const agentId = trimmed(header.get("agent_id"));
29193
+ const status = header.get("status");
29194
+ if (status === "running" && header.has("task_id")) {
29195
+ return { kind: "background", summary: void 0, role, agentId };
29196
+ }
29197
+ if (isError || status === "failed") {
29198
+ return {
29199
+ kind: header.get("stop_reason") === "cancelled" ? "cancelled" : "failed",
29200
+ summary: trimmed(stripTrailingHints(body)) ?? trimmed(text),
29201
+ role,
29202
+ agentId
29203
+ };
29204
+ }
29205
+ const marker = "[summary]";
29206
+ const content = body.startsWith(marker) ? body.slice(marker.length) : body;
29207
+ const answer = trimmed(stripTrailingHints(content));
29208
+ const notice = trimmed(header.get("notice"));
29209
+ const summary = [notice === void 0 ? void 0 : `notice: ${notice}`, answer].filter((part) => part !== void 0).join("\n\n");
29210
+ return { kind: "completed", summary: trimmed(summary), role, agentId };
29211
+ }
29212
+ function parseSwarmResult(text, isError) {
29213
+ const inner = text === void 0 ? void 0 : /<summary>([\s\S]*?)<\/summary>/u.exec(text)?.[1];
29214
+ return {
29215
+ kind: isError ? "failed" : "completed",
29216
+ summary: trimmed(inner) ?? trimmed(text),
29217
+ role: void 0,
29218
+ agentId: void 0
29219
+ };
29220
+ }
29221
+ function resolveOutcome(call, kind) {
29222
+ if (call.status !== "completed" && call.status !== "failed") return void 0;
29223
+ const text = typeof call.rawOutput === "string" ? trimmed(call.rawOutput) : void 0;
29224
+ const isError = call.status === "failed";
29225
+ if (text === void 0 && !isError) return void 0;
29226
+ const outcome = kind === "swarm" ? parseSwarmResult(text, isError) : parseAgentResult(text, isError);
29227
+ const ids = {
29228
+ ...outcome.role === void 0 ? {} : { role: outcome.role },
29229
+ ...outcome.agentId === void 0 ? {} : { agentRef: outcome.agentId }
29230
+ };
29231
+ if (outcome.kind === "background") return { type: "background", ...ids };
29232
+ return {
29233
+ type: "final",
29234
+ status: outcome.kind,
29235
+ keepToolRow: true,
29236
+ ...ids,
29237
+ ...outcome.kind === "cancelled" || outcome.summary === void 0 ? {} : { summary: boundTailText(outcome.summary) }
29238
+ };
29239
+ }
29240
+ var asKind = (kind) => kind === "swarm" ? "swarm" : "subagent";
29241
+ var kimiToolSubagents = {
29242
+ recognize: classify,
29243
+ // 首帧没有入参:等带参数的那一帧再判断是新 agent 还是续接。
29244
+ ready: (call) => call.rawInput !== void 0,
29245
+ facts: (call, kind, previous) => describe3(asKind(kind), call, previous),
29246
+ announce: "started",
29247
+ resolve: (call, kind) => resolveOutcome(call, asKind(kind)),
29248
+ resumes: (call, kind) => {
29249
+ if (asKind(kind) !== "subagent" || !isRecord10(call.rawInput)) return void 0;
29250
+ return trimmed(call.rawInput["resume"]);
29251
+ },
29252
+ endNote: ({ background }) => background ? { description: BACKGROUND_ENDED_DESCRIPTION, timelineBypass: true } : { description: FOREGROUND_ENDED_DESCRIPTION }
29253
+ };
29254
+
28552
29255
  // src/kimiInstance.ts
28553
29256
  var RESUME_SCHEMA_VERSION = 1;
28554
29257
  var CLIENT_INFO = { name: "ycodium-agent-kimi", version: "0.1.0" };
@@ -28631,6 +29334,7 @@ function kimiHooks(recipe) {
28631
29334
  const homePath = resolveHomePath(recipe);
28632
29335
  return {
28633
29336
  permissionUserInput: kimiPermissionUserInput,
29337
+ toolSubagents: kimiToolSubagents,
28634
29338
  readContextWindow: ({ sessionId }) => readKimiContextWindow({ homePath, acpSessionId: sessionId })
28635
29339
  };
28636
29340
  }
@@ -28798,9 +29502,7 @@ var createKimiInstance = (config2, host) => {
28798
29502
  ...resume === void 0 ? {} : { resumeSessionId: resume.sessionId }
28799
29503
  });
28800
29504
  const forwarding = (async () => {
28801
- for await (const event of executor.events) {
28802
- events.push(event);
28803
- }
29505
+ for await (const event of executor.events) events.push(event);
28804
29506
  })();
28805
29507
  try {
28806
29508
  throwIfStartAborted(signal);
@@ -28970,7 +29672,7 @@ var executors = [
28970
29672
  ];
28971
29673
  var index_default = definePlugin({
28972
29674
  id: "agent-kimi",
28973
- apiVersion: 48,
29675
+ apiVersion: 49,
28974
29676
  executors,
28975
29677
  activate(ctx) {
28976
29678
  ctx.log.info("Kimi Code executor ready");
package/locales/en.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "form": {
3
3
  "binaryPath": {
4
4
  "title": "Program path",
5
- "description": "Path to the Kimi Code program this provider runs.",
5
+ "description": "Must be an absolute path.",
6
6
  "placeholder": "kimi"
7
7
  },
8
8
  "homePath": {
@@ -2,7 +2,7 @@
2
2
  "form": {
3
3
  "binaryPath": {
4
4
  "title": "程序路径",
5
- "description": "此 provider 运行的 Kimi Code 程序的路径。",
5
+ "description": "必须是绝对路径。",
6
6
  "placeholder": "kimi"
7
7
  },
8
8
  "homePath": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ycodium-ai/agent-kimi",
3
- "version": "0.2.3598",
3
+ "version": "0.2.3614",
4
4
  "description": "Kimi Code executor: runs `kimi acp` over Agent Client Protocol stdio.",
5
5
  "keywords": [
6
6
  "ycodium",
@@ -21,9 +21,9 @@
21
21
  "@types/node": "24.12.4",
22
22
  "esbuild": "0.28.1",
23
23
  "vite-plus": "0.2.2",
24
- "@ycodium-ai/plugin-api": "0.2.3598",
25
- "ycodium-executor-testing": "0.0.0",
26
- "ycodium-acp-executor": "0.2.3598"
24
+ "ycodium-acp-executor": "0.2.3614",
25
+ "@ycodium-ai/plugin-api": "0.2.3614",
26
+ "ycodium-executor-testing": "0.0.0"
27
27
  },
28
28
  "ycodium": {
29
29
  "plugin": "./dist/index.js",