@agimon-ai/doompi-workflow 0.0.1-alpha.28 → 0.0.1-alpha.29

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/README.md CHANGED
@@ -1,10 +1,12 @@
1
1
  # @agimon-ai/doompi-workflow
2
2
 
3
- Asynchronous workflow discovery, launch, monitoring, control, and terminal-failure recovery for DoomPi.
3
+ Asynchronous workflow discovery, launch, monitoring, control, and recovery from terminal failures
4
+ for DoomPi.
4
5
 
5
6
  Part of the [DoomPi distribution](https://www.npmjs.com/package/@agimon-ai/doompi).
6
7
 
7
- The integration embeds `@agimon-ai/workflow-mcp`; workflow files describe job dependencies and host-executed steps while DoomPi provides session-scoped tools and TUI surfaces.
8
+ The integration embeds `@agimon-ai/workflow-mcp`. Workflow files describe job dependencies and
9
+ host-executed steps, while DoomPi provides session-scoped tools and TUI surfaces.
8
10
 
9
11
  > **Alpha:** workflow and recovery contracts may change between releases.
10
12
 
@@ -22,7 +24,8 @@ enabled. For standalone Pi:
22
24
  pi install npm:@agimon-ai/doompi-workflow
23
25
  ```
24
26
 
25
- Enable tools with `SPC w e`, or set `WORKFLOW_MCP_MODE=on` for a non-interactive harness that cannot toggle the minor mode.
27
+ Enable tools with `SPC w e`, or set `WORKFLOW_MCP_MODE=on` for a non-interactive harness that cannot
28
+ toggle the minor mode.
26
29
 
27
30
  ## Define a workflow
28
31
 
@@ -46,39 +49,63 @@ jobs:
46
49
  run: node scripts/write-verification-summary.mjs
47
50
  ```
48
51
 
49
- `run` commands execute on the host with the workflow process's environment and privileges. Review workflow files as executable code. Runner-specific `interactiveRun` mappings are available for commands that genuinely require a TTY.
52
+ The GitHub-style `runs-on` value does not create an Ubuntu VM or container. `run` commands execute
53
+ on the host with the workflow process's environment and privileges. Review workflow files as
54
+ executable code. Runner-specific `interactiveRun` mappings are available for commands that require
55
+ a TTY.
50
56
 
51
57
  ## Launch and monitor
52
58
 
53
59
  Core tools are:
54
60
 
55
- - `list_workflows`: discover workflows;
56
- - `launch_workflow`: register and start an asynchronous run;
61
+ - `list_workflows`: discover workflows.
62
+ - `launch_workflow`: register and start an asynchronous run.
57
63
  - `workflow_run`: inspect or control a run through supported actions.
58
64
 
59
- Launch returns after the run is registered, not after all jobs complete. Use status/follow controls for progress and wait for a terminal notification.
65
+ Launch returns after the run is registered, not after all jobs complete. Use status and follow
66
+ controls for progress, and wait for a terminal notification.
60
67
 
61
- In the TUI, `SPC w w` launches, `SPC w l` manages runs, `SPC w r` opens recovery, and `SPC w e` toggles model-visible tools. The root session can launch; child sessions can inspect the catalog but do not receive an unrestricted workflow factory.
68
+ In the TUI, `SPC w w` launches, `SPC w l` manages runs, `SPC w r` opens recovery, and `SPC w e`
69
+ toggles model-visible tools. The root session can launch; child sessions can inspect the catalog but
70
+ do not receive an unrestricted workflow factory.
62
71
 
63
72
  ## Storage, concurrency, and lifecycle
64
73
 
65
- Registry data defaults to `$HOME/.workflow-mcp`; set `WORKFLOW_MCP_HOME` to relocate it. Persisted records contain workflow/run identity, job and step state, ownership, output locations, and recovery evidence. The default concurrency ceiling is five runs.
74
+ Registry data defaults to `$HOME/.workflow-mcp`; set `WORKFLOW_MCP_HOME` to relocate it. Persisted
75
+ records contain workflow and run identity, job and step state, ownership, output locations, and
76
+ recovery evidence. The default concurrency ceiling is five runs.
66
77
 
67
- Runs embedded directly in the Pi process end with that process. Runs launched through a terminal host can have a different lifetime and remain CLI-manageable. Active session runs are contributed through Team's injected `doom/background-work` service; the contribution rebinds after provider replacement and retracts with Workflow. Reconciliation distinguishes stale registry records from live processes; do not assume every run outlives its parent session.
78
+ Runs embedded directly in the Pi process end with that process. Runs launched through a terminal
79
+ host such as tmux or cmux can have a different lifetime and remain manageable from the CLI. When
80
+ Team is loaded, Workflow contributes active session runs through Team's `doom/background-work`
81
+ service. That contribution reconnects after Team is replaced and is removed when Workflow unloads.
82
+ Reconciliation distinguishes stale registry records from live processes; do not assume every run
83
+ outlives its parent session.
68
84
 
69
- Each workflow or repair step can launch commands and model-backed agents, consuming provider quota and repeating external side effects.
85
+ Each workflow or repair step can launch commands and model-backed agents, consuming provider quota
86
+ and repeating external side effects.
70
87
 
71
88
  ## Recovery
72
89
 
73
- Recovery is available only for terminal failed records. A recovery claim atomically adopts the eligible failure, validates the evidence, and transfers ownership to the replay session. Live controls remain scoped to the owning session. Recovery does not launch a second copy beside a still-running job and is not a blanket promise that parent-session work survives shutdown.
90
+ Recovery is available only for records in a terminal failure state. A recovery claim atomically
91
+ adopts the eligible failure, validates the evidence, and transfers ownership to the replay session.
92
+ Live controls remain scoped to the owning session. Recovery does not launch a second copy beside a
93
+ still-running job and does not guarantee that parent-session work survives shutdown.
74
94
 
75
- The package publishes `workflow-recovery` for active Workflow mode and contributes `workflow-help` while parent Help mode is active. Help deactivation logically hides the descriptor; cached files may remain.
95
+ The package publishes `workflow-recovery` for active Workflow mode. While parent Help mode is
96
+ active, it also contributes `doompi-author-workflow` for writing workflow definitions and
97
+ `doompi-use-workflow` for launching, monitoring, and recovering runs. Deactivating Help hides both
98
+ descriptors; cached files may remain.
76
99
 
77
- Every `extensions/pi` factory invocation joins the runner-scoped Cordis host and mounts one Workflow plugin fiber. Pi owns reload and invokes `session_shutdown`; Workflow then fences retained callbacks, interrupts bounded inline work, releases Cordis service and UI registrations, disposes its plugin fiber, and finally releases its host lease. Dispatcher children load the same host/finalizer graph in their own process, then use this factory's typed child role without an alternate extension entry.
100
+ Each Pi extension instance uses the runner's shared Doom lifecycle. Reload or shutdown stops retained
101
+ callbacks, interrupts bounded inline work, removes UI and service registrations, and releases
102
+ package resources. Detached children load the same extension entry in their own process.
78
103
 
79
104
  ## Pi extension
80
105
 
81
- The sole factory is published at `@agimon-ai/doompi-workflow/extensions/pi` and declared in `package.json.pi.extensions`. Package installation loads it automatically; there is no secondary registration or dispatcher export.
106
+ The Pi extension is published at `@agimon-ai/doompi-workflow/extensions/pi` and declared in
107
+ `package.json.pi.extensions`. Installing the package loads it automatically; there is no secondary
108
+ registration or dispatcher export.
82
109
 
83
110
  ## Development
84
111
 
@@ -1,2 +1,2 @@
1
- const e=require("./leader.cjs"),t=require("./workflow/piExtension.cjs");let n=require("node:async_hooks"),r=require("node:fs"),i=require("node:path"),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 n.AsyncLocalStorage,r=!0,i=!1,a=0,o,s,c=()=>{if(!r)return!1;let e=t.getStore();return e===void 0||e===m||e===a},l=e=>{if(!r)return!1;let n=t.getStore();return n===m||e===a&&(n===void 0||n===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=n=>{c()&&e.registerTool({...n,execute:async(e,o,s,c,l)=>{let d=a;if(!r||i||s?.aborted)return S();let f=u(l,d),p=c?e=>{r&&d===a&&!s?.aborted&&c(e)}:void 0,m=await t.run(d,()=>n.execute(e,o,s,p,f));return r&&!i&&d===a?m:S()}})},f=(n,o)=>{c()&&e.registerCommand(n,{...o,handler:async(e,n)=>{let s=a;!r||i||await t.run(s,()=>o.handler(e,u(n,s)))}})},h=(n,o)=>{c()&&e.registerShortcut(n,{...o,handler:e=>{let n=a;if(!(!r||i))return t.run(n,()=>o.handler(u(e,n)))}})};return{pi:new Proxy(e,{get(e,n){return n===`on`?(n,c)=>{_(e,`on`)(n,(e,l)=>{if(!r||i)return;n===`session_start`&&(e!==o||l!==s)&&(a+=1,o=e,s=l);let d=a;return t.run(d,()=>c(e,u(l,d)))})}:n===`registerTool`?d:n===`registerCommand`?f:n===`registerShortcut`?h:n===`getActiveTools`?()=>c()?e.getActiveTools():[]:n===`setActiveTools`?t=>{c()&&e.setActiveTools(t)}:n===`sendMessage`?(...t)=>{c()&&e.sendMessage(...t)}:n===`sendUserMessage`?(...t)=>{c()&&e.sendUserMessage(...t)}:n===`exec`?(...t)=>c()?e.exec(...t):Promise.resolve({code:1,killed:!1,stderr:p,stdout:``}):_(e,n)}}),beginDisposal(){i||!r||(i=!0,a+=1)},finishDisposal(){r=!1,i=!1,a+=1},isCurrentInvocation:c,runCleanup:e=>t.run(m,e)}}function w(n,o,s,f=process.env){let p=x(f),m=p?b(f):void 0,h=p?y(o,m,f):o,g;if(n.effect(()=>()=>g?g.dispose():Promise.resolve(),`${d}/runtime`),n.inject([l.DOOM_SKILL_SOURCES_SERVICE],e=>{let t=(0,i.resolve)((0,i.dirname)((0,a.fileURLToPath)(require("url").pathToFileURL(__filename).href)),`../../../skills`);if(!(0,r.existsSync)(t))return;let n=(0,l.requireDoomSkillSourcesService)(e).register({source:d,directories:[t]});return()=>n.dispose()}),p){g=t.installWorkflowPiRuntime({cordis:n,initialMode:!0,isActive:s})(h);return}let _=!1,v;n.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)}}),n.inject([c.DOOM_HELP_SERVICE],e=>{let t=(0,c.requireDoomHelpService)(e).register({source:d,moduleUrl:require("url").pathToFileURL(__filename).href,skills:[{name:`workflow-help`,description:`Use Doom Pi workflows, monitor long-running jobs, and recover failed runs safely.`}]});return()=>t.dispose()}),g=t.installWorkflowPiRuntime({cordis:n,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;
1
+ const e=require("./leader.cjs"),t=require("./workflow/piExtension.cjs");let n=require("node:async_hooks"),r=require("node:fs"),i=require("node:path"),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 n.AsyncLocalStorage,r=!0,i=!1,a=0,o,s,c=()=>{if(!r)return!1;let e=t.getStore();return e===void 0||e===m||e===a},l=e=>{if(!r)return!1;let n=t.getStore();return n===m||e===a&&(n===void 0||n===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=n=>{c()&&e.registerTool({...n,execute:async(e,o,s,c,l)=>{let d=a;if(!r||i||s?.aborted)return S();let f=u(l,d),p=c?e=>{r&&d===a&&!s?.aborted&&c(e)}:void 0,m=await t.run(d,()=>n.execute(e,o,s,p,f));return r&&!i&&d===a?m:S()}})},f=(n,o)=>{c()&&e.registerCommand(n,{...o,handler:async(e,n)=>{let s=a;!r||i||await t.run(s,()=>o.handler(e,u(n,s)))}})},h=(n,o)=>{c()&&e.registerShortcut(n,{...o,handler:e=>{let n=a;if(!(!r||i))return t.run(n,()=>o.handler(u(e,n)))}})};return{pi:new Proxy(e,{get(e,n){return n===`on`?(n,c)=>{_(e,`on`)(n,(e,l)=>{if(!r||i)return;n===`session_start`&&(e!==o||l!==s)&&(a+=1,o=e,s=l);let d=a;return t.run(d,()=>c(e,u(l,d)))})}:n===`registerTool`?d:n===`registerCommand`?f:n===`registerShortcut`?h:n===`getActiveTools`?()=>c()?e.getActiveTools():[]:n===`setActiveTools`?t=>{c()&&e.setActiveTools(t)}:n===`sendMessage`?(...t)=>{c()&&e.sendMessage(...t)}:n===`sendUserMessage`?(...t)=>{c()&&e.sendUserMessage(...t)}:n===`exec`?(...t)=>c()?e.exec(...t):Promise.resolve({code:1,killed:!1,stderr:p,stdout:``}):_(e,n)}}),beginDisposal(){i||!r||(i=!0,a+=1)},finishDisposal(){r=!1,i=!1,a+=1},isCurrentInvocation:c,runCleanup:e=>t.run(m,e)}}function w(n,o,s,f=process.env){let p=x(f),m=p?b(f):void 0,h=p?y(o,m,f):o,g;if(n.effect(()=>()=>g?g.dispose():Promise.resolve(),`${d}/runtime`),n.inject([l.DOOM_SKILL_SOURCES_SERVICE],e=>{let t=(0,i.resolve)((0,i.dirname)((0,a.fileURLToPath)(require("url").pathToFileURL(__filename).href)),`../../../skills`);if(!(0,r.existsSync)(t))return;let n=(0,l.requireDoomSkillSourcesService)(e).register({source:d,directories:[t]});return()=>n.dispose()}),p){g=t.installWorkflowPiRuntime({cordis:n,initialMode:!0,isActive:s})(h);return}let _=!1,v;n.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)}}),n.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:n,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: 'workflow-help',\n description: 'Use Doom Pi workflows, monitor long-running jobs, and recover failed runs 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":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"extension.d.cts","names":[],"sources":["../../../src/adapters/pi/extension.ts"],"mappings":";;;;iBAmWsB,kBAAkB,IAAI,eAAe"}
1
+ {"version":3,"file":"extension.d.cts","names":[],"sources":["../../../src/adapters/pi/extension.ts"],"mappings":";;;;iBAyWsB,kBAAkB,IAAI,eAAe"}
@@ -1 +1 @@
1
- {"version":3,"file":"extension.d.mts","names":[],"sources":["../../../src/adapters/pi/extension.ts"],"mappings":";;;;iBAmWsB,kBAAkB,IAAI,eAAe"}
1
+ {"version":3,"file":"extension.d.mts","names":[],"sources":["../../../src/adapters/pi/extension.ts"],"mappings":";;;;iBAyWsB,kBAAkB,IAAI,eAAe"}
@@ -1,2 +1,2 @@
1
- import{registerLeaderContribution as e}from"./leader.mjs";import{installWorkflowPiRuntime as t}from"./workflow/piExtension.mjs";import{AsyncLocalStorage as n}from"node:async_hooks";import{existsSync as r}from"node:fs";import{dirname as i,resolve as a}from"node:path";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 n,r=!0,i=!1,a=0,o,s,c=()=>{if(!r)return!1;let e=t.getStore();return e===void 0||e===b||e===a},l=e=>{if(!r)return!1;let n=t.getStore();return n===b||e===a&&(n===void 0||n===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=n=>{c()&&e.registerTool({...n,execute:async(e,o,s,c,l)=>{let d=a;if(!r||i||s?.aborted)return O();let f=u(l,d),p=c?e=>{r&&d===a&&!s?.aborted&&c(e)}:void 0,m=await t.run(d,()=>n.execute(e,o,s,p,f));return r&&!i&&d===a?m:O()}})},f=(n,o)=>{c()&&e.registerCommand(n,{...o,handler:async(e,n)=>{let s=a;!r||i||await t.run(s,()=>o.handler(e,u(n,s)))}})},p=(n,o)=>{c()&&e.registerShortcut(n,{...o,handler:e=>{let n=a;if(!(!r||i))return t.run(n,()=>o.handler(u(e,n)))}})};return{pi:new Proxy(e,{get(e,n){return n===`on`?(n,c)=>{C(e,`on`)(n,(e,l)=>{if(!r||i)return;n===`session_start`&&(e!==o||l!==s)&&(a+=1,o=e,s=l);let d=a;return t.run(d,()=>c(e,u(l,d)))})}:n===`registerTool`?d:n===`registerCommand`?f:n===`registerShortcut`?p:n===`getActiveTools`?()=>c()?e.getActiveTools():[]:n===`setActiveTools`?t=>{c()&&e.setActiveTools(t)}:n===`sendMessage`?(...t)=>{c()&&e.sendMessage(...t)}:n===`sendUserMessage`?(...t)=>{c()&&e.sendUserMessage(...t)}:n===`exec`?(...t)=>c()?e.exec(...t):Promise.resolve({code:1,killed:!1,stderr:y,stdout:``}):C(e,n)}}),beginDisposal(){i||!r||(i=!0,a+=1)},finishDisposal(){r=!1,i=!1,a+=1},isCurrentInvocation:c,runCleanup:e=>t.run(b,e)}}function A(n,s,c,l=process.env){let u=D(l),v=u?E(l):void 0,y=u?T(s,v,l):s,b;if(n.effect(()=>()=>b?b.dispose():Promise.resolve(),`${_}/runtime`),n.inject([p],e=>{let t=a(i(o(import.meta.url)),`../../../skills`);if(!r(t))return;let n=m(e).register({source:_,directories:[t]});return()=>n.dispose()}),u){b=t({cordis:n,initialMode:!0,isActive:c})(y);return}let x=!1,S;n.inject([h],t=>{let n=e(g(t),x);return S=n,()=>{n.dispose(),S===n&&(S=void 0)}}),n.inject([d],e=>{let t=f(e).register({source:_,moduleUrl:import.meta.url,skills:[{name:`workflow-help`,description:`Use Doom Pi workflows, monitor long-running jobs, and recover failed runs safely.`}]});return()=>t.dispose()}),b=t({cordis:n,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};
1
+ import{registerLeaderContribution as e}from"./leader.mjs";import{installWorkflowPiRuntime as t}from"./workflow/piExtension.mjs";import{AsyncLocalStorage as n}from"node:async_hooks";import{existsSync as r}from"node:fs";import{dirname as i,resolve as a}from"node:path";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 n,r=!0,i=!1,a=0,o,s,c=()=>{if(!r)return!1;let e=t.getStore();return e===void 0||e===b||e===a},l=e=>{if(!r)return!1;let n=t.getStore();return n===b||e===a&&(n===void 0||n===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=n=>{c()&&e.registerTool({...n,execute:async(e,o,s,c,l)=>{let d=a;if(!r||i||s?.aborted)return O();let f=u(l,d),p=c?e=>{r&&d===a&&!s?.aborted&&c(e)}:void 0,m=await t.run(d,()=>n.execute(e,o,s,p,f));return r&&!i&&d===a?m:O()}})},f=(n,o)=>{c()&&e.registerCommand(n,{...o,handler:async(e,n)=>{let s=a;!r||i||await t.run(s,()=>o.handler(e,u(n,s)))}})},p=(n,o)=>{c()&&e.registerShortcut(n,{...o,handler:e=>{let n=a;if(!(!r||i))return t.run(n,()=>o.handler(u(e,n)))}})};return{pi:new Proxy(e,{get(e,n){return n===`on`?(n,c)=>{C(e,`on`)(n,(e,l)=>{if(!r||i)return;n===`session_start`&&(e!==o||l!==s)&&(a+=1,o=e,s=l);let d=a;return t.run(d,()=>c(e,u(l,d)))})}:n===`registerTool`?d:n===`registerCommand`?f:n===`registerShortcut`?p:n===`getActiveTools`?()=>c()?e.getActiveTools():[]:n===`setActiveTools`?t=>{c()&&e.setActiveTools(t)}:n===`sendMessage`?(...t)=>{c()&&e.sendMessage(...t)}:n===`sendUserMessage`?(...t)=>{c()&&e.sendUserMessage(...t)}:n===`exec`?(...t)=>c()?e.exec(...t):Promise.resolve({code:1,killed:!1,stderr:y,stdout:``}):C(e,n)}}),beginDisposal(){i||!r||(i=!0,a+=1)},finishDisposal(){r=!1,i=!1,a+=1},isCurrentInvocation:c,runCleanup:e=>t.run(b,e)}}function A(n,s,c,l=process.env){let u=D(l),v=u?E(l):void 0,y=u?T(s,v,l):s,b;if(n.effect(()=>()=>b?b.dispose():Promise.resolve(),`${_}/runtime`),n.inject([p],e=>{let t=a(i(o(import.meta.url)),`../../../skills`);if(!r(t))return;let n=m(e).register({source:_,directories:[t]});return()=>n.dispose()}),u){b=t({cordis:n,initialMode:!0,isActive:c})(y);return}let x=!1,S;n.inject([h],t=>{let n=e(g(t),x);return S=n,()=>{n.dispose(),S===n&&(S=void 0)}}),n.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:n,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: 'workflow-help',\n description: 'Use Doom Pi workflows, monitor long-running jobs, and recover failed runs 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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"}
package/llms.txt CHANGED
@@ -1,8 +1,16 @@
1
1
  # Doom Pi Workflow
2
2
 
3
- Package-owned guidance for launching, monitoring, and recovering Doom Pi workflows.
3
+ Package-owned guidance for authoring, launching, monitoring, and recovering Doom Pi workflows.
4
4
 
5
- ## Resources
5
+ ## How to author
6
6
 
7
- - [Package README](./README.md): Workflow mode, leader bindings, execution model, monitoring, and public entrypoints.
7
+ - [Author DoomPi workflows](./src/prompts/doompi-author-workflow/SKILL.md): Define workflow graphs and host-executed steps safely.
8
+
9
+ ## How to use
10
+
11
+ - [Use DoomPi workflows](./src/prompts/doompi-use-workflow/SKILL.md): Discover, launch, monitor, control, and recover asynchronous runs.
8
12
  - [Workflow recovery skill](./skills/workflow-recovery/SKILL.md): Evidence-first recovery of failed or interrupted workflow runs.
13
+
14
+ ## Package reference
15
+
16
+ - [Package README](./README.md): Workflow mode, leader bindings, execution model, monitoring, and public entrypoints.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agimon-ai/doompi-workflow",
3
- "version": "0.0.1-alpha.28",
3
+ "version": "0.0.1-alpha.29",
4
4
  "description": "GitHub Actions-style workflow graphs, artifacts, recovery, and asynchronous runs for DoomPi.",
5
5
  "keywords": [
6
6
  "ai",
@@ -21,6 +21,7 @@
21
21
  "dist",
22
22
  "llms.txt",
23
23
  "README.md",
24
+ "src/prompts",
24
25
  "skills/workflow-recovery/SKILL.md"
25
26
  ],
26
27
  "type": "module",
@@ -49,9 +50,9 @@
49
50
  "@deepseek-ai/cordis": "4.0.1",
50
51
  "@modelcontextprotocol/sdk": "1.30.0",
51
52
  "zod": "4.4.3",
52
- "@agimon-ai/doompi-extension-contracts": "0.0.1-alpha.28",
53
- "@agimon-ai/doompi-telemetry": "0.0.1-alpha.28",
54
- "@agimon-ai/doompi-ui": "0.0.1-alpha.28"
53
+ "@agimon-ai/doompi-extension-contracts": "0.0.1-alpha.29",
54
+ "@agimon-ai/doompi-telemetry": "0.0.1-alpha.29",
55
+ "@agimon-ai/doompi-ui": "0.0.1-alpha.29"
55
56
  },
56
57
  "devDependencies": {
57
58
  "@earendil-works/pi-coding-agent": "0.84.2",
@@ -0,0 +1,32 @@
1
+ ---
2
+ name: doompi-author-workflow
3
+ 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.
4
+ ---
5
+
6
+ # Author DoomPi workflows
7
+
8
+ Create a `*.workflow.yml` file and keep its graph explicit:
9
+
10
+ ```yaml
11
+ name: verify
12
+
13
+ jobs:
14
+ test:
15
+ runs-on: ubuntu-latest
16
+ steps:
17
+ - name: Run tests
18
+ run: pnpm test
19
+
20
+ summarize:
21
+ runs-on: ubuntu-latest
22
+ needs: test
23
+ steps:
24
+ - name: Record result
25
+ run: node scripts/write-verification-summary.mjs
26
+ ```
27
+
28
+ `needs` expresses job ordering. Keep steps small enough that failure evidence identifies the command that needs attention. Use runner-specific `interactiveRun` configuration when a command genuinely requires a TTY.
29
+
30
+ The GitHub-style `runs-on` value does not provision a VM or container. Every `run` command executes on the workflow host with that process's environment and privileges. Review workflow changes as executable code, avoid embedding secrets, and make retry-sensitive external side effects explicit.
31
+
32
+ Before relying on the graph, enable Workflow mode, discover it with `list_workflows`, launch a disposable run, and inspect job and step status with `workflow_run`. Test a failure path when recovery behavior matters.
@@ -0,0 +1,21 @@
1
+ ---
2
+ name: doompi-use-workflow
3
+ description: Use DoomPi workflows. Use when discovering or launching workflows, monitoring or controlling asynchronous runs, interpreting terminal notifications, or recovering a failed run safely.
4
+ ---
5
+
6
+ # Use DoomPi workflows
7
+
8
+ Enable model-visible Workflow tools with `SPC w e`. In a non-interactive harness, set `WORKFLOW_MCP_MODE=on` before starting the session.
9
+
10
+ Use the tools in this order:
11
+
12
+ 1. `list_workflows` discovers available workflow definitions.
13
+ 2. `launch_workflow` registers and starts a run. A successful launch response does not mean the jobs have finished.
14
+ 3. `workflow_run` inspects status, follows progress, or applies a supported control action.
15
+ 4. Wait for a terminal notification and verify the final job and step states before reporting success.
16
+
17
+ In the TUI, `SPC w w` launches a workflow, `SPC w l` manages runs, and `SPC w r` opens recovery. The root session can launch. Child sessions can inspect the catalog but do not receive an unrestricted workflow factory.
18
+
19
+ Recover only a record already in a terminal failure state. Inspect its evidence first, then use the package's `workflow-recovery` runtime skill for the repair workflow. Recovery atomically adopts eligible work, but it cannot make non-idempotent external side effects safe or guarantee that in-process work survives parent shutdown.
20
+
21
+ Workflow steps and model-backed repair can consume provider quota and repeat external effects. Do not relaunch or replay a run merely because progress is quiet. Inspect live ownership and persisted state first.