lody 0.92.1 → 0.93.2

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.
@@ -38,10 +38,10 @@ import { AsyncLocalStorage } from "async_hooks";
38
38
  import * as os$2 from "node:os";
39
39
  import os__default from "node:os";
40
40
  import { isIPv4, isIPv6 } from "net";
41
- import { R as RequestError, a as CreateElicitationResponse, n as ndJsonStream, b as agent$1, m as methods } from "./chunks/acp-DBqkNziA.js";
41
+ import { R as RequestError, a as CreateElicitationResponse, n as ndJsonStream, b as agent$1, m as methods } from "./chunks/acp-DCoAUZc7.js";
42
42
  import { L as LODY_EXTENSION_METHODS } from "./chunks/methods-CvEDMahD.js";
43
43
  import { p as packageJson } from "./chunks/package-DlotPnLa.js";
44
- import { s as string, b as boolean, c as array, o as object, n as number, l as literal } from "./chunks/schemas-Du3qLZPS.js";
44
+ import { s as string, b as boolean, c as array, o as object, n as number, l as literal } from "./chunks/schemas-C-NzK2uQ.js";
45
45
  import { WritableStream, ReadableStream as ReadableStream$1 } from "node:stream/web";
46
46
  (async () => {
47
47
  var One = Object.create;
package/dist/codex-acp.js CHANGED
@@ -3,5 +3,5 @@
3
3
  console.error("CODEX_PATH is required for the bundled Codex ACP adapter.");
4
4
  process.exit(1);
5
5
  }
6
- await import("./chunks/index-Cy4lnlSp.js");
6
+ await import("./chunks/index-Bxge7nr7.js");
7
7
  })();
@@ -1,9 +1,10 @@
1
1
  import { randomUUID, createHash } from "node:crypto";
2
2
  import { isAbsolute } from "node:path";
3
3
  import { Readable, Writable } from "node:stream";
4
- import { n as ndJsonStream, A as AgentSideConnection, R as RequestError, P as PROTOCOL_VERSION } from "./chunks/acp-DBqkNziA.js";
5
- import { j as DEEPSEEK_HARNESS_BASE_URL_ENV, k as DEEPSEEK_HARNESS_API_KEY_ENV, b as ACP_EXTENSION_DSH_VERSION, a as DEEPSEEK_HARNESS_AGENT_PRESETS } from "./chunks/profile-DLuYzIs8.js";
6
- import "./chunks/schemas-Du3qLZPS.js";
4
+ import { n as ndJsonStream, A as AgentSideConnection, R as RequestError, P as PROTOCOL_VERSION } from "./chunks/acp-DCoAUZc7.js";
5
+ import { j as DEEPSEEK_HARNESS_BASE_URL_ENV, k as DEEPSEEK_HARNESS_API_KEY_ENV, a as DEEPSEEK_HARNESS_AGENT_PRESETS, b as ACP_EXTENSION_DSH_VERSION } from "./chunks/profile-Dv5auBPt.js";
6
+ import { c as createPlanModeConfigOption, L as LODY_PLAN_MODE_CONFIG_ID } from "./chunks/plan-mode-BLOER-fv.js";
7
+ import "./chunks/schemas-C-NzK2uQ.js";
7
8
  const name = "acp-extension-dsh";
8
9
  const inject = [
9
10
  "agents",
@@ -289,6 +290,9 @@ function installModelSelection(agentContext, selection) {
289
290
  };
290
291
  });
291
292
  }
293
+ function planModeService(record) {
294
+ return record.agent.ctx.get("planMode");
295
+ }
292
296
  function configOptions(record) {
293
297
  const options = [
294
298
  {
@@ -334,6 +338,11 @@ function configOptions(record) {
334
338
  }))
335
339
  }
336
340
  ];
341
+ const plan = planModeService(record);
342
+ if (plan) {
343
+ const state = plan.get(record.agent);
344
+ options.push(createPlanModeConfigOption(state.pending ?? state.active));
345
+ }
337
346
  const model = record.models.find((candidate) => candidate.id === record.selection.current.model);
