@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
@@ -1 +1 @@
1
- {"version":3,"file":"bash.js","sourceRoot":"","sources":["../../../src/core/tools/bash.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AgentTool, AgentToolResult } from \"@greppleai/grip-agent-core\";\nimport { Container, Text, truncateToWidth } from \"@greppleai/grip-tui\";\nimport { type Static, Type } from \"typebox\";\nimport { keyHint } from \"../../modes/interactive/components/keybinding-hints.ts\";\nimport { truncateToVisualLines } from \"../../modes/interactive/components/visual-truncate.ts\";\nimport { theme } from \"../../modes/interactive/theme/theme.ts\";\nimport { getShellEnv } from \"../../utils/shell.ts\";\nimport type { ExtensionContext, ToolDefinition, ToolRenderResultOptions } from \"../extensions/types.ts\";\nimport {\n\tGRIP_SESSION_ENVIRONMENT_NAMES,\n\ttype GripSessionEnvironment,\n\tLocalToolOperations,\n\ttype ToolBackendInfo,\n\ttype ToolOperations,\n} from \"./operations.ts\";\nimport { OutputAccumulator, type OutputSnapshot } from \"./output-accumulator.ts\";\nimport { formatBackendIcon, getTextOutput, invalidArgText, str } from \"./render-utils.ts\";\nimport { wrapToolDefinition } from \"./tool-definition-wrapper.ts\";\nimport { DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES, formatSize, type TruncationResult } from \"./truncate.ts\";\n\nconst bashSchema = Type.Object({\n\tcommand: Type.String({ description: \"Bash command to execute\" }),\n\ttimeout: Type.Optional(Type.Number({ description: \"Timeout in seconds (optional, no default timeout)\" })),\n});\n\nexport type BashToolInput = Static<typeof bashSchema>;\n\nexport interface BashToolDetails {\n\ttruncation?: TruncationResult;\n\tfullOutputPath?: string;\n}\n\n/**\n * Create tool operations using grip's built-in local shell execution backend.\n *\n * This is useful for extensions that intercept user_bash and still want grip's\n * standard local shell behavior while wrapping or rewriting commands.\n */\nexport interface LocalBashOperationsOptions {\n\tcwd?: string;\n\tshellPath?: string;\n}\n\nexport function createLocalBashOperations(options?: LocalBashOperationsOptions): ToolOperations {\n\treturn new LocalToolOperations(options?.cwd ?? process.cwd(), { shellPath: options?.shellPath });\n}\n\nexport interface BashSpawnContext {\n\tcommand: string;\n\tcwd: string;\n\tenv: NodeJS.ProcessEnv;\n}\n\nexport type BashSpawnHook = (context: BashSpawnContext) => BashSpawnContext;\n\nexport type BashSessionEnvironment = GripSessionEnvironment;\n\nexport function getBashSessionEnvironment(ctx: ExtensionContext | undefined): BashSessionEnvironment {\n\tif (!ctx?.session) return {};\n\tconst environment: BashSessionEnvironment = {\n\t\tGRIP_SESSION_ID: ctx.session.getSessionId(),\n\t};\n\tif (ctx.thinkingLevel) environment.GRIP_REASONING_LEVEL = ctx.thinkingLevel;\n\tconst sessionFile = ctx.session.getSessionReference();\n\tif (sessionFile) environment.GRIP_SESSION_FILE = sessionFile;\n\tif (ctx.model) {\n\t\tenvironment.GRIP_PROVIDER = ctx.model.provider;\n\t\tenvironment.GRIP_MODEL = ctx.model.id;\n\t}\n\treturn environment;\n}\n\nfunction resolveSpawnContext(\n\tcommand: string,\n\tcwd: string,\n\tspawnHook: BashSpawnHook | undefined,\n\texposeSessionEnvironment: boolean,\n\tctx: ExtensionContext | undefined,\n): BashSpawnContext {\n\tconst env = { ...getShellEnv() };\n\tfor (const name of GRIP_SESSION_ENVIRONMENT_NAMES) delete env[name];\n\tif (exposeSessionEnvironment) Object.assign(env, getBashSessionEnvironment(ctx));\n\tconst baseContext: BashSpawnContext = { command, cwd, env };\n\treturn spawnHook ? spawnHook(baseContext) : baseContext;\n}\n\nexport interface BashToolOptions {\n\t/** Command prefix prepended to every command (for example shell setup commands) */\n\tcommandPrefix?: string;\n\t/** Expose current grip session metadata as GRIP_* environment variables. Default: true */\n\texposeSessionEnvironment?: boolean;\n\t/** Hook to adjust command, cwd, or env before execution */\n\tspawnHook?: BashSpawnHook;\n}\n\nconst BASH_PREVIEW_LINES = 5;\nconst BASH_UPDATE_THROTTLE_MS = 100;\n\ntype BashRenderState = {\n\tstartedAt: number | undefined;\n\tendedAt: number | undefined;\n\tinterval: NodeJS.Timeout | undefined;\n};\n\ntype BashResultRenderState = {\n\tcachedWidth: number | undefined;\n\tcachedLines: string[] | undefined;\n\tcachedSkipped: number | undefined;\n};\n\nclass BashResultRenderComponent extends Container {\n\tstate: BashResultRenderState = {\n\t\tcachedWidth: undefined,\n\t\tcachedLines: undefined,\n\t\tcachedSkipped: undefined,\n\t};\n}\n\nfunction formatDuration(ms: number): string {\n\treturn `${(ms / 1000).toFixed(1)}s`;\n}\n\nfunction formatBackendSuffix(backendInfo: ToolBackendInfo | undefined): string {\n\tif (!backendInfo) return \"\";\n\tif (backendInfo.type === \"local\") return theme.fg(\"muted\", ` [local ${backendInfo.cwd}]`);\n\treturn backendInfo.configured\n\t\t? theme.fg(\"muted\", ` [remote ${backendInfo.url}:${backendInfo.cwd}]`)\n\t\t: theme.fg(\"warning\", ` [remote not configured ${backendInfo.cwd}]`);\n}\n\nfunction formatBashCall(args: Partial<BashToolInput> | undefined, backendInfo?: ToolBackendInfo): string {\n\tconst command = str(args?.command);\n\tconst timeout = args?.timeout;\n\tconst timeoutSuffix = timeout ? theme.fg(\"muted\", ` (timeout ${timeout}s)`) : \"\";\n\tconst commandDisplay = command === null ? invalidArgText(theme) : command ? command : theme.fg(\"toolOutput\", \"...\");\n\treturn (\n\t\tformatBackendIcon(backendInfo, theme) + theme.fg(\"toolTitle\", theme.bold(`$ ${commandDisplay}`)) + timeoutSuffix\n\t);\n}\n\nfunction getBashDisplayOutput(\n\tresult: AgentToolResult<BashToolDetails | undefined>,\n\tshowImages: boolean,\n\tisPartial: boolean,\n): string {\n\tconst output = getTextOutput(result, showImages).trim();\n\tconst truncation = result.details?.truncation;\n\tconst fullOutputPath = result.details?.fullOutputPath;\n\tif (isPartial || !truncation?.truncated || !fullOutputPath || !output.endsWith(\"]\")) return output;\n\tconst footerStart = output.lastIndexOf(\"\\n\\n[\");\n\tif (footerStart === -1 || !output.slice(footerStart).includes(fullOutputPath)) return output;\n\treturn output.slice(0, footerStart).trimEnd();\n}\n\nfunction addBashOutput(\n\tcomponent: BashResultRenderComponent,\n\tstate: BashResultRenderState,\n\toutput: string,\n\toptions: ToolRenderResultOptions,\n): void {\n\tif (!output) return;\n\tconst styledOutput = output\n\t\t.split(\"\\n\")\n\t\t.map((line) => theme.fg(\"toolOutput\", line))\n\t\t.join(\"\\n\");\n\tif (options.expanded) {\n\t\tcomponent.addChild(new Text(`\\n${styledOutput}`, 0, 0));\n\t\treturn;\n\t}\n\tcomponent.addChild({\n\t\trender: (width: number) => {\n\t\t\tif (state.cachedLines === undefined || state.cachedWidth !== width) {\n\t\t\t\tconst preview = truncateToVisualLines(styledOutput, BASH_PREVIEW_LINES, width);\n\t\t\t\tstate.cachedLines = preview.visualLines;\n\t\t\t\tstate.cachedSkipped = preview.skippedCount;\n\t\t\t\tstate.cachedWidth = width;\n\t\t\t}\n\t\t\tif (state.cachedSkipped && state.cachedSkipped > 0) {\n\t\t\t\tconst hint =\n\t\t\t\t\ttheme.fg(\"muted\", `... (${state.cachedSkipped} earlier lines,`) +\n\t\t\t\t\t` ${keyHint(\"app.tools.expand\", \"to expand\")})`;\n\t\t\t\treturn [\"\", truncateToWidth(hint, width, \"...\"), ...(state.cachedLines ?? [])];\n\t\t\t}\n\t\t\treturn [\"\", ...(state.cachedLines ?? [])];\n\t\t},\n\t\tinvalidate: () => {\n\t\t\tstate.cachedWidth = undefined;\n\t\t\tstate.cachedLines = undefined;\n\t\t\tstate.cachedSkipped = undefined;\n\t\t},\n\t});\n}\n\nfunction addBashOutputWarnings(\n\tcomponent: BashResultRenderComponent,\n\ttruncation: TruncationResult | undefined,\n\tfullOutputPath: string | undefined,\n): void {\n\tif (!truncation?.truncated && !fullOutputPath) return;\n\tconst warnings: string[] = [];\n\tif (fullOutputPath) warnings.push(`Full output: ${fullOutputPath}`);\n\tif (truncation?.truncated) {\n\t\tif (truncation.truncatedBy === \"lines\") {\n\t\t\twarnings.push(`Truncated: showing ${truncation.outputLines} of ${truncation.totalLines} lines`);\n\t\t} else {\n\t\t\twarnings.push(\n\t\t\t\t`Truncated: ${truncation.outputLines} lines shown (${formatSize(truncation.maxBytes ?? DEFAULT_MAX_BYTES)} limit)`,\n\t\t\t);\n\t\t}\n\t}\n\tcomponent.addChild(new Text(`\\n${theme.fg(\"warning\", `[${warnings.join(\". \")}]`)}`, 0, 0));\n}\n\nfunction rebuildBashResultRenderComponent(\n\tcomponent: BashResultRenderComponent,\n\tresult: AgentToolResult<BashToolDetails | undefined>,\n\toptions: ToolRenderResultOptions,\n\tshowImages: boolean,\n\tstartedAt: number | undefined,\n\tendedAt: number | undefined,\n\tbackendInfo: ToolBackendInfo | undefined,\n): void {\n\tconst state = component.state;\n\tcomponent.clear();\n\tconst output = getBashDisplayOutput(result, showImages, options.isPartial);\n\taddBashOutput(component, state, output, options);\n\taddBashOutputWarnings(component, result.details?.truncation, result.details?.fullOutputPath);\n\tif (startedAt === undefined) return;\n\tconst label = options.isPartial ? \"Elapsed\" : \"Took\";\n\tconst endTime = endedAt ?? Date.now();\n\tcomponent.addChild(\n\t\tnew Text(\n\t\t\t`\\n${theme.fg(\"muted\", `${label} ${formatDuration(endTime - startedAt)}`)}${formatBackendSuffix(backendInfo)}`,\n\t\t\t0,\n\t\t\t0,\n\t\t),\n\t);\n}\n\nfunction appendBashStatus(text: string, status: string): string {\n\treturn `${text ? `${text}\\n\\n` : \"\"}${status}`;\n}\n\nfunction throwBashExecutionError(error: unknown, outputText: string): never {\n\tif (error instanceof Error && error.message === \"aborted\") {\n\t\tthrow new Error(appendBashStatus(outputText, \"Command aborted\"));\n\t}\n\tif (error instanceof Error && error.message.startsWith(\"timeout:\")) {\n\t\tconst timeoutSeconds = error.message.split(\":\")[1];\n\t\tthrow new Error(appendBashStatus(outputText, `Command timed out after ${timeoutSeconds} seconds`));\n\t}\n\tthrow error;\n}\n\nexport type BashToolDefinition = ToolDefinition<typeof bashSchema, BashToolDetails | undefined, BashRenderState>;\n\nexport function createBashToolDefinition(operations: ToolOperations, options?: BashToolOptions): BashToolDefinition {\n\tconst ops = operations;\n\tconst commandPrefix = options?.commandPrefix;\n\tconst exposeSessionEnvironment = options?.exposeSessionEnvironment ?? true;\n\tconst spawnHook = options?.spawnHook;\n\treturn {\n\t\tname: \"bash\",\n\t\tlabel: \"bash\",\n\t\tdescription: `Execute a bash command in the current working directory. Returns stdout and stderr. Output is truncated to last ${DEFAULT_MAX_LINES} lines or ${DEFAULT_MAX_BYTES / 1024}KB (whichever is hit first). If truncated, full output is saved to a temp file. Optionally provide a timeout in seconds.`,\n\t\tpromptSnippet: \"Execute bash commands (ls, grep, find, etc.)\",\n\t\tpromptGuidelines: exposeSessionEnvironment\n\t\t\t? [\"Inspect GRIP_* environment variables for current model and session details.\"]\n\t\t\t: undefined,\n\t\tparameters: bashSchema,\n\t\tasync execute(_toolCallId, { command, timeout }: BashToolInput, signal?: AbortSignal, onUpdate?, ctx?) {\n\t\t\tconst resolvedCommand = commandPrefix ? `${commandPrefix}\\n${command}` : command;\n\t\t\tconst spawnContext = resolveSpawnContext(resolvedCommand, ops.cwd, spawnHook, exposeSessionEnvironment, ctx);\n\t\t\tconst output = new OutputAccumulator({ tempFilePrefix: \"grip-bash\" });\n\t\t\tlet updateTimer: NodeJS.Timeout | undefined;\n\t\t\tlet updateDirty = false;\n\t\t\tlet lastUpdateAt = 0;\n\n\t\t\tconst emitOutputUpdate = () => {\n\t\t\t\tif (!onUpdate || !updateDirty) return;\n\t\t\t\tupdateDirty = false;\n\t\t\t\tlastUpdateAt = Date.now();\n\t\t\t\tconst snapshot = output.snapshot({ persistIfTruncated: true });\n\t\t\t\tonUpdate({\n\t\t\t\t\tcontent: [{ type: \"text\", text: snapshot.content || \"\" }],\n\t\t\t\t\tdetails: {\n\t\t\t\t\t\ttruncation: snapshot.truncation.truncated ? snapshot.truncation : undefined,\n\t\t\t\t\t\tfullOutputPath: snapshot.fullOutputPath,\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t};\n\n\t\t\tconst clearUpdateTimer = () => {\n\t\t\t\tif (updateTimer) {\n\t\t\t\t\tclearTimeout(updateTimer);\n\t\t\t\t\tupdateTimer = undefined;\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tconst scheduleOutputUpdate = () => {\n\t\t\t\tif (!onUpdate) return;\n\t\t\t\tupdateDirty = true;\n\t\t\t\tconst delay = BASH_UPDATE_THROTTLE_MS - (Date.now() - lastUpdateAt);\n\t\t\t\tif (delay <= 0) {\n\t\t\t\t\tclearUpdateTimer();\n\t\t\t\t\temitOutputUpdate();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tupdateTimer ??= setTimeout(() => {\n\t\t\t\t\tupdateTimer = undefined;\n\t\t\t\t\temitOutputUpdate();\n\t\t\t\t}, delay);\n\t\t\t};\n\n\t\t\tif (onUpdate) {\n\t\t\t\tonUpdate({ content: [], details: undefined });\n\t\t\t}\n\n\t\t\tconst handleData = (data: Buffer) => {\n\t\t\t\toutput.append(data);\n\t\t\t\tscheduleOutputUpdate();\n\t\t\t};\n\n\t\t\tconst finishOutput = async () => {\n\t\t\t\toutput.finish();\n\t\t\t\tclearUpdateTimer();\n\t\t\t\temitOutputUpdate();\n\t\t\t\tconst snapshot = output.snapshot({ persistIfTruncated: true });\n\t\t\t\tawait output.closeTempFile();\n\t\t\t\treturn snapshot;\n\t\t\t};\n\n\t\t\tconst formatOutput = (snapshot: OutputSnapshot, emptyText = \"(no output)\") => {\n\t\t\t\tconst truncation = snapshot.truncation;\n\t\t\t\tlet text = snapshot.content || emptyText;\n\t\t\t\tlet details: BashToolDetails | undefined;\n\t\t\t\tif (truncation.truncated) {\n\t\t\t\t\tdetails = { truncation, fullOutputPath: snapshot.fullOutputPath };\n\t\t\t\t\tconst startLine = truncation.totalLines - truncation.outputLines + 1;\n\t\t\t\t\tconst endLine = truncation.totalLines;\n\t\t\t\t\tif (truncation.lastLinePartial) {\n\t\t\t\t\t\tconst lastLineSize = formatSize(output.getLastLineBytes());\n\t\t\t\t\t\ttext += `\\n\\n[Showing last ${formatSize(truncation.outputBytes)} of line ${endLine} (line is ${lastLineSize}). Full output: ${snapshot.fullOutputPath}]`;\n\t\t\t\t\t} else if (truncation.truncatedBy === \"lines\") {\n\t\t\t\t\t\ttext += `\\n\\n[Showing lines ${startLine}-${endLine} of ${truncation.totalLines}. Full output: ${snapshot.fullOutputPath}]`;\n\t\t\t\t\t} else {\n\t\t\t\t\t\ttext += `\\n\\n[Showing lines ${startLine}-${endLine} of ${truncation.totalLines} (${formatSize(DEFAULT_MAX_BYTES)} limit). Full output: ${snapshot.fullOutputPath}]`;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn { text, details };\n\t\t\t};\n\n\t\t\ttry {\n\t\t\t\tlet exitCode: number | null;\n\t\t\t\ttry {\n\t\t\t\t\tconst result = await ops.exec(spawnContext.command, {\n\t\t\t\t\t\tcwd: spawnContext.cwd,\n\t\t\t\t\t\tonData: handleData,\n\t\t\t\t\t\tsignal,\n\t\t\t\t\t\ttimeout,\n\t\t\t\t\t\tenv: spawnContext.env,\n\t\t\t\t\t});\n\t\t\t\t\texitCode = result.exitCode;\n\t\t\t\t} catch (err) {\n\t\t\t\t\tconst snapshot = await finishOutput();\n\t\t\t\t\tconst { text } = formatOutput(snapshot, \"\");\n\t\t\t\t\tthrowBashExecutionError(err, text);\n\t\t\t\t}\n\n\t\t\t\tconst snapshot = await finishOutput();\n\t\t\t\tconst { text: outputText, details } = formatOutput(snapshot);\n\t\t\t\tif (exitCode !== 0 && exitCode !== null) {\n\t\t\t\t\tthrow new Error(appendBashStatus(outputText, `Command exited with code ${exitCode}`));\n\t\t\t\t}\n\t\t\t\treturn { content: [{ type: \"text\", text: outputText }], details };\n\t\t\t} finally {\n\t\t\t\tclearUpdateTimer();\n\t\t\t}\n\t\t},\n\t\trenderCall(args, _theme, context) {\n\t\t\tconst state = context.state;\n\t\t\tif (context.executionStarted && state.startedAt === undefined) {\n\t\t\t\tstate.startedAt = Date.now();\n\t\t\t\tstate.endedAt = undefined;\n\t\t\t}\n\t\t\tconst text = (context.lastComponent as Text | undefined) ?? new Text(\"\", 0, 0);\n\t\t\ttext.setText(formatBashCall(args, ops.getBackendInfo?.()));\n\t\t\treturn text;\n\t\t},\n\t\trenderResult(result, options, _theme, context) {\n\t\t\tconst state = context.state;\n\t\t\tif (state.startedAt !== undefined && options.isPartial && !state.interval) {\n\t\t\t\tstate.interval = setInterval(() => context.invalidate(), 1000);\n\t\t\t}\n\t\t\tif (!options.isPartial || context.isError) {\n\t\t\t\tstate.endedAt ??= Date.now();\n\t\t\t\tif (state.interval) {\n\t\t\t\t\tclearInterval(state.interval);\n\t\t\t\t\tstate.interval = undefined;\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst component =\n\t\t\t\t(context.lastComponent as BashResultRenderComponent | undefined) ?? new BashResultRenderComponent();\n\t\t\trebuildBashResultRenderComponent(\n\t\t\t\tcomponent,\n\t\t\t\tresult as any,\n\t\t\t\toptions,\n\t\t\t\tcontext.showImages,\n\t\t\t\tstate.startedAt,\n\t\t\t\tstate.endedAt,\n\t\t\t\tops.getBackendInfo?.(),\n\t\t\t);\n\t\t\tcomponent.invalidate();\n\t\t\treturn component;\n\t\t},\n\t};\n}\n\nexport function createBashTool(operations: ToolOperations, options?: BashToolOptions): AgentTool<typeof bashSchema> {\n\treturn wrapToolDefinition(createBashToolDefinition(operations, options));\n}\n"]}
1
+ {"version":3,"file":"bash.js","sourceRoot":"","sources":["../../../src/core/tools/bash.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AgentTool, AgentToolResult } from \"@greppleai/grip-agent-core\";\nimport { Container, Text, truncateToWidth } from \"@greppleai/grip-tui\";\nimport { type Static, Type } from \"typebox\";\nimport { keyText } from \"../../modes/interactive/components/keybinding-hints.ts\";\nimport { truncateToVisualLines } from \"../../modes/interactive/components/visual-truncate.ts\";\nimport { theme } from \"../../modes/interactive/theme/theme.ts\";\nimport { getShellEnv } from \"../../utils/shell.ts\";\nimport type { ExtensionContext, ToolDefinition, ToolRenderResultOptions } from \"../extensions/types.ts\";\nimport {\n\tGRIP_SESSION_ENVIRONMENT_NAMES,\n\ttype GripSessionEnvironment,\n\tLocalToolOperations,\n\ttype ToolBackendInfo,\n\ttype ToolOperations,\n} from \"./operations.ts\";\nimport { OutputAccumulator, type OutputSnapshot } from \"./output-accumulator.ts\";\nimport { formatBackendIcon, getTextOutput, invalidArgText, str } from \"./render-utils.ts\";\nimport { wrapToolDefinition } from \"./tool-definition-wrapper.ts\";\nimport { DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES, formatSize, type TruncationResult } from \"./truncate.ts\";\n\nconst bashSchema = Type.Object({\n\tcommand: Type.String({ description: \"Bash command to execute\" }),\n\ttimeout: Type.Optional(Type.Number({ description: \"Timeout in seconds (optional, no default timeout)\" })),\n});\n\nexport type BashToolInput = Static<typeof bashSchema>;\n\nexport interface BashToolDetails {\n\ttruncation?: TruncationResult;\n\tfullOutputPath?: string;\n}\n\n/**\n * Create tool operations using grip's built-in local shell execution backend.\n *\n * This is useful for extensions that intercept user_bash and still want grip's\n * standard local shell behavior while wrapping or rewriting commands.\n */\nexport interface LocalBashOperationsOptions {\n\tcwd?: string;\n\tshellPath?: string;\n}\n\nexport function createLocalBashOperations(options?: LocalBashOperationsOptions): ToolOperations {\n\treturn new LocalToolOperations(options?.cwd ?? process.cwd(), { shellPath: options?.shellPath });\n}\n\nexport interface BashSpawnContext {\n\tcommand: string;\n\tcwd: string;\n\tenv: NodeJS.ProcessEnv;\n}\n\nexport type BashSpawnHook = (context: BashSpawnContext) => BashSpawnContext;\n\nexport type BashSessionEnvironment = GripSessionEnvironment;\n\nexport function getBashSessionEnvironment(ctx: ExtensionContext | undefined): BashSessionEnvironment {\n\tif (!ctx?.session) return {};\n\tconst environment: BashSessionEnvironment = {\n\t\tGRIP_SESSION_ID: ctx.session.getSessionId(),\n\t};\n\tif (ctx.thinkingLevel) environment.GRIP_REASONING_LEVEL = ctx.thinkingLevel;\n\tconst sessionFile = ctx.session.getSessionReference();\n\tif (sessionFile) environment.GRIP_SESSION_FILE = sessionFile;\n\tif (ctx.model) {\n\t\tenvironment.GRIP_PROVIDER = ctx.model.provider;\n\t\tenvironment.GRIP_MODEL = ctx.model.id;\n\t}\n\treturn environment;\n}\n\nfunction resolveSpawnContext(\n\tcommand: string,\n\tcwd: string,\n\tspawnHook: BashSpawnHook | undefined,\n\texposeSessionEnvironment: boolean,\n\tctx: ExtensionContext | undefined,\n): BashSpawnContext {\n\tconst env = { ...getShellEnv() };\n\tfor (const name of GRIP_SESSION_ENVIRONMENT_NAMES) delete env[name];\n\tif (exposeSessionEnvironment) Object.assign(env, getBashSessionEnvironment(ctx));\n\tconst baseContext: BashSpawnContext = { command, cwd, env };\n\treturn spawnHook ? spawnHook(baseContext) : baseContext;\n}\n\nexport interface BashToolOptions {\n\t/** Command prefix prepended to every command (for example shell setup commands) */\n\tcommandPrefix?: string;\n\t/** Expose current grip session metadata as GRIP_* environment variables. Default: true */\n\texposeSessionEnvironment?: boolean;\n\t/** Hook to adjust command, cwd, or env before execution */\n\tspawnHook?: BashSpawnHook;\n}\n\nconst BASH_PREVIEW_LINES = 5;\nconst BASH_UPDATE_THROTTLE_MS = 100;\n\ntype BashRenderState = {\n\tstartedAt: number | undefined;\n\tendedAt: number | undefined;\n\tinterval: NodeJS.Timeout | undefined;\n};\n\ntype BashResultRenderState = {\n\tcachedWidth: number | undefined;\n\tcachedLines: string[] | undefined;\n\tcachedSkipped: number | undefined;\n};\n\nclass BashResultRenderComponent extends Container {\n\tstate: BashResultRenderState = {\n\t\tcachedWidth: undefined,\n\t\tcachedLines: undefined,\n\t\tcachedSkipped: undefined,\n\t};\n}\n\nfunction formatDuration(ms: number): string {\n\treturn `${(ms / 1000).toFixed(1)}s`;\n}\n\nfunction formatBackendSuffix(backendInfo: ToolBackendInfo | undefined): string {\n\tif (!backendInfo) return \"\";\n\tif (backendInfo.type === \"local\") return theme.fg(\"muted\", ` [local ${backendInfo.cwd}]`);\n\treturn backendInfo.configured\n\t\t? theme.fg(\"muted\", ` [remote ${backendInfo.url}:${backendInfo.cwd}]`)\n\t\t: theme.fg(\"warning\", ` [remote not configured ${backendInfo.cwd}]`);\n}\n\nfunction formatBashCall(args: Partial<BashToolInput> | undefined, backendInfo?: ToolBackendInfo): string {\n\tconst command = str(args?.command);\n\tconst timeout = args?.timeout;\n\tconst timeoutSuffix = timeout ? theme.fg(\"muted\", ` (timeout ${timeout}s)`) : \"\";\n\tconst commandDisplay = command === null ? invalidArgText(theme) : command ? command : theme.fg(\"toolOutput\", \"...\");\n\treturn (\n\t\tformatBackendIcon(backendInfo, theme) +\n\t\ttheme.fg(\"toolTitle\", theme.bold(\"$ \")) +\n\t\ttheme.fg(\"accent\", theme.bold(commandDisplay)) +\n\t\ttimeoutSuffix\n\t);\n}\n\nfunction getBashDisplayOutput(\n\tresult: AgentToolResult<BashToolDetails | undefined>,\n\tshowImages: boolean,\n\tisPartial: boolean,\n): string {\n\tconst output = getTextOutput(result, showImages).trim();\n\tconst truncation = result.details?.truncation;\n\tconst fullOutputPath = result.details?.fullOutputPath;\n\tif (isPartial || !truncation?.truncated || !fullOutputPath || !output.endsWith(\"]\")) return output;\n\tconst footerStart = output.lastIndexOf(\"\\n\\n[\");\n\tif (footerStart === -1 || !output.slice(footerStart).includes(fullOutputPath)) return output;\n\treturn output.slice(0, footerStart).trimEnd();\n}\n\nfunction addBashOutput(\n\tcomponent: BashResultRenderComponent,\n\tstate: BashResultRenderState,\n\toutput: string,\n\toptions: ToolRenderResultOptions,\n): void {\n\tif (!output) return;\n\tconst styledOutput = output\n\t\t.split(\"\\n\")\n\t\t.map((line) => theme.fg(\"toolOutput\", line))\n\t\t.join(\"\\n\");\n\tif (options.expanded) {\n\t\tcomponent.addChild(new Text(`\\n${styledOutput}`, 0, 0));\n\t\treturn;\n\t}\n\tcomponent.addChild({\n\t\trender: (width: number) => {\n\t\t\tif (state.cachedLines === undefined || state.cachedWidth !== width) {\n\t\t\t\tconst preview = truncateToVisualLines(styledOutput, BASH_PREVIEW_LINES, width);\n\t\t\t\tstate.cachedLines = preview.visualLines;\n\t\t\t\tstate.cachedSkipped = preview.skippedCount;\n\t\t\t\tstate.cachedWidth = width;\n\t\t\t}\n\t\t\treturn [\"\", ...(state.cachedLines ?? [])];\n\t\t},\n\t\tinvalidate: () => {\n\t\t\tstate.cachedWidth = undefined;\n\t\t\tstate.cachedLines = undefined;\n\t\t\tstate.cachedSkipped = undefined;\n\t\t},\n\t});\n}\n\nfunction addBashOutputWarnings(\n\tcomponent: BashResultRenderComponent,\n\ttruncation: TruncationResult | undefined,\n\tfullOutputPath: string | undefined,\n): void {\n\tif (!truncation?.truncated && !fullOutputPath) return;\n\tconst warnings: string[] = [];\n\tif (fullOutputPath) warnings.push(`Full output: ${fullOutputPath}`);\n\tif (truncation?.truncated) {\n\t\tif (truncation.truncatedBy === \"lines\") {\n\t\t\twarnings.push(`Truncated: showing ${truncation.outputLines} of ${truncation.totalLines} lines`);\n\t\t} else {\n\t\t\twarnings.push(\n\t\t\t\t`Truncated: ${truncation.outputLines} lines shown (${formatSize(truncation.maxBytes ?? DEFAULT_MAX_BYTES)} limit)`,\n\t\t\t);\n\t\t}\n\t}\n\tcomponent.addChild(new Text(`\\n${theme.fg(\"warning\", `[${warnings.join(\". \")}]`)}`, 0, 0));\n}\n\nfunction rebuildBashResultRenderComponent(\n\tcomponent: BashResultRenderComponent,\n\tresult: AgentToolResult<BashToolDetails | undefined>,\n\toptions: ToolRenderResultOptions,\n\tisError: boolean,\n\tshowImages: boolean,\n\tstartedAt: number | undefined,\n\tendedAt: number | undefined,\n\tbackendInfo: ToolBackendInfo | undefined,\n): void {\n\tconst state = component.state;\n\tcomponent.clear();\n\tconst output = getBashDisplayOutput(result, showImages, options.isPartial);\n\taddBashOutput(component, state, output, options);\n\taddBashOutputWarnings(component, result.details?.truncation, result.details?.fullOutputPath);\n\tif (startedAt !== undefined) {\n\t\tconst label = options.isPartial ? \"Elapsed\" : \"Took\";\n\t\tconst endTime = endedAt ?? Date.now();\n\t\tcomponent.addChild(\n\t\t\tnew Text(\n\t\t\t\t`\\n${theme.fg(options.isPartial ? \"muted\" : isError ? \"error\" : \"success\", `${label} ${formatDuration(endTime - startedAt)}`)}${formatBackendSuffix(backendInfo)}`,\n\t\t\t\t0,\n\t\t\t\t0,\n\t\t\t),\n\t\t);\n\t}\n\tif (!options.expanded && output) {\n\t\tcomponent.addChild({\n\t\t\trender: (width) => {\n\t\t\t\tif (!state.cachedSkipped || width < 1) return [];\n\t\t\t\tconst expandKey = keyText(\"app.tools.expand\");\n\t\t\t\tconst hint =\n\t\t\t\t\ttheme.fg(\"warning\", `${state.cachedSkipped} lines hidden`) +\n\t\t\t\t\t(expandKey ? theme.fg(\"accent\", ` · ${expandKey} to expand`) : \"\");\n\t\t\t\treturn [\"\", truncateToWidth(hint, width, \"...\")];\n\t\t\t},\n\t\t\tinvalidate: () => {},\n\t\t});\n\t}\n}\n\nfunction appendBashStatus(text: string, status: string): string {\n\treturn `${text ? `${text}\\n\\n` : \"\"}${status}`;\n}\n\nfunction throwBashExecutionError(error: unknown, outputText: string): never {\n\tif (error instanceof Error && error.message === \"aborted\") {\n\t\tthrow new Error(appendBashStatus(outputText, \"Command aborted\"));\n\t}\n\tif (error instanceof Error && error.message.startsWith(\"timeout:\")) {\n\t\tconst timeoutSeconds = error.message.split(\":\")[1];\n\t\tthrow new Error(appendBashStatus(outputText, `Command timed out after ${timeoutSeconds} seconds`));\n\t}\n\tthrow error;\n}\n\nexport type BashToolDefinition = ToolDefinition<typeof bashSchema, BashToolDetails | undefined, BashRenderState>;\n\nexport function createBashToolDefinition(operations: ToolOperations, options?: BashToolOptions): BashToolDefinition {\n\tconst ops = operations;\n\tconst commandPrefix = options?.commandPrefix;\n\tconst exposeSessionEnvironment = options?.exposeSessionEnvironment ?? true;\n\tconst spawnHook = options?.spawnHook;\n\treturn {\n\t\tname: \"bash\",\n\t\tlabel: \"bash\",\n\t\tdescription: `Execute a bash command in the current working directory. Returns stdout and stderr. Output is truncated to last ${DEFAULT_MAX_LINES} lines or ${DEFAULT_MAX_BYTES / 1024}KB (whichever is hit first). If truncated, full output is saved to a temp file. Optionally provide a timeout in seconds.`,\n\t\tpromptSnippet: \"Execute bash commands (ls, grep, find, etc.)\",\n\t\tpromptGuidelines: exposeSessionEnvironment\n\t\t\t? [\"Inspect GRIP_* environment variables for current model and session details.\"]\n\t\t\t: undefined,\n\t\tparameters: bashSchema,\n\t\tasync execute(_toolCallId, { command, timeout }: BashToolInput, signal?: AbortSignal, onUpdate?, ctx?) {\n\t\t\tconst resolvedCommand = commandPrefix ? `${commandPrefix}\\n${command}` : command;\n\t\t\tconst spawnContext = resolveSpawnContext(resolvedCommand, ops.cwd, spawnHook, exposeSessionEnvironment, ctx);\n\t\t\tconst output = new OutputAccumulator({ tempFilePrefix: \"grip-bash\" });\n\t\t\tlet updateTimer: NodeJS.Timeout | undefined;\n\t\t\tlet updateDirty = false;\n\t\t\tlet lastUpdateAt = 0;\n\n\t\t\tconst emitOutputUpdate = () => {\n\t\t\t\tif (!onUpdate || !updateDirty) return;\n\t\t\t\tupdateDirty = false;\n\t\t\t\tlastUpdateAt = Date.now();\n\t\t\t\tconst snapshot = output.snapshot({ persistIfTruncated: true });\n\t\t\t\tonUpdate({\n\t\t\t\t\tcontent: [{ type: \"text\", text: snapshot.content || \"\" }],\n\t\t\t\t\tdetails: {\n\t\t\t\t\t\ttruncation: snapshot.truncation.truncated ? snapshot.truncation : undefined,\n\t\t\t\t\t\tfullOutputPath: snapshot.fullOutputPath,\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t};\n\n\t\t\tconst clearUpdateTimer = () => {\n\t\t\t\tif (updateTimer) {\n\t\t\t\t\tclearTimeout(updateTimer);\n\t\t\t\t\tupdateTimer = undefined;\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tconst scheduleOutputUpdate = () => {\n\t\t\t\tif (!onUpdate) return;\n\t\t\t\tupdateDirty = true;\n\t\t\t\tconst delay = BASH_UPDATE_THROTTLE_MS - (Date.now() - lastUpdateAt);\n\t\t\t\tif (delay <= 0) {\n\t\t\t\t\tclearUpdateTimer();\n\t\t\t\t\temitOutputUpdate();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tupdateTimer ??= setTimeout(() => {\n\t\t\t\t\tupdateTimer = undefined;\n\t\t\t\t\temitOutputUpdate();\n\t\t\t\t}, delay);\n\t\t\t};\n\n\t\t\tif (onUpdate) {\n\t\t\t\tonUpdate({ content: [], details: undefined });\n\t\t\t}\n\n\t\t\tconst handleData = (data: Buffer) => {\n\t\t\t\toutput.append(data);\n\t\t\t\tscheduleOutputUpdate();\n\t\t\t};\n\n\t\t\tconst finishOutput = async () => {\n\t\t\t\toutput.finish();\n\t\t\t\tclearUpdateTimer();\n\t\t\t\temitOutputUpdate();\n\t\t\t\tconst snapshot = output.snapshot({ persistIfTruncated: true });\n\t\t\t\tawait output.closeTempFile();\n\t\t\t\treturn snapshot;\n\t\t\t};\n\n\t\t\tconst formatOutput = (snapshot: OutputSnapshot, emptyText = \"(no output)\") => {\n\t\t\t\tconst truncation = snapshot.truncation;\n\t\t\t\tlet text = snapshot.content || emptyText;\n\t\t\t\tlet details: BashToolDetails | undefined;\n\t\t\t\tif (truncation.truncated) {\n\t\t\t\t\tdetails = { truncation, fullOutputPath: snapshot.fullOutputPath };\n\t\t\t\t\tconst startLine = truncation.totalLines - truncation.outputLines + 1;\n\t\t\t\t\tconst endLine = truncation.totalLines;\n\t\t\t\t\tif (truncation.lastLinePartial) {\n\t\t\t\t\t\tconst lastLineSize = formatSize(output.getLastLineBytes());\n\t\t\t\t\t\ttext += `\\n\\n[Showing last ${formatSize(truncation.outputBytes)} of line ${endLine} (line is ${lastLineSize}). Full output: ${snapshot.fullOutputPath}]`;\n\t\t\t\t\t} else if (truncation.truncatedBy === \"lines\") {\n\t\t\t\t\t\ttext += `\\n\\n[Showing lines ${startLine}-${endLine} of ${truncation.totalLines}. Full output: ${snapshot.fullOutputPath}]`;\n\t\t\t\t\t} else {\n\t\t\t\t\t\ttext += `\\n\\n[Showing lines ${startLine}-${endLine} of ${truncation.totalLines} (${formatSize(DEFAULT_MAX_BYTES)} limit). Full output: ${snapshot.fullOutputPath}]`;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn { text, details };\n\t\t\t};\n\n\t\t\ttry {\n\t\t\t\tlet exitCode: number | null;\n\t\t\t\ttry {\n\t\t\t\t\tconst result = await ops.exec(spawnContext.command, {\n\t\t\t\t\t\tcwd: spawnContext.cwd,\n\t\t\t\t\t\tonData: handleData,\n\t\t\t\t\t\tsignal,\n\t\t\t\t\t\ttimeout,\n\t\t\t\t\t\tenv: spawnContext.env,\n\t\t\t\t\t});\n\t\t\t\t\texitCode = result.exitCode;\n\t\t\t\t} catch (err) {\n\t\t\t\t\tconst snapshot = await finishOutput();\n\t\t\t\t\tconst { text } = formatOutput(snapshot, \"\");\n\t\t\t\t\tthrowBashExecutionError(err, text);\n\t\t\t\t}\n\n\t\t\t\tconst snapshot = await finishOutput();\n\t\t\t\tconst { text: outputText, details } = formatOutput(snapshot);\n\t\t\t\tif (exitCode !== 0 && exitCode !== null) {\n\t\t\t\t\tthrow new Error(appendBashStatus(outputText, `Command exited with code ${exitCode}`));\n\t\t\t\t}\n\t\t\t\treturn { content: [{ type: \"text\", text: outputText }], details };\n\t\t\t} finally {\n\t\t\t\tclearUpdateTimer();\n\t\t\t}\n\t\t},\n\t\trenderCall(args, _theme, context) {\n\t\t\tconst state = context.state;\n\t\t\tif (context.executionStarted && state.startedAt === undefined) {\n\t\t\t\tstate.startedAt = Date.now();\n\t\t\t\tstate.endedAt = undefined;\n\t\t\t}\n\t\t\tconst text = (context.lastComponent as Text | undefined) ?? new Text(\"\", 0, 0);\n\t\t\ttext.setText(formatBashCall(args, ops.getBackendInfo?.()));\n\t\t\treturn text;\n\t\t},\n\t\trenderResult(result, options, _theme, context) {\n\t\t\tconst state = context.state;\n\t\t\tif (state.startedAt !== undefined && options.isPartial && !state.interval) {\n\t\t\t\tstate.interval = setInterval(() => context.invalidate(), 1000);\n\t\t\t}\n\t\t\tif (!options.isPartial || context.isError) {\n\t\t\t\tstate.endedAt ??= Date.now();\n\t\t\t\tif (state.interval) {\n\t\t\t\t\tclearInterval(state.interval);\n\t\t\t\t\tstate.interval = undefined;\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst component =\n\t\t\t\t(context.lastComponent as BashResultRenderComponent | undefined) ?? new BashResultRenderComponent();\n\t\t\trebuildBashResultRenderComponent(\n\t\t\t\tcomponent,\n\t\t\t\tresult as any,\n\t\t\t\toptions,\n\t\t\t\tcontext.isError,\n\t\t\t\tcontext.showImages,\n\t\t\t\tstate.startedAt,\n\t\t\t\tstate.endedAt,\n\t\t\t\tops.getBackendInfo?.(),\n\t\t\t);\n\t\t\tcomponent.invalidate();\n\t\t\treturn component;\n\t\t},\n\t};\n}\n\nexport function createBashTool(operations: ToolOperations, options?: BashToolOptions): AgentTool<typeof bashSchema> {\n\treturn wrapToolDefinition(createBashToolDefinition(operations, options));\n}\n"]}
@@ -1,3 +1,5 @@
1
+ /** Apply a channel-local precondition inside the existing process-wide per-file queue. */
2
+ export declare function withFileMutationGuard<T>(guard: (filePath: string) => Promise<void>, action: () => Promise<T>): Promise<T>;
1
3
  /**
2
4
  * Serialize file mutation operations targeting the same file.
3
5
  * Operations for different files still run in parallel.
@@ -1 +1 @@
1
- {"version":3,"file":"file-mutation-queue.d.ts","sourceRoot":"","sources":["../../../src/core/tools/file-mutation-queue.ts"],"names":[],"mappings":"AAkCA;;;GAGG;AACH,wBAAsB,qBAAqB,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CA6BjG","sourcesContent":["import { realpath } from \"node:fs/promises\";\nimport { basename, dirname, join, resolve } from \"node:path\";\n\nconst fileMutationQueues = new Map<string, Promise<void>>();\nlet registrationQueue = Promise.resolve();\n\nfunction isMissingPathError(error: unknown): boolean {\n\treturn (\n\t\ttypeof error === \"object\" &&\n\t\terror !== null &&\n\t\t\"code\" in error &&\n\t\t(error.code === \"ENOENT\" || error.code === \"ENOTDIR\")\n\t);\n}\n\nasync function getMutationQueueKey(filePath: string): Promise<string> {\n\tconst resolvedPath = resolve(filePath);\n\tconst missingSegments: string[] = [];\n\tlet existingAncestor = resolvedPath;\n\n\twhile (true) {\n\t\ttry {\n\t\t\tconst canonicalAncestor = await realpath(existingAncestor);\n\t\t\treturn missingSegments.length > 0 ? join(canonicalAncestor, ...missingSegments) : canonicalAncestor;\n\t\t} catch (error) {\n\t\t\tif (!isMissingPathError(error)) throw error;\n\t\t\tconst parent = dirname(existingAncestor);\n\t\t\tif (parent === existingAncestor) return resolvedPath;\n\t\t\tmissingSegments.unshift(basename(existingAncestor));\n\t\t\texistingAncestor = parent;\n\t\t}\n\t}\n}\n\n/**\n * Serialize file mutation operations targeting the same file.\n * Operations for different files still run in parallel.\n */\nexport async function withFileMutationQueue<T>(filePath: string, fn: () => Promise<T>): Promise<T> {\n\tconst registration = registrationQueue.then(async () => {\n\t\tconst key = await getMutationQueueKey(filePath);\n\t\tconst currentQueue = fileMutationQueues.get(key) ?? Promise.resolve();\n\n\t\tlet releaseNext!: () => void;\n\t\tconst nextQueue = new Promise<void>((resolveQueue) => {\n\t\t\treleaseNext = resolveQueue;\n\t\t});\n\t\tconst chainedQueue = currentQueue.then(() => nextQueue);\n\t\tfileMutationQueues.set(key, chainedQueue);\n\n\t\treturn { key, currentQueue, chainedQueue, releaseNext };\n\t});\n\tregistrationQueue = registration.then(\n\t\t() => undefined,\n\t\t() => undefined,\n\t);\n\n\tconst { key, currentQueue, chainedQueue, releaseNext } = await registration;\n\tawait currentQueue;\n\ttry {\n\t\treturn await fn();\n\t} finally {\n\t\treleaseNext();\n\t\tif (fileMutationQueues.get(key) === chainedQueue) {\n\t\t\tfileMutationQueues.delete(key);\n\t\t}\n\t}\n}\n"]}
1
+ {"version":3,"file":"file-mutation-queue.d.ts","sourceRoot":"","sources":["../../../src/core/tools/file-mutation-queue.ts"],"names":[],"mappings":"AAKA,0FAA0F;AAC1F,wBAAgB,qBAAqB,CAAC,CAAC,EACtC,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,EAC1C,MAAM,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GACtB,OAAO,CAAC,CAAC,CAAC,CAEZ;AAgCD;;;GAGG;AACH,wBAAsB,qBAAqB,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CA+BjG","sourcesContent":["import { AsyncLocalStorage } from \"node:async_hooks\";\nimport { realpath } from \"node:fs/promises\";\nimport { basename, dirname, join, resolve } from \"node:path\";\n\nconst mutationGuard = new AsyncLocalStorage<(filePath: string) => Promise<void>>();\n/** Apply a channel-local precondition inside the existing process-wide per-file queue. */\nexport function withFileMutationGuard<T>(\n\tguard: (filePath: string) => Promise<void>,\n\taction: () => Promise<T>,\n): Promise<T> {\n\treturn mutationGuard.run(guard, action);\n}\nconst fileMutationQueues = new Map<string, Promise<void>>();\nlet registrationQueue = Promise.resolve();\n\nfunction isMissingPathError(error: unknown): boolean {\n\treturn (\n\t\ttypeof error === \"object\" &&\n\t\terror !== null &&\n\t\t\"code\" in error &&\n\t\t(error.code === \"ENOENT\" || error.code === \"ENOTDIR\")\n\t);\n}\n\nasync function getMutationQueueKey(filePath: string): Promise<string> {\n\tconst resolvedPath = resolve(filePath);\n\tconst missingSegments: string[] = [];\n\tlet existingAncestor = resolvedPath;\n\n\twhile (true) {\n\t\ttry {\n\t\t\tconst canonicalAncestor = await realpath(existingAncestor);\n\t\t\treturn missingSegments.length > 0 ? join(canonicalAncestor, ...missingSegments) : canonicalAncestor;\n\t\t} catch (error) {\n\t\t\tif (!isMissingPathError(error)) throw error;\n\t\t\tconst parent = dirname(existingAncestor);\n\t\t\tif (parent === existingAncestor) return resolvedPath;\n\t\t\tmissingSegments.unshift(basename(existingAncestor));\n\t\t\texistingAncestor = parent;\n\t\t}\n\t}\n}\n\n/**\n * Serialize file mutation operations targeting the same file.\n * Operations for different files still run in parallel.\n */\nexport async function withFileMutationQueue<T>(filePath: string, fn: () => Promise<T>): Promise<T> {\n\tconst guard = mutationGuard.getStore();\n\tconst registration = registrationQueue.then(async () => {\n\t\tconst key = await getMutationQueueKey(filePath);\n\t\tconst currentQueue = fileMutationQueues.get(key) ?? Promise.resolve();\n\n\t\tlet releaseNext!: () => void;\n\t\tconst nextQueue = new Promise<void>((resolveQueue) => {\n\t\t\treleaseNext = resolveQueue;\n\t\t});\n\t\tconst chainedQueue = currentQueue.then(() => nextQueue);\n\t\tfileMutationQueues.set(key, chainedQueue);\n\n\t\treturn { key, currentQueue, chainedQueue, releaseNext };\n\t});\n\tregistrationQueue = registration.then(\n\t\t() => undefined,\n\t\t() => undefined,\n\t);\n\n\tconst { key, currentQueue, chainedQueue, releaseNext } = await registration;\n\tawait currentQueue;\n\ttry {\n\t\tif (guard) await guard(filePath);\n\t\treturn await fn();\n\t} finally {\n\t\treleaseNext();\n\t\tif (fileMutationQueues.get(key) === chainedQueue) {\n\t\t\tfileMutationQueues.delete(key);\n\t\t}\n\t}\n}\n"]}
@@ -1,5 +1,11 @@
1
+ import { AsyncLocalStorage } from "node:async_hooks";
1
2
  import { realpath } from "node:fs/promises";
