@revisium/orchestrator 0.0.0 → 0.1.0-alpha.10

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 (547) hide show
  1. package/README.md +202 -1
  2. package/control-plane/bootstrap.config.json +104 -2
  3. package/control-plane/default-playbook/catalog/pipelines.json +657 -0
  4. package/control-plane/default-playbook/catalog/roles.json +65 -0
  5. package/control-plane/default-playbook/package.json +5 -0
  6. package/control-plane/default-playbook/playbook.json +13 -0
  7. package/control-plane/default-playbook/prompts/analyst.md +29 -0
  8. package/control-plane/default-playbook/prompts/developer.md +26 -0
  9. package/control-plane/default-playbook/prompts/integrator.md +20 -0
  10. package/control-plane/default-playbook/prompts/orchestrator.md +23 -0
  11. package/control-plane/default-playbook/prompts/reviewer.md +28 -0
  12. package/control-plane/default-playbook/prompts/triager.md +51 -0
  13. package/control-plane/default-playbook/prompts/watcher.md +24 -0
  14. package/dist/api/graphql-api/filters/agent-observability-exception.filter.js +24 -0
  15. package/dist/api/graphql-api/filters/agent-observability-exception.filter.js.map +1 -0
  16. package/dist/api/graphql-api/filters/graphql-validation-exception.filter.js +36 -0
  17. package/dist/api/graphql-api/filters/graphql-validation-exception.filter.js.map +1 -0
  18. package/dist/api/graphql-api/graphql-api.module.js +77 -0
  19. package/dist/api/graphql-api/graphql-api.module.js.map +1 -0
  20. package/dist/api/graphql-api/graphql-ws/agent-observability-subscription-bridge.service.js +296 -0
  21. package/dist/api/graphql-api/graphql-ws/agent-observability-subscription-bridge.service.js.map +1 -0
  22. package/dist/api/graphql-api/graphql-ws/agent-observability-subscription-mappers.js +64 -0
  23. package/dist/api/graphql-api/graphql-ws/agent-observability-subscription-mappers.js.map +1 -0
  24. package/dist/api/graphql-api/graphql-ws/constants.js +11 -0
  25. package/dist/api/graphql-api/graphql-ws/constants.js.map +1 -0
  26. package/dist/api/graphql-api/graphql-ws/pubsub.module.js +32 -0
  27. package/dist/api/graphql-api/graphql-ws/pubsub.module.js.map +1 -0
  28. package/dist/api/graphql-api/graphql-ws/run-progress-subscription-poller.service.js +101 -0
  29. package/dist/api/graphql-api/graphql-ws/run-progress-subscription-poller.service.js.map +1 -0
  30. package/dist/api/graphql-api/graphql-ws/subscription-bridge.service.js +115 -0
  31. package/dist/api/graphql-api/graphql-ws/subscription-bridge.service.js.map +1 -0
  32. package/dist/api/graphql-api/graphql-ws/subscription-mappers.js +85 -0
  33. package/dist/api/graphql-api/graphql-ws/subscription-mappers.js.map +1 -0
  34. package/dist/api/graphql-api/graphql-ws/ws.js +22 -0
  35. package/dist/api/graphql-api/graphql-ws/ws.js.map +1 -0
  36. package/dist/api/graphql-api/inbox/inbox-subscription.resolver.js +56 -0
  37. package/dist/api/graphql-api/inbox/inbox-subscription.resolver.js.map +1 -0
  38. package/dist/api/graphql-api/inbox/inbox.resolver.js +125 -0
  39. package/dist/api/graphql-api/inbox/inbox.resolver.js.map +1 -0
  40. package/dist/api/graphql-api/inbox/inputs/answer-question.input.js +33 -0
  41. package/dist/api/graphql-api/inbox/inputs/answer-question.input.js.map +1 -0
  42. package/dist/api/graphql-api/inbox/inputs/gate-decision.input.js +27 -0
  43. package/dist/api/graphql-api/inbox/inputs/gate-decision.input.js.map +1 -0
  44. package/dist/api/graphql-api/inbox/inputs/list-inbox.input.js +28 -0
  45. package/dist/api/graphql-api/inbox/inputs/list-inbox.input.js.map +1 -0
  46. package/dist/api/graphql-api/inbox/inputs/resolve-inbox-item.input.js +38 -0
  47. package/dist/api/graphql-api/inbox/inputs/resolve-inbox-item.input.js.map +1 -0
  48. package/dist/api/graphql-api/inbox/model/gate-risk.model.js +53 -0
  49. package/dist/api/graphql-api/inbox/model/gate-risk.model.js.map +1 -0
  50. package/dist/api/graphql-api/inbox/model/inbox-connection.model.js +16 -0
  51. package/dist/api/graphql-api/inbox/model/inbox-connection.model.js.map +1 -0
  52. package/dist/api/graphql-api/inbox/model/inbox-item.model.js +88 -0
  53. package/dist/api/graphql-api/inbox/model/inbox-item.model.js.map +1 -0
  54. package/dist/api/graphql-api/inbox/model/inbox-resolution.model.js +48 -0
  55. package/dist/api/graphql-api/inbox/model/inbox-resolution.model.js.map +1 -0
  56. package/dist/api/graphql-api/method/inputs/list-method.input.js +15 -0
  57. package/dist/api/graphql-api/method/inputs/list-method.input.js.map +1 -0
  58. package/dist/api/graphql-api/method/method.resolver.js +90 -0
  59. package/dist/api/graphql-api/method/method.resolver.js.map +1 -0
  60. package/dist/api/graphql-api/method/model/alternative-role-group.model.js +32 -0
  61. package/dist/api/graphql-api/method/model/alternative-role-group.model.js.map +1 -0
  62. package/dist/api/graphql-api/method/model/pipeline-connection.model.js +16 -0
  63. package/dist/api/graphql-api/method/model/pipeline-connection.model.js.map +1 -0
  64. package/dist/api/graphql-api/method/model/pipeline.model.js +69 -0
  65. package/dist/api/graphql-api/method/model/pipeline.model.js.map +1 -0
  66. package/dist/api/graphql-api/method/model/playbook-connection.model.js +16 -0
  67. package/dist/api/graphql-api/method/model/playbook-connection.model.js.map +1 -0
  68. package/dist/api/graphql-api/method/model/playbook.model.js +47 -0
  69. package/dist/api/graphql-api/method/model/playbook.model.js.map +1 -0
  70. package/dist/api/graphql-api/method/model/role-connection.model.js +16 -0
  71. package/dist/api/graphql-api/method/model/role-connection.model.js.map +1 -0
  72. package/dist/api/graphql-api/method/model/role.model.js +98 -0
  73. package/dist/api/graphql-api/method/model/role.model.js.map +1 -0
  74. package/dist/api/graphql-api/pr/inputs/pr-readiness.input.js +52 -0
  75. package/dist/api/graphql-api/pr/inputs/pr-readiness.input.js.map +1 -0
  76. package/dist/api/graphql-api/pr/model/pr-readiness.model.js +403 -0
  77. package/dist/api/graphql-api/pr/model/pr-readiness.model.js.map +1 -0
  78. package/dist/api/graphql-api/pr/pr.resolver.js +76 -0
  79. package/dist/api/graphql-api/pr/pr.resolver.js.map +1 -0
  80. package/dist/api/graphql-api/registerGraphqlEnums.js +9 -0
  81. package/dist/api/graphql-api/registerGraphqlEnums.js.map +1 -0
  82. package/dist/api/graphql-api/runs/inputs/create-run.input.js +63 -0
  83. package/dist/api/graphql-api/runs/inputs/create-run.input.js.map +1 -0
  84. package/dist/api/graphql-api/runs/inputs/get-agent-log.input.js +48 -0
  85. package/dist/api/graphql-api/runs/inputs/get-agent-log.input.js.map +1 -0
  86. package/dist/api/graphql-api/runs/inputs/get-run-attempts.input.js +32 -0
  87. package/dist/api/graphql-api/runs/inputs/get-run-attempts.input.js.map +1 -0
  88. package/dist/api/graphql-api/runs/inputs/get-run-events.input.js +28 -0
  89. package/dist/api/graphql-api/runs/inputs/get-run-events.input.js.map +1 -0
  90. package/dist/api/graphql-api/runs/inputs/list-runs.input.js +23 -0
  91. package/dist/api/graphql-api/runs/inputs/list-runs.input.js.map +1 -0
  92. package/dist/api/graphql-api/runs/inputs/simulate-route.input.js +43 -0
  93. package/dist/api/graphql-api/runs/inputs/simulate-route.input.js.map +1 -0
  94. package/dist/api/graphql-api/runs/model/agent-activity.model.js +367 -0
  95. package/dist/api/graphql-api/runs/model/agent-activity.model.js.map +1 -0
  96. package/dist/api/graphql-api/runs/model/create-run-result.model.js +53 -0
  97. package/dist/api/graphql-api/runs/model/create-run-result.model.js.map +1 -0
  98. package/dist/api/graphql-api/runs/model/run-attempt.model.js +114 -0
  99. package/dist/api/graphql-api/runs/model/run-attempt.model.js.map +1 -0
  100. package/dist/api/graphql-api/runs/model/run-connection.model.js +16 -0
  101. package/dist/api/graphql-api/runs/model/run-connection.model.js.map +1 -0
  102. package/dist/api/graphql-api/runs/model/run-cost.model.js +67 -0
  103. package/dist/api/graphql-api/runs/model/run-cost.model.js.map +1 -0
  104. package/dist/api/graphql-api/runs/model/run-digest.model.js +41 -0
  105. package/dist/api/graphql-api/runs/model/run-digest.model.js.map +1 -0
  106. package/dist/api/graphql-api/runs/model/run-event-connection.model.js +16 -0
  107. package/dist/api/graphql-api/runs/model/run-event-connection.model.js.map +1 -0
  108. package/dist/api/graphql-api/runs/model/run-event.model.js +58 -0
  109. package/dist/api/graphql-api/runs/model/run-event.model.js.map +1 -0
  110. package/dist/api/graphql-api/runs/model/run-progress.model.js +33 -0
  111. package/dist/api/graphql-api/runs/model/run-progress.model.js.map +1 -0
  112. package/dist/api/graphql-api/runs/model/run-workflow.model.js +287 -0
  113. package/dist/api/graphql-api/runs/model/run-workflow.model.js.map +1 -0
  114. package/dist/api/graphql-api/runs/model/run.model.js +57 -0
  115. package/dist/api/graphql-api/runs/model/run.model.js.map +1 -0
  116. package/dist/api/graphql-api/runs/model/usage.model.js +32 -0
  117. package/dist/api/graphql-api/runs/model/usage.model.js.map +1 -0
  118. package/dist/api/graphql-api/runs/run-digest.resolver.js +40 -0
  119. package/dist/api/graphql-api/runs/run-digest.resolver.js.map +1 -0
  120. package/dist/api/graphql-api/runs/run-events.resolver.js +45 -0
  121. package/dist/api/graphql-api/runs/run-events.resolver.js.map +1 -0
  122. package/dist/api/graphql-api/runs/run-progress.resolver.js +53 -0
  123. package/dist/api/graphql-api/runs/run-progress.resolver.js.map +1 -0
  124. package/dist/api/graphql-api/runs/runs-subscription.resolver.js +124 -0
  125. package/dist/api/graphql-api/runs/runs-subscription.resolver.js.map +1 -0
  126. package/dist/api/graphql-api/runs/runs.resolver.js +165 -0
  127. package/dist/api/graphql-api/runs/runs.resolver.js.map +1 -0
  128. package/dist/api/graphql-api/share/graphql-param-types.js +6 -0
  129. package/dist/api/graphql-api/share/graphql-param-types.js.map +1 -0
  130. package/dist/api/graphql-api/share/inputs/connection.input.js +27 -0
  131. package/dist/api/graphql-api/share/inputs/connection.input.js.map +1 -0
  132. package/dist/api/graphql-api/share/inputs/run-subscription.input.js +22 -0
  133. package/dist/api/graphql-api/share/inputs/run-subscription.input.js.map +1 -0
  134. package/dist/api/graphql-api/share/model/paginated.model.js +75 -0
  135. package/dist/api/graphql-api/share/model/paginated.model.js.map +1 -0
  136. package/dist/api/graphql-api/system/model/daemon-status.model.js +47 -0
  137. package/dist/api/graphql-api/system/model/daemon-status.model.js.map +1 -0
  138. package/dist/api/graphql-api/system/model/doctor-result.model.js +33 -0
  139. package/dist/api/graphql-api/system/model/doctor-result.model.js.map +1 -0
  140. package/dist/api/graphql-api/system/model/project.model.js +37 -0
  141. package/dist/api/graphql-api/system/model/project.model.js.map +1 -0
  142. package/dist/api/graphql-api/system/model/repository-context.model.js +70 -0
  143. package/dist/api/graphql-api/system/model/repository-context.model.js.map +1 -0
  144. package/dist/api/graphql-api/system/model/repository-validation.model.js +62 -0
  145. package/dist/api/graphql-api/system/model/repository-validation.model.js.map +1 -0
  146. package/dist/api/graphql-api/system/model/system-status.model.js +29 -0
  147. package/dist/api/graphql-api/system/model/system-status.model.js.map +1 -0
  148. package/dist/api/graphql-api/system/system.resolver.js +83 -0
  149. package/dist/api/graphql-api/system/system.resolver.js.map +1 -0
  150. package/dist/cli/commands/doctor-report.js +70 -0
  151. package/dist/cli/commands/doctor-report.js.map +1 -0
  152. package/dist/cli/commands/lifecycle.js +463 -0
  153. package/dist/cli/commands/lifecycle.js.map +1 -0
  154. package/dist/cli/commands/mcp.js +53 -8
  155. package/dist/cli/commands/mcp.js.map +1 -1
  156. package/dist/cli/commands/process-tree.js +87 -0
  157. package/dist/cli/commands/process-tree.js.map +1 -0
  158. package/dist/cli/commands/revisium-helpers.js +2 -2
  159. package/dist/cli/commands/rogue-reaper.js +63 -0
  160. package/dist/cli/commands/rogue-reaper.js.map +1 -0
  161. package/dist/cli/index.js +5 -29
  162. package/dist/cli/index.js.map +1 -1
  163. package/dist/cli/program.js +7 -23
  164. package/dist/cli/program.js.map +1 -1
  165. package/dist/config.js +79 -1
  166. package/dist/config.js.map +1 -1
  167. package/dist/control-plane/bootstrap.js +101 -0
  168. package/dist/control-plane/bootstrap.js.map +1 -0
  169. package/dist/control-plane/change-notifications.js +35 -0
  170. package/dist/control-plane/change-notifications.js.map +1 -0
  171. package/dist/control-plane/client-transport.js +74 -41
  172. package/dist/control-plane/client-transport.js.map +1 -1
  173. package/dist/control-plane/data-access.js +13 -5
  174. package/dist/control-plane/data-access.js.map +1 -1
  175. package/dist/control-plane/definitions.js +1 -1
  176. package/dist/control-plane/definitions.js.map +1 -1
  177. package/dist/control-plane/inbox.js +5 -31
  178. package/dist/control-plane/inbox.js.map +1 -1
  179. package/dist/control-plane/index.js +1 -1
  180. package/dist/control-plane/index.js.map +1 -1
  181. package/dist/control-plane/json-fields.js +2 -0
  182. package/dist/control-plane/json-fields.js.map +1 -1
  183. package/dist/control-plane/resolve-cwd.js +76 -8
  184. package/dist/control-plane/resolve-cwd.js.map +1 -1
  185. package/dist/control-plane/schema-migration.js +82 -0
  186. package/dist/control-plane/schema-migration.js.map +1 -0
  187. package/dist/control-plane/seed-default-playbook.js +141 -0
  188. package/dist/control-plane/seed-default-playbook.js.map +1 -0
  189. package/dist/control-plane/steps.js +15 -349
  190. package/dist/control-plane/steps.js.map +1 -1
  191. package/dist/control-plane/tables.js +1 -0
  192. package/dist/control-plane/tables.js.map +1 -1
  193. package/dist/e2e/kit/agents.js +148 -0
  194. package/dist/e2e/kit/agents.js.map +1 -0
  195. package/dist/e2e/kit/assertions.js +163 -0
  196. package/dist/e2e/kit/assertions.js.map +1 -0
  197. package/dist/e2e/kit/crash.js +44 -0
  198. package/dist/e2e/kit/crash.js.map +1 -0
  199. package/dist/e2e/kit/drive.js +45 -0
  200. package/dist/e2e/kit/drive.js.map +1 -0
  201. package/dist/e2e/kit/env.js +16 -0
  202. package/dist/e2e/kit/env.js.map +1 -0
  203. package/dist/e2e/kit/fake-integrator.js +57 -0
  204. package/dist/e2e/kit/fake-integrator.js.map +1 -0
  205. package/dist/e2e/kit/gh-emulator.js +213 -0
  206. package/dist/e2e/kit/gh-emulator.js.map +1 -0
  207. package/dist/e2e/kit/git-target-repo.js +115 -0
  208. package/dist/e2e/kit/git-target-repo.js.map +1 -0
  209. package/dist/e2e/kit/harness.js +72 -0
  210. package/dist/e2e/kit/harness.js.map +1 -0
  211. package/dist/e2e/kit/index.js +14 -0
  212. package/dist/e2e/kit/index.js.map +1 -0
  213. package/dist/e2e/kit/mcp.js +32 -0
  214. package/dist/e2e/kit/mcp.js.map +1 -0
  215. package/dist/e2e/kit/scenarios.js +196 -0
  216. package/dist/e2e/kit/scenarios.js.map +1 -0
  217. package/dist/e2e/recovery-crash-child.js +29 -0
  218. package/dist/e2e/recovery-crash-child.js.map +1 -0
  219. package/dist/e2e/recovery-dd-crash-child.js +43 -0
  220. package/dist/e2e/recovery-dd-crash-child.js.map +1 -0
  221. package/dist/engine/dbos.module.js +2 -1
  222. package/dist/engine/dbos.module.js.map +1 -1
  223. package/dist/engine/dbos.service.js +89 -18
  224. package/dist/engine/dbos.service.js.map +1 -1
  225. package/dist/engine/ensure-postgres.js +18 -4
  226. package/dist/engine/ensure-postgres.js.map +1 -1
  227. package/dist/features/inbox/commands/handlers/inbox-command.handlers.js +80 -0
  228. package/dist/features/inbox/commands/handlers/inbox-command.handlers.js.map +1 -0
  229. package/dist/features/inbox/commands/impl/answer-question.command.js +7 -0
  230. package/dist/features/inbox/commands/impl/answer-question.command.js.map +1 -0
  231. package/dist/features/inbox/commands/impl/approve-gate.command.js +7 -0
  232. package/dist/features/inbox/commands/impl/approve-gate.command.js.map +1 -0
  233. package/dist/features/inbox/commands/impl/reject-gate.command.js +7 -0
  234. package/dist/features/inbox/commands/impl/reject-gate.command.js.map +1 -0
  235. package/dist/features/inbox/commands/impl/resolve-inbox-item.command.js +7 -0
  236. package/dist/features/inbox/commands/impl/resolve-inbox-item.command.js.map +1 -0
  237. package/dist/features/inbox/commands/index.js +8 -0
  238. package/dist/features/inbox/commands/index.js.map +1 -0
  239. package/dist/features/inbox/inbox-api.module.js +23 -0
  240. package/dist/features/inbox/inbox-api.module.js.map +1 -0
  241. package/dist/features/inbox/inbox-api.service.js +63 -0
  242. package/dist/features/inbox/inbox-api.service.js.map +1 -0
  243. package/dist/features/inbox/queries/handlers/inbox-query.handlers.js +111 -0
  244. package/dist/features/inbox/queries/handlers/inbox-query.handlers.js.map +1 -0
  245. package/dist/features/inbox/queries/impl/get-inbox-item.query.js +7 -0
  246. package/dist/features/inbox/queries/impl/get-inbox-item.query.js.map +1 -0
  247. package/dist/features/inbox/queries/impl/get-pending-decisions.query.js +7 -0
  248. package/dist/features/inbox/queries/impl/get-pending-decisions.query.js.map +1 -0
  249. package/dist/features/inbox/queries/impl/list-inbox.query.js +7 -0
  250. package/dist/features/inbox/queries/impl/list-inbox.query.js.map +1 -0
  251. package/dist/features/inbox/queries/impl/summarize-gate-risk.query.js +7 -0
  252. package/dist/features/inbox/queries/impl/summarize-gate-risk.query.js.map +1 -0
  253. package/dist/features/inbox/queries/index.js +8 -0
  254. package/dist/features/inbox/queries/index.js.map +1 -0
  255. package/dist/features/method/commands/index.js +2 -0
  256. package/dist/features/method/commands/index.js.map +1 -0
  257. package/dist/features/method/method-api.module.js +23 -0
  258. package/dist/features/method/method-api.module.js.map +1 -0
  259. package/dist/features/method/method-api.service.js +47 -0
  260. package/dist/features/method/method-api.service.js.map +1 -0
  261. package/dist/features/method/queries/handlers/method-query.handlers.js +115 -0
  262. package/dist/features/method/queries/handlers/method-query.handlers.js.map +1 -0
  263. package/dist/features/method/queries/impl/get-pipeline.query.js +7 -0
  264. package/dist/features/method/queries/impl/get-pipeline.query.js.map +1 -0
  265. package/dist/features/method/queries/impl/get-role.query.js +7 -0
  266. package/dist/features/method/queries/impl/get-role.query.js.map +1 -0
  267. package/dist/features/method/queries/impl/list-pipelines.query.js +7 -0
  268. package/dist/features/method/queries/impl/list-pipelines.query.js.map +1 -0
  269. package/dist/features/method/queries/impl/list-playbooks.query.js +7 -0
  270. package/dist/features/method/queries/impl/list-playbooks.query.js.map +1 -0
  271. package/dist/features/method/queries/impl/list-roles.query.js +7 -0
  272. package/dist/features/method/queries/impl/list-roles.query.js.map +1 -0
  273. package/dist/features/method/queries/index.js +9 -0
  274. package/dist/features/method/queries/index.js.map +1 -0
  275. package/dist/features/pr/commands/index.js +2 -0
  276. package/dist/features/pr/commands/index.js.map +1 -0
  277. package/dist/features/pr/pr-api.module.js +23 -0
  278. package/dist/features/pr/pr-api.module.js.map +1 -0
  279. package/dist/features/pr/pr-api.service.js +35 -0
  280. package/dist/features/pr/pr-api.service.js.map +1 -0
  281. package/dist/features/pr/queries/handlers/pr-query.handlers.js +48 -0
  282. package/dist/features/pr/queries/handlers/pr-query.handlers.js.map +1 -0
  283. package/dist/features/pr/queries/impl/get-pr-readiness.query.js +7 -0
  284. package/dist/features/pr/queries/impl/get-pr-readiness.query.js.map +1 -0
  285. package/dist/features/pr/queries/impl/list-pr-feedback.query.js +7 -0
  286. package/dist/features/pr/queries/impl/list-pr-feedback.query.js.map +1 -0
  287. package/dist/features/pr/queries/index.js +3 -0
  288. package/dist/features/pr/queries/index.js.map +1 -0
  289. package/dist/features/runs/commands/handlers/runs-command.handlers.js +32 -0
  290. package/dist/features/runs/commands/handlers/runs-command.handlers.js.map +1 -0
  291. package/dist/features/runs/commands/impl/create-run.command.js +7 -0
  292. package/dist/features/runs/commands/impl/create-run.command.js.map +1 -0
  293. package/dist/features/runs/commands/index.js +3 -0
  294. package/dist/features/runs/commands/index.js.map +1 -0
  295. package/dist/features/runs/queries/handlers/runs-query.handlers.js +359 -0
  296. package/dist/features/runs/queries/handlers/runs-query.handlers.js.map +1 -0
  297. package/dist/features/runs/queries/impl/get-agent-activity.query.js +7 -0
  298. package/dist/features/runs/queries/impl/get-agent-activity.query.js.map +1 -0
  299. package/dist/features/runs/queries/impl/get-agent-attempts.query.js +7 -0
  300. package/dist/features/runs/queries/impl/get-agent-attempts.query.js.map +1 -0
  301. package/dist/features/runs/queries/impl/get-agent-log.query.js +7 -0
  302. package/dist/features/runs/queries/impl/get-agent-log.query.js.map +1 -0
  303. package/dist/features/runs/queries/impl/get-run-attempts.query.js +7 -0
  304. package/dist/features/runs/queries/impl/get-run-attempts.query.js.map +1 -0
  305. package/dist/features/runs/queries/impl/get-run-digest.query.js +7 -0
  306. package/dist/features/runs/queries/impl/get-run-digest.query.js.map +1 -0
  307. package/dist/features/runs/queries/impl/get-run-events.query.js +7 -0
  308. package/dist/features/runs/queries/impl/get-run-events.query.js.map +1 -0
  309. package/dist/features/runs/queries/impl/get-run-progress.query.js +7 -0
  310. package/dist/features/runs/queries/impl/get-run-progress.query.js.map +1 -0
  311. package/dist/features/runs/queries/impl/get-run-workflow.query.js +7 -0
  312. package/dist/features/runs/queries/impl/get-run-workflow.query.js.map +1 -0
  313. package/dist/features/runs/queries/impl/get-run.query.js +7 -0
  314. package/dist/features/runs/queries/impl/get-run.query.js.map +1 -0
  315. package/dist/features/runs/queries/impl/list-runs.query.js +7 -0
  316. package/dist/features/runs/queries/impl/list-runs.query.js.map +1 -0
  317. package/dist/features/runs/queries/impl/simulate-route.query.js +7 -0
  318. package/dist/features/runs/queries/impl/simulate-route.query.js.map +1 -0
  319. package/dist/features/runs/queries/index.js +15 -0
  320. package/dist/features/runs/queries/index.js.map +1 -0
  321. package/dist/features/runs/runs-api.module.js +23 -0
  322. package/dist/features/runs/runs-api.module.js.map +1 -0
  323. package/dist/features/runs/runs-api.service.js +79 -0
  324. package/dist/features/runs/runs-api.service.js.map +1 -0
  325. package/dist/features/shared/connection.js +39 -0
  326. package/dist/features/shared/connection.js.map +1 -0
  327. package/dist/features/system/commands/index.js +2 -0
  328. package/dist/features/system/commands/index.js.map +1 -0
  329. package/dist/features/system/queries/handlers/system-query.handlers.js +96 -0
  330. package/dist/features/system/queries/handlers/system-query.handlers.js.map +1 -0
  331. package/dist/features/system/queries/impl/doctor.query.js +4 -0
  332. package/dist/features/system/queries/impl/doctor.query.js.map +1 -0
  333. package/dist/features/system/queries/impl/get-project.query.js +4 -0
  334. package/dist/features/system/queries/impl/get-project.query.js.map +1 -0
  335. package/dist/features/system/queries/impl/get-repository-context.query.js +7 -0
  336. package/dist/features/system/queries/impl/get-repository-context.query.js.map +1 -0
  337. package/dist/features/system/queries/impl/get-status.query.js +4 -0
  338. package/dist/features/system/queries/impl/get-status.query.js.map +1 -0
  339. package/dist/features/system/queries/impl/validate-repository.query.js +7 -0
  340. package/dist/features/system/queries/impl/validate-repository.query.js.map +1 -0
  341. package/dist/features/system/queries/index.js +9 -0
  342. package/dist/features/system/queries/index.js.map +1 -0
  343. package/dist/features/system/system-api.module.js +23 -0
  344. package/dist/features/system/system-api.module.js.map +1 -0
  345. package/dist/features/system/system-api.service.js +47 -0
  346. package/dist/features/system/system-api.service.js.map +1 -0
  347. package/dist/host/daemon.js +99 -0
  348. package/dist/host/daemon.js.map +1 -0
  349. package/dist/host/dbos-identity.js +42 -0
  350. package/dist/host/dbos-identity.js.map +1 -0
  351. package/dist/host/ensure-host.js +120 -0
  352. package/dist/host/ensure-host.js.map +1 -0
  353. package/dist/host/host-runtime.js +90 -0
  354. package/dist/host/host-runtime.js.map +1 -0
  355. package/dist/host/host.lifecycle.js +16 -3
  356. package/dist/host/host.lifecycle.js.map +1 -1
  357. package/dist/host/queue-ownership.js +70 -0
  358. package/dist/host/queue-ownership.js.map +1 -0
  359. package/dist/host/queue-poller-census.js +47 -0
  360. package/dist/host/queue-poller-census.js.map +1 -0
  361. package/dist/http/graphql-host.js +56 -0
  362. package/dist/http/graphql-host.js.map +1 -0
  363. package/dist/{mcp/mcp.module.js → http/graphql-host.module.js} +12 -10
  364. package/dist/http/graphql-host.module.js.map +1 -0
  365. package/dist/infrastructure/metrics/graphql/graphql-metrics.js +91 -0
  366. package/dist/infrastructure/metrics/graphql/graphql-metrics.js.map +1 -0
  367. package/dist/mcp/mcp-capabilities.js +9 -1
  368. package/dist/mcp/mcp-capabilities.js.map +1 -1
  369. package/dist/mcp/mcp-facade.service.js +68 -5
  370. package/dist/mcp/mcp-facade.service.js.map +1 -1
  371. package/dist/mcp/mcp-http.service.js +95 -0
  372. package/dist/mcp/mcp-http.service.js.map +1 -0
  373. package/dist/mcp/mcp-tools.js +93 -7
  374. package/dist/mcp/mcp-tools.js.map +1 -1
  375. package/dist/observability/agent-activity-reporter.js +185 -0
  376. package/dist/observability/agent-activity-reporter.js.map +1 -0
  377. package/dist/observability/agent-observability.service.js +1142 -0
  378. package/dist/observability/agent-observability.service.js.map +1 -0
  379. package/dist/observability/index.js +4 -0
  380. package/dist/observability/index.js.map +1 -0
  381. package/dist/observability/types.js +11 -0
  382. package/dist/observability/types.js.map +1 -0
  383. package/dist/pipeline/await-human.js +8 -4
  384. package/dist/pipeline/await-human.js.map +1 -1
  385. package/dist/pipeline/data-driven-task.workflow.js +667 -0
  386. package/dist/pipeline/data-driven-task.workflow.js.map +1 -0
  387. package/dist/pipeline/data-driven-template.js +57 -0
  388. package/dist/pipeline/data-driven-template.js.map +1 -0
  389. package/dist/pipeline/pipeline.module.js +2 -2
  390. package/dist/pipeline/pipeline.module.js.map +1 -1
  391. package/dist/pipeline/pipeline.service.js +412 -0
  392. package/dist/pipeline/pipeline.service.js.map +1 -0
  393. package/dist/pipeline/route-contract.js +82 -0
  394. package/dist/pipeline/route-contract.js.map +1 -0
  395. package/dist/pipeline-core/index.js +16 -0
  396. package/dist/pipeline-core/index.js.map +1 -0
  397. package/dist/pipeline-core/interpret.js +353 -0
  398. package/dist/pipeline-core/interpret.js.map +1 -0
  399. package/dist/pipeline-core/kit/assertions.js +61 -0
  400. package/dist/pipeline-core/kit/assertions.js.map +1 -0
  401. package/dist/pipeline-core/kit/builders.js +138 -0
  402. package/dist/pipeline-core/kit/builders.js.map +1 -0
  403. package/dist/pipeline-core/kit/drive.js +96 -0
  404. package/dist/pipeline-core/kit/drive.js.map +1 -0
  405. package/dist/pipeline-core/kit/fixtures.js +342 -0
  406. package/dist/pipeline-core/kit/fixtures.js.map +1 -0
  407. package/dist/pipeline-core/kit/index.js +13 -0
  408. package/dist/pipeline-core/kit/index.js.map +1 -0
  409. package/dist/pipeline-core/types.js +134 -0
  410. package/dist/pipeline-core/types.js.map +1 -0
  411. package/dist/pipeline-core/validate-capability.js +23 -0
  412. package/dist/pipeline-core/validate-capability.js.map +1 -0
  413. package/dist/pipeline-core/validate-conflicts.js +37 -0
  414. package/dist/pipeline-core/validate-conflicts.js.map +1 -0
  415. package/dist/pipeline-core/validate-dataflow.js +212 -0
  416. package/dist/pipeline-core/validate-dataflow.js.map +1 -0
  417. package/dist/pipeline-core/validate-diff.js +158 -0
  418. package/dist/pipeline-core/validate-diff.js.map +1 -0
  419. package/dist/pipeline-core/validate-edges.js +44 -0
  420. package/dist/pipeline-core/validate-edges.js.map +1 -0
  421. package/dist/pipeline-core/validate-graph.js +123 -0
  422. package/dist/pipeline-core/validate-graph.js.map +1 -0
  423. package/dist/pipeline-core/validate-loops.js +226 -0
  424. package/dist/pipeline-core/validate-loops.js.map +1 -0
  425. package/dist/pipeline-core/validate-parallel.js +132 -0
  426. package/dist/pipeline-core/validate-parallel.js.map +1 -0
  427. package/dist/pipeline-core/validate-sink.js +27 -0
  428. package/dist/pipeline-core/validate-sink.js.map +1 -0
  429. package/dist/pipeline-core/validate-topology.js +140 -0
  430. package/dist/pipeline-core/validate-topology.js.map +1 -0
  431. package/dist/pipeline-core/validate-verdict.js +72 -0
  432. package/dist/pipeline-core/validate-verdict.js.map +1 -0
  433. package/dist/pipeline-core/validate.js +119 -0
  434. package/dist/pipeline-core/validate.js.map +1 -0
  435. package/dist/playbook/catalog-loader.js +14 -1
  436. package/dist/playbook/catalog-loader.js.map +1 -1
  437. package/dist/playbook/import-mapper.js +14 -38
  438. package/dist/playbook/import-mapper.js.map +1 -1
  439. package/dist/playbook/manifest.js +1 -1
  440. package/dist/revisium/playbooks.service.js +107 -19
  441. package/dist/revisium/playbooks.service.js.map +1 -1
  442. package/dist/revisium/roles.service.js +1 -1
  443. package/dist/revisium/roles.service.js.map +1 -1
  444. package/dist/revisium/run.service.js +55 -8
  445. package/dist/revisium/run.service.js.map +1 -1
  446. package/dist/run/append-event.js +24 -1
  447. package/dist/run/append-event.js.map +1 -1
  448. package/dist/run/block-run.js +17 -0
  449. package/dist/run/block-run.js.map +1 -0
  450. package/dist/run/complete-run.js +18 -0
  451. package/dist/run/complete-run.js.map +1 -0
  452. package/dist/run/create-run.js +33 -36
  453. package/dist/run/create-run.js.map +1 -1
  454. package/dist/run/inspect-run.js +31 -30
  455. package/dist/run/inspect-run.js.map +1 -1
  456. package/dist/run/run-outputs.js +88 -0
  457. package/dist/run/run-outputs.js.map +1 -0
  458. package/dist/run/terminal-run-status.js +66 -7
  459. package/dist/run/terminal-run-status.js.map +1 -1
  460. package/dist/runners/claude-code.service.js +5 -0
  461. package/dist/runners/claude-code.service.js.map +1 -1
  462. package/dist/runners/codex.service.js +51 -0
  463. package/dist/runners/codex.service.js.map +1 -0
  464. package/dist/runners/integrator-branch-naming.js +39 -0
  465. package/dist/runners/integrator-branch-naming.js.map +1 -0
  466. package/dist/runners/integrator-git.js +84 -0
  467. package/dist/runners/integrator-git.js.map +1 -0
  468. package/dist/runners/integrator-remote.js +34 -0
  469. package/dist/runners/integrator-remote.js.map +1 -0
  470. package/dist/runners/integrator-types.js +9 -0
  471. package/dist/runners/integrator-types.js.map +1 -0
  472. package/dist/runners/integrator.js +327 -147
  473. package/dist/runners/integrator.js.map +1 -1
  474. package/dist/runners/runner.module.js +7 -3
  475. package/dist/runners/runner.module.js.map +1 -1
  476. package/dist/runners/worktree.service.js +63 -0
  477. package/dist/runners/worktree.service.js.map +1 -0
  478. package/dist/smoke/cli.js +47 -0
  479. package/dist/smoke/cli.js.map +1 -0
  480. package/dist/smoke/isolation.js +113 -0
  481. package/dist/smoke/isolation.js.map +1 -0
  482. package/dist/task-control-plane/run-watch.service.js +281 -0
  483. package/dist/task-control-plane/run-watch.service.js.map +1 -0
  484. package/dist/task-control-plane/task-control-plane-api.service.js +569 -23
  485. package/dist/task-control-plane/task-control-plane-api.service.js.map +1 -1
  486. package/dist/task-control-plane/task-control-plane.module.js +22 -3
  487. package/dist/task-control-plane/task-control-plane.module.js.map +1 -1
  488. package/dist/worker/artifact-store.js +116 -0
  489. package/dist/worker/artifact-store.js.map +1 -0
  490. package/dist/worker/build-context.js +169 -2
  491. package/dist/worker/build-context.js.map +1 -1
  492. package/dist/worker/claude-code-runner.js +159 -68
  493. package/dist/worker/claude-code-runner.js.map +1 -1
  494. package/dist/worker/codex-runner.js +474 -0
  495. package/dist/worker/codex-runner.js.map +1 -0
  496. package/dist/worker/git-worktree-manager.js +129 -65
  497. package/dist/worker/git-worktree-manager.js.map +1 -1
  498. package/dist/worker/process-executor.js +23 -2
  499. package/dist/worker/process-executor.js.map +1 -1
  500. package/dist/worker/result-envelope.js +115 -57
  501. package/dist/worker/result-envelope.js.map +1 -1
  502. package/dist/worker/runner-common.js +106 -0
  503. package/dist/worker/runner-common.js.map +1 -0
  504. package/dist/worker/runner-dispatch.js +4 -1
  505. package/dist/worker/runner-dispatch.js.map +1 -1
  506. package/dist/worker/runner.js +11 -1
  507. package/dist/worker/runner.js.map +1 -1
  508. package/dist/worker/stub-runner.js +7 -45
  509. package/dist/worker/stub-runner.js.map +1 -1
  510. package/package.json +32 -7
  511. package/dist/app.module.js +0 -28
  512. package/dist/app.module.js.map +0 -1
  513. package/dist/cli/commands/bootstrap.js +0 -39
  514. package/dist/cli/commands/bootstrap.js.map +0 -1
  515. package/dist/cli/commands/dev.js +0 -143
  516. package/dist/cli/commands/dev.js.map +0 -1
  517. package/dist/cli/commands/inbox.js +0 -288
  518. package/dist/cli/commands/inbox.js.map +0 -1
  519. package/dist/cli/commands/playbook.js +0 -74
  520. package/dist/cli/commands/playbook.js.map +0 -1
  521. package/dist/cli/commands/poll-workflow-state.js +0 -144
  522. package/dist/cli/commands/poll-workflow-state.js.map +0 -1
  523. package/dist/cli/commands/revisium-context.js +0 -23
  524. package/dist/cli/commands/revisium-context.js.map +0 -1
  525. package/dist/cli/commands/revisium.js +0 -89
  526. package/dist/cli/commands/revisium.js.map +0 -1
  527. package/dist/cli/commands/run.js +0 -426
  528. package/dist/cli/commands/run.js.map +0 -1
  529. package/dist/cli/commands/work.js +0 -149
  530. package/dist/cli/commands/work.js.map +0 -1
  531. package/dist/cli/live-guard.js +0 -49
  532. package/dist/cli/live-guard.js.map +0 -1
  533. package/dist/cli/needs-host.js +0 -78
  534. package/dist/cli/needs-host.js.map +0 -1
  535. package/dist/mcp/mcp-stdio.service.js +0 -48
  536. package/dist/mcp/mcp-stdio.service.js.map +0 -1
  537. package/dist/mcp/mcp.module.js.map +0 -1
  538. package/dist/pipeline/develop-task.workflow.js +0 -497
  539. package/dist/pipeline/develop-task.workflow.js.map +0 -1
  540. package/dist/worker/loop.js +0 -122
  541. package/dist/worker/loop.js.map +0 -1
  542. package/dist/worker/script-runner.js +0 -28
  543. package/dist/worker/script-runner.js.map +0 -1
  544. package/dist/worker/worker-id.js +0 -18
  545. package/dist/worker/worker-id.js.map +0 -1
  546. package/dist/worker/worktree-manager.js +0 -7
  547. package/dist/worker/worktree-manager.js.map +0 -1
