@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
@@ -5,12 +5,14 @@
5
5
  * This handles AGENTS.md files that live in project root (not in config directories
6
6
  * like .OpenAI code backend/ or .gemini/, which are handled by their respective providers).
7
7
  */
8
+ import type * as fsSync from "node:fs";
8
9
  import * as fs from "node:fs/promises";
9
10
  import * as path from "node:path";
10
11
  import { registerProvider } from "../capability";
11
12
  import { type ContextFile, contextFileCapability } from "../capability/context-file";
13
+ import type { FileIdentity } from "../capability/fs";
12
14
  import type { LoadContext, LoadResult } from "../capability/types";
13
- import { calculateDepth, createSourceMeta } from "./helpers";
15
+ import { calculateDepth, canonicalizePathWithinHome, createSourceMeta } from "./helpers";
14
16
 
15
17
  const PROVIDER_ID = "agents-md";
16
18
  const DISPLAY_NAME = "AGENTS.md";
@@ -25,17 +27,60 @@ const AGGREGATE_LIMIT_WARNING = "Skipped one or more AGENTS.md files that exceed
25
27
  export type AgentsMdReader = (
26
28
  filePath: string,
27
29
  maxBytes: number,
30
+ options?: { noFollow?: boolean; home?: string; homeIdentity?: FileIdentity },
28
31
  ) => Promise<{ content: string | null; byteLength: number; tooLarge: boolean }>;
29
32
 
