AGENTS.md@packages/dashboard-plugin-runtime/src/server · git:20260907.33d6653 · 2026-09-07 · sha256 e6d6b0b83b10fd23

AGENTS.md@packages/dashboard-plugin-runtime/src/server git:20260907.33d6653A

Immutable. This exact content is served forever at /api/v1/blob/e6d6b0b83b10fd23.

# DOX — packages/dashboard-plugin-runtime/src/server

Files in this directory. One row per source file.

| File | Purpose |
|------|---------|
| `config-validator.ts` | `validatePluginConfig`, `applySchemaDefaults` — Ajv JSON-Schema 7 validation for plugin config writes. |
| `index.ts` | Server-side barrel for `@blackbelt-technology/dashboard-plugin-runtime/server`. Re-exports `loader`, `server-context`, `plugin-status-store`, `config-validator`, `requirement-probes`, `service-probes/pi-model-proxy`, plus pure-TS `../dependency-graph` (avoids pulling React-y main barrel). |
| `installed-probe.ts` | `createIsPiExtensionInstalled({listGlobal, listLocal, now?, ttlMs?})` — host factory for the `isPiExtensionInstalled` capability (design D2). Answers from the UNION of global+local scopes (superset of the probe's global-only wiring), matched with `installedMatchesName` (id/name/displayName/source incl. `sourcesMatch`). Boolean-only; success-only cache (~30s TTL, mirrors `requirement-probes.ts`); a scan failure REJECTS — never resolves `false` — and is never cached. See change: add-blackhole-session-pipeline. |
| `loader.ts` | `discoverPlugins` + `loadServerEntries` (failure-isolated) + `getPluginStatusStore`. Adds `deterministicSerializePlugins` (sorted manifest fields, stable JSON) + `pluginRegistryHash(plugins)` (sha256 over serialization). Shared by vite-plugin (build-time `PLUGIN_REGISTRY_HASH` emission) + server (`/api/health.bundleHash`). See change: fix-pi-flows-end-to-end. |
| `plugin-status-store.ts` | In-memory `PluginStatusStore` for `/api/health.plugins[]`. Adds `recordBridgeProbe(pluginId, snapshot)` + `getBridgeProbe(pluginId)`. `listAll()` enriches returned status rows with `lastProbe`. Plugins (e.g. flows-anthropic-bridge) push runtime telemetry via `recordBridgeProbe`; surfaces in health endpoint without dedicated REST route. See change: fix-pi-flows-end-to-end. |
| `requirement-probes.ts` | Declarative requirement probes for `PluginManifest.requires`. Exports `probePiExtension(id, listInstalled)` (cross-refs installed pi-extension set), `probeBinary(name)` (PATH lookup via tool registry), `probeService(name, detectors)` (dispatches to service-probe map), `runRequirementProbes(requires, deps)` (parallel probes → `PluginRequirementReport`), `missingFromReport(report)` (flat `string[]` for UI). 30s in-process cache keyed by category+name. See change: add-plugin-activation-ui. `installedMatchesName` source comparison delegates to `sourcesMatch`; piExtensions requirement now satisfied by git/local-build installs (behavior follows shared matcher fix, no code change). See change: roles-standalone-defaults-and-local-install-detection. Adds `probePath(rawPath, deps)` — absolute-path existence probe with `${configKey}` interpolation from the plugin's validated config (`pluginConfig`/`configSchemaKeys`/`pathExists` deps); wired into `runRequirementProbesFor` (paths after services) + `missingFromReport`. See change: add-apple-tools-imcp-plugin. Exports `installedMatchesName` (was private) so the `isPiExtensionInstalled` capability matches with the SAME logic, not a weaker source-only comparison. See change: add-blackhole-session-pipeline. |
| `server-context.ts` | `createServerPluginContext(deps, pluginId)` factory — creates `ServerPluginContext` scoped to plugin with namespaced logger + typed config accessors. `ServerPluginContext` gains `onEvent(handler)` — subscribe all forwarded pi events, returns unsubscribe. Real `registerPiHandler` channel. `sendToSession(sessionId, text)` — send prompt/command into session; `/`-prefixed text routes to extension-command dispatch. New types `OnEventFn`, `SendToSessionFn`. See change: add-goal-continuation-plugin. ServerPluginContext gains `spawnSession(opts)` hook (PluginSpawnOptions/PluginSpawnResult/SpawnSessionFn), gated to first-party plugins. ServerPluginContext + ServerContextDeps gain abortSession: AbortSessionFn ((sessionId)=>boolean). New type AbortSessionFn. Gated to trusted plugins by host (mirrors spawnSession); untrusted returns false. createServerPluginContext forwards deps.abortSession. See change: add-automation-plugin. See change: automation-ui-mockup-parity. ServerPluginContext gains `provide(name,value)`/`consume<T>(name)` cross-plugin service seam. ServerContextDeps gains provide/consume. See change: register-plugin-automation-events. ServerPluginContext gains `emitEventToSession(sessionId, eventType, data?)` (EmitEventToSessionFn); trust-gated; relays `plugin_emit_event` control message to session. See change: automation-emit-configured-event. ServerPluginContext gains `consumeAll(prefix)` — enumerate host service board by key prefix; publish/collect; in-process only. See change: decouple-automation-action-registry. ServerPluginContext + ServerContextDeps gain `abortSpawnedRun(args): Promise<boolean>` (AbortSpawnedRunFn, args `{sessionId?,spawnToken?,graceful?}`; RENAMED from `abortAutomationRun`/`AbortAutomationRunFn` — generic spawned-run kill shared by automation AND goal-supervisor, no behavior change); trust-gated by host; untrusted resolves false. Terminates a spawned driver session (Stop hard-kill / completion graceful). See changes: fix-automation-stop-zombie-runs, add-goal-session-supervisor. ServerPluginContext + ServerContextDeps gain `onSessionEnded(handler)` (OnSessionEndedFn) — subscribe session-end/unregister, returns unsubscribe; fired from the transport/liveness layer even when no terminal pi event was forwarded. See change: finalize-automation-run-on-session-death. ServerPluginContext + ServerContextDeps gain optional `modelRuntime` (`PluginModelRuntime` = `{getModelRegistry():Promise<PluginModelRegistry\|null>, streamSimple:PluginStreamSimpleFn}`) — in-process model registry + streamSimple seam (creds resolved server-side; no model-proxy loopback); absent when the proxy is unavailable so plugins degrade. New types `PluginModelRuntime`/`PluginModelRegistry`/`PluginStreamSimpleFn`. Host wires it in `server.ts` mirroring the grammar-route adapter (`system`→`context.systemPrompt`). See change: make-grammar-fully-plugin-contained. ServerPluginContext + ServerContextDeps gain optional `isPiExtensionInstalled(name): Promise<boolean>` (`IsPiExtensionInstalledFn`) — registry-backed boolean-only existence oracle, ungated, host-cached ~30s, union of global+local scopes; scan failure REJECTS (never resolves false); OPTIONAL so injected test contexts / older hosts stay valid (plugin owns the fallback); host wires via `createIsPiExtensionInstalled` (installed-probe.ts). See change: add-blackhole-session-pipeline. `RegisterPiHandlerFn` widened to `(msg, sessionId)` — the CONNECTION's registered session id, passed through `dispatchPluginPiMessage`, NOT `msg.sessionId`; lets a plugin attribute a bridge message as a trust decision (mint a session-scoped credential) rather than a hint. Additive: `(msg)`-only handlers stay valid. See change: add-dashboard-mcp-server. `PluginSpawnOptions` gains optional `scope` block (`tools`/`excludeTools`/`noBuiltinTools`/`noTools`/`skills`/`noSkills`/`extensions`/`extensionConfig`) constraining the spawned session's tool/skill/extension surface. Deliberately NO `noExtensions` toggle — disabling extension discovery would stop the dashboard bridge loading and make the session uncontrollable (design D2/D6); `extensions` is an additive allowlist, discovery still runs. New export `pluginSpawnToSessionOptions(opts): MappedSpawnOptions` — total, pure mapper (NEVER throws; plugin input is untrusted JS). Reproduces the old inline hook literal (`strategy:"headless"`, `--model`, `--name` from `automationRun?.name`) + flattens `scope` to flat `SessionFlags` argv fields plus `extensionConfig` (env). Sanitizes: malformed containers treated as absent, non-string/empty/NUL argv entries dropped, NUL env values dropped; conflicting `noTools`+`tools` BOTH forwarded (pi arbitrates, D3). New type `MappedSpawnOptions` (extends `SessionFlags` + `strategy` + `extensionConfig`). Host hook in `server.ts` calls the mapper BEFORE `pendingAutomationRunRegistry.enqueue` so a rejected input cannot strand a stale `cwd`-keyed stamp (D7). `scope.extensionConfig` values widened to `string \| string[]` — scalar env verbatim, array `JSON.stringify` (D8). ServerPluginContext + ServerContextDeps gain `registerCwdPolicy(cwd, policy)` / `unregisterCwdPolicy(cwd)` (RegisterCwdPolicyFn/UnregisterCwdPolicyFn) + type `PluginCwdPolicy` (tightening fields only, NO extensions/extensionConfig) — pin a host cwd capability floor; trust-gated (priority≤100, untrusted no-op); host wraps the server's `CwdPolicyRegistry`, which THROWS on extension fields / overly-broad targets. See change: add-plugin-spawn-scope (Part B host-cwd-policy). |