stageflow 0.8.0 → 0.12.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.
- package/README.md +24 -14
- package/dist/agent/activity.d.ts +9 -0
- package/dist/agent/agentBackend.d.ts +32 -0
- package/dist/agent/agentBackend.js +38 -0
- package/dist/agent/claudeActivity.d.ts +15 -0
- package/dist/agent/claudeActivity.js +58 -0
- package/dist/agent/claudeAdapter.d.ts +5 -0
- package/dist/agent/claudeAdapter.js +301 -0
- package/dist/agent/claudeSession.d.ts +18 -0
- package/dist/agent/claudeSession.js +54 -0
- package/dist/agent/claudeTools.d.ts +89 -0
- package/dist/agent/claudeTools.js +135 -0
- package/dist/agent/fakeAgent.js +24 -4
- package/dist/agent/piAdapter.d.ts +6 -3
- package/dist/agent/piAdapter.js +129 -21
- package/dist/agent/port.d.ts +50 -1
- package/dist/agent/resolveAgentPort.d.ts +26 -0
- package/dist/agent/resolveAgentPort.js +39 -0
- package/dist/cli/runCommand.js +2 -2
- package/dist/cli/runsCommand.d.ts +22 -0
- package/dist/cli/runsCommand.js +773 -0
- package/dist/cli/validateOutput.d.ts +2 -2
- package/dist/cli/validateOutput.js +2 -2
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +43 -5
- package/dist/config/browseCatalog.js +1 -1
- package/dist/config/createPipeline.d.ts +2 -1
- package/dist/config/createPipeline.js +34 -9
- package/dist/config/createStage.js +14 -9
- package/dist/config/loadPipeline.js +10 -1
- package/dist/config/loadStage.js +138 -2
- package/dist/config/loadStageflowManifest.js +14 -1
- package/dist/config/mergePipelineIncludes.d.ts +1 -0
- package/dist/config/mergePipelineIncludes.js +14 -1
- package/dist/config/normalizePipelineStageEntry.d.ts +7 -2
- package/dist/config/normalizePipelineStageEntry.js +49 -6
- package/dist/config/parseCompletionContract.d.ts +8 -0
- package/dist/config/parseCompletionContract.js +218 -0
- package/dist/config/parsePreEmitChecks.d.ts +3 -0
- package/dist/config/parsePreEmitChecks.js +65 -0
- package/dist/config/pipelineNeeds.d.ts +19 -0
- package/dist/config/pipelineNeeds.js +151 -0
- package/dist/config/pipelineStageKeys.js +16 -1
- package/dist/config/resolveForkEmitContext.d.ts +6 -2
- package/dist/config/resolveForkEmitContext.js +9 -2
- package/dist/config/resolvePipelineDag.d.ts +2 -1
- package/dist/config/resolvePipelineDag.js +182 -39
- package/dist/config/validateCatalog.d.ts +1 -1
- package/dist/config/validateCompletionContract.d.ts +4 -0
- package/dist/config/validateCompletionContract.js +52 -0
- package/dist/envelope/check.d.ts +1 -1
- package/dist/envelope/check.js +86 -13
- package/dist/envelope/cloneForks.d.ts +1 -1
- package/dist/envelope/cloneForks.js +24 -9
- package/dist/envelope/feedbackLoop.d.ts +4 -0
- package/dist/envelope/feedbackLoop.js +31 -0
- package/dist/envelope/payloadSchema.d.ts +7 -1
- package/dist/envelope/payloadSchema.js +170 -13
- package/dist/envelope/preEmitChecks.d.ts +29 -0
- package/dist/envelope/preEmitChecks.js +57 -0
- package/dist/hitl/qaTrail.d.ts +25 -0
- package/dist/hitl/qaTrail.js +51 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/mcp/catalogTools.js +4 -2
- package/dist/mcp/controlTools.js +72 -0
- package/dist/mcp/waitingGates.d.ts +2 -1
- package/dist/mcp/waitingGates.js +17 -1
- package/dist/package-meta.d.ts +1 -0
- package/dist/package-meta.js +1 -0
- package/dist/projection/projectRun.d.ts +4 -1
- package/dist/projection/projectRun.js +7 -0
- package/dist/prompt/feedbackLoopContext.d.ts +2 -0
- package/dist/prompt/feedbackLoopContext.js +17 -0
- package/dist/prompt/priorEnvelope.d.ts +2 -2
- package/dist/prompt/priorEnvelope.js +4 -1
- package/dist/runstore/pipelineDagSnapshot.d.ts +1 -0
- package/dist/runstore/pipelineDagSnapshot.js +51 -11
- package/dist/runstore/port.d.ts +185 -3
- package/dist/runstore/port.js +42 -3
- package/dist/runstore/runProjection.d.ts +7 -3
- package/dist/runstore/runProjection.js +30 -15
- package/dist/runstore/sqlite/SqliteRunStore.d.ts +25 -1
- package/dist/runstore/sqlite/SqliteRunStore.js +626 -8
- package/dist/runstore/sqlite/schema.d.ts +1 -1
- package/dist/runstore/sqlite/schema.js +99 -0
- package/dist/runstore/stageInstanceId.d.ts +1 -1
- package/dist/runstore/stageInstanceId.js +3 -2
- package/dist/runstore/trackProjection.js +59 -27
- package/dist/runstore/verificationHistory.d.ts +19 -0
- package/dist/runstore/verificationHistory.js +30 -0
- package/dist/runtime/cloneSchedule.d.ts +40 -4
- package/dist/runtime/cloneSchedule.js +212 -28
- package/dist/runtime/completionCheckRunner.d.ts +137 -0
- package/dist/runtime/completionCheckRunner.js +365 -0
- package/dist/runtime/completionCheckoutBaseline.d.ts +3 -0
- package/dist/runtime/completionCheckoutBaseline.js +44 -0
- package/dist/runtime/envelopeRouting.d.ts +10 -3
- package/dist/runtime/envelopeRouting.js +146 -5
- package/dist/runtime/feedbackLoopCoordinator.d.ts +74 -0
- package/dist/runtime/feedbackLoopCoordinator.js +446 -0
- package/dist/runtime/feedbackLoopDecision.d.ts +44 -0
- package/dist/runtime/feedbackLoopDecision.js +207 -0
- package/dist/runtime/feedbackLoopRoute.d.ts +5 -0
- package/dist/runtime/feedbackLoopRoute.js +13 -0
- package/dist/runtime/feedbackLoopSchedule.d.ts +38 -0
- package/dist/runtime/feedbackLoopSchedule.js +72 -0
- package/dist/runtime/forkGeneration.d.ts +72 -0
- package/dist/runtime/forkGeneration.js +295 -0
- package/dist/runtime/gitCheckoutCapability.d.ts +7 -0
- package/dist/runtime/gitCheckoutCapability.js +97 -0
- package/dist/runtime/manualRecoveryState.d.ts +40 -0
- package/dist/runtime/manualRecoveryState.js +73 -0
- package/dist/runtime/pipelineScheduler.d.ts +13 -1
- package/dist/runtime/pipelineScheduler.js +328 -45
- package/dist/runtime/replayLifecycle.d.ts +107 -0
- package/dist/runtime/replayLifecycle.js +168 -0
- package/dist/runtime/runManager.d.ts +61 -1
- package/dist/runtime/runManager.js +501 -7
- package/dist/runtime/runRetryCoordinator.d.ts +8 -1
- package/dist/runtime/runRetryCoordinator.js +47 -10
- package/dist/runtime/stageAttemptBootstrap.d.ts +3 -1
- package/dist/runtime/stageAttemptBootstrap.js +79 -1
- package/dist/runtime/stageProcessLauncher.d.ts +1 -1
- package/dist/runtime/stageRecovery.js +1 -1
- package/dist/runtime/stageRunner.d.ts +4 -1
- package/dist/runtime/stageRunner.js +29 -3
- package/dist/runtime/stageWorker.js +69 -2
- package/dist/runtime/stageWorkerProtocol.d.ts +1 -1
- package/dist/runtime/verifiedStageExecution.d.ts +29 -0
- package/dist/runtime/verifiedStageExecution.js +119 -0
- package/dist/server/bootstrap.js +1 -0
- package/dist/server/http.js +114 -0
- package/dist/server/operatorResults.js +2 -0
- package/dist/tools/askOperator.d.ts +161 -5
- package/dist/tools/askOperator.js +58 -25
- package/dist/tools/emitStageEnvelope.d.ts +12 -12
- package/dist/tools/emitStageEnvelope.js +112 -24
- package/dist/types/completion.d.ts +43 -0
- package/dist/types/completion.js +1 -0
- package/dist/types/envelope.d.ts +18 -0
- package/dist/types/forkChoice.d.ts +9 -4
- package/dist/types/forkChoice.js +1 -1
- package/dist/types/pipeline.d.ts +39 -2
- package/dist/types/preEmitCheck.d.ts +18 -0
- package/dist/types/preEmitCheck.js +1 -0
- package/dist/types/stage.d.ts +13 -0
- package/dist/types/stageflowManifest.d.ts +2 -0
- package/dist/ui/assets/index-CXGz4nL7.js +118 -0
- package/dist/ui/assets/{index-C3N7MXAC.css → index-uVHiae8s.css} +1 -1
- package/dist/ui/index.html +2 -2
- package/package.json +2 -1
- package/skills/stageflow/references/control-surface.md +4 -2
- package/skills/stageflow-author/references/catalog-mapping.md +26 -2
- package/skills/stageflow-author/references/catalog-write-conventions.md +1 -1
- package/skills/stageflow-run/SKILL.md +56 -25
- package/skills/stageflow-run/references/mcp-call.md +2 -2
- package/skills/stageflow-run/references/native-question-ui.md +78 -0
- package/dist/ui/assets/index-CSoGDA3A.js +0 -118
package/dist/ui/index.html
CHANGED
|
@@ -13,8 +13,8 @@
|
|
|
13
13
|
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Figtree:wght@400;500;600;700&display=swap" />
|
|
14
14
|
<link rel="icon" type="image/svg+xml" href="/stageflow-icon.svg" />
|
|
15
15
|
<title>Stageflow</title>
|
|
16
|
-
<script type="module" crossorigin src="/assets/index-
|
|
17
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
16
|
+
<script type="module" crossorigin src="/assets/index-CXGz4nL7.js"></script>
|
|
17
|
+
<link rel="stylesheet" crossorigin href="/assets/index-uVHiae8s.css">
|
|
18
18
|
</head>
|
|
19
19
|
<body>
|
|
20
20
|
<div id="root"></div>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "stageflow",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
4
4
|
"description": "Stageflow — CLI pipeline runtime for configurable stages on Pi",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -60,6 +60,7 @@
|
|
|
60
60
|
"vitest": "^4.1.10"
|
|
61
61
|
},
|
|
62
62
|
"dependencies": {
|
|
63
|
+
"@anthropic-ai/claude-agent-sdk": "^0.3.263",
|
|
63
64
|
"@earendil-works/pi-ai": "^0.84.1",
|
|
64
65
|
"@earendil-works/pi-coding-agent": "^0.84.1",
|
|
65
66
|
"@modelcontextprotocol/node": "^2.0.0",
|
|
@@ -11,7 +11,7 @@ node ../scripts/detect-host.mjs
|
|
|
11
11
|
node ../scripts/detect-host.mjs --base-url http://127.0.0.1:3847
|
|
12
12
|
```
|
|
13
13
|
|
|
14
|
-
The script `GET`s `{baseUrl}/api/health` (default `http://127.0.0.1:3847`, 1500 ms timeout). **up** means HTTP 200 and parseable JSON. Non-200, non-JSON, or timeout is **down**.
|
|
14
|
+
The script `GET`s `{baseUrl}/api/health` (default `http://127.0.0.1:3847`, 1500 ms timeout). **up** means HTTP 200 and parseable JSON. Non-200, non-JSON, or timeout is **down**. This probe is Stageflow host up/down only — it does not detect a coding-agent question UI. Gate presentation lives in [`../../stageflow-run/references/native-question-ui.md`](../../stageflow-run/references/native-question-ui.md).
|
|
15
15
|
|
|
16
16
|
Stdout is one line: `up <baseUrl>` or `down <baseUrl>`. Exit `0` when up, `1` when down, `2` on usage error.
|
|
17
17
|
|
|
@@ -23,4 +23,6 @@ Use MCP tools over the Streamable HTTP endpoint at `{baseUrl}/mcp`. Tool names a
|
|
|
23
23
|
|
|
24
24
|
## When the host is down
|
|
25
25
|
|
|
26
|
-
Use the `sf` CLI. Command names and flags live in [docs/cli-reference.md](../../../docs/cli-reference.md). Typical talking-job commands: `sf run`, `sf validate`, `sf envelope get`, `sf artifact read`, `sf providers`.
|
|
26
|
+
Use the `sf` CLI. Command names and flags live in [docs/cli-reference.md](../../../docs/cli-reference.md). Typical talking-job commands: `sf run`, `sf runs waiting`, `sf runs answer`, `sf runs wait`, `sf validate`, `sf envelope get`, `sf artifact read`, `sf providers`.
|
|
27
|
+
|
|
28
|
+
Probe before each mutating `sf runs` verb. If the probe is **up** or the command refuses because a host is up, continue that gate via MCP — do not start a second mutating writer, and do not start `sf mcp` as a disposable bridge.
|
|
@@ -16,18 +16,19 @@ Collision handling lives in [`catalog-write-conventions.md`](catalog-write-conve
|
|
|
16
16
|
|
|
17
17
|
## Sequencing
|
|
18
18
|
|
|
19
|
-
`needs` is a single parent stage id
|
|
19
|
+
`needs` is a single parent stage id, or an array of at least two parent ids when one step waits for several earlier steps. Strings default to `on: [succeeded]`. `{ id, on }` accepts a non-empty unique subset of `succeeded` | `failed` | `skipped`.
|
|
20
20
|
|
|
21
21
|
| Human says | Pipeline shape |
|
|
22
22
|
|---|---|
|
|
23
23
|
| Steps in order | Each later stage `needs` the previous id. No `fork` field. |
|
|
24
24
|
| Steps happen together | Sibling stages share one `needs` (the same parent). No `fork` field. |
|
|
25
|
+
| A later step waits for two or more earlier steps | That stage `needs` an array of those parent ids (length ≥ 2). |
|
|
25
26
|
| Exactly one branch runs | Deciding stage gets `fork: { select: one }`. Each branch `needs` the decider. |
|
|
26
27
|
| Either, both, or a subset may run | Deciding stage gets `fork: { select: subset }`. Each branch `needs` the decider. |
|
|
27
28
|
|
|
28
29
|
Default `select` is `one` unless the human says more than one branch can run.
|
|
29
30
|
|
|
30
|
-
Map linear chains, sibling fan-out, and single-level `fork`. A runtime clone count is not knowable from a static description —
|
|
31
|
+
Map linear chains, sibling fan-out, generic fan-in (`needs` array), and single-level `fork`. A runtime clone count is not knowable from a static description — leave `clonable` and `clone_forks` unset. Clone-list joins (one catalog parent, `priorEnvelopes`) are not this shape.
|
|
31
32
|
|
|
32
33
|
A review, approval, or sign-off step is a gated stage: put `gate_kinds` on that stage file and follow [`stage-prompt-template.md`](stage-prompt-template.md).
|
|
33
34
|
|
|
@@ -110,6 +111,29 @@ stages:
|
|
|
110
111
|
|
|
111
112
|
No `fork` field. Both siblings run.
|
|
112
113
|
|
|
114
|
+
### Diamond join
|
|
115
|
+
|
|
116
|
+
"After research and validation both finish, synthesize":
|
|
117
|
+
|
|
118
|
+
```yaml
|
|
119
|
+
stages:
|
|
120
|
+
- id: clarify
|
|
121
|
+
uses: ./clarify.yaml
|
|
122
|
+
- id: research
|
|
123
|
+
uses: ./research.yaml
|
|
124
|
+
needs: clarify
|
|
125
|
+
- id: validation
|
|
126
|
+
uses: ./validation.yaml
|
|
127
|
+
needs: clarify
|
|
128
|
+
- id: synthesize
|
|
129
|
+
uses: ./synthesize.yaml
|
|
130
|
+
needs:
|
|
131
|
+
- research
|
|
132
|
+
- validation
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
`needs` array length is ≥ 2. The join reads `priorEnvelopesByStage`, not clone-list `priorEnvelopes`.
|
|
136
|
+
|
|
113
137
|
### Fork, select subset
|
|
114
138
|
|
|
115
139
|
"Email, post, or both could go out":
|
|
@@ -20,7 +20,7 @@ One directory (or the project root) holds:
|
|
|
20
20
|
| `<pipeline-id>.pipeline.yaml` | `id` matching the filename stem; `stages:` object entries |
|
|
21
21
|
| `<stage-id>.yaml` | `id`, `system_prompt`, `model`; `gate_kinds` when the step is gated |
|
|
22
22
|
|
|
23
|
-
Each pipeline stage entry has `id` and `uses: ./<id>.yaml` (path relative to the pipeline file). Non-root stages add `needs: <parent-id
|
|
23
|
+
Each pipeline stage entry has `id` and `uses: ./<id>.yaml` (path relative to the pipeline file). Non-root stages add `needs: <parent-id>`, or `needs:` as an array of at least two parent ids (strings or `{ id, on }`) when the stage waits for several parents. A deciding stage adds `fork:`. Filename stem matches `id` on every file.
|
|
24
24
|
|
|
25
25
|
## Collisions
|
|
26
26
|
|
|
@@ -2,19 +2,20 @@
|
|
|
2
2
|
name: stageflow-run
|
|
3
3
|
description: >-
|
|
4
4
|
Starts a catalog pipeline from the harness, reports progress, and answers
|
|
5
|
-
every HITL gate
|
|
6
|
-
|
|
5
|
+
every HITL gate on the host native question UI when it can, otherwise in
|
|
6
|
+
this chat. Triggers: run a pipeline, start <pipeline>, check on my run,
|
|
7
|
+
answer the pending question.
|
|
7
8
|
compatibility: Requires the sf CLI on PATH. An MCP host (sf ui or sf mcp) is optional.
|
|
8
9
|
disable-model-invocation: true
|
|
9
10
|
---
|
|
10
11
|
|
|
11
12
|
# Stageflow run
|
|
12
13
|
|
|
13
|
-
Start a catalog pipeline,
|
|
14
|
+
Start a catalog pipeline, present HITL on the host native question UI when it can, and report the outcome. Author and session-capture own catalog YAML; this job owns the run and any throwaway `*.task.yaml`.
|
|
14
15
|
|
|
15
16
|
Talking jobs cite [`../stageflow/references/control-surface.md`](../stageflow/references/control-surface.md). Probe with [`../stageflow/scripts/detect-host.mjs`](../stageflow/scripts/detect-host.mjs) only. Do not write a second probe.
|
|
16
17
|
|
|
17
|
-
MCP tool shapes: [`docs/mcp.md`](../../docs/mcp.md). CLI flags and exit codes: [`docs/cli-reference.md`](../../docs/cli-reference.md). Direct tool calls: [`references/mcp-call.md`](references/mcp-call.md). Selection: [`references/task-and-pipeline-selection.md`](references/task-and-pipeline-selection.md).
|
|
18
|
+
MCP tool shapes: [`docs/mcp.md`](../../docs/mcp.md). CLI flags and exit codes: [`docs/cli-reference.md`](../../docs/cli-reference.md). Direct tool calls: [`references/mcp-call.md`](references/mcp-call.md). Selection: [`references/task-and-pipeline-selection.md`](references/task-and-pipeline-selection.md). Gate presentation: [`references/native-question-ui.md`](references/native-question-ui.md).
|
|
18
19
|
|
|
19
20
|
## Preconditions
|
|
20
21
|
|
|
@@ -38,15 +39,15 @@ node ../stageflow/scripts/detect-host.mjs --base-url http://127.0.0.1:3847
|
|
|
38
39
|
|
|
39
40
|
Read [`references/task-and-pipeline-selection.md`](references/task-and-pipeline-selection.md). Follow it until you have a pipeline filesystem path and a task (catalog path, MCP inline object, or CLI throwaway file).
|
|
40
41
|
|
|
41
|
-
If this chat already has a `runId` and the request is check, answer, or continue: skip start. Host up → [Wait](#wait). Host down
|
|
42
|
+
If this chat already has a `runId` and the request is check, answer, or continue: skip start. Host up → [Wait](#wait). Host down → [CLI wait](#cli-wait).
|
|
42
43
|
|
|
43
44
|
**Done when** the target is named, or a live `runId` is in hand.
|
|
44
45
|
|
|
45
46
|
## MCP tools
|
|
46
47
|
|
|
47
|
-
Prefer this harness's native Stageflow MCP tools when their names are already in the tool list. When they are not, call [`scripts/mcp-call.mjs`](scripts/mcp-call.mjs) — see [`references/mcp-call.md`](references/mcp-call.md).
|
|
48
|
+
Prefer this harness's native Stageflow MCP tools when their names are already in the tool list. When they are not, call [`scripts/mcp-call.mjs`](scripts/mcp-call.mjs) — see [`references/mcp-call.md`](references/mcp-call.md). Use `--stateless` only when the **user** started the host with `--mcp-stateless`. Do not start `sf mcp` from this skill.
|
|
48
49
|
|
|
49
|
-
Call only `list_pipelines`, `list_tasks`, `start_run`, `get_run`, `wait_run`, `list_waiting`, `answer_gate`, `get_health`.
|
|
50
|
+
Call only these Stageflow MCP tools: `list_pipelines`, `list_tasks`, `start_run`, `get_run`, `wait_run`, `list_waiting`, `answer_gate`, `get_health`. Host question tools already in this harness's tool list (`AskQuestion`, `AskUserQuestion`, `ask_user`) are for [Gate](#gate) presentation, not Stageflow MCP.
|
|
50
51
|
|
|
51
52
|
## MCP path
|
|
52
53
|
|
|
@@ -78,9 +79,14 @@ Call only `list_pipelines`, `list_tasks`, `start_run`, `get_run`, `wait_run`, `l
|
|
|
78
79
|
|
|
79
80
|
### Gate
|
|
80
81
|
|
|
82
|
+
Read [`references/native-question-ui.md`](references/native-question-ui.md) before presenting a pending prompt.
|
|
83
|
+
|
|
81
84
|
1. `list_waiting` with `{ "runId" }`.
|
|
82
85
|
2. Print the pending prompt text **verbatim** (and artifacts / sub-questions when present).
|
|
83
|
-
3.
|
|
86
|
+
3. Present the decision as that reference directs:
|
|
87
|
+
- If a host question tool is already in this harness's tool list and the gate is representable, invoke that picker. For `multi_question`, one picker call with one question per sub-item when **every** sub-question is representable, then one `answer_gate`; otherwise the **whole** gate in this chat.
|
|
88
|
+
- Otherwise collect the reply in this chat.
|
|
89
|
+
4. Map the reply to the prompt `kind` (picker Accept/Reject → `accept`/`reject`):
|
|
84
90
|
|
|
85
91
|
| kind | `answer` |
|
|
86
92
|
|---|---|
|
|
@@ -89,12 +95,12 @@ Call only `list_pipelines`, `list_tasks`, `start_run`, `get_run`, `wait_run`, `l
|
|
|
89
95
|
| `artifact_backed` | `{ "promptId", "kind": "artifact_backed", "decision": "accept" \| "reject" }` |
|
|
90
96
|
| `multi_question` | `{ "promptId", "kind": "multi_question", "answers": { "<id>": { "kind", "text" \| "decision" } } }` |
|
|
91
97
|
|
|
92
|
-
`promptId` is `pending_prompt.id` or `waiting_prompt_id`. Map yes/y/accept/approve to `accept`; no/n/reject/deny to `reject`.
|
|
98
|
+
`promptId` is `pending_prompt.id` or `waiting_prompt_id`. Map yes/y/accept/approve/Accept to `accept`; no/n/reject/deny/Reject to `reject`. Each `multi_question` sub-answer keeps that sub's `kind` (`confirm` → `decision`, `free_text` → `text`). Collect every sub-answer before `answer_gate`.
|
|
93
99
|
|
|
94
|
-
|
|
95
|
-
|
|
100
|
+
5. `answer_gate` with `{ "runId", "stageId", "answer" }`.
|
|
101
|
+
6. Return to [Wait](#wait).
|
|
96
102
|
|
|
97
|
-
**Done when** `answer_gate` returns `{ "ok": true }` and Wait is re-entered. On `isError` (400 / 404 / 409), print the payload and
|
|
103
|
+
**Done when** `answer_gate` returns `{ "ok": true }` and Wait is re-entered. On `isError` (400 / 404 / 409), print the payload and collect a corrected reply the same way (picker if still representable; otherwise this chat).
|
|
98
104
|
|
|
99
105
|
## CLI path
|
|
100
106
|
|
|
@@ -113,24 +119,49 @@ Parse the single JSON document.
|
|
|
113
119
|
| `0` | `succeeded` | [Report](#report) |
|
|
114
120
|
| `1` | `failed` | [Report](#report) `reason` verbatim |
|
|
115
121
|
| `1` | `busy` | [Report](#report) `busy_capacity` / `busy_checkout` and the included fields. Stop. |
|
|
116
|
-
| `2` | `waiting` | [
|
|
122
|
+
| `2` | `waiting` | [CLI wait](#cli-wait) with this `runId` / `runDir` |
|
|
123
|
+
|
|
124
|
+
**Done when** the outcome is reported, or a waiting exit has handed `runId` to CLI wait.
|
|
125
|
+
|
|
126
|
+
## CLI wait
|
|
127
|
+
|
|
128
|
+
Host down after `sf run` exit `2`, or when this chat already has a `runId` and the host is down. Do not start `sf mcp`. Presentation follows [`references/native-question-ui.md`](references/native-question-ui.md). Submit with `sf runs answer --json`.
|
|
129
|
+
|
|
130
|
+
1. Probe again with [`../stageflow/scripts/detect-host.mjs`](../stageflow/scripts/detect-host.mjs) (the host may have appeared). Probe before each `sf runs answer`.
|
|
131
|
+
2. **Up:** use that host — native tools if present, otherwise `mcp-call.mjs` (omit `--stateless` unless the user started the host with `--mcp-stateless`). Continue at [Gate](#gate).
|
|
132
|
+
3. **Down:**
|
|
117
133
|
|
|
118
|
-
|
|
134
|
+
```
|
|
135
|
+
sf runs waiting --run <runId> --json
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Print the pending prompt **verbatim**. Present it as native-question-ui directs (picker or chat). Map the reply with the [Gate](#gate) kind table.
|
|
119
139
|
|
|
120
|
-
|
|
140
|
+
```
|
|
141
|
+
sf runs answer --run <runId> --stage <stageId> --answer '<json>' --json
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Success is `{ "ok": true }` exit `0` even if the run parks again. Do not treat answer as terminal.
|
|
145
|
+
|
|
146
|
+
```
|
|
147
|
+
sf runs wait --run <runId> --json --until any
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
A shorter `--timeout-ms` is fine when the harness timeout is tight. Default `60000`, max `240000`. Branch on JSON `reason`, not wait exit `0`:
|
|
151
|
+
|
|
152
|
+
| `reason` | next |
|
|
153
|
+
|---|---|
|
|
154
|
+
| `waiting` or `already` with a waiting snapshot | `sf runs waiting` then answer |
|
|
155
|
+
| `terminal` | [Report](#report) |
|
|
156
|
+
| `timeout` | call `sf runs wait` again |
|
|
121
157
|
|
|
122
|
-
|
|
158
|
+
Wait abort (exit `130`, `{ "error", "code": "aborted" }`) → [Report](#report): the run continues, resumable later. Stop. Do not reuse `sf run` exit `2` for wait.
|
|
123
159
|
|
|
124
|
-
|
|
125
|
-
2. **Up:** use that host — native tools if present, otherwise `mcp-call.mjs` (omit `--stateless`). Continue at [Gate](#gate).
|
|
126
|
-
3. **Down:** start `sf mcp --mcp-stateless` as a background process on that same project root. This is a disposable HITL bridge, not setup auto-start.
|
|
127
|
-
4. Probe until stdout is `up <baseUrl>`, at most 8 times. **Done when** the host is up. If it stays down, [Report](#report) the parked `runId` / `runDir` and the bind/stderr text. Stop.
|
|
128
|
-
5. Drive the rest with `mcp-call.mjs --stateless` only: [Gate](#gate) → [Wait](#wait) until terminal. A second wait on the same run reuses this process.
|
|
129
|
-
6. When the run is terminal, stop the bridge process.
|
|
160
|
+
4. If `sf runs answer` refuses because the host came up, continue that gate via MCP [Gate](#gate). Do not dual-write.
|
|
130
161
|
|
|
131
162
|
If this chat ends before terminal, say the run stays in `.stageflow/` and to continue by re-invoking this skill (or starting a host and using MCP).
|
|
132
163
|
|
|
133
|
-
**Done when** the run is terminal
|
|
164
|
+
**Done when** the run is terminal, aborted, or a hard error is printed.
|
|
134
165
|
|
|
135
166
|
## Report
|
|
136
167
|
|
|
@@ -142,10 +173,10 @@ Print one shape on every path:
|
|
|
142
173
|
4. Run id.
|
|
143
174
|
5. Run folder (`runDir` / `.stageflow/`).
|
|
144
175
|
|
|
145
|
-
MCP `get_run` / `wait_run` carry live stage state. `sf run --json` reports start and finish only — say that in chat when the CLI path ran without a
|
|
176
|
+
MCP `get_run` / `wait_run` and `sf runs wait` / `sf runs show` `--json` carry live stage state. `sf run --json` reports start and finish only — say that in chat when the CLI path ran without a later wait/show. A succeeded report names id and folder and does not keep waiting language. A failure names `reason` in this same shape.
|
|
146
177
|
|
|
147
178
|
**Done when** that five-part report is printed.
|
|
148
179
|
|
|
149
180
|
## Non-goals
|
|
150
181
|
|
|
151
|
-
This job starts, watches, and answers runs. It does not require `sf ui`. The operator console is not the answer path. It does not register or invent an MCP tool.
|
|
182
|
+
This job starts, watches, and answers runs. It does not require `sf ui`. The operator console is not the answer path. It does not register or invent an MCP tool. It does not start `sf mcp`. It does not retry, abandon, or rerun stages.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# mcp-call
|
|
2
2
|
|
|
3
|
-
Use [`../scripts/mcp-call.mjs`](../scripts/mcp-call.mjs) when this harness has no native Stageflow MCP tools
|
|
3
|
+
Use [`../scripts/mcp-call.mjs`](../scripts/mcp-call.mjs) when the Stageflow host is up and this harness has no native Stageflow MCP tools. Do not start `sf mcp` from this skill.
|
|
4
4
|
|
|
5
5
|
```
|
|
6
6
|
node scripts/mcp-call.mjs --base-url <url> --tool <name> --args '<json>' [--stateless]
|
|
@@ -8,7 +8,7 @@ node scripts/mcp-call.mjs --base-url <url> --tool <name> --args '<json>' [--stat
|
|
|
8
8
|
|
|
9
9
|
Default `--base-url` is `http://127.0.0.1:3847`. `--args` defaults to `{}`.
|
|
10
10
|
|
|
11
|
-
`--stateless` sends one `tools/call` and no session header. Use it for hosts started with `sf mcp --mcp-stateless
|
|
11
|
+
`--stateless` sends one `tools/call` and no session header. Use it for hosts the **user** started with `sf mcp --mcp-stateless`, not a skill-started process.
|
|
12
12
|
|
|
13
13
|
Without `--stateless`: `initialize`, capture `Mcp-Session-Id`, reuse it for `tools/call`.
|
|
14
14
|
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# Native question UI
|
|
2
|
+
|
|
3
|
+
Present a waiting HITL gate on the coding-agent host's structured question tool when that tool can represent the gate. Otherwise collect the reply in this chat. Submit with `answer_gate` when the Stageflow host is up, or `sf runs answer --json` when it is down. This file is presentation only — it does not change MCP tools, `detect-host.mjs`, or the operator console.
|
|
4
|
+
|
|
5
|
+
Read `pending_prompt` from `list_waiting` or `sf runs waiting --json` (not only `waiting_questions` strings).
|
|
6
|
+
|
|
7
|
+
## Detect a picker
|
|
8
|
+
|
|
9
|
+
Inspect **this harness's current tool list**. A picker is a tool that collects a structured choice (question text plus labeled options), not a free-form chat reply.
|
|
10
|
+
|
|
11
|
+
Do not run `detect-host.mjs` for this. That script is Stageflow host up/down (MCP vs CLI). It does not detect a question UI.
|
|
12
|
+
|
|
13
|
+
| If this name is in the tool list | Use it |
|
|
14
|
+
|---|---|
|
|
15
|
+
| `AskQuestion` | Cursor question cards |
|
|
16
|
+
| `AskUserQuestion` | Claude Code questions |
|
|
17
|
+
| `ask_user` | Pi questions |
|
|
18
|
+
|
|
19
|
+
If none of those (or another options-based question tool already in the list) is present, collect in chat. Do not invent a tool name or call shape. Codex and OpenCode stay in chat until an options-based question tool appears in the list.
|
|
20
|
+
|
|
21
|
+
Pass the pending message as the question text and the mapped labels as that tool's options. One question per call except `multi_question`: one call with one question per representable sub-item when the tool accepts a questions array (Cursor `AskQuestion` does). Do not invent placeholder options such as Type in Other — host Other is the custom-text escape. If the picker returns a custom or Other value, map that text the same as a chat reply.
|
|
22
|
+
|
|
23
|
+
**Done when** you know picker or chat for this gate.
|
|
24
|
+
|
|
25
|
+
## Representable vs chat
|
|
26
|
+
|
|
27
|
+
| Gate | Picker | Chat |
|
|
28
|
+
|---|---|---|
|
|
29
|
+
| `confirm` | Accept and Reject | Host has no picker |
|
|
30
|
+
| `artifact_backed` | Accept and Reject (decision only) | Host has no picker |
|
|
31
|
+
| `free_text` with a harvested closed set | Those names as options | Open-ended, or harvest is ambiguous |
|
|
32
|
+
| `free_text` with no closed set | — | Always |
|
|
33
|
+
| `multi_question` | One picker call with one question per sub-item when **every** sub-question is representable; then one submit | Any sub-question is open or unmappable — the **whole** gate in chat |
|
|
34
|
+
|
|
35
|
+
Map picker Accept / yes / approve → `accept`. Reject / no / deny → `reject`. Always include `promptId` (`pending_prompt.id` or `waiting_prompt_id`).
|
|
36
|
+
|
|
37
|
+
| Item | Submit |
|
|
38
|
+
|---|---|
|
|
39
|
+
| Top-level or sub `confirm` | `{ "kind": "confirm", "decision": "accept" \| "reject" }` |
|
|
40
|
+
| Top-level `artifact_backed` | `{ "kind": "artifact_backed", "decision": "accept" \| "reject" }` plus optional `text` after a reject follow-up |
|
|
41
|
+
| Top-level or sub `free_text` (including harvested) | `{ "kind": "free_text", "text": "<selected label>" }` |
|
|
42
|
+
|
|
43
|
+
Never submit a `confirm` item as `free_text`. For `multi_question`, wrap sub-answers in the Gate `answers` object and submit once (`answer_gate` or `sf runs answer --json`).
|
|
44
|
+
|
|
45
|
+
When in doubt, chat.
|
|
46
|
+
|
|
47
|
+
**Done when** the path (picker or chat) is chosen from this table.
|
|
48
|
+
|
|
49
|
+
## Harvest
|
|
50
|
+
|
|
51
|
+
Harvest only from `pending_prompt.message` (or a `multi_question` item `message`) when it names a **hard closed set**:
|
|
52
|
+
|
|
53
|
+
- `reply with exactly one of:`
|
|
54
|
+
- `choose one of:`
|
|
55
|
+
- `exactly one of`
|
|
56
|
+
|
|
57
|
+
Split the names after that directive (commas, `or`, newlines). Those literal names are the picker options.
|
|
58
|
+
|
|
59
|
+
Do **not** harvest loose example lists (`you might try`, `e.g.`, `for example` without `exactly one of`). A listed example set that is not a hard closed directive stays in chat.
|
|
60
|
+
|
|
61
|
+
Submit the selected label as `text` for `free_text` items. If the prompt also lists aliases for the same choice (for example `minimal` and `prototype~1`), keep every listed name on the card; do not rewrite the label at submit — the stage maps synonyms.
|
|
62
|
+
|
|
63
|
+
**Done when** the option list is the prompt's literal closed set, or the gate stayed in chat.
|
|
64
|
+
|
|
65
|
+
## Artifact-backed
|
|
66
|
+
|
|
67
|
+
1. Print the pending message and artifact paths (from `list_waiting`) in this chat. Do not put artifact bodies on the card. Do not call `read_artifact` unless the human asked to open a file.
|
|
68
|
+
2. Open the picker with Accept and Reject.
|
|
69
|
+
3. Accept → submit immediately (`decision: "accept"`).
|
|
70
|
+
4. Reject → one chat follow-up for optional notes, then submit `decision: "reject"` and `text` when they typed notes.
|
|
71
|
+
|
|
72
|
+
**Done when** the operator has seen the paths in chat and the decision is on the picker (or chat if there is no picker).
|
|
73
|
+
|
|
74
|
+
## Errors
|
|
75
|
+
|
|
76
|
+
On MCP `answer_gate` `isError` (400 / 404 / 409) or CLI `sf runs answer` exit `1`, print the payload. Collect a corrected reply on the picker if the gate is still representable; otherwise chat. Then submit again.
|
|
77
|
+
|
|
78
|
+
**Done when** `{ "ok": true }` or the error payload has been printed and a corrected reply is in hand.
|