@gajae-code/coding-agent 0.16.7 → 0.17.1

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 (321) hide show
  1. package/CHANGELOG.md +180 -1
  2. package/dist/types/capability/fs.d.ts +37 -4
  3. package/dist/types/capability/index.d.ts +19 -0
  4. package/dist/types/capability/types.d.ts +9 -0
  5. package/dist/types/cli/doctor/args.d.ts +36 -0
  6. package/dist/types/cli/doctor/checks.d.ts +18 -0
  7. package/dist/types/cli/doctor/config-repairs.d.ts +38 -0
  8. package/dist/types/cli/doctor/context.d.ts +89 -0
  9. package/dist/types/cli/doctor/files.d.ts +27 -0
  10. package/dist/types/cli/doctor/ids.d.ts +20 -0
  11. package/dist/types/cli/doctor/install-repairs.d.ts +94 -0
  12. package/dist/types/cli/doctor/journal.d.ts +43 -0
  13. package/dist/types/cli/doctor/managed-link.d.ts +39 -0
  14. package/dist/types/cli/doctor/native-probe.d.ts +2 -0
  15. package/dist/types/cli/doctor/permission-repairs.d.ts +25 -0
  16. package/dist/types/cli/doctor/plugin-quarantine.d.ts +77 -0
  17. package/dist/types/cli/doctor/plugin-restore.d.ts +40 -0
  18. package/dist/types/cli/doctor/plugin-targets.d.ts +11 -0
  19. package/dist/types/cli/doctor/probe-client.d.ts +9 -0
  20. package/dist/types/cli/doctor/probe-types.d.ts +18 -0
  21. package/dist/types/cli/doctor/probe.d.ts +3 -0
  22. package/dist/types/cli/doctor/projection-probe.d.ts +2 -0
  23. package/dist/types/cli/doctor/repairs.d.ts +28 -0
  24. package/dist/types/cli/doctor/report.d.ts +27 -0
  25. package/dist/types/cli/doctor/runner.d.ts +4 -0
  26. package/dist/types/cli/doctor/safety.d.ts +10 -0
  27. package/dist/types/cli/doctor/service-repairs.d.ts +8 -0
  28. package/dist/types/cli/doctor/service-targets.d.ts +41 -0
  29. package/dist/types/cli/doctor/types.d.ts +145 -0
  30. package/dist/types/cli/doctor-cli.d.ts +7 -0
  31. package/dist/types/cli/doctor-supervisor.d.ts +121 -0
  32. package/dist/types/cli/doctor-worker.d.ts +29 -0
  33. package/dist/types/cli/install-activation.d.ts +158 -0
  34. package/dist/types/cli/macos-community-app.d.ts +41 -0
  35. package/dist/types/cli/mcp-cli.d.ts +0 -11
  36. package/dist/types/cli/update-cli.d.ts +41 -4
  37. package/dist/types/cli-main.d.ts +174 -0
  38. package/dist/types/cli.d.ts +1 -171
  39. package/dist/types/commands/doctor.d.ts +7 -0
  40. package/dist/types/commands/sdk.d.ts +17 -8
  41. package/dist/types/config/autorouting-tier-map.d.ts +19 -0
  42. package/dist/types/config/file-lock.d.ts +27 -3
  43. package/dist/types/config/model-preset-registry.d.ts +1 -0
  44. package/dist/types/config/model-registry.d.ts +12 -5
  45. package/dist/types/config/model-resolver.d.ts +16 -3
  46. package/dist/types/config/models-config-schema.d.ts +2 -0
  47. package/dist/types/config/settings-schema.d.ts +1 -1
  48. package/dist/types/coordinator-mcp/question-gate-codec.d.ts +1 -1
  49. package/dist/types/coordinator-mcp/session-reaper.d.ts +15 -5
  50. package/dist/types/cursor.d.ts +9 -9
  51. package/dist/types/discovery/agents-md.d.ts +6 -1
  52. package/dist/types/discovery/helpers.d.ts +36 -7
  53. package/dist/types/discovery/index.d.ts +1 -1
  54. package/dist/types/discovery/mcp-json.d.ts +2 -0
  55. package/dist/types/extensibility/extensions/loader.d.ts +22 -1
  56. package/dist/types/extensibility/gjc-plugins/lifecycle.d.ts +14 -1
  57. package/dist/types/extensibility/gjc-plugins/paths.d.ts +2 -1
  58. package/dist/types/extensibility/gjc-plugins/types.d.ts +25 -0
  59. package/dist/types/extensibility/plugins/manager.d.ts +33 -4
  60. package/dist/types/extensibility/plugins/marketplace/cache.d.ts +6 -0
  61. package/dist/types/extensibility/plugins/marketplace/manager.d.ts +186 -0
  62. package/dist/types/extensibility/plugins/marketplace/registry.d.ts +27 -0
  63. package/dist/types/extensibility/plugins/marketplace/source-resolver.d.ts +16 -1
  64. package/dist/types/extensibility/runtime-skill-discovery.d.ts +16 -3
  65. package/dist/types/gjc-runtime/autoresearch-runtime.d.ts +6 -0
  66. package/dist/types/gjc-runtime/launch-worktree.d.ts +53 -0
  67. package/dist/types/gjc-runtime/session-layout.d.ts +8 -0
  68. package/dist/types/gjc-runtime/session-state-lock.d.ts +15 -0
  69. package/dist/types/gjc-runtime/session-state-sidecar.d.ts +13 -0
  70. package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +6 -0
  71. package/dist/types/main.d.ts +19 -1
  72. package/dist/types/modes/acp/acp-agent.d.ts +40 -0
  73. package/dist/types/modes/components/agent-dashboard.d.ts +1 -0
  74. package/dist/types/modes/components/model-selector.d.ts +2 -0
  75. package/dist/types/modes/interactive-mode.d.ts +1 -1
  76. package/dist/types/runtime-mcp/config-writer.d.ts +47 -0
  77. package/dist/types/runtime-mcp/policy-json-edit.d.ts +60 -0
  78. package/dist/types/runtime-mcp/startup-policy.d.ts +4 -0
  79. package/dist/types/runtime-mcp/types.d.ts +2 -2
  80. package/dist/types/sdk/acp/adapter.d.ts +5 -1
  81. package/dist/types/sdk/broker/broker.d.ts +51 -0
  82. package/dist/types/sdk/broker/daemon-entry.d.ts +39 -0
  83. package/dist/types/sdk/broker/discovery.d.ts +38 -0
  84. package/dist/types/sdk/broker/doctor-restart.d.ts +79 -0
  85. package/dist/types/sdk/broker/endpoint-authority.d.ts +7 -0
  86. package/dist/types/sdk/broker/ensure.d.ts +14 -0
  87. package/dist/types/sdk/broker/lifecycle.d.ts +2 -0
  88. package/dist/types/sdk/broker/process-incarnation.d.ts +50 -0
  89. package/dist/types/sdk/broker/runtime.d.ts +26 -0
  90. package/dist/types/sdk/broker/session-index.d.ts +13 -4
  91. package/dist/types/sdk/broker/worktree-occupancy.d.ts +14 -0
  92. package/dist/types/sdk/bus/chat-daemon-cli.d.ts +16 -0
  93. package/dist/types/sdk/bus/chat-daemon-control.d.ts +101 -6
  94. package/dist/types/sdk/bus/chat-daemon-runtime.d.ts +16 -0
  95. package/dist/types/sdk/bus/daemon-startup-exclusion.d.ts +43 -0
  96. package/dist/types/sdk/bus/doctor-daemon-restart-client.d.ts +51 -0
  97. package/dist/types/sdk/bus/doctor-daemon-restart.d.ts +39 -0
  98. package/dist/types/sdk/bus/index.d.ts +2 -0
  99. package/dist/types/sdk/bus/notification-service.d.ts +3 -0
  100. package/dist/types/sdk/bus/telegram-daemon-cli.d.ts +11 -0
  101. package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +1 -1
  102. package/dist/types/sdk/bus/telegram-daemon-control.d.ts +5 -0
  103. package/dist/types/sdk/bus/telegram-daemon-owner-registry.d.ts +1 -0
  104. package/dist/types/sdk/bus/telegram-daemon.d.ts +36 -5
  105. package/dist/types/sdk/cli/session-cli.d.ts +1 -0
  106. package/dist/types/sdk/host/session-runtime.d.ts +4 -0
  107. package/dist/types/sdk/host/surface-policy.d.ts +3 -1
  108. package/dist/types/sdk/lifecycle/service.d.ts +44 -10
  109. package/dist/types/sdk/prompt-deadline-lease.d.ts +6 -3
  110. package/dist/types/sdk/prompt-deadline-manager.d.ts +2 -2
  111. package/dist/types/sdk/prompt-failure.d.ts +47 -0
  112. package/dist/types/sdk/prompt-status.d.ts +19 -0
  113. package/dist/types/sdk/router/session-router.d.ts +3 -4
  114. package/dist/types/sdk/service-artifact-paths.d.ts +16 -0
  115. package/dist/types/sdk/session-authority.d.ts +9 -0
  116. package/dist/types/sdk/session.d.ts +3 -1
  117. package/dist/types/session/agent-session.d.ts +1 -1
  118. package/dist/types/session/blob-store.d.ts +1 -1
  119. package/dist/types/session/fallback-chain-controller.d.ts +3 -6
  120. package/dist/types/session/internal/managed-session-scope.d.ts +44 -0
  121. package/dist/types/session/primary-control-surface.d.ts +4 -0
  122. package/dist/types/session/session-manager.d.ts +6 -0
  123. package/dist/types/system-prompt.d.ts +6 -0
  124. package/dist/types/tools/ask.d.ts +25 -0
  125. package/dist/types/tools/image-gen.d.ts +0 -4
  126. package/dist/types/tools/skill-discovery.d.ts +7 -5
  127. package/dist/types/tools/ultragoal-ask-guard.d.ts +7 -4
  128. package/dist/types/utils/changelog.d.ts +0 -9
  129. package/dist/types/utils/mupdf-wasm.d.ts +1 -0
  130. package/package.json +9 -8
  131. package/scripts/compile-args.ts +8 -4
  132. package/src/capability/fs.ts +395 -17
  133. package/src/capability/index.ts +273 -10
  134. package/src/capability/types.ts +9 -0
  135. package/src/cli/customize-doctor.ts +18 -12
  136. package/src/cli/doctor/args.ts +236 -0
  137. package/src/cli/doctor/checks.ts +431 -0
  138. package/src/cli/doctor/config-repairs.ts +517 -0
  139. package/src/cli/doctor/context.ts +149 -0
  140. package/src/cli/doctor/files.ts +149 -0
  141. package/src/cli/doctor/ids.ts +115 -0
  142. package/src/cli/doctor/install-repairs.ts +335 -0
  143. package/src/cli/doctor/journal.ts +423 -0
  144. package/src/cli/doctor/managed-link.ts +514 -0
  145. package/src/cli/doctor/native-probe.ts +13 -0
  146. package/src/cli/doctor/permission-repairs.ts +242 -0
  147. package/src/cli/doctor/plugin-quarantine.ts +377 -0
  148. package/src/cli/doctor/plugin-restore.ts +59 -0
  149. package/src/cli/doctor/plugin-targets.ts +373 -0
  150. package/src/cli/doctor/probe-client.ts +195 -0
  151. package/src/cli/doctor/probe-types.ts +21 -0
  152. package/src/cli/doctor/probe.ts +42 -0
  153. package/src/cli/doctor/projection-probe.ts +249 -0
  154. package/src/cli/doctor/repairs.ts +1157 -0
  155. package/src/cli/doctor/report.ts +176 -0
  156. package/src/cli/doctor/runner.ts +342 -0
  157. package/src/cli/doctor/safety.ts +40 -0
  158. package/src/cli/doctor/service-repairs.ts +452 -0
  159. package/src/cli/doctor/service-targets.ts +274 -0
  160. package/src/cli/doctor/types.ts +164 -0
  161. package/src/cli/doctor-cli.ts +62 -0
  162. package/src/cli/doctor-supervisor.ts +571 -0
  163. package/src/cli/doctor-worker.ts +181 -0
  164. package/src/cli/install-activation.ts +666 -0
  165. package/src/cli/macos-community-app.ts +1449 -0
  166. package/src/cli/mcp-cli.ts +11 -21
  167. package/src/cli/nofile-limit.ts +5 -1
  168. package/src/cli/plugin-cli.ts +6 -1
  169. package/src/cli/update-cli.ts +362 -264
  170. package/src/cli-main.ts +517 -0
  171. package/src/cli.ts +64 -433
  172. package/src/commands/autoresearch.ts +11 -5
  173. package/src/commands/completion.ts +1 -1
  174. package/src/commands/doctor.ts +13 -0
  175. package/src/commands/sdk.ts +58 -10
  176. package/src/commands/ultragoal.ts +11 -5
  177. package/src/config/autorouting-tier-map.ts +7 -0
  178. package/src/config/file-lock.ts +736 -71
  179. package/src/config/mcp-schema.json +3 -3
  180. package/src/config/model-preset-registry.ts +1 -0
  181. package/src/config/model-registry.ts +80 -27
  182. package/src/config/model-resolver.ts +33 -4
  183. package/src/config/models-config-schema.ts +2 -0
  184. package/src/config/settings-schema.ts +6 -2
  185. package/src/coordinator-mcp/question-gate-codec.ts +1 -0
  186. package/src/coordinator-mcp/server.ts +534 -51
  187. package/src/coordinator-mcp/session-reaper.ts +64 -8
  188. package/src/cursor.ts +223 -53
  189. package/src/discovery/agents-md.ts +74 -8
  190. package/src/discovery/agents.ts +32 -7
  191. package/src/discovery/builtin.ts +184 -75
  192. package/src/discovery/claude-plugins.ts +85 -17
  193. package/src/discovery/cline.ts +38 -10
  194. package/src/discovery/cursor.ts +19 -6
  195. package/src/discovery/gemini.ts +62 -22
  196. package/src/discovery/github.ts +13 -3
  197. package/src/discovery/helpers.ts +483 -89
  198. package/src/discovery/index.ts +1 -0
  199. package/src/discovery/mcp-json.ts +36 -10
  200. package/src/discovery/opencode.ts +36 -19
  201. package/src/discovery/ssh.ts +11 -8
  202. package/src/discovery/windsurf.ts +12 -3
  203. package/src/exec/bash-executor.ts +10 -3
  204. package/src/exec/isolated-shell.ts +15 -2
  205. package/src/extensibility/extensions/loader.ts +88 -26
  206. package/src/extensibility/gjc-plugins/lifecycle.ts +189 -0
  207. package/src/extensibility/gjc-plugins/paths.ts +4 -1
  208. package/src/extensibility/gjc-plugins/types.ts +27 -0
  209. package/src/extensibility/plugins/manager.ts +200 -22
  210. package/src/extensibility/plugins/marketplace/cache.ts +55 -2
  211. package/src/extensibility/plugins/marketplace/manager.ts +1262 -162
  212. package/src/extensibility/plugins/marketplace/registry.ts +153 -0
  213. package/src/extensibility/plugins/marketplace/source-resolver.ts +116 -10
  214. package/src/extensibility/runtime-skill-discovery.ts +61 -9
  215. package/src/extensibility/skill-management.ts +7 -1
  216. package/src/gjc-runtime/autoresearch-runtime.ts +10 -1
  217. package/src/gjc-runtime/launch-worktree.test.ts +248 -0
  218. package/src/gjc-runtime/launch-worktree.ts +275 -38
  219. package/src/gjc-runtime/repository-binding.ts +26 -1
  220. package/src/gjc-runtime/session-layout.ts +10 -0
  221. package/src/gjc-runtime/session-state-lock.ts +330 -23
  222. package/src/gjc-runtime/session-state-sidecar.ts +328 -80
  223. package/src/gjc-runtime/ultragoal-runtime.ts +10 -1
  224. package/src/harness-control-plane/session-lease.ts +2 -2
  225. package/src/internal-urls/docs-index.generated.ts +28 -26
  226. package/src/main.ts +150 -29
  227. package/src/memories/index.ts +24 -2
  228. package/src/modes/acp/acp-agent.ts +522 -68
  229. package/src/modes/components/agent-dashboard.ts +3 -1
  230. package/src/modes/components/model-selector.ts +28 -19
  231. package/src/modes/components/plugin-settings.ts +2 -1
  232. package/src/modes/controllers/selector-controller.ts +15 -9
  233. package/src/modes/interactive-mode.ts +21 -3
  234. package/src/prompts/system/system-prompt.md +5 -1
  235. package/src/prompts/tools/browser.md +1 -1
  236. package/src/prompts/tools/move-session.md +4 -0
  237. package/src/prompts/tools/read.md +2 -1
  238. package/src/prompts/tools/skill-discovery.md +6 -5
  239. package/src/prompts/tools/task.md +1 -0
  240. package/src/runtime-mcp/config-writer.ts +228 -0
  241. package/src/runtime-mcp/oauth-discovery.ts +114 -14
  242. package/src/runtime-mcp/policy-json-edit.ts +129 -0
  243. package/src/runtime-mcp/startup-policy.ts +14 -0
  244. package/src/runtime-mcp/types.ts +2 -2
  245. package/src/sdk/acp/adapter.ts +27 -12
  246. package/src/sdk/broker/broker.ts +273 -44
  247. package/src/sdk/broker/daemon-entry.ts +119 -0
  248. package/src/sdk/broker/discovery.ts +173 -20
  249. package/src/sdk/broker/doctor-restart.ts +206 -0
  250. package/src/sdk/broker/endpoint-authority.ts +36 -0
  251. package/src/sdk/broker/ensure.ts +49 -14
  252. package/src/sdk/broker/lifecycle.ts +174 -42
  253. package/src/sdk/broker/process-incarnation.ts +86 -2
  254. package/src/sdk/broker/runtime.ts +145 -21
  255. package/src/sdk/broker/session-index.ts +136 -22
  256. package/src/sdk/broker/spawn-substrate.ts +4 -1
  257. package/src/sdk/broker/transport.ts +5 -0
  258. package/src/sdk/broker/worktree-occupancy.ts +21 -1
  259. package/src/sdk/bus/chat-daemon-cli.ts +63 -0
  260. package/src/sdk/bus/chat-daemon-control.ts +260 -64
  261. package/src/sdk/bus/chat-daemon-runtime.ts +141 -25
  262. package/src/sdk/bus/daemon-startup-exclusion.ts +119 -0
  263. package/src/sdk/bus/doctor-daemon-restart-client.ts +291 -0
  264. package/src/sdk/bus/doctor-daemon-restart.ts +92 -0
  265. package/src/sdk/bus/index.ts +252 -51
  266. package/src/sdk/bus/kind-aware-reconciliation.ts +76 -41
  267. package/src/sdk/bus/notification-service.ts +21 -3
  268. package/src/sdk/bus/reconciliation-store.ts +17 -11
  269. package/src/sdk/bus/telegram-daemon-cli.ts +91 -5
  270. package/src/sdk/bus/telegram-daemon-contract.ts +1 -1
  271. package/src/sdk/bus/telegram-daemon-control.ts +47 -0
  272. package/src/sdk/bus/telegram-daemon-owner-registry.ts +1 -1
  273. package/src/sdk/bus/telegram-daemon.ts +178 -13
  274. package/src/sdk/cli/session-cli.ts +100 -12
  275. package/src/sdk/host/host.ts +2 -7
  276. package/src/sdk/host/session-runtime.test.ts +329 -8
  277. package/src/sdk/host/session-runtime.ts +202 -50
  278. package/src/sdk/host/surface-policy.ts +8 -1
  279. package/src/sdk/lifecycle/service.ts +199 -22
  280. package/src/sdk/mcp/server.ts +1 -1
  281. package/src/sdk/prompt-deadline-lease.ts +12 -4
  282. package/src/sdk/prompt-deadline-manager.ts +5 -7
  283. package/src/sdk/prompt-failure.ts +148 -0
  284. package/src/sdk/prompt-status.ts +30 -1
  285. package/src/sdk/prompt-watchdog.ts +11 -3
  286. package/src/sdk/protocol/operation-inventory.generated.json +18 -0
  287. package/src/sdk/protocol/operation-registry.ts +20 -8
  288. package/src/sdk/router/session-router.ts +8 -3
  289. package/src/sdk/service-artifact-paths.ts +26 -0
  290. package/src/sdk/session-authority.ts +10 -0
  291. package/src/sdk/session.ts +361 -194
  292. package/src/session/agent-session.ts +398 -93
  293. package/src/session/blob-store.ts +9 -9
  294. package/src/session/fallback-chain-controller.ts +3 -7
  295. package/src/session/internal/managed-session-scope.ts +445 -29
  296. package/src/session/internal/managed-session-storage.ts +38 -6
  297. package/src/session/primary-control-surface.ts +12 -0
  298. package/src/session/session-manager.ts +11 -3
  299. package/src/session/session-storage.ts +1 -1
  300. package/src/session-import/codex.ts +6 -1
  301. package/src/session-import/redact.ts +7 -0
  302. package/src/skill-state/workflow-mutation-guard.ts +645 -39
  303. package/src/slash-commands/builtin-registry.ts +7 -3
  304. package/src/system-prompt.ts +8 -0
  305. package/src/task/executor.ts +36 -3
  306. package/src/task/types.ts +27 -5
  307. package/src/tools/ask.ts +114 -34
  308. package/src/tools/browser/actions.ts +2 -2
  309. package/src/tools/browser/tab-worker.ts +11 -8
  310. package/src/tools/descriptors.ts +6 -1
  311. package/src/tools/fetch.ts +7 -2
  312. package/src/tools/image-gen.ts +33 -34
  313. package/src/tools/read.ts +78 -32
  314. package/src/tools/skill-discovery.ts +17 -7
  315. package/src/tools/tool-catalog.generated.ts +8 -8
  316. package/src/tools/ultragoal-ask-guard.ts +16 -26
  317. package/src/utils/changelog.ts +7 -1
  318. package/src/utils/git.ts +3 -1
  319. package/src/utils/markit.ts +24 -2
  320. package/src/utils/mupdf-wasm.ts +26 -0
  321. package/src/web/search/providers/openai-compatible.ts +191 -14
