saeeol 1.3.0 → 1.3.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 (537) hide show
  1. package/.turbo/turbo-typecheck.log +1 -0
  2. package/AGENTS.md +72 -0
  3. package/BUN_SHELL_MIGRATION_PLAN.md +136 -0
  4. package/Dockerfile +18 -0
  5. package/assets/saeeol.ico +0 -0
  6. package/bin/saeeol.cjs +0 -0
  7. package/database.db +0 -0
  8. package/drizzle.config.ts +10 -0
  9. package/git +0 -0
  10. package/migration/20260127222353_familiar_lady_ursula/migration.sql +90 -0
  11. package/migration/20260127222353_familiar_lady_ursula/snapshot.json +796 -0
  12. package/migration/20260211171708_add_project_commands/migration.sql +1 -0
  13. package/migration/20260211171708_add_project_commands/snapshot.json +806 -0
  14. package/migration/20260213144116_wakeful_the_professor/migration.sql +11 -0
  15. package/migration/20260213144116_wakeful_the_professor/snapshot.json +897 -0
  16. package/migration/20260225215848_workspace/migration.sql +7 -0
  17. package/migration/20260225215848_workspace/snapshot.json +959 -0
  18. package/migration/20260227213759_add_session_workspace_id/migration.sql +2 -0
  19. package/migration/20260227213759_add_session_workspace_id/snapshot.json +983 -0
  20. package/migration/20260228203230_blue_harpoon/migration.sql +17 -0
  21. package/migration/20260228203230_blue_harpoon/snapshot.json +1102 -0
  22. package/migration/20260303231226_add_workspace_fields/migration.sql +5 -0
  23. package/migration/20260303231226_add_workspace_fields/snapshot.json +1013 -0
  24. package/migration/20260309230000_move_org_to_state/migration.sql +3 -0
  25. package/migration/20260309230000_move_org_to_state/snapshot.json +1156 -0
  26. package/migration/20260312043431_session_message_cursor/migration.sql +4 -0
  27. package/migration/20260312043431_session_message_cursor/snapshot.json +1168 -0
  28. package/migration/20260323234822_events/migration.sql +13 -0
  29. package/migration/20260323234822_events/snapshot.json +1271 -0
  30. package/migration/20260410174513_workspace-name/migration.sql +16 -0
  31. package/migration/20260410174513_workspace-name/snapshot.json +1271 -0
  32. package/migration/20260413175956_chief_energizer/migration.sql +13 -0
  33. package/migration/20260413175956_chief_energizer/snapshot.json +1399 -0
  34. package/migration/20260423070820_add_icon_url_override/migration.sql +2 -0
  35. package/migration/20260423070820_add_icon_url_override/snapshot.json +1409 -0
  36. package/migration/20260428004200_add_session_path/migration.sql +1 -0
  37. package/migration/20260428004200_add_session_path/snapshot.json +1419 -0
  38. package/npm/bin/saeeol +42 -0
  39. package/npm/package.json +39 -0
  40. package/npm/postinstall.js +162 -0
  41. package/package.json +201 -207
  42. package/parsers-config.ts +289 -0
  43. package/script/build.ts +393 -0
  44. package/script/check-migrations.ts +16 -0
  45. package/script/fix-node-pty.ts +34 -0
  46. package/script/generate.ts +23 -0
  47. package/script/postinstall.mjs +189 -0
  48. package/script/publish.ts +200 -0
  49. package/script/run-workspace-server +106 -0
  50. package/script/schema.ts +63 -0
  51. package/script/test-runner.ts +420 -0
  52. package/script/time.ts +6 -0
  53. package/script/trace-imports.ts +153 -0
  54. package/script/upgrade-opentui.ts +64 -0
  55. package/scripts/diff-sdk-types.sh +52 -0
  56. package/specs/effect/facades.md +221 -0
  57. package/specs/effect/http-api.md +401 -0
  58. package/specs/effect/instance-context.md +309 -0
  59. package/specs/effect/loose-ends.md +34 -0
  60. package/specs/effect/migration.md +299 -0
  61. package/specs/effect/routes.md +64 -0
  62. package/specs/effect/schema.md +399 -0
  63. package/specs/effect/server-package.md +668 -0
  64. package/specs/effect/tools.md +90 -0
  65. package/specs/tui-plugins.md +433 -0
  66. package/specs/v2/api.ts +67 -0
  67. package/specs/v2/keymappings.md +10 -0
  68. package/specs/v2/message-shape.md +136 -0
  69. package/src/acp/agent-message.ts +1 -1
  70. package/src/acp/agent-utils.ts +1 -1
  71. package/src/boxes/ansi.ts +17 -0
  72. package/src/boxes/atomic-write.ts +35 -0
  73. package/src/boxes/b64.ts +58 -0
  74. package/src/boxes/bash-security.ts +129 -0
  75. package/src/boxes/bom.ts +18 -0
  76. package/src/boxes/cancel.ts +16 -0
  77. package/src/boxes/chop.ts +12 -0
  78. package/src/boxes/clamp.ts +3 -0
  79. package/src/boxes/compact.ts +9 -0
  80. package/src/boxes/cost-tracker.ts +116 -0
  81. package/src/boxes/dataurl.ts +29 -0
  82. package/src/boxes/delay.ts +27 -0
  83. package/src/boxes/diff-apply.ts +53 -0
  84. package/src/boxes/disposable.ts +13 -0
  85. package/src/boxes/err.ts +34 -0
  86. package/src/boxes/human.ts +47 -0
  87. package/src/boxes/iife.ts +9 -0
  88. package/src/boxes/latch.ts +8 -0
  89. package/src/boxes/memory.ts +198 -0
  90. package/src/boxes/net.ts +16 -0
  91. package/src/boxes/plural.ts +4 -0
  92. package/src/boxes/puny.ts +21 -0
  93. package/src/boxes/retry.ts +49 -0
  94. package/src/boxes/rwlock.ts +41 -0
  95. package/src/boxes/schedule.ts +71 -0
  96. package/src/boxes/scope.ts +21 -0
  97. package/src/boxes/tokens.ts +9 -0
  98. package/src/boxes/ttl-cache.ts +63 -0
  99. package/src/boxes/typed-event.ts +51 -0
  100. package/src/boxes/uid.ts +50 -0
  101. package/src/boxes/wave6.test.ts +296 -0
  102. package/src/boxes/wildcard.ts +58 -0
  103. package/src/bus/global.ts +1 -1
  104. package/src/cli/cmd/github-run-api.ts +2 -2
  105. package/src/cli/cmd/run-events.ts +2 -2
  106. package/src/cli/cmd/tui/component/logo.tsx +1 -1
  107. package/src/cli/cmd/tui/component/prompt/use-prompt-memos.ts +2 -2
  108. package/src/cli/cmd/tui/context/app/editor-zed.ts +1 -1
  109. package/src/cli/cmd/tui/context/app/editor.ts +1 -1
  110. package/src/cli/cmd/tui/context/app/theme.tsx +1 -1
  111. package/src/cli/cmd/tui/util/revert-diff.ts +1 -1
  112. package/src/overlay/cli/cmd/roll-call-call.ts +1 -1
  113. package/src/overlay/cost-tracker/format.ts +1 -1
  114. package/src/overlay/cost-tracker/index.ts +4 -4
  115. package/src/overlay/cost-tracker/state.ts +2 -2
  116. package/src/overlay/cost-tracker/types.ts +2 -2
  117. package/src/overlay/memory/age.ts +1 -1
  118. package/src/overlay/memory/index.ts +4 -4
  119. package/src/overlay/memory/paths.ts +2 -2
  120. package/src/overlay/memory/scan.ts +1 -1
  121. package/src/overlay/memory/types.ts +2 -2
  122. package/src/overlay/tool/bash-security.ts +3 -3
  123. package/src/overlay/util/url.ts +1 -1
  124. package/src/plugin/codex-auth.ts +1 -1
  125. package/src/provider/model-cache.ts +2 -2
  126. package/src/provider/provider-resolve.ts +3 -3
  127. package/src/provider/transform-message.ts +1 -1
  128. package/src/server/routes/game.ts +284 -0
  129. package/src/server/server.ts +2 -0
  130. package/src/session/core/compaction/compaction-helpers.ts +1 -1
  131. package/src/session/core/compaction/compaction.ts +1 -1
  132. package/src/session/core/session.ts +2 -0
  133. package/src/sessions/ingest-queue.ts +2 -2
  134. package/src/sessions/remote-ws.ts +1 -1
  135. package/src/tool/workflow/question.ts +1 -1
  136. package/src/util/abort.ts +1 -1
  137. package/src/util/bom.ts +2 -2
  138. package/src/util/color.ts +1 -1
  139. package/src/util/data-url.ts +1 -1
  140. package/src/util/defer.ts +1 -1
  141. package/src/util/error.ts +2 -2
  142. package/src/util/filesystem.ts +2 -2
  143. package/src/util/format.ts +1 -1
  144. package/src/util/iife.ts +1 -1
  145. package/src/util/local-context.ts +1 -1
  146. package/src/util/locale.ts +2 -2
  147. package/src/util/lock.ts +1 -1
  148. package/src/util/network.ts +1 -1
  149. package/src/util/signal.ts +1 -1
  150. package/src/util/token.ts +1 -1
  151. package/src/util/wildcard.ts +1 -1
  152. package/sst-env.d.ts +10 -0
  153. package/test/AGENTS.md +133 -0
  154. package/test/account/repo.test.ts +352 -0
  155. package/test/account/service.test.ts +456 -0
  156. package/test/acp/agent-interface.test.ts +51 -0
  157. package/test/acp/event-subscription.test.ts +725 -0
  158. package/test/agent/agent.test.ts +890 -0
  159. package/test/auth/auth.test.ts +86 -0
  160. package/test/bun/registry.test.ts +75 -0
  161. package/test/bus/bus-effect.test.ts +161 -0
  162. package/test/bus/bus-integration.test.ts +87 -0
  163. package/test/bus/bus.test.ts +219 -0
  164. package/test/cli/account.test.ts +26 -0
  165. package/test/cli/auto-mode.test.ts +75 -0
  166. package/test/cli/bin-saeeol.test.ts +8 -0
  167. package/test/cli/cmd/tui/prompt-part.test.ts +47 -0
  168. package/test/cli/cmd/tui/prompt-traits.test.ts +38 -0
  169. package/test/cli/cmd/tui/sync.test.tsx +159 -0
  170. package/test/cli/error.test.ts +18 -0
  171. package/test/cli/github-action.test.ts +198 -0
  172. package/test/cli/github-remote.test.ts +85 -0
  173. package/test/cli/import.test.ts +97 -0
  174. package/test/cli/install-artifact.test.ts +72 -0
  175. package/test/cli/plugin-auth-picker.test.ts +120 -0
  176. package/test/cli/pr.test.ts +59 -0
  177. package/test/cli/tui/editor-context-zed.test.ts +356 -0
  178. package/test/cli/tui/editor-context.test.tsx +228 -0
  179. package/test/cli/tui/keybind-plugin.test.ts +90 -0
  180. package/test/cli/tui/markdown.test.ts +161 -0
  181. package/test/cli/tui/plugin-add.test.ts +111 -0
  182. package/test/cli/tui/plugin-install.test.ts +87 -0
  183. package/test/cli/tui/plugin-lifecycle.test.ts +224 -0
  184. package/test/cli/tui/plugin-loader-entrypoint.test.ts +484 -0
  185. package/test/cli/tui/plugin-loader-pure.test.ts +71 -0
  186. package/test/cli/tui/plugin-loader.test.ts +816 -0
  187. package/test/cli/tui/plugin-toggle.test.ts +157 -0
  188. package/test/cli/tui/revert-diff.test.ts +35 -0
  189. package/test/cli/tui/slot-replace.test.tsx +47 -0
  190. package/test/cli/tui/theme-store.test.ts +54 -0
  191. package/test/cli/tui/thread.test.ts +28 -0
  192. package/test/cli/tui/transcript.test.ts +426 -0
  193. package/test/cli/tui/usage.test.ts +60 -0
  194. package/test/cli/tui/use-event.test.tsx +175 -0
  195. package/test/config/agent-color.test.ts +67 -0
  196. package/test/config/config.test.ts +2544 -0
  197. package/test/config/fixtures/empty-frontmatter.md +4 -0
  198. package/test/config/fixtures/frontmatter.md +28 -0
  199. package/test/config/fixtures/markdown-header.md +11 -0
  200. package/test/config/fixtures/no-frontmatter.md +1 -0
  201. package/test/config/fixtures/weird-model-id.md +13 -0
  202. package/test/config/lsp.test.ts +87 -0
  203. package/test/config/markdown.test.ts +228 -0
  204. package/test/config/plugin.test.ts +0 -0
  205. package/test/config/tui.test.ts +624 -0
  206. package/test/control-plane/adapters.test.ts +71 -0
  207. package/test/control-plane/workspace.test.ts +1526 -0
  208. package/test/effect/app-runtime-logger.test.ts +98 -0
  209. package/test/effect/config-service.test.ts +65 -0
  210. package/test/effect/instance-state.test.ts +394 -0
  211. package/test/effect/run-service.test.ts +89 -0
  212. package/test/effect/runner.test.ts +523 -0
  213. package/test/fake/provider.ts +82 -0
  214. package/test/file/fsmonitor.test.ts +68 -0
  215. package/test/file/ignore.test.ts +10 -0
  216. package/test/file/index.test.ts +954 -0
  217. package/test/file/path-traversal.test.ts +205 -0
  218. package/test/file/ripgrep.test.ts +226 -0
  219. package/test/file/watcher.test.ts +249 -0
  220. package/test/filesystem/filesystem.test.ts +319 -0
  221. package/test/fixture/db.ts +11 -0
  222. package/test/fixture/fixture.test.ts +26 -0
  223. package/test/fixture/fixture.ts +175 -0
  224. package/test/fixture/flock-worker.ts +72 -0
  225. package/test/fixture/log-init-worker.ts +62 -0
  226. package/test/fixture/lsp/fake-lsp-server.js +249 -0
  227. package/test/fixture/plug-worker.ts +93 -0
  228. package/test/fixture/plugin-meta-worker.ts +19 -0
  229. package/test/fixture/skills/agents-sdk/SKILL.md +152 -0
  230. package/test/fixture/skills/cloudflare/SKILL.md +211 -0
  231. package/test/fixture/skills/index.json +6 -0
  232. package/test/fixture/tui-plugin.ts +323 -0
  233. package/test/fixture/tui-runtime.ts +31 -0
  234. package/test/format/format.test.ts +272 -0
  235. package/test/git/git.test.ts +128 -0
  236. package/test/ide/ide.test.ts +82 -0
  237. package/test/installation/installation.test.ts +168 -0
  238. package/test/keybind.test.ts +421 -0
  239. package/test/lib/effect.ts +53 -0
  240. package/test/lib/filesystem.ts +10 -0
  241. package/test/lib/llm-server.ts +778 -0
  242. package/test/lib/websocket.ts +46 -0
  243. package/test/lsp/client.test.ts +482 -0
  244. package/test/lsp/index.test.ts +160 -0
  245. package/test/lsp/launch.test.ts +22 -0
  246. package/test/lsp/lifecycle.test.ts +184 -0
  247. package/test/ltm/ltm.test.ts +230 -0
  248. package/test/mcp/headers.test.ts +178 -0
  249. package/test/mcp/lifecycle.test.ts +787 -0
  250. package/test/mcp/oauth-auto-connect.test.ts +311 -0
  251. package/test/mcp/oauth-browser.test.ts +276 -0
  252. package/test/mcp/oauth-callback.test.ts +34 -0
  253. package/test/memory/abort-leak-webfetch.ts +49 -0
  254. package/test/memory/abort-leak.test.ts +128 -0
  255. package/test/patch/patch.test.ts +348 -0
  256. package/test/permission/arity.test.ts +33 -0
  257. package/test/permission/next.test.ts +1227 -0
  258. package/test/permission/next.toConfig.test.ts +110 -0
  259. package/test/permission-task.test.ts +326 -0
  260. package/test/plugin/auth-override.test.ts +79 -0
  261. package/test/plugin/cloudflare.test.ts +68 -0
  262. package/test/plugin/codex.test.ts +123 -0
  263. package/test/plugin/github-copilot-models.test.ts +261 -0
  264. package/test/plugin/install-concurrency.test.ts +140 -0
  265. package/test/plugin/install.test.ts +570 -0
  266. package/test/plugin/loader-shared.test.ts +1169 -0
  267. package/test/plugin/meta.test.ts +137 -0
  268. package/test/plugin/plugin-contract.test.ts +291 -0
  269. package/test/plugin/shared.test.ts +88 -0
  270. package/test/plugin/trigger.test.ts +102 -0
  271. package/test/plugin/workspace-adapter.test.ts +109 -0
  272. package/test/preload.ts +77 -0
  273. package/test/project/instance.test.ts +276 -0
  274. package/test/project/migrate-global.test.ts +152 -0
  275. package/test/project/project.test.ts +600 -0
  276. package/test/project/vcs.test.ts +286 -0
  277. package/test/project/worktree-remove.test.ts +126 -0
  278. package/test/project/worktree.test.ts +223 -0
  279. package/test/provider/amazon-bedrock.test.ts +462 -0
  280. package/test/provider/copilot/convert-to-copilot-messages.test.ts +523 -0
  281. package/test/provider/copilot/copilot-chat-model.test.ts +592 -0
  282. package/test/provider/gitlab-duo.test.ts +413 -0
  283. package/test/provider/local.test.ts +208 -0
  284. package/test/provider/models.test.ts +261 -0
  285. package/test/provider/provider-category.test.ts +190 -0
  286. package/test/provider/provider.test.ts +2758 -0
  287. package/test/provider/transform.test.ts +3681 -0
  288. package/test/pty/pty-output-isolation.test.ts +147 -0
  289. package/test/pty/pty-session.test.ts +102 -0
  290. package/test/pty/pty-shell.test.ts +104 -0
  291. package/test/question/question.test.ts +490 -0
  292. package/test/saeeol/agent-global-config-dirs.test.ts +24 -0
  293. package/test/saeeol/agent-manager-tool.test.ts +71 -0
  294. package/test/saeeol/agent-permission-overrides.test.ts +75 -0
  295. package/test/saeeol/agent-skill-permissions.test.ts +37 -0
  296. package/test/saeeol/ask-agent-permissions.test.ts +303 -0
  297. package/test/saeeol/bash-hierarchy.test.ts +64 -0
  298. package/test/saeeol/bash-permission-metadata.test.ts +66 -0
  299. package/test/saeeol/bash-security-extended.test.ts +243 -0
  300. package/test/saeeol/bedrock-claude-empty-content.test.ts +138 -0
  301. package/test/saeeol/boxes-integration.test.ts +415 -0
  302. package/test/saeeol/builtin-skills.test.ts +75 -0
  303. package/test/saeeol/cleanup.ts +28 -0
  304. package/test/saeeol/cli/dev-setup.test.ts +74 -0
  305. package/test/saeeol/cli/roll-call.test.ts +161 -0
  306. package/test/saeeol/cli-run-auto-helper.test.ts +58 -0
  307. package/test/saeeol/codex-auth-refresh.test.ts +124 -0
  308. package/test/saeeol/commit-message/generate.test.ts +188 -0
  309. package/test/saeeol/commit-message/git-context.test.ts +303 -0
  310. package/test/saeeol/commit-message-windows.test.ts +38 -0
  311. package/test/saeeol/compaction-payload-recovery.test.ts +406 -0
  312. package/test/saeeol/compaction-preservation-audit.test.ts +122 -0
  313. package/test/saeeol/compaction-skip-guard.test.ts +224 -0
  314. package/test/saeeol/compaction-smart-select.test.ts +100 -0
  315. package/test/saeeol/config/config.test.ts +166 -0
  316. package/test/saeeol/config/indexing-default-plugin.test.ts +82 -0
  317. package/test/saeeol/config/opentelemetry-default.test.ts +29 -0
  318. package/test/saeeol/config-gitignore.test.ts +70 -0
  319. package/test/saeeol/config-injector.test.ts +305 -0
  320. package/test/saeeol/config-resilience.test.ts +234 -0
  321. package/test/saeeol/config-validation.test.ts +183 -0
  322. package/test/saeeol/cost-propagation.test.ts +94 -0
  323. package/test/saeeol/cost-tracker-extended.test.ts +141 -0
  324. package/test/saeeol/cost-tracker.test.ts +64 -0
  325. package/test/saeeol/custom-provider-delete.test.ts +149 -0
  326. package/test/saeeol/diff-full.test.ts +226 -0
  327. package/test/saeeol/edit-permission-filediff.test.ts +223 -0
  328. package/test/saeeol/encoding.test.ts +364 -0
  329. package/test/saeeol/enhance-prompt.test.ts +61 -0
  330. package/test/saeeol/ensure-plan-dir.test.ts +32 -0
  331. package/test/saeeol/errors.test.ts +144 -0
  332. package/test/saeeol/external-directory-boundary.test.ts +96 -0
  333. package/test/saeeol/gateway-headers.test.ts +88 -0
  334. package/test/saeeol/help.test.ts +191 -0
  335. package/test/saeeol/ignore-migrator.test.ts +308 -0
  336. package/test/saeeol/indexing-auth.test.ts +45 -0
  337. package/test/saeeol/indexing-feature.test.ts +44 -0
  338. package/test/saeeol/indexing-label.test.ts +70 -0
  339. package/test/saeeol/indexing-startup.test.ts +381 -0
  340. package/test/saeeol/indexing-worktree.test.ts +73 -0
  341. package/test/saeeol/instruction.test.ts +136 -0
  342. package/test/saeeol/lancedb-runtime.test.ts +116 -0
  343. package/test/saeeol/loader-auth.test.ts +168 -0
  344. package/test/saeeol/local-model.test.ts +621 -0
  345. package/test/saeeol/logo.test.ts +31 -0
  346. package/test/saeeol/lsp-typescript-lightweight.test.ts +89 -0
  347. package/test/saeeol/mcp-branding.test.ts +33 -0
  348. package/test/saeeol/mcp-docker-rm.test.ts +32 -0
  349. package/test/saeeol/mcp-migrator.test.ts +736 -0
  350. package/test/saeeol/mcp-oauth-callback.test.ts +33 -0
  351. package/test/saeeol/memory-io.test.ts +198 -0
  352. package/test/saeeol/memory-paths.test.ts +87 -0
  353. package/test/saeeol/memory-security.test.ts +166 -0
  354. package/test/saeeol/model-cache-org.test.ts +164 -0
  355. package/test/saeeol/model-info-panel-utils.test.ts +52 -0
  356. package/test/saeeol/model-info-panel.types.test.ts +7 -0
  357. package/test/saeeol/models-401-fallback.test.ts +52 -0
  358. package/test/saeeol/modes-migrator.test.ts +320 -0
  359. package/test/saeeol/nvidia-headers.test.ts +74 -0
  360. package/test/saeeol/patch-jsonc.test.ts +73 -0
  361. package/test/saeeol/patch.test.ts +172 -0
  362. package/test/saeeol/paths.test.ts +265 -0
  363. package/test/saeeol/permission/config-paths.test.ts +174 -0
  364. package/test/saeeol/permission/env-read.test.ts +149 -0
  365. package/test/saeeol/permission/external-directory-allow.test.ts +327 -0
  366. package/test/saeeol/permission/next.always-rules.test.ts +882 -0
  367. package/test/saeeol/permission/next.reply-http.test.ts +205 -0
  368. package/test/saeeol/permission/next.reply-routing.test.ts +184 -0
  369. package/test/saeeol/plan-exit-detection.test.ts +494 -0
  370. package/test/saeeol/plan-followup.test.ts +1376 -0
  371. package/test/saeeol/project-config-update.test.ts +120 -0
  372. package/test/saeeol/project-id.test.ts +455 -0
  373. package/test/saeeol/provider-cost.test.ts +171 -0
  374. package/test/saeeol/provider-list-failed-state.test.ts +100 -0
  375. package/test/saeeol/question-dismiss-all.test.ts +174 -0
  376. package/test/saeeol/read-directory.test.ts +116 -0
  377. package/test/saeeol/rules-migrator.test.ts +257 -0
  378. package/test/saeeol/run-auto.test.ts +176 -0
  379. package/test/saeeol/run-network.test.ts +224 -0
  380. package/test/saeeol/semantic-search.test.ts +186 -0
  381. package/test/saeeol/server/permission-allow-everything.test.ts +125 -0
  382. package/test/saeeol/session/instruction-substitution.test.ts +72 -0
  383. package/test/saeeol/session/platform-attribution.test.ts +118 -0
  384. package/test/saeeol/session/session.test.ts +105 -0
  385. package/test/saeeol/session-compaction-cap.test.ts +399 -0
  386. package/test/saeeol/session-compaction-chunks.test.ts +501 -0
  387. package/test/saeeol/session-compaction-safety.test.ts +481 -0
  388. package/test/saeeol/session-fork-remap.test.ts +251 -0
  389. package/test/saeeol/session-import-service.test.ts +114 -0
  390. package/test/saeeol/session-list.test.ts +47 -0
  391. package/test/saeeol/session-message-metadata.test.ts +128 -0
  392. package/test/saeeol/session-overflow.test.ts +78 -0
  393. package/test/saeeol/session-processor-empty-tool-calls.test.ts +571 -0
  394. package/test/saeeol/session-processor-network-offline.test.ts +204 -0
  395. package/test/saeeol/session-processor-retry-limit.test.ts +238 -0
  396. package/test/saeeol/session-processor-review-telemetry.test.ts +82 -0
  397. package/test/saeeol/session-prompt-compaction-safety.test.ts +517 -0
  398. package/test/saeeol/session-prompt-queue.test.ts +815 -0
  399. package/test/saeeol/sessions/inflight-cache.test.ts +157 -0
  400. package/test/saeeol/sessions/ingest-queue.test.ts +402 -0
  401. package/test/saeeol/sessions/remote-protocol.test.ts +258 -0
  402. package/test/saeeol/sessions/remote-sender.test.ts +1036 -0
  403. package/test/saeeol/sessions/remote-ws.test.ts +367 -0
  404. package/test/saeeol/sessions/sessions-enable-remote.test.disable +181 -0
  405. package/test/saeeol/slot-prop-reactivity.test.ts +142 -0
  406. package/test/saeeol/snapshot-cache.test.ts +84 -0
  407. package/test/saeeol/snapshot-freeze-repro.test.ts +100 -0
  408. package/test/saeeol/snapshot-track-timeout.test.ts +519 -0
  409. package/test/saeeol/stats-subagent-cost.test.ts +123 -0
  410. package/test/saeeol/suggestion/auto-dismiss.test.ts +65 -0
  411. package/test/saeeol/suggestion/suggestion.test.ts +145 -0
  412. package/test/saeeol/suggestion/tool.test.ts +298 -0
  413. package/test/saeeol/summary-file-diff.test.ts +28 -0
  414. package/test/saeeol/system-prompt.test.ts +142 -0
  415. package/test/saeeol/task-nesting.test.ts +193 -0
  416. package/test/saeeol/telemetry/feedback.test.ts +8 -0
  417. package/test/saeeol/todo-view.test.ts +57 -0
  418. package/test/saeeol/tool-encoding.test.ts +455 -0
  419. package/test/saeeol/tool-registry-indexing-import-failure.test.ts +49 -0
  420. package/test/saeeol/tool-registry-indexing.test.ts +236 -0
  421. package/test/saeeol/tool-registry-semantic-import-failure.test.ts +55 -0
  422. package/test/saeeol/tool-task-model.test.ts +352 -0
  423. package/test/saeeol/transform-opus-4.7.test.ts +89 -0
  424. package/test/saeeol/tui-diff.test.ts +91 -0
  425. package/test/saeeol/tui-sync.test.ts +80 -0
  426. package/test/saeeol/util/url.test.ts +141 -0
  427. package/test/saeeol/workflows-migrator.test.ts +261 -0
  428. package/test/saeeol/worktree-diff-summary.test.ts +64 -0
  429. package/test/saeeol/worktree-diff.test.ts +223 -0
  430. package/test/saeeol/worktree-remove-lock.test.ts +82 -0
  431. package/test/server/AGENTS.md +15 -0
  432. package/test/server/contract.test.ts +357 -0
  433. package/test/server/experimental-session-list.test.ts +157 -0
  434. package/test/server/global-session-list.test.ts +155 -0
  435. package/test/server/httpapi-authorization.test.ts +103 -0
  436. package/test/server/httpapi-bridge.test.ts +440 -0
  437. package/test/server/httpapi-config.test.ts +67 -0
  438. package/test/server/httpapi-cors.test.ts +89 -0
  439. package/test/server/httpapi-event.test.ts +57 -0
  440. package/test/server/httpapi-experimental.test.ts +219 -0
  441. package/test/server/httpapi-file.test.ts +79 -0
  442. package/test/server/httpapi-instance-context.test.ts +237 -0
  443. package/test/server/httpapi-instance.legacy.test.ts +140 -0
  444. package/test/server/httpapi-instance.test.ts +83 -0
  445. package/test/server/httpapi-json-parity.test.ts +263 -0
  446. package/test/server/httpapi-mcp-oauth.test.ts +76 -0
  447. package/test/server/httpapi-mcp.test.ts +189 -0
  448. package/test/server/httpapi-provider.test.ts +153 -0
  449. package/test/server/httpapi-pty-websocket.test.ts +16 -0
  450. package/test/server/httpapi-pty.test.ts +175 -0
  451. package/test/server/httpapi-raw-route-auth.test.ts +89 -0
  452. package/test/server/httpapi-sdk.test.ts +681 -0
  453. package/test/server/httpapi-session.test.ts +464 -0
  454. package/test/server/httpapi-sync.test.ts +130 -0
  455. package/test/server/httpapi-tui.test.ts +121 -0
  456. package/test/server/httpapi-workspace-routing.test.ts +471 -0
  457. package/test/server/httpapi-workspace.test.ts +427 -0
  458. package/test/server/lib/conformance.ts +88 -0
  459. package/test/server/lib/stateful.ts +112 -0
  460. package/test/server/project-init-git.test.ts +113 -0
  461. package/test/server/proxy-util.test.ts +113 -0
  462. package/test/server/session-actions.test.ts +49 -0
  463. package/test/server/session-list.test.ts +238 -0
  464. package/test/server/session-messages.test.ts +167 -0
  465. package/test/server/session-select.test.ts +100 -0
  466. package/test/server/trace-attributes.test.ts +76 -0
  467. package/test/server/workspace-proxy.test.ts +165 -0
  468. package/test/server/workspace-routing.test.ts +85 -0
  469. package/test/session/compaction.test.ts +2420 -0
  470. package/test/session/instruction.test.ts +247 -0
  471. package/test/session/llm.test.ts +1273 -0
  472. package/test/session/message-v2.test.ts +1291 -0
  473. package/test/session/messages-pagination.test.ts +1173 -0
  474. package/test/session/network.test.ts +249 -0
  475. package/test/session/processor-effect.test.ts +847 -0
  476. package/test/session/prompt.test.ts +2131 -0
  477. package/test/session/retry.test.ts +340 -0
  478. package/test/session/revert-compact.test.ts +639 -0
  479. package/test/session/schema-decoding.test.ts +311 -0
  480. package/test/session/session-entry-stepper.test.ts +917 -0
  481. package/test/session/session-schema.test.ts +76 -0
  482. package/test/session/snapshot-tool-race.test.ts +257 -0
  483. package/test/session/structured-output-integration.test.ts +265 -0
  484. package/test/session/structured-output.test.ts +381 -0
  485. package/test/session/system.test.ts +73 -0
  486. package/test/share/share-next.test.ts +333 -0
  487. package/test/shell/shell.test.ts +99 -0
  488. package/test/skill/discovery.test.ts +116 -0
  489. package/test/skill/skill.test.ts +393 -0
  490. package/test/snapshot/snapshot.test.ts +1531 -0
  491. package/test/storage/db.test.ts +23 -0
  492. package/test/storage/json-migration.test.ts +832 -0
  493. package/test/storage/storage.test.ts +293 -0
  494. package/test/suggestion/suggestion.test.ts +1 -0
  495. package/test/sync/index.test.ts +256 -0
  496. package/test/tool/__snapshots__/parameters.test.ts.snap +500 -0
  497. package/test/tool/__snapshots__/tool.test.ts.snap +9 -0
  498. package/test/tool/apply_patch.test.ts +614 -0
  499. package/test/tool/bash.test.ts +1225 -0
  500. package/test/tool/diagnostics-filter.test.ts +55 -0
  501. package/test/tool/edit.test.ts +754 -0
  502. package/test/tool/external-directory.test.ts +169 -0
  503. package/test/tool/fixtures/large-image.png +0 -0
  504. package/test/tool/fixtures/models-api.json +65179 -0
  505. package/test/tool/glob.test.ts +107 -0
  506. package/test/tool/grep.test.ts +114 -0
  507. package/test/tool/lsp.test.ts +187 -0
  508. package/test/tool/parameters.test.ts +243 -0
  509. package/test/tool/question.test.ts +129 -0
  510. package/test/tool/read.test.ts +500 -0
  511. package/test/tool/recall.test.ts +151 -0
  512. package/test/tool/registry.test.ts +203 -0
  513. package/test/tool/skill.test.ts +135 -0
  514. package/test/tool/suggest.test.ts +1 -0
  515. package/test/tool/task.test.ts +612 -0
  516. package/test/tool/tool-define.test.ts +99 -0
  517. package/test/tool/truncation.test.ts +260 -0
  518. package/test/tool/webfetch.test.ts +103 -0
  519. package/test/tool/write.test.ts +291 -0
  520. package/test/util/data-url.test.ts +14 -0
  521. package/test/util/effect-zod.test.ts +754 -0
  522. package/test/util/error.test.ts +38 -0
  523. package/test/util/filesystem.test.ts +656 -0
  524. package/test/util/format.test.ts +59 -0
  525. package/test/util/glob.test.ts +164 -0
  526. package/test/util/iife.test.ts +36 -0
  527. package/test/util/lazy.test.ts +50 -0
  528. package/test/util/lock.test.ts +72 -0
  529. package/test/util/log.test.ts +86 -0
  530. package/test/util/module.test.ts +59 -0
  531. package/test/util/process.test.ts +128 -0
  532. package/test/util/timeout.test.ts +21 -0
  533. package/test/util/which.test.ts +100 -0
  534. package/test/util/wildcard.test.ts +90 -0
  535. package/test/workspace/workspace-restore.test.ts +296 -0
  536. package/src/provider/models-snapshot.d.ts +0 -2
  537. package/src/provider/models-snapshot.js +0 -3
