@ycodium-ai/agent-grok 0.2.3766 → 0.2.3799

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 +135 -50
  2. package/package.json +3 -3
package/dist/index.js CHANGED
@@ -88,7 +88,10 @@ var GROK_CARD = {
88
88
  // 压缩生命周期事件报;CLI 没宣告这条命令时 `compactContext` 拒绝,不退回发文本。
89
89
  contextCompaction: "native",
90
90
  // 子 agent 经父会话上的 subagent_* 通知报成 task.*(grokSubagents.ts)。
91
- reportsSpawnedAgents: true
91
+ reportsSpawnedAgents: true,
92
+ // 插话:起会话前在配置里打开 `follow_up_behavior = "steer"`(grokFollowUpSteer.ts),运行中再发的 prompt
93
+ // 由 grok 在下一个工具调用结束时并进这一轮(1.0.70 ACP 模式实测);执行器不等前一条应答就写出去。
94
+ turnSteering: true
92
95
  },
93
96
  // 凭据由 CLI 自己保管在 `<GROK_HOME>/auth.json`,宿主读它只认 `/token`。
94
97
  credentialSources: [
@@ -26288,6 +26291,7 @@ function createExecutorPrompt({
26288
26291
  promptResults,
26289
26292
  settleToolSubagents
26290
26293
  }) {
26294
+ const ownOutcomes = /* @__PURE__ */ new Map();
26291
26295
  const settleTurn = (input2) => {
26292
26296
  if (!isLive(input2.expectedGeneration) || input2.record.finished) {
26293
26297
  return false;
@@ -26304,14 +26308,22 @@ function createExecutorPrompt({
26304
26308
  }
26305
26309
  closeActiveAssistantSegment(input2.turnId);
26306
26310
  shared.promptsInFlight = Math.max(0, shared.promptsInFlight - 1);
26307
- if (shared.promptsInFlight !== 0 || shared.activeTurnId !== input2.turnId) return false;
26311
+ if (shared.promptsInFlight !== 0 || shared.activeTurnId !== input2.turnId) {
26312
+ if (input2.foldedIn !== true) ownOutcomes.set(input2.turnId, input2);
26313
+ return false;
26314
+ }
26315
+ const outcome = (input2.foldedIn === true ? ownOutcomes.get(input2.turnId) : void 0) ?? input2;
26316
+ ownOutcomes.delete(input2.turnId);
26308
26317
  shared.activeTurnId = void 0;
26309
26318
  if (shared.status !== "disposed") {
26310
26319
  shared.status = "ready";
26311
26320
  }
26312
26321
  const interrupted = promptResults.hasInterrupted(input2.turnId);
26313
26322
  settleToolSubagents(
26314
- toolSubagentSettlementOfTurn(interrupted ? "cancelled" : input2.state, input2.failureCategory),
26323
+ toolSubagentSettlementOfTurn(
26324
+ interrupted ? "cancelled" : outcome.state,
26325
+ outcome.failureCategory
26326
+ ),
26315
26327
  input2.turnId
26316
26328
  );
26317
26329
  emit({
@@ -26321,10 +26333,10 @@ function createExecutorPrompt({
26321
26333
  threadId: options.threadId,
26322
26334
  turnId: input2.turnId,
26323
26335
  payload: {
26324
- state: interrupted ? "cancelled" : input2.state,
26325
- stopReason: interrupted ? "cancelled" : input2.stopReason,
26326
- ...input2.errorMessage === void 0 ? {} : { errorMessage: input2.errorMessage },
26327
- ...input2.state === "failed" ? failureCategoryField(input2.failureCategory) : {}
26336
+ state: interrupted ? "cancelled" : outcome.state,
26337
+ stopReason: interrupted ? "cancelled" : outcome.stopReason,
26338
+ ...outcome.errorMessage === void 0 ? {} : { errorMessage: outcome.errorMessage },
26339
+ ...outcome.state === "failed" ? failureCategoryField(outcome.failureCategory) : {}
26328
26340
  }
26329
26341
  });
26330
26342
  interruptedTurnIds.delete(input2.turnId);
@@ -26349,7 +26361,7 @@ function createExecutorPrompt({
26349
26361
  return await promptRpc;
26350
26362
  } else {
26351
26363
  const { promise: early, resolve: settleEarly } = Promise.withResolvers();
26352
- shared.pendingSettlement = {
26364
+ shared.pendingSettlements.set(promptKey, {
26353
26365
  promptKey,
26354
26366
  sessionId,
26355
26367
  settle: settleEarly,
@@ -26358,7 +26370,7 @@ function createExecutorPrompt({
26358
26370
  onError: () => promptResults.reject(record4.promptId),
26359
26371
  hasResult: () => record4.response !== void 0
26360
26372
  }
26361
- };
26373
+ });
26362
26374
  try {
26363
26375
  const outcome = await Promise.race([
26364
26376
  promptRpc.then((value) => {
@@ -26371,7 +26383,7 @@ function createExecutorPrompt({
26371
26383
  if (outcome.error !== void 0) throw outcome.error;
26372
26384
  return outcome.response;
26373
26385
  } finally {
26374
- shared.pendingSettlement = void 0;
26386
+ shared.pendingSettlements.delete(promptKey);
26375
26387
  void promptRpc.catch(() => void 0);
26376
26388
  }
26377
26389
  }
@@ -26521,7 +26533,9 @@ function createExecutorPrompt({
26521
26533
  settle2({
26522
26534
  turnId,
26523
26535
  stopReason,
26524
- state: response2.stopReason === "cancelled" ? "cancelled" : "completed"
26536
+ state: response2.stopReason === "cancelled" ? "cancelled" : "completed",
26537
+ // 立刻写出的插话没被打断却应答 cancelled:agent 把它并进了这一轮(grok 的 removedFromQueue)。
26538
+ foldedIn: response2.stopReason === "cancelled" && input2.expectedActiveTurnId !== void 0 && hooks.queuesPromptsWhileRunning === true
26525
26539
  });
26526
26540
  return {
26527
26541
  threadId: options.threadId,
@@ -26530,7 +26544,8 @@ function createExecutorPrompt({
26530
26544
  ...response2.usage !== void 0 && response2.usage !== null ? { usage: response2.usage } : {}
26531
26545
  };
26532
26546
  };
26533
- await promptMutex.acquire();
26547
+ const writesWhileRunning = input2.expectedActiveTurnId !== void 0 && hooks.queuesPromptsWhileRunning === true;
26548
+ if (!writesWhileRunning) await promptMutex.acquire();
26534
26549
  let response;
26535
26550
  try {
26536
26551
  failIfDisposed();
@@ -26612,7 +26627,7 @@ function createExecutorPrompt({
26612
26627
  await options.beforeApplyResult();
26613
26628
  }
26614
26629
  } finally {
26615
- promptMutex.release();
26630
+ if (!writesWhileRunning) promptMutex.release();
26616
26631
  notifyPromptSettled();
26617
26632
  }
26618
26633
  if (!isLive(started.generation)) {
@@ -27655,28 +27670,29 @@ function createExecutorSettlement({
27655
27670
  if (settledPromptKeys.length > SETTLED_PROMPT_KEY_LIMIT) settledPromptKeys.shift();
27656
27671
  };
27657
27672
  const abortPendingSettlement = (reason, failureCategory) => {
27658
- const waiting = shared.pendingSettlement;
27659
- if (waiting === void 0) return;
27660
- if (waiting.hasResult?.()) return;
27661
- shared.pendingSettlement = void 0;
27662
- waiting.settle({
27663
- error: withFailureCategory(
27664
- new AcpExecutorRequestError("session/prompt", reason),
27665
- failureCategory
27666
- )
27667
- });
27673
+ for (const [key, waiting] of shared.pendingSettlements) {
27674
+ if (waiting.hasResult?.()) continue;
27675
+ shared.pendingSettlements.delete(key);
27676
+ waiting.settle({
27677
+ error: withFailureCategory(
27678
+ new AcpExecutorRequestError("session/prompt", reason),
27679
+ failureCategory
27680
+ )
27681
+ });
27682
+ }
27668
27683
  };
27669
27684
  const settleFromNotification = (method, params) => {
27670
27685
  const hook = hooks.promptSettlement;
27671
- if (hook === void 0 || shared.pendingSettlement === void 0) return false;
27686
+ if (hook === void 0 || shared.pendingSettlements.size === 0) return false;
27672
27687
  const signal = hook.fromNotification(method, params);
27673
27688
  if (signal === void 0) return false;
27674
- const waiting = shared.pendingSettlement;
27675
- if (signal.sessionId !== waiting.sessionId) return false;
27676
- if (signal.promptKey !== void 0 && signal.promptKey !== waiting.promptKey) {
27677
- return settledPromptKeys.includes(signal.promptKey);
27689
+ const waiting = signal.promptKey === void 0 ? [...shared.pendingSettlements.values()].find(
27690
+ (candidate) => candidate.sessionId === signal.sessionId
27691
+ ) : shared.pendingSettlements.get(signal.promptKey);
27692
+ if (waiting === void 0 || waiting.sessionId !== signal.sessionId) {
27693
+ return signal.promptKey !== void 0 && settledPromptKeys.includes(signal.promptKey);
27678
27694
  }
27679
- shared.pendingSettlement = void 0;
27695
+ shared.pendingSettlements.delete(waiting.promptKey);
27680
27696
  rememberSettledPromptKey(waiting.promptKey);
27681
27697
  if (signal.outcome === "error") {
27682
27698
  waiting.onError?.();
@@ -28270,7 +28286,7 @@ function createAcpExecutor(options) {
28270
28286
  startInFlight: void 0,
28271
28287
  activeTurnId: void 0,
28272
28288
  promptsInFlight: 0,
28273
- pendingSettlement: void 0,
28289
+ pendingSettlements: /* @__PURE__ */ new Map(),
28274
28290
  manualCompaction: void 0,
28275
28291
  assistantSegment: { nextSegmentIndex: 0 },
28276
28292
  modeState: void 0,
@@ -29238,30 +29254,38 @@ var grokTurnUsageReport = (promptResponse) => {
29238
29254
  ...sessionId === void 0 ? {} : { sessionId }
29239
29255
  };
29240
29256
  };
29257
+ var mergeReports = (left, right) => {
29258
+ if (left === void 0 || right === void 0) return left ?? right;
29259
+ const totals = {
29260
+ uncachedInputTokens: left.totals.uncachedInputTokens + right.totals.uncachedInputTokens,
29261
+ cachedInputTokens: left.totals.cachedInputTokens + right.totals.cachedInputTokens,
29262
+ cacheCreationTokens: left.totals.cacheCreationTokens + right.totals.cacheCreationTokens,
29263
+ outputTokens: left.totals.outputTokens + right.totals.outputTokens,
29264
+ reasoningTokens: left.totals.reasoningTokens + right.totals.reasoningTokens
29265
+ };
29266
+ const larger = sumOf(left.totals) >= sumOf(right.totals) ? left : right;
29267
+ return { ...larger, totals };
29268
+ };
29241
29269
  var createGrokTurnUsageCollector = (base) => {
29242
29270
  const pastKeys = [];
29243
- let promptKey;
29244
- let report;
29245
- let seen = false;
29271
+ const turn = /* @__PURE__ */ new Map();
29272
+ const answered = /* @__PURE__ */ new Set();
29273
+ let latestKey;
29246
29274
  let wake;
29247
- const mark = () => {
29248
- seen = true;
29249
- wake?.();
29250
- };
29275
+ const allAnswered = () => [...turn.keys()].every((key) => answered.has(key));
29251
29276
  const observe = (response) => {
29252
- const key = base.responseKey?.(response);
29253
- if (key !== void 0 && key !== promptKey && pastKeys.includes(key)) return;
29254
- report = grokTurnUsageReport(response) ?? report;
29255
- mark();
29277
+ const key = base.responseKey?.(response) ?? latestKey;
29278
+ if (key === void 0) return;
29279
+ if (!turn.has(key) && pastKeys.includes(key)) return;
29280
+ turn.set(key, grokTurnUsageReport(response) ?? turn.get(key));
29281
+ answered.add(key);
29282
+ if (allAnswered()) wake?.();
29256
29283
  };
29257
29284
  const settlement = {
29258
29285
  ...base,
29259
29286
  requestFields: (key) => {
29260
- if (promptKey !== void 0) pastKeys.push(promptKey);
29261
- if (pastKeys.length > 16) pastKeys.shift();
29262
- promptKey = key;
29263
- report = void 0;
29264
- seen = false;
29287
+ turn.set(key, void 0);
29288
+ latestKey = key;
29265
29289
  return base.requestFields(key);
29266
29290
  },
29267
29291
  responseKey: (response) => {
@@ -29271,7 +29295,7 @@ var createGrokTurnUsageCollector = (base) => {
29271
29295
  };
29272
29296
  const attach = async (event) => {
29273
29297
  if (event.type !== "turn.completed" || !isRecord8(event.payload)) return event;
29274
- if (event.payload["state"] === "completed" && !seen) {
29298
+ if (event.payload["state"] === "completed" && !allAnswered()) {
29275
29299
  await new Promise((resolve2) => {
29276
29300
  const timer = setTimeout(resolve2, USAGE_GRACE_MS);
29277
29301
  wake = () => {
@@ -29281,8 +29305,12 @@ var createGrokTurnUsageCollector = (base) => {
29281
29305
  });
29282
29306
  wake = void 0;
29283
29307
  }
29284
- const taken = report;
29285
- report = void 0;
29308
+ let taken;
29309
+ for (const report of turn.values()) taken = mergeReports(taken, report);
29310
+ for (const key of turn.keys()) pastKeys.push(key);
29311
+ while (pastKeys.length > 16) pastKeys.shift();
29312
+ turn.clear();
29313
+ answered.clear();
29286
29314
  return taken === void 0 ? event : { ...event, payload: { ...event.payload, tokenUsage: taken } };
29287
29315
  };
29288
29316
  return { settlement, attach };
@@ -30489,6 +30517,55 @@ function createGrokModelCatalog(input2) {
30489
30517
  return { readCliVersion, refreshModelCatalog, refreshForSend };
30490
30518
  }
30491
30519
 
30520
+ // src/grokFollowUpSteer.ts
30521
+ import * as NodeFS5 from "node:fs";
30522
+ import * as NodePath8 from "node:path";
30523
+ var SETTING = 'follow_up_behavior = "steer"';
30524
+ function grokFollowUpSteerIsOn(text4) {
30525
+ const lines = text4.split(/\r?\n/).map((line) => line.trim());
30526
+ const headers = lines.flatMap((line, index) => line === "[ui]" ? [index] : []);
30527
+ const header = headers[0];
30528
+ if (headers.length !== 1 || header === void 0) return false;
30529
+ for (const line of lines.slice(header + 1)) {
30530
+ if (line.startsWith("[")) return false;
30531
+ if (line === SETTING) return true;
30532
+ }
30533
+ return false;
30534
+ }
30535
+ function ensureGrokFollowUpSteer(homePath) {
30536
+ const path = NodePath8.join(homePath, "config.toml");
30537
+ try {
30538
+ return grokFollowUpSteerIsOn(NodeFS5.readFileSync(path, "utf8")) ? "on" : "off";
30539
+ } catch (error62) {
30540
+ if (error62.code !== "ENOENT") throw error62;
30541
+ }
30542
+ NodeFS5.mkdirSync(homePath, { recursive: true });
30543
+ try {
30544
+ NodeFS5.writeFileSync(path, `[ui]
30545
+ ${SETTING}
30546
+ `, { encoding: "utf8", flag: "wx" });
30547
+ return "created";
30548
+ } catch (error62) {
30549
+ if (error62.code !== "EEXIST") throw error62;
30550
+ return grokFollowUpSteerIsOn(NodeFS5.readFileSync(path, "utf8")) ? "on" : "off";
30551
+ }
30552
+ }
30553
+ function prepareGrokFollowUpSteer(homePath, log, threadId) {
30554
+ try {
30555
+ if (ensureGrokFollowUpSteer(homePath) === "off" && threadId !== void 0) {
30556
+ log.info(
30557
+ "grok follow-up steering is off in config.toml; mid-turn messages run after the current request",
30558
+ { threadId }
30559
+ );
30560
+ }
30561
+ } catch (error62) {
30562
+ log.warn("could not read or create the grok config", {
30563
+ ...threadId === void 0 ? {} : { threadId },
30564
+ cause: error62 instanceof Error ? error62.message : String(error62)
30565
+ });
30566
+ }
30567
+ }
30568
+
30492
30569
  // src/grokInstance.ts
30493
30570
  var RESUME_SCHEMA_VERSION = 1;
30494
30571
  var yieldTick = () => new Promise((resolve2) => {
@@ -30603,6 +30680,7 @@ var createGrokInstance = (config2, host) => {
30603
30680
  detail: "Grok program path is not configured"
30604
30681
  };
30605
30682
  }
30683
+ prepareGrokFollowUpSteer(resolveAccountHomePath(recipe, recipe.homePath), host.log);
30606
30684
  const env = spawnEnv();
30607
30685
  const version2 = await raceAbort3(modelCatalog.readCliVersion(signal), signal);
30608
30686
  if (signal?.aborted) throw new Error("aborted");
@@ -30691,6 +30769,11 @@ var createGrokInstance = (config2, host) => {
30691
30769
  if (disposed) throw new Error("instance disposed");
30692
30770
  throwIfStartAborted(signal);
30693
30771
  }
30772
+ prepareGrokFollowUpSteer(
30773
+ resolveAccountHomePath(recipe, recipe.homePath),
30774
+ host.log,
30775
+ input2.threadId
30776
+ );
30694
30777
  const env = spawnEnv();
30695
30778
  const effort = modelSelectionStringOption(input2.modelSelection, GROK_REASONING_OPTION_ID);
30696
30779
  const baseArgs = tokenizeLaunchArgs(recipe.launchArgs);
@@ -30737,6 +30820,8 @@ var createGrokInstance = (config2, host) => {
30737
30820
  authMethodId: resolveGrokAuthMethodId,
30738
30821
  hooks: {
30739
30822
  promptSettlement: turnUsage.settlement,
30823
+ // 运行中再收到的 prompt 由 grok 自己排队、在下一个工具调用结束时并进这一轮(配合上面写的配置)。
30824
+ queuesPromptsWhileRunning: true,
30740
30825
  modelDefaultEffort: grokModelDefaultEffort,
30741
30826
  // 派发子 agent 的调用由子 agent 名单接管,不再画成普通工具。
30742
30827
  onToolCallUpdated: (update) => ({
@@ -30978,7 +31063,7 @@ var executors = [
30978
31063
  ];
30979
31064
  var index_default = definePlugin({
30980
31065
  id: "agent-grok",
30981
- apiVersion: 56,
31066
+ apiVersion: 60,
30982
31067
  executors,
30983
31068
  activate(ctx) {
30984
31069
  ctx.command({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ycodium-ai/agent-grok",
3
- "version": "0.2.3766",
3
+ "version": "0.2.3799",
4
4
  "description": "Grok executor: grok CLI over Agent Client Protocol stdio.",
5
5
  "keywords": [
6
6
  "ycodium",
@@ -21,8 +21,8 @@
21
21
  "@types/node": "24.12.4",
22
22
  "esbuild": "0.28.1",
23
23
  "vite-plus": "0.2.2",
24
- "@ycodium-ai/plugin-api": "0.2.3766",
25
- "ycodium-acp-executor": "0.2.3766",
24
+ "@ycodium-ai/plugin-api": "0.2.3799",
25
+ "ycodium-acp-executor": "0.2.3799",
26
26
  "ycodium-executor-testing": "0.0.0"
27
27
  },
28
28
  "ycodium": {