@ycodium-ai/agent-grok 0.2.3710 → 0.2.3766

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -6,6 +6,11 @@ var __export = (target, all) => {
6
6
 
7
7
  // src/index.ts
8
8
  import { definePlugin } from "@ycodium-ai/plugin-api";
9
+ import {
10
+ EXECUTOR_EFFECTIVE_PAYLOAD_SCHEMA,
11
+ executorHomeEffectiveValue,
12
+ readExecutorEffectiveRequest
13
+ } from "@ycodium-ai/plugin-api/executor";
9
14
 
10
15
  // src/card.ts
11
16
  var GROK_DRIVER_KIND = "grok";
@@ -23,6 +28,8 @@ var GROK_CARD = {
23
28
  // `grokInstance.startSession` 里按用户选择拼。
24
29
  launchArgs: "agent stdio",
25
30
  homeEnvVar: "GROK_HOME",
31
+ // 设置表单里留空的格子此刻会用什么,见 index.ts 的同名命令。
32
+ effectiveCommand: "effectiveConfig",
26
33
  // 只有一个账户目录,它同时装着会话历史:换目录,已开的 thread 就接不上这个实例了。
27
34
  accountDirectory: {
28
35
  configKeys: ["homePath"],
@@ -161,6 +168,166 @@ var GROK_CARD = {
161
168
  }
162
169
  };
163
170
 
171
+ // src/grokHome.ts
172
+ import * as NodeCrypto from "node:crypto";
173
+ import * as NodeOS from "node:os";
174
+ import * as NodePath2 from "node:path";
175
+ import { mergeLaunchEnv } from "@ycodium-ai/plugin-api/executor";
176
+
177
+ // src/grokAccount.ts
178
+ import * as NodeFS from "node:fs";
179
+ import * as NodePath from "node:path";
180
+ var AUTH_FILE_NAME = "auth.json";
181
+ var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
182
+ var readString = (record4, key) => {
183
+ const value = record4[key];
184
+ if (typeof value !== "string") return void 0;
185
+ const trimmed3 = value.trim();
186
+ return trimmed3.length > 0 ? trimmed3 : void 0;
187
+ };
188
+ var parseJsonRecord = (raw) => {
189
+ let parsed;
190
+ try {
191
+ parsed = JSON.parse(raw);
192
+ } catch {
193
+ return void 0;
194
+ }
195
+ return isRecord(parsed) ? parsed : void 0;
196
+ };
197
+ var directoryExists = (homePath) => {
198
+ try {
199
+ return NodeFS.statSync(homePath).isDirectory();
200
+ } catch {
201
+ return false;
202
+ }
203
+ };
204
+ var readCredentialFile = (path) => {
205
+ try {
206
+ return NodeFS.readFileSync(path, "utf8");
207
+ } catch {
208
+ return void 0;
209
+ }
210
+ };
211
+ var signedOutProbe = (exists) => ({
212
+ state: "signed-out",
213
+ directoryExists: exists,
214
+ email: void 0,
215
+ name: void 0,
216
+ expiresAt: void 0
217
+ });
218
+ var unreadableProbe = (exists) => ({
219
+ state: "unreadable",
220
+ directoryExists: exists,
221
+ email: void 0,
222
+ name: void 0,
223
+ expiresAt: void 0
224
+ });
225
+ var joinName = (record4) => {
226
+ const first = readString(record4, "first_name");
227
+ const last = readString(record4, "last_name");
228
+ const joined = [first, last].filter((part) => part !== void 0).join(" ");
229
+ return joined.length > 0 ? joined : void 0;
230
+ };
231
+ var findIdentityEntry = (parsed) => {
232
+ for (const value of Object.values(parsed)) {
233
+ if (!isRecord(value)) continue;
234
+ if (readString(value, "email") !== void 0 || readString(value, "user_id") !== void 0) {
235
+ return value;
236
+ }
237
+ }
238
+ return void 0;
239
+ };
240
+ var isExpired = (expiresAt, nowMs) => {
241
+ if (expiresAt === void 0) return false;
242
+ const parsed = Date.parse(expiresAt);
243
+ return Number.isNaN(parsed) ? false : parsed <= nowMs;
244
+ };
245
+ var readGrokAccount = (homePath) => {
246
+ const exists = directoryExists(homePath);
247
+ const raw = readCredentialFile(NodePath.join(homePath, AUTH_FILE_NAME));
248
+ if (raw === void 0) return signedOutProbe(exists);
249
+ const parsed = parseJsonRecord(raw);
250
+ const identity = parsed === void 0 ? void 0 : findIdentityEntry(parsed);
251
+ if (identity === void 0) return unreadableProbe(exists);
252
+ const expiresAt = readString(identity, "expires_at");
253
+ return {
254
+ state: isExpired(expiresAt, Date.now()) ? "expired" : "signed-in",
255
+ directoryExists: exists,
256
+ email: readString(identity, "email"),
257
+ name: joinName(identity),
258
+ expiresAt
259
+ };
260
+ };
261
+
262
+ // src/grokHome.ts
263
+ function expandHomePath(value) {
264
+ if (value === "~") return NodeOS.homedir();
265
+ if (value.startsWith("~/") || value.startsWith("~\\")) {
266
+ return NodePath2.join(NodeOS.homedir(), value.slice(2));
267
+ }
268
+ return value;
269
+ }
270
+ function resolveGrokHomePath(value) {
271
+ const trimmed3 = value.trim();
272
+ if (trimmed3.length === 0) return NodePath2.join(NodeOS.homedir(), ".grok");
273
+ return NodePath2.resolve(expandHomePath(trimmed3));
274
+ }
275
+ function probeGrokAccount(accountPath) {
276
+ const resolved = resolveGrokHomePath(accountPath);
277
+ const account = readGrokAccount(resolved);
278
+ return {
279
+ accountPath: resolved,
280
+ state: account.state,
281
+ ...account.email === void 0 ? {} : { email: account.email },
282
+ ...account.name === void 0 ? {} : { name: account.name },
283
+ ...account.expiresAt === void 0 ? {} : { expiresAt: account.expiresAt }
284
+ };
285
+ }
286
+ function grokContinuationKey(recipe) {
287
+ return `grok:home:${resolveGrokHomePath(recipe.homePath)}`;
288
+ }
289
+ function resolveAccountHomePath(recipe, configuredHome) {
290
+ const fromEnvironment = recipe.environment["GROK_HOME"]?.trim();
291
+ if (fromEnvironment !== void 0 && fromEnvironment.length > 0) {
292
+ return NodePath2.resolve(expandHomePath(fromEnvironment));
293
+ }
294
+ return resolveGrokHomePath(configuredHome);
295
+ }
296
+ function absentPath() {
297
+ return NodePath2.join(
298
+ NodeOS.tmpdir(),
299
+ `ycodium-grok-no-auth-${NodeCrypto.randomUUID()}`,
300
+ "auth.json"
301
+ );
302
+ }
303
+ function ownEndpointAuthGuard(recipe) {
304
+ const connectivity = GROK_CARD.modelConnectivity;
305
+ const baseUrlKey = connectivity.baseUrlEnvKey;
306
+ if (baseUrlKey === void 0 || (recipe.environment[baseUrlKey]?.trim() ?? "") === "") return {};
307
+ const guard = {};
308
+ for (const entry of connectivity.extraEnv ?? []) {
309
+ if (entry.value === "$absentPath") guard[entry.name] = absentPath();
310
+ }
311
+ return guard;
312
+ }
313
+ function buildSignInEnv(recipe) {
314
+ const configuredHome = recipe.homePath;
315
+ const homeInjection = configuredHome.trim().length > 0 ? { [GROK_CARD.homeEnvVar]: resolveGrokHomePath(configuredHome) } : {};
316
+ return mergeLaunchEnv({
317
+ accountHome: homeInjection,
318
+ instanceTable: recipe.environment,
319
+ handOverrides: { GROK_OAUTH2_REFERRER: "ycodium" }
320
+ });
321
+ }
322
+ function buildSpawnEnv(recipe, resolvedHomePath, homeConfigured) {
323
+ const homeInjection = homeConfigured ? { [GROK_CARD.homeEnvVar]: resolvedHomePath } : {};
324
+ return mergeLaunchEnv({
325
+ accountHome: { ...homeInjection, ...ownEndpointAuthGuard(recipe) },
326
+ instanceTable: recipe.environment,
327
+ handOverrides: { GROK_OAUTH2_REFERRER: "ycodium" }
328
+ });
329
+ }
330
+
164
331
  // src/grokInstance.ts
165
332
  import * as NodeCrypto3 from "node:crypto";
166
333
  import {
@@ -169,7 +336,7 @@ import {
169
336
  import { nodeStreamsFromHandle as nodeStreamsFromHandle2 } from "@ycodium-ai/plugin-api/process-stdin";
170
337
 
171
338
  // ../../packages/ycodium-acp-executor/src/executor.ts
172
- import * as NodeCrypto from "node:crypto";
339
+ import * as NodeCrypto2 from "node:crypto";
173
340
 
174
341
  // ../../packages/ycodium-acp-executor/src/approvals.ts
175
342
  function createApprovalGate(input2) {
@@ -397,7 +564,7 @@ var UnboundedEventStream = class {
397
564
  };
398
565
 
399
566
  // ../../packages/ycodium-acp-executor/src/config-options.ts
400
- function isRecord(value) {
567
+ function isRecord2(value) {
401
568
  return typeof value === "object" && value !== null && !Array.isArray(value);
402
569
  }
403
570
  function asString(value) {
@@ -416,7 +583,7 @@ function flattenChoices(rawOptions, currentValue) {
416
583
  const out = [];
417
584
  const seen = /* @__PURE__ */ new Set();
418
585
  const push = (entry) => {
419
- if (!isRecord(entry)) return;
586
+ if (!isRecord2(entry)) return;
420
587
  const value = asString(entry["value"]);
421
588
  if (value === void 0 || seen.has(value)) return;
422
589
  seen.add(value);
@@ -431,7 +598,7 @@ function flattenChoices(rawOptions, currentValue) {
431
598
  });
432
599
  };
433
600
  for (const entry of rawOptions) {
434
- if (isRecord(entry) && Array.isArray(entry["options"])) {
601
+ if (isRecord2(entry) && Array.isArray(entry["options"])) {
435
602
  for (const nested of entry["options"]) push(nested);
436
603
  continue;
437
604
  }
@@ -440,7 +607,7 @@ function flattenChoices(rawOptions, currentValue) {
440
607
  return out;
441
608
  }
442
609
  function isConfigOption(entry, configId) {
443
- return isRecord(entry) && asString(entry["id"])?.trim() === configId.trim();
610
+ return isRecord2(entry) && asString(entry["id"])?.trim() === configId.trim();
444
611
  }
445
612
  function findConfigOptionEntry(configOptions, configId) {
446
613
  return Array.isArray(configOptions) && configId.trim() !== "" ? configOptions.find(
@@ -455,7 +622,7 @@ function withConfigOptionValue(configOptions, configId, value) {
455
622
  }
456
623
  function pickEffortOption(configOptions) {
457
624
  if (!Array.isArray(configOptions)) return void 0;
458
- const candidates = configOptions.filter(isRecord).filter((option) => normalizedField(option, "type") === "select").filter(looksLikeEffortOption);
625
+ const candidates = configOptions.filter(isRecord2).filter((option) => normalizedField(option, "type") === "select").filter(looksLikeEffortOption);
459
626
  return candidates.find((option) => normalizedField(option, "category") === "model_option") ?? candidates.find((option) => normalizedField(option, "id") === "effort") ?? candidates.find((option) => normalizedField(option, "category") === "thought_level") ?? candidates[0];
460
627
  }
461
628
  function findAcpEffortConfig(configOptions) {
@@ -475,7 +642,7 @@ function findAcpEffortConfig(configOptions) {
475
642
  }
476
643
 
477
644
  // ../../packages/ycodium-acp-executor/src/util.ts
478
- function isRecord2(value) {
645
+ function isRecord3(value) {
479
646
  return typeof value === "object" && value !== null && !Array.isArray(value);
480
647
  }
481
648
  function asTrimmedString(value) {
@@ -530,11 +697,11 @@ function extractCommandFromTitle(title) {
530
697
  return backtickMatch?.[1]?.trim() || void 0;
531
698
  }
532
699
  function extractToolCommand(data, title) {
533
- const item = isRecord2(data?.["item"]) ? data["item"] : void 0;
534
- const itemInput = item && isRecord2(item["input"]) ? item["input"] : void 0;
535
- const itemResult = item && isRecord2(item["result"]) ? item["result"] : void 0;
536
- const rawInput = isRecord2(data?.["rawInput"]) ? data["rawInput"] : void 0;
537
- const dataInput = isRecord2(data?.["input"]) ? data["input"] : void 0;
700
+ const item = isRecord3(data?.["item"]) ? data["item"] : void 0;
701
+ const itemInput = item && isRecord3(item["input"]) ? item["input"] : void 0;
702
+ const itemResult = item && isRecord3(item["result"]) ? item["result"] : void 0;
703
+ const rawInput = isRecord3(data?.["rawInput"]) ? data["rawInput"] : void 0;
704
+ const dataInput = isRecord3(data?.["input"]) ? data["input"] : void 0;
538
705
  const candidates = [
539
706
  normalizeCommandValue(item?.["command"]),
540
707
  normalizeCommandValue(itemInput?.["command"]),
@@ -579,7 +746,7 @@ function collectPaths(value, paths, seen, depth) {
579
746
  }
580
747
  return;
581
748
  }
582
- if (!isRecord2(value)) {
749
+ if (!isRecord3(value)) {
583
750
  return;
584
751
  }
585
752
  for (const key of ["path", "filePath", "relativePath", "filename", "newPath", "oldPath"]) {
@@ -657,7 +824,7 @@ function deriveToolActivityPresentation(input2) {
657
824
  const title = asTrimmedString(input2.title);
658
825
  const detail = stripTrailingExitCode(asTrimmedString(input2.detail));
659
826
  const fallbackSummary = asTrimmedString(input2.fallbackSummary) ?? "Tool";
660
- const data = isRecord2(input2.data) ? input2.data : void 0;
827
+ const data = isRecord3(input2.data) ? input2.data : void 0;
661
828
  const command = extractToolCommand(data, title);
662
829
  const primaryPath = extractPrimaryPath(data);
663
830
  const action = classifyToolAction({
@@ -689,7 +856,7 @@ function deriveToolActivityPresentation(input2) {
689
856
  };
690
857
  }
691
858
  if (action === "search") {
692
- const rawInput = isRecord2(data?.["rawInput"]) ? data["rawInput"] : void 0;
859
+ const rawInput = isRecord3(data?.["rawInput"]) ? data["rawInput"] : void 0;
693
860
  const query = asTrimmedString(rawInput?.["query"]) ?? asTrimmedString(rawInput?.["pattern"]) ?? asTrimmedString(rawInput?.["searchTerm"]);
694
861
  return {
695
862
  summary: "Searched files",
@@ -749,7 +916,7 @@ function extractCommandFromTitle2(title) {
749
916
  return match?.[1]?.trim() || void 0;
750
917
  }
751
918
  function extractToolCallCommand(rawInput, title) {
752
- if (isRecord2(rawInput)) {
919
+ if (isRecord3(rawInput)) {
753
920
  const directCommand = normalizeCommandValue2(rawInput["command"]);
754
921
  if (directCommand) {
755
922
  return directCommand;
@@ -777,7 +944,7 @@ function boundTailText(text4, limit = TOOL_CALL_CONTENT_MAX_CHARS) {
777
944
  var boundToolCallOutputText = (text4) => boundTailText(text4);
778
945
  var RAW_OUTPUT_TEXT_FIELDS = ["content", "stdout", "stderr", "output"];
779
946
  function boundToolCallRawOutput(rawOutput) {
780
- if (!isRecord2(rawOutput)) {
947
+ if (!isRecord3(rawOutput)) {
781
948
  return rawOutput;
782
949
  }
783
950
  let changed = false;
@@ -969,11 +1136,11 @@ function collectSessionConfigOptionValues(configOption) {
969
1136
  );
970
1137
  }
971
1138
  function collectSessionModelIds(models) {
972
- if (!isRecord2(models) || !Array.isArray(models["availableModels"])) {
1139
+ if (!isRecord3(models) || !Array.isArray(models["availableModels"])) {
973
1140
  return [];
974
1141
  }
975
1142
  return models["availableModels"].flatMap(
976
- (model) => isRecord2(model) && typeof model["modelId"] === "string" ? [model["modelId"]] : []
1143
+ (model) => isRecord3(model) && typeof model["modelId"] === "string" ? [model["modelId"]] : []
977
1144
  );
978
1145
  }
979
1146
  function catalogModelLastSegment(value) {
@@ -1045,7 +1212,7 @@ function parseAvailableCommandsUpdate(params) {
1045
1212
  }
1046
1213
  const commands = [];
1047
1214
  for (const command of availableCommands) {
1048
- if (!isRecord2(command)) {
1215
+ if (!isRecord3(command)) {
1049
1216
  continue;
1050
1217
  }
1051
1218
  const name = typeof command["name"] === "string" ? command["name"].trim() : "";
@@ -1053,7 +1220,7 @@ function parseAvailableCommandsUpdate(params) {
1053
1220
  continue;
1054
1221
  }
1055
1222
  const description = typeof command["description"] === "string" ? command["description"].trim() || void 0 : void 0;
1056
- const input2 = isRecord2(command["input"]) ? command["input"] : void 0;
1223
+ const input2 = isRecord3(command["input"]) ? command["input"] : void 0;
1057
1224
  const inputHint = typeof input2?.["hint"] === "string" ? input2["hint"].trim() || void 0 : void 0;
1058
1225
  commands.push({
1059
1226
  name,
@@ -1107,7 +1274,7 @@ function boundToolCallRawPayload(params, update, toolCall) {
1107
1274
  }
1108
1275
  function textFromContentBlocks(blocks) {
1109
1276
  const parts = blocks.flatMap(
1110
- (block) => isRecord2(block) && block["type"] === "text" && typeof block["text"] === "string" ? [block["text"]] : []
1277
+ (block) => isRecord3(block) && block["type"] === "text" && typeof block["text"] === "string" ? [block["text"]] : []
1111
1278
  );
1112
1279
  const joined = parts.join("");
1113
1280
  return joined.trim().length > 0 ? joined : void 0;
@@ -1132,7 +1299,7 @@ function parseSessionUpdateEvent(params) {
1132
1299
  const entries = Array.isArray(upd["entries"]) ? upd["entries"] : [];
1133
1300
  const plan = entries.map((entry, index) => ({
1134
1301
  step: readAcpText(entry, "content") ?? `Step ${index + 1}`,
1135
- status: normalizePlanStepStatus(isRecord2(entry) ? entry["status"] : void 0)
1302
+ status: normalizePlanStepStatus(isRecord3(entry) ? entry["status"] : void 0)
1136
1303
  }));
1137
1304
  if (plan.length > 0) {
1138
1305
  events.push({
@@ -1172,7 +1339,7 @@ function parseSessionUpdateEvent(params) {
1172
1339
  }
1173
1340
  case "agent_message_chunk":
1174
1341
  case "agent_thought_chunk": {
1175
- const content = isRecord2(upd["content"]) ? upd["content"] : void 0;
1342
+ const content = isRecord3(upd["content"]) ? upd["content"] : void 0;
1176
1343
  const contentType = content?.["type"];
1177
1344
  const streamKind = kind === "agent_thought_chunk" ? "reasoning_text" : "assistant_text";
1178
1345
  if (contentType === "text") {
@@ -1212,7 +1379,7 @@ function parseSessionUpdateEvent(params) {
1212
1379
  }
1213
1380
  case "compaction_summary_chunk": {
1214
1381
  const compactionId = readAcpText(upd, "compactionId");
1215
- const content = isRecord2(upd["content"]) ? upd["content"] : void 0;
1382
+ const content = isRecord3(upd["content"]) ? upd["content"] : void 0;
1216
1383
  const text4 = content?.["type"] === "text" ? content["text"] : void 0;
1217
1384
  if (compactionId === void 0 || typeof text4 !== "string" || text4.length === 0) break;
1218
1385
  events.push({ _tag: "CompactionSummaryChunk", compactionId, text: text4 });
@@ -22906,17 +23073,17 @@ function isResponseMessage(value) {
22906
23073
  function isNotificationMessage(value) {
22907
23074
  return isJsonRpcEnvelope(value) && !("id" in value) && typeof value["method"] === "string";
22908
23075
  }
22909
- function isRecord3(value) {
23076
+ function isRecord4(value) {
22910
23077
  return typeof value === "object" && value !== null;
22911
23078
  }
22912
23079
  function isJsonRpcEnvelope(value) {
22913
- return isRecord3(value) && value["jsonrpc"] === "2.0";
23080
+ return isRecord4(value) && value["jsonrpc"] === "2.0";
22914
23081
  }
22915
23082
  function isJsonRpcId(value) {
22916
23083
  return value === null || typeof value === "string" || typeof value === "number" && Number.isFinite(value);
22917
23084
  }
22918
23085
  function isResponseShapedMessage(value) {
22919
- return isRecord3(value) && !("method" in value) && ("id" in value || "result" in value || "error" in value);
23086
+ return isRecord4(value) && !("method" in value) && ("id" in value || "result" in value || "error" in value);
22920
23087
  }
22921
23088
  function isResponseBatch(batch) {
22922
23089
  let hasValidCall = false;
@@ -22926,7 +23093,7 @@ function isResponseBatch(batch) {
22926
23093
  for (const entry of batch) {
22927
23094
  hasValidCall ||= isRequestMessage(entry) || isNotificationMessage(entry);
22928
23095
  hasValidResponse ||= isResponseMessage(entry);
22929
- if (!isRecord3(entry)) {
23096
+ if (!isRecord4(entry)) {
22930
23097
  continue;
22931
23098
  }
22932
23099
  hasCallShape ||= "method" in entry;
@@ -22941,13 +23108,13 @@ function isResponseBatch(batch) {
22941
23108
  return hasResponseShape && !hasCallShape;
22942
23109
  }
22943
23110
  function cancelRequestId(params) {
22944
- if (!isRecord3(params) || !isJsonRpcId(params["requestId"])) {
23111
+ if (!isRecord4(params) || !isJsonRpcId(params["requestId"])) {
22945
23112
  return void 0;
22946
23113
  }
22947
23114
  return params["requestId"];
22948
23115
  }
22949
23116
  function isErrorResponse(value) {
22950
- return isRecord3(value) && typeof value["code"] === "number" && Number.isInteger(value["code"]) && typeof value["message"] === "string";
23117
+ return isRecord4(value) && typeof value["code"] === "number" && Number.isInteger(value["code"]) && typeof value["message"] === "string";
22951
23118
  }
22952
23119
  var Handled = {
22953
23120
  /**
@@ -23492,7 +23659,7 @@ var Connection = class {
23492
23659
  if (this.abortController.signal.aborted) {
23493
23660
  return Promise.resolve();
23494
23661
  }
23495
- if (!isRecord3(message)) {
23662
+ if (!isRecord4(message)) {
23496
23663
  console.error("Invalid message", { message });
23497
23664
  return Promise.resolve();
23498
23665
  }
@@ -23895,7 +24062,7 @@ function ndJsonStream(output2, input2) {
23895
24062
  await writeJson(protocolErrorResponse(RequestError.parseError()));
23896
24063
  return;
23897
24064
  }
23898
- if (isRecord3(message) || Array.isArray(message)) {
24065
+ if (isRecord4(message) || Array.isArray(message)) {
23899
24066
  controller.enqueue(message);
23900
24067
  } else {
23901
24068
  await writeJson(protocolErrorResponse(RequestError.invalidRequest(message)));
@@ -24882,14 +25049,14 @@ var legacyClientNotificationMethods = /* @__PURE__ */ new Set([
24882
25049
  import * as NodeStream from "node:stream";
24883
25050
 
24884
25051
  // ../../packages/ycodium-acp-executor/src/jsonrpc.ts
24885
- function isRecord4(value) {
25052
+ function isRecord5(value) {
24886
25053
  return typeof value === "object" && value !== null;
24887
25054
  }
24888
25055
  function isJsonRpcId2(value) {
24889
25056
  return typeof value === "string" || typeof value === "number" && Number.isFinite(value);
24890
25057
  }
24891
25058
  function isJsonRpcEnvelope2(value) {
24892
- return isRecord4(value) && value["jsonrpc"] === "2.0";
25059
+ return isRecord5(value) && value["jsonrpc"] === "2.0";
24893
25060
  }
24894
25061
  function isRequestMessage2(value) {
24895
25062
  return isJsonRpcEnvelope2(value) && "id" in value && typeof value["method"] === "string" && isJsonRpcId2(value["id"]);
@@ -24909,7 +25076,7 @@ function isNotificationMessage2(value) {
24909
25076
  return isJsonRpcEnvelope2(value) && !("id" in value) && typeof value["method"] === "string";
24910
25077
  }
24911
25078
  function readJsonRpcError(value) {
24912
- if (!isRecord4(value) || typeof value["code"] !== "number" || typeof value["message"] !== "string") {
25079
+ if (!isRecord5(value) || typeof value["code"] !== "number" || typeof value["message"] !== "string") {
24913
25080
  return void 0;
24914
25081
  }
24915
25082
  return {
@@ -24982,11 +25149,11 @@ var KNOWN_SESSION_UPDATES = /* @__PURE__ */ new Set([
24982
25149
  "compaction_summary_chunk"
24983
25150
  ]);
24984
25151
  function isSessionUpdateEnvelope(payload) {
24985
- if (!isRecord4(payload) || typeof payload["sessionId"] !== "string") {
25152
+ if (!isRecord5(payload) || typeof payload["sessionId"] !== "string") {
24986
25153
  return false;
24987
25154
  }
24988
25155
  const update = payload["update"];
24989
- return isRecord4(update) && typeof update["sessionUpdate"] === "string";
25156
+ return isRecord5(update) && typeof update["sessionUpdate"] === "string";
24990
25157
  }
24991
25158
  function hasKnownVariantShape(update) {
24992
25159
  const kind = update["sessionUpdate"];
@@ -24994,7 +25161,7 @@ function hasKnownVariantShape(update) {
24994
25161
  return false;
24995
25162
  }
24996
25163
  if (kind === "user_message_chunk" || kind === "agent_message_chunk" || kind === "agent_thought_chunk") {
24997
- return isRecord4(update["content"]);
25164
+ return isRecord5(update["content"]);
24998
25165
  }
24999
25166
  return true;
25000
25167
  }
@@ -25400,7 +25567,7 @@ function forgetConfigOptionValue(state, configId) {
25400
25567
  const forgetsModel = configId === void 0 || configId === state.modelConfigId;
25401
25568
  const { currentModelReported: _forgottenReport, ...withoutReport } = state;
25402
25569
  const configOptions = configId !== void 0 && Array.isArray(state.configOptions) ? state.configOptions.map((option) => {
25403
- if (!isRecord2(option) || option["id"] !== configId) return option;
25570
+ if (!isRecord3(option) || option["id"] !== configId) return option;
25404
25571
  const { currentValue: _forgotten, ...rest } = option;
25405
25572
  return rest;
25406
25573
  }) : state.configOptions;
@@ -25411,7 +25578,7 @@ function forgetConfigOptionValue(state, configId) {
25411
25578
  };
25412
25579
  }
25413
25580
  function modelSelectionStateFromSetup(setup) {
25414
- const sessionModels = isRecord2(setup.models) ? setup.models : void 0;
25581
+ const sessionModels = isRecord3(setup.models) ? setup.models : void 0;
25415
25582
  const modelConfigId = extractModelConfigId(setup);
25416
25583
  const strategy = modelConfigId !== void 0 ? "config-option" : "session/set_model";
25417
25584
  const modelConfigOption = modelConfigId === void 0 ? void 0 : findSessionConfigOption(setup.configOptions, modelConfigId);
@@ -26779,7 +26946,7 @@ function createExecutorRequests({
26779
26946
  pendingUserInputs.delete(requestId);
26780
26947
  pending.resolve({
26781
26948
  kind: "answered",
26782
- answers: isRecord2(answers) ? answers : {}
26949
+ answers: isRecord3(answers) ? answers : {}
26783
26950
  });
26784
26951
  };
26785
26952
  return {
@@ -26793,7 +26960,7 @@ function createExecutorRequests({
26793
26960
  }
26794
26961
 
26795
26962
  // ../../packages/ycodium-acp-executor/src/artifacts.ts
26796
- import * as NodePath from "node:path";
26963
+ import * as NodePath3 from "node:path";
26797
26964
  import * as NodeURL from "node:url";
26798
26965
  function resourceLinkArtifacts(content) {
26799
26966
  if (content["type"] !== "resource_link" || typeof content["uri"] !== "string") return [];
@@ -26804,7 +26971,7 @@ function resourceLinkArtifacts(content) {
26804
26971
  } catch {
26805
26972
  return [];
26806
26973
  }
26807
- if (!NodePath.isAbsolute(path)) return [];
26974
+ if (!NodePath3.isAbsolute(path)) return [];
26808
26975
  const name = typeof content["name"] === "string" ? content["name"].trim() : "";
26809
26976
  const mimeType = typeof content["mimeType"] === "string" ? content["mimeType"].trim() : "";
26810
26977
  return [
@@ -27078,18 +27245,18 @@ function modelRefreshFailedError(requestedModelId, error62) {
27078
27245
  }
27079
27246
 
27080
27247
  // ../../packages/ycodium-acp-executor/src/runtime-mode.ts
27081
- function isRecord5(value) {
27248
+ function isRecord6(value) {
27082
27249
  return typeof value === "object" && value !== null && !Array.isArray(value);
27083
27250
  }
27084
27251
  function asString2(value) {
27085
27252
  return typeof value === "string" ? value : void 0;
27086
27253
  }
27087
27254
  function acpModeConfigId(setup) {
27088
- if (!isRecord5(setup)) return void 0;
27255
+ if (!isRecord6(setup)) return void 0;
27089
27256
  const configOptions = setup["configOptions"];
27090
27257
  if (!Array.isArray(configOptions)) return void 0;
27091
27258
  for (const option of configOptions) {
27092
- if (!isRecord5(option)) continue;
27259
+ if (!isRecord6(option)) continue;
27093
27260
  if (asString2(option["category"])?.trim().toLowerCase() !== "mode") continue;
27094
27261
  const id = asString2(option["id"])?.trim();
27095
27262
  if (id) return id;
@@ -27536,14 +27703,14 @@ function createExecutorSettlement({
27536
27703
 
27537
27704
  // ../../packages/ycodium-acp-executor/src/executor/subagent-sessions.ts
27538
27705
  function parseState(raw) {
27539
- if (!isRecord2(raw)) return { status: void 0, error: void 0 };
27706
+ if (!isRecord3(raw)) return { status: void 0, error: void 0 };
27540
27707
  const state = raw["state"];
27541
27708
  if (state === "running") return { status: "running", error: void 0 };
27542
27709
  if (state === "requires_action") return { status: "waiting", error: void 0 };
27543
27710
  if (state !== "idle") return { status: void 0, error: void 0 };
27544
27711
  const stopReason = raw["stopReason"];
27545
- const error62 = isRecord2(raw["error"]) ? asTrimmedString(raw["error"]["message"]) : void 0;
27546
- if (stopReason === "error" || isRecord2(raw["error"])) return { status: "failed", error: error62 };
27712
+ const error62 = isRecord3(raw["error"]) ? asTrimmedString(raw["error"]["message"]) : void 0;
27713
+ if (stopReason === "error" || isRecord3(raw["error"])) return { status: "failed", error: error62 };
27547
27714
  if (stopReason === "cancelled") return { status: "cancelled", error: void 0 };
27548
27715
  return { status: "idle", error: void 0 };
27549
27716
  }
@@ -27559,7 +27726,7 @@ function childLinkage(childId, record4) {
27559
27726
  function createSubagentSessionTracker(registry2) {
27560
27727
  const observe = (notification, ctx) => {
27561
27728
  const update = notification.update;
27562
- if (!isRecord2(update) || update["sessionUpdate"] !== "subagent_update") return [];
27729
+ if (!isRecord3(update) || update["sessionUpdate"] !== "subagent_update") return [];
27563
27730
  const childId = asTrimmedString(update["sessionId"]);
27564
27731
  if (childId === void 0 || ctx.ownSessionIds.has(childId)) return [];
27565
27732
  const outerId = notification.sessionId;
@@ -27984,9 +28151,9 @@ function readOpenToolCalls(toolCalls, turnId) {
27984
28151
  }
27985
28152
 
27986
28153
  // ../../packages/ycodium-acp-executor/src/protocol-log.ts
27987
- import * as NodeFS from "node:fs";
27988
- import * as NodeOS from "node:os";
27989
- import * as NodePath2 from "node:path";
28154
+ import * as NodeFS2 from "node:fs";
28155
+ import * as NodeOS2 from "node:os";
28156
+ import * as NodePath4 from "node:path";
27990
28157
  var DEFAULT_MAX_BYTES = 8 * 1024 * 1024;
27991
28158
  var UNSAFE_FILE_CHARS = /[^A-Za-z0-9._-]+/gu;
27992
28159
  function safeFileName(value) {
@@ -27994,12 +28161,12 @@ function safeFileName(value) {
27994
28161
  }
27995
28162
  function createAcpProtocolLogFile(input2) {
27996
28163
  const maxBytes = input2.maxBytes ?? DEFAULT_MAX_BYTES;
27997
- NodeFS.mkdirSync(input2.directory, { recursive: true });
27998
- const filePath = NodePath2.join(
28164
+ NodeFS2.mkdirSync(input2.directory, { recursive: true });
28165
+ const filePath = NodePath4.join(
27999
28166
  input2.directory,
28000
28167
  `${safeFileName(input2.provider)}-${safeFileName(input2.threadId)}-${Date.now().toString(36)}.ndjson`
28001
28168
  );
28002
- const stream = (input2.openStream ?? ((path) => NodeFS.createWriteStream(path, { flags: "a" })))(
28169
+ const stream = (input2.openStream ?? ((path) => NodeFS2.createWriteStream(path, { flags: "a" })))(
28003
28170
  filePath
28004
28171
  );
28005
28172
  let bytes = 0;
@@ -28051,7 +28218,7 @@ function createAcpProtocolLogFromEnv(input2) {
28051
28218
  const flag = process.env.YCODIUM_ACP_PROTOCOL_LOG;
28052
28219
  if (flag !== "1" && flag?.toLowerCase() !== "true") return void 0;
28053
28220
  const configured = process.env.YCODIUM_ACP_PROTOCOL_LOG_DIR?.trim();
28054
- const directory = configured && configured.length > 0 ? configured : NodePath2.join(NodeOS.tmpdir(), "ycodium-acp-protocol");
28221
+ const directory = configured && configured.length > 0 ? configured : NodePath4.join(NodeOS2.tmpdir(), "ycodium-acp-protocol");
28055
28222
  try {
28056
28223
  const sink = createAcpProtocolLogFile({
28057
28224
  directory,
@@ -28075,7 +28242,7 @@ function defaultNowIso() {
28075
28242
  return (/* @__PURE__ */ new Date()).toISOString();
28076
28243
  }
28077
28244
  function defaultRandomId() {
28078
- return NodeCrypto.randomUUID();
28245
+ return NodeCrypto2.randomUUID();
28079
28246
  }
28080
28247
  function createAcpExecutor(options) {
28081
28248
  const hooks = { ...defaultVendorHooks, ...options.hooks };
@@ -28671,28 +28838,28 @@ var MAX_VERSION_OUTPUT_BYTES = 64 * 1024;
28671
28838
 
28672
28839
  // src/config.ts
28673
28840
  var GROK_DEFAULT_LAUNCH_ARGS = "agent stdio";
28674
- function isRecord6(value) {
28841
+ function isRecord7(value) {
28675
28842
  return typeof value === "object" && value !== null && !Array.isArray(value);
28676
28843
  }
28677
- function readString(value) {
28844
+ function readString2(value) {
28678
28845
  return typeof value === "string" ? value : "";
28679
28846
  }
28680
28847
  function readTrimmed(value) {
28681
- return readString(value).trim();
28848
+ return readString2(value).trim();
28682
28849
  }
28683
28850
  function readEnvironment(value) {
28684
28851
  if (!Array.isArray(value)) return {};
28685
28852
  const env = {};
28686
28853
  for (const entry of value) {
28687
- if (!isRecord6(entry)) continue;
28854
+ if (!isRecord7(entry)) continue;
28688
28855
  const name = readTrimmed(entry["name"]);
28689
28856
  if (name.length === 0) continue;
28690
- env[name] = readString(entry["value"]);
28857
+ env[name] = readString2(entry["value"]);
28691
28858
  }
28692
28859
  return env;
28693
28860
  }
28694
28861
  function parseGrokInstanceConfig(config2) {
28695
- if (!isRecord6(config2)) {
28862
+ if (!isRecord7(config2)) {
28696
28863
  return {
28697
28864
  binaryPath: "",
28698
28865
  launchArgs: GROK_DEFAULT_LAUNCH_ARGS,
@@ -28702,11 +28869,11 @@ function parseGrokInstanceConfig(config2) {
28702
28869
  envelopeDriver: void 0
28703
28870
  };
28704
28871
  }
28705
- const nested = isRecord6(config2["config"]) ? config2["config"] : void 0;
28872
+ const nested = isRecord7(config2["config"]) ? config2["config"] : void 0;
28706
28873
  const settings = nested ?? config2;
28707
28874
  return {
28708
28875
  binaryPath: readTrimmed(settings["binaryPath"]),
28709
- launchArgs: readString(settings["launchArgs"]) || GROK_DEFAULT_LAUNCH_ARGS,
28876
+ launchArgs: readString2(settings["launchArgs"]) || GROK_DEFAULT_LAUNCH_ARGS,
28710
28877
  homePath: readTrimmed(settings["homePath"]) || readTrimmed(config2["homePath"]),
28711
28878
  homeEnvVar: readTrimmed(settings["homeEnvVar"]) || readTrimmed(config2["homeEnvVar"]),
28712
28879
  environment: readEnvironment(config2["environment"]),
@@ -28830,166 +28997,6 @@ function readDeviceCodeOutput(stdout, onText) {
28830
28997
  return { text: text4, code };
28831
28998
  }
28832
28999
 
28833
- // src/grokHome.ts
28834
- import * as NodeCrypto2 from "node:crypto";
28835
- import * as NodeOS2 from "node:os";
28836
- import * as NodePath4 from "node:path";
28837
- import { mergeLaunchEnv } from "@ycodium-ai/plugin-api/executor";
28838
-
28839
- // src/grokAccount.ts
28840
- import * as NodeFS2 from "node:fs";
28841
- import * as NodePath3 from "node:path";
28842
- var AUTH_FILE_NAME = "auth.json";
28843
- var isRecord7 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
28844
- var readString2 = (record4, key) => {
28845
- const value = record4[key];
28846
- if (typeof value !== "string") return void 0;
28847
- const trimmed3 = value.trim();
28848
- return trimmed3.length > 0 ? trimmed3 : void 0;
28849
- };
28850
- var parseJsonRecord = (raw) => {
28851
- let parsed;
28852
- try {
28853
- parsed = JSON.parse(raw);
28854
- } catch {
28855
- return void 0;
28856
- }
28857
- return isRecord7(parsed) ? parsed : void 0;
28858
- };
28859
- var directoryExists = (homePath) => {
28860
- try {
28861
- return NodeFS2.statSync(homePath).isDirectory();
28862
- } catch {
28863
- return false;
28864
- }
28865
- };
28866
- var readCredentialFile = (path) => {
28867
- try {
28868
- return NodeFS2.readFileSync(path, "utf8");
28869
- } catch {
28870
- return void 0;
28871
- }
28872
- };
28873
- var signedOutProbe = (exists) => ({
28874
- state: "signed-out",
28875
- directoryExists: exists,
28876
- email: void 0,
28877
- name: void 0,
28878
- expiresAt: void 0
28879
- });
28880
- var unreadableProbe = (exists) => ({
28881
- state: "unreadable",
28882
- directoryExists: exists,
28883
- email: void 0,
28884
- name: void 0,
28885
- expiresAt: void 0
28886
- });
28887
- var joinName = (record4) => {
28888
- const first = readString2(record4, "first_name");
28889
- const last = readString2(record4, "last_name");
28890
- const joined = [first, last].filter((part) => part !== void 0).join(" ");
28891
- return joined.length > 0 ? joined : void 0;
28892
- };
28893
- var findIdentityEntry = (parsed) => {
28894
- for (const value of Object.values(parsed)) {
28895
- if (!isRecord7(value)) continue;
28896
- if (readString2(value, "email") !== void 0 || readString2(value, "user_id") !== void 0) {
28897
- return value;
28898
- }
28899
- }
28900
- return void 0;
28901
- };
28902
- var isExpired = (expiresAt, nowMs) => {
28903
- if (expiresAt === void 0) return false;
28904
- const parsed = Date.parse(expiresAt);
28905
- return Number.isNaN(parsed) ? false : parsed <= nowMs;
28906
- };
28907
- var readGrokAccount = (homePath) => {
28908
- const exists = directoryExists(homePath);
28909
- const raw = readCredentialFile(NodePath3.join(homePath, AUTH_FILE_NAME));
28910
- if (raw === void 0) return signedOutProbe(exists);
28911
- const parsed = parseJsonRecord(raw);
28912
- const identity = parsed === void 0 ? void 0 : findIdentityEntry(parsed);
28913
- if (identity === void 0) return unreadableProbe(exists);
28914
- const expiresAt = readString2(identity, "expires_at");
28915
- return {
28916
- state: isExpired(expiresAt, Date.now()) ? "expired" : "signed-in",
28917
- directoryExists: exists,
28918
- email: readString2(identity, "email"),
28919
- name: joinName(identity),
28920
- expiresAt
28921
- };
28922
- };
28923
-
28924
- // src/grokHome.ts
28925
- function expandHomePath(value) {
28926
- if (value === "~") return NodeOS2.homedir();
28927
- if (value.startsWith("~/") || value.startsWith("~\\")) {
28928
- return NodePath4.join(NodeOS2.homedir(), value.slice(2));
28929
- }
28930
- return value;
28931
- }
28932
- function resolveGrokHomePath(value) {
28933
- const trimmed3 = value.trim();
28934
- if (trimmed3.length === 0) return NodePath4.join(NodeOS2.homedir(), ".grok");
28935
- return NodePath4.resolve(expandHomePath(trimmed3));
28936
- }
28937
- function probeGrokAccount(accountPath) {
28938
- const resolved = resolveGrokHomePath(accountPath);
28939
- const account = readGrokAccount(resolved);
28940
- return {
28941
- accountPath: resolved,
28942
- state: account.state,
28943
- ...account.email === void 0 ? {} : { email: account.email },
28944
- ...account.name === void 0 ? {} : { name: account.name },
28945
- ...account.expiresAt === void 0 ? {} : { expiresAt: account.expiresAt }
28946
- };
28947
- }
28948
- function grokContinuationKey(recipe) {
28949
- return `grok:home:${resolveGrokHomePath(recipe.homePath)}`;
28950
- }
28951
- function resolveAccountHomePath(recipe, configuredHome) {
28952
- const fromEnvironment = recipe.environment["GROK_HOME"]?.trim();
28953
- if (fromEnvironment !== void 0 && fromEnvironment.length > 0) {
28954
- return NodePath4.resolve(expandHomePath(fromEnvironment));
28955
- }
28956
- return resolveGrokHomePath(configuredHome);
28957
- }
28958
- function absentPath() {
28959
- return NodePath4.join(
28960
- NodeOS2.tmpdir(),
28961
- `ycodium-grok-no-auth-${NodeCrypto2.randomUUID()}`,
28962
- "auth.json"
28963
- );
28964
- }
28965
- function ownEndpointAuthGuard(recipe) {
28966
- const connectivity = GROK_CARD.modelConnectivity;
28967
- const baseUrlKey = connectivity.baseUrlEnvKey;
28968
- if (baseUrlKey === void 0 || (recipe.environment[baseUrlKey]?.trim() ?? "") === "") return {};
28969
- const guard = {};
28970
- for (const entry of connectivity.extraEnv ?? []) {
28971
- if (entry.value === "$absentPath") guard[entry.name] = absentPath();
28972
- }
28973
- return guard;
28974
- }
28975
- function buildSignInEnv(recipe) {
28976
- const configuredHome = recipe.homePath;
28977
- const homeInjection = configuredHome.trim().length > 0 ? { [GROK_CARD.homeEnvVar]: resolveGrokHomePath(configuredHome) } : {};
28978
- return mergeLaunchEnv({
28979
- accountHome: homeInjection,
28980
- instanceTable: recipe.environment,
28981
- handOverrides: { GROK_OAUTH2_REFERRER: "ycodium" }
28982
- });
28983
- }
28984
- function buildSpawnEnv(recipe, resolvedHomePath, homeConfigured) {
28985
- const homeInjection = homeConfigured ? { [GROK_CARD.homeEnvVar]: resolvedHomePath } : {};
28986
- return mergeLaunchEnv({
28987
- accountHome: { ...homeInjection, ...ownEndpointAuthGuard(recipe) },
28988
- instanceTable: recipe.environment,
28989
- handOverrides: { GROK_OAUTH2_REFERRER: "ycodium" }
28990
- });
28991
- }
28992
-
28993
29000
  // src/grokSignIn.ts
28994
29001
  function makeGrokSignIn(input2) {
28995
29002
  const { host, recipe } = input2;
@@ -30961,6 +30968,7 @@ var createGrokInstance = (config2, host) => {
30961
30968
  var executors = [
30962
30969
  {
30963
30970
  card: GROK_CARD,
30971
+ lifecycle: [{ kind: "detect", reversible: true }],
30964
30972
  createInstance: async (config2, host) => {
30965
30973
  host.log.info("createInstance");
30966
30974
  return createGrokInstance(config2, host);
@@ -30970,9 +30978,26 @@ var executors = [
30970
30978
  ];
30971
30979
  var index_default = definePlugin({
30972
30980
  id: "agent-grok",
30973
- apiVersion: 53,
30981
+ apiVersion: 56,
30974
30982
  executors,
30975
30983
  activate(ctx) {
30984
+ ctx.command({
30985
+ name: "effectiveConfig",
30986
+ description: "Settings form: the account directory an empty path resolves to on this machine.",
30987
+ scope: "read",
30988
+ payloadSchema: EXECUTOR_EFFECTIVE_PAYLOAD_SCHEMA,
30989
+ handle: async (payload) => {
30990
+ const request = readExecutorEffectiveRequest(payload);
30991
+ return {
30992
+ homePath: executorHomeEffectiveValue(
30993
+ "GROK_HOME",
30994
+ request,
30995
+ process.env,
30996
+ (value) => resolveGrokHomePath(value ?? "")
30997
+ )
30998
+ };
30999
+ }
31000
+ });
30976
31001
  ctx.log.info("grok executor ready");
30977
31002
  }
30978
31003
  });
package/locales/en.json CHANGED
@@ -1,4 +1,8 @@
1
1
  {
2
+ "presentation": {
3
+ "name": "Grok",
4
+ "description": "Use Grok in Ycodium and pick up earlier sessions where they left off."
5
+ },
2
6
  "form": {
3
7
  "binaryPath": {
4
8
  "title": "Program path",
@@ -9,6 +13,11 @@
9
13
  "title": "Account directory",
10
14
  "description": "Sessions and the `grok login` credentials live here, so providers sharing this directory can pick up each other's threads and providers with different directories cannot. Leave empty to use the CLI's default (~/.grok).",
11
15
  "placeholder": "e.g. ~/.grok-work"
16
+ },
17
+ "effective": {
18
+ "provider": "{{name}} in this provider",
19
+ "machine": "{{name}} on this machine",
20
+ "default": "default"
12
21
  }
13
22
  },
14
23
  "usage": {
@@ -1,4 +1,8 @@
1
1
  {
2
+ "presentation": {
3
+ "name": "Grok",
4
+ "description": "在 Ycodium 里使用 Grok,之前的会话可以接着做。"
5
+ },
2
6
  "form": {
3
7
  "binaryPath": {
4
8
  "title": "程序路径",
@@ -9,6 +13,11 @@
9
13
  "title": "账号目录",
10
14
  "description": "会话和 `grok login` 的凭据都保存在这里,因此共用同一目录的 provider 可以接手彼此的会话,目录不同的则不能。留空则使用该命令行工具的默认目录(~/.grok)。",
11
15
  "placeholder": "例如 ~/.grok-work"
16
+ },
17
+ "effective": {
18
+ "provider": "本 provider 的 {{name}}",
19
+ "machine": "本机的 {{name}}",
20
+ "default": "默认"
12
21
  }
13
22
  },
14
23
  "usage": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ycodium-ai/agent-grok",
3
- "version": "0.2.3710",
3
+ "version": "0.2.3766",
4
4
  "description": "Grok executor: grok CLI over Agent Client Protocol stdio.",
5
5
  "keywords": [
6
6
  "ycodium",
@@ -21,14 +21,16 @@
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.3710",
25
- "ycodium-acp-executor": "0.2.3710",
24
+ "@ycodium-ai/plugin-api": "0.2.3766",
25
+ "ycodium-acp-executor": "0.2.3766",
26
26
  "ycodium-executor-testing": "0.0.0"
27
27
  },
28
28
  "ycodium": {
29
29
  "plugin": "./dist/index.js",
30
30
  "displayName": "Grok",
31
- "description": "Run Grok (grok agent stdio) with its session/resume continuation, reasoning-effort spawn tiers, and device-code sign-in.",
31
+ "displayNameKey": "presentation.name",
32
+ "description": "Use Grok in Ycodium and pick up earlier sessions where they left off.",
33
+ "descriptionKey": "presentation.description",
32
34
  "icon": "./assets/icon.svg",
33
35
  "permissions": [
34
36
  {