From 27fc64548407833e04a00455e373ad8a10d2c403 Mon Sep 17 00:00:00 2001 From: Gustavo Madeira Santana Date: Sun, 15 Mar 2026 16:55:12 +0000 Subject: [PATCH] Plugins: extend registry writes for hooks --- src/extension-host/cutover-inventory.md | 116 ++++++++++----------- src/extension-host/registry-writes.test.ts | 49 ++++++++- src/extension-host/registry-writes.ts | 38 +++++++ src/plugins/registry.ts | 35 ++++--- 4 files changed, 166 insertions(+), 72 deletions(-) diff --git a/src/extension-host/cutover-inventory.md b/src/extension-host/cutover-inventory.md index c7d00f9c4c3..fe13f47002a 100644 --- a/src/extension-host/cutover-inventory.md +++ b/src/extension-host/cutover-inventory.md @@ -25,64 +25,64 @@ This is an implementation checklist, not a future-design spec. ## Current Inventory -| Surface | Current implementation | Target owner | Status | How it has been handled so far | -| ------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- | ------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Active runtime registry state | `src/plugins/runtime.ts` plus global plugin runtime state | `src/extension-host/active-registry.ts` | `moved` | Host-owned active registry exists; `src/plugins/runtime.ts` is now a compatibility facade. | -| Normalized extension descriptor model | plugin manifests and package metadata interpreted ad hoc across `src/plugins/manifest.ts`, `src/plugins/discovery.ts`, `src/plugins/install.ts` | `src/extension-host/schema.ts` | `partial` | `ResolvedExtension`, `ResolvedContribution`, and `ContributionPolicy` exist; current manifests project into them through compatibility adapters. | -| Resolved static registry | flat rows in `src/plugins/manifest-registry.ts` | `src/extension-host/resolved-registry.ts` | `partial` | Manifest records now carry `resolvedExtension`; a host-owned resolved registry view exists for static consumers. | -| Manifest/package metadata loading | `src/plugins/manifest.ts`, `src/plugins/discovery.ts`, `src/plugins/install.ts` | `src/extension-host/schema.ts` and `src/extension-host/manifest-registry.ts` | `partial` | Package metadata parsing is routed through host schema helpers; legacy loader flow still supplies the source manifests. | -| Loader SDK alias compatibility | `src/plugins/loader.ts` | `src/extension-host/loader-compat.ts` | `partial` | Plugin-SDK alias candidate ordering, alias-file resolution, and scoped alias-map construction now live in host-owned loader compatibility helpers. | -| Loader alias-wired module loader creation | `src/plugins/loader.ts` | `src/extension-host/loader-module-loader.ts` | `partial` | Lazy Jiti creation and SDK-alias-wired module loading now delegate through a host-owned loader-module-loader helper. | -| Loader cache key and registry cache control | `src/plugins/loader.ts` | `src/extension-host/loader-cache.ts` | `partial` | Cache-key construction, LRU registry cache reads and writes, and cache clearing now delegate through host-owned loader-cache helpers while preserving the current cache shape and cap. | -| Loader lazy runtime proxy creation | `src/plugins/loader.ts` | `src/extension-host/loader-runtime-proxy.ts` | `partial` | Lazy plugin runtime creation now delegates through a host-owned loader-runtime-proxy helper instead of remaining inline in the orchestrator. | -| Loader provenance and duplicate-order policy | `src/plugins/loader.ts` | `src/extension-host/loader-policy.ts` | `partial` | Plugin-record creation, duplicate precedence, and provenance indexing now live in host-owned loader-policy helpers. | -| Loader discovery policy results | mixed inside `src/extension-host/loader-policy.ts` and `src/extension-host/loader-orchestrator.ts` | `src/extension-host/loader-discovery-policy.ts` | `partial` | Open-allowlist discovery warnings now resolve through explicit host-owned discovery-policy results before the orchestrator logs them. | -| Loader initial candidate planning and record creation | `src/plugins/loader.ts` | `src/extension-host/loader-records.ts` | `partial` | Duplicate detection, initial record creation, manifest metadata attachment, and first-pass enable-state planning now delegate through host-owned loader-records helpers. | -| Loader entry-path opening and module import | `src/plugins/loader.ts` | `src/extension-host/loader-import.ts` | `partial` | Boundary-checked entry opening and module import now delegate through host-owned loader-import helpers while preserving the current trusted in-process loading model. | -| Loader module-export, config-validation, and memory-slot decisions | `src/plugins/loader.ts` | `src/extension-host/loader-runtime.ts` | `partial` | Module export resolution, export-metadata application, config validation, and early or final memory-slot decisions now delegate through host-owned loader-runtime helpers. | -| Loader post-import planning and register execution | `src/plugins/loader.ts` | `src/extension-host/loader-register.ts` | `partial` | Definition application, post-import validation planning, and `register(...)` execution now delegate through host-owned loader-register helpers while preserving current plugin behavior. | -| Loader per-candidate orchestration | `src/plugins/loader.ts` | `src/extension-host/loader-flow.ts` | `partial` | The per-candidate load flow now runs through a host-owned orchestrator that composes planning, import, runtime validation, register execution, and record-state helpers. | -| Loader top-level load orchestration | `src/plugins/loader.ts` | `src/extension-host/loader-orchestrator.ts` | `partial` | High-level load entry and compatibility facade behavior now route through a host-owned loader orchestrator while `src/plugins/loader.ts` remains the external compatibility surface. | -| Loader host process state | mixed inside `src/plugins/loader.ts` and `src/extension-host/loader-orchestrator.ts` | `src/extension-host/loader-host-state.ts` | `partial` | Shared discovery warning-cache state and loader reset behavior now delegate through a host-owned loader-host-state helper. | -| Loader preflight and cache-hit setup | mixed inside `src/plugins/loader.ts` and `src/extension-host/loader-orchestrator.ts` | `src/extension-host/loader-preflight.ts` | `partial` | Test-default application, config normalization, cache-key construction, cache-hit activation, and command-clear preflight now delegate through a host-owned loader-preflight helper. | -| Loader post-preflight pipeline composition | mixed inside `src/plugins/loader.ts` and `src/extension-host/loader-orchestrator.ts` | `src/extension-host/loader-pipeline.ts` | `partial` | Post-preflight execution setup and session-run composition now delegate through a host-owned loader-pipeline helper. | -| Loader execution setup composition | mixed inside `src/plugins/loader.ts` and `src/extension-host/loader-orchestrator.ts` | `src/extension-host/loader-execution.ts` | `partial` | Runtime creation, registry creation, bootstrap setup, module-loader creation, and session creation now delegate through a host-owned loader-execution helper. | -| Loader discovery and manifest bootstrap | mixed inside `src/plugins/loader.ts` and `src/extension-host/loader-orchestrator.ts` | `src/extension-host/loader-bootstrap.ts` | `partial` | Discovery, manifest loading, manifest diagnostics, discovery-policy logging, provenance building, and candidate ordering now delegate through a host-owned loader-bootstrap helper. | -| Loader mutable activation state session | local variables in `src/extension-host/loader-orchestrator.ts` | `src/extension-host/loader-session.ts` | `partial` | Seen-id tracking, memory-slot selection state, and finalization inputs now live in a host-owned loader session instead of being spread across top-level loader variables. | -| Loader session run and finalization composition | mixed inside `src/extension-host/loader-orchestrator.ts` and `src/extension-host/loader-session.ts` | `src/extension-host/loader-run.ts` | `partial` | Candidate iteration, manifest lookup, per-candidate session processing, and finalization handoff now delegate through a host-owned loader-run helper. | -| Loader activation policy outcomes | open-coded in `src/extension-host/loader-flow.ts` | `src/extension-host/loader-activation-policy.ts` | `partial` | Duplicate precedence, config enablement, and early memory-slot gating now resolve through explicit host-owned activation-policy outcomes instead of remaining as inline loader decisions. | -| Loader record-state transitions | `src/plugins/loader.ts` | `src/extension-host/loader-state.ts` | `partial` | The loader now enforces an explicit lifecycle transition model (`prepared -> imported -> validated -> registered -> ready`, plus terminal `disabled` and `error`) while still mapping back to compatibility `PluginRecord.status` values. | -| Loader finalization policy results | mixed inside `src/extension-host/loader-policy.ts` and `src/extension-host/loader-finalize.ts` | `src/extension-host/loader-finalization-policy.ts` | `partial` | Memory-slot finalization warnings and provenance-based untracked-extension warnings now resolve through explicit host-owned finalization-policy results before the finalizer applies them. | -| Loader final cache, readiness, and activation finalization | `src/plugins/loader.ts` | `src/extension-host/loader-finalize.ts` | `partial` | Cache writes, readiness promotion, and registry activation now delegate through a host-owned loader-finalize helper; broader host lifecycle and policy semantics are still pending. | -| Channel lookup | `src/channels/plugins/index.ts`, `src/channels/plugins/registry-loader.ts`, `src/channels/registry.ts` | extension-host-backed registries plus kernel channel contracts | `partial` | Readers now consume the host-owned active registry, but writes still originate from plugin registration. | -| Dock lookup | `src/channels/dock.ts` | host-owned static descriptors | `partial` | Runtime lookup now uses the host boundary; dock ownership itself has not moved yet. | -| Message-channel normalization | `src/utils/message-channel.ts` | host-owned channel registry view | `partial` | Lookup path now reads through the host-owned active registry. | -| Default plugin HTTP route lookup | `src/plugins/http-registry.ts` | host-owned route registry | `partial` | Default registry resolution now uses the host boundary; route registration compatibility still flows through the legacy plugin API. | -| Channel catalog static metadata | `src/channels/plugins/catalog.ts` | host-owned static descriptors | `partial` | Package metadata parsing now flows through host schema helpers; full canonical catalog migration has not started. | -| Plugin skill discovery | `src/agents/skills/plugin-skills.ts` | host-owned resolved registry | `moved` | Static consumer now reads only resolved-extension data for skill paths and enablement filtering. | -| Plugin auto-enable | `src/config/plugin-auto-enable.ts` | host-owned resolved registry | `partial` | Primary logic runs on resolved-extension data; old manifest-registry injection remains as a compatibility input for older callers and tests. | -| Config validation indexing | `src/config/validation.ts`, `src/config/resolved-extension-validation.ts` | host-owned resolved registry | `moved` | Validation indexing now builds from resolved-extension records instead of flat manifest rows. | -| Config doc baseline generation | `src/config/doc-baseline.ts` | host-owned resolved registry | `moved` | Bundled plugin and channel metadata now load through the resolved-extension registry. | -| Plugin loader activation | `src/plugins/loader.ts` | extension host lifecycle + compatibility loader | `partial` | Activation now routes through `src/extension-host/activation.ts`, but discovery, enablement, provenance, module loading, and policy still live in the legacy plugin loader. | -| Channel registration writes | `src/plugins/registry.ts` | host-owned channel registry | `partial` | Validation and normalization now delegate to `src/extension-host/runtime-registrations.ts`, and compatibility writes now route through `src/extension-host/registry-writes.ts`; the legacy plugin API still remains the call surface. | -| Provider registration writes | `src/plugins/registry.ts` | host-owned provider registry | `partial` | Provider normalization still happens in plugin-era validation, duplicate detection and normalized registration shape now delegate to `src/extension-host/runtime-registrations.ts`, and compatibility writes now route through `src/extension-host/registry-writes.ts`. | -| HTTP route registration writes | `src/plugins/registry.ts` | host-owned route registry | `partial` | Route validation and normalization now delegate to `src/extension-host/runtime-registrations.ts`, and compatibility append or replace writes now route through `src/extension-host/registry-writes.ts`. | -| Gateway method registration writes | `src/plugins/registry.ts` | host-owned runtime contribution registry | `partial` | Duplicate detection and normalized method registration now delegate to `src/extension-host/runtime-registrations.ts`, and compatibility writes now route through `src/extension-host/registry-writes.ts`. | -| Tool registration writes | `src/plugins/registry.ts` | host-owned tool registry | `partial` | Tool-name normalization and tool-factory shaping delegate through `src/extension-host/runtime-registrations.ts`, and low-risk compatibility writes now route through `src/extension-host/registry-writes.ts`; duplicate handling still follows the legacy tool path. | -| CLI registration writes | `src/plugins/registry.ts` | host-owned CLI registry | `partial` | CLI command-name normalization delegates through `src/extension-host/runtime-registrations.ts`, and compatibility writes now route through `src/extension-host/registry-writes.ts`; the legacy plugin API still remains the call surface. | -| Service registration writes | `src/plugins/registry.ts` | host-owned service registry | `partial` | Service-id normalization delegates through `src/extension-host/runtime-registrations.ts`, and compatibility writes now route through `src/extension-host/registry-writes.ts`; lifecycle remains legacy-owned. | -| Command registration writes | `src/plugins/registry.ts` | host-owned command registry | `partial` | Command-name normalization delegates through `src/extension-host/runtime-registrations.ts`, duplicate enforcement still depends on the legacy plugin command registry, and the final compatibility write now routes through `src/extension-host/registry-writes.ts`. | -| Context-engine registration writes | `src/plugins/registry.ts` | host-owned context-engine registry | `partial` | Context-engine id normalization now delegates to `src/extension-host/runtime-registrations.ts`, but the actual context-engine registry remains legacy-owned. | -| Legacy hook registration writes | `src/plugins/registry.ts` | host-owned hook registry | `partial` | Hook-entry construction and event normalization now delegate to `src/extension-host/runtime-registrations.ts`, but internal-hook bridging still remains in the legacy plugin registry. | -| Typed-hook registration writes | `src/plugins/registry.ts` | host-owned typed-hook registry | `partial` | Typed-hook record construction and hook-name validation now delegate to `src/extension-host/runtime-registrations.ts`, but prompt-injection policy and execution semantics remain legacy-owned. | -| Hook execution and global runner | `src/plugins/hook-runner-global.ts`, `src/hooks/internal-hooks.ts`, plugin hook registration in `src/plugins/registry.ts` | canonical kernel event stages + host bridges | `not started` | No canonical event-stage migration has landed yet. | -| Service lifecycle | `src/plugins/services.ts` and plugin service registration | extension host lifecycle | `not started` | Service startup and teardown still depend on legacy plugin registry/service ownership. | -| CLI registration | plugin CLI registration in `src/plugins/registry.ts` and CLI loaders | extension host registry + static descriptors where possible | `not started` | No host-owned CLI registry exists yet. | -| Gateway/server methods | `src/plugins/registry.ts` gateway handler registration | host-owned runtime contribution registry | `not started` | Still registered directly into the legacy plugin registry. | -| Slot arbitration | `src/plugins/slots.ts` | host-owned arbitration model | `not started` | Current slot selection remains plugin-era logic. | -| ACP backend registry | `src/acp/runtime/registry.ts` | host-owned runtime-backend registry | `not started` | ACP backends still mutate a global ACP runtime registry directly. | -| Onboarding/install/setup surfaces | `src/plugins/install.ts`, package manifests, channel catalog, onboarding commands | host-owned static descriptors | `partial` | Static metadata normalization has started; full setup/install descriptor migration is not done. | -| Pilot migrations | `extensions/thread-ownership`, `extensions/telegram`, `extensions/acpx` | extension-host path with parity tracking | `not started` | No pilot runs through the host path yet. | +| Surface | Current implementation | Target owner | Status | How it has been handled so far | +| ------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- | ------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Active runtime registry state | `src/plugins/runtime.ts` plus global plugin runtime state | `src/extension-host/active-registry.ts` | `moved` | Host-owned active registry exists; `src/plugins/runtime.ts` is now a compatibility facade. | +| Normalized extension descriptor model | plugin manifests and package metadata interpreted ad hoc across `src/plugins/manifest.ts`, `src/plugins/discovery.ts`, `src/plugins/install.ts` | `src/extension-host/schema.ts` | `partial` | `ResolvedExtension`, `ResolvedContribution`, and `ContributionPolicy` exist; current manifests project into them through compatibility adapters. | +| Resolved static registry | flat rows in `src/plugins/manifest-registry.ts` | `src/extension-host/resolved-registry.ts` | `partial` | Manifest records now carry `resolvedExtension`; a host-owned resolved registry view exists for static consumers. | +| Manifest/package metadata loading | `src/plugins/manifest.ts`, `src/plugins/discovery.ts`, `src/plugins/install.ts` | `src/extension-host/schema.ts` and `src/extension-host/manifest-registry.ts` | `partial` | Package metadata parsing is routed through host schema helpers; legacy loader flow still supplies the source manifests. | +| Loader SDK alias compatibility | `src/plugins/loader.ts` | `src/extension-host/loader-compat.ts` | `partial` | Plugin-SDK alias candidate ordering, alias-file resolution, and scoped alias-map construction now live in host-owned loader compatibility helpers. | +| Loader alias-wired module loader creation | `src/plugins/loader.ts` | `src/extension-host/loader-module-loader.ts` | `partial` | Lazy Jiti creation and SDK-alias-wired module loading now delegate through a host-owned loader-module-loader helper. | +| Loader cache key and registry cache control | `src/plugins/loader.ts` | `src/extension-host/loader-cache.ts` | `partial` | Cache-key construction, LRU registry cache reads and writes, and cache clearing now delegate through host-owned loader-cache helpers while preserving the current cache shape and cap. | +| Loader lazy runtime proxy creation | `src/plugins/loader.ts` | `src/extension-host/loader-runtime-proxy.ts` | `partial` | Lazy plugin runtime creation now delegates through a host-owned loader-runtime-proxy helper instead of remaining inline in the orchestrator. | +| Loader provenance and duplicate-order policy | `src/plugins/loader.ts` | `src/extension-host/loader-policy.ts` | `partial` | Plugin-record creation, duplicate precedence, and provenance indexing now live in host-owned loader-policy helpers. | +| Loader discovery policy results | mixed inside `src/extension-host/loader-policy.ts` and `src/extension-host/loader-orchestrator.ts` | `src/extension-host/loader-discovery-policy.ts` | `partial` | Open-allowlist discovery warnings now resolve through explicit host-owned discovery-policy results before the orchestrator logs them. | +| Loader initial candidate planning and record creation | `src/plugins/loader.ts` | `src/extension-host/loader-records.ts` | `partial` | Duplicate detection, initial record creation, manifest metadata attachment, and first-pass enable-state planning now delegate through host-owned loader-records helpers. | +| Loader entry-path opening and module import | `src/plugins/loader.ts` | `src/extension-host/loader-import.ts` | `partial` | Boundary-checked entry opening and module import now delegate through host-owned loader-import helpers while preserving the current trusted in-process loading model. | +| Loader module-export, config-validation, and memory-slot decisions | `src/plugins/loader.ts` | `src/extension-host/loader-runtime.ts` | `partial` | Module export resolution, export-metadata application, config validation, and early or final memory-slot decisions now delegate through host-owned loader-runtime helpers. | +| Loader post-import planning and register execution | `src/plugins/loader.ts` | `src/extension-host/loader-register.ts` | `partial` | Definition application, post-import validation planning, and `register(...)` execution now delegate through host-owned loader-register helpers while preserving current plugin behavior. | +| Loader per-candidate orchestration | `src/plugins/loader.ts` | `src/extension-host/loader-flow.ts` | `partial` | The per-candidate load flow now runs through a host-owned orchestrator that composes planning, import, runtime validation, register execution, and record-state helpers. | +| Loader top-level load orchestration | `src/plugins/loader.ts` | `src/extension-host/loader-orchestrator.ts` | `partial` | High-level load entry and compatibility facade behavior now route through a host-owned loader orchestrator while `src/plugins/loader.ts` remains the external compatibility surface. | +| Loader host process state | mixed inside `src/plugins/loader.ts` and `src/extension-host/loader-orchestrator.ts` | `src/extension-host/loader-host-state.ts` | `partial` | Shared discovery warning-cache state and loader reset behavior now delegate through a host-owned loader-host-state helper. | +| Loader preflight and cache-hit setup | mixed inside `src/plugins/loader.ts` and `src/extension-host/loader-orchestrator.ts` | `src/extension-host/loader-preflight.ts` | `partial` | Test-default application, config normalization, cache-key construction, cache-hit activation, and command-clear preflight now delegate through a host-owned loader-preflight helper. | +| Loader post-preflight pipeline composition | mixed inside `src/plugins/loader.ts` and `src/extension-host/loader-orchestrator.ts` | `src/extension-host/loader-pipeline.ts` | `partial` | Post-preflight execution setup and session-run composition now delegate through a host-owned loader-pipeline helper. | +| Loader execution setup composition | mixed inside `src/plugins/loader.ts` and `src/extension-host/loader-orchestrator.ts` | `src/extension-host/loader-execution.ts` | `partial` | Runtime creation, registry creation, bootstrap setup, module-loader creation, and session creation now delegate through a host-owned loader-execution helper. | +| Loader discovery and manifest bootstrap | mixed inside `src/plugins/loader.ts` and `src/extension-host/loader-orchestrator.ts` | `src/extension-host/loader-bootstrap.ts` | `partial` | Discovery, manifest loading, manifest diagnostics, discovery-policy logging, provenance building, and candidate ordering now delegate through a host-owned loader-bootstrap helper. | +| Loader mutable activation state session | local variables in `src/extension-host/loader-orchestrator.ts` | `src/extension-host/loader-session.ts` | `partial` | Seen-id tracking, memory-slot selection state, and finalization inputs now live in a host-owned loader session instead of being spread across top-level loader variables. | +| Loader session run and finalization composition | mixed inside `src/extension-host/loader-orchestrator.ts` and `src/extension-host/loader-session.ts` | `src/extension-host/loader-run.ts` | `partial` | Candidate iteration, manifest lookup, per-candidate session processing, and finalization handoff now delegate through a host-owned loader-run helper. | +| Loader activation policy outcomes | open-coded in `src/extension-host/loader-flow.ts` | `src/extension-host/loader-activation-policy.ts` | `partial` | Duplicate precedence, config enablement, and early memory-slot gating now resolve through explicit host-owned activation-policy outcomes instead of remaining as inline loader decisions. | +| Loader record-state transitions | `src/plugins/loader.ts` | `src/extension-host/loader-state.ts` | `partial` | The loader now enforces an explicit lifecycle transition model (`prepared -> imported -> validated -> registered -> ready`, plus terminal `disabled` and `error`) while still mapping back to compatibility `PluginRecord.status` values. | +| Loader finalization policy results | mixed inside `src/extension-host/loader-policy.ts` and `src/extension-host/loader-finalize.ts` | `src/extension-host/loader-finalization-policy.ts` | `partial` | Memory-slot finalization warnings and provenance-based untracked-extension warnings now resolve through explicit host-owned finalization-policy results before the finalizer applies them. | +| Loader final cache, readiness, and activation finalization | `src/plugins/loader.ts` | `src/extension-host/loader-finalize.ts` | `partial` | Cache writes, readiness promotion, and registry activation now delegate through a host-owned loader-finalize helper; broader host lifecycle and policy semantics are still pending. | +| Channel lookup | `src/channels/plugins/index.ts`, `src/channels/plugins/registry-loader.ts`, `src/channels/registry.ts` | extension-host-backed registries plus kernel channel contracts | `partial` | Readers now consume the host-owned active registry, but writes still originate from plugin registration. | +| Dock lookup | `src/channels/dock.ts` | host-owned static descriptors | `partial` | Runtime lookup now uses the host boundary; dock ownership itself has not moved yet. | +| Message-channel normalization | `src/utils/message-channel.ts` | host-owned channel registry view | `partial` | Lookup path now reads through the host-owned active registry. | +| Default plugin HTTP route lookup | `src/plugins/http-registry.ts` | host-owned route registry | `partial` | Default registry resolution now uses the host boundary; route registration compatibility still flows through the legacy plugin API. | +| Channel catalog static metadata | `src/channels/plugins/catalog.ts` | host-owned static descriptors | `partial` | Package metadata parsing now flows through host schema helpers; full canonical catalog migration has not started. | +| Plugin skill discovery | `src/agents/skills/plugin-skills.ts` | host-owned resolved registry | `moved` | Static consumer now reads only resolved-extension data for skill paths and enablement filtering. | +| Plugin auto-enable | `src/config/plugin-auto-enable.ts` | host-owned resolved registry | `partial` | Primary logic runs on resolved-extension data; old manifest-registry injection remains as a compatibility input for older callers and tests. | +| Config validation indexing | `src/config/validation.ts`, `src/config/resolved-extension-validation.ts` | host-owned resolved registry | `moved` | Validation indexing now builds from resolved-extension records instead of flat manifest rows. | +| Config doc baseline generation | `src/config/doc-baseline.ts` | host-owned resolved registry | `moved` | Bundled plugin and channel metadata now load through the resolved-extension registry. | +| Plugin loader activation | `src/plugins/loader.ts` | extension host lifecycle + compatibility loader | `partial` | Activation now routes through `src/extension-host/activation.ts`, but discovery, enablement, provenance, module loading, and policy still live in the legacy plugin loader. | +| Channel registration writes | `src/plugins/registry.ts` | host-owned channel registry | `partial` | Validation and normalization now delegate to `src/extension-host/runtime-registrations.ts`, and compatibility writes now route through `src/extension-host/registry-writes.ts`; the legacy plugin API still remains the call surface. | +| Provider registration writes | `src/plugins/registry.ts` | host-owned provider registry | `partial` | Provider normalization still happens in plugin-era validation, duplicate detection and normalized registration shape now delegate to `src/extension-host/runtime-registrations.ts`, and compatibility writes now route through `src/extension-host/registry-writes.ts`. | +| HTTP route registration writes | `src/plugins/registry.ts` | host-owned route registry | `partial` | Route validation and normalization now delegate to `src/extension-host/runtime-registrations.ts`, and compatibility append or replace writes now route through `src/extension-host/registry-writes.ts`. | +| Gateway method registration writes | `src/plugins/registry.ts` | host-owned runtime contribution registry | `partial` | Duplicate detection and normalized method registration now delegate to `src/extension-host/runtime-registrations.ts`, and compatibility writes now route through `src/extension-host/registry-writes.ts`. | +| Tool registration writes | `src/plugins/registry.ts` | host-owned tool registry | `partial` | Tool-name normalization and tool-factory shaping delegate through `src/extension-host/runtime-registrations.ts`, and low-risk compatibility writes now route through `src/extension-host/registry-writes.ts`; duplicate handling still follows the legacy tool path. | +| CLI registration writes | `src/plugins/registry.ts` | host-owned CLI registry | `partial` | CLI command-name normalization delegates through `src/extension-host/runtime-registrations.ts`, and compatibility writes now route through `src/extension-host/registry-writes.ts`; the legacy plugin API still remains the call surface. | +| Service registration writes | `src/plugins/registry.ts` | host-owned service registry | `partial` | Service-id normalization delegates through `src/extension-host/runtime-registrations.ts`, and compatibility writes now route through `src/extension-host/registry-writes.ts`; lifecycle remains legacy-owned. | +| Command registration writes | `src/plugins/registry.ts` | host-owned command registry | `partial` | Command-name normalization delegates through `src/extension-host/runtime-registrations.ts`, duplicate enforcement still depends on the legacy plugin command registry, and the final compatibility write now routes through `src/extension-host/registry-writes.ts`. | +| Context-engine registration writes | `src/plugins/registry.ts` | host-owned context-engine registry | `partial` | Context-engine id normalization now delegates to `src/extension-host/runtime-registrations.ts`, and the compatibility write now routes through `src/extension-host/registry-writes.ts`; the actual context-engine registry remains legacy-owned. | +| Legacy hook registration writes | `src/plugins/registry.ts` | host-owned hook registry | `partial` | Hook-entry construction and event normalization now delegate to `src/extension-host/runtime-registrations.ts`, and the compatibility write now routes through `src/extension-host/registry-writes.ts`; internal-hook bridging still remains in the legacy plugin registry. | +| Typed-hook registration writes | `src/plugins/registry.ts` | host-owned typed-hook registry | `partial` | Typed-hook record construction and hook-name validation now delegate to `src/extension-host/runtime-registrations.ts`, and the compatibility write now routes through `src/extension-host/registry-writes.ts`; prompt-injection policy and execution semantics remain legacy-owned. | +| Hook execution and global runner | `src/plugins/hook-runner-global.ts`, `src/hooks/internal-hooks.ts`, plugin hook registration in `src/plugins/registry.ts` | canonical kernel event stages + host bridges | `not started` | No canonical event-stage migration has landed yet. | +| Service lifecycle | `src/plugins/services.ts` and plugin service registration | extension host lifecycle | `not started` | Service startup and teardown still depend on legacy plugin registry/service ownership. | +| CLI registration | plugin CLI registration in `src/plugins/registry.ts` and CLI loaders | extension host registry + static descriptors where possible | `not started` | No host-owned CLI registry exists yet. | +| Gateway/server methods | `src/plugins/registry.ts` gateway handler registration | host-owned runtime contribution registry | `not started` | Still registered directly into the legacy plugin registry. | +| Slot arbitration | `src/plugins/slots.ts` | host-owned arbitration model | `not started` | Current slot selection remains plugin-era logic. | +| ACP backend registry | `src/acp/runtime/registry.ts` | host-owned runtime-backend registry | `not started` | ACP backends still mutate a global ACP runtime registry directly. | +| Onboarding/install/setup surfaces | `src/plugins/install.ts`, package manifests, channel catalog, onboarding commands | host-owned static descriptors | `partial` | Static metadata normalization has started; full setup/install descriptor migration is not done. | +| Pilot migrations | `extensions/thread-ownership`, `extensions/telegram`, `extensions/acpx` | extension-host path with parity tracking | `not started` | No pilot runs through the host path yet. | ## Completed Pattern So Far diff --git a/src/extension-host/registry-writes.test.ts b/src/extension-host/registry-writes.test.ts index 9261f93af88..f8ba5154da7 100644 --- a/src/extension-host/registry-writes.test.ts +++ b/src/extension-host/registry-writes.test.ts @@ -1,14 +1,17 @@ -import { describe, expect, it } from "vitest"; +import { describe, expect, it, vi } from "vitest"; import { createEmptyPluginRegistry, type PluginRecord } from "../plugins/registry.js"; import { addExtensionChannelRegistration, addExtensionCliRegistration, addExtensionCommandRegistration, + addExtensionContextEngineRegistration, addExtensionGatewayMethodRegistration, + addExtensionLegacyHookRegistration, addExtensionHttpRouteRegistration, addExtensionProviderRegistration, addExtensionServiceRegistration, addExtensionToolRegistration, + addExtensionTypedHookRegistration, } from "./registry-writes.js"; function createRecord(): PluginRecord { @@ -152,4 +155,48 @@ describe("extension host registry writes", () => { expect(registry.channels).toHaveLength(1); expect(registry.providers).toHaveLength(1); }); + + it("writes legacy hooks, typed hooks, and context engines through host helpers", () => { + const registry = createEmptyPluginRegistry(); + const record = createRecord(); + const registerEngine = vi.fn(); + + addExtensionLegacyHookRegistration({ + registry, + record, + hookName: "before_send", + events: ["before_send"], + entry: { + pluginId: record.id, + entry: {} as never, + events: ["before_send"], + source: record.source, + handler: (() => {}) as never, + }, + }); + addExtensionTypedHookRegistration({ + registry, + record, + entry: { + pluginId: record.id, + hookName: "before_send" as never, + handler: (() => {}) as never, + priority: 0, + source: record.source, + } as never, + }); + addExtensionContextEngineRegistration({ + entry: { + engineId: "context-demo", + factory: (() => ({}) as never) as never, + }, + registerEngine, + }); + + expect(record.hookNames).toEqual(["before_send"]); + expect(record.hookCount).toBe(1); + expect(registry.hooks).toHaveLength(1); + expect(registry.typedHooks).toHaveLength(1); + expect(registerEngine).toHaveBeenCalledWith("context-demo", expect.any(Function)); + }); }); diff --git a/src/extension-host/registry-writes.ts b/src/extension-host/registry-writes.ts index 15af63ccc17..b293f8af49b 100644 --- a/src/extension-host/registry-writes.ts +++ b/src/extension-host/registry-writes.ts @@ -1,8 +1,10 @@ +import { registerContextEngine, type ContextEngineFactory } from "../context-engine/registry.js"; import type { GatewayRequestHandler } from "../gateway/server-methods/types.js"; import type { PluginChannelRegistration, PluginCliRegistration, PluginCommandRegistration, + PluginHookRegistration, PluginHttpRouteRegistration, PluginRecord, PluginRegistry, @@ -10,10 +12,13 @@ import type { PluginServiceRegistration, PluginToolRegistration, } from "../plugins/registry.js"; +import type { PluginHookRegistration as TypedPluginHookRegistration } from "../plugins/types.js"; import type { ExtensionHostChannelRegistration, ExtensionHostCliRegistration, ExtensionHostCommandRegistration, + ExtensionHostContextEngineRegistration, + ExtensionHostLegacyHookRegistration, ExtensionHostHttpRouteRegistration, ExtensionHostProviderRegistration, ExtensionHostServiceRegistration, @@ -69,6 +74,31 @@ export function addExtensionProviderRegistration(params: { params.registry.providers.push(params.entry as PluginProviderRegistration); } +export function addExtensionLegacyHookRegistration(params: { + registry: PluginRegistry; + record: PluginRecord; + hookName: string; + entry: ExtensionHostLegacyHookRegistration; + events: string[]; +}): void { + params.record.hookNames.push(params.hookName); + params.registry.hooks.push({ + pluginId: params.entry.pluginId, + entry: params.entry.entry, + events: params.events, + source: params.entry.source, + } as PluginHookRegistration); +} + +export function addExtensionTypedHookRegistration(params: { + registry: PluginRegistry; + record: PluginRecord; + entry: TypedPluginHookRegistration; +}): void { + params.record.hookCount += 1; + params.registry.typedHooks.push(params.entry); +} + export function addExtensionToolRegistration(params: { registry: PluginRegistry; record: PluginRecord; @@ -110,3 +140,11 @@ export function addExtensionCommandRegistration(params: { params.record.commands.push(params.commandName); params.registry.commands.push(params.entry as PluginCommandRegistration); } + +export function addExtensionContextEngineRegistration(params: { + entry: ExtensionHostContextEngineRegistration; + registerEngine?: (engineId: string, factory: ContextEngineFactory) => void; +}): void { + const registerEngine = params.registerEngine ?? registerContextEngine; + registerEngine(params.entry.engineId, params.entry.factory); +} diff --git a/src/plugins/registry.ts b/src/plugins/registry.ts index 5b640bc78a4..96971333efe 100644 --- a/src/plugins/registry.ts +++ b/src/plugins/registry.ts @@ -6,11 +6,14 @@ import { addExtensionChannelRegistration, addExtensionCliRegistration, addExtensionCommandRegistration, + addExtensionContextEngineRegistration, addExtensionGatewayMethodRegistration, + addExtensionLegacyHookRegistration, addExtensionHttpRouteRegistration, addExtensionProviderRegistration, addExtensionServiceRegistration, addExtensionToolRegistration, + addExtensionTypedHookRegistration, } from "../extension-host/registry-writes.js"; import { resolveExtensionChannelRegistration, @@ -255,12 +258,12 @@ export function createPluginRegistry(registryParams: PluginRegistryParams) { }); return; } - record.hookNames.push(normalized.hookName); - registry.hooks.push({ - pluginId: normalized.entry.pluginId, - entry: normalized.entry.entry, + addExtensionLegacyHookRegistration({ + registry, + record, + hookName: normalized.hookName, + entry: normalized.entry, events: normalized.events, - source: normalized.entry.source, }); const hookSystemEnabled = config?.hooks?.internal?.enabled === true; @@ -513,13 +516,16 @@ export function createPluginRegistry(registryParams: PluginRegistryParams) { ) as PluginHookHandlerMap[K]; } } - record.hookCount += 1; - registry.typedHooks.push({ - ...normalized.entry, - pluginId: record.id, - hookName: normalized.hookName, - handler: effectiveHandler, - } as TypedPluginHookRegistration); + addExtensionTypedHookRegistration({ + registry, + record, + entry: { + ...normalized.entry, + pluginId: record.id, + hookName: normalized.hookName, + handler: effectiveHandler, + } as TypedPluginHookRegistration, + }); }; const normalizeLogger = (logger: PluginLogger): PluginLogger => ({ @@ -571,7 +577,10 @@ export function createPluginRegistry(registryParams: PluginRegistryParams) { }); return; } - registerContextEngine(result.entry.engineId, result.entry.factory); + addExtensionContextEngineRegistration({ + entry: result.entry, + registerEngine: registerContextEngine, + }); }, resolvePath: (input: string) => resolveUserPath(input), on: (hookName, handler, opts) =>