stageflow 0.36.0 → 0.40.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 +61 -26
- package/dist/agent/claudeAdapter.js +21 -1
- package/dist/agent/claudeTools.d.ts +13 -0
- package/dist/agent/claudeTools.js +98 -0
- package/dist/agent/piAdapter.d.ts +9 -1
- package/dist/agent/piAdapter.js +59 -18
- package/dist/agent/piIsolatedMcp.js +14 -2
- package/dist/agent/piIsolatedMcpProbe.d.ts +5 -1
- package/dist/agent/piIsolatedMcpProbe.js +16 -30
- package/dist/agent/port.d.ts +5 -0
- package/dist/agent/sandboxPiTools.d.ts +99 -0
- package/dist/agent/sandboxPiTools.js +109 -0
- package/dist/agents/agentId.d.ts +8 -0
- package/dist/agents/agentId.js +27 -0
- package/dist/agents/approvalBroker.d.ts +16 -0
- package/dist/agents/approvalBroker.js +45 -0
- package/dist/agents/authorChat.d.ts +19 -0
- package/dist/agents/authorChat.js +63 -0
- package/dist/agents/chatTurn.d.ts +23 -0
- package/dist/agents/chatTurn.js +77 -0
- package/dist/agents/communications/agentmail.d.ts +19 -0
- package/dist/agents/communications/agentmail.js +46 -0
- package/dist/agents/communications/agentmailListener.d.ts +59 -0
- package/dist/agents/communications/agentmailListener.js +183 -0
- package/dist/agents/communications/allowlist.d.ts +10 -0
- package/dist/agents/communications/allowlist.js +36 -0
- package/dist/agents/communications/channelConnections.d.ts +3 -0
- package/dist/agents/communications/channelConnections.js +51 -0
- package/dist/agents/communications/delivery.d.ts +53 -0
- package/dist/agents/communications/delivery.js +142 -0
- package/dist/agents/communications/email.d.ts +26 -0
- package/dist/agents/communications/email.js +20 -0
- package/dist/agents/communications/identity.d.ts +13 -0
- package/dist/agents/communications/identity.js +18 -0
- package/dist/agents/communications/imessageListener.d.ts +41 -0
- package/dist/agents/communications/imessageListener.js +92 -0
- package/dist/agents/communications/inbound.d.ts +63 -0
- package/dist/agents/communications/inbound.js +143 -0
- package/dist/agents/communications/listener.d.ts +36 -0
- package/dist/agents/communications/listener.js +38 -0
- package/dist/agents/communications/photon.d.ts +19 -0
- package/dist/agents/communications/photon.js +38 -0
- package/dist/agents/communications/port.d.ts +27 -0
- package/dist/agents/communications/port.js +6 -0
- package/dist/agents/communications/slack.d.ts +28 -0
- package/dist/agents/communications/slack.js +65 -0
- package/dist/agents/communications/supervisor.d.ts +36 -0
- package/dist/agents/communications/supervisor.js +96 -0
- package/dist/agents/communications/telegram.d.ts +19 -0
- package/dist/agents/communications/telegram.js +52 -0
- package/dist/agents/definitionSummary.d.ts +13 -0
- package/dist/agents/definitionSummary.js +91 -0
- package/dist/agents/delegate.d.ts +3 -0
- package/dist/agents/delegate.js +8 -0
- package/dist/agents/errors.d.ts +10 -0
- package/dist/agents/errors.js +32 -0
- package/dist/agents/guardrails.d.ts +24 -0
- package/dist/agents/guardrails.js +39 -0
- package/dist/agents/historyFit.d.ts +10 -0
- package/dist/agents/historyFit.js +26 -0
- package/dist/agents/hostContext.d.ts +54 -0
- package/dist/agents/hostContext.js +156 -0
- package/dist/agents/ledgers.d.ts +29 -0
- package/dist/agents/ledgers.js +31 -0
- package/dist/agents/lineDiff.d.ts +12 -0
- package/dist/agents/lineDiff.js +46 -0
- package/dist/agents/ndjson.d.ts +9 -0
- package/dist/agents/ndjson.js +47 -0
- package/dist/agents/otlpExport.d.ts +9 -0
- package/dist/agents/otlpExport.js +58 -0
- package/dist/agents/otlpSink.d.ts +7 -0
- package/dist/agents/otlpSink.js +23 -0
- package/dist/agents/policy.d.ts +31 -0
- package/dist/agents/policy.js +84 -0
- package/dist/agents/projectRoot.d.ts +5 -0
- package/dist/agents/projectRoot.js +15 -0
- package/dist/agents/runtime/piRuntime.d.ts +52 -0
- package/dist/agents/runtime/piRuntime.js +474 -0
- package/dist/agents/runtime/types.d.ts +77 -0
- package/dist/agents/runtime/types.js +26 -0
- package/dist/agents/sandbox.d.ts +17 -0
- package/dist/agents/sandbox.js +10 -0
- package/dist/agents/scheduleSource.d.ts +32 -0
- package/dist/agents/scheduleSource.js +100 -0
- package/dist/agents/service.d.ts +298 -0
- package/dist/agents/service.js +1105 -0
- package/dist/agents/sessions.d.ts +38 -0
- package/dist/agents/sessions.js +321 -0
- package/dist/agents/store.d.ts +277 -0
- package/dist/agents/store.js +643 -0
- package/dist/agents/trace.d.ts +27 -0
- package/dist/agents/trace.js +29 -0
- package/dist/agents/turnLifecycle.d.ts +72 -0
- package/dist/agents/turnLifecycle.js +157 -0
- package/dist/agents/webChat/publicChat.d.ts +36 -0
- package/dist/agents/webChat/publicChat.js +290 -0
- package/dist/agents/webChat/rateLimit.d.ts +15 -0
- package/dist/agents/webChat/rateLimit.js +33 -0
- package/dist/agents/webChat/slug.d.ts +2 -0
- package/dist/agents/webChat/slug.js +9 -0
- package/dist/channels/providers/agentmail.js +14 -1
- package/dist/channels/providers/photonImessage.js +36 -0
- package/dist/channels/providers/slack.js +2 -1
- package/dist/channels/providers/telegram.js +2 -1
- package/dist/channels/types.d.ts +27 -0
- package/dist/cli/agentsCommand.d.ts +14 -0
- package/dist/cli/agentsCommand.js +291 -0
- package/dist/cli.js +9 -0
- package/dist/config/browseCatalog.js +4 -11
- package/dist/config/draftPackage.d.ts +1 -0
- package/dist/config/draftPackage.js +4 -0
- package/dist/config/hostConfig.js +9 -0
- package/dist/config/loadAgent.d.ts +266 -0
- package/dist/config/loadAgent.js +374 -0
- package/dist/config/loadPipeline.js +5 -2
- package/dist/config/loadTask.js +34 -1
- package/dist/config/mcpHostCatalog.d.ts +50 -0
- package/dist/config/mcpHostCatalog.js +405 -0
- package/dist/config/mcpOAuth.d.ts +37 -0
- package/dist/config/mcpOAuth.js +263 -0
- package/dist/config/mcpRedaction.d.ts +42 -0
- package/dist/config/mcpRedaction.js +314 -0
- package/dist/config/mcpToolCache.d.ts +20 -0
- package/dist/config/mcpToolCache.js +43 -0
- package/dist/config/mergePipelineIncludes.d.ts +2 -0
- package/dist/config/mergePipelineIncludes.js +25 -0
- package/dist/config/resolveStageMcpServers.d.ts +35 -3
- package/dist/config/resolveStageMcpServers.js +124 -9
- package/dist/config/validateCatalog.d.ts +1 -1
- package/dist/config/validateCatalog.js +22 -1
- package/dist/mcp/agentTools.d.ts +8 -0
- package/dist/mcp/agentTools.js +132 -0
- package/dist/mcp/catalogTools.js +17 -10
- package/dist/mcp/deps.d.ts +6 -0
- package/dist/mcp/pipelineAuthorSupport.d.ts +19 -0
- package/dist/mcp/pipelineAuthorSupport.js +10 -0
- package/dist/mcp/pipelineAuthorTools.d.ts +8 -0
- package/dist/mcp/pipelineAuthorTools.js +109 -0
- package/dist/mcp/server.js +5 -0
- package/dist/mcp/tools.js +4 -0
- package/dist/mcp/triggerTools.js +32 -0
- package/dist/memory/access.d.ts +36 -0
- package/dist/memory/access.js +116 -0
- package/dist/memory/agentIssues.d.ts +3 -0
- package/dist/memory/agentIssues.js +23 -0
- package/dist/memory/bashDeny.d.ts +6 -0
- package/dist/memory/bashDeny.js +35 -0
- package/dist/memory/host.d.ts +51 -0
- package/dist/memory/host.js +394 -0
- package/dist/memory/ids.d.ts +15 -0
- package/dist/memory/ids.js +31 -0
- package/dist/memory/worker.d.ts +1 -0
- package/dist/memory/worker.js +84 -0
- package/dist/models/catalog.d.ts +113 -0
- package/dist/models/catalog.js +238 -0
- package/dist/models/response.d.ts +29 -0
- package/dist/models/response.js +25 -0
- package/dist/operatorAgent/agentAuthorContext.d.ts +17 -0
- package/dist/operatorAgent/agentAuthorContext.js +32 -0
- package/dist/operatorAgent/fakeHost.js +2 -0
- package/dist/operatorAgent/host.js +6 -3
- package/dist/operatorAgent/index.d.ts +4 -0
- package/dist/operatorAgent/index.js +4 -0
- package/dist/operatorAgent/piModel.js +2 -0
- package/dist/operatorAgent/profiles/agentAuthor.d.ts +11 -0
- package/dist/operatorAgent/profiles/agentAuthor.js +192 -0
- package/dist/operatorAgent/profiles/agentAuthorPlaybook.d.ts +1 -0
- package/dist/operatorAgent/profiles/agentAuthorPlaybook.js +29 -0
- package/dist/operatorAgent/profiles/workshopAuthor.js +2 -2
- package/dist/operatorAgent/profiles/workshopAuthorTools.js +3 -3
- package/dist/operatorAgent/proposals.d.ts +1 -0
- package/dist/operatorAgent/proposals.js +23 -2
- package/dist/package-meta.d.ts +1 -1
- package/dist/package-meta.js +1 -1
- package/dist/preflight/pipelinePreflight.js +1 -0
- package/dist/runstore/createStore.d.ts +2 -0
- package/dist/runstore/createStore.js +8 -0
- package/dist/runstore/overviewSummary.d.ts +56 -0
- package/dist/runstore/overviewSummary.js +134 -0
- package/dist/runstore/port.d.ts +3 -0
- package/dist/runstore/sqlite/SqliteRunStore.d.ts +2 -0
- package/dist/runstore/sqlite/SqliteRunStore.js +18 -0
- package/dist/runstore/sqlite/migrations/013-agents.d.ts +12 -0
- package/dist/runstore/sqlite/migrations/013-agents.js +156 -0
- package/dist/runstore/sqlite/migrations/014-agent-deliveries.d.ts +11 -0
- package/dist/runstore/sqlite/migrations/014-agent-deliveries.js +32 -0
- package/dist/runstore/sqlite/migrations/index.js +4 -0
- package/dist/runtime/gitCheckoutCapability.d.ts +1 -0
- package/dist/runtime/gitCheckoutCapability.js +1 -1
- package/dist/runtime/gitWorktreeCheckout.d.ts +22 -0
- package/dist/runtime/gitWorktreeCheckout.js +120 -0
- package/dist/runtime/sandboxContainer.d.ts +62 -0
- package/dist/runtime/sandboxContainer.js +151 -0
- package/dist/runtime/sandboxDesk.d.ts +28 -0
- package/dist/runtime/sandboxDesk.js +242 -0
- package/dist/runtime/sandboxFiles.d.ts +20 -0
- package/dist/runtime/sandboxFiles.js +150 -0
- package/dist/runtime/stageAttemptBootstrap.js +15 -1
- package/dist/runtime/stageEnvironment.d.ts +2 -0
- package/dist/runtime/stageEnvironment.js +8 -1
- package/dist/runtime/stageProcessLauncher.js +23 -0
- package/dist/runtime/stageRoots.d.ts +9 -0
- package/dist/runtime/stageRoots.js +14 -0
- package/dist/runtime/stageRunner.d.ts +5 -0
- package/dist/runtime/stageRunner.js +38 -4
- package/dist/runtime/stageSandbox.d.ts +16 -0
- package/dist/runtime/stageSandbox.js +54 -0
- package/dist/runtime/stageSecrets.d.ts +6 -1
- package/dist/runtime/stageSecrets.js +34 -1
- package/dist/runtime/stageWorker.js +138 -105
- package/dist/runtime/workspaceBinding.d.ts +2 -1
- package/dist/runtime/workspaceBinding.js +20 -4
- package/dist/server/agentAuthorRoutes.d.ts +5 -0
- package/dist/server/agentAuthorRoutes.js +64 -0
- package/dist/server/agentRoutes.d.ts +5 -0
- package/dist/server/agentRoutes.js +323 -0
- package/dist/server/bootstrap.d.ts +8 -0
- package/dist/server/bootstrap.js +36 -10
- package/dist/server/controlToken.js +8 -0
- package/dist/server/http.d.ts +8 -1
- package/dist/server/http.js +172 -20
- package/dist/server/mcpHost.js +1 -0
- package/dist/server/memoryRoutes.d.ts +5 -0
- package/dist/server/memoryRoutes.js +146 -0
- package/dist/server/projectMcpRoutes.d.ts +1 -0
- package/dist/server/projectMcpRoutes.js +241 -3
- package/dist/toolkit/guard.d.ts +27 -0
- package/dist/toolkit/guard.js +98 -0
- package/dist/toolkit/index.d.ts +48 -0
- package/dist/toolkit/index.js +82 -0
- package/dist/toolkit/meta.d.ts +36 -0
- package/dist/toolkit/meta.js +117 -0
- package/dist/toolkit/profiles.d.ts +13 -0
- package/dist/toolkit/profiles.js +25 -0
- package/dist/toolkit/record.d.ts +15 -0
- package/dist/toolkit/record.js +17 -0
- package/dist/tools/memoryTools.d.ts +95 -0
- package/dist/tools/memoryTools.js +179 -0
- package/dist/types/pipeline.d.ts +3 -0
- package/dist/types/stage.d.ts +2 -0
- package/dist/types/task.d.ts +11 -1
- package/dist/ui/assets/AssistantRuntimeProvider-CsiPFhCT.js +9 -0
- package/dist/ui/assets/MarkdownText-D7tYaIFO.js +29 -0
- package/dist/ui/assets/ReadonlyStore-BfXqHykm.js +6 -0
- package/dist/ui/assets/Tooltip-DrpoRRNi.js +1 -0
- package/dist/ui/assets/chat-BbLukq9W.js +5 -0
- package/dist/ui/assets/chat-zIZU_bP1.css +2 -0
- package/dist/ui/assets/jsx-runtime-iVoFAvQ_.js +1 -0
- package/dist/ui/assets/{index-Clv4sJpU.css → main-BFo79krC.css} +1 -1
- package/dist/ui/assets/main-CijYMeM0.js +259 -0
- package/dist/ui/chat.html +25 -0
- package/dist/ui/index.html +6 -2
- package/dist/workshop/modelSettings.d.ts +2 -2
- package/dist/workshop/modelSettings.js +5 -11
- package/examples/README.md +1 -0
- package/examples/channels-smoke/README.md +12 -1
- package/examples/github-release/README.md +1 -1
- package/examples/memory/README.md +101 -0
- package/examples/memory/desk-outage.task.yaml +15 -0
- package/examples/memory/desk-refund.task.yaml +15 -0
- package/examples/memory/desk-revisit-outage.task.yaml +11 -0
- package/examples/memory/desk-revisit.pipeline.yaml +123 -0
- package/examples/memory/desk.pipeline.yaml +192 -0
- package/examples/memory/file-card.pipeline.yaml +39 -0
- package/examples/memory/file-ledger.task.yaml +22 -0
- package/examples/memory/file-release.task.yaml +24 -0
- package/examples/memory/file-rich.pipeline.yaml +40 -0
- package/examples/memory/file-ticket.task.yaml +7 -0
- package/examples/memory/file-timeline.task.yaml +28 -0
- package/examples/memory/find-card.pipeline.yaml +46 -0
- package/examples/memory/find-ticket.task.yaml +7 -0
- package/examples/memory/missing.pipeline.yaml +16 -0
- package/examples/memory/probe-bash.task.yaml +3 -0
- package/examples/memory/recall.pipeline.yaml +24 -0
- package/examples/memory/recall.task.yaml +5 -0
- package/examples/memory/remember-ada.task.yaml +6 -0
- package/examples/memory/remember-grace.task.yaml +6 -0
- package/examples/memory/remember.pipeline.yaml +58 -0
- package/examples/memory/sandbox-memory-bash.pipeline.yaml +28 -0
- package/examples/memory/stageflow.yaml +12 -0
- package/examples/memory/update-card.pipeline.yaml +43 -0
- package/examples/memory/update-ledger.task.yaml +23 -0
- package/examples/memory/update-rich.pipeline.yaml +44 -0
- package/examples/memory/update-ticket.task.yaml +7 -0
- package/examples/ship-feature/README.md +15 -0
- package/examples/stage-mcp/README.md +1 -1
- package/package.json +2 -1
- package/dist/ui/assets/index-BOhqnPNg.js +0 -260
package/README.md
CHANGED
|
@@ -34,8 +34,11 @@ The same pipeline runs three ways without rewriting anything:
|
|
|
34
34
|
- **Envelope handoffs** — typed stage payloads and artifacts via `write_stage_artifact` / `emit_stage_envelope`
|
|
35
35
|
- **Verified stage execution** — after-phase `verify` checks with durable evidence, plus `on_verify_fail` repair or manual operator recovery ([guide](docs/verified-stage-execution.md))
|
|
36
36
|
- **HITL gates** — operator questions in the console; CI exits `2` when a run is waiting
|
|
37
|
-
- **
|
|
37
|
+
- **Agents** — project-owned, revisioned agents (`agents/*.agent.yaml`) with granted tools, ask-first approvals, traces, channels, schedules and a public web chat page ([guide](docs/agents.md))
|
|
38
|
+
- **Operator console** — an Inbox for gates and agent approvals, pipeline runs, agents and their sessions, connections, MCP servers and settings at `http://127.0.0.1:3847` ([guide](docs/operator-console.md))
|
|
38
39
|
- **MCP endpoint** — Streamable HTTP at `/mcp` when `sf ui` or `sf mcp` is running
|
|
40
|
+
- **MCP servers for agents and stages** — add servers, secrets and OAuth sign-in from Settings → Project MCP, or in the project `.mcp.json` ([guide](docs/mcp-servers.md))
|
|
41
|
+
- **Connections** — Slack, Telegram, iMessage, WhatsApp and AgentMail channels, and email accounts, managed from one page ([guide](docs/channels.md))
|
|
39
42
|
- **Standalone stage execution** — run a single stage directly with `run_stage` (MCP tool, `sf run-stage` CLI, or the A2A `run_stage` operation), no pipeline file required; chain calls with `envelope_ref` (one or many prior results) instead of a pipeline DAG
|
|
40
43
|
- **CI / headless** — `sf validate --strict --json`, `sf run --json` with exit codes `0` / `1` / `2`
|
|
41
44
|
- **Parallel stages** — pipeline DAG with fan-out, join, and Clone Chains (one Clone Instance per Clone Array element; see [YAML catalog](docs/yaml-catalog.md#clone-chain))
|
|
@@ -50,9 +53,43 @@ The scheduler owns orchestration semantics: DAG readiness, bounded parallelism,
|
|
|
50
53
|
|
|
51
54
|
See [Architecture](docs/architecture.md) for component boundaries, execution flow, persistence, recovery behavior, and the tradeoffs behind fresh sessions and explicit handoffs.
|
|
52
55
|
|
|
53
|
-
##
|
|
56
|
+
## Get started
|
|
54
57
|
|
|
55
|
-
|
|
58
|
+
### Docker (recommended)
|
|
59
|
+
|
|
60
|
+
Run Stageflow from the published image. It is [`ghcr.io/tejasghutukade/stageflow`](https://github.com/tejasghutukade/stageflow/pkgs/container/stageflow). `0.39` is the current minor tag (`0.39.0` is that exact release).
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
docker run --rm --name stageflow \
|
|
64
|
+
-e STAGEFLOW_ALLOW_UNAUTHENTICATED_BIND=1 \
|
|
65
|
+
-p 127.0.0.1:3847:3847 \
|
|
66
|
+
-v stageflow-data:/data \
|
|
67
|
+
ghcr.io/tejasghutukade/stageflow:0.39 \
|
|
68
|
+
sf ui
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Open [http://127.0.0.1:3847](http://127.0.0.1:3847). Connect a provider under **Settings → Providers**, then **New pipeline** on the Pipelines page. The `stageflow-data` volume keeps that state across runs.
|
|
72
|
+
|
|
73
|
+
The process inside the container binds `0.0.0.0`, which is what Docker port publish needs. The `-p` address keeps the port on this machine, and the allow flag lets it start without a bearer. Do not publish the port on `0.0.0.0` with that flag set. `sf ui` is required because the image command is `sf mcp`.
|
|
74
|
+
|
|
75
|
+
To show the console a project directory:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
docker run --rm --name stageflow \
|
|
79
|
+
-e STAGEFLOW_ALLOW_UNAUTHENTICATED_BIND=1 \
|
|
80
|
+
-e STAGEFLOW_ENSURE_PROJECT_ROOTS=/project \
|
|
81
|
+
-p 127.0.0.1:3847:3847 \
|
|
82
|
+
-v stageflow-data:/data \
|
|
83
|
+
-v "$PWD":/project \
|
|
84
|
+
ghcr.io/tejasghutukade/stageflow:0.39 \
|
|
85
|
+
sf ui
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Building from this checkout, instead of pulling, is [Docker — Local try](docs/docker.md#local-try-compose).
|
|
89
|
+
|
|
90
|
+
### Node
|
|
91
|
+
|
|
92
|
+
Install the `sf` command on the machine when you want it in the shell. Requires **Node.js ≥ 20**.
|
|
56
93
|
|
|
57
94
|
**Quick install (macOS / Linux):**
|
|
58
95
|
|
|
@@ -72,36 +109,27 @@ npm i -g ./stageflow-*.tgz
|
|
|
72
109
|
|
|
73
110
|
`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.
|
|
74
111
|
|
|
75
|
-
**Harness skills** (Cursor, Claude Code, Codex, Pi, OpenCode) — from a consumer project:
|
|
76
|
-
|
|
77
|
-
```bash
|
|
78
|
-
npx skills add tejasghutukade/stageflow
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
Then ask the agent to set up Stageflow. Details: [docs/skills-suite.md](docs/skills-suite.md).
|
|
82
|
-
|
|
83
|
-
## Quick start
|
|
84
|
-
|
|
85
112
|
In a project directory (preferably a git repo):
|
|
86
113
|
|
|
87
114
|
```bash
|
|
88
115
|
sf init
|
|
116
|
+
sf ui
|
|
117
|
+
sf run --pipeline pipelines/hello.pipeline.yaml --task tasks/hello.task.yaml
|
|
89
118
|
```
|
|
90
119
|
|
|
91
|
-
|
|
120
|
+
`sf init` scaffolds `stageflow.yaml`, `pipelines/hello.pipeline.yaml`, and `tasks/hello.task.yaml`. Connect a provider before `sf run` — see below. The console is [http://127.0.0.1:3847](http://127.0.0.1:3847).
|
|
92
121
|
|
|
93
|
-
|
|
122
|
+
**Harness skills** (Cursor, Claude Code, Codex, Pi, OpenCode) — from a consumer project:
|
|
94
123
|
|
|
95
124
|
```bash
|
|
96
|
-
|
|
97
|
-
sf run --pipeline pipelines/hello.pipeline.yaml --task tasks/hello.task.yaml
|
|
125
|
+
npx skills add tejasghutukade/stageflow
|
|
98
126
|
```
|
|
99
127
|
|
|
100
|
-
|
|
128
|
+
Then ask the agent to set up Stageflow. Details: [docs/skills-suite.md](docs/skills-suite.md).
|
|
101
129
|
|
|
102
|
-
|
|
130
|
+
**Workshop** (**New pipeline** on the Pipelines page, or `#/workshop`; it has no rail item) drafts a pipeline in chat and saves it under `workshop/<pipeline-id>/` so it shows up on Run. Guide: [docs/workshop.md](docs/workshop.md).
|
|
103
131
|
|
|
104
|
-
|
|
132
|
+
Expanded walkthrough: [docs/quickstart.md](docs/quickstart.md)
|
|
105
133
|
|
|
106
134
|
## Connect a model provider
|
|
107
135
|
|
|
@@ -140,11 +168,14 @@ Full reference: [docs/providers.md](docs/providers.md)
|
|
|
140
168
|
|
|
141
169
|
Start the console with `sf ui` (default `http://127.0.0.1:3847`).
|
|
142
170
|
|
|
143
|
-
- **
|
|
144
|
-
- **
|
|
145
|
-
- **
|
|
146
|
-
- **Pipelines** — browse
|
|
147
|
-
- **
|
|
171
|
+
- **Inbox** — pipeline gates and agent approvals that wait on you, then failed and finished runs; Approve or Deny without leaving the page, with optional desktop notifications
|
|
172
|
+
- **Pipeline runs** — active and recent runs, capacity, and status at a glance
|
|
173
|
+
- **Run detail** — Timeline, Graph or List of the stages; select a stage for its transcript, events, envelope and artifacts. Created runs show **not started** with a **Start run** action until history exists
|
|
174
|
+
- **Pipelines, Tasks, Catalog, Triggers** — browse and edit the YAML, start runs; **New pipeline** drafts one in the Workshop
|
|
175
|
+
- **Agents** and **Agent sessions** — a directory with each agent's reach (web chat link, channel, schedule), cost and status; create and edit with version history; sessions, approvals and a run inspector
|
|
176
|
+
- **Connections** — channels, email accounts and tool servers
|
|
177
|
+
- **Settings** — Providers (`pi_home` or `sf_owned` credential storage), Concurrency, MCP server, Project MCP (servers, secrets, sign-in) and Notifications
|
|
178
|
+
- **Command palette** — `⌘K` from any page. Page-by-page guide: [docs/operator-console.md](docs/operator-console.md)
|
|
148
179
|
|
|
149
180
|
Brand assets live under `docs/img/` (`stageflow-og.svg`, `stageflow-icon.svg`).
|
|
150
181
|
|
|
@@ -266,7 +297,11 @@ Full docs: **[tejasghutukade.github.io/stageflow](https://tejasghutukade.github.
|
|
|
266
297
|
| [docs/a2a.md](docs/a2a.md) | Publish pipelines for other agents to call over JSON-RPC (A2A), plus the wildcard-access `run_stage` operation |
|
|
267
298
|
| [docs/providers.md](docs/providers.md) | Pi providers, `sf providers` |
|
|
268
299
|
| [docs/docker.md](docs/docker.md) | Docker Compose local try, image pull/run, backup, self-hosting |
|
|
269
|
-
| [docs/operator-console.md](docs/operator-console.md) | Console
|
|
300
|
+
| [docs/operator-console.md](docs/operator-console.md) | Console navigation, command palette, shortcuts and routes; one page per area ([Inbox](docs/console-inbox.md), [Runs](docs/console-runs.md), [Agents](docs/console-agents.md), [Connections](docs/console-connections.md), [Settings](docs/console-settings.md)) |
|
|
301
|
+
| [docs/agents.md](docs/agents.md) | Agents: definition, runtime, approvals, channels, schedules, web chat |
|
|
302
|
+
| [docs/mcp-servers.md](docs/mcp-servers.md) | Add MCP servers, secrets and OAuth; use them from agents and stages |
|
|
303
|
+
| [docs/channels.md](docs/channels.md) | Connections and communication channels |
|
|
304
|
+
| [docs/environment-variables.md](docs/environment-variables.md) | Every `STAGEFLOW_*` environment variable |
|
|
270
305
|
| [docs/workshop.md](docs/workshop.md) | Workshop Author — chat-draft a pipeline, save it, and run it |
|
|
271
306
|
| [docs/skills-suite.md](docs/skills-suite.md) | Harness skills — router + jobs for Cursor, Claude Code, Codex, Pi, OpenCode |
|
|
272
307
|
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
import { query } from "@anthropic-ai/claude-agent-sdk";
|
|
42
42
|
import { isAdvancingEnvelope } from "../envelope/check.js";
|
|
43
43
|
import { assertAnswerMatchesPrompt, parseAskOperatorAnswer, summarizeAnswer, } from "../tools/askOperator.js";
|
|
44
|
-
import { buildStageflowMcpServer, EMIT_STAGE_ENVELOPE_TOOL_NAME, STAGEFLOW_MCP_SERVER_NAME, WRITE_STAGE_ARTIFACT_TOOL_NAME, ASK_OPERATOR_TOOL_NAME, } from "./claudeTools.js";
|
|
44
|
+
import { buildStageflowMcpServer, EMIT_STAGE_ENVELOPE_TOOL_NAME, STAGEFLOW_MCP_SERVER_NAME, WRITE_STAGE_ARTIFACT_TOOL_NAME, ASK_OPERATOR_TOOL_NAME, SANDBOX_BASH_TOOL_NAME, SANDBOX_EDIT_TOOL_NAME, SANDBOX_READ_TOOL_NAME, SANDBOX_WRITE_TOOL_NAME, } from "./claudeTools.js";
|
|
45
45
|
import { createClaudeActivityMapper } from "./claudeActivity.js";
|
|
46
46
|
import { DEFAULT_EMIT_REMINDERS, emitReminderPrompt, formatProviderError } from "./stageTurnDriver.js";
|
|
47
47
|
import { readActivityVerbose } from "./activity.js";
|
|
@@ -84,6 +84,12 @@ function preflight(input) {
|
|
|
84
84
|
reason: "stage declares channels; the claude agent backend does not provide send_message yet, so the stage would run without it",
|
|
85
85
|
};
|
|
86
86
|
}
|
|
87
|
+
if (input.stage.memories?.length) {
|
|
88
|
+
return {
|
|
89
|
+
ok: false,
|
|
90
|
+
reason: "stage pipeline declares memories; the claude agent backend does not provide read_memory, replace_memory, add_memory_card, update_memory_card, read_memory_card, and search_memory_cards yet, so the stage would run without them",
|
|
91
|
+
};
|
|
92
|
+
}
|
|
87
93
|
return resolveClaudeModel(input.stage.model);
|
|
88
94
|
}
|
|
89
95
|
function resultFromCapture(capture, providerError) {
|
|
@@ -209,6 +215,7 @@ async function runTurn(input, model, markerPath, promptText, resumeSessionId, us
|
|
|
209
215
|
input.onActivity?.({ event: "turn_start" });
|
|
210
216
|
let sessionId = resumeSessionId;
|
|
211
217
|
let providerError;
|
|
218
|
+
const containerName = input.roots.containerName;
|
|
212
219
|
try {
|
|
213
220
|
const mcpServer = buildStageflowMcpServer({
|
|
214
221
|
capture: emitCapture,
|
|
@@ -232,6 +239,9 @@ async function runTurn(input, model, markerPath, promptText, resumeSessionId, us
|
|
|
232
239
|
},
|
|
233
240
|
}
|
|
234
241
|
: {}),
|
|
242
|
+
...(containerName !== undefined
|
|
243
|
+
? { sandboxBash: { containerName, dockerBin: input.roots.containerDockerBin } }
|
|
244
|
+
: {}),
|
|
235
245
|
});
|
|
236
246
|
const passedServers = Object.fromEntries(Object.entries(input.resolvedMcpServers ?? {}).map(([name, config]) => [
|
|
237
247
|
name,
|
|
@@ -252,6 +262,16 @@ async function runTurn(input, model, markerPath, promptText, resumeSessionId, us
|
|
|
252
262
|
persistSession: true,
|
|
253
263
|
strictMcpConfig: true,
|
|
254
264
|
...(resumeSessionId !== undefined ? { resume: resumeSessionId } : {}),
|
|
265
|
+
...(containerName !== undefined
|
|
266
|
+
? {
|
|
267
|
+
toolAliases: {
|
|
268
|
+
Bash: SANDBOX_BASH_TOOL_NAME,
|
|
269
|
+
Read: SANDBOX_READ_TOOL_NAME,
|
|
270
|
+
Write: SANDBOX_WRITE_TOOL_NAME,
|
|
271
|
+
Edit: SANDBOX_EDIT_TOOL_NAME,
|
|
272
|
+
},
|
|
273
|
+
}
|
|
274
|
+
: {}),
|
|
255
275
|
},
|
|
256
276
|
});
|
|
257
277
|
for await (const message of stream) {
|
|
@@ -31,10 +31,15 @@ import { type WriteStageArtifactOptions } from "../tools/writeStageArtifact.js";
|
|
|
31
31
|
import { type AskOperatorKind, type AskOperatorPrompt } from "../tools/askOperator.js";
|
|
32
32
|
import type { CloneEmitContext, ForkEmitContext } from "../types/forkChoice.js";
|
|
33
33
|
import type { PreEmitCheckOptions } from "../envelope/preEmitChecks.js";
|
|
34
|
+
import { execInSandboxContainer as defaultExecInSandboxContainer } from "../runtime/sandboxContainer.js";
|
|
34
35
|
export declare const STAGEFLOW_MCP_SERVER_NAME = "stageflow";
|
|
35
36
|
export declare const EMIT_STAGE_ENVELOPE_TOOL_NAME = "mcp__stageflow__emit_stage_envelope";
|
|
36
37
|
export declare const WRITE_STAGE_ARTIFACT_TOOL_NAME = "mcp__stageflow__write_stage_artifact";
|
|
37
38
|
export declare const ASK_OPERATOR_TOOL_NAME = "mcp__stageflow__ask_operator";
|
|
39
|
+
export declare const SANDBOX_BASH_TOOL_NAME = "mcp__stageflow__sandbox_bash";
|
|
40
|
+
export declare const SANDBOX_READ_TOOL_NAME = "mcp__stageflow__sandbox_read";
|
|
41
|
+
export declare const SANDBOX_WRITE_TOOL_NAME = "mcp__stageflow__sandbox_write";
|
|
42
|
+
export declare const SANDBOX_EDIT_TOOL_NAME = "mcp__stageflow__sandbox_edit";
|
|
38
43
|
export type AskOperatorCapture = {
|
|
39
44
|
prompt?: AskOperatorPrompt;
|
|
40
45
|
};
|
|
@@ -72,6 +77,12 @@ export declare const writeStageArtifactShape: {
|
|
|
72
77
|
path: z.ZodString;
|
|
73
78
|
content: z.ZodString;
|
|
74
79
|
};
|
|
80
|
+
type SandboxExecFn = typeof defaultExecInSandboxContainer;
|
|
81
|
+
export type SandboxBashOptions = {
|
|
82
|
+
containerName: string;
|
|
83
|
+
dockerBin?: string;
|
|
84
|
+
execute?: SandboxExecFn;
|
|
85
|
+
};
|
|
75
86
|
export declare function buildStageflowMcpServer(options: {
|
|
76
87
|
capture: EmitCapture;
|
|
77
88
|
payloadSchema?: unknown;
|
|
@@ -83,4 +94,6 @@ export declare function buildStageflowMcpServer(options: {
|
|
|
83
94
|
capture: AskOperatorCapture;
|
|
84
95
|
allowedKinds?: readonly AskOperatorKind[];
|
|
85
96
|
};
|
|
97
|
+
sandboxBash?: SandboxBashOptions;
|
|
86
98
|
}): McpServerConfig;
|
|
99
|
+
export {};
|
|
@@ -29,10 +29,16 @@ import { z } from "zod";
|
|
|
29
29
|
import { createEmitStageEnvelopeTool, } from "../tools/emitStageEnvelope.js";
|
|
30
30
|
import { createWriteStageArtifactTool, } from "../tools/writeStageArtifact.js";
|
|
31
31
|
import { ASK_OPERATOR_KINDS, AskOperatorError, normalizePromptIds, parseAskOperatorParams, } from "../tools/askOperator.js";
|
|
32
|
+
import { execInSandboxContainer as defaultExecInSandboxContainer } from "../runtime/sandboxContainer.js";
|
|
33
|
+
import { editSandboxFile, formatSandboxBashResult, readSandboxFile, sliceSandboxLines, writeSandboxFile, } from "../runtime/sandboxFiles.js";
|
|
32
34
|
export const STAGEFLOW_MCP_SERVER_NAME = "stageflow";
|
|
33
35
|
export const EMIT_STAGE_ENVELOPE_TOOL_NAME = `mcp__${STAGEFLOW_MCP_SERVER_NAME}__emit_stage_envelope`;
|
|
34
36
|
export const WRITE_STAGE_ARTIFACT_TOOL_NAME = `mcp__${STAGEFLOW_MCP_SERVER_NAME}__write_stage_artifact`;
|
|
35
37
|
export const ASK_OPERATOR_TOOL_NAME = `mcp__${STAGEFLOW_MCP_SERVER_NAME}__ask_operator`;
|
|
38
|
+
export const SANDBOX_BASH_TOOL_NAME = `mcp__${STAGEFLOW_MCP_SERVER_NAME}__sandbox_bash`;
|
|
39
|
+
export const SANDBOX_READ_TOOL_NAME = `mcp__${STAGEFLOW_MCP_SERVER_NAME}__sandbox_read`;
|
|
40
|
+
export const SANDBOX_WRITE_TOOL_NAME = `mcp__${STAGEFLOW_MCP_SERVER_NAME}__sandbox_write`;
|
|
41
|
+
export const SANDBOX_EDIT_TOOL_NAME = `mcp__${STAGEFLOW_MCP_SERVER_NAME}__sandbox_edit`;
|
|
36
42
|
/**
|
|
37
43
|
* Exported so a parity test can assert this stays field-for-field aligned
|
|
38
44
|
* with `tools/emitStageEnvelope.ts`'s typebox schema — the two are hand
|
|
@@ -74,6 +80,9 @@ const askOperatorShape = {
|
|
|
74
80
|
questions: z.array(z.record(z.string(), z.unknown())).optional(),
|
|
75
81
|
artifacts: z.array(z.string()).optional(),
|
|
76
82
|
};
|
|
83
|
+
const sandboxBashShape = {
|
|
84
|
+
command: z.string().min(1),
|
|
85
|
+
};
|
|
77
86
|
/** Adapts a `{content, details?, isError?, terminate?}` tool result to plain MCP CallToolResult. */
|
|
78
87
|
function toCallToolResult(result) {
|
|
79
88
|
return {
|
|
@@ -111,6 +120,92 @@ function buildAskOperatorTool(capture, allowedKinds) {
|
|
|
111
120
|
};
|
|
112
121
|
});
|
|
113
122
|
}
|
|
123
|
+
/**
|
|
124
|
+
* When a sandbox container is set, Read/Write/Edit/Bash are aliased onto
|
|
125
|
+
* these tools. They run against the attempt desk at /workspace. Other stage
|
|
126
|
+
* tools stay on the host.
|
|
127
|
+
*/
|
|
128
|
+
function buildSandboxBashTool(options) {
|
|
129
|
+
const execute = options.execute ?? defaultExecInSandboxContainer;
|
|
130
|
+
return tool("sandbox_bash", "Run a shell command inside this stage attempt's sandbox. The working directory is /workspace. The host filesystem is not mounted.", sandboxBashShape, async (args) => {
|
|
131
|
+
let result;
|
|
132
|
+
try {
|
|
133
|
+
result = await execute({
|
|
134
|
+
containerName: options.containerName,
|
|
135
|
+
command: args.command,
|
|
136
|
+
dockerBin: options.dockerBin,
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
catch (err) {
|
|
140
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
141
|
+
return {
|
|
142
|
+
content: [{ type: "text", text: `sandbox_bash could not run: ${message}` }],
|
|
143
|
+
isError: true,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
return {
|
|
147
|
+
content: [{ type: "text", text: formatSandboxBashResult(result) }],
|
|
148
|
+
};
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
const sandboxReadShape = {
|
|
152
|
+
file_path: z.string().min(1),
|
|
153
|
+
offset: z.number().optional(),
|
|
154
|
+
limit: z.number().optional(),
|
|
155
|
+
};
|
|
156
|
+
const sandboxWriteShape = {
|
|
157
|
+
file_path: z.string().min(1),
|
|
158
|
+
content: z.string(),
|
|
159
|
+
};
|
|
160
|
+
const sandboxEditShape = {
|
|
161
|
+
file_path: z.string().min(1),
|
|
162
|
+
old_string: z.string(),
|
|
163
|
+
new_string: z.string(),
|
|
164
|
+
};
|
|
165
|
+
function sandboxFileError(err, label) {
|
|
166
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
167
|
+
return {
|
|
168
|
+
content: [{ type: "text", text: `${label} could not run: ${message}` }],
|
|
169
|
+
isError: true,
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
function buildSandboxReadTool(options) {
|
|
173
|
+
return tool("sandbox_read", "Read a file inside this stage attempt's sandbox. Paths are relative to /workspace or absolute under /workspace. Host paths are rejected.", sandboxReadShape, async (args) => {
|
|
174
|
+
try {
|
|
175
|
+
const text = await readSandboxFile(options, args.file_path);
|
|
176
|
+
return {
|
|
177
|
+
content: [{ type: "text", text: sliceSandboxLines(text, args.offset, args.limit) }],
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
catch (err) {
|
|
181
|
+
return sandboxFileError(err, "sandbox_read");
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
function buildSandboxWriteTool(options) {
|
|
186
|
+
return tool("sandbox_write", "Write a file inside this stage attempt's sandbox. Paths are relative to /workspace or absolute under /workspace. Host paths are rejected.", sandboxWriteShape, async (args) => {
|
|
187
|
+
try {
|
|
188
|
+
await writeSandboxFile(options, args.file_path, args.content);
|
|
189
|
+
return { content: [{ type: "text", text: `Successfully wrote to ${args.file_path}` }] };
|
|
190
|
+
}
|
|
191
|
+
catch (err) {
|
|
192
|
+
return sandboxFileError(err, "sandbox_write");
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
function buildSandboxEditTool(options) {
|
|
197
|
+
return tool("sandbox_edit", "Edit a file inside this stage attempt's sandbox. old_string must match exactly once. Paths stay under /workspace.", sandboxEditShape, async (args) => {
|
|
198
|
+
try {
|
|
199
|
+
await editSandboxFile(options, args.file_path, [
|
|
200
|
+
{ oldText: args.old_string, newText: args.new_string },
|
|
201
|
+
]);
|
|
202
|
+
return { content: [{ type: "text", text: `Successfully edited ${args.file_path}` }] };
|
|
203
|
+
}
|
|
204
|
+
catch (err) {
|
|
205
|
+
return sandboxFileError(err, "sandbox_edit");
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
}
|
|
114
209
|
export function buildStageflowMcpServer(options) {
|
|
115
210
|
const emitDef = createEmitStageEnvelopeTool(options.capture, options.payloadSchema, options.forkEmitContext, options.cloneEmitContext, options.preEmitCheckOptions);
|
|
116
211
|
const artifactDef = createWriteStageArtifactTool(options.writeStageArtifact);
|
|
@@ -123,6 +218,9 @@ export function buildStageflowMcpServer(options) {
|
|
|
123
218
|
if (options.askOperator) {
|
|
124
219
|
tools.push(buildAskOperatorTool(options.askOperator.capture, options.askOperator.allowedKinds));
|
|
125
220
|
}
|
|
221
|
+
if (options.sandboxBash) {
|
|
222
|
+
tools.push(buildSandboxReadTool(options.sandboxBash), buildSandboxWriteTool(options.sandboxBash), buildSandboxEditTool(options.sandboxBash), buildSandboxBashTool(options.sandboxBash));
|
|
223
|
+
}
|
|
126
224
|
return createSdkMcpServer({
|
|
127
225
|
name: STAGEFLOW_MCP_SERVER_NAME,
|
|
128
226
|
tools,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type AgentSession, type ExtensionFactory, SessionManager } from "@earendil-works/pi-coding-agent";
|
|
1
|
+
import { type AgentSession, type ExtensionFactory, DefaultResourceLoader, SessionManager } from "@earendil-works/pi-coding-agent";
|
|
2
2
|
import type { StageRoots } from "../runtime/stageRoots.js";
|
|
3
3
|
import { type AskOperatorPrompt, type AskOperatorWaitBridge } from "../tools/askOperator.js";
|
|
4
4
|
import "./cursorProvider.js";
|
|
@@ -76,6 +76,14 @@ export type RouteSessionEventToProgressOptions = {
|
|
|
76
76
|
export declare function routeSessionEventToProgress(event: Record<string, unknown>, options: RouteSessionEventToProgressOptions): void;
|
|
77
77
|
export declare function composeStageUserPrompt(input: StageRunInput, emitToolName: string, emitHintOverride?: string, artifactToolName?: string): string;
|
|
78
78
|
export declare function composeFeedbackResumePrompt(input: StageRunInput): string;
|
|
79
|
+
export declare function mcpToolMetadataMissing(loader: DefaultResourceLoader): boolean;
|
|
80
|
+
export declare function collectMcpExtensionToolNames(loader: DefaultResourceLoader): {
|
|
81
|
+
ok: true;
|
|
82
|
+
names: string[];
|
|
83
|
+
} | {
|
|
84
|
+
ok: false;
|
|
85
|
+
unrequested: string[];
|
|
86
|
+
};
|
|
79
87
|
/** Stable Pi session JSONL path under the run workspace stage dir. */
|
|
80
88
|
export declare function stageSessionFilePath(roots: Pick<StageRoots, "runWorkspaceDir" | "attempt">, stageId: string): string;
|
|
81
89
|
export declare function resolveStageSessionFile(input: StageRunInput): string;
|
package/dist/agent/piAdapter.js
CHANGED
|
@@ -48,8 +48,11 @@ import { createEmitStageEnvelopeTool, } from "../tools/emitStageEnvelope.js";
|
|
|
48
48
|
import { remindUntilEmitted } from "./stageTurnDriver.js";
|
|
49
49
|
import { createAskOperatorTool, } from "../tools/askOperator.js";
|
|
50
50
|
import { createWriteStageArtifactTool } from "../tools/writeStageArtifact.js";
|
|
51
|
+
import { createSandboxBashTool, createSandboxEditTool, createSandboxReadTool, createSandboxWriteTool, } from "./sandboxPiTools.js";
|
|
51
52
|
import { createSendEmailTool } from "../tools/sendEmail.js";
|
|
52
53
|
import { createSendMessageTool } from "../tools/sendMessage.js";
|
|
54
|
+
import { createMemoryStoreBashDenyExtension } from "../memory/bashDeny.js";
|
|
55
|
+
import { memorySession } from "../tools/memoryTools.js";
|
|
53
56
|
import { createReplyEmailTool } from "../tools/replyEmail.js";
|
|
54
57
|
import { createGetEmailTool, createSearchEmailTool, createDownloadEmailAttachmentTool } from "../tools/readEmail.js";
|
|
55
58
|
import "./cursorProvider.js";
|
|
@@ -460,6 +463,7 @@ function buildUserPrompt(input, emitToolName, emitHintOverride, artifactToolName
|
|
|
460
463
|
}
|
|
461
464
|
const attempt = input.roots.attempt ?? 1;
|
|
462
465
|
const attemptArtifactsPath = `stages/${runtimeStageId(input)}/attempts/${attempt}/artifacts/`;
|
|
466
|
+
const sandboxed = input.roots.containerName !== undefined;
|
|
463
467
|
const skillBaseDirs = [
|
|
464
468
|
input.stage.skill !== undefined && input.skillFilePath !== undefined
|
|
465
469
|
? path.dirname(input.skillFilePath)
|
|
@@ -474,29 +478,45 @@ function buildUserPrompt(input, emitToolName, emitHintOverride, artifactToolName
|
|
|
474
478
|
(input.stage.email?.length ?? 0) > 0
|
|
475
479
|
? `Large tool results are saved automatically as stage artifacts under ${attemptArtifactsPath}${TOOL_OUTPUT_ARTIFACT_DIR}/. The tool result names the path. Read or grep that file; do not copy tool output into ${artifactToolName ?? "an artifact"}.`
|
|
476
480
|
: "";
|
|
477
|
-
const artifactGuidance =
|
|
481
|
+
const artifactGuidance = sandboxed
|
|
478
482
|
? [
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
]
|
|
488
|
-
|
|
489
|
-
.join("\n")
|
|
490
|
-
: artifactToolName !== undefined
|
|
483
|
+
"Sandbox desk: /workspace. read, bash, write, and edit run only inside that desk.",
|
|
484
|
+
input.roots.checkoutRoot
|
|
485
|
+
? `Checkout: /workspace/checkout. The host checkout path is not mounted.`
|
|
486
|
+
: "This stage has no checkout. Scratch files go in /workspace/work.",
|
|
487
|
+
"Attempt artifacts are at /workspace/artifacts. Scratch space is /workspace/work.",
|
|
488
|
+
artifactToolName !== undefined
|
|
489
|
+
? `Create factory stage artifacts only with ${artifactToolName} (path relative to ${attemptArtifactsPath}). Use the returned run-relative path in ${emitToolName}.`
|
|
490
|
+
: `Create factory stage artifacts under ${attemptArtifactsPath} relative to the run folder.`,
|
|
491
|
+
].join("\n")
|
|
492
|
+
: input.roots.mode === "bound" && artifactToolName !== undefined
|
|
491
493
|
? [
|
|
492
|
-
`
|
|
494
|
+
`Project checkout (agent cwd): ${input.roots.checkoutRoot}`,
|
|
495
|
+
`Factory run workspace: ${input.roots.runWorkspaceDir}`,
|
|
496
|
+
`Builtin tools (read, bash, write, edit) are for work inside the project checkout.`,
|
|
497
|
+
skillBaseDir !== undefined
|
|
498
|
+
? `Absolute read and bash against ${skillBaseDir} are in bounds for the linked skill.`
|
|
499
|
+
: undefined,
|
|
500
|
+
`Create factory stage artifacts only with ${artifactToolName} (path relative to ${attemptArtifactsPath}). Use the returned run-relative path in ${emitToolName}.`,
|
|
493
501
|
`This is soft guidance: prefer ${artifactToolName} for factory files under the run workspace; do not rely on relative builtin writes for those files.`,
|
|
494
|
-
]
|
|
495
|
-
|
|
502
|
+
]
|
|
503
|
+
.filter(Boolean)
|
|
504
|
+
.join("\n")
|
|
505
|
+
: artifactToolName !== undefined
|
|
506
|
+
? [
|
|
507
|
+
`Create factory stage artifacts with ${artifactToolName} (path relative to ${attemptArtifactsPath}). Use the returned run-relative path in ${emitToolName}.`,
|
|
508
|
+
`This is soft guidance: prefer ${artifactToolName} for factory files under the run workspace; do not rely on relative builtin writes for those files.`,
|
|
509
|
+
].join("\n")
|
|
510
|
+
: `Create factory stage artifacts under ${attemptArtifactsPath} relative to the run folder.`;
|
|
496
511
|
return [
|
|
497
512
|
...(input.stage.channels?.length ? [
|
|
498
513
|
`Stageflow channels: ${JSON.stringify(input.stage.channels.map(channel => ({ channel: channel.alias ?? channel.connection, operations: channel.operations })))}. Use send_message with the channel name. Recipients are fixed by configuration and you cannot change them. Use a stable operationKey for each intended message. Provider acceptance does not prove delivery. Never resend an unknown submission automatically.`,
|
|
499
514
|
] : []),
|
|
515
|
+
...(input.memoryPrompt !== undefined
|
|
516
|
+
? [input.memoryPrompt]
|
|
517
|
+
: input.memory
|
|
518
|
+
? [memorySession(input.memory).prompt]
|
|
519
|
+
: []),
|
|
500
520
|
...(input.stage.email?.length ? [
|
|
501
521
|
`Stageflow email permissions: ${JSON.stringify(input.stage.email)}. Use only declared accounts and operations. Use a stable operationKey for each intended message. Provider acceptance does not prove delivery. Never resend an unknown submission automatically.`,
|
|
502
522
|
] : []),
|
|
@@ -566,10 +586,10 @@ function mcpExtensionTools(loader) {
|
|
|
566
586
|
.extensions.filter((ext) => ext.path === inlinePath)
|
|
567
587
|
.flatMap((ext) => [...ext.tools.keys()]);
|
|
568
588
|
}
|
|
569
|
-
function mcpToolMetadataMissing(loader) {
|
|
589
|
+
export function mcpToolMetadataMissing(loader) {
|
|
570
590
|
return mcpExtensionTools(loader).includes("mcp");
|
|
571
591
|
}
|
|
572
|
-
function collectMcpExtensionToolNames(loader) {
|
|
592
|
+
export function collectMcpExtensionToolNames(loader) {
|
|
573
593
|
const names = [];
|
|
574
594
|
const unrequested = [];
|
|
575
595
|
for (const name of mcpExtensionTools(loader)) {
|
|
@@ -928,6 +948,12 @@ async function prepareStageSessionWiring(input, sessionManager, existingAskWaitC
|
|
|
928
948
|
: {}),
|
|
929
949
|
}),
|
|
930
950
|
},
|
|
951
|
+
...(input.stage.memories?.length
|
|
952
|
+
? [{
|
|
953
|
+
name: "stageflow-memory-bash-deny",
|
|
954
|
+
factory: createMemoryStoreBashDenyExtension({ home: globalStageflowHome() }),
|
|
955
|
+
}]
|
|
956
|
+
: []),
|
|
931
957
|
{
|
|
932
958
|
name: STAGEFLOW_TOOL_OUTPUT_EXTENSION_NAME,
|
|
933
959
|
factory: createToolOutputBudgetExtension({
|
|
@@ -1007,6 +1033,12 @@ async function prepareStageSessionWiring(input, sessionManager, existingAskWaitC
|
|
|
1007
1033
|
customTools.push(defineTool(definition));
|
|
1008
1034
|
tools.push(definition.name);
|
|
1009
1035
|
}
|
|
1036
|
+
if (input.memory) {
|
|
1037
|
+
for (const definition of memorySession(input.memory).tools) {
|
|
1038
|
+
customTools.push(defineTool(definition));
|
|
1039
|
+
tools.push(definition.name);
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
1010
1042
|
if (input.email) {
|
|
1011
1043
|
const permitted = new Set(input.stage.email?.flatMap(permission => permission.operations));
|
|
1012
1044
|
const definitions = [
|
|
@@ -1022,6 +1054,15 @@ async function prepareStageSessionWiring(input, sessionManager, existingAskWaitC
|
|
|
1022
1054
|
tools.push(definition.name);
|
|
1023
1055
|
}
|
|
1024
1056
|
}
|
|
1057
|
+
if (roots.containerName !== undefined) {
|
|
1058
|
+
const sandboxTools = {
|
|
1059
|
+
containerName: roots.containerName,
|
|
1060
|
+
...(roots.containerDockerBin !== undefined
|
|
1061
|
+
? { dockerBin: roots.containerDockerBin }
|
|
1062
|
+
: {}),
|
|
1063
|
+
};
|
|
1064
|
+
customTools.push(defineTool(createSandboxReadTool(sandboxTools)), defineTool(createSandboxWriteTool(sandboxTools)), defineTool(createSandboxEditTool(sandboxTools)), defineTool(createSandboxBashTool(sandboxTools)));
|
|
1065
|
+
}
|
|
1025
1066
|
return {
|
|
1026
1067
|
sessionManager,
|
|
1027
1068
|
modelRuntime,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
import { createEventBus, } from "@earendil-works/pi-coding-agent";
|
|
3
|
+
import { prepareMcpOAuth } from "../config/mcpOAuth.js";
|
|
3
4
|
import { StageMcpError, } from "../config/resolveStageMcpServers.js";
|
|
4
5
|
import { globalStageflowHome } from "../project/globalHome.js";
|
|
5
6
|
import { TOOL_OUTPUT_HARD_CEILING_BYTES } from "./toolOutputBudget.js";
|
|
@@ -48,7 +49,7 @@ const EMPTY_ATTACH = {
|
|
|
48
49
|
connecting: undefined,
|
|
49
50
|
};
|
|
50
51
|
let cachedCreateMcpAdapter;
|
|
51
|
-
function toIsolatedMcpConfig(snapshot, lifecycle = "lazy") {
|
|
52
|
+
function toIsolatedMcpConfig(snapshot, lifecycle = "lazy", oauth = false) {
|
|
52
53
|
const mcpServers = {};
|
|
53
54
|
for (const [name, entry] of Object.entries(snapshot)) {
|
|
54
55
|
const isolated = {
|
|
@@ -65,6 +66,7 @@ function toIsolatedMcpConfig(snapshot, lifecycle = "lazy") {
|
|
|
65
66
|
mcpServers,
|
|
66
67
|
settings: {
|
|
67
68
|
...ISOLATED_MCP_SETTINGS,
|
|
69
|
+
...(oauth ? { oauthCredentialStore: "encrypted-file" } : {}),
|
|
68
70
|
outputGuard: { ...ISOLATED_MCP_SETTINGS.outputGuard },
|
|
69
71
|
},
|
|
70
72
|
};
|
|
@@ -235,11 +237,21 @@ export async function attachIsolatedMcp(snapshot, options) {
|
|
|
235
237
|
return EMPTY_ATTACH;
|
|
236
238
|
}
|
|
237
239
|
const createMcpAdapter = await loadCreateMcpAdapter();
|
|
240
|
+
let oauth = false;
|
|
241
|
+
if (Object.values(snapshot).some((entry) => typeof entry.url === "string")) {
|
|
242
|
+
try {
|
|
243
|
+
prepareMcpOAuth();
|
|
244
|
+
oauth = true;
|
|
245
|
+
}
|
|
246
|
+
catch {
|
|
247
|
+
// Sign-in is optional; a store that cannot be prepared must not stop servers that need none.
|
|
248
|
+
}
|
|
249
|
+
}
|
|
238
250
|
const extensionFactories = [
|
|
239
251
|
{
|
|
240
252
|
name: STAGEFLOW_PI_MCP_EXTENSION_NAME,
|
|
241
253
|
factory: createMcpAdapter({
|
|
242
|
-
config: toIsolatedMcpConfig(snapshot, options?.lifecycle ?? "lazy"),
|
|
254
|
+
config: toIsolatedMcpConfig(snapshot, options?.lifecycle ?? "lazy", oauth),
|
|
243
255
|
}),
|
|
244
256
|
},
|
|
245
257
|
];
|
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
import { type ResolvedMcpServers } from "../config/resolveStageMcpServers.js";
|
|
2
|
+
import { type McpToolInfo } from "../config/mcpToolCache.js";
|
|
2
3
|
export declare const PROJECT_MCP_PROBE_TIMEOUT_MS = 30000;
|
|
3
4
|
export type ProjectMcpProbeStatus = "connected" | "needs_auth" | "connect_failed" | "unresolved_var" | "invalid_config" | "missing_catalog" | "cancelled";
|
|
4
5
|
export type ProjectMcpProbeResult = {
|
|
5
6
|
name: string;
|
|
6
7
|
status: ProjectMcpProbeStatus;
|
|
7
8
|
error?: string;
|
|
9
|
+
/** Tools the server lists, set when the probe connected. */
|
|
10
|
+
tools?: McpToolInfo[];
|
|
8
11
|
};
|
|
9
12
|
export type ProbeProjectMcpServerOptions = {
|
|
10
13
|
projectRoot: string;
|
|
11
14
|
name: string;
|
|
12
|
-
env
|
|
15
|
+
/** Host env that STAGEFLOW_MCP_ENV_ALLOW may draw from. Defaults to process.env; nothing else is read. */
|
|
16
|
+
hostEnv?: NodeJS.ProcessEnv;
|
|
13
17
|
signal?: AbortSignal;
|
|
14
18
|
timeoutMs?: number;
|
|
15
19
|
};
|