apex-code 0.0.1-alpha.9 → 0.0.4

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 (513) hide show
  1. package/CHANGELOG.md +150 -2
  2. package/README.md +9 -8
  3. package/dist/cli/args.d.ts +25 -1
  4. package/dist/cli/args.d.ts.map +1 -1
  5. package/dist/cli/args.js +43 -1
  6. package/dist/cli/args.js.map +1 -1
  7. package/dist/cli/mcp-command.d.ts +9 -0
  8. package/dist/cli/mcp-command.d.ts.map +1 -0
  9. package/dist/cli/mcp-command.js +53 -0
  10. package/dist/cli/mcp-command.js.map +1 -0
  11. package/dist/cli/project-trust.d.ts.map +1 -1
  12. package/dist/cli/project-trust.js +3 -1
  13. package/dist/cli/project-trust.js.map +1 -1
  14. package/dist/cli/startup-ui.d.ts.map +1 -1
  15. package/dist/cli/startup-ui.js +2 -1
  16. package/dist/cli/startup-ui.js.map +1 -1
  17. package/dist/cli.d.ts.map +1 -1
  18. package/dist/cli.js +94 -5
  19. package/dist/cli.js.map +1 -1
  20. package/dist/config.d.ts.map +1 -1
  21. package/dist/config.js +24 -5
  22. package/dist/config.js.map +1 -1
  23. package/dist/core/agent-session-services.d.ts +3 -0
  24. package/dist/core/agent-session-services.d.ts.map +1 -1
  25. package/dist/core/agent-session-services.js +1 -0
  26. package/dist/core/agent-session-services.js.map +1 -1
  27. package/dist/core/agent-session.d.ts +157 -8
  28. package/dist/core/agent-session.d.ts.map +1 -1
  29. package/dist/core/agent-session.js +565 -22
  30. package/dist/core/agent-session.js.map +1 -1
  31. package/dist/core/bash-executor.d.ts.map +1 -1
  32. package/dist/core/bash-executor.js +7 -0
  33. package/dist/core/bash-executor.js.map +1 -1
  34. package/dist/core/checkpoints/git-checkpoints.d.ts +39 -0
  35. package/dist/core/checkpoints/git-checkpoints.d.ts.map +1 -0
  36. package/dist/core/checkpoints/git-checkpoints.js +251 -0
  37. package/dist/core/checkpoints/git-checkpoints.js.map +1 -0
  38. package/dist/core/checkpoints/session-checkpoints.d.ts +23 -0
  39. package/dist/core/checkpoints/session-checkpoints.d.ts.map +1 -0
  40. package/dist/core/checkpoints/session-checkpoints.js +36 -0
  41. package/dist/core/checkpoints/session-checkpoints.js.map +1 -0
  42. package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
  43. package/dist/core/compaction/branch-summarization.js +4 -3
  44. package/dist/core/compaction/branch-summarization.js.map +1 -1
  45. package/dist/core/compaction/compaction.d.ts +5 -0
  46. package/dist/core/compaction/compaction.d.ts.map +1 -1
  47. package/dist/core/compaction/compaction.js +19 -5
  48. package/dist/core/compaction/compaction.js.map +1 -1
  49. package/dist/core/delegation/runtime.d.ts +6 -5
  50. package/dist/core/delegation/runtime.d.ts.map +1 -1
  51. package/dist/core/delegation/runtime.js +4 -3
  52. package/dist/core/delegation/runtime.js.map +1 -1
  53. package/dist/core/exec.d.ts.map +1 -1
  54. package/dist/core/exec.js +5 -2
  55. package/dist/core/exec.js.map +1 -1
  56. package/dist/core/extensions/index.d.ts +1 -1
  57. package/dist/core/extensions/index.d.ts.map +1 -1
  58. package/dist/core/extensions/index.js.map +1 -1
  59. package/dist/core/extensions/loader.d.ts.map +1 -1
  60. package/dist/core/extensions/loader.js +8 -2
  61. package/dist/core/extensions/loader.js.map +1 -1
  62. package/dist/core/extensions/runner.d.ts +5 -0
  63. package/dist/core/extensions/runner.d.ts.map +1 -1
  64. package/dist/core/extensions/runner.js +45 -1
  65. package/dist/core/extensions/runner.js.map +1 -1
  66. package/dist/core/extensions/source-runtime.d.ts +14 -0
  67. package/dist/core/extensions/source-runtime.d.ts.map +1 -0
  68. package/dist/core/extensions/source-runtime.js +56 -0
  69. package/dist/core/extensions/source-runtime.js.map +1 -0
  70. package/dist/core/extensions/types.d.ts +22 -2
  71. package/dist/core/extensions/types.d.ts.map +1 -1
  72. package/dist/core/extensions/types.js.map +1 -1
  73. package/dist/core/formatter-lifecycle.d.ts +69 -0
  74. package/dist/core/formatter-lifecycle.d.ts.map +1 -0
  75. package/dist/core/formatter-lifecycle.js +360 -0
  76. package/dist/core/formatter-lifecycle.js.map +1 -0
  77. package/dist/core/hooks/command-handler.d.ts +19 -0
  78. package/dist/core/hooks/command-handler.d.ts.map +1 -0
  79. package/dist/core/hooks/command-handler.js +112 -0
  80. package/dist/core/hooks/command-handler.js.map +1 -0
  81. package/dist/core/hooks/http-handler.d.ts +10 -0
  82. package/dist/core/hooks/http-handler.d.ts.map +1 -0
  83. package/dist/core/hooks/http-handler.js +36 -0
  84. package/dist/core/hooks/http-handler.js.map +1 -0
  85. package/dist/core/hooks/loader.d.ts +29 -0
  86. package/dist/core/hooks/loader.d.ts.map +1 -0
  87. package/dist/core/hooks/loader.js +139 -0
  88. package/dist/core/hooks/loader.js.map +1 -0
  89. package/dist/core/hooks/runtime.d.ts +13 -0
  90. package/dist/core/hooks/runtime.d.ts.map +1 -0
  91. package/dist/core/hooks/runtime.js +63 -0
  92. package/dist/core/hooks/runtime.js.map +1 -0
  93. package/dist/core/hooks/types.d.ts +83 -0
  94. package/dist/core/hooks/types.d.ts.map +1 -0
  95. package/dist/core/hooks/types.js +24 -0
  96. package/dist/core/hooks/types.js.map +1 -0
  97. package/dist/core/mcp/config.d.ts +16 -0
  98. package/dist/core/mcp/config.d.ts.map +1 -0
  99. package/dist/core/mcp/config.js +166 -0
  100. package/dist/core/mcp/config.js.map +1 -0
  101. package/dist/core/mcp/connector.d.ts +35 -0
  102. package/dist/core/mcp/connector.d.ts.map +1 -0
  103. package/dist/core/mcp/connector.js +105 -0
  104. package/dist/core/mcp/connector.js.map +1 -0
  105. package/dist/core/mcp/contract.d.ts +19 -0
  106. package/dist/core/mcp/contract.d.ts.map +1 -0
  107. package/dist/core/mcp/contract.js +89 -0
  108. package/dist/core/mcp/contract.js.map +1 -0
  109. package/dist/core/mcp/mcp-tool.d.ts +32 -0
  110. package/dist/core/mcp/mcp-tool.d.ts.map +1 -0
  111. package/dist/core/mcp/mcp-tool.js +171 -0
  112. package/dist/core/mcp/mcp-tool.js.map +1 -0
  113. package/dist/core/mcp/metadata-cache.d.ts +29 -0
  114. package/dist/core/mcp/metadata-cache.d.ts.map +1 -0
  115. package/dist/core/mcp/metadata-cache.js +99 -0
  116. package/dist/core/mcp/metadata-cache.js.map +1 -0
  117. package/dist/core/mcp/oauth/authorize.d.ts +21 -0
  118. package/dist/core/mcp/oauth/authorize.d.ts.map +1 -0
  119. package/dist/core/mcp/oauth/authorize.js +32 -0
  120. package/dist/core/mcp/oauth/authorize.js.map +1 -0
  121. package/dist/core/mcp/oauth/discover.d.ts +27 -0
  122. package/dist/core/mcp/oauth/discover.d.ts.map +1 -0
  123. package/dist/core/mcp/oauth/discover.js +116 -0
  124. package/dist/core/mcp/oauth/discover.js.map +1 -0
  125. package/dist/core/mcp/oauth/flow.d.ts +31 -0
  126. package/dist/core/mcp/oauth/flow.d.ts.map +1 -0
  127. package/dist/core/mcp/oauth/flow.js +175 -0
  128. package/dist/core/mcp/oauth/flow.js.map +1 -0
  129. package/dist/core/mcp/oauth/mcp-token.d.ts +51 -0
  130. package/dist/core/mcp/oauth/mcp-token.d.ts.map +1 -0
  131. package/dist/core/mcp/oauth/mcp-token.js +129 -0
  132. package/dist/core/mcp/oauth/mcp-token.js.map +1 -0
  133. package/dist/core/mcp/oauth/pkce.d.ts +12 -0
  134. package/dist/core/mcp/oauth/pkce.d.ts.map +1 -0
  135. package/dist/core/mcp/oauth/pkce.js +11 -0
  136. package/dist/core/mcp/oauth/pkce.js.map +1 -0
  137. package/dist/core/mcp/oauth/register.d.ts +16 -0
  138. package/dist/core/mcp/oauth/register.d.ts.map +1 -0
  139. package/dist/core/mcp/oauth/register.js +42 -0
  140. package/dist/core/mcp/oauth/register.js.map +1 -0
  141. package/dist/core/mcp/oauth/token-client.d.ts +33 -0
  142. package/dist/core/mcp/oauth/token-client.d.ts.map +1 -0
  143. package/dist/core/mcp/oauth/token-client.js +65 -0
  144. package/dist/core/mcp/oauth/token-client.js.map +1 -0
  145. package/dist/core/mcp/runtime.d.ts +20 -0
  146. package/dist/core/mcp/runtime.d.ts.map +1 -0
  147. package/dist/core/mcp/runtime.js +37 -0
  148. package/dist/core/mcp/runtime.js.map +1 -0
  149. package/dist/core/mcp/schema.d.ts +22 -0
  150. package/dist/core/mcp/schema.d.ts.map +1 -0
  151. package/dist/core/mcp/schema.js +20 -0
  152. package/dist/core/mcp/schema.js.map +1 -0
  153. package/dist/core/mcp/server-manager.d.ts +43 -0
  154. package/dist/core/mcp/server-manager.d.ts.map +1 -0
  155. package/dist/core/mcp/server-manager.js +142 -0
  156. package/dist/core/mcp/server-manager.js.map +1 -0
  157. package/dist/core/mcp/types.d.ts +100 -0
  158. package/dist/core/mcp/types.d.ts.map +1 -0
  159. package/dist/core/mcp/types.js +8 -0
  160. package/dist/core/mcp/types.js.map +1 -0
  161. package/dist/core/permissions/gate.d.ts.map +1 -1
  162. package/dist/core/permissions/gate.js +45 -4
  163. package/dist/core/permissions/gate.js.map +1 -1
  164. package/dist/core/permissions/operations.d.ts +20 -0
  165. package/dist/core/permissions/operations.d.ts.map +1 -0
  166. package/dist/core/permissions/operations.js +9 -0
  167. package/dist/core/permissions/operations.js.map +1 -0
  168. package/dist/core/permissions/policy-command.d.ts +42 -0
  169. package/dist/core/permissions/policy-command.d.ts.map +1 -0
  170. package/dist/core/permissions/policy-command.js +44 -0
  171. package/dist/core/permissions/policy-command.js.map +1 -0
  172. package/dist/core/permissions/responder.d.ts +49 -2
  173. package/dist/core/permissions/responder.d.ts.map +1 -1
  174. package/dist/core/permissions/responder.js +20 -8
  175. package/dist/core/permissions/responder.js.map +1 -1
  176. package/dist/core/permissions/rules.d.ts.map +1 -1
  177. package/dist/core/permissions/rules.js +11 -1
  178. package/dist/core/permissions/rules.js.map +1 -1
  179. package/dist/core/permissions/store.d.ts +18 -0
  180. package/dist/core/permissions/store.d.ts.map +1 -1
  181. package/dist/core/permissions/store.js +89 -8
  182. package/dist/core/permissions/store.js.map +1 -1
  183. package/dist/core/policy-executor.d.ts +47 -0
  184. package/dist/core/policy-executor.d.ts.map +1 -0
  185. package/dist/core/policy-executor.js +228 -0
  186. package/dist/core/policy-executor.js.map +1 -0
  187. package/dist/core/policy-loader.d.ts +72 -0
  188. package/dist/core/policy-loader.d.ts.map +1 -0
  189. package/dist/core/policy-loader.js +195 -0
  190. package/dist/core/policy-loader.js.map +1 -0
  191. package/dist/core/project-trust.d.ts +1 -1
  192. package/dist/core/project-trust.d.ts.map +1 -1
  193. package/dist/core/project-trust.js.map +1 -1
  194. package/dist/core/sandbox/bwrap-arguments.d.ts +41 -0
  195. package/dist/core/sandbox/bwrap-arguments.d.ts.map +1 -0
  196. package/dist/core/sandbox/bwrap-arguments.js +136 -0
  197. package/dist/core/sandbox/bwrap-arguments.js.map +1 -0
  198. package/dist/core/sandbox/child-entry.d.ts.map +1 -1
  199. package/dist/core/sandbox/child-entry.js +3 -1
  200. package/dist/core/sandbox/child-entry.js.map +1 -1
  201. package/dist/core/sandbox/cli-launch.d.ts +84 -4
  202. package/dist/core/sandbox/cli-launch.d.ts.map +1 -1
  203. package/dist/core/sandbox/cli-launch.js +115 -16
  204. package/dist/core/sandbox/cli-launch.js.map +1 -1
  205. package/dist/core/sandbox/cli-supervisor.d.ts +2 -0
  206. package/dist/core/sandbox/cli-supervisor.d.ts.map +1 -1
  207. package/dist/core/sandbox/cli-supervisor.js +50 -2
  208. package/dist/core/sandbox/cli-supervisor.js.map +1 -1
  209. package/dist/core/sandbox/full-access.d.ts +28 -0
  210. package/dist/core/sandbox/full-access.d.ts.map +1 -0
  211. package/dist/core/sandbox/full-access.js +63 -0
  212. package/dist/core/sandbox/full-access.js.map +1 -0
  213. package/dist/core/sandbox/git-identity.d.ts +57 -0
  214. package/dist/core/sandbox/git-identity.d.ts.map +1 -0
  215. package/dist/core/sandbox/git-identity.js +78 -0
  216. package/dist/core/sandbox/git-identity.js.map +1 -0
  217. package/dist/core/sandbox/host-approval.d.ts +42 -0
  218. package/dist/core/sandbox/host-approval.d.ts.map +1 -0
  219. package/dist/core/sandbox/host-approval.js +99 -0
  220. package/dist/core/sandbox/host-approval.js.map +1 -0
  221. package/dist/core/sandbox/linux-backend.d.ts +14 -0
  222. package/dist/core/sandbox/linux-backend.d.ts.map +1 -1
  223. package/dist/core/sandbox/linux-backend.js +158 -80
  224. package/dist/core/sandbox/linux-backend.js.map +1 -1
  225. package/dist/core/sandbox/macos-backend.d.ts +5 -0
  226. package/dist/core/sandbox/macos-backend.d.ts.map +1 -1
  227. package/dist/core/sandbox/macos-backend.js +164 -5
  228. package/dist/core/sandbox/macos-backend.js.map +1 -1
  229. package/dist/core/sandbox/network-proxy.d.ts +19 -0
  230. package/dist/core/sandbox/network-proxy.d.ts.map +1 -1
  231. package/dist/core/sandbox/network-proxy.js +93 -19
  232. package/dist/core/sandbox/network-proxy.js.map +1 -1
  233. package/dist/core/sandbox/policy.d.ts +8 -0
  234. package/dist/core/sandbox/policy.d.ts.map +1 -1
  235. package/dist/core/sandbox/policy.js +29 -12
  236. package/dist/core/sandbox/policy.js.map +1 -1
  237. package/dist/core/sandbox/profiles.d.ts +25 -0
  238. package/dist/core/sandbox/profiles.d.ts.map +1 -0
  239. package/dist/core/sandbox/profiles.js +23 -0
  240. package/dist/core/sandbox/profiles.js.map +1 -0
  241. package/dist/core/sandbox/rpc/command-client.d.ts +28 -0
  242. package/dist/core/sandbox/rpc/command-client.d.ts.map +1 -0
  243. package/dist/core/sandbox/rpc/command-client.js +86 -0
  244. package/dist/core/sandbox/rpc/command-client.js.map +1 -0
  245. package/dist/core/sandbox/rpc/command-proxy.d.ts +48 -0
  246. package/dist/core/sandbox/rpc/command-proxy.d.ts.map +1 -0
  247. package/dist/core/sandbox/rpc/command-proxy.js +120 -0
  248. package/dist/core/sandbox/rpc/command-proxy.js.map +1 -0
  249. package/dist/core/sandbox/rpc/credential-proxy.d.ts.map +1 -1
  250. package/dist/core/sandbox/rpc/credential-proxy.js +2 -96
  251. package/dist/core/sandbox/rpc/credential-proxy.js.map +1 -1
  252. package/dist/core/sandbox/rpc/framing.d.ts +35 -0
  253. package/dist/core/sandbox/rpc/framing.d.ts.map +1 -0
  254. package/dist/core/sandbox/rpc/framing.js +114 -0
  255. package/dist/core/sandbox/rpc/framing.js.map +1 -0
  256. package/dist/core/sandbox/rpc/git-credential-helper.d.ts +47 -0
  257. package/dist/core/sandbox/rpc/git-credential-helper.d.ts.map +1 -0
  258. package/dist/core/sandbox/rpc/git-credential-helper.js +222 -0
  259. package/dist/core/sandbox/rpc/git-credential-helper.js.map +1 -0
  260. package/dist/core/sandbox/rpc/git-credential-proxy.d.ts +66 -0
  261. package/dist/core/sandbox/rpc/git-credential-proxy.d.ts.map +1 -0
  262. package/dist/core/sandbox/rpc/git-credential-proxy.js +209 -0
  263. package/dist/core/sandbox/rpc/git-credential-proxy.js.map +1 -0
  264. package/dist/core/sandbox/supervisor-temp.d.ts +18 -0
  265. package/dist/core/sandbox/supervisor-temp.d.ts.map +1 -0
  266. package/dist/core/sandbox/supervisor-temp.js +21 -0
  267. package/dist/core/sandbox/supervisor-temp.js.map +1 -0
  268. package/dist/core/sandbox/supervisor.d.ts +12 -1
  269. package/dist/core/sandbox/supervisor.d.ts.map +1 -1
  270. package/dist/core/sandbox/supervisor.js +15 -1
  271. package/dist/core/sandbox/supervisor.js.map +1 -1
  272. package/dist/core/sandbox/terminal-handoff.d.ts +68 -0
  273. package/dist/core/sandbox/terminal-handoff.d.ts.map +1 -0
  274. package/dist/core/sandbox/terminal-handoff.js +221 -0
  275. package/dist/core/sandbox/terminal-handoff.js.map +1 -0
  276. package/dist/core/sandbox/terminal-size.d.ts +13 -0
  277. package/dist/core/sandbox/terminal-size.d.ts.map +1 -1
  278. package/dist/core/sandbox/terminal-size.js +23 -2
  279. package/dist/core/sandbox/terminal-size.js.map +1 -1
  280. package/dist/core/sdk.d.ts +22 -0
  281. package/dist/core/sdk.d.ts.map +1 -1
  282. package/dist/core/sdk.js +53 -1
  283. package/dist/core/sdk.js.map +1 -1
  284. package/dist/core/session-manager.d.ts +13 -1
  285. package/dist/core/session-manager.d.ts.map +1 -1
  286. package/dist/core/session-manager.js +32 -5
  287. package/dist/core/session-manager.js.map +1 -1
  288. package/dist/core/settings-manager.d.ts +163 -1
  289. package/dist/core/settings-manager.d.ts.map +1 -1
  290. package/dist/core/settings-manager.js +107 -0
  291. package/dist/core/settings-manager.js.map +1 -1
  292. package/dist/core/slash-commands.d.ts.map +1 -1
  293. package/dist/core/slash-commands.js +2 -0
  294. package/dist/core/slash-commands.js.map +1 -1
  295. package/dist/core/tools/background-shell.d.ts +39 -0
  296. package/dist/core/tools/background-shell.d.ts.map +1 -0
  297. package/dist/core/tools/background-shell.js +81 -0
  298. package/dist/core/tools/background-shell.js.map +1 -0
  299. package/dist/core/tools/bash-command-segments.d.ts.map +1 -1
  300. package/dist/core/tools/bash-command-segments.js +2 -0
  301. package/dist/core/tools/bash-command-segments.js.map +1 -1
  302. package/dist/core/tools/bash.d.ts +30 -15
  303. package/dist/core/tools/bash.d.ts.map +1 -1
  304. package/dist/core/tools/bash.js +228 -12
  305. package/dist/core/tools/bash.js.map +1 -1
  306. package/dist/core/tools/contract-snapshot.d.ts +58 -0
  307. package/dist/core/tools/contract-snapshot.d.ts.map +1 -0
  308. package/dist/core/tools/contract-snapshot.js +51 -0
  309. package/dist/core/tools/contract-snapshot.js.map +1 -0
  310. package/dist/core/tools/contract.d.ts +53 -5
  311. package/dist/core/tools/contract.d.ts.map +1 -1
  312. package/dist/core/tools/contract.js +22 -2
  313. package/dist/core/tools/contract.js.map +1 -1
  314. package/dist/core/tools/delegate.d.ts +6 -4
  315. package/dist/core/tools/delegate.d.ts.map +1 -1
  316. package/dist/core/tools/delegate.js +21 -11
  317. package/dist/core/tools/delegate.js.map +1 -1
  318. package/dist/core/tools/edit-diff.d.ts +48 -0
  319. package/dist/core/tools/edit-diff.d.ts.map +1 -1
  320. package/dist/core/tools/edit-diff.js +215 -3
  321. package/dist/core/tools/edit-diff.js.map +1 -1
  322. package/dist/core/tools/edit.d.ts +8 -0
  323. package/dist/core/tools/edit.d.ts.map +1 -1
  324. package/dist/core/tools/edit.js +53 -7
  325. package/dist/core/tools/edit.js.map +1 -1
  326. package/dist/core/tools/find.d.ts.map +1 -1
  327. package/dist/core/tools/find.js +5 -2
  328. package/dist/core/tools/find.js.map +1 -1
  329. package/dist/core/tools/grep.d.ts.map +1 -1
  330. package/dist/core/tools/grep.js +5 -2
  331. package/dist/core/tools/grep.js.map +1 -1
  332. package/dist/core/tools/index.d.ts +10 -1
  333. package/dist/core/tools/index.d.ts.map +1 -1
  334. package/dist/core/tools/index.js +3 -0
  335. package/dist/core/tools/index.js.map +1 -1
  336. package/dist/core/tools/ls.d.ts.map +1 -1
  337. package/dist/core/tools/ls.js +5 -2
  338. package/dist/core/tools/ls.js.map +1 -1
  339. package/dist/core/tools/lsp.d.ts +19 -1
  340. package/dist/core/tools/lsp.d.ts.map +1 -1
  341. package/dist/core/tools/lsp.js +163 -10
  342. package/dist/core/tools/lsp.js.map +1 -1
  343. package/dist/core/tools/path-permission.d.ts +3 -0
  344. package/dist/core/tools/path-permission.d.ts.map +1 -1
  345. package/dist/core/tools/path-permission.js +16 -4
  346. package/dist/core/tools/path-permission.js.map +1 -1
  347. package/dist/core/tools/path-utils.d.ts +27 -2
  348. package/dist/core/tools/path-utils.d.ts.map +1 -1
  349. package/dist/core/tools/path-utils.js +221 -4
  350. package/dist/core/tools/path-utils.js.map +1 -1
  351. package/dist/core/tools/read.d.ts.map +1 -1
  352. package/dist/core/tools/read.js +20 -11
  353. package/dist/core/tools/read.js.map +1 -1
  354. package/dist/core/tools/test.d.ts +54 -9
  355. package/dist/core/tools/test.d.ts.map +1 -1
  356. package/dist/core/tools/test.js +239 -19
  357. package/dist/core/tools/test.js.map +1 -1
  358. package/dist/core/tools/write.d.ts +7 -0
  359. package/dist/core/tools/write.d.ts.map +1 -1
  360. package/dist/core/tools/write.js +64 -9
  361. package/dist/core/tools/write.js.map +1 -1
  362. package/dist/core/verification-lifecycle.d.ts +77 -0
  363. package/dist/core/verification-lifecycle.d.ts.map +1 -0
  364. package/dist/core/verification-lifecycle.js +146 -0
  365. package/dist/core/verification-lifecycle.js.map +1 -0
  366. package/dist/core/workspace/artifacts.d.ts +45 -0
  367. package/dist/core/workspace/artifacts.d.ts.map +1 -0
  368. package/dist/core/workspace/artifacts.js +165 -0
  369. package/dist/core/workspace/artifacts.js.map +1 -0
  370. package/dist/core/workspace/comparison.d.ts +27 -0
  371. package/dist/core/workspace/comparison.d.ts.map +1 -0
  372. package/dist/core/workspace/comparison.js +94 -0
  373. package/dist/core/workspace/comparison.js.map +1 -0
  374. package/dist/core/workspace/git-observer.d.ts +48 -0
  375. package/dist/core/workspace/git-observer.d.ts.map +1 -0
  376. package/dist/core/workspace/git-observer.js +346 -0
  377. package/dist/core/workspace/git-observer.js.map +1 -0
  378. package/dist/core/workspace/projection.d.ts +3 -0
  379. package/dist/core/workspace/projection.d.ts.map +1 -0
  380. package/dist/core/workspace/projection.js +56 -0
  381. package/dist/core/workspace/projection.js.map +1 -0
  382. package/dist/core/workspace/state.d.ts +133 -0
  383. package/dist/core/workspace/state.d.ts.map +1 -0
  384. package/dist/core/workspace/state.js +96 -0
  385. package/dist/core/workspace/state.js.map +1 -0
  386. package/dist/extensions/llama/client.d.ts +6 -0
  387. package/dist/extensions/llama/client.d.ts.map +1 -1
  388. package/dist/extensions/llama/client.js +7 -0
  389. package/dist/extensions/llama/client.js.map +1 -1
  390. package/dist/extensions/llama/provider.d.ts +3 -1
  391. package/dist/extensions/llama/provider.d.ts.map +1 -1
  392. package/dist/extensions/llama/provider.js +27 -6
  393. package/dist/extensions/llama/provider.js.map +1 -1
  394. package/dist/index.d.ts +4 -1
  395. package/dist/index.d.ts.map +1 -1
  396. package/dist/index.js +3 -0
  397. package/dist/index.js.map +1 -1
  398. package/dist/main.d.ts +3 -1
  399. package/dist/main.d.ts.map +1 -1
  400. package/dist/main.js +41 -9
  401. package/dist/main.js.map +1 -1
  402. package/dist/modes/acp/server.d.ts +55 -0
  403. package/dist/modes/acp/server.d.ts.map +1 -0
  404. package/dist/modes/acp/server.js +244 -0
  405. package/dist/modes/acp/server.js.map +1 -0
  406. package/dist/modes/acp/translate.d.ts +37 -0
  407. package/dist/modes/acp/translate.d.ts.map +1 -0
  408. package/dist/modes/acp/translate.js +50 -0
  409. package/dist/modes/acp/translate.js.map +1 -0
  410. package/dist/modes/index.d.ts +1 -0
  411. package/dist/modes/index.d.ts.map +1 -1
  412. package/dist/modes/index.js +1 -0
  413. package/dist/modes/index.js.map +1 -1
  414. package/dist/modes/interactive/components/assistant-message.d.ts +29 -0
  415. package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  416. package/dist/modes/interactive/components/assistant-message.js +182 -23
  417. package/dist/modes/interactive/components/assistant-message.js.map +1 -1
  418. package/dist/modes/interactive/components/bash-execution.d.ts +7 -0
  419. package/dist/modes/interactive/components/bash-execution.d.ts.map +1 -1
  420. package/dist/modes/interactive/components/bash-execution.js +20 -0
  421. package/dist/modes/interactive/components/bash-execution.js.map +1 -1
  422. package/dist/modes/interactive/components/custom-editor.d.ts +9 -4
  423. package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -1
  424. package/dist/modes/interactive/components/custom-editor.js +20 -9
  425. package/dist/modes/interactive/components/custom-editor.js.map +1 -1
  426. package/dist/modes/interactive/components/extension-selector.d.ts +7 -1
  427. package/dist/modes/interactive/components/extension-selector.d.ts.map +1 -1
  428. package/dist/modes/interactive/components/extension-selector.js +7 -3
  429. package/dist/modes/interactive/components/extension-selector.js.map +1 -1
  430. package/dist/modes/interactive/components/footer.d.ts +19 -0
  431. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  432. package/dist/modes/interactive/components/footer.js +70 -10
  433. package/dist/modes/interactive/components/footer.js.map +1 -1
  434. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  435. package/dist/modes/interactive/components/model-selector.js +6 -6
  436. package/dist/modes/interactive/components/model-selector.js.map +1 -1
  437. package/dist/modes/interactive/components/permission-preview.d.ts +15 -0
  438. package/dist/modes/interactive/components/permission-preview.d.ts.map +1 -0
  439. package/dist/modes/interactive/components/permission-preview.js +33 -0
  440. package/dist/modes/interactive/components/permission-preview.js.map +1 -0
  441. package/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
  442. package/dist/modes/interactive/components/session-selector.js +2 -2
  443. package/dist/modes/interactive/components/session-selector.js.map +1 -1
  444. package/dist/modes/interactive/components/settings-selector.d.ts +2 -0
  445. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  446. package/dist/modes/interactive/components/settings-selector.js +10 -0
  447. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  448. package/dist/modes/interactive/components/tool-execution.d.ts +23 -1
  449. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  450. package/dist/modes/interactive/components/tool-execution.js +64 -10
  451. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  452. package/dist/modes/interactive/interactive-mode.d.ts +34 -0
  453. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  454. package/dist/modes/interactive/interactive-mode.js +231 -26
  455. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  456. package/dist/modes/interactive/theme/theme.d.ts +12 -0
  457. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  458. package/dist/modes/interactive/theme/theme.js +15 -5
  459. package/dist/modes/interactive/theme/theme.js.map +1 -1
  460. package/dist/modes/print-mode.d.ts.map +1 -1
  461. package/dist/modes/print-mode.js +27 -12
  462. package/dist/modes/print-mode.js.map +1 -1
  463. package/dist/modes/rpc/rpc-client.d.ts +7 -0
  464. package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  465. package/dist/modes/rpc/rpc-client.js +7 -0
  466. package/dist/modes/rpc/rpc-client.js.map +1 -1
  467. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  468. package/dist/modes/rpc/rpc-mode.js +3 -0
  469. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  470. package/dist/modes/rpc/rpc-types.d.ts +12 -0
  471. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  472. package/dist/modes/rpc/rpc-types.js.map +1 -1
  473. package/dist/package-manager-cli.d.ts.map +1 -1
  474. package/dist/package-manager-cli.js +0 -6
  475. package/dist/package-manager-cli.js.map +1 -1
  476. package/dist/utils/shell.d.ts.map +1 -1
  477. package/dist/utils/shell.js +6 -4
  478. package/dist/utils/shell.js.map +1 -1
  479. package/dist/utils/version-check.d.ts.map +1 -1
  480. package/dist/utils/version-check.js +6 -4
  481. package/dist/utils/version-check.js.map +1 -1
  482. package/docs/compaction.md +4 -0
  483. package/docs/custom-provider.md +7 -7
  484. package/docs/environment-variables.md +1 -1
  485. package/docs/extensions.md +18 -0
  486. package/docs/keybindings.md +1 -1
  487. package/docs/quickstart.md +1 -1
  488. package/docs/rpc.md +23 -0
  489. package/docs/sdk.md +25 -0
  490. package/docs/settings.md +12 -4
  491. package/docs/terminal-setup.md +12 -0
  492. package/docs/usage.md +4 -3
  493. package/examples/extensions/built-in-tool-renderer.ts +8 -4
  494. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  495. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  496. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  497. package/examples/extensions/git-checkpoint.ts +24 -36
  498. package/examples/extensions/gondolin/package-lock.json +2 -2
  499. package/examples/extensions/gondolin/package.json +1 -1
  500. package/examples/extensions/minimal-mode.ts +2 -2
  501. package/examples/extensions/permission-gate.ts +1 -1
  502. package/examples/extensions/plan-mode/index.ts +1 -1
  503. package/examples/extensions/rpc-demo.ts +1 -1
  504. package/examples/extensions/sandbox/package-lock.json +2 -2
  505. package/examples/extensions/sandbox/package.json +1 -1
  506. package/examples/extensions/with-deps/package-lock.json +2 -2
  507. package/examples/extensions/with-deps/package.json +1 -1
  508. package/npm-shrinkwrap.json +194 -46
  509. package/package.json +14 -12
  510. package/dist/server/create-harness.d.ts +0 -26
  511. package/dist/server/create-harness.d.ts.map +0 -1
  512. package/dist/server/create-harness.js +0 -107
  513. package/dist/server/create-harness.js.map +0 -1
