@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
@@ -0,0 +1,22 @@
1
+ export * as ConfigReference from "./reference"
2
+
3
+ import { Schema } from "effect"
4
+
5
+ export class Git extends Schema.Class<Git>("ConfigV2.Reference.Git")({
6
+ repository: Schema.String,
7
+ branch: Schema.String.pipe(Schema.optional),
8
+ description: Schema.String.pipe(Schema.optional),
9
+ hidden: Schema.Boolean.pipe(Schema.optional),
10
+ }) {}
11
+
12
+ export class Local extends Schema.Class<Local>("ConfigV2.Reference.Local")({
13
+ path: Schema.String,
14
+ description: Schema.String.pipe(Schema.optional),
15
+ hidden: Schema.Boolean.pipe(Schema.optional),
16
+ }) {}
17
+
18
+ export const Entry = Schema.Union([Schema.String, Git, Local])
19
+ export type Entry = typeof Entry.Type
20
+
21
+ export const Info = Schema.Record(Schema.String, Entry)
22
+ export type Info = typeof Info.Type
@@ -0,0 +1,9 @@
1
+ export * as ConfigToolOutput from "./tool-output"
2
+
3
+ import { Schema } from "effect"
4
+ import { PositiveInt } from "../schema"
5
+
6
+ export class Info extends Schema.Class<Info>("ConfigV2.ToolOutput")({
7
+ max_lines: PositiveInt.pipe(Schema.optional),
8
+ max_bytes: PositiveInt.pipe(Schema.optional),
9
+ }) {}
@@ -0,0 +1,7 @@
1
+ export * as ConfigWatcher from "./watcher"
2
+
3
+ import { Schema } from "effect"
4
+
5
+ export class Info extends Schema.Class<Info>("ConfigV2.Watcher")({
6
+ ignore: Schema.String.pipe(Schema.Array, Schema.optional),
7
+ }) {}
package/src/config.ts ADDED
@@ -0,0 +1,227 @@
1
+ export * as Config from "./config"
2
+
3
+ import { makeLocationNode } from "./effect/app-node"
4
+ import path from "path"
5
+ import { type ParseError, parse } from "jsonc-parser"
6
+ import { Context, Effect, Layer, Option, Schema } from "effect"
7
+ import { Permission } from "@neurocode-ai/schema/permission"
8
+ import { FSUtil } from "./fs-util"
9
+ import { Global } from "./global"
10
+ import { Location } from "./location"
11
+ import { Policy } from "./policy"
12
+ import { AbsolutePath } from "./schema"
13
+ import { ConfigAgent } from "./config/agent"
14
+ import { ConfigAttachments } from "./config/attachments"
15
+ import { ConfigCompaction } from "./config/compaction"
16
+ import { ConfigCommand } from "./config/command"
17
+ import { ConfigExperimental } from "./config/experimental"
18
+ import { ConfigFormatter } from "./config/formatter"
19
+ import { ConfigLSP } from "./config/lsp"
20
+ import { ConfigMCP } from "./config/mcp"
21
+ import { ConfigPlugin } from "./config/plugin"
22
+ import { ConfigProvider } from "./config/provider"
23
+ import { ConfigReference } from "./config/reference"
24
+ import { ConfigToolOutput } from "./config/tool-output"
25
+ import { ConfigWatcher } from "./config/watcher"
26
+ import { ConfigV1 } from "./v1/config/config"
27
+ import { ConfigMigrateV1 } from "./v1/config/migrate"
28
+
29
+ export class Info extends Schema.Class<Info>("Config.Info")({
30
+ $schema: Schema.optional(Schema.String).annotate({
31
+ description: "JSON schema reference for configuration validation",
32
+ }),
33
+ shell: Schema.String.pipe(Schema.optional).annotate({
34
+ description: "Default shell to use for terminal and shell tool execution",
35
+ }),
36
+ model: Schema.String.pipe(Schema.optional).annotate({
37
+ description: "Default model to use when no session or agent model is selected",
38
+ }),
39
+ default_agent: Schema.String.pipe(Schema.optional).annotate({
40
+ description: "Default primary agent to use when no session agent is selected",
41
+ }),
42
+ autoupdate: Schema.Union([Schema.Boolean, Schema.Literal("notify")])
43
+ .pipe(Schema.optional)
44
+ .annotate({
45
+ description: "Automatically update or notify when a new version is available",
46
+ }),
47
+ share: Schema.Literals(["manual", "auto", "disabled"]).pipe(Schema.optional).annotate({
48
+ description: "Control whether sessions may be shared manually, automatically, or not at all",
49
+ }),
50
+ enterprise: Schema.Struct({
51
+ url: Schema.String.pipe(Schema.optional),
52
+ })
53
+ .pipe(Schema.optional)
54
+ .annotate({
55
+ description: "Enterprise sharing service configuration",
56
+ }),
57
+ username: Schema.String.pipe(Schema.optional).annotate({
58
+ description: "Username displayed in conversations and used for telemetry identity",
59
+ }),
60
+ permissions: Permission.Ruleset.pipe(Schema.optional).annotate({
61
+ description: "Ordered tool permission rules applied to agent tool use",
62
+ }),
63
+ agents: Schema.Record(Schema.String, ConfigAgent.Info).pipe(Schema.optional).annotate({
64
+ description: "Named built-in agent overrides and custom agent definitions",
65
+ }),
66
+ snapshots: Schema.Boolean.pipe(Schema.optional).annotate({
67
+ description: "Enable snapshots used for undo and revert behavior",
68
+ }),
69
+ watcher: ConfigWatcher.Info.pipe(Schema.optional).annotate({
70
+ description: "Filesystem watcher configuration",
71
+ }),
72
+ formatter: ConfigFormatter.Info.pipe(Schema.optional).annotate({
73
+ description: "Enable built-in formatters or configure formatter overrides",
74
+ }),
75
+ lsp: ConfigLSP.Info.pipe(Schema.optional).annotate({
76
+ description: "Enable built-in language servers or configure server overrides",
77
+ }),
78
+ attachments: ConfigAttachments.Info.pipe(Schema.optional).annotate({
79
+ description: "Attachment processing configuration",
80
+ }),
81
+ tool_output: ConfigToolOutput.Info.pipe(Schema.optional).annotate({
82
+ description: "Tool output truncation thresholds",
83
+ }),
84
+ mcp: ConfigMCP.Info.pipe(Schema.optional).annotate({
85
+ description: "MCP server configuration",
86
+ }),
87
+ compaction: ConfigCompaction.Info.pipe(Schema.optional).annotate({
88
+ description: "Conversation compaction behavior",
89
+ }),
90
+ skills: Schema.String.pipe(Schema.Array, Schema.optional).annotate({
91
+ description: "Additional paths or URLs to discover skills from",
92
+ }),
93
+ commands: Schema.Record(Schema.String, ConfigCommand.Info).pipe(Schema.optional).annotate({
94
+ description: "Named slash command definitions",
95
+ }),
96
+ instructions: Schema.String.pipe(Schema.Array, Schema.optional).annotate({
97
+ description: "Additional paths or URLs supplying ambient instructions",
98
+ }),
99
+ references: ConfigReference.Info.pipe(Schema.optional).annotate({
100
+ description: "Named local directories or Git repositories available as external context",
101
+ }),
102
+ plugins: ConfigPlugin.Plugins.pipe(Schema.optional).annotate({
103
+ description: "Ordered external plugin packages to load",
104
+ }),
105
+ experimental: ConfigExperimental.Experimental.pipe(Schema.optional),
106
+ providers: Schema.Record(Schema.String, ConfigProvider.Info).pipe(Schema.optional),
107
+ }) {}
108
+
109
+ export class Document extends Schema.Class<Document>("Config.Document")({
110
+ type: Schema.Literal("document"),
111
+ path: Schema.String.pipe(Schema.optional),
112
+ info: Info,
113
+ }) {}
114
+
115
+ export class Directory extends Schema.Class<Directory>("Config.Directory")({
116
+ type: Schema.Literal("directory"),
117
+ path: AbsolutePath,
118
+ }) {}
119
+
120
+ export type Entry = Document | Directory
121
+
122
+ export function latest<K extends keyof Info>(entries: readonly Entry[], key: K): Info[K] | undefined {
123
+ return entries
124
+ .filter((entry): entry is Document => entry.type === "document")
125
+ .findLast((entry) => entry.info[key] !== undefined)?.info[key]
126
+ }
127
+
128
+ export interface Interface {
129
+ /** Returns location config documents and supplemental directories from lowest to highest priority. */
130
+ readonly entries: () => Effect.Effect<Entry[]>
131
+ }
132
+
133
+ export class Service extends Context.Service<Service, Interface>()("@neurocode/v2/Config") {}
134
+
135
+ const layer = Layer.effect(
136
+ Service,
137
+ Effect.gen(function* () {
138
+ const fs = yield* FSUtil.Service
139
+ const global = yield* Global.Service
140
+ const location = yield* Location.Service
141
+ const policy = yield* Policy.Service
142
+ const names = ["neurocode.json", "neurocode.jsonc"]
143
+ const decodeOptions = { errors: "all", onExcessProperty: "ignore", propertyOrder: "original" } as const
144
+ const decodeInfo = Schema.decodeUnknownOption(Info, decodeOptions)
145
+ const decodeV1Info = Schema.decodeUnknownOption(ConfigV1.Info, decodeOptions)
146
+
147
+ const loadFile = Effect.fnUntraced(function* (filepath: string) {
148
+ const text = yield* fs.readFileStringSafe(filepath)
149
+ if (!text) return
150
+
151
+ const errors: ParseError[] = []
152
+ const input: unknown = parse(text, errors, { allowTrailingComma: true })
153
+ if (errors.length) return
154
+
155
+ const info = Option.getOrUndefined(
156
+ ConfigMigrateV1.isV1(input)
157
+ ? decodeV1Info(input).pipe(Option.map(ConfigMigrateV1.migrate), Option.flatMap(decodeInfo))
158
+ : decodeInfo(input),
159
+ )
160
+ if (!info) return
161
+ return new Document({ type: "document", path: filepath, info })
162
+ })
163
+
164
+ const loadDirectory = Effect.fnUntraced(function* (directory: AbsolutePath) {
165
+ return [
166
+ ...(yield* Effect.forEach(names, (file) => loadFile(path.join(directory, file))).pipe(
167
+ Effect.map((configs) => configs.filter((config): config is Document => config !== undefined)),
168
+ )),
169
+ new Directory({ type: "directory", path: directory }),
170
+ ]
171
+ })
172
+
173
+ const globalDirectory = AbsolutePath.make(global.config)
174
+ const locationIsGlobal = path.resolve(location.directory) === path.resolve(global.config)
175
+ // Read configuration once when this location opens. Later calls reuse these
176
+ // values until the location is reopened.
177
+ const discovered = locationIsGlobal
178
+ ? []
179
+ : yield* fs
180
+ .up({
181
+ targets: [".neurocode", ...names.toReversed()],
182
+ start: location.directory,
183
+ stop: location.project.directory,
184
+ })
185
+ .pipe(Effect.orDie)
186
+ const directories = [
187
+ globalDirectory,
188
+ ...discovered
189
+ .filter((item) => path.basename(item) === ".neurocode")
190
+ .toReversed()
191
+ .map((directory) => AbsolutePath.make(directory)),
192
+ ]
193
+ // A config closer to the opened directory should win over one higher up.
194
+ // Search starts nearby, so reverse the results before applying them.
195
+ const directPaths = discovered.filter((item) => path.basename(item) !== ".neurocode").toReversed()
196
+ const direct = yield* Effect.forEach(directPaths, loadFile).pipe(
197
+ Effect.orDie,
198
+ Effect.map((configs) => configs.filter((config): config is Document => config !== undefined)),
199
+ )
200
+ const supplementary = yield* Effect.forEach(directories, loadDirectory).pipe(Effect.orDie)
201
+ // Apply general settings first and more specific settings last:
202
+ // global config, project files, then `.neurocode` files.
203
+ const configs = [...(supplementary[0] ?? []), ...direct, ...supplementary.slice(1).flat()]
204
+ // Rules use the opposite order so a user-global rule can override a
205
+ // repository rule. Statement order inside each file stays unchanged.
206
+ yield* policy.load(
207
+ configs
208
+ .filter((config): config is Document => config.type === "document")
209
+ .toReversed()
210
+ .flatMap((config) => config.info.experimental?.policies ?? []),
211
+ )
212
+
213
+ return Service.of({
214
+ entries: Effect.fn("Config.entries")(function* () {
215
+ return configs
216
+ }),
217
+ })
218
+ }),
219
+ )
220
+
221
+ export const locationLayer = layer.pipe(Layer.provideMerge(Policy.locationLayer))
222
+
223
+ export const node = makeLocationNode({
224
+ service: Service,
225
+ layer,
226
+ deps: [FSUtil.node, Global.node, Location.node, Policy.node],
227
+ })
@@ -0,0 +1,148 @@
1
+ export * as MoveSession from "./move-session"
2
+
3
+ import { Context, DateTime, Effect, Layer, Schema } from "effect"
4
+ import { makeGlobalNode } from "../effect/app-node"
5
+ import { EventV2 } from "../event"
6
+ import { Git } from "../git"
7
+ import { Location } from "../location"
8
+ import { ProjectV2 } from "../project"
9
+ import { SessionV2 } from "../session"
10
+ import { SessionEvent } from "../session/event"
11
+ import { SessionSchema } from "../session/schema"
12
+ import { SessionStore } from "../session/store"
13
+ import { AbsolutePath, RelativePath } from "../schema"
14
+ import path from "path"
15
+
16
+ export const Destination = Schema.Struct({
17
+ directory: AbsolutePath,
18
+ }).annotate({ identifier: "MoveSession.Destination" })
19
+ export type Destination = typeof Destination.Type
20
+
21
+ export const Input = Schema.Struct({
22
+ sessionID: SessionSchema.ID,
23
+ destination: Destination,
24
+ moveChanges: Schema.optional(Schema.Boolean),
25
+ }).annotate({ identifier: "MoveSession.Input" })
26
+ export type Input = typeof Input.Type
27
+
28
+ export class DestinationProjectMismatchError extends Schema.TaggedErrorClass<DestinationProjectMismatchError>()(
29
+ "MoveSession.DestinationProjectMismatchError",
30
+ {
31
+ expected: ProjectV2.ID,
32
+ actual: ProjectV2.ID,
33
+ },
34
+ ) {}
35
+
36
+ export class ApplyChangesError extends Schema.TaggedErrorClass<ApplyChangesError>()("MoveSession.ApplyChangesError", {
37
+ message: Schema.String,
38
+ }) {}
39
+
40
+ export class CaptureChangesError extends Schema.TaggedErrorClass<CaptureChangesError>()(
41
+ "MoveSession.CaptureChangesError",
42
+ {
43
+ message: Schema.String,
44
+ },
45
+ ) {}
46
+
47
+ export class ResetSourceChangesError extends Schema.TaggedErrorClass<ResetSourceChangesError>()(
48
+ "MoveSession.ResetSourceChangesError",
49
+ {
50
+ directory: AbsolutePath,
51
+ message: Schema.String,
52
+ cause: Schema.optional(Schema.Defect()),
53
+ },
54
+ ) {}
55
+
56
+ export type Error =
57
+ | SessionV2.NotFoundError
58
+ | DestinationProjectMismatchError
59
+ | CaptureChangesError
60
+ | ApplyChangesError
61
+ | ResetSourceChangesError
62
+
63
+ export interface Interface {
64
+ readonly moveSession: (input: Input) => Effect.Effect<void, Error>
65
+ }
66
+
67
+ export class Service extends Context.Service<Service, Interface>()("@neurocode/ControlPlaneMoveSession") {}
68
+
69
+ const layer = Layer.effect(
70
+ Service,
71
+ Effect.gen(function* () {
72
+ const git = yield* Git.Service
73
+ const events = yield* EventV2.Service
74
+ const project = yield* ProjectV2.Service
75
+ const sessions = yield* SessionStore.Service
76
+
77
+ const moveSession = Effect.fn("MoveSession.moveSession")(function* (input: Input) {
78
+ const current = yield* sessions.get(input.sessionID)
79
+ if (!current) return yield* new SessionV2.NotFoundError({ sessionID: input.sessionID })
80
+ const directory = AbsolutePath.make(input.destination.directory)
81
+ if (current.location.directory === directory) return
82
+
83
+ const source = yield* project.resolve(current.location.directory)
84
+ const destination = yield* project.resolve(directory)
85
+ if (current.projectID !== destination.id) {
86
+ return yield* new DestinationProjectMismatchError({ expected: current.projectID, actual: destination.id })
87
+ }
88
+
89
+ const moveChanges = input.moveChanges && source.directory !== destination.directory
90
+ const sourceRepository = moveChanges ? yield* git.repo.discover(current.location.directory) : undefined
91
+ if (moveChanges && !sourceRepository)
92
+ return yield* new CaptureChangesError({ message: "Source is not a Git repository" })
93
+ const patch = sourceRepository
94
+ ? yield* git.change
95
+ .capture({ repository: sourceRepository, path: current.location.directory })
96
+ .pipe(Effect.mapError((error) => new CaptureChangesError({ message: error.message })))
97
+ : Git.ChangeSet.make("")
98
+ if (patch) {
99
+ const repository = yield* git.repo.discover(directory)
100
+ if (!repository) return yield* new ApplyChangesError({ message: "Destination is not a Git repository" })
101
+ yield* git.change
102
+ .apply({ repository, path: directory, changes: patch })
103
+ .pipe(Effect.mapError((error) => new ApplyChangesError({ message: error.message })))
104
+ }
105
+
106
+ yield* events.publish(SessionEvent.Moved, {
107
+ sessionID: input.sessionID,
108
+ location: Location.Ref.make({ directory }),
109
+ subdirectory: RelativePath.make(path.relative(destination.directory, directory).replaceAll("\\", "/")),
110
+ timestamp: yield* DateTime.now,
111
+ })
112
+
113
+ if (patch) {
114
+ const repository = yield* git.repo.discover(current.location.directory)
115
+ if (!repository)
116
+ return yield* new ResetSourceChangesError({
117
+ directory: current.location.directory,
118
+ message: "Source is not a Git repository",
119
+ })
120
+ yield* git.change
121
+ .discard({
122
+ repository,
123
+ path: current.location.directory,
124
+ index: "preserve",
125
+ untracked: "remove",
126
+ })
127
+ .pipe(
128
+ Effect.mapError(
129
+ (error) =>
130
+ new ResetSourceChangesError({
131
+ directory: current.location.directory,
132
+ message: error.message,
133
+ cause: error.cause,
134
+ }),
135
+ ),
136
+ )
137
+ }
138
+ })
139
+
140
+ return Service.of({ moveSession })
141
+ }),
142
+ )
143
+
144
+ export const node = makeGlobalNode({
145
+ service: Service,
146
+ layer,
147
+ deps: [Git.node, EventV2.node, ProjectV2.node, SessionStore.node],
148
+ })
@@ -0,0 +1,20 @@
1
+ import { sqliteTable, text, integer } from "drizzle-orm/sqlite-core"
2
+ import { ProjectTable } from "../project/sql"
3
+ import { ProjectV2 } from "../project"
4
+ import { WorkspaceV2 } from "../workspace"
5
+
6
+ export const WorkspaceTable = sqliteTable("workspace", {
7
+ id: text().$type<WorkspaceV2.ID>().primaryKey(),
8
+ type: text().notNull(),
9
+ name: text().notNull().default(""),
10
+ branch: text(),
11
+ directory: text(),
12
+ extra: text({ mode: "json" }),
13
+ project_id: text()
14
+ .$type<ProjectV2.ID>()
15
+ .notNull()
16
+ .references(() => ProjectTable.id, { onDelete: "cascade" }),
17
+ time_used: integer()
18
+ .notNull()
19
+ .$default(() => Date.now()),
20
+ })
@@ -0,0 +1,14 @@
1
+ import { integer, sqliteTable, text } from "drizzle-orm/sqlite-core"
2
+ import { Timestamps } from "../database/schema.sql"
3
+ import type { Credential } from "../credential"
4
+
5
+ export const CredentialTable = sqliteTable("credential", {
6
+ id: text().$type<Credential.ID>().primaryKey(),
7
+ integration_id: text().$type<Credential.Info["integrationID"]>(),
8
+ label: text().notNull(),
9
+ value: text({ mode: "json" }).$type<Credential.Value>().notNull(),
10
+ connector_id: text(),
11
+ method_id: text(),
12
+ active: integer({ mode: "boolean" }),
13
+ ...Timestamps,
14
+ })
@@ -0,0 +1,138 @@
1
+ export * as Credential from "./credential"
2
+
3
+ import { asc, eq } from "drizzle-orm"
4
+ import { Context, Effect, Layer, Schema } from "effect"
5
+ import { Credential } from "@neurocode-ai/schema/credential"
6
+ import { Integration } from "@neurocode-ai/schema/integration"
7
+ import { Database } from "./database/database"
8
+ import { makeGlobalNode } from "./effect/app-node"
9
+ import { CredentialTable } from "./credential/sql"
10
+
11
+ export const ID = Credential.ID
12
+ export type ID = Credential.ID
13
+
14
+ export const OAuth = Credential.OAuth
15
+ export type OAuth = Credential.OAuth
16
+
17
+ export const Key = Credential.Key
18
+ export type Key = Credential.Key
19
+
20
+ export const Value = Credential.Value
21
+ export type Value = Credential.Value
22
+
23
+ export class Info extends Schema.Class<Info>("Credential.Info")({
24
+ id: ID,
25
+ integrationID: Integration.ID,
26
+ label: Schema.String,
27
+ value: Value,
28
+ }) {}
29
+
30
+ export interface Interface {
31
+ /** Returns every stored credential. */
32
+ readonly all: () => Effect.Effect<Info[]>
33
+ /** Returns stored credentials belonging to one integration. */
34
+ readonly list: (integrationID: Integration.ID) => Effect.Effect<Info[]>
35
+ /** Returns one stored credential by ID. */
36
+ readonly get: (id: ID) => Effect.Effect<Info | undefined>
37
+ /** Replaces any credential for an integration and returns the new record. */
38
+ readonly create: (input: {
39
+ readonly integrationID: Integration.ID
40
+ readonly value: Value
41
+ readonly label?: string
42
+ }) => Effect.Effect<Info>
43
+ /** Updates the label or secret value of a stored credential. */
44
+ readonly update: (id: ID, updates: Partial<Pick<Info, "label" | "value">>) => Effect.Effect<void>
45
+ /** Removes a stored credential. */
46
+ readonly remove: (id: ID) => Effect.Effect<void>
47
+ }
48
+
49
+ export class Service extends Context.Service<Service, Interface>()("@neurocode/v2/Credential") {}
50
+
51
+ const layer = Layer.effect(
52
+ Service,
53
+ Effect.gen(function* () {
54
+ const { db } = yield* Database.Service
55
+ const decode = Schema.decodeUnknownSync(Value)
56
+ const stored = (row: typeof CredentialTable.$inferSelect) => {
57
+ if (!row.integration_id) return
58
+ return new Info({
59
+ id: row.id,
60
+ integrationID: row.integration_id,
61
+ label: row.label,
62
+ value: decode(row.value),
63
+ })
64
+ }
65
+
66
+ return Service.of({
67
+ all: Effect.fn("Credential.all")(function* () {
68
+ return (yield* db
69
+ .select()
70
+ .from(CredentialTable)
71
+ .orderBy(asc(CredentialTable.time_created))
72
+ .all()
73
+ .pipe(Effect.orDie)).flatMap((row) => {
74
+ const credential = stored(row)
75
+ return credential ? [credential] : []
76
+ })
77
+ }),
78
+ list: Effect.fn("Credential.list")(function* (integrationID) {
79
+ return (yield* db
80
+ .select()
81
+ .from(CredentialTable)
82
+ .where(eq(CredentialTable.integration_id, integrationID))
83
+ .orderBy(asc(CredentialTable.time_created))
84
+ .all()
85
+ .pipe(Effect.orDie)).flatMap((row) => {
86
+ const credential = stored(row)
87
+ return credential ? [credential] : []
88
+ })
89
+ }),
90
+ get: Effect.fn("Credential.get")(function* (id) {
91
+ const row = yield* db.select().from(CredentialTable).where(eq(CredentialTable.id, id)).get().pipe(Effect.orDie)
92
+ return row ? stored(row) : undefined
93
+ }),
94
+ create: Effect.fn("Credential.create")(function* (input) {
95
+ const credential = new Info({
96
+ id: ID.create(),
97
+ integrationID: input.integrationID,
98
+ label: input.label ?? "default",
99
+ value: input.value,
100
+ })
101
+ yield* db
102
+ .transaction((tx) =>
103
+ Effect.gen(function* () {
104
+ yield* tx
105
+ .delete(CredentialTable)
106
+ .where(eq(CredentialTable.integration_id, credential.integrationID))
107
+ .run()
108
+ yield* tx
109
+ .insert(CredentialTable)
110
+ .values({
111
+ id: credential.id,
112
+ integration_id: credential.integrationID,
113
+ label: credential.label,
114
+ value: credential.value,
115
+ })
116
+ .run()
117
+ }),
118
+ )
119
+ .pipe(Effect.orDie)
120
+ return credential
121
+ }),
122
+ update: Effect.fn("Credential.update")(function* (id, updates) {
123
+ if (!updates.label && !updates.value) return
124
+ yield* db
125
+ .update(CredentialTable)
126
+ .set({ label: updates.label, value: updates.value })
127
+ .where(eq(CredentialTable.id, id))
128
+ .run()
129
+ .pipe(Effect.orDie)
130
+ }),
131
+ remove: Effect.fn("Credential.remove")(function* (id) {
132
+ yield* db.delete(CredentialTable).where(eq(CredentialTable.id, id)).run().pipe(Effect.orDie)
133
+ }),
134
+ })
135
+ }),
136
+ )
137
+
138
+ export const node = makeGlobalNode({ service: Service, layer, deps: [Database.node] })