bonecode 1.0.0

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 (605) hide show
  1. package/ARCHITECTURE.md +183 -0
  2. package/README.md +71 -0
  3. package/bin/bonecode +62 -0
  4. package/bone/migrations/rag_vectors.sql +258 -0
  5. package/bone/output/agent/.dockerignore +7 -0
  6. package/bone/output/agent/.env.example +36 -0
  7. package/bone/output/agent/.github/workflows/ci.yaml +58 -0
  8. package/bone/output/agent/AgentDomain.bone.map +350 -0
  9. package/bone/output/agent/AgentDomain.postman_collection.json +958 -0
  10. package/bone/output/agent/Dockerfile +22 -0
  11. package/bone/output/agent/README.md +47 -0
  12. package/bone/output/agent/admin/index.html +740 -0
  13. package/bone/output/agent/docker-compose.yaml +22 -0
  14. package/bone/output/agent/k8s/deployment.yaml +75 -0
  15. package/bone/output/agent/migrations/agent.sql +36 -0
  16. package/bone/output/agent/migrations/agent_instance.sql +36 -0
  17. package/bone/output/agent/migrations/audit_log.sql +18 -0
  18. package/bone/output/agent/migrations/build_step.sql +34 -0
  19. package/bone/output/agent/migrations/event_outbox.sql +31 -0
  20. package/bone/output/agent/migrations/plan.sql +30 -0
  21. package/bone/output/agent/migrations/task.sql +30 -0
  22. package/bone/output/agent/migrations/tool_call.sql +33 -0
  23. package/bone/output/agent/openapi.yaml +1116 -0
  24. package/bone/output/agent/package.json +36 -0
  25. package/bone/output/agent/schema.graphql +233 -0
  26. package/bone/output/agent/sdk/client.ts +231 -0
  27. package/bone/output/agent/src/algorithms.ts +2 -0
  28. package/bone/output/agent/src/audit.ts +44 -0
  29. package/bone/output/agent/src/auth.ts +57 -0
  30. package/bone/output/agent/src/cron.ts +12 -0
  31. package/bone/output/agent/src/db.ts +32 -0
  32. package/bone/output/agent/src/debug.ts +66 -0
  33. package/bone/output/agent/src/events.ts +243 -0
  34. package/bone/output/agent/src/extensions.ts +54 -0
  35. package/bone/output/agent/src/failure_rules.ts +323 -0
  36. package/bone/output/agent/src/flows.ts +168 -0
  37. package/bone/output/agent/src/health.ts +43 -0
  38. package/bone/output/agent/src/index.ts +100 -0
  39. package/bone/output/agent/src/logger.ts +66 -0
  40. package/bone/output/agent/src/metrics.ts +75 -0
  41. package/bone/output/agent/src/migrate.ts +352 -0
  42. package/bone/output/agent/src/migration_diff.ts +108 -0
  43. package/bone/output/agent/src/notify.ts +125 -0
  44. package/bone/output/agent/src/routes/agent_instance.ts +234 -0
  45. package/bone/output/agent/src/routes/build_step.ts +105 -0
  46. package/bone/output/agent/src/routes/plan.ts +91 -0
  47. package/bone/output/agent/src/routes/task.ts +105 -0
  48. package/bone/output/agent/src/routes/tool_call.ts +166 -0
  49. package/bone/output/agent/src/schemas.ts +384 -0
  50. package/bone/output/agent/src/state_machines/agent_instance.ts +24 -0
  51. package/bone/output/agent/src/state_machines/build_step.ts +22 -0
  52. package/bone/output/agent/src/state_machines/plan.ts +22 -0
  53. package/bone/output/agent/src/state_machines/task.ts +22 -0
  54. package/bone/output/agent/src/state_machines/tool_call.ts +22 -0
  55. package/bone/output/agent/src/tests.ts +362 -0
  56. package/bone/output/agent/src/websocket.ts +201 -0
  57. package/bone/output/agent/tsconfig.json +25 -0
  58. package/bone/output/rag/.dockerignore +7 -0
  59. package/bone/output/rag/.env.example +36 -0
  60. package/bone/output/rag/.github/workflows/ci.yaml +58 -0
  61. package/bone/output/rag/Dockerfile +22 -0
  62. package/bone/output/rag/RAGDomain.bone.map +287 -0
  63. package/bone/output/rag/RAGDomain.postman_collection.json +923 -0
  64. package/bone/output/rag/README.md +47 -0
  65. package/bone/output/rag/admin/index.html +818 -0
  66. package/bone/output/rag/docker-compose.yaml +22 -0
  67. package/bone/output/rag/k8s/deployment.yaml +75 -0
  68. package/bone/output/rag/migrations/audit_log.sql +18 -0
  69. package/bone/output/rag/migrations/code_chunk.sql +34 -0
  70. package/bone/output/rag/migrations/code_file.sql +33 -0
  71. package/bone/output/rag/migrations/event_outbox.sql +31 -0
  72. package/bone/output/rag/migrations/indexing_job.sql +33 -0
  73. package/bone/output/rag/migrations/knowledge_base.sql +35 -0
  74. package/bone/output/rag/migrations/memory_entry.sql +34 -0
  75. package/bone/output/rag/openapi.yaml +1097 -0
  76. package/bone/output/rag/package.json +36 -0
  77. package/bone/output/rag/schema.graphql +245 -0
  78. package/bone/output/rag/sdk/client.ts +234 -0
  79. package/bone/output/rag/src/algorithms.ts +2 -0
  80. package/bone/output/rag/src/audit.ts +37 -0
  81. package/bone/output/rag/src/auth.ts +57 -0
  82. package/bone/output/rag/src/cron.ts +12 -0
  83. package/bone/output/rag/src/db.ts +32 -0
  84. package/bone/output/rag/src/debug.ts +66 -0
  85. package/bone/output/rag/src/events.ts +243 -0
  86. package/bone/output/rag/src/extensions.ts +350 -0
  87. package/bone/output/rag/src/failure_rules.ts +315 -0
  88. package/bone/output/rag/src/flows.ts +239 -0
  89. package/bone/output/rag/src/health.ts +43 -0
  90. package/bone/output/rag/src/index.ts +95 -0
  91. package/bone/output/rag/src/logger.ts +66 -0
  92. package/bone/output/rag/src/metrics.ts +75 -0
  93. package/bone/output/rag/src/migrate.ts +364 -0
  94. package/bone/output/rag/src/migration_diff.ts +108 -0
  95. package/bone/output/rag/src/notify.ts +99 -0
  96. package/bone/output/rag/src/routes/code_chunk.ts +75 -0
  97. package/bone/output/rag/src/routes/code_file.ts +101 -0
  98. package/bone/output/rag/src/routes/indexing_job.ts +87 -0
  99. package/bone/output/rag/src/routes/knowledge_base.ts +230 -0
  100. package/bone/output/rag/src/routes/memory_entry.ts +87 -0
  101. package/bone/output/rag/src/schemas.ts +394 -0
  102. package/bone/output/rag/src/state_machines/code_file.ts +23 -0
  103. package/bone/output/rag/src/state_machines/indexing_job.ts +22 -0
  104. package/bone/output/rag/src/state_machines/knowledge_base.ts +23 -0
  105. package/bone/output/rag/src/state_machines/memory_entry.ts +20 -0
  106. package/bone/output/rag/src/tests.ts +340 -0
  107. package/bone/output/rag/tsconfig.json +25 -0
  108. package/bone/output/session/.dockerignore +7 -0
  109. package/bone/output/session/.env.example +36 -0
  110. package/bone/output/session/.github/workflows/ci.yaml +58 -0
  111. package/bone/output/session/Dockerfile +22 -0
  112. package/bone/output/session/README.md +47 -0
  113. package/bone/output/session/SessionDomain.bone.map +350 -0
  114. package/bone/output/session/SessionDomain.postman_collection.json +958 -0
  115. package/bone/output/session/admin/index.html +667 -0
  116. package/bone/output/session/docker-compose.yaml +22 -0
  117. package/bone/output/session/k8s/deployment.yaml +75 -0
  118. package/bone/output/session/migrations/audit_log.sql +18 -0
  119. package/bone/output/session/migrations/event_outbox.sql +31 -0
  120. package/bone/output/session/migrations/message.sql +31 -0
  121. package/bone/output/session/migrations/part.sql +28 -0
  122. package/bone/output/session/migrations/permission.sql +28 -0
  123. package/bone/output/session/migrations/project.sql +28 -0
  124. package/bone/output/session/migrations/session.sql +38 -0
  125. package/bone/output/session/openapi.yaml +1101 -0
  126. package/bone/output/session/package.json +36 -0
  127. package/bone/output/session/schema.graphql +222 -0
  128. package/bone/output/session/sdk/client.ts +225 -0
  129. package/bone/output/session/src/algorithms.ts +2 -0
  130. package/bone/output/session/src/audit.ts +44 -0
  131. package/bone/output/session/src/auth.ts +57 -0
  132. package/bone/output/session/src/cron.ts +12 -0
  133. package/bone/output/session/src/db.ts +32 -0
  134. package/bone/output/session/src/debug.ts +66 -0
  135. package/bone/output/session/src/events.ts +270 -0
  136. package/bone/output/session/src/extensions.ts +215 -0
  137. package/bone/output/session/src/failure_rules.ts +284 -0
  138. package/bone/output/session/src/flows.ts +168 -0
  139. package/bone/output/session/src/health.ts +43 -0
  140. package/bone/output/session/src/index.ts +100 -0
  141. package/bone/output/session/src/logger.ts +66 -0
  142. package/bone/output/session/src/metrics.ts +75 -0
  143. package/bone/output/session/src/migrate.ts +332 -0
  144. package/bone/output/session/src/migration_diff.ts +108 -0
  145. package/bone/output/session/src/notify.ts +112 -0
  146. package/bone/output/session/src/routes/message.ts +93 -0
  147. package/bone/output/session/src/routes/part.ts +79 -0
  148. package/bone/output/session/src/routes/permission.ts +79 -0
  149. package/bone/output/session/src/routes/project.ts +79 -0
  150. package/bone/output/session/src/routes/session.ts +294 -0
  151. package/bone/output/session/src/schemas.ts +357 -0
  152. package/bone/output/session/src/state_machines/session.ts +23 -0
  153. package/bone/output/session/src/tests.ts +326 -0
  154. package/bone/output/session/src/websocket.ts +201 -0
  155. package/bone/output/session/tsconfig.json +25 -0
  156. package/bone/output/workspace/.dockerignore +7 -0
  157. package/bone/output/workspace/.env.example +36 -0
  158. package/bone/output/workspace/.github/workflows/ci.yaml +58 -0
  159. package/bone/output/workspace/Dockerfile +22 -0
  160. package/bone/output/workspace/README.md +45 -0
  161. package/bone/output/workspace/WorkspaceDomain.bone.map +189 -0
  162. package/bone/output/workspace/WorkspaceDomain.postman_collection.json +621 -0
  163. package/bone/output/workspace/admin/index.html +485 -0
  164. package/bone/output/workspace/docker-compose.yaml +22 -0
  165. package/bone/output/workspace/k8s/deployment.yaml +75 -0
  166. package/bone/output/workspace/migrations/audit_log.sql +18 -0
  167. package/bone/output/workspace/migrations/codebase.sql +34 -0
  168. package/bone/output/workspace/migrations/event_outbox.sql +31 -0
  169. package/bone/output/workspace/migrations/snapshot.sql +32 -0
  170. package/bone/output/workspace/migrations/workspace.sql +33 -0
  171. package/bone/output/workspace/openapi.yaml +721 -0
  172. package/bone/output/workspace/package.json +36 -0
  173. package/bone/output/workspace/schema.graphql +153 -0
  174. package/bone/output/workspace/sdk/client.ts +155 -0
  175. package/bone/output/workspace/src/algorithms.ts +2 -0
  176. package/bone/output/workspace/src/audit.ts +37 -0
  177. package/bone/output/workspace/src/auth.ts +57 -0
  178. package/bone/output/workspace/src/cron.ts +12 -0
  179. package/bone/output/workspace/src/db.ts +32 -0
  180. package/bone/output/workspace/src/debug.ts +66 -0
  181. package/bone/output/workspace/src/events.ts +243 -0
  182. package/bone/output/workspace/src/extensions.ts +44 -0
  183. package/bone/output/workspace/src/failure_rules.ts +153 -0
  184. package/bone/output/workspace/src/health.ts +43 -0
  185. package/bone/output/workspace/src/index.ts +89 -0
  186. package/bone/output/workspace/src/logger.ts +66 -0
  187. package/bone/output/workspace/src/metrics.ts +75 -0
  188. package/bone/output/workspace/src/migrate.ts +220 -0
  189. package/bone/output/workspace/src/migration_diff.ts +108 -0
  190. package/bone/output/workspace/src/notify.ts +73 -0
  191. package/bone/output/workspace/src/routes/codebase.ts +87 -0
  192. package/bone/output/workspace/src/routes/snapshot.ts +127 -0
  193. package/bone/output/workspace/src/routes/workspace.ts +190 -0
  194. package/bone/output/workspace/src/schemas.ts +231 -0
  195. package/bone/output/workspace/src/state_machines/codebase.ts +21 -0
  196. package/bone/output/workspace/src/state_machines/snapshot.ts +20 -0
  197. package/bone/output/workspace/src/state_machines/workspace.ts +21 -0
  198. package/bone/output/workspace/src/tests.ts +249 -0
  199. package/bone/output/workspace/tsconfig.json +25 -0
  200. package/compat/opencode_adapter.ts +410 -0
  201. package/package.json +69 -0
  202. package/scripts/check_benchmark_session.js +34 -0
  203. package/scripts/check_finish_event.js +24 -0
  204. package/scripts/check_parts.js +15 -0
  205. package/scripts/compile.js +79 -0
  206. package/scripts/copy_opencode.ps1 +53 -0
  207. package/scripts/create_functions.sql +129 -0
  208. package/scripts/migrate.js +85 -0
  209. package/scripts/migrate_from_opencode.ts +218 -0
  210. package/scripts/test_agent_loop.js +101 -0
  211. package/scripts/test_api.ps1 +116 -0
  212. package/scripts/test_context_builder.js +136 -0
  213. package/scripts/test_context_builder.ts +97 -0
  214. package/scripts/test_rag.js +189 -0
  215. package/scripts/test_stream_events.js +36 -0
  216. package/scripts/test_websocket_and_saga.js +216 -0
  217. package/src/cli.ts +475 -0
  218. package/src/config.ts +162 -0
  219. package/src/context_builder.ts +598 -0
  220. package/src/engine/account/account.sql.ts +39 -0
  221. package/src/engine/account/account.ts +456 -0
  222. package/src/engine/account/repo.ts +166 -0
  223. package/src/engine/account/schema.ts +99 -0
  224. package/src/engine/account/url.ts +8 -0
  225. package/src/engine/acp/README.md +174 -0
  226. package/src/engine/acp/agent.ts +1968 -0
  227. package/src/engine/acp/runtime.ts +22 -0
  228. package/src/engine/acp/session.ts +122 -0
  229. package/src/engine/acp/types.ts +24 -0
  230. package/src/engine/agent/agent.ts +463 -0
  231. package/src/engine/agent/generate.txt +75 -0
  232. package/src/engine/agent/prompt/compaction.txt +9 -0
  233. package/src/engine/agent/prompt/explore.txt +18 -0
  234. package/src/engine/agent/prompt/scout.txt +36 -0
  235. package/src/engine/agent/prompt/summary.txt +11 -0
  236. package/src/engine/agent/prompt/title.txt +44 -0
  237. package/src/engine/agent/subagent-permissions.ts +34 -0
  238. package/src/engine/auth/index.ts +96 -0
  239. package/src/engine/background/background/job.ts +200 -0
  240. package/src/engine/background/job.ts +200 -0
  241. package/src/engine/bus/bus-event.ts +45 -0
  242. package/src/engine/bus/global.ts +22 -0
  243. package/src/engine/bus/index.ts +203 -0
  244. package/src/engine/command/command/index.ts +181 -0
  245. package/src/engine/command/command/template/initialize.txt +66 -0
  246. package/src/engine/command/command/template/review.txt +101 -0
  247. package/src/engine/command/index.ts +181 -0
  248. package/src/engine/command/template/initialize.txt +66 -0
  249. package/src/engine/command/template/review.txt +101 -0
  250. package/src/engine/config/agent.ts +172 -0
  251. package/src/engine/config/attachment.ts +25 -0
  252. package/src/engine/config/command.ts +62 -0
  253. package/src/engine/config/config.ts +833 -0
  254. package/src/engine/config/console-state.ts +14 -0
  255. package/src/engine/config/entry-name.ts +16 -0
  256. package/src/engine/config/error.ts +23 -0
  257. package/src/engine/config/formatter.ts +13 -0
  258. package/src/engine/config/layout.ts +6 -0
  259. package/src/engine/config/lsp.ts +43 -0
  260. package/src/engine/config/managed.ts +71 -0
  261. package/src/engine/config/markdown.ts +96 -0
  262. package/src/engine/config/mcp.ts +56 -0
  263. package/src/engine/config/model-id.ts +5 -0
  264. package/src/engine/config/parse.ts +79 -0
  265. package/src/engine/config/paths.ts +45 -0
  266. package/src/engine/config/permission.ts +58 -0
  267. package/src/engine/config/plugin.ts +84 -0
  268. package/src/engine/config/provider.ts +111 -0
  269. package/src/engine/config/reference.ts +23 -0
  270. package/src/engine/config/server.ts +19 -0
  271. package/src/engine/config/skills.ts +14 -0
  272. package/src/engine/config/variable.ts +90 -0
  273. package/src/engine/control-plane/adapters/index.ts +41 -0
  274. package/src/engine/control-plane/adapters/worktree.ts +96 -0
  275. package/src/engine/control-plane/dev/README.md +19 -0
  276. package/src/engine/control-plane/dev/debug-workspace-plugin.ts +73 -0
  277. package/src/engine/control-plane/schema.ts +14 -0
  278. package/src/engine/control-plane/types.ts +59 -0
  279. package/src/engine/control-plane/util.ts +39 -0
  280. package/src/engine/control-plane/workspace-adapter-runtime.ts +51 -0
  281. package/src/engine/control-plane/workspace-context.ts +26 -0
  282. package/src/engine/control-plane/workspace.sql.ts +20 -0
  283. package/src/engine/control-plane/workspace.ts +1072 -0
  284. package/src/engine/data-migration.ts +161 -0
  285. package/src/engine/effect/app-runtime.ts +143 -0
  286. package/src/engine/effect/bootstrap-runtime.ts +29 -0
  287. package/src/engine/effect/bridge.ts +84 -0
  288. package/src/engine/effect/config-service.ts +67 -0
  289. package/src/engine/effect/instance-ref.ts +11 -0
  290. package/src/engine/effect/instance-registry.ts +12 -0
  291. package/src/engine/effect/instance-state.ts +72 -0
  292. package/src/engine/effect/promise.ts +17 -0
  293. package/src/engine/effect/run-service.ts +47 -0
  294. package/src/engine/effect/runner.ts +217 -0
  295. package/src/engine/effect/runtime-flags.ts +74 -0
  296. package/src/engine/effect/service-use.ts +38 -0
  297. package/src/engine/env/index.ts +37 -0
  298. package/src/engine/event-v2-bridge.ts +89 -0
  299. package/src/engine/file/file/ignore.ts +81 -0
  300. package/src/engine/file/file/index.ts +651 -0
  301. package/src/engine/file/file/protected.ts +59 -0
  302. package/src/engine/file/file/ripgrep.ts +481 -0
  303. package/src/engine/file/file/watcher.ts +167 -0
  304. package/src/engine/file/ignore.ts +81 -0
  305. package/src/engine/file/index.ts +651 -0
  306. package/src/engine/file/protected.ts +59 -0
  307. package/src/engine/file/ripgrep.ts +481 -0
  308. package/src/engine/file/watcher.ts +167 -0
  309. package/src/engine/format/format/formatter.ts +404 -0
  310. package/src/engine/format/format/index.ts +209 -0
  311. package/src/engine/format/formatter.ts +404 -0
  312. package/src/engine/format/index.ts +209 -0
  313. package/src/engine/git/git/index.ts +347 -0
  314. package/src/engine/git/index.ts +347 -0
  315. package/src/engine/id/id.ts +80 -0
  316. package/src/engine/ide/index.ts +70 -0
  317. package/src/engine/image/image/image.ts +176 -0
  318. package/src/engine/image/image.ts +176 -0
  319. package/src/engine/index.ts +251 -0
  320. package/src/engine/installation/index.ts +327 -0
  321. package/src/engine/lsp/client.ts +707 -0
  322. package/src/engine/lsp/diagnostic.ts +29 -0
  323. package/src/engine/lsp/language.ts +121 -0
  324. package/src/engine/lsp/launch.ts +21 -0
  325. package/src/engine/lsp/lsp/client.ts +707 -0
  326. package/src/engine/lsp/lsp/diagnostic.ts +29 -0
  327. package/src/engine/lsp/lsp/language.ts +121 -0
  328. package/src/engine/lsp/lsp/launch.ts +21 -0
  329. package/src/engine/lsp/lsp/lsp.ts +507 -0
  330. package/src/engine/lsp/lsp/server.ts +2064 -0
  331. package/src/engine/lsp/lsp.ts +507 -0
  332. package/src/engine/lsp/server.ts +2064 -0
  333. package/src/engine/mcp/auth.ts +146 -0
  334. package/src/engine/mcp/index.ts +958 -0
  335. package/src/engine/mcp/mcp/auth.ts +146 -0
  336. package/src/engine/mcp/mcp/index.ts +958 -0
  337. package/src/engine/mcp/mcp/oauth-callback.ts +232 -0
  338. package/src/engine/mcp/mcp/oauth-provider.ts +214 -0
  339. package/src/engine/mcp/oauth-callback.ts +232 -0
  340. package/src/engine/mcp/oauth-provider.ts +214 -0
  341. package/src/engine/node.ts +6 -0
  342. package/src/engine/patch/index.ts +689 -0
  343. package/src/engine/patch/patch/index.ts +689 -0
  344. package/src/engine/permission/arity.ts +163 -0
  345. package/src/engine/permission/evaluate.ts +15 -0
  346. package/src/engine/permission/index.ts +306 -0
  347. package/src/engine/permission/permission/arity.ts +163 -0
  348. package/src/engine/permission/permission/evaluate.ts +15 -0
  349. package/src/engine/permission/permission/index.ts +306 -0
  350. package/src/engine/permission/permission/schema.ts +13 -0
  351. package/src/engine/permission/schema.ts +13 -0
  352. package/src/engine/plugin/azure.ts +26 -0
  353. package/src/engine/plugin/cloudflare.ts +76 -0
  354. package/src/engine/plugin/codex.ts +622 -0
  355. package/src/engine/plugin/digitalocean.ts +411 -0
  356. package/src/engine/plugin/github-copilot/copilot.ts +394 -0
  357. package/src/engine/plugin/github-copilot/models.ts +196 -0
  358. package/src/engine/plugin/index.ts +295 -0
  359. package/src/engine/plugin/install.ts +439 -0
  360. package/src/engine/plugin/loader.ts +216 -0
  361. package/src/engine/plugin/meta.ts +188 -0
  362. package/src/engine/plugin/shared.ts +323 -0
  363. package/src/engine/project/bootstrap-service.ts +9 -0
  364. package/src/engine/project/bootstrap.ts +75 -0
  365. package/src/engine/project/instance-context.ts +24 -0
  366. package/src/engine/project/instance-layer.ts +11 -0
  367. package/src/engine/project/instance-runtime.ts +16 -0
  368. package/src/engine/project/instance-store.ts +193 -0
  369. package/src/engine/project/project.sql.ts +17 -0
  370. package/src/engine/project/project.ts +537 -0
  371. package/src/engine/project/schema.ts +13 -0
  372. package/src/engine/project/vcs.ts +405 -0
  373. package/src/engine/provider/auth.ts +225 -0
  374. package/src/engine/provider/error.ts +204 -0
  375. package/src/engine/provider/model-status.ts +8 -0
  376. package/src/engine/provider/provider.ts +1843 -0
  377. package/src/engine/provider/schema.ts +30 -0
  378. package/src/engine/provider/sdk/copilot/AGENTS.md +1 -0
  379. package/src/engine/provider/transform.ts +1376 -0
  380. package/src/engine/pty/index.ts +365 -0
  381. package/src/engine/pty/input.ts +24 -0
  382. package/src/engine/pty/pty/index.ts +365 -0
  383. package/src/engine/pty/pty/input.ts +24 -0
  384. package/src/engine/pty/pty/pty.bun.ts +26 -0
  385. package/src/engine/pty/pty/pty.node.ts +27 -0
  386. package/src/engine/pty/pty/pty.ts +25 -0
  387. package/src/engine/pty/pty/schema.ts +14 -0
  388. package/src/engine/pty/pty/ticket.ts +68 -0
  389. package/src/engine/pty/pty.bun.ts +26 -0
  390. package/src/engine/pty/pty.node.ts +27 -0
  391. package/src/engine/pty/pty.ts +25 -0
  392. package/src/engine/pty/schema.ts +14 -0
  393. package/src/engine/pty/ticket.ts +68 -0
  394. package/src/engine/question/index.ts +213 -0
  395. package/src/engine/question/question/index.ts +213 -0
  396. package/src/engine/question/question/schema.ts +10 -0
  397. package/src/engine/question/schema.ts +10 -0
  398. package/src/engine/reference/reference/reference.ts +241 -0
  399. package/src/engine/reference/reference/repository-cache.ts +147 -0
  400. package/src/engine/reference/reference.ts +241 -0
  401. package/src/engine/reference/repository-cache.ts +147 -0
  402. package/src/engine/session/compaction.ts +651 -0
  403. package/src/engine/session/compaction_logic.ts +120 -0
  404. package/src/engine/session/instruction.ts +238 -0
  405. package/src/engine/session/instruction_loader.ts +54 -0
  406. package/src/engine/session/llm.ts +459 -0
  407. package/src/engine/session/message-error.ts +14 -0
  408. package/src/engine/session/message-v2.ts +1202 -0
  409. package/src/engine/session/message.ts +146 -0
  410. package/src/engine/session/overflow.ts +32 -0
  411. package/src/engine/session/overflow_check.ts +46 -0
  412. package/src/engine/session/processor.ts +823 -0
  413. package/src/engine/session/prompt/anthropic.txt +105 -0
  414. package/src/engine/session/prompt/beast.txt +147 -0
  415. package/src/engine/session/prompt/build-switch.txt +5 -0
  416. package/src/engine/session/prompt/codex.txt +79 -0
  417. package/src/engine/session/prompt/copilot-gpt-5.txt +143 -0
  418. package/src/engine/session/prompt/default.txt +105 -0
  419. package/src/engine/session/prompt/gemini.txt +155 -0
  420. package/src/engine/session/prompt/gpt.txt +107 -0
  421. package/src/engine/session/prompt/kimi.txt +95 -0
  422. package/src/engine/session/prompt/max-steps.txt +16 -0
  423. package/src/engine/session/prompt/plan-reminder-anthropic.txt +67 -0
  424. package/src/engine/session/prompt/plan.txt +26 -0
  425. package/src/engine/session/prompt/trinity.txt +97 -0
  426. package/src/engine/session/prompt.ts +671 -0
  427. package/src/engine/session/provider_transform.ts +187 -0
  428. package/src/engine/session/retry.ts +200 -0
  429. package/src/engine/session/retry_logic.ts +65 -0
  430. package/src/engine/session/revert.ts +162 -0
  431. package/src/engine/session/run-state.ts +153 -0
  432. package/src/engine/session/schema.ts +26 -0
  433. package/src/engine/session/session.sql.ts +137 -0
  434. package/src/engine/session/session.ts +1011 -0
  435. package/src/engine/session/status.ts +94 -0
  436. package/src/engine/session/summary.ts +164 -0
  437. package/src/engine/session/system.ts +84 -0
  438. package/src/engine/session/system_prompt.ts +65 -0
  439. package/src/engine/session/todo.ts +81 -0
  440. package/src/engine/session/tool_registry.ts +162 -0
  441. package/src/engine/share/session.ts +61 -0
  442. package/src/engine/share/share-next.ts +376 -0
  443. package/src/engine/share/share.sql.ts +13 -0
  444. package/src/engine/shell/shell/shell.ts +215 -0
  445. package/src/engine/shell/shell.ts +215 -0
  446. package/src/engine/skill/discovery.ts +116 -0
  447. package/src/engine/skill/index.ts +336 -0
  448. package/src/engine/skill/prompt/customize-opencode.md +377 -0
  449. package/src/engine/skill/skill/discovery.ts +116 -0
  450. package/src/engine/skill/skill/index.ts +336 -0
  451. package/src/engine/skill/skill/prompt/customize-opencode.md +377 -0
  452. package/src/engine/snapshot/index.ts +762 -0
  453. package/src/engine/snapshot/snapshot/index.ts +762 -0
  454. package/src/engine/sync/README.md +179 -0
  455. package/src/engine/sync/event.sql.ts +17 -0
  456. package/src/engine/sync/index.ts +410 -0
  457. package/src/engine/sync/schema.ts +11 -0
  458. package/src/engine/temporary.ts +33 -0
  459. package/src/engine/tool/apply_patch.ts +313 -0
  460. package/src/engine/tool/apply_patch.txt +33 -0
  461. package/src/engine/tool/edit.ts +711 -0
  462. package/src/engine/tool/edit.txt +10 -0
  463. package/src/engine/tool/external-directory.ts +49 -0
  464. package/src/engine/tool/glob.ts +103 -0
  465. package/src/engine/tool/glob.txt +6 -0
  466. package/src/engine/tool/grep.ts +156 -0
  467. package/src/engine/tool/grep.txt +8 -0
  468. package/src/engine/tool/invalid.ts +21 -0
  469. package/src/engine/tool/json-schema.ts +164 -0
  470. package/src/engine/tool/lsp.ts +113 -0
  471. package/src/engine/tool/lsp.txt +24 -0
  472. package/src/engine/tool/mcp-websearch.ts +96 -0
  473. package/src/engine/tool/plan-enter.txt +14 -0
  474. package/src/engine/tool/plan-exit.txt +13 -0
  475. package/src/engine/tool/plan.ts +78 -0
  476. package/src/engine/tool/question.ts +44 -0
  477. package/src/engine/tool/question.txt +10 -0
  478. package/src/engine/tool/read.ts +337 -0
  479. package/src/engine/tool/read.txt +14 -0
  480. package/src/engine/tool/registry.ts +472 -0
  481. package/src/engine/tool/repo_clone.ts +80 -0
  482. package/src/engine/tool/repo_clone.txt +5 -0
  483. package/src/engine/tool/repo_overview.ts +279 -0
  484. package/src/engine/tool/repo_overview.txt +4 -0
  485. package/src/engine/tool/schema.ts +14 -0
  486. package/src/engine/tool/shell/id.ts +19 -0
  487. package/src/engine/tool/shell/prompt.ts +295 -0
  488. package/src/engine/tool/shell/shell.txt +77 -0
  489. package/src/engine/tool/shell.ts +647 -0
  490. package/src/engine/tool/skill.ts +75 -0
  491. package/src/engine/tool/skill.txt +5 -0
  492. package/src/engine/tool/task.ts +337 -0
  493. package/src/engine/tool/task.txt +58 -0
  494. package/src/engine/tool/task_status.ts +179 -0
  495. package/src/engine/tool/task_status.txt +13 -0
  496. package/src/engine/tool/todo.ts +57 -0
  497. package/src/engine/tool/todowrite.txt +167 -0
  498. package/src/engine/tool/tool/apply_patch.ts +313 -0
  499. package/src/engine/tool/tool/apply_patch.txt +33 -0
  500. package/src/engine/tool/tool/edit.ts +711 -0
  501. package/src/engine/tool/tool/edit.txt +10 -0
  502. package/src/engine/tool/tool/external-directory.ts +49 -0
  503. package/src/engine/tool/tool/glob.ts +103 -0
  504. package/src/engine/tool/tool/glob.txt +6 -0
  505. package/src/engine/tool/tool/grep.ts +156 -0
  506. package/src/engine/tool/tool/grep.txt +8 -0
  507. package/src/engine/tool/tool/invalid.ts +21 -0
  508. package/src/engine/tool/tool/json-schema.ts +164 -0
  509. package/src/engine/tool/tool/lsp.ts +113 -0
  510. package/src/engine/tool/tool/lsp.txt +24 -0
  511. package/src/engine/tool/tool/mcp-websearch.ts +96 -0
  512. package/src/engine/tool/tool/plan-enter.txt +14 -0
  513. package/src/engine/tool/tool/plan-exit.txt +13 -0
  514. package/src/engine/tool/tool/plan.ts +78 -0
  515. package/src/engine/tool/tool/question.ts +44 -0
  516. package/src/engine/tool/tool/question.txt +10 -0
  517. package/src/engine/tool/tool/read.ts +337 -0
  518. package/src/engine/tool/tool/read.txt +14 -0
  519. package/src/engine/tool/tool/registry.ts +472 -0
  520. package/src/engine/tool/tool/repo_clone.ts +80 -0
  521. package/src/engine/tool/tool/repo_clone.txt +5 -0
  522. package/src/engine/tool/tool/repo_overview.ts +279 -0
  523. package/src/engine/tool/tool/repo_overview.txt +4 -0
  524. package/src/engine/tool/tool/schema.ts +14 -0
  525. package/src/engine/tool/tool/shell/id.ts +19 -0
  526. package/src/engine/tool/tool/shell/prompt.ts +295 -0
  527. package/src/engine/tool/tool/shell/shell.txt +77 -0
  528. package/src/engine/tool/tool/shell.ts +647 -0
  529. package/src/engine/tool/tool/skill.ts +75 -0
  530. package/src/engine/tool/tool/skill.txt +5 -0
  531. package/src/engine/tool/tool/task.ts +337 -0
  532. package/src/engine/tool/tool/task.txt +58 -0
  533. package/src/engine/tool/tool/task_status.ts +179 -0
  534. package/src/engine/tool/tool/task_status.txt +13 -0
  535. package/src/engine/tool/tool/todo.ts +57 -0
  536. package/src/engine/tool/tool/todowrite.txt +167 -0
  537. package/src/engine/tool/tool/tool.ts +164 -0
  538. package/src/engine/tool/tool/truncate.ts +160 -0
  539. package/src/engine/tool/tool/truncation-dir.ts +4 -0
  540. package/src/engine/tool/tool/webfetch.ts +192 -0
  541. package/src/engine/tool/tool/webfetch.txt +13 -0
  542. package/src/engine/tool/tool/websearch.ts +143 -0
  543. package/src/engine/tool/tool/websearch.txt +14 -0
  544. package/src/engine/tool/tool/write.ts +104 -0
  545. package/src/engine/tool/tool/write.txt +8 -0
  546. package/src/engine/tool/tool.ts +164 -0
  547. package/src/engine/tool/truncate.ts +160 -0
  548. package/src/engine/tool/truncation-dir.ts +4 -0
  549. package/src/engine/tool/webfetch.ts +192 -0
  550. package/src/engine/tool/webfetch.txt +13 -0
  551. package/src/engine/tool/websearch.ts +143 -0
  552. package/src/engine/tool/websearch.txt +14 -0
  553. package/src/engine/tool/write.ts +104 -0
  554. package/src/engine/tool/write.txt +8 -0
  555. package/src/engine/util/archive.ts +17 -0
  556. package/src/engine/util/bom.ts +31 -0
  557. package/src/engine/util/data-url.ts +9 -0
  558. package/src/engine/util/defer.ts +10 -0
  559. package/src/engine/util/effect-http-client.ts +11 -0
  560. package/src/engine/util/error.ts +88 -0
  561. package/src/engine/util/filesystem.ts +252 -0
  562. package/src/engine/util/format.ts +20 -0
  563. package/src/engine/util/iife.ts +3 -0
  564. package/src/engine/util/lazy.ts +20 -0
  565. package/src/engine/util/local-context.ts +25 -0
  566. package/src/engine/util/locale.ts +86 -0
  567. package/src/engine/util/media.ts +26 -0
  568. package/src/engine/util/process.ts +176 -0
  569. package/src/engine/util/queue.ts +32 -0
  570. package/src/engine/util/record.ts +3 -0
  571. package/src/engine/util/repository.ts +158 -0
  572. package/src/engine/util/rpc.ts +66 -0
  573. package/src/engine/util/signal.ts +12 -0
  574. package/src/engine/util/timeout.ts +13 -0
  575. package/src/engine/util/token.ts +7 -0
  576. package/src/engine/util/util/archive.ts +17 -0
  577. package/src/engine/util/util/bom.ts +31 -0
  578. package/src/engine/util/util/data-url.ts +9 -0
  579. package/src/engine/util/util/defer.ts +10 -0
  580. package/src/engine/util/util/effect-http-client.ts +11 -0
  581. package/src/engine/util/util/error.ts +88 -0
  582. package/src/engine/util/util/filesystem.ts +252 -0
  583. package/src/engine/util/util/format.ts +20 -0
  584. package/src/engine/util/util/iife.ts +3 -0
  585. package/src/engine/util/util/lazy.ts +20 -0
  586. package/src/engine/util/util/local-context.ts +25 -0
  587. package/src/engine/util/util/locale.ts +86 -0
  588. package/src/engine/util/util/media.ts +26 -0
  589. package/src/engine/util/util/process.ts +176 -0
  590. package/src/engine/util/util/queue.ts +32 -0
  591. package/src/engine/util/util/record.ts +3 -0
  592. package/src/engine/util/util/repository.ts +158 -0
  593. package/src/engine/util/util/rpc.ts +66 -0
  594. package/src/engine/util/util/signal.ts +12 -0
  595. package/src/engine/util/util/timeout.ts +13 -0
  596. package/src/engine/util/util/token.ts +7 -0
  597. package/src/engine/util/util/which.ts +14 -0
  598. package/src/engine/util/util/wildcard.ts +59 -0
  599. package/src/engine/util/which.ts +14 -0
  600. package/src/engine/util/wildcard.ts +59 -0
  601. package/src/engine/worktree/index.ts +621 -0
  602. package/src/rag_worker.ts +519 -0
  603. package/src/server.ts +201 -0
  604. package/src/tui.ts +637 -0
  605. package/tsconfig.json +24 -0
