@otto-code/server 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (734) hide show
  1. package/.env.example +31 -0
  2. package/README.md +111 -0
  3. package/dist/scripts/dev-runner.js +37 -0
  4. package/dist/scripts/mcp-stdio-socket-bridge-cli.mjs +62 -0
  5. package/dist/scripts/supervisor-entrypoint.js +141 -0
  6. package/dist/scripts/supervisor-log-config.js +31 -0
  7. package/dist/scripts/supervisor.js +252 -0
  8. package/dist/server/executable-resolution/executable-resolution.d.ts +10 -0
  9. package/dist/server/executable-resolution/executable-resolution.js +118 -0
  10. package/dist/server/executable-resolution/windows.d.ts +18 -0
  11. package/dist/server/executable-resolution/windows.js +65 -0
  12. package/dist/server/server/agent/activity-curator.d.ts +28 -0
  13. package/dist/server/server/agent/activity-curator.js +227 -0
  14. package/dist/server/server/agent/agent-archive.d.ts +11 -0
  15. package/dist/server/server/agent/agent-archive.js +16 -0
  16. package/dist/server/server/agent/agent-loading.d.ts +14 -0
  17. package/dist/server/server/agent/agent-loading.js +54 -0
  18. package/dist/server/server/agent/agent-manager.d.ts +470 -0
  19. package/dist/server/server/agent/agent-manager.js +3049 -0
  20. package/dist/server/server/agent/agent-personalities.d.ts +59 -0
  21. package/dist/server/server/agent/agent-personalities.js +103 -0
  22. package/dist/server/server/agent/agent-projections.d.ts +35 -0
  23. package/dist/server/server/agent/agent-projections.js +488 -0
  24. package/dist/server/server/agent/agent-prompt.d.ts +76 -0
  25. package/dist/server/server/agent/agent-prompt.js +230 -0
  26. package/dist/server/server/agent/agent-response-loop.d.ts +72 -0
  27. package/dist/server/server/agent/agent-response-loop.js +333 -0
  28. package/dist/server/server/agent/agent-sdk-types.d.ts +736 -0
  29. package/dist/server/server/agent/agent-sdk-types.js +22 -0
  30. package/dist/server/server/agent/agent-storage.d.ts +147 -0
  31. package/dist/server/server/agent/agent-storage.js +326 -0
  32. package/dist/server/server/agent/agent-stream-coalescer.d.ts +41 -0
  33. package/dist/server/server/agent/agent-stream-coalescer.js +166 -0
  34. package/dist/server/server/agent/agent-timeline-store-types.d.ts +50 -0
  35. package/dist/server/server/agent/agent-timeline-store-types.js +2 -0
  36. package/dist/server/server/agent/agent-timeline-store.d.ts +27 -0
  37. package/dist/server/server/agent/agent-timeline-store.js +230 -0
  38. package/dist/server/server/agent/audio-utils.d.ts +3 -0
  39. package/dist/server/server/agent/audio-utils.js +19 -0
  40. package/dist/server/server/agent/create-agent/create.d.ts +91 -0
  41. package/dist/server/server/agent/create-agent/create.js +370 -0
  42. package/dist/server/server/agent/create-agent-lifecycle-dispatch.d.ts +54 -0
  43. package/dist/server/server/agent/create-agent-lifecycle-dispatch.js +152 -0
  44. package/dist/server/server/agent/create-agent-mode.d.ts +13 -0
  45. package/dist/server/server/agent/create-agent-mode.js +62 -0
  46. package/dist/server/server/agent/create-agent-title.d.ts +10 -0
  47. package/dist/server/server/agent/create-agent-title.js +34 -0
  48. package/dist/server/server/agent/dictation-debug.d.ts +13 -0
  49. package/dist/server/server/agent/dictation-debug.js +50 -0
  50. package/dist/server/server/agent/effort-levels.d.ts +2 -0
  51. package/dist/server/server/agent/effort-levels.js +7 -0
  52. package/dist/server/server/agent/foreground-run-state.d.ts +50 -0
  53. package/dist/server/server/agent/foreground-run-state.js +160 -0
  54. package/dist/server/server/agent/import-sessions.d.ts +47 -0
  55. package/dist/server/server/agent/import-sessions.js +145 -0
  56. package/dist/server/server/agent/lifecycle-command.d.ts +73 -0
  57. package/dist/server/server/agent/lifecycle-command.js +92 -0
  58. package/dist/server/server/agent/mcp-server.d.ts +5 -0
  59. package/dist/server/server/agent/mcp-server.js +66 -0
  60. package/dist/server/server/agent/mcp-shared.d.ts +192 -0
  61. package/dist/server/server/agent/mcp-shared.js +201 -0
  62. package/dist/server/server/agent/pcm16-resampler.d.ts +14 -0
  63. package/dist/server/server/agent/pcm16-resampler.js +63 -0
  64. package/dist/server/server/agent/permission-response.d.ts +15 -0
  65. package/dist/server/server/agent/permission-response.js +14 -0
  66. package/dist/server/server/agent/personality-stats-store.d.ts +23 -0
  67. package/dist/server/server/agent/personality-stats-store.js +65 -0
  68. package/dist/server/server/agent/prompt-attachments.d.ts +7 -0
  69. package/dist/server/server/agent/prompt-attachments.js +117 -0
  70. package/dist/server/server/agent/provider-history-timestamps.d.ts +2 -0
  71. package/dist/server/server/agent/provider-history-timestamps.js +16 -0
  72. package/dist/server/server/agent/provider-launch-config.d.ts +43 -0
  73. package/dist/server/server/agent/provider-launch-config.js +167 -0
  74. package/dist/server/server/agent/provider-notices.d.ts +3 -0
  75. package/dist/server/server/agent/provider-notices.js +5 -0
  76. package/dist/server/server/agent/provider-registry.d.ts +41 -0
  77. package/dist/server/server/agent/provider-registry.js +532 -0
  78. package/dist/server/server/agent/provider-session-import.d.ts +10 -0
  79. package/dist/server/server/agent/provider-session-import.js +49 -0
  80. package/dist/server/server/agent/provider-snapshot-manager.d.ts +142 -0
  81. package/dist/server/server/agent/provider-snapshot-manager.js +690 -0
  82. package/dist/server/server/agent/providers/acp-agent.d.ts +353 -0
  83. package/dist/server/server/agent/providers/acp-agent.js +2439 -0
  84. package/dist/server/server/agent/providers/claude/agent.d.ts +66 -0
  85. package/dist/server/server/agent/providers/claude/agent.js +4535 -0
  86. package/dist/server/server/agent/providers/claude/feature-definitions.d.ts +8 -0
  87. package/dist/server/server/agent/providers/claude/feature-definitions.js +24 -0
  88. package/dist/server/server/agent/providers/claude/model-manifest.d.ts +90 -0
  89. package/dist/server/server/agent/providers/claude/model-manifest.js +205 -0
  90. package/dist/server/server/agent/providers/claude/models.d.ts +11 -0
  91. package/dist/server/server/agent/providers/claude/models.js +97 -0
  92. package/dist/server/server/agent/providers/claude/partial-json.d.ts +5 -0
  93. package/dist/server/server/agent/providers/claude/partial-json.js +306 -0
  94. package/dist/server/server/agent/providers/claude/project-dir.d.ts +6 -0
  95. package/dist/server/server/agent/providers/claude/project-dir.js +57 -0
  96. package/dist/server/server/agent/providers/claude/query.d.ts +17 -0
  97. package/dist/server/server/agent/providers/claude/query.js +86 -0
  98. package/dist/server/server/agent/providers/claude/rewind.d.ts +30 -0
  99. package/dist/server/server/agent/providers/claude/rewind.js +30 -0
  100. package/dist/server/server/agent/providers/claude/sidechain-tracker.d.ts +22 -0
  101. package/dist/server/server/agent/providers/claude/sidechain-tracker.js +232 -0
  102. package/dist/server/server/agent/providers/claude/task-notification-tool-call.d.ts +35 -0
  103. package/dist/server/server/agent/providers/claude/task-notification-tool-call.js +272 -0
  104. package/dist/server/server/agent/providers/claude/test-rewind-claude-sdk.d.ts +24 -0
  105. package/dist/server/server/agent/providers/claude/test-rewind-claude-sdk.js +23 -0
  106. package/dist/server/server/agent/providers/claude/tool-call-detail-parser.d.ts +3 -0
  107. package/dist/server/server/agent/providers/claude/tool-call-detail-parser.js +130 -0
  108. package/dist/server/server/agent/providers/claude/tool-call-mapper.d.ts +16 -0
  109. package/dist/server/server/agent/providers/claude/tool-call-mapper.js +129 -0
  110. package/dist/server/server/agent/providers/codex/app-server-transport.d.ts +90 -0
  111. package/dist/server/server/agent/providers/codex/app-server-transport.js +262 -0
  112. package/dist/server/server/agent/providers/codex/rewind.d.ts +21 -0
  113. package/dist/server/server/agent/providers/codex/rewind.js +46 -0
  114. package/dist/server/server/agent/providers/codex/test-utils/fake-app-server.d.ts +36 -0
  115. package/dist/server/server/agent/providers/codex/test-utils/fake-app-server.js +217 -0
  116. package/dist/server/server/agent/providers/codex/tool-call-detail-parser.d.ts +12 -0
  117. package/dist/server/server/agent/providers/codex/tool-call-detail-parser.js +104 -0
  118. package/dist/server/server/agent/providers/codex/tool-call-mapper.d.ts +24 -0
  119. package/dist/server/server/agent/providers/codex/tool-call-mapper.js +830 -0
  120. package/dist/server/server/agent/providers/codex-app-server-agent.d.ts +308 -0
  121. package/dist/server/server/agent/providers/codex-app-server-agent.js +4515 -0
  122. package/dist/server/server/agent/providers/codex-feature-definitions.d.ts +11 -0
  123. package/dist/server/server/agent/providers/codex-feature-definitions.js +45 -0
  124. package/dist/server/server/agent/providers/copilot-acp-agent.d.ts +26 -0
  125. package/dist/server/server/agent/providers/copilot-acp-agent.js +188 -0
  126. package/dist/server/server/agent/providers/cursor-acp-agent.d.ts +15 -0
  127. package/dist/server/server/agent/providers/cursor-acp-agent.js +18 -0
  128. package/dist/server/server/agent/providers/diagnostic-utils.d.ts +36 -0
  129. package/dist/server/server/agent/providers/diagnostic-utils.js +355 -0
  130. package/dist/server/server/agent/providers/generic-acp-agent.d.ts +42 -0
  131. package/dist/server/server/agent/providers/generic-acp-agent.js +160 -0
  132. package/dist/server/server/agent/providers/kiro-acp-agent.d.ts +17 -0
  133. package/dist/server/server/agent/providers/kiro-acp-agent.js +78 -0
  134. package/dist/server/server/agent/providers/mock-load-test-agent.d.ts +85 -0
  135. package/dist/server/server/agent/providers/mock-load-test-agent.js +1010 -0
  136. package/dist/server/server/agent/providers/mock-slow-provider.d.ts +14 -0
  137. package/dist/server/server/agent/providers/mock-slow-provider.js +39 -0
  138. package/dist/server/server/agent/providers/openai-compat-agent.d.ts +422 -0
  139. package/dist/server/server/agent/providers/openai-compat-agent.js +2536 -0
  140. package/dist/server/server/agent/providers/openai-compat-feature-definitions.d.ts +40 -0
  141. package/dist/server/server/agent/providers/openai-compat-feature-definitions.js +82 -0
  142. package/dist/server/server/agent/providers/openai-compat-mcp.d.ts +87 -0
  143. package/dist/server/server/agent/providers/openai-compat-mcp.js +353 -0
  144. package/dist/server/server/agent/providers/openai-compat-otto-tool-permissions.d.ts +29 -0
  145. package/dist/server/server/agent/providers/openai-compat-otto-tool-permissions.js +96 -0
  146. package/dist/server/server/agent/providers/openai-compat-tools.d.ts +69 -0
  147. package/dist/server/server/agent/providers/openai-compat-tools.js +1224 -0
  148. package/dist/server/server/agent/providers/opencode/paths.d.ts +2 -0
  149. package/dist/server/server/agent/providers/opencode/paths.js +7 -0
  150. package/dist/server/server/agent/providers/opencode/rewind.d.ts +18 -0
  151. package/dist/server/server/agent/providers/opencode/rewind.js +14 -0
  152. package/dist/server/server/agent/providers/opencode/server-manager.d.ts +91 -0
  153. package/dist/server/server/agent/providers/opencode/server-manager.js +425 -0
  154. package/dist/server/server/agent/providers/opencode/test-server-manager.d.ts +25 -0
  155. package/dist/server/server/agent/providers/opencode/test-server-manager.js +39 -0
  156. package/dist/server/server/agent/providers/opencode/test-utils/test-opencode-harness.d.ts +92 -0
  157. package/dist/server/server/agent/providers/opencode/test-utils/test-opencode-harness.js +260 -0
  158. package/dist/server/server/agent/providers/opencode/tool-call-detail-parser.d.ts +3 -0
  159. package/dist/server/server/agent/providers/opencode/tool-call-detail-parser.js +256 -0
  160. package/dist/server/server/agent/providers/opencode/tool-call-mapper.d.ts +13 -0
  161. package/dist/server/server/agent/providers/opencode/tool-call-mapper.js +53 -0
  162. package/dist/server/server/agent/providers/opencode-agent.d.ts +286 -0
  163. package/dist/server/server/agent/providers/opencode-agent.js +2830 -0
  164. package/dist/server/server/agent/providers/pi/agent.d.ts +132 -0
  165. package/dist/server/server/agent/providers/pi/agent.js +1649 -0
  166. package/dist/server/server/agent/providers/pi/cli-runtime.d.ts +21 -0
  167. package/dist/server/server/agent/providers/pi/cli-runtime.js +261 -0
  168. package/dist/server/server/agent/providers/pi/history-mapper.d.ts +9 -0
  169. package/dist/server/server/agent/providers/pi/history-mapper.js +140 -0
  170. package/dist/server/server/agent/providers/pi/rewind.d.ts +8 -0
  171. package/dist/server/server/agent/providers/pi/rewind.js +8 -0
  172. package/dist/server/server/agent/providers/pi/rpc-types.d.ts +208 -0
  173. package/dist/server/server/agent/providers/pi/rpc-types.js +2 -0
  174. package/dist/server/server/agent/providers/pi/runtime.d.ts +57 -0
  175. package/dist/server/server/agent/providers/pi/runtime.js +56 -0
  176. package/dist/server/server/agent/providers/pi/session-descriptor.d.ts +16 -0
  177. package/dist/server/server/agent/providers/pi/session-descriptor.js +346 -0
  178. package/dist/server/server/agent/providers/pi/test-utils/fake-pi.d.ts +83 -0
  179. package/dist/server/server/agent/providers/pi/test-utils/fake-pi.js +184 -0
  180. package/dist/server/server/agent/providers/pi/tool-call-mapper.d.ts +112 -0
  181. package/dist/server/server/agent/providers/pi/tool-call-mapper.js +284 -0
  182. package/dist/server/server/agent/providers/provider-image-output.d.ts +25 -0
  183. package/dist/server/server/agent/providers/provider-image-output.js +111 -0
  184. package/dist/server/server/agent/providers/provider-runner.d.ts +27 -0
  185. package/dist/server/server/agent/providers/provider-runner.js +81 -0
  186. package/dist/server/server/agent/providers/test-utils/session-stream-adapter.d.ts +3 -0
  187. package/dist/server/server/agent/providers/test-utils/session-stream-adapter.js +60 -0
  188. package/dist/server/server/agent/providers/tool-call-detail-primitives.d.ts +884 -0
  189. package/dist/server/server/agent/providers/tool-call-detail-primitives.js +778 -0
  190. package/dist/server/server/agent/providers/tool-call-mapper-utils.d.ts +25 -0
  191. package/dist/server/server/agent/providers/tool-call-mapper-utils.js +208 -0
  192. package/dist/server/server/agent/providers/trae-acp-agent.d.ts +15 -0
  193. package/dist/server/server/agent/providers/trae-acp-agent.js +18 -0
  194. package/dist/server/server/agent/recordings-debug.d.ts +3 -0
  195. package/dist/server/server/agent/recordings-debug.js +19 -0
  196. package/dist/server/server/agent/rewind/rewind.d.ts +10 -0
  197. package/dist/server/server/agent/rewind/rewind.js +29 -0
  198. package/dist/server/server/agent/rewind/test-rewind-session.d.ts +48 -0
  199. package/dist/server/server/agent/rewind/test-rewind-session.js +88 -0
  200. package/dist/server/server/agent/runtime-mcp-config.d.ts +14 -0
  201. package/dist/server/server/agent/runtime-mcp-config.js +53 -0
  202. package/dist/server/server/agent/structured-generation-providers.d.ts +29 -0
  203. package/dist/server/server/agent/structured-generation-providers.js +236 -0
  204. package/dist/server/server/agent/stt-debug.d.ts +10 -0
  205. package/dist/server/server/agent/stt-debug.js +33 -0
  206. package/dist/server/server/agent/stt-manager.d.ts +38 -0
  207. package/dist/server/server/agent/stt-manager.js +246 -0
  208. package/dist/server/server/agent/system-prompt.d.ts +2 -0
  209. package/dist/server/server/agent/system-prompt.js +8 -0
  210. package/dist/server/server/agent/timeline-append.d.ts +10 -0
  211. package/dist/server/server/agent/timeline-append.js +27 -0
  212. package/dist/server/server/agent/timeline-projection.d.ts +71 -0
  213. package/dist/server/server/agent/timeline-projection.js +373 -0
  214. package/dist/server/server/agent/tools/otto-tools.d.ts +80 -0
  215. package/dist/server/server/agent/tools/otto-tools.js +2912 -0
  216. package/dist/server/server/agent/tools/types.d.ts +36 -0
  217. package/dist/server/server/agent/tools/types.js +2 -0
  218. package/dist/server/server/agent/tts-debug.d.ts +8 -0
  219. package/dist/server/server/agent/tts-debug.js +24 -0
  220. package/dist/server/server/agent/tts-manager.d.ts +41 -0
  221. package/dist/server/server/agent/tts-manager.js +375 -0
  222. package/dist/server/server/agent-attention-policy.d.ts +29 -0
  223. package/dist/server/server/agent-attention-policy.js +36 -0
  224. package/dist/server/server/artifact/artifact-prompt.d.ts +2 -0
  225. package/dist/server/server/artifact/artifact-prompt.js +15 -0
  226. package/dist/server/server/artifact/artifact-service.d.ts +91 -0
  227. package/dist/server/server/artifact/artifact-service.js +419 -0
  228. package/dist/server/server/artifact/artifact-store.d.ts +24 -0
  229. package/dist/server/server/artifact/artifact-store.js +168 -0
  230. package/dist/server/server/artifact/artifact-watcher.d.ts +34 -0
  231. package/dist/server/server/artifact/artifact-watcher.js +146 -0
  232. package/dist/server/server/artifact/html-validator.d.ts +26 -0
  233. package/dist/server/server/artifact/html-validator.js +92 -0
  234. package/dist/server/server/atomic-file.d.ts +5 -0
  235. package/dist/server/server/atomic-file.js +19 -0
  236. package/dist/server/server/auth.d.ts +38 -0
  237. package/dist/server/server/auth.js +143 -0
  238. package/dist/server/server/auto-archive-on-merge/archive-if-safe.d.ts +40 -0
  239. package/dist/server/server/auto-archive-on-merge/archive-if-safe.js +92 -0
  240. package/dist/server/server/auto-archive-on-merge/index.d.ts +8 -0
  241. package/dist/server/server/auto-archive-on-merge/index.js +15 -0
  242. package/dist/server/server/bootstrap.d.ts +127 -0
  243. package/dist/server/server/bootstrap.js +1331 -0
  244. package/dist/server/server/browser-tools/broker.d.ts +45 -0
  245. package/dist/server/server/browser-tools/broker.js +390 -0
  246. package/dist/server/server/browser-tools/errors.d.ts +22 -0
  247. package/dist/server/server/browser-tools/errors.js +27 -0
  248. package/dist/server/server/browser-tools/policy.d.ts +10 -0
  249. package/dist/server/server/browser-tools/policy.js +16 -0
  250. package/dist/server/server/browser-tools/tools.d.ts +24 -0
  251. package/dist/server/server/browser-tools/tools.js +967 -0
  252. package/dist/server/server/chat/chat-mentions.d.ts +47 -0
  253. package/dist/server/server/chat/chat-mentions.js +113 -0
  254. package/dist/server/server/chat/chat-service.d.ts +78 -0
  255. package/dist/server/server/chat/chat-service.js +337 -0
  256. package/dist/server/server/checkout/status-projection.d.ts +19 -0
  257. package/dist/server/server/checkout/status-projection.js +117 -0
  258. package/dist/server/server/checkout-diff-manager.d.ts +41 -0
  259. package/dist/server/server/checkout-diff-manager.js +189 -0
  260. package/dist/server/server/checkout-git-utils.d.ts +11 -0
  261. package/dist/server/server/checkout-git-utils.js +34 -0
  262. package/dist/server/server/client-message-id.d.ts +3 -0
  263. package/dist/server/server/client-message-id.js +12 -0
  264. package/dist/server/server/config.d.ts +22 -0
  265. package/dist/server/server/config.js +330 -0
  266. package/dist/server/server/connection-offer.d.ts +20 -0
  267. package/dist/server/server/connection-offer.js +59 -0
  268. package/dist/server/server/daemon-config-store.d.ts +44 -0
  269. package/dist/server/server/daemon-config-store.js +530 -0
  270. package/dist/server/server/daemon-keypair.d.ts +8 -0
  271. package/dist/server/server/daemon-keypair.js +42 -0
  272. package/dist/server/server/daemon-version.d.ts +5 -0
  273. package/dist/server/server/daemon-version.js +22 -0
  274. package/dist/server/server/daemon-worker.d.ts +2 -0
  275. package/dist/server/server/daemon-worker.js +251 -0
  276. package/dist/server/server/dictation/dictation-stream-manager.d.ts +87 -0
  277. package/dist/server/server/dictation/dictation-stream-manager.js +572 -0
  278. package/dist/server/server/exports.d.ts +24 -0
  279. package/dist/server/server/exports.js +24 -0
  280. package/dist/server/server/file-download/token-store.d.ts +24 -0
  281. package/dist/server/server/file-download/token-store.js +40 -0
  282. package/dist/server/server/file-explorer/code-index.d.ts +28 -0
  283. package/dist/server/server/file-explorer/code-index.js +127 -0
  284. package/dist/server/server/file-explorer/file-search.d.ts +72 -0
  285. package/dist/server/server/file-explorer/file-search.js +297 -0
  286. package/dist/server/server/file-explorer/file-watcher.d.ts +40 -0
  287. package/dist/server/server/file-explorer/file-watcher.js +0 -0
  288. package/dist/server/server/file-explorer/gitignore.d.ts +39 -0
  289. package/dist/server/server/file-explorer/gitignore.js +191 -0
  290. package/dist/server/server/file-explorer/service.d.ts +103 -0
  291. package/dist/server/server/file-explorer/service.js +420 -0
  292. package/dist/server/server/file-upload/index.d.ts +27 -0
  293. package/dist/server/server/file-upload/index.js +158 -0
  294. package/dist/server/server/hostnames.d.ts +13 -0
  295. package/dist/server/server/hostnames.js +94 -0
  296. package/dist/server/server/json-utils.d.ts +11 -0
  297. package/dist/server/server/json-utils.js +45 -0
  298. package/dist/server/server/lifecycle-reasons.d.ts +4 -0
  299. package/dist/server/server/lifecycle-reasons.js +6 -0
  300. package/dist/server/server/logger.d.ts +29 -0
  301. package/dist/server/server/logger.js +125 -0
  302. package/dist/server/server/loop-service.d.ts +234 -0
  303. package/dist/server/server/loop-service.js +804 -0
  304. package/dist/server/server/managed-processes/managed-processes.d.ts +76 -0
  305. package/dist/server/server/managed-processes/managed-processes.js +326 -0
  306. package/dist/server/server/messages.d.ts +9 -0
  307. package/dist/server/server/messages.js +29 -0
  308. package/dist/server/server/migrations/backfill-workspace-id.migration.d.ts +9 -0
  309. package/dist/server/server/migrations/backfill-workspace-id.migration.js +64 -0
  310. package/dist/server/server/otto-env.d.ts +13 -0
  311. package/dist/server/server/otto-env.js +53 -0
  312. package/dist/server/server/otto-home.d.ts +2 -0
  313. package/dist/server/server/otto-home.js +19 -0
  314. package/dist/server/server/otto-worktree-service.d.ts +52 -0
  315. package/dist/server/server/otto-worktree-service.js +294 -0
  316. package/dist/server/server/package-version.d.ts +19 -0
  317. package/dist/server/server/package-version.js +60 -0
  318. package/dist/server/server/pagination/cursor.d.ts +16 -0
  319. package/dist/server/server/pagination/cursor.js +62 -0
  320. package/dist/server/server/pagination/sortable-pager.d.ts +24 -0
  321. package/dist/server/server/pagination/sortable-pager.js +68 -0
  322. package/dist/server/server/pairing-offer.d.ts +18 -0
  323. package/dist/server/server/pairing-offer.js +47 -0
  324. package/dist/server/server/pairing-qr.d.ts +7 -0
  325. package/dist/server/server/pairing-qr.js +45 -0
  326. package/dist/server/server/path-utils.d.ts +5 -0
  327. package/dist/server/server/path-utils.js +34 -0
  328. package/dist/server/server/persisted-config.d.ts +417 -0
  329. package/dist/server/server/persisted-config.js +471 -0
  330. package/dist/server/server/persistence-hooks.d.ts +31 -0
  331. package/dist/server/server/persistence-hooks.js +93 -0
  332. package/dist/server/server/pid-lock.d.ts +32 -0
  333. package/dist/server/server/pid-lock.js +170 -0
  334. package/dist/server/server/preview/dev-server-manager.d.ts +108 -0
  335. package/dist/server/server/preview/dev-server-manager.js +426 -0
  336. package/dist/server/server/preview/launch-config.d.ts +37 -0
  337. package/dist/server/server/preview/launch-config.js +63 -0
  338. package/dist/server/server/preview/preview-tools.d.ts +30 -0
  339. package/dist/server/server/preview/preview-tools.js +249 -0
  340. package/dist/server/server/private-files.d.ts +6 -0
  341. package/dist/server/server/private-files.js +37 -0
  342. package/dist/server/server/process-diagnostics.d.ts +17 -0
  343. package/dist/server/server/process-diagnostics.js +22 -0
  344. package/dist/server/server/push/notifications.d.ts +9 -0
  345. package/dist/server/server/push/notifications.js +15 -0
  346. package/dist/server/server/push/push-service.d.ts +20 -0
  347. package/dist/server/server/push/push-service.js +66 -0
  348. package/dist/server/server/push/token-store.d.ts +18 -0
  349. package/dist/server/server/push/token-store.js +68 -0
  350. package/dist/server/server/relay-transport.d.ts +31 -0
  351. package/dist/server/server/relay-transport.js +514 -0
  352. package/dist/server/server/resolve-workspace-id-for-path.d.ts +3 -0
  353. package/dist/server/server/resolve-workspace-id-for-path.js +41 -0
  354. package/dist/server/server/resolve-worktree-creation-intent.d.ts +32 -0
  355. package/dist/server/server/resolve-worktree-creation-intent.js +121 -0
  356. package/dist/server/server/schedule/cron.d.ts +4 -0
  357. package/dist/server/server/schedule/cron.js +81 -0
  358. package/dist/server/server/schedule/service.d.ts +89 -0
  359. package/dist/server/server/schedule/service.js +882 -0
  360. package/dist/server/server/schedule/store.d.ts +27 -0
  361. package/dist/server/server/schedule/store.js +190 -0
  362. package/dist/server/server/script-health-monitor.d.ts +39 -0
  363. package/dist/server/server/script-health-monitor.js +159 -0
  364. package/dist/server/server/script-proxy.d.ts +3 -0
  365. package/dist/server/server/script-proxy.js +2 -0
  366. package/dist/server/server/script-route-branch-handler.d.ts +10 -0
  367. package/dist/server/server/script-route-branch-handler.js +11 -0
  368. package/dist/server/server/script-status-projection.d.ts +36 -0
  369. package/dist/server/server/script-status-projection.js +200 -0
  370. package/dist/server/server/server-id.d.ts +17 -0
  371. package/dist/server/server/server-id.js +68 -0
  372. package/dist/server/server/service-proxy.d.ts +237 -0
  373. package/dist/server/server/service-proxy.js +714 -0
  374. package/dist/server/server/session/agent-config/agent-config-session.d.ts +59 -0
  375. package/dist/server/server/session/agent-config/agent-config-session.js +110 -0
  376. package/dist/server/server/session/agent-updates/agent-updates-service.d.ts +65 -0
  377. package/dist/server/server/session/agent-updates/agent-updates-service.js +232 -0
  378. package/dist/server/server/session/artifact/artifact-session.d.ts +45 -0
  379. package/dist/server/server/session/artifact/artifact-session.js +293 -0
  380. package/dist/server/server/session/chat/chat-schedule-loop-session.d.ts +120 -0
  381. package/dist/server/server/session/chat/chat-schedule-loop-session.js +489 -0
  382. package/dist/server/server/session/checkout/checkout-session.d.ts +146 -0
  383. package/dist/server/server/session/checkout/checkout-session.js +1000 -0
  384. package/dist/server/server/session/checkout/git-metadata-generator.d.ts +53 -0
  385. package/dist/server/server/session/checkout/git-metadata-generator.js +159 -0
  386. package/dist/server/server/session/daemon/daemon-self-update-session-controller.d.ts +32 -0
  387. package/dist/server/server/session/daemon/daemon-self-update-session-controller.js +88 -0
  388. package/dist/server/server/session/daemon/daemon-self-updater.d.ts +32 -0
  389. package/dist/server/server/session/daemon/daemon-self-updater.js +56 -0
  390. package/dist/server/server/session/daemon/daemon-session.d.ts +76 -0
  391. package/dist/server/server/session/daemon/daemon-session.js +148 -0
  392. package/dist/server/server/session/daemon/diagnostics.d.ts +41 -0
  393. package/dist/server/server/session/daemon/diagnostics.js +431 -0
  394. package/dist/server/server/session/daemon/install-origin.d.ts +7 -0
  395. package/dist/server/server/session/daemon/install-origin.js +64 -0
  396. package/dist/server/server/session/daemon/npm-global-cli.d.ts +29 -0
  397. package/dist/server/server/session/daemon/npm-global-cli.js +98 -0
  398. package/dist/server/server/session/files/workspace-files-session.d.ts +60 -0
  399. package/dist/server/server/session/files/workspace-files-session.js +518 -0
  400. package/dist/server/server/session/git-mutation/git-mutation-service.d.ts +34 -0
  401. package/dist/server/server/session/git-mutation/git-mutation-service.js +71 -0
  402. package/dist/server/server/session/project-config/project-config-session.d.ts +34 -0
  403. package/dist/server/server/session/project-config/project-config-session.js +125 -0
  404. package/dist/server/server/session/provider/provider-catalog-session.d.ts +74 -0
  405. package/dist/server/server/session/provider/provider-catalog-session.js +343 -0
  406. package/dist/server/server/session/voice/voice-session.d.ts +167 -0
  407. package/dist/server/server/session/voice/voice-session.js +912 -0
  408. package/dist/server/server/session/voice/voice-turn-controller.d.ts +38 -0
  409. package/dist/server/server/session/voice/voice-turn-controller.js +393 -0
  410. package/dist/server/server/session/workspace-git-observer/workspace-git-observer-service.d.ts +36 -0
  411. package/dist/server/server/session/workspace-git-observer/workspace-git-observer-service.js +134 -0
  412. package/dist/server/server/session/workspace-provisioning/workspace-provisioning-service.d.ts +34 -0
  413. package/dist/server/server/session/workspace-provisioning/workspace-provisioning-service.js +190 -0
  414. package/dist/server/server/session/workspace-scripts/workspace-scripts-service.d.ts +41 -0
  415. package/dist/server/server/session/workspace-scripts/workspace-scripts-service.js +100 -0
  416. package/dist/server/server/session.d.ts +450 -0
  417. package/dist/server/server/session.js +4763 -0
  418. package/dist/server/server/speech/audio.d.ts +10 -0
  419. package/dist/server/server/speech/audio.js +101 -0
  420. package/dist/server/server/speech/provider-resolver.d.ts +3 -0
  421. package/dist/server/server/speech/provider-resolver.js +7 -0
  422. package/dist/server/server/speech/providers/local/config.d.ts +30 -0
  423. package/dist/server/server/speech/providers/local/config.js +133 -0
  424. package/dist/server/server/speech/providers/local/models.d.ts +13 -0
  425. package/dist/server/server/speech/providers/local/models.js +21 -0
  426. package/dist/server/server/speech/providers/local/runtime.d.ts +31 -0
  427. package/dist/server/server/speech/providers/local/runtime.js +135 -0
  428. package/dist/server/server/speech/providers/local/sherpa/assets/silero_vad.onnx +0 -0
  429. package/dist/server/server/speech/providers/local/sherpa/model-catalog.d.ts +109 -0
  430. package/dist/server/server/speech/providers/local/sherpa/model-catalog.js +129 -0
  431. package/dist/server/server/speech/providers/local/sherpa/model-downloader.d.ts +16 -0
  432. package/dist/server/server/speech/providers/local/sherpa/model-downloader.js +195 -0
  433. package/dist/server/server/speech/providers/local/sherpa/sherpa-offline-recognizer.d.ts +49 -0
  434. package/dist/server/server/speech/providers/local/sherpa/sherpa-offline-recognizer.js +73 -0
  435. package/dist/server/server/speech/providers/local/sherpa/sherpa-onnx-node-loader.d.ts +9 -0
  436. package/dist/server/server/speech/providers/local/sherpa/sherpa-onnx-node-loader.js +107 -0
  437. package/dist/server/server/speech/providers/local/sherpa/sherpa-parakeet-realtime-session.d.ts +28 -0
  438. package/dist/server/server/speech/providers/local/sherpa/sherpa-parakeet-realtime-session.js +137 -0
  439. package/dist/server/server/speech/providers/local/sherpa/sherpa-parakeet-stt.d.ts +21 -0
  440. package/dist/server/server/speech/providers/local/sherpa/sherpa-parakeet-stt.js +133 -0
  441. package/dist/server/server/speech/providers/local/sherpa/sherpa-runtime-env.d.ts +18 -0
  442. package/dist/server/server/speech/providers/local/sherpa/sherpa-runtime-env.js +84 -0
  443. package/dist/server/server/speech/providers/local/sherpa/sherpa-tts.d.ts +24 -0
  444. package/dist/server/server/speech/providers/local/sherpa/sherpa-tts.js +145 -0
  445. package/dist/server/server/speech/providers/local/sherpa/silero-vad-provider.d.ts +19 -0
  446. package/dist/server/server/speech/providers/local/sherpa/silero-vad-provider.js +49 -0
  447. package/dist/server/server/speech/providers/local/sherpa/silero-vad-session.d.ts +38 -0
  448. package/dist/server/server/speech/providers/local/sherpa/silero-vad-session.js +176 -0
  449. package/dist/server/server/speech/providers/local/sherpa/tts-voices.d.ts +10 -0
  450. package/dist/server/server/speech/providers/local/sherpa/tts-voices.js +86 -0
  451. package/dist/server/server/speech/providers/local/worker-bytes.d.ts +4 -0
  452. package/dist/server/server/speech/providers/local/worker-bytes.js +9 -0
  453. package/dist/server/server/speech/providers/local/worker-client.d.ts +93 -0
  454. package/dist/server/server/speech/providers/local/worker-client.js +591 -0
  455. package/dist/server/server/speech/providers/local/worker-process.d.ts +2 -0
  456. package/dist/server/server/speech/providers/local/worker-process.js +270 -0
  457. package/dist/server/server/speech/providers/local/worker-protocol.d.ts +96 -0
  458. package/dist/server/server/speech/providers/local/worker-protocol.js +2 -0
  459. package/dist/server/server/speech/providers/openai/config.d.ts +19 -0
  460. package/dist/server/server/speech/providers/openai/config.js +160 -0
  461. package/dist/server/server/speech/providers/openai/runtime.d.ts +29 -0
  462. package/dist/server/server/speech/providers/openai/runtime.js +102 -0
  463. package/dist/server/server/speech/providers/openai/stt.d.ts +23 -0
  464. package/dist/server/server/speech/providers/openai/stt.js +209 -0
  465. package/dist/server/server/speech/providers/openai/tts.d.ts +20 -0
  466. package/dist/server/server/speech/providers/openai/tts.js +70 -0
  467. package/dist/server/server/speech/speech-config-resolver.d.ts +11 -0
  468. package/dist/server/server/speech/speech-config-resolver.js +132 -0
  469. package/dist/server/server/speech/speech-provider.d.ts +71 -0
  470. package/dist/server/server/speech/speech-provider.js +2 -0
  471. package/dist/server/server/speech/speech-runtime.d.ts +67 -0
  472. package/dist/server/server/speech/speech-runtime.js +615 -0
  473. package/dist/server/server/speech/speech-settings-options.d.ts +14 -0
  474. package/dist/server/server/speech/speech-settings-options.js +73 -0
  475. package/dist/server/server/speech/speech-types.d.ts +22 -0
  476. package/dist/server/server/speech/speech-types.js +8 -0
  477. package/dist/server/server/speech/turn-detection-provider.d.ts +23 -0
  478. package/dist/server/server/speech/turn-detection-provider.js +2 -0
  479. package/dist/server/server/utils/diff-highlighter.d.ts +57 -0
  480. package/dist/server/server/utils/diff-highlighter.js +279 -0
  481. package/dist/server/server/voice-config.d.ts +15 -0
  482. package/dist/server/server/voice-config.js +54 -0
  483. package/dist/server/server/voice-permission-policy.d.ts +4 -0
  484. package/dist/server/server/voice-permission-policy.js +13 -0
  485. package/dist/server/server/voice-types.d.ts +12 -0
  486. package/dist/server/server/voice-types.js +2 -0
  487. package/dist/server/server/web-ui.d.ts +10 -0
  488. package/dist/server/server/web-ui.js +205 -0
  489. package/dist/server/server/websocket/runtime-metrics.d.ts +94 -0
  490. package/dist/server/server/websocket/runtime-metrics.js +148 -0
  491. package/dist/server/server/websocket-server.d.ts +187 -0
  492. package/dist/server/server/websocket-server.js +1766 -0
  493. package/dist/server/server/workspace-archive-service.d.ts +65 -0
  494. package/dist/server/server/workspace-archive-service.js +229 -0
  495. package/dist/server/server/workspace-auto-name.d.ts +55 -0
  496. package/dist/server/server/workspace-auto-name.js +128 -0
  497. package/dist/server/server/workspace-bootstrap-dedupe.d.ts +34 -0
  498. package/dist/server/server/workspace-bootstrap-dedupe.js +23 -0
  499. package/dist/server/server/workspace-directory.d.ts +93 -0
  500. package/dist/server/server/workspace-directory.js +422 -0
  501. package/dist/server/server/workspace-git-metadata.d.ts +24 -0
  502. package/dist/server/server/workspace-git-metadata.js +55 -0
  503. package/dist/server/server/workspace-git-service.d.ts +270 -0
  504. package/dist/server/server/workspace-git-service.js +1423 -0
  505. package/dist/server/server/workspace-reconciliation-service.d.ts +57 -0
  506. package/dist/server/server/workspace-reconciliation-service.js +278 -0
  507. package/dist/server/server/workspace-registry-bootstrap.d.ts +13 -0
  508. package/dist/server/server/workspace-registry-bootstrap.js +121 -0
  509. package/dist/server/server/workspace-registry-model.d.ts +55 -0
  510. package/dist/server/server/workspace-registry-model.js +190 -0
  511. package/dist/server/server/workspace-registry.d.ts +117 -0
  512. package/dist/server/server/workspace-registry.js +194 -0
  513. package/dist/server/server/workspace-script-runtime-store.d.ts +28 -0
  514. package/dist/server/server/workspace-script-runtime-store.js +78 -0
  515. package/dist/server/server/workspace-service-env.d.ts +18 -0
  516. package/dist/server/server/workspace-service-env.js +85 -0
  517. package/dist/server/server/workspace-service-port-registry.d.ts +19 -0
  518. package/dist/server/server/workspace-service-port-registry.js +59 -0
  519. package/dist/server/server/worktree/commands.d.ts +55 -0
  520. package/dist/server/server/worktree/commands.js +107 -0
  521. package/dist/server/server/worktree-bootstrap.d.ts +76 -0
  522. package/dist/server/server/worktree-bootstrap.js +720 -0
  523. package/dist/server/server/worktree-branch-name-generator.d.ts +35 -0
  524. package/dist/server/server/worktree-branch-name-generator.js +91 -0
  525. package/dist/server/server/worktree-core.d.ts +31 -0
  526. package/dist/server/server/worktree-core.js +105 -0
  527. package/dist/server/server/worktree-errors.d.ts +12 -0
  528. package/dist/server/server/worktree-errors.js +28 -0
  529. package/dist/server/server/worktree-session.d.ts +152 -0
  530. package/dist/server/server/worktree-session.js +466 -0
  531. package/dist/server/services/git-hosting/bitbucket-cloud-service.d.ts +24 -0
  532. package/dist/server/services/git-hosting/bitbucket-cloud-service.js +754 -0
  533. package/dist/server/services/git-hosting/github-hosting-service.d.ts +4 -0
  534. package/dist/server/services/git-hosting/github-hosting-service.js +11 -0
  535. package/dist/server/services/git-hosting/hosting-http-client.d.ts +20 -0
  536. package/dist/server/services/git-hosting/hosting-http-client.js +131 -0
  537. package/dist/server/services/git-hosting/request-cache.d.ts +20 -0
  538. package/dist/server/services/git-hosting/request-cache.js +68 -0
  539. package/dist/server/services/git-hosting/resolver.d.ts +32 -0
  540. package/dist/server/services/git-hosting/resolver.js +152 -0
  541. package/dist/server/services/git-hosting/router.d.ts +4 -0
  542. package/dist/server/services/git-hosting/router.js +105 -0
  543. package/dist/server/services/git-hosting/status-poller.d.ts +39 -0
  544. package/dist/server/services/git-hosting/status-poller.js +149 -0
  545. package/dist/server/services/git-hosting/types.d.ts +53 -0
  546. package/dist/server/services/git-hosting/types.js +75 -0
  547. package/dist/server/services/github-service.d.ts +339 -0
  548. package/dist/server/services/github-service.js +2109 -0
  549. package/dist/server/services/quota-fetcher/manifest.d.ts +4 -0
  550. package/dist/server/services/quota-fetcher/manifest.js +52 -0
  551. package/dist/server/services/quota-fetcher/provider.d.ts +17 -0
  552. package/dist/server/services/quota-fetcher/provider.js +2 -0
  553. package/dist/server/services/quota-fetcher/providers/claude.d.ts +26 -0
  554. package/dist/server/services/quota-fetcher/providers/claude.js +217 -0
  555. package/dist/server/services/quota-fetcher/providers/codex.d.ts +23 -0
  556. package/dist/server/services/quota-fetcher/providers/codex.js +211 -0
  557. package/dist/server/services/quota-fetcher/providers/copilot.d.ts +17 -0
  558. package/dist/server/services/quota-fetcher/providers/copilot.js +75 -0
  559. package/dist/server/services/quota-fetcher/providers/cursor.d.ts +17 -0
  560. package/dist/server/services/quota-fetcher/providers/cursor.js +123 -0
  561. package/dist/server/services/quota-fetcher/providers/grok.d.ts +18 -0
  562. package/dist/server/services/quota-fetcher/providers/grok.js +89 -0
  563. package/dist/server/services/quota-fetcher/providers/kimi.d.ts +20 -0
  564. package/dist/server/services/quota-fetcher/providers/kimi.js +89 -0
  565. package/dist/server/services/quota-fetcher/providers/minimax.d.ts +29 -0
  566. package/dist/server/services/quota-fetcher/providers/minimax.js +227 -0
  567. package/dist/server/services/quota-fetcher/providers/zai.d.ts +17 -0
  568. package/dist/server/services/quota-fetcher/providers/zai.js +58 -0
  569. package/dist/server/services/quota-fetcher/service.d.ts +28 -0
  570. package/dist/server/services/quota-fetcher/service.js +58 -0
  571. package/dist/server/services/quota-fetcher/usage.d.ts +22 -0
  572. package/dist/server/services/quota-fetcher/usage.js +49 -0
  573. package/dist/server/terminal/activity/terminal-activity-tracker.d.ts +20 -0
  574. package/dist/server/terminal/activity/terminal-activity-tracker.js +59 -0
  575. package/dist/server/terminal/agent-hooks/agent-hook-installer.d.ts +62 -0
  576. package/dist/server/terminal/agent-hooks/agent-hook-installer.js +117 -0
  577. package/dist/server/terminal/agent-hooks/claude/claude-settings.d.ts +7 -0
  578. package/dist/server/terminal/agent-hooks/claude/claude-settings.js +88 -0
  579. package/dist/server/terminal/agent-hooks/claude/claude.d.ts +4 -0
  580. package/dist/server/terminal/agent-hooks/claude/claude.js +47 -0
  581. package/dist/server/terminal/agent-hooks/codex/codex-settings.d.ts +7 -0
  582. package/dist/server/terminal/agent-hooks/codex/codex-settings.js +99 -0
  583. package/dist/server/terminal/agent-hooks/codex/codex.d.ts +4 -0
  584. package/dist/server/terminal/agent-hooks/codex/codex.js +30 -0
  585. package/dist/server/terminal/agent-hooks/opencode/opencode-plugin.d.ts +4 -0
  586. package/dist/server/terminal/agent-hooks/opencode/opencode-plugin.js +46 -0
  587. package/dist/server/terminal/agent-hooks/opencode/opencode.d.ts +3 -0
  588. package/dist/server/terminal/agent-hooks/opencode/opencode.js +23 -0
  589. package/dist/server/terminal/agent-hooks/provider-registry.d.ts +24 -0
  590. package/dist/server/terminal/agent-hooks/provider-registry.js +36 -0
  591. package/dist/server/terminal/agent-hooks/terminal-agent-hook-setting.d.ts +10 -0
  592. package/dist/server/terminal/agent-hooks/terminal-agent-hook-setting.js +26 -0
  593. package/dist/server/terminal/shell-integration/zsh/.zshenv +17 -0
  594. package/dist/server/terminal/shell-integration/zsh/otto-integration.zsh +32 -0
  595. package/dist/server/terminal/terminal-capture.d.ts +12 -0
  596. package/dist/server/terminal/terminal-capture.js +43 -0
  597. package/dist/server/terminal/terminal-manager-factory.d.ts +6 -0
  598. package/dist/server/terminal/terminal-manager-factory.js +5 -0
  599. package/dist/server/terminal/terminal-manager.d.ts +78 -0
  600. package/dist/server/terminal/terminal-manager.js +319 -0
  601. package/dist/server/terminal/terminal-output-coalescer.d.ts +34 -0
  602. package/dist/server/terminal/terminal-output-coalescer.js +73 -0
  603. package/dist/server/terminal/terminal-restore.d.ts +18 -0
  604. package/dist/server/terminal/terminal-restore.js +52 -0
  605. package/dist/server/terminal/terminal-session-controller.d.ts +85 -0
  606. package/dist/server/terminal/terminal-session-controller.js +819 -0
  607. package/dist/server/terminal/terminal-ts-loader.mjs +20 -0
  608. package/dist/server/terminal/terminal-worker-process.d.ts +2 -0
  609. package/dist/server/terminal/terminal-worker-process.js +304 -0
  610. package/dist/server/terminal/terminal-worker-protocol.d.ts +119 -0
  611. package/dist/server/terminal/terminal-worker-protocol.js +2 -0
  612. package/dist/server/terminal/terminal.d.ts +151 -0
  613. package/dist/server/terminal/terminal.js +1155 -0
  614. package/dist/server/terminal/worker-terminal-manager.d.ts +20 -0
  615. package/dist/server/terminal/worker-terminal-manager.js +688 -0
  616. package/dist/server/utils/build-metadata-prompt.d.ts +19 -0
  617. package/dist/server/utils/build-metadata-prompt.js +29 -0
  618. package/dist/server/utils/checkout-git.d.ts +253 -0
  619. package/dist/server/utils/checkout-git.js +2241 -0
  620. package/dist/server/utils/directory-suggestions.d.ts +26 -0
  621. package/dist/server/utils/directory-suggestions.js +830 -0
  622. package/dist/server/utils/git-rev-parse-path.d.ts +3 -0
  623. package/dist/server/utils/git-rev-parse-path.js +21 -0
  624. package/dist/server/utils/github-remote.d.ts +9 -0
  625. package/dist/server/utils/github-remote.js +62 -0
  626. package/dist/server/utils/otto-config-file.d.ts +30 -0
  627. package/dist/server/utils/otto-config-file.js +90 -0
  628. package/dist/server/utils/path.d.ts +18 -0
  629. package/dist/server/utils/path.js +123 -0
  630. package/dist/server/utils/project-icon.d.ts +39 -0
  631. package/dist/server/utils/project-icon.js +364 -0
  632. package/dist/server/utils/promise-timeout.d.ts +9 -0
  633. package/dist/server/utils/promise-timeout.js +25 -0
  634. package/dist/server/utils/run-git-command.d.ts +37 -0
  635. package/dist/server/utils/run-git-command.js +232 -0
  636. package/dist/server/utils/spawn.d.ts +25 -0
  637. package/dist/server/utils/spawn.js +59 -0
  638. package/dist/server/utils/string-command-shell.d.ts +13 -0
  639. package/dist/server/utils/string-command-shell.js +37 -0
  640. package/dist/server/utils/tree-kill.d.ts +18 -0
  641. package/dist/server/utils/tree-kill.js +77 -0
  642. package/dist/server/utils/windows-command.d.ts +15 -0
  643. package/dist/server/utils/windows-command.js +41 -0
  644. package/dist/server/utils/worktree-metadata.d.ts +39 -0
  645. package/dist/server/utils/worktree-metadata.js +174 -0
  646. package/dist/server/utils/worktree.d.ts +205 -0
  647. package/dist/server/utils/worktree.js +1120 -0
  648. package/dist/server/web-ui/_expo/static/css/xterm-3bb1704bf6cb0876640973dc0244b4cb.css +1 -0
  649. package/dist/server/web-ui/_expo/static/css/xterm-3bb1704bf6cb0876640973dc0244b4cb.css.br +0 -0
  650. package/dist/server/web-ui/_expo/static/css/xterm-3bb1704bf6cb0876640973dc0244b4cb.css.gz +0 -0
  651. package/dist/server/web-ui/_expo/static/js/web/desktop-attachment-bridge-a25c6055b348629689f75618db2c83e1.js +1 -0
  652. package/dist/server/web-ui/_expo/static/js/web/desktop-attachment-bridge-a25c6055b348629689f75618db2c83e1.js.br +0 -0
  653. package/dist/server/web-ui/_expo/static/js/web/desktop-attachment-bridge-a25c6055b348629689f75618db2c83e1.js.gz +0 -0
  654. package/dist/server/web-ui/_expo/static/js/web/desktop-attachment-store-7dc26ea1611eb9d7c2971a6745e5bf5c.js +1 -0
  655. package/dist/server/web-ui/_expo/static/js/web/desktop-attachment-store-7dc26ea1611eb9d7c2971a6745e5bf5c.js.br +0 -0
  656. package/dist/server/web-ui/_expo/static/js/web/desktop-attachment-store-7dc26ea1611eb9d7c2971a6745e5bf5c.js.gz +0 -0
  657. package/dist/server/web-ui/_expo/static/js/web/index-ea91f496cee180fa5bf308c8cc177646.js +3199 -0
  658. package/dist/server/web-ui/_expo/static/js/web/index-ea91f496cee180fa5bf308c8cc177646.js.br +0 -0
  659. package/dist/server/web-ui/_expo/static/js/web/index-ea91f496cee180fa5bf308c8cc177646.js.gz +0 -0
  660. package/dist/server/web-ui/_expo/static/js/web/indexeddb-attachment-store-b6b853a967343babf305512fe8c5958c.js +1 -0
  661. package/dist/server/web-ui/_expo/static/js/web/indexeddb-attachment-store-b6b853a967343babf305512fe8c5958c.js.br +0 -0
  662. package/dist/server/web-ui/_expo/static/js/web/indexeddb-attachment-store-b6b853a967343babf305512fe8c5958c.js.gz +0 -0
  663. package/dist/server/web-ui/_expo/static/js/web/native-file-attachment-store-17223e158406802adc686d02d946573d.js +3 -0
  664. package/dist/server/web-ui/_expo/static/js/web/native-file-attachment-store-17223e158406802adc686d02d946573d.js.br +0 -0
  665. package/dist/server/web-ui/_expo/static/js/web/native-file-attachment-store-17223e158406802adc686d02d946573d.js.gz +0 -0
  666. package/dist/server/web-ui/apple-touch-icon.png +0 -0
  667. package/dist/server/web-ui/assets/__node_modules/@expo-google-fonts/inter/400Regular/Inter_400Regular.51b6ad87261f18b6433ec52871ddfabc.ttf +0 -0
  668. package/dist/server/web-ui/assets/__node_modules/@expo-google-fonts/jetbrains-mono/400Regular/JetBrainsMono_400Regular.a0147b5ab9e4946e81879aef45313def.ttf +0 -0
  669. package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/back-icon-mask.0a328cd9c1afd0afe8e3b1ec5165b1b4.png +0 -0
  670. package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/back-icon.35ba0eaec5a4f5ed12ca16fabeae451d.png +0 -0
  671. package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/clear-icon.c94f6478e7ae0cdd9f15de1fcb9e5e55.png +0 -0
  672. package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/clear-icon.c94f6478e7ae0cdd9f15de1fcb9e5e55@2x.png +0 -0
  673. package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/clear-icon.c94f6478e7ae0cdd9f15de1fcb9e5e55@3x.png +0 -0
  674. package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/clear-icon.c94f6478e7ae0cdd9f15de1fcb9e5e55@4x.png +0 -0
  675. package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/close-icon.808e1b1b9b53114ec2838071a7e6daa7.png +0 -0
  676. package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/close-icon.808e1b1b9b53114ec2838071a7e6daa7@2x.png +0 -0
  677. package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/close-icon.808e1b1b9b53114ec2838071a7e6daa7@3x.png +0 -0
  678. package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/close-icon.808e1b1b9b53114ec2838071a7e6daa7@4x.png +0 -0
  679. package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/search-icon.286d67d3f74808a60a78d3ebf1a5fb57.png +0 -0
  680. package/dist/server/web-ui/assets/__node_modules/expo-router/assets/arrow_down.017bc6ba3fc25503e5eb5e53826d48a8.png +0 -0
  681. package/dist/server/web-ui/assets/__node_modules/expo-router/assets/error.d1ea1496f9057eb392d5bbf3732a61b7.png +0 -0
  682. package/dist/server/web-ui/assets/__node_modules/expo-router/assets/file.19eeb73b9593a38f8e9f418337fc7d10.png +0 -0
  683. package/dist/server/web-ui/assets/__node_modules/expo-router/assets/forward.d8b800c443b8972542883e0b9de2bdc6.png +0 -0
  684. package/dist/server/web-ui/assets/__node_modules/expo-router/assets/pkg.ab19f4cbc543357183a20571f68380a3.png +0 -0
  685. package/dist/server/web-ui/assets/__node_modules/expo-router/assets/sitemap.412dd9275b6b48ad28f5e3d81bb1f626.png +0 -0
  686. package/dist/server/web-ui/assets/__node_modules/expo-router/assets/unmatched.20e71bdf79e3a97bf55fd9e164041578.png +0 -0
  687. package/dist/server/web-ui/assets/assets/images/editor-apps/antigravity.6e91a685c33435e0b466a56db86cf141.png +0 -0
  688. package/dist/server/web-ui/assets/assets/images/editor-apps/clion.baa1f4eb572e962a6117bdbee95749e4.png +0 -0
  689. package/dist/server/web-ui/assets/assets/images/editor-apps/cursor.c31d6bce4fe9aadc3fe59962f4c4fcf3.png +0 -0
  690. package/dist/server/web-ui/assets/assets/images/editor-apps/finder.7f68fc2c475621a672e1be09309d5567.png +0 -0
  691. package/dist/server/web-ui/assets/assets/images/editor-apps/goland.0796ab2b9be2637fd1cdcde891fad3ca.png +0 -0
  692. package/dist/server/web-ui/assets/assets/images/editor-apps/intellij-idea.33bc9ff9c0815d8ad43f618c8c244401.png +0 -0
  693. package/dist/server/web-ui/assets/assets/images/editor-apps/phpstorm.c60b33bc7a8309143d232e14b76554c5.png +0 -0
  694. package/dist/server/web-ui/assets/assets/images/editor-apps/pycharm.88f25ac484989c45c25323e0adccfa9f.png +0 -0
  695. package/dist/server/web-ui/assets/assets/images/editor-apps/rider.1a1091dc3a0ede0461f0aa38a54f080a.png +0 -0
  696. package/dist/server/web-ui/assets/assets/images/editor-apps/rubymine.0e5b66fd7d43f8cbac0f05bba97c9a21.png +0 -0
  697. package/dist/server/web-ui/assets/assets/images/editor-apps/rustrover.3590ef05482369f41d3484c90272570d.png +0 -0
  698. package/dist/server/web-ui/assets/assets/images/editor-apps/vscode.832bdb4c685d930f1c864c793703600b.png +0 -0
  699. package/dist/server/web-ui/assets/assets/images/editor-apps/webstorm.aa5dc2cd8c20cc0a155c4c5c5ab3c5f5.png +0 -0
  700. package/dist/server/web-ui/assets/assets/images/editor-apps/zed.f3a670b7f9aa226da4fe53fb86f1abbd.png +0 -0
  701. package/dist/server/web-ui/assets/assets/images/favicon-dark-attention.3b2c331d11122d8f743c73a5878593be.png +0 -0
  702. package/dist/server/web-ui/assets/assets/images/favicon-dark-running.6077c2ab71021b31c82a42e0b42ad837.png +0 -0
  703. package/dist/server/web-ui/assets/assets/images/favicon-dark.12984efe6d435a13794a74cd2f858dcd.png +0 -0
  704. package/dist/server/web-ui/assets/assets/images/favicon-light-attention.3b2c331d11122d8f743c73a5878593be.png +0 -0
  705. package/dist/server/web-ui/assets/assets/images/favicon-light-running.6077c2ab71021b31c82a42e0b42ad837.png +0 -0
  706. package/dist/server/web-ui/assets/assets/images/favicon-light.12984efe6d435a13794a74cd2f858dcd.png +0 -0
  707. package/dist/server/web-ui/assets/assets/images/notification-icon.2394c3b427c0f5ee2bb84864d8fb0f45.png +0 -0
  708. package/dist/server/web-ui/favicon.ico +0 -0
  709. package/dist/server/web-ui/index.html +147 -0
  710. package/dist/server/web-ui/index.html.br +0 -0
  711. package/dist/server/web-ui/index.html.gz +0 -0
  712. package/dist/server/web-ui/manifest.json +27 -0
  713. package/dist/server/web-ui/manifest.json.br +0 -0
  714. package/dist/server/web-ui/manifest.json.gz +0 -0
  715. package/dist/server/web-ui/metadata.json +1 -0
  716. package/dist/server/web-ui/metadata.json.br +1 -0
  717. package/dist/server/web-ui/metadata.json.gz +0 -0
  718. package/dist/server/web-ui/pwa-icon-192.png +0 -0
  719. package/dist/server/web-ui/pwa-icon-512.png +0 -0
  720. package/dist/server/web-ui/robots.txt +2 -0
  721. package/dist/src/executable-resolution/executable-resolution.js +118 -0
  722. package/dist/src/executable-resolution/windows.js +65 -0
  723. package/dist/src/server/agent/provider-launch-config.js +167 -0
  724. package/dist/src/server/otto-env.js +53 -0
  725. package/dist/src/server/otto-home.js +19 -0
  726. package/dist/src/server/persisted-config.js +471 -0
  727. package/dist/src/server/pid-lock.js +170 -0
  728. package/dist/src/server/private-files.js +37 -0
  729. package/dist/src/server/speech/providers/local/sherpa/sherpa-runtime-env.js +84 -0
  730. package/dist/src/terminal/shell-integration/zsh/.zshenv +17 -0
  731. package/dist/src/terminal/shell-integration/zsh/otto-integration.zsh +32 -0
  732. package/dist/src/utils/spawn.js +59 -0
  733. package/dist/src/utils/windows-command.js +41 -0
  734. package/package.json +118 -0
