@agimon-ai/doompi-workflow 0.0.1-alpha.73 → 0.0.1-alpha.76

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 (218) hide show
  1. package/LICENSE +4 -2
  2. package/README.md +8 -5
  3. package/package.json +19 -18
  4. package/src/types/webWorkflowTerminal.ts +7 -6
  5. package/dist/_virtual/_rolldown/runtime.cjs +0 -1
  6. package/dist/api-contracts.cjs +0 -1
  7. package/dist/api-contracts.d.cts +0 -2
  8. package/dist/api-contracts.d.mts +0 -2
  9. package/dist/api-contracts.mjs +0 -1
  10. package/dist/extensions/pi.cjs +0 -2
  11. package/dist/extensions/pi.cjs.map +0 -1
  12. package/dist/extensions/pi.d.cts +0 -6
  13. package/dist/extensions/pi.d.cts.map +0 -1
  14. package/dist/extensions/pi.d.mts +0 -6
  15. package/dist/extensions/pi.d.mts.map +0 -1
  16. package/dist/extensions/pi.mjs +0 -2
  17. package/dist/extensions/pi.mjs.map +0 -1
  18. package/dist/extensions/server.cjs +0 -2
  19. package/dist/extensions/server.cjs.map +0 -1
  20. package/dist/extensions/server.d.cts +0 -5
  21. package/dist/extensions/server.d.cts.map +0 -1
  22. package/dist/extensions/server.d.mts +0 -5
  23. package/dist/extensions/server.d.mts.map +0 -1
  24. package/dist/extensions/server.mjs +0 -2
  25. package/dist/extensions/server.mjs.map +0 -1
  26. package/dist/index.cjs +0 -1
  27. package/dist/index.d.cts +0 -2
  28. package/dist/index.d.mts +0 -2
  29. package/dist/index.mjs +0 -1
  30. package/dist/src/constants/workflow.cjs +0 -2
  31. package/dist/src/constants/workflow.cjs.map +0 -1
  32. package/dist/src/constants/workflow.mjs +0 -2
  33. package/dist/src/constants/workflow.mjs.map +0 -1
  34. package/dist/src/controllers/serverRuntime.cjs +0 -2
  35. package/dist/src/controllers/serverRuntime.cjs.map +0 -1
  36. package/dist/src/controllers/serverRuntime.mjs +0 -2
  37. package/dist/src/controllers/serverRuntime.mjs.map +0 -1
  38. package/dist/src/controllers/workflowCatalogChannel.cjs +0 -2
  39. package/dist/src/controllers/workflowCatalogChannel.cjs.map +0 -1
  40. package/dist/src/controllers/workflowCatalogChannel.mjs +0 -2
  41. package/dist/src/controllers/workflowCatalogChannel.mjs.map +0 -1
  42. package/dist/src/controllers/workflowHubApi.cjs +0 -2
  43. package/dist/src/controllers/workflowHubApi.cjs.map +0 -1
  44. package/dist/src/controllers/workflowHubApi.mjs +0 -2
  45. package/dist/src/controllers/workflowHubApi.mjs.map +0 -1
  46. package/dist/src/controllers/workflowsHubChannel.cjs +0 -2
  47. package/dist/src/controllers/workflowsHubChannel.cjs.map +0 -1
  48. package/dist/src/controllers/workflowsHubChannel.mjs +0 -2
  49. package/dist/src/controllers/workflowsHubChannel.mjs.map +0 -1
  50. package/dist/src/schemas/apiContracts.cjs +0 -2
  51. package/dist/src/schemas/apiContracts.cjs.map +0 -1
  52. package/dist/src/schemas/apiContracts.d.cts +0 -53
  53. package/dist/src/schemas/apiContracts.d.cts.map +0 -1
  54. package/dist/src/schemas/apiContracts.d.mts +0 -53
  55. package/dist/src/schemas/apiContracts.d.mts.map +0 -1
  56. package/dist/src/schemas/apiContracts.mjs +0 -2
  57. package/dist/src/schemas/apiContracts.mjs.map +0 -1
  58. package/dist/src/schemas/workflowPi.cjs +0 -2
  59. package/dist/src/schemas/workflowPi.cjs.map +0 -1
  60. package/dist/src/schemas/workflowPi.d.cts +0 -1
  61. package/dist/src/schemas/workflowPi.d.mts +0 -1
  62. package/dist/src/schemas/workflowPi.mjs +0 -2
  63. package/dist/src/schemas/workflowPi.mjs.map +0 -1
  64. package/dist/src/services/backgroundWork/index.cjs +0 -2
  65. package/dist/src/services/backgroundWork/index.cjs.map +0 -1
  66. package/dist/src/services/backgroundWork/index.mjs +0 -2
  67. package/dist/src/services/backgroundWork/index.mjs.map +0 -1
  68. package/dist/src/services/piToolBridge/index.cjs +0 -11
  69. package/dist/src/services/piToolBridge/index.cjs.map +0 -1
  70. package/dist/src/services/piToolBridge/index.mjs +0 -11
  71. package/dist/src/services/piToolBridge/index.mjs.map +0 -1
  72. package/dist/src/services/webWorkflowCatalog/index.cjs +0 -2
  73. package/dist/src/services/webWorkflowCatalog/index.cjs.map +0 -1
  74. package/dist/src/services/webWorkflowCatalog/index.mjs +0 -2
  75. package/dist/src/services/webWorkflowCatalog/index.mjs.map +0 -1
  76. package/dist/src/services/workflowCatalogDeps/index.cjs +0 -2
  77. package/dist/src/services/workflowCatalogDeps/index.cjs.map +0 -1
  78. package/dist/src/services/workflowCatalogDeps/index.mjs +0 -2
  79. package/dist/src/services/workflowCatalogDeps/index.mjs.map +0 -1
  80. package/dist/src/services/workflowExecution/index.cjs +0 -2
  81. package/dist/src/services/workflowExecution/index.cjs.map +0 -1
  82. package/dist/src/services/workflowExecution/index.d.cts +0 -6
  83. package/dist/src/services/workflowExecution/index.d.mts +0 -6
  84. package/dist/src/services/workflowExecution/index.mjs +0 -2
  85. package/dist/src/services/workflowExecution/index.mjs.map +0 -1
  86. package/dist/src/services/workflowFence/index.cjs +0 -2
  87. package/dist/src/services/workflowFence/index.cjs.map +0 -1
  88. package/dist/src/services/workflowFence/index.mjs +0 -2
  89. package/dist/src/services/workflowFence/index.mjs.map +0 -1
  90. package/dist/src/services/workflowLaunchCommand/index.cjs +0 -2
  91. package/dist/src/services/workflowLaunchCommand/index.cjs.map +0 -1
  92. package/dist/src/services/workflowLaunchCommand/index.d.cts +0 -52
  93. package/dist/src/services/workflowLaunchCommand/index.d.cts.map +0 -1
  94. package/dist/src/services/workflowLaunchCommand/index.d.mts +0 -52
  95. package/dist/src/services/workflowLaunchCommand/index.d.mts.map +0 -1
  96. package/dist/src/services/workflowLaunchCommand/index.mjs +0 -2
  97. package/dist/src/services/workflowLaunchCommand/index.mjs.map +0 -1
  98. package/dist/src/services/workflowLauncher/index.cjs +0 -2
  99. package/dist/src/services/workflowLauncher/index.cjs.map +0 -1
  100. package/dist/src/services/workflowLauncher/index.mjs +0 -2
  101. package/dist/src/services/workflowLauncher/index.mjs.map +0 -1
  102. package/dist/src/services/workflowNarration/index.cjs +0 -2
  103. package/dist/src/services/workflowNarration/index.cjs.map +0 -1
  104. package/dist/src/services/workflowNarration/index.mjs +0 -2
  105. package/dist/src/services/workflowNarration/index.mjs.map +0 -1
  106. package/dist/src/services/workflowResource/index.cjs +0 -2
  107. package/dist/src/services/workflowResource/index.cjs.map +0 -1
  108. package/dist/src/services/workflowResource/index.mjs +0 -2
  109. package/dist/src/services/workflowResource/index.mjs.map +0 -1
  110. package/dist/src/services/workflowRuns/index.cjs +0 -3
  111. package/dist/src/services/workflowRuns/index.cjs.map +0 -1
  112. package/dist/src/services/workflowRuns/index.mjs +0 -3
  113. package/dist/src/services/workflowRuns/index.mjs.map +0 -1
  114. package/dist/src/services/workflowTerminal/index.cjs +0 -2
  115. package/dist/src/services/workflowTerminal/index.cjs.map +0 -1
  116. package/dist/src/services/workflowTerminal/index.mjs +0 -2
  117. package/dist/src/services/workflowTerminal/index.mjs.map +0 -1
  118. package/dist/src/services/workflowWatcher/index.cjs +0 -2
  119. package/dist/src/services/workflowWatcher/index.cjs.map +0 -1
  120. package/dist/src/services/workflowWatcher/index.mjs +0 -2
  121. package/dist/src/services/workflowWatcher/index.mjs.map +0 -1
  122. package/dist/src/tools/workflowTools.cjs +0 -5
  123. package/dist/src/tools/workflowTools.cjs.map +0 -1
  124. package/dist/src/tools/workflowTools.mjs +0 -5
  125. package/dist/src/tools/workflowTools.mjs.map +0 -1
  126. package/dist/src/tui/leader.cjs +0 -2
  127. package/dist/src/tui/leader.cjs.map +0 -1
  128. package/dist/src/tui/leader.mjs +0 -2
  129. package/dist/src/tui/leader.mjs.map +0 -1
  130. package/dist/src/tui/workflow/doomOverlay.cjs +0 -2
  131. package/dist/src/tui/workflow/doomOverlay.cjs.map +0 -1
  132. package/dist/src/tui/workflow/doomOverlay.mjs +0 -2
  133. package/dist/src/tui/workflow/doomOverlay.mjs.map +0 -1
  134. package/dist/src/tui/workflow/overlayText.cjs +0 -2
  135. package/dist/src/tui/workflow/overlayText.cjs.map +0 -1
  136. package/dist/src/tui/workflow/overlayText.mjs +0 -2
  137. package/dist/src/tui/workflow/overlayText.mjs.map +0 -1
  138. package/dist/src/tui/workflow/rendering.cjs +0 -2
  139. package/dist/src/tui/workflow/rendering.cjs.map +0 -1
  140. package/dist/src/tui/workflow/rendering.mjs +0 -2
  141. package/dist/src/tui/workflow/rendering.mjs.map +0 -1
  142. package/dist/src/tui/workflow/workflowCatalog.cjs +0 -2
  143. package/dist/src/tui/workflow/workflowCatalog.cjs.map +0 -1
  144. package/dist/src/tui/workflow/workflowCatalog.mjs +0 -2
  145. package/dist/src/tui/workflow/workflowCatalog.mjs.map +0 -1
  146. package/dist/src/tui/workflow/workflowFinishedMessage.cjs +0 -3
  147. package/dist/src/tui/workflow/workflowFinishedMessage.cjs.map +0 -1
  148. package/dist/src/tui/workflow/workflowFinishedMessage.mjs +0 -3
  149. package/dist/src/tui/workflow/workflowFinishedMessage.mjs.map +0 -1
  150. package/dist/src/tui/workflow/workflowInspector.cjs +0 -2
  151. package/dist/src/tui/workflow/workflowInspector.cjs.map +0 -1
  152. package/dist/src/tui/workflow/workflowInspector.mjs +0 -2
  153. package/dist/src/tui/workflow/workflowInspector.mjs.map +0 -1
  154. package/dist/src/tui/workflow/workflowOverlay.cjs +0 -2
  155. package/dist/src/tui/workflow/workflowOverlay.cjs.map +0 -1
  156. package/dist/src/tui/workflow/workflowOverlay.mjs +0 -2
  157. package/dist/src/tui/workflow/workflowOverlay.mjs.map +0 -1
  158. package/dist/src/tui/workflow/workflowPicker.cjs +0 -2
  159. package/dist/src/tui/workflow/workflowPicker.cjs.map +0 -1
  160. package/dist/src/tui/workflow/workflowPicker.mjs +0 -2
  161. package/dist/src/tui/workflow/workflowPicker.mjs.map +0 -1
  162. package/dist/src/tui/workflow/workflowProgressOverlay.cjs +0 -2
  163. package/dist/src/tui/workflow/workflowProgressOverlay.cjs.map +0 -1
  164. package/dist/src/tui/workflow/workflowProgressOverlay.mjs +0 -2
  165. package/dist/src/tui/workflow/workflowProgressOverlay.mjs.map +0 -1
  166. package/dist/src/tui/workflow/workflowPrompt.cjs +0 -2
  167. package/dist/src/tui/workflow/workflowPrompt.cjs.map +0 -1
  168. package/dist/src/tui/workflow/workflowPrompt.mjs +0 -2
  169. package/dist/src/tui/workflow/workflowPrompt.mjs.map +0 -1
  170. package/dist/src/tui/workflow/workflowRunPanel.cjs +0 -2
  171. package/dist/src/tui/workflow/workflowRunPanel.cjs.map +0 -1
  172. package/dist/src/tui/workflow/workflowRunPanel.mjs +0 -2
  173. package/dist/src/tui/workflow/workflowRunPanel.mjs.map +0 -1
  174. package/dist/src/tui/workflow/workflowStatusRow.cjs +0 -2
  175. package/dist/src/tui/workflow/workflowStatusRow.cjs.map +0 -1
  176. package/dist/src/tui/workflow/workflowStatusRow.mjs +0 -2
  177. package/dist/src/tui/workflow/workflowStatusRow.mjs.map +0 -1
  178. package/dist/src/tui/workflow/workflowStepMessage.cjs +0 -2
  179. package/dist/src/tui/workflow/workflowStepMessage.cjs.map +0 -1
  180. package/dist/src/tui/workflow/workflowStepMessage.mjs +0 -2
  181. package/dist/src/tui/workflow/workflowStepMessage.mjs.map +0 -1
  182. package/dist/src/tui/workflow/workflowToolRender.cjs +0 -7
  183. package/dist/src/tui/workflow/workflowToolRender.cjs.map +0 -1
  184. package/dist/src/tui/workflow/workflowToolRender.mjs +0 -7
  185. package/dist/src/tui/workflow/workflowToolRender.mjs.map +0 -1
  186. package/dist/src/tui/workflowRuntime.cjs +0 -9
  187. package/dist/src/tui/workflowRuntime.cjs.map +0 -1
  188. package/dist/src/tui/workflowRuntime.d.cts +0 -33
  189. package/dist/src/tui/workflowRuntime.d.cts.map +0 -1
  190. package/dist/src/tui/workflowRuntime.d.mts +0 -33
  191. package/dist/src/tui/workflowRuntime.d.mts.map +0 -1
  192. package/dist/src/tui/workflowRuntime.mjs +0 -9
  193. package/dist/src/tui/workflowRuntime.mjs.map +0 -1
  194. package/dist/src/types/webWorkflowTerminal.cjs +0 -2
  195. package/dist/src/types/webWorkflowTerminal.cjs.map +0 -1
  196. package/dist/src/types/webWorkflowTerminal.mjs +0 -2
  197. package/dist/src/types/webWorkflowTerminal.mjs.map +0 -1
  198. package/dist/src/types/webWorkflows.cjs +0 -2
  199. package/dist/src/types/webWorkflows.cjs.map +0 -1
  200. package/dist/src/types/webWorkflows.mjs +0 -2
  201. package/dist/src/types/webWorkflows.mjs.map +0 -1
  202. package/src/extensions/web.ts +0 -80
  203. package/src/web/api/terminalApi.ts +0 -208
  204. package/src/web/components/ArtifactsPane.tsx +0 -451
  205. package/src/web/components/LaunchWorkflowDialog.tsx +0 -259
  206. package/src/web/components/StepTerminalPanel.tsx +0 -243
  207. package/src/web/components/WorkflowCatalogDrawer.tsx +0 -229
  208. package/src/web/components/WorkflowToolMessage.tsx +0 -55
  209. package/src/web/components/WorkflowsActivitySection.tsx +0 -155
  210. package/src/web/components/WorkflowsPanel.tsx +0 -750
  211. package/src/web/lib/launchLine.ts +0 -66
  212. package/src/web/lib/runDuration.ts +0 -15
  213. package/src/web/lib/workflowActivity.ts +0 -102
  214. package/src/web/lib/workflowToolRender.ts +0 -585
  215. package/src/web/stores/catalogStore.ts +0 -123
  216. package/src/web/stores/workflowsStore.ts +0 -91
  217. package/src/web/tsconfig.json +0 -22
  218. /package/{src/web/LICENSE → LICENSE.web} +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"file":"workflowHubApi.cjs","names":["WorkflowRegistryService","WorkflowTerminalFacade","createWorkflowTerminalService","Hono","streamSSE","WORKFLOW_SCREEN_EVENT","randomUUID","path","fs","Readable","WORKFLOW_API_BASE_PATH"],"sources":["../../../src/controllers/workflowHubApi.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { Readable } from 'node:stream';\n\nimport type { DoomApi, DoomApiContext, DoomApiHandler } from '@agimon-ai/doompi-core/package-api';\nimport {\n WorkflowRegistryService,\n WorkflowTerminalService as WorkflowTerminalFacade,\n type WorkflowRunRecord,\n type WorkflowStage,\n} from '@agimon-ai/workflow-mcp';\nimport { type Context, Hono } from 'hono';\nimport { streamSSE } from 'hono/streaming';\n\nimport { createWorkflowTerminalService } from '../services/workflowTerminal';\nimport {\n WORKFLOW_API_BASE_PATH,\n WORKFLOW_SCREEN_EVENT,\n type WorkflowArtifactContentResponse,\n type WorkflowArtifactsResponse,\n type WorkflowArtifactView,\n type WorkflowControlResponse,\n type WorkflowDeleteResponse,\n type WorkflowScreenEvent,\n} from '../types/webWorkflowTerminal';\n\n/** Stages a run can be recorded under, newest first: a live run is the common case. */\nconst STAGES: readonly WorkflowStage[] = ['running', 'error', 'completed'];\n/** Lines of screen the stream sends, which is a terminal's visible height plus room. */\nconst SCREEN_LINES = 48;\n/** How often the stream re-reads; the service coalesces, so this is an upper bound. */\nconst STREAM_TICK_MS = 500;\n/** How often a settled run is re-checked before the stream closes itself. */\nconst SETTLED_POLL_TICKS = 4;\n/** Bytes of one textual artifact returned as JSON; binary previews use a stream. */\nconst MAX_ARTIFACT_BYTES = 512 * 1024;\n/** Path segments a client supplies are names, never paths. */\nconst SAFE_SEGMENT = /^[\\w.@-]+$/;\nconst ARTIFACT_MIME_TYPES: Readonly<Record<string, string>> = {\n '.aac': 'audio/aac',\n '.bmp': 'image/bmp',\n '.css': 'text/css',\n '.csv': 'text/csv',\n '.gif': 'image/gif',\n '.htm': 'text/html',\n '.html': 'text/html',\n '.ico': 'image/x-icon',\n '.jpeg': 'image/jpeg',\n '.jpg': 'image/jpeg',\n '.js': 'text/javascript',\n '.json': 'application/json',\n '.jsx': 'text/jsx',\n '.m4a': 'audio/mp4',\n '.markdown': 'text/markdown',\n '.md': 'text/markdown',\n '.mov': 'video/quicktime',\n '.mp3': 'audio/mpeg',\n '.mp4': 'video/mp4',\n '.oga': 'audio/ogg',\n '.ogg': 'audio/ogg',\n '.ogv': 'video/ogg',\n '.pdf': 'application/pdf',\n '.png': 'image/png',\n '.sh': 'text/x-shellscript',\n '.svg': 'image/svg+xml',\n '.toml': 'text/toml',\n '.ts': 'text/typescript',\n '.tsv': 'text/tab-separated-values',\n '.tsx': 'text/tsx',\n '.txt': 'text/plain',\n '.wav': 'audio/wav',\n '.webm': 'video/webm',\n '.webp': 'image/webp',\n '.xml': 'application/xml',\n '.yaml': 'text/yaml',\n '.yml': 'text/yaml',\n};\n\n/**\n * One request's JSON body as a plain record.\n *\n * A body that is absent, malformed, or not an object reads as empty rather\n * than failing the route: every field is checked by the route anyway, and a\n * 400 naming the missing field beats a parse error naming nothing.\n */\nasync function jsonBody(context: Context): Promise<Record<string, unknown>> {\n const parsed: unknown = await context.req.json().catch(() => undefined);\n return parsed !== null && typeof parsed === 'object' && !Array.isArray(parsed)\n ? (parsed as Record<string, unknown>)\n : {};\n}\n\n/** Rejects anything that could climb out of the registry before a path is built. */\nfunction isSafeSegment(value: string): boolean {\n return value !== '' && value !== '../adapters' && value !== '..' && SAFE_SEGMENT.test(value);\n}\n\n/** One run's identity for caches and leases, stable across stage moves. */\nfunction runIdentityOf(workspace: string, runKey: string): string {\n return `${workspace}/${runKey}`;\n}\n\nexport interface WorkflowHubApiOptions {\n registry?: WorkflowRegistryService;\n terminal?: WorkflowTerminalFacade;\n now?: () => number;\n}\n\n/**\n * This package's HTTP surface: one workflow run's terminal, and the files its\n * run directory holds.\n *\n * Mounted in the hub rather than in a session's server, because a run's\n * terminal belongs to its multiplexer rather than to the session that launched\n * it: any process can read a tmux pane, and the session's own server cannot\n * reach anything more. The routes therefore name a run, never a session.\n */\nexport function createWorkflowHubApi(options: WorkflowHubApiOptions = {}): Hono {\n const registry = options.registry ?? new WorkflowRegistryService();\n const facade = options.terminal ?? new WorkflowTerminalFacade();\n const terminal = createWorkflowTerminalService<WorkflowRunRecord>({\n terminal: facade,\n now: options.now ?? (() => Date.now()),\n });\n const app = new Hono();\n\n /** The record behind a request, whichever stage it now sits in. */\n const runOf = async (workspace: string, runKey: string): Promise<WorkflowRunRecord | undefined> => {\n if (!isSafeSegment(workspace) || !isSafeSegment(runKey)) return undefined;\n for (const stage of STAGES) {\n try {\n return await registry.readRunByKey(workspace, stage, runKey);\n } catch {\n // Not in this stage. A run moves between them as it settles, so a miss\n // here is ordinary; only a miss in every stage means no such run.\n }\n }\n return undefined;\n };\n\n const missing = (workspace: string, runKey: string): { error: string } => ({\n error: `No workflow run '${runKey}' in workspace '${workspace}'.`,\n });\n\n app.get('/runs/:workspace/:runKey/screen/stream', async (context) => {\n const workspace = context.req.param('workspace');\n const runKey = context.req.param('runKey');\n const record = await runOf(workspace, runKey);\n if (record === undefined) return context.json(missing(workspace, runKey), 404);\n const identity = runIdentityOf(workspace, runKey);\n\n return streamSSE(context, async (stream) => {\n let settledTicks = 0;\n let running = true;\n stream.onAbort(() => {\n running = false;\n });\n while (running) {\n const current = (await runOf(workspace, runKey)) ?? record;\n const capabilities = terminal.capabilities(current);\n const lines = capabilities.readable ? await terminal.screen(identity, current, SCREEN_LINES) : [];\n // A settled run is read a few more times before the stream closes: the\n // last thing a failing step printed is what the reader came for, and it\n // lands after the record has already moved to its final stage.\n const settled = current.stage !== 'running';\n if (settled) settledTicks += 1;\n const ended = settled && settledTicks >= SETTLED_POLL_TICKS;\n const event: WorkflowScreenEvent = { lines, capabilities, ...(ended ? { ended: true } : {}) };\n await stream.writeSSE({ event: WORKFLOW_SCREEN_EVENT, data: JSON.stringify(event) });\n if (ended) break;\n await stream.sleep(STREAM_TICK_MS);\n }\n terminal.forget(new Set());\n });\n });\n\n app.post('/runs/:workspace/:runKey/control', async (context) => {\n const workspace = context.req.param('workspace');\n const runKey = context.req.param('runKey');\n const record = await runOf(workspace, runKey);\n if (record === undefined) return context.json(missing(workspace, runKey), 404);\n const identity = runIdentityOf(workspace, runKey);\n const body = await jsonBody(context);\n const capabilities = terminal.capabilities(record);\n\n if (body.release === true) {\n if (typeof body.token === 'string') terminal.releaseControl(identity, body.token);\n const released: WorkflowControlResponse = { held: false };\n return context.json(released);\n }\n if (!capabilities.writable) {\n const refused: WorkflowControlResponse = { held: false, reason: capabilities.reason };\n return context.json(refused, 409);\n }\n const token = typeof body.token === 'string' && body.token !== '' ? body.token : randomUUID();\n if (!terminal.takeControl(identity, token)) {\n const taken: WorkflowControlResponse = { held: false, reason: 'Another reader holds the keyboard.' };\n return context.json(taken, 409);\n }\n const held: WorkflowControlResponse = { held: true, token };\n return context.json(held);\n });\n\n app.post('/runs/:workspace/:runKey/keys', async (context) => {\n const workspace = context.req.param('workspace');\n const runKey = context.req.param('runKey');\n const record = await runOf(workspace, runKey);\n if (record === undefined) return context.json(missing(workspace, runKey), 404);\n const body = await jsonBody(context);\n if (typeof body.token !== 'string' || typeof body.data !== 'string') {\n return context.json({ error: 'A keystroke needs a control token and its data.' }, 400);\n }\n try {\n await terminal.write(runIdentityOf(workspace, runKey), record, body.token, body.data);\n } catch (error) {\n return context.json({ error: error instanceof Error ? error.message : String(error) }, 409);\n }\n return context.body(null, 204);\n });\n\n app.post('/runs/:workspace/:runKey/resize', async (context) => {\n const workspace = context.req.param('workspace');\n const runKey = context.req.param('runKey');\n const record = await runOf(workspace, runKey);\n if (record === undefined) return context.json(missing(workspace, runKey), 404);\n const body = await jsonBody(context);\n const { token, columns, rows } = body;\n if (typeof token !== 'string' || typeof columns !== 'number' || typeof rows !== 'number') {\n return context.json({ error: 'A resize needs a control token, columns and rows.' }, 400);\n }\n try {\n const resized = await terminal.resize(runIdentityOf(workspace, runKey), record, token, columns, rows);\n return context.json({ resized });\n } catch (error) {\n return context.json({ error: error instanceof Error ? error.message : String(error) }, 409);\n }\n });\n\n app.delete('/runs/:workspace/:runKey', async (context) => {\n const workspace = context.req.param('workspace');\n const runKey = context.req.param('runKey');\n const record = await runOf(workspace, runKey);\n if (record === undefined) return context.json(missing(workspace, runKey), 404);\n if (record.stage === 'running') {\n return context.json({ error: 'A running workflow must be stopped before it can be deleted.' }, 409);\n }\n try {\n fs.rmSync(registry.runDirectoryFor(record), { recursive: true, force: true });\n } catch (error) {\n return context.json({ error: error instanceof Error ? error.message : String(error) }, 409);\n }\n const response: WorkflowDeleteResponse = { deleted: true };\n return context.json(response);\n });\n\n app.get('/runs/:workspace/:runKey/artifacts', async (context) => {\n const workspace = context.req.param('workspace');\n const runKey = context.req.param('runKey');\n const record = await runOf(workspace, runKey);\n if (record === undefined) return context.json(missing(workspace, runKey), 404);\n const runDir = registry.runDirectoryFor(record);\n const body: WorkflowArtifactsResponse = {\n runDir,\n description: record.runDirectory?.description ?? '',\n artifacts: readArtifacts(runDir, record),\n };\n return context.json(body);\n });\n\n app.get('/runs/:workspace/:runKey/artifacts/:name{.+}', async (context) => {\n const workspace = context.req.param('workspace');\n const runKey = context.req.param('runKey');\n const record = await runOf(workspace, runKey);\n if (record === undefined) return context.json(missing(workspace, runKey), 404);\n const runDir = registry.runDirectoryFor(record);\n const requested = context.req.param('name');\n const resolved = resolveInside(runDir, requested);\n if (resolved === undefined) return context.json({ error: `'${requested}' is not inside this run.` }, 400);\n if (context.req.query('raw') === '1') {\n const response = streamArtifact(\n resolved,\n requested,\n context.req.header('range'),\n context.req.query('download') === '1',\n );\n return response ?? context.json({ error: `'${requested}' has not been written.` }, 404);\n }\n const body = readArtifact(resolved, requested);\n if (body === undefined) return context.json({ error: `'${requested}' has not been written.` }, 404);\n return context.json(body);\n });\n\n return app;\n}\n\n/**\n * A path inside the run directory, or undefined when it points anywhere else.\n *\n * The client supplies this one verbatim, unlike the run key, so it is resolved\n * and then checked rather than trusted: without the check the route would read\n * any file the hub can reach.\n */\nfunction resolveInside(runDir: string, requested: string): string | undefined {\n const resolved = path.resolve(runDir, requested);\n const root = path.resolve(runDir);\n if (resolved !== root && !resolved.startsWith(`${root}${path.sep}`)) return undefined;\n return resolved;\n}\n\n/** Size and modification time of one path, or undefined when it is not there. */\nfunction statOf(target: string): fs.Stats | undefined {\n try {\n return fs.statSync(target);\n } catch {\n return undefined; // Not written yet, which is an ordinary state for an artifact.\n }\n}\n\nfunction stateOf(stats: fs.Stats | undefined): WorkflowArtifactView['state'] {\n if (stats === undefined) return 'pending';\n if (stats.isDirectory()) return 'written';\n return stats.size === 0 ? 'empty' : 'written';\n}\n\nfunction viewOf(\n runDir: string,\n entry: Omit<WorkflowArtifactView, 'state' | 'size' | 'modifiedAt'>,\n): WorkflowArtifactView {\n const stats = statOf(path.join(runDir, entry.path));\n return {\n ...entry,\n state: stateOf(stats),\n ...(stats === undefined ? {} : { size: stats.size, modifiedAt: stats.mtime.toISOString() }),\n };\n}\n\n/**\n * What the run directory holds: the workflow's own declaration first, then the\n * files nobody declared.\n *\n * The declaration is the point of the folder, so it leads and keeps its order\n * even for entries no job has written yet. The rest follows rather than being\n * hidden: the engine's own context.md and progress log are often exactly what a\n * reader is looking for.\n */\nfunction readArtifacts(runDir: string, record: WorkflowRunRecord): WorkflowArtifactView[] {\n const declared = (record.runDirectory?.entries ?? []).map((entry) =>\n viewOf(runDir, {\n path: entry.path,\n kind: entry.kind,\n description: entry.description,\n producedBy: entry['produced-by'] ?? [],\n declared: true,\n }),\n );\n const claimed = new Set(declared.map((entry) => entry.path));\n let names: fs.Dirent[];\n try {\n names = fs.readdirSync(runDir, { withFileTypes: true });\n } catch {\n return declared; // The directory is gone; the declaration is still worth showing.\n }\n const found = names\n .filter((entry) => !claimed.has(entry.name))\n .map((entry) =>\n viewOf(runDir, {\n path: entry.name,\n kind: entry.isDirectory() ? 'directory' : 'file',\n description: '',\n producedBy: [],\n declared: false,\n }),\n )\n .sort((left, right) => left.path.localeCompare(right.path));\n return [...declared, ...found];\n}\n\n/** Browser content type inferred from a filename without trusting client input. */\nfunction artifactMimeType(requested: string): string {\n return ARTIFACT_MIME_TYPES[path.extname(requested).toLowerCase()] ?? 'application/octet-stream';\n}\n\nfunction isTextArtifact(mimeType: string): boolean {\n return mimeType.startsWith('text/') || mimeType === 'application/json' || mimeType === 'application/xml';\n}\n\n/** One artifact's metadata and bounded text, or undefined when it is not readable. */\nfunction readArtifact(resolved: string, requested: string): WorkflowArtifactContentResponse | undefined {\n const stats = statOf(resolved);\n if (stats === undefined || stats.isDirectory()) return undefined;\n const mimeType = artifactMimeType(requested);\n let text: string | undefined;\n if (isTextArtifact(mimeType)) {\n try {\n const handle = fs.openSync(resolved, 'r');\n try {\n const buffer = Buffer.alloc(Math.min(stats.size, MAX_ARTIFACT_BYTES));\n fs.readSync(handle, buffer, 0, buffer.length, 0);\n text = buffer.toString('utf8');\n } finally {\n fs.closeSync(handle);\n }\n } catch {\n return undefined;\n }\n }\n return {\n path: requested,\n size: stats.size,\n modifiedAt: stats.mtime.toISOString(),\n mimeType,\n ...(text === undefined ? {} : { text }),\n truncated: text !== undefined && stats.size > MAX_ARTIFACT_BYTES,\n };\n}\n\n/** Streams media with byte-range support so browser audio, video, and PDF controls can seek. */\nfunction streamArtifact(\n resolved: string,\n requested: string,\n rangeHeader: string | undefined,\n download: boolean,\n): Response | undefined {\n const stats = statOf(resolved);\n if (stats === undefined || stats.isDirectory()) return undefined;\n const mimeType = artifactMimeType(requested);\n let start = 0;\n let end = Math.max(0, stats.size - 1);\n let partial = false;\n const match = rangeHeader?.match(/^bytes=(\\d*)-(\\d*)$/);\n if (match !== null && match !== undefined && stats.size > 0) {\n const requestedStart = match[1] === '' ? undefined : Number(match[1]);\n const requestedEnd = match[2] === '' ? undefined : Number(match[2]);\n if (requestedStart === undefined && requestedEnd !== undefined) {\n start = Math.max(0, stats.size - requestedEnd);\n } else if (requestedStart !== undefined) {\n start = requestedStart;\n end = requestedEnd === undefined ? end : Math.min(requestedEnd, end);\n }\n if (start >= stats.size || start > end) {\n return new Response(null, { status: 416, headers: { 'content-range': `bytes */${String(stats.size)}` } });\n }\n partial = true;\n }\n const filename = path.basename(requested).replace(/[\"\\\\\\r\\n]/g, '_');\n const forceDownload = download || mimeType === 'text/html';\n const headers = new Headers({\n 'accept-ranges': 'bytes',\n 'content-disposition': `${forceDownload ? 'attachment' : 'inline'}; filename=\"${filename}\"`,\n 'content-length': String(stats.size === 0 ? 0 : end - start + 1),\n 'content-type': mimeType,\n 'x-content-type-options': 'nosniff',\n });\n if (partial) headers.set('content-range', `bytes ${String(start)}-${String(end)}/${String(stats.size)}`);\n if (stats.size === 0) return new Response(null, { status: 200, headers });\n const body = Readable.toWeb(fs.createReadStream(resolved, { start, end }));\n return new Response(body, { status: partial ? 206 : 200, headers });\n}\n\n/** The named export a host imports from this package's built hub entry. */\nexport const api: DoomApi = {\n basePath: WORKFLOW_API_BASE_PATH,\n start(_context: DoomApiContext): DoomApiHandler {\n // Hub-scoped: the host hands no session, and these routes want none. A run\n // is addressed by its registry identity, which is machine-wide.\n const app = createWorkflowHubApi();\n return {\n fetch: (request) => app.fetch(request),\n // Nothing outlives a request: a stream's loop ends when its own socket\n // aborts, and the caches it touched are per run rather than per client.\n close: () => undefined,\n };\n },\n};\n"],"mappings":"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"}
