@gajae-code/coding-agent 0.18.11 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (462) hide show
  1. package/CHANGELOG.md +760 -0
  2. package/dist/types/async/job-manager.d.ts +7 -3
  3. package/dist/types/async/result-formatting.d.ts +2 -0
  4. package/dist/types/autoresearch/git.d.ts +1 -1
  5. package/dist/types/capability/fs.d.ts +2 -2
  6. package/dist/types/capability/rule.d.ts +7 -2
  7. package/dist/types/cli/accounts-cli.d.ts +2 -2
  8. package/dist/types/cli/append-system-prompt.d.ts +6 -0
  9. package/dist/types/cli/args.d.ts +7 -2
  10. package/dist/types/cli/customize-doctor-precedence.d.ts +16 -0
  11. package/dist/types/cli/customize-doctor.d.ts +4 -3
  12. package/dist/types/cli/doctor/install-repairs.d.ts +4 -1
  13. package/dist/types/cli/install-activation.d.ts +10 -4
  14. package/dist/types/cli/public-command-errors.d.ts +2 -0
  15. package/dist/types/cli/root-flags.d.ts +0 -12
  16. package/dist/types/cli/update-cli.d.ts +21 -3
  17. package/dist/types/cli-main.d.ts +0 -6
  18. package/dist/types/cli-ordinary.d.ts +2 -2
  19. package/dist/types/commands/accounts.d.ts +2 -1
  20. package/dist/types/commands/launch.d.ts +0 -6
  21. package/dist/types/commands/migrate.d.ts +1 -1
  22. package/dist/types/commands/sdk.d.ts +13 -0
  23. package/dist/types/commit/changelog/index.d.ts +1 -0
  24. package/dist/types/commit/map-reduce/index.d.ts +4 -4
  25. package/dist/types/config/autorouting-tier-map.d.ts +53 -1
  26. package/dist/types/config/config-hot-reload-trust.d.ts +12 -0
  27. package/dist/types/config/config-hot-reload.d.ts +44 -0
  28. package/dist/types/config/file-lock.d.ts +52 -3
  29. package/dist/types/config/model-discovery-manager.d.ts +2 -0
  30. package/dist/types/config/model-profile-activation.d.ts +79 -9
  31. package/dist/types/config/model-registry.d.ts +72 -8
  32. package/dist/types/config/model-resolver.d.ts +7 -4
  33. package/dist/types/config/models-config-schema.d.ts +10 -0
  34. package/dist/types/config/settings-schema.d.ts +57 -4
  35. package/dist/types/config/settings.d.ts +33 -1
  36. package/dist/types/coordinator-mcp/server.d.ts +4 -0
  37. package/dist/types/customization/import.d.ts +2 -0
  38. package/dist/types/customization/types.d.ts +1 -1
  39. package/dist/types/dap/config.d.ts +8 -4
  40. package/dist/types/dap/session.d.ts +11 -2
  41. package/dist/types/debug/system-info.d.ts +6 -1
  42. package/dist/types/discovery/helpers.d.ts +7 -2
  43. package/dist/types/discovery/mcp-compat.d.ts +3 -1
  44. package/dist/types/edit/index.d.ts +5 -0
  45. package/dist/types/edit/modes/patch.d.ts +14 -0
  46. package/dist/types/edit/modes/replace.d.ts +3 -0
  47. package/dist/types/eval/py/runtime.d.ts +9 -0
  48. package/dist/types/exec/bash-executor.d.ts +12 -0
  49. package/dist/types/export/ttsr.d.ts +19 -0
  50. package/dist/types/extensibility/extensions/types.d.ts +5 -1
  51. package/dist/types/extensibility/slash-commands.d.ts +1 -0
  52. package/dist/types/gjc-runtime/managed-owner-admission.d.ts +3 -0
  53. package/dist/types/gjc-runtime/ralplan-current-final.d.ts +54 -0
  54. package/dist/types/gjc-runtime/ralplan-runtime.d.ts +5 -0
  55. package/dist/types/gjc-runtime/session-state-lock.d.ts +2 -0
  56. package/dist/types/gjc-runtime/state-runtime.d.ts +0 -2
  57. package/dist/types/gjc-runtime/tmux-sessions.d.ts +24 -0
  58. package/dist/types/gjc-runtime/ultragoal-change-set.d.ts +6 -0
  59. package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +1 -0
  60. package/dist/types/harness-control-plane/receipt-spool.d.ts +4 -0
  61. package/dist/types/hindsight/content.d.ts +5 -6
  62. package/dist/types/internal-urls/types.d.ts +2 -0
  63. package/dist/types/lsp/clients/swiftlint-client.d.ts +0 -4
  64. package/dist/types/main.d.ts +23 -18
  65. package/dist/types/migrate/action-planner.d.ts +10 -1
  66. package/dist/types/migrate/adapters/codex.d.ts +2 -1
  67. package/dist/types/migrate/adapters/index.d.ts +42 -1
  68. package/dist/types/migrate/executor.d.ts +3 -1
  69. package/dist/types/migrate/hook-module.d.ts +23 -0
  70. package/dist/types/migrate/report.d.ts +2 -1
  71. package/dist/types/migrate/types.d.ts +82 -3
  72. package/dist/types/modes/components/agent-dashboard.d.ts +1 -1
  73. package/dist/types/modes/components/model-selector.d.ts +43 -0
  74. package/dist/types/modes/components/settings-selector.d.ts +6 -0
  75. package/dist/types/modes/controllers/selector-controller.d.ts +2 -0
  76. package/dist/types/modes/prompt-suggestion-controller.d.ts +1 -1
  77. package/dist/types/modes/theme/theme.d.ts +1 -0
  78. package/dist/types/modes/utils/host-status.d.ts +15 -11
  79. package/dist/types/runtime-mcp/content-limits.d.ts +4 -0
  80. package/dist/types/runtime-mcp/manager.d.ts +7 -0
  81. package/dist/types/runtime-mcp/plugin-mcp-name-filter.d.ts +22 -0
  82. package/dist/types/runtime-mcp/project-host-values.d.ts +11 -5
  83. package/dist/types/runtime-mcp/tool-bridge.d.ts +4 -0
  84. package/dist/types/sdk/broker/broker-exit.d.ts +3 -3
  85. package/dist/types/sdk/broker/broker.d.ts +29 -2
  86. package/dist/types/sdk/broker/daemon-entry.d.ts +11 -1
  87. package/dist/types/sdk/broker/doctor-restart.d.ts +1 -1
  88. package/dist/types/sdk/broker/ensure.d.ts +141 -9
  89. package/dist/types/sdk/broker/hop.d.ts +45 -0
  90. package/dist/types/sdk/broker/lifecycle-ledger.d.ts +18 -10
  91. package/dist/types/sdk/broker/lifecycle.d.ts +22 -0
  92. package/dist/types/sdk/broker/lock-host-identity.d.ts +5 -0
  93. package/dist/types/sdk/broker/session-index.d.ts +6 -1
  94. package/dist/types/sdk/broker/transport.d.ts +2 -0
  95. package/dist/types/sdk/bus/chat-command-policy.d.ts +2 -1
  96. package/dist/types/sdk/bus/index.d.ts +44 -0
  97. package/dist/types/sdk/bus/reconciliation-store.d.ts +10 -0
  98. package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +9 -1
  99. package/dist/types/sdk/bus/telegram-daemon.d.ts +2 -0
  100. package/dist/types/sdk/bus/threaded-render.d.ts +2 -1
  101. package/dist/types/sdk/cli/session-cli.d.ts +0 -1
  102. package/dist/types/sdk/host/control/operations.d.ts +5 -1
  103. package/dist/types/sdk/host/events.d.ts +4 -0
  104. package/dist/types/sdk/host/host.d.ts +2 -1
  105. package/dist/types/sdk/host/prompt-image-upload.d.ts +43 -0
  106. package/dist/types/sdk/host/session-runtime.d.ts +40 -8
  107. package/dist/types/sdk/host/surface-policy.d.ts +2 -1
  108. package/dist/types/sdk/lifecycle/service.d.ts +12 -0
  109. package/dist/types/sdk/prompt-deadline-flush.d.ts +42 -4
  110. package/dist/types/sdk/prompt-deadline-manager.d.ts +2 -0
  111. package/dist/types/sdk/prompt-status.d.ts +11 -4
  112. package/dist/types/sdk/protocol/adapter-validation.d.ts +10 -4
  113. package/dist/types/sdk/reconciliation-extensions.d.ts +1 -1
  114. package/dist/types/sdk/session-list.d.ts +1 -1
  115. package/dist/types/session/agent-session.d.ts +107 -9
  116. package/dist/types/session/internal/managed-session-scope.d.ts +2 -2
  117. package/dist/types/session/internal/managed-session-storage.d.ts +1 -1
  118. package/dist/types/session/internal/native-publish-outcome.d.ts +1 -1
  119. package/dist/types/session/messages.d.ts +5 -1
  120. package/dist/types/session/session-manager.d.ts +29 -3
  121. package/dist/types/session/startup-auth-config.d.ts +24 -2
  122. package/dist/types/session/streaming-output.d.ts +11 -0
  123. package/dist/types/session/terminal-abort.d.ts +3 -1
  124. package/dist/types/session-import/service.d.ts +1 -1
  125. package/dist/types/skill-state/workflow-mutation-guard.d.ts +20 -4
  126. package/dist/types/slash-commands/helpers/mcp.d.ts +5 -0
  127. package/dist/types/slash-commands/helpers/provider-auth-status.d.ts +8 -0
  128. package/dist/types/ssh/ssh-executor.d.ts +8 -1
  129. package/dist/types/system-prompt.d.ts +3 -2
  130. package/dist/types/task/executor.d.ts +19 -2
  131. package/dist/types/task/role-body.d.ts +11 -0
  132. package/dist/types/tools/ast-edit-write-path.d.ts +10 -0
  133. package/dist/types/tools/atomic-file-write.d.ts +9 -7
  134. package/dist/types/tools/awaiting-input.d.ts +28 -0
  135. package/dist/types/tools/bash-allowed-prefixes.d.ts +17 -0
  136. package/dist/types/tools/bash-skill-urls.d.ts +4 -0
  137. package/dist/types/tools/bash.d.ts +7 -0
  138. package/dist/types/tools/descriptors.d.ts +15 -2
  139. package/dist/types/tools/fetch.d.ts +7 -1
  140. package/dist/types/tools/index.d.ts +2 -0
  141. package/dist/types/tools/irc.d.ts +5 -3
  142. package/dist/types/tools/python.d.ts +1 -1
  143. package/dist/types/tools/report-issue.d.ts +41 -0
  144. package/dist/types/tools/subagent.d.ts +23 -0
  145. package/dist/types/tools/tmux-self-injection-guard.d.ts +2 -0
  146. package/dist/types/tools/ultragoal-ask-guard.d.ts +1 -1
  147. package/dist/types/tools/write.d.ts +2 -0
  148. package/dist/types/utils/file-mentions.d.ts +5 -0
  149. package/dist/types/utils/image-limits.d.ts +7 -0
  150. package/dist/types/utils/image-loading.d.ts +0 -1
  151. package/dist/types/utils/open.d.ts +0 -1
  152. package/dist/types/utils/pasted-image-loading.d.ts +1 -5
  153. package/dist/types/utils/prompt-suggestion.d.ts +2 -2
  154. package/dist/types/utils/shell-snapshot.d.ts +2 -0
  155. package/dist/types/utils/title-generator.d.ts +13 -3
  156. package/dist/types/web/scrapers/types.d.ts +8 -1
  157. package/dist/types/web/search/providers/codex.d.ts +6 -7
  158. package/dist/types/web/search/providers/insane.d.ts +7 -1
  159. package/dist/types/web/search/providers/searxng.d.ts +5 -0
  160. package/package.json +10 -9
  161. package/scripts/acp-conformance-agent.ts +12 -2
  162. package/scripts/build-binary.ts +2 -1
  163. package/scripts/capture-subagent-compact-panel-showcase.ts +105 -0
  164. package/scripts/generate-docs-index.ts +8 -1
  165. package/scripts/generate-sdk-operation-inventory.ts +38 -4
  166. package/src/async/job-manager.ts +356 -56
  167. package/src/async/result-formatting.ts +40 -0
  168. package/src/autoresearch/git.ts +9 -2
  169. package/src/autoresearch/prompts/prompt-setup.md +1 -1
  170. package/src/capability/fs.ts +58 -22
  171. package/src/capability/index.ts +2 -2
  172. package/src/capability/rule.ts +11 -2
  173. package/src/cli/accounts-cli.ts +176 -8
  174. package/src/cli/append-system-prompt.ts +12 -0
  175. package/src/cli/args.ts +11 -7
  176. package/src/cli/auth-broker-cli.ts +21 -11
  177. package/src/cli/customize-doctor-precedence.ts +31 -0
  178. package/src/cli/customize-doctor.ts +49 -39
  179. package/src/cli/doctor/install-repairs.ts +35 -5
  180. package/src/cli/fast-help.ts +1 -3
  181. package/src/cli/install-activation.ts +47 -4
  182. package/src/cli/migrate-cli.ts +19 -6
  183. package/src/cli/public-command-errors.ts +6 -0
  184. package/src/cli/root-flags.ts +2 -4
  185. package/src/cli/stats-cli.ts +9 -5
  186. package/src/cli/update-cli.ts +471 -13
  187. package/src/cli-ordinary.ts +7 -2
  188. package/src/commands/accounts.ts +10 -5
  189. package/src/commands/harness.ts +25 -15
  190. package/src/commands/migrate.ts +3 -3
  191. package/src/commands/sdk.ts +277 -50
  192. package/src/commands/ultragoal.ts +16 -8
  193. package/src/commit/agentic/agent.ts +6 -6
  194. package/src/commit/analysis/conventional.ts +14 -5
  195. package/src/commit/changelog/index.ts +39 -1
  196. package/src/commit/map-reduce/index.ts +7 -7
  197. package/src/commit/message.ts +10 -3
  198. package/src/commit/pipeline.ts +12 -12
  199. package/src/config/autorouting-tier-map.ts +50 -1
  200. package/src/config/config-file.ts +106 -3
  201. package/src/config/config-hot-reload-trust.ts +148 -0
  202. package/src/config/config-hot-reload.ts +742 -0
  203. package/src/config/file-lock.ts +1615 -328
  204. package/src/config/keybindings.ts +154 -7
  205. package/src/config/model-bindings-applier.ts +85 -47
  206. package/src/config/model-discovery-manager.ts +10 -0
  207. package/src/config/model-profile-activation.ts +889 -148
  208. package/src/config/model-registry.ts +1706 -205
  209. package/src/config/model-resolver.ts +52 -22
  210. package/src/config/models-config-schema.ts +6 -0
  211. package/src/config/settings-schema.ts +73 -4
  212. package/src/config/settings.ts +460 -24
  213. package/src/coordinator-mcp/policy.ts +26 -1
  214. package/src/coordinator-mcp/server.ts +37 -2
  215. package/src/crash/sanitize.ts +7 -1
  216. package/src/customization/import.ts +279 -8
  217. package/src/customization/inventory.ts +24 -8
  218. package/src/customization/types.ts +1 -0
  219. package/src/dap/client.ts +3 -2
  220. package/src/dap/config.ts +31 -11
  221. package/src/dap/defaults.json +1 -1
  222. package/src/dap/session.ts +161 -36
  223. package/src/debug/crash-diagnostics.ts +89 -6
  224. package/src/debug/report-bundle.ts +90 -2
  225. package/src/debug/system-info.ts +8 -3
  226. package/src/defaults/gjc/extensions/grok-cli-vendor/src/shared/base-url.ts +21 -7
  227. package/src/discovery/agents-md.ts +1 -1
  228. package/src/discovery/builtin.ts +4 -3
  229. package/src/discovery/helpers.ts +25 -16
  230. package/src/discovery/mcp-compat.ts +120 -17
  231. package/src/edit/apply-patch/index.ts +12 -1
  232. package/src/edit/index.ts +204 -22
  233. package/src/edit/modes/patch.ts +214 -8
  234. package/src/edit/modes/replace.ts +136 -0
  235. package/src/eval/js/context-manager.ts +29 -3
  236. package/src/eval/js/worker-entry.ts +51 -18
  237. package/src/eval/py/executor.ts +14 -4
  238. package/src/eval/py/runner.py +144 -5
  239. package/src/eval/py/runtime.ts +44 -4
  240. package/src/exec/bash-executor.ts +81 -4
  241. package/src/exec/bash-shell-guardian.ts +4 -1
  242. package/src/exec/bash-shell-supervisor.ts +8 -4
  243. package/src/exec/isolated-shell.ts +5 -1
  244. package/src/exec/non-interactive-env.ts +0 -1
  245. package/src/export/html/index.ts +86 -6
  246. package/src/export/ttsr.ts +176 -1
  247. package/src/extensibility/custom-tools/wrapper.ts +3 -1
  248. package/src/extensibility/extensions/loader.ts +10 -1
  249. package/src/extensibility/extensions/ouroboros-ooo-bridge.ts +63 -1
  250. package/src/extensibility/extensions/types.ts +5 -1
  251. package/src/extensibility/hooks/loader.ts +10 -2
  252. package/src/extensibility/plugins/marketplace/fetcher.ts +2 -2
  253. package/src/extensibility/plugins/marketplace/manager.ts +36 -10
  254. package/src/extensibility/slash-commands.ts +5 -0
  255. package/src/gjc-runtime/gc-runtime.ts +37 -7
  256. package/src/gjc-runtime/launch-tmux.ts +30 -15
  257. package/src/gjc-runtime/launch-worktree.test.ts +148 -0
  258. package/src/gjc-runtime/launch-worktree.ts +101 -14
  259. package/src/gjc-runtime/managed-owner-admission.ts +46 -2
  260. package/src/gjc-runtime/ralplan-current-final.ts +294 -0
  261. package/src/gjc-runtime/ralplan-runtime.ts +95 -1
  262. package/src/gjc-runtime/session-resolution.ts +38 -6
  263. package/src/gjc-runtime/session-state-lock.ts +59 -14
  264. package/src/gjc-runtime/state-runtime.ts +23 -19
  265. package/src/gjc-runtime/state-writer.ts +109 -5
  266. package/src/gjc-runtime/tmux-sessions.ts +93 -13
  267. package/src/gjc-runtime/ultragoal-change-set.ts +90 -11
  268. package/src/gjc-runtime/ultragoal-guard.ts +9 -1
  269. package/src/gjc-runtime/ultragoal-runtime.ts +15 -5
  270. package/src/harness-control-plane/finalize.ts +11 -0
  271. package/src/harness-control-plane/receipt-spool.ts +105 -28
  272. package/src/harness-control-plane/storage.ts +124 -6
  273. package/src/hindsight/content.ts +116 -16
  274. package/src/hindsight/mental-models.ts +22 -10
  275. package/src/hindsight/state.ts +7 -4
  276. package/src/hooks/native-skill-hook.ts +17 -12
  277. package/src/internal-urls/artifact-protocol.ts +3 -1
  278. package/src/internal-urls/docs-index.generated.ts +23 -22
  279. package/src/internal-urls/local-protocol.ts +167 -8
  280. package/src/internal-urls/types.ts +2 -0
  281. package/src/lsp/client.ts +2 -1
  282. package/src/lsp/clients/biome-client.ts +2 -0
  283. package/src/lsp/clients/swiftlint-client.ts +2 -0
  284. package/src/lsp/index.ts +29 -0
  285. package/src/lsp/workspace-path.ts +23 -0
  286. package/src/main.ts +330 -92
  287. package/src/memories/index.ts +25 -2
  288. package/src/migrate/action-planner.ts +574 -2
  289. package/src/migrate/adapters/claude-code.ts +162 -5
  290. package/src/migrate/adapters/codex.ts +77 -7
  291. package/src/migrate/adapters/index.ts +258 -5
  292. package/src/migrate/adapters/opencode.ts +10 -1
  293. package/src/migrate/executor.ts +222 -7
  294. package/src/migrate/hook-module.ts +193 -0
  295. package/src/migrate/report.ts +63 -2
  296. package/src/migrate/types.ts +84 -4
  297. package/src/modes/DESIGN.md +51 -0
  298. package/src/modes/acp/acp-agent.ts +10 -1
  299. package/src/modes/acp/permission-mode.ts +18 -1
  300. package/src/modes/components/agent-dashboard.ts +20 -4
  301. package/src/modes/components/assistant-message.ts +55 -1
  302. package/src/modes/components/customization/customization-dashboard.ts +7 -0
  303. package/src/modes/components/customization/import-wizard.ts +29 -3
  304. package/src/modes/components/model-selector.ts +157 -103
  305. package/src/modes/components/settings-selector.ts +103 -37
  306. package/src/modes/components/welcome.ts +3 -1
  307. package/src/modes/controllers/command-controller.ts +300 -9
  308. package/src/modes/controllers/event-controller.ts +1 -1
  309. package/src/modes/controllers/extension-ui-controller.ts +31 -1
  310. package/src/modes/controllers/goal-mode-controller.ts +79 -10
  311. package/src/modes/controllers/input-controller.ts +2 -0
  312. package/src/modes/controllers/plan-mode-controller.ts +66 -11
  313. package/src/modes/controllers/runtime-mcp-command-controller.ts +7 -6
  314. package/src/modes/controllers/selector-controller.ts +76 -7
  315. package/src/modes/interactive-mode.ts +123 -4
  316. package/src/modes/prompt-suggestion-controller.ts +1 -1
  317. package/src/modes/runtime-init.ts +5 -1
  318. package/src/modes/theme/theme.ts +15 -0
  319. package/src/modes/utils/host-status.ts +21 -10
  320. package/src/prompts/agent-fragments/browser-aside.md +2 -2
  321. package/src/prompts/system/irc-incoming.md +3 -1
  322. package/src/prompts/tools/debug.md +1 -1
  323. package/src/prompts/tools/irc.md +1 -1
  324. package/src/prompts/tools/read.md +1 -1
  325. package/src/prompts/tools/report-issue.md +13 -0
  326. package/src/prompts/tools/task.md +1 -1
  327. package/src/runtime-credential-selector.ts +2 -2
  328. package/src/runtime-mcp/content-limits.ts +7 -0
  329. package/src/runtime-mcp/manager-pool.test.ts +5 -4
  330. package/src/runtime-mcp/manager.ts +40 -3
  331. package/src/runtime-mcp/oauth-flow.ts +32 -6
  332. package/src/runtime-mcp/plugin-mcp-name-filter.ts +62 -0
  333. package/src/runtime-mcp/project-host-values.ts +12 -8
  334. package/src/runtime-mcp/render.ts +16 -1
  335. package/src/runtime-mcp/tool-bridge.ts +194 -21
  336. package/src/runtime-mcp/transports/stdio.ts +2 -1
  337. package/src/sdk/broker/broker-exit.ts +10 -5
  338. package/src/sdk/broker/broker.ts +484 -85
  339. package/src/sdk/broker/daemon-entry.ts +338 -116
  340. package/src/sdk/broker/doctor-restart.ts +2 -1
  341. package/src/sdk/broker/ensure.ts +1214 -268
  342. package/src/sdk/broker/hop.ts +286 -0
  343. package/src/sdk/broker/lifecycle-ledger.ts +38 -11
  344. package/src/sdk/broker/lifecycle.ts +102 -6
  345. package/src/sdk/broker/lock-host-identity.ts +29 -0
  346. package/src/sdk/broker/session-index.ts +91 -16
  347. package/src/sdk/broker/transport.ts +17 -1
  348. package/src/sdk/bus/chat-command-policy.ts +16 -3
  349. package/src/sdk/bus/chat-daemon-control.ts +2 -2
  350. package/src/sdk/bus/index.ts +460 -35
  351. package/src/sdk/bus/reconciliation-store.ts +201 -8
  352. package/src/sdk/bus/session-control.ts +2 -0
  353. package/src/sdk/bus/slack-daemon.ts +105 -9
  354. package/src/sdk/bus/telegram-daemon-contract.ts +9 -1
  355. package/src/sdk/bus/telegram-daemon.ts +10 -2
  356. package/src/sdk/bus/threaded-render.ts +20 -1
  357. package/src/sdk/cli/session-cli.ts +23 -6
  358. package/src/sdk/host/control/dispatch.ts +13 -1
  359. package/src/sdk/host/control/operations.ts +10 -1
  360. package/src/sdk/host/events.ts +67 -4
  361. package/src/sdk/host/host.ts +8 -2
  362. package/src/sdk/host/prompt-image-upload.ts +438 -0
  363. package/src/sdk/host/sdk-surface-parity.test.ts +15 -0
  364. package/src/sdk/host/session-runtime.test.ts +1737 -129
  365. package/src/sdk/host/session-runtime.ts +684 -83
  366. package/src/sdk/host/surface-policy.ts +4 -0
  367. package/src/sdk/host/websocket-transport.lifecycle.test.ts +90 -2
  368. package/src/sdk/host/websocket-transport.ts +19 -7
  369. package/src/sdk/lifecycle/service.ts +15 -1
  370. package/src/sdk/prompt-deadline-flush.ts +78 -5
  371. package/src/sdk/prompt-deadline-manager.ts +68 -14
  372. package/src/sdk/prompt-status.ts +6 -4
  373. package/src/sdk/protocol/adapter-validation.ts +81 -12
  374. package/src/sdk/protocol/operation-inventory.generated.json +261 -2
  375. package/src/sdk/protocol/operation-registry.ts +33 -3
  376. package/src/sdk/reconciliation-extensions.ts +1 -0
  377. package/src/sdk/session-list.ts +1 -0
  378. package/src/sdk/session.ts +237 -97
  379. package/src/session/agent-session.ts +2726 -479
  380. package/src/session/history-storage.ts +182 -12
  381. package/src/session/internal/managed-session-scope.ts +173 -57
  382. package/src/session/internal/managed-session-storage.ts +85 -12
  383. package/src/session/internal/native-publish-outcome.ts +5 -1
  384. package/src/session/messages.ts +47 -6
  385. package/src/session/session-manager.ts +326 -19
  386. package/src/session/startup-auth-config.ts +116 -8
  387. package/src/session/streaming-output.ts +262 -4
  388. package/src/session/terminal-abort.ts +14 -2
  389. package/src/session-import/redact.ts +53 -2
  390. package/src/session-import/service.ts +1 -0
  391. package/src/skill-state/workflow-mutation-guard.ts +2145 -206
  392. package/src/slash-commands/builtin-registry.ts +21 -6
  393. package/src/slash-commands/helpers/credential-switch.ts +10 -5
  394. package/src/slash-commands/helpers/mcp.ts +11 -3
  395. package/src/slash-commands/helpers/provider-auth-status.ts +456 -0
  396. package/src/ssh/ssh-executor.ts +9 -2
  397. package/src/stt/recorder.ts +26 -9
  398. package/src/system-prompt.ts +61 -16
  399. package/src/task/executor.ts +115 -20
  400. package/src/task/index.ts +389 -175
  401. package/src/task/role-body.ts +84 -0
  402. package/src/task/token-log-mode.test.ts +162 -0
  403. package/src/task/token-log.ts +91 -2
  404. package/src/tools/archive-reader.ts +650 -37
  405. package/src/tools/ask.ts +1 -0
  406. package/src/tools/ast-edit-write-path.ts +64 -0
  407. package/src/tools/ast-edit.ts +18 -1
  408. package/src/tools/ast-grep.ts +7 -2
  409. package/src/tools/atomic-file-write.ts +46 -15
  410. package/src/tools/auto-generated-guard.ts +15 -3
  411. package/src/tools/awaiting-input.ts +114 -0
  412. package/src/tools/bash-allowed-prefixes.ts +214 -6
  413. package/src/tools/bash-interactive.ts +14 -2
  414. package/src/tools/bash-skill-urls.ts +58 -5
  415. package/src/tools/bash.ts +113 -26
  416. package/src/tools/browser/tab-worker.ts +39 -13
  417. package/src/tools/browser.ts +17 -7
  418. package/src/tools/cron.ts +10 -1
  419. package/src/tools/debug.ts +16 -8
  420. package/src/tools/descriptors.test.ts +44 -0
  421. package/src/tools/descriptors.ts +50 -6
  422. package/src/tools/eval.ts +15 -0
  423. package/src/tools/fetch.ts +93 -4
  424. package/src/tools/gh.ts +27 -12
  425. package/src/tools/image-gen.ts +20 -6
  426. package/src/tools/index.ts +2 -0
  427. package/src/tools/irc.ts +5 -3
  428. package/src/tools/monitor.ts +9 -1
  429. package/src/tools/output-meta.ts +74 -5
  430. package/src/tools/python.ts +1 -1
  431. package/src/tools/read.ts +26 -37
  432. package/src/tools/recipe/runner.ts +23 -12
  433. package/src/tools/render-utils.ts +2 -2
  434. package/src/tools/report-issue.ts +184 -0
  435. package/src/tools/search.ts +3 -2
  436. package/src/tools/ssh.ts +274 -8
  437. package/src/tools/subagent-render.ts +114 -12
  438. package/src/tools/subagent.ts +118 -5
  439. package/src/tools/tmux-self-injection-guard.ts +3301 -122
  440. package/src/tools/todo-write.ts +41 -3
  441. package/src/tools/tool-catalog.generated.ts +32 -4
  442. package/src/tools/ultragoal-ask-guard.ts +13 -1
  443. package/src/tools/write.ts +42 -3
  444. package/src/tui/tree-list.ts +15 -1
  445. package/src/utils/commit-message-generator.ts +12 -4
  446. package/src/utils/external-editor.ts +21 -7
  447. package/src/utils/file-mentions.ts +75 -7
  448. package/src/utils/git.ts +38 -7
  449. package/src/utils/image-limits.ts +8 -0
  450. package/src/utils/image-loading.ts +1 -1
  451. package/src/utils/open.ts +73 -4
  452. package/src/utils/pasted-image-loading.ts +9 -8
  453. package/src/utils/prompt-suggestion.ts +5 -4
  454. package/src/utils/shell-snapshot.ts +9 -1
  455. package/src/utils/title-generator.ts +72 -22
  456. package/src/web/scrapers/index.ts +3 -1
  457. package/src/web/scrapers/types.ts +8 -0
  458. package/src/web/scrapers/youtube.ts +3 -2
  459. package/src/web/search/index.ts +2 -1
  460. package/src/web/search/providers/codex.ts +29 -32
  461. package/src/web/search/providers/insane.ts +91 -28
  462. package/src/web/search/providers/searxng.ts +62 -10
