@agimon-ai/po-workflow 0.0.0-alpha.0

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.
@@ -0,0 +1,64 @@
1
+ ---
2
+ name: po-author-workflow
3
+ description: Author Po 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 Po 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
+ steps:
16
+ - name: Run tests
17
+ run: pnpm test
18
+
19
+ summarize:
20
+ needs: test
21
+ steps:
22
+ - name: Record result
23
+ run: node scripts/write-verification-summary.mjs
24
+ ```
25
+
26
+ `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.
27
+
28
+ For an agent step, declare the command once under `commands` (usually in an imported file) and let the step name it. A command is a Liquid template; `inProcess: true` lets the Po server run it as a child session instead of the shell, and the CLI and the TUI run the rendered command:
29
+
30
+ ```yaml
31
+ imports: [commands.yml]
32
+ choices:
33
+ default: { model: openai-codex/gpt-6-sol, thinking: medium }
34
+ jobs:
35
+ develop:
36
+ steps:
37
+ - name: Develop
38
+ runConfig: { majorMode: dev, domains: [engineering] }
39
+ systemPrompt: ${{ env.JOB_SYSTEM_PROMPT }}
40
+ prompt: ${{ env.WORKFLOW_CONTEXT }}
41
+ interactiveRun: [{ command: po }, { command: terminal }]
42
+ ```
43
+
44
+ `po` and `terminal` are the commands `commands.yml` declares; a step can only name a declared command. `runConfig` takes `majorMode`, `minorModes`, `profile`, `domains`, `model`, and `thinking`, plus any key the template reads; any other key fails the step, so a typo such as `majormode` never runs on workspace defaults. `choices` compose model and thinking, with per-command overrides such as `terminal: { model: openai-codex/gpt-6-luna }`; a step can pin `choice:`, and a launch picks `command=` and `choice=` for the rest. Templates must quote prompts with `{{ prompt | shell }}`.
45
+
46
+ Before committing a workflow, run `workflow-mcp doctor <file-or-directory> --run-config-keys majorMode,minorModes,profile,domains,model,thinking` and fix every error it reports; the Po server refuses to launch a workflow with errors.
47
+
48
+ An agent step ends by recording its outcome: `workflow-mcp step complete`, `step fix --restart-from <job> --reason ...`, or `step fail --reason ...`. The engine puts the instructions in the step's system prompt, and an agent that stops without deciding is sent back until it does. Write repair contracts in those terms, not as `fix.md`.
49
+
50
+ What a step or job must leave is held the same way. A step's `artifacts` must be written during the step. A job must be the last to have written each run-directory entry it produces (`produced-by`): an entry an earlier job wrote must be written again, while a retry or a job that skips work it already did keeps its own output, and removing a file counts. `writes.ndjson` in the run directory records which job and step last wrote each entry. An agent step that ends its job is sent back until those entries are written, and the job fails when one was not. List a job under `produced-by` only when it writes the entry in every run. Write a `description` for every entry: the reminder tells the agent what the file holds from it.
51
+
52
+ Put independent checks in a `parallel` group. A `run:` check with `restart-from: <job>` turns a failure into a fix request, and every fix request of the job merges into one repair, so one loop back fixes all of them. Keep steps that edit files out of a group: its steps share the checkout.
53
+
54
+ Every `run` command executes on the workflow host with that process's environment and privileges. There is no VM, container, or sandbox. Review workflow changes as executable code, avoid embedding secrets, and make retry-sensitive external side effects explicit.
55
+
56
+ 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.
57
+
58
+ ## Native execution contract
59
+
60
+ Agent steps require an effective `runConfig.majorMode`; delegated workflows require top-level `runConfig.majorMode` before their owner child is created. Selection projects major mode, minor modes, profile and domains, adding the workflow minor mode once. `subagentModel` and `subagentThinking` set Team preferences independently of the step model.
61
+
62
+ The native host requires execution-state inspection before creating agent steps. After settlement it waits for idle, no pending messages and a known background snapshot without core holds or holding-provider errors before checking decisions. Unknown ownership waits closed without consuming reminders; stops interrupt reads and retry delays.
63
+
64
+ Delegated successes are informational. Errors wake the workflow owner through durable custom-message admission; the launcher gets an error notification, not an agent-waking failure completion. Diagnosis stays local. Recovery, pause, resume and stop are request-driven. User-requested stops do not trigger diagnosis or automatic restart.
@@ -0,0 +1,21 @@
1
+ ---
2
+ name: po-use-workflow
3
+ description: Use Po 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 Po 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. For a workflow whose agent steps name commands, pass `command` (such as `po` or `claude`) and `choice` (a declared model choice); both default to what the workflow lists first and to `default`.
14
+ 3. `workflow_run` inspects status, follows progress, or applies a supported control action. In server/web sessions it supports status, pause, resume, and stop; `workflow_tools` carries `recovery-evidence` and `recover`.
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 l` lists the repository's workflows and launches the one under the cursor with `r`, `SPC w r` inspects this session's runs, and `SPC w c` 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.
@@ -0,0 +1,114 @@
1
+ /**
2
+ * The wire contract between this package's hub API and its cockpit client:
3
+ * one run's terminal, and the files its run directory holds.
4
+ *
5
+ * Both halves of the cockpit import these names, so the route strings and the
6
+ * event names live here rather than being spelled twice.
7
+ */
8
+
9
+ /**
10
+ * Segment this package's API is mounted under, below /api/plugins/.
11
+ *
12
+ * The same word names `src/extensions/(backend)/api/workflow/`, which is where
13
+ * the build reads the mount for the browser's client, so the two cannot drift.
14
+ * The routes themselves are declared in `apiRoutes.ts`.
15
+ */
16
+ export const WORKFLOW_API_BASE_PATH = 'workflow';
17
+
18
+ /** The SSE event name the screen stream writes. */
19
+ export const WORKFLOW_SCREEN_EVENT = 'screen';
20
+
21
+ /** What a surface may do with one run's terminal, and why anything is missing. */
22
+ export interface WorkflowTerminalCapabilitiesView {
23
+ readable: boolean;
24
+ writable: boolean;
25
+ resizable: boolean;
26
+ reason?: string;
27
+ }
28
+
29
+ /** One frame of a run's terminal, as the stream pushes it. */
30
+ export interface WorkflowScreenEvent {
31
+ /** The visible screen, newest line last, carrying the colour the run printed. */
32
+ lines: string[];
33
+ capabilities: WorkflowTerminalCapabilitiesView;
34
+ /** True once the run has settled, after which the stream ends. */
35
+ ended?: boolean;
36
+ }
37
+
38
+ /** Answer to taking or releasing the keyboard for one run. */
39
+ export interface WorkflowControlResponse {
40
+ /** True when this caller now holds the keyboard. */
41
+ held: boolean;
42
+ /** The token later writes must carry; absent when control was refused or released. */
43
+ token?: string;
44
+ /** Why control was refused, worded for a reader. */
45
+ reason?: string;
46
+ }
47
+
48
+ /** Answer after a settled run and its directory have been permanently removed. */
49
+ export interface WorkflowDeleteResponse {
50
+ deleted: true;
51
+ }
52
+
53
+ /** Answer once a stop request is recorded; the engine ends the run at its next check. */
54
+ export interface WorkflowStopResponse {
55
+ requested: true;
56
+ }
57
+
58
+ /** Answer once guidance reached the running step's agent session. */
59
+ export interface WorkflowSteerResponse {
60
+ delivered: true;
61
+ }
62
+
63
+ /** What launching a workflow into a session answered, as the launch tool reports it. */
64
+ export interface WorkflowLaunchResponse {
65
+ text: string;
66
+ isError?: boolean;
67
+ }
68
+
69
+ /** How a declared run-directory entry compares with what is on disk. */
70
+ export type WorkflowArtifactState = 'written' | 'empty' | 'pending' | 'unreadable';
71
+
72
+ /** One file or directory in a run's own folder. */
73
+ export interface WorkflowArtifactView {
74
+ /** Path relative to the run directory, which is also the read route's parameter. */
75
+ path: string;
76
+ kind: 'file' | 'directory';
77
+ /** What the workflow says this file is for; empty for a file it never declared. */
78
+ description: string;
79
+ /** The jobs the workflow says write it. */
80
+ producedBy: string[];
81
+ /** True when the workflow's run-directory block names this entry. */
82
+ declared: boolean;
83
+ state: WorkflowArtifactState;
84
+ /** Bytes on disk, absent until the file exists. */
85
+ size?: number;
86
+ /** ISO 8601 of the last write, absent until the file exists. */
87
+ modifiedAt?: string;
88
+ }
89
+
90
+ export interface WorkflowArtifactsResponse {
91
+ /** Absolute path of the run's own directory, which the reader may want to open. */
92
+ runDir: string;
93
+ /** What the workflow says the directory is for. */
94
+ description: string;
95
+ artifacts: WorkflowArtifactView[];
96
+ }
97
+
98
+ /** One artifact's metadata and optional text, as the viewer tab reads it. */
99
+ export interface WorkflowArtifactContentResponse {
100
+ path: string;
101
+ size: number;
102
+ modifiedAt: string;
103
+ /** Media type inferred from the file extension by the trusted hub. Absent on older hubs. */
104
+ mimeType?: string;
105
+ /** Present for text formats; binary files are streamed from the raw route instead. */
106
+ text?: string;
107
+ /** True when textual content was longer than the reader's byte budget. */
108
+ truncated: boolean;
109
+ }
110
+
111
+ /** Acknowledges opening the registered run folder in Finder. */
112
+ export interface WorkflowOpenRunDirectoryResponse {
113
+ opened: true;
114
+ }
@@ -0,0 +1,208 @@
1
+ /**
2
+ * Workflow run view types shared by this package's hub channel and its web
3
+ * plugin. The shapes mirror what the embedded workflow-mcp engine records;
4
+ * the cockpit renders them as 'workflow_runs' channel payloads.
5
+ */
6
+
7
+ /** workflow-mcp's own stage vocabulary; the registry bucket a run record sits in. */
8
+ export type WorkflowStage = 'running' | 'completed' | 'error';
9
+
10
+ /** workflow-mcp's terminal outcome for a finished run. */
11
+ export type WorkflowOutcome = 'success' | 'skipped' | 'failed' | 'interrupted';
12
+
13
+ /** Recorded job/step states, plus pending entries from the workflow definition. */
14
+ export type WorkflowProgressState =
15
+ | 'pending'
16
+ | 'running'
17
+ | 'completed'
18
+ | 'skipped'
19
+ | 'failed'
20
+ | 'pause_requested'
21
+ | 'paused'
22
+ | 'resumed';
23
+
24
+ /** A customRun step ran as this child session. */
25
+ export const STEP_SESSION_REF_KIND = 'session';
26
+ /** A command step ran in this terminal pane. */
27
+ export const STEP_PANE_REF_KIND = 'pane';
28
+
29
+ /** Where a host executor ran a step: `session` for a customRun agent, `pane` for a command. */
30
+ export interface WorkflowStepRefView {
31
+ kind: string;
32
+ id: string;
33
+ label?: string;
34
+ }
35
+
36
+ export interface WorkflowStepView {
37
+ name: string;
38
+ status: WorkflowProgressState;
39
+ /** Kept after the step ends, so a finished agent step still opens its conversation. */
40
+ ref?: WorkflowStepRefView;
41
+ /** Why the step is in its state, when the engine recorded one (e.g. a skip condition). */
42
+ reason?: string;
43
+ /** The parallel group the step ran in, beside the group's other steps. */
44
+ group?: string;
45
+ /** ISO 8601 of the step's first running event. */
46
+ startedAt?: string;
47
+ /** ISO 8601 of the step's terminal event. */
48
+ endedAt?: string;
49
+ }
50
+
51
+ /** 'pre' and 'post' are the engine's pseudo-jobs for its pre:/post: blocks. */
52
+ export type WorkflowJobPhase = 'pre' | 'job' | 'post';
53
+
54
+ export interface WorkflowJobView {
55
+ name: string;
56
+ phase: WorkflowJobPhase;
57
+ status: WorkflowProgressState;
58
+ reason?: string;
59
+ /** Position among the run's jobs, when the engine recorded one. */
60
+ index?: number;
61
+ total?: number;
62
+ startedAt?: string;
63
+ endedAt?: string;
64
+ steps: WorkflowStepView[];
65
+ }
66
+
67
+ /** The job and step a run is on right now; drives the NOW breadcrumb. */
68
+ export interface WorkflowPosition {
69
+ job: string;
70
+ step?: string;
71
+ index?: number;
72
+ total?: number;
73
+ }
74
+
75
+ /**
76
+ * One workflow run of a session, read from workflow-mcp's registry: the
77
+ * run.json record plus the folded progress.ndjson job tree.
78
+ */
79
+ export interface WorkflowRunView {
80
+ /** Engine generation identity, distinct from a reused run key. */
81
+ runId?: string;
82
+ /** Registry identity within its workspace; unique per workspace+stage. */
83
+ runKey: string;
84
+ workspace: string;
85
+ displayName: string;
86
+ workflowName?: string;
87
+ workflowPath: string;
88
+ stage: WorkflowStage;
89
+ outcome?: WorkflowOutcome;
90
+ /** Present while the engine is pausing or paused; absent for plain running. */
91
+ executionState?: 'running' | 'pause_requested' | 'paused' | 'resume_requested';
92
+ /** The prompt the run was launched with, when one was recorded. */
93
+ prompt?: string;
94
+ /** ISO 8601, from the run record. */
95
+ startedAt: string;
96
+ finishedAt?: string;
97
+ /** Failure cause with ANSI escapes stripped, present when the run errored. */
98
+ errorMessage?: string;
99
+ /** The job the engine blamed for the failure, when it recorded one. */
100
+ failedJob?: string;
101
+ /** True when reconciliation decided the recorded pid is no longer alive. */
102
+ stale?: boolean;
103
+ staleReason?: string;
104
+ worktreeBranch?: string;
105
+ /** The session that owns the run (its PI_SESSION_ID); absent on records that carry none. */
106
+ ownerSessionId?: string;
107
+ /** The session that launched the run into its own workflow session, when it was handed off. */
108
+ launcherSessionId?: string;
109
+ position?: WorkflowPosition;
110
+ jobs: WorkflowJobView[];
111
+ }
112
+
113
+ /** What the workflow runs channel publishes for one session. */
114
+ export interface WorkflowRunsPayload {
115
+ runs: WorkflowRunView[];
116
+ }
117
+
118
+ export const WORKFLOW_RUNS_TYPE = 'workflow_runs';
119
+
120
+ /**
121
+ * One workflow the session's directory can launch, as the catalog drawer
122
+ * lists it: the engine's own catalog entry plus the parsed detail the drawer
123
+ * unfolds and the launch dialog turns into fields.
124
+ */
125
+ export interface WorkflowCatalogEntryView {
126
+ /** Absolute path, the row's stable identity. */
127
+ path: string;
128
+ relativePath: string;
129
+ name: string;
130
+ description: string;
131
+ tags: string[];
132
+ /** The trigger names the file declares, such as workflow_dispatch. */
133
+ triggers: string[];
134
+ inputs: WorkflowCatalogInputView[];
135
+ jobs: WorkflowCatalogJobView[];
136
+ artifacts: WorkflowCatalogArtifactView[];
137
+ /** Absent when the workflow names no runner map, which means any runner will do. */
138
+ runners?: string[];
139
+ /** Commands every templated step offers; absent when no step is templated. */
140
+ commands?: string[];
141
+ /** Declared model choices; `default` is used when a launch names none. */
142
+ choices?: string[];
143
+ /** Set when the file could not be parsed; the row shows this instead of guessing. */
144
+ error?: string;
145
+ }
146
+
147
+ export interface WorkflowCatalogInputView {
148
+ name: string;
149
+ description?: string;
150
+ required?: boolean;
151
+ default?: string;
152
+ type?: string;
153
+ options?: string[];
154
+ }
155
+
156
+ export interface WorkflowCatalogJobView {
157
+ name: string;
158
+ steps: string[];
159
+ }
160
+
161
+ export interface WorkflowCatalogArtifactView {
162
+ path: string;
163
+ kind: 'file' | 'directory';
164
+ description: string;
165
+ producedBy: string[];
166
+ }
167
+
168
+ /** What the catalog channel publishes for one session. */
169
+ export interface WorkflowCatalogPayload {
170
+ /** The directory the catalog was read from, which is the session's own. */
171
+ cwd: string;
172
+ workflows: WorkflowCatalogEntryView[];
173
+ /** Present when the directory could not be read at all. */
174
+ warning?: string;
175
+ }
176
+
177
+ export const WORKFLOW_CATALOG_TYPE = 'workflow_catalog';
178
+
179
+ export interface WorkflowInputSummary {
180
+ name: string;
181
+ description?: string;
182
+ required?: boolean;
183
+ default?: string;
184
+ options?: readonly string[];
185
+ }
186
+
187
+ export interface WorkflowJobSummary {
188
+ name: string;
189
+ steps: readonly string[];
190
+ }
191
+
192
+ /**
193
+ * What the detail pane shows for one workflow. `runners` is undefined when the
194
+ * workflow names no runner map at all, which is not the same as naming an empty
195
+ * one - see `compatibleRunners`.
196
+ */
197
+ export interface WorkflowCatalogDetail {
198
+ triggers: readonly string[];
199
+ inputs: readonly WorkflowInputSummary[];
200
+ jobs: readonly WorkflowJobSummary[];
201
+ runners?: readonly string[];
202
+ /** Commands every templated step offers; absent when no step is templated. */
203
+ commands?: readonly string[];
204
+ /** Declared model choices. */
205
+ choices?: readonly string[];
206
+ /** Set when the file could not be parsed; the pane shows this instead of guessing. */
207
+ error?: string;
208
+ }