@@ -0,0 +1,1331 @@
1
+ import express from "express";
2
+ import { createServer as createHTTPServer } from "http";
3
+ import { constants, existsSync, unlinkSync } from "fs";
4
+ import { open } from "fs/promises";
5
+ import { randomUUID } from "node:crypto";
6
+ import { hostname as getHostname } from "node:os";
7
+ import path from "node:path";
8
+ import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
9
+ import { z } from "zod";
10
+ import { createBranchChangeRouteHandler } from "./script-route-branch-handler.js";
11
+ function resolveBoundListenTarget(listenTarget, httpServer) {
12
+ if (listenTarget.type !== "tcp") {
13
+ return listenTarget;
14
+ }
15
+ const address = httpServer.address();
16
+ if (!address || typeof address === "string") {
17
+ throw new Error("HTTP server did not expose a TCP address after listening");
18
+ }
19
+ return {
20
+ type: "tcp",
21
+ host: listenTarget.host,
22
+ port: address.port,
23
+ };
24
+ }
25
+ // Matches a Windows drive-letter path like C:\ or D:\
26
+ const WINDOWS_DRIVE_RE = /^[A-Za-z]:\\/;
27
+ export function parseListenString(listen) {
28
+ // 1. Windows named pipes: \\.\pipe\... or pipe://...
29
+ if (listen.startsWith("\\\\.\\pipe\\") || listen.startsWith("pipe://")) {
30
+ return {
31
+ type: "pipe",
32
+ path: listen.startsWith("pipe://") ? listen.slice("pipe://".length) : listen,
33
+ };
34
+ }
35
+ // 2. Explicit unix:// prefix
36
+ if (listen.startsWith("unix://")) {
37
+ return { type: "socket", path: listen.slice(7) };
38
+ }
39
+ // 3. Reject Windows absolute drive paths — they are not Unix sockets
40
+ if (WINDOWS_DRIVE_RE.test(listen)) {
41
+ throw new Error(`Invalid listen string (Windows path is not a valid listen target): ${listen}`);
42
+ }
43
+ // 4. POSIX absolute path (/ or ~) — Unix socket
44
+ if (listen.startsWith("/") || listen.startsWith("~")) {
45
+ return { type: "socket", path: listen };
46
+ }
47
+ // 5. Pure numeric — TCP port on 127.0.0.1
48
+ const trimmed = listen.trim();
49
+ if (/^\d+$/.test(trimmed)) {
50
+ const port = parseInt(trimmed, 10);
51
+ return { type: "tcp", host: "127.0.0.1", port };
52
+ }
53
+ // 6. host:port — TCP
54
+ if (listen.includes(":")) {
55
+ const lastColonIdx = listen.lastIndexOf(":");
56
+ const host = listen.slice(0, lastColonIdx);
57
+ const portStr = listen.slice(lastColonIdx + 1);
58
+ const parsedPort = parseInt(portStr, 10);
59
+ if (!Number.isFinite(parsedPort)) {
60
+ throw new Error(`Invalid port in listen string: ${listen}`);
61
+ }
62
+ const cleanHost = host.startsWith("[") && host.endsWith("]") ? host.slice(1, -1) : host;
63
+ return { type: "tcp", host: cleanHost || "127.0.0.1", port: parsedPort };
64
+ }
65
+ throw new Error(`Invalid listen string: ${listen}`);
66
+ }
67
+ function formatListenTarget(listenTarget) {
68
+ if (!listenTarget) {
69
+ return null;
70
+ }
71
+ if (listenTarget.type === "tcp") {
72
+ return `${listenTarget.host}:${listenTarget.port}`;
73
+ }
74
+ return listenTarget.path;
75
+ }
76
+ import { VoiceAssistantWebSocketServer } from "./websocket-server.js";
77
+ import { createGitHubHostingService } from "../services/git-hosting/github-hosting-service.js";
78
+ import { createGitHostingResolver } from "../services/git-hosting/resolver.js";
79
+ import { createGitHostingRouter } from "../services/git-hosting/router.js";
80
+ import { readOttoConfigJson } from "../utils/otto-config-file.js";
81
+ import { createOttoWorktree as createRegisteredOttoWorktree, createLocalCheckoutWorkspace, } from "./otto-worktree-service.js";
82
+ import { createOttoWorktreeWorkflow } from "./worktree-session.js";
83
+ import { DownloadTokenStore } from "./file-download/token-store.js";
84
+ import { createSpeechService } from "./speech/speech-runtime.js";
85
+ import { AgentManager } from "./agent/agent-manager.js";
86
+ import { AgentStorage } from "./agent/agent-storage.js";
87
+ import { PersonalityStatsStore } from "./agent/personality-stats-store.js";
88
+ import { attachAgentStoragePersistence } from "./persistence-hooks.js";
89
+ import { createAgentMcpServer } from "./agent/mcp-server.js";
90
+ import { createOttoToolCatalog } from "./agent/tools/otto-tools.js";
91
+ import { ArtifactService } from "./artifact/artifact-service.js";
92
+ import { DevServerManager } from "./preview/dev-server-manager.js";
93
+ import { ProviderSnapshotManager } from "./agent/provider-snapshot-manager.js";
94
+ import { bootstrapWorkspaceRegistries } from "./workspace-registry-bootstrap.js";
95
+ import { WorkspaceReconciliationService } from "./workspace-reconciliation-service.js";
96
+ import { FileBackedProjectRegistry, FileBackedWorkspaceRegistry } from "./workspace-registry.js";
97
+ import { FileBackedChatService } from "./chat/chat-service.js";
98
+ import { CheckoutDiffManager } from "./checkout-diff-manager.js";
99
+ import { LoopService } from "./loop-service.js";
100
+ import { ScheduleService } from "./schedule/service.js";
101
+ import { DaemonConfigStore } from "./daemon-config-store.js";
102
+ import { BrowserToolsBroker } from "./browser-tools/broker.js";
103
+ import { DaemonConfigBrowserToolsPolicy } from "./browser-tools/policy.js";
104
+ import { WorkspaceGitServiceImpl } from "./workspace-git-service.js";
105
+ import { resolveWorkspaceIdForPath } from "./resolve-workspace-id-for-path.js";
106
+ import { archiveByScope, archivePersistedWorkspaceRecord, killTerminalsForWorkspace, } from "./workspace-archive-service.js";
107
+ import { setupAutoArchiveOnMerge } from "./auto-archive-on-merge/index.js";
108
+ import { wrapSessionMessage } from "./messages.js";
109
+ import { createConfiguredTerminalManager } from "../terminal/terminal-manager-factory.js";
110
+ import { applyTerminalAgentHookSetting } from "../terminal/agent-hooks/terminal-agent-hook-setting.js";
111
+ import { createConnectionOfferV2, encodeOfferToFragmentUrl } from "./connection-offer.js";
112
+ import { loadOrCreateDaemonKeyPair } from "./daemon-keypair.js";
113
+ import { startRelayTransport } from "./relay-transport.js";
114
+ import { getOrCreateServerId } from "./server-id.js";
115
+ import { resolveDaemonVersion } from "./daemon-version.js";
116
+ import { DEFAULT_AGENT_PERSONALITIES } from "@otto-code/protocol/default-personalities";
117
+ import { loadPersistedConfig, } from "./persisted-config.js";
118
+ import { resolveSpeechConfig } from "./speech/speech-config-resolver.js";
119
+ import { DEFAULT_LOCAL_STT_MODEL, DEFAULT_LOCAL_TTS_MODEL, getLocalTtsDefaultSpeakerId, resolveLocalTtsVoiceName, } from "./speech/providers/local/models.js";
120
+ import { createServiceProxySubsystem } from "./service-proxy.js";
121
+ import { ScriptHealthMonitor } from "./script-health-monitor.js";
122
+ import { createScriptStatusEmitter } from "./script-status-projection.js";
123
+ import { WorkspaceScriptRuntimeStore } from "./workspace-script-runtime-store.js";
124
+ import { createManagedProcessRegistry, createSystemManagedProcessTable, } from "./managed-processes/managed-processes.js";
125
+ import { terminateWithTreeKill } from "../utils/tree-kill.js";
126
+ import { isHostnameAllowed } from "./hostnames.js";
127
+ import { createRequireBearerMiddleware, isAgentMcpRequestAuthorized, } from "./auth.js";
128
+ import { createWebUiMiddleware } from "./web-ui.js";
129
+ import { WorkspaceAutoName } from "./workspace-auto-name.js";
130
+ import { createGitMutationService } from "./session/git-mutation/git-mutation-service.js";
131
+ import { workspaceIdsOnCheckout } from "./workspace-directory.js";
132
+ import { resolveFirstAgentPromptTitle } from "./agent/create-agent-title.js";
133
+ import { createAgentCommand, } from "./agent/create-agent/create.js";
134
+ const MAX_MCP_DEBUG_BATCH_ITEMS = 10;
135
+ const REDACTED_LOG_VALUE = "[redacted]";
136
+ const DOWNLOAD_OPEN_FLAGS = process.platform === "win32" ? constants.O_RDONLY : constants.O_RDONLY | constants.O_NOFOLLOW;
137
+ function formatHostForHttpUrl(host) {
138
+ return host.includes(":") && !host.startsWith("[") ? `[${host}]` : host;
139
+ }
140
+ function resolveAgentMcpClientHost(host) {
141
+ if (host === "0.0.0.0") {
142
+ return "127.0.0.1";
143
+ }
144
+ if (host === "::" || host === "[::]") {
145
+ return "::1";
146
+ }
147
+ return host;
148
+ }
149
+ function createAgentMcpBaseUrl(listenTarget) {
150
+ if (!listenTarget || listenTarget.type !== "tcp") {
151
+ return null;
152
+ }
153
+ const host = resolveAgentMcpClientHost(listenTarget.host);
154
+ return new URL("/mcp/agents", `http://${formatHostForHttpUrl(host)}:${listenTarget.port}`).toString();
155
+ }
156
+ function createTerminalActivityUrl(listenTarget) {
157
+ if (!listenTarget || listenTarget.type !== "tcp") {
158
+ return null;
159
+ }
160
+ const host = resolveAgentMcpClientHost(listenTarget.host);
161
+ return new URL("/api/terminal-activity", `http://${formatHostForHttpUrl(host)}:${listenTarget.port}`).toString();
162
+ }
163
+ const TerminalActivityReportSchema = z.object({
164
+ terminalId: z.string().min(1),
165
+ token: z.string().min(1),
166
+ state: z.enum(["running", "idle", "needs-input"]),
167
+ });
168
+ const TERMINAL_ACTIVITY_STATE_MAP = {
169
+ running: "working",
170
+ idle: "idle",
171
+ "needs-input": "attention",
172
+ };
173
+ const LOOPBACK_REMOTE_ADDRESSES = new Set(["127.0.0.1", "::1", "::ffff:127.0.0.1"]);
174
+ function isLoopbackRemoteAddress(remoteAddress) {
175
+ return remoteAddress !== undefined && LOOPBACK_REMOTE_ADDRESSES.has(remoteAddress);
176
+ }
177
+ export function createTerminalActivityRouteHandler(terminalManager) {
178
+ return async (req, res) => {
179
+ if (!isLoopbackRemoteAddress(req.socket.remoteAddress)) {
180
+ res.status(403).json({ error: "Forbidden" });
181
+ return;
182
+ }
183
+ const parsed = TerminalActivityReportSchema.safeParse(req.body);
184
+ if (!parsed.success) {
185
+ res.status(400).json({ error: "Invalid terminal activity report" });
186
+ return;
187
+ }
188
+ const validation = terminalManager.validateTerminalActivityToken(parsed.data.terminalId, parsed.data.token);
189
+ if (validation !== "valid") {
190
+ res.status(403).json({ error: "Forbidden" });
191
+ return;
192
+ }
193
+ try {
194
+ const updated = await terminalManager.setTerminalActivity(parsed.data.terminalId, TERMINAL_ACTIVITY_STATE_MAP[parsed.data.state]);
195
+ if (!updated) {
196
+ res.status(403).json({ error: "Forbidden" });
197
+ return;
198
+ }
199
+ res.status(204).end();
200
+ }
201
+ catch {
202
+ res.status(500).json({ error: "Failed to update terminal activity" });
203
+ }
204
+ };
205
+ }
206
+ function summarizeAgentMcpDebugMessage(body) {
207
+ if (!body || typeof body !== "object" || Array.isArray(body)) {
208
+ return {
209
+ type: body === null ? "null" : typeof body,
210
+ };
211
+ }
212
+ const record = body;
213
+ const method = typeof record.method === "string" ? record.method : undefined;
214
+ return {
215
+ type: "object",
216
+ ...(typeof record.jsonrpc === "string" ? { jsonrpc: record.jsonrpc } : {}),
217
+ ...(method ? { method } : {}),
218
+ hasId: Object.prototype.hasOwnProperty.call(record, "id"),
219
+ hasParams: Object.prototype.hasOwnProperty.call(record, "params"),
220
+ };
221
+ }
222
+ function summarizeAgentMcpDebugBody(body) {
223
+ if (!Array.isArray(body)) {
224
+ return summarizeAgentMcpDebugMessage(body);
225
+ }
226
+ const messages = body.slice(0, MAX_MCP_DEBUG_BATCH_ITEMS).map(summarizeAgentMcpDebugMessage);
227
+ return {
228
+ type: "batch",
229
+ count: body.length,
230
+ messages,
231
+ ...(body.length > messages.length ? { omitted: body.length - messages.length } : {}),
232
+ };
233
+ }
234
+ function createBootstrapManagedProcessRegistry(config, logger) {
235
+ if (config.managedProcesses) {
236
+ return config.managedProcesses;
237
+ }
238
+ return createManagedProcessRegistry({
239
+ ottoHome: config.ottoHome,
240
+ processTable: createSystemManagedProcessTable(),
241
+ terminateProcess: terminateWithTreeKill,
242
+ logger,
243
+ });
244
+ }
245
+ async function reconcileManagedProcessLedger(managedProcesses, logger) {
246
+ const reapResult = await managedProcesses.reapStale();
247
+ if (reapResult.checked > 0 || reapResult.errors.length > 0) {
248
+ logger.info(reapResult, "Managed helper process ledger reconciled");
249
+ }
250
+ }
251
+ function mountWebUi(app, config, logger) {
252
+ app.use(createWebUiMiddleware({
253
+ enabled: config.webUi?.enabled ?? false,
254
+ distDir: config.webUi?.distDir ?? null,
255
+ label: getHostname(),
256
+ logger,
257
+ }));
258
+ }
259
+ function resolveExpressTrustProxySetting(config) {
260
+ return config.trustedProxies ?? ["loopback"];
261
+ }
262
+ function initialMutableSttConfig(input) {
263
+ const model = input.provider === "local" ? input.localModel : input.openaiModel;
264
+ return {
265
+ provider: input.provider,
266
+ ...(model ? { model } : {}),
267
+ ...(input.language ? { language: input.language } : {}),
268
+ };
269
+ }
270
+ function initialMutableTtsConfig(input) {
271
+ if (input.provider !== "local") {
272
+ return {
273
+ provider: input.provider,
274
+ ...(input.openai.model ? { model: input.openai.model } : {}),
275
+ ...(input.openai.voice ? { voice: input.openai.voice } : {}),
276
+ };
277
+ }
278
+ return {
279
+ provider: input.provider,
280
+ model: input.local.model,
281
+ ...(input.local.voice ? { voice: input.local.voice } : {}),
282
+ ...(input.local.speed !== undefined ? { speed: input.local.speed } : {}),
283
+ };
284
+ }
285
+ function collectInitialMutableSpeechInputs(config) {
286
+ return {
287
+ requested: config.speech?.providers,
288
+ localModels: config.speech?.local?.models,
289
+ openai: config.openai,
290
+ languages: {
291
+ dictation: config.speech?.sttLanguages?.dictation,
292
+ voice: config.speech?.sttLanguages?.voice,
293
+ },
294
+ };
295
+ }
296
+ function initialMutableDictationSection(inputs) {
297
+ return {
298
+ enabled: inputs.requested?.dictationStt.enabled !== false,
299
+ stt: initialMutableSttConfig({
300
+ provider: inputs.requested?.dictationStt.provider ?? "local",
301
+ localModel: inputs.localModels?.dictationStt ?? DEFAULT_LOCAL_STT_MODEL,
302
+ openaiModel: inputs.openai?.stt?.model,
303
+ language: inputs.languages.dictation,
304
+ }),
305
+ };
306
+ }
307
+ function resolveInitialLocalTtsSelection(localModels) {
308
+ const model = localModels?.voiceTts ?? DEFAULT_LOCAL_TTS_MODEL;
309
+ const voice = resolveLocalTtsVoiceName(model, localModels?.voiceTtsSpeakerId ?? getLocalTtsDefaultSpeakerId(model));
310
+ return { model, voice, speed: localModels?.voiceTtsSpeed };
311
+ }
312
+ function initialMutableVoiceModeSection(inputs) {
313
+ const voiceTtsEnabled = inputs.requested?.voiceTts.enabled !== false;
314
+ const voiceSttEnabled = inputs.requested?.voiceStt.enabled !== false;
315
+ return {
316
+ enabled: voiceTtsEnabled || voiceSttEnabled,
317
+ stt: initialMutableSttConfig({
318
+ provider: inputs.requested?.voiceStt.provider ?? "local",
319
+ localModel: inputs.localModels?.voiceStt ?? DEFAULT_LOCAL_STT_MODEL,
320
+ openaiModel: inputs.openai?.stt?.model,
321
+ language: inputs.languages.voice,
322
+ }),
323
+ tts: initialMutableTtsConfig({
324
+ provider: inputs.requested?.voiceTts.provider ?? "local",
325
+ local: resolveInitialLocalTtsSelection(inputs.localModels),
326
+ openai: { model: inputs.openai?.tts?.model, voice: inputs.openai?.tts?.voice },
327
+ }),
328
+ };
329
+ }
330
+ // The mutable speech section mirrors the resolved speech config so clients see
331
+ // effective values (defaults included), not just what config.json happens to pin.
332
+ function createInitialMutableSpeechConfig(config) {
333
+ const inputs = collectInitialMutableSpeechInputs(config);
334
+ // Only the key stored in config.json is echoed to clients — an env-only key
335
+ // (OPENAI_API_KEY) stays out of the mutable config so unrelated speech
336
+ // patches can never copy it onto disk.
337
+ const persistedOpenAiApiKey = loadPersistedConfig(config.ottoHome).providers?.openai?.apiKey;
338
+ return {
339
+ dictation: initialMutableDictationSection(inputs),
340
+ voiceMode: initialMutableVoiceModeSection(inputs),
341
+ ...(persistedOpenAiApiKey ? { openai: { apiKey: persistedOpenAiApiKey } } : {}),
342
+ };
343
+ }
344
+ function createInitialMutableDaemonConfig(config) {
345
+ const providers = Object.fromEntries(Object.entries(config.providerOverrides ?? {}).map(([providerId, override]) => {
346
+ // Carry the full override so clients can inspect and edit custom
347
+ // provider config (extends, env, models). The provider config schema is
348
+ // passthrough, and persistence re-validates via ProviderOverrideSchema.
349
+ const providerConfig = { ...override };
350
+ return [providerId, providerConfig];
351
+ }));
352
+ // Per-project git hosting credentials round-trip config.json ⇄ mutable
353
+ // config the same way the speech OpenAI key does.
354
+ const persistedGitHosting = loadPersistedConfig(config.ottoHome).gitHosting;
355
+ const initialConfig = {
356
+ mcp: { injectIntoAgents: config.mcpInjectIntoAgents ?? true },
357
+ browserTools: { enabled: config.browserToolsEnabled ?? false },
358
+ providers,
359
+ metadataGeneration: {
360
+ providers: config.metadataGeneration?.providers ?? [],
361
+ },
362
+ autoArchiveAfterMerge: config.autoArchiveAfterMerge ?? false,
363
+ enableTerminalAgentHooks: config.enableTerminalAgentHooks ?? false,
364
+ appendSystemPrompt: config.appendSystemPrompt ?? "",
365
+ speech: createInitialMutableSpeechConfig(config),
366
+ ...(persistedGitHosting ? { gitHosting: persistedGitHosting } : {}),
367
+ agentPersonalities: {
368
+ // A host that has never carried a personalities section (undefined, not an
369
+ // empty roster the user cleared) is seeded with the shipped starter team so
370
+ // a fresh install opens with a working, role-complete roster. The one-time
371
+ // seed is also recorded on disk right after the store is built (see
372
+ // seedDefaultPersonalitiesIfAbsent) so clearing the whole team sticks.
373
+ personalities: config.agentPersonalities === undefined
374
+ ? [...DEFAULT_AGENT_PERSONALITIES]
375
+ : (config.agentPersonalities.personalities ?? []),
376
+ },
377
+ };
378
+ if (config.terminalProfiles !== undefined) {
379
+ initialConfig.terminalProfiles = config.terminalProfiles;
380
+ }
381
+ return initialConfig;
382
+ }
383
+ export async function createOttoDaemon(config, rootLogger) {
384
+ const logger = rootLogger.child({ module: "bootstrap" });
385
+ const bootstrapStart = performance.now();
386
+ const elapsed = () => `${(performance.now() - bootstrapStart).toFixed(0)}ms`;
387
+ const daemonVersion = resolveDaemonVersion(import.meta.url);
388
+ const daemonConfigStore = new DaemonConfigStore(config.ottoHome, createInitialMutableDaemonConfig(config), logger);
389
+ // Record the first-run seed on disk so the shipped starter team survives a
390
+ // restart AND a subsequent "delete every personality" stays deleted.
391
+ daemonConfigStore.seedDefaultPersonalitiesIfAbsent(DEFAULT_AGENT_PERSONALITIES);
392
+ const browserToolsPolicy = new DaemonConfigBrowserToolsPolicy(daemonConfigStore);
393
+ const browserToolsBroker = new BrowserToolsBroker({});
394
+ const previewDevServers = new DevServerManager({ logger });
395
+ const serverId = getOrCreateServerId(config.ottoHome, { logger });
396
+ const daemonKeyPair = await loadOrCreateDaemonKeyPair(config.ottoHome, logger);
397
+ const managedProcesses = createBootstrapManagedProcessRegistry(config, logger);
398
+ // Reconcile the helper-process ledger in the background so it never blocks the
399
+ // daemon from coming up; terminating a live leftover can take a few seconds.
400
+ // Best-effort, so a failure is logged here rather than crashing startup.
401
+ void reconcileManagedProcessLedger(managedProcesses, logger).catch((error) => {
402
+ logger.warn({ err: error }, "Failed to reconcile managed helper process ledger");
403
+ });
404
+ let relayTransport = null;
405
+ const staticDir = config.staticDir;
406
+ const downloadTokenTtlMs = config.downloadTokenTtlMs ?? 60000;
407
+ const downloadTokenStore = new DownloadTokenStore({
408
+ ttlMs: downloadTokenTtlMs,
409
+ });
410
+ // Capability token authenticating the daemon's own agents to the loopback
411
+ // Agent MCP endpoint (/mcp/agents). Random per daemon run, injected only into
412
+ // local agent configs and the daemon's own MCP client — never sent to remote
413
+ // clients — so it cannot be replayed off-box. This lets the injected MCP
414
+ // authenticate even when the daemon password is set via the app (hash only,
415
+ // no plaintext available). Mirrors the /api/files/download capability-token
416
+ // pattern.
417
+ const agentMcpAuthToken = randomUUID();
418
+ const listenTarget = parseListenString(config.listen);
419
+ const app = express();
420
+ app.set("trust proxy", resolveExpressTrustProxySetting(config));
421
+ let boundListenTarget = null;
422
+ let workspaceRegistry = null;
423
+ const terminalManager = createConfiguredTerminalManager({
424
+ getTerminalActivityUrl: () => createTerminalActivityUrl(boundListenTarget),
425
+ });
426
+ applyTerminalAgentHookSetting({ store: daemonConfigStore, logger });
427
+ const serviceProxyPublicBaseUrl = config.serviceProxy?.publicBaseUrl
428
+ ? config.serviceProxy.publicBaseUrl
429
+ : null;
430
+ const serviceProxy = createServiceProxySubsystem({
431
+ logger,
432
+ publicBaseUrl: serviceProxyPublicBaseUrl,
433
+ });
434
+ const scriptRuntimeStore = new WorkspaceScriptRuntimeStore();
435
+ const configuredHostnames = config.hostnames ?? config.allowedHosts;
436
+ let wsServer = null;
437
+ let serviceProxyListenTarget = null;
438
+ const scriptHealthMonitor = new ScriptHealthMonitor({
439
+ serviceProxy,
440
+ onChange: createScriptStatusEmitter({
441
+ sessions: () => wsServer?.listActiveSessions().map((session) => ({
442
+ emit: (message) => session.emitServerMessage(message),
443
+ })) ?? [],
444
+ serviceProxy,
445
+ runtimeStore: scriptRuntimeStore,
446
+ daemonPort: () => (boundListenTarget?.type === "tcp" ? boundListenTarget.port : null),
447
+ resolveWorkspaceDirectory: async (workspaceId) => (await workspaceRegistry?.get(workspaceId))?.cwd ?? null,
448
+ logger,
449
+ serviceProxyPublicBaseUrl,
450
+ }),
451
+ });
452
+ const handleBranchChange = createBranchChangeRouteHandler({
453
+ serviceProxy,
454
+ onRoutesChanged: (workspaceId) => {
455
+ scriptHealthMonitor.invalidateWorkspace(workspaceId);
456
+ },
457
+ logger,
458
+ });
459
+ // Service proxy classifies service hosts before daemon auth/route fallthrough.
460
+ // Registered service hosts proxy directly; known service namespaces without a
461
+ // route return 404 and never reach daemon APIs.
462
+ app.use(serviceProxy.middleware());
463
+ // Host allowlist / DNS rebinding protection (vite-like semantics).
464
+ // For non-TCP (unix sockets), skip host validation.
465
+ if (listenTarget.type === "tcp") {
466
+ app.use((req, res, next) => {
467
+ const hostHeader = typeof req.headers.host === "string" ? req.headers.host : undefined;
468
+ if (!isHostnameAllowed(hostHeader, configuredHostnames)) {
469
+ res.status(403).json({ error: "Invalid Host header" });
470
+ return;
471
+ }
472
+ next();
473
+ });
474
+ }
475
+ // CORS - allow same-origin + configured origins
476
+ const allowedOrigins = new Set([
477
+ ...config.corsAllowedOrigins,
478
+ // Packaged desktop renderers use the custom otto:// protocol scheme.
479
+ "otto://app",
480
+ // For TCP, add localhost variants
481
+ ...(listenTarget.type === "tcp"
482
+ ? [
483
+ `http://${listenTarget.host}:${listenTarget.port}`,
484
+ `http://localhost:${listenTarget.port}`,
485
+ `http://127.0.0.1:${listenTarget.port}`,
486
+ ]
487
+ : []),
488
+ ]);
489
+ app.use((req, res, next) => {
490
+ const origin = req.headers.origin;
491
+ if (origin && (allowedOrigins.has("*") || allowedOrigins.has(origin))) {
492
+ res.setHeader("Access-Control-Allow-Origin", origin);
493
+ res.setHeader("Access-Control-Allow-Methods", "GET, POST, DELETE, OPTIONS");
494
+ res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
495
+ res.setHeader("Access-Control-Allow-Credentials", "true");
496
+ }
497
+ if (req.method === "OPTIONS") {
498
+ res.status(204).end();
499
+ return;
500
+ }
501
+ next();
502
+ });
503
+ // Local, harmless, and token-gated; deliberately skips daemon auth.
504
+ app.post("/api/terminal-activity", express.json(), createTerminalActivityRouteHandler(terminalManager));
505
+ // Serve the bundled browser web UI when enabled. Mounted after service-proxy
506
+ // classification and host/CORS handling, but before daemon bearer auth, so
507
+ // static app files load without the daemon password while API/WebSocket calls
508
+ // remain protected.
509
+ mountWebUi(app, config, logger);
510
+ app.use(createRequireBearerMiddleware(config.auth, (context) => {
511
+ logger.warn(context, "Rejected HTTP request with invalid daemon password");
512
+ }));
513
+ app.use(express.json());
514
+ // Serve static files from public directory
515
+ app.use("/public", express.static(staticDir));
516
+ // Health check endpoint
517
+ app.get("/api/health", (_req, res) => {
518
+ res.json({ status: "ok", timestamp: new Date().toISOString() });
519
+ });
520
+ app.get("/api/status", (_req, res) => {
521
+ res.json({
522
+ status: "server_info",
523
+ serverId,
524
+ hostname: getHostname(),
525
+ version: daemonVersion,
526
+ listen: formatListenTarget(boundListenTarget ?? listenTarget),
527
+ });
528
+ });
529
+ const handleFileDownload = async (req, res) => {
530
+ const token = typeof req.query.token === "string" && req.query.token.trim().length > 0
531
+ ? req.query.token.trim()
532
+ : null;
533
+ if (!token) {
534
+ res.status(400).json({ error: "Missing download token" });
535
+ return;
536
+ }
537
+ const entry = downloadTokenStore.consumeToken(token);
538
+ if (!entry) {
539
+ res.status(403).json({ error: "Invalid or expired token" });
540
+ return;
541
+ }
542
+ let fileHandle = null;
543
+ try {
544
+ fileHandle = await open(entry.absolutePath, DOWNLOAD_OPEN_FLAGS);
545
+ const fileStats = await fileHandle.stat();
546
+ if (!fileStats.isFile()) {
547
+ res.status(404).json({ error: "File not found" });
548
+ return;
549
+ }
550
+ const safeFileName = entry.fileName.replace(/["\r\n]/g, "_");
551
+ res.setHeader("Content-Type", entry.mimeType);
552
+ res.setHeader("Content-Disposition", `attachment; filename="${safeFileName}"`);
553
+ res.setHeader("Content-Length", fileStats.size.toString());
554
+ const stream = fileHandle.createReadStream();
555
+ fileHandle = null;
556
+ stream.on("error", (err) => {
557
+ logger.error({ err }, "Failed to stream download");
558
+ if (!res.headersSent) {
559
+ res.status(500).json({ error: "Failed to read file" });
560
+ }
561
+ else {
562
+ res.end();
563
+ }
564
+ });
565
+ stream.pipe(res);
566
+ }
567
+ catch (err) {
568
+ logger.error({ err }, "Failed to download file");
569
+ if (!res.headersSent) {
570
+ res.status(404).json({ error: "File not found" });
571
+ }
572
+ }
573
+ finally {
574
+ await fileHandle?.close().catch(() => undefined);
575
+ }
576
+ };
577
+ app.get("/api/files/download", (req, res) => {
578
+ void handleFileDownload(req, res);
579
+ });
580
+ const httpServer = createHTTPServer(app);
581
+ // Script proxy WebSocket upgrade handler — must be registered before the
582
+ // VoiceAssistantWebSocketServer attaches its own "upgrade" listener so that
583
+ // script-bound upgrades are forwarded first. The handler is a no-op for
584
+ // requests that don't match a registered script route.
585
+ httpServer.on("upgrade", serviceProxy.upgradeHandler({ passthroughUnknown: true }));
586
+ if (config.serviceProxy?.standaloneListen) {
587
+ serviceProxyListenTarget = parseListenString(config.serviceProxy.standaloneListen);
588
+ }
589
+ const agentStorage = new AgentStorage(config.agentStoragePath, logger);
590
+ const personalityStatsStore = new PersonalityStatsStore(path.join(config.ottoHome, "stats", "personality-usage.json"), logger);
591
+ const projectRegistry = new FileBackedProjectRegistry(path.join(config.ottoHome, "projects", "projects.json"), logger);
592
+ workspaceRegistry = new FileBackedWorkspaceRegistry(path.join(config.ottoHome, "projects", "workspaces.json"), logger);
593
+ const chatService = new FileBackedChatService({
594
+ ottoHome: config.ottoHome,
595
+ logger,
596
+ });
597
+ // All PR/issue functionality routes through the git hosting layer: each
598
+ // call resolves the target project's configured provider (GitHub via gh
599
+ // CLI, Bitbucket Cloud via REST). `github` keeps its historical name — it
600
+ // is the provider-routing facade, not the gh CLI.
601
+ const gitHostingResolver = createGitHostingResolver({
602
+ github: createGitHubHostingService(),
603
+ getDaemonConfig: () => daemonConfigStore.get(),
604
+ readOttoConfigJson,
605
+ ottoHome: config.ottoHome,
606
+ });
607
+ const github = createGitHostingRouter(gitHostingResolver);
608
+ const unsubscribeGitHostingConfigChange = daemonConfigStore.onChange(() => {
609
+ // Provider credentials may have changed; re-resolve on next use.
610
+ gitHostingResolver.invalidateAll();
611
+ });
612
+ const workspaceGitService = new WorkspaceGitServiceImpl({
613
+ logger,
614
+ ottoHome: config.ottoHome,
615
+ worktreesRoot: config.worktreesRoot,
616
+ deps: {
617
+ github,
618
+ resolveHostingForCwd: async (cwd) => {
619
+ const resolved = await gitHostingResolver.resolveForCwd(cwd);
620
+ return {
621
+ providerId: resolved.providerId,
622
+ capabilities: resolved.capabilities,
623
+ credentialsMissing: resolved.credentialsMissing,
624
+ };
625
+ },
626
+ },
627
+ });
628
+ const providerSnapshotLogger = logger.child({ module: "provider-snapshot-manager" });
629
+ const providerSnapshotManager = new ProviderSnapshotManager({
630
+ logger: providerSnapshotLogger,
631
+ runtimeSettings: config.agentProviderSettings,
632
+ providerOverrides: config.providerOverrides,
633
+ workspaceGitService,
634
+ managedProcesses,
635
+ isDev: config.isDev === true,
636
+ extraClients: config.agentClients,
637
+ });
638
+ const initialAgentManagerState = providerSnapshotManager.getAgentManagerProviderState();
639
+ const agentManager = new AgentManager({
640
+ clients: initialAgentManagerState.clients,
641
+ providerDefinitions: initialAgentManagerState.providerDefinitions,
642
+ registry: agentStorage,
643
+ appendSystemPrompt: config.appendSystemPrompt,
644
+ onWorkspaceStateMayHaveChanged: ({ cwd }) => {
645
+ workspaceGitService.onWorkspaceStateMayHaveChanged(cwd);
646
+ },
647
+ // Counted at the createAgent choke point so composer, MCP create_agent,
648
+ // and schedule spawns all land in the per-personality usage stats.
649
+ onPersonalitySpawn: (personalityId) => {
650
+ void personalityStatsStore.increment(personalityId);
651
+ },
652
+ mcpAuthToken: agentMcpAuthToken,
653
+ logger,
654
+ });
655
+ const detachAgentStoragePersistence = attachAgentStoragePersistence(logger, agentManager, agentStorage);
656
+ await agentStorage.initialize();
657
+ logger.info({ elapsed: elapsed() }, "Agent storage initialized");
658
+ await bootstrapWorkspaceRegistries({
659
+ ottoHome: config.ottoHome,
660
+ agentStorage,
661
+ projectRegistry,
662
+ workspaceRegistry,
663
+ workspaceGitService,
664
+ logger,
665
+ });
666
+ logger.info({ elapsed: elapsed() }, "Workspace registries bootstrapped");
667
+ const workspaceReconciliation = new WorkspaceReconciliationService({
668
+ projectRegistry,
669
+ workspaceRegistry,
670
+ logger,
671
+ workspaceGitService,
672
+ });
673
+ void (async () => {
674
+ try {
675
+ const result = await workspaceReconciliation.runOnce();
676
+ logger.info({
677
+ elapsed: elapsed(),
678
+ changeCount: result.changesApplied.length,
679
+ }, "Workspace registries reconciled");
680
+ }
681
+ catch (error) {
682
+ logger.error({ err: error }, "Background workspace reconciliation failed");
683
+ }
684
+ })();
685
+ await chatService.initialize();
686
+ logger.info({ elapsed: elapsed() }, "Chat service initialized");
687
+ const checkoutDiffManager = new CheckoutDiffManager({
688
+ logger,
689
+ ottoHome: config.ottoHome,
690
+ workspaceGitService,
691
+ });
692
+ const archiveWorkspaceRecordExternal = async (workspaceId) => {
693
+ const sessions = wsServer?.listActiveSessions() ?? [];
694
+ if (sessions.length > 0) {
695
+ await Promise.all(sessions.map((session) => session.archiveWorkspaceRecordForExternalMutation(workspaceId)));
696
+ return;
697
+ }
698
+ await archivePersistedWorkspaceRecord({
699
+ workspaceId,
700
+ workspaceRegistry,
701
+ });
702
+ };
703
+ // external path→workspace adapter, not ownership: archive-by-path requests that
704
+ // arrive with a worktree path and no workspaceId (old clients / CLI).
705
+ const findWorkspaceIdForCwdExternal = async (cwd) => {
706
+ return resolveWorkspaceIdForPath(cwd, await workspaceRegistry.list());
707
+ };
708
+ const ensureWorkspaceForCreateExternal = async (cwd, firstAgentContext) => {
709
+ const workspace = await createLocalCheckoutWorkspace({ cwd, title: resolveFirstAgentPromptTitle(firstAgentContext) }, { projectRegistry, workspaceRegistry, workspaceGitService });
710
+ if (firstAgentContext) {
711
+ workspaceAutoName.scheduleForDirectory({
712
+ workspaceId: workspace.workspaceId,
713
+ cwd: workspace.cwd,
714
+ firstAgentContext,
715
+ });
716
+ }
717
+ return workspace.workspaceId;
718
+ };
719
+ const listActiveWorkspacesExternal = async () => {
720
+ const workspaces = await workspaceRegistry.list();
721
+ return workspaces
722
+ .filter((workspace) => !workspace.archivedAt)
723
+ .map((workspace) => ({
724
+ workspaceId: workspace.workspaceId,
725
+ cwd: workspace.cwd,
726
+ kind: workspace.kind,
727
+ }));
728
+ };
729
+ const markWorkspaceArchivingExternal = (workspaceIds, archivingAt) => {
730
+ const workspaceIdList = Array.from(workspaceIds);
731
+ for (const session of wsServer?.listActiveSessions() ?? []) {
732
+ session.markWorkspaceArchivingForExternalMutation(workspaceIdList, archivingAt);
733
+ }
734
+ };
735
+ const clearWorkspaceArchivingExternal = (workspaceIds) => {
736
+ const workspaceIdList = Array.from(workspaceIds);
737
+ for (const session of wsServer?.listActiveSessions() ?? []) {
738
+ session.clearWorkspaceArchivingForExternalMutation(workspaceIdList);
739
+ }
740
+ };
741
+ const emitWorkspaceUpdatesExternal = async (workspaceIds) => {
742
+ const workspaceIdList = Array.from(workspaceIds);
743
+ await Promise.all((wsServer?.listActiveSessions() ?? []).map((session) => session.emitWorkspaceUpdatesForExternalWorkspaceIds(workspaceIdList)));
744
+ };
745
+ const ensureWorkspaceForCreateAndBroadcastExternal = async (cwd, firstAgentContext) => {
746
+ const workspaceId = await ensureWorkspaceForCreateExternal(cwd, firstAgentContext);
747
+ await emitWorkspaceUpdatesExternal([workspaceId]);
748
+ return workspaceId;
749
+ };
750
+ const emitWorkspaceUpdateForCwdExternal = async (cwd) => {
751
+ const workspaceIds = workspaceIdsOnCheckout(await workspaceRegistry.list(), cwd);
752
+ await emitWorkspaceUpdatesExternal(workspaceIds);
753
+ };
754
+ const emitExternalSessionMessage = (message) => {
755
+ wsServer?.broadcast(wrapSessionMessage(message));
756
+ };
757
+ // Daemon-global artifact service backing the create_artifact agent tool.
758
+ // Client-initiated artifact RPCs go through each session's own service
759
+ // instance; both share the same file-backed store under $OTTO_HOME. Status
760
+ // notifications broadcast to every client so artifacts created by agents
761
+ // show up live everywhere.
762
+ const toolArtifactService = new ArtifactService({
763
+ projectCwd: config.ottoHome,
764
+ logger,
765
+ agentManager,
766
+ providerSnapshotManager,
767
+ broadcastArtifactUpdate: (metadata) => {
768
+ emitExternalSessionMessage({
769
+ type: "artifact.updated.notification",
770
+ payload: { artifact: metadata },
771
+ });
772
+ },
773
+ });
774
+ const workspaceAutoName = new WorkspaceAutoName({
775
+ agentManager,
776
+ workspaceRegistry,
777
+ workspaceGitService,
778
+ providerSnapshotManager,
779
+ readDaemonConfig: () => ({ metadataGeneration: daemonConfigStore.get().metadataGeneration }),
780
+ gitMutation: createGitMutationService({
781
+ workspaceGitService,
782
+ github,
783
+ logger,
784
+ }),
785
+ emitWorkspaceUpdateForCwd: emitWorkspaceUpdateForCwdExternal,
786
+ emitWorkspaceUpdateForWorkspaceId: async (workspaceId) => {
787
+ await emitWorkspaceUpdatesExternal([workspaceId]);
788
+ },
789
+ logger,
790
+ });
791
+ setupAutoArchiveOnMerge({
792
+ ottoHome: config.ottoHome,
793
+ ottoWorktreesBaseRoot: config.worktreesRoot,
794
+ daemonConfigStore,
795
+ workspaceGitService,
796
+ github,
797
+ agentManager,
798
+ agentStorage,
799
+ terminalManager,
800
+ logger,
801
+ findWorkspaceIdForCwd: findWorkspaceIdForCwdExternal,
802
+ listActiveWorkspaces: listActiveWorkspacesExternal,
803
+ archiveWorkspaceRecord: archiveWorkspaceRecordExternal,
804
+ markWorkspaceArchiving: markWorkspaceArchivingExternal,
805
+ clearWorkspaceArchiving: clearWorkspaceArchivingExternal,
806
+ emitWorkspaceUpdatesForWorkspaceIds: emitWorkspaceUpdatesExternal,
807
+ });
808
+ const createOttoWorktreeForTools = async (input, serviceOptions) => {
809
+ return createOttoWorktreeWorkflow({
810
+ ottoHome: config.ottoHome,
811
+ worktreesRoot: config.worktreesRoot,
812
+ createOttoWorktree: async (workflowInput, workflowOptions) => {
813
+ return createRegisteredOttoWorktree(workflowInput, {
814
+ github,
815
+ ...(workflowOptions?.resolveDefaultBranch
816
+ ? {
817
+ resolveDefaultBranch: workflowOptions.resolveDefaultBranch,
818
+ }
819
+ : {}),
820
+ projectRegistry,
821
+ workspaceRegistry,
822
+ workspaceGitService,
823
+ });
824
+ },
825
+ warmWorkspaceGitData: async (workspace) => {
826
+ await Promise.all(wsServer
827
+ ?.listActiveSessions()
828
+ .map((session) => session.warmWorkspaceGitDataForWorkspace(workspace)) ?? []);
829
+ },
830
+ autoNameWorkspaceBranchForFirstAgent: (autoNameInput) => workspaceAutoName.scheduleForWorktree(autoNameInput),
831
+ emitWorkspaceUpdateForWorkspaceId: async (workspaceId) => {
832
+ await emitWorkspaceUpdatesExternal([workspaceId]);
833
+ },
834
+ cacheWorkspaceSetupSnapshot: () => { },
835
+ emit: emitExternalSessionMessage,
836
+ sessionLogger: logger,
837
+ terminalManager,
838
+ archiveWorkspaceRecord: archiveWorkspaceRecordExternal,
839
+ serviceProxy,
840
+ scriptRuntimeStore,
841
+ getDaemonTcpPort: () => (boundListenTarget?.type === "tcp" ? boundListenTarget.port : null),
842
+ getDaemonTcpHost: () => (boundListenTarget?.type === "tcp" ? boundListenTarget.host : null),
843
+ serviceProxyPublicBaseUrl,
844
+ onScriptsChanged: null,
845
+ }, input, serviceOptions);
846
+ };
847
+ const createAgentCommandDependencies = {
848
+ agentManager,
849
+ agentStorage,
850
+ logger,
851
+ ottoHome: config.ottoHome,
852
+ worktreesRoot: config.worktreesRoot,
853
+ terminalManager,
854
+ providerSnapshotManager,
855
+ createOttoWorktree: createOttoWorktreeForTools,
856
+ ensureWorkspaceForCreate: ensureWorkspaceForCreateAndBroadcastExternal,
857
+ };
858
+ const createAgent = (input) => createAgentCommand(createAgentCommandDependencies, input);
859
+ const loopService = new LoopService({
860
+ ottoHome: config.ottoHome,
861
+ logger,
862
+ agentManager,
863
+ createAgent,
864
+ ensureWorkspaceForCreate: ensureWorkspaceForCreateAndBroadcastExternal,
865
+ });
866
+ await loopService.initialize();
867
+ logger.info({ elapsed: elapsed() }, "Loop service initialized");
868
+ const createScheduleLocalWorkspaceExternal = async (input) => {
869
+ const workspace = await createLocalCheckoutWorkspace({ cwd: input.cwd, title: resolveFirstAgentPromptTitle(input.firstAgentContext) }, { projectRegistry, workspaceRegistry, workspaceGitService });
870
+ workspaceAutoName.scheduleForDirectory({
871
+ workspaceId: workspace.workspaceId,
872
+ cwd: workspace.cwd,
873
+ firstAgentContext: input.firstAgentContext,
874
+ });
875
+ await emitWorkspaceUpdatesExternal([workspace.workspaceId]);
876
+ return workspace;
877
+ };
878
+ const createScheduleOttoWorktreeExternal = async (input) => {
879
+ const result = await createOttoWorktreeForTools({
880
+ cwd: input.cwd,
881
+ firstAgentContext: input.firstAgentContext,
882
+ });
883
+ await emitWorkspaceUpdatesExternal([result.workspace.workspaceId]);
884
+ return result;
885
+ };
886
+ const archiveScheduleWorkspaceExternal = async (workspaceId, repoRoot) => {
887
+ await archiveByScope({
888
+ ottoHome: config.ottoHome,
889
+ ottoWorktreesBaseRoot: config.worktreesRoot,
890
+ github,
891
+ workspaceGitService,
892
+ agentManager,
893
+ agentStorage,
894
+ findWorkspaceIdForCwd: findWorkspaceIdForCwdExternal,
895
+ listActiveWorkspaces: listActiveWorkspacesExternal,
896
+ archiveWorkspaceRecord: archiveWorkspaceRecordExternal,
897
+ emitWorkspaceUpdatesForWorkspaceIds: emitWorkspaceUpdatesExternal,
898
+ markWorkspaceArchiving: markWorkspaceArchivingExternal,
899
+ clearWorkspaceArchiving: clearWorkspaceArchivingExternal,
900
+ killTerminalsForWorkspace: (workspaceIdToKill) => killTerminalsForWorkspace({
901
+ terminalManager,
902
+ sessionLogger: logger,
903
+ }, workspaceIdToKill),
904
+ sessionLogger: logger,
905
+ }, {
906
+ scope: { kind: "workspace", workspaceId },
907
+ repoRoot,
908
+ ottoWorktreesBaseRoot: config.worktreesRoot,
909
+ requestId: "schedule-run-finish",
910
+ });
911
+ };
912
+ const scheduleService = new ScheduleService({
913
+ ottoHome: config.ottoHome,
914
+ logger,
915
+ agentManager,
916
+ agentStorage,
917
+ createAgent,
918
+ createLocalCheckoutWorkspace: createScheduleLocalWorkspaceExternal,
919
+ createOttoWorktreeWorkspace: createScheduleOttoWorktreeExternal,
920
+ archiveWorkspace: archiveScheduleWorkspaceExternal,
921
+ providerSnapshotManager,
922
+ readAgentPersonalities: () => daemonConfigStore.get().agentPersonalities?.personalities ?? [],
923
+ });
924
+ await scheduleService.start();
925
+ agentManager.setAgentArchivedCallback(async (agentId) => {
926
+ try {
927
+ await scheduleService.completeForAgent(agentId);
928
+ }
929
+ catch (error) {
930
+ logger.warn({ err: error, agentId }, "Failed to complete schedules for archived agent");
931
+ }
932
+ });
933
+ logger.info({ elapsed: elapsed() }, "Schedule service initialized");
934
+ logger.info({ elapsed: elapsed() }, "Loading persisted agent registry");
935
+ const persistedRecords = await agentStorage.list();
936
+ logger.info({ elapsed: elapsed() }, `Agent registry loaded (${persistedRecords.length} record${persistedRecords.length === 1 ? "" : "s"}); agents will initialize on demand`);
937
+ logger.info("Voice mode configured for agent-scoped resume flow (no dedicated voice assistant provider)");
938
+ logger.info({ elapsed: elapsed() }, "Preparing voice and MCP runtime");
939
+ const createAgentToolHostDependencies = (runtime) => ({
940
+ agentManager,
941
+ agentStorage,
942
+ terminalManager,
943
+ getDaemonTcpPort: () => (boundListenTarget?.type === "tcp" ? boundListenTarget.port : null),
944
+ scheduleService,
945
+ providerSnapshotManager,
946
+ readAgentPersonalities: () => daemonConfigStore.get().agentPersonalities?.personalities ?? [],
947
+ github,
948
+ workspaceGitService,
949
+ findWorkspaceIdForCwd: findWorkspaceIdForCwdExternal,
950
+ listActiveWorkspaces: listActiveWorkspacesExternal,
951
+ archiveWorkspaceRecord: archiveWorkspaceRecordExternal,
952
+ emitWorkspaceUpdatesForWorkspaceIds: emitWorkspaceUpdatesExternal,
953
+ workspaceRegistry,
954
+ projectRegistry,
955
+ markWorkspaceArchiving: markWorkspaceArchivingExternal,
956
+ clearWorkspaceArchiving: clearWorkspaceArchivingExternal,
957
+ ensureWorkspaceForCreate: createAgentCommandDependencies.ensureWorkspaceForCreate,
958
+ createOttoWorktree: createAgentCommandDependencies.createOttoWorktree,
959
+ browserToolsEnabled: browserToolsPolicy.isEnabled(),
960
+ browserToolsBroker,
961
+ previewDevServers,
962
+ artifactService: toolArtifactService,
963
+ emitArtifactCreated: (artifact) => {
964
+ emitExternalSessionMessage({
965
+ type: "artifact.created.notification",
966
+ payload: { artifact },
967
+ });
968
+ },
969
+ emitArtifactUpdated: (artifact) => {
970
+ emitExternalSessionMessage({
971
+ type: "artifact.updated.notification",
972
+ payload: { artifact },
973
+ });
974
+ },
975
+ ottoHome: config.ottoHome,
976
+ worktreesRoot: config.worktreesRoot,
977
+ callerAgentId: runtime.callerAgentId,
978
+ enableVoiceTools: runtime.enableVoiceTools,
979
+ voiceOnly: runtime.voiceOnly,
980
+ resolveSpeakHandler: (agentId) => wsServer?.resolveVoiceSpeakHandler(agentId) ?? null,
981
+ resolveCallerContext: (agentId) => wsServer?.resolveVoiceCallerContext(agentId) ?? null,
982
+ logger,
983
+ });
984
+ const createAgentToolCatalog = (runtime) => createOttoToolCatalog(createAgentToolHostDependencies(runtime));
985
+ agentManager.setOttoToolCatalogFactory(createAgentToolCatalog);
986
+ agentManager.setOttoToolsEnabled(config.mcpInjectIntoAgents !== false);
987
+ const mcpEnabled = config.mcpEnabled ?? true;
988
+ let agentMcpBaseUrl = null;
989
+ if (mcpEnabled) {
990
+ const agentMcpRoute = "/mcp/agents";
991
+ const createAgentMcpSession = async (callerAgentId) => {
992
+ const agentMcpServer = await createAgentMcpServer(createAgentToolHostDependencies({ callerAgentId }));
993
+ // Stateless mode: each HTTP request builds a fresh server + transport that is
994
+ // torn down when the response closes, so no per-session state is retained between
995
+ // requests. The agent control plane only lists and calls tools, neither of which
996
+ // needs cross-request state, so sessions would only pin memory for the life of the
997
+ // daemon (agents that exit without a clean DELETE never get reaped).
998
+ const transport = new StreamableHTTPServerTransport({
999
+ sessionIdGenerator: undefined,
1000
+ // NOTE: We enforce a Vite-like host allowlist at the app/websocket layer.
1001
+ // StreamableHTTPServerTransport's built-in check requires exact Host header matches.
1002
+ enableDnsRebindingProtection: false,
1003
+ });
1004
+ Object.assign(transport, {
1005
+ onerror: (err) => {
1006
+ logger.error({ err }, "Agent MCP transport error");
1007
+ },
1008
+ });
1009
+ await agentMcpServer.connect(transport);
1010
+ return { server: agentMcpServer, transport };
1011
+ };
1012
+ const runAgentMcpRequest = async (req, res) => {
1013
+ // This route is exempt from the global daemon-password middleware, so it
1014
+ // authenticates here using the injected capability token (or a valid
1015
+ // daemon password). Without this, a password-protected daemon would be
1016
+ // wide open on its agent control plane.
1017
+ if (!(await isAgentMcpRequestAuthorized({
1018
+ password: config.auth?.password,
1019
+ capabilityToken: agentMcpAuthToken,
1020
+ authorizationHeader: req.header("authorization"),
1021
+ }))) {
1022
+ res.status(401).json({ error: "Unauthorized" });
1023
+ return;
1024
+ }
1025
+ if (config.mcpDebug) {
1026
+ logger.debug({
1027
+ method: req.method,
1028
+ url: req.originalUrl,
1029
+ sessionId: req.header("mcp-session-id"),
1030
+ authorization: req.header("authorization") ? REDACTED_LOG_VALUE : undefined,
1031
+ body: summarizeAgentMcpDebugBody(req.body),
1032
+ }, "Agent MCP request");
1033
+ }
1034
+ try {
1035
+ // Stateless: GET (standalone SSE) and DELETE (session termination) have no
1036
+ // meaning without sessions. The MCP client tolerates 405 on the GET stream
1037
+ // and never issues a DELETE because it is never handed a session id.
1038
+ if (req.method !== "POST") {
1039
+ res.status(405).json({
1040
+ jsonrpc: "2.0",
1041
+ error: {
1042
+ code: -32000,
1043
+ message: "Method not allowed",
1044
+ },
1045
+ id: null,
1046
+ });
1047
+ return;
1048
+ }
1049
+ const callerAgentIdRaw = req.query.callerAgentId;
1050
+ let callerAgentId;
1051
+ if (typeof callerAgentIdRaw === "string") {
1052
+ callerAgentId = callerAgentIdRaw;
1053
+ }
1054
+ else if (Array.isArray(callerAgentIdRaw) && typeof callerAgentIdRaw[0] === "string") {
1055
+ callerAgentId = callerAgentIdRaw[0];
1056
+ }
1057
+ const { server, transport } = await createAgentMcpSession(callerAgentId);
1058
+ res.on("close", () => {
1059
+ void transport.close();
1060
+ void server.close();
1061
+ });
1062
+ await transport.handleRequest(req, res, req.body);
1063
+ }
1064
+ catch (err) {
1065
+ logger.error({ err }, "Failed to handle Agent MCP request");
1066
+ if (!res.headersSent) {
1067
+ res.status(500).json({
1068
+ jsonrpc: "2.0",
1069
+ error: {
1070
+ code: -32603,
1071
+ message: "Internal MCP server error",
1072
+ },
1073
+ id: null,
1074
+ });
1075
+ }
1076
+ }
1077
+ };
1078
+ const handleAgentMcpRequest = (req, res) => {
1079
+ void runAgentMcpRequest(req, res);
1080
+ };
1081
+ app.post(agentMcpRoute, handleAgentMcpRequest);
1082
+ app.get(agentMcpRoute, handleAgentMcpRequest);
1083
+ app.delete(agentMcpRoute, handleAgentMcpRequest);
1084
+ logger.info({ route: agentMcpRoute }, "Agent MCP server mounted on main app");
1085
+ }
1086
+ else {
1087
+ logger.info("Agent MCP HTTP endpoint disabled");
1088
+ }
1089
+ const speechService = createSpeechService({
1090
+ logger,
1091
+ openaiConfig: config.openai,
1092
+ speechConfig: config.speech,
1093
+ });
1094
+ logger.info({ elapsed: elapsed() }, "Speech service created");
1095
+ // Hot-reload speech providers when daemon config changes (settings UI writes
1096
+ // features.dictation/voiceMode via the daemon config store). reconfigure()
1097
+ // no-ops when the resolved speech config is unchanged, so unrelated config
1098
+ // patches never churn the speech worker.
1099
+ const unsubscribeSpeechConfigChange = daemonConfigStore.onChange(() => {
1100
+ try {
1101
+ const persisted = loadPersistedConfig(config.ottoHome, logger);
1102
+ const resolved = resolveSpeechConfig({
1103
+ ottoHome: config.ottoHome,
1104
+ env: process.env,
1105
+ persisted,
1106
+ });
1107
+ void speechService
1108
+ .reconfigure({ openaiConfig: resolved.openai, speechConfig: resolved.speech })
1109
+ .catch((error) => {
1110
+ logger.warn({ err: error }, "Speech reconfigure failed after config change");
1111
+ });
1112
+ }
1113
+ catch (error) {
1114
+ logger.warn({ err: error }, "Failed to re-resolve speech config after config change");
1115
+ }
1116
+ });
1117
+ logger.info({ elapsed: elapsed() }, "Bootstrap complete, ready to start listening");
1118
+ const start = async () => {
1119
+ let mainStarted = false;
1120
+ try {
1121
+ if (serviceProxyListenTarget) {
1122
+ const boundServiceProxyTarget = await serviceProxy.startStandalone({
1123
+ listenTarget: serviceProxyListenTarget,
1124
+ });
1125
+ serviceProxyListenTarget = boundServiceProxyTarget;
1126
+ logger.info({
1127
+ listen: formatListenTarget(serviceProxyListenTarget),
1128
+ publicBaseUrl: serviceProxyPublicBaseUrl,
1129
+ elapsed: elapsed(),
1130
+ }, "Service proxy listening");
1131
+ }
1132
+ // Start main HTTP server
1133
+ await new Promise((resolve, reject) => {
1134
+ const onError = (err) => {
1135
+ httpServer.off("listening", onListening);
1136
+ reject(err);
1137
+ };
1138
+ const onListening = () => {
1139
+ httpServer.off("error", onError);
1140
+ mainStarted = true;
1141
+ const logAndResolve = async () => {
1142
+ boundListenTarget = resolveBoundListenTarget(listenTarget, httpServer);
1143
+ const mcpBaseUrl = mcpEnabled ? createAgentMcpBaseUrl(boundListenTarget) : null;
1144
+ agentMcpBaseUrl = config.mcpInjectIntoAgents === false ? null : mcpBaseUrl;
1145
+ agentManager.setMcpBaseUrl(agentMcpBaseUrl);
1146
+ agentManager.setOttoToolsEnabled(config.mcpInjectIntoAgents !== false);
1147
+ daemonConfigStore.onFieldChange("mcp.injectIntoAgents", (value) => {
1148
+ agentManager.setMcpBaseUrl(value ? mcpBaseUrl : null);
1149
+ agentManager.setOttoToolsEnabled(value !== false);
1150
+ });
1151
+ daemonConfigStore.onFieldChange("appendSystemPrompt", (value) => {
1152
+ agentManager.setAppendSystemPrompt(typeof value === "string" ? value : "");
1153
+ });
1154
+ const relayEnabled = config.relayEnabled ?? true;
1155
+ const relayEndpoint = config.relayEndpoint ?? "relay.otto-code.me:443";
1156
+ const relayPublicEndpoint = config.relayPublicEndpoint ?? relayEndpoint;
1157
+ const relayUseTls = config.relayUseTls ?? relayEndpoint === "relay.otto-code.me:443";
1158
+ const relayPublicUseTls = config.relayPublicUseTls ?? relayUseTls;
1159
+ const appBaseUrl = config.appBaseUrl ?? "https://app.otto-code.me";
1160
+ if (boundListenTarget.type === "tcp") {
1161
+ logger.info({
1162
+ host: boundListenTarget.host,
1163
+ port: boundListenTarget.port,
1164
+ authRequired: !!config.auth?.password,
1165
+ elapsed: elapsed(),
1166
+ }, `Server listening on http://${boundListenTarget.host}:${boundListenTarget.port}`);
1167
+ }
1168
+ else {
1169
+ logger.info({
1170
+ path: boundListenTarget.path,
1171
+ authRequired: !!config.auth?.password,
1172
+ elapsed: elapsed(),
1173
+ }, `Server listening on ${boundListenTarget.path}`);
1174
+ }
1175
+ if (config.auth?.password) {
1176
+ logger.info("Daemon password authentication enabled");
1177
+ }
1178
+ wsServer = new VoiceAssistantWebSocketServer(httpServer, logger, serverId, agentManager, agentStorage, downloadTokenStore, config.ottoHome, daemonConfigStore, mcpBaseUrl, { allowedOrigins, hostnames: configuredHostnames }, workspaceAutoName, config.auth, speechService, terminalManager, {
1179
+ finalTimeoutMs: config.dictationFinalTimeoutMs,
1180
+ }, daemonVersion, (intent) => {
1181
+ try {
1182
+ config.onLifecycleIntent?.(intent);
1183
+ }
1184
+ catch (error) {
1185
+ logger.error({ err: error, intent }, "Failed to handle daemon lifecycle intent");
1186
+ }
1187
+ }, projectRegistry, workspaceRegistry, chatService, loopService, scheduleService, checkoutDiffManager, serviceProxy, scriptRuntimeStore, handleBranchChange, () => (boundListenTarget?.type === "tcp" ? boundListenTarget.port : null), () => (boundListenTarget?.type === "tcp" ? boundListenTarget.host : null), (hostname) => scriptHealthMonitor.getHealthForHostname(hostname), workspaceGitService, github, config.pushNotificationSender, providerSnapshotManager, {
1188
+ listen: formatListenTarget(boundListenTarget ?? listenTarget),
1189
+ worktreesRoot: config.worktreesRoot,
1190
+ appBaseUrl: config.appBaseUrl,
1191
+ relay: {
1192
+ enabled: relayEnabled,
1193
+ endpoint: relayEndpoint,
1194
+ publicEndpoint: relayPublicEndpoint,
1195
+ useTls: relayUseTls,
1196
+ publicUseTls: relayPublicUseTls,
1197
+ },
1198
+ }, serviceProxyPublicBaseUrl, browserToolsBroker, previewDevServers, gitHostingResolver);
1199
+ wsServer.setPersonalityStatsProvider(() => personalityStatsStore.get());
1200
+ // Sanity guard: never let preview "stop external server" tree-kill
1201
+ // Otto's own runtime — the daemon's listen port or a loopback dev
1202
+ // server hosting a connected client (e.g. Metro serving this app).
1203
+ const wsServerForProtectedPorts = wsServer;
1204
+ previewDevServers.setProtectedPortsProvider(() => {
1205
+ const ports = new Set();
1206
+ const bound = boundListenTarget ?? listenTarget;
1207
+ if (bound.type === "tcp") {
1208
+ ports.add(bound.port);
1209
+ }
1210
+ for (const port of wsServerForProtectedPorts.getConnectedClientOriginPorts()) {
1211
+ ports.add(port);
1212
+ }
1213
+ return [...ports];
1214
+ });
1215
+ if (relayEnabled) {
1216
+ const offer = await createConnectionOfferV2({
1217
+ serverId,
1218
+ daemonPublicKeyB64: daemonKeyPair.publicKeyB64,
1219
+ relay: {
1220
+ endpoint: relayPublicEndpoint,
1221
+ useTls: relayPublicUseTls,
1222
+ },
1223
+ });
1224
+ encodeOfferToFragmentUrl({ offer, appBaseUrl });
1225
+ relayTransport?.stop().catch(() => undefined);
1226
+ relayTransport = startRelayTransport({
1227
+ logger,
1228
+ attachSocket: (ws, metadata) => {
1229
+ if (!wsServer) {
1230
+ throw new Error("WebSocket server not initialized");
1231
+ }
1232
+ return wsServer.attachExternalSocket(ws, metadata);
1233
+ },
1234
+ relayEndpoint,
1235
+ relayUseTls,
1236
+ serverId,
1237
+ daemonKeyPair: daemonKeyPair.keyPair,
1238
+ });
1239
+ }
1240
+ };
1241
+ logAndResolve().then(resolve, reject);
1242
+ };
1243
+ httpServer.once("error", onError);
1244
+ httpServer.once("listening", onListening);
1245
+ if (listenTarget.type === "tcp") {
1246
+ httpServer.listen(listenTarget.port, listenTarget.host);
1247
+ }
1248
+ else {
1249
+ if (listenTarget.type === "socket" && existsSync(listenTarget.path)) {
1250
+ unlinkSync(listenTarget.path);
1251
+ }
1252
+ httpServer.listen(listenTarget.path);
1253
+ }
1254
+ });
1255
+ // Start speech service after listening so synchronous Sherpa native
1256
+ // model loading doesn't block the server from accepting connections.
1257
+ speechService.start();
1258
+ scriptHealthMonitor.start();
1259
+ }
1260
+ catch (error) {
1261
+ await serviceProxy.stopStandalone().catch(() => undefined);
1262
+ if (mainStarted) {
1263
+ httpServer.closeAllConnections();
1264
+ await new Promise((resolve) => httpServer.close(() => resolve()));
1265
+ }
1266
+ throw error;
1267
+ }
1268
+ };
1269
+ const stop = async () => {
1270
+ scriptHealthMonitor.stop();
1271
+ // Dev servers first: they are children of this daemon and hold ports the
1272
+ // next daemon instance may want.
1273
+ await previewDevServers.shutdown().catch(() => undefined);
1274
+ await closeAllAgents(logger, agentManager);
1275
+ await agentManager.flush().catch(() => undefined);
1276
+ detachAgentStoragePersistence();
1277
+ await agentStorage.flush().catch(() => undefined);
1278
+ await providerSnapshotManager.shutdown();
1279
+ terminalManager.killAll();
1280
+ unsubscribeSpeechConfigChange();
1281
+ unsubscribeGitHostingConfigChange();
1282
+ github.dispose?.();
1283
+ speechService.stop();
1284
+ toolArtifactService.stop();
1285
+ await scheduleService.stop().catch(() => undefined);
1286
+ await relayTransport?.stop().catch(() => undefined);
1287
+ if (wsServer) {
1288
+ await wsServer.close();
1289
+ }
1290
+ await serviceProxy.stopStandalone();
1291
+ // Force-drop remaining sockets so httpServer.close() resolves promptly.
1292
+ // We've already closed wsServer (which sent ws-layer close frames) and
1293
+ // stopped every other service, so anything still attached is a TCP
1294
+ // socket whose higher-level shutdown hasn't fully released it (e.g.
1295
+ // upgraded WS sockets in the closing handshake, or HTTP keep-alive
1296
+ // sockets in CLOSE_WAIT). closeIdleConnections() does not catch
1297
+ // upgraded sockets, so we use closeAllConnections() here.
1298
+ httpServer.closeAllConnections();
1299
+ await new Promise((resolve) => {
1300
+ httpServer.close(() => resolve());
1301
+ });
1302
+ // Clean up socket files
1303
+ if (listenTarget.type === "socket" && existsSync(listenTarget.path)) {
1304
+ unlinkSync(listenTarget.path);
1305
+ }
1306
+ };
1307
+ return {
1308
+ config,
1309
+ agentManager,
1310
+ agentStorage,
1311
+ terminalManager,
1312
+ serviceProxy,
1313
+ scriptRuntimeStore,
1314
+ browserToolsBroker,
1315
+ start,
1316
+ stop,
1317
+ getListenTarget: () => boundListenTarget,
1318
+ };
1319
+ }
1320
+ async function closeAllAgents(logger, agentManager) {
1321
+ const agents = agentManager.listAgents();
1322
+ await Promise.all(agents.map(async (agent) => {
1323
+ try {
1324
+ await agentManager.closeAgent(agent.id);
1325
+ }
1326
+ catch (err) {
1327
+ logger.error({ err, agentId: agent.id }, "Failed to close agent");
1328
+ }
1329
+ }));
1330
+ }
1331
+ //# sourceMappingURL=bootstrap.js.map