@ycodium-ai/agent-kimi 0.2.3130 → 0.2.3168

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 +337 -107
  2. package/package.json +3 -3
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,41 @@ 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
+
27227
+ // ../../packages/ycodium-acp-executor/src/sessionProcess.ts
27228
+ var fileName = (path) => path.slice(path.lastIndexOf("/") + 1);
27229
+ var withoutExe = (name) => name.replace(/\.exe$/, "");
27230
+ function acpNativeSessionProcessMatcher(program, args) {
27231
+ return (argv, platform) => {
27232
+ const normalize = (value) => platform === "win32" ? withoutExe(value.replaceAll("\\", "/").toLowerCase()) : value;
27233
+ const expected = normalize(program);
27234
+ const actual = normalize(argv[0] ?? "");
27235
+ const sameProgram = expected.includes("/") ? actual === expected : fileName(actual) === fileName(expected);
27236
+ const rest = argv.slice(1);
27237
+ return sameProgram && rest.length === args.length && rest.every((arg, i) => arg === args[i]);
27238
+ };
27239
+ }
27240
+
27055
27241
  // ../../packages/ycodium-acp-executor/src/prompt-dispatch.ts
27056
27242
  var dispatchAcpPrompt = (executor, input2) => {
27057
27243
  const { promise: dispatched, resolve: resolve3 } = Promise.withResolvers();
@@ -27967,6 +28153,44 @@ async function probeKimi(input2) {
27967
28153
  }
27968
28154
  }
27969
28155
 
28156
+ // src/openCommands.ts
28157
+ var basename = (path) => path.split(/[\\/]/).at(-1)?.toLowerCase() ?? "";
28158
+ var gitBashPath = (path) => path.replace(/^([A-Za-z]):[\\/]?/, (_match, drive) => `/${drive.toLowerCase()}/`).replaceAll("\\", "/").replace(/\/+$/, "");
28159
+ function kimiCommandMatcher(input2) {
28160
+ return (argv, platform) => {
28161
+ if (platform !== "win32" || input2.cwd.includes("'")) return false;
28162
+ return argv.length === 3 && basename(argv[0] ?? "") === "bash.exe" && argv[1] === "-c" && argv[2] === `cd '${gitBashPath(input2.cwd)}' && ${input2.command}`;
28163
+ };
28164
+ }
28165
+ var commandOf = (call) => {
28166
+ if (call.kind !== "execute") return void 0;
28167
+ const rawInput = call.rawInput;
28168
+ if (typeof rawInput !== "object" || rawInput === null) return void 0;
28169
+ const input2 = rawInput;
28170
+ if (input2["run_in_background"] === true) return void 0;
28171
+ const command = input2["command"];
28172
+ return typeof command === "string" && command.trim() !== "" ? command : void 0;
28173
+ };
28174
+ function kimiOpenCommands(session) {
28175
+ const commands = session.openToolCalls.flatMap((call) => {
28176
+ const command = commandOf(call);
28177
+ if (command === void 0) return [];
28178
+ return [
28179
+ {
28180
+ toolCallId: call.toolCallId,
28181
+ startedAtMs: call.startedAtMs,
28182
+ matches: kimiCommandMatcher({ command, cwd: session.cwd })
28183
+ }
28184
+ ];
28185
+ });
28186
+ if (commands.length === 0) return void 0;
28187
+ return {
28188
+ rootPid: session.rootPid,
28189
+ sessionProcess: acpNativeSessionProcessMatcher(session.program, session.args),
28190
+ commands
28191
+ };
28192
+ }
28193
+
27970
28194
  // src/runtimeModeApproval.ts
