@agimon-ai/doompi-workflow 0.0.1-alpha.2 → 0.0.1-alpha.20
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 +47 -21
- package/dist/adapters/pi/dispatcher.cjs.map +1 -0
- package/dist/{dispatcher.d.cts → adapters/pi/dispatcher.d.cts} +1 -1
- package/dist/adapters/pi/dispatcher.d.cts.map +1 -0
- package/dist/{dispatcher.d.mts → adapters/pi/dispatcher.d.mts} +1 -1
- package/dist/adapters/pi/dispatcher.d.mts.map +1 -0
- package/dist/adapters/pi/dispatcher.mjs.map +1 -0
- package/dist/adapters/pi/extension.cjs +2 -0
- package/dist/adapters/pi/extension.cjs.map +1 -0
- package/dist/{extension.d.cts → adapters/pi/extension.d.cts} +1 -1
- package/dist/adapters/pi/extension.d.cts.map +1 -0
- package/dist/{extension.d.mts → adapters/pi/extension.d.mts} +1 -1
- package/dist/adapters/pi/extension.d.mts.map +1 -0
- package/dist/adapters/pi/extension.mjs +2 -0
- package/dist/adapters/pi/extension.mjs.map +1 -0
- package/dist/adapters/pi/leader.cjs +2 -0
- package/dist/adapters/pi/leader.cjs.map +1 -0
- package/dist/adapters/pi/leader.mjs +2 -0
- package/dist/adapters/pi/leader.mjs.map +1 -0
- package/dist/adapters/pi/workflow/piExtension.cjs +11 -0
- package/dist/adapters/pi/workflow/piExtension.cjs.map +1 -0
- package/dist/adapters/pi/workflow/piExtension.mjs +11 -0
- package/dist/adapters/pi/workflow/piExtension.mjs.map +1 -0
- package/dist/{workflow → adapters/pi/workflow}/piToolBridge.cjs +1 -1
- package/dist/adapters/pi/workflow/piToolBridge.cjs.map +1 -0
- package/dist/{workflow → adapters/pi/workflow}/piToolBridge.mjs +1 -1
- package/dist/adapters/pi/workflow/piToolBridge.mjs.map +1 -0
- package/dist/adapters/pi/workflow/piTools.cjs +5 -0
- package/dist/adapters/pi/workflow/piTools.cjs.map +1 -0
- package/dist/adapters/pi/workflow/piTools.mjs +5 -0
- package/dist/adapters/pi/workflow/piTools.mjs.map +1 -0
- package/dist/adapters/pi/workflow/workflowLauncher.cjs +2 -0
- package/dist/adapters/pi/workflow/workflowLauncher.cjs.map +1 -0
- package/dist/adapters/pi/workflow/workflowLauncher.mjs +2 -0
- package/dist/adapters/pi/workflow/workflowLauncher.mjs.map +1 -0
- package/dist/extensions/dispatcher.cjs +1 -1
- package/dist/extensions/dispatcher.cjs.map +1 -1
- package/dist/extensions/dispatcher.d.cts +1 -1
- package/dist/extensions/dispatcher.d.mts +1 -1
- package/dist/extensions/dispatcher.mjs +1 -1
- package/dist/extensions/dispatcher.mjs.map +1 -1
- package/dist/extensions/pi.cjs +1 -1
- package/dist/extensions/pi.cjs.map +1 -1
- package/dist/extensions/pi.d.cts +1 -1
- package/dist/extensions/pi.d.mts +1 -1
- package/dist/extensions/pi.mjs +1 -1
- package/dist/extensions/pi.mjs.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +1 -1
- package/dist/schemas/workflowPi.cjs +2 -0
- package/dist/schemas/workflowPi.cjs.map +1 -0
- package/dist/schemas/workflowPi.mjs +2 -0
- package/dist/schemas/workflowPi.mjs.map +1 -0
- package/dist/services/workflow/backgroundWork.cjs.map +1 -0
- package/dist/services/workflow/backgroundWork.mjs.map +1 -0
- package/dist/services/workflow/workflowNarration.cjs +2 -0
- package/dist/services/workflow/workflowNarration.cjs.map +1 -0
- package/dist/services/workflow/workflowNarration.mjs +2 -0
- package/dist/services/workflow/workflowNarration.mjs.map +1 -0
- package/dist/tui/workflow/doomOverlay.cjs.map +1 -0
- package/dist/tui/workflow/doomOverlay.mjs.map +1 -0
- package/dist/tui/workflow/overlayText.cjs.map +1 -0
- package/dist/tui/workflow/overlayText.mjs.map +1 -0
- package/dist/tui/workflow/rendering.cjs.map +1 -0
- package/dist/tui/workflow/rendering.mjs.map +1 -0
- package/dist/tui/workflow/workflowInspector.cjs +2 -0
- package/dist/{workflow/workflowInspector.mjs.map → tui/workflow/workflowInspector.cjs.map} +1 -1
- package/dist/tui/workflow/workflowInspector.mjs +2 -0
- package/dist/tui/workflow/workflowInspector.mjs.map +1 -0
- package/dist/tui/workflow/workflowOverlay.cjs.map +1 -0
- package/dist/tui/workflow/workflowOverlay.mjs.map +1 -0
- package/dist/tui/workflow/workflowPicker.cjs.map +1 -0
- package/dist/tui/workflow/workflowPicker.mjs.map +1 -0
- package/dist/tui/workflow/workflowProgressOverlay.cjs +2 -0
- package/dist/tui/workflow/workflowProgressOverlay.cjs.map +1 -0
- package/dist/tui/workflow/workflowProgressOverlay.mjs +2 -0
- package/dist/tui/workflow/workflowProgressOverlay.mjs.map +1 -0
- package/dist/tui/workflow/workflowPrompt.cjs.map +1 -0
- package/dist/tui/workflow/workflowPrompt.mjs.map +1 -0
- package/dist/tui/workflow/workflowRunPanel.cjs.map +1 -0
- package/dist/tui/workflow/workflowRunPanel.mjs.map +1 -0
- package/dist/tui/workflow/workflowStatusRow.cjs +2 -0
- package/dist/tui/workflow/workflowStatusRow.cjs.map +1 -0
- package/dist/tui/workflow/workflowStatusRow.mjs +2 -0
- package/dist/tui/workflow/workflowStatusRow.mjs.map +1 -0
- package/dist/tui/workflow/workflowStepMessage.cjs +2 -0
- package/dist/tui/workflow/workflowStepMessage.cjs.map +1 -0
- package/dist/tui/workflow/workflowStepMessage.mjs +2 -0
- package/dist/tui/workflow/workflowStepMessage.mjs.map +1 -0
- package/dist/tui/workflow/workflowToolRender.cjs +7 -0
- package/dist/tui/workflow/workflowToolRender.cjs.map +1 -0
- package/dist/tui/workflow/workflowToolRender.mjs +7 -0
- package/dist/tui/workflow/workflowToolRender.mjs.map +1 -0
- package/dist/{types.cjs → types/index.cjs} +1 -1
- package/dist/types/index.cjs.map +1 -0
- package/dist/{types.mjs → types/index.mjs} +1 -1
- package/dist/types/index.mjs.map +1 -0
- package/llms.txt +8 -0
- package/package.json +20 -11
- package/dist/dispatcher.cjs.map +0 -1
- package/dist/dispatcher.d.cts.map +0 -1
- package/dist/dispatcher.d.mts.map +0 -1
- package/dist/dispatcher.mjs.map +0 -1
- package/dist/extension.cjs +0 -2
- package/dist/extension.cjs.map +0 -1
- package/dist/extension.d.cts.map +0 -1
- package/dist/extension.d.mts.map +0 -1
- package/dist/extension.mjs +0 -2
- package/dist/extension.mjs.map +0 -1
- package/dist/leader.cjs +0 -2
- package/dist/leader.cjs.map +0 -1
- package/dist/leader.mjs +0 -2
- package/dist/leader.mjs.map +0 -1
- package/dist/types.cjs.map +0 -1
- package/dist/types.mjs.map +0 -1
- package/dist/workflow/backgroundWork.cjs.map +0 -1
- package/dist/workflow/backgroundWork.mjs.map +0 -1
- package/dist/workflow/doomOverlay.cjs.map +0 -1
- package/dist/workflow/doomOverlay.mjs.map +0 -1
- package/dist/workflow/overlayText.cjs.map +0 -1
- package/dist/workflow/overlayText.mjs.map +0 -1
- package/dist/workflow/piExtension.cjs +0 -11
- package/dist/workflow/piExtension.cjs.map +0 -1
- package/dist/workflow/piExtension.mjs +0 -11
- package/dist/workflow/piExtension.mjs.map +0 -1
- package/dist/workflow/piToolBridge.cjs.map +0 -1
- package/dist/workflow/piToolBridge.mjs.map +0 -1
- package/dist/workflow/piTools.cjs +0 -4
- package/dist/workflow/piTools.cjs.map +0 -1
- package/dist/workflow/piTools.mjs +0 -4
- package/dist/workflow/piTools.mjs.map +0 -1
- package/dist/workflow/rendering.cjs.map +0 -1
- package/dist/workflow/rendering.mjs.map +0 -1
- package/dist/workflow/workflowFooterStatus.cjs +0 -2
- package/dist/workflow/workflowFooterStatus.cjs.map +0 -1
- package/dist/workflow/workflowFooterStatus.mjs +0 -2
- package/dist/workflow/workflowFooterStatus.mjs.map +0 -1
- package/dist/workflow/workflowInspector.cjs +0 -2
- package/dist/workflow/workflowInspector.cjs.map +0 -1
- package/dist/workflow/workflowInspector.mjs +0 -2
- package/dist/workflow/workflowLauncher.cjs +0 -2
- package/dist/workflow/workflowLauncher.cjs.map +0 -1
- package/dist/workflow/workflowLauncher.mjs +0 -2
- package/dist/workflow/workflowLauncher.mjs.map +0 -1
- package/dist/workflow/workflowOverlay.cjs.map +0 -1
- package/dist/workflow/workflowOverlay.mjs.map +0 -1
- package/dist/workflow/workflowPicker.cjs.map +0 -1
- package/dist/workflow/workflowPicker.mjs.map +0 -1
- package/dist/workflow/workflowPrompt.cjs.map +0 -1
- package/dist/workflow/workflowPrompt.mjs.map +0 -1
- package/dist/workflow/workflowRunPanel.cjs.map +0 -1
- package/dist/workflow/workflowRunPanel.mjs.map +0 -1
- package/dist/workflow/workflowStatusRow.cjs +0 -2
- package/dist/workflow/workflowStatusRow.cjs.map +0 -1
- package/dist/workflow/workflowStatusRow.mjs +0 -2
- package/dist/workflow/workflowStatusRow.mjs.map +0 -1
- package/dist/workflow/workflowStepMessage.cjs +0 -2
- package/dist/workflow/workflowStepMessage.cjs.map +0 -1
- package/dist/workflow/workflowStepMessage.mjs +0 -2
- package/dist/workflow/workflowStepMessage.mjs.map +0 -1
- /package/dist/{dispatcher.cjs → adapters/pi/dispatcher.cjs} +0 -0
- /package/dist/{dispatcher.mjs → adapters/pi/dispatcher.mjs} +0 -0
- /package/dist/{workflow → services/workflow}/backgroundWork.cjs +0 -0
- /package/dist/{workflow → services/workflow}/backgroundWork.mjs +0 -0
- /package/dist/{workflow → tui/workflow}/doomOverlay.cjs +0 -0
- /package/dist/{workflow → tui/workflow}/doomOverlay.mjs +0 -0
- /package/dist/{workflow → tui/workflow}/overlayText.cjs +0 -0
- /package/dist/{workflow → tui/workflow}/overlayText.mjs +0 -0
- /package/dist/{workflow → tui/workflow}/rendering.cjs +0 -0
- /package/dist/{workflow → tui/workflow}/rendering.mjs +0 -0
- /package/dist/{workflow → tui/workflow}/workflowOverlay.cjs +0 -0
- /package/dist/{workflow → tui/workflow}/workflowOverlay.mjs +0 -0
- /package/dist/{workflow → tui/workflow}/workflowPicker.cjs +0 -0
- /package/dist/{workflow → tui/workflow}/workflowPicker.mjs +0 -0
- /package/dist/{workflow → tui/workflow}/workflowPrompt.cjs +0 -0
- /package/dist/{workflow → tui/workflow}/workflowPrompt.mjs +0 -0
- /package/dist/{workflow → tui/workflow}/workflowRunPanel.cjs +0 -0
- /package/dist/{workflow → tui/workflow}/workflowRunPanel.mjs +0 -0
package/README.md
CHANGED
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
# @agimon-ai/doompi-workflow
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**Long work needs edges.**
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
This package gives Doompi the job graph GitHub Actions users already know: `on:`, `jobs:`,
|
|
6
6
|
`needs:`, `steps:`, timeouts, and declared artifacts. Every step names the session it wants,
|
|
7
|
-
so
|
|
7
|
+
so implementation can use coding tools while a later release note gets marketing context
|
|
8
|
+
and a brand profile. One run does not mean one swollen context window.
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
Workflow is a minor mode. Its tools can be enabled or removed during the session, while
|
|
11
|
+
running jobs continue under the boundaries declared by the workflow.
|
|
12
|
+
|
|
13
|
+
This is part of [Doompi](https://www.npmjs.com/package/@agimon-ai/doompi). Most users get
|
|
14
|
+
it with the distribution.
|
|
12
15
|
|
|
13
16
|
## Install
|
|
14
17
|
|
|
@@ -18,29 +21,52 @@ npm install @agimon-ai/doompi-workflow
|
|
|
18
21
|
|
|
19
22
|
## How it loads
|
|
20
23
|
|
|
21
|
-
Doompi loads
|
|
22
|
-
`.doom/modes.yaml
|
|
23
|
-
|
|
24
|
-
Extension entry: `@agimon-ai/doompi-workflow/extensions/pi`
|
|
24
|
+
Doompi loads the workflow integration as core. It is not a major-mode layer and does not
|
|
25
|
+
appear in `.doom/modes.yaml`. The bare package name follows its Pi manifest and loads the
|
|
26
|
+
Pi adapter.
|
|
25
27
|
|
|
26
28
|
## Leader
|
|
27
29
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
+
- `SPC w w` chooses and launches a workflow.
|
|
31
|
+
- `SPC w l` opens running workflows and their controls.
|
|
32
|
+
- `SPC w r` recovers a failed workflow.
|
|
33
|
+
- `SPC w e` gives the agent workflow tools or takes them back.
|
|
34
|
+
|
|
35
|
+
## Recovery
|
|
36
|
+
|
|
37
|
+
The recovery picker can see terminal failures from earlier Pi sessions. The
|
|
38
|
+
`workflow_run` action `recovery-evidence` reads only terminal registry evidence; status,
|
|
39
|
+
tail, and every live control remain scoped to the session that owns the run.
|
|
40
|
+
|
|
41
|
+
Replay claims the failed record atomically, transfers only `PI_SESSION_ID`, and binds new
|
|
42
|
+
controls to the exact run generation that was authorized. Recovery resumes the failed
|
|
43
|
+
work; it does not quietly launch a second copy beside it.
|
|
44
|
+
|
|
45
|
+
## Package guidance
|
|
46
|
+
|
|
47
|
+
The package ships a root `llms.txt` index. While the parent-only Help mode is active it
|
|
48
|
+
contributes `workflow-help`; deactivating Help removes that generated wrapper. The
|
|
49
|
+
`workflow-recovery` skill is model-visible only while Workflow mode is active.
|
|
50
|
+
|
|
51
|
+
## Progress without transcript noise
|
|
30
52
|
|
|
31
|
-
|
|
53
|
+
Running workflows appear in a transient task-like list above the editor with the current
|
|
54
|
+
step and an animated spinner. Step updates and raw launcher frames stay out of chat. The
|
|
55
|
+
list disappears on completion, and only terminal success or failure emits an agent
|
|
56
|
+
message. Raw output remains available through the follow and open views; it does not take
|
|
57
|
+
over the footer.
|
|
32
58
|
|
|
33
|
-
The dispatcher exposes `list_workflows` to any session but
|
|
34
|
-
|
|
59
|
+
The dispatcher exposes `list_workflows` to any session, but only the root session gets
|
|
60
|
+
`launch_workflow`. A subagent may inspect the catalog; it may not spawn a new factory.
|
|
35
61
|
|
|
36
62
|
## Entry points
|
|
37
63
|
|
|
38
|
-
| Import |
|
|
39
|
-
| -------------------------------------------------------------- |
|
|
40
|
-
| `@agimon-ai/doompi-workflow` |
|
|
41
|
-
| `@agimon-ai/doompi-workflow/extensions/dispatcher` |
|
|
42
|
-
| `@agimon-ai/doompi-workflow/extensions/pi` |
|
|
43
|
-
| `@agimon-ai/doompi-workflow/skills/workflow-recovery/SKILL.md` |
|
|
64
|
+
| Import | Purpose |
|
|
65
|
+
| -------------------------------------------------------------- | ----------------------------- |
|
|
66
|
+
| `@agimon-ai/doompi-workflow` | Library API |
|
|
67
|
+
| `@agimon-ai/doompi-workflow/extensions/dispatcher` | Workflow dispatcher extension |
|
|
68
|
+
| `@agimon-ai/doompi-workflow/extensions/pi` | Pi extension entry point |
|
|
69
|
+
| `@agimon-ai/doompi-workflow/skills/workflow-recovery/SKILL.md` | Recovery skill |
|
|
44
70
|
|
|
45
71
|
## License
|
|
46
72
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dispatcher.cjs","names":["resolveRootSessionId","readChildProcessContext","SUBAGENT_PARENT_SESSION_ENV","WorkflowModeChanged"],"sources":["../../../src/adapters/pi/dispatcher.ts"],"sourcesContent":["import {\n readChildProcessContext,\n resolveRootSessionId,\n SUBAGENT_PARENT_SESSION_ENV,\n} from '@agimon-ai/doompi-extension-contracts/child-process';\nimport { createProtocolRuntime } from '@agimon-ai/doompi-extension-contracts/protocol';\nimport { WorkflowModeChanged } from '@agimon-ai/doompi-extension-contracts/workflow';\nimport type { ExtensionAPI, ExtensionContext } from '@earendil-works/pi-coding-agent';\nimport { createWorkflowPiExtension } from './workflow/piExtension.ts';\n\ntype RegisteredTool = Parameters<ExtensionAPI['registerTool']>[0];\nconst DISPATCHER_TOOL_NAMES = new Set(['list_workflows', 'launch_workflow']);\nconst ROOT_SCOPED_TOOL_NAMES = new Set(['launch_workflow']);\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\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 registerWorkflowDispatcherExtension(pi: ExtensionAPI): void {\n const parentSessionId = resolveDispatcherParentSession(process.env);\n const rootSessionId = parentSessionId ? resolveRootSessionId(parentSessionId) : undefined;\n createWorkflowPiExtension()(createDispatcherBridge(pi, rootSessionId));\n\n pi.on('session_start', (_event, ctx) => {\n const sessionId = resolveRootSessionId(ctx.sessionManager.getSessionId());\n createProtocolRuntime({\n bus: pi.events,\n source: '@agimon-ai/doompi-workflow/dispatcher',\n sessionId,\n }).notify(WorkflowModeChanged, { mode: 'on', ...(rootSessionId ? { parentSessionId: rootSessionId } : {}) });\n });\n}\n"],"mappings":"2OAWA,MAAM,EAAwB,IAAI,IAAI,CAAC,iBAAkB,iBAAiB,CAAC,EACrE,EAAyB,IAAI,IAAI,CAAC,iBAAiB,CAAC,EAE1D,SAAS,EAAW,EAAgB,EAAgC,CAClE,IAAM,EAAQ,QAAQ,IAAI,EAAQ,EAAU,CAAM,EAClD,OAAO,OAAO,GAAU,WAAa,EAAM,KAAK,CAAM,EAAI,CAC5D,CAEA,SAAS,EAAkB,EAAuB,EAA2C,CAC3F,IAAM,EAAiB,IAAI,MAAM,EAAI,eAAgB,CACnD,IAAI,EAAQ,EAAU,CAEpB,OADI,IAAa,mBAA6B,EACvC,EAAW,EAAQ,CAAQ,CACpC,CACF,CAAC,EACD,OAAO,IAAI,MAAM,EAAK,CACpB,IAAI,EAAQ,EAAU,CAEpB,OADI,IAAa,iBAAyB,EACnC,EAAW,EAAQ,CAAQ,CACpC,CACF,CAAC,CACH,CAEA,SAAgB,EACd,EACA,EACA,EAA4D,QAAQ,IACtD,CACd,IAAM,EAAgB,GAAA,EAAkBA,EAAAA,qBAAAA,CAAqB,EAAiB,CAAW,EAAI,IAAA,GAC7F,OAAO,IAAI,MAAM,EAAI,CACnB,IAAI,EAAQ,EAAU,CAKpB,OAJI,IAAa,iBACP,GAA0B,EAAO,eAAe,EAAM,OAAQ,GAAS,IAAS,cAAc,CAAC,EAErG,IAAa,eACT,GAA+B,CAChC,KAAsB,IAAI,EAAK,IAAI,EACxC,IAAI,CAAC,GAAiB,CAAC,EAAuB,IAAI,EAAK,IAAI,EAAG,CAC5D,EAAO,aAAa,CAAI,EACxB,MACF,CACA,EAAO,aAAa,CAClB,GAAG,EACH,SAAU,EAAY,EAAQ,EAAQ,EAAU,IAC9C,EAAK,QAAQ,EAAY,EAAQ,EAAQ,EAAU,EAAkB,EAAK,CAAa,CAAC,CAC5F,CAAC,CALD,CAMF,EAZwC,EAAW,EAAQ,CAAQ,CAarE,CACF,CAAC,CACH,CAEA,SAAgB,EACd,EACoB,CAEpB,OAAA,EADqBC,EAAAA,wBAAAA,CAAwB,CAC3B,CAAC,EAAE,kBAAoB,EAAYC,EAAAA,4BAA4B,EAAE,KAAK,GAAK,IAAA,GAC/F,CAEA,SAAgB,EAAoC,EAAwB,CAC1E,IAAM,EAAkB,EAA+B,QAAQ,GAAG,EAC5D,EAAgB,GAAA,EAAkBF,EAAAA,qBAAAA,CAAqB,CAAe,EAAI,IAAA,GAChF,EAAA,0BAA0B,CAAC,CAAC,EAAuB,EAAI,CAAa,CAAC,EAErE,EAAG,GAAG,iBAAkB,EAAQ,IAAQ,CACtC,IAAM,GAAA,EAAYA,EAAAA,qBAAAA,CAAqB,EAAI,eAAe,aAAa,CAAC,GACxE,EAAA,EAAA,sBAAA,CAAsB,CACpB,IAAK,EAAG,OACR,OAAQ,wCACR,WACF,CAAC,CAAC,CAAC,OAAOG,EAAAA,oBAAqB,CAAE,KAAM,KAAM,GAAI,EAAgB,CAAE,gBAAiB,CAAc,EAAI,CAAC,CAAG,CAAC,CAC7G,CAAC,CACH"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
2
|
-
//#region src/dispatcher.d.ts
|
|
2
|
+
//#region src/adapters/pi/dispatcher.d.ts
|
|
3
3
|
declare function registerWorkflowDispatcherExtension(pi: ExtensionAPI): void;
|
|
4
4
|
//#endregion
|
|
5
5
|
export { registerWorkflowDispatcherExtension };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dispatcher.d.cts","names":[],"sources":["../../../src/adapters/pi/dispatcher.ts"],"mappings":";;iBAqEgB,oCAAoC,IAAI"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
2
|
-
//#region src/dispatcher.d.ts
|
|
2
|
+
//#region src/adapters/pi/dispatcher.d.ts
|
|
3
3
|
declare function registerWorkflowDispatcherExtension(pi: ExtensionAPI): void;
|
|
4
4
|
//#endregion
|
|
5
5
|
export { registerWorkflowDispatcherExtension };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dispatcher.d.mts","names":[],"sources":["../../../src/adapters/pi/dispatcher.ts"],"mappings":";;iBAqEgB,oCAAoC,IAAI"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dispatcher.mjs","names":[],"sources":["../../../src/adapters/pi/dispatcher.ts"],"sourcesContent":["import {\n readChildProcessContext,\n resolveRootSessionId,\n SUBAGENT_PARENT_SESSION_ENV,\n} from '@agimon-ai/doompi-extension-contracts/child-process';\nimport { createProtocolRuntime } from '@agimon-ai/doompi-extension-contracts/protocol';\nimport { WorkflowModeChanged } from '@agimon-ai/doompi-extension-contracts/workflow';\nimport type { ExtensionAPI, ExtensionContext } from '@earendil-works/pi-coding-agent';\nimport { createWorkflowPiExtension } from './workflow/piExtension.ts';\n\ntype RegisteredTool = Parameters<ExtensionAPI['registerTool']>[0];\nconst DISPATCHER_TOOL_NAMES = new Set(['list_workflows', 'launch_workflow']);\nconst ROOT_SCOPED_TOOL_NAMES = new Set(['launch_workflow']);\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\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 registerWorkflowDispatcherExtension(pi: ExtensionAPI): void {\n const parentSessionId = resolveDispatcherParentSession(process.env);\n const rootSessionId = parentSessionId ? resolveRootSessionId(parentSessionId) : undefined;\n createWorkflowPiExtension()(createDispatcherBridge(pi, rootSessionId));\n\n pi.on('session_start', (_event, ctx) => {\n const sessionId = resolveRootSessionId(ctx.sessionManager.getSessionId());\n createProtocolRuntime({\n bus: pi.events,\n source: '@agimon-ai/doompi-workflow/dispatcher',\n sessionId,\n }).notify(WorkflowModeChanged, { mode: 'on', ...(rootSessionId ? { parentSessionId: rootSessionId } : {}) });\n });\n}\n"],"mappings":"4YAWA,MAAM,EAAwB,IAAI,IAAI,CAAC,iBAAkB,iBAAiB,CAAC,EACrE,EAAyB,IAAI,IAAI,CAAC,iBAAiB,CAAC,EAE1D,SAAS,EAAW,EAAgB,EAAgC,CAClE,IAAM,EAAQ,QAAQ,IAAI,EAAQ,EAAU,CAAM,EAClD,OAAO,OAAO,GAAU,WAAa,EAAM,KAAK,CAAM,EAAI,CAC5D,CAEA,SAAS,EAAkB,EAAuB,EAA2C,CAC3F,IAAM,EAAiB,IAAI,MAAM,EAAI,eAAgB,CACnD,IAAI,EAAQ,EAAU,CAEpB,OADI,IAAa,mBAA6B,EACvC,EAAW,EAAQ,CAAQ,CACpC,CACF,CAAC,EACD,OAAO,IAAI,MAAM,EAAK,CACpB,IAAI,EAAQ,EAAU,CAEpB,OADI,IAAa,iBAAyB,EACnC,EAAW,EAAQ,CAAQ,CACpC,CACF,CAAC,CACH,CAEA,SAAgB,EACd,EACA,EACA,EAA4D,QAAQ,IACtD,CACd,IAAM,EAAgB,EAAkB,EAAqB,EAAiB,CAAW,EAAI,IAAA,GAC7F,OAAO,IAAI,MAAM,EAAI,CACnB,IAAI,EAAQ,EAAU,CAKpB,OAJI,IAAa,iBACP,GAA0B,EAAO,eAAe,EAAM,OAAQ,GAAS,IAAS,cAAc,CAAC,EAErG,IAAa,eACT,GAA+B,CAChC,KAAsB,IAAI,EAAK,IAAI,EACxC,IAAI,CAAC,GAAiB,CAAC,EAAuB,IAAI,EAAK,IAAI,EAAG,CAC5D,EAAO,aAAa,CAAI,EACxB,MACF,CACA,EAAO,aAAa,CAClB,GAAG,EACH,SAAU,EAAY,EAAQ,EAAQ,EAAU,IAC9C,EAAK,QAAQ,EAAY,EAAQ,EAAQ,EAAU,EAAkB,EAAK,CAAa,CAAC,CAC5F,CAAC,CALD,CAMF,EAZwC,EAAW,EAAQ,CAAQ,CAarE,CACF,CAAC,CACH,CAEA,SAAgB,EACd,EACoB,CAEpB,OADqB,EAAwB,CAC3B,CAAC,EAAE,kBAAoB,EAAY,EAA4B,EAAE,KAAK,GAAK,IAAA,GAC/F,CAEA,SAAgB,EAAoC,EAAwB,CAC1E,IAAM,EAAkB,EAA+B,QAAQ,GAAG,EAC5D,EAAgB,EAAkB,EAAqB,CAAe,EAAI,IAAA,GAChF,EAA0B,CAAC,CAAC,EAAuB,EAAI,CAAa,CAAC,EAErE,EAAG,GAAG,iBAAkB,EAAQ,IAAQ,CACtC,IAAM,EAAY,EAAqB,EAAI,eAAe,aAAa,CAAC,EACxE,EAAsB,CACpB,IAAK,EAAG,OACR,OAAQ,wCACR,WACF,CAAC,CAAC,CAAC,OAAO,EAAqB,CAAE,KAAM,KAAM,GAAI,EAAgB,CAAE,gBAAiB,CAAc,EAAI,CAAC,CAAG,CAAC,CAC7G,CAAC,CACH"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=require("./workflow/piExtension.cjs"),t=require("./leader.cjs");let n=require("@agimon-ai/doompi-extension-contracts/help");function r(r){let i=t.registerLeaderContribution(r),a=(0,n.registerDoomHelpContribution)(r,{source:`@agimon-ai/doompi-workflow`,moduleUrl:require("url").pathToFileURL(__filename).href,skills:[{name:`workflow-help`,description:`Use Doom Pi workflows, monitor long-running jobs, and recover failed runs safely.`}]});e.createWorkflowPiExtension({onModeChange:e=>i.setMode(e)})(r),r.on(`session_shutdown`,()=>{a.dispose(),i.dispose()})}exports.registerWorkflowExtension=r;
|
|
2
|
+
//# sourceMappingURL=extension.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extension.cjs","names":["registerLeaderContribution","registerDoomHelpContribution"],"sources":["../../../src/adapters/pi/extension.ts"],"sourcesContent":["import { registerDoomHelpContribution } from '@agimon-ai/doompi-extension-contracts/help';\nimport type { ExtensionAPI } from '@earendil-works/pi-coding-agent';\nimport { registerLeaderContribution } from './leader.ts';\nimport { createWorkflowPiExtension } from './workflow/piExtension.ts';\n\nexport function registerWorkflowExtension(pi: ExtensionAPI): void {\n const leader = registerLeaderContribution(pi);\n const help = registerDoomHelpContribution(pi, {\n source: '@agimon-ai/doompi-workflow',\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 createWorkflowPiExtension({ onModeChange: (enabled) => leader.setMode(enabled) })(pi);\n pi.on('session_shutdown', () => {\n help.dispose();\n leader.dispose();\n });\n}\n"],"mappings":"oIAKA,SAAgB,EAA0B,EAAwB,CAChE,IAAM,EAASA,EAAAA,2BAA2B,CAAE,EACtC,GAAA,EAAOC,EAAAA,6BAAAA,CAA6B,EAAI,CAC5C,OAAQ,6BACR,UAAA,QAAA,KAAA,CAAA,CAAA,cAAA,UAAA,CAAA,CAAA,KACA,OAAQ,CACN,CACE,KAAM,gBACN,YAAa,mFACf,CACF,CACF,CAAC,EACD,EAAA,0BAA0B,CAAE,aAAe,GAAY,EAAO,QAAQ,CAAO,CAAE,CAAC,CAAC,CAAC,CAAE,EACpF,EAAG,GAAG,uBAA0B,CAC9B,EAAK,QAAQ,EACb,EAAO,QAAQ,CACjB,CAAC,CACH"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extension.d.cts","names":[],"sources":["../../../src/adapters/pi/extension.ts"],"mappings":";;iBAKgB,0BAA0B,IAAI"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extension.d.mts","names":[],"sources":["../../../src/adapters/pi/extension.ts"],"mappings":";;iBAKgB,0BAA0B,IAAI"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{createWorkflowPiExtension as e}from"./workflow/piExtension.mjs";import{registerLeaderContribution as t}from"./leader.mjs";import{registerDoomHelpContribution as n}from"@agimon-ai/doompi-extension-contracts/help";function r(r){let i=t(r),a=n(r,{source:`@agimon-ai/doompi-workflow`,moduleUrl:import.meta.url,skills:[{name:`workflow-help`,description:`Use Doom Pi workflows, monitor long-running jobs, and recover failed runs safely.`}]});e({onModeChange:e=>i.setMode(e)})(r),r.on(`session_shutdown`,()=>{a.dispose(),i.dispose()})}export{r as registerWorkflowExtension};
|
|
2
|
+
//# sourceMappingURL=extension.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extension.mjs","names":[],"sources":["../../../src/adapters/pi/extension.ts"],"sourcesContent":["import { registerDoomHelpContribution } from '@agimon-ai/doompi-extension-contracts/help';\nimport type { ExtensionAPI } from '@earendil-works/pi-coding-agent';\nimport { registerLeaderContribution } from './leader.ts';\nimport { createWorkflowPiExtension } from './workflow/piExtension.ts';\n\nexport function registerWorkflowExtension(pi: ExtensionAPI): void {\n const leader = registerLeaderContribution(pi);\n const help = registerDoomHelpContribution(pi, {\n source: '@agimon-ai/doompi-workflow',\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 createWorkflowPiExtension({ onModeChange: (enabled) => leader.setMode(enabled) })(pi);\n pi.on('session_shutdown', () => {\n help.dispose();\n leader.dispose();\n });\n}\n"],"mappings":"2NAKA,SAAgB,EAA0B,EAAwB,CAChE,IAAM,EAAS,EAA2B,CAAE,EACtC,EAAO,EAA6B,EAAI,CAC5C,OAAQ,6BACR,UAAW,YAAY,IACvB,OAAQ,CACN,CACE,KAAM,gBACN,YAAa,mFACf,CACF,CACF,CAAC,EACD,EAA0B,CAAE,aAAe,GAAY,EAAO,QAAQ,CAAO,CAAE,CAAC,CAAC,CAAC,CAAE,EACpF,EAAG,GAAG,uBAA0B,CAC9B,EAAK,QAAQ,EACb,EAAO,QAAQ,CACjB,CAAC,CACH"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=require("../../types/index.cjs");let t=require("@agimon-ai/doompi-extension-contracts/leader");const n=`doom-workflow`,r={key:`w`,label:e.LEADER_LABEL,order:50};function i(t){return[{id:`${n}.manage`,path:[r,{key:`l`,label:`manage`,detail:`running workflows`}],action:{name:e.LEADER_MANAGE_ACTION}},{id:`${n}.launch`,path:[r,{key:`w`,label:`launch`,detail:`start a workflow`}],action:{name:e.LEADER_LAUNCH_ACTION}},t?{id:`${n}.disable`,path:[r,{key:`e`,label:`exit`,detail:`take back workflow tools`}],action:{name:e.LEADER_DISABLE_ACTION}}:{id:`${n}.enable`,path:[r,{key:`e`,label:`enter`,detail:`give the agent workflow tools`}],action:{name:e.LEADER_ENABLE_ACTION}},{id:`${n}.recover`,path:[r,{key:`r`,label:`recover`,detail:`failed workflow`}],action:{name:e.LEADER_RECOVER_ACTION}}]}function a(n){let r=(0,t.createDoomLeaderContribution)(n,{source:e.PACKAGE_SOURCE,bindings:i(!1)});return{setMode(e){r.update(i(e))},dispose(){r.dispose()}}}exports.registerLeaderContribution=a,exports.workflowLeaderBindings=i;
|
|
2
|
+
//# sourceMappingURL=leader.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"leader.cjs","names":["LEADER_LABEL","LEADER_MANAGE_ACTION","LEADER_LAUNCH_ACTION","LEADER_DISABLE_ACTION","LEADER_ENABLE_ACTION","LEADER_RECOVER_ACTION","createDoomLeaderContribution","PACKAGE_SOURCE"],"sources":["../../../src/adapters/pi/leader.ts"],"sourcesContent":["import { createDoomLeaderContribution, type LeaderBinding } from '@agimon-ai/doompi-extension-contracts/leader';\nimport type { ExtensionAPI } from '@earendil-works/pi-coding-agent';\nimport {\n LEADER_DISABLE_ACTION,\n LEADER_ENABLE_ACTION,\n LEADER_KEY,\n LEADER_LABEL,\n LEADER_LAUNCH_ACTION,\n LEADER_MANAGE_ACTION,\n LEADER_ORDER,\n LEADER_RECOVER_ACTION,\n PACKAGE_SOURCE,\n} from '../../types/index.ts';\n\nconst LEADER_BINDING_PREFIX = 'doom-workflow';\nconst GROUP_SEGMENT = { key: LEADER_KEY, label: LEADER_LABEL, order: LEADER_ORDER } as const;\n\nexport interface WorkflowLeaderHandle {\n /** Republish the menu for the mode's current state. */\n setMode(enabled: boolean): void;\n dispose(): void;\n}\n\n/**\n * The menu as it stands with the mode on or off.\n *\n * One entry on `e` rather than an enable row beside a disable row: only one of\n * the two ever does anything, and a menu that prints both makes the reader\n * check the mode line to find out which. The label says which way it flips.\n */\nexport function workflowLeaderBindings(mode: boolean): LeaderBinding[] {\n return [\n {\n id: `${LEADER_BINDING_PREFIX}.manage`,\n path: [GROUP_SEGMENT, { key: 'l', label: 'manage', detail: 'running workflows' }],\n action: { name: LEADER_MANAGE_ACTION },\n },\n {\n id: `${LEADER_BINDING_PREFIX}.launch`,\n path: [GROUP_SEGMENT, { key: 'w', label: 'launch', detail: 'start a workflow' }],\n action: { name: LEADER_LAUNCH_ACTION },\n },\n mode\n ? {\n id: `${LEADER_BINDING_PREFIX}.disable`,\n path: [GROUP_SEGMENT, { key: 'e', label: 'exit', detail: 'take back workflow tools' }],\n action: { name: LEADER_DISABLE_ACTION },\n }\n : {\n id: `${LEADER_BINDING_PREFIX}.enable`,\n path: [GROUP_SEGMENT, { key: 'e', label: 'enter', detail: 'give the agent workflow tools' }],\n action: { name: LEADER_ENABLE_ACTION },\n },\n {\n id: `${LEADER_BINDING_PREFIX}.recover`,\n path: [GROUP_SEGMENT, { key: 'r', label: 'recover', detail: 'failed workflow' }],\n action: { name: LEADER_RECOVER_ACTION },\n },\n ];\n}\n\nexport function registerLeaderContribution(pi: Pick<ExtensionAPI, 'events' | 'on'>): WorkflowLeaderHandle {\n const contribution = createDoomLeaderContribution(pi, {\n source: PACKAGE_SOURCE,\n bindings: workflowLeaderBindings(false),\n });\n return {\n setMode(enabled) {\n contribution.update(workflowLeaderBindings(enabled));\n },\n dispose() {\n contribution.dispose();\n },\n };\n}\n"],"mappings":"uGAcA,MAAM,EAAwB,gBACxB,EAAgB,CAAE,IAAA,IAAiB,MAAOA,EAAAA,aAAc,MAAA,EAAoB,EAelF,SAAgB,EAAuB,EAAgC,CACrE,MAAO,CACL,CACE,GAAI,GAAG,EAAsB,SAC7B,KAAM,CAAC,EAAe,CAAE,IAAK,IAAK,MAAO,SAAU,OAAQ,mBAAoB,CAAC,EAChF,OAAQ,CAAE,KAAMC,EAAAA,oBAAqB,CACvC,EACA,CACE,GAAI,GAAG,EAAsB,SAC7B,KAAM,CAAC,EAAe,CAAE,IAAK,IAAK,MAAO,SAAU,OAAQ,kBAAmB,CAAC,EAC/E,OAAQ,CAAE,KAAMC,EAAAA,oBAAqB,CACvC,EACA,EACI,CACE,GAAI,GAAG,EAAsB,UAC7B,KAAM,CAAC,EAAe,CAAE,IAAK,IAAK,MAAO,OAAQ,OAAQ,0BAA2B,CAAC,EACrF,OAAQ,CAAE,KAAMC,EAAAA,qBAAsB,CACxC,EACA,CACE,GAAI,GAAG,EAAsB,SAC7B,KAAM,CAAC,EAAe,CAAE,IAAK,IAAK,MAAO,QAAS,OAAQ,+BAAgC,CAAC,EAC3F,OAAQ,CAAE,KAAMC,EAAAA,oBAAqB,CACvC,EACJ,CACE,GAAI,GAAG,EAAsB,UAC7B,KAAM,CAAC,EAAe,CAAE,IAAK,IAAK,MAAO,UAAW,OAAQ,iBAAkB,CAAC,EAC/E,OAAQ,CAAE,KAAMC,EAAAA,qBAAsB,CACxC,CACF,CACF,CAEA,SAAgB,EAA2B,EAA+D,CACxG,IAAM,GAAA,EAAeC,EAAAA,6BAAAA,CAA6B,EAAI,CACpD,OAAQC,EAAAA,eACR,SAAU,EAAuB,EAAK,CACxC,CAAC,EACD,MAAO,CACL,QAAQ,EAAS,CACf,EAAa,OAAO,EAAuB,CAAO,CAAC,CACrD,EACA,SAAU,CACR,EAAa,QAAQ,CACvB,CACF,CACF"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{LEADER_DISABLE_ACTION as e,LEADER_ENABLE_ACTION as t,LEADER_LABEL as n,LEADER_LAUNCH_ACTION as r,LEADER_MANAGE_ACTION as i,LEADER_RECOVER_ACTION as a,PACKAGE_SOURCE as o}from"../../types/index.mjs";import{createDoomLeaderContribution as s}from"@agimon-ai/doompi-extension-contracts/leader";const c=`doom-workflow`,l={key:`w`,label:n,order:50};function u(n){return[{id:`${c}.manage`,path:[l,{key:`l`,label:`manage`,detail:`running workflows`}],action:{name:i}},{id:`${c}.launch`,path:[l,{key:`w`,label:`launch`,detail:`start a workflow`}],action:{name:r}},n?{id:`${c}.disable`,path:[l,{key:`e`,label:`exit`,detail:`take back workflow tools`}],action:{name:e}}:{id:`${c}.enable`,path:[l,{key:`e`,label:`enter`,detail:`give the agent workflow tools`}],action:{name:t}},{id:`${c}.recover`,path:[l,{key:`r`,label:`recover`,detail:`failed workflow`}],action:{name:a}}]}function d(e){let t=s(e,{source:o,bindings:u(!1)});return{setMode(e){t.update(u(e))},dispose(){t.dispose()}}}export{d as registerLeaderContribution,u as workflowLeaderBindings};
|
|
2
|
+
//# sourceMappingURL=leader.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"leader.mjs","names":[],"sources":["../../../src/adapters/pi/leader.ts"],"sourcesContent":["import { createDoomLeaderContribution, type LeaderBinding } from '@agimon-ai/doompi-extension-contracts/leader';\nimport type { ExtensionAPI } from '@earendil-works/pi-coding-agent';\nimport {\n LEADER_DISABLE_ACTION,\n LEADER_ENABLE_ACTION,\n LEADER_KEY,\n LEADER_LABEL,\n LEADER_LAUNCH_ACTION,\n LEADER_MANAGE_ACTION,\n LEADER_ORDER,\n LEADER_RECOVER_ACTION,\n PACKAGE_SOURCE,\n} from '../../types/index.ts';\n\nconst LEADER_BINDING_PREFIX = 'doom-workflow';\nconst GROUP_SEGMENT = { key: LEADER_KEY, label: LEADER_LABEL, order: LEADER_ORDER } as const;\n\nexport interface WorkflowLeaderHandle {\n /** Republish the menu for the mode's current state. */\n setMode(enabled: boolean): void;\n dispose(): void;\n}\n\n/**\n * The menu as it stands with the mode on or off.\n *\n * One entry on `e` rather than an enable row beside a disable row: only one of\n * the two ever does anything, and a menu that prints both makes the reader\n * check the mode line to find out which. The label says which way it flips.\n */\nexport function workflowLeaderBindings(mode: boolean): LeaderBinding[] {\n return [\n {\n id: `${LEADER_BINDING_PREFIX}.manage`,\n path: [GROUP_SEGMENT, { key: 'l', label: 'manage', detail: 'running workflows' }],\n action: { name: LEADER_MANAGE_ACTION },\n },\n {\n id: `${LEADER_BINDING_PREFIX}.launch`,\n path: [GROUP_SEGMENT, { key: 'w', label: 'launch', detail: 'start a workflow' }],\n action: { name: LEADER_LAUNCH_ACTION },\n },\n mode\n ? {\n id: `${LEADER_BINDING_PREFIX}.disable`,\n path: [GROUP_SEGMENT, { key: 'e', label: 'exit', detail: 'take back workflow tools' }],\n action: { name: LEADER_DISABLE_ACTION },\n }\n : {\n id: `${LEADER_BINDING_PREFIX}.enable`,\n path: [GROUP_SEGMENT, { key: 'e', label: 'enter', detail: 'give the agent workflow tools' }],\n action: { name: LEADER_ENABLE_ACTION },\n },\n {\n id: `${LEADER_BINDING_PREFIX}.recover`,\n path: [GROUP_SEGMENT, { key: 'r', label: 'recover', detail: 'failed workflow' }],\n action: { name: LEADER_RECOVER_ACTION },\n },\n ];\n}\n\nexport function registerLeaderContribution(pi: Pick<ExtensionAPI, 'events' | 'on'>): WorkflowLeaderHandle {\n const contribution = createDoomLeaderContribution(pi, {\n source: PACKAGE_SOURCE,\n bindings: workflowLeaderBindings(false),\n });\n return {\n setMode(enabled) {\n contribution.update(workflowLeaderBindings(enabled));\n },\n dispose() {\n contribution.dispose();\n },\n };\n}\n"],"mappings":"ySAcA,MAAM,EAAwB,gBACxB,EAAgB,CAAE,IAAA,IAAiB,MAAO,EAAc,MAAA,EAAoB,EAelF,SAAgB,EAAuB,EAAgC,CACrE,MAAO,CACL,CACE,GAAI,GAAG,EAAsB,SAC7B,KAAM,CAAC,EAAe,CAAE,IAAK,IAAK,MAAO,SAAU,OAAQ,mBAAoB,CAAC,EAChF,OAAQ,CAAE,KAAM,CAAqB,CACvC,EACA,CACE,GAAI,GAAG,EAAsB,SAC7B,KAAM,CAAC,EAAe,CAAE,IAAK,IAAK,MAAO,SAAU,OAAQ,kBAAmB,CAAC,EAC/E,OAAQ,CAAE,KAAM,CAAqB,CACvC,EACA,EACI,CACE,GAAI,GAAG,EAAsB,UAC7B,KAAM,CAAC,EAAe,CAAE,IAAK,IAAK,MAAO,OAAQ,OAAQ,0BAA2B,CAAC,EACrF,OAAQ,CAAE,KAAM,CAAsB,CACxC,EACA,CACE,GAAI,GAAG,EAAsB,SAC7B,KAAM,CAAC,EAAe,CAAE,IAAK,IAAK,MAAO,QAAS,OAAQ,+BAAgC,CAAC,EAC3F,OAAQ,CAAE,KAAM,CAAqB,CACvC,EACJ,CACE,GAAI,GAAG,EAAsB,UAC7B,KAAM,CAAC,EAAe,CAAE,IAAK,IAAK,MAAO,UAAW,OAAQ,iBAAkB,CAAC,EAC/E,OAAQ,CAAE,KAAM,CAAsB,CACxC,CACF,CACF,CAEA,SAAgB,EAA2B,EAA+D,CACxG,IAAM,EAAe,EAA6B,EAAI,CACpD,OAAQ,EACR,SAAU,EAAuB,EAAK,CACxC,CAAC,EACD,MAAO,CACL,QAAQ,EAAS,CACf,EAAa,OAAO,EAAuB,CAAO,CAAC,CACrD,EACA,SAAU,CACR,EAAa,QAAQ,CACvB,CACF,CACF"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
const e=require("../../../services/workflow/backgroundWork.cjs"),t=require("../../../services/workflow/workflowNarration.cjs"),n=require("../../../tui/workflow/workflowInspector.cjs"),r=require("../../../tui/workflow/workflowOverlay.cjs"),i=require("../../../tui/workflow/workflowPicker.cjs"),a=require("../../../tui/workflow/workflowProgressOverlay.cjs"),o=require("../../../tui/workflow/workflowPrompt.cjs"),s=require("../../../tui/workflow/workflowRunPanel.cjs"),c=require("../../../tui/workflow/workflowStepMessage.cjs"),l=require("../../../types/index.cjs"),u=require("./piToolBridge.cjs"),d=require("./piTools.cjs"),f=require("./workflowLauncher.cjs");let p=require("@agimon-ai/doompi-extension-contracts/child-process"),m=require("@agimon-ai/doompi-extension-contracts/protocol"),ee=require("@agimon-ai/doompi-extension-contracts/workflow"),h=require("node:child_process"),te=require("node:fs"),ne=require("node:fs/promises"),g=require("node:module"),_=require("node:path"),re=require("node:url"),ie=require("@agimon-ai/doompi-extension-contracts/leader"),ae=require("@agimon-ai/doompi-extension-contracts/mode"),oe=require("@agimon-ai/doompi-extension-contracts/narration"),se=require("@agimon-ai/doompi-extension-contracts/skills"),ce=require("@agimon-ai/doompi-telemetry"),v=require("@agimon-ai/workflow-mcp"),y=require("@earendil-works/pi-tui");const le={overlay:!0,overlayOptions:{anchor:`top-left`,width:`100%`,maxHeight:`100%`,margin:0}},ue={narrate:()=>void 0},de=`workflow-mcp-follow`,fe=`activate`,pe=`deactivate`;function me(e,t){return{activation:e?`active`:`inactive`,condition:`ready`,...e?{detail:t>0?`${t} active runs`:`tools enabled`,color:`accent`}:{},actions:[...e?[{id:fe,enabled:!1,disabledReason:`Workflow mode is active.`}]:[{id:fe,enabled:!0}],...e?[{id:pe,enabled:!0}]:[{id:pe,enabled:!1,disabledReason:`Workflow mode is inactive.`}]]}}const he=`SPC › w / workflows › w / launch`,b=`ctrl+alt+w`,x=`ctrl+alt+q`,ge={alt:`⌥`,cmd:`⌘`,ctrl:`⌃`,meta:`⌘`,shift:`⇧`};function S(e,t=process.platform){let n=e.split(`+`);return t===`darwin`?n.map(e=>ge[e]??e.toUpperCase()).join(``):n.map(e=>e.charAt(0).toUpperCase()+e.slice(1)).join(`+`)}const C=1e4,w=`running`,T=`completed`,E=`failed`,D=`error`,O=`error`,_e=`doom_workflow.launch_requested`,ve=`../../../../skills`,k={running:`▸`,repair:`⚠`,completed:`✔`,failed:`✖`,separator:`·`};function ye(e,t=process.platform){let n=S(x,t);return e?[`Esc Esc closes this view`,`${S(b,t)} switches typing`,`${n} closes`].join(` ${k.separator} `):`view only ${k.separator} ${n} closes this view`}function be(e,t=process.platform){let n=[S(x,t),`close`];return e?[[`Esc Esc`,`close`],[S(b,t),`switch typing`],n]:[n]}function xe(e,t,n,r){let i=`${k.running} ${e.runKey}`,a=[e.workspace,A(e),e.runner].filter(Boolean).join(` ${k.separator} `),o=`${S(b)} to background`,s=n.fg(`accent`,(0,y.truncateToWidth)(i,r));return(0,y.visibleWidth)(i)+3+(0,y.visibleWidth)(a)<=r&&(s+=` `+n.fg(`muted`,a),(0,y.visibleWidth)(i)+3+(0,y.visibleWidth)(a)+3+(0,y.visibleWidth)(o)<=r&&(s+=` `+n.fg(`dim`,o))),[s,...t.map(e=>(0,y.truncateToWidth)(e,r))]}function Se(e,t,n){let r=[];for(let i of e){let e=i.status===T?k.completed:i.status===E?k.failed:k.running,a=i.status===T?`success`:i.status===E?O:`accent`,o=i.total?` ${k.separator} ${(i.index??0)+1}/${i.total}`:``;if(r.push((0,y.truncateToWidth)(t.fg(a,`${e} ${i.name}`)+t.fg(`dim`,o),n)),i.status===w)for(let e of i.steps){let i=e.status===T?k.completed:e.status===E?k.failed:k.running,a=e.status===w?`muted`:e.status===E?O:`dim`;r.push((0,y.truncateToWidth)(t.fg(a,` ${i} ${e.name}`),n))}}return r}function Ce(e,t){let n=Date.parse(e.startedAt);return Number.isFinite(n)?t.filter(e=>{let t=Date.parse(e.at);return!Number.isFinite(t)||t>=n}):t}function A(e){return e.stale?`stale`:e.stage===D?E:e.stage}const we=`CMUX_WORKSPACE_ID`,Te=`TMUX_PANE`,Ee=`WORKFLOW_TMUX_SESSION_NAME`,De=[`CMUX_SURFACE_ID`,p.SUBAGENT_ROOT_SESSION_ENV,we,Te,`WORKFLOW_LAUNCHER_TYPE`,Ee,`WORKFLOW_STATUS_FILE`],j=`tmux`,M=`cmux`,Oe=`native`,ke=`This run points at Pi's own terminal, so its launcher screen is hidden. Use workflow status instead.`;function Ae(e){let t=e.launcher;if(t?.type===M){let e=process.env[we];return!!e&&t.workspaceId===e}if(t?.type!==j)return!1;let n=process.env[Te],r=process.env[Ee];return!!n&&t.paneId===n||!!r&&t.sessionName===r}const je=`No durable launcher identity is recorded for this run.`;function Me(e){return(0,_.basename)(e,(0,_.extname)(e))}function Ne(){let e={...process.env};for(let t of De)delete e[t];return e}function Pe(){let e=process.env.WORKFLOW_LAUNCHER;return e===j||e===M?e:void 0}function Fe(e){return e.map(e=>`'${e.replaceAll(`'`,`'\\''`)}'`).join(` `)}function Ie(){try{return(0,g.createRequire)(require("url").pathToFileURL(__filename).href).resolve(`@agimon-ai/workflow-mcp/cli`)}catch{return}}const Le=async(e,t,n)=>{await new Promise((r,i)=>{let a=(0,h.spawn)(e,[...t],{...n,detached:!0,stdio:`ignore`});a.once(`error`,i),a.once(`spawn`,()=>{a.off(`error`,i),a.unref(),r()})})};function Re(e,t){if(Array.isArray(e)){for(let n of e)Re(n,t);return}if(!(!e||typeof e!=`object`))for(let[n,r]of Object.entries(e))(n===`interactiveRun`||n===`run`)&&r&&typeof r==`object`&&!Array.isArray(r)?t.push(Object.keys(r)):Re(r,t)}function ze(e){let t=[];if(Re(e,t),t.length!==0)return t[0].filter(e=>t.every(t=>t.includes(e))).sort()}async function N(e,t){let n=[],r=1,i=!0;for(;i;){let a=await e.listRunsPage({page:r,pageSize:100,workspace:t});n.push(...a.items),i=a.hasNextPage,r+=1}return n}function Be(e){return[e.runKey,e.workspace,A(e),e.runner].filter(Boolean).join(` ${k.separator} `)}function P(h={}){return g=>{let y=h.featureFactory?.()??(0,v.createEmbeddedWorkflowFeature)(),{parser:ge,recoverService:E,registry:O,runService:we,runTool:Te}=y,Ee=h.spawnDetached??Le,De=h.monitorIntervalMs??3e4,Re=h.followIntervalMs??2e3,P,Ve=!1,F,He=!1,I,Ue=new Map,L=new Map,We=new Set,Ge=0,R,Ke,qe=ue,Je=!1,Ye,z=0,B=!1,Xe,Ze=(0,ae.registerMinorModeOwner)(g,{descriptor:{source:l.PACKAGE_SOURCE,id:`workflow`,label:`Workflow`,description:`Workflow discovery, launch, inspection, control, and recovery tools.`,order:20,actions:[{id:fe,label:`Activate`,description:`Enable workflow tools for this session.`,contexts:[`tui`,`headless`],parameters:[]},{id:pe,label:`Deactivate`,description:`Disable workflow tools without stopping active runs.`,contexts:[`tui`,`headless`],parameters:[]}]},initialState:me(!1,0),async handleAction(e,t,n){if(e===fe)return await Gt(!0,n.context),{message:`Workflow mode activated.`};if(e===pe)return await Gt(!1,n.context),{message:`Workflow mode deactivated.`};throw Error(`Unknown workflow mode action: ${e}`)}}),Qe,V,$e,et=!1,tt=new Map,H,nt=[],U=new Map,rt=[],W=new Map,it=0,at=new a.WorkflowProgressOverlay,ot,G,K=e=>(G??=(0,ce.createDoomTelemetry)({serviceName:`doom-workflow`,packageName:`@agimon-ai/doompi-workflow`,cwd:e?.cwd,env:process.env,enableLogs:!0,enableTraces:!0}),G),st=e=>{e&&(ot=(0,p.resolveRootSessionId)(e))},ct=()=>{let e=ot??Qe?.sessionManager.getSessionId();return e?(0,p.resolveRootSessionId)(e):void 0},lt=(e,t)=>e.sessionManager.getSessionId().trim()===t,ut=y.createRecoverTool({ownerSessionId:ct}),dt=y.createControlTool(),q=new Set,ft=e=>{let t=Date.now();return q.add(e),K().recordEvent(`doom_workflow.run_started`,{outcome:`started`}),e.then(()=>(q.delete(e),K().recordEvent(`doom_workflow.run_finished`,{duration_ms:Date.now()-t,outcome:`completed`})),n=>(q.delete(e),K().recordError(`doom_workflow.run_failed`,n,{duration_ms:Date.now()-t}))),e},pt=(e,t)=>{let n=ze(ge.parseWorkflowFile(e));if(!(!n||n.includes(t)))return`Runner ${JSON.stringify(t)} is not available across this workflow. Compatible runners: ${n.join(`, `)||`none`}.`},mt=d.createWorkflowLaunchExecutor({activeRunCount:async()=>z,observeSession:st,rejectRunner:pt,runTool:Te,trackPendingRun:ft,...h.launchAckPollMs===void 0?{}:{launchAckPollMs:h.launchAckPollMs},...h.launchAckTimeoutMs===void 0?{}:{launchAckTimeoutMs:h.launchAckTimeoutMs},findLaunchedRun:async({sessionId:e,since:t,workflowPath:n})=>{let r=Me(n),i=n=>{if(!u.isSessionRun(n,e)||n.stale||n.workflowId&&n.workflowId!==r)return!1;let i=Date.parse(n.startedAt);return Number.isFinite(i)&&i>=t-2e3};return H?(await H.refresh(),rt.find(i)):(await N(O)).find(i)},onLateFailure:(e,t)=>{let n=e instanceof Error?e.message:String(e);t.ui.notify(u.launchNotice(`Workflow launch failed after it was reported started.\n${n}`),`warning`)},onLaunch:async e=>(await K(e).recordEvent(_e,{outcome:`requested`}),Z(e,!1))}),ht=()=>{let e=[...W.values()].sort((e,t)=>Date.parse(e.startedAt)-Date.parse(t.startedAt)||X(e).localeCompare(X(t)));at.update(e.map(e=>a.workflowProgressRow(e,U.get(X(e))??[])))},J=e=>{it+=1;let t=X(e);e.stage===w&&!e.stale?W.set(t,e):W.delete(t),ht()},gt=e=>{W.clear();for(let t of e)W.set(X(t),t);ht()},_t=(e,t)=>{let n=U.get(e)??[];n.push(t),U.set(e,n)},Y,vt=e=>{Y||(Y=setTimeout(()=>{Y=void 0,Z(e,!0)},120),Y.unref?.())},yt=e=>{U.delete(e)},bt=async e=>{let t=(0,p.resolveRootSessionId)(e.sessionManager.getSessionId()),n=y.createRunControl({recordFilter:e=>u.isSessionRun(e,t)});H=n,nt.push(n.on(`step`,t=>{let n=X(t.record);_t(n,{at:t.at,job:t.job,status:t.status,step:t.step,type:`step`,...t.reason===void 0?{}:{reason:t.reason}}),J(t.record),vt(e)}),n.on(`job`,t=>{let n=X(t.record);_t(n,{at:t.at,job:t.job,status:t.status,type:`job`,...t.index===void 0?{}:{index:t.index},...t.reason===void 0?{}:{reason:t.reason},...t.total===void 0?{}:{total:t.total}}),J(t.record),vt(e)}),n.on(`runStarted`,t=>{rt.unshift(t.record),rt.length>32&&(rt.length=32),J(t.record),vt(e)}),n.on(`runUpdated`,t=>{J(t.record),Z(e,!0)}),n.on(`runFinished`,t=>{J(t.record),Z(e,!0)})),await n.start()},xt=()=>{Y&&clearTimeout(Y),Y=void 0;for(let e of nt.splice(0))e();H?.dispose(),H=void 0,U.clear(),it+=1,W.clear(),ht()},St=()=>{V?.teardown(),V?.handle?.hide(),V=void 0},X=e=>e.runId??`${e.workspace}/${e.runKey}`,Ct=()=>{let e=me(B,z),t=JSON.stringify(e);t!==Xe&&(Xe=t,Ze.publish(e))},Z=async(e,n)=>{let r=K(e);if(Ve){r.recordEvent(`doom_workflow.monitor_skipped`,{outcome:`already_running`});return}let i=Date.now(),a=it;Ve=!0;try{let o=await N(O),s=(0,p.resolveRootSessionId)(e.sessionManager.getSessionId()),c=lt(e,s),l=o.filter(e=>u.isSessionRun(e,s)),d=l.filter(e=>e.stage===w&&!e.stale),f=l.filter(e=>e.stage===D);a===it&>(d),r.recordEvent(`doom_workflow.monitor_counts`,{"workflow.run_count":l.length,"workflow.running_count":d.length,"workflow.failed_count":f.length,mode:B?`on`:`off`});let m=u.runsForSession(o,s).filter(e=>!e.stale);z=m.length,Ct(),R?.update(m.map(e=>({id:e.runId??e.runKey,sessionId:s}))),Ot(new Set(l.map(e=>X(e))));let ee=new Map(l.map(e=>[X(e),e.stage]));for(let e of l){let r=X(e),i=Ue.get(r),a=Date.parse(e.startedAt),o=i===void 0&&(Ge===0||!Number.isFinite(a)||a>=Ge);Je&&t.narrateWorkflowTransition(qe,e,i===w||i===T||i===D?i:void 0,o),!(!n||!c||e.stage===w)&&!We.has(r)&&(i===w||o)&&L.set(r,e)}let h=new Set([...l.filter(e=>e.stage===w&&!e.stale).map(e=>X(e)),...L.keys()]);for(let e of U.keys())h.has(e)||yt(e);if(n)for(let t of l){if(Ue.get(X(t))!==w||t.stage===w)continue;let n=t.stage===T,i=t.failedJob?` at ${t.failedJob}`:``;e.ui.notify(n?`${k.completed} ${t.runKey} completed`:`${k.failed} ${t.runKey} ${A(t)}${i}`,n?`info`:`warning`),r.recordEvent(`doom_workflow.run_transition`,{outcome:n?`completed`:A(t)})}if(c&&L.size>0)try{let e=[...L.values()],t=await Promise.all(e.map(async e=>{if(e.stage===T)return u.finishedRunSummary(e,[]);let t=X(e),n=U.get(t)??Ce(e,await(0,v.readWorkflowProgress)(O.runDirectoryFor(e)));return u.finishedRunSummary(e,(0,v.summarizeWorkflowProgress)(n))}));g.sendMessage({customType:`workflow-run-finished`,content:t.join(`
|
|
2
|
+
|
|
3
|
+
`),display:!0,details:{runIds:e.map(e=>e.runId??X(e))}},{triggerTurn:!0,deliverAs:`steer`});for(let t of e){let e=X(t);We.add(e),L.delete(e),yt(e)}}catch(e){await r.recordError(`doom_workflow.terminal_delivery_failed`,e,{"workflow.pending_terminal_count":L.size})}Ue=ee,r.recordEvent(`doom_workflow.monitor_finished`,{duration_ms:Date.now()-i,outcome:`completed`})}catch(t){await r.recordError(`doom_workflow.monitor_failed`,t,{duration_ms:Date.now()-i}),e.ui.notify(`Workflow status refresh failed: ${t instanceof Error?t.message:String(t)}`,`warning`)}finally{Ve=!1}},wt=e=>{F&&clearInterval(F),F=void 0,I=void 0,e.ui.setWidget(de,void 0)},Tt=async e=>{let t=e.launcher;if(!t)return[je];if(Ae(e))return[ke];if(t.type===j){let n=t.paneId??t.sessionId??t.sessionName,r=await g.exec(j,[`capture-pane`,`-p`,`-t`,n,`-S`,`-24`],{timeout:C});return r.code===0?r.stdout.trimEnd().split(`
|
|
4
|
+
`).slice(-24):[`Unable to read ${e.runKey}: ${r.stderr.trim()||`tmux exited ${r.code}`}`]}if(t.type===Oe)return[`This run is hosted natively and has no launcher screen to read.`];let n=await g.exec(M,[`read-screen`,`--workspace`,t.workspaceId,`--scrollback`,`--lines`,`24`],{timeout:C});return n.code===0?n.stdout.trimEnd().split(`
|
|
5
|
+
`).slice(-24):[`Unable to read ${e.runKey}: ${n.stderr.trim()||`cmux exited ${n.code}`}`]},Et=new Map,Dt=async e=>{let t=X(e),n=Et.get(t)??{at:0,lines:[]};if(Et.set(t,n),n.inFlight||Date.now()-n.at<750)return n.lines;n.inFlight=Tt(e);try{n.lines=await n.inFlight}finally{n.at=Date.now(),n.inFlight=void 0}return n.lines},Ot=e=>{for(let t of Et.keys())e.has(t)||Et.delete(t)},kt=async e=>{let t=e.launcher;if(!t)return je;if(Ae(e))return ke;if(t.type===Oe)return`This run is hosted natively rather than in a multiplexer, so there is no separate window to bring forward.`;if(t.type===M)return await g.exec(M,[`select-workspace`,`--workspace`,t.workspaceId],{timeout:C}),`Opened ${e.runKey} in cmux.`;if(!process.env.TMUX)return`Pi is not attached to tmux. Follow the output instead, or attach to the recorded tmux session manually.`;let n=t.sessionId??t.sessionName,r=await g.exec(j,[`switch-client`,`-t`,n],{timeout:C});return r.code===0?(t.paneId&&await g.exec(j,[`select-pane`,`-t`,t.paneId],{timeout:C}),`Opened ${e.runKey} in tmux.`):r.stderr.trim()||`Unable to open tmux session ${n}.`},At=async(e,t)=>{if(!He){He=!0;try{let n=(0,v.summarizeWorkflowProgress)(Ce(e,await(0,v.readWorkflowProgress)(O.runDirectoryFor(e)))),r=n.length>0?void 0:await Dt(e);t.ui.setWidget(de,(t,i)=>({render:t=>xe(e,r??Se(n,i,t),i,t),invalidate:()=>{}}),{placement:`aboveEditor`})}finally{He=!1}}},jt=async(e,t)=>{wt(t),I=e,await At(e,t),F=setInterval(()=>{I&&At(I,t)},Re),F.unref?.()},Mt=async(e,t)=>{let n=e.launcher;if(!(!n||n.type===Oe||Ae(e))){if(n.type===j){let e=n.paneId??n.sessionId??n.sessionName;await g.exec(j,[`send-keys`,`-t`,e,`-l`,t],{timeout:C});return}await g.exec(M,[`send`,`--workspace`,n.workspaceId,t],{timeout:C})}},Nt=async(e,t)=>{St();let n=!Ae(e)&&(e.launcher?.type===j||e.launcher?.type===M),i=new r.TerminalInputBatcher(t=>Mt(e,t)),a=[],o=[],c,l=!1,u=async()=>{a=(0,v.summarizeWorkflowProgress)(Ce(e,await(0,v.readWorkflowProgress)(O.runDirectoryFor(e)))),o=await Dt(e)};await u();let d=()=>{c&&clearInterval(c),c=void 0,i.dispose()},f={interactive:n,runKey:e.runKey,teardown:d};V=f;let p=()=>{St(),t.ui.notify(`Closed the ${e.runKey} view. The run keeps going.`,`info`)};return t.ui.custom((r,m,ee,h)=>(c=setInterval(()=>{l||(l=!0,u().finally(()=>{l=!1,r.requestRender()}))},200),c.unref?.(),new s.WorkflowRunPanelComponent(r,m,{runKey:e.runKey,label:`${k.running} ${Be(e)}`,breadcrumb:`SPC › w / workflows › run`,interactive:n,footer:ye(n),hints:be(n),snapshot:e=>({progress:Se(a,m,e),output:o}),onUnfollow:()=>{f.handle?.unfocus(),t.ui.notify(`Typing goes to this session. The run keeps going.`,`info`)},onClose:p,sendInput:e=>i.write(e),flushInput:()=>void i.flush(),unfollowShortcut:b,closeShortcut:x,onDispose:()=>{d(),V===f&&(V=void 0),h()}})),{...s.runPanelUiOptions(n),onHandle:e=>{f.handle=e}}),t.ui.notify(n?`${e.runKey}: typing goes to the run. Press Esc twice to close, ${S(b)} to type here instead.`:`${e.runKey}: view only, because this run is hosted natively. ${S(x)} closes it.`,`info`),n?[`Showing ${e.runKey} over Pi. Typing goes to the run.`,`Pressing Escape twice quickly closes the view, and a single Escape still reaches the run.`,`${S(b)} moves typing between the run and this session,`,`and ${S(x)} closes the view. The run keeps going either way.`].join(` `):[`Showing ${e.runKey} over Pi, as a view only:`,`the run is hosted natively rather than in a multiplexer, so there is no terminal to type into.`,`Typing stays with this session, and ${S(x)} closes the view.`].join(` `)},Pt=async(e,t=!1)=>{if(!(et||V||I)){et=!0;try{let r=await e.ui.custom((e,t,r,i)=>new n.WorkflowInspectorComponent(e,t,{list:async()=>{let e=ct(),t=(await N(O)).filter(t=>t.stage===w&&!t.stale&&u.isSessionRun(t,e));return tt=new Map(t.map(e=>[X(e),e])),Promise.all(t.map(async e=>({displayName:e.displayName,executionState:e.executionState,jobs:(0,v.summarizeWorkflowProgress)(Ce(e,await(0,v.readWorkflowProgress)(O.runDirectoryFor(e)))),key:X(e),output:[],runKey:e.runKey,startedAt:e.startedAt,workspace:e.workspace})))},output:async e=>{let t=tt.get(e.key);return t?Dt(t):[`Workflow is no longer active.`]}},i),{...le,onHandle:e=>{$e=e}});if(r){let n=await Q(r);t?await zt(n,e):await Nt(n,e)}}finally{$e=void 0,et=!1,tt.clear()}}},Q=async(e,t=ct())=>{st(t);let n=(await N(O,e.workspace)).filter(n=>n.runKey===e.runKey&&u.isSessionRun(n,t));if(n.length>1)throw Error(u.withOptions(`Workflow run key ${JSON.stringify(e.runKey)} exists in more than one workspace in this session.`,[`Retry with the workspace from the run identity.`]));let r=n[0];if(!r)throw Error(u.withOptions(`No workflow run matches ${JSON.stringify(e.runKey)} in this session.`,[`workflow_run with action status: use a run key from this session before retrying.`,`Tell the user this session did not launch that run rather than guessing a similar key. A run launched by another Pi session, or from the command line, is managed there or with the workflow-mcp CLI.`]));return r},Ft=async e=>{let t=(await N(O,e.workspace)).filter(t=>t.runKey===e.runKey&&t.stage===D);if(t.length>1)throw Error(u.withOptions(`Failed workflow run key ${JSON.stringify(e.runKey)} exists in more than one workspace.`,[`Retry with the workspace from the recovery picker or run identity.`]));let n=t[0];if(!n)throw Error(u.withOptions(`No failed workflow run matches ${JSON.stringify(e.runKey)}.`,[`Use the recovery picker to select an interrupted or failed run, or retry with its workspace.`,`Do not recover a running or completed workflow.`]));return n},It=async e=>{let t=O.runDirectoryFor(e),n=[(0,_.resolve)(t,`changelog.md`),(0,_.resolve)(t,`context.md`),(0,_.resolve)(t,`progress.ndjson`)],r=[];for(let e of n)try{let t=await(0,ne.readFile)(e,`utf-8`);r.push(`--- ${(0,_.basename)(e)} ---`,t.slice(-65536).trimEnd())}catch(e){if(e.code!==`ENOENT`)throw e}return r.length>0?r:[`No durable recovery evidence files are recorded for this run.`]},Lt=async(e,t)=>{let n=await Ft(e);return await K(t).recordEvent(`doom_workflow.recovery_evidence_requested`,{outcome:`requested`}),[`Workflow: ${Be(n)}`,`--- run.json ---`,JSON.stringify(n,null,2),...await It(n)].join(`
|
|
6
|
+
`)},Rt=async(e,t)=>{let n=Ie();if(!n)return;let r=await Ft(e),i;try{i=ge.parseWorkflowFile(r.workflowPath)}catch{return}if(!i[`launch-command`])return;let a=(0,p.resolveRootSessionId)(t.sessionManager.getSessionId()),o=await O.claimRunRecovery(r.workspace,r.runKey,a),s=r.originalRepoPath??t.cwd,c=Fe([process.execPath,n,`recover-workflow`,r.runKey,`--workspace`,r.workspace,`--recovery-claim`,o.claimId,...e.runner?[`--runner`,e.runner]:[]]),l=Pe();try{await Ee(process.execPath,[n,`launch-process`,...l?[`--launcher`,l]:[],`--name`,`${r.displayName} recover`,`--cwd`,s,`--command`,c],{cwd:s,env:Ne()})}catch(e){throw await O.releaseRunRecoveryClaim(r.workspace,r.runKey,o.claimId),e}return await K(t).recordEvent(`doom_workflow.recover_delegated`,{launcher:l??`auto`,outcome:`delegated`}),`Recovering ${r.runKey} in a launcher of its own, so this session keeps its terminal. It reports back here when it ends.`},zt=async(e,t)=>{let n=e;for(;;){let e=[`Open output`],r=n.executionState??w;r===w&&e.push(`Pause`),r===`paused`&&e.push(`Resume`),n.stage===w&&e.push(`Stop`),e.push(`Back`);let i=await o.openWorkflowChoice(t,{title:`Manage ${n.runKey}`,breadcrumb:`SPC › w / workflows › l / manage`,choices:e});if(!i||i===`Back`)return;if(n=await Q(n),n.stage!==w){t.ui.notify(`${n.runKey} is no longer running.`,`warning`);return}if(i===`Open output`){await Nt(n,t);return}if(i===`Stop`){if(!await t.ui.confirm(`Stop ${n.runKey}?`,`The workflow will be interrupted and will not be restarted automatically.`))continue;if(n=await Q(n),n.stage!==w){t.ui.notify(`${n.runKey} is no longer running.`,`warning`);return}}if(i!==`Pause`&&i!==`Resume`&&i!==`Stop`)continue;if(!n.runId)throw Error(`${n.runKey} has no controllable run generation.`);let a=await dt.execute({action:i.toLowerCase(),expectedRunId:n.runId,...i===`Stop`?{reason:`Stopped from the workflow manager.`}:{},runKey:n.runKey,workspace:n.workspace});if(a.isError)throw Error(u.toolResultText(a)||`Unable to ${i.toLowerCase()} workflow.`);t.ui.notify(u.toolResultText(a)||`${i} requested for ${n.runKey}.`,`info`),await Z(t,!1),n=await Q(n)}},Bt=(e,t)=>({editor:(t,n)=>e.ui.editor(t,n),input:(n,r)=>o.openWorkflowInput(e,{title:n,breadcrumb:t,...r?{fallback:r}:{}}),notify:(t,n)=>e.ui.notify(t,n),select:(n,r)=>o.openWorkflowChoice(e,{title:n,breadcrumb:t,choices:r})}),Vt=async e=>{if(!e.hasUI){e.ui.notify(`Workflow launch needs an interactive Pi session.`,`warning`);return}let t=await f.openWorkflowLaunchWizard({compatibleRunners:ze,cwd:e.cwd,launch:(e,t)=>mt.execute(e,t),listWorkflowsTool:y.listWorkflowsTool,parseWorkflow:e=>ge.parseWorkflowFile(e),pickWorkflow:t=>i.openWorkflowPickerOverlay(e,{title:`WORKFLOW LAUNCHER`,breadcrumb:he,unit:`workflows`,action:`launch`,filterPlaceholder:`type to filter by name, path or tag`,emptyMessage:`No workflow definitions were found in this repository.`},t.map(e=>({key:e.path,name:e.name,detail:e.relativePath,search:[e.description,...e.tags].join(` `),value:e}))),ui:Bt(e,he)},e);t&&e.ui.notify(u.launchNotice(u.toolResultText(t)),`info`)},Ht=async e=>{let t=(await N(O)).filter(e=>e.stage===D);if(t.length===0){e.ui.notify(`No failed workflows are available to recover.`,`info`);return}let n=await i.openWorkflowPickerOverlay(e,{title:`WORKFLOW RECOVERY`,breadcrumb:`SPC › w / workflows › r / recover`,unit:`failed runs`,action:`recover`,filterPlaceholder:`type to filter by name, run key or failed job`,emptyMessage:`No failed workflows are available to recover.`},t.map(e=>({key:`${e.workspace}/${e.runKey}`,name:e.displayName,detail:`${e.workspace}/${e.runKey}${e.failedJob?` · ${e.failedJob}`:``}`,...e.failedJob?{search:e.failedJob}:{},value:e})));if(!n)return;let r=await Ft(n);if(r.stage!==D)throw Error(`${r.runKey} is no longer failed.`);let a=(0,_.resolve)((0,_.dirname)((0,re.fileURLToPath)(require("url").pathToFileURL(__filename).href)),ve,`workflow-recovery`,`SKILL.md`);if(!(0,te.existsSync)(a))throw Error(`Workflow recovery skill is unavailable at ${a}.`);Ut(!0),e.ui.notify(`Workflow tools enabled. The agent will investigate this failure and choose the safe next step.`,`info`),g.sendUserMessage([`Investigate recovery for this failed workflow run. This selection is permission to investigate, not permission to recover blindly.`,`Run identity: ${JSON.stringify({runKey:r.runKey,workspace:r.workspace})}`,`Read the package-owned recovery skill with the read tool at ${JSON.stringify(a)} before acting.`,`Inspect status, every relevant log source, process state, the staged run.json, and only the repair referenced by activeRepairId.`,`Choose exactly one outcome: recover, launch fresh, or defer. Never edit issue.md or repair.json, never scan historical repairs, and never pass recovery dryRun or a job override.`,`Do not recover terminal, approval-pending, blocked, or otherwise unsafe work. If evidence is incomplete, defer and explain what is missing.`,`If recovery is justified, preserve the recorded worktree and repair metadata, then verify real process and registry progress before reporting success.`].join(`
|
|
7
|
+
`),{deliverAs:`followUp`})},Ut=e=>{let t=new Set(g.getActiveTools());for(let n of d.WORKFLOW_PI_TOOL_NAMES)e?t.add(n):t.delete(n);g.setActiveTools([...t]),B=e,Ct(),h.onModeChange?.(e)},Wt=()=>B?[`${k.completed} Workflow mode on`,`Ask for workflows in plain language: the agent can list, launch, inspect, pause, resume, stop, and recover runs.`,`${S(b)} stops following a run without stopping it.`].join(`
|
|
8
|
+
`):[`Workflow mode off`,`Press SPC w e to give the agent its workflow tools. Runs already going are unaffected.`].join(`
|
|
9
|
+
`),Gt=async(e,t)=>{Ut(e),B||wt(t),t.ui.notify(Wt(),`info`),await Z(t,!1)};g.registerShortcut(b,{description:`Open workflows or move typing between a workflow run and this session`,handler:async e=>{let t=V?.handle;if(t){if(!V?.interactive){e.ui.notify(`This run is hosted natively, so the view takes no typing. Your keys stay here.`,`info`);return}t.isFocused()?(t.unfocus(),e.ui.notify(`Typing goes to this session. The run keeps going.`,`info`)):(t.focus(),e.ui.notify(`Typing goes to ${V?.runKey}.`,`info`));return}if(I){wt(e),e.ui.notify(`Workflow continues in the background.`,`info`);return}await Pt(e)}}),g.registerShortcut(x,{description:`Close the workflow run view`,handler:e=>{if(!V)return;let t=V.runKey;St(),e.ui.notify(`Closed the ${t} view. The run keeps going.`,`info`)}}),g.on(`resources_discover`,()=>{if(!B)return{skillPaths:[]};let e=(0,_.resolve)((0,_.dirname)((0,re.fileURLToPath)(require("url").pathToFileURL(__filename).href)),ve);return{skillPaths:(0,te.existsSync)(e)?[e]:[]}});let Kt=(0,_.resolve)((0,_.dirname)((0,re.fileURLToPath)(require("url").pathToFileURL(__filename).href)),ve);(0,te.existsSync)(Kt)&&(0,se.registerDoomSkillSource)(g,{source:`@agimon-ai/doompi-workflow`,directories:[Kt]}),g.registerMessageRenderer(`workflow-step`,(e,{outputPad:t},n)=>{let r=c.isWorkflowStepMessageDetails(e.details)?e.details:void 0,i=typeof e.content==`string`?e.content:JSON.stringify(e.content);return c.renderWorkflowStepMessage(r,i,t,n)}),d.registerWorkflowPiTools(g,{runTool:Te,recoverTool:ut,launchExecutor:mt,controlTool:dt,requireSessionRun:Q,requireRecoverableRun:Ft,readRecoveryEvidence:Lt,delegateRecovery:Rt,trackPendingRun:ft,observeSession:st,activeRunCount:async()=>z,onLaunch:async e=>(await K(e).recordEvent(_e,{outcome:`requested`}),Z(e,!1)),followRun:async(e,t)=>{let n=await Q(e,t.sessionManager.getSessionId());return await K(t).recordEvent(`doom_workflow.follow_started`,{outcome:`started`}),await jt(n,t),`Following ${Be(n)}. Press ${S(b)} to stop following; the run keeps going.`},tailRun:async(e,t)=>{let n=await Q(e,t.sessionManager.getSessionId());return await K(t).recordEvent(`doom_workflow.tail_requested`,{outcome:`requested`}),[`Workflow: ${Be(n)}`,`Stage: ${A(n)}`,`Raw launcher output is hidden from chat. Use action follow or open to view it.`].join(`
|
|
10
|
+
`)},openRun:async(e,t)=>{let n=await Q(e,t.sessionManager.getSessionId());return await K(t).recordEvent(`doom_workflow.open_requested`,{outcome:`requested`}),t.hasUI?Nt(n,t):kt(n)},rejectRunner:pt});let $=e=>e;(0,ie.registerDoomLeaderActionHandlers)(g,{source:`@agimon-ai/doompi-workflow`,handlers:{[l.LEADER_ENABLE_ACTION]:e=>Gt(!0,$(e)),[l.LEADER_DISABLE_ACTION]:e=>Gt(!1,$(e)),[l.LEADER_MANAGE_ACTION]:e=>Pt($(e),!0),[l.LEADER_LAUNCH_ACTION]:e=>Vt($(e)),[l.LEADER_RECOVER_ACTION]:e=>Ht($(e))},onError:(e,t,n)=>{$(n).ui.notify(e instanceof Error?e.message:String(e),`error`)}}),g.on(`session_start`,async(t,n)=>{let r=K(n);await r.recordEvent(`doom_workflow.session_started`,{mode:process.env.WORKFLOW_MCP_MODE===`on`?`on`:`off`,outcome:`started`}),Qe=n,at.setUICtx(n.ui),xt(),P&&clearInterval(P),P=void 0;let i=n.sessionManager.getSessionId();qe=(0,oe.createNarrationRequester)({bus:g.events,source:l.PACKAGE_SOURCE,sessionId:i});let a=(0,p.resolveRootSessionId)(i);st(a),Ge=Date.now(),Je=!1,Ue=new Map,L.clear(),We.clear(),Ke=(0,m.createProtocolRuntime)({bus:g.events,source:`@agimon-ai/doompi-workflow`,sessionId:a}),Ye?.(),Ye=Ke.onNotification(ee.WorkflowModeChanged,({mode:e})=>{let t=e===`on`;t!==B&&(Ut(t),r.recordEvent(`doom_workflow.mode_changed`,{mode:e}),Z(n,!1))}),Ut(process.env.WORKFLOW_MCP_MODE===`on`),R?.dispose(),R=e.registerRunProvider(Ke),await bt(n),await Z(n,!1),Je=!0,P&&clearInterval(P),P=setInterval(()=>void Z(n,!0),De),P.unref?.()}),g.on(`session_shutdown`,async(e,t)=>{if(q.size>0){for(let e of[we,E])e.interrupt(`SIGTERM`,{phase:`workflow`,source:`pi-session-shutdown`});await Promise.race([Promise.allSettled(q),new Promise(e=>setTimeout(e,1e4).unref?.())])}xt(),P&&clearInterval(P),P=void 0,R?.dispose(),R=void 0,Ye?.(),Ye=void 0,Ke=void 0,qe=ue,Je=!1,$e?.hide(),$e=void 0,et=!1,tt.clear(),Et.clear(),St(),z=0,wt(t),at.dispose(),B=!1,Xe=void 0,Ze.publish(me(!1,0)),await G?.recordEvent(`doom_workflow.session_finished`,{"workflow.pending_count":q.size,outcome:q.size===0?`completed`:`interrupted`}),await G?.shutdown(),G=void 0})}}exports.compatibleRunners=ze,exports.createWorkflowPiExtension=P,exports.panelHint=ye,exports.panelHints=be,exports.shortcutLabel=S;
|
|
11
|
+
//# sourceMappingURL=piExtension.cjs.map
|