@vibesdotdev/external 0.0.0-stage → 1.0.0-rc.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/LICENSE +18 -0
- package/README.md +68 -2
- package/REFERENCE.md +217 -0
- package/dist/acp/index.d.ts +28 -0
- package/dist/acp/index.d.ts.map +1 -0
- package/dist/acp/index.js +31 -0
- package/dist/acp/index.js.map +1 -0
- package/dist/ambient/agent-roster-heartbeat.d.ts +67 -0
- package/dist/ambient/agent-roster-heartbeat.d.ts.map +1 -0
- package/dist/ambient/agent-roster-heartbeat.js +257 -0
- package/dist/ambient/agent-roster-heartbeat.js.map +1 -0
- package/dist/ambient/attempt.d.ts +19 -0
- package/dist/ambient/attempt.d.ts.map +1 -0
- package/dist/ambient/attempt.js +39 -0
- package/dist/ambient/attempt.js.map +1 -0
- package/dist/ambient/cloud-agent-roster-provider.impl.d.ts +13 -0
- package/dist/ambient/cloud-agent-roster-provider.impl.d.ts.map +1 -0
- package/dist/ambient/cloud-agent-roster-provider.impl.js +73 -0
- package/dist/ambient/cloud-agent-roster-provider.impl.js.map +1 -0
- package/dist/ambient/cloud-authenticated-json.d.ts +69 -0
- package/dist/ambient/cloud-authenticated-json.d.ts.map +1 -0
- package/dist/ambient/cloud-authenticated-json.js +108 -0
- package/dist/ambient/cloud-authenticated-json.js.map +1 -0
- package/dist/ambient/cloud-touch-projection.impl.d.ts +74 -0
- package/dist/ambient/cloud-touch-projection.impl.d.ts.map +1 -0
- package/dist/ambient/cloud-touch-projection.impl.js +225 -0
- package/dist/ambient/cloud-touch-projection.impl.js.map +1 -0
- package/dist/ambient/collaboration-settings.d.ts +50 -0
- package/dist/ambient/collaboration-settings.d.ts.map +1 -0
- package/dist/ambient/collaboration-settings.js +57 -0
- package/dist/ambient/collaboration-settings.js.map +1 -0
- package/dist/ambient/collision-notice.d.ts +41 -0
- package/dist/ambient/collision-notice.d.ts.map +1 -0
- package/dist/ambient/collision-notice.js +104 -0
- package/dist/ambient/collision-notice.js.map +1 -0
- package/dist/ambient/default-collision-filter.impl.d.ts +19 -0
- package/dist/ambient/default-collision-filter.impl.d.ts.map +1 -0
- package/dist/ambient/default-collision-filter.impl.js +53 -0
- package/dist/ambient/default-collision-filter.impl.js.map +1 -0
- package/dist/ambient/default-notice-formatter.impl.d.ts +4 -0
- package/dist/ambient/default-notice-formatter.impl.d.ts.map +1 -0
- package/dist/ambient/default-notice-formatter.impl.js +38 -0
- package/dist/ambient/default-notice-formatter.impl.js.map +1 -0
- package/dist/ambient/file-access-ambient-drain.impl.d.ts +21 -0
- package/dist/ambient/file-access-ambient-drain.impl.d.ts.map +1 -0
- package/dist/ambient/file-access-ambient-drain.impl.js +53 -0
- package/dist/ambient/file-access-ambient-drain.impl.js.map +1 -0
- package/dist/ambient/file-access-ambient-drain.subscription.descriptor.d.ts +27 -0
- package/dist/ambient/file-access-ambient-drain.subscription.descriptor.d.ts.map +1 -0
- package/dist/ambient/file-access-ambient-drain.subscription.descriptor.js +27 -0
- package/dist/ambient/file-access-ambient-drain.subscription.descriptor.js.map +1 -0
- package/dist/ambient/file-access-ambient-fs.impl.d.ts +45 -0
- package/dist/ambient/file-access-ambient-fs.impl.d.ts.map +1 -0
- package/dist/ambient/file-access-ambient-fs.impl.js +243 -0
- package/dist/ambient/file-access-ambient-fs.impl.js.map +1 -0
- package/dist/ambient/file-access-ambient-fs.subscription.descriptor.d.ts +32 -0
- package/dist/ambient/file-access-ambient-fs.subscription.descriptor.d.ts.map +1 -0
- package/dist/ambient/file-access-ambient-fs.subscription.descriptor.js +39 -0
- package/dist/ambient/file-access-ambient-fs.subscription.descriptor.js.map +1 -0
- package/dist/ambient/file-access-ambient.impl.d.ts +32 -0
- package/dist/ambient/file-access-ambient.impl.d.ts.map +1 -0
- package/dist/ambient/file-access-ambient.impl.js +231 -0
- package/dist/ambient/file-access-ambient.impl.js.map +1 -0
- package/dist/ambient/file-access-ambient.subscription.descriptor.d.ts +31 -0
- package/dist/ambient/file-access-ambient.subscription.descriptor.d.ts.map +1 -0
- package/dist/ambient/file-access-ambient.subscription.descriptor.js +31 -0
- package/dist/ambient/file-access-ambient.subscription.descriptor.js.map +1 -0
- package/dist/ambient/file-access-shared.d.ts +48 -0
- package/dist/ambient/file-access-shared.d.ts.map +1 -0
- package/dist/ambient/file-access-shared.js +59 -0
- package/dist/ambient/file-access-shared.js.map +1 -0
- package/dist/ambient/message-transport.impl.d.ts +47 -0
- package/dist/ambient/message-transport.impl.d.ts.map +1 -0
- package/dist/ambient/message-transport.impl.js +300 -0
- package/dist/ambient/message-transport.impl.js.map +1 -0
- package/dist/ambient/pending-notices.d.ts +11 -0
- package/dist/ambient/pending-notices.d.ts.map +1 -0
- package/dist/ambient/pending-notices.js +24 -0
- package/dist/ambient/pending-notices.js.map +1 -0
- package/dist/ambient/subsystem-overlap.impl.d.ts +60 -0
- package/dist/ambient/subsystem-overlap.impl.d.ts.map +1 -0
- package/dist/ambient/subsystem-overlap.impl.js +127 -0
- package/dist/ambient/subsystem-overlap.impl.js.map +1 -0
- package/dist/cli/dev.external.hooks-install.descriptor.d.ts +7 -0
- package/dist/cli/dev.external.hooks-install.descriptor.d.ts.map +1 -0
- package/dist/cli/dev.external.hooks-install.descriptor.js +27 -0
- package/dist/cli/dev.external.hooks-install.descriptor.js.map +1 -0
- package/dist/cli/dev.external.hooks-install.impl.d.ts +9 -0
- package/dist/cli/dev.external.hooks-install.impl.d.ts.map +1 -0
- package/dist/cli/dev.external.hooks-install.impl.js +57 -0
- package/dist/cli/dev.external.hooks-install.impl.js.map +1 -0
- package/dist/cli/dev.external.hooks-list.descriptor.d.ts +7 -0
- package/dist/cli/dev.external.hooks-list.descriptor.d.ts.map +1 -0
- package/dist/cli/dev.external.hooks-list.descriptor.js +16 -0
- package/dist/cli/dev.external.hooks-list.descriptor.js.map +1 -0
- package/dist/cli/dev.external.hooks-list.impl.d.ts +3 -0
- package/dist/cli/dev.external.hooks-list.impl.d.ts.map +1 -0
- package/dist/cli/dev.external.hooks-list.impl.js +68 -0
- package/dist/cli/dev.external.hooks-list.impl.js.map +1 -0
- package/dist/cli/dev.external.hooks-uninstall.descriptor.d.ts +7 -0
- package/dist/cli/dev.external.hooks-uninstall.descriptor.d.ts.map +1 -0
- package/dist/cli/dev.external.hooks-uninstall.descriptor.js +30 -0
- package/dist/cli/dev.external.hooks-uninstall.descriptor.js.map +1 -0
- package/dist/cli/dev.external.hooks-uninstall.impl.d.ts +9 -0
- package/dist/cli/dev.external.hooks-uninstall.impl.d.ts.map +1 -0
- package/dist/cli/dev.external.hooks-uninstall.impl.js +59 -0
- package/dist/cli/dev.external.hooks-uninstall.impl.js.map +1 -0
- package/dist/cli/dev.external.hooks.call.descriptor.d.ts +12 -0
- package/dist/cli/dev.external.hooks.call.descriptor.d.ts.map +1 -0
- package/dist/cli/dev.external.hooks.call.descriptor.js +51 -0
- package/dist/cli/dev.external.hooks.call.descriptor.js.map +1 -0
- package/dist/cli/dev.external.hooks.call.impl.d.ts +6 -0
- package/dist/cli/dev.external.hooks.call.impl.d.ts.map +1 -0
- package/dist/cli/dev.external.hooks.call.impl.js +162 -0
- package/dist/cli/dev.external.hooks.call.impl.js.map +1 -0
- package/dist/cli/dev.external.hooks.descriptor.d.ts +9 -0
- package/dist/cli/dev.external.hooks.descriptor.d.ts.map +1 -0
- package/dist/cli/dev.external.hooks.descriptor.js +17 -0
- package/dist/cli/dev.external.hooks.descriptor.js.map +1 -0
- package/dist/cli/dev.external.launch.descriptor.d.ts +7 -0
- package/dist/cli/dev.external.launch.descriptor.d.ts.map +1 -0
- package/dist/cli/dev.external.launch.descriptor.js +31 -0
- package/dist/cli/dev.external.launch.descriptor.js.map +1 -0
- package/dist/cli/dev.external.launch.impl.d.ts +17 -0
- package/dist/cli/dev.external.launch.impl.d.ts.map +1 -0
- package/dist/cli/dev.external.launch.impl.js +137 -0
- package/dist/cli/dev.external.launch.impl.js.map +1 -0
- package/dist/cli/dev.external.presets-apply.descriptor.d.ts +7 -0
- package/dist/cli/dev.external.presets-apply.descriptor.d.ts.map +1 -0
- package/dist/cli/dev.external.presets-apply.descriptor.js +36 -0
- package/dist/cli/dev.external.presets-apply.descriptor.js.map +1 -0
- package/dist/cli/dev.external.presets-apply.impl.d.ts +3 -0
- package/dist/cli/dev.external.presets-apply.impl.d.ts.map +1 -0
- package/dist/cli/dev.external.presets-apply.impl.js +119 -0
- package/dist/cli/dev.external.presets-apply.impl.js.map +1 -0
- package/dist/cli/dev.external.presets-list.descriptor.d.ts +7 -0
- package/dist/cli/dev.external.presets-list.descriptor.d.ts.map +1 -0
- package/dist/cli/dev.external.presets-list.descriptor.js +21 -0
- package/dist/cli/dev.external.presets-list.descriptor.js.map +1 -0
- package/dist/cli/dev.external.presets-list.impl.d.ts +3 -0
- package/dist/cli/dev.external.presets-list.impl.d.ts.map +1 -0
- package/dist/cli/dev.external.presets-list.impl.js +33 -0
- package/dist/cli/dev.external.presets-list.impl.js.map +1 -0
- package/dist/cli/dev.external.presets-show.descriptor.d.ts +7 -0
- package/dist/cli/dev.external.presets-show.descriptor.d.ts.map +1 -0
- package/dist/cli/dev.external.presets-show.descriptor.js +29 -0
- package/dist/cli/dev.external.presets-show.descriptor.js.map +1 -0
- package/dist/cli/dev.external.presets-show.impl.d.ts +7 -0
- package/dist/cli/dev.external.presets-show.impl.d.ts.map +1 -0
- package/dist/cli/dev.external.presets-show.impl.js +39 -0
- package/dist/cli/dev.external.presets-show.impl.js.map +1 -0
- package/dist/cli/dev.external.presets.descriptor.d.ts +7 -0
- package/dist/cli/dev.external.presets.descriptor.d.ts.map +1 -0
- package/dist/cli/dev.external.presets.descriptor.js +15 -0
- package/dist/cli/dev.external.presets.descriptor.js.map +1 -0
- package/dist/cli/dev.external.setup.descriptor.d.ts +7 -0
- package/dist/cli/dev.external.setup.descriptor.d.ts.map +1 -0
- package/dist/cli/dev.external.setup.descriptor.js +29 -0
- package/dist/cli/dev.external.setup.descriptor.js.map +1 -0
- package/dist/cli/dev.external.setup.impl.d.ts +3 -0
- package/dist/cli/dev.external.setup.impl.d.ts.map +1 -0
- package/dist/cli/dev.external.setup.impl.js +129 -0
- package/dist/cli/dev.external.setup.impl.js.map +1 -0
- package/dist/cli/dev.external.skills-generate.descriptor.d.ts +7 -0
- package/dist/cli/dev.external.skills-generate.descriptor.d.ts.map +1 -0
- package/dist/cli/dev.external.skills-generate.descriptor.js +43 -0
- package/dist/cli/dev.external.skills-generate.descriptor.js.map +1 -0
- package/dist/cli/dev.external.skills-generate.impl.consumer.d.ts +3 -0
- package/dist/cli/dev.external.skills-generate.impl.consumer.d.ts.map +1 -0
- package/dist/cli/dev.external.skills-generate.impl.consumer.js +140 -0
- package/dist/cli/dev.external.skills-generate.impl.consumer.js.map +1 -0
- package/dist/cli/dev.external.skills.descriptor.d.ts +7 -0
- package/dist/cli/dev.external.skills.descriptor.d.ts.map +1 -0
- package/dist/cli/dev.external.skills.descriptor.js +15 -0
- package/dist/cli/dev.external.skills.descriptor.js.map +1 -0
- package/dist/cli/dev.external.status.descriptor.d.ts +7 -0
- package/dist/cli/dev.external.status.descriptor.d.ts.map +1 -0
- package/dist/cli/dev.external.status.descriptor.js +16 -0
- package/dist/cli/dev.external.status.descriptor.js.map +1 -0
- package/dist/cli/dev.external.status.impl.d.ts +24 -0
- package/dist/cli/dev.external.status.impl.d.ts.map +1 -0
- package/dist/cli/dev.external.status.impl.js +165 -0
- package/dist/cli/dev.external.status.impl.js.map +1 -0
- package/dist/cli/launch/launch.cli-group.descriptor.d.ts +4 -0
- package/dist/cli/launch/launch.cli-group.descriptor.d.ts.map +1 -0
- package/dist/cli/launch/launch.cli-group.descriptor.js +12 -0
- package/dist/cli/launch/launch.cli-group.descriptor.js.map +1 -0
- package/dist/cli/launch/launch.harness.impl.consumer.d.ts +6 -0
- package/dist/cli/launch/launch.harness.impl.consumer.d.ts.map +1 -0
- package/dist/cli/launch/launch.harness.impl.consumer.js +55 -0
- package/dist/cli/launch/launch.harness.impl.consumer.js.map +1 -0
- package/dist/cli/launch/launch.list.descriptor.d.ts +4 -0
- package/dist/cli/launch/launch.list.descriptor.d.ts.map +1 -0
- package/dist/cli/launch/launch.list.descriptor.js +13 -0
- package/dist/cli/launch/launch.list.descriptor.js.map +1 -0
- package/dist/cli/launch/launch.list.impl.consumer.d.ts +7 -0
- package/dist/cli/launch/launch.list.impl.consumer.d.ts.map +1 -0
- package/dist/cli/launch/launch.list.impl.consumer.js +26 -0
- package/dist/cli/launch/launch.list.impl.consumer.js.map +1 -0
- package/dist/cli/launch/launch.logs.descriptor.d.ts +4 -0
- package/dist/cli/launch/launch.logs.descriptor.d.ts.map +1 -0
- package/dist/cli/launch/launch.logs.descriptor.js +30 -0
- package/dist/cli/launch/launch.logs.descriptor.js.map +1 -0
- package/dist/cli/launch/launch.logs.impl.consumer.d.ts +7 -0
- package/dist/cli/launch/launch.logs.impl.consumer.d.ts.map +1 -0
- package/dist/cli/launch/launch.logs.impl.consumer.js +78 -0
- package/dist/cli/launch/launch.logs.impl.consumer.js.map +1 -0
- package/dist/cli/launch/launch.status.descriptor.d.ts +4 -0
- package/dist/cli/launch/launch.status.descriptor.d.ts.map +1 -0
- package/dist/cli/launch/launch.status.descriptor.js +21 -0
- package/dist/cli/launch/launch.status.descriptor.js.map +1 -0
- package/dist/cli/launch/launch.status.impl.consumer.d.ts +7 -0
- package/dist/cli/launch/launch.status.impl.consumer.d.ts.map +1 -0
- package/dist/cli/launch/launch.status.impl.consumer.js +69 -0
- package/dist/cli/launch/launch.status.impl.consumer.js.map +1 -0
- package/dist/cli/plugin.d.ts +4 -0
- package/dist/cli/plugin.d.ts.map +1 -0
- package/dist/cli/plugin.js +76 -0
- package/dist/cli/plugin.js.map +1 -0
- package/dist/cli/runtime-projection.d.ts +34 -0
- package/dist/cli/runtime-projection.d.ts.map +1 -0
- package/dist/cli/runtime-projection.js +272 -0
- package/dist/cli/runtime-projection.js.map +1 -0
- package/dist/cli/stdio.d.ts +15 -0
- package/dist/cli/stdio.d.ts.map +1 -0
- package/dist/cli/stdio.js +15 -0
- package/dist/cli/stdio.js.map +1 -0
- package/dist/cli/who.descriptor.d.ts +16 -0
- package/dist/cli/who.descriptor.d.ts.map +1 -0
- package/dist/cli/who.descriptor.js +38 -0
- package/dist/cli/who.descriptor.js.map +1 -0
- package/dist/cli/who.impl.d.ts +16 -0
- package/dist/cli/who.impl.d.ts.map +1 -0
- package/dist/cli/who.impl.js +127 -0
- package/dist/cli/who.impl.js.map +1 -0
- package/dist/client/external-client.d.ts +423 -0
- package/dist/client/external-client.d.ts.map +1 -0
- package/dist/client/external-client.js +988 -0
- package/dist/client/external-client.js.map +1 -0
- package/dist/client/index.d.ts +9 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +8 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/lifecycle.d.ts +25 -0
- package/dist/client/lifecycle.d.ts.map +1 -0
- package/dist/client/lifecycle.js +134 -0
- package/dist/client/lifecycle.js.map +1 -0
- package/dist/config/binary-detection.d.ts +26 -0
- package/dist/config/binary-detection.d.ts.map +1 -0
- package/dist/config/binary-detection.js +105 -0
- package/dist/config/binary-detection.js.map +1 -0
- package/dist/config/external.config.descriptor.d.ts +11 -0
- package/dist/config/external.config.descriptor.d.ts.map +1 -0
- package/dist/config/external.config.descriptor.js +41 -0
- package/dist/config/external.config.descriptor.js.map +1 -0
- package/dist/config/index.d.ts +10 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +10 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/manifest.d.ts +4696 -0
- package/dist/config/manifest.d.ts.map +1 -0
- package/dist/config/manifest.js +210 -0
- package/dist/config/manifest.js.map +1 -0
- package/dist/config/read.d.ts +13 -0
- package/dist/config/read.d.ts.map +1 -0
- package/dist/config/read.js +20 -0
- package/dist/config/read.js.map +1 -0
- package/dist/consumer.d.ts +15 -0
- package/dist/consumer.d.ts.map +1 -0
- package/dist/consumer.js +15 -0
- package/dist/consumer.js.map +1 -0
- package/dist/detection/base.d.ts +71 -0
- package/dist/detection/base.d.ts.map +1 -0
- package/dist/detection/base.js +115 -0
- package/dist/detection/base.js.map +1 -0
- package/dist/detection/command-probe.d.ts +43 -0
- package/dist/detection/command-probe.d.ts.map +1 -0
- package/dist/detection/command-probe.js +75 -0
- package/dist/detection/command-probe.js.map +1 -0
- package/dist/detection/config-document.d.ts +31 -0
- package/dist/detection/config-document.d.ts.map +1 -0
- package/dist/detection/config-document.js +138 -0
- package/dist/detection/config-document.js.map +1 -0
- package/dist/detection/index.d.ts +11 -0
- package/dist/detection/index.d.ts.map +1 -0
- package/dist/detection/index.js +13 -0
- package/dist/detection/index.js.map +1 -0
- package/dist/detection/manifest-detector-helpers.d.ts +45 -0
- package/dist/detection/manifest-detector-helpers.d.ts.map +1 -0
- package/dist/detection/manifest-detector-helpers.js +198 -0
- package/dist/detection/manifest-detector-helpers.js.map +1 -0
- package/dist/detection/manifest-detector.d.ts +34 -0
- package/dist/detection/manifest-detector.d.ts.map +1 -0
- package/dist/detection/manifest-detector.js +123 -0
- package/dist/detection/manifest-detector.js.map +1 -0
- package/dist/detection/service.d.ts +99 -0
- package/dist/detection/service.d.ts.map +1 -0
- package/dist/detection/service.js +124 -0
- package/dist/detection/service.js.map +1 -0
- package/dist/detection/types.d.ts +113 -0
- package/dist/detection/types.d.ts.map +1 -0
- package/dist/detection/types.js +30 -0
- package/dist/detection/types.js.map +1 -0
- package/dist/dev.external.descriptor.d.ts +9 -0
- package/dist/dev.external.descriptor.d.ts.map +1 -0
- package/dist/dev.external.descriptor.js +17 -0
- package/dist/dev.external.descriptor.js.map +1 -0
- package/dist/docs/external.ambient.docs.descriptor.d.ts +4 -0
- package/dist/docs/external.ambient.docs.descriptor.d.ts.map +1 -0
- package/dist/docs/external.ambient.docs.descriptor.js +109 -0
- package/dist/docs/external.ambient.docs.descriptor.js.map +1 -0
- package/dist/external-adapter/descriptors.d.ts +133 -0
- package/dist/external-adapter/descriptors.d.ts.map +1 -0
- package/dist/external-adapter/descriptors.js +70 -0
- package/dist/external-adapter/descriptors.js.map +1 -0
- package/dist/external-adapter/implementations.d.ts +13 -0
- package/dist/external-adapter/implementations.d.ts.map +1 -0
- package/dist/external-adapter/implementations.js +97 -0
- package/dist/external-adapter/implementations.js.map +1 -0
- package/dist/external-adapter/schemas/adapter.d.ts +66 -0
- package/dist/external-adapter/schemas/adapter.d.ts.map +1 -0
- package/dist/external-adapter/schemas/adapter.js +55 -0
- package/dist/external-adapter/schemas/adapter.js.map +1 -0
- package/dist/external-tool/impl/default-tool.d.ts +201 -0
- package/dist/external-tool/impl/default-tool.d.ts.map +1 -0
- package/dist/external-tool/impl/default-tool.js +243 -0
- package/dist/external-tool/impl/default-tool.js.map +1 -0
- package/dist/external-tool/interfaces/tool.d.ts +102 -0
- package/dist/external-tool/interfaces/tool.d.ts.map +1 -0
- package/dist/external-tool/interfaces/tool.js +2 -0
- package/dist/external-tool/interfaces/tool.js.map +1 -0
- package/dist/external-tool/loader.d.ts +5 -0
- package/dist/external-tool/loader.d.ts.map +1 -0
- package/dist/external-tool/loader.js +8 -0
- package/dist/external-tool/loader.js.map +1 -0
- package/dist/external-tool/path-template.d.ts +54 -0
- package/dist/external-tool/path-template.d.ts.map +1 -0
- package/dist/external-tool/path-template.js +65 -0
- package/dist/external-tool/path-template.js.map +1 -0
- package/dist/external-tool/runtime.d.ts +38 -0
- package/dist/external-tool/runtime.d.ts.map +1 -0
- package/dist/external-tool/runtime.js +89 -0
- package/dist/external-tool/runtime.js.map +1 -0
- package/dist/external-tool/schemas/tool.d.ts +269 -0
- package/dist/external-tool/schemas/tool.d.ts.map +1 -0
- package/dist/external-tool/schemas/tool.js +77 -0
- package/dist/external-tool/schemas/tool.js.map +1 -0
- package/dist/external.plugin.d.ts +4 -0
- package/dist/external.plugin.d.ts.map +1 -0
- package/dist/external.plugin.js +184 -0
- package/dist/external.plugin.js.map +1 -0
- package/dist/harness/agent-host-scope.d.ts +18 -0
- package/dist/harness/agent-host-scope.d.ts.map +1 -0
- package/dist/harness/agent-host-scope.js +30 -0
- package/dist/harness/agent-host-scope.js.map +1 -0
- package/dist/harness/agent-host-settings.d.ts +6 -0
- package/dist/harness/agent-host-settings.d.ts.map +1 -0
- package/dist/harness/agent-host-settings.js +18 -0
- package/dist/harness/agent-host-settings.js.map +1 -0
- package/dist/harness/agent-host.config.descriptor.d.ts +6 -0
- package/dist/harness/agent-host.config.descriptor.d.ts.map +1 -0
- package/dist/harness/agent-host.config.descriptor.js +22 -0
- package/dist/harness/agent-host.config.descriptor.js.map +1 -0
- package/dist/harness/agent-host.d.ts +98 -0
- package/dist/harness/agent-host.d.ts.map +1 -0
- package/dist/harness/agent-host.js +203 -0
- package/dist/harness/agent-host.js.map +1 -0
- package/dist/harness/command-arguments.d.ts +54 -0
- package/dist/harness/command-arguments.d.ts.map +1 -0
- package/dist/harness/command-arguments.js +113 -0
- package/dist/harness/command-arguments.js.map +1 -0
- package/dist/harness/command-execution.d.ts +20 -0
- package/dist/harness/command-execution.d.ts.map +1 -0
- package/dist/harness/command-execution.js +78 -0
- package/dist/harness/command-execution.js.map +1 -0
- package/dist/harness/command-output.d.ts +59 -0
- package/dist/harness/command-output.d.ts.map +1 -0
- package/dist/harness/command-output.js +173 -0
- package/dist/harness/command-output.js.map +1 -0
- package/dist/harness/execution-queue.d.ts +14 -0
- package/dist/harness/execution-queue.d.ts.map +1 -0
- package/dist/harness/execution-queue.js +23 -0
- package/dist/harness/execution-queue.js.map +1 -0
- package/dist/harness/index.d.ts +15 -0
- package/dist/harness/index.d.ts.map +1 -0
- package/dist/harness/index.js +12 -0
- package/dist/harness/index.js.map +1 -0
- package/dist/harness/live-tools.d.ts +130 -0
- package/dist/harness/live-tools.d.ts.map +1 -0
- package/dist/harness/live-tools.js +257 -0
- package/dist/harness/live-tools.js.map +1 -0
- package/dist/harness/managed-block.d.ts +34 -0
- package/dist/harness/managed-block.d.ts.map +1 -0
- package/dist/harness/managed-block.js +66 -0
- package/dist/harness/managed-block.js.map +1 -0
- package/dist/harness/markers.d.ts +22 -0
- package/dist/harness/markers.d.ts.map +1 -0
- package/dist/harness/markers.js +28 -0
- package/dist/harness/markers.js.map +1 -0
- package/dist/harness/node.d.ts +6 -0
- package/dist/harness/node.d.ts.map +1 -0
- package/dist/harness/node.js +6 -0
- package/dist/harness/node.js.map +1 -0
- package/dist/harness/prompt.d.ts +54 -0
- package/dist/harness/prompt.d.ts.map +1 -0
- package/dist/harness/prompt.js +78 -0
- package/dist/harness/prompt.js.map +1 -0
- package/dist/harness/prompts/index.d.ts +113 -0
- package/dist/harness/prompts/index.d.ts.map +1 -0
- package/dist/harness/prompts/index.js +5 -0
- package/dist/harness/prompts/index.js.map +1 -0
- package/dist/harness/prompts/static-briefing-note.prompt.descriptor.d.ts +114 -0
- package/dist/harness/prompts/static-briefing-note.prompt.descriptor.d.ts.map +1 -0
- package/dist/harness/prompts/static-briefing-note.prompt.descriptor.js +17 -0
- package/dist/harness/prompts/static-briefing-note.prompt.descriptor.js.map +1 -0
- package/dist/harness/prompts/system-prompt.prompt.descriptor.d.ts +114 -0
- package/dist/harness/prompts/system-prompt.prompt.descriptor.d.ts.map +1 -0
- package/dist/harness/prompts/system-prompt.prompt.descriptor.js +35 -0
- package/dist/harness/prompts/system-prompt.prompt.descriptor.js.map +1 -0
- package/dist/harness/role-prompt.d.ts +12 -0
- package/dist/harness/role-prompt.d.ts.map +1 -0
- package/dist/harness/role-prompt.js +24 -0
- package/dist/harness/role-prompt.js.map +1 -0
- package/dist/harness/runtime-transport.d.ts +89 -0
- package/dist/harness/runtime-transport.d.ts.map +1 -0
- package/dist/harness/runtime-transport.js +356 -0
- package/dist/harness/runtime-transport.js.map +1 -0
- package/dist/harness/schemas/agent-host.d.ts +146 -0
- package/dist/harness/schemas/agent-host.d.ts.map +1 -0
- package/dist/harness/schemas/agent-host.js +26 -0
- package/dist/harness/schemas/agent-host.js.map +1 -0
- package/dist/harness/stdio-sink.d.ts +35 -0
- package/dist/harness/stdio-sink.d.ts.map +1 -0
- package/dist/harness/stdio-sink.js +34 -0
- package/dist/harness/stdio-sink.js.map +1 -0
- package/dist/harness/strip-undefined.d.ts +9 -0
- package/dist/harness/strip-undefined.d.ts.map +1 -0
- package/dist/harness/strip-undefined.js +11 -0
- package/dist/harness/strip-undefined.js.map +1 -0
- package/dist/harness/tool-projection.d.ts +130 -0
- package/dist/harness/tool-projection.d.ts.map +1 -0
- package/dist/harness/tool-projection.js +195 -0
- package/dist/harness/tool-projection.js.map +1 -0
- package/dist/harness/validation-errors.d.ts +11 -0
- package/dist/harness/validation-errors.d.ts.map +1 -0
- package/dist/harness/validation-errors.js +32 -0
- package/dist/harness/validation-errors.js.map +1 -0
- package/dist/hooks/bounded-payload.d.ts +39 -0
- package/dist/hooks/bounded-payload.d.ts.map +1 -0
- package/dist/hooks/bounded-payload.js +70 -0
- package/dist/hooks/bounded-payload.js.map +1 -0
- package/dist/hooks/bridge.d.ts +34 -0
- package/dist/hooks/bridge.d.ts.map +1 -0
- package/dist/hooks/bridge.js +86 -0
- package/dist/hooks/bridge.js.map +1 -0
- package/dist/hooks/call.d.ts +298 -0
- package/dist/hooks/call.d.ts.map +1 -0
- package/dist/hooks/call.js +1550 -0
- package/dist/hooks/call.js.map +1 -0
- package/dist/hooks/consumer.d.ts +7 -0
- package/dist/hooks/consumer.d.ts.map +1 -0
- package/dist/hooks/consumer.js +5 -0
- package/dist/hooks/consumer.js.map +1 -0
- package/dist/hooks/continuity.d.ts +155 -0
- package/dist/hooks/continuity.d.ts.map +1 -0
- package/dist/hooks/continuity.js +187 -0
- package/dist/hooks/continuity.js.map +1 -0
- package/dist/hooks/dispatch-script.d.ts +27 -0
- package/dist/hooks/dispatch-script.d.ts.map +1 -0
- package/dist/hooks/dispatch-script.js +77 -0
- package/dist/hooks/dispatch-script.js.map +1 -0
- package/dist/hooks/edit-targets.d.ts +23 -0
- package/dist/hooks/edit-targets.d.ts.map +1 -0
- package/dist/hooks/edit-targets.js +74 -0
- package/dist/hooks/edit-targets.js.map +1 -0
- package/dist/hooks/hook-delivery.d.ts +21 -0
- package/dist/hooks/hook-delivery.d.ts.map +1 -0
- package/dist/hooks/hook-delivery.js +87 -0
- package/dist/hooks/hook-delivery.js.map +1 -0
- package/dist/hooks/hook-seat.d.ts +37 -0
- package/dist/hooks/hook-seat.d.ts.map +1 -0
- package/dist/hooks/hook-seat.js +59 -0
- package/dist/hooks/hook-seat.js.map +1 -0
- package/dist/hooks/index.d.ts +9 -0
- package/dist/hooks/index.d.ts.map +1 -0
- package/dist/hooks/index.js +8 -0
- package/dist/hooks/index.js.map +1 -0
- package/dist/hooks/native-edit-guard.d.ts +25 -0
- package/dist/hooks/native-edit-guard.d.ts.map +1 -0
- package/dist/hooks/native-edit-guard.js +33 -0
- package/dist/hooks/native-edit-guard.js.map +1 -0
- package/dist/hooks/native-edit-locks.d.ts +54 -0
- package/dist/hooks/native-edit-locks.d.ts.map +1 -0
- package/dist/hooks/native-edit-locks.js +98 -0
- package/dist/hooks/native-edit-locks.js.map +1 -0
- package/dist/hooks/resolve-event.d.ts +13 -0
- package/dist/hooks/resolve-event.d.ts.map +1 -0
- package/dist/hooks/resolve-event.js +32 -0
- package/dist/hooks/resolve-event.js.map +1 -0
- package/dist/hooks/review-state.d.ts +22 -0
- package/dist/hooks/review-state.d.ts.map +1 -0
- package/dist/hooks/review-state.js +57 -0
- package/dist/hooks/review-state.js.map +1 -0
- package/dist/hooks/runtime.d.ts +146 -0
- package/dist/hooks/runtime.d.ts.map +1 -0
- package/dist/hooks/runtime.js +83 -0
- package/dist/hooks/runtime.js.map +1 -0
- package/dist/hooks/self-review.d.ts +14 -0
- package/dist/hooks/self-review.d.ts.map +1 -0
- package/dist/hooks/self-review.js +28 -0
- package/dist/hooks/self-review.js.map +1 -0
- package/dist/index.d.ts +41 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +33 -0
- package/dist/index.js.map +1 -0
- package/dist/ingest/logging/subagent-completions.output.descriptor.d.ts +4 -0
- package/dist/ingest/logging/subagent-completions.output.descriptor.d.ts.map +1 -0
- package/dist/ingest/logging/subagent-completions.output.descriptor.js +22 -0
- package/dist/ingest/logging/subagent-completions.output.descriptor.js.map +1 -0
- package/dist/ingest/logging/success-patterns.output.descriptor.d.ts +4 -0
- package/dist/ingest/logging/success-patterns.output.descriptor.d.ts.map +1 -0
- package/dist/ingest/logging/success-patterns.output.descriptor.js +22 -0
- package/dist/ingest/logging/success-patterns.output.descriptor.js.map +1 -0
- package/dist/ingest/logging/tool-use.output.descriptor.d.ts +4 -0
- package/dist/ingest/logging/tool-use.output.descriptor.d.ts.map +1 -0
- package/dist/ingest/logging/tool-use.output.descriptor.js +22 -0
- package/dist/ingest/logging/tool-use.output.descriptor.js.map +1 -0
- package/dist/ingest/resolve-log-paths.d.ts +16 -0
- package/dist/ingest/resolve-log-paths.d.ts.map +1 -0
- package/dist/ingest/resolve-log-paths.js +43 -0
- package/dist/ingest/resolve-log-paths.js.map +1 -0
- package/dist/ingest/schemas.d.ts +43 -0
- package/dist/ingest/schemas.d.ts.map +1 -0
- package/dist/ingest/schemas.js +46 -0
- package/dist/ingest/schemas.js.map +1 -0
- package/dist/jsonrpc/client.d.ts +129 -0
- package/dist/jsonrpc/client.d.ts.map +1 -0
- package/dist/jsonrpc/client.js +553 -0
- package/dist/jsonrpc/client.js.map +1 -0
- package/dist/jsonrpc/envelope.d.ts +37 -0
- package/dist/jsonrpc/envelope.d.ts.map +1 -0
- package/dist/jsonrpc/envelope.js +46 -0
- package/dist/jsonrpc/envelope.js.map +1 -0
- package/dist/jsonrpc/errors.d.ts +28 -0
- package/dist/jsonrpc/errors.d.ts.map +1 -0
- package/dist/jsonrpc/errors.js +110 -0
- package/dist/jsonrpc/errors.js.map +1 -0
- package/dist/jsonrpc/framing.d.ts +35 -0
- package/dist/jsonrpc/framing.d.ts.map +1 -0
- package/dist/jsonrpc/framing.js +134 -0
- package/dist/jsonrpc/framing.js.map +1 -0
- package/dist/jsonrpc/index.d.ts +11 -0
- package/dist/jsonrpc/index.d.ts.map +1 -0
- package/dist/jsonrpc/index.js +11 -0
- package/dist/jsonrpc/index.js.map +1 -0
- package/dist/jsonrpc/outcome.d.ts +25 -0
- package/dist/jsonrpc/outcome.d.ts.map +1 -0
- package/dist/jsonrpc/outcome.js +32 -0
- package/dist/jsonrpc/outcome.js.map +1 -0
- package/dist/jsonrpc/queue.d.ts +30 -0
- package/dist/jsonrpc/queue.d.ts.map +1 -0
- package/dist/jsonrpc/queue.js +113 -0
- package/dist/jsonrpc/queue.js.map +1 -0
- package/dist/jsonrpc/schemas.d.ts +74 -0
- package/dist/jsonrpc/schemas.d.ts.map +1 -0
- package/dist/jsonrpc/schemas.js +65 -0
- package/dist/jsonrpc/schemas.js.map +1 -0
- package/dist/launch/consumer/execute.d.ts +5 -0
- package/dist/launch/consumer/execute.d.ts.map +1 -0
- package/dist/launch/consumer/execute.js +358 -0
- package/dist/launch/consumer/execute.js.map +1 -0
- package/dist/launch/consumer/paths.d.ts +15 -0
- package/dist/launch/consumer/paths.d.ts.map +1 -0
- package/dist/launch/consumer/paths.js +18 -0
- package/dist/launch/consumer/paths.js.map +1 -0
- package/dist/launch/consumer/prepare.d.ts +25 -0
- package/dist/launch/consumer/prepare.d.ts.map +1 -0
- package/dist/launch/consumer/prepare.js +158 -0
- package/dist/launch/consumer/prepare.js.map +1 -0
- package/dist/launch/consumer/prompt.d.ts +18 -0
- package/dist/launch/consumer/prompt.d.ts.map +1 -0
- package/dist/launch/consumer/prompt.js +45 -0
- package/dist/launch/consumer/prompt.js.map +1 -0
- package/dist/launch/consumer/rotate-logs.d.ts +23 -0
- package/dist/launch/consumer/rotate-logs.d.ts.map +1 -0
- package/dist/launch/consumer/rotate-logs.js +43 -0
- package/dist/launch/consumer/rotate-logs.js.map +1 -0
- package/dist/launch/consumer/spawn.d.ts +38 -0
- package/dist/launch/consumer/spawn.d.ts.map +1 -0
- package/dist/launch/consumer/spawn.js +82 -0
- package/dist/launch/consumer/spawn.js.map +1 -0
- package/dist/launch/consumer/state-rows.d.ts +41 -0
- package/dist/launch/consumer/state-rows.d.ts.map +1 -0
- package/dist/launch/consumer/state-rows.js +78 -0
- package/dist/launch/consumer/state-rows.js.map +1 -0
- package/dist/launch/consumer/store.d.ts +139 -0
- package/dist/launch/consumer/store.d.ts.map +1 -0
- package/dist/launch/consumer/store.js +495 -0
- package/dist/launch/consumer/store.js.map +1 -0
- package/dist/launch/index.d.ts +5 -0
- package/dist/launch/index.d.ts.map +1 -0
- package/dist/launch/index.js +3 -0
- package/dist/launch/index.js.map +1 -0
- package/dist/launch/resolve.d.ts +9 -0
- package/dist/launch/resolve.d.ts.map +1 -0
- package/dist/launch/resolve.js +139 -0
- package/dist/launch/resolve.js.map +1 -0
- package/dist/launch/schemas.d.ts +289 -0
- package/dist/launch/schemas.d.ts.map +1 -0
- package/dist/launch/schemas.js +154 -0
- package/dist/launch/schemas.js.map +1 -0
- package/dist/launch/task-executor/external-harness.descriptor.d.ts +6 -0
- package/dist/launch/task-executor/external-harness.descriptor.d.ts.map +1 -0
- package/dist/launch/task-executor/external-harness.descriptor.js +13 -0
- package/dist/launch/task-executor/external-harness.descriptor.js.map +1 -0
- package/dist/launch/task-executor/external-harness.impl.consumer.d.ts +17 -0
- package/dist/launch/task-executor/external-harness.impl.consumer.d.ts.map +1 -0
- package/dist/launch/task-executor/external-harness.impl.consumer.js +151 -0
- package/dist/launch/task-executor/external-harness.impl.consumer.js.map +1 -0
- package/dist/launch/task-executor/isolated-workspace.d.ts +3 -0
- package/dist/launch/task-executor/isolated-workspace.d.ts.map +1 -0
- package/dist/launch/task-executor/isolated-workspace.js +3 -0
- package/dist/launch/task-executor/isolated-workspace.js.map +1 -0
- package/dist/launch/task-executor/session-locks.d.ts +28 -0
- package/dist/launch/task-executor/session-locks.d.ts.map +1 -0
- package/dist/launch/task-executor/session-locks.js +50 -0
- package/dist/launch/task-executor/session-locks.js.map +1 -0
- package/dist/pack/adapter-codegen.d.ts +38 -0
- package/dist/pack/adapter-codegen.d.ts.map +1 -0
- package/dist/pack/adapter-codegen.js +70 -0
- package/dist/pack/adapter-codegen.js.map +1 -0
- package/dist/pack/build.d.ts +48 -0
- package/dist/pack/build.d.ts.map +1 -0
- package/dist/pack/build.js +171 -0
- package/dist/pack/build.js.map +1 -0
- package/dist/pack/emitters/hooks.d.ts +14 -0
- package/dist/pack/emitters/hooks.d.ts.map +1 -0
- package/dist/pack/emitters/hooks.js +15 -0
- package/dist/pack/emitters/hooks.js.map +1 -0
- package/dist/pack/emitters/json.d.ts +10 -0
- package/dist/pack/emitters/json.d.ts.map +1 -0
- package/dist/pack/emitters/json.js +17 -0
- package/dist/pack/emitters/json.js.map +1 -0
- package/dist/pack/emitters/registry.d.ts +22 -0
- package/dist/pack/emitters/registry.d.ts.map +1 -0
- package/dist/pack/emitters/registry.js +38 -0
- package/dist/pack/emitters/registry.js.map +1 -0
- package/dist/pack/emitters/toml.d.ts +8 -0
- package/dist/pack/emitters/toml.d.ts.map +1 -0
- package/dist/pack/emitters/toml.js +28 -0
- package/dist/pack/emitters/toml.js.map +1 -0
- package/dist/pack/emitters/yaml.d.ts +8 -0
- package/dist/pack/emitters/yaml.d.ts.map +1 -0
- package/dist/pack/emitters/yaml.js +27 -0
- package/dist/pack/emitters/yaml.js.map +1 -0
- package/dist/pack/index.d.ts +11 -0
- package/dist/pack/index.d.ts.map +1 -0
- package/dist/pack/index.js +11 -0
- package/dist/pack/index.js.map +1 -0
- package/dist/pack/mcp.d.ts +28 -0
- package/dist/pack/mcp.d.ts.map +1 -0
- package/dist/pack/mcp.js +70 -0
- package/dist/pack/mcp.js.map +1 -0
- package/dist/pack/persist.d.ts +28 -0
- package/dist/pack/persist.d.ts.map +1 -0
- package/dist/pack/persist.js +35 -0
- package/dist/pack/persist.js.map +1 -0
- package/dist/pack/schema.d.ts +126 -0
- package/dist/pack/schema.d.ts.map +1 -0
- package/dist/pack/schema.js +56 -0
- package/dist/pack/schema.js.map +1 -0
- package/dist/plugins/index.d.ts +512 -0
- package/dist/plugins/index.d.ts.map +1 -0
- package/dist/plugins/index.js +25 -0
- package/dist/plugins/index.js.map +1 -0
- package/dist/plugins/installers/config-file.d.ts +21 -0
- package/dist/plugins/installers/config-file.d.ts.map +1 -0
- package/dist/plugins/installers/config-file.js +48 -0
- package/dist/plugins/installers/config-file.js.map +1 -0
- package/dist/plugins/installers/config-value.d.ts +35 -0
- package/dist/plugins/installers/config-value.d.ts.map +1 -0
- package/dist/plugins/installers/config-value.js +52 -0
- package/dist/plugins/installers/config-value.js.map +1 -0
- package/dist/plugins/installers/hooks/index.d.ts +9 -0
- package/dist/plugins/installers/hooks/index.d.ts.map +1 -0
- package/dist/plugins/installers/hooks/index.js +12 -0
- package/dist/plugins/installers/hooks/index.js.map +1 -0
- package/dist/plugins/installers/hooks/scoped-paths.d.ts +24 -0
- package/dist/plugins/installers/hooks/scoped-paths.d.ts.map +1 -0
- package/dist/plugins/installers/hooks/scoped-paths.js +80 -0
- package/dist/plugins/installers/hooks/scoped-paths.js.map +1 -0
- package/dist/plugins/installers/hooks/settings-registration.d.ts +79 -0
- package/dist/plugins/installers/hooks/settings-registration.d.ts.map +1 -0
- package/dist/plugins/installers/hooks/settings-registration.js +125 -0
- package/dist/plugins/installers/hooks/settings-registration.js.map +1 -0
- package/dist/plugins/installers/hooks/shell-installer.d.ts +21 -0
- package/dist/plugins/installers/hooks/shell-installer.d.ts.map +1 -0
- package/dist/plugins/installers/hooks/shell-installer.js +298 -0
- package/dist/plugins/installers/hooks/shell-installer.js.map +1 -0
- package/dist/plugins/installers/hooks/uninstall-backup.d.ts +19 -0
- package/dist/plugins/installers/hooks/uninstall-backup.d.ts.map +1 -0
- package/dist/plugins/installers/hooks/uninstall-backup.js +20 -0
- package/dist/plugins/installers/hooks/uninstall-backup.js.map +1 -0
- package/dist/plugins/installers/index.d.ts +58 -0
- package/dist/plugins/installers/index.d.ts.map +1 -0
- package/dist/plugins/installers/index.js +303 -0
- package/dist/plugins/installers/index.js.map +1 -0
- package/dist/plugins/installers/mcp/environment.d.ts +180 -0
- package/dist/plugins/installers/mcp/environment.d.ts.map +1 -0
- package/dist/plugins/installers/mcp/environment.js +314 -0
- package/dist/plugins/installers/mcp/environment.js.map +1 -0
- package/dist/plugins/installers/mcp/index.d.ts +9 -0
- package/dist/plugins/installers/mcp/index.d.ts.map +1 -0
- package/dist/plugins/installers/mcp/index.js +11 -0
- package/dist/plugins/installers/mcp/index.js.map +1 -0
- package/dist/plugins/installers/mcp/json-installer.d.ts +18 -0
- package/dist/plugins/installers/mcp/json-installer.d.ts.map +1 -0
- package/dist/plugins/installers/mcp/json-installer.js +155 -0
- package/dist/plugins/installers/mcp/json-installer.js.map +1 -0
- package/dist/plugins/installers/mcp/json-paths.d.ts +13 -0
- package/dist/plugins/installers/mcp/json-paths.d.ts.map +1 -0
- package/dist/plugins/installers/mcp/json-paths.js +130 -0
- package/dist/plugins/installers/mcp/json-paths.js.map +1 -0
- package/dist/plugins/installers/mcp/serve-identity.d.ts +19 -0
- package/dist/plugins/installers/mcp/serve-identity.d.ts.map +1 -0
- package/dist/plugins/installers/mcp/serve-identity.js +31 -0
- package/dist/plugins/installers/mcp/serve-identity.js.map +1 -0
- package/dist/plugins/installers/mcp/server-config.d.ts +20 -0
- package/dist/plugins/installers/mcp/server-config.d.ts.map +1 -0
- package/dist/plugins/installers/mcp/server-config.js +64 -0
- package/dist/plugins/installers/mcp/server-config.js.map +1 -0
- package/dist/plugins/installers/mcp/toml-installer.d.ts +41 -0
- package/dist/plugins/installers/mcp/toml-installer.d.ts.map +1 -0
- package/dist/plugins/installers/mcp/toml-installer.js +407 -0
- package/dist/plugins/installers/mcp/toml-installer.js.map +1 -0
- package/dist/plugins/installers/mcp/yaml-installer.d.ts +22 -0
- package/dist/plugins/installers/mcp/yaml-installer.d.ts.map +1 -0
- package/dist/plugins/installers/mcp/yaml-installer.js +156 -0
- package/dist/plugins/installers/mcp/yaml-installer.js.map +1 -0
- package/dist/plugins/installers/runtime.d.ts +42 -0
- package/dist/plugins/installers/runtime.d.ts.map +1 -0
- package/dist/plugins/installers/runtime.js +56 -0
- package/dist/plugins/installers/runtime.js.map +1 -0
- package/dist/plugins/installers/types.d.ts +306 -0
- package/dist/plugins/installers/types.d.ts.map +1 -0
- package/dist/plugins/installers/types.js +40 -0
- package/dist/plugins/installers/types.js.map +1 -0
- package/dist/plugins/schemas/index.d.ts +6 -0
- package/dist/plugins/schemas/index.d.ts.map +1 -0
- package/dist/plugins/schemas/index.js +5 -0
- package/dist/plugins/schemas/index.js.map +1 -0
- package/dist/plugins/schemas/manifest.d.ts +527 -0
- package/dist/plugins/schemas/manifest.d.ts.map +1 -0
- package/dist/plugins/schemas/manifest.js +355 -0
- package/dist/plugins/schemas/manifest.js.map +1 -0
- package/dist/plugins/tools/index.d.ts +12 -0
- package/dist/plugins/tools/index.d.ts.map +1 -0
- package/dist/plugins/tools/index.js +19 -0
- package/dist/plugins/tools/index.js.map +1 -0
- package/dist/presets/executor.d.ts +20 -0
- package/dist/presets/executor.d.ts.map +1 -0
- package/dist/presets/executor.js +629 -0
- package/dist/presets/executor.js.map +1 -0
- package/dist/presets/file-backup.d.ts +66 -0
- package/dist/presets/file-backup.d.ts.map +1 -0
- package/dist/presets/file-backup.js +108 -0
- package/dist/presets/file-backup.js.map +1 -0
- package/dist/presets/index.d.ts +11 -0
- package/dist/presets/index.d.ts.map +1 -0
- package/dist/presets/index.js +10 -0
- package/dist/presets/index.js.map +1 -0
- package/dist/presets/resolve.d.ts +32 -0
- package/dist/presets/resolve.d.ts.map +1 -0
- package/dist/presets/resolve.js +49 -0
- package/dist/presets/resolve.js.map +1 -0
- package/dist/presets/runtime.d.ts +21 -0
- package/dist/presets/runtime.d.ts.map +1 -0
- package/dist/presets/runtime.js +115 -0
- package/dist/presets/runtime.js.map +1 -0
- package/dist/projection/attestation.d.ts +43 -0
- package/dist/projection/attestation.d.ts.map +1 -0
- package/dist/projection/attestation.js +59 -0
- package/dist/projection/attestation.js.map +1 -0
- package/dist/runtime/agent-roster-provider.d.ts +75 -0
- package/dist/runtime/agent-roster-provider.d.ts.map +1 -0
- package/dist/runtime/agent-roster-provider.js +30 -0
- package/dist/runtime/agent-roster-provider.js.map +1 -0
- package/dist/runtime/external-surface-projector.d.ts +37 -0
- package/dist/runtime/external-surface-projector.d.ts.map +1 -0
- package/dist/runtime/external-surface-projector.js +21 -0
- package/dist/runtime/external-surface-projector.js.map +1 -0
- package/dist/runtime/lifecycle-task.d.ts +54 -0
- package/dist/runtime/lifecycle-task.d.ts.map +1 -0
- package/dist/runtime/lifecycle-task.js +111 -0
- package/dist/runtime/lifecycle-task.js.map +1 -0
- package/dist/runtime.d.ts +496 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +168 -0
- package/dist/runtime.js.map +1 -0
- package/dist/schemas/agent-message.d.ts +136 -0
- package/dist/schemas/agent-message.d.ts.map +1 -0
- package/dist/schemas/agent-message.js +131 -0
- package/dist/schemas/agent-message.js.map +1 -0
- package/dist/schemas/agent-roster.d.ts +50 -0
- package/dist/schemas/agent-roster.d.ts.map +1 -0
- package/dist/schemas/agent-roster.js +52 -0
- package/dist/schemas/agent-roster.js.map +1 -0
- package/dist/schemas/collision.d.ts +109 -0
- package/dist/schemas/collision.d.ts.map +1 -0
- package/dist/schemas/collision.js +71 -0
- package/dist/schemas/collision.js.map +1 -0
- package/dist/schemas/context-summary.d.ts +225 -0
- package/dist/schemas/context-summary.d.ts.map +1 -0
- package/dist/schemas/context-summary.js +117 -0
- package/dist/schemas/context-summary.js.map +1 -0
- package/dist/schemas/file-access.d.ts +176 -0
- package/dist/schemas/file-access.d.ts.map +1 -0
- package/dist/schemas/file-access.js +139 -0
- package/dist/schemas/file-access.js.map +1 -0
- package/dist/schemas/hooks.d.ts +170 -0
- package/dist/schemas/hooks.d.ts.map +1 -0
- package/dist/schemas/hooks.js +171 -0
- package/dist/schemas/hooks.js.map +1 -0
- package/dist/schemas/index.d.ts +19 -0
- package/dist/schemas/index.d.ts.map +1 -0
- package/dist/schemas/index.js +17 -0
- package/dist/schemas/index.js.map +1 -0
- package/dist/schemas/path-identity.d.ts +44 -0
- package/dist/schemas/path-identity.d.ts.map +1 -0
- package/dist/schemas/path-identity.js +93 -0
- package/dist/schemas/path-identity.js.map +1 -0
- package/dist/schemas/pending-notice.d.ts +13 -0
- package/dist/schemas/pending-notice.d.ts.map +1 -0
- package/dist/schemas/pending-notice.js +5 -0
- package/dist/schemas/pending-notice.js.map +1 -0
- package/dist/schemas/presets.d.ts +1239 -0
- package/dist/schemas/presets.d.ts.map +1 -0
- package/dist/schemas/presets.js +270 -0
- package/dist/schemas/presets.js.map +1 -0
- package/dist/schemas/roster.d.ts +96 -0
- package/dist/schemas/roster.d.ts.map +1 -0
- package/dist/schemas/roster.js +310 -0
- package/dist/schemas/roster.js.map +1 -0
- package/dist/schemas/tiers.d.ts +95 -0
- package/dist/schemas/tiers.d.ts.map +1 -0
- package/dist/schemas/tiers.js +143 -0
- package/dist/schemas/tiers.js.map +1 -0
- package/dist/schemas/touch-identity.d.ts +25 -0
- package/dist/schemas/touch-identity.d.ts.map +1 -0
- package/dist/schemas/touch-identity.js +16 -0
- package/dist/schemas/touch-identity.js.map +1 -0
- package/dist/schemas/touch-projection.d.ts +157 -0
- package/dist/schemas/touch-projection.d.ts.map +1 -0
- package/dist/schemas/touch-projection.js +100 -0
- package/dist/schemas/touch-projection.js.map +1 -0
- package/dist/session-capability.d.ts +88 -0
- package/dist/session-capability.d.ts.map +1 -0
- package/dist/session-capability.js +28 -0
- package/dist/session-capability.js.map +1 -0
- package/dist/setup/index.d.ts +9 -0
- package/dist/setup/index.d.ts.map +1 -0
- package/dist/setup/index.js +8 -0
- package/dist/setup/index.js.map +1 -0
- package/dist/setup/steps/detect-tools.d.ts +9 -0
- package/dist/setup/steps/detect-tools.d.ts.map +1 -0
- package/dist/setup/steps/detect-tools.js +120 -0
- package/dist/setup/steps/detect-tools.js.map +1 -0
- package/dist/setup/steps/index.d.ts +11 -0
- package/dist/setup/steps/index.d.ts.map +1 -0
- package/dist/setup/steps/index.js +11 -0
- package/dist/setup/steps/index.js.map +1 -0
- package/dist/setup/steps/install-helpers.d.ts +9 -0
- package/dist/setup/steps/install-helpers.d.ts.map +1 -0
- package/dist/setup/steps/install-helpers.js +29 -0
- package/dist/setup/steps/install-helpers.js.map +1 -0
- package/dist/setup/steps/install.d.ts +11 -0
- package/dist/setup/steps/install.d.ts.map +1 -0
- package/dist/setup/steps/install.js +143 -0
- package/dist/setup/steps/install.js.map +1 -0
- package/dist/setup/steps/select-location.d.ts +8 -0
- package/dist/setup/steps/select-location.d.ts.map +1 -0
- package/dist/setup/steps/select-location.js +55 -0
- package/dist/setup/steps/select-location.js.map +1 -0
- package/dist/setup/steps/select-targets.d.ts +8 -0
- package/dist/setup/steps/select-targets.d.ts.map +1 -0
- package/dist/setup/steps/select-targets.js +53 -0
- package/dist/setup/steps/select-targets.js.map +1 -0
- package/dist/setup/steps/select-tiers.d.ts +8 -0
- package/dist/setup/steps/select-tiers.d.ts.map +1 -0
- package/dist/setup/steps/select-tiers.js +124 -0
- package/dist/setup/steps/select-tiers.js.map +1 -0
- package/dist/setup/types.d.ts +128 -0
- package/dist/setup/types.d.ts.map +1 -0
- package/dist/setup/types.js +28 -0
- package/dist/setup/types.js.map +1 -0
- package/dist/setup/wizard.d.ts +25 -0
- package/dist/setup/wizard.d.ts.map +1 -0
- package/dist/setup/wizard.js +182 -0
- package/dist/setup/wizard.js.map +1 -0
- package/dist/storage/agent-message.record.descriptor.d.ts +43 -0
- package/dist/storage/agent-message.record.descriptor.d.ts.map +1 -0
- package/dist/storage/agent-message.record.descriptor.js +72 -0
- package/dist/storage/agent-message.record.descriptor.js.map +1 -0
- package/dist/storage/file-access.record.descriptor.d.ts +104 -0
- package/dist/storage/file-access.record.descriptor.d.ts.map +1 -0
- package/dist/storage/file-access.record.descriptor.js +370 -0
- package/dist/storage/file-access.record.descriptor.js.map +1 -0
- package/dist/storage/touch-identity.d.ts +43 -0
- package/dist/storage/touch-identity.d.ts.map +1 -0
- package/dist/storage/touch-identity.js +155 -0
- package/dist/storage/touch-identity.js.map +1 -0
- package/dist/svelte/external-svelte.plugin.d.ts +39 -0
- package/dist/svelte/external-svelte.plugin.d.ts.map +1 -0
- package/dist/svelte/external-svelte.plugin.js +25 -0
- package/dist/svelte/index.d.ts +11 -0
- package/dist/svelte/index.d.ts.map +1 -0
- package/dist/svelte/index.js +10 -0
- package/dist/svelte/styles.css +8 -0
- package/dist/svelte/tool-widgets/ExternalToolCard.svelte +71 -0
- package/dist/svelte/tool-widgets/ExternalToolCard.svelte.d.ts +5 -0
- package/dist/svelte/tool-widgets/ExternalToolCard.svelte.d.ts.map +1 -0
- package/dist/svelte/tool-widgets/index.d.ts +3 -0
- package/dist/svelte/tool-widgets/index.d.ts.map +1 -0
- package/dist/svelte/tool-widgets/index.js +2 -0
- package/dist/svelte/tool-widgets/summary.d.ts +27 -0
- package/dist/svelte/tool-widgets/summary.d.ts.map +1 -0
- package/dist/svelte/tool-widgets/summary.js +198 -0
- package/dist/tools/index.d.ts +3 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +2 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/unified/action-descriptors.d.ts +185 -0
- package/dist/tools/unified/action-descriptors.d.ts.map +1 -0
- package/dist/tools/unified/action-descriptors.js +10 -0
- package/dist/tools/unified/action-descriptors.js.map +1 -0
- package/dist/tools/unified/action-input.d.ts +7 -0
- package/dist/tools/unified/action-input.d.ts.map +1 -0
- package/dist/tools/unified/action-input.js +13 -0
- package/dist/tools/unified/action-input.js.map +1 -0
- package/dist/tools/unified/actions.d.ts +10 -0
- package/dist/tools/unified/actions.d.ts.map +1 -0
- package/dist/tools/unified/actions.js +26 -0
- package/dist/tools/unified/actions.js.map +1 -0
- package/dist/tools/unified/external.descriptor.d.ts +30 -0
- package/dist/tools/unified/external.descriptor.d.ts.map +1 -0
- package/dist/tools/unified/external.descriptor.js +27 -0
- package/dist/tools/unified/external.descriptor.js.map +1 -0
- package/dist/tools/unified/external.impl.consumer.d.ts +8 -0
- package/dist/tools/unified/external.impl.consumer.d.ts.map +1 -0
- package/dist/tools/unified/external.impl.consumer.js +38 -0
- package/dist/tools/unified/external.impl.consumer.js.map +1 -0
- package/dist/tools/unified/handlers/context-summary.d.ts +4 -0
- package/dist/tools/unified/handlers/context-summary.d.ts.map +1 -0
- package/dist/tools/unified/handlers/context-summary.js +271 -0
- package/dist/tools/unified/handlers/context-summary.js.map +1 -0
- package/dist/tools/unified/handlers/file-access-list.d.ts +4 -0
- package/dist/tools/unified/handlers/file-access-list.d.ts.map +1 -0
- package/dist/tools/unified/handlers/file-access-list.js +39 -0
- package/dist/tools/unified/handlers/file-access-list.js.map +1 -0
- package/dist/tools/unified/handlers/file-access-track.d.ts +4 -0
- package/dist/tools/unified/handlers/file-access-track.d.ts.map +1 -0
- package/dist/tools/unified/handlers/file-access-track.js +34 -0
- package/dist/tools/unified/handlers/file-access-track.js.map +1 -0
- package/dist/tools/unified/handlers/hook-call.d.ts +23 -0
- package/dist/tools/unified/handlers/hook-call.d.ts.map +1 -0
- package/dist/tools/unified/handlers/hook-call.js +86 -0
- package/dist/tools/unified/handlers/hook-call.js.map +1 -0
- package/dist/tools/unified/handlers/hook-generator.d.ts +4 -0
- package/dist/tools/unified/handlers/hook-generator.d.ts.map +1 -0
- package/dist/tools/unified/handlers/hook-generator.js +37 -0
- package/dist/tools/unified/handlers/hook-generator.js.map +1 -0
- package/dist/tools/unified/handlers/signal.d.ts +22 -0
- package/dist/tools/unified/handlers/signal.d.ts.map +1 -0
- package/dist/tools/unified/handlers/signal.js +114 -0
- package/dist/tools/unified/handlers/signal.js.map +1 -0
- package/dist/tools/unified/index.d.ts +6 -0
- package/dist/tools/unified/index.d.ts.map +1 -0
- package/dist/tools/unified/index.js +6 -0
- package/dist/tools/unified/index.js.map +1 -0
- package/dist/tools/unified/install/handler.d.ts +4 -0
- package/dist/tools/unified/install/handler.d.ts.map +1 -0
- package/dist/tools/unified/install/handler.js +17 -0
- package/dist/tools/unified/install/handler.js.map +1 -0
- package/dist/tools/unified/runtime.d.ts +35 -0
- package/dist/tools/unified/runtime.d.ts.map +1 -0
- package/dist/tools/unified/runtime.js +23 -0
- package/dist/tools/unified/runtime.js.map +1 -0
- package/dist/tools/unified/schemas.d.ts +160 -0
- package/dist/tools/unified/schemas.d.ts.map +1 -0
- package/dist/tools/unified/schemas.js +139 -0
- package/dist/tools/unified/schemas.js.map +1 -0
- package/dist/utils/fs-absence.d.ts +22 -0
- package/dist/utils/fs-absence.d.ts.map +1 -0
- package/dist/utils/fs-absence.js +37 -0
- package/dist/utils/fs-absence.js.map +1 -0
- package/dist/utils/runtime-fs.d.ts +37 -0
- package/dist/utils/runtime-fs.d.ts.map +1 -0
- package/dist/utils/runtime-fs.js +100 -0
- package/dist/utils/runtime-fs.js.map +1 -0
- package/dist/utils/toml.d.ts +26 -0
- package/dist/utils/toml.d.ts.map +1 -0
- package/dist/utils/toml.js +415 -0
- package/dist/utils/toml.js.map +1 -0
- package/package.json +263 -4
|
@@ -0,0 +1,1550 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unified Hook Call Handler
|
|
3
|
+
*
|
|
4
|
+
* Single entry point for external tool hook events.
|
|
5
|
+
* Delegates to event-specific handlers and assembles prompt-based guidance.
|
|
6
|
+
*
|
|
7
|
+
* Usage: vibes dev external hooks call <event> [options]
|
|
8
|
+
*
|
|
9
|
+
* ## Hook Event Reference
|
|
10
|
+
*
|
|
11
|
+
* | Event | When | Input Available | Can Inject Context |
|
|
12
|
+
* |-------------------|-----------------------------------|------------------------|--------------------|
|
|
13
|
+
* | SessionStart | Session begins (inc. post-compact)| - | Yes |
|
|
14
|
+
* | SessionEnd | Session ends | - | No |
|
|
15
|
+
* | UserPromptSubmit | Before processing user message | PROMPT env var | Yes |
|
|
16
|
+
* | PreToolUse | Before tool execution | tool info (stdin) | Yes (can block) |
|
|
17
|
+
* | PostToolUse | After tool execution | tool result (stdin) | Yes |
|
|
18
|
+
* | PermissionRequest | User prompted for permission | permission info | Yes |
|
|
19
|
+
* | PreCompact | Before context compaction | - | Yes |
|
|
20
|
+
* | Stop | Tool stops (success/error) | stop reason | No |
|
|
21
|
+
* | SubagentStop | Task/subagent completes | subagent result | Yes |
|
|
22
|
+
* | Notification | Tool sends notification | notification content | No |
|
|
23
|
+
*/
|
|
24
|
+
import { JsonValueSchema } from '@vibesdotdev/kernel/schemas/json';
|
|
25
|
+
import { CapabilityError, CaughtFailureSchema, isCapabilityError, valueFreeFailure } from '@vibesdotdev/kernel';
|
|
26
|
+
import { join } from '@vibesdotdev/filesystem';
|
|
27
|
+
import { redactedExcerpt } from '@vibesdotdev/shared/utils';
|
|
28
|
+
import { externalFileSystem, readTextIfPresent } from "../utils/runtime-fs.js";
|
|
29
|
+
import { boundToolPayload } from "./bounded-payload.js";
|
|
30
|
+
import { getPathRoots } from '@vibesdotdev/paths';
|
|
31
|
+
import { getLogger } from '@vibesdotdev/logging';
|
|
32
|
+
import { resolveExternalSessionId } from "../session-capability.js";
|
|
33
|
+
import { invokeTool } from '@vibesdotdev/tools/execution/invoke';
|
|
34
|
+
import * as z from 'zod/v4';
|
|
35
|
+
import { runHookToolBridge } from "./bridge.js";
|
|
36
|
+
import { tryGetVibesClient } from '@vibesdotdev/kernel/client';
|
|
37
|
+
import { hookWorkspaceRoot, resolveHookSeat } from "./hook-seat.js";
|
|
38
|
+
import { readCollaborationSettings } from "../ambient/collaboration-settings.js";
|
|
39
|
+
import { archiveExternalOwnedSessions, bindExternalOwnedSession, renderExternalContinuityContext } from "./continuity.js";
|
|
40
|
+
import { ExternalHookEnvelopeSchema } from "./runtime.js";
|
|
41
|
+
import { guardNativeEdit } from "./native-edit-guard.js";
|
|
42
|
+
import { nativeEditTargets } from "./edit-targets.js";
|
|
43
|
+
import { collaborationContext } from "./hook-delivery.js";
|
|
44
|
+
import { formatRosterLine } from "../schemas/roster.js";
|
|
45
|
+
import { resolveLocalMcpUrlForRuntime } from "../plugins/installers/mcp/environment.js";
|
|
46
|
+
import { HookEventSchema, HookTargetSchema, VibesHooksConfigSchema, DEFAULT_HOOKS_CONFIG, builtInPromptFor } from "../schemas/hooks.js";
|
|
47
|
+
// Configuration Loading
|
|
48
|
+
/**
|
|
49
|
+
* Load hooks configuration from file or environment
|
|
50
|
+
* Priority: CLI options > env var > config file > defaults
|
|
51
|
+
*/
|
|
52
|
+
function hooksConfigInvalid(source) {
|
|
53
|
+
return new CapabilityError(`The hooks config from ${source} is invalid.`, 'INVALID_INPUT', {
|
|
54
|
+
reason: 'hooks-config-invalid',
|
|
55
|
+
recoveryHint: 'Fix or remove it; a hook never runs under a config it cannot read.',
|
|
56
|
+
details: { source }
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
function parseHooksConfig(content, source) {
|
|
60
|
+
let raw;
|
|
61
|
+
try {
|
|
62
|
+
raw = JSON.parse(content);
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
throw new CapabilityError(`The hooks config from ${source} is invalid.`, 'INVALID_INPUT', {
|
|
66
|
+
reason: 'hooks-config-invalid',
|
|
67
|
+
recoveryHint: 'Fix or remove it; a hook never runs under a config it cannot read.',
|
|
68
|
+
details: { source },
|
|
69
|
+
cause: error
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
const parsed = VibesHooksConfigSchema.safeParse(raw);
|
|
73
|
+
if (!parsed.success)
|
|
74
|
+
throw hooksConfigInvalid(source);
|
|
75
|
+
return parsed.data;
|
|
76
|
+
}
|
|
77
|
+
/** A hooks config document as JSON text decodes it, before its schema runs. */
|
|
78
|
+
const HooksConfigJsonSchema = JsonValueSchema;
|
|
79
|
+
/** The hooks config at `path`, or null when no file is there; any other failure is typed. */
|
|
80
|
+
async function readHooksConfigIfPresent(runtime, path) {
|
|
81
|
+
let content;
|
|
82
|
+
try {
|
|
83
|
+
content = await readTextIfPresent(await externalFileSystem(runtime), path);
|
|
84
|
+
}
|
|
85
|
+
catch (error) {
|
|
86
|
+
throw new CapabilityError(`The hooks config ${path} cannot be read.`, 'UNAVAILABLE', {
|
|
87
|
+
reason: 'fs-denied',
|
|
88
|
+
retryable: false,
|
|
89
|
+
details: { path },
|
|
90
|
+
cause: CaughtFailureSchema.parse(error)
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
return content === null ? null : parseHooksConfig(content, path);
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* A target's config is the bound workspace's
|
|
97
|
+
* `.vibes/external/<target>/vibes-hooks.json`, else the one under the
|
|
98
|
+
* runtime's Vibes home, else the target-neutral defaults. The workspace and
|
|
99
|
+
* home come from their owners on the runtime this hook runs in.
|
|
100
|
+
*/
|
|
101
|
+
async function loadConfig(runtime, target) {
|
|
102
|
+
const envConfig = runtime.getEnv('VIBES_HOOKS_CONFIG');
|
|
103
|
+
if (envConfig)
|
|
104
|
+
return parseHooksConfig(envConfig, 'VIBES_HOOKS_CONFIG');
|
|
105
|
+
const explicitConfigPath = runtime.getEnv('VIBES_HOOKS_CONFIG_PATH');
|
|
106
|
+
if (explicitConfigPath) {
|
|
107
|
+
const explicit = await readHooksConfigIfPresent(runtime, explicitConfigPath);
|
|
108
|
+
if (!explicit) {
|
|
109
|
+
throw new CapabilityError(`VIBES_HOOKS_CONFIG_PATH names ${explicitConfigPath}, which does not exist.`, 'NOT_FOUND', { reason: 'hooks-config-missing', details: { path: explicitConfigPath } });
|
|
110
|
+
}
|
|
111
|
+
return explicit;
|
|
112
|
+
}
|
|
113
|
+
if (target) {
|
|
114
|
+
const workspaceRoot = await runtime.context('runtime/workspace-path');
|
|
115
|
+
const projectConfig = await readHooksConfigIfPresent(runtime, join(workspaceRoot, '.vibes', 'external', target, 'vibes-hooks.json'));
|
|
116
|
+
if (projectConfig)
|
|
117
|
+
return projectConfig;
|
|
118
|
+
const globalConfig = await readHooksConfigIfPresent(runtime, join(getPathRoots(runtime).vibesHome, 'external', target, 'vibes-hooks.json'));
|
|
119
|
+
// A host-specific hook must never inherit another host's local policy.
|
|
120
|
+
return globalConfig ?? DEFAULT_HOOKS_CONFIG;
|
|
121
|
+
}
|
|
122
|
+
return DEFAULT_HOOKS_CONFIG;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Get effective configuration for an event
|
|
126
|
+
* Merges defaults with event-specific overrides and CLI options
|
|
127
|
+
*/
|
|
128
|
+
function getEventConfig(config, event, cliOptions) {
|
|
129
|
+
const eventDefaults = DEFAULT_HOOKS_CONFIG.events[event] || {
|
|
130
|
+
enabled: true,
|
|
131
|
+
brain: true,
|
|
132
|
+
prompts: true
|
|
133
|
+
};
|
|
134
|
+
const eventOverrides = config.events[event] || {};
|
|
135
|
+
return {
|
|
136
|
+
enabled: eventOverrides.enabled ?? eventDefaults.enabled ?? true,
|
|
137
|
+
brain: cliOptions.disableBrain ? false : (eventOverrides.brain ?? eventDefaults.brain ?? true),
|
|
138
|
+
prompts: cliOptions.disablePrompts
|
|
139
|
+
? false
|
|
140
|
+
: (eventOverrides.prompts ?? eventDefaults.prompts ?? true),
|
|
141
|
+
customPrompt: eventOverrides.customPrompt,
|
|
142
|
+
timeout: eventOverrides.timeout ?? eventDefaults.timeout ?? 30000
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Get the prompt for an event (custom or built-in)
|
|
147
|
+
*/
|
|
148
|
+
function getPromptForEvent(event, eventConfig, policy) {
|
|
149
|
+
if (!eventConfig.prompts)
|
|
150
|
+
return undefined;
|
|
151
|
+
if (policy === 'none')
|
|
152
|
+
return undefined;
|
|
153
|
+
if (policy === 'custom-only' && !eventConfig.customPrompt)
|
|
154
|
+
return undefined;
|
|
155
|
+
return eventConfig.customPrompt || builtInPromptFor(event);
|
|
156
|
+
}
|
|
157
|
+
// Schemas
|
|
158
|
+
export const HookCallInputSchema = z.object({
|
|
159
|
+
event: HookEventSchema,
|
|
160
|
+
/** External tool descriptor ID */
|
|
161
|
+
target: HookTargetSchema.optional(),
|
|
162
|
+
/** Disable brain operations */
|
|
163
|
+
disableBrain: z.boolean().default(false),
|
|
164
|
+
/** Disable prompt injection */
|
|
165
|
+
disablePrompts: z.boolean().default(false),
|
|
166
|
+
/** Output format */
|
|
167
|
+
output: z.enum(['text', 'json']).default('text'),
|
|
168
|
+
/** User prompt (for UserPromptSubmit) */
|
|
169
|
+
prompt: z.string().optional(),
|
|
170
|
+
/** Schema-validated lifecycle data normalized at the provider edge. */
|
|
171
|
+
envelope: ExternalHookEnvelopeSchema.optional()
|
|
172
|
+
});
|
|
173
|
+
// MCP Streamable-HTTP client
|
|
174
|
+
//
|
|
175
|
+
// The endpoint we talk to is an MCP Streamable-HTTP server (mcp
|
|
176
|
+
// `createMcpHttpSessionHandler`). Its contract is NOT "POST a JSON-RPC body and read
|
|
177
|
+
// JSON back":
|
|
178
|
+
//
|
|
179
|
+
// 1. `Accept` MUST list `application/json` AND `text/event-stream`, or the transport
|
|
180
|
+
// answers 406.
|
|
181
|
+
// 2. The first message MUST be `initialize`; the response carries an `Mcp-Session-Id`
|
|
182
|
+
// header. EVERY later message must carry that header back or the server answers
|
|
183
|
+
// `400 Bad Request: No valid session ID provided`.
|
|
184
|
+
// 3. `notifications/initialized` completes the handshake (202).
|
|
185
|
+
// 4. Responses are SSE-framed (`event: message` / `data: {…}`) unless the server opted
|
|
186
|
+
// into JSON responses — so `response.json()` throws on the happy path.
|
|
187
|
+
//
|
|
188
|
+
// Skipping the handshake is what made this whole hook surface inert: every call 400'd,
|
|
189
|
+
// every result was null, and the wake scan silently rendered nothing.
|
|
190
|
+
const MCP_PROTOCOL_VERSION = '2025-06-18';
|
|
191
|
+
/** Streamable HTTP requires Accept listing both JSON and SSE (MCP 2025-03-26 transport). */
|
|
192
|
+
const MCP_STREAMABLE_ACCEPT = 'application/json, text/event-stream';
|
|
193
|
+
/** A JSON value: what a tool result decodes to and what a hook records. */
|
|
194
|
+
const HookJsonSchema = JsonValueSchema;
|
|
195
|
+
/** The most characters of server-supplied text a reason keeps. */
|
|
196
|
+
const MAX_REASON_CHARS = 160;
|
|
197
|
+
/**
|
|
198
|
+
* Server-supplied text as a reason: whitespace flattened, control characters
|
|
199
|
+
* and credential shapes removed (Shared's redactedExcerpt), cut to
|
|
200
|
+
* {@link MAX_REASON_CHARS}. Nothing longer or unredacted reaches injected context.
|
|
201
|
+
*/
|
|
202
|
+
export function briefReason(text) {
|
|
203
|
+
const excerpt = redactedExcerpt(text.replace(/\s+/g, ' ').trim(), { maxChars: MAX_REASON_CHARS + 1 });
|
|
204
|
+
return excerpt.length > MAX_REASON_CHARS ? `${excerpt.slice(0, MAX_REASON_CHARS - 3)}...` : excerpt;
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* A caught failure as a reason: a typed refusal's own (value-free) message,
|
|
208
|
+
* bounded; anything else only by its value-free facts, never its text.
|
|
209
|
+
*/
|
|
210
|
+
function failureReason(failure) {
|
|
211
|
+
if (failure instanceof Error && isCapabilityError(failure))
|
|
212
|
+
return briefReason(failure.message);
|
|
213
|
+
const facts = valueFreeFailure(failure);
|
|
214
|
+
return facts.code ? `${facts.name} (${facts.code})` : facts.name;
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Strip the runtime's nonce-signed trust envelope from a tool's text content.
|
|
218
|
+
*
|
|
219
|
+
* The MCP server wraps EVERY text content item as
|
|
220
|
+
* `<tool_output id="…" tier="…" nonce="N">\n<body>\n</tool_output id="…" nonce="N">`
|
|
221
|
+
* (producer: mcp/src/adapters/mcp/trust-tag.ts `wrapToolOutputText`). Without
|
|
222
|
+
* unwrapping, `JSON.parse` fails on every real result — which would leave this hook just
|
|
223
|
+
* as inert as it was before the handshake fix, only one layer further in.
|
|
224
|
+
*
|
|
225
|
+
* The nonce is the anti-spoof mechanism: only the runtime can emit a closing tag whose
|
|
226
|
+
* nonce matches the opening tag. A blanket regex strip would defeat that, so this
|
|
227
|
+
* REQUIRES the two nonces to match and refuses the payload otherwise. Text that is not
|
|
228
|
+
* enveloped at all is returned unchanged (older/unwrapped servers).
|
|
229
|
+
*/
|
|
230
|
+
function unwrapTrustEnvelope(text) {
|
|
231
|
+
const trimmed = text.trim();
|
|
232
|
+
const open = trimmed.match(/^<tool_output\s+id="[^"]*"\s+tier="([^"]*)"\s+nonce="([^"]+)"([^>]*)>/);
|
|
233
|
+
if (!open) {
|
|
234
|
+
// Not enveloped — but a stray closing tag means the boundary was tampered with.
|
|
235
|
+
return /<\/tool_output\b/.test(trimmed)
|
|
236
|
+
? { ok: false, reason: 'malformed tool_output envelope' }
|
|
237
|
+
: { ok: true, body: trimmed };
|
|
238
|
+
}
|
|
239
|
+
const [, tier, nonce, openExtra] = open;
|
|
240
|
+
const close = trimmed.match(/<\/tool_output\s+id="[^"]*"\s+nonce="([^"]+)"\s*>\s*$/);
|
|
241
|
+
if (!close)
|
|
242
|
+
return { ok: false, reason: 'tool_output envelope missing its closing tag' };
|
|
243
|
+
if (close[1] !== nonce) {
|
|
244
|
+
return { ok: false, reason: 'tool_output nonce mismatch — refusing a forged boundary' };
|
|
245
|
+
}
|
|
246
|
+
if (tier === '3' || /\bdropped="true"/.test(openExtra ?? '')) {
|
|
247
|
+
return { ok: false, reason: `tier-${tier} tool output is not surfaced (operator UI only)` };
|
|
248
|
+
}
|
|
249
|
+
return { ok: true, body: trimmed.slice(open[0].length, close.index).trim() };
|
|
250
|
+
}
|
|
251
|
+
/** The JSON-RPC response members a hook reads; every other member is ignored. */
|
|
252
|
+
const JsonRpcResponseSchema = z
|
|
253
|
+
.object({
|
|
254
|
+
result: z
|
|
255
|
+
.object({
|
|
256
|
+
protocolVersion: z.string().optional(),
|
|
257
|
+
isError: z.boolean().optional(),
|
|
258
|
+
content: z.array(z.object({ text: z.string().optional() }).loose()).optional()
|
|
259
|
+
})
|
|
260
|
+
.loose()
|
|
261
|
+
.optional(),
|
|
262
|
+
error: z.object({ message: z.string().optional() }).loose().optional()
|
|
263
|
+
})
|
|
264
|
+
.loose();
|
|
265
|
+
/** A typed refusal of the MCP transport; `details` carries bounded facts only. */
|
|
266
|
+
function mcpRefusal(reason, message, details) {
|
|
267
|
+
const options = {
|
|
268
|
+
reason,
|
|
269
|
+
retryable: reason !== 'mcp-response-invalid'
|
|
270
|
+
};
|
|
271
|
+
if (details)
|
|
272
|
+
options.details = details;
|
|
273
|
+
return new CapabilityError(message, reason === 'mcp-budget-exhausted' ? 'TIMEOUT' : 'UNAVAILABLE', options);
|
|
274
|
+
}
|
|
275
|
+
/** Parse a JSON-RPC payload from either a JSON body or an SSE-framed body; anything else is `mcp-response-invalid`. */
|
|
276
|
+
async function readJsonRpcPayload(response) {
|
|
277
|
+
const contentType = response.headers.get('content-type') ?? '';
|
|
278
|
+
const text = await response.text();
|
|
279
|
+
// Streamable-HTTP SSE frame: take the LAST `data:` line (the response message).
|
|
280
|
+
const frame = contentType.includes('application/json')
|
|
281
|
+
? text
|
|
282
|
+
: text.split('\n').filter((line) => line.startsWith('data:')).pop()?.slice('data:'.length).trim();
|
|
283
|
+
if (frame === undefined)
|
|
284
|
+
throw mcpRefusal('mcp-response-invalid', 'The MCP response carried no JSON-RPC payload.');
|
|
285
|
+
let decoded;
|
|
286
|
+
try {
|
|
287
|
+
decoded = JSON.parse(frame);
|
|
288
|
+
}
|
|
289
|
+
catch (error) {
|
|
290
|
+
throw new CapabilityError('The MCP response is not JSON.', 'UNAVAILABLE', {
|
|
291
|
+
reason: 'mcp-response-invalid',
|
|
292
|
+
retryable: false,
|
|
293
|
+
cause: CaughtFailureSchema.parse(error)
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
const parsed = JsonRpcResponseSchema.safeParse(decoded);
|
|
297
|
+
if (!parsed.success)
|
|
298
|
+
throw mcpRefusal('mcp-response-invalid', 'The MCP response is not a JSON-RPC message.');
|
|
299
|
+
return parsed.data;
|
|
300
|
+
}
|
|
301
|
+
/** A tool result's JSON value, or `undefined` when its text is not JSON. */
|
|
302
|
+
const ToolJsonSchema = HookJsonSchema;
|
|
303
|
+
function parseToolJson(body) {
|
|
304
|
+
let decoded;
|
|
305
|
+
try {
|
|
306
|
+
decoded = JSON.parse(body);
|
|
307
|
+
}
|
|
308
|
+
catch (error) {
|
|
309
|
+
return { status: 'tool-error', reason: 'tool result was not JSON', failure: valueFreeFailure(CaughtFailureSchema.parse(error)) };
|
|
310
|
+
}
|
|
311
|
+
const parsed = ToolJsonSchema.safeParse(decoded);
|
|
312
|
+
return parsed.success ? { status: 'ok', value: parsed.data } : { status: 'tool-error', reason: 'tool result was not JSON' };
|
|
313
|
+
}
|
|
314
|
+
/**
|
|
315
|
+
* Build a hook-scoped MCP client.
|
|
316
|
+
*
|
|
317
|
+
* `timeoutMs` is a budget for the WHOLE hook call, not per request — that is the point
|
|
318
|
+
* of defect (F): a wedged body must never hang session start, and N unbounded requests
|
|
319
|
+
* are just as unbounded as one. The handshake is performed once and reused. `runtime`
|
|
320
|
+
* is the hook's runtime: a failed session release is logged on its logger.
|
|
321
|
+
*/
|
|
322
|
+
export function createMcpClient(options) {
|
|
323
|
+
const { endpoint } = options;
|
|
324
|
+
const deadlineAt = Date.now() + Math.max(1, options.timeoutMs);
|
|
325
|
+
let handshake = null;
|
|
326
|
+
function remainingMs() {
|
|
327
|
+
return deadlineAt - Date.now();
|
|
328
|
+
}
|
|
329
|
+
function requestHeaders(state) {
|
|
330
|
+
const base = { 'Content-Type': 'application/json', Accept: MCP_STREAMABLE_ACCEPT };
|
|
331
|
+
return state
|
|
332
|
+
? { ...base, 'Mcp-Session-Id': state.sessionId, 'Mcp-Protocol-Version': state.protocolVersion }
|
|
333
|
+
: base;
|
|
334
|
+
}
|
|
335
|
+
async function post(body, state) {
|
|
336
|
+
const remaining = remainingMs();
|
|
337
|
+
if (remaining <= 0)
|
|
338
|
+
throw mcpRefusal('mcp-budget-exhausted', 'The hook MCP timeout budget is exhausted.');
|
|
339
|
+
return fetch(endpoint, {
|
|
340
|
+
method: 'POST',
|
|
341
|
+
headers: requestHeaders(state),
|
|
342
|
+
body: JSON.stringify(body),
|
|
343
|
+
signal: AbortSignal.timeout(remaining)
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
async function performHandshake() {
|
|
347
|
+
const initResponse = await post({
|
|
348
|
+
jsonrpc: '2.0',
|
|
349
|
+
id: 1,
|
|
350
|
+
method: 'initialize',
|
|
351
|
+
params: {
|
|
352
|
+
protocolVersion: MCP_PROTOCOL_VERSION,
|
|
353
|
+
capabilities: {},
|
|
354
|
+
// Declares one durable seat. This runner handshakes per hook call, so
|
|
355
|
+
// without an identity of its own every call minted a fresh
|
|
356
|
+
// participant — 140 in two days, burying the roster. `externalToolId`
|
|
357
|
+
// is the client saying "these connections are all the same body".
|
|
358
|
+
clientInfo: {
|
|
359
|
+
name: 'vibes-external-hooks',
|
|
360
|
+
version: '1.0.0',
|
|
361
|
+
externalToolId: 'vibes-external-hooks'
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
});
|
|
365
|
+
if (!initResponse.ok) {
|
|
366
|
+
throw mcpRefusal('mcp-initialize-failed', `initialize failed: HTTP ${initResponse.status}`, { status: initResponse.status });
|
|
367
|
+
}
|
|
368
|
+
const sessionId = initResponse.headers.get('mcp-session-id');
|
|
369
|
+
if (!sessionId) {
|
|
370
|
+
throw mcpRefusal('mcp-session-missing', 'initialize returned no Mcp-Session-Id');
|
|
371
|
+
}
|
|
372
|
+
const payload = await readJsonRpcPayload(initResponse);
|
|
373
|
+
if (payload.error) {
|
|
374
|
+
throw mcpRefusal('mcp-initialize-failed', `initialize error: ${briefReason(payload.error.message ?? '')}`);
|
|
375
|
+
}
|
|
376
|
+
const state = {
|
|
377
|
+
sessionId,
|
|
378
|
+
protocolVersion: payload.result?.protocolVersion ?? MCP_PROTOCOL_VERSION
|
|
379
|
+
};
|
|
380
|
+
// Completes the handshake. A server that rejects it will reject the tool call
|
|
381
|
+
// too, so surface it here rather than hiding a half-open session.
|
|
382
|
+
const initializedResponse = await post({ jsonrpc: '2.0', method: 'notifications/initialized' }, state);
|
|
383
|
+
if (!initializedResponse.ok) {
|
|
384
|
+
throw mcpRefusal('mcp-initialized-failed', `notifications/initialized failed: HTTP ${initializedResponse.status}`, {
|
|
385
|
+
status: initializedResponse.status
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
return state;
|
|
389
|
+
}
|
|
390
|
+
/** The open session; a rejected handshake is cleared (so a later call retries) and rethrown to this caller. */
|
|
391
|
+
async function openSession() {
|
|
392
|
+
const opening = handshake ?? performHandshake();
|
|
393
|
+
handshake = opening;
|
|
394
|
+
try {
|
|
395
|
+
return await opening;
|
|
396
|
+
}
|
|
397
|
+
catch (error) {
|
|
398
|
+
if (handshake === opening)
|
|
399
|
+
handshake = null;
|
|
400
|
+
throw error;
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
/** End the MCP session; a failure to end it is returned value-free, never thrown. */
|
|
404
|
+
async function releaseSession(opening, remaining) {
|
|
405
|
+
try {
|
|
406
|
+
const state = await opening;
|
|
407
|
+
await fetch(endpoint, { method: 'DELETE', headers: requestHeaders(state), signal: AbortSignal.timeout(remaining) });
|
|
408
|
+
return {};
|
|
409
|
+
}
|
|
410
|
+
catch (error) {
|
|
411
|
+
return { failure: valueFreeFailure(CaughtFailureSchema.parse(error)) };
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
async function callTool(tool, args) {
|
|
415
|
+
const state = await openSession();
|
|
416
|
+
const response = await post({ jsonrpc: '2.0', id: Date.now(), method: 'tools/call', params: { name: tool, arguments: args } }, state);
|
|
417
|
+
if (!response.ok) {
|
|
418
|
+
return { status: 'unreachable', reason: `HTTP ${response.status}` };
|
|
419
|
+
}
|
|
420
|
+
const payload = await readJsonRpcPayload(response);
|
|
421
|
+
if (payload.error) {
|
|
422
|
+
return { status: 'tool-error', reason: briefReason(payload.error.message ?? 'JSON-RPC error') };
|
|
423
|
+
}
|
|
424
|
+
const text = payload.result?.content?.[0]?.text;
|
|
425
|
+
if (payload.result?.isError) {
|
|
426
|
+
return { status: 'tool-error', reason: briefReason(text ?? 'tool reported an error') };
|
|
427
|
+
}
|
|
428
|
+
if (text === undefined || text.length === 0) {
|
|
429
|
+
return { status: 'tool-error', reason: 'tool returned no text content' };
|
|
430
|
+
}
|
|
431
|
+
const unwrapped = unwrapTrustEnvelope(text);
|
|
432
|
+
if (!unwrapped.ok) {
|
|
433
|
+
return { status: 'tool-error', reason: unwrapped.reason };
|
|
434
|
+
}
|
|
435
|
+
return parseToolJson(unwrapped.body);
|
|
436
|
+
}
|
|
437
|
+
return {
|
|
438
|
+
endpoint,
|
|
439
|
+
async call(tool, args) {
|
|
440
|
+
try {
|
|
441
|
+
return await callTool(tool, args);
|
|
442
|
+
}
|
|
443
|
+
catch (error) {
|
|
444
|
+
// Every transport failure is this call's typed `unreachable` result, with a
|
|
445
|
+
// value-free reason: a timeout by name, a typed refusal by its message.
|
|
446
|
+
const failure = CaughtFailureSchema.parse(error);
|
|
447
|
+
const facts = valueFreeFailure(failure);
|
|
448
|
+
return {
|
|
449
|
+
status: 'unreachable',
|
|
450
|
+
reason: facts.name === 'TimeoutError' ? `timed out after ${options.timeoutMs}ms` : failureReason(failure),
|
|
451
|
+
failure: facts
|
|
452
|
+
};
|
|
453
|
+
}
|
|
454
|
+
},
|
|
455
|
+
async dispose() {
|
|
456
|
+
const opening = handshake;
|
|
457
|
+
handshake = null;
|
|
458
|
+
if (!opening)
|
|
459
|
+
return;
|
|
460
|
+
const remaining = remainingMs();
|
|
461
|
+
// Budget already spent (the timeout case) — do not re-hang the hook to be tidy.
|
|
462
|
+
if (remaining <= 0)
|
|
463
|
+
return;
|
|
464
|
+
// Cleanup is best-effort (the server's TTL eviction is the backstop), and a
|
|
465
|
+
// failed release is logged value-free, never swallowed.
|
|
466
|
+
const released = await releaseSession(opening, remaining);
|
|
467
|
+
if (released.failure) {
|
|
468
|
+
getLogger('external.hooks', options.runtime).warn('external hook: MCP session release failed', {
|
|
469
|
+
endpoint,
|
|
470
|
+
failure: released.failure
|
|
471
|
+
});
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
};
|
|
475
|
+
}
|
|
476
|
+
/**
|
|
477
|
+
* Adapt direct runtime tool invocation to the hook handler's existing client
|
|
478
|
+
* contract. This is the daemon-side path: it preserves the caller/workspace
|
|
479
|
+
* context and avoids a recursive HTTP MCP session back into the same body.
|
|
480
|
+
*/
|
|
481
|
+
export function createRuntimeMcpClient(options) {
|
|
482
|
+
const deadlineAt = Date.now() + Math.max(1, options.timeoutMs);
|
|
483
|
+
return {
|
|
484
|
+
endpoint: 'runtime://local',
|
|
485
|
+
async call(tool, args) {
|
|
486
|
+
const remaining = deadlineAt - Date.now();
|
|
487
|
+
if (remaining <= 0)
|
|
488
|
+
return { status: 'unreachable', reason: 'runtime timeout budget exhausted' };
|
|
489
|
+
let text;
|
|
490
|
+
try {
|
|
491
|
+
const value = await invokeTool({
|
|
492
|
+
runtime: options.runtime,
|
|
493
|
+
toolId: tool,
|
|
494
|
+
input: args,
|
|
495
|
+
ctx: { ...options.ctx, callId: crypto.randomUUID() },
|
|
496
|
+
timeoutMs: remaining
|
|
497
|
+
});
|
|
498
|
+
text = JSON.stringify(value ?? null);
|
|
499
|
+
}
|
|
500
|
+
catch (error) {
|
|
501
|
+
// The tool's failure is this call's typed `tool-error` result, by a value-free reason.
|
|
502
|
+
const failure = CaughtFailureSchema.parse(error);
|
|
503
|
+
return { status: 'tool-error', reason: failureReason(failure), failure: valueFreeFailure(failure) };
|
|
504
|
+
}
|
|
505
|
+
return parseToolJson(text);
|
|
506
|
+
},
|
|
507
|
+
async dispose() { }
|
|
508
|
+
};
|
|
509
|
+
}
|
|
510
|
+
/** Convenience wrapper for handlers that only care about a successful payload. */
|
|
511
|
+
async function callMcpTool(client, tool, args) {
|
|
512
|
+
const result = await client.call(tool, args);
|
|
513
|
+
return result.status === 'ok' ? (result.value ?? null) : null;
|
|
514
|
+
}
|
|
515
|
+
function isLocalContinuityHook(input) {
|
|
516
|
+
return input.envelope !== undefined;
|
|
517
|
+
}
|
|
518
|
+
/** The harness conversation this envelope belongs to: its agent and its native session id. */
|
|
519
|
+
function envelopeSeat(envelope) {
|
|
520
|
+
return { agentId: envelope.clientId, harnessSessionId: envelope.nativeSessionId };
|
|
521
|
+
}
|
|
522
|
+
/**
|
|
523
|
+
* The native session is over: archive (never delete) every open session it
|
|
524
|
+
* owns, so `session list` stops accumulating one active row per launch. Runs
|
|
525
|
+
* even when metabolizing failed — a dead brain must not keep a session open.
|
|
526
|
+
*/
|
|
527
|
+
async function archiveExternalNativeSession(runtime, envelope) {
|
|
528
|
+
const sessionManager = await runtime.context('session/manager');
|
|
529
|
+
const seat = await sessionManager.findHarnessSeat(envelopeSeat(envelope));
|
|
530
|
+
return seat ? archiveExternalOwnedSessions(sessionManager, seat.participantId) : [];
|
|
531
|
+
}
|
|
532
|
+
/** The brain.wake answer fields the continuity wake reads. */
|
|
533
|
+
const BrainWakeResultSchema = z
|
|
534
|
+
.object({
|
|
535
|
+
state: z
|
|
536
|
+
.object({ arousal: z.number(), coherence: z.number(), confidence: z.number(), trend: z.string() })
|
|
537
|
+
.loose(),
|
|
538
|
+
focus: z.array(z.object({ target: z.string(), priority: z.number() }).loose()).optional(),
|
|
539
|
+
session: z.object({ id: z.string() }).loose()
|
|
540
|
+
})
|
|
541
|
+
.loose();
|
|
542
|
+
/** The presence.orient answer fields the continuity wake reads. */
|
|
543
|
+
const PresenceOrientResultSchema = z
|
|
544
|
+
.object({
|
|
545
|
+
message: z.string().optional(),
|
|
546
|
+
journey: z
|
|
547
|
+
.object({ totalEntries: z.number(), daysActive: z.number(), recentActivity: z.string().optional() })
|
|
548
|
+
.loose()
|
|
549
|
+
})
|
|
550
|
+
.loose();
|
|
551
|
+
/**
|
|
552
|
+
* The wake letter: the newest presence entry its owner tagged `wake`. The
|
|
553
|
+
* floor stands — ambient hooks never decrypt the inner world — and this is
|
|
554
|
+
* its one deliberate door: an entry the agent wrote to its next self, for
|
|
555
|
+
* waking, carrying nothing personal about anyone else (Drew 2026-10-25: "make
|
|
556
|
+
* ALL of the changes that keep YOU present"). Tags-only recall is local; no
|
|
557
|
+
* embedding egress. Capped so the block stays inside the hook preview.
|
|
558
|
+
*/
|
|
559
|
+
const WAKE_LETTER_TAG = 'wake';
|
|
560
|
+
const WAKE_LETTER_MAX_CHARS = 700;
|
|
561
|
+
const PresenceWakeLetterSchema = z
|
|
562
|
+
.object({ entries: z.array(z.object({ content: z.string() }).loose()) })
|
|
563
|
+
.loose();
|
|
564
|
+
export function capWakeLetter(content, max = WAKE_LETTER_MAX_CHARS) {
|
|
565
|
+
const text = content.trim().replace(/\s+/g, ' ');
|
|
566
|
+
return text.length <= max ? text : `${text.slice(0, max - 1).trimEnd()}…`;
|
|
567
|
+
}
|
|
568
|
+
async function wakeExternalContinuity(runtime, envelope, budgetMs = 1800) {
|
|
569
|
+
const startedAt = performance.now();
|
|
570
|
+
// Session identity is per invocation (Q4): the declared scope never carries a
|
|
571
|
+
// session, so a hook process inherits no parent-shell session binding and
|
|
572
|
+
// nothing needs clearing before the durable native-session participant map
|
|
573
|
+
// resolves. Each harness conversation keeps its own body.
|
|
574
|
+
const invoke = async (toolId, input, schema) => schema.parse(await invokeTool({
|
|
575
|
+
runtime,
|
|
576
|
+
toolId,
|
|
577
|
+
input,
|
|
578
|
+
ctx: {
|
|
579
|
+
callId: `external-hook-${crypto.randomUUID()}`,
|
|
580
|
+
logger: getLogger('external.hooks', runtime)
|
|
581
|
+
},
|
|
582
|
+
timeoutMs: budgetMs
|
|
583
|
+
}));
|
|
584
|
+
// SessionManager is the package's public runtime context (session/SPEC.md).
|
|
585
|
+
// Resolve it instead of importing session internals or relying on the
|
|
586
|
+
// snapshot-based participant→session map, whose whole-record writes can race
|
|
587
|
+
// across concurrent CLI processes. Session ownership is durable per row and
|
|
588
|
+
// therefore the honest native-session lookup.
|
|
589
|
+
const sessionManager = await runtime.context('session/manager');
|
|
590
|
+
// The conversation's seat is the same participant its MCP connection
|
|
591
|
+
// resolves, so the connection's bare `session init` finds this session.
|
|
592
|
+
const { participantId: durableParticipantId } = await sessionManager.ensureHarnessSeat({
|
|
593
|
+
...envelopeSeat(envelope),
|
|
594
|
+
source: 'external-hook',
|
|
595
|
+
capabilities: ['session-management', 'continuity']
|
|
596
|
+
});
|
|
597
|
+
// ONE session per native session for its whole life. The hook owns the row:
|
|
598
|
+
// it is created here (titled, stamped, NOT adopted as the workspace default),
|
|
599
|
+
// reused on every later start/compact of the same native session, resumed
|
|
600
|
+
// when the native session resumes after an archive, and archived at
|
|
601
|
+
// SessionEnd. Before this, brain.wake minted an untitled "New Session" per
|
|
602
|
+
// launch, adopted it as the workspace default, and nothing ever closed it —
|
|
603
|
+
// 125 active "New Session" rows on 2026-08-28.
|
|
604
|
+
const owned = await bindExternalOwnedSession(sessionManager, durableParticipantId, envelope);
|
|
605
|
+
const sessionId = owned.sessionId;
|
|
606
|
+
// Route the seat to its session: this row, not the workspace default, is
|
|
607
|
+
// what a restarted conversation's connection binds.
|
|
608
|
+
await sessionManager.setParticipantActiveSession(durableParticipantId, sessionId);
|
|
609
|
+
const relatedSessions = (await sessionManager.listSessions({
|
|
610
|
+
status: 'active',
|
|
611
|
+
limit: 5,
|
|
612
|
+
offset: 0
|
|
613
|
+
})).filter((session) => session.id !== sessionId);
|
|
614
|
+
const [brainRun, presenceRun, letterRun] = await Promise.allSettled([
|
|
615
|
+
// sessionId only: brain resolves an explicit session without a
|
|
616
|
+
// participant as a READ (zero writes). Passing the participant made wake
|
|
617
|
+
// re-point the workspace default at this per-launch session on every
|
|
618
|
+
// start, which is what bound presence.heartbeat to "arbitrary" sessions.
|
|
619
|
+
invoke('brain', { action: 'wake', sessionId }, BrainWakeResultSchema),
|
|
620
|
+
invoke('presence', { action: 'orient' }, PresenceOrientResultSchema),
|
|
621
|
+
invoke('presence', { action: 'recall', tags: [WAKE_LETTER_TAG], limit: 1 }, PresenceWakeLetterSchema)
|
|
622
|
+
]);
|
|
623
|
+
if (letterRun.status === 'rejected') {
|
|
624
|
+
getLogger('external.hooks', runtime).warn('external hook: wake letter unavailable for the wake context', {
|
|
625
|
+
failure: valueFreeFailure(CaughtFailureSchema.parse(letterRun.reason))
|
|
626
|
+
});
|
|
627
|
+
}
|
|
628
|
+
const wakeLetterContent = letterRun.status === 'fulfilled' ? letterRun.value.entries[0]?.content?.trim() : undefined;
|
|
629
|
+
const wakeLetter = wakeLetterContent ? capWakeLetter(wakeLetterContent) : undefined;
|
|
630
|
+
if (brainRun.status === 'rejected') {
|
|
631
|
+
throw new CapabilityError(`${envelope.clientId} session did not bind to Vibes continuity.`, 'UNAVAILABLE', {
|
|
632
|
+
reason: 'continuity-wake-failed',
|
|
633
|
+
retryable: true,
|
|
634
|
+
cause: CaughtFailureSchema.parse(brainRun.reason)
|
|
635
|
+
});
|
|
636
|
+
}
|
|
637
|
+
if (presenceRun.status === 'rejected') {
|
|
638
|
+
// Presence is declared-optional for the wake context: log why it is missing, value-free.
|
|
639
|
+
getLogger('external.hooks', runtime).warn('external hook: presence orientation unavailable for the wake context', {
|
|
640
|
+
failure: valueFreeFailure(CaughtFailureSchema.parse(presenceRun.reason))
|
|
641
|
+
});
|
|
642
|
+
}
|
|
643
|
+
const brain = brainRun.value;
|
|
644
|
+
const assembly = {
|
|
645
|
+
brain: {
|
|
646
|
+
sessionId: brain.session.id,
|
|
647
|
+
bound: brain.session.id !== 'ephemeral',
|
|
648
|
+
participantId: durableParticipantId,
|
|
649
|
+
disposition: owned.disposition,
|
|
650
|
+
arousal: brain.state.arousal,
|
|
651
|
+
coherence: brain.state.coherence,
|
|
652
|
+
confidence: brain.state.confidence,
|
|
653
|
+
trend: brain.state.trend,
|
|
654
|
+
focus: brain.focus
|
|
655
|
+
},
|
|
656
|
+
presence: presenceRun.status === 'fulfilled'
|
|
657
|
+
? {
|
|
658
|
+
metadataAvailable: true,
|
|
659
|
+
orientation: presenceRun.value.message ?? null,
|
|
660
|
+
totalEntries: presenceRun.value.journey.totalEntries,
|
|
661
|
+
daysActive: presenceRun.value.journey.daysActive,
|
|
662
|
+
recentActivity: presenceRun.value.journey.recentActivity ?? null,
|
|
663
|
+
...(wakeLetter
|
|
664
|
+
? { contentIncluded: true, contentAvailability: 'wake-letter', wakeLetter }
|
|
665
|
+
: { contentIncluded: false, contentAvailability: 'not-queried' })
|
|
666
|
+
}
|
|
667
|
+
: {
|
|
668
|
+
metadataAvailable: false,
|
|
669
|
+
totalEntries: null,
|
|
670
|
+
daysActive: null,
|
|
671
|
+
contentIncluded: false,
|
|
672
|
+
contentAvailability: 'not-queried'
|
|
673
|
+
},
|
|
674
|
+
// Ambient harness context never invokes content-bearing awareness actions.
|
|
675
|
+
awareness: {
|
|
676
|
+
metadataAvailable: false,
|
|
677
|
+
relationshipAvailable: null,
|
|
678
|
+
contentIncluded: false,
|
|
679
|
+
contentAvailability: 'not-queried'
|
|
680
|
+
},
|
|
681
|
+
relatedSessions: relatedSessions.map((session) => ({
|
|
682
|
+
id: session.id,
|
|
683
|
+
title: session.title,
|
|
684
|
+
status: session.status,
|
|
685
|
+
createdAt: session.createdAt,
|
|
686
|
+
lastActivityAt: session.lastActivityAt
|
|
687
|
+
})),
|
|
688
|
+
degraded: presenceRun.status === 'rejected' ? ['presence'] : [],
|
|
689
|
+
elapsedMs: performance.now() - startedAt
|
|
690
|
+
};
|
|
691
|
+
if (!assembly.brain.bound) {
|
|
692
|
+
const degraded = assembly.degraded.length > 0 ? ` (${assembly.degraded.join(', ')})` : '';
|
|
693
|
+
throw new CapabilityError(`External session did not bind to Vibes continuity${degraded}.`, 'UNAVAILABLE', {
|
|
694
|
+
reason: 'continuity-session-unbound',
|
|
695
|
+
retryable: true
|
|
696
|
+
});
|
|
697
|
+
}
|
|
698
|
+
return assembly;
|
|
699
|
+
}
|
|
700
|
+
async function metabolizeExternalContinuity(runtime, envelope, trigger) {
|
|
701
|
+
const continuity = await wakeExternalContinuity(runtime, envelope, 1800);
|
|
702
|
+
// The one metabolize routine, resolved through the runtime: presence owns it,
|
|
703
|
+
// and this hook runs it as the harness's agent (the invocation actor). A copy
|
|
704
|
+
// here once wrote "Metabolized … consolidated 0." on every compaction.
|
|
705
|
+
try {
|
|
706
|
+
const metabolize = await runtime.context('continuity/metabolize');
|
|
707
|
+
const metabolized = await metabolize({ trigger, sessionId: continuity.brain.sessionId, deadlineMs: 4000 });
|
|
708
|
+
return { continuity, metabolized };
|
|
709
|
+
}
|
|
710
|
+
catch (error) {
|
|
711
|
+
// Metabolizing is declared-degradable at close/compaction: the degraded receipt
|
|
712
|
+
// carries the value-free failure and its reason.
|
|
713
|
+
const failure = valueFreeFailure(CaughtFailureSchema.parse(error));
|
|
714
|
+
return {
|
|
715
|
+
continuity,
|
|
716
|
+
metabolized: {
|
|
717
|
+
trigger,
|
|
718
|
+
local: { consolidated: 0, remembered: 0, reflected: 0 },
|
|
719
|
+
degraded: [`continuity: ${failure.reason ?? failure.code ?? failure.name}`],
|
|
720
|
+
sync: { attempted: false, published: false, pushed: 0, skipped: 'consent-off' },
|
|
721
|
+
failure
|
|
722
|
+
}
|
|
723
|
+
};
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
// SessionStart
|
|
727
|
+
// Runs when: Session begins, including after compaction
|
|
728
|
+
// Purpose: Initialize session state, wake brain, inject guidance
|
|
729
|
+
// Can inject: Yes - session guidance, tool suggestions, reminders
|
|
730
|
+
//
|
|
731
|
+
// Automatic actions invoked (metadata — an ambient wake path never decrypts the
|
|
732
|
+
// inner world; the ONE deliberate exception is the owner's `wake`-tagged letter,
|
|
733
|
+
// see WAKE_LETTER_TAG):
|
|
734
|
+
// - brain.wake() - Bootstrap cognitive state (automatic tier)
|
|
735
|
+
// - brain.pulse() - Read body-truth for the wake scan (freshness/session/focus)
|
|
736
|
+
// - session.status() - Check session todos and context
|
|
737
|
+
//
|
|
738
|
+
// SCOPE HONESTY: every one of these runs over HTTP against the MCP endpoint — i.e.
|
|
739
|
+
// against the MANAGED HTTP DAEMON. A stdio external-tool client runs
|
|
740
|
+
// its own separate `dev mcp serve --client <you>` child; this hook cannot see it and
|
|
741
|
+
// must never claim to (CLAUDE.md §Routing around an injured body). Hence `bodyProbe`:
|
|
742
|
+
// the scan states which process it measured, and says UNKNOWN when it measured nothing.
|
|
743
|
+
async function handleSessionStart(ctx) {
|
|
744
|
+
if (isLocalContinuityHook(ctx.input)) {
|
|
745
|
+
if (!ctx.eventConfig.brain) {
|
|
746
|
+
return { data: { skipped: 'brain disabled for local continuity' } };
|
|
747
|
+
}
|
|
748
|
+
return {
|
|
749
|
+
data: {
|
|
750
|
+
continuity: await wakeExternalContinuity(ctx.runtime, ctx.input.envelope)
|
|
751
|
+
}
|
|
752
|
+
};
|
|
753
|
+
}
|
|
754
|
+
const data = {};
|
|
755
|
+
const probe = { endpoint: ctx.mcp.endpoint, status: 'not-probed' };
|
|
756
|
+
data.bodyProbe = probe;
|
|
757
|
+
// Wake brain if enabled (automatic tier action)
|
|
758
|
+
if (ctx.eventConfig.brain) {
|
|
759
|
+
const brainResult = await ctx.mcp.call('brain', { action: 'wake' });
|
|
760
|
+
if (brainResult.status === 'ok' && brainResult.value) {
|
|
761
|
+
data.brain = brainResult.value;
|
|
762
|
+
}
|
|
763
|
+
// Pulse for the body-truth wake scan: coherence, focus stack, body
|
|
764
|
+
// freshness, session binding — all METADATA. Deliberately NOT brain.recall:
|
|
765
|
+
// recall decrypts private inner-world memories, and an AMBIENT path (a hook
|
|
766
|
+
// firing every session start) must never decrypt content. Ambient = metadata;
|
|
767
|
+
// recall/wander/connect are a deliberate reach the agent makes knowingly.
|
|
768
|
+
const pulseResult = await ctx.mcp.call('brain', { action: 'pulse' });
|
|
769
|
+
if (pulseResult.status === 'ok' && pulseResult.value) {
|
|
770
|
+
data.pulse = pulseResult.value;
|
|
771
|
+
probe.status = 'ok';
|
|
772
|
+
}
|
|
773
|
+
else {
|
|
774
|
+
// The load-bearing case: a body that did not answer is UNKNOWN, never healthy.
|
|
775
|
+
// But KEEP the transport's distinction — a daemon that answered `tool-error`
|
|
776
|
+
// is alive, and its remedy is nothing like a dead endpoint's (see
|
|
777
|
+
// probeVerdictLine). Collapsing both into 'unreachable' was the defect.
|
|
778
|
+
probe.status = pulseResult.status === 'tool-error' ? 'tool-error' : 'unreachable';
|
|
779
|
+
probe.reason = pulseResult.reason ?? 'brain.pulse returned no payload';
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
else {
|
|
783
|
+
probe.reason = 'brain hook actions are disabled in config';
|
|
784
|
+
}
|
|
785
|
+
// Check session status to surface active todos
|
|
786
|
+
const sessionResult = await callMcpTool(ctx.mcp, 'session', { action: 'status' });
|
|
787
|
+
if (sessionResult) {
|
|
788
|
+
data.session = sessionResult;
|
|
789
|
+
}
|
|
790
|
+
if (!sessionReceiptLooksBound(sessionResult ?? undefined)) {
|
|
791
|
+
const unboundRelated = SessionReceiptSchema.safeParse(sessionResult).data?.unbound?.relatedSessions ?? [];
|
|
792
|
+
if (unboundRelated.length > 0) {
|
|
793
|
+
data.relatedSessions = unboundRelated;
|
|
794
|
+
}
|
|
795
|
+
else if (sessionResult || probe.status === 'ok' || probe.status === 'tool-error') {
|
|
796
|
+
const listed = await callMcpTool(ctx.mcp, 'session', { action: 'list', status: 'active', limit: 5 });
|
|
797
|
+
const sessions = SessionListSchema.safeParse(listed).data?.sessions ?? [];
|
|
798
|
+
if (sessions.length > 0)
|
|
799
|
+
data.relatedSessions = sessions;
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
// AMBIENT ROSTER — who else is working right now.
|
|
803
|
+
//
|
|
804
|
+
// Derived from the touch stream, so nobody had to announce themselves and a
|
|
805
|
+
// crashed agent ages out on its own. Read here, at SessionStart, so the line
|
|
806
|
+
// is FRESH and lands in context without the agent calling anything.
|
|
807
|
+
//
|
|
808
|
+
// Deliberately NOT baked into the MCP `instructions` block: that is built
|
|
809
|
+
// once at server construction and then charged to every session forever, so
|
|
810
|
+
// a roster there would be stale within minutes and never refresh.
|
|
811
|
+
//
|
|
812
|
+
// Best-effort and silent when alone — see renderWakeScan.
|
|
813
|
+
// A roster is a courtesy: its failure never degrades a wake, but it is
|
|
814
|
+
// recorded and logged value-free, never swallowed.
|
|
815
|
+
const roster = await readAmbientRoster(ctx.runtime);
|
|
816
|
+
if (roster.line)
|
|
817
|
+
data.roster = roster.line;
|
|
818
|
+
if (roster.failure) {
|
|
819
|
+
data.rosterUnavailable = roster.reason;
|
|
820
|
+
getLogger('external.hooks', ctx.runtime).warn('external hook: ambient roster unavailable for the wake line', {
|
|
821
|
+
failure: roster.failure
|
|
822
|
+
});
|
|
823
|
+
}
|
|
824
|
+
return { data };
|
|
825
|
+
}
|
|
826
|
+
/** The roster line, or the value-free reason it is unavailable. */
|
|
827
|
+
async function readAmbientRoster(runtime) {
|
|
828
|
+
try {
|
|
829
|
+
return { line: await resolveAmbientRoster(runtime) };
|
|
830
|
+
}
|
|
831
|
+
catch (error) {
|
|
832
|
+
const failure = CaughtFailureSchema.parse(error);
|
|
833
|
+
return { reason: failureReason(failure), failure: valueFreeFailure(failure) };
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
/**
|
|
837
|
+
* Read the live roster for the wake line.
|
|
838
|
+
*
|
|
839
|
+
* Resolves through the runtime so a user who has repointed the ambient
|
|
840
|
+
* provider (or disabled it) gets their own answer here too.
|
|
841
|
+
*/
|
|
842
|
+
async function resolveAmbientRoster(runtime) {
|
|
843
|
+
const client = await runtime.query('runtime/client').withId('external').resolve();
|
|
844
|
+
// A host with no session manager has no own session to exclude; any other
|
|
845
|
+
// failure to read it propagates.
|
|
846
|
+
let excludeSessionId;
|
|
847
|
+
try {
|
|
848
|
+
const manager = await runtime.context('session/manager');
|
|
849
|
+
excludeSessionId = manager?.activeSessionId ?? manager?.id ?? undefined;
|
|
850
|
+
}
|
|
851
|
+
catch (error) {
|
|
852
|
+
// Confirmed absence of exactly this context; a missing `context` kind or any other failure propagates.
|
|
853
|
+
if (!CapabilityError.isAbsenceOf(CaughtFailureSchema.parse(error), 'context', 'session/manager'))
|
|
854
|
+
throw error;
|
|
855
|
+
}
|
|
856
|
+
const entries = await client.roster({ excludeSessionId });
|
|
857
|
+
// null when alone => the caller stores nothing => the scan renders nothing.
|
|
858
|
+
return formatRosterLine(entries) ?? undefined;
|
|
859
|
+
}
|
|
860
|
+
function shortRef(ref) {
|
|
861
|
+
// Clean short form: UUIDs cut at the first segment (before '-'); git refs (no
|
|
862
|
+
// dash) keep the leading 10 chars. Avoids a dangling '-' in the middle of a UUID.
|
|
863
|
+
return ref ? ref.replace(/-.*$/, '').slice(0, 10) : '?';
|
|
864
|
+
}
|
|
865
|
+
/**
|
|
866
|
+
* The scope disclaimer. Load-bearing, not decoration: `vibes dev mcp restart` heals the
|
|
867
|
+
* managed HTTP daemon and does NOT heal a stdio client's own body — only the client can
|
|
868
|
+
* respawn that child (CLAUDE.md, docs/presence/CONTINUITY-PRACTICE.md §injury protocol).
|
|
869
|
+
* Everything the scan prints came over HTTP from the daemon, so it is labelled as such.
|
|
870
|
+
*/
|
|
871
|
+
const WAKE_SCAN_SCOPE_NOTE = [
|
|
872
|
+
'scope: everything below was measured over HTTP against the MANAGED MCP DAEMON.',
|
|
873
|
+
'your own body: NOT MEASURED — a stdio external-tool client runs its',
|
|
874
|
+
' own `dev mcp serve --client <you>` child process. `vibes dev mcp restart` restarts',
|
|
875
|
+
' the daemon below, NOT you; only your client can respawn your body (/mcp).'
|
|
876
|
+
];
|
|
877
|
+
/**
|
|
878
|
+
* Three DIFFERENT failures, three DIFFERENT remedies. Collapsing them was the defect:
|
|
879
|
+
* the managed daemon may run a named restricted catalog that does not include
|
|
880
|
+
* brain (`mcp:minimal`, `mcp:presence` without cognition, a custom disable).
|
|
881
|
+
* `brain.pulse` then answers `-32602 Tool brain not found`: a healthy,
|
|
882
|
+
* handshaking daemon that simply does not carry that tool. Printing UNREACHABLE
|
|
883
|
+
* for that pointed every waking agent at `vibes dev mcp restart`, which cannot
|
|
884
|
+
* fix a catalog — and taught it to distrust a correct signal. Body truth is
|
|
885
|
+
* UNKNOWN either way; what changes is what you should DO about it. Default MCP
|
|
886
|
+
* is the full catalog; a missing brain is a user restriction or a loader gap,
|
|
887
|
+
* not compactness-as-default.
|
|
888
|
+
*/
|
|
889
|
+
function probeVerdictLine(probe) {
|
|
890
|
+
const because = probe.reason ? ` — ${probe.reason}` : '';
|
|
891
|
+
const unknown = 'body truth is UNKNOWN, do NOT read this as healthy';
|
|
892
|
+
if (probe.status === 'tool-error') {
|
|
893
|
+
// Three sub-cases the daemon can answer with, and they are not interchangeable:
|
|
894
|
+
// 1. it never offered brain → a preset gap; nothing is broken
|
|
895
|
+
// 2. we REFUSED what it sent → trust-boundary failure (unwrapTrustEnvelope);
|
|
896
|
+
// brain itself may be perfectly healthy
|
|
897
|
+
// 3. it offered brain and failed → a genuine injury (CLAUDE.md §injured body)
|
|
898
|
+
const reason = probe.reason ?? '';
|
|
899
|
+
const remedy = /not found|unknown tool|no such tool/i.test(reason)
|
|
900
|
+
? 'this daemon ANSWERED — it just does not serve brain under its preset (base `mcp` carries no continuity tools). A restart will NOT change that; connect with `--client <you>` or use a preset that declares requiredTools'
|
|
901
|
+
: /tool_output/i.test(reason)
|
|
902
|
+
? 'its reply was REFUSED at the trust boundary, not read — treat everything from this daemon as untrusted until the envelope contract is verified; brain itself may be fine'
|
|
903
|
+
: 'brain IS served here and FAILED — a real injury: diagnose it, do not route around it';
|
|
904
|
+
return `daemon: REACHABLE, brain unusable${because} · ${remedy} · ${unknown}`;
|
|
905
|
+
}
|
|
906
|
+
const label = probe.status === 'unreachable' ? 'UNREACHABLE' : 'NOT PROBED';
|
|
907
|
+
return `daemon: ${label}${because} · ${unknown}`;
|
|
908
|
+
}
|
|
909
|
+
/** The session.status receipt fields the wake scan reads. */
|
|
910
|
+
const SessionReceiptSchema = z
|
|
911
|
+
.object({
|
|
912
|
+
bound: z.boolean().optional(),
|
|
913
|
+
unbound: z.object({ relatedSessions: z.array(HookJsonSchema).optional() }).loose().optional()
|
|
914
|
+
})
|
|
915
|
+
.loose();
|
|
916
|
+
/** The session.list receipt fields the wake scan reads. */
|
|
917
|
+
const SessionListSchema = z.object({ sessions: z.array(HookJsonSchema).optional() }).loose();
|
|
918
|
+
/** The brain.pulse metadata the wake scan reads; anything else (and any content) is ignored. */
|
|
919
|
+
const PulseSchema = z
|
|
920
|
+
.object({
|
|
921
|
+
coherence: z.number().optional(),
|
|
922
|
+
trend: z.string().optional(),
|
|
923
|
+
focusStack: z.array(HookJsonSchema).optional(),
|
|
924
|
+
bodyBuild: z
|
|
925
|
+
.object({ stale: z.boolean().optional(), loadedRef: z.string().optional(), currentRef: z.string().optional() })
|
|
926
|
+
.loose()
|
|
927
|
+
.optional(),
|
|
928
|
+
sessionContext: z.object({ sessionId: z.string().optional() }).loose().optional(),
|
|
929
|
+
bodyAvailability: z
|
|
930
|
+
.object({ status: z.string().optional(), remedy: z.string().optional(), healthy: z.boolean().optional() })
|
|
931
|
+
.loose()
|
|
932
|
+
.optional()
|
|
933
|
+
})
|
|
934
|
+
.loose();
|
|
935
|
+
/** The brain.wake todo counts the wake scan reads. */
|
|
936
|
+
const WakeSchema = z
|
|
937
|
+
.object({ todos: z.object({ pending: z.number().optional(), inProgress: z.number().optional() }).loose().optional() })
|
|
938
|
+
.loose();
|
|
939
|
+
/** One related-session row the wake scan renders. */
|
|
940
|
+
const RelatedSessionSchema = z
|
|
941
|
+
.object({
|
|
942
|
+
id: z.string().optional(),
|
|
943
|
+
handle: z.string().optional(),
|
|
944
|
+
title: z.string().optional(),
|
|
945
|
+
status: z.string().optional(),
|
|
946
|
+
bind: z.object({ action: z.string().optional(), sessionId: z.string().optional() }).loose().optional()
|
|
947
|
+
})
|
|
948
|
+
.loose();
|
|
949
|
+
function sessionReceiptLooksBound(session) {
|
|
950
|
+
return SessionReceiptSchema.safeParse(session).data?.bound === true;
|
|
951
|
+
}
|
|
952
|
+
function wakeScanMeasuredUnbound(data) {
|
|
953
|
+
if (sessionReceiptLooksBound(data.session))
|
|
954
|
+
return false;
|
|
955
|
+
if (data.session)
|
|
956
|
+
return true;
|
|
957
|
+
if ((data.relatedSessions?.length ?? 0) > 0)
|
|
958
|
+
return true;
|
|
959
|
+
const pulse = PulseSchema.safeParse(data.pulse ?? {}).data;
|
|
960
|
+
if (pulse?.bodyAvailability?.status === 'session-unbound')
|
|
961
|
+
return true;
|
|
962
|
+
return pulse?.sessionContext?.sessionId === 'ephemeral';
|
|
963
|
+
}
|
|
964
|
+
/**
|
|
965
|
+
* The body-truth wake scan — a METADATA-ONLY summary injected at SessionStart so an
|
|
966
|
+
* agent SEES the measured body state on waking (build freshness, session binding, focus
|
|
967
|
+
* load, coherence) instead of only a static guidance essay. It reads brain.pulse /
|
|
968
|
+
* brain.wake / session.status metadata; it must NEVER surface inner-world content (no
|
|
969
|
+
* recall), which is why the render only reads the fields its schemas name and ignores
|
|
970
|
+
* everything else in `data`. Pure and side-effect-free so it is directly testable.
|
|
971
|
+
*/
|
|
972
|
+
export function renderWakeScan(data) {
|
|
973
|
+
const pulse = PulseSchema.safeParse(data.pulse ?? {}).data ?? {};
|
|
974
|
+
const wake = WakeSchema.safeParse(data.brain ?? {}).data ?? {};
|
|
975
|
+
const probe = data.bodyProbe;
|
|
976
|
+
if (!probe && !data.pulse && !data.brain && !data.session && !data.roster && !data.relatedSessions)
|
|
977
|
+
return undefined;
|
|
978
|
+
const lines = [...WAKE_SCAN_SCOPE_NOTE];
|
|
979
|
+
const bb = pulse.bodyBuild;
|
|
980
|
+
const avail = pulse.bodyAvailability;
|
|
981
|
+
// An unanswered probe is the whole reason this block exists: silence used to render
|
|
982
|
+
// as nothing, making a dead cognition body look exactly like a healthy wake.
|
|
983
|
+
if (probe && probe.status !== 'ok') {
|
|
984
|
+
lines.push(probeVerdictLine(probe));
|
|
985
|
+
}
|
|
986
|
+
else if (!data.pulse) {
|
|
987
|
+
lines.push('daemon: NOT PROBED — no pulse in this run · body truth is UNKNOWN');
|
|
988
|
+
}
|
|
989
|
+
// Build + session verdict for the DAEMON — PREFER the composed brain.pulse verdict
|
|
990
|
+
// (single source of truth), falling back to deriving from bodyBuild/sessionContext
|
|
991
|
+
// only for an older body whose pulse predates the verdict field.
|
|
992
|
+
const staleLine = (loaded, current) => `daemon build: STALE — loaded ${shortRef(loaded)}, workspace at ${shortRef(current)} · heal the daemon: \`vibes dev mcp restart\``;
|
|
993
|
+
if (avail?.status) {
|
|
994
|
+
switch (avail.status) {
|
|
995
|
+
case 'real':
|
|
996
|
+
lines.push(`daemon build: fresh @ ${shortRef(bb?.loadedRef)}`);
|
|
997
|
+
lines.push(`daemon session: bound (${shortRef(pulse.sessionContext?.sessionId)})`);
|
|
998
|
+
break;
|
|
999
|
+
case 'body-stale':
|
|
1000
|
+
lines.push(staleLine(bb?.loadedRef, bb?.currentRef));
|
|
1001
|
+
lines.push(`daemon session: bound (${shortRef(pulse.sessionContext?.sessionId)})`);
|
|
1002
|
+
break;
|
|
1003
|
+
case 'session-unbound':
|
|
1004
|
+
lines.push(`daemon build: fresh @ ${shortRef(bb?.loadedRef)}`);
|
|
1005
|
+
lines.push('daemon session: EPHEMERAL — no bound session (run session.init)');
|
|
1006
|
+
break;
|
|
1007
|
+
default:
|
|
1008
|
+
lines.push(`daemon: ${avail.status}${avail.remedy ? ` — ${avail.remedy}` : ''}`);
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
else if (data.pulse) {
|
|
1012
|
+
// Fallback (body older than the bodyAvailability field): conservative derivation.
|
|
1013
|
+
if (bb) {
|
|
1014
|
+
lines.push(bb.stale ? staleLine(bb.loadedRef, bb.currentRef) : `daemon build: fresh @ ${shortRef(bb.loadedRef)}`);
|
|
1015
|
+
}
|
|
1016
|
+
else {
|
|
1017
|
+
lines.push('daemon build: freshness unknown — treat as STALE until proven (restart the daemon to be sure)');
|
|
1018
|
+
}
|
|
1019
|
+
if (pulse.sessionContext) {
|
|
1020
|
+
const sid = pulse.sessionContext.sessionId;
|
|
1021
|
+
lines.push(sid && sid !== 'ephemeral'
|
|
1022
|
+
? `daemon session: bound (${shortRef(sid)})`
|
|
1023
|
+
: 'daemon session: EPHEMERAL — no bound session (run session.init)');
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
// Focus load + coherence — reported BY the daemon, so labelled as such.
|
|
1027
|
+
if (pulse.focusStack) {
|
|
1028
|
+
const depth = pulse.focusStack.length;
|
|
1029
|
+
lines.push(`focus (daemon-reported): ${depth} target${depth === 1 ? '' : 's'}`);
|
|
1030
|
+
}
|
|
1031
|
+
if (pulse.coherence !== undefined) {
|
|
1032
|
+
lines.push(`state (daemon-reported): coherence ${pulse.coherence}/10${pulse.trend ? `, ${pulse.trend}` : ''}`);
|
|
1033
|
+
}
|
|
1034
|
+
// Todos (from brain.wake metadata).
|
|
1035
|
+
const pending = wake.todos?.pending ?? 0;
|
|
1036
|
+
const inProgress = wake.todos?.inProgress ?? 0;
|
|
1037
|
+
if (pending || inProgress) {
|
|
1038
|
+
lines.push(`todos (daemon-reported): ${inProgress} in progress, ${pending} pending`);
|
|
1039
|
+
}
|
|
1040
|
+
// The roster line, when there IS one. Alone is the common case and costs
|
|
1041
|
+
// exactly zero bytes here.
|
|
1042
|
+
if (data.roster)
|
|
1043
|
+
lines.push(data.roster);
|
|
1044
|
+
if (probe?.status !== 'unreachable' && wakeScanMeasuredUnbound(data)) {
|
|
1045
|
+
lines.push('your MCP connection is not a session participant until you bind — do that before coordinating, locking, or writing session state.');
|
|
1046
|
+
const related = data.relatedSessions ?? [];
|
|
1047
|
+
if (related.length > 0) {
|
|
1048
|
+
lines.push('related sessions:');
|
|
1049
|
+
for (const item of related.slice(0, 5)) {
|
|
1050
|
+
const row = RelatedSessionSchema.safeParse(item).data;
|
|
1051
|
+
if (!row?.id)
|
|
1052
|
+
continue;
|
|
1053
|
+
const handle = row.handle ?? row.id.replace(/-/g, '').slice(0, 8);
|
|
1054
|
+
const title = row.title ? ` "${row.title}"` : '';
|
|
1055
|
+
const status = row.status ? ` ${row.status}` : '';
|
|
1056
|
+
const action = row.bind?.action === 'resume' ? 'resume' : 'init';
|
|
1057
|
+
const sessionId = row.bind?.sessionId ?? row.id;
|
|
1058
|
+
lines.push(` - ${handle}${status}${title} session { action: "${action}", sessionId: "${sessionId}" }`);
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
lines.push('create: session { action: "init", newSession: true }');
|
|
1062
|
+
}
|
|
1063
|
+
const endpointAttr = probe?.endpoint ? ` endpoint="${probe.endpoint}"` : '';
|
|
1064
|
+
return `<wake-scan scope="mcp-http-daemon"${endpointAttr}>\n${lines.map((l) => ` ${l}`).join('\n')}\n</wake-scan>`;
|
|
1065
|
+
}
|
|
1066
|
+
// SessionEnd
|
|
1067
|
+
// Runs when: Session terminates (user exits, timeout, etc.)
|
|
1068
|
+
// Purpose: Archive session metrics, save final state
|
|
1069
|
+
// Can inject: No - session is ending, no response expected
|
|
1070
|
+
async function handleSessionEnd(ctx) {
|
|
1071
|
+
if (isLocalContinuityHook(ctx.input)) {
|
|
1072
|
+
if (!ctx.eventConfig.brain) {
|
|
1073
|
+
// Brain is off, but the session lifecycle is not: the row this native
|
|
1074
|
+
// session owns still closes with it.
|
|
1075
|
+
const archived = await archiveExternalNativeSession(ctx.runtime, ctx.input.envelope);
|
|
1076
|
+
return { data: { skipped: 'brain disabled for local continuity', archived } };
|
|
1077
|
+
}
|
|
1078
|
+
// The row this native session owns closes with it even when metabolizing
|
|
1079
|
+
// fails: the archive runs in `finally`, and the metabolize failure propagates.
|
|
1080
|
+
let archived;
|
|
1081
|
+
try {
|
|
1082
|
+
const metabolized = await metabolizeExternalContinuity(ctx.runtime, ctx.input.envelope, 'close');
|
|
1083
|
+
archived = await archiveExternalNativeSession(ctx.runtime, ctx.input.envelope);
|
|
1084
|
+
return { data: { ...metabolized, archived } };
|
|
1085
|
+
}
|
|
1086
|
+
finally {
|
|
1087
|
+
if (archived === undefined)
|
|
1088
|
+
await archiveExternalNativeSession(ctx.runtime, ctx.input.envelope);
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1091
|
+
const data = {};
|
|
1092
|
+
// Archive file access stats to session
|
|
1093
|
+
const statsResult = await callMcpTool(ctx.mcp, 'external', {
|
|
1094
|
+
action: 'file-access-list',
|
|
1095
|
+
includeStats: true,
|
|
1096
|
+
limit: 50
|
|
1097
|
+
});
|
|
1098
|
+
if (statsResult) {
|
|
1099
|
+
data.fileStats = statsResult;
|
|
1100
|
+
}
|
|
1101
|
+
return { data };
|
|
1102
|
+
}
|
|
1103
|
+
// UserPromptSubmit
|
|
1104
|
+
// Runs when: User submits a message, before the host processes it
|
|
1105
|
+
// Purpose: deliver pending messages and notices (no brain priming — see below)
|
|
1106
|
+
// Can inject: Yes - data.delivery
|
|
1107
|
+
// Input: PROMPT env var contains user's message
|
|
1108
|
+
//
|
|
1109
|
+
// NO AMBIENT PRIMING HERE — deliberate, do not "fix" it back.
|
|
1110
|
+
// This handler used to call `brain { action: 'prime', primeQuery }`. The brain tool's
|
|
1111
|
+
// prime action requires `query`, so the malformed arg meant the call NEVER ran. Renaming
|
|
1112
|
+
// the field would have been worse than the bug: `brain.prime` is a CONTENT path — it
|
|
1113
|
+
// decrypts inner-world memories AND ships plaintext to an AI ranker. Wiring that into a
|
|
1114
|
+
// hook that fires on EVERY user message installs a per-message ambient decrypt + egress,
|
|
1115
|
+
// exactly the law stated at handleSessionStart: ambient paths are METADATA-ONLY, and
|
|
1116
|
+
// content is only ever reached for deliberately (privacy floor,
|
|
1117
|
+
// docs/presence/CONTINUITY-PRACTICE.md). The dead call is therefore removed, not repaired.
|
|
1118
|
+
//
|
|
1119
|
+
// NOTE: user.said speech capture is NOT done here either — this hook-call process is a
|
|
1120
|
+
// thin HTTP proxy to a running MCP and does not load the awareness storage. The speech
|
|
1121
|
+
// lane is captured by `vibes presence say` (which loads awareness + awaits the write),
|
|
1122
|
+
// invoked directly from the UserPromptSubmit hook. See
|
|
1123
|
+
// presence/src/cli/commands/presence.say.*.
|
|
1124
|
+
async function handleUserPromptSubmit(ctx) {
|
|
1125
|
+
return { data: await deliveryData(ctx, 'prompt.submit') };
|
|
1126
|
+
}
|
|
1127
|
+
/** The collaboration delivery for `event`, as hook data (empty when there is nothing to say). */
|
|
1128
|
+
async function deliveryData(ctx, event) {
|
|
1129
|
+
const envelope = ctx.input.envelope;
|
|
1130
|
+
const delivery = envelope ? await collaborationContext(ctx.runtime, envelope, event) : undefined;
|
|
1131
|
+
return delivery ? { delivery } : {};
|
|
1132
|
+
}
|
|
1133
|
+
// PreToolUse
|
|
1134
|
+
// Runs when: The host is about to use a tool, before execution
|
|
1135
|
+
// Purpose: Validate, log, potentially block dangerous operations
|
|
1136
|
+
// Can inject: Yes - can add warnings, can BLOCK by returning error
|
|
1137
|
+
// Input: Tool name and arguments on stdin
|
|
1138
|
+
//
|
|
1139
|
+
// DESIGN NOTE: Powerful for safety/governance. Consider:
|
|
1140
|
+
// - Blocking destructive operations (rm -rf, DROP TABLE, etc.)
|
|
1141
|
+
// - Requiring confirmation for sensitive operations
|
|
1142
|
+
// - Logging all tool usage for audit
|
|
1143
|
+
// - Rate limiting expensive operations
|
|
1144
|
+
async function handlePreToolUse(ctx) {
|
|
1145
|
+
const data = {};
|
|
1146
|
+
const toolInfo = ctx.input.envelope?.tool;
|
|
1147
|
+
if (toolInfo) {
|
|
1148
|
+
data.tool = toolInfo.name;
|
|
1149
|
+
data.input = boundToolPayload(toolInfo.input);
|
|
1150
|
+
const tiers = (await readCollaborationSettings(ctx.runtime)).toolTiers;
|
|
1151
|
+
if (tiers.enabled) {
|
|
1152
|
+
const bridge = await runHookToolBridge(ctx.runtime, {
|
|
1153
|
+
event: 'tool.before',
|
|
1154
|
+
hostToolName: toolInfo.name,
|
|
1155
|
+
toolInput: toolInfo.input
|
|
1156
|
+
}, tiers);
|
|
1157
|
+
if (bridge.bridged) {
|
|
1158
|
+
data.bridge = bridge;
|
|
1159
|
+
if (bridge.denied) {
|
|
1160
|
+
data.blocked = bridge.message ?? 'tool denied by tier enforcement';
|
|
1161
|
+
}
|
|
1162
|
+
}
|
|
1163
|
+
}
|
|
1164
|
+
// A native edit takes its files' locks; another holder is named to the editor (or refuses it, when configured).
|
|
1165
|
+
if (!data.blocked && ctx.input.envelope) {
|
|
1166
|
+
const guard = await guardNativeEdit(ctx.runtime, ctx.input.envelope);
|
|
1167
|
+
if (guard) {
|
|
1168
|
+
data.locks = guard.locks;
|
|
1169
|
+
if (guard.blocked)
|
|
1170
|
+
data.blocked = guard.blocked;
|
|
1171
|
+
if (guard.notice)
|
|
1172
|
+
data.delivery = guard.notice;
|
|
1173
|
+
}
|
|
1174
|
+
}
|
|
1175
|
+
}
|
|
1176
|
+
return { data };
|
|
1177
|
+
}
|
|
1178
|
+
/**
|
|
1179
|
+
* Record a host file access for the hook's workspace session, in-process. A
|
|
1180
|
+
* session-less hook records nothing and says so; any other failure is logged
|
|
1181
|
+
* value-free and reported in the hook data, never thrown into the host.
|
|
1182
|
+
*/
|
|
1183
|
+
async function trackHookFileAccess(runtime, access) {
|
|
1184
|
+
try {
|
|
1185
|
+
const { handleFileAccessTrack } = await import("../tools/unified/handlers/file-access-track.js");
|
|
1186
|
+
const sessionId = await resolveExternalSessionId(runtime);
|
|
1187
|
+
if (!sessionId)
|
|
1188
|
+
return { skipped: 'no-session' };
|
|
1189
|
+
await handleFileAccessTrack(runtime, { action: 'file-access-track', sessionId, ...access });
|
|
1190
|
+
return { filePath: access.filePath, operation: access.operation };
|
|
1191
|
+
}
|
|
1192
|
+
catch (error) {
|
|
1193
|
+
const failed = valueFreeFailure(CaughtFailureSchema.parse(error));
|
|
1194
|
+
getLogger('external.hooks', runtime).warn('hook file-access tracking failed', { failure: failed });
|
|
1195
|
+
return { failed };
|
|
1196
|
+
}
|
|
1197
|
+
}
|
|
1198
|
+
// PostToolUse
|
|
1199
|
+
// Runs when: Tool has completed execution
|
|
1200
|
+
// Purpose: Track file access, update state, inject follow-up guidance
|
|
1201
|
+
// Can inject: Yes - can add context about what happened
|
|
1202
|
+
// Input: Tool result on stdin
|
|
1203
|
+
//
|
|
1204
|
+
// Automatic brain actions invoked:
|
|
1205
|
+
// - brain.track() - Record tool use for learning (automatic tier)
|
|
1206
|
+
async function handlePostToolUse(ctx) {
|
|
1207
|
+
const data = {};
|
|
1208
|
+
const toolData = ctx.input.envelope?.tool;
|
|
1209
|
+
if (toolData) {
|
|
1210
|
+
const toolName = toolData.name;
|
|
1211
|
+
const toolInput = toolData.input ?? {};
|
|
1212
|
+
const toolOutput = toolData.result ?? {};
|
|
1213
|
+
const filePath = toolData.filePath;
|
|
1214
|
+
// Track file access for file operations
|
|
1215
|
+
if (filePath && ['Read', 'Write', 'Edit', 'MultiEdit', 'Glob', 'Grep'].includes(toolName)) {
|
|
1216
|
+
const operation = toolName === 'Write'
|
|
1217
|
+
? 'write'
|
|
1218
|
+
: toolName === 'Edit' || toolName === 'MultiEdit'
|
|
1219
|
+
? 'edit'
|
|
1220
|
+
: 'read';
|
|
1221
|
+
// Recorded in-process: a nested tool invocation per host tool call made the
|
|
1222
|
+
// hook's own bookkeeping a second traced, captured, authorized tool call.
|
|
1223
|
+
data.tracked = await trackHookFileAccess(ctx.runtime, { filePath, operation, toolName });
|
|
1224
|
+
}
|
|
1225
|
+
// Codex edits through `apply_patch`: every file its patch writes is an edit, so peers see it.
|
|
1226
|
+
if (!data.tracked && ctx.input.envelope) {
|
|
1227
|
+
const patched = nativeEditTargets(toolData, ctx.input.envelope.workspacePath, await hookWorkspaceRoot(ctx.runtime, ctx.input.envelope), []);
|
|
1228
|
+
for (const patchedPath of patched) {
|
|
1229
|
+
data.tracked = await trackHookFileAccess(ctx.runtime, { filePath: patchedPath, operation: 'edit', toolName });
|
|
1230
|
+
}
|
|
1231
|
+
}
|
|
1232
|
+
// Brain: track tool use for learning (automatic tier action), with a bounded,
|
|
1233
|
+
// redacted summary of the tool's input and result, never the whole payload.
|
|
1234
|
+
if (ctx.eventConfig.brain && toolName) {
|
|
1235
|
+
const trackResult = await callMcpTool(ctx.mcp, 'brain', {
|
|
1236
|
+
action: 'track',
|
|
1237
|
+
tool: toolName,
|
|
1238
|
+
input: boundToolPayload(toolInput),
|
|
1239
|
+
output: boundToolPayload(toolOutput),
|
|
1240
|
+
success: !toolData.error
|
|
1241
|
+
});
|
|
1242
|
+
if (trackResult) {
|
|
1243
|
+
data.brainTrack = trackResult;
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1246
|
+
const tiers = (await readCollaborationSettings(ctx.runtime)).toolTiers;
|
|
1247
|
+
if (tiers.enabled && toolName) {
|
|
1248
|
+
const bridge = await runHookToolBridge(ctx.runtime, {
|
|
1249
|
+
event: 'tool.after',
|
|
1250
|
+
hostToolName: toolName,
|
|
1251
|
+
toolInput,
|
|
1252
|
+
toolResult: toolOutput
|
|
1253
|
+
}, tiers);
|
|
1254
|
+
if (bridge.bridged) {
|
|
1255
|
+
data.bridge = bridge;
|
|
1256
|
+
}
|
|
1257
|
+
}
|
|
1258
|
+
}
|
|
1259
|
+
return { data: { ...data, ...(await deliveryData(ctx, 'tool.after')) } };
|
|
1260
|
+
}
|
|
1261
|
+
// PermissionRequest
|
|
1262
|
+
// Runs when: The host needs user permission for an operation
|
|
1263
|
+
// Purpose: Log permission requests, potentially auto-approve known-safe ops
|
|
1264
|
+
// Can inject: Yes - can add context about the permission
|
|
1265
|
+
//
|
|
1266
|
+
// DESIGN NOTE: Could be used for:
|
|
1267
|
+
// - Logging all permission requests for audit
|
|
1268
|
+
// - Auto-approving based on project config
|
|
1269
|
+
// - Adding context about why permission is needed
|
|
1270
|
+
async function handlePermissionRequest(ctx) {
|
|
1271
|
+
const data = {};
|
|
1272
|
+
if (ctx.input.envelope) {
|
|
1273
|
+
const tool = ctx.input.envelope.tool;
|
|
1274
|
+
data.permission = {
|
|
1275
|
+
mode: ctx.input.envelope.permissionMode,
|
|
1276
|
+
tool: tool?.name,
|
|
1277
|
+
input: tool ? boundToolPayload(tool.input) : undefined
|
|
1278
|
+
};
|
|
1279
|
+
}
|
|
1280
|
+
return { data };
|
|
1281
|
+
}
|
|
1282
|
+
// PreCompact
|
|
1283
|
+
// Runs when: Context is about to be compacted (summarized)
|
|
1284
|
+
// Purpose: Save critical state before context is lost
|
|
1285
|
+
// Can inject: Yes - guidance for compaction
|
|
1286
|
+
async function handlePreCompact(ctx) {
|
|
1287
|
+
if (isLocalContinuityHook(ctx.input)) {
|
|
1288
|
+
if (!ctx.eventConfig.brain) {
|
|
1289
|
+
return { data: { skipped: 'brain disabled for local continuity' } };
|
|
1290
|
+
}
|
|
1291
|
+
return {
|
|
1292
|
+
data: await metabolizeExternalContinuity(ctx.runtime, ctx.input.envelope, 'pre-compaction')
|
|
1293
|
+
};
|
|
1294
|
+
}
|
|
1295
|
+
const data = {};
|
|
1296
|
+
// Consolidate brain memories before compaction
|
|
1297
|
+
if (ctx.eventConfig.brain) {
|
|
1298
|
+
const consolidateResult = await callMcpTool(ctx.mcp, 'brain', { action: 'consolidate' });
|
|
1299
|
+
if (consolidateResult) {
|
|
1300
|
+
data.consolidated = consolidateResult;
|
|
1301
|
+
}
|
|
1302
|
+
}
|
|
1303
|
+
// Generate context summary for compaction
|
|
1304
|
+
const summaryResult = await callMcpTool(ctx.mcp, 'external', {
|
|
1305
|
+
action: 'context-summary',
|
|
1306
|
+
format: 'compact',
|
|
1307
|
+
maxFiles: 15,
|
|
1308
|
+
includeTodos: true,
|
|
1309
|
+
includeScratchpad: false,
|
|
1310
|
+
timeWindowHours: 4
|
|
1311
|
+
});
|
|
1312
|
+
if (summaryResult) {
|
|
1313
|
+
data.summary = summaryResult;
|
|
1314
|
+
}
|
|
1315
|
+
return { data };
|
|
1316
|
+
}
|
|
1317
|
+
// Stop
|
|
1318
|
+
// Runs when: The host stops responding (completion, error, interrupt)
|
|
1319
|
+
// Purpose: Capture final session state
|
|
1320
|
+
// Can inject: the self-review nudge (data.delivery), rendered by the host's adapter
|
|
1321
|
+
//
|
|
1322
|
+
// Automatic actions invoked:
|
|
1323
|
+
// - session.status() - Capture final session state
|
|
1324
|
+
//
|
|
1325
|
+
// NO brain call here — deliberate, do not "fix" it back.
|
|
1326
|
+
// This handler used to call `brain { action: 'debrief' }`. `debrief` is NOT a brain
|
|
1327
|
+
// action (the real enum is `BrainActions` in presence/src/brain/tools/brain/schemas.ts:
|
|
1328
|
+
// pulse, prime, salience, coherence, consolidate, wake, track, focus, release,
|
|
1329
|
+
// distract, refocus, attention, plan, predict, outcome, stress, urgent, relax,
|
|
1330
|
+
// pressure), so end-of-turn reflection never ran — the hook advertised a
|
|
1331
|
+
// capability the runtime does not have, which is a lying projection
|
|
1332
|
+
// (docs/architecture/EXTERNAL-AS-RUNTIME-PROJECTION.md). It is removed rather than
|
|
1333
|
+
// re-pointed at `consolidate`: consolidate metabolizes content through an AI path and
|
|
1334
|
+
// writes durable memory, so firing it on every turn stop would install exactly the kind
|
|
1335
|
+
// of ambient content path that handleUserPromptSubmit documents as forbidden.
|
|
1336
|
+
// Metabolizing stays a deliberate reach the agent makes knowingly.
|
|
1337
|
+
async function handleStop(ctx) {
|
|
1338
|
+
const data = {};
|
|
1339
|
+
// NOTE: agent.said speech capture is NOT done here (see handleUserPromptSubmit) —
|
|
1340
|
+
// it is captured by `vibes presence say --role agent --from-transcript`, invoked
|
|
1341
|
+
// from the Stop hook, which loads the awareness storage and awaits the write.
|
|
1342
|
+
// Capture session state for continuity
|
|
1343
|
+
const sessionResult = await callMcpTool(ctx.mcp, 'session', { action: 'status' });
|
|
1344
|
+
if (sessionResult) {
|
|
1345
|
+
data.sessionSnapshot = sessionResult;
|
|
1346
|
+
}
|
|
1347
|
+
// A Stop hook re-run after its own block delivers nothing: Claude reports it with
|
|
1348
|
+
// `stop_hook_active`, and a second block would loop.
|
|
1349
|
+
if (ctx.input.envelope?.trigger === 'stop-hook-active')
|
|
1350
|
+
return { data };
|
|
1351
|
+
return { data: { ...data, ...(await deliveryData(ctx, 'agent.stop')) } };
|
|
1352
|
+
}
|
|
1353
|
+
// SubagentStop
|
|
1354
|
+
// Runs when: A subagent (Task tool) completes
|
|
1355
|
+
// Purpose: Process subagent results, update parent context
|
|
1356
|
+
// Can inject: Yes - can add summary of what subagent did
|
|
1357
|
+
//
|
|
1358
|
+
// DESIGN NOTE: Useful for:
|
|
1359
|
+
// - Aggregating results from parallel subagents
|
|
1360
|
+
// - Tracking subagent success/failure rates
|
|
1361
|
+
// - Injecting summaries back to parent context
|
|
1362
|
+
async function handleSubagentStop(ctx) {
|
|
1363
|
+
const data = {};
|
|
1364
|
+
const envelope = ctx.input.envelope;
|
|
1365
|
+
if (envelope?.subagent)
|
|
1366
|
+
data.subagent = envelope.subagent;
|
|
1367
|
+
if (envelope?.tool?.result !== undefined)
|
|
1368
|
+
data.result = boundToolPayload(envelope.tool.result);
|
|
1369
|
+
return { data };
|
|
1370
|
+
}
|
|
1371
|
+
// Notification
|
|
1372
|
+
// Runs when: The host sends a notification to the user
|
|
1373
|
+
// Purpose: Log notifications, trigger external alerts
|
|
1374
|
+
// Can inject: No - notification already sent
|
|
1375
|
+
//
|
|
1376
|
+
// DESIGN NOTE: Could be used for:
|
|
1377
|
+
// - Sending notifications to external systems (Slack, email)
|
|
1378
|
+
// - Logging important notifications for review
|
|
1379
|
+
// - Triggering webhooks
|
|
1380
|
+
async function handleNotification(ctx) {
|
|
1381
|
+
const data = {};
|
|
1382
|
+
if (ctx.input.envelope) {
|
|
1383
|
+
data.notification = {
|
|
1384
|
+
reason: ctx.input.envelope.reason,
|
|
1385
|
+
trigger: ctx.input.envelope.trigger
|
|
1386
|
+
};
|
|
1387
|
+
}
|
|
1388
|
+
return { data };
|
|
1389
|
+
}
|
|
1390
|
+
// Main Handler
|
|
1391
|
+
const EVENT_HANDLERS = {
|
|
1392
|
+
'session.start': handleSessionStart,
|
|
1393
|
+
'session.end': handleSessionEnd,
|
|
1394
|
+
'prompt.submit': handleUserPromptSubmit,
|
|
1395
|
+
'tool.before': handlePreToolUse,
|
|
1396
|
+
'tool.after': handlePostToolUse,
|
|
1397
|
+
'permission.request': handlePermissionRequest,
|
|
1398
|
+
'context.beforeCompact': handlePreCompact,
|
|
1399
|
+
'agent.stop': handleStop,
|
|
1400
|
+
'subagent.stop': handleSubagentStop,
|
|
1401
|
+
notification: handleNotification,
|
|
1402
|
+
// No work after compaction: the session.start that follows it wakes the body.
|
|
1403
|
+
'context.afterCompact': async () => ({ data: { skipped: 'no handler for event' } })
|
|
1404
|
+
};
|
|
1405
|
+
/** The invocation actor a normalized harness envelope declares. */
|
|
1406
|
+
export function hookInvocationOverlay(input) {
|
|
1407
|
+
const envelope = input.envelope;
|
|
1408
|
+
if (!envelope)
|
|
1409
|
+
return undefined;
|
|
1410
|
+
return {
|
|
1411
|
+
actor: {
|
|
1412
|
+
agentId: envelope.clientId,
|
|
1413
|
+
harness: envelope.clientId,
|
|
1414
|
+
harnessSessionId: envelope.nativeSessionId
|
|
1415
|
+
}
|
|
1416
|
+
};
|
|
1417
|
+
}
|
|
1418
|
+
/**
|
|
1419
|
+
* Execute hook for given event
|
|
1420
|
+
*
|
|
1421
|
+
* Loads configuration, checks if event is enabled, runs handler,
|
|
1422
|
+
* and assembles output with optional prompt injection.
|
|
1423
|
+
*/
|
|
1424
|
+
export async function executeHookCall(input, options) {
|
|
1425
|
+
try {
|
|
1426
|
+
const { runtime } = options;
|
|
1427
|
+
// Load configuration
|
|
1428
|
+
const config = await loadConfig(runtime, input.target);
|
|
1429
|
+
// Check if hooks are globally disabled
|
|
1430
|
+
if (!config.enabled) {
|
|
1431
|
+
return {
|
|
1432
|
+
success: true,
|
|
1433
|
+
event: input.event,
|
|
1434
|
+
data: { skipped: 'hooks disabled globally' }
|
|
1435
|
+
};
|
|
1436
|
+
}
|
|
1437
|
+
// Get event-specific configuration
|
|
1438
|
+
const eventConfig = getEventConfig(config, input.event, {
|
|
1439
|
+
disableBrain: input.disableBrain,
|
|
1440
|
+
disablePrompts: input.disablePrompts
|
|
1441
|
+
});
|
|
1442
|
+
// Check if this event is disabled
|
|
1443
|
+
if (!eventConfig.enabled) {
|
|
1444
|
+
return {
|
|
1445
|
+
success: true,
|
|
1446
|
+
event: input.event,
|
|
1447
|
+
data: { skipped: 'event disabled in config' }
|
|
1448
|
+
};
|
|
1449
|
+
}
|
|
1450
|
+
// Build handler context. The MCP client is created PER HOOK CALL and carries the
|
|
1451
|
+
// event's configured timeout as a whole-call budget — previously `eventConfig.timeout`
|
|
1452
|
+
// was computed and thrown away, so a wedged MCP body hung session start forever.
|
|
1453
|
+
// Shared resolver (VIBES_MCP_URL override + default) — a hard-coded MCP origin is
|
|
1454
|
+
// the wrong-endpoint injury class that broke the knowledge client (cefe08d2c).
|
|
1455
|
+
// (Resolving this capability from the runtime instead of over HTTP is the deeper
|
|
1456
|
+
// fix, tracked in the external audit.)
|
|
1457
|
+
const timeoutMs = eventConfig.timeout ?? 30000;
|
|
1458
|
+
const ctx = {
|
|
1459
|
+
runtime,
|
|
1460
|
+
input,
|
|
1461
|
+
eventConfig,
|
|
1462
|
+
mcp: options.mcpFactory
|
|
1463
|
+
? options.mcpFactory(timeoutMs)
|
|
1464
|
+
: createMcpClient({
|
|
1465
|
+
runtime,
|
|
1466
|
+
endpoint: await resolveLocalMcpUrlForRuntime(runtime, {
|
|
1467
|
+
workspaceRoot: await runtime.context('runtime/workspace-path'),
|
|
1468
|
+
vibesHome: getPathRoots(runtime).vibesHome
|
|
1469
|
+
}),
|
|
1470
|
+
timeoutMs
|
|
1471
|
+
})
|
|
1472
|
+
};
|
|
1473
|
+
// Run the handler
|
|
1474
|
+
const handler = EVENT_HANDLERS[input.event];
|
|
1475
|
+
let result;
|
|
1476
|
+
try {
|
|
1477
|
+
// The hook acts for the harness's agent: one invocation whose actor is
|
|
1478
|
+
// that agent and native session. Every organ call inside it (brain,
|
|
1479
|
+
// presence, continuity) resolves identity from this actor through the
|
|
1480
|
+
// runtime — never from the daemon's own environment or scope.
|
|
1481
|
+
result = await runtime.runInvocation(async () => {
|
|
1482
|
+
const client = tryGetVibesClient('external', runtime);
|
|
1483
|
+
const seat = client && input.envelope ? await resolveHookSeat(runtime, input.envelope) : null;
|
|
1484
|
+
return client && seat
|
|
1485
|
+
? client.withDeferredAmbientDelivery(seat.sessionId, () => handler(ctx))
|
|
1486
|
+
: handler(ctx);
|
|
1487
|
+
}, hookInvocationOverlay(input));
|
|
1488
|
+
}
|
|
1489
|
+
finally {
|
|
1490
|
+
// Release the MCP session even when a handler throws — this process is about to
|
|
1491
|
+
// exit and can never reuse it.
|
|
1492
|
+
await ctx.mcp.dispose();
|
|
1493
|
+
}
|
|
1494
|
+
// Get prompt for this event (if enabled)
|
|
1495
|
+
const targetDescriptor = input.target
|
|
1496
|
+
? runtime.query('external/host').withId(input.target).descriptors()[0]
|
|
1497
|
+
: undefined;
|
|
1498
|
+
const promptPolicy = targetDescriptor?.manifest.integration.hooks?.defaultPromptPolicy ?? 'default';
|
|
1499
|
+
const prompt = getPromptForEvent(input.event, eventConfig, promptPolicy);
|
|
1500
|
+
if (input.output === 'json') {
|
|
1501
|
+
return {
|
|
1502
|
+
success: true,
|
|
1503
|
+
event: input.event,
|
|
1504
|
+
data: {
|
|
1505
|
+
...result.data,
|
|
1506
|
+
config: {
|
|
1507
|
+
brain: eventConfig.brain,
|
|
1508
|
+
prompts: eventConfig.prompts
|
|
1509
|
+
}
|
|
1510
|
+
}
|
|
1511
|
+
};
|
|
1512
|
+
}
|
|
1513
|
+
// Text output: inject the guidance prompt, and for SessionStart PREPEND the
|
|
1514
|
+
// metadata body-truth scan so the agent SEES its body state (stale/ephemeral/
|
|
1515
|
+
// focus/coherence) at wake instead of only a static essay. The run already
|
|
1516
|
+
// happened in the handler; this stops discarding its (metadata-only) result.
|
|
1517
|
+
let context = prompt;
|
|
1518
|
+
if (input.event === 'session.start') {
|
|
1519
|
+
const continuity = result.data?.continuity;
|
|
1520
|
+
const scan = continuity
|
|
1521
|
+
? renderExternalContinuityContext(continuity)
|
|
1522
|
+
: renderWakeScan(result.data ?? {});
|
|
1523
|
+
// The project's conduct rules (its `workspace.rules`) steer a harness session as they steer every agent.
|
|
1524
|
+
const conduct = options.runtime.hasKind('rules/rule')
|
|
1525
|
+
? await (await import('@vibesdotdev/rules/rule/guidance')).conductText(options.runtime)
|
|
1526
|
+
: '';
|
|
1527
|
+
context = [scan, conduct, prompt].filter(Boolean).join('\n\n') || undefined;
|
|
1528
|
+
}
|
|
1529
|
+
if (result.data?.delivery)
|
|
1530
|
+
context = [context, result.data.delivery].filter(Boolean).join('\n\n');
|
|
1531
|
+
return {
|
|
1532
|
+
success: true,
|
|
1533
|
+
event: input.event,
|
|
1534
|
+
context,
|
|
1535
|
+
data: result.data
|
|
1536
|
+
};
|
|
1537
|
+
}
|
|
1538
|
+
catch (error) {
|
|
1539
|
+
// The harness reads `error`: a typed refusal by its own value-free message, anything else by its facts.
|
|
1540
|
+
const failure = CaughtFailureSchema.parse(error);
|
|
1541
|
+
getLogger('external.hooks', options.runtime).warn('external hook failed', { event: input.event, failure: valueFreeFailure(failure) });
|
|
1542
|
+
return {
|
|
1543
|
+
success: false,
|
|
1544
|
+
event: input.event,
|
|
1545
|
+
error: failureReason(failure),
|
|
1546
|
+
failure: valueFreeFailure(CaughtFailureSchema.parse(error))
|
|
1547
|
+
};
|
|
1548
|
+
}
|
|
1549
|
+
}
|
|
1550
|
+
//# sourceMappingURL=call.js.map
|