soraacode 0.2.0 → 0.2.2

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 (353) hide show
  1. package/LICENSE +21 -0
  2. package/bin/soraacode.exe +10 -0
  3. package/package.json +17 -25
  4. package/{script/postinstall.mjs → postinstall.mjs} +5 -5
  5. package/AGENTS.md +0 -131
  6. package/Dockerfile +0 -18
  7. package/README.md +0 -15
  8. package/bin/opencode +0 -199
  9. package/bin/soraacode +0 -199
  10. package/bunfig.toml +0 -7
  11. package/git +0 -0
  12. package/migration/20260511173437_session-metadata/migration.sql +0 -1
  13. package/migration/20260511173437_session-metadata/snapshot.json +0 -1500
  14. package/parsers-config.ts +0 -1
  15. package/script/bench-search.ts +0 -94
  16. package/script/bench-test-suite.ts +0 -52
  17. package/script/build-headless.ts +0 -51
  18. package/script/build.ts +0 -245
  19. package/script/generate.ts +0 -14
  20. package/script/httpapi-exercise.ts +0 -1
  21. package/script/profile-test-files.ts +0 -42
  22. package/script/publish.ts +0 -213
  23. package/script/run-workspace-server +0 -106
  24. package/script/schema.ts +0 -77
  25. package/script/time.ts +0 -6
  26. package/script/trace-imports.ts +0 -153
  27. package/soraacode-0.1.7.tgz +0 -0
  28. package/specs/effect/error-boundaries-plan.md +0 -235
  29. package/specs/effect/errors.md +0 -207
  30. package/specs/effect/facades.md +0 -218
  31. package/specs/effect/guide.md +0 -247
  32. package/specs/effect/instance-context.md +0 -13
  33. package/specs/effect/loose-ends.md +0 -30
  34. package/specs/effect/migration.md +0 -62
  35. package/specs/effect/routes.md +0 -61
  36. package/specs/effect/schema.md +0 -88
  37. package/specs/effect/server-package.md +0 -58
  38. package/specs/effect/todo.md +0 -241
  39. package/specs/effect/tools.md +0 -88
  40. package/specs/openapi-translation-cleanup.md +0 -204
  41. package/specs/tui-plugins.md +0 -544
  42. package/specs/v2/api.ts +0 -67
  43. package/specs/v2/message-shape.md +0 -136
  44. package/specs/v2/notifications.md +0 -13
  45. package/specs/v2/tui-command-shim.md +0 -67
  46. package/src/account/account.ts +0 -481
  47. package/src/account/repo.ts +0 -171
  48. package/src/account/schema.ts +0 -99
  49. package/src/account/url.ts +0 -8
  50. package/src/acp/agent.ts +0 -95
  51. package/src/acp/config-option.ts +0 -206
  52. package/src/acp/content.ts +0 -269
  53. package/src/acp/directory.ts +0 -212
  54. package/src/acp/error.ts +0 -97
  55. package/src/acp/event.ts +0 -421
  56. package/src/acp/permission.ts +0 -254
  57. package/src/acp/profile.ts +0 -42
  58. package/src/acp/service.ts +0 -1226
  59. package/src/acp/session.ts +0 -232
  60. package/src/acp/tool.ts +0 -364
  61. package/src/acp/usage.ts +0 -243
  62. package/src/agent/agent.ts +0 -450
  63. package/src/agent/generate.txt +0 -75
  64. package/src/agent/prompt/compaction.txt +0 -5
  65. package/src/agent/prompt/explore.txt +0 -18
  66. package/src/agent/prompt/summary.txt +0 -11
  67. package/src/agent/prompt/title.txt +0 -44
  68. package/src/agent/subagent-permissions.ts +0 -27
  69. package/src/audio.d.ts +0 -14
  70. package/src/auth/index.ts +0 -97
  71. package/src/background/job.ts +0 -37
  72. package/src/bus/global.ts +0 -22
  73. package/src/cli/bootstrap.ts +0 -11
  74. package/src/cli/cmd/cmd.ts +0 -7
  75. package/src/cli/cmd/serve.ts +0 -24
  76. package/src/cli/effect/prompt.ts +0 -37
  77. package/src/cli/effect-cmd.ts +0 -96
  78. package/src/cli/error.ts +0 -130
  79. package/src/cli/heap.ts +0 -45
  80. package/src/cli/logo.ts +0 -2
  81. package/src/cli/network.ts +0 -80
  82. package/src/cli/ui.ts +0 -132
  83. package/src/command/index.ts +0 -177
  84. package/src/command/template/initialize.txt +0 -66
  85. package/src/command/template/review.txt +0 -101
  86. package/src/config/agent.ts +0 -59
  87. package/src/config/command.ts +0 -39
  88. package/src/config/config.ts +0 -707
  89. package/src/config/entry-name.ts +0 -19
  90. package/src/config/managed.ts +0 -69
  91. package/src/config/markdown.ts +0 -36
  92. package/src/config/parse.ts +0 -61
  93. package/src/config/paths.ts +0 -45
  94. package/src/config/plugin.ts +0 -79
  95. package/src/config/v2-compat.ts +0 -449
  96. package/src/config/variable.ts +0 -91
  97. package/src/control-plane/adapters/index.ts +0 -41
  98. package/src/control-plane/adapters/worktree.ts +0 -96
  99. package/src/control-plane/dev/README.md +0 -19
  100. package/src/control-plane/dev/debug-workspace-plugin.ts +0 -73
  101. package/src/control-plane/types.ts +0 -59
  102. package/src/control-plane/util.ts +0 -39
  103. package/src/control-plane/workspace-adapter-runtime.ts +0 -51
  104. package/src/control-plane/workspace-context.ts +0 -26
  105. package/src/control-plane/workspace.ts +0 -966
  106. package/src/effect/app-node-builder-v1.ts +0 -12
  107. package/src/effect/app-runtime.ts +0 -135
  108. package/src/effect/bootstrap-runtime.ts +0 -19
  109. package/src/effect/bridge.ts +0 -84
  110. package/src/effect/config-service.ts +0 -67
  111. package/src/effect/instance-ref.ts +0 -11
  112. package/src/effect/instance-registry.ts +0 -12
  113. package/src/effect/instance-state.ts +0 -69
  114. package/src/effect/promise.ts +0 -17
  115. package/src/effect/run-service.ts +0 -47
  116. package/src/effect/runner.ts +0 -217
  117. package/src/effect/runtime-flags.ts +0 -78
  118. package/src/env/index.ts +0 -41
  119. package/src/event-manifest.ts +0 -3
  120. package/src/event-v2-bridge.ts +0 -71
  121. package/src/format/formatter.ts +0 -404
  122. package/src/format/index.ts +0 -203
  123. package/src/git/index.ts +0 -348
  124. package/src/id/id.ts +0 -80
  125. package/src/ide/index.ts +0 -54
  126. package/src/image/image.ts +0 -172
  127. package/src/index-headless.ts +0 -89
  128. package/src/index.ts +0 -93
  129. package/src/installation/index.ts +0 -336
  130. package/src/lsp/client.ts +0 -650
  131. package/src/lsp/diagnostic.ts +0 -29
  132. package/src/lsp/language.ts +0 -121
  133. package/src/lsp/launch.ts +0 -21
  134. package/src/lsp/lsp.ts +0 -507
  135. package/src/lsp/server.ts +0 -1983
  136. package/src/markdown.d.ts +0 -4
  137. package/src/mcp/auth.ts +0 -163
  138. package/src/mcp/browser.ts +0 -37
  139. package/src/mcp/catalog.ts +0 -170
  140. package/src/mcp/index.ts +0 -1004
  141. package/src/mcp/oauth-callback.ts +0 -194
  142. package/src/mcp/oauth-provider.ts +0 -259
  143. package/src/node.ts +0 -4
  144. package/src/patch/index.ts +0 -686
  145. package/src/permission/arity.ts +0 -163
  146. package/src/permission/evaluate.ts +0 -1
  147. package/src/permission/index.ts +0 -223
  148. package/src/plugin/github-copilot/copilot.ts +0 -415
  149. package/src/plugin/github-copilot/models.ts +0 -261
  150. package/src/plugin/index.ts +0 -298
  151. package/src/plugin/install.ts +0 -439
  152. package/src/plugin/loader.ts +0 -237
  153. package/src/plugin/meta.ts +0 -188
  154. package/src/plugin/openai/README.md +0 -31
  155. package/src/plugin/openai/codex.ts +0 -575
  156. package/src/plugin/openai/ws-pool.ts +0 -271
  157. package/src/plugin/openai/ws.ts +0 -383
  158. package/src/plugin/pty-environment.ts +0 -24
  159. package/src/plugin/shared.ts +0 -323
  160. package/src/project/bootstrap-service.ts +0 -9
  161. package/src/project/bootstrap.ts +0 -58
  162. package/src/project/instance-context.ts +0 -24
  163. package/src/project/instance-runtime.ts +0 -16
  164. package/src/project/instance-store.ts +0 -213
  165. package/src/project/project.ts +0 -483
  166. package/src/project/vcs.ts +0 -423
  167. package/src/provider/auth.ts +0 -229
  168. package/src/provider/error.ts +0 -195
  169. package/src/provider/model-status.ts +0 -8
  170. package/src/provider/provider.ts +0 -1955
  171. package/src/provider/transform.ts +0 -1923
  172. package/src/question/index.ts +0 -161
  173. package/src/question/schema.ts +0 -4
  174. package/src/server/auth.ts +0 -48
  175. package/src/server/event.ts +0 -10
  176. package/src/server/global-lifecycle.ts +0 -28
  177. package/src/server/mdns.ts +0 -47
  178. package/src/server/proxy-util.ts +0 -48
  179. package/src/server/routes/instance/httpapi/AGENTS.md +0 -39
  180. package/src/server/routes/instance/httpapi/api.ts +0 -97
  181. package/src/server/routes/instance/httpapi/errors.ts +0 -193
  182. package/src/server/routes/instance/httpapi/groups/config.ts +0 -65
  183. package/src/server/routes/instance/httpapi/groups/control-plane.ts +0 -35
  184. package/src/server/routes/instance/httpapi/groups/control.ts +0 -76
  185. package/src/server/routes/instance/httpapi/groups/event.ts +0 -29
  186. package/src/server/routes/instance/httpapi/groups/experimental.ts +0 -275
  187. package/src/server/routes/instance/httpapi/groups/file.ts +0 -185
  188. package/src/server/routes/instance/httpapi/groups/global.ts +0 -139
  189. package/src/server/routes/instance/httpapi/groups/instance.ts +0 -206
  190. package/src/server/routes/instance/httpapi/groups/mcp.ts +0 -156
  191. package/src/server/routes/instance/httpapi/groups/metadata.ts +0 -18
  192. package/src/server/routes/instance/httpapi/groups/permission.ts +0 -61
  193. package/src/server/routes/instance/httpapi/groups/project-copy.ts +0 -32
  194. package/src/server/routes/instance/httpapi/groups/project.ts +0 -93
  195. package/src/server/routes/instance/httpapi/groups/provider.ts +0 -101
  196. package/src/server/routes/instance/httpapi/groups/pty.ts +0 -172
  197. package/src/server/routes/instance/httpapi/groups/query.ts +0 -12
  198. package/src/server/routes/instance/httpapi/groups/question.ts +0 -74
  199. package/src/server/routes/instance/httpapi/groups/session.ts +0 -462
  200. package/src/server/routes/instance/httpapi/groups/sync.ts +0 -113
  201. package/src/server/routes/instance/httpapi/groups/tui.ts +0 -208
  202. package/src/server/routes/instance/httpapi/groups/workspace.ts +0 -141
  203. package/src/server/routes/instance/httpapi/handlers/config.ts +0 -34
  204. package/src/server/routes/instance/httpapi/handlers/control-plane.ts +0 -37
  205. package/src/server/routes/instance/httpapi/handlers/control.ts +0 -43
  206. package/src/server/routes/instance/httpapi/handlers/event.ts +0 -99
  207. package/src/server/routes/instance/httpapi/handlers/experimental.ts +0 -193
  208. package/src/server/routes/instance/httpapi/handlers/file.ts +0 -139
  209. package/src/server/routes/instance/httpapi/handlers/global.ts +0 -126
  210. package/src/server/routes/instance/httpapi/handlers/instance.ts +0 -110
  211. package/src/server/routes/instance/httpapi/handlers/mcp.ts +0 -111
  212. package/src/server/routes/instance/httpapi/handlers/permission.ts +0 -41
  213. package/src/server/routes/instance/httpapi/handlers/project-copy.ts +0 -83
  214. package/src/server/routes/instance/httpapi/handlers/project.ts +0 -63
  215. package/src/server/routes/instance/httpapi/handlers/provider.ts +0 -116
  216. package/src/server/routes/instance/httpapi/handlers/pty.ts +0 -273
  217. package/src/server/routes/instance/httpapi/handlers/question.ts +0 -54
  218. package/src/server/routes/instance/httpapi/handlers/session-errors.ts +0 -21
  219. package/src/server/routes/instance/httpapi/handlers/session.ts +0 -442
  220. package/src/server/routes/instance/httpapi/handlers/sync.ts +0 -89
  221. package/src/server/routes/instance/httpapi/handlers/tui.ts +0 -131
  222. package/src/server/routes/instance/httpapi/handlers/workspace.ts +0 -102
  223. package/src/server/routes/instance/httpapi/lifecycle.ts +0 -54
  224. package/src/server/routes/instance/httpapi/middleware/authorization.ts +0 -150
  225. package/src/server/routes/instance/httpapi/middleware/compression.ts +0 -64
  226. package/src/server/routes/instance/httpapi/middleware/cors-vary.ts +0 -29
  227. package/src/server/routes/instance/httpapi/middleware/error.ts +0 -44
  228. package/src/server/routes/instance/httpapi/middleware/fence.ts +0 -25
  229. package/src/server/routes/instance/httpapi/middleware/instance-context.ts +0 -43
  230. package/src/server/routes/instance/httpapi/middleware/proxy.ts +0 -131
  231. package/src/server/routes/instance/httpapi/middleware/schema-error.ts +0 -41
  232. package/src/server/routes/instance/httpapi/middleware/workspace-routing.ts +0 -250
  233. package/src/server/routes/instance/httpapi/public.ts +0 -537
  234. package/src/server/routes/instance/httpapi/server.ts +0 -325
  235. package/src/server/routes/instance/httpapi/websocket-tracker.ts +0 -60
  236. package/src/server/server.ts +0 -224
  237. package/src/server/shared/fence.ts +0 -60
  238. package/src/server/shared/pty-ticket.ts +0 -15
  239. package/src/server/shared/public-ui.ts +0 -12
  240. package/src/server/shared/tui-control.ts +0 -28
  241. package/src/server/shared/ui.ts +0 -108
  242. package/src/server/shared/workspace-routing.ts +0 -45
  243. package/src/server/tui-event.ts +0 -1
  244. package/src/session/compaction.ts +0 -608
  245. package/src/session/instruction.ts +0 -237
  246. package/src/session/llm/AGENTS.md +0 -90
  247. package/src/session/llm/ai-sdk.ts +0 -291
  248. package/src/session/llm/native-request.ts +0 -196
  249. package/src/session/llm/native-runtime.ts +0 -195
  250. package/src/session/llm/request.ts +0 -226
  251. package/src/session/llm.ts +0 -390
  252. package/src/session/message-error.ts +0 -14
  253. package/src/session/message-v2.ts +0 -742
  254. package/src/session/message.ts +0 -148
  255. package/src/session/overflow.ts +0 -34
  256. package/src/session/processor.ts +0 -732
  257. package/src/session/prompt/build-switch.txt +0 -5
  258. package/src/session/prompt/default.txt +0 -35
  259. package/src/session/prompt/plan-mode.txt +0 -70
  260. package/src/session/prompt/plan.txt +0 -26
  261. package/src/session/prompt.ts +0 -1631
  262. package/src/session/reminders.ts +0 -92
  263. package/src/session/retry.ts +0 -209
  264. package/src/session/revert.ts +0 -136
  265. package/src/session/run-state.ts +0 -151
  266. package/src/session/schema.ts +0 -26
  267. package/src/session/session.ts +0 -1016
  268. package/src/session/status.ts +0 -56
  269. package/src/session/summary.ts +0 -160
  270. package/src/session/system.ts +0 -123
  271. package/src/session/todo.ts +0 -74
  272. package/src/session/tools.ts +0 -590
  273. package/src/share/session.ts +0 -58
  274. package/src/share/share-next.ts +0 -371
  275. package/src/skill/discovery.ts +0 -140
  276. package/src/skill/index.ts +0 -354
  277. package/src/snapshot/index.ts +0 -807
  278. package/src/sql.d.ts +0 -4
  279. package/src/storage/schema.ts +0 -5
  280. package/src/storage/storage.ts +0 -327
  281. package/src/sync/README.md +0 -179
  282. package/src/sync/schema.ts +0 -11
  283. package/src/temporary.ts +0 -31
  284. package/src/tool/apply_patch.ts +0 -313
  285. package/src/tool/apply_patch.txt +0 -14
  286. package/src/tool/code-mode.ts +0 -310
  287. package/src/tool/edit.ts +0 -737
  288. package/src/tool/edit.txt +0 -13
  289. package/src/tool/external-directory.ts +0 -49
  290. package/src/tool/glob.ts +0 -76
  291. package/src/tool/glob.txt +0 -7
  292. package/src/tool/grep.ts +0 -115
  293. package/src/tool/grep.txt +0 -7
  294. package/src/tool/invalid.ts +0 -21
  295. package/src/tool/json-schema.ts +0 -164
  296. package/src/tool/lsp.ts +0 -113
  297. package/src/tool/lsp.txt +0 -1
  298. package/src/tool/mcp-websearch.ts +0 -96
  299. package/src/tool/plan-enter.txt +0 -1
  300. package/src/tool/plan-exit.txt +0 -1
  301. package/src/tool/plan.ts +0 -79
  302. package/src/tool/question.ts +0 -44
  303. package/src/tool/question.txt +0 -1
  304. package/src/tool/read.ts +0 -386
  305. package/src/tool/read.txt +0 -13
  306. package/src/tool/registry.ts +0 -455
  307. package/src/tool/schema.ts +0 -14
  308. package/src/tool/shell/id.ts +0 -19
  309. package/src/tool/shell/prompt.ts +0 -293
  310. package/src/tool/shell/shell.txt +0 -21
  311. package/src/tool/shell.ts +0 -645
  312. package/src/tool/skill.ts +0 -70
  313. package/src/tool/skill.txt +0 -1
  314. package/src/tool/task.ts +0 -371
  315. package/src/tool/task.txt +0 -11
  316. package/src/tool/todo.ts +0 -46
  317. package/src/tool/todowrite.txt +0 -11
  318. package/src/tool/tool.ts +0 -183
  319. package/src/tool/truncate.ts +0 -156
  320. package/src/tool/truncation-dir.ts +0 -4
  321. package/src/tool/webfetch.ts +0 -192
  322. package/src/tool/webfetch.txt +0 -9
  323. package/src/tool/websearch.ts +0 -143
  324. package/src/tool/websearch.txt +0 -9
  325. package/src/tool/write.ts +0 -104
  326. package/src/tool/write.txt +0 -11
  327. package/src/util/archive.ts +0 -17
  328. package/src/util/bom.ts +0 -27
  329. package/src/util/data-url.ts +0 -9
  330. package/src/util/defer.ts +0 -10
  331. package/src/util/effect-http-client.ts +0 -11
  332. package/src/util/error.ts +0 -16
  333. package/src/util/filesystem.ts +0 -251
  334. package/src/util/html.ts +0 -8
  335. package/src/util/iife.ts +0 -3
  336. package/src/util/lazy.ts +0 -20
  337. package/src/util/local-context.ts +0 -25
  338. package/src/util/locale.ts +0 -4
  339. package/src/util/media.ts +0 -26
  340. package/src/util/process.ts +0 -177
  341. package/src/util/proxy-env.ts +0 -72
  342. package/src/util/queue.ts +0 -32
  343. package/src/util/record.ts +0 -3
  344. package/src/util/repository.ts +0 -232
  345. package/src/util/rpc.ts +0 -66
  346. package/src/util/signal.ts +0 -12
  347. package/src/util/timeout.ts +0 -13
  348. package/src/util/token.ts +0 -1
  349. package/src/util/wildcard.ts +0 -59
  350. package/src/worktree/index.ts +0 -623
  351. package/sst-env.d.ts +0 -10
  352. package/tsconfig.headless.json +0 -13
  353. package/tsconfig.json +0 -16