@@ -179,6 +179,109 @@ async function assertDirectoryNotSymlink(directoryPath: string): Promise<void> {
179
179
  }
180
180
  }
181
181
 
182
+ /** Group-write and world-write bits. Either one lets another user rename the session directory. */
183
+ const GROUP_OR_WORLD_WRITABLE = 0o022;
184
+
185
+ interface DirectoryIdentityStat {
186
+ isDirectory(): boolean;
187
+ isSymbolicLink(): boolean;
188
+ dev: number;
189
+ ino: number;
190
+ uid: number;
191
+ mode: number;
192
+ }
193
+
194
+ interface ManagedRootIdentity {
195
+ readonly dev: number;
196
+ readonly ino: number;
197
+ readonly uid: number;
198
+ }
199
+
200
+ function isUnsafeDirectory(stat: DirectoryIdentityStat): boolean {
201
+ return !stat.isDirectory() || stat.isSymbolicLink();
202
+ }
203
+
204
+ function managedRootIdentity(stat: DirectoryIdentityStat): ManagedRootIdentity {
205
+ return { dev: stat.dev, ino: stat.ino, uid: stat.uid };
206
+ }
207
+
208
+ function sameManagedRoot(stat: DirectoryIdentityStat, recorded: ManagedRootIdentity): boolean {
209
+ if (isUnsafeDirectory(stat) || stat.dev !== recorded.dev || stat.ino !== recorded.ino) return false;
210
+ // A later chmod to 0755 keeps the same directory. Only a writable bit, a new
211
+ // inode, or a different owner means the root was replaced or opened up.
212
+ if (process.platform === "win32") return true;
213
+ return stat.uid === recorded.uid && (stat.mode & GROUP_OR_WORLD_WRITABLE) === 0;
214
+ }
215
+
216
+ /**
217
+ * An existing managed session directory is adopted only when this user owns it
218
+ * and the group and world cannot write it. Windows has no POSIX owner bits.
219
+ */
220
+ function assertAdoptedManagedRoot(stat: DirectoryIdentityStat): void {
221
+ if (isUnsafeDirectory(stat)) throw new Error("Unsafe local:// root");
222
+ if (process.platform === "win32") return;
223
+ const uid = process.getuid?.();
224
+ if (uid === undefined || stat.uid !== uid || (stat.mode & GROUP_OR_WORLD_WRITABLE) !== 0) {
225
+ throw new Error("Unsafe local:// root");
226
+ }
227
+ }
228
+
229
+ function assertManagedParentSync(localRoot: string): void {
230
+ let parentStat: fsSync.Stats;
231
+ try {
232
+ parentStat = fsSync.lstatSync(path.dirname(localRoot));
233
+ } catch {
234
+ throw new Error("Unsafe local:// root");
235
+ }
236
+ assertAdoptedManagedRoot(parentStat);
237
+ }
238
+
239
+ /**
240
+ * Keep a pinned root unless a changed inode is validated as a settled
241
+ * recreation. A root created by this call may be rebound after migration.
242
+ */
243
+ function acceptManagedRootForInit(
244
+ localRoot: string,
245
+ rootStat: DirectoryIdentityStat,
246
+ recorded: ManagedRootIdentity | undefined,
247
+ adoptedExisting: boolean,
248
+ ): void {
249
+ if (recorded && adoptedExisting) {
250
+ if (sameManagedRoot(rootStat, recorded)) return;
251
+ const pending = pendingCreatedRoots.get(localRoot);
252
+ if (pending && sameManagedRoot(rootStat, pending)) return;
253
+ if (isSettledRecreatedManagedRoot(localRoot, rootStat)) return;
254
+ throw new Error("Unsafe local:// root");
255
+ }
256
+ if (recorded || adoptedExisting) {
257
+ assertAdoptedManagedRoot(rootStat);
258
+ return;
259
+ }
260
+ if (isUnsafeDirectory(rootStat)) throw new Error("Unsafe local:// root");
261
+ }
262
+
263
+ /**
264
+ * A pinned root whose inode changed is still ours when a gjc initializer
265
+ * completed it: the temp sweeper (or `gjc gc`) removed the directory and
266
+ * another process for the same session recreated it, writing a settled
267
+ * migration marker. POSIX owner and write-bit checks on the root and parent
268
+ * protect this exception. Windows changed-inode adoption stays fail-closed
269
+ * until this path validates the replacement's identity and ACLs.
270
+ */
271
+ function isSettledRecreatedManagedRoot(localRoot: string, rootStat: DirectoryIdentityStat): boolean {
272
+ if (process.platform === "win32") return false;
273
+ try {
274
+ assertAdoptedManagedRoot(rootStat);
275
+ assertManagedParentSync(localRoot);
276
+ const marker = path.join(localRoot, LEGACY_MIGRATION_MARKER);
277
+ const markerStat = fsSync.lstatSync(marker);
278
+ if (!markerStat.isFile() || markerStat.isSymbolicLink()) return false;
279
+ return isSettledMigrationMarkerValue(fsSync.readFileSync(marker, "utf8"));
280
+ } catch {
281
+ return false;
282
+ }
283
+ }
284
+
182
285
  const LEGACY_MIGRATION_MARKER = ".gjc-local-legacy-migrated-v1";