@@ -0,0 +1,228 @@
1
+ /**
2
+ * VF.3: the bounded argv command executor (spec
3
+ * 2026-09-01-configured-verification-and-formatting.md § 2). Runs a
4
+ * `CommandPolicy` from the VF.2 loader verbatim: executable + argv spawn
5
+ * with no shell, under the policy's numeric bounds, with process-tree
6
+ * termination on timeout and cancellation. Outcomes are structured values;
7
+ * a command that merely fails is data, never a throw.
8
+ *
9
+ * Trusting the loader: this module does NOT re-validate policy fields the
10
+ * loader already guarantees (numeric bounds, shell:false, argv shapes). The
11
+ * one defensive re-check is the cwd confinement, because a resolved path is
12
+ * cheap to verify and the worst failure here is running outside the
13
+ * workspace.
14
+ */
15
+ import { spawn } from "node:child_process";
16
+ import { isAbsolute, relative, resolve } from "node:path";
17
+ import { killProcessTree, sanitizeBinaryOutput } from "../utils/shell.js";
18
+ /** Hard retention ceiling for artifact bytes, independent of policy caps. */
19
+ const MAX_RETENTION_BYTES = 8 * 1024 * 1024;
20
+ function newStream() {
21
+ return { chunks: [], bytes: 0, window: [], truncated: false };
22
+ }
23
+ function capture(stream, chunk, capBytes) {
24
+ stream.bytes += chunk.byteLength;
25
+ if (stream.bytes > capBytes) {
26
+ stream.truncated = true;
27
+ }
28
+ const retentionTotal = stream.chunks.reduce((sum, part) => sum + part.byteLength, 0);
29
+ if (retentionTotal < MAX_RETENTION_BYTES) {
30
+ stream.chunks.push(chunk);
31
+ }
32
+ // Keep the excerpt window bounded to the last capBytes so it shows the
33
+ // most recent output, not the start.
34
+ stream.window.push(chunk);
35
+ const windowTotal = stream.window.reduce((sum, part) => sum + part.byteLength, 0);
36
+ if (windowTotal > capBytes) {
37
+ stream.truncated = true;
38
+ let drop = windowTotal - capBytes;
39
+ while (drop > 0 && stream.window.length > 0) {
40
+ const first = stream.window[0];
41
+ if (first.byteLength <= drop) {
42
+ drop -= first.byteLength;
43
+ stream.window.shift();
44
+ }
45
+ else {
46
+ stream.window[0] = first.subarray(drop);
47
+ drop = 0;
48
+ }
49
+ }
50
+ }
51
+ }
52
+ function decode(stream) {
53
+ return sanitizeBinaryOutput(Buffer.concat(stream.window).toString("utf-8"));
54
+ }
55
+ function excerptFor(stdout, stderr, policy) {
56
+ const sections = [];
57
+ const stdoutText = decode(stdout);
58
+ const stderrText = decode(stderr);
59
+ if (stdoutText.length > 0) {
60
+ sections.push(stdoutText);
61
+ }
62
+ if (stderrText.length > 0) {
63
+ sections.push(stderrText);
64
+ }
65
+ const combined = sections.join("\n");
66
+ const lines = combined.split("\n");
67
+ if (lines.length > policy.maxOutputLines) {
68
+ // The notice lives INSIDE the budget: the excerpt never exceeds
69
+ // maxOutputLines lines in total.
70
+ return [
71
+ ...lines.slice(0, policy.maxOutputLines - 1),
72
+ `<${lines.length - policy.maxOutputLines + 1} more lines truncated>`,
73
+ ].join("\n");
74
+ }
75
+ return combined;
76
+ }
77
+ export async function runPolicyCommand(command, options) {
78
+ const started = Date.now();
79
+ const base = {
80
+ policyId: command.id,
81
+ executable: command.executable,
82
+ argv: command.argv,
83
+ };
84
+ if (options.signal?.aborted) {
85
+ return {
86
+ ...base,
87
+ argv: base.argv,
88
+ cwd: options.workspaceRoot,
89
+ durationMs: 0,
90
+ status: "cancelled",
91
+ truncated: false,
92
+ stdoutBytes: 0,
93
+ stderrBytes: 0,
94
+ outputExcerpt: "",
95
+ refusalReason: "cancelled before the command started",
96
+ };
97
+ }
98
+ // Defense in depth against a cwd escaping the workspace. The loader
99
+ // already rejects absolute paths and ".." segments; resolve() here is the
100
+ // second gate, on the computed path rather than the configured string.
101
+ const cwd = command.cwd === "workspace" ? options.workspaceRoot : resolve(options.workspaceRoot, command.cwd);
102
+ // The containment check uses relative() — a startsWith string comparison
103
+ // breaks on Windows, where resolve() switches to backslash separators.
104
+ const containmentRoot = resolve(options.workspaceRoot);
105
+ const containmentRel = relative(containmentRoot, cwd);
106
+ const insideWorkspace = containmentRel === "" || (!containmentRel.startsWith("..") && !isAbsolute(containmentRel));
107
+ if (!insideWorkspace) {
108
+ return {
109
+ ...base,
110
+ cwd,
111
+ durationMs: 0,
112
+ status: "refused",
113
+ truncated: false,
114
+ stdoutBytes: 0,
115
+ stderrBytes: 0,
116
+ outputExcerpt: "",
117
+ refusalReason: `cwd ${command.cwd} resolves outside the workspace`,
118
+ };
119
+ }
120
+ return await new Promise((resolveRun) => {
121
+ let child;
122
+ try {
123
+ child = spawn(command.executable, command.argv, {
124
+ cwd,
125
+ shell: false,
126
+ detached: true,
127
+ windowsHide: true,
128
+ stdio: ["ignore", "pipe", "pipe"],
129
+ });
130
+ }
131
+ catch (error) {
132
+ resolveRun({
133
+ ...base,
134
+ cwd,
135
+ durationMs: Date.now() - started,
136
+ status: "spawn-failed",
137
+ truncated: false,
138
+ stdoutBytes: 0,
139
+ stderrBytes: 0,
140
+ outputExcerpt: "",
141
+ refusalReason: error instanceof Error ? error.message : String(error),
142
+ });
143
+ return;
144
+ }
145
+ const stdout = newStream();
146
+ const stderr = newStream();
147
+ let settled = false;
148
+ let killed;
149
+ let spawnError;
150
+ const finish = (extra) => {
151
+ if (settled) {
152
+ return;
153
+ }
154
+ settled = true;
155
+ clearTimeout(timer);
156
+ options.signal?.removeEventListener("abort", onAbort);
157
+ const outputExcerpt = excerptFor(stdout, stderr, command);
158
+ // Only stdout: the artifact is the raw stream. Both streams: a
159
+ // stderr section is appended so the raw stdout bytes stay intact.
160
+ const retained = [...stdout.chunks];
161
+ if (stderr.chunks.length > 0) {
162
+ if (retained.length > 0) {
163
+ retained.push(Buffer.from("\n"));
164
+ }
165
+ retained.push(Buffer.from("stderr:\n"), ...stderr.chunks);
166
+ }
167
+ const artifactPromise = options.artifactStore && retained.length > 0
168
+ ? options.artifactStore.writeArtifact(Buffer.concat(retained)).catch(() => undefined)
169
+ : Promise.resolve(undefined);
170
+ void artifactPromise.then((artifact) => {
171
+ resolveRun({
172
+ ...base,
173
+ cwd,
174
+ durationMs: Date.now() - started,
175
+ truncated: stdout.truncated || stderr.truncated,
176
+ stdoutBytes: stdout.bytes,
177
+ stderrBytes: stderr.bytes,
178
+ outputExcerpt,
179
+ ...(artifact !== undefined ? { artifact } : {}),
180
+ ...extra,
181
+ });
182
+ });
183
+ };
184
+ const onAbort = () => {
185
+ if (settled || child.pid === undefined) {
186
+ return;
187
+ }
188
+ killed = "cancelled";
189
+ killProcessTree(child.pid);
190
+ };
191
+ options.signal?.addEventListener("abort", onAbort, { once: true });
192
+ const timer = setTimeout(() => {
193
+ if (settled || child.pid === undefined) {
194
+ return;
195
+ }
196
+ killed = "timeout";
197
+ killProcessTree(child.pid);
198
+ }, command.timeoutMs);
199
+ child.once("error", (error) => {
200
+ spawnError = error;
201
+ });
202
+ child.stdout?.on("data", (chunk) => capture(stdout, chunk, command.maxOutputBytes));
203
+ child.stderr?.on("data", (chunk) => capture(stderr, chunk, command.maxOutputBytes));
204
+ child.once("close", (code, termSignal) => {
205
+ if (spawnError !== undefined) {
206
+ finish({
207
+ status: "spawn-failed",
208
+ refusalReason: spawnError.message,
209
+ });
210
+ return;
211
+ }
212
+ if (killed === "timeout") {
213
+ finish({ status: "timeout", ...(termSignal ? { signal: termSignal } : {}) });
214
+ return;
215
+ }
216
+ if (killed === "cancelled") {
217
+ finish({ status: "cancelled", ...(termSignal ? { signal: termSignal } : {}) });
218
+ return;
219
+ }
220
+ finish({
221
+ status: code === 0 ? "passed" : "failed",
222
+ ...(code !== null ? { exitCode: code } : {}),
223
+ ...(termSignal ? { signal: termSignal } : {}),
224
+ });
225
+ });
226
+ });
227
+ }
228
+ //# sourceMappingURL=policy-executor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"policy-executor.js","sourceRoot":"","sources":["../../src/core/policy-executor.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * VF.3: the bounded argv command executor (spec\n * 2026-09-01-configured-verification-and-formatting.md § 2). Runs a\n * `CommandPolicy` from the VF.2 loader verbatim: executable + argv spawn\n * with no shell, under the policy's numeric bounds, with process-tree\n * termination on timeout and cancellation. Outcomes are structured values;\n * a command that merely fails is data, never a throw.\n *\n * Trusting the loader: this module does NOT re-validate policy fields the\n * loader already guarantees (numeric bounds, shell:false, argv shapes). The\n * one defensive re-check is the cwd confinement, because a resolved path is\n * cheap to verify and the worst failure here is running outside the\n * workspace.\n */\n\nimport { spawn } from \"node:child_process\";\nimport { isAbsolute, relative, resolve } from \"node:path\";\nimport { killProcessTree, sanitizeBinaryOutput } from \"../utils/shell.ts\";\nimport type { CommandPolicy } from \"./policy-loader.ts\";\nimport type { WorkspaceArtifactRef, WorkspaceArtifactStore } from \"./workspace/artifacts.ts\";\n\nexport type PolicyRunStatus =\n\t| \"passed\"\n\t| \"failed\"\n\t| \"spawn-failed\"\n\t| \"timeout\"\n\t| \"cancelled\"\n\t| \"refused\"\n\t/** PS.2: the command ran but wrote outside its declared scope, so nothing outside was promoted. Never \"passed\". */\n\t| \"scope-violated\";\n\nexport interface PolicyRunOptions {\n\tworkspaceRoot: string;\n\tsignal?: AbortSignal;\n\t/** When present, the full combined output is retained under the session's artifact policy. */\n\tartifactStore?: WorkspaceArtifactStore;\n}\n\nexport interface PolicyRunOutcome {\n\tstatus: PolicyRunStatus;\n\tpolicyId: string;\n\texecutable: string;\n\targv: string[];\n\t/** The working directory the command actually ran in (or would have run in). */\n\tcwd: string;\n\tdurationMs: number;\n\texitCode?: number;\n\tsignal?: string;\n\t/** True when output was captured only partially because a bound was hit. */\n\ttruncated: boolean;\n\tstdoutBytes: number;\n\tstderrBytes: number;\n\t/** Bounded, sanitized text for the model; full bytes go to the artifact store. */\n\toutputExcerpt: string;\n\tartifact?: WorkspaceArtifactRef;\n\t/** Why a command could not run at all (\"refused\" / \"spawn-failed\"). */\n\trefusalReason?: string;\n}\n\n/** Hard retention ceiling for artifact bytes, independent of policy caps. */\nconst MAX_RETENTION_BYTES = 8 * 1024 * 1024;\n\ninterface CapturedStream {\n\t/** Full stream bytes up to the retention ceiling — what the artifact keeps. */\n\tchunks: Buffer[];\n\t/** True total bytes seen, even past the retention ceiling. */\n\tbytes: number;\n\t/** Last capBytes of the stream — what the excerpt shows. */\n\twindow: Buffer[];\n\ttruncated: boolean;\n}\n\nfunction newStream(): CapturedStream {\n\treturn { chunks: [], bytes: 0, window: [], truncated: false };\n}\n\nfunction capture(stream: CapturedStream, chunk: Buffer, capBytes: number): void {\n\tstream.bytes += chunk.byteLength;\n\tif (stream.bytes > capBytes) {\n\t\tstream.truncated = true;\n\t}\n\tconst retentionTotal = stream.chunks.reduce((sum, part) => sum + part.byteLength, 0);\n\tif (retentionTotal < MAX_RETENTION_BYTES) {\n\t\tstream.chunks.push(chunk);\n\t}\n\t// Keep the excerpt window bounded to the last capBytes so it shows the\n\t// most recent output, not the start.\n\tstream.window.push(chunk);\n\tconst windowTotal = stream.window.reduce((sum, part) => sum + part.byteLength, 0);\n\tif (windowTotal > capBytes) {\n\t\tstream.truncated = true;\n\t\tlet drop = windowTotal - capBytes;\n\t\twhile (drop > 0 && stream.window.length > 0) {\n\t\t\tconst first = stream.window[0];\n\t\t\tif (first.byteLength <= drop) {\n\t\t\t\tdrop -= first.byteLength;\n\t\t\t\tstream.window.shift();\n\t\t\t} else {\n\t\t\t\tstream.window[0] = first.subarray(drop);\n\t\t\t\tdrop = 0;\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunction decode(stream: CapturedStream): string {\n\treturn sanitizeBinaryOutput(Buffer.concat(stream.window).toString(\"utf-8\"));\n}\n\nfunction excerptFor(stdout: CapturedStream, stderr: CapturedStream, policy: CommandPolicy): string {\n\tconst sections: string[] = [];\n\tconst stdoutText = decode(stdout);\n\tconst stderrText = decode(stderr);\n\tif (stdoutText.length > 0) {\n\t\tsections.push(stdoutText);\n\t}\n\tif (stderrText.length > 0) {\n\t\tsections.push(stderrText);\n\t}\n\tconst combined = sections.join(\"\\n\");\n\tconst lines = combined.split(\"\\n\");\n\tif (lines.length > policy.maxOutputLines) {\n\t\t// The notice lives INSIDE the budget: the excerpt never exceeds\n\t\t// maxOutputLines lines in total.\n\t\treturn [\n\t\t\t...lines.slice(0, policy.maxOutputLines - 1),\n\t\t\t`<${lines.length - policy.maxOutputLines + 1} more lines truncated>`,\n\t\t].join(\"\\n\");\n\t}\n\treturn combined;\n}\n\nexport async function runPolicyCommand(command: CommandPolicy, options: PolicyRunOptions): Promise<PolicyRunOutcome> {\n\tconst started = Date.now();\n\tconst base = {\n\t\tpolicyId: command.id,\n\t\texecutable: command.executable,\n\t\targv: command.argv,\n\t};\n\n\tif (options.signal?.aborted) {\n\t\treturn {\n\t\t\t...base,\n\t\t\targv: base.argv,\n\t\t\tcwd: options.workspaceRoot,\n\t\t\tdurationMs: 0,\n\t\t\tstatus: \"cancelled\",\n\t\t\ttruncated: false,\n\t\t\tstdoutBytes: 0,\n\t\t\tstderrBytes: 0,\n\t\t\toutputExcerpt: \"\",\n\t\t\trefusalReason: \"cancelled before the command started\",\n\t\t};\n\t}\n\n\t// Defense in depth against a cwd escaping the workspace. The loader\n\t// already rejects absolute paths and \"..\" segments; resolve() here is the\n\t// second gate, on the computed path rather than the configured string.\n\tconst cwd = command.cwd === \"workspace\" ? options.workspaceRoot : resolve(options.workspaceRoot, command.cwd);\n\t// The containment check uses relative() — a startsWith string comparison\n\t// breaks on Windows, where resolve() switches to backslash separators.\n\tconst containmentRoot = resolve(options.workspaceRoot);\n\tconst containmentRel = relative(containmentRoot, cwd);\n\tconst insideWorkspace = containmentRel === \"\" || (!containmentRel.startsWith(\"..\") && !isAbsolute(containmentRel));\n\tif (!insideWorkspace) {\n\t\treturn {\n\t\t\t...base,\n\t\t\tcwd,\n\t\t\tdurationMs: 0,\n\t\t\tstatus: \"refused\",\n\t\t\ttruncated: false,\n\t\t\tstdoutBytes: 0,\n\t\t\tstderrBytes: 0,\n\t\t\toutputExcerpt: \"\",\n\t\t\trefusalReason: `cwd ${command.cwd} resolves outside the workspace`,\n\t\t};\n\t}\n\n\treturn await new Promise<PolicyRunOutcome>((resolveRun) => {\n\t\tlet child: ReturnType<typeof spawn>;\n\t\ttry {\n\t\t\tchild = spawn(command.executable, command.argv, {\n\t\t\t\tcwd,\n\t\t\t\tshell: false,\n\t\t\t\tdetached: true,\n\t\t\t\twindowsHide: true,\n\t\t\t\tstdio: [\"ignore\", \"pipe\", \"pipe\"],\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tresolveRun({\n\t\t\t\t...base,\n\t\t\t\tcwd,\n\t\t\t\tdurationMs: Date.now() - started,\n\t\t\t\tstatus: \"spawn-failed\",\n\t\t\t\ttruncated: false,\n\t\t\t\tstdoutBytes: 0,\n\t\t\t\tstderrBytes: 0,\n\t\t\t\toutputExcerpt: \"\",\n\t\t\t\trefusalReason: error instanceof Error ? error.message : String(error),\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\tconst stdout = newStream();\n\t\tconst stderr = newStream();\n\t\tlet settled = false;\n\t\tlet killed: \"timeout\" | \"cancelled\" | undefined;\n\t\tlet spawnError: Error | undefined;\n\n\t\tconst finish = (extra: Partial<PolicyRunOutcome> & { status: PolicyRunStatus }) => {\n\t\t\tif (settled) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tsettled = true;\n\t\t\tclearTimeout(timer);\n\t\t\toptions.signal?.removeEventListener(\"abort\", onAbort);\n\t\t\tconst outputExcerpt = excerptFor(stdout, stderr, command);\n\t\t\t// Only stdout: the artifact is the raw stream. Both streams: a\n\t\t\t// stderr section is appended so the raw stdout bytes stay intact.\n\t\t\tconst retained: Buffer[] = [...stdout.chunks];\n\t\t\tif (stderr.chunks.length > 0) {\n\t\t\t\tif (retained.length > 0) {\n\t\t\t\t\tretained.push(Buffer.from(\"\\n\"));\n\t\t\t\t}\n\t\t\t\tretained.push(Buffer.from(\"stderr:\\n\"), ...stderr.chunks);\n\t\t\t}\n\t\t\tconst artifactPromise =\n\t\t\t\toptions.artifactStore && retained.length > 0\n\t\t\t\t\t? options.artifactStore.writeArtifact(Buffer.concat(retained)).catch(() => undefined)\n\t\t\t\t\t: Promise.resolve(undefined);\n\n\t\t\tvoid artifactPromise.then((artifact) => {\n\t\t\t\tresolveRun({\n\t\t\t\t\t...base,\n\t\t\t\t\tcwd,\n\t\t\t\t\tdurationMs: Date.now() - started,\n\t\t\t\t\ttruncated: stdout.truncated || stderr.truncated,\n\t\t\t\t\tstdoutBytes: stdout.bytes,\n\t\t\t\t\tstderrBytes: stderr.bytes,\n\t\t\t\t\toutputExcerpt,\n\t\t\t\t\t...(artifact !== undefined ? { artifact } : {}),\n\t\t\t\t\t...extra,\n\t\t\t\t});\n\t\t\t});\n\t\t};\n\n\t\tconst onAbort = () => {\n\t\t\tif (settled || child.pid === undefined) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tkilled = \"cancelled\";\n\t\t\tkillProcessTree(child.pid);\n\t\t};\n\t\toptions.signal?.addEventListener(\"abort\", onAbort, { once: true });\n\n\t\tconst timer = setTimeout(() => {\n\t\t\tif (settled || child.pid === undefined) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tkilled = \"timeout\";\n\t\t\tkillProcessTree(child.pid);\n\t\t}, command.timeoutMs);\n\n\t\tchild.once(\"error\", (error) => {\n\t\t\tspawnError = error;\n\t\t});\n\t\tchild.stdout?.on(\"data\", (chunk: Buffer) => capture(stdout, chunk, command.maxOutputBytes));\n\t\tchild.stderr?.on(\"data\", (chunk: Buffer) => capture(stderr, chunk, command.maxOutputBytes));\n\n\t\tchild.once(\"close\", (code, termSignal) => {\n\t\t\tif (spawnError !== undefined) {\n\t\t\t\tfinish({\n\t\t\t\t\tstatus: \"spawn-failed\",\n\t\t\t\t\trefusalReason: spawnError.message,\n\t\t\t\t});\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (killed === \"timeout\") {\n\t\t\t\tfinish({ status: \"timeout\", ...(termSignal ? { signal: termSignal } : {}) });\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (killed === \"cancelled\") {\n\t\t\t\tfinish({ status: \"cancelled\", ...(termSignal ? { signal: termSignal } : {}) });\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tfinish({\n\t\t\t\tstatus: code === 0 ? \"passed\" : \"failed\",\n\t\t\t\t...(code !== null ? { exitCode: code } : {}),\n\t\t\t\t...(termSignal ? { signal: termSignal } : {}),\n\t\t\t});\n\t\t});\n\t});\n}\n"]}
@@ -0,0 +1,72 @@
1
+ /**
2
+ * VF.2: the ONE projection from policy settings to runtime policies (spec
3
+ * 2026-09-01-configured-verification-and-formatting.md § 1; ADR 0028; ADR
4
+ * 0010's one-projection rule applied to configured commands). Nothing else
5
+ * in the codebase re-derives defaults, bounds, or trust stamps — surfaces
6
+ * consume `loadPolicyConfiguration()` output, and the executor (VF.3)
7
+ * consumes these policies verbatim.
8
+ *
9
+ * Strictness contract: any validation error inside a source drops that
10
+ * entire source and surfaces structured errors. Half-applied policies are
11
+ * worse than absent ones because they look configured while running under
12
+ * different bounds than the user wrote. A missing `policies` key stays
13
+ * inert: no policies, no errors, no runtime.
14
+ */
15
+ /** Hard bounds. Caps exist so a typo cannot configure an unbounded run. */
16
+ export declare const DEFAULT_TIMEOUT_MS = 120000;
17
+ export declare const MAX_TIMEOUT_MS = 600000;
18
+ export declare const DEFAULT_MAX_OUTPUT_BYTES = 262144;
19
+ export declare const MAX_MAX_OUTPUT_BYTES = 1048576;
20
+ export declare const DEFAULT_MAX_OUTPUT_LINES = 2000;
21
+ export declare const MAX_MAX_OUTPUT_LINES = 10000;
22
+ /** Capability names from the shared tool-contract vocabulary (ADR 0010). */
23
+ export type PolicyCapability = "exec" | "fs.write";
24
+ export type PolicyPermission = "allow" | "ask" | "deny";
25
+ export type PolicySource = "user" | "project";
26
+ export interface CommandPolicy {
27
+ id: string;
28
+ executable: string;
29
+ argv: string[];
30
+ cwd: "workspace" | string;
31
+ pathScope?: string[];
32
+ timeoutMs: number;
33
+ maxOutputBytes: number;
34
+ maxOutputLines: number;
35
+ shell: false;
36
+ permission: PolicyPermission;
37
+ trustedSource: PolicySource;
38
+ }
39
+ export interface VerificationPolicy extends CommandPolicy {
40
+ kind: "verification";
41
+ blocksCompletion: boolean;
42
+ }
43
+ export interface FormatterPolicy extends CommandPolicy {
44
+ kind: "formatter";
45
+ mutatesFiles: true;
46
+ declaredPaths: string[];
47
+ }
48
+ export interface PolicyLoadError {
49
+ source: PolicySource;
50
+ /** Policy ID when the error is attributable to one entry. */
51
+ policyId?: string;
52
+ message: string;
53
+ }
54
+ export interface PolicySnapshot {
55
+ verification: VerificationPolicy[];
56
+ formatter: FormatterPolicy[];
57
+ errors: PolicyLoadError[];
58
+ }
59
+ export interface PolicyConfigurationInput {
60
+ globalSettings?: unknown;
61
+ projectSettings?: unknown;
62
+ projectTrusted: boolean;
63
+ }
64
+ /**
65
+ * Project raw per-source policy settings onto the canonical snapshot. The
66
+ * project source participates only when the project is trusted (ADR 0028);
67
+ * within a trusted project a project policy ID replaces the user policy
68
+ * with the same ID. This function never throws and never reads ambient
69
+ * state: identical inputs project identical snapshots.
70
+ */
71
+ export declare function loadPolicyConfiguration(input: PolicyConfigurationInput): PolicySnapshot;
72
+ //# sourceMappingURL=policy-loader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"policy-loader.d.ts","sourceRoot":"","sources":["../../src/core/policy-loader.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,2EAA2E;AAC3E,eAAO,MAAM,kBAAkB,SAAU,CAAC;AAC1C,eAAO,MAAM,cAAc,SAAU,CAAC;AACtC,eAAO,MAAM,wBAAwB,SAAU,CAAC;AAChD,eAAO,MAAM,oBAAoB,UAAY,CAAC;AAC9C,eAAO,MAAM,wBAAwB,OAAQ,CAAC;AAC9C,eAAO,MAAM,oBAAoB,QAAS,CAAC;AAE3C,4EAA4E;AAC5E,MAAM,MAAM,gBAAgB,GAAG,MAAM,GAAG,UAAU,CAAC;AACnD,MAAM,MAAM,gBAAgB,GAAG,OAAO,GAAG,KAAK,GAAG,MAAM,CAAC;AACxD,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,SAAS,CAAC;AAE9C,MAAM,WAAW,aAAa;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,GAAG,EAAE,WAAW,GAAG,MAAM,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;IACvB,KAAK,EAAE,KAAK,CAAC;IACb,UAAU,EAAE,gBAAgB,CAAC;IAC7B,aAAa,EAAE,YAAY,CAAC;CAC5B;AAED,MAAM,WAAW,kBAAmB,SAAQ,aAAa;IACxD,IAAI,EAAE,cAAc,CAAC;IACrB,gBAAgB,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,eAAgB,SAAQ,aAAa;IACrD,IAAI,EAAE,WAAW,CAAC;IAClB,YAAY,EAAE,IAAI,CAAC;IACnB,aAAa,EAAE,MAAM,EAAE,CAAC;CACxB;AAID,MAAM,WAAW,eAAe;IAC/B,MAAM,EAAE,YAAY,CAAC;IACrB,6DAA6D;IAC7D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,cAAc;IAC9B,YAAY,EAAE,kBAAkB,EAAE,CAAC;IACnC,SAAS,EAAE,eAAe,EAAE,CAAC;IAC7B,MAAM,EAAE,eAAe,EAAE,CAAC;CAC1B;AAED,MAAM,WAAW,wBAAwB;IACxC,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,cAAc,EAAE,OAAO,CAAC;CACxB;AAsLD;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,wBAAwB,GAAG,cAAc,CAmBvF","sourcesContent":["/**\n * VF.2: the ONE projection from policy settings to runtime policies (spec\n * 2026-09-01-configured-verification-and-formatting.md § 1; ADR 0028; ADR\n * 0010's one-projection rule applied to configured commands). Nothing else\n * in the codebase re-derives defaults, bounds, or trust stamps — surfaces\n * consume `loadPolicyConfiguration()` output, and the executor (VF.3)\n * consumes these policies verbatim.\n *\n * Strictness contract: any validation error inside a source drops that\n * entire source and surfaces structured errors. Half-applied policies are\n * worse than absent ones because they look configured while running under\n * different bounds than the user wrote. A missing `policies` key stays\n * inert: no policies, no errors, no runtime.\n */\n\n/** Hard bounds. Caps exist so a typo cannot configure an unbounded run. */\nexport const DEFAULT_TIMEOUT_MS = 120_000;\nexport const MAX_TIMEOUT_MS = 600_000;\nexport const DEFAULT_MAX_OUTPUT_BYTES = 262_144; // 256 KiB\nexport const MAX_MAX_OUTPUT_BYTES = 1_048_576; // 1 MiB\nexport const DEFAULT_MAX_OUTPUT_LINES = 2_000;\nexport const MAX_MAX_OUTPUT_LINES = 10_000;\n\n/** Capability names from the shared tool-contract vocabulary (ADR 0010). */\nexport type PolicyCapability = \"exec\" | \"fs.write\";\nexport type PolicyPermission = \"allow\" | \"ask\" | \"deny\";\nexport type PolicySource = \"user\" | \"project\";\n\nexport interface CommandPolicy {\n\tid: string;\n\texecutable: string;\n\targv: string[];\n\tcwd: \"workspace\" | string;\n\tpathScope?: string[];\n\ttimeoutMs: number;\n\tmaxOutputBytes: number;\n\tmaxOutputLines: number;\n\tshell: false;\n\tpermission: PolicyPermission;\n\ttrustedSource: PolicySource;\n}\n\nexport interface VerificationPolicy extends CommandPolicy {\n\tkind: \"verification\";\n\tblocksCompletion: boolean;\n}\n\nexport interface FormatterPolicy extends CommandPolicy {\n\tkind: \"formatter\";\n\tmutatesFiles: true;\n\tdeclaredPaths: string[];\n}\n\ntype StampedPolicy = VerificationPolicy | FormatterPolicy;\n\nexport interface PolicyLoadError {\n\tsource: PolicySource;\n\t/** Policy ID when the error is attributable to one entry. */\n\tpolicyId?: string;\n\tmessage: string;\n}\n\nexport interface PolicySnapshot {\n\tverification: VerificationPolicy[];\n\tformatter: FormatterPolicy[];\n\terrors: PolicyLoadError[];\n}\n\nexport interface PolicyConfigurationInput {\n\tglobalSettings?: unknown;\n\tprojectSettings?: unknown;\n\tprojectTrusted: boolean;\n}\n\ninterface RawPolicyEntry extends Record<string, unknown> {\n\tid?: unknown;\n\texecutable?: unknown;\n\targv?: unknown;\n\tcwd?: unknown;\n\tpathScope?: unknown;\n\ttimeoutMs?: unknown;\n\tmaxOutputBytes?: unknown;\n\tmaxOutputLines?: unknown;\n\tshell?: unknown;\n\tpermission?: unknown;\n}\n\nconst KNOWN_BASE_FIELDS = new Set([\n\t\"id\",\n\t\"executable\",\n\t\"argv\",\n\t\"cwd\",\n\t\"pathScope\",\n\t\"timeoutMs\",\n\t\"maxOutputBytes\",\n\t\"maxOutputLines\",\n\t\"shell\",\n\t\"permission\",\n]);\n\nfunction hasTraversalSegment(path: string): boolean {\n\treturn path.split(\"/\").includes(\"..\");\n}\n\n/**\n * Validate one raw entry and stamp the canonical runtime shape. Returns\n * either the policy or an error message; the caller drops the source on any\n * error. Field-by-field so error messages name the offending field.\n */\nfunction stampPolicy(\n\tsource: PolicySource,\n\tkind: \"verification\" | \"formatter\",\n\tentry: RawPolicyEntry,\n\tseenIds: Set<string>,\n): { policy?: StampedPolicy; error?: string; policyId?: string } {\n\tconst policyId = typeof entry.id === \"string\" ? entry.id : undefined;\n\tconst fail = (message: string): { error: string; policyId?: string } => ({ error: message, policyId });\n\n\tif (typeof entry.id !== \"string\" || entry.id.length === 0) {\n\t\treturn fail(\"id must be a non-empty string\");\n\t}\n\tif (seenIds.has(entry.id)) {\n\t\treturn fail(`duplicate policy id \"${entry.id}\" within one source`);\n\t}\n\tif (typeof entry.executable !== \"string\" || entry.executable.length === 0) {\n\t\treturn fail(\"executable must be a non-empty string\");\n\t}\n\tif (!Array.isArray(entry.argv) || entry.argv.length === 0 || entry.argv.some((part) => typeof part !== \"string\")) {\n\t\treturn fail(\"argv must be a non-empty array of strings\");\n\t}\n\tif (entry.shell !== undefined && entry.shell !== false) {\n\t\treturn fail(\"shell must be literal false; shell execution is not a supported policy configuration\");\n\t}\n\tif (entry.cwd !== undefined) {\n\t\tif (typeof entry.cwd !== \"string\" || entry.cwd.length === 0) {\n\t\t\treturn fail('cwd must be the literal \"workspace\" or a non-empty workspace-relative path');\n\t\t}\n\t\tif (entry.cwd !== \"workspace\" && (entry.cwd.startsWith(\"/\") || hasTraversalSegment(entry.cwd))) {\n\t\t\treturn fail('cwd must stay inside the workspace: no absolute paths, no \"..\" segments');\n\t\t}\n\t}\n\tif (entry.pathScope !== undefined) {\n\t\tif (\n\t\t\t!Array.isArray(entry.pathScope) ||\n\t\t\tentry.pathScope.length === 0 ||\n\t\t\tentry.pathScope.some((part) => typeof part !== \"string\" || part.length === 0 || hasTraversalSegment(part))\n\t\t) {\n\t\t\treturn fail('pathScope must be non-empty workspace-relative patterns without \"..\" segments');\n\t\t}\n\t}\n\n\tconst timeoutMs = (entry.timeoutMs ?? DEFAULT_TIMEOUT_MS) as number;\n\tif (!Number.isInteger(timeoutMs) || timeoutMs <= 0 || timeoutMs > MAX_TIMEOUT_MS) {\n\t\treturn fail(`timeoutMs must be a positive integer no greater than ${MAX_TIMEOUT_MS}`);\n\t}\n\tconst maxOutputBytes = (entry.maxOutputBytes ?? DEFAULT_MAX_OUTPUT_BYTES) as number;\n\tif (!Number.isInteger(maxOutputBytes) || maxOutputBytes <= 0 || maxOutputBytes > MAX_MAX_OUTPUT_BYTES) {\n\t\treturn fail(`maxOutputBytes must be a positive integer no greater than ${MAX_MAX_OUTPUT_BYTES}`);\n\t}\n\tconst maxOutputLines = (entry.maxOutputLines ?? DEFAULT_MAX_OUTPUT_LINES) as number;\n\tif (!Number.isInteger(maxOutputLines) || maxOutputLines <= 0 || maxOutputLines > MAX_MAX_OUTPUT_LINES) {\n\t\treturn fail(`maxOutputLines must be a positive integer no greater than ${MAX_MAX_OUTPUT_LINES}`);\n\t}\n\n\tconst permission = (entry.permission ?? \"ask\") as PolicyPermission;\n\tif (permission !== \"allow\" && permission !== \"ask\" && permission !== \"deny\") {\n\t\treturn fail('permission must be \"allow\", \"ask\", or \"deny\"');\n\t}\n\n\tfor (const field of Object.keys(entry)) {\n\t\tif (\n\t\t\t!KNOWN_BASE_FIELDS.has(field) &&\n\t\t\t!(kind === \"verification\" && field === \"blocksCompletion\") &&\n\t\t\t!(kind === \"formatter\" && field === \"declaredPaths\")\n\t\t) {\n\t\t\treturn fail(`unknown policy field \"${field}\"`);\n\t\t}\n\t}\n\n\tconst base: Omit<CommandPolicy, \"trustedSource\"> & { trustedSource: PolicySource } = {\n\t\tid: entry.id,\n\t\texecutable: entry.executable,\n\t\targv: entry.argv as string[],\n\t\tcwd: (entry.cwd as string | undefined) ?? \"workspace\",\n\t\t...(entry.pathScope !== undefined ? { pathScope: entry.pathScope as string[] } : {}),\n\t\ttimeoutMs,\n\t\tmaxOutputBytes,\n\t\tmaxOutputLines,\n\t\tshell: false,\n\t\tpermission,\n\t\ttrustedSource: source,\n\t};\n\n\tif (kind === \"verification\") {\n\t\tif (entry.blocksCompletion !== undefined && typeof entry.blocksCompletion !== \"boolean\") {\n\t\t\treturn fail(\"blocksCompletion must be a boolean\");\n\t\t}\n\t\treturn { policy: { ...base, kind, blocksCompletion: (entry.blocksCompletion as boolean | undefined) ?? false } };\n\t}\n\n\tif (!Array.isArray(entry.declaredPaths) || entry.declaredPaths.length === 0) {\n\t\treturn fail(\"declaredPaths is required for formatter policies and must be a non-empty array\");\n\t}\n\tif (entry.declaredPaths.some((part) => typeof part !== \"string\" || part.length === 0 || hasTraversalSegment(part))) {\n\t\treturn fail('declaredPaths must be non-empty workspace-relative patterns without \"..\" segments');\n\t}\n\treturn { policy: { ...base, kind, mutatesFiles: true, declaredPaths: entry.declaredPaths as string[] } };\n}\n\n/**\n * Load one source. Returns the stamped policies plus errors; on ANY error\n * the source contributes nothing (fail closed, all-or-nothing per source).\n */\nfunction loadSource(source: PolicySource, raw: unknown, errors: PolicyLoadError[]): StampedPolicy[] {\n\tif (raw === undefined || raw === null) {\n\t\treturn [];\n\t}\n\tconst fail = (message: string, policyId?: string): [] => {\n\t\terrors.push({ source, policyId, message });\n\t\treturn [];\n\t};\n\tif (typeof raw !== \"object\") {\n\t\treturn fail(\"policies must be an object\");\n\t}\n\tconst policies = raw as Record<string, unknown>;\n\tif (policies.schemaVersion !== 1) {\n\t\treturn fail(`unsupported policies schemaVersion ${JSON.stringify(policies.schemaVersion)}`);\n\t}\n\n\tconst seenIds = new Set<string>();\n\tconst stamped: StampedPolicy[] = [];\n\tfor (const kind of [\"verification\", \"formatter\"] as const) {\n\t\tconst entries = policies[kind];\n\t\tif (entries === undefined) {\n\t\t\tcontinue;\n\t\t}\n\t\tif (!Array.isArray(entries)) {\n\t\t\treturn fail(`policies.${kind} must be an array`);\n\t\t}\n\t\tfor (const entry of entries) {\n\t\t\tif (typeof entry !== \"object\" || entry === null) {\n\t\t\t\treturn fail(`policies.${kind} contains a non-object entry`);\n\t\t\t}\n\t\t\tconst result = stampPolicy(source, kind, entry as RawPolicyEntry, seenIds);\n\t\t\tif (result.error !== undefined || result.policy === undefined) {\n\t\t\t\treturn fail(result.error ?? \"policy failed to stamp\", result.policyId);\n\t\t\t}\n\t\t\tseenIds.add(result.policy.id);\n\t\t\tstamped.push(result.policy);\n\t\t}\n\t}\n\treturn stamped;\n}\n\n/**\n * Project raw per-source policy settings onto the canonical snapshot. The\n * project source participates only when the project is trusted (ADR 0028);\n * within a trusted project a project policy ID replaces the user policy\n * with the same ID. This function never throws and never reads ambient\n * state: identical inputs project identical snapshots.\n */\nexport function loadPolicyConfiguration(input: PolicyConfigurationInput): PolicySnapshot {\n\tconst errors: PolicyLoadError[] = [];\n\tconst userPolicies = loadSource(\"user\", input.globalSettings, errors);\n\tconst projectPolicies = input.projectTrusted ? loadSource(\"project\", input.projectSettings, errors) : [];\n\n\tconst verification: VerificationPolicy[] = [];\n\tconst formatter: FormatterPolicy[] = [];\n\tconst byId = new Map<string, StampedPolicy>();\n\tfor (const policy of [...userPolicies, ...projectPolicies]) {\n\t\tbyId.set(policy.id, policy);\n\t}\n\tfor (const policy of byId.values()) {\n\t\tif (policy.kind === \"verification\") {\n\t\t\tverification.push(policy);\n\t\t} else {\n\t\t\tformatter.push(policy as FormatterPolicy);\n\t\t}\n\t}\n\treturn { verification, formatter, errors };\n}\n"]}
@@ -0,0 +1,195 @@
1
+ /**
2
+ * VF.2: the ONE projection from policy settings to runtime policies (spec
3
+ * 2026-09-01-configured-verification-and-formatting.md § 1; ADR 0028; ADR
4
+ * 0010's one-projection rule applied to configured commands). Nothing else
5
+ * in the codebase re-derives defaults, bounds, or trust stamps — surfaces
6
+ * consume `loadPolicyConfiguration()` output, and the executor (VF.3)
7
+ * consumes these policies verbatim.
8
+ *
9
+ * Strictness contract: any validation error inside a source drops that
10
+ * entire source and surfaces structured errors. Half-applied policies are
11
+ * worse than absent ones because they look configured while running under
12
+ * different bounds than the user wrote. A missing `policies` key stays
13
+ * inert: no policies, no errors, no runtime.
14
+ */
15
+ /** Hard bounds. Caps exist so a typo cannot configure an unbounded run. */
16
+ export const DEFAULT_TIMEOUT_MS = 120_000;
17
+ export const MAX_TIMEOUT_MS = 600_000;
18
+ export const DEFAULT_MAX_OUTPUT_BYTES = 262_144; // 256 KiB
19
+ export const MAX_MAX_OUTPUT_BYTES = 1_048_576; // 1 MiB
20
+ export const DEFAULT_MAX_OUTPUT_LINES = 2_000;
21
+ export const MAX_MAX_OUTPUT_LINES = 10_000;
22
+ const KNOWN_BASE_FIELDS = new Set([
23
+ "id",
24
+ "executable",
25
+ "argv",
26
+ "cwd",
27
+ "pathScope",
28
+ "timeoutMs",
29
+ "maxOutputBytes",
30
+ "maxOutputLines",
31
+ "shell",
32
+ "permission",
33
+ ]);
34
+ function hasTraversalSegment(path) {
35
+ return path.split("/").includes("..");
36
+ }
37
+ /**
38
+ * Validate one raw entry and stamp the canonical runtime shape. Returns
39
+ * either the policy or an error message; the caller drops the source on any
40
+ * error. Field-by-field so error messages name the offending field.
41
+ */
42
+ function stampPolicy(source, kind, entry, seenIds) {
43
+ const policyId = typeof entry.id === "string" ? entry.id : undefined;
44
+ const fail = (message) => ({ error: message, policyId });
45
+ if (typeof entry.id !== "string" || entry.id.length === 0) {
46
+ return fail("id must be a non-empty string");
47
+ }
48
+ if (seenIds.has(entry.id)) {
49
+ return fail(`duplicate policy id "${entry.id}" within one source`);
50
+ }
51
+ if (typeof entry.executable !== "string" || entry.executable.length === 0) {
52
+ return fail("executable must be a non-empty string");
53
+ }
54
+ if (!Array.isArray(entry.argv) || entry.argv.length === 0 || entry.argv.some((part) => typeof part !== "string")) {
55
+ return fail("argv must be a non-empty array of strings");
56
+ }
57
+ if (entry.shell !== undefined && entry.shell !== false) {
58
+ return fail("shell must be literal false; shell execution is not a supported policy configuration");
59
+ }
60
+ if (entry.cwd !== undefined) {
61
+ if (typeof entry.cwd !== "string" || entry.cwd.length === 0) {
62
+ return fail('cwd must be the literal "workspace" or a non-empty workspace-relative path');
63
+ }
64
+ if (entry.cwd !== "workspace" && (entry.cwd.startsWith("/") || hasTraversalSegment(entry.cwd))) {
65
+ return fail('cwd must stay inside the workspace: no absolute paths, no ".." segments');
66
+ }
67
+ }
68
+ if (entry.pathScope !== undefined) {
69
+ if (!Array.isArray(entry.pathScope) ||
70
+ entry.pathScope.length === 0 ||
71
+ entry.pathScope.some((part) => typeof part !== "string" || part.length === 0 || hasTraversalSegment(part))) {
72
+ return fail('pathScope must be non-empty workspace-relative patterns without ".." segments');
73
+ }
74
+ }
75
+ const timeoutMs = (entry.timeoutMs ?? DEFAULT_TIMEOUT_MS);
76
+ if (!Number.isInteger(timeoutMs) || timeoutMs <= 0 || timeoutMs > MAX_TIMEOUT_MS) {
77
+ return fail(`timeoutMs must be a positive integer no greater than ${MAX_TIMEOUT_MS}`);
78
+ }
79
+ const maxOutputBytes = (entry.maxOutputBytes ?? DEFAULT_MAX_OUTPUT_BYTES);
80
+ if (!Number.isInteger(maxOutputBytes) || maxOutputBytes <= 0 || maxOutputBytes > MAX_MAX_OUTPUT_BYTES) {
81
+ return fail(`maxOutputBytes must be a positive integer no greater than ${MAX_MAX_OUTPUT_BYTES}`);
82
+ }
83
+ const maxOutputLines = (entry.maxOutputLines ?? DEFAULT_MAX_OUTPUT_LINES);
84
+ if (!Number.isInteger(maxOutputLines) || maxOutputLines <= 0 || maxOutputLines > MAX_MAX_OUTPUT_LINES) {
85
+ return fail(`maxOutputLines must be a positive integer no greater than ${MAX_MAX_OUTPUT_LINES}`);
86
+ }
87
+ const permission = (entry.permission ?? "ask");
88
+ if (permission !== "allow" && permission !== "ask" && permission !== "deny") {
89
+ return fail('permission must be "allow", "ask", or "deny"');
90
+ }
91
+ for (const field of Object.keys(entry)) {
92
+ if (!KNOWN_BASE_FIELDS.has(field) &&
93
+ !(kind === "verification" && field === "blocksCompletion") &&
94
+ !(kind === "formatter" && field === "declaredPaths")) {
95
+ return fail(`unknown policy field "${field}"`);
96
+ }
97
+ }
98
+ const base = {
99
+ id: entry.id,
100
+ executable: entry.executable,
101
+ argv: entry.argv,
102
+ cwd: entry.cwd ?? "workspace",
103
+ ...(entry.pathScope !== undefined ? { pathScope: entry.pathScope } : {}),
104
+ timeoutMs,
105
+ maxOutputBytes,
106
+ maxOutputLines,
107
+ shell: false,
108
+ permission,
109
+ trustedSource: source,
110
+ };
111
+ if (kind === "verification") {
112
+ if (entry.blocksCompletion !== undefined && typeof entry.blocksCompletion !== "boolean") {
113
+ return fail("blocksCompletion must be a boolean");
114
+ }
115
+ return { policy: { ...base, kind, blocksCompletion: entry.blocksCompletion ?? false } };
116
+ }
117
+ if (!Array.isArray(entry.declaredPaths) || entry.declaredPaths.length === 0) {
118
+ return fail("declaredPaths is required for formatter policies and must be a non-empty array");
119
+ }
120
+ if (entry.declaredPaths.some((part) => typeof part !== "string" || part.length === 0 || hasTraversalSegment(part))) {
121
+ return fail('declaredPaths must be non-empty workspace-relative patterns without ".." segments');
122
+ }
123
+ return { policy: { ...base, kind, mutatesFiles: true, declaredPaths: entry.declaredPaths } };
124
+ }
125
+ /**
126
+ * Load one source. Returns the stamped policies plus errors; on ANY error
127
+ * the source contributes nothing (fail closed, all-or-nothing per source).
128
+ */
129
+ function loadSource(source, raw, errors) {
130
+ if (raw === undefined || raw === null) {
131
+ return [];
132
+ }
133
+ const fail = (message, policyId) => {
134
+ errors.push({ source, policyId, message });
135
+ return [];
136
+ };
137
+ if (typeof raw !== "object") {
138
+ return fail("policies must be an object");
139
+ }
140
+ const policies = raw;
141
+ if (policies.schemaVersion !== 1) {
142
+ return fail(`unsupported policies schemaVersion ${JSON.stringify(policies.schemaVersion)}`);
143
+ }
144
+ const seenIds = new Set();
145
+ const stamped = [];
146
+ for (const kind of ["verification", "formatter"]) {
147
+ const entries = policies[kind];
148
+ if (entries === undefined) {
149
+ continue;
150
+ }
151
+ if (!Array.isArray(entries)) {
152
+ return fail(`policies.${kind} must be an array`);
153
+ }
154
+ for (const entry of entries) {
155
+ if (typeof entry !== "object" || entry === null) {
156
+ return fail(`policies.${kind} contains a non-object entry`);
157
+ }
158
+ const result = stampPolicy(source, kind, entry, seenIds);
159
+ if (result.error !== undefined || result.policy === undefined) {
160
+ return fail(result.error ?? "policy failed to stamp", result.policyId);
161
+ }
162
+ seenIds.add(result.policy.id);
163
+ stamped.push(result.policy);
164
+ }
165
+ }
166
+ return stamped;
167
+ }
168
+ /**
169
+ * Project raw per-source policy settings onto the canonical snapshot. The
170
+ * project source participates only when the project is trusted (ADR 0028);
171
+ * within a trusted project a project policy ID replaces the user policy
172
+ * with the same ID. This function never throws and never reads ambient
173
+ * state: identical inputs project identical snapshots.
174
+ */
175
+ export function loadPolicyConfiguration(input) {
176
+ const errors = [];
177
+ const userPolicies = loadSource("user", input.globalSettings, errors);
178
+ const projectPolicies = input.projectTrusted ? loadSource("project", input.projectSettings, errors) : [];
179
+ const verification = [];
180
+ const formatter = [];
181
+ const byId = new Map();
182
+ for (const policy of [...userPolicies, ...projectPolicies]) {
183
+ byId.set(policy.id, policy);
184
+ }
185
+ for (const policy of byId.values()) {
186
+ if (policy.kind === "verification") {
187
+ verification.push(policy);
188
+ }
189
+ else {
190
+ formatter.push(policy);
191
+ }
192
+ }
193
+ return { verification, formatter, errors };
194
+ }
195
+ //# sourceMappingURL=policy-loader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"policy-loader.js","sourceRoot":"","sources":["../../src/core/policy-loader.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,2EAA2E;AAC3E,MAAM,CAAC,MAAM,kBAAkB,GAAG,OAAO,CAAC;AAC1C,MAAM,CAAC,MAAM,cAAc,GAAG,OAAO,CAAC;AACtC,MAAM,CAAC,MAAM,wBAAwB,GAAG,OAAO,CAAC,CAAC,UAAU;AAC3D,MAAM,CAAC,MAAM,oBAAoB,GAAG,SAAS,CAAC,CAAC,QAAQ;AACvD,MAAM,CAAC,MAAM,wBAAwB,GAAG,KAAK,CAAC;AAC9C,MAAM,CAAC,MAAM,oBAAoB,GAAG,MAAM,CAAC;AAkE3C,MAAM,iBAAiB,GAAG,IAAI,GAAG,CAAC;IACjC,IAAI;IACJ,YAAY;IACZ,MAAM;IACN,KAAK;IACL,WAAW;IACX,WAAW;IACX,gBAAgB;IAChB,gBAAgB;IAChB,OAAO;IACP,YAAY;CACZ,CAAC,CAAC;AAEH,SAAS,mBAAmB,CAAC,IAAY,EAAW;IACnD,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;AAAA,CACtC;AAED;;;;GAIG;AACH,SAAS,WAAW,CACnB,MAAoB,EACpB,IAAkC,EAClC,KAAqB,EACrB,OAAoB,EAC4C;IAChE,MAAM,QAAQ,GAAG,OAAO,KAAK,CAAC,EAAE,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;IACrE,MAAM,IAAI,GAAG,CAAC,OAAe,EAAwC,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,CAAC;IAEvG,IAAI,OAAO,KAAK,CAAC,EAAE,KAAK,QAAQ,IAAI,KAAK,CAAC,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC3D,OAAO,IAAI,CAAC,+BAA+B,CAAC,CAAC;IAC9C,CAAC;IACD,IAAI,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,CAAC;QAC3B,OAAO,IAAI,CAAC,wBAAwB,KAAK,CAAC,EAAE,qBAAqB,CAAC,CAAC;IACpE,CAAC;IACD,IAAI,OAAO,KAAK,CAAC,UAAU,KAAK,QAAQ,IAAI,KAAK,CAAC,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC3E,OAAO,IAAI,CAAC,uCAAuC,CAAC,CAAC;IACtD,CAAC;IACD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,IAAI,KAAK,QAAQ,CAAC,EAAE,CAAC;QAClH,OAAO,IAAI,CAAC,2CAA2C,CAAC,CAAC;IAC1D,CAAC;IACD,IAAI,KAAK,CAAC,KAAK,KAAK,SAAS,IAAI,KAAK,CAAC,KAAK,KAAK,KAAK,EAAE,CAAC;QACxD,OAAO,IAAI,CAAC,sFAAsF,CAAC,CAAC;IACrG,CAAC;IACD,IAAI,KAAK,CAAC,GAAG,KAAK,SAAS,EAAE,CAAC;QAC7B,IAAI,OAAO,KAAK,CAAC,GAAG,KAAK,QAAQ,IAAI,KAAK,CAAC,GAAG,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC7D,OAAO,IAAI,CAAC,4EAA4E,CAAC,CAAC;QAC3F,CAAC;QACD,IAAI,KAAK,CAAC,GAAG,KAAK,WAAW,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,mBAAmB,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;YAChG,OAAO,IAAI,CAAC,yEAAyE,CAAC,CAAC;QACxF,CAAC;IACF,CAAC;IACD,IAAI,KAAK,CAAC,SAAS,KAAK,SAAS,EAAE,CAAC;QACnC,IACC,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,SAAS,CAAC;YAC/B,KAAK,CAAC,SAAS,CAAC,MAAM,KAAK,CAAC;YAC5B,KAAK,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,IAAI,mBAAmB,CAAC,IAAI,CAAC,CAAC,EACzG,CAAC;YACF,OAAO,IAAI,CAAC,+EAA+E,CAAC,CAAC;QAC9F,CAAC;IACF,CAAC;IAED,MAAM,SAAS,GAAG,CAAC,KAAK,CAAC,SAAS,IAAI,kBAAkB,CAAW,CAAC;IACpE,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,SAAS,CAAC,IAAI,SAAS,IAAI,CAAC,IAAI,SAAS,GAAG,cAAc,EAAE,CAAC;QAClF,OAAO,IAAI,CAAC,wDAAwD,cAAc,EAAE,CAAC,CAAC;IACvF,CAAC;IACD,MAAM,cAAc,GAAG,CAAC,KAAK,CAAC,cAAc,IAAI,wBAAwB,CAAW,CAAC;IACpF,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,cAAc,IAAI,CAAC,IAAI,cAAc,GAAG,oBAAoB,EAAE,CAAC;QACvG,OAAO,IAAI,CAAC,6DAA6D,oBAAoB,EAAE,CAAC,CAAC;IAClG,CAAC;IACD,MAAM,cAAc,GAAG,CAAC,KAAK,CAAC,cAAc,IAAI,wBAAwB,CAAW,CAAC;IACpF,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,cAAc,IAAI,CAAC,IAAI,cAAc,GAAG,oBAAoB,EAAE,CAAC;QACvG,OAAO,IAAI,CAAC,6DAA6D,oBAAoB,EAAE,CAAC,CAAC;IAClG,CAAC;IAED,MAAM,UAAU,GAAG,CAAC,KAAK,CAAC,UAAU,IAAI,KAAK,CAAqB,CAAC;IACnE,IAAI,UAAU,KAAK,OAAO,IAAI,UAAU,KAAK,KAAK,IAAI,UAAU,KAAK,MAAM,EAAE,CAAC;QAC7E,OAAO,IAAI,CAAC,8CAA8C,CAAC,CAAC;IAC7D,CAAC;IAED,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QACxC,IACC,CAAC,iBAAiB,CAAC,GAAG,CAAC,KAAK,CAAC;YAC7B,CAAC,CAAC,IAAI,KAAK,cAAc,IAAI,KAAK,KAAK,kBAAkB,CAAC;YAC1D,CAAC,CAAC,IAAI,KAAK,WAAW,IAAI,KAAK,KAAK,eAAe,CAAC,EACnD,CAAC;YACF,OAAO,IAAI,CAAC,yBAAyB,KAAK,GAAG,CAAC,CAAC;QAChD,CAAC;IACF,CAAC;IAED,MAAM,IAAI,GAA2E;QACpF,EAAE,EAAE,KAAK,CAAC,EAAE;QACZ,UAAU,EAAE,KAAK,CAAC,UAAU;QAC5B,IAAI,EAAE,KAAK,CAAC,IAAgB;QAC5B,GAAG,EAAG,KAAK,CAAC,GAA0B,IAAI,WAAW;QACrD,GAAG,CAAC,KAAK,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,KAAK,CAAC,SAAqB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACpF,SAAS;QACT,cAAc;QACd,cAAc;QACd,KAAK,EAAE,KAAK;QACZ,UAAU;QACV,aAAa,EAAE,MAAM;KACrB,CAAC;IAEF,IAAI,IAAI,KAAK,cAAc,EAAE,CAAC;QAC7B,IAAI,KAAK,CAAC,gBAAgB,KAAK,SAAS,IAAI,OAAO,KAAK,CAAC,gBAAgB,KAAK,SAAS,EAAE,CAAC;YACzF,OAAO,IAAI,CAAC,oCAAoC,CAAC,CAAC;QACnD,CAAC;QACD,OAAO,EAAE,MAAM,EAAE,EAAE,GAAG,IAAI,EAAE,IAAI,EAAE,gBAAgB,EAAG,KAAK,CAAC,gBAAwC,IAAI,KAAK,EAAE,EAAE,CAAC;IAClH,CAAC;IAED,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,aAAa,CAAC,IAAI,KAAK,CAAC,aAAa,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC7E,OAAO,IAAI,CAAC,gFAAgF,CAAC,CAAC;IAC/F,CAAC;IACD,IAAI,KAAK,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,IAAI,mBAAmB,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;QACpH,OAAO,IAAI,CAAC,mFAAmF,CAAC,CAAC;IAClG,CAAC;IACD,OAAO,EAAE,MAAM,EAAE,EAAE,GAAG,IAAI,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,aAAa,EAAE,KAAK,CAAC,aAAyB,EAAE,EAAE,CAAC;AAAA,CACzG;AAED;;;GAGG;AACH,SAAS,UAAU,CAAC,MAAoB,EAAE,GAAY,EAAE,MAAyB,EAAmB;IACnG,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;QACvC,OAAO,EAAE,CAAC;IACX,CAAC;IACD,MAAM,IAAI,GAAG,CAAC,OAAe,EAAE,QAAiB,EAAM,EAAE,CAAC;QACxD,MAAM,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC;QAC3C,OAAO,EAAE,CAAC;IAAA,CACV,CAAC;IACF,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;QAC7B,OAAO,IAAI,CAAC,4BAA4B,CAAC,CAAC;IAC3C,CAAC;IACD,MAAM,QAAQ,GAAG,GAA8B,CAAC;IAChD,IAAI,QAAQ,CAAC,aAAa,KAAK,CAAC,EAAE,CAAC;QAClC,OAAO,IAAI,CAAC,sCAAsC,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC;IAC7F,CAAC;IAED,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;IAClC,MAAM,OAAO,GAAoB,EAAE,CAAC;IACpC,KAAK,MAAM,IAAI,IAAI,CAAC,cAAc,EAAE,WAAW,CAAU,EAAE,CAAC;QAC3D,MAAM,OAAO,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;QAC/B,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;YAC3B,SAAS;QACV,CAAC;QACD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;YAC7B,OAAO,IAAI,CAAC,YAAY,IAAI,mBAAmB,CAAC,CAAC;QAClD,CAAC;QACD,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YAC7B,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;gBACjD,OAAO,IAAI,CAAC,YAAY,IAAI,8BAA8B,CAAC,CAAC;YAC7D,CAAC;YACD,MAAM,MAAM,GAAG,WAAW,CAAC,MAAM,EAAE,IAAI,EAAE,KAAuB,EAAE,OAAO,CAAC,CAAC;YAC3E,IAAI,MAAM,CAAC,KAAK,KAAK,SAAS,IAAI,MAAM,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;gBAC/D,OAAO,IAAI,CAAC,MAAM,CAAC,KAAK,IAAI,wBAAwB,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC;YACxE,CAAC;YACD,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;YAC9B,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAC7B,CAAC;IACF,CAAC;IACD,OAAO,OAAO,CAAC;AAAA,CACf;AAED;;;;;;GAMG;AACH,MAAM,UAAU,uBAAuB,CAAC,KAA+B,EAAkB;IACxF,MAAM,MAAM,GAAsB,EAAE,CAAC;IACrC,MAAM,YAAY,GAAG,UAAU,CAAC,MAAM,EAAE,KAAK,CAAC,cAAc,EAAE,MAAM,CAAC,CAAC;IACtE,MAAM,eAAe,GAAG,KAAK,CAAC,cAAc,CAAC,CAAC,CAAC,UAAU,CAAC,SAAS,EAAE,KAAK,CAAC,eAAe,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAEzG,MAAM,YAAY,GAAyB,EAAE,CAAC;IAC9C,MAAM,SAAS,GAAsB,EAAE,CAAC;IACxC,MAAM,IAAI,GAAG,IAAI,GAAG,EAAyB,CAAC;IAC9C,KAAK,MAAM,MAAM,IAAI,CAAC,GAAG,YAAY,EAAE,GAAG,eAAe,CAAC,EAAE,CAAC;QAC5D,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;IAC7B,CAAC;IACD,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC;QACpC,IAAI,MAAM,CAAC,IAAI,KAAK,cAAc,EAAE,CAAC;YACpC,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC3B,CAAC;aAAM,CAAC;YACP,SAAS,CAAC,IAAI,CAAC,MAAyB,CAAC,CAAC;QAC3C,CAAC;IACF,CAAC;IACD,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC;AAAA,CAC3C","sourcesContent":["/**\n * VF.2: the ONE projection from policy settings to runtime policies (spec\n * 2026-09-01-configured-verification-and-formatting.md § 1; ADR 0028; ADR\n * 0010's one-projection rule applied to configured commands). Nothing else\n * in the codebase re-derives defaults, bounds, or trust stamps — surfaces\n * consume `loadPolicyConfiguration()` output, and the executor (VF.3)\n * consumes these policies verbatim.\n *\n * Strictness contract: any validation error inside a source drops that\n * entire source and surfaces structured errors. Half-applied policies are\n * worse than absent ones because they look configured while running under\n * different bounds than the user wrote. A missing `policies` key stays\n * inert: no policies, no errors, no runtime.\n */\n\n/** Hard bounds. Caps exist so a typo cannot configure an unbounded run. */\nexport const DEFAULT_TIMEOUT_MS = 120_000;\nexport const MAX_TIMEOUT_MS = 600_000;\nexport const DEFAULT_MAX_OUTPUT_BYTES = 262_144; // 256 KiB\nexport const MAX_MAX_OUTPUT_BYTES = 1_048_576; // 1 MiB\nexport const DEFAULT_MAX_OUTPUT_LINES = 2_000;\nexport const MAX_MAX_OUTPUT_LINES = 10_000;\n\n/** Capability names from the shared tool-contract vocabulary (ADR 0010). */\nexport type PolicyCapability = \"exec\" | \"fs.write\";\nexport type PolicyPermission = \"allow\" | \"ask\" | \"deny\";\nexport type PolicySource = \"user\" | \"project\";\n\nexport interface CommandPolicy {\n\tid: string;\n\texecutable: string;\n\targv: string[];\n\tcwd: \"workspace\" | string;\n\tpathScope?: string[];\n\ttimeoutMs: number;\n\tmaxOutputBytes: number;\n\tmaxOutputLines: number;\n\tshell: false;\n\tpermission: PolicyPermission;\n\ttrustedSource: PolicySource;\n}\n\nexport interface VerificationPolicy extends CommandPolicy {\n\tkind: \"verification\";\n\tblocksCompletion: boolean;\n}\n\nexport interface FormatterPolicy extends CommandPolicy {\n\tkind: \"formatter\";\n\tmutatesFiles: true;\n\tdeclaredPaths: string[];\n}\n\ntype StampedPolicy = VerificationPolicy | FormatterPolicy;\n\nexport interface PolicyLoadError {\n\tsource: PolicySource;\n\t/** Policy ID when the error is attributable to one entry. */\n\tpolicyId?: string;\n\tmessage: string;\n}\n\nexport interface PolicySnapshot {\n\tverification: VerificationPolicy[];\n\tformatter: FormatterPolicy[];\n\terrors: PolicyLoadError[];\n}\n\nexport interface PolicyConfigurationInput {\n\tglobalSettings?: unknown;\n\tprojectSettings?: unknown;\n\tprojectTrusted: boolean;\n}\n\ninterface RawPolicyEntry extends Record<string, unknown> {\n\tid?: unknown;\n\texecutable?: unknown;\n\targv?: unknown;\n\tcwd?: unknown;\n\tpathScope?: unknown;\n\ttimeoutMs?: unknown;\n\tmaxOutputBytes?: unknown;\n\tmaxOutputLines?: unknown;\n\tshell?: unknown;\n\tpermission?: unknown;\n}\n\nconst KNOWN_BASE_FIELDS = new Set([\n\t\"id\",\n\t\"executable\",\n\t\"argv\",\n\t\"cwd\",\n\t\"pathScope\",\n\t\"timeoutMs\",\n\t\"maxOutputBytes\",\n\t\"maxOutputLines\",\n\t\"shell\",\n\t\"permission\",\n]);\n\nfunction hasTraversalSegment(path: string): boolean {\n\treturn path.split(\"/\").includes(\"..\");\n}\n\n/**\n * Validate one raw entry and stamp the canonical runtime shape. Returns\n * either the policy or an error message; the caller drops the source on any\n * error. Field-by-field so error messages name the offending field.\n */\nfunction stampPolicy(\n\tsource: PolicySource,\n\tkind: \"verification\" | \"formatter\",\n\tentry: RawPolicyEntry,\n\tseenIds: Set<string>,\n): { policy?: StampedPolicy; error?: string; policyId?: string } {\n\tconst policyId = typeof entry.id === \"string\" ? entry.id : undefined;\n\tconst fail = (message: string): { error: string; policyId?: string } => ({ error: message, policyId });\n\n\tif (typeof entry.id !== \"string\" || entry.id.length === 0) {\n\t\treturn fail(\"id must be a non-empty string\");\n\t}\n\tif (seenIds.has(entry.id)) {\n\t\treturn fail(`duplicate policy id \"${entry.id}\" within one source`);\n\t}\n\tif (typeof entry.executable !== \"string\" || entry.executable.length === 0) {\n\t\treturn fail(\"executable must be a non-empty string\");\n\t}\n\tif (!Array.isArray(entry.argv) || entry.argv.length === 0 || entry.argv.some((part) => typeof part !== \"string\")) {\n\t\treturn fail(\"argv must be a non-empty array of strings\");\n\t}\n\tif (entry.shell !== undefined && entry.shell !== false) {\n\t\treturn fail(\"shell must be literal false; shell execution is not a supported policy configuration\");\n\t}\n\tif (entry.cwd !== undefined) {\n\t\tif (typeof entry.cwd !== \"string\" || entry.cwd.length === 0) {\n\t\t\treturn fail('cwd must be the literal \"workspace\" or a non-empty workspace-relative path');\n\t\t}\n\t\tif (entry.cwd !== \"workspace\" && (entry.cwd.startsWith(\"/\") || hasTraversalSegment(entry.cwd))) {\n\t\t\treturn fail('cwd must stay inside the workspace: no absolute paths, no \"..\" segments');\n\t\t}\n\t}\n\tif (entry.pathScope !== undefined) {\n\t\tif (\n\t\t\t!Array.isArray(entry.pathScope) ||\n\t\t\tentry.pathScope.length === 0 ||\n\t\t\tentry.pathScope.some((part) => typeof part !== \"string\" || part.length === 0 || hasTraversalSegment(part))\n\t\t) {\n\t\t\treturn fail('pathScope must be non-empty workspace-relative patterns without \"..\" segments');\n\t\t}\n\t}\n\n\tconst timeoutMs = (entry.timeoutMs ?? DEFAULT_TIMEOUT_MS) as number;\n\tif (!Number.isInteger(timeoutMs) || timeoutMs <= 0 || timeoutMs > MAX_TIMEOUT_MS) {\n\t\treturn fail(`timeoutMs must be a positive integer no greater than ${MAX_TIMEOUT_MS}`);\n\t}\n\tconst maxOutputBytes = (entry.maxOutputBytes ?? DEFAULT_MAX_OUTPUT_BYTES) as number;\n\tif (!Number.isInteger(maxOutputBytes) || maxOutputBytes <= 0 || maxOutputBytes > MAX_MAX_OUTPUT_BYTES) {\n\t\treturn fail(`maxOutputBytes must be a positive integer no greater than ${MAX_MAX_OUTPUT_BYTES}`);\n\t}\n\tconst maxOutputLines = (entry.maxOutputLines ?? DEFAULT_MAX_OUTPUT_LINES) as number;\n\tif (!Number.isInteger(maxOutputLines) || maxOutputLines <= 0 || maxOutputLines > MAX_MAX_OUTPUT_LINES) {\n\t\treturn fail(`maxOutputLines must be a positive integer no greater than ${MAX_MAX_OUTPUT_LINES}`);\n\t}\n\n\tconst permission = (entry.permission ?? \"ask\") as PolicyPermission;\n\tif (permission !== \"allow\" && permission !== \"ask\" && permission !== \"deny\") {\n\t\treturn fail('permission must be \"allow\", \"ask\", or \"deny\"');\n\t}\n\n\tfor (const field of Object.keys(entry)) {\n\t\tif (\n\t\t\t!KNOWN_BASE_FIELDS.has(field) &&\n\t\t\t!(kind === \"verification\" && field === \"blocksCompletion\") &&\n\t\t\t!(kind === \"formatter\" && field === \"declaredPaths\")\n\t\t) {\n\t\t\treturn fail(`unknown policy field \"${field}\"`);\n\t\t}\n\t}\n\n\tconst base: Omit<CommandPolicy, \"trustedSource\"> & { trustedSource: PolicySource } = {\n\t\tid: entry.id,\n\t\texecutable: entry.executable,\n\t\targv: entry.argv as string[],\n\t\tcwd: (entry.cwd as string | undefined) ?? \"workspace\",\n\t\t...(entry.pathScope !== undefined ? { pathScope: entry.pathScope as string[] } : {}),\n\t\ttimeoutMs,\n\t\tmaxOutputBytes,\n\t\tmaxOutputLines,\n\t\tshell: false,\n\t\tpermission,\n\t\ttrustedSource: source,\n\t};\n\n\tif (kind === \"verification\") {\n\t\tif (entry.blocksCompletion !== undefined && typeof entry.blocksCompletion !== \"boolean\") {\n\t\t\treturn fail(\"blocksCompletion must be a boolean\");\n\t\t}\n\t\treturn { policy: { ...base, kind, blocksCompletion: (entry.blocksCompletion as boolean | undefined) ?? false } };\n\t}\n\n\tif (!Array.isArray(entry.declaredPaths) || entry.declaredPaths.length === 0) {\n\t\treturn fail(\"declaredPaths is required for formatter policies and must be a non-empty array\");\n\t}\n\tif (entry.declaredPaths.some((part) => typeof part !== \"string\" || part.length === 0 || hasTraversalSegment(part))) {\n\t\treturn fail('declaredPaths must be non-empty workspace-relative patterns without \"..\" segments');\n\t}\n\treturn { policy: { ...base, kind, mutatesFiles: true, declaredPaths: entry.declaredPaths as string[] } };\n}\n\n/**\n * Load one source. Returns the stamped policies plus errors; on ANY error\n * the source contributes nothing (fail closed, all-or-nothing per source).\n */\nfunction loadSource(source: PolicySource, raw: unknown, errors: PolicyLoadError[]): StampedPolicy[] {\n\tif (raw === undefined || raw === null) {\n\t\treturn [];\n\t}\n\tconst fail = (message: string, policyId?: string): [] => {\n\t\terrors.push({ source, policyId, message });\n\t\treturn [];\n\t};\n\tif (typeof raw !== \"object\") {\n\t\treturn fail(\"policies must be an object\");\n\t}\n\tconst policies = raw as Record<string, unknown>;\n\tif (policies.schemaVersion !== 1) {\n\t\treturn fail(`unsupported policies schemaVersion ${JSON.stringify(policies.schemaVersion)}`);\n\t}\n\n\tconst seenIds = new Set<string>();\n\tconst stamped: StampedPolicy[] = [];\n\tfor (const kind of [\"verification\", \"formatter\"] as const) {\n\t\tconst entries = policies[kind];\n\t\tif (entries === undefined) {\n\t\t\tcontinue;\n\t\t}\n\t\tif (!Array.isArray(entries)) {\n\t\t\treturn fail(`policies.${kind} must be an array`);\n\t\t}\n\t\tfor (const entry of entries) {\n\t\t\tif (typeof entry !== \"object\" || entry === null) {\n\t\t\t\treturn fail(`policies.${kind} contains a non-object entry`);\n\t\t\t}\n\t\t\tconst result = stampPolicy(source, kind, entry as RawPolicyEntry, seenIds);\n\t\t\tif (result.error !== undefined || result.policy === undefined) {\n\t\t\t\treturn fail(result.error ?? \"policy failed to stamp\", result.policyId);\n\t\t\t}\n\t\t\tseenIds.add(result.policy.id);\n\t\t\tstamped.push(result.policy);\n\t\t}\n\t}\n\treturn stamped;\n}\n\n/**\n * Project raw per-source policy settings onto the canonical snapshot. The\n * project source participates only when the project is trusted (ADR 0028);\n * within a trusted project a project policy ID replaces the user policy\n * with the same ID. This function never throws and never reads ambient\n * state: identical inputs project identical snapshots.\n */\nexport function loadPolicyConfiguration(input: PolicyConfigurationInput): PolicySnapshot {\n\tconst errors: PolicyLoadError[] = [];\n\tconst userPolicies = loadSource(\"user\", input.globalSettings, errors);\n\tconst projectPolicies = input.projectTrusted ? loadSource(\"project\", input.projectSettings, errors) : [];\n\n\tconst verification: VerificationPolicy[] = [];\n\tconst formatter: FormatterPolicy[] = [];\n\tconst byId = new Map<string, StampedPolicy>();\n\tfor (const policy of [...userPolicies, ...projectPolicies]) {\n\t\tbyId.set(policy.id, policy);\n\t}\n\tfor (const policy of byId.values()) {\n\t\tif (policy.kind === \"verification\") {\n\t\t\tverification.push(policy);\n\t\t} else {\n\t\t\tformatter.push(policy as FormatterPolicy);\n\t\t}\n\t}\n\treturn { verification, formatter, errors };\n}\n"]}
@@ -1,7 +1,7 @@
1
1
  import type { LoadExtensionsResult, ProjectTrustContext } from "./extensions/types.ts";