338
347
  if (model?.reasoning && record.selection.current.reasoningEffort) {
339
348
  options.push({
@@ -710,6 +719,12 @@ function apply(ctx, rawConfig) {
710
719
  const record = sessions.get(session.header.id);
711
720
  if (!record || record.agent.session !== session)
712
721
  return;
722
+ if (event.type === "plan/mode") {
723
+ enqueueNotification(record, {
724
+ sessionId: record.agent.session.id,
725
+ update: { sessionUpdate: "config_option_update", configOptions: configOptions(record) }
726
+ });
727
+ }
713
728
  if (PERMISSION_EVENT_TYPES.has(event.type))
714
729
  schedulePermissionSync(record);
715
730
  try {
@@ -827,6 +842,14 @@ function apply(ctx, rawConfig) {
827
842
  record.permissionOptions = permissionState(ctx, permissionMode);
828
843
  };
829
844
  const setConfigOption = (record, params) => {
845
+ if (params.configId === LODY_PLAN_MODE_CONFIG_ID) {
846
+ const plan = planModeService(record);
847
+ if (!plan || typeof params.value !== "boolean") {
848
+ throw invalidParams("Plan mode requires a supported Agent preset and a boolean value");
849
+ }
850
+ plan.set(record.agent, params.value);
851
+ return { configOptions: configOptions(record) };
852
+ }
830
853
  const value = requireSelectValue(params);
831
854
  if (params.configId === MODE_CONFIG_ID) {
832
855
  setPermissionMode(record, value);
@@ -1,7 +1,7 @@
1
1
  import { opendir } from "node:fs/promises";
2
2
  import path__default from "node:path";
3
- import { z as buildCodeCollabFileIndexState, F as scanGitDirectoryEntries, H as joinWorkspacePath, I as isInsideGitWorktree, D as computeAllChanges, J as pathSegmentComparisonKey, G as closeDirectoryQuietly } from "./chunks/directory-handle-Bp47LF5g.js";
4
- import "./chunks/schemas-Du3qLZPS.js";
3
+ import { z as buildCodeCollabFileIndexState, F as scanGitDirectoryEntries, H as joinWorkspacePath, I as isInsideGitWorktree, D as computeAllChanges, J as pathSegmentComparisonKey, G as closeDirectoryQuietly } from "./chunks/directory-handle-35P9qcGk.js";
4
+ import "./chunks/schemas-C-NzK2uQ.js";
5
5
  import "node:child_process";
6
6
  import "node:util";
7
7
  import "./chunks/diff-line-counts-bmGB_T-n.js";
package/dist/grok-acp.js CHANGED
@@ -1,11 +1,159 @@
1
1
  import { createInterface } from "node:readline";
2
- import { r as runtimeManifest } from "./chunks/runtime-manifest-DnyvD_EC.js";
2
+ import { r as runtimeManifest } from "./chunks/runtime-manifest-FeOT7MvW.js";
3
3
  import { L as LODY_EXTENSION_METHODS } from "./chunks/methods-CvEDMahD.js";
4
+ import { L as LODY_PLAN_MODE_CONFIG_ID, c as createPlanModeConfigOption } from "./chunks/plan-mode-BLOER-fv.js";
4
5
  import { spawn } from "node:child_process";
6
+ function update(sessionId, value) {
7
+ return { jsonrpc: "2.0", method: "session/update", params: { sessionId, update: value } };
8
+ }
9
+ class GrokPlanReviewBridge {
10
+ constructor() {
11
+ this.pending = /* @__PURE__ */ new Map();
12
+ }
13
+ start(message, sessions, capabilities) {
14
+ const wrapped = message.params?.method === "x.ai/exit_plan_mode";
15
+ const params = wrapped ? message.params.params : message.params;
16
+ const { sessionId, toolCallId, planContent } = params ?? {};
17
+ if (message.id === void 0) return { toRuntime: [], toClient: [] };
18
+ if (!sessions.has(sessionId) || typeof toolCallId !== "string" || !toolCallId || planContent != null && typeof planContent !== "string") {
19
+ return {
20
+ toRuntime: [
21
+ {
22
+ jsonrpc: "2.0",
23
+ id: message.id,
24
+ error: { code: -32602, message: "Invalid plan approval request" }
25
+ }
26
+ ],
27
+ toClient: []
28
+ };
29
+ }
30
+ const plan = planContent ?? "";
31
+ const review = {
32
+ id: message.id,
33
+ sessionId,
34
+ toolCallId,
35
+ wrapped,
36
+ feedbackSupported: Boolean(capabilities.elicitation?.form),
37
+ phase: "approval"
38
+ };
39
+ this.pending.set(message.id, review);
40
+ const separatePlan = capabilities.plan != null;
41
+ const toolCall = {
42
+ toolCallId,
43
+ title: "Implement this plan?",
44
+ kind: "switch_mode",
45
+ status: "pending",
46
+ rawInput: { plan },
47
+ content: separatePlan && plan.trim() ? [] : [
48
+ {
49
+ type: "content",
50
+ content: {
51
+ type: "text",
52
+ text: plan.trim() ? plan : "No plan written yet."
53
+ }
54
+ }
55
+ ]
56
+ };
57
+ const toClient = [];
58
+ if (separatePlan)
59
+ toClient.push(
60
+ update(sessionId, {
61
+ sessionUpdate: "plan_update",
62
+ plan: { type: "markdown", planId: toolCallId, content: plan }
63
+ })
64
+ );
65
+ toClient.push(update(sessionId, { sessionUpdate: "tool_call", ...toolCall }));
66
+ toClient.push({
67
+ jsonrpc: "2.0",
68
+ id: message.id,
69
+ method: "session/request_permission",
70
+ params: {
71
+ sessionId,
72
+ toolCall,
73
+ options: [
74
+ { optionId: "approve", name: "Implement this plan", kind: "allow_once" },
75
+ { optionId: "revise", name: "Keep planning", kind: "reject_once" },
76
+ { optionId: "abandon", name: "Exit without implementing this plan", kind: "reject_once" }
77
+ ]
78
+ }
79
+ });
80
+ return { toRuntime: [], toClient };
81
+ }
82
+ response(message) {
83
+ if (typeof message.method === "string") return void 0;
84
+ const review = this.pending.get(message.id);
85
+ if (!review) return void 0;
86
+ if (review.phase === "feedback") {
87
+ const feedback = !message.error && message.result?.action === "accept" && typeof message.result.content?.feedback === "string" ? message.result.content.feedback : void 0;
88
+ return this.finish(review, "cancelled", feedback);
89
+ }
90
+ const selected = message.result?.outcome?.outcome === "selected" ? message.result.outcome.optionId : void 0;
91
+ if (!message.error && selected === "approve") return this.finish(review, "approved");
92
+ if (!message.error && selected === "abandon") return this.finish(review, "abandoned");
93
+ if (!message.error && selected === "revise" && review.feedbackSupported) {
94
+ review.phase = "feedback";
95
+ return {
96
+ toRuntime: [],
97
+ toClient: [
98
+ {
99
+ jsonrpc: "2.0",
100
+ id: review.id,
101
+ method: "elicitation/create",
102
+ params: {
103
+ sessionId: review.sessionId,
104
+ toolCallId: `${review.toolCallId}:feedback`,
105
+ mode: "form",
106
+ message: "What should change in the plan?",
107
+ requestedSchema: {
108
+ type: "object",
109
+ properties: { feedback: { type: "string", title: "Feedback" } }
110
+ },
111
+ _meta: { lody: { elicitation: { version: 1 } } }
112
+ }
113
+ }
114
+ ]
115
+ };
116
+ }
117
+ return this.finish(review, "cancelled");
118
+ }
119
+ finish(review, outcome, feedback) {
120
+ this.pending.delete(review.id);
121
+ const result = { outcome, ...feedback ? { feedback } : {} };
122
+ const toClient = [
123
+ update(review.sessionId, {
124
+ sessionUpdate: "tool_call_update",
125
+ toolCallId: review.toolCallId,
126
+ status: "completed",
127
+ rawOutput: result
128
+ })
129
+ ];
130
+ if (review.phase === "feedback")
131
+ toClient.push(
132
+ update(review.sessionId, {
133
+ sessionUpdate: "tool_call_update",
134
+ toolCallId: `${review.toolCallId}:feedback`,
135
+ status: "completed"
136
+ })
137
+ );
138
+ return {
139
+ toRuntime: [{ jsonrpc: "2.0", id: review.id, result: review.wrapped ? { result } : result }],
140
+ toClient
141
+ };
142
+ }
143
+ cancel(sessionId) {
144
+ const output = { toRuntime: [], toClient: [] };
145
+ for (const review of [...this.pending.values()]) {
146
+ if (review.sessionId !== sessionId) continue;
147
+ const result = this.finish(review, "cancelled");
148
+ output.toRuntime.push(...result.toRuntime);
149
+ output.toClient.push(...result.toClient);
150
+ }
151
+ return output;
152
+ }
153
+ }
5
154
  const contract = runtimeManifest.privateWireContract;
6
155
  const PERMISSION_MODES = {
7
156
  ask: { permission_mode: "ask", yolo_mode: false, auto_mode: false },
8
- auto: { permission_mode: "auto", yolo_mode: false, auto_mode: true },
9
157
  "always-approve": {
10
158
  permission_mode: "always-approve",
11
159
  yolo_mode: true,
@@ -249,7 +397,8 @@ function readLodySessionConfigOption(meta, configId) {
249
397
  }
250
398
  function translateSessionStart(message) {
251
399
  const params = message.params ?? {};
252
- const permissionMode = readLodySessionConfigOption(params._meta, "permission_mode");
400
+ const requestedMode = readLodySessionConfigOption(params._meta, "permission_mode");
401
+ const permissionMode = requestedMode === "auto" ? "ask" : requestedMode;
253
402
  const mapped = typeof permissionMode === "string" ? PERMISSION_MODES[permissionMode] : void 0;
254
403
  if (!mapped) return { message, permissionMode: void 0 };
255
404
  const translated = {
@@ -259,7 +408,7 @@ function translateSessionStart(message) {
259
408
  _meta: {
260
409
  ...stripLodySessionConfig(params._meta),
261
410
  yoloMode: mapped.yolo_mode,
262
- autoMode: mapped.auto_mode
411
+ autoMode: false
263
412
  }
264
413
  }
265
414
  };
@@ -271,6 +420,8 @@ function translateSessionStart(message) {
271
420
  class GrokAcpCompatibilityProxy {
272
421
  constructor({ deferSessionResponseUntilModelSnapshot = false } = {}) {
273
422
  this.sessions = /* @__PURE__ */ new Map();
423
+ this.planReviews = new GrokPlanReviewBridge();
424
+ this.clientCapabilities = {};
274
425
  this.pending = /* @__PURE__ */ new Map();
275
426
  this.pendingSessionResponses = /* @__PURE__ */ new Map();
276
427
  this.clientVisibleSessions = /* @__PURE__ */ new Set();
@@ -413,8 +564,15 @@ class GrokAcpCompatibilityProxy {
413
564
  }
414
565
  handleClient(message) {
415
566
  if (!message || typeof message !== "object") return { toRuntime: [message], toClient: [] };
567
+ const planResponse = this.planReviews.response(message);
568
+ if (planResponse) return planResponse;
416
569
  const params = message.params ?? {};
570
+ if (message.method === "session/cancel") {
571
+ const cancelled = this.planReviews.cancel(params.sessionId);
572
+ return { toRuntime: [...cancelled.toRuntime, message], toClient: cancelled.toClient };
573
+ }
417
574
  if (message.method === "initialize" && message.id !== void 0) {
575
+ this.clientCapabilities = params.clientCapabilities ?? {};
418
576
  this.pending.set(message.id, { kind: "initialize" });
419
577
  return { toRuntime: [message], toClient: [] };
420
578
  }
@@ -457,13 +615,16 @@ class GrokAcpCompatibilityProxy {
457
615
  }
458
616
  const { sessionId, configId, value } = params;
459
617
  const state = this.sessions.get(sessionId);
460
- if (!state || typeof value !== "string") {
618
+ if (!state || (configId === LODY_PLAN_MODE_CONFIG_ID ? typeof value !== "boolean" : typeof value !== "string")) {
461
619
  return {
462
620
  toRuntime: [],
463
621
  toClient: [errorResponse(message.id, "Unknown Grok session or invalid config value")]
464
622
  };
465
623
  }
466
624
  if (configId === "permission_mode") {
625
+ if (!Object.hasOwn(PERMISSION_MODES, value)) {
626
+ return unsupportedConfigOption(message.id, configId);
627
+ }
467
628
  const notification = permissionNotification(state.clientIdentifier, value);
468
629
  if (!notification || !state.clientIdentifier) {
469
630
  return {
@@ -516,8 +677,8 @@ class GrokAcpCompatibilityProxy {
516
677
  method: "session/set_model",
517
678
  params: { sessionId, modelId: value }
518
679
  };
519
- } else if (configId === "interaction_mode") {
520
- effectiveValue = LEGACY_INTERACTION_ALIASES[value] ?? value;
680
+ } else if (configId === LODY_PLAN_MODE_CONFIG_ID || configId === "interaction_mode") {
681
+ effectiveValue = configId === LODY_PLAN_MODE_CONFIG_ID ? value ? "plan" : "agent" : LEGACY_INTERACTION_ALIASES[value] ?? value;
521
682
  const modeId = INTERACTION_TO_RUNTIME[effectiveValue];
522
683
  if (!modeId) return unsupportedConfigOption(message.id, configId);
523
684
  translated = {
@@ -632,7 +793,8 @@ class GrokAcpCompatibilityProxy {
632
793
  if (!state) return { toRuntime: [], toClient: [message] };
633
794
  if (pending.configId === "model") this.applyCurrentModel(state, pending.value);
634
795
  if (pending.configId === "reasoning_effort") state.reasoningEffort = pending.value;
635
- if (pending.configId === "interaction_mode") state.interactionMode = pending.value;
796
+ if (pending.configId === LODY_PLAN_MODE_CONFIG_ID || pending.configId === "interaction_mode")
797
+ state.interactionMode = pending.value;
636
798
  return {
637
799
  toRuntime: [],
638
800
  toClient: [
@@ -647,30 +809,67 @@ class GrokAcpCompatibilityProxy {
647
809
  handleRuntimeMethod(message) {
648
810
  const passthrough = { toRuntime: [], toClient: [message] };
649
811
  const logicalMethod = logicalExtensionMethod(message.method);
812
+ if (logicalMethod === contract.planApprovalRequest) {
813
+ return this.planReviews.start(message, this.sessions, this.clientCapabilities);
814
+ }
650
815
  if (logicalMethod === contract.modelsUpdateNotification) {
651
816
  return this.handleModelSnapshot(message);
652
817
  }
653
818
  const sessionId = message.params?.sessionId;
654
- const update = message.params?.update;
819
+ const update2 = message.params?.update;
655
820
  const state = this.sessions.get(sessionId);
656
821
  if (!state) return passthrough;
657
- if (logicalMethod === contract.sessionUpdateNotification && update?.sessionUpdate === contract.turnCompletedUpdate) {
822
+ if (message.method === "session/request_permission" && message.id !== void 0 && this.permissionModes.get(state.clientIdentifier) === "always-approve" && !message.params?._meta?.lody?.elicitation && message.params?.toolCall?.kind !== "switch_mode" && Array.isArray(message.params?.options)) {
823
+ const options = message.params.options;
824
+ const isSelectable = (option) => typeof option?.optionId === "string";
825
+ const allow = options.find((option) => isSelectable(option) && option.kind === "allow_once") ?? options.find((option) => isSelectable(option) && option.kind === "allow_always");
826
+ if (allow) {
827
+ return {
828
+ toRuntime: [
829
+ {
830
+ jsonrpc: "2.0",
831
+ id: message.id,
832
+ result: { outcome: { outcome: "selected", optionId: allow.optionId } }
833
+ }
834
+ ],
835
+ toClient: []
836
+ };
837
+ }
838
+ }
839
+ if (logicalMethod === contract.sessionUpdateNotification && update2?.sessionUpdate === contract.turnCompletedUpdate) {
658
840
  if (message.params?._meta?.isReplay === true) return passthrough;
659
- const promptId = update.prompt_id ?? update.promptId;
660
- const usage = this.usageForPrompt(state, promptId, update.usage);
841
+ const promptId = update2.prompt_id ?? update2.promptId;
842
+ const usage = this.usageForPrompt(state, promptId, update2.usage);
661
843
  return {
662
844
  toRuntime: this.usageRefreshRequestsForPrompt(state, promptId),
663
845
  toClient: usage ? [message, usage] : [message]
664
846
  };
665
847
  }
666
- if (message.method === "session/update" && update?.sessionUpdate === "current_mode_update") {
667
- const interactionMode = INTERACTION_FROM_RUNTIME[update.currentModeId];
668
- if (interactionMode) state.interactionMode = interactionMode;
669
- return passthrough;
848
+ if (message.method === "session/update" && update2?.sessionUpdate === "current_mode_update") {
849
+ const interactionMode = INTERACTION_FROM_RUNTIME[update2.currentModeId];
850
+ if (!interactionMode) return passthrough;
851
+ state.interactionMode = interactionMode;
852
+ return {
853
+ toRuntime: [],
854
+ toClient: [
855
+ message,
856
+ {
857
+ jsonrpc: "2.0",
858
+ method: "session/update",
859
+ params: {
860
+ sessionId,
861
+ update: {
862
+ sessionUpdate: "config_option_update",
863
+ configOptions: this.configOptions(state)
864
+ }
865
+ }
866
+ }
867
+ ]
868
+ };
670
869
  }
671
- if (logicalMethod === contract.sessionNotification && update?.sessionUpdate === "model_changed") {
672
- const modelId = update.model_id ?? update.modelId;
673
- const reasoningEffort = update.reasoning_effort ?? update.reasoningEffort;
870
+ if (logicalMethod === contract.sessionNotification && update2?.sessionUpdate === "model_changed") {
871
+ const modelId = update2.model_id ?? update2.modelId;
872
+ const reasoningEffort = update2.reasoning_effort ?? update2.reasoningEffort;
674
873
  if (typeof modelId === "string") {
675
874
  this.applyCurrentModel(state, modelId, reasoningEffort);
676
875
  }
@@ -720,18 +919,6 @@ class GrokAcpCompatibilityProxy {
720
919
  };
721
920
  }
722
921
  configOptions(state) {
723
- const modes = [
724
- {
725
- value: "agent",
726
- name: "Agent",
727
- description: "Use tools and make changes when needed"
728
- },
729
- {
730
- value: "plan",
731
- name: "Plan",
732
- description: "Plan without modifying the workspace"
733
- }
734
- ];
735
922
  const permissions = [
736
923
  {
737
924
  value: "ask",
@@ -744,22 +931,8 @@ class GrokAcpCompatibilityProxy {
744
931
  description: "Approve protected actions automatically"
745
932
  }
746
933
  ];
747
- {
748
- permissions.splice(1, 0, {
749
- value: "auto",
750
- name: "Auto",
751
- description: "Let Grok decide when approval is required"
752
- });
753
- }
754
934
  const options = [
755
- selectOption(
756
- "interaction_mode",
757
- "Interaction Mode",
758
- "Controls how Grok works",
759
- state.interactionMode,
760
- modes,
761
- "mode"
762
- ),
935
+ createPlanModeConfigOption(state.interactionMode === "plan"),
763
936
  selectOption(
764
937
  "permission_mode",
765
938
  "Permission Mode",