@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/npm.ts ADDED
@@ -0,0 +1,269 @@
1
+ export * as Npm from "./npm"
2
+
3
+ import path from "path"
4
+ import npa from "npm-package-arg"
5
+ import { Effect, Schema, Context, Layer, Option, FileSystem } from "effect"
6
+ import { NodeFileSystem } from "@effect/platform-node"
7
+ import { FSUtil } from "./fs-util"
8
+ import { Global } from "./global"
9
+ import { EffectFlock } from "./util/effect-flock"
10
+ import { makeGlobalNode } from "./effect/app-node"
11
+ import { filesystem } from "./effect/app-node-platform"
12
+ import { LayerNode } from "./effect/layer-node"
13
+ import { makeRuntime } from "./effect/runtime"
14
+ import { NpmConfig } from "./npm-config"
15
+
16
+ export class InstallFailedError extends Schema.TaggedErrorClass<InstallFailedError>()("NpmInstallFailedError", {
17
+ add: Schema.Array(Schema.String).pipe(Schema.optional),
18
+ dir: Schema.String,
19
+ cause: Schema.optional(Schema.Defect()),
20
+ }) {}
21
+
22
+ export interface EntryPoint {
23
+ readonly directory: string
24
+ readonly entrypoint?: string
25
+ }
26
+
27
+ export interface Interface {
28
+ readonly add: (pkg: string) => Effect.Effect<EntryPoint, InstallFailedError | EffectFlock.LockError>
29
+ readonly install: (
30
+ dir: string,
31
+ input?: {
32
+ add: {
33
+ name: string
34
+ version?: string
35
+ }[]
36
+ },
37
+ ) => Effect.Effect<void, EffectFlock.LockError | InstallFailedError>
38
+ readonly which: (pkg: string, bin?: string) => Effect.Effect<string | undefined>
39
+ }
40
+
41
+ export class Service extends Context.Service<Service, Interface>()("@neurocode/Npm") {}
42
+
43
+ const illegal = process.platform === "win32" ? new Set(["<", ">", ":", '"', "|", "?", "*"]) : undefined
44
+
45
+ export function sanitize(pkg: string) {
46
+ if (!illegal) return pkg
47
+ return Array.from(pkg, (char) => (illegal.has(char) || char.charCodeAt(0) < 32 ? "_" : char)).join("")
48
+ }
49
+
50
+ const resolveEntryPoint = (name: string, dir: string): EntryPoint => {
51
+ let entrypoint: string | undefined
52
+ try {
53
+ entrypoint = typeof Bun !== "undefined" ? import.meta.resolve(name, dir) : import.meta.resolve(dir)
54
+ } catch {
55
+ entrypoint = undefined
56
+ }
57
+ return {
58
+ directory: dir,
59
+ entrypoint,
60
+ }
61
+ }
62
+
63
+ interface ArboristNode {
64
+ name: string
65
+ path: string
66
+ }
67
+
68
+ interface ArboristTree {
69
+ edgesOut: Map<string, { to?: ArboristNode }>
70
+ }
71
+
72
+ const layer = Layer.effect(
73
+ Service,
74
+ Effect.gen(function* () {
75
+ const afs = yield* FSUtil.Service
76
+ const global = yield* Global.Service
77
+ const fs = yield* FileSystem.FileSystem
78
+ const flock = yield* EffectFlock.Service
79
+ const directory = (pkg: string) => path.join(global.cache, "packages", sanitize(pkg))
80
+ const reify = (input: { dir: string; add?: string[] }) =>
81
+ Effect.gen(function* () {
82
+ yield* flock.acquire(`npm-install:${input.dir}`)
83
+ const { Arborist } = yield* Effect.promise(() => import("@npmcli/arborist"))
84
+ const add = input.add ?? []
85
+ const npmOptions = yield* NpmConfig.load(input.dir)
86
+ const arborist = new Arborist({
87
+ ...npmOptions,
88
+ path: input.dir,
89
+ binLinks: true,
90
+ progress: false,
91
+ savePrefix: "",
92
+ ignoreScripts: true,
93
+ })
94
+ return yield* Effect.tryPromise({
95
+ try: () =>
96
+ arborist.reify({
97
+ ...npmOptions,
98
+ add,
99
+ save: true,
100
+ saveType: "prod",
101
+ }),
102
+ catch: (cause) =>
103
+ new InstallFailedError({
104
+ cause,
105
+ add,
106
+ dir: input.dir,
107
+ }),
108
+ }) as Effect.Effect<ArboristTree, InstallFailedError>
109
+ }).pipe(
110
+ Effect.withSpan("Npm.reify", {
111
+ attributes: input,
112
+ }),
113
+ )
114
+
115
+ const add = Effect.fn("Npm.add")(function* (pkg: string) {
116
+ const dir = directory(pkg)
117
+ const name = (() => {
118
+ try {
119
+ return npa(pkg).name ?? pkg
120
+ } catch {
121
+ return pkg
122
+ }
123
+ })()
124
+
125
+ if (yield* afs.existsSafe(path.join(dir, "node_modules", name))) {
126
+ return resolveEntryPoint(name, path.join(dir, "node_modules", name))
127
+ }
128
+
129
+ const tree = yield* reify({ dir, add: [pkg] })
130
+ const first = tree.edgesOut.values().next().value?.to
131
+ if (!first) {
132
+ const result = resolveEntryPoint(name, path.join(dir, "node_modules", name))
133
+ if (result.entrypoint) return result
134
+ return yield* new InstallFailedError({ add: [pkg], dir })
135
+ }
136
+ return resolveEntryPoint(first.name, first.path)
137
+ }, Effect.scoped)
138
+
139
+ const install: Interface["install"] = Effect.fn("Npm.install")(function* (dir, input) {
140
+ const canWrite = yield* afs.access(dir, { writable: true }).pipe(
141
+ Effect.as(true),
142
+ Effect.orElseSucceed(() => false),
143
+ )
144
+ if (!canWrite) return
145
+
146
+ const add = input?.add.map((pkg) => [pkg.name, pkg.version].filter(Boolean).join("@")) ?? []
147
+ if (
148
+ yield* Effect.gen(function* () {
149
+ const nodeModulesExists = yield* afs.existsSafe(path.join(dir, "node_modules"))
150
+ if (!nodeModulesExists) {
151
+ yield* reify({ add, dir })
152
+ return true
153
+ }
154
+ return false
155
+ }).pipe(Effect.withSpan("Npm.checkNodeModules"))
156
+ )
157
+ return
158
+
159
+ yield* Effect.gen(function* () {
160
+ const pkg = yield* afs.readJson(path.join(dir, "package.json")).pipe(Effect.orElseSucceed(() => ({})))
161
+ const lock = yield* afs.readJson(path.join(dir, "package-lock.json")).pipe(Effect.orElseSucceed(() => ({})))
162
+
163
+ const pkgAny = pkg as any
164
+ const lockAny = lock as any
165
+ const declared = new Set([
166
+ ...Object.keys(pkgAny?.dependencies || {}),
167
+ ...Object.keys(pkgAny?.devDependencies || {}),
168
+ ...Object.keys(pkgAny?.peerDependencies || {}),
169
+ ...Object.keys(pkgAny?.optionalDependencies || {}),
170
+ ...(input?.add || []).map((pkg) => pkg.name),
171
+ ])
172
+
173
+ const root = lockAny?.packages?.[""] || {}
174
+ const locked = new Set([
175
+ ...Object.keys(root?.dependencies || {}),
176
+ ...Object.keys(root?.devDependencies || {}),
177
+ ...Object.keys(root?.peerDependencies || {}),
178
+ ...Object.keys(root?.optionalDependencies || {}),
179
+ ])
180
+
181
+ for (const name of declared) {
182
+ if (!locked.has(name)) {
183
+ yield* reify({ dir, add })
184
+ return
185
+ }
186
+ }
187
+ }).pipe(Effect.withSpan("Npm.checkDirty"))
188
+
189
+ return
190
+ }, Effect.scoped)
191
+
192
+ const which = Effect.fn("Npm.which")(function* (pkg: string, bin?: string) {
193
+ const dir = directory(pkg)
194
+ const binDir = path.join(dir, "node_modules", ".bin")
195
+
196
+ const pick = Effect.fnUntraced(function* () {
197
+ const files = yield* fs.readDirectory(binDir).pipe(Effect.catch(() => Effect.succeed([] as string[])))
198
+
199
+ if (files.length === 0) return Option.none<string>()
200
+ // Caller picked a specific bin (e.g. pyright exposes both `pyright` and
201
+ // `pyright-langserver`); trust the hint if the package provides it.
202
+ if (bin) return files.includes(bin) ? Option.some(bin) : Option.none<string>()
203
+ if (files.length === 1) return Option.some(files[0])
204
+
205
+ const pkgJson = yield* afs.readJson(path.join(dir, "node_modules", pkg, "package.json")).pipe(Effect.option)
206
+
207
+ if (Option.isSome(pkgJson)) {
208
+ const parsed = pkgJson.value as { bin?: string | Record<string, string> }
209
+ if (parsed?.bin) {
210
+ const unscoped = pkg.startsWith("@") ? pkg.split("/")[1] : pkg
211
+ const parsedBin = parsed.bin
212
+ if (typeof parsedBin === "string") return Option.some(unscoped)
213
+ const keys = Object.keys(parsedBin)
214
+ if (keys.length === 1) return Option.some(keys[0])
215
+ return parsedBin[unscoped] ? Option.some(unscoped) : Option.some(keys[0])
216
+ }
217
+ }
218
+
219
+ return Option.some(files[0])
220
+ })
221
+
222
+ return Option.getOrUndefined(
223
+ yield* Effect.gen(function* () {
224
+ const bin = yield* pick()
225
+ if (Option.isSome(bin)) {
226
+ return Option.some(path.join(binDir, bin.value))
227
+ }
228
+
229
+ yield* fs.remove(path.join(dir, "package-lock.json")).pipe(Effect.orElseSucceed(() => {}))
230
+
231
+ yield* add(pkg)
232
+
233
+ const resolved = yield* pick()
234
+ if (Option.isNone(resolved)) return Option.none<string>()
235
+ return Option.some(path.join(binDir, resolved.value))
236
+ }).pipe(
237
+ Effect.scoped,
238
+ Effect.orElseSucceed(() => Option.none<string>()),
239
+ ),
240
+ )
241
+ })
242
+
243
+ return Service.of({
244
+ add,
245
+ install,
246
+ which,
247
+ })
248
+ }),
249
+ )
250
+
251
+ export const node = makeGlobalNode({
252
+ service: Service,
253
+ layer: layer,
254
+ deps: [FSUtil.node, Global.node, filesystem, EffectFlock.node],
255
+ })
256
+
257
+ const { runPromise } = makeRuntime(Service, LayerNode.compile(node))
258
+
259
+ export async function install(...args: Parameters<Interface["install"]>) {
260
+ return runPromise((svc) => svc.install(...args))
261
+ }
262
+
263
+ export async function add(...args: Parameters<Interface["add"]>) {
264
+ return runPromise((svc) => svc.add(...args))
265
+ }
266
+
267
+ export async function which(...args: Parameters<Interface["which"]>) {
268
+ return runPromise((svc) => svc.which(...args))
269
+ }
@@ -0,0 +1,276 @@
1
+ // Branded HTML pages for local OAuth callback servers.
2
+ //
3
+ // These are served by the loopback HTTP servers that finish an OAuth exchange
4
+ // (MCP, Codex/ChatGPT, xAI, Snowflake, DigitalOcean, ...). The functions return
5
+ // a fully self-contained HTML string with no external assets, so they work
6
+ // offline and drop into any transport (`res.end(...)`, Effect `response.end`,
7
+ // etc.).
8
+ //
9
+ // The visual language mirrors the NeuroCode app: the design tokens are a curated
10
+ // subset of the OC-2 semantic tokens in `packages/ui/src/styles/theme.css`, and
11
+ // the wordmark is the same geometry as `packages/ui/src/components/logo.tsx`.
12
+ // Keep this file in sync with those sources when the brand changes.
13
+
14
+ export interface CallbackPageOptions {
15
+ /** Friendly integration name shown as a subtitle, e.g. "xAI", "Snowflake", "MCP". */
16
+ provider?: string
17
+ /** Attempt to close the window shortly after success. Defaults to true. */
18
+ autoClose?: boolean
19
+ }
20
+
21
+ export function success(options?: CallbackPageOptions) {
22
+ const provider = options?.provider
23
+ return renderDocument({
24
+ title: "Authorization successful",
25
+ body: renderCard({
26
+ status: "success",
27
+ headline: "Authorization successful",
28
+ message: provider ? `NeuroCode is now connected to ${escapeHtml(provider)}.` : "NeuroCode is now authorized.",
29
+ footnote: "You can close this window.",
30
+ }),
31
+ script: options?.autoClose === false ? undefined : AUTO_CLOSE_SCRIPT,
32
+ })
33
+ }
34
+
35
+ export function error(detail: string, options?: CallbackPageOptions) {
36
+ const provider = options?.provider
37
+ return renderDocument({
38
+ title: "Authorization failed",
39
+ body: renderCard({
40
+ status: "error",
41
+ headline: "Authorization failed",
42
+ message: provider
43
+ ? `NeuroCode couldn't finish connecting to ${escapeHtml(provider)}.`
44
+ : "NeuroCode couldn't complete authorization.",
45
+ detail,
46
+ footnote: "Close this window and try again from NeuroCode.",
47
+ }),
48
+ })
49
+ }
50
+
51
+ export interface BootstrapOptions {
52
+ /** Same-origin path the in-browser script POSTs the parsed callback to. */
53
+ tokenPath: string
54
+ provider?: string
55
+ }
56
+
57
+ // For flows where the credential arrives in the URL fragment (implicit grant),
58
+ // the browser must relay it back to the loopback server. This renders a pending
59
+ // page whose script reads the fragment, POSTs it to `tokenPath`, then resolves
60
+ // to the success or error state in place.
61
+ export function bootstrap(options: BootstrapOptions) {
62
+ return renderDocument({
63
+ title: "Finishing sign-in",
64
+ body: renderCard({
65
+ status: "pending",
66
+ headline: "Finishing sign-in",
67
+ message: options.provider
68
+ ? `Completing your ${escapeHtml(options.provider)} authorization.`
69
+ : "Completing authorization.",
70
+ footnote: "You can close this window once sign-in finishes.",
71
+ }),
72
+ script: bootstrapScript(options),
73
+ })
74
+ }
75
+
76
+ export * as OauthCallbackPage from "./page"
77
+
78
+ type Status = "pending" | "success" | "error"
79
+
80
+ function renderCard(input: { status: Status; headline: string; message: string; detail?: string; footnote: string }) {
81
+ const detail = input.detail?.trim()
82
+ return `<main class="card" id="oc-card" data-status="${input.status}" role="status" aria-live="polite">
83
+ <div class="brand">${WORDMARK}</div>
84
+ <div class="status" aria-hidden="true">
85
+ <span class="icon icon-pending">${ICON_SPINNER}</span>
86
+ <span class="icon icon-success">${ICON_CHECK}</span>
87
+ <span class="icon icon-error">${ICON_CROSS}</span>
88
+ </div>
89
+ <h1 class="headline" id="oc-headline">${escapeHtml(input.headline)}</h1>
90
+ <p class="message" id="oc-message">${input.message}</p>
91
+ <pre class="detail" id="oc-detail"${detail ? "" : " hidden"}>${detail ? escapeHtml(detail) : ""}</pre>
92
+ <p class="footnote" id="oc-footnote">${escapeHtml(input.footnote)}</p>
93
+ </main>`
94
+ }
95
+
96
+ function renderDocument(input: { title: string; body: string; script?: string }) {
97
+ return `<!doctype html>
98
+ <html lang="en">
99
+ <head>
100
+ <meta charset="utf-8" />
101
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
102
+ <meta name="robots" content="noindex" />
103
+ <title>${escapeHtml(input.title)} · NeuroCode</title>
104
+ <style>${STYLES}</style>
105
+ </head>
106
+ <body>
107
+ ${input.body}${input.script ? `\n <script>${input.script}</script>` : ""}
108
+ </body>
109
+ </html>`
110
+ }
111
+
112
+ const AUTO_CLOSE_SCRIPT = `setTimeout(function(){try{window.close()}catch(e){}},2500)`
113
+
114
+ function bootstrapScript(options: BootstrapOptions) {
115
+ return `var PROVIDER=${scriptString(options.provider ?? "")};
116
+ var TOKEN_URL=new URL(${scriptString(options.tokenPath)},window.location.origin).href;
117
+ (function(){
118
+ var card=document.getElementById("oc-card"),headline=document.getElementById("oc-headline"),message=document.getElementById("oc-message"),detail=document.getElementById("oc-detail"),footnote=document.getElementById("oc-footnote");
119
+ function fail(text){card.dataset.status="error";headline.textContent="Authorization failed";message.textContent=PROVIDER?("NeuroCode couldn't finish connecting to "+PROVIDER+"."):"NeuroCode couldn't complete authorization.";if(text){detail.textContent=text;detail.hidden=false}footnote.textContent="Close this window and try again from NeuroCode."}
120
+ function ok(){card.dataset.status="success";headline.textContent="Authorization successful";message.textContent=PROVIDER?("NeuroCode is now connected to "+PROVIDER+"."):"NeuroCode is now authorized.";detail.hidden=true;footnote.textContent="You can close this window.";setTimeout(function(){try{window.close()}catch(e){}},2500)}
121
+ try{
122
+ var hash=new URLSearchParams((window.location.hash||"").slice(1));
123
+ var search=new URLSearchParams(window.location.search||"");
124
+ var err=hash.get("error")||search.get("error");
125
+ var errDescription=hash.get("error_description")||search.get("error_description");
126
+ var body=err?{error:err,error_description:errDescription||""}:{access_token:hash.get("access_token")||"",expires_in:hash.get("expires_in")||"0",state:hash.get("state")||""};
127
+ fetch(TOKEN_URL,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(body)}).then(function(res){
128
+ if(!res.ok)return res.text().catch(function(){return""}).then(function(t){throw new Error(t||("callback failed ("+res.status+")"))});
129
+ if(err){fail(errDescription||err);return}
130
+ ok();
131
+ }).catch(function(e){fail(String(e&&e.message?e.message:e))});
132
+ }catch(e){fail(String(e&&e.message?e.message:e))}
133
+ })()`
134
+ }
135
+
136
+ function scriptString(value: string) {
137
+ return JSON.stringify(value).replaceAll("<", "\\u003c")
138
+ }
139
+
140
+ function escapeHtml(value: string) {
141
+ return value
142
+ .replaceAll("&", "&amp;")
143
+ .replaceAll("<", "&lt;")
144
+ .replaceAll(">", "&gt;")
145
+ .replaceAll('"', "&quot;")
146
+ .replaceAll("'", "&#39;")
147
+ }
148
+
149
+ // Curated subset of OC-2 tokens (packages/ui/src/styles/theme.css). Default is
150
+ // light; dark applies via prefers-color-scheme. The [data-theme] selectors let a
151
+ // host force a scheme without changing the default.
152
+ const LIGHT_VARS = `
153
+ --oc-bg: #f8f8f8;
154
+ --oc-card: #fcfcfc;
155
+ --oc-text-strong: #171717;
156
+ --oc-text-base: #6f6f6f;
157
+ --oc-text-weak: #8f8f8f;
158
+ --oc-border-weak: #e5e5e5;
159
+ --oc-icon-strong: #171717;
160
+ --oc-icon-base: #8f8f8f;
161
+ --oc-icon-weak: #dbdbdb;
162
+ --oc-success: #2dba26;
163
+ --oc-error: #ed4831;
164
+ --oc-detail-bg: #fff8f6;
165
+ --oc-detail-border: #fdc3b7;
166
+ --oc-shadow: 0 16px 48px -6px rgba(0,0,0,.10), 0 6px 12px -2px rgba(0,0,0,.05), 0 1px 2px rgba(0,0,0,.06);`
167
+
168
+ const DARK_VARS = `
169
+ --oc-bg: #101010;
170
+ --oc-card: #161616;
171
+ --oc-text-strong: rgba(255,255,255,.936);
172
+ --oc-text-base: rgba(255,255,255,.618);
173
+ --oc-text-weak: rgba(255,255,255,.422);
174
+ --oc-border-weak: #282828;
175
+ --oc-icon-strong: #ededed;
176
+ --oc-icon-base: #7e7e7e;
177
+ --oc-icon-weak: #343434;
178
+ --oc-success: #12c905;
179
+ --oc-error: #fc533a;
180
+ --oc-detail-bg: #28110c;
181
+ --oc-detail-border: #6a1206;
182
+ --oc-shadow: 0 16px 48px -6px rgba(0,0,0,.55), 0 6px 12px -2px rgba(0,0,0,.35), 0 1px 2px rgba(0,0,0,.4);`
183
+
184
+ const STYLES = `
185
+ :root { color-scheme: light dark;${LIGHT_VARS}
186
+ --oc-font-sans: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
187
+ --oc-font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
188
+ }
189
+ @media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) {${DARK_VARS} } }
190
+ :root[data-theme="dark"] {${DARK_VARS} }
191
+ :root[data-theme="light"] {${LIGHT_VARS} }
192
+
193
+ * { box-sizing: border-box; }
194
+ html, body { margin: 0; height: 100%; }
195
+ body {
196
+ min-height: 100vh;
197
+ display: grid;
198
+ place-items: center;
199
+ padding: 24px;
200
+ background: var(--oc-bg);
201
+ color: var(--oc-text-base);
202
+ font-family: var(--oc-font-sans);
203
+ line-height: 1.5;
204
+ -webkit-font-smoothing: antialiased;
205
+ text-rendering: optimizeLegibility;
206
+ }
207
+ .card {
208
+ width: min(100%, 28rem);
209
+ padding: 2.25rem 2rem 1.75rem;
210
+ background: var(--oc-card);
211
+ border: 1px solid var(--oc-border-weak);
212
+ border-radius: 14px;
213
+ box-shadow: var(--oc-shadow);
214
+ text-align: center;
215
+ }
216
+ .brand { display: flex; justify-content: center; margin-bottom: 1.75rem; }
217
+ .brand svg { height: 19px; width: auto; }
218
+ .status { display: flex; justify-content: center; margin-bottom: 1.125rem; }
219
+ .icon { display: none; line-height: 0; }
220
+ .icon svg { display: block; }
221
+ .card[data-status="pending"] .icon-pending,
222
+ .card[data-status="success"] .icon-success,
223
+ .card[data-status="error"] .icon-error { display: block; }
224
+ .icon-success { color: var(--oc-success); }
225
+ .icon-error { color: var(--oc-error); }
226
+ .icon-pending { color: var(--oc-text-weak); }
227
+ .headline { margin: 0; font-size: 1.1875rem; font-weight: 500; line-height: 1.3; letter-spacing: -0.012em; color: var(--oc-text-strong); }
228
+ .message { margin: 0.5rem 0 0; font-size: 0.9375rem; color: var(--oc-text-base); }
229
+ .detail {
230
+ margin: 1.25rem 0 0;
231
+ padding: 0.75rem 0.875rem;
232
+ text-align: left;
233
+ font-family: var(--oc-font-mono);
234
+ font-size: 0.8125rem;
235
+ line-height: 1.55;
236
+ color: var(--oc-text-strong);
237
+ background: var(--oc-detail-bg);
238
+ border: 1px solid var(--oc-detail-border);
239
+ border-radius: 8px;
240
+ white-space: pre-wrap;
241
+ word-break: break-word;
242
+ max-height: 9.5rem;
243
+ overflow: auto;
244
+ }
245
+ .detail[hidden] { display: none; }
246
+ .footnote { margin: 1.5rem 0 0; font-size: 0.8125rem; color: var(--oc-text-weak); }
247
+ .spinner { animation: oc-spin 0.8s linear infinite; transform-origin: center; }
248
+ @keyframes oc-spin { to { transform: rotate(360deg); } }
249
+ @media (prefers-reduced-motion: reduce) { .spinner { animation: none; } }
250
+ `
251
+
252
+ // NeuroCode wordmark — same path geometry as packages/ui/src/components/logo.tsx (Logo).
253
+ const WORDMARK = `<svg class="wordmark" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 234 42" fill="none" aria-label="NeuroCode" role="img">
254
+ <path d="M18 30H6V18H18V30Z" fill="var(--oc-icon-weak)" />
255
+ <path d="M18 12H6V30H18V12ZM24 36H0V6H24V36Z" fill="var(--oc-icon-base)" />
256
+ <path d="M48 30H36V18H48V30Z" fill="var(--oc-icon-weak)" />
257
+ <path d="M36 30H48V12H36V30ZM54 36H36V42H30V6H54V36Z" fill="var(--oc-icon-base)" />
258
+ <path d="M84 24V30H66V24H84Z" fill="var(--oc-icon-weak)" />
259
+ <path d="M84 24H66V30H84V36H60V6H84V24ZM66 18H78V12H66V18Z" fill="var(--oc-icon-base)" />
260
+ <path d="M108 36H96V18H108V36Z" fill="var(--oc-icon-weak)" />
261
+ <path d="M108 12H96V36H90V6H108V12ZM114 36H108V12H114V36Z" fill="var(--oc-icon-base)" />
262
+ <path d="M144 30H126V18H144V30Z" fill="var(--oc-icon-weak)" />
263
+ <path d="M144 12H126V30H144V36H120V6H144V12Z" fill="var(--oc-icon-strong)" />
264
+ <path d="M168 30H156V18H168V30Z" fill="var(--oc-icon-weak)" />
265
+ <path d="M168 12H156V30H168V12ZM174 36H150V6H174V36Z" fill="var(--oc-icon-strong)" />
266
+ <path d="M198 30H186V18H198V30Z" fill="var(--oc-icon-weak)" />
267
+ <path d="M198 12H186V30H198V12ZM204 36H180V6H198V0H204V36Z" fill="var(--oc-icon-strong)" />
268
+ <path d="M234 24V30H216V24H234Z" fill="var(--oc-icon-weak)" />
269
+ <path d="M216 12V18H228V12H216ZM234 24H216V30H234V36H210V6H234V24Z" fill="var(--oc-icon-strong)" />
270
+ </svg>`
271
+
272
+ const ICON_CHECK = `<svg viewBox="0 0 24 24" width="30" height="30" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="9" /><path d="m8.5 12.5 2.4 2.4 4.6-5.4" /></svg>`
273
+
274
+ const ICON_CROSS = `<svg viewBox="0 0 24 24" width="30" height="30" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="9" /><path d="m9 9 6 6m0-6-6 6" /></svg>`
275
+
276
+ const ICON_SPINNER = `<svg class="spinner" viewBox="0 0 24 24" width="30" height="30" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><circle cx="12" cy="12" r="9" opacity="0.2" /><path d="M21 12a9 9 0 0 0-9-9" /></svg>`
@@ -0,0 +1,71 @@
1
+ import { Formatter, Logger, type LogLevel } from "effect"
2
+ import path from "path"
3
+ import { Global } from "../global"
4
+ import { runID } from "./shared"
5
+
6
+ function formatter(id: string = runID) {
7
+ return Logger.map(Logger.formatStructured, (output) => {
8
+ const messages = Array.isArray(output.message) ? output.message : [output.message]
9
+ return [
10
+ ["timestamp", output.timestamp],
11
+ ["level", output.level],
12
+ ["run", id],
13
+ ...messages.flatMap((value) => (plain(value) ? flatten(value) : [["message", value] as const])),
14
+ ...(output.cause === undefined ? [] : [["cause", output.cause] as const]),
15
+ ...flatten(output.spans),
16
+ ...flatten(output.annotations),
17
+ ]
18
+ .map(([key, value]) => `${key}=${format(value)}`)
19
+ .join(" ")
20
+ })
21
+ }
22
+
23
+ function flatten(
24
+ input: Record<string, unknown>,
25
+ prefix = "",
26
+ seen = new WeakSet<object>(),
27
+ ): Array<readonly [string, unknown]> {
28
+ if (seen.has(input)) return [[prefix, "[Circular]"]]
29
+ seen.add(input)
30
+ const entries = Object.entries(input)
31
+ if (entries.length === 0 && prefix) return [[prefix, input]]
32
+ return entries.flatMap(([key, value]) => {
33
+ const path = prefix ? `${prefix}.${key}` : key
34
+ return plain(value) ? flatten(value, path, seen) : [[path, value] as const]
35
+ })
36
+ }
37
+
38
+ function plain(input: unknown): input is Record<string, unknown> {
39
+ if (input === null || typeof input !== "object" || Array.isArray(input)) return false
40
+ const prototype = Object.getPrototypeOf(input)
41
+ return prototype === Object.prototype || prototype === null
42
+ }
43
+
44
+ function format(input: unknown) {
45
+ const value = typeof input === "string" ? input : Formatter.format(input)
46
+ return /^[^\s="\\]+$/.test(value) ? value : JSON.stringify(value)
47
+ }
48
+
49
+ export function fileLogger(file = path.join(Global.Path.log, "neurocode.log"), id: string = runID) {
50
+ // Do not set batchWindow to 0; it causes high idle CPU usage.
51
+ return Logger.toFile(formatter(id), file, { flag: "a" })
52
+ }
53
+
54
+ const stderrLogger = Logger.make((options) => process.stderr.write(formatter().log(options) + "\n"))
55
+
56
+ export function minimumLogLevel() {
57
+ const value = process.env.NEUROCODE_LOG_LEVEL?.toUpperCase()
58
+ const levels = {
59
+ DEBUG: "Debug",
60
+ INFO: "Info",
61
+ WARN: "Warn",
62
+ ERROR: "Error",
63
+ } as const satisfies Record<string, LogLevel.LogLevel>
64
+ return value && value in levels ? levels[value as keyof typeof levels] : levels.INFO
65
+ }
66
+
67
+ export function loggers() {
68
+ return process.env.NEUROCODE_PRINT_LOGS === "1" ? [fileLogger(), stderrLogger] : [fileLogger()]
69
+ }
70
+
71
+ export * as Logging from "./logging"