@ycodium-ai/agent-grok 0.2.2130 → 0.2.2155

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 (3) hide show
  1. package/README.md +3 -2
  2. package/dist/index.js +115 -19
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -34,8 +34,9 @@ Facts:
34
34
  credential reads use the home the subprocess actually sees. An empty
35
35
  account directory means the CLI default `~/.grok` is used without injection.
36
36
  - Manifest permission is `{ kind: "executor" }`.
37
- - Session model switching is `unsupported` on the card until the plugin execution
38
- path is wired to the model-management UI.
37
+ - Native in-session model switching is `unsupported` on the card. The host applies
38
+ upstream model changes by rebinding the local endpoint and resuming the existing
39
+ conversation; the CLI's internal model is tracked separately.
39
40
 
40
41
  Develop with `vp run --filter @ycodium-ai/agent-grok typecheck`, `build`, and `test`.
41
42
 
package/dist/index.js CHANGED
@@ -57,8 +57,7 @@ var GROK_CARD = {
57
57
  verifiedSkills: false,
58
58
  everydayHarness: false,
59
59
  capabilities: {
60
- // Grok 换模型只有 `session/set_config_option` 一条路;库会按握手形状自动协商,
61
- // 但插件侧的执行路径还没接到模型管理 UI,名片必须说实话。
60
+ // 插件不承诺原生会话内活切型号;宿主更换上游模型时重绑端点并续接原生会话。
62
61
  sessionModelSwitch: "unsupported",
63
62
  // 运行模式只在 spawn 时定得了(`--permission-mode` 是启动旗标,ACP 那一侧
64
63
  // `modes: null`,`session/set_mode` 对它无效),所以不许声称能会话中途换。
@@ -94,7 +93,11 @@ var GROK_CARD = {
94
93
  additionalProperties: true
95
94
  },
96
95
  modelConnectivity: {
97
- dialects: ["anthropic-messages", "openai-responses", "openai-chat-completions"],
96
+ // 只报绑定送得出去的那一种:后缀是 `/chat/completions`,绑定也不替它改 `api_backend`,
97
+ // 所以它打本机端点说的永远是 chat。CLI 换了配置能说 messages / responses 不算——
98
+ // 报三种时兼容判定照着挑了「直通」,起手才发现后缀对不上,接 Anthropic 格式的脑与
99
+ // Responses 格式的订阅全起不来。别家的脑经宿主翻译接上。
100
+ dialects: ["openai-chat-completions"],
98
101
  acceptsExternalEndpoints: true,
99
102
  // 地址那一栏必须投:不设 `GROK_MODELS_BASE_URL` 的话整条请求根本不来我们这儿
100
103
  // (`grokModelProxyEgress.test.ts` 拿掉这一栏就红)。
@@ -23751,6 +23754,7 @@ function createAcpConnection(options) {
23751
23754
  options.readable.off("data", onData);
23752
23755
  options.readable.off("end", onEnd);
23753
23756
  options.readable.off("error", onError);
23757
+ options.writable.off("error", onWritableError);
23754
23758
  try {
23755
23759
  await byteWriter.close();
23756
23760
  } catch {
@@ -23889,8 +23893,17 @@ function createAcpConnection(options) {
23889
23893
  endInboundBatch();
23890
23894
  }
23891
23895
  };
23896
+ const failInbound = (error62) => {
23897
+ if (closedError !== void 0) return;
23898
+ options.onInboundFailure?.(error62);
23899
+ void shutdown(
23900
+ new AcpConnectionClosedError(
23901
+ `ACP inbound handling failed: ${error62 instanceof Error ? error62.message : String(error62)}`
23902
+ )
23903
+ );
23904
+ };
23892
23905
  const enqueueInbound = (work) => {
23893
- inboundTail = inboundTail.then(work, work);
23906
+ inboundTail = inboundTail.then(work).catch(failInbound);
23894
23907
  };
23895
23908
  const onData = (chunk) => {
23896
23909
  if (closedError !== void 0) {
@@ -23940,9 +23953,15 @@ function createAcpConnection(options) {
23940
23953
  );
23941
23954
  });
23942
23955
  };
23956
+ const onWritableError = (error62) => {
23957
+ void shutdown(
23958
+ error62 instanceof Error ? error62 : new AcpConnectionClosedError("ACP output stream error")
23959
+ );
23960
+ };
23943
23961
  options.readable.on("data", onData);
23944
23962
  options.readable.on("end", onEnd);
23945
23963
  options.readable.on("error", onError);
23964
+ options.writable.once("error", onWritableError);
23946
23965
  const request = (async (method, params) => {
23947
23966
  failIfClosed();
23948
23967
  const id = nextRequestId;
@@ -23951,7 +23970,7 @@ function createAcpConnection(options) {
23951
23970
  pending.set(String(id), { method, resolve: resolve2, reject });
23952
23971
  const message = params === void 0 ? { jsonrpc: "2.0", id, method } : { jsonrpc: "2.0", id, method, params };
23953
23972
  try {
23954
- await writeMessage(message);
23973
+ await Promise.race([writeMessage(message), promise2]);
23955
23974
  } catch (error62) {
23956
23975
  if (pending.delete(String(id))) {
23957
23976
  reject(error62);
@@ -25726,6 +25745,12 @@ function createAcpExecutor(options) {
25726
25745
  const connection = createAcpConnection({
25727
25746
  readable: options.readable,
25728
25747
  writable: options.writable,
25748
+ onInboundFailure: (error62) => {
25749
+ options.log?.warn?.("acp connection closed: an inbound request could not be answered", {
25750
+ threadId: options.threadId,
25751
+ error: error62 instanceof Error ? error62.message : String(error62)
25752
+ });
25753
+ },
25729
25754
  onNotification: (notification) => {
25730
25755
  if (notification.method === "session/update") {
25731
25756
  handleSessionUpdate(notification.params);
@@ -26363,10 +26388,10 @@ function createAcpExecutor(options) {
26363
26388
  }
26364
26389
 
26365
26390
  // ../../packages/ycodium-acp-executor/src/probe.ts
26366
- function withProbeTimeout(promise2, timeoutMs, method) {
26391
+ function withProbeTimeout(promise2, timeoutMs, what) {
26367
26392
  return new Promise((resolve2, reject) => {
26368
26393
  const timer = setTimeout(() => {
26369
- reject(new Error(`ACP probe request '${method}' timed out after ${timeoutMs}ms`));
26394
+ reject(new Error(`${what} timed out after ${timeoutMs}ms`));
26370
26395
  }, timeoutMs);
26371
26396
  timer.unref?.();
26372
26397
  promise2.then(
@@ -26391,11 +26416,15 @@ async function openAcpProbeHandshake(input2) {
26391
26416
  const initializeResult = await withProbeTimeout(
26392
26417
  connection.request("initialize", acpInitializePayload(input2.clientInfo)),
26393
26418
  input2.requestTimeoutMs,
26394
- "initialize"
26419
+ "ACP probe request 'initialize'"
26395
26420
  );
26396
26421
  return {
26397
26422
  initializeResult,
26398
- request: (method, params) => withProbeTimeout(connection.request(method, params), input2.requestTimeoutMs, method),
26423
+ request: (method, params) => withProbeTimeout(
26424
+ connection.request(method, params),
26425
+ input2.requestTimeoutMs,
26426
+ `ACP probe request '${method}'`
26427
+ ),
26399
26428
  close: () => connection.close()
26400
26429
  };
26401
26430
  } catch (error62) {
@@ -26403,6 +26432,57 @@ async function openAcpProbeHandshake(input2) {
26403
26432
  throw error62;
26404
26433
  }
26405
26434
  }
26435
+ var CLI_VERSION_PATTERN = /(?<![\d.])(\d+\.\d+\.\d+(?:-[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?)/;
26436
+ function parseCliVersion(output2) {
26437
+ return CLI_VERSION_PATTERN.exec(output2)?.[1] ?? null;
26438
+ }
26439
+ async function collectCappedText(stream, maxBytes) {
26440
+ const decoder = new TextDecoder();
26441
+ let text = "";
26442
+ let total = 0;
26443
+ for await (const chunk of stream) {
26444
+ total += chunk.byteLength;
26445
+ if (total <= maxBytes) text += decoder.decode(chunk, { stream: true });
26446
+ }
26447
+ return { text: text + decoder.decode(), overflowed: total > maxBytes };
26448
+ }
26449
+ async function collectProbeProcess(child, input2) {
26450
+ try {
26451
+ await child.closeStdin().catch(() => void 0);
26452
+ const [stdout, stderr, exitCode] = await withProbeTimeout(
26453
+ Promise.all([
26454
+ collectCappedText(child.stdout, input2.maxOutputBytes),
26455
+ collectCappedText(child.stderr, input2.maxOutputBytes),
26456
+ child.exited
26457
+ ]),
26458
+ input2.timeoutMs,
26459
+ input2.what
26460
+ );
26461
+ return { stdout: stdout.overflowed ? void 0 : stdout.text, stderr: stderr.text, exitCode };
26462
+ } finally {
26463
+ await child.kill("SIGTERM").catch(() => void 0);
26464
+ }
26465
+ }
26466
+ var MAX_VERSION_OUTPUT_BYTES = 64 * 1024;
26467
+ async function probeCliVersion(input2) {
26468
+ let child;
26469
+ try {
26470
+ child = await input2.spawn();
26471
+ } catch {
26472
+ return null;
26473
+ }
26474
+ try {
26475
+ const run = await collectProbeProcess(child, {
26476
+ timeoutMs: input2.timeoutMs,
26477
+ maxOutputBytes: MAX_VERSION_OUTPUT_BYTES,
26478
+ what: "--version"
26479
+ });
26480
+ if (run.exitCode !== 0) return null;
26481
+ return parseCliVersion(run.stdout ?? "") ?? parseCliVersion(run.stderr);
26482
+ } catch {
26483
+ return null;
26484
+ }
26485
+ }
26406
26486
 
26407
26487
  // src/config.ts
26408
26488
  var GROK_DEFAULT_LAUNCH_ARGS = "agent stdio";
@@ -26880,6 +26960,9 @@ var grokPromptSettlement = {
26880
26960
  };
26881
26961
 
26882
26962
  // src/probeModels.ts
26963
+ import {
26964
+ ExecutorSpawnRefusedError
26965
+ } from "@ycodium-ai/plugin-api/executor";
26883
26966
  import { nodeStreamsFromHandle } from "@ycodium-ai/plugin-api/process-stdin";
26884
26967
  var PROBE_REQUEST_TIMEOUT_MS = 2e4;
26885
26968
  function grokProbeModels(initializeResult) {
@@ -26904,6 +26987,7 @@ async function probeGrokModels(input2) {
26904
26987
  ...Object.keys(env).length > 0 ? { env } : {}
26905
26988
  });
26906
26989
  } catch (error62) {
26990
+ if (error62 instanceof ExecutorSpawnRefusedError) throw error62;
26907
26991
  return { kind: "not-started", detail: failureDetail(error62) };
26908
26992
  }
26909
26993
  let handshake;
@@ -26923,6 +27007,18 @@ async function probeGrokModels(input2) {
26923
27007
  await handle.kill("SIGTERM").catch(() => void 0);
26924
27008
  }
26925
27009
  }
27010
+ async function probeGrokVersion(input2) {
27011
+ const { recipe, host, env } = input2;
27012
+ return probeCliVersion({
27013
+ spawn: () => host.process.spawn({
27014
+ program: recipe.binaryPath,
27015
+ args: ["--version"],
27016
+ owner: { bindingId: host.bindingId },
27017
+ ...Object.keys(env).length > 0 ? { env } : {}
27018
+ }),
27019
+ timeoutMs: PROBE_REQUEST_TIMEOUT_MS
27020
+ });
27021
+ }
26926
27022
 
26927
27023
  // src/grokInstance.ts
26928
27024
  var RESUME_SCHEMA_VERSION = 1;
@@ -27051,19 +27147,19 @@ var createGrokInstance = (config2, host) => {
27051
27147
  };
27052
27148
  }
27053
27149
  const configuredHome = recipe.homePath;
27054
- const outcome = await probeGrokModels({
27150
+ const env = buildSpawnEnv(
27055
27151
  recipe,
27056
- host,
27057
- env: buildSpawnEnv(
27058
- recipe,
27059
- resolveGrokHomePath(configuredHome),
27060
- configuredHome.trim().length > 0
27061
- )
27062
- });
27152
+ resolveGrokHomePath(configuredHome),
27153
+ configuredHome.trim().length > 0
27154
+ );
27155
+ const [outcome, version2] = await Promise.all([
27156
+ probeGrokModels({ recipe, host, env }),
27157
+ probeGrokVersion({ recipe, host, env })
27158
+ ]);
27063
27159
  if (outcome.kind !== "answered") {
27064
27160
  return {
27065
27161
  installed: outcome.kind === "failed",
27066
- version: null,
27162
+ version: version2,
27067
27163
  readiness: "error",
27068
27164
  auth: probeAuth(),
27069
27165
  ...outcome.detail === void 0 ? {} : { detail: outcome.detail },
@@ -27074,7 +27170,7 @@ var createGrokInstance = (config2, host) => {
27074
27170
  }
27075
27171
  return {
27076
27172
  installed: true,
27077
- version: null,
27173
+ version: version2,
27078
27174
  readiness: "ready",
27079
27175
  auth: probeAuth(),
27080
27176
  ...outcome.models === void 0 ? {} : { models: outcome.models },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ycodium-ai/agent-grok",
3
- "version": "0.2.2130",
3
+ "version": "0.2.2155",
4
4
  "description": "Grok executor: grok 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.2130",
23
+ "@ycodium-ai/plugin-api": "0.2.2155",
24
24
  "ycodium-acp-protocol": "0.0.0",
25
25
  "ycodium-acp-executor": "0.0.0"
26
26
  },