Skip to content

[AI-stack P5] plugin-ai: opt-in --gateway centralized AI service #262

Description

@rickylabs

Add an opt-in --gateway mode to the ai plugin that centralizes AI provider access and MCP-OAuth custody behind one NetScript service, sibling to (never replacing) the default in-process path.

Context

Part of epic #238 · cluster plugin-ai · wave defer · depends-on P1, P2, E1

This is the last node in the plugin-ai DAG: it only becomes buildable once the in-process default emitters exist (P1, P2) and the @netscript/ai core contract (E1) is stable enough to implement against. It is deferred because most single-instance deployments never need centralized custody — this slice exists for the scaled/multi-instance case.

Problem / motivation

Without this slice, every emitter call goes straight to the provider in-process, and any MCP-OAuth token or provider secret has to live wherever the calling instance runs. That is fine for one instance; it is exactly the secret-sprawl / hand-rolled-proxy pattern eis-chat fell into once more than one process needed the same authenticated provider session — no single place owns token lifecycle, refresh, or revocation. The doctrine's answer for this class of problem is a thin, opt-in centralizing service (same shape as workers-api in the workers plugin), not a bespoke sidecar. SO-7 specifically calls for centralized MCP-OAuth custody; today nothing in @netscript/ai or the ai plugin provides it.

Scope

Ships

  • plugins/ai/services/src/main.tscreatePluginService shell over @netscript/ai, implementing aiContract as an HTTP service entrypoint (mirrors plugins/workers/services/src/main.ts shape).
  • plugins/ai/src/public/mod.ts — conditional .withService({ name: 'ai-gateway', entrypoint: './services/src/main.ts', port: <assigned> }), registered only when the plugin is configured with --gateway (or the equivalent manifest flag); absent entirely in default mode.
  • Centralized MCP-OAuth custody module behind the gateway service: single token/secret store and refresh path, consumed only by the gateway process (SO-7).
  • Emitter proxy mode: the in-process emitters from P1/P2 detect --gateway and forward calls to the gateway service over the same aiContract surface instead of calling providers directly.

Out of scope

  • Default in-process AI path (emitters calling providers directly) — owned by P1/P2.
  • @netscript/ai core contract, aiContract shape, and provider subpaths (@netscript/ai/anthropic, etc.) — owned by E1 and sibling ENGINE-cluster provider slices.
  • OTel/TelemetryPort wiring — owned by the ./otel slice (E9).
  • fresh-ui chat primitives and ui:add ai registration — owned by FB0.
  • Named orchestration strategies — explicitly out of v1 per the ratified primitives-only decision.

Public surface

  • plugins/ai/services/src/main.ts (new): gateway service entrypoint exporting the createPluginService-built server implementing aiContract.
  • plugins/ai/src/public/mod.ts: conditional .withService(...) contribution, only present when --gateway is set; manifest otherwise unchanged from P1/P2.
  • MCP-OAuth custody port/module used exclusively by the gateway service (path finalized against the P1/P2 plugin-ai layout); no new symbols added to @netscript/ai itself — this slice only consumes aiContract, it does not extend it.

Acceptance & fitness gates

  • F-3: gateway service respects domain -> ports -> application -> adapters layering; deno task arch:check passes for plugins/ai.
  • F-5: public surface (plugins/ai/src/public/mod.ts) carries JSDoc + @module, <=20 exports.
  • F-6: plugins/ai publishes via deno task publish:dry-run WITHOUT --allow-slow-types.
  • F-13: gateway service declares an explicit runtime lifecycle (start/stop(), AbortSignal-driven shutdown), consistent with the workers service reference.
  • gate:e2e — scaffold.runtime case: scaffold a project with the ai plugin configured --gateway, boot Aspire, confirm the ai-gateway service starts, an in-process emitter call proxies through it successfully, and the default (non-gateway) scaffold path is unaffected.
  • MCP-OAuth custody: only the gateway process holds provider OAuth secrets; emitters running in --gateway mode carry no OAuth material locally (SO-7).
  • Default in-process path from P1/P2 has zero behavioral regression when --gateway is unset.

Dependencies

depends-on: P1, P2, E1 (GitHub numbers tracked in the #238 DAG checklist).

Size

L

Part of #238

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions