@neurocode-ai/core 1.18.8

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 (490) hide show
  1. package/bin/neurocode +199 -0
  2. package/bunfig.toml +2 -0
  3. package/drizzle.config.ts +10 -0
  4. package/package.json +131 -0
  5. package/schema.json +2071 -0
  6. package/script/fix-node-pty.ts +28 -0
  7. package/script/migration.ts +196 -0
  8. package/src/account/sql.ts +39 -0
  9. package/src/account.ts +101 -0
  10. package/src/agent.ts +111 -0
  11. package/src/aisdk.ts +235 -0
  12. package/src/background-job.ts +365 -0
  13. package/src/catalog.ts +301 -0
  14. package/src/command.ts +64 -0
  15. package/src/config/agent.ts +25 -0
  16. package/src/config/attachments.ts +15 -0
  17. package/src/config/command.ts +12 -0
  18. package/src/config/compaction.ts +15 -0
  19. package/src/config/experimental.ts +18 -0
  20. package/src/config/formatter.ts +12 -0
  21. package/src/config/lsp.ts +18 -0
  22. package/src/config/markdown.ts +36 -0
  23. package/src/config/mcp.ts +48 -0
  24. package/src/config/plugin/agent.ts +179 -0
  25. package/src/config/plugin/command.ts +83 -0
  26. package/src/config/plugin/external.ts +91 -0
  27. package/src/config/plugin/provider.ts +113 -0
  28. package/src/config/plugin/reference.ts +70 -0
  29. package/src/config/plugin/skill.ts +50 -0
  30. package/src/config/plugin.ts +13 -0
  31. package/src/config/provider.ts +71 -0
  32. package/src/config/reference.ts +22 -0
  33. package/src/config/tool-output.ts +9 -0
  34. package/src/config/watcher.ts +7 -0
  35. package/src/config.ts +227 -0
  36. package/src/control-plane/move-session.ts +148 -0
  37. package/src/control-plane/workspace.sql.ts +20 -0
  38. package/src/credential/sql.ts +14 -0
  39. package/src/credential.ts +138 -0
  40. package/src/cross-spawn-spawner.ts +507 -0
  41. package/src/data-migration.sql.ts +6 -0
  42. package/src/database/database.ts +57 -0
  43. package/src/database/migration/20260127222353_familiar_lady_ursula.ts +107 -0
  44. package/src/database/migration/20260211171708_add_project_commands.ts +11 -0
  45. package/src/database/migration/20260213144116_wakeful_the_professor.ts +23 -0
  46. package/src/database/migration/20260225215848_workspace.ts +19 -0
  47. package/src/database/migration/20260227213759_add_session_workspace_id.ts +12 -0
  48. package/src/database/migration/20260228203230_blue_harpoon.ts +30 -0
  49. package/src/database/migration/20260303231226_add_workspace_fields.ts +15 -0
  50. package/src/database/migration/20260309230000_move_org_to_state.ts +15 -0
  51. package/src/database/migration/20260312043431_session_message_cursor.ts +16 -0
  52. package/src/database/migration/20260323234822_events.ts +26 -0
  53. package/src/database/migration/20260410174513_workspace-name.ts +29 -0
  54. package/src/database/migration/20260413175956_chief_energizer.ts +24 -0
  55. package/src/database/migration/20260423070820_add_icon_url_override.ts +14 -0
  56. package/src/database/migration/20260427172553_slow_nightmare.ts +30 -0
  57. package/src/database/migration/20260428004200_add_session_path.ts +11 -0
  58. package/src/database/migration/20260501142318_next_venus.ts +12 -0
  59. package/src/database/migration/20260504145000_add_sync_owner.ts +11 -0
  60. package/src/database/migration/20260507164347_add_workspace_time.ts +11 -0
  61. package/src/database/migration/20260510033149_session_usage.ts +56 -0
  62. package/src/database/migration/20260511000411_data_migration_state.ts +16 -0
  63. package/src/database/migration/20260511173437_session-metadata.ts +16 -0
  64. package/src/database/migration/20260601010001_normalize_storage_paths.ts +22 -0
  65. package/src/database/migration/20260601202201_amazing_prowler.ts +11 -0
  66. package/src/database/migration/20260602002951_lowly_union_jack.ts +24 -0
  67. package/src/database/migration/20260602182828_add_project_directories.ts +20 -0
  68. package/src/database/migration/20260603001617_session_message_projection_indexes.ts +19 -0
  69. package/src/database/migration/20260603040000_session_message_projection_order.ts +19 -0
  70. package/src/database/migration/20260603141458_session_input_inbox.ts +25 -0
  71. package/src/database/migration/20260603160727_jittery_ezekiel_stane.ts +20 -0
  72. package/src/database/migration/20260604172448_event_sourced_session_input.ts +47 -0
  73. package/src/database/migration/20260605003541_add_session_context_snapshot.ts +21 -0
  74. package/src/database/migration/20260605042240_add_context_epoch_agent.ts +11 -0
  75. package/src/database/migration/20260611035744_credential.ts +25 -0
  76. package/src/database/migration/20260611192811_lush_chimera.ts +25 -0
  77. package/src/database/migration/20260612174303_project_dir_strategy.ts +29 -0
  78. package/src/database/migration/20260622142730_simplify_session_context_epoch.ts +13 -0
  79. package/src/database/migration/20260622170816_reset_v2_session_state.ts +17 -0
  80. package/src/database/migration/20260622202450_simplify_session_input.ts +17 -0
  81. package/src/database/migration.gen.ts +44 -0
  82. package/src/database/migration.ts +81 -0
  83. package/src/database/path.ts +91 -0
  84. package/src/database/schema.gen.ts +274 -0
  85. package/src/database/schema.sql.ts +10 -0
  86. package/src/database/sqlite.bun.ts +183 -0
  87. package/src/database/sqlite.node.ts +178 -0
  88. package/src/database/sqlite.ts +8 -0
  89. package/src/effect/app-node-builder.ts +23 -0
  90. package/src/effect/app-node-platform.ts +18 -0
  91. package/src/effect/app-node.ts +14 -0
  92. package/src/effect/dfdf +1 -0
  93. package/src/effect/keyed-mutex.ts +45 -0
  94. package/src/effect/layer-node.ts +333 -0
  95. package/src/effect/memo-map.ts +3 -0
  96. package/src/effect/runtime.ts +21 -0
  97. package/src/effect/service-use.ts +43 -0
  98. package/src/event/sql.ts +25 -0
  99. package/src/event.ts +638 -0
  100. package/src/file-mutation.ts +207 -0
  101. package/src/file.ts +6 -0
  102. package/src/filesystem/fff.bun.ts +140 -0
  103. package/src/filesystem/fff.node.ts +138 -0
  104. package/src/filesystem/ignore.ts +67 -0
  105. package/src/filesystem/protected.ts +53 -0
  106. package/src/filesystem/search.ts +239 -0
  107. package/src/filesystem/watcher.ts +140 -0
  108. package/src/filesystem.ts +118 -0
  109. package/src/flag/flag.ts +78 -0
  110. package/src/fs-util.ts +274 -0
  111. package/src/git.ts +987 -0
  112. package/src/github-copilot/README.md +5 -0
  113. package/src/github-copilot/chat/convert-to-openai-compatible-chat-messages.ts +170 -0
  114. package/src/github-copilot/chat/get-response-metadata.ts +15 -0
  115. package/src/github-copilot/chat/map-openai-compatible-finish-reason.ts +19 -0
  116. package/src/github-copilot/chat/openai-compatible-api-types.ts +64 -0
  117. package/src/github-copilot/chat/openai-compatible-chat-language-model.ts +815 -0
  118. package/src/github-copilot/chat/openai-compatible-chat-options.ts +28 -0
  119. package/src/github-copilot/chat/openai-compatible-metadata-extractor.ts +44 -0
  120. package/src/github-copilot/chat/openai-compatible-prepare-tools.ts +83 -0
  121. package/src/github-copilot/copilot-provider.ts +100 -0
  122. package/src/github-copilot/openai-compatible-error.ts +27 -0
  123. package/src/github-copilot/responses/convert-to-openai-responses-input.ts +335 -0
  124. package/src/github-copilot/responses/map-openai-responses-finish-reason.ts +22 -0
  125. package/src/github-copilot/responses/openai-config.ts +18 -0
  126. package/src/github-copilot/responses/openai-error.ts +22 -0
  127. package/src/github-copilot/responses/openai-responses-api-types.ts +214 -0
  128. package/src/github-copilot/responses/openai-responses-language-model.ts +1770 -0
  129. package/src/github-copilot/responses/openai-responses-prepare-tools.ts +173 -0
  130. package/src/github-copilot/responses/openai-responses-settings.ts +1 -0
  131. package/src/github-copilot/responses/tool/code-interpreter.ts +87 -0
  132. package/src/github-copilot/responses/tool/file-search.ts +127 -0
  133. package/src/github-copilot/responses/tool/image-generation.ts +114 -0
  134. package/src/github-copilot/responses/tool/local-shell.ts +64 -0
  135. package/src/github-copilot/responses/tool/web-search-preview.ts +103 -0
  136. package/src/github-copilot/responses/tool/web-search.ts +102 -0
  137. package/src/global.ts +87 -0
  138. package/src/id/id.ts +47 -0
  139. package/src/image/photon.ts +94 -0
  140. package/src/image.ts +81 -0
  141. package/src/installation/version.ts +8 -0
  142. package/src/instruction-context.ts +101 -0
  143. package/src/integration/connection.ts +12 -0
  144. package/src/integration.ts +520 -0
  145. package/src/location-mutation.ts +162 -0
  146. package/src/location-service-map.ts +18 -0
  147. package/src/location-services.ts +115 -0
  148. package/src/location.ts +39 -0
  149. package/src/markdown.d.ts +4 -0
  150. package/src/model.ts +41 -0
  151. package/src/models-dev.ts +260 -0
  152. package/src/npm-config.ts +40 -0
  153. package/src/npm.ts +269 -0
  154. package/src/oauth/page.ts +276 -0
  155. package/src/observability/logging.ts +71 -0
  156. package/src/observability/otlp.ts +79 -0
  157. package/src/observability/shared.ts +1 -0
  158. package/src/observability.ts +24 -0
  159. package/src/patch.ts +197 -0
  160. package/src/permission/saved.ts +79 -0
  161. package/src/permission/sql.ts +20 -0
  162. package/src/permission.ts +310 -0
  163. package/src/plugin/agent.ts +206 -0
  164. package/src/plugin/command/initialize.txt +65 -0
  165. package/src/plugin/command/review.txt +100 -0
  166. package/src/plugin/command.ts +25 -0
  167. package/src/plugin/host.ts +219 -0
  168. package/src/plugin/internal.ts +153 -0
  169. package/src/plugin/layer-map.example.ts +94 -0
  170. package/src/plugin/models-dev.ts +183 -0
  171. package/src/plugin/promise.ts +93 -0
  172. package/src/plugin/provider/alibaba.ts +15 -0
  173. package/src/plugin/provider/amazon-bedrock.ts +126 -0
  174. package/src/plugin/provider/anthropic.ts +27 -0
  175. package/src/plugin/provider/azure.ts +82 -0
  176. package/src/plugin/provider/cerebras.ts +26 -0
  177. package/src/plugin/provider/cloudflare-ai-gateway.ts +81 -0
  178. package/src/plugin/provider/cloudflare-workers-ai.ts +81 -0
  179. package/src/plugin/provider/cohere.ts +15 -0
  180. package/src/plugin/provider/deepinfra.ts +15 -0
  181. package/src/plugin/provider/dynamic.ts +31 -0
  182. package/src/plugin/provider/gateway.ts +15 -0
  183. package/src/plugin/provider/github-copilot.ts +52 -0
  184. package/src/plugin/provider/gitlab.ts +65 -0
  185. package/src/plugin/provider/google-vertex.ts +173 -0
  186. package/src/plugin/provider/google.ts +15 -0
  187. package/src/plugin/provider/groq.ts +15 -0
  188. package/src/plugin/provider/kilo.ts +21 -0
  189. package/src/plugin/provider/llmgateway.ts +26 -0
  190. package/src/plugin/provider/mistral.ts +15 -0
  191. package/src/plugin/provider/nvidia.ts +22 -0
  192. package/src/plugin/provider/openai-compatible.ts +17 -0
  193. package/src/plugin/provider/openai.ts +292 -0
  194. package/src/plugin/provider/opencode.ts +311 -0
  195. package/src/plugin/provider/openrouter.ts +36 -0
  196. package/src/plugin/provider/perplexity.ts +15 -0
  197. package/src/plugin/provider/sap-ai-core.ts +46 -0
  198. package/src/plugin/provider/snowflake-cortex.ts +89 -0
  199. package/src/plugin/provider/togetherai.ts +15 -0
  200. package/src/plugin/provider/venice.ts +15 -0
  201. package/src/plugin/provider/vercel.ts +27 -0
  202. package/src/plugin/provider/xai.ts +22 -0
  203. package/src/plugin/provider/zenmux.ts +21 -0
  204. package/src/plugin/provider.ts +71 -0
  205. package/src/plugin/skill/customize-neurocode.md +452 -0
  206. package/src/plugin/skill.ts +31 -0
  207. package/src/plugin/variant.ts +39 -0
  208. package/src/plugin.ts +167 -0
  209. package/src/policy.ts +49 -0
  210. package/src/process.ts +261 -0
  211. package/src/project/copy-strategies.ts +35 -0
  212. package/src/project/copy.ts +292 -0
  213. package/src/project/directories.ts +158 -0
  214. package/src/project/schema.ts +16 -0
  215. package/src/project/sql.ts +35 -0
  216. package/src/project.ts +136 -0
  217. package/src/provider.ts +25 -0
  218. package/src/pty/protocol.ts +37 -0
  219. package/src/pty/pty.bun.ts +26 -0
  220. package/src/pty/pty.node.ts +29 -0
  221. package/src/pty/pty.ts +25 -0
  222. package/src/pty/schema.ts +1 -0
  223. package/src/pty/ticket.ts +56 -0
  224. package/src/pty.ts +318 -0
  225. package/src/public-event-manifest.ts +7 -0
  226. package/src/question.ts +153 -0
  227. package/src/reference/guidance.ts +69 -0
  228. package/src/reference.ts +125 -0
  229. package/src/repository-cache.ts +259 -0
  230. package/src/repository.ts +214 -0
  231. package/src/ripgrep/binary.ts +132 -0
  232. package/src/ripgrep.ts +281 -0
  233. package/src/schema.ts +79 -0
  234. package/src/session/compaction.ts +241 -0
  235. package/src/session/context-epoch.ts +174 -0
  236. package/src/session/error.ts +24 -0
  237. package/src/session/event.ts +2 -0
  238. package/src/session/execution/local.ts +46 -0
  239. package/src/session/execution.ts +34 -0
  240. package/src/session/history.ts +101 -0
  241. package/src/session/info.ts +50 -0
  242. package/src/session/input.ts +288 -0
  243. package/src/session/message-updater.ts +397 -0
  244. package/src/session/message.ts +2 -0
  245. package/src/session/projector.ts +458 -0
  246. package/src/session/prompt.ts +1 -0
  247. package/src/session/revert.ts +121 -0
  248. package/src/session/run-coordinator.ts +104 -0
  249. package/src/session/runner/index.ts +28 -0
  250. package/src/session/runner/llm.ts +432 -0
  251. package/src/session/runner/max-steps.ts +16 -0
  252. package/src/session/runner/model.ts +218 -0
  253. package/src/session/runner/publish-llm-event.ts +423 -0
  254. package/src/session/runner/to-llm-message.ts +171 -0
  255. package/src/session/schema.ts +9 -0
  256. package/src/session/sql.ts +176 -0
  257. package/src/session/store.ts +63 -0
  258. package/src/session/todo.ts +78 -0
  259. package/src/session.ts +486 -0
  260. package/src/share/sql.ts +13 -0
  261. package/src/shell.ts +226 -0
  262. package/src/skill/discovery.ts +213 -0
  263. package/src/skill/guidance.ts +76 -0
  264. package/src/skill.ts +132 -0
  265. package/src/snapshot.ts +266 -0
  266. package/src/state.ts +128 -0
  267. package/src/system-context/builtins.ts +50 -0
  268. package/src/system-context/index.ts +320 -0
  269. package/src/system-context/registry.ts +49 -0
  270. package/src/tool/AGENTS.md +59 -0
  271. package/src/tool/application-tools.ts +57 -0
  272. package/src/tool/apply-patch.ts +219 -0
  273. package/src/tool/bash.ts +207 -0
  274. package/src/tool/builtins.ts +48 -0
  275. package/src/tool/edit.ts +223 -0
  276. package/src/tool/glob.ts +105 -0
  277. package/src/tool/grep.ts +137 -0
  278. package/src/tool/http-body.ts +30 -0
  279. package/src/tool/question.ts +94 -0
  280. package/src/tool/read-filesystem.ts +366 -0
  281. package/src/tool/read.ts +117 -0
  282. package/src/tool/registry.ts +147 -0
  283. package/src/tool/skill.ts +109 -0
  284. package/src/tool/todowrite.ts +62 -0
  285. package/src/tool/tool.ts +162 -0
  286. package/src/tool/tools.ts +13 -0
  287. package/src/tool/webfetch.ts +218 -0
  288. package/src/tool/websearch.ts +260 -0
  289. package/src/tool/write.ts +101 -0
  290. package/src/tool-output-store.ts +211 -0
  291. package/src/util/array.ts +10 -0
  292. package/src/util/binary.ts +41 -0
  293. package/src/util/effect-flock.ts +284 -0
  294. package/src/util/encode.ts +51 -0
  295. package/src/util/error.ts +70 -0
  296. package/src/util/flock.ts +358 -0
  297. package/src/util/glob.ts +34 -0
  298. package/src/util/hash.ts +11 -0
  299. package/src/util/identifier.ts +1 -0
  300. package/src/util/iife.ts +3 -0
  301. package/src/util/lazy.ts +11 -0
  302. package/src/util/module.ts +10 -0
  303. package/src/util/path.ts +37 -0
  304. package/src/util/retry.ts +42 -0
  305. package/src/util/slug.ts +74 -0
  306. package/src/util/token.ts +5 -0
  307. package/src/util/which.ts +14 -0
  308. package/src/util/wildcard.ts +14 -0
  309. package/src/v1/config/agent.ts +89 -0
  310. package/src/v1/config/attachment.ts +25 -0
  311. package/src/v1/config/command.ts +13 -0
  312. package/src/v1/config/config.ts +192 -0
  313. package/src/v1/config/console-state.ts +16 -0
  314. package/src/v1/config/error.ts +39 -0
  315. package/src/v1/config/formatter.ts +13 -0
  316. package/src/v1/config/layout.ts +6 -0
  317. package/src/v1/config/lsp.ts +80 -0
  318. package/src/v1/config/mcp.ts +63 -0
  319. package/src/v1/config/migrate.ts +254 -0
  320. package/src/v1/config/permission.ts +50 -0
  321. package/src/v1/config/plugin.ts +9 -0
  322. package/src/v1/config/provider-options.ts +227 -0
  323. package/src/v1/config/provider.ts +121 -0
  324. package/src/v1/config/server.ts +19 -0
  325. package/src/v1/config/skills.ts +13 -0
  326. package/src/v1/permission.ts +33 -0
  327. package/src/v1/session.ts +68 -0
  328. package/src/v2-schema.ts +3 -0
  329. package/src/workspace.ts +6 -0
  330. package/sst-env.d.ts +10 -0
  331. package/test/agent.test.ts +131 -0
  332. package/test/application-tools.test.ts +287 -0
  333. package/test/background-job.test.ts +106 -0
  334. package/test/catalog.test.ts +353 -0
  335. package/test/command.test.ts +56 -0
  336. package/test/config/agent.test.ts +349 -0
  337. package/test/config/command.test.ts +83 -0
  338. package/test/config/config.test.ts +798 -0
  339. package/test/config/fixtures/plugin/directory-plugin.ts +13 -0
  340. package/test/config/plugin.test.ts +248 -0
  341. package/test/config/provider-options.test.ts +224 -0
  342. package/test/config/provider.test.ts +269 -0
  343. package/test/config/skill.test.ts +80 -0
  344. package/test/credential.test.ts +36 -0
  345. package/test/database-migration.test.ts +644 -0
  346. package/test/effect/cross-spawn-spawner.test.ts +426 -0
  347. package/test/effect/keyed-mutex.test.ts +73 -0
  348. package/test/effect/layer-node/layer-node-types.test.ts +143 -0
  349. package/test/effect/layer-node/layer-node.test.ts +261 -0
  350. package/test/effect/layer-node/node-build.test.ts +125 -0
  351. package/test/effect/observability.test.ts +109 -0
  352. package/test/event.test.ts +1124 -0
  353. package/test/file-mutation.test.ts +368 -0
  354. package/test/filesystem/filesystem.test.ts +387 -0
  355. package/test/filesystem/ignore.test.ts +10 -0
  356. package/test/filesystem/search.test.ts +44 -0
  357. package/test/filesystem/watcher.test.ts +266 -0
  358. package/test/fixture/effect-flock-worker.ts +60 -0
  359. package/test/fixture/flock-worker.ts +72 -0
  360. package/test/fixture/git.ts +49 -0
  361. package/test/fixture/location.ts +26 -0
  362. package/test/fixture/tmpdir.ts +25 -0
  363. package/test/fixtures/recordings/session-runner/openai-chat-streams-text.json +27 -0
  364. package/test/git.test.ts +164 -0
  365. package/test/github-copilot/convert-to-copilot-messages.test.ts +523 -0
  366. package/test/github-copilot/copilot-chat-model.test.ts +592 -0
  367. package/test/github-copilot/openai-responses-language-model.test.ts +206 -0
  368. package/test/global.test.ts +16 -0
  369. package/test/instruction-context.test.ts +323 -0
  370. package/test/integration.test.ts +349 -0
  371. package/test/legacy-event-schema.test.ts +16 -0
  372. package/test/lib/effect.ts +53 -0
  373. package/test/lib/tool.ts +20 -0
  374. package/test/location-filesystem.test.ts +69 -0
  375. package/test/location-layer.test.ts +226 -0
  376. package/test/location-mutation.test.ts +178 -0
  377. package/test/location.test.ts +42 -0
  378. package/test/model.test.ts +23 -0
  379. package/test/models.test.ts +290 -0
  380. package/test/move-session.test.ts +235 -0
  381. package/test/npm-config.test.ts +51 -0
  382. package/test/npm.test.ts +84 -0
  383. package/test/oauth-page.test.ts +15 -0
  384. package/test/patch.test.ts +68 -0
  385. package/test/permission.test.ts +315 -0
  386. package/test/plugin/command.test.ts +47 -0
  387. package/test/plugin/fixture.ts +52 -0
  388. package/test/plugin/fixtures/config-effect-plugin.ts +15 -0
  389. package/test/plugin/fixtures/config-promise-plugin.ts +13 -0
  390. package/test/plugin/fixtures/invalid-plugin.ts +1 -0
  391. package/test/plugin/fixtures/models-dev.json +14 -0
  392. package/test/plugin/fixtures/provider-factory.ts +9 -0
  393. package/test/plugin/host.ts +303 -0
  394. package/test/plugin/models-dev.test.ts +170 -0
  395. package/test/plugin/promise.test.ts +67 -0
  396. package/test/plugin/provider-alibaba.test.ts +92 -0
  397. package/test/plugin/provider-amazon-bedrock.test.ts +622 -0
  398. package/test/plugin/provider-anthropic.test.ts +94 -0
  399. package/test/plugin/provider-azure-cognitive-services.test.ts +199 -0
  400. package/test/plugin/provider-azure.test.ts +293 -0
  401. package/test/plugin/provider-cerebras.test.ts +136 -0
  402. package/test/plugin/provider-cloudflare-ai-gateway.test.ts +426 -0
  403. package/test/plugin/provider-cloudflare-workers-ai.test.ts +279 -0
  404. package/test/plugin/provider-cohere.test.ts +123 -0
  405. package/test/plugin/provider-deepinfra.test.ts +159 -0
  406. package/test/plugin/provider-dynamic.test.ts +184 -0
  407. package/test/plugin/provider-gateway.test.ts +115 -0
  408. package/test/plugin/provider-github-copilot.test.ts +278 -0
  409. package/test/plugin/provider-gitlab.test.ts +308 -0
  410. package/test/plugin/provider-google-vertex-anthropic.test.ts +264 -0
  411. package/test/plugin/provider-google-vertex.test.ts +387 -0
  412. package/test/plugin/provider-google.test.ts +80 -0
  413. package/test/plugin/provider-groq.test.ts +121 -0
  414. package/test/plugin/provider-kilo.test.ts +99 -0
  415. package/test/plugin/provider-llmgateway.test.ts +80 -0
  416. package/test/plugin/provider-mistral.test.ts +129 -0
  417. package/test/plugin/provider-nvidia.test.ts +97 -0
  418. package/test/plugin/provider-openai-compatible.test.ts +107 -0
  419. package/test/plugin/provider-openai.test.ts +176 -0
  420. package/test/plugin/provider-opencode.test.ts +423 -0
  421. package/test/plugin/provider-openrouter.test.ts +113 -0
  422. package/test/plugin/provider-perplexity.test.ts +122 -0
  423. package/test/plugin/provider-sap-ai-core.test.ts +159 -0
  424. package/test/plugin/provider-snowflake-cortex.test.ts +261 -0
  425. package/test/plugin/provider-togetherai.test.ts +129 -0
  426. package/test/plugin/provider-venice.test.ts +115 -0
  427. package/test/plugin/provider-vercel.test.ts +83 -0
  428. package/test/plugin/provider-xai.test.ts +125 -0
  429. package/test/plugin/provider-zenmux.test.ts +110 -0
  430. package/test/plugin/skill.test.ts +25 -0
  431. package/test/plugin/variant.test.ts +67 -0
  432. package/test/plugin.test.ts +71 -0
  433. package/test/policy.test.ts +85 -0
  434. package/test/preload.ts +5 -0
  435. package/test/process/process.test.ts +368 -0
  436. package/test/project-copy.test.ts +400 -0
  437. package/test/project-directories.test.ts +61 -0
  438. package/test/project.test.ts +221 -0
  439. package/test/provider-mistral.test.ts +282 -0
  440. package/test/provider-xai-responses.test.ts +32 -0
  441. package/test/pty/info-schema.test.ts +32 -0
  442. package/test/pty/protocol.test.ts +27 -0
  443. package/test/pty/pty-session.test.ts +240 -0
  444. package/test/pty/ticket.test.ts +62 -0
  445. package/test/question.test.ts +114 -0
  446. package/test/reference-guidance.test.ts +76 -0
  447. package/test/reference.test.ts +78 -0
  448. package/test/repository-cache.test.ts +153 -0
  449. package/test/repository.test.ts +71 -0
  450. package/test/ripgrep.test.ts +65 -0
  451. package/test/session-compaction.test.ts +27 -0
  452. package/test/session-create.test.ts +425 -0
  453. package/test/session-history.test.ts +165 -0
  454. package/test/session-projector.test.ts +533 -0
  455. package/test/session-prompt.test.ts +584 -0
  456. package/test/session-run-coordinator.test.ts +418 -0
  457. package/test/session-runner-message.test.ts +501 -0
  458. package/test/session-runner-model.test.ts +347 -0
  459. package/test/session-runner-recorded.test.ts +193 -0
  460. package/test/session-runner-tool-events.test.ts +136 -0
  461. package/test/session-runner-tool-registry.test.ts +452 -0
  462. package/test/session-runner.test.ts +3365 -0
  463. package/test/session-todo.test.ts +94 -0
  464. package/test/session-tool-progress.test.ts +158 -0
  465. package/test/shared-schema.test.ts +206 -0
  466. package/test/shell.test.ts +108 -0
  467. package/test/skill/guidance.test.ts +144 -0
  468. package/test/skill-discovery.test.ts +165 -0
  469. package/test/skill.test.ts +125 -0
  470. package/test/snapshot.test.ts +178 -0
  471. package/test/state.test.ts +115 -0
  472. package/test/system-context/builtins.test.ts +129 -0
  473. package/test/system-context/index.test.ts +307 -0
  474. package/test/system-context/registry.test.ts +114 -0
  475. package/test/tool-apply-patch.test.ts +436 -0
  476. package/test/tool-bash.test.ts +437 -0
  477. package/test/tool-edit.test.ts +434 -0
  478. package/test/tool-output-store.test.ts +247 -0
  479. package/test/tool-question.test.ts +162 -0
  480. package/test/tool-read-filesystem.test.ts +118 -0
  481. package/test/tool-read.test.ts +674 -0
  482. package/test/tool-skill.test.ts +149 -0
  483. package/test/tool-todowrite.test.ts +125 -0
  484. package/test/tool-webfetch.test.ts +281 -0
  485. package/test/tool-websearch.test.ts +316 -0
  486. package/test/tool-write.test.ts +305 -0
  487. package/test/util/effect-flock.test.ts +392 -0
  488. package/test/util/flock.test.ts +428 -0
  489. package/test/util/which.test.ts +100 -0
  490. package/tsconfig.json +7 -0
