@agimon-ai/doompi-workflow 0.0.1-alpha.101 → 0.0.1-alpha.104
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 +34 -10
- package/dist/extensions/pi.cjs +37 -94
- package/dist/extensions/pi.cjs.map +1 -1
- package/dist/extensions/pi.d.mts.map +1 -1
- package/dist/extensions/pi.mjs +33 -90
- package/dist/extensions/pi.mjs.map +1 -1
- package/dist/extensions/server.cjs +569 -45
- package/dist/extensions/server.cjs.map +1 -1
- package/dist/extensions/server.d.mts.map +1 -1
- package/dist/extensions/server.mjs +570 -46
- package/dist/extensions/server.mjs.map +1 -1
- package/dist/extensions/web.mjs +3234 -3249
- package/dist/extensions/web.mjs.map +1 -1
- package/dist/{workflowTerminal-QzufjHCa.mjs → workflowTerminal-Cuc79Gog.mjs} +125 -13
- package/dist/workflowTerminal-Cuc79Gog.mjs.map +1 -0
- package/dist/{workflowTerminal-Bb4bLtLu.cjs → workflowTerminal-DeJ9BBta.cjs} +208 -12
- package/dist/workflowTerminal-DeJ9BBta.cjs.map +1 -0
- package/package.json +8 -8
- package/skills/workflow-recovery/SKILL.md +17 -15
- package/src/prompts/doompi-use-workflow/SKILL.md +1 -1
- package/src/types/webWorkflows.ts +4 -0
- package/dist/workflowTerminal-Bb4bLtLu.cjs.map +0 -1
- package/dist/workflowTerminal-QzufjHCa.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -64,9 +64,10 @@ Core tools are:
|
|
|
64
64
|
- `list_workflows`: discover workflows.
|
|
65
65
|
- `launch_workflow`: register and start an asynchronous run.
|
|
66
66
|
- `workflow_run`: inspect or control a run through supported actions.
|
|
67
|
+
- `workflow_tools` (server/web sessions): read failed-run recovery evidence or resume the existing run.
|
|
67
68
|
|
|
68
|
-
Launch returns after the run is registered, not after all jobs complete. Use status
|
|
69
|
-
|
|
69
|
+
Launch returns after the run is registered, not after all jobs complete. Use status for progress
|
|
70
|
+
and wait for a terminal notification. The TUI also supports follow controls.
|
|
70
71
|
|
|
71
72
|
In the TUI, `SPC w l` lists the repository's workflows and launches the one under the cursor with
|
|
72
73
|
`r`, `SPC w r` inspects this session's runs, `SPC w c` opens recovery, and `SPC w e`
|
|
@@ -96,10 +97,31 @@ adopts the eligible failure, validates the evidence, and transfers ownership to
|
|
|
96
97
|
Live controls remain scoped to the owning session. Recovery does not launch a second copy beside a
|
|
97
98
|
still-running job and does not guarantee that parent-session work survives shutdown.
|
|
98
99
|
|
|
99
|
-
The package publishes `workflow-recovery` for
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
100
|
+
The package publishes `workflow-recovery`, `doompi-author-workflow` for writing workflow
|
|
101
|
+
definitions, and `doompi-use-workflow` for launching, monitoring, and recovering runs while Workflow
|
|
102
|
+
mode is active. Deactivating Workflow mode hides them; cached files may remain.
|
|
103
|
+
|
|
104
|
+
## Workflow sessions
|
|
105
|
+
|
|
106
|
+
In a server (web cockpit) session, a launch does not run in the session that asked for it. The
|
|
107
|
+
session creates a child **workflow session** (provenance `workflow-session`), nested under it in the
|
|
108
|
+
rail, and hands it the launch. The workflow session owns the run (`PI_SESSION_ID`), runs its step
|
|
109
|
+
sessions, and has Workflow mode on, so its agent can troubleshoot and recover the run. The run's
|
|
110
|
+
environment also carries `DOOMPI_WORKFLOW_LAUNCHER_SESSION_ID`, naming the launching session, which
|
|
111
|
+
lists the run in its Activity group, is told when it ends, and may read its status.
|
|
112
|
+
|
|
113
|
+
- The workflow session's rail row shows the run (`workflow · build › test · 4m`, or a failure or
|
|
114
|
+
pause marked like a pending question) through the host's per-session activity line.
|
|
115
|
+
- A failure or finish is posted into the workflow session's conversation as a notice, without
|
|
116
|
+
starting an agent turn. The owner agent also gets a live brief of its runs on every turn.
|
|
117
|
+
- After a success, a workflow session nobody interacted with asks its launcher to release it, and it
|
|
118
|
+
becomes `stopped · open to wake`. A session whose run failed or paused, or whose agent you
|
|
119
|
+
prompted, stays live until you remove it. Removing or restarting a live workflow session stops its
|
|
120
|
+
run.
|
|
121
|
+
- Recovery is refused from any session other than the owner while the owner is live.
|
|
122
|
+
|
|
123
|
+
Launches from inside a workflow session, and launches in hosts without a session service (the TUI
|
|
124
|
+
and CLI), run in place as before.
|
|
103
125
|
|
|
104
126
|
Each Pi extension instance uses the runner's shared Doom lifecycle. Reload or shutdown stops retained
|
|
105
127
|
callbacks, interrupts bounded inline work, removes UI and service registrations, and releases
|
|
@@ -125,10 +147,12 @@ Maintained by [Agimon](https://agimon.ai/about).
|
|
|
125
147
|
## Web cockpit plugin
|
|
126
148
|
|
|
127
149
|
The `.web` routed files under `src/extensions/workspaces/sessions/(frontend)` are this package's
|
|
128
|
-
DoomPi web cockpit plugin: the
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
stores and
|
|
150
|
+
DoomPi web cockpit plugin: the `workflow` dock face beside Activity and Context (the owned run's
|
|
151
|
+
jobs, active steps and artifacts, each opening its own tab), the workflow catalog and launch
|
|
152
|
+
dialog in the overlay slot, the Activity group's run rows (a handed-off run opens its workflow
|
|
153
|
+
session), their stores, and the `workflow_runs` session channel, compiled into the cockpit bundle
|
|
154
|
+
by `doompi-web`'s build. Each surface keeps its components in a colocated `_components` folder and
|
|
155
|
+
its stores and helpers in `_lib`.
|
|
132
156
|
The hub-side data source ships behind the `./web-hub` subpath and reads the workflow registry
|
|
133
157
|
(run.json plus progress.ndjson) exactly as the engine writes it. Both halves are declared by the
|
|
134
158
|
`doompiWeb` block in package.json.
|
package/dist/extensions/pi.cjs
CHANGED
|
@@ -2,7 +2,7 @@ Object.defineProperties(exports, {
|
|
|
2
2
|
__esModule: { value: true },
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
|
-
const require_workflowTerminal = require("../workflowTerminal-
|
|
5
|
+
const require_workflowTerminal = require("../workflowTerminal-DeJ9BBta.cjs");
|
|
6
6
|
const require_workflowLaunchCommand = require("../workflowLaunchCommand-DJyDB96d.cjs");
|
|
7
7
|
let _agimon_ai_doompi_core_piExtension = require("@agimon-ai/doompi-core/piExtension");
|
|
8
8
|
let _agimon_ai_doompi_core_extensionFile = require("@agimon-ai/doompi-core/extensionFile");
|
|
@@ -23,76 +23,6 @@ let _agimon_ai_doompi_core_readiness = require("@agimon-ai/doompi-core/readiness
|
|
|
23
23
|
let _agimon_ai_doompi_minor_mode = require("@agimon-ai/doompi-minor-mode");
|
|
24
24
|
let _agimon_ai_doompi_telemetry = require("@agimon-ai/doompi-telemetry");
|
|
25
25
|
let _earendil_works_pi_tui = require("@earendil-works/pi-tui");
|
|
26
|
-
//#region src/schemas/workflowPi.ts
|
|
27
|
-
const RUN_SELECTOR_FIELDS = {
|
|
28
|
-
runKey: zod.z.string().min(1).describe("Run key of the workflow run."),
|
|
29
|
-
workspace: zod.z.string().optional().describe("Workspace the run belongs to.")
|
|
30
|
-
};
|
|
31
|
-
const workflowRunInputSchema = zod.z.discriminatedUnion("action", [
|
|
32
|
-
zod.z.object({
|
|
33
|
-
action: zod.z.literal("status"),
|
|
34
|
-
...RUN_SELECTOR_FIELDS
|
|
35
|
-
}).strict(),
|
|
36
|
-
zod.z.object({
|
|
37
|
-
action: zod.z.literal("tail"),
|
|
38
|
-
...RUN_SELECTOR_FIELDS
|
|
39
|
-
}).strict(),
|
|
40
|
-
zod.z.object({
|
|
41
|
-
action: zod.z.literal("recovery-evidence"),
|
|
42
|
-
...RUN_SELECTOR_FIELDS
|
|
43
|
-
}).strict(),
|
|
44
|
-
zod.z.object({
|
|
45
|
-
action: zod.z.literal("follow"),
|
|
46
|
-
...RUN_SELECTOR_FIELDS
|
|
47
|
-
}).strict(),
|
|
48
|
-
zod.z.object({
|
|
49
|
-
action: zod.z.literal("open"),
|
|
50
|
-
...RUN_SELECTOR_FIELDS
|
|
51
|
-
}).strict(),
|
|
52
|
-
zod.z.object({
|
|
53
|
-
action: zod.z.literal("pause"),
|
|
54
|
-
...RUN_SELECTOR_FIELDS,
|
|
55
|
-
reason: zod.z.string().optional()
|
|
56
|
-
}).strict(),
|
|
57
|
-
zod.z.object({
|
|
58
|
-
action: zod.z.literal("resume"),
|
|
59
|
-
...RUN_SELECTOR_FIELDS,
|
|
60
|
-
reason: zod.z.string().optional()
|
|
61
|
-
}).strict(),
|
|
62
|
-
zod.z.object({
|
|
63
|
-
action: zod.z.literal("stop"),
|
|
64
|
-
...RUN_SELECTOR_FIELDS,
|
|
65
|
-
reason: zod.z.string().optional()
|
|
66
|
-
}).strict(),
|
|
67
|
-
zod.z.object({
|
|
68
|
-
action: zod.z.literal("recover"),
|
|
69
|
-
...RUN_SELECTOR_FIELDS,
|
|
70
|
-
dryRun: zod.z.boolean().optional(),
|
|
71
|
-
job: zod.z.string().optional(),
|
|
72
|
-
runner: zod.z.string().optional()
|
|
73
|
-
}).strict()
|
|
74
|
-
]);
|
|
75
|
-
const dispatchInputSchema = zod.z.object({
|
|
76
|
-
description: zod.z.string().optional().describe("Description shown for the workflow input."),
|
|
77
|
-
required: zod.z.boolean().optional().describe("Whether the workflow input must be provided."),
|
|
78
|
-
default: zod.z.string().optional().describe("Default value for the workflow input."),
|
|
79
|
-
type: zod.z.string().optional().describe("Declared workflow input type."),
|
|
80
|
-
options: zod.z.array(zod.z.string()).optional().describe("Allowed values for the workflow input.")
|
|
81
|
-
}).passthrough();
|
|
82
|
-
const workflowDispatchInputsSchema = zod.z.object({ inputs: zod.z.record(zod.z.string(), dispatchInputSchema).optional().describe("Workflow dispatch input definitions.") }).passthrough();
|
|
83
|
-
const workflowCatalogPageSchema = zod.z.object({
|
|
84
|
-
directory: zod.z.string().min(1).describe("Directory searched for workflow definitions."),
|
|
85
|
-
hasNextPage: zod.z.boolean().describe("Whether another catalog page exists."),
|
|
86
|
-
page: zod.z.number().int().positive().describe("One-based catalog page number."),
|
|
87
|
-
pageSize: zod.z.number().int().positive().describe("Catalog page size."),
|
|
88
|
-
workflows: zod.z.array(zod.z.object({
|
|
89
|
-
path: zod.z.string().min(1).describe("Workflow path relative to the catalog directory."),
|
|
90
|
-
name: zod.z.string().describe("Workflow display name."),
|
|
91
|
-
description: zod.z.string().describe("Workflow description."),
|
|
92
|
-
tags: zod.z.array(zod.z.string()).describe("Workflow tags.")
|
|
93
|
-
}).passthrough()).describe("Workflow definitions on this page.")
|
|
94
|
-
}).passthrough();
|
|
95
|
-
//#endregion
|
|
96
26
|
//#region src/services/workflowTools/index.ts
|
|
97
27
|
const PI_LAUNCH_FIELDS = {
|
|
98
28
|
workflowPath: true,
|
|
@@ -175,11 +105,11 @@ function createWorkflowTools(dependencies, renderers = () => ({})) {
|
|
|
175
105
|
"Use action recover only after loading the `workflow-recovery` skill and gathering its required evidence. Recovery may adopt a terminal failed run from an earlier Pi session; it never grants cross-session control of running work. Never edit issue.md or repair.json, and verify real process and registry progress before recovery.",
|
|
176
106
|
"Never unlock or force-release an Agiflow claim from this tool."
|
|
177
107
|
],
|
|
178
|
-
parameters: zod.z.toJSONSchema(workflowRunInputSchema),
|
|
108
|
+
parameters: zod.z.toJSONSchema(require_workflowTerminal.workflowRunInputSchema),
|
|
179
109
|
renderShell: "self",
|
|
180
110
|
...renderers(require_workflowTerminal.WORKFLOW_RUN_TOOL_NAME),
|
|
181
111
|
async execute(_toolCallId, rawParams, _signal, onUpdate, ctx) {
|
|
182
|
-
const input = workflowRunInputSchema.parse(rawParams);
|
|
112
|
+
const input = require_workflowTerminal.workflowRunInputSchema.parse(rawParams);
|
|
183
113
|
const sessionId = (0, _agimon_ai_doompi_core_childProcess.resolveRootSessionId)(ctx.sessionManager.getSessionId(), dependencies.environment);
|
|
184
114
|
dependencies.observeSession?.(sessionId);
|
|
185
115
|
const selector = {
|
|
@@ -887,7 +817,7 @@ function workflowTriggerBlock(workflow) {
|
|
|
887
817
|
function dispatchInputs(workflow) {
|
|
888
818
|
const dispatch = workflowTriggerBlock(workflow)[WORKFLOW_DISPATCH_TRIGGER];
|
|
889
819
|
if (dispatch === null || dispatch === void 0) return {};
|
|
890
|
-
const parsed = workflowDispatchInputsSchema.safeParse(dispatch);
|
|
820
|
+
const parsed = require_workflowTerminal.workflowDispatchInputsSchema.safeParse(dispatch);
|
|
891
821
|
if (!parsed.success) throw new Error(`Invalid workflow_dispatch input definitions: ${parsed.error.message}`);
|
|
892
822
|
return parsed.data.inputs ?? {};
|
|
893
823
|
}
|
|
@@ -902,7 +832,7 @@ function parseWorkflowCatalogPage(result) {
|
|
|
902
832
|
} catch (cause) {
|
|
903
833
|
throw new Error("Workflow catalog returned invalid JSON.", { cause });
|
|
904
834
|
}
|
|
905
|
-
const parsed = workflowCatalogPageSchema.safeParse(value);
|
|
835
|
+
const parsed = require_workflowTerminal.workflowCatalogPageSchema.safeParse(value);
|
|
906
836
|
if (!parsed.success) throw new Error(`Workflow catalog response was invalid: ${parsed.error.message}`);
|
|
907
837
|
return {
|
|
908
838
|
directory: parsed.data.directory,
|
|
@@ -3532,12 +3462,13 @@ function createWorkflowPiRuntime(pi, options) {
|
|
|
3532
3462
|
if (!refreshIsCurrent()) return;
|
|
3533
3463
|
const sessionId = (0, _agimon_ai_doompi_core_childProcess.resolveRootSessionId)(ctx.sessionManager.getSessionId(), options.environment);
|
|
3534
3464
|
const rootProcess = isRootProcess(ctx, sessionId);
|
|
3535
|
-
const
|
|
3536
|
-
const
|
|
3537
|
-
const
|
|
3465
|
+
const ownRecords = records.filter((record) => require_workflowTerminal.isSessionRun(record, sessionId));
|
|
3466
|
+
const sessionRecords = records.filter((record) => require_workflowTerminal.isSessionRun(record, sessionId) || require_workflowTerminal.isLaunchedRun(record, sessionId));
|
|
3467
|
+
const running = ownRecords.filter((record) => record.stage === RUNNING_STATUS && !record.stale);
|
|
3468
|
+
const failed = ownRecords.filter((record) => record.stage === ERROR_STAGE);
|
|
3538
3469
|
if (activeRevisionAtRead === activeRunRevision) replaceActiveRecords(running);
|
|
3539
3470
|
activeTelemetry.recordEvent("doom_workflow.monitor_counts", {
|
|
3540
|
-
"workflow.run_count":
|
|
3471
|
+
"workflow.run_count": ownRecords.length,
|
|
3541
3472
|
"workflow.running_count": running.length,
|
|
3542
3473
|
"workflow.failed_count": failed.length,
|
|
3543
3474
|
mode: workflowMode ? "on" : "off"
|
|
@@ -3552,11 +3483,12 @@ function createWorkflowPiRuntime(pi, options) {
|
|
|
3552
3483
|
const previous = previousStages.get(identity);
|
|
3553
3484
|
const startedAt = Date.parse(record.startedAt);
|
|
3554
3485
|
const startedDuringSession = previous === void 0 && (sessionStartedAt === 0 || !Number.isFinite(startedAt) || startedAt >= sessionStartedAt);
|
|
3555
|
-
if (lifecycleNarrationReady) narrateWorkflowTransition(narrationSink, record, previous === RUNNING_STATUS || previous === COMPLETED_STATUS || previous === ERROR_STAGE ? previous : void 0, startedDuringSession);
|
|
3486
|
+
if (lifecycleNarrationReady && require_workflowTerminal.isSessionRun(record, sessionId)) narrateWorkflowTransition(narrationSink, record, previous === RUNNING_STATUS || previous === COMPLETED_STATUS || previous === ERROR_STAGE ? previous : void 0, startedDuringSession);
|
|
3556
3487
|
if (!notifyChanges || !rootProcess || record.stage === RUNNING_STATUS) continue;
|
|
3557
3488
|
if (!deliveredTerminalRuns.has(identity) && (previous === RUNNING_STATUS || startedDuringSession)) pendingTerminalRuns.set(identity, record);
|
|
3558
3489
|
}
|
|
3559
3490
|
const retainedWork = new Map(mine.map((record) => [runIdentity(record), record]));
|
|
3491
|
+
for (const record of sessionRecords) if (require_workflowTerminal.isLaunchedRun(record, sessionId) && record.stage === RUNNING_STATUS && !record.stale) retainedWork.set(runIdentity(record), record);
|
|
3560
3492
|
for (const [identity, record] of pendingTerminalRuns) retainedWork.set(identity, record);
|
|
3561
3493
|
if (!refreshIsCurrent()) return;
|
|
3562
3494
|
runProvider?.update([...retainedWork.values()].map((record) => ({
|
|
@@ -3575,28 +3507,39 @@ function createWorkflowPiRuntime(pi, options) {
|
|
|
3575
3507
|
if (rootProcess && pendingTerminalRuns.size > 0) try {
|
|
3576
3508
|
const terminalRecords = [...pendingTerminalRuns.values()];
|
|
3577
3509
|
const summaries = await Promise.all(terminalRecords.map(async (record) => {
|
|
3578
|
-
if (record.stage === COMPLETED_STATUS)
|
|
3510
|
+
if (record.stage === COMPLETED_STATUS) {
|
|
3511
|
+
const summary = require_workflowTerminal.finishedRunSummary(record, []);
|
|
3512
|
+
return require_workflowTerminal.isLaunchedRun(record, sessionId) ? `${summary}\n${require_workflowTerminal.launchedRunHint(record)}` : summary;
|
|
3513
|
+
}
|
|
3579
3514
|
const identity = runIdentity(record);
|
|
3580
3515
|
const events = runEvents.get(identity) ?? progressEventsForRun(record, await (0, _agimon_ai_workflow_mcp.readWorkflowProgress)(registry.runDirectoryFor(record)));
|
|
3581
|
-
|
|
3516
|
+
const summary = require_workflowTerminal.finishedRunSummary(record, (0, _agimon_ai_workflow_mcp.summarizeWorkflowProgress)(events));
|
|
3517
|
+
return require_workflowTerminal.isLaunchedRun(record, sessionId) ? `${summary}\n${require_workflowTerminal.launchedRunHint(record)}` : summary;
|
|
3582
3518
|
}));
|
|
3583
3519
|
if (!refreshIsCurrent()) return;
|
|
3584
|
-
|
|
3520
|
+
const message = (indexes) => ({
|
|
3585
3521
|
customType: WORKFLOW_FINISHED_MESSAGE$1,
|
|
3586
|
-
content: summaries.join("\n\n"),
|
|
3522
|
+
content: indexes.map((index) => summaries[index]).join("\n\n"),
|
|
3587
3523
|
display: true,
|
|
3588
3524
|
details: {
|
|
3589
|
-
runIds:
|
|
3590
|
-
runs:
|
|
3591
|
-
|
|
3592
|
-
|
|
3593
|
-
|
|
3594
|
-
|
|
3595
|
-
|
|
3596
|
-
|
|
3597
|
-
|
|
3525
|
+
runIds: indexes.map((index) => terminalRecords[index].runId ?? runIdentity(terminalRecords[index])),
|
|
3526
|
+
runs: indexes.map((index) => {
|
|
3527
|
+
const record = terminalRecords[index];
|
|
3528
|
+
return {
|
|
3529
|
+
runKey: record.runKey,
|
|
3530
|
+
workspace: record.workspace,
|
|
3531
|
+
stage: record.stage,
|
|
3532
|
+
...record.workflowId ? { workflowId: record.workflowId } : {},
|
|
3533
|
+
...record.failedJob ? { failedJob: record.failedJob } : {},
|
|
3534
|
+
...record.errorMessage ? { error: record.errorMessage } : {}
|
|
3535
|
+
};
|
|
3536
|
+
})
|
|
3598
3537
|
}
|
|
3599
|
-
}
|
|
3538
|
+
});
|
|
3539
|
+
const quiet = terminalRecords.flatMap((record, index) => require_workflowTerminal.isOwnedDelegatedRun(record, sessionId) ? [index] : []);
|
|
3540
|
+
const loud = terminalRecords.flatMap((record, index) => require_workflowTerminal.isOwnedDelegatedRun(record, sessionId) ? [] : [index]);
|
|
3541
|
+
if (quiet.length > 0) pi.sendMessage(message(quiet), { triggerTurn: false });
|
|
3542
|
+
if (loud.length > 0) pi.sendMessage(message(loud), {
|
|
3600
3543
|
triggerTurn: true,
|
|
3601
3544
|
deliverAs: "steer"
|
|
3602
3545
|
});
|