@greppleai/grip 0.0.1 → 0.1.3

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 (197) hide show
  1. package/CHANGELOG.md +93 -0
  2. package/README.md +13 -7
  3. package/dist/bun/cli.d.ts.map +1 -1
  4. package/dist/bun/cli.js +4 -2
  5. package/dist/bun/cli.js.map +1 -1
  6. package/dist/bun/embedded-assets.d.ts +3 -0
  7. package/dist/bun/embedded-assets.d.ts.map +1 -0
  8. package/dist/bun/embedded-assets.js +52 -0
  9. package/dist/bun/embedded-assets.js.map +1 -0
  10. package/dist/cli/args.d.ts.map +1 -1
  11. package/dist/cli/args.js +1 -0
  12. package/dist/cli/args.js.map +1 -1
  13. package/dist/config.d.ts.map +1 -1
  14. package/dist/config.js +3 -3
  15. package/dist/config.js.map +1 -1
  16. package/dist/core/agent-session.d.ts.map +1 -1
  17. package/dist/core/agent-session.js +17 -2
  18. package/dist/core/agent-session.js.map +1 -1
  19. package/dist/core/remote-workspace-protocol/contract.d.ts +13 -4
  20. package/dist/core/remote-workspace-protocol/contract.d.ts.map +1 -1
  21. package/dist/core/remote-workspace-protocol/contract.js +17 -1
  22. package/dist/core/remote-workspace-protocol/contract.js.map +1 -1
  23. package/dist/core/remote-workspace-protocol/guest-shared-client.d.ts +16 -0
  24. package/dist/core/remote-workspace-protocol/guest-shared-client.d.ts.map +1 -0
  25. package/dist/core/remote-workspace-protocol/guest-shared-client.js +54 -0
  26. package/dist/core/remote-workspace-protocol/guest-shared-client.js.map +1 -0
  27. package/dist/core/remote-workspace-protocol/guest-shared-outbound-scheduler.d.ts +23 -0
  28. package/dist/core/remote-workspace-protocol/guest-shared-outbound-scheduler.d.ts.map +1 -0
  29. package/dist/core/remote-workspace-protocol/guest-shared-outbound-scheduler.js +112 -0
  30. package/dist/core/remote-workspace-protocol/guest-shared-outbound-scheduler.js.map +1 -0
  31. package/dist/core/remote-workspace-protocol/guest-shared-uplink.d.ts +30 -0
  32. package/dist/core/remote-workspace-protocol/guest-shared-uplink.d.ts.map +1 -0
  33. package/dist/core/remote-workspace-protocol/guest-shared-uplink.js +243 -0
  34. package/dist/core/remote-workspace-protocol/guest-shared-uplink.js.map +1 -0
  35. package/dist/core/remote-workspace-protocol/lima-owner-directory.d.ts +3 -0
  36. package/dist/core/remote-workspace-protocol/lima-owner-directory.d.ts.map +1 -0
  37. package/dist/core/remote-workspace-protocol/lima-owner-directory.js +31 -0
  38. package/dist/core/remote-workspace-protocol/lima-owner-directory.js.map +1 -0
  39. package/dist/core/remote-workspace-protocol/lima-owner-lock.d.ts +9 -0
  40. package/dist/core/remote-workspace-protocol/lima-owner-lock.d.ts.map +1 -0
  41. package/dist/core/remote-workspace-protocol/lima-owner-lock.js +89 -0
  42. package/dist/core/remote-workspace-protocol/lima-owner-lock.js.map +1 -0
  43. package/dist/core/remote-workspace-protocol/session.d.ts +2 -0
  44. package/dist/core/remote-workspace-protocol/session.d.ts.map +1 -1
  45. package/dist/core/remote-workspace-protocol/session.js +3 -0
  46. package/dist/core/remote-workspace-protocol/session.js.map +1 -1
  47. package/dist/core/remote-workspace-protocol/shared-transport.d.ts +71 -0
  48. package/dist/core/remote-workspace-protocol/shared-transport.d.ts.map +1 -0
  49. package/dist/core/remote-workspace-protocol/shared-transport.js +68 -0
  50. package/dist/core/remote-workspace-protocol/shared-transport.js.map +1 -0
  51. package/dist/core/sandbox/command.d.ts +2 -4
  52. package/dist/core/sandbox/command.d.ts.map +1 -1
  53. package/dist/core/sandbox/command.js +24 -46
  54. package/dist/core/sandbox/command.js.map +1 -1
  55. package/dist/core/sandbox/config.d.ts +91 -0
  56. package/dist/core/sandbox/config.d.ts.map +1 -0
  57. package/dist/core/sandbox/config.js +88 -0
  58. package/dist/core/sandbox/config.js.map +1 -0
  59. package/dist/core/sandbox/lima-guest-binary.d.ts +11 -0
  60. package/dist/core/sandbox/lima-guest-binary.d.ts.map +1 -0
  61. package/dist/core/sandbox/lima-guest-binary.js +140 -0
  62. package/dist/core/sandbox/lima-guest-binary.js.map +1 -0
  63. package/dist/core/sandbox/lima.d.ts +22 -3
  64. package/dist/core/sandbox/lima.d.ts.map +1 -1
  65. package/dist/core/sandbox/lima.js +434 -42
  66. package/dist/core/sandbox/lima.js.map +1 -1
  67. package/dist/core/sandbox/shared-lima-roots.d.ts +11 -0
  68. package/dist/core/sandbox/shared-lima-roots.d.ts.map +1 -0
  69. package/dist/core/sandbox/shared-lima-roots.js +25 -0
  70. package/dist/core/sandbox/shared-lima-roots.js.map +1 -0
  71. package/dist/core/sandbox/update-shared-guest-binary.d.ts +8 -0
  72. package/dist/core/sandbox/update-shared-guest-binary.d.ts.map +1 -0
  73. package/dist/core/sandbox/update-shared-guest-binary.js +100 -0
  74. package/dist/core/sandbox/update-shared-guest-binary.js.map +1 -0
  75. package/dist/core/settings-manager.d.ts +1 -5
  76. package/dist/core/settings-manager.d.ts.map +1 -1
  77. package/dist/core/settings-manager.js.map +1 -1
  78. package/dist/core/tools/bash.d.ts.map +1 -1
  79. package/dist/core/tools/bash.js +25 -14
  80. package/dist/core/tools/bash.js.map +1 -1
  81. package/dist/core/tools/file-mutation-queue.d.ts +2 -0
  82. package/dist/core/tools/file-mutation-queue.d.ts.map +1 -1
  83. package/dist/core/tools/file-mutation-queue.js +9 -0
  84. package/dist/core/tools/file-mutation-queue.js.map +1 -1
  85. package/dist/core/tools/operations.d.ts +13 -2
  86. package/dist/core/tools/operations.d.ts.map +1 -1
  87. package/dist/core/tools/operations.js +132 -36
  88. package/dist/core/tools/operations.js.map +1 -1
  89. package/dist/daemon/activity-budget.d.ts +10 -0
  90. package/dist/daemon/activity-budget.d.ts.map +1 -0
  91. package/dist/daemon/activity-budget.js +34 -0
  92. package/dist/daemon/activity-budget.js.map +1 -0
  93. package/dist/daemon/config.d.ts +2 -0
  94. package/dist/daemon/config.d.ts.map +1 -1
  95. package/dist/daemon/config.js +15 -0
  96. package/dist/daemon/config.js.map +1 -1
  97. package/dist/daemon/outbound-budget.d.ts +10 -0
  98. package/dist/daemon/outbound-budget.d.ts.map +1 -0
  99. package/dist/daemon/outbound-budget.js +30 -0
  100. package/dist/daemon/outbound-budget.js.map +1 -0
  101. package/dist/daemon/security.d.ts.map +1 -1
  102. package/dist/daemon/security.js +3 -1
  103. package/dist/daemon/security.js.map +1 -1
  104. package/dist/daemon/server.d.ts.map +1 -1
  105. package/dist/daemon/server.js +104 -24
  106. package/dist/daemon/server.js.map +1 -1
  107. package/dist/daemon/shared-connection.d.ts +33 -0
  108. package/dist/daemon/shared-connection.d.ts.map +1 -0
  109. package/dist/daemon/shared-connection.js +271 -0
  110. package/dist/daemon/shared-connection.js.map +1 -0
  111. package/dist/daemon/shared-revision.d.ts +5 -0
  112. package/dist/daemon/shared-revision.d.ts.map +1 -0
  113. package/dist/daemon/shared-revision.js +61 -0
  114. package/dist/daemon/shared-revision.js.map +1 -0
  115. package/dist/daemon/shared-roots.d.ts +9 -0
  116. package/dist/daemon/shared-roots.d.ts.map +1 -0
  117. package/dist/daemon/shared-roots.js +33 -0
  118. package/dist/daemon/shared-roots.js.map +1 -0
  119. package/dist/daemon/workspace-runtime.d.ts +1 -1
  120. package/dist/daemon/workspace-runtime.d.ts.map +1 -1
  121. package/dist/daemon/workspace-runtime.js +42 -10
  122. package/dist/daemon/workspace-runtime.js.map +1 -1
  123. package/dist/main.d.ts.map +1 -1
  124. package/dist/main.js +5 -3
  125. package/dist/main.js.map +1 -1
  126. package/dist/modes/interactive/components/assistant-message.d.ts +1 -0
  127. package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  128. package/dist/modes/interactive/components/assistant-message.js +13 -3
  129. package/dist/modes/interactive/components/assistant-message.js.map +1 -1
  130. package/dist/modes/interactive/components/branch-summary-message.d.ts +4 -3
  131. package/dist/modes/interactive/components/branch-summary-message.d.ts.map +1 -1
  132. package/dist/modes/interactive/components/branch-summary-message.js +5 -4
  133. package/dist/modes/interactive/components/branch-summary-message.js.map +1 -1
  134. package/dist/modes/interactive/components/compaction-summary-message.d.ts +4 -3
  135. package/dist/modes/interactive/components/compaction-summary-message.d.ts.map +1 -1
  136. package/dist/modes/interactive/components/compaction-summary-message.js +5 -4
  137. package/dist/modes/interactive/components/compaction-summary-message.js.map +1 -1
  138. package/dist/modes/interactive/components/custom-message.d.ts.map +1 -1
  139. package/dist/modes/interactive/components/custom-message.js +4 -3
  140. package/dist/modes/interactive/components/custom-message.js.map +1 -1
  141. package/dist/modes/interactive/components/hook-execution.d.ts.map +1 -1
  142. package/dist/modes/interactive/components/hook-execution.js +3 -2
  143. package/dist/modes/interactive/components/hook-execution.js.map +1 -1
  144. package/dist/modes/interactive/components/message-card.d.ts +9 -0
  145. package/dist/modes/interactive/components/message-card.d.ts.map +1 -0
  146. package/dist/modes/interactive/components/message-card.js +24 -0
  147. package/dist/modes/interactive/components/message-card.js.map +1 -0
  148. package/dist/modes/interactive/components/skill-invocation-message.d.ts +4 -3
  149. package/dist/modes/interactive/components/skill-invocation-message.d.ts.map +1 -1
  150. package/dist/modes/interactive/components/skill-invocation-message.js +5 -4
  151. package/dist/modes/interactive/components/skill-invocation-message.js.map +1 -1
  152. package/dist/modes/interactive/components/tool-execution.d.ts +1 -1
  153. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  154. package/dist/modes/interactive/components/tool-execution.js +27 -21
  155. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  156. package/dist/modes/interactive/components/user-message.d.ts.map +1 -1
  157. package/dist/modes/interactive/components/user-message.js +3 -2
  158. package/dist/modes/interactive/components/user-message.js.map +1 -1
  159. package/dist/modes/interactive/interactive-mode.d.ts +0 -3
  160. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  161. package/dist/modes/interactive/interactive-mode.js +55 -44
  162. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  163. package/dist/modes/interactive/theme/theme.d.ts +3 -0
  164. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  165. package/dist/modes/interactive/theme/theme.js +10 -0
  166. package/dist/modes/interactive/theme/theme.js.map +1 -1
  167. package/dist/utils/clipboard-native.d.ts.map +1 -1
  168. package/dist/utils/clipboard-native.js +1 -1
  169. package/dist/utils/clipboard-native.js.map +1 -1
  170. package/dist/utils/image-resize.d.ts.map +1 -1
  171. package/dist/utils/image-resize.js +5 -0
  172. package/dist/utils/image-resize.js.map +1 -1
  173. package/dist/utils/photon.d.ts.map +1 -1
  174. package/dist/utils/photon.js +1 -0
  175. package/dist/utils/photon.js.map +1 -1
  176. package/docs/daemon.md +6 -4
  177. package/docs/docs.json +1 -1
  178. package/docs/index.md +8 -12
  179. package/docs/packages.md +3 -3
  180. package/docs/quickstart.md +11 -19
  181. package/docs/sandbox.md +40 -117
  182. package/docs/settings.md +12 -11
  183. package/docs/termux.md +1 -18
  184. package/docs/usage.md +2 -2
  185. package/examples/README.md +0 -2
  186. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  187. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  188. package/examples/extensions/sandbox/package.json +1 -1
  189. package/examples/extensions/with-deps/package.json +1 -1
  190. package/npm-shrinkwrap.json +12 -12
  191. package/package.json +5 -6
  192. package/dist/core/sandbox/docker.d.ts +0 -169
  193. package/dist/core/sandbox/docker.d.ts.map +0 -1
  194. package/dist/core/sandbox/docker.js +0 -633
  195. package/dist/core/sandbox/docker.js.map +0 -1
  196. package/examples/sandbox-docker/Dockerfile +0 -274
  197. package/examples/sandbox-docker/README.md +0 -88