package/src/pty.ts ADDED
@@ -0,0 +1,318 @@
1
+ export * as Pty from "./pty"
2
+
3
+ import { makeLocationNode } from "./effect/app-node"
4
+ import type { Disp, Proc } from "#pty"
5
+ import { Context, Effect, Layer, Schema, Types } from "effect"
6
+ import { Pty } from "@neurocode-ai/schema/pty"
7
+ import { Config } from "./config"
8
+ import { EventV2 } from "./event"
9
+ import { Location } from "./location"
10
+ import { PtyID } from "./pty/schema"
11
+ import { Shell } from "./shell"
12
+ import { lazy } from "./util/lazy"
13
+
14
+ const BUFFER_LIMIT = 1024 * 1024 * 2
15
+ // Exited sessions stay observable (status, exit code, retained output) until removed explicitly.
16
+ // Cap retention so abandoned terminals do not accumulate unbounded buffers.
17
+ const EXITED_LIMIT = 25
18
+ const pty = lazy(() => import("#pty"))
19
+
20
+ type Subscriber = {
21
+ readonly onData: (chunk: string) => void
22
+ readonly onEnd: (event: { exitCode?: number }) => void
23
+ active: boolean
24
+ detached: boolean
25
+ pending: string[]
26
+ end?: { exitCode?: number }
27
+ }
28
+
29
+ type Active = {
30
+ info: Info
31
+ process: Proc
32
+ buffer: string
33
+ bufferCursor: number
34
+ cursor: number
35
+ subscribers: Map<object, Subscriber>
36
+ listeners: Disp[]
37
+ }
38
+
39
+ export const Info = Pty.Info
40
+ export type Info = Types.DeepMutable<typeof Info.Type>
41
+
42
+ export const CreateInput = Pty.CreateInput
43
+
44
+ export type CreateInput = Types.DeepMutable<typeof CreateInput.Type>
45
+
46
+ export const UpdateInput = Pty.UpdateInput
47
+
48
+ export type UpdateInput = Types.DeepMutable<typeof UpdateInput.Type>
49
+
50
+ export const Event = Pty.Event
51
+
52
+ export type AttachInput = {
53
+ // Absolute output cursor to replay from. -1 tails from the current end; omitted replays the full retained buffer.
54
+ readonly cursor?: number
55
+ // Callbacks fire synchronously from the native PTY data path; keep them non-blocking.
56
+ readonly onData: (chunk: string) => void
57
+ // Fired once when the session stops producing output: process exit (exitCode set), removal, or service teardown.
58
+ readonly onEnd: (event: { exitCode?: number }) => void
59
+ }
60
+
61
+ export type Attachment = {
62
+ // Retained output from the requested cursor to the current end.
63
+ readonly replay: string
64
+ // Absolute output cursor after replay.
65
+ readonly cursor: number
66
+ readonly write: (data: string) => void
67
+ // Starts live delivery after the caller has applied replay and cursor metadata.
68
+ readonly activate: () => void
69
+ readonly detach: () => void
70
+ }
71
+
72
+ export class NotFoundError extends Schema.TaggedErrorClass<NotFoundError>()("Pty.NotFoundError", {
73
+ ptyID: PtyID,
74
+ }) {}
75
+
76
+ export class ExitedError extends Schema.TaggedErrorClass<ExitedError>()("Pty.ExitedError", {
77
+ ptyID: PtyID,
78
+ }) {}
79
+
80
+ export interface Interface {
81
+ readonly list: () => Effect.Effect<Info[]>
82
+ readonly get: (id: PtyID) => Effect.Effect<Info, NotFoundError>
83
+ readonly create: (input: CreateInput) => Effect.Effect<Info>
84
+ readonly update: (id: PtyID, input: UpdateInput) => Effect.Effect<Info, NotFoundError>
85
+ readonly remove: (id: PtyID) => Effect.Effect<void, NotFoundError>
86
+ readonly write: (id: PtyID, data: string) => Effect.Effect<void, NotFoundError>
87
+ readonly attach: (id: PtyID, input: AttachInput) => Effect.Effect<Attachment, NotFoundError | ExitedError>
88
+ }
89
+
90
+ export class Service extends Context.Service<Service, Interface>()("@neurocode/v2/Pty") {}
91
+
92
+ const layer = Layer.effect(
93
+ Service,
94
+ Effect.gen(function* () {
95
+ const events = yield* EventV2.Service
96
+ const location = yield* Location.Service
97
+ const config = yield* Config.Service
98
+ const context = yield* Effect.context()
99
+ const runFork = Effect.runForkWith(context)
100
+ const sessions = new Map<PtyID, Active>()
101
+ const exitOrder: PtyID[] = []
102
+
103
+ function notifyEnd(session: Active, event: { exitCode?: number }) {
104
+ for (const subscriber of session.subscribers.values()) {
105
+ if (!subscriber.active) {
106
+ subscriber.end = event
107
+ continue
108
+ }
109
+ try {
110
+ subscriber.onEnd(event)
111
+ } catch {}
112
+ }
113
+ session.subscribers.clear()
114
+ }
115
+
116
+ function teardown(session: Active) {
117
+ for (const listener of session.listeners) listener.dispose()
118
+ session.listeners.length = 0
119
+ if (session.info.status === "running") {
120
+ try {
121
+ session.process.kill()
122
+ } catch {}
123
+ }
124
+ notifyEnd(session, {})
125
+ }
126
+
127
+ yield* Effect.addFinalizer(() =>
128
+ Effect.sync(() => {
129
+ for (const session of sessions.values()) teardown(session)
130
+ sessions.clear()
131
+ exitOrder.length = 0
132
+ }),
133
+ )
134
+
135
+ const requireSession = Effect.fn("Pty.requireSession")(function* (id: PtyID) {
136
+ const session = sessions.get(id)
137
+ if (!session) return yield* new NotFoundError({ ptyID: id })
138
+ return session
139
+ })
140
+
141
+ const removeSession = Effect.fnUntraced(function* (id: PtyID) {
142
+ const session = sessions.get(id)
143
+ if (!session) return
144
+ sessions.delete(id)
145
+ const index = exitOrder.indexOf(id)
146
+ if (index !== -1) exitOrder.splice(index, 1)
147
+ yield* Effect.logInfo("removing session", { id })
148
+ teardown(session)
149
+ yield* events.publish(Event.Deleted, { id: session.info.id })
150
+ })
151
+
152
+ const remove = Effect.fn("Pty.remove")(function* (id: PtyID) {
153
+ yield* requireSession(id)
154
+ yield* removeSession(id)
155
+ })
156
+
157
+ const list = Effect.fn("Pty.list")(function* () {
158
+ return Array.from(sessions.values()).map((session) => session.info)
159
+ })
160
+
161
+ const get = Effect.fn("Pty.get")(function* (id: PtyID) {
162
+ return (yield* requireSession(id)).info
163
+ })
164
+
165
+ const create = Effect.fn("Pty.create")(function* (input: CreateInput) {
166
+ const id = PtyID.ascending()
167
+ const command = input.command || Shell.preferred(Config.latest(yield* config.entries(), "shell"))
168
+ const args = Shell.login(command) ? [...(input.args ?? []), "-l"] : [...(input.args ?? [])]
169
+ const cwd = input.cwd || location.directory
170
+ const env = {
171
+ ...process.env,
172
+ ...input.env,
173
+ TERM: "xterm-256color",
174
+ NEUROCODE_TERMINAL: "1",
175
+ } as Record<string, string>
176
+ if (process.platform === "win32") {
177
+ env.LC_ALL = "C.UTF-8"
178
+ env.LC_CTYPE = "C.UTF-8"
179
+ env.LANG = "C.UTF-8"
180
+ }
181
+ yield* Effect.logInfo("creating session", { id, cmd: command, args, cwd })
182
+ const { spawn } = yield* Effect.promise(() => pty())
183
+ const proc = yield* Effect.sync(() => spawn(command, args, { name: "xterm-256color", cwd, env }))
184
+ const info: Info = {
185
+ id,
186
+ title: input.title || `Terminal ${id.slice(-4)}`,
187
+ command,
188
+ args,
189
+ cwd,
190
+ status: "running",
191
+ pid: proc.pid,
192
+ }
193
+ const session: Active = {
194
+ info,
195
+ process: proc,
196
+ buffer: "",
197
+ bufferCursor: 0,
198
+ cursor: 0,
199
+ subscribers: new Map(),
200
+ listeners: [],
201
+ }
202
+ sessions.set(id, session)
203
+ session.listeners.push(
204
+ proc.onData((chunk) => {
205
+ session.cursor += chunk.length
206
+ for (const [token, subscriber] of session.subscribers.entries()) {
207
+ if (!subscriber.active) {
208
+ subscriber.pending.push(chunk)
209
+ continue
210
+ }
211
+ try {
212
+ subscriber.onData(chunk)
213
+ } catch {
214
+ session.subscribers.delete(token)
215
+ }
216
+ }
217
+ session.buffer += chunk
218
+ if (session.buffer.length <= BUFFER_LIMIT) return
219
+ const excess = session.buffer.length - BUFFER_LIMIT
220
+ session.buffer = session.buffer.slice(excess)
221
+ session.bufferCursor += excess
222
+ }),
223
+ proc.onExit(({ exitCode }) => {
224
+ if (session.info.status === "exited") return
225
+ session.info.status = "exited"
226
+ session.info.exitCode = exitCode
227
+ notifyEnd(session, { exitCode })
228
+ exitOrder.push(id)
229
+ runFork(
230
+ Effect.gen(function* () {
231
+ yield* Effect.logInfo("session exited", { id, exitCode })
232
+ yield* events.publish(Event.Exited, { id, exitCode })
233
+ while (exitOrder.length > EXITED_LIMIT) {
234
+ const oldest = exitOrder[0]
235
+ if (!oldest) break
236
+ yield* removeSession(oldest)
237
+ }
238
+ }),
239
+ )
240
+ }),
241
+ )
242
+ yield* events.publish(Event.Created, { info })
243
+ return info
244
+ })
245
+
246
+ const update = Effect.fn("Pty.update")(function* (id: PtyID, input: UpdateInput) {
247
+ const session = yield* requireSession(id)
248
+ if (input.title) session.info.title = input.title
249
+ if (input.size && session.info.status === "running") session.process.resize(input.size.cols, input.size.rows)
250
+ yield* events.publish(Event.Updated, { info: session.info })
251
+ return session.info
252
+ })
253
+
254
+ const write = Effect.fn("Pty.write")(function* (id: PtyID, data: string) {
255
+ const session = yield* requireSession(id)
256
+ if (session.info.status === "running") session.process.write(data)
257
+ })
258
+
259
+ const attach = Effect.fn("Pty.attach")(function* (id: PtyID, input: AttachInput) {
260
+ const session = yield* requireSession(id)
261
+ if (session.info.status !== "running") return yield* new ExitedError({ ptyID: id })
262
+ yield* Effect.logInfo("client attached to session", { id, directory: location.directory })
263
+ const token = {}
264
+ const subscriber: Subscriber = {
265
+ onData: input.onData,
266
+ onEnd: input.onEnd,
267
+ active: false,
268
+ detached: false,
269
+ pending: [],
270
+ }
271
+ session.subscribers.set(token, subscriber)
272
+ const start = session.bufferCursor
273
+ const end = session.cursor
274
+ const from =
275
+ input.cursor === -1
276
+ ? end
277
+ : typeof input.cursor === "number" && Number.isSafeInteger(input.cursor)
278
+ ? Math.max(0, input.cursor)
279
+ : 0
280
+ const replay = (() => {
281
+ if (!session.buffer || from >= end) return ""
282
+ const offset = Math.max(0, from - start)
283
+ if (offset >= session.buffer.length) return ""
284
+ return session.buffer.slice(offset)
285
+ })()
286
+ return {
287
+ replay,
288
+ cursor: end,
289
+ write: (data: string) => {
290
+ if (session.info.status === "running") session.process.write(data)
291
+ },
292
+ activate: () => {
293
+ if (subscriber.active || subscriber.detached) return
294
+ subscriber.active = true
295
+ try {
296
+ for (const chunk of subscriber.pending) subscriber.onData(chunk)
297
+ subscriber.pending.length = 0
298
+ if (subscriber.end) subscriber.onEnd(subscriber.end)
299
+ } catch {
300
+ session.subscribers.delete(token)
301
+ }
302
+ },
303
+ detach: () => {
304
+ subscriber.detached = true
305
+ subscriber.pending.length = 0
306
+ subscriber.end = undefined
307
+ session.subscribers.delete(token)
308
+ },
309
+ }
310
+ })
311
+
312
+ return Service.of({ list, get, create, update, remove, write, attach })
313
+ }),
314
+ )
315
+
316
+ export const locationLayer = layer.pipe(Layer.provide(Config.locationLayer))
317
+
318
+ export const node = makeLocationNode({ service: Service, layer, deps: [EventV2.node, Location.node, Config.node] })
@@ -0,0 +1,7 @@
1
+ export * as PublicEventManifest from "./public-event-manifest"
2
+
3
+ import { Event } from "@neurocode-ai/schema/event"
4
+ import { EventManifest } from "@neurocode-ai/schema/event-manifest"
5
+
6
+ export const Definitions = EventManifest.ServerDefinitions
7
+ export const Latest = Event.latest(Definitions)
@@ -0,0 +1,153 @@
1
+ export * as QuestionV2 from "./question"
2
+
3
+ import { makeLocationNode } from "./effect/app-node"
4
+ import { Context, Deferred, Effect, Layer, Schema } from "effect"
5
+ import { Question } from "@neurocode-ai/schema/question"
6
+ import { EventV2 } from "./event"
7
+ import { SessionSchema } from "./session/schema"
8
+
9
+ export const ID = Question.ID
10
+ export type ID = typeof ID.Type
11
+
12
+ export const Option = Question.Option
13
+ export type Option = typeof Option.Type
14
+
15
+ export const Info = Question.Info
16
+ export type Info = typeof Info.Type
17
+
18
+ export const Prompt = Question.Prompt
19
+ export type Prompt = typeof Prompt.Type
20
+
21
+ export const Tool = Question.Tool
22
+ export type Tool = typeof Tool.Type
23
+
24
+ export const Request = Question.Request
25
+ export type Request = typeof Request.Type
26
+
27
+ export const Answer = Question.Answer
28
+ export type Answer = typeof Answer.Type
29
+
30
+ export const Reply = Question.Reply
31
+ export type Reply = typeof Reply.Type
32
+
33
+ export const Event = Question.Event
34
+
35
+ export class RejectedError extends Schema.TaggedErrorClass<RejectedError>()("QuestionV2.RejectedError", {}) {
36
+ override get message() {
37
+ return "The user dismissed this question"
38
+ }
39
+ }
40
+
41
+ export class NotFoundError extends Schema.TaggedErrorClass<NotFoundError>()("QuestionV2.NotFoundError", {
42
+ requestID: ID,
43
+ }) {}
44
+
45
+ export interface AskInput {
46
+ readonly sessionID: SessionSchema.ID
47
+ readonly questions: ReadonlyArray<Info>
48
+ readonly tool?: Tool
49
+ }
50
+
51
+ export interface ReplyInput {
52
+ readonly requestID: ID
53
+ readonly answers: ReadonlyArray<Answer>
54
+ }
55
+
56
+ export interface Interface {
57
+ readonly ask: (input: AskInput) => Effect.Effect<ReadonlyArray<Answer>, RejectedError>
58
+ readonly reply: (input: ReplyInput) => Effect.Effect<void, NotFoundError>
59
+ readonly reject: (requestID: ID) => Effect.Effect<void, NotFoundError>
60
+ readonly list: () => Effect.Effect<ReadonlyArray<Request>>
61
+ }
62
+
63
+ export class Service extends Context.Service<Service, Interface>()("@neurocode/v2/Question") {}
64
+
65
+ interface Pending {
66
+ readonly request: Request
67
+ readonly deferred: Deferred.Deferred<ReadonlyArray<Answer>, RejectedError>
68
+ }
69
+
70
+ /**
71
+ * Location-owned pending prompts. The Location layer map must materialize this
72
+ * layer once per embedded Location so replies cannot settle another Location's
73
+ * deferred request.
74
+ */
75
+ const layer = Layer.effect(
76
+ Service,
77
+ Effect.gen(function* () {
78
+ const events = yield* EventV2.Service
79
+ const pending = new Map<ID, Pending>()
80
+
81
+ yield* Effect.addFinalizer(() =>
82
+ Effect.forEach(pending.values(), (item) => Deferred.fail(item.deferred, new RejectedError()), {
83
+ discard: true,
84
+ }).pipe(
85
+ Effect.ensuring(
86
+ Effect.sync(() => {
87
+ pending.clear()
88
+ }),
89
+ ),
90
+ ),
91
+ )
92
+
93
+ const ask = Effect.fn("QuestionV2.ask")((input: AskInput) =>
94
+ Effect.uninterruptibleMask((restore) =>
95
+ Effect.gen(function* () {
96
+ const id = ID.ascending()
97
+ const deferred = yield* Deferred.make<ReadonlyArray<Answer>, RejectedError>()
98
+ const request: Request = { id, ...input }
99
+ pending.set(id, { request, deferred })
100
+ return yield* events.publish(Event.Asked, request).pipe(
101
+ Effect.andThen(restore(Deferred.await(deferred))),
102
+ Effect.ensuring(
103
+ Effect.sync(() => {
104
+ pending.delete(id)
105
+ }),
106
+ ),
107
+ )
108
+ }),
109
+ ),
110
+ )
111
+
112
+ const reply = Effect.fn("QuestionV2.reply")((input: ReplyInput) =>
113
+ Effect.uninterruptible(
114
+ Effect.gen(function* () {
115
+ const existing = pending.get(input.requestID)
116
+ if (!existing) return yield* new NotFoundError({ requestID: input.requestID })
117
+ yield* events.publish(Event.Replied, {
118
+ sessionID: existing.request.sessionID,
119
+ requestID: existing.request.id,
120
+ answers: input.answers.map((answer) => [...answer]),
121
+ })
122
+ yield* Deferred.succeed(existing.deferred, input.answers)
123
+ pending.delete(input.requestID)
124
+ }),
125
+ ),
126
+ )
127
+
128
+ const reject = Effect.fn("QuestionV2.reject")((requestID: ID) =>
129
+ Effect.uninterruptible(
130
+ Effect.gen(function* () {
131
+ const existing = pending.get(requestID)
132
+ if (!existing) return yield* new NotFoundError({ requestID })
133
+ yield* events.publish(Event.Rejected, {
134
+ sessionID: existing.request.sessionID,
135
+ requestID: existing.request.id,
136
+ })
137
+ yield* Deferred.fail(existing.deferred, new RejectedError())
138
+ pending.delete(requestID)
139
+ }),
140
+ ),
141
+ )
142
+
143
+ const list = Effect.fn("QuestionV2.list")(function* () {
144
+ return Array.from(pending.values(), (item) => item.request)
145
+ })
146
+
147
+ return Service.of({ ask, reply, reject, list })
148
+ }),
149
+ )
150
+
151
+ export const locationLayer = layer
152
+
153
+ export const node = makeLocationNode({ service: Service, layer, deps: [EventV2.node] })
@@ -0,0 +1,69 @@
1
+ export * as ReferenceGuidance from "./guidance"
2
+
3
+ import { makeLocationNode } from "../effect/app-node"
4
+ import { Context, Effect, Layer, Schema } from "effect"
5
+ import { Reference } from "../reference"
6
+ import { SystemContext } from "../system-context/index"
7
+
8
+ const Summary = Schema.Struct({
9
+ name: Schema.String,
10
+ path: Schema.String,
11
+ description: Schema.String.pipe(Schema.optional),
12
+ })
13
+
14
+ const render = (references: ReadonlyArray<typeof Summary.Type>) =>
15
+ [
16
+ "Project references provide additional directories that can be accessed when relevant.",
17
+ "<available_references>",
18
+ ...references.flatMap((reference) => [
19
+ " <reference>",
20
+ ` <name>${reference.name}</name>`,
21
+ ` <path>${reference.path}</path>`,
22
+ ...(reference.description === undefined ? [] : [` <description>${reference.description}</description>`]),
23
+ " </reference>",
24
+ ]),
25
+ "</available_references>",
26
+ ].join("\n")
27
+
28
+ export interface Interface {
29
+ readonly load: () => Effect.Effect<SystemContext.SystemContext>
30
+ }
31
+
32
+ export class Service extends Context.Service<Service, Interface>()("@neurocode/v2/ReferenceGuidance") {}
33
+
34
+ const layer = Layer.effect(
35
+ Service,
36
+ Effect.gen(function* () {
37
+ const references = yield* Reference.Service
38
+
39
+ return Service.of({
40
+ load: Effect.fn("ReferenceGuidance.load")(function* () {
41
+ const available = (yield* references.list())
42
+ .filter((reference) => reference.description !== undefined)
43
+ .map((reference) => ({
44
+ name: reference.name,
45
+ path: reference.path,
46
+ description: reference.description,
47
+ }))
48
+ .toSorted((a, b) => a.name.localeCompare(b.name))
49
+ if (available.length === 0) return SystemContext.empty
50
+ return SystemContext.make({
51
+ key: SystemContext.Key.make("core/reference-guidance"),
52
+ codec: Schema.toCodecJson(Schema.Array(Summary)),
53
+ load: Effect.succeed(available),
54
+ baseline: render,
55
+ update: (_previous, current) =>
56
+ [
57
+ "The available project references have changed. This list supersedes the previous reference list.",
58
+ render(current),
59
+ ].join("\n"),
60
+ removed: () => "Project reference guidance is no longer available. Do not use previously listed references.",
61
+ })
62
+ }),
63
+ })
64
+ }),
65
+ )
66
+
67
+ export const locationLayer = layer
68
+
69
+ export const node = makeLocationNode({ service: Service, layer, deps: [Reference.node] })
@@ -0,0 +1,125 @@
1
+ export * as Reference from "./reference"
2
+
3
+ import { makeLocationNode } from "./effect/app-node"
4
+ import { Context, Effect, Layer, Scope, Types } from "effect"
5
+ import { Reference } from "@neurocode-ai/schema/reference"
6
+ import { Global } from "./global"
7
+ import { EventV2 } from "./event"
8
+ import { Repository } from "./repository"
9
+ import { RepositoryCache } from "./repository-cache"
10
+ import { AbsolutePath } from "./schema"
11
+ import { State } from "./state"
12
+
13
+ export const LocalSource = Reference.LocalSource
14
+ export type LocalSource = Reference.LocalSource
15
+
16
+ export const GitSource = Reference.GitSource
17
+ export type GitSource = Reference.GitSource
18
+
19
+ export const Source = Reference.Source
20
+ export type Source = Reference.Source
21
+
22
+ export const Event = Reference.Event
23
+
24
+ export const Info = Reference.Info
25
+ export type Info = Reference.Info
26
+
27
+ type Data = {
28
+ sources: Map<string, Types.DeepMutable<Source>>
29
+ }
30
+
31
+ type Draft = {
32
+ add(name: string, source: Source): void
33
+ remove(name: string): void
34
+ list(): readonly [string, Source][]
35
+ }
36
+
37
+ export interface Interface extends State.Transformable<Draft> {
38
+ readonly list: () => Effect.Effect<Info[]>
39
+ }
40
+
41
+ export class Service extends Context.Service<Service, Interface>()("@neurocode/v2/Reference") {}
42
+
43
+ const layer = Layer.effect(
44
+ Service,
45
+ Effect.gen(function* () {
46
+ const global = yield* Global.Service
47
+ const events = yield* EventV2.Service
48
+ const cache = yield* RepositoryCache.Service
49
+ const scope = yield* Scope.Scope
50
+ const materialized = new Map<string, Info>()
51
+ const state = State.create<Data, Draft>({
52
+ initial: () => ({ sources: new Map() }),
53
+ draft: (draft) => ({
54
+ add: (name, source) => draft.sources.set(name, source as Types.DeepMutable<Source>),
55
+ remove: (name) => draft.sources.delete(name),
56
+ list: () => Array.from(draft.sources.entries()) as [string, Source][],
57
+ }),
58
+ finalize: (draft) =>
59
+ Effect.gen(function* () {
60
+ materialized.clear()
61
+ for (const [name, source] of draft.list()) {
62
+ if (source.type === "local") {
63
+ materialized.set(
64
+ name,
65
+ new Info({
66
+ name,
67
+ path: source.path,
68
+ ...(source.description === undefined ? {} : { description: source.description }),
69
+ ...(source.hidden === undefined ? {} : { hidden: source.hidden }),
70
+ source,
71
+ }),
72
+ )
73
+ continue
74
+ }
75
+ const repository = Repository.parse(source.repository)
76
+ if (!repository || !Repository.isRemote(repository)) continue
77
+ if (source.branch) {
78
+ try {
79
+ Repository.validateBranch(source.branch)
80
+ } catch {
81
+ continue
82
+ }
83
+ }
84
+ materialized.set(
85
+ name,
86
+ new Info({
87
+ name,
88
+ path: AbsolutePath.make(Repository.cachePath(global.repos, repository, source.branch)),
89
+ ...(source.description === undefined ? {} : { description: source.description }),
90
+ ...(source.hidden === undefined ? {} : { hidden: source.hidden }),
91
+ source,
92
+ }),
93
+ )
94
+ yield* cache.ensure({ reference: repository, branch: source.branch, refresh: true }).pipe(
95
+ Effect.catchCause((cause) =>
96
+ Effect.logWarning("failed to materialize reference", {
97
+ name,
98
+ repository: source.repository,
99
+ cause,
100
+ }),
101
+ ),
102
+ Effect.forkIn(scope),
103
+ )
104
+ }
105
+ yield* events.publish(Event.Updated, {})
106
+ }),
107
+ })
108
+
109
+ return Service.of({
110
+ transform: state.transform,
111
+ reload: state.reload,
112
+ list: Effect.fn("Reference.list")(function* () {
113
+ return Array.from(materialized.values())
114
+ }),
115
+ })
116
+ }),
117
+ )
118
+
119
+ export const locationLayer = layer
120
+
121
+ export const node = makeLocationNode({
122
+ service: Service,
123
+ layer,
124
+ deps: [Global.node, EventV2.node, RepositoryCache.node],
125
+ })