lody 0.77.2 → 0.78.0

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.
@@ -487,8 +487,10 @@ const normalizeCodeCollabFileIndexSignalValue = (value) => {
487
487
  return value.v === 1 && isNonNegativeInteger(value.r) ? { v: 1, r: value.r } : void 0;
488
488
  };
489
489
  const isCodeCollabFileIndexSignalKey = (key) => key.length === 1 && key[0] === CODE_COLLAB_FILE_INDEX_SIGNAL_KEY[0];
490
- function readCodeCollabFileIndexFromFlock(flock) {
490
+ const isCorruptedCodeCollabWorkspacePath = (workspacePath) => workspacePath.includes("\uFFFD");
491
+ function scanCodeCollabFileIndexFlock(flock) {
491
492
  const fileIndex = {};
493
+ const corruptedPaths = [];
492
494
  for (const row of flock.scan()) {
493
495
  if (row.value === void 0) {
494
496
  continue;
@@ -497,13 +499,17 @@ function readCodeCollabFileIndexFromFlock(flock) {
497
499
  if (typeof workspacePath !== "string" || workspacePath.length === 0 || extra.length > 0) {
498
500
  continue;
499
501
  }
502
+ if (isCorruptedCodeCollabWorkspacePath(workspacePath)) {
503
+ corruptedPaths.push(workspacePath);
504
+ continue;
505
+ }
500
506
  const parsed = normalizeCodeCollabFileIndexValue(row.value);
501
507
  if (!parsed) {
502
508
  continue;
503
509
  }
504
510
  fileIndex[workspacePath] = parsed;
505
511
  }
506
- return fileIndex;
512
+ return { fileIndex, corruptedPaths };
507
513
  }
508
514
  function readCodeCollabFileIndexSignalFromFlock(flock) {
509
515
  for (const row of flock.scan()) {
@@ -531,8 +537,12 @@ function writeCodeCollabFileIndexSignalToFlock(flock, revision, nowMs) {
531
537
  return true;
532
538
  }
533
539
  function writeCodeCollabFileIndexToFlock(flock, next, nowMs) {
534
- const previous = readCodeCollabFileIndexFromFlock(flock);
540
+ const { fileIndex: previous, corruptedPaths } = scanCodeCollabFileIndexFlock(flock);
535
541
  let changed = false;
542
+ for (const workspacePath of corruptedPaths) {
543
+ flock.delete(codeCollabFileIndexKeyForWorkspacePath(workspacePath), nowMs);
544
+ changed = true;
545
+ }
536
546
  for (const workspacePath of Object.keys(previous)) {
537
547
  if (Object.prototype.hasOwnProperty.call(next, workspacePath)) {
538
548
  continue;
@@ -49,11 +49,12 @@ import "node:worker_threads";
49
49
  import "node:url";
50
50
  import "node:console";
51
51
  import "node:dns";
52
- import "./directory-handle-BBQKxZ5F.js";
52
+ import "./directory-handle-OPjjCl3O.js";
53
53
  import "./diff-line-counts-bmGB_T-n.js";
54
54
  import "./package-qnDHmQq4.js";
55
55
  import "node:string_decoder";
56
56
  import "node:stream/promises";
57
+ import "./runtime-manifest-Fr5DEH54.js";
57
58
  import "async_hooks";
58
59
  import "module";
59
60
  import "./diff-worker-task-8tVIjLTu.js";
@@ -75,7 +76,7 @@ let __tla = Promise.all([
75
76
  }
76
77
  })()
77
78
  ]).then(async () => {
78
- const reviewViewerVersion = "0.77.2";
79
+ const reviewViewerVersion = "0.78.0";
79
80
  const reviewViewerSha256 = "41b6ffd0acff2c355e190b604776a89138f9598a1772d09390a6d5af514e71bb";
80
81
  const reviewViewerFileName = "standalone.html";
81
82
  const DEFAULT_CDN_BASES = [
@@ -0,0 +1,9 @@
1
+ const officialRuntime = { "version": "1.0.0" };
2
+ const privateWireContract = { "permissionNotification": "x.ai/yolo_mode_changed", "sessionConfigMeta": "x.ai/sessionConfig", "sessionNotification": "x.ai/session_notification", "sessionUpdateNotification": "x.ai/session/update", "turnCompletedUpdate": "turn_completed", "sessionInfoRequest": "x.ai/session/info", "billingRequest": "x.ai/billing", "weeklyUsagePeriodType": "USAGE_PERIOD_TYPE_WEEKLY", "lodyUsageNotification": "acp_ext:session_usage_update", "lodyRateLimitsNotification": "acp_ext:session_rate_limits", "reasoningEffortMeta": "reasoningEffort", "autoPermissionMode": true };
3
+ const runtimeManifest = {
4
+ officialRuntime,
5
+ privateWireContract
6
+ };
7
+ export {
8
+ runtimeManifest as r
9
+ };
@@ -1,6 +1,6 @@
1
1
  import { opendir } from "node:fs/promises";
2
2
  import path__default from "node:path";
3
- import { x as buildCodeCollabFileIndexState, D as scanGitDirectoryEntries, F as joinWorkspacePath, G as isInsideGitWorktree, A as computeAllChanges, H as pathSegmentComparisonKey, E as closeDirectoryQuietly } from "./chunks/directory-handle-BBQKxZ5F.js";
3
+ import { x as buildCodeCollabFileIndexState, D as scanGitDirectoryEntries, F as joinWorkspacePath, G as isInsideGitWorktree, A as computeAllChanges, H as pathSegmentComparisonKey, E as closeDirectoryQuietly } from "./chunks/directory-handle-OPjjCl3O.js";
4
4
  import "./chunks/schemas-Du3qLZPS.js";
5
5
  import "node:child_process";
6
6
  import "node:util";
@@ -0,0 +1,599 @@
1
+ import { spawn } from "node:child_process";
2
+ import { createInterface } from "node:readline";
3
+ import { r as runtimeManifest } from "./chunks/runtime-manifest-Fr5DEH54.js";
4
+ const contract = runtimeManifest.privateWireContract;
5
+ const PERMISSION_MODES = {
6
+ ask: { permission_mode: "ask", yolo_mode: false, auto_mode: false },
7
+ auto: { permission_mode: "auto", yolo_mode: false, auto_mode: true },
8
+ "always-approve": {
9
+ permission_mode: "always-approve",
10
+ yolo_mode: true,
11
+ auto_mode: false
12
+ }
13
+ };
14
+ const INTERACTION_TO_RUNTIME = { agent: "default", plan: "plan" };
15
+ const INTERACTION_FROM_RUNTIME = { default: "agent", plan: "plan", ask: "plan" };
16
+ const LEGACY_INTERACTION_ALIASES = { ask: "plan" };
17
+ const INTERNAL_REQUESTS = {
18
+ context: { contractKey: "sessionInfoRequest", params: (sessionId) => ({ sessionId }) },
19
+ billing: { contractKey: "billingRequest", params: () => ({}) }
20
+ };
21
+ const USD_TICKS_PER_USD = 1e10;
22
+ const MAX_TRACKED_PROMPTS = 256;
23
+ const FIVE_HOUR_WINDOW_MINS = 5 * 60;
24
+ const SEVEN_DAY_WINDOW_MINS = 7 * 24 * 60;
25
+ function logicalExtensionMethod(method) {
26
+ return typeof method === "string" && method.startsWith("_") ? method.slice(1) : method;
27
+ }
28
+ function wireExtensionMethod(method) {
29
+ return `_${method}`;
30
+ }
31
+ function extensionNotification(contractKey, params) {
32
+ return { jsonrpc: "2.0", method: wireExtensionMethod(contract[contractKey]), params };
33
+ }
34
+ function optionalNonnegativeNumber(value) {
35
+ return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : void 0;
36
+ }
37
+ function nonnegativeNumber(value) {
38
+ return optionalNonnegativeNumber(value) ?? 0;
39
+ }
40
+ function optionalAmount(value) {
41
+ return optionalNonnegativeNumber(value?.val ?? value);
42
+ }
43
+ function billingUsagePercent(config, period) {
44
+ const fallback = period === config.currentPeriod ? config : {};
45
+ const explicit = optionalNonnegativeNumber(period.creditUsagePercent) ?? optionalNonnegativeNumber(fallback.creditUsagePercent);
46
+ if (explicit !== void 0) return explicit;
47
+ const limit = optionalAmount(period.monthlyLimit ?? fallback.monthlyLimit);
48
+ const used = optionalAmount(
49
+ period.totalUsed ?? period.includedUsed ?? period.used ?? fallback.totalUsed ?? fallback.used
50
+ );
51
+ if (limit && used !== void 0) return used / limit * 100;
52
+ const hasOfficialZeroUsage = config.isUnifiedBillingUser === true && config.currentPeriod?.type === contract.weeklyUsagePeriodType && optionalAmount(config.onDemandCap) === 0 && optionalAmount(config.onDemandUsed) === 0 && optionalAmount(config.prepaidBalance) === 0;
53
+ if (hasOfficialZeroUsage) return 0;
54
+ return void 0;
55
+ }
56
+ function rateLimitsPayload(planName, window) {
57
+ const isFiveHour = window?.windowDurationMins === FIVE_HOUR_WINDOW_MINS;
58
+ const isSevenDay = window?.windowDurationMins === SEVEN_DAY_WINDOW_MINS;
59
+ return {
60
+ schemaVersion: 2,
61
+ planName,
62
+ limitName: "Grok Build",
63
+ limitId: "grok",
64
+ windows: window ? [window] : [],
65
+ fiveHour: isFiveHour ? window.usedPercent : null,
66
+ sevenDay: isSevenDay ? window.usedPercent : null,
67
+ fiveHourResetAt: isFiveHour ? window.resetsAt : null,
68
+ sevenDayResetAt: isSevenDay ? window.resetsAt : null,
69
+ ...window ? {} : { apiUnavailable: true }
70
+ };
71
+ }
72
+ function normalizeUsageModel(usage, usageIsIncomplete) {
73
+ const inputTokens = nonnegativeNumber(usage?.inputTokens);
74
+ const outputTokens = nonnegativeNumber(usage?.outputTokens);
75
+ const cacheReadInputTokens = nonnegativeNumber(usage?.cachedReadTokens);
76
+ const cacheCreationInputTokens = nonnegativeNumber(usage?.cacheCreationTokens);
77
+ const reasoningOutputTokens = nonnegativeNumber(usage?.reasoningTokens);
78
+ const costUsdTicks = optionalNonnegativeNumber(usage?.costUsdTicks);
79
+ const hasTrustworthyCost = !usageIsIncomplete && usage?.costIsPartial !== true && costUsdTicks !== void 0;
80
+ return {
81
+ // Grok's ACP totals include cache buckets in inputTokens and reasoning in
82
+ // outputTokens. Lody stores disjoint buckets and adds them for reporting.
83
+ inputTokens: Math.max(0, inputTokens - cacheReadInputTokens - cacheCreationInputTokens),
84
+ outputTokens: Math.max(0, outputTokens - reasoningOutputTokens),
85
+ cacheReadInputTokens,
86
+ cacheCreationInputTokens,
87
+ reasoningOutputTokens,
88
+ ...hasTrustworthyCost ? { costUSD: costUsdTicks / USD_TICKS_PER_USD } : {}
89
+ };
90
+ }
91
+ function normalizePromptUsage(promptUsage) {
92
+ if (!promptUsage || typeof promptUsage !== "object") return void 0;
93
+ const usageIsIncomplete = promptUsage.usageIsIncomplete === true;
94
+ const usage = normalizeUsageModel(promptUsage, usageIsIncomplete);
95
+ const modelUsage = {};
96
+ if (promptUsage.modelUsage && typeof promptUsage.modelUsage === "object") {
97
+ for (const [modelId, model] of Object.entries(promptUsage.modelUsage)) {
98
+ if (model && typeof model === "object") {
99
+ modelUsage[modelId] = normalizeUsageModel(model, usageIsIncomplete);
100
+ }
101
+ }
102
+ }
103
+ return {
104
+ usage,
105
+ ...Object.keys(modelUsage).length ? { modelUsage } : {}
106
+ };
107
+ }
108
+ function usageNotification(promptUsage) {
109
+ const params = normalizePromptUsage(promptUsage);
110
+ return params && extensionNotification("lodyUsageNotification", params);
111
+ }
112
+ function normalizeBillingRateLimits(billing) {
113
+ if (!billing || typeof billing !== "object") return void 0;
114
+ const config = billing.config;
115
+ if (!config || typeof config !== "object") return void 0;
116
+ const latestHistory = Array.isArray(config.history) ? config.history.at(-1) : void 0;
117
+ const period = config.currentPeriod ?? latestHistory ?? config;
118
+ let usedPercent = billingUsagePercent(config, period);
119
+ if (usedPercent !== void 0) usedPercent = Math.min(100, usedPercent);
120
+ const start = period?.start ?? period?.billingPeriodStart ?? config.billingPeriodStart;
121
+ const end = period?.end ?? period?.billingPeriodEnd ?? config.billingPeriodEnd;
122
+ const startMs = typeof start === "string" ? Date.parse(start) : Number.NaN;
123
+ const endMs = typeof end === "string" ? Date.parse(end) : Number.NaN;
124
+ const measuredWindowDurationMins = Number.isFinite(startMs) && Number.isFinite(endMs) && endMs > startMs ? Math.round((endMs - startMs) / 6e4) : null;
125
+ const windowDurationMins = period?.type === contract.weeklyUsagePeriodType ? SEVEN_DAY_WINDOW_MINS : measuredWindowDurationMins;
126
+ const resetsAt = Number.isFinite(endMs) ? endMs : null;
127
+ const tier = billing.subscriptionTier ?? billing.subscription_tier;
128
+ const planName = typeof tier === "string" && tier.trim() ? tier : null;
129
+ if (usedPercent === void 0) {
130
+ if (!planName && windowDurationMins === null && resetsAt === null) return void 0;
131
+ return rateLimitsPayload(planName, null);
132
+ }
133
+ return rateLimitsPayload(planName, { usedPercent, windowDurationMins, resetsAt });
134
+ }
135
+ function rateLimitsNotification(billing) {
136
+ const params = normalizeBillingRateLimits(billing);
137
+ return params && extensionNotification("lodyRateLimitsNotification", params);
138
+ }
139
+ function contextUsageNotification(sessionId, context) {
140
+ const size = nonnegativeNumber(context?.total);
141
+ const used = nonnegativeNumber(context?.used);
142
+ if (size <= 0) return void 0;
143
+ return {
144
+ jsonrpc: "2.0",
145
+ method: "session/update",
146
+ params: {
147
+ sessionId,
148
+ update: { sessionUpdate: "usage_update", size, used }
149
+ }
150
+ };
151
+ }
152
+ function unwrapExtensionResult(result) {
153
+ if (!result || typeof result !== "object") return result;
154
+ return Object.prototype.hasOwnProperty.call(result, "result") ? result.result : result;
155
+ }
156
+ function rememberPrompt(set, promptId) {
157
+ if (!promptId) return true;
158
+ if (set.has(promptId)) return false;
159
+ set.add(promptId);
160
+ if (set.size > MAX_TRACKED_PROMPTS) {
161
+ set.delete(set.values().next().value);
162
+ }
163
+ return true;
164
+ }
165
+ function errorResponse(id, message) {
166
+ return { jsonrpc: "2.0", id, error: { code: -32602, message } };
167
+ }
168
+ function unsupportedConfigOption(id, configId) {
169
+ return {
170
+ toRuntime: [],
171
+ toClient: [errorResponse(id, `Unsupported Grok config option: ${configId}`)]
172
+ };
173
+ }
174
+ function optionName(value) {
175
+ if (value === "xhigh") return "X-High";
176
+ return value.charAt(0).toUpperCase() + value.slice(1);
177
+ }
178
+ function legacyOptions(result) {
179
+ const options = result?._meta?.[contract.sessionConfigMeta]?.options;
180
+ return Array.isArray(options) ? options : [];
181
+ }
182
+ function readReasoningEfforts(model) {
183
+ const raw = model?._meta?.reasoningEfforts ?? model?._meta?.reasoning_efforts;
184
+ if (!Array.isArray(raw)) return [];
185
+ return raw.map((item) => typeof item === "string" ? item : item?.id ?? item?.reasoningEffort).filter((item) => typeof item === "string");
186
+ }
187
+ function selectOption(id, name, description, currentValue, options, category) {
188
+ return {
189
+ id,
190
+ name,
191
+ description,
192
+ category,
193
+ type: "select",
194
+ currentValue,
195
+ options
196
+ };
197
+ }
198
+ function permissionNotification(clientIdentifier, mode) {
199
+ const mapped = PERMISSION_MODES[mode];
200
+ if (!mapped) return void 0;
201
+ return extensionNotification("permissionNotification", { clientIdentifier, ...mapped });
202
+ }
203
+ class GrokAcpCompatibilityProxy {
204
+ constructor() {
205
+ this.sessions = /* @__PURE__ */ new Map();
206
+ this.pending = /* @__PURE__ */ new Map();
207
+ this.permissionModes = /* @__PURE__ */ new Map();
208
+ this.nextInternalRequestId = Number.MAX_SAFE_INTEGER;
209
+ }
210
+ internalRequest(kind, sessionId) {
211
+ while (this.pending.has(this.nextInternalRequestId)) this.nextInternalRequestId -= 1;
212
+ const id = this.nextInternalRequestId;
213
+ this.nextInternalRequestId -= 1;
214
+ this.pending.set(id, { kind, sessionId });
215
+ const { contractKey, params } = INTERNAL_REQUESTS[kind];
216
+ return {
217
+ jsonrpc: "2.0",
218
+ id,
219
+ method: wireExtensionMethod(contract[contractKey]),
220
+ params: params(sessionId)
221
+ };
222
+ }
223
+ usageRefreshRequests(sessionId) {
224
+ return [this.internalRequest("context", sessionId), this.internalRequest("billing", sessionId)];
225
+ }
226
+ usageRefreshRequestsForPrompt(state, promptId) {
227
+ if (!rememberPrompt(state.usageRefreshPromptIds, promptId)) return [];
228
+ return this.usageRefreshRequests(state.sessionId);
229
+ }
230
+ usageForPrompt(state, promptId, promptUsage) {
231
+ const notification = usageNotification(promptUsage);
232
+ if (!notification) return void 0;
233
+ if (!rememberPrompt(state.usagePromptIds, promptId)) return void 0;
234
+ return notification;
235
+ }
236
+ handleClient(message) {
237
+ if (!message || typeof message !== "object") return { toRuntime: [message], toClient: [] };
238
+ const params = message.params ?? {};
239
+ if (message.method === "session/new" || message.method === "session/load" || message.method === "session/resume") {
240
+ const clientIdentifier = params._meta?.clientIdentifier;
241
+ if (message.id !== void 0) {
242
+ this.pending.set(message.id, {
243
+ kind: "session",
244
+ method: message.method,
245
+ sessionId: params.sessionId,
246
+ clientIdentifier
247
+ });
248
+ }
249
+ return { toRuntime: [message], toClient: [] };
250
+ }
251
+ if (message.method === "session/prompt") {
252
+ if (message.id !== void 0) {
253
+ this.pending.set(message.id, {
254
+ kind: "prompt",
255
+ sessionId: params.sessionId
256
+ });
257
+ }
258
+ return { toRuntime: [message], toClient: [] };
259
+ }
260
+ if (message.method !== "session/set_config_option") {
261
+ return { toRuntime: [message], toClient: [] };
262
+ }
263
+ const { sessionId, configId, value } = params;
264
+ const state = this.sessions.get(sessionId);
265
+ if (!state || typeof value !== "string") {
266
+ return {
267
+ toRuntime: [],
268
+ toClient: [errorResponse(message.id, "Unknown Grok session or invalid config value")]
269
+ };
270
+ }
271
+ if (configId === "permission_mode") {
272
+ const notification = permissionNotification(state.clientIdentifier, value);
273
+ if (!notification || !state.clientIdentifier) {
274
+ return {
275
+ toRuntime: [],
276
+ toClient: [
277
+ errorResponse(
278
+ message.id,
279
+ "Grok permission mode requires the current Lody clientIdentifier"
280
+ )
281
+ ]
282
+ };
283
+ }
284
+ this.permissionModes.set(state.clientIdentifier, value);
285
+ return {
286
+ toRuntime: [notification],
287
+ toClient: [
288
+ {
289
+ jsonrpc: "2.0",
290
+ id: message.id,
291
+ result: { configOptions: this.configOptions(state) }
292
+ }
293
+ ]
294
+ };
295
+ }
296
+ let translated;
297
+ let effectiveValue = value;
298
+ if (configId === "reasoning_effort") {
299
+ if (!state.currentModelId || !state.reasoningEfforts.includes(value)) {
300
+ return {
301
+ toRuntime: [],
302
+ toClient: [errorResponse(message.id, "Unsupported Grok reasoning effort")]
303
+ };
304
+ }
305
+ translated = {
306
+ jsonrpc: "2.0",
307
+ id: message.id,
308
+ method: "session/set_model",
309
+ params: {
310
+ sessionId,
311
+ modelId: state.currentModelId,
312
+ _meta: {
313
+ [contract.reasoningEffortMeta]: value
314
+ }
315
+ }
316
+ };
317
+ } else if (configId === "model") {
318
+ translated = {
319
+ jsonrpc: "2.0",
320
+ id: message.id,
321
+ method: "session/set_model",
322
+ params: { sessionId, modelId: value }
323
+ };
324
+ } else if (configId === "interaction_mode") {
325
+ effectiveValue = LEGACY_INTERACTION_ALIASES[value] ?? value;
326
+ const modeId = INTERACTION_TO_RUNTIME[effectiveValue];
327
+ if (!modeId) return unsupportedConfigOption(message.id, configId);
328
+ translated = {
329
+ jsonrpc: "2.0",
330
+ id: message.id,
331
+ method: "session/set_mode",
332
+ params: { sessionId, modeId }
333
+ };
334
+ } else {
335
+ return unsupportedConfigOption(message.id, configId);
336
+ }
337
+ this.pending.set(message.id, {
338
+ kind: "config",
339
+ sessionId,
340
+ configId,
341
+ value: effectiveValue
342
+ });
343
+ return { toRuntime: [translated], toClient: [] };
344
+ }
345
+ handleRuntime(message) {
346
+ if (!message || typeof message !== "object") return { toRuntime: [], toClient: [message] };
347
+ if (typeof message.method === "string") return this.handleRuntimeMethod(message);
348
+ const pending = this.pending.get(message.id);
349
+ if (!pending) return { toRuntime: [], toClient: [message] };
350
+ this.pending.delete(message.id);
351
+ if (pending.kind === "context") {
352
+ if (message.error) return { toRuntime: [], toClient: [] };
353
+ const sessionInfo = unwrapExtensionResult(message.result);
354
+ const contextNotification = contextUsageNotification(pending.sessionId, sessionInfo?.context);
355
+ return {
356
+ toRuntime: [],
357
+ toClient: contextNotification ? [contextNotification] : []
358
+ };
359
+ }
360
+ if (pending.kind === "billing") {
361
+ const notification = message.error ? extensionNotification("lodyRateLimitsNotification", rateLimitsPayload(null, null)) : rateLimitsNotification(unwrapExtensionResult(message.result));
362
+ return {
363
+ toRuntime: [],
364
+ toClient: notification ? [notification] : []
365
+ };
366
+ }
367
+ if (message.error) return { toRuntime: [], toClient: [message] };
368
+ if (pending.kind === "session") {
369
+ const result = message.result ?? {};
370
+ const sessionId = result.sessionId ?? pending.sessionId;
371
+ if (!sessionId) return { toRuntime: [], toClient: [message] };
372
+ const state2 = this.stateFromSessionResponse(sessionId, pending.clientIdentifier, result);
373
+ this.sessions.set(sessionId, state2);
374
+ return {
375
+ toRuntime: this.usageRefreshRequests(sessionId),
376
+ toClient: [
377
+ {
378
+ ...message,
379
+ result: { ...result, configOptions: this.configOptions(state2) }
380
+ }
381
+ ]
382
+ };
383
+ }
384
+ if (pending.kind === "prompt") {
385
+ const state2 = this.sessions.get(pending.sessionId);
386
+ if (!state2) return { toRuntime: [], toClient: [message] };
387
+ const meta = message.result?._meta;
388
+ const promptId = meta?.promptId ?? meta?.requestId;
389
+ const usage = this.usageForPrompt(state2, promptId, meta?.usage);
390
+ return {
391
+ toRuntime: this.usageRefreshRequestsForPrompt(state2, promptId),
392
+ toClient: usage ? [message, usage] : [message]
393
+ };
394
+ }
395
+ const state = this.sessions.get(pending.sessionId);
396
+ if (!state) return { toRuntime: [], toClient: [message] };
397
+ if (pending.configId === "model") state.currentModelId = pending.value;
398
+ if (pending.configId === "reasoning_effort") state.reasoningEffort = pending.value;
399
+ if (pending.configId === "interaction_mode") state.interactionMode = pending.value;
400
+ return {
401
+ toRuntime: [],
402
+ toClient: [
403
+ {
404
+ jsonrpc: "2.0",
405
+ id: message.id,
406
+ result: { configOptions: this.configOptions(state) }
407
+ }
408
+ ]
409
+ };
410
+ }
411
+ handleRuntimeMethod(message) {
412
+ const passthrough = { toRuntime: [], toClient: [message] };
413
+ const sessionId = message.params?.sessionId;
414
+ const update = message.params?.update;
415
+ const state = this.sessions.get(sessionId);
416
+ if (!state) return passthrough;
417
+ const logicalMethod = logicalExtensionMethod(message.method);
418
+ if (logicalMethod === contract.sessionUpdateNotification && update?.sessionUpdate === contract.turnCompletedUpdate) {
419
+ if (message.params?._meta?.isReplay === true) return passthrough;
420
+ const promptId = update.prompt_id ?? update.promptId;
421
+ const usage = this.usageForPrompt(state, promptId, update.usage);
422
+ return {
423
+ toRuntime: this.usageRefreshRequestsForPrompt(state, promptId),
424
+ toClient: usage ? [message, usage] : [message]
425
+ };
426
+ }
427
+ if (message.method === "session/update" && update?.sessionUpdate === "current_mode_update") {
428
+ const interactionMode = INTERACTION_FROM_RUNTIME[update.currentModeId];
429
+ if (interactionMode) state.interactionMode = interactionMode;
430
+ return passthrough;
431
+ }
432
+ if (logicalMethod === contract.sessionNotification && update?.sessionUpdate === "model_changed") {
433
+ const modelId = update.model_id ?? update.modelId;
434
+ const reasoningEffort = update.reasoning_effort ?? update.reasoningEffort;
435
+ if (typeof modelId === "string") state.currentModelId = modelId;
436
+ if (typeof reasoningEffort === "string") state.reasoningEffort = reasoningEffort;
437
+ return {
438
+ toRuntime: [this.internalRequest("context", sessionId)],
439
+ toClient: [message]
440
+ };
441
+ }
442
+ return passthrough;
443
+ }
444
+ stateFromSessionResponse(sessionId, clientIdentifier, result) {
445
+ const old = this.sessions.get(sessionId);
446
+ const legacy = legacyOptions(result);
447
+ const legacyModels = legacy.filter((option) => option.category === "model");
448
+ const legacyEfforts = legacy.filter((option) => option.category === "mode");
449
+ const availableModels = result.models?.availableModels ?? [];
450
+ const currentModelId = result.models?.currentModelId ?? legacyModels.find((option) => option.selected)?.id ?? old?.currentModelId;
451
+ const currentModel = availableModels.find((model) => model.modelId === currentModelId);
452
+ const metadataEfforts = readReasoningEfforts(currentModel);
453
+ const reasoningEfforts = metadataEfforts.length ? metadataEfforts : legacyEfforts.map((option) => option.id);
454
+ return {
455
+ sessionId,
456
+ clientIdentifier: clientIdentifier ?? old?.clientIdentifier,
457
+ interactionMode: INTERACTION_FROM_RUNTIME[result.modes?.currentModeId] ?? old?.interactionMode ?? "agent",
458
+ currentModelId,
459
+ models: availableModels.length ? availableModels : legacyModels.map((option) => ({
460
+ modelId: option.id,
461
+ name: option.label,
462
+ description: option.description
463
+ })),
464
+ reasoningEfforts,
465
+ reasoningEffort: legacyEfforts.find((option) => option.selected)?.id ?? old?.reasoningEffort ?? reasoningEfforts[0],
466
+ usagePromptIds: old?.usagePromptIds ?? /* @__PURE__ */ new Set(),
467
+ usageRefreshPromptIds: old?.usageRefreshPromptIds ?? /* @__PURE__ */ new Set()
468
+ };
469
+ }
470
+ configOptions(state) {
471
+ const modes = [
472
+ {
473
+ value: "agent",
474
+ name: "Agent",
475
+ description: "Use tools and make changes when needed"
476
+ },
477
+ {
478
+ value: "plan",
479
+ name: "Plan",
480
+ description: "Plan without modifying the workspace"
481
+ }
482
+ ];
483
+ const permissions = [
484
+ {
485
+ value: "ask",
486
+ name: "Ask Every Time",
487
+ description: "Request approval for protected actions"
488
+ },
489
+ {
490
+ value: "always-approve",
491
+ name: "Always Approve",
492
+ description: "Approve protected actions automatically"
493
+ }
494
+ ];
495
+ {
496
+ permissions.splice(1, 0, {
497
+ value: "auto",
498
+ name: "Auto",
499
+ description: "Let Grok decide when approval is required"
500
+ });
501
+ }
502
+ const options = [
503
+ selectOption(
504
+ "interaction_mode",
505
+ "Interaction Mode",
506
+ "Controls how Grok works",
507
+ state.interactionMode,
508
+ modes,
509
+ "mode"
510
+ ),
511
+ selectOption(
512
+ "permission_mode",
513
+ "Permission Mode",
514
+ "Controls protected tool approvals",
515
+ this.permissionModes.get(state.clientIdentifier) ?? "ask",
516
+ permissions,
517
+ "_permission"
518
+ )
519
+ ];
520
+ if (state.currentModelId) {
521
+ options.push(
522
+ selectOption(
523
+ "model",
524
+ "Model",
525
+ "Select the model used for this session",
526
+ state.currentModelId,
527
+ state.models.map((model) => ({
528
+ value: model.modelId,
529
+ name: model.name || model.modelId,
530
+ description: model.description ?? null
531
+ })),
532
+ "model"
533
+ )
534
+ );
535
+ }
536
+ if (state.reasoningEfforts.length && state.reasoningEffort) {
537
+ options.push(
538
+ selectOption(
539
+ "reasoning_effort",
540
+ "Reasoning Effort",
541
+ "Controls how much reasoning the model performs",
542
+ state.reasoningEffort,
543
+ state.reasoningEfforts.map((value) => ({
544
+ value,
545
+ name: optionName(value),
546
+ description: null
547
+ })),
548
+ "thought_level"
549
+ )
550
+ );
551
+ }
552
+ return options;
553
+ }
554
+ }
555
+ const grokPath = process.env.GROK_PATH;
556
+ if (!grokPath) {
557
+ console.error("GROK_PATH must point to the official Grok runtime");
558
+ process.exit(1);
559
+ }
560
+ const child = spawn(grokPath, ["agent", "stdio"], {
561
+ env: { ...process.env, GROK_DISABLE_AUTOUPDATER: "1" },
562
+ stdio: ["pipe", "pipe", "inherit"]
563
+ });
564
+ const proxy = new GrokAcpCompatibilityProxy();
565
+ function write(stream, message) {
566
+ stream.write(`${JSON.stringify(message)}
567
+ `);
568
+ }
569
+ createInterface({ input: process.stdin }).on("line", (line) => {
570
+ try {
571
+ const output = proxy.handleClient(JSON.parse(line));
572
+ for (const message of output.toRuntime) write(child.stdin, message);
573
+ for (const message of output.toClient) write(process.stdout, message);
574
+ } catch (error) {
575
+ console.error(
576
+ `Invalid ACP client message: ${error instanceof Error ? error.message : String(error)}`
577
+ );
578
+ }
579
+ });
580
+ createInterface({ input: child.stdout }).on("line", (line) => {
581
+ try {
582
+ const output = proxy.handleRuntime(JSON.parse(line));
583
+ for (const message of output.toRuntime) write(child.stdin, message);
584
+ for (const message of output.toClient) write(process.stdout, message);
585
+ } catch (error) {
586
+ console.error(
587
+ `Invalid Grok runtime message: ${error instanceof Error ? error.message : String(error)}`
588
+ );
589
+ }
590
+ });
591
+ process.stdin.on("end", () => child.stdin.end());
592
+ child.on("error", (error) => {
593
+ console.error(`Failed to launch official Grok runtime: ${error.message}`);
594
+ process.exitCode = 1;
595
+ });
596
+ child.on("exit", (code, signal) => {
597
+ if (signal) process.kill(process.pid, signal);
598
+ else process.exitCode = code ?? 1;
599
+ });