@@ -0,0 +1,96 @@
1
+ import { Duration, Effect, Schema } from "effect"
2
+ import { HttpClient, HttpClientRequest } from "effect/unstable/http"
3
+
4
+ export const EXA_URL = process.env.EXA_API_KEY
5
+ ? `https://mcp.exa.ai/mcp?exaApiKey=${encodeURIComponent(process.env.EXA_API_KEY)}`
6
+ : "https://mcp.exa.ai/mcp"
7
+ export const PARALLEL_URL = "https://search.parallel.ai/mcp"
8
+
9
+ const McpResult = Schema.Struct({
10
+ result: Schema.Struct({
11
+ content: Schema.Array(
12
+ Schema.Struct({
13
+ type: Schema.String,
14
+ text: Schema.String,
15
+ }),
16
+ ),
17
+ }),
18
+ })
19
+
20
+ const decode = Schema.decodeUnknownEffect(Schema.fromJsonString(McpResult))
21
+
22
+ const parsePayload = (payload: string) =>
23
+ Effect.gen(function* () {
24
+ const trimmed = payload.trim()
25
+ if (!trimmed.startsWith("{")) return undefined
26
+ const data = yield* decode(trimmed)
27
+ return data.result.content.find((item) => item.text)?.text
28
+ })
29
+
30
+ export const parseResponse = Effect.fn("McpWebSearch.parseResponse")(function* (body: string) {
31
+ const trimmed = body.trim()
32
+ const direct = trimmed ? yield* parsePayload(trimmed) : undefined
33
+ if (direct) return direct
34
+
35
+ for (const line of body.split("\n")) {
36
+ if (!line.startsWith("data: ")) continue
37
+ const data = yield* parsePayload(line.substring(6))
38
+ if (data) return data
39
+ }
40
+ return undefined
41
+ })
42
+
43
+ export const SearchArgs = Schema.Struct({
44
+ query: Schema.String,
45
+ type: Schema.String,
46
+ numResults: Schema.Number,
47
+ livecrawl: Schema.String,
48
+ contextMaxCharacters: Schema.optional(Schema.Number),
49
+ })
50
+
51
+ export const ParallelSearchArgs = Schema.Struct({
52
+ objective: Schema.String,
53
+ search_queries: Schema.Array(Schema.String),
54
+ session_id: Schema.optional(Schema.String),
55
+ model_name: Schema.optional(Schema.String),
56
+ })
57
+
58
+ const McpRequest = <F extends Schema.Struct.Fields>(args: Schema.Struct<F>) =>
59
+ Schema.Struct({
60
+ jsonrpc: Schema.Literal("2.0"),
61
+ id: Schema.Literal(1),
62
+ method: Schema.Literal("tools/call"),
63
+ params: Schema.Struct({
64
+ name: Schema.String,
65
+ arguments: args,
66
+ }),
67
+ })
68
+
69
+ export const call = <F extends Schema.Struct.Fields>(
70
+ http: HttpClient.HttpClient,
71
+ url: string,
72
+ tool: string,
73
+ args: Schema.Struct<F>,
74
+ value: Schema.Struct.Type<F>,
75
+ timeout: Duration.Input,
76
+ headers?: Record<string, string>,
77
+ ) =>
78
+ Effect.gen(function* () {
79
+ const request = yield* HttpClientRequest.post(url).pipe(
80
+ HttpClientRequest.accept("application/json, text/event-stream"),
81
+ HttpClientRequest.setHeaders(headers ?? {}),
82
+ HttpClientRequest.schemaBodyJson(McpRequest(args))({
83
+ jsonrpc: "2.0" as const,
84
+ id: 1 as const,
85
+ method: "tools/call" as const,
86
+ params: { name: tool, arguments: value },
87
+ }),
88
+ )
89
+ const response = yield* HttpClient.filterStatusOk(http)
90
+ .execute(request)
91
+ .pipe(
92
+ Effect.timeoutOrElse({ duration: timeout, orElse: () => Effect.die(new Error(`${tool} request timed out`)) }),
93
+ )
94
+ const body = yield* response.text
95
+ return yield* parseResponse(body)
96
+ })
@@ -0,0 +1,14 @@
1
+ Use this tool to suggest switching to plan agent when the user's request would benefit from planning before implementation.
2
+
3
+ If they explicitly mention wanting to create a plan ALWAYS call this tool first.
4
+
5
+ This tool will ask the user if they want to switch to plan agent.
6
+
7
+ Call this tool when:
8
+ - The user's request is complex and would benefit from planning first
9
+ - You want to research and design before making changes
10
+ - The task involves multiple files or significant architectural decisions
11
+
12
+ Do NOT call this tool:
13
+ - For simple, straightforward tasks
14
+ - When the user explicitly wants immediate implementation
@@ -0,0 +1,13 @@
1
+ Use this tool when you have completed the planning phase and are ready to exit plan agent.
2
+
3
+ This tool will ask the user if they want to switch to build agent to start implementing the plan.
4
+
5
+ Call this tool:
6
+ - After you have written a complete plan to the plan file
7
+ - After you have clarified any questions with the user
8
+ - When you are confident the plan is ready for implementation
9
+
10
+ Do NOT call this tool:
11
+ - Before you have created or finalized the plan
12
+ - If you still have unanswered questions about the implementation
13
+ - If the user has indicated they want to continue planning
@@ -0,0 +1,78 @@
1
+ import path from "path"
2
+ import { Effect, Schema } from "effect"
3
+ import * as Tool from "./tool"
4
+ import { Question } from "../question"
5
+ import { Session } from "@/session/session"
6
+ import { MessageV2 } from "../session/message-v2"
7
+ import { Provider } from "@/provider/provider"
8
+ import { InstanceState } from "@/effect/instance-state"
9
+ import { MessageID, PartID } from "../session/schema"
10
+ import EXIT_DESCRIPTION from "./plan-exit.txt"
11
+
12
+ export const Parameters = Schema.Struct({})
13
+
14
+ export const PlanExitTool = Tool.define(
15
+ "plan_exit",
16
+ Effect.gen(function* () {
17
+ const session = yield* Session.Service
18
+ const question = yield* Question.Service
19
+ const provider = yield* Provider.Service
20
+
21
+ return {
22
+ description: EXIT_DESCRIPTION,
23
+ parameters: Parameters,
24
+ execute: (_params: {}, ctx: Tool.Context) =>
25
+ Effect.gen(function* () {
26
+ const instance = yield* InstanceState.context
27
+ const info = yield* session.get(ctx.sessionID)
28
+ const plan = path.relative(instance.worktree, Session.plan(info, instance))
29
+ const answers = yield* question.ask({
30
+ sessionID: ctx.sessionID,
31
+ questions: [
32
+ {
33
+ question: `Plan at ${plan} is complete. Would you like to switch to the build agent and start implementing?`,
34
+ header: "Build Agent",
35
+ custom: false,
36
+ options: [
37
+ { label: "Yes", description: "Switch to build agent and start implementing the plan" },
38
+ { label: "No", description: "Stay with plan agent to continue refining the plan" },
39
+ ],
40
+ },
41
+ ],
42
+ tool: ctx.callID ? { messageID: ctx.messageID, callID: ctx.callID } : undefined,
43
+ })
44
+
45
+ if (answers[0]?.[0] === "No") yield* new Question.RejectedError()
46
+
47
+ const messages = yield* session.messages({ sessionID: ctx.sessionID }).pipe(Effect.orDie)
48
+ const lastUser = messages.findLast((item) => item.info.role === "user" && item.info.model)
49
+ const model =
50
+ lastUser?.info.role === "user" && lastUser.info.model ? lastUser.info.model : yield* provider.defaultModel()
51
+
52
+ const msg: MessageV2.User = {
53
+ id: MessageID.ascending(),
54
+ sessionID: ctx.sessionID,
55
+ role: "user",
56
+ time: { created: Date.now() },
57
+ agent: "build",
58
+ model,
59
+ }
60
+ yield* session.updateMessage(msg)
61
+ yield* session.updatePart({
62
+ id: PartID.ascending(),
63
+ messageID: msg.id,
64
+ sessionID: ctx.sessionID,
65
+ type: "text",
66
+ text: `The plan at ${plan} has been approved, you can now edit files. Execute the plan`,
67
+ synthetic: true,
68
+ } satisfies MessageV2.TextPart)
69
+
70
+ return {
71
+ title: "Switching to build agent",
72
+ output: "User approved switching to build agent. Wait for further instructions.",
73
+ metadata: {},
74
+ }
75
+ }).pipe(Effect.orDie),
76
+ }
77
+ }),
78
+ )
@@ -0,0 +1,44 @@
1
+ import { Effect, Schema } from "effect"
2
+ import * as Tool from "./tool"
3
+ import { Question } from "../question"
4
+ import DESCRIPTION from "./question.txt"
5
+
6
+ export const Parameters = Schema.Struct({
7
+ questions: Schema.mutable(Schema.Array(Question.Prompt)).annotate({ description: "Questions to ask" }),
8
+ })
9
+
10
+ type Metadata = {
11
+ answers: ReadonlyArray<Question.Answer>
12
+ }
13
+
14
+ export const QuestionTool = Tool.define<typeof Parameters, Metadata, Question.Service>(
15
+ "question",
16
+ Effect.gen(function* () {
17
+ const question = yield* Question.Service
18
+
19
+ return {
20
+ description: DESCRIPTION,
21
+ parameters: Parameters,
22
+ execute: (params: Schema.Schema.Type<typeof Parameters>, ctx: Tool.Context<Metadata>) =>
23
+ Effect.gen(function* () {
24
+ const answers = yield* question.ask({
25
+ sessionID: ctx.sessionID,
26
+ questions: params.questions,
27
+ tool: ctx.callID ? { messageID: ctx.messageID, callID: ctx.callID } : undefined,
28
+ })
29
+
30
+ const formatted = params.questions
31
+ .map((q, i) => `"${q.question}"="${answers[i]?.length ? answers[i].join(", ") : "Unanswered"}"`)
32
+ .join(", ")
33
+
34
+ return {
35
+ title: `Asked ${params.questions.length} question${params.questions.length > 1 ? "s" : ""}`,
36
+ output: `User has answered your questions: ${formatted}. You can now continue with the user's answers in mind.`,
37
+ metadata: {
38
+ answers,
39
+ },
40
+ }
41
+ }).pipe(Effect.orDie),
42
+ }
43
+ }),
44
+ )
@@ -0,0 +1,10 @@
1
+ Use this tool when you need to ask the user questions during execution. This allows you to:
2
+ 1. Gather user preferences or requirements
3
+ 2. Clarify ambiguous instructions
4
+ 3. Get decisions on implementation choices as you work
5
+ 4. Offer choices to the user about what direction to take.
6
+
7
+ Usage notes:
8
+ - When `custom` is enabled (default), a "Type your own answer" option is added automatically; don't include "Other" or catch-all options
9
+ - Answers are returned as arrays of labels; set `multiple: true` to allow selecting more than one
10
+ - If you recommend a specific option, make that the first option in the list and add "(Recommended)" at the end of the label
@@ -0,0 +1,337 @@
1
+ import { Effect, Option, Schema, Scope, Stream } from "effect"
2
+ import { NonNegativeInt } from "@opencode-ai/core/schema"
3
+ import * as path from "path"
4
+ import * as Tool from "./tool"
5
+ import { AppFileSystem } from "@opencode-ai/core/filesystem"
6
+ import { LSP } from "@/lsp/lsp"
7
+ import DESCRIPTION from "./read.txt"
8
+ import { InstanceState } from "@/effect/instance-state"
9
+ import { assertExternalDirectoryEffect } from "./external-directory"
10
+ import { Instruction } from "../session/instruction"
11
+ import { isPdfAttachment, sniffAttachmentMime } from "@/util/media"
12
+ import { Reference } from "@/reference/reference"
13
+
14
+ const DEFAULT_READ_LIMIT = 2000
15
+ const MAX_LINE_LENGTH = 2000
16
+ const MAX_LINE_SUFFIX = `... (line truncated to ${MAX_LINE_LENGTH} chars)`
17
+ const MAX_BYTES = 50 * 1024
18
+ const MAX_BYTES_LABEL = `${MAX_BYTES / 1024} KB`
19
+ const SAMPLE_BYTES = 4096
20
+ const SUPPORTED_IMAGE_MIMES = new Set(["image/jpeg", "image/png", "image/gif", "image/webp"])
21
+
22
+ // `offset` and `limit` were originally `z.coerce.number()` — the runtime
23
+ // coercion was useful when the tool was called from a shell but serves no
24
+ // purpose in the LLM tool-call path (the model emits typed JSON). The JSON
25
+ // Schema output is identical (`type: "number"`), so the LLM view is
26
+ // unchanged; purely CLI-facing uses must now send numbers rather than strings.
27
+ export const Parameters = Schema.Struct({
28
+ filePath: Schema.String.annotate({ description: "The absolute path to the file or directory to read" }),
29
+ offset: Schema.optional(NonNegativeInt).annotate({
30
+ description: "The line number to start reading from (1-indexed)",
31
+ }),
32
+ limit: Schema.optional(NonNegativeInt).annotate({
33
+ description: "The maximum number of lines to read (defaults to 2000)",
34
+ }),
35
+ })
36
+
37
+ export const ReadTool = Tool.define(
38
+ "read",
39
+ Effect.gen(function* () {
40
+ const fs = yield* AppFileSystem.Service
41
+ const instruction = yield* Instruction.Service
42
+ const lsp = yield* LSP.Service
43
+ const reference = yield* Reference.Service
44
+ const scope = yield* Scope.Scope
45
+
46
+ const miss = Effect.fn("ReadTool.miss")(function* (filepath: string) {
47
+ const dir = path.dirname(filepath)
48
+ const base = path.basename(filepath)
49
+ const items = yield* fs.readDirectory(dir).pipe(
50
+ Effect.map((items) =>
51
+ items
52
+ .filter(
53
+ (item) =>
54
+ item.toLowerCase().includes(base.toLowerCase()) || base.toLowerCase().includes(item.toLowerCase()),
55
+ )
56
+ .map((item) => path.join(dir, item))
57
+ .slice(0, 3),
58
+ ),
59
+ Effect.catch(() => Effect.succeed([] as string[])),
60
+ )
61
+
62
+ if (items.length > 0) {
63
+ return yield* Effect.fail(
64
+ new Error(`File not found: ${filepath}\n\nDid you mean one of these?\n${items.join("\n")}`),
65
+ )
66
+ }
67
+
68
+ return yield* Effect.fail(new Error(`File not found: ${filepath}`))
69
+ })
70
+
71
+ const list = Effect.fn("ReadTool.list")(function* (filepath: string) {
72
+ const items = yield* fs.readDirectoryEntries(filepath)
73
+ return yield* Effect.forEach(
74
+ items,
75
+ Effect.fnUntraced(function* (item) {
76
+ if (item.type === "directory") return item.name + "/"
77
+ if (item.type !== "symlink") return item.name
78
+
79
+ const target = yield* fs.stat(path.join(filepath, item.name)).pipe(Effect.catch(() => Effect.void))
80
+ if (target?.type === "Directory") return item.name + "/"
81
+ return item.name
82
+ }),
83
+ { concurrency: "unbounded" },
84
+ ).pipe(Effect.map((items: string[]) => items.sort((a, b) => a.localeCompare(b))))
85
+ })
86
+
87
+ const warm = Effect.fn("ReadTool.warm")(function* (filepath: string) {
88
+ yield* lsp.touchFile(filepath).pipe(Effect.ignore, Effect.forkIn(scope))
89
+ })
90
+
91
+ const readSample = Effect.fn("ReadTool.readSample")(function* (
92
+ filepath: string,
93
+ fileSize: number,
94
+ sampleSize: number,
95
+ ) {
96
+ if (fileSize === 0) return new Uint8Array()
97
+
98
+ return yield* Effect.scoped(
99
+ Effect.gen(function* () {
100
+ const file = yield* fs.open(filepath, { flag: "r" })
101
+ return Option.getOrElse(yield* file.readAlloc(Math.min(sampleSize, fileSize)), () => new Uint8Array())
102
+ }),
103
+ )
104
+ })
105
+
106
+ const lines = Effect.fn("ReadTool.lines")(function* (filepath: string, opts: { limit: number; offset: number }) {
107
+ const start = opts.offset - 1
108
+ const raw: string[] = []
109
+ const flags = { bytes: 0, count: 0, cut: false, more: false, done: false }
110
+
111
+ // Note: prefer manual TextDecoder over Stream.decodeText — when the source stream
112
+ // ends without flushing, decodeText drops the final unterminated line. We also
113
+ // avoid Stream.runForEachWhile (it currently swallows the final unterminated
114
+ // line of the upstream splitLines pipeline) and instead toggle a `done` flag
115
+ // and ignore subsequent lines.
116
+ const decoder = new TextDecoder("utf-8")
117
+ yield* fs.stream(filepath).pipe(
118
+ Stream.map((bytes) => decoder.decode(bytes, { stream: true })),
119
+ Stream.splitLines,
120
+ Stream.runForEach((text) =>
121
+ Effect.sync(() => {
122
+ if (flags.done) return
123
+ flags.count += 1
124
+ if (flags.count <= start) return
125
+
126
+ if (raw.length >= opts.limit) {
127
+ flags.more = true
128
+ return
129
+ }
130
+
131
+ const line = text.length > MAX_LINE_LENGTH ? text.substring(0, MAX_LINE_LENGTH) + MAX_LINE_SUFFIX : text
132
+ const size = Buffer.byteLength(line, "utf-8") + (raw.length > 0 ? 1 : 0)
133
+ if (flags.bytes + size > MAX_BYTES) {
134
+ flags.cut = true
135
+ flags.more = true
136
+ flags.done = true
137
+ return
138
+ }
139
+
140
+ raw.push(line)
141
+ flags.bytes += size
142
+ }),
143
+ ),
144
+ )
145
+
146
+ return { raw, count: flags.count, cut: flags.cut, more: flags.more, offset: opts.offset }
147
+ })
148
+
149
+ const isBinaryFile = (filepath: string, bytes: Uint8Array) => {
150
+ const ext = path.extname(filepath).toLowerCase()
151
+ switch (ext) {
152
+ case ".zip":
153
+ case ".tar":
154
+ case ".gz":
155
+ case ".exe":
156
+ case ".dll":
157
+ case ".so":
158
+ case ".class":
159
+ case ".jar":
160
+ case ".war":
161
+ case ".7z":
162
+ case ".doc":
163
+ case ".docx":
164
+ case ".xls":
165
+ case ".xlsx":
166
+ case ".ppt":
167
+ case ".pptx":
168
+ case ".odt":
169
+ case ".ods":
170
+ case ".odp":
171
+ case ".bin":
172
+ case ".dat":
173
+ case ".obj":
174
+ case ".o":
175
+ case ".a":
176
+ case ".lib":
177
+ case ".wasm":
178
+ case ".pyc":
179
+ case ".pyo":
180
+ return true
181
+ }
182
+
183
+ if (bytes.length === 0) return false
184
+
185
+ let nonPrintableCount = 0
186
+ for (let i = 0; i < bytes.length; i++) {
187
+ if (bytes[i] === 0) return true
188
+ if (bytes[i] < 9 || (bytes[i] > 13 && bytes[i] < 32)) {
189
+ nonPrintableCount++
190
+ }
191
+ }
192
+
193
+ return nonPrintableCount / bytes.length > 0.3
194
+ }
195
+
196
+ const run = Effect.fn("ReadTool.execute")(function* (
197
+ params: Schema.Schema.Type<typeof Parameters>,
198
+ ctx: Tool.Context,
199
+ ) {
200
+ const instance = yield* InstanceState.context
201
+ let filepath = params.filePath
202
+ if (!path.isAbsolute(filepath)) {
203
+ filepath = path.resolve(instance.directory, filepath)
204
+ }
205
+ if (process.platform === "win32") {
206
+ filepath = AppFileSystem.normalizePath(filepath)
207
+ }
208
+ yield* reference.ensure(filepath)
209
+ const title = path.relative(instance.worktree, filepath)
210
+
211
+ const stat = yield* fs.stat(filepath).pipe(
212
+ Effect.catchIf(
213
+ (err) => "reason" in err && err.reason._tag === "NotFound",
214
+ () => Effect.succeed(undefined),
215
+ ),
216
+ )
217
+
218
+ yield* assertExternalDirectoryEffect(ctx, filepath, {
219
+ bypass: Boolean(ctx.extra?.["bypassCwdCheck"]) || (yield* reference.contains(filepath)),
220
+ kind: stat?.type === "Directory" ? "directory" : "file",
221
+ })
222
+
223
+ yield* ctx.ask({
224
+ permission: "read",
225
+ patterns: [path.relative(instance.worktree, filepath)],
226
+ always: ["*"],
227
+ metadata: {},
228
+ })
229
+
230
+ if (!stat) return yield* miss(filepath)
231
+
232
+ if (stat.type === "Directory") {
233
+ const items = yield* list(filepath)
234
+ const limit = params.limit ?? DEFAULT_READ_LIMIT
235
+ const offset = params.offset || 1
236
+ const start = offset - 1
237
+ const sliced = items.slice(start, start + limit)
238
+ const truncated = start + sliced.length < items.length
239
+
240
+ return {
241
+ title,
242
+ output: [
243
+ `<path>${filepath}</path>`,
244
+ `<type>directory</type>`,
245
+ `<entries>`,
246
+ sliced.join("\n"),
247
+ truncated
248
+ ? `\n(Showing ${sliced.length} of ${items.length} entries. Use 'offset' parameter to read beyond entry ${offset + sliced.length})`
249
+ : `\n(${items.length} entries)`,
250
+ `</entries>`,
251
+ ].join("\n"),
252
+ metadata: {
253
+ preview: sliced.slice(0, 20).join("\n"),
254
+ truncated,
255
+ loaded: [] as string[],
256
+ },
257
+ }
258
+ }
259
+
260
+ const loaded = yield* instruction.resolve(ctx.messages, filepath, ctx.messageID)
261
+ const sample = yield* readSample(filepath, Number(stat.size), SAMPLE_BYTES)
262
+
263
+ const mime = sniffAttachmentMime(sample, AppFileSystem.mimeType(filepath))
264
+ const isImage = SUPPORTED_IMAGE_MIMES.has(mime)
265
+
266
+ if (isImage || isPdfAttachment(mime)) {
267
+ const bytes = yield* fs.readFile(filepath)
268
+ const msg = isPdfAttachment(mime) ? "PDF read successfully" : "Image read successfully"
269
+ return {
270
+ title,
271
+ output: msg,
272
+ metadata: {
273
+ preview: msg,
274
+ truncated: false,
275
+ loaded: loaded.map((item) => item.filepath),
276
+ },
277
+ attachments: [
278
+ {
279
+ type: "file" as const,
280
+ mime,
281
+ url: `data:${mime};base64,${Buffer.from(bytes).toString("base64")}`,
282
+ },
283
+ ],
284
+ }
285
+ }
286
+
287
+ if (isBinaryFile(filepath, sample)) {
288
+ return yield* Effect.fail(new Error(`Cannot read binary file: ${filepath}`))
289
+ }
290
+
291
+ const file = yield* lines(filepath, { limit: params.limit ?? DEFAULT_READ_LIMIT, offset: params.offset || 1 })
292
+ if (file.count < file.offset && !(file.count === 0 && file.offset === 1)) {
293
+ return yield* Effect.fail(
294
+ new Error(`Offset ${file.offset} is out of range for this file (${file.count} lines)`),
295
+ )
296
+ }
297
+
298
+ let output = [`<path>${filepath}</path>`, `<type>file</type>`, "<content>\n"].join("\n")
299
+ output += file.raw.map((line, i) => `${i + file.offset}: ${line}`).join("\n")
300
+
301
+ const last = file.offset + file.raw.length - 1
302
+ const next = last + 1
303
+ const truncated = file.more || file.cut
304
+ if (file.cut) {
305
+ output += `\n\n(Output capped at ${MAX_BYTES_LABEL}. Showing lines ${file.offset}-${last}. Use offset=${next} to continue.)`
306
+ } else if (file.more) {
307
+ output += `\n\n(Showing lines ${file.offset}-${last} of ${file.count}. Use offset=${next} to continue.)`
308
+ } else {
309
+ output += `\n\n(End of file - total ${file.count} lines)`
310
+ }
311
+ output += "\n</content>"
312
+
313
+ yield* warm(filepath)
314
+
315
+ if (loaded.length > 0) {
316
+ output += `\n\n<system-reminder>\n${loaded.map((item) => item.content).join("\n\n")}\n</system-reminder>`
317
+ }
318
+
319
+ return {
320
+ title,
321
+ output,
322
+ metadata: {
323
+ preview: file.raw.slice(0, 20).join("\n"),
324
+ truncated,
325
+ loaded: loaded.map((item) => item.filepath),
326
+ },
327
+ }
328
+ })
329
+
330
+ return {
331
+ description: DESCRIPTION,
332
+ parameters: Parameters,
333
+ execute: (params: Schema.Schema.Type<typeof Parameters>, ctx: Tool.Context) =>
334
+ run(params, ctx).pipe(Effect.orDie),
335
+ }
336
+ }),
337
+ )
@@ -0,0 +1,14 @@
1
+ Read a file or directory from the local filesystem. If the path does not exist, an error is returned.
2
+
3
+ Usage:
4
+ - The filePath parameter should be an absolute path.
5
+ - By default, this tool returns up to 2000 lines from the start of the file.
6
+ - The offset parameter is the line number to start from (1-indexed).
7
+ - To read later sections, call this tool again with a larger offset.
8
+ - Use the grep tool to find specific content in large files or files with long lines.
9
+ - If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.
10
+ - Contents are returned with each line prefixed by its line number as `<line>: <content>`. For example, if a file has contents "foo\n", you will receive "1: foo\n". For directories, entries are returned one per line (without line numbers) with a trailing `/` for subdirectories.
11
+ - Any line longer than 2000 characters is truncated.
12
+ - Call this tool in parallel when you know there are multiple files you want to read.
13
+ - Avoid tiny repeated slices (30 line chunks). If you need more context, read a larger window.
14
+ - This tool can read image files and PDFs and return them as file attachments.