Introduces the External Capability abstraction: platform-registered,
org-enabled document/media transform services invoked as side effects of
an AgentRun. The first concrete capability is pdf_to_md_bundle backed by
MinerU. This lands the contract, schema, adapter interface, and a fully
mock-tested adapter; the real MinerU HTTP client is deferred until
credentials and pricing are confirmed.
Contract:
- spec/Spec/System/Agent/Capability.lean pins ExternalCapability,
OrganizationCapabilityConnection (reuses ADR-0024 envelope, distinct
from model-provider connection), and CapabilityInvocation.Authorized
(workspace containment predicate, ADR-0018). lake build green (41 jobs).
- ADR-0027 records why capability credentials are a separate connection
type (MinerU/Whisper have their own auth shape, not OpenRouter /v1/models),
the adapter seam, and what is deferred (capability invocation record,
pricebook, agent-facing tool discovery).
Schema:
- OrganizationCapabilityConnection + CapabilityCredentialVersion, mirroring
the Feishu Application Connection shape. Unique by (organizationId,
capabilityId). Migration 20260718130000 applied.
Adapter (hub/src/capability):
- types.ts: CapabilityId, CapabilityAdapter interface, secret payload,
CapabilityConnectionUnavailable.
- mineruClient.ts: MineruClient interface + MineruParseResult + errors.
Real HTTP client deferred; the interface is the seam.
- capabilityConnections.ts: resolveCapabilityCredential — org-scoped,
fail-closed, reuses LocalSecretEnvelope with purpose="capability".
- pdfToMdBundle.ts: adapter that resolves credential, confines input/output
to the run workspace (ADR-0018), calls MineruClient, writes md + images
into workspace, and writes a UsageFact (kind=external_capability,
quantity=pages, costSource=provider_reported|unknown per ADR-0022).
Tests (7, all green):
- md + images written to workspace, UsageFact attributes pages + cost
- costUsd null -> costSource=unknown (missing cost != zero, ADR-0022)
- no ACTIVE connection -> fail-closed, no fact written
- input/output path escape -> CapabilityPathEscape
- MinerU client error propagates, no fact written
- empty markdown -> parse failure
Deferred (needs operator): MinerU account/API key/pricing, real MineruClient
HTTP implementation, org admin UI for capability connection management,
agent-facing tool exposure (MCP vs built-in).
ADR-0022 / Spec.System.Capacity pins layered capacity limits: a platform
ceiling per dimension is unbreakable, and each organization may only set a
lower `organizationLimit`. The effective limit is the minimum of the two
(`LayeredLimit.effective`); dimensions with no org override fall back to
the platform ceiling. No dimension may be unlimited (a ceiling must exist
before an org limit can be set, `LayeredLimit.Valid`).
Add the backend: the pinned 23 CapacityDimension set + labels
(src/capacity/dimensions.ts), platform ceilings sourced from existing
runtime env vars plus `HUB_CEILING_<DIMENSION>` (src/capacity/ceilings.ts),
the OrganizationCapacityPolicy prisma model + migration, the
getCapacityPolicy/setCapacityPolicy service enforcing LayeredLimit.Valid,
and org-admin GET/PUT /api/org/:orgSlug/capacity-policy routes wired into
the org route tree.
Add the admin-web surface: CapacityDimension/CapacityPolicyView api client
types, capacityPolicy/setCapacityPolicy methods, a `容量` nav entry, and a
capacity page that lists every dimension with its platform ceiling (or
`未配置` when unset), an org-limit input (disabled until a ceiling exists),
and a live effective-value column. Saving sends the partial limits map;
the service rejects values above the ceiling or for unconfigured dimensions.
ADR-0023 / Spec.System.PlatformAdministration pins the platform
administrator as a separate identity/session/audit control plane,
intentionally not modeled in alpha (ADR-0025). The legacy
PlatformRoleAssignment / PlatformRole{ADMIN,TEACHER} table had no runtime
reader (no guard, route, or service queried it for an authorization
decision) and ADR-0023 requires it to be replaced before the platform
panel ships.
Drop the model, the PlatformRole enum, the User.platformRoles relation,
and the migration. Stop seeding platformRoles in externalSync principal
ingestion and the integration test helper. Update the doc comments on
OrganizationMembership and PermissionRole to point at the platform-admin
control plane instead of the dropped model.
The 20260709180000_organization_tenant_root backfill only referenced
PlatformRoleAssignment in a one-time INSERT...SELECT; no persistent
object references it, so dropping the table is safe after that migration.
main now ships the canonical org-scoped agent-config implementation
(OrganizationAgentRole/OrganizationAgentSkill + hub/src/agent/configuration.ts
+ hub/src/agent/skillStore.ts per ADR-0018, and envelope-encrypted
OrganizationProviderConnection per ADR-0024). The earlier admin-panel
prototype (OrgModel/OrgRole/simple ProviderConnection baseUrl+authToken,
modelRoutes.ts, agentConfig.ts, migration 20260710120000, admin-web
models/roles pages) is superseded and clashes with main's schema; drop it.
Follow-up still needed: rewire admin-web SPA to the new config APIs
(+layout.svelte nav still lists models/roles, RoleCard.svelte unused).