183
286
  const MAX_LEGACY_LOCAL_BYTES = 64 * 1024 * 1024;
184
287
 
@@ -508,9 +611,33 @@ async function migrateLegacyLocal(
508
611
  }
509
612
  }
510
613
 
511
- const initializedLocalRoots = new Set<string>();
614
+ const initializedLocalRoots = new Map<string, ManagedRootIdentity>();
615
+ /** A directory this process created and has not finished migrating. */
616
+ const pendingCreatedRoots = new Map<string, ManagedRootIdentity>();
512
617
  const initializingLocalRoots = new Map<string, Promise<string>>();
513
618
 
619
+ function assertRememberedManagedRoot(localRoot: string, recorded: ManagedRootIdentity): void {
620
+ let stat: fsSync.Stats;
621
+ try {
622
+ stat = fsSync.lstatSync(localRoot);
623
+ } catch {
624
+ throw new Error("Unsafe local:// root");
625
+ }
626
+ if (!sameManagedRoot(stat, recorded)) throw new Error("Unsafe local:// root");
627
+ }
628
+
629
+ function rememberManagedRoot(localRoot: string, expected: ManagedRootIdentity): void {
630
+ let stat: fsSync.Stats;
631
+ try {
632
+ stat = fsSync.lstatSync(localRoot);
633
+ } catch {
634
+ throw new Error("Unsafe local:// root");
635
+ }
636
+ if (!sameManagedRoot(stat, expected)) throw new Error("Unsafe local:// root");
637
+ initializedLocalRoots.set(localRoot, managedRootIdentity(stat));
638
+ pendingCreatedRoots.delete(localRoot);
639
+ }
640
+
514
641
  /**
515
642
  * Completes the mandatory legacy migration gate for a local root.
516
643
  * Call and await this before using the synchronous path resolver for reads or writes.
@@ -559,16 +686,21 @@ async function initializeManagedLocalRoot(options: LocalProtocolOptions, localRo
559
686
 
560
687
  await fs.mkdir(scratchParent, { recursive: true, mode: 0o700 });
561
688
  await assertDirectoryNotSymlink(scratchParent);
689
+ assertAdoptedManagedRoot(await fs.lstat(scratchParent));
690
+ let adoptedExisting = false;
562
691
  try {
563
692
  await fs.mkdir(localRoot, { mode: 0o700 });
564
693
  } catch (error) {
565
694
  if (error instanceof Error && "code" in error && error.code === "EEXIST") {
566
- await assertDirectoryNotSymlink(localRoot);
695
+ adoptedExisting = true;
567
696
  } else {
568
697
  throw error;
569
698
  }
570
699
  }
571
- await assertDirectoryNotSymlink(localRoot);
700
+ const rootStat = await fs.lstat(localRoot);
701
+ const recorded = initializedLocalRoots.get(localRoot);
702
+ acceptManagedRootForInit(localRoot, rootStat, recorded, adoptedExisting);
703
+ if (!adoptedExisting) pendingCreatedRoots.set(localRoot, managedRootIdentity(rootStat));
572
704
  await migrateLegacyLocal(options, localRoot, scratchParent);
573
705
 
574
706
  const resolvedScratchParent = await fs.realpath(scratchParent);
@@ -577,7 +709,10 @@ async function initializeManagedLocalRoot(options: LocalProtocolOptions, localRo
577
709
  if (resolvedLocalRoot === resolvedScratchParent) {
578
710
  throw new Error("Unsafe local:// root");
579
711
  }
580
- initializedLocalRoots.add(resolvedLocalRoot);
712
+ // Key the lexical path the sync resolver checks. realpath(/var -> /private/var)
713
+ // is a different string for the same directory, so caching only the real path
714
+ // let a later rename-swap skip this identity check.
715
+ rememberManagedRoot(localRoot, managedRootIdentity(rootStat));
581
716
  return resolvedLocalRoot;
582
717
  }
583
718
 
@@ -587,18 +722,41 @@ function initializeLocalRootSyncWhenLegacyAbsent(options: LocalProtocolOptions,
587
722
  initializeExplicitLocalRootSync(explicitRoot);
588
723
  return;
589
724
  }
590
- if (initializedLocalRoots.has(localRoot)) return;
725
+ const recorded = initializedLocalRoots.get(localRoot);
726
+ if (recorded) {
727
+ assertManagedParentSync(localRoot);
728
+ let present: fsSync.Stats | undefined;
729
+ try {
730
+ present = fsSync.lstatSync(localRoot);
731
+ } catch (error) {
732
+ if (!isEnoent(error)) throw new Error("Unsafe local:// root");
733
+ }
734
+ if (present) {
735
+ if (sameManagedRoot(present, recorded)) {
736
+ assertRememberedManagedRoot(localRoot, recorded);
737
+ return;
738
+ }
739
+ const pending = pendingCreatedRoots.get(localRoot);
740
+ if (!(pending && sameManagedRoot(present, pending)) && !isSettledRecreatedManagedRoot(localRoot, present)) {
741
+ throw new Error("Unsafe local:// root");
742
+ }
743
+ }
744
+ }
591
745
  const scratchParent = path.dirname(localRoot);
592
746
  fsSync.mkdirSync(scratchParent, { recursive: true, mode: 0o700 });
593
747
  const parentStat = fsSync.lstatSync(scratchParent);
594
748
  if (!parentStat.isDirectory() || parentStat.isSymbolicLink()) throw new Error("Unsafe local:// root");
749
+ assertAdoptedManagedRoot(parentStat);
750
+ let adoptedExisting = false;
595
751
  try {
596
752
  fsSync.mkdirSync(localRoot, { mode: 0o700 });
597
753
  } catch (error) {
598
754
  if ((error as NodeJS.ErrnoException).code !== "EEXIST") throw error;
755
+ adoptedExisting = true;
599
756
  }
600
757
  const rootStat = fsSync.lstatSync(localRoot);
601
- if (!rootStat.isDirectory() || rootStat.isSymbolicLink()) throw new Error("Unsafe local:// root");
758
+ acceptManagedRootForInit(localRoot, rootStat, recorded, adoptedExisting);
759
+ if (!adoptedExisting) pendingCreatedRoots.set(localRoot, managedRootIdentity(rootStat));
602
760
  const marker = path.join(localRoot, LEGACY_MIGRATION_MARKER);
603
761
  try {
604
762
  // Accept exactly the marker states the async gate treats as settled. A
@@ -608,7 +766,7 @@ function initializeLocalRootSyncWhenLegacyAbsent(options: LocalProtocolOptions,
608
766
  // async gate had already completed.
609
767
  if (!isSettledMigrationMarkerValue(fsSync.readFileSync(marker, "utf8")))
610
768
  throw new Error("Unsafe local:// migration marker");
611
- initializedLocalRoots.add(localRoot);
769
+ rememberManagedRoot(localRoot, managedRootIdentity(rootStat));
612
770
  return;
613
771
  } catch (error) {
614
772
  if (!isEnoent(error)) throw error;
@@ -629,7 +787,7 @@ function initializeLocalRootSyncWhenLegacyAbsent(options: LocalProtocolOptions,
629
787
  if (!isSettledMigrationMarkerValue(fsSync.readFileSync(marker, "utf8")))
630
788
  throw new Error("Unsafe local:// migration marker");
631
789
  }
632
- initializedLocalRoots.add(localRoot);
790
+ rememberManagedRoot(localRoot, managedRootIdentity(rootStat));
633
791
  }
634
792
 
635
793
  export function resolveLocalRoot(options: LocalProtocolOptions): string {
@@ -696,6 +854,7 @@ export class LocalProtocolHandler implements ProtocolHandler {
696
854
  LocalProtocolHandler.#override = undefined;
697
855
  LocalProtocolHandler.#ownedOverrides = [];
698
856
  initializedLocalRoots.clear();
857
+ pendingCreatedRoots.clear();
699
858
  initializingLocalRoots.clear();
700
859
  }
701
860
 
@@ -21,6 +21,8 @@ export interface InternalResource {
21
21
  size?: number;
22
22
  /** Underlying filesystem path (for debugging, not exposed to agent) */
