# NODE.DC AI Workspace Assistant

Platform-owned shared state service for the cross-surface AI Workspace assistant.

It owns:

- user AI Workspace executors/devices and selected executor;
- assistant conversation/thread metadata;
- surface context and enabled tool packs;
- linked artifacts between Engine, Ops and future platform surfaces.

It does not replace the AI Workspace Hub. The Hub remains the thin transport/rendezvous layer for remote Codex workers.

It does not replace the legacy Ops MCP config flow:

```text
external Codex client -> ~/.codex/config.toml -> nodedc-ops-agent MCP -> Ops Agent Gateway
```

That flow remains a separate product path.

## API

All endpoints require the shared internal token. App BFFs pass the resolved platform subject through:

```text
X-NODEDC-User-Id
X-NODEDC-User-Email
X-NODEDC-User-Role
X-NODEDC-User-Groups
```

`Role` and `Groups` are identity inputs for entitlement adapters. App backends still own object-level ACL enforcement.

Executor registry:

```text
GET    /api/ai-workspace/assistant/v1/executors
POST   /api/ai-workspace/assistant/v1/executors
PATCH  /api/ai-workspace/assistant/v1/executors/:executorId
DELETE /api/ai-workspace/assistant/v1/executors/:executorId
POST   /api/ai-workspace/assistant/v1/executors/:executorId/select
```

Conversations:

```text
GET   /api/ai-workspace/assistant/v1/threads
POST  /api/ai-workspace/assistant/v1/threads
GET   /api/ai-workspace/assistant/v1/threads/:threadId
PATCH /api/ai-workspace/assistant/v1/threads/:threadId
POST  /api/ai-workspace/assistant/v1/threads/:threadId/messages
GET   /api/ai-workspace/assistant/v1/threads/:threadId/messages
```

Assistant actions:

```text
POST /api/ai-workspace/assistant/v1/actions
```

This is the platform entrypoint for ontology-backed assistant tool calls. Natural-language chat dispatch still goes through the selected assistant model/executor first; `ontology-core` is the deterministic policy/action layer the assistant calls after it has interpreted the user's intent.

Request shape:

```json
{
  "phase": "preview",
  "input": {
    "actionId": "hub.user.block",
    "targetUserId": "user_123",
    "confirmed": true,
    "idempotencyKey": "hub.user.block:user_admin:user_123"
  }
}
```

For write execution, first call `phase=preview`, show the returned `action.confirmation` to the user, then call `phase=execute` with `confirmationToken`. The final mutation still goes through the app-owned Launcher admin API guards; this service only routes the registered action.

Launcher action gateway configuration:

```text
NDC_ONTOLOGY_LAUNCHER_BASE_URL=http://launcher.local.nodedc
NDC_LAUNCHER_INTERNAL_ACCESS_TOKEN=...
```

If `NDC_LAUNCHER_INTERNAL_ACCESS_TOKEN` is not set, the service falls back to `NODEDC_INTERNAL_ACCESS_TOKEN`.

Launcher entitlement adapter configuration:

```text
AI_WORKSPACE_LAUNCHER_ENTITLEMENT_URL=http://launcher:5173/api/ai-workspace/internal/v1/entitlements
AI_WORKSPACE_LAUNCHER_ENTITLEMENT_TOKEN=...
AI_WORKSPACE_LAUNCHER_ENTITLEMENT_REQUIRED=true
```

The Launcher adapter is evaluated after app-owned adapters and may return `launcher`, `ops`, and `engine` app grants. A Launcher denial is the top-level service-access decision and clears MCP servers for that app in the run profile; app-owned adapters still own app-local scopes and runtime tokens.

The first HUB action ids exposed to assistants are:

- `hub.user.read_admin_summary`
- `hub.invite.list_pending`
- `hub.access_request.list_pending`

Read actions can be executed without user confirmation after the assistant has selected the structured action. Privileged/write actions still require explicit preview, user confirmation, and execute.

App BFFs should pass active scope facts in action input (`clientId`, `coreAssistantRole`, `membershipRole`, `membershipStatus`) so policy stays data-driven instead of prompt-driven.

Supported initial surfaces:

- `engine`
- `ops`
- `global`

Supported initial tool packs:

- `engine`
- `ops`
- `ndc-agent-core`
- `deploy`
- `docs`

## Run Profile

Every dispatch builds an `ai-workspace.run-profile.v1` payload. The raw profile is sent only to the bridge worker; public API responses and run metadata keep MCP headers redacted.

The canonical cross-surface execution contract is [AI Workspace Protocol v1](./docs/AI_WORKSPACE_PROTOCOL_V1.md). It defines layer ownership, run profile shape, tool manifests, worker boundaries, Hub boundaries, app adapter rules, and the migration path from the current scaffold.

The worker uses `runProfile.toolProfile.mcpServers` to create an isolated per-run `CODEX_HOME/config.toml`. Install-time MCP config remains only as a legacy fallback when no dynamic profile is available.

Runtime deploy readiness is tracked in [TEST_MATRIX.md](./TEST_MATRIX.md). The `smoke:run-profile` script covers the entitlement adapter to run profile contract and public secret redaction.

## Entitlement Adapters

App grants can be resolved dynamically before dispatch through adapter endpoints. Configure adapters with:

```text
AI_WORKSPACE_ENTITLEMENT_ADAPTERS_JSON='{"ops":{"url":"https://ops.example/api/ai-workspace/entitlements","tokenEnv":"NODEDC_INTERNAL_ACCESS_TOKEN","required":false}}'
```

or app-specific variables:

```text
AI_WORKSPACE_OPS_ENTITLEMENT_URL=https://ops.example/api/ai-workspace/entitlements
AI_WORKSPACE_OPS_ENTITLEMENT_TOKEN=...
AI_WORKSPACE_OPS_ENTITLEMENT_REQUIRED=false
AI_WORKSPACE_ENGINE_ENTITLEMENT_URL=https://engine.example/api/ai-workspace/internal/v1/entitlements
AI_WORKSPACE_ENGINE_ENTITLEMENT_TOKEN=...
AI_WORKSPACE_ENGINE_ENTITLEMENT_REQUIRED=false
```

Adapter request body:

```json
{
  "schemaVersion": "ai-workspace.entitlement-request.v1",
  "appId": "ops",
  "owner": {},
  "activeContext": {},
  "runContext": {},
  "requestedAt": "2026-06-13T00:00:00.000Z"
}
```

Adapter response can return `appGrants`, `grants`, `grant`, `appGrant`, or a top-level grant with `mcpServers` and `scopes`. Adapter grants override matching settings grants for the current run only.
