@ycodium-ai/agent-grok 0.2.2634 → 0.2.2664

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 +660 -152
  2. package/package.json +3 -3
package/dist/index.js CHANGED
@@ -70,7 +70,10 @@ var GROK_CARD = {
70
70
  // 档位走 spawn 旗标,切档要重开会话(与 runtimeModeSwitch 同为 next-turn 的原因)。
71
71
  // 声明这一位让界面画这双手自报的三档;不声明就退回宿主四档,而四档在这只手上
72
72
  // 有两档撞车(都落到 `auto`)。
73
- nativeModeSwitch: true
73
+ nativeModeSwitch: true,
74
+ // 压缩走 CLI 宣告过的 `/compact` 命令(见 `compactContext`),进度与结果由执行器按
75
+ // 压缩生命周期事件报;CLI 没宣告这条命令时 `compactContext` 拒绝,不退回发文本。
76
+ contextCompaction: "native"
74
77
  },
75
78
  // 凭据由 CLI 自己保管在 `<GROK_HOME>/auth.json`,宿主读它只认 `/token`。
76
79
  credentialSources: [
@@ -152,7 +155,7 @@ var GROK_CARD = {
152
155
 
153
156
  // src/grokInstance.ts
154
157
  import * as NodeCrypto2 from "node:crypto";
155
- import * as NodeOS from "node:os";
158
+ import * as NodeOS2 from "node:os";
156
159
  import * as NodePath2 from "node:path";
157
160
  import { mergeLaunchEnv } from "@ycodium-ai/plugin-api/executor";
158
161
  import { nodeStreamsFromHandle as nodeStreamsFromHandle2 } from "@ycodium-ai/plugin-api/process-stdin";
@@ -3000,23 +3003,23 @@ function canParseURL(input2) {
3000
3003
  return false;
3001
3004
  }
3002
3005
  }
3003
- function validateURL(trimmed2, def) {
3006
+ function validateURL(trimmed3, def) {
3004
3007
  if (!("normalize" in def) && !("hostname" in def) && !("protocol" in def)) {
3005
- return canParseURL(trimmed2) || URL_UNPARSEABLE;
3008
+ return canParseURL(trimmed3) || URL_UNPARSEABLE;
3006
3009
  }
3007
- return parseURLObject(trimmed2, def);
3010
+ return parseURLObject(trimmed3, def);
3008
3011
  }
3009
- function parseURLObject(trimmed2, def) {
3010
- if (!def.normalize && def.protocol?.source === httpProtocol.source && !/^https?:\/\//i.test(trimmed2)) {
3012
+ function parseURLObject(trimmed3, def) {
3013
+ if (!def.normalize && def.protocol?.source === httpProtocol.source && !/^https?:\/\//i.test(trimmed3)) {
3011
3014
  return URL_BAD_FORMAT;
3012
3015
  }
3013
3016
  try {
3014
3017
  if (typeof URL !== "undefined") {
3015
3018
  const URLStatic = URL;
3016
3019
  if (typeof URLStatic.parse === "function")
3017
- return URLStatic.parse(trimmed2) ?? URL_UNPARSEABLE;
3020
+ return URLStatic.parse(trimmed3) ?? URL_UNPARSEABLE;
3018
3021
  }
3019
- return new URL(trimmed2);
3022
+ return new URL(trimmed3);
3020
3023
  } catch {
3021
3024
  return URL_UNPARSEABLE;
3022
3025
  }
@@ -3037,8 +3040,8 @@ var $ZodURL = /* @__PURE__ */ $constructor("$ZodURL", (inst, def) => {
3037
3040
  $ZodStringFormat.init(inst, def);
3038
3041
  inst._zod.check = (payload) => {
3039
3042
  try {
3040
- const trimmed2 = payload.value.trim();
3041
- const url2 = validateURL(trimmed2, def);
3043
+ const trimmed3 = payload.value.trim();
3044
+ const url2 = validateURL(trimmed3, def);
3042
3045
  if (url2 === URL_BAD_FORMAT) {
3043
3046
  payload.issues.push({
3044
3047
  code: "invalid_format",
@@ -3061,7 +3064,7 @@ var $ZodURL = /* @__PURE__ */ $constructor("$ZodURL", (inst, def) => {
3061
3064
  return;
3062
3065
  }
3063
3066
  if (url2 === true) {
3064
- payload.value = stripTabAndNewline(trimmed2);
3067
+ payload.value = stripTabAndNewline(trimmed3);
3065
3068
  return;
3066
3069
  }
3067
3070
  if (def.hostname && !urlHostnameOk(url2, def.hostname)) {
@@ -3086,7 +3089,7 @@ var $ZodURL = /* @__PURE__ */ $constructor("$ZodURL", (inst, def) => {
3086
3089
  continue: !def.abort
3087
3090
  });
3088
3091
  }
3089
- payload.value = def.normalize ? url2.href : stripTabAndNewline(trimmed2);
3092
+ payload.value = def.normalize ? url2.href : stripTabAndNewline(trimmed3);
3090
3093
  return;
3091
3094
  } catch (_) {
3092
3095
  payload.issues.push({
@@ -9521,8 +9524,8 @@ function ko_default() {
9521
9524
  }
9522
9525
 
9523
9526
  // ../../node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/locales/lt.js
9524
- var capitalizeFirstCharacter = (text) => {
9525
- return text.charAt(0).toUpperCase() + text.slice(1);
9527
+ var capitalizeFirstCharacter = (text2) => {
9528
+ return text2.charAt(0).toUpperCase() + text2.slice(1);
9526
9529
  };
9527
9530
  function getUnitTypeFromNumber(number4) {
9528
9531
  const abs = Math.abs(number4);
@@ -16816,8 +16819,8 @@ function rewriteKeyNames(ctx) {
16816
16819
  bySchema.set(entry.schema, entry);
16817
16820
  }
16818
16821
  const rewrites = /* @__PURE__ */ new Map();
16819
- for (const record2 of pendingRecords.get(ctx) ?? []) {
16820
- const seen = ctx.seen.get(record2);
16822
+ for (const record3 of pendingRecords.get(ctx) ?? []) {
16823
+ const seen = ctx.seen.get(record3);
16821
16824
  const names = (seen?.def ?? seen?.schema)?.propertyNames;
16822
16825
  if (!names || names === true || rewrites.has(names))
16823
16826
  continue;
@@ -23584,11 +23587,21 @@ var AcpExecutorRequestError = class extends AcpExecutorError {
23584
23587
  }
23585
23588
  };
23586
23589
  var AcpConnectionClosedError = class extends Error {
23587
- constructor(message = "ACP connection closed") {
23588
- super(message);
23590
+ constructor(message = "ACP connection closed", options) {
23591
+ super(message, options);
23589
23592
  this.name = "AcpConnectionClosedError";
23590
23593
  }
23591
23594
  };
23595
+ async function withStartupStderr(error62, stderrTail) {
23596
+ if (!(error62 instanceof AcpConnectionClosedError) || stderrTail === void 0) return error62;
23597
+ const tail = await stderrTail();
23598
+ if (tail.length === 0) return error62;
23599
+ return new AcpConnectionClosedError(`${error62.message}
23600
+ Agent stderr (last lines):
23601
+ ${tail}`, {
23602
+ cause: error62
23603
+ });
23604
+ }
23592
23605
 
23593
23606
  // ../../packages/ycodium-acp-executor/src/jsonrpc.ts
23594
23607
  function isRecord2(value) {
@@ -23730,12 +23743,12 @@ function asBytes(chunk) {
23730
23743
  throw new TypeError("ACP readable produced a non-byte chunk");
23731
23744
  }
23732
23745
  function isEnqueueableLine(lineBytes) {
23733
- const trimmed2 = textDecoder.decode(lineBytes).trim();
23734
- if (!trimmed2) {
23746
+ const trimmed3 = textDecoder.decode(lineBytes).trim();
23747
+ if (!trimmed3) {
23735
23748
  return false;
23736
23749
  }
23737
23750
  try {
23738
- const parsed = JSON.parse(trimmed2);
23751
+ const parsed = JSON.parse(trimmed3);
23739
23752
  return typeof parsed === "object" && parsed !== null;
23740
23753
  } catch {
23741
23754
  return false;
@@ -23933,9 +23946,9 @@ function createAcpConnection(options) {
23933
23946
  const handleChunk = async (bytes) => {
23934
23947
  const lines = lineBuffer.push(bytes);
23935
23948
  for (const line of lines) {
23936
- const trimmed2 = textDecoder.decode(line).trim();
23937
- if (trimmed2) {
23938
- log({ direction: "incoming", stage: "raw", payload: trimmed2 });
23949
+ const trimmed3 = textDecoder.decode(line).trim();
23950
+ if (trimmed3) {
23951
+ log({ direction: "incoming", stage: "raw", payload: trimmed3 });
23939
23952
  }
23940
23953
  }
23941
23954
  const expected = lines.filter(isEnqueueableLine).length;
@@ -23972,9 +23985,9 @@ function createAcpConnection(options) {
23972
23985
  enqueueInbound(async () => {
23973
23986
  const last = lineBuffer.flush();
23974
23987
  if (last) {
23975
- const trimmed2 = textDecoder.decode(last).trim();
23976
- if (trimmed2) {
23977
- log({ direction: "incoming", stage: "raw", payload: trimmed2 });
23988
+ const trimmed3 = textDecoder.decode(last).trim();
23989
+ if (trimmed3) {
23990
+ log({ direction: "incoming", stage: "raw", payload: trimmed3 });
23978
23991
  }
23979
23992
  }
23980
23993
  const expected = last && isEnqueueableLine(last) ? 1 : 0;
@@ -24087,10 +24100,10 @@ function selectAutoApprovedAcpPermissionOption(request) {
24087
24100
  }
24088
24101
  function parseAcpResumeCursor(raw, schemaVersion) {
24089
24102
  if (typeof raw !== "object" || raw === null || Array.isArray(raw)) return void 0;
24090
- const record2 = raw;
24091
- if (record2["schemaVersion"] !== schemaVersion) return void 0;
24092
- if (typeof record2["sessionId"] !== "string" || !record2["sessionId"].trim()) return void 0;
24093
- return { sessionId: record2["sessionId"].trim() };
24103
+ const record3 = raw;
24104
+ if (record3["schemaVersion"] !== schemaVersion) return void 0;
24105
+ if (typeof record3["sessionId"] !== "string" || !record3["sessionId"].trim()) return void 0;
24106
+ return { sessionId: record3["sessionId"].trim() };
24094
24107
  }
24095
24108
  function acpPromptSettlementBelongsToContext(input2) {
24096
24109
  return input2.liveAcpSessionId === input2.expectedAcpSessionId && (input2.liveActiveTurnId === input2.turnId || input2.liveSessionActiveTurnId === input2.turnId);
@@ -24099,18 +24112,18 @@ function isAcpRequestPermissionParams(value) {
24099
24112
  if (typeof value !== "object" || value === null) {
24100
24113
  return false;
24101
24114
  }
24102
- const record2 = value;
24103
- if (typeof record2["sessionId"] !== "string") {
24115
+ const record3 = value;
24116
+ if (typeof record3["sessionId"] !== "string") {
24104
24117
  return false;
24105
24118
  }
24106
- const toolCall = record2["toolCall"];
24119
+ const toolCall = record3["toolCall"];
24107
24120
  if (typeof toolCall !== "object" || toolCall === null) {
24108
24121
  return false;
24109
24122
  }
24110
24123
  if (typeof toolCall["toolCallId"] !== "string") {
24111
24124
  return false;
24112
24125
  }
24113
- return Array.isArray(record2["options"]);
24126
+ return Array.isArray(record3["options"]);
24114
24127
  }
24115
24128
 
24116
24129
  // ../../packages/ycodium-acp-executor/src/approvals.ts
@@ -24635,8 +24648,8 @@ function asTrimmedString(value) {
24635
24648
  if (typeof value !== "string") {
24636
24649
  return void 0;
24637
24650
  }
24638
- const trimmed2 = value.trim();
24639
- return trimmed2.length > 0 ? trimmed2 : void 0;
24651
+ const trimmed3 = value.trim();
24652
+ return trimmed3.length > 0 ? trimmed3 : void 0;
24640
24653
  }
24641
24654
 
24642
24655
  // ../../packages/ycodium-acp-executor/src/tool-presentation.ts
@@ -24658,12 +24671,12 @@ function normalizeCommandValue(value) {
24658
24671
  return parts.length > 0 ? parts.join(" ") : void 0;
24659
24672
  }
24660
24673
  function stripTrailingExitCode(value) {
24661
- const trimmed2 = value?.trim();
24662
- if (!trimmed2) {
24674
+ const trimmed3 = value?.trim();
24675
+ if (!trimmed3) {
24663
24676
  return void 0;
24664
24677
  }
24665
- const match = /^(?<output>[\s\S]*?)(?:\s*<exited with exit code \d+>)\s*$/iu.exec(trimmed2);
24666
- const output2 = match?.groups?.output?.trim() ?? trimmed2;
24678
+ const match = /^(?<output>[\s\S]*?)(?:\s*<exited with exit code \d+>)\s*$/iu.exec(trimmed3);
24679
+ const output2 = match?.groups?.output?.trim() ?? trimmed3;
24667
24680
  return output2.length > 0 ? output2 : void 0;
24668
24681
  }
24669
24682
  function extractCommandFromTitle(title) {
@@ -24753,11 +24766,11 @@ function extractPrimaryPath(data) {
24753
24766
  return paths[0];
24754
24767
  }
24755
24768
  function normalizeEquivalentValue(value) {
24756
- const trimmed2 = asTrimmedString(value);
24757
- if (!trimmed2) {
24769
+ const trimmed3 = asTrimmedString(value);
24770
+ if (!trimmed3) {
24758
24771
  return void 0;
24759
24772
  }
24760
- return trimmed2.replace(/\s+/gu, " ").replace(/\s+(?:complete|completed|started)\s*$/iu, "").trim();
24773
+ return trimmed3.replace(/\s+/gu, " ").replace(/\s+(?:complete|completed|started)\s*$/iu, "").trim();
24761
24774
  }
24762
24775
  function isEquivalent(left, right) {
24763
24776
  const normalizedLeft = normalizeEquivalentValue(left)?.toLowerCase();
@@ -24845,7 +24858,7 @@ function extractModelConfigId(sessionResponse) {
24845
24858
  return opt.id.trim();
24846
24859
  }
24847
24860
  }
24848
- return void 0;
24861
+ return configOptions.find((opt) => opt.id.trim() === "model" && opt.type === "select") ? "model" : void 0;
24849
24862
  }
24850
24863
  function findSessionConfigOption(configOptions, configId) {
24851
24864
  if (!configOptions) {
@@ -25023,11 +25036,11 @@ function extractToolCallCommand(rawInput, title) {
25023
25036
  }
25024
25037
  var TOOL_CALL_CONTENT_MAX_CHARS = 8e3;
25025
25038
  var TOOL_CALL_CONTENT_TRUNCATION_MARKER = "[Earlier output truncated]\n\n";
25026
- function boundToolCallOutputText(text) {
25027
- if (text.length <= TOOL_CALL_CONTENT_MAX_CHARS) {
25028
- return text;
25039
+ function boundToolCallOutputText(text2) {
25040
+ if (text2.length <= TOOL_CALL_CONTENT_MAX_CHARS) {
25041
+ return text2;
25029
25042
  }
25030
- const tail = text.slice(text.length - TOOL_CALL_CONTENT_MAX_CHARS);
25043
+ const tail = text2.slice(text2.length - TOOL_CALL_CONTENT_MAX_CHARS);
25031
25044
  return `${TOOL_CALL_CONTENT_TRUNCATION_MARKER}${tail}`;
25032
25045
  }
25033
25046
  var RAW_OUTPUT_TEXT_FIELDS = ["content", "stdout", "stderr", "output"];
@@ -25059,9 +25072,9 @@ function extractTextContentFromToolCallContent(content) {
25059
25072
  const typed = content;
25060
25073
  const chunks = [];
25061
25074
  for (const entry of typed) {
25062
- const text = toolCallContentText(entry)?.trim();
25063
- if (text) {
25064
- chunks.push(text);
25075
+ const text2 = toolCallContentText(entry)?.trim();
25076
+ if (text2) {
25077
+ chunks.push(text2);
25065
25078
  }
25066
25079
  }
25067
25080
  if (chunks.length === 0) {
@@ -25239,8 +25252,15 @@ function boundToolCallRawPayload(params, update, toolCall) {
25239
25252
  function readAcpText(source, key) {
25240
25253
  const value = source?.[key];
25241
25254
  if (typeof value !== "string") return void 0;
25242
- const trimmed2 = value.trim();
25243
- return trimmed2.length > 0 ? trimmed2 : void 0;
25255
+ const trimmed3 = value.trim();
25256
+ return trimmed3.length > 0 ? trimmed3 : void 0;
25257
+ }
25258
+ function textFromContentBlocks(blocks) {
25259
+ const parts = blocks.flatMap(
25260
+ (block) => isRecord5(block) && block["type"] === "text" && typeof block["text"] === "string" ? [block["text"]] : []
25261
+ );
25262
+ const joined = parts.join("");
25263
+ return joined.trim().length > 0 ? joined : void 0;
25244
25264
  }
25245
25265
  function parseSessionUpdateEvent(params) {
25246
25266
  const upd = params.update;
@@ -25305,11 +25325,11 @@ function parseSessionUpdateEvent(params) {
25305
25325
  const contentType = content?.["type"];
25306
25326
  const streamKind = kind === "agent_thought_chunk" ? "reasoning_text" : "assistant_text";
25307
25327
  if (contentType === "text") {
25308
- const text = typeof content?.["text"] === "string" ? content["text"] : "";
25309
- if (text.length > 0) {
25328
+ const text2 = typeof content?.["text"] === "string" ? content["text"] : "";
25329
+ if (text2.length > 0) {
25310
25330
  events.push({
25311
25331
  _tag: "ContentDelta",
25312
- text,
25332
+ text: text2,
25313
25333
  streamKind,
25314
25334
  rawPayload: params
25315
25335
  });
@@ -25325,6 +25345,28 @@ function parseSessionUpdateEvent(params) {
25325
25345
  }
25326
25346
  break;
25327
25347
  }
25348
+ case "compaction_update": {
25349
+ const compactionId = readAcpText(upd, "compactionId");
25350
+ const status = readAcpText(upd, "status");
25351
+ if (compactionId === void 0 || status === void 0) break;
25352
+ const rawSummary = upd["summary"];
25353
+ events.push({
25354
+ _tag: "CompactionUpdated",
25355
+ compactionId,
25356
+ status,
25357
+ summary: rawSummary === void 0 ? void 0 : Array.isArray(rawSummary) ? textFromContentBlocks(rawSummary) ?? null : null,
25358
+ error: readAcpText(upd, "error")
25359
+ });
25360
+ break;
25361
+ }
25362
+ case "compaction_summary_chunk": {
25363
+ const compactionId = readAcpText(upd, "compactionId");
25364
+ const content = isRecord5(upd["content"]) ? upd["content"] : void 0;
25365
+ const text2 = content?.["type"] === "text" ? content["text"] : void 0;
25366
+ if (compactionId === void 0 || typeof text2 !== "string" || text2.length === 0) break;
25367
+ events.push({ _tag: "CompactionSummaryChunk", compactionId, text: text2 });
25368
+ break;
25369
+ }
25328
25370
  default:
25329
25371
  break;
25330
25372
  }
@@ -25421,6 +25463,97 @@ function createAcpExecutor(options) {
25421
25463
  if (settledPromptKeys.length > SETTLED_PROMPT_KEY_LIMIT) settledPromptKeys.shift();
25422
25464
  };
25423
25465
  const toolCalls = /* @__PURE__ */ new Map();
25466
+ const compactions = /* @__PURE__ */ new Map();
25467
+ let manualCompaction;
25468
+ const COMPACTION_SUMMARY_MAX_CHARS = 8e3;
25469
+ const nowMs = () => options.nowMs?.() ?? Date.now();
25470
+ const emitCompactionState = (record3, state) => {
25471
+ const summary = record3.summary?.trim() ?? "";
25472
+ const truncated = summary.length > COMPACTION_SUMMARY_MAX_CHARS;
25473
+ emit({
25474
+ type: "thread.state.changed",
25475
+ ...stamp(),
25476
+ provider: options.provider,
25477
+ threadId: options.threadId,
25478
+ ...activeTurnId !== void 0 ? { turnId: activeTurnId } : {},
25479
+ payload: {
25480
+ state,
25481
+ compaction: {
25482
+ compactionId: record3.snapshotId,
25483
+ trigger: record3.trigger,
25484
+ ...state === "compacted" ? { durationMs: Math.max(0, nowMs() - record3.startedAtMs) } : {},
25485
+ ...summary.length > 0 ? {
25486
+ summary: truncated ? summary.slice(0, COMPACTION_SUMMARY_MAX_CHARS) : summary,
25487
+ ...truncated ? { summaryTruncated: true } : {}
25488
+ } : {}
25489
+ }
25490
+ }
25491
+ });
25492
+ };
25493
+ const applyCompactionUpdate = (event) => {
25494
+ let record3 = compactions.get(event.compactionId);
25495
+ if (record3 === void 0) {
25496
+ if (manualCompaction !== void 0 && !manualCompaction.adopted) {
25497
+ manualCompaction.adopted = true;
25498
+ record3 = manualCompaction;
25499
+ } else {
25500
+ record3 = {
25501
+ snapshotId: randomId(),
25502
+ trigger: "automatic",
25503
+ startedAtMs: nowMs(),
25504
+ summary: void 0,
25505
+ announced: false,
25506
+ finished: false,
25507
+ adopted: false
25508
+ };
25509
+ }
25510
+ compactions.set(event.compactionId, record3);
25511
+ }
25512
+ if (event.summary !== void 0) record3.summary = event.summary ?? void 0;
25513
+ switch (event.status) {
25514
+ case "in_progress":
25515
+ if (!record3.announced) {
25516
+ record3.announced = true;
25517
+ emitCompactionState(record3, "compacting");
25518
+ }
25519
+ return;
25520
+ case "completed":
25521
+ record3.announced = true;
25522
+ record3.finished = true;
25523
+ compactions.delete(event.compactionId);
25524
+ emitCompactionState(record3, "compacted");
25525
+ return;
25526
+ case "failed":
25527
+ case "cancelled":
25528
+ record3.finished = true;
25529
+ compactions.delete(event.compactionId);
25530
+ emit({
25531
+ type: "runtime.warning",
25532
+ ...stamp(),
25533
+ provider: options.provider,
25534
+ threadId: options.threadId,
25535
+ ...activeTurnId !== void 0 ? { turnId: activeTurnId } : {},
25536
+ payload: {
25537
+ message: event.error ?? `Context compaction ${event.status}`,
25538
+ detail: { code: `context-compaction-${event.status}` }
25539
+ }
25540
+ });
25541
+ return;
25542
+ default:
25543
+ return;
25544
+ }
25545
+ };
25546
+ const shownToolCallIds = /* @__PURE__ */ new Set();
25547
+ const SHOWN_TOOL_CALL_LIMIT = 256;
25548
+ const rememberShownToolCall = (toolCallId) => {
25549
+ shownToolCallIds.add(toolCallId);
25550
+ if (shownToolCallIds.size <= SHOWN_TOOL_CALL_LIMIT) return;
25551
+ for (const id of shownToolCallIds) {
25552
+ if (toolCalls.has(id)) continue;
25553
+ shownToolCallIds.delete(id);
25554
+ return;
25555
+ }
25556
+ };
25424
25557
  let assistantSegment = { nextSegmentIndex: 0 };
25425
25558
  let modeState;
25426
25559
  let availableCommands = [];
@@ -25507,7 +25640,6 @@ function createAcpExecutor(options) {
25507
25640
  }
25508
25641
  for (const event of parsed.events) {
25509
25642
  if (event._tag === "ToolCallUpdated") {
25510
- closeActiveAssistantSegment(activeTurnId);
25511
25643
  const tracked = toolCalls.get(event.toolCall.toolCallId);
25512
25644
  const previous = tracked?.state;
25513
25645
  const nextToolCall = mergeToolCallState(previous, event.toolCall);
@@ -25526,9 +25658,35 @@ function createAcpExecutor(options) {
25526
25658
  skippedSinceEmit: decision.skippedSinceEmit
25527
25659
  });
25528
25660
  }
25529
- if (!decision.emit) {
25661
+ const hookResult = hooks.onToolCallUpdated?.({
25662
+ toolCallId: nextToolCall.toolCallId,
25663
+ status: nextToolCall.status,
25664
+ title: nextToolCall.title,
25665
+ kind: nextToolCall.kind,
25666
+ rawInput: nextToolCall.data["rawInput"],
25667
+ rawOutput: nextToolCall.data["rawOutput"],
25668
+ meta: params.update._meta,
25669
+ turnId: activeTurnId
25670
+ });
25671
+ const taskEvents = hookResult === void 0 ? [] : "events" in hookResult ? hookResult.events : hookResult;
25672
+ const handledByHook = hookResult !== void 0 && "events" in hookResult && hookResult.handled === true;
25673
+ for (const taskEvent of taskEvents) {
25674
+ emit({
25675
+ type: taskEvent.type,
25676
+ ...stamp(),
25677
+ provider: options.provider,
25678
+ threadId: options.threadId,
25679
+ ...taskEvent.turnId !== void 0 ? { turnId: taskEvent.turnId } : {},
25680
+ payload: taskEvent.payload
25681
+ });
25682
+ }
25683
+ if (handledByHook || !decision.emit) {
25530
25684
  continue;
25531
25685
  }
25686
+ if (!shownToolCallIds.has(nextToolCall.toolCallId)) {
25687
+ rememberShownToolCall(nextToolCall.toolCallId);
25688
+ closeActiveAssistantSegment(activeTurnId);
25689
+ }
25532
25690
  emit(
25533
25691
  makeAcpToolCallEvent({
25534
25692
  stamp: stamp(),
@@ -25541,7 +25699,21 @@ function createAcpExecutor(options) {
25541
25699
  );
25542
25700
  continue;
25543
25701
  }
25702
+ if (event._tag === "CompactionUpdated") {
25703
+ applyCompactionUpdate(event);
25704
+ continue;
25705
+ }
25706
+ if (event._tag === "CompactionSummaryChunk") {
25707
+ const record3 = compactions.get(event.compactionId);
25708
+ if (record3 !== void 0 && !record3.finished) {
25709
+ record3.summary = (record3.summary ?? "") + event.text;
25710
+ }
25711
+ continue;
25712
+ }
25544
25713
  if (event._tag === "ContentDelta") {
25714
+ if (manualCompaction !== void 0) {
25715
+ continue;
25716
+ }
25545
25717
  if (event.streamKind === "reasoning_text") {
25546
25718
  if (event.text.trim().length === 0) {
25547
25719
  continue;
@@ -25784,7 +25956,9 @@ function createAcpExecutor(options) {
25784
25956
  },
25785
25957
  onNotification: (notification) => {
25786
25958
  if (notification.method === "session/update") {
25787
- handleSessionUpdate(notification.params);
25959
+ const params = notification.params;
25960
+ const transformed = hooks.transformSessionUpdate ? hooks.transformSessionUpdate(params) : params;
25961
+ if (transformed !== void 0) handleSessionUpdate(transformed);
25788
25962
  return;
25789
25963
  }
25790
25964
  if (settleFromNotification(notification.method, notification.params)) return;
@@ -25918,13 +26092,15 @@ function createAcpExecutor(options) {
25918
26092
  options.mcpServers ?? [],
25919
26093
  initialized.agentCapabilities
25920
26094
  );
26095
+ const additionalDirectories = options.additionalDirectories !== void 0 && options.additionalDirectories.length > 0 ? { additionalDirectories: options.additionalDirectories } : {};
25921
26096
  let setup;
25922
26097
  let nextSessionId;
25923
26098
  if (options.resumeSessionId && options.resumeMethod === "session/resume") {
25924
26099
  setup = await connection.request("session/resume", {
25925
26100
  sessionId: options.resumeSessionId,
25926
26101
  cwd: options.cwd,
25927
- mcpServers
26102
+ mcpServers,
26103
+ ...additionalDirectories
25928
26104
  });
25929
26105
  nextSessionId = options.resumeSessionId;
25930
26106
  } else if (options.resumeSessionId) {
@@ -25933,7 +26109,8 @@ function createAcpExecutor(options) {
25933
26109
  setup = await connection.request("session/load", {
25934
26110
  sessionId: options.resumeSessionId,
25935
26111
  cwd: options.cwd,
25936
- mcpServers
26112
+ mcpServers,
26113
+ ...additionalDirectories
25937
26114
  });
25938
26115
  } finally {
25939
26116
  sessionLoadInFlight = false;
@@ -25946,7 +26123,8 @@ function createAcpExecutor(options) {
25946
26123
  } else {
25947
26124
  const created = await connection.request("session/new", {
25948
26125
  cwd: options.cwd,
25949
- mcpServers
26126
+ mcpServers,
26127
+ ...additionalDirectories
25950
26128
  });
25951
26129
  nextSessionId = created.sessionId;
25952
26130
  setup = created;
@@ -26020,13 +26198,13 @@ function createAcpExecutor(options) {
26020
26198
  startInFlight = void 0;
26021
26199
  return result;
26022
26200
  },
26023
- (error62) => {
26201
+ async (error62) => {
26024
26202
  startInFlight = void 0;
26025
26203
  if (isLive(expectedGeneration) && !startResult) {
26026
26204
  status = "idle";
26027
26205
  generation = expectedGeneration;
26028
26206
  }
26029
- throw error62;
26207
+ throw await withStartupStderr(error62, options.stderrTail);
26030
26208
  }
26031
26209
  );
26032
26210
  return startInFlight;
@@ -26094,6 +26272,36 @@ function createAcpExecutor(options) {
26094
26272
  interruptedTurnIds.delete(input2.turnId);
26095
26273
  }
26096
26274
  };
26275
+ const sendPromptRpc = async (sessionId2, promptParts) => {
26276
+ const settlementHooks = hooks.promptSettlement;
26277
+ const promptKey = settlementHooks === void 0 ? void 0 : randomId();
26278
+ const promptRpc = connection.request("session/prompt", {
26279
+ sessionId: sessionId2,
26280
+ prompt: promptParts,
26281
+ ...settlementHooks !== void 0 && promptKey !== void 0 ? settlementHooks.requestFields(promptKey) : {}
26282
+ });
26283
+ if (settlementHooks === void 0 || promptKey === void 0) {
26284
+ return await promptRpc;
26285
+ } else {
26286
+ const { promise: early, resolve: settleEarly } = Promise.withResolvers();
26287
+ pendingSettlement = { promptKey, sessionId: sessionId2, settle: settleEarly };
26288
+ try {
26289
+ const outcome = await Promise.race([
26290
+ promptRpc.then((value) => {
26291
+ const key = settlementHooks.responseKey?.(value);
26292
+ if (key !== void 0) rememberSettledPromptKey(key);
26293
+ return { response: value };
26294
+ }),
26295
+ early
26296
+ ]);
26297
+ if (outcome.error !== void 0) throw outcome.error;
26298
+ return outcome.response;
26299
+ } finally {
26300
+ pendingSettlement = void 0;
26301
+ void promptRpc.catch(() => void 0);
26302
+ }
26303
+ }
26304
+ };
26097
26305
  const prompt = async (input2) => {
26098
26306
  failIfDisposed();
26099
26307
  const started = requireStarted();
@@ -26144,35 +26352,8 @@ function createAcpExecutor(options) {
26144
26352
  expectedGeneration: started.generation
26145
26353
  });
26146
26354
  closeActiveAssistantSegment(turnId);
26147
- const settlementHooks = hooks.promptSettlement;
26148
- const promptKey = settlementHooks === void 0 ? void 0 : randomId();
26149
- const promptRpc = connection.request("session/prompt", {
26150
- sessionId: started.sessionId,
26151
- prompt: promptParts,
26152
- ...settlementHooks !== void 0 && promptKey !== void 0 ? settlementHooks.requestFields(promptKey) : {}
26153
- });
26154
26355
  try {
26155
- if (settlementHooks === void 0 || promptKey === void 0) {
26156
- response = await promptRpc;
26157
- } else {
26158
- const { promise: early, resolve: settleEarly } = Promise.withResolvers();
26159
- pendingSettlement = { promptKey, sessionId: started.sessionId, settle: settleEarly };
26160
- try {
26161
- const outcome = await Promise.race([
26162
- promptRpc.then((value) => {
26163
- const key = settlementHooks.responseKey?.(value);
26164
- if (key !== void 0) rememberSettledPromptKey(key);
26165
- return { response: value };
26166
- }),
26167
- early
26168
- ]);
26169
- if (outcome.error !== void 0) throw outcome.error;
26170
- response = outcome.response;
26171
- } finally {
26172
- pendingSettlement = void 0;
26173
- void promptRpc.catch(() => void 0);
26174
- }
26175
- }
26356
+ response = await sendPromptRpc(started.sessionId, promptParts);
26176
26357
  } catch (error62) {
26177
26358
  if (disposed) {
26178
26359
  throw new AcpExecutorDisposedError();
@@ -26336,6 +26517,74 @@ function createAcpExecutor(options) {
26336
26517
  };
26337
26518
  apply();
26338
26519
  };
26520
+ const compact = async (input2) => {
26521
+ failIfDisposed();
26522
+ const started = requireStarted();
26523
+ const trimmedName = input2.commandName.trim();
26524
+ const commandName = trimmedName.startsWith("/") ? trimmedName.slice(1) : trimmedName;
26525
+ if (!availableCommands.some((command) => command.name === commandName)) {
26526
+ throw new AcpExecutorError(
26527
+ "command-unavailable",
26528
+ `The agent has not advertised the /${commandName} command`
26529
+ );
26530
+ }
26531
+ if (promptsInFlight > 0 || promptMutex.isLocked || manualCompaction !== void 0) {
26532
+ throw new AcpExecutorError(
26533
+ "busy",
26534
+ "Cannot compact while a turn or another compaction is running"
26535
+ );
26536
+ }
26537
+ const record3 = {
26538
+ snapshotId: randomId(),
26539
+ trigger: "manual",
26540
+ startedAtMs: nowMs(),
26541
+ summary: void 0,
26542
+ announced: true,
26543
+ finished: false,
26544
+ adopted: false
26545
+ };
26546
+ await promptMutex.acquire();
26547
+ manualCompaction = record3;
26548
+ const emitSessionState = (state) => {
26549
+ emit({
26550
+ type: "session.state.changed",
26551
+ ...stamp(),
26552
+ provider: options.provider,
26553
+ threadId: options.threadId,
26554
+ payload: { state, reason: "compaction" }
26555
+ });
26556
+ };
26557
+ try {
26558
+ failIfDisposed();
26559
+ emitSessionState("compacting");
26560
+ emitCompactionState(record3, "compacting");
26561
+ let response;
26562
+ try {
26563
+ response = await sendPromptRpc(started.sessionId, [
26564
+ { type: "text", text: `/${commandName}` }
26565
+ ]);
26566
+ } catch (error62) {
26567
+ if (disposed) throw new AcpExecutorDisposedError();
26568
+ throw error62;
26569
+ }
26570
+ await connection.awaitInboundIdle();
26571
+ if (response.stopReason === "cancelled") {
26572
+ return;
26573
+ }
26574
+ if (!record3.finished) {
26575
+ record3.finished = true;
26576
+ emitCompactionState(record3, "compacted");
26577
+ }
26578
+ } finally {
26579
+ for (const [agentId, tracked] of compactions) {
26580
+ if (tracked === record3) compactions.delete(agentId);
26581
+ }
26582
+ manualCompaction = void 0;
26583
+ promptMutex.release();
26584
+ notifyPromptSettled();
26585
+ if (!disposed) emitSessionState("ready");
26586
+ }
26587
+ };
26339
26588
  const setNativeMode = async (modeId) => {
26340
26589
  failIfDisposed();
26341
26590
  const started = startResult;
@@ -26423,6 +26672,7 @@ function createAcpExecutor(options) {
26423
26672
  respondToRequest,
26424
26673
  respondToUserInput,
26425
26674
  setNativeMode,
26675
+ compact,
26426
26676
  dispose
26427
26677
  };
26428
26678
  }
@@ -26498,13 +26748,13 @@ function parseCliVersion(output2) {
26498
26748
  }
26499
26749
  async function collectCappedText(stream, maxBytes) {
26500
26750
  const decoder = new TextDecoder();
26501
- let text = "";
26751
+ let text2 = "";
26502
26752
  let total = 0;
26503
26753
  for await (const chunk of stream) {
26504
26754
  total += chunk.byteLength;
26505
- if (total <= maxBytes) text += decoder.decode(chunk, { stream: true });
26755
+ if (total <= maxBytes) text2 += decoder.decode(chunk, { stream: true });
26506
26756
  }
26507
- return { text: text + decoder.decode(), overflowed: total > maxBytes };
26757
+ return { text: text2 + decoder.decode(), overflowed: total > maxBytes };
26508
26758
  }
26509
26759
  async function collectProbeProcess(child, input2) {
26510
26760
  try {
@@ -26645,11 +26895,11 @@ import * as NodeFS from "node:fs";
26645
26895
  import * as NodePath from "node:path";
26646
26896
  var AUTH_FILE_NAME = "auth.json";
26647
26897
  var isRecord7 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
26648
- var readString2 = (record2, key) => {
26649
- const value = record2[key];
26898
+ var readString2 = (record3, key) => {
26899
+ const value = record3[key];
26650
26900
  if (typeof value !== "string") return void 0;
26651
- const trimmed2 = value.trim();
26652
- return trimmed2.length > 0 ? trimmed2 : void 0;
26901
+ const trimmed3 = value.trim();
26902
+ return trimmed3.length > 0 ? trimmed3 : void 0;
26653
26903
  };
26654
26904
  var parseJsonRecord = (raw) => {
26655
26905
  let parsed;
@@ -26688,9 +26938,9 @@ var unreadableProbe = (exists) => ({
26688
26938
  name: void 0,
26689
26939
  expiresAt: void 0
26690
26940
  });
26691
- var joinName = (record2) => {
26692
- const first = readString2(record2, "first_name");
26693
- const last = readString2(record2, "last_name");
26941
+ var joinName = (record3) => {
26942
+ const first = readString2(record3, "first_name");
26943
+ const last = readString2(record3, "last_name");
26694
26944
  const joined = [first, last].filter((part) => part !== void 0).join(" ");
26695
26945
  return joined.length > 0 ? joined : void 0;
26696
26946
  };
@@ -26725,13 +26975,176 @@ var readGrokAccount = (homePath) => {
26725
26975
  };
26726
26976
  };
26727
26977
 
26728
- // src/grokSupport.ts
26729
- import { modelSelectionStringOption } from "@ycodium-ai/plugin-api/executor";
26978
+ // src/grokPlanGate.ts
26979
+ var XAI_EXIT_PLAN_MODE_METHODS = [
26980
+ "_x.ai/exit_plan_mode",
26981
+ "x.ai/exit_plan_mode"
26982
+ ];
26983
+ var GROK_PLAN_APPROVE_OPTION_ID = "approved";
26984
+ var GROK_PLAN_ABANDON_OPTION_ID = "abandoned";
26985
+ var GROK_EMPTY_PLAN_TEXT = "The agent exited plan mode without writing a plan.";
26730
26986
  var isRecord8 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
26731
26987
  var trimmed = (value) => {
26732
26988
  if (typeof value !== "string") return void 0;
26733
- const text = value.trim();
26734
- return text.length > 0 ? text : void 0;
26989
+ const text2 = value.trim();
26990
+ return text2.length > 0 ? text2 : void 0;
26991
+ };
26992
+ function parseXAiExitPlanModeParams(params) {
26993
+ const candidates = [params, ...isRecord8(params) ? [params["params"]] : []];
26994
+ for (const candidate of candidates) {
26995
+ if (!isRecord8(candidate)) continue;
26996
+ const toolCallId = trimmed(candidate["toolCallId"]);
26997
+ if (toolCallId === void 0) continue;
26998
+ return { toolCallId, planContent: trimmed(candidate["planContent"]) };
26999
+ }
27000
+ return void 0;
27001
+ }
27002
+ function grokPlanQuestion(params) {
27003
+ return {
27004
+ id: params.toolCallId,
27005
+ header: "Plan",
27006
+ question: params.planContent ?? GROK_EMPTY_PLAN_TEXT,
27007
+ kind: "plan",
27008
+ allowFreeform: true,
27009
+ multiSelect: false,
27010
+ // 选项只给语义:用户读到的文案由宿主按界面语言渲染,label 仅是协议要求非空的稳定值。
27011
+ options: [
27012
+ {
27013
+ id: GROK_PLAN_APPROVE_OPTION_ID,
27014
+ label: GROK_PLAN_APPROVE_OPTION_ID,
27015
+ description: "",
27016
+ planAction: "approve"
27017
+ },
27018
+ {
27019
+ id: GROK_PLAN_ABANDON_OPTION_ID,
27020
+ label: GROK_PLAN_ABANDON_OPTION_ID,
27021
+ description: "",
27022
+ planAction: "stop"
27023
+ }
27024
+ ]
27025
+ };
27026
+ }
27027
+ var ABANDONED_FEEDBACK = "The user chose not to continue with this plan. Stop here and wait for their next message.";
27028
+ var abandoned = () => ({
27029
+ outcome: "abandoned",
27030
+ feedback: ABANDONED_FEEDBACK
27031
+ });
27032
+ function makeXAiExitPlanModeResponse(params, resolution) {
27033
+ if (resolution.kind === "cancelled") return abandoned();
27034
+ const answer = resolution.answers[params.toolCallId];
27035
+ const selected = typeof answer === "string" ? answer : Array.isArray(answer) && answer.length === 1 && typeof answer[0] === "string" ? answer[0] : void 0;
27036
+ const text2 = trimmed(selected);
27037
+ if (text2 === void 0) return abandoned();
27038
+ if (text2 === GROK_PLAN_APPROVE_OPTION_ID) return { outcome: "approved" };
27039
+ if (text2 === GROK_PLAN_ABANDON_OPTION_ID) return abandoned();
27040
+ return { outcome: "request_changes", feedback: text2 };
27041
+ }
27042
+
27043
+ // src/grokBackgroundTasks.ts
27044
+ var record2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
27045
+ var text = (value) => typeof value === "string" ? value.trim() || void 0 : void 0;
27046
+ var lifecycle = (status, exitCode) => {
27047
+ switch (text(status)?.toLowerCase()) {
27048
+ case "pending":
27049
+ case "running":
27050
+ return "running";
27051
+ case "completed":
27052
+ case "success":
27053
+ case "succeeded":
27054
+ return "completed";
27055
+ case "failed":
27056
+ case "error":
27057
+ return "failed";
27058
+ case "stopped":
27059
+ case "killed":
27060
+ case "cancelled":
27061
+ return "stopped";
27062
+ default:
27063
+ return typeof exitCode === "number" && Number.isFinite(exitCode) ? exitCode === 0 ? "completed" : "failed" : void 0;
27064
+ }
27065
+ };
27066
+ function buildGrokBackgroundTaskEvents(input2) {
27067
+ const { tasks, toolCallId, toolCallStatus, turnId } = input2;
27068
+ const output2 = record2(input2.rawOutput);
27069
+ const events = [];
27070
+ if (toolCallStatus !== "completed" && toolCallStatus !== "failed" && output2["type"] !== "BackgroundTaskStarted") {
27071
+ return events;
27072
+ }
27073
+ const attribution = (task) => task.turnId !== void 0 && task.turnId === turnId ? { turnId } : {};
27074
+ const start = (id, taskType, description, toolUseId) => {
27075
+ const known = tasks.get(id);
27076
+ if (known) return known;
27077
+ const task = {
27078
+ payload: {
27079
+ taskId: id,
27080
+ taskType,
27081
+ description,
27082
+ title: description,
27083
+ ...toolUseId ? { toolUseId } : {}
27084
+ },
27085
+ // 轮询可能重新发现老任务,那时并不知道它是哪一轮起的。
27086
+ turnId: toolUseId ? turnId : void 0
27087
+ };
27088
+ tasks.set(id, task);
27089
+ events.push({ type: "task.started", payload: task.payload, ...attribution(task) });
27090
+ return task;
27091
+ };
27092
+ const complete = (task, status, summary) => {
27093
+ tasks.delete(task.payload.taskId);
27094
+ events.push({
27095
+ type: "task.completed",
27096
+ payload: { ...task.payload, status, ...summary ? { summary } : {} },
27097
+ ...attribution(task)
27098
+ });
27099
+ };
27100
+ if (output2["type"] === "Monitor" && toolCallStatus === "completed") {
27101
+ const id = text(output2["taskId"]);
27102
+ if (id) {
27103
+ start(id, "monitor", text(record2(input2.rawInput)["description"]) ?? "Monitor", toolCallId);
27104
+ }
27105
+ } else if (output2["type"] === "BackgroundTaskStarted") {
27106
+ const id = text(output2["task_id"]) ?? text(output2["taskId"]);
27107
+ const command = text(output2["command"]);
27108
+ if (id && command) start(id, "shell", command.split("\n")[0].slice(0, 200), toolCallId);
27109
+ } else if (output2["type"] === "TaskOutput" || output2["type"] === "KillTask") {
27110
+ const results = record2(output2["MultiResult"])["results"];
27111
+ for (const value of Array.isArray(results) ? results : [output2["Result"]]) {
27112
+ const result = record2(value);
27113
+ const id = text(result["task_id"]);
27114
+ if (!id) continue;
27115
+ if (output2["type"] === "KillTask") {
27116
+ const task2 = tasks.get(id);
27117
+ if (task2 && toolCallStatus === "completed" && result["outcome"] === "killed") {
27118
+ complete(task2, "stopped");
27119
+ }
27120
+ continue;
27121
+ }
27122
+ const command = text(result["command"]);
27123
+ const status = lifecycle(result["status"], result["exit_code"]);
27124
+ if (!command || !status || command.startsWith("[subagent:")) continue;
27125
+ const task = start(id, /^\[monitor[:\]]/.test(command) ? "monitor" : "shell", command);
27126
+ const summary = text(result["output"])?.split("\n").find((line) => line.trim())?.trim();
27127
+ if (status === "running") {
27128
+ events.push({
27129
+ type: "task.progress",
27130
+ payload: { ...task.payload, ...summary ? { summary } : {} },
27131
+ ...attribution(task)
27132
+ });
27133
+ } else {
27134
+ complete(task, status, summary);
27135
+ }
27136
+ }
27137
+ }
27138
+ return events;
27139
+ }
27140
+
27141
+ // src/grokSupport.ts
27142
+ import { modelSelectionStringOption } from "@ycodium-ai/plugin-api/executor";
27143
+ var isRecord9 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
27144
+ var trimmed2 = (value) => {
27145
+ if (typeof value !== "string") return void 0;
27146
+ const text2 = value.trim();
27147
+ return text2.length > 0 ? text2 : void 0;
26735
27148
  };
26736
27149
  function grokReasoningEffortArgs(effort) {
26737
27150
  const value = effort?.trim();
@@ -26842,45 +27255,45 @@ function grokRuntimeModeCaveat(nativeModeId) {
26842
27255
  }
26843
27256
  var GROK_REASONING_OPTION_ID = "reasoningEffort";
26844
27257
  function resolveGrokAuthMethodId(initializeResult) {
26845
- const authMethods = isRecord8(initializeResult) ? initializeResult["authMethods"] : void 0;
27258
+ const authMethods = isRecord9(initializeResult) ? initializeResult["authMethods"] : void 0;
26846
27259
  if (!Array.isArray(authMethods)) return void 0;
26847
27260
  for (const method of authMethods) {
26848
- if (isRecord8(method) && method["id"] === "cached_token") return "cached_token";
27261
+ if (isRecord9(method) && method["id"] === "cached_token") return "cached_token";
26849
27262
  }
26850
27263
  return void 0;
26851
27264
  }
26852
27265
  var positive = (value) => typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.trunc(value) : void 0;
26853
27266
  function grokPromptUsage(promptResponse) {
26854
- if (!isRecord8(promptResponse)) return void 0;
27267
+ if (!isRecord9(promptResponse)) return void 0;
26855
27268
  const meta3 = promptResponse["_meta"];
26856
- if (!isRecord8(meta3)) return void 0;
27269
+ if (!isRecord9(meta3)) return void 0;
26857
27270
  const usedTokens = positive(meta3["totalTokens"]);
26858
27271
  if (usedTokens === void 0) return void 0;
26859
- return { usedTokens, modelId: trimmed(meta3["modelId"]) };
27272
+ return { usedTokens, modelId: trimmed2(meta3["modelId"]) };
26860
27273
  }
26861
27274
  function grokModelState(initializeResult) {
26862
- if (!isRecord8(initializeResult)) return void 0;
27275
+ if (!isRecord9(initializeResult)) return void 0;
26863
27276
  const meta3 = initializeResult["_meta"];
26864
- if (!isRecord8(meta3)) return void 0;
27277
+ if (!isRecord9(meta3)) return void 0;
26865
27278
  const modelState = meta3["modelState"];
26866
- if (!isRecord8(modelState)) return void 0;
27279
+ if (!isRecord9(modelState)) return void 0;
26867
27280
  const availableModels = modelState["availableModels"];
26868
27281
  if (!Array.isArray(availableModels)) return void 0;
26869
- return { currentModelId: trimmed(modelState["currentModelId"]), availableModels };
27282
+ return { currentModelId: trimmed2(modelState["currentModelId"]), availableModels };
26870
27283
  }
26871
27284
  function grokAdvertisedModels(initializeResult) {
26872
27285
  const state = grokModelState(initializeResult);
26873
27286
  if (state === void 0) return [];
26874
27287
  const models = [];
26875
27288
  for (const entry of state.availableModels) {
26876
- if (!isRecord8(entry)) continue;
26877
- const modelId = trimmed(entry["modelId"]);
27289
+ if (!isRecord9(entry)) continue;
27290
+ const modelId = trimmed2(entry["modelId"]);
26878
27291
  if (modelId === void 0) continue;
26879
27292
  const modelMeta = entry["_meta"];
26880
- const contextWindow = isRecord8(modelMeta) ? positive(modelMeta["totalContextTokens"]) : void 0;
27293
+ const contextWindow = isRecord9(modelMeta) ? positive(modelMeta["totalContextTokens"]) : void 0;
26881
27294
  models.push({
26882
27295
  modelId,
26883
- name: trimmed(entry["name"]),
27296
+ name: trimmed2(entry["name"]),
26884
27297
  isDefault: state.currentModelId !== void 0 && modelId === state.currentModelId,
26885
27298
  contextWindow
26886
27299
  });
@@ -26892,7 +27305,7 @@ var XAI_ASK_USER_QUESTION_METHODS = [
26892
27305
  "x.ai/ask_user_question"
26893
27306
  ];
26894
27307
  var readOption = (value) => {
26895
- if (!isRecord8(value)) return void 0;
27308
+ if (!isRecord9(value)) return void 0;
26896
27309
  const label = typeof value["label"] === "string" ? value["label"] : void 0;
26897
27310
  if (label === void 0) return void 0;
26898
27311
  return {
@@ -26903,7 +27316,7 @@ var readOption = (value) => {
26903
27316
  };
26904
27317
  };
26905
27318
  var readQuestion = (value) => {
26906
- if (!isRecord8(value)) return void 0;
27319
+ if (!isRecord9(value)) return void 0;
26907
27320
  const question = typeof value["question"] === "string" ? value["question"] : void 0;
26908
27321
  if (question === void 0) return void 0;
26909
27322
  const optionsRaw = value["options"];
@@ -26919,9 +27332,9 @@ var readQuestion = (value) => {
26919
27332
  };
26920
27333
  };
26921
27334
  function parseXAiAskUserQuestionParams(params) {
26922
- const candidates = [params, ...isRecord8(params) ? [params["params"]] : []];
27335
+ const candidates = [params, ...isRecord9(params) ? [params["params"]] : []];
26923
27336
  for (const candidate of candidates) {
26924
- if (!isRecord8(candidate)) continue;
27337
+ if (!isRecord9(candidate)) continue;
26925
27338
  const questionsRaw = candidate["questions"];
26926
27339
  if (!Array.isArray(questionsRaw)) continue;
26927
27340
  const questions = questionsRaw.flatMap((entry) => {
@@ -26947,12 +27360,12 @@ function extractXAiAskUserQuestions(params) {
26947
27360
  var answerValues = (answer) => {
26948
27361
  if (Array.isArray(answer)) {
26949
27362
  return answer.flatMap((entry) => {
26950
- const text2 = trimmed(entry);
26951
- return text2 === void 0 ? [] : [text2];
27363
+ const text3 = trimmed2(entry);
27364
+ return text3 === void 0 ? [] : [text3];
26952
27365
  });
26953
27366
  }
26954
- const text = trimmed(answer);
26955
- return text === void 0 ? [] : [text];
27367
+ const text2 = trimmed2(answer);
27368
+ return text2 === void 0 ? [] : [text2];
26956
27369
  };
26957
27370
  function makeXAiAskUserQuestionResponse(params, answers) {
26958
27371
  const normalized = params.questions.flatMap((question) => {
@@ -26963,7 +27376,7 @@ function makeXAiAskUserQuestionResponse(params, answers) {
26963
27376
  const resolved = values.map((value) => ({ value, option: optionByLabel.get(value) }));
26964
27377
  const selectedLabels = resolved.flatMap(({ option }) => option ? [option.label] : []);
26965
27378
  const notes = resolved.flatMap(({ option, value }) => option ? [] : [value]);
26966
- const preview = question.multiSelect ? void 0 : resolved.map(({ option }) => trimmed(option?.preview)).find((value) => value);
27379
+ const preview = question.multiSelect ? void 0 : resolved.map(({ option }) => trimmed2(option?.preview)).find((value) => value);
26967
27380
  const annotation = preview !== void 0 || notes.length > 0 ? {
26968
27381
  ...preview !== void 0 ? { preview } : {},
26969
27382
  ...notes.length > 0 ? { notes: notes.join("\n") } : {}
@@ -26993,34 +27406,99 @@ function makeXAiAskUserQuestionCancelledResponse() {
26993
27406
  return { outcome: "cancelled" };
26994
27407
  }
26995
27408
  var XAI_PROMPT_COMPLETE_METHOD = "_x.ai/session/prompt_complete";
27409
+ var GROK_RATE_LIMIT_MESSAGE = "Grok usage limit reached or rate limited. Try again later.";
27410
+ var grokAgentResultMessage = (value) => {
27411
+ if (typeof value === "string") return trimmed2(value);
27412
+ return isRecord9(value) ? trimmed2(value["message"]) : void 0;
27413
+ };
26996
27414
  var grokPromptSettlement = {
26997
27415
  // 关联 id 双写 promptId 与 requestId(宿主 XAiAcpExtension 同款):
26998
27416
  // agent 认回哪个键是它的事,两个都给才不猜版本。
26999
27417
  requestFields: (promptKey) => ({ _meta: { promptId: promptKey, requestId: promptKey } }),
27000
27418
  responseKey: (response) => {
27001
- if (!isRecord8(response)) return void 0;
27419
+ if (!isRecord9(response)) return void 0;
27002
27420
  const meta3 = response["_meta"];
27003
- if (!isRecord8(meta3)) return void 0;
27004
- return trimmed(meta3["promptId"]) ?? trimmed(meta3["requestId"]);
27421
+ if (!isRecord9(meta3)) return void 0;
27422
+ return trimmed2(meta3["promptId"]) ?? trimmed2(meta3["requestId"]);
27005
27423
  },
27006
27424
  fromNotification: (method, params) => {
27007
27425
  if (method !== XAI_PROMPT_COMPLETE_METHOD) return void 0;
27008
- if (!isRecord8(params)) return void 0;
27426
+ if (!isRecord9(params)) return void 0;
27009
27427
  const sessionId = params["sessionId"];
27010
27428
  if (typeof sessionId !== "string") return void 0;
27011
- const stopReason = trimmed(params["stopReason"]);
27429
+ const stopReason = trimmed2(params["stopReason"]);
27430
+ const failed = stopReason === "error" || stopReason === "rate_limit";
27431
+ const detail = stopReason === "rate_limit" ? GROK_RATE_LIMIT_MESSAGE : grokAgentResultMessage(params["agentResult"]);
27012
27432
  return {
27013
27433
  sessionId,
27014
27434
  ...typeof params["promptId"] === "string" ? { promptKey: params["promptId"] } : {},
27015
- outcome: stopReason === "error" ? "error" : "completed",
27435
+ outcome: failed ? "error" : "completed",
27016
27436
  ...stopReason === void 0 ? {} : { stopReason },
27017
- ...typeof params["agentResult"] === "string" ? { detail: params["agentResult"] } : {}
27437
+ ...detail === void 0 ? {} : { detail }
27018
27438
  };
27019
27439
  }
27020
27440
  };
27021
27441
 
27442
+ // src/grokSkills.ts
27443
+ import * as NodeOS from "node:os";
27444
+ var INSPECT_TIMEOUT_MS = 15e3;
27445
+ var MAX_INSPECT_OUTPUT_BYTES = 4 * 1024 * 1024;
27446
+ var GROK_INSPECT_ARGS = ["inspect", "--json"];
27447
+ var isRecord10 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
27448
+ var trimmedString = (value) => typeof value === "string" ? value.trim() : "";
27449
+ function parseGrokInspectSkills(stdout) {
27450
+ let parsed;
27451
+ try {
27452
+ parsed = JSON.parse(stdout);
27453
+ } catch {
27454
+ return [];
27455
+ }
27456
+ const entries = isRecord10(parsed) ? parsed["skills"] : void 0;
27457
+ if (!Array.isArray(entries)) return [];
27458
+ const byName = /* @__PURE__ */ new Map();
27459
+ for (const entry of entries) {
27460
+ if (!isRecord10(entry)) continue;
27461
+ const name = trimmedString(entry["name"]);
27462
+ const source = isRecord10(entry["source"]) ? entry["source"] : void 0;
27463
+ const path = trimmedString(source?.["path"]);
27464
+ if (name.length === 0 || path.length === 0) continue;
27465
+ const scope = trimmedString(source?.["type"]);
27466
+ const description = trimmedString(entry["description"]);
27467
+ byName.set(name, {
27468
+ name,
27469
+ path,
27470
+ enabled: entry["userInvocable"] !== false,
27471
+ ...scope.length > 0 ? { scope } : {},
27472
+ ...description.length > 0 ? { description } : {}
27473
+ });
27474
+ }
27475
+ return [...byName.values()].toSorted((left, right) => left.name.localeCompare(right.name));
27476
+ }
27477
+ async function probeGrokSkills(input2) {
27478
+ const { recipe, host, env } = input2;
27479
+ try {
27480
+ const handle = await host.process.spawn({
27481
+ program: recipe.binaryPath,
27482
+ args: GROK_INSPECT_ARGS,
27483
+ cwd: NodeOS.homedir(),
27484
+ owner: { bindingId: host.bindingId },
27485
+ ...Object.keys(env).length > 0 ? { env } : {}
27486
+ });
27487
+ const run = await collectProbeProcess(handle, {
27488
+ timeoutMs: INSPECT_TIMEOUT_MS,
27489
+ maxOutputBytes: MAX_INSPECT_OUTPUT_BYTES,
27490
+ what: "grok inspect"
27491
+ });
27492
+ if (run.exitCode !== 0 || run.stdout === void 0) return [];
27493
+ return parseGrokInspectSkills(run.stdout);
27494
+ } catch {
27495
+ return [];
27496
+ }
27497
+ }
27498
+
27022
27499
  // src/probeModels.ts
27023
27500
  import {
27501
+ describeExecutorSpawnFailure,
27024
27502
  ExecutorSpawnRefusedError
27025
27503
  } from "@ycodium-ai/plugin-api/executor";
27026
27504
  import { nodeStreamsFromHandle } from "@ycodium-ai/plugin-api/process-stdin";
@@ -27048,7 +27526,7 @@ async function probeGrokModels(input2) {
27048
27526
  });
27049
27527
  } catch (error62) {
27050
27528
  if (error62 instanceof ExecutorSpawnRefusedError) throw error62;
27051
- return { kind: "not-started", detail: failureDetail(error62) };
27529
+ return { kind: "not-started", ...describeExecutorSpawnFailure(error62) };
27052
27530
  }
27053
27531
  let handshake;
27054
27532
  try {
@@ -27123,16 +27601,16 @@ var deviceCodeUrl = (output2) => DEVICE_CODE_URL.exec(output2)?.[0];
27123
27601
  var deviceCodeValue = (output2) => DEVICE_CODE_VALUE.exec(output2)?.[1];
27124
27602
  var hasDeviceCode = (output2) => deviceCodeUrl(output2) !== void 0 && deviceCodeValue(output2) !== void 0;
27125
27603
  function expandHomePath(value) {
27126
- if (value === "~") return NodeOS.homedir();
27604
+ if (value === "~") return NodeOS2.homedir();
27127
27605
  if (value.startsWith("~/") || value.startsWith("~\\")) {
27128
- return NodePath2.join(NodeOS.homedir(), value.slice(2));
27606
+ return NodePath2.join(NodeOS2.homedir(), value.slice(2));
27129
27607
  }
27130
27608
  return value;
27131
27609
  }
27132
27610
  function resolveGrokHomePath(value) {
27133
- const trimmed2 = value.trim();
27134
- if (trimmed2.length === 0) return NodePath2.join(NodeOS.homedir(), ".grok");
27135
- return NodePath2.resolve(expandHomePath(trimmed2));
27611
+ const trimmed3 = value.trim();
27612
+ if (trimmed3.length === 0) return NodePath2.join(NodeOS2.homedir(), ".grok");
27613
+ return NodePath2.resolve(expandHomePath(trimmed3));
27136
27614
  }
27137
27615
  function probeGrokAccount(accountPath) {
27138
27616
  const resolved = resolveGrokHomePath(accountPath);
@@ -27157,7 +27635,7 @@ function resolveAccountHomePath(recipe, configuredHome) {
27157
27635
  }
27158
27636
  function absentPath() {
27159
27637
  return NodePath2.join(
27160
- NodeOS.tmpdir(),
27638
+ NodeOS2.tmpdir(),
27161
27639
  `ycodium-grok-no-auth-${NodeCrypto2.randomUUID()}`,
27162
27640
  "auth.json"
27163
27641
  );
@@ -27239,7 +27717,7 @@ var createGrokInstance = (config2, host) => {
27239
27717
  return {
27240
27718
  installed: false,
27241
27719
  version: null,
27242
- readiness: "error",
27720
+ readiness: "not-installed",
27243
27721
  auth: { status: "unknown" },
27244
27722
  detail: "Grok program path is not configured"
27245
27723
  };
@@ -27250,15 +27728,17 @@ var createGrokInstance = (config2, host) => {
27250
27728
  resolveGrokHomePath(configuredHome),
27251
27729
  configuredHome.trim().length > 0
27252
27730
  );
27253
- const [outcome, version2] = await Promise.all([
27731
+ const [outcome, version2, skills] = await Promise.all([
27254
27732
  probeGrokModels({ recipe, host, env }),
27255
- probeGrokVersion({ recipe, host, env })
27733
+ probeGrokVersion({ recipe, host, env }),
27734
+ probeGrokSkills({ recipe, host, env })
27256
27735
  ]);
27257
27736
  if (outcome.kind !== "answered") {
27258
27737
  return {
27259
27738
  installed: outcome.kind === "failed",
27260
27739
  version: version2,
27261
- readiness: "error",
27740
+ // 找不到、路径不存在、不可执行是 not-installed,别的启动失败与握不上手都是 error。
27741
+ readiness: outcome.kind === "not-started" ? outcome.readiness : "error",
27262
27742
  auth: probeAuth(),
27263
27743
  ...outcome.detail === void 0 ? {} : { detail: outcome.detail },
27264
27744
  // 装着但握不上手时,界面照样画权限格。不报档就退回宿主四档,而那四档对这双手
@@ -27272,6 +27752,7 @@ var createGrokInstance = (config2, host) => {
27272
27752
  readiness: "ready",
27273
27753
  auth: probeAuth(),
27274
27754
  ...outcome.models === void 0 ? {} : { models: outcome.models },
27755
+ skills,
27275
27756
  ...GROK_NATIVE_MODE_REPORT
27276
27757
  };
27277
27758
  };
@@ -27315,13 +27796,15 @@ var createGrokInstance = (config2, host) => {
27315
27796
  owner: { bindingId: host.bindingId },
27316
27797
  ...Object.keys(env).length > 0 ? { env } : {}
27317
27798
  });
27318
- const { readable, writable } = nodeStreamsFromHandle2(handle);
27799
+ const { readable, writable, stderrTail } = nodeStreamsFromHandle2(handle);
27319
27800
  const resume = input2.discardResumeCursor === true ? void 0 : parseAcpResumeCursor(input2.resumeCursor, RESUME_SCHEMA_VERSION);
27320
27801
  const requestedModelId = modelIdFromSelection(input2.modelSelection);
27321
27802
  let initializeResult;
27803
+ const backgroundTasks = /* @__PURE__ */ new Map();
27322
27804
  const executor = createAcpExecutor({
27323
27805
  readable,
27324
27806
  writable,
27807
+ stderrTail,
27325
27808
  cwd,
27326
27809
  threadId: input2.threadId,
27327
27810
  provider: GROK_DRIVER_KIND,
@@ -27333,7 +27816,23 @@ var createGrokInstance = (config2, host) => {
27333
27816
  authMethodId: resolveGrokAuthMethodId,
27334
27817
  hooks: {
27335
27818
  promptSettlement: grokPromptSettlement,
27819
+ onToolCallUpdated: (update) => buildGrokBackgroundTaskEvents({
27820
+ tasks: backgroundTasks,
27821
+ toolCallId: update.toolCallId,
27822
+ rawInput: update.rawInput,
27823
+ rawOutput: update.rawOutput,
27824
+ toolCallStatus: update.status,
27825
+ turnId: update.turnId
27826
+ }),
27336
27827
  onExtensionRequest: async (method, params, ctx) => {
27828
+ if (XAI_EXIT_PLAN_MODE_METHODS.includes(method)) {
27829
+ const plan = parseXAiExitPlanModeParams(params);
27830
+ if (plan === void 0) {
27831
+ throw new Error(`malformed ${method} params`);
27832
+ }
27833
+ const resolution2 = await ctx.askUserInput([grokPlanQuestion(plan)]);
27834
+ return makeXAiExitPlanModeResponse(plan, resolution2);
27835
+ }
27337
27836
  if (!XAI_ASK_USER_QUESTION_METHODS.includes(method)) {
27338
27837
  throw new Error(`unsupported extension method ${method}`);
27339
27838
  }
@@ -27495,6 +27994,15 @@ var createGrokInstance = (config2, host) => {
27495
27994
  const booked = requireThread(threadId);
27496
27995
  await booked.executor.respondToUserInput(requestId, answers);
27497
27996
  },
27997
+ // 原生压缩:让 CLI 执行它自己宣告的 `/compact`。这不是用户消息——执行器不开轮次、
27998
+ // 不把命令吐的文本放进时间线,进度与结果只走压缩生命周期事件。
27999
+ compactContext: async (threadId) => {
28000
+ const booked = requireThread(threadId);
28001
+ if (booked.session.status === "running") {
28002
+ throw new Error("Cannot compact context while a turn is running");
28003
+ }
28004
+ await booked.executor.compact({ commandName: "compact" });
28005
+ },
27498
28006
  stopSession: async (threadId) => {
27499
28007
  const booked = threads.get(threadId);
27500
28008
  if (booked === void 0) return;
@@ -27546,9 +28054,9 @@ var createGrokInstance = (config2, host) => {
27546
28054
  const stdoutText = (async () => {
27547
28055
  const decoder = new TextDecoder();
27548
28056
  for await (const chunk of handle.stdout) {
27549
- const text = decoder.decode(chunk, { stream: true });
27550
- collected.push(text);
27551
- host.log.info("sign-in output", { text });
28057
+ const text2 = decoder.decode(chunk, { stream: true });
28058
+ collected.push(text2);
28059
+ host.log.info("sign-in output", { text: text2 });
27552
28060
  if (onCodeFound !== void 0 && hasDeviceCode(collected.join(""))) {
27553
28061
  onCodeFound();
27554
28062
  onCodeFound = void 0;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ycodium-ai/agent-grok",
3
- "version": "0.2.2634",
3
+ "version": "0.2.2664",
4
4
  "description": "Grok executor: grok CLI over Agent Client Protocol stdio.",
5
5
  "keywords": [
6
6
  "ycodium",
@@ -21,9 +21,9 @@
21
21
  "@types/node": "24.12.4",
22
22
  "esbuild": "0.28.1",
23
23
  "vite-plus": "0.2.2",
24
+ "@ycodium-ai/plugin-api": "0.2.2664",
24
25
  "ycodium-executor-testing": "0.0.0",
25
- "@ycodium-ai/plugin-api": "0.2.2634",
26
- "ycodium-acp-executor": "0.2.2634"
26
+ "ycodium-acp-executor": "0.2.2664"
27
27
  },
28
28
  "ycodium": {
29
29
  "plugin": "./dist/index.js",