27971
28195
  function acpRuntimeModeAutoApproves(runtimeMode, toolKind) {
27972
28196
  switch (runtimeMode) {
@@ -28078,12 +28302,9 @@ var createKimiInstance = (config2, host) => {
28078
28302
  const events = new EventBus();
28079
28303
  const threads = /* @__PURE__ */ new Map();
28080
28304
  let disposed = false;
28305
+ let disposeTask;
28081
28306
  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
- };
28307
+ const { requireThread, stopSession, stopAll } = createAcpSessionRegistry(threads, stopBooked);
28087
28308
  const instance = {
28088
28309
  probe: async () => {
28089
28310
  if (recipe.binaryPath.length === 0) {
@@ -28158,15 +28379,18 @@ var createKimiInstance = (config2, host) => {
28158
28379
  if (cwd.trim() === "") throw new Error("workspace path missing");
28159
28380
  const existing = threads.get(input2.threadId);
28160
28381
  if (existing !== void 0) {
28161
- await stopBooked(existing);
28162
- threads.delete(input2.threadId);
28382
+ await stopSession(input2.threadId, existing);
28383
+ if (disposed) throw new Error("instance disposed");
28163
28384
  throwIfStartAborted(signal);
28164
28385
  }
28165
28386
  const env = buildSpawnEnv(recipe, recipe.homePath);
28166
- const handle = await host.process.spawn({
28387
+ const spawned = {
28167
28388
  program: recipe.binaryPath,
28168
28389
  args: tokenizeLaunchArgs(recipe.launchArgs),
28169
- cwd,
28390
+ cwd
28391
+ };
28392
+ const handle = await host.process.spawn({
28393
+ ...spawned,
28170
28394
  owner: { bindingId: host.bindingId },
28171
28395
  ...Object.keys(env).length > 0 ? { env } : {}
28172
28396
  });
@@ -28252,7 +28476,8 @@ var createKimiInstance = (config2, host) => {
28252
28476
  turns: [],
28253
28477
  executor,
28254
28478
  handle,
28255
- forwarding
28479
+ forwarding,
28480
+ spawned
28256
28481
  });
28257
28482
  host.log.info("session started", {
28258
28483
  threadId: input2.threadId,
@@ -28293,11 +28518,20 @@ var createKimiInstance = (config2, host) => {
28293
28518
  },
28294
28519
  interruptTurn: async (threadId, turnId) => {
28295
28520
  const booked = threads.get(threadId);
28296
- if (booked === void 0) return;
28521
+ if (booked === void 0 || booked.closing) return;
28297
28522
  host.log.info("interrupt turn", { threadId, turnId });
28298
28523
  await booked.executor.cancel(turnId);
28299
28524
  await yieldTick();
28300
28525
  },
28526
+ openCommandProcesses: async (threadId) => {
28527
+ const booked = threads.get(threadId);
28528
+ if (booked === void 0) return void 0;
28529
+ return kimiOpenCommands({
28530
+ rootPid: booked.handle.pid,
28531
+ ...booked.spawned,
28532
+ openToolCalls: booked.executor.openToolCalls()
28533
+ });
28534
+ },
28301
28535
  respondToRequest: async (threadId, requestId, decision, reason) => {
28302
28536
  const booked = requireThread(threadId);
28303
28537
  await booked.executor.respondToRequest(
@@ -28334,12 +28568,7 @@ var createKimiInstance = (config2, host) => {
28334
28568
  updatedAt: nowIso()
28335
28569
  };
28336
28570
  },
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
- },
28571
+ stopSession: (threadId) => stopSession(threadId),
28343
28572
  readThread: async (threadId) => {
28344
28573
  const booked = requireThread(threadId);
28345
28574
  return { threadId, turns: booked.turns };
@@ -28355,13 +28584,14 @@ var createKimiInstance = (config2, host) => {
28355
28584
  ...createKimiTokenReader({ homePath: usageHome })
28356
28585
  },
28357
28586
  continuationKey: kimiContinuationKey(recipe),
28358
- dispose: async () => {
28359
- if (disposed) return;
28587
+ dispose: () => {
28588
+ if (disposeTask) return disposeTask;
28360
28589
  disposed = true;
28361
- const booked = [...threads.values()];
28362
- threads.clear();
28363
- await Promise.all(booked.map((entry) => stopBooked(entry)));
28364
- events.close();
28590
+ disposeTask = (async () => {
28591
+ await stopAll();
28592
+ events.close();
28593
+ })();
28594
+ return disposeTask;
28365
28595
  }
28366
28596
  };
28367
28597
  return instance;
@@ -28379,7 +28609,7 @@ var executors = [
28379
28609
  ];
28380
28610
  var index_default = definePlugin({
28381
28611
  id: "agent-kimi",
28382
- apiVersion: 14,
28612
+ apiVersion: 16,
28383
28613
  executors,
28384
28614
  activate(ctx) {
28385
28615
  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.3130",
3
+ "version": "0.2.3168",
4
4
  "description": "Kimi Code executor: runs `kimi acp` over Agent Client Protocol stdio.",
5
5
  "keywords": [
6
6
  "ycodium",
@@ -21,8 +21,8 @@
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.3130",
25
- "ycodium-acp-executor": "0.2.3130",
24
+ "@ycodium-ai/plugin-api": "0.2.3168",
25
+ "ycodium-acp-executor": "0.2.3168",
26
26
  "ycodium-executor-testing": "0.0.0"
27
27
  },
28
28
  "ycodium": {