33
+ function sameFileIdentity(left: fsSync.Stats, right: fsSync.Stats): boolean {
34
+ return left.dev === right.dev && left.ino === right.ino;
35
+ }
36
+
30
37
  async function readBoundedAgentsMdFile(
31
38
  filePath: string,
32
39
  maxBytes: number,
40
+ options?: { noFollow?: boolean; home?: string; homeIdentity?: FileIdentity },
33
41
  ): Promise<{ content: string | null; byteLength: number; tooLarge: boolean }> {
42
+ const noFollow = options?.noFollow === true;
43
+ const homeIsStable = async (): Promise<boolean> => {
44
+ if (!options?.home || !options.homeIdentity) return true;
45
+ try {
46
+ const current = await fs.stat(options.home);
47
+ return current.dev === options.homeIdentity.dev && current.ino === options.homeIdentity.ino;
48
+ } catch {
49
+ return false;
50
+ }
51
+ };
52
+ let before: fsSync.Stats | undefined;
53
+ let canonicalPath: string | undefined;
34
54
  try {
35
- const file = await fs.open(filePath, fs.constants.O_RDONLY | fs.constants.O_NONBLOCK);
55
+ if (!(await homeIsStable())) return { content: null, byteLength: 0, tooLarge: false };
56
+ if (noFollow) {
57
+ // `loadAgentsMd` canonicalizes the candidate before calling the reader,
58
+ // but that pathname can be swapped before open. Recheck the canonical
59
+ // target immediately before opening and bracket the descriptor with
60
+ // identity checks so explicit-home reads cannot follow a raced link.
61
+ canonicalPath = await fs.realpath(filePath);
62
+ if (canonicalPath !== path.resolve(filePath)) return { content: null, byteLength: 0, tooLarge: false };
63
+ before = await fs.lstat(filePath);
64
+ if (before.isSymbolicLink() || !before.isFile() || before.nlink !== 1)
65
+ return { content: null, byteLength: 0, tooLarge: false };
66
+ }
67
+ const flags =
68
+ fs.constants.O_RDONLY |
69
+ (typeof fs.constants.O_NONBLOCK === "number" ? fs.constants.O_NONBLOCK : 0) |
70
+ (noFollow && typeof fs.constants.O_NOFOLLOW === "number" ? fs.constants.O_NOFOLLOW : 0);
71
+ const file = await fs.open(filePath, flags);
36
72
  try {
37
73
  const stats = await file.stat();
38
74
  if (!stats.isFile()) return { content: null, byteLength: 0, tooLarge: false };
75
+ if (before && (!sameFileIdentity(before, stats) || stats.nlink !== 1))
76
+ return { content: null, byteLength: 0, tooLarge: false };
77
+ if (before) {
78
+ if (canonicalPath !== (await fs.realpath(filePath)))
79
+ return { content: null, byteLength: 0, tooLarge: false };
80
+ const after = await fs.lstat(filePath);
81
+ if (after.isSymbolicLink() || after.nlink !== 1 || !sameFileIdentity(before, after))
82
+ return { content: null, byteLength: 0, tooLarge: false };
83
+ }
39
84
  const bytes = Buffer.alloc(maxBytes + 1);
40
85
  let bytesRead = 0;
41
86
  while (bytesRead < bytes.length) {
@@ -44,6 +89,9 @@ async function readBoundedAgentsMdFile(
44
89
  bytesRead += result.bytesRead;
45
90
  }
46
91
  if (bytesRead > maxBytes) return { content: null, byteLength: bytesRead, tooLarge: true };
92
+ if (before && canonicalPath !== (await fs.realpath(filePath)))
93
+ return { content: null, byteLength: 0, tooLarge: false };
94
+ if (!(await homeIsStable())) return { content: null, byteLength: 0, tooLarge: false };
47
95
  return {
48
96
  content: new TextDecoder().decode(bytes.subarray(0, bytesRead)),
49
97
  byteLength: bytesRead,
@@ -71,6 +119,17 @@ export async function loadAgentsMd(
71
119
  let aggregateBytes = 0;
72
120
  let omittedOversizedFile = false;
73
121
  let omittedAggregateFile = false;
122
+ const homeRelative = path.relative(ctx.home, ctx.cwd);
123
+ const cwdIsWithinHome =
124
+ homeRelative === "" ||
125
+ (homeRelative !== ".." && !homeRelative.startsWith(`..${path.sep}`) && !path.isAbsolute(homeRelative));
126
+ const stopDirectory = ctx.repoRoot ?? (ctx.isolatedHome || cwdIsWithinHome ? ctx.home : path.parse(ctx.cwd).root);
127
+ const relativeStop = path.relative(stopDirectory, ctx.cwd);
128
+ const effectiveStopDirectory =
129
+ relativeStop === "" ||
130
+ (relativeStop !== ".." && !relativeStop.startsWith(`..${path.sep}`) && !path.isAbsolute(relativeStop))
131
+ ? stopDirectory
132
+ : ctx.cwd;
74
133
 
75
134
  while (true) {
76
135
  scannedDirectories += 1;
@@ -81,25 +140,32 @@ export async function loadAgentsMd(
81
140
  if (!baseName.startsWith(".")) {
82
141
  const remainingAggregateBytes = MAX_AGGREGATE_BYTES - aggregateBytes;
83
142
  const allowedBytes = Math.min(MAX_FILE_BYTES, remainingAggregateBytes);
84
- const result = await readCandidate(candidate, allowedBytes);
143
+ const authorizedCandidate = await canonicalizePathWithinHome(ctx, candidate, undefined, "project");
144
+ const result = authorizedCandidate
145
+ ? await readCandidate(authorizedCandidate, allowedBytes, {
146
+ noFollow: ctx.isolatedHome === true,
147
+ home: ctx.home,
148
+ homeIdentity: ctx.homeIdentity,
149
+ })
150
+ : { content: null, byteLength: 0, tooLarge: false };
85
151
  if (result.tooLarge) {
86
152
  if (allowedBytes < MAX_FILE_BYTES) omittedAggregateFile = true;
87
153
  else omittedOversizedFile = true;
88
154
  } else if (result.content !== null) {
89
- const fileDir = path.dirname(candidate);
155
+ const filePath = authorizedCandidate ?? candidate;
156
+ const fileDir = path.dirname(filePath);
90
157
  items.push({
91
- path: candidate,
158
+ path: filePath,
92
159
  content: result.content,
93
160
  level: "project",
94
161
  depth: calculateDepth(ctx.cwd, fileDir, path.sep),
95
- _source: createSourceMeta(PROVIDER_ID, candidate, "project"),
162
+ _source: createSourceMeta(PROVIDER_ID, filePath, "project"),
96
163
  });
97
164
  aggregateBytes += result.byteLength;
98
165
  }
99
166
  }
100
167
 
101
- const stopDirectory = ctx.repoRoot ?? ctx.home;
102
- if (current === stopDirectory) break;
168
+ if (current === effectiveStopDirectory) break;
103
169
  if (scannedDirectories === MAX_ANCESTOR_DIRECTORIES) {
104
170
  warnings.push(DIRECTORY_LIMIT_WARNING);
105
171
  break;
@@ -18,7 +18,9 @@ import type { LoadContext, LoadResult } from "../capability/types";
18
18
  import {
19
19
  buildRuleFromMarkdown,
20
20
  calculateDepth,
21
+ canonicalizePathWithinHome,
21
22
  createSourceMeta,
23
+ getReadOptions,
22
24
  loadFilesFromDir,
23
25
  scanSkillsFromDir,
24
26
  } from "./helpers";
@@ -46,13 +48,24 @@ function getUserPathCandidates(ctx: LoadContext, ...segments: string[]): string[
46
48
  export function getProjectPathCandidates(ctx: LoadContext, ...segments: string[]): string[] {
47
49
  const paths: string[] = [];
48
50
  let current = ctx.cwd;
51
+ const homeRelative = path.relative(ctx.home, ctx.cwd);
52
+ const cwdIsWithinHome =
53
+ homeRelative === "" ||
54
+ (homeRelative !== ".." && !homeRelative.startsWith(`..${path.sep}`) && !path.isAbsolute(homeRelative));
55
+ const stopDirectory = ctx.repoRoot ?? (ctx.isolatedHome || cwdIsWithinHome ? ctx.home : path.parse(ctx.cwd).root);
56
+ const relativeStop = path.relative(stopDirectory, ctx.cwd);
57
+ const effectiveStopDirectory =
58
+ relativeStop === "" ||
59
+ (relativeStop !== ".." && !relativeStop.startsWith(`..${path.sep}`) && !path.isAbsolute(relativeStop))
60
+ ? stopDirectory
61
+ : ctx.cwd;
49
62
  while (true) {
50
63
  if (current !== ctx.home) {
51
64
  for (const baseDir of AGENT_DIR_CANDIDATES) {
52
65
  paths.push(path.join(current, baseDir, ...segments));
53
66
  }
54
67
  }
55
- if (current === (ctx.repoRoot ?? ctx.home)) break;
68
+ if (current === effectiveStopDirectory) break;
56
69
  const parent = path.dirname(current);
57
70
  if (parent === current) break;
58
71
  current = parent;
@@ -181,12 +194,21 @@ registerProvider<SlashCommand>(slashCommandCapability.id, {
181
194
  // Context Files (AGENTS.md)
182
195
  async function loadContextFiles(ctx: LoadContext): Promise<LoadResult<ContextFile>> {
183
196
  const load = async (filePath: string, level: "user" | "project"): Promise<ContextFile | null> => {
184
- const content = await readFile(filePath);
197
+ const scope = level === "user" ? "user" : "project";
198
+ const canonicalPath = await canonicalizePathWithinHome(ctx, filePath, undefined, scope);
199
+ if (!canonicalPath) return null;
200
+ const content = await readFile(canonicalPath, getReadOptions(ctx, scope));
185
201
  if (!content) return null;
186
- // filePath is <ancestor>/.agent(s)/AGENTS.md — go up past the config dir to the ancestor
187
- const ancestorDir = path.dirname(path.dirname(filePath));
202
+ // canonicalPath is <ancestor>/.agent(s)/AGENTS.md — go up past the config dir to the ancestor
203
+ const ancestorDir = path.dirname(path.dirname(canonicalPath));
188
204
  const depth = level === "project" ? calculateDepth(ctx.cwd, ancestorDir, path.sep) : undefined;
189
- return { path: filePath, content, level, depth, _source: createSourceMeta(PROVIDER_ID, filePath, level) };
205
+ return {
206
+ path: canonicalPath,
207
+ content,
208
+ level,
209
+ depth,
210
+ _source: createSourceMeta(PROVIDER_ID, canonicalPath, level),
211
+ };
190
212
  };
191
213
 
192
214
  const results = await Promise.all([
@@ -208,9 +230,12 @@ registerProvider<ContextFile>(contextFileCapability.id, {
208
230
  // System Prompt (SYSTEM.md)
209
231
  async function loadSystemPrompt(ctx: LoadContext): Promise<LoadResult<SystemPrompt>> {
210
232
  const load = async (filePath: string, level: "user" | "project"): Promise<SystemPrompt | null> => {
211
- const content = await readFile(filePath);
233
+ const scope = level === "user" ? "user" : "project";
234
+ const canonicalPath = await canonicalizePathWithinHome(ctx, filePath, undefined, scope);
235
+ if (!canonicalPath) return null;
236
+ const content = await readFile(canonicalPath, getReadOptions(ctx, scope));
212
237
  if (!content) return null;
213
- return { path: filePath, content, level, _source: createSourceMeta(PROVIDER_ID, filePath, level) };
238
+ return { path: canonicalPath, content, level, _source: createSourceMeta(PROVIDER_ID, canonicalPath, level) };
214
239
  };
215
240
 
216
241
  const results = await Promise.all([