stageflow 0.33.0 → 0.36.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 +3 -3
- package/dist/agent/adapterSpill.d.ts +14 -0
- package/dist/agent/adapterSpill.js +83 -0
- package/dist/agent/agentBackend.d.ts +2 -2
- package/dist/agent/agentBackend.js +2 -2
- package/dist/agent/claudeAdapter.d.ts +13 -2
- package/dist/agent/claudeAdapter.js +56 -6
- package/dist/agent/cursorProvider.d.ts +3 -0
- package/dist/agent/cursorProvider.js +23 -6
- package/dist/agent/fakeAgent.d.ts +3 -3
- package/dist/agent/piAdapter.d.ts +2 -2
- package/dist/agent/piAdapter.js +125 -15
- package/dist/agent/piIsolatedMcp.d.ts +1 -0
- package/dist/agent/piIsolatedMcp.js +19 -1
- package/dist/agent/piIsolatedMcpProbe.d.ts +11 -0
- package/dist/agent/piIsolatedMcpProbe.js +20 -0
- package/dist/agent/port.d.ts +4 -2
- package/dist/agent/providerAuth.js +36 -3
- package/dist/agent/{resolveAgentPort.d.ts → resolveStagePort.d.ts} +8 -8
- package/dist/agent/{resolveAgentPort.js → resolveStagePort.js} +11 -11
- package/dist/agent/stageTurnDriver.d.ts +33 -0
- package/dist/agent/stageTurnDriver.js +64 -0
- package/dist/agent/toolOutputBudget.d.ts +46 -0
- package/dist/agent/toolOutputBudget.js +97 -0
- package/dist/agent/toolOutputBudgetExtension.d.ts +22 -0
- package/dist/agent/toolOutputBudgetExtension.js +141 -0
- package/dist/agent/toolOutputSpill.d.ts +34 -0
- package/dist/agent/toolOutputSpill.js +85 -0
- package/dist/browser/agentBrowserLiveViewRelay.d.ts +41 -0
- package/dist/browser/agentBrowserLiveViewRelay.js +801 -0
- package/dist/browser/anchor.d.ts +2 -0
- package/dist/browser/anchor.js +2 -1
- package/dist/browser/auditSink.d.ts +8 -0
- package/dist/browser/browserHost.d.ts +29 -0
- package/dist/browser/browserSweep.d.ts +4 -1
- package/dist/browser/browserSweep.js +7 -0
- package/dist/browser/browserTeardown.d.ts +1 -0
- package/dist/browser/browserTeardown.js +32 -2
- package/dist/browser/containerBrowserEndpoint.d.ts +11 -0
- package/dist/browser/containerBrowserEndpoint.js +57 -0
- package/dist/browser/containerBrowserHost.d.ts +24 -0
- package/dist/browser/containerBrowserHost.js +147 -0
- package/dist/browser/dockerSandboxOrchestrator.d.ts +26 -0
- package/dist/browser/dockerSandboxOrchestrator.js +142 -0
- package/dist/browser/fakeLiveViewRelay.d.ts +15 -0
- package/dist/browser/fakeLiveViewRelay.js +94 -0
- package/dist/browser/fakeLiveViewSource.d.ts +6 -0
- package/dist/browser/fakeLiveViewSource.js +16 -0
- package/dist/browser/fakeRemoteBrowserHost.d.ts +2 -1
- package/dist/browser/fakeRemoteBrowserHost.js +2 -1
- package/dist/browser/fakeSandboxOrchestrator.d.ts +9 -0
- package/dist/browser/fakeSandboxOrchestrator.js +44 -0
- package/dist/browser/gateHandoff.d.ts +0 -2
- package/dist/browser/gateHandoff.js +5 -11
- package/dist/browser/hostCapabilities.d.ts +23 -0
- package/dist/browser/hostCapabilities.js +23 -0
- package/dist/browser/humanLogin.d.ts +2 -2
- package/dist/browser/humanLogin.js +11 -2
- package/dist/browser/liveViewPersisted.d.ts +11 -0
- package/dist/browser/liveViewPersisted.js +22 -0
- package/dist/browser/liveViewRelay.d.ts +76 -0
- package/dist/browser/liveViewRelay.js +1 -0
- package/dist/browser/liveViewSessions.d.ts +25 -0
- package/dist/browser/liveViewSessions.js +139 -0
- package/dist/browser/liveViewSource.d.ts +25 -0
- package/dist/browser/liveViewSource.js +7 -0
- package/dist/browser/liveViewTickets.d.ts +46 -0
- package/dist/browser/liveViewTickets.js +122 -0
- package/dist/browser/localBrowserHost.d.ts +10 -0
- package/dist/browser/localBrowserHost.js +53 -7
- package/dist/browser/ownerScope.d.ts +10 -0
- package/dist/browser/ownerScope.js +6 -0
- package/dist/browser/profileStore.d.ts +5 -2
- package/dist/browser/profileStore.js +3 -5
- package/dist/browser/sandboxOrchestrator.d.ts +60 -0
- package/dist/browser/sandboxOrchestrator.js +8 -0
- package/dist/browser/sessionApiSandboxOrchestrator.d.ts +34 -0
- package/dist/browser/sessionApiSandboxOrchestrator.js +108 -0
- package/dist/browser/stageBrowserEnv.d.ts +7 -0
- package/dist/browser/stageBrowserEnv.js +53 -15
- package/dist/browser/stageHandoff.d.ts +47 -0
- package/dist/browser/stageHandoff.js +101 -0
- package/dist/browser/stageProfileLock.d.ts +1 -0
- package/dist/browser/stageProfileLock.js +1 -3
- package/dist/browser/volumeProfileStore.d.ts +7 -0
- package/dist/browser/volumeProfileStore.js +35 -0
- package/dist/channels/config.d.ts +11 -0
- package/dist/channels/config.js +95 -0
- package/dist/channels/configWriter.d.ts +40 -0
- package/dist/channels/configWriter.js +148 -0
- package/dist/channels/connectionEdits.d.ts +34 -0
- package/dist/channels/connectionEdits.js +53 -0
- package/dist/channels/errorInfo.d.ts +18 -0
- package/dist/channels/errorInfo.js +71 -0
- package/dist/channels/errors.d.ts +12 -0
- package/dist/channels/errors.js +13 -0
- package/dist/channels/health.d.ts +41 -0
- package/dist/channels/health.js +99 -0
- package/dist/channels/host.d.ts +55 -0
- package/dist/channels/host.js +185 -0
- package/dist/channels/http.d.ts +31 -0
- package/dist/channels/http.js +79 -0
- package/dist/channels/index.d.ts +10 -0
- package/dist/channels/index.js +9 -0
- package/dist/channels/ledger.d.ts +37 -0
- package/dist/channels/ledger.js +95 -0
- package/dist/channels/providers/agentmail.d.ts +26 -0
- package/dist/channels/providers/agentmail.js +81 -0
- package/dist/channels/providers/kit.d.ts +40 -0
- package/dist/channels/providers/kit.js +78 -0
- package/dist/channels/providers/photonImessage.d.ts +45 -0
- package/dist/channels/providers/photonImessage.js +152 -0
- package/dist/channels/providers/photonWhatsapp.d.ts +49 -0
- package/dist/channels/providers/photonWhatsapp.js +146 -0
- package/dist/channels/providers/slack.d.ts +25 -0
- package/dist/channels/providers/slack.js +101 -0
- package/dist/channels/providers/telegram.d.ts +21 -0
- package/dist/channels/providers/telegram.js +100 -0
- package/dist/channels/providers.d.ts +5 -0
- package/dist/channels/providers.js +22 -0
- package/dist/channels/registry.d.ts +54 -0
- package/dist/channels/registry.js +186 -0
- package/dist/channels/secretStore.d.ts +21 -0
- package/dist/channels/secretStore.js +75 -0
- package/dist/channels/secrets.d.ts +15 -0
- package/dist/channels/secrets.js +50 -0
- package/dist/channels/service.d.ts +54 -0
- package/dist/channels/service.js +307 -0
- package/dist/channels/stageChannels.d.ts +45 -0
- package/dist/channels/stageChannels.js +110 -0
- package/dist/channels/types.d.ts +142 -0
- package/dist/channels/types.js +1 -0
- package/dist/cli/browserCommand.d.ts +3 -1
- package/dist/cli/browserCommand.js +25 -20
- package/dist/cli/doctorCommand.js +3 -1
- package/dist/cli/resolveCliProjectRoot.d.ts +7 -0
- package/dist/cli/resolveCliProjectRoot.js +27 -0
- package/dist/cli/runCommand.d.ts +1 -1
- package/dist/cli/runCommand.js +59 -17
- package/dist/cli/skillsCommand.d.ts +20 -0
- package/dist/cli/skillsCommand.js +74 -31
- package/dist/cli/validateCommand.d.ts +1 -1
- package/dist/cli/validateCommand.js +22 -4
- package/dist/cli.js +4 -4
- package/dist/config/browseCatalog.d.ts +2 -0
- package/dist/config/browseCatalog.js +6 -0
- package/dist/config/catalogRelativePath.d.ts +4 -0
- package/dist/config/catalogRelativePath.js +37 -9
- package/dist/config/createStage.d.ts +1 -0
- package/dist/config/createStage.js +11 -2
- package/dist/config/createTask.d.ts +30 -0
- package/dist/config/createTask.js +161 -0
- package/dist/config/createTrigger.d.ts +15 -0
- package/dist/config/createTrigger.js +32 -25
- package/dist/config/draftPackage.d.ts +42 -0
- package/dist/config/draftPackage.js +220 -23
- package/dist/config/ensureCatalogScanRoot.d.ts +2 -1
- package/dist/config/ensureCatalogScanRoot.js +6 -2
- package/dist/config/findTaskInCatalog.d.ts +33 -0
- package/dist/config/findTaskInCatalog.js +69 -0
- package/dist/config/hostConfig.d.ts +21 -0
- package/dist/config/hostConfig.js +114 -1
- package/dist/config/importTask.d.ts +19 -0
- package/dist/config/importTask.js +133 -0
- package/dist/config/installSkillRequest.d.ts +40 -0
- package/dist/config/installSkillRequest.js +286 -0
- package/dist/config/loadOutcome.d.ts +11 -6
- package/dist/config/loadPipeline.js +27 -4
- package/dist/config/loadStage.js +17 -0
- package/dist/config/loadStageflowManifest.js +4 -1
- package/dist/config/loadTask.js +2 -0
- package/dist/config/loadTrigger.js +2 -0
- package/dist/config/mergePipelineIncludes.d.ts +3 -0
- package/dist/config/mergePipelineIncludes.js +30 -0
- package/dist/config/normalizePipelineStageEntry.js +10 -1
- package/dist/config/pipelineStageKeys.js +1 -0
- package/dist/config/printTargetYaml.js +0 -1
- package/dist/config/publishDraftPackage.d.ts +5 -1
- package/dist/config/publishDraftPackage.js +4 -1
- package/dist/config/resolveCatalogRoots.d.ts +4 -1
- package/dist/config/resolveCatalogRoots.js +18 -1
- package/dist/config/skillUsageIndex.d.ts +6 -0
- package/dist/config/skillUsageIndex.js +39 -0
- package/dist/config/stageBrowser.js +13 -0
- package/dist/config/triggerAdapterStatus.d.ts +11 -0
- package/dist/config/triggerAdapterStatus.js +90 -0
- package/dist/config/triggerCatalog.d.ts +41 -2
- package/dist/config/triggerCatalog.js +107 -15
- package/dist/config/updateTask.d.ts +30 -0
- package/dist/config/updateTask.js +119 -0
- package/dist/config/updateTrigger.d.ts +38 -0
- package/dist/config/updateTrigger.js +220 -0
- package/dist/config/validateCatalog.d.ts +9 -2
- package/dist/config/validateCatalog.js +71 -7
- package/dist/config/workshopCatalogFilenames.d.ts +37 -0
- package/dist/config/workshopCatalogFilenames.js +125 -0
- package/dist/config/workshopCatalogRepair.d.ts +7 -0
- package/dist/config/workshopCatalogRepair.js +144 -0
- package/dist/config/yamlParsePosition.d.ts +4 -0
- package/dist/config/yamlParsePosition.js +9 -0
- package/dist/errors/codes.d.ts +1 -1
- package/dist/errors/codes.js +1 -0
- package/dist/hosted/runtime.d.ts +2 -2
- package/dist/hosted/types.d.ts +1 -1
- package/dist/hosted/types.js +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.js +2 -1
- package/dist/mcp/catalogTools.js +1 -0
- package/dist/mcp/getStartedTool.js +15 -6
- package/dist/mcp/triggerTools.js +2 -8
- package/dist/operatorAgent/agentPort.d.ts +11 -0
- package/dist/operatorAgent/agentPort.js +12 -0
- package/dist/operatorAgent/index.d.ts +1 -0
- package/dist/operatorAgent/index.js +1 -0
- package/dist/operatorAgent/piModel.js +26 -6
- package/dist/operatorAgent/profiles/workshopAuthorPlaybook.d.ts +1 -1
- package/dist/operatorAgent/profiles/workshopAuthorPlaybook.js +4 -2
- package/dist/operatorAgent/profiles/workshopAuthorTools.js +103 -16
- package/dist/package-meta.d.ts +1 -1
- package/dist/package-meta.js +1 -1
- package/dist/preflight/channelChecks.d.ts +17 -0
- package/dist/preflight/channelChecks.js +106 -0
- package/dist/preflight/pipelinePreflight.d.ts +9 -2
- package/dist/preflight/pipelinePreflight.js +9 -1
- package/dist/project/ensureProjectRootsFromEnv.d.ts +3 -0
- package/dist/project/ensureProjectRootsFromEnv.js +22 -0
- package/dist/project/globalHome.d.ts +1 -0
- package/dist/project/globalHome.js +18 -0
- package/dist/project/workspaceCatalog.d.ts +10 -0
- package/dist/project/workspaceCatalog.js +91 -0
- package/dist/runstore/backup.js +14 -4
- package/dist/runstore/port.d.ts +11 -1
- package/dist/runstore/restore.js +5 -2
- package/dist/runstore/sqlite/SqliteRunStore.d.ts +8 -0
- package/dist/runstore/sqlite/SqliteRunStore.js +29 -9
- package/dist/runstore/sqlite/migrations/012-trigger-fires.d.ts +7 -0
- package/dist/runstore/sqlite/migrations/012-trigger-fires.js +18 -0
- package/dist/runstore/sqlite/migrations/index.js +2 -0
- package/dist/runstore/stageSnapshot.d.ts +3 -1
- package/dist/runstore/stageSnapshot.js +19 -0
- package/dist/runstore/workspaceLayout.js +27 -0
- package/dist/runstore/writeContainedArtifact.d.ts +1 -0
- package/dist/runstore/writeContainedArtifact.js +52 -0
- package/dist/runtime/credentialBinding.js +14 -6
- package/dist/runtime/emailSource.js +20 -28
- package/dist/runtime/githubPollSource.js +20 -28
- package/dist/runtime/pipelineRunner.d.ts +4 -4
- package/dist/runtime/pipelineRunner.js +3 -1
- package/dist/runtime/pipelineScheduler.d.ts +2 -2
- package/dist/runtime/pipelineScheduler.js +3 -0
- package/dist/runtime/repositoryMaterialize.d.ts +1 -1
- package/dist/runtime/resumeReconstruct.d.ts +2 -2
- package/dist/runtime/resumeTimedOut.d.ts +2 -2
- package/dist/runtime/runManager.d.ts +18 -2
- package/dist/runtime/runManager.js +99 -31
- package/dist/runtime/runRetryCoordinator.d.ts +2 -2
- package/dist/runtime/scheduleSource.js +15 -24
- package/dist/runtime/stageAttemptBootstrap.d.ts +2 -2
- package/dist/runtime/stageAttemptBootstrap.js +26 -1
- package/dist/runtime/stageProcessLauncher.js +33 -3
- package/dist/runtime/stageRoots.js +8 -1
- package/dist/runtime/stageRunner.d.ts +2 -2
- package/dist/runtime/stageRunner.js +8 -2
- package/dist/runtime/stageWorker.js +2 -2
- package/dist/runtime/triggerPort.d.ts +5 -3
- package/dist/runtime/triggerRunner.d.ts +1 -0
- package/dist/runtime/triggerRunner.js +27 -20
- package/dist/runtime/webhookAccept.d.ts +25 -0
- package/dist/runtime/webhookAccept.js +56 -0
- package/dist/server/bootstrap.d.ts +10 -2
- package/dist/server/bootstrap.js +19 -1
- package/dist/server/channelRoutes.d.ts +9 -0
- package/dist/server/channelRoutes.js +88 -0
- package/dist/server/controlToken.d.ts +1 -1
- package/dist/server/controlToken.js +18 -2
- package/dist/server/healthSurfaces.d.ts +12 -0
- package/dist/server/healthSurfaces.js +27 -6
- package/dist/server/http.d.ts +19 -10
- package/dist/server/http.js +616 -85
- package/dist/server/liveViewPath.d.ts +9 -0
- package/dist/server/liveViewPath.js +8 -0
- package/dist/server/liveViewRoutes.d.ts +58 -0
- package/dist/server/liveViewRoutes.js +565 -0
- package/dist/server/mcpHost.d.ts +4 -2
- package/dist/server/mcpHost.js +2 -0
- package/dist/server/readCatalogFile.d.ts +29 -0
- package/dist/server/readCatalogFile.js +215 -0
- package/dist/server/triggerRoutes.d.ts +1 -0
- package/dist/server/triggerRoutes.js +200 -73
- package/dist/tools/sendMessage.d.ts +21 -0
- package/dist/tools/sendMessage.js +25 -0
- package/dist/tools/writeStageArtifact.d.ts +9 -0
- package/dist/tools/writeStageArtifact.js +2 -47
- package/dist/types/pipeline.d.ts +5 -2
- package/dist/types/stage.d.ts +14 -1
- package/dist/types/stageflowManifest.d.ts +1 -1
- package/dist/ui/assets/geist-cyrillic-ext-wght-normal-DjL33-gN.woff2 +0 -0
- package/dist/ui/assets/geist-cyrillic-wght-normal-BEAKL7Jp.woff2 +0 -0
- package/dist/ui/assets/geist-latin-ext-wght-normal-DC-KSUi6.woff2 +0 -0
- package/dist/ui/assets/geist-latin-wght-normal-BgDaEnEv.woff2 +0 -0
- package/dist/ui/assets/geist-mono-cyrillic-ext-wght-normal-X_5orZeX.woff2 +0 -0
- package/dist/ui/assets/geist-mono-cyrillic-wght-normal-DiZS0aHC.woff2 +0 -0
- package/dist/ui/assets/geist-mono-latin-ext-wght-normal-Bwz-egvJ.woff2 +0 -0
- package/dist/ui/assets/geist-mono-latin-wght-normal-XN7g48iV.woff2 +0 -0
- package/dist/ui/assets/geist-mono-symbols2-wght-normal-CO5SzqOn.woff2 +0 -0
- package/dist/ui/assets/geist-mono-vietnamese-wght-normal-DadHysG0.woff2 +0 -0
- package/dist/ui/assets/geist-vietnamese-wght-normal-6IgcOCM7.woff2 +0 -0
- package/dist/ui/assets/index-BOhqnPNg.js +260 -0
- package/dist/ui/assets/{index-Bv4p1aPK.css → index-Clv4sJpU.css} +1 -1
- package/dist/ui/index.html +4 -4
- package/dist/workshop/autosave.d.ts +2 -2
- package/dist/workshop/autosave.js +4 -6
- package/dist/workshop/buildStore.d.ts +2 -0
- package/dist/workshop/chatAttachments.d.ts +27 -0
- package/dist/workshop/chatAttachments.js +98 -0
- package/dist/workshop/chatTurn.d.ts +5 -2
- package/dist/workshop/chatTurn.js +22 -18
- package/dist/workshop/sessionMutations.d.ts +17 -0
- package/dist/workshop/sessionMutations.js +107 -0
- package/dist/workshop/sessionStore.d.ts +10 -0
- package/dist/workshop/sessionStore.js +52 -0
- package/dist/workshop/workshopPin.d.ts +4 -3
- package/dist/workshop/workshopPin.js +26 -6
- package/dist/workshop/workshopTranscriptTurn.d.ts +8 -0
- package/dist/workshop/workshopTranscriptTurn.js +46 -0
- package/examples/README.md +1 -1
- package/examples/archify-on-pr/README.md +1 -1
- package/examples/archify-on-pr/archify-on-pr.pipeline.yaml +0 -1
- package/examples/archify-on-pr/author-diagrams.yaml +1 -0
- package/examples/channels-smoke/README.md +12 -0
- package/examples/channels-smoke/channels-smoke.pipeline.yaml +11 -0
- package/examples/channels-smoke/channels-smoke.task.yaml +3 -0
- package/examples/channels-smoke/send-email.yaml +32 -0
- package/examples/channels-smoke/send-text.yaml +31 -0
- package/examples/conditional-fork/branch-a.yaml +1 -1
- package/examples/conditional-fork/decide.yaml +2 -1
- package/examples/feature-loop/{implement.yaml → feature-implement.yaml} +1 -1
- package/examples/feature-loop/feature-loop.pipeline.yaml +10 -10
- package/examples/feature-loop/{plan.yaml → feature-plan.yaml} +1 -1
- package/examples/feature-loop/{review.yaml → feature-review.yaml} +1 -1
- package/examples/feedback-loop/{implement.yaml → feedback-implement.yaml} +1 -1
- package/examples/feedback-loop/feedback-loop-wait-human.pipeline.yaml +9 -9
- package/examples/feedback-loop/feedback-loop.pipeline.yaml +9 -9
- package/examples/feedback-loop/{plan.yaml → feedback-plan.yaml} +1 -1
- package/examples/feedback-loop/{review.yaml → feedback-review.yaml} +1 -1
- package/examples/generic-fan-in/{research.yaml → fan-in-research.yaml} +1 -1
- package/examples/generic-fan-in/generic-fan-in-accepted.pipeline.yaml +3 -3
- package/examples/generic-fan-in/generic-fan-in.pipeline.yaml +3 -3
- package/examples/hello-world/{research.yaml → hello-research.yaml} +1 -1
- package/examples/hello-world/hello.pipeline.yaml +2 -2
- package/examples/mcp-hitl-tour/{review.yaml → mcp-hitl-review.yaml} +1 -1
- package/examples/mcp-hitl-tour/mcp-hitl-tour.pipeline.yaml +3 -3
- package/examples/oss-issue-contribution/oss-explain-fix.yaml +1 -0
- package/examples/oss-issue-contribution/oss-explain-issue.yaml +1 -0
- package/examples/oss-issue-contribution/oss-issue-contribution.pipeline.yaml +0 -2
- package/examples/stageflow.yaml +6 -0
- package/examples/triggers/sample-github-pr.trigger.yaml +12 -0
- package/examples/triggers/sample-inbox-email.trigger.yaml +14 -0
- package/examples/triggers/sample-manual-hello.trigger.yaml +5 -0
- package/examples/triggers/sample-nightly-hello.trigger.yaml +8 -0
- package/examples/triggers/sample-signed-webhook.trigger.yaml +11 -0
- package/examples/triggers-readiness/README.md +48 -0
- package/examples/triggers-readiness/readiness-emit.yaml +18 -0
- package/examples/triggers-readiness/readiness.pipeline.yaml +4 -0
- package/examples/triggers-readiness/readiness.task.yaml +6 -0
- package/examples/triggers-readiness/stageflow.yaml +13 -0
- package/examples/triggers-readiness/triggers/readiness-dynamic-manual.trigger.yaml +4 -0
- package/examples/triggers-readiness/triggers/readiness-email.trigger.yaml +12 -0
- package/examples/triggers-readiness/triggers/readiness-github-pr.trigger.yaml +12 -0
- package/examples/triggers-readiness/triggers/readiness-manual.trigger.yaml +5 -0
- package/examples/triggers-readiness/triggers/readiness-schedule.trigger.yaml +8 -0
- package/examples/triggers-readiness/triggers/readiness-webhook.trigger.yaml +11 -0
- package/package.json +8 -3
- package/skills/stageflow/references/control-surface.md +1 -1
- package/skills/stageflow-author/references/catalog-write-conventions.md +2 -2
- package/skills/stageflow-run/SKILL.md +1 -1
- package/skills/stageflow-run/references/mcp-call.md +1 -1
- package/skills/stageflow-setup/SKILL.md +1 -1
- package/dist/ui/assets/index-59cjF0OA.js +0 -133
package/dist/agent/piAdapter.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Pi implementation of
|
|
2
|
+
* Pi implementation of StagePort.
|
|
3
3
|
*
|
|
4
4
|
* One stage = one `createAgentSession` (single-shot SDK path, not
|
|
5
5
|
* AgentSessionRuntime / interactive / RPC). The session is sealed from host
|
|
@@ -35,19 +35,25 @@ import path from "node:path";
|
|
|
35
35
|
import { BROWSER_SKILL_NAME } from "../config/builtinSkills.js";
|
|
36
36
|
import { defineTool, ModelRuntime, resolveCliModel, SessionManager, SettingsManager, } from "@earendil-works/pi-coding-agent";
|
|
37
37
|
import { StageMcpError } from "../config/resolveStageMcpServers.js";
|
|
38
|
-
import { attachIsolatedMcp, STAGEFLOW_PI_MCP_EXTENSION_NAME, } from "./piIsolatedMcp.js";
|
|
38
|
+
import { attachIsolatedMcp, STAGEFLOW_PI_MCP_EXTENSION_NAME, UNREQUESTED_MCP_ADAPTER_TOOLS, } from "./piIsolatedMcp.js";
|
|
39
|
+
import { warmIsolatedMcpMetadata } from "./piIsolatedMcpProbe.js";
|
|
40
|
+
import { createToolOutputBudgetExtension, STAGEFLOW_TOOL_OUTPUT_EXTENSION_NAME, } from "./toolOutputBudgetExtension.js";
|
|
41
|
+
import { TOOL_OUTPUT_ARTIFACT_DIR } from "./toolOutputSpill.js";
|
|
39
42
|
import { createPiAgentSession, createSealedResourceLoader, } from "./piSessionFactory.js";
|
|
40
43
|
import { isAdvancingEnvelope } from "../envelope/check.js";
|
|
41
44
|
import { formatFeedbackLoopContext } from "../prompt/feedbackLoopContext.js";
|
|
42
45
|
import { formatPriorEnvelope } from "../prompt/priorEnvelope.js";
|
|
43
46
|
import { attemptContext, noAttemptContext, } from "../runtime/stageAttemptContext.js";
|
|
44
47
|
import { createEmitStageEnvelopeTool, } from "../tools/emitStageEnvelope.js";
|
|
48
|
+
import { remindUntilEmitted } from "./stageTurnDriver.js";
|
|
45
49
|
import { createAskOperatorTool, } from "../tools/askOperator.js";
|
|
46
50
|
import { createWriteStageArtifactTool } from "../tools/writeStageArtifact.js";
|
|
47
51
|
import { createSendEmailTool } from "../tools/sendEmail.js";
|
|
52
|
+
import { createSendMessageTool } from "../tools/sendMessage.js";
|
|
48
53
|
import { createReplyEmailTool } from "../tools/replyEmail.js";
|
|
49
54
|
import { createGetEmailTool, createSearchEmailTool, createDownloadEmailAttachmentTool } from "../tools/readEmail.js";
|
|
50
55
|
import "./cursorProvider.js";
|
|
56
|
+
import { isCursorModelRef, readCursorApiKey } from "./cursorProvider.js";
|
|
51
57
|
import { findProviderSupport } from "./providerSupport.js";
|
|
52
58
|
import { mapSessionEventToActivity, readActivityVerbose } from "./activity.js";
|
|
53
59
|
import { createStageActivityObserver, } from "./activityObserver.js";
|
|
@@ -55,6 +61,8 @@ import { DEFAULT_STAGE_TIMEOUT_MS, runtimeStageId, runStageViaOpen } from "./por
|
|
|
55
61
|
import { addModelUsage, emptyStageUsage } from "../types/usage.js";
|
|
56
62
|
import { TIMEOUT_ABORT_TOOL_RESULT, composeTimeoutResumePrompt, stageTimeoutReason, } from "./stageTimeout.js";
|
|
57
63
|
import { globalStageflowHome } from "../project/globalHome.js";
|
|
64
|
+
import { isUsableAuthFile, STAGEFLOW_AGENT_AUTH_PATH_ENV, } from "../runtime/credentialBinding.js";
|
|
65
|
+
import { SF_STAGE_WORKER } from "../runtime/stageWorkerProtocol.js";
|
|
58
66
|
import { durableRootFileToolDenial, STAGEFLOW_PATH_DENIED, } from "../runstore/workspaceLayout.js";
|
|
59
67
|
export { STAGEFLOW_PATH_DENIED };
|
|
60
68
|
export { createSealedResourceLoader } from "./piSessionFactory.js";
|
|
@@ -462,6 +470,10 @@ function buildUserPrompt(input, emitToolName, emitHintOverride, artifactToolName
|
|
|
462
470
|
].filter((dir) => dir !== undefined);
|
|
463
471
|
const skillBaseDir = skillBaseDirs.length > 0 ? skillBaseDirs.join(", ") : undefined;
|
|
464
472
|
const browserGuidance = formatBrowserGuidance(input);
|
|
473
|
+
const toolOutputGuidance = Object.keys(input.resolvedMcpServers ?? {}).length > 0 ||
|
|
474
|
+
(input.stage.email?.length ?? 0) > 0
|
|
475
|
+
? `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
|
+
: "";
|
|
465
477
|
const artifactGuidance = input.roots.mode === "bound" && artifactToolName !== undefined
|
|
466
478
|
? [
|
|
467
479
|
`Project checkout (agent cwd): ${input.roots.checkoutRoot}`,
|
|
@@ -482,6 +494,9 @@ function buildUserPrompt(input, emitToolName, emitHintOverride, artifactToolName
|
|
|
482
494
|
].join("\n")
|
|
483
495
|
: `Create factory stage artifacts under ${attemptArtifactsPath} relative to the run folder.`;
|
|
484
496
|
return [
|
|
497
|
+
...(input.stage.channels?.length ? [
|
|
498
|
+
`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
|
+
] : []),
|
|
485
500
|
...(input.stage.email?.length ? [
|
|
486
501
|
`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.`,
|
|
487
502
|
] : []),
|
|
@@ -499,6 +514,7 @@ function buildUserPrompt(input, emitToolName, emitHintOverride, artifactToolName
|
|
|
499
514
|
: "",
|
|
500
515
|
"",
|
|
501
516
|
artifactGuidance,
|
|
517
|
+
toolOutputGuidance,
|
|
502
518
|
browserGuidance,
|
|
503
519
|
emitHint,
|
|
504
520
|
]
|
|
@@ -543,15 +559,26 @@ export function composeFeedbackResumePrompt(input) {
|
|
|
543
559
|
formatFeedbackLoopContext(input.feedbackLoopContext, input.sessionMode ?? "feedback_resume"),
|
|
544
560
|
].join("\n\n");
|
|
545
561
|
}
|
|
546
|
-
function
|
|
562
|
+
function mcpExtensionTools(loader) {
|
|
547
563
|
const inlinePath = `<inline:${STAGEFLOW_PI_MCP_EXTENSION_NAME}>`;
|
|
564
|
+
return loader
|
|
565
|
+
.getExtensions()
|
|
566
|
+
.extensions.filter((ext) => ext.path === inlinePath)
|
|
567
|
+
.flatMap((ext) => [...ext.tools.keys()]);
|
|
568
|
+
}
|
|
569
|
+
function mcpToolMetadataMissing(loader) {
|
|
570
|
+
return mcpExtensionTools(loader).includes("mcp");
|
|
571
|
+
}
|
|
572
|
+
function collectMcpExtensionToolNames(loader) {
|
|
548
573
|
const names = [];
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
574
|
+
const unrequested = [];
|
|
575
|
+
for (const name of mcpExtensionTools(loader)) {
|
|
576
|
+
if (UNREQUESTED_MCP_ADAPTER_TOOLS.has(name))
|
|
577
|
+
unrequested.push(name);
|
|
578
|
+
else
|
|
579
|
+
names.push(name);
|
|
580
|
+
}
|
|
581
|
+
return unrequested.length > 0 ? { ok: false, unrequested } : { ok: true, names };
|
|
555
582
|
}
|
|
556
583
|
async function shutdownSession(session) {
|
|
557
584
|
if (!session) {
|
|
@@ -574,7 +601,10 @@ async function shutdownSession(session) {
|
|
|
574
601
|
// ignore dispose errors during teardown
|
|
575
602
|
}
|
|
576
603
|
}
|
|
577
|
-
function resultFromCapture(capture) {
|
|
604
|
+
function resultFromCapture(capture, providerError) {
|
|
605
|
+
if (providerError !== undefined && !capture.envelope) {
|
|
606
|
+
return { ok: false, reason: `provider error: ${providerError}` };
|
|
607
|
+
}
|
|
578
608
|
if (capture.error && !capture.envelope) {
|
|
579
609
|
return { ok: false, reason: capture.error };
|
|
580
610
|
}
|
|
@@ -800,6 +830,26 @@ export function injectOpaqueAnswerIntoSession(sessionManager, answer) {
|
|
|
800
830
|
async function prepareStageSessionWiring(input, sessionManager, existingAskWaitChannel) {
|
|
801
831
|
const { roots } = input;
|
|
802
832
|
const provider = findProviderSupport(input.stage.model);
|
|
833
|
+
if (process.env[SF_STAGE_WORKER] === "1" && !provider) {
|
|
834
|
+
if (!roots.authPath || !isUsableAuthFile(roots.authPath)) {
|
|
835
|
+
const authPath = roots.authPath;
|
|
836
|
+
return {
|
|
837
|
+
ok: false,
|
|
838
|
+
reason: authPath
|
|
839
|
+
? `Stage provider auth is not configured (auth file missing or empty: ${authPath}).`
|
|
840
|
+
: `Stage provider auth is not configured (${STAGEFLOW_AGENT_AUTH_PATH_ENV} is unset).`,
|
|
841
|
+
};
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
if (process.env[SF_STAGE_WORKER] === "1" &&
|
|
845
|
+
provider &&
|
|
846
|
+
isCursorModelRef(input.stage.model) &&
|
|
847
|
+
!readCursorApiKey()) {
|
|
848
|
+
return {
|
|
849
|
+
ok: false,
|
|
850
|
+
reason: "Stage provider auth is not configured (Cursor API key missing from the credential root).",
|
|
851
|
+
};
|
|
852
|
+
}
|
|
803
853
|
const capture = {};
|
|
804
854
|
const usage = emptyStageUsage();
|
|
805
855
|
const askWaitChannel = existingAskWaitChannel ?? new AskOperatorWaitChannel();
|
|
@@ -859,7 +909,9 @@ async function prepareStageSessionWiring(input, sessionManager, existingAskWaitC
|
|
|
859
909
|
const settingsManager = SettingsManager.inMemory({
|
|
860
910
|
compaction: { enabled: false },
|
|
861
911
|
});
|
|
862
|
-
attached = await attachIsolatedMcp(input.resolvedMcpServers
|
|
912
|
+
attached = await attachIsolatedMcp(input.resolvedMcpServers, {
|
|
913
|
+
lifecycle: "eager",
|
|
914
|
+
});
|
|
863
915
|
const failAfterAttach = (reason) => {
|
|
864
916
|
attached?.cancel?.();
|
|
865
917
|
restoreProvider?.();
|
|
@@ -876,6 +928,21 @@ async function prepareStageSessionWiring(input, sessionManager, existingAskWaitC
|
|
|
876
928
|
: {}),
|
|
877
929
|
}),
|
|
878
930
|
},
|
|
931
|
+
{
|
|
932
|
+
name: STAGEFLOW_TOOL_OUTPUT_EXTENSION_NAME,
|
|
933
|
+
factory: createToolOutputBudgetExtension({
|
|
934
|
+
runWorkspaceDir: roots.runWorkspaceDir,
|
|
935
|
+
stageId: runtimeStageId(input),
|
|
936
|
+
attempt: roots.attempt ?? 1,
|
|
937
|
+
exempt: new Set([
|
|
938
|
+
"read",
|
|
939
|
+
"bash",
|
|
940
|
+
emitDef.name,
|
|
941
|
+
artifactDef.name,
|
|
942
|
+
askDef?.name ?? "ask_operator",
|
|
943
|
+
]),
|
|
944
|
+
}),
|
|
945
|
+
},
|
|
879
946
|
...(attached.extensionFactories ?? []),
|
|
880
947
|
];
|
|
881
948
|
const loader = createSealedResourceLoader({
|
|
@@ -889,6 +956,23 @@ async function prepareStageSessionWiring(input, sessionManager, existingAskWaitC
|
|
|
889
956
|
...(attached.eventBus !== undefined ? { eventBus: attached.eventBus } : {}),
|
|
890
957
|
});
|
|
891
958
|
await loader.reload();
|
|
959
|
+
if (attached.extensionFactories !== undefined &&
|
|
960
|
+
input.resolvedMcpServers !== undefined &&
|
|
961
|
+
mcpToolMetadataMissing(loader)) {
|
|
962
|
+
// A cold adapter metadata cache registers only the `mcp` gateway at load,
|
|
963
|
+
// and the session allowlist is fixed at load. Warm the cache, then reload
|
|
964
|
+
// so the declared servers' direct tools exist when the allowlist is built.
|
|
965
|
+
try {
|
|
966
|
+
await warmIsolatedMcpMetadata(input.resolvedMcpServers, { cwd: roots.cwd });
|
|
967
|
+
}
|
|
968
|
+
catch (err) {
|
|
969
|
+
return failAfterAttach(err instanceof Error ? err.message : String(err));
|
|
970
|
+
}
|
|
971
|
+
await loader.reload();
|
|
972
|
+
if (mcpToolMetadataMissing(loader)) {
|
|
973
|
+
return failAfterAttach(`MCP tool metadata unavailable for: ${Object.keys(input.resolvedMcpServers).join(", ")}`);
|
|
974
|
+
}
|
|
975
|
+
}
|
|
892
976
|
const extensionErrors = loader.getExtensions().errors;
|
|
893
977
|
if (extensionErrors.length > 0) {
|
|
894
978
|
return failAfterAttach(`Failed to load Pi extension(s): ${extensionErrors
|
|
@@ -912,7 +996,16 @@ async function prepareStageSessionWiring(input, sessionManager, existingAskWaitC
|
|
|
912
996
|
: [emitTool, artifactTool];
|
|
913
997
|
const tools = resolveStageToolNames(emitDef.name, artifactDef.name, askDef?.name ?? "ask_operator", gateKinds);
|
|
914
998
|
if (attached.extensionFactories !== undefined) {
|
|
915
|
-
|
|
999
|
+
const mcpTools = collectMcpExtensionToolNames(loader);
|
|
1000
|
+
if (!mcpTools.ok) {
|
|
1001
|
+
return failAfterAttach(`MCP adapter registered unrequested tool(s): ${mcpTools.unrequested.join(", ")}`);
|
|
1002
|
+
}
|
|
1003
|
+
tools.push(...mcpTools.names);
|
|
1004
|
+
}
|
|
1005
|
+
if (input.channels && input.stage.channels?.some(channel => channel.operations.includes("send"))) {
|
|
1006
|
+
const definition = createSendMessageTool(input.channels);
|
|
1007
|
+
customTools.push(defineTool(definition));
|
|
1008
|
+
tools.push(definition.name);
|
|
916
1009
|
}
|
|
917
1010
|
if (input.email) {
|
|
918
1011
|
const permitted = new Set(input.stage.email?.flatMap(permission => permission.operations));
|
|
@@ -1178,6 +1271,7 @@ export class PiAgentAdapter {
|
|
|
1178
1271
|
let wiring;
|
|
1179
1272
|
let unsubscribeProgress;
|
|
1180
1273
|
let prepareError;
|
|
1274
|
+
let closing = false;
|
|
1181
1275
|
const preparePromise = (async () => {
|
|
1182
1276
|
if (forceNewSession) {
|
|
1183
1277
|
await mkdir(path.dirname(sessionFile), { recursive: true });
|
|
@@ -1213,15 +1307,30 @@ export class PiAgentAdapter {
|
|
|
1213
1307
|
const controller = new AbortController();
|
|
1214
1308
|
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
1215
1309
|
try {
|
|
1216
|
-
const
|
|
1310
|
+
const live = session;
|
|
1311
|
+
const capture = wiring.capture;
|
|
1312
|
+
const emitToolName = wiring.emitDefName;
|
|
1313
|
+
const turns = {
|
|
1314
|
+
prompt: (text) => live.prompt(text),
|
|
1315
|
+
get messages() {
|
|
1316
|
+
return live.messages ?? live.agent?.state?.messages ?? [];
|
|
1317
|
+
},
|
|
1318
|
+
};
|
|
1319
|
+
const workPromise = (async () => {
|
|
1320
|
+
await work();
|
|
1321
|
+
return remindUntilEmitted(turns, capture, {
|
|
1322
|
+
emitToolName,
|
|
1323
|
+
shouldStop: () => closing || controller.signal.aborted,
|
|
1324
|
+
});
|
|
1325
|
+
})();
|
|
1217
1326
|
const abortPromise = new Promise((_, reject) => {
|
|
1218
1327
|
controller.signal.addEventListener("abort", () => {
|
|
1219
1328
|
void session?.abort();
|
|
1220
1329
|
reject(new Error(stageTimeoutReason(timeoutMs)));
|
|
1221
1330
|
});
|
|
1222
1331
|
});
|
|
1223
|
-
await Promise.race([workPromise, abortPromise]);
|
|
1224
|
-
return { ...resultFromCapture(
|
|
1332
|
+
const outcome = await Promise.race([workPromise, abortPromise]);
|
|
1333
|
+
return { ...resultFromCapture(capture, outcome.providerError), usage: wiring.usage };
|
|
1225
1334
|
}
|
|
1226
1335
|
catch (err) {
|
|
1227
1336
|
if (sessionManager) {
|
|
@@ -1292,6 +1401,7 @@ export class PiAgentAdapter {
|
|
|
1292
1401
|
}
|
|
1293
1402
|
: undefined,
|
|
1294
1403
|
onClose: async (closeOptions) => {
|
|
1404
|
+
closing = true;
|
|
1295
1405
|
await preparePromise.catch(() => undefined);
|
|
1296
1406
|
if (closeOptions?.park) {
|
|
1297
1407
|
if (sessionManager) {
|
|
@@ -17,6 +17,7 @@ export type IsolatedMcpAttach = {
|
|
|
17
17
|
connecting: Promise<void> | undefined;
|
|
18
18
|
cancel?: () => void;
|
|
19
19
|
};
|
|
20
|
+
export declare const UNREQUESTED_MCP_ADAPTER_TOOLS: ReadonlySet<string>;
|
|
20
21
|
export declare function emitIsolatedMcpStatus(eventBus: EventBus, servers: ReadonlyArray<{
|
|
21
22
|
name: string;
|
|
22
23
|
status: string;
|
|
@@ -2,6 +2,7 @@ import path from "node:path";
|
|
|
2
2
|
import { createEventBus, } from "@earendil-works/pi-coding-agent";
|
|
3
3
|
import { StageMcpError, } from "../config/resolveStageMcpServers.js";
|
|
4
4
|
import { globalStageflowHome } from "../project/globalHome.js";
|
|
5
|
+
import { TOOL_OUTPUT_HARD_CEILING_BYTES } from "./toolOutputBudget.js";
|
|
5
6
|
export const STAGEFLOW_PI_MCP_EXTENSION_NAME = "stageflow-mcp";
|
|
6
7
|
const MCP_STATUS_EVENT = "pi-mcp-adapter/status/v1";
|
|
7
8
|
const DEFAULT_ISOLATED_MCP_CONNECT_TIMEOUT_MS = 30_000;
|
|
@@ -13,11 +14,25 @@ export function jitiFsCacheDir() {
|
|
|
13
14
|
export function resetCreateMcpAdapterCacheForTests() {
|
|
14
15
|
cachedCreateMcpAdapter = undefined;
|
|
15
16
|
}
|
|
17
|
+
// Stages get only the declared servers' direct tools: no mcpScript sandbox and
|
|
18
|
+
// no `mcp` gateway. The adapter's output guard is a backstop; the stage budget
|
|
19
|
+
// lives in toolOutputBudgetExtension.
|
|
16
20
|
const ISOLATED_MCP_SETTINGS = {
|
|
17
21
|
directTools: true,
|
|
18
22
|
elicitation: false,
|
|
19
23
|
hostConfigDiscovery: "off",
|
|
24
|
+
scriptMode: false,
|
|
25
|
+
disableProxyTool: true,
|
|
26
|
+
outputGuard: {
|
|
27
|
+
maxBytes: TOOL_OUTPUT_HARD_CEILING_BYTES,
|
|
28
|
+
maxLines: Number.MAX_SAFE_INTEGER,
|
|
29
|
+
detailsMaxBytes: 16 * 1024,
|
|
30
|
+
},
|
|
20
31
|
};
|
|
32
|
+
export const UNREQUESTED_MCP_ADAPTER_TOOLS = new Set([
|
|
33
|
+
"mcpScript",
|
|
34
|
+
"mcp",
|
|
35
|
+
]);
|
|
21
36
|
const SUCCESS_STATUSES = new Set([
|
|
22
37
|
"connected",
|
|
23
38
|
"cached",
|
|
@@ -48,7 +63,10 @@ function toIsolatedMcpConfig(snapshot, lifecycle = "lazy") {
|
|
|
48
63
|
}
|
|
49
64
|
return {
|
|
50
65
|
mcpServers,
|
|
51
|
-
settings: {
|
|
66
|
+
settings: {
|
|
67
|
+
...ISOLATED_MCP_SETTINGS,
|
|
68
|
+
outputGuard: { ...ISOLATED_MCP_SETTINGS.outputGuard },
|
|
69
|
+
},
|
|
52
70
|
};
|
|
53
71
|
}
|
|
54
72
|
async function loadCreateMcpAdapter() {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type ResolvedMcpServers } from "../config/resolveStageMcpServers.js";
|
|
1
2
|
export declare const PROJECT_MCP_PROBE_TIMEOUT_MS = 30000;
|
|
2
3
|
export type ProjectMcpProbeStatus = "connected" | "needs_auth" | "connect_failed" | "unresolved_var" | "invalid_config" | "missing_catalog" | "cancelled";
|
|
3
4
|
export type ProjectMcpProbeResult = {
|
|
@@ -13,3 +14,13 @@ export type ProbeProjectMcpServerOptions = {
|
|
|
13
14
|
timeoutMs?: number;
|
|
14
15
|
};
|
|
15
16
|
export declare function probeProjectMcpServer(options: ProbeProjectMcpServerOptions): Promise<ProjectMcpProbeResult>;
|
|
17
|
+
/**
|
|
18
|
+
* Connects the snapshot's servers headlessly so the adapter writes its tool
|
|
19
|
+
* metadata cache into the current Pi agent dir. A sealed loader reloaded after
|
|
20
|
+
* this registers the servers' direct tools at load time, which is when the
|
|
21
|
+
* stage tool allowlist is computed.
|
|
22
|
+
*/
|
|
23
|
+
export declare function warmIsolatedMcpMetadata(snapshot: ResolvedMcpServers, options: {
|
|
24
|
+
cwd: string;
|
|
25
|
+
timeoutMs?: number;
|
|
26
|
+
}): Promise<void>;
|
|
@@ -290,3 +290,23 @@ export async function probeProjectMcpServer(options) {
|
|
|
290
290
|
await dispose();
|
|
291
291
|
}
|
|
292
292
|
}
|
|
293
|
+
/**
|
|
294
|
+
* Connects the snapshot's servers headlessly so the adapter writes its tool
|
|
295
|
+
* metadata cache into the current Pi agent dir. A sealed loader reloaded after
|
|
296
|
+
* this registers the servers' direct tools at load time, which is when the
|
|
297
|
+
* stage tool allowlist is computed.
|
|
298
|
+
*/
|
|
299
|
+
export async function warmIsolatedMcpMetadata(snapshot, options) {
|
|
300
|
+
const attached = await piIsolatedMcp.attachIsolatedMcp(snapshot, {
|
|
301
|
+
lifecycle: "eager",
|
|
302
|
+
...(options.timeoutMs !== undefined ? { timeoutMs: options.timeoutMs } : {}),
|
|
303
|
+
});
|
|
304
|
+
const shutdown = instantiateProbeFactories(attached, options.cwd);
|
|
305
|
+
try {
|
|
306
|
+
await attached.connecting;
|
|
307
|
+
}
|
|
308
|
+
finally {
|
|
309
|
+
attached.cancel?.();
|
|
310
|
+
await shutdown();
|
|
311
|
+
}
|
|
312
|
+
}
|
package/dist/agent/port.d.ts
CHANGED
|
@@ -8,6 +8,7 @@ import type { StageActivityEvent } from "./activity.js";
|
|
|
8
8
|
import type { ResolvedMcpServers } from "../config/resolveStageMcpServers.js";
|
|
9
9
|
import type { StageRoots } from "../runtime/stageRoots.js";
|
|
10
10
|
import type { StageEmail } from "../email/host.js";
|
|
11
|
+
import type { StageChannels } from "../channels/host.js";
|
|
11
12
|
import type { QaExchange } from "../hitl/qaTrail.js";
|
|
12
13
|
import type { StageUsage } from "../types/usage.js";
|
|
13
14
|
/** Opaque to runtime; adapters interpret. */
|
|
@@ -65,6 +66,7 @@ export type StageRunInput = {
|
|
|
65
66
|
onAssistantTextDelta?: (delta: string) => void;
|
|
66
67
|
skillFilePath?: string;
|
|
67
68
|
email?: StageEmail;
|
|
69
|
+
channels?: StageChannels;
|
|
68
70
|
/** Bundled browser skill; set by the runtime when the stage has `browser`. */
|
|
69
71
|
browserSkillFilePath?: string;
|
|
70
72
|
forkEmitContext?: ForkEmitContext;
|
|
@@ -122,12 +124,12 @@ export interface StageHandle {
|
|
|
122
124
|
deliverAnswer(answer: OpaqueAnswer): void;
|
|
123
125
|
close(options?: StageHandleCloseOptions): Promise<void>;
|
|
124
126
|
}
|
|
125
|
-
export interface
|
|
127
|
+
export interface StagePort {
|
|
126
128
|
openStage(input: StageRunInput): StageHandle;
|
|
127
129
|
runStage(input: StageRunInput): Promise<StageRunResult>;
|
|
128
130
|
}
|
|
129
131
|
/** Thin non-HITL wrapper: open → next; unexpected wait fails closed. */
|
|
130
|
-
export declare function runStageViaOpen(port: Pick<
|
|
132
|
+
export declare function runStageViaOpen(port: Pick<StagePort, "openStage">, input: StageRunInput): Promise<StageRunResult>;
|
|
131
133
|
/** Wrap a single-shot runner as a handle that never waits (Pi U2 shim). */
|
|
132
134
|
export declare function createCompletedOnlyStageHandle(options: {
|
|
133
135
|
stageId: string;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
1
2
|
import { mkdir, writeFile, chmod, access } from "node:fs/promises";
|
|
2
3
|
import path from "node:path";
|
|
3
4
|
import { CredentialSynchronizationError, ModelRuntime, } from "@earendil-works/pi-coding-agent";
|
|
5
|
+
import { builtinProviders } from "@earendil-works/pi-ai/providers/all";
|
|
4
6
|
import { parseCredentialSource, resolveCredentialBinding, writeCredentialSourceToContext, isUsableAuthFile, } from "../runtime/credentialBinding.js";
|
|
5
7
|
import { getCursorHostStatus } from "./cursorProvider.js";
|
|
6
8
|
import { readPersistedCredentialSourceFromContext, } from "../runtime/settingsFile.js";
|
|
@@ -23,7 +25,13 @@ export class ProviderAuthError extends Error {
|
|
|
23
25
|
this.status = status;
|
|
24
26
|
}
|
|
25
27
|
}
|
|
26
|
-
const defaultCreateRuntime = async (authPath) => ModelRuntime.create({
|
|
28
|
+
const defaultCreateRuntime = async (authPath) => ModelRuntime.create({
|
|
29
|
+
authPath,
|
|
30
|
+
// ModelRuntime otherwise reads models.json from PI_CODING_AGENT_DIR,
|
|
31
|
+
// which stays on the process data directory.
|
|
32
|
+
modelsPath: path.join(path.dirname(authPath), "models.json"),
|
|
33
|
+
refreshOnCreate: false,
|
|
34
|
+
});
|
|
27
35
|
async function ensurePiAuthFile(authPath) {
|
|
28
36
|
const resolved = path.resolve(authPath);
|
|
29
37
|
await mkdir(path.dirname(resolved), { recursive: true });
|
|
@@ -49,6 +57,31 @@ export async function openRuntime(cwd, ctx = defaultContext) {
|
|
|
49
57
|
const binding = resolveCredentialBinding(cwd);
|
|
50
58
|
return ctx.createRuntime(binding.authPath);
|
|
51
59
|
}
|
|
60
|
+
function catalogRuntime() {
|
|
61
|
+
const providers = builtinProviders();
|
|
62
|
+
return {
|
|
63
|
+
getProviders: () => providers,
|
|
64
|
+
getProvider: (id) => providers.find((provider) => provider.id === id),
|
|
65
|
+
getProviderAuthStatus: () => ({ configured: false }),
|
|
66
|
+
listCredentials: async () => [],
|
|
67
|
+
checkAuth: async () => undefined,
|
|
68
|
+
login: async () => {
|
|
69
|
+
throw new ProviderAuthError("Provider login failed", 500);
|
|
70
|
+
},
|
|
71
|
+
logout: async () => {
|
|
72
|
+
throw new ProviderAuthError("Provider logout failed", 500);
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
async function openReadRuntime(cwd, ctx) {
|
|
77
|
+
const binding = resolveCredentialBinding(cwd);
|
|
78
|
+
// ModelRuntime.create writes auth.json when that file is missing.
|
|
79
|
+
if (ctx.createRuntime === defaultCreateRuntime &&
|
|
80
|
+
!existsSync(binding.authPath)) {
|
|
81
|
+
return catalogRuntime();
|
|
82
|
+
}
|
|
83
|
+
return ctx.createRuntime(binding.authPath);
|
|
84
|
+
}
|
|
52
85
|
export function isLoginCapable(provider) {
|
|
53
86
|
return (provider.auth?.oauth != null || provider.auth?.apiKey?.login != null);
|
|
54
87
|
}
|
|
@@ -133,7 +166,7 @@ export async function statusForProvider(runtime, providerId) {
|
|
|
133
166
|
return mapped;
|
|
134
167
|
}
|
|
135
168
|
export async function listProviders(cwd, ctx = defaultContext) {
|
|
136
|
-
const runtime = await
|
|
169
|
+
const runtime = await openReadRuntime(cwd, ctx);
|
|
137
170
|
return {
|
|
138
171
|
authShell: "pi",
|
|
139
172
|
via: "pi",
|
|
@@ -144,7 +177,7 @@ export async function listProviders(cwd, ctx = defaultContext) {
|
|
|
144
177
|
};
|
|
145
178
|
}
|
|
146
179
|
export async function getAuthStatus(cwd, providerId, ctx = defaultContext) {
|
|
147
|
-
const runtime = await
|
|
180
|
+
const runtime = await openReadRuntime(cwd, ctx);
|
|
148
181
|
if (providerId !== undefined) {
|
|
149
182
|
if (!runtime.getProvider(providerId)) {
|
|
150
183
|
throw new ProviderAuthError("Provider not found", 404);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type AgentBackendId } from "./agentBackend.js";
|
|
2
|
-
import type {
|
|
2
|
+
import type { StagePort } from "./port.js";
|
|
3
3
|
import type { LoadedManifest } from "../types/stageflowManifest.js";
|
|
4
4
|
export type AgentSelection = {
|
|
5
5
|
/** stageflow.yaml top-level `agent`. */
|
|
@@ -10,14 +10,14 @@ export type AgentSelection = {
|
|
|
10
10
|
stage?: AgentBackendId;
|
|
11
11
|
};
|
|
12
12
|
/** stage > pipeline > global > "pi", with stage gated off for now. */
|
|
13
|
-
export declare function
|
|
14
|
-
/** Existing Pi
|
|
15
|
-
export declare function
|
|
16
|
-
export declare function
|
|
13
|
+
export declare function resolveStageBackend(selection?: AgentSelection): AgentBackendId;
|
|
14
|
+
/** Existing Pi StagePort. Hosted runs pass this into createHostedRuntime. */
|
|
15
|
+
export declare function createPiStagePort(): StagePort;
|
|
16
|
+
export declare function resolveStagePort(selection?: AgentSelection): StagePort;
|
|
17
17
|
/** The global tier's backend id from an already-loaded manifest, or undefined for missing/invalid/unset. */
|
|
18
|
-
export declare function
|
|
18
|
+
export declare function globalStageBackendFromManifest(manifest: LoadedManifest | null): AgentBackendId | undefined;
|
|
19
19
|
/**
|
|
20
|
-
* Resolves an
|
|
20
|
+
* Resolves an StagePort using only the global (stageflow.yaml) tier — for
|
|
21
21
|
* the three call sites that construct an agent before any pipeline is
|
|
22
22
|
* known (`cli.ts`'s ui/mcp servers, `runCommand.ts`, `runsCommand.ts`).
|
|
23
23
|
* Named explicitly so it's clear these sites are scoped to the global tier
|
|
@@ -25,4 +25,4 @@ export declare function globalAgentBackendFromManifest(manifest: LoadedManifest
|
|
|
25
25
|
* through `stageWorker.ts`, which resolves the full stage > pipeline >
|
|
26
26
|
* global chain once it has loaded the specific pipeline and stage.
|
|
27
27
|
*/
|
|
28
|
-
export declare function
|
|
28
|
+
export declare function resolveGlobalOnlyStagePort(projectRoot: string): Promise<StagePort>;
|
|
@@ -3,31 +3,31 @@ import { PiAgentAdapter } from "./piAdapter.js";
|
|
|
3
3
|
import { ClaudeAgentAdapter } from "./claudeAdapter.js";
|
|
4
4
|
import { loadStageflowManifestOutcome } from "../config/loadStageflowManifest.js";
|
|
5
5
|
/** stage > pipeline > global > "pi", with stage gated off for now. */
|
|
6
|
-
export function
|
|
6
|
+
export function resolveStageBackend(selection = {}) {
|
|
7
7
|
if (STAGE_LEVEL_AGENT_OVERRIDE_ENABLED && selection.stage !== undefined) {
|
|
8
8
|
return selection.stage;
|
|
9
9
|
}
|
|
10
10
|
return selection.pipeline ?? selection.global ?? "pi";
|
|
11
11
|
}
|
|
12
|
-
/** Existing Pi
|
|
13
|
-
export function
|
|
12
|
+
/** Existing Pi StagePort. Hosted runs pass this into createHostedRuntime. */
|
|
13
|
+
export function createPiStagePort() {
|
|
14
14
|
return new PiAgentAdapter();
|
|
15
15
|
}
|
|
16
|
-
export function
|
|
17
|
-
const backend =
|
|
16
|
+
export function resolveStagePort(selection = {}) {
|
|
17
|
+
const backend = resolveStageBackend(selection);
|
|
18
18
|
switch (backend) {
|
|
19
19
|
case "pi":
|
|
20
|
-
return
|
|
20
|
+
return createPiStagePort();
|
|
21
21
|
case "claude":
|
|
22
22
|
return new ClaudeAgentAdapter();
|
|
23
23
|
}
|
|
24
24
|
}
|
|
25
25
|
/** The global tier's backend id from an already-loaded manifest, or undefined for missing/invalid/unset. */
|
|
26
|
-
export function
|
|
26
|
+
export function globalStageBackendFromManifest(manifest) {
|
|
27
27
|
return manifest ? asAgentBackendId(manifest.manifest.agent) : undefined;
|
|
28
28
|
}
|
|
29
29
|
/**
|
|
30
|
-
* Resolves an
|
|
30
|
+
* Resolves an StagePort using only the global (stageflow.yaml) tier — for
|
|
31
31
|
* the three call sites that construct an agent before any pipeline is
|
|
32
32
|
* known (`cli.ts`'s ui/mcp servers, `runCommand.ts`, `runsCommand.ts`).
|
|
33
33
|
* Named explicitly so it's clear these sites are scoped to the global tier
|
|
@@ -35,9 +35,9 @@ export function globalAgentBackendFromManifest(manifest) {
|
|
|
35
35
|
* through `stageWorker.ts`, which resolves the full stage > pipeline >
|
|
36
36
|
* global chain once it has loaded the specific pipeline and stage.
|
|
37
37
|
*/
|
|
38
|
-
export async function
|
|
38
|
+
export async function resolveGlobalOnlyStagePort(projectRoot) {
|
|
39
39
|
const outcome = await loadStageflowManifestOutcome(projectRoot);
|
|
40
|
-
return
|
|
41
|
-
global: outcome.ok ?
|
|
40
|
+
return resolveStagePort({
|
|
41
|
+
global: outcome.ok ? globalStageBackendFromManifest(outcome.value) : undefined,
|
|
42
42
|
});
|
|
43
43
|
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { EmitCapture } from "../tools/emitStageEnvelope.js";
|
|
2
|
+
/** Follow-up turns sent when the agent ends its turn without an accepted envelope. */
|
|
3
|
+
export declare const DEFAULT_EMIT_REMINDERS = 2;
|
|
4
|
+
/** The slice of a Pi session the driver needs; a fake in tests. */
|
|
5
|
+
export type StageTurnSession = {
|
|
6
|
+
prompt(text: string): Promise<void>;
|
|
7
|
+
readonly messages: readonly unknown[];
|
|
8
|
+
};
|
|
9
|
+
export type RemindOptions = {
|
|
10
|
+
emitToolName: string;
|
|
11
|
+
maxReminders?: number;
|
|
12
|
+
/** True once the stage is closing, parked, or timed out: never start another turn then. */
|
|
13
|
+
shouldStop: () => boolean;
|
|
14
|
+
};
|
|
15
|
+
export type RemindOutcome = {
|
|
16
|
+
reminders: number;
|
|
17
|
+
/** Set when the last turn ended on a provider error; no reminder is sent after one. */
|
|
18
|
+
providerError?: string;
|
|
19
|
+
};
|
|
20
|
+
/** Trimmed, length-capped provider error text for a stage failure reason. */
|
|
21
|
+
export declare function formatProviderError(text: string | undefined): string;
|
|
22
|
+
/**
|
|
23
|
+
* Error text of the last assistant turn when it ended on a provider error (bad key, auth,
|
|
24
|
+
* model unavailable, or a transient error Pi already gave up retrying). Undefined otherwise.
|
|
25
|
+
*/
|
|
26
|
+
export declare function lastTurnProviderError(messages: readonly unknown[]): string | undefined;
|
|
27
|
+
export declare function emitReminderPrompt(emitToolName: string, attempt: number, max: number, rejectedEmit?: string): string;
|
|
28
|
+
/**
|
|
29
|
+
* Called after a turn returns. While no envelope was accepted, nudge the agent with a short
|
|
30
|
+
* follow-up turn, up to `maxReminders` times. Stops at once when an envelope is captured,
|
|
31
|
+
* the stage is stopping, the turn was aborted, or the turn ended on a provider error.
|
|
32
|
+
*/
|
|
33
|
+
export declare function remindUntilEmitted(session: StageTurnSession, capture: EmitCapture, options: RemindOptions): Promise<RemindOutcome>;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/** Follow-up turns sent when the agent ends its turn without an accepted envelope. */
|
|
2
|
+
export const DEFAULT_EMIT_REMINDERS = 2;
|
|
3
|
+
const PROVIDER_ERROR_MAX_CHARS = 500;
|
|
4
|
+
function lastAssistant(messages) {
|
|
5
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
6
|
+
const m = messages[i];
|
|
7
|
+
if (m && typeof m === "object" && m.role === "assistant")
|
|
8
|
+
return m;
|
|
9
|
+
}
|
|
10
|
+
return undefined;
|
|
11
|
+
}
|
|
12
|
+
/** Trimmed, length-capped provider error text for a stage failure reason. */
|
|
13
|
+
export function formatProviderError(text) {
|
|
14
|
+
const t = text?.trim() ? text.trim() : "the model provider returned an error";
|
|
15
|
+
return t.length > PROVIDER_ERROR_MAX_CHARS ? `${t.slice(0, PROVIDER_ERROR_MAX_CHARS)}…` : t;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Error text of the last assistant turn when it ended on a provider error (bad key, auth,
|
|
19
|
+
* model unavailable, or a transient error Pi already gave up retrying). Undefined otherwise.
|
|
20
|
+
*/
|
|
21
|
+
export function lastTurnProviderError(messages) {
|
|
22
|
+
const m = lastAssistant(messages);
|
|
23
|
+
if (!m || m.stopReason !== "error")
|
|
24
|
+
return undefined;
|
|
25
|
+
return formatProviderError(typeof m.errorMessage === "string" ? m.errorMessage : undefined);
|
|
26
|
+
}
|
|
27
|
+
function lastTurnAborted(messages) {
|
|
28
|
+
return lastAssistant(messages)?.stopReason === "aborted";
|
|
29
|
+
}
|
|
30
|
+
export function emitReminderPrompt(emitToolName, attempt, max, rejectedEmit) {
|
|
31
|
+
const lines = [
|
|
32
|
+
`Reminder ${attempt} of ${max}: your turn ended without an accepted ${emitToolName} call, so this stage has not finished.`,
|
|
33
|
+
];
|
|
34
|
+
if (rejectedEmit !== undefined) {
|
|
35
|
+
lines.push(`Your last ${emitToolName} call was rejected: ${rejectedEmit}`);
|
|
36
|
+
lines.push(`Fix that and call ${emitToolName} again.`);
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
lines.push(`Continue the stage's work if any is left, then call ${emitToolName}.`);
|
|
40
|
+
}
|
|
41
|
+
lines.push(`If the stage cannot be completed, call ${emitToolName} with status "failure" and a summary that says why. Do not end your turn without calling it.`);
|
|
42
|
+
return lines.join("\n");
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Called after a turn returns. While no envelope was accepted, nudge the agent with a short
|
|
46
|
+
* follow-up turn, up to `maxReminders` times. Stops at once when an envelope is captured,
|
|
47
|
+
* the stage is stopping, the turn was aborted, or the turn ended on a provider error.
|
|
48
|
+
*/
|
|
49
|
+
export async function remindUntilEmitted(session, capture, options) {
|
|
50
|
+
const max = options.maxReminders ?? DEFAULT_EMIT_REMINDERS;
|
|
51
|
+
let reminders = 0;
|
|
52
|
+
for (;;) {
|
|
53
|
+
if (capture.envelope)
|
|
54
|
+
return { reminders };
|
|
55
|
+
const providerError = lastTurnProviderError(session.messages);
|
|
56
|
+
if (providerError !== undefined)
|
|
57
|
+
return { reminders, providerError };
|
|
58
|
+
if (options.shouldStop() || lastTurnAborted(session.messages) || reminders >= max) {
|
|
59
|
+
return { reminders };
|
|
60
|
+
}
|
|
61
|
+
reminders++;
|
|
62
|
+
await session.prompt(emitReminderPrompt(options.emitToolName, reminders, max, capture.error));
|
|
63
|
+
}
|
|
64
|
+
}
|