dodo-mcp 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (447) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +425 -0
  3. package/dist/auth/adapter.js +43 -0
  4. package/dist/auth/adapter.js.map +1 -0
  5. package/dist/auth/clientDeletion.js +45 -0
  6. package/dist/auth/clientDeletion.js.map +1 -0
  7. package/dist/auth/clients.js +55 -0
  8. package/dist/auth/clients.js.map +1 -0
  9. package/dist/auth/constants.js +10 -0
  10. package/dist/auth/constants.js.map +1 -0
  11. package/dist/auth/interactions.js +184 -0
  12. package/dist/auth/interactions.js.map +1 -0
  13. package/dist/auth/keys.js +56 -0
  14. package/dist/auth/keys.js.map +1 -0
  15. package/dist/auth/provider.js +131 -0
  16. package/dist/auth/provider.js.map +1 -0
  17. package/dist/auth/verifier.js +74 -0
  18. package/dist/auth/verifier.js.map +1 -0
  19. package/dist/cli/kill.js +124 -0
  20. package/dist/cli/kill.js.map +1 -0
  21. package/dist/cli/main.js +1393 -0
  22. package/dist/cli/main.js.map +1 -0
  23. package/dist/cli/menu.js +127 -0
  24. package/dist/cli/menu.js.map +1 -0
  25. package/dist/cli/terminal.js +30 -0
  26. package/dist/cli/terminal.js.map +1 -0
  27. package/dist/config/globalConfig.js +144 -0
  28. package/dist/config/globalConfig.js.map +1 -0
  29. package/dist/config/limits.js +42 -0
  30. package/dist/config/limits.js.map +1 -0
  31. package/dist/config/paths.js +94 -0
  32. package/dist/config/paths.js.map +1 -0
  33. package/dist/config/projectConfig.js +63 -0
  34. package/dist/config/projectConfig.js.map +1 -0
  35. package/dist/config/stateImport.js +306 -0
  36. package/dist/config/stateImport.js.map +1 -0
  37. package/dist/config/tunnelConfig.js +23 -0
  38. package/dist/config/tunnelConfig.js.map +1 -0
  39. package/dist/errors.js +82 -0
  40. package/dist/errors.js.map +1 -0
  41. package/dist/evaluation/contracts.js +86 -0
  42. package/dist/evaluation/contracts.js.map +1 -0
  43. package/dist/evaluation/dodoBench.js +69 -0
  44. package/dist/evaluation/dodoBench.js.map +1 -0
  45. package/dist/index.js +26 -0
  46. package/dist/index.js.map +1 -0
  47. package/dist/ipc/authentication.js +117 -0
  48. package/dist/ipc/authentication.js.map +1 -0
  49. package/dist/ipc/client.js +86 -0
  50. package/dist/ipc/client.js.map +1 -0
  51. package/dist/ipc/installationClient.js +83 -0
  52. package/dist/ipc/installationClient.js.map +1 -0
  53. package/dist/ipc/ownerControl.js +46 -0
  54. package/dist/ipc/ownerControl.js.map +1 -0
  55. package/dist/ipc/protocol.js +2 -0
  56. package/dist/ipc/protocol.js.map +1 -0
  57. package/dist/ipc/server.js +108 -0
  58. package/dist/ipc/server.js.map +1 -0
  59. package/dist/platform/execResolve.js +64 -0
  60. package/dist/platform/execResolve.js.map +1 -0
  61. package/dist/platform/fileIdentity.js +26 -0
  62. package/dist/platform/fileIdentity.js.map +1 -0
  63. package/dist/platform/fsRetry.js +22 -0
  64. package/dist/platform/fsRetry.js.map +1 -0
  65. package/dist/platform/pathPolicy.js +29 -0
  66. package/dist/platform/pathPolicy.js.map +1 -0
  67. package/dist/platform/privateFs.js +116 -0
  68. package/dist/platform/privateFs.js.map +1 -0
  69. package/dist/platform/processTree.js +31 -0
  70. package/dist/platform/processTree.js.map +1 -0
  71. package/dist/platform/shell.js +74 -0
  72. package/dist/platform/shell.js.map +1 -0
  73. package/dist/platform/speech.js +94 -0
  74. package/dist/platform/speech.js.map +1 -0
  75. package/dist/platform/system.js +28 -0
  76. package/dist/platform/system.js.map +1 -0
  77. package/dist/platform/windowsSandbox.js +129 -0
  78. package/dist/platform/windowsSandbox.js.map +1 -0
  79. package/dist/projects/federation.js +312 -0
  80. package/dist/projects/federation.js.map +1 -0
  81. package/dist/projects/registry.js +291 -0
  82. package/dist/projects/registry.js.map +1 -0
  83. package/dist/security/accessMode.js +19 -0
  84. package/dist/security/accessMode.js.map +1 -0
  85. package/dist/security/approvals.js +56 -0
  86. package/dist/security/approvals.js.map +1 -0
  87. package/dist/security/env.js +57 -0
  88. package/dist/security/env.js.map +1 -0
  89. package/dist/security/mutationQueue.js +78 -0
  90. package/dist/security/mutationQueue.js.map +1 -0
  91. package/dist/security/policy.js +22 -0
  92. package/dist/security/policy.js.map +1 -0
  93. package/dist/security/projectAuthority.js +22 -0
  94. package/dist/security/projectAuthority.js.map +1 -0
  95. package/dist/security/redact.js +30 -0
  96. package/dist/security/redact.js.map +1 -0
  97. package/dist/server/aiAdmin.js +201 -0
  98. package/dist/server/aiAdmin.js.map +1 -0
  99. package/dist/server/appServer.js +461 -0
  100. package/dist/server/appServer.js.map +1 -0
  101. package/dist/server/bootstrap.js +185 -0
  102. package/dist/server/bootstrap.js.map +1 -0
  103. package/dist/server/configUi/app.css +347 -0
  104. package/dist/server/configUi/app.js +958 -0
  105. package/dist/server/configUi/index.html +379 -0
  106. package/dist/server/configUi/ui/alerts.js +92 -0
  107. package/dist/server/configUi/ui/dom.js +19 -0
  108. package/dist/server/configUi/ui/tooltips.js +115 -0
  109. package/dist/server/configUi/vendor/VERSION.txt +1 -0
  110. package/dist/server/configUi/vendor/sweetalert2.min.css +1 -0
  111. package/dist/server/configUi/vendor/sweetalert2.min.js +5 -0
  112. package/dist/server/configUi/workbench.css +62 -0
  113. package/dist/server/configUi/workbench.js +394 -0
  114. package/dist/server/installationRuntime.js +155 -0
  115. package/dist/server/installationRuntime.js.map +1 -0
  116. package/dist/server/instructions.js +17 -0
  117. package/dist/server/instructions.js.map +1 -0
  118. package/dist/server/ipcDispatch.js +225 -0
  119. package/dist/server/ipcDispatch.js.map +1 -0
  120. package/dist/server/localConfig.js +561 -0
  121. package/dist/server/localConfig.js.map +1 -0
  122. package/dist/server/optionalServices.js +30 -0
  123. package/dist/server/optionalServices.js.map +1 -0
  124. package/dist/server/projectLease.js +46 -0
  125. package/dist/server/projectLease.js.map +1 -0
  126. package/dist/server/stdioServer.js +75 -0
  127. package/dist/server/stdioServer.js.map +1 -0
  128. package/dist/server/version.js +13 -0
  129. package/dist/server/version.js.map +1 -0
  130. package/dist/server/workspaceHost.js +194 -0
  131. package/dist/server/workspaceHost.js.map +1 -0
  132. package/dist/services/agent/agentService.js +598 -0
  133. package/dist/services/agent/agentService.js.map +1 -0
  134. package/dist/services/agent/contracts.js +102 -0
  135. package/dist/services/agent/contracts.js.map +1 -0
  136. package/dist/services/ai/adapters.js +287 -0
  137. package/dist/services/ai/adapters.js.map +1 -0
  138. package/dist/services/ai/contracts.js +39 -0
  139. package/dist/services/ai/contracts.js.map +1 -0
  140. package/dist/services/ai/credentials.js +77 -0
  141. package/dist/services/ai/credentials.js.map +1 -0
  142. package/dist/services/ai/network.js +113 -0
  143. package/dist/services/ai/network.js.map +1 -0
  144. package/dist/services/ai/settings.js +178 -0
  145. package/dist/services/ai/settings.js.map +1 -0
  146. package/dist/services/ai/subagents.js +468 -0
  147. package/dist/services/ai/subagents.js.map +1 -0
  148. package/dist/services/assistance/analysis.js +352 -0
  149. package/dist/services/assistance/analysis.js.map +1 -0
  150. package/dist/services/assistance/contracts.js +54 -0
  151. package/dist/services/assistance/contracts.js.map +1 -0
  152. package/dist/services/assistance/verification.js +274 -0
  153. package/dist/services/assistance/verification.js.map +1 -0
  154. package/dist/services/brain/brainParser.js +97 -0
  155. package/dist/services/brain/brainParser.js.map +1 -0
  156. package/dist/services/brain/brainService.js +733 -0
  157. package/dist/services/brain/brainService.js.map +1 -0
  158. package/dist/services/brain/brainWorker.js +212 -0
  159. package/dist/services/brain/brainWorker.js.map +1 -0
  160. package/dist/services/brain/contracts.js +154 -0
  161. package/dist/services/brain/contracts.js.map +1 -0
  162. package/dist/services/brain/protocol.js +2 -0
  163. package/dist/services/brain/protocol.js.map +1 -0
  164. package/dist/services/changes/applier.js +544 -0
  165. package/dist/services/changes/applier.js.map +1 -0
  166. package/dist/services/changes/contentBudget.js +11 -0
  167. package/dist/services/changes/contentBudget.js.map +1 -0
  168. package/dist/services/changes/planner.js +284 -0
  169. package/dist/services/changes/planner.js.map +1 -0
  170. package/dist/services/changes/types.js +3 -0
  171. package/dist/services/changes/types.js.map +1 -0
  172. package/dist/services/context/contextEngine.js +746 -0
  173. package/dist/services/context/contextEngine.js.map +1 -0
  174. package/dist/services/context/contracts.js +88 -0
  175. package/dist/services/context/contracts.js.map +1 -0
  176. package/dist/services/desktop/desktopPolicy.js +76 -0
  177. package/dist/services/desktop/desktopPolicy.js.map +1 -0
  178. package/dist/services/desktop/desktopService.js +130 -0
  179. package/dist/services/desktop/desktopService.js.map +1 -0
  180. package/dist/services/desktop/nativeBackend.js +174 -0
  181. package/dist/services/desktop/nativeBackend.js.map +1 -0
  182. package/dist/services/desktop/protocol.js +32 -0
  183. package/dist/services/desktop/protocol.js.map +1 -0
  184. package/dist/services/files/readService.js +136 -0
  185. package/dist/services/files/readService.js.map +1 -0
  186. package/dist/services/git/gitService.js +369 -0
  187. package/dist/services/git/gitService.js.map +1 -0
  188. package/dist/services/intelligence/intelService.js +136 -0
  189. package/dist/services/intelligence/intelService.js.map +1 -0
  190. package/dist/services/intelligence/protocol.js +2 -0
  191. package/dist/services/intelligence/protocol.js.map +1 -0
  192. package/dist/services/intelligence/tsWorker.js +419 -0
  193. package/dist/services/intelligence/tsWorker.js.map +1 -0
  194. package/dist/services/jobs/commandInput.js +34 -0
  195. package/dist/services/jobs/commandInput.js.map +1 -0
  196. package/dist/services/jobs/jobManager.js +398 -0
  197. package/dist/services/jobs/jobManager.js.map +1 -0
  198. package/dist/services/jobs/sandbox.js +156 -0
  199. package/dist/services/jobs/sandbox.js.map +1 -0
  200. package/dist/services/jobs/sandboxWiring.js +37 -0
  201. package/dist/services/jobs/sandboxWiring.js.map +1 -0
  202. package/dist/services/jobs/spool.js +112 -0
  203. package/dist/services/jobs/spool.js.map +1 -0
  204. package/dist/services/jobs/windowsSandboxChild.js +24 -0
  205. package/dist/services/jobs/windowsSandboxChild.js.map +1 -0
  206. package/dist/services/jobs/windowsSandboxWorker.js +155 -0
  207. package/dist/services/jobs/windowsSandboxWorker.js.map +1 -0
  208. package/dist/services/lsp/documentUri.js +16 -0
  209. package/dist/services/lsp/documentUri.js.map +1 -0
  210. package/dist/services/lsp/jsonrpc.js +382 -0
  211. package/dist/services/lsp/jsonrpc.js.map +1 -0
  212. package/dist/services/lsp/lspService.js +1051 -0
  213. package/dist/services/lsp/lspService.js.map +1 -0
  214. package/dist/services/memory/contracts.js +94 -0
  215. package/dist/services/memory/contracts.js.map +1 -0
  216. package/dist/services/memory/memoryService.js +683 -0
  217. package/dist/services/memory/memoryService.js.map +1 -0
  218. package/dist/services/multimodal/browserService.js +407 -0
  219. package/dist/services/multimodal/browserService.js.map +1 -0
  220. package/dist/services/multimodal/byteRange.js +31 -0
  221. package/dist/services/multimodal/byteRange.js.map +1 -0
  222. package/dist/services/multimodal/contracts.js +59 -0
  223. package/dist/services/multimodal/contracts.js.map +1 -0
  224. package/dist/services/multimodal/gameService.js +95 -0
  225. package/dist/services/multimodal/gameService.js.map +1 -0
  226. package/dist/services/multimodal/images.js +107 -0
  227. package/dist/services/multimodal/images.js.map +1 -0
  228. package/dist/services/multimodal/mediaService.js +279 -0
  229. package/dist/services/multimodal/mediaService.js.map +1 -0
  230. package/dist/services/multimodal/mediaWorker.js +143 -0
  231. package/dist/services/multimodal/mediaWorker.js.map +1 -0
  232. package/dist/services/multimodal/multimodalService.js +51 -0
  233. package/dist/services/multimodal/multimodalService.js.map +1 -0
  234. package/dist/services/multimodal/network.js +69 -0
  235. package/dist/services/multimodal/network.js.map +1 -0
  236. package/dist/services/multimodal/operations.js +40 -0
  237. package/dist/services/multimodal/operations.js.map +1 -0
  238. package/dist/services/multimodal/storage.js +160 -0
  239. package/dist/services/multimodal/storage.js.map +1 -0
  240. package/dist/services/multimodal/workflowService.js +179 -0
  241. package/dist/services/multimodal/workflowService.js.map +1 -0
  242. package/dist/services/overview.js +284 -0
  243. package/dist/services/overview.js.map +1 -0
  244. package/dist/services/resources/casStore.js +304 -0
  245. package/dist/services/resources/casStore.js.map +1 -0
  246. package/dist/services/resources/contracts.js +75 -0
  247. package/dist/services/resources/contracts.js.map +1 -0
  248. package/dist/services/resources/mime.js +77 -0
  249. package/dist/services/resources/mime.js.map +1 -0
  250. package/dist/services/resources/providerRegistry.js +22 -0
  251. package/dist/services/resources/providerRegistry.js.map +1 -0
  252. package/dist/services/resources/providers.js +120 -0
  253. package/dist/services/resources/providers.js.map +1 -0
  254. package/dist/services/resources/resourceService.js +282 -0
  255. package/dist/services/resources/resourceService.js.map +1 -0
  256. package/dist/services/runtime/contracts.js +65 -0
  257. package/dist/services/runtime/contracts.js.map +1 -0
  258. package/dist/services/runtime/runtimeService.js +364 -0
  259. package/dist/services/runtime/runtimeService.js.map +1 -0
  260. package/dist/services/schedules/scheduleService.js +198 -0
  261. package/dist/services/schedules/scheduleService.js.map +1 -0
  262. package/dist/services/search/searchService.js +434 -0
  263. package/dist/services/search/searchService.js.map +1 -0
  264. package/dist/services/search/searchWorker.js +58 -0
  265. package/dist/services/search/searchWorker.js.map +1 -0
  266. package/dist/setup/componentProbes.js +259 -0
  267. package/dist/setup/componentProbes.js.map +1 -0
  268. package/dist/setup/download.js +139 -0
  269. package/dist/setup/download.js.map +1 -0
  270. package/dist/setup/managedTools.js +118 -0
  271. package/dist/setup/managedTools.js.map +1 -0
  272. package/dist/setup/sandboxProbe.js +172 -0
  273. package/dist/setup/sandboxProbe.js.map +1 -0
  274. package/dist/setup/setup.js +522 -0
  275. package/dist/setup/setup.js.map +1 -0
  276. package/dist/setup/windowsChromiumAcl.js +56 -0
  277. package/dist/setup/windowsChromiumAcl.js.map +1 -0
  278. package/dist/setup/windowsSandboxInstall.js +94 -0
  279. package/dist/setup/windowsSandboxInstall.js.map +1 -0
  280. package/dist/setup/windowsSpeechInstall.js +86 -0
  281. package/dist/setup/windowsSpeechInstall.js.map +1 -0
  282. package/dist/store/db.js +711 -0
  283. package/dist/store/db.js.map +1 -0
  284. package/dist/store/scopeEncoding.js +15 -0
  285. package/dist/store/scopeEncoding.js.map +1 -0
  286. package/dist/store/store.js +467 -0
  287. package/dist/store/store.js.map +1 -0
  288. package/dist/tools/agentRuntimeTools.js +216 -0
  289. package/dist/tools/agentRuntimeTools.js.map +1 -0
  290. package/dist/tools/agentTools.js +317 -0
  291. package/dist/tools/agentTools.js.map +1 -0
  292. package/dist/tools/assistanceTools.js +126 -0
  293. package/dist/tools/assistanceTools.js.map +1 -0
  294. package/dist/tools/brainTools.js +130 -0
  295. package/dist/tools/brainTools.js.map +1 -0
  296. package/dist/tools/catalog.js +30 -0
  297. package/dist/tools/catalog.js.map +1 -0
  298. package/dist/tools/changeTools.js +188 -0
  299. package/dist/tools/changeTools.js.map +1 -0
  300. package/dist/tools/context.js +243 -0
  301. package/dist/tools/context.js.map +1 -0
  302. package/dist/tools/contextTools.js +57 -0
  303. package/dist/tools/contextTools.js.map +1 -0
  304. package/dist/tools/coreCatalog.js +82 -0
  305. package/dist/tools/coreCatalog.js.map +1 -0
  306. package/dist/tools/desktopTools.js +41 -0
  307. package/dist/tools/desktopTools.js.map +1 -0
  308. package/dist/tools/directTools.js +273 -0
  309. package/dist/tools/directTools.js.map +1 -0
  310. package/dist/tools/envelope.js +71 -0
  311. package/dist/tools/envelope.js.map +1 -0
  312. package/dist/tools/extraTools.js +298 -0
  313. package/dist/tools/extraTools.js.map +1 -0
  314. package/dist/tools/gitTools.js +93 -0
  315. package/dist/tools/gitTools.js.map +1 -0
  316. package/dist/tools/jobTools.js +454 -0
  317. package/dist/tools/jobTools.js.map +1 -0
  318. package/dist/tools/memoryTools.js +102 -0
  319. package/dist/tools/memoryTools.js.map +1 -0
  320. package/dist/tools/metaTools.js +153 -0
  321. package/dist/tools/metaTools.js.map +1 -0
  322. package/dist/tools/multimodalTools.js +262 -0
  323. package/dist/tools/multimodalTools.js.map +1 -0
  324. package/dist/tools/projectTools.js +261 -0
  325. package/dist/tools/projectTools.js.map +1 -0
  326. package/dist/tools/resourceTools.js +81 -0
  327. package/dist/tools/resourceTools.js.map +1 -0
  328. package/dist/tools/runtimeTools.js +137 -0
  329. package/dist/tools/runtimeTools.js.map +1 -0
  330. package/dist/tools/scheduleTools.js +10 -0
  331. package/dist/tools/scheduleTools.js.map +1 -0
  332. package/dist/tools/semanticTools.js +141 -0
  333. package/dist/tools/semanticTools.js.map +1 -0
  334. package/dist/tools/subagentTools.js +32 -0
  335. package/dist/tools/subagentTools.js.map +1 -0
  336. package/dist/tools/surface.js +644 -0
  337. package/dist/tools/surface.js.map +1 -0
  338. package/dist/tunnel/control.js +113 -0
  339. package/dist/tunnel/control.js.map +1 -0
  340. package/dist/tunnel/credentials.js +392 -0
  341. package/dist/tunnel/credentials.js.map +1 -0
  342. package/dist/tunnel/log.js +84 -0
  343. package/dist/tunnel/log.js.map +1 -0
  344. package/dist/tunnel/runtime.js +55 -0
  345. package/dist/tunnel/runtime.js.map +1 -0
  346. package/dist/tunnel/supervisor.js +280 -0
  347. package/dist/tunnel/supervisor.js.map +1 -0
  348. package/dist/util/bytes.js +53 -0
  349. package/dist/util/bytes.js.map +1 -0
  350. package/dist/util/hash.js +53 -0
  351. package/dist/util/hash.js.map +1 -0
  352. package/dist/workspace/fs.js +305 -0
  353. package/dist/workspace/fs.js.map +1 -0
  354. package/dist/workspace/identity.js +17 -0
  355. package/dist/workspace/identity.js.map +1 -0
  356. package/dist/workspace/ignores.js +173 -0
  357. package/dist/workspace/ignores.js.map +1 -0
  358. package/dist/workspace/root.js +59 -0
  359. package/dist/workspace/root.js.map +1 -0
  360. package/docs/AGENT_RUNTIME.md +154 -0
  361. package/docs/AI_PROVIDERS.md +110 -0
  362. package/docs/ARCHITECTURE.md +331 -0
  363. package/docs/ASSISTANCE.md +31 -0
  364. package/docs/AUTH.md +59 -0
  365. package/docs/BRAIN.md +92 -0
  366. package/docs/COMPATIBILITY.md +92 -0
  367. package/docs/CONFIRMATION.md +13 -0
  368. package/docs/CONTEXT.md +87 -0
  369. package/docs/DESKTOP.md +35 -0
  370. package/docs/EVALUATION.md +86 -0
  371. package/docs/LARGE_INPUTS.md +21 -0
  372. package/docs/LSP.md +21 -0
  373. package/docs/MANUAL_ACCEPTANCE.md +283 -0
  374. package/docs/MEMORY.md +128 -0
  375. package/docs/MULTIMODAL.md +32 -0
  376. package/docs/PROJECTS.md +146 -0
  377. package/docs/RELEASE_1.0.0.md +111 -0
  378. package/docs/RELEASE_NOTES.md +92 -0
  379. package/docs/RESOURCES.md +83 -0
  380. package/docs/RUNTIME.md +125 -0
  381. package/docs/SCHEDULES.md +23 -0
  382. package/docs/SECURITY.md +250 -0
  383. package/docs/TEST_REPORT.md +312 -0
  384. package/docs/TUNNEL.md +86 -0
  385. package/docs/WEB_CLIENTS.md +43 -0
  386. package/docs/WINDOWS.md +79 -0
  387. package/docs/adr/001-one-process-one-workspace.md +22 -0
  388. package/docs/adr/002-external-tunnel.md +19 -0
  389. package/docs/adr/003-sdk-v2-legacy-stateless.md +20 -0
  390. package/docs/adr/004-embedded-oauth-provider.md +19 -0
  391. package/docs/adr/005-static-client-baseline.md +19 -0
  392. package/docs/adr/006-guarded-fs-explicit-exec.md +16 -0
  393. package/docs/adr/007-preview-hash-journal.md +18 -0
  394. package/docs/adr/008-ts-js-semantics-first.md +17 -0
  395. package/docs/adr/009-jobs-polling.md +18 -0
  396. package/docs/adr/010-sqlite-better-sqlite3.md +18 -0
  397. package/docs/adr/011-env-example-secure-default.md +19 -0
  398. package/docs/adr/012-workspace-scope-grant.md +24 -0
  399. package/docs/adr/013-loopback-proxy-trust.md +18 -0
  400. package/docs/adr/014-direct-coding-tier.md +48 -0
  401. package/docs/adr/015-coding-agent-parity.md +56 -0
  402. package/docs/adr/016-oidc-openid-consent.md +15 -0
  403. package/docs/adr/017-installation-identity-local-config.md +44 -0
  404. package/docs/adr/018-access-scope-encoding.md +11 -0
  405. package/docs/adr/019-runtime-workspace-switch.md +59 -0
  406. package/docs/adr/020-desktop-access.md +50 -0
  407. package/docs/adr/021-persistent-desktop-consent.md +39 -0
  408. package/docs/adr/022-owner-kill-preserves-login.md +30 -0
  409. package/docs/adr/023-large-coding-inputs.md +49 -0
  410. package/docs/adr/024-usage-rounds-and-schedules.md +72 -0
  411. package/docs/adr/025-owner-chat-whitelist.md +53 -0
  412. package/docs/adr/026-remove-conversation-confirmation.md +35 -0
  413. package/docs/adr/027-workspace-client-list.md +20 -0
  414. package/docs/adr/028-owner-client-deletion.md +25 -0
  415. package/docs/adr/029-compact-tool-surface.md +121 -0
  416. package/docs/adr/030-windows-native-candidate.md +71 -0
  417. package/docs/adr/031-owner-selected-tunnel-supervision.md +13 -0
  418. package/docs/adr/032-owner-project-registry.md +43 -0
  419. package/docs/adr/033-read-only-project-federation.md +61 -0
  420. package/docs/adr/034-universal-resource-cas.md +60 -0
  421. package/docs/adr/035-project-brain-incremental-index.md +51 -0
  422. package/docs/adr/036-context-engine-evidence.md +53 -0
  423. package/docs/adr/037-owner-reviewed-memory.md +46 -0
  424. package/docs/adr/038-runtime-intelligence.md +46 -0
  425. package/docs/adr/039-advanced-agent-runtime.md +57 -0
  426. package/docs/adr/040-dodobench-release-gate.md +43 -0
  427. package/docs/adr/041-local-macos-docker-linux-gates.md +32 -0
  428. package/docs/adr/042-project-directory-generation-identity.md +36 -0
  429. package/docs/adr/043-trusted-self-hosted-platform-gates.md +38 -0
  430. package/docs/adr/044-global-launcher-and-cli-menu.md +43 -0
  431. package/docs/adr/045-ai-providers-multiproject.md +56 -0
  432. package/docs/adr/046-personal-access-mode.md +32 -0
  433. package/docs/adr/README.md +64 -0
  434. package/examples/.dodo.json.example +20 -0
  435. package/examples/cloudflared.yml +23 -0
  436. package/examples/reverse-proxy-fixture.mjs +37 -0
  437. package/native/desktop-linux.py +387 -0
  438. package/native/desktop-windows.cs +180 -0
  439. package/native/desktop.swift +212 -0
  440. package/package.json +145 -0
  441. package/schemas/global-config.schema.json +497 -0
  442. package/schemas/project-config.schema.json +75 -0
  443. package/schemas/tools.compact.json +3236 -0
  444. package/schemas/tools.hybrid.json +8194 -0
  445. package/schemas/tools.json +34518 -0
  446. package/scripts/guard-publish.mjs +12 -0
  447. package/scripts/setup-multimodal.mjs +70 -0
