task: refine plugin notification boundary

This commit is contained in:
Keisuke Hirata 2026-06-05 05:23:23 +09:00
parent 9753b34fb9
commit d7e416982f
No known key found for this signature in database
4 changed files with 118 additions and 39 deletions

View File

@ -0,0 +1,13 @@
# Decision: remove generic event channels and standardize BackgroundTask
The Plugin/Feature base Pod API should not expose a plugin-defined event-channel mechanism for arbitrary structured client/UI payloads.
Revised boundary:
- Model-visible notifications use the existing durable `Method::Notify` / `SystemItem::Notification` / `Event::SystemItem` path. If the model can see it, it must be committed to history and visible to users on replay/inspection.
- `Event::Alert`-like output is a short transient human-facing alert only. It is not model-visible, not session history, and not a structured UI extension channel.
- Diagnostics/status are host-defined operational records for install/runtime/capability/task reporting, not arbitrary plugin UI messages.
- Dialogs/confirmations/custom UI are deferred. If needed later, they should be a separate host-defined interaction protocol, not a generic plugin event channel.
- `BackgroundTaskContribution` is a first-class contribution kind. The host starts, tracks, cancels, and reports background tasks; feature modules must not spawn untracked async loops. Task output is limited to granted sinks/services: model notification, alert, diagnostics, and host-granted services.
This keeps the Plugin API centered on Tools, safe Hooks, host-managed BackgroundTasks, durable model-visible notifications, and bounded host-defined operational reporting.

View File

