soraacode 0.2.0 → 0.2.1

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/acp/usage.ts DELETED
@@ -1,243 +0,0 @@
1
- import type { AgentSideConnection, Usage } from "@agentclientprotocol/sdk"
2
- import type { AssistantMessage as OpenCodeAssistantMessage, Message } from "@soraacode/sdk/v2"
3
- import { InstanceRef } from "@/effect/instance-ref"
4
- import { InstanceBootstrap } from "@/project/bootstrap"
5
- import { InstanceStore } from "@/project/instance-store"
6
- import { makeGlobalNode, Node } from "@soraacode/core/effect/app-node"
7
- import { LayerNode } from "@soraacode/core/effect/layer-node"
8
- import { ProviderV2 } from "@soraacode/core/provider"
9
- import { ModelV2 } from "@soraacode/core/model"
10
- import { Provider } from "@/provider/provider"
11
- import { Context, Effect, Layer, SynchronizedRef } from "effect"
12
-
13
- export type AssistantTokenCost = Pick<OpenCodeAssistantMessage, "cost" | "tokens">
14
-
15
- export type AssistantMessage = AssistantTokenCost &
16
- Pick<OpenCodeAssistantMessage, "role"> &
17
- Partial<Pick<OpenCodeAssistantMessage, "providerID" | "modelID">>
18
-
19
- export type SessionMessage = {
20
- readonly info: { readonly role: Message["role"] } | AssistantMessage
21
- }
22
-
23
- export type MessagesInput = {
24
- readonly sessionID: string
25
- readonly directory: string
26
- }
27
-
28
- export type SDK = {
29
- readonly session: {
30
- readonly messages: (
31
- parameters: { readonly sessionID: string; readonly directory: string },
32
- options: { readonly throwOnError: true },
33
- ) => Promise<{ readonly data?: readonly SessionMessage[] | null }>
34
- }
35
- }
36
-
37
- export interface MessageLoaderInterface {
38
- readonly messages: (input: MessagesInput) => Effect.Effect<readonly SessionMessage[], unknown>
39
- }
40
-
41
- export interface ContextLimitLoaderInterface {
42
- readonly providers: (directory: string) => Effect.Effect<Record<ProviderV2.ID, Provider.Info>, unknown>
43
- }
44
-
45
- export type UsageConnection = Pick<AgentSideConnection, "sessionUpdate">
46
-
47
- export interface Interface {
48
- readonly buildUsage: (message: AssistantTokenCost) => Usage
49
- readonly latestAssistantMessage: (messages: readonly SessionMessage[]) => AssistantMessage | undefined
50
- readonly totalSessionCost: (messages: readonly SessionMessage[]) => number
51
- readonly contextLimit: (input: {
52
- readonly directory: string
53
- readonly providerID: ProviderV2.ID
54
- readonly modelID: ModelV2.ID
55
- }) => Effect.Effect<number | undefined>
56
- readonly sendUpdate: (input: {
57
- readonly connection: UsageConnection
58
- readonly sessionID: string
59
- readonly directory: string
60
- }) => Effect.Effect<void>
61
- }
62
-
63
- export class MessageLoader extends Context.Service<MessageLoader, MessageLoaderInterface>()(
64
- "@opencode/ACPUsageMessageLoader",
65
- ) {}
66
-
67
- export class ContextLimitLoader extends Context.Service<ContextLimitLoader, ContextLimitLoaderInterface>()(
68
- "@opencode/ACPUsageContextLimitLoader",
69
- ) {}
70
-
71
- export class Service extends Context.Service<Service, Interface>()("@opencode/ACPUsage") {}
72
-
73
- export function messageLoaderFromSDK(sdk: SDK): MessageLoaderInterface {
74
- return MessageLoader.of({
75
- messages: (input) =>
76
- Effect.promise(() =>
77
- sdk.session
78
- .messages({ sessionID: input.sessionID, directory: input.directory }, { throwOnError: true })
79
- .then((response) => response.data ?? []),
80
- ),
81
- })
82
- }
83
-
84
- export const messageLoaderLayer = (sdk: SDK) => Layer.succeed(MessageLoader, messageLoaderFromSDK(sdk))
85
-
86
- export function contextTokens(message: AssistantTokenCost): number {
87
- return message.tokens.input + message.tokens.cache.read + message.tokens.cache.write
88
- }
89
-
90
- export function buildUsage(message: AssistantTokenCost): Usage {
91
- const cachedReadTokens = message.tokens.cache.read
92
- const cachedWriteTokens = message.tokens.cache.write
93
- const thoughtTokens = message.tokens.reasoning
94
-
95
- return {
96
- inputTokens: message.tokens.input,
97
- outputTokens: message.tokens.output,
98
- totalTokens: message.tokens.input + message.tokens.output + thoughtTokens + cachedReadTokens + cachedWriteTokens,
99
- ...(thoughtTokens > 0 ? { thoughtTokens } : {}),
100
- ...(cachedReadTokens > 0 ? { cachedReadTokens } : {}),
101
- ...(cachedWriteTokens > 0 ? { cachedWriteTokens } : {}),
102
- }
103
- }
104
-
105
- export function latestAssistantMessage(messages: readonly SessionMessage[]): AssistantMessage | undefined {
106
- return messages
107
- .filter((message): message is { readonly info: AssistantMessage } => message.info.role === "assistant")
108
- .at(-1)?.info
109
- }
110
-
111
- export function totalSessionCost(messages: readonly SessionMessage[]): number {
112
- return messages
113
- .filter((message): message is { readonly info: AssistantMessage } => message.info.role === "assistant")
114
- .reduce((sum, message) => sum + message.info.cost, 0)
115
- }
116
-
117
- export function findContextLimit(
118
- providers: Record<ProviderV2.ID, Provider.Info>,
119
- providerID: ProviderV2.ID,
120
- modelID: ModelV2.ID,
121
- ): number | undefined {
122
- return providers[providerID]?.models[modelID]?.limit.context
123
- }
124
-
125
- export const contextLimitLoaderLayer = Layer.effect(
126
- ContextLimitLoader,
127
- Effect.gen(function* () {
128
- const store = yield* InstanceStore.Service
129
- const provider = yield* Provider.Service
130
-
131
- return ContextLimitLoader.of({
132
- providers: Effect.fn("ACPUsageContextLimitLoader.providers")(function* (directory) {
133
- const ctx = yield* store.load({ directory })
134
- return yield* Effect.gen(function* () {
135
- return yield* provider.list()
136
- }).pipe(Effect.provideService(InstanceRef, ctx))
137
- }),
138
- })
139
- }),
140
- )
141
-
142
- const layer = Layer.effect(
143
- Service,
144
- Effect.gen(function* () {
145
- const messageLoader = yield* MessageLoader
146
- const contextLimitLoader = yield* ContextLimitLoader
147
- const limits = yield* SynchronizedRef.make(new Map<string, Effect.Effect<number | undefined>>())
148
-
149
- const cachedLimit = Effect.fnUntraced(function* (input: {
150
- readonly directory: string
151
- readonly providerID: ProviderV2.ID
152
- readonly modelID: ModelV2.ID
153
- }) {
154
- return yield* SynchronizedRef.modifyEffect(
155
- limits,
156
- Effect.fnUntraced(function* (items) {
157
- const key = `${input.directory}\u0000${input.providerID}\u0000${input.modelID}`
158
- const current = items.get(key)
159
- if (current) return [current, items] as const
160
- const next = yield* Effect.cached(
161
- contextLimitLoader.providers(input.directory).pipe(
162
- Effect.map((providers) => findContextLimit(providers, input.providerID, input.modelID)),
163
- Effect.catch((error) =>
164
- Effect.logError("failed to get providers for usage context limit", { error: error }).pipe(
165
- Effect.as(undefined),
166
- ),
167
- ),
168
- ),
169
- )
170
- return [next, new Map(items).set(key, next)] as const
171
- }),
172
- )
173
- })
174
-
175
- const contextLimit = Effect.fn("ACPUsage.contextLimit")(function* (input: {
176
- readonly directory: string
177
- readonly providerID: ProviderV2.ID
178
- readonly modelID: ModelV2.ID
179
- }) {
180
- return yield* yield* cachedLimit(input)
181
- })
182
-
183
- const sendUpdate = Effect.fn("ACPUsage.sendUpdate")(function* (input: {
184
- readonly connection: UsageConnection
185
- readonly sessionID: string
186
- readonly directory: string
187
- }) {
188
- const messages = yield* messageLoader
189
- .messages({ sessionID: input.sessionID, directory: input.directory })
190
- .pipe(
191
- Effect.catch((error) =>
192
- Effect.logError("failed to fetch messages for usage update", { error: error }).pipe(Effect.as(undefined)),
193
- ),
194
- )
195
- if (!messages) return
196
-
197
- const message = latestAssistantMessage(messages)
198
- if (!message) return
199
- if (!message.providerID || !message.modelID) return
200
-
201
- const size = yield* contextLimit({
202
- directory: input.directory,
203
- providerID: ProviderV2.ID.make(message.providerID),
204
- modelID: ModelV2.ID.make(message.modelID),
205
- })
206
- if (!size) return
207
-
208
- yield* Effect.promise(() =>
209
- input.connection
210
- .sessionUpdate({
211
- sessionId: input.sessionID,
212
- update: {
213
- sessionUpdate: "usage_update",
214
- used: contextTokens(message),
215
- size,
216
- cost: { amount: totalSessionCost(messages), currency: "USD" },
217
- },
218
- })
219
- .catch(() => {}),
220
- )
221
- })
222
-
223
- return Service.of({
224
- buildUsage,
225
- latestAssistantMessage,
226
- totalSessionCost,
227
- contextLimit,
228
- sendUpdate,
229
- })
230
- }),
231
- )
232
-
233
- export const messageLoaderNode = LayerNode.unbound(MessageLoader, Node.tags.values.global)
234
-
235
- export const contextLimitLoaderNode = makeGlobalNode({
236
- service: ContextLimitLoader,
237
- layer: contextLimitLoaderLayer,
238
- deps: [Provider.node, InstanceStore.node],
239
- })
240
-
241
- export const node = makeGlobalNode({ service: Service, layer, deps: [messageLoaderNode, contextLimitLoaderNode] })
242
-
243
- export * as UsageService from "./usage"
@@ -1,450 +0,0 @@
1
- import { LayerNode } from "@soraacode/core/effect/layer-node"
2
- import { PermissionV1 } from "@soraacode/core/v1/permission"
3
- import { Config } from "@/config/config"
4
- import { serviceUse } from "@soraacode/core/effect/service-use"
5
- import { Provider } from "@/provider/provider"
6
-
7
- import { generateObject, streamObject, type ModelMessage } from "ai"
8
- import { Truncate } from "@/tool/truncate"
9
- import { Auth } from "../auth"
10
- import { ProviderTransform } from "@/provider/transform"
11
-
12
- import PROMPT_GENERATE from "./generate.txt"
13
- import PROMPT_COMPACTION from "./prompt/compaction.txt"
14
- import PROMPT_EXPLORE from "./prompt/explore.txt"
15
- import PROMPT_SUMMARY from "./prompt/summary.txt"
16
- import PROMPT_TITLE from "./prompt/title.txt"
17
- import { Permission } from "@/permission"
18
- import { mergeDeep, pipe, sortBy, values } from "remeda"
19
- import { Global } from "@soraacode/core/global"
20
- import path from "path"
21
- import { Plugin } from "@/plugin"
22
- import { Skill } from "../skill"
23
- import { Effect, Context, Layer, Schema } from "effect"
24
- import { InstanceState } from "@/effect/instance-state"
25
- import * as Option from "effect/Option"
26
- import { AbsolutePath, type DeepMutable } from "@soraacode/core/schema"
27
- import { ProviderV2 } from "@soraacode/core/provider"
28
- import { ModelV2 } from "@soraacode/core/model"
29
- import { LocationServiceMap, locationServiceMapLayer } from "@soraacode/core/location-services"
30
- import { Reference } from "@soraacode/core/reference"
31
- import { Location } from "@soraacode/core/location"
32
- import { PluginV2 } from "@soraacode/core/plugin"
33
-
34
- export const Info = Schema.Struct({
35
- name: Schema.String,
36
- description: Schema.optional(Schema.String),
37
- mode: Schema.Literals(["subagent", "primary", "all"]),
38
- native: Schema.optional(Schema.Boolean),
39
- hidden: Schema.optional(Schema.Boolean),
40
- topP: Schema.optional(Schema.Finite),
41
- temperature: Schema.optional(Schema.Finite),
42
- color: Schema.optional(Schema.String),
43
- permission: PermissionV1.Ruleset,
44
- model: Schema.optional(
45
- Schema.Struct({
46
- modelID: ModelV2.ID,
47
- providerID: ProviderV2.ID,
48
- }),
49
- ),
50
- variant: Schema.optional(Schema.String),
51
- prompt: Schema.optional(Schema.String),
52
- options: Schema.Record(Schema.String, Schema.Unknown),
53
- steps: Schema.optional(Schema.Finite),
54
- }).annotate({ identifier: "Agent" })
55
- export type Info = DeepMutable<Schema.Schema.Type<typeof Info>>
56
-
57
- const GeneratedAgent = Schema.Struct({
58
- identifier: Schema.String,
59
- whenToUse: Schema.String,
60
- systemPrompt: Schema.String,
61
- })
62
-
63
- export interface Interface {
64
- readonly get: (agent: string) => Effect.Effect<Info>
65
- readonly list: () => Effect.Effect<Info[]>
66
- readonly defaultInfo: () => Effect.Effect<Info>
67
- readonly defaultAgent: () => Effect.Effect<string>
68
- readonly generate: (input: {
69
- description: string
70
- model?: { providerID: ProviderV2.ID; modelID: ModelV2.ID }
71
- }) => Effect.Effect<
72
- {
73
- identifier: string
74
- whenToUse: string
75
- systemPrompt: string
76
- },
77
- Provider.DefaultModelError
78
- >
79
- }
80
-
81
- type State = Omit<Interface, "generate">
82
-
83
- export class Service extends Context.Service<Service, Interface>()("@opencode/Agent") {}
84
-
85
- export const use = serviceUse(Service)
86
-
87
- const layer = Layer.effect(
88
- Service,
89
- Effect.gen(function* () {
90
- const config = yield* Config.Service
91
- const auth = yield* Auth.Service
92
- const plugin = yield* Plugin.Service
93
- const skill = yield* Skill.Service
94
- const provider = yield* Provider.Service
95
- const locations = yield* LocationServiceMap.Service
96
-
97
- const state = yield* InstanceState.make<State>(
98
- Effect.fn("Agent.state")(function* (ctx) {
99
- const cfg = yield* config.get()
100
- const skillDirs = yield* skill.dirs()
101
- const referenceDirs = Object.keys(cfg.references ?? cfg.reference ?? {}).length
102
- ? yield* Effect.gen(function* () {
103
- yield* (yield* PluginV2.Service).wait(PluginV2.ID.make("core/config-reference"))
104
- return (yield* (yield* Reference.Service).list()).map((reference) => reference.path)
105
- }).pipe(Effect.provide(locations.get(Location.Ref.make({ directory: AbsolutePath.make(ctx.directory) }))))
106
- : []
107
- const whitelistedDirs = [
108
- Truncate.GLOB,
109
- path.join(Global.Path.tmp, "*"),
110
- ...skillDirs.map((dir) => path.join(dir, "*")),
111
- ...referenceDirs.map((dir) => path.join(dir, "*")),
112
- ]
113
- const readonlyExternalDirectory = {
114
- "*": "ask",
115
- ...Object.fromEntries(whitelistedDirs.map((dir) => [dir, "allow"])),
116
- } satisfies Record<string, "allow" | "ask" | "deny">
117
-
118
- const defaults = Permission.fromConfig({
119
- "*": "allow",
120
- doom_loop: "ask",
121
- external_directory: {
122
- "*": "ask",
123
- ...Object.fromEntries(whitelistedDirs.map((dir) => [dir, "allow"])),
124
- },
125
- question: "deny",
126
- plan_enter: "deny",
127
- plan_exit: "deny",
128
- // mirrors github.com/github/gitignore Node.gitignore pattern for .env files
129
- read: {
130
- "*": "allow",
131
- "*.env": "ask",
132
- "*.env.*": "ask",
133
- "*.env.example": "allow",
134
- },
135
- })
136
-
137
- const user = Permission.fromConfig(cfg.permission ?? {})
138
-
139
- const agents: Record<string, Info> = {
140
- build: {
141
- name: "build",
142
- description: "The default agent. Executes tools based on configured permissions.",
143
- options: {},
144
- permission: Permission.merge(
145
- defaults,
146
- Permission.fromConfig({
147
- question: "allow",
148
- plan_enter: "allow",
149
- }),
150
- user,
151
- ),
152
- mode: "primary",
153
- native: true,
154
- },
155
- plan: {
156
- name: "plan",
157
- description: "Plan mode. Disallows all edit tools.",
158
- options: {},
159
- permission: Permission.merge(
160
- defaults,
161
- Permission.fromConfig({
162
- question: "allow",
163
- plan_exit: "allow",
164
- task: {
165
- general: "deny",
166
- },
167
- external_directory: {
168
- [path.join(Global.Path.data, "plans", "*")]: "allow",
169
- },
170
- edit: {
171
- "*": "deny",
172
- [path.join(".opencode", "plans", "*.md")]: "allow",
173
- [path.relative(ctx.worktree, path.join(Global.Path.data, path.join("plans", "*.md")))]: "allow",
174
- },
175
- }),
176
- user,
177
- ),
178
- mode: "primary",
179
- native: true,
180
- },
181
- general: {
182
- name: "general",
183
- description: `General-purpose agent for researching complex questions and executing multi-step tasks. Use this agent to execute multiple units of work in parallel.`,
184
- permission: Permission.merge(
185
- defaults,
186
- Permission.fromConfig({
187
- todowrite: "deny",
188
- }),
189
- user,
190
- ),
191
- options: {},
192
- mode: "subagent",
193
- native: true,
194
- },
195
- explore: {
196
- name: "explore",
197
- permission: Permission.merge(
198
- defaults,
199
- Permission.fromConfig({
200
- "*": "deny",
201
- grep: "allow",
202
- glob: "allow",
203
- list: "allow",
204
- bash: "allow",
205
- webfetch: "allow",
206
- websearch: "allow",
207
- read: "allow",
208
- external_directory: readonlyExternalDirectory,
209
- }),
210
- user,
211
- ),
212
- description: `Fast agent specialized for exploring codebases. Use this when you need to quickly find files by patterns (eg. "src/components/**/*.tsx"), search code for keywords (eg. "API endpoints"), or answer questions about the codebase (eg. "how do API endpoints work?"). When calling this agent, specify the desired thoroughness level: "quick" for basic searches, "medium" for moderate exploration, or "very thorough" for comprehensive analysis across multiple locations and naming conventions.`,
213
- prompt: PROMPT_EXPLORE,
214
- options: {},
215
- mode: "subagent",
216
- native: true,
217
- },
218
- compaction: {
219
- name: "compaction",
220
- mode: "primary",
221
- native: true,
222
- hidden: true,
223
- prompt: PROMPT_COMPACTION,
224
- permission: Permission.merge(
225
- defaults,
226
- Permission.fromConfig({
227
- "*": "deny",
228
- }),
229
- user,
230
- ),
231
- options: {},
232
- },
233
- title: {
234
- name: "title",
235
- mode: "primary",
236
- options: {},
237
- native: true,
238
- hidden: true,
239
- temperature: 0.5,
240
- permission: Permission.merge(
241
- defaults,
242
- Permission.fromConfig({
243
- "*": "deny",
244
- }),
245
- user,
246
- ),
247
- prompt: PROMPT_TITLE,
248
- },
249
- summary: {
250
- name: "summary",
251
- mode: "primary",
252
- options: {},
253
- native: true,
254
- hidden: true,
255
- permission: Permission.merge(
256
- defaults,
257
- Permission.fromConfig({
258
- "*": "deny",
259
- }),
260
- user,
261
- ),
262
- prompt: PROMPT_SUMMARY,
263
- },
264
- }
265
-
266
- for (const [key, value] of Object.entries(cfg.agent ?? {})) {
267
- if (value.disable) {
268
- delete agents[key]
269
- continue
270
- }
271
- let item = agents[key]
272
- if (!item)
273
- item = agents[key] = {
274
- name: key,
275
- mode: "all",
276
- permission: Permission.merge(defaults, user),
277
- options: {},
278
- native: false,
279
- }
280
- if (value.model) item.model = Provider.parseModel(value.model)
281
- item.variant = value.variant ?? item.variant
282
- item.prompt = value.prompt ?? item.prompt
283
- item.description = value.description ?? item.description
284
- item.temperature = value.temperature ?? item.temperature
285
- item.topP = value.top_p ?? item.topP
286
- item.mode = value.mode ?? item.mode
287
- item.color = value.color ?? item.color
288
- item.hidden = value.hidden ?? item.hidden
289
- item.name = value.name ?? item.name
290
- item.steps = value.steps ?? item.steps
291
- item.options = mergeDeep(item.options, value.options ?? {})
292
- item.permission = Permission.merge(item.permission, Permission.fromConfig(value.permission ?? {}))
293
- }
294
-
295
- // Ensure Truncate.GLOB is allowed unless explicitly configured
296
- for (const name in agents) {
297
- const agent = agents[name]
298
- const explicit = agent.permission.some((r) => {
299
- if (r.permission !== "external_directory") return false
300
- if (r.action !== "deny") return false
301
- return r.pattern === Truncate.GLOB
302
- })
303
- if (explicit) continue
304
-
305
- agents[name].permission = Permission.merge(
306
- agents[name].permission,
307
- Permission.fromConfig({ external_directory: { [Truncate.GLOB]: "allow" } }),
308
- )
309
- }
310
-
311
- const get = Effect.fnUntraced(function* (agent: string) {
312
- return agents[agent]
313
- })
314
-
315
- const list = Effect.fnUntraced(function* () {
316
- const cfg = yield* config.get()
317
- return pipe(
318
- agents,
319
- values(),
320
- sortBy(
321
- [(x) => (cfg.default_agent ? x.name === cfg.default_agent : x.name === "build"), "desc"],
322
- [(x) => x.name, "asc"],
323
- ),
324
- )
325
- })
326
-
327
- const defaultInfo = Effect.fnUntraced(function* () {
328
- const c = yield* config.get()
329
- if (c.default_agent) {
330
- const agent = agents[c.default_agent]
331
- if (!agent) throw new Error(`default agent "${c.default_agent}" not found`)
332
- if (agent.mode === "subagent") throw new Error(`default agent "${c.default_agent}" is a subagent`)
333
- if (agent.hidden === true) throw new Error(`default agent "${c.default_agent}" is hidden`)
334
- return agent
335
- }
336
- const visible = Object.values(agents).find((a) => a.mode !== "subagent" && a.hidden !== true)
337
- if (!visible) throw new Error("no primary visible agent found")
338
- return visible
339
- })
340
-
341
- const defaultAgent = Effect.fnUntraced(function* () {
342
- return (yield* defaultInfo()).name
343
- })
344
-
345
- return {
346
- get,
347
- list,
348
- defaultInfo,
349
- defaultAgent,
350
- } satisfies State
351
- }),
352
- )
353
-
354
- return Service.of({
355
- get: Effect.fn("Agent.get")(function* (agent: string) {
356
- return yield* InstanceState.useEffect(state, (s) => s.get(agent))
357
- }),
358
- list: Effect.fn("Agent.list")(function* () {
359
- return yield* InstanceState.useEffect(state, (s) => s.list())
360
- }),
361
- defaultInfo: Effect.fn("Agent.defaultInfo")(function* () {
362
- return yield* InstanceState.useEffect(state, (s) => s.defaultInfo())
363
- }),
364
- defaultAgent: Effect.fn("Agent.defaultAgent")(function* () {
365
- return yield* InstanceState.useEffect(state, (s) => s.defaultAgent())
366
- }),
367
- generate: Effect.fn("Agent.generate")(function* (input: {
368
- description: string
369
- model?: { providerID: ProviderV2.ID; modelID: ModelV2.ID }
370
- }) {
371
- const cfg = yield* config.get()
372
- const model = input.model ?? (yield* provider.defaultModel())
373
- const resolved = yield* provider.getModel(model.providerID, model.modelID)
374
- const language = yield* provider.getLanguage(resolved)
375
- const tracer = undefined
376
-
377
- const system = [PROMPT_GENERATE]
378
- yield* plugin.trigger("experimental.chat.system.transform", { model: resolved }, { system })
379
- const existing = yield* InstanceState.useEffect(state, (s) => s.list())
380
-
381
- // TODO: clean this up so provider specific logic doesnt bleed over
382
- const authInfo = yield* auth.get(model.providerID).pipe(Effect.orDie)
383
- const isOpenaiOauth = model.providerID === "openai" && authInfo?.type === "oauth"
384
-
385
- const params = {
386
- experimental_telemetry: {
387
- isEnabled: cfg.experimental?.openTelemetry,
388
- tracer,
389
- metadata: {
390
- userId: cfg.username ?? "unknown",
391
- },
392
- },
393
- temperature: 0.3,
394
- messages: [
395
- ...(isOpenaiOauth
396
- ? []
397
- : system.map(
398
- (item): ModelMessage => ({
399
- role: "system",
400
- content: item,
401
- }),
402
- )),
403
- {
404
- role: "user",
405
- content: `Create an agent configuration based on this request: "${input.description}".\n\nIMPORTANT: The following identifiers already exist and must NOT be used: ${existing.map((i) => i.name).join(", ")}\n Return ONLY the JSON object, no other text, do not wrap in backticks`,
406
- },
407
- ],
408
- model: language,
409
- schema: Object.assign(
410
- Schema.toStandardSchemaV1(GeneratedAgent),
411
- Schema.toStandardJSONSchemaV1(GeneratedAgent),
412
- ),
413
- } satisfies Parameters<typeof generateObject>[0]
414
-
415
- if (isOpenaiOauth) {
416
- return yield* Effect.promise(async () => {
417
- const result = streamObject({
418
- ...params,
419
- providerOptions: ProviderTransform.providerOptions(resolved, {
420
- instructions: system.join("\n"),
421
- store: false,
422
- }),
423
- onError: () => {},
424
- })
425
- for await (const part of result.fullStream) {
426
- if (part.type === "error") throw part.error
427
- }
428
- return result.object
429
- })
430
- }
431
-
432
- return yield* Effect.promise(() => generateObject(params).then((r) => r.object))
433
- }),
434
- })
435
- }),
436
- )
437
-
438
- const locationServiceMapNode = LayerNode.make({
439
- service: LocationServiceMap.Service,
440
- layer: locationServiceMapLayer,
441
- deps: [],
442
- })
443
-
444
- export const node = LayerNode.make({
445
- service: Service,
446
- layer: layer,
447
- deps: [Config.node, Auth.node, Plugin.node, Skill.node, Provider.node, locationServiceMapNode],
448
- })
449
-
450
- export * as Agent from "./agent"