@@ -0,0 +1,598 @@
1
+ import { parsePatch } from 'diff';
2
+ import { DodoError } from '../../errors.js';
3
+ import { redact } from '../../security/redact.js';
4
+ import { digestOf, newId } from '../../util/hash.js';
5
+ import { actorKey, liveAccess } from '../multimodal/storage.js';
6
+ import { AGENT_SCHEMA_VERSION, AgentCapabilities, AgentHypothesis, AgentIntent, AgentPlan, AgentRun, AgentSkillDetail, AgentSkillSummary, AgentSnapshot, } from './contracts.js';
7
+ const MAX_OPEN_RUNS = 16;
8
+ const MAX_RETAINED_RUNS = 100;
9
+ const MAX_PLAN_STEPS = 100;
10
+ const MAX_INTENTS = 128;
11
+ const MAX_SNAPSHOT_FILES = 10_000;
12
+ const RUN_RETENTION_MS = 7 * 24 * 60 * 60 * 1000;
13
+ const SKILL_PROPOSAL_TTL_MS = 24 * 60 * 60 * 1000;
14
+ function parseObject(value, fallback) {
15
+ try {
16
+ return JSON.parse(value);
17
+ }
18
+ catch {
19
+ return fallback;
20
+ }
21
+ }
22
+ function sameProgram(a, b) {
23
+ return process.platform === 'win32' ? a.toLowerCase() === b.toLowerCase() : a === b;
24
+ }
25
+ function withinPrefix(path, prefix) {
26
+ if (prefix === '.')
27
+ return true;
28
+ const left = process.platform === 'win32' ? path.toLowerCase() : path;
29
+ const right = process.platform === 'win32' ? prefix.toLowerCase() : prefix;
30
+ return left === right || left.startsWith(`${right}/`);
31
+ }
32
+ function pathsOverlap(a, b) {
33
+ return withinPrefix(a, b) || withinPrefix(b, a);
34
+ }
35
+ /** Durable coordination state. It narrows an existing principal; it never grants authority or executes skills. */
36
+ export class AgentRuntimeService {
37
+ services;
38
+ closed = false;
39
+ constructor(services) {
40
+ this.services = services;
41
+ this.recoverOnBoot();
42
+ }
43
+ close() { this.closed = true; }
44
+ open(ctx, input) {
45
+ this.check();
46
+ liveAccess(ctx, 'dodo:write');
47
+ this.expire();
48
+ const owner = actorKey(ctx.principal);
49
+ const open = this.services.store.db.prepare("SELECT COUNT(*) AS n FROM agent_runs WHERE workspace_id=? AND owner=? AND status IN ('ACTIVE','PAUSED','RECOVERY_REQUIRED')").get(this.services.workspaceId, owner).n;
50
+ if (open >= MAX_OPEN_RUNS)
51
+ throw new DodoError('RESOURCE_LIMIT', `at most ${MAX_OPEN_RUNS} open agent runs per client and workspace`);
52
+ const retained = this.services.store.db.prepare('SELECT COUNT(*) AS n FROM agent_runs WHERE workspace_id=? AND owner=?').get(this.services.workspaceId, owner).n;
53
+ if (retained >= MAX_RETAINED_RUNS)
54
+ throw new DodoError('RESOURCE_LIMIT', `at most ${MAX_RETAINED_RUNS} retained agent runs per client and workspace`);
55
+ const capabilities = this.normalizeCapabilities(ctx, input.capabilities);
56
+ const now = Date.now(), id = newId('arun'), expiresAt = now + capabilities.maxWallMinutes * 60_000;
57
+ this.services.store.db.prepare(`INSERT INTO agent_runs(id,workspace_id,opened_epoch,owner,goal,criteria,capabilities,status,revision,action_count,created_at,updated_at,expires_at,completed_at)
58
+ VALUES (?,?,?,?,?,?,?,'ACTIVE',1,0,?,?,?,NULL)`).run(id, this.services.workspaceId, this.services.epoch, owner, input.goal, JSON.stringify(input.completionCriteria), JSON.stringify(capabilities), now, now, expiresAt);
59
+ return this.runData(this.runRow(id, owner));
60
+ }
61
+ status(ctx, runId) {
62
+ this.check();
63
+ liveAccess(ctx, 'dodo:read');
64
+ this.expire();
65
+ const owner = actorKey(ctx.principal), run = this.requireRun(runId, owner);
66
+ const planRow = this.services.store.db.prepare('SELECT revision,steps,content_hash,created_at FROM agent_plans WHERE run_id=? AND owner=? ORDER BY revision DESC LIMIT 1')
67
+ .get(run.id, owner);
68
+ const plan = planRow ? AgentPlan.parse({ runId: run.id, revision: planRow.revision, steps: parseObject(planRow.steps, []), contentHash: planRow.content_hash, createdAt: planRow.created_at, immutable: true }) : null;
69
+ const hypotheses = this.hypothesisRows(run.id, owner).map((row) => this.hypothesisData(row));
70
+ const intents = this.services.store.db.prepare("SELECT * FROM agent_intents WHERE run_id=? AND owner=? AND status='ACTIVE' ORDER BY created_at").all(run.id, owner).map((row) => this.intentData(row));
71
+ const latestJudgement = this.services.store.db.prepare('SELECT id,payload,content_hash,created_at FROM agent_judgements WHERE run_id=? AND owner=? ORDER BY created_at DESC LIMIT 1')
72
+ .get(run.id, owner);
73
+ const snapshots = this.services.store.db.prepare('SELECT COUNT(*) AS n FROM agent_snapshots WHERE run_id=? AND owner=?').get(run.id, owner).n;
74
+ return {
75
+ schemaVersion: AGENT_SCHEMA_VERSION, run: this.runData(run), plan, hypotheses, intents, snapshots,
76
+ latestJudgement: latestJudgement ? { judgementId: latestJudgement.id, ...parseObject(latestJudgement.payload, {}), contentHash: latestJudgement.content_hash, createdAt: latestJudgement.created_at } : null,
77
+ note: 'Coordinator state is durable guidance. Target tools, live OAuth/target authority, trust, approvals, path guards and command sandbox remain authoritative.',
78
+ };
79
+ }
80
+ setPlan(ctx, runId, expectedRevision, steps) {
81
+ this.check();
82
+ liveAccess(ctx, 'dodo:write');
83
+ const owner = actorKey(ctx.principal), run = this.requireActiveRun(runId, owner);
84
+ if (run.revision !== expectedRevision)
85
+ throw new DodoError('CONFLICT', 'agent run revision changed; read agent_run_status and retry', { detail: { expectedRevision, actualRevision: run.revision } });
86
+ if (steps.length < 1 || steps.length > MAX_PLAN_STEPS)
87
+ throw new DodoError('RESOURCE_LIMIT', `agent plan must contain 1..${MAX_PLAN_STEPS} steps`);
88
+ const parsed = AgentPlan.shape.steps.parse(steps);
89
+ this.validatePlan(parsed);
90
+ const revision = run.revision + 1, createdAt = Date.now(), contentHash = digestOf(parsed);
91
+ this.services.store.db.transaction(() => {
92
+ const changed = this.services.store.db.prepare('UPDATE agent_runs SET revision=?,updated_at=? WHERE id=? AND owner=? AND revision=?')
93
+ .run(revision, createdAt, run.id, owner, expectedRevision);
94
+ if (changed.changes !== 1)
95
+ throw new DodoError('CONFLICT', 'agent run revision changed while saving the plan');
96
+ this.services.store.db.prepare('INSERT INTO agent_plans(run_id,revision,workspace_id,owner,steps,content_hash,created_at) VALUES (?,?,?,?,?,?,?)')
97
+ .run(run.id, revision, this.services.workspaceId, owner, JSON.stringify(parsed), contentHash, createdAt);
98
+ })();
99
+ return AgentPlan.parse({ runId: run.id, revision, steps: parsed, contentHash, createdAt, immutable: true });
100
+ }
101
+ openHypothesis(ctx, runId, input) {
102
+ this.check();
103
+ liveAccess(ctx, 'dodo:write');
104
+ const owner = actorKey(ctx.principal), run = this.requireActiveRun(runId, owner), capabilities = this.capabilities(run);
105
+ const count = this.services.store.db.prepare('SELECT COUNT(*) AS n FROM agent_hypotheses WHERE run_id=? AND owner=?').get(run.id, owner).n;
106
+ if (count >= capabilities.maxHypotheses)
107
+ throw new DodoError('RESOURCE_LIMIT', `agent run allows at most ${capabilities.maxHypotheses} hypotheses`);
108
+ const now = Date.now(), id = newId('ahyp');
109
+ this.services.store.db.prepare(`INSERT INTO agent_hypotheses(id,run_id,workspace_id,owner,title,probable_cause,expected_evidence,status,created_at,updated_at)
110
+ VALUES (?,?,?,?,?,?,?,'ACTIVE',?,?)`).run(id, run.id, this.services.workspaceId, owner, input.title, input.probableCause, JSON.stringify(input.expectedEvidence), now, now);
111
+ return this.hypothesisData(this.hypothesisRow(run.id, id, owner));
112
+ }
113
+ acquireIntent(ctx, runId, hypothesisId, kind, rawKey, ttlMinutes) {
114
+ this.check();
115
+ liveAccess(ctx, 'dodo:write');
116
+ this.expire();
117
+ const owner = actorKey(ctx.principal), run = this.requireActiveRun(runId, owner);
118
+ this.requireActiveHypothesis(run.id, hypothesisId, owner);
119
+ const held = this.services.store.db.prepare("SELECT COUNT(*) AS n FROM agent_intents WHERE run_id=? AND owner=? AND status='ACTIVE'").get(run.id, owner).n;
120
+ if (held >= MAX_INTENTS)
121
+ throw new DodoError('RESOURCE_LIMIT', `at most ${MAX_INTENTS} active intents per agent run`);
122
+ const resourceKey = this.normalizeIntentKey(kind, rawKey);
123
+ const candidates = this.services.store.db.prepare("SELECT * FROM agent_intents WHERE workspace_id=? AND kind=? AND status='ACTIVE' AND expires_at>?")
124
+ .all(this.services.workspaceId, kind, Date.now());
125
+ const conflict = candidates.find((row) => row.hypothesis_id !== hypothesisId && (kind === 'path' ? pathsOverlap(row.resource_key, resourceKey) : row.resource_key === resourceKey));
126
+ if (conflict)
127
+ throw new DodoError('CONFLICT', `another active hypothesis holds an overlapping ${kind} intent`, { recovery: 'wait for release/expiry or use a separate non-overlapping path/resource' });
128
+ const existing = candidates.find((row) => row.hypothesis_id === hypothesisId && row.resource_key === resourceKey);
129
+ if (existing)
130
+ return this.intentData(existing);
131
+ const now = Date.now(), expiresAt = Math.min(run.expires_at, now + ttlMinutes * 60_000), id = newId('aintent');
132
+ this.services.store.db.prepare(`INSERT INTO agent_intents(id,run_id,hypothesis_id,workspace_id,owner,kind,resource_key,status,created_at,expires_at,released_at)
133
+ VALUES (?,?,?,?,?,?,?,'ACTIVE',?,?,NULL)`).run(id, run.id, hypothesisId, this.services.workspaceId, owner, kind, resourceKey, now, expiresAt);
134
+ return this.intentData(this.intentRow(id, owner));
135
+ }
136
+ releaseIntent(ctx, runId, hypothesisId, intentId) {
137
+ this.check();
138
+ liveAccess(ctx, 'dodo:write');
139
+ const owner = actorKey(ctx.principal);
140
+ this.requireRun(runId, owner);
141
+ this.requireHypothesis(runId, hypothesisId, owner);
142
+ const row = this.intentRow(intentId, owner);
143
+ if (!row || row.run_id !== runId || row.hypothesis_id !== hypothesisId)
144
+ throw new DodoError('NOT_FOUND', 'agent intent was not found for this run and hypothesis');
145
+ if (row.status === 'ACTIVE')
146
+ this.services.store.db.prepare("UPDATE agent_intents SET status='RELEASED',released_at=? WHERE id=? AND owner=?").run(Date.now(), row.id, owner);
147
+ return this.intentData(this.intentRow(row.id, owner));
148
+ }
149
+ beginOperation(ctx, runId, hypothesisId, operation, args, requiredScope) {
150
+ this.check();
151
+ liveAccess(ctx, requiredScope);
152
+ const owner = actorKey(ctx.principal), run = this.requireActiveRun(runId, owner), hypothesis = this.requireActiveHypothesis(run.id, hypothesisId, owner);
153
+ const capabilities = this.capabilities(run);
154
+ this.assertProjects(args, capabilities);
155
+ this.assertFeature(operation, args, capabilities);
156
+ if (requiredScope === 'dodo:write')
157
+ this.assertWritableOperation(ctx, run, hypothesis, operation, args, capabilities);
158
+ if (requiredScope === 'dodo:exec')
159
+ this.assertExecutableOperation(ctx, operation, args, capabilities);
160
+ const id = newId('aaction'), now = Date.now(), inputHash = digestOf({ operation, args });
161
+ this.services.store.db.transaction(() => {
162
+ const updated = this.services.store.db.prepare('UPDATE agent_runs SET action_count=action_count+1,updated_at=? WHERE id=? AND owner=? AND action_count<?')
163
+ .run(now, run.id, owner, capabilities.maxActions);
164
+ if (updated.changes !== 1)
165
+ throw new DodoError('RESOURCE_LIMIT', `agent run allows at most ${capabilities.maxActions} target actions`);
166
+ this.services.store.db.prepare(`INSERT INTO agent_actions(id,run_id,hypothesis_id,workspace_id,owner,operation,input_hash,status,result_code,result_hash,created_at,completed_at)
167
+ VALUES (?,?,?,?,?,?,?,'RUNNING',NULL,NULL,?,NULL)`).run(id, run.id, hypothesis.id, this.services.workspaceId, owner, operation, inputHash, now);
168
+ })();
169
+ return { actionId: id, runId: run.id, hypothesisId: hypothesis.id };
170
+ }
171
+ finishOperation(ticket, result) {
172
+ this.services.store.db.prepare("UPDATE agent_actions SET status=?,result_code=?,result_hash=?,completed_at=? WHERE id=? AND status='RUNNING'")
173
+ .run(result.ok ? 'COMPLETED' : 'FAILED', result.code ?? null, result.digest, Date.now(), ticket.actionId);
174
+ }
175
+ createSnapshot(ctx, runId, hypothesisId) {
176
+ this.check();
177
+ liveAccess(ctx, 'dodo:read');
178
+ const owner = actorKey(ctx.principal), run = this.requireActiveRun(runId, owner);
179
+ this.requireActiveHypothesis(run.id, hypothesisId, owner);
180
+ const manifest = this.buildManifest(), manifestHash = digestOf(manifest.files), id = newId('asnap'), now = Date.now();
181
+ this.services.store.db.prepare('INSERT INTO agent_snapshots(id,run_id,hypothesis_id,workspace_id,owner,manifest_hash,manifest,created_at) VALUES (?,?,?,?,?,?,?,?)')
182
+ .run(id, run.id, hypothesisId, this.services.workspaceId, owner, manifestHash, JSON.stringify(manifest), now);
183
+ return AgentSnapshot.parse({ snapshotId: id, runId: run.id, hypothesisId, manifestHash, fileCount: manifest.files.length, truncated: manifest.truncated, createdAt: now, storesFileContents: false });
184
+ }
185
+ compareSnapshot(ctx, runId, hypothesisId, snapshotId) {
186
+ this.check();
187
+ liveAccess(ctx, 'dodo:read');
188
+ const owner = actorKey(ctx.principal);
189
+ this.requireRun(runId, owner);
190
+ this.requireHypothesis(runId, hypothesisId, owner);
191
+ const { row, manifest: before } = this.requireSnapshot(runId, hypothesisId, snapshotId, owner), after = this.buildManifest();
192
+ const old = new Map(before.files.map((file) => [file.path, file])), current = new Map(after.files.map((file) => [file.path, file]));
193
+ const added = [], removed = [], modified = [];
194
+ for (const [file, stat] of current) {
195
+ const prior = old.get(file);
196
+ if (!prior)
197
+ added.push(file);
198
+ else if (digestOf(prior) !== digestOf(stat))
199
+ modified.push(file);
200
+ }
201
+ for (const file of old.keys())
202
+ if (!current.has(file))
203
+ removed.push(file);
204
+ const candidates = this.rollbackCandidates(before, ctx.principal.grantId);
205
+ const total = added.length + removed.length + modified.length, max = 100;
206
+ return {
207
+ snapshot: AgentSnapshot.parse({ snapshotId: row.id, runId, hypothesisId, manifestHash: row.manifest_hash, fileCount: before.files.length, truncated: before.truncated, createdAt: row.created_at, storesFileContents: false }),
208
+ currentManifestHash: digestOf(after.files), changed: total > 0, changes: { added: added.slice(0, max), removed: removed.slice(0, max), modified: modified.slice(0, max), total, truncated: total > max || before.truncated || after.truncated },
209
+ rollbackCandidates: candidates,
210
+ note: 'Snapshot comparison uses guarded metadata. Rollback candidates still require agent_snapshot_rollback and the original rollback_changes conflict/approval checks.',
211
+ };
212
+ }
213
+ assertSnapshotRollback(ctx, runId, hypothesisId, snapshotId, changesetId) {
214
+ this.check();
215
+ liveAccess(ctx, 'dodo:write');
216
+ const owner = actorKey(ctx.principal), run = this.requireActiveRun(runId, owner), hypothesis = this.requireActiveHypothesis(run.id, hypothesisId, owner);
217
+ const { manifest } = this.requireSnapshot(run.id, hypothesis.id, snapshotId, owner);
218
+ if (!this.rollbackCandidates(manifest, ctx.principal.grantId).some((item) => item.changesetId === changesetId))
219
+ throw new DodoError('CONFLICT', 'changeset is not a caller-owned committed rollback candidate created after this snapshot');
220
+ const paths = this.services.store.listJournalSteps(changesetId).flatMap((step) => [step.path, ...(step.destPath ? [step.destPath] : [])]);
221
+ this.assertAllowedPaths(paths, this.capabilities(run));
222
+ this.assertHeldLocks(run.id, hypothesis.id, owner, paths);
223
+ }
224
+ async judge(ctx, runId, results) {
225
+ this.check();
226
+ liveAccess(ctx, 'dodo:write');
227
+ const owner = actorKey(ctx.principal), run = this.requireActiveRun(runId, owner);
228
+ const runtime = this.services.runtime;
229
+ if (!runtime)
230
+ throw new DodoError('NOT_SUPPORTED', 'Runtime Intelligence is required to judge hypotheses');
231
+ const seen = new Set(), normalized = [];
232
+ for (const result of results) {
233
+ if (seen.has(result.hypothesisId))
234
+ throw new DodoError('INVALID_INPUT', 'duplicate hypothesis in judge input');
235
+ seen.add(result.hypothesisId);
236
+ const hypothesis = this.requireActiveHypothesis(run.id, result.hypothesisId, owner);
237
+ if (result.evidence.length < 1)
238
+ throw new DodoError('INVALID_INPUT', 'each hypothesis judgement requires current runtime evidence');
239
+ const evidenceIds = [];
240
+ for (const ref of result.evidence) {
241
+ const evidence = await runtime.evidence(ctx, ref.sessionId, ref.evidenceId, true);
242
+ if (evidence.status !== 'CURRENT')
243
+ throw new DodoError('CONFLICT', `evidence ${ref.evidenceId} is ${evidence.status}; collect current evidence before judging`);
244
+ evidenceIds.push(evidence.evidenceId);
245
+ }
246
+ normalized.push({ hypothesisId: hypothesis.id, verdict: result.verdict, score: result.score, rationale: redact(result.rationale), evidenceIds });
247
+ }
248
+ const ranked = [...normalized].sort((a, b) => Number(b['score']) - Number(a['score']) || String(a['hypothesisId']).localeCompare(String(b['hypothesisId'])));
249
+ const winner = ranked.find((item) => item['verdict'] === 'passed') ?? null, now = Date.now(), id = newId('ajudge');
250
+ this.services.store.db.transaction(() => {
251
+ for (const item of normalized)
252
+ this.services.store.db.prepare('UPDATE agent_hypotheses SET status=?,updated_at=? WHERE id=? AND run_id=? AND owner=?')
253
+ .run(String(item['verdict']).toUpperCase(), now, item['hypothesisId'], run.id, owner);
254
+ const payload = { results: normalized, winnerHypothesisId: winner?.['hypothesisId'] ?? null, deterministicOrder: ranked.map((item) => item['hypothesisId']), authority: 'evidence_only' };
255
+ const contentHash = digestOf(payload);
256
+ this.services.store.db.prepare('INSERT INTO agent_judgements(id,run_id,workspace_id,owner,payload,content_hash,created_at) VALUES (?,?,?,?,?,?,?)')
257
+ .run(id, run.id, this.services.workspaceId, owner, JSON.stringify(payload), contentHash, now);
258
+ })();
259
+ const row = this.services.store.db.prepare('SELECT payload,content_hash,created_at FROM agent_judgements WHERE id=?').get(id);
260
+ return { judgementId: id, ...parseObject(row.payload, {}), contentHash: row.content_hash, createdAt: row.created_at };
261
+ }
262
+ async control(ctx, runId, input) {
263
+ this.check();
264
+ liveAccess(ctx, 'dodo:write');
265
+ this.expire();
266
+ const owner = actorKey(ctx.principal), run = this.requireRun(runId, owner), now = Date.now();
267
+ if (input.action === 'pause') {
268
+ if (run.status !== 'ACTIVE')
269
+ throw new DodoError('CONFLICT', `only an ACTIVE run can pause (current ${run.status})`);
270
+ this.updateRunStatus(run, owner, 'PAUSED');
271
+ }
272
+ else if (input.action === 'resume') {
273
+ if (run.status !== 'PAUSED')
274
+ throw new DodoError('CONFLICT', `only a PAUSED run can resume (current ${run.status})`);
275
+ this.updateRunStatus(run, owner, 'ACTIVE');
276
+ }
277
+ else if (input.action === 'recover') {
278
+ if (run.status !== 'RECOVERY_REQUIRED')
279
+ throw new DodoError('CONFLICT', `only a RECOVERY_REQUIRED run can recover (current ${run.status})`);
280
+ this.services.store.db.prepare("UPDATE agent_runs SET status='ACTIVE',opened_epoch=?,updated_at=? WHERE id=? AND owner=?").run(this.services.epoch, now, run.id, owner);
281
+ }
282
+ else if (input.action === 'cancel') {
283
+ if (['COMPLETED', 'CANCELED', 'EXPIRED'].includes(run.status))
284
+ throw new DodoError('CONFLICT', `run is already ${run.status}`);
285
+ this.services.store.db.transaction(() => { this.updateRunStatus(run, owner, 'CANCELED', now); this.releaseRunIntents(run.id, owner, now); })();
286
+ }
287
+ else {
288
+ if (!['ACTIVE', 'PAUSED'].includes(run.status))
289
+ throw new DodoError('CONFLICT', `run cannot complete from ${run.status}`);
290
+ const criteria = parseObject(run.criteria, []), results = input.criteriaResults ?? [];
291
+ if (results.length !== criteria.length || criteria.some((criterion) => !results.some((result) => result.criterion === criterion && result.passed))) {
292
+ throw new DodoError('CONFLICT', 'every exact completion criterion must have a passed result before completing the run');
293
+ }
294
+ const runtime = this.services.runtime;
295
+ if (!runtime)
296
+ throw new DodoError('NOT_SUPPORTED', 'Runtime Intelligence is required to verify completion evidence');
297
+ for (const result of results) {
298
+ if (result.evidence.length < 1)
299
+ throw new DodoError('INVALID_INPUT', `criterion requires current evidence: ${result.criterion}`);
300
+ for (const ref of result.evidence) {
301
+ const evidence = await runtime.evidence(ctx, ref.sessionId, ref.evidenceId, true);
302
+ if (evidence.status !== 'CURRENT')
303
+ throw new DodoError('CONFLICT', `completion evidence ${ref.evidenceId} is ${evidence.status}`);
304
+ }
305
+ }
306
+ this.services.store.db.transaction(() => { this.updateRunStatus(run, owner, 'COMPLETED', now); this.releaseRunIntents(run.id, owner, now); })();
307
+ }
308
+ return { run: this.runData(this.runRow(run.id, owner)), jobsKilled: false, note: 'Coordinator control never kills jobs. Cancel target work explicitly through its owning runtime/job operation.' };
309
+ }
310
+ proposeSkill(ctx, input) {
311
+ this.check();
312
+ liveAccess(ctx, 'dodo:write');
313
+ this.expire();
314
+ const clean = parseObject(redact(JSON.stringify(input)), { title: '', summary: '', steps: [], requiredCapabilities: [] });
315
+ const key = input.key.toLowerCase();
316
+ const current = this.currentSkill(key);
317
+ if (current && input.baseVersion !== current.version)
318
+ throw new DodoError('CONFLICT', `skill ${key} is at version ${current.version}; propose against that exact baseVersion`);
319
+ if (!current && input.baseVersion !== undefined)
320
+ throw new DodoError('CONFLICT', 'baseVersion was supplied for a new skill');
321
+ const payload = { key, title: clean.title, summary: clean.summary, steps: clean.steps, requiredCapabilities: clean.requiredCapabilities, baseSkillId: current?.id ?? null, baseVersion: current?.version ?? null, authority: 'untrusted_guidance' };
322
+ const digest = digestOf(payload), now = Date.now(), id = newId('askillprop');
323
+ this.services.store.db.prepare(`INSERT INTO agent_skill_proposals(id,workspace_id,principal,skill_key,title,summary,steps,capabilities,base_skill_id,base_version,digest,status,created_at,expires_at,reviewed_at,review_note)
324
+ VALUES (?,?,?,?,?,?,?,?,?,?,?,'PENDING',?,?,NULL,NULL)`).run(id, this.services.workspaceId, ctx.principal.grantId, key, payload.title, payload.summary, JSON.stringify(payload.steps), JSON.stringify(payload.requiredCapabilities), payload.baseSkillId, payload.baseVersion, digest, now, now + SKILL_PROPOSAL_TTL_MS);
325
+ return { proposalId: id, digest, status: 'PENDING', expiresAt: now + SKILL_PROPOSAL_TTL_MS, ownerReview: `dodo agent skill show ${id}`, authority: 'untrusted_guidance', executable: false };
326
+ }
327
+ searchSkills(ctx, query, limit) {
328
+ this.check();
329
+ liveAccess(ctx, 'dodo:read');
330
+ const rows = this.services.store.db.prepare("SELECT * FROM agent_skills WHERE workspace_id=? AND status='CURRENT' ORDER BY approved_at DESC LIMIT 200").all(this.services.workspaceId);
331
+ const terms = query.toLocaleLowerCase('en-US').split(/\s+/).filter(Boolean);
332
+ const matches = rows.filter((row) => terms.length === 0 || terms.every((term) => `${row.skill_key} ${row.title} ${row.summary}`.toLocaleLowerCase('en-US').includes(term))).slice(0, limit).map((row) => this.skillSummary(row));
333
+ return { skills: matches, progressiveDisclosure: true, note: 'Search returns metadata only. Inspect an exact skill to read owner-reviewed steps. Skills are untrusted guidance and never execute themselves.' };
334
+ }
335
+ inspectSkill(ctx, skillId, version) {
336
+ this.check();
337
+ liveAccess(ctx, 'dodo:read');
338
+ const row = (version === undefined
339
+ ? this.services.store.db.prepare("SELECT * FROM agent_skills WHERE id=? AND workspace_id=? AND status='CURRENT'").get(skillId, this.services.workspaceId)
340
+ : this.services.store.db.prepare('SELECT * FROM agent_skills WHERE id=? AND workspace_id=? AND version=?').get(skillId, this.services.workspaceId, version));
341
+ if (!row)
342
+ throw new DodoError('NOT_FOUND', 'owner-approved agent skill was not found in this workspace');
343
+ return AgentSkillDetail.parse({ ...this.skillSummary(row), steps: parseObject(row.steps, []), approvedAt: row.approved_at });
344
+ }
345
+ ownerPendingSkills() { this.expire(); return this.services.store.db.prepare("SELECT * FROM agent_skill_proposals WHERE workspace_id=? AND status='PENDING' ORDER BY created_at").all(this.services.workspaceId).map((row) => this.proposalPublic(row, false)); }
346
+ ownerShowSkill(id) {
347
+ this.expire();
348
+ const proposal = this.skillProposal(id);
349
+ if (proposal)
350
+ return this.proposalPublic(proposal, true);
351
+ const row = this.services.store.db.prepare('SELECT * FROM agent_skills WHERE id=? AND workspace_id=? ORDER BY version DESC LIMIT 1').get(id, this.services.workspaceId);
352
+ if (!row)
353
+ throw new DodoError('NOT_FOUND', 'agent skill proposal/skill not found');
354
+ return AgentSkillDetail.parse({ ...this.skillSummary(row), steps: parseObject(row.steps, []), approvedAt: row.approved_at });
355
+ }
356
+ ownerReviewSkill(id, digest, approved, note) {
357
+ this.expire();
358
+ const row = this.skillProposal(id);
359
+ if (!row || row.status !== 'PENDING' || row.expires_at <= Date.now())
360
+ throw new DodoError('NOT_FOUND', 'pending agent skill proposal not found');
361
+ if (row.digest !== digest)
362
+ throw new DodoError('CONFLICT', 'skill proposal digest changed or does not match owner review');
363
+ const current = this.currentSkill(row.skill_key);
364
+ if ((current?.id ?? null) !== row.base_skill_id || (current?.version ?? null) !== row.base_version)
365
+ throw new DodoError('CONFLICT', 'current skill version changed after this proposal; review a new proposal');
366
+ const now = Date.now(), reviewNote = redact(note).slice(0, 500);
367
+ if (!approved) {
368
+ const reject = this.services.store.db.transaction(() => {
369
+ const changed = this.services.store.db.prepare("UPDATE agent_skill_proposals SET status='REJECTED',reviewed_at=?,review_note=? WHERE id=? AND status='PENDING'").run(now, reviewNote, row.id);
370
+ if (changed.changes !== 1)
371
+ throw new DodoError('CONFLICT', 'skill proposal changed while it was being reviewed');
372
+ this.services.store.audit({ principal: 'local-agent-owner', workspaceId: this.services.workspaceId, tool: 'local.agent.skill.reject', inputDigest: row.digest.slice(0, 24), result: 'rejected' });
373
+ });
374
+ reject.immediate();
375
+ return { proposalId: row.id, status: 'REJECTED', executable: false };
376
+ }
377
+ const skillId = current?.id ?? newId('askill'), version = (current?.version ?? 0) + 1;
378
+ const approve = this.services.store.db.transaction(() => {
379
+ if (current)
380
+ this.services.store.db.prepare("UPDATE agent_skills SET status='SUPERSEDED' WHERE id=? AND version=? AND status='CURRENT'").run(current.id, current.version);
381
+ this.services.store.db.prepare(`INSERT INTO agent_skills(id,workspace_id,skill_key,title,summary,steps,capabilities,version,proposal_id,digest,status,created_at,approved_at)
382
+ VALUES (?,?,?,?,?,?,?,?,?,?,'CURRENT',?,?)`).run(skillId, this.services.workspaceId, row.skill_key, row.title, row.summary, row.steps, row.capabilities, version, row.id, row.digest, row.created_at, now);
383
+ const changed = this.services.store.db.prepare("UPDATE agent_skill_proposals SET status='APPROVED',reviewed_at=?,review_note=? WHERE id=? AND status='PENDING'").run(now, reviewNote, row.id);
384
+ if (changed.changes !== 1)
385
+ throw new DodoError('CONFLICT', 'skill proposal changed while it was being reviewed');
386
+ this.services.store.audit({ principal: 'local-agent-owner', workspaceId: this.services.workspaceId, tool: 'local.agent.skill.approve', inputDigest: row.digest.slice(0, 24), result: `approved:v${version}` });
387
+ });
388
+ approve.immediate();
389
+ return { ...this.skillSummary(this.currentSkill(row.skill_key)), status: 'CURRENT', executable: false };
390
+ }
391
+ diagnostics(ctx) {
392
+ this.check();
393
+ liveAccess(ctx, 'dodo:read');
394
+ this.expire();
395
+ const owner = actorKey(ctx.principal);
396
+ const row = this.services.store.db.prepare(`SELECT
397
+ (SELECT COUNT(*) FROM agent_runs WHERE workspace_id=? AND owner=? AND status IN ('ACTIVE','PAUSED','RECOVERY_REQUIRED')) AS runs,
398
+ (SELECT COUNT(*) FROM agent_intents WHERE workspace_id=? AND owner=? AND status='ACTIVE') AS intents,
399
+ (SELECT COUNT(*) FROM agent_skills WHERE workspace_id=? AND status='CURRENT') AS skills`).get(this.services.workspaceId, owner, this.services.workspaceId, owner, this.services.workspaceId);
400
+ return { schemaVersion: AGENT_SCHEMA_VERSION, available: true, ...row, grantsAuthority: false, executesSkills: false };
401
+ }
402
+ normalizeCapabilities(ctx, input) {
403
+ const authorized = new Set(this.services.federation.listAuthorized(ctx.principal).projects.map((project) => project.projectId));
404
+ for (const projectId of input.allowedProjectIds)
405
+ if (!authorized.has(projectId))
406
+ throw new DodoError('FORBIDDEN', `project ${projectId} is not currently readable by this client`);
407
+ const writablePaths = [...new Set(input.writablePaths.map((value) => this.services.wfs.resolve(value, { allowMissing: true }).rel))].sort();
408
+ return AgentCapabilities.parse({ ...input, allowedProjectIds: [...new Set(input.allowedProjectIds)].sort(), writablePaths, allowedPrograms: [...new Set(input.allowedPrograms)].sort(), secretAccess: false });
409
+ }
410
+ validatePlan(steps) {
411
+ const ids = new Set();
412
+ for (const step of steps) {
413
+ if (ids.has(step.id))
414
+ throw new DodoError('INVALID_INPUT', `duplicate plan step id ${step.id}`);
415
+ ids.add(step.id);
416
+ }
417
+ for (const step of steps)
418
+ for (const dependency of step.dependsOn)
419
+ if (!ids.has(dependency) || dependency === step.id)
420
+ throw new DodoError('INVALID_INPUT', `invalid dependency ${dependency} for step ${step.id}`);
421
+ const visiting = new Set(), visited = new Set(), by = new Map(steps.map((step) => [step.id, step]));
422
+ const visit = (id) => { if (visiting.has(id))
423
+ throw new DodoError('INVALID_INPUT', 'agent plan dependency cycle detected'); if (visited.has(id))
424
+ return; visiting.add(id); for (const dep of by.get(id)?.dependsOn ?? [])
425
+ visit(dep); visiting.delete(id); visited.add(id); };
426
+ for (const id of ids)
427
+ visit(id);
428
+ }
429
+ assertProjects(args, capabilities) {
430
+ const requested = [typeof args['projectId'] === 'string' ? args['projectId'] : null, ...(Array.isArray(args['projectIds']) ? args['projectIds'].filter((item) => typeof item === 'string') : [])].filter((item) => item !== null);
431
+ for (const projectId of requested)
432
+ if (!capabilities.allowedProjectIds.includes(projectId))
433
+ throw new DodoError('FORBIDDEN', `agent run does not allow project ${projectId}`);
434
+ }
435
+ assertFeature(operation, args, capabilities) {
436
+ if ((operation.startsWith('browser_') || operation.startsWith('game_')) && !capabilities.allowBrowser)
437
+ throw new DodoError('FORBIDDEN', 'agent run does not allow browser/game operations');
438
+ if (operation.startsWith('desktop_') && !capabilities.allowDesktop)
439
+ throw new DodoError('FORBIDDEN', 'agent run does not allow desktop operations');
440
+ if (/^(?:media_|image_view|screen_observe|speech_synthesize|multimodal_status|resource_)/.test(operation) && !capabilities.allowMedia)
441
+ throw new DodoError('FORBIDDEN', 'agent run does not allow media/resource operations');
442
+ if (operation.startsWith('workflow_') && !capabilities.allowWorkflow)
443
+ throw new DodoError('FORBIDDEN', 'agent run does not allow workflow operations');
444
+ if ((args['network'] === true || operation === 'fetch_url') && !capabilities.allowNetwork)
445
+ throw new DodoError('FORBIDDEN', 'agent run does not allow network access');
446
+ }
447
+ assertWritableOperation(ctx, run, hypothesis, operation, args, capabilities) {
448
+ const paths = this.operationPaths(ctx, operation, args);
449
+ if (paths.length === 0)
450
+ return;
451
+ this.assertAllowedPaths(paths, capabilities);
452
+ this.assertHeldLocks(run.id, hypothesis.id, run.owner, paths);
453
+ }
454
+ assertExecutableOperation(ctx, operation, args, capabilities) {
455
+ if (operation === 'run_command' || operation === 'run_commands' || operation === 'schedule_propose' || operation === 'job_input' || operation === 'job_cancel') {
456
+ throw new DodoError('NOT_SUPPORTED', `${operation} is not available through managed agent execution; use explicit argv/runtime task operations and owned handles`);
457
+ }
458
+ let program;
459
+ if (operation === 'exec_command' || operation === 'runtime_task_start')
460
+ program = typeof args['program'] === 'string' ? args['program'] : undefined;
461
+ if (operation === 'run_task') {
462
+ const recipe = this.services.overview.discoverTasks(this.services.projectConfig).find((item) => item.id === args['taskId'] && item.recipeDigest === args['recipeDigest']);
463
+ if (!recipe)
464
+ throw new DodoError('CONFLICT', 'task recipe is missing or changed; call project_overview again');
465
+ program = recipe.program;
466
+ }
467
+ if (program && !capabilities.allowedPrograms.some((allowed) => sameProgram(allowed, program)))
468
+ throw new DodoError('FORBIDDEN', `program ${program} is outside this agent run's allowlist`);
469
+ if (program) {
470
+ const running = this.services.jobs.list(this.services.workspaceId, 500).filter((job) => job.principal === ctx.principal.grantId && job.status === 'running').length;
471
+ if (running >= capabilities.maxRunningJobs)
472
+ throw new DodoError('RESOURCE_LIMIT', `agent run allows at most ${capabilities.maxRunningJobs} running jobs for this grant`);
473
+ }
474
+ }
475
+ operationPaths(ctx, operation, args) {
476
+ const raw = [];
477
+ const add = (value) => { if (typeof value === 'string')
478
+ raw.push(value); };
479
+ if (['write_file', 'edit_file', 'delete_path', 'make_directory'].includes(operation))
480
+ add(args['path']);
481
+ else if (operation === 'move_path') {
482
+ add(args['path']);
483
+ add(args['destPath']);
484
+ }
485
+ else if (operation === 'git_commit' || operation === 'replace_in_files') {
486
+ if (operation === 'replace_in_files' && !Array.isArray(args['paths']))
487
+ throw new DodoError('INVALID_INPUT', 'managed replace_in_files requires explicit paths; fileGlob alone is too broad for agent path capabilities');
488
+ for (const value of Array.isArray(args['paths']) ? args['paths'] : [])
489
+ add(value);
490
+ }
491
+ else if (operation === 'preview_changes') {
492
+ for (const op of Array.isArray(args['ops']) ? args['ops'] : [])
493
+ if (op && typeof op === 'object') {
494
+ add(op['path']);
495
+ add(op['destPath']);
496
+ }
497
+ }
498
+ else if (operation === 'apply_patch' && typeof args['patch'] === 'string') {
499
+ for (const file of parsePatch(args['patch'])) {
500
+ add(this.patchPath(file.oldFileName));
501
+ add(this.patchPath(file.newFileName));
502
+ }
503
+ }
504
+ else if (operation === 'apply_changes') {
505
+ const planId = String(args['planId'] ?? ''), plan = this.services.store.getPlan(planId);
506
+ if (!plan || plan.workspaceId !== this.services.workspaceId || plan.epoch !== this.services.epoch || plan.principal !== ctx.principal.grantId || plan.invalidatedAt !== null || plan.expiresAt <= Date.now())
507
+ throw new DodoError('CONFLICT', 'change plan is unavailable, stale or owned by another principal');
508
+ const payload = parseObject(plan.payload, null);
509
+ if (!payload)
510
+ throw new DodoError('CONFLICT', 'change plan payload is invalid');
511
+ for (const file of payload.files) {
512
+ add(file.path);
513
+ add(file.destPath);
514
+ }
515
+ }
516
+ else if (operation === 'rollback_changes') {
517
+ const changeset = this.services.store.getChangeset(String(args['changesetId'] ?? ''));
518
+ if (!changeset || changeset.workspaceId !== this.services.workspaceId || changeset.principal !== ctx.principal.grantId)
519
+ throw new DodoError('NOT_FOUND', 'caller-owned changeset was not found');
520
+ for (const step of this.services.store.listJournalSteps(changeset.id)) {
521
+ add(step.path);
522
+ add(step.destPath);
523
+ }
524
+ }
525
+ return [...new Set(raw.filter(Boolean).map((value) => this.services.wfs.resolve(value, { allowMissing: true }).rel))];
526
+ }
527
+ patchPath(value) { if (!value || value === '/dev/null')
528
+ return undefined; const clean = value.trim(); return clean.startsWith('a/') || clean.startsWith('b/') ? clean.slice(2) : clean; }
529
+ assertAllowedPaths(paths, capabilities) { for (const value of paths)
530
+ if (!capabilities.writablePaths.some((prefix) => withinPrefix(value, prefix)))
531
+ throw new DodoError('FORBIDDEN', `path ${value} is outside this agent run's writable paths`); }
532
+ assertHeldLocks(runId, hypothesisId, owner, paths) {
533
+ this.expire();
534
+ const locks = this.services.store.db.prepare("SELECT resource_key FROM agent_intents WHERE run_id=? AND hypothesis_id=? AND owner=? AND kind='path' AND status='ACTIVE' AND expires_at>?").all(runId, hypothesisId, owner, Date.now());
535
+ for (const value of paths)
536
+ if (!locks.some((lock) => withinPrefix(value, lock.resource_key)))
537
+ throw new DodoError('CONFLICT', `hypothesis must hold an active path intent covering ${value} before a managed write`);
538
+ }
539
+ buildManifest() {
540
+ const files = [];
541
+ for (const entry of this.services.wfs.walk({ maxEntries: MAX_SNAPSHOT_FILES + 1, maxDepth: 64 })) {
542
+ if (entry.stat.isFile())
543
+ files.push({ path: entry.rel, bytes: entry.stat.size, mtimeMs: entry.stat.mtimeMs, ctimeMs: entry.stat.ctimeMs });
544
+ if (files.length > MAX_SNAPSHOT_FILES)
545
+ break;
546
+ }
547
+ return { files: files.slice(0, MAX_SNAPSHOT_FILES), truncated: files.length > MAX_SNAPSHOT_FILES, baselineChangesets: this.services.store.listChangesets(this.services.workspaceId, 1000).map((item) => item.id) };
548
+ }
549
+ rollbackCandidates(manifest, principal) {
550
+ const baseline = new Set(manifest.baselineChangesets);
551
+ return this.services.store.listChangesets(this.services.workspaceId, 1000).filter((item) => !baseline.has(item.id) && item.principal === principal && item.kind === 'apply' && item.status === 'committed').map((item) => ({ changesetId: item.id, createdAt: item.createdAt, summaryHash: digestOf(item.summary ?? '') }));
552
+ }
553
+ normalizeIntentKey(kind, raw) {
554
+ if (kind === 'path')
555
+ return this.services.wfs.resolve(raw, { allowMissing: true }).rel;
556
+ const value = raw.trim();
557
+ if (!value || value.length > 512 || /[\u0000-\u001f\u007f]/.test(value))
558
+ throw new DodoError('INVALID_INPUT', `${kind} intent key must be 1..512 printable characters`);
559
+ return process.platform === 'win32' ? value.toLowerCase() : value;
560
+ }
561
+ runRow(id, owner) { return this.services.store.db.prepare('SELECT * FROM agent_runs WHERE id=? AND workspace_id=? AND owner=?').get(id, this.services.workspaceId, owner); }
562
+ requireRun(id, owner) { const row = this.runRow(id, owner); if (!row)
563
+ throw new DodoError('NOT_FOUND', 'agent run was not found for this workspace and client'); return row; }
564
+ requireActiveRun(id, owner) { const row = this.requireRun(id, owner); if (row.status !== 'ACTIVE')
565
+ throw new DodoError(row.status === 'RECOVERY_REQUIRED' ? 'CONFLICT' : 'CONFLICT', `agent run is ${row.status}; recover/resume or open another run before acting`); if (row.opened_epoch !== this.services.epoch)
566
+ throw new DodoError('STALE_WORKSPACE', 'agent run must be recovered after server restart or workspace switch'); return row; }
567
+ hypothesisRows(runId, owner) { return this.services.store.db.prepare('SELECT * FROM agent_hypotheses WHERE run_id=? AND workspace_id=? AND owner=? ORDER BY created_at').all(runId, this.services.workspaceId, owner); }
568
+ hypothesisRow(runId, id, owner) { return this.services.store.db.prepare('SELECT * FROM agent_hypotheses WHERE id=? AND run_id=? AND workspace_id=? AND owner=?').get(id, runId, this.services.workspaceId, owner); }
569
+ requireHypothesis(runId, id, owner) { const row = this.hypothesisRow(runId, id, owner); if (!row)
570
+ throw new DodoError('NOT_FOUND', 'agent hypothesis was not found for this run, workspace and client'); return row; }
571
+ requireActiveHypothesis(runId, id, owner) { const row = this.requireHypothesis(runId, id, owner); if (row.status !== 'ACTIVE')
572
+ throw new DodoError('CONFLICT', `agent hypothesis is ${row.status}`); return row; }
573
+ intentRow(id, owner) { return this.services.store.db.prepare('SELECT * FROM agent_intents WHERE id=? AND workspace_id=? AND owner=?').get(id, this.services.workspaceId, owner); }
574
+ requireSnapshot(runId, hypothesisId, id, owner) { const row = this.services.store.db.prepare('SELECT * FROM agent_snapshots WHERE id=? AND run_id=? AND hypothesis_id=? AND workspace_id=? AND owner=?').get(id, runId, hypothesisId, this.services.workspaceId, owner); if (!row)
575
+ throw new DodoError('NOT_FOUND', 'agent snapshot was not found for this run and hypothesis'); return { row, manifest: parseObject(row.manifest, { files: [], truncated: true, baselineChangesets: [] }) }; }
576
+ capabilities(row) { return AgentCapabilities.parse(parseObject(row.capabilities, {})); }
577
+ runData(row) { return AgentRun.parse({ schemaVersion: AGENT_SCHEMA_VERSION, runId: row.id, goal: row.goal, completionCriteria: parseObject(row.criteria, []), capabilities: this.capabilities(row), status: row.status, revision: row.revision, actionCount: row.action_count, workspaceId: row.workspace_id, openedEpoch: row.opened_epoch, createdAt: row.created_at, updatedAt: row.updated_at, expiresAt: row.expires_at, completedAt: row.completed_at, authority: 'coordination_only' }); }
578
+ hypothesisData(row) { return AgentHypothesis.parse({ hypothesisId: row.id, runId: row.run_id, title: row.title, probableCause: row.probable_cause, expectedEvidence: parseObject(row.expected_evidence, []), status: row.status, createdAt: row.created_at, updatedAt: row.updated_at }); }
579
+ intentData(row) { return AgentIntent.parse({ intentId: row.id, runId: row.run_id, hypothesisId: row.hypothesis_id, kind: row.kind, resourceKey: row.resource_key, status: row.status, createdAt: row.created_at, expiresAt: row.expires_at, releasedAt: row.released_at }); }
580
+ currentSkill(key) { return this.services.store.db.prepare("SELECT * FROM agent_skills WHERE workspace_id=? AND skill_key=? AND status='CURRENT'").get(this.services.workspaceId, key); }
581
+ skillProposal(id) { return this.services.store.db.prepare('SELECT * FROM agent_skill_proposals WHERE id=? AND workspace_id=?').get(id, this.services.workspaceId); }
582
+ skillSummary(row) { return AgentSkillSummary.parse({ skillId: row.id, key: row.skill_key, title: row.title, summary: row.summary, version: row.version, requiredCapabilities: parseObject(row.capabilities, []), digest: row.digest, authority: 'untrusted_guidance' }); }
583
+ proposalPublic(row, detail) { return { proposalId: row.id, key: row.skill_key, title: row.title, summary: row.summary, ...(detail ? { steps: parseObject(row.steps, []), requiredCapabilities: parseObject(row.capabilities, []) } : {}), baseSkillId: row.base_skill_id, baseVersion: row.base_version, digest: row.digest, status: row.status, createdAt: row.created_at, expiresAt: row.expires_at, reviewedAt: row.reviewed_at, reviewNote: row.review_note, authority: 'untrusted_guidance', executable: false }; }
584
+ updateRunStatus(run, owner, status, now = Date.now()) { this.services.store.db.prepare('UPDATE agent_runs SET status=?,updated_at=?,completed_at=? WHERE id=? AND owner=?').run(status, now, ['COMPLETED', 'CANCELED', 'EXPIRED'].includes(status) ? now : null, run.id, owner); }
585
+ releaseRunIntents(runId, owner, now) { this.services.store.db.prepare("UPDATE agent_intents SET status='RELEASED',released_at=? WHERE run_id=? AND owner=? AND status='ACTIVE'").run(now, runId, owner); }
586
+ expire() { const now = Date.now(); this.services.store.db.transaction(() => { this.services.store.db.prepare("UPDATE agent_intents SET status='EXPIRED',released_at=? WHERE status='ACTIVE' AND expires_at<=?").run(now, now); this.services.store.db.prepare("UPDATE agent_runs SET status='EXPIRED',completed_at=?,updated_at=? WHERE status IN ('ACTIVE','PAUSED','RECOVERY_REQUIRED') AND expires_at<=?").run(now, now, now); this.services.store.db.prepare("UPDATE agent_skill_proposals SET status='EXPIRED' WHERE status='PENDING' AND expires_at<=?").run(now); this.services.store.db.prepare("DELETE FROM agent_runs WHERE status IN ('COMPLETED','CANCELED','EXPIRED') AND COALESCE(completed_at,updated_at)<=?").run(now - RUN_RETENTION_MS); })(); }
587
+ recoverOnBoot() {
588
+ const now = Date.now();
589
+ this.expire();
590
+ this.services.store.db.transaction(() => {
591
+ this.services.store.db.prepare("UPDATE agent_actions SET status='INTERRUPTED',result_code='SERVER_RESTARTED',completed_at=? WHERE workspace_id=? AND status='RUNNING'").run(now, this.services.workspaceId);
592
+ this.services.store.db.prepare("UPDATE agent_runs SET status='RECOVERY_REQUIRED',updated_at=? WHERE workspace_id=? AND status='ACTIVE' AND opened_epoch<>?").run(now, this.services.workspaceId, this.services.epoch);
593
+ })();
594
+ }
595
+ check() { if (this.closed)
596
+ throw new DodoError('STALE_WORKSPACE', 'advanced agent runtime workspace is closed'); }
597
+ }
598
+ //# sourceMappingURL=agentService.js.map