@ycodium-ai/agent-kimi 0.2.3122 → 0.2.3158

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +269 -104
  2. package/package.json +4 -4
package/dist/index.js CHANGED
@@ -24945,7 +24945,10 @@ function createAcpConnection(options) {
24945
24945
  }
24946
24946
  pending.delete(key);
24947
24947
  try {
24948
- options.onResponse?.(waiter.method);
24948
+ options.onResponse?.(
24949
+ waiter,
24950
+ Object.hasOwn(message, "error") ? { error: message["error"] } : { result: message["result"] }
24951
+ );
24949
24952
  } catch {
24950
24953
  }
24951
24954
  if (Object.hasOwn(message, "error")) {
@@ -25078,12 +25081,17 @@ function createAcpConnection(options) {
25078
25081
  options.readable.on("end", onEnd);
25079
25082
  options.readable.on("error", onError);
25080
25083
  options.writable.once("error", onWritableError);
25081
- const begin = (method, params) => {
25084
+ const begin = (method, params, promptId) => {
25082
25085
  failIfClosed();
25083
25086
  const id = nextRequestId;
25084
25087
  nextRequestId += 1;
25085
25088
  const { promise: promise2, resolve: resolve3, reject } = Promise.withResolvers();
25086
- pending.set(String(id), { method, resolve: resolve3, reject });
25089
+ pending.set(String(id), {
25090
+ method,
25091
+ ...promptId === void 0 ? {} : { promptId },
25092
+ resolve: resolve3,
25093
+ reject
25094
+ });
25087
25095
  const message = params === void 0 ? { jsonrpc: "2.0", id, method } : { jsonrpc: "2.0", id, method, params };
25088
25096
  const written = Promise.race([writeMessage(message), promise2]).then(
25089
25097
  () => void 0,
@@ -25153,12 +25161,14 @@ function createExecutorLifecycle({
25153
25161
  notifyPromptSettled,
25154
25162
  eventStream,
25155
25163
  onInputDead,
25156
- shared
25164
+ shared,
25165
+ promptResults
25157
25166
  }) {
25158
25167
  const cancel = async (turnId) => {
25159
25168
  failIfDisposed();
25160
25169
  const started = requireStarted();
25161
25170
  const targetTurnId = turnId ?? shared.activeTurnId;
25171
+ promptResults.stop(targetTurnId);
25162
25172
  if (targetTurnId !== void 0) {
25163
25173
  interruptedTurnIds.add(targetTurnId);
25164
25174
  }
@@ -25169,6 +25179,9 @@ function createExecutorLifecycle({
25169
25179
  if (shared.sessionId) {
25170
25180
  await connection.notify("session/cancel", { sessionId: shared.sessionId });
25171
25181
  }
25182
+ await promptResults.wait(targetTurnId);
25183
+ if (targetTurnId !== void 0 && shared.activeTurnId !== targetTurnId && !interruptedTurnIds.has(targetTurnId))
25184
+ return;
25172
25185
  const settled = await waitForPromptIdle(cancelGraceMs);
25173
25186
  if (!settled) {
25174
25187
  throw new AcpExecutorCancelTimeoutError();
@@ -25189,6 +25202,7 @@ function createExecutorLifecycle({
25189
25202
  payload: { state: "cancelled", stopReason: "cancelled" }
25190
25203
  });
25191
25204
  interruptedTurnIds.delete(targetTurnId);
25205
+ promptResults.forget(targetTurnId);
25192
25206
  }
25193
25207
  }
25194
25208
  }
@@ -25263,33 +25277,39 @@ function createExecutorLifecycle({
25263
25277
  if (!shared.disposed) emitSessionState("ready");
25264
25278
  }
25265
25279
  };
25266
- const dispose = async () => {
25267
- if (shared.disposed) {
25268
- return;
25269
- }
25280
+ let disposing;
25281
+ const dispose = () => {
25282
+ if (disposing) return disposing;
25283
+ shared.disposing = true;
25284
+ promptResults.stop();
25270
25285
  settleAllApprovals("cancel");
25271
25286
  settleAllUserInputs();
25272
25287
  abortPendingSettlement("executor disposed");
25273
25288
  notifyPromptSettled();
25274
25289
  shared.abortLocalPrompt?.();
25275
- emit({
25276
- type: "session.exited",
25277
- ...stamp(),
25278
- provider: options.provider,
25279
- threadId: options.threadId,
25280
- payload: { reason: "disposed" }
25281
- });
25282
- shared.disposed = true;
25283
- shared.generation += 1;
25284
- shared.status = "disposed";
25285
- eventStream.close();
25286
- options.readable.off("end", onInputDead);
25287
- options.readable.off("error", onInputDead);
25288
- options.readable.pause();
25289
- if (!shared.connectionClosed) {
25290
- await connection.awaitInboundIdle().catch(() => void 0);
25291
- await connection.close().catch(() => void 0);
25292
- }
25290
+ disposing = (async () => {
25291
+ promptResults.finishUnreceived();
25292
+ await promptResults.wait();
25293
+ emit({
25294
+ type: "session.exited",
25295
+ ...stamp(),
25296
+ provider: options.provider,
25297
+ threadId: options.threadId,
25298
+ payload: { reason: "disposed" }
25299
+ });
25300
+ shared.disposed = true;
25301
+ shared.generation += 1;
25302
+ shared.status = "disposed";
25303
+ eventStream.close();
25304
+ options.readable.off("end", onInputDead);
25305
+ options.readable.off("error", onInputDead);
25306
+ options.readable.pause();
25307
+ if (!shared.connectionClosed) {
25308
+ await connection.awaitInboundIdle().catch(() => void 0);
25309
+ await connection.close().catch(() => void 0);
25310
+ }
25311
+ })();
25312
+ return disposing;
25293
25313
  };
25294
25314
  return { cancel, compact, dispose };
25295
25315
  }
@@ -25393,12 +25413,14 @@ function createExecutorPrompt({
25393
25413
  promptMutex,
25394
25414
  applyRequestedModel,
25395
25415
  notifyPromptSettled,
25396
- shared
25416
+ shared,
25417
+ promptResults
25397
25418
  }) {
25398
25419
  const settleTurn = (input2) => {
25399
- if (!isLive(input2.expectedGeneration)) {
25420
+ if (!isLive(input2.expectedGeneration) || input2.record.finished) {
25400
25421
  return false;
25401
25422
  }
25423
+ promptResults.complete(input2.record);
25402
25424
  if (!acpPromptSettlementBelongsToContext({
25403
25425
  liveAcpSessionId: shared.sessionId ?? "",
25404
25426
  expectedAcpSessionId: input2.expectedSessionId,
@@ -25415,6 +25437,7 @@ function createExecutorPrompt({
25415
25437
  if (shared.status !== "disposed") {
25416
25438
  shared.status = "ready";
25417
25439
  }
25440
+ const interrupted = promptResults.hasInterrupted(input2.turnId);
25418
25441
  emit({
25419
25442
  type: "turn.completed",
25420
25443
  ...stamp(),
@@ -25422,30 +25445,44 @@ function createExecutorPrompt({
25422
25445
  threadId: options.threadId,
25423
25446
  turnId: input2.turnId,
25424
25447
  payload: {
25425
- state: input2.state,
25426
- stopReason: input2.stopReason,
25448
+ state: interrupted ? "cancelled" : input2.state,
25449
+ stopReason: interrupted ? "cancelled" : input2.stopReason,
25427
25450
  ...input2.errorMessage === void 0 ? {} : { errorMessage: input2.errorMessage },
25428
25451
  ...input2.state === "failed" ? failureCategoryField(input2.failureCategory) : {}
25429
25452
  }
25430
25453
  });
25431
25454
  interruptedTurnIds.delete(input2.turnId);
25455
+ promptResults.forget(input2.turnId);
25432
25456
  return true;
25433
25457
  };
25434
- const sendPromptRpc = async (sessionId, promptParts, onWritten) => {
25458
+ const sendPromptRpc = async (sessionId, promptParts, onWritten, record2) => {
25435
25459
  const settlementHooks = hooks.promptSettlement;
25436
25460
  const promptKey = settlementHooks === void 0 ? void 0 : randomId();
25437
- const pending = connection.begin("session/prompt", {
25438
- sessionId,
25439
- prompt: promptParts,
25440
- ...settlementHooks !== void 0 && promptKey !== void 0 ? settlementHooks.requestFields(promptKey) : {}
25441
- });
25461
+ const pending = connection.begin(
25462
+ "session/prompt",
25463
+ {
25464
+ sessionId,
25465
+ prompt: promptParts,
25466
+ ...settlementHooks !== void 0 && promptKey !== void 0 ? settlementHooks.requestFields(promptKey) : {}
25467
+ },
25468
+ record2?.promptId
25469
+ );
25442
25470
  void pending.written.then(onWritten, () => void 0);
25443
25471
  const promptRpc = pending.response;
25444
25472
  if (settlementHooks === void 0 || promptKey === void 0) {
25445
25473
  return await promptRpc;
25446
25474
  } else {
25447
25475
  const { promise: early, resolve: settleEarly } = Promise.withResolvers();
25448
- shared.pendingSettlement = { promptKey, sessionId, settle: settleEarly };
25476
+ shared.pendingSettlement = {
25477
+ promptKey,
25478
+ sessionId,
25479
+ settle: settleEarly,
25480
+ ...record2 === void 0 ? {} : {
25481
+ onResponse: (response) => promptResults.receive(record2.promptId, response),
25482
+ onError: () => promptResults.reject(record2.promptId),
25483
+ hasResult: () => record2.response !== void 0
25484
+ }
25485
+ };
25449
25486
  try {
25450
25487
  const outcome = await Promise.race([
25451
25488
  promptRpc.then((value) => {
@@ -25466,9 +25503,11 @@ function createExecutorPrompt({
25466
25503
  const prompt = async (input2) => {
25467
25504
  failIfDisposed();
25468
25505
  const started = requireStarted();
25506
+ let record2;
25469
25507
  const settle2 = (rest) => settleTurn({
25470
25508
  expectedGeneration: started.generation,
25471
25509
  expectedSessionId: started.sessionId,
25510
+ record: record2,
25472
25511
  ...rest
25473
25512
  });
25474
25513
  let turnId;
@@ -25481,6 +25520,17 @@ function createExecutorPrompt({
25481
25520
  randomId
25482
25521
  );
25483
25522
  shared.promptsInFlight += 1;
25523
+ record2 = promptResults.add(
25524
+ {
25525
+ promptId: randomId(),
25526
+ sessionId: started.sessionId,
25527
+ generation: started.generation,
25528
+ turnId
25529
+ },
25530
+ () => {
25531
+ finishPrompt();
25532
+ }
25533
+ );
25484
25534
  shared.activeTurnId = turnId;
25485
25535
  shared.status = "running";
25486
25536
  if (shared.promptsInFlight === 1) {
@@ -25497,6 +25547,42 @@ function createExecutorPrompt({
25497
25547
  threadMutex.release();
25498
25548
  }
25499
25549
  const promptParts = input2.prompt ?? (input2.text !== void 0 ? [{ type: "text", text: input2.text }] : [{ type: "text", text: "" }]);
25550
+ let contextWindow;
25551
+ const finishPrompt = () => {
25552
+ if (!isLive(started.generation) || record2.finished) throw new AcpExecutorDisposedError();
25553
+ const response2 = record2.response;
25554
+ if (record2.interrupted || response2 === void 0) {
25555
+ settle2({ turnId, stopReason: "cancelled", state: "cancelled" });
25556
+ return { threadId: options.threadId, turnId, stopReason: "cancelled" };
25557
+ }
25558
+ const stopReason = hooks.completedStopReason?.(response2) ?? response2.stopReason;
25559
+ if (contextWindow !== void 0) {
25560
+ emit({
25561
+ type: "thread.token-usage.updated",
25562
+ ...stamp(),
25563
+ provider: options.provider,
25564
+ threadId: options.threadId,
25565
+ turnId,
25566
+ payload: {
25567
+ usage: {
25568
+ usedTokens: contextWindow.usedTokens,
25569
+ ...contextWindow.maxTokens === void 0 ? {} : { maxTokens: contextWindow.maxTokens }
25570
+ }
25571
+ }
25572
+ });
25573
+ }
25574
+ settle2({
25575
+ turnId,
25576
+ stopReason,
25577
+ state: response2.stopReason === "cancelled" ? "cancelled" : "completed"
25578
+ });
25579
+ return {
25580
+ threadId: options.threadId,
25581
+ turnId,
25582
+ stopReason,
25583
+ ...response2.usage !== void 0 && response2.usage !== null ? { usage: response2.usage } : {}
25584
+ };
25585
+ };
25500
25586
  await promptMutex.acquire();
25501
25587
  let response;
25502
25588
  try {
@@ -25511,6 +25597,7 @@ function createExecutorPrompt({
25511
25597
  expectedGeneration: started.generation
25512
25598
  });
25513
25599
  }
25600
+ failIfDisposed();
25514
25601
  if (isStale()) {
25515
25602
  settle2({ turnId, stopReason: "cancelled", state: "cancelled" });
25516
25603
  return { threadId: options.threadId, turnId, stopReason: "cancelled" };
@@ -25518,10 +25605,15 @@ function createExecutorPrompt({
25518
25605
  closeActiveAssistantSegment(turnId);
25519
25606
  let written = false;
25520
25607
  try {
25521
- response = await sendPromptRpc(started.sessionId, promptParts, () => {
25522
- written = true;
25523
- input2.onDispatched?.(turnId);
25524
- });
25608
+ response = await sendPromptRpc(
25609
+ started.sessionId,
25610
+ promptParts,
25611
+ () => {
25612
+ written = true;
25613
+ input2.onDispatched?.(turnId);
25614
+ },
25615
+ record2
25616
+ );
25525
25617
  } catch (error62) {
25526
25618
  if (shared.disposed) {
25527
25619
  throw new AcpExecutorDisposedError();
@@ -25560,8 +25652,7 @@ function createExecutorPrompt({
25560
25652
  if (turnId === void 0) {
25561
25653
  throw new AcpExecutorError("internal", "ACP prompt settled without a turn id");
25562
25654
  }
25563
- const settledTurnId = turnId;
25564
- let contextWindow;
25655
+ if (record2.finished) throw new AcpExecutorDisposedError();
25565
25656
  if (hooks.readContextWindow !== void 0) {
25566
25657
  try {
25567
25658
  contextWindow = await hooks.readContextWindow({
@@ -25572,43 +25663,6 @@ function createExecutorPrompt({
25572
25663
  contextWindow = void 0;
25573
25664
  }
25574
25665
  }
25575
- const finishPrompt = () => {
25576
- if (interruptedTurnIds.has(settledTurnId)) {
25577
- settle2({ turnId: settledTurnId, stopReason: "cancelled", state: "cancelled" });
25578
- return {
25579
- threadId: options.threadId,
25580
- turnId: settledTurnId,
25581
- stopReason: "cancelled"
25582
- };
25583
- }
25584
- const stopReason = hooks.completedStopReason?.(response) ?? response.stopReason;
25585
- if (contextWindow !== void 0) {
25586
- emit({
25587
- type: "thread.token-usage.updated",
25588
- ...stamp(),
25589
- provider: options.provider,
25590
- threadId: options.threadId,
25591
- turnId: settledTurnId,
25592
- payload: {
25593
- usage: {
25594
- usedTokens: contextWindow.usedTokens,
25595
- ...contextWindow.maxTokens === void 0 ? {} : { maxTokens: contextWindow.maxTokens }
25596
- }
25597
- }
25598
- });
25599
- }
25600
- settle2({
25601
- turnId: settledTurnId,
25602
- stopReason,
25603
- state: response.stopReason === "cancelled" ? "cancelled" : "completed"
25604
- });
25605
- return {
25606
- threadId: options.threadId,
25607
- turnId: settledTurnId,
25608
- stopReason,
25609
- ...response.usage !== void 0 && response.usage !== null ? { usage: response.usage } : {}
25610
- };
25611
- };
25612
25666
  if (threadMutex.tryAcquire()) {
25613
25667
  try {
25614
25668
  return finishPrompt();
@@ -25626,6 +25680,91 @@ function createExecutorPrompt({
25626
25680
  return { sendPromptRpc, prompt };
25627
25681
  }
25628
25682
 
25683
+ // ../../packages/ycodium-acp-executor/src/executor/prompt-results.ts
25684
+ function createPromptResults({
25685
+ options,
25686
+ shared
25687
+ }) {
25688
+ const prompts = /* @__PURE__ */ new Map();
25689
+ const add = (identity, finish) => {
25690
+ const { promise: done, resolve: resolve3 } = Promise.withResolvers();
25691
+ const record2 = {
25692
+ ...identity,
25693
+ finish,
25694
+ done,
25695
+ resolve: resolve3,
25696
+ rejected: false,
25697
+ interrupted: false,
25698
+ finished: false
25699
+ };
25700
+ prompts.set(record2.promptId, record2);
25701
+ return record2;
25702
+ };
25703
+ const receive = (promptId, response) => {
25704
+ const record2 = prompts.get(promptId);
25705
+ if (!record2 || record2.finished || record2.rejected || record2.response !== void 0) return;
25706
+ if (shared.disposed || shared.generation !== record2.generation || shared.sessionId !== record2.sessionId)
25707
+ return;
25708
+ record2.response = response;
25709
+ };
25710
+ const reject = (promptId) => {
25711
+ const record2 = prompts.get(promptId);
25712
+ if (record2 && record2.response === void 0) record2.rejected = true;
25713
+ };
25714
+ const stop = (turnId) => {
25715
+ for (const record2 of prompts.values()) {
25716
+ if (!record2.finished && (turnId === void 0 || record2.turnId === turnId) && record2.response === void 0) {
25717
+ record2.interrupted = true;
25718
+ }
25719
+ }
25720
+ };
25721
+ const complete = (record2) => {
25722
+ record2.finished = true;
25723
+ record2.resolve();
25724
+ };
25725
+ const hasInterrupted = (turnId) => [...prompts.values()].some((record2) => record2.turnId === turnId && record2.interrupted);
25726
+ const forget = (turnId) => {
25727
+ for (const [id, record2] of prompts) {
25728
+ if (record2.turnId === turnId) {
25729
+ complete(record2);
25730
+ prompts.delete(id);
25731
+ }
25732
+ }
25733
+ };
25734
+ const wait = async (turnId) => {
25735
+ const claimed = [...prompts.values()].filter(
25736
+ (record2) => !record2.finished && record2.response !== void 0 && (turnId === void 0 || record2.turnId === turnId)
25737
+ );
25738
+ if (claimed.length === 0) return;
25739
+ let timer;
25740
+ try {
25741
+ const drained = await Promise.race([
25742
+ Promise.all(claimed.map((record2) => record2.done)).then(() => true),
25743
+ new Promise((resolve3) => {
25744
+ timer = setTimeout(() => resolve3(false), options.resultDrainTimeoutMs ?? 2e3);
25745
+ })
25746
+ ]);
25747
+ if (drained) return;
25748
+ options.log?.warn?.("ACP result settlement timed out", { threadId: options.threadId });
25749
+ for (const record2 of claimed) if (!record2.finished) record2.finish();
25750
+ } finally {
25751
+ clearTimeout(timer);
25752
+ }
25753
+ };
25754
+ const finishUnreceived = () => {
25755
+ const claimedTurns = new Set(
25756
+ [...prompts.values()].filter((record2) => !record2.finished && record2.response !== void 0).map((record2) => record2.turnId)
25757
+ );
25758
+ for (const record2 of prompts.values()) {
25759
+ if (!record2.finished && record2.response === void 0) {
25760
+ if (claimedTurns.has(record2.turnId)) record2.finish();
25761
+ else complete(record2);
25762
+ }
25763
+ }
25764
+ };
25765
+ return { add, receive, reject, stop, complete, hasInterrupted, forget, wait, finishUnreceived };
25766
+ }
25767
+
25629
25768
  // ../../packages/ycodium-acp-executor/src/user-input-pending.ts
25630
25769
  var userInputAnswerOptionId = (question, answers) => {
25631
25770
  const answer = answers[question.id];
@@ -25932,7 +26071,7 @@ function createExecutorRuntime({
25932
26071
  turnId: () => shared.activeTurnId
25933
26072
  });
25934
26073
  const failIfDisposed = () => {
25935
- if (shared.disposed) {
26074
+ if (shared.disposed || shared.disposing) {
25936
26075
  throw new AcpExecutorDisposedError();
25937
26076
  }
25938
26077
  };
@@ -26457,6 +26596,7 @@ function createExecutorSettlement({
26457
26596
  const abortPendingSettlement = (reason, failureCategory) => {
26458
26597
  const waiting = shared.pendingSettlement;
26459
26598
  if (waiting === void 0) return;
26599
+ if (waiting.hasResult?.()) return;
26460
26600
  shared.pendingSettlement = void 0;
26461
26601
  waiting.settle({
26462
26602
  error: withFailureCategory(
@@ -26478,6 +26618,7 @@ function createExecutorSettlement({
26478
26618
  shared.pendingSettlement = void 0;
26479
26619
  rememberSettledPromptKey(waiting.promptKey);
26480
26620
  if (signal.outcome === "error") {
26621
+ waiting.onError?.();
26481
26622
  waiting.settle({
26482
26623
  error: withFailureCategory(
26483
26624
  new AcpExecutorRequestError(
@@ -26488,8 +26629,10 @@ function createExecutorSettlement({
26488
26629
  )
26489
26630
  });
26490
26631
  } else {
26632
+ const response = { stopReason: signal.stopReason ?? "end_turn" };
26633
+ waiting.onResponse?.(response);
26491
26634
  waiting.settle({
26492
- response: { stopReason: signal.stopReason ?? "end_turn" }
26635
+ response
26493
26636
  });
26494
26637
  }
26495
26638
  return true;
@@ -26819,6 +26962,7 @@ function createAcpExecutor(options) {
26819
26962
  const shared = {
26820
26963
  generation: 0,
26821
26964
  disposed: false,
26965
+ disposing: false,
26822
26966
  status: "idle",
26823
26967
  sessionId: void 0,
26824
26968
  modelSelectionState: {
@@ -26906,6 +27050,7 @@ function createAcpExecutor(options) {
26906
27050
  shared
26907
27051
  });
26908
27052
  const { rememberSettledPromptKey, abortPendingSettlement, settleFromNotification } = createExecutorSettlement({ hooks, shared });
27053
+ const promptResults = createPromptResults({ options, shared });
26909
27054
  const connection = createAcpConnection({
26910
27055
  readable: options.readable,
26911
27056
  writable: options.writable,
@@ -26916,8 +27061,12 @@ function createAcpExecutor(options) {
26916
27061
  });
26917
27062
  },
26918
27063
  // 窗口在应答帧到达的那一刻同步收掉:应答之后的通知是真输出,不能再丢。
26919
- onResponse: (method) => {
27064
+ onResponse: ({ method, promptId }, outcome) => {
26920
27065
  if (method === "session/load") shared.sessionLoadInFlight = false;
27066
+ if (promptId !== void 0) {
27067
+ if ("result" in outcome) promptResults.receive(promptId, outcome.result);
27068
+ else promptResults.reject(promptId);
27069
+ }
26921
27070
  },
26922
27071
  onNotification: (notification) => {
26923
27072
  if (notification.method === "session/update") {
@@ -26982,6 +27131,7 @@ function createAcpExecutor(options) {
26982
27131
  shared
26983
27132
  });
26984
27133
  const { sendPromptRpc, prompt } = createExecutorPrompt({
27134
+ promptResults,
26985
27135
  isLive,
26986
27136
  closeActiveAssistantSegment,
26987
27137
  emit,
@@ -27001,6 +27151,7 @@ function createAcpExecutor(options) {
27001
27151
  shared
27002
27152
  });
27003
27153
  const { cancel, compact, dispose } = createExecutorLifecycle({
27154
+ promptResults,
27004
27155
  failIfDisposed,
27005
27156
  requireStarted,
27006
27157
  interruptedTurnIds,
@@ -27052,6 +27203,27 @@ function createAcpExecutor(options) {
27052
27203
  };
27053
27204
  }
27054
27205
 
27206
+ // ../../packages/ycodium-acp-executor/src/session-registry.ts
27207
+ function createAcpSessionRegistry(threads, stopBooked2) {
27208
+ const requireThread = (threadId) => {
27209
+ const booked = threads.get(threadId);
27210
+ if (!booked) throw new Error(`unknown thread ${threadId}`);
27211
+ if (booked.closing) throw new Error(`thread ${threadId} is closing`);
27212
+ return booked;
27213
+ };
27214
+ const stopSession = (threadId, booked = threads.get(threadId)) => {
27215
+ if (!booked) return Promise.resolve();
27216
+ booked.closing ??= Promise.resolve().then(() => stopBooked2(booked)).then(() => {
27217
+ if (threads.get(threadId) === booked) threads.delete(threadId);
27218
+ });
27219
+ return booked.closing;
27220
+ };
27221
+ const stopAll = () => Promise.all([...threads].map(([threadId, booked]) => stopSession(threadId, booked))).then(
27222
+ () => void 0
27223
+ );
27224
+ return { requireThread, stopSession, stopAll };
27225
+ }
27226
+
27055
27227
  // ../../packages/ycodium-acp-executor/src/prompt-dispatch.ts
27056
27228
  var dispatchAcpPrompt = (executor, input2) => {
27057
27229
  const { promise: dispatched, resolve: resolve3 } = Promise.withResolvers();
@@ -28078,12 +28250,9 @@ var createKimiInstance = (config2, host) => {
28078
28250
  const events = new EventBus();
28079
28251
  const threads = /* @__PURE__ */ new Map();
28080
28252
  let disposed = false;
28253
+ let disposeTask;
28081
28254
  const usageHome = kimiEffectiveHome(resolveHomePath(recipe), process.env[recipe.homeEnvVar]);
28082
- const requireThread = (threadId) => {
28083
- const booked = threads.get(threadId);
28084
- if (!booked) throw new Error(`unknown thread ${threadId}`);
28085
- return booked;
28086
- };
28255
+ const { requireThread, stopSession, stopAll } = createAcpSessionRegistry(threads, stopBooked);
28087
28256
  const instance = {
28088
28257
  probe: async () => {
28089
28258
  if (recipe.binaryPath.length === 0) {
@@ -28158,8 +28327,8 @@ var createKimiInstance = (config2, host) => {
28158
28327
  if (cwd.trim() === "") throw new Error("workspace path missing");
28159
28328
  const existing = threads.get(input2.threadId);
28160
28329
  if (existing !== void 0) {
28161
- await stopBooked(existing);
28162
- threads.delete(input2.threadId);
28330
+ await stopSession(input2.threadId, existing);
28331
+ if (disposed) throw new Error("instance disposed");
28163
28332
  throwIfStartAborted(signal);
28164
28333
  }
28165
28334
  const env = buildSpawnEnv(recipe, recipe.homePath);
@@ -28293,7 +28462,7 @@ var createKimiInstance = (config2, host) => {
28293
28462
  },
28294
28463
  interruptTurn: async (threadId, turnId) => {
28295
28464
  const booked = threads.get(threadId);
28296
- if (booked === void 0) return;
28465
+ if (booked === void 0 || booked.closing) return;
28297
28466
  host.log.info("interrupt turn", { threadId, turnId });
28298
28467
  await booked.executor.cancel(turnId);
28299
28468
  await yieldTick();
@@ -28334,12 +28503,7 @@ var createKimiInstance = (config2, host) => {
28334
28503
  updatedAt: nowIso()
28335
28504
  };
28336
28505
  },
28337
- stopSession: async (threadId) => {
28338
- const booked = threads.get(threadId);
28339
- if (booked === void 0) return;
28340
- threads.delete(threadId);
28341
- await stopBooked(booked);
28342
- },
28506
+ stopSession: (threadId) => stopSession(threadId),
28343
28507
  readThread: async (threadId) => {
28344
28508
  const booked = requireThread(threadId);
28345
28509
  return { threadId, turns: booked.turns };
@@ -28355,13 +28519,14 @@ var createKimiInstance = (config2, host) => {
28355
28519
  ...createKimiTokenReader({ homePath: usageHome })
28356
28520
  },
28357
28521
  continuationKey: kimiContinuationKey(recipe),
28358
- dispose: async () => {
28359
- if (disposed) return;
28522
+ dispose: () => {
28523
+ if (disposeTask) return disposeTask;
28360
28524
  disposed = true;
28361
- const booked = [...threads.values()];
28362
- threads.clear();
28363
- await Promise.all(booked.map((entry) => stopBooked(entry)));
28364
- events.close();
28525
+ disposeTask = (async () => {
28526
+ await stopAll();
28527
+ events.close();
28528
+ })();
28529
+ return disposeTask;
28365
28530
  }
28366
28531
  };
28367
28532
  return instance;
@@ -28379,7 +28544,7 @@ var executors = [
28379
28544
  ];
28380
28545
  var index_default = definePlugin({
28381
28546
  id: "agent-kimi",
28382
- apiVersion: 13,
28547
+ apiVersion: 15,
28383
28548
  executors,
28384
28549
  activate(ctx) {
28385
28550
  ctx.log.info("Kimi Code executor ready");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ycodium-ai/agent-kimi",
3
- "version": "0.2.3122",
3
+ "version": "0.2.3158",
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-acp-executor": "0.2.3122",
25
- "@ycodium-ai/plugin-api": "0.2.3122",
26
- "ycodium-executor-testing": "0.0.0"
24
+ "@ycodium-ai/plugin-api": "0.2.3158",
25
+ "ycodium-executor-testing": "0.0.0",
26
+ "ycodium-acp-executor": "0.2.3158"
27
27
  },
28
28
  "ycodium": {
29
29
  "plugin": "./dist/index.js",