@@ -0,0 +1,1142 @@
1
+ import { open, readdir, readFile, lstat, realpath } from 'node:fs/promises';
2
+ import { constants } from 'node:fs';
3
+ import { isAbsolute, join, relative, resolve, win32 } from 'node:path';
4
+ import { TextDecoder } from 'node:util';
5
+ import { redactTokens } from '../runners/gh-identity.js';
6
+ import { AGENT_ACTIVITY_EVENT_KEY, AGENT_OUTPUT_STREAM_KEY, AgentObservabilityError, } from './types.js';
7
+ const DEFAULT_READ_BYTES = 65_536;
8
+ const MAX_READ_BYTES = 1_048_576;
9
+ const MAX_METADATA_BYTES = 65_536;
10
+ const SAFE_SEGMENT_RE = /^[A-Za-z0-9_.:-]+$/;
11
+ const LOG_FILES = {
12
+ stdout: 'stdout.log',
13
+ stderr: 'stderr.log',
14
+ events: 'events.jsonl',
15
+ };
16
+ const STDOUT_MARKER = '--- stdout ---\n';
17
+ const STDERR_MARKER = '\n--- stderr ---\n';
18
+ const UTF8_DECODER = new TextDecoder('utf-8', { fatal: true });
19
+ const UTF8_BOUNDARY_CONTEXT_BYTES = 3;
20
+ const SENSITIVE_BOUNDARY_SCAN_BYTES = 65_536;
21
+ const TOKEN_PATTERN = /\b(?:gh[opsru]_[A-Za-z0-9]{20,}|github_pat_[A-Za-z0-9_]{20,})\b/g;
22
+ const WINDOWS_PATH_PATTERN = /\b[A-Za-z]:\\[^\s'"`]+/g;
23
+ const POSIX_PATH_PATTERN = /\/(?:Users|private|tmp|var|opt|home|workspace|Volumes)\/[^\s'"`)]+/g;
24
+ const DEFAULT_STREAM_EVENT_LIMIT = 100;
25
+ const MAX_STREAM_EVENT_LIMIT = 1_000;
26
+ const DEFAULT_STREAM_READ_TIMEOUT_MS = 250;
27
+ const DEFAULT_IDLE_THRESHOLD_MS = 120_000;
28
+ const MAX_STREAM_ACTIVITY_SCAN_EVENTS = 10_000;
29
+ const SAFE_CURSOR_RE = /^[A-Za-z0-9_.:-]+$/;
30
+ const OUTPUT_EVENT_KINDS = new Set(['activity', 'output', 'parsed_event', 'status']);
31
+ const ACTIVITY_STATUSES = new Set([
32
+ 'starting',
33
+ 'running',
34
+ 'idle',
35
+ 'permission_blocked',
36
+ 'cancelled',
37
+ 'exited',
38
+ 'timed_out',
39
+ 'failed',
40
+ ]);
41
+ export class AgentObservabilityService {
42
+ artifactRoot;
43
+ runExists;
44
+ dbos;
45
+ idleThresholdMs;
46
+ now;
47
+ constructor(options) {
48
+ this.artifactRoot = resolve(options.artifactRoot);
49
+ this.runExists = options.runExists;
50
+ this.dbos = options.dbos;
51
+ this.idleThresholdMs = validateIdleThreshold(options.idleThresholdMs);
52
+ this.now = options.now ?? (() => Date.now());
53
+ }
54
+ async getAgentActivity(runId) {
55
+ const safeRunId = validateSegment(runId, 'runId');
56
+ await this.assertRunExists(safeRunId);
57
+ const eventSnapshot = await this.dbos?.getEvent(safeRunId, AGENT_ACTIVITY_EVENT_KEY, { timeoutSeconds: 0 });
58
+ if (eventSnapshot)
59
+ return this.classifyActivity(eventSnapshot);
60
+ const streamActivity = await this.readLatestAgentActivityFromStream(safeRunId);
61
+ if (streamActivity)
62
+ return streamActivity;
63
+ return this.getAgentActivityFromArtifacts(safeRunId);
64
+ }
65
+ async readAgentOutputEvents(input) {
66
+ const safeRunId = validateSegment(input.runId, 'runId');
67
+ const cursor = input.cursor === undefined ? undefined : validateCursor(input.cursor);
68
+ const limit = validateEventLimit(input.limit);
69
+ const timeoutMs = validateStreamTimeout(input.timeoutMs);
70
+ await this.assertRunExists(safeRunId);
71
+ if (!this.dbos) {
72
+ return { runId: safeRunId, events: [], cursorExpired: cursor !== undefined };
73
+ }
74
+ const generator = this.dbos.readStream(safeRunId, AGENT_OUTPUT_STREAM_KEY);
75
+ return withGeneratorCleanup(generator, async () => {
76
+ const page = await readBoundedOutputEvents({
77
+ generator,
78
+ runId: safeRunId,
79
+ cursor,
80
+ limit,
81
+ timeoutMs,
82
+ });
83
+ return {
84
+ runId: safeRunId,
85
+ events: page.events,
86
+ nextCursor: page.events.at(-1)?.cursor,
87
+ cursorExpired: hasExpiredCursor(cursor, page.cursorFound),
88
+ };
89
+ });
90
+ }
91
+ async *watchAgentOutput(input) {
92
+ const safeRunId = validateSegment(input.runId, 'runId');
93
+ const cursor = input.cursor === undefined ? undefined : validateCursor(input.cursor);
94
+ if (!this.dbos) {
95
+ throw new AgentObservabilityError('DBOS_STREAM_UNAVAILABLE', 'DBOS stream reader is not configured');
96
+ }
97
+ let cursorFound = cursor === undefined;
98
+ let scannedBeforeCursor = 0;
99
+ for await (const raw of this.dbos.readStream(safeRunId, AGENT_OUTPUT_STREAM_KEY)) {
100
+ if (!cursorFound)
101
+ scannedBeforeCursor += 1;
102
+ const event = normalizeOutputEvent(safeRunId, raw);
103
+ if (!event) {
104
+ if (scannedBeforeCursor >= MAX_STREAM_EVENT_LIMIT)
105
+ throw cursorExpiredError();
106
+ continue;
107
+ }
108
+ if (cursorFound) {
109
+ yield event;
110
+ continue;
111
+ }
112
+ cursorFound = event.cursor === cursor;
113
+ if (cursorFound)
114
+ continue;
115
+ if (scannedBeforeCursor >= MAX_STREAM_EVENT_LIMIT)
116
+ throw cursorExpiredError();
117
+ }
118
+ if (hasExpiredCursor(cursor, cursorFound)) {
119
+ throw cursorExpiredError();
120
+ }
121
+ }
122
+ async *watchAgentActivity(input) {
123
+ const snapshots = new Map();
124
+ for await (const event of this.watchAgentOutput(input)) {
125
+ if (!event.snapshot)
126
+ continue;
127
+ snapshots.set(event.attemptId, this.classifySnapshot(event.snapshot));
128
+ yield this.buildRunActivity(event.runId, [...snapshots.values()]);
129
+ }
130
+ }
131
+ async listAgentAttempts(runId) {
132
+ const safeRunId = validateSegment(runId, 'runId');
133
+ const runDir = await this.resolveRunDirectory(safeRunId);
134
+ if (!runDir.exists)
135
+ return [];
136
+ const entries = await readdir(runDir.path, { withFileTypes: true });
137
+ const attempts = [];
138
+ for (const entry of entries) {
139
+ if (entry.isSymbolicLink()) {
140
+ throw validationFailure('artifact attempt must not be a symlink');
141
+ }
142
+ if (!entry.isDirectory())
143
+ continue;
144
+ const attemptId = validateSegment(entry.name, 'attemptId');
145
+ const attemptDir = join(runDir.path, attemptId);
146
+ await ensureDirectoryInsideRoot(attemptDir, runDir.realRoot, 'agent attempt');
147
+ attempts.push(await this.readAttemptSummary(safeRunId, attemptId, attemptDir, runDir.realRoot));
148
+ }
149
+ return attempts
150
+ .sort((a, b) => {
151
+ const timeDiff = compareOptionalTimeAsc(a.startedSortAt, b.startedSortAt);
152
+ return timeDiff !== 0 ? timeDiff : a.attemptId.localeCompare(b.attemptId);
153
+ })
154
+ .map(({ latestSortAt: _latestSortAt, startedSortAt: _startedSortAt, ...summary }) => summary);
155
+ }
156
+ async getAgentLog(input) {
157
+ const safeRunId = validateSegment(input.runId, 'runId');
158
+ const stream = validateStream(input.stream);
159
+ const bounds = validateBounds(input);
160
+ const explicitAttemptId = input.attemptId !== undefined ? validateSegment(input.attemptId, 'attemptId') : undefined;
161
+ const runDir = await this.resolveRunDirectory(safeRunId);
162
+ const attemptId = explicitAttemptId ?? (await this.selectLatestAttemptId(safeRunId));
163
+ if (!runDir.exists) {
164
+ throw new AgentObservabilityError('NO_AGENT_ATTEMPT_AVAILABLE', 'no agent attempt artifacts are available');
165
+ }
166
+ const attemptDir = join(runDir.path, attemptId);
167
+ const attemptExists = await directoryExistsInsideRoot(attemptDir, runDir.realRoot, 'agent attempt');
168
+ if (!attemptExists) {
169
+ throw new AgentObservabilityError('NO_AGENT_ATTEMPT_AVAILABLE', 'agent attempt artifacts are not available');
170
+ }
171
+ if (stream === 'combined') {
172
+ return this.readCombinedLog(safeRunId, attemptId, attemptDir, runDir.realRoot, bounds);
173
+ }
174
+ const file = await resolveOptionalFile(join(attemptDir, LOG_FILES[stream]), runDir.realRoot, `${stream} log`);
175
+ if (!file.exists)
176
+ return emptyChunk(safeRunId, attemptId, stream);
177
+ const chunk = await readFileChunk(file, bounds);
178
+ return {
179
+ runId: safeRunId,
180
+ attemptId,
181
+ stream,
182
+ ...chunk,
183
+ };
184
+ }
185
+ async selectLatestAttemptId(runId) {
186
+ const attempts = await this.listAgentAttempts(runId);
187
+ if (attempts.length === 0) {
188
+ throw new AgentObservabilityError('NO_AGENT_ATTEMPT_AVAILABLE', 'no agent attempt artifacts are available');
189
+ }
190
+ return attempts
191
+ .map((attempt) => ({
192
+ ...attempt,
193
+ latestSortAt: parseDate(attempt.finishedAt) ?? parseDate(attempt.startedAt),
194
+ }))
195
+ .sort((a, b) => {
196
+ const timeDiff = compareOptionalTimeAsc(a.latestSortAt, b.latestSortAt);
197
+ return timeDiff !== 0 ? timeDiff : a.attemptId.localeCompare(b.attemptId);
198
+ })
199
+ .at(-1).attemptId;
200
+ }
201
+ async getAgentActivityFromArtifacts(runId) {
202
+ let attempts;
203
+ try {
204
+ attempts = await this.listAgentAttempts(runId);
205
+ }
206
+ catch (err) {
207
+ if (err instanceof AgentObservabilityError) {
208
+ if (err.code === 'NO_AGENT_ATTEMPT_AVAILABLE')
209
+ return null;
210
+ }
211
+ throw err;
212
+ }
213
+ if (attempts.length === 0)
214
+ return null;
215
+ return this.buildRunActivity(runId, attempts.map((attempt) => {
216
+ const finishedAt = attempt.finishedAt;
217
+ const latestAt = finishedAt ?? attempt.startedAt;
218
+ const snapshot = {
219
+ runId,
220
+ attemptId: attempt.attemptId,
221
+ stepId: attempt.stepId,
222
+ ...(attempt.stepKey ? { stepKey: attempt.stepKey } : {}),
223
+ role: attempt.role,
224
+ runner: attempt.runner,
225
+ status: statusFromArtifact(attempt.status, attempt.timedOut),
226
+ startedAt: attempt.startedAt,
227
+ lastEventAt: latestAt,
228
+ stdoutBytes: attempt.stdoutBytes,
229
+ stderrBytes: attempt.stderrBytes,
230
+ eventCount: 0,
231
+ artifactRef: attempt.artifactRef,
232
+ };
233
+ if (finishedAt)
234
+ snapshot.lastOutputAt = finishedAt;
235
+ const exitCode = attempt.exitCode;
236
+ if (exitCode === null || typeof exitCode === 'number')
237
+ snapshot.exitCode = exitCode;
238
+ const timedOut = attempt.timedOut;
239
+ if (typeof timedOut === 'boolean')
240
+ snapshot.timedOut = timedOut;
241
+ return snapshot;
242
+ }));
243
+ }
244
+ async readLatestAgentActivityFromStream(runId) {
245
+ if (!this.dbos)
246
+ return null;
247
+ const generator = this.dbos.readStream(runId, AGENT_OUTPUT_STREAM_KEY);
248
+ const snapshots = new Map();
249
+ let scanned = 0;
250
+ await withGeneratorCleanup(generator, async () => {
251
+ while (scanned < MAX_STREAM_ACTIVITY_SCAN_EVENTS) {
252
+ const next = await nextWithTimeout(generator, DEFAULT_STREAM_READ_TIMEOUT_MS);
253
+ if (next === 'timeout' || next.done)
254
+ break;
255
+ scanned += 1;
256
+ const event = normalizeOutputEvent(runId, next.value);
257
+ if (event?.snapshot)
258
+ snapshots.set(event.attemptId, event.snapshot);
259
+ }
260
+ });
261
+ if (scanned >= MAX_STREAM_ACTIVITY_SCAN_EVENTS)
262
+ return null;
263
+ if (snapshots.size === 0)
264
+ return null;
265
+ return this.buildRunActivity(runId, [...snapshots.values()]);
266
+ }
267
+ buildRunActivity(runId, attempts) {
268
+ return buildRunActivity(runId, attempts.map((attempt) => this.classifySnapshot(redactActivitySnapshot(attempt))));
269
+ }
270
+ classifyActivity(activity) {
271
+ return this.buildRunActivity(activity.runId, activity.attempts);
272
+ }
273
+ classifySnapshot(snapshot) {
274
+ if (snapshot.status !== 'starting' && snapshot.status !== 'running')
275
+ return snapshot;
276
+ const latestAt = parseDate(snapshot.lastOutputAt) ?? parseDate(snapshot.lastEventAt);
277
+ if (latestAt === undefined)
278
+ return snapshot;
279
+ if (this.now() - latestAt < this.idleThresholdMs)
280
+ return snapshot;
281
+ return { ...snapshot, status: 'idle' };
282
+ }
283
+ async assertRunExists(runId) {
284
+ if (!this.runExists)
285
+ return;
286
+ const exists = await this.runExists(runId);
287
+ if (!exists)
288
+ throw new AgentObservabilityError('RUN_NOT_FOUND', 'run was not found');
289
+ }
290
+ async resolveRunDirectory(runId) {
291
+ if (this.runExists) {
292
+ const exists = await this.runExists(runId);
293
+ if (!exists)
294
+ throw new AgentObservabilityError('RUN_NOT_FOUND', 'run was not found');
295
+ }
296
+ const realRoot = await resolveRootIfPresent(this.artifactRoot);
297
+ const runPath = join(this.artifactRoot, runId);
298
+ const exists = realRoot
299
+ ? await directoryExistsInsideRoot(runPath, realRoot, 'run artifact')
300
+ : false;
301
+ if (!exists) {
302
+ if (this.runExists)
303
+ return { exists: false, realRoot };
304
+ throw new AgentObservabilityError('RUN_NOT_FOUND', 'run artifact directory was not found');
305
+ }
306
+ if (!realRoot)
307
+ throw new AgentObservabilityError('RUN_NOT_FOUND', 'run artifact directory was not found');
308
+ return { exists: true, path: runPath, realRoot };
309
+ }
310
+ async readAttemptSummary(runId, attemptId, attemptDir, realRoot) {
311
+ const dirStat = await lstat(attemptDir);
312
+ const meta = await readMetadata(join(attemptDir, 'meta.json'), realRoot);
313
+ const stdoutBytes = await optionalFileSize(join(attemptDir, 'stdout.log'), realRoot, 'stdout log');
314
+ const stderrBytes = await optionalFileSize(join(attemptDir, 'stderr.log'), realRoot, 'stderr log');
315
+ const startedAt = stringValue(meta.startedAt) ?? dirStat.mtime.toISOString();
316
+ const finishedAt = stringValue(meta.finishedAt);
317
+ const exitCode = numberOrNull(meta.exitCode) ?? numberOrNull(meta.code);
318
+ const timedOut = booleanValue(meta.timedOut);
319
+ const summary = {
320
+ runId,
321
+ attemptId,
322
+ stepId: redactPublicText(stringValue(meta.stepId) ?? ''),
323
+ role: redactPublicText(stringValue(meta.role) ?? 'unknown'),
324
+ runner: redactPublicText(stringValue(meta.runner) ?? 'unknown'),
325
+ artifactRef: validateArtifactRef(`${runId}/${attemptId}`),
326
+ startedAt: redactPublicText(startedAt),
327
+ status: redactPublicText(stringValue(meta.status) ?? 'unknown'),
328
+ stdoutBytes,
329
+ stderrBytes,
330
+ startedSortAt: parseDate(startedAt),
331
+ latestSortAt: parseDate(finishedAt) ?? parseDate(startedAt),
332
+ };
333
+ const stepKey = stringValue(meta.stepKey);
334
+ if (stepKey)
335
+ summary.stepKey = redactPublicText(stepKey);
336
+ if (finishedAt)
337
+ summary.finishedAt = redactPublicText(finishedAt);
338
+ if (exitCode !== undefined)
339
+ summary.exitCode = exitCode;
340
+ if (timedOut !== undefined)
341
+ summary.timedOut = timedOut;
342
+ return summary;
343
+ }
344
+ async readCombinedLog(runId, attemptId, attemptDir, realRoot, bounds) {
345
+ const stdout = await resolveOptionalFile(join(attemptDir, 'stdout.log'), realRoot, 'stdout log');
346
+ const stderr = await resolveOptionalFile(join(attemptDir, 'stderr.log'), realRoot, 'stderr log');
347
+ const segments = [
348
+ bufferSegment(STDOUT_MARKER),
349
+ { kind: 'file', file: stdout, size: stdout.size },
350
+ bufferSegment(STDERR_MARKER),
351
+ { kind: 'file', file: stderr, size: stderr.size },
352
+ ];
353
+ const chunk = await readSyntheticChunk(segments, bounds);
354
+ return {
355
+ runId,
356
+ attemptId,
357
+ stream: 'combined',
358
+ ...chunk,
359
+ };
360
+ }
361
+ }
362
+ function validateSegment(value, label) {
363
+ if (value.length === 0 ||
364
+ value.includes('\0') ||
365
+ value.includes('/') ||
366
+ value.includes('\\') ||
367
+ value === '.' ||
368
+ value === '..' ||
369
+ isAbsolute(value) ||
370
+ win32.isAbsolute(value) ||
371
+ !SAFE_SEGMENT_RE.test(value)) {
372
+ throw validationFailure(`invalid ${label}`);
373
+ }
374
+ return value;
375
+ }
376
+ function validateArtifactRef(value) {
377
+ const parts = value.split('/');
378
+ if (parts.length !== 2)
379
+ throw validationFailure('invalid artifactRef');
380
+ return `${validateSegment(parts[0] ?? '', 'artifactRef runId')}/${validateSegment(parts[1] ?? '', 'artifactRef attemptId')}`;
381
+ }
382
+ function validateStream(stream) {
383
+ if (stream !== 'stdout' && stream !== 'stderr' && stream !== 'events' && stream !== 'combined') {
384
+ throw validationFailure('invalid log stream');
385
+ }
386
+ return stream;
387
+ }
388
+ function validateBounds(input) {
389
+ const hasTail = input.tailBytes !== undefined;
390
+ const hasOffsetOrLimit = input.offsetBytes !== undefined || input.limitBytes !== undefined;
391
+ if (hasTail && hasOffsetOrLimit) {
392
+ throw validationFailure('tailBytes cannot be combined with offsetBytes or limitBytes');
393
+ }
394
+ if (hasTail) {
395
+ const tailBytes = validatePositiveByteCount(input.tailBytes, 'tailBytes');
396
+ return { mode: 'tail', tailBytes };
397
+ }
398
+ if (hasOffsetOrLimit) {
399
+ const offsetBytes = input.offsetBytes ?? 0;
400
+ if (!Number.isInteger(offsetBytes) || offsetBytes < 0)
401
+ throw validationFailure('offsetBytes must be non-negative');
402
+ const limitBytes = validatePositiveByteCount(input.limitBytes ?? DEFAULT_READ_BYTES, 'limitBytes');
403
+ return { mode: 'offset', offsetBytes, limitBytes };
404
+ }
405
+ return { mode: 'tail', tailBytes: DEFAULT_READ_BYTES };
406
+ }
407
+ function validatePositiveByteCount(value, label) {
408
+ if (value === undefined || !Number.isInteger(value) || value <= 0) {
409
+ throw validationFailure(`${label} must be a positive integer`);
410
+ }
411
+ if (value > MAX_READ_BYTES)
412
+ throw validationFailure(`${label} exceeds maximum allowed bytes`);
413
+ return value;
414
+ }
415
+ function validateEventLimit(value) {
416
+ const limit = value ?? DEFAULT_STREAM_EVENT_LIMIT;
417
+ if (!Number.isInteger(limit) || limit <= 0)
418
+ throw validationFailure('limit must be a positive integer');
419
+ if (limit > MAX_STREAM_EVENT_LIMIT)
420
+ throw validationFailure('limit exceeds maximum allowed events');
421
+ return limit;
422
+ }
423
+ function validateStreamTimeout(value) {
424
+ const timeout = value ?? DEFAULT_STREAM_READ_TIMEOUT_MS;
425
+ if (!Number.isInteger(timeout) || timeout <= 0)
426
+ throw validationFailure('timeoutMs must be a positive integer');
427
+ return timeout;
428
+ }
429
+ function validateIdleThreshold(value) {
430
+ const threshold = value ?? DEFAULT_IDLE_THRESHOLD_MS;
431
+ if (!Number.isInteger(threshold) || threshold <= 0) {
432
+ throw validationFailure('idleThresholdMs must be a positive integer');
433
+ }
434
+ return threshold;
435
+ }
436
+ function validateCursor(value) {
437
+ if (value.length === 0 || value.length > 512 || !SAFE_CURSOR_RE.test(value)) {
438
+ throw validationFailure('invalid cursor');
439
+ }
440
+ return value;
441
+ }
442
+ async function readBoundedOutputEvents(input) {
443
+ const events = [];
444
+ let cursorFound = input.cursor === undefined;
445
+ let scannedBeforeCursor = 0;
446
+ while (events.length < input.limit) {
447
+ const next = await nextWithTimeout(input.generator, input.timeoutMs);
448
+ if (next === 'timeout' || next.done)
449
+ break;
450
+ const state = processOutputEventCandidate(input.runId, input.cursor, next.value, cursorFound, scannedBeforeCursor);
451
+ cursorFound = state.cursorFound;
452
+ scannedBeforeCursor = state.scannedBeforeCursor;
453
+ if (state.event)
454
+ events.push(state.event);
455
+ if (state.stop)
456
+ break;
457
+ }
458
+ return { events, cursorFound };
459
+ }
460
+ function processOutputEventCandidate(runId, cursor, value, cursorFound, scannedBeforeCursor) {
461
+ const nextScanned = cursorFound ? scannedBeforeCursor : scannedBeforeCursor + 1;
462
+ const event = normalizeOutputEvent(runId, value);
463
+ if (event) {
464
+ if (cursorFound)
465
+ return { event, cursorFound, scannedBeforeCursor: nextScanned, stop: false };
466
+ const found = event.cursor === cursor;
467
+ return {
468
+ cursorFound: found,
469
+ scannedBeforeCursor: nextScanned,
470
+ stop: cursorMissedScanLimit(found, nextScanned),
471
+ };
472
+ }
473
+ return {
474
+ cursorFound,
475
+ scannedBeforeCursor: nextScanned,
476
+ stop: cursor !== undefined && cursorFound === false && nextScanned >= MAX_STREAM_EVENT_LIMIT,
477
+ };
478
+ }
479
+ function hasExpiredCursor(cursor, cursorFound) {
480
+ return typeof cursor === 'string' && cursorFound === false;
481
+ }
482
+ function cursorExpiredError() {
483
+ return new AgentObservabilityError('STREAM_CURSOR_EXPIRED', 'stream cursor was not found before the scan limit');
484
+ }
485
+ function cursorMissedScanLimit(cursorFound, scannedBeforeCursor) {
486
+ if (cursorFound)
487
+ return false;
488
+ return scannedBeforeCursor >= MAX_STREAM_EVENT_LIMIT;
489
+ }
490
+ async function nextWithTimeout(generator, timeoutMs) {
491
+ let timer;
492
+ try {
493
+ return await Promise.race([
494
+ generator.next(),
495
+ new Promise((resolve) => {
496
+ timer = setTimeout(() => resolve('timeout'), timeoutMs);
497
+ }),
498
+ ]);
499
+ }
500
+ finally {
501
+ if (timer)
502
+ clearTimeout(timer);
503
+ }
504
+ }
505
+ async function withGeneratorCleanup(generator, read) {
506
+ let result;
507
+ let hasResult = false;
508
+ let primaryError;
509
+ try {
510
+ result = await read();
511
+ hasResult = true;
512
+ }
513
+ catch (error) {
514
+ primaryError = error;
515
+ }
516
+ let cleanupError;
517
+ try {
518
+ await generator.return(undefined);
519
+ }
520
+ catch (error) {
521
+ cleanupError = error;
522
+ }
523
+ if (primaryError !== undefined)
524
+ throw primaryError;
525
+ if (cleanupError !== undefined)
526
+ throw cleanupError;
527
+ if (hasResult)
528
+ return result;
529
+ throw validationFailure('stream reader did not produce a result');
530
+ }
531
+ function normalizeOutputEvent(runId, value) {
532
+ const event = asRecord(value);
533
+ if (event?.runId !== runId)
534
+ return null;
535
+ const fields = normalizeOutputEventFields(event);
536
+ if (!fields)
537
+ return null;
538
+ const normalized = {
539
+ cursor: fields.cursor,
540
+ runId,
541
+ attemptId: fields.attemptId,
542
+ stepId: redactPublicText(fields.stepId),
543
+ at: fields.at,
544
+ kind: fields.kind,
545
+ };
546
+ applyOutputEventOptionalFields(normalized, event, runId, fields.attemptId);
547
+ return normalized;
548
+ }
549
+ function normalizeOutputEventFields(event) {
550
+ const cursor = normalizeCursorValue(event.cursor);
551
+ const attemptId = normalizeSegmentValue(event.attemptId, 'attemptId');
552
+ const stepId = normalizeNonEmptyString(event.stepId);
553
+ const at = normalizeNonEmptyString(event.at);
554
+ const kind = normalizeOutputEventKind(event.kind);
555
+ if (!cursor || !attemptId || !stepId || !at || !kind)
556
+ return undefined;
557
+ return { cursor, attemptId, stepId, at, kind };
558
+ }
559
+ function applyOutputEventOptionalFields(normalized, event, runId, attemptId) {
560
+ applyOutputEventIdentityFields(normalized, event);
561
+ applyOutputEventByteFields(normalized, event);
562
+ applyOutputEventTextFields(normalized, event);
563
+ const statusHint = normalizeActivityStatus(event.statusHint);
564
+ if (statusHint)
565
+ normalized.statusHint = statusHint;
566
+ const snapshot = normalizeActivitySnapshotForOutput(runId, attemptId, event.snapshot);
567
+ if (snapshot)
568
+ normalized.snapshot = snapshot;
569
+ }
570
+ function applyOutputEventIdentityFields(normalized, event) {
571
+ const stepKey = normalizeNonEmptyString(event.stepKey);
572
+ if (stepKey)
573
+ normalized.stepKey = redactPublicText(stepKey);
574
+ const attemptSeq = normalizeNonNegativeInteger(event.attemptSeq);
575
+ if (attemptSeq !== undefined)
576
+ normalized.attemptSeq = attemptSeq;
577
+ const stream = normalizeOutputStream(event.stream);
578
+ if (stream)
579
+ normalized.stream = stream;
580
+ }
581
+ function applyOutputEventByteFields(normalized, event) {
582
+ const bytes = normalizeNonNegativeInteger(event.bytes);
583
+ if (bytes !== undefined)
584
+ normalized.bytes = bytes;
585
+ const outputOffsetBytes = normalizeNonNegativeInteger(event.outputOffsetBytes);
586
+ if (outputOffsetBytes !== undefined)
587
+ normalized.outputOffsetBytes = outputOffsetBytes;
588
+ }
589
+ function applyOutputEventTextFields(normalized, event) {
590
+ if (typeof event.preview === 'string')
591
+ normalized.preview = redactPublicText(event.preview);
592
+ if (typeof event.parsedType === 'string')
593
+ normalized.parsedType = redactPublicText(event.parsedType);
594
+ }
595
+ function normalizeCursorValue(value) {
596
+ if (typeof value !== 'string')
597
+ return undefined;
598
+ try {
599
+ return validateCursor(value);
600
+ }
601
+ catch {
602
+ return undefined;
603
+ }
604
+ }
605
+ function normalizeSegmentValue(value, name) {
606
+ if (typeof value !== 'string')
607
+ return undefined;
608
+ try {
609
+ return validateSegment(value, name);
610
+ }
611
+ catch {
612
+ return undefined;
613
+ }
614
+ }
615
+ function normalizeNonEmptyString(value) {
616
+ return typeof value === 'string' && value.length > 0 ? value : undefined;
617
+ }
618
+ function normalizeString(value) {
619
+ return typeof value === 'string' ? value : undefined;
620
+ }
621
+ function normalizeOutputEventKind(value) {
622
+ if (typeof value !== 'string')
623
+ return undefined;
624
+ const kind = value;
625
+ return OUTPUT_EVENT_KINDS.has(kind) ? kind : undefined;
626
+ }
627
+ function normalizeOutputStream(value) {
628
+ if (value === undefined)
629
+ return undefined;
630
+ if (value === 'stdout' || value === 'stderr' || value === 'agent-jsonl')
631
+ return value;
632
+ return undefined;
633
+ }
634
+ function normalizeActivityStatus(value) {
635
+ if (typeof value !== 'string')
636
+ return undefined;
637
+ const status = value;
638
+ return ACTIVITY_STATUSES.has(status) ? status : undefined;
639
+ }
640
+ function normalizeActivitySnapshotForOutput(runId, attemptId, value) {
641
+ const snapshot = asRecord(value);
642
+ if (!snapshot)
643
+ return undefined;
644
+ if (snapshot.runId !== runId || snapshot.attemptId !== attemptId)
645
+ return undefined;
646
+ const fields = normalizeActivitySnapshotFields(snapshot);
647
+ if (!fields)
648
+ return undefined;
649
+ const normalized = {
650
+ runId,
651
+ attemptId,
652
+ stepId: redactPublicText(fields.stepId),
653
+ role: redactPublicText(fields.role),
654
+ runner: redactPublicText(fields.runner),
655
+ status: fields.status,
656
+ startedAt: fields.startedAt,
657
+ lastEventAt: fields.lastEventAt,
658
+ stdoutBytes: fields.stdoutBytes,
659
+ stderrBytes: fields.stderrBytes,
660
+ eventCount: fields.eventCount,
661
+ artifactRef: redactPublicText(fields.artifactRef),
662
+ };
663
+ applyActivitySnapshotOptionalFields(normalized, snapshot);
664
+ return normalized;
665
+ }
666
+ function normalizeActivitySnapshotFields(snapshot) {
667
+ const fields = {
668
+ stepId: normalizeNonEmptyString(snapshot.stepId),
669
+ role: normalizeString(snapshot.role),
670
+ runner: normalizeString(snapshot.runner),
671
+ status: normalizeActivityStatus(snapshot.status),
672
+ startedAt: normalizeString(snapshot.startedAt),
673
+ lastEventAt: normalizeString(snapshot.lastEventAt),
674
+ stdoutBytes: normalizeNonNegativeInteger(snapshot.stdoutBytes),
675
+ stderrBytes: normalizeNonNegativeInteger(snapshot.stderrBytes),
676
+ eventCount: normalizeNonNegativeInteger(snapshot.eventCount),
677
+ artifactRef: normalizeNonEmptyString(snapshot.artifactRef),
678
+ };
679
+ if (Object.values(fields).includes(undefined))
680
+ return undefined;
681
+ return fields;
682
+ }
683
+ function applyActivitySnapshotOptionalFields(normalized, snapshot) {
684
+ const stepKey = normalizeNonEmptyString(snapshot.stepKey);
685
+ if (stepKey)
686
+ normalized.stepKey = redactPublicText(stepKey);
687
+ if (typeof snapshot.pid === 'number' && Number.isInteger(snapshot.pid) && snapshot.pid > 0)
688
+ normalized.pid = snapshot.pid;
689
+ if (typeof snapshot.lastOutputAt === 'string')
690
+ normalized.lastOutputAt = snapshot.lastOutputAt;
691
+ const lastStream = normalizeOutputStream(snapshot.lastStream);
692
+ if (lastStream)
693
+ normalized.lastStream = lastStream;
694
+ if (typeof snapshot.exitCode === 'number' && Number.isInteger(snapshot.exitCode))
695
+ normalized.exitCode = snapshot.exitCode;
696
+ if (snapshot.exitCode === null)
697
+ normalized.exitCode = null;
698
+ if (typeof snapshot.timedOut === 'boolean')
699
+ normalized.timedOut = snapshot.timedOut;
700
+ if (typeof snapshot.error === 'string')
701
+ normalized.error = redactPublicText(snapshot.error);
702
+ }
703
+ function normalizeNonNegativeInteger(value) {
704
+ if (typeof value !== 'number' || !Number.isSafeInteger(value) || value < 0)
705
+ return undefined;
706
+ return value;
707
+ }
708
+ function asRecord(value) {
709
+ if (!value || typeof value !== 'object' || Array.isArray(value))
710
+ return undefined;
711
+ return value;
712
+ }
713
+ function redactActivitySnapshot(snapshot) {
714
+ return {
715
+ ...snapshot,
716
+ stepId: redactPublicText(snapshot.stepId),
717
+ ...(snapshot.stepKey ? { stepKey: redactPublicText(snapshot.stepKey) } : {}),
718
+ role: redactPublicText(snapshot.role),
719
+ runner: redactPublicText(snapshot.runner),
720
+ artifactRef: redactPublicText(snapshot.artifactRef),
721
+ ...(snapshot.error ? { error: redactPublicText(snapshot.error) } : {}),
722
+ };
723
+ }
724
+ function buildRunActivity(runId, attempts) {
725
+ const sorted = [...attempts];
726
+ sorted.sort((a, b) => {
727
+ const startedDiff = (parseDate(a.startedAt) ?? 0) - (parseDate(b.startedAt) ?? 0);
728
+ if (startedDiff === 0)
729
+ return a.attemptId.localeCompare(b.attemptId);
730
+ return startedDiff;
731
+ });
732
+ const activityTimes = sorted
733
+ .map((attempt) => attempt.lastEventAt || attempt.startedAt)
734
+ .sort((a, b) => a.localeCompare(b));
735
+ const latestActivityAt = activityTimes.at(-1) ?? new Date(0).toISOString();
736
+ const outputTimes = sorted
737
+ .map((attempt) => attempt.lastOutputAt)
738
+ .filter((value) => typeof value === 'string' && value.length > 0)
739
+ .sort((a, b) => a.localeCompare(b));
740
+ const latestOutputAt = outputTimes.at(-1);
741
+ return {
742
+ runId,
743
+ aggregateStatus: aggregateStatus(sorted),
744
+ latestActivityAt,
745
+ ...(latestOutputAt ? { latestOutputAt } : {}),
746
+ attempts: sorted,
747
+ };
748
+ }
749
+ function aggregateStatus(attempts) {
750
+ const statuses = new Set(attempts.map((attempt) => attempt.status));
751
+ if (statuses.has('timed_out'))
752
+ return 'timed_out';
753
+ if (statuses.has('cancelled'))
754
+ return 'cancelled';
755
+ if (statuses.has('failed'))
756
+ return 'failed';
757
+ if (statuses.has('permission_blocked'))
758
+ return 'permission_blocked';
759
+ if (statuses.has('running') || statuses.has('starting'))
760
+ return 'running';
761
+ if (statuses.has('idle'))
762
+ return 'idle';
763
+ return 'exited';
764
+ }
765
+ function statusFromArtifact(status, timedOut) {
766
+ if (timedOut || status === 'timed_out')
767
+ return 'timed_out';
768
+ if (status === 'error' || status === 'failed')
769
+ return 'failed';
770
+ if (status === 'running' || status === 'starting')
771
+ return 'running';
772
+ if (status === 'cancelled')
773
+ return 'cancelled';
774
+ if (status === 'permission_blocked')
775
+ return 'permission_blocked';
776
+ if (status === 'idle')
777
+ return 'idle';
778
+ return 'exited';
779
+ }
780
+ async function resolveRootIfPresent(root) {
781
+ try {
782
+ const rootStat = await lstat(root);
783
+ if (!rootStat.isDirectory() || rootStat.isSymbolicLink()) {
784
+ throw validationFailure('artifact root must be a directory');
785
+ }
786
+ return await realpath(root);
787
+ }
788
+ catch (error) {
789
+ if (isNotFound(error))
790
+ return undefined;
791
+ throw error;
792
+ }
793
+ }
794
+ async function directoryExistsInsideRoot(path, realRoot, label) {
795
+ try {
796
+ await ensureDirectoryInsideRoot(path, realRoot, label);
797
+ return true;
798
+ }
799
+ catch (error) {
800
+ if (isNotFound(error))
801
+ return false;
802
+ throw error;
803
+ }
804
+ }
805
+ async function ensureDirectoryInsideRoot(path, realRoot, label) {
806
+ const itemStat = await lstat(path);
807
+ if (itemStat.isSymbolicLink())
808
+ throw validationFailure(`${label} must not be a symlink`);
809
+ if (!itemStat.isDirectory())
810
+ throw validationFailure(`${label} must be a directory`);
811
+ assertInsideRoot(await realpath(path), realRoot);
812
+ }
813
+ async function resolveOptionalFile(path, realRoot, label) {
814
+ try {
815
+ const itemStat = await lstat(path);
816
+ if (itemStat.isSymbolicLink())
817
+ throw validationFailure(`${label} must not be a symlink`);
818
+ if (!itemStat.isFile())
819
+ throw validationFailure(`${label} must be a file`);
820
+ const realFilePath = await realpath(path);
821
+ assertInsideRoot(realFilePath, realRoot);
822
+ return { exists: true, path: realFilePath, size: itemStat.size };
823
+ }
824
+ catch (error) {
825
+ if (isNotFound(error))
826
+ return { exists: false, size: 0 };
827
+ throw error;
828
+ }
829
+ }
830
+ async function optionalFileSize(path, realRoot, label) {
831
+ return (await resolveOptionalFile(path, realRoot, label)).size;
832
+ }
833
+ async function readMetadata(path, realRoot) {
834
+ const file = await resolveOptionalFile(path, realRoot, 'metadata');
835
+ if (!file.exists)
836
+ return {};
837
+ if (file.size > MAX_METADATA_BYTES) {
838
+ throw validationFailure('artifact metadata exceeds maximum allowed bytes');
839
+ }
840
+ try {
841
+ const content = await readFile(file.path, 'utf8');
842
+ const parsed = JSON.parse(content);
843
+ return parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? parsed : {};
844
+ }
845
+ catch {
846
+ throw validationFailure('invalid artifact metadata');
847
+ }
848
+ }
849
+ async function readFileChunk(file, bounds) {
850
+ const totalBytes = file.size;
851
+ const range = rangeForBounds(totalBytes, bounds);
852
+ if (range.start === range.end) {
853
+ return {
854
+ offsetBytes: range.start,
855
+ nextOffsetBytes: range.end,
856
+ totalBytes,
857
+ truncated: range.truncated,
858
+ content: '',
859
+ };
860
+ }
861
+ const expanded = expandRange(range.start, range.end, totalBytes, SENSITIVE_BOUNDARY_SCAN_BYTES);
862
+ const expandedWindow = alignUtf8Window(await readFileRange(file.path, expanded.start, expanded.end), expanded.start);
863
+ const aligned = alignUtf8Range(expandedWindow.buffer, range.start - expandedWindow.absoluteStart, range.end - expandedWindow.absoluteStart, expandedWindow.absoluteStart);
864
+ const content = redactPublicChunk(expandedWindow, aligned, totalBytes);
865
+ return {
866
+ offsetBytes: aligned.start,
867
+ nextOffsetBytes: aligned.end,
868
+ totalBytes,
869
+ truncated: bounds.mode === 'tail' ? aligned.start > 0 : aligned.end < totalBytes,
870
+ content,
871
+ };
872
+ }
873
+ async function readSyntheticChunk(segments, bounds) {
874
+ const totalBytes = segments.reduce((sum, segment) => sum + segment.size, 0);
875
+ const range = rangeForBounds(totalBytes, bounds);
876
+ if (range.start === range.end) {
877
+ return {
878
+ offsetBytes: range.start,
879
+ nextOffsetBytes: range.end,
880
+ totalBytes,
881
+ truncated: range.truncated,
882
+ content: '',
883
+ };
884
+ }
885
+ const expanded = expandRange(range.start, range.end, totalBytes, SENSITIVE_BOUNDARY_SCAN_BYTES);
886
+ const expandedWindow = alignUtf8Window(await readSyntheticRange(segments, expanded.start, expanded.end), expanded.start);
887
+ const aligned = alignUtf8Range(expandedWindow.buffer, range.start - expandedWindow.absoluteStart, range.end - expandedWindow.absoluteStart, expandedWindow.absoluteStart);
888
+ const content = redactPublicChunk(expandedWindow, aligned, totalBytes);
889
+ return {
890
+ offsetBytes: aligned.start,
891
+ nextOffsetBytes: aligned.end,
892
+ totalBytes,
893
+ truncated: bounds.mode === 'tail' ? aligned.start > 0 : aligned.end < totalBytes,
894
+ content,
895
+ };
896
+ }
897
+ function rangeForBounds(totalBytes, bounds) {
898
+ if (bounds.mode === 'tail') {
899
+ const start = Math.max(0, totalBytes - bounds.tailBytes);
900
+ return { start, end: totalBytes, truncated: start > 0 };
901
+ }
902
+ const start = Math.min(bounds.offsetBytes, totalBytes);
903
+ const end = Math.min(totalBytes, start + bounds.limitBytes);
904
+ return { start, end, truncated: end < totalBytes };
905
+ }
906
+ function expandRange(start, end, totalBytes, contextBytes) {
907
+ return {
908
+ start: Math.max(0, start - contextBytes - UTF8_BOUNDARY_CONTEXT_BYTES),
909
+ end: Math.min(totalBytes, end + contextBytes + UTF8_BOUNDARY_CONTEXT_BYTES),
910
+ };
911
+ }
912
+ async function readFileRange(path, start, end) {
913
+ const length = Math.max(0, end - start);
914
+ const buffer = Buffer.alloc(length);
915
+ const handle = await open(path, constants.O_RDONLY);
916
+ try {
917
+ let filled = 0;
918
+ while (filled < length) {
919
+ const { bytesRead } = await handle.read(buffer, filled, length - filled, start + filled);
920
+ if (bytesRead === 0)
921
+ break;
922
+ filled += bytesRead;
923
+ }
924
+ return filled === length ? buffer : buffer.subarray(0, filled);
925
+ }
926
+ finally {
927
+ await handle.close();
928
+ }
929
+ }
930
+ async function readSyntheticRange(segments, start, end) {
931
+ const chunks = [];
932
+ let cursor = 0;
933
+ for (const segment of segments) {
934
+ const segmentStart = cursor;
935
+ const segmentEnd = cursor + segment.size;
936
+ cursor = segmentEnd;
937
+ if (end <= segmentStart || start >= segmentEnd)
938
+ continue;
939
+ const localStart = Math.max(0, start - segmentStart);
940
+ const localEnd = Math.min(segment.size, end - segmentStart);
941
+ if (segment.kind === 'buffer') {
942
+ chunks.push(segment.buffer.subarray(localStart, localEnd));
943
+ }
944
+ else if (segment.file.exists) {
945
+ chunks.push(await readFileRange(segment.file.path, localStart, localEnd));
946
+ }
947
+ }
948
+ return Buffer.concat(chunks);
949
+ }
950
+ function alignUtf8Range(buffer, requestedStartIndex, requestedEndIndex, absoluteBufferStart) {
951
+ let startIndex = Math.max(0, requestedStartIndex);
952
+ while (startIndex < buffer.length && isUtf8ContinuationByte(buffer[startIndex]))
953
+ startIndex += 1;
954
+ let endIndex = Math.min(buffer.length, requestedEndIndex);
955
+ while (endIndex > startIndex && endIndex < buffer.length && isUtf8ContinuationByte(buffer[endIndex]))
956
+ endIndex -= 1;
957
+ if (endIndex < startIndex)
958
+ endIndex = startIndex;
959
+ return {
960
+ startIndex,
961
+ endIndex,
962
+ start: absoluteBufferStart + startIndex,
963
+ end: absoluteBufferStart + endIndex,
964
+ };
965
+ }
966
+ function isUtf8ContinuationByte(value) {
967
+ return value !== undefined && (value & 0b1100_0000) === 0b1000_0000;
968
+ }
969
+ function decodeUtf8(buffer) {
970
+ try {
971
+ return UTF8_DECODER.decode(buffer);
972
+ }
973
+ catch {
974
+ throw validationFailure('log content is not valid UTF-8');
975
+ }
976
+ }
977
+ function redactPublicChunk(expandedWindow, aligned, totalBytes) {
978
+ const expandedText = decodeUtf8(expandedWindow.buffer);
979
+ const absoluteBufferStart = expandedWindow.absoluteStart;
980
+ const absoluteBufferEnd = expandedWindow.absoluteStart + expandedWindow.buffer.byteLength;
981
+ const sensitiveSpans = findSensitiveSpans(expandedText, absoluteBufferStart, absoluteBufferEnd, totalBytes, aligned.start, aligned.end);
982
+ const requestedStart = aligned.start;
983
+ const requestedEnd = aligned.end;
984
+ let cursor = requestedStart;
985
+ let content = '';
986
+ for (const span of sensitiveSpans) {
987
+ if (span.end <= cursor || span.start >= requestedEnd)
988
+ continue;
989
+ const safeEnd = Math.min(span.start, requestedEnd);
990
+ if (cursor < safeEnd) {
991
+ content += decodeUtf8(expandedWindow.buffer.subarray(cursor - absoluteBufferStart, safeEnd - absoluteBufferStart));
992
+ }
993
+ content += span.replacement;
994
+ cursor = Math.min(Math.max(cursor, span.end), requestedEnd);
995
+ }
996
+ if (cursor < requestedEnd) {
997
+ content += decodeUtf8(expandedWindow.buffer.subarray(cursor - absoluteBufferStart, requestedEnd - absoluteBufferStart));
998
+ }
999
+ return redactPublicText(content);
1000
+ }
1001
+ function alignUtf8Window(buffer, absoluteStart) {
1002
+ let startIndex = 0;
1003
+ while (startIndex < buffer.length && isUtf8ContinuationByte(buffer[startIndex]))
1004
+ startIndex += 1;
1005
+ for (let endIndex = buffer.length; endIndex >= startIndex; endIndex -= 1) {
1006
+ const candidate = buffer.subarray(startIndex, endIndex);
1007
+ try {
1008
+ UTF8_DECODER.decode(candidate);
1009
+ return {
1010
+ buffer: candidate,
1011
+ absoluteStart: absoluteStart + startIndex,
1012
+ };
1013
+ }
1014
+ catch {
1015
+ if (buffer.length - endIndex > UTF8_BOUNDARY_CONTEXT_BYTES)
1016
+ break;
1017
+ }
1018
+ }
1019
+ throw validationFailure('log content is not valid UTF-8');
1020
+ }
1021
+ function findSensitiveSpans(text, absoluteBufferStart, absoluteBufferEnd, totalBytes, requestedStart, requestedEnd) {
1022
+ return [
1023
+ ...findPatternSpans(text, TOKEN_PATTERN, '[REDACTED]', absoluteBufferStart),
1024
+ ...findPatternSpans(text, WINDOWS_PATH_PATTERN, '[REDACTED_PATH]', absoluteBufferStart),
1025
+ ...findPatternSpans(text, POSIX_PATH_PATTERN, '[REDACTED_PATH]', absoluteBufferStart),
1026
+ ...findUnprovenSensitiveSpans(text, absoluteBufferStart, absoluteBufferEnd, totalBytes, requestedStart, requestedEnd),
1027
+ ].sort((a, b) => {
1028
+ const diff = a.start - b.start;
1029
+ if (diff !== 0)
1030
+ return diff;
1031
+ return b.end - a.end;
1032
+ });
1033
+ }
1034
+ function findUnprovenSensitiveSpans(text, absoluteBufferStart, absoluteBufferEnd, totalBytes, requestedStart, requestedEnd) {
1035
+ const spans = [];
1036
+ let runStartIndex;
1037
+ for (let index = 0; index <= text.length; index += 1) {
1038
+ const char = index < text.length ? text[index] : undefined;
1039
+ if (char !== undefined && !isSensitiveDelimiter(char)) {
1040
+ runStartIndex ??= index;
1041
+ continue;
1042
+ }
1043
+ if (runStartIndex === undefined)
1044
+ continue;
1045
+ const runEndIndex = index;
1046
+ const runStart = absoluteBufferStart + Buffer.byteLength(text.slice(0, runStartIndex), 'utf8');
1047
+ const runEnd = absoluteBufferStart + Buffer.byteLength(text.slice(0, runEndIndex), 'utf8');
1048
+ const leftBoundaryProven = runStartIndex > 0 || absoluteBufferStart === 0;
1049
+ const rightBoundaryProven = runEndIndex < text.length || absoluteBufferEnd === totalBytes;
1050
+ if (runEnd > requestedStart &&
1051
+ runStart < requestedEnd &&
1052
+ (!leftBoundaryProven || !rightBoundaryProven)) {
1053
+ spans.push({
1054
+ start: runStart,
1055
+ end: runEnd,
1056
+ replacement: runContainsPathSeparator(text.slice(runStartIndex, runEndIndex))
1057
+ ? '[REDACTED_PATH]'
1058
+ : '[REDACTED]',
1059
+ });
1060
+ }
1061
+ runStartIndex = undefined;
1062
+ }
1063
+ return spans;
1064
+ }
1065
+ function isSensitiveDelimiter(char) {
1066
+ return /\s/.test(char) || '"\'`<>(){}[],;'.includes(char);
1067
+ }
1068
+ function runContainsPathSeparator(value) {
1069
+ return value.includes('/') || value.includes('\\');
1070
+ }
1071
+ function findPatternSpans(text, pattern, replacement, absoluteBufferStart) {
1072
+ const spans = [];
1073
+ pattern.lastIndex = 0;
1074
+ for (const match of text.matchAll(pattern)) {
1075
+ const matchText = match[0];
1076
+ if (match.index === undefined || matchText.length === 0)
1077
+ continue;
1078
+ const start = absoluteBufferStart + Buffer.byteLength(text.slice(0, match.index), 'utf8');
1079
+ const end = start + Buffer.byteLength(matchText, 'utf8');
1080
+ spans.push({ start, end, replacement });
1081
+ }
1082
+ return spans;
1083
+ }
1084
+ function bufferSegment(value) {
1085
+ const buffer = Buffer.from(value, 'utf8');
1086
+ return { kind: 'buffer', buffer, size: buffer.byteLength };
1087
+ }
1088
+ function assertInsideRoot(candidateRealPath, realRoot) {
1089
+ const rel = relative(realRoot, candidateRealPath);
1090
+ if (rel === '..' || rel.startsWith('../') || rel.startsWith('..\\') || isAbsolute(rel) || win32.isAbsolute(rel)) {
1091
+ throw validationFailure('artifact path escapes the artifact root');
1092
+ }
1093
+ }
1094
+ function emptyChunk(runId, attemptId, stream) {
1095
+ return {
1096
+ runId,
1097
+ attemptId,
1098
+ stream,
1099
+ offsetBytes: 0,
1100
+ totalBytes: 0,
1101
+ truncated: false,
1102
+ content: '',
1103
+ };
1104
+ }
1105
+ function stringValue(value) {
1106
+ return typeof value === 'string' ? value : undefined;
1107
+ }
1108
+ function numberOrNull(value) {
1109
+ if (value === null)
1110
+ return null;
1111
+ return typeof value === 'number' && Number.isFinite(value) ? value : undefined;
1112
+ }
1113
+ function booleanValue(value) {
1114
+ return typeof value === 'boolean' ? value : undefined;
1115
+ }
1116
+ function parseDate(value) {
1117
+ if (!value)
1118
+ return undefined;
1119
+ const time = Date.parse(value);
1120
+ return Number.isNaN(time) ? undefined : time;
1121
+ }
1122
+ function compareOptionalTimeAsc(left, right) {
1123
+ if (left !== undefined && right !== undefined)
1124
+ return left - right;
1125
+ if (left === undefined && right !== undefined)
1126
+ return -1;
1127
+ if (left !== undefined && right === undefined)
1128
+ return 1;
1129
+ return 0;
1130
+ }
1131
+ function redactPublicText(text) {
1132
+ return redactTokens(text)
1133
+ .replace(/\b[A-Za-z]:\\[^\s'"`]+/g, '[REDACTED_PATH]')
1134
+ .replace(/(^|[\s"'`=:(])\/(?:Users|private|tmp|var|opt|home|workspace|Volumes)\/[^\s"'`)]+/g, '$1[REDACTED_PATH]');
1135
+ }
1136
+ function validationFailure(message) {
1137
+ return new AgentObservabilityError('VALIDATION_FAILURE', message);
1138
+ }
1139
+ function isNotFound(error) {
1140
+ return typeof error === 'object' && error !== null && 'code' in error && error.code === 'ENOENT';
1141
+ }
1142
+ //# sourceMappingURL=agent-observability.service.js.map