@agimon-ai/doompi-workflow 0.0.1-alpha.58 → 0.0.1-alpha.64
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.
- package/dist/adapters/pi/extension.cjs +1 -1
- package/dist/adapters/pi/extension.cjs.map +1 -1
- package/dist/adapters/pi/extension.d.cts +2 -2
- package/dist/adapters/pi/extension.d.cts.map +1 -1
- package/dist/adapters/pi/extension.d.mts +2 -2
- package/dist/adapters/pi/extension.d.mts.map +1 -1
- package/dist/adapters/pi/extension.mjs +1 -1
- package/dist/adapters/pi/extension.mjs.map +1 -1
- package/dist/adapters/pi/workflow/piExtension.cjs +1 -1
- package/dist/adapters/pi/workflow/piExtension.cjs.map +1 -1
- package/dist/adapters/pi/workflow/piExtension.mjs +1 -1
- package/dist/adapters/pi/workflow/piExtension.mjs.map +1 -1
- package/dist/adapters/workflowCatalogChannel.d.cts +3 -4
- package/dist/adapters/workflowCatalogChannel.d.cts.map +1 -1
- package/dist/adapters/workflowCatalogChannel.d.mts +3 -4
- package/dist/adapters/workflowCatalogChannel.d.mts.map +1 -1
- package/dist/adapters/workflowHubApi.cjs +1 -1
- package/dist/adapters/workflowHubApi.cjs.map +1 -1
- package/dist/adapters/workflowHubApi.d.cts +3 -4
- package/dist/adapters/workflowHubApi.d.cts.map +1 -1
- package/dist/adapters/workflowHubApi.d.mts +3 -4
- package/dist/adapters/workflowHubApi.d.mts.map +1 -1
- package/dist/adapters/workflowHubApi.mjs +1 -1
- package/dist/adapters/workflowHubApi.mjs.map +1 -1
- package/dist/adapters/workflowWatcher.d.cts +3 -4
- package/dist/adapters/workflowWatcher.d.cts.map +1 -1
- package/dist/adapters/workflowWatcher.d.mts +3 -4
- package/dist/adapters/workflowWatcher.d.mts.map +1 -1
- package/dist/adapters/workflowsHubChannel.d.cts +3 -4
- package/dist/adapters/workflowsHubChannel.d.cts.map +1 -1
- package/dist/adapters/workflowsHubChannel.d.mts +3 -4
- package/dist/adapters/workflowsHubChannel.d.mts.map +1 -1
- package/dist/services/webWorkflowCatalog.d.cts +4 -5
- package/dist/services/webWorkflowCatalog.d.cts.map +1 -1
- package/dist/services/webWorkflowCatalog.d.mts +4 -5
- package/dist/services/webWorkflowCatalog.d.mts.map +1 -1
- package/dist/services/workflowNarration.cjs +1 -1
- package/dist/services/workflowNarration.cjs.map +1 -1
- package/dist/services/workflowNarration.mjs +1 -1
- package/dist/services/workflowNarration.mjs.map +1 -1
- package/dist/services/workflowRuns.d.cts +1 -2
- package/dist/services/workflowRuns.d.cts.map +1 -1
- package/dist/services/workflowRuns.d.mts +1 -2
- package/dist/services/workflowRuns.d.mts.map +1 -1
- package/dist/tui/workflow/workflowInspector.cjs +1 -1
- package/dist/tui/workflow/workflowInspector.cjs.map +1 -1
- package/dist/tui/workflow/workflowInspector.mjs +1 -1
- package/dist/tui/workflow/workflowInspector.mjs.map +1 -1
- package/dist/tui/workflow/workflowOverlay.cjs +1 -1
- package/dist/tui/workflow/workflowOverlay.cjs.map +1 -1
- package/dist/tui/workflow/workflowOverlay.mjs +1 -1
- package/dist/tui/workflow/workflowOverlay.mjs.map +1 -1
- package/dist/tui/workflow/workflowToolRender.cjs +1 -1
- package/dist/tui/workflow/workflowToolRender.cjs.map +1 -1
- package/dist/tui/workflow/workflowToolRender.mjs +1 -1
- package/dist/tui/workflow/workflowToolRender.mjs.map +1 -1
- package/dist/types/webWorkflows.d.cts +11 -12
- package/dist/types/webWorkflows.d.cts.map +1 -1
- package/dist/types/webWorkflows.d.mts +11 -12
- package/dist/types/webWorkflows.d.mts.map +1 -1
- package/package.json +18 -18
- package/src/web/{terminalApi.ts → api/terminalApi.ts} +1 -1
- package/src/web/{ArtifactsPane.tsx → components/ArtifactsPane.tsx} +3 -3
- package/src/web/{LaunchWorkflowDialog.tsx → components/LaunchWorkflowDialog.tsx} +3 -3
- package/src/web/{StepTerminalPanel.tsx → components/StepTerminalPanel.tsx} +4 -4
- package/src/web/{WorkflowCatalogDrawer.tsx → components/WorkflowCatalogDrawer.tsx} +2 -2
- package/src/web/{WorkflowToolMessage.tsx → components/WorkflowToolMessage.tsx} +1 -1
- package/src/web/{WorkflowsActivitySection.tsx → components/WorkflowsActivitySection.tsx} +3 -3
- package/src/web/{WorkflowsPanel.tsx → components/WorkflowsPanel.tsx} +7 -7
- package/src/web/index.ts +5 -5
- package/src/web/{launchLine.ts → lib/launchLine.ts} +1 -1
- package/src/web/{workflowActivity.ts → lib/workflowActivity.ts} +1 -1
- package/src/web/{catalogStore.ts → stores/catalogStore.ts} +1 -1
- package/src/web/{workflowsStore.ts → stores/workflowsStore.ts} +2 -2
- /package/src/web/{runDuration.ts → lib/runDuration.ts} +0 -0
- /package/src/web/{workflowToolRender.ts → lib/workflowToolRender.ts} +0 -0
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require("./leader.cjs"),t=require("./workflow/piExtension.cjs");let n=require("node:fs"),r=require("node:path"),i=require("node:async_hooks"),a=require("node:url"),o=require("@agimon-ai/doompi-extension-contracts/child-process"),s=require("@agimon-ai/doompi-extension-contracts/cordis-host"),c=require("@agimon-ai/doompi-extension-contracts/help"),l=require("@agimon-ai/doompi-extension-contracts/skills"),u=require("@agimon-ai/doompi-extension-contracts/ui-hub");const d=`@agimon-ai/doompi-workflow`,f=`agiflow-dispatcher`,p=`The workflow runtime is no longer active.`,m=Symbol(`doom-workflow-cleanup`),h=new Set([`list_workflows`,`launch_workflow`]),g=new Set([`launch_workflow`]);function _(e,t){let n=Reflect.get(e,t,e);return typeof n==`function`?n.bind(e):n}function v(e,t){let n=new Proxy(e.sessionManager,{get(e,n){return n===`getSessionId`?()=>t:_(e,n)}});return new Proxy(e,{get(e,t){return t===`sessionManager`?n:_(e,t)}})}function y(e,t,n=process.env){let r=t?(0,o.resolveRootSessionId)(t,n):void 0;return new Proxy(e,{get(e,t){return t===`setActiveTools`?t=>e.setActiveTools(t.filter(e=>e!==`workflow_run`)):t===`registerTool`?t=>{if(h.has(t.name)){if(!r||!g.has(t.name)){e.registerTool(t);return}e.registerTool({...t,execute:(e,n,i,a,o)=>t.execute(e,n,i,a,v(o,r))})}}:_(e,t)}})}function b(e){return(0,o.readChildProcessContext)(e)?.parentSessionId??(e[o.SUBAGENT_PARENT_SESSION_ENV]?.trim()||void 0)}function x(e){return(0,o.readChildProcessContext)(e)?.mode===f||e.PI_SUBAGENT_CHILD_AGENT===f&&!!e[o.SUBAGENT_PARENT_SESSION_ENV]?.trim()}function S(){return{content:[{type:`text`,text:p}],details:void 0}}function C(e){let t=new i.AsyncLocalStorage,n=!0,r=!1,a=0,o,s,c=()=>{if(!n)return!1;let e=t.getStore();return e===void 0||e===m||e===a},l=e=>{if(!n)return!1;let r=t.getStore();return r===m||e===a&&(r===void 0||r===e)},u=(e,t)=>{let n=new Proxy(e.ui,{get(e,n){let r=Reflect.get(e,n,e);return typeof r==`function`?(...i)=>{if(l(t))return Reflect.apply(r,e,i);if(n===`confirm`)return Promise.resolve(!1);if(n===`custom`||n===`editor`||n===`input`||n===`select`)return Promise.resolve(void 0)}:r}});return new Proxy(e,{get(e,t){return t===`ui`?n:_(e,t)}})},d=i=>{c()&&e.registerTool({...i,execute:async(e,o,s,c,l)=>{let d=a;if(!n||r||s?.aborted)return S();let f=u(l,d),p=c?e=>{n&&d===a&&!s?.aborted&&c(e)}:void 0,m=await t.run(d,()=>i.execute(e,o,s,p,f));return n&&!r&&d===a?m:S()}})},f=(i,o)=>{c()&&e.registerCommand(i,{...o,handler:async(e,i)=>{let s=a
|
|
1
|
+
const e=require("./leader.cjs"),t=require("./workflow/piExtension.cjs");let n=require("node:fs"),r=require("node:path"),i=require("node:async_hooks"),a=require("node:url"),o=require("@agimon-ai/doompi-extension-contracts/child-process"),s=require("@agimon-ai/doompi-extension-contracts/cordis-host"),c=require("@agimon-ai/doompi-extension-contracts/help"),l=require("@agimon-ai/doompi-extension-contracts/skills"),u=require("@agimon-ai/doompi-extension-contracts/ui-hub");const d=`@agimon-ai/doompi-workflow`,f=`agiflow-dispatcher`,p=`The workflow runtime is no longer active.`,m=Symbol(`doom-workflow-cleanup`),h=new Set([`list_workflows`,`launch_workflow`]),g=new Set([`launch_workflow`]);function _(e,t){let n=Reflect.get(e,t,e);return typeof n==`function`?n.bind(e):n}function v(e,t){let n=new Proxy(e.sessionManager,{get(e,n){return n===`getSessionId`?()=>t:_(e,n)}});return new Proxy(e,{get(e,t){return t===`sessionManager`?n:_(e,t)}})}function y(e,t,n=process.env){let r=t?(0,o.resolveRootSessionId)(t,n):void 0;return new Proxy(e,{get(e,t){return t===`setActiveTools`?t=>e.setActiveTools(t.filter(e=>e!==`workflow_run`)):t===`registerTool`?t=>{if(h.has(t.name)){if(!r||!g.has(t.name)){e.registerTool(t);return}e.registerTool({...t,execute:(e,n,i,a,o)=>t.execute(e,n,i,a,v(o,r))})}}:_(e,t)}})}function b(e){return(0,o.readChildProcessContext)(e)?.parentSessionId??(e[o.SUBAGENT_PARENT_SESSION_ENV]?.trim()||void 0)}function x(e){return(0,o.readChildProcessContext)(e)?.mode===f||e.PI_SUBAGENT_CHILD_AGENT===f&&!!e[o.SUBAGENT_PARENT_SESSION_ENV]?.trim()}function S(){return{content:[{type:`text`,text:p}],details:void 0}}function C(e){let t=new i.AsyncLocalStorage,n=!0,r=!1,a=0,o,s,c=()=>{if(!n)return!1;let e=t.getStore();return e===void 0||e===m||e===a},l=e=>{if(!n)return!1;let r=t.getStore();return r===m||e===a&&(r===void 0||r===e)},u=(e,t)=>{let n=new Proxy(e.ui,{get(e,n){let r=Reflect.get(e,n,e);return typeof r==`function`?(...i)=>{if(l(t))return Reflect.apply(r,e,i);if(n===`confirm`)return Promise.resolve(!1);if(n===`custom`||n===`editor`||n===`input`||n===`select`)return Promise.resolve(void 0)}:r}});return new Proxy(e,{get(e,t){return t===`ui`?n:_(e,t)}})},d=i=>{c()&&e.registerTool({...i,execute:async(e,o,s,c,l)=>{let d=a;if(!n||r||s?.aborted)return S();let f=u(l,d),p=c?e=>{n&&d===a&&!s?.aborted&&c(e)}:void 0,m=await t.run(d,()=>i.execute(e,o,s,p,f));return n&&!r&&d===a?m:S()}})},f=(i,o)=>{c()&&e.registerCommand(i,{...o,handler:async(e,i)=>{let s=a;n&&!r&&await t.run(s,()=>o.handler(e,u(i,s)))}})},h=(i,o)=>{c()&&e.registerShortcut(i,{...o,handler:e=>{let i=a;if(n&&!r)return t.run(i,()=>o.handler(u(e,i)))}})};return{pi:new Proxy(e,{get(e,i){return i===`on`?(i,c)=>{_(e,`on`)(i,(e,l)=>{if(!n||r)return;i===`session_start`&&(e!==o||l!==s)&&(a+=1,o=e,s=l);let d=a;return t.run(d,()=>c(e,u(l,d)))})}:i===`registerTool`?d:i===`registerCommand`?f:i===`registerShortcut`?h:i===`getActiveTools`?()=>c()?e.getActiveTools():[]:i===`setActiveTools`?t=>{c()&&e.setActiveTools(t)}:i===`sendMessage`?(...t)=>{c()&&e.sendMessage(...t)}:i===`sendUserMessage`?(...t)=>{c()&&e.sendUserMessage(...t)}:i===`exec`?(...t)=>c()?e.exec(...t):Promise.resolve({code:1,killed:!1,stderr:p,stdout:``}):_(e,i)}}),beginDisposal(){!r&&n&&(r=!0,a+=1)},finishDisposal(){n=!1,r=!1,a+=1},isCurrentInvocation:c,runCleanup:e=>t.run(m,e)}}function w(i,o,s,f=process.env){let p=x(f),m=p?b(f):void 0,h=p?y(o,m,f):o,g;if(i.effect(()=>()=>g?g.dispose():Promise.resolve(),`${d}/runtime`),i.inject([l.DOOM_SKILL_SOURCES_SERVICE],e=>{let t=(0,r.resolve)((0,r.dirname)((0,a.fileURLToPath)(require("url").pathToFileURL(__filename).href)),`../../../skills`);if(!(0,n.existsSync)(t))return;let i=(0,l.requireDoomSkillSourcesService)(e).register({source:d,directories:[t]});return()=>i.dispose()}),p){g=t.installWorkflowPiRuntime({cordis:i,initialMode:!0,isActive:s})(h);return}let _=!1,v;i.inject([u.DOOM_UI_HUB_SERVICE],t=>{let n=e.registerLeaderContribution((0,u.requireDoomUiHub)(t),_);return v=n,()=>{n.dispose(),v===n&&(v=void 0)}}),i.inject([c.DOOM_HELP_SERVICE],e=>{let t=(0,c.requireDoomHelpService)(e).register({source:d,moduleUrl:require("url").pathToFileURL(__filename).href,skills:[{name:`doompi-author-workflow`,description:`Author DoomPi workflow definitions. Use when creating or changing a *.workflow.yml graph, arranging job dependencies and host-executed steps, or deciding how a command requiring a TTY should run.`},{name:`doompi-use-workflow`,description:`Use DoomPi workflows. Use when discovering or launching workflows, monitoring or controlling asynchronous runs, interpreting terminal notifications, or recovering a failed run safely.`}]});return()=>t.dispose()}),g=t.installWorkflowPiRuntime({cordis:i,isActive:s,onModeChange:e=>{_=e,v?.setMode(e)}})(h)}function T(e,{fence:t,environment:n}){w(e,t.pi,t.isCurrentInvocation,n)}async function E(e){let t=await(0,s.connectDoomCordisHost)(e,d),n=C(e),r=t.root.plugin(T,{fence:n,environment:process.env}),i,a=()=>i||(n.beginDisposal(),i=n.runCleanup(async()=>{try{await r.dispose()}finally{try{n.finishDisposal()}finally{await t.dispose()}}}),i);try{await r}catch(e){throw await a(),e}e.on(`session_shutdown`,a)}exports.createDispatcherBridge=y,exports.default=E,exports.workflowExtension=E,exports.installWorkflowRuntime=w,exports.isWorkflowDispatcherProcess=x,exports.resolveDispatcherParentSession=b;
|
|
2
2
|
//# sourceMappingURL=extension.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extension.cjs","names":["resolveRootSessionId","readChildProcessContext","SUBAGENT_PARENT_SESSION_ENV","AsyncLocalStorage","DOOM_SKILL_SOURCES_SERVICE","resolve","dirname","fileURLToPath","existsSync","requireDoomSkillSourcesService","installWorkflowPiRuntime","DOOM_UI_HUB_SERVICE","registerLeaderContribution","requireDoomUiHub","DOOM_HELP_SERVICE","requireDoomHelpService","connectDoomCordisHost"],"sources":["../../../src/adapters/pi/extension.ts"],"sourcesContent":["import { AsyncLocalStorage } from 'node:async_hooks';\nimport { existsSync } from 'node:fs';\nimport { dirname, resolve } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport {\n readChildProcessContext,\n resolveRootSessionId,\n SUBAGENT_PARENT_SESSION_ENV,\n} from '@agimon-ai/doompi-extension-contracts/child-process';\nimport { connectDoomCordisHost } from '@agimon-ai/doompi-extension-contracts/cordis-host';\nimport { DOOM_HELP_SERVICE, requireDoomHelpService } from '@agimon-ai/doompi-extension-contracts/help';\nimport {\n DOOM_SKILL_SOURCES_SERVICE,\n requireDoomSkillSourcesService,\n} from '@agimon-ai/doompi-extension-contracts/skills';\nimport { DOOM_UI_HUB_SERVICE, requireDoomUiHub } from '@agimon-ai/doompi-extension-contracts/ui-hub';\nimport type { Context } from '@deepseek-ai/cordis';\nimport type { ExtensionAPI, ExtensionContext } from '@earendil-works/pi-coding-agent';\nimport { registerLeaderContribution } from './leader.ts';\nimport { installWorkflowPiRuntime, type WorkflowPiRuntime } from './workflow/piExtension.ts';\n\nconst PACKAGE_SOURCE = '@agimon-ai/doompi-workflow';\nconst SKILL_RELATIVE_PATH = '../../../skills';\nconst DISPATCHER_AGENT_NAME = 'agiflow-dispatcher';\nconst SESSION_START_EVENT = 'session_start';\nconst SESSION_SHUTDOWN_EVENT = 'session_shutdown';\nconst INACTIVE_RUNTIME_MESSAGE = 'The workflow runtime is no longer active.';\nconst CLEANUP_SCOPE = Symbol('doom-workflow-cleanup');\n\nconst DISPATCHER_TOOL_NAMES = new Set(['list_workflows', 'launch_workflow']);\nconst ROOT_SCOPED_TOOL_NAMES = new Set(['launch_workflow']);\n\ntype RegisteredTool = Parameters<ExtensionAPI['registerTool']>[0];\ntype CommandOptions = Parameters<ExtensionAPI['registerCommand']>[1];\ntype ShortcutOptions = Parameters<ExtensionAPI['registerShortcut']>[1];\ntype RuntimeScope = number | typeof CLEANUP_SCOPE;\ntype LifecycleHandler = (event: unknown, context: ExtensionContext) => unknown;\n\ninterface WorkflowFence {\n readonly pi: ExtensionAPI;\n readonly beginDisposal: () => void;\n readonly finishDisposal: () => void;\n readonly isCurrentInvocation: () => boolean;\n readonly runCleanup: <T>(operation: () => T) => T;\n}\n\nfunction boundValue(target: object, property: PropertyKey): unknown {\n const value = Reflect.get(target, property, target);\n return typeof value === 'function' ? value.bind(target) : value;\n}\n\nfunction withParentSession(ctx: ExtensionContext, parentSessionId: string): ExtensionContext {\n const sessionManager = new Proxy(ctx.sessionManager, {\n get(target, property) {\n if (property === 'getSessionId') return () => parentSessionId;\n return boundValue(target, property);\n },\n });\n return new Proxy(ctx, {\n get(target, property) {\n if (property === 'sessionManager') return sessionManager;\n return boundValue(target, property);\n },\n });\n}\n\n/** Restrict a dispatcher child to discovery and root-owned launch. */\nexport function createDispatcherBridge(\n pi: ExtensionAPI,\n parentSessionId: string | undefined,\n environment: Readonly<Record<string, string | undefined>> = process.env,\n): ExtensionAPI {\n const rootSessionId = parentSessionId ? resolveRootSessionId(parentSessionId, environment) : undefined;\n return new Proxy(pi, {\n get(target, property) {\n if (property === 'setActiveTools') {\n return (names: string[]): void => target.setActiveTools(names.filter((name) => name !== 'workflow_run'));\n }\n if (property !== 'registerTool') return boundValue(target, property);\n return (tool: RegisteredTool): void => {\n if (!DISPATCHER_TOOL_NAMES.has(tool.name)) return;\n if (!rootSessionId || !ROOT_SCOPED_TOOL_NAMES.has(tool.name)) {\n target.registerTool(tool);\n return;\n }\n target.registerTool({\n ...tool,\n execute: (toolCallId, params, signal, onUpdate, ctx) =>\n tool.execute(toolCallId, params, signal, onUpdate, withParentSession(ctx, rootSessionId)),\n });\n };\n },\n });\n}\n\nexport function resolveDispatcherParentSession(\n environment: Readonly<Record<string, string | undefined>>,\n): string | undefined {\n const childContext = readChildProcessContext(environment);\n return childContext?.parentSessionId ?? (environment[SUBAGENT_PARENT_SESSION_ENV]?.trim() || undefined);\n}\n\nexport function isWorkflowDispatcherProcess(environment: Readonly<Record<string, string | undefined>>): boolean {\n const childContext = readChildProcessContext(environment);\n if (childContext?.mode === DISPATCHER_AGENT_NAME) return true;\n return (\n environment.PI_SUBAGENT_CHILD_AGENT === DISPATCHER_AGENT_NAME &&\n Boolean(environment[SUBAGENT_PARENT_SESSION_ENV]?.trim())\n );\n}\n\nfunction inactiveToolResult(): Awaited<ReturnType<RegisteredTool['execute']>> {\n return {\n content: [{ type: 'text', text: INACTIVE_RUNTIME_MESSAGE }],\n details: undefined,\n };\n}\n\n/** Fence every callback retained by Pi after this package root is replaced. */\nfunction createWorkflowFence(host: ExtensionAPI): WorkflowFence {\n const scope = new AsyncLocalStorage<RuntimeScope>();\n let active = true;\n let disposing = false;\n let generation = 0;\n let lastSessionStartEvent: unknown;\n let lastSessionStartContext: ExtensionContext | undefined;\n\n const isCurrentInvocation = (): boolean => {\n if (!active) return false;\n const currentScope = scope.getStore();\n return currentScope === undefined || currentScope === CLEANUP_SCOPE || currentScope === generation;\n };\n const ownsContext = (contextGeneration: number): boolean => {\n if (!active) return false;\n const currentScope = scope.getStore();\n return (\n currentScope === CLEANUP_SCOPE ||\n (contextGeneration === generation && (currentScope === undefined || currentScope === contextGeneration))\n );\n };\n\n const guardContext = <TContext extends ExtensionContext>(context: TContext, contextGeneration: number): TContext => {\n const guardedUi = new Proxy(context.ui, {\n get(target, property) {\n const value = Reflect.get(target, property, target);\n if (typeof value !== 'function') return value;\n return (...args: unknown[]): unknown => {\n if (ownsContext(contextGeneration)) return Reflect.apply(value, target, args);\n if (property === 'confirm') return Promise.resolve(false);\n if (property === 'custom' || property === 'editor' || property === 'input' || property === 'select') {\n return Promise.resolve(undefined);\n }\n return undefined;\n };\n },\n });\n return new Proxy(context, {\n get(target, property) {\n if (property === 'ui') return guardedUi;\n return boundValue(target, property);\n },\n }) as TContext;\n };\n\n const registerTool = (tool: RegisteredTool): void => {\n if (!isCurrentInvocation()) return;\n host.registerTool({\n ...tool,\n execute: async (toolCallId, params, signal, onUpdate, context) => {\n const executionGeneration = generation;\n if (!active || disposing || signal?.aborted) return inactiveToolResult();\n const guardedContext = guardContext(context, executionGeneration);\n const guardedUpdate = onUpdate\n ? (update: Parameters<NonNullable<typeof onUpdate>>[0]): void => {\n if (active && executionGeneration === generation && !signal?.aborted) onUpdate(update);\n }\n : undefined;\n const result = await scope.run(executionGeneration, () =>\n tool.execute(toolCallId, params, signal, guardedUpdate, guardedContext),\n );\n return active && !disposing && executionGeneration === generation ? result : inactiveToolResult();\n },\n });\n };\n\n const registerCommand = (name: string, options: CommandOptions): void => {\n if (!isCurrentInvocation()) return;\n host.registerCommand(name, {\n ...options,\n handler: async (args, context) => {\n const executionGeneration = generation;\n if (!active || disposing) return;\n await scope.run(executionGeneration, () => options.handler(args, guardContext(context, executionGeneration)));\n },\n });\n };\n\n const registerShortcut = (\n shortcut: Parameters<ExtensionAPI['registerShortcut']>[0],\n options: ShortcutOptions,\n ): void => {\n if (!isCurrentInvocation()) return;\n host.registerShortcut(shortcut, {\n ...options,\n handler: (context) => {\n const executionGeneration = generation;\n if (!active || disposing) return;\n return scope.run(executionGeneration, () => options.handler(guardContext(context, executionGeneration)));\n },\n });\n };\n\n const guardedPi = new Proxy(host, {\n get(target, property) {\n if (property === 'on') {\n return (eventName: string, handler: LifecycleHandler): void => {\n const registerLifecycleHandler = boundValue(target, 'on') as (\n name: string,\n callback: LifecycleHandler,\n ) => void;\n registerLifecycleHandler(eventName, (event: unknown, context: ExtensionContext) => {\n if (!active || disposing) return undefined;\n if (\n eventName === SESSION_START_EVENT &&\n (event !== lastSessionStartEvent || context !== lastSessionStartContext)\n ) {\n generation += 1;\n lastSessionStartEvent = event;\n lastSessionStartContext = context;\n }\n const eventGeneration = generation;\n return scope.run(eventGeneration, () => handler(event, guardContext(context, eventGeneration)));\n });\n };\n }\n if (property === 'registerTool') return registerTool;\n if (property === 'registerCommand') return registerCommand;\n if (property === 'registerShortcut') return registerShortcut;\n if (property === 'getActiveTools') {\n return (): string[] => (isCurrentInvocation() ? target.getActiveTools() : []);\n }\n if (property === 'setActiveTools') {\n return (names: string[]): void => {\n if (isCurrentInvocation()) target.setActiveTools(names);\n };\n }\n if (property === 'sendMessage') {\n return (...args: Parameters<ExtensionAPI['sendMessage']>): void => {\n if (isCurrentInvocation()) target.sendMessage(...args);\n };\n }\n if (property === 'sendUserMessage') {\n return (...args: Parameters<ExtensionAPI['sendUserMessage']>): void => {\n if (isCurrentInvocation()) target.sendUserMessage(...args);\n };\n }\n if (property === 'exec') {\n return (...args: Parameters<ExtensionAPI['exec']>): ReturnType<ExtensionAPI['exec']> =>\n isCurrentInvocation()\n ? target.exec(...args)\n : Promise.resolve({ code: 1, killed: false, stderr: INACTIVE_RUNTIME_MESSAGE, stdout: '' });\n }\n return boundValue(target, property);\n },\n });\n\n return {\n pi: guardedPi,\n beginDisposal() {\n if (disposing || !active) return;\n disposing = true;\n generation += 1;\n },\n finishDisposal() {\n active = false;\n disposing = false;\n generation += 1;\n },\n isCurrentInvocation,\n runCleanup: (operation) => scope.run(CLEANUP_SCOPE, operation),\n };\n}\n\n/** Install one normal or dispatcher runtime into its host-owned Cordis plugin fiber. */\nexport function installWorkflowRuntime(\n cordis: Context,\n pi: ExtensionAPI,\n isCurrentInvocation: () => boolean,\n environment: Readonly<Record<string, string | undefined>> = process.env,\n): void {\n const dispatcher = isWorkflowDispatcherProcess(environment);\n const parentSessionId = dispatcher ? resolveDispatcherParentSession(environment) : undefined;\n const runtimePi = dispatcher ? createDispatcherBridge(pi, parentSessionId, environment) : pi;\n\n let runtime: WorkflowPiRuntime | undefined;\n cordis.effect(() => () => (runtime ? runtime.dispose() : Promise.resolve()), `${PACKAGE_SOURCE}/runtime`);\n\n cordis.inject([DOOM_SKILL_SOURCES_SERVICE], (skillContext) => {\n const directory = resolve(dirname(fileURLToPath(import.meta.url)), SKILL_RELATIVE_PATH);\n if (!existsSync(directory)) return undefined;\n const contribution = requireDoomSkillSourcesService(skillContext).register({\n source: PACKAGE_SOURCE,\n directories: [directory],\n });\n return () => contribution.dispose();\n });\n\n if (dispatcher) {\n runtime = installWorkflowPiRuntime({ cordis, initialMode: true, isActive: isCurrentInvocation })(runtimePi);\n return;\n }\n\n let workflowMode = false;\n let leader: ReturnType<typeof registerLeaderContribution> | undefined;\n cordis.inject([DOOM_UI_HUB_SERVICE], (uiContext) => {\n const contribution = registerLeaderContribution(requireDoomUiHub(uiContext), workflowMode);\n leader = contribution;\n return () => {\n contribution.dispose();\n if (leader === contribution) leader = undefined;\n };\n });\n cordis.inject([DOOM_HELP_SERVICE], (helpContext) => {\n const help = requireDoomHelpService(helpContext).register({\n source: PACKAGE_SOURCE,\n moduleUrl: import.meta.url,\n skills: [\n {\n name: 'doompi-author-workflow',\n description:\n 'Author DoomPi workflow definitions. Use when creating or changing a *.workflow.yml graph, arranging job dependencies and host-executed steps, or deciding how a command requiring a TTY should run.',\n },\n {\n name: 'doompi-use-workflow',\n description:\n 'Use DoomPi workflows. Use when discovering or launching workflows, monitoring or controlling asynchronous runs, interpreting terminal notifications, or recovering a failed run safely.',\n },\n ],\n });\n return () => help.dispose();\n });\n runtime = installWorkflowPiRuntime({\n cordis,\n isActive: isCurrentInvocation,\n onModeChange: (enabled) => {\n workflowMode = enabled;\n leader?.setMode(enabled);\n },\n })(runtimePi);\n}\n\ninterface WorkflowPluginConfig {\n readonly fence: WorkflowFence;\n readonly environment: Readonly<Record<string, string | undefined>>;\n}\n\nfunction workflowPlugin(cordis: Context, { fence, environment }: WorkflowPluginConfig): void {\n installWorkflowRuntime(cordis, fence.pi, fence.isCurrentInvocation, environment);\n}\n\n/** The package's sole Pi factory; Pi reloads it and Cordis owns all package resources. */\nexport async function workflowExtension(pi: ExtensionAPI): Promise<void> {\n const connection = await connectDoomCordisHost(pi, PACKAGE_SOURCE);\n const fence = createWorkflowFence(pi);\n const fiber = connection.root.plugin(workflowPlugin, { fence, environment: process.env });\n let disposal: Promise<void> | undefined;\n const dispose = (): Promise<void> => {\n if (disposal) return disposal;\n fence.beginDisposal();\n disposal = fence.runCleanup(async () => {\n try {\n await fiber.dispose();\n } finally {\n try {\n fence.finishDisposal();\n } finally {\n await connection.dispose();\n }\n }\n });\n return disposal;\n };\n\n try {\n await fiber;\n } catch (error) {\n await dispose();\n throw error;\n }\n pi.on(SESSION_SHUTDOWN_EVENT, dispose);\n}\n\nexport default workflowExtension;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"extension.cjs","names":["resolveRootSessionId","readChildProcessContext","SUBAGENT_PARENT_SESSION_ENV","AsyncLocalStorage","DOOM_SKILL_SOURCES_SERVICE","resolve","dirname","fileURLToPath","existsSync","requireDoomSkillSourcesService","installWorkflowPiRuntime","DOOM_UI_HUB_SERVICE","registerLeaderContribution","requireDoomUiHub","DOOM_HELP_SERVICE","requireDoomHelpService","connectDoomCordisHost"],"sources":["../../../src/adapters/pi/extension.ts"],"sourcesContent":["import { AsyncLocalStorage } from 'node:async_hooks';\nimport { existsSync } from 'node:fs';\nimport { dirname, resolve } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport {\n readChildProcessContext,\n resolveRootSessionId,\n SUBAGENT_PARENT_SESSION_ENV,\n} from '@agimon-ai/doompi-extension-contracts/child-process';\nimport { connectDoomCordisHost } from '@agimon-ai/doompi-extension-contracts/cordis-host';\nimport { DOOM_HELP_SERVICE, requireDoomHelpService } from '@agimon-ai/doompi-extension-contracts/help';\nimport {\n DOOM_SKILL_SOURCES_SERVICE,\n requireDoomSkillSourcesService,\n} from '@agimon-ai/doompi-extension-contracts/skills';\nimport { DOOM_UI_HUB_SERVICE, requireDoomUiHub } from '@agimon-ai/doompi-extension-contracts/ui-hub';\nimport type { Context } from '@deepseek-ai/cordis';\nimport type { ExtensionAPI, ExtensionContext } from '@earendil-works/pi-coding-agent';\nimport { registerLeaderContribution } from './leader.ts';\nimport { installWorkflowPiRuntime, type WorkflowPiRuntime } from './workflow/piExtension.ts';\n\nconst PACKAGE_SOURCE = '@agimon-ai/doompi-workflow';\nconst SKILL_RELATIVE_PATH = '../../../skills';\nconst DISPATCHER_AGENT_NAME = 'agiflow-dispatcher';\nconst SESSION_START_EVENT = 'session_start';\nconst SESSION_SHUTDOWN_EVENT = 'session_shutdown';\nconst INACTIVE_RUNTIME_MESSAGE = 'The workflow runtime is no longer active.';\nconst CLEANUP_SCOPE = Symbol('doom-workflow-cleanup');\n\nconst DISPATCHER_TOOL_NAMES = new Set(['list_workflows', 'launch_workflow']);\nconst ROOT_SCOPED_TOOL_NAMES = new Set(['launch_workflow']);\n\ntype RegisteredTool = Parameters<ExtensionAPI['registerTool']>[0];\ntype CommandOptions = Parameters<ExtensionAPI['registerCommand']>[1];\ntype ShortcutOptions = Parameters<ExtensionAPI['registerShortcut']>[1];\ntype RuntimeScope = number | typeof CLEANUP_SCOPE;\ntype LifecycleHandler = (event: unknown, context: ExtensionContext) => unknown;\n\ninterface WorkflowFence {\n readonly pi: ExtensionAPI;\n readonly beginDisposal: () => void;\n readonly finishDisposal: () => void;\n readonly isCurrentInvocation: () => boolean;\n readonly runCleanup: <T>(operation: () => T) => T;\n}\n\nfunction boundValue(target: object, property: PropertyKey): unknown {\n const value = Reflect.get(target, property, target);\n return typeof value === 'function' ? value.bind(target) : value;\n}\n\nfunction withParentSession(ctx: ExtensionContext, parentSessionId: string): ExtensionContext {\n const sessionManager = new Proxy(ctx.sessionManager, {\n get(target, property) {\n if (property === 'getSessionId') return () => parentSessionId;\n return boundValue(target, property);\n },\n });\n return new Proxy(ctx, {\n get(target, property) {\n if (property === 'sessionManager') return sessionManager;\n return boundValue(target, property);\n },\n });\n}\n\n/** Restrict a dispatcher child to discovery and root-owned launch. */\nexport function createDispatcherBridge(\n pi: ExtensionAPI,\n parentSessionId: string | undefined,\n environment: Readonly<Record<string, string | undefined>> = process.env,\n): ExtensionAPI {\n const rootSessionId = parentSessionId ? resolveRootSessionId(parentSessionId, environment) : undefined;\n return new Proxy(pi, {\n get(target, property) {\n if (property === 'setActiveTools') {\n return (names: string[]): void => target.setActiveTools(names.filter((name) => name !== 'workflow_run'));\n }\n if (property !== 'registerTool') return boundValue(target, property);\n return (tool: RegisteredTool): void => {\n if (!DISPATCHER_TOOL_NAMES.has(tool.name)) return;\n if (!rootSessionId || !ROOT_SCOPED_TOOL_NAMES.has(tool.name)) {\n target.registerTool(tool);\n return;\n }\n target.registerTool({\n ...tool,\n execute: (toolCallId, params, signal, onUpdate, ctx) =>\n tool.execute(toolCallId, params, signal, onUpdate, withParentSession(ctx, rootSessionId)),\n });\n };\n },\n });\n}\n\nexport function resolveDispatcherParentSession(\n environment: Readonly<Record<string, string | undefined>>,\n): string | undefined {\n const childContext = readChildProcessContext(environment);\n return childContext?.parentSessionId ?? (environment[SUBAGENT_PARENT_SESSION_ENV]?.trim() || undefined);\n}\n\nexport function isWorkflowDispatcherProcess(environment: Readonly<Record<string, string | undefined>>): boolean {\n const childContext = readChildProcessContext(environment);\n if (childContext?.mode === DISPATCHER_AGENT_NAME) return true;\n return (\n environment.PI_SUBAGENT_CHILD_AGENT === DISPATCHER_AGENT_NAME &&\n Boolean(environment[SUBAGENT_PARENT_SESSION_ENV]?.trim())\n );\n}\n\nfunction inactiveToolResult(): Awaited<ReturnType<RegisteredTool['execute']>> {\n return {\n content: [{ type: 'text', text: INACTIVE_RUNTIME_MESSAGE }],\n details: undefined,\n };\n}\n\n/** Fence every callback retained by Pi after this package root is replaced. */\nfunction createWorkflowFence(host: ExtensionAPI): WorkflowFence {\n const scope = new AsyncLocalStorage<RuntimeScope>();\n let active = true;\n let disposing = false;\n let generation = 0;\n let lastSessionStartEvent: unknown;\n let lastSessionStartContext: ExtensionContext | undefined;\n\n const isCurrentInvocation = (): boolean => {\n if (!active) return false;\n const currentScope = scope.getStore();\n return currentScope === undefined || currentScope === CLEANUP_SCOPE || currentScope === generation;\n };\n const ownsContext = (contextGeneration: number): boolean => {\n if (!active) return false;\n const currentScope = scope.getStore();\n return (\n currentScope === CLEANUP_SCOPE ||\n (contextGeneration === generation && (currentScope === undefined || currentScope === contextGeneration))\n );\n };\n\n const guardContext = <TContext extends ExtensionContext>(context: TContext, contextGeneration: number): TContext => {\n const guardedUi = new Proxy(context.ui, {\n get(target, property) {\n const value = Reflect.get(target, property, target);\n if (typeof value !== 'function') return value;\n return (...args: unknown[]): unknown => {\n if (ownsContext(contextGeneration)) return Reflect.apply(value, target, args);\n if (property === 'confirm') return Promise.resolve(false);\n if (property === 'custom' || property === 'editor' || property === 'input' || property === 'select') {\n return Promise.resolve(undefined);\n }\n return undefined;\n };\n },\n });\n return new Proxy(context, {\n get(target, property) {\n if (property === 'ui') return guardedUi;\n return boundValue(target, property);\n },\n }) as TContext;\n };\n\n const registerTool = (tool: RegisteredTool): void => {\n if (!isCurrentInvocation()) return;\n host.registerTool({\n ...tool,\n execute: async (toolCallId, params, signal, onUpdate, context) => {\n const executionGeneration = generation;\n if (!active || disposing || signal?.aborted) return inactiveToolResult();\n const guardedContext = guardContext(context, executionGeneration);\n const guardedUpdate = onUpdate\n ? (update: Parameters<NonNullable<typeof onUpdate>>[0]): void => {\n if (active && executionGeneration === generation && !signal?.aborted) onUpdate(update);\n }\n : undefined;\n const result = await scope.run(executionGeneration, () =>\n tool.execute(toolCallId, params, signal, guardedUpdate, guardedContext),\n );\n return active && !disposing && executionGeneration === generation ? result : inactiveToolResult();\n },\n });\n };\n\n const registerCommand = (name: string, options: CommandOptions): void => {\n if (!isCurrentInvocation()) return;\n host.registerCommand(name, {\n ...options,\n handler: async (args, context) => {\n const executionGeneration = generation;\n if (!active || disposing) return;\n await scope.run(executionGeneration, () => options.handler(args, guardContext(context, executionGeneration)));\n },\n });\n };\n\n const registerShortcut = (\n shortcut: Parameters<ExtensionAPI['registerShortcut']>[0],\n options: ShortcutOptions,\n ): void => {\n if (!isCurrentInvocation()) return;\n host.registerShortcut(shortcut, {\n ...options,\n handler: (context) => {\n const executionGeneration = generation;\n if (!active || disposing) return;\n return scope.run(executionGeneration, () => options.handler(guardContext(context, executionGeneration)));\n },\n });\n };\n\n const guardedPi = new Proxy(host, {\n get(target, property) {\n if (property === 'on') {\n return (eventName: string, handler: LifecycleHandler): void => {\n const registerLifecycleHandler = boundValue(target, 'on') as (\n name: string,\n callback: LifecycleHandler,\n ) => void;\n registerLifecycleHandler(eventName, (event: unknown, context: ExtensionContext) => {\n if (!active || disposing) return undefined;\n if (\n eventName === SESSION_START_EVENT &&\n (event !== lastSessionStartEvent || context !== lastSessionStartContext)\n ) {\n generation += 1;\n lastSessionStartEvent = event;\n lastSessionStartContext = context;\n }\n const eventGeneration = generation;\n return scope.run(eventGeneration, () => handler(event, guardContext(context, eventGeneration)));\n });\n };\n }\n if (property === 'registerTool') return registerTool;\n if (property === 'registerCommand') return registerCommand;\n if (property === 'registerShortcut') return registerShortcut;\n if (property === 'getActiveTools') {\n return (): string[] => (isCurrentInvocation() ? target.getActiveTools() : []);\n }\n if (property === 'setActiveTools') {\n return (names: string[]): void => {\n if (isCurrentInvocation()) target.setActiveTools(names);\n };\n }\n if (property === 'sendMessage') {\n return (...args: Parameters<ExtensionAPI['sendMessage']>): void => {\n if (isCurrentInvocation()) target.sendMessage(...args);\n };\n }\n if (property === 'sendUserMessage') {\n return (...args: Parameters<ExtensionAPI['sendUserMessage']>): void => {\n if (isCurrentInvocation()) target.sendUserMessage(...args);\n };\n }\n if (property === 'exec') {\n return (...args: Parameters<ExtensionAPI['exec']>): ReturnType<ExtensionAPI['exec']> =>\n isCurrentInvocation()\n ? target.exec(...args)\n : Promise.resolve({ code: 1, killed: false, stderr: INACTIVE_RUNTIME_MESSAGE, stdout: '' });\n }\n return boundValue(target, property);\n },\n });\n\n return {\n pi: guardedPi,\n beginDisposal() {\n if (disposing || !active) return;\n disposing = true;\n generation += 1;\n },\n finishDisposal() {\n active = false;\n disposing = false;\n generation += 1;\n },\n isCurrentInvocation,\n runCleanup: (operation) => scope.run(CLEANUP_SCOPE, operation),\n };\n}\n\n/** Install one normal or dispatcher runtime into its host-owned Cordis plugin fiber. */\nexport function installWorkflowRuntime(\n cordis: Context,\n pi: ExtensionAPI,\n isCurrentInvocation: () => boolean,\n environment: Readonly<Record<string, string | undefined>> = process.env,\n): void {\n const dispatcher = isWorkflowDispatcherProcess(environment);\n const parentSessionId = dispatcher ? resolveDispatcherParentSession(environment) : undefined;\n const runtimePi = dispatcher ? createDispatcherBridge(pi, parentSessionId, environment) : pi;\n\n let runtime: WorkflowPiRuntime | undefined;\n cordis.effect(() => () => (runtime ? runtime.dispose() : Promise.resolve()), `${PACKAGE_SOURCE}/runtime`);\n\n cordis.inject([DOOM_SKILL_SOURCES_SERVICE], (skillContext) => {\n const directory = resolve(dirname(fileURLToPath(import.meta.url)), SKILL_RELATIVE_PATH);\n if (!existsSync(directory)) return undefined;\n const contribution = requireDoomSkillSourcesService(skillContext).register({\n source: PACKAGE_SOURCE,\n directories: [directory],\n });\n return () => contribution.dispose();\n });\n\n if (dispatcher) {\n runtime = installWorkflowPiRuntime({ cordis, initialMode: true, isActive: isCurrentInvocation })(runtimePi);\n return;\n }\n\n let workflowMode = false;\n let leader: ReturnType<typeof registerLeaderContribution> | undefined;\n cordis.inject([DOOM_UI_HUB_SERVICE], (uiContext) => {\n const contribution = registerLeaderContribution(requireDoomUiHub(uiContext), workflowMode);\n leader = contribution;\n return () => {\n contribution.dispose();\n if (leader === contribution) leader = undefined;\n };\n });\n cordis.inject([DOOM_HELP_SERVICE], (helpContext) => {\n const help = requireDoomHelpService(helpContext).register({\n source: PACKAGE_SOURCE,\n moduleUrl: import.meta.url,\n skills: [\n {\n name: 'doompi-author-workflow',\n description:\n 'Author DoomPi workflow definitions. Use when creating or changing a *.workflow.yml graph, arranging job dependencies and host-executed steps, or deciding how a command requiring a TTY should run.',\n },\n {\n name: 'doompi-use-workflow',\n description:\n 'Use DoomPi workflows. Use when discovering or launching workflows, monitoring or controlling asynchronous runs, interpreting terminal notifications, or recovering a failed run safely.',\n },\n ],\n });\n return () => help.dispose();\n });\n runtime = installWorkflowPiRuntime({\n cordis,\n isActive: isCurrentInvocation,\n onModeChange: (enabled) => {\n workflowMode = enabled;\n leader?.setMode(enabled);\n },\n })(runtimePi);\n}\n\ninterface WorkflowPluginConfig {\n readonly fence: WorkflowFence;\n readonly environment: Readonly<Record<string, string | undefined>>;\n}\n\nfunction workflowPlugin(cordis: Context, { fence, environment }: WorkflowPluginConfig): void {\n installWorkflowRuntime(cordis, fence.pi, fence.isCurrentInvocation, environment);\n}\n\n/** The package's sole Pi factory; Pi reloads it and Cordis owns all package resources. */\nexport async function workflowExtension(pi: ExtensionAPI): Promise<void> {\n const connection = await connectDoomCordisHost(pi, PACKAGE_SOURCE);\n const fence = createWorkflowFence(pi);\n const fiber = connection.root.plugin(workflowPlugin, { fence, environment: process.env });\n let disposal: Promise<void> | undefined;\n const dispose = (): Promise<void> => {\n if (disposal) return disposal;\n fence.beginDisposal();\n disposal = fence.runCleanup(async () => {\n try {\n await fiber.dispose();\n } finally {\n try {\n fence.finishDisposal();\n } finally {\n await connection.dispose();\n }\n }\n });\n return disposal;\n };\n\n try {\n await fiber;\n } catch (error) {\n await dispose();\n throw error;\n }\n pi.on(SESSION_SHUTDOWN_EVENT, dispose);\n}\n\nexport default workflowExtension;\n"],"mappings":"wdAqBA,MAAM,EAAiB,6BAEjB,EAAwB,qBAGxB,EAA2B,4CAC3B,EAAgB,OAAO,uBAAuB,EAE9C,EAAwB,IAAI,IAAI,CAAC,iBAAkB,iBAAiB,CAAC,EACrE,EAAyB,IAAI,IAAI,CAAC,iBAAiB,CAAC,EAgB1D,SAAS,EAAW,EAAgB,EAAgC,CAClE,IAAM,EAAQ,QAAQ,IAAI,EAAQ,EAAU,CAAM,EAClD,OAAO,OAAO,GAAU,WAAa,EAAM,KAAK,CAAM,EAAI,CAC5D,CAEA,SAAS,EAAkB,EAAuB,EAA2C,CAC3F,IAAM,EAAiB,IAAI,MAAM,EAAI,eAAgB,CACnD,IAAI,EAAQ,EAAU,CAEpB,OADI,IAAa,mBAA6B,EACvC,EAAW,EAAQ,CAAQ,CACpC,CACF,CAAC,EACD,OAAO,IAAI,MAAM,EAAK,CACpB,IAAI,EAAQ,EAAU,CAEpB,OADI,IAAa,iBAAyB,EACnC,EAAW,EAAQ,CAAQ,CACpC,CACF,CAAC,CACH,CAGA,SAAgB,EACd,EACA,EACA,EAA4D,QAAQ,IACtD,CACd,IAAM,EAAgB,GAAA,EAAkBA,EAAAA,qBAAAA,CAAqB,EAAiB,CAAW,EAAI,IAAA,GAC7F,OAAO,IAAI,MAAM,EAAI,CACnB,IAAI,EAAQ,EAAU,CAKpB,OAJI,IAAa,iBACP,GAA0B,EAAO,eAAe,EAAM,OAAQ,GAAS,IAAS,cAAc,CAAC,EAErG,IAAa,eACT,GAA+B,CAChC,KAAsB,IAAI,EAAK,IAAI,EACxC,IAAI,CAAC,GAAiB,CAAC,EAAuB,IAAI,EAAK,IAAI,EAAG,CAC5D,EAAO,aAAa,CAAI,EACxB,MACF,CACA,EAAO,aAAa,CAClB,GAAG,EACH,SAAU,EAAY,EAAQ,EAAQ,EAAU,IAC9C,EAAK,QAAQ,EAAY,EAAQ,EAAQ,EAAU,EAAkB,EAAK,CAAa,CAAC,CAC5F,CAAC,CALD,CAMF,EAZwC,EAAW,EAAQ,CAAQ,CAarE,CACF,CAAC,CACH,CAEA,SAAgB,EACd,EACoB,CAEpB,OAAA,EADqBC,EAAAA,wBAAAA,CAAwB,CAC3B,CAAC,EAAE,kBAAoB,EAAYC,EAAAA,4BAA4B,EAAE,KAAK,GAAK,IAAA,GAC/F,CAEA,SAAgB,EAA4B,EAAoE,CAG9G,OADA,EADqBD,EAAAA,wBAAAA,CAAwB,CAC9B,CAAC,EAAE,OAAS,GAEzB,EAAY,0BAA4B,GACxC,EAAQ,EAAYC,EAAAA,4BAA4B,EAAE,KAAK,CAE3D,CAEA,SAAS,GAAqE,CAC5E,MAAO,CACL,QAAS,CAAC,CAAE,KAAM,OAAQ,KAAM,CAAyB,CAAC,EAC1D,QAAS,IAAA,EACX,CACF,CAGA,SAAS,EAAoB,EAAmC,CAC9D,IAAM,EAAQ,IAAIC,EAAAA,kBACd,EAAS,GACT,EAAY,GACZ,EAAa,EACb,EACA,EAEE,MAAqC,CACzC,GAAI,CAAC,EAAQ,MAAO,GACpB,IAAM,EAAe,EAAM,SAAS,EACpC,OAAO,IAAiB,IAAA,IAAa,IAAiB,GAAiB,IAAiB,CAC1F,EACM,EAAe,GAAuC,CAC1D,GAAI,CAAC,EAAQ,MAAO,GACpB,IAAM,EAAe,EAAM,SAAS,EACpC,OACE,IAAiB,GAChB,IAAsB,IAAe,IAAiB,IAAA,IAAa,IAAiB,EAEzF,EAEM,GAAmD,EAAmB,IAAwC,CAClH,IAAM,EAAY,IAAI,MAAM,EAAQ,GAAI,CACtC,IAAI,EAAQ,EAAU,CACpB,IAAM,EAAQ,QAAQ,IAAI,EAAQ,EAAU,CAAM,EAElD,OADI,OAAO,GAAU,YACb,GAAG,IAA6B,CACtC,GAAI,EAAY,CAAiB,EAAG,OAAO,QAAQ,MAAM,EAAO,EAAQ,CAAI,EAC5E,GAAI,IAAa,UAAW,OAAO,QAAQ,QAAQ,EAAK,EACxD,GAAI,IAAa,UAAY,IAAa,UAAY,IAAa,SAAW,IAAa,SACzF,OAAO,QAAQ,QAAQ,IAAA,EAAS,CAGpC,EARwC,CAS1C,CACF,CAAC,EACD,OAAO,IAAI,MAAM,EAAS,CACxB,IAAI,EAAQ,EAAU,CAEpB,OADI,IAAa,KAAa,EACvB,EAAW,EAAQ,CAAQ,CACpC,CACF,CAAC,CACH,EAEM,EAAgB,GAA+B,CAC9C,EAAoB,GACzB,EAAK,aAAa,CAChB,GAAG,EACH,QAAS,MAAO,EAAY,EAAQ,EAAQ,EAAU,IAAY,CAChE,IAAM,EAAsB,EAC5B,GAAI,CAAC,GAAU,GAAa,GAAQ,QAAS,OAAO,EAAmB,EACvE,IAAM,EAAiB,EAAa,EAAS,CAAmB,EAC1D,EAAgB,EACjB,GAA8D,CACzD,GAAU,IAAwB,GAAc,CAAC,GAAQ,SAAS,EAAS,CAAM,CACvF,EACA,IAAA,GACE,EAAS,MAAM,EAAM,IAAI,MAC7B,EAAK,QAAQ,EAAY,EAAQ,EAAQ,EAAe,CAAc,CACxE,EACA,OAAO,GAAU,CAAC,GAAa,IAAwB,EAAa,EAAS,EAAmB,CAClG,CACF,CAAC,CACH,EAEM,GAAmB,EAAc,IAAkC,CAClE,EAAoB,GACzB,EAAK,gBAAgB,EAAM,CACzB,GAAG,EACH,QAAS,MAAO,EAAM,IAAY,CAChC,IAAM,EAAsB,EACvB,GAAU,IACf,MAAM,EAAM,IAAI,MAA2B,EAAQ,QAAQ,EAAM,EAAa,EAAS,CAAmB,CAAC,CAAC,CAC9G,CACF,CAAC,CACH,EAEM,GACJ,EACA,IACS,CACJ,EAAoB,GACzB,EAAK,iBAAiB,EAAU,CAC9B,GAAG,EACH,QAAU,GAAY,CACpB,IAAM,EAAsB,EACxB,GAAC,GAAU,GACf,OAAO,EAAM,IAAI,MAA2B,EAAQ,QAAQ,EAAa,EAAS,CAAmB,CAAC,CAAC,CACzG,CACF,CAAC,CACH,EAwDA,MAAO,CACL,GAAI,IAvDgB,MAAM,EAAM,CAChC,IAAI,EAAQ,EAAU,CAiDpB,OAhDI,IAAa,MACP,EAAmB,IAAoC,CAK7D,EAJ4C,EAAQ,IAI7B,CAAC,CAAC,GAAY,EAAgB,IAA8B,CACjF,GAAI,CAAC,GAAU,EAAW,OAExB,IAAc,kBACb,IAAU,GAAyB,IAAY,KAEhD,GAAc,EACd,EAAwB,EACxB,EAA0B,GAE5B,IAAM,EAAkB,EACxB,OAAO,EAAM,IAAI,MAAuB,EAAQ,EAAO,EAAa,EAAS,CAAe,CAAC,CAAC,CAChG,CAAC,CACH,EAEE,IAAa,eAAuB,EACpC,IAAa,kBAA0B,EACvC,IAAa,mBAA2B,EACxC,IAAa,qBACS,EAAoB,EAAI,EAAO,eAAe,EAAI,CAAC,EAEzE,IAAa,iBACP,GAA0B,CAC5B,EAAoB,GAAG,EAAO,eAAe,CAAK,CACxD,EAEE,IAAa,eACP,GAAG,IAAwD,CAC7D,EAAoB,GAAG,EAAO,YAAY,GAAG,CAAI,CACvD,EAEE,IAAa,mBACP,GAAG,IAA4D,CACjE,EAAoB,GAAG,EAAO,gBAAgB,GAAG,CAAI,CAC3D,EAEE,IAAa,QACP,GAAG,IACT,EAAoB,EAChB,EAAO,KAAK,GAAG,CAAI,EACnB,QAAQ,QAAQ,CAAE,KAAM,EAAG,OAAQ,GAAO,OAAQ,EAA0B,OAAQ,EAAG,CAAC,EAEzF,EAAW,EAAQ,CAAQ,CACpC,CACF,CAGc,EACZ,eAAgB,CACV,IAAc,IAClB,EAAY,GACZ,GAAc,EAChB,EACA,gBAAiB,CACf,EAAS,GACT,EAAY,GACZ,GAAc,CAChB,EACA,sBACA,WAAa,GAAc,EAAM,IAAI,EAAe,CAAS,CAC/D,CACF,CAGA,SAAgB,EACd,EACA,EACA,EACA,EAA4D,QAAQ,IAC9D,CACN,IAAM,EAAa,EAA4B,CAAW,EACpD,EAAkB,EAAa,EAA+B,CAAW,EAAI,IAAA,GAC7E,EAAY,EAAa,EAAuB,EAAI,EAAiB,CAAW,EAAI,EAEtF,EAaJ,GAZA,EAAO,eAAoB,EAAU,EAAQ,QAAQ,EAAI,QAAQ,QAAQ,EAAI,GAAG,EAAe,SAAS,EAExG,EAAO,OAAO,CAACC,EAAAA,0BAA0B,EAAI,GAAiB,CAC5D,IAAM,GAAA,EAAYC,EAAAA,QAAAA,EAAAA,EAAQC,EAAAA,QAAAA,EAAAA,EAAQC,EAAAA,cAAAA,CAAAA,QAAAA,KAAAA,CAAAA,CAAAA,cAAAA,UAAAA,CAAAA,CAAAA,IAA6B,CAAC,EAAG,iBAAmB,EACtF,GAAI,EAAA,EAACC,EAAAA,WAAAA,CAAW,CAAS,EAAG,OAC5B,IAAM,GAAA,EAAeC,EAAAA,+BAAAA,CAA+B,CAAY,CAAC,CAAC,SAAS,CACzE,OAAQ,EACR,YAAa,CAAC,CAAS,CACzB,CAAC,EACD,UAAa,EAAa,QAAQ,CACpC,CAAC,EAEG,EAAY,CACd,EAAUC,EAAAA,yBAAyB,CAAE,SAAQ,YAAa,GAAM,SAAU,CAAoB,CAAC,CAAC,CAAC,CAAS,EAC1G,MACF,CAEA,IAAI,EAAe,GACf,EACJ,EAAO,OAAO,CAACC,EAAAA,mBAAmB,EAAI,GAAc,CAClD,IAAM,EAAeC,EAAAA,4BAAAA,EAA2BC,EAAAA,iBAAAA,CAAiB,CAAS,EAAG,CAAY,EAEzF,MADA,GAAS,MACI,CACX,EAAa,QAAQ,EACjB,IAAW,IAAc,EAAS,IAAA,GACxC,CACF,CAAC,EACD,EAAO,OAAO,CAACC,EAAAA,iBAAiB,EAAI,GAAgB,CAClD,IAAM,GAAA,EAAOC,EAAAA,uBAAAA,CAAuB,CAAW,CAAC,CAAC,SAAS,CACxD,OAAQ,EACR,UAAA,QAAA,KAAA,CAAA,CAAA,cAAA,UAAA,CAAA,CAAA,KACA,OAAQ,CACN,CACE,KAAM,yBACN,YACE,qMACJ,EACA,CACE,KAAM,sBACN,YACE,yLACJ,CACF,CACF,CAAC,EACD,UAAa,EAAK,QAAQ,CAC5B,CAAC,EACD,EAAUL,EAAAA,yBAAyB,CACjC,SACA,SAAU,EACV,aAAe,GAAY,CACzB,EAAe,EACf,GAAQ,QAAQ,CAAO,CACzB,CACF,CAAC,CAAC,CAAC,CAAS,CACd,CAOA,SAAS,EAAe,EAAiB,CAAE,QAAO,eAA2C,CAC3F,EAAuB,EAAQ,EAAM,GAAI,EAAM,oBAAqB,CAAW,CACjF,CAGA,eAAsB,EAAkB,EAAiC,CACvE,IAAM,EAAa,MAAA,EAAMM,EAAAA,sBAAAA,CAAsB,EAAI,CAAc,EAC3D,EAAQ,EAAoB,CAAE,EAC9B,EAAQ,EAAW,KAAK,OAAO,EAAgB,CAAE,QAAO,YAAa,QAAQ,GAAI,CAAC,EACpF,EACE,MACA,IACJ,EAAM,cAAc,EACpB,EAAW,EAAM,WAAW,SAAY,CACtC,GAAI,CACF,MAAM,EAAM,QAAQ,CACtB,QAAU,CACR,GAAI,CACF,EAAM,eAAe,CACvB,QAAU,CACR,MAAM,EAAW,QAAQ,CAC3B,CACF,CACF,CAAC,EACM,GAGT,GAAI,CACF,MAAM,CACR,OAAS,EAAO,CAEd,MADA,MAAM,EAAQ,EACR,CACR,CACA,EAAG,GAAG,mBAAwB,CAAO,CACvC"}
|
|
@@ -2,7 +2,7 @@ import "@deepseek-ai/cordis";
|
|
|
2
2
|
import { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
3
3
|
//#region src/adapters/pi/extension.d.ts
|
|
4
4
|
/** The package's sole Pi factory; Pi reloads it and Cordis owns all package resources. */
|
|
5
|
-
declare function workflowExtension(pi: ExtensionAPI): Promise<void>;
|
|
5
|
+
export declare function workflowExtension(pi: ExtensionAPI): Promise<void>;
|
|
6
6
|
//#endregion
|
|
7
|
-
export { workflowExtension as default
|
|
7
|
+
export { workflowExtension as default };
|
|
8
8
|
//# sourceMappingURL=extension.d.cts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extension.d.cts","names":[],"sources":["../../../src/adapters/pi/extension.ts"],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"extension.d.cts","names":[],"sources":["../../../src/adapters/pi/extension.ts"],"mappings":";;;;wBAyWsB,kBAAkB,IAAI,eAAe"}
|
|
@@ -2,7 +2,7 @@ import "@deepseek-ai/cordis";
|
|
|
2
2
|
import { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
3
3
|
//#region src/adapters/pi/extension.d.ts
|
|
4
4
|
/** The package's sole Pi factory; Pi reloads it and Cordis owns all package resources. */
|
|
5
|
-
declare function workflowExtension(pi: ExtensionAPI): Promise<void>;
|
|
5
|
+
export declare function workflowExtension(pi: ExtensionAPI): Promise<void>;
|
|
6
6
|
//#endregion
|
|
7
|
-
export { workflowExtension as default
|
|
7
|
+
export { workflowExtension as default };
|
|
8
8
|
//# sourceMappingURL=extension.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extension.d.mts","names":[],"sources":["../../../src/adapters/pi/extension.ts"],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"extension.d.mts","names":[],"sources":["../../../src/adapters/pi/extension.ts"],"mappings":";;;;wBAyWsB,kBAAkB,IAAI,eAAe"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{registerLeaderContribution as e}from"./leader.mjs";import{installWorkflowPiRuntime as t}from"./workflow/piExtension.mjs";import{existsSync as n}from"node:fs";import{dirname as r,resolve as i}from"node:path";import{AsyncLocalStorage as a}from"node:async_hooks";import{fileURLToPath as o}from"node:url";import{SUBAGENT_PARENT_SESSION_ENV as s,readChildProcessContext as c,resolveRootSessionId as l}from"@agimon-ai/doompi-extension-contracts/child-process";import{connectDoomCordisHost as u}from"@agimon-ai/doompi-extension-contracts/cordis-host";import{DOOM_HELP_SERVICE as d,requireDoomHelpService as f}from"@agimon-ai/doompi-extension-contracts/help";import{DOOM_SKILL_SOURCES_SERVICE as p,requireDoomSkillSourcesService as m}from"@agimon-ai/doompi-extension-contracts/skills";import{DOOM_UI_HUB_SERVICE as h,requireDoomUiHub as g}from"@agimon-ai/doompi-extension-contracts/ui-hub";const _=`@agimon-ai/doompi-workflow`,v=`agiflow-dispatcher`,y=`The workflow runtime is no longer active.`,b=Symbol(`doom-workflow-cleanup`),x=new Set([`list_workflows`,`launch_workflow`]),S=new Set([`launch_workflow`]);function C(e,t){let n=Reflect.get(e,t,e);return typeof n==`function`?n.bind(e):n}function w(e,t){let n=new Proxy(e.sessionManager,{get(e,n){return n===`getSessionId`?()=>t:C(e,n)}});return new Proxy(e,{get(e,t){return t===`sessionManager`?n:C(e,t)}})}function T(e,t,n=process.env){let r=t?l(t,n):void 0;return new Proxy(e,{get(e,t){return t===`setActiveTools`?t=>e.setActiveTools(t.filter(e=>e!==`workflow_run`)):t===`registerTool`?t=>{if(x.has(t.name)){if(!r||!S.has(t.name)){e.registerTool(t);return}e.registerTool({...t,execute:(e,n,i,a,o)=>t.execute(e,n,i,a,w(o,r))})}}:C(e,t)}})}function E(e){return c(e)?.parentSessionId??(e[s]?.trim()||void 0)}function D(e){return c(e)?.mode===v||e.PI_SUBAGENT_CHILD_AGENT===v&&!!e[s]?.trim()}function O(){return{content:[{type:`text`,text:y}],details:void 0}}function k(e){let t=new a,n=!0,r=!1,i=0,o,s,c=()=>{if(!n)return!1;let e=t.getStore();return e===void 0||e===b||e===i},l=e=>{if(!n)return!1;let r=t.getStore();return r===b||e===i&&(r===void 0||r===e)},u=(e,t)=>{let n=new Proxy(e.ui,{get(e,n){let r=Reflect.get(e,n,e);return typeof r==`function`?(...i)=>{if(l(t))return Reflect.apply(r,e,i);if(n===`confirm`)return Promise.resolve(!1);if(n===`custom`||n===`editor`||n===`input`||n===`select`)return Promise.resolve(void 0)}:r}});return new Proxy(e,{get(e,t){return t===`ui`?n:C(e,t)}})},d=a=>{c()&&e.registerTool({...a,execute:async(e,o,s,c,l)=>{let d=i;if(!n||r||s?.aborted)return O();let f=u(l,d),p=c?e=>{n&&d===i&&!s?.aborted&&c(e)}:void 0,m=await t.run(d,()=>a.execute(e,o,s,p,f));return n&&!r&&d===i?m:O()}})},f=(a,o)=>{c()&&e.registerCommand(a,{...o,handler:async(e,a)=>{let s=i
|
|
1
|
+
import{registerLeaderContribution as e}from"./leader.mjs";import{installWorkflowPiRuntime as t}from"./workflow/piExtension.mjs";import{existsSync as n}from"node:fs";import{dirname as r,resolve as i}from"node:path";import{AsyncLocalStorage as a}from"node:async_hooks";import{fileURLToPath as o}from"node:url";import{SUBAGENT_PARENT_SESSION_ENV as s,readChildProcessContext as c,resolveRootSessionId as l}from"@agimon-ai/doompi-extension-contracts/child-process";import{connectDoomCordisHost as u}from"@agimon-ai/doompi-extension-contracts/cordis-host";import{DOOM_HELP_SERVICE as d,requireDoomHelpService as f}from"@agimon-ai/doompi-extension-contracts/help";import{DOOM_SKILL_SOURCES_SERVICE as p,requireDoomSkillSourcesService as m}from"@agimon-ai/doompi-extension-contracts/skills";import{DOOM_UI_HUB_SERVICE as h,requireDoomUiHub as g}from"@agimon-ai/doompi-extension-contracts/ui-hub";const _=`@agimon-ai/doompi-workflow`,v=`agiflow-dispatcher`,y=`The workflow runtime is no longer active.`,b=Symbol(`doom-workflow-cleanup`),x=new Set([`list_workflows`,`launch_workflow`]),S=new Set([`launch_workflow`]);function C(e,t){let n=Reflect.get(e,t,e);return typeof n==`function`?n.bind(e):n}function w(e,t){let n=new Proxy(e.sessionManager,{get(e,n){return n===`getSessionId`?()=>t:C(e,n)}});return new Proxy(e,{get(e,t){return t===`sessionManager`?n:C(e,t)}})}function T(e,t,n=process.env){let r=t?l(t,n):void 0;return new Proxy(e,{get(e,t){return t===`setActiveTools`?t=>e.setActiveTools(t.filter(e=>e!==`workflow_run`)):t===`registerTool`?t=>{if(x.has(t.name)){if(!r||!S.has(t.name)){e.registerTool(t);return}e.registerTool({...t,execute:(e,n,i,a,o)=>t.execute(e,n,i,a,w(o,r))})}}:C(e,t)}})}function E(e){return c(e)?.parentSessionId??(e[s]?.trim()||void 0)}function D(e){return c(e)?.mode===v||e.PI_SUBAGENT_CHILD_AGENT===v&&!!e[s]?.trim()}function O(){return{content:[{type:`text`,text:y}],details:void 0}}function k(e){let t=new a,n=!0,r=!1,i=0,o,s,c=()=>{if(!n)return!1;let e=t.getStore();return e===void 0||e===b||e===i},l=e=>{if(!n)return!1;let r=t.getStore();return r===b||e===i&&(r===void 0||r===e)},u=(e,t)=>{let n=new Proxy(e.ui,{get(e,n){let r=Reflect.get(e,n,e);return typeof r==`function`?(...i)=>{if(l(t))return Reflect.apply(r,e,i);if(n===`confirm`)return Promise.resolve(!1);if(n===`custom`||n===`editor`||n===`input`||n===`select`)return Promise.resolve(void 0)}:r}});return new Proxy(e,{get(e,t){return t===`ui`?n:C(e,t)}})},d=a=>{c()&&e.registerTool({...a,execute:async(e,o,s,c,l)=>{let d=i;if(!n||r||s?.aborted)return O();let f=u(l,d),p=c?e=>{n&&d===i&&!s?.aborted&&c(e)}:void 0,m=await t.run(d,()=>a.execute(e,o,s,p,f));return n&&!r&&d===i?m:O()}})},f=(a,o)=>{c()&&e.registerCommand(a,{...o,handler:async(e,a)=>{let s=i;n&&!r&&await t.run(s,()=>o.handler(e,u(a,s)))}})},p=(a,o)=>{c()&&e.registerShortcut(a,{...o,handler:e=>{let a=i;if(n&&!r)return t.run(a,()=>o.handler(u(e,a)))}})};return{pi:new Proxy(e,{get(e,a){return a===`on`?(a,c)=>{C(e,`on`)(a,(e,l)=>{if(!n||r)return;a===`session_start`&&(e!==o||l!==s)&&(i+=1,o=e,s=l);let d=i;return t.run(d,()=>c(e,u(l,d)))})}:a===`registerTool`?d:a===`registerCommand`?f:a===`registerShortcut`?p:a===`getActiveTools`?()=>c()?e.getActiveTools():[]:a===`setActiveTools`?t=>{c()&&e.setActiveTools(t)}:a===`sendMessage`?(...t)=>{c()&&e.sendMessage(...t)}:a===`sendUserMessage`?(...t)=>{c()&&e.sendUserMessage(...t)}:a===`exec`?(...t)=>c()?e.exec(...t):Promise.resolve({code:1,killed:!1,stderr:y,stdout:``}):C(e,a)}}),beginDisposal(){!r&&n&&(r=!0,i+=1)},finishDisposal(){n=!1,r=!1,i+=1},isCurrentInvocation:c,runCleanup:e=>t.run(b,e)}}function A(a,s,c,l=process.env){let u=D(l),v=u?E(l):void 0,y=u?T(s,v,l):s,b;if(a.effect(()=>()=>b?b.dispose():Promise.resolve(),`${_}/runtime`),a.inject([p],e=>{let t=i(r(o(import.meta.url)),`../../../skills`);if(!n(t))return;let a=m(e).register({source:_,directories:[t]});return()=>a.dispose()}),u){b=t({cordis:a,initialMode:!0,isActive:c})(y);return}let x=!1,S;a.inject([h],t=>{let n=e(g(t),x);return S=n,()=>{n.dispose(),S===n&&(S=void 0)}}),a.inject([d],e=>{let t=f(e).register({source:_,moduleUrl:import.meta.url,skills:[{name:`doompi-author-workflow`,description:`Author DoomPi workflow definitions. Use when creating or changing a *.workflow.yml graph, arranging job dependencies and host-executed steps, or deciding how a command requiring a TTY should run.`},{name:`doompi-use-workflow`,description:`Use DoomPi workflows. Use when discovering or launching workflows, monitoring or controlling asynchronous runs, interpreting terminal notifications, or recovering a failed run safely.`}]});return()=>t.dispose()}),b=t({cordis:a,isActive:c,onModeChange:e=>{x=e,S?.setMode(e)}})(y)}function j(e,{fence:t,environment:n}){A(e,t.pi,t.isCurrentInvocation,n)}async function M(e){let t=await u(e,_),n=k(e),r=t.root.plugin(j,{fence:n,environment:process.env}),i,a=()=>i||(n.beginDisposal(),i=n.runCleanup(async()=>{try{await r.dispose()}finally{try{n.finishDisposal()}finally{await t.dispose()}}}),i);try{await r}catch(e){throw await a(),e}e.on(`session_shutdown`,a)}export{T as createDispatcherBridge,M as default,M as workflowExtension,A as installWorkflowRuntime,D as isWorkflowDispatcherProcess,E as resolveDispatcherParentSession};
|
|
2
2
|
//# sourceMappingURL=extension.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extension.mjs","names":[],"sources":["../../../src/adapters/pi/extension.ts"],"sourcesContent":["import { AsyncLocalStorage } from 'node:async_hooks';\nimport { existsSync } from 'node:fs';\nimport { dirname, resolve } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport {\n readChildProcessContext,\n resolveRootSessionId,\n SUBAGENT_PARENT_SESSION_ENV,\n} from '@agimon-ai/doompi-extension-contracts/child-process';\nimport { connectDoomCordisHost } from '@agimon-ai/doompi-extension-contracts/cordis-host';\nimport { DOOM_HELP_SERVICE, requireDoomHelpService } from '@agimon-ai/doompi-extension-contracts/help';\nimport {\n DOOM_SKILL_SOURCES_SERVICE,\n requireDoomSkillSourcesService,\n} from '@agimon-ai/doompi-extension-contracts/skills';\nimport { DOOM_UI_HUB_SERVICE, requireDoomUiHub } from '@agimon-ai/doompi-extension-contracts/ui-hub';\nimport type { Context } from '@deepseek-ai/cordis';\nimport type { ExtensionAPI, ExtensionContext } from '@earendil-works/pi-coding-agent';\nimport { registerLeaderContribution } from './leader.ts';\nimport { installWorkflowPiRuntime, type WorkflowPiRuntime } from './workflow/piExtension.ts';\n\nconst PACKAGE_SOURCE = '@agimon-ai/doompi-workflow';\nconst SKILL_RELATIVE_PATH = '../../../skills';\nconst DISPATCHER_AGENT_NAME = 'agiflow-dispatcher';\nconst SESSION_START_EVENT = 'session_start';\nconst SESSION_SHUTDOWN_EVENT = 'session_shutdown';\nconst INACTIVE_RUNTIME_MESSAGE = 'The workflow runtime is no longer active.';\nconst CLEANUP_SCOPE = Symbol('doom-workflow-cleanup');\n\nconst DISPATCHER_TOOL_NAMES = new Set(['list_workflows', 'launch_workflow']);\nconst ROOT_SCOPED_TOOL_NAMES = new Set(['launch_workflow']);\n\ntype RegisteredTool = Parameters<ExtensionAPI['registerTool']>[0];\ntype CommandOptions = Parameters<ExtensionAPI['registerCommand']>[1];\ntype ShortcutOptions = Parameters<ExtensionAPI['registerShortcut']>[1];\ntype RuntimeScope = number | typeof CLEANUP_SCOPE;\ntype LifecycleHandler = (event: unknown, context: ExtensionContext) => unknown;\n\ninterface WorkflowFence {\n readonly pi: ExtensionAPI;\n readonly beginDisposal: () => void;\n readonly finishDisposal: () => void;\n readonly isCurrentInvocation: () => boolean;\n readonly runCleanup: <T>(operation: () => T) => T;\n}\n\nfunction boundValue(target: object, property: PropertyKey): unknown {\n const value = Reflect.get(target, property, target);\n return typeof value === 'function' ? value.bind(target) : value;\n}\n\nfunction withParentSession(ctx: ExtensionContext, parentSessionId: string): ExtensionContext {\n const sessionManager = new Proxy(ctx.sessionManager, {\n get(target, property) {\n if (property === 'getSessionId') return () => parentSessionId;\n return boundValue(target, property);\n },\n });\n return new Proxy(ctx, {\n get(target, property) {\n if (property === 'sessionManager') return sessionManager;\n return boundValue(target, property);\n },\n });\n}\n\n/** Restrict a dispatcher child to discovery and root-owned launch. */\nexport function createDispatcherBridge(\n pi: ExtensionAPI,\n parentSessionId: string | undefined,\n environment: Readonly<Record<string, string | undefined>> = process.env,\n): ExtensionAPI {\n const rootSessionId = parentSessionId ? resolveRootSessionId(parentSessionId, environment) : undefined;\n return new Proxy(pi, {\n get(target, property) {\n if (property === 'setActiveTools') {\n return (names: string[]): void => target.setActiveTools(names.filter((name) => name !== 'workflow_run'));\n }\n if (property !== 'registerTool') return boundValue(target, property);\n return (tool: RegisteredTool): void => {\n if (!DISPATCHER_TOOL_NAMES.has(tool.name)) return;\n if (!rootSessionId || !ROOT_SCOPED_TOOL_NAMES.has(tool.name)) {\n target.registerTool(tool);\n return;\n }\n target.registerTool({\n ...tool,\n execute: (toolCallId, params, signal, onUpdate, ctx) =>\n tool.execute(toolCallId, params, signal, onUpdate, withParentSession(ctx, rootSessionId)),\n });\n };\n },\n });\n}\n\nexport function resolveDispatcherParentSession(\n environment: Readonly<Record<string, string | undefined>>,\n): string | undefined {\n const childContext = readChildProcessContext(environment);\n return childContext?.parentSessionId ?? (environment[SUBAGENT_PARENT_SESSION_ENV]?.trim() || undefined);\n}\n\nexport function isWorkflowDispatcherProcess(environment: Readonly<Record<string, string | undefined>>): boolean {\n const childContext = readChildProcessContext(environment);\n if (childContext?.mode === DISPATCHER_AGENT_NAME) return true;\n return (\n environment.PI_SUBAGENT_CHILD_AGENT === DISPATCHER_AGENT_NAME &&\n Boolean(environment[SUBAGENT_PARENT_SESSION_ENV]?.trim())\n );\n}\n\nfunction inactiveToolResult(): Awaited<ReturnType<RegisteredTool['execute']>> {\n return {\n content: [{ type: 'text', text: INACTIVE_RUNTIME_MESSAGE }],\n details: undefined,\n };\n}\n\n/** Fence every callback retained by Pi after this package root is replaced. */\nfunction createWorkflowFence(host: ExtensionAPI): WorkflowFence {\n const scope = new AsyncLocalStorage<RuntimeScope>();\n let active = true;\n let disposing = false;\n let generation = 0;\n let lastSessionStartEvent: unknown;\n let lastSessionStartContext: ExtensionContext | undefined;\n\n const isCurrentInvocation = (): boolean => {\n if (!active) return false;\n const currentScope = scope.getStore();\n return currentScope === undefined || currentScope === CLEANUP_SCOPE || currentScope === generation;\n };\n const ownsContext = (contextGeneration: number): boolean => {\n if (!active) return false;\n const currentScope = scope.getStore();\n return (\n currentScope === CLEANUP_SCOPE ||\n (contextGeneration === generation && (currentScope === undefined || currentScope === contextGeneration))\n );\n };\n\n const guardContext = <TContext extends ExtensionContext>(context: TContext, contextGeneration: number): TContext => {\n const guardedUi = new Proxy(context.ui, {\n get(target, property) {\n const value = Reflect.get(target, property, target);\n if (typeof value !== 'function') return value;\n return (...args: unknown[]): unknown => {\n if (ownsContext(contextGeneration)) return Reflect.apply(value, target, args);\n if (property === 'confirm') return Promise.resolve(false);\n if (property === 'custom' || property === 'editor' || property === 'input' || property === 'select') {\n return Promise.resolve(undefined);\n }\n return undefined;\n };\n },\n });\n return new Proxy(context, {\n get(target, property) {\n if (property === 'ui') return guardedUi;\n return boundValue(target, property);\n },\n }) as TContext;\n };\n\n const registerTool = (tool: RegisteredTool): void => {\n if (!isCurrentInvocation()) return;\n host.registerTool({\n ...tool,\n execute: async (toolCallId, params, signal, onUpdate, context) => {\n const executionGeneration = generation;\n if (!active || disposing || signal?.aborted) return inactiveToolResult();\n const guardedContext = guardContext(context, executionGeneration);\n const guardedUpdate = onUpdate\n ? (update: Parameters<NonNullable<typeof onUpdate>>[0]): void => {\n if (active && executionGeneration === generation && !signal?.aborted) onUpdate(update);\n }\n : undefined;\n const result = await scope.run(executionGeneration, () =>\n tool.execute(toolCallId, params, signal, guardedUpdate, guardedContext),\n );\n return active && !disposing && executionGeneration === generation ? result : inactiveToolResult();\n },\n });\n };\n\n const registerCommand = (name: string, options: CommandOptions): void => {\n if (!isCurrentInvocation()) return;\n host.registerCommand(name, {\n ...options,\n handler: async (args, context) => {\n const executionGeneration = generation;\n if (!active || disposing) return;\n await scope.run(executionGeneration, () => options.handler(args, guardContext(context, executionGeneration)));\n },\n });\n };\n\n const registerShortcut = (\n shortcut: Parameters<ExtensionAPI['registerShortcut']>[0],\n options: ShortcutOptions,\n ): void => {\n if (!isCurrentInvocation()) return;\n host.registerShortcut(shortcut, {\n ...options,\n handler: (context) => {\n const executionGeneration = generation;\n if (!active || disposing) return;\n return scope.run(executionGeneration, () => options.handler(guardContext(context, executionGeneration)));\n },\n });\n };\n\n const guardedPi = new Proxy(host, {\n get(target, property) {\n if (property === 'on') {\n return (eventName: string, handler: LifecycleHandler): void => {\n const registerLifecycleHandler = boundValue(target, 'on') as (\n name: string,\n callback: LifecycleHandler,\n ) => void;\n registerLifecycleHandler(eventName, (event: unknown, context: ExtensionContext) => {\n if (!active || disposing) return undefined;\n if (\n eventName === SESSION_START_EVENT &&\n (event !== lastSessionStartEvent || context !== lastSessionStartContext)\n ) {\n generation += 1;\n lastSessionStartEvent = event;\n lastSessionStartContext = context;\n }\n const eventGeneration = generation;\n return scope.run(eventGeneration, () => handler(event, guardContext(context, eventGeneration)));\n });\n };\n }\n if (property === 'registerTool') return registerTool;\n if (property === 'registerCommand') return registerCommand;\n if (property === 'registerShortcut') return registerShortcut;\n if (property === 'getActiveTools') {\n return (): string[] => (isCurrentInvocation() ? target.getActiveTools() : []);\n }\n if (property === 'setActiveTools') {\n return (names: string[]): void => {\n if (isCurrentInvocation()) target.setActiveTools(names);\n };\n }\n if (property === 'sendMessage') {\n return (...args: Parameters<ExtensionAPI['sendMessage']>): void => {\n if (isCurrentInvocation()) target.sendMessage(...args);\n };\n }\n if (property === 'sendUserMessage') {\n return (...args: Parameters<ExtensionAPI['sendUserMessage']>): void => {\n if (isCurrentInvocation()) target.sendUserMessage(...args);\n };\n }\n if (property === 'exec') {\n return (...args: Parameters<ExtensionAPI['exec']>): ReturnType<ExtensionAPI['exec']> =>\n isCurrentInvocation()\n ? target.exec(...args)\n : Promise.resolve({ code: 1, killed: false, stderr: INACTIVE_RUNTIME_MESSAGE, stdout: '' });\n }\n return boundValue(target, property);\n },\n });\n\n return {\n pi: guardedPi,\n beginDisposal() {\n if (disposing || !active) return;\n disposing = true;\n generation += 1;\n },\n finishDisposal() {\n active = false;\n disposing = false;\n generation += 1;\n },\n isCurrentInvocation,\n runCleanup: (operation) => scope.run(CLEANUP_SCOPE, operation),\n };\n}\n\n/** Install one normal or dispatcher runtime into its host-owned Cordis plugin fiber. */\nexport function installWorkflowRuntime(\n cordis: Context,\n pi: ExtensionAPI,\n isCurrentInvocation: () => boolean,\n environment: Readonly<Record<string, string | undefined>> = process.env,\n): void {\n const dispatcher = isWorkflowDispatcherProcess(environment);\n const parentSessionId = dispatcher ? resolveDispatcherParentSession(environment) : undefined;\n const runtimePi = dispatcher ? createDispatcherBridge(pi, parentSessionId, environment) : pi;\n\n let runtime: WorkflowPiRuntime | undefined;\n cordis.effect(() => () => (runtime ? runtime.dispose() : Promise.resolve()), `${PACKAGE_SOURCE}/runtime`);\n\n cordis.inject([DOOM_SKILL_SOURCES_SERVICE], (skillContext) => {\n const directory = resolve(dirname(fileURLToPath(import.meta.url)), SKILL_RELATIVE_PATH);\n if (!existsSync(directory)) return undefined;\n const contribution = requireDoomSkillSourcesService(skillContext).register({\n source: PACKAGE_SOURCE,\n directories: [directory],\n });\n return () => contribution.dispose();\n });\n\n if (dispatcher) {\n runtime = installWorkflowPiRuntime({ cordis, initialMode: true, isActive: isCurrentInvocation })(runtimePi);\n return;\n }\n\n let workflowMode = false;\n let leader: ReturnType<typeof registerLeaderContribution> | undefined;\n cordis.inject([DOOM_UI_HUB_SERVICE], (uiContext) => {\n const contribution = registerLeaderContribution(requireDoomUiHub(uiContext), workflowMode);\n leader = contribution;\n return () => {\n contribution.dispose();\n if (leader === contribution) leader = undefined;\n };\n });\n cordis.inject([DOOM_HELP_SERVICE], (helpContext) => {\n const help = requireDoomHelpService(helpContext).register({\n source: PACKAGE_SOURCE,\n moduleUrl: import.meta.url,\n skills: [\n {\n name: 'doompi-author-workflow',\n description:\n 'Author DoomPi workflow definitions. Use when creating or changing a *.workflow.yml graph, arranging job dependencies and host-executed steps, or deciding how a command requiring a TTY should run.',\n },\n {\n name: 'doompi-use-workflow',\n description:\n 'Use DoomPi workflows. Use when discovering or launching workflows, monitoring or controlling asynchronous runs, interpreting terminal notifications, or recovering a failed run safely.',\n },\n ],\n });\n return () => help.dispose();\n });\n runtime = installWorkflowPiRuntime({\n cordis,\n isActive: isCurrentInvocation,\n onModeChange: (enabled) => {\n workflowMode = enabled;\n leader?.setMode(enabled);\n },\n })(runtimePi);\n}\n\ninterface WorkflowPluginConfig {\n readonly fence: WorkflowFence;\n readonly environment: Readonly<Record<string, string | undefined>>;\n}\n\nfunction workflowPlugin(cordis: Context, { fence, environment }: WorkflowPluginConfig): void {\n installWorkflowRuntime(cordis, fence.pi, fence.isCurrentInvocation, environment);\n}\n\n/** The package's sole Pi factory; Pi reloads it and Cordis owns all package resources. */\nexport async function workflowExtension(pi: ExtensionAPI): Promise<void> {\n const connection = await connectDoomCordisHost(pi, PACKAGE_SOURCE);\n const fence = createWorkflowFence(pi);\n const fiber = connection.root.plugin(workflowPlugin, { fence, environment: process.env });\n let disposal: Promise<void> | undefined;\n const dispose = (): Promise<void> => {\n if (disposal) return disposal;\n fence.beginDisposal();\n disposal = fence.runCleanup(async () => {\n try {\n await fiber.dispose();\n } finally {\n try {\n fence.finishDisposal();\n } finally {\n await connection.dispose();\n }\n }\n });\n return disposal;\n };\n\n try {\n await fiber;\n } catch (error) {\n await dispose();\n throw error;\n }\n pi.on(SESSION_SHUTDOWN_EVENT, dispose);\n}\n\nexport default workflowExtension;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"extension.mjs","names":[],"sources":["../../../src/adapters/pi/extension.ts"],"sourcesContent":["import { AsyncLocalStorage } from 'node:async_hooks';\nimport { existsSync } from 'node:fs';\nimport { dirname, resolve } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport {\n readChildProcessContext,\n resolveRootSessionId,\n SUBAGENT_PARENT_SESSION_ENV,\n} from '@agimon-ai/doompi-extension-contracts/child-process';\nimport { connectDoomCordisHost } from '@agimon-ai/doompi-extension-contracts/cordis-host';\nimport { DOOM_HELP_SERVICE, requireDoomHelpService } from '@agimon-ai/doompi-extension-contracts/help';\nimport {\n DOOM_SKILL_SOURCES_SERVICE,\n requireDoomSkillSourcesService,\n} from '@agimon-ai/doompi-extension-contracts/skills';\nimport { DOOM_UI_HUB_SERVICE, requireDoomUiHub } from '@agimon-ai/doompi-extension-contracts/ui-hub';\nimport type { Context } from '@deepseek-ai/cordis';\nimport type { ExtensionAPI, ExtensionContext } from '@earendil-works/pi-coding-agent';\nimport { registerLeaderContribution } from './leader.ts';\nimport { installWorkflowPiRuntime, type WorkflowPiRuntime } from './workflow/piExtension.ts';\n\nconst PACKAGE_SOURCE = '@agimon-ai/doompi-workflow';\nconst SKILL_RELATIVE_PATH = '../../../skills';\nconst DISPATCHER_AGENT_NAME = 'agiflow-dispatcher';\nconst SESSION_START_EVENT = 'session_start';\nconst SESSION_SHUTDOWN_EVENT = 'session_shutdown';\nconst INACTIVE_RUNTIME_MESSAGE = 'The workflow runtime is no longer active.';\nconst CLEANUP_SCOPE = Symbol('doom-workflow-cleanup');\n\nconst DISPATCHER_TOOL_NAMES = new Set(['list_workflows', 'launch_workflow']);\nconst ROOT_SCOPED_TOOL_NAMES = new Set(['launch_workflow']);\n\ntype RegisteredTool = Parameters<ExtensionAPI['registerTool']>[0];\ntype CommandOptions = Parameters<ExtensionAPI['registerCommand']>[1];\ntype ShortcutOptions = Parameters<ExtensionAPI['registerShortcut']>[1];\ntype RuntimeScope = number | typeof CLEANUP_SCOPE;\ntype LifecycleHandler = (event: unknown, context: ExtensionContext) => unknown;\n\ninterface WorkflowFence {\n readonly pi: ExtensionAPI;\n readonly beginDisposal: () => void;\n readonly finishDisposal: () => void;\n readonly isCurrentInvocation: () => boolean;\n readonly runCleanup: <T>(operation: () => T) => T;\n}\n\nfunction boundValue(target: object, property: PropertyKey): unknown {\n const value = Reflect.get(target, property, target);\n return typeof value === 'function' ? value.bind(target) : value;\n}\n\nfunction withParentSession(ctx: ExtensionContext, parentSessionId: string): ExtensionContext {\n const sessionManager = new Proxy(ctx.sessionManager, {\n get(target, property) {\n if (property === 'getSessionId') return () => parentSessionId;\n return boundValue(target, property);\n },\n });\n return new Proxy(ctx, {\n get(target, property) {\n if (property === 'sessionManager') return sessionManager;\n return boundValue(target, property);\n },\n });\n}\n\n/** Restrict a dispatcher child to discovery and root-owned launch. */\nexport function createDispatcherBridge(\n pi: ExtensionAPI,\n parentSessionId: string | undefined,\n environment: Readonly<Record<string, string | undefined>> = process.env,\n): ExtensionAPI {\n const rootSessionId = parentSessionId ? resolveRootSessionId(parentSessionId, environment) : undefined;\n return new Proxy(pi, {\n get(target, property) {\n if (property === 'setActiveTools') {\n return (names: string[]): void => target.setActiveTools(names.filter((name) => name !== 'workflow_run'));\n }\n if (property !== 'registerTool') return boundValue(target, property);\n return (tool: RegisteredTool): void => {\n if (!DISPATCHER_TOOL_NAMES.has(tool.name)) return;\n if (!rootSessionId || !ROOT_SCOPED_TOOL_NAMES.has(tool.name)) {\n target.registerTool(tool);\n return;\n }\n target.registerTool({\n ...tool,\n execute: (toolCallId, params, signal, onUpdate, ctx) =>\n tool.execute(toolCallId, params, signal, onUpdate, withParentSession(ctx, rootSessionId)),\n });\n };\n },\n });\n}\n\nexport function resolveDispatcherParentSession(\n environment: Readonly<Record<string, string | undefined>>,\n): string | undefined {\n const childContext = readChildProcessContext(environment);\n return childContext?.parentSessionId ?? (environment[SUBAGENT_PARENT_SESSION_ENV]?.trim() || undefined);\n}\n\nexport function isWorkflowDispatcherProcess(environment: Readonly<Record<string, string | undefined>>): boolean {\n const childContext = readChildProcessContext(environment);\n if (childContext?.mode === DISPATCHER_AGENT_NAME) return true;\n return (\n environment.PI_SUBAGENT_CHILD_AGENT === DISPATCHER_AGENT_NAME &&\n Boolean(environment[SUBAGENT_PARENT_SESSION_ENV]?.trim())\n );\n}\n\nfunction inactiveToolResult(): Awaited<ReturnType<RegisteredTool['execute']>> {\n return {\n content: [{ type: 'text', text: INACTIVE_RUNTIME_MESSAGE }],\n details: undefined,\n };\n}\n\n/** Fence every callback retained by Pi after this package root is replaced. */\nfunction createWorkflowFence(host: ExtensionAPI): WorkflowFence {\n const scope = new AsyncLocalStorage<RuntimeScope>();\n let active = true;\n let disposing = false;\n let generation = 0;\n let lastSessionStartEvent: unknown;\n let lastSessionStartContext: ExtensionContext | undefined;\n\n const isCurrentInvocation = (): boolean => {\n if (!active) return false;\n const currentScope = scope.getStore();\n return currentScope === undefined || currentScope === CLEANUP_SCOPE || currentScope === generation;\n };\n const ownsContext = (contextGeneration: number): boolean => {\n if (!active) return false;\n const currentScope = scope.getStore();\n return (\n currentScope === CLEANUP_SCOPE ||\n (contextGeneration === generation && (currentScope === undefined || currentScope === contextGeneration))\n );\n };\n\n const guardContext = <TContext extends ExtensionContext>(context: TContext, contextGeneration: number): TContext => {\n const guardedUi = new Proxy(context.ui, {\n get(target, property) {\n const value = Reflect.get(target, property, target);\n if (typeof value !== 'function') return value;\n return (...args: unknown[]): unknown => {\n if (ownsContext(contextGeneration)) return Reflect.apply(value, target, args);\n if (property === 'confirm') return Promise.resolve(false);\n if (property === 'custom' || property === 'editor' || property === 'input' || property === 'select') {\n return Promise.resolve(undefined);\n }\n return undefined;\n };\n },\n });\n return new Proxy(context, {\n get(target, property) {\n if (property === 'ui') return guardedUi;\n return boundValue(target, property);\n },\n }) as TContext;\n };\n\n const registerTool = (tool: RegisteredTool): void => {\n if (!isCurrentInvocation()) return;\n host.registerTool({\n ...tool,\n execute: async (toolCallId, params, signal, onUpdate, context) => {\n const executionGeneration = generation;\n if (!active || disposing || signal?.aborted) return inactiveToolResult();\n const guardedContext = guardContext(context, executionGeneration);\n const guardedUpdate = onUpdate\n ? (update: Parameters<NonNullable<typeof onUpdate>>[0]): void => {\n if (active && executionGeneration === generation && !signal?.aborted) onUpdate(update);\n }\n : undefined;\n const result = await scope.run(executionGeneration, () =>\n tool.execute(toolCallId, params, signal, guardedUpdate, guardedContext),\n );\n return active && !disposing && executionGeneration === generation ? result : inactiveToolResult();\n },\n });\n };\n\n const registerCommand = (name: string, options: CommandOptions): void => {\n if (!isCurrentInvocation()) return;\n host.registerCommand(name, {\n ...options,\n handler: async (args, context) => {\n const executionGeneration = generation;\n if (!active || disposing) return;\n await scope.run(executionGeneration, () => options.handler(args, guardContext(context, executionGeneration)));\n },\n });\n };\n\n const registerShortcut = (\n shortcut: Parameters<ExtensionAPI['registerShortcut']>[0],\n options: ShortcutOptions,\n ): void => {\n if (!isCurrentInvocation()) return;\n host.registerShortcut(shortcut, {\n ...options,\n handler: (context) => {\n const executionGeneration = generation;\n if (!active || disposing) return;\n return scope.run(executionGeneration, () => options.handler(guardContext(context, executionGeneration)));\n },\n });\n };\n\n const guardedPi = new Proxy(host, {\n get(target, property) {\n if (property === 'on') {\n return (eventName: string, handler: LifecycleHandler): void => {\n const registerLifecycleHandler = boundValue(target, 'on') as (\n name: string,\n callback: LifecycleHandler,\n ) => void;\n registerLifecycleHandler(eventName, (event: unknown, context: ExtensionContext) => {\n if (!active || disposing) return undefined;\n if (\n eventName === SESSION_START_EVENT &&\n (event !== lastSessionStartEvent || context !== lastSessionStartContext)\n ) {\n generation += 1;\n lastSessionStartEvent = event;\n lastSessionStartContext = context;\n }\n const eventGeneration = generation;\n return scope.run(eventGeneration, () => handler(event, guardContext(context, eventGeneration)));\n });\n };\n }\n if (property === 'registerTool') return registerTool;\n if (property === 'registerCommand') return registerCommand;\n if (property === 'registerShortcut') return registerShortcut;\n if (property === 'getActiveTools') {\n return (): string[] => (isCurrentInvocation() ? target.getActiveTools() : []);\n }\n if (property === 'setActiveTools') {\n return (names: string[]): void => {\n if (isCurrentInvocation()) target.setActiveTools(names);\n };\n }\n if (property === 'sendMessage') {\n return (...args: Parameters<ExtensionAPI['sendMessage']>): void => {\n if (isCurrentInvocation()) target.sendMessage(...args);\n };\n }\n if (property === 'sendUserMessage') {\n return (...args: Parameters<ExtensionAPI['sendUserMessage']>): void => {\n if (isCurrentInvocation()) target.sendUserMessage(...args);\n };\n }\n if (property === 'exec') {\n return (...args: Parameters<ExtensionAPI['exec']>): ReturnType<ExtensionAPI['exec']> =>\n isCurrentInvocation()\n ? target.exec(...args)\n : Promise.resolve({ code: 1, killed: false, stderr: INACTIVE_RUNTIME_MESSAGE, stdout: '' });\n }\n return boundValue(target, property);\n },\n });\n\n return {\n pi: guardedPi,\n beginDisposal() {\n if (disposing || !active) return;\n disposing = true;\n generation += 1;\n },\n finishDisposal() {\n active = false;\n disposing = false;\n generation += 1;\n },\n isCurrentInvocation,\n runCleanup: (operation) => scope.run(CLEANUP_SCOPE, operation),\n };\n}\n\n/** Install one normal or dispatcher runtime into its host-owned Cordis plugin fiber. */\nexport function installWorkflowRuntime(\n cordis: Context,\n pi: ExtensionAPI,\n isCurrentInvocation: () => boolean,\n environment: Readonly<Record<string, string | undefined>> = process.env,\n): void {\n const dispatcher = isWorkflowDispatcherProcess(environment);\n const parentSessionId = dispatcher ? resolveDispatcherParentSession(environment) : undefined;\n const runtimePi = dispatcher ? createDispatcherBridge(pi, parentSessionId, environment) : pi;\n\n let runtime: WorkflowPiRuntime | undefined;\n cordis.effect(() => () => (runtime ? runtime.dispose() : Promise.resolve()), `${PACKAGE_SOURCE}/runtime`);\n\n cordis.inject([DOOM_SKILL_SOURCES_SERVICE], (skillContext) => {\n const directory = resolve(dirname(fileURLToPath(import.meta.url)), SKILL_RELATIVE_PATH);\n if (!existsSync(directory)) return undefined;\n const contribution = requireDoomSkillSourcesService(skillContext).register({\n source: PACKAGE_SOURCE,\n directories: [directory],\n });\n return () => contribution.dispose();\n });\n\n if (dispatcher) {\n runtime = installWorkflowPiRuntime({ cordis, initialMode: true, isActive: isCurrentInvocation })(runtimePi);\n return;\n }\n\n let workflowMode = false;\n let leader: ReturnType<typeof registerLeaderContribution> | undefined;\n cordis.inject([DOOM_UI_HUB_SERVICE], (uiContext) => {\n const contribution = registerLeaderContribution(requireDoomUiHub(uiContext), workflowMode);\n leader = contribution;\n return () => {\n contribution.dispose();\n if (leader === contribution) leader = undefined;\n };\n });\n cordis.inject([DOOM_HELP_SERVICE], (helpContext) => {\n const help = requireDoomHelpService(helpContext).register({\n source: PACKAGE_SOURCE,\n moduleUrl: import.meta.url,\n skills: [\n {\n name: 'doompi-author-workflow',\n description:\n 'Author DoomPi workflow definitions. Use when creating or changing a *.workflow.yml graph, arranging job dependencies and host-executed steps, or deciding how a command requiring a TTY should run.',\n },\n {\n name: 'doompi-use-workflow',\n description:\n 'Use DoomPi workflows. Use when discovering or launching workflows, monitoring or controlling asynchronous runs, interpreting terminal notifications, or recovering a failed run safely.',\n },\n ],\n });\n return () => help.dispose();\n });\n runtime = installWorkflowPiRuntime({\n cordis,\n isActive: isCurrentInvocation,\n onModeChange: (enabled) => {\n workflowMode = enabled;\n leader?.setMode(enabled);\n },\n })(runtimePi);\n}\n\ninterface WorkflowPluginConfig {\n readonly fence: WorkflowFence;\n readonly environment: Readonly<Record<string, string | undefined>>;\n}\n\nfunction workflowPlugin(cordis: Context, { fence, environment }: WorkflowPluginConfig): void {\n installWorkflowRuntime(cordis, fence.pi, fence.isCurrentInvocation, environment);\n}\n\n/** The package's sole Pi factory; Pi reloads it and Cordis owns all package resources. */\nexport async function workflowExtension(pi: ExtensionAPI): Promise<void> {\n const connection = await connectDoomCordisHost(pi, PACKAGE_SOURCE);\n const fence = createWorkflowFence(pi);\n const fiber = connection.root.plugin(workflowPlugin, { fence, environment: process.env });\n let disposal: Promise<void> | undefined;\n const dispose = (): Promise<void> => {\n if (disposal) return disposal;\n fence.beginDisposal();\n disposal = fence.runCleanup(async () => {\n try {\n await fiber.dispose();\n } finally {\n try {\n fence.finishDisposal();\n } finally {\n await connection.dispose();\n }\n }\n });\n return disposal;\n };\n\n try {\n await fiber;\n } catch (error) {\n await dispose();\n throw error;\n }\n pi.on(SESSION_SHUTDOWN_EVENT, dispose);\n}\n\nexport default workflowExtension;\n"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const e=require("../../../services/workflowTerminal.cjs"),t=require("../../../types/index.cjs"),n=require("../../../services/backgroundWork.cjs"),r=require("../../../services/workflowNarration.cjs"),i=require("../../../tui/workflow/workflowInspector.cjs"),a=require("../../../tui/workflow/workflowOverlay.cjs"),o=require("../../../tui/workflow/workflowFinishedMessage.cjs"),s=require("../../../tui/workflow/workflowCatalog.cjs"),c=require("../../../tui/workflow/workflowPicker.cjs"),l=require("../../../tui/workflow/workflowProgressOverlay.cjs"),u=require("../../../tui/workflow/workflowPrompt.cjs"),d=require("../../../tui/workflow/workflowRunPanel.cjs"),f=require("../../../tui/workflow/workflowStepMessage.cjs"),p=require("./piToolBridge.cjs"),m=require("./piTools.cjs"),h=require("./workflowLauncher.cjs"),ee=require("../../../services/workflowLaunchCommand.cjs");let te=require("node:crypto"),ne=require("node:fs"),g=require("node:path"),_=require("@agimon-ai/workflow-mcp"),re=require("node:url"),v=require("@agimon-ai/doompi-extension-contracts/child-process"),ie=require("@agimon-ai/doompi-extension-contracts/ui-hub"),y=require("node:child_process"),ae=require("node:fs/promises"),b=require("node:module"),oe=require("@agimon-ai/doompi-extension-contracts/mode"),se=require("@agimon-ai/doompi-extension-contracts/narration"),ce=require("@agimon-ai/doompi-extension-contracts/readiness"),le=require("@agimon-ai/doompi-telemetry"),x=require("@earendil-works/pi-tui");const ue={overlay:!0,overlayOptions:{anchor:`top-left`,width:`100%`,maxHeight:`100%`,margin:0}},de=`workflow-mcp-follow`,fe=`activate`,pe=`deactivate`;function me(e,t){return{activation:e?`active`:`inactive`,condition:`ready`,...e?{detail:t>0?`${t} active runs`:`tools enabled`,color:`accent`}:{},actions:[...e?[{id:fe,enabled:!1,disabledReason:`Workflow mode is active.`}]:[{id:fe,enabled:!0}],...e?[{id:pe,enabled:!0}]:[{id:pe,enabled:!1,disabledReason:`Workflow mode is inactive.`}]]}}const S=`ctrl+alt+w`,C=`ctrl+alt+q`,he={alt:`⌥`,cmd:`⌘`,ctrl:`⌃`,meta:`⌘`,shift:`⇧`};function w(e,t=process.platform){let n=e.split(`+`);return t===`darwin`?n.map(e=>he[e]??e.toUpperCase()).join(``):n.map(e=>e.charAt(0).toUpperCase()+e.slice(1)).join(`+`)}const ge=1e4,T=`running`,E=`completed`,D=`failed`,O=`error`,k=`error`,_e=`doom_workflow.launch_requested`,ve=`../../../../skills`,A={running:`▸`,repair:`⚠`,completed:`✔`,failed:`✖`,separator:`·`};function ye(e,t=process.platform){let n=w(C,t);return e?[`Esc Esc closes this view`,`${w(S,t)} switches typing`,`${n} closes`].join(` ${A.separator} `):`view only ${A.separator} ${n} closes this view`}function be(e,t=process.platform){let n=[w(C,t),`close`];return e?[[`Esc Esc`,`close`],[w(S,t),`switch typing`],n]:[n]}function xe(e,t,n,r){let i=`${A.running} ${e.runKey}`,a=[e.workspace,we(e),e.runner].filter(Boolean).join(` ${A.separator} `),o=`${w(S)} to background`,s=n.fg(`accent`,(0,x.truncateToWidth)(i,r));return(0,x.visibleWidth)(i)+3+(0,x.visibleWidth)(a)<=r&&(s+=` `+n.fg(`muted`,a),(0,x.visibleWidth)(i)+3+(0,x.visibleWidth)(a)+3+(0,x.visibleWidth)(o)<=r&&(s+=` `+n.fg(`dim`,o))),[s,...t.map(e=>(0,x.truncateToWidth)(e,r))]}function Se(e,t,n){let r=[];for(let i of e){let e=i.status===E?A.completed:i.status===D?A.failed:A.running,a=i.status===E?`success`:i.status===D?k:`accent`,o=i.total?` ${A.separator} ${(i.index??0)+1}/${i.total}`:``;if(r.push((0,x.truncateToWidth)(t.fg(a,`${e} ${i.name}`)+t.fg(`dim`,o),n)),i.status===T)for(let e of i.steps){let i=e.status===E?A.completed:e.status===D?A.failed:A.running,a=e.status===T?`muted`:e.status===D?k:`dim`;r.push((0,x.truncateToWidth)(t.fg(a,` ${i} ${e.name}`),n))}}return r}function Ce(e,t){let n=Date.parse(e.startedAt);return Number.isFinite(n)?t.filter(e=>{let t=Date.parse(e.at);return!Number.isFinite(t)||t>=n}):t}function we(e){return e.stale?`stale`:e.stage===O?D:e.stage}const Te=[`CMUX_SURFACE_ID`,v.SUBAGENT_ROOT_SESSION_ENV,`CMUX_WORKSPACE_ID`,`TMUX_PANE`,`WORKFLOW_LAUNCHER_TYPE`,`WORKFLOW_TMUX_SESSION_NAME`,`WORKFLOW_STATUS_FILE`],Ee=`tmux`,De=`cmux`,Oe=`This run points at Pi's own terminal, so its launcher screen is hidden. Use workflow status instead.`;function ke(e){return(0,g.basename)(e,(0,g.extname)(e))}function Ae(){let e={...process.env};for(let t of Te)delete e[t];return e}function je(){let e=process.env.WORKFLOW_LAUNCHER;return e===Ee||e===De?e:void 0}function Me(e){return e.map(e=>`'${e.replaceAll(`'`,`'\\''`)}'`).join(` `)}function Ne(){try{return(0,b.createRequire)(require("url").pathToFileURL(__filename).href).resolve(`@agimon-ai/workflow-mcp/cli`)}catch{return}}const Pe=async(e,t,n)=>{await new Promise((r,i)=>{let a=(0,y.spawn)(e,[...t],{...n,detached:!0,stdio:`ignore`});a.once(`error`,i),a.once(`spawn`,()=>{a.off(`error`,i),a.unref(),r()})})};function j(e,t){if(Array.isArray(e)){for(let n of e)j(n,t);return}if(!(!e||typeof e!=`object`))for(let[n,r]of Object.entries(e))(n===`interactiveRun`||n===`run`)&&r&&typeof r==`object`&&!Array.isArray(r)?t.push(Object.keys(r)):j(r,t)}function Fe(e){let t=[];if(j(e,t),t.length!==0)return t[0].filter(e=>t.every(t=>t.includes(e))).sort()}async function M(e,t){let n=[],r=1,i=!0;for(;i;){let a=await e.listRunsPage({page:r,pageSize:100,workspace:t});n.push(...a.items),i=a.hasNextPage,r+=1}return n}function Ie(e){return[e.runKey,e.workspace,we(e),e.runner].filter(Boolean).join(` ${A.separator} `)}function Le(y={}){return b=>{let x=y.featureFactory?.()??(0,_.createEmbeddedWorkflowFeature)(),he=(0,te.randomUUID)(),D=!0,k=0,Te,j,Le,Re=y.isActive??(()=>!0),N=()=>D&&Re(),ze=e=>{let t=e.diagnostics.join(`; `),n=(e.error?.message??t)||`Initialization did not complete.`,r=`${e.packageId} initialization ${e.state}: ${n}`;H?.hasUI?H.ui.notify(r,`warning`):process.emitWarning(r)},Be=e=>(y.cordis?(0,ce.readDoomReadinessCoordinator)(y.cordis):void 0)||(Le??=(0,ce.createDoomReadinessCoordinator)({notify:ze}),Le),P=async e=>{e?.throwIfAborted();let t=j;if(!t)throw Error(`Workflow session initialization has not started.`);let n=await t;if(e?.throwIfAborted(),await n.wait(e?{signal:e}:void 0),!N()||t!==j)throw Error(`Workflow readiness belongs to a stale extension generation.`)},Ve=(e,t)=>{let n=Reflect.get(e,t,e);return typeof n==`function`?n.bind(e):n},He=new Proxy(b,{get(e,t){return t===`registerTool`?t=>{e.registerTool({...t,execute:async(e,n,r,i,a)=>(await P(r),t.execute(e,n,r,i,a))})}:Ve(e,t)}}),{parser:Ue,recoverService:We,registry:F,runService:Ge,runTool:Ke}=x,qe=y.spawnDetached??Pe,Je=y.monitorIntervalMs??3e4,Ye=y.followIntervalMs??2e3,I,Xe,L,Ze=!1,R,Qe=new Map,z=new Map,$e=new Set,et=0,tt=y.cordis?n.registerRunProvider(y.cordis):void 0,nt=!1,B=0,V=!1,rt,it;y.cordis?.inject([oe.DOOM_MINOR_MODE_CATALOG_SERVICE],e=>{let n=me(V,B),r=(0,oe.registerMinorModeOwner)((0,oe.requireMinorModeCatalog)(e),{descriptor:{source:t.PACKAGE_SOURCE,id:`workflow`,label:`Workflow`,description:`Workflow discovery, launch, inspection, control, and recovery tools.`,order:20,actions:[{id:fe,label:`Activate`,description:`Enable workflow tools for this session.`,contexts:[`tui`,`headless`],parameters:[]},{id:pe,label:`Deactivate`,description:`Disable workflow tools without stopping active runs.`,contexts:[`tui`,`headless`],parameters:[]}]},initialState:n,async handleAction(e,t,n){if(await P(),e===fe)return await on(!0,n.context),{message:`Workflow mode activated.`};if(e===pe)return await on(!1,n.context),{message:`Workflow mode deactivated.`};throw Error(`Unknown workflow mode action: ${e}`)}});return it=r,rt=JSON.stringify(n),()=>{r.dispose(),it===r&&(it=void 0,rt=void 0)}});let H,U,at,ot=!1,st=new Map,W,ct=new Set,G=new Map,lt=[],ut=new Map,dt=0,ft=new l.WorkflowProgressOverlay,pt,mt,K=e=>(mt??=(0,le.createDoomTelemetry)({serviceName:`doom-workflow`,packageName:`@agimon-ai/doompi-workflow`,cwd:e?.cwd,env:process.env,enableLogs:!0,enableTraces:!0}),mt),ht,gt=e=>{K(H).recordWarning(`doom_workflow.narration_failed`,e).catch(e=>{process.emitWarning(`Doom-workflow could not record a narration failure: ${String(e)}`)})},_t={narrate(e){let t=ht,n=(0,se.createNarrationRequest)(e);if(!(!t||!n))try{Promise.resolve(t.request(n)).catch(gt)}catch(e){gt(e)}}};y.cordis?.inject([se.DOOM_NARRATION_SERVICE],e=>{let t=(0,se.requireDoomNarrationService)(e);return ht=t,()=>{ht===t&&(ht=void 0)}});let vt=e=>{e&&N()&&(pt=(0,v.resolveRootSessionId)(e))},yt=()=>{let e=pt??H?.sessionManager.getSessionId();return e?(0,v.resolveRootSessionId)(e):void 0},bt=(e,t)=>e.sessionManager.getSessionId().trim()===t,xt=x.createRecoverTool({ownerSessionId:yt}),St=x.createControlTool(),q=new Set,Ct=e=>{let t=Date.now(),n=K();return q.add(e),n.recordEvent(`doom_workflow.run_started`,{outcome:`started`}),e.then(()=>(q.delete(e),n.recordEvent(`doom_workflow.run_finished`,{duration_ms:Date.now()-t,outcome:`completed`})),r=>(q.delete(e),n.recordError(`doom_workflow.run_failed`,r,{duration_ms:Date.now()-t}))),e},wt=(e,t)=>{let n=Fe(Ue.parseWorkflowFile(e));if(!(!n||n.includes(t)))return`Runner ${JSON.stringify(t)} is not available across this workflow. Compatible runners: ${n.join(`, `)||`none`}.`},Tt=m.createWorkflowLaunchExecutor({activeRunCount:async()=>B,observeSession:vt,rejectRunner:wt,runTool:Ke,trackPendingRun:Ct,...y.launchAckPollMs===void 0?{}:{launchAckPollMs:y.launchAckPollMs},...y.launchAckTimeoutMs===void 0?{}:{launchAckTimeoutMs:y.launchAckTimeoutMs},findLaunchedRun:async({sessionId:e,since:t,workflowPath:n})=>{let r=ke(n),i=n=>{if(!p.isSessionRun(n,e)||n.stale||n.workflowId&&n.workflowId!==r)return!1;let i=Date.parse(n.startedAt);return Number.isFinite(i)&&i>=t-2e3};return W?(await W.refresh(),lt.find(i)):(await M(F)).find(i)},onLateFailure:(e,t)=>{let n=e instanceof Error?e.message:String(e);t.ui.notify(p.launchNotice(`Workflow launch failed after it was reported started.\n${n}`),`warning`)},onLaunch:async e=>(await K(e).recordEvent(_e,{outcome:`requested`}),X(e,!1))}),Et=()=>{let e=[...ut.values()].sort((e,t)=>Date.parse(e.startedAt)-Date.parse(t.startedAt)||Y(e).localeCompare(Y(t)));ft.update(e.map(e=>l.workflowProgressRow(e,G.get(Y(e))??[])))},Dt=e=>{dt+=1;let t=Y(e);e.stage===T&&!e.stale?ut.set(t,e):ut.delete(t),Et()},Ot=e=>{ut.clear();for(let t of e)ut.set(Y(t),t);Et()},kt=(e,t)=>{let n=G.get(e)??[];n.push(t),G.set(e,n)},J,At=e=>{!N()||e!==H||J||(J=setTimeout(()=>{J=void 0,N()&&e===H&&X(e,!0)},120),J.unref?.())},jt=e=>{G.delete(e)},Mt=async e=>{let t=(0,v.resolveRootSessionId)(e.sessionManager.getSessionId()),n=x.createRunControl({recordFilter:e=>p.isSessionRun(e,t)});W=n;let r=()=>N()&&e===H&&W===n,i=[n.on(`step`,t=>{if(!r())return;let n=Y(t.record);kt(n,{at:t.at,job:t.job,status:t.status,step:t.step,type:`step`,...t.reason===void 0?{}:{reason:t.reason}}),Dt(t.record),At(e)}),n.on(`job`,t=>{if(!r())return;let n=Y(t.record);kt(n,{at:t.at,job:t.job,status:t.status,type:`job`,...t.index===void 0?{}:{index:t.index},...t.reason===void 0?{}:{reason:t.reason},...t.total===void 0?{}:{total:t.total}}),Dt(t.record),At(e)}),n.on(`runStarted`,t=>{r()&&(lt.unshift(t.record),lt.length>32&&(lt.length=32),Dt(t.record),At(e))}),n.on(`runUpdated`,t=>{r()&&(Dt(t.record),X(e,!0))}),n.on(`runFinished`,t=>{r()&&(Dt(t.record),X(e,!0))})];for(let e of i)ct.add(e);try{await n.start()}catch(e){for(let e of i)ct.delete(e),e();throw n.dispose(),W===n&&(W=void 0),e}if(!N()||e!==H||W!==n){for(let e of i)ct.delete(e),e();n.dispose(),W===n&&(W=void 0)}},Nt=()=>{J&&clearTimeout(J),J=void 0;for(let e of ct)e();ct.clear(),W?.dispose(),W=void 0,G.clear(),dt+=1,ut.clear(),Et()},Pt=()=>{U?.teardown(),U?.handle?.hide(),U=void 0},Y=e=>e.runId??`${e.workspace}/${e.runKey}`,Ft=()=>{let e=it;if(!e)return;let t=me(V,B),n=JSON.stringify(t);n!==rt&&(rt=n,e.publish(t))},X=async(e,t)=>{let n=k,i=()=>N()&&n===k&&e===H;if(!i())return;let a=K(e);if(Xe===n){a.recordEvent(`doom_workflow.monitor_skipped`,{outcome:`already_running`});return}let s=Date.now(),c=dt;Xe=n;try{let n=await M(F);if(!i())return;let l=(0,v.resolveRootSessionId)(e.sessionManager.getSessionId()),u=bt(e,l),d=n.filter(e=>p.isSessionRun(e,l)),f=d.filter(e=>e.stage===T&&!e.stale),m=d.filter(e=>e.stage===O);c===dt&&Ot(f),a.recordEvent(`doom_workflow.monitor_counts`,{"workflow.run_count":d.length,"workflow.running_count":f.length,"workflow.failed_count":m.length,mode:V?`on`:`off`});let h=p.runsForSession(n,l).filter(e=>!e.stale);B=h.length,Ft(),Vt(new Set(d.map(e=>Y(e))));let ee=new Map(d.map(e=>[Y(e),e.stage]));for(let e of d){let n=Y(e),i=Qe.get(n),a=Date.parse(e.startedAt),o=i===void 0&&(et===0||!Number.isFinite(a)||a>=et);nt&&r.narrateWorkflowTransition(_t,e,i===T||i===E||i===O?i:void 0,o),!(!t||!u||e.stage===T)&&!$e.has(n)&&(i===T||o)&&z.set(n,e)}let te=new Map(h.map(e=>[Y(e),e]));for(let[e,t]of z)te.set(e,t);if(!i())return;tt?.update([...te.values()].map(e=>({id:e.runId??e.runKey,sessionId:l})));let ne=new Set([...d.filter(e=>e.stage===T&&!e.stale).map(e=>Y(e)),...z.keys()]);for(let e of G.keys())ne.has(e)||jt(e);if(t)for(let t of d){if(Qe.get(Y(t))!==T||t.stage===T)continue;let n=t.stage===E,r=t.failedJob?` at ${t.failedJob}`:``;e.ui.notify(n?`${A.completed} ${t.runKey} completed`:`${A.failed} ${t.runKey} ${we(t)}${r}`,n?`info`:`warning`),a.recordEvent(`doom_workflow.run_transition`,{outcome:n?`completed`:we(t)})}if(u&&z.size>0)try{let e=[...z.values()],t=await Promise.all(e.map(async e=>{if(e.stage===E)return p.finishedRunSummary(e,[]);let t=Y(e),n=G.get(t)??Ce(e,await(0,_.readWorkflowProgress)(F.runDirectoryFor(e)));return p.finishedRunSummary(e,(0,_.summarizeWorkflowProgress)(n))}));if(!i()||(b.sendMessage({customType:o.WORKFLOW_FINISHED_MESSAGE,content:t.join(`
|
|
1
|
+
const e=require("../../../services/workflowTerminal.cjs"),t=require("../../../types/index.cjs"),n=require("../../../services/backgroundWork.cjs"),r=require("../../../services/workflowNarration.cjs"),i=require("../../../tui/workflow/workflowInspector.cjs"),a=require("../../../tui/workflow/workflowOverlay.cjs"),o=require("../../../tui/workflow/workflowFinishedMessage.cjs"),s=require("../../../tui/workflow/workflowCatalog.cjs"),c=require("../../../tui/workflow/workflowPicker.cjs"),l=require("../../../tui/workflow/workflowProgressOverlay.cjs"),u=require("../../../tui/workflow/workflowPrompt.cjs"),d=require("../../../tui/workflow/workflowRunPanel.cjs"),f=require("../../../tui/workflow/workflowStepMessage.cjs"),p=require("./piToolBridge.cjs"),m=require("./piTools.cjs"),h=require("./workflowLauncher.cjs"),ee=require("../../../services/workflowLaunchCommand.cjs");let te=require("node:crypto"),ne=require("node:fs"),g=require("node:path"),_=require("@agimon-ai/workflow-mcp"),re=require("node:url"),v=require("@agimon-ai/doompi-extension-contracts/child-process"),ie=require("@agimon-ai/doompi-extension-contracts/ui-hub"),y=require("node:child_process"),ae=require("node:fs/promises"),b=require("node:module"),oe=require("@agimon-ai/doompi-extension-contracts/mode"),se=require("@agimon-ai/doompi-extension-contracts/narration"),ce=require("@agimon-ai/doompi-extension-contracts/readiness"),le=require("@agimon-ai/doompi-telemetry"),x=require("@earendil-works/pi-tui");const ue={overlay:!0,overlayOptions:{anchor:`top-left`,width:`100%`,maxHeight:`100%`,margin:0}},de=`workflow-mcp-follow`,fe=`activate`,pe=`deactivate`;function me(e,t){return{activation:e?`active`:`inactive`,condition:`ready`,...e?{detail:t>0?`${t} active runs`:`tools enabled`,color:`accent`}:{},actions:[...e?[{id:fe,enabled:!1,disabledReason:`Workflow mode is active.`}]:[{id:fe,enabled:!0}],...e?[{id:pe,enabled:!0}]:[{id:pe,enabled:!1,disabledReason:`Workflow mode is inactive.`}]]}}const S=`ctrl+alt+w`,C=`ctrl+alt+q`,he={alt:`⌥`,cmd:`⌘`,ctrl:`⌃`,meta:`⌘`,shift:`⇧`};function w(e,t=process.platform){let n=e.split(`+`);return t===`darwin`?n.map(e=>he[e]??e.toUpperCase()).join(``):n.map(e=>e.charAt(0).toUpperCase()+e.slice(1)).join(`+`)}const ge=1e4,T=`running`,E=`completed`,D=`failed`,O=`error`,k=`error`,_e=`doom_workflow.launch_requested`,ve=`../../../../skills`,A={running:`▸`,repair:`⚠`,completed:`✔`,failed:`✖`,separator:`·`};function ye(e,t=process.platform){let n=w(C,t);return e?[`Esc Esc closes this view`,`${w(S,t)} switches typing`,`${n} closes`].join(` ${A.separator} `):`view only ${A.separator} ${n} closes this view`}function be(e,t=process.platform){let n=[w(C,t),`close`];return e?[[`Esc Esc`,`close`],[w(S,t),`switch typing`],n]:[n]}function xe(e,t,n,r){let i=`${A.running} ${e.runKey}`,a=[e.workspace,we(e),e.runner].filter(Boolean).join(` ${A.separator} `),o=`${w(S)} to background`,s=n.fg(`accent`,(0,x.truncateToWidth)(i,r));return(0,x.visibleWidth)(i)+3+(0,x.visibleWidth)(a)<=r&&(s+=` `+n.fg(`muted`,a),(0,x.visibleWidth)(i)+3+(0,x.visibleWidth)(a)+3+(0,x.visibleWidth)(o)<=r&&(s+=` `+n.fg(`dim`,o))),[s,...t.map(e=>(0,x.truncateToWidth)(e,r))]}function Se(e,t,n){let r=[];for(let i of e){let e=i.status===E?A.completed:i.status===D?A.failed:A.running,a=i.status===E?`success`:i.status===D?k:`accent`,o=i.total?` ${A.separator} ${(i.index??0)+1}/${i.total}`:``;if(r.push((0,x.truncateToWidth)(t.fg(a,`${e} ${i.name}`)+t.fg(`dim`,o),n)),i.status===T)for(let e of i.steps){let i=e.status===E?A.completed:e.status===D?A.failed:A.running,a=e.status===T?`muted`:e.status===D?k:`dim`;r.push((0,x.truncateToWidth)(t.fg(a,` ${i} ${e.name}`),n))}}return r}function Ce(e,t){let n=Date.parse(e.startedAt);return Number.isFinite(n)?t.filter(e=>{let t=Date.parse(e.at);return!Number.isFinite(t)||t>=n}):t}function we(e){return e.stale?`stale`:e.stage===O?D:e.stage}const Te=[`CMUX_SURFACE_ID`,v.SUBAGENT_ROOT_SESSION_ENV,`CMUX_WORKSPACE_ID`,`TMUX_PANE`,`WORKFLOW_LAUNCHER_TYPE`,`WORKFLOW_TMUX_SESSION_NAME`,`WORKFLOW_STATUS_FILE`],Ee=`tmux`,De=`cmux`,Oe=`This run points at Pi's own terminal, so its launcher screen is hidden. Use workflow status instead.`;function ke(e){return(0,g.basename)(e,(0,g.extname)(e))}function Ae(){let e={...process.env};for(let t of Te)delete e[t];return e}function je(){let e=process.env.WORKFLOW_LAUNCHER;return e===Ee||e===De?e:void 0}function Me(e){return e.map(e=>`'${e.replaceAll(`'`,`'\\''`)}'`).join(` `)}function Ne(){try{return(0,b.createRequire)(require("url").pathToFileURL(__filename).href).resolve(`@agimon-ai/workflow-mcp/cli`)}catch{return}}const Pe=async(e,t,n)=>{await new Promise((r,i)=>{let a=(0,y.spawn)(e,[...t],{...n,detached:!0,stdio:`ignore`});a.once(`error`,i),a.once(`spawn`,()=>{a.off(`error`,i),a.unref(),r()})})};function j(e,t){if(Array.isArray(e)){for(let n of e)j(n,t);return}if(e&&typeof e==`object`)for(let[n,r]of Object.entries(e))(n===`interactiveRun`||n===`run`)&&r&&typeof r==`object`&&!Array.isArray(r)?t.push(Object.keys(r)):j(r,t)}function Fe(e){let t=[];if(j(e,t),t.length!==0)return t[0].filter(e=>t.every(t=>t.includes(e))).sort()}async function M(e,t){let n=[],r=1,i=!0;for(;i;){let a=await e.listRunsPage({page:r,pageSize:100,workspace:t});n.push(...a.items),i=a.hasNextPage,r+=1}return n}function Ie(e){return[e.runKey,e.workspace,we(e),e.runner].filter(Boolean).join(` ${A.separator} `)}function Le(y={}){return b=>{let x=y.featureFactory?.()??(0,_.createEmbeddedWorkflowFeature)(),he=(0,te.randomUUID)(),D=!0,k=0,Te,j,Le,Re=y.isActive??(()=>!0),N=()=>D&&Re(),ze=e=>{let t=e.diagnostics.join(`; `),n=(e.error?.message??t)||`Initialization did not complete.`,r=`${e.packageId} initialization ${e.state}: ${n}`;H?.hasUI?H.ui.notify(r,`warning`):process.emitWarning(r)},Be=e=>(y.cordis?(0,ce.readDoomReadinessCoordinator)(y.cordis):void 0)||(Le??=(0,ce.createDoomReadinessCoordinator)({notify:ze}),Le),P=async e=>{e?.throwIfAborted();let t=j;if(!t)throw Error(`Workflow session initialization has not started.`);let n=await t;if(e?.throwIfAborted(),await n.wait(e?{signal:e}:void 0),!N()||t!==j)throw Error(`Workflow readiness belongs to a stale extension generation.`)},Ve=(e,t)=>{let n=Reflect.get(e,t,e);return typeof n==`function`?n.bind(e):n},He=new Proxy(b,{get(e,t){return t===`registerTool`?t=>{e.registerTool({...t,execute:async(e,n,r,i,a)=>(await P(r),t.execute(e,n,r,i,a))})}:Ve(e,t)}}),{parser:Ue,recoverService:We,registry:F,runService:Ge,runTool:Ke}=x,qe=y.spawnDetached??Pe,Je=y.monitorIntervalMs??3e4,Ye=y.followIntervalMs??2e3,I,Xe,L,Ze=!1,R,Qe=new Map,z=new Map,$e=new Set,et=0,tt=y.cordis?n.registerRunProvider(y.cordis):void 0,nt=!1,B=0,V=!1,rt,it;y.cordis?.inject([oe.DOOM_MINOR_MODE_CATALOG_SERVICE],e=>{let n=me(V,B),r=(0,oe.registerMinorModeOwner)((0,oe.requireMinorModeCatalog)(e),{descriptor:{source:t.PACKAGE_SOURCE,id:`workflow`,label:`Workflow`,description:`Workflow discovery, launch, inspection, control, and recovery tools.`,order:20,actions:[{id:fe,label:`Activate`,description:`Enable workflow tools for this session.`,contexts:[`tui`,`headless`],parameters:[]},{id:pe,label:`Deactivate`,description:`Disable workflow tools without stopping active runs.`,contexts:[`tui`,`headless`],parameters:[]}]},initialState:n,async handleAction(e,t,n){if(await P(),e===fe)return await on(!0,n.context),{message:`Workflow mode activated.`};if(e===pe)return await on(!1,n.context),{message:`Workflow mode deactivated.`};throw Error(`Unknown workflow mode action: ${e}`)}});return it=r,rt=JSON.stringify(n),()=>{r.dispose(),it===r&&(it=void 0,rt=void 0)}});let H,U,at,ot=!1,st=new Map,W,ct=new Set,G=new Map,lt=[],ut=new Map,dt=0,ft=new l.WorkflowProgressOverlay,pt,mt,K=e=>(mt??=(0,le.createDoomTelemetry)({serviceName:`doom-workflow`,packageName:`@agimon-ai/doompi-workflow`,cwd:e?.cwd,env:process.env,enableLogs:!0,enableTraces:!0}),mt),ht,gt=e=>{K(H).recordWarning(`doom_workflow.narration_failed`,e).catch(e=>{process.emitWarning(`Doom-workflow could not record a narration failure: ${String(e)}`)})},_t={narrate(e){let t=ht,n=(0,se.createNarrationRequest)(e);if(t&&n)try{Promise.resolve(t.request(n)).catch(gt)}catch(e){gt(e)}}};y.cordis?.inject([se.DOOM_NARRATION_SERVICE],e=>{let t=(0,se.requireDoomNarrationService)(e);return ht=t,()=>{ht===t&&(ht=void 0)}});let vt=e=>{e&&N()&&(pt=(0,v.resolveRootSessionId)(e))},yt=()=>{let e=pt??H?.sessionManager.getSessionId();return e?(0,v.resolveRootSessionId)(e):void 0},bt=(e,t)=>e.sessionManager.getSessionId().trim()===t,xt=x.createRecoverTool({ownerSessionId:yt}),St=x.createControlTool(),q=new Set,Ct=e=>{let t=Date.now(),n=K();return q.add(e),n.recordEvent(`doom_workflow.run_started`,{outcome:`started`}),e.then(()=>(q.delete(e),n.recordEvent(`doom_workflow.run_finished`,{duration_ms:Date.now()-t,outcome:`completed`})),r=>(q.delete(e),n.recordError(`doom_workflow.run_failed`,r,{duration_ms:Date.now()-t}))),e},wt=(e,t)=>{let n=Fe(Ue.parseWorkflowFile(e));if(n&&!n.includes(t))return`Runner ${JSON.stringify(t)} is not available across this workflow. Compatible runners: ${n.join(`, `)||`none`}.`},Tt=m.createWorkflowLaunchExecutor({activeRunCount:async()=>B,observeSession:vt,rejectRunner:wt,runTool:Ke,trackPendingRun:Ct,...y.launchAckPollMs===void 0?{}:{launchAckPollMs:y.launchAckPollMs},...y.launchAckTimeoutMs===void 0?{}:{launchAckTimeoutMs:y.launchAckTimeoutMs},findLaunchedRun:async({sessionId:e,since:t,workflowPath:n})=>{let r=ke(n),i=n=>{if(!p.isSessionRun(n,e)||n.stale||n.workflowId&&n.workflowId!==r)return!1;let i=Date.parse(n.startedAt);return Number.isFinite(i)&&i>=t-2e3};return W?(await W.refresh(),lt.find(i)):(await M(F)).find(i)},onLateFailure:(e,t)=>{let n=e instanceof Error?e.message:String(e);t.ui.notify(p.launchNotice(`Workflow launch failed after it was reported started.\n${n}`),`warning`)},onLaunch:async e=>(await K(e).recordEvent(_e,{outcome:`requested`}),X(e,!1))}),Et=()=>{let e=[...ut.values()].sort((e,t)=>Date.parse(e.startedAt)-Date.parse(t.startedAt)||Y(e).localeCompare(Y(t)));ft.update(e.map(e=>l.workflowProgressRow(e,G.get(Y(e))??[])))},Dt=e=>{dt+=1;let t=Y(e);e.stage===T&&!e.stale?ut.set(t,e):ut.delete(t),Et()},Ot=e=>{ut.clear();for(let t of e)ut.set(Y(t),t);Et()},kt=(e,t)=>{let n=G.get(e)??[];n.push(t),G.set(e,n)},J,At=e=>{N()&&e===H&&!J&&(J=setTimeout(()=>{J=void 0,N()&&e===H&&X(e,!0)},120),J.unref?.())},jt=e=>{G.delete(e)},Mt=async e=>{let t=(0,v.resolveRootSessionId)(e.sessionManager.getSessionId()),n=x.createRunControl({recordFilter:e=>p.isSessionRun(e,t)});W=n;let r=()=>N()&&e===H&&W===n,i=[n.on(`step`,t=>{if(!r())return;let n=Y(t.record);kt(n,{at:t.at,job:t.job,status:t.status,step:t.step,type:`step`,...t.reason===void 0?{}:{reason:t.reason}}),Dt(t.record),At(e)}),n.on(`job`,t=>{if(!r())return;let n=Y(t.record);kt(n,{at:t.at,job:t.job,status:t.status,type:`job`,...t.index===void 0?{}:{index:t.index},...t.reason===void 0?{}:{reason:t.reason},...t.total===void 0?{}:{total:t.total}}),Dt(t.record),At(e)}),n.on(`runStarted`,t=>{r()&&(lt.unshift(t.record),lt.length>32&&(lt.length=32),Dt(t.record),At(e))}),n.on(`runUpdated`,t=>{r()&&(Dt(t.record),X(e,!0))}),n.on(`runFinished`,t=>{r()&&(Dt(t.record),X(e,!0))})];for(let e of i)ct.add(e);try{await n.start()}catch(e){for(let e of i)ct.delete(e),e();throw n.dispose(),W===n&&(W=void 0),e}if(!N()||e!==H||W!==n){for(let e of i)ct.delete(e),e();n.dispose(),W===n&&(W=void 0)}},Nt=()=>{J&&clearTimeout(J),J=void 0;for(let e of ct)e();ct.clear(),W?.dispose(),W=void 0,G.clear(),dt+=1,ut.clear(),Et()},Pt=()=>{U?.teardown(),U?.handle?.hide(),U=void 0},Y=e=>e.runId??`${e.workspace}/${e.runKey}`,Ft=()=>{let e=it;if(!e)return;let t=me(V,B),n=JSON.stringify(t);n!==rt&&(rt=n,e.publish(t))},X=async(e,t)=>{let n=k,i=()=>N()&&n===k&&e===H;if(!i())return;let a=K(e);if(Xe===n){a.recordEvent(`doom_workflow.monitor_skipped`,{outcome:`already_running`});return}let s=Date.now(),c=dt;Xe=n;try{let n=await M(F);if(!i())return;let l=(0,v.resolveRootSessionId)(e.sessionManager.getSessionId()),u=bt(e,l),d=n.filter(e=>p.isSessionRun(e,l)),f=d.filter(e=>e.stage===T&&!e.stale),m=d.filter(e=>e.stage===O);c===dt&&Ot(f),a.recordEvent(`doom_workflow.monitor_counts`,{"workflow.run_count":d.length,"workflow.running_count":f.length,"workflow.failed_count":m.length,mode:V?`on`:`off`});let h=p.runsForSession(n,l).filter(e=>!e.stale);B=h.length,Ft(),Vt(new Set(d.map(e=>Y(e))));let ee=new Map(d.map(e=>[Y(e),e.stage]));for(let e of d){let n=Y(e),i=Qe.get(n),a=Date.parse(e.startedAt),o=i===void 0&&(et===0||!Number.isFinite(a)||a>=et);nt&&r.narrateWorkflowTransition(_t,e,i===T||i===E||i===O?i:void 0,o),t&&u&&e.stage!==T&&!$e.has(n)&&(i===T||o)&&z.set(n,e)}let te=new Map(h.map(e=>[Y(e),e]));for(let[e,t]of z)te.set(e,t);if(!i())return;tt?.update([...te.values()].map(e=>({id:e.runId??e.runKey,sessionId:l})));let ne=new Set([...d.filter(e=>e.stage===T&&!e.stale).map(e=>Y(e)),...z.keys()]);for(let e of G.keys())ne.has(e)||jt(e);if(t)for(let t of d){if(Qe.get(Y(t))!==T||t.stage===T)continue;let n=t.stage===E,r=t.failedJob?` at ${t.failedJob}`:``;e.ui.notify(n?`${A.completed} ${t.runKey} completed`:`${A.failed} ${t.runKey} ${we(t)}${r}`,n?`info`:`warning`),a.recordEvent(`doom_workflow.run_transition`,{outcome:n?`completed`:we(t)})}if(u&&z.size>0)try{let e=[...z.values()],t=await Promise.all(e.map(async e=>{if(e.stage===E)return p.finishedRunSummary(e,[]);let t=Y(e),n=G.get(t)??Ce(e,await(0,_.readWorkflowProgress)(F.runDirectoryFor(e)));return p.finishedRunSummary(e,(0,_.summarizeWorkflowProgress)(n))}));if(!i()||(b.sendMessage({customType:o.WORKFLOW_FINISHED_MESSAGE,content:t.join(`
|
|
2
2
|
|
|
3
3
|
`),display:!0,details:{runIds:e.map(e=>e.runId??Y(e)),runs:e.map(e=>({runKey:e.runKey,workspace:e.workspace,stage:e.stage,...e.workflowId?{workflowId:e.workflowId}:{},...e.failedJob?{failedJob:e.failedJob}:{},...e.errorMessage?{error:e.errorMessage}:{}}))}},{triggerTurn:!0,deliverAs:`steer`}),!i()))return;for(let t of e){let e=Y(t);$e.add(e),z.delete(e),jt(e)}tt?.update(h.map(e=>({id:e.runId??e.runKey,sessionId:l})))}catch(e){await a.recordError(`doom_workflow.terminal_delivery_failed`,e,{"workflow.pending_terminal_count":z.size})}if(!i())return;Qe=ee,a.recordEvent(`doom_workflow.monitor_finished`,{duration_ms:Date.now()-s,outcome:`completed`})}catch(t){await a.recordError(`doom_workflow.monitor_failed`,t,{duration_ms:Date.now()-s}),i()&&e.ui.notify(`Workflow status refresh failed: ${t instanceof Error?t.message:String(t)}`,`warning`)}finally{Xe===n&&(Xe=void 0)}},It=e=>{L&&clearInterval(L),L=void 0,R=void 0,e.ui.setWidget(de,void 0)},Z=new _.WorkflowTerminalService({run:async(e,t,n)=>{let r=await b.exec(e,[...t],{timeout:n.timeoutMs});return{code:r.code,stdout:r.stdout,stderr:r.stderr}},lines:24,timeoutMs:ge}),Lt=e.createWorkflowTerminalService({terminal:Z,now:()=>Date.now(),refreshMs:750}),Rt=new Map,zt=(e,t,n)=>{let r=Y(e),i=`${t}x${n}`;Rt.get(r)!==i&&(Rt.set(r,i),Z.resize(e,t,n))},Bt=e=>Z.targetsCurrentTerminal(e)?Promise.resolve([Oe]):Lt.screen(Y(e),e,24),Vt=e=>{Lt.forget(e);for(let t of Rt.keys())e.has(t)||Rt.delete(t)},Ht=async e=>{let t=e.launcher;if(!t)return`No durable launcher identity is recorded for this run.`;if(Z.targetsCurrentTerminal(e))return Oe;if(t.type===`native`)return`This run is hosted natively rather than in a multiplexer, so there is no separate window to bring forward.`;if(t.type===De)return await b.exec(De,[`select-workspace`,`--workspace`,t.workspaceId],{timeout:ge}),`Opened ${e.runKey} in cmux.`;if(!process.env.TMUX)return`Pi is not attached to tmux. Follow the output instead, or attach to the recorded tmux session manually.`;let n=t.sessionId??t.sessionName,r=await b.exec(Ee,[`switch-client`,`-t`,n],{timeout:ge});return r.code===0?(t.paneId&&await b.exec(Ee,[`select-pane`,`-t`,t.paneId],{timeout:ge}),`Opened ${e.runKey} in tmux.`):r.stderr.trim()||`Unable to open tmux session ${n}.`},Ut=async(e,t)=>{if(!Ze){Ze=!0;try{let n=(0,_.summarizeWorkflowProgress)(Ce(e,await(0,_.readWorkflowProgress)(F.runDirectoryFor(e)))),r=n.length>0?void 0:await Bt(e);t.ui.setWidget(de,(t,i)=>({render:t=>xe(e,r??Se(n,i,t),i,t),invalidate:()=>{}}),{placement:`aboveEditor`})}finally{Ze=!1}}},Wt=async(e,t)=>{It(t),R=e,await Ut(e,t),L=setInterval(()=>{R&&Ut(R,t)},Ye),L.unref?.()},Gt=async(e,t)=>{Z.capabilities(e).writable&&await Z.write(e,t)},Kt=async(e,t)=>{Pt();let n=Z.capabilities(e).writable,r=new a.TerminalInputBatcher(t=>Gt(e,t)),i=[],o=[],s,c=!1,l=async()=>{i=(0,_.summarizeWorkflowProgress)(Ce(e,await(0,_.readWorkflowProgress)(F.runDirectoryFor(e)))),o=await Bt(e)};await l();let u=()=>{s&&clearInterval(s),s=void 0,r.dispose()},f={interactive:n,runKey:e.runKey,teardown:u};U=f;let p=()=>{Pt(),t.ui.notify(`Closed the ${e.runKey} view. The run keeps going.`,`info`)};return t.ui.custom((a,m,h,ee)=>(s=setInterval(()=>{c||(c=!0,l().finally(()=>{c=!1,a.requestRender()}))},200),s.unref?.(),new d.WorkflowRunPanelComponent(a,m,{runKey:e.runKey,label:`${A.running} ${Ie(e)}`,breadcrumb:`SPC › w / workflows › run`,interactive:n,footer:ye(n),hints:be(n),snapshot:e=>({progress:Se(i,m,e),output:o}),onViewport:(t,n)=>zt(e,t,n),onUnfollow:()=>{f.handle?.unfocus(),t.ui.notify(`Typing goes to this session. The run keeps going.`,`info`)},onClose:p,sendInput:e=>r.write(e),flushInput:()=>void r.flush(),unfollowShortcut:S,closeShortcut:C,onDispose:()=>{u(),U===f&&(U=void 0),ee()}})),{...d.runPanelUiOptions(n),onHandle:e=>{f.handle=e}}),t.ui.notify(n?`${e.runKey}: typing goes to the run. Press Esc twice to close, ${w(S)} to type here instead.`:`${e.runKey}: view only, because this run is hosted natively. ${w(C)} closes it.`,`info`),n?[`Showing ${e.runKey} over Pi. Typing goes to the run.`,`Pressing Escape twice quickly closes the view, and a single Escape still reaches the run.`,`${w(S)} moves typing between the run and this session,`,`and ${w(C)} closes the view. The run keeps going either way.`].join(` `):[`Showing ${e.runKey} over Pi, as a view only:`,`the run is hosted natively rather than in a multiplexer, so there is no terminal to type into.`,`Typing stays with this session, and ${w(C)} closes the view.`].join(` `)},qt=async(e,t=!1)=>{if(!(ot||U||R)){ot=!0;try{let n=await e.ui.custom((e,t,n,r)=>new i.WorkflowInspectorComponent(e,t,{list:async()=>{let e=yt(),t=(await M(F)).filter(t=>t.stage===T&&!t.stale&&p.isSessionRun(t,e));return st=new Map(t.map(e=>[Y(e),e])),Promise.all(t.map(async e=>({displayName:e.displayName,executionState:e.executionState,jobs:(0,_.summarizeWorkflowProgress)(Ce(e,await(0,_.readWorkflowProgress)(F.runDirectoryFor(e)))),key:Y(e),output:[],runKey:e.runKey,startedAt:e.startedAt,workspace:e.workspace})))},output:async e=>{let t=st.get(e.key);return t?Bt(t):[`Workflow is no longer active.`]}},r),{...ue,onHandle:e=>{at=e}});if(n){let r=await Q(n);t?await Qt(r,e):await Kt(r,e)}}finally{at=void 0,ot=!1,st.clear()}}},Q=async(e,t=yt())=>{vt(t);let n=(await M(F,e.workspace)).filter(n=>n.runKey===e.runKey&&p.isSessionRun(n,t));if(n.length>1)throw Error(p.withOptions(`Workflow run key ${JSON.stringify(e.runKey)} exists in more than one workspace in this session.`,[`Retry with the workspace from the run identity.`]));let r=n[0];if(!r)throw Error(p.withOptions(`No workflow run matches ${JSON.stringify(e.runKey)} in this session.`,[`workflow_run with action status: use a run key from this session before retrying.`,`Tell the user this session did not launch that run rather than guessing a similar key. A run launched by another Pi session, or from the command line, is managed there or with the workflow-mcp CLI.`]));return r},Jt=async e=>{let t=(await M(F,e.workspace)).filter(t=>t.runKey===e.runKey&&t.stage===O);if(t.length>1)throw Error(p.withOptions(`Failed workflow run key ${JSON.stringify(e.runKey)} exists in more than one workspace.`,[`Retry with the workspace from the recovery picker or run identity.`]));let n=t[0];if(!n)throw Error(p.withOptions(`No failed workflow run matches ${JSON.stringify(e.runKey)}.`,[`Use the recovery picker to select an interrupted or failed run, or retry with its workspace.`,`Do not recover a running or completed workflow.`]));return n},Yt=async e=>{let t=F.runDirectoryFor(e),n=[(0,g.resolve)(t,`changelog.md`),(0,g.resolve)(t,`context.md`),(0,g.resolve)(t,`progress.ndjson`)],r=[];for(let e of n)try{let t=await(0,ae.readFile)(e,`utf-8`);r.push(`--- ${(0,g.basename)(e)} ---`,t.slice(-65536).trimEnd())}catch(e){if(e.code!==`ENOENT`)throw e}return r.length>0?r:[`No durable recovery evidence files are recorded for this run.`]},Xt=async(e,t)=>{let n=await Jt(e);return await K(t).recordEvent(`doom_workflow.recovery_evidence_requested`,{outcome:`requested`}),[`Workflow: ${Ie(n)}`,`--- run.json ---`,JSON.stringify(n,null,2),...await Yt(n)].join(`
|
|
4
4
|
`)},Zt=async(e,t)=>{let n=Ne();if(!n)return;let r=await Jt(e),i;try{i=Ue.parseWorkflowFile(r.workflowPath)}catch{return}if(!i[`launch-command`])return;let a=(0,v.resolveRootSessionId)(t.sessionManager.getSessionId()),o=await F.claimRunRecovery(r.workspace,r.runKey,a),s=r.originalRepoPath??t.cwd,c=Me([process.execPath,n,`recover-workflow`,r.runKey,`--workspace`,r.workspace,`--recovery-claim`,o.claimId,...e.runner?[`--runner`,e.runner]:[]]),l=je();try{await qe(process.execPath,[n,`launch-process`,...l?[`--launcher`,l]:[],`--name`,`${r.displayName} recover`,`--cwd`,s,`--command`,c],{cwd:s,env:Ae()})}catch(e){throw await F.releaseRunRecoveryClaim(r.workspace,r.runKey,o.claimId),e}return await K(t).recordEvent(`doom_workflow.recover_delegated`,{launcher:l??`auto`,outcome:`delegated`}),`Recovering ${r.runKey} in a launcher of its own, so this session keeps its terminal. It reports back here when it ends.`},Qt=async(e,t)=>{let n=e;for(;;){let e=[`Open output`],r=n.executionState??T;r===T&&e.push(`Pause`),r===`paused`&&e.push(`Resume`),n.stage===T&&e.push(`Stop`),e.push(`Back`);let i=await u.openWorkflowChoice(t,{title:`Manage ${n.runKey}`,breadcrumb:`SPC › w / workflows › r / runs`,choices:e});if(!i||i===`Back`)return;if(n=await Q(n),n.stage!==T){t.ui.notify(`${n.runKey} is no longer running.`,`warning`);return}if(i===`Open output`){await Kt(n,t);return}if(i===`Stop`){if(!await t.ui.confirm(`Stop ${n.runKey}?`,`The workflow will be interrupted and will not be restarted automatically.`))continue;if(n=await Q(n),n.stage!==T){t.ui.notify(`${n.runKey} is no longer running.`,`warning`);return}}if(i!==`Pause`&&i!==`Resume`&&i!==`Stop`)continue;if(!n.runId)throw Error(`${n.runKey} has no controllable run generation.`);let a=await St.execute({action:i.toLowerCase(),expectedRunId:n.runId,...i===`Stop`?{reason:`Stopped from the workflow manager.`}:{},runKey:n.runKey,workspace:n.workspace});if(a.isError)throw Error(p.toolResultText(a)||`Unable to ${i.toLowerCase()} workflow.`);t.ui.notify(p.toolResultText(a)||`${i} requested for ${n.runKey}.`,`info`),await X(t,!1),n=await Q(n)}},$t=(e,t)=>({editor:(t,n)=>e.ui.editor(t,n),input:(n,r)=>u.openWorkflowInput(e,{title:n,breadcrumb:t,...r?{fallback:r}:{}}),notify:(t,n)=>e.ui.notify(t,n),select:(n,r)=>u.openWorkflowChoice(e,{title:n,breadcrumb:t,choices:r})}),en=async e=>{if(!e.hasUI){e.ui.notify(`The workflow list needs an interactive Pi session.`,`warning`);return}let t=await h.loadWorkflowCatalog(x.listWorkflowsTool,e.cwd),n=new Map(t.map(e=>[e.path,e])),r={compatibleRunners:Fe,launch:(e,t)=>Tt.execute(e,t),parseWorkflow:e=>Ue.parseWorkflowFile(e),ui:$t(e,`SPC › w / workflows › l / list`)};await s.openWorkflowCatalogOverlay(e,t.map(e=>({key:e.path,name:e.name,relativePath:e.relativePath,description:e.description,tags:e.tags})),{loadDetail:e=>h.summarizeWorkflowFile(e.key,r),launchWorkflow:t=>{let i=n.get(t.key);i&&h.launchWorkflowEntry(r,i,e).then(t=>{t&&e.ui.notify(p.launchNotice(p.toolResultText(t)),`info`)},t=>{e.ui.notify(t instanceof Error?t.message:String(t),`error`)})}})},tn=async(e,t)=>{let n=ee.parseWorkflowLaunchCommand(e);if(ee.isLaunchParseFailure(n)){t.ui.notify(n.error,`error`);return}let r=await h.loadWorkflowCatalog(x.listWorkflowsTool,t.cwd),i=ee.resolveWorkflowEntry(r,n.workflow);if(!i){t.ui.notify(`No workflow matches '${n.workflow}' under ${t.cwd}.`,`error`);return}let a=h.summarizeWorkflowFile(i.path,{compatibleRunners:Fe,parseWorkflow:e=>Ue.parseWorkflowFile(e)});if(a.error!==void 0){t.ui.notify(`${i.name} could not be read: ${a.error}`,`error`);return}let o=ee.validateWorkflowLaunch(a,n);if(o.length>0){t.ui.notify(o.join(` `),`error`);return}let s=Ue.parseWorkflowFile(i.path),c={workflowPath:i.path,...n.runner===void 0?{}:{runner:n.runner},...typeof s.workspace==`string`?{workspace:s.workspace}:{},...n.prompt===void 0?{}:{prompt:n.prompt},...Object.keys(n.inputs).length===0?{}:{inputs:n.inputs}},l=await Tt.execute(c,t);t.ui.notify(p.launchNotice(p.toolResultText(l)),`info`)};b.registerCommand(`workflow-launch`,{description:`Launch a workflow: /workflow-launch <workflow> [runner=x] [key=value …] [prompt]`,handler:async(e,t)=>{await P();try{await tn(e,t)}catch(e){t.ui.notify(e instanceof Error?e.message:String(e),`error`)}}});let nn=async e=>{let t=(await M(F)).filter(e=>e.stage===O);if(t.length===0){e.ui.notify(`No failed workflows are available to recover.`,`info`);return}let n=await c.openWorkflowPickerOverlay(e,{title:`WORKFLOW RECOVERY`,breadcrumb:`SPC › w / workflows › c / recover`,unit:`failed runs`,action:`recover`,filterPlaceholder:`type to filter by name, run key or failed job`,emptyMessage:`No failed workflows are available to recover.`},t.map(e=>({key:`${e.workspace}/${e.runKey}`,name:e.displayName,detail:`${e.workspace}/${e.runKey}${e.failedJob?` · ${e.failedJob}`:``}`,...e.failedJob?{search:e.failedJob}:{},value:e})));if(!n)return;let r=await Jt(n);if(r.stage!==O)throw Error(`${r.runKey} is no longer failed.`);let i=(0,g.resolve)((0,g.dirname)((0,re.fileURLToPath)(require("url").pathToFileURL(__filename).href)),ve,`workflow-recovery`,`SKILL.md`);if(!(0,ne.existsSync)(i))throw Error(`Workflow recovery skill is unavailable at ${i}.`);rn(!0),e.ui.notify(`Workflow tools enabled. The agent will investigate this failure and choose the safe next step.`,`info`),b.sendUserMessage([`Investigate recovery for this failed workflow run. This selection is permission to investigate, not permission to recover blindly.`,`Run identity: ${JSON.stringify({runKey:r.runKey,workspace:r.workspace})}`,`Read the package-owned recovery skill with the read tool at ${JSON.stringify(i)} before acting.`,`Inspect status, every relevant log source, process state, the staged run.json, and only the repair referenced by activeRepairId.`,`Choose exactly one outcome: recover, launch fresh, or defer. Never edit issue.md or repair.json, never scan historical repairs, and never pass recovery dryRun or a job override.`,`Do not recover terminal, approval-pending, blocked, or otherwise unsafe work. If evidence is incomplete, defer and explain what is missing.`,`If recovery is justified, preserve the recorded worktree and repair metadata, then verify real process and registry progress before reporting success.`].join(`
|