463 lines
24 KiB
Markdown
463 lines
24 KiB
Markdown
# Public Pod-side API for Feature / Plugin Contributions
|
|
|
|
## 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.
|
|
|
|
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.
|
|
|
|
Recommended placement: create `crates/pod/src/feature.rs` (or `crates/pod/src/feature/mod.rs` once it grows) and export it as `pod::feature`. Keep `llm-worker::Interceptor` internal; expose only hardened `pod::hook` types and contribution registrars.
|
|
|
|
## 2. Current relevant Pod/Worker surfaces
|
|
|
|
The design should build on these existing surfaces rather than bypassing them:
|
|
|
|
- `crates/pod/src/hook.rs`
|
|
- Current public-ish hook layer wraps `llm_worker::Interceptor` with `HookRegistry`, `HookRegistryBuilder`, `Hook`, and per-event hook traits.
|
|
- It already provides Pod-specific hook events such as pre-request, post-assistant, pre-tool-call, post-tool-call, and turn-end.
|
|
- It is not yet safe enough as a public plugin API because some hook actions can carry raw `llm_worker::Item` values (`PreRequestAction::ContinueWith`, `TurnEndAction::ContinueWithMessages`). The feature API must depend on the post-hardening surface, not these raw item mutation forms.
|
|
|
|
- `crates/pod/src/ipc/interceptor.rs`
|
|
- `PodInterceptor` is the bridge between Worker callbacks and Pod behavior.
|
|
- It runs hooks, drains pending attachments/notifications, records memory/tool usage, and turns model-visible additions into committed `SystemItem` session log entries before appending them to Worker history.
|
|
- This is the right place for host-mediated durable append paths; it is not a plugin API itself.
|
|
|
|
- `crates/pod/src/controller.rs`
|
|
- Controller startup currently registers built-in Pod tools through ad hoc code paths.
|
|
- The feature registry should replace those ad hoc registrations incrementally by installing contributions into the same worker/tool/hook surfaces during Pod construction.
|
|
|
|
- `crates/pod/src/pod.rs`
|
|
- `Pod` owns the durable session log, metadata, runtime event channel, notification helpers, pending system attachments, scope, and Worker lifecycle.
|
|
- It exposes internal methods that can append history or send alerts/events. The public feature API should not expose `Pod` or `Worker` directly; it should expose narrow sinks that route through these existing methods.
|
|
|
|
- `crates/pod/src/permission.rs`
|
|
- Manifest tool permissions are enforced as a `PreToolCallHook`.
|
|
- Feature tools must remain subject to the same PreToolCall permission path. Feature capability grants do not replace per-call tool permission.
|
|
|
|
- `crates/llm-worker/src/tool.rs` and `crates/llm-worker/src/tool_server.rs`
|
|
- `ToolDefinition`, `Tool`, `ToolMeta`, `ToolResult`, `ToolOutput`, and `ToolServerHandle` define the normal tool execution path.
|
|
- Tools registered here get normal schema exposure, execution, bounded output handling, and history result recording.
|
|
- The public feature API should register `ToolDefinition`s into this registry rather than introducing a separate plugin dispatch layer.
|
|
|
|
- `crates/llm-worker/src/interceptor.rs`
|
|
- The lower-level interceptor is powerful and Worker-oriented. It should remain internal because it can influence model request construction too directly.
|
|
- Public features should use `pod::hook` only after that API has been narrowed to durable, auditable actions.
|
|
|
|
- `crates/tools/src/lib.rs`
|
|
- Existing built-in tools already use shared tool abstractions and scoped filesystem/runtime handles.
|
|
- Those tool constructors can become built-in feature contributions without changing model-visible tool names.
|
|
|
|
- `crates/pod/src/workflow/mod.rs`
|
|
- Workflow invocation currently resolves user input segments into system items through the Pod's durable attachment path.
|
|
- This is a useful pattern for feature-owned model-visible additions: resolve through a host-owned append path and commit what the model sees. It should not become a general plugin context injection mechanism.
|
|
|
|
## 3. Proposed public API shape
|
|
|
|
### Types/modules
|
|
|
|
Add a new module under `pod`:
|
|
|
|
```rust
|
|
pub mod feature {
|
|
pub mod capability;
|
|
pub mod diagnostic;
|
|
pub mod event;
|
|
pub mod hook;
|
|
pub mod registry;
|
|
pub mod tool;
|
|
|
|
pub use capability::{CapabilityGrantSet, CapabilityRequest, HostCapability};
|
|
pub use diagnostic::{FeatureDiagnostic, FeatureInstallReport};
|
|
pub use registry::{FeatureDescriptor, FeatureId, FeatureInstallContext, FeatureModule, FeatureRegistryBuilder, FeatureRuntimeKind};
|
|
pub use tool::ToolContribution;
|
|
}
|
|
```
|
|
|
|
Core trait and registry shape:
|
|
|
|
```rust
|
|
pub trait FeatureModule: Send + Sync + 'static {
|
|
fn descriptor(&self) -> FeatureDescriptor;
|
|
|
|
fn install(&self, ctx: &mut FeatureInstallContext<'_>) -> Result<(), FeatureInstallError>;
|
|
}
|
|
|
|
pub struct FeatureDescriptor {
|
|
pub id: FeatureId, // source-qualified identity, e.g. builtin:task
|
|
pub display_name: String,
|
|
pub version: Option<String>,
|
|
pub runtime: FeatureRuntimeKind, // Builtin, ExternalProcess, McpBridge, WasmPlaceholder, DeclarativePlaceholder
|
|
pub requested_capabilities: Vec<CapabilityRequest>,
|
|
pub declared_tools: Vec<ToolDeclaration>,
|
|
pub declared_hooks: Vec<HookDeclaration>,
|
|
pub declared_event_channels: Vec<EventChannelDeclaration>,
|
|
}
|
|
|
|
pub enum FeatureRuntimeKind {
|
|
Builtin,
|
|
ExternalProcess,
|
|
McpBridge,
|
|
WasmPlaceholder,
|
|
DeclarativePlaceholder,
|
|
}
|
|
|
|
pub struct FeatureInstallContext<'a> {
|
|
// No Pod or Worker reference.
|
|
pub feature_id: &'a FeatureId,
|
|
pub grants: &'a CapabilityGrantSet,
|
|
pub tools: ToolRegistrar<'a>,
|
|
pub hooks: PublicHookRegistrar<'a>,
|
|
pub notify: FeatureNotifySink<'a>,
|
|
pub events: FeatureEventSink<'a>,
|
|
pub diagnostics: FeatureDiagnosticSink<'a>,
|
|
pub services: FeatureServiceProvider<'a>,
|
|
}
|
|
```
|
|
|
|
Important details:
|
|
|
|
- `FeatureDescriptor` is declarative and serializable. It is safe to show in diagnostics, profile previews, and `ListFeatures`-style future tooling.
|
|
- `FeatureModule::install` is runtime code that wires stateful tool/hook implementations into host registrars.
|
|
- `FeatureInstallContext` must not expose `Pod`, `Worker`, raw `ToolServerHandle`, raw `Interceptor`, raw `NotifyBuffer`, raw `LogWriter`, raw `event_tx`, or direct history mutation.
|
|
- `FeatureServiceProvider` returns only host services backed by granted capabilities, for example scoped filesystem access, WorkItem store access, memory access, Pod orchestration handles, web provider handles, or secret references. It should return `Denied`/`Unavailable` diagnostics instead of exposing partial internals.
|
|
|
|
### Example registration snippet
|
|
|
|
This is illustrative shape, not proposed final exact Rust syntax:
|
|
|
|
```rust
|
|
use pod::feature::{
|
|
CapabilityRequest, FeatureDescriptor, FeatureId, FeatureInstallContext,
|
|
FeatureModule, FeatureRuntimeKind, HostCapability, ToolContribution,
|
|
};
|
|
|
|
pub struct WorkItemFeature {
|
|
state: std::sync::Arc<WorkItemFeatureState>,
|
|
}
|
|
|
|
impl FeatureModule for WorkItemFeature {
|
|
fn descriptor(&self) -> FeatureDescriptor {
|
|
FeatureDescriptor::builder(FeatureId::builtin("work-item"))
|
|
.display_name("WorkItem intake and routing")
|
|
.runtime(FeatureRuntimeKind::Builtin)
|
|
.request(CapabilityRequest::required(
|
|
HostCapability::WorkItemStore { read: true, write: true },
|
|
"create and update WorkItem records through host-owned ticket storage",
|
|
))
|
|
.request(CapabilityRequest::optional(
|
|
HostCapability::EmitUserEvent,
|
|
"surface routing diagnostics to the TUI/actionbar",
|
|
))
|
|
.tool("WorkItemCreate")
|
|
.tool("WorkItemComment")
|
|
.hook("work_item_intake_pre_tool_audit", pod::hook::HookPoint::PreToolCall)
|
|
.event_channel("work-item")
|
|
.build()
|
|
}
|
|
|
|
fn install(&self, ctx: &mut FeatureInstallContext<'_>) -> Result<(), FeatureInstallError> {
|
|
let store = ctx.services.work_item_store()?;
|
|
|
|
ctx.tools.register(ToolContribution::new(
|
|
"WorkItemCreate",
|
|
work_item_create_tool(store.clone(), self.state.clone()),
|
|
))?;
|
|
|
|
ctx.hooks.pre_tool_call(
|
|
"work_item_intake_pre_tool_audit",
|
|
WorkItemAuditHook::new(self.state.clone()),
|
|
)?;
|
|
|
|
ctx.events.declare_channel("work-item")?;
|
|
Ok(())
|
|
}
|
|
}
|
|
```
|
|
|
|
The feature keeps `WorkItemFeatureState`. The Pod keeps only registration records, diagnostics, and the normal host services it already owns.
|
|
|
|
### Tool contribution
|
|
|
|
A tool contribution should be a thin wrapper around `llm_worker::ToolDefinition` plus feature metadata:
|
|
|
|
```rust
|
|
pub struct ToolContribution {
|
|
pub feature_id: FeatureId,
|
|
pub name: ToolName,
|
|
pub definition: llm_worker::ToolDefinition,
|
|
pub required_capabilities: Vec<HostCapability>,
|
|
}
|
|
```
|
|
|
|
Rules:
|
|
|
|
- Register into the existing `ToolRegistry` / `ToolServerHandle`; do not add a plugin-dispatcher tool that multiplexes plugin calls outside normal tool history.
|
|
- Preserve normal `PreToolCall` permission evaluation, tool-call history, result history, output truncation/bounding, and diagnostic behavior.
|
|
- Host-controlled feature enablement decides whether a contributed tool is installed. Manifest/profile tool permission still decides whether a model may call it at runtime.
|
|
- Duplicate tool names should be rejected during feature registry preflight with a diagnostic, not discovered later through a panic or undefined ordering.
|
|
- Public feature identity should be source-qualified (`builtin:memory`, `project:foo`, `plugin:<digest>:bar`), while model-visible tool names should remain explicit stable names. Do not auto-prefix model tool names unless the project deliberately chooses a future namespacing policy.
|
|
- Tool schemas/descriptions must be part of the normal `ToolDefinition` path so model-visible surfaces remain inspectable and bounded.
|
|
- If a required host service is not granted or configured, the tool should not be registered; the install report should explain the skipped contribution.
|
|
|
|
### Hook contribution
|
|
|
|
Hook contribution must depend on the safe hook surface produced by `hook-public-surface-hardening`.
|
|
|
|
Recommended public hook principles:
|
|
|
|
- Public hooks register through `PublicHookRegistrar`, which wraps `HookRegistryBuilder` but exposes only hardened hook traits/actions.
|
|
- Public hooks receive snapshots/views, not mutable Pod/Worker handles.
|
|
- Public hook return values should be decisions such as continue, deny/rewrite a tool decision through a host-defined synthetic result path, emit diagnostics, or request a durable notification/history append through a host sink. They should not return raw `llm_worker::Item` vectors.
|
|
- Public hooks must not be able to mutate request context, session history, or Worker state invisibly.
|
|
- Permission enforcement hooks remain host/internal and run before feature hooks for `PreToolCall` so a feature cannot approve a denied tool call.
|
|
- Hook ordering should be explicit and stable: internal safety hooks first, public feature hooks in registry order or declared priority bands, internal usage/accounting hooks where needed. Priority should be coarse, not arbitrary integer ordering that lets plugins fight for precedence.
|
|
|
|
Possible hardened hook action shape:
|
|
|
|
```rust
|
|
pub enum PublicPreToolCallDecision {
|
|
Continue,
|
|
DenyWithSyntheticError { message: String },
|
|
EmitDiagnostic { diagnostic: FeatureDiagnostic },
|
|
}
|
|
|
|
pub trait PublicPreToolCallHook: Send + Sync {
|
|
fn on_pre_tool_call(&self, event: PublicPreToolCallEvent<'_>) -> PublicPreToolCallDecision;
|
|
}
|
|
```
|
|
|
|
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
|
|
|
|
Expose two distinct sinks:
|
|
|
|
```rust
|
|
pub struct FeatureNotifySink<'a> { /* host-owned */ }
|
|
pub struct FeatureEventSink<'a> { /* host-owned */ }
|
|
```
|
|
|
|
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_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`.
|
|
- 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.
|
|
- Notifications and events should require explicit capabilities such as `EmitModelNotification` and `EmitUserEvent`.
|
|
- Background feature tasks must use these sinks; they must not hold raw log writers or append directly to history.
|
|
|
|
Useful initial event shape:
|
|
|
|
```rust
|
|
pub struct FeatureEvent {
|
|
pub feature_id: FeatureId,
|
|
pub level: FeatureEventLevel, // Info, Warn, Error
|
|
pub channel: String, // e.g. "work-item"
|
|
pub summary: String,
|
|
pub detail: Option<String>,
|
|
pub model_visible: bool, // false unless host routes through notify_model
|
|
}
|
|
```
|
|
|
|
`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.
|
|
|
|
### Capability request/grant/diagnostics
|
|
|
|
Capabilities are requested by descriptors and granted by the host. A feature may request a capability, but it must not assume the capability exists.
|
|
|
|
Initial capability categories:
|
|
|
|
```rust
|
|
pub enum HostCapability {
|
|
ContributeTool { name: ToolName },
|
|
ContributeHook { point: pod::hook::HookPoint },
|
|
EmitUserEvent,
|
|
EmitModelNotification,
|
|
ScopedFs { read: bool, write: bool, execute: bool },
|
|
WorkItemStore { read: bool, write: bool },
|
|
MemoryStore { read: bool, write: bool },
|
|
PodManagement { spawn: bool, message: bool, restore: bool },
|
|
Network { purpose: NetworkPurpose },
|
|
SecretRef { id: String },
|
|
}
|
|
```
|
|
|
|
Important separation:
|
|
|
|
- Capability grants decide whether a feature may install and receive host services.
|
|
- Tool permissions decide whether an installed tool call may execute for a specific Pod/run.
|
|
- Scope permissions decide which filesystem paths or delegated Pod capabilities a host service may touch.
|
|
|
|
Diagnostics should be first-class:
|
|
|
|
```rust
|
|
pub struct FeatureInstallReport {
|
|
pub feature_id: FeatureId,
|
|
pub enabled: bool,
|
|
pub granted: Vec<HostCapability>,
|
|
pub denied: Vec<CapabilityDenial>,
|
|
pub installed_tools: Vec<ToolName>,
|
|
pub installed_hooks: Vec<String>,
|
|
pub skipped_contributions: Vec<SkippedContribution>,
|
|
pub diagnostics: Vec<FeatureDiagnostic>,
|
|
}
|
|
```
|
|
|
|
Diagnostics must avoid secrets and must be safe for session logs, TUI display, and future `ListFeatures`/profile validation output.
|
|
|
|
## 4. State ownership model
|
|
|
|
Feature state belongs to the feature module.
|
|
|
|
- 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.
|
|
- 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.
|
|
- 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.
|
|
|
|
This model lets built-ins and plugins share the same contribution shape while keeping Pod runtime ownership clear.
|
|
|
|
## 5. Safety invariants / forbidden operations
|
|
|
|
Public features/plugins must not be able to perform these operations:
|
|
|
|
- Mutate prompt context directly.
|
|
- Append, remove, reorder, or rewrite Worker history directly.
|
|
- Insert model-visible text that is not committed through a durable host path.
|
|
- Return raw `llm_worker::Item` values from public hooks.
|
|
- Access raw `Worker`, raw `Pod`, raw `ToolServerHandle`, raw `llm_worker::Interceptor`, raw `NotifyBuffer`, raw session log writer, or raw event sender.
|
|
- Register tools outside `ToolRegistry` or bypass normal tool-result history recording.
|
|
- Bypass `PreToolCall` permission policy.
|
|
- Grant themselves capabilities or infer grants from successful construction.
|
|
- Mutate manifest/profile/scope state directly.
|
|
- Perform filesystem/process/network/secret access outside granted host services.
|
|
- Emit unbounded tool outputs, event payloads, diagnostics, or notification bodies.
|
|
- 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.
|
|
|
|
Positive invariant: if the model can see a feature-produced fact, a future replay/resume must have a durable explanation for why that fact was present.
|
|
|
|
## 6. Placement and crate-boundary recommendation
|
|
|
|
Recommended placement:
|
|
|
|
- `crates/pod/src/feature.rs` or `crates/pod/src/feature/mod.rs`
|
|
- public feature traits/types
|
|
- feature registry builder
|
|
- install reports/diagnostics
|
|
- capability request/grant model
|
|
- typed registrars/sinks
|
|
|
|
- `crates/pod/src/hook.rs`
|
|
- remains the public hook module after hardening
|
|
- should expose safe Pod-level hook traits/actions only
|
|
- should not re-export `llm_worker::Interceptor` power
|
|
|
|
- `crates/llm-worker`
|
|
- remains owner of generic LLM tools/interceptors/history machinery
|
|
- should not depend on `pod::feature`
|
|
|
|
- `crates/tools`
|
|
- remains a source of reusable tool implementations
|
|
- built-in feature modules in `pod` can wrap these constructors into `ToolContribution`s
|
|
|
|
- Future external plugin crates/processes
|
|
- should adapt into `FeatureDescriptor` + `FeatureModule` or a host-side adapter that produces equivalent contributions
|
|
- should not be called directly by the Pod except through the registry/registrars
|
|
|
|
Install location in Pod startup:
|
|
|
|
1. Resolve manifest/profile and host capability policy.
|
|
2. Construct `Pod` and internal safety surfaces.
|
|
3. Install host/internal hooks such as manifest permission enforcement.
|
|
4. Build and install enabled feature modules through `FeatureRegistryBuilder`.
|
|
5. Flush/register tools through the existing Worker tool registry.
|
|
6. Freeze/install the Pod interceptor and start normal run/attach behavior.
|
|
|
|
The exact sequencing can be adjusted to match current construction, but the invariant should hold: public feature hooks cannot precede host safety hooks, and feature tools must exist before the model receives the final tool schema for a run.
|
|
|
|
## 7. Migration path from current built-in registrations
|
|
|
|
Recommended migration is incremental and behavior-preserving:
|
|
|
|
1. Land hook public-surface hardening first.
|
|
- Remove/replace public raw `Item`-carrying hook actions.
|
|
- Define which hook decisions are safe for external contributors.
|
|
|
|
2. Add `pod::feature` with no behavior change.
|
|
- Implement descriptors, capability grants, install reports, and registrars.
|
|
- Initially register no external plugins.
|
|
|
|
3. Wrap current built-in tool registration as built-in feature modules.
|
|
- Start with a small built-in feature whose state/services are already cleanly bounded.
|
|
- Preserve existing tool names, schemas, and permission behavior.
|
|
- Convert duplicate-name failures into registry diagnostics before flushing tools.
|
|
|
|
4. Move larger built-in groups behind feature modules.
|
|
- Filesystem/process tools from `crates/tools`.
|
|
- Memory tools.
|
|
- Pod orchestration tools.
|
|
- Task/WorkItem tools once their stores and hooks have explicit capabilities.
|
|
- Web tools as configured provider-backed features.
|
|
|
|
5. Move built-in hook contributions only after safe hook semantics are stable.
|
|
- Keep manifest permission enforcement as an internal host hook, not a feature hook.
|
|
- Keep accounting/usage hooks internal unless they become genuine feature behavior.
|
|
|
|
6. Treat workflow/user-input expansion separately.
|
|
- Workflow invocation already uses a durable system-item attachment pattern.
|
|
- Do not expose arbitrary workflow-like context injection to plugins until there is a safe typed command/input-contribution API with durable append semantics.
|
|
|
|
7. Add profile/manifest enablement after built-ins work through the same registry.
|
|
- Built-ins and external plugins should share descriptor/capability/install-report mechanics.
|
|
- Host policy may grant built-ins by default, but built-ins should still declare what they use.
|
|
|
|
## 8. Impact on WorkItem / MCP / plugin distribution follow-ups
|
|
|
|
WorkItem / intake routing:
|
|
|
|
- WorkItem routing can become a built-in feature that contributes WorkItem tools, optional routing hooks, and user-visible action events.
|
|
- It should request `WorkItemStore` and event/notification capabilities instead of reaching into ticket files ad hoc.
|
|
- 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.
|
|
|
|
MCP:
|
|
|
|
- MCP should be an adapter/runtime kind that produces normal `ToolContribution`s and possibly safe event diagnostics.
|
|
- 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 transport/session details are out of scope for the base API beyond the `FeatureRuntimeKind::McpBridge` placeholder.
|
|
|
|
Plugin distribution:
|
|
|
|
- Archive validation, cache extraction, signing/trust, WASM execution, external process supervision, and package update policy should remain separate follow-up designs.
|
|
- Distribution mechanisms should eventually produce the same descriptor/capability/contribution objects as built-ins.
|
|
- Capability grants are the host trust boundary; package installation alone must not grant runtime authority.
|
|
|
|
## 9. Open questions / risks
|
|
|
|
1. Tool naming policy is the highest-risk API decision.
|
|
- Recommendation: feature identities are source-qualified, model-visible tool names stay explicit and stable, and collisions are rejected by the host.
|
|
- Risk: external plugins may need namespacing later. Auto-prefixing now would avoid collisions but would also change model-facing ergonomics and diverge from current built-in tool names.
|
|
|
|
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.
|
|
|
|
3. Notification/event durability needs precise semantics.
|
|
- User-visible events may be live-only, while model-visible notifications must be durable. The public API should make this distinction impossible to miss.
|
|
|
|
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.
|
|
|
|
5. Runtime enable/disable is not designed here.
|
|
- Initial registry should be install-at-startup. Hot reload or dynamic plugin enablement needs separate lifecycle, cleanup, and schema-refresh design.
|
|
|
|
6. Persistent plugin state needs a future host service.
|
|
- The base API says state is feature-owned, but external plugins will still need a sanctioned durable state directory/store with migration/versioning rules.
|
|
|
|
7. Background tasks need lifecycle policy.
|
|
- If external plugins can spawn tasks, the host must define shutdown, cancellation, panic handling, diagnostic routing, and whether task output may become model-visible.
|
|
|
|
8. Existing workflow/input expansion is close to the forbidden boundary.
|
|
- It is safe only because it commits system items before model visibility. Any future plugin command/input contribution must preserve that durable replay property.
|