2
3
  import { basename, dirname, join, resolve } from "node:path";
4
+ const mutationGuard = new AsyncLocalStorage();
5
+ /** Apply a channel-local precondition inside the existing process-wide per-file queue. */
6
+ export function withFileMutationGuard(guard, action) {
7
+ return mutationGuard.run(guard, action);
8
+ }
3
9
  const fileMutationQueues = new Map();
4
10
  let registrationQueue = Promise.resolve();
5
11
  function isMissingPathError(error) {
@@ -33,6 +39,7 @@ async function getMutationQueueKey(filePath) {
33
39
  * Operations for different files still run in parallel.
34
40
  */
35
41
  export async function withFileMutationQueue(filePath, fn) {
42
+ const guard = mutationGuard.getStore();
36
43
  const registration = registrationQueue.then(async () => {
37
44
  const key = await getMutationQueueKey(filePath);
38
45
  const currentQueue = fileMutationQueues.get(key) ?? Promise.resolve();
@@ -48,6 +55,8 @@ export async function withFileMutationQueue(filePath, fn) {
48
55
  const { key, currentQueue, chainedQueue, releaseNext } = await registration;
49
56
  await currentQueue;
50
57
  try {
58
+ if (guard)
59
+ await guard(filePath);
51
60
  return await fn();
52
61
  }
53
62
  finally {
@@ -1 +1 @@
1
- {"version":3,"file":"file-mutation-queue.js","sourceRoot":"","sources":["../../../src/core/tools/file-mutation-queue.ts"],"names":[],"mappings":"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","sourcesContent":["import { realpath } from \"node:fs/promises\";\nimport { basename, dirname, join, resolve } from \"node:path\";\n\nconst fileMutationQueues = new Map<string, Promise<void>>();\nlet registrationQueue = Promise.resolve();\n\nfunction isMissingPathError(error: unknown): boolean {\n\treturn (\n\t\ttypeof error === \"object\" &&\n\t\terror !== null &&\n\t\t\"code\" in error &&\n\t\t(error.code === \"ENOENT\" || error.code === \"ENOTDIR\")\n\t);\n}\n\nasync function getMutationQueueKey(filePath: string): Promise<string> {\n\tconst resolvedPath = resolve(filePath);\n\tconst missingSegments: string[] = [];\n\tlet existingAncestor = resolvedPath;\n\n\twhile (true) {\n\t\ttry {\n\t\t\tconst canonicalAncestor = await realpath(existingAncestor);\n\t\t\treturn missingSegments.length > 0 ? join(canonicalAncestor, ...missingSegments) : canonicalAncestor;\n\t\t} catch (error) {\n\t\t\tif (!isMissingPathError(error)) throw error;\n\t\t\tconst parent = dirname(existingAncestor);\n\t\t\tif (parent === existingAncestor) return resolvedPath;\n\t\t\tmissingSegments.unshift(basename(existingAncestor));\n\t\t\texistingAncestor = parent;\n\t\t}\n\t}\n}\n\n/**\n * Serialize file mutation operations targeting the same file.\n * Operations for different files still run in parallel.\n */\nexport async function withFileMutationQueue<T>(filePath: string, fn: () => Promise<T>): Promise<T> {\n\tconst registration = registrationQueue.then(async () => {\n\t\tconst key = await getMutationQueueKey(filePath);\n\t\tconst currentQueue = fileMutationQueues.get(key) ?? Promise.resolve();\n\n\t\tlet releaseNext!: () => void;\n\t\tconst nextQueue = new Promise<void>((resolveQueue) => {\n\t\t\treleaseNext = resolveQueue;\n\t\t});\n\t\tconst chainedQueue = currentQueue.then(() => nextQueue);\n\t\tfileMutationQueues.set(key, chainedQueue);\n\n\t\treturn { key, currentQueue, chainedQueue, releaseNext };\n\t});\n\tregistrationQueue = registration.then(\n\t\t() => undefined,\n\t\t() => undefined,\n\t);\n\n\tconst { key, currentQueue, chainedQueue, releaseNext } = await registration;\n\tawait currentQueue;\n\ttry {\n\t\treturn await fn();\n\t} finally {\n\t\treleaseNext();\n\t\tif (fileMutationQueues.get(key) === chainedQueue) {\n\t\t\tfileMutationQueues.delete(key);\n\t\t}\n\t}\n}\n"]}
1
+ {"version":3,"file":"file-mutation-queue.js","sourceRoot":"","sources":["../../../src/core/tools/file-mutation-queue.ts"],"names":[],"mappings":"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","sourcesContent":["import { AsyncLocalStorage } from \"node:async_hooks\";\nimport { realpath } from \"node:fs/promises\";\nimport { basename, dirname, join, resolve } from \"node:path\";\n\nconst mutationGuard = new AsyncLocalStorage<(filePath: string) => Promise<void>>();\n/** Apply a channel-local precondition inside the existing process-wide per-file queue. */\nexport function withFileMutationGuard<T>(\n\tguard: (filePath: string) => Promise<void>,\n\taction: () => Promise<T>,\n): Promise<T> {\n\treturn mutationGuard.run(guard, action);\n}\nconst fileMutationQueues = new Map<string, Promise<void>>();\nlet registrationQueue = Promise.resolve();\n\nfunction isMissingPathError(error: unknown): boolean {\n\treturn (\n\t\ttypeof error === \"object\" &&\n\t\terror !== null &&\n\t\t\"code\" in error &&\n\t\t(error.code === \"ENOENT\" || error.code === \"ENOTDIR\")\n\t);\n}\n\nasync function getMutationQueueKey(filePath: string): Promise<string> {\n\tconst resolvedPath = resolve(filePath);\n\tconst missingSegments: string[] = [];\n\tlet existingAncestor = resolvedPath;\n\n\twhile (true) {\n\t\ttry {\n\t\t\tconst canonicalAncestor = await realpath(existingAncestor);\n\t\t\treturn missingSegments.length > 0 ? join(canonicalAncestor, ...missingSegments) : canonicalAncestor;\n\t\t} catch (error) {\n\t\t\tif (!isMissingPathError(error)) throw error;\n\t\t\tconst parent = dirname(existingAncestor);\n\t\t\tif (parent === existingAncestor) return resolvedPath;\n\t\t\tmissingSegments.unshift(basename(existingAncestor));\n\t\t\texistingAncestor = parent;\n\t\t}\n\t}\n}\n\n/**\n * Serialize file mutation operations targeting the same file.\n * Operations for different files still run in parallel.\n */\nexport async function withFileMutationQueue<T>(filePath: string, fn: () => Promise<T>): Promise<T> {\n\tconst guard = mutationGuard.getStore();\n\tconst registration = registrationQueue.then(async () => {\n\t\tconst key = await getMutationQueueKey(filePath);\n\t\tconst currentQueue = fileMutationQueues.get(key) ?? Promise.resolve();\n\n\t\tlet releaseNext!: () => void;\n\t\tconst nextQueue = new Promise<void>((resolveQueue) => {\n\t\t\treleaseNext = resolveQueue;\n\t\t});\n\t\tconst chainedQueue = currentQueue.then(() => nextQueue);\n\t\tfileMutationQueues.set(key, chainedQueue);\n\n\t\treturn { key, currentQueue, chainedQueue, releaseNext };\n\t});\n\tregistrationQueue = registration.then(\n\t\t() => undefined,\n\t\t() => undefined,\n\t);\n\n\tconst { key, currentQueue, chainedQueue, releaseNext } = await registration;\n\tawait currentQueue;\n\ttry {\n\t\tif (guard) await guard(filePath);\n\t\treturn await fn();\n\t} finally {\n\t\treleaseNext();\n\t\tif (fileMutationQueues.get(key) === chainedQueue) {\n\t\t\tfileMutationQueues.delete(key);\n\t\t}\n\t}\n}\n"]}
@@ -6,6 +6,7 @@ import type { WorkspaceIdentity } from "../workspace-identity.ts";
6
6
  export type ToolAccessMode = "exists" | "read" | "write" | "readwrite";
7
7
  export type WorkspaceToolExecutionTarget = "local" | "remote" | "unavailable";
8
8
  export type RemoteWorkspaceToolExecutionTarget = Exclude<WorkspaceToolExecutionTarget, "local">;
9
+ type RemoteToolTransportProtocol = "ws";
9
10
  export interface ToolFileStat {
10
11
  isDirectory: () => boolean;
11
12
  isFile: () => boolean;
@@ -133,7 +134,8 @@ export declare class DeferredRemoteToolOperations implements ToolOperations {
133
134
  private remoteCatalogUnsubscribe;
134
135
  private readonly catalogListeners;
135
136
  private disposePromise;
136
- constructor(cwd: string);
137
+ private readonly onDisconnect;
138
+ constructor(cwd: string, onDisconnect?: (message: string) => void);
137
139
  configureRemote(url: string, options?: DeferredRemoteToolOperationsConnectOptions): Promise<ToolBackendInfo>;
138
140
  clear(): Promise<void>;
139
141
  private requireOperations;
@@ -150,6 +152,7 @@ export declare class DeferredRemoteToolOperations implements ToolOperations {
150
152
  grep(options: ToolGrepOptions): Promise<ToolGrepResult>;
151
153
  detectImageMimeType(path: string): Promise<string | null | undefined>;
152
154
  getBackendInfo(): ToolBackendInfo;
155
+ isDisconnected(): boolean;
153
156
  resolveWorkspaceToolExecution(name: string, parameterSchema: unknown): RemoteWorkspaceToolExecutionTarget;
154
157
  executeWorkspaceTool(name: string, invocation: WorkspaceToolRemoteInvocation): Promise<AgentToolResult<unknown>>;
155
158
  getRemoteLspStatus(): RemoteLspStatus;
@@ -160,27 +163,33 @@ export declare class DeferredRemoteToolOperations implements ToolOperations {
160
163
  export interface RemoteToolOperationsConnectOptions {
161
164
  token?: string;
162
165
  handshakeTimeoutMs?: number;
166
+ onDisconnect?: (message: string) => void;
163
167
  }
164
168
  export declare class RemoteToolOperations implements ToolOperations {
165
169
  readonly url: string;
166
- readonly protocol: "ws";
170
+ readonly protocol: RemoteToolTransportProtocol;
167
171
  cwd: string;
168
172
  workspacePathFlavor: PortablePathFlavor;
169
173
  private workspaceId;
170
174
  private readonly socket;
175
+ private readonly isShared;
171
176
  private readonly client;
172
177
  private readonly localToolSchemas;
173
178
  private readonly catalogListeners;
174
179
  private remoteLspStatus;
180
+ private disconnected;
175
181
  private disposePromise;
176
182
  private constructor();
177
183
  static connect(url: string, options?: RemoteToolOperationsConnectOptions): Promise<RemoteToolOperations>;
184
+ static connectGuestShared(url: string, root: string, token: string, options?: RemoteToolOperationsConnectOptions): Promise<RemoteToolOperations>;
178
185
  resolveWorkspaceToolExecution(name: string, parameterSchema: unknown): RemoteWorkspaceToolExecutionTarget;
179
186
  executeWorkspaceTool(name: string, invocation: WorkspaceToolRemoteInvocation): Promise<AgentToolResult<unknown>>;
180
187
  getRemoteLspStatus(): RemoteLspStatus;
181
188
  onWorkspaceToolCatalogChanged(listener: () => void | Promise<void>): () => void;
182
189
  private refreshRemoteLspStatus;
183
190
  private request;
191
+ private revisionForTool;
192
+ private sharedExpectedRevision;
184
193
  exec(command: string, options: ToolExecOptions): Promise<ToolExecResult>;
185
194
  access(path: string, mode?: ToolAccessMode): Promise<void>;
186
195
  readFile(path: string): Promise<Buffer>;
@@ -195,7 +204,9 @@ export declare class RemoteToolOperations implements ToolOperations {
195
204
  grep(options: ToolGrepOptions): Promise<ToolGrepResult>;
196
205
  detectImageMimeType(path: string): Promise<string | null | undefined>;
197
206
  getBackendInfo(): ToolBackendInfo;
207
+ isDisconnected(): boolean;
198
208
  dispose(): Promise<void>;
199
209
  }
200
210
  export declare function createRemoteToolOperations(url: string, options?: RemoteToolOperationsConnectOptions): Promise<RemoteToolOperations>;
211
+ export {};
201
212
  //# sourceMappingURL=operations.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"operations.d.ts","sourceRoot":"","sources":["../../../src/core/tools/operations.ts"],"names":[],"mappings":"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","sourcesContent":["import { type ChildProcess, spawn } from \"node:child_process\";\nimport { createHash } from \"node:crypto\";\nimport { constants, createReadStream, createWriteStream, type Stats, type WriteStream } from \"node:fs\";\nimport {\n\taccess as fsAccess,\n\tmkdir as fsMkdir,\n\treaddir as fsReaddir,\n\treadFile as fsReadFile,\n\tstat as fsStat,\n\twriteFile as fsWriteFile,\n} from \"node:fs/promises\";\nimport { posix, win32 } from \"node:path\";\nimport { pipeline } from \"node:stream/promises\";\nimport type { AgentToolResult, AgentToolUpdateCallback } from \"@greppleai/grip-agent-core\";\nimport WebSocket, { type RawData } from \"ws\";\nimport { DAEMON_WEBSOCKET_PROTOCOL } from \"../../daemon/config.ts\";\nimport { waitForChildProcess } from \"../../utils/child-process.ts\";\nimport { detectSupportedImageMimeTypeFromFile } from \"../../utils/mime.ts\";\nimport {\n\tgetShellConfig,\n\tgetShellEnv,\n\tkillProcessTree,\n\ttrackDetachedChildPid,\n\tuntrackDetachedChildPid,\n} from \"../../utils/shell.ts\";\nimport type { PortablePathFlavor } from \"../lsp/portable-path.ts\";\nimport {\n\tDEFAULT_REMOTE_WORKSPACE_PROTOCOL_LIMITS,\n\tdecodeCanonicalBase64,\n\thashRemoteWorkspaceJson,\n\tparseRemoteWorkspaceToolResult,\n\ttype RemoteLspStatus,\n\ttype RemoteWorkspaceClientMessage,\n\ttype RemoteWorkspaceMethod,\n} from \"../remote-workspace-protocol/contract.ts\";\nimport {\n\tRemoteWorkspaceClientProtocol,\n\ttype RemoteWorkspaceProtocolCloseReason,\n\tRemoteWorkspaceRequestError,\n} from \"../remote-workspace-protocol/session.ts\";\nimport type { WorkspaceIdentity } from \"../workspace-identity.ts\";\n\nexport type ToolAccessMode = \"exists\" | \"read\" | \"write\" | \"readwrite\";\nexport type WorkspaceToolExecutionTarget = \"local\" | \"remote\" | \"unavailable\";\nexport type RemoteWorkspaceToolExecutionTarget = Exclude<WorkspaceToolExecutionTarget, \"local\">;\n\nexport interface ToolFileStat {\n\tisDirectory: () => boolean;\n\tisFile: () => boolean;\n}\n\nexport interface ToolExecOptions {\n\tcwd?: string;\n\tonData: (data: Buffer) => void;\n\tsignal?: AbortSignal;\n\ttimeout?: number;\n\tenv?: NodeJS.ProcessEnv;\n}\n\nexport interface ToolExecResult {\n\texitCode: number | null;\n}\n\nexport interface ToolMkdirOptions {\n\trecursive?: boolean;\n}\n\nexport interface ToolGlobOptions {\n\tignore: string[];\n\tlimit: number;\n}\n\nexport interface ToolGrepOptions {\n\tpattern: string;\n\tpath: string;\n\tglob?: string;\n\tignoreCase?: boolean;\n\tliteral?: boolean;\n\tlimit: number;\n}\n\nexport interface ToolGrepMatch {\n\tfilePath: string;\n\tlineNumber: number;\n\tlineText?: string;\n}\n\nexport interface ToolGrepResult {\n\tisDirectory: boolean;\n\tmatches: ToolGrepMatch[];\n}\n\nexport type ToolBackendInfo =\n\t| { type: \"local\"; cwd: string }\n\t| { type: \"remote\"; cwd: string; configured: false }\n\t| {\n\t\t\ttype: \"remote\";\n\t\t\tcwd: string;\n\t\t\turl: string;\n\t\t\tprotocol: \"ws\";\n\t\t\tconfigured: true;\n\t\t\tworkspace: WorkspaceIdentity;\n\t };\n\n// grip-ignore noNearIdenticalDataStructures: Remote backend invocations require host-normalized execution options, while public host invocations accept omitted options and apply defaults.\nexport interface WorkspaceToolRemoteInvocation {\n\ttoolCallId: string;\n\targuments: unknown;\n\tsignal?: AbortSignal;\n\tonUpdate?: AgentToolUpdateCallback<unknown>;\n\texecutionOptions: WorkspaceToolExecutionOptions;\n}\n\nexport interface GripSessionEnvironment {\n\tGRIP_SESSION_ID?: string;\n\tGRIP_SESSION_FILE?: string;\n\tGRIP_PROVIDER?: string;\n\tGRIP_MODEL?: string;\n\tGRIP_REASONING_LEVEL?: string;\n}\n\nexport const GRIP_SESSION_ENVIRONMENT_NAMES = Object.freeze([\n\t\"GRIP_SESSION_ID\",\n\t\"GRIP_SESSION_FILE\",\n\t\"GRIP_PROVIDER\",\n\t\"GRIP_MODEL\",\n\t\"GRIP_REASONING_LEVEL\",\n] as const);\n\nexport interface WorkspaceToolExecutionOptions {\n\timageAutoResize?: boolean;\n\tshellCommandPrefix?: string;\n\tsessionEnvironment?: GripSessionEnvironment;\n}\n\ninterface NormalizedRemoteWorkspaceUrl {\n\turl: string;\n\tprotocol: \"ws\";\n}\n\nexport interface DeferredRemoteToolOperationsConnectOptions extends RemoteToolOperationsConnectOptions {\n\texpectedCwd?: string;\n}\n\nexport type BorrowedToolOperations = Omit<ToolOperations, \"dispose\">;\n\nexport function borrowToolOperations(operations: ToolOperations): BorrowedToolOperations {\n\tconst boundMethods = new Map<PropertyKey, unknown>();\n\treturn new Proxy(operations, {\n\t\tget(target, property, receiver) {\n\t\t\tif (property === \"dispose\") return undefined;\n\t\t\tconst value = Reflect.get(target, property, receiver) as unknown;\n\t\t\tif (typeof value !== \"function\") return value;\n\t\t\tlet bound = boundMethods.get(property);\n\t\t\tif (!bound) {\n\t\t\t\tbound = value.bind(target);\n\t\t\t\tboundMethods.set(property, bound);\n\t\t\t}\n\t\t\treturn bound;\n\t\t},\n\t\thas(target, property) {\n\t\t\treturn property === \"dispose\" ? false : Reflect.has(target, property);\n\t\t},\n\t}) as BorrowedToolOperations;\n}\n\nexport interface ToolOperations {\n\tcwd: string;\n\texec(command: string, options: ToolExecOptions): Promise<ToolExecResult>;\n\taccess(path: string, mode?: ToolAccessMode): Promise<void>;\n\treadFile(path: string): Promise<Buffer>;\n\twriteFile(path: string, content: string | Buffer): Promise<void>;\n\treadResource?(path: string): Promise<Buffer>;\n\tuploadFile?(sourcePath: string, destinationPath: string): Promise<void>;\n\tdownloadFile?(sourcePath: string, destinationPath: string): Promise<void>;\n\tmkdir(path: string, options?: ToolMkdirOptions): Promise<void>;\n\tstat(path: string): Promise<ToolFileStat>;\n\treaddir(path: string): Promise<string[]>;\n\tglob?(pattern: string, cwd: string, options: ToolGlobOptions): Promise<string[]>;\n\tgrep?(options: ToolGrepOptions): Promise<ToolGrepResult>;\n\tdetectImageMimeType?(path: string): Promise<string | null | undefined>;\n\tgetBackendInfo?(): ToolBackendInfo;\n\tresolveWorkspaceToolExecution?(name: string, parameterSchema: unknown): WorkspaceToolExecutionTarget;\n\texecuteWorkspaceTool?(name: string, invocation: WorkspaceToolRemoteInvocation): Promise<AgentToolResult<unknown>>;\n\tonWorkspaceToolCatalogChanged?(listener: () => void | Promise<void>): () => void;\n\tgetRemoteLspStatus?(): RemoteLspStatus;\n\tdispose?(): Promise<void>;\n}\n\nexport interface LocalToolOperationsOptions {\n\tshellPath?: string;\n}\n\nfunction workspaceRootsEqual(left: string, right: string, pathFlavor: PortablePathFlavor): boolean {\n\tif (pathFlavor === \"windows\") return win32.normalize(left).toLowerCase() === win32.normalize(right).toLowerCase();\n\treturn posix.normalize(left) === posix.normalize(right);\n}\n\nfunction accessModeToFsMode(mode: ToolAccessMode | undefined): number {\n\tswitch (mode) {\n\t\tcase \"read\":\n\t\t\treturn constants.R_OK;\n\t\tcase \"write\":\n\t\t\treturn constants.W_OK;\n\t\tcase \"readwrite\":\n\t\t\treturn constants.R_OK | constants.W_OK;\n\t\tcase \"exists\":\n\t\tcase undefined:\n\t\t\treturn constants.F_OK;\n\t}\n}\n\nconst MAX_TIMEOUT_MS = 2_147_483_647;\nconst MAX_TIMEOUT_SECONDS = MAX_TIMEOUT_MS / 1000;\n\nfunction resolveTimeoutMs(timeout: number | undefined): number | undefined {\n\tif (timeout === undefined) return undefined;\n\tif (!Number.isFinite(timeout) || timeout <= 0) {\n\t\tthrow new Error(\"Invalid timeout: must be a finite number of seconds\");\n\t}\n\n\tconst timeoutMs = timeout * 1000;\n\tif (timeoutMs > MAX_TIMEOUT_MS) {\n\t\tthrow new Error(`Invalid timeout: maximum is ${MAX_TIMEOUT_SECONDS} seconds`);\n\t}\n\treturn timeoutMs;\n}\n\nfunction registerChildProcessAbort(signal: AbortSignal | undefined, child: ChildProcess): () => void {\n\tconst onAbort = (): void => {\n\t\tif (child.pid) killProcessTree(child.pid);\n\t};\n\tif (!signal) return () => undefined;\n\tif (signal.aborted) onAbort();\n\telse signal.addEventListener(\"abort\", onAbort, { once: true });\n\treturn () => signal.removeEventListener(\"abort\", onAbort);\n}\n\nfunction terminateLingeringProcessGroup(child: ChildProcess): void {\n\tif (!child.pid || process.platform === \"win32\") return;\n\ttry {\n\t\tprocess.kill(-child.pid, 0);\n\t\tkillProcessTree(child.pid);\n\t} catch {}\n}\n\nfunction cleanupChildExecution(\n\tchild: ChildProcess,\n\ttimeoutHandle: NodeJS.Timeout | undefined,\n\tremoveAbortListener: () => void,\n): void {\n\tif (child.pid) untrackDetachedChildPid(child.pid);\n\tif (timeoutHandle) clearTimeout(timeoutHandle);\n\tremoveAbortListener();\n}\n\nasync function copyFileStream(sourcePath: string, destinationPath: string): Promise<void> {\n\tawait pipeline(createReadStream(sourcePath), createWriteStream(destinationPath));\n}\n\nfunction writeStreamChunk(stream: WriteStream, chunk: Buffer): Promise<void> {\n\treturn new Promise((resolve, reject) => {\n\t\tconst onError = (error: Error) => {\n\t\t\tstream.off(\"error\", onError);\n\t\t\treject(error);\n\t\t};\n\t\tstream.once(\"error\", onError);\n\t\tstream.write(chunk, (error) => {\n\t\t\tstream.off(\"error\", onError);\n\t\t\tif (error) {\n\t\t\t\treject(error);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tresolve();\n\t\t});\n\t});\n}\n\nfunction endWriteStream(stream: WriteStream): Promise<void> {\n\treturn new Promise((resolve, reject) => {\n\t\tconst onError = (error: Error) => {\n\t\t\tstream.off(\"error\", onError);\n\t\t\treject(error);\n\t\t};\n\t\tstream.once(\"error\", onError);\n\t\tstream.end(() => {\n\t\t\tstream.off(\"error\", onError);\n\t\t\tresolve();\n\t\t});\n\t});\n}\n\nexport class LocalToolOperations implements ToolOperations {\n\tcwd: string;\n\tprivate shellPath: string | undefined;\n\n\tconstructor(cwd: string, options: LocalToolOperationsOptions = {}) {\n\t\tthis.cwd = cwd;\n\t\tthis.shellPath = options.shellPath;\n\t}\n\n\tsetShellPath(shellPath: string | undefined): void {\n\t\tthis.shellPath = shellPath;\n\t}\n\n\tasync exec(command: string, options: ToolExecOptions): Promise<ToolExecResult> {\n\t\tconst timeoutMs = resolveTimeoutMs(options.timeout);\n\t\tconst cwd = options.cwd ?? this.cwd;\n\t\tconst { shell, args } = getShellConfig(this.shellPath);\n\t\ttry {\n\t\t\tawait fsAccess(cwd, constants.F_OK);\n\t\t} catch {\n\t\t\tthrow new Error(`Working directory does not exist: ${cwd}\\nCannot execute bash commands.`);\n\t\t}\n\t\tif (options.signal?.aborted) {\n\t\t\tthrow new Error(\"aborted\");\n\t\t}\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tconst child = spawn(shell, [...args, command], {\n\t\t\t\tcwd,\n\t\t\t\tdetached: process.platform !== \"win32\",\n\t\t\t\tenv: options.env ?? getShellEnv(),\n\t\t\t\tstdio: [\"ignore\", \"pipe\", \"pipe\"],\n\t\t\t\twindowsHide: true,\n\t\t\t});\n\t\t\tif (child.pid) trackDetachedChildPid(child.pid);\n\t\t\tlet timedOut = false;\n\t\t\tlet timeoutHandle: NodeJS.Timeout | undefined;\n\t\t\tif (timeoutMs !== undefined) {\n\t\t\t\ttimeoutHandle = setTimeout(() => {\n\t\t\t\t\ttimedOut = true;\n\t\t\t\t\tif (child.pid) killProcessTree(child.pid);\n\t\t\t\t}, timeoutMs);\n\t\t\t}\n\t\t\tchild.stdout?.on(\"data\", options.onData);\n\t\t\tchild.stderr?.on(\"data\", options.onData);\n\t\t\tconst removeAbortListener = registerChildProcessAbort(options.signal, child);\n\t\t\twaitForChildProcess(child)\n\t\t\t\t.then((code) => {\n\t\t\t\t\tterminateLingeringProcessGroup(child);\n\t\t\t\t\tcleanupChildExecution(child, timeoutHandle, removeAbortListener);\n\t\t\t\t\tif (options.signal?.aborted) {\n\t\t\t\t\t\treject(new Error(\"aborted\"));\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tif (timedOut) {\n\t\t\t\t\t\treject(new Error(`timeout:${options.timeout}`));\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tresolve({ exitCode: code });\n\t\t\t\t})\n\t\t\t\t.catch((error: unknown) => {\n\t\t\t\t\tcleanupChildExecution(child, timeoutHandle, removeAbortListener);\n\t\t\t\t\treject(error);\n\t\t\t\t});\n\t\t});\n\t}\n\n\tasync access(path: string, mode?: ToolAccessMode): Promise<void> {\n\t\tawait fsAccess(path, accessModeToFsMode(mode));\n\t}\n\n\tasync readFile(path: string): Promise<Buffer> {\n\t\treturn fsReadFile(path);\n\t}\n\n\tasync writeFile(path: string, content: string | Buffer): Promise<void> {\n\t\tawait fsWriteFile(path, content, typeof content === \"string\" ? \"utf-8\" : undefined);\n\t}\n\n\tasync uploadFile(sourcePath: string, destinationPath: string): Promise<void> {\n\t\tawait copyFileStream(sourcePath, destinationPath);\n\t}\n\n\tasync downloadFile(sourcePath: string, destinationPath: string): Promise<void> {\n\t\tawait copyFileStream(sourcePath, destinationPath);\n\t}\n\n\tasync mkdir(path: string, options: ToolMkdirOptions = {}): Promise<void> {\n\t\tawait fsMkdir(path, { recursive: options.recursive ?? false });\n\t}\n\n\tasync stat(path: string): Promise<Stats> {\n\t\treturn fsStat(path);\n\t}\n\n\tasync readdir(path: string): Promise<string[]> {\n\t\treturn fsReaddir(path);\n\t}\n\n\tasync detectImageMimeType(path: string): Promise<string | null | undefined> {\n\t\treturn detectSupportedImageMimeTypeFromFile(path);\n\t}\n\n\tgetBackendInfo(): ToolBackendInfo {\n\t\treturn { type: \"local\", cwd: this.cwd };\n\t}\n}\n\nexport class DeferredRemoteToolOperations implements ToolOperations {\n\tcwd: string;\n\tprivate readonly expectedCwd: string;\n\tprivate operations: RemoteToolOperations | undefined;\n\tprivate remoteCatalogUnsubscribe: (() => void) | undefined;\n\tprivate readonly catalogListeners = new Set<() => void | Promise<void>>();\n\tprivate disposePromise: Promise<void> | undefined;\n\n\tconstructor(cwd: string) {\n\t\tthis.cwd = cwd;\n\t\tthis.expectedCwd = cwd;\n\t}\n\n\tasync configureRemote(\n\t\turl: string,\n\t\toptions: DeferredRemoteToolOperationsConnectOptions = {},\n\t): Promise<ToolBackendInfo> {\n\t\tconst next = await createRemoteToolOperations(url, options);\n\t\ttry {\n\t\t\tconst stat = await next.stat(next.cwd);\n\t\t\tif (!stat.isDirectory()) throw new Error(`Remote daemon cwd is not a directory: ${next.cwd}`);\n\t\t\tconst expectedCwd = options.expectedCwd ?? this.expectedCwd;\n\t\t\tif (!workspaceRootsEqual(next.cwd, expectedCwd, next.workspacePathFlavor)) {\n\t\t\t\tthrow new Error(`Remote daemon workspace root mismatch: expected ${expectedCwd}, received ${next.cwd}`);\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tawait next.dispose();\n\t\t\tthrow error;\n\t\t}\n\t\tawait this.replaceOperations(next);\n\t\treturn this.getBackendInfo();\n\t}\n\n\tasync clear(): Promise<void> {\n\t\tconst previous = this.operations;\n\t\tthis.remoteCatalogUnsubscribe?.();\n\t\tthis.remoteCatalogUnsubscribe = undefined;\n\t\tthis.operations = undefined;\n\t\tawait previous?.dispose?.();\n\t}\n\n\tprivate requireOperations(): RemoteToolOperations {\n\t\tif (!this.operations) {\n\t\t\tthrow new Error(\n\t\t\t\t\"Remote backend is not configured. Configure it over RPC or with /sandbox before using tools.\",\n\t\t\t);\n\t\t}\n\t\treturn this.operations;\n\t}\n\n\tasync exec(command: string, options: ToolExecOptions): Promise<ToolExecResult> {\n\t\treturn this.requireOperations().exec(command, options);\n\t}\n\n\tasync access(path: string, mode?: ToolAccessMode): Promise<void> {\n\t\tawait this.requireOperations().access(path, mode);\n\t}\n\n\tasync readFile(path: string): Promise<Buffer> {\n\t\treturn this.requireOperations().readFile(path);\n\t}\n\n\tasync writeFile(path: string, content: string | Buffer): Promise<void> {\n\t\tawait this.requireOperations().writeFile(path, content);\n\t}\n\n\tasync uploadFile(sourcePath: string, destinationPath: string): Promise<void> {\n\t\tconst operations = this.requireOperations();\n\t\tif (!operations.uploadFile) throw new Error(\"Remote backend does not support file upload\");\n\t\tawait operations.uploadFile(sourcePath, destinationPath);\n\t}\n\n\tasync downloadFile(sourcePath: string, destinationPath: string): Promise<void> {\n\t\tconst operations = this.requireOperations();\n\t\tif (!operations.downloadFile) throw new Error(\"Remote backend does not support file download\");\n\t\tawait operations.downloadFile(sourcePath, destinationPath);\n\t}\n\n\tasync mkdir(path: string, options?: ToolMkdirOptions): Promise<void> {\n\t\tawait this.requireOperations().mkdir(path, options);\n\t}\n\n\tasync stat(path: string): Promise<ToolFileStat> {\n\t\treturn this.requireOperations().stat(path);\n\t}\n\n\tasync readdir(path: string): Promise<string[]> {\n\t\treturn this.requireOperations().readdir(path);\n\t}\n\n\tasync glob(pattern: string, cwd: string, options: ToolGlobOptions): Promise<string[]> {\n\t\treturn this.requireOperations().glob(pattern, cwd, options);\n\t}\n\n\tasync grep(options: ToolGrepOptions): Promise<ToolGrepResult> {\n\t\treturn this.requireOperations().grep(options);\n\t}\n\n\tasync detectImageMimeType(path: string): Promise<string | null | undefined> {\n\t\treturn this.requireOperations().detectImageMimeType(path);\n\t}\n\n\tgetBackendInfo(): ToolBackendInfo {\n\t\treturn this.operations?.getBackendInfo() ?? { type: \"remote\", cwd: this.cwd, configured: false };\n\t}\n\n\tresolveWorkspaceToolExecution(name: string, parameterSchema: unknown): RemoteWorkspaceToolExecutionTarget {\n\t\treturn this.operations ? this.operations.resolveWorkspaceToolExecution(name, parameterSchema) : \"unavailable\";\n\t}\n\n\texecuteWorkspaceTool(name: string, invocation: WorkspaceToolRemoteInvocation): Promise<AgentToolResult<unknown>> {\n\t\treturn this.requireOperations().executeWorkspaceTool(name, invocation);\n\t}\n\n\tgetRemoteLspStatus(): RemoteLspStatus {\n\t\treturn this.operations?.getRemoteLspStatus() ?? { enabled: false, servers: [] };\n\t}\n\n\tonWorkspaceToolCatalogChanged(listener: () => void | Promise<void>): () => void {\n\t\tthis.catalogListeners.add(listener);\n\t\treturn () => this.catalogListeners.delete(listener);\n\t}\n\n\tdispose(): Promise<void> {\n\t\tthis.disposePromise ??= (async () => {\n\t\t\tthis.remoteCatalogUnsubscribe?.();\n\t\t\tthis.remoteCatalogUnsubscribe = undefined;\n\t\t\tconst previous = this.operations;\n\t\t\tthis.operations = undefined;\n\t\t\tawait previous?.dispose?.();\n\t\t})();\n\t\treturn this.disposePromise;\n\t}\n\n\tprivate async replaceOperations(next: RemoteToolOperations): Promise<void> {\n\t\tif (this.disposePromise) {\n\t\t\tawait next.dispose?.();\n\t\t\tthrow new Error(\"Deferred remote backend is disposed\");\n\t\t}\n\t\tconst previous = this.operations;\n\t\tthis.remoteCatalogUnsubscribe?.();\n\t\tthis.remoteCatalogUnsubscribe = undefined;\n\t\tthis.operations = next;\n\t\tthis.cwd = next.cwd;\n\t\tthis.remoteCatalogUnsubscribe = next.onWorkspaceToolCatalogChanged(() =>\n\t\t\tPromise.all([...this.catalogListeners].map((listener) => listener())).then(() => undefined),\n\t\t);\n\t\tawait previous?.dispose?.();\n\t}\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null;\n}\n\nfunction requireRecord(value: unknown, label: string): Record<string, unknown> {\n\tif (!isRecord(value)) throw new Error(`Invalid remote workspace ${label}`);\n\treturn value;\n}\n\nfunction requireString(value: unknown, label: string): string {\n\tif (typeof value !== \"string\") throw new Error(`Invalid remote workspace ${label}`);\n\treturn value;\n}\n\nfunction normalizeRemoteUrl(url: string): NormalizedRemoteWorkspaceUrl {\n\tconst parsed = new URL(url);\n\tif (parsed.protocol !== \"ws:\" && parsed.protocol !== \"wss:\") {\n\t\tthrow new Error(`--remote supports ws:// and wss:// URLs, got ${parsed.protocol}`);\n\t}\n\tif (parsed.username || parsed.password) throw new Error(\"Remote workspace URLs must not contain credentials\");\n\tif (parsed.hash) throw new Error(\"Remote workspace URLs must not contain fragments\");\n\treturn { url: parsed.toString(), protocol: \"ws\" };\n}\n\nfunction websocketPayload(data: RawData): Uint8Array {\n\tif (Buffer.isBuffer(data)) return Uint8Array.from(data);\n\tif (Array.isArray(data)) return Uint8Array.from(Buffer.concat(data));\n\treturn Uint8Array.from(new Uint8Array(data));\n}\n\nfunction websocketCloseCode(reason: RemoteWorkspaceProtocolCloseReason): number {\n\tswitch (reason.code) {\n\t\tcase \"normal\":\n\t\t\treturn 1000;\n\t\tcase \"protocol_error\":\n\t\t\treturn 1002;\n\t\tcase \"invalid_payload\":\n\t\t\treturn 1007;\n\t\tcase \"policy_violation\":\n\t\t\treturn 1008;\n\t\tcase \"message_too_large\":\n\t\t\treturn 1009;\n\t}\n}\n\nexport interface RemoteToolOperationsConnectOptions {\n\ttoken?: string;\n\thandshakeTimeoutMs?: number;\n}\n\nexport class RemoteToolOperations implements ToolOperations {\n\treadonly url: string;\n\treadonly protocol: \"ws\";\n\tcwd: string;\n\tworkspacePathFlavor: PortablePathFlavor = \"posix\";\n\tprivate workspaceId = \"\";\n\tprivate readonly socket: WebSocket;\n\tprivate readonly client: RemoteWorkspaceClientProtocol;\n\tprivate readonly localToolSchemas = new Map<string, string>();\n\tprivate readonly catalogListeners = new Set<() => void | Promise<void>>();\n\tprivate remoteLspStatus: RemoteLspStatus = { enabled: false, servers: [] };\n\tprivate disposePromise: Promise<void> | undefined;\n\n\tprivate constructor(url: string, protocol: \"ws\", socket: WebSocket, options: RemoteToolOperationsConnectOptions) {\n\t\tthis.url = url;\n\t\tthis.protocol = protocol;\n\t\tthis.socket = socket;\n\t\tthis.cwd = \"/\";\n\t\tconst transport = {\n\t\t\tsend: (message: RemoteWorkspaceClientMessage): Promise<void> =>\n\t\t\t\tnew Promise((resolve, reject) => {\n\t\t\t\t\tif (socket.readyState !== WebSocket.OPEN) {\n\t\t\t\t\t\treject(new Error(\"Remote workspace connection is not open\"));\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tsocket.send(JSON.stringify(message), { binary: false, compress: false }, (error) => {\n\t\t\t\t\t\tif (error) reject(error);\n\t\t\t\t\t\telse resolve();\n\t\t\t\t\t});\n\t\t\t\t}),\n\t\t\tclose: (reason: RemoteWorkspaceProtocolCloseReason): Promise<void> => {\n\t\t\t\tif (socket.readyState === WebSocket.CLOSED) return Promise.resolve();\n\t\t\t\treturn new Promise((resolve) => {\n\t\t\t\t\tsocket.once(\"close\", () => resolve());\n\t\t\t\t\tsocket.close(websocketCloseCode(reason), reason.message);\n\t\t\t\t});\n\t\t\t},\n\t\t};\n\t\tthis.client = new RemoteWorkspaceClientProtocol(transport, {\n\t\t\trequiredCapabilities: [\"primitive_operations\", \"tool_updates\"],\n\t\t\toptionalCapabilities: [\"catalog_refresh\", \"file_transfer\", \"artifacts\", \"lsp_status\"],\n\t\t\treceiveLimits: DEFAULT_REMOTE_WORKSPACE_PROTOCOL_LIMITS,\n\t\t\tlocalToolSchemas: this.localToolSchemas,\n\t\t\thandshakeTimeoutMs: options.handshakeTimeoutMs,\n\t\t\tonCatalogRefreshed: async () => {\n\t\t\t\tawait this.refreshRemoteLspStatus();\n\t\t\t\tawait Promise.all([...this.catalogListeners].map((listener) => listener()));\n\t\t\t},\n\t\t});\n\t\tsocket.on(\"message\", (data, isBinary) => {\n\t\t\tif (isBinary) {\n\t\t\t\tvoid this.client.disconnect(\"Remote workspace sent an unexpected binary frame\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tvoid this.client.receive(websocketPayload(data)).catch(() => undefined);\n\t\t});\n\t\tsocket.on(\"close\", () => void this.client.disconnect(\"Remote workspace connection closed\"));\n\t\tsocket.on(\"error\", () => void this.client.disconnect(\"Remote workspace connection failed\"));\n\t}\n\n\tstatic async connect(url: string, options: RemoteToolOperationsConnectOptions = {}): Promise<RemoteToolOperations> {\n\t\tconst normalized = normalizeRemoteUrl(url);\n\t\tconst token = options.token ?? process.env.GRIP_REMOTE_TOKEN;\n\t\tconst socket = await new Promise<WebSocket>((resolve, reject) => {\n\t\t\tconst websocket = new WebSocket(normalized.url, DAEMON_WEBSOCKET_PROTOCOL, {\n\t\t\t\tperMessageDeflate: false,\n\t\t\t\t...(token ? { headers: { authorization: `Bearer ${token}` } } : {}),\n\t\t\t});\n\t\t\tconst cleanup = () => {\n\t\t\t\twebsocket.off(\"open\", onOpen);\n\t\t\t\twebsocket.off(\"error\", onError);\n\t\t\t};\n\t\t\tconst onOpen = () => {\n\t\t\t\tcleanup();\n\t\t\t\tresolve(websocket);\n\t\t\t};\n\t\t\tconst onError = () => {\n\t\t\t\tcleanup();\n\t\t\t\twebsocket.terminate();\n\t\t\t\treject(new Error(`Failed to connect remote workspace: ${normalized.url}`));\n\t\t\t};\n\t\t\twebsocket.once(\"open\", onOpen);\n\t\t\twebsocket.once(\"error\", onError);\n\t\t});\n\t\tconst operations = new RemoteToolOperations(normalized.url, normalized.protocol, socket, options);\n\t\ttry {\n\t\t\tconst handshake = await operations.client.start();\n\t\t\toperations.cwd = handshake.workspace.root;\n\t\t\toperations.workspacePathFlavor = handshake.workspace.pathFlavor;\n\t\t\toperations.workspaceId = handshake.workspace.id;\n\t\t\tawait operations.refreshRemoteLspStatus();\n\t\t\treturn operations;\n\t\t} catch (error) {\n\t\t\tsocket.terminate();\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tresolveWorkspaceToolExecution(name: string, parameterSchema: unknown): RemoteWorkspaceToolExecutionTarget {\n\t\tconst schemaHash = hashRemoteWorkspaceJson(parameterSchema);\n\t\tconst catalogTool = this.client.handshake?.catalog.tools.find((tool) => tool.name === name);\n\t\tif (!catalogTool) return \"unavailable\";\n\t\tif (catalogTool.schemaHash !== schemaHash) {\n\t\t\tthrow new Error(`Remote tool schema does not match the local canonical definition: ${name}`);\n\t\t}\n\t\tthis.localToolSchemas.set(name, schemaHash);\n\t\tthis.client.setLocalToolSchemas(this.localToolSchemas);\n\t\treturn \"remote\";\n\t}\n\n\tasync executeWorkspaceTool(\n\t\tname: string,\n\t\tinvocation: WorkspaceToolRemoteInvocation,\n\t): Promise<AgentToolResult<unknown>> {\n\t\tconst invoke = async () => {\n\t\t\tawait this.client.waitForCatalogRefresh();\n\t\t\tconst handshake = this.client.handshake;\n\t\t\tif (!handshake) throw new Error(\"Remote workspace protocol handshake is unavailable\");\n\t\t\tconst catalogTool = handshake.catalog.tools.find((tool) => tool.name === name);\n\t\t\tconst schemaHash = this.localToolSchemas.get(name);\n\t\t\tif (!catalogTool || !schemaHash || catalogTool.schemaHash !== schemaHash) {\n\t\t\t\tthrow new Error(`Remote tool is unavailable or has schema drift: ${name}`);\n\t\t\t}\n\t\t\tconst result = requireRecord(\n\t\t\t\tawait this.client.request(\n\t\t\t\t\t\"tool.invoke\",\n\t\t\t\t\t{\n\t\t\t\t\t\tgeneration: handshake.catalog.generation,\n\t\t\t\t\t\tcatalogHash: handshake.catalogHash,\n\t\t\t\t\t\ttoolName: name,\n\t\t\t\t\t\tschemaHash,\n\t\t\t\t\t\targumentsPrepared: true,\n\t\t\t\t\t\targuments: invocation.arguments,\n\t\t\t\t\t\texecutionOptions: invocation.executionOptions,\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tsignal: invocation.signal,\n\t\t\t\t\t\tonUpdate: invocation.onUpdate\n\t\t\t\t\t\t\t? (update) =>\n\t\t\t\t\t\t\t\t\tinvocation.onUpdate!(parseRemoteWorkspaceToolResult(update) as AgentToolResult<unknown>)\n\t\t\t\t\t\t\t: undefined,\n\t\t\t\t\t},\n\t\t\t\t),\n\t\t\t\t\"tool result\",\n\t\t\t);\n\t\t\tconst toolResult = result;\n\t\t\tif (!Array.isArray(toolResult.content)) throw new Error(\"Invalid remote workspace tool result content\");\n\t\t\tif (name.startsWith(\"lsp_\")) await this.refreshRemoteLspStatus().catch(() => undefined);\n\t\t\treturn {\n\t\t\t\tcontent: toolResult.content as AgentToolResult<unknown>[\"content\"],\n\t\t\t\tdetails: toolResult.details,\n\t\t\t\t...(typeof toolResult.terminate === \"boolean\" ? { terminate: toolResult.terminate } : {}),\n\t\t\t};\n\t\t};\n\t\ttry {\n\t\t\treturn await invoke();\n\t\t} catch (error) {\n\t\t\tif (\n\t\t\t\terror instanceof RemoteWorkspaceRequestError &&\n\t\t\t\terror.code === \"stale_generation\" &&\n\t\t\t\terror.executionState === \"not_started\"\n\t\t\t) {\n\t\t\t\treturn invoke();\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tgetRemoteLspStatus(): RemoteLspStatus {\n\t\treturn structuredClone(this.remoteLspStatus);\n\t}\n\n\tonWorkspaceToolCatalogChanged(listener: () => void | Promise<void>): () => void {\n\t\tthis.catalogListeners.add(listener);\n\t\treturn () => this.catalogListeners.delete(listener);\n\t}\n\n\tprivate async refreshRemoteLspStatus(): Promise<void> {\n\t\tif (!this.client.handshake?.catalog.operations.includes(\"lsp.status\")) {\n\t\t\tthis.remoteLspStatus = { enabled: false, servers: [] };\n\t\t\treturn;\n\t\t}\n\t\tthis.remoteLspStatus = (await this.client.request(\"lsp.status\", {})) as RemoteLspStatus;\n\t}\n\n\tprivate request(method: RemoteWorkspaceMethod, params: unknown) {\n\t\treturn this.client.request(method, params);\n\t}\n\n\tasync exec(command: string, options: ToolExecOptions): Promise<ToolExecResult> {\n\t\tconst result = requireRecord(\n\t\t\tawait this.client.request(\n\t\t\t\t\"workspace.exec\",\n\t\t\t\t{ command, cwd: options.cwd ?? this.cwd },\n\t\t\t\t{\n\t\t\t\t\tsignal: options.signal,\n\t\t\t\t\ttimeoutMs: options.timeout === undefined ? undefined : Math.round(options.timeout * 1000),\n\t\t\t\t\tonUpdate: (update) => {\n\t\t\t\t\t\tconst record = requireRecord(update, \"exec update\");\n\t\t\t\t\t\toptions.onData(\n\t\t\t\t\t\t\tdecodeCanonicalBase64(requireString(record.dataBase64, \"exec update data\"), 1024 * 1024),\n\t\t\t\t\t\t);\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t),\n\t\t\t\"exec result\",\n\t\t);\n\t\treturn { exitCode: typeof result.exitCode === \"number\" ? result.exitCode : null };\n\t}\n\n\tasync access(path: string, mode?: ToolAccessMode): Promise<void> {\n\t\tawait this.request(\"workspace.access\", { path, ...(mode ? { mode } : {}) });\n\t}\n\n\tasync readFile(path: string): Promise<Buffer> {\n\t\tconst result = requireRecord(await this.request(\"workspace.read\", { path }), \"read result\");\n\t\treturn decodeCanonicalBase64(requireString(result.contentBase64, \"read content\"), Number.MAX_SAFE_INTEGER);\n\t}\n\n\tasync writeFile(path: string, content: string | Buffer): Promise<void> {\n\t\tawait this.request(\"workspace.write\", { path, contentBase64: Buffer.from(content).toString(\"base64\") });\n\t}\n\n\tasync readResource(path: string): Promise<Buffer> {\n\t\tif (!this.client.handshake?.catalog.operations.includes(\"resource.read\")) {\n\t\t\tthrow new Error(\"Remote workspace does not expose resource reads\");\n\t\t}\n\t\tconst result = requireRecord(await this.request(\"resource.read\", { path }), \"resource read result\");\n\t\treturn decodeCanonicalBase64(\n\t\t\trequireString(result.contentBase64, \"resource read content\"),\n\t\t\tNumber.MAX_SAFE_INTEGER,\n\t\t);\n\t}\n\n\tasync uploadFile(sourcePath: string, destinationPath: string): Promise<void> {\n\t\tconst content = await fsReadFile(sourcePath);\n\t\tconst sha256 = createHash(\"sha256\").update(content).digest(\"hex\");\n\t\tconst handle = this.client.beginRequest(\"transfer.upload\", {\n\t\t\tpath: destinationPath,\n\t\t\tlength: content.byteLength,\n\t\t\tsha256,\n\t\t\toverwrite: true,\n\t\t});\n\t\tconst chunkBytes = this.client.handshake?.limits.maxTransferChunkBytes ?? 64 * 1024;\n\t\tfor (let offset = 0; offset < content.byteLength; offset += chunkBytes) {\n\t\t\tawait handle.sendTransferChunk(content.subarray(offset, Math.min(offset + chunkBytes, content.byteLength)));\n\t\t}\n\t\tawait handle.finishTransfer(content.byteLength, sha256);\n\t\tawait handle.result;\n\t}\n\n\tasync downloadFile(sourcePath: string, destinationPath: string): Promise<void> {\n\t\tconst stream = createWriteStream(destinationPath);\n\t\ttry {\n\t\t\tawait this.client.request(\n\t\t\t\t\"transfer.download\",\n\t\t\t\t{ path: sourcePath },\n\t\t\t\t{\n\t\t\t\t\tonTransferChunk: (chunk) => writeStreamChunk(stream, chunk),\n\t\t\t\t},\n\t\t\t);\n\t\t\tawait endWriteStream(stream);\n\t\t} catch (error) {\n\t\t\tstream.destroy();\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tasync mkdir(path: string, options: ToolMkdirOptions = {}): Promise<void> {\n\t\tawait this.request(\"workspace.mkdir\", { path, recursive: options.recursive ?? false });\n\t}\n\n\tasync stat(path: string): Promise<ToolFileStat> {\n\t\tconst result = requireRecord(await this.request(\"workspace.stat\", { path }), \"stat result\");\n\t\treturn {\n\t\t\tisDirectory: () => result.kind === \"directory\",\n\t\t\tisFile: () => result.kind === \"file\",\n\t\t};\n\t}\n\n\tasync readdir(path: string): Promise<string[]> {\n\t\tconst result = requireRecord(await this.request(\"workspace.readdir\", { path }), \"readdir result\");\n\t\tif (!Array.isArray(result.entries) || !result.entries.every((entry) => typeof entry === \"string\")) {\n\t\t\tthrow new Error(\"Invalid remote workspace readdir entries\");\n\t\t}\n\t\treturn result.entries;\n\t}\n\n\tasync glob(pattern: string, cwd: string, options: ToolGlobOptions): Promise<string[]> {\n\t\tconst result = requireRecord(\n\t\t\tawait this.request(\"workspace.glob\", { pattern, cwd, ignore: options.ignore, limit: options.limit }),\n\t\t\t\"glob result\",\n\t\t);\n\t\tif (!Array.isArray(result.matches) || !result.matches.every((entry) => typeof entry === \"string\")) {\n\t\t\tthrow new Error(\"Invalid remote workspace glob matches\");\n\t\t}\n\t\treturn result.matches;\n\t}\n\n\tasync grep(options: ToolGrepOptions): Promise<ToolGrepResult> {\n\t\tconst result = requireRecord(await this.request(\"workspace.grep\", options), \"grep result\");\n\t\tif (!Array.isArray(result.matches)) throw new Error(\"Invalid remote workspace grep matches\");\n\t\treturn {\n\t\t\tisDirectory: result.isDirectory === true,\n\t\t\tmatches: result.matches.map((entry) => {\n\t\t\t\tconst match = requireRecord(entry, \"grep match\");\n\t\t\t\treturn {\n\t\t\t\t\tfilePath: requireString(match.filePath, \"grep file path\"),\n\t\t\t\t\tlineNumber: typeof match.lineNumber === \"number\" ? match.lineNumber : 0,\n\t\t\t\t\t...(typeof match.lineText === \"string\" ? { lineText: match.lineText } : {}),\n\t\t\t\t};\n\t\t\t}),\n\t\t};\n\t}\n\n\tasync detectImageMimeType(path: string): Promise<string | null | undefined> {\n\t\tconst result = requireRecord(await this.request(\"workspace.detect_image_mime\", { path }), \"image MIME result\");\n\t\treturn typeof result.mimeType === \"string\" ? result.mimeType : null;\n\t}\n\n\tgetBackendInfo(): ToolBackendInfo {\n\t\treturn {\n\t\t\ttype: \"remote\",\n\t\t\tcwd: this.cwd,\n\t\t\turl: this.url,\n\t\t\tprotocol: this.protocol,\n\t\t\tconfigured: true,\n\t\t\tworkspace: { id: this.workspaceId, root: this.cwd, pathFlavor: this.workspacePathFlavor },\n\t\t};\n\t}\n\n\tdispose(): Promise<void> {\n\t\tthis.disposePromise ??= this.client.close().catch(() => {\n\t\t\tthis.socket.terminate();\n\t\t});\n\t\treturn this.disposePromise;\n\t}\n}\n\nexport function createRemoteToolOperations(\n\turl: string,\n\toptions?: RemoteToolOperationsConnectOptions,\n): Promise<RemoteToolOperations> {\n\treturn RemoteToolOperations.connect(url, options);\n}\n"]}
1
+ {"version":3,"file":"operations.d.ts","sourceRoot":"","sources":["../../../src/core/tools/operations.ts"],"names":[],"mappings":"AAEA,OAAO,EAAkD,KAAK,KAAK,EAAoB,MAAM,SAAS,CAAC;AAWvG,OAAO,KAAK,EAAE,eAAe,EAAE,uBAAuB,EAAE,MAAM,4BAA4B,CAAC;AAa3F,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAClE,OAAO,EAKN,KAAK,eAAe,EAGpB,MAAM,0CAA0C,CAAC;AAOlD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAElE,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,GAAG,WAAW,CAAC;AACvE,MAAM,MAAM,4BAA4B,GAAG,OAAO,GAAG,QAAQ,GAAG,aAAa,CAAC;AAC9E,MAAM,MAAM,kCAAkC,GAAG,OAAO,CAAC,4BAA4B,EAAE,OAAO,CAAC,CAAC;AAChG,KAAK,2BAA2B,GAAG,IAAI,CAAC;AAExC,MAAM,WAAW,YAAY;IAC5B,WAAW,EAAE,MAAM,OAAO,CAAC;IAC3B,MAAM,EAAE,MAAM,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,eAAe;IAC/B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;CACxB;AAED,MAAM,WAAW,cAAc;IAC9B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB;AAED,MAAM,WAAW,gBAAgB;IAChC,SAAS,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,eAAe;IAC/B,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,eAAe;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,aAAa;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,cAAc;IAC9B,WAAW,EAAE,OAAO,CAAC;IACrB,OAAO,EAAE,aAAa,EAAE,CAAC;CACzB;AAED,MAAM,MAAM,eAAe,GACxB;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAC9B;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,KAAK,CAAA;CAAE,GAClD;IACA,IAAI,EAAE,QAAQ,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,IAAI,CAAC;IACf,UAAU,EAAE,IAAI,CAAC;IACjB,SAAS,EAAE,iBAAiB,CAAC;CAC5B,CAAC;AAGL,MAAM,WAAW,6BAA6B;IAC7C,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,QAAQ,CAAC,EAAE,uBAAuB,CAAC,OAAO,CAAC,CAAC;IAC5C,gBAAgB,EAAE,6BAA6B,CAAC;CAChD;AAED,MAAM,WAAW,sBAAsB;IACtC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,eAAO,MAAM,8BAA8B,0GAMhC,CAAC;AAEZ,MAAM,WAAW,6BAA6B;IAC7C,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,kBAAkB,CAAC,EAAE,sBAAsB,CAAC;CAC5C;AAOD,MAAM,WAAW,0CAA2C,SAAQ,kCAAkC;IACrG,WAAW,CAAC,EAAE,MAAM,CAAC;CACrB;AAID,MAAM,MAAM,sBAAsB,GAAG,IAAI,CAAC,cAAc,EAAE,SAAS,CAAC,CAAC;AAErE,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,cAAc,GAAG,sBAAsB,CAkBvF;AAED,MAAM,WAAW,cAAc;IAC9B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;IACzE,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3D,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACxC,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACjE,YAAY,CAAC,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC7C,UAAU,CAAC,CAAC,UAAU,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACxE,YAAY,CAAC,CAAC,UAAU,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1E,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/D,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;IAC1C,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IACzC,IAAI,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IACjF,IAAI,CAAC,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;IACzD,mBAAmB,CAAC,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;IACvE,cAAc,CAAC,IAAI,eAAe,CAAC;IACnC,6BAA6B,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,eAAe,EAAE,OAAO,GAAG,4BAA4B,CAAC;IACrG,oBAAoB,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,6BAA6B,GAAG,OAAO,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC,CAAC;IAClH,6BAA6B,CAAC,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,MAAM,IAAI,CAAC;IACjF,kBAAkB,CAAC,IAAI,eAAe,CAAC;IACvC,OAAO,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1B;AAED,MAAM,WAAW,0BAA0B;IAC1C,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB;AAqGD,qBAAa,mBAAoB,YAAW,cAAc;IACzD,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,CAAC,SAAS,CAAqB;IAEtC,YAAY,GAAG,EAAE,MAAM,EAAE,OAAO,GAAE,0BAA+B,EAGhE;IAED,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAEhD;IAEK,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,cAAc,CAAC,CAmD7E;IAEK,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAE/D;IAEK,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAE5C;IAEK,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAErE;IAEK,UAAU,CAAC,UAAU,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE3E;IAEK,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE7E;IAEK,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,gBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC,CAEvE;IAEK,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,CAEvC;IAEK,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAE7C;IAEK,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,CAE1E;IAED,cAAc,IAAI,eAAe,CAEhC;CACD;AAED,qBAAa,4BAA6B,YAAW,cAAc;IAClE,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,UAAU,CAAmC;IACrD,OAAO,CAAC,wBAAwB,CAA2B;IAC3D,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAyC;IAC1E,OAAO,CAAC,cAAc,CAA4B;IAClD,OAAO,CAAC,QAAQ,CAAC,YAAY,CAA0C;IAEvE,YAAY,GAAG,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,EAIhE;IAEK,eAAe,CACpB,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,0CAA+C,GACtD,OAAO,CAAC,eAAe,CAAC,CAe1B;IAEK,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAM3B;IAED,OAAO,CAAC,iBAAiB;IASnB,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,cAAc,CAAC,CAE7E;IAEK,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAE/D;IAEK,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAE5C;IAEK,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAErE;IAEK,UAAU,CAAC,UAAU,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAI3E;IAEK,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAI7E;IAEK,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAEnE;IAEK,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAE9C;IAEK,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAE7C;IAEK,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAEpF;IAEK,IAAI,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,cAAc,CAAC,CAE5D;IAEK,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,CAE1E;IAED,cAAc,IAAI,eAAe,CAEhC;IACD,cAAc,IAAI,OAAO,CAExB;IAED,6BAA6B,CAAC,IAAI,EAAE,MAAM,EAAE,eAAe,EAAE,OAAO,GAAG,kCAAkC,CAExG;IAED,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,6BAA6B,GAAG,OAAO,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC,CAE/G;IAED,kBAAkB,IAAI,eAAe,CAEpC;IAED,6BAA6B,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,MAAM,IAAI,CAG9E;IAED,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CASvB;YAEa,iBAAiB;CAe/B;AA+CD,MAAM,WAAW,kCAAkC;IAClD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACzC;AAED,qBAAa,oBAAqB,YAAW,cAAc;IAC1D,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,QAAQ,EAAE,2BAA2B,CAAC;IAC/C,GAAG,EAAE,MAAM,CAAC;IACZ,mBAAmB,EAAE,kBAAkB,CAAW;IAClD,OAAO,CAAC,WAAW,CAAM;IACzB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAyC;IAChE,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAU;IACnC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAgC;IACvD,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAA6B;IAC9D,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAyC;IAC1E,OAAO,CAAC,eAAe,CAAoD;IAC3E,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,cAAc,CAA4B;IAElD,OAAO,eAgEN;IAED,OAAa,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,GAAE,kCAAuC,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAoCjH;IAED,OAAa,kBAAkB,CAC9B,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,kCAAuC,GAC9C,OAAO,CAAC,oBAAoB,CAAC,CAiB/B;IAED,6BAA6B,CAAC,IAAI,EAAE,MAAM,EAAE,eAAe,EAAE,OAAO,GAAG,kCAAkC,CAUxG;IAEK,oBAAoB,CACzB,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,6BAA6B,GACvC,OAAO,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC,CAuDnC;IAED,kBAAkB,IAAI,eAAe,CAEpC;IAED,6BAA6B,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,MAAM,IAAI,CAG9E;YAEa,sBAAsB;IAQpC,OAAO,CAAC,OAAO;YAID,eAAe;YAaf,sBAAsB;IA4B9B,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,cAAc,CAAC,CAmB7E;IAEK,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAE/D;IAEK,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAG5C;IAEK,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAOrE;IAEK,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAShD;IAEK,UAAU,CAAC,UAAU,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAgB3E;IAEK,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAgB7E;IAEK,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,gBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC,CAEvE;IAEK,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAO9C;IAEK,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAM7C;IAEK,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CASpF;IAEK,IAAI,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,cAAc,CAAC,CAc5D;IAEK,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,CAG1E;IAED,cAAc,IAAI,eAAe,CAShC;IACD,cAAc,IAAI,OAAO,CAExB;IAED,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAKvB;CACD;AAED,wBAAgB,0BAA0B,CACzC,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE,kCAAkC,GAC1C,OAAO,CAAC,oBAAoB,CAAC,CAW/B","sourcesContent":["import { type ChildProcess, spawn } from \"node:child_process\";\nimport { createHash } from \"node:crypto\";\nimport { constants, createReadStream, createWriteStream, type Stats, type WriteStream } from \"node:fs\";\nimport {\n\taccess as fsAccess,\n\tmkdir as fsMkdir,\n\treaddir as fsReaddir,\n\treadFile as fsReadFile,\n\tstat as fsStat,\n\twriteFile as fsWriteFile,\n} from \"node:fs/promises\";\nimport { posix, win32 } from \"node:path\";\nimport { pipeline } from \"node:stream/promises\";\nimport type { AgentToolResult, AgentToolUpdateCallback } from \"@greppleai/grip-agent-core\";\nimport WebSocket, { type RawData } from \"ws\";\nimport { DAEMON_WEBSOCKET_PROTOCOL } from \"../../daemon/config.ts\";\nimport { waitForChildProcess } from \"../../utils/child-process.ts\";\nimport { detectSupportedImageMimeTypeFromFile } from \"../../utils/mime.ts\";\nimport {\n\tgetShellConfig,\n\tgetShellEnv,\n\tkillProcessTree,\n\ttrackDetachedChildPid,\n\tuntrackDetachedChildPid,\n} from \"../../utils/shell.ts\";\n\nimport type { PortablePathFlavor } from \"../lsp/portable-path.ts\";\nimport {\n\tDEFAULT_REMOTE_WORKSPACE_PROTOCOL_LIMITS,\n\tdecodeCanonicalBase64,\n\thashRemoteWorkspaceJson,\n\tparseRemoteWorkspaceToolResult,\n\ttype RemoteLspStatus,\n\ttype RemoteWorkspaceClientMessage,\n\ttype RemoteWorkspaceMethod,\n} from \"../remote-workspace-protocol/contract.ts\";\nimport { GuestSharedClientTransport } from \"../remote-workspace-protocol/guest-shared-client.ts\";\nimport {\n\tRemoteWorkspaceClientProtocol,\n\ttype RemoteWorkspaceProtocolCloseReason,\n\tRemoteWorkspaceRequestError,\n} from \"../remote-workspace-protocol/session.ts\";\nimport type { WorkspaceIdentity } from \"../workspace-identity.ts\";\n\nexport type ToolAccessMode = \"exists\" | \"read\" | \"write\" | \"readwrite\";\nexport type WorkspaceToolExecutionTarget = \"local\" | \"remote\" | \"unavailable\";\nexport type RemoteWorkspaceToolExecutionTarget = Exclude<WorkspaceToolExecutionTarget, \"local\">;\ntype RemoteToolTransportProtocol = \"ws\";\n\nexport interface ToolFileStat {\n\tisDirectory: () => boolean;\n\tisFile: () => boolean;\n}\n\nexport interface ToolExecOptions {\n\tcwd?: string;\n\tonData: (data: Buffer) => void;\n\tsignal?: AbortSignal;\n\ttimeout?: number;\n\tenv?: NodeJS.ProcessEnv;\n}\n\nexport interface ToolExecResult {\n\texitCode: number | null;\n}\n\nexport interface ToolMkdirOptions {\n\trecursive?: boolean;\n}\n\nexport interface ToolGlobOptions {\n\tignore: string[];\n\tlimit: number;\n}\n\nexport interface ToolGrepOptions {\n\tpattern: string;\n\tpath: string;\n\tglob?: string;\n\tignoreCase?: boolean;\n\tliteral?: boolean;\n\tlimit: number;\n}\n\nexport interface ToolGrepMatch {\n\tfilePath: string;\n\tlineNumber: number;\n\tlineText?: string;\n}\n\nexport interface ToolGrepResult {\n\tisDirectory: boolean;\n\tmatches: ToolGrepMatch[];\n}\n\nexport type ToolBackendInfo =\n\t| { type: \"local\"; cwd: string }\n\t| { type: \"remote\"; cwd: string; configured: false }\n\t| {\n\t\t\ttype: \"remote\";\n\t\t\tcwd: string;\n\t\t\turl: string;\n\t\t\tprotocol: \"ws\";\n\t\t\tconfigured: true;\n\t\t\tworkspace: WorkspaceIdentity;\n\t };\n\n// grip-ignore noNearIdenticalDataStructures: Remote backend invocations require host-normalized execution options, while public host invocations accept omitted options and apply defaults.\nexport interface WorkspaceToolRemoteInvocation {\n\ttoolCallId: string;\n\targuments: unknown;\n\tsignal?: AbortSignal;\n\tonUpdate?: AgentToolUpdateCallback<unknown>;\n\texecutionOptions: WorkspaceToolExecutionOptions;\n}\n\nexport interface GripSessionEnvironment {\n\tGRIP_SESSION_ID?: string;\n\tGRIP_SESSION_FILE?: string;\n\tGRIP_PROVIDER?: string;\n\tGRIP_MODEL?: string;\n\tGRIP_REASONING_LEVEL?: string;\n}\n\nexport const GRIP_SESSION_ENVIRONMENT_NAMES = Object.freeze([\n\t\"GRIP_SESSION_ID\",\n\t\"GRIP_SESSION_FILE\",\n\t\"GRIP_PROVIDER\",\n\t\"GRIP_MODEL\",\n\t\"GRIP_REASONING_LEVEL\",\n] as const);\n\nexport interface WorkspaceToolExecutionOptions {\n\timageAutoResize?: boolean;\n\tshellCommandPrefix?: string;\n\tsessionEnvironment?: GripSessionEnvironment;\n}\n\ninterface NormalizedRemoteWorkspaceUrl {\n\turl: string;\n\tprotocol: \"ws\";\n}\n\nexport interface DeferredRemoteToolOperationsConnectOptions extends RemoteToolOperationsConnectOptions {\n\texpectedCwd?: string;\n}\n\ntype SharedExpectedRevision = { sha256: string } | { absent: true };\n\nexport type BorrowedToolOperations = Omit<ToolOperations, \"dispose\">;\n\nexport function borrowToolOperations(operations: ToolOperations): BorrowedToolOperations {\n\tconst boundMethods = new Map<PropertyKey, unknown>();\n\treturn new Proxy(operations, {\n\t\tget(target, property, receiver) {\n\t\t\tif (property === \"dispose\") return undefined;\n\t\t\tconst value = Reflect.get(target, property, receiver) as unknown;\n\t\t\tif (typeof value !== \"function\") return value;\n\t\t\tlet bound = boundMethods.get(property);\n\t\t\tif (!bound) {\n\t\t\t\tbound = value.bind(target);\n\t\t\t\tboundMethods.set(property, bound);\n\t\t\t}\n\t\t\treturn bound;\n\t\t},\n\t\thas(target, property) {\n\t\t\treturn property === \"dispose\" ? false : Reflect.has(target, property);\n\t\t},\n\t}) as BorrowedToolOperations;\n}\n\nexport interface ToolOperations {\n\tcwd: string;\n\texec(command: string, options: ToolExecOptions): Promise<ToolExecResult>;\n\taccess(path: string, mode?: ToolAccessMode): Promise<void>;\n\treadFile(path: string): Promise<Buffer>;\n\twriteFile(path: string, content: string | Buffer): Promise<void>;\n\treadResource?(path: string): Promise<Buffer>;\n\tuploadFile?(sourcePath: string, destinationPath: string): Promise<void>;\n\tdownloadFile?(sourcePath: string, destinationPath: string): Promise<void>;\n\tmkdir(path: string, options?: ToolMkdirOptions): Promise<void>;\n\tstat(path: string): Promise<ToolFileStat>;\n\treaddir(path: string): Promise<string[]>;\n\tglob?(pattern: string, cwd: string, options: ToolGlobOptions): Promise<string[]>;\n\tgrep?(options: ToolGrepOptions): Promise<ToolGrepResult>;\n\tdetectImageMimeType?(path: string): Promise<string | null | undefined>;\n\tgetBackendInfo?(): ToolBackendInfo;\n\tresolveWorkspaceToolExecution?(name: string, parameterSchema: unknown): WorkspaceToolExecutionTarget;\n\texecuteWorkspaceTool?(name: string, invocation: WorkspaceToolRemoteInvocation): Promise<AgentToolResult<unknown>>;\n\tonWorkspaceToolCatalogChanged?(listener: () => void | Promise<void>): () => void;\n\tgetRemoteLspStatus?(): RemoteLspStatus;\n\tdispose?(): Promise<void>;\n}\n\nexport interface LocalToolOperationsOptions {\n\tshellPath?: string;\n}\n\nfunction workspaceRootsEqual(left: string, right: string, pathFlavor: PortablePathFlavor): boolean {\n\tif (pathFlavor === \"windows\") return win32.normalize(left).toLowerCase() === win32.normalize(right).toLowerCase();\n\treturn posix.normalize(left) === posix.normalize(right);\n}\n\nfunction accessModeToFsMode(mode: ToolAccessMode | undefined): number {\n\tswitch (mode) {\n\t\tcase \"read\":\n\t\t\treturn constants.R_OK;\n\t\tcase \"write\":\n\t\t\treturn constants.W_OK;\n\t\tcase \"readwrite\":\n\t\t\treturn constants.R_OK | constants.W_OK;\n\t\tcase \"exists\":\n\t\tcase undefined:\n\t\t\treturn constants.F_OK;\n\t}\n}\n\nconst MAX_TIMEOUT_MS = 2_147_483_647;\nconst MAX_TIMEOUT_SECONDS = MAX_TIMEOUT_MS / 1000;\n\nfunction resolveTimeoutMs(timeout: number | undefined): number | undefined {\n\tif (timeout === undefined) return undefined;\n\tif (!Number.isFinite(timeout) || timeout <= 0) {\n\t\tthrow new Error(\"Invalid timeout: must be a finite number of seconds\");\n\t}\n\n\tconst timeoutMs = timeout * 1000;\n\tif (timeoutMs > MAX_TIMEOUT_MS) {\n\t\tthrow new Error(`Invalid timeout: maximum is ${MAX_TIMEOUT_SECONDS} seconds`);\n\t}\n\treturn timeoutMs;\n}\n\nfunction registerChildProcessAbort(signal: AbortSignal | undefined, child: ChildProcess): () => void {\n\tconst onAbort = (): void => {\n\t\tif (child.pid) killProcessTree(child.pid);\n\t};\n\tif (!signal) return () => undefined;\n\tif (signal.aborted) onAbort();\n\telse signal.addEventListener(\"abort\", onAbort, { once: true });\n\treturn () => signal.removeEventListener(\"abort\", onAbort);\n}\n\nfunction terminateLingeringProcessGroup(child: ChildProcess): void {\n\tif (!child.pid || process.platform === \"win32\") return;\n\ttry {\n\t\tprocess.kill(-child.pid, 0);\n\t\tkillProcessTree(child.pid);\n\t} catch {}\n}\n\nfunction cleanupChildExecution(\n\tchild: ChildProcess,\n\ttimeoutHandle: NodeJS.Timeout | undefined,\n\tremoveAbortListener: () => void,\n): void {\n\tif (child.pid) untrackDetachedChildPid(child.pid);\n\tif (timeoutHandle) clearTimeout(timeoutHandle);\n\tremoveAbortListener();\n}\n\nasync function copyFileStream(sourcePath: string, destinationPath: string): Promise<void> {\n\tawait pipeline(createReadStream(sourcePath), createWriteStream(destinationPath));\n}\n\nfunction writeStreamChunk(stream: WriteStream, chunk: Buffer): Promise<void> {\n\treturn new Promise((resolve, reject) => {\n\t\tconst onError = (error: Error) => {\n\t\t\tstream.off(\"error\", onError);\n\t\t\treject(error);\n\t\t};\n\t\tstream.once(\"error\", onError);\n\t\tstream.write(chunk, (error) => {\n\t\t\tstream.off(\"error\", onError);\n\t\t\tif (error) {\n\t\t\t\treject(error);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tresolve();\n\t\t});\n\t});\n}\n\nfunction endWriteStream(stream: WriteStream): Promise<void> {\n\treturn new Promise((resolve, reject) => {\n\t\tconst onError = (error: Error) => {\n\t\t\tstream.off(\"error\", onError);\n\t\t\treject(error);\n\t\t};\n\t\tstream.once(\"error\", onError);\n\t\tstream.end(() => {\n\t\t\tstream.off(\"error\", onError);\n\t\t\tresolve();\n\t\t});\n\t});\n}\n\nexport class LocalToolOperations implements ToolOperations {\n\tcwd: string;\n\tprivate shellPath: string | undefined;\n\n\tconstructor(cwd: string, options: LocalToolOperationsOptions = {}) {\n\t\tthis.cwd = cwd;\n\t\tthis.shellPath = options.shellPath;\n\t}\n\n\tsetShellPath(shellPath: string | undefined): void {\n\t\tthis.shellPath = shellPath;\n\t}\n\n\tasync exec(command: string, options: ToolExecOptions): Promise<ToolExecResult> {\n\t\tconst timeoutMs = resolveTimeoutMs(options.timeout);\n\t\tconst cwd = options.cwd ?? this.cwd;\n\t\tconst { shell, args } = getShellConfig(this.shellPath);\n\t\ttry {\n\t\t\tawait fsAccess(cwd, constants.F_OK);\n\t\t} catch {\n\t\t\tthrow new Error(`Working directory does not exist: ${cwd}\\nCannot execute bash commands.`);\n\t\t}\n\t\tif (options.signal?.aborted) {\n\t\t\tthrow new Error(\"aborted\");\n\t\t}\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tconst child = spawn(shell, [...args, command], {\n\t\t\t\tcwd,\n\t\t\t\tdetached: process.platform !== \"win32\",\n\t\t\t\tenv: options.env ?? getShellEnv(),\n\t\t\t\tstdio: [\"ignore\", \"pipe\", \"pipe\"],\n\t\t\t\twindowsHide: true,\n\t\t\t});\n\t\t\tif (child.pid) trackDetachedChildPid(child.pid);\n\t\t\tlet timedOut = false;\n\t\t\tlet timeoutHandle: NodeJS.Timeout | undefined;\n\t\t\tif (timeoutMs !== undefined) {\n\t\t\t\ttimeoutHandle = setTimeout(() => {\n\t\t\t\t\ttimedOut = true;\n\t\t\t\t\tif (child.pid) killProcessTree(child.pid);\n\t\t\t\t}, timeoutMs);\n\t\t\t}\n\t\t\tchild.stdout?.on(\"data\", options.onData);\n\t\t\tchild.stderr?.on(\"data\", options.onData);\n\t\t\tconst removeAbortListener = registerChildProcessAbort(options.signal, child);\n\t\t\twaitForChildProcess(child)\n\t\t\t\t.then((code) => {\n\t\t\t\t\tterminateLingeringProcessGroup(child);\n\t\t\t\t\tcleanupChildExecution(child, timeoutHandle, removeAbortListener);\n\t\t\t\t\tif (options.signal?.aborted) {\n\t\t\t\t\t\treject(new Error(\"aborted\"));\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tif (timedOut) {\n\t\t\t\t\t\treject(new Error(`timeout:${options.timeout}`));\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tresolve({ exitCode: code });\n\t\t\t\t})\n\t\t\t\t.catch((error: unknown) => {\n\t\t\t\t\tcleanupChildExecution(child, timeoutHandle, removeAbortListener);\n\t\t\t\t\treject(error);\n\t\t\t\t});\n\t\t});\n\t}\n\n\tasync access(path: string, mode?: ToolAccessMode): Promise<void> {\n\t\tawait fsAccess(path, accessModeToFsMode(mode));\n\t}\n\n\tasync readFile(path: string): Promise<Buffer> {\n\t\treturn fsReadFile(path);\n\t}\n\n\tasync writeFile(path: string, content: string | Buffer): Promise<void> {\n\t\tawait fsWriteFile(path, content, typeof content === \"string\" ? \"utf-8\" : undefined);\n\t}\n\n\tasync uploadFile(sourcePath: string, destinationPath: string): Promise<void> {\n\t\tawait copyFileStream(sourcePath, destinationPath);\n\t}\n\n\tasync downloadFile(sourcePath: string, destinationPath: string): Promise<void> {\n\t\tawait copyFileStream(sourcePath, destinationPath);\n\t}\n\n\tasync mkdir(path: string, options: ToolMkdirOptions = {}): Promise<void> {\n\t\tawait fsMkdir(path, { recursive: options.recursive ?? false });\n\t}\n\n\tasync stat(path: string): Promise<Stats> {\n\t\treturn fsStat(path);\n\t}\n\n\tasync readdir(path: string): Promise<string[]> {\n\t\treturn fsReaddir(path);\n\t}\n\n\tasync detectImageMimeType(path: string): Promise<string | null | undefined> {\n\t\treturn detectSupportedImageMimeTypeFromFile(path);\n\t}\n\n\tgetBackendInfo(): ToolBackendInfo {\n\t\treturn { type: \"local\", cwd: this.cwd };\n\t}\n}\n\nexport class DeferredRemoteToolOperations implements ToolOperations {\n\tcwd: string;\n\tprivate readonly expectedCwd: string;\n\tprivate operations: RemoteToolOperations | undefined;\n\tprivate remoteCatalogUnsubscribe: (() => void) | undefined;\n\tprivate readonly catalogListeners = new Set<() => void | Promise<void>>();\n\tprivate disposePromise: Promise<void> | undefined;\n\tprivate readonly onDisconnect: ((message: string) => void) | undefined;\n\n\tconstructor(cwd: string, onDisconnect?: (message: string) => void) {\n\t\tthis.cwd = cwd;\n\t\tthis.expectedCwd = cwd;\n\t\tthis.onDisconnect = onDisconnect;\n\t}\n\n\tasync configureRemote(\n\t\turl: string,\n\t\toptions: DeferredRemoteToolOperationsConnectOptions = {},\n\t): Promise<ToolBackendInfo> {\n\t\tconst next = await createRemoteToolOperations(url, { ...options, onDisconnect: this.onDisconnect });\n\t\ttry {\n\t\t\tconst stat = await next.stat(next.cwd);\n\t\t\tif (!stat.isDirectory()) throw new Error(`Remote daemon cwd is not a directory: ${next.cwd}`);\n\t\t\tconst expectedCwd = options.expectedCwd ?? this.expectedCwd;\n\t\t\tif (!workspaceRootsEqual(next.cwd, expectedCwd, next.workspacePathFlavor)) {\n\t\t\t\tthrow new Error(`Remote daemon workspace root mismatch: expected ${expectedCwd}, received ${next.cwd}`);\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tawait next.dispose();\n\t\t\tthrow error;\n\t\t}\n\t\tawait this.replaceOperations(next);\n\t\treturn this.getBackendInfo();\n\t}\n\n\tasync clear(): Promise<void> {\n\t\tconst previous = this.operations;\n\t\tthis.remoteCatalogUnsubscribe?.();\n\t\tthis.remoteCatalogUnsubscribe = undefined;\n\t\tthis.operations = undefined;\n\t\tawait previous?.dispose?.();\n\t}\n\n\tprivate requireOperations(): RemoteToolOperations {\n\t\tif (!this.operations) {\n\t\t\tthrow new Error(\n\t\t\t\t\"Remote backend is not configured. Configure it over RPC or with /sandbox before using tools.\",\n\t\t\t);\n\t\t}\n\t\treturn this.operations;\n\t}\n\n\tasync exec(command: string, options: ToolExecOptions): Promise<ToolExecResult> {\n\t\treturn this.requireOperations().exec(command, options);\n\t}\n\n\tasync access(path: string, mode?: ToolAccessMode): Promise<void> {\n\t\tawait this.requireOperations().access(path, mode);\n\t}\n\n\tasync readFile(path: string): Promise<Buffer> {\n\t\treturn this.requireOperations().readFile(path);\n\t}\n\n\tasync writeFile(path: string, content: string | Buffer): Promise<void> {\n\t\tawait this.requireOperations().writeFile(path, content);\n\t}\n\n\tasync uploadFile(sourcePath: string, destinationPath: string): Promise<void> {\n\t\tconst operations = this.requireOperations();\n\t\tif (!operations.uploadFile) throw new Error(\"Remote backend does not support file upload\");\n\t\tawait operations.uploadFile(sourcePath, destinationPath);\n\t}\n\n\tasync downloadFile(sourcePath: string, destinationPath: string): Promise<void> {\n\t\tconst operations = this.requireOperations();\n\t\tif (!operations.downloadFile) throw new Error(\"Remote backend does not support file download\");\n\t\tawait operations.downloadFile(sourcePath, destinationPath);\n\t}\n\n\tasync mkdir(path: string, options?: ToolMkdirOptions): Promise<void> {\n\t\tawait this.requireOperations().mkdir(path, options);\n\t}\n\n\tasync stat(path: string): Promise<ToolFileStat> {\n\t\treturn this.requireOperations().stat(path);\n\t}\n\n\tasync readdir(path: string): Promise<string[]> {\n\t\treturn this.requireOperations().readdir(path);\n\t}\n\n\tasync glob(pattern: string, cwd: string, options: ToolGlobOptions): Promise<string[]> {\n\t\treturn this.requireOperations().glob(pattern, cwd, options);\n\t}\n\n\tasync grep(options: ToolGrepOptions): Promise<ToolGrepResult> {\n\t\treturn this.requireOperations().grep(options);\n\t}\n\n\tasync detectImageMimeType(path: string): Promise<string | null | undefined> {\n\t\treturn this.requireOperations().detectImageMimeType(path);\n\t}\n\n\tgetBackendInfo(): ToolBackendInfo {\n\t\treturn this.operations?.getBackendInfo() ?? { type: \"remote\", cwd: this.cwd, configured: false };\n\t}\n\tisDisconnected(): boolean {\n\t\treturn this.operations?.isDisconnected() ?? false;\n\t}\n\n\tresolveWorkspaceToolExecution(name: string, parameterSchema: unknown): RemoteWorkspaceToolExecutionTarget {\n\t\treturn this.operations ? this.operations.resolveWorkspaceToolExecution(name, parameterSchema) : \"unavailable\";\n\t}\n\n\texecuteWorkspaceTool(name: string, invocation: WorkspaceToolRemoteInvocation): Promise<AgentToolResult<unknown>> {\n\t\treturn this.requireOperations().executeWorkspaceTool(name, invocation);\n\t}\n\n\tgetRemoteLspStatus(): RemoteLspStatus {\n\t\treturn this.operations?.getRemoteLspStatus() ?? { enabled: false, servers: [] };\n\t}\n\n\tonWorkspaceToolCatalogChanged(listener: () => void | Promise<void>): () => void {\n\t\tthis.catalogListeners.add(listener);\n\t\treturn () => this.catalogListeners.delete(listener);\n\t}\n\n\tdispose(): Promise<void> {\n\t\tthis.disposePromise ??= (async () => {\n\t\t\tthis.remoteCatalogUnsubscribe?.();\n\t\t\tthis.remoteCatalogUnsubscribe = undefined;\n\t\t\tconst previous = this.operations;\n\t\t\tthis.operations = undefined;\n\t\t\tawait previous?.dispose?.();\n\t\t})();\n\t\treturn this.disposePromise;\n\t}\n\n\tprivate async replaceOperations(next: RemoteToolOperations): Promise<void> {\n\t\tif (this.disposePromise) {\n\t\t\tawait next.dispose?.();\n\t\t\tthrow new Error(\"Deferred remote backend is disposed\");\n\t\t}\n\t\tconst previous = this.operations;\n\t\tthis.remoteCatalogUnsubscribe?.();\n\t\tthis.remoteCatalogUnsubscribe = undefined;\n\t\tthis.operations = next;\n\t\tthis.cwd = next.cwd;\n\t\tthis.remoteCatalogUnsubscribe = next.onWorkspaceToolCatalogChanged(() =>\n\t\t\tPromise.all([...this.catalogListeners].map((listener) => listener())).then(() => undefined),\n\t\t);\n\t\tawait previous?.dispose?.();\n\t}\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null;\n}\n\nfunction requireRecord(value: unknown, label: string): Record<string, unknown> {\n\tif (!isRecord(value)) throw new Error(`Invalid remote workspace ${label}`);\n\treturn value;\n}\n\nfunction requireString(value: unknown, label: string): string {\n\tif (typeof value !== \"string\") throw new Error(`Invalid remote workspace ${label}`);\n\treturn value;\n}\n\nfunction normalizeRemoteUrl(url: string): NormalizedRemoteWorkspaceUrl {\n\tconst parsed = new URL(url);\n\tif (parsed.protocol !== \"ws:\" && parsed.protocol !== \"wss:\") {\n\t\tthrow new Error(`--remote supports ws:// and wss:// URLs, got ${parsed.protocol}`);\n\t}\n\tif (parsed.username || parsed.password) throw new Error(\"Remote workspace URLs must not contain credentials\");\n\tif (parsed.hash) throw new Error(\"Remote workspace URLs must not contain fragments\");\n\treturn { url: parsed.toString(), protocol: \"ws\" };\n}\n\nfunction websocketPayload(data: RawData): Uint8Array {\n\tif (Buffer.isBuffer(data)) return Uint8Array.from(data);\n\tif (Array.isArray(data)) return Uint8Array.from(Buffer.concat(data));\n\treturn Uint8Array.from(new Uint8Array(data));\n}\n\nfunction websocketCloseCode(reason: RemoteWorkspaceProtocolCloseReason): number {\n\tswitch (reason.code) {\n\t\tcase \"normal\":\n\t\t\treturn 1000;\n\t\tcase \"protocol_error\":\n\t\t\treturn 1002;\n\t\tcase \"invalid_payload\":\n\t\t\treturn 1007;\n\t\tcase \"policy_violation\":\n\t\t\treturn 1008;\n\t\tcase \"message_too_large\":\n\t\t\treturn 1009;\n\t}\n}\n\nexport interface RemoteToolOperationsConnectOptions {\n\ttoken?: string;\n\thandshakeTimeoutMs?: number;\n\tonDisconnect?: (message: string) => void;\n}\n\nexport class RemoteToolOperations implements ToolOperations {\n\treadonly url: string;\n\treadonly protocol: RemoteToolTransportProtocol;\n\tcwd: string;\n\tworkspacePathFlavor: PortablePathFlavor = \"posix\";\n\tprivate workspaceId = \"\";\n\tprivate readonly socket: WebSocket | GuestSharedClientTransport;\n\tprivate readonly isShared: boolean;\n\tprivate readonly client: RemoteWorkspaceClientProtocol;\n\tprivate readonly localToolSchemas = new Map<string, string>();\n\tprivate readonly catalogListeners = new Set<() => void | Promise<void>>();\n\tprivate remoteLspStatus: RemoteLspStatus = { enabled: false, servers: [] };\n\tprivate disconnected = false;\n\tprivate disposePromise: Promise<void> | undefined;\n\n\tprivate constructor(\n\t\turl: string,\n\t\tprotocol: RemoteToolTransportProtocol,\n\t\tsocket: WebSocket | GuestSharedClientTransport,\n\t\toptions: RemoteToolOperationsConnectOptions,\n\t) {\n\t\tthis.url = url;\n\t\tthis.protocol = protocol;\n\t\tthis.socket = socket;\n\t\tthis.isShared = socket instanceof GuestSharedClientTransport;\n\t\tthis.cwd = \"/\";\n\t\tconst transport =\n\t\t\tsocket instanceof GuestSharedClientTransport\n\t\t\t\t? socket\n\t\t\t\t: {\n\t\t\t\t\t\tsend: (message: RemoteWorkspaceClientMessage): Promise<void> =>\n\t\t\t\t\t\t\tnew Promise((resolve, reject) => {\n\t\t\t\t\t\t\t\tif (socket.readyState !== WebSocket.OPEN) {\n\t\t\t\t\t\t\t\t\treject(new Error(\"Remote workspace connection is not open\"));\n\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tsocket.send(JSON.stringify(message), { binary: false, compress: false }, (error) => {\n\t\t\t\t\t\t\t\t\tif (error) reject(error);\n\t\t\t\t\t\t\t\t\telse resolve();\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\tclose: (reason: RemoteWorkspaceProtocolCloseReason): Promise<void> => {\n\t\t\t\t\t\t\tif (socket.readyState === WebSocket.CLOSED) return Promise.resolve();\n\t\t\t\t\t\t\treturn new Promise((resolve) => {\n\t\t\t\t\t\t\t\tsocket.once(\"close\", () => resolve());\n\t\t\t\t\t\t\t\tsocket.close(websocketCloseCode(reason), reason.message);\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t},\n\t\t\t\t\t};\n\t\tthis.client = new RemoteWorkspaceClientProtocol(transport, {\n\t\t\trequiredCapabilities: [\"primitive_operations\", \"tool_updates\"],\n\t\t\toptionalCapabilities: this.isShared\n\t\t\t\t? [\"catalog_refresh\", \"artifacts\", \"lsp_status\"]\n\t\t\t\t: [\"catalog_refresh\", \"file_transfer\", \"artifacts\", \"lsp_status\"],\n\t\t\treceiveLimits: DEFAULT_REMOTE_WORKSPACE_PROTOCOL_LIMITS,\n\t\t\tlocalToolSchemas: this.localToolSchemas,\n\t\t\thandshakeTimeoutMs: options.handshakeTimeoutMs,\n\t\t\tonDisconnect: (message) => {\n\t\t\t\tthis.disconnected = true;\n\t\t\t\toptions.onDisconnect?.(message);\n\t\t\t},\n\t\t\tonCatalogRefreshed: async () => {\n\t\t\t\tawait this.refreshRemoteLspStatus();\n\t\t\t\tawait Promise.all([...this.catalogListeners].map((listener) => listener()));\n\t\t\t},\n\t\t});\n\t\tif (socket instanceof GuestSharedClientTransport) {\n\t\t\tsocket.bind(this.client);\n\t\t} else {\n\t\t\tsocket.on(\"message\", (data, isBinary) => {\n\t\t\t\tif (isBinary) {\n\t\t\t\t\tvoid this.client.disconnect(\"Remote workspace sent an unexpected binary frame\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tvoid this.client.receive(websocketPayload(data)).catch(() => undefined);\n\t\t\t});\n\t\t\tsocket.on(\"close\", () => void this.client.disconnect(\"Remote workspace connection closed\"));\n\t\t\tsocket.on(\"error\", () => void this.client.disconnect(\"Remote workspace connection failed\"));\n\t\t}\n\t}\n\n\tstatic async connect(url: string, options: RemoteToolOperationsConnectOptions = {}): Promise<RemoteToolOperations> {\n\t\tconst normalized = normalizeRemoteUrl(url);\n\t\tconst token = options.token ?? process.env.GRIP_REMOTE_TOKEN;\n\t\tconst socket = await new Promise<WebSocket>((resolve, reject) => {\n\t\t\tconst websocket = new WebSocket(normalized.url, DAEMON_WEBSOCKET_PROTOCOL, {\n\t\t\t\tperMessageDeflate: false,\n\t\t\t\t...(token ? { headers: { authorization: `Bearer ${token}` } } : {}),\n\t\t\t});\n\t\t\tconst cleanup = () => {\n\t\t\t\twebsocket.off(\"open\", onOpen);\n\t\t\t\twebsocket.off(\"error\", onError);\n\t\t\t};\n\t\t\tconst onOpen = () => {\n\t\t\t\tcleanup();\n\t\t\t\tresolve(websocket);\n\t\t\t};\n\t\t\tconst onError = () => {\n\t\t\t\tcleanup();\n\t\t\t\twebsocket.terminate();\n\t\t\t\treject(new Error(`Failed to connect remote workspace: ${normalized.url}`));\n\t\t\t};\n\t\t\twebsocket.once(\"open\", onOpen);\n\t\t\twebsocket.once(\"error\", onError);\n\t\t});\n\t\tconst operations = new RemoteToolOperations(normalized.url, normalized.protocol, socket, options);\n\t\ttry {\n\t\t\tconst handshake = await operations.client.start();\n\t\t\toperations.cwd = handshake.workspace.root;\n\t\t\toperations.workspacePathFlavor = handshake.workspace.pathFlavor;\n\t\t\toperations.workspaceId = handshake.workspace.id;\n\t\t\tawait operations.refreshRemoteLspStatus();\n\t\t\treturn operations;\n\t\t} catch (error) {\n\t\t\tsocket.terminate();\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tstatic async connectGuestShared(\n\t\turl: string,\n\t\troot: string,\n\t\ttoken: string,\n\t\toptions: RemoteToolOperationsConnectOptions = {},\n\t): Promise<RemoteToolOperations> {\n\t\tconst socket = await GuestSharedClientTransport.attach(url, root, token);\n\t\tconst operations = new RemoteToolOperations(url, \"ws\", socket, options);\n\t\ttry {\n\t\t\tconst handshake = await operations.client.start();\n\t\t\tif (!workspaceRootsEqual(handshake.workspace.root, root, handshake.workspace.pathFlavor)) {\n\t\t\t\tthrow new Error(\"Guest shared workspace root differs from daemon handshake\");\n\t\t\t}\n\t\t\toperations.cwd = handshake.workspace.root;\n\t\t\toperations.workspacePathFlavor = handshake.workspace.pathFlavor;\n\t\t\toperations.workspaceId = handshake.workspace.id;\n\t\t\tawait operations.refreshRemoteLspStatus();\n\t\t\treturn operations;\n\t\t} catch (error) {\n\t\t\tawait socket.close({ code: \"normal\", message: \"Shared guest attach failed\" });\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tresolveWorkspaceToolExecution(name: string, parameterSchema: unknown): RemoteWorkspaceToolExecutionTarget {\n\t\tconst schemaHash = hashRemoteWorkspaceJson(parameterSchema);\n\t\tconst catalogTool = this.client.handshake?.catalog.tools.find((tool) => tool.name === name);\n\t\tif (!catalogTool) return \"unavailable\";\n\t\tif (catalogTool.schemaHash !== schemaHash) {\n\t\t\tthrow new Error(`Remote tool schema does not match the local canonical definition: ${name}`);\n\t\t}\n\t\tthis.localToolSchemas.set(name, schemaHash);\n\t\tthis.client.setLocalToolSchemas(this.localToolSchemas);\n\t\treturn \"remote\";\n\t}\n\n\tasync executeWorkspaceTool(\n\t\tname: string,\n\t\tinvocation: WorkspaceToolRemoteInvocation,\n\t): Promise<AgentToolResult<unknown>> {\n\t\tconst invoke = async () => {\n\t\t\tawait this.client.waitForCatalogRefresh();\n\t\t\tconst handshake = this.client.handshake;\n\t\t\tif (!handshake) throw new Error(\"Remote workspace protocol handshake is unavailable\");\n\t\t\tconst catalogTool = handshake.catalog.tools.find((tool) => tool.name === name);\n\t\t\tconst schemaHash = this.localToolSchemas.get(name);\n\t\t\tif (!catalogTool || !schemaHash || catalogTool.schemaHash !== schemaHash) {\n\t\t\t\tthrow new Error(`Remote tool is unavailable or has schema drift: ${name}`);\n\t\t\t}\n\t\t\tconst expectedRevision = await this.revisionForTool(name, invocation);\n\t\t\tconst result = requireRecord(\n\t\t\t\tawait this.client.request(\n\t\t\t\t\t\"tool.invoke\",\n\t\t\t\t\t{\n\t\t\t\t\t\tgeneration: handshake.catalog.generation,\n\t\t\t\t\t\tcatalogHash: handshake.catalogHash,\n\t\t\t\t\t\ttoolName: name,\n\t\t\t\t\t\tschemaHash,\n\t\t\t\t\t\targumentsPrepared: true,\n\t\t\t\t\t\targuments: invocation.arguments,\n\t\t\t\t\t\t...(expectedRevision ? { expectedRevision } : {}),\n\t\t\t\t\t\texecutionOptions: invocation.executionOptions,\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tsignal: invocation.signal,\n\t\t\t\t\t\tonUpdate: invocation.onUpdate\n\t\t\t\t\t\t\t? (update) =>\n\t\t\t\t\t\t\t\t\tinvocation.onUpdate!(parseRemoteWorkspaceToolResult(update) as AgentToolResult<unknown>)\n\t\t\t\t\t\t\t: undefined,\n\t\t\t\t\t},\n\t\t\t\t),\n\t\t\t\t\"tool result\",\n\t\t\t);\n\t\t\tconst toolResult = result;\n\t\t\tif (!Array.isArray(toolResult.content)) throw new Error(\"Invalid remote workspace tool result content\");\n\t\t\tif (name.startsWith(\"lsp_\")) await this.refreshRemoteLspStatus().catch(() => undefined);\n\t\t\treturn {\n\t\t\t\tcontent: toolResult.content as AgentToolResult<unknown>[\"content\"],\n\t\t\t\tdetails: toolResult.details,\n\t\t\t\t...(typeof toolResult.terminate === \"boolean\" ? { terminate: toolResult.terminate } : {}),\n\t\t\t};\n\t\t};\n\t\ttry {\n\t\t\treturn await invoke();\n\t\t} catch (error) {\n\t\t\tif (\n\t\t\t\terror instanceof RemoteWorkspaceRequestError &&\n\t\t\t\terror.code === \"stale_generation\" &&\n\t\t\t\terror.executionState === \"not_started\"\n\t\t\t) {\n\t\t\t\treturn invoke();\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tgetRemoteLspStatus(): RemoteLspStatus {\n\t\treturn structuredClone(this.remoteLspStatus);\n\t}\n\n\tonWorkspaceToolCatalogChanged(listener: () => void | Promise<void>): () => void {\n\t\tthis.catalogListeners.add(listener);\n\t\treturn () => this.catalogListeners.delete(listener);\n\t}\n\n\tprivate async refreshRemoteLspStatus(): Promise<void> {\n\t\tif (!this.client.handshake?.catalog.operations.includes(\"lsp.status\")) {\n\t\t\tthis.remoteLspStatus = { enabled: false, servers: [] };\n\t\t\treturn;\n\t\t}\n\t\tthis.remoteLspStatus = (await this.client.request(\"lsp.status\", {})) as RemoteLspStatus;\n\t}\n\n\tprivate request(method: RemoteWorkspaceMethod, params: unknown) {\n\t\treturn this.client.request(method, params);\n\t}\n\n\tprivate async revisionForTool(\n\t\tname: string,\n\t\tinvocation: WorkspaceToolRemoteInvocation,\n\t): Promise<SharedExpectedRevision | undefined> {\n\t\tif (!this.isShared || (name !== \"edit\" && name !== \"write\")) return undefined;\n\t\tconst path = requireString(\n\t\t\trequireRecord(invocation.arguments, \"shared mutation arguments\").path,\n\t\t\t\"shared mutation path\",\n\t\t);\n\t\treturn this.sharedExpectedRevision(path, invocation.signal, name === \"write\");\n\t}\n\n\t/** Observe exact bytes for CAS, or a daemon-confirmed absent target for creation. */\n\tprivate async sharedExpectedRevision(\n\t\tpath: string,\n\t\tsignal?: AbortSignal,\n\t\tallowAbsent = true,\n\t): Promise<SharedExpectedRevision> {\n\t\tlet read: unknown;\n\t\ttry {\n\t\t\tread = await this.client.request(\"workspace.read\", { path }, { signal });\n\t\t} catch (error) {\n\t\t\tif (!allowAbsent) throw error;\n\t\t\t// Do not infer absence from an error message: only a v2 server stat of the\n\t\t\t// same workspace and path may establish the expected-absent precondition.\n\t\t\tconst stat = requireRecord(\n\t\t\t\tawait this.client.request(\"workspace.stat\", { path }, { signal }),\n\t\t\t\t\"shared stat result\",\n\t\t\t);\n\t\t\tif (stat.kind !== \"absent\") throw error;\n\t\t\treturn { absent: true };\n\t\t}\n\t\tconst result = requireRecord(read, \"shared read result\");\n\t\tconst content = decodeCanonicalBase64(\n\t\t\trequireString(result.contentBase64, \"shared read content\"),\n\t\t\tNumber.MAX_SAFE_INTEGER,\n\t\t);\n\t\tconst sha256 = createHash(\"sha256\").update(content).digest(\"hex\");\n\t\tif (result.revision !== sha256) throw new Error(\"Shared daemon did not confirm the observed file revision\");\n\t\treturn { sha256 };\n\t}\n\tasync exec(command: string, options: ToolExecOptions): Promise<ToolExecResult> {\n\t\tconst result = requireRecord(\n\t\t\tawait this.client.request(\n\t\t\t\t\"workspace.exec\",\n\t\t\t\t{ command, cwd: options.cwd ?? this.cwd },\n\t\t\t\t{\n\t\t\t\t\tsignal: options.signal,\n\t\t\t\t\ttimeoutMs: options.timeout === undefined ? undefined : Math.round(options.timeout * 1000),\n\t\t\t\t\tonUpdate: (update) => {\n\t\t\t\t\t\tconst record = requireRecord(update, \"exec update\");\n\t\t\t\t\t\toptions.onData(\n\t\t\t\t\t\t\tdecodeCanonicalBase64(requireString(record.dataBase64, \"exec update data\"), 1024 * 1024),\n\t\t\t\t\t\t);\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t),\n\t\t\t\"exec result\",\n\t\t);\n\t\treturn { exitCode: typeof result.exitCode === \"number\" ? result.exitCode : null };\n\t}\n\n\tasync access(path: string, mode?: ToolAccessMode): Promise<void> {\n\t\tawait this.request(\"workspace.access\", { path, ...(mode ? { mode } : {}) });\n\t}\n\n\tasync readFile(path: string): Promise<Buffer> {\n\t\tconst result = requireRecord(await this.request(\"workspace.read\", { path }), \"read result\");\n\t\treturn decodeCanonicalBase64(requireString(result.contentBase64, \"read content\"), Number.MAX_SAFE_INTEGER);\n\t}\n\n\tasync writeFile(path: string, content: string | Buffer): Promise<void> {\n\t\tconst expectedRevision = this.isShared ? await this.sharedExpectedRevision(path) : undefined;\n\t\tawait this.request(\"workspace.write\", {\n\t\t\tpath,\n\t\t\tcontentBase64: Buffer.from(content).toString(\"base64\"),\n\t\t\t...(expectedRevision ? { expectedRevision } : {}),\n\t\t});\n\t}\n\n\tasync readResource(path: string): Promise<Buffer> {\n\t\tif (!this.client.handshake?.catalog.operations.includes(\"resource.read\")) {\n\t\t\tthrow new Error(\"Remote workspace does not expose resource reads\");\n\t\t}\n\t\tconst result = requireRecord(await this.request(\"resource.read\", { path }), \"resource read result\");\n\t\treturn decodeCanonicalBase64(\n\t\t\trequireString(result.contentBase64, \"resource read content\"),\n\t\t\tNumber.MAX_SAFE_INTEGER,\n\t\t);\n\t}\n\n\tasync uploadFile(sourcePath: string, destinationPath: string): Promise<void> {\n\t\tif (this.isShared) throw new Error(\"Shared guest file transfers are unavailable\");\n\t\tconst content = await fsReadFile(sourcePath);\n\t\tconst sha256 = createHash(\"sha256\").update(content).digest(\"hex\");\n\t\tconst handle = this.client.beginRequest(\"transfer.upload\", {\n\t\t\tpath: destinationPath,\n\t\t\tlength: content.byteLength,\n\t\t\tsha256,\n\t\t\toverwrite: true,\n\t\t});\n\t\tconst chunkBytes = this.client.handshake?.limits.maxTransferChunkBytes ?? 64 * 1024;\n\t\tfor (let offset = 0; offset < content.byteLength; offset += chunkBytes) {\n\t\t\tawait handle.sendTransferChunk(content.subarray(offset, Math.min(offset + chunkBytes, content.byteLength)));\n\t\t}\n\t\tawait handle.finishTransfer(content.byteLength, sha256);\n\t\tawait handle.result;\n\t}\n\n\tasync downloadFile(sourcePath: string, destinationPath: string): Promise<void> {\n\t\tif (this.isShared) throw new Error(\"Shared guest file transfers are unavailable\");\n\t\tconst stream = createWriteStream(destinationPath);\n\t\ttry {\n\t\t\tawait this.client.request(\n\t\t\t\t\"transfer.download\",\n\t\t\t\t{ path: sourcePath },\n\t\t\t\t{\n\t\t\t\t\tonTransferChunk: (chunk) => writeStreamChunk(stream, chunk),\n\t\t\t\t},\n\t\t\t);\n\t\t\tawait endWriteStream(stream);\n\t\t} catch (error) {\n\t\t\tstream.destroy();\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tasync mkdir(path: string, options: ToolMkdirOptions = {}): Promise<void> {\n\t\tawait this.request(\"workspace.mkdir\", { path, recursive: options.recursive ?? false });\n\t}\n\n\tasync stat(path: string): Promise<ToolFileStat> {\n\t\tconst result = requireRecord(await this.request(\"workspace.stat\", { path }), \"stat result\");\n\t\tif (result.kind === \"absent\") throw new Error(`Remote workspace path does not exist: ${path}`);\n\t\treturn {\n\t\t\tisDirectory: () => result.kind === \"directory\",\n\t\t\tisFile: () => result.kind === \"file\",\n\t\t};\n\t}\n\n\tasync readdir(path: string): Promise<string[]> {\n\t\tconst result = requireRecord(await this.request(\"workspace.readdir\", { path }), \"readdir result\");\n\t\tif (!Array.isArray(result.entries) || !result.entries.every((entry) => typeof entry === \"string\")) {\n\t\t\tthrow new Error(\"Invalid remote workspace readdir entries\");\n\t\t}\n\t\treturn result.entries;\n\t}\n\n\tasync glob(pattern: string, cwd: string, options: ToolGlobOptions): Promise<string[]> {\n\t\tconst result = requireRecord(\n\t\t\tawait this.request(\"workspace.glob\", { pattern, cwd, ignore: options.ignore, limit: options.limit }),\n\t\t\t\"glob result\",\n\t\t);\n\t\tif (!Array.isArray(result.matches) || !result.matches.every((entry) => typeof entry === \"string\")) {\n\t\t\tthrow new Error(\"Invalid remote workspace glob matches\");\n\t\t}\n\t\treturn result.matches;\n\t}\n\n\tasync grep(options: ToolGrepOptions): Promise<ToolGrepResult> {\n\t\tconst result = requireRecord(await this.request(\"workspace.grep\", options), \"grep result\");\n\t\tif (!Array.isArray(result.matches)) throw new Error(\"Invalid remote workspace grep matches\");\n\t\treturn {\n\t\t\tisDirectory: result.isDirectory === true,\n\t\t\tmatches: result.matches.map((entry) => {\n\t\t\t\tconst match = requireRecord(entry, \"grep match\");\n\t\t\t\treturn {\n\t\t\t\t\tfilePath: requireString(match.filePath, \"grep file path\"),\n\t\t\t\t\tlineNumber: typeof match.lineNumber === \"number\" ? match.lineNumber : 0,\n\t\t\t\t\t...(typeof match.lineText === \"string\" ? { lineText: match.lineText } : {}),\n\t\t\t\t};\n\t\t\t}),\n\t\t};\n\t}\n\n\tasync detectImageMimeType(path: string): Promise<string | null | undefined> {\n\t\tconst result = requireRecord(await this.request(\"workspace.detect_image_mime\", { path }), \"image MIME result\");\n\t\treturn typeof result.mimeType === \"string\" ? result.mimeType : null;\n\t}\n\n\tgetBackendInfo(): ToolBackendInfo {\n\t\treturn {\n\t\t\ttype: \"remote\",\n\t\t\tcwd: this.cwd,\n\t\t\turl: this.url,\n\t\t\tprotocol: this.protocol,\n\t\t\tconfigured: true,\n\t\t\tworkspace: { id: this.workspaceId, root: this.cwd, pathFlavor: this.workspacePathFlavor },\n\t\t};\n\t}\n\tisDisconnected(): boolean {\n\t\treturn this.disconnected;\n\t}\n\n\tdispose(): Promise<void> {\n\t\tthis.disposePromise ??= this.client.close().catch(() => {\n\t\t\tif (this.socket instanceof WebSocket) this.socket.terminate();\n\t\t});\n\t\treturn this.disposePromise;\n\t}\n}\n\nexport function createRemoteToolOperations(\n\turl: string,\n\toptions?: RemoteToolOperationsConnectOptions,\n): Promise<RemoteToolOperations> {\n\tif (url.startsWith(\"grip+ws:\")) {\n\t\tconst parsed = new URL(url.replace(/^grip\\+/, \"\"));\n\t\tconst root = parsed.searchParams.get(\"root\");\n\t\tif (!root || !posix.isAbsolute(root) || parsed.searchParams.size !== 1 || !options?.token) {\n\t\t\treturn Promise.reject(new Error(\"Invalid guest shared workspace attachment\"));\n\t\t}\n\t\tparsed.search = \"\";\n\t\treturn RemoteToolOperations.connectGuestShared(parsed.toString(), root, options.token, options);\n\t}\n\treturn RemoteToolOperations.connect(url, options);\n}\n"]}