patchwork-os 0.2.0-beta.9.canary.92 → 1.1.0-beta.1
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.bridge.md +15 -16
- package/README.md +46 -458
- package/dist/activationMetrics.js +2 -3
- package/dist/activationMetrics.js.map +1 -1
- package/dist/activityLog.d.ts +1 -0
- package/dist/activityLog.js +8 -3
- package/dist/activityLog.js.map +1 -1
- package/dist/adapters/gemini.js +2 -2
- package/dist/adapters/gemini.js.map +1 -1
- package/dist/adapters/grok.js +6 -1
- package/dist/adapters/grok.js.map +1 -1
- package/dist/adapters/openai.js +4 -0
- package/dist/adapters/openai.js.map +1 -1
- package/dist/approvalHttp.d.ts +9 -0
- package/dist/approvalHttp.js +210 -171
- package/dist/approvalHttp.js.map +1 -1
- package/dist/approvalKpi.d.ts +102 -0
- package/dist/approvalKpi.js +282 -0
- package/dist/approvalKpi.js.map +1 -0
- package/dist/approvalQueue.d.ts +1 -1
- package/dist/approvalQueue.js.map +1 -1
- package/dist/automation.d.ts +45 -4
- package/dist/automation.js +123 -15
- package/dist/automation.js.map +1 -1
- package/dist/bridge.d.ts +10 -0
- package/dist/bridge.js +225 -46
- package/dist/bridge.js.map +1 -1
- package/dist/bridgeLockDiscovery.d.ts +16 -1
- package/dist/bridgeLockDiscovery.js +38 -4
- package/dist/bridgeLockDiscovery.js.map +1 -1
- package/dist/bridgeToolsRules.js +3 -18
- package/dist/bridgeToolsRules.js.map +1 -1
- package/dist/claudeAuthHttp.d.ts +25 -0
- package/dist/claudeAuthHttp.js +219 -0
- package/dist/claudeAuthHttp.js.map +1 -0
- package/dist/claudeDriver.js +4 -1
- package/dist/claudeDriver.js.map +1 -1
- package/dist/claudeOrchestrator.d.ts +15 -0
- package/dist/claudeOrchestrator.js +44 -0
- package/dist/claudeOrchestrator.js.map +1 -1
- package/dist/commands/connect.d.ts +47 -0
- package/dist/commands/connect.js +419 -0
- package/dist/commands/connect.js.map +1 -0
- package/dist/commands/install.js +3 -10
- package/dist/commands/install.js.map +1 -1
- package/dist/commands/launchd.d.ts +7 -0
- package/dist/commands/launchd.js +20 -2
- package/dist/commands/launchd.js.map +1 -1
- package/dist/commands/patchworkInit.d.ts +7 -0
- package/dist/commands/patchworkInit.js +26 -0
- package/dist/commands/patchworkInit.js.map +1 -1
- package/dist/commands/recipe.d.ts +59 -4
- package/dist/commands/recipe.js +206 -5
- package/dist/commands/recipe.js.map +1 -1
- package/dist/commands/recipeInstall.d.ts +9 -0
- package/dist/commands/recipeInstall.js +56 -3
- package/dist/commands/recipeInstall.js.map +1 -1
- package/dist/commands/task.d.ts +25 -0
- package/dist/commands/task.js +61 -41
- package/dist/commands/task.js.map +1 -1
- package/dist/commands/tokenEfficiency.d.ts +4 -0
- package/dist/commands/tokenEfficiency.js +23 -26
- package/dist/commands/tokenEfficiency.js.map +1 -1
- package/dist/commands/tracesExport.d.ts +15 -1
- package/dist/commands/tracesExport.js +39 -5
- package/dist/commands/tracesExport.js.map +1 -1
- package/dist/config.js +16 -0
- package/dist/config.js.map +1 -1
- package/dist/connectorRoutes.js +613 -62
- package/dist/connectorRoutes.js.map +1 -1
- package/dist/connectors/asana.js +9 -3
- package/dist/connectors/asana.js.map +1 -1
- package/dist/connectors/baseConnector.d.ts +16 -0
- package/dist/connectors/baseConnector.js +2 -0
- package/dist/connectors/baseConnector.js.map +1 -1
- package/dist/connectors/caldiy.js +34 -0
- package/dist/connectors/caldiy.js.map +1 -1
- package/dist/connectors/confluence.js +9 -2
- package/dist/connectors/confluence.js.map +1 -1
- package/dist/connectors/connectorActivity.d.ts +24 -0
- package/dist/connectors/connectorActivity.js +32 -0
- package/dist/connectors/connectorActivity.js.map +1 -0
- package/dist/connectors/connectorRedirectUri.js +22 -2
- package/dist/connectors/connectorRedirectUri.js.map +1 -1
- package/dist/connectors/connectorRegistry.d.ts +0 -3
- package/dist/connectors/connectorRegistry.js +8 -8
- package/dist/connectors/connectorRegistry.js.map +1 -1
- package/dist/connectors/datadog.js +8 -1
- package/dist/connectors/datadog.js.map +1 -1
- package/dist/connectors/discord.js +4 -3
- package/dist/connectors/discord.js.map +1 -1
- package/dist/connectors/elasticsearch.js +22 -0
- package/dist/connectors/elasticsearch.js.map +1 -1
- package/dist/connectors/github.d.ts +38 -0
- package/dist/connectors/github.js +91 -5
- package/dist/connectors/github.js.map +1 -1
- package/dist/connectors/gitlab.js +19 -16
- package/dist/connectors/gitlab.js.map +1 -1
- package/dist/connectors/gmail.d.ts +5 -0
- package/dist/connectors/gmail.js +53 -11
- package/dist/connectors/gmail.js.map +1 -1
- package/dist/connectors/googleCalendar.js +5 -4
- package/dist/connectors/googleCalendar.js.map +1 -1
- package/dist/connectors/googleDocs.js +7 -6
- package/dist/connectors/googleDocs.js.map +1 -1
- package/dist/connectors/googleDrive.js +15 -8
- package/dist/connectors/googleDrive.js.map +1 -1
- package/dist/connectors/grafana.js +39 -3
- package/dist/connectors/grafana.js.map +1 -1
- package/dist/connectors/jira.d.ts +1 -1
- package/dist/connectors/jira.js +19 -4
- package/dist/connectors/jira.js.map +1 -1
- package/dist/connectors/linear.js +2 -2
- package/dist/connectors/linear.js.map +1 -1
- package/dist/connectors/mcpClient.d.ts +29 -1
- package/dist/connectors/mcpClient.js +114 -44
- package/dist/connectors/mcpClient.js.map +1 -1
- package/dist/connectors/mcpOAuth.d.ts +1 -0
- package/dist/connectors/mcpOAuth.js +49 -4
- package/dist/connectors/mcpOAuth.js.map +1 -1
- package/dist/connectors/monday.js +5 -4
- package/dist/connectors/monday.js.map +1 -1
- package/dist/connectors/mongodb.js +32 -5
- package/dist/connectors/mongodb.js.map +1 -1
- package/dist/connectors/notion.d.ts +1 -1
- package/dist/connectors/notion.js +22 -6
- package/dist/connectors/notion.js.map +1 -1
- package/dist/connectors/oauthError.d.ts +17 -0
- package/dist/connectors/oauthError.js +50 -0
- package/dist/connectors/oauthError.js.map +1 -0
- package/dist/connectors/postgres.d.ts +7 -0
- package/dist/connectors/postgres.js +32 -2
- package/dist/connectors/postgres.js.map +1 -1
- package/dist/connectors/posthog.js +26 -0
- package/dist/connectors/posthog.js.map +1 -1
- package/dist/connectors/redis.d.ts +1 -0
- package/dist/connectors/redis.js +47 -20
- package/dist/connectors/redis.js.map +1 -1
- package/dist/connectors/salesforce.js +66 -5
- package/dist/connectors/salesforce.js.map +1 -1
- package/dist/connectors/sentry.js +2 -2
- package/dist/connectors/sentry.js.map +1 -1
- package/dist/connectors/slack.js +37 -14
- package/dist/connectors/slack.js.map +1 -1
- package/dist/connectors/snowflake.js +24 -0
- package/dist/connectors/snowflake.js.map +1 -1
- package/dist/connectors/stripe.js +9 -2
- package/dist/connectors/stripe.js.map +1 -1
- package/dist/connectors/supabase.js +34 -0
- package/dist/connectors/supabase.js.map +1 -1
- package/dist/connectors/tokenStorage.d.ts +20 -0
- package/dist/connectors/tokenStorage.js +196 -64
- package/dist/connectors/tokenStorage.js.map +1 -1
- package/dist/connectors/woocommerce.js +34 -0
- package/dist/connectors/woocommerce.js.map +1 -1
- package/dist/copilot/parseIntent.d.ts +58 -0
- package/dist/copilot/parseIntent.js +149 -0
- package/dist/copilot/parseIntent.js.map +1 -0
- package/dist/decisionReplay.d.ts +8 -6
- package/dist/decisionReplay.js +35 -11
- package/dist/decisionReplay.js.map +1 -1
- package/dist/decisionTraceLog.d.ts +9 -0
- package/dist/decisionTraceLog.js +6 -0
- package/dist/decisionTraceLog.js.map +1 -1
- package/dist/drivers/claude/api.js +49 -7
- package/dist/drivers/claude/api.js.map +1 -1
- package/dist/drivers/claude/envSanitizer.d.ts +9 -1
- package/dist/drivers/claude/envSanitizer.js +41 -3
- package/dist/drivers/claude/envSanitizer.js.map +1 -1
- package/dist/drivers/claude/streamParser.d.ts +13 -0
- package/dist/drivers/claude/streamParser.js.map +1 -1
- package/dist/drivers/claude/subprocess.js +131 -16
- package/dist/drivers/claude/subprocess.js.map +1 -1
- package/dist/drivers/claude/subprocessSettings.d.ts +1 -1
- package/dist/drivers/claude/subprocessSettings.js +18 -4
- package/dist/drivers/claude/subprocessSettings.js.map +1 -1
- package/dist/drivers/gemini/index.d.ts +13 -0
- package/dist/drivers/gemini/index.js +211 -67
- package/dist/drivers/gemini/index.js.map +1 -1
- package/dist/drivers/local/index.d.ts +2 -17
- package/dist/drivers/local/index.js +5 -92
- package/dist/drivers/local/index.js.map +1 -1
- package/dist/drivers/openai/index.js +69 -9
- package/dist/drivers/openai/index.js.map +1 -1
- package/dist/drivers/outputCap.d.ts +27 -0
- package/dist/drivers/outputCap.js +50 -0
- package/dist/drivers/outputCap.js.map +1 -0
- package/dist/embeddings/cosine.d.ts +23 -0
- package/dist/embeddings/cosine.js +59 -0
- package/dist/embeddings/cosine.js.map +1 -0
- package/dist/embeddings/index.d.ts +19 -0
- package/dist/embeddings/index.js +35 -0
- package/dist/embeddings/index.js.map +1 -0
- package/dist/embeddings/localEmbeddings.d.ts +29 -0
- package/dist/embeddings/localEmbeddings.js +103 -0
- package/dist/embeddings/localEmbeddings.js.map +1 -0
- package/dist/embeddings/localEndpoints.d.ts +24 -0
- package/dist/embeddings/localEndpoints.js +37 -0
- package/dist/embeddings/localEndpoints.js.map +1 -0
- package/dist/embeddings/types.d.ts +32 -0
- package/dist/embeddings/types.js +12 -0
- package/dist/embeddings/types.js.map +1 -0
- package/dist/featureFlags.d.ts +18 -0
- package/dist/featureFlags.js +32 -0
- package/dist/featureFlags.js.map +1 -1
- package/dist/fp/automationInterpreter.js +118 -39
- package/dist/fp/automationInterpreter.js.map +1 -1
- package/dist/fp/automationProgram.d.ts +10 -0
- package/dist/fp/automationProgram.js.map +1 -1
- package/dist/fp/automationState.d.ts +6 -0
- package/dist/fp/automationState.js +38 -0
- package/dist/fp/automationState.js.map +1 -1
- package/dist/fp/commandDescription.d.ts +6 -0
- package/dist/fp/commandDescription.js +18 -5
- package/dist/fp/commandDescription.js.map +1 -1
- package/dist/fp/interpreterContext.d.ts +16 -5
- package/dist/fp/interpreterContext.js +59 -3
- package/dist/fp/interpreterContext.js.map +1 -1
- package/dist/fp/policyParser.js +18 -8
- package/dist/fp/policyParser.js.map +1 -1
- package/dist/haltPushDispatch.d.ts +4 -0
- package/dist/haltPushDispatch.js +7 -22
- package/dist/haltPushDispatch.js.map +1 -1
- package/dist/inboxRoutes.js +2 -2
- package/dist/inboxRoutes.js.map +1 -1
- package/dist/index.js +672 -135
- package/dist/index.js.map +1 -1
- package/dist/installGuard.js +6 -0
- package/dist/installGuard.js.map +1 -1
- package/dist/localEndpointGuard.d.ts +25 -0
- package/dist/localEndpointGuard.js +101 -0
- package/dist/localEndpointGuard.js.map +1 -0
- package/dist/mcpRoutes.js +1 -1
- package/dist/mcpRoutes.js.map +1 -1
- package/dist/oauth.d.ts +13 -0
- package/dist/oauth.js +53 -9
- package/dist/oauth.js.map +1 -1
- package/dist/orchestrator/childBridgeClient.d.ts +7 -0
- package/dist/orchestrator/childBridgeClient.js +42 -1
- package/dist/orchestrator/childBridgeClient.js.map +1 -1
- package/dist/orchestrator/orchestratorBridge.d.ts +33 -0
- package/dist/orchestrator/orchestratorBridge.js +75 -9
- package/dist/orchestrator/orchestratorBridge.js.map +1 -1
- package/dist/patchworkConfig.d.ts +4 -0
- package/dist/patchworkConfig.js +33 -4
- package/dist/patchworkConfig.js.map +1 -1
- package/dist/probe.js +39 -5
- package/dist/probe.js.map +1 -1
- package/dist/recipeOrchestration.d.ts +59 -0
- package/dist/recipeOrchestration.js +314 -60
- package/dist/recipeOrchestration.js.map +1 -1
- package/dist/recipeRoutes.d.ts +93 -0
- package/dist/recipeRoutes.js +664 -27
- package/dist/recipeRoutes.js.map +1 -1
- package/dist/recipes/RecipeOrchestrator.d.ts +14 -0
- package/dist/recipes/RecipeOrchestrator.js +42 -2
- package/dist/recipes/RecipeOrchestrator.js.map +1 -1
- package/dist/recipes/agentExecutor.d.ts +51 -2
- package/dist/recipes/agentExecutor.js +86 -13
- package/dist/recipes/agentExecutor.js.map +1 -1
- package/dist/recipes/chainedRunner.d.ts +111 -3
- package/dist/recipes/chainedRunner.js +440 -45
- package/dist/recipes/chainedRunner.js.map +1 -1
- package/dist/recipes/compiler.js +42 -29
- package/dist/recipes/compiler.js.map +1 -1
- package/dist/recipes/connectorPreflight.js +31 -0
- package/dist/recipes/connectorPreflight.js.map +1 -1
- package/dist/recipes/dependencyGraph.d.ts +16 -0
- package/dist/recipes/dependencyGraph.js +37 -2
- package/dist/recipes/dependencyGraph.js.map +1 -1
- package/dist/recipes/eventTriggerPrograms.d.ts +36 -0
- package/dist/recipes/eventTriggerPrograms.js +158 -0
- package/dist/recipes/eventTriggerPrograms.js.map +1 -0
- package/dist/recipes/githubInstallSource.d.ts +9 -2
- package/dist/recipes/githubInstallSource.js +36 -6
- package/dist/recipes/githubInstallSource.js.map +1 -1
- package/dist/recipes/haltCategory.d.ts +10 -0
- package/dist/recipes/haltCategory.js +11 -1
- package/dist/recipes/haltCategory.js.map +1 -1
- package/dist/recipes/idempotencyKey.d.ts +30 -3
- package/dist/recipes/idempotencyKey.js +89 -5
- package/dist/recipes/idempotencyKey.js.map +1 -1
- package/dist/recipes/installer.js +12 -35
- package/dist/recipes/installer.js.map +1 -1
- package/dist/recipes/judgeVerdict.d.ts +11 -1
- package/dist/recipes/judgeVerdict.js +47 -7
- package/dist/recipes/judgeVerdict.js.map +1 -1
- package/dist/recipes/names.d.ts +5 -0
- package/dist/recipes/names.js +10 -5
- package/dist/recipes/names.js.map +1 -1
- package/dist/recipes/parser.js +74 -10
- package/dist/recipes/parser.js.map +1 -1
- package/dist/recipes/pricing/costRouter.d.ts +43 -0
- package/dist/recipes/pricing/costRouter.js +44 -0
- package/dist/recipes/pricing/costRouter.js.map +1 -0
- package/dist/recipes/pricing/priceTable.d.ts +76 -0
- package/dist/recipes/pricing/priceTable.js +144 -0
- package/dist/recipes/pricing/priceTable.js.map +1 -0
- package/dist/recipes/replayRun.js +5 -2
- package/dist/recipes/replayRun.js.map +1 -1
- package/dist/recipes/resolveRecipePath.js +15 -1
- package/dist/recipes/resolveRecipePath.js.map +1 -1
- package/dist/recipes/runBudget.d.ts +93 -32
- package/dist/recipes/runBudget.js +213 -49
- package/dist/recipes/runBudget.js.map +1 -1
- package/dist/recipes/runRegistry.d.ts +32 -0
- package/dist/recipes/runRegistry.js +54 -0
- package/dist/recipes/runRegistry.js.map +1 -0
- package/dist/recipes/scheduler.js +6 -2
- package/dist/recipes/scheduler.js.map +1 -1
- package/dist/recipes/schema.d.ts +59 -6
- package/dist/recipes/schemaGenerator.d.ts +9 -0
- package/dist/recipes/schemaGenerator.js +446 -77
- package/dist/recipes/schemaGenerator.js.map +1 -1
- package/dist/recipes/simulation/aggregateRunRisk.d.ts +46 -0
- package/dist/recipes/simulation/aggregateRunRisk.js +120 -0
- package/dist/recipes/simulation/aggregateRunRisk.js.map +1 -0
- package/dist/recipes/simulation/costProjector.d.ts +32 -0
- package/dist/recipes/simulation/costProjector.js +194 -0
- package/dist/recipes/simulation/costProjector.js.map +1 -0
- package/dist/recipes/simulation/sideEffects.d.ts +32 -0
- package/dist/recipes/simulation/sideEffects.js +62 -0
- package/dist/recipes/simulation/sideEffects.js.map +1 -0
- package/dist/recipes/simulation/simulate.d.ts +36 -0
- package/dist/recipes/simulation/simulate.js +264 -0
- package/dist/recipes/simulation/simulate.js.map +1 -0
- package/dist/recipes/simulation/simulateMockedRun.d.ts +52 -0
- package/dist/recipes/simulation/simulateMockedRun.js +84 -0
- package/dist/recipes/simulation/simulateMockedRun.js.map +1 -0
- package/dist/recipes/simulation/synthesizeMockedOutputs.d.ts +31 -0
- package/dist/recipes/simulation/synthesizeMockedOutputs.js +50 -0
- package/dist/recipes/simulation/synthesizeMockedOutputs.js.map +1 -0
- package/dist/recipes/simulation/types.d.ts +198 -0
- package/dist/recipes/simulation/types.js +30 -0
- package/dist/recipes/simulation/types.js.map +1 -0
- package/dist/recipes/stepObservation.d.ts +12 -0
- package/dist/recipes/stepObservation.js +21 -1
- package/dist/recipes/stepObservation.js.map +1 -1
- package/dist/recipes/toolRegistry.js +61 -24
- package/dist/recipes/toolRegistry.js.map +1 -1
- package/dist/recipes/tools/airtable.d.ts +15 -0
- package/dist/recipes/tools/airtable.js +240 -0
- package/dist/recipes/tools/airtable.js.map +1 -0
- package/dist/recipes/tools/caldiy.d.ts +13 -0
- package/dist/recipes/tools/caldiy.js +215 -0
- package/dist/recipes/tools/caldiy.js.map +1 -0
- package/dist/recipes/tools/circleci.d.ts +10 -0
- package/dist/recipes/tools/circleci.js +204 -0
- package/dist/recipes/tools/circleci.js.map +1 -0
- package/dist/recipes/tools/cloudflare.d.ts +13 -0
- package/dist/recipes/tools/cloudflare.js +211 -0
- package/dist/recipes/tools/cloudflare.js.map +1 -0
- package/dist/recipes/tools/confluence.js +11 -10
- package/dist/recipes/tools/confluence.js.map +1 -1
- package/dist/recipes/tools/datadog.js +13 -12
- package/dist/recipes/tools/datadog.js.map +1 -1
- package/dist/recipes/tools/docs.d.ts +18 -0
- package/dist/recipes/tools/docs.js +95 -0
- package/dist/recipes/tools/docs.js.map +1 -0
- package/dist/recipes/tools/elasticsearch.d.ts +11 -0
- package/dist/recipes/tools/elasticsearch.js +157 -0
- package/dist/recipes/tools/elasticsearch.js.map +1 -0
- package/dist/recipes/tools/fanOut.js +4 -4
- package/dist/recipes/tools/fanOut.js.map +1 -1
- package/dist/recipes/tools/figma.d.ts +12 -0
- package/dist/recipes/tools/figma.js +195 -0
- package/dist/recipes/tools/figma.js.map +1 -0
- package/dist/recipes/tools/file.js +43 -22
- package/dist/recipes/tools/file.js.map +1 -1
- package/dist/recipes/tools/github.js +162 -5
- package/dist/recipes/tools/github.js.map +1 -1
- package/dist/recipes/tools/gmail.js +45 -33
- package/dist/recipes/tools/gmail.js.map +1 -1
- package/dist/recipes/tools/grafana.d.ts +11 -0
- package/dist/recipes/tools/grafana.js +216 -0
- package/dist/recipes/tools/grafana.js.map +1 -0
- package/dist/recipes/tools/http.d.ts +1 -1
- package/dist/recipes/tools/http.js +10 -28
- package/dist/recipes/tools/http.js.map +1 -1
- package/dist/recipes/tools/hubspot.js +13 -12
- package/dist/recipes/tools/hubspot.js.map +1 -1
- package/dist/recipes/tools/index.d.ts +28 -0
- package/dist/recipes/tools/index.js +28 -0
- package/dist/recipes/tools/index.js.map +1 -1
- package/dist/recipes/tools/intercom.js +11 -10
- package/dist/recipes/tools/intercom.js.map +1 -1
- package/dist/recipes/tools/jira.js +13 -0
- package/dist/recipes/tools/jira.js.map +1 -1
- package/dist/recipes/tools/meetingNotes.js +2 -2
- package/dist/recipes/tools/monday.d.ts +22 -0
- package/dist/recipes/tools/monday.js +242 -0
- package/dist/recipes/tools/monday.js.map +1 -0
- package/dist/recipes/tools/mongodb.d.ts +19 -0
- package/dist/recipes/tools/mongodb.js +194 -0
- package/dist/recipes/tools/mongodb.js.map +1 -0
- package/dist/recipes/tools/notion.js +11 -10
- package/dist/recipes/tools/notion.js.map +1 -1
- package/dist/recipes/tools/obsidian.d.ts +15 -0
- package/dist/recipes/tools/obsidian.js +173 -0
- package/dist/recipes/tools/obsidian.js.map +1 -0
- package/dist/recipes/tools/outcomes.d.ts +15 -0
- package/dist/recipes/tools/outcomes.js +111 -0
- package/dist/recipes/tools/outcomes.js.map +1 -0
- package/dist/recipes/tools/paystack.d.ts +11 -0
- package/dist/recipes/tools/paystack.js +212 -0
- package/dist/recipes/tools/paystack.js.map +1 -0
- package/dist/recipes/tools/pipedrive.d.ts +16 -0
- package/dist/recipes/tools/pipedrive.js +234 -0
- package/dist/recipes/tools/pipedrive.js.map +1 -0
- package/dist/recipes/tools/postgres.d.ts +15 -0
- package/dist/recipes/tools/postgres.js +186 -0
- package/dist/recipes/tools/postgres.js.map +1 -0
- package/dist/recipes/tools/posthog.d.ts +16 -0
- package/dist/recipes/tools/posthog.js +219 -0
- package/dist/recipes/tools/posthog.js.map +1 -0
- package/dist/recipes/tools/redis.d.ts +9 -0
- package/dist/recipes/tools/redis.js +141 -0
- package/dist/recipes/tools/redis.js.map +1 -0
- package/dist/recipes/tools/resend.d.ts +8 -0
- package/dist/recipes/tools/resend.js +153 -0
- package/dist/recipes/tools/resend.js.map +1 -0
- package/dist/recipes/tools/salesforce.d.ts +16 -0
- package/dist/recipes/tools/salesforce.js +184 -0
- package/dist/recipes/tools/salesforce.js.map +1 -0
- package/dist/recipes/tools/sendgrid.d.ts +9 -0
- package/dist/recipes/tools/sendgrid.js +175 -0
- package/dist/recipes/tools/sendgrid.js.map +1 -0
- package/dist/recipes/tools/shopify.d.ts +16 -0
- package/dist/recipes/tools/shopify.js +266 -0
- package/dist/recipes/tools/shopify.js.map +1 -0
- package/dist/recipes/tools/snowflake.d.ts +16 -0
- package/dist/recipes/tools/snowflake.js +173 -0
- package/dist/recipes/tools/snowflake.js.map +1 -0
- package/dist/recipes/tools/stripe.js +13 -12
- package/dist/recipes/tools/stripe.js.map +1 -1
- package/dist/recipes/tools/supabase.d.ts +9 -0
- package/dist/recipes/tools/supabase.js +133 -0
- package/dist/recipes/tools/supabase.js.map +1 -0
- package/dist/recipes/tools/todoist.d.ts +15 -0
- package/dist/recipes/tools/todoist.js +228 -0
- package/dist/recipes/tools/todoist.js.map +1 -0
- package/dist/recipes/tools/twilio.d.ts +11 -0
- package/dist/recipes/tools/twilio.js +180 -0
- package/dist/recipes/tools/twilio.js.map +1 -0
- package/dist/recipes/tools/vercel.d.ts +9 -0
- package/dist/recipes/tools/vercel.js +146 -0
- package/dist/recipes/tools/vercel.js.map +1 -0
- package/dist/recipes/tools/webflow.d.ts +11 -0
- package/dist/recipes/tools/webflow.js +244 -0
- package/dist/recipes/tools/webflow.js.map +1 -0
- package/dist/recipes/tools/woocommerce.d.ts +18 -0
- package/dist/recipes/tools/woocommerce.js +260 -0
- package/dist/recipes/tools/woocommerce.js.map +1 -0
- package/dist/recipes/tools/wrapConnectorExecute.d.ts +25 -0
- package/dist/recipes/tools/wrapConnectorExecute.js +37 -0
- package/dist/recipes/tools/wrapConnectorExecute.js.map +1 -0
- package/dist/recipes/tools/zendesk.js +11 -10
- package/dist/recipes/tools/zendesk.js.map +1 -1
- package/dist/recipes/triggerVars.d.ts +15 -0
- package/dist/recipes/triggerVars.js +57 -0
- package/dist/recipes/triggerVars.js.map +1 -0
- package/dist/recipes/validation.d.ts +1 -1
- package/dist/recipes/validation.js +536 -12
- package/dist/recipes/validation.js.map +1 -1
- package/dist/recipes/yamlPositions.d.ts +1 -1
- package/dist/recipes/yamlPositions.js +7 -2
- package/dist/recipes/yamlPositions.js.map +1 -1
- package/dist/recipes/yamlRunner.d.ts +216 -4
- package/dist/recipes/yamlRunner.js +1030 -96
- package/dist/recipes/yamlRunner.js.map +1 -1
- package/dist/recipesHttp.d.ts +5 -0
- package/dist/recipesHttp.js +68 -6
- package/dist/recipesHttp.js.map +1 -1
- package/dist/resources.js +14 -1
- package/dist/resources.js.map +1 -1
- package/dist/riskSignals.d.ts +42 -0
- package/dist/riskSignals.js +197 -0
- package/dist/riskSignals.js.map +1 -0
- package/dist/riskTier.d.ts +9 -0
- package/dist/riskTier.js +48 -0
- package/dist/riskTier.js.map +1 -1
- package/dist/runLog.d.ts +53 -0
- package/dist/runLog.js +75 -16
- package/dist/runLog.js.map +1 -1
- package/dist/schemas/recipe.v1.json +360 -16
- package/dist/server.d.ts +60 -0
- package/dist/server.js +192 -31
- package/dist/server.js.map +1 -1
- package/dist/sessionCheckpoint.js +4 -1
- package/dist/sessionCheckpoint.js.map +1 -1
- package/dist/sessionDetail.d.ts +43 -0
- package/dist/sessionDetail.js +59 -0
- package/dist/sessionDetail.js.map +1 -0
- package/dist/settingsEnv.d.ts +47 -0
- package/dist/settingsEnv.js +88 -0
- package/dist/settingsEnv.js.map +1 -0
- package/dist/streamableHttp.js +44 -10
- package/dist/streamableHttp.js.map +1 -1
- package/dist/ta/backtest/abnday.d.ts +53 -0
- package/dist/ta/backtest/abnday.js +123 -0
- package/dist/ta/backtest/abnday.js.map +1 -0
- package/dist/ta/backtest/directional.d.ts +48 -0
- package/dist/ta/backtest/directional.js +103 -0
- package/dist/ta/backtest/directional.js.map +1 -0
- package/dist/ta/backtest/fundx.d.ts +67 -0
- package/dist/ta/backtest/fundx.js +264 -0
- package/dist/ta/backtest/fundx.js.map +1 -0
- package/dist/ta/backtest/fvg.d.ts +62 -0
- package/dist/ta/backtest/fvg.js +236 -0
- package/dist/ta/backtest/fvg.js.map +1 -0
- package/dist/ta/backtest/ichimoku.d.ts +41 -0
- package/dist/ta/backtest/ichimoku.js +146 -0
- package/dist/ta/backtest/ichimoku.js.map +1 -0
- package/dist/ta/backtest/ingest.d.ts +20 -0
- package/dist/ta/backtest/ingest.js +104 -0
- package/dist/ta/backtest/ingest.js.map +1 -0
- package/dist/ta/backtest/msb.d.ts +49 -0
- package/dist/ta/backtest/msb.js +162 -0
- package/dist/ta/backtest/msb.js.map +1 -0
- package/dist/ta/backtest/scoring.d.ts +33 -0
- package/dist/ta/backtest/scoring.js +82 -0
- package/dist/ta/backtest/scoring.js.map +1 -0
- package/dist/ta/backtest/summary.d.ts +20 -0
- package/dist/ta/backtest/summary.js +42 -0
- package/dist/ta/backtest/summary.js.map +1 -0
- package/dist/ta/backtest/tsmom.d.ts +72 -0
- package/dist/ta/backtest/tsmom.js +202 -0
- package/dist/ta/backtest/tsmom.js.map +1 -0
- package/dist/ta/backtest/volsq.d.ts +34 -0
- package/dist/ta/backtest/volsq.js +154 -0
- package/dist/ta/backtest/volsq.js.map +1 -0
- package/dist/ta/backtest/walkForward.d.ts +45 -0
- package/dist/ta/backtest/walkForward.js +136 -0
- package/dist/ta/backtest/walkForward.js.map +1 -0
- package/dist/ta/cycles.d.ts +15 -0
- package/dist/ta/cycles.js +31 -0
- package/dist/ta/cycles.js.map +1 -0
- package/dist/ta/desk/accrualEmitter.d.ts +59 -0
- package/dist/ta/desk/accrualEmitter.js +136 -0
- package/dist/ta/desk/accrualEmitter.js.map +1 -0
- package/dist/ta/desk/cellBacktest.d.ts +179 -0
- package/dist/ta/desk/cellBacktest.js +514 -0
- package/dist/ta/desk/cellBacktest.js.map +1 -0
- package/dist/ta/desk/cells/wpMaRejection.d.ts +49 -0
- package/dist/ta/desk/cells/wpMaRejection.js +141 -0
- package/dist/ta/desk/cells/wpMaRejection.js.map +1 -0
- package/dist/ta/desk/cells/wpVolumeClimax.d.ts +57 -0
- package/dist/ta/desk/cells/wpVolumeClimax.js +193 -0
- package/dist/ta/desk/cells/wpVolumeClimax.js.map +1 -0
- package/dist/ta/desk/collectors.d.ts +18 -0
- package/dist/ta/desk/collectors.js +450 -0
- package/dist/ta/desk/collectors.js.map +1 -0
- package/dist/ta/desk/contract.d.ts +51 -0
- package/dist/ta/desk/contract.js +261 -0
- package/dist/ta/desk/contract.js.map +1 -0
- package/dist/ta/desk/deskLedger.d.ts +36 -0
- package/dist/ta/desk/deskLedger.js +239 -0
- package/dist/ta/desk/deskLedger.js.map +1 -0
- package/dist/ta/desk/liqTape.d.ts +37 -0
- package/dist/ta/desk/liqTape.js +99 -0
- package/dist/ta/desk/liqTape.js.map +1 -0
- package/dist/ta/desk/post.d.ts +20 -0
- package/dist/ta/desk/post.js +65 -0
- package/dist/ta/desk/post.js.map +1 -0
- package/dist/ta/desk/render.d.ts +25 -0
- package/dist/ta/desk/render.js +398 -0
- package/dist/ta/desk/render.js.map +1 -0
- package/dist/ta/desk/surfaces.d.ts +216 -0
- package/dist/ta/desk/surfaces.js +472 -0
- package/dist/ta/desk/surfaces.js.map +1 -0
- package/dist/ta/desk/types.d.ts +174 -0
- package/dist/ta/desk/types.js +104 -0
- package/dist/ta/desk/types.js.map +1 -0
- package/dist/ta/index.d.ts +23 -0
- package/dist/ta/index.js +54 -0
- package/dist/ta/index.js.map +1 -0
- package/dist/ta/ledger.d.ts +44 -0
- package/dist/ta/ledger.js +95 -0
- package/dist/ta/ledger.js.map +1 -0
- package/dist/ta/levels.d.ts +21 -0
- package/dist/ta/levels.js +41 -0
- package/dist/ta/levels.js.map +1 -0
- package/dist/ta/swings.d.ts +30 -0
- package/dist/ta/swings.js +83 -0
- package/dist/ta/swings.js.map +1 -0
- package/dist/ta/types.d.ts +81 -0
- package/dist/ta/types.js +20 -0
- package/dist/ta/types.js.map +1 -0
- package/dist/telemetry.js +20 -9
- package/dist/telemetry.js.map +1 -1
- package/dist/tokenUsageTracker.d.ts +2 -1
- package/dist/tokenUsageTracker.js +38 -20
- package/dist/tokenUsageTracker.js.map +1 -1
- package/dist/tools/contextBundle.js +13 -9
- package/dist/tools/contextBundle.js.map +1 -1
- package/dist/tools/ctxGetTaskContext.js +8 -3
- package/dist/tools/ctxGetTaskContext.js.map +1 -1
- package/dist/tools/ctxQueryTraces.d.ts +18 -1
- package/dist/tools/ctxQueryTraces.js +176 -27
- package/dist/tools/ctxQueryTraces.js.map +1 -1
- package/dist/tools/enrichCommit.js +5 -2
- package/dist/tools/enrichCommit.js.map +1 -1
- package/dist/tools/getDependencyTree.js +2 -2
- package/dist/tools/getDependencyTree.js.map +1 -1
- package/dist/tools/getDiagnostics.js +3 -0
- package/dist/tools/getDiagnostics.js.map +1 -1
- package/dist/tools/getDocumentSymbols.js +5 -1
- package/dist/tools/getDocumentSymbols.js.map +1 -1
- package/dist/tools/getGitHotspots.js +12 -1
- package/dist/tools/getGitHotspots.js.map +1 -1
- package/dist/tools/getGitLog.js +5 -2
- package/dist/tools/getGitLog.js.map +1 -1
- package/dist/tools/getGitStatus.js +6 -1
- package/dist/tools/getGitStatus.js.map +1 -1
- package/dist/tools/getProjectContext.js +3 -1
- package/dist/tools/getProjectContext.js.map +1 -1
- package/dist/tools/getToolCapabilities.js +39 -4
- package/dist/tools/getToolCapabilities.js.map +1 -1
- package/dist/tools/gitWrite.js +12 -3
- package/dist/tools/gitWrite.js.map +1 -1
- package/dist/tools/github/composite.d.ts +1 -1
- package/dist/tools/github/composite.js +7 -7
- package/dist/tools/github/composite.js.map +1 -1
- package/dist/tools/github/pr.d.ts +6 -6
- package/dist/tools/github/pr.js +18 -11
- package/dist/tools/github/pr.js.map +1 -1
- package/dist/tools/httpClient.js +55 -3
- package/dist/tools/httpClient.js.map +1 -1
- package/dist/tools/index.d.ts +2 -1
- package/dist/tools/index.js +19 -12
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/lsp.d.ts +6 -0
- package/dist/tools/lsp.js +81 -12
- package/dist/tools/lsp.js.map +1 -1
- package/dist/tools/previewEdit.js +16 -1
- package/dist/tools/previewEdit.js.map +1 -1
- package/dist/tools/resumeClaudeTask.js +17 -0
- package/dist/tools/resumeClaudeTask.js.map +1 -1
- package/dist/tools/searchAndReplace.js +9 -5
- package/dist/tools/searchAndReplace.js.map +1 -1
- package/dist/tools/searchTools.d.ts +26 -0
- package/dist/tools/searchTools.js +42 -2
- package/dist/tools/searchTools.js.map +1 -1
- package/dist/tools/terminal.js +23 -51
- package/dist/tools/terminal.js.map +1 -1
- package/dist/tools/transaction.d.ts +9 -1
- package/dist/tools/transaction.js +59 -4
- package/dist/tools/transaction.js.map +1 -1
- package/dist/tools/utils.d.ts +16 -0
- package/dist/tools/utils.js +77 -7
- package/dist/tools/utils.js.map +1 -1
- package/dist/transport.d.ts +31 -0
- package/dist/transport.js +117 -14
- package/dist/transport.js.map +1 -1
- package/dist/wireHaltPushDispatch.js +12 -0
- package/dist/wireHaltPushDispatch.js.map +1 -1
- package/dist/workerGateDecisionLog.d.ts +141 -0
- package/dist/workerGateDecisionLog.js +361 -0
- package/dist/workerGateDecisionLog.js.map +1 -0
- package/dist/workers/actionClass.d.ts +38 -0
- package/dist/workers/actionClass.js +155 -0
- package/dist/workers/actionClass.js.map +1 -0
- package/dist/workers/backtest.d.ts +68 -0
- package/dist/workers/backtest.js +113 -0
- package/dist/workers/backtest.js.map +1 -0
- package/dist/workers/contextRisk.d.ts +35 -0
- package/dist/workers/contextRisk.js +20 -0
- package/dist/workers/contextRisk.js.map +1 -0
- package/dist/workers/contextRiskScorer.d.ts +48 -0
- package/dist/workers/contextRiskScorer.js +94 -0
- package/dist/workers/contextRiskScorer.js.map +1 -0
- package/dist/workers/graduation.d.ts +61 -0
- package/dist/workers/graduation.js +112 -0
- package/dist/workers/graduation.js.map +1 -0
- package/dist/workers/outcomeStore.d.ts +98 -0
- package/dist/workers/outcomeStore.js +146 -0
- package/dist/workers/outcomeStore.js.map +1 -0
- package/dist/workers/outcomesCli.d.ts +34 -0
- package/dist/workers/outcomesCli.js +91 -0
- package/dist/workers/outcomesCli.js.map +1 -0
- package/dist/workers/runWorkerShadow.d.ts +90 -0
- package/dist/workers/runWorkerShadow.js +323 -0
- package/dist/workers/runWorkerShadow.js.map +1 -0
- package/dist/workers/shadowGate.d.ts +30 -0
- package/dist/workers/shadowGate.js +58 -0
- package/dist/workers/shadowGate.js.map +1 -0
- package/dist/workers/shadowObserver.d.ts +165 -0
- package/dist/workers/shadowObserver.js +203 -0
- package/dist/workers/shadowObserver.js.map +1 -0
- package/dist/workers/shadowReport.d.ts +16 -0
- package/dist/workers/shadowReport.js +61 -0
- package/dist/workers/shadowReport.js.map +1 -0
- package/dist/workers/shadowRun.d.ts +38 -0
- package/dist/workers/shadowRun.js +44 -0
- package/dist/workers/shadowRun.js.map +1 -0
- package/dist/workers/trustLevel.d.ts +82 -0
- package/dist/workers/trustLevel.js +100 -0
- package/dist/workers/trustLevel.js.map +1 -0
- package/dist/workers/worker.d.ts +49 -0
- package/dist/workers/worker.js +88 -0
- package/dist/workers/worker.js.map +1 -0
- package/dist/workers/workerGate.d.ts +101 -0
- package/dist/workers/workerGate.js +210 -0
- package/dist/workers/workerGate.js.map +1 -0
- package/dist/workers/workerLevelStore.d.ts +33 -0
- package/dist/workers/workerLevelStore.js +87 -0
- package/dist/workers/workerLevelStore.js.map +1 -0
- package/dist/workers/workerLoader.d.ts +7 -0
- package/dist/workers/workerLoader.js +31 -0
- package/dist/workers/workerLoader.js.map +1 -0
- package/dist/writeFileAtomic.js +40 -13
- package/dist/writeFileAtomic.js.map +1 -1
- package/package.json +14 -6
- package/scripts/mcp-stdio-shim.cjs +247 -87
- package/templates/CLAUDE.bridge.md +1 -1
- package/templates/recipes/dependency-bump.yaml +33 -0
- package/templates/recipes/incident-to-pr.yaml +187 -0
- package/templates/recipes/outcome-ingester.yaml +53 -0
- package/templates/recipes/release-notes.yaml +30 -0
- package/templates/recipes/triage-failing-tests-autofile.yaml +152 -0
- package/templates/recipes/triage-failing-tests.yaml +38 -0
- package/templates/workers/dependency-upkeep.worker.yaml +26 -0
- package/templates/workers/release-notes.worker.yaml +17 -0
- package/templates/workers/test-guardian.worker.yaml +24 -0
- package/deploy/README.md +0 -172
- package/deploy/bootstrap-new-vps.sh +0 -364
- package/deploy/bootstrap-vps.sh +0 -192
- package/deploy/claude-ide-bridge.service.template +0 -67
- package/deploy/claude-ide-bridge@.service +0 -31
- package/deploy/deploy-dashboard.sh +0 -198
- package/deploy/deploy-landing.sh +0 -136
- package/deploy/ecosystem.config.js.example +0 -36
- package/deploy/install-vps-service.sh +0 -240
- package/deploy/macos/README.md +0 -153
- package/deploy/macos/com.patchwork.bridge.plist.template +0 -54
- package/deploy/macos/com.patchwork.tunnel.plist.template +0 -76
- package/deploy/macos/install-mac-bridge.sh +0 -244
- package/deploy/macos/uninstall-mac-bridge.sh +0 -22
- package/deploy/nginx-claude-bridge.conf.template +0 -129
|
@@ -6,14 +6,22 @@
|
|
|
6
6
|
* - Conditional step execution (when)
|
|
7
7
|
* - Dry-run mode
|
|
8
8
|
*/
|
|
9
|
+
import { classifyTool } from "../riskTier.js";
|
|
9
10
|
import { buildDependencyGraph, executeWithDependencies, } from "./dependencyGraph.js";
|
|
10
11
|
import { categoriseHaltReason, deriveHaltReasonFromError, } from "./haltCategory.js";
|
|
11
12
|
import { mockNestedRecipe, resolveNestedVars, validateNestedRecipe, } from "./nestedRecipeStep.js";
|
|
12
13
|
import { createOutputRegistry } from "./outputRegistry.js";
|
|
14
|
+
import { loadPriceTable } from "./pricing/priceTable.js";
|
|
13
15
|
import { resolveRecipePath } from "./resolveRecipePath.js";
|
|
14
|
-
import {
|
|
16
|
+
import { RunBudget } from "./runBudget.js";
|
|
17
|
+
import { registerRun, unregisterRun } from "./runRegistry.js";
|
|
18
|
+
import { captureForRunlog, detectSilentFail, redactSecretsForPrompt, } from "./stepObservation.js";
|
|
15
19
|
import { compileTemplate } from "./templateEngine.js";
|
|
16
|
-
import { evaluateStepExpect } from "./yamlRunner.js";
|
|
20
|
+
import { computeAgentCallUsage, declaredRecipeEnv, evaluateStepExpect, resolveRouting, } from "./yamlRunner.js";
|
|
21
|
+
/** Normalise the union AgentExecutor return into an AgentResult. */
|
|
22
|
+
function toChainedAgentResult(v) {
|
|
23
|
+
return typeof v === "string" ? { text: v } : v;
|
|
24
|
+
}
|
|
17
25
|
function nestedRecipeRef(step) {
|
|
18
26
|
return typeof step.recipe === "string"
|
|
19
27
|
? step.recipe
|
|
@@ -26,7 +34,7 @@ export function buildTemplateContext(registry, env) {
|
|
|
26
34
|
return registry.toTemplateContext(env);
|
|
27
35
|
}
|
|
28
36
|
/** Resolve all template strings in a step */
|
|
29
|
-
export function resolveStepTemplates(step, context) {
|
|
37
|
+
export function resolveStepTemplates(step, context, secretKeys) {
|
|
30
38
|
const resolved = {};
|
|
31
39
|
const errors = [];
|
|
32
40
|
// W3: keys that are recipe metadata, not tool params
|
|
@@ -46,6 +54,12 @@ export function resolveStepTemplates(step, context) {
|
|
|
46
54
|
"retry",
|
|
47
55
|
"retryDelay",
|
|
48
56
|
"parallel",
|
|
57
|
+
// Runner-meta declared on ChainedStep — must NOT be forwarded as tool
|
|
58
|
+
// params (the `expect` AJV schema object + numeric timeout especially leak
|
|
59
|
+
// internal runner config into tools that log their raw params).
|
|
60
|
+
"expect",
|
|
61
|
+
"timeout_ms",
|
|
62
|
+
"silentFailDetection",
|
|
49
63
|
]);
|
|
50
64
|
// Resolve tool params
|
|
51
65
|
for (const [key, value] of Object.entries(step)) {
|
|
@@ -89,10 +103,20 @@ export function resolveStepTemplates(step, context) {
|
|
|
89
103
|
// assert on err.code without needing to inspect step results).
|
|
90
104
|
resolveRecipePath(resolved.path, { write: toolId !== "file.read" });
|
|
91
105
|
}
|
|
92
|
-
// Resolve agent prompt if present
|
|
106
|
+
// Resolve agent prompt if present.
|
|
93
107
|
if (step.agent && typeof step.agent.prompt === "string") {
|
|
94
108
|
const compiled = compileTemplate(step.agent.prompt);
|
|
95
|
-
|
|
109
|
+
// Tier-1 #5 (audit 2026-06-22): redact recipe-declared secrets (`type: env`
|
|
110
|
+
// vars) from the LLM-facing prompt. Their raw values still flow to TOOL
|
|
111
|
+
// params above (an http header / DB password legitimately needs the
|
|
112
|
+
// secret), but must never reach the model verbatim. Mirrors the flat runner.
|
|
113
|
+
const promptContext = secretKeys && secretKeys.size > 0
|
|
114
|
+
? {
|
|
115
|
+
...context,
|
|
116
|
+
env: redactSecretsForPrompt(context.env, secretKeys),
|
|
117
|
+
}
|
|
118
|
+
: context;
|
|
119
|
+
const result = compiled.evaluate(promptContext);
|
|
96
120
|
if ("error" in result) {
|
|
97
121
|
errors.push(result.error);
|
|
98
122
|
}
|
|
@@ -102,34 +126,86 @@ export function resolveStepTemplates(step, context) {
|
|
|
102
126
|
}
|
|
103
127
|
// Evaluate when condition
|
|
104
128
|
let conditionResult = true;
|
|
105
|
-
if (step.when) {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
if (
|
|
109
|
-
errors.push(result.error);
|
|
129
|
+
if (step.when !== undefined) {
|
|
130
|
+
// M25: YAML may deliver `when: false` as a boolean even though the TS type
|
|
131
|
+
// says string — cast to unknown before comparing to silence the type error.
|
|
132
|
+
if (step.when === false) {
|
|
110
133
|
conditionResult = false;
|
|
111
134
|
}
|
|
112
|
-
else {
|
|
113
|
-
|
|
114
|
-
const
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
135
|
+
else if (step.when) {
|
|
136
|
+
const compiled = compileTemplate(step.when);
|
|
137
|
+
const result = compiled.evaluate(context);
|
|
138
|
+
if ("error" in result) {
|
|
139
|
+
errors.push(result.error);
|
|
140
|
+
conditionResult = false;
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
// Falsy if the WHOLE value is a falsy token OR its LAST token is — so a
|
|
144
|
+
// `when` fed an agent's free-text decision (prose ending in "true"/"false")
|
|
145
|
+
// gates on the verdict, not on "non-empty ⇒ truthy". Single-token guards
|
|
146
|
+
// are unchanged (last token === whole value). Kept in lockstep with
|
|
147
|
+
// yamlRunner.ts's guard (runner behavioral parity).
|
|
148
|
+
const val = result.value.trim().toLowerCase();
|
|
149
|
+
const FALSY = new Set(["", "0", "false", "null", "undefined"]);
|
|
150
|
+
const lastToken = (val.split(/\s+/).pop() ?? "").replace(/[^a-z0-9]/g, "");
|
|
151
|
+
conditionResult = !!val && !FALSY.has(val) && !FALSY.has(lastToken);
|
|
152
|
+
}
|
|
121
153
|
}
|
|
122
154
|
}
|
|
123
155
|
return { resolved, conditionResult, errors };
|
|
124
156
|
}
|
|
157
|
+
/**
|
|
158
|
+
* Race `work` against a per-step timeout. Returns `work` unchanged when no
|
|
159
|
+
* positive timeout is set. On expiry the returned promise rejects with a
|
|
160
|
+
* `step_timeout` error (executeChainedStep's catch turns that into a step
|
|
161
|
+
* failure). The underlying call is not cancellable here — it keeps running but
|
|
162
|
+
* its result is ignored — matching the flat runner's documented behaviour.
|
|
163
|
+
*/
|
|
164
|
+
function raceStepTimeout(work, timeoutMs, label, signal) {
|
|
165
|
+
const hasTimeout = typeof timeoutMs === "number" && timeoutMs > 0;
|
|
166
|
+
if (!hasTimeout && !signal)
|
|
167
|
+
return work;
|
|
168
|
+
// Run-cancel: reject promptly when the run is aborted so an in-flight
|
|
169
|
+
// tool/agent step doesn't block the run from draining (the underlying call
|
|
170
|
+
// keeps running but its result is ignored — same contract as the timeout).
|
|
171
|
+
if (signal?.aborted) {
|
|
172
|
+
return Promise.reject(new Error(`step_cancelled: run aborted before step "${label}" completed`));
|
|
173
|
+
}
|
|
174
|
+
const racers = [];
|
|
175
|
+
let timer;
|
|
176
|
+
if (hasTimeout) {
|
|
177
|
+
racers.push(new Promise((_, reject) => {
|
|
178
|
+
timer = setTimeout(() => {
|
|
179
|
+
reject(new Error(`step_timeout: exceeded ${timeoutMs}ms in step "${label}"`));
|
|
180
|
+
}, timeoutMs);
|
|
181
|
+
}));
|
|
182
|
+
}
|
|
183
|
+
let onAbort;
|
|
184
|
+
if (signal) {
|
|
185
|
+
racers.push(new Promise((_, reject) => {
|
|
186
|
+
onAbort = () => reject(new Error(`step_cancelled: run aborted during step "${label}"`));
|
|
187
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
188
|
+
}));
|
|
189
|
+
}
|
|
190
|
+
return Promise.race([work, ...racers]).finally(() => {
|
|
191
|
+
if (timer)
|
|
192
|
+
clearTimeout(timer);
|
|
193
|
+
if (signal && onAbort)
|
|
194
|
+
signal.removeEventListener("abort", onAbort);
|
|
195
|
+
});
|
|
196
|
+
}
|
|
125
197
|
/** Execute a single step */
|
|
126
198
|
export async function executeChainedStep(ctx, deps) {
|
|
127
|
-
const { registry, step, options, depth } = ctx;
|
|
199
|
+
const { registry, step, options, depth, recipe } = ctx;
|
|
128
200
|
const { dryRun } = options;
|
|
129
201
|
// Build template context
|
|
130
202
|
const templateContext = buildTemplateContext(registry, options.env);
|
|
203
|
+
// Tier-1 #5 (audit 2026-06-22): keys declared via a recipe-level `type: env`
|
|
204
|
+
// block are secrets — redacted from the LLM-facing agent prompt (but kept
|
|
205
|
+
// real for tool params). Mirrors the flat runner's `secretKeys`.
|
|
206
|
+
const secretKeys = new Set(Object.keys(declaredRecipeEnv(recipe, options.env)));
|
|
131
207
|
// Resolve templates
|
|
132
|
-
const { resolved, conditionResult, errors } = resolveStepTemplates(step, templateContext);
|
|
208
|
+
const { resolved, conditionResult, errors } = resolveStepTemplates(step, templateContext, secretKeys);
|
|
133
209
|
if (errors.length > 0) {
|
|
134
210
|
return {
|
|
135
211
|
success: false,
|
|
@@ -212,6 +288,60 @@ export async function executeChainedStep(ctx, deps) {
|
|
|
212
288
|
}
|
|
213
289
|
// Execute based on step type
|
|
214
290
|
try {
|
|
291
|
+
// Tier-1 #6 (audit 2026-06-22): budget admission BEFORE dispatch, gating
|
|
292
|
+
// ALL step types — not just agent steps. The admission check used to live
|
|
293
|
+
// inside the `if (step.agent)` branch, so a breached budget kept executing
|
|
294
|
+
// tool / nested-recipe steps unbounded. admit() is a pure read-only breach
|
|
295
|
+
// check (no counter side-effects), so gating every step is safe;
|
|
296
|
+
// subscription drivers report no usage and fail open inside RunBudget, so
|
|
297
|
+
// this is a no-op until a measured agent step breaches the cap. Mirrors the
|
|
298
|
+
// flat runner ("Bug (3)").
|
|
299
|
+
const budget = ctx.budget;
|
|
300
|
+
if (budget) {
|
|
301
|
+
const admission = budget.admit();
|
|
302
|
+
if (!admission.admitted) {
|
|
303
|
+
return {
|
|
304
|
+
success: false,
|
|
305
|
+
error: admission.reason ?? "Run exceeded its budget — budget_exceeded.",
|
|
306
|
+
resolvedParams: resolved,
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
// Tier-1 #4 (audit 2026-06-22): approval gate, mirroring the flat runner.
|
|
311
|
+
// Engages only when the bridge injected `requireApprovalFn` (approvalGate
|
|
312
|
+
// != "off"), respects the per-recipe `requireApproval: false` opt-out, and
|
|
313
|
+
// gates only the TOP-LEVEL run (depth === 0). Safe-by-default rationale: a
|
|
314
|
+
// chained recipe always has `trigger.type: "chained"` (that's how
|
|
315
|
+
// dispatchRecipe routes it), so cron/webhook can never fire one directly —
|
|
316
|
+
// they route to the flat runner. A top-level chained run is therefore the
|
|
317
|
+
// user-dispatched (interactive) case the flat runner gates as "manual"; a
|
|
318
|
+
// NESTED chained recipe (depth > 0) runs under a parent whose own gating
|
|
319
|
+
// already governed admission, so re-gating every nested step would block
|
|
320
|
+
// automated parents mid-flight. The injected fn applies the tier threshold
|
|
321
|
+
// itself and returns `true` for steps that don't need sign-off; a `false`
|
|
322
|
+
// result is an explicit human rejection → halt the step (and, via the
|
|
323
|
+
// dependency graph, its dependents). Only agent/tool steps are gated;
|
|
324
|
+
// nested-recipe steps are gated through their own inner steps.
|
|
325
|
+
if (deps.requireApprovalFn &&
|
|
326
|
+
depth === 0 &&
|
|
327
|
+
(step.agent || step.tool) &&
|
|
328
|
+
recipe.requireApproval !== false) {
|
|
329
|
+
const approvalToolId = step.agent ? "agent" : (step.tool ?? "unknown");
|
|
330
|
+
const approved = await deps.requireApprovalFn({
|
|
331
|
+
toolId: approvalToolId,
|
|
332
|
+
tier: classifyTool(approvalToolId),
|
|
333
|
+
summary: step.agent ? "agent step" : `tool ${approvalToolId}`,
|
|
334
|
+
params: step.agent ? undefined : resolved,
|
|
335
|
+
...(options.signal && { signal: options.signal }), // L1
|
|
336
|
+
});
|
|
337
|
+
if (!approved) {
|
|
338
|
+
return {
|
|
339
|
+
success: false,
|
|
340
|
+
error: "Step rejected by approval gate — approval_rejected.",
|
|
341
|
+
resolvedParams: resolved,
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
}
|
|
215
345
|
const recipeRef = nestedRecipeRef(step);
|
|
216
346
|
if (recipeRef) {
|
|
217
347
|
// Nested recipe call
|
|
@@ -264,6 +394,8 @@ export async function executeChainedStep(ctx, deps) {
|
|
|
264
394
|
maxDepth: options.maxDepth,
|
|
265
395
|
sourcePath: nestedRecipe.sourcePath,
|
|
266
396
|
env: { ...options.env, ...resolvedVars }, // Merge resolved vars into env
|
|
397
|
+
budget: ctx.budget, // share the single per-run budget across the tree
|
|
398
|
+
priceTable: ctx.priceTable, // reuse the once-loaded price table
|
|
267
399
|
};
|
|
268
400
|
const childResult = await runChainedRecipe(nestedRecipe.recipe, childOptions, deps, childRegistry, depth + 1);
|
|
269
401
|
return {
|
|
@@ -278,16 +410,89 @@ export async function executeChainedStep(ctx, deps) {
|
|
|
278
410
|
else if (step.agent) {
|
|
279
411
|
// Agent step
|
|
280
412
|
const prompt = resolved.agentPrompt ?? step.agent.prompt;
|
|
281
|
-
|
|
413
|
+
// Budget admission already happened at the top of this try block
|
|
414
|
+
// (Tier-1 #6) — it now gates every step type, so the agent-only check
|
|
415
|
+
// that used to live here was removed. `budget` is the try-scoped const.
|
|
416
|
+
// Phase 4: opt-in cost-aware routing. No-op when the step has no
|
|
417
|
+
// `downshift` list or no USD cap is set on the run budget.
|
|
418
|
+
let dispatchDriver = step.agent.driver;
|
|
419
|
+
let dispatchModel = step.agent.model;
|
|
420
|
+
if (budget && step.agent.downshift?.length) {
|
|
421
|
+
const routed = resolveRouting({ driver: step.agent.driver, model: step.agent.model }, step.agent.downshift, prompt, budget);
|
|
422
|
+
dispatchDriver = routed.driver === "api" ? "anthropic" : routed.driver;
|
|
423
|
+
dispatchModel = routed.model;
|
|
424
|
+
}
|
|
425
|
+
const agentReturn = toChainedAgentResult(await raceStepTimeout(deps.executeAgent(prompt, dispatchModel, dispatchDriver, {
|
|
426
|
+
...(step.agent.mcpAccess !== undefined && {
|
|
427
|
+
mcpAccess: step.agent.mcpAccess,
|
|
428
|
+
}),
|
|
429
|
+
...(step.agent.sandbox !== undefined && {
|
|
430
|
+
sandbox: step.agent.sandbox,
|
|
431
|
+
}),
|
|
432
|
+
...(step.agent.tools !== undefined && {
|
|
433
|
+
allowedTools: step.agent.tools,
|
|
434
|
+
}),
|
|
435
|
+
...(step.agent.disallowedTools !== undefined && {
|
|
436
|
+
disallowedTools: step.agent.disallowedTools,
|
|
437
|
+
}),
|
|
438
|
+
}), step.timeout_ms, step.id, options.signal));
|
|
439
|
+
// Reconcile REAL usage after dispatch (the closure now surfaces it
|
|
440
|
+
// instead of discarding it). Subscription drivers report no usage →
|
|
441
|
+
// RunBudget fails open with a one-time warning.
|
|
442
|
+
if (budget) {
|
|
443
|
+
// Resolve the driver this call should be BILLED against, mirroring the
|
|
444
|
+
// flat runner exactly (#850 parity):
|
|
445
|
+
// 1. Prefer the driver `_executeAgent` actually resolved+stamped
|
|
446
|
+
// (`servedBy.driver`) — the single source of auto-detect truth.
|
|
447
|
+
// 2. Normalize the "api" alias to "anthropic" (the downshift branch
|
|
448
|
+
// already does this) so a metered call with `agent.driver: "api"`
|
|
449
|
+
// and no `servedBy` is charged, not silently dropped.
|
|
450
|
+
// 3. When the step declares NO driver and the call left no
|
|
451
|
+
// `servedBy`, default to "anthropic" — the same API path
|
|
452
|
+
// `_executeAgent`'s auto-detect lands on for an unspecified
|
|
453
|
+
// driver. The flat runner reaches this default via `servedBy`
|
|
454
|
+
// (it always routes through `_executeAgent`); the chained runner
|
|
455
|
+
// may be handed an injected `executeAgent` that returns measured
|
|
456
|
+
// usage without a `servedBy`, so it must apply the same default
|
|
457
|
+
// here or usdMax would be enforced on one runner and not the
|
|
458
|
+
// other for identical recipes. A subscription/CLI call that
|
|
459
|
+
// genuinely incurs no metered cost stamps `servedBy.driver:
|
|
460
|
+
// "subprocess"` via `_executeAgent`, so it never reaches this
|
|
461
|
+
// default and stays correctly fail-open.
|
|
462
|
+
const reconcileDriver = agentReturn.servedBy?.driver ??
|
|
463
|
+
(dispatchDriver === "api" || dispatchDriver === undefined
|
|
464
|
+
? "anthropic"
|
|
465
|
+
: dispatchDriver);
|
|
466
|
+
budget.reconcile(reconcileDriver, agentReturn.usage, agentReturn.servedBy?.model ?? dispatchModel, {
|
|
467
|
+
inputChars: prompt.length,
|
|
468
|
+
outputChars: agentReturn.text.length,
|
|
469
|
+
});
|
|
470
|
+
}
|
|
471
|
+
// P1: per-step agent token usage (chained = one agent call per step).
|
|
472
|
+
// Attached to every return below so it lands on the persisted step row
|
|
473
|
+
// regardless of success / failure. Absent for unmeasured drivers.
|
|
474
|
+
// Thread the once-loaded price table to avoid per-step disk I/O.
|
|
475
|
+
const usage = computeAgentCallUsage(agentReturn.usage, agentReturn.servedBy, ctx.priceTable);
|
|
476
|
+
let result = agentReturn.text;
|
|
282
477
|
// Detect failure signals returned as sentinel strings (mirrors flat runner)
|
|
283
478
|
if (typeof result === "string" &&
|
|
284
479
|
result.startsWith("[agent step failed:")) {
|
|
285
|
-
return {
|
|
480
|
+
return {
|
|
481
|
+
success: false,
|
|
482
|
+
error: result,
|
|
483
|
+
resolvedParams: resolved,
|
|
484
|
+
...(usage ? { usage } : {}),
|
|
485
|
+
};
|
|
286
486
|
}
|
|
287
487
|
const agentSilentFail = step.silentFailDetection !== false ? detectSilentFail(result) : null;
|
|
288
488
|
if (agentSilentFail) {
|
|
289
489
|
const reason = `silent-fail detected (${agentSilentFail.reason}): ${agentSilentFail.matched}`;
|
|
290
|
-
return {
|
|
490
|
+
return {
|
|
491
|
+
success: false,
|
|
492
|
+
error: reason,
|
|
493
|
+
resolvedParams: resolved,
|
|
494
|
+
...(usage ? { usage } : {}),
|
|
495
|
+
};
|
|
291
496
|
}
|
|
292
497
|
if (step.transform) {
|
|
293
498
|
try {
|
|
@@ -297,21 +502,34 @@ export async function executeChainedStep(ctx, deps) {
|
|
|
297
502
|
console.warn(`transform failed for step ${step.id}: ${err}`);
|
|
298
503
|
}
|
|
299
504
|
}
|
|
505
|
+
let agentExpectWarnings;
|
|
300
506
|
if (step.expect) {
|
|
301
507
|
const failures = await evaluateStepExpect(step.expect, result);
|
|
302
|
-
if (failures.length > 0
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
508
|
+
if (failures.length > 0) {
|
|
509
|
+
if ((step.expect.on_fail ?? "halt") === "halt") {
|
|
510
|
+
return {
|
|
511
|
+
success: false,
|
|
512
|
+
error: `expect failed: ${failures.join("; ")}`,
|
|
513
|
+
resolvedParams: resolved,
|
|
514
|
+
...(usage ? { usage } : {}),
|
|
515
|
+
};
|
|
516
|
+
}
|
|
517
|
+
// on_fail: "warn" — keep going but surface the failures (mirrors
|
|
518
|
+
// the flat runner's `last.expectWarnings`); previously dropped.
|
|
519
|
+
agentExpectWarnings = failures;
|
|
308
520
|
}
|
|
309
521
|
}
|
|
310
|
-
return {
|
|
522
|
+
return {
|
|
523
|
+
success: true,
|
|
524
|
+
data: result,
|
|
525
|
+
resolvedParams: resolved,
|
|
526
|
+
...(usage ? { usage } : {}),
|
|
527
|
+
...(agentExpectWarnings ? { expectWarnings: agentExpectWarnings } : {}),
|
|
528
|
+
};
|
|
311
529
|
}
|
|
312
530
|
else if (step.tool) {
|
|
313
531
|
// Tool step
|
|
314
|
-
let result = await deps.executeTool(step.tool, resolved);
|
|
532
|
+
let result = await raceStepTimeout(deps.executeTool(step.tool, resolved), step.timeout_ms, step.id, options.signal);
|
|
315
533
|
// Detect tool-level errors reported as JSON {ok: false, error: ...} (mirrors flat runner)
|
|
316
534
|
if (typeof result === "string") {
|
|
317
535
|
try {
|
|
@@ -341,17 +559,28 @@ export async function executeChainedStep(ctx, deps) {
|
|
|
341
559
|
console.warn(`transform failed for step ${step.id}: ${err}`);
|
|
342
560
|
}
|
|
343
561
|
}
|
|
562
|
+
let toolExpectWarnings;
|
|
344
563
|
if (step.expect) {
|
|
345
564
|
const failures = await evaluateStepExpect(step.expect, result);
|
|
346
|
-
if (failures.length > 0
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
565
|
+
if (failures.length > 0) {
|
|
566
|
+
if ((step.expect.on_fail ?? "halt") === "halt") {
|
|
567
|
+
return {
|
|
568
|
+
success: false,
|
|
569
|
+
error: `expect failed: ${failures.join("; ")}`,
|
|
570
|
+
resolvedParams: resolved,
|
|
571
|
+
};
|
|
572
|
+
}
|
|
573
|
+
// on_fail: "warn" — keep going but surface the failures (mirrors
|
|
574
|
+
// the flat runner's `last.expectWarnings`); previously dropped.
|
|
575
|
+
toolExpectWarnings = failures;
|
|
352
576
|
}
|
|
353
577
|
}
|
|
354
|
-
return {
|
|
578
|
+
return {
|
|
579
|
+
success: true,
|
|
580
|
+
data: result,
|
|
581
|
+
resolvedParams: resolved,
|
|
582
|
+
...(toolExpectWarnings ? { expectWarnings: toolExpectWarnings } : {}),
|
|
583
|
+
};
|
|
355
584
|
}
|
|
356
585
|
else {
|
|
357
586
|
return { success: false, error: "Step has no tool, agent, or recipe" };
|
|
@@ -362,15 +591,40 @@ export async function executeChainedStep(ctx, deps) {
|
|
|
362
591
|
return { success: false, error: msg };
|
|
363
592
|
}
|
|
364
593
|
}
|
|
594
|
+
/** Upper bound on retries — clamps absurd/misconfigured values so a recipe
|
|
595
|
+
* can't spin a step unboundedly. Mirrors the `maximum` on `retry` in the
|
|
596
|
+
* recipe JSON schema. */
|
|
597
|
+
const MAX_RETRIES = 20;
|
|
365
598
|
async function withRetry(fn, maxRetries, delayMs) {
|
|
599
|
+
// Audit 2026-06-03 (HIGH #8): clamp the retry count. A negative value (typo
|
|
600
|
+
// like `retry: -1`) made `attempt <= maxRetries` immediately false, so the
|
|
601
|
+
// step NEVER ran and silently reported as failed. A non-finite or huge value
|
|
602
|
+
// would spin unboundedly. Floor to an integer in [0, MAX_RETRIES]; non-finite
|
|
603
|
+
// → 0 (run once, no retries).
|
|
604
|
+
const safeRetries = Number.isFinite(maxRetries)
|
|
605
|
+
? Math.min(MAX_RETRIES, Math.max(0, Math.floor(maxRetries)))
|
|
606
|
+
: 0;
|
|
366
607
|
let last = { success: false };
|
|
367
|
-
for (let attempt = 0; attempt <=
|
|
608
|
+
for (let attempt = 0; attempt <= safeRetries; attempt++) {
|
|
368
609
|
if (attempt > 0) {
|
|
369
610
|
await new Promise((r) => setTimeout(r, delayMs));
|
|
370
611
|
}
|
|
371
612
|
last = await fn();
|
|
372
613
|
if (last.success)
|
|
373
614
|
return last;
|
|
615
|
+
// Audit 2026-06-10 recipe-runners-2: do NOT retry on a step_timeout. The
|
|
616
|
+
// timed-out attempt's tool/agent call keeps running in the background
|
|
617
|
+
// (raceStepTimeout only abandons the wait, it does not cancel the call);
|
|
618
|
+
// re-issuing the step is pointless for write tools (the in-flight
|
|
619
|
+
// idempotency ledger short-circuits the retry to the same promise) and a
|
|
620
|
+
// duplicate-side-effect hazard for any tool the ledger cannot dedup. A
|
|
621
|
+
// true cancel needs an AbortSignal threaded through every tool/connector,
|
|
622
|
+
// which is out of scope; refusing to retry on timeout is the safe contract.
|
|
623
|
+
if (last.error?.startsWith("step_timeout:"))
|
|
624
|
+
return last;
|
|
625
|
+
// Do not retry cancelled steps — the run was aborted intentionally.
|
|
626
|
+
if (last.error?.startsWith("step_cancelled:"))
|
|
627
|
+
return last;
|
|
374
628
|
}
|
|
375
629
|
return last;
|
|
376
630
|
}
|
|
@@ -398,6 +652,14 @@ export function expandParallelSteps(steps) {
|
|
|
398
652
|
if (Array.isArray(step.parallel) && step.parallel.length > 0) {
|
|
399
653
|
// Generate a stable group id from position if the group has no id.
|
|
400
654
|
const groupId = step.id ?? `parallel_${i}`;
|
|
655
|
+
// Track the group container id too — otherwise a later flat step reusing
|
|
656
|
+
// the same id passes the duplicate check and silently corrupts the
|
|
657
|
+
// dependency graph (dependents `awaits: [groupId]` are rewritten to the
|
|
658
|
+
// group's children, never the shadowed flat step).
|
|
659
|
+
if (seenIds.has(groupId)) {
|
|
660
|
+
throw new Error(`expandParallelSteps: duplicate step id "${groupId}" — each step id must be unique`);
|
|
661
|
+
}
|
|
662
|
+
seenIds.add(groupId);
|
|
401
663
|
const groupAwaits = step.awaits ?? [];
|
|
402
664
|
const childIds = [];
|
|
403
665
|
for (let j = 0; j < step.parallel.length; j++) {
|
|
@@ -422,6 +684,22 @@ export function expandParallelSteps(steps) {
|
|
|
422
684
|
}
|
|
423
685
|
groupChildren.set(groupId, childIds);
|
|
424
686
|
}
|
|
687
|
+
else if (typeof step.parallel === "object" &&
|
|
688
|
+
step.parallel !== null &&
|
|
689
|
+
!Array.isArray(step.parallel) &&
|
|
690
|
+
"each" in step.parallel) {
|
|
691
|
+
// parallel: { each, as, steps } — runtime map-reduce over an array that a
|
|
692
|
+
// PRIOR step produces. Real support needs dynamic agent fan-out (per-iter
|
|
693
|
+
// budget/judge/silent-fail) the chained engine doesn't have yet, and the
|
|
694
|
+
// array isn't known at plan-expansion time. This object form used to fall
|
|
695
|
+
// through to the flat-step branch below and execute ZERO iterations —
|
|
696
|
+
// silent data loss with no signal. Fail loud instead. (audit
|
|
697
|
+
// recipe-chained-1)
|
|
698
|
+
throw new Error(`Step "${step.id ?? `parallel_${i}`}" uses parallel:{ each } ` +
|
|
699
|
+
`(runtime map-reduce), which is not yet implemented in chained ` +
|
|
700
|
+
"recipes. Use the `fan_out` tool step for tool-only loops; agent " +
|
|
701
|
+
"fan-out over a runtime-produced array is not yet supported.");
|
|
702
|
+
}
|
|
425
703
|
else {
|
|
426
704
|
if (step.id && seenIds.has(step.id)) {
|
|
427
705
|
throw new Error(`expandParallelSteps: duplicate step id "${step.id}" — each step id must be unique`);
|
|
@@ -463,6 +741,20 @@ export async function runChainedRecipe(recipe, options, deps, existingRegistry,
|
|
|
463
741
|
}
|
|
464
742
|
}
|
|
465
743
|
const registry = existingRegistry ?? createOutputRegistry();
|
|
744
|
+
// Per-run budget: build once at the top level from `recipe.budget`; nested
|
|
745
|
+
// recipe calls inherit the SAME instance via `options.budget` so the whole
|
|
746
|
+
// tree shares one cap (mirrors the flat path's single RunBudget per run).
|
|
747
|
+
// Absent `recipe.budget` → an empty RunBudget whose admit/reconcile are
|
|
748
|
+
// no-ops, so there's no behaviour change for budget-less recipes.
|
|
749
|
+
const budget = options.budget ?? new RunBudget(recipe.budget);
|
|
750
|
+
// True only when THIS call constructed the budget — used to gate
|
|
751
|
+
// refreshPrices() so an injected (unit-test) budget keeps its stable prices
|
|
752
|
+
// and nested calls don't double-refresh the shared parent budget.
|
|
753
|
+
const ownsBudget = options.budget === undefined;
|
|
754
|
+
// Load the price table ONCE per run (inherited by nested recipe calls via
|
|
755
|
+
// options.priceTable) so each agent step's computeAgentCallUsage avoids a
|
|
756
|
+
// synchronous existsSync/readFileSync round-trip — mirrors the flat runner.
|
|
757
|
+
const priceTable = options.priceTable ?? loadPriceTable();
|
|
466
758
|
// Expand parallel: sugar into flat steps before building the dependency graph.
|
|
467
759
|
const steps = expandParallelSteps(recipe.steps);
|
|
468
760
|
// Build dependency graph
|
|
@@ -496,7 +788,17 @@ export async function runChainedRecipe(recipe, options, deps, existingRegistry,
|
|
|
496
788
|
// Non-fatal — run-log failures must never break recipe execution.
|
|
497
789
|
}
|
|
498
790
|
}
|
|
499
|
-
|
|
791
|
+
// A phantom `awaits:` target (one matching no real step) is invisible to
|
|
792
|
+
// cycle detection but would silently drop the awaiting step and its
|
|
793
|
+
// dependents from execution while the run still reports success. Reject
|
|
794
|
+
// the run with a descriptive error BEFORE executing any step, mirroring
|
|
795
|
+
// the circular-dependency guard below.
|
|
796
|
+
const fatalGraphError = depGraph.unknownAwaitTargets.length > 0
|
|
797
|
+
? `Recipe has steps that await unknown step(s): ${depGraph.unknownAwaitTargets.join(", ")}`
|
|
798
|
+
: depGraph.hasCycles
|
|
799
|
+
? "Recipe has circular dependencies"
|
|
800
|
+
: undefined;
|
|
801
|
+
if (fatalGraphError) {
|
|
500
802
|
if (depth === 0) {
|
|
501
803
|
const doneAt = Date.now();
|
|
502
804
|
const durationMs = doneAt - runStartedAt;
|
|
@@ -507,7 +809,7 @@ export async function runChainedRecipe(recipe, options, deps, existingRegistry,
|
|
|
507
809
|
doneAt,
|
|
508
810
|
durationMs,
|
|
509
811
|
stepResults: [],
|
|
510
|
-
errorMessage:
|
|
812
|
+
errorMessage: fatalGraphError,
|
|
511
813
|
});
|
|
512
814
|
}
|
|
513
815
|
else if (options.runLogDir) {
|
|
@@ -522,7 +824,7 @@ export async function runChainedRecipe(recipe, options, deps, existingRegistry,
|
|
|
522
824
|
startedAt: runStartedAt,
|
|
523
825
|
doneAt,
|
|
524
826
|
durationMs,
|
|
525
|
-
errorMessage:
|
|
827
|
+
errorMessage: fatalGraphError,
|
|
526
828
|
stepResults: [],
|
|
527
829
|
});
|
|
528
830
|
}
|
|
@@ -535,7 +837,7 @@ export async function runChainedRecipe(recipe, options, deps, existingRegistry,
|
|
|
535
837
|
success: false,
|
|
536
838
|
stepResults: new Map(),
|
|
537
839
|
summary: { total: 0, succeeded: 0, failed: 0, skipped: 0 },
|
|
538
|
-
errorMessage:
|
|
840
|
+
errorMessage: fatalGraphError,
|
|
539
841
|
context: {},
|
|
540
842
|
};
|
|
541
843
|
}
|
|
@@ -553,6 +855,13 @@ export async function runChainedRecipe(recipe, options, deps, existingRegistry,
|
|
|
553
855
|
// Populated at the `registry.set(...)` site during step execution and
|
|
554
856
|
// attached to the final `RunStepResult` written via `completeRun`.
|
|
555
857
|
const capturedStepData = new Map();
|
|
858
|
+
// P1 cost/token corpus — per-step agent usage, keyed by stepId. Populated
|
|
859
|
+
// for every depth-0 agent step (independent of the VD-2 runLog-gated
|
|
860
|
+
// capture above) so both the bridge and CLI persistence paths get it.
|
|
861
|
+
const capturedUsage = new Map();
|
|
862
|
+
// `expect` warnings (on_fail: "warn") per depth-0 step, keyed by stepId, so
|
|
863
|
+
// they land on the persisted step row instead of being silently dropped.
|
|
864
|
+
const capturedExpectWarnings = new Map();
|
|
556
865
|
// VD-1 live-tail: when an `activityLog` is provided AND we have a `runSeq`
|
|
557
866
|
// (depth 0, runLog supplied), broadcast `recipe_step_start` /
|
|
558
867
|
// `recipe_step_done` events so the dashboard `/runs/[seq]` page can
|
|
@@ -636,21 +945,45 @@ export async function runChainedRecipe(recipe, options, deps, existingRegistry,
|
|
|
636
945
|
}
|
|
637
946
|
: options.onStepComplete;
|
|
638
947
|
// Execute with dependency tracking
|
|
948
|
+
// Run-level cancellation. The top-level run (depth 0) registers its own
|
|
949
|
+
// AbortController keyed by the RunLog seq so `POST /runs/:seq/cancel` can
|
|
950
|
+
// abort it; nested recipes inherit the parent's signal. Combine both so
|
|
951
|
+
// either source cancels. (run-cancel)
|
|
952
|
+
const runController = depth === 0 && runSeq !== undefined ? registerRun(runSeq) : undefined;
|
|
953
|
+
const cancelSignals = [options.signal, runController?.signal].filter((s) => !!s);
|
|
954
|
+
const effectiveSignal = cancelSignals.length === 0
|
|
955
|
+
? undefined
|
|
956
|
+
: cancelSignals.length === 1
|
|
957
|
+
? cancelSignals[0]
|
|
958
|
+
: AbortSignal.any(cancelSignals);
|
|
959
|
+
// Expose the combined signal on the options that flow into executeChainedStep
|
|
960
|
+
// so an in-flight tool/agent step (via raceStepTimeout) also aborts on cancel,
|
|
961
|
+
// not just not-yet-started steps. Safe no-op when undefined. (run-cancel)
|
|
962
|
+
options.signal = effectiveSignal;
|
|
639
963
|
const execOptions = {
|
|
640
964
|
maxConcurrency: options.maxConcurrency,
|
|
641
965
|
onStepStart: wrappedOnStepStart,
|
|
642
966
|
onStepComplete: wrappedOnStepComplete,
|
|
967
|
+
signal: effectiveSignal,
|
|
643
968
|
};
|
|
644
969
|
const stepExecutor = async (stepId) => {
|
|
645
970
|
const step = stepMap.get(stepId);
|
|
646
971
|
if (!step)
|
|
647
972
|
throw new Error(`Step ${stepId} not found`);
|
|
973
|
+
// Honour the refreshPrices() contract for long-running recipes: pick up a
|
|
974
|
+
// mid-run ~/.patchwork/prices.json update for budget enforcement. No-op
|
|
975
|
+
// unless a usdMax cap is set; gated on ownsBudget so an injected
|
|
976
|
+
// (unit-test) budget keeps its stable prices.
|
|
977
|
+
if (ownsBudget)
|
|
978
|
+
budget.refreshPrices();
|
|
648
979
|
const ctx = {
|
|
649
980
|
registry,
|
|
650
981
|
step,
|
|
651
982
|
options,
|
|
652
983
|
recipe,
|
|
653
984
|
depth,
|
|
985
|
+
budget,
|
|
986
|
+
priceTable,
|
|
654
987
|
};
|
|
655
988
|
const retryCount = step.retry ?? recipe.on_error?.retry ?? 0;
|
|
656
989
|
const retryDelay = step.retryDelay ?? recipe.on_error?.retryDelay ?? 1000;
|
|
@@ -660,6 +993,15 @@ export async function runChainedRecipe(recipe, options, deps, existingRegistry,
|
|
|
660
993
|
durationMs: Date.now() - stepStart,
|
|
661
994
|
skipped: result.skipped,
|
|
662
995
|
});
|
|
996
|
+
// P1: stash this step's agent token usage (depth-0 persistence reads it).
|
|
997
|
+
if (depth === 0 && result.usage) {
|
|
998
|
+
capturedUsage.set(stepId, result.usage);
|
|
999
|
+
}
|
|
1000
|
+
// Stash on_fail:"warn" expect failures so the persisted step row + the
|
|
1001
|
+
// dashboard surface them (previously silently dropped in chained runs).
|
|
1002
|
+
if (depth === 0 && result.expectWarnings?.length) {
|
|
1003
|
+
capturedExpectWarnings.set(stepId, result.expectWarnings);
|
|
1004
|
+
}
|
|
663
1005
|
// Recipe-level on_error.fallback: "log_only" and "deliver_original" both
|
|
664
1006
|
// treat step failures as non-fatal (fail-open) — same semantics as
|
|
665
1007
|
// step-level optional: true. "abort" is the default (propagate failure).
|
|
@@ -711,7 +1053,17 @@ export async function runChainedRecipe(recipe, options, deps, existingRegistry,
|
|
|
711
1053
|
throw new Error(result.error ?? `Step ${stepId} failed`);
|
|
712
1054
|
}
|
|
713
1055
|
};
|
|
714
|
-
|
|
1056
|
+
let stepResults;
|
|
1057
|
+
try {
|
|
1058
|
+
stepResults = await executeWithDependencies(depGraph, stepExecutor, execOptions);
|
|
1059
|
+
}
|
|
1060
|
+
finally {
|
|
1061
|
+
// Drop the run from the registry as soon as execution finishes (success,
|
|
1062
|
+
// failure, or cancel) so the seq can't be cancelled post-hoc and the map
|
|
1063
|
+
// doesn't leak. Only the top-level run registered one. (run-cancel)
|
|
1064
|
+
if (runController && runSeq !== undefined)
|
|
1065
|
+
unregisterRun(runSeq);
|
|
1066
|
+
}
|
|
715
1067
|
// Merge timings into step results
|
|
716
1068
|
const enrichedResults = new Map();
|
|
717
1069
|
let failed = 0;
|
|
@@ -756,6 +1108,9 @@ export async function runChainedRecipe(recipe, options, deps, existingRegistry,
|
|
|
756
1108
|
try {
|
|
757
1109
|
const doneAt = Date.now();
|
|
758
1110
|
const stepResultsList = [];
|
|
1111
|
+
// P1: run-level token aggregate, summed from per-step agent usage.
|
|
1112
|
+
const runTok = { inputTokens: 0, outputTokens: 0, measured: false };
|
|
1113
|
+
let runCostUsd;
|
|
759
1114
|
for (const [id, r] of enrichedResults) {
|
|
760
1115
|
const step = stepMap.get(id);
|
|
761
1116
|
const captured = capturedStepData.get(id);
|
|
@@ -771,6 +1126,16 @@ export async function runChainedRecipe(recipe, options, deps, existingRegistry,
|
|
|
771
1126
|
status,
|
|
772
1127
|
error: errorMsg,
|
|
773
1128
|
});
|
|
1129
|
+
const expectWarnings = capturedExpectWarnings.get(id);
|
|
1130
|
+
const usage = capturedUsage.get(id);
|
|
1131
|
+
if (usage) {
|
|
1132
|
+
runTok.measured = true;
|
|
1133
|
+
runTok.inputTokens += usage.inputTokens;
|
|
1134
|
+
runTok.outputTokens += usage.outputTokens;
|
|
1135
|
+
if (typeof usage.costUsd === "number") {
|
|
1136
|
+
runCostUsd = (runCostUsd ?? 0) + usage.costUsd;
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
774
1139
|
stepResultsList.push({
|
|
775
1140
|
id,
|
|
776
1141
|
tool: step?.tool,
|
|
@@ -788,12 +1153,36 @@ export async function runChainedRecipe(recipe, options, deps, existingRegistry,
|
|
|
788
1153
|
...(captured?.startedAt !== undefined && {
|
|
789
1154
|
startedAt: captured.startedAt,
|
|
790
1155
|
}),
|
|
1156
|
+
// P1: per-step token usage (absent for tool / unmeasured steps).
|
|
1157
|
+
...(usage
|
|
1158
|
+
? {
|
|
1159
|
+
inputTokens: usage.inputTokens,
|
|
1160
|
+
outputTokens: usage.outputTokens,
|
|
1161
|
+
...(typeof usage.costUsd === "number"
|
|
1162
|
+
? { costUsd: usage.costUsd }
|
|
1163
|
+
: {}),
|
|
1164
|
+
}
|
|
1165
|
+
: {}),
|
|
1166
|
+
...(expectWarnings?.length ? { expectWarnings } : {}),
|
|
791
1167
|
});
|
|
792
1168
|
}
|
|
1169
|
+
// P1: assemble run-level totals (only when any step reported usage) +
|
|
1170
|
+
// budget totals (only when a budget was configured).
|
|
1171
|
+
const tokenTotals = runTok.measured
|
|
1172
|
+
? {
|
|
1173
|
+
inputTokens: runTok.inputTokens,
|
|
1174
|
+
outputTokens: runTok.outputTokens,
|
|
1175
|
+
...(typeof runCostUsd === "number" ? { costUsd: runCostUsd } : {}),
|
|
1176
|
+
}
|
|
1177
|
+
: undefined;
|
|
1178
|
+
const budgetTotals = recipe.budget ? budget.totals() : undefined;
|
|
793
1179
|
const outputTail = stepResultsList
|
|
794
1180
|
.map((s) => `[${s.status}] ${s.tool ?? s.id}${s.error ? `: ${s.error}` : ""}`)
|
|
795
1181
|
.join("\n")
|
|
796
1182
|
.slice(0, 2000);
|
|
1183
|
+
// Budget warnings/spend — record like the flat path so chained runs
|
|
1184
|
+
// surface spend + fail-open notices on the same dashboard surfaces.
|
|
1185
|
+
const budgetWarnings = budget.finalWarnings();
|
|
797
1186
|
if (options.runLog && runSeq !== undefined) {
|
|
798
1187
|
options.runLog.completeRun(runSeq, {
|
|
799
1188
|
status: result.success ? "done" : "error",
|
|
@@ -801,9 +1190,12 @@ export async function runChainedRecipe(recipe, options, deps, existingRegistry,
|
|
|
801
1190
|
durationMs: doneAt - runStartedAt,
|
|
802
1191
|
stepResults: stepResultsList,
|
|
803
1192
|
outputTail,
|
|
1193
|
+
...(budgetWarnings.length > 0 && { budgetWarnings }),
|
|
804
1194
|
...(result.errorMessage !== undefined && {
|
|
805
1195
|
errorMessage: result.errorMessage,
|
|
806
1196
|
}),
|
|
1197
|
+
...(tokenTotals ? { tokenTotals } : {}),
|
|
1198
|
+
...(budgetTotals ? { budgetTotals } : {}),
|
|
807
1199
|
});
|
|
808
1200
|
if (broadcastActivity && broadcastSeq !== undefined) {
|
|
809
1201
|
try {
|
|
@@ -839,6 +1231,9 @@ export async function runChainedRecipe(recipe, options, deps, existingRegistry,
|
|
|
839
1231
|
outputTail,
|
|
840
1232
|
errorMessage: result.errorMessage,
|
|
841
1233
|
stepResults: stepResultsList,
|
|
1234
|
+
...(budgetWarnings.length > 0 && { budgetWarnings }),
|
|
1235
|
+
...(tokenTotals ? { tokenTotals } : {}),
|
|
1236
|
+
...(budgetTotals ? { budgetTotals } : {}),
|
|
842
1237
|
});
|
|
843
1238
|
}
|
|
844
1239
|
}
|