patchwork-os 0.2.0-alpha.8 → 0.2.0-beta.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.bridge.md +6 -0
- package/README.md +315 -35
- package/deploy/bootstrap-new-vps.sh +12 -12
- package/deploy/bootstrap-vps.sh +187 -0
- package/deploy/deploy-dashboard.sh +174 -0
- package/deploy/deploy-landing.sh +136 -0
- package/dist/activationMetrics.d.ts +67 -0
- package/dist/activationMetrics.js +255 -0
- package/dist/activationMetrics.js.map +1 -0
- package/dist/activityLog.d.ts +49 -0
- package/dist/activityLog.js +78 -0
- package/dist/activityLog.js.map +1 -1
- package/dist/approvalHttp.d.ts +49 -2
- package/dist/approvalHttp.js +217 -21
- package/dist/approvalHttp.js.map +1 -1
- package/dist/approvalInsights.d.ts +49 -0
- package/dist/approvalInsights.js +97 -0
- package/dist/approvalInsights.js.map +1 -0
- package/dist/approvalQueue.d.ts +27 -1
- package/dist/approvalQueue.js +123 -3
- package/dist/approvalQueue.js.map +1 -1
- package/dist/approvalSignals.d.ts +124 -0
- package/dist/approvalSignals.js +512 -0
- package/dist/approvalSignals.js.map +1 -0
- package/dist/automation.d.ts +57 -0
- package/dist/automation.js +156 -59
- package/dist/automation.js.map +1 -1
- package/dist/automationSuggestions.d.ts +79 -0
- package/dist/automationSuggestions.js +150 -0
- package/dist/automationSuggestions.js.map +1 -0
- package/dist/bridge.d.ts +3 -0
- package/dist/bridge.js +174 -143
- package/dist/bridge.js.map +1 -1
- package/dist/bridgeToken.js +57 -19
- package/dist/bridgeToken.js.map +1 -1
- package/dist/ccPermissions.d.ts +15 -0
- package/dist/ccPermissions.js +21 -4
- package/dist/ccPermissions.js.map +1 -1
- package/dist/claudeDriver.js +74 -16
- package/dist/claudeDriver.js.map +1 -1
- package/dist/claudeOrchestrator.d.ts +1 -1
- package/dist/claudeOrchestrator.js +14 -8
- package/dist/claudeOrchestrator.js.map +1 -1
- package/dist/commands/dashboard.js +1 -1
- package/dist/commands/dashboard.js.map +1 -1
- package/dist/commands/launchd.d.ts +2 -0
- package/dist/commands/launchd.js +94 -0
- package/dist/commands/launchd.js.map +1 -0
- package/dist/commands/patchworkInit.d.ts +8 -0
- package/dist/commands/patchworkInit.js +77 -11
- package/dist/commands/patchworkInit.js.map +1 -1
- package/dist/commands/recipe.d.ts +289 -0
- package/dist/commands/recipe.js +1359 -0
- package/dist/commands/recipe.js.map +1 -0
- package/dist/commands/recipeInstall.d.ts +150 -0
- package/dist/commands/recipeInstall.js +647 -0
- package/dist/commands/recipeInstall.js.map +1 -0
- package/dist/commands/tracesExport.d.ts +83 -0
- package/dist/commands/tracesExport.js +269 -0
- package/dist/commands/tracesExport.js.map +1 -0
- package/dist/commands/tracesImport.d.ts +56 -0
- package/dist/commands/tracesImport.js +161 -0
- package/dist/commands/tracesImport.js.map +1 -0
- package/dist/config.d.ts +22 -1
- package/dist/config.js +108 -9
- package/dist/config.js.map +1 -1
- package/dist/connectorRoutes.d.ts +43 -0
- package/dist/connectorRoutes.js +1609 -0
- package/dist/connectorRoutes.js.map +1 -0
- package/dist/connectors/asana.d.ts +198 -0
- package/dist/connectors/asana.js +679 -0
- package/dist/connectors/asana.js.map +1 -0
- package/dist/connectors/baseConnector.d.ts +153 -0
- package/dist/connectors/baseConnector.js +336 -0
- package/dist/connectors/baseConnector.js.map +1 -0
- package/dist/connectors/confluence.d.ts +111 -0
- package/dist/connectors/confluence.js +406 -0
- package/dist/connectors/confluence.js.map +1 -0
- package/dist/connectors/datadog.d.ts +116 -0
- package/dist/connectors/datadog.js +385 -0
- package/dist/connectors/datadog.js.map +1 -0
- package/dist/connectors/discord.d.ts +150 -0
- package/dist/connectors/discord.js +543 -0
- package/dist/connectors/discord.js.map +1 -0
- package/dist/connectors/fixtureLibrary.d.ts +21 -0
- package/dist/connectors/fixtureLibrary.js +70 -0
- package/dist/connectors/fixtureLibrary.js.map +1 -0
- package/dist/connectors/fixtureRecorder.d.ts +1 -0
- package/dist/connectors/fixtureRecorder.js +35 -0
- package/dist/connectors/fixtureRecorder.js.map +1 -0
- package/dist/connectors/github.js +17 -18
- package/dist/connectors/github.js.map +1 -1
- package/dist/connectors/gitlab.d.ts +180 -0
- package/dist/connectors/gitlab.js +582 -0
- package/dist/connectors/gitlab.js.map +1 -0
- package/dist/connectors/gmail.d.ts +4 -1
- package/dist/connectors/gmail.js +157 -27
- package/dist/connectors/gmail.js.map +1 -1
- package/dist/connectors/googleCalendar.d.ts +4 -1
- package/dist/connectors/googleCalendar.js +88 -25
- package/dist/connectors/googleCalendar.js.map +1 -1
- package/dist/connectors/googleDrive.d.ts +34 -0
- package/dist/connectors/googleDrive.js +321 -0
- package/dist/connectors/googleDrive.js.map +1 -0
- package/dist/connectors/htmlEscape.d.ts +5 -0
- package/dist/connectors/htmlEscape.js +13 -0
- package/dist/connectors/htmlEscape.js.map +1 -0
- package/dist/connectors/hubspot.d.ts +112 -0
- package/dist/connectors/hubspot.js +408 -0
- package/dist/connectors/hubspot.js.map +1 -0
- package/dist/connectors/intercom.d.ts +102 -0
- package/dist/connectors/intercom.js +402 -0
- package/dist/connectors/intercom.js.map +1 -0
- package/dist/connectors/jira.d.ts +98 -0
- package/dist/connectors/jira.js +379 -0
- package/dist/connectors/jira.js.map +1 -0
- package/dist/connectors/linear.js +30 -19
- package/dist/connectors/linear.js.map +1 -1
- package/dist/connectors/mcpOAuth.d.ts +3 -0
- package/dist/connectors/mcpOAuth.js +64 -10
- package/dist/connectors/mcpOAuth.js.map +1 -1
- package/dist/connectors/mockConnector.d.ts +28 -0
- package/dist/connectors/mockConnector.js +81 -0
- package/dist/connectors/mockConnector.js.map +1 -0
- package/dist/connectors/notion.d.ts +143 -0
- package/dist/connectors/notion.js +424 -0
- package/dist/connectors/notion.js.map +1 -0
- package/dist/connectors/oauthStateStore.d.ts +31 -0
- package/dist/connectors/oauthStateStore.js +52 -0
- package/dist/connectors/oauthStateStore.js.map +1 -0
- package/dist/connectors/pagerduty.d.ts +160 -0
- package/dist/connectors/pagerduty.js +464 -0
- package/dist/connectors/pagerduty.js.map +1 -0
- package/dist/connectors/sentry.js +5 -13
- package/dist/connectors/sentry.js.map +1 -1
- package/dist/connectors/slack.d.ts +16 -1
- package/dist/connectors/slack.js +155 -32
- package/dist/connectors/slack.js.map +1 -1
- package/dist/connectors/stripe.d.ts +116 -0
- package/dist/connectors/stripe.js +379 -0
- package/dist/connectors/stripe.js.map +1 -0
- package/dist/connectors/tokenStorage.d.ts +35 -0
- package/dist/connectors/tokenStorage.js +484 -0
- package/dist/connectors/tokenStorage.js.map +1 -0
- package/dist/connectors/zendesk.d.ts +104 -0
- package/dist/connectors/zendesk.js +442 -0
- package/dist/connectors/zendesk.js.map +1 -0
- package/dist/cors.d.ts +10 -0
- package/dist/cors.js +29 -0
- package/dist/cors.js.map +1 -0
- package/dist/decisionReplay.d.ts +72 -0
- package/dist/decisionReplay.js +92 -0
- package/dist/decisionReplay.js.map +1 -0
- package/dist/decisionTraceLog.d.ts +6 -0
- package/dist/decisionTraceLog.js +54 -2
- package/dist/decisionTraceLog.js.map +1 -1
- package/dist/drivers/gemini/index.d.ts +5 -1
- package/dist/drivers/gemini/index.js +39 -5
- package/dist/drivers/gemini/index.js.map +1 -1
- package/dist/drivers/index.d.ts +5 -0
- package/dist/drivers/index.js +1 -1
- package/dist/drivers/index.js.map +1 -1
- package/dist/featureFlags.d.ts +79 -0
- package/dist/featureFlags.js +208 -0
- package/dist/featureFlags.js.map +1 -0
- package/dist/fp/automationInterpreter.js +26 -21
- package/dist/fp/automationInterpreter.js.map +1 -1
- package/dist/fp/automationProgram.d.ts +1 -1
- package/dist/fp/automationProgram.js.map +1 -1
- package/dist/fp/automationState.js +4 -1
- package/dist/fp/automationState.js.map +1 -1
- package/dist/fp/policyParser.js +21 -1
- package/dist/fp/policyParser.js.map +1 -1
- package/dist/inboxRoutes.d.ts +22 -0
- package/dist/inboxRoutes.js +114 -0
- package/dist/inboxRoutes.js.map +1 -0
- package/dist/index.js +1400 -201
- package/dist/index.js.map +1 -1
- package/dist/installGuard.d.ts +25 -0
- package/dist/installGuard.js +48 -0
- package/dist/installGuard.js.map +1 -0
- package/dist/mcpRoutes.d.ts +37 -0
- package/dist/mcpRoutes.js +76 -0
- package/dist/mcpRoutes.js.map +1 -0
- package/dist/oauth.d.ts +7 -1
- package/dist/oauth.js +201 -39
- package/dist/oauth.js.map +1 -1
- package/dist/oauthRoutes.d.ts +32 -0
- package/dist/oauthRoutes.js +124 -0
- package/dist/oauthRoutes.js.map +1 -0
- package/dist/orchestrator/orchestratorBridge.js +2 -2
- package/dist/orchestrator/orchestratorBridge.js.map +1 -1
- package/dist/patchworkConfig.d.ts +16 -0
- package/dist/patchworkConfig.js +1 -1
- package/dist/patchworkConfig.js.map +1 -1
- package/dist/pluginLoader.d.ts +28 -0
- package/dist/pluginLoader.js +77 -11
- package/dist/pluginLoader.js.map +1 -1
- package/dist/pluginWatcher.js +8 -3
- package/dist/pluginWatcher.js.map +1 -1
- package/dist/preToolUseHook.d.ts +12 -0
- package/dist/preToolUseHook.js +23 -0
- package/dist/preToolUseHook.js.map +1 -1
- package/dist/recipeOrchestration.d.ts +121 -0
- package/dist/recipeOrchestration.js +955 -0
- package/dist/recipeOrchestration.js.map +1 -0
- package/dist/recipeRoutes.d.ts +180 -0
- package/dist/recipeRoutes.js +1345 -0
- package/dist/recipeRoutes.js.map +1 -0
- package/dist/recipes/RecipeOrchestrator.d.ts +40 -0
- package/dist/recipes/RecipeOrchestrator.js +51 -0
- package/dist/recipes/RecipeOrchestrator.js.map +1 -0
- package/dist/recipes/agentExecutor.d.ts +29 -0
- package/dist/recipes/agentExecutor.js +49 -0
- package/dist/recipes/agentExecutor.js.map +1 -0
- package/dist/recipes/chainedRunner.d.ts +191 -0
- package/dist/recipes/chainedRunner.js +759 -0
- package/dist/recipes/chainedRunner.js.map +1 -0
- package/dist/recipes/compiler.js +3 -3
- package/dist/recipes/compiler.js.map +1 -1
- package/dist/recipes/dependencyGraph.d.ts +39 -0
- package/dist/recipes/dependencyGraph.js +199 -0
- package/dist/recipes/dependencyGraph.js.map +1 -0
- package/dist/recipes/disabledMarkers.d.ts +48 -0
- package/dist/recipes/disabledMarkers.js +52 -0
- package/dist/recipes/disabledMarkers.js.map +1 -0
- package/dist/recipes/installer.js +3 -3
- package/dist/recipes/installer.js.map +1 -1
- package/dist/recipes/legacyRecipeCompat.d.ts +10 -0
- package/dist/recipes/legacyRecipeCompat.js +131 -0
- package/dist/recipes/legacyRecipeCompat.js.map +1 -0
- package/dist/recipes/manifest.d.ts +47 -0
- package/dist/recipes/manifest.js +156 -0
- package/dist/recipes/manifest.js.map +1 -0
- package/dist/recipes/migrationWarnings.d.ts +12 -0
- package/dist/recipes/migrationWarnings.js +44 -0
- package/dist/recipes/migrationWarnings.js.map +1 -0
- package/dist/recipes/migrations/index.d.ts +24 -0
- package/dist/recipes/migrations/index.js +55 -0
- package/dist/recipes/migrations/index.js.map +1 -0
- package/dist/recipes/migrations/types.d.ts +28 -0
- package/dist/recipes/migrations/types.js +2 -0
- package/dist/recipes/migrations/types.js.map +1 -0
- package/dist/recipes/migrations/v1.d.ts +11 -0
- package/dist/recipes/migrations/v1.js +18 -0
- package/dist/recipes/migrations/v1.js.map +1 -0
- package/dist/recipes/names.d.ts +40 -0
- package/dist/recipes/names.js +66 -0
- package/dist/recipes/names.js.map +1 -0
- package/dist/recipes/nestedRecipeStep.d.ts +58 -0
- package/dist/recipes/nestedRecipeStep.js +95 -0
- package/dist/recipes/nestedRecipeStep.js.map +1 -0
- package/dist/recipes/outputRegistry.d.ts +28 -0
- package/dist/recipes/outputRegistry.js +52 -0
- package/dist/recipes/outputRegistry.js.map +1 -0
- package/dist/recipes/parser.js +4 -1
- package/dist/recipes/parser.js.map +1 -1
- package/dist/recipes/replayRun.d.ts +62 -0
- package/dist/recipes/replayRun.js +97 -0
- package/dist/recipes/replayRun.js.map +1 -0
- package/dist/recipes/resolveRecipePath.d.ts +69 -0
- package/dist/recipes/resolveRecipePath.js +202 -0
- package/dist/recipes/resolveRecipePath.js.map +1 -0
- package/dist/recipes/scheduler.d.ts +23 -7
- package/dist/recipes/scheduler.js +225 -45
- package/dist/recipes/scheduler.js.map +1 -1
- package/dist/recipes/schema.d.ts +17 -2
- package/dist/recipes/schemaGenerator.d.ts +28 -0
- package/dist/recipes/schemaGenerator.js +565 -0
- package/dist/recipes/schemaGenerator.js.map +1 -0
- package/dist/recipes/stepObservation.d.ts +44 -0
- package/dist/recipes/stepObservation.js +232 -0
- package/dist/recipes/stepObservation.js.map +1 -0
- package/dist/recipes/templateEngine.d.ts +62 -0
- package/dist/recipes/templateEngine.js +201 -0
- package/dist/recipes/templateEngine.js.map +1 -0
- package/dist/recipes/toolRegistry.d.ts +186 -0
- package/dist/recipes/toolRegistry.js +309 -0
- package/dist/recipes/toolRegistry.js.map +1 -0
- package/dist/recipes/tools/asana.d.ts +16 -0
- package/dist/recipes/tools/asana.js +524 -0
- package/dist/recipes/tools/asana.js.map +1 -0
- package/dist/recipes/tools/calendar.d.ts +6 -0
- package/dist/recipes/tools/calendar.js +61 -0
- package/dist/recipes/tools/calendar.js.map +1 -0
- package/dist/recipes/tools/confluence.d.ts +6 -0
- package/dist/recipes/tools/confluence.js +254 -0
- package/dist/recipes/tools/confluence.js.map +1 -0
- package/dist/recipes/tools/datadog.d.ts +6 -0
- package/dist/recipes/tools/datadog.js +239 -0
- package/dist/recipes/tools/datadog.js.map +1 -0
- package/dist/recipes/tools/diagnostics.d.ts +6 -0
- package/dist/recipes/tools/diagnostics.js +36 -0
- package/dist/recipes/tools/diagnostics.js.map +1 -0
- package/dist/recipes/tools/discord.d.ts +18 -0
- package/dist/recipes/tools/discord.js +254 -0
- package/dist/recipes/tools/discord.js.map +1 -0
- package/dist/recipes/tools/file.d.ts +12 -0
- package/dist/recipes/tools/file.js +174 -0
- package/dist/recipes/tools/file.js.map +1 -0
- package/dist/recipes/tools/git.d.ts +6 -0
- package/dist/recipes/tools/git.js +63 -0
- package/dist/recipes/tools/git.js.map +1 -0
- package/dist/recipes/tools/github.d.ts +6 -0
- package/dist/recipes/tools/github.js +116 -0
- package/dist/recipes/tools/github.js.map +1 -0
- package/dist/recipes/tools/gitlab.d.ts +11 -0
- package/dist/recipes/tools/gitlab.js +285 -0
- package/dist/recipes/tools/gitlab.js.map +1 -0
- package/dist/recipes/tools/gmail.d.ts +6 -0
- package/dist/recipes/tools/gmail.js +434 -0
- package/dist/recipes/tools/gmail.js.map +1 -0
- package/dist/recipes/tools/googleDrive.d.ts +1 -0
- package/dist/recipes/tools/googleDrive.js +55 -0
- package/dist/recipes/tools/googleDrive.js.map +1 -0
- package/dist/recipes/tools/hubspot.d.ts +6 -0
- package/dist/recipes/tools/hubspot.js +232 -0
- package/dist/recipes/tools/hubspot.js.map +1 -0
- package/dist/recipes/tools/index.d.ts +30 -0
- package/dist/recipes/tools/index.js +33 -0
- package/dist/recipes/tools/index.js.map +1 -0
- package/dist/recipes/tools/intercom.d.ts +6 -0
- package/dist/recipes/tools/intercom.js +226 -0
- package/dist/recipes/tools/intercom.js.map +1 -0
- package/dist/recipes/tools/jira.d.ts +14 -0
- package/dist/recipes/tools/jira.js +369 -0
- package/dist/recipes/tools/jira.js.map +1 -0
- package/dist/recipes/tools/linear.d.ts +7 -0
- package/dist/recipes/tools/linear.js +307 -0
- package/dist/recipes/tools/linear.js.map +1 -0
- package/dist/recipes/tools/meetingNotes.d.ts +21 -0
- package/dist/recipes/tools/meetingNotes.js +701 -0
- package/dist/recipes/tools/meetingNotes.js.map +1 -0
- package/dist/recipes/tools/notion.d.ts +6 -0
- package/dist/recipes/tools/notion.js +278 -0
- package/dist/recipes/tools/notion.js.map +1 -0
- package/dist/recipes/tools/pagerduty.d.ts +15 -0
- package/dist/recipes/tools/pagerduty.js +451 -0
- package/dist/recipes/tools/pagerduty.js.map +1 -0
- package/dist/recipes/tools/sentry.d.ts +12 -0
- package/dist/recipes/tools/sentry.js +73 -0
- package/dist/recipes/tools/sentry.js.map +1 -0
- package/dist/recipes/tools/slack.d.ts +6 -0
- package/dist/recipes/tools/slack.js +82 -0
- package/dist/recipes/tools/slack.js.map +1 -0
- package/dist/recipes/tools/stripe.d.ts +6 -0
- package/dist/recipes/tools/stripe.js +265 -0
- package/dist/recipes/tools/stripe.js.map +1 -0
- package/dist/recipes/tools/zendesk.d.ts +6 -0
- package/dist/recipes/tools/zendesk.js +245 -0
- package/dist/recipes/tools/zendesk.js.map +1 -0
- package/dist/recipes/validation.d.ts +13 -0
- package/dist/recipes/validation.js +617 -0
- package/dist/recipes/validation.js.map +1 -0
- package/dist/recipes/yamlRunner.d.ts +116 -1
- package/dist/recipes/yamlRunner.js +1000 -401
- package/dist/recipes/yamlRunner.js.map +1 -1
- package/dist/recipesHttp.d.ts +137 -6
- package/dist/recipesHttp.js +941 -29
- package/dist/recipesHttp.js.map +1 -1
- package/dist/riskTier.js +7 -1
- package/dist/riskTier.js.map +1 -1
- package/dist/runLog.d.ts +100 -1
- package/dist/runLog.js +258 -5
- package/dist/runLog.js.map +1 -1
- package/dist/schemas/dry-run-plan.v1.json +139 -0
- package/dist/schemas/recipe.v1.json +684 -0
- package/dist/server.d.ts +121 -8
- package/dist/server.js +538 -735
- package/dist/server.js.map +1 -1
- package/dist/ssrfGuard.d.ts +54 -0
- package/dist/ssrfGuard.js +122 -0
- package/dist/ssrfGuard.js.map +1 -0
- package/dist/streamableHttp.d.ts +39 -1
- package/dist/streamableHttp.js +128 -17
- package/dist/streamableHttp.js.map +1 -1
- package/dist/tokenUsageTracker.d.ts +33 -0
- package/dist/tokenUsageTracker.js +146 -0
- package/dist/tokenUsageTracker.js.map +1 -0
- package/dist/tools/activityLog.d.ts +2 -0
- package/dist/tools/addLinearComment.d.ts +1 -0
- package/dist/tools/addLinearComment.js +4 -2
- package/dist/tools/addLinearComment.js.map +1 -1
- package/dist/tools/batchLsp.d.ts +3 -0
- package/dist/tools/bridgeDoctor.d.ts +1 -0
- package/dist/tools/bridgeDoctor.js +2 -2
- package/dist/tools/bridgeDoctor.js.map +1 -1
- package/dist/tools/bridgeStatus.d.ts +1 -0
- package/dist/tools/cancelClaudeTask.d.ts +2 -0
- package/dist/tools/cancelClaudeTask.js +1 -0
- package/dist/tools/cancelClaudeTask.js.map +1 -1
- package/dist/tools/checkDocumentDirty.d.ts +1 -0
- package/dist/tools/clipboard.d.ts +2 -0
- package/dist/tools/closeTabs.d.ts +2 -0
- package/dist/tools/codeLens.d.ts +1 -0
- package/dist/tools/contextBundle.d.ts +1 -0
- package/dist/tools/createIssueFromAIComment.d.ts +1 -0
- package/dist/tools/createLinearIssue.d.ts +1 -0
- package/dist/tools/ctxGetTaskContext.d.ts +1 -0
- package/dist/tools/ctxQueryTraces.d.ts +1 -0
- package/dist/tools/ctxSaveTrace.d.ts +1 -0
- package/dist/tools/debug.d.ts +4 -0
- package/dist/tools/decorations.d.ts +2 -0
- package/dist/tools/documentLinks.d.ts +1 -0
- package/dist/tools/editText.d.ts +1 -0
- package/dist/tools/enrichCommit.d.ts +1 -0
- package/dist/tools/enrichStackTrace.d.ts +1 -0
- package/dist/tools/explainDiagnostic.d.ts +1 -0
- package/dist/tools/explainSymbol.d.ts +1 -0
- package/dist/tools/fetchCalendarEvents.d.ts +1 -0
- package/dist/tools/fetchGithubIssue.d.ts +1 -0
- package/dist/tools/fetchGithubPR.d.ts +1 -0
- package/dist/tools/fetchLinearIssue.d.ts +1 -0
- package/dist/tools/fetchSentryIssue.d.ts +1 -0
- package/dist/tools/fetchSlackProfile.d.ts +1 -0
- package/dist/tools/fetchSlackProfile.js +4 -1
- package/dist/tools/fetchSlackProfile.js.map +1 -1
- package/dist/tools/fileOperations.d.ts +3 -0
- package/dist/tools/fileWatcher.d.ts +2 -0
- package/dist/tools/findFiles.d.ts +1 -0
- package/dist/tools/findRelatedTests.d.ts +1 -0
- package/dist/tools/fixAllLintErrors.d.ts +1 -0
- package/dist/tools/foldingRanges.d.ts +1 -0
- package/dist/tools/formatDocument.d.ts +1 -0
- package/dist/tools/generateTests.d.ts +1 -0
- package/dist/tools/getAIComments.d.ts +1 -0
- package/dist/tools/getAnalyticsReport.d.ts +1 -0
- package/dist/tools/getArchitectureContext.d.ts +1 -0
- package/dist/tools/getBufferContent.d.ts +1 -0
- package/dist/tools/getChangeImpact.d.ts +1 -0
- package/dist/tools/getClaudeTaskStatus.d.ts +2 -0
- package/dist/tools/getClaudeTaskStatus.js +1 -0
- package/dist/tools/getClaudeTaskStatus.js.map +1 -1
- package/dist/tools/getCodeCoverage.d.ts +1 -0
- package/dist/tools/getCommitsForIssue.d.ts +1 -0
- package/dist/tools/getConnectorStatus.d.ts +1 -0
- package/dist/tools/getCurrentSelection.d.ts +2 -0
- package/dist/tools/getDebugState.d.ts +1 -0
- package/dist/tools/getDependencyTree.d.ts +1 -0
- package/dist/tools/getDiagnostics.d.ts +1 -0
- package/dist/tools/getDiffFromHandoff.d.ts +1 -0
- package/dist/tools/getDocumentSymbols.d.ts +25 -0
- package/dist/tools/getDocumentSymbols.js +74 -8
- package/dist/tools/getDocumentSymbols.js.map +1 -1
- package/dist/tools/getFileTree.d.ts +1 -0
- package/dist/tools/getGitDiff.d.ts +1 -0
- package/dist/tools/getGitHotspots.d.ts +1 -0
- package/dist/tools/getGitLog.d.ts +1 -0
- package/dist/tools/getGitStatus.d.ts +1 -0
- package/dist/tools/getImportTree.d.ts +1 -0
- package/dist/tools/getImportedSignatures.d.ts +1 -0
- package/dist/tools/getOpenEditors.d.ts +1 -0
- package/dist/tools/getPRTemplate.d.ts +1 -0
- package/dist/tools/getProjectContext.d.ts +1 -0
- package/dist/tools/getProjectInfo.d.ts +1 -0
- package/dist/tools/getSecurityAdvisories.d.ts +1 -0
- package/dist/tools/getSecurityAdvisories.js +10 -1
- package/dist/tools/getSecurityAdvisories.js.map +1 -1
- package/dist/tools/getSessionUsage.d.ts +4 -0
- package/dist/tools/getSessionUsage.js +3 -0
- package/dist/tools/getSessionUsage.js.map +1 -1
- package/dist/tools/getSymbolHistory.d.ts +1 -0
- package/dist/tools/getToolCapabilities.d.ts +1 -0
- package/dist/tools/getTypeSignature.d.ts +1 -0
- package/dist/tools/getWorkspaceFolders.d.ts +1 -0
- package/dist/tools/getWorkspaceSettings.d.ts +1 -0
- package/dist/tools/gitHistory.d.ts +2 -0
- package/dist/tools/gitWrite.d.ts +11 -0
- package/dist/tools/github/actions.d.ts +2 -0
- package/dist/tools/github/actions.js +4 -2
- package/dist/tools/github/actions.js.map +1 -1
- package/dist/tools/github/composite.d.ts +342 -0
- package/dist/tools/github/composite.js +343 -0
- package/dist/tools/github/composite.js.map +1 -0
- package/dist/tools/github/index.d.ts +1 -0
- package/dist/tools/github/index.js +1 -0
- package/dist/tools/github/index.js.map +1 -1
- package/dist/tools/github/issues.d.ts +4 -0
- package/dist/tools/github/issues.js +8 -4
- package/dist/tools/github/issues.js.map +1 -1
- package/dist/tools/github/pr.d.ts +7 -0
- package/dist/tools/github/pr.js +50 -12
- package/dist/tools/github/pr.js.map +1 -1
- package/dist/tools/handoffNote.d.ts +4 -0
- package/dist/tools/handoffNote.js +2 -0
- package/dist/tools/handoffNote.js.map +1 -1
- package/dist/tools/hoverAtCursor.d.ts +1 -0
- package/dist/tools/httpClient.d.ts +2 -0
- package/dist/tools/index.d.ts +8 -0
- package/dist/tools/index.js +47 -8
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/inlayHints.d.ts +1 -0
- package/dist/tools/launchQuickTask.d.ts +2 -0
- package/dist/tools/launchQuickTask.js +1 -0
- package/dist/tools/launchQuickTask.js.map +1 -1
- package/dist/tools/listClaudeTasks.d.ts +2 -0
- package/dist/tools/listClaudeTasks.js +1 -0
- package/dist/tools/listClaudeTasks.js.map +1 -1
- package/dist/tools/listTerminals.d.ts +1 -0
- package/dist/tools/lsp.d.ts +14 -0
- package/dist/tools/navigateToSymbolByName.d.ts +1 -0
- package/dist/tools/openDiff.d.ts +1 -0
- package/dist/tools/openFile.d.ts +1 -0
- package/dist/tools/openInBrowser.d.ts +1 -0
- package/dist/tools/organizeImports.d.ts +1 -0
- package/dist/tools/performanceReport.d.ts +1 -0
- package/dist/tools/planPersistence.d.ts +5 -0
- package/dist/tools/previewEdit.d.ts +1 -0
- package/dist/tools/refactorAnalyze.d.ts +1 -0
- package/dist/tools/refactorPreview.d.ts +2 -0
- package/dist/tools/refactorPreview.js +1 -0
- package/dist/tools/refactorPreview.js.map +1 -1
- package/dist/tools/replaceBlock.d.ts +1 -0
- package/dist/tools/resumeClaudeTask.d.ts +2 -0
- package/dist/tools/resumeClaudeTask.js +1 -0
- package/dist/tools/resumeClaudeTask.js.map +1 -1
- package/dist/tools/runClaudeTask.d.ts +2 -0
- package/dist/tools/runClaudeTask.js +1 -0
- package/dist/tools/runClaudeTask.js.map +1 -1
- package/dist/tools/runCommand.d.ts +1 -0
- package/dist/tools/runTests.d.ts +1 -0
- package/dist/tools/saveDocument.d.ts +1 -0
- package/dist/tools/screenshotAndAnnotate.d.ts +1 -0
- package/dist/tools/searchAndReplace.d.ts +1 -0
- package/dist/tools/searchTools.d.ts +1 -0
- package/dist/tools/searchTools.js +1 -1
- package/dist/tools/searchTools.js.map +1 -1
- package/dist/tools/searchWorkspace.d.ts +1 -0
- package/dist/tools/selectionRanges.d.ts +1 -0
- package/dist/tools/semanticTokens.d.ts +1 -0
- package/dist/tools/setActiveWorkspaceFolder.d.ts +1 -0
- package/dist/tools/signatureHelp.d.ts +1 -0
- package/dist/tools/slackListChannels.d.ts +1 -0
- package/dist/tools/slackListChannels.js.map +1 -1
- package/dist/tools/slackPostMessage.d.ts +1 -0
- package/dist/tools/slackPostMessage.js +11 -6
- package/dist/tools/slackPostMessage.js.map +1 -1
- package/dist/tools/terminal.d.ts +6 -0
- package/dist/tools/testTraceToSource.d.ts +1 -0
- package/dist/tools/testTraceToSource.js +2 -2
- package/dist/tools/testTraceToSource.js.map +1 -1
- package/dist/tools/transaction.d.ts +23 -0
- package/dist/tools/transaction.js +29 -0
- package/dist/tools/transaction.js.map +1 -1
- package/dist/tools/typeHierarchy.d.ts +1 -0
- package/dist/tools/updateLinearIssue.d.ts +1 -0
- package/dist/tools/updateLinearIssue.js +20 -6
- package/dist/tools/updateLinearIssue.js.map +1 -1
- package/dist/tools/utils.d.ts +2 -0
- package/dist/tools/utils.js.map +1 -1
- package/dist/tools/vscodeCommands.d.ts +2 -0
- package/dist/tools/vscodeTasks.d.ts +2 -0
- package/dist/tools/workspaceSettings.d.ts +1 -0
- package/dist/traceEncryption.d.ts +46 -0
- package/dist/traceEncryption.js +124 -0
- package/dist/traceEncryption.js.map +1 -0
- package/dist/transport.d.ts +46 -1
- package/dist/transport.js +173 -19
- package/dist/transport.js.map +1 -1
- package/package.json +30 -8
- package/scripts/mcp-stdio-shim.cjs +19 -3
- package/scripts/start-all.sh +30 -1
- package/templates/automation-policies/recipe-authoring.json +25 -0
- package/templates/automation-policy.example.json +6 -0
- package/templates/co.patchwork-os.bridge.plist +34 -0
- package/templates/policies/README.md +72 -0
- package/templates/policies/conservative.json +14 -0
- package/templates/policies/developer.json +14 -0
- package/templates/policies/headless-ci.json +24 -0
- package/templates/policies/personal-assistant.json +15 -0
- package/templates/policies/regulated-industry.json +18 -0
- package/templates/recipes/lint-on-save.yaml +1 -2
- package/templates/recipes/morning-brief-slack.yaml +57 -0
- package/templates/recipes/morning-brief.yaml +2 -2
- package/templates/recipes/project-health-check.yaml +50 -0
- package/templates/recipes/webhook/README.md +70 -0
- package/templates/recipes/webhook/capture-thought.yaml +26 -0
- package/templates/recipes/webhook/customer-escalation.yaml +49 -0
- package/templates/recipes/webhook/incident-intake.yaml +46 -0
- package/templates/recipes/webhook/meeting-prep.yaml +48 -0
- package/templates/recipes/webhook/morning-brief.yaml +57 -0
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* automationSuggestions — pattern-mine the activity log + run history to
|
|
3
|
+
* surface "you've been doing X by hand; want to make a recipe?" hints.
|
|
4
|
+
*
|
|
5
|
+
* From docs/strategic/2026-05-02/memory-ecosystem-report.md §6. Three of
|
|
6
|
+
* the four query-catalog patterns ship here; the fourth ("repeated
|
|
7
|
+
* manual workflow" via PrefixSpan-lite sequence mining) is deferred to
|
|
8
|
+
* a follow-up — the agent flagged it as the only entry in the catalog
|
|
9
|
+
* that requires a new ~50-line primitive (`mineSequences`), and shipping
|
|
10
|
+
* it without dashboard rendering would be premature.
|
|
11
|
+
*
|
|
12
|
+
* 1. **Co-occurring pairs** — tools that often run within W minutes of
|
|
13
|
+
* each other but don't already appear together in any successful
|
|
14
|
+
* recipe → "create a recipe?" suggestion.
|
|
15
|
+
* 2. **Installed but unused** — tools registered with the bridge but
|
|
16
|
+
* never called → "review your installed tool list" suggestion.
|
|
17
|
+
* 3. **Recipe trust graduation** — recipes with ≥ 10 runs all status
|
|
18
|
+
* "done" → "consider auto-approving" suggestion (does NOT auto-
|
|
19
|
+
* change policy; this is purely a hint).
|
|
20
|
+
*
|
|
21
|
+
* Pure function over (ActivityLog, RecipeRunLog, ToolRegistry). No I/O
|
|
22
|
+
* of its own; tested in isolation by feeding mock instances. The CLI
|
|
23
|
+
* `patchwork suggest` is a thin printer over this output.
|
|
24
|
+
*/
|
|
25
|
+
import { computeCoOccurrence } from "./fp/activityAnalytics.js";
|
|
26
|
+
import { listTools } from "./recipes/toolRegistry.js";
|
|
27
|
+
const DEFAULT_CO_OCCURRENCE_WINDOW_MS = 5 * 60 * 1000;
|
|
28
|
+
const DEFAULT_CO_OCCURRENCE_MIN_COUNT = 5;
|
|
29
|
+
const DEFAULT_ACTIVITY_SINCE_MS = 7 * 24 * 60 * 60 * 1000;
|
|
30
|
+
const DEFAULT_TRUST_GRADUATION_MIN_RUNS = 10;
|
|
31
|
+
/**
|
|
32
|
+
* Compute the full suggestion set. Returns at most 30 suggestions
|
|
33
|
+
* (10 of each kind) sorted by salience within each bucket.
|
|
34
|
+
*/
|
|
35
|
+
export function computeAutomationSuggestions(deps) {
|
|
36
|
+
const window = deps.coOccurrenceWindowMs ?? DEFAULT_CO_OCCURRENCE_WINDOW_MS;
|
|
37
|
+
const minCount = deps.coOccurrenceMinCount ?? DEFAULT_CO_OCCURRENCE_MIN_COUNT;
|
|
38
|
+
const sinceLookback = deps.activitySinceMs ?? DEFAULT_ACTIVITY_SINCE_MS;
|
|
39
|
+
const minRuns = deps.trustGraduationMinRuns ?? DEFAULT_TRUST_GRADUATION_MIN_RUNS;
|
|
40
|
+
const sinceMs = Date.now() - sinceLookback;
|
|
41
|
+
const recent = deps.activityLog.queryAll({ sinceMs });
|
|
42
|
+
const suggestions = [];
|
|
43
|
+
// (1) Co-occurring tool pairs that aren't already in a recipe.
|
|
44
|
+
// The agent's filter `!pairAlreadyInRecipe(p, runs)` requires us to
|
|
45
|
+
// know which (tool, tool) pairs ever appeared together inside a single
|
|
46
|
+
// recipe run. We synthesize that set from RecipeRun.stepResults.
|
|
47
|
+
const pairsInRecipes = deps.recipeRunLog
|
|
48
|
+
? buildRecipePairSet(deps.recipeRunLog)
|
|
49
|
+
: new Set();
|
|
50
|
+
const coOccurringPairs = computeCoOccurrence(recent, window, 50)
|
|
51
|
+
.filter((p) => p.count >= minCount)
|
|
52
|
+
.filter((p) => !pairsInRecipes.has(p.pair));
|
|
53
|
+
for (const { pair, count } of coOccurringPairs.slice(0, 10)) {
|
|
54
|
+
const [a, b] = pair.split("|");
|
|
55
|
+
suggestions.push({
|
|
56
|
+
kind: "co_occurring_pair",
|
|
57
|
+
label: `You called ${a} and ${b} together ${count} times in the last 7 days. Create a recipe?`,
|
|
58
|
+
details: { pair: [a, b], count },
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
// (2) Installed but never used (in the lookback window).
|
|
62
|
+
const listToolNames = deps.listToolNamesFn ?? defaultListToolNames;
|
|
63
|
+
const installed = listToolNames();
|
|
64
|
+
if (installed.length > 0) {
|
|
65
|
+
const usedSet = new Set();
|
|
66
|
+
for (const e of recent) {
|
|
67
|
+
if (e.tool)
|
|
68
|
+
usedSet.add(e.tool);
|
|
69
|
+
}
|
|
70
|
+
const unused = installed.filter((t) => !usedSet.has(t));
|
|
71
|
+
if (unused.length > 0) {
|
|
72
|
+
// Single rolled-up suggestion with the count + a few examples;
|
|
73
|
+
// listing every unused tool would flood the output for fresh
|
|
74
|
+
// installs where ~150 of 170 tools have never been called.
|
|
75
|
+
const examples = unused.slice(0, 5);
|
|
76
|
+
const more = unused.length > 5 ? `, … (+${unused.length - 5} more)` : "";
|
|
77
|
+
suggestions.push({
|
|
78
|
+
kind: "installed_but_unused",
|
|
79
|
+
label: `${unused.length} installed tools haven't been used in the last 7 days. Examples: ${examples.join(", ")}${more}.`,
|
|
80
|
+
details: { unusedTools: unused.slice(0, 50) },
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
// (3) Recipe trust graduation — recipes with ≥ minRuns successful runs.
|
|
85
|
+
if (deps.recipeRunLog) {
|
|
86
|
+
const byRecipe = groupRunsByRecipe(deps.recipeRunLog);
|
|
87
|
+
const graduates = [...byRecipe.entries()]
|
|
88
|
+
.filter(([, runs]) => runs.length >= minRuns)
|
|
89
|
+
.filter(([, runs]) => runs.every((r) => r.status === "done"))
|
|
90
|
+
.sort((a, b) => b[1].length - a[1].length)
|
|
91
|
+
.slice(0, 10);
|
|
92
|
+
for (const [recipeName, runs] of graduates) {
|
|
93
|
+
suggestions.push({
|
|
94
|
+
kind: "recipe_trust_graduation",
|
|
95
|
+
label: `Recipe \`${recipeName}\` has succeeded ${runs.length}/${runs.length} times — consider trust graduation.`,
|
|
96
|
+
details: { recipeName, runCount: runs.length },
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return suggestions;
|
|
101
|
+
}
|
|
102
|
+
function defaultListToolNames() {
|
|
103
|
+
return listTools().map((t) => t.id);
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Build the set of (toolA, toolB) pairs that ever appeared together
|
|
107
|
+
* inside a single recipe run. Pairs are alphabetized to match
|
|
108
|
+
* `computeCoOccurrence`'s key shape ("a|b" with a < b).
|
|
109
|
+
*
|
|
110
|
+
* We pull from the most recent 500 runs (the default in-memory cap) —
|
|
111
|
+
* older runs are evicted from RAM and we don't pay for the disk scan
|
|
112
|
+
* here; if a tool pair only ever appeared in a 6-month-old run, the
|
|
113
|
+
* suggestion will fire as if the pair is new, which is a fine UX
|
|
114
|
+
* (the user has time to re-promote it).
|
|
115
|
+
*/
|
|
116
|
+
function buildRecipePairSet(runLog) {
|
|
117
|
+
const pairs = new Set();
|
|
118
|
+
const runs = runLog.query({ limit: 500 });
|
|
119
|
+
for (const run of runs) {
|
|
120
|
+
if (!run.stepResults || run.stepResults.length < 2)
|
|
121
|
+
continue;
|
|
122
|
+
const tools = run.stepResults
|
|
123
|
+
.map((s) => s.tool)
|
|
124
|
+
.filter((t) => typeof t === "string" && t.length > 0);
|
|
125
|
+
for (let i = 0; i < tools.length; i++) {
|
|
126
|
+
for (let j = i + 1; j < tools.length; j++) {
|
|
127
|
+
const a = tools[i];
|
|
128
|
+
const b = tools[j];
|
|
129
|
+
if (!a || !b || a === b)
|
|
130
|
+
continue;
|
|
131
|
+
const key = a < b ? `${a}|${b}` : `${b}|${a}`;
|
|
132
|
+
pairs.add(key);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return pairs;
|
|
137
|
+
}
|
|
138
|
+
function groupRunsByRecipe(runLog) {
|
|
139
|
+
const grouped = new Map();
|
|
140
|
+
// We pull more than the default 100 since we're computing aggregate
|
|
141
|
+
// stats — 500 is the in-memory cap and matches the typical buffer.
|
|
142
|
+
const runs = runLog.query({ limit: 500 });
|
|
143
|
+
for (const run of runs) {
|
|
144
|
+
const list = grouped.get(run.recipeName) ?? [];
|
|
145
|
+
list.push(run);
|
|
146
|
+
grouped.set(run.recipeName, list);
|
|
147
|
+
}
|
|
148
|
+
return grouped;
|
|
149
|
+
}
|
|
150
|
+
//# sourceMappingURL=automationSuggestions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"automationSuggestions.js","sourceRoot":"","sources":["../src/automationSuggestions.ts"],"names":[],"mappings":"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"}
|
package/dist/bridge.d.ts
CHANGED
|
@@ -39,8 +39,10 @@ export declare class Bridge {
|
|
|
39
39
|
private pluginTools;
|
|
40
40
|
private pluginWatcher;
|
|
41
41
|
private automationHooks;
|
|
42
|
+
private recipeOrchestration;
|
|
42
43
|
private recipeScheduler;
|
|
43
44
|
private recipeRunLog;
|
|
45
|
+
private recipeOrchestrator;
|
|
44
46
|
private commitIssueLinkLog;
|
|
45
47
|
private decisionTraceLog;
|
|
46
48
|
/** Pre-computed digest of recent decisions, refreshed on each session connect. */
|
|
@@ -56,6 +58,7 @@ export declare class Bridge {
|
|
|
56
58
|
/** ISO timestamp of last getProjectContext cache write — drives status-bar "context X min ago". */
|
|
57
59
|
private _lastContextCachedAt;
|
|
58
60
|
private wsHeartbeatInterval;
|
|
61
|
+
private tokenUsageTracker;
|
|
59
62
|
constructor(config: Config);
|
|
60
63
|
/** Push live bridge state to the extension for status-bar display. */
|
|
61
64
|
private _emitLiveState;
|
package/dist/bridge.js
CHANGED
|
@@ -11,27 +11,32 @@ import { getApprovalQueue } from "./approvalQueue.js";
|
|
|
11
11
|
import { AutomationHooks, loadPolicy } from "./automation.js";
|
|
12
12
|
import { loadOrCreateBridgeToken } from "./bridgeToken.js";
|
|
13
13
|
import { repairBridgeToolsRulesIfStale } from "./bridgeToolsRules.js";
|
|
14
|
-
import { createDriver } from "./claudeDriver.js";
|
|
15
14
|
import { ClaudeOrchestrator } from "./claudeOrchestrator.js";
|
|
16
15
|
import { CommitIssueLinkLog } from "./commitIssueLinkLog.js";
|
|
17
16
|
import { DecisionTraceLog } from "./decisionTraceLog.js";
|
|
17
|
+
import { createDriver } from "./drivers/index.js";
|
|
18
18
|
import { ExtensionClient } from "./extensionClient.js";
|
|
19
|
+
import { lockKillSwitchEnv } from "./featureFlags.js";
|
|
19
20
|
import { FileLock } from "./fileLock.js";
|
|
20
21
|
import { buildEnforcementReminder } from "./instructionsUtils.js";
|
|
21
22
|
import { LockFileManager } from "./lockfile.js";
|
|
22
23
|
import { Logger } from "./logger.js";
|
|
23
24
|
import { OAuthServerImpl } from "./oauth.js";
|
|
25
|
+
import { loadConfig as loadPatchworkConfig } from "./patchworkConfig.js";
|
|
24
26
|
import { loadPlugins, loadPluginsFull } from "./pluginLoader.js";
|
|
25
27
|
import { PluginWatcher } from "./pluginWatcher.js";
|
|
28
|
+
import { isPreToolUseHookRegistered } from "./preToolUseHook.js";
|
|
26
29
|
import { probeAll } from "./probe.js";
|
|
27
|
-
import {
|
|
28
|
-
import {
|
|
30
|
+
import { RecipeOrchestration } from "./recipeOrchestration.js";
|
|
31
|
+
import { warnAboutLegacyPermissionsSidecars } from "./recipes/migrationWarnings.js";
|
|
32
|
+
import { RecipeOrchestrator } from "./recipes/RecipeOrchestrator.js";
|
|
29
33
|
import { classifyTool } from "./riskTier.js";
|
|
30
34
|
import { RecipeRunLog } from "./runLog.js";
|
|
31
35
|
import { Server } from "./server.js";
|
|
32
36
|
import { SessionCheckpoint } from "./sessionCheckpoint.js";
|
|
33
37
|
import { StreamableHttpHandler } from "./streamableHttp.js";
|
|
34
38
|
import { initTelemetry, shutdownTelemetry } from "./telemetry.js";
|
|
39
|
+
import { TokenUsageTracker } from "./tokenUsageTracker.js";
|
|
35
40
|
import { createCtxQueryTracesTool } from "./tools/ctxQueryTraces.js";
|
|
36
41
|
import { readNote, writeNote } from "./tools/handoffNote.js";
|
|
37
42
|
import { registerAllTools } from "./tools/index.js";
|
|
@@ -112,8 +117,10 @@ export class Bridge {
|
|
|
112
117
|
pluginTools = [];
|
|
113
118
|
pluginWatcher = null;
|
|
114
119
|
automationHooks = undefined;
|
|
120
|
+
recipeOrchestration = null;
|
|
115
121
|
recipeScheduler = null;
|
|
116
122
|
recipeRunLog = null;
|
|
123
|
+
recipeOrchestrator = null;
|
|
117
124
|
commitIssueLinkLog = null;
|
|
118
125
|
decisionTraceLog = null;
|
|
119
126
|
/** Pre-computed digest of recent decisions, refreshed on each session connect. */
|
|
@@ -129,6 +136,7 @@ export class Bridge {
|
|
|
129
136
|
/** ISO timestamp of last getProjectContext cache write — drives status-bar "context X min ago". */
|
|
130
137
|
_lastContextCachedAt = null;
|
|
131
138
|
wsHeartbeatInterval = null;
|
|
139
|
+
tokenUsageTracker;
|
|
132
140
|
constructor(config) {
|
|
133
141
|
this.config = config;
|
|
134
142
|
this.logger = new Logger(config.verbose, config.jsonl);
|
|
@@ -136,6 +144,7 @@ export class Bridge {
|
|
|
136
144
|
const configDir = process.env.CLAUDE_CONFIG_DIR ?? path.join(os.homedir(), ".claude");
|
|
137
145
|
this.authToken = config.fixedToken ?? loadOrCreateBridgeToken(configDir);
|
|
138
146
|
this.server = new Server(this.authToken, this.logger, config.corsOrigins);
|
|
147
|
+
this.server.bridgeConfigPath = config.configFilePath ?? undefined;
|
|
139
148
|
if (config.issuerUrl) {
|
|
140
149
|
this.oauthServer = new OAuthServerImpl(this.authToken, config.issuerUrl, {
|
|
141
150
|
configDir,
|
|
@@ -150,6 +159,10 @@ export class Bridge {
|
|
|
150
159
|
this.logger.info(`Audit log: ${config.auditLogPath}`);
|
|
151
160
|
}
|
|
152
161
|
this.extensionClient = new ExtensionClient(this.logger);
|
|
162
|
+
this.tokenUsageTracker = new TokenUsageTracker({
|
|
163
|
+
workspace: config.workspace,
|
|
164
|
+
logger: { warn: (m) => this.logger.warn(m) },
|
|
165
|
+
});
|
|
153
166
|
// Handle new Claude Code connections
|
|
154
167
|
this.server.on("connection", async (ws) => {
|
|
155
168
|
// Reject connections before probes are ready
|
|
@@ -181,6 +194,15 @@ export class Bridge {
|
|
|
181
194
|
existing.graceTimer = null;
|
|
182
195
|
existing.ws = ws;
|
|
183
196
|
existing.wsAlive = true;
|
|
197
|
+
// Clean up old WS listener + reject stale pending elicitations
|
|
198
|
+
// before attaching the new socket. Without this, the previous
|
|
199
|
+
// listener accumulates and pending elicitations from the old
|
|
200
|
+
// client can resolve against the new connection's `activeWs`,
|
|
201
|
+
// surfacing prompts to a client that didn't request them.
|
|
202
|
+
// Intentionally NOT calling `detach()` — that would abort
|
|
203
|
+
// in-flight tool calls, but grace-period semantics preserve
|
|
204
|
+
// those across the reconnect.
|
|
205
|
+
existing.transport.detachSoft();
|
|
184
206
|
existing.transport.attach(ws);
|
|
185
207
|
ws.on("pong", () => {
|
|
186
208
|
existing.wsAlive = true;
|
|
@@ -239,8 +261,26 @@ export class Bridge {
|
|
|
239
261
|
transport.sessionId = sessionId;
|
|
240
262
|
transport.setActivityLog(this.activityLog);
|
|
241
263
|
transport.setToolRateLimit(this.config.toolRateLimit);
|
|
264
|
+
if (this.config.lazyTools)
|
|
265
|
+
transport.setLazyTools(true);
|
|
242
266
|
if (this.server.approvalGate !== "off") {
|
|
243
267
|
this.logger.info(`[patchwork] approval gate active: ${this.server.approvalGate} tier(s) require dashboard approval`);
|
|
268
|
+
// The approval gate only sees traffic if Claude Code's PreToolUse
|
|
269
|
+
// hook is registered to POST into /approvals. If it isn't, every
|
|
270
|
+
// CC tool call bypasses the bridge entirely — the queue stays
|
|
271
|
+
// empty, no `approval_decision` rows accumulate, and the entire
|
|
272
|
+
// personalSignals catalog has no input data. Silent foot-gun
|
|
273
|
+
// that wasted hours of investigation; surface it loudly.
|
|
274
|
+
try {
|
|
275
|
+
const ccSettingsPath = path.join(process.env.CLAUDE_CONFIG_DIR ?? path.join(os.homedir(), ".claude"), "settings.json");
|
|
276
|
+
if (!isPreToolUseHookRegistered(ccSettingsPath)) {
|
|
277
|
+
this.logger.warn?.(`[patchwork] WARNING: approval gate is on but no PreToolUse hook found in ${ccSettingsPath}. ` +
|
|
278
|
+
`Claude Code will bypass the approval queue. Run 'patchwork-init' to register the hook.`);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
catch {
|
|
282
|
+
// Best-effort warning — never block startup on a hook check failure.
|
|
283
|
+
}
|
|
244
284
|
transport.setApprovalGate(async ({ toolName, params, sessionId }) => {
|
|
245
285
|
const tier = classifyTool(toolName);
|
|
246
286
|
if (this.server.approvalGate === "off")
|
|
@@ -677,7 +717,11 @@ export class Bridge {
|
|
|
677
717
|
async start() {
|
|
678
718
|
// 0. Initialize OpenTelemetry (no-op when OTEL_EXPORTER_OTLP_ENDPOINT is unset)
|
|
679
719
|
initTelemetry();
|
|
680
|
-
// 0a.
|
|
720
|
+
// 0a. Snapshot env-derived kill-switch flags. After this, plugins or
|
|
721
|
+
// recipe steps mutating `process.env.PATCHWORK_FLAG_*` cannot disable an
|
|
722
|
+
// active emergency stop. Closes MED-2 from the 2026-04-28 audit.
|
|
723
|
+
lockKillSwitchEnv();
|
|
724
|
+
// 0b. Auto-repair .claude/rules/bridge-tools.md if stale (present but missing the
|
|
681
725
|
// current version sentinel). Older package versions write stale files that may
|
|
682
726
|
// lack new tool substitution rules. Repair is silent on success; falls back to
|
|
683
727
|
// warn-only if the template is missing or the write fails.
|
|
@@ -685,6 +729,7 @@ export class Bridge {
|
|
|
685
729
|
// 1. Probe available CLI tools (pass workspace so local node_modules/.bin is checked)
|
|
686
730
|
this.probes = await probeAll(this.config.workspace);
|
|
687
731
|
this.ready = true;
|
|
732
|
+
this.tokenUsageTracker.start();
|
|
688
733
|
// 2. Load plugins (after probes, before accepting sessions)
|
|
689
734
|
this.pluginTools = await loadPlugins(this.config.plugins, this.config, this.logger);
|
|
690
735
|
if (this.config.pluginWatch && this.config.plugins.length > 0) {
|
|
@@ -703,8 +748,19 @@ export class Bridge {
|
|
|
703
748
|
this.logger.info(`Available linters: ${probeList(["tsc", "eslint", "pyright", "ruff", "cargo", "go", "biome"])}`);
|
|
704
749
|
this.logger.info(`Available test runners: ${probeList(["vitest", "jest", "pytest", "cargo", "go"])}`);
|
|
705
750
|
// 2. Initialize Claude driver and orchestrator (if configured)
|
|
706
|
-
if (this.config.
|
|
707
|
-
const driver = createDriver(this.config.
|
|
751
|
+
if (this.config.driver !== "none") {
|
|
752
|
+
const driver = createDriver(this.config.driver, {
|
|
753
|
+
binary: this.config.claudeBinary,
|
|
754
|
+
antBinary: this.config.antBinary,
|
|
755
|
+
bridgeMcp: this.config.driver === "gemini"
|
|
756
|
+
? () => this.port > 0
|
|
757
|
+
? {
|
|
758
|
+
url: `http://127.0.0.1:${this.port}/mcp`,
|
|
759
|
+
authToken: this.authToken,
|
|
760
|
+
}
|
|
761
|
+
: undefined
|
|
762
|
+
: undefined,
|
|
763
|
+
}, (msg) => this.logger.info(msg));
|
|
708
764
|
// Patchwork: enrichment link log is useful regardless of orchestrator.
|
|
709
765
|
const patchworkDir = path.join(os.homedir(), ".patchwork");
|
|
710
766
|
this.commitIssueLinkLog = new CommitIssueLinkLog({
|
|
@@ -764,17 +820,33 @@ export class Bridge {
|
|
|
764
820
|
},
|
|
765
821
|
});
|
|
766
822
|
this.logger.info(`[bridge] Claude driver: ${driver.name}`);
|
|
767
|
-
//
|
|
823
|
+
// Recipe orchestrator — owns in-flight dedup across all entry paths.
|
|
824
|
+
this.recipeOrchestrator = new RecipeOrchestrator({
|
|
825
|
+
workdir: this.config.workspace,
|
|
826
|
+
});
|
|
827
|
+
// Patchwork: wire recipe server fns + build cron scheduler.
|
|
768
828
|
const recipesDir = path.join(os.homedir(), ".patchwork", "recipes");
|
|
769
|
-
|
|
829
|
+
// One-shot scan for legacy `<name>.permissions.json` sidecars (alpha.36+
|
|
830
|
+
// no longer generates them). Skipped under NODE_ENV=test.
|
|
831
|
+
warnAboutLegacyPermissionsSidecars(recipesDir);
|
|
832
|
+
this.recipeOrchestration = new RecipeOrchestration({
|
|
833
|
+
server: this.server,
|
|
834
|
+
getOrchestrator: () => this.orchestrator,
|
|
835
|
+
recipeOrchestrator: this.recipeOrchestrator,
|
|
836
|
+
recipeRunLog: this.recipeRunLog,
|
|
837
|
+
activityLog: this.activityLog,
|
|
838
|
+
workdir: this.config.workspace,
|
|
839
|
+
logger: this.logger,
|
|
840
|
+
});
|
|
841
|
+
this.recipeOrchestration.wireServerFns();
|
|
842
|
+
this.recipeScheduler = RecipeOrchestration.buildScheduler({
|
|
770
843
|
recipesDir,
|
|
771
|
-
|
|
844
|
+
runRecipeFn: async (name) => this.server.runRecipeFn?.(name),
|
|
845
|
+
enqueue: (opts) => this.orchestrator?.enqueue(opts) ?? "",
|
|
772
846
|
logger: this.logger,
|
|
773
847
|
});
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
this.logger.info(`[patchwork] scheduled ${scheduled.length} cron recipe${scheduled.length === 1 ? "" : "s"}`);
|
|
777
|
-
}
|
|
848
|
+
// scheduler.start() deferred to after this.port is set (see below)
|
|
849
|
+
// so bridgeMcp callback has a valid port when first cron fires.
|
|
778
850
|
}
|
|
779
851
|
}
|
|
780
852
|
if (this.config.automationEnabled) {
|
|
@@ -808,6 +880,7 @@ export class Bridge {
|
|
|
808
880
|
extensionCircuitBreaker: this.extensionClient.getCircuitBreakerState(),
|
|
809
881
|
extensionDisconnectCount: this.extensionDisconnectCount,
|
|
810
882
|
recentActivity: this.activityLog.query({ last: 10 }),
|
|
883
|
+
tokens: this.tokenUsageTracker.getTotals(),
|
|
811
884
|
};
|
|
812
885
|
};
|
|
813
886
|
this.server.metricsFn = () => this.activityLog.toPrometheus({
|
|
@@ -974,6 +1047,37 @@ export class Bridge {
|
|
|
974
1047
|
};
|
|
975
1048
|
};
|
|
976
1049
|
this.server.streamFn = (listener) => this.activityLog.subscribe(listener);
|
|
1050
|
+
this.server.cancelTaskFn = (id) => this.orchestrator?.cancel(id, "user") ?? false;
|
|
1051
|
+
// Wire `/sessions` for the dashboard's Sessions page. The same Map is
|
|
1052
|
+
// already iterated by `statusFn` for the bridge-status payload — exposing
|
|
1053
|
+
// it as a list-only endpoint lets the dashboard render per-session cards
|
|
1054
|
+
// without coupling to the rest of the status payload. Without this the
|
|
1055
|
+
// page returns 404 forever (sessionsFn was declared but never assigned),
|
|
1056
|
+
// even when sessions are connected.
|
|
1057
|
+
this.server.sessionsFn = () => {
|
|
1058
|
+
const approvals = getApprovalQueue().list();
|
|
1059
|
+
const pendingBySession = new Map();
|
|
1060
|
+
for (const a of approvals) {
|
|
1061
|
+
if (!a.sessionId)
|
|
1062
|
+
continue;
|
|
1063
|
+
// Match by 8-char prefix because the dashboard's session id is
|
|
1064
|
+
// the same prefix shape (statusFn emits `s.id.slice(0, 8)`).
|
|
1065
|
+
const prefix = a.sessionId.slice(0, 8);
|
|
1066
|
+
pendingBySession.set(prefix, (pendingBySession.get(prefix) ?? 0) + 1);
|
|
1067
|
+
}
|
|
1068
|
+
const out = [];
|
|
1069
|
+
for (const s of this.sessions.values()) {
|
|
1070
|
+
const id = s.id.slice(0, 8);
|
|
1071
|
+
out.push({
|
|
1072
|
+
id,
|
|
1073
|
+
connectedAt: new Date(s.connectedAt).toISOString(),
|
|
1074
|
+
openedFileCount: s.openedFiles.size,
|
|
1075
|
+
pendingApprovals: pendingBySession.get(id) ?? 0,
|
|
1076
|
+
...(s.remoteAddr ? { remoteAddr: s.remoteAddr } : {}),
|
|
1077
|
+
});
|
|
1078
|
+
}
|
|
1079
|
+
return out;
|
|
1080
|
+
};
|
|
977
1081
|
this.server.tasksFn = () => ({
|
|
978
1082
|
tasks: (this.orchestrator?.list() ?? []).map((t) => ({
|
|
979
1083
|
taskId: t.id,
|
|
@@ -1000,134 +1104,21 @@ export class Bridge {
|
|
|
1000
1104
|
this.server.approvalWebhookUrl =
|
|
1001
1105
|
this.config.approvalWebhookUrl ?? undefined;
|
|
1002
1106
|
this.server.onApprovalDecision = (event, meta) => this.activityLog.recordEvent(event, meta);
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
}),
|
|
1019
|
-
...(q.status !== undefined && {
|
|
1020
|
-
status: q.status,
|
|
1021
|
-
}),
|
|
1022
|
-
...(q.recipe !== undefined && { recipe: q.recipe }),
|
|
1023
|
-
...(q.after !== undefined && { after: q.after }),
|
|
1024
|
-
});
|
|
1025
|
-
};
|
|
1026
|
-
this.server.sessionsFn = () => [...this.sessions.values()].map((s) => {
|
|
1027
|
-
const tools = this.activityLog.querySessionTools(s.id, 1);
|
|
1028
|
-
return {
|
|
1029
|
-
id: s.id,
|
|
1030
|
-
connectedAt: new Date(s.connectedAt).toISOString(),
|
|
1031
|
-
openedFileCount: s.openedFiles.size,
|
|
1032
|
-
pendingApprovals: getApprovalQueue()
|
|
1033
|
-
.list()
|
|
1034
|
-
.filter((a) => a.sessionId === s.id).length,
|
|
1035
|
-
firstTool: tools[0]?.tool,
|
|
1036
|
-
remoteAddr: s.remoteAddr,
|
|
1037
|
-
};
|
|
1038
|
-
});
|
|
1039
|
-
this.server.sessionDetailFn = (id) => {
|
|
1040
|
-
const s = this.sessions.get(id);
|
|
1041
|
-
const summary = s
|
|
1042
|
-
? {
|
|
1043
|
-
id: s.id,
|
|
1044
|
-
connectedAt: new Date(s.connectedAt).toISOString(),
|
|
1045
|
-
openedFileCount: s.openedFiles.size,
|
|
1046
|
-
pendingApprovals: getApprovalQueue()
|
|
1047
|
-
.list()
|
|
1048
|
-
.filter((a) => a.sessionId === s.id).length,
|
|
1049
|
-
}
|
|
1050
|
-
: null;
|
|
1051
|
-
const lifecycle = this.activityLog.querySessionLifecycle(id, 100);
|
|
1052
|
-
const tools = this.activityLog.querySessionTools(id, 100);
|
|
1053
|
-
const decisions = this.decisionTraceLog?.query({ sessionId: id, limit: 50 }) ?? [];
|
|
1054
|
-
const approvals = getApprovalQueue()
|
|
1055
|
-
.list()
|
|
1056
|
-
.filter((a) => a.sessionId === id);
|
|
1057
|
-
return {
|
|
1058
|
-
summary,
|
|
1059
|
-
lifecycle: lifecycle,
|
|
1060
|
-
tools: tools,
|
|
1061
|
-
decisions: decisions,
|
|
1062
|
-
approvals: approvals,
|
|
1063
|
-
};
|
|
1064
|
-
};
|
|
1065
|
-
this.server.webhookFn = async (hookPath, payload) => {
|
|
1066
|
-
if (!this.orchestrator) {
|
|
1067
|
-
return {
|
|
1068
|
-
ok: false,
|
|
1069
|
-
error: "orchestrator_unavailable",
|
|
1070
|
-
};
|
|
1071
|
-
}
|
|
1072
|
-
const recipesDir = path.join(os.homedir(), ".patchwork", "recipes");
|
|
1073
|
-
const match = findWebhookRecipe(recipesDir, hookPath);
|
|
1074
|
-
if (!match) {
|
|
1075
|
-
return { ok: false, error: "not_found" };
|
|
1076
|
-
}
|
|
1077
|
-
const loaded = loadRecipePrompt(recipesDir, match.name);
|
|
1078
|
-
if (!loaded) {
|
|
1079
|
-
return { ok: false, error: "recipe_file_missing" };
|
|
1080
|
-
}
|
|
1081
|
-
try {
|
|
1082
|
-
const taskId = this.orchestrator.enqueue({
|
|
1083
|
-
prompt: renderWebhookPrompt(loaded.prompt, payload),
|
|
1084
|
-
triggerSource: `webhook:${match.name}`,
|
|
1085
|
-
});
|
|
1086
|
-
return { ok: true, taskId, name: match.name };
|
|
1087
|
-
}
|
|
1088
|
-
catch (err) {
|
|
1089
|
-
return {
|
|
1090
|
-
ok: false,
|
|
1091
|
-
error: err instanceof Error ? err.message : String(err),
|
|
1092
|
-
};
|
|
1093
|
-
}
|
|
1094
|
-
};
|
|
1095
|
-
this.server.runRecipeFn = async (name, vars) => {
|
|
1096
|
-
if (!this.orchestrator) {
|
|
1097
|
-
return {
|
|
1098
|
-
ok: false,
|
|
1099
|
-
error: "Orchestrator unavailable — start bridge with --claude-driver subprocess",
|
|
1100
|
-
};
|
|
1101
|
-
}
|
|
1102
|
-
const recipesDir = path.join(os.homedir(), ".patchwork", "recipes");
|
|
1103
|
-
const loaded = loadRecipePrompt(recipesDir, name);
|
|
1104
|
-
if (!loaded) {
|
|
1105
|
-
return {
|
|
1106
|
-
ok: false,
|
|
1107
|
-
error: `Recipe "${name}" not found in ${recipesDir}`,
|
|
1108
|
-
};
|
|
1109
|
-
}
|
|
1110
|
-
try {
|
|
1111
|
-
let prompt = loaded.prompt;
|
|
1112
|
-
if (vars && Object.keys(vars).length > 0) {
|
|
1113
|
-
const varLines = Object.entries(vars)
|
|
1114
|
-
.map(([k, v]) => `${k}=${v}`)
|
|
1115
|
-
.join("\n");
|
|
1116
|
-
prompt = `Variables:\n${varLines}\n\n${prompt}`;
|
|
1117
|
-
}
|
|
1118
|
-
const taskId = this.orchestrator.enqueue({
|
|
1119
|
-
prompt,
|
|
1120
|
-
triggerSource: `recipe:${name}`,
|
|
1121
|
-
});
|
|
1122
|
-
return { ok: true, taskId };
|
|
1123
|
-
}
|
|
1124
|
-
catch (err) {
|
|
1125
|
-
return {
|
|
1126
|
-
ok: false,
|
|
1127
|
-
error: err instanceof Error ? err.message : String(err),
|
|
1128
|
-
};
|
|
1129
|
-
}
|
|
1130
|
-
};
|
|
1107
|
+
// Wire activityLog into approvalHttp so passive risk personalization
|
|
1108
|
+
// signals (src/approvalSignals.ts) actually compute against the
|
|
1109
|
+
// user's history. Without this, the personalSignals catalog is
|
|
1110
|
+
// defined but inert — every approval queue entry has
|
|
1111
|
+
// personalSignals: undefined.
|
|
1112
|
+
this.server.activityLog = this.activityLog;
|
|
1113
|
+
// Same wire pattern for h6 (recipe-step trust). recipeRunLog may be
|
|
1114
|
+
// undefined when no orchestrator is configured; that's fine — h6
|
|
1115
|
+
// silently skips and the other 11 heuristics still compute.
|
|
1116
|
+
this.server.recipeRunLog = this.recipeRunLog ?? undefined;
|
|
1117
|
+
// Opt-in switch for personalSignals h10 (time-of-day anomaly).
|
|
1118
|
+
// Default false; user enables via --enable-time-of-day-anomaly or
|
|
1119
|
+
// ~/.patchwork/config.json's enableTimeOfDayAnomaly field.
|
|
1120
|
+
this.server.enableTimeOfDayAnomaly =
|
|
1121
|
+
this.config.enableTimeOfDayAnomaly ?? false;
|
|
1131
1122
|
this.server.readyFn = () => {
|
|
1132
1123
|
// Count tools from the first active session (all sessions share the same tool set)
|
|
1133
1124
|
const anySession = [...this.sessions.values()][0];
|
|
@@ -1168,11 +1159,18 @@ export class Bridge {
|
|
|
1168
1159
|
patchwork: {
|
|
1169
1160
|
workspace: this.config.workspace,
|
|
1170
1161
|
approvalGate: this.server.approvalGate,
|
|
1162
|
+
enableTimeOfDayAnomaly: this.server.enableTimeOfDayAnomaly,
|
|
1171
1163
|
fullMode: this.config.fullMode,
|
|
1172
|
-
|
|
1164
|
+
driver: this.config.driver,
|
|
1165
|
+
model: loadPatchworkConfig().model,
|
|
1166
|
+
localEndpoint: loadPatchworkConfig().localEndpoint,
|
|
1167
|
+
localModel: loadPatchworkConfig().localModel,
|
|
1173
1168
|
automationEnabled: this.config.automationEnabled,
|
|
1174
1169
|
port: this.port,
|
|
1175
1170
|
webhookUrl: this.server.approvalWebhookUrl ?? null,
|
|
1171
|
+
pushServiceUrl: this.server.pushServiceUrl ?? null,
|
|
1172
|
+
pushServiceToken: this.server.pushServiceToken ? "***" : null,
|
|
1173
|
+
pushServiceBaseUrl: this.server.pushServiceBaseUrl ?? null,
|
|
1176
1174
|
},
|
|
1177
1175
|
};
|
|
1178
1176
|
};
|
|
@@ -1259,6 +1257,31 @@ export class Bridge {
|
|
|
1259
1257
|
: null, async () => {
|
|
1260
1258
|
await this.refreshRecentTracesDigest();
|
|
1261
1259
|
return this.buildInstructions();
|
|
1260
|
+
},
|
|
1261
|
+
// Tail-end deps so `registerAllTools` registers the same tool set
|
|
1262
|
+
// on Streamable-HTTP sessions as on WebSocket. Without this object,
|
|
1263
|
+
// `ctxSaveTrace`, `ctxQueryTraces`, and any tool gated on the
|
|
1264
|
+
// remaining deps silently failed to register over Streamable HTTP
|
|
1265
|
+
// (caught dogfooding `ctx-loop-test` from a remote MCP client).
|
|
1266
|
+
{
|
|
1267
|
+
automationHooks: this.automationHooks,
|
|
1268
|
+
getDisconnectInfo: () => ({
|
|
1269
|
+
at: this.lastDisconnectAt,
|
|
1270
|
+
code: this.lastDisconnectCode,
|
|
1271
|
+
reason: this.lastDisconnectReason,
|
|
1272
|
+
}),
|
|
1273
|
+
onContextCacheUpdated: (generatedAt) => {
|
|
1274
|
+
this._lastContextCachedAt = generatedAt;
|
|
1275
|
+
this._emitLiveState();
|
|
1276
|
+
},
|
|
1277
|
+
getExtensionDisconnectCount: () => this.extensionDisconnectCount,
|
|
1278
|
+
...(this.commitIssueLinkLog && {
|
|
1279
|
+
commitIssueLinkLog: this.commitIssueLinkLog,
|
|
1280
|
+
}),
|
|
1281
|
+
...(this.recipeRunLog && { recipeRunLog: this.recipeRunLog }),
|
|
1282
|
+
...(this.decisionTraceLog && {
|
|
1283
|
+
decisionTraceLog: this.decisionTraceLog,
|
|
1284
|
+
}),
|
|
1262
1285
|
});
|
|
1263
1286
|
this.server.httpMcpHandler = (req, res) => this.httpMcpHandler?.handle(req, res) ?? Promise.resolve();
|
|
1264
1287
|
// 3. Check for stale lock files
|
|
@@ -1275,6 +1298,13 @@ export class Bridge {
|
|
|
1275
1298
|
throw err;
|
|
1276
1299
|
}
|
|
1277
1300
|
this.port = port;
|
|
1301
|
+
// 4a-deferred. Start recipe scheduler now that port is known (bridgeMcp needs a valid port).
|
|
1302
|
+
if (this.recipeScheduler) {
|
|
1303
|
+
const scheduled = this.recipeScheduler.start();
|
|
1304
|
+
if (scheduled.length > 0) {
|
|
1305
|
+
this.logger.info(`[patchwork] scheduled ${scheduled.length} cron recipe${scheduled.length === 1 ? "" : "s"}`);
|
|
1306
|
+
}
|
|
1307
|
+
}
|
|
1278
1308
|
// 4b. Start WebSocket keepalive heartbeat (keeps MCP session alive during long idle periods)
|
|
1279
1309
|
this._startWsHeartbeat();
|
|
1280
1310
|
// 4c. Load persisted tasks from previous sessions (best-effort)
|
|
@@ -1438,6 +1468,7 @@ export class Bridge {
|
|
|
1438
1468
|
this.stopped = true;
|
|
1439
1469
|
this.logger.info("Shutting down...");
|
|
1440
1470
|
this._stopPeriodicSnapshots();
|
|
1471
|
+
this.tokenUsageTracker.stop();
|
|
1441
1472
|
this.automationHooks?.destroy();
|
|
1442
1473
|
this.recipeScheduler?.stop();
|
|
1443
1474
|
this.recipeScheduler = null;
|
|
@@ -1471,7 +1502,7 @@ export class Bridge {
|
|
|
1471
1502
|
if (this.checkpoint && this.port > 0) {
|
|
1472
1503
|
try {
|
|
1473
1504
|
await Promise.race([
|
|
1474
|
-
Promise.resolve().then(() => this.checkpoint
|
|
1505
|
+
Promise.resolve().then(() => this.checkpoint?.write(this._buildCheckpoint(this.port))),
|
|
1475
1506
|
new Promise((resolve) => setTimeout(resolve, 3000)),
|
|
1476
1507
|
]);
|
|
1477
1508
|
}
|