stageflow 0.3.0 → 0.9.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 +44 -14
- package/dist/agent/activity.d.ts +2 -0
- package/dist/agent/cursorProvider.d.ts +1 -10
- package/dist/agent/cursorProvider.js +18 -10
- package/dist/agent/fakeAgent.js +11 -7
- package/dist/agent/piAdapter.d.ts +2 -1
- package/dist/agent/piAdapter.js +28 -25
- package/dist/agent/port.d.ts +6 -1
- package/dist/agent/port.js +3 -0
- package/dist/cli/artifactCommand.d.ts +10 -0
- package/dist/cli/artifactCommand.js +120 -0
- package/dist/cli/envelopeCommand.d.ts +32 -0
- package/dist/cli/envelopeCommand.js +285 -0
- package/dist/cli/exportRunCommand.d.ts +10 -0
- package/dist/cli/exportRunCommand.js +150 -0
- package/dist/cli/handoffFormat.d.ts +22 -0
- package/dist/cli/handoffFormat.js +61 -0
- package/dist/cli/runCommand.d.ts +5 -1
- package/dist/cli/runCommand.js +36 -5
- package/dist/cli/runOutput.d.ts +4 -1
- package/dist/cli/runOutput.js +36 -8
- package/dist/cli/runsCommand.d.ts +22 -0
- package/dist/cli/runsCommand.js +591 -0
- package/dist/cli/skillsCommand.d.ts +16 -0
- package/dist/cli/skillsCommand.js +459 -0
- package/dist/cli/validateOutput.d.ts +2 -2
- package/dist/cli/validateOutput.js +2 -2
- package/dist/cli.js +110 -5
- package/dist/config/loadPipeline.js +5 -1
- package/dist/config/normalizePipelineStageEntry.d.ts +2 -0
- package/dist/config/normalizePipelineStageEntry.js +25 -1
- package/dist/config/pipelineStageKeys.js +1 -1
- package/dist/config/resolveForkEmitContext.d.ts +2 -1
- package/dist/config/resolveForkEmitContext.js +23 -1
- package/dist/config/resolvePipelineDag.js +36 -4
- package/dist/envelope/check.d.ts +2 -0
- package/dist/envelope/check.js +51 -0
- package/dist/envelope/cloneForks.d.ts +2 -0
- package/dist/envelope/cloneForks.js +62 -0
- package/dist/envelope/forkChoice.js +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +3 -1
- package/dist/mcp/catalogTools.d.ts +3 -0
- package/dist/mcp/catalogTools.js +187 -0
- package/dist/mcp/controlTools.d.ts +3 -0
- package/dist/mcp/controlTools.js +147 -0
- package/dist/mcp/deps.d.ts +11 -0
- package/dist/mcp/deps.js +1 -0
- package/dist/mcp/projectRun.d.ts +1 -40
- package/dist/mcp/projectRun.js +1 -64
- package/dist/mcp/resources.d.ts +5 -0
- package/dist/mcp/resources.js +45 -0
- package/dist/mcp/server.d.ts +16 -3
- package/dist/mcp/server.js +159 -3
- package/dist/mcp/toolResults.d.ts +7 -0
- package/dist/mcp/toolResults.js +6 -0
- package/dist/mcp/tools.d.ts +2 -7
- package/dist/mcp/tools.js +52 -103
- package/dist/mcp/waitRun.d.ts +46 -0
- package/dist/mcp/waitRun.js +146 -0
- package/dist/mcp/waitingGates.d.ts +12 -0
- package/dist/mcp/waitingGates.js +83 -0
- package/dist/package-meta.d.ts +1 -0
- package/dist/package-meta.js +1 -0
- package/dist/projection/projectRun.d.ts +44 -0
- package/dist/projection/projectRun.js +71 -0
- package/dist/prompt/priorEnvelope.d.ts +1 -1
- package/dist/prompt/priorEnvelope.js +4 -1
- package/dist/runstore/paths.js +2 -0
- package/dist/runstore/pipelineDagSnapshot.d.ts +9 -0
- package/dist/runstore/pipelineDagSnapshot.js +90 -1
- package/dist/runstore/port.d.ts +13 -2
- package/dist/runstore/port.js +4 -1
- package/dist/runstore/runProjection.d.ts +1 -1
- package/dist/runstore/runProjection.js +14 -3
- package/dist/runstore/sqlite/SqliteRunStore.d.ts +3 -2
- package/dist/runstore/sqlite/SqliteRunStore.js +35 -3
- package/dist/runstore/stageInstanceId.d.ts +4 -0
- package/dist/runstore/stageInstanceId.js +23 -0
- package/dist/runstore/syntheticStageSnapshot.d.ts +1 -1
- package/dist/runstore/syntheticStageSnapshot.js +2 -1
- package/dist/runstore/trackProjection.js +28 -12
- package/dist/runstore/workspaceLayout.d.ts +1 -0
- package/dist/runstore/workspaceLayout.js +1 -1
- package/dist/runtime/cloneSchedule.d.ts +18 -0
- package/dist/runtime/cloneSchedule.js +233 -0
- package/dist/runtime/envelopeRouting.d.ts +2 -1
- package/dist/runtime/envelopeRouting.js +80 -13
- package/dist/runtime/pipelineScheduler.d.ts +6 -2
- package/dist/runtime/pipelineScheduler.js +166 -30
- package/dist/runtime/resumeReconstruct.js +8 -3
- package/dist/runtime/runChangeBus.d.ts +18 -0
- package/dist/runtime/runChangeBus.js +83 -0
- package/dist/runtime/runManager.d.ts +7 -0
- package/dist/runtime/runManager.js +31 -2
- package/dist/runtime/runRetryCoordinator.d.ts +2 -0
- package/dist/runtime/runRetryCoordinator.js +4 -1
- package/dist/runtime/stageAttemptBootstrap.d.ts +1 -0
- package/dist/runtime/stageAttemptBootstrap.js +17 -9
- package/dist/runtime/stageRunner.d.ts +1 -0
- package/dist/runtime/stageRunner.js +17 -15
- package/dist/runtime/stageWorker.js +11 -4
- package/dist/server/bootstrap.d.ts +32 -0
- package/dist/server/bootstrap.js +56 -0
- package/dist/server/createHttpHost.d.ts +34 -0
- package/dist/server/createHttpHost.js +70 -0
- package/dist/server/http.d.ts +6 -13
- package/dist/server/http.js +295 -374
- package/dist/server/mcpHost.d.ts +22 -0
- package/dist/server/mcpHost.js +21 -0
- package/dist/server/operatorResults.d.ts +13 -0
- package/dist/server/operatorResults.js +22 -0
- package/dist/tools/emitStageEnvelope.d.ts +11 -2
- package/dist/tools/emitStageEnvelope.js +22 -6
- package/dist/types/envelope.d.ts +2 -0
- package/dist/types/forkChoice.d.ts +22 -0
- package/dist/types/pipeline.d.ts +8 -0
- package/dist/ui/assets/{index-DefBlEvN.css → index-C3N7MXAC.css} +1 -1
- package/dist/ui/assets/index-CSoGDA3A.js +118 -0
- package/dist/ui/index.html +2 -2
- package/package.json +2 -1
- package/skills/install-suite.sh +107 -0
- package/skills/stageflow/SKILL.md +26 -0
- package/skills/stageflow/references/control-surface.md +28 -0
- package/skills/stageflow/scripts/detect-host.mjs +54 -0
- package/skills/stageflow/scripts/detect-host.test.mjs +97 -0
- package/skills/stageflow-author/SKILL.md +58 -0
- package/skills/stageflow-author/assets/examples/branch-decision/hotfix.yaml +10 -0
- package/skills/stageflow-author/assets/examples/branch-decision/release-gate.pipeline.yaml +12 -0
- package/skills/stageflow-author/assets/examples/branch-decision/run-tests.yaml +13 -0
- package/skills/stageflow-author/assets/examples/branch-decision/ship.yaml +10 -0
- package/skills/stageflow-author/assets/examples/linear-review/draft.yaml +9 -0
- package/skills/stageflow-author/assets/examples/linear-review/publish.yaml +10 -0
- package/skills/stageflow-author/assets/examples/linear-review/review-loop.pipeline.yaml +10 -0
- package/skills/stageflow-author/assets/examples/linear-review/review.yaml +15 -0
- package/skills/stageflow-author/assets/examples/non-sdlc-digest/gather.yaml +9 -0
- package/skills/stageflow-author/assets/examples/non-sdlc-digest/research-digest.pipeline.yaml +10 -0
- package/skills/stageflow-author/assets/examples/non-sdlc-digest/send.yaml +10 -0
- package/skills/stageflow-author/assets/examples/non-sdlc-digest/summarize.yaml +9 -0
- package/skills/stageflow-author/references/catalog-mapping.md +131 -0
- package/skills/stageflow-author/references/catalog-write-conventions.md +29 -0
- package/skills/stageflow-author/references/stage-prompt-template.md +55 -0
- package/skills/stageflow-author/references/validate-and-report.md +38 -0
- package/skills/stageflow-delegate/SKILL.md +18 -0
- package/skills/stageflow-delegate/references/authoring-or-run.md +15 -0
- package/skills/stageflow-delegate/references/example-walkthrough.md +30 -0
- package/skills/stageflow-delegate/references/pattern-detection.md +44 -0
- package/skills/stageflow-run/SKILL.md +182 -0
- package/skills/stageflow-run/references/mcp-call.md +17 -0
- package/skills/stageflow-run/references/native-question-ui.md +78 -0
- package/skills/stageflow-run/references/task-and-pipeline-selection.md +52 -0
- package/skills/stageflow-run/scripts/mcp-call.mjs +299 -0
- package/skills/stageflow-run/scripts/mcp-call.test.mjs +271 -0
- package/skills/stageflow-session-capture/SKILL.md +94 -0
- package/skills/stageflow-session-capture/assets/example-pipeline/example.pipeline.yaml +7 -0
- package/skills/stageflow-session-capture/assets/example-pipeline/implement.yaml +8 -0
- package/skills/stageflow-session-capture/assets/example-pipeline/research.yaml +8 -0
- package/skills/stageflow-session-capture/references/catalog-authoring.md +40 -0
- package/skills/stageflow-session-capture/references/transcript-sources.md +25 -0
- package/skills/stageflow-session-capture/scripts/check-provider-gate.mjs +43 -0
- package/skills/stageflow-session-capture/scripts/locate-session-transcript.mjs +141 -0
- package/skills/stageflow-session-capture/scripts/resolve-catalog-id.mjs +119 -0
- package/skills/stageflow-setup/SKILL.md +110 -0
- package/dist/ui/assets/index-CFSzDZje.js +0 -118
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# <img src="ui/public/stageflow-icon.svg" alt="" width="44" height="44" valign="middle"> Stageflow
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Open-source runtime for **configurable multi-stage agent workflows** with typed handoffs, DAG execution, human gates, MCP, CI, and a local operator console.
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/stageflow)
|
|
6
6
|
[](https://www.npmjs.com/package/stageflow)
|
|
@@ -9,7 +9,7 @@ CLI pipeline runtime for **configurable stages** on [Pi](https://github.com/badl
|
|
|
9
9
|
[](https://github.com/tejasghutukade/stageflow/issues)
|
|
10
10
|
[](https://github.com/tejasghutukade/stageflow/actions/workflows/ci.yml)
|
|
11
11
|
|
|
12
|
-
Author pipeline-owned YAML in your project. Stageflow
|
|
12
|
+
Author pipeline-owned YAML in your project. Stageflow schedules each stage in a fresh agent session, moves context through explicit envelopes and artifacts, and persists run state under `.stageflow/`. [Pi](https://github.com/badlogic/pi-mono) is the current agent execution backend. Bins: **`sf`** and **`stageflow`**.
|
|
13
13
|
|
|
14
14
|
## Why Stageflow?
|
|
15
15
|
|
|
@@ -21,7 +21,7 @@ The same pipeline runs three ways without rewriting anything:
|
|
|
21
21
|
|
|
22
22
|
- **Locally** — `sf ui` for triage, provider setup, and gate replies
|
|
23
23
|
- **Headless / CI** — `sf validate` and `sf run --json` with predictable exit codes
|
|
24
|
-
- **Via MCP** — Streamable HTTP tools when
|
|
24
|
+
- **Via MCP** — Streamable HTTP tools when `sf ui` or `sf mcp` is running
|
|
25
25
|
|
|
26
26
|
**Stageflow is not an SDLC tool.** Software delivery is a popular pattern in fixtures and dogfood flows, but stages are user-authored and domain-agnostic. If you can express a multi-step workflow in YAML, Stageflow can run it on Pi.
|
|
27
27
|
|
|
@@ -33,11 +33,20 @@ The same pipeline runs three ways without rewriting anything:
|
|
|
33
33
|
- **Envelope handoffs** — typed stage payloads and artifacts via `write_stage_artifact` / `emit_stage_envelope`
|
|
34
34
|
- **HITL gates** — operator questions in the console; CI exits `2` when a run is waiting
|
|
35
35
|
- **Operator console** — triage runs, connect providers, answer gates, inspect transcripts at `http://127.0.0.1:3847`
|
|
36
|
-
- **MCP endpoint** — Streamable HTTP at `/mcp` when `sf ui` is running
|
|
36
|
+
- **MCP endpoint** — Streamable HTTP at `/mcp` when `sf ui` or `sf mcp` is running
|
|
37
37
|
- **CI / headless** — `sf validate --strict --json`, `sf run --json` with exit codes `0` / `1` / `2`
|
|
38
38
|
- **Parallel stages** — pipeline DAG with fan-out and join (see [YAML catalog](docs/yaml-catalog.md))
|
|
39
|
+
- **Clonable fan-out** — clone one successor N times at completion, then join (see [YAML catalog](docs/yaml-catalog.md#clonable-successors))
|
|
39
40
|
- **SQLite run store** — `<git-root>/.stageflow/` state plus per-run workspaces under `.stageflow/runs/`
|
|
40
41
|
|
|
42
|
+
## Architecture at a glance
|
|
43
|
+
|
|
44
|
+

|
|
45
|
+
|
|
46
|
+
The scheduler owns orchestration semantics: DAG readiness, bounded parallelism, fan-out/join, conditional routing, retries, skipped branches, and resumable human gates. Agent execution sits behind `AgentPort`; Pi supplies the current coding-agent session, while Stageflow owns pipeline state, stage workspaces, handoff validation, and the interfaces used by the CLI, console, MCP, and CI.
|
|
47
|
+
|
|
48
|
+
See [Architecture](docs/architecture.md) for component boundaries, execution flow, persistence, recovery behavior, and the tradeoffs behind fresh sessions and explicit handoffs.
|
|
49
|
+
|
|
41
50
|
## Installation
|
|
42
51
|
|
|
43
52
|
Requires **Node.js ≥ 20**.
|
|
@@ -60,6 +69,14 @@ npm i -g ./stageflow-*.tgz
|
|
|
60
69
|
|
|
61
70
|
`better-sqlite3` ships prebuilds for common platforms. `--ignore-scripts` is fine when a prebuild exists. Benign `node-gyp` warnings during install can be ignored if `require("better-sqlite3")` works.
|
|
62
71
|
|
|
72
|
+
**Harness skills** (Cursor, Claude Code, Codex, Pi, OpenCode) — from a consumer project:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
npx skills add tejasghutukade/stageflow
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Then ask the agent to set up Stageflow. Details: [docs/skills-suite.md](docs/skills-suite.md).
|
|
79
|
+
|
|
63
80
|
## Quick start
|
|
64
81
|
|
|
65
82
|
In a project directory (preferably a git repo):
|
|
@@ -108,7 +125,7 @@ sf providers detect
|
|
|
108
125
|
sf providers source set pi_home # or sf_owned
|
|
109
126
|
```
|
|
110
127
|
|
|
111
|
-
|
|
128
|
+
Pi is Stageflow's current agent execution backend. Stageflow owns the workflow layer around it, and you do not need Pi CLI `/login` as a prerequisite when providers are configured through the console or `sf providers`.
|
|
112
129
|
|
|
113
130
|
Full reference: [docs/providers.md](docs/providers.md)
|
|
114
131
|
|
|
@@ -117,12 +134,12 @@ Full reference: [docs/providers.md](docs/providers.md)
|
|
|
117
134
|
Start the console with `sf ui` (default `http://127.0.0.1:3847`).
|
|
118
135
|
|
|
119
136
|
- **Runs** — active and recent pipeline runs, capacity, and status at a glance
|
|
120
|
-
- **Run detail** — stage
|
|
137
|
+
- **Run detail** — spatial stage map; select a stage for the gated workspace (logs, files, envelopes, HITL). Created runs show **not started** with a **Start run** action until history exists
|
|
121
138
|
- **HITL reply** — answer operator gates (`ask_operator`) without leaving the browser
|
|
122
139
|
- **Pipelines** — browse manifest-declared pipeline definitions
|
|
123
140
|
- **Settings → Providers** — connect model providers (`pi_home` or `sf_owned` credential storage)
|
|
124
141
|
|
|
125
|
-
|
|
142
|
+
Brand assets live under `docs/img/` (`stageflow-og.svg`, `stageflow-icon.svg`).
|
|
126
143
|
|
|
127
144
|
## Headless / CI
|
|
128
145
|
|
|
@@ -132,7 +149,7 @@ The guest actor is the CLI (`sf` / `stageflow`). `sf ui` and MCP are not require
|
|
|
132
149
|
sf validate --strict --json
|
|
133
150
|
```
|
|
134
151
|
|
|
135
|
-
Validate exits `0` or `1` only (no waiting / `2`).
|
|
152
|
+
Validate exits `0` or `1` only (no waiting / `2`). With no flags, `sf validate` checks pipelines and tasks in the manifest (plus stages). `--pipeline` validates that pipeline and its stages only; `--task` validates that task file. It never proves provider auth or checkout paths.
|
|
136
153
|
|
|
137
154
|
```bash
|
|
138
155
|
sf providers login <providerId> --api-key-env <VAR>
|
|
@@ -155,17 +172,25 @@ The process exits `0` when the Run succeeded, `1` when it failed (including a bu
|
|
|
155
172
|
|
|
156
173
|
On a mixed Pipeline, default wait parks the Run (exit `2`). `--skip-gates` fails the Stage (exit `1`). A Pipeline with no HITL does not need the flag.
|
|
157
174
|
|
|
175
|
+
Post-run extraction (dogfooded in [Archify PR diagrams](examples/archify-on-pr/)):
|
|
176
|
+
|
|
177
|
+
```bash
|
|
178
|
+
sf run ... --json --include stages > sf-run.json
|
|
179
|
+
sf envelope get --from sf-run.json --stage author-diagrams --format handoff --json
|
|
180
|
+
sf skills install --from-zip <url> --skill-name archify
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
See [docs/ci.md](docs/ci.md) for the full CI recipe and [`.github/actions/sf-run`](.github/actions/sf-run) composite action.
|
|
184
|
+
|
|
158
185
|
## State
|
|
159
186
|
|
|
160
187
|
Runtime state lives in **`<git-root>/.stageflow/`** when inside a git repository (SQLite + per-run workspaces under `.stageflow/runs/`). Global config and `sf_owned` auth live under **`~/.stageflow/`**. If `.stageflow` is missing and `.software-factory` exists from an older install, the next store open renames it to `.stageflow` once.
|
|
161
188
|
|
|
162
189
|
## MCP
|
|
163
190
|
|
|
164
|
-
`sf ui`
|
|
165
|
-
|
|
166
|
-
Available tools: `list_pipelines`, `list_tasks`, `list_runs`, `get_health`, `start_run`, `get_run`, `read_artifact`.
|
|
191
|
+
Host MCP via `sf ui` or `sf mcp` at `http://127.0.0.1:3847/mcp` (URL printed on boot). Sessions are the default. Point a Cursor (or other) MCP client at that URL. Do not run both hosts against the same store.
|
|
167
192
|
|
|
168
|
-
Full
|
|
193
|
+
HITL-aware tools include `wait_run`, `answer_gate`, and `list_waiting`. Full tool list: [docs/mcp.md](docs/mcp.md).
|
|
169
194
|
|
|
170
195
|
## Stageflow vs Conductor
|
|
171
196
|
|
|
@@ -177,7 +202,7 @@ Both projects address multi-step agent workflows. They differ in orchestration m
|
|
|
177
202
|
| **Orchestration** | Pipeline DAG + stage worker | Jinja routing, no LLM in router |
|
|
178
203
|
| **Unit of work** | Task → Pipeline → Stage attempts | Workflow → Agents |
|
|
179
204
|
| **Handoff** | Typed **envelope** + artifacts | Agent output → context |
|
|
180
|
-
| **Human gates** |
|
|
205
|
+
| **Human gates** | Console + MCP + harness native question UI | Dashboard + TUI fleet |
|
|
181
206
|
| **Runtime** | Node.js, Pi coding agent | Python, Copilot/Claude SDKs |
|
|
182
207
|
| **Best for** | Personal/team **multi-stage Pi workflows** you define (releases, research, SDLC, …) | Enterprise multi-agent workflows |
|
|
183
208
|
|
|
@@ -187,8 +212,11 @@ If you want deterministic YAML routing across many agents, look at [Conductor](h
|
|
|
187
212
|
|
|
188
213
|
| Example | Description |
|
|
189
214
|
|---------|-------------|
|
|
215
|
+
| **[archify-on-pr](examples/archify-on-pr/)** | **Featured** — PR diagram automation: conditional fork, skill binding, envelope handoff, GHA deliver |
|
|
190
216
|
| [hello-world](examples/hello-world/) | Single stage, domain-neutral |
|
|
191
217
|
| [plan-review](examples/plan-review/) | Multi-stage with operator gate — SDLC-style **example** |
|
|
218
|
+
| [conditional-fork](examples/conditional-fork/) | Exclusive fork routing with operator branch choice |
|
|
219
|
+
| [clonable-fanout](examples/clonable-fanout/) | Clone one successor N times, then join |
|
|
192
220
|
| [github-release](examples/github-release/) | Dogfood: draft + publish GitHub Release |
|
|
193
221
|
| [ci-validate](examples/ci-validate/) | Strict validate in CI |
|
|
194
222
|
|
|
@@ -201,15 +229,17 @@ Full docs: **[tejasghutukade.github.io/stageflow](https://tejasghutukade.github.
|
|
|
201
229
|
| Doc | Description |
|
|
202
230
|
|-----|-------------|
|
|
203
231
|
| [docs/README.md](docs/README.md) | Documentation index |
|
|
232
|
+
| [docs/architecture.md](docs/architecture.md) | Runtime components, execution flow, persistence, and design decisions |
|
|
204
233
|
| [docs/quickstart.md](docs/quickstart.md) | Expanded quick start |
|
|
205
234
|
| [docs/yaml-catalog.md](docs/yaml-catalog.md) | Pipelines, stages, tasks schema |
|
|
206
|
-
| [docs/cli-reference.md](docs/cli-reference.md) | `sf run`, `sf validate`, `sf ui`, `sf providers` |
|
|
235
|
+
| [docs/cli-reference.md](docs/cli-reference.md) | `sf init`, `sf run`, `sf validate`, `sf ui`, `sf mcp`, `sf envelope`, `sf export-run`, `sf artifact`, `sf skills`, `sf providers` |
|
|
207
236
|
| [docs/envelopes.md](docs/envelopes.md) | Handoff envelope contract |
|
|
208
237
|
| [docs/hitl.md](docs/hitl.md) | Gate kinds, `--skip-gates`, exit code `2` |
|
|
209
238
|
| [docs/ci.md](docs/ci.md) | `--json`, env vars, GitHub Actions |
|
|
210
239
|
| [docs/mcp.md](docs/mcp.md) | MCP tool reference |
|
|
211
240
|
| [docs/providers.md](docs/providers.md) | Pi providers, `sf providers` |
|
|
212
241
|
| [docs/operator-console.md](docs/operator-console.md) | Console IA and settings |
|
|
242
|
+
| [docs/skills-suite.md](docs/skills-suite.md) | Harness skills — router + jobs for Cursor, Claude Code, Codex, Pi, OpenCode |
|
|
213
243
|
| [docs/compare-conductor.md](docs/compare-conductor.md) | Positioning deep dive |
|
|
214
244
|
|
|
215
245
|
## Develop from source
|
package/dist/agent/activity.d.ts
CHANGED
|
@@ -68,6 +68,8 @@ export type StageLifecycleEvent = {
|
|
|
68
68
|
} | {
|
|
69
69
|
event: "failed";
|
|
70
70
|
reason: string;
|
|
71
|
+
} | {
|
|
72
|
+
event: "skipped";
|
|
71
73
|
};
|
|
72
74
|
export type StageLogLine = StageActivityEvent | StageLifecycleEvent;
|
|
73
75
|
export declare function truncateActivityText(value: unknown, limit?: number): string | undefined;
|
|
@@ -1,14 +1,5 @@
|
|
|
1
1
|
import { type StageProviderSupport } from "./providerSupport.js";
|
|
2
|
-
|
|
3
|
-
* Locate the pi-cursor-sdk extension entry without opening full global package
|
|
4
|
-
* discovery. Stages stay sealed; only this allowlisted path is loaded.
|
|
5
|
-
*
|
|
6
|
-
* Resolution order:
|
|
7
|
-
* 1. STAGEFLOW_CURSOR_EXTENSION (absolute path to the extension .ts/.js)
|
|
8
|
-
* 2. Path package from ~/.pi/agent/settings.json (same source interactive pi uses)
|
|
9
|
-
* 3. npm install under ~/.pi/agent/npm/node_modules/pi-cursor-sdk
|
|
10
|
-
* 4. Sibling checkout at ../pi-cursor-sdk relative to this repo
|
|
11
|
-
*/
|
|
2
|
+
export declare function cursorExtensionEntryInPackage(packageRoot: string): string | undefined;
|
|
12
3
|
export declare function resolveCursorExtensionPath(): string | undefined;
|
|
13
4
|
export declare function isCursorModelRef(modelRef: string): boolean;
|
|
14
5
|
export declare const cursorProviderSupport: StageProviderSupport;
|
|
@@ -18,8 +18,19 @@ const CURSOR_SETTING_SOURCES_ENV = "PI_CURSOR_SETTING_SOURCES";
|
|
|
18
18
|
* 1. STAGEFLOW_CURSOR_EXTENSION (absolute path to the extension .ts/.js)
|
|
19
19
|
* 2. Path package from ~/.pi/agent/settings.json (same source interactive pi uses)
|
|
20
20
|
* 3. npm install under ~/.pi/agent/npm/node_modules/pi-cursor-sdk
|
|
21
|
+
* (`dist/index.js` for 0.3+, `src/index.ts` for older publishes)
|
|
21
22
|
* 4. Sibling checkout at ../pi-cursor-sdk relative to this repo
|
|
22
23
|
*/
|
|
24
|
+
const CURSOR_PACKAGE_ENTRIES = ["dist/index.js", "src/index.ts"];
|
|
25
|
+
export function cursorExtensionEntryInPackage(packageRoot) {
|
|
26
|
+
for (const rel of CURSOR_PACKAGE_ENTRIES) {
|
|
27
|
+
const full = path.join(packageRoot, rel);
|
|
28
|
+
if (existsSync(full)) {
|
|
29
|
+
return full;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return undefined;
|
|
33
|
+
}
|
|
23
34
|
export function resolveCursorExtensionPath() {
|
|
24
35
|
const fromEnv = process.env.STAGEFLOW_CURSOR_EXTENSION?.trim();
|
|
25
36
|
if (fromEnv && existsSync(fromEnv)) {
|
|
@@ -29,16 +40,13 @@ export function resolveCursorExtensionPath() {
|
|
|
29
40
|
if (fromSettings) {
|
|
30
41
|
return fromSettings;
|
|
31
42
|
}
|
|
32
|
-
const npmEntry = path.join(os.homedir(), ".pi", "agent", "npm", "node_modules", "pi-cursor-sdk"
|
|
33
|
-
if (
|
|
43
|
+
const npmEntry = cursorExtensionEntryInPackage(path.join(os.homedir(), ".pi", "agent", "npm", "node_modules", "pi-cursor-sdk"));
|
|
44
|
+
if (npmEntry) {
|
|
34
45
|
return npmEntry;
|
|
35
46
|
}
|
|
36
47
|
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
return sibling;
|
|
40
|
-
}
|
|
41
|
-
return undefined;
|
|
48
|
+
const siblingRoot = path.resolve(here, "../../../pi-cursor-sdk");
|
|
49
|
+
return cursorExtensionEntryInPackage(siblingRoot);
|
|
42
50
|
}
|
|
43
51
|
export function isCursorModelRef(modelRef) {
|
|
44
52
|
const slash = modelRef.indexOf("/");
|
|
@@ -60,7 +68,7 @@ function missingExtensionReason(modelRef) {
|
|
|
60
68
|
return [
|
|
61
69
|
`Model "${modelRef}" requires pi-cursor-sdk, but no extension entry was found.`,
|
|
62
70
|
"Install with `pi install npm:pi-cursor-sdk`, or set STAGEFLOW_CURSOR_EXTENSION",
|
|
63
|
-
"to the absolute path of pi-cursor-sdk/src/index.ts.",
|
|
71
|
+
"to the absolute path of pi-cursor-sdk/dist/index.js (or src/index.ts).",
|
|
64
72
|
"Also ensure a Cursor SDK API key is available via Pi /login or CURSOR_API_KEY.",
|
|
65
73
|
].join(" ");
|
|
66
74
|
}
|
|
@@ -130,8 +138,8 @@ function resolveFromPiSettings() {
|
|
|
130
138
|
}
|
|
131
139
|
}
|
|
132
140
|
}
|
|
133
|
-
const declared =
|
|
134
|
-
if (
|
|
141
|
+
const declared = cursorExtensionEntryInPackage(packageRoot);
|
|
142
|
+
if (declared) {
|
|
135
143
|
return declared;
|
|
136
144
|
}
|
|
137
145
|
}
|
package/dist/agent/fakeAgent.js
CHANGED
|
@@ -2,10 +2,11 @@ import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from "
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { assertRequiredEnvelope, isAdvancingEnvelope, } from "../envelope/check.js";
|
|
4
4
|
import { assertEnvelopePayload } from "../envelope/payloadSchema.js";
|
|
5
|
+
import { assertCloneForks } from "../envelope/cloneForks.js";
|
|
5
6
|
import { assertForkEnvelope } from "../envelope/forkChoice.js";
|
|
6
7
|
import { assertAnswerMatchesPrompt, parseAskOperatorAnswer, } from "../tools/askOperator.js";
|
|
7
8
|
import { tryParsePendingPrompt } from "../runtime/stageHitl.js";
|
|
8
|
-
import { runStageViaOpen } from "./port.js";
|
|
9
|
+
import { runtimeStageId, runStageViaOpen } from "./port.js";
|
|
9
10
|
function opaqueEqual(a, b) {
|
|
10
11
|
if (Object.is(a, b))
|
|
11
12
|
return true;
|
|
@@ -83,7 +84,7 @@ export class FakeAgent {
|
|
|
83
84
|
return answer;
|
|
84
85
|
};
|
|
85
86
|
if (behavior.type === "wait_then_emit") {
|
|
86
|
-
const loaded = loadFakeHitlResume(input.roots, input
|
|
87
|
+
const loaded = loadFakeHitlResume(input.roots, runtimeStageId(input));
|
|
87
88
|
if (loaded === "corrupt") {
|
|
88
89
|
resumeCorrupt = true;
|
|
89
90
|
}
|
|
@@ -116,7 +117,7 @@ export class FakeAgent {
|
|
|
116
117
|
});
|
|
117
118
|
};
|
|
118
119
|
const finishEmit = () => {
|
|
119
|
-
clearFakeHitlResume(input.roots, input
|
|
120
|
+
clearFakeHitlResume(input.roots, runtimeStageId(input));
|
|
120
121
|
if (behavior.type === "throw") {
|
|
121
122
|
input.onActivity?.({ event: "agent_end" });
|
|
122
123
|
return {
|
|
@@ -139,6 +140,9 @@ export class FakeAgent {
|
|
|
139
140
|
if (input.forkEmitContext !== undefined) {
|
|
140
141
|
assertForkEnvelope(envelope, input.forkEmitContext);
|
|
141
142
|
}
|
|
143
|
+
if (input.cloneEmitContext !== undefined) {
|
|
144
|
+
assertCloneForks(envelope, input.cloneEmitContext);
|
|
145
|
+
}
|
|
142
146
|
assertEnvelopePayload(envelope, input.stage.payload_schema);
|
|
143
147
|
if (!isAdvancingEnvelope(envelope)) {
|
|
144
148
|
input.onActivity?.({ event: "agent_end" });
|
|
@@ -171,7 +175,7 @@ export class FakeAgent {
|
|
|
171
175
|
}
|
|
172
176
|
};
|
|
173
177
|
return {
|
|
174
|
-
stageId: input
|
|
178
|
+
stageId: runtimeStageId(input),
|
|
175
179
|
async next() {
|
|
176
180
|
if (closed) {
|
|
177
181
|
return {
|
|
@@ -210,7 +214,7 @@ export class FakeAgent {
|
|
|
210
214
|
}
|
|
211
215
|
catch (err) {
|
|
212
216
|
input.onActivity?.({ event: "agent_end" });
|
|
213
|
-
clearFakeHitlResume(input.roots, input
|
|
217
|
+
clearFakeHitlResume(input.roots, runtimeStageId(input));
|
|
214
218
|
return {
|
|
215
219
|
status: "completed",
|
|
216
220
|
result: {
|
|
@@ -227,7 +231,7 @@ export class FakeAgent {
|
|
|
227
231
|
answer !== undefined &&
|
|
228
232
|
!opaqueEqual(answer, expected)) {
|
|
229
233
|
input.onActivity?.({ event: "agent_end" });
|
|
230
|
-
clearFakeHitlResume(input.roots, input
|
|
234
|
+
clearFakeHitlResume(input.roots, runtimeStageId(input));
|
|
231
235
|
return {
|
|
232
236
|
status: "completed",
|
|
233
237
|
result: {
|
|
@@ -241,7 +245,7 @@ export class FakeAgent {
|
|
|
241
245
|
const request = behavior.waitRequests[waitIndex];
|
|
242
246
|
waitIndex += 1;
|
|
243
247
|
armWait();
|
|
244
|
-
writeFakeHitlResume(input.roots, input
|
|
248
|
+
writeFakeHitlResume(input.roots, runtimeStageId(input), {
|
|
245
249
|
waitRequests: behavior.waitRequests,
|
|
246
250
|
expectedAnswers: behavior.expectedAnswers,
|
|
247
251
|
envelope: behavior.envelope,
|
|
@@ -84,6 +84,7 @@ export declare function createSealedResourceLoader(options: {
|
|
|
84
84
|
}): DefaultResourceLoader;
|
|
85
85
|
/** Stable Pi session JSONL path under the run workspace stage dir. */
|
|
86
86
|
export declare function stageSessionFilePath(roots: Pick<StageRoots, "runWorkspaceDir" | "attempt">, stageId: string): string;
|
|
87
|
+
export declare function resolveStageSessionFile(input: StageRunInput): string;
|
|
87
88
|
export declare class StageSessionReconstructError extends Error {
|
|
88
89
|
readonly stageId: string;
|
|
89
90
|
readonly sessionFile: string;
|
|
@@ -97,7 +98,7 @@ export declare class StageSessionReconstructError extends Error {
|
|
|
97
98
|
* Create or reopen the durable stage session file under the run workspace.
|
|
98
99
|
* Touches an empty file first so Pi flushes the session header immediately.
|
|
99
100
|
*/
|
|
100
|
-
export declare function createStageSessionManager(roots: StageRoots, stageId: string): Promise<SessionManager>;
|
|
101
|
+
export declare function createStageSessionManager(roots: StageRoots, stageId: string, sessionFile?: string): Promise<SessionManager>;
|
|
101
102
|
/**
|
|
102
103
|
* Open an existing stage session for post-restart reconstruct.
|
|
103
104
|
* Missing or corrupt files fail closed (KTD7) — never create a fresh session.
|
package/dist/agent/piAdapter.js
CHANGED
|
@@ -43,7 +43,7 @@ import "./cursorProvider.js";
|
|
|
43
43
|
import { findProviderSupport } from "./providerSupport.js";
|
|
44
44
|
import { mapSessionEventToActivity, readActivityVerbose } from "./activity.js";
|
|
45
45
|
import { createStageActivityObserver, } from "./activityObserver.js";
|
|
46
|
-
import { DEFAULT_STAGE_TIMEOUT_MS, runStageViaOpen } from "./port.js";
|
|
46
|
+
import { DEFAULT_STAGE_TIMEOUT_MS, runtimeStageId, runStageViaOpen } from "./port.js";
|
|
47
47
|
/**
|
|
48
48
|
* Stage tool allowlist for sealed Pi sessions.
|
|
49
49
|
* Always includes `ask_operator` (R7); `write_stage_artifact` when registered
|
|
@@ -365,7 +365,7 @@ function buildUserPrompt(input, emitToolName, emitHintOverride, artifactToolName
|
|
|
365
365
|
emitHint += `\nOn status=success, payload is required and must match this JSON Schema:\n${JSON.stringify(input.stage.payload_schema, null, 2)}`;
|
|
366
366
|
}
|
|
367
367
|
const attempt = input.roots.attempt ?? 1;
|
|
368
|
-
const attemptArtifactsPath = `stages/${input
|
|
368
|
+
const attemptArtifactsPath = `stages/${runtimeStageId(input)}/attempts/${attempt}/artifacts/`;
|
|
369
369
|
const skillBaseDir = input.stage.skill !== undefined && input.skillFilePath !== undefined
|
|
370
370
|
? path.dirname(input.skillFilePath)
|
|
371
371
|
: undefined;
|
|
@@ -390,11 +390,11 @@ function buildUserPrompt(input, emitToolName, emitHintOverride, artifactToolName
|
|
|
390
390
|
: `Create factory stage artifacts under ${attemptArtifactsPath} relative to the run folder.`;
|
|
391
391
|
return [
|
|
392
392
|
`Task id: ${input.task.id}`,
|
|
393
|
-
`Stage id: ${input
|
|
393
|
+
`Stage id: ${runtimeStageId(input)}`,
|
|
394
394
|
`Goal: ${input.task.goal}`,
|
|
395
395
|
input.task.context ? `Context: ${input.task.context}` : "",
|
|
396
396
|
input.task.constraints ? `Constraints: ${input.task.constraints}` : "",
|
|
397
|
-
formatPriorEnvelope(input.priorEnvelope),
|
|
397
|
+
formatPriorEnvelope(input.priorEnvelope, input.priorEnvelopes),
|
|
398
398
|
"",
|
|
399
399
|
artifactGuidance,
|
|
400
400
|
emitHint,
|
|
@@ -490,6 +490,10 @@ export function stageSessionFilePath(roots, stageId) {
|
|
|
490
490
|
: noAttemptContext();
|
|
491
491
|
return ctx.sessionPath(roots.runWorkspaceDir, stageId);
|
|
492
492
|
}
|
|
493
|
+
export function resolveStageSessionFile(input) {
|
|
494
|
+
return (input.resumeToken ??
|
|
495
|
+
stageSessionFilePath(input.roots, runtimeStageId(input)));
|
|
496
|
+
}
|
|
493
497
|
export class StageSessionReconstructError extends Error {
|
|
494
498
|
stageId;
|
|
495
499
|
sessionFile;
|
|
@@ -504,8 +508,7 @@ export class StageSessionReconstructError extends Error {
|
|
|
504
508
|
* Create or reopen the durable stage session file under the run workspace.
|
|
505
509
|
* Touches an empty file first so Pi flushes the session header immediately.
|
|
506
510
|
*/
|
|
507
|
-
export async function createStageSessionManager(roots, stageId) {
|
|
508
|
-
const sessionFile = stageSessionFilePath(roots, stageId);
|
|
511
|
+
export async function createStageSessionManager(roots, stageId, sessionFile = stageSessionFilePath(roots, stageId)) {
|
|
509
512
|
await mkdir(path.dirname(sessionFile), { recursive: true });
|
|
510
513
|
if (!(await fileExists(sessionFile))) {
|
|
511
514
|
await writeFile(sessionFile, "");
|
|
@@ -683,7 +686,7 @@ async function prepareStageSessionWiring(input, sessionManager, existingAskWaitC
|
|
|
683
686
|
const provider = findProviderSupport(input.stage.model);
|
|
684
687
|
const capture = {};
|
|
685
688
|
const askWaitChannel = existingAskWaitChannel ?? new AskOperatorWaitChannel();
|
|
686
|
-
const emitDef = createEmitStageEnvelopeTool(capture, input.stage.payload_schema, input.forkEmitContext);
|
|
689
|
+
const emitDef = createEmitStageEnvelopeTool(capture, input.stage.payload_schema, input.forkEmitContext, input.cloneEmitContext);
|
|
687
690
|
const emitTool = defineTool(emitDef);
|
|
688
691
|
const askDef = createAskOperatorTool({
|
|
689
692
|
requestWait: (prompt) => askWaitChannel.requestWait(prompt),
|
|
@@ -691,7 +694,7 @@ async function prepareStageSessionWiring(input, sessionManager, existingAskWaitC
|
|
|
691
694
|
const askTool = defineTool(askDef);
|
|
692
695
|
const artifactDef = createWriteStageArtifactTool({
|
|
693
696
|
runWorkspaceDir: roots.runWorkspaceDir,
|
|
694
|
-
stageId: input
|
|
697
|
+
stageId: runtimeStageId(input),
|
|
695
698
|
attempt: roots.attempt ?? 1,
|
|
696
699
|
});
|
|
697
700
|
const artifactTool = defineTool(artifactDef);
|
|
@@ -788,18 +791,18 @@ function isStageRunResult(value) {
|
|
|
788
791
|
* Does not auto-continue the model turn — U4 owns the yield loop.
|
|
789
792
|
*/
|
|
790
793
|
export async function reconstructStageSessionForAnswer(input, answer) {
|
|
791
|
-
const sessionManager = await openStageSessionManager(input.roots, input
|
|
792
|
-
const sessionFile = ensureStageSessionFlushed(sessionManager, input
|
|
794
|
+
const sessionManager = await openStageSessionManager(input.roots, runtimeStageId(input));
|
|
795
|
+
const sessionFile = ensureStageSessionFlushed(sessionManager, runtimeStageId(input));
|
|
793
796
|
let injection;
|
|
794
797
|
try {
|
|
795
798
|
injection = injectOpaqueAnswerIntoSession(sessionManager, answer);
|
|
796
799
|
}
|
|
797
800
|
catch (err) {
|
|
798
|
-
throw new StageSessionReconstructError(`failed to inject answer into stage session: ${sessionFile}`, { stageId: input
|
|
801
|
+
throw new StageSessionReconstructError(`failed to inject answer into stage session: ${sessionFile}`, { stageId: runtimeStageId(input), sessionFile, cause: err });
|
|
799
802
|
}
|
|
800
803
|
const wiring = await prepareStageSessionWiring(input, sessionManager);
|
|
801
804
|
if (isStageRunResult(wiring)) {
|
|
802
|
-
throw new StageSessionReconstructError(wiring.ok === false ? wiring.reason : "stage session reconstruct failed", { stageId: input
|
|
805
|
+
throw new StageSessionReconstructError(wiring.ok === false ? wiring.reason : "stage session reconstruct failed", { stageId: runtimeStageId(input), sessionFile });
|
|
803
806
|
}
|
|
804
807
|
let session;
|
|
805
808
|
try {
|
|
@@ -820,7 +823,7 @@ export async function reconstructStageSessionForAnswer(input, answer) {
|
|
|
820
823
|
modelRuntime: wiring.modelRuntime,
|
|
821
824
|
});
|
|
822
825
|
if (resolved.error || !resolved.model) {
|
|
823
|
-
throw new StageSessionReconstructError(resolved.error ?? `Model not found: ${input.stage.model}`, { stageId: input
|
|
826
|
+
throw new StageSessionReconstructError(resolved.error ?? `Model not found: ${input.stage.model}`, { stageId: runtimeStageId(input), sessionFile });
|
|
824
827
|
}
|
|
825
828
|
await session.setModel(resolved.model);
|
|
826
829
|
if (resolved.thinkingLevel) {
|
|
@@ -846,7 +849,7 @@ export async function reconstructStageSessionForAnswer(input, answer) {
|
|
|
846
849
|
wiring.restoreProvider?.();
|
|
847
850
|
if (err instanceof StageSessionReconstructError)
|
|
848
851
|
throw err;
|
|
849
|
-
throw new StageSessionReconstructError(`failed to reconstruct stage session: ${err instanceof Error ? err.message : String(err)}`, { stageId: input
|
|
852
|
+
throw new StageSessionReconstructError(`failed to reconstruct stage session: ${err instanceof Error ? err.message : String(err)}`, { stageId: runtimeStageId(input), sessionFile, cause: err });
|
|
850
853
|
}
|
|
851
854
|
}
|
|
852
855
|
async function bindStageSession(input, sessionManager, wiring) {
|
|
@@ -862,7 +865,7 @@ async function bindStageSession(input, sessionManager, wiring) {
|
|
|
862
865
|
settingsManager: wiring.settingsManager,
|
|
863
866
|
});
|
|
864
867
|
const session = created.session;
|
|
865
|
-
ensureStageSessionFlushed(sessionManager, input
|
|
868
|
+
ensureStageSessionFlushed(sessionManager, runtimeStageId(input));
|
|
866
869
|
await session.bindExtensions({});
|
|
867
870
|
const resolved = resolveCliModel({
|
|
868
871
|
cliModel: input.stage.model,
|
|
@@ -897,7 +900,7 @@ export class PiAgentAdapter {
|
|
|
897
900
|
openStage(input) {
|
|
898
901
|
const timeoutMs = input.timeoutMs ?? DEFAULT_STAGE_TIMEOUT_MS;
|
|
899
902
|
const askWaitChannel = new AskOperatorWaitChannel();
|
|
900
|
-
const sessionFile =
|
|
903
|
+
const sessionFile = resolveStageSessionFile(input);
|
|
901
904
|
let sessionManager;
|
|
902
905
|
let resumeWaiting = false;
|
|
903
906
|
if (existsSync(sessionFile)) {
|
|
@@ -906,14 +909,14 @@ export class PiAgentAdapter {
|
|
|
906
909
|
}
|
|
907
910
|
catch (err) {
|
|
908
911
|
throw new StageSessionReconstructError(`stage session file corrupt or unreadable for resume: ${sessionFile}`, {
|
|
909
|
-
stageId: input
|
|
912
|
+
stageId: runtimeStageId(input),
|
|
910
913
|
sessionFile,
|
|
911
914
|
cause: err,
|
|
912
915
|
});
|
|
913
916
|
}
|
|
914
|
-
ensureStageSessionFlushed(sessionManager, input
|
|
917
|
+
ensureStageSessionFlushed(sessionManager, runtimeStageId(input));
|
|
915
918
|
if (!findOpenToolCall(sessionManager)) {
|
|
916
|
-
throw new StageSessionReconstructError(`stage session has no open tool call for resume: ${sessionFile}`, { stageId: input
|
|
919
|
+
throw new StageSessionReconstructError(`stage session has no open tool call for resume: ${sessionFile}`, { stageId: runtimeStageId(input), sessionFile });
|
|
917
920
|
}
|
|
918
921
|
resumeWaiting = true;
|
|
919
922
|
}
|
|
@@ -923,7 +926,7 @@ export class PiAgentAdapter {
|
|
|
923
926
|
let prepareError;
|
|
924
927
|
const preparePromise = (async () => {
|
|
925
928
|
if (!sessionManager) {
|
|
926
|
-
sessionManager = await createStageSessionManager(input.roots, input
|
|
929
|
+
sessionManager = await createStageSessionManager(input.roots, runtimeStageId(input), sessionFile);
|
|
927
930
|
}
|
|
928
931
|
const prepared = await prepareStageSessionWiring(input, sessionManager, askWaitChannel);
|
|
929
932
|
if (isStageRunResult(prepared)) {
|
|
@@ -975,11 +978,11 @@ export class PiAgentAdapter {
|
|
|
975
978
|
}
|
|
976
979
|
};
|
|
977
980
|
return createConnectedAskWaitStageHandle({
|
|
978
|
-
stageId: input
|
|
981
|
+
stageId: runtimeStageId(input),
|
|
979
982
|
askWaitChannel,
|
|
980
983
|
onBeforeWaitYield: () => {
|
|
981
984
|
if (sessionManager) {
|
|
982
|
-
ensureStageSessionFlushed(sessionManager, input
|
|
985
|
+
ensureStageSessionFlushed(sessionManager, runtimeStageId(input));
|
|
983
986
|
}
|
|
984
987
|
},
|
|
985
988
|
run: async () => {
|
|
@@ -992,7 +995,7 @@ export class PiAgentAdapter {
|
|
|
992
995
|
? {
|
|
993
996
|
onDeliver: (answer) => {
|
|
994
997
|
if (!sessionManager) {
|
|
995
|
-
throw new StageSessionReconstructError("stage session missing during resume deliver", { stageId: input
|
|
998
|
+
throw new StageSessionReconstructError("stage session missing during resume deliver", { stageId: runtimeStageId(input), sessionFile });
|
|
996
999
|
}
|
|
997
1000
|
injectOpaqueAnswerIntoSession(sessionManager, answer);
|
|
998
1001
|
if (session) {
|
|
@@ -1002,7 +1005,7 @@ export class PiAgentAdapter {
|
|
|
1002
1005
|
continueRun: async () => {
|
|
1003
1006
|
return runWithTimeout(async () => {
|
|
1004
1007
|
if (!session || !sessionManager) {
|
|
1005
|
-
throw new StageSessionReconstructError("stage session missing during resume continue", { stageId: input
|
|
1008
|
+
throw new StageSessionReconstructError("stage session missing during resume continue", { stageId: runtimeStageId(input), sessionFile });
|
|
1006
1009
|
}
|
|
1007
1010
|
syncAgentMessagesFromSession(session, sessionManager);
|
|
1008
1011
|
await session.agent.continue();
|
|
@@ -1014,7 +1017,7 @@ export class PiAgentAdapter {
|
|
|
1014
1017
|
await preparePromise.catch(() => undefined);
|
|
1015
1018
|
if (closeOptions?.park) {
|
|
1016
1019
|
if (sessionManager) {
|
|
1017
|
-
ensureStageSessionFlushed(sessionManager, input
|
|
1020
|
+
ensureStageSessionFlushed(sessionManager, runtimeStageId(input));
|
|
1018
1021
|
if (askWaitChannel.hasPending) {
|
|
1019
1022
|
await repairPrematureAskOperatorClosure(sessionFile);
|
|
1020
1023
|
}
|
package/dist/agent/port.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { StageEnvelope } from "../types/envelope.js";
|
|
2
|
-
import type { ForkEmitContext } from "../types/forkChoice.js";
|
|
2
|
+
import type { CloneEmitContext, ForkEmitContext } from "../types/forkChoice.js";
|
|
3
3
|
import type { StageConfig } from "../types/stage.js";
|
|
4
4
|
import type { TaskFile } from "../types/task.js";
|
|
5
5
|
import type { StageActivityEvent } from "./activity.js";
|
|
@@ -9,15 +9,20 @@ export type StageResumeToken = string;
|
|
|
9
9
|
export type StageRunInput = {
|
|
10
10
|
roots: StageRoots;
|
|
11
11
|
stage: StageConfig;
|
|
12
|
+
/** Runtime instance id (`work~2`). Defaults to `stage.id` when omitted. */
|
|
13
|
+
stageId?: string;
|
|
12
14
|
task: TaskFile;
|
|
13
15
|
priorEnvelope: StageEnvelope | null;
|
|
16
|
+
priorEnvelopes?: StageEnvelope[];
|
|
14
17
|
timeoutMs?: number;
|
|
15
18
|
resumeToken?: StageResumeToken;
|
|
16
19
|
/** Optional observe hook; HITL wait/answer uses openStage beside this. */
|
|
17
20
|
onActivity?: (event: StageActivityEvent) => void;
|
|
18
21
|
skillFilePath?: string;
|
|
19
22
|
forkEmitContext?: ForkEmitContext;
|
|
23
|
+
cloneEmitContext?: CloneEmitContext;
|
|
20
24
|
};
|
|
25
|
+
export declare function runtimeStageId(input: Pick<StageRunInput, "stage" | "stageId">): string;
|
|
21
26
|
export type StageRunResult = {
|
|
22
27
|
ok: true;
|
|
23
28
|
envelope: StageEnvelope;
|
package/dist/agent/port.js
CHANGED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare const ARTIFACT_USAGE = "Usage:\n sf artifact read --run <runId> --path <relPath> [--out <file>]";
|
|
2
|
+
export type ArtifactCommandIo = {
|
|
3
|
+
log: (line: string) => void;
|
|
4
|
+
error: (line: string) => void;
|
|
5
|
+
};
|
|
6
|
+
export declare function runArtifactCommand(args: string[], options?: {
|
|
7
|
+
cwd?: string;
|
|
8
|
+
projectRoot?: string;
|
|
9
|
+
io?: Partial<ArtifactCommandIo>;
|
|
10
|
+
}): Promise<number>;
|