@ -2,7 +2,7 @@
## 1. Summary recommendation ## 1. Summary recommendation
Introduce a `pod::feature` public API as the single Pod-side registration layer for built-in features and future external plugins. A feature module should declare its identity, requested capabilities, and contributions, then install those contributions only through typed host registrars for existing Pod/Worker surfaces: `ToolRegistry`, the hardened safe `pod::hook` surface, and host-owned notification/event/history append paths. Introduce a `pod::feature` public API as the single Pod-side registration layer for built-in features and future external plugins. A feature module should declare its identity, requested capabilities, and contributions, then install those contributions only through typed host registrars for existing Pod/Worker surfaces: `ToolRegistry`, the hardened safe `pod::hook` surface, host-managed background tasks, and host-owned notification/history/alert/diagnostic paths.
The registry should not become a second runtime, a plugin dispatcher tool, or a generic `Pod` mutation escape hatch. Feature state remains inside the feature module; the Pod owns only install metadata, diagnostics, granted host handles, and normal durable session/runtime surfaces. The registry should not become a second runtime, a plugin dispatcher tool, or a generic `Pod` mutation escape hatch. Feature state remains inside the feature module; the Pod owns only install metadata, diagnostics, granted host handles, and normal durable session/runtime surfaces.
@ -59,16 +59,19 @@ Add a new module under `pod`:
```rust ```rust
pub mod feature { pub mod feature {
pub mod background;
pub mod capability; pub mod capability;
pub mod diagnostic; pub mod diagnostic;
pub mod event;
pub mod hook; pub mod hook;
pub mod notify;
pub mod registry; pub mod registry;
pub mod tool; pub mod tool;
pub use background::{BackgroundTaskContribution, BackgroundTaskRegistrar};
pub use capability::{CapabilityGrantSet, CapabilityRequest, HostCapability}; pub use capability::{CapabilityGrantSet, CapabilityRequest, HostCapability};
pub use diagnostic::{FeatureDiagnostic, FeatureInstallReport}; pub use diagnostic::{FeatureDiagnostic, FeatureInstallReport};
pub use registry::{FeatureDescriptor, FeatureId, FeatureInstallContext, FeatureModule, FeatureRegistryBuilder, FeatureRuntimeKind}; pub use notify::{FeatureAlertSink, FeatureNotifySink};
pub use registry::{FeatureDescriptor, FeatureId, FeatureInstallContext, FeatureModule, FeatureRuntimeKind};
pub use tool::ToolContribution; pub use tool::ToolContribution;
} }
``` ```
@ -90,7 +93,7 @@ pub struct FeatureDescriptor {
pub requested_capabilities: Vec<CapabilityRequest>, pub requested_capabilities: Vec<CapabilityRequest>,
pub declared_tools: Vec<ToolDeclaration>, pub declared_tools: Vec<ToolDeclaration>,
pub declared_hooks: Vec<HookDeclaration>, pub declared_hooks: Vec<HookDeclaration>,
pub declared_event_channels: Vec<EventChannelDeclaration>, pub declared_background_tasks: Vec<BackgroundTaskDeclaration>,
} }
pub enum FeatureRuntimeKind { pub enum FeatureRuntimeKind {
@ -107,8 +110,9 @@ pub struct FeatureInstallContext<'a> {
pub grants: &'a CapabilityGrantSet, pub grants: &'a CapabilityGrantSet,
pub tools: ToolRegistrar<'a>, pub tools: ToolRegistrar<'a>,
pub hooks: PublicHookRegistrar<'a>, pub hooks: PublicHookRegistrar<'a>,
pub background_tasks: BackgroundTaskRegistrar<'a>,
pub notify: FeatureNotifySink<'a>, pub notify: FeatureNotifySink<'a>,
pub events: FeatureEventSink<'a>, pub alerts: FeatureAlertSink<'a>,
pub diagnostics: FeatureDiagnosticSink<'a>, pub diagnostics: FeatureDiagnosticSink<'a>,
pub services: FeatureServiceProvider<'a>, pub services: FeatureServiceProvider<'a>,
} }
@ -145,13 +149,21 @@ impl FeatureModule for WorkItemFeature {
"create and update WorkItem records through host-owned ticket storage", "create and update WorkItem records through host-owned ticket storage",
)) ))
.request(CapabilityRequest::optional( .request(CapabilityRequest::optional(
HostCapability::EmitUserEvent, HostCapability::EmitModelNotification,
"surface routing diagnostics to the TUI/actionbar", "commit user-action-required notices through the existing Notify/SystemItem path when the model should see them",
))
.request(CapabilityRequest::optional(
HostCapability::EmitAlert,
"surface short transient human-facing warnings when the watcher fails",
))
.request(CapabilityRequest::optional(
HostCapability::RunBackgroundTask,
"watch the WorkItem store for state changes without ad hoc tokio::spawn usage",
)) ))
.tool("WorkItemCreate") .tool("WorkItemCreate")
.tool("WorkItemComment") .tool("WorkItemComment")
.hook("work_item_intake_pre_tool_audit", pod::hook::HookPoint::PreToolCall) .hook("work_item_intake_pre_tool_audit", pod::hook::HookPoint::PreToolCall)
.event_channel("work-item") .background_task("work_item_watch")
.build() .build()
} }
@ -168,7 +180,10 @@ impl FeatureModule for WorkItemFeature {
WorkItemAuditHook::new(self.state.clone()), WorkItemAuditHook::new(self.state.clone()),
)?; )?;
ctx.events.declare_channel("work-item")?; ctx.background_tasks.register(BackgroundTaskContribution::pod_lifetime(
"work_item_watch",
WorkItemWatchTask::new(store.clone(), self.state.clone()),
))?;
Ok(()) Ok(())
} }
} }
@ -228,37 +243,59 @@ pub trait PublicPreToolCallHook: Send + Sync {
If a hook needs to add model-visible text, it should use `FeatureNotifySink::notify_model(...)` or another host-owned durable append API, not return an `Item`. If a hook needs to add model-visible text, it should use `FeatureNotifySink::notify_model(...)` or another host-owned durable append API, not return an `Item`.
### Notification/event contribution ### Notification, alert, and diagnostic surfaces
Expose two distinct sinks: Do not add a plugin-defined event-channel API. A feature should not be able to publish arbitrary structured UI payloads or ask clients to render feature-specific dialogs through the base Pod API.
Expose narrowly-scoped host sinks instead:
```rust ```rust
pub struct FeatureNotifySink<'a> { /* host-owned */ } pub struct FeatureNotifySink<'a> { /* host-owned */ }
pub struct FeatureEventSink<'a> { /* host-owned */ } pub struct FeatureAlertSink<'a> { /* host-owned */ }
pub struct FeatureDiagnosticSink<'a> { /* host-owned */ }
``` ```
Recommended behavior: Recommended behavior:
- `FeatureNotifySink::notify_model(...)` creates a model-visible notification through the existing durable notification/system-item path. The host commits the corresponding `SystemItem` before it is appended to Worker history. - `FeatureNotifySink::notify_model(...)` is the feature-facing form of the existing `Method::Notify` / `SystemItem::Notification` path. It creates a model-visible, history-backed notification by asking the host to commit a `LogEntry::SystemItem`; clients then see the same committed item through `Event::SystemItem`. Disk-side and wire-side remain 1:1.
- `FeatureNotifySink::notify_user(...)` or `FeatureEventSink::emit(...)` creates user-visible diagnostics/progress/action events through the existing alert/event path. These are not model-visible unless explicitly routed through `notify_model`. - `FeatureAlertSink::alert(...)` emits a short human-facing transient alert equivalent to `Event::Alert`. It is UI/person-facing only: not model-visible, not session history, not replayed as an input, and not a structured UI extension channel.
- Event payloads should be typed, bounded, and feature-identified. Avoid arbitrary JSON blobs as the first public API; allow an opaque bounded metadata field only if diagnostics require it. - `FeatureDiagnosticSink` records install/runtime/capability/task diagnostics for host logs, future `ListFeatures`, profile validation, and TUI diagnostics. Diagnostics are host-defined records, not arbitrary client-rendered UI payloads.
- Notifications and events should require explicit capabilities such as `EmitModelNotification` and `EmitUserEvent`. - Dialogs, confirmations, and other interactive client UI are out of scope for the base API. If needed later, add a separate host-defined interaction protocol; do not use a generic event channel for it.
- Background feature tasks must use these sinks; they must not hold raw log writers or append directly to history. - Model-visible notifications, transient alerts, and diagnostics should require distinct capabilities such as `EmitModelNotification`, `EmitAlert`, and diagnostic/reporting rights where policy needs them.
- Background feature tasks must use these sinks; they must not hold raw log writers, raw event senders, or append directly to history.
Useful initial event shape: ### Background task contribution
Background tasks should be a first-class feature contribution. Without this, asynchronous features will inevitably create ad hoc `tokio::spawn` loops, private channels, and shutdown/reporting paths outside the registry.
Initial shape:
```rust ```rust
pub struct FeatureEvent { pub struct BackgroundTaskContribution {
pub feature_id: FeatureId, pub feature_id: FeatureId,
pub level: FeatureEventLevel, // Info, Warn, Error pub id: FeatureTaskId,
pub channel: String, // e.g. "work-item" pub description: String,
pub summary: String, pub activation: TaskActivation,
pub detail: Option<String>, pub restart: RestartPolicy,
pub model_visible: bool, // false unless host routes through notify_model pub shutdown: ShutdownPolicy,
pub required_capabilities: Vec<HostCapability>,
}
pub struct FeatureTaskContext<'a> {
pub cancellation: CancellationToken,
pub notify: FeatureNotifySink<'a>,
pub alerts: FeatureAlertSink<'a>,
pub diagnostics: FeatureDiagnosticSink<'a>,
pub services: FeatureServiceProvider<'a>,
} }
``` ```
`model_visible` should be host-controlled in practice: a feature may request model visibility, but the sink decides whether that capability is granted and records the durable append if it is. Rules:
- The host starts, tracks, cancels, and reports background tasks. Feature modules register tasks; they do not spawn untracked runtime loops.
- Task output is limited to granted sinks/services: model-visible notification through `FeatureNotifySink`, human transient alert through `FeatureAlertSink`, diagnostics through `FeatureDiagnosticSink`, and host-granted service operations.
- The host records task lifecycle/status in install/runtime diagnostics. Do not expose arbitrary feature-defined event channels for task progress.
- Initial activation can be conservative (`PodLifetime` and/or `OnDemand`); restart policy may start with `Never`, but the API should make shutdown/cancellation behavior explicit from the beginning.
### Capability request/grant/diagnostics ### Capability request/grant/diagnostics
@ -270,8 +307,9 @@ Initial capability categories:
pub enum HostCapability { pub enum HostCapability {
ContributeTool { name: ToolName }, ContributeTool { name: ToolName },
ContributeHook { point: pod::hook::HookPoint }, ContributeHook { point: pod::hook::HookPoint },
EmitUserEvent, RunBackgroundTask,
EmitModelNotification, EmitModelNotification,
EmitAlert,
ScopedFs { read: bool, write: bool, execute: bool }, ScopedFs { read: bool, write: bool, execute: bool },
WorkItemStore { read: bool, write: bool }, WorkItemStore { read: bool, write: bool },
MemoryStore { read: bool, write: bool }, MemoryStore { read: bool, write: bool },
@ -297,6 +335,7 @@ pub struct FeatureInstallReport {
pub denied: Vec<CapabilityDenial>, pub denied: Vec<CapabilityDenial>,
pub installed_tools: Vec<ToolName>, pub installed_tools: Vec<ToolName>,
pub installed_hooks: Vec<String>, pub installed_hooks: Vec<String>,
pub installed_background_tasks: Vec<FeatureTaskId>,
pub skipped_contributions: Vec<SkippedContribution>, pub skipped_contributions: Vec<SkippedContribution>,
pub diagnostics: Vec<FeatureDiagnostic>, pub diagnostics: Vec<FeatureDiagnostic>,
} }
@ -311,7 +350,7 @@ Feature state belongs to the feature module.
- A feature may own `Arc<State>` and clone it into contributed tools, hooks, and background tasks. - A feature may own `Arc<State>` and clone it into contributed tools, hooks, and background tasks.
- The Pod registry stores descriptors, install reports, enabled/disabled status, and host-owned handles. It does not store feature business state. - The Pod registry stores descriptors, install reports, enabled/disabled status, and host-owned handles. It does not store feature business state.
- Durable feature data must live in a feature-owned or host-granted store with an explicit API: WorkItem files through a WorkItem service, memory records through memory APIs, plugin config/state through a future plugin-state service, etc. - Durable feature data must live in a feature-owned or host-granted store with an explicit API: WorkItem files through a WorkItem service, memory records through memory APIs, plugin config/state through a future plugin-state service, etc.
- Session history is not feature storage. It is an audit/replay record of model-visible interactions and host-visible events. - Session history is not feature storage. It is an audit/replay record of model-visible interactions and committed system items.
- A feature that needs restoration after process restart should reconstruct itself from its own durable store/config plus normal Pod metadata, not from private data hidden in Worker context. - A feature that needs restoration after process restart should reconstruct itself from its own durable store/config plus normal Pod metadata, not from private data hidden in Worker context.
- Background tasks are allowed only if they communicate through granted sinks/services and have a defined shutdown/lifecycle policy owned by the host. - Background tasks are allowed only if they communicate through granted sinks/services and have a defined shutdown/lifecycle policy owned by the host.
@ -331,7 +370,8 @@ Public features/plugins must not be able to perform these operations:
- Grant themselves capabilities or infer grants from successful construction. - Grant themselves capabilities or infer grants from successful construction.
- Mutate manifest/profile/scope state directly. - Mutate manifest/profile/scope state directly.
- Perform filesystem/process/network/secret access outside granted host services. - Perform filesystem/process/network/secret access outside granted host services.
- Emit unbounded tool outputs, event payloads, diagnostics, or notification bodies. - Emit unbounded tool outputs, diagnostics, alerts, task-status reports, or notification bodies.
- Emit arbitrary plugin-defined UI payloads, dialogs, or event-channel messages.
- Put secrets into diagnostics, session logs, model context, TUI output, or feature install reports. - Put secrets into diagnostics, session logs, model context, TUI output, or feature install reports.
- Depend on MCP/WASM/package-distribution mechanics in the base Pod API. - Depend on MCP/WASM/package-distribution mechanics in the base Pod API.
@ -416,14 +456,14 @@ Recommended migration is incremental and behavior-preserving:
WorkItem / intake routing: WorkItem / intake routing:
- WorkItem routing can become a built-in feature that contributes WorkItem tools, optional routing hooks, and user-visible action events. - WorkItem routing can become a built-in feature that contributes WorkItem tools, optional routing hooks, and host-managed background tasks such as a watcher.
- It should request `WorkItemStore` and event/notification capabilities instead of reaching into ticket files ad hoc. - It should request `WorkItemStore`, `RunBackgroundTask`, and notification/alert capabilities instead of reaching into ticket files ad hoc.
- Model-visible routing hints or intake results must be committed through notification/history append paths. - Model-visible routing hints or intake results must be committed through notification/history append paths.
- This registry gives the WorkItem feature a clean way to install without making WorkItem a special Pod runtime mode. - This registry gives the WorkItem feature a clean way to install without making WorkItem a special Pod runtime mode.
MCP: MCP:
- MCP should be an adapter/runtime kind that produces normal `ToolContribution`s and possibly safe event diagnostics. - MCP should be an adapter/runtime kind that produces normal `ToolContribution`s and, when needed, host-managed background tasks for connection/session supervision plus bounded diagnostics/alerts.
- MCP tool calls must still pass through `ToolRegistry`, PreToolCall permission, output bounding, and history result recording. - MCP tool calls must still pass through `ToolRegistry`, PreToolCall permission, output bounding, and history result recording.
- MCP resources/prompts should not become invisible prompt injection. If exposed later, they should be explicit tools, user-invoked attachments, or durable notification/history appends. - MCP resources/prompts should not become invisible prompt injection. If exposed later, they should be explicit tools, user-invoked attachments, or durable notification/history appends.
- MCP transport/session details are out of scope for the base API beyond the `FeatureRuntimeKind::McpBridge` placeholder. - MCP transport/session details are out of scope for the base API beyond the `FeatureRuntimeKind::McpBridge` placeholder.
@ -443,8 +483,10 @@ Plugin distribution:
2. The exact safe hook action set must be settled by `hook-public-surface-hardening`. 2. The exact safe hook action set must be settled by `hook-public-surface-hardening`.
- Especially important: whether public pre-tool hooks may synthesize denials/results, and how durable append requests are represented. - Especially important: whether public pre-tool hooks may synthesize denials/results, and how durable append requests are represented.
3. Notification/event durability needs precise semantics. 3. Notification, alert, and diagnostic semantics need precise names and capabilities.
- User-visible events may be live-only, while model-visible notifications must be durable. The public API should make this distinction impossible to miss. - Model-visible notifications must be durable and should use the existing `Notify` / `SystemItem` / `Event::SystemItem` path.
- `Alert`-like output is transient human-facing text only.
- Diagnostics/status are host-defined operational records, not plugin-defined UI channels.
4. Capability granularity can easily become either too coarse or too noisy. 4. Capability granularity can easily become either too coarse or too noisy.
- Start with coarse host-service capabilities plus normal tool permissions, then split only when real features need finer grants. - Start with coarse host-service capabilities plus normal tool permissions, then split only when real features need finer grants.

View File

@ -7,7 +7,7 @@ kind: feature
priority: P1 priority: P1
labels: [plugin, registry, tools, hooks, orchestration] labels: [plugin, registry, tools, hooks, orchestration]
created_at: 2026-06-03T12:23:17Z created_at: 2026-06-03T12:23:17Z
updated_at: 2026-06-03T16:44:05Z updated_at: 2026-06-04T20:23:15Z
assignee: null assignee: null
legacy_ticket: null legacy_ticket: null
--- ---
@ -16,7 +16,7 @@ legacy_ticket: null
Yoi already has many capability surfaces: built-in tools, memory tools, Pod management tools, manifest permission hooks, workflow assets, notifications, and planned WorkItem / MCP / plugin features. If new features keep registering themselves through ad hoc Pod/Worker code paths, Plugin system work will not produce a single management boundary and later features such as WorkItem intake will be hard to detach. Yoi already has many capability surfaces: built-in tools, memory tools, Pod management tools, manifest permission hooks, workflow assets, notifications, and planned WorkItem / MCP / plugin features. If new features keep registering themselves through ad hoc Pod/Worker code paths, Plugin system work will not produce a single management boundary and later features such as WorkItem intake will be hard to detach.
The immediate need is not package distribution or WASM execution. The immediate need is a runtime feature contribution registry that lets built-in features and future external plugins contribute through the same existing host surfaces: Tools, Hooks, and notification/event paths. The immediate need is not package distribution or WASM execution. The immediate need is a runtime feature contribution registry that lets built-in features and future external plugins contribute through the same existing host surfaces: Tools, Hooks, host-managed BackgroundTasks, and durable notification/history plus alert/diagnostic paths.
## Direction ## Direction
@ -26,7 +26,9 @@ Introduce a feature registry boundary for Pod runtime capability installation.
- Pod interaction happens through existing surfaces: - Pod interaction happens through existing surfaces:
- Tool contributions registered into the normal ToolRegistry / permission / history path. - Tool contributions registered into the normal ToolRegistry / permission / history path.
- Hook contributions registered through the public Pod Hook boundary. - Hook contributions registered through the public Pod Hook boundary.
- Notification/event contributions use existing durable Notify / Event paths rather than invisible context injection. - BackgroundTask contributions are host-managed for async feature work; feature modules must not create untracked runtime loops.
- Model-visible notifications use the existing durable Notify / SystemItem / Event::SystemItem path rather than invisible context injection.
- Transient human-facing alerts and diagnostics are separate host-defined outputs, not arbitrary plugin UI channels.
- The registry is responsible for discovery/enablement diagnostics and installation into existing surfaces; it must not create a parallel execution path. - The registry is responsible for discovery/enablement diagnostics and installation into existing surfaces; it must not create a parallel execution path.
- Built-in features should be expressible as feature contributions first. External plugin runtimes can be added later. - Built-in features should be expressible as feature contributions first. External plugin runtimes can be added later.
@ -44,7 +46,8 @@ Pure descriptor types may later move to a separate `plugin` / `extension` crate
- Define contribution descriptors or install abstractions for: - Define contribution descriptors or install abstractions for:
- Tools - Tools
- Hooks - Hooks
- Notification/event-facing capabilities where needed - BackgroundTasks
- Model-visible notification, transient alert, and diagnostic capabilities where needed
- Define capability request / host grant data structures suitable for policy diagnostics. - Define capability request / host grant data structures suitable for policy diagnostics.
- Add a registry/builder/install context that can install enabled feature contributions into existing Pod/Worker surfaces. - Add a registry/builder/install context that can install enabled feature contributions into existing Pod/Worker surfaces.
- Preserve current behavior while moving registration toward the registry. - Preserve current behavior while moving registration toward the registry.
@ -83,7 +86,7 @@ Pure descriptor types may later move to a separate `plugin` / `extension` crate
- The codebase has a first-class feature contribution registry boundary for Pod runtime installation. - The codebase has a first-class feature contribution registry boundary for Pod runtime installation.
- At least one built-in capability group is registered through the new registry without changing behavior. - At least one built-in capability group is registered through the new registry without changing behavior.
- The registry can describe Tool and Hook contributions and records source/runtime/capability diagnostics. - The registry can describe Tool, Hook, and BackgroundTask contributions and records source/runtime/capability diagnostics.
- Feature installation uses existing ToolRegistry, HookRegistry, and notification/history paths; no parallel Pod context injection path is introduced. - Feature installation uses existing ToolRegistry, HookRegistry, host-managed BackgroundTask lifecycle, and notification/history paths; no parallel Pod context injection path or arbitrary plugin UI channel is introduced.
- WorkItem and MCP follow-up tickets can target this registry instead of adding ad hoc registration code. - WorkItem and MCP follow-up tickets can target this registry instead of adding ad hoc registration code.
- Focused tests cover the migrated built-in registration and capability/diagnostic behavior. - Focused tests cover the migrated built-in registration and capability/diagnostic behavior.

View File

@ -588,4 +588,25 @@ Plugin distribution:
- It is safe only because it commits system items before model visibility. Any future plugin command/input contribution must preserve that durable replay property. - It is safe only because it commits system items before model visibility. Any future plugin command/input contribution must preserve that durable replay property.
---
<!-- event: decision author: hare at: 2026-06-04T20:23:15Z -->
## Decision
# Decision: remove generic event channels and standardize BackgroundTask
The Plugin/Feature base Pod API should not expose a plugin-defined event-channel mechanism for arbitrary structured client/UI payloads.
Revised boundary:
- Model-visible notifications use the existing durable `Method::Notify` / `SystemItem::Notification` / `Event::SystemItem` path. If the model can see it, it must be committed to history and visible to users on replay/inspection.
- `Event::Alert`-like output is a short transient human-facing alert only. It is not model-visible, not session history, and not a structured UI extension channel.
- Diagnostics/status are host-defined operational records for install/runtime/capability/task reporting, not arbitrary plugin UI messages.
- Dialogs/confirmations/custom UI are deferred. If needed later, they should be a separate host-defined interaction protocol, not a generic plugin event channel.
- `BackgroundTaskContribution` is a first-class contribution kind. The host starts, tracks, cancels, and reports background tasks; feature modules must not spawn untracked async loops. Task output is limited to granted sinks/services: model notification, alert, diagnostics, and host-granted services.
This keeps the Plugin API centered on Tools, safe Hooks, host-managed BackgroundTasks, durable model-visible notifications, and bounded host-defined operational reporting.
--- ---