@@ -1,5 +1,5 @@
1
- import { fetchSaeeolModels, type SaeeolModelsResult } from "@saeeol/gateway"
2
- import { TTLCache } from "@saeeol/boxes/ttl-cache"
1
+ import { fetchSaeeolModels, type SaeeolModelsResult } from "@saeeol/gateway"
2
+ import { TTLCache } from "@/boxes/ttl-cache"
3
3
  import { Config } from "../config/config"
4
4
  import { Auth } from "../auth"
5
5
  import * as Log from "@saeeol/core/util/log"
@@ -188,7 +188,7 @@ export async function resolveSDK(model: Model, s: State, envs: Record<string, st
188
188
  void Bus.publish(ProviderInstallEvent.Started, {
189
189
  providerID: model.providerID,
190
190
  pkg: model.api.npm,
191
- })
191
+ }).catch(() => {})
192
192
  try {
193
193
  const item = await Npm.add(model.api.npm)
194
194
  if (!item.entrypoint) throw new Error(`Package ${model.api.npm} has no import entrypoint`)
@@ -196,13 +196,13 @@ export async function resolveSDK(model: Model, s: State, envs: Record<string, st
196
196
  void Bus.publish(ProviderInstallEvent.Completed, {
197
197
  providerID: model.providerID,
198
198
  pkg: model.api.npm,
199
- })
199
+ }).catch(() => {})
200
200
  } catch (installErr) {
201
201
  void Bus.publish(ProviderInstallEvent.Failed, {
202
202
  providerID: model.providerID,
203
203
  pkg: model.api.npm,
204
204
  error: installErr instanceof Error ? installErr.message : String(installErr),
205
- })
205
+ }).catch(() => {})
206
206
  throw installErr