@@ -1,2 +0,0 @@
1
- import{createWorkflowTerminalService as e}from"../services/workflowTerminal/index.mjs";import{WORKFLOW_API_BASE_PATH as t,WORKFLOW_SCREEN_EVENT as n}from"../types/webWorkflowTerminal.mjs";import r from"node:fs";import i from"node:path";import{WorkflowRegistryService as a,WorkflowTerminalService as o}from"@agimon-ai/workflow-mcp";import{randomUUID as s}from"node:crypto";import{Readable as c}from"node:stream";import{Hono as l}from"hono";import{streamSSE as u}from"hono/streaming";const d=[`running`,`error`,`completed`],f=524288,p=/^[\w.@-]+$/,m={".aac":`audio/aac`,".bmp":`image/bmp`,".css":`text/css`,".csv":`text/csv`,".gif":`image/gif`,".htm":`text/html`,".html":`text/html`,".ico":`image/x-icon`,".jpeg":`image/jpeg`,".jpg":`image/jpeg`,".js":`text/javascript`,".json":`application/json`,".jsx":`text/jsx`,".m4a":`audio/mp4`,".markdown":`text/markdown`,".md":`text/markdown`,".mov":`video/quicktime`,".mp3":`audio/mpeg`,".mp4":`video/mp4`,".oga":`audio/ogg`,".ogg":`audio/ogg`,".ogv":`video/ogg`,".pdf":`application/pdf`,".png":`image/png`,".sh":`text/x-shellscript`,".svg":`image/svg+xml`,".toml":`text/toml`,".ts":`text/typescript`,".tsv":`text/tab-separated-values`,".tsx":`text/tsx`,".txt":`text/plain`,".wav":`audio/wav`,".webm":`video/webm`,".webp":`image/webp`,".xml":`application/xml`,".yaml":`text/yaml`,".yml":`text/yaml`};async function h(e){let t=await e.req.json().catch(()=>void 0);return typeof t==`object`&&t&&!Array.isArray(t)?t:{}}function g(e){return e!==``&&e!==`../adapters`&&e!==`..`&&p.test(e)}function _(e,t){return`${e}/${t}`}function v(t={}){let i=t.registry??new a,c=t.terminal??new o,f=e({terminal:c,now:t.now??(()=>Date.now())}),p=new l,m=async(e,t)=>{if(g(e)&&g(t))for(let n of d)try{return await i.readRunByKey(e,n,t)}catch{}},v=(e,t)=>({error:`No workflow run '${t}' in workspace '${e}'.`});return p.get(`/runs/:workspace/:runKey/screen/stream`,async e=>{let t=e.req.param(`workspace`),r=e.req.param(`runKey`),i=await m(t,r);if(i===void 0)return e.json(v(t,r),404);let a=_(t,r);return u(e,async e=>{let o=0,s=!0;for(e.onAbort(()=>{s=!1});s;){let s=await m(t,r)??i,c=f.capabilities(s),l=c.readable?await f.screen(a,s,48):[],u=s.stage!==`running`;u&&(o+=1);let d=u&&o>=4,p={lines:l,capabilities:c,...d?{ended:!0}:{}};if(await e.writeSSE({event:n,data:JSON.stringify(p)}),d)break;await e.sleep(500)}f.forget(new Set)})}),p.post(`/runs/:workspace/:runKey/control`,async e=>{let t=e.req.param(`workspace`),n=e.req.param(`runKey`),r=await m(t,n);if(r===void 0)return e.json(v(t,n),404);let i=_(t,n),a=await h(e),o=f.capabilities(r);if(a.release===!0)return typeof a.token==`string`&&f.releaseControl(i,a.token),e.json({held:!1});if(!o.writable){let t={held:!1,reason:o.reason};return e.json(t,409)}let c=typeof a.token==`string`&&a.token!==``?a.token:s();if(!f.takeControl(i,c))return e.json({held:!1,reason:`Another reader holds the keyboard.`},409);let l={held:!0,token:c};return e.json(l)}),p.post(`/runs/:workspace/:runKey/keys`,async e=>{let t=e.req.param(`workspace`),n=e.req.param(`runKey`),r=await m(t,n);if(r===void 0)return e.json(v(t,n),404);let i=await h(e);if(typeof i.token!=`string`||typeof i.data!=`string`)return e.json({error:`A keystroke needs a control token and its data.`},400);try{await f.write(_(t,n),r,i.token,i.data)}catch(t){return e.json({error:t instanceof Error?t.message:String(t)},409)}return e.body(null,204)}),p.post(`/runs/:workspace/:runKey/resize`,async e=>{let t=e.req.param(`workspace`),n=e.req.param(`runKey`),r=await m(t,n);if(r===void 0)return e.json(v(t,n),404);let{token:i,columns:a,rows:o}=await h(e);if(typeof i!=`string`||typeof a!=`number`||typeof o!=`number`)return e.json({error:`A resize needs a control token, columns and rows.`},400);try{let s=await f.resize(_(t,n),r,i,a,o);return e.json({resized:s})}catch(t){return e.json({error:t instanceof Error?t.message:String(t)},409)}}),p.delete(`/runs/:workspace/:runKey`,async e=>{let t=e.req.param(`workspace`),n=e.req.param(`runKey`),a=await m(t,n);if(a===void 0)return e.json(v(t,n),404);if(a.stage===`running`)return e.json({error:`A running workflow must be stopped before it can be deleted.`},409);try{r.rmSync(i.runDirectoryFor(a),{recursive:!0,force:!0})}catch(t){return e.json({error:t instanceof Error?t.message:String(t)},409)}return e.json({deleted:!0})}),p.get(`/runs/:workspace/:runKey/artifacts`,async e=>{let t=e.req.param(`workspace`),n=e.req.param(`runKey`),r=await m(t,n);if(r===void 0)return e.json(v(t,n),404);let a=i.runDirectoryFor(r),o={runDir:a,description:r.runDirectory?.description??``,artifacts:C(a,r)};return e.json(o)}),p.get(`/runs/:workspace/:runKey/artifacts/:name{.+}`,async e=>{let t=e.req.param(`workspace`),n=e.req.param(`runKey`),r=await m(t,n);if(r===void 0)return e.json(v(t,n),404);let a=i.runDirectoryFor(r),o=e.req.param(`name`),s=y(a,o);if(s===void 0)return e.json({error:`'${o}' is not inside this run.`},400);if(e.req.query(`raw`)===`1`)return D(s,o,e.req.header(`range`),e.req.query(`download`)===`1`)??e.json({error:`'${o}' has not been written.`},404);let c=E(s,o);return c===void 0?e.json({error:`'${o}' has not been written.`},404):e.json(c)}),p}function y(e,t){let n=i.resolve(e,t),r=i.resolve(e);if(n===r||n.startsWith(`${r}${i.sep}`))return n}function b(e){try{return r.statSync(e)}catch{return}}function x(e){return e===void 0?`pending`:e.isDirectory()?`written`:e.size===0?`empty`:`written`}function S(e,t){let n=b(i.join(e,t.path));return{...t,state:x(n),...n===void 0?{}:{size:n.size,modifiedAt:n.mtime.toISOString()}}}function C(e,t){let n=(t.runDirectory?.entries??[]).map(t=>S(e,{path:t.path,kind:t.kind,description:t.description,producedBy:t[`produced-by`]??[],declared:!0})),i=new Set(n.map(e=>e.path)),a;try{a=r.readdirSync(e,{withFileTypes:!0})}catch{return n}let o=a.filter(e=>!i.has(e.name)).map(t=>S(e,{path:t.name,kind:t.isDirectory()?`directory`:`file`,description:``,producedBy:[],declared:!1})).sort((e,t)=>e.path.localeCompare(t.path));return[...n,...o]}function w(e){return m[i.extname(e).toLowerCase()]??`application/octet-stream`}function T(e){return e.startsWith(`text/`)||e===`application/json`||e===`application/xml`}function E(e,t){let n=b(e);if(n===void 0||n.isDirectory())return;let i=w(t),a;if(T(i))try{let t=r.openSync(e,`r`);try{let e=Buffer.alloc(Math.min(n.size,f));r.readSync(t,e,0,e.length,0),a=e.toString(`utf8`)}finally{r.closeSync(t)}}catch{return}return{path:t,size:n.size,modifiedAt:n.mtime.toISOString(),mimeType:i,...a===void 0?{}:{text:a},truncated:a!==void 0&&n.size>f}}function D(e,t,n,a){let o=b(e);if(o===void 0||o.isDirectory())return;let s=w(t),l=0,u=Math.max(0,o.size-1),d=!1,f=n?.match(/^bytes=(\d*)-(\d*)$/);if(f!=null&&o.size>0){let e=f[1]===``?void 0:Number(f[1]),t=f[2]===``?void 0:Number(f[2]);if(e===void 0&&t!==void 0?l=Math.max(0,o.size-t):e!==void 0&&(l=e,u=t===void 0?u:Math.min(t,u)),l>=o.size||l>u)return new Response(null,{status:416,headers:{"content-range":`bytes */${String(o.size)}`}});d=!0}let p=i.basename(t).replace(/["\\\r\n]/g,`_`),m=new Headers({"accept-ranges":`bytes`,"content-disposition":`${a||s===`text/html`?`attachment`:`inline`}; filename="${p}"`,"content-length":String(o.size===0?0:u-l+1),"content-type":s,"x-content-type-options":`nosniff`});if(d&&m.set(`content-range`,`bytes ${String(l)}-${String(u)}/${String(o.size)}`),o.size===0)return new Response(null,{status:200,headers:m});let h=c.toWeb(r.createReadStream(e,{start:l,end:u}));return new Response(h,{status:d?206:200,headers:m})}const O={basePath:t,start(e){let t=v();return{fetch:e=>t.fetch(e),close:()=>void 0}}};export{O as api,v as createWorkflowHubApi};
2
- //# sourceMappingURL=workflowHubApi.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"workflowHubApi.mjs","names":["WorkflowTerminalFacade"],"sources":["../../../src/controllers/workflowHubApi.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { Readable } from 'node:stream';\n\nimport type { DoomApi, DoomApiContext, DoomApiHandler } from '@agimon-ai/doompi-core/package-api';\nimport {\n WorkflowRegistryService,\n WorkflowTerminalService as WorkflowTerminalFacade,\n type WorkflowRunRecord,\n type WorkflowStage,\n} from '@agimon-ai/workflow-mcp';\nimport { type Context, Hono } from 'hono';\nimport { streamSSE } from 'hono/streaming';\n\nimport { createWorkflowTerminalService } from '../services/workflowTerminal';\nimport {\n WORKFLOW_API_BASE_PATH,\n WORKFLOW_SCREEN_EVENT,\n type WorkflowArtifactContentResponse,\n type WorkflowArtifactsResponse,\n type WorkflowArtifactView,\n type WorkflowControlResponse,\n type WorkflowDeleteResponse,\n type WorkflowScreenEvent,\n} from '../types/webWorkflowTerminal';\n\n/** Stages a run can be recorded under, newest first: a live run is the common case. */\nconst STAGES: readonly WorkflowStage[] = ['running', 'error', 'completed'];\n/** Lines of screen the stream sends, which is a terminal's visible height plus room. */\nconst SCREEN_LINES = 48;\n/** How often the stream re-reads; the service coalesces, so this is an upper bound. */\nconst STREAM_TICK_MS = 500;\n/** How often a settled run is re-checked before the stream closes itself. */\nconst SETTLED_POLL_TICKS = 4;\n/** Bytes of one textual artifact returned as JSON; binary previews use a stream. */\nconst MAX_ARTIFACT_BYTES = 512 * 1024;\n/** Path segments a client supplies are names, never paths. */\nconst SAFE_SEGMENT = /^[\\w.@-]+$/;\nconst ARTIFACT_MIME_TYPES: Readonly<Record<string, string>> = {\n '.aac': 'audio/aac',\n '.bmp': 'image/bmp',\n '.css': 'text/css',\n '.csv': 'text/csv',\n '.gif': 'image/gif',\n '.htm': 'text/html',\n '.html': 'text/html',\n '.ico': 'image/x-icon',\n '.jpeg': 'image/jpeg',\n '.jpg': 'image/jpeg',\n '.js': 'text/javascript',\n '.json': 'application/json',\n '.jsx': 'text/jsx',\n '.m4a': 'audio/mp4',\n '.markdown': 'text/markdown',\n '.md': 'text/markdown',\n '.mov': 'video/quicktime',\n '.mp3': 'audio/mpeg',\n '.mp4': 'video/mp4',\n '.oga': 'audio/ogg',\n '.ogg': 'audio/ogg',\n '.ogv': 'video/ogg',\n '.pdf': 'application/pdf',\n '.png': 'image/png',\n '.sh': 'text/x-shellscript',\n '.svg': 'image/svg+xml',\n '.toml': 'text/toml',\n '.ts': 'text/typescript',\n '.tsv': 'text/tab-separated-values',\n '.tsx': 'text/tsx',\n '.txt': 'text/plain',\n '.wav': 'audio/wav',\n '.webm': 'video/webm',\n '.webp': 'image/webp',\n '.xml': 'application/xml',\n '.yaml': 'text/yaml',\n '.yml': 'text/yaml',\n};\n\n/**\n * One request's JSON body as a plain record.\n *\n * A body that is absent, malformed, or not an object reads as empty rather\n * than failing the route: every field is checked by the route anyway, and a\n * 400 naming the missing field beats a parse error naming nothing.\n */\nasync function jsonBody(context: Context): Promise<Record<string, unknown>> {\n const parsed: unknown = await context.req.json().catch(() => undefined);\n return parsed !== null && typeof parsed === 'object' && !Array.isArray(parsed)\n ? (parsed as Record<string, unknown>)\n : {};\n}\n\n/** Rejects anything that could climb out of the registry before a path is built. */\nfunction isSafeSegment(value: string): boolean {\n return value !== '' && value !== '../adapters' && value !== '..' && SAFE_SEGMENT.test(value);\n}\n\n/** One run's identity for caches and leases, stable across stage moves. */\nfunction runIdentityOf(workspace: string, runKey: string): string {\n return `${workspace}/${runKey}`;\n}\n\nexport interface WorkflowHubApiOptions {\n registry?: WorkflowRegistryService;\n terminal?: WorkflowTerminalFacade;\n now?: () => number;\n}\n\n/**\n * This package's HTTP surface: one workflow run's terminal, and the files its\n * run directory holds.\n *\n * Mounted in the hub rather than in a session's server, because a run's\n * terminal belongs to its multiplexer rather than to the session that launched\n * it: any process can read a tmux pane, and the session's own server cannot\n * reach anything more. The routes therefore name a run, never a session.\n */\nexport function createWorkflowHubApi(options: WorkflowHubApiOptions = {}): Hono {\n const registry = options.registry ?? new WorkflowRegistryService();\n const facade = options.terminal ?? new WorkflowTerminalFacade();\n const terminal = createWorkflowTerminalService<WorkflowRunRecord>({\n terminal: facade,\n now: options.now ?? (() => Date.now()),\n });\n const app = new Hono();\n\n /** The record behind a request, whichever stage it now sits in. */\n const runOf = async (workspace: string, runKey: string): Promise<WorkflowRunRecord | undefined> => {\n if (!isSafeSegment(workspace) || !isSafeSegment(runKey)) return undefined;\n for (const stage of STAGES) {\n try {\n return await registry.readRunByKey(workspace, stage, runKey);\n } catch {\n // Not in this stage. A run moves between them as it settles, so a miss\n // here is ordinary; only a miss in every stage means no such run.\n }\n }\n return undefined;\n };\n\n const missing = (workspace: string, runKey: string): { error: string } => ({\n error: `No workflow run '${runKey}' in workspace '${workspace}'.`,\n });\n\n app.get('/runs/:workspace/:runKey/screen/stream', async (context) => {\n const workspace = context.req.param('workspace');\n const runKey = context.req.param('runKey');\n const record = await runOf(workspace, runKey);\n if (record === undefined) return context.json(missing(workspace, runKey), 404);\n const identity = runIdentityOf(workspace, runKey);\n\n return streamSSE(context, async (stream) => {\n let settledTicks = 0;\n let running = true;\n stream.onAbort(() => {\n running = false;\n });\n while (running) {\n const current = (await runOf(workspace, runKey)) ?? record;\n const capabilities = terminal.capabilities(current);\n const lines = capabilities.readable ? await terminal.screen(identity, current, SCREEN_LINES) : [];\n // A settled run is read a few more times before the stream closes: the\n // last thing a failing step printed is what the reader came for, and it\n // lands after the record has already moved to its final stage.\n const settled = current.stage !== 'running';\n if (settled) settledTicks += 1;\n const ended = settled && settledTicks >= SETTLED_POLL_TICKS;\n const event: WorkflowScreenEvent = { lines, capabilities, ...(ended ? { ended: true } : {}) };\n await stream.writeSSE({ event: WORKFLOW_SCREEN_EVENT, data: JSON.stringify(event) });\n if (ended) break;\n await stream.sleep(STREAM_TICK_MS);\n }\n terminal.forget(new Set());\n });\n });\n\n app.post('/runs/:workspace/:runKey/control', async (context) => {\n const workspace = context.req.param('workspace');\n const runKey = context.req.param('runKey');\n const record = await runOf(workspace, runKey);\n if (record === undefined) return context.json(missing(workspace, runKey), 404);\n const identity = runIdentityOf(workspace, runKey);\n const body = await jsonBody(context);\n const capabilities = terminal.capabilities(record);\n\n if (body.release === true) {\n if (typeof body.token === 'string') terminal.releaseControl(identity, body.token);\n const released: WorkflowControlResponse = { held: false };\n return context.json(released);\n }\n if (!capabilities.writable) {\n const refused: WorkflowControlResponse = { held: false, reason: capabilities.reason };\n return context.json(refused, 409);\n }\n const token = typeof body.token === 'string' && body.token !== '' ? body.token : randomUUID();\n if (!terminal.takeControl(identity, token)) {\n const taken: WorkflowControlResponse = { held: false, reason: 'Another reader holds the keyboard.' };\n return context.json(taken, 409);\n }\n const held: WorkflowControlResponse = { held: true, token };\n return context.json(held);\n });\n\n app.post('/runs/:workspace/:runKey/keys', async (context) => {\n const workspace = context.req.param('workspace');\n const runKey = context.req.param('runKey');\n const record = await runOf(workspace, runKey);\n if (record === undefined) return context.json(missing(workspace, runKey), 404);\n const body = await jsonBody(context);\n if (typeof body.token !== 'string' || typeof body.data !== 'string') {\n return context.json({ error: 'A keystroke needs a control token and its data.' }, 400);\n }\n try {\n await terminal.write(runIdentityOf(workspace, runKey), record, body.token, body.data);\n } catch (error) {\n return context.json({ error: error instanceof Error ? error.message : String(error) }, 409);\n }\n return context.body(null, 204);\n });\n\n app.post('/runs/:workspace/:runKey/resize', async (context) => {\n const workspace = context.req.param('workspace');\n const runKey = context.req.param('runKey');\n const record = await runOf(workspace, runKey);\n if (record === undefined) return context.json(missing(workspace, runKey), 404);\n const body = await jsonBody(context);\n const { token, columns, rows } = body;\n if (typeof token !== 'string' || typeof columns !== 'number' || typeof rows !== 'number') {\n return context.json({ error: 'A resize needs a control token, columns and rows.' }, 400);\n }\n try {\n const resized = await terminal.resize(runIdentityOf(workspace, runKey), record, token, columns, rows);\n return context.json({ resized });\n } catch (error) {\n return context.json({ error: error instanceof Error ? error.message : String(error) }, 409);\n }\n });\n\n app.delete('/runs/:workspace/:runKey', async (context) => {\n const workspace = context.req.param('workspace');\n const runKey = context.req.param('runKey');\n const record = await runOf(workspace, runKey);\n if (record === undefined) return context.json(missing(workspace, runKey), 404);\n if (record.stage === 'running') {\n return context.json({ error: 'A running workflow must be stopped before it can be deleted.' }, 409);\n }\n try {\n fs.rmSync(registry.runDirectoryFor(record), { recursive: true, force: true });\n } catch (error) {\n return context.json({ error: error instanceof Error ? error.message : String(error) }, 409);\n }\n const response: WorkflowDeleteResponse = { deleted: true };\n return context.json(response);\n });\n\n app.get('/runs/:workspace/:runKey/artifacts', async (context) => {\n const workspace = context.req.param('workspace');\n const runKey = context.req.param('runKey');\n const record = await runOf(workspace, runKey);\n if (record === undefined) return context.json(missing(workspace, runKey), 404);\n const runDir = registry.runDirectoryFor(record);\n const body: WorkflowArtifactsResponse = {\n runDir,\n description: record.runDirectory?.description ?? '',\n artifacts: readArtifacts(runDir, record),\n };\n return context.json(body);\n });\n\n app.get('/runs/:workspace/:runKey/artifacts/:name{.+}', async (context) => {\n const workspace = context.req.param('workspace');\n const runKey = context.req.param('runKey');\n const record = await runOf(workspace, runKey);\n if (record === undefined) return context.json(missing(workspace, runKey), 404);\n const runDir = registry.runDirectoryFor(record);\n const requested = context.req.param('name');\n const resolved = resolveInside(runDir, requested);\n if (resolved === undefined) return context.json({ error: `'${requested}' is not inside this run.` }, 400);\n if (context.req.query('raw') === '1') {\n const response = streamArtifact(\n resolved,\n requested,\n context.req.header('range'),\n context.req.query('download') === '1',\n );\n return response ?? context.json({ error: `'${requested}' has not been written.` }, 404);\n }\n const body = readArtifact(resolved, requested);\n if (body === undefined) return context.json({ error: `'${requested}' has not been written.` }, 404);\n return context.json(body);\n });\n\n return app;\n}\n\n/**\n * A path inside the run directory, or undefined when it points anywhere else.\n *\n * The client supplies this one verbatim, unlike the run key, so it is resolved\n * and then checked rather than trusted: without the check the route would read\n * any file the hub can reach.\n */\nfunction resolveInside(runDir: string, requested: string): string | undefined {\n const resolved = path.resolve(runDir, requested);\n const root = path.resolve(runDir);\n if (resolved !== root && !resolved.startsWith(`${root}${path.sep}`)) return undefined;\n return resolved;\n}\n\n/** Size and modification time of one path, or undefined when it is not there. */\nfunction statOf(target: string): fs.Stats | undefined {\n try {\n return fs.statSync(target);\n } catch {\n return undefined; // Not written yet, which is an ordinary state for an artifact.\n }\n}\n\nfunction stateOf(stats: fs.Stats | undefined): WorkflowArtifactView['state'] {\n if (stats === undefined) return 'pending';\n if (stats.isDirectory()) return 'written';\n return stats.size === 0 ? 'empty' : 'written';\n}\n\nfunction viewOf(\n runDir: string,\n entry: Omit<WorkflowArtifactView, 'state' | 'size' | 'modifiedAt'>,\n): WorkflowArtifactView {\n const stats = statOf(path.join(runDir, entry.path));\n return {\n ...entry,\n state: stateOf(stats),\n ...(stats === undefined ? {} : { size: stats.size, modifiedAt: stats.mtime.toISOString() }),\n };\n}\n\n/**\n * What the run directory holds: the workflow's own declaration first, then the\n * files nobody declared.\n *\n * The declaration is the point of the folder, so it leads and keeps its order\n * even for entries no job has written yet. The rest follows rather than being\n * hidden: the engine's own context.md and progress log are often exactly what a\n * reader is looking for.\n */\nfunction readArtifacts(runDir: string, record: WorkflowRunRecord): WorkflowArtifactView[] {\n const declared = (record.runDirectory?.entries ?? []).map((entry) =>\n viewOf(runDir, {\n path: entry.path,\n kind: entry.kind,\n description: entry.description,\n producedBy: entry['produced-by'] ?? [],\n declared: true,\n }),\n );\n const claimed = new Set(declared.map((entry) => entry.path));\n let names: fs.Dirent[];\n try {\n names = fs.readdirSync(runDir, { withFileTypes: true });\n } catch {\n return declared; // The directory is gone; the declaration is still worth showing.\n }\n const found = names\n .filter((entry) => !claimed.has(entry.name))\n .map((entry) =>\n viewOf(runDir, {\n path: entry.name,\n kind: entry.isDirectory() ? 'directory' : 'file',\n description: '',\n producedBy: [],\n declared: false,\n }),\n )\n .sort((left, right) => left.path.localeCompare(right.path));\n return [...declared, ...found];\n}\n\n/** Browser content type inferred from a filename without trusting client input. */\nfunction artifactMimeType(requested: string): string {\n return ARTIFACT_MIME_TYPES[path.extname(requested).toLowerCase()] ?? 'application/octet-stream';\n}\n\nfunction isTextArtifact(mimeType: string): boolean {\n return mimeType.startsWith('text/') || mimeType === 'application/json' || mimeType === 'application/xml';\n}\n\n/** One artifact's metadata and bounded text, or undefined when it is not readable. */\nfunction readArtifact(resolved: string, requested: string): WorkflowArtifactContentResponse | undefined {\n const stats = statOf(resolved);\n if (stats === undefined || stats.isDirectory()) return undefined;\n const mimeType = artifactMimeType(requested);\n let text: string | undefined;\n if (isTextArtifact(mimeType)) {\n try {\n const handle = fs.openSync(resolved, 'r');\n try {\n const buffer = Buffer.alloc(Math.min(stats.size, MAX_ARTIFACT_BYTES));\n fs.readSync(handle, buffer, 0, buffer.length, 0);\n text = buffer.toString('utf8');\n } finally {\n fs.closeSync(handle);\n }\n } catch {\n return undefined;\n }\n }\n return {\n path: requested,\n size: stats.size,\n modifiedAt: stats.mtime.toISOString(),\n mimeType,\n ...(text === undefined ? {} : { text }),\n truncated: text !== undefined && stats.size > MAX_ARTIFACT_BYTES,\n };\n}\n\n/** Streams media with byte-range support so browser audio, video, and PDF controls can seek. */\nfunction streamArtifact(\n resolved: string,\n requested: string,\n rangeHeader: string | undefined,\n download: boolean,\n): Response | undefined {\n const stats = statOf(resolved);\n if (stats === undefined || stats.isDirectory()) return undefined;\n const mimeType = artifactMimeType(requested);\n let start = 0;\n let end = Math.max(0, stats.size - 1);\n let partial = false;\n const match = rangeHeader?.match(/^bytes=(\\d*)-(\\d*)$/);\n if (match !== null && match !== undefined && stats.size > 0) {\n const requestedStart = match[1] === '' ? undefined : Number(match[1]);\n const requestedEnd = match[2] === '' ? undefined : Number(match[2]);\n if (requestedStart === undefined && requestedEnd !== undefined) {\n start = Math.max(0, stats.size - requestedEnd);\n } else if (requestedStart !== undefined) {\n start = requestedStart;\n end = requestedEnd === undefined ? end : Math.min(requestedEnd, end);\n }\n if (start >= stats.size || start > end) {\n return new Response(null, { status: 416, headers: { 'content-range': `bytes */${String(stats.size)}` } });\n }\n partial = true;\n }\n const filename = path.basename(requested).replace(/[\"\\\\\\r\\n]/g, '_');\n const forceDownload = download || mimeType === 'text/html';\n const headers = new Headers({\n 'accept-ranges': 'bytes',\n 'content-disposition': `${forceDownload ? 'attachment' : 'inline'}; filename=\"${filename}\"`,\n 'content-length': String(stats.size === 0 ? 0 : end - start + 1),\n 'content-type': mimeType,\n 'x-content-type-options': 'nosniff',\n });\n if (partial) headers.set('content-range', `bytes ${String(start)}-${String(end)}/${String(stats.size)}`);\n if (stats.size === 0) return new Response(null, { status: 200, headers });\n const body = Readable.toWeb(fs.createReadStream(resolved, { start, end }));\n return new Response(body, { status: partial ? 206 : 200, headers });\n}\n\n/** The named export a host imports from this package's built hub entry. */\nexport const api: DoomApi = {\n basePath: WORKFLOW_API_BASE_PATH,\n start(_context: DoomApiContext): DoomApiHandler {\n // Hub-scoped: the host hands no session, and these routes want none. A run\n // is addressed by its registry identity, which is machine-wide.\n const app = createWorkflowHubApi();\n return {\n fetch: (request) => app.fetch(request),\n // Nothing outlives a request: a stream's loop ends when its own socket\n // aborts, and the caches it touched are per run rather than per client.\n close: () => undefined,\n };\n },\n};\n"],"mappings":"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"}
@@ -1,2 +0,0 @@
1
- const e=require("../services/workflowRuns/index.cjs"),t=require("../services/workflowWatcher/index.cjs"),n=require("../types/webWorkflows.cjs");function r(e){return typeof e==`object`&&!!e&&Array.isArray(e.runs)}function i(i={}){let a=i.read??t.readWorkflowRuns;return{frameType:n.WORKFLOW_RUNS_TYPE,start(t){let i=new Map,o=new Map,s=new Map,c=(t,n)=>e.presentWorkflowRuns(n.filter(n=>e.runBelongsToSession(n,t.sessionId)).map(e=>e.view),Date.now()),l=(e,n)=>{o.set(e.sessionId,n),t.publish(e.sessionId,n)};return{payloadFor(e){return i.has(e.sessionId)?o.get(e.sessionId):void 0},sessionAdded(e){i.set(e.sessionId,e),s.get(e.sessionId)?.();let o=t.directEvents.subscribe(n.WORKFLOW_RUNS_TYPE,e.sessionId,t=>{r(t)&&l(e,t)},{replayLatest:!0});s.set(e.sessionId,o);let u={runs:c(e,a({environment:e.environment}))};l(e,u)},sessionRemoved(e){s.get(e)?.(),s.delete(e),i.delete(e),o.delete(e)},close(){for(let e of s.values())e();s.clear(),i.clear(),o.clear()}}}}}exports.createWorkflowsChannel=i;
2
- //# sourceMappingURL=workflowsHubChannel.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"workflowsHubChannel.cjs","names":["readWorkflowRuns","WORKFLOW_RUNS_TYPE","presentWorkflowRuns","runBelongsToSession"],"sources":["../../../src/controllers/workflowsHubChannel.ts"],"sourcesContent":["import type { DoomHubChannelSource, DoomHubSessionScope, DoomHubChannel } from '@agimon-ai/doompi-core/hub-channel';\n\nimport { presentWorkflowRuns, runBelongsToSession, type ParsedWorkflowRun } from '../services/workflowRuns';\nimport { readWorkflowRuns, type ReadWorkflowRunsOptions } from '../services/workflowWatcher';\nimport { WORKFLOW_RUNS_TYPE, type WorkflowRunView, type WorkflowRunsPayload } from '../types/webWorkflows';\n\nexport interface WorkflowsChannelOptions {\n /** Injectable durable initial snapshot reader for tests. */\n read?: (options: ReadWorkflowRunsOptions) => ParsedWorkflowRun[];\n}\n\nfunction isWorkflowRunsPayload(value: unknown): value is WorkflowRunsPayload {\n return typeof value === 'object' && value !== null && Array.isArray((value as { runs?: unknown }).runs);\n}\n\n/**\n * The workflow runs data channel. Durable registry state seeds each session\n * once; subsequent updates arrive through the host-owned direct event bus.\n */\nexport function createWorkflowsChannel(options: WorkflowsChannelOptions = {}): DoomHubChannel {\n const read = options.read ?? readWorkflowRuns;\n return {\n frameType: WORKFLOW_RUNS_TYPE,\n start(host) {\n const scopes = new Map<string, DoomHubSessionScope>();\n const latest = new Map<string, WorkflowRunsPayload>();\n const subscriptions = new Map<string, () => void>();\n const runsFor = (scope: DoomHubSessionScope, parsed: readonly ParsedWorkflowRun[]): WorkflowRunView[] =>\n presentWorkflowRuns(\n parsed.filter((run) => runBelongsToSession(run, scope.sessionId)).map((run) => run.view),\n Date.now(),\n );\n const publish = (scope: DoomHubSessionScope, payload: WorkflowRunsPayload): void => {\n latest.set(scope.sessionId, payload);\n host.publish(scope.sessionId, payload);\n };\n const source: DoomHubChannelSource = {\n payloadFor(scope) {\n return scopes.has(scope.sessionId) ? latest.get(scope.sessionId) : undefined;\n },\n sessionAdded(scope) {\n scopes.set(scope.sessionId, scope);\n subscriptions.get(scope.sessionId)?.();\n const unsubscribe = host.directEvents.subscribe(\n WORKFLOW_RUNS_TYPE,\n scope.sessionId,\n (value) => {\n if (isWorkflowRunsPayload(value)) publish(scope, value);\n },\n { replayLatest: true },\n );\n subscriptions.set(scope.sessionId, unsubscribe);\n const payload = { runs: runsFor(scope, read({ environment: scope.environment })) };\n publish(scope, payload);\n },\n sessionRemoved(sessionId) {\n subscriptions.get(sessionId)?.();\n subscriptions.delete(sessionId);\n scopes.delete(sessionId);\n latest.delete(sessionId);\n },\n close() {\n for (const unsubscribe of subscriptions.values()) unsubscribe();\n subscriptions.clear();\n scopes.clear();\n latest.clear();\n },\n };\n return source;\n },\n };\n}\n"],"mappings":"gJAWA,SAAS,EAAsB,EAA8C,CAC3E,OAAO,OAAO,GAAU,YAAY,GAAkB,MAAM,QAAS,EAA6B,IAAI,CACxG,CAMA,SAAgB,EAAuB,EAAmC,CAAC,EAAmB,CAC5F,IAAM,EAAO,EAAQ,MAAQA,EAAAA,iBAC7B,MAAO,CACL,UAAWC,EAAAA,mBACX,MAAM,EAAM,CACV,IAAM,EAAS,IAAI,IACb,EAAS,IAAI,IACb,EAAgB,IAAI,IACpB,GAAW,EAA4B,IAC3CC,EAAAA,oBACE,EAAO,OAAQ,GAAQC,EAAAA,oBAAoB,EAAK,EAAM,SAAS,CAAC,CAAC,CAAC,IAAK,GAAQ,EAAI,IAAI,EACvF,KAAK,IAAI,CACX,EACI,GAAW,EAA4B,IAAuC,CAClF,EAAO,IAAI,EAAM,UAAW,CAAO,EACnC,EAAK,QAAQ,EAAM,UAAW,CAAO,CACvC,EAiCA,MAAO,CA/BL,WAAW,EAAO,CAChB,OAAO,EAAO,IAAI,EAAM,SAAS,EAAI,EAAO,IAAI,EAAM,SAAS,EAAI,IAAA,EACrE,EACA,aAAa,EAAO,CAClB,EAAO,IAAI,EAAM,UAAW,CAAK,EACjC,EAAc,IAAI,EAAM,SAAS,CAAC,GAAG,EACrC,IAAM,EAAc,EAAK,aAAa,UACpCF,EAAAA,mBACA,EAAM,UACL,GAAU,CACL,EAAsB,CAAK,GAAG,EAAQ,EAAO,CAAK,CACxD,EACA,CAAE,aAAc,EAAK,CACvB,EACA,EAAc,IAAI,EAAM,UAAW,CAAW,EAC9C,IAAM,EAAU,CAAE,KAAM,EAAQ,EAAO,EAAK,CAAE,YAAa,EAAM,WAAY,CAAC,CAAC,CAAE,EACjF,EAAQ,EAAO,CAAO,CACxB,EACA,eAAe,EAAW,CACxB,EAAc,IAAI,CAAS,CAAC,GAAG,EAC/B,EAAc,OAAO,CAAS,EAC9B,EAAO,OAAO,CAAS,EACvB,EAAO,OAAO,CAAS,CACzB,EACA,OAAQ,CACN,IAAK,IAAM,KAAe,EAAc,OAAO,EAAG,EAAY,EAC9D,EAAc,MAAM,EACpB,EAAO,MAAM,EACb,EAAO,MAAM,CACf,CAEU,CACd,CACF,CACF"}
@@ -1,2 +0,0 @@
1
- import{presentWorkflowRuns as e,runBelongsToSession as t}from"../services/workflowRuns/index.mjs";import{readWorkflowRuns as n}from"../services/workflowWatcher/index.mjs";import{WORKFLOW_RUNS_TYPE as r}from"../types/webWorkflows.mjs";function i(e){return typeof e==`object`&&!!e&&Array.isArray(e.runs)}function a(a={}){let o=a.read??n;return{frameType:r,start(n){let a=new Map,s=new Map,c=new Map,l=(n,r)=>e(r.filter(e=>t(e,n.sessionId)).map(e=>e.view),Date.now()),u=(e,t)=>{s.set(e.sessionId,t),n.publish(e.sessionId,t)};return{payloadFor(e){return a.has(e.sessionId)?s.get(e.sessionId):void 0},sessionAdded(e){a.set(e.sessionId,e),c.get(e.sessionId)?.();let t=n.directEvents.subscribe(r,e.sessionId,t=>{i(t)&&u(e,t)},{replayLatest:!0});c.set(e.sessionId,t);let s={runs:l(e,o({environment:e.environment}))};u(e,s)},sessionRemoved(e){c.get(e)?.(),c.delete(e),a.delete(e),s.delete(e)},close(){for(let e of c.values())e();c.clear(),a.clear(),s.clear()}}}}}export{a as createWorkflowsChannel};
2
- //# sourceMappingURL=workflowsHubChannel.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"workflowsHubChannel.mjs","names":[],"sources":["../../../src/controllers/workflowsHubChannel.ts"],"sourcesContent":["import type { DoomHubChannelSource, DoomHubSessionScope, DoomHubChannel } from '@agimon-ai/doompi-core/hub-channel';\n\nimport { presentWorkflowRuns, runBelongsToSession, type ParsedWorkflowRun } from '../services/workflowRuns';\nimport { readWorkflowRuns, type ReadWorkflowRunsOptions } from '../services/workflowWatcher';\nimport { WORKFLOW_RUNS_TYPE, type WorkflowRunView, type WorkflowRunsPayload } from '../types/webWorkflows';\n\nexport interface WorkflowsChannelOptions {\n /** Injectable durable initial snapshot reader for tests. */\n read?: (options: ReadWorkflowRunsOptions) => ParsedWorkflowRun[];\n}\n\nfunction isWorkflowRunsPayload(value: unknown): value is WorkflowRunsPayload {\n return typeof value === 'object' && value !== null && Array.isArray((value as { runs?: unknown }).runs);\n}\n\n/**\n * The workflow runs data channel. Durable registry state seeds each session\n * once; subsequent updates arrive through the host-owned direct event bus.\n */\nexport function createWorkflowsChannel(options: WorkflowsChannelOptions = {}): DoomHubChannel {\n const read = options.read ?? readWorkflowRuns;\n return {\n frameType: WORKFLOW_RUNS_TYPE,\n start(host) {\n const scopes = new Map<string, DoomHubSessionScope>();\n const latest = new Map<string, WorkflowRunsPayload>();\n const subscriptions = new Map<string, () => void>();\n const runsFor = (scope: DoomHubSessionScope, parsed: readonly ParsedWorkflowRun[]): WorkflowRunView[] =>\n presentWorkflowRuns(\n parsed.filter((run) => runBelongsToSession(run, scope.sessionId)).map((run) => run.view),\n Date.now(),\n );\n const publish = (scope: DoomHubSessionScope, payload: WorkflowRunsPayload): void => {\n latest.set(scope.sessionId, payload);\n host.publish(scope.sessionId, payload);\n };\n const source: DoomHubChannelSource = {\n payloadFor(scope) {\n return scopes.has(scope.sessionId) ? latest.get(scope.sessionId) : undefined;\n },\n sessionAdded(scope) {\n scopes.set(scope.sessionId, scope);\n subscriptions.get(scope.sessionId)?.();\n const unsubscribe = host.directEvents.subscribe(\n WORKFLOW_RUNS_TYPE,\n scope.sessionId,\n (value) => {\n if (isWorkflowRunsPayload(value)) publish(scope, value);\n },\n { replayLatest: true },\n );\n subscriptions.set(scope.sessionId, unsubscribe);\n const payload = { runs: runsFor(scope, read({ environment: scope.environment })) };\n publish(scope, payload);\n },\n sessionRemoved(sessionId) {\n subscriptions.get(sessionId)?.();\n subscriptions.delete(sessionId);\n scopes.delete(sessionId);\n latest.delete(sessionId);\n },\n close() {\n for (const unsubscribe of subscriptions.values()) unsubscribe();\n subscriptions.clear();\n scopes.clear();\n latest.clear();\n },\n };\n return source;\n },\n };\n}\n"],"mappings":"0OAWA,SAAS,EAAsB,EAA8C,CAC3E,OAAO,OAAO,GAAU,YAAY,GAAkB,MAAM,QAAS,EAA6B,IAAI,CACxG,CAMA,SAAgB,EAAuB,EAAmC,CAAC,EAAmB,CAC5F,IAAM,EAAO,EAAQ,MAAQ,EAC7B,MAAO,CACL,UAAW,EACX,MAAM,EAAM,CACV,IAAM,EAAS,IAAI,IACb,EAAS,IAAI,IACb,EAAgB,IAAI,IACpB,GAAW,EAA4B,IAC3C,EACE,EAAO,OAAQ,GAAQ,EAAoB,EAAK,EAAM,SAAS,CAAC,CAAC,CAAC,IAAK,GAAQ,EAAI,IAAI,EACvF,KAAK,IAAI,CACX,EACI,GAAW,EAA4B,IAAuC,CAClF,EAAO,IAAI,EAAM,UAAW,CAAO,EACnC,EAAK,QAAQ,EAAM,UAAW,CAAO,CACvC,EAiCA,MAAO,CA/BL,WAAW,EAAO,CAChB,OAAO,EAAO,IAAI,EAAM,SAAS,EAAI,EAAO,IAAI,EAAM,SAAS,EAAI,IAAA,EACrE,EACA,aAAa,EAAO,CAClB,EAAO,IAAI,EAAM,UAAW,CAAK,EACjC,EAAc,IAAI,EAAM,SAAS,CAAC,GAAG,EACrC,IAAM,EAAc,EAAK,aAAa,UACpC,EACA,EAAM,UACL,GAAU,CACL,EAAsB,CAAK,GAAG,EAAQ,EAAO,CAAK,CACxD,EACA,CAAE,aAAc,EAAK,CACvB,EACA,EAAc,IAAI,EAAM,UAAW,CAAW,EAC9C,IAAM,EAAU,CAAE,KAAM,EAAQ,EAAO,EAAK,CAAE,YAAa,EAAM,WAAY,CAAC,CAAC,CAAE,EACjF,EAAQ,EAAO,CAAO,CACxB,EACA,eAAe,EAAW,CACxB,EAAc,IAAI,CAAS,CAAC,GAAG,EAC/B,EAAc,OAAO,CAAS,EAC9B,EAAO,OAAO,CAAS,EACvB,EAAO,OAAO,CAAS,CACzB,EACA,OAAQ,CACN,IAAK,IAAM,KAAe,EAAc,OAAO,EAAG,EAAY,EAC9D,EAAc,MAAM,EACpB,EAAO,MAAM,EACb,EAAO,MAAM,CACf,CAEU,CACd,CACF,CACF"}
@@ -1,2 +0,0 @@
1
- let e=require("@agimon-ai/doompi-core/api-contracts"),t=require("typebox");const n=t.Type.String(),r=t.Type.Number(),i=t.Type.Boolean(),a=t.Type.Optional,o=e=>t.Type.Union(e.map(e=>t.Type.Literal(e))),s={name:n,status:o([`running`,`completed`,`skipped`,`failed`,`pause_requested`,`paused`,`resumed`]),reason:a(n),startedAt:a(n),endedAt:a(n)},c=t.Type.Object({runKey:n,workspace:n,displayName:n,workflowName:a(n),workflowPath:n,stage:o([`running`,`completed`,`error`]),outcome:a(o([`success`,`skipped`,`failed`,`interrupted`])),executionState:a(o([`running`,`pause_requested`,`paused`,`resume_requested`])),prompt:a(n),startedAt:n,finishedAt:a(n),errorMessage:a(n),failedJob:a(n),stale:a(i),staleReason:a(n),worktreeBranch:a(n),position:a(t.Type.Object({job:n,step:a(n),index:a(r),total:a(r)})),jobs:t.Type.Array(t.Type.Object({...s,phase:o([`pre`,`job`,`post`]),index:a(r),total:a(r),steps:t.Type.Array(t.Type.Object(s))}))}),l=t.Type.Object({runs:t.Type.Array(c)}),u={path:n,kind:o([`file`,`directory`]),description:n,producedBy:t.Type.Array(n)},d=t.Type.Object({cwd:n,warning:a(n),workflows:t.Type.Array(t.Type.Object({path:n,relativePath:n,name:n,description:n,tags:t.Type.Array(n),triggers:t.Type.Array(n),inputs:t.Type.Array(t.Type.Object({name:n,description:a(n),required:a(i),default:a(n),type:a(n),options:a(t.Type.Array(n))})),jobs:t.Type.Array(t.Type.Object({name:n,steps:t.Type.Array(n)})),artifacts:t.Type.Array(t.Type.Object(u)),runners:a(t.Type.Array(n)),error:a(n)}))}),f=t.Type.Object({lines:t.Type.Array(n),capabilities:t.Type.Object({readable:i,writable:i,resizable:i,reason:a(n)}),ended:a(i)}),p=t.Type.Object({held:i,token:a(n),reason:a(n)}),m=`/runs/{workspace}/{runKey}`,h=(0,e.defineApiContract)({version:1,dynamic:[],http:[`global`,`workspace`,`session`].flatMap(s=>[{id:`workflow.screen`,scope:s,basePath:`workflow`,path:`${m}/screen/stream`,method:`GET`,authentication:`owner`,description:`Follow the workflow terminal.`,responses:{200:{description:`SSE stream with JSON screen events.`,contentType:`text/event-stream`,schema:n,events:{screen:f}},404:{description:`Run unavailable.`,schema:t.Type.Object({error:n})}}},{id:`workflow.control`,scope:s,basePath:`workflow`,path:`${m}/control`,method:`POST`,authentication:`owner`,description:`Take or release the keyboard.`,body:{required:!0,contentType:`application/json`,schema:t.Type.Object({release:a(i),token:a(n)})},responses:{...(0,e.jsonApiResponses)(p),409:{description:`Keyboard unavailable.`,schema:p}}},{id:`workflow.keys`,scope:s,basePath:`workflow`,path:`${m}/keys`,method:`POST`,authentication:`owner`,description:`Write terminal data with the control token.`,body:{required:!0,contentType:`application/json`,schema:t.Type.Object({token:n,data:n})},responses:{204:{description:`Data written.`},400:{description:`Invalid request.`,schema:t.Type.Object({error:n})},409:{description:`Write refused.`,schema:t.Type.Object({error:n})}}},{id:`workflow.resize`,scope:s,basePath:`workflow`,path:`${m}/resize`,method:`POST`,authentication:`owner`,description:`Resize the terminal.`,body:{required:!0,contentType:`application/json`,schema:t.Type.Object({token:n,columns:r,rows:r})},responses:(0,e.jsonApiResponses)(t.Type.Object({resized:i}))},{id:`workflow.delete`,scope:s,basePath:`workflow`,path:m,method:`DELETE`,authentication:`owner`,description:`Delete a settled run and its directory.`,responses:(0,e.jsonApiResponses)(t.Type.Object({deleted:t.Type.Literal(!0)}))},{id:`workflow.artifacts`,scope:s,basePath:`workflow`,path:`${m}/artifacts`,method:`GET`,authentication:`owner`,description:`List declared and discovered artifacts.`,responses:(0,e.jsonApiResponses)(t.Type.Object({runDir:n,description:n,artifacts:t.Type.Array(t.Type.Object({...u,declared:i,state:o([`written`,`empty`,`pending`,`unreadable`]),size:a(r),modifiedAt:a(n)}))}))},{id:`workflow.artifact`,scope:s,basePath:`workflow`,path:`${m}/artifacts/{name}`,method:`GET`,authentication:`owner`,description:`Read artifact metadata and text. name may contain nested path segments. raw=1 streams binary with its media type and supports byte ranges.`,parameters:[{name:`raw`,in:`query`,required:!1,schema:o([`0`,`1`])},{name:`download`,in:`query`,required:!1,schema:o([`0`,`1`])},{name:`range`,in:`header`,required:!1,schema:n}],responses:{...(0,e.jsonApiResponses)(t.Type.Union([t.Type.Object({path:n,size:r,modifiedAt:n,mimeType:a(n),text:a(n),truncated:i}),n])),206:{description:`Partial raw bytes.`,contentType:`application/octet-stream`,schema:n},416:{description:`Unsatisfiable byte range.`}}}]),sockets:[`global`,`workspace`].flatMap(e=>[{id:`workflow.runs`,scope:e,service:`doompi.hub.v1`,member:`workflow_runs`,kind:`channel`,direction:`server-to-client`,description:`Workflow runs channel payload.`,input:l},{id:`workflow.catalog`,scope:e,service:`doompi.hub.v1`,member:`workflow_catalog`,kind:`channel`,direction:`server-to-client`,description:`Workflow catalog channel payload.`,input:d}])});exports.WorkflowCatalogSchema=d,exports.WorkflowRunsSchema=l,exports.apiContracts=h,exports.default=h;
2
- //# sourceMappingURL=apiContracts.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"apiContracts.cjs","names":["Type","defineApiContract","jsonApiResponses"],"sources":["../../../src/schemas/apiContracts.ts"],"sourcesContent":["import { defineApiContract, jsonApiResponses, type DoomHttpContract } from '@agimon-ai/doompi-core/api-contracts';\nimport { Type } from 'typebox';\n\nconst S = Type.String();\nconst N = Type.Number();\nconst B = Type.Boolean();\nconst O = Type.Optional;\nconst values = <T extends string>(items: T[]) => Type.Union(items.map((item) => Type.Literal(item)));\nconst Progress = values(['running', 'completed', 'skipped', 'failed', 'pause_requested', 'paused', 'resumed']);\nconst Step = { name: S, status: Progress, reason: O(S), startedAt: O(S), endedAt: O(S) };\nconst Run = Type.Object({\n runKey: S,\n workspace: S,\n displayName: S,\n workflowName: O(S),\n workflowPath: S,\n stage: values(['running', 'completed', 'error']),\n outcome: O(values(['success', 'skipped', 'failed', 'interrupted'])),\n executionState: O(values(['running', 'pause_requested', 'paused', 'resume_requested'])),\n prompt: O(S),\n startedAt: S,\n finishedAt: O(S),\n errorMessage: O(S),\n failedJob: O(S),\n stale: O(B),\n staleReason: O(S),\n worktreeBranch: O(S),\n position: O(Type.Object({ job: S, step: O(S), index: O(N), total: O(N) })),\n jobs: Type.Array(\n Type.Object({\n ...Step,\n phase: values(['pre', 'job', 'post']),\n index: O(N),\n total: O(N),\n steps: Type.Array(Type.Object(Step)),\n }),\n ),\n});\nexport const WorkflowRunsSchema = Type.Object({ runs: Type.Array(Run) });\nconst Artifact = { path: S, kind: values(['file', 'directory']), description: S, producedBy: Type.Array(S) };\nexport const WorkflowCatalogSchema = Type.Object({\n cwd: S,\n warning: O(S),\n workflows: Type.Array(\n Type.Object({\n path: S,\n relativePath: S,\n name: S,\n description: S,\n tags: Type.Array(S),\n triggers: Type.Array(S),\n inputs: Type.Array(\n Type.Object({\n name: S,\n description: O(S),\n required: O(B),\n default: O(S),\n type: O(S),\n options: O(Type.Array(S)),\n }),\n ),\n jobs: Type.Array(Type.Object({ name: S, steps: Type.Array(S) })),\n artifacts: Type.Array(Type.Object(Artifact)),\n runners: O(Type.Array(S)),\n error: O(S),\n }),\n ),\n});\nconst Screen = Type.Object({\n lines: Type.Array(S),\n capabilities: Type.Object({ readable: B, writable: B, resizable: B, reason: O(S) }),\n ended: O(B),\n});\nconst Control = Type.Object({ held: B, token: O(S), reason: O(S) });\nconst root = '/runs/{workspace}/{runKey}';\nexport const apiContracts = defineApiContract({\n version: 1,\n dynamic: [],\n http: (['global', 'workspace', 'session'] as const).flatMap((scope): DoomHttpContract[] => [\n {\n id: 'workflow.screen',\n scope,\n basePath: 'workflow',\n path: `${root}/screen/stream`,\n method: 'GET',\n authentication: 'owner',\n description: 'Follow the workflow terminal.',\n responses: {\n '200': {\n description: 'SSE stream with JSON screen events.',\n contentType: 'text/event-stream',\n schema: S,\n events: { screen: Screen },\n },\n '404': { description: 'Run unavailable.', schema: Type.Object({ error: S }) },\n },\n },\n {\n id: 'workflow.control',\n scope,\n basePath: 'workflow',\n path: `${root}/control`,\n method: 'POST',\n authentication: 'owner',\n description: 'Take or release the keyboard.',\n body: { required: true, contentType: 'application/json', schema: Type.Object({ release: O(B), token: O(S) }) },\n responses: { ...jsonApiResponses(Control), '409': { description: 'Keyboard unavailable.', schema: Control } },\n },\n {\n id: 'workflow.keys',\n scope,\n basePath: 'workflow',\n path: `${root}/keys`,\n method: 'POST',\n authentication: 'owner',\n description: 'Write terminal data with the control token.',\n body: { required: true, contentType: 'application/json', schema: Type.Object({ token: S, data: S }) },\n responses: {\n '204': { description: 'Data written.' },\n '400': { description: 'Invalid request.', schema: Type.Object({ error: S }) },\n '409': { description: 'Write refused.', schema: Type.Object({ error: S }) },\n },\n },\n {\n id: 'workflow.resize',\n scope,\n basePath: 'workflow',\n path: `${root}/resize`,\n method: 'POST',\n authentication: 'owner',\n description: 'Resize the terminal.',\n body: { required: true, contentType: 'application/json', schema: Type.Object({ token: S, columns: N, rows: N }) },\n responses: jsonApiResponses(Type.Object({ resized: B })),\n },\n {\n id: 'workflow.delete',\n scope,\n basePath: 'workflow',\n path: root,\n method: 'DELETE',\n authentication: 'owner',\n description: 'Delete a settled run and its directory.',\n responses: jsonApiResponses(Type.Object({ deleted: Type.Literal(true) })),\n },\n {\n id: 'workflow.artifacts',\n scope,\n basePath: 'workflow',\n path: `${root}/artifacts`,\n method: 'GET',\n authentication: 'owner',\n description: 'List declared and discovered artifacts.',\n responses: jsonApiResponses(\n Type.Object({\n runDir: S,\n description: S,\n artifacts: Type.Array(\n Type.Object({\n ...Artifact,\n declared: B,\n state: values(['written', 'empty', 'pending', 'unreadable']),\n size: O(N),\n modifiedAt: O(S),\n }),\n ),\n }),\n ),\n },\n {\n id: 'workflow.artifact',\n scope,\n basePath: 'workflow',\n path: `${root}/artifacts/{name}`,\n method: 'GET',\n authentication: 'owner',\n description:\n 'Read artifact metadata and text. name may contain nested path segments. raw=1 streams binary with its media type and supports byte ranges.',\n parameters: [\n { name: 'raw', in: 'query', required: false, schema: values(['0', '1']) },\n { name: 'download', in: 'query', required: false, schema: values(['0', '1']) },\n { name: 'range', in: 'header', required: false, schema: S },\n ],\n responses: {\n ...jsonApiResponses(\n Type.Union([Type.Object({ path: S, size: N, modifiedAt: S, mimeType: O(S), text: O(S), truncated: B }), S]),\n ),\n '206': { description: 'Partial raw bytes.', contentType: 'application/octet-stream', schema: S },\n '416': { description: 'Unsatisfiable byte range.' },\n },\n },\n ]),\n sockets: (['global', 'workspace'] as const).flatMap((scope) => [\n {\n id: 'workflow.runs',\n scope,\n service: 'doompi.hub.v1',\n member: 'workflow_runs',\n kind: 'channel',\n direction: 'server-to-client',\n description: 'Workflow runs channel payload.',\n input: WorkflowRunsSchema,\n },\n {\n id: 'workflow.catalog',\n scope,\n service: 'doompi.hub.v1',\n member: 'workflow_catalog',\n kind: 'channel',\n direction: 'server-to-client',\n description: 'Workflow catalog channel payload.',\n input: WorkflowCatalogSchema,\n },\n ]),\n});\nexport default apiContracts;\n"],"mappings":"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"}
@@ -1,53 +0,0 @@
1
- import "typebox";
2
- //#region src/schemas/apiContracts.d.ts
3
- export declare const apiContracts: {
4
- version: 1;
5
- protocols?: Record<string, string> | undefined;
6
- schemas?: Record<string, boolean | object> | undefined;
7
- http: {
8
- id: string;
9
- scope: "global" | "session" | "workspace";
10
- basePath?: string | undefined;
11
- path: string;
12
- method: "DELETE" | "GET" | "HEAD" | "OPTIONS" | "PATCH" | "POST" | "PUT";
13
- description: string;
14
- availability?: string | undefined;
15
- authentication: "device" | "none" | "owner";
16
- parameters?: {
17
- name: string;
18
- in: "header" | "path" | "query";
19
- required: boolean;
20
- schema: boolean | object;
21
- }[] | undefined;
22
- body?: {
23
- contentType: string;
24
- contentTypes?: string[] | undefined;
25
- required: boolean;
26
- schema: boolean | object;
27
- } | undefined;
28
- responses: Record<string, {
29
- description: string;
30
- contentType?: string | undefined;
31
- schema?: boolean | object | undefined;
32
- events?: Record<string, boolean | object> | undefined;
33
- }>;
34
- }[];
35
- sockets: {
36
- id: string;
37
- scope: "global" | "session" | "workspace";
38
- description: string;
39
- availability?: string | undefined;
40
- service: string;
41
- member: string;
42
- direction: "client-to-server" | "server-to-client";
43
- kind: "channel" | "method" | "state";
44
- input: boolean | object;
45
- output?: boolean | object | undefined;
46
- errors?: boolean | object | undefined;
47
- }[];
48
- dynamic: string[];
49
- gaps?: string[] | undefined;
50
- };
51
- //#endregion
52
- export { apiContracts as default };
53
- //# sourceMappingURL=apiContracts.d.cts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"apiContracts.d.cts","names":[],"sources":["../../../src/schemas/apiContracts.ts"],"mappings":";;qBA2Ea"}
@@ -1,53 +0,0 @@
1
- import { Type } from "typebox";
2
- //#region src/schemas/apiContracts.d.ts
3
- export declare const apiContracts: {
4
- version: 1;
5
- protocols?: Record<string, string> | undefined;
6
- schemas?: Record<string, boolean | object> | undefined;
7
- http: {
8
- id: string;
9
- scope: "global" | "session" | "workspace";
10
- basePath?: string | undefined;
11
- path: string;
12
- method: "DELETE" | "GET" | "HEAD" | "OPTIONS" | "PATCH" | "POST" | "PUT";
13
- description: string;
14
- availability?: string | undefined;
15
- authentication: "device" | "none" | "owner";
16
- parameters?: {
17
- name: string;
18
- in: "header" | "path" | "query";
19
- required: boolean;
20
- schema: boolean | object;
21
- }[] | undefined;
22
- body?: {
23
- contentType: string;
24
- contentTypes?: string[] | undefined;
25
- required: boolean;
26
- schema: boolean | object;
27
- } | undefined;
28
- responses: Record<string, {
29
- description: string;
30
- contentType?: string | undefined;
31
- schema?: boolean | object | undefined;
32
- events?: Record<string, boolean | object> | undefined;
33
- }>;
34
- }[];
35
- sockets: {
36
- id: string;
37
- scope: "global" | "session" | "workspace";
38
- description: string;
39
- availability?: string | undefined;
40
- service: string;
41
- member: string;
42
- direction: "client-to-server" | "server-to-client";
43
- kind: "channel" | "method" | "state";
44
- input: boolean | object;
45
- output?: boolean | object | undefined;
46
- errors?: boolean | object | undefined;
47
- }[];
48
- dynamic: string[];
49
- gaps?: string[] | undefined;
50
- };
51
- //#endregion
52
- export { apiContracts as default };
53
- //# sourceMappingURL=apiContracts.d.mts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"apiContracts.d.mts","names":[],"sources":["../../../src/schemas/apiContracts.ts"],"mappings":";;qBA2Ea"}
@@ -1,2 +0,0 @@
1
- import{defineApiContract as e,jsonApiResponses as t}from"@agimon-ai/doompi-core/api-contracts";import{Type as n}from"typebox";const r=n.String(),i=n.Number(),a=n.Boolean(),o=n.Optional,s=e=>n.Union(e.map(e=>n.Literal(e))),c={name:r,status:s([`running`,`completed`,`skipped`,`failed`,`pause_requested`,`paused`,`resumed`]),reason:o(r),startedAt:o(r),endedAt:o(r)},l=n.Object({runKey:r,workspace:r,displayName:r,workflowName:o(r),workflowPath:r,stage:s([`running`,`completed`,`error`]),outcome:o(s([`success`,`skipped`,`failed`,`interrupted`])),executionState:o(s([`running`,`pause_requested`,`paused`,`resume_requested`])),prompt:o(r),startedAt:r,finishedAt:o(r),errorMessage:o(r),failedJob:o(r),stale:o(a),staleReason:o(r),worktreeBranch:o(r),position:o(n.Object({job:r,step:o(r),index:o(i),total:o(i)})),jobs:n.Array(n.Object({...c,phase:s([`pre`,`job`,`post`]),index:o(i),total:o(i),steps:n.Array(n.Object(c))}))}),u=n.Object({runs:n.Array(l)}),d={path:r,kind:s([`file`,`directory`]),description:r,producedBy:n.Array(r)},f=n.Object({cwd:r,warning:o(r),workflows:n.Array(n.Object({path:r,relativePath:r,name:r,description:r,tags:n.Array(r),triggers:n.Array(r),inputs:n.Array(n.Object({name:r,description:o(r),required:o(a),default:o(r),type:o(r),options:o(n.Array(r))})),jobs:n.Array(n.Object({name:r,steps:n.Array(r)})),artifacts:n.Array(n.Object(d)),runners:o(n.Array(r)),error:o(r)}))}),p=n.Object({lines:n.Array(r),capabilities:n.Object({readable:a,writable:a,resizable:a,reason:o(r)}),ended:o(a)}),m=n.Object({held:a,token:o(r),reason:o(r)}),h=`/runs/{workspace}/{runKey}`,g=e({version:1,dynamic:[],http:[`global`,`workspace`,`session`].flatMap(e=>[{id:`workflow.screen`,scope:e,basePath:`workflow`,path:`${h}/screen/stream`,method:`GET`,authentication:`owner`,description:`Follow the workflow terminal.`,responses:{200:{description:`SSE stream with JSON screen events.`,contentType:`text/event-stream`,schema:r,events:{screen:p}},404:{description:`Run unavailable.`,schema:n.Object({error:r})}}},{id:`workflow.control`,scope:e,basePath:`workflow`,path:`${h}/control`,method:`POST`,authentication:`owner`,description:`Take or release the keyboard.`,body:{required:!0,contentType:`application/json`,schema:n.Object({release:o(a),token:o(r)})},responses:{...t(m),409:{description:`Keyboard unavailable.`,schema:m}}},{id:`workflow.keys`,scope:e,basePath:`workflow`,path:`${h}/keys`,method:`POST`,authentication:`owner`,description:`Write terminal data with the control token.`,body:{required:!0,contentType:`application/json`,schema:n.Object({token:r,data:r})},responses:{204:{description:`Data written.`},400:{description:`Invalid request.`,schema:n.Object({error:r})},409:{description:`Write refused.`,schema:n.Object({error:r})}}},{id:`workflow.resize`,scope:e,basePath:`workflow`,path:`${h}/resize`,method:`POST`,authentication:`owner`,description:`Resize the terminal.`,body:{required:!0,contentType:`application/json`,schema:n.Object({token:r,columns:i,rows:i})},responses:t(n.Object({resized:a}))},{id:`workflow.delete`,scope:e,basePath:`workflow`,path:h,method:`DELETE`,authentication:`owner`,description:`Delete a settled run and its directory.`,responses:t(n.Object({deleted:n.Literal(!0)}))},{id:`workflow.artifacts`,scope:e,basePath:`workflow`,path:`${h}/artifacts`,method:`GET`,authentication:`owner`,description:`List declared and discovered artifacts.`,responses:t(n.Object({runDir:r,description:r,artifacts:n.Array(n.Object({...d,declared:a,state:s([`written`,`empty`,`pending`,`unreadable`]),size:o(i),modifiedAt:o(r)}))}))},{id:`workflow.artifact`,scope:e,basePath:`workflow`,path:`${h}/artifacts/{name}`,method:`GET`,authentication:`owner`,description:`Read artifact metadata and text. name may contain nested path segments. raw=1 streams binary with its media type and supports byte ranges.`,parameters:[{name:`raw`,in:`query`,required:!1,schema:s([`0`,`1`])},{name:`download`,in:`query`,required:!1,schema:s([`0`,`1`])},{name:`range`,in:`header`,required:!1,schema:r}],responses:{...t(n.Union([n.Object({path:r,size:i,modifiedAt:r,mimeType:o(r),text:o(r),truncated:a}),r])),206:{description:`Partial raw bytes.`,contentType:`application/octet-stream`,schema:r},416:{description:`Unsatisfiable byte range.`}}}]),sockets:[`global`,`workspace`].flatMap(e=>[{id:`workflow.runs`,scope:e,service:`doompi.hub.v1`,member:`workflow_runs`,kind:`channel`,direction:`server-to-client`,description:`Workflow runs channel payload.`,input:u},{id:`workflow.catalog`,scope:e,service:`doompi.hub.v1`,member:`workflow_catalog`,kind:`channel`,direction:`server-to-client`,description:`Workflow catalog channel payload.`,input:f}])});export{f as WorkflowCatalogSchema,u as WorkflowRunsSchema,g as apiContracts,g as default};
2
- //# sourceMappingURL=apiContracts.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"apiContracts.mjs","names":[],"sources":["../../../src/schemas/apiContracts.ts"],"sourcesContent":["import { defineApiContract, jsonApiResponses, type DoomHttpContract } from '@agimon-ai/doompi-core/api-contracts';\nimport { Type } from 'typebox';\n\nconst S = Type.String();\nconst N = Type.Number();\nconst B = Type.Boolean();\nconst O = Type.Optional;\nconst values = <T extends string>(items: T[]) => Type.Union(items.map((item) => Type.Literal(item)));\nconst Progress = values(['running', 'completed', 'skipped', 'failed', 'pause_requested', 'paused', 'resumed']);\nconst Step = { name: S, status: Progress, reason: O(S), startedAt: O(S), endedAt: O(S) };\nconst Run = Type.Object({\n runKey: S,\n workspace: S,\n displayName: S,\n workflowName: O(S),\n workflowPath: S,\n stage: values(['running', 'completed', 'error']),\n outcome: O(values(['success', 'skipped', 'failed', 'interrupted'])),\n executionState: O(values(['running', 'pause_requested', 'paused', 'resume_requested'])),\n prompt: O(S),\n startedAt: S,\n finishedAt: O(S),\n errorMessage: O(S),\n failedJob: O(S),\n stale: O(B),\n staleReason: O(S),\n worktreeBranch: O(S),\n position: O(Type.Object({ job: S, step: O(S), index: O(N), total: O(N) })),\n jobs: Type.Array(\n Type.Object({\n ...Step,\n phase: values(['pre', 'job', 'post']),\n index: O(N),\n total: O(N),\n steps: Type.Array(Type.Object(Step)),\n }),\n ),\n});\nexport const WorkflowRunsSchema = Type.Object({ runs: Type.Array(Run) });\nconst Artifact = { path: S, kind: values(['file', 'directory']), description: S, producedBy: Type.Array(S) };\nexport const WorkflowCatalogSchema = Type.Object({\n cwd: S,\n warning: O(S),\n workflows: Type.Array(\n Type.Object({\n path: S,\n relativePath: S,\n name: S,\n description: S,\n tags: Type.Array(S),\n triggers: Type.Array(S),\n inputs: Type.Array(\n Type.Object({\n name: S,\n description: O(S),\n required: O(B),\n default: O(S),\n type: O(S),\n options: O(Type.Array(S)),\n }),\n ),\n jobs: Type.Array(Type.Object({ name: S, steps: Type.Array(S) })),\n artifacts: Type.Array(Type.Object(Artifact)),\n runners: O(Type.Array(S)),\n error: O(S),\n }),\n ),\n});\nconst Screen = Type.Object({\n lines: Type.Array(S),\n capabilities: Type.Object({ readable: B, writable: B, resizable: B, reason: O(S) }),\n ended: O(B),\n});\nconst Control = Type.Object({ held: B, token: O(S), reason: O(S) });\nconst root = '/runs/{workspace}/{runKey}';\nexport const apiContracts = defineApiContract({\n version: 1,\n dynamic: [],\n http: (['global', 'workspace', 'session'] as const).flatMap((scope): DoomHttpContract[] => [\n {\n id: 'workflow.screen',\n scope,\n basePath: 'workflow',\n path: `${root}/screen/stream`,\n method: 'GET',\n authentication: 'owner',\n description: 'Follow the workflow terminal.',\n responses: {\n '200': {\n description: 'SSE stream with JSON screen events.',\n contentType: 'text/event-stream',\n schema: S,\n events: { screen: Screen },\n },\n '404': { description: 'Run unavailable.', schema: Type.Object({ error: S }) },\n },\n },\n {\n id: 'workflow.control',\n scope,\n basePath: 'workflow',\n path: `${root}/control`,\n method: 'POST',\n authentication: 'owner',\n description: 'Take or release the keyboard.',\n body: { required: true, contentType: 'application/json', schema: Type.Object({ release: O(B), token: O(S) }) },\n responses: { ...jsonApiResponses(Control), '409': { description: 'Keyboard unavailable.', schema: Control } },\n },\n {\n id: 'workflow.keys',\n scope,\n basePath: 'workflow',\n path: `${root}/keys`,\n method: 'POST',\n authentication: 'owner',\n description: 'Write terminal data with the control token.',\n body: { required: true, contentType: 'application/json', schema: Type.Object({ token: S, data: S }) },\n responses: {\n '204': { description: 'Data written.' },\n '400': { description: 'Invalid request.', schema: Type.Object({ error: S }) },\n '409': { description: 'Write refused.', schema: Type.Object({ error: S }) },\n },\n },\n {\n id: 'workflow.resize',\n scope,\n basePath: 'workflow',\n path: `${root}/resize`,\n method: 'POST',\n authentication: 'owner',\n description: 'Resize the terminal.',\n body: { required: true, contentType: 'application/json', schema: Type.Object({ token: S, columns: N, rows: N }) },\n responses: jsonApiResponses(Type.Object({ resized: B })),\n },\n {\n id: 'workflow.delete',\n scope,\n basePath: 'workflow',\n path: root,\n method: 'DELETE',\n authentication: 'owner',\n description: 'Delete a settled run and its directory.',\n responses: jsonApiResponses(Type.Object({ deleted: Type.Literal(true) })),\n },\n {\n id: 'workflow.artifacts',\n scope,\n basePath: 'workflow',\n path: `${root}/artifacts`,\n method: 'GET',\n authentication: 'owner',\n description: 'List declared and discovered artifacts.',\n responses: jsonApiResponses(\n Type.Object({\n runDir: S,\n description: S,\n artifacts: Type.Array(\n Type.Object({\n ...Artifact,\n declared: B,\n state: values(['written', 'empty', 'pending', 'unreadable']),\n size: O(N),\n modifiedAt: O(S),\n }),\n ),\n }),\n ),\n },\n {\n id: 'workflow.artifact',\n scope,\n basePath: 'workflow',\n path: `${root}/artifacts/{name}`,\n method: 'GET',\n authentication: 'owner',\n description:\n 'Read artifact metadata and text. name may contain nested path segments. raw=1 streams binary with its media type and supports byte ranges.',\n parameters: [\n { name: 'raw', in: 'query', required: false, schema: values(['0', '1']) },\n { name: 'download', in: 'query', required: false, schema: values(['0', '1']) },\n { name: 'range', in: 'header', required: false, schema: S },\n ],\n responses: {\n ...jsonApiResponses(\n Type.Union([Type.Object({ path: S, size: N, modifiedAt: S, mimeType: O(S), text: O(S), truncated: B }), S]),\n ),\n '206': { description: 'Partial raw bytes.', contentType: 'application/octet-stream', schema: S },\n '416': { description: 'Unsatisfiable byte range.' },\n },\n },\n ]),\n sockets: (['global', 'workspace'] as const).flatMap((scope) => [\n {\n id: 'workflow.runs',\n scope,\n service: 'doompi.hub.v1',\n member: 'workflow_runs',\n kind: 'channel',\n direction: 'server-to-client',\n description: 'Workflow runs channel payload.',\n input: WorkflowRunsSchema,\n },\n {\n id: 'workflow.catalog',\n scope,\n service: 'doompi.hub.v1',\n member: 'workflow_catalog',\n kind: 'channel',\n direction: 'server-to-client',\n description: 'Workflow catalog channel payload.',\n input: WorkflowCatalogSchema,\n },\n ]),\n});\nexport default apiContracts;\n"],"mappings":"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"}
@@ -1,2 +0,0 @@
1
- let e=require("zod");const t={runKey:e.z.string().min(1).describe(`Run key of the workflow run.`),workspace:e.z.string().optional().describe(`Workspace the run belongs to.`)},n=e.z.discriminatedUnion(`action`,[e.z.object({action:e.z.literal(`status`),...t}).strict(),e.z.object({action:e.z.literal(`tail`),...t}).strict(),e.z.object({action:e.z.literal(`recovery-evidence`),...t}).strict(),e.z.object({action:e.z.literal(`follow`),...t}).strict(),e.z.object({action:e.z.literal(`open`),...t}).strict(),e.z.object({action:e.z.literal(`pause`),...t,reason:e.z.string().optional()}).strict(),e.z.object({action:e.z.literal(`resume`),...t,reason:e.z.string().optional()}).strict(),e.z.object({action:e.z.literal(`stop`),...t,reason:e.z.string().optional()}).strict(),e.z.object({action:e.z.literal(`recover`),...t,dryRun:e.z.boolean().optional(),job:e.z.string().optional(),runner:e.z.string().optional()}).strict()]),r=e.z.object({description:e.z.string().optional().describe(`Description shown for the workflow input.`),required:e.z.boolean().optional().describe(`Whether the workflow input must be provided.`),default:e.z.string().optional().describe(`Default value for the workflow input.`),type:e.z.string().optional().describe(`Declared workflow input type.`),options:e.z.array(e.z.string()).optional().describe(`Allowed values for the workflow input.`)}).passthrough(),i=e.z.object({inputs:e.z.record(e.z.string(),r).optional().describe(`Workflow dispatch input definitions.`)}).passthrough(),a=e.z.object({directory:e.z.string().min(1).describe(`Directory searched for workflow definitions.`),hasNextPage:e.z.boolean().describe(`Whether another catalog page exists.`),page:e.z.number().int().positive().describe(`One-based catalog page number.`),pageSize:e.z.number().int().positive().describe(`Catalog page size.`),workflows:e.z.array(e.z.object({path:e.z.string().min(1).describe(`Workflow path relative to the catalog directory.`),name:e.z.string().describe(`Workflow display name.`),description:e.z.string().describe(`Workflow description.`),tags:e.z.array(e.z.string()).describe(`Workflow tags.`)}).passthrough()).describe(`Workflow definitions on this page.`)}).passthrough();exports.dispatchInputSchema=r,exports.workflowCatalogPageSchema=a,exports.workflowDispatchInputsSchema=i,exports.workflowRunInputSchema=n;
2
- //# sourceMappingURL=workflowPi.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"workflowPi.cjs","names":["z"],"sources":["../../../src/schemas/workflowPi.ts"],"sourcesContent":["import { z } from 'zod';\n\nconst RUN_SELECTOR_FIELDS = {\n runKey: z.string().min(1).describe('Run key of the workflow run.'),\n workspace: z.string().optional().describe('Workspace the run belongs to.'),\n} as const;\n\nexport const workflowRunInputSchema = z.discriminatedUnion('action', [\n z.object({ action: z.literal('status'), ...RUN_SELECTOR_FIELDS }).strict(),\n z.object({ action: z.literal('tail'), ...RUN_SELECTOR_FIELDS }).strict(),\n z.object({ action: z.literal('recovery-evidence'), ...RUN_SELECTOR_FIELDS }).strict(),\n z.object({ action: z.literal('follow'), ...RUN_SELECTOR_FIELDS }).strict(),\n z.object({ action: z.literal('open'), ...RUN_SELECTOR_FIELDS }).strict(),\n z.object({ action: z.literal('pause'), ...RUN_SELECTOR_FIELDS, reason: z.string().optional() }).strict(),\n z.object({ action: z.literal('resume'), ...RUN_SELECTOR_FIELDS, reason: z.string().optional() }).strict(),\n z.object({ action: z.literal('stop'), ...RUN_SELECTOR_FIELDS, reason: z.string().optional() }).strict(),\n z\n .object({\n action: z.literal('recover'),\n ...RUN_SELECTOR_FIELDS,\n dryRun: z.boolean().optional(),\n job: z.string().optional(),\n runner: z.string().optional(),\n })\n .strict(),\n]);\n\nexport type WorkflowRunInput = z.infer<typeof workflowRunInputSchema>;\n\nexport const dispatchInputSchema = z\n .object({\n description: z.string().optional().describe('Description shown for the workflow input.'),\n required: z.boolean().optional().describe('Whether the workflow input must be provided.'),\n default: z.string().optional().describe('Default value for the workflow input.'),\n type: z.string().optional().describe('Declared workflow input type.'),\n options: z.array(z.string()).optional().describe('Allowed values for the workflow input.'),\n })\n .passthrough();\n\nexport type DispatchInput = z.infer<typeof dispatchInputSchema>;\n\nexport const workflowDispatchInputsSchema = z\n .object({\n inputs: z.record(z.string(), dispatchInputSchema).optional().describe('Workflow dispatch input definitions.'),\n })\n .passthrough();\n\nexport const workflowCatalogPageSchema = z\n .object({\n directory: z.string().min(1).describe('Directory searched for workflow definitions.'),\n hasNextPage: z.boolean().describe('Whether another catalog page exists.'),\n page: z.number().int().positive().describe('One-based catalog page number.'),\n pageSize: z.number().int().positive().describe('Catalog page size.'),\n workflows: z\n .array(\n z\n .object({\n path: z.string().min(1).describe('Workflow path relative to the catalog directory.'),\n name: z.string().describe('Workflow display name.'),\n description: z.string().describe('Workflow description.'),\n tags: z.array(z.string()).describe('Workflow tags.'),\n })\n .passthrough(),\n )\n .describe('Workflow definitions on this page.'),\n })\n .passthrough();\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- import "zod";
@@ -1 +0,0 @@
1
- import { z } from "zod";
@@ -1,2 +0,0 @@
1
- import{z as e}from"zod";const t={runKey:e.string().min(1).describe(`Run key of the workflow run.`),workspace:e.string().optional().describe(`Workspace the run belongs to.`)},n=e.discriminatedUnion(`action`,[e.object({action:e.literal(`status`),...t}).strict(),e.object({action:e.literal(`tail`),...t}).strict(),e.object({action:e.literal(`recovery-evidence`),...t}).strict(),e.object({action:e.literal(`follow`),...t}).strict(),e.object({action:e.literal(`open`),...t}).strict(),e.object({action:e.literal(`pause`),...t,reason:e.string().optional()}).strict(),e.object({action:e.literal(`resume`),...t,reason:e.string().optional()}).strict(),e.object({action:e.literal(`stop`),...t,reason:e.string().optional()}).strict(),e.object({action:e.literal(`recover`),...t,dryRun:e.boolean().optional(),job:e.string().optional(),runner:e.string().optional()}).strict()]),r=e.object({description:e.string().optional().describe(`Description shown for the workflow input.`),required:e.boolean().optional().describe(`Whether the workflow input must be provided.`),default:e.string().optional().describe(`Default value for the workflow input.`),type:e.string().optional().describe(`Declared workflow input type.`),options:e.array(e.string()).optional().describe(`Allowed values for the workflow input.`)}).passthrough(),i=e.object({inputs:e.record(e.string(),r).optional().describe(`Workflow dispatch input definitions.`)}).passthrough(),a=e.object({directory:e.string().min(1).describe(`Directory searched for workflow definitions.`),hasNextPage:e.boolean().describe(`Whether another catalog page exists.`),page:e.number().int().positive().describe(`One-based catalog page number.`),pageSize:e.number().int().positive().describe(`Catalog page size.`),workflows:e.array(e.object({path:e.string().min(1).describe(`Workflow path relative to the catalog directory.`),name:e.string().describe(`Workflow display name.`),description:e.string().describe(`Workflow description.`),tags:e.array(e.string()).describe(`Workflow tags.`)}).passthrough()).describe(`Workflow definitions on this page.`)}).passthrough();export{r as dispatchInputSchema,a as workflowCatalogPageSchema,i as workflowDispatchInputsSchema,n as workflowRunInputSchema};
2
- //# sourceMappingURL=workflowPi.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"workflowPi.mjs","names":[],"sources":["../../../src/schemas/workflowPi.ts"],"sourcesContent":["import { z } from 'zod';\n\nconst RUN_SELECTOR_FIELDS = {\n runKey: z.string().min(1).describe('Run key of the workflow run.'),\n workspace: z.string().optional().describe('Workspace the run belongs to.'),\n} as const;\n\nexport const workflowRunInputSchema = z.discriminatedUnion('action', [\n z.object({ action: z.literal('status'), ...RUN_SELECTOR_FIELDS }).strict(),\n z.object({ action: z.literal('tail'), ...RUN_SELECTOR_FIELDS }).strict(),\n z.object({ action: z.literal('recovery-evidence'), ...RUN_SELECTOR_FIELDS }).strict(),\n z.object({ action: z.literal('follow'), ...RUN_SELECTOR_FIELDS }).strict(),\n z.object({ action: z.literal('open'), ...RUN_SELECTOR_FIELDS }).strict(),\n z.object({ action: z.literal('pause'), ...RUN_SELECTOR_FIELDS, reason: z.string().optional() }).strict(),\n z.object({ action: z.literal('resume'), ...RUN_SELECTOR_FIELDS, reason: z.string().optional() }).strict(),\n z.object({ action: z.literal('stop'), ...RUN_SELECTOR_FIELDS, reason: z.string().optional() }).strict(),\n z\n .object({\n action: z.literal('recover'),\n ...RUN_SELECTOR_FIELDS,\n dryRun: z.boolean().optional(),\n job: z.string().optional(),\n runner: z.string().optional(),\n })\n .strict(),\n]);\n\nexport type WorkflowRunInput = z.infer<typeof workflowRunInputSchema>;\n\nexport const dispatchInputSchema = z\n .object({\n description: z.string().optional().describe('Description shown for the workflow input.'),\n required: z.boolean().optional().describe('Whether the workflow input must be provided.'),\n default: z.string().optional().describe('Default value for the workflow input.'),\n type: z.string().optional().describe('Declared workflow input type.'),\n options: z.array(z.string()).optional().describe('Allowed values for the workflow input.'),\n })\n .passthrough();\n\nexport type DispatchInput = z.infer<typeof dispatchInputSchema>;\n\nexport const workflowDispatchInputsSchema = z\n .object({\n inputs: z.record(z.string(), dispatchInputSchema).optional().describe('Workflow dispatch input definitions.'),\n })\n .passthrough();\n\nexport const workflowCatalogPageSchema = z\n .object({\n directory: z.string().min(1).describe('Directory searched for workflow definitions.'),\n hasNextPage: z.boolean().describe('Whether another catalog page exists.'),\n page: z.number().int().positive().describe('One-based catalog page number.'),\n pageSize: z.number().int().positive().describe('Catalog page size.'),\n workflows: z\n .array(\n z\n .object({\n path: z.string().min(1).describe('Workflow path relative to the catalog directory.'),\n name: z.string().describe('Workflow display name.'),\n description: z.string().describe('Workflow description.'),\n tags: z.array(z.string()).describe('Workflow tags.'),\n })\n .passthrough(),\n )\n .describe('Workflow definitions on this page.'),\n })\n .passthrough();\n"],"mappings":"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"}
@@ -1,2 +0,0 @@
1
- let e=require("@agimon-ai/doompi-core/background-work");function t(t){let n=[],r,i=!1;return t.inject([e.DOOM_BACKGROUND_WORK_SERVICE],t=>{if(i)return;let a=(0,e.readDoomBackgroundWorkService)(t);if(!a)return;let o=a.register({provider:`workflow-mcp`,listActiveWork:()=>n});return r=o,()=>{o.dispose(),r===o&&(r=void 0)}}),{update(e){n=e,r?.update()},dispose(){i||(i=!0,n=[],r?.dispose(),r=void 0)}}}exports.registerRunProvider=t;
2
- //# sourceMappingURL=index.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.cjs","names":["DOOM_BACKGROUND_WORK_SERVICE","readDoomBackgroundWorkService"],"sources":["../../../../src/services/backgroundWork/index.ts"],"sourcesContent":["/**\n * Background-work registration for workflow runs.\n *\n * DESIGN PATTERNS:\n * - Hard dependency binding through Cordis injection\n * - Snapshot pattern: the extension pushes the current run set, the provider\n * only reads it\n *\n * CODING STANDARDS:\n * - Named exports only\n * - Explicit return types on exported functions\n *\n * AVOID:\n * - Process-global symbols or dynamic imports for extension communication\n * - Reporting runs owned by another Pi session\n */\n\nimport {\n DOOM_BACKGROUND_WORK_SERVICE,\n type BackgroundWorkProviderHandle,\n readDoomBackgroundWorkService,\n} from '@agimon-ai/doompi-core/background-work';\nimport type { Context } from '@deepseek-ai/cordis';\n\nexport interface RunProviderItem {\n id: string;\n sessionId: string;\n}\n\nexport interface RunProviderHandle {\n /** Replace the reported run set. */\n update(items: RunProviderItem[]): void;\n /** Remove the registration. Safe to call when registration never happened. */\n dispose(): void;\n}\n\nconst PROVIDER_NAME = 'workflow-mcp';\n\n/**\n * Register a background-work provider for this session's workflow runs.\n *\n * The runtime pins all announcements to the active Pi session and validates\n * each snapshot before it reaches another extension.\n */\nexport function registerRunProvider(ctx: Context): RunProviderHandle {\n let items: RunProviderItem[] = [];\n let provider: BackgroundWorkProviderHandle | undefined;\n let disposed = false;\n ctx.inject([DOOM_BACKGROUND_WORK_SERVICE], (serviceContext) => {\n if (disposed) return undefined;\n const service = readDoomBackgroundWorkService(serviceContext);\n if (!service) return undefined;\n const registration = service.register({ provider: PROVIDER_NAME, listActiveWork: () => items });\n provider = registration;\n return () => {\n registration.dispose();\n if (provider === registration) provider = undefined;\n };\n });\n const handle: RunProviderHandle = {\n update(next) {\n items = next;\n provider?.update();\n },\n dispose() {\n if (disposed) return;\n disposed = true;\n items = [];\n provider?.dispose();\n provider = undefined;\n },\n };\n return handle;\n}\n"],"mappings":"wDA4CA,SAAgB,EAAoB,EAAiC,CACnE,IAAI,EAA2B,CAAC,EAC5B,EACA,EAAW,GAyBf,OAxBA,EAAI,OAAO,CAACA,EAAAA,4BAA4B,EAAI,GAAmB,CAC7D,GAAI,EAAU,OACd,IAAM,GAAA,EAAUC,EAAAA,8BAAAA,CAA8B,CAAc,EAC5D,GAAI,CAAC,EAAS,OACd,IAAM,EAAe,EAAQ,SAAS,CAAE,SAAU,eAAe,mBAAsB,CAAM,CAAC,EAE9F,MADA,GAAW,MACE,CACX,EAAa,QAAQ,EACjB,IAAa,IAAc,EAAW,IAAA,GAC5C,CACF,CAAC,EAcM,CAZL,OAAO,EAAM,CACX,EAAQ,EACR,GAAU,OAAO,CACnB,EACA,SAAU,CACJ,IACJ,EAAW,GACX,EAAQ,CAAC,EACT,GAAU,QAAQ,EAClB,EAAW,IAAA,GACb,CAEU,CACd"}
@@ -1,2 +0,0 @@
1
- import{DOOM_BACKGROUND_WORK_SERVICE as e,readDoomBackgroundWorkService as t}from"@agimon-ai/doompi-core/background-work";function n(n){let r=[],i,a=!1;return n.inject([e],e=>{if(a)return;let n=t(e);if(!n)return;let o=n.register({provider:`workflow-mcp`,listActiveWork:()=>r});return i=o,()=>{o.dispose(),i===o&&(i=void 0)}}),{update(e){r=e,i?.update()},dispose(){a||(a=!0,r=[],i?.dispose(),i=void 0)}}}export{n as registerRunProvider};
2
- //# sourceMappingURL=index.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../../../src/services/backgroundWork/index.ts"],"sourcesContent":["/**\n * Background-work registration for workflow runs.\n *\n * DESIGN PATTERNS:\n * - Hard dependency binding through Cordis injection\n * - Snapshot pattern: the extension pushes the current run set, the provider\n * only reads it\n *\n * CODING STANDARDS:\n * - Named exports only\n * - Explicit return types on exported functions\n *\n * AVOID:\n * - Process-global symbols or dynamic imports for extension communication\n * - Reporting runs owned by another Pi session\n */\n\nimport {\n DOOM_BACKGROUND_WORK_SERVICE,\n type BackgroundWorkProviderHandle,\n readDoomBackgroundWorkService,\n} from '@agimon-ai/doompi-core/background-work';\nimport type { Context } from '@deepseek-ai/cordis';\n\nexport interface RunProviderItem {\n id: string;\n sessionId: string;\n}\n\nexport interface RunProviderHandle {\n /** Replace the reported run set. */\n update(items: RunProviderItem[]): void;\n /** Remove the registration. Safe to call when registration never happened. */\n dispose(): void;\n}\n\nconst PROVIDER_NAME = 'workflow-mcp';\n\n/**\n * Register a background-work provider for this session's workflow runs.\n *\n * The runtime pins all announcements to the active Pi session and validates\n * each snapshot before it reaches another extension.\n */\nexport function registerRunProvider(ctx: Context): RunProviderHandle {\n let items: RunProviderItem[] = [];\n let provider: BackgroundWorkProviderHandle | undefined;\n let disposed = false;\n ctx.inject([DOOM_BACKGROUND_WORK_SERVICE], (serviceContext) => {\n if (disposed) return undefined;\n const service = readDoomBackgroundWorkService(serviceContext);\n if (!service) return undefined;\n const registration = service.register({ provider: PROVIDER_NAME, listActiveWork: () => items });\n provider = registration;\n return () => {\n registration.dispose();\n if (provider === registration) provider = undefined;\n };\n });\n const handle: RunProviderHandle = {\n update(next) {\n items = next;\n provider?.update();\n },\n dispose() {\n if (disposed) return;\n disposed = true;\n items = [];\n provider?.dispose();\n provider = undefined;\n },\n };\n return handle;\n}\n"],"mappings":"yHA4CA,SAAgB,EAAoB,EAAiC,CACnE,IAAI,EAA2B,CAAC,EAC5B,EACA,EAAW,GAyBf,OAxBA,EAAI,OAAO,CAAC,CAA4B,EAAI,GAAmB,CAC7D,GAAI,EAAU,OACd,IAAM,EAAU,EAA8B,CAAc,EAC5D,GAAI,CAAC,EAAS,OACd,IAAM,EAAe,EAAQ,SAAS,CAAE,SAAU,eAAe,mBAAsB,CAAM,CAAC,EAE9F,MADA,GAAW,MACE,CACX,EAAa,QAAQ,EACjB,IAAa,IAAc,EAAW,IAAA,GAC5C,CACF,CAAC,EAcM,CAZL,OAAO,EAAM,CACX,EAAQ,EACR,GAAU,OAAO,CACnB,EACA,SAAU,CACJ,IACJ,EAAW,GACX,EAAQ,CAAC,EACT,GAAU,QAAQ,EAClB,EAAW,IAAA,GACb,CAEU,CACd"}
@@ -1,11 +0,0 @@
1
- function e(e=process.env){let t=Number(e.WORKFLOW_MCP_MAX_CONCURRENT);return Number.isInteger(t)&&t>0?t:5}function t(e,t){return t!==void 0&&e.env?.PI_SESSION_ID===t}function n(e,n){return e.filter(e=>t(e,n)&&e.stage===`running`)}function r(e){return[`Started ${e.displayName} in workspace ${e.workspace}.`,`Run key: ${e.runKey}`,`The run is registered and going. This call returned at that point rather than waiting for the launcher process to exit.`].join(`
2
- `)}function i(e){return[`Launch handed off for ${e}, but no run has registered yet.`,`Nothing was cancelled: the launcher is still starting, and a run that registers later still reports back here when it finishes.`,`Do not launch it again. Ask the user to check the active workflow list if they need to see it sooner.`].join(`
3
- `)}const a=new RegExp([`\\u001B\\[[0-9;?]*[ -/]*[@-~]`,`\\u001B\\][^\\u0007\\u001B]*(?:\\u0007|\\u001B\\\\)`,`\\u001B[@-Z\\\\-_]`].join(`|`),`g`),o=/^[\s─-╿]*$/u;function s(e,t=3){let n=e.replace(a,``).split(`
4
- `).map(e=>e.trimEnd()).filter(e=>e.trim().length>0&&!o.test(e)).map(e=>e.length>100?`${e.slice(0,99)}…`:e);return n.length===0?`Workflow started.`:n.length<=t?n.join(`
5
- `):[...n.slice(0,t),`… ${n.length-t} more lines, in the run's own output.`].join(`
6
- `)}function c(e){return e.content.map(e=>e.type===`text`?e.text:``).filter(Boolean).join(`
7
- `).trim()}function l(e,t,n=[]){let r=c(t)||`No output.`;if(t.isError)throw Error(u(r,n));return{content:[{type:`text`,text:r}],details:{tool:e}}}function u(e,t){return t.length===0?e:[e,``,`Options, put these to the user rather than picking one yourself:`,...t.map(d)].join(`
8
- `)}function d(e){return`- ${e}`}function f(e,t){let n=t.find(e=>e.status===`failed`),r=n?.steps.find(e=>e.status===`failed`);return{job:n?.name??e.failedJob,step:r?.name,reason:r?.reason??n?.reason}}function p(e,t=[]){let n=e.stage===`completed`?`completed`:`ended in ${e.stage}`,r=f(e,t),i=[`Workflow run ${e.runKey} in workspace ${e.workspace} ${n}.`,e.workflowId?`Workflow: ${e.workflowId}`:void 0,r.job?`Failed job: ${r.job}${r.step?` (step: ${r.step})`:``}`:void 0,e.errorMessage?`Error: ${e.errorMessage}`:void 0,r.reason&&r.reason!==e.errorMessage?`Step reported: ${r.reason}`:void 0],a=e.env?.AGIFLOW_JOB_ID;a&&i.push(`Agiflow job: ${e.env?.AGIFLOW_JOB_KIND??`unknown`} ${a}`);let o=i.filter(e=>!!e).join(`
9
- `);if(e.stage===`completed`)return o;if(e.outcome===`interrupted`)return u(o,[`workflow_run {"action":"recover","runKey":${JSON.stringify(e.runKey)}}: pick it up where it stopped.`,`Leave it stopped and move on.`]);let s=[e.errorMessage,r.reason].filter(Boolean).join(`
10
- `);return s.includes(`JOB_ALREADY_CLAIMED`)?u(o,[`Report contention: another worker owns this job. Do not unlock, release, or retry the claim automatically.`]):s.includes(`WORKFLOW_NOT_OWNED`)||/release failed/i.test(s)?u(o,[`Inspect the current workflow ownership and ask the user before any release or unlock. Never force another worker’s lock.`]):/running workflow not found/i.test(s)?u(o,[`workflow_run {"action":"status","runKey":${JSON.stringify(e.runKey)}}: re-check current state before retrying.`,`Report the recorded terminal state when no running owner remains.`]):u(o,[`workflow_run {"action":"tail","runKey":${JSON.stringify(e.runKey)}}: read the run's own output first, when the error above does not explain the failure.`,`workflow_run {"action":"recover","runKey":${JSON.stringify(e.runKey)}}: resume from the failed job, keeping the work already done. Fits a transient or external cause.`,`Report the failure and stop, when it needs a code change or a decision you cannot make.`])}exports.PI_SESSION_ENV=`PI_SESSION_ID`,exports.finishedRunSummary=p,exports.isSessionRun=t,exports.launchHandoffSummary=i,exports.launchNotice=s,exports.launchedRunSummary=r,exports.resolveMaxConcurrent=e,exports.runsForSession=n,exports.toAgentToolResult=l,exports.toolResultText=c,exports.withOptions=u;
11
- //# sourceMappingURL=index.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.cjs","names":[],"sources":["../../../../src/services/piToolBridge/index.ts"],"sourcesContent":["/**\n * Pure bridging logic between MCP tool results and the Pi tool surface.\n *\n * DESIGN PATTERNS:\n * - Adapter pattern: MCP `CallToolResult` is converted to Pi's result shape\n * - Pure module: no service or tool-class imports, so this is unit-testable\n * without constructing the workflow execution stack\n *\n * CODING STANDARDS:\n * - Named exports only\n * - Explicit return types on exported functions\n *\n * AVOID:\n * - Importing tool classes or services here (that belongs in `piTools.ts`)\n * - Returning an error flag Pi does not read; Pi tools signal failure by throwing\n */\n\nimport type { WorkflowProgressJob, WorkflowRunRecord } from '@agimon-ai/workflow-mcp';\nimport type { AgentToolResult } from '@earendil-works/pi-coding-agent';\nimport type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';\n\n/**\n * Environment variable stamped onto every run launched from a Pi session.\n *\n * `RunWorkflowService` forwards `env` to the inner CLI invocation across the\n * `launch-command` delegation boundary and persists it on the run record, so\n * this survives backgrounding and identifies the launching session afterwards.\n */\nexport const PI_SESSION_ENV = 'PI_SESSION_ID';\n\n/** Operator override for the per-session launch ceiling. */\nconst MAX_CONCURRENT_ENV = 'WORKFLOW_MCP_MAX_CONCURRENT';\nconst DEFAULT_MAX_CONCURRENT = 5;\n\nexport interface WorkflowToolDetails {\n tool: string;\n}\n\n/** Resolve the per-session launch ceiling from the environment. */\nexport function resolveMaxConcurrent(env: NodeJS.ProcessEnv = process.env): number {\n const parsed = Number(env[MAX_CONCURRENT_ENV]);\n return Number.isInteger(parsed) && parsed > 0 ? parsed : DEFAULT_MAX_CONCURRENT;\n}\n\n/**\n * True when this session launched the run.\n *\n * Fails closed on a missing session id: an unstamped record (a CLI launch) and\n * a call arriving before the session id is known both match nothing, so no run\n * ever leaks across sessions through an undefined-equals-undefined comparison.\n */\nexport function isSessionRun(record: WorkflowRunRecord, sessionId: string | undefined): boolean {\n return sessionId !== undefined && record.env?.[PI_SESSION_ENV] === sessionId;\n}\n\n/** Runs launched by one Pi session that are still running. */\nexport function runsForSession(records: WorkflowRunRecord[], sessionId: string): WorkflowRunRecord[] {\n return records.filter((record) => isSessionRun(record, sessionId) && record.stage === 'running');\n}\n\n/**\n * What the caller is told when the registry, not the launcher, acknowledged a\n * launch.\n *\n * Leads with the run key because it is the handle every other workflow action\n * takes, and it is no longer available any other way: the launcher's own output\n * is not waited for, so nothing else in this result carries it.\n */\nexport function launchedRunSummary(record: WorkflowRunRecord): string {\n return [\n `Started ${record.displayName} in workspace ${record.workspace}.`,\n `Run key: ${record.runKey}`,\n 'The run is registered and going. This call returned at that point rather than waiting for the launcher process to exit.',\n ].join('\\n');\n}\n\n/**\n * What the caller is told when the launch was handed off but no run has\n * registered inside the acknowledgement budget.\n *\n * Deliberately not an error: the launcher is slow rather than broken often\n * enough that failing here would strand runs that go on to start normally.\n * There is no run key to offer yet, so it points at the surfaces that do not\n * need one.\n */\nexport function launchHandoffSummary(workflowPath: string): string {\n return [\n `Launch handed off for ${workflowPath}, but no run has registered yet.`,\n 'Nothing was cancelled: the launcher is still starting, and a run that registers later still reports back here when it finishes.',\n 'Do not launch it again. Ask the user to check the active workflow list if they need to see it sooner.',\n ].join('\\n');\n}\n\n/**\n * Escape sequences a terminal consumes rather than prints.\n *\n * Built from a string with the rule silenced, the way `doom-runner`'s own\n * scrubber does it, because matching the escape byte is the entire job. Not\n * imported from there: that package's published build is a stale artifact\n * missing the export, and a launch notice should not need another package's\n * build to be current.\n */\n// oxlint-disable-next-line no-control-regex -- matching terminal control bytes is the point\nconst ESCAPE_SEQUENCE = new RegExp(\n ['\\\\u001B\\\\[[0-9;?]*[ -/]*[@-~]', '\\\\u001B\\\\][^\\\\u0007\\\\u001B]*(?:\\\\u0007|\\\\u001B\\\\\\\\)', '\\\\u001B[@-Z\\\\\\\\-_]'].join(\n '|',\n ),\n 'g',\n);\n\n/** Rows made only of box-drawing: separators that carry nothing once trimmed. */\nconst RULE_ROW = /^[\\s─-╿]*$/u;\nconst NOTICE_LINE_LIMIT = 3;\n/**\n * Widest a notice row may be before it is clipped.\n *\n * A three-line cap does not bound a toast on its own: the engine announces its\n * delegation by echoing the whole shell command, temp script path included, and\n * one line of that wraps across three rows of a narrow terminal and shoves the\n * transcript around. Clipped at a width every terminal can hold instead.\n */\nconst NOTICE_WIDTH_LIMIT = 100;\n\n/**\n * Cut a tool result down to something a toast can hold.\n *\n * The launch result used to reach `ui.notify` whole, and for a workflow that\n * executes in this process that result is the engine's entire console log:\n * banner, run directory, job tree, rules, colour. Pi rendered every line of it\n * over the transcript, which reads as a broken screen rather than as output.\n * Escape sequences go first: the log carries colour the notification would\n * render as literal text, and a stray clear or cursor-move would do to the\n * screen exactly what this function exists to prevent.\n */\nexport function launchNotice(text: string, limit: number = NOTICE_LINE_LIMIT): string {\n const lines = text\n .replace(ESCAPE_SEQUENCE, '')\n .split('\\n')\n .map((line) => line.trimEnd())\n .filter((line) => line.trim().length > 0 && !RULE_ROW.test(line))\n .map((line) => (line.length > NOTICE_WIDTH_LIMIT ? `${line.slice(0, NOTICE_WIDTH_LIMIT - 1)}…` : line));\n if (lines.length === 0) return 'Workflow started.';\n if (lines.length <= limit) return lines.join('\\n');\n return [...lines.slice(0, limit), `… ${lines.length - limit} more lines, in the run's own output.`].join('\\n');\n}\n\n/** Flatten MCP tool content to text for Pi. */\nexport function toolResultText(result: CallToolResult): string {\n return result.content\n .map((part) => (part.type === 'text' ? part.text : ''))\n .filter(Boolean)\n .join('\\n')\n .trim();\n}\n\n/**\n * Convert an MCP tool result into a Pi tool result.\n *\n * Pi signals tool failure by throwing: the agent loop catches it and builds the\n * error result. So an `isError` MCP result becomes a thrown error rather than a\n * successful result carrying a flag Pi would ignore.\n */\nexport function toAgentToolResult(\n tool: string,\n result: CallToolResult,\n errorOptions: string[] = [],\n): AgentToolResult<WorkflowToolDetails> {\n const text = toolResultText(result) || 'No output.';\n if (result.isError) throw new Error(withOptions(text, errorOptions));\n return { content: [{ type: 'text', text }], details: { tool } };\n}\n\n/**\n * Attach a short menu of next steps to a problem.\n *\n * An error with no route forward is where an agent invents one: retrying a\n * capacity limit, relaunching a run that should be recovered, or silently\n * giving up on work the user is waiting for. Naming two or three real options\n * turns an error into a decision, and the decision belongs to the user.\n */\nexport function withOptions(problem: string, options: string[]): string {\n if (options.length === 0) return problem;\n return [problem, '', 'Options, put these to the user rather than picking one yourself:', ...options.map(bullet)].join(\n '\\n',\n );\n}\n\nfunction bullet(option: string): string {\n return `- ${option}`;\n}\n\n/** The job and step a run died on, preferring the progress log to the record. */\nfunction failurePosition(\n record: WorkflowRunRecord,\n jobs: WorkflowProgressJob[],\n): { job?: string; step?: string; reason?: string } {\n // The progress log knows which step was in flight; the record only names the\n // job. Falling back keeps this working for runs that predate the log.\n const failedJob = jobs.find((job) => job.status === 'failed');\n const failedStep = failedJob?.steps.find((step) => step.status === 'failed');\n return {\n job: failedJob?.name ?? record.failedJob,\n step: failedStep?.name,\n reason: failedStep?.reason ?? failedJob?.reason,\n };\n}\n\n/**\n * Describe a finished run for the agent that launched it.\n *\n * Carries the job identifiers back so a dispatching agent can match the run to\n * the work item it launched it for without another lookup. When the run failed,\n * carries the failing step and a set of options too: the agent is being told\n * about this out of band, with no user question in flight, so without them it\n * has to guess whether to recover, relaunch, or report.\n */\nexport function finishedRunSummary(record: WorkflowRunRecord, jobs: WorkflowProgressJob[] = []): string {\n const outcome = record.stage === 'completed' ? 'completed' : `ended in ${record.stage}`;\n const failure = failurePosition(record, jobs);\n const lines: Array<string | undefined> = [\n `Workflow run ${record.runKey} in workspace ${record.workspace} ${outcome}.`,\n record.workflowId ? `Workflow: ${record.workflowId}` : undefined,\n failure.job ? `Failed job: ${failure.job}${failure.step ? ` (step: ${failure.step})` : ''}` : undefined,\n record.errorMessage ? `Error: ${record.errorMessage}` : undefined,\n failure.reason && failure.reason !== record.errorMessage ? `Step reported: ${failure.reason}` : undefined,\n ];\n const jobId = record.env?.AGIFLOW_JOB_ID;\n if (jobId) lines.push(`Agiflow job: ${record.env?.AGIFLOW_JOB_KIND ?? 'unknown'} ${jobId}`);\n\n const summary = lines.filter((line): line is string => Boolean(line)).join('\\n');\n if (record.stage === 'completed') return summary;\n\n // A run the user stopped is not a defect, so it gets no diagnosis step: the\n // question is only whether to pick it back up.\n const stopped = record.outcome === 'interrupted';\n if (stopped) {\n return withOptions(summary, [\n `workflow_run {\"action\":\"recover\",\"runKey\":${JSON.stringify(record.runKey)}}: pick it up where it stopped.`,\n 'Leave it stopped and move on.',\n ]);\n }\n\n const diagnosticText = [record.errorMessage, failure.reason].filter(Boolean).join('\\n');\n if (diagnosticText.includes('JOB_ALREADY_CLAIMED')) {\n return withOptions(summary, [\n 'Report contention: another worker owns this job. Do not unlock, release, or retry the claim automatically.',\n ]);\n }\n if (diagnosticText.includes('WORKFLOW_NOT_OWNED') || /release failed/i.test(diagnosticText)) {\n return withOptions(summary, [\n 'Inspect the current workflow ownership and ask the user before any release or unlock. Never force another worker’s lock.',\n ]);\n }\n if (/running workflow not found/i.test(diagnosticText)) {\n return withOptions(summary, [\n `workflow_run {\"action\":\"status\",\"runKey\":${JSON.stringify(record.runKey)}}: re-check current state before retrying.`,\n 'Report the recorded terminal state when no running owner remains.',\n ]);\n }\n\n return withOptions(summary, [\n `workflow_run {\"action\":\"tail\",\"runKey\":${JSON.stringify(record.runKey)}}: read the run's own output first, when the error above does not explain the failure.`,\n `workflow_run {\"action\":\"recover\",\"runKey\":${JSON.stringify(record.runKey)}}: resume from the failed job, keeping the work already done. Fits a transient or external cause.`,\n 'Report the failure and stop, when it needs a code change or a decision you cannot make.',\n ]);\n}\n"],"mappings":"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"}
@@ -1,11 +0,0 @@
1
- const e=`PI_SESSION_ID`;function t(e=process.env){let t=Number(e.WORKFLOW_MCP_MAX_CONCURRENT);return Number.isInteger(t)&&t>0?t:5}function n(e,t){return t!==void 0&&e.env?.PI_SESSION_ID===t}function r(e,t){return e.filter(e=>n(e,t)&&e.stage===`running`)}function i(e){return[`Started ${e.displayName} in workspace ${e.workspace}.`,`Run key: ${e.runKey}`,`The run is registered and going. This call returned at that point rather than waiting for the launcher process to exit.`].join(`
2
- `)}function a(e){return[`Launch handed off for ${e}, but no run has registered yet.`,`Nothing was cancelled: the launcher is still starting, and a run that registers later still reports back here when it finishes.`,`Do not launch it again. Ask the user to check the active workflow list if they need to see it sooner.`].join(`
3
- `)}const o=new RegExp([`\\u001B\\[[0-9;?]*[ -/]*[@-~]`,`\\u001B\\][^\\u0007\\u001B]*(?:\\u0007|\\u001B\\\\)`,`\\u001B[@-Z\\\\-_]`].join(`|`),`g`),s=/^[\s─-╿]*$/u;function c(e,t=3){let n=e.replace(o,``).split(`
4
- `).map(e=>e.trimEnd()).filter(e=>e.trim().length>0&&!s.test(e)).map(e=>e.length>100?`${e.slice(0,99)}…`:e);return n.length===0?`Workflow started.`:n.length<=t?n.join(`
5
- `):[...n.slice(0,t),`… ${n.length-t} more lines, in the run's own output.`].join(`
6
- `)}function l(e){return e.content.map(e=>e.type===`text`?e.text:``).filter(Boolean).join(`
7
- `).trim()}function u(e,t,n=[]){let r=l(t)||`No output.`;if(t.isError)throw Error(d(r,n));return{content:[{type:`text`,text:r}],details:{tool:e}}}function d(e,t){return t.length===0?e:[e,``,`Options, put these to the user rather than picking one yourself:`,...t.map(f)].join(`
8
- `)}function f(e){return`- ${e}`}function p(e,t){let n=t.find(e=>e.status===`failed`),r=n?.steps.find(e=>e.status===`failed`);return{job:n?.name??e.failedJob,step:r?.name,reason:r?.reason??n?.reason}}function m(e,t=[]){let n=e.stage===`completed`?`completed`:`ended in ${e.stage}`,r=p(e,t),i=[`Workflow run ${e.runKey} in workspace ${e.workspace} ${n}.`,e.workflowId?`Workflow: ${e.workflowId}`:void 0,r.job?`Failed job: ${r.job}${r.step?` (step: ${r.step})`:``}`:void 0,e.errorMessage?`Error: ${e.errorMessage}`:void 0,r.reason&&r.reason!==e.errorMessage?`Step reported: ${r.reason}`:void 0],a=e.env?.AGIFLOW_JOB_ID;a&&i.push(`Agiflow job: ${e.env?.AGIFLOW_JOB_KIND??`unknown`} ${a}`);let o=i.filter(e=>!!e).join(`
9
- `);if(e.stage===`completed`)return o;if(e.outcome===`interrupted`)return d(o,[`workflow_run {"action":"recover","runKey":${JSON.stringify(e.runKey)}}: pick it up where it stopped.`,`Leave it stopped and move on.`]);let s=[e.errorMessage,r.reason].filter(Boolean).join(`
10
- `);return s.includes(`JOB_ALREADY_CLAIMED`)?d(o,[`Report contention: another worker owns this job. Do not unlock, release, or retry the claim automatically.`]):s.includes(`WORKFLOW_NOT_OWNED`)||/release failed/i.test(s)?d(o,[`Inspect the current workflow ownership and ask the user before any release or unlock. Never force another worker’s lock.`]):/running workflow not found/i.test(s)?d(o,[`workflow_run {"action":"status","runKey":${JSON.stringify(e.runKey)}}: re-check current state before retrying.`,`Report the recorded terminal state when no running owner remains.`]):d(o,[`workflow_run {"action":"tail","runKey":${JSON.stringify(e.runKey)}}: read the run's own output first, when the error above does not explain the failure.`,`workflow_run {"action":"recover","runKey":${JSON.stringify(e.runKey)}}: resume from the failed job, keeping the work already done. Fits a transient or external cause.`,`Report the failure and stop, when it needs a code change or a decision you cannot make.`])}export{e as PI_SESSION_ENV,m as finishedRunSummary,n as isSessionRun,a as launchHandoffSummary,c as launchNotice,i as launchedRunSummary,t as resolveMaxConcurrent,r as runsForSession,u as toAgentToolResult,l as toolResultText,d as withOptions};
11
- //# sourceMappingURL=index.mjs.map