2
2
  import type { DefaultProjectTrust } from "./settings-manager.ts";
3
3
  import { type ProjectTrustStore } from "./trust-manager.ts";
4
- export type AppMode = "interactive" | "print" | "json" | "rpc";
4
+ export type AppMode = "interactive" | "print" | "json" | "rpc" | "acp";
5
5
  export interface ResolveProjectTrustedOptions {
6
6
  cwd: string;
7
7
  trustStore: ProjectTrustStore;
@@ -1 +1 @@
1
- {"version":3,"file":"project-trust.d.ts","sourceRoot":"","sources":["../../src/core/project-trust.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AACvF,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AACjE,OAAO,EAIN,KAAK,iBAAiB,EACtB,MAAM,oBAAoB,CAAC;AAE5B,MAAM,MAAM,OAAO,GAAG,aAAa,GAAG,OAAO,GAAG,MAAM,GAAG,KAAK,CAAC;AAE/D,MAAM,WAAW,4BAA4B;IAC5C,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,EAAE,iBAAiB,CAAC;IAC9B,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,mBAAmB,CAAC,EAAE,mBAAmB,CAAC;IAC1C,gBAAgB,CAAC,EAAE,oBAAoB,CAAC;IACxC,mBAAmB,EAAE,mBAAmB,CAAC;IACzC,gBAAgB,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CAC7C;AAwBD,wBAAsB,qBAAqB,CAAC,OAAO,EAAE,4BAA4B,GAAG,OAAO,CAAC,OAAO,CAAC,CAkDnG","sourcesContent":["import { CONFIG_DIR_NAME } from \"../config.ts\";\nimport { emitProjectTrustEvent } from \"./extensions/runner.ts\";\nimport type { LoadExtensionsResult, ProjectTrustContext } from \"./extensions/types.ts\";\nimport type { DefaultProjectTrust } from \"./settings-manager.ts\";\nimport {\n\tgetProjectTrustOptions,\n\thasTrustRequiringProjectResources,\n\ttype ProjectTrustOption,\n\ttype ProjectTrustStore,\n} from \"./trust-manager.ts\";\n\nexport type AppMode = \"interactive\" | \"print\" | \"json\" | \"rpc\";\n\nexport interface ResolveProjectTrustedOptions {\n\tcwd: string;\n\ttrustStore: ProjectTrustStore;\n\ttrustOverride?: boolean;\n\tdefaultProjectTrust?: DefaultProjectTrust;\n\textensionsResult?: LoadExtensionsResult;\n\tprojectTrustContext: ProjectTrustContext;\n\tonExtensionError?: (message: string) => void;\n}\n\nfunction formatProjectTrustPrompt(cwd: string): string {\n\treturn `Trust project folder?\\n${cwd}\\n\\nThis allows Apex Code to load ${CONFIG_DIR_NAME} settings and resources, install missing project packages, execute project extensions, and start configured language servers.`;\n}\n\nasync function selectProjectTrustOption(\n\tcwd: string,\n\tctx: ProjectTrustContext,\n): Promise<ProjectTrustOption | undefined> {\n\tconst options = getProjectTrustOptions(cwd, { includeSessionOnly: true });\n\tconst selected = await ctx.ui.select(\n\t\tformatProjectTrustPrompt(cwd),\n\t\toptions.map((option) => option.label),\n\t);\n\treturn options.find((option) => option.label === selected);\n}\n\nfunction saveProjectTrustPromptResult(trustStore: ProjectTrustStore, result: ProjectTrustOption): void {\n\tif (result.updates.length > 0) {\n\t\ttrustStore.setMany(result.updates);\n\t}\n}\n\nexport async function resolveProjectTrusted(options: ResolveProjectTrustedOptions): Promise<boolean> {\n\tif (options.trustOverride !== undefined) {\n\t\treturn options.trustOverride;\n\t}\n\tif (!hasTrustRequiringProjectResources(options.cwd)) {\n\t\treturn true;\n\t}\n\n\tif (options.extensionsResult) {\n\t\tconst { result, errors } = await emitProjectTrustEvent(\n\t\t\toptions.extensionsResult,\n\t\t\t{ type: \"project_trust\", cwd: options.cwd },\n\t\t\toptions.projectTrustContext,\n\t\t);\n\t\tfor (const error of errors) {\n\t\t\toptions.onExtensionError?.(`Extension \"${error.extensionPath}\" project_trust error: ${error.error}`);\n\t\t}\n\t\tif (result) {\n\t\t\tconst trusted = result.trusted === \"yes\";\n\t\t\tif (result.remember === true) {\n\t\t\t\toptions.trustStore.set(options.cwd, trusted);\n\t\t\t}\n\t\t\treturn trusted;\n\t\t}\n\t}\n\n\tconst decision = options.trustStore.get(options.cwd);\n\tif (decision !== null) {\n\t\treturn decision;\n\t}\n\n\tswitch (options.defaultProjectTrust ?? \"ask\") {\n\t\tcase \"always\":\n\t\t\treturn true;\n\t\tcase \"never\":\n\t\t\treturn false;\n\t\tcase \"ask\":\n\t\t\tbreak;\n\t}\n\n\tif (!options.projectTrustContext.hasUI) {\n\t\treturn false;\n\t}\n\n\tconst selected = await selectProjectTrustOption(options.cwd, options.projectTrustContext);\n\tif (selected !== undefined) {\n\t\tsaveProjectTrustPromptResult(options.trustStore, selected);\n\t\treturn selected.trusted;\n\t}\n\treturn false;\n}\n"]}
1
+ {"version":3,"file":"project-trust.d.ts","sourceRoot":"","sources":["../../src/core/project-trust.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AACvF,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AACjE,OAAO,EAIN,KAAK,iBAAiB,EACtB,MAAM,oBAAoB,CAAC;AAE5B,MAAM,MAAM,OAAO,GAAG,aAAa,GAAG,OAAO,GAAG,MAAM,GAAG,KAAK,GAAG,KAAK,CAAC;AAEvE,MAAM,WAAW,4BAA4B;IAC5C,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,EAAE,iBAAiB,CAAC;IAC9B,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,mBAAmB,CAAC,EAAE,mBAAmB,CAAC;IAC1C,gBAAgB,CAAC,EAAE,oBAAoB,CAAC;IACxC,mBAAmB,EAAE,mBAAmB,CAAC;IACzC,gBAAgB,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CAC7C;AAwBD,wBAAsB,qBAAqB,CAAC,OAAO,EAAE,4BAA4B,GAAG,OAAO,CAAC,OAAO,CAAC,CAkDnG","sourcesContent":["import { CONFIG_DIR_NAME } from \"../config.ts\";\nimport { emitProjectTrustEvent } from \"./extensions/runner.ts\";\nimport type { LoadExtensionsResult, ProjectTrustContext } from \"./extensions/types.ts\";\nimport type { DefaultProjectTrust } from \"./settings-manager.ts\";\nimport {\n\tgetProjectTrustOptions,\n\thasTrustRequiringProjectResources,\n\ttype ProjectTrustOption,\n\ttype ProjectTrustStore,\n} from \"./trust-manager.ts\";\n\nexport type AppMode = \"interactive\" | \"print\" | \"json\" | \"rpc\" | \"acp\";\n\nexport interface ResolveProjectTrustedOptions {\n\tcwd: string;\n\ttrustStore: ProjectTrustStore;\n\ttrustOverride?: boolean;\n\tdefaultProjectTrust?: DefaultProjectTrust;\n\textensionsResult?: LoadExtensionsResult;\n\tprojectTrustContext: ProjectTrustContext;\n\tonExtensionError?: (message: string) => void;\n}\n\nfunction formatProjectTrustPrompt(cwd: string): string {\n\treturn `Trust project folder?\\n${cwd}\\n\\nThis allows Apex Code to load ${CONFIG_DIR_NAME} settings and resources, install missing project packages, execute project extensions, and start configured language servers.`;\n}\n\nasync function selectProjectTrustOption(\n\tcwd: string,\n\tctx: ProjectTrustContext,\n): Promise<ProjectTrustOption | undefined> {\n\tconst options = getProjectTrustOptions(cwd, { includeSessionOnly: true });\n\tconst selected = await ctx.ui.select(\n\t\tformatProjectTrustPrompt(cwd),\n\t\toptions.map((option) => option.label),\n\t);\n\treturn options.find((option) => option.label === selected);\n}\n\nfunction saveProjectTrustPromptResult(trustStore: ProjectTrustStore, result: ProjectTrustOption): void {\n\tif (result.updates.length > 0) {\n\t\ttrustStore.setMany(result.updates);\n\t}\n}\n\nexport async function resolveProjectTrusted(options: ResolveProjectTrustedOptions): Promise<boolean> {\n\tif (options.trustOverride !== undefined) {\n\t\treturn options.trustOverride;\n\t}\n\tif (!hasTrustRequiringProjectResources(options.cwd)) {\n\t\treturn true;\n\t}\n\n\tif (options.extensionsResult) {\n\t\tconst { result, errors } = await emitProjectTrustEvent(\n\t\t\toptions.extensionsResult,\n\t\t\t{ type: \"project_trust\", cwd: options.cwd },\n\t\t\toptions.projectTrustContext,\n\t\t);\n\t\tfor (const error of errors) {\n\t\t\toptions.onExtensionError?.(`Extension \"${error.extensionPath}\" project_trust error: ${error.error}`);\n\t\t}\n\t\tif (result) {\n\t\t\tconst trusted = result.trusted === \"yes\";\n\t\t\tif (result.remember === true) {\n\t\t\t\toptions.trustStore.set(options.cwd, trusted);\n\t\t\t}\n\t\t\treturn trusted;\n\t\t}\n\t}\n\n\tconst decision = options.trustStore.get(options.cwd);\n\tif (decision !== null) {\n\t\treturn decision;\n\t}\n\n\tswitch (options.defaultProjectTrust ?? \"ask\") {\n\t\tcase \"always\":\n\t\t\treturn true;\n\t\tcase \"never\":\n\t\t\treturn false;\n\t\tcase \"ask\":\n\t\t\tbreak;\n\t}\n\n\tif (!options.projectTrustContext.hasUI) {\n\t\treturn false;\n\t}\n\n\tconst selected = await selectProjectTrustOption(options.cwd, options.projectTrustContext);\n\tif (selected !== undefined) {\n\t\tsaveProjectTrustPromptResult(options.trustStore, selected);\n\t\treturn selected.trusted;\n\t}\n\treturn false;\n}\n"]}