@@ -21,7 +21,7 @@ import { defaultModelPerProvider, findExactModelReferenceMatch, resolveModelScop
21
21
  import { DefaultPackageManager } from "../../core/package-manager.js";
22
22
  import { BUILT_IN_PROVIDER_DISPLAY_NAMES } from "../../core/provider-display-names.js";
23
23
  import { formatSandboxList, formatSandboxStartResult, formatSandboxStopResult, parseSandboxUserCommand, } from "../../core/sandbox/command.js";
24
- import { DockerSandboxService, redactSecrets, } from "../../core/sandbox/docker.js";
24
+ import { redactSecrets, } from "../../core/sandbox/config.js";
25
25
  import { LimaSandboxService } from "../../core/sandbox/lima.js";
26
26
  import { LocalSessionManager } from "../../core/session/local-session-manager.js";
27
27
  import { formatMissingSessionCwdPrompt, MissingSessionCwdError } from "../../core/session-cwd.js";
@@ -253,10 +253,8 @@ export class InteractiveMode {
253
253
  // Tool output expansion state
254
254
  toolOutputExpanded = false;
255
255
  activeSandboxContainerId;
256
- activeSandboxRuntime;
257
256
  activeSandboxBackendConnected = false;
258
257
  sessionSandboxStates = new Map();
259
- managedSandboxContainers = new Map();
260
258
  managedLimaInstances = new Map();
261
259
  // Thinking block visibility state
262
260
  hideThinkingBlock = false;
@@ -2640,6 +2638,8 @@ export class InteractiveMode {
2640
2638
  const submittedText = text.trim();
2641
2639
  if (!submittedText)
2642
2640
  return;
2641
+ // Keep attempted commands as well as accepted prompts available through editor history.
2642
+ this.editor.addToHistory?.(submittedText);
2643
2643
  if (await this.handleBuiltInEditorCommand(submittedText))
2644
2644
  return;
2645
2645
  if (await this.handleBashEditorSubmission(submittedText))
@@ -4952,6 +4952,7 @@ export class InteractiveMode {
4952
4952
  this.editorContainer.clear();
4953
4953
  this.editorContainer.addChild(editor);
4954
4954
  this.ui.setFocus(editor);
4955
+ this.updateToolBackendStatus();
4955
4956
  this.ui.requestRender();
4956
4957
  };
4957
4958
  try {
@@ -5230,12 +5231,6 @@ export class InteractiveMode {
5230
5231
  this.setupAutocompleteProvider();
5231
5232
  this.showLoadedResources({ force: true, showDiagnosticsWhenQuiet: true });
5232
5233
  }
5233
- createDockerSandboxService() {
5234
- return new DockerSandboxService({
5235
- settingsManager: this.settingsManager,
5236
- managedContainers: this.managedSandboxContainers,
5237
- });
5238
- }
5239
5234
  createLimaSandboxService() {
5240
5235
  return new LimaSandboxService({
5241
5236
  settingsManager: this.settingsManager,
@@ -5243,7 +5238,6 @@ export class InteractiveMode {
5243
5238
  });
5244
5239
  }
5245
5240
  async disposeRuntimeResources() {
5246
- const dockerSandboxService = this.createDockerSandboxService();
5247
5241
  const limaSandboxService = this.createLimaSandboxService();
5248
5242
  const errors = [];
5249
5243
  try {
@@ -5253,7 +5247,7 @@ export class InteractiveMode {
5253
5247
  errors.push(error instanceof Error ? error : new Error(String(error)));
5254
5248
  }
5255
5249
  try {
5256
- await Promise.all([dockerSandboxService.stopManagedContainers(), limaSandboxService.stopManagedInstances()]);
5250
+ await limaSandboxService.stopManagedInstances();
5257
5251
  }
5258
5252
  catch (error) {
5259
5253
  errors.push(error instanceof Error ? error : new Error(String(error)));
@@ -5262,13 +5256,11 @@ export class InteractiveMode {
5262
5256
  }
5263
5257
  async restoreCurrentSessionSandbox() {
5264
5258
  this.activeSandboxContainerId = undefined;
5265
- this.activeSandboxRuntime = undefined;
5266
5259
  this.activeSandboxBackendConnected = false;
5267
5260
  const state = this.sessionSandboxStates.get(getSessionSandboxKey(this.activeSession));
5268
5261
  if (!state)
5269
5262
  return;
5270
5263
  this.activeSandboxContainerId = state.containerId;
5271
- this.activeSandboxRuntime = state.runtime ?? "docker";
5272
5264
  try {
5273
5265
  await this.session.activateSandboxDaemon({
5274
5266
  url: state.url,
@@ -5286,7 +5278,6 @@ export class InteractiveMode {
5286
5278
  hasSandboxActivationConflict(sessionKey) {
5287
5279
  return (this.activeSandboxBackendConnected ||
5288
5280
  this.activeSandboxContainerId !== undefined ||
5289
- [...this.managedSandboxContainers.values()].some((container) => container.ownerId === this.activeSession.getSessionId()) ||
5290
5281
  [...(this.managedLimaInstances?.values() ?? [])].some((instance) => instance.ownerId === this.activeSession.getSessionId()) ||
5291
5282
  this.sessionSandboxStates.has(sessionKey));
5292
5283
  }
@@ -5300,13 +5291,12 @@ export class InteractiveMode {
5300
5291
  if (!this.activeSandboxBackendConnected && this.sessionSandboxStates.has(sessionKey)) {
5301
5292
  this.sessionSandboxStates.delete(sessionKey);
5302
5293
  this.activeSandboxContainerId = undefined;
5303
- this.activeSandboxRuntime = undefined;
5304
5294
  this.showStatus("Stale sandbox state cleared");
5305
5295
  return;
5306
5296
  }
5307
5297
  await this.session.clearRemoteSandbox();
5308
5298
  this.sessionSandboxStates.delete(sessionKey);
5309
- this.activeSandboxRuntime = undefined;
5299
+ this.activeSandboxContainerId = undefined;
5310
5300
  this.activeSandboxBackendConnected = false;
5311
5301
  this.refreshUiAfterBackendChange();
5312
5302
  this.updateToolBackendStatus();
@@ -5320,7 +5310,6 @@ export class InteractiveMode {
5320
5310
  const token = process.env.GRIP_REMOTE_TOKEN ?? "";
5321
5311
  const info = await this.session.activateSandboxDaemon({ url, token, expectedCwd });
5322
5312
  this.activeSandboxContainerId = undefined;
5323
- this.activeSandboxRuntime = undefined;
5324
5313
  this.activeSandboxBackendConnected = true;
5325
5314
  this.sessionSandboxStates.set(sessionKey, { type: "daemon", url, token, expectedCwd });
5326
5315
  this.refreshUiAfterBackendChange();
@@ -5368,10 +5357,15 @@ export class InteractiveMode {
5368
5357
  if (info)
5369
5358
  return info;
5370
5359
  const activationError = lastActivationError instanceof Error ? lastActivationError : new Error(String(lastActivationError));
5360
+ if (result.daemonUrl.startsWith("grip+ws:")) {
5361
+ // A shared-VM attach owns only its channel, never the VM or guest daemon.
5362
+ this.activeSandboxContainerId = undefined;
5363
+ this.activeSandboxBackendConnected = false;
5364
+ throw activationError;
5365
+ }
5371
5366
  try {
5372
5367
  await service.stop({ workspaceRoot, currentContainerId: result.containerId });
5373
5368
  this.activeSandboxContainerId = undefined;
5374
- this.activeSandboxRuntime = undefined;
5375
5369
  this.activeSandboxBackendConnected = false;
5376
5370
  }
5377
5371
  catch (cleanupError) {
@@ -5382,18 +5376,24 @@ export class InteractiveMode {
5382
5376
  }
5383
5377
  throw activationError;
5384
5378
  }
5385
- async startSandboxBackend(command, service, workspaceRoot, sessionKey, runtime) {
5379
+ async startSandboxBackend(command, service, workspaceRoot, sessionKey, updatingAttached = false) {
5386
5380
  const loader = this.showSandboxStartupLoader();
5387
5381
  try {
5388
5382
  const result = await service.start({
5389
5383
  workspaceRoot,
5390
- ...(command.image ? { image: command.image } : {}),
5391
5384
  ...(command.name ? { name: command.name } : {}),
5392
5385
  ...(command.template ? { limaTemplate: command.template } : {}),
5386
+ ...(command.update ? { update: true } : {}),
5393
5387
  sessionId: this.activeSession.getSessionId(),
5394
5388
  });
5389
+ if (updatingAttached) {
5390
+ await this.session.abort();
5391
+ await this.session.clearRemoteSandbox();
5392
+ this.sessionSandboxStates.delete(sessionKey);
5393
+ this.activeSandboxContainerId = undefined;
5394
+ this.activeSandboxBackendConnected = false;
5395
+ }
5395
5396
  this.activeSandboxContainerId = result.containerId;
5396
- this.activeSandboxRuntime = runtime;
5397
5397
  const info = await this.activateStartedSandbox(service, workspaceRoot, result);
5398
5398
  this.activeSandboxBackendConnected = true;
5399
5399
  this.sessionSandboxStates.set(sessionKey, {
@@ -5402,11 +5402,15 @@ export class InteractiveMode {
5402
5402
  token: result.token,
5403
5403
  expectedCwd: result.workspaceMountPath,
5404
5404
  containerId: result.containerId,
5405
- runtime,
5406
5405
  });
5407
5406
  this.refreshUiAfterBackendChange();
5408
5407
  this.updateToolBackendStatus();
5409
- this.showStatus([formatSandboxStartResult(result), this.formatToolBackendStatus(info)].join("\n"));
5408
+ this.showStatus([
5409
+ command.update
5410
+ ? formatSandboxStartResult(result).replace("Sandbox started:", "Sandbox updated:")
5411
+ : formatSandboxStartResult(result),
5412
+ this.formatToolBackendStatus(info),
5413
+ ].join("\n"));
5410
5414
  }
5411
5415
  finally {
5412
5416
  this.hideSandboxStartupLoader(loader);
@@ -5435,7 +5439,6 @@ export class InteractiveMode {
5435
5439
  }
5436
5440
  this.sessionSandboxStates.delete(sessionKey);
5437
5441
  this.activeSandboxContainerId = undefined;
5438
- this.activeSandboxRuntime = undefined;
5439
5442
  this.activeSandboxBackendConnected = false;
5440
5443
  return;
5441
5444
  }
@@ -5449,7 +5452,6 @@ export class InteractiveMode {
5449
5452
  }
5450
5453
  if (stoppedActiveSandbox) {
5451
5454
  this.activeSandboxContainerId = undefined;
5452
- this.activeSandboxRuntime = undefined;
5453
5455
  this.activeSandboxBackendConnected = false;
5454
5456
  }
5455
5457
  }
@@ -5463,6 +5465,20 @@ export class InteractiveMode {
5463
5465
  this.showStatus("Sandbox detached");
5464
5466
  return;
5465
5467
  }
5468
+ const attached = this.sessionSandboxStates.get(sessionKey);
5469
+ if (attached?.url?.startsWith("grip+ws:") &&
5470
+ attached.containerId === this.activeSandboxContainerId &&
5471
+ (!target || target === attached.containerId)) {
5472
+ if (this.activeSandboxBackendConnected)
5473
+ await this.session.clearRemoteSandbox();
5474
+ this.sessionSandboxStates.delete(sessionKey);
5475
+ this.activeSandboxContainerId = undefined;
5476
+ this.activeSandboxBackendConnected = false;
5477
+ this.refreshUiAfterBackendChange();
5478
+ this.updateToolBackendStatus();
5479
+ this.showStatus("Shared sandbox detached; VM and guest daemon remain running");
5480
+ return;
5481
+ }
5466
5482
  const result = await service.stop({
5467
5483
  workspaceRoot,
5468
5484
  target,
@@ -5471,7 +5487,7 @@ export class InteractiveMode {
5471
5487
  await this.reconcileStoppedSandbox(result, sessionKey);
5472
5488
  this.showStatus(formatSandboxStopResult(result));
5473
5489
  }
5474
- async executeSandboxCommand(command, dockerService, workspaceRoot, sessionKey) {
5490
+ async executeSandboxCommand(command, service, workspaceRoot, sessionKey) {
5475
5491
  switch (command.subcommand) {
5476
5492
  case "status":
5477
5493
  this.showStatus(this.formatToolBackendStatus(this.session.getToolBackendInfo()));
@@ -5482,29 +5498,24 @@ export class InteractiveMode {
5482
5498
  case "attach":
5483
5499
  if (!this.ensureSandboxCanActivate(sessionKey))
5484
5500
  return;
5485
- await this.attachSandboxBackend(command.url, dockerService, sessionKey);
5501
+ await this.attachSandboxBackend(command.url, service, sessionKey);
5486
5502
  return;
5487
- case "start":
5488
- if (!this.ensureSandboxCanActivate(sessionKey))
5503
+ case "start": {
5504
+ const attached = this.sessionSandboxStates.get(sessionKey);
5505
+ const updatingAttached = command.update === true &&
5506
+ attached?.url.startsWith("grip+ws:") === true &&
5507
+ attached.containerId === command.name &&
5508
+ this.activeSandboxBackendConnected;
5509
+ if (!updatingAttached && !this.ensureSandboxCanActivate(sessionKey))
5489
5510
  return;
5490
- {
5491
- const runtime = command.runtime ?? dockerService.resolveConfig().runtime;
5492
- const service = runtime === "lima" ? this.createLimaSandboxService() : dockerService;
5493
- await this.startSandboxBackend(command, service, workspaceRoot, sessionKey, runtime);
5494
- }
5511
+ await this.startSandboxBackend(command, service, workspaceRoot, sessionKey, updatingAttached);
5495
5512
  return;
5513
+ }
5496
5514
  case "list":
5497
- {
5498
- const runtime = command.runtime ?? dockerService.resolveConfig().runtime;
5499
- const service = runtime === "lima" ? this.createLimaSandboxService() : dockerService;
5500
- this.showStatus(formatSandboxList(await service.list({ workspaceRoot })));
5501
- }
5515
+ this.showStatus(formatSandboxList(await service.list({ workspaceRoot })));
5502
5516
  return;
5503
- case "stop": {
5504
- const runtime = this.activeSandboxRuntime ?? dockerService.resolveConfig().runtime;
5505
- const service = runtime === "lima" ? this.createLimaSandboxService() : dockerService;
5517
+ case "stop":
5506
5518
  await this.stopSandboxBackend(command.target, service, workspaceRoot, sessionKey);
5507
- }
5508
5519
  }
5509
5520
  }
5510
5521
  async handleSandboxCommand(text) {
@@ -5516,7 +5527,7 @@ export class InteractiveMode {
5516
5527
  this.showWarning(error instanceof Error ? error.message : String(error));
5517
5528
  return;
5518
5529
  }
5519
- const service = this.createDockerSandboxService();
5530
+ const service = this.createLimaSandboxService();
5520
5531
  const workspaceRoot = this.activeSession.getCwd();
5521
5532
  const sessionKey = getSessionSandboxKey(this.activeSession);
5522
5533
  try {