23
23
  sourcePath?: string;
24
+ /** True when model-facing reads must wrap the selected content as untrusted. */
25
+ untrusted?: boolean;
24
26
  /** Additional notes about resolution */
25
27
  notes?: string[];
26
28
  /**
package/src/lsp/client.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { isEnoent, isKnownSinkPeerClosedError, logger, untilAborted } from "@gajae-code/utils";
2
+ import { projectSafeSpawnEnv } from "@gajae-code/utils/shell-config";
2
3
  import { formatCrashDiagnosticNotice, writeCrashReport } from "../debug/crash-diagnostics";
3
4
  import { registerResourceOwner, spawnOwnedProcess } from "../runtime/process-lifecycle";
4
5
  import { ToolAbortError, throwIfAborted } from "../tools/tool-errors";
@@ -538,7 +539,7 @@ export async function getOrCreateClient(config: ServerConfig, cwd: string, initT
538
539
  const owner = spawnOwnedProcess([command, ...args], {
539
540
  cwd,
540
541
  stdin: "pipe",
541
- env: env ? { ...Bun.env, ...env } : undefined,
542
+ env: { ...projectSafeSpawnEnv(), ...env },
542
543
  name: `lsp:${config.command}`,
543
544
  });
544
545
  const proc = owner.child;
@@ -3,6 +3,7 @@
3
3
  * Uses Biome's CLI with JSON output instead of LSP (which has stale diagnostics issues).
4
4
  */