207
207
  }
208
208
  } else {
@@ -1,4 +1,4 @@
1
- import { parse as parseDataUrl } from "@saeeol/boxes/dataurl"
1
+ import { parse as parseDataUrl } from "@/boxes/dataurl"
2
2
  import type { ModelMessage } from "ai"
3
3
  import { mergeDeep, unique } from "remeda"
4
4
  import type * as Provider from "./provider"
@@ -0,0 +1,284 @@
1
+ import { Hono } from "hono"
2
+ import { z } from "zod"
3
+ import { generateText } from "ai"
4
+ import { createOpenAI } from "@ai-sdk/openai"
5
+ import { createOpenAICompatible } from "@ai-sdk/openai-compatible"
6
+ import * as Log from "@saeeol/core/util/log"
7
+ import { lazy } from "@/util/lazy"
8
+
9
+ const log = Log.create({ service: "game-event" })
10
+
11
+ // ── Request schemas ──────────────────────────────────────────────
12
+
13
+ const GameContextSchema = z.object({
14
+ character: z.object({ id: z.string(), name: z.string() }).optional(),
15
+ stats: z.record(z.number()).optional(),
16
+ flags: z.record(z.any()).optional(),
17
+ items: z.record(z.number()).optional(),
18
+ route: z.string().optional(),
19
+ act: z.string().optional(),
20
+ choice_history: z.array(z.string()).optional(),
21
+ bosses_defeated: z.array(z.string()).optional(),
22
+ })
23
+
24
+ const EventSchema = z.object({
25
+ id: z.string().optional(),
26
+ lines: z.array(z.object({ speaker: z.string(), text: z.string() }).passthrough()).optional(),
27
+ choices: z.array(z.any()).optional(),
28
+ nextSceneId: z.string().optional(),
29
+ }).passthrough()
30
+
31
+ const BaseRequestSchema = z.object({
32
+ action: z.enum(["augment", "generate", "reaction"]),
33
+ context: GameContextSchema.optional(),
34
+ })
35
+
36
+ const AugmentRequestSchema = BaseRequestSchema.extend({
37
+ action: z.literal("augment"),
38
+ event: EventSchema,
39
+ })
40
+
41
+ const GenerateRequestSchema = BaseRequestSchema.extend({
42
+ action: z.literal("generate"),
43
+ event_id: z.string(),
44
+ prompt_hint: z.record(z.any()).optional(),
45
+ })
46
+
47
+ const ReactionRequestSchema = BaseRequestSchema.extend({
48
+ action: z.literal("reaction"),
49
+ choice_text: z.string(),
50
+ event: EventSchema,
51
+ })
52
+
53
+ const RequestSchema = z.discriminatedUnion("action", [
54
+ AugmentRequestSchema,
55
+ GenerateRequestSchema,
56
+ ReactionRequestSchema,
57
+ ])
58
+
59
+ // ── Prompt builders ──────────────────────────────────────────────
60
+
61
+ function buildSystemPrompt(hint: Record<string, any> = {}): string {
62
+ const tone = hint.tone ?? "몰입감 있고 생생한 묘사"
63
+ const themes = (hint.themes ?? []).join(", ")
64
+ const escalation = hint.escalation ?? ""
65
+
66
+ return `너는 에로틱 판타지 던전 크롤러 게임의 AI 내레이터다.
67
+
68
+ ## 역할
69
+ - 플레이어의 행동과 상황에 맞는 생생한 묘사를 생성한다
70
+ - 캐릭터의 성격과 현재 상태(HP, 음란도, 개발도, 소지 아이템, 플래그)를 반영한다
71
+ - 항상 한국어로 응답한다
72
+
73
+ ## 분위기
74
+ - 톤: ${tone}
75
+ - 테마: ${themes}
76
+ ${escalation ? `- 에스컬레이션: ${escalation}` : ""}
77
+
78
+ ## 응답 형식
79
+ 항상 다음 JSON 스키마로 응답한다. 다른 텍스트는 절대 포함하지 않는다:
80
+ {
81
+ "lines": [
82
+ { "speaker": "나레이션", "text": "묘사 텍스트", "ai": true }
83
+ ]
84
+ }
85
+
86
+ ## 규칙
87
+ - lines는 1~3개
88
+ - 각 line의 text는 1~3문장
89
+ - 캐릭터 이름은 context에서 가져와서 자연스럽게 사용
90
+ - 현재 루트와 act의 분위기를 반영
91
+ - 플래그가 있으면 이전 경험을 참조
92
+ - 절대 JSON 이외의 텍스트를 출력하지 않는다`
93
+ }
94
+
95
+ function buildAugmentPrompt(event: any, context: any): string {
96
+ const charName = context?.character?.name ?? "모험자"
97
+ const charId = context?.character?.id ?? ""
98
+ const route = context?.route ?? ""
99
+ const flags = context?.flags ?? {}
100
+ const stats = context?.stats ?? {}
101
+ const originalLines = (event?.lines ?? []).map((l: any) => `${l.speaker}: ${l.text}`).join("\n")
102
+
103
+ return `이 이벤트에 분위기를 더하는 AI 묘사를 생성하라.
104
+
105
+ ## 현재 상태
106
+ - 캐릭터: ${charName} (${charId})
107
+ - 루트: ${route}
108
+ - HP: ${stats.hp ?? "?"}, 음란도: ${stats.lewdness ?? "?"}, 개발도: ${stats.corruption ?? "?"}
109
+ - 플래그: ${JSON.stringify(flags)}
110
+
111
+ ## 원본 이벤트 대사
112
+ ${originalLines}
113
+
114
+ ## 요청
115
+ 원본 대사 앞에 들어갈 1~2줄의 분위기 묘사를 생성하라. 캐릭터의 현재 상태와 플래그를 반영하라.`
116
+ }
117
+
118
+ function buildGeneratePrompt(eventId: string, context: any, hint: Record<string, any> = {}): string {
119
+ const charName = context?.character?.name ?? "모험자"
120
+ const route = context?.route ?? ""
121
+ const act = context?.act ?? ""
122
+ const stats = context?.stats ?? {}
123
+ const flags = context?.flags ?? {}
124
+ const items = context?.items ?? {}
125
+ const charHints = hint.character_hints ?? {}
126
+ const charId = context?.character?.id ?? ""
127
+ const charHint = charHints[charId] ?? ""
128
+
129
+ return `완전히 새로운 게임 이벤트를 생성하라.
130
+
131
+ ## 이벤트 ID: ${eventId}
132
+ ## Act: ${act}, Route: ${route}
133
+
134
+ ## 캐릭터
135
+ - 이름: ${charName} (${charId})
136
+ - 성향: ${charHint}
137
+ - HP: ${stats.hp ?? "?"}, 음란도: ${stats.lewdness ?? "?"}, 개발도: ${stats.corruption ?? "?"}
138
+
139
+ ## 소지품
140
+ ${JSON.stringify(items)}
141
+
142
+ ## 경험한 일들 (플래그)
143
+ ${JSON.stringify(flags)}
144
+
145
+ ## 요청
146
+ 다음 JSON 스키마로 완전한 이벤트를 생성하라:
147
+ {
148
+ "id": "${eventId}",
149
+ "act": "${act}",
150
+ "lines": [
151
+ { "speaker": "나레이션", "text": "...", "ai": true }
152
+ ],
153
+ "choices": [
154
+ { "text": "선택지 텍스트", "nextSceneId": "다음_이벤트_ID", "conditions": [], "effects": [] }
155
+ ],
156
+ "nextSceneId": "기본_다음_이벤트_ID"
157
+ }
158
+
159
+ 2~4줄의 대사와 2~3개의 선택지를 만들어라.`
160
+ }
161
+
162
+ function buildReactionPrompt(choiceText: string, event: any, context: any): string {
163
+ const charName = context?.character?.name ?? "모험자"
164
+ const stats = context?.stats ?? {}
165
+
166
+ return `플레이어가 선택을 한 직후의 반응 묘사를 생성하라.
167
+
168
+ ## 선택한 것: "${choiceText}"
169
+ ## 캐릭터: ${charName}
170
+ - HP: ${stats.hp ?? "?"}, 음란도: ${stats.lewdness ?? "?"}
171
+
172
+ ## 요청
173
+ 선택 직후의 짧은 반응 묘사 1~2줄을 생성하라. 캐릭터의 감정과 상황 변화를 반영하라.
174
+
175
+ JSON 형식:
176
+ {
177
+ "lines": [
178
+ { "speaker": "나레이션", "text": "...", "ai": true }
179
+ ]
180
+ }`
181
+ }
182
+
183
+ // ── LLM call ─────────────────────────────────────────────────────
184
+
185
+ function createProvider() {
186
+ // Use OPENAI_API_KEY or OPENAI_BASE_URL from env
187
+ const apiKey = process.env.OPENAI_API_KEY ?? process.env.ANTHROPIC_API_KEY ?? ""
188
+ const baseURL = process.env.OPENAI_BASE_URL
189
+
190
+ if (baseURL) {
191
+ return createOpenAICompatible({ name: "custom", baseURL, apiKey })
192
+ }
193
+
194
+ return createOpenAI({ apiKey })
195
+ }
196
+
197
+ async function callLLM(systemPrompt: string, userPrompt: string): Promise<any> {
198
+ const provider = createProvider()
199
+ const modelId = process.env.GAME_LLM_MODEL ?? "gpt-4o-mini"
200
+ const model = provider(modelId)
201
+
202
+ const result = await generateText({
203
+ model,
204
+ system: systemPrompt,
205
+ prompt: userPrompt,
206
+ maxTokens: 1024,
207
+ temperature: 0.8,
208
+ })
209
+
210
+ // Extract JSON from response (handle markdown code blocks)
211
+ let text = result.text.trim()
212
+ const jsonMatch = text.match(/```(?:json)?\s*([\s\S]*?)```/)
213
+ if (jsonMatch) {
214
+ text = jsonMatch[1].trim()
215
+ }
216
+
217
+ return JSON.parse(text)
218
+ }
219
+
220
+ // ── Routes ───────────────────────────────────────────────────────
221
+
222
+ export const GameEventRoutes = lazy(() =>
223
+ new Hono()
224
+ .post(
225
+ "/event",
226
+ async (c) => {
227
+ const body = await c.req.json()
228
+ const parsed = RequestSchema.safeParse(body)
229
+
230
+ if (!parsed.success) {
231
+ return c.json({ error: "Invalid request", details: parsed.error.flatten() }, 400)
232
+ }
233
+
234
+ const req = parsed.data
235
+ const context = req.context ?? {}
236
+
237
+ try {
238
+ switch (req.action) {
239
+ case "augment": {
240
+ const hint: Record<string, any> = {}
241
+ const systemPrompt = buildSystemPrompt(hint)
242
+ const userPrompt = buildAugmentPrompt(req.event, context)
243
+
244
+ const result = await callLLM(systemPrompt, userPrompt)
245
+
246
+ // Merge AI lines into original event
247
+ const aiLines: any[] = result.lines ?? []
248
+ const originalLines: any[] = req.event?.lines ?? []
249
+ const merged = {
250
+ ...req.event,
251
+ lines: [...aiLines, ...originalLines],
252
+ }
253
+
254
+ log.info("augment event", { id: req.event?.id, aiLines: aiLines.length })
255
+ return c.json(merged)
256
+ }
257
+
258
+ case "generate": {
259
+ const hint = req.prompt_hint ?? {}
260
+ const systemPrompt = buildSystemPrompt(hint)
261
+ const userPrompt = buildGeneratePrompt(req.event_id, context, hint)
262
+
263
+ const result = await callLLM(systemPrompt, userPrompt)
264
+ log.info("generate event", { id: req.event_id })
265
+ return c.json(result)
266
+ }
267
+
268
+ case "reaction": {
269
+ const hint: Record<string, any> = {}
270
+ const systemPrompt = buildSystemPrompt(hint)
271
+ const userPrompt = buildReactionPrompt(req.choice_text, req.event, context)
272
+
273
+ const result = await callLLM(systemPrompt, userPrompt)
274
+ log.info("reaction", { choice: req.choice_text })
275
+ return c.json(result)
276
+ }
277
+ }
278
+ } catch (err: any) {
279
+ log.error("game event failed", { error: err.message })
280
+ return c.json({ error: err.message }, 500)
281
+ }
282
+ },
283
+ ),
284
+ )
@@ -13,6 +13,7 @@ import { InstanceRoutes } from "./routes/instance"
13
13
  import { ControlPlaneRoutes } from "./routes/control"
14
14
  import { UIRoutes } from "./routes/ui"
15
15
  import { GlobalRoutes } from "./routes/global"
16
+ import { GameEventRoutes } from "./routes/game"
16
17
  import { WorkspaceRouterMiddleware } from "./workspace"
17
18
  import { InstanceMiddleware } from "./routes/instance/middleware"
18
19
  import { WorkspaceRoutes } from "./routes/control/workspace"
@@ -106,6 +107,7 @@ function createHono(opts: CorsOptions, selection: ServerBackend.Selection = Serv
106
107
  .use(CompressionMiddleware)
107
108
  .use(CorsMiddleware(opts))
108
109
  .route("/global", GlobalRoutes())
110
+ .route("/api", GameEventRoutes())
109
111
 
110
112
  const runtime = adapter.create(app)
111
113
 
@@ -1,4 +1,4 @@
1
- import { clamp } from "@saeeol/boxes/clamp"
1
+ import { clamp } from "@/boxes/clamp"
2
2
  import { BusEvent } from "@/bus/bus-event"
3
3
  import { SessionID, MessageID } from "../schema"
4
4
  import { Provider } from "@/provider/provider"
@@ -1,4 +1,4 @@
1
- import { clamp } from "@saeeol/boxes/clamp"
1
+ import { clamp } from "@/boxes/clamp"
2
2
  import { BusEvent } from "@/bus/bus-event"
3
3
  import { Bus } from "@/bus"
4
4
  import * as Session from "../session"
@@ -317,7 +317,9 @@ export const getUsage = (input: {
317
317
  input.metadata?.["anthropic"]?.["cacheCreationInputTokens"] ??
318
318
  // google-vertex-anthropic returns metadata under "vertex" key
319
319
  input.metadata?.["vertex"]?.["cacheCreationInputTokens"] ??
320
+ // @ts-expect-error
320
321
  input.metadata?.["bedrock"]?.["usage"]?.["cacheWriteInputTokens"] ??
322
+ // @ts-expect-error
321
323
  input.metadata?.["venice"]?.["usage"]?.["cacheCreationInputTokens"] ??
322
324
  0,
323
325
  ),
@@ -1,7 +1,7 @@
1
- import { ulid } from "ulid"
1
+ import { ulid } from "ulid"
2
2
  import type * as SDK from "@saeeol/sdk/v2"
3
3
  import type { SaeeolSession } from "@/saeeol/session"
4
- import { expDelay } from "@saeeol/boxes/schedule"
4
+ import { expDelay } from "@/boxes/schedule"
5
5
 
6
6
  export namespace IngestQueue {
7
7
  export type Client = {
@@ -1,4 +1,4 @@
1
- import { random as randomId } from "@saeeol/boxes/uid"
1
+ import { random as randomId } from "@/boxes/uid"
2
2
  import { RemoteProtocol } from "@/sessions/remote-protocol"
3
3
 
4
4
  export namespace RemoteWS {
@@ -1,5 +1,5 @@
1
1
  import { Effect, Schema } from "effect"
2
- import { plural } from "@saeeol/boxes/plural"
2
+ import { plural } from "@/boxes/plural"
3
3
  import * as Tool from "../core/tool"
4
4
  import { Question } from "../../question"
5
5
  import DESCRIPTION from "./question.txt"
package/src/util/abort.ts CHANGED
@@ -1 +1 @@
1
- export { cancelAfter as abortAfter, cancelAny as abortAfterAny } from "@saeeol/boxes/cancel"
1
+ export { cancelAfter as abortAfter, cancelAny as abortAfterAny } from "@/boxes/cancel"
package/src/util/bom.ts CHANGED
@@ -1,7 +1,7 @@
1
- export { split, join } from "@saeeol/boxes/bom"
1
+ export { split, join } from "@/boxes/bom"
2
2
  import { Effect } from "effect"
3
3
  import { AppFileSystem } from "@saeeol/core/filesystem"
4
- import { split, join } from "@saeeol/boxes/bom"
4
+ import { split, join } from "@/boxes/bom"
5
5
 
6
6
  export const readFile = Effect.fn("Bom.readFile")(function* (fs: AppFileSystem.Interface, filePath: string) {
7
7
  return split(new TextDecoder("utf-8", { ignoreBOM: true }).decode(yield* fs.readFile(filePath)))
package/src/util/color.ts CHANGED
@@ -1,3 +1,3 @@
1
- export { valid as isValidHex, rgb as hexToRgb, bold as hexToAnsiBold } from "@saeeol/boxes/ansi"
1
+ export { valid as isValidHex, rgb as hexToRgb, bold as hexToAnsiBold } from "@/boxes/ansi"
2
2
 
3
3
  export * as Color from "./color"
@@ -1 +1 @@
1
- export { decode as decodeDataUrl } from "@saeeol/boxes/dataurl"
1
+ export { decode as decodeDataUrl } from "@/boxes/dataurl"
package/src/util/defer.ts CHANGED
@@ -1 +1 @@
1
- export { disposable as defer } from "@saeeol/boxes/disposable"
1
+ export { disposable as defer } from "@/boxes/disposable"
package/src/util/error.ts CHANGED
@@ -1,5 +1,5 @@
1
- export { msg as errorMessage, fmt as errorFormat } from "@saeeol/boxes/err"
2
- import { msg as errorMessage, fmt as errorFormat } from "@saeeol/boxes/err"
1
+ export { msg as errorMessage, fmt as errorFormat } from "@/boxes/err"
2
+ import { msg as errorMessage, fmt as errorFormat } from "@/boxes/err"
3
3
  import { isRecord } from "./record"
4
4
 
5
5
  export function errorData(error: unknown) {
@@ -1,5 +1,5 @@
1
- import { chmod, mkdir, readFile, stat as statFile, writeFile } from "fs/promises"
2
- import { atomicWrite } from "@saeeol/boxes/atomic-write"
1
+ import { chmod, mkdir, readFile, stat as statFile, writeFile } from "fs/promises"
2
+ import { atomicWrite } from "@/boxes/atomic-write"
3
3
  import { createWriteStream, existsSync, statSync } from "fs"
4
4
  import { realpathSync } from "fs"
5
5
  import { dirname, isAbsolute, join, relative, resolve as pathResolve, sep, win32 } from "path"
@@ -1 +1 @@
1
- export { secsDuration as formatDuration } from "@saeeol/boxes/human"
1
+ export { secsDuration as formatDuration } from "@/boxes/human"
package/src/util/iife.ts CHANGED
@@ -1 +1 @@
1
- export { iife } from "@saeeol/boxes/iife"
1
+ export { iife } from "@/boxes/iife"
@@ -1,3 +1,3 @@
1
- export { scope as create, NotFound } from "@saeeol/boxes/scope"
1
+ export { scope as create, NotFound } from "@/boxes/scope"
2
2
 
3
3
  export * as LocalContext from "./local-context"
@@ -1,5 +1,5 @@
1
- import { time, datetime } from "@saeeol/boxes/human"
2
- export { title as titlecase, time, datetime, compact as number, duration, truncate, truncateMid as truncateMiddle, plural as pluralize } from "@saeeol/boxes/human"
1
+ import { time, datetime } from "@/boxes/human"
2
+ export { title as titlecase, time, datetime, compact as number, duration, truncate, truncateMid as truncateMiddle, plural as pluralize } from "@/boxes/human"
3
3
 
4
4
  export function todayTimeOrDateTime(input: number): string {
5
5
  const date = new Date(input)
package/src/util/lock.ts CHANGED
@@ -1,3 +1,3 @@
1
- export { read, write } from "@saeeol/boxes/rwlock"
1
+ export { read, write } from "@/boxes/rwlock"
2
2
 
3
3
  export * as Lock from "./lock"
@@ -1 +1 @@
1
- export { online, proxied } from "@saeeol/boxes/net"
1
+ export { online, proxied } from "@/boxes/net"
@@ -1,4 +1,4 @@
1
- import { latch } from "@saeeol/boxes/latch"
1
+ import { latch } from "@/boxes/latch"
2
2
 
3
3
  /** One-shot signal (latch wrapper matching original trigger/wait API) */
4
4
  export function signal() {
package/src/util/token.ts CHANGED
@@ -1,3 +1,3 @@
1
- export { count as estimate } from "@saeeol/boxes/tokens"
1
+ export { count as estimate } from "@/boxes/tokens"
2
2
 
3
3
  export * as Token from "./token"
@@ -1,4 +1,4 @@
1
- import { globMatch, globBest, globStructured } from "@saeeol/boxes/wildcard"
1
+ import { globMatch, globBest, globStructured } from "@/boxes/wildcard"
2
2
 
3
3
  const ci = process.platform === "win32"
4
4
 
package/sst-env.d.ts ADDED
@@ -0,0 +1,10 @@
1
+ /* This file is auto-generated by SST. Do not edit. */
2
+ /* tslint:disable */
3
+ /* eslint-disable */
4
+ /* deno-fmt-ignore-file */
5
+ /* biome-ignore-all lint: auto-generated */
6
+
7
+ /// <reference path="../../sst-env.d.ts" />
8
+
9
+ import "sst"
10
+ export {}
package/test/AGENTS.md ADDED
@@ -0,0 +1,133 @@
1
+ # Test Fixtures Guide
2
+
3
+ ## Temporary Directory Fixture
4
+
5
+ The `tmpdir` function in `fixture/fixture.ts` creates temporary directories for tests with automatic cleanup.
6
+
7
+ ### Basic Usage
8
+
9
+ ```typescript
10
+ import { tmpdir } from "./fixture/fixture"
11
+
12
+ test("example", async () => {
13
+ await using tmp = await tmpdir()
14
+ // tmp.path is the temp directory path
15
+ // automatically cleaned up when test ends
16
+ })
17
+ ```
18
+
19
+ ### Options
20
+
21
+ - `git?: boolean` - Initialize a git repo with a root commit
22
+ - `config?: Partial<Config.Info>` - Write an `saeeol.json` config file
23
+ - `init?: (dir: string) => Promise<T>` - Custom setup function, returns value accessible as `tmp.extra`
24
+ - `dispose?: (dir: string) => Promise<T>` - Custom cleanup function
25
+
26
+ ### Examples
27
+
28
+ **Git repository:**
29
+
30
+ ```typescript
31
+ await using tmp = await tmpdir({ git: true })
32
+ ```
33
+
34
+ **With config file:**
35
+
36
+ ```typescript
37
+ await using tmp = await tmpdir({
38
+ config: { model: "test/model", username: "testuser" },
39
+ })
40
+ ```
41
+
42
+ **Custom initialization (returns extra data):**
43
+
44
+ ```typescript
45
+ await using tmp = await tmpdir<string>({
46
+ init: async (dir) => {
47
+ await Bun.write(path.join(dir, "file.txt"), "content")
48
+ return "extra data"
49
+ },
50
+ })
51
+ // Access extra data via tmp.extra
52
+ console.log(tmp.extra) // "extra data"
53
+ ```
54
+
55
+ **With cleanup:**
56
+
57
+ ```typescript
58
+ await using tmp = await tmpdir({
59
+ init: async (dir) => {
60
+ const specialDir = path.join(dir, "special")
61
+ await fs.mkdir(specialDir)
62
+ return specialDir
63
+ },
64
+ dispose: async (dir) => {
65
+ // Custom cleanup logic
66
+ await fs.rm(path.join(dir, "special"), { recursive: true })
67
+ },
68
+ })
69
+ ```
70
+
71
+ ### Returned Object
72
+
73
+ - `path: string` - Absolute path to the temp directory (realpath resolved)
74
+ - `extra: T` - Value returned by the `init` function
75
+ - `[Symbol.asyncDispose]` - Enables automatic cleanup via `await using`
76
+
77
+ ### Notes
78
+
79
+ - Directories are created in the system temp folder with prefix `saeeol-test-`
80
+ - Use `await using` for automatic cleanup when the variable goes out of scope
81
+ - Paths are sanitized to strip null bytes (defensive fix for CI environments)
82
+
83
+ ## Testing With Effects
84
+
85
+ Use `testEffect(...)` from `test/lib/effect.ts` for tests that exercise Effect services or Effect-based workflows.
86
+
87
+ ### Core Pattern
88
+
89
+ ```typescript
90
+ import { describe, expect } from "bun:test"
91
+ import { Effect, Layer } from "effect"
92
+ import { provideTmpdirInstance } from "../fixture/fixture"
93
+ import { testEffect } from "../lib/effect"
94
+
95
+ const it = testEffect(Layer.mergeAll(MyService.defaultLayer))
96
+
97
+ describe("my service", () => {
98
+ it.live("does the thing", () =>
99
+ provideTmpdirInstance(() =>
100
+ Effect.gen(function* () {
101
+ const svc = yield* MyService.Service
102
+ const out = yield* svc.run()
103
+ expect(out).toEqual("ok")
104
+ }),
105
+ ),
106
+ )
107
+ })
108
+ ```
109
+
110
+ ### `it.effect` vs `it.live`
111
+
112
+ - Use `it.effect(...)` when the test should run with `TestClock` and `TestConsole`.
113
+ - Use `it.live(...)` when the test depends on real time, filesystem mtimes, child processes, git, locks, or other live OS behavior.
114
+ - Most integration-style tests in this package use `it.live(...)`.
115
+
116
+ ### Effect Fixtures
117
+
118
+ Prefer the Effect-aware helpers from `fixture/fixture.ts` instead of building a manual runtime in each test.
119
+
120
+ - `tmpdirScoped(options?)` creates a scoped temp directory and cleans it up when the Effect scope closes.
121
+ - `provideInstance(dir)(effect)` is the low-level helper. It does not create a directory; it just runs an Effect with `Instance.current` bound to `dir`.
122
+ - `provideTmpdirInstance((dir) => effect, options?)` is the convenience helper. It creates a temp directory, binds it as the active instance, and disposes the instance on cleanup.
123
+ - `provideTmpdirServer((input) => effect, options?)` does the same, but also provides the test LLM server.
124
+
125
+ Use `provideTmpdirInstance(...)` by default when a test only needs one temp instance. Use `tmpdirScoped()` plus `provideInstance(...)` when a test needs multiple directories, custom setup before binding, or needs to switch instance context within one test.
126
+
127
+ ### Style
128
+
129
+ - Define `const it = testEffect(...)` near the top of the file.
130
+ - Keep the test body inside `Effect.gen(function* () { ... })`.
131
+ - Yield services directly with `yield* MyService.Service` or `yield* MyTool`.
132
+ - Avoid custom `ManagedRuntime`, `attach(...)`, or ad hoc `run(...)` wrappers when `testEffect(...)` already provides the runtime.
133
+ - When a test needs instance-local state, prefer `provideTmpdirInstance(...)` or `provideInstance(...)` over manual `Instance.provide(...)` inside Promise-style tests.