package/src/tool/plan.ts DELETED
@@ -1,79 +0,0 @@
1
- import path from "path"
2
- import { SessionV1 } from "@soraacode/core/v1/session"
3
- import { Effect, Schema } from "effect"
4
- import * as Tool from "./tool"
5
- import { Question } from "../question"
6
- import { Session } from "@/session/session"
7
- import { MessageV2 } from "../session/message-v2"
8
- import { Provider } from "@/provider/provider"
9
- import { InstanceState } from "@/effect/instance-state"
10
- import { MessageID, PartID } from "../session/schema"
11
- import EXIT_DESCRIPTION from "./plan-exit.txt"
12
-
13
- export const Parameters = Schema.Struct({})
14
-
15
- export const PlanExitTool = Tool.define(
16
- "plan_exit",
17
- Effect.gen(function* () {
18
- const session = yield* Session.Service
19
- const question = yield* Question.Service
20
- const provider = yield* Provider.Service
21
-
22
- return {
23
- description: EXIT_DESCRIPTION,
24
- parameters: Parameters,
25
- execute: (_params: {}, ctx: Tool.Context) =>
26
- Effect.gen(function* () {
27
- const instance = yield* InstanceState.context
28
- const info = yield* session.get(ctx.sessionID)
29
- const plan = path.relative(instance.worktree, Session.plan(info, instance))
30
- const answers = yield* question.ask({
31
- sessionID: ctx.sessionID,
32
- questions: [
33
- {
34
- question: `Plan at ${plan} is complete. Would you like to switch to the build agent and start implementing?`,
35
- header: "Build Agent",
36
- custom: false,
37
- options: [
38
- { label: "Yes", description: "Switch to build agent and start implementing the plan" },
39
- { label: "No", description: "Stay with plan agent to continue refining the plan" },
40
- ],
41
- },
42
- ],
43
- tool: ctx.callID ? { messageID: ctx.messageID, callID: ctx.callID } : undefined,
44
- })
45
-
46
- if (answers[0]?.[0] === "No") yield* new Question.RejectedError()
47
-
48
- const messages = yield* session.messages({ sessionID: ctx.sessionID }).pipe(Effect.orDie)
49
- const lastUser = messages.findLast((item) => item.info.role === "user" && item.info.model)
50
- const model =
51
- lastUser?.info.role === "user" && lastUser.info.model ? lastUser.info.model : yield* provider.defaultModel()
52
-
53
- const msg: SessionV1.User = {
54
- id: MessageID.ascending(),
55
- sessionID: ctx.sessionID,
56
- role: "user",
57
- time: { created: Date.now() },
58
- agent: "build",
59
- model,
60
- }
61
- yield* session.updateMessage(msg)
62
- yield* session.updatePart({
63
- id: PartID.ascending(),
64
- messageID: msg.id,
65
- sessionID: ctx.sessionID,
66
- type: "text",
67
- text: `The plan at ${plan} has been approved, you can now edit files. Execute the plan`,
68
- synthetic: true,
69
- } satisfies SessionV1.TextPart)
70
-
71
- return {
72
- title: "Switching to build agent",
73
- output: "User approved switching to build agent. Wait for further instructions.",
74
- metadata: {},
75
- }
76
- }).pipe(Effect.orDie),
77
- }
78
- }),
79
- )
@@ -1,44 +0,0 @@
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
- )
@@ -1 +0,0 @@
1
- Ask the user for requirements, preferences, clarification, or implementation decisions. custom is enabled by default, so do not add an "Other" option. Use multiple: true for multi-select. If recommending an option, put it first and append (Recommended).
package/src/tool/read.ts DELETED
@@ -1,386 +0,0 @@
1
- import { Effect, Option, Schema, Scope, Stream } from "effect"
2
- import { NonNegativeInt } from "@soraacode/core/schema"
3
- import * as path from "path"
4
- import * as Tool from "./tool"
5
- import { FSUtil } from "@soraacode/core/fs-util"
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
-
13
- const DEFAULT_READ_LIMIT = 2000
14
- const MAX_LINE_LENGTH = 2000
15
- const MAX_LINE_SUFFIX = `... (line truncated to ${MAX_LINE_LENGTH} chars)`
16
- const MAX_BYTES = 50 * 1024
17
- const MAX_BYTES_LABEL = `${MAX_BYTES / 1024} KB`
18
- const SAMPLE_BYTES = 4096
19
- const SUPPORTED_IMAGE_MIMES = new Set(["image/jpeg", "image/png", "image/gif", "image/webp"])
20
-
21
- class ReadStop extends Schema.TaggedErrorClass<ReadStop>()("ReadStop", {}) {}
22
-
23
- // `offset` and `limit` were originally `z.coerce.number()` — the runtime
24
- // coercion was useful when the tool was called from a shell but serves no
25
- // purpose in the LLM tool-call path (the model emits typed JSON). The JSON
26
- // Schema output is identical (`type: "number"`), so the LLM view is
27
- // unchanged; purely CLI-facing uses must now send numbers rather than strings.
28
- export const Parameters = Schema.Struct({
29
- filePath: Schema.String.annotate({ description: "The absolute path to the file or directory to read" }),
30
- offset: Schema.optional(NonNegativeInt).annotate({
31
- description: "The line number to start reading from (1-indexed)",
32
- }),
33
- limit: Schema.optional(NonNegativeInt).annotate({
34
- description: "The maximum number of lines to read (defaults to 2000)",
35
- }),
36
- })
37
-
38
- type Display =
39
- | {
40
- type: "directory"
41
- path: string
42
- entries: string[]
43
- offset: number
44
- totalEntries: number
45
- truncated: boolean
46
- }
47
- | {
48
- type: "file"
49
- path: string
50
- text: string
51
- lineStart: number
52
- lineEnd: number
53
- totalLines: number
54
- truncated: boolean
55
- }
56
-
57
- type Metadata = {
58
- preview: string
59
- truncated: boolean
60
- loaded: string[]
61
- display?: Display
62
- }
63
-
64
- export const ReadTool = Tool.define<
65
- typeof Parameters,
66
- Metadata,
67
- FSUtil.Service | Instruction.Service | LSP.Service | Scope.Scope
68
- >(
69
- "read",
70
- Effect.gen(function* () {
71
- const fs = yield* FSUtil.Service
72
- const instruction = yield* Instruction.Service
73
- const lsp = yield* LSP.Service
74
- const scope = yield* Scope.Scope
75
-
76
- const miss = Effect.fn("ReadTool.miss")(function* (filepath: string) {
77
- const dir = path.dirname(filepath)
78
- const base = path.basename(filepath)
79
- const items = yield* fs.readDirectory(dir).pipe(
80
- Effect.map((items) =>
81
- items
82
- .filter(
83
- (item) =>
84
- item.toLowerCase().includes(base.toLowerCase()) || base.toLowerCase().includes(item.toLowerCase()),
85
- )
86
- .map((item) => path.join(dir, item))
87
- .slice(0, 3),
88
- ),
89
- Effect.catch(() => Effect.succeed([] as string[])),
90
- )
91
-
92
- if (items.length > 0) {
93
- return yield* Effect.fail(
94
- new Error(`File not found: ${filepath}\n\nDid you mean one of these?\n${items.join("\n")}`),
95
- )
96
- }
97
-
98
- return yield* Effect.fail(new Error(`File not found: ${filepath}`))
99
- })
100
-
101
- const list = Effect.fn("ReadTool.list")(function* (filepath: string) {
102
- const items = yield* fs.readDirectoryEntries(filepath)
103
- return yield* Effect.forEach(
104
- items,
105
- Effect.fnUntraced(function* (item) {
106
- if (item.type === "directory") return item.name + "/"
107
- if (item.type !== "symlink") return item.name
108
-
109
- const target = yield* fs.stat(path.join(filepath, item.name)).pipe(Effect.catch(() => Effect.void))
110
- if (target?.type === "Directory") return item.name + "/"
111
- return item.name
112
- }),
113
- { concurrency: "unbounded" },
114
- ).pipe(Effect.map((items: string[]) => items.sort((a, b) => a.localeCompare(b))))
115
- })
116
-
117
- const warm = Effect.fn("ReadTool.warm")(function* (filepath: string) {
118
- // LSP warm-up is optional; do not let a background defect fail an otherwise successful read.
119
- yield* lsp.touchFile(filepath).pipe(Effect.ignoreCause, Effect.forkIn(scope))
120
- })
121
-
122
- const readSample = Effect.fn("ReadTool.readSample")(function* (
123
- filepath: string,
124
- fileSize: number,
125
- sampleSize: number,
126
- ) {
127
- if (fileSize === 0) return new Uint8Array()
128
-
129
- return yield* Effect.scoped(
130
- Effect.gen(function* () {
131
- const file = yield* fs.open(filepath, { flag: "r" })
132
- return Option.getOrElse(yield* file.readAlloc(Math.min(sampleSize, fileSize)), () => new Uint8Array())
133
- }),
134
- )
135
- })
136
-
137
- const lines = Effect.fn("ReadTool.lines")(function* (filepath: string, opts: { limit: number; offset: number }) {
138
- const start = opts.offset - 1
139
- const raw: string[] = []
140
- const flags = { bytes: 0, count: 0, cut: false, more: false, done: false }
141
-
142
- // Note: prefer manual TextDecoder over Stream.decodeText — when the source stream
143
- // ends without flushing, decodeText drops the final unterminated line. We also
144
- // avoid Stream.runForEachWhile (it currently swallows the final unterminated
145
- // line of the upstream splitLines pipeline) and use a tagged error to stop the
146
- // upstream file stream as soon as the byte cap is reached.
147
- const decoder = new TextDecoder("utf-8")
148
- yield* fs.stream(filepath).pipe(
149
- Stream.map((bytes) => decoder.decode(bytes, { stream: true })),
150
- Stream.splitLines,
151
- Stream.runForEach((text) =>
152
- Effect.gen(function* () {
153
- if (flags.done) return yield* new ReadStop()
154
- flags.count += 1
155
- if (flags.count <= start) return
156
-
157
- if (raw.length >= opts.limit) {
158
- flags.more = true
159
- return
160
- }
161
-
162
- const line = text.length > MAX_LINE_LENGTH ? text.substring(0, MAX_LINE_LENGTH) + MAX_LINE_SUFFIX : text
163
- const size = Buffer.byteLength(line, "utf-8") + (raw.length > 0 ? 1 : 0)
164
- if (flags.bytes + size <= MAX_BYTES) {
165
- raw.push(line)
166
- flags.bytes += size
167
- return
168
- }
169
-
170
- flags.cut = true
171
- flags.more = true
172
- flags.done = true
173
- return yield* new ReadStop()
174
- }),
175
- ),
176
- Effect.catchTag("ReadStop", () => Effect.void),
177
- )
178
-
179
- return { raw, count: flags.count, cut: flags.cut, more: flags.more, offset: opts.offset }
180
- })
181
-
182
- const isBinaryFile = (filepath: string, bytes: Uint8Array) => {
183
- const ext = path.extname(filepath).toLowerCase()
184
- switch (ext) {
185
- case ".zip":
186
- case ".tar":
187
- case ".gz":
188
- case ".exe":
189
- case ".dll":
190
- case ".so":
191
- case ".class":
192
- case ".jar":
193
- case ".war":
194
- case ".7z":
195
- case ".doc":
196
- case ".docx":
197
- case ".xls":
198
- case ".xlsx":
199
- case ".ppt":
200
- case ".pptx":
201
- case ".odt":
202
- case ".ods":
203
- case ".odp":
204
- case ".bin":
205
- case ".dat":
206
- case ".obj":
207
- case ".o":
208
- case ".a":
209
- case ".lib":
210
- case ".wasm":
211
- case ".pyc":
212
- case ".pyo":
213
- return true
214
- }
215
-
216
- if (bytes.length === 0) return false
217
-
218
- let nonPrintableCount = 0
219
- for (let i = 0; i < bytes.length; i++) {
220
- if (bytes[i] === 0) return true
221
- if (bytes[i] < 9 || (bytes[i] > 13 && bytes[i] < 32)) {
222
- nonPrintableCount++
223
- }
224
- }
225
-
226
- return nonPrintableCount / bytes.length > 0.3
227
- }
228
-
229
- const run = Effect.fn("ReadTool.execute")(function* (
230
- params: Schema.Schema.Type<typeof Parameters>,
231
- ctx: Tool.Context<Metadata>,
232
- ) {
233
- const instance = yield* InstanceState.context
234
- let filepath = params.filePath
235
- if (!path.isAbsolute(filepath)) {
236
- filepath = path.resolve(instance.directory, filepath)
237
- }
238
- if (process.platform === "win32") {
239
- filepath = FSUtil.normalizePath(filepath)
240
- }
241
- const title = path.relative(instance.worktree, filepath)
242
-
243
- const stat = yield* fs.stat(filepath).pipe(
244
- Effect.catchIf(
245
- (err) => "reason" in err && err.reason._tag === "NotFound",
246
- () => Effect.succeed(undefined),
247
- ),
248
- )
249
-
250
- yield* assertExternalDirectoryEffect(ctx, filepath, {
251
- bypass: Boolean(ctx.extra?.["bypassCwdCheck"]),
252
- kind: stat?.type === "Directory" ? "directory" : "file",
253
- })
254
-
255
- yield* ctx.ask({
256
- permission: "read",
257
- patterns: [path.relative(instance.worktree, filepath)],
258
- always: ["*"],
259
- metadata: {},
260
- })
261
-
262
- if (!stat) return yield* miss(filepath)
263
-
264
- if (stat.type === "Directory") {
265
- const items = yield* list(filepath)
266
- const limit = params.limit ?? DEFAULT_READ_LIMIT
267
- const offset = params.offset || 1
268
- const start = offset - 1
269
- const sliced = items.slice(start, start + limit)
270
- const truncated = start + sliced.length < items.length
271
-
272
- return {
273
- title,
274
- output: [
275
- `<path>${filepath}</path>`,
276
- `<type>directory</type>`,
277
- `<entries>`,
278
- sliced.join("\n"),
279
- truncated
280
- ? `\n(Showing ${sliced.length} of ${items.length} entries. Use 'offset' parameter to read beyond entry ${offset + sliced.length})`
281
- : `\n(${items.length} entries)`,
282
- `</entries>`,
283
- ].join("\n"),
284
- metadata: {
285
- preview: sliced.slice(0, 20).join("\n"),
286
- truncated,
287
- loaded: [] as string[],
288
- display: {
289
- type: "directory" as const,
290
- path: filepath,
291
- entries: sliced,
292
- offset,
293
- totalEntries: items.length,
294
- truncated,
295
- },
296
- },
297
- }
298
- }
299
-
300
- const loaded = yield* instruction.resolve(ctx.messages, filepath, ctx.messageID)
301
- const sample = yield* readSample(filepath, Number(stat.size), SAMPLE_BYTES)
302
-
303
- const mime = sniffAttachmentMime(sample, FSUtil.mimeType(filepath))
304
- const isImage = SUPPORTED_IMAGE_MIMES.has(mime)
305
-
306
- if (isImage || isPdfAttachment(mime)) {
307
- const bytes = yield* fs.readFile(filepath)
308
- const msg = isPdfAttachment(mime) ? "PDF read successfully" : "Image read successfully"
309
- return {
310
- title,
311
- output: msg,
312
- metadata: {
313
- preview: msg,
314
- truncated: false,
315
- loaded: loaded.map((item) => item.filepath),
316
- },
317
- attachments: [
318
- {
319
- type: "file" as const,
320
- mime,
321
- url: `data:${mime};base64,${Buffer.from(bytes).toString("base64")}`,
322
- },
323
- ],
324
- }
325
- }
326
-
327
- if (isBinaryFile(filepath, sample)) {
328
- return yield* Effect.fail(new Error(`Cannot read binary file: ${filepath}`))
329
- }
330
-
331
- const file = yield* lines(filepath, { limit: params.limit ?? DEFAULT_READ_LIMIT, offset: params.offset || 1 })
332
- if (file.count < file.offset && !(file.count === 0 && file.offset === 1)) {
333
- return yield* Effect.fail(
334
- new Error(`Offset ${file.offset} is out of range for this file (${file.count} lines)`),
335
- )
336
- }
337
-
338
- let output = [`<path>${filepath}</path>`, `<type>file</type>`, "<content>\n"].join("\n")
339
- output += file.raw.map((line, i) => `${i + file.offset}: ${line}`).join("\n")
340
-
341
- const last = file.offset + file.raw.length - 1
342
- const next = last + 1
343
- const truncated = file.more || file.cut
344
- if (file.cut) {
345
- output += `\n\n(Output capped at ${MAX_BYTES_LABEL}. Showing lines ${file.offset}-${last}. Use offset=${next} to continue.)`
346
- } else if (file.more) {
347
- output += `\n\n(Showing lines ${file.offset}-${last} of ${file.count}. Use offset=${next} to continue.)`
348
- } else {
349
- output += `\n\n(End of file - total ${file.count} lines)`
350
- }
351
- output += "\n</content>"
352
-
353
- yield* warm(filepath)
354
-
355
- if (loaded.length > 0) {
356
- output += `\n\n<system-reminder>\n${loaded.map((item) => item.content).join("\n\n")}\n</system-reminder>`
357
- }
358
-
359
- return {
360
- title,
361
- output,
362
- metadata: {
363
- preview: file.raw.slice(0, 20).join("\n"),
364
- truncated,
365
- loaded: loaded.map((item) => item.filepath),
366
- display: {
367
- type: "file" as const,
368
- path: filepath,
369
- text: file.raw.join("\n"),
370
- lineStart: file.offset,
371
- lineEnd: last,
372
- totalLines: file.count,
373
- truncated,
374
- },
375
- },
376
- }
377
- })
378
-
379
- return {
380
- description: DESCRIPTION,
381
- parameters: Parameters,
382
- execute: (params: Schema.Schema.Type<typeof Parameters>, ctx: Tool.Context<Metadata>) =>
383
- run(params, ctx).pipe(Effect.orDie),
384
- }
385
- }),
386
- )
package/src/tool/read.txt DELETED
@@ -1,13 +0,0 @@
1
- Read files/directories using absolute paths.
2
-
3
- Default: first 2000 lines.
4
-
5
- Use offset for later sections.
6
-
7
- Use grep for large-file content search and glob when the path is unknown.
8
-
9
- Read multiple files in parallel.
10
-
11
- Prefer larger windows over tiny slices.
12
-
13
- Supports image/PDF attachments.