@ycodium-ai/agent-acp 0.2.2126 → 0.2.2143

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 +295 -181
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -5098,7 +5098,7 @@ var recursive = /* @__PURE__ */ new WeakMap();
5098
5098
  var NONE = 0;
5099
5099
  var ASSUMED = 1;
5100
5100
  var PROVEN = 2;
5101
- function isRecursive(inst, stack, resolve) {
5101
+ function isRecursive(inst, stack, resolve2) {
5102
5102
  const cached2 = recursive.get(inst);
5103
5103
  if (cached2 !== void 0)
5104
5104
  return cached2 ? PROVEN : NONE;
@@ -5108,7 +5108,7 @@ function isRecursive(inst, stack, resolve) {
5108
5108
  let result = NONE;
5109
5109
  const check2 = (child) => {
5110
5110
  if (result !== PROVEN && child?._zod) {
5111
- const answer = isRecursive(child, stack, resolve);
5111
+ const answer = isRecursive(child, stack, resolve2);
5112
5112
  if (answer > result)
5113
5113
  result = answer;
5114
5114
  }
@@ -5119,7 +5119,7 @@ function isRecursive(inst, stack, resolve) {
5119
5119
  const desc = Object.getOwnPropertyDescriptor(sh, key);
5120
5120
  if (spread && !desc.enumerable)
5121
5121
  continue;
5122
- const child = desc.get ? ASSUMED : desc.value?._zod ? isRecursive(desc.value, stack, resolve) : NONE;
5122
+ const child = desc.get ? ASSUMED : desc.value?._zod ? isRecursive(desc.value, stack, resolve2) : NONE;
5123
5123
  if (child > answer)
5124
5124
  answer = child;
5125
5125
  }
@@ -5183,7 +5183,7 @@ function isRecursive(inst, stack, resolve) {
5183
5183
  break;
5184
5184
  // `$ZodLazy` caches its inner on the def, so a resolved edge is followed exactly
5185
5185
  case "lazy": {
5186
- const inner = def._cachedInner ?? (resolve ? inst._zod.innerType : void 0);
5186
+ const inner = def._cachedInner ?? (resolve2 ? inst._zod.innerType : void 0);
5187
5187
  merge2(inner ? isRecursive(inner, stack, false) : ASSUMED);
5188
5188
  break;
5189
5189
  }
@@ -21847,11 +21847,11 @@ var Connection = class {
21847
21847
  const id = this.nextRequestId++;
21848
21848
  let cancel = () => {
21849
21849
  };
21850
- const response = new Promise((resolve, reject) => {
21850
+ const response = new Promise((resolve2, reject) => {
21851
21851
  const pendingResponse = {
21852
21852
  resolve: (value) => {
21853
21853
  try {
21854
- resolve(mapResponse ? mapResponse(value) : value);
21854
+ resolve2(mapResponse ? mapResponse(value) : value);
21855
21855
  } catch (error62) {
21856
21856
  reject(error62);
21857
21857
  }
@@ -21908,8 +21908,8 @@ var Connection = class {
21908
21908
  this.stream = stream;
21909
21909
  this.staticHandlers = handlers;
21910
21910
  this.allowBatches = options?.allowBatches ?? true;
21911
- this.closedPromise = new Promise((resolve) => {
21912
- this.abortController.signal.addEventListener("abort", () => resolve());
21911
+ this.closedPromise = new Promise((resolve2) => {
21912
+ this.abortController.signal.addEventListener("abort", () => resolve2());
21913
21913
  });
21914
21914
  void this.receive();
21915
21915
  }
@@ -22803,8 +22803,8 @@ var AsyncQueue = class {
22803
22803
  if (this.failed) {
22804
22804
  return Promise.reject(this.failure);
22805
22805
  }
22806
- return new Promise((resolve, reject) => {
22807
- this.waiters.push({ resolve, reject });
22806
+ return new Promise((resolve2, reject) => {
22807
+ this.waiters.push({ resolve: resolve2, reject });
22808
22808
  });
22809
22809
  }
22810
22810
  };
@@ -23633,6 +23633,7 @@ function createAcpConnection(options) {
23633
23633
  options.readable.off("data", onData);
23634
23634
  options.readable.off("end", onEnd);
23635
23635
  options.readable.off("error", onError);
23636
+ options.writable.off("error", onWritableError);
23636
23637
  try {
23637
23638
  await byteWriter.close();
23638
23639
  } catch {
@@ -23771,8 +23772,17 @@ function createAcpConnection(options) {
23771
23772
  endInboundBatch();
23772
23773
  }
23773
23774
  };
23775
+ const failInbound = (error62) => {
23776
+ if (closedError !== void 0) return;
23777
+ options.onInboundFailure?.(error62);
23778
+ void shutdown(
23779
+ new AcpConnectionClosedError(
23780
+ `ACP inbound handling failed: ${error62 instanceof Error ? error62.message : String(error62)}`
23781
+ )
23782
+ );
23783
+ };
23774
23784
  const enqueueInbound = (work) => {
23775
- inboundTail = inboundTail.then(work, work);
23785
+ inboundTail = inboundTail.then(work).catch(failInbound);
23776
23786
  };
23777
23787
  const onData = (chunk) => {
23778
23788
  if (closedError !== void 0) {
@@ -23822,18 +23832,24 @@ function createAcpConnection(options) {
23822
23832
  );
23823
23833
  });
23824
23834
  };
23835
+ const onWritableError = (error62) => {
23836
+ void shutdown(
23837
+ error62 instanceof Error ? error62 : new AcpConnectionClosedError("ACP output stream error")
23838
+ );
23839
+ };
23825
23840
  options.readable.on("data", onData);
23826
23841
  options.readable.on("end", onEnd);
23827
23842
  options.readable.on("error", onError);
23843
+ options.writable.once("error", onWritableError);
23828
23844
  const request = (async (method, params) => {
23829
23845
  failIfClosed();
23830
23846
  const id = nextRequestId;
23831
23847
  nextRequestId += 1;
23832
- const { promise: promise2, resolve, reject } = Promise.withResolvers();
23833
- pending.set(String(id), { method, resolve, reject });
23848
+ const { promise: promise2, resolve: resolve2, reject } = Promise.withResolvers();
23849
+ pending.set(String(id), { method, resolve: resolve2, reject });
23834
23850
  const message = params === void 0 ? { jsonrpc: "2.0", id, method } : { jsonrpc: "2.0", id, method, params };
23835
23851
  try {
23836
- await writeMessage(message);
23852
+ await Promise.race([writeMessage(message), promise2]);
23837
23853
  } catch (error62) {
23838
23854
  if (pending.delete(String(id))) {
23839
23855
  reject(error62);
@@ -23851,8 +23867,8 @@ function createAcpConnection(options) {
23851
23867
  if (inboundBatches === 0) {
23852
23868
  return;
23853
23869
  }
23854
- await new Promise((resolve) => {
23855
- idleWaiters.push(resolve);
23870
+ await new Promise((resolve2) => {
23871
+ idleWaiters.push(resolve2);
23856
23872
  if (inboundBatches === 0) {
23857
23873
  releaseIdleWaiters();
23858
23874
  }
@@ -24031,8 +24047,8 @@ var UnboundedEventStream = class {
24031
24047
  iterators.subscribers.delete(subscriber);
24032
24048
  return { value: void 0, done: true };
24033
24049
  }
24034
- await new Promise((resolve) => {
24035
- subscriber.wake = resolve;
24050
+ await new Promise((resolve2) => {
24051
+ subscriber.wake = resolve2;
24036
24052
  });
24037
24053
  }
24038
24054
  },
@@ -24523,8 +24539,8 @@ var Mutex = class {
24523
24539
  this.locked = true;
24524
24540
  return Promise.resolve();
24525
24541
  }
24526
- return new Promise((resolve) => {
24527
- this.waiters.push(resolve);
24542
+ return new Promise((resolve2) => {
24543
+ this.waiters.push(resolve2);
24528
24544
  });
24529
24545
  }
24530
24546
  tryAcquire() {
@@ -25658,14 +25674,14 @@ function createAcpExecutor(options) {
25658
25674
  }
25659
25675
  }
25660
25676
  const requestId = randomId();
25661
- const outcome = await new Promise((resolve) => {
25677
+ const outcome = await new Promise((resolve2) => {
25662
25678
  const pending = {
25663
25679
  requestId,
25664
25680
  request: params,
25665
25681
  permissionRequest,
25666
25682
  turnId: activeTurnId,
25667
25683
  settled: false,
25668
- resolve
25684
+ resolve: resolve2
25669
25685
  };
25670
25686
  approvals.add(pending);
25671
25687
  emit(
@@ -25691,6 +25707,12 @@ function createAcpExecutor(options) {
25691
25707
  const connection = createAcpConnection({
25692
25708
  readable: options.readable,
25693
25709
  writable: options.writable,
25710
+ onInboundFailure: (error62) => {
25711
+ options.log?.warn?.("acp connection closed: an inbound request could not be answered", {
25712
+ threadId: options.threadId,
25713
+ error: error62 instanceof Error ? error62.message : String(error62)
25714
+ });
25715
+ },
25694
25716
  onNotification: (notification) => {
25695
25717
  if (notification.method === "session/update") {
25696
25718
  handleSessionUpdate(notification.params);
@@ -25948,14 +25970,14 @@ function createAcpExecutor(options) {
25948
25970
  if (!promptMutex.isLocked) {
25949
25971
  return true;
25950
25972
  }
25951
- return await new Promise((resolve) => {
25973
+ return await new Promise((resolve2) => {
25952
25974
  const timer = setTimeout(() => {
25953
25975
  promptWaiters = promptWaiters.filter((waiter) => waiter !== onSettle);
25954
- resolve(false);
25976
+ resolve2(false);
25955
25977
  }, timeoutMs);
25956
25978
  const onSettle = () => {
25957
25979
  clearTimeout(timer);
25958
- resolve(true);
25980
+ resolve2(true);
25959
25981
  };
25960
25982
  promptWaiters.push(onSettle);
25961
25983
  });
@@ -26328,16 +26350,16 @@ function createAcpExecutor(options) {
26328
26350
  }
26329
26351
 
26330
26352
  // ../../packages/ycodium-acp-executor/src/probe.ts
26331
- function withProbeTimeout(promise2, timeoutMs, method) {
26332
- return new Promise((resolve, reject) => {
26353
+ function withProbeTimeout(promise2, timeoutMs, what) {
26354
+ return new Promise((resolve2, reject) => {
26333
26355
  const timer = setTimeout(() => {
26334
- reject(new Error(`ACP probe request '${method}' timed out after ${timeoutMs}ms`));
26356
+ reject(new Error(`${what} timed out after ${timeoutMs}ms`));
26335
26357
  }, timeoutMs);
26336
26358
  timer.unref?.();
26337
26359
  promise2.then(
26338
26360
  (value) => {
26339
26361
  clearTimeout(timer);
26340
- resolve(value);
26362
+ resolve2(value);
26341
26363
  },
26342
26364
  (error62) => {
26343
26365
  clearTimeout(timer);
@@ -26356,11 +26378,15 @@ async function openAcpProbeHandshake(input2) {
26356
26378
  const initializeResult = await withProbeTimeout(
26357
26379
  connection.request("initialize", acpInitializePayload(input2.clientInfo)),
26358
26380
  input2.requestTimeoutMs,
26359
- "initialize"
26381
+ "ACP probe request 'initialize'"
26360
26382
  );
26361
26383
  return {
26362
26384
  initializeResult,
26363
- request: (method, params) => withProbeTimeout(connection.request(method, params), input2.requestTimeoutMs, method),
26385
+ request: (method, params) => withProbeTimeout(
26386
+ connection.request(method, params),
26387
+ input2.requestTimeoutMs,
26388
+ `ACP probe request '${method}'`
26389
+ ),
26364
26390
  close: () => connection.close()
26365
26391
  };
26366
26392
  } catch (error62) {
@@ -26368,6 +26394,62 @@ async function openAcpProbeHandshake(input2) {
26368
26394
  throw error62;
26369
26395
  }
26370
26396
  }
26397
+ function acpAgentVersion(initializeResult) {
26398
+ if (!isRecord5(initializeResult)) return null;
26399
+ const agentInfo = initializeResult["agentInfo"];
26400
+ return isRecord5(agentInfo) ? asTrimmedString(agentInfo["version"]) ?? null : null;
26401
+ }
26402
+ var CLI_VERSION_PATTERN = /(?<![\d.])(\d+\.\d+\.\d+(?:-[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?)/;
26403
+ function parseCliVersion(output2) {
26404
+ return CLI_VERSION_PATTERN.exec(output2)?.[1] ?? null;
26405
+ }
26406
+ async function collectCappedText(stream, maxBytes) {
26407
+ const decoder = new TextDecoder();
26408
+ let text = "";
26409
+ let total = 0;
26410
+ for await (const chunk of stream) {
26411
+ total += chunk.byteLength;
26412
+ if (total <= maxBytes) text += decoder.decode(chunk, { stream: true });
26413
+ }
26414
+ return { text: text + decoder.decode(), overflowed: total > maxBytes };
26415
+ }
26416
+ async function collectProbeProcess(child, input2) {
26417
+ try {
26418
+ await child.closeStdin().catch(() => void 0);
26419
+ const [stdout, stderr, exitCode] = await withProbeTimeout(
26420
+ Promise.all([
26421
+ collectCappedText(child.stdout, input2.maxOutputBytes),
26422
+ collectCappedText(child.stderr, input2.maxOutputBytes),
26423
+ child.exited
26424
+ ]),
26425
+ input2.timeoutMs,
26426
+ input2.what
26427
+ );
26428
+ return { stdout: stdout.overflowed ? void 0 : stdout.text, stderr: stderr.text, exitCode };
26429
+ } finally {
26430
+ await child.kill("SIGTERM").catch(() => void 0);
26431
+ }
26432
+ }
26433
+ var MAX_VERSION_OUTPUT_BYTES = 64 * 1024;
26434
+ async function probeCliVersion(input2) {
26435
+ let child;
26436
+ try {
26437
+ child = await input2.spawn();
26438
+ } catch {
26439
+ return null;
26440
+ }
26441
+ try {
26442
+ const run = await collectProbeProcess(child, {
26443
+ timeoutMs: input2.timeoutMs,
26444
+ maxOutputBytes: MAX_VERSION_OUTPUT_BYTES,
26445
+ what: "--version"
26446
+ });
26447
+ if (run.exitCode !== 0) return null;
26448
+ return parseCliVersion(run.stdout ?? "") ?? parseCliVersion(run.stderr);
26449
+ } catch {
26450
+ return null;
26451
+ }
26452
+ }
26371
26453
 
26372
26454
  // src/card.ts
26373
26455
  var ACP_DRIVER_KIND = "acp";
@@ -26668,118 +26750,17 @@ function readKimiContextWindow(input2) {
26668
26750
  }
26669
26751
 
26670
26752
  // src/kimiModels.ts
26671
- var PROBE_TIMEOUT_MS = 15e3;
26672
- var MAX_OUTPUT_BYTES = 1024 * 1024;
26673
- var KIMI_MODEL_LIST_ARGS = ["provider", "list", "--json"];
26674
- function isRecord8(value) {
26675
- return typeof value === "object" && value !== null && !Array.isArray(value);
26676
- }
26677
- function asString3(value) {
26678
- return typeof value === "string" && value.trim().length > 0 ? value.trim() : void 0;
26679
- }
26680
- function positiveInt2(value) {
26681
- return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.trunc(value) : void 0;
26682
- }
26683
- function kimiProbeModels(stdout) {
26684
- let parsed;
26685
- try {
26686
- parsed = JSON.parse(stdout);
26687
- } catch {
26688
- return void 0;
26689
- }
26690
- if (!isRecord8(parsed)) return void 0;
26691
- const models = parsed["models"];
26692
- if (!isRecord8(models)) return void 0;
26693
- const out = [];
26694
- for (const [slug, entry] of Object.entries(models)) {
26695
- const trimmed = slug.trim();
26696
- if (trimmed.length === 0 || !isRecord8(entry)) continue;
26697
- const name = asString3(entry["displayName"]);
26698
- const contextWindow = positiveInt2(entry["maxContextSize"]);
26699
- out.push({
26700
- slug: trimmed,
26701
- ...name === void 0 ? {} : { name },
26702
- ...contextWindow === void 0 ? {} : { contextWindow }
26703
- });
26704
- }
26705
- return out;
26706
- }
26707
- function withProbeTimeout2(promise2, what) {
26708
- return new Promise((resolve, reject) => {
26709
- const timer = setTimeout(
26710
- () => reject(new Error(`${what} timed out after ${PROBE_TIMEOUT_MS}ms`)),
26711
- PROBE_TIMEOUT_MS
26712
- );
26713
- timer.unref?.();
26714
- promise2.then(
26715
- (value) => {
26716
- clearTimeout(timer);
26717
- resolve(value);
26718
- },
26719
- (error62) => {
26720
- clearTimeout(timer);
26721
- reject(error62 instanceof Error ? error62 : new Error(String(error62)));
26722
- }
26723
- );
26724
- });
26725
- }
26726
- function concat(chunks) {
26727
- const total = chunks.reduce((sum, chunk) => sum + chunk.byteLength, 0);
26728
- const out = new Uint8Array(total);
26729
- let offset = 0;
26730
- for (const chunk of chunks) {
26731
- out.set(chunk, offset);
26732
- offset += chunk.byteLength;
26733
- }
26734
- return out;
26735
- }
26736
- async function probeKimiModels(input2) {
26737
- const { recipe, host, env } = input2;
26738
- if (recipe.binaryPath.length === 0) return void 0;
26739
- let handle;
26740
- try {
26741
- handle = await host.process.spawn({
26742
- program: recipe.binaryPath,
26743
- // 实例信封里的启动参数(Kimi 那行预填 `acp`)在这一栏用不上:读配置要换成
26744
- // `provider list --json`,留着 `acp` 就等于让它起 ACP 服务然后什么都不问。
26745
- args: KIMI_MODEL_LIST_ARGS,
26746
- cwd: process.cwd(),
26747
- owner: { bindingId: host.bindingId },
26748
- ...Object.keys(env).length > 0 ? { env } : {}
26749
- });
26750
- const spawned = handle;
26751
- const collect = withProbeTimeout2(
26752
- (async () => {
26753
- const chunks = [];
26754
- let total = 0;
26755
- for await (const chunk of spawned.stdout) {
26756
- total += chunk.byteLength;
26757
- if (total > MAX_OUTPUT_BYTES) return void 0;
26758
- chunks.push(chunk);
26759
- }
26760
- return new TextDecoder().decode(concat(chunks));
26761
- })(),
26762
- "kimi provider list"
26763
- );
26764
- const drain = (async () => {
26765
- for await (const _chunk of spawned.stderr) {
26766
- }
26767
- })().catch(() => void 0);
26768
- const [stdout, , exitCode] = await Promise.all([collect, drain, spawned.exited]);
26769
- if (stdout === void 0) return void 0;
26770
- if (exitCode !== 0) return void 0;
26771
- return kimiProbeModels(stdout);
26772
- } catch {
26773
- return void 0;
26774
- } finally {
26775
- await handle?.kill("SIGTERM").catch(() => void 0);
26776
- }
26777
- }
26753
+ import {
26754
+ ExecutorSpawnRefusedError as ExecutorSpawnRefusedError2
26755
+ } from "@ycodium-ai/plugin-api/executor";
26778
26756
 
26779
26757
  // src/probeModels.ts
26780
26758
  import * as NodeFS2 from "node:fs";
26781
26759
  import * as NodeOS from "node:os";
26782
26760
  import * as NodePath2 from "node:path";
26761
+ import {
26762
+ ExecutorSpawnRefusedError
26763
+ } from "@ycodium-ai/plugin-api/executor";
26783
26764
  import { nodeStreamsFromHandle } from "@ycodium-ai/plugin-api/process-stdin";
26784
26765
  var PROBE_REQUEST_TIMEOUT_MS = 15e3;
26785
26766
  var CLIENT_INFO = { name: "ycodium-agent-acp-probe", version: "0.2.0" };
@@ -26788,18 +26769,18 @@ function acpProbeSessionCwd(instanceId) {
26788
26769
  const safe = instanceId.replace(/[^a-zA-Z0-9_-]/g, "_");
26789
26770
  return NodePath2.join(NodeOS.tmpdir(), `ycodium-acp-probe-${safe}`);
26790
26771
  }
26791
- function isRecord9(value) {
26772
+ function isRecord8(value) {
26792
26773
  return typeof value === "object" && value !== null && !Array.isArray(value);
26793
26774
  }
26794
- function asString4(value) {
26775
+ function asString3(value) {
26795
26776
  return typeof value === "string" ? value : void 0;
26796
26777
  }
26797
26778
  function findAcpProbeSessionId(listed, probeCwd) {
26798
- if (!isRecord9(listed) || !Array.isArray(listed["sessions"])) return void 0;
26779
+ if (!isRecord8(listed) || !Array.isArray(listed["sessions"])) return void 0;
26799
26780
  for (const entry of listed["sessions"]) {
26800
- if (!isRecord9(entry)) continue;
26801
- if (asString4(entry["cwd"]) !== probeCwd) continue;
26802
- const sessionId = asString4(entry["sessionId"]);
26781
+ if (!isRecord8(entry)) continue;
26782
+ if (asString3(entry["cwd"]) !== probeCwd) continue;
26783
+ const sessionId = asString3(entry["sessionId"]);
26803
26784
  if (sessionId !== void 0) return sessionId;
26804
26785
  }
26805
26786
  return void 0;
@@ -26814,7 +26795,7 @@ async function probeSessionSetup(handshake, probeCwd) {
26814
26795
  return handshake.request("session/new", { cwd: probeCwd, mcpServers: [] });
26815
26796
  }
26816
26797
  function acpProbeModels(setup) {
26817
- if (!isRecord9(setup)) return void 0;
26798
+ if (!isRecord8(setup)) return void 0;
26818
26799
  const rawOptions = setup["configOptions"];
26819
26800
  const choices = acpModelChoicesFromConfigOptions(rawOptions) ?? acpModelChoicesFromSessionModels(setup["models"]);
26820
26801
  if (choices === void 0 || choices.length === 0) return void 0;
@@ -26842,14 +26823,16 @@ function acpProbeModels(setup) {
26842
26823
  };
26843
26824
  return models.map((model) => ({ ...model, capabilities }));
26844
26825
  }
26845
- var failureDetail = (error62) => (error62 instanceof Error ? error62.message : String(error62)).trim() || void 0;
26826
+ var probeFailureDetail = (error62) => (error62 instanceof Error ? error62.message : String(error62)).trim() || void 0;
26827
+ var ACP_AUTH_REQUIRED_CODE = RequestError.authRequired().code;
26828
+ var isAcpAuthRequired = (error62) => error62 instanceof RequestError && error62.code === ACP_AUTH_REQUIRED_CODE;
26846
26829
  async function probeAcpModelsViaSession(input2) {
26847
26830
  const { recipe, host, env } = input2;
26848
26831
  const probeCwd = acpProbeSessionCwd(host.instanceId);
26849
26832
  try {
26850
26833
  NodeFS2.mkdirSync(probeCwd, { recursive: true });
26851
26834
  } catch (error62) {
26852
- return { kind: "failed", detail: failureDetail(error62) };
26835
+ return { kind: "failed", detail: probeFailureDetail(error62), version: null };
26853
26836
  }
26854
26837
  let handle;
26855
26838
  try {
@@ -26861,7 +26844,8 @@ async function probeAcpModelsViaSession(input2) {
26861
26844
  ...Object.keys(env).length > 0 ? { env } : {}
26862
26845
  });
26863
26846
  } catch (error62) {
26864
- return { kind: "not-started", detail: failureDetail(error62) };
26847
+ if (error62 instanceof ExecutorSpawnRefusedError) throw error62;
26848
+ return { kind: "not-started", detail: probeFailureDetail(error62) };
26865
26849
  }
26866
26850
  let handshake;
26867
26851
  try {
@@ -26875,6 +26859,7 @@ async function probeAcpModelsViaSession(input2) {
26875
26859
  const setup = await probeSessionSetup(handshake, probeCwd);
26876
26860
  return {
26877
26861
  kind: "answered",
26862
+ version: acpAgentVersion(handshake.initializeResult),
26878
26863
  models: acpProbeModels(setup),
26879
26864
  // 审批档原样上报:宿主落进 provider 快照的 `nativeModes`,composer 直接画这只手
26880
26865
  // 自己的那几档,不再退回宿主四档开关。
@@ -26886,13 +26871,124 @@ async function probeAcpModelsViaSession(input2) {
26886
26871
  currentNativeModeId: acpCurrentModeId(setup)
26887
26872
  };
26888
26873
  } catch (error62) {
26889
- return { kind: "failed", detail: failureDetail(error62) };
26874
+ const version2 = acpAgentVersion(handshake?.initializeResult);
26875
+ if (handshake !== void 0 && isAcpAuthRequired(error62)) {
26876
+ return { kind: "auth-required", version: version2 };
26877
+ }
26878
+ return { kind: "failed", detail: probeFailureDetail(error62), version: version2 };
26890
26879
  } finally {
26891
26880
  await handshake?.close().catch(() => void 0);
26892
26881
  await handle.kill("SIGTERM").catch(() => void 0);
26893
26882
  }
26894
26883
  }
26895
26884
 
26885
+ // src/kimiModels.ts
26886
+ var PROBE_TIMEOUT_MS = 15e3;
26887
+ var MAX_OUTPUT_BYTES = 1024 * 1024;
26888
+ var KIMI_MODEL_LIST_ARGS = ["provider", "list", "--json"];
26889
+ function isRecord9(value) {
26890
+ return typeof value === "object" && value !== null && !Array.isArray(value);
26891
+ }
26892
+ function asString4(value) {
26893
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : void 0;
26894
+ }
26895
+ function positiveInt2(value) {
26896
+ return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.trunc(value) : void 0;
26897
+ }
26898
+ function kimiProbeModels(stdout) {
26899
+ let parsed;
26900
+ try {
26901
+ parsed = JSON.parse(stdout);
26902
+ } catch {
26903
+ return void 0;
26904
+ }
26905
+ if (!isRecord9(parsed)) return void 0;
26906
+ const models = parsed["models"];
26907
+ if (!isRecord9(models)) return void 0;
26908
+ const out = [];
26909
+ for (const [slug, entry] of Object.entries(models)) {
26910
+ const trimmed = slug.trim();
26911
+ if (trimmed.length === 0 || !isRecord9(entry)) continue;
26912
+ const name = asString4(entry["displayName"]);
26913
+ const contextWindow = positiveInt2(entry["maxContextSize"]);
26914
+ out.push({
26915
+ slug: trimmed,
26916
+ ...name === void 0 ? {} : { name },
26917
+ ...contextWindow === void 0 ? {} : { contextWindow }
26918
+ });
26919
+ }
26920
+ return out;
26921
+ }
26922
+ async function probeKimiModels(input2) {
26923
+ const { recipe, host, env } = input2;
26924
+ let handle;
26925
+ try {
26926
+ handle = await host.process.spawn({
26927
+ program: recipe.binaryPath,
26928
+ // 实例信封里的启动参数(Kimi 那行预填 `acp`)在这一栏用不上:读配置要换成
26929
+ // `provider list --json`,留着 `acp` 就等于让它起 ACP 服务然后什么都不问。
26930
+ args: KIMI_MODEL_LIST_ARGS,
26931
+ cwd: process.cwd(),
26932
+ owner: { bindingId: host.bindingId },
26933
+ ...Object.keys(env).length > 0 ? { env } : {}
26934
+ });
26935
+ } catch (error62) {
26936
+ if (error62 instanceof ExecutorSpawnRefusedError2) throw error62;
26937
+ return { kind: "not-started", detail: probeFailureDetail(error62) };
26938
+ }
26939
+ try {
26940
+ const run = await collectProbeProcess(handle, {
26941
+ timeoutMs: input2.timeoutMs ?? PROBE_TIMEOUT_MS,
26942
+ // 这份 JSON 实测 1.7KB;封顶是防 CLI 抽风刷日志把内存吃掉。
26943
+ maxOutputBytes: MAX_OUTPUT_BYTES,
26944
+ what: "kimi provider list"
26945
+ });
26946
+ if (run.exitCode !== 0) {
26947
+ const stderr = run.stderr.trim();
26948
+ return {
26949
+ kind: "failed",
26950
+ detail: stderr.length > 0 ? stderr : run.exitCode === null ? "kimi provider list was killed by a signal" : `kimi provider list exited with code ${run.exitCode}`
26951
+ };
26952
+ }
26953
+ return {
26954
+ kind: "answered",
26955
+ models: run.stdout === void 0 ? void 0 : kimiProbeModels(run.stdout)
26956
+ };
26957
+ } catch (error62) {
26958
+ return { kind: "failed", detail: probeFailureDetail(error62) };
26959
+ }
26960
+ }
26961
+ async function probeKimiVersion(input2) {
26962
+ const { recipe, host, env } = input2;
26963
+ return probeCliVersion({
26964
+ spawn: () => host.process.spawn({
26965
+ program: recipe.binaryPath,
26966
+ args: ["--version"],
26967
+ cwd: process.cwd(),
26968
+ owner: { bindingId: host.bindingId },
26969
+ ...Object.keys(env).length > 0 ? { env } : {}
26970
+ }),
26971
+ timeoutMs: PROBE_TIMEOUT_MS
26972
+ });
26973
+ }
26974
+ async function probeKimi(input2) {
26975
+ const [listed, version2] = await Promise.all([probeKimiModels(input2), probeKimiVersion(input2)]);
26976
+ switch (listed.kind) {
26977
+ case "not-started":
26978
+ return { kind: "not-started", detail: listed.detail };
26979
+ case "failed":
26980
+ return { kind: "failed", detail: listed.detail, version: version2 };
26981
+ case "answered":
26982
+ return {
26983
+ kind: "answered",
26984
+ version: version2,
26985
+ models: listed.models,
26986
+ nativeModes: [],
26987
+ currentNativeModeId: void 0
26988
+ };
26989
+ }
26990
+ }
26991
+
26896
26992
  // src/runtimeModeApproval.ts
26897
26993
  function acpRuntimeModeAutoApproves(runtimeMode, toolKind) {
26898
26994
  switch (runtimeMode) {
@@ -26912,8 +27008,8 @@ function acpSessionModeToApply(requestedNativeModeId, startedModeId) {
26912
27008
  // src/acpInstance.ts
26913
27009
  var RESUME_SCHEMA_VERSION = 1;
26914
27010
  var CLIENT_INFO2 = { name: "ycodium-agent-acp", version: "0.2.0" };
26915
- var yieldTick = () => new Promise((resolve) => {
26916
- setImmediate(resolve);
27011
+ var yieldTick = () => new Promise((resolve2) => {
27012
+ setImmediate(resolve2);
26917
27013
  });
26918
27014
  var EventBus = class {
26919
27015
  #queued = [];
@@ -26940,8 +27036,8 @@ var EventBus = class {
26940
27036
  const queued = this.#queued.shift();
26941
27037
  if (queued !== void 0) return Promise.resolve({ done: false, value: queued });
26942
27038
  if (this.#closed) return Promise.resolve({ done: true, value: void 0 });
26943
- return new Promise((resolve) => {
26944
- this.#waiters.push(resolve);
27039
+ return new Promise((resolve2) => {
27040
+ this.#waiters.push(resolve2);
26945
27041
  });
26946
27042
  }
26947
27043
  };
@@ -26975,6 +27071,11 @@ function buildSpawnEnv(recipe, configuredHomePath, launchEnv = {}) {
26975
27071
  function nowIso() {
26976
27072
  return (/* @__PURE__ */ new Date()).toISOString();
26977
27073
  }
27074
+ function acpContinuationKey(recipe) {
27075
+ const homePath = recipe.homePath.trim();
27076
+ const resolvedHomePath = homePath.length > 0 ? NodePath3.resolve(expandHomePath(homePath)) : "";
27077
+ return `acp:home:${recipe.binaryPath.trim()}\0${resolvedHomePath}`;
27078
+ }
26978
27079
  function resolveHomePath(recipe, launchHomePath) {
26979
27080
  const envVar = recipe.homeEnvVar.trim();
26980
27081
  const fromEnvironment = envVar.length > 0 ? recipe.environment[envVar]?.trim() : void 0;
@@ -27023,34 +27124,46 @@ var createAcpInstance = (config2, host) => {
27023
27124
  detail: "ACP program path is not configured"
27024
27125
  };
27025
27126
  }
27026
- const outcome = isKimiInstance(recipe) ? {
27027
- kind: "answered",
27028
- models: await probeKimiModels({
27029
- recipe,
27030
- host,
27031
- env: buildSpawnEnv(recipe, recipe.homePath)
27032
- }),
27033
- nativeModes: void 0,
27034
- currentNativeModeId: void 0
27035
- } : await probeAcpModelsViaSession({
27036
- recipe,
27037
- host,
27038
- env: buildSpawnEnv(recipe, recipe.homePath)
27039
- });
27040
- const models = outcome.kind === "answered" ? outcome.models : void 0;
27041
- const nativeModes = outcome.kind === "answered" && outcome.nativeModes !== void 0 ? outcome.nativeModes : void 0;
27042
- const currentNativeModeId = outcome.kind === "answered" ? outcome.currentNativeModeId : void 0;
27043
- return {
27044
- installed: true,
27045
- version: null,
27046
- readiness: "unchecked",
27047
- auth: { status: "unknown" },
27048
- ...models === void 0 ? {} : { models },
27049
- ...nativeModes === void 0 || nativeModes.length === 0 ? {} : {
27050
- nativeModes,
27051
- ...currentNativeModeId === void 0 ? {} : { currentNativeModeId }
27052
- }
27053
- };
27127
+ const env = buildSpawnEnv(recipe, recipe.homePath);
27128
+ const outcome = isKimiInstance(recipe) ? await probeKimi({ recipe, host, env }) : await probeAcpModelsViaSession({ recipe, host, env });
27129
+ switch (outcome.kind) {
27130
+ case "not-started":
27131
+ return {
27132
+ installed: false,
27133
+ version: null,
27134
+ readiness: "error",
27135
+ auth: { status: "unknown" },
27136
+ ...outcome.detail === void 0 ? {} : { detail: outcome.detail }
27137
+ };
27138
+ case "failed":
27139
+ return {
27140
+ installed: true,
27141
+ version: outcome.version,
27142
+ readiness: "error",
27143
+ auth: { status: "unknown" },
27144
+ ...outcome.detail === void 0 ? {} : { detail: outcome.detail }
27145
+ };
27146
+ case "auth-required":
27147
+ return {
27148
+ installed: true,
27149
+ version: outcome.version,
27150
+ readiness: "ready",
27151
+ auth: { status: "unauthenticated" }
27152
+ };
27153
+ case "answered":
27154
+ return {
27155
+ installed: true,
27156
+ version: outcome.version,
27157
+ readiness: "ready",
27158
+ auth: { status: "unknown" },
27159
+ ...outcome.models === void 0 ? {} : { models: outcome.models },
27160
+ // 清单与当前档必须同进同出:只报清单,界面在清单里找不到当前档会整个不画那一格。
27161
+ ...outcome.nativeModes.length === 0 ? {} : {
27162
+ nativeModes: outcome.nativeModes,
27163
+ ...outcome.currentNativeModeId === void 0 ? {} : { currentNativeModeId: outcome.currentNativeModeId }
27164
+ }
27165
+ };
27166
+ }
27054
27167
  },
27055
27168
  startSession: async (input2, signal) => {
27056
27169
  if (disposed) throw new Error("instance disposed");
@@ -27249,6 +27362,7 @@ var createAcpInstance = (config2, host) => {
27249
27362
  return { threadId, turns: booked.turns };
27250
27363
  },
27251
27364
  events,
27365
+ continuationKey: acpContinuationKey(recipe),
27252
27366
  dispose: async () => {
27253
27367
  if (disposed) return;
27254
27368
  disposed = true;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ycodium-ai/agent-acp",
3
- "version": "0.2.2126",
3
+ "version": "0.2.2143",
4
4
  "description": "Generic ACP executor: user-supplied CLI over Agent Client Protocol stdio.",
5
5
  "keywords": [
6
6
  "ycodium",
@@ -20,7 +20,7 @@
20
20
  "@types/node": "24.12.4",
21
21
  "esbuild": "0.28.1",
22
22
  "vite-plus": "0.2.2",
23
- "@ycodium-ai/plugin-api": "0.2.2126",
23
+ "@ycodium-ai/plugin-api": "0.2.2143",
24
24
  "ycodium-acp-executor": "0.0.0",
25
25
  "ycodium-acp-protocol": "0.0.0"
26
26
  },