stageflow 0.33.0 → 0.35.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 +50 -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 +116 -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 +2 -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/cli/browserCommand.d.ts +3 -1
- package/dist/cli/browserCommand.js +25 -20
- 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 +41 -0
- package/dist/config/draftPackage.js +216 -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 +113 -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 +0 -1
- package/dist/config/loadStage.js +2 -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.js +3 -0
- package/dist/config/normalizePipelineStageEntry.js +10 -1
- 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 +6 -2
- package/dist/config/validateCatalog.js +48 -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/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/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/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/pipelineScheduler.d.ts +2 -2
- package/dist/runtime/pipelineScheduler.js +3 -0
- 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 +95 -30
- 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 +10 -1
- package/dist/runtime/stageProcessLauncher.js +2 -2
- 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 +7 -2
- package/dist/server/bootstrap.js +15 -1
- 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 +605 -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/readEmail.d.ts +3 -3
- package/dist/tools/writeStageArtifact.d.ts +9 -0
- package/dist/tools/writeStageArtifact.js +2 -47
- package/dist/types/pipeline.d.ts +1 -1
- package/dist/types/stage.d.ts +1 -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-BoFPkKFg.js +256 -0
- package/dist/ui/assets/{index-Bv4p1aPK.css → index-CIPbbBUi.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/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 +1 -1
- 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,24 @@ 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";
|
|
48
52
|
import { createReplyEmailTool } from "../tools/replyEmail.js";
|
|
49
53
|
import { createGetEmailTool, createSearchEmailTool, createDownloadEmailAttachmentTool } from "../tools/readEmail.js";
|
|
50
54
|
import "./cursorProvider.js";
|
|
55
|
+
import { isCursorModelRef, readCursorApiKey } from "./cursorProvider.js";
|
|
51
56
|
import { findProviderSupport } from "./providerSupport.js";
|
|
52
57
|
import { mapSessionEventToActivity, readActivityVerbose } from "./activity.js";
|
|
53
58
|
import { createStageActivityObserver, } from "./activityObserver.js";
|
|
@@ -55,6 +60,8 @@ import { DEFAULT_STAGE_TIMEOUT_MS, runtimeStageId, runStageViaOpen } from "./por
|
|
|
55
60
|
import { addModelUsage, emptyStageUsage } from "../types/usage.js";
|
|
56
61
|
import { TIMEOUT_ABORT_TOOL_RESULT, composeTimeoutResumePrompt, stageTimeoutReason, } from "./stageTimeout.js";
|
|
57
62
|
import { globalStageflowHome } from "../project/globalHome.js";
|
|
63
|
+
import { isUsableAuthFile, STAGEFLOW_AGENT_AUTH_PATH_ENV, } from "../runtime/credentialBinding.js";
|
|
64
|
+
import { SF_STAGE_WORKER } from "../runtime/stageWorkerProtocol.js";
|
|
58
65
|
import { durableRootFileToolDenial, STAGEFLOW_PATH_DENIED, } from "../runstore/workspaceLayout.js";
|
|
59
66
|
export { STAGEFLOW_PATH_DENIED };
|
|
60
67
|
export { createSealedResourceLoader } from "./piSessionFactory.js";
|
|
@@ -462,6 +469,10 @@ function buildUserPrompt(input, emitToolName, emitHintOverride, artifactToolName
|
|
|
462
469
|
].filter((dir) => dir !== undefined);
|
|
463
470
|
const skillBaseDir = skillBaseDirs.length > 0 ? skillBaseDirs.join(", ") : undefined;
|
|
464
471
|
const browserGuidance = formatBrowserGuidance(input);
|
|
472
|
+
const toolOutputGuidance = Object.keys(input.resolvedMcpServers ?? {}).length > 0 ||
|
|
473
|
+
(input.stage.email?.length ?? 0) > 0
|
|
474
|
+
? `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"}.`
|
|
475
|
+
: "";
|
|
465
476
|
const artifactGuidance = input.roots.mode === "bound" && artifactToolName !== undefined
|
|
466
477
|
? [
|
|
467
478
|
`Project checkout (agent cwd): ${input.roots.checkoutRoot}`,
|
|
@@ -499,6 +510,7 @@ function buildUserPrompt(input, emitToolName, emitHintOverride, artifactToolName
|
|
|
499
510
|
: "",
|
|
500
511
|
"",
|
|
501
512
|
artifactGuidance,
|
|
513
|
+
toolOutputGuidance,
|
|
502
514
|
browserGuidance,
|
|
503
515
|
emitHint,
|
|
504
516
|
]
|
|
@@ -543,15 +555,26 @@ export function composeFeedbackResumePrompt(input) {
|
|
|
543
555
|
formatFeedbackLoopContext(input.feedbackLoopContext, input.sessionMode ?? "feedback_resume"),
|
|
544
556
|
].join("\n\n");
|
|
545
557
|
}
|
|
546
|
-
function
|
|
558
|
+
function mcpExtensionTools(loader) {
|
|
547
559
|
const inlinePath = `<inline:${STAGEFLOW_PI_MCP_EXTENSION_NAME}>`;
|
|
560
|
+
return loader
|
|
561
|
+
.getExtensions()
|
|
562
|
+
.extensions.filter((ext) => ext.path === inlinePath)
|
|
563
|
+
.flatMap((ext) => [...ext.tools.keys()]);
|
|
564
|
+
}
|
|
565
|
+
function mcpToolMetadataMissing(loader) {
|
|
566
|
+
return mcpExtensionTools(loader).includes("mcp");
|
|
567
|
+
}
|
|
568
|
+
function collectMcpExtensionToolNames(loader) {
|
|
548
569
|
const names = [];
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
570
|
+
const unrequested = [];
|
|
571
|
+
for (const name of mcpExtensionTools(loader)) {
|
|
572
|
+
if (UNREQUESTED_MCP_ADAPTER_TOOLS.has(name))
|
|
573
|
+
unrequested.push(name);
|
|
574
|
+
else
|
|
575
|
+
names.push(name);
|
|
576
|
+
}
|
|
577
|
+
return unrequested.length > 0 ? { ok: false, unrequested } : { ok: true, names };
|
|
555
578
|
}
|
|
556
579
|
async function shutdownSession(session) {
|
|
557
580
|
if (!session) {
|
|
@@ -574,7 +597,10 @@ async function shutdownSession(session) {
|
|
|
574
597
|
// ignore dispose errors during teardown
|
|
575
598
|
}
|
|
576
599
|
}
|
|
577
|
-
function resultFromCapture(capture) {
|
|
600
|
+
function resultFromCapture(capture, providerError) {
|
|
601
|
+
if (providerError !== undefined && !capture.envelope) {
|
|
602
|
+
return { ok: false, reason: `provider error: ${providerError}` };
|
|
603
|
+
}
|
|
578
604
|
if (capture.error && !capture.envelope) {
|
|
579
605
|
return { ok: false, reason: capture.error };
|
|
580
606
|
}
|
|
@@ -800,6 +826,26 @@ export function injectOpaqueAnswerIntoSession(sessionManager, answer) {
|
|
|
800
826
|
async function prepareStageSessionWiring(input, sessionManager, existingAskWaitChannel) {
|
|
801
827
|
const { roots } = input;
|
|
802
828
|
const provider = findProviderSupport(input.stage.model);
|
|
829
|
+
if (process.env[SF_STAGE_WORKER] === "1" && !provider) {
|
|
830
|
+
if (!roots.authPath || !isUsableAuthFile(roots.authPath)) {
|
|
831
|
+
const authPath = roots.authPath;
|
|
832
|
+
return {
|
|
833
|
+
ok: false,
|
|
834
|
+
reason: authPath
|
|
835
|
+
? `Stage provider auth is not configured (auth file missing or empty: ${authPath}).`
|
|
836
|
+
: `Stage provider auth is not configured (${STAGEFLOW_AGENT_AUTH_PATH_ENV} is unset).`,
|
|
837
|
+
};
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
if (process.env[SF_STAGE_WORKER] === "1" &&
|
|
841
|
+
provider &&
|
|
842
|
+
isCursorModelRef(input.stage.model) &&
|
|
843
|
+
!readCursorApiKey()) {
|
|
844
|
+
return {
|
|
845
|
+
ok: false,
|
|
846
|
+
reason: "Stage provider auth is not configured (Cursor API key missing from the credential root).",
|
|
847
|
+
};
|
|
848
|
+
}
|
|
803
849
|
const capture = {};
|
|
804
850
|
const usage = emptyStageUsage();
|
|
805
851
|
const askWaitChannel = existingAskWaitChannel ?? new AskOperatorWaitChannel();
|
|
@@ -859,7 +905,9 @@ async function prepareStageSessionWiring(input, sessionManager, existingAskWaitC
|
|
|
859
905
|
const settingsManager = SettingsManager.inMemory({
|
|
860
906
|
compaction: { enabled: false },
|
|
861
907
|
});
|
|
862
|
-
attached = await attachIsolatedMcp(input.resolvedMcpServers
|
|
908
|
+
attached = await attachIsolatedMcp(input.resolvedMcpServers, {
|
|
909
|
+
lifecycle: "eager",
|
|
910
|
+
});
|
|
863
911
|
const failAfterAttach = (reason) => {
|
|
864
912
|
attached?.cancel?.();
|
|
865
913
|
restoreProvider?.();
|
|
@@ -876,6 +924,21 @@ async function prepareStageSessionWiring(input, sessionManager, existingAskWaitC
|
|
|
876
924
|
: {}),
|
|
877
925
|
}),
|
|
878
926
|
},
|
|
927
|
+
{
|
|
928
|
+
name: STAGEFLOW_TOOL_OUTPUT_EXTENSION_NAME,
|
|
929
|
+
factory: createToolOutputBudgetExtension({
|
|
930
|
+
runWorkspaceDir: roots.runWorkspaceDir,
|
|
931
|
+
stageId: runtimeStageId(input),
|
|
932
|
+
attempt: roots.attempt ?? 1,
|
|
933
|
+
exempt: new Set([
|
|
934
|
+
"read",
|
|
935
|
+
"bash",
|
|
936
|
+
emitDef.name,
|
|
937
|
+
artifactDef.name,
|
|
938
|
+
askDef?.name ?? "ask_operator",
|
|
939
|
+
]),
|
|
940
|
+
}),
|
|
941
|
+
},
|
|
879
942
|
...(attached.extensionFactories ?? []),
|
|
880
943
|
];
|
|
881
944
|
const loader = createSealedResourceLoader({
|
|
@@ -889,6 +952,23 @@ async function prepareStageSessionWiring(input, sessionManager, existingAskWaitC
|
|
|
889
952
|
...(attached.eventBus !== undefined ? { eventBus: attached.eventBus } : {}),
|
|
890
953
|
});
|
|
891
954
|
await loader.reload();
|
|
955
|
+
if (attached.extensionFactories !== undefined &&
|
|
956
|
+
input.resolvedMcpServers !== undefined &&
|
|
957
|
+
mcpToolMetadataMissing(loader)) {
|
|
958
|
+
// A cold adapter metadata cache registers only the `mcp` gateway at load,
|
|
959
|
+
// and the session allowlist is fixed at load. Warm the cache, then reload
|
|
960
|
+
// so the declared servers' direct tools exist when the allowlist is built.
|
|
961
|
+
try {
|
|
962
|
+
await warmIsolatedMcpMetadata(input.resolvedMcpServers, { cwd: roots.cwd });
|
|
963
|
+
}
|
|
964
|
+
catch (err) {
|
|
965
|
+
return failAfterAttach(err instanceof Error ? err.message : String(err));
|
|
966
|
+
}
|
|
967
|
+
await loader.reload();
|
|
968
|
+
if (mcpToolMetadataMissing(loader)) {
|
|
969
|
+
return failAfterAttach(`MCP tool metadata unavailable for: ${Object.keys(input.resolvedMcpServers).join(", ")}`);
|
|
970
|
+
}
|
|
971
|
+
}
|
|
892
972
|
const extensionErrors = loader.getExtensions().errors;
|
|
893
973
|
if (extensionErrors.length > 0) {
|
|
894
974
|
return failAfterAttach(`Failed to load Pi extension(s): ${extensionErrors
|
|
@@ -912,7 +992,11 @@ async function prepareStageSessionWiring(input, sessionManager, existingAskWaitC
|
|
|
912
992
|
: [emitTool, artifactTool];
|
|
913
993
|
const tools = resolveStageToolNames(emitDef.name, artifactDef.name, askDef?.name ?? "ask_operator", gateKinds);
|
|
914
994
|
if (attached.extensionFactories !== undefined) {
|
|
915
|
-
|
|
995
|
+
const mcpTools = collectMcpExtensionToolNames(loader);
|
|
996
|
+
if (!mcpTools.ok) {
|
|
997
|
+
return failAfterAttach(`MCP adapter registered unrequested tool(s): ${mcpTools.unrequested.join(", ")}`);
|
|
998
|
+
}
|
|
999
|
+
tools.push(...mcpTools.names);
|
|
916
1000
|
}
|
|
917
1001
|
if (input.email) {
|
|
918
1002
|
const permitted = new Set(input.stage.email?.flatMap(permission => permission.operations));
|
|
@@ -1178,6 +1262,7 @@ export class PiAgentAdapter {
|
|
|
1178
1262
|
let wiring;
|
|
1179
1263
|
let unsubscribeProgress;
|
|
1180
1264
|
let prepareError;
|
|
1265
|
+
let closing = false;
|
|
1181
1266
|
const preparePromise = (async () => {
|
|
1182
1267
|
if (forceNewSession) {
|
|
1183
1268
|
await mkdir(path.dirname(sessionFile), { recursive: true });
|
|
@@ -1213,15 +1298,30 @@ export class PiAgentAdapter {
|
|
|
1213
1298
|
const controller = new AbortController();
|
|
1214
1299
|
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
1215
1300
|
try {
|
|
1216
|
-
const
|
|
1301
|
+
const live = session;
|
|
1302
|
+
const capture = wiring.capture;
|
|
1303
|
+
const emitToolName = wiring.emitDefName;
|
|
1304
|
+
const turns = {
|
|
1305
|
+
prompt: (text) => live.prompt(text),
|
|
1306
|
+
get messages() {
|
|
1307
|
+
return live.messages ?? live.agent?.state?.messages ?? [];
|
|
1308
|
+
},
|
|
1309
|
+
};
|
|
1310
|
+
const workPromise = (async () => {
|
|
1311
|
+
await work();
|
|
1312
|
+
return remindUntilEmitted(turns, capture, {
|
|
1313
|
+
emitToolName,
|
|
1314
|
+
shouldStop: () => closing || controller.signal.aborted,
|
|
1315
|
+
});
|
|
1316
|
+
})();
|
|
1217
1317
|
const abortPromise = new Promise((_, reject) => {
|
|
1218
1318
|
controller.signal.addEventListener("abort", () => {
|
|
1219
1319
|
void session?.abort();
|
|
1220
1320
|
reject(new Error(stageTimeoutReason(timeoutMs)));
|
|
1221
1321
|
});
|
|
1222
1322
|
});
|
|
1223
|
-
await Promise.race([workPromise, abortPromise]);
|
|
1224
|
-
return { ...resultFromCapture(
|
|
1323
|
+
const outcome = await Promise.race([workPromise, abortPromise]);
|
|
1324
|
+
return { ...resultFromCapture(capture, outcome.providerError), usage: wiring.usage };
|
|
1225
1325
|
}
|
|
1226
1326
|
catch (err) {
|
|
1227
1327
|
if (sessionManager) {
|
|
@@ -1292,6 +1392,7 @@ export class PiAgentAdapter {
|
|
|
1292
1392
|
}
|
|
1293
1393
|
: undefined,
|
|
1294
1394
|
onClose: async (closeOptions) => {
|
|
1395
|
+
closing = true;
|
|
1295
1396
|
await preparePromise.catch(() => undefined);
|
|
1296
1397
|
if (closeOptions?.park) {
|
|
1297
1398
|
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
|
@@ -122,12 +122,12 @@ export interface StageHandle {
|
|
|
122
122
|
deliverAnswer(answer: OpaqueAnswer): void;
|
|
123
123
|
close(options?: StageHandleCloseOptions): Promise<void>;
|
|
124
124
|
}
|
|
125
|
-
export interface
|
|
125
|
+
export interface StagePort {
|
|
126
126
|
openStage(input: StageRunInput): StageHandle;
|
|
127
127
|
runStage(input: StageRunInput): Promise<StageRunResult>;
|
|
128
128
|
}
|
|
129
129
|
/** Thin non-HITL wrapper: open → next; unexpected wait fails closed. */
|
|
130
|
-
export declare function runStageViaOpen(port: Pick<
|
|
130
|
+
export declare function runStageViaOpen(port: Pick<StagePort, "openStage">, input: StageRunInput): Promise<StageRunResult>;
|
|
131
131
|
/** Wrap a single-shot runner as a handle that never waits (Pi U2 shim). */
|
|
132
132
|
export declare function createCompletedOnlyStageHandle(options: {
|
|
133
133
|
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
|
+
}
|