stageflow 0.25.0 → 0.30.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 +14 -3
- package/builtin-skills/browser/SKILL.md +42 -0
- package/dist/a2a/contracts.d.ts +1 -0
- package/dist/a2a/contracts.js +1 -0
- package/dist/a2a/registry.d.ts +2 -0
- package/dist/a2a/registry.js +20 -1
- package/dist/a2a/server.d.ts +7 -0
- package/dist/a2a/server.js +5 -2
- package/dist/a2a/service.d.ts +1 -1
- package/dist/a2a/service.js +57 -8
- package/dist/a2a/store.d.ts +8 -5
- package/dist/a2a/store.js +24 -67
- package/dist/agent/activity.d.ts +3 -0
- package/dist/agent/bootProviderConfig.d.ts +21 -0
- package/dist/agent/bootProviderConfig.js +74 -0
- package/dist/agent/claudeAdapter.js +2 -2
- package/dist/agent/cursorProvider.d.ts +2 -0
- package/dist/agent/cursorProvider.js +30 -5
- package/dist/agent/fakeAgent.d.ts +1 -0
- package/dist/agent/fakeAgent.js +13 -0
- package/dist/agent/piAdapter.d.ts +11 -24
- package/dist/agent/piAdapter.js +107 -50
- package/dist/agent/piIsolatedMcp.d.ts +2 -0
- package/dist/agent/piIsolatedMcp.js +10 -2
- package/dist/agent/piSessionFactory.d.ts +38 -0
- package/dist/agent/piSessionFactory.js +60 -0
- package/dist/agent/port.d.ts +8 -0
- package/dist/agent/streamLogRedact.d.ts +7 -1
- package/dist/agent/streamLogRedact.js +26 -11
- package/dist/browser/anchor.d.ts +27 -0
- package/dist/browser/anchor.js +127 -0
- package/dist/browser/auditSink.d.ts +38 -0
- package/dist/browser/auditSink.js +34 -0
- package/dist/browser/browserHost.d.ts +86 -0
- package/dist/browser/browserHost.js +7 -0
- package/dist/browser/browserSweep.d.ts +14 -0
- package/dist/browser/browserSweep.js +54 -0
- package/dist/browser/browserTeardown.d.ts +56 -0
- package/dist/browser/browserTeardown.js +235 -0
- package/dist/browser/fakeRemoteBrowserHost.d.ts +2 -0
- package/dist/browser/fakeRemoteBrowserHost.js +34 -0
- package/dist/browser/gateHandoff.d.ts +25 -0
- package/dist/browser/gateHandoff.js +83 -0
- package/dist/browser/humanLogin.d.ts +18 -0
- package/dist/browser/humanLogin.js +34 -0
- package/dist/browser/keyProvider.d.ts +10 -0
- package/dist/browser/keyProvider.js +39 -0
- package/dist/browser/localBrowserHost.d.ts +10 -0
- package/dist/browser/localBrowserHost.js +152 -0
- package/dist/browser/localProfileLock.d.ts +2 -0
- package/dist/browser/localProfileLock.js +218 -0
- package/dist/browser/localProfileStore.d.ts +5 -0
- package/dist/browser/localProfileStore.js +91 -0
- package/dist/browser/loginCheck.d.ts +53 -0
- package/dist/browser/loginCheck.js +147 -0
- package/dist/browser/memoryProfileLock.d.ts +2 -0
- package/dist/browser/memoryProfileLock.js +53 -0
- package/dist/browser/memoryProfileStore.d.ts +2 -0
- package/dist/browser/memoryProfileStore.js +31 -0
- package/dist/browser/navigationAudit.d.ts +16 -0
- package/dist/browser/navigationAudit.js +53 -0
- package/dist/browser/persistedEnv.d.ts +3 -0
- package/dist/browser/persistedEnv.js +15 -0
- package/dist/browser/profileLock.d.ts +40 -0
- package/dist/browser/profileLock.js +10 -0
- package/dist/browser/profileStore.d.ts +23 -0
- package/dist/browser/profileStore.js +39 -0
- package/dist/browser/runLiveness.d.ts +13 -0
- package/dist/browser/runLiveness.js +39 -0
- package/dist/browser/sitePolicy.d.ts +10 -0
- package/dist/browser/sitePolicy.js +61 -0
- package/dist/browser/stageBrowserEnv.d.ts +22 -0
- package/dist/browser/stageBrowserEnv.js +147 -0
- package/dist/browser/stageProfileLock.d.ts +17 -0
- package/dist/browser/stageProfileLock.js +39 -0
- package/dist/cli/artifactCommand.js +9 -16
- package/dist/cli/backupCommand.d.ts +9 -0
- package/dist/cli/backupCommand.js +111 -0
- package/dist/cli/browserCommand.d.ts +44 -0
- package/dist/cli/browserCommand.js +472 -0
- package/dist/cli/debugRunCommand.d.ts +10 -0
- package/dist/cli/debugRunCommand.js +109 -0
- package/dist/cli/doctorCommand.d.ts +32 -0
- package/dist/cli/doctorCommand.js +453 -0
- package/dist/cli/envelopeCommand.js +9 -2
- package/dist/cli/exportAllCommand.d.ts +55 -0
- package/dist/cli/exportAllCommand.js +218 -0
- package/dist/cli/exportRunCommand.js +23 -21
- package/dist/cli/hostClient.d.ts +41 -3
- package/dist/cli/hostClient.js +129 -14
- package/dist/cli/initCommand.d.ts +1 -0
- package/dist/cli/initCommand.js +23 -7
- package/dist/cli/initTemplates.d.ts +5 -2
- package/dist/cli/initTemplates.js +32 -2
- package/dist/cli/resolveSafeOutPath.d.ts +2 -0
- package/dist/cli/resolveSafeOutPath.js +40 -0
- package/dist/cli/restoreCommand.d.ts +10 -0
- package/dist/cli/restoreCommand.js +90 -0
- package/dist/cli/runCommand.d.ts +3 -2
- package/dist/cli/runCommand.js +93 -11
- package/dist/cli/runOutput.d.ts +4 -0
- package/dist/cli/runOutput.js +33 -7
- package/dist/cli/runStageCommand.d.ts +1 -0
- package/dist/cli/runStageCommand.js +44 -25
- package/dist/cli/runsCommand.d.ts +1 -1
- package/dist/cli/runsCommand.js +168 -10
- package/dist/cli/triggerCommand.d.ts +12 -0
- package/dist/cli/triggerCommand.js +183 -0
- package/dist/cli/validateCommand.d.ts +1 -1
- package/dist/cli/validateCommand.js +23 -6
- package/dist/cli.js +202 -32
- package/dist/config/browseCatalog.d.ts +1 -0
- package/dist/config/browseCatalog.js +5 -4
- package/dist/config/builtinSkills.d.ts +4 -0
- package/dist/config/builtinSkills.js +23 -0
- package/dist/config/catalogRelativePath.d.ts +63 -0
- package/dist/config/catalogRelativePath.js +126 -0
- package/dist/config/configOrigin.d.ts +27 -0
- package/dist/config/configOrigin.js +59 -0
- package/dist/config/createTrigger.d.ts +27 -0
- package/dist/config/createTrigger.js +232 -0
- package/dist/config/draftPackage.d.ts +117 -0
- package/dist/config/draftPackage.js +550 -0
- package/dist/config/ensureCatalogScanRoot.d.ts +16 -0
- package/dist/config/ensureCatalogScanRoot.js +73 -0
- package/dist/config/hostConfig.d.ts +57 -0
- package/dist/config/hostConfig.js +360 -0
- package/dist/config/listSkills.d.ts +26 -0
- package/dist/config/listSkills.js +206 -5
- package/dist/config/loadOutcome.d.ts +7 -1
- package/dist/config/loadPipeline.js +39 -2
- package/dist/config/loadStage.js +25 -0
- package/dist/config/loadStageflowManifest.js +25 -0
- package/dist/config/loadTask.d.ts +2 -0
- package/dist/config/loadTask.js +57 -18
- package/dist/config/loadTrigger.d.ts +9 -0
- package/dist/config/loadTrigger.js +120 -0
- package/dist/config/materializeStageModels.js +5 -1
- package/dist/config/mergePipelineIncludes.d.ts +3 -0
- package/dist/config/mergePipelineIncludes.js +19 -0
- package/dist/config/migrateYaml.js +6 -11
- package/dist/config/multiProjectCatalog.d.ts +32 -0
- package/dist/config/multiProjectCatalog.js +150 -0
- package/dist/config/normalizePipelineStageEntry.js +27 -1
- package/dist/config/parseCompletionContract.js +6 -0
- package/dist/config/pipelineStageKeys.js +3 -0
- package/dist/config/publishDraftPackage.d.ts +7 -0
- package/dist/config/publishDraftPackage.js +25 -0
- package/dist/config/resolveCatalogContext.js +1 -1
- package/dist/config/resolveCatalogRoots.d.ts +27 -0
- package/dist/config/resolveCatalogRoots.js +76 -0
- package/dist/config/resolveModel.d.ts +7 -1
- package/dist/config/resolveModel.js +11 -1
- package/dist/config/resolvePipelineDag.js +1 -1
- package/dist/config/resolveStageMcpServers.d.ts +9 -1
- package/dist/config/resolveStageMcpServers.js +36 -9
- package/dist/config/scanCatalogPaths.d.ts +1 -1
- package/dist/config/scanCatalogPaths.js +6 -2
- package/dist/config/secretFromEnvOrFile.d.ts +9 -0
- package/dist/config/secretFromEnvOrFile.js +34 -0
- package/dist/config/seededCatalog.d.ts +11 -0
- package/dist/config/seededCatalog.js +29 -0
- package/dist/config/stageBrowser.d.ts +12 -0
- package/dist/config/stageBrowser.js +118 -0
- package/dist/config/toolRequires.d.ts +24 -0
- package/dist/config/toolRequires.js +115 -0
- package/dist/config/triggerCatalog.d.ts +10 -0
- package/dist/config/triggerCatalog.js +37 -0
- package/dist/config/validateCatalog.d.ts +17 -3
- package/dist/config/validateCatalog.js +223 -3
- package/dist/config/validateCompletionContract.js +8 -0
- package/dist/diagnostics/checks.d.ts +39 -0
- package/dist/diagnostics/checks.js +135 -0
- package/dist/envelope/preEmitChecks.d.ts +1 -1
- package/dist/envelope/preEmitChecks.js +8 -0
- package/dist/errors/codes.d.ts +14 -0
- package/dist/errors/codes.js +59 -0
- package/dist/git/cache.d.ts +14 -0
- package/dist/git/cache.js +150 -0
- package/dist/git/credentials.d.ts +9 -0
- package/dist/git/credentials.js +88 -0
- package/dist/git/errors.d.ts +20 -0
- package/dist/git/errors.js +94 -0
- package/dist/git/exec.d.ts +16 -0
- package/dist/git/exec.js +135 -0
- package/dist/git/operations.d.ts +29 -0
- package/dist/git/operations.js +153 -0
- package/dist/git/version.d.ts +1 -0
- package/dist/git/version.js +10 -0
- package/dist/logging/audit.d.ts +16 -0
- package/dist/logging/audit.js +19 -0
- package/dist/logging/commonWords.d.ts +2 -0
- package/dist/logging/commonWords.js +13 -0
- package/dist/logging/logger.d.ts +49 -0
- package/dist/logging/logger.js +193 -0
- package/dist/logging/namedSecrets.d.ts +6 -0
- package/dist/logging/namedSecrets.js +58 -0
- package/dist/logging/redact.d.ts +17 -0
- package/dist/logging/redact.js +105 -0
- package/dist/mcp/catalogTools.js +492 -81
- package/dist/mcp/checkoutTools.d.ts +52 -0
- package/dist/mcp/checkoutTools.js +332 -0
- package/dist/mcp/controlTools.js +182 -6
- package/dist/mcp/deps.d.ts +3 -0
- package/dist/mcp/getStartedTool.d.ts +3 -0
- package/dist/mcp/getStartedTool.js +62 -0
- package/dist/mcp/readArtifact.d.ts +11 -0
- package/dist/mcp/readArtifact.js +46 -30
- package/dist/mcp/server.js +2 -0
- package/dist/mcp/tools.js +6 -0
- package/dist/mcp/triggerTools.d.ts +3 -0
- package/dist/mcp/triggerTools.js +176 -0
- package/dist/net/proxy.d.ts +14 -0
- package/dist/net/proxy.js +78 -0
- package/dist/operatorAgent/docsRetrieval.d.ts +40 -0
- package/dist/operatorAgent/docsRetrieval.js +269 -0
- package/dist/operatorAgent/draftContext.d.ts +16 -0
- package/dist/operatorAgent/draftContext.js +68 -0
- package/dist/operatorAgent/fakeHost.d.ts +39 -0
- package/dist/operatorAgent/fakeHost.js +92 -0
- package/dist/operatorAgent/host.d.ts +40 -0
- package/dist/operatorAgent/host.js +179 -0
- package/dist/operatorAgent/index.d.ts +9 -0
- package/dist/operatorAgent/index.js +8 -0
- package/dist/operatorAgent/piModel.d.ts +80 -0
- package/dist/operatorAgent/piModel.js +528 -0
- package/dist/operatorAgent/profiles/workshopAuthor.d.ts +23 -0
- package/dist/operatorAgent/profiles/workshopAuthor.js +338 -0
- package/dist/operatorAgent/profiles/workshopAuthorPlaybook.d.ts +6 -0
- package/dist/operatorAgent/profiles/workshopAuthorPlaybook.js +103 -0
- package/dist/operatorAgent/profiles/workshopAuthorTools.d.ts +57 -0
- package/dist/operatorAgent/profiles/workshopAuthorTools.js +623 -0
- package/dist/operatorAgent/proposals.d.ts +32 -0
- package/dist/operatorAgent/proposals.js +144 -0
- package/dist/operatorAgent/types.d.ts +128 -0
- package/dist/operatorAgent/types.js +1 -0
- package/dist/package-meta.d.ts +2 -1
- package/dist/package-meta.js +2 -1
- package/dist/preflight/bash.d.ts +8 -0
- package/dist/preflight/bash.js +34 -0
- package/dist/preflight/claudeRoot.d.ts +11 -0
- package/dist/preflight/claudeRoot.js +23 -0
- package/dist/preflight/mcpCommands.d.ts +8 -0
- package/dist/preflight/mcpCommands.js +36 -0
- package/dist/preflight/pipelinePreflight.d.ts +40 -0
- package/dist/preflight/pipelinePreflight.js +192 -0
- package/dist/preflight/tls.d.ts +6 -0
- package/dist/preflight/tls.js +18 -0
- package/dist/preflight/toolchain.d.ts +45 -0
- package/dist/preflight/toolchain.js +219 -0
- package/dist/project/findManifestRoot.d.ts +3 -0
- package/dist/project/findManifestRoot.js +35 -0
- package/dist/project/findProjectRoot.js +3 -4
- package/dist/project/globalHome.d.ts +7 -0
- package/dist/project/globalHome.js +63 -2
- package/dist/project/resolveProjectContext.js +2 -2
- package/dist/project/resolveStageflowContext.d.ts +7 -0
- package/dist/project/resolveStageflowContext.js +46 -30
- package/dist/projection/projectRun.d.ts +2 -1
- package/dist/projection/projectRun.js +1 -0
- package/dist/runstore/assertTmpdir.d.ts +1 -0
- package/dist/runstore/assertTmpdir.js +25 -0
- package/dist/runstore/backup.d.ts +52 -0
- package/dist/runstore/backup.js +292 -0
- package/dist/runstore/createStore.d.ts +18 -1
- package/dist/runstore/createStore.js +64 -5
- package/dist/runstore/debugBundle.d.ts +60 -0
- package/dist/runstore/debugBundle.js +143 -0
- package/dist/runstore/diskUsage.d.ts +50 -0
- package/dist/runstore/diskUsage.js +214 -0
- package/dist/runstore/exportRunPayload.d.ts +8 -0
- package/dist/runstore/exportRunPayload.js +23 -0
- package/dist/runstore/normalizeCatalogPath.d.ts +1 -0
- package/dist/runstore/normalizeCatalogPath.js +10 -0
- package/dist/runstore/paths.d.ts +5 -0
- package/dist/runstore/paths.js +145 -1
- package/dist/runstore/port.d.ts +139 -13
- package/dist/runstore/port.js +8 -1
- package/dist/runstore/restore.d.ts +81 -0
- package/dist/runstore/restore.js +355 -0
- package/dist/runstore/retention.d.ts +27 -0
- package/dist/runstore/retention.js +74 -0
- package/dist/runstore/runManifest.d.ts +146 -0
- package/dist/runstore/runManifest.js +288 -0
- package/dist/runstore/runProjection.d.ts +3 -1
- package/dist/runstore/runProjection.js +63 -1
- package/dist/runstore/sqlite/SqliteRunStore.d.ts +35 -3
- package/dist/runstore/sqlite/SqliteRunStore.js +442 -219
- package/dist/runstore/sqlite/applyStorePragmas.d.ts +39 -0
- package/dist/runstore/sqlite/applyStorePragmas.js +145 -0
- package/dist/runstore/sqlite/migrations/001-baseline.d.ts +7 -0
- package/dist/runstore/sqlite/migrations/001-baseline.js +10 -0
- package/dist/runstore/sqlite/migrations/002-repository-binding.d.ts +7 -0
- package/dist/runstore/sqlite/migrations/002-repository-binding.js +29 -0
- package/dist/runstore/sqlite/migrations/003-run-lifecycle.d.ts +7 -0
- package/dist/runstore/sqlite/migrations/003-run-lifecycle.js +28 -0
- package/dist/runstore/sqlite/migrations/004-auto-resume-count.d.ts +7 -0
- package/dist/runstore/sqlite/migrations/004-auto-resume-count.js +19 -0
- package/dist/runstore/sqlite/migrations/005-config-origins.d.ts +7 -0
- package/dist/runstore/sqlite/migrations/005-config-origins.js +19 -0
- package/dist/runstore/sqlite/migrations/006-pipeline-body-and-caller.d.ts +7 -0
- package/dist/runstore/sqlite/migrations/006-pipeline-body-and-caller.js +28 -0
- package/dist/runstore/sqlite/migrations/007-projects-registry.d.ts +7 -0
- package/dist/runstore/sqlite/migrations/007-projects-registry.js +31 -0
- package/dist/runstore/sqlite/migrations/008-triggers-table.d.ts +7 -0
- package/dist/runstore/sqlite/migrations/008-triggers-table.js +19 -0
- package/dist/runstore/sqlite/migrations/009-trigger-next-run.d.ts +7 -0
- package/dist/runstore/sqlite/migrations/009-trigger-next-run.js +19 -0
- package/dist/runstore/sqlite/migrations/010-trigger-adapter-state.d.ts +7 -0
- package/dist/runstore/sqlite/migrations/010-trigger-adapter-state.js +17 -0
- package/dist/runstore/sqlite/migrations/index.d.ts +21 -0
- package/dist/runstore/sqlite/migrations/index.js +78 -0
- package/dist/runstore/sqlite/schema.d.ts +20 -1
- package/dist/runstore/sqlite/schema.js +277 -1
- package/dist/runstore/sqlite/storeOpenError.d.ts +6 -0
- package/dist/runstore/sqlite/storeOpenError.js +11 -0
- package/dist/runstore/sqlite/storeSchemaError.d.ts +5 -0
- package/dist/runstore/sqlite/storeSchemaError.js +8 -0
- package/dist/runstore/stageExecution.js +3 -0
- package/dist/runstore/storeFilesystem.d.ts +32 -0
- package/dist/runstore/storeFilesystem.js +148 -0
- package/dist/runstore/trackProjection.js +2 -0
- package/dist/runstore/workspaceLayout.d.ts +13 -0
- package/dist/runstore/workspaceLayout.js +76 -0
- package/dist/runtime/answerResume.d.ts +1 -0
- package/dist/runtime/answerResume.js +3 -1
- package/dist/runtime/completionCheckRunner.d.ts +15 -1
- package/dist/runtime/completionCheckRunner.js +67 -7
- package/dist/runtime/containerLimits.d.ts +16 -0
- package/dist/runtime/containerLimits.js +84 -0
- package/dist/runtime/credentialBinding.d.ts +2 -0
- package/dist/runtime/credentialBinding.js +3 -0
- package/dist/runtime/credentialMaterialisation.d.ts +28 -0
- package/dist/runtime/credentialMaterialisation.js +128 -0
- package/dist/runtime/emailSource.d.ts +72 -0
- package/dist/runtime/emailSource.js +304 -0
- package/dist/runtime/gitCheckoutCapability.js +7 -13
- package/dist/runtime/githubPollSource.d.ts +86 -0
- package/dist/runtime/githubPollSource.js +284 -0
- package/dist/runtime/pipelineRunner.d.ts +40 -1
- package/dist/runtime/pipelineRunner.js +181 -17
- package/dist/runtime/pipelineScheduler.d.ts +17 -0
- package/dist/runtime/pipelineScheduler.js +220 -28
- package/dist/runtime/reloadRunCatalog.js +6 -1
- package/dist/runtime/repositoryMaterialize.d.ts +47 -0
- package/dist/runtime/repositoryMaterialize.js +235 -0
- package/dist/runtime/resumeTimedOut.d.ts +1 -1
- package/dist/runtime/resumeTimedOut.js +5 -2
- package/dist/runtime/runChangeBus.js +9 -0
- package/dist/runtime/runDeletion.d.ts +7 -0
- package/dist/runtime/runDeletion.js +37 -0
- package/dist/runtime/runManager.d.ts +169 -4
- package/dist/runtime/runManager.js +1748 -83
- package/dist/runtime/runRetentionSweep.d.ts +31 -0
- package/dist/runtime/runRetentionSweep.js +328 -0
- package/dist/runtime/runRetryCoordinator.d.ts +4 -0
- package/dist/runtime/runRetryCoordinator.js +20 -2
- package/dist/runtime/runSkills.d.ts +40 -0
- package/dist/runtime/runSkills.js +173 -0
- package/dist/runtime/scheduleSource.d.ts +38 -0
- package/dist/runtime/scheduleSource.js +119 -0
- package/dist/runtime/settingsFile.d.ts +5 -0
- package/dist/runtime/settingsFile.js +61 -54
- package/dist/runtime/stageAttemptBootstrap.d.ts +5 -1
- package/dist/runtime/stageAttemptBootstrap.js +250 -27
- package/dist/runtime/stageCacheEnv.d.ts +6 -0
- package/dist/runtime/stageCacheEnv.js +34 -0
- package/dist/runtime/stageConcurrency.d.ts +2 -1
- package/dist/runtime/stageConcurrency.js +4 -2
- package/dist/runtime/stageEnvironment.d.ts +38 -0
- package/dist/runtime/stageEnvironment.js +194 -0
- package/dist/runtime/stageHitl.d.ts +1 -0
- package/dist/runtime/stageHitl.js +5 -1
- package/dist/runtime/stageProcessLauncher.d.ts +20 -0
- package/dist/runtime/stageProcessLauncher.js +236 -41
- package/dist/runtime/stageRecovery.d.ts +3 -1
- package/dist/runtime/stageRecovery.js +19 -4
- package/dist/runtime/stageRoots.d.ts +38 -1
- package/dist/runtime/stageRoots.js +118 -0
- package/dist/runtime/stageRunner.d.ts +6 -0
- package/dist/runtime/stageRunner.js +32 -6
- package/dist/runtime/stageSecretDecl.d.ts +9 -0
- package/dist/runtime/stageSecretDecl.js +102 -0
- package/dist/runtime/stageSecrets.d.ts +40 -0
- package/dist/runtime/stageSecrets.js +181 -0
- package/dist/runtime/stageStreamLog.d.ts +1 -0
- package/dist/runtime/stageStreamLog.js +2 -1
- package/dist/runtime/stageWorker.d.ts +3 -2
- package/dist/runtime/stageWorker.js +58 -10
- package/dist/runtime/stageWorkerProtocol.d.ts +5 -0
- package/dist/runtime/stageWorkerProtocol.js +8 -0
- package/dist/runtime/startPayload.d.ts +32 -0
- package/dist/runtime/startPayload.js +59 -0
- package/dist/runtime/taskInput.js +14 -5
- package/dist/runtime/triggerCatalog.d.ts +6 -0
- package/dist/runtime/triggerCatalog.js +24 -0
- package/dist/runtime/triggerEventMatch.d.ts +2 -0
- package/dist/runtime/triggerEventMatch.js +6 -0
- package/dist/runtime/triggerPort.d.ts +14 -0
- package/dist/runtime/triggerPort.js +1 -0
- package/dist/runtime/triggerRunner.d.ts +17 -0
- package/dist/runtime/triggerRunner.js +89 -0
- package/dist/runtime/verifiedStageExecution.d.ts +8 -0
- package/dist/runtime/verifiedStageExecution.js +16 -1
- package/dist/runtime/webhookSignature.d.ts +1 -0
- package/dist/runtime/webhookSignature.js +12 -0
- package/dist/runtime/workspaceBinding.d.ts +20 -0
- package/dist/runtime/workspaceBinding.js +60 -0
- package/dist/server/allowedHosts.d.ts +19 -0
- package/dist/server/allowedHosts.js +179 -0
- package/dist/server/bootstrap.d.ts +42 -2
- package/dist/server/bootstrap.js +273 -5
- package/dist/server/controlToken.d.ts +47 -0
- package/dist/server/controlToken.js +254 -0
- package/dist/server/createHttpHost.d.ts +11 -2
- package/dist/server/createHttpHost.js +106 -10
- package/dist/server/ensureGlobalService.d.ts +5 -1
- package/dist/server/ensureGlobalService.js +35 -1
- package/dist/server/healthSurfaces.d.ts +6 -0
- package/dist/server/healthSurfaces.js +123 -0
- package/dist/server/http.d.ts +27 -1
- package/dist/server/http.js +2254 -477
- package/dist/server/listenHost.d.ts +5 -0
- package/dist/server/listenHost.js +56 -0
- package/dist/server/mcpHost.d.ts +10 -1
- package/dist/server/mcpHost.js +36 -6
- package/dist/server/operatorResults.d.ts +2 -2
- package/dist/server/operatorResults.js +40 -29
- package/dist/server/requestAuthContext.d.ts +12 -0
- package/dist/server/requestAuthContext.js +21 -0
- package/dist/server/shutdown.d.ts +69 -0
- package/dist/server/shutdown.js +219 -0
- package/dist/server/triggerRoutes.d.ts +15 -0
- package/dist/server/triggerRoutes.js +268 -0
- package/dist/tools/askOperator.d.ts +3 -2
- package/dist/types/completion.d.ts +3 -0
- package/dist/types/pipeline.d.ts +10 -1
- package/dist/types/preEmitCheck.d.ts +7 -0
- package/dist/types/stage.d.ts +22 -0
- package/dist/types/stageflowManifest.d.ts +3 -0
- package/dist/types/task.d.ts +8 -0
- package/dist/types/trigger.d.ts +18 -0
- package/dist/types/trigger.js +1 -0
- package/dist/ui/assets/{index-CSsDued1.css → index-Bv4p1aPK.css} +1 -1
- package/dist/ui/assets/index-CntB4bZR.js +133 -0
- package/dist/ui/index.html +2 -2
- package/dist/workshop/autosave.d.ts +58 -0
- package/dist/workshop/autosave.js +222 -0
- package/dist/workshop/buildStore.d.ts +79 -0
- package/dist/workshop/buildStore.js +326 -0
- package/dist/workshop/chatTurn.d.ts +232 -0
- package/dist/workshop/chatTurn.js +475 -0
- package/dist/workshop/modelSettings.d.ts +18 -0
- package/dist/workshop/modelSettings.js +44 -0
- package/dist/workshop/sessionStore.d.ts +61 -0
- package/dist/workshop/sessionStore.js +254 -0
- package/dist/workshop/workshopPin.d.ts +72 -0
- package/dist/workshop/workshopPin.js +193 -0
- package/examples/README.md +99 -0
- package/examples/archify-on-pr/README.md +213 -0
- package/examples/archify-on-pr/archify-on-pr.pipeline.yaml +19 -0
- package/examples/archify-on-pr/archify-on-pr.task.yaml +19 -0
- package/examples/archify-on-pr/author-diagrams.yaml +106 -0
- package/examples/archify-on-pr/detect-changes.yaml +89 -0
- package/examples/browser-session/README.md +77 -0
- package/examples/browser-session/browser-session.pipeline.yaml +50 -0
- package/examples/browser-session/browser-session.task.yaml +6 -0
- package/examples/browser-session/check-login.yaml +22 -0
- package/examples/browser-session/fixture-server.mjs +64 -0
- package/examples/browser-session/human-login.yaml +31 -0
- package/examples/browser-session/merge.yaml +36 -0
- package/examples/browser-session/work-a.yaml +41 -0
- package/examples/browser-session/work-b.yaml +41 -0
- package/examples/ci-validate/README.md +35 -0
- package/examples/ci-validate/check.yaml +13 -0
- package/examples/ci-validate/ci-demo.pipeline.yaml +4 -0
- package/examples/ci-validate/validate.sh +5 -0
- package/examples/compliance-review-demo/README.md +125 -0
- package/examples/compliance-review-demo/compliance-review-demo.pipeline.yaml +29 -0
- package/examples/compliance-review-demo/connect-account.yaml +47 -0
- package/examples/compliance-review-demo/map-to-control.yaml +123 -0
- package/examples/compliance-review-demo/poll-bucket-config.yaml +103 -0
- package/examples/compliance-review-demo/publish-report.yaml +79 -0
- package/examples/compliance-review-demo/review-verdict.yaml +122 -0
- package/examples/compliance-review-demo/run-compliance-check.yaml +96 -0
- package/examples/compliance-review-demo/store-evidence.yaml +143 -0
- package/examples/conditional-fork/README.md +89 -0
- package/examples/conditional-fork/branch-a.yaml +13 -0
- package/examples/conditional-fork/branch-b.yaml +13 -0
- package/examples/conditional-fork/decide.yaml +35 -0
- package/examples/conditional-fork/fork-demo.pipeline.yaml +20 -0
- package/examples/conditional-fork/fork-demo.task.yaml +8 -0
- package/examples/context7-mcp/README.md +42 -0
- package/examples/context7-mcp/context7-mcp.pipeline.yaml +15 -0
- package/examples/context7-mcp/context7-mcp.task.yaml +4 -0
- package/examples/context7-mcp/fetch-docs.yaml +52 -0
- package/examples/context7-mcp/resolve-library.yaml +31 -0
- package/examples/context7-mcp/write-brief.yaml +52 -0
- package/examples/feature-loop/README.md +104 -0
- package/examples/feature-loop/address-feedback.yaml +148 -0
- package/examples/feature-loop/align.yaml +116 -0
- package/examples/feature-loop/decompose.yaml +86 -0
- package/examples/feature-loop/default-model-hierarchy.task.yaml +58 -0
- package/examples/feature-loop/feature-loop.pipeline.yaml +140 -0
- package/examples/feature-loop/feature-loop.task.yaml +25 -0
- package/examples/feature-loop/implement.yaml +128 -0
- package/examples/feature-loop/plan.yaml +111 -0
- package/examples/feature-loop/publish.yaml +167 -0
- package/examples/feature-loop/review.yaml +146 -0
- package/examples/feature-loop/verify.yaml +106 -0
- package/examples/feedback-loop/README.md +72 -0
- package/examples/feedback-loop/VERIFY.md +208 -0
- package/examples/feedback-loop/continue-only.task.yaml +4 -0
- package/examples/feedback-loop/exhaust-require-continue.task.yaml +6 -0
- package/examples/feedback-loop/exhaust-wait-human.task.yaml +11 -0
- package/examples/feedback-loop/feedback-loop-wait-human.pipeline.yaml +23 -0
- package/examples/feedback-loop/feedback-loop.pipeline.yaml +23 -0
- package/examples/feedback-loop/feedback-loop.task.yaml +4 -0
- package/examples/feedback-loop/implement.yaml +14 -0
- package/examples/feedback-loop/plan.yaml +14 -0
- package/examples/feedback-loop/review.yaml +15 -0
- package/examples/feedback-loop/send-back-once.task.yaml +6 -0
- package/examples/feedback-loop/submit.yaml +12 -0
- package/examples/generic-fan-in/README.md +151 -0
- package/examples/generic-fan-in/clarify.yaml +18 -0
- package/examples/generic-fan-in/generic-fan-in-accepted.pipeline.yaml +19 -0
- package/examples/generic-fan-in/generic-fan-in-accepted.task.yaml +8 -0
- package/examples/generic-fan-in/generic-fan-in.pipeline.yaml +18 -0
- package/examples/generic-fan-in/generic-fan-in.task.yaml +9 -0
- package/examples/generic-fan-in/research.yaml +45 -0
- package/examples/generic-fan-in/synthesize.yaml +66 -0
- package/examples/generic-fan-in/validation.yaml +45 -0
- package/examples/github-release/README.md +78 -0
- package/examples/github-release/draft-release-notes.yaml +64 -0
- package/examples/github-release/github-release.pipeline.yaml +11 -0
- package/examples/github-release/github-release.task.yaml +13 -0
- package/examples/github-release/publish-github-release.yaml +64 -0
- package/examples/hello-world/README.md +28 -0
- package/examples/hello-world/hello.pipeline.yaml +4 -0
- package/examples/hello-world/my-task.task.yaml +6 -0
- package/examples/hello-world/research.yaml +18 -0
- package/examples/mcp-hitl-tour/README.md +113 -0
- package/examples/mcp-hitl-tour/draft-angle.yaml +33 -0
- package/examples/mcp-hitl-tour/draft-short.yaml +32 -0
- package/examples/mcp-hitl-tour/emit-angles.yaml +45 -0
- package/examples/mcp-hitl-tour/gather.yaml +31 -0
- package/examples/mcp-hitl-tour/intake.yaml +62 -0
- package/examples/mcp-hitl-tour/mcp-hitl-tour-exhaust.task.yaml +13 -0
- package/examples/mcp-hitl-tour/mcp-hitl-tour.pipeline.yaml +54 -0
- package/examples/mcp-hitl-tour/mcp-hitl-tour.task.yaml +16 -0
- package/examples/mcp-hitl-tour/polish.yaml +33 -0
- package/examples/mcp-hitl-tour/review.yaml +17 -0
- package/examples/mcp-hitl-tour/ship.yaml +25 -0
- package/examples/oss-issue-contribution/README.md +218 -0
- package/examples/oss-issue-contribution/oss-address-review-feedback.yaml +117 -0
- package/examples/oss-issue-contribution/oss-approve-contribution.yaml +181 -0
- package/examples/oss-issue-contribution/oss-approve-plan.yaml +111 -0
- package/examples/oss-issue-contribution/oss-explain-fix.yaml +108 -0
- package/examples/oss-issue-contribution/oss-explain-issue.yaml +114 -0
- package/examples/oss-issue-contribution/oss-implement-source-fix.yaml +171 -0
- package/examples/oss-issue-contribution/oss-investigate-area.yaml +116 -0
- package/examples/oss-issue-contribution/oss-issue-contribution.pipeline.yaml +125 -0
- package/examples/oss-issue-contribution/oss-issue-intake.yaml +106 -0
- package/examples/oss-issue-contribution/oss-plan-investigation.yaml +112 -0
- package/examples/oss-issue-contribution/oss-plan-review.yaml +109 -0
- package/examples/oss-issue-contribution/oss-reproduce-issue.yaml +140 -0
- package/examples/oss-issue-contribution/oss-review-change.yaml +107 -0
- package/examples/oss-issue-contribution/oss-verify-fix.yaml +130 -0
- package/examples/oss-issue-contribution/oss-write-regression-test.yaml +135 -0
- package/examples/oss-issue-contribution/pi-7629.task.yaml +79 -0
- package/examples/oss-issue-contribution/pi-8919.task.yaml +75 -0
- package/examples/oss-issue-contribution/pydantic-ai-7971.task.yaml +53 -0
- package/examples/oss-quick-fix/README.md +65 -0
- package/examples/oss-quick-fix/implement-fix.yaml +84 -0
- package/examples/oss-quick-fix/oss-quick-fix.pipeline.yaml +35 -0
- package/examples/oss-quick-fix/oss-quick-fix.task.yaml +19 -0
- package/examples/oss-quick-fix/ship-fix.yaml +81 -0
- package/examples/oss-quick-fix/understand-issue.yaml +67 -0
- package/examples/oss-quick-fix/verify-fix.yaml +73 -0
- package/examples/plan-review/README.md +37 -0
- package/examples/plan-review/plan-review-followup.yaml +13 -0
- package/examples/plan-review/plan-review.pipeline.yaml +9 -0
- package/examples/plan-review/plan-review.task.yaml +9 -0
- package/examples/plan-review/plan-review.yaml +25 -0
- package/examples/playwright-mcp/README.md +39 -0
- package/examples/playwright-mcp/playwright-mcp.pipeline.yaml +5 -0
- package/examples/playwright-mcp/playwright-mcp.task.yaml +4 -0
- package/examples/playwright-mcp/screenshot.yaml +40 -0
- package/examples/retry-tour/README.md +75 -0
- package/examples/retry-tour/retry-tour.pipeline.yaml +122 -0
- package/examples/retry-tour/retry-tour.task.yaml +9 -0
- package/examples/route-if-tour/README.md +202 -0
- package/examples/route-if-tour/route-if-tour.pipeline.yaml +950 -0
- package/examples/route-if-tour/route-if-tour.task.yaml +4 -0
- package/examples/route-wiring-smoke-test/01-core-routing.pipeline.yaml +168 -0
- package/examples/route-wiring-smoke-test/02-on-gating.pipeline.yaml +44 -0
- package/examples/route-wiring-smoke-test/03-two-entry-points.pipeline.yaml +28 -0
- package/examples/route-wiring-smoke-test/04-entry-false-equivalent-to-omitted.pipeline.yaml +27 -0
- package/examples/route-wiring-smoke-test/05-fork-choice.pipeline.yaml +110 -0
- package/examples/route-wiring-smoke-test/06-fan-out-fan-in.pipeline.yaml +82 -0
- package/examples/route-wiring-smoke-test/07-loop-human-decision.pipeline.yaml +82 -0
- package/examples/route-wiring-smoke-test/08-skip-fallback-and-multi-on-gate.pipeline.yaml +77 -0
- package/examples/route-wiring-smoke-test/09-multi-loop-targets.pipeline.yaml +102 -0
- package/examples/route-wiring-smoke-test/10-uses-dialect-fork.pipeline.yaml +23 -0
- package/examples/route-wiring-smoke-test/11-sequential-io-handoff.pipeline.yaml +37 -0
- package/examples/route-wiring-smoke-test/12-complex-io-schemas.pipeline.yaml +138 -0
- package/examples/route-wiring-smoke-test/13-ref-io-handoff.pipeline.yaml +84 -0
- package/examples/route-wiring-smoke-test/14-if-eq-gating.pipeline.yaml +50 -0
- package/examples/route-wiring-smoke-test/15-if-all-gated.pipeline.yaml +37 -0
- package/examples/route-wiring-smoke-test/16-if-composition.pipeline.yaml +65 -0
- package/examples/route-wiring-smoke-test/17-if-join.pipeline.yaml +80 -0
- package/examples/route-wiring-smoke-test/README.md +377 -0
- package/examples/route-wiring-smoke-test/rejected/01-reject-legacy-needs.pipeline.yaml +26 -0
- package/examples/route-wiring-smoke-test/rejected/02-reject-legacy-fork.pipeline.yaml +25 -0
- package/examples/route-wiring-smoke-test/rejected/03-reject-legacy-feedback-loop.pipeline.yaml +30 -0
- package/examples/route-wiring-smoke-test/rejected/04-reject-missing-entry.pipeline.yaml +24 -0
- package/examples/route-wiring-smoke-test/rejected/05-reject-unreachable.pipeline.yaml +35 -0
- package/examples/route-wiring-smoke-test/rejected/06-reject-allow-none-without-route-select.pipeline.yaml +37 -0
- package/examples/route-wiring-smoke-test/rejected/07-reject-cycle.pipeline.yaml +27 -0
- package/examples/route-wiring-smoke-test/rejected/08-reject-loop-non-ancestor.pipeline.yaml +42 -0
- package/examples/route-wiring-smoke-test/rejected/09-reject-route-select-leaf.pipeline.yaml +26 -0
- package/examples/route-wiring-smoke-test/rejected/12-reject-loop-replay-unsafe.pipeline.yaml +55 -0
- package/examples/route-wiring-smoke-test/rejected/13-reject-multiple-loop-entries.pipeline.yaml +51 -0
- package/examples/route-wiring-smoke-test/rejected/14-reject-duplicate-stage-id.pipeline.yaml +35 -0
- package/examples/route-wiring-smoke-test/rejected/15-reject-unknown-route-target.pipeline.yaml +15 -0
- package/examples/route-wiring-smoke-test/rejected/16-reject-malformed-route-item.pipeline.yaml +15 -0
- package/examples/route-wiring-smoke-test/rejected/17-reject-io-incompatible.pipeline.yaml +33 -0
- package/examples/route-wiring-smoke-test/rejected/18-reject-nested-io.pipeline.yaml +51 -0
- package/examples/route-wiring-smoke-test/rejected/19-reject-array-item-io.pipeline.yaml +55 -0
- package/examples/route-wiring-smoke-test/rejected/20-reject-closed-io.pipeline.yaml +36 -0
- package/examples/route-wiring-smoke-test/rejected/21-reject-ref-io.pipeline.yaml +42 -0
- package/examples/route-wiring-smoke-test/rejected/22-reject-if-unknown-field.pipeline.yaml +33 -0
- package/examples/route-wiring-smoke-test/rejected/23-reject-if-optional-field.pipeline.yaml +32 -0
- package/examples/route-wiring-smoke-test/rejected/24-reject-if-empty-all.pipeline.yaml +31 -0
- package/examples/route-wiring-smoke-test/rejected/25-reject-if-gt-on-string.pipeline.yaml +33 -0
- package/examples/route-wiring-smoke-test/rejected/26-reject-if-optional-nested.pipeline.yaml +36 -0
- package/examples/route-wiring-smoke-test/rejected/27-reject-if-on-loop.pipeline.yaml +63 -0
- package/examples/route-wiring-smoke-test/rejected/29-reject-if-on-failed.pipeline.yaml +46 -0
- package/examples/route-wiring-smoke-test/smoke-test.task.yaml +4 -0
- package/examples/route-wiring-smoke-test/stages/branch-a.yaml +12 -0
- package/examples/route-wiring-smoke-test/stages/branch-b.yaml +12 -0
- package/examples/route-wiring-smoke-test/stages/decide.yaml +12 -0
- package/examples/route-wiring-smoke-test/stages/finish.yaml +12 -0
- package/examples/ship-feature/README.md +190 -0
- package/examples/ship-feature/feat-address-feedback.yaml +119 -0
- package/examples/ship-feature/feat-implement.yaml +180 -0
- package/examples/ship-feature/feat-plan.yaml +120 -0
- package/examples/ship-feature/feat-review.yaml +109 -0
- package/examples/ship-feature/feat-ship.yaml +203 -0
- package/examples/ship-feature/ship-feature.pipeline.yaml +44 -0
- package/examples/ship-feature/ship-feature.task.yaml +78 -0
- package/examples/stage-mcp/README.md +145 -0
- package/examples/stage-mcp/echo-mcp.mjs +20 -0
- package/examples/stage-mcp/stage-mcp-dead.pipeline.yaml +5 -0
- package/examples/stage-mcp/stage-mcp-dead.task.yaml +3 -0
- package/examples/stage-mcp/stage-mcp-missing-var.pipeline.yaml +5 -0
- package/examples/stage-mcp/stage-mcp-missing-var.task.yaml +3 -0
- package/examples/stage-mcp/stage-mcp.pipeline.yaml +5 -0
- package/examples/stage-mcp/stage-mcp.task.yaml +4 -0
- package/examples/stage-mcp/use-echo.yaml +20 -0
- package/examples/stageflow.yaml +51 -0
- package/examples/verify-tour/README.md +80 -0
- package/examples/verify-tour/verify-tour.pipeline.yaml +162 -0
- package/examples/verify-tour/verify-tour.task.yaml +11 -0
- package/package.json +9 -1
- package/skills/stageflow/SKILL.md +6 -6
- package/skills/stageflow/references/control-surface.md +13 -5
- package/skills/stageflow/scripts/detect-host.mjs +14 -1
- package/skills/stageflow/scripts/detect-host.test.mjs +29 -1
- package/skills/stageflow-author/SKILL.md +4 -3
- package/skills/stageflow-author/references/catalog-mapping.md +16 -0
- package/skills/stageflow-author/references/catalog-write-conventions.md +1 -1
- package/skills/stageflow-author/references/stage-prompt-template.md +4 -4
- package/skills/stageflow-run/SKILL.md +22 -12
- package/skills/stageflow-run/references/mcp-call.md +9 -1
- package/skills/stageflow-run/references/task-and-pipeline-selection.md +19 -6
- package/skills/stageflow-run/scripts/mcp-call.mjs +46 -6
- package/skills/stageflow-run/scripts/mcp-call.test.mjs +64 -0
- package/skills/stageflow-session-capture/references/catalog-authoring.md +2 -0
- package/skills/stageflow-setup/SKILL.md +51 -8
- package/dist/ui/assets/index-Da5EHU6M.js +0 -119
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { A2aStore } from "../a2a/store.js";
|
|
2
|
+
import { type RetentionWindows } from "../runstore/retention.js";
|
|
3
|
+
import type { RunStore } from "../runstore/port.js";
|
|
4
|
+
export type RetentionSweepReport = {
|
|
5
|
+
slimmed: string[];
|
|
6
|
+
purged: string[];
|
|
7
|
+
bareCachesEvicted: string[];
|
|
8
|
+
};
|
|
9
|
+
export type RunRetentionSweepOptions = {
|
|
10
|
+
now?: Date;
|
|
11
|
+
/** When false (default), compute candidates but skip mutating calls. */
|
|
12
|
+
execute?: boolean;
|
|
13
|
+
windows?: RetentionWindows;
|
|
14
|
+
env?: NodeJS.ProcessEnv;
|
|
15
|
+
artifactMaxBytes?: number;
|
|
16
|
+
bareCacheTtlMs?: number;
|
|
17
|
+
};
|
|
18
|
+
export declare function getSlimWalkCallCount(): number;
|
|
19
|
+
export declare function resetSlimWalkCallCount(): void;
|
|
20
|
+
/**
|
|
21
|
+
* Walk attempt + legacy artifact trees and remove SLIM-eligible paths.
|
|
22
|
+
* Never removes envelope.json, log.jsonl, or under-threshold artifacts.
|
|
23
|
+
*/
|
|
24
|
+
export declare function slimRunWorkspaceArtifacts(workspaceDir: string, maxBytes: number): Promise<void>;
|
|
25
|
+
/**
|
|
26
|
+
* Two-stage retention: SLIM then PURGE, then bare-cache eviction.
|
|
27
|
+
* Dry-run (`execute: false`) computes the same candidates and skips mutations.
|
|
28
|
+
*/
|
|
29
|
+
export declare function runRetentionSweep(store: RunStore, a2aStore: A2aStore | undefined, options?: RunRetentionSweepOptions): Promise<RetentionSweepReport>;
|
|
30
|
+
/** Test helper: expose bare-cache path resolution used by the eviction pass. */
|
|
31
|
+
export declare function bareCachePathForRepository(repository: string): string;
|
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
import { readdir, rm, stat } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { bareCachePath } from "../git/cache.js";
|
|
4
|
+
import { globalStageflowHome } from "../project/globalHome.js";
|
|
5
|
+
import { bareCacheTtlFromEnv, isTerminalRunStatus, retentionDecision, retentionWindowsFromEnv, slimArtifactMaxBytesFromEnv, } from "../runstore/retention.js";
|
|
6
|
+
import { reclaimWorkspaceBinding, worktreePathForRun, } from "./repositoryMaterialize.js";
|
|
7
|
+
import { deleteRunEverywhere } from "./runDeletion.js";
|
|
8
|
+
const SLIM_ATTEMPT_FILES = ["pi-session.jsonl", "stream.log"];
|
|
9
|
+
const SLIM_ATTEMPT_DIRS = [".pi-agent"];
|
|
10
|
+
let slimWalkCallCount = 0;
|
|
11
|
+
export function getSlimWalkCallCount() {
|
|
12
|
+
return slimWalkCallCount;
|
|
13
|
+
}
|
|
14
|
+
export function resetSlimWalkCallCount() {
|
|
15
|
+
slimWalkCallCount = 0;
|
|
16
|
+
}
|
|
17
|
+
async function pathExists(target) {
|
|
18
|
+
try {
|
|
19
|
+
await stat(target);
|
|
20
|
+
return true;
|
|
21
|
+
}
|
|
22
|
+
catch (err) {
|
|
23
|
+
if (err.code === "ENOENT")
|
|
24
|
+
return false;
|
|
25
|
+
throw err;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
async function rmForce(target) {
|
|
29
|
+
try {
|
|
30
|
+
await rm(target, { recursive: true, force: true });
|
|
31
|
+
}
|
|
32
|
+
catch (err) {
|
|
33
|
+
console.error(`runRetentionSweep: rm failed for ${target}: ${err instanceof Error ? err.message : String(err)}`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
async function removeOversizedArtifacts(artifactsDir, maxBytes) {
|
|
37
|
+
if (!(await pathExists(artifactsDir)))
|
|
38
|
+
return;
|
|
39
|
+
async function walk(current) {
|
|
40
|
+
let entries;
|
|
41
|
+
try {
|
|
42
|
+
entries = await readdir(current, { withFileTypes: true });
|
|
43
|
+
}
|
|
44
|
+
catch (err) {
|
|
45
|
+
if (err.code === "ENOENT")
|
|
46
|
+
return;
|
|
47
|
+
throw err;
|
|
48
|
+
}
|
|
49
|
+
for (const entry of entries) {
|
|
50
|
+
const full = path.join(current, entry.name);
|
|
51
|
+
if (entry.isDirectory()) {
|
|
52
|
+
await walk(full);
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
if (!entry.isFile())
|
|
56
|
+
continue;
|
|
57
|
+
let st;
|
|
58
|
+
try {
|
|
59
|
+
st = await stat(full);
|
|
60
|
+
}
|
|
61
|
+
catch (err) {
|
|
62
|
+
if (err.code === "ENOENT")
|
|
63
|
+
continue;
|
|
64
|
+
throw err;
|
|
65
|
+
}
|
|
66
|
+
if (st.size > maxBytes) {
|
|
67
|
+
await rmForce(full);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
await walk(artifactsDir);
|
|
72
|
+
}
|
|
73
|
+
async function slimAttemptDir(attemptDir, maxBytes) {
|
|
74
|
+
for (const name of SLIM_ATTEMPT_DIRS) {
|
|
75
|
+
await rmForce(path.join(attemptDir, name));
|
|
76
|
+
}
|
|
77
|
+
for (const name of SLIM_ATTEMPT_FILES) {
|
|
78
|
+
await rmForce(path.join(attemptDir, name));
|
|
79
|
+
}
|
|
80
|
+
await removeOversizedArtifacts(path.join(attemptDir, "artifacts"), maxBytes);
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Walk attempt + legacy artifact trees and remove SLIM-eligible paths.
|
|
84
|
+
* Never removes envelope.json, log.jsonl, or under-threshold artifacts.
|
|
85
|
+
*/
|
|
86
|
+
export async function slimRunWorkspaceArtifacts(workspaceDir, maxBytes) {
|
|
87
|
+
slimWalkCallCount += 1;
|
|
88
|
+
const stagesRoot = path.join(workspaceDir, "stages");
|
|
89
|
+
if (!(await pathExists(stagesRoot)))
|
|
90
|
+
return;
|
|
91
|
+
let stageEntries;
|
|
92
|
+
try {
|
|
93
|
+
stageEntries = await readdir(stagesRoot, { withFileTypes: true });
|
|
94
|
+
}
|
|
95
|
+
catch (err) {
|
|
96
|
+
if (err.code === "ENOENT")
|
|
97
|
+
return;
|
|
98
|
+
throw err;
|
|
99
|
+
}
|
|
100
|
+
for (const stageEntry of stageEntries) {
|
|
101
|
+
if (!stageEntry.isDirectory())
|
|
102
|
+
continue;
|
|
103
|
+
const stageDir = path.join(stagesRoot, stageEntry.name);
|
|
104
|
+
const attemptsRoot = path.join(stageDir, "attempts");
|
|
105
|
+
if (await pathExists(attemptsRoot)) {
|
|
106
|
+
let attemptEntries;
|
|
107
|
+
try {
|
|
108
|
+
attemptEntries = await readdir(attemptsRoot, { withFileTypes: true });
|
|
109
|
+
}
|
|
110
|
+
catch (err) {
|
|
111
|
+
if (err.code === "ENOENT")
|
|
112
|
+
continue;
|
|
113
|
+
throw err;
|
|
114
|
+
}
|
|
115
|
+
for (const attemptEntry of attemptEntries) {
|
|
116
|
+
if (!attemptEntry.isDirectory())
|
|
117
|
+
continue;
|
|
118
|
+
await slimAttemptDir(path.join(attemptsRoot, attemptEntry.name), maxBytes);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
await removeOversizedArtifacts(path.join(stageDir, "artifacts"), maxBytes);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
async function slimOneRun(store, meta, now, artifactMaxBytes) {
|
|
125
|
+
try {
|
|
126
|
+
await reclaimWorkspaceBinding(meta, { keepRunBranch: true });
|
|
127
|
+
}
|
|
128
|
+
catch (err) {
|
|
129
|
+
console.error(`runRetentionSweep: worktree reclaim failed for ${meta.run_id}: ${err instanceof Error ? err.message : String(err)}`);
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
132
|
+
const workspaceDir = store.getWorkspaceDir(meta.run_id);
|
|
133
|
+
try {
|
|
134
|
+
await slimRunWorkspaceArtifacts(workspaceDir, artifactMaxBytes);
|
|
135
|
+
}
|
|
136
|
+
catch (err) {
|
|
137
|
+
console.error(`runRetentionSweep: artifact slim failed for ${meta.run_id}: ${err instanceof Error ? err.message : String(err)}`);
|
|
138
|
+
return false;
|
|
139
|
+
}
|
|
140
|
+
await store.setSlimmedAt(meta.run_id, now.toISOString());
|
|
141
|
+
return true;
|
|
142
|
+
}
|
|
143
|
+
async function listBareCachePaths(reposRoot) {
|
|
144
|
+
if (!(await pathExists(reposRoot)))
|
|
145
|
+
return [];
|
|
146
|
+
const out = [];
|
|
147
|
+
async function walk(current) {
|
|
148
|
+
let entries;
|
|
149
|
+
try {
|
|
150
|
+
entries = await readdir(current, { withFileTypes: true });
|
|
151
|
+
}
|
|
152
|
+
catch (err) {
|
|
153
|
+
if (err.code === "ENOENT")
|
|
154
|
+
return;
|
|
155
|
+
throw err;
|
|
156
|
+
}
|
|
157
|
+
for (const entry of entries) {
|
|
158
|
+
if (!entry.isDirectory())
|
|
159
|
+
continue;
|
|
160
|
+
const full = path.join(current, entry.name);
|
|
161
|
+
if (entry.name.endsWith(".git")) {
|
|
162
|
+
out.push(full);
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
165
|
+
await walk(full);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
await walk(reposRoot);
|
|
170
|
+
return out;
|
|
171
|
+
}
|
|
172
|
+
function repositoryForCachePath(cachePath) {
|
|
173
|
+
const reposRoot = path.join(globalStageflowHome(), "repos");
|
|
174
|
+
const rel = path.relative(reposRoot, cachePath);
|
|
175
|
+
if (rel.startsWith("..") || path.isAbsolute(rel))
|
|
176
|
+
return undefined;
|
|
177
|
+
const parts = rel.split(path.sep);
|
|
178
|
+
if (parts.length < 3)
|
|
179
|
+
return undefined;
|
|
180
|
+
const owner = parts[parts.length - 2];
|
|
181
|
+
const repoGit = parts[parts.length - 1];
|
|
182
|
+
if (!repoGit.endsWith(".git"))
|
|
183
|
+
return undefined;
|
|
184
|
+
return `${owner}/${repoGit.slice(0, -".git".length)}`;
|
|
185
|
+
}
|
|
186
|
+
function repositoryOf(run) {
|
|
187
|
+
if ("binding" in run && run.binding?.repository) {
|
|
188
|
+
return run.binding.repository;
|
|
189
|
+
}
|
|
190
|
+
if ("repository" in run && run.repository) {
|
|
191
|
+
return run.repository;
|
|
192
|
+
}
|
|
193
|
+
return undefined;
|
|
194
|
+
}
|
|
195
|
+
function runsForRepository(runs, repository) {
|
|
196
|
+
return runs.filter((r) => repositoryOf(r) === repository);
|
|
197
|
+
}
|
|
198
|
+
async function hasLiveWorktree(runs) {
|
|
199
|
+
for (const run of runs) {
|
|
200
|
+
const checkout = "checkout_root" in run && typeof run.checkout_root === "string"
|
|
201
|
+
? run.checkout_root
|
|
202
|
+
: undefined;
|
|
203
|
+
const candidates = [
|
|
204
|
+
...(checkout ? [checkout] : []),
|
|
205
|
+
worktreePathForRun(run.run_id),
|
|
206
|
+
];
|
|
207
|
+
for (const candidate of candidates) {
|
|
208
|
+
if (candidate !== "" && (await pathExists(candidate)))
|
|
209
|
+
return true;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
return false;
|
|
213
|
+
}
|
|
214
|
+
function allReferencingRunsPastBareTtl(runs, now, ttlMs) {
|
|
215
|
+
if (runs.length === 0)
|
|
216
|
+
return true;
|
|
217
|
+
for (const run of runs) {
|
|
218
|
+
if (!isTerminalRunStatus(run.status))
|
|
219
|
+
return false;
|
|
220
|
+
if (run.finished_at === undefined || run.finished_at === "")
|
|
221
|
+
return false;
|
|
222
|
+
const finishedMs = Date.parse(run.finished_at);
|
|
223
|
+
if (!Number.isFinite(finishedMs))
|
|
224
|
+
return false;
|
|
225
|
+
if (now.getTime() - finishedMs < ttlMs)
|
|
226
|
+
return false;
|
|
227
|
+
}
|
|
228
|
+
return true;
|
|
229
|
+
}
|
|
230
|
+
async function evictBareCaches(options) {
|
|
231
|
+
const reposRoot = path.join(globalStageflowHome(), "repos");
|
|
232
|
+
const caches = await listBareCachePaths(reposRoot);
|
|
233
|
+
const evicted = [];
|
|
234
|
+
for (const cachePath of caches) {
|
|
235
|
+
const repository = repositoryForCachePath(cachePath);
|
|
236
|
+
if (repository === undefined)
|
|
237
|
+
continue;
|
|
238
|
+
// Prefer store-based live-worktree check (cheaper than `git worktree list`).
|
|
239
|
+
const referencing = runsForRepository(options.runs, repository);
|
|
240
|
+
if (await hasLiveWorktree(referencing))
|
|
241
|
+
continue;
|
|
242
|
+
if (!allReferencingRunsPastBareTtl(referencing, options.now, options.ttlMs)) {
|
|
243
|
+
continue;
|
|
244
|
+
}
|
|
245
|
+
evicted.push(cachePath);
|
|
246
|
+
if (options.execute) {
|
|
247
|
+
await rmForce(cachePath);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
return evicted;
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Two-stage retention: SLIM then PURGE, then bare-cache eviction.
|
|
254
|
+
* Dry-run (`execute: false`) computes the same candidates and skips mutations.
|
|
255
|
+
*/
|
|
256
|
+
export async function runRetentionSweep(store, a2aStore, options = {}) {
|
|
257
|
+
const now = options.now ?? new Date();
|
|
258
|
+
const execute = options.execute === true;
|
|
259
|
+
const env = options.env ?? process.env;
|
|
260
|
+
const windows = options.windows ?? retentionWindowsFromEnv(env);
|
|
261
|
+
const artifactMaxBytes = options.artifactMaxBytes ?? slimArtifactMaxBytesFromEnv(env);
|
|
262
|
+
const bareCacheTtlMs = options.bareCacheTtlMs ?? bareCacheTtlFromEnv(env);
|
|
263
|
+
const runs = await store.listRuns();
|
|
264
|
+
const slimmed = [];
|
|
265
|
+
const purged = [];
|
|
266
|
+
const metas = new Map();
|
|
267
|
+
async function metaFor(runId) {
|
|
268
|
+
let meta = metas.get(runId);
|
|
269
|
+
if (meta === undefined) {
|
|
270
|
+
meta = await store.readRunMeta(runId);
|
|
271
|
+
metas.set(runId, meta);
|
|
272
|
+
}
|
|
273
|
+
return meta;
|
|
274
|
+
}
|
|
275
|
+
// Decide from durable meta (status/finished_at/slimmed_at), not listRuns'
|
|
276
|
+
// derived status — stage rows can make a terminal run look "running".
|
|
277
|
+
const decisions = [];
|
|
278
|
+
for (const summary of runs) {
|
|
279
|
+
const meta = await metaFor(summary.run_id);
|
|
280
|
+
const decision = retentionDecision(meta, now, windows);
|
|
281
|
+
if (decision === "slim" || decision === "purge") {
|
|
282
|
+
decisions.push({ runId: summary.run_id, decision });
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
const purgeCandidates = decisions.filter((d) => d.decision === "purge");
|
|
286
|
+
if (execute && a2aStore === undefined && purgeCandidates.length > 0) {
|
|
287
|
+
throw new Error("runRetentionSweep: a2aStore is required when execute is true and PURGE candidates exist");
|
|
288
|
+
}
|
|
289
|
+
for (const { runId, decision } of decisions) {
|
|
290
|
+
if (decision !== "slim")
|
|
291
|
+
continue;
|
|
292
|
+
const meta = await metaFor(runId);
|
|
293
|
+
if (execute) {
|
|
294
|
+
const ok = await slimOneRun(store, meta, now, artifactMaxBytes);
|
|
295
|
+
if (!ok)
|
|
296
|
+
continue;
|
|
297
|
+
slimmed.push(runId);
|
|
298
|
+
metas.set(runId, {
|
|
299
|
+
...meta,
|
|
300
|
+
slimmed_at: now.toISOString(),
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
else {
|
|
304
|
+
slimmed.push(runId);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
for (const { runId, decision } of decisions) {
|
|
308
|
+
if (decision !== "purge")
|
|
309
|
+
continue;
|
|
310
|
+
purged.push(runId);
|
|
311
|
+
if (execute) {
|
|
312
|
+
await deleteRunEverywhere(store, a2aStore, runId);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
// Re-list for bare-cache decisions after PURGE removed rows when execute.
|
|
316
|
+
const runsForBare = execute ? await store.listRuns() : runs;
|
|
317
|
+
const bareCachesEvicted = await evictBareCaches({
|
|
318
|
+
runs: runsForBare,
|
|
319
|
+
now,
|
|
320
|
+
ttlMs: bareCacheTtlMs,
|
|
321
|
+
execute,
|
|
322
|
+
});
|
|
323
|
+
return { slimmed, purged, bareCachesEvicted };
|
|
324
|
+
}
|
|
325
|
+
/** Test helper: expose bare-cache path resolution used by the eviction pass. */
|
|
326
|
+
export function bareCachePathForRepository(repository) {
|
|
327
|
+
return bareCachePath(repository);
|
|
328
|
+
}
|
|
@@ -3,6 +3,7 @@ import { type RunDetail, type RunPipelineDagSnapshot, type RunStatus, type RunSt
|
|
|
3
3
|
import type { PipelineRunResult } from "./pipelineRunner.js";
|
|
4
4
|
import { type SchedulerPreparedPipeline } from "./pipelineScheduler.js";
|
|
5
5
|
import type { StageExecutionMode } from "./stageConcurrency.js";
|
|
6
|
+
import type { StageBrowserSupport } from "../browser/browserHost.js";
|
|
6
7
|
import type { StageProcessLauncher } from "./stageProcessLauncher.js";
|
|
7
8
|
import type { StageHitlController } from "./stageHitl.js";
|
|
8
9
|
import type { OperatorCatalog } from "./stageAttemptBootstrap.js";
|
|
@@ -34,6 +35,7 @@ export type RetryStageResult = {
|
|
|
34
35
|
ok: false;
|
|
35
36
|
reason: string;
|
|
36
37
|
status?: number;
|
|
38
|
+
code?: string;
|
|
37
39
|
};
|
|
38
40
|
export type RetryTrackingPort = {
|
|
39
41
|
ensureResumeTracked(runId: string): Promise<{
|
|
@@ -57,6 +59,7 @@ export type RetryStageRequest = {
|
|
|
57
59
|
agent: AgentPort;
|
|
58
60
|
cwd: string;
|
|
59
61
|
operatorCatalog?: OperatorCatalog;
|
|
62
|
+
browser?: StageBrowserSupport;
|
|
60
63
|
maxActiveStagesPerRun: number;
|
|
61
64
|
executionMode: StageExecutionMode;
|
|
62
65
|
stageProcessLauncher?: StageProcessLauncher;
|
|
@@ -77,6 +80,7 @@ export declare function assertStageRetryEligible(detail: RunDetail, stageId: str
|
|
|
77
80
|
ok: false;
|
|
78
81
|
reason: string;
|
|
79
82
|
status: number;
|
|
83
|
+
code: string;
|
|
80
84
|
};
|
|
81
85
|
export declare function readRetryRootWaitTimeoutMs(): number;
|
|
82
86
|
export declare class RunRetryCoordinator {
|
|
@@ -37,6 +37,7 @@ export function assertStageRetryEligible(detail, stageId, opts) {
|
|
|
37
37
|
ok: false,
|
|
38
38
|
reason: `Stage not found: ${stageId}`,
|
|
39
39
|
status: 404,
|
|
40
|
+
code: "stage_not_found",
|
|
40
41
|
};
|
|
41
42
|
}
|
|
42
43
|
if (stageSnap.status === "waiting_for_input") {
|
|
@@ -44,6 +45,7 @@ export function assertStageRetryEligible(detail, stageId, opts) {
|
|
|
44
45
|
ok: false,
|
|
45
46
|
reason: `Stage is waiting for input and cannot be retried`,
|
|
46
47
|
status: 409,
|
|
48
|
+
code: "hitl_not_retriable",
|
|
47
49
|
};
|
|
48
50
|
}
|
|
49
51
|
const runStatus = opts?.persistedStatus ?? detail.status;
|
|
@@ -58,6 +60,7 @@ export function assertStageRetryEligible(detail, stageId, opts) {
|
|
|
58
60
|
ok: false,
|
|
59
61
|
reason: `Run is not failed or succeeded (status=${runStatus})`,
|
|
60
62
|
status: 409,
|
|
63
|
+
code: "run_not_retryable",
|
|
61
64
|
};
|
|
62
65
|
}
|
|
63
66
|
}
|
|
@@ -66,12 +69,18 @@ export function assertStageRetryEligible(detail, stageId, opts) {
|
|
|
66
69
|
ok: false,
|
|
67
70
|
reason: `Stage is not failed or succeeded (status=${stageSnap.status})`,
|
|
68
71
|
status: 409,
|
|
72
|
+
code: "stage_not_failed",
|
|
69
73
|
};
|
|
70
74
|
}
|
|
71
75
|
if (stageSnap.status === "succeeded") {
|
|
72
76
|
const blocker = succeededStageRetryBlocker(stageSnap);
|
|
73
77
|
if (blocker !== undefined) {
|
|
74
|
-
return {
|
|
78
|
+
return {
|
|
79
|
+
ok: false,
|
|
80
|
+
reason: blocker,
|
|
81
|
+
status: 409,
|
|
82
|
+
code: "run_not_retryable",
|
|
83
|
+
};
|
|
75
84
|
}
|
|
76
85
|
}
|
|
77
86
|
return { ok: true };
|
|
@@ -256,7 +265,12 @@ export class RunRetryCoordinator {
|
|
|
256
265
|
dag = meta.pipeline_dag;
|
|
257
266
|
}
|
|
258
267
|
catch {
|
|
259
|
-
return {
|
|
268
|
+
return {
|
|
269
|
+
ok: false,
|
|
270
|
+
reason: `Run not found: ${runId}`,
|
|
271
|
+
status: 404,
|
|
272
|
+
code: "run_not_found",
|
|
273
|
+
};
|
|
260
274
|
}
|
|
261
275
|
const recoveryActive = this.isActive(runId);
|
|
262
276
|
const eligibility = assertStageRetryEligible(detail, stageId, {
|
|
@@ -269,6 +283,7 @@ export class RunRetryCoordinator {
|
|
|
269
283
|
ok: false,
|
|
270
284
|
reason: eligibility.reason,
|
|
271
285
|
status: eligibility.status,
|
|
286
|
+
code: eligibility.code,
|
|
272
287
|
};
|
|
273
288
|
}
|
|
274
289
|
if (recoveryActive) {
|
|
@@ -277,6 +292,7 @@ export class RunRetryCoordinator {
|
|
|
277
292
|
ok: false,
|
|
278
293
|
reason: `Retry already in progress for run ${runId} stage ${stageId}`,
|
|
279
294
|
status: 409,
|
|
295
|
+
code: "retry_in_progress",
|
|
280
296
|
};
|
|
281
297
|
}
|
|
282
298
|
}
|
|
@@ -285,6 +301,7 @@ export class RunRetryCoordinator {
|
|
|
285
301
|
ok: false,
|
|
286
302
|
reason: `Run ${runId} already has active orchestration`,
|
|
287
303
|
status: 409,
|
|
304
|
+
code: "run_not_retryable",
|
|
288
305
|
};
|
|
289
306
|
}
|
|
290
307
|
const priorRunStatus = metaStatus;
|
|
@@ -327,6 +344,7 @@ export class RunRetryCoordinator {
|
|
|
327
344
|
checkoutRoot: meta.checkout_root,
|
|
328
345
|
hitl: req.hitl,
|
|
329
346
|
operatorCatalog: req.operatorCatalog,
|
|
347
|
+
...(req.browser !== undefined ? { browser: req.browser } : {}),
|
|
330
348
|
};
|
|
331
349
|
if (this.isActive(runId)) {
|
|
332
350
|
await this.addRoot(runId, stageId, execution.attempt);
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export declare const SKILLS_INVALID_NAME: "skills_invalid_name";
|
|
2
|
+
export declare const SKILLS_INVALID_PATH: "skills_invalid_path";
|
|
3
|
+
export declare const SKILLS_MISSING_SKILL_MD: "skills_missing_skill_md";
|
|
4
|
+
export declare const SKILLS_PAYLOAD_TOO_LARGE: "skills_payload_too_large";
|
|
5
|
+
export type SkillsPayloadErrorCode = typeof SKILLS_INVALID_NAME | typeof SKILLS_INVALID_PATH | typeof SKILLS_MISSING_SKILL_MD | typeof SKILLS_PAYLOAD_TOO_LARGE;
|
|
6
|
+
/** name → relativePath → utf-8 file contents */
|
|
7
|
+
export type SkillsPayload = Record<string, Record<string, string>>;
|
|
8
|
+
export type SkillsValidationOk = {
|
|
9
|
+
ok: true;
|
|
10
|
+
skills: SkillsPayload;
|
|
11
|
+
bytes: number;
|
|
12
|
+
};
|
|
13
|
+
export type SkillsValidationErr = {
|
|
14
|
+
ok: false;
|
|
15
|
+
code: SkillsPayloadErrorCode;
|
|
16
|
+
reason: string;
|
|
17
|
+
bytes?: number;
|
|
18
|
+
maxBytes?: number;
|
|
19
|
+
};
|
|
20
|
+
export type SkillsValidationResult = SkillsValidationOk | SkillsValidationErr;
|
|
21
|
+
export declare function runSkillsDir(workspaceDir: string): string;
|
|
22
|
+
export declare function skillPackDir(workspaceDir: string, skillName: string): string;
|
|
23
|
+
export declare function assertValidSkillName(name: string): {
|
|
24
|
+
ok: true;
|
|
25
|
+
} | {
|
|
26
|
+
ok: false;
|
|
27
|
+
reason: string;
|
|
28
|
+
};
|
|
29
|
+
export declare function assertValidSkillRelativePath(relPath: string): {
|
|
30
|
+
ok: true;
|
|
31
|
+
} | {
|
|
32
|
+
ok: false;
|
|
33
|
+
reason: string;
|
|
34
|
+
};
|
|
35
|
+
export declare function measureSkillsPayloadBytes(skills: SkillsPayload): number;
|
|
36
|
+
export declare function validateSkillsPayload(skills: unknown, options?: {
|
|
37
|
+
pipelineBytes?: number;
|
|
38
|
+
maxBytes?: number;
|
|
39
|
+
}): SkillsValidationResult;
|
|
40
|
+
export declare function materializeRunSkills(workspaceDir: string, skills: SkillsPayload): Promise<void>;
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { mkdir, realpath, writeFile } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { START_PAYLOAD_MAX_BYTES } from "./startPayload.js";
|
|
4
|
+
import { isInsideDir } from "../runstore/workspaceLayout.js";
|
|
5
|
+
export const SKILLS_INVALID_NAME = "skills_invalid_name";
|
|
6
|
+
export const SKILLS_INVALID_PATH = "skills_invalid_path";
|
|
7
|
+
export const SKILLS_MISSING_SKILL_MD = "skills_missing_skill_md";
|
|
8
|
+
export const SKILLS_PAYLOAD_TOO_LARGE = "skills_payload_too_large";
|
|
9
|
+
const SKILL_MD = "SKILL.md";
|
|
10
|
+
export function runSkillsDir(workspaceDir) {
|
|
11
|
+
return path.join(workspaceDir, "skills");
|
|
12
|
+
}
|
|
13
|
+
export function skillPackDir(workspaceDir, skillName) {
|
|
14
|
+
return path.join(runSkillsDir(workspaceDir), skillName);
|
|
15
|
+
}
|
|
16
|
+
function hasForbiddenNameChars(name) {
|
|
17
|
+
return (name.includes("/") ||
|
|
18
|
+
name.includes("\\") ||
|
|
19
|
+
name.includes("\0") ||
|
|
20
|
+
name.includes("\u0000"));
|
|
21
|
+
}
|
|
22
|
+
function hasDotDotSegment(rel) {
|
|
23
|
+
const normalized = rel.replace(/\\/g, "/");
|
|
24
|
+
return normalized.split("/").some((segment) => segment === "..");
|
|
25
|
+
}
|
|
26
|
+
export function assertValidSkillName(name) {
|
|
27
|
+
if (typeof name !== "string" || name.length === 0) {
|
|
28
|
+
return { ok: false, reason: "Skill name must be a non-empty string" };
|
|
29
|
+
}
|
|
30
|
+
if (hasForbiddenNameChars(name)) {
|
|
31
|
+
return {
|
|
32
|
+
ok: false,
|
|
33
|
+
reason: `Skill name "${name}" must not contain "/", "\\\\", or NUL`,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
if (path.isAbsolute(name) || hasDotDotSegment(name) || name === "..") {
|
|
37
|
+
return {
|
|
38
|
+
ok: false,
|
|
39
|
+
reason: `Skill name "${name}" must not be absolute or contain ".."`,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
return { ok: true };
|
|
43
|
+
}
|
|
44
|
+
export function assertValidSkillRelativePath(relPath) {
|
|
45
|
+
if (typeof relPath !== "string" || relPath.length === 0) {
|
|
46
|
+
return { ok: false, reason: "Skill file path must be a non-empty string" };
|
|
47
|
+
}
|
|
48
|
+
if (path.isAbsolute(relPath) || hasDotDotSegment(relPath)) {
|
|
49
|
+
return {
|
|
50
|
+
ok: false,
|
|
51
|
+
reason: `Skill file path "${relPath}" must be relative and must not contain ".."`,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
return { ok: true };
|
|
55
|
+
}
|
|
56
|
+
export function measureSkillsPayloadBytes(skills) {
|
|
57
|
+
let bytes = 0;
|
|
58
|
+
for (const files of Object.values(skills)) {
|
|
59
|
+
for (const content of Object.values(files)) {
|
|
60
|
+
bytes += Buffer.byteLength(content, "utf8");
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return bytes;
|
|
64
|
+
}
|
|
65
|
+
export function validateSkillsPayload(skills, options) {
|
|
66
|
+
if (skills === undefined || skills === null) {
|
|
67
|
+
return { ok: true, skills: {}, bytes: 0 };
|
|
68
|
+
}
|
|
69
|
+
if (typeof skills !== "object" || Array.isArray(skills)) {
|
|
70
|
+
return {
|
|
71
|
+
ok: false,
|
|
72
|
+
code: SKILLS_INVALID_NAME,
|
|
73
|
+
reason: "skills must be a map of name → { relativePath: contents }",
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
const maxBytes = options?.maxBytes ?? START_PAYLOAD_MAX_BYTES;
|
|
77
|
+
const pipelineBytes = options?.pipelineBytes ?? 0;
|
|
78
|
+
const out = {};
|
|
79
|
+
for (const [name, files] of Object.entries(skills)) {
|
|
80
|
+
const nameCheck = assertValidSkillName(name);
|
|
81
|
+
if (!nameCheck.ok) {
|
|
82
|
+
return { ok: false, code: SKILLS_INVALID_NAME, reason: nameCheck.reason };
|
|
83
|
+
}
|
|
84
|
+
if (files === null || typeof files !== "object" || Array.isArray(files)) {
|
|
85
|
+
return {
|
|
86
|
+
ok: false,
|
|
87
|
+
code: SKILLS_MISSING_SKILL_MD,
|
|
88
|
+
reason: `Skill "${name}" must map to a files object including ${SKILL_MD}`,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
const fileMap = {};
|
|
92
|
+
let hasSkillMd = false;
|
|
93
|
+
for (const [relPath, content] of Object.entries(files)) {
|
|
94
|
+
const pathCheck = assertValidSkillRelativePath(relPath);
|
|
95
|
+
if (!pathCheck.ok) {
|
|
96
|
+
return {
|
|
97
|
+
ok: false,
|
|
98
|
+
code: SKILLS_INVALID_PATH,
|
|
99
|
+
reason: pathCheck.reason,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
if (typeof content !== "string") {
|
|
103
|
+
return {
|
|
104
|
+
ok: false,
|
|
105
|
+
code: SKILLS_INVALID_PATH,
|
|
106
|
+
reason: `Skill "${name}" file "${relPath}" content must be a UTF-8 string`,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
if (relPath === SKILL_MD || relPath.replace(/\\/g, "/") === SKILL_MD) {
|
|
110
|
+
hasSkillMd = true;
|
|
111
|
+
}
|
|
112
|
+
fileMap[relPath] = content;
|
|
113
|
+
}
|
|
114
|
+
if (!hasSkillMd) {
|
|
115
|
+
return {
|
|
116
|
+
ok: false,
|
|
117
|
+
code: SKILLS_MISSING_SKILL_MD,
|
|
118
|
+
reason: `Skill "${name}" requires ${SKILL_MD}`,
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
out[name] = fileMap;
|
|
122
|
+
}
|
|
123
|
+
const bytes = measureSkillsPayloadBytes(out);
|
|
124
|
+
if (pipelineBytes + bytes > maxBytes) {
|
|
125
|
+
return {
|
|
126
|
+
ok: false,
|
|
127
|
+
code: SKILLS_PAYLOAD_TOO_LARGE,
|
|
128
|
+
reason: `Start payload (pipeline + skills) is ${pipelineBytes + bytes} bytes; max is ${maxBytes}`,
|
|
129
|
+
bytes: pipelineBytes + bytes,
|
|
130
|
+
maxBytes,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
return { ok: true, skills: out, bytes };
|
|
134
|
+
}
|
|
135
|
+
export async function materializeRunSkills(workspaceDir, skills) {
|
|
136
|
+
if (Object.keys(skills).length === 0)
|
|
137
|
+
return;
|
|
138
|
+
const skillsRoot = runSkillsDir(workspaceDir);
|
|
139
|
+
await mkdir(skillsRoot, { recursive: true });
|
|
140
|
+
const skillsRootReal = await realpath(skillsRoot);
|
|
141
|
+
for (const [name, files] of Object.entries(skills)) {
|
|
142
|
+
const nameCheck = assertValidSkillName(name);
|
|
143
|
+
if (!nameCheck.ok) {
|
|
144
|
+
throw new Error(nameCheck.reason);
|
|
145
|
+
}
|
|
146
|
+
const destDir = skillPackDir(workspaceDir, name);
|
|
147
|
+
await mkdir(destDir, { recursive: true });
|
|
148
|
+
const destDirResolved = path.resolve(destDir);
|
|
149
|
+
if (!isInsideDir(destDirResolved, path.resolve(skillsRoot))) {
|
|
150
|
+
throw new Error(`Skill "${name}" destination escapes run skills directory`);
|
|
151
|
+
}
|
|
152
|
+
const destDirReal = await realpath(destDir);
|
|
153
|
+
if (!isInsideDir(destDirReal, skillsRootReal)) {
|
|
154
|
+
throw new Error(`Skill "${name}" destination realpath escapes run skills directory`);
|
|
155
|
+
}
|
|
156
|
+
for (const [relPath, content] of Object.entries(files)) {
|
|
157
|
+
const pathCheck = assertValidSkillRelativePath(relPath);
|
|
158
|
+
if (!pathCheck.ok) {
|
|
159
|
+
throw new Error(pathCheck.reason);
|
|
160
|
+
}
|
|
161
|
+
const dest = path.resolve(destDir, relPath);
|
|
162
|
+
if (!isInsideDir(dest, destDirResolved)) {
|
|
163
|
+
throw new Error(`Skill "${name}" file "${relPath}" escapes skill pack directory`);
|
|
164
|
+
}
|
|
165
|
+
await mkdir(path.dirname(dest), { recursive: true });
|
|
166
|
+
await writeFile(dest, content, "utf8");
|
|
167
|
+
const fileReal = await realpath(dest);
|
|
168
|
+
if (!isInsideDir(fileReal, destDirReal)) {
|
|
169
|
+
throw new Error(`Skill "${name}" file "${relPath}" realpath escapes skill pack directory`);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|