5
5
  import path from "node:path";
6
+ import { projectSafeSpawnEnv } from "@gajae-code/utils/shell-config";
6
7
  import type { Diagnostic, DiagnosticSeverity, LinterClient, ServerConfig } from "../../lsp/types";
7
8
 
8
9
  // =============================================================================
@@ -84,6 +85,7 @@ async function runBiome(
84
85
  try {
85
86
  const proc = Bun.spawn([command, ...args], {
86
87
  cwd,
88
+ env: projectSafeSpawnEnv(),
87
89
  stdout: "pipe",
88
90
  stderr: "pipe",
89
91
  windowsHide: true,
@@ -2,6 +2,7 @@
2
2
  * SwiftLint CLI-based linter client.
3
3
  * Parses SwiftLint's JSON reporter output into LSP Diagnostic format.
4
4
  */
5
+ import { projectSafeSpawnEnv } from "@gajae-code/utils/shell-config";
5
6
  import type { Diagnostic, DiagnosticSeverity, LinterClient, ServerConfig } from "../../lsp/types";
6
7
 
7
8
  /** Shape of a single violation from `swiftlint lint --reporter json`. */
@@ -36,6 +37,7 @@ async function runSwiftLint(
36
37
  try {
37
38
  const proc = Bun.spawn([command, ...args], {
38
39
  cwd,
40
+ env: projectSafeSpawnEnv(),
39
41
  stdout: "pipe",
40
42
  stderr: "pipe",
41
43
  windowsHide: true,
package/src/lsp/index.ts CHANGED
@@ -77,6 +77,7 @@ import {
77
77
  symbolKindToIcon,
78
78
  uriToFile,
79
79
  } from "./utils";
80
+ import { assertInsideWorkspace } from "./workspace-path";
80
81
 
81
82
  export type { LspServerStatus } from "./client";
82
83
  export type { LspToolDetails } from "./types";
@@ -347,6 +348,17 @@ interface FileRenamePair {
347
348
  * For files this is a single pair. For directories this walks every regular file underneath
348
349
  * and produces a parallel pair anchored at the new directory root.
349
350
  */
351
+ /**
352
+ * POSIX `/` contains every absolute path, and the shared helper's prefix for
353
+ * it is `//`, so skip containment there. A Windows drive root is only that
354
+ * volume, so it is checked. Any other platform root keeps the previous skip.
355
+ */
356
+ function workspaceRootSkipsContainment(root: string): boolean {
357
+ if (root === "/") return true;
358
+ if (/^[A-Za-z]:[\\/]$/.test(root)) return false;
359
+ return root === path.parse(root).root;
360
+ }
361
+
350
362
  async function enumerateRenamePairs(
351
363
  source: string,
352
364
  dest: string,
@@ -1525,6 +1537,23 @@ export class LspTool implements AgentTool<typeof lspSchema, LspToolDetails, Them
1525
1537
  }
1526
1538
  }
1527
1539
 
1540
+ // Reject every path this rename will write before the first write. Check the
1541
+ // caller signal after each await, including the last one.
1542
+ const cwd = this.session.cwd;
1543
+ throwIfAborted(signal);
1544
+ const workspaceRoot = await fs.promises.realpath(cwd);
1545
+ throwIfAborted(signal);
1546
+ if (!workspaceRootSkipsContainment(workspaceRoot)) {
1547
+ await assertInsideWorkspace(cwd, source);
1548
+ throwIfAborted(signal);
1549
+ await assertInsideWorkspace(cwd, dest);
1550
+ throwIfAborted(signal);
1551
+ for (const uri of acceptedByUri.keys()) {
1552
+ await assertInsideWorkspace(cwd, uriToFile(uri));
1553
+ throwIfAborted(signal);
1554
+ }
1555
+ }
1556
+
1528
1557
  for (const [uri, bucket] of acceptedByUri) {
1529
1558
  const filePath = uriToFile(uri);
1530
1559
  await applyTextEdits(filePath, bucket.edits);
@@ -6,7 +6,30 @@ function isEnoent(error: unknown): boolean {
6
6
  return (error as NodeJS.ErrnoException).code === "ENOENT";
7
7
  }
8
8
 
9
+ /** Drive-letter root such as `C:\`. A UNC share root is not a drive root. */
10
+ function windowsDriveLetter(root: string): string | null {
11
+ const match = /^([A-Za-z]):[\\/]$/.exec(root);
12
+ return match ? match[1].toLowerCase() : null;
13
+ }
14
+
15
+ /**
16
+ * A Windows drive root already ends in a separator. Prefixing another one
17
+ * (`C:\` + `\`) matches nothing on that volume, so compare the drive letter.
18
+ * Same-volume paths stay inside, including `\\?\C:\`. Other drives and UNC
19
+ * shares do not.
20
+ */
21
+ function sameWindowsDrive(driveRoot: string, candidate: string): boolean {
22
+ const letter = windowsDriveLetter(driveRoot);
23
+ if (!letter) return false;
24
+ const normalized = candidate.replace(/\//g, "\\");
25
+ const extended = /^\\\\\?\\([A-Za-z]):\\/.exec(normalized);
26
+ if (extended) return extended[1].toLowerCase() === letter;
27
+ if (normalized.startsWith("\\\\")) return false;
28
+ return path.win32.parse(normalized).root.toLowerCase() === `${letter}:\\`;
29
+ }
30
+
9
31
  function escapes(root: string, candidate: string): boolean {
32
+ if (windowsDriveLetter(root)) return !sameWindowsDrive(root, candidate);
10
33
  return candidate !== root && !candidate.startsWith(`${root}${path.sep}`);
11
34
  }
12
35