@@ -59,6 +59,7 @@ export {
59
59
  listCapabilities,
60
60
  // Loading API
61
61
  loadCapability,
62
+ loadCapabilityForHome,
62
63
  releaseSettingsScope,
63
64
  // Cache management
64
65
  reset,
@@ -11,10 +11,10 @@ import * as fsPromises from "node:fs/promises";
11
11
  import * as path from "node:path";
12
12
  import { logger, tryParseJson } from "@gajae-code/utils";
13
13
  import { registerProvider } from "../capability";
14
- import { readFile } from "../capability/fs";
14
+ import { type ReadFileOptions, readFile } from "../capability/fs";
15
15
  import { type MCPServer, mcpCapability } from "../capability/mcp";
16
16
  import type { LoadContext, LoadResult, SourceMeta } from "../capability/types";
17
- import { createSourceMeta, expandEnvVarsDeep } from "./helpers";
17
+ import { canonicalizePathWithinHome, createSourceMeta, expandEnvVarsDeep, getReadOptions } from "./helpers";
18
18
 
19
19
  const PROVIDER_ID = "mcp-json";
20
20
  const DISPLAY_NAME = "MCP Config";
@@ -440,18 +440,40 @@ async function readExactMCPConfigFile(filePath: string): Promise<string | null>
440
440
  export async function loadMCPJsonFile(
441
441
  filePath: string,
442
442
  level: "user" | "project",
443
- options?: { quiet?: boolean; useCache?: boolean },
443
+ options?: { quiet?: boolean; useCache?: boolean; readOptions?: ReadFileOptions },
444
444
  ): Promise<MCPJsonLoadResult> {
445
445
  const warnings: string[] = [];
446
446
  const items: MCPServer[] = [];
447
+ let sourcePath = filePath;
448
+ if (options?.readOptions?.isolatedHome) {
449
+ if (typeof options.readOptions.home !== "string") {
450
+ if (options.quiet) warnings.push("MCP configuration unavailable");
451
+ return { items, warnings, disabledServers: [] };
452
+ }
453
+ const authorizedPath = await canonicalizePathWithinHome(
454
+ {
455
+ home: options.readOptions.home,
456
+ isolatedHome: true,
457
+ userAgentDir: options.readOptions.userAgentDir,
458
+ },
459
+ filePath,
460
+ undefined,
461
+ options.readOptions.scope ?? "project",
462
+ );
463
+ if (!authorizedPath) {
464
+ if (options.quiet) warnings.push("MCP configuration unavailable");
465
+ return { items, warnings, disabledServers: [] };
466
+ }
467
+ sourcePath = authorizedPath;
468
+ }
447
469
 
448
470
  const content = options?.quiet
449
- ? await readExactMCPConfigFile(filePath)
471
+ ? options.readOptions?.isolatedHome
472
+ ? await readFile(sourcePath, { ...options.readOptions, bypassCache: true })
473
+ : await readExactMCPConfigFile(sourcePath)
450
474
  : options?.useCache === false
451
- ? await Bun.file(filePath)
452
- .text()
453
- .catch(() => null)
454
- : await readFile(filePath);
475
+ ? await readFile(filePath, { ...options.readOptions, bypassCache: true })
476
+ : await readFile(filePath, options?.readOptions);
455
477
  if (content === null) {
456
478
  if (options?.quiet) warnings.push("MCP configuration unavailable");
457
479
  return { items, warnings, disabledServers: [] };
@@ -474,7 +496,7 @@ export async function loadMCPJsonFile(
474
496
  } else {
475
497
  validConfig = config as MCPConfigFile;
476
498
  }
477
- const source = createSourceMeta(PROVIDER_ID, filePath, level);
499
+ const source = createSourceMeta(PROVIDER_ID, sourcePath, level);
478
500
  items.push(...transformMCPConfig(validConfig, source, options?.quiet));
479
501
 
480
502
  return {
@@ -490,7 +512,11 @@ export async function loadMCPJsonFile(
490
512
  async function load(ctx: LoadContext): Promise<LoadResult<MCPServer>> {
491
513
  const filenames = ["mcp.json", ".mcp.json"];
492
514
  const results = await Promise.all(
493
- filenames.map(filename => loadMCPJsonFile(path.join(ctx.cwd, filename), "project")),
515
+ filenames.map(filename =>
516
+ loadMCPJsonFile(path.join(ctx.cwd, filename), "project", {
517
+ readOptions: getReadOptions(ctx, "project"),
518
+ }),
519
+ ),
494
520
  );
495
521
 
496
522
  const allItems = results.flatMap(r => r.items);
@@ -32,9 +32,11 @@ import type { Settings as RuntimeSettings } from "../config/settings";
32
32
 
33
33
  import {
34
34
  buildExtensionModuleItems,
35
+ canonicalizePathWithinHome,
35
36
  createSourceMeta,
36
37
  discoverExtensionModulePaths,
37
38
  getProjectPath,
39
+ getReadOptions,
38
40
  getUserPath,
39
41
  loadFilesFromDir,
40
42
  scanSkillsFromDir,
@@ -53,9 +55,12 @@ async function loadContextFiles(ctx: LoadContext): Promise<LoadResult<ContextFil
53
55
  const warnings: string[] = [];
54
56
 
55
57
  // User-level only: ~/.config/opencode/AGENTS.md
56
- const userAgentsMd = getUserPath(ctx, "opencode", "AGENTS.md");
58
+ const rawUserAgentsMd = getUserPath(ctx, "opencode", "AGENTS.md");
59
+ const userAgentsMd = rawUserAgentsMd
60
+ ? await canonicalizePathWithinHome(ctx, rawUserAgentsMd, undefined, "user")
61
+ : null;
57
62
  if (userAgentsMd) {
58
- const content = await readFile(userAgentsMd);
63
+ const content = await readFile(userAgentsMd, getReadOptions(ctx, "user"));
59
64
  if (content) {
60
65
  items.push({
61
66
  path: userAgentsMd,
@@ -115,8 +120,10 @@ async function loadExtensionModules(ctx: LoadContext): Promise<LoadResult<Extens
115
120
  const projectPluginsDir = getProjectPath(ctx, "opencode", "plugins");
116
121
 
117
122
  const [userPaths, projectPaths] = await Promise.all([
118
- userPluginsDir ? discoverExtensionModulePaths(ctx, userPluginsDir) : Promise.resolve([]),
119
- projectPluginsDir ? discoverExtensionModulePaths(ctx, projectPluginsDir) : Promise.resolve([]),
123
+ userPluginsDir ? discoverExtensionModulePaths(ctx, userPluginsDir, { scope: "user" }) : Promise.resolve([]),
124
+ projectPluginsDir
125
+ ? discoverExtensionModulePaths(ctx, projectPluginsDir, { scope: "project" })
126
+ : Promise.resolve([]),
120
127
  ]);
121
128
 
122
129
  const items = buildExtensionModuleItems(PROVIDER_ID, userPaths, projectPaths);
@@ -201,9 +208,12 @@ async function loadSettings(ctx: LoadContext): Promise<LoadResult<SettingsCapabi
201
208
  const warnings: string[] = [];
202
209
 
203
210
  // User-level: ~/.config/opencode/opencode.json
204
- const userConfigPath = getUserPath(ctx, "opencode", "opencode.json");
211
+ const rawUserConfigPath = getUserPath(ctx, "opencode", "opencode.json");
212
+ const userConfigPath = rawUserConfigPath
213
+ ? await canonicalizePathWithinHome(ctx, rawUserConfigPath, undefined, "user")
214
+ : null;
205
215
  if (userConfigPath) {
206
- const content = await readFile(userConfigPath);
216
+ const content = await readFile(userConfigPath, getReadOptions(ctx, "user"));
207
217
  if (content) {
208
218
  const parsed = tryParseJson<Record<string, unknown>>(content);
209
219
  if (parsed) {
@@ -220,19 +230,26 @@ async function loadSettings(ctx: LoadContext): Promise<LoadResult<SettingsCapabi
220
230
  }
221
231
 
222
232
  // Project-level: opencode.json in project root
223
- const projectConfigPath = path.join(ctx.cwd, "opencode.json");
224
- const content = await readFile(projectConfigPath);
225
- if (content) {
226
- const parsed = tryParseJson<Record<string, unknown>>(content);
227
- if (parsed) {
228
- items.push({
229
- path: projectConfigPath,
230
- data: parsed,
231
- level: "project",
232
- _source: createSourceMeta(PROVIDER_ID, projectConfigPath, "project"),
233
- });
234
- } else {
235
- warnings.push(`Invalid JSON in ${projectConfigPath}`);
233
+ const projectConfigPath = await canonicalizePathWithinHome(
234
+ ctx,
235
+ path.join(ctx.cwd, "opencode.json"),
236
+ undefined,
237
+ "project",
238
+ );
239
+ if (projectConfigPath) {
240
+ const content = await readFile(projectConfigPath, getReadOptions(ctx, "project"));
241
+ if (content) {
242
+ const parsed = tryParseJson<Record<string, unknown>>(content);
243
+ if (parsed) {
244
+ items.push({
245
+ path: projectConfigPath,
246
+ data: parsed,
247
+ level: "project",
248
+ _source: createSourceMeta(PROVIDER_ID, projectConfigPath, "project"),
249
+ });
250
+ } else {
251
+ warnings.push(`Invalid JSON in ${projectConfigPath}`);
252
+ }
236
253
  }
237
254
  }
238
255
 
@@ -12,7 +12,7 @@ import { type SSHHost, sshCapability } from "../capability/ssh";
12
12
  import type { LoadContext, LoadResult, SourceMeta } from "../capability/types";
13
13
  import { validateSshDestination } from "../ssh/utils";
14
14
  import { expandTilde } from "../tools/path-utils";
15
- import { createSourceMeta, expandEnvVarsDeep } from "./helpers";
15
+ import { canonicalizePathWithinHome, createSourceMeta, expandEnvVarsDeep, getReadOptions } from "./helpers";
16
16
 
17
17
  const PROVIDER_ID = "ssh-json";
18
18
  const DISPLAY_NAME = "SSH Config";
@@ -102,29 +102,32 @@ async function loadSshJsonFile(
102
102
  ): Promise<LoadResult<SSHHost>> {
103
103
  const items: SSHHost[] = [];
104
104
  const warnings: string[] = [];
105
- const content = await readFile(filePath);
105
+ const scope = level === "user" ? "native" : "project";
106
+ const canonicalPath = await canonicalizePathWithinHome(ctx, filePath, undefined, scope);
107
+ if (!canonicalPath) return { items, warnings };
108
+ const content = await readFile(canonicalPath, getReadOptions(ctx, scope));
106
109
  if (content === null) {
107
110
  return { items, warnings };
108
111
  }
109
112
  const parsed = tryParseJson<SSHConfigFile>(content);
110
113
  if (!parsed) {
111
- warnings.push(`Failed to parse JSON in ${filePath}`);
114
+ warnings.push(`Failed to parse JSON in ${canonicalPath}`);
112
115
  return { items, warnings };
113
116
  }
114
117
  const config = expandEnvVarsDeep(parsed);
115
118
  if (!config.hosts || typeof config.hosts !== "object") {
116
- warnings.push(`Missing hosts in ${filePath}`);
119
+ warnings.push(`Missing hosts in ${canonicalPath}`);
117
120
  return { items, warnings };
118
121
  }
119
122
 
120
- const source = createSourceMeta(PROVIDER_ID, filePath, level);
123
+ const source = createSourceMeta(PROVIDER_ID, canonicalPath, level);
121
124
  for (const [name, rawHost] of Object.entries(config.hosts)) {
122
125
  if (!name.trim()) {
123
- warnings.push(`Invalid SSH host name in ${filePath}`);
126
+ warnings.push(`Invalid SSH host name in ${canonicalPath}`);
124
127
  continue;
125
128
  }
126
129
  if (!rawHost || typeof rawHost !== "object") {
127
- warnings.push(`Invalid host entry in ${filePath}: ${name}`);
130
+ warnings.push(`Invalid host entry in ${canonicalPath}: ${name}`);
128
131
  continue;
129
132
  }
130
133
  const host = normalizeHost(name, rawHost, source, ctx.home, warnings);
@@ -139,7 +142,7 @@ async function loadSshJsonFile(
139
142
  async function load(ctx: LoadContext): Promise<LoadResult<SSHHost>> {
140
143
  const candidateSources: Array<{ path: string; level: "user" | "project" }> = [
141
144
  { path: getSSHConfigPath("project", ctx.cwd), level: "project" },
142
- { path: getSSHConfigPath("user", ctx.cwd), level: "user" },
145
+ { path: getSSHConfigPath("user", ctx.cwd, ctx.userAgentDir), level: "user" },
143
146
  { path: path.join(ctx.cwd, "ssh.json"), level: "project" },
144
147
  { path: path.join(ctx.cwd, ".ssh.json"), level: "project" },
145
148
  ];
@@ -17,7 +17,15 @@ import { registerProvider } from "../capability";
17
17
  import { readFile } from "../capability/fs";
18
18
  import { type Rule, ruleCapability } from "../capability/rule";
19
19
  import type { LoadContext, LoadResult } from "../capability/types";
20
- import { buildRuleFromMarkdown, createSourceMeta, getProjectPath, getUserPath, loadFilesFromDir } from "./helpers";
20
+ import {
21
+ buildRuleFromMarkdown,
22
+ canonicalizePathWithinHome,
23
+ createSourceMeta,
24
+ getProjectPath,
25
+ getReadOptions,
26
+ getUserPath,
27
+ loadFilesFromDir,
28
+ } from "./helpers";
21
29
 
22
30
  const PROVIDER_ID = "windsurf";
23
31
  const DISPLAY_NAME = "Windsurf";
@@ -32,9 +40,10 @@ async function loadRules(ctx: LoadContext): Promise<LoadResult<Rule>> {
32
40
  const warnings: string[] = [];
33
41
 
34
42
  // User-level: ~/.codeium/windsurf/memories/global_rules.md
35
- const userPath = getUserPath(ctx, "windsurf", "memories/global_rules.md");
43
+ const rawUserPath = getUserPath(ctx, "windsurf", "memories/global_rules.md");
44
+ const userPath = rawUserPath ? await canonicalizePathWithinHome(ctx, rawUserPath, undefined, "user") : null;
36
45
  if (userPath) {
37
- const content = await readFile(userPath);
46
+ const content = await readFile(userPath, getReadOptions(ctx, "user"));
38
47
  if (content) {
39
48
  const source = createSourceMeta(PROVIDER_ID, userPath, "user");
40
49
  items.push(buildRuleFromMarkdown("global_rules.md", content, userPath, source, { ruleName: "global_rules" }));
@@ -533,6 +533,10 @@ export async function executeBash(command: string, options?: BashExecutorOptions
533
533
  !abortPromise
534
534
  ) {
535
535
  abortPromise = activeShell.abort();
536
+ // Cancellation owns this acknowledgement immediately: the run can settle
537
+ // before cleanup reaches it, and worker close can reject both promises.
538
+ // Keep the original promise for cleanup without an unobserved window.
539
+ void abortPromise.catch(() => undefined);
536
540
  }
537
541
  };
538
542
  const abortDeferred = Promise.withResolvers<"abort">();
@@ -549,9 +553,6 @@ export async function executeBash(command: string, options?: BashExecutorOptions
549
553
  ),
550
554
  Bun.sleep(CANCEL_CLEANUP_WAIT_MS).then(() => false),
551
555
  ]);
552
- if (abortPromise) {
553
- await Promise.race([abortPromise.catch(() => undefined), Bun.sleep(CANCEL_CLEANUP_WAIT_MS)]);
554
- }
555
556
  return settled;
556
557
  };
557
558
  if (userSignal) {
@@ -797,6 +798,12 @@ export async function executeBash(command: string, options?: BashExecutorOptions
797
798
  const disposePromise = oneShotShell.close();
798
799
  await Promise.race([disposePromise.catch(() => undefined), Bun.sleep(CANCEL_CLEANUP_WAIT_MS)]);
799
800
  }
801
+ // Also join cancellation requested after run settlement or on a path that
802
+ // skipped run cleanup. Close first so a pending acknowledgement cannot
803
+ // hold a one-shot worker open.
804
+ if (abortPromise) {
805
+ await Promise.race([abortPromise.catch(() => undefined), Bun.sleep(CANCEL_CLEANUP_WAIT_MS)]);
806
+ }
800
807
  }
801
808
  }
802
809
 
@@ -153,8 +153,16 @@ export class IsolatedShell {
153
153
  }
154
154
  this.#pending.set(id, pending);
155
155
  const { signal: _signal, ...workerOptions } = options;
156
+ try {
157
+ this.#send({ type: "run", token: this.#protocolToken, id, options: workerOptions });
158
+ } catch (error) {
159
+ // A failed send has no protocol response owner. Do not leave its
160
+ // unobserved deferred for close() to reject later.
161
+ this.#pending.delete(id);
162
+ pending.removeAbortListener?.();
163
+ throw error;
164
+ }
156
165
  if (options.signal instanceof AbortSignal) this.#dispatchedRunSignals.add(options.signal);
157
- this.#send({ type: "run", token: this.#protocolToken, id, options: workerOptions });
158
166
  return await deferred.promise;
159
167
  } finally {
160
168
  if (options.signal instanceof AbortSignal) this.#activeRunSignals.delete(options.signal);
@@ -193,7 +201,12 @@ export class IsolatedShell {
193
201
  const id = this.#nextId++;
194
202
  const deferred = Promise.withResolvers<void>();
195
203
  this.#pending.set(id, { kind: "void", resolve: deferred.resolve, reject: deferred.reject });
196
- this.#send({ type, token: this.#protocolToken, id });
204
+ try {
205
+ this.#send({ type, token: this.#protocolToken, id });
206
+ } catch (error) {
207
+ this.#pending.delete(id);
208
+ throw error;
209
+ }
197
210
  await deferred.promise;
198
211
  }
199
212
 
@@ -7,9 +7,10 @@ import * as path from "node:path";
7
7
  import type { ThinkingLevel } from "@gajae-code/agent-core";
8
8
  import type { ImageContent, Model, TextContent, Tool, UsageReport } from "@gajae-code/ai/core";
9
9
  import type { KeyId } from "@gajae-code/tui";
10
- import { hasFsCode, isEacces, isEnoent, logger } from "@gajae-code/utils";
10
+ import { hasFsCode, isEacces, isEnoent, logger, safeErrorDescription } from "@gajae-code/utils";
11
11
  import * as Zod from "zod/v4";
12
12
  import { type ExtensionModule, extensionModuleCapability } from "../../capability/extension-module";
13
+ import type { Settings } from "../../config/settings";
13
14
  import { loadCapability } from "../../discovery";
14
15
  import { getExtensionNameFromPath } from "../../discovery/helpers";
15
16
  import type { ExecOptions } from "../../exec/exec";
@@ -511,9 +512,9 @@ async function loadExtension(
511
512
  eventBus: EventBus,
512
513
  runtime: IExtensionRuntime,
513
514
  ): Promise<{ extension: Extension | null; error: string | null }> {
514
- const resolvedPath = resolvePath(extensionPath, cwd);
515
515
  let activation: ExtensionActivationScope | undefined;
516
516
  try {
517
+ const resolvedPath = resolvePath(extensionPath, cwd);
517
518
  const module = (await loadLegacyPiModule(resolvedPath)) as LoadedExtensionModule;
518
519
  const factory = getExtensionFactory(module);
519
520
 
@@ -540,8 +541,7 @@ async function loadExtension(
540
541
  return { extension, error: null };
541
542
  } catch (err) {
542
543
  activation?.rollback();
543
- const message = err instanceof Error ? err.message : String(err);
544
- return { extension: null, error: `Failed to load extension: ${message}` };
544
+ return { extension: null, error: `Failed to load extension: ${safeErrorDescription(err)}` };
545
545
  }
546
546
  }
547
547
 
@@ -588,7 +588,7 @@ export async function loadExtensions(paths: string[], cwd: string, eventBus?: Ev
588
588
  const { extension, error } = await loadExtension(extPath, cwd, resolvedEventBus, runtime);
589
589
 
590
590
  if (error) {
591
- errors.push({ path: extPath, error });
591
+ errors.push({ path: safeErrorDescription(extPath), error });
592
592
  continue;
593
593
  }
594
594
 
@@ -622,7 +622,7 @@ async function readExtensionManifest(packageJsonPath: string): Promise<Extension
622
622
  if (isEnoent(error) || isEacces(error) || hasFsCode(error, "EPERM")) {
623
623
  return null;
624
624
  }
625
- logger.warn("Failed to read extension manifest", { path: packageJsonPath, error: String(error) });
625
+ logger.warn("Failed to read extension manifest", { path: packageJsonPath, error: safeErrorDescription(error) });
626
626
  return null;
627
627
  }
628
628
  }
@@ -705,7 +705,7 @@ async function discoverExtensionsInDir(dir: string): Promise<string[]> {
705
705
  entries = await fs.readdir(dir, { withFileTypes: true });
706
706
  } catch (err) {
707
707
  if (isEnoent(err)) return [];
708
- logger.warn("Failed to discover extensions in directory", { path: dir, error: String(err) });
708
+ logger.warn("Failed to discover extensions in directory", { path: dir, error: safeErrorDescription(err) });
709
709
  return [];
710
710
  }
711
711
  entries.sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0));
@@ -729,6 +729,27 @@ async function discoverExtensionsInDir(dir: string): Promise<string[]> {
729
729
  return discovered;
730
730
  }
731
731
 
732
+ /**
733
+ * Session-scoped discovery inputs.
734
+ *
735
+ * A session's selected agent directory is not necessarily the process-global
736
+ * one (`createAgentSession({ agentDir })`), so user-scope extension modules and
737
+ * provider policy must resolve against the caller's profile rather than
738
+ * `getAgentDir()`.
739
+ */
740
+ export interface DiscoverExtensionsOptions {
741
+ /**
742
+ * Agent directory backing user-scope discovery. Default: `getAgentDir()`.
743
+ * Only the native capability provider is scoped this way; installed
744
+ * plugin-bundle extension paths stay home-scoped by design.
745
+ */
746
+ agentDir?: string;
747
+ /** Resolver-owned classification for `agentDir`. */
748
+ profileAuthority?: "default" | "custom";
749
+ /** Session settings whose provider policy applies to this load. */
750
+ settings?: Settings;
751
+ }
752
+
732
753
  /**
733
754
  * Discover and load extensions from standard locations.
734
755
  */
@@ -737,14 +758,21 @@ export async function discoverAndLoadExtensions(
737
758
  cwd: string,
738
759
  eventBus?: EventBus,
739
760
  disabledExtensionIds: string[] = [],
761
+ options: DiscoverExtensionsOptions = {},
740
762
  ): Promise<LoadExtensionsResult> {
741
763
  const allPaths: string[] = [];
742
764
  const seen = new Set<string>();
743
765
  const disabled = new Set(disabledExtensionIds);
766
+ // Environmental failures on a configured entry degrade to recorded errors instead
767
+ // of aborting the whole load, so one unreadable, unresolvable, or pathological
768
+ // entry — including a directory whose entries cannot be resolved — cannot drop
769
+ // every other extension the session would have loaded.
770
+ const discoveryErrors: Array<{ path: string; error: string }> = [];
744
771
 
745
772
  const isDisabledName = (name: string): boolean => disabled.has(`extension-module:${name}`);
746
773
 
747
774
  const addPath = (extPath: string): void => {
775
+ if (isDisabledName(getExtensionNameFromPath(extPath))) return;
748
776
  const resolved = path.resolve(extPath);
749
777
  if (!seen.has(resolved)) {
750
778
  seen.add(resolved);
@@ -753,44 +781,77 @@ export async function discoverAndLoadExtensions(
753
781
  };
754
782
 
755
783
  const addPaths = (paths: string[]) => {
756
- for (const extPath of paths) {
757
- if (isDisabledName(getExtensionNameFromPath(extPath))) continue;
758
- addPath(extPath);
759
- }
784
+ for (const extPath of paths) addPath(extPath);
760
785
  };
761
786
 
762
787
  // 1. Discover extension modules via capability API (native .gjc/.pi only)
763
- const discovered = await loadCapability<ExtensionModule>(extensionModuleCapability.id, { cwd });
788
+ const discovered = await loadCapability<ExtensionModule>(extensionModuleCapability.id, {
789
+ cwd,
790
+ ...(options.agentDir === undefined ? {} : { agentDir: options.agentDir }),
791
+ ...(options.profileAuthority === undefined ? {} : { profileAuthority: options.profileAuthority }),
792
+ ...(options.settings === undefined ? {} : { settings: options.settings }),
793
+ });
764
794
  for (const ext of discovered.items) {
765
795
  if (ext._source.provider !== "native") continue;
766
- if (isDisabledName(ext.name)) continue;
767
796
  addPath(ext.path);
768
797
  }
769
798
 
770
799
  // 2. Discover extension entry points from installed plugins
771
- addPaths(await getAllPluginExtensionPaths(cwd));
800
+ try {
801
+ addPaths(await getAllPluginExtensionPaths(cwd));
802
+ } catch (error) {
803
+ discoveryErrors.push({
804
+ path: "<plugin-registry>",
805
+ error: `Failed to read plugin extension paths: ${safeErrorDescription(error)}`,
806
+ });
807
+ logger.warn("Failed to read plugin extension paths", { error: safeErrorDescription(error) });
808
+ }
772
809
 
773
810
  // 3. Explicitly configured paths
774
811
  for (const configuredPath of configuredPaths) {
775
- const resolved = resolvePath(configuredPath, cwd);
776
-
812
+ let resolved: string;
813
+ try {
814
+ resolved = resolvePath(configuredPath, cwd);
815
+ } catch (error) {
816
+ discoveryErrors.push({
817
+ path: safeErrorDescription(configuredPath),
818
+ error: `Failed to resolve extension path: ${safeErrorDescription(error)}`,
819
+ });
820
+ continue;
821
+ }
777
822
  let stat: fs1.Stats | null = null;
778
823
  try {
779
824
  stat = await fs.stat(resolved);
780
825
  } catch (err) {
781
- if (!isEnoent(err)) throw err;
826
+ if (!isEnoent(err)) {
827
+ discoveryErrors.push({
828
+ path: resolved,
829
+ error: `Failed to inspect extension path: ${safeErrorDescription(err)}`,
830
+ });
831
+ continue;
832
+ }
782
833
  }
783
834
 
784
835
  if (stat?.isDirectory()) {
785
- const entries = await resolveExtensionEntries(resolved);
786
- if (entries) {
787
- addPaths(entries);
788
- continue;
789
- }
836
+ try {
837
+ const entries = await resolveExtensionEntries(resolved);
838
+ if (entries) {
839
+ addPaths(entries);
840
+ continue;
841
+ }
790
842
 
791
- const discovered = await discoverExtensionsInDir(resolved);
792
- if (discovered.length > 0) {
793
- addPaths(discovered);
843
+ const discovered = await discoverExtensionsInDir(resolved);
844
+ if (discovered.length > 0) {
845
+ addPaths(discovered);
846
+ }
847
+ } catch (error) {
848
+ // Directory entry resolution rethrows non-ENOENT/EACCES/EPERM faults
849
+ // (ELOOP, ENAMETOOLONG, EIO). Record the offending directory and keep the
850
+ // modules every other source contributes instead of aborting the pass.
851
+ discoveryErrors.push({
852
+ path: resolved,
853
+ error: `Failed to inspect extension entries: ${safeErrorDescription(error)}`,
854
+ });
794
855
  }
795
856
  continue;
796
857
  }
@@ -798,5 +859,6 @@ export async function discoverAndLoadExtensions(
798
859
  addPath(resolved);
799
860
  }
800
861
 
801
- return loadExtensions(allPaths, cwd, eventBus);
862
+ const result = await loadExtensions(allPaths, cwd, eventBus);
863
+ return discoveryErrors.length === 0 ? result : { ...result, errors: [...discoveryErrors, ...result.errors] };
802
864
  }