Found while working #16142: the card's replacement text needed the real behaviour of /api/v1/ai/** when no AI service is mounted, and the two docblocks a reader would reach for disagree with the code. Recorded only — not claimed, no assignee. Out of scope for #16142, which is a README-only content fix.
What the code does
packages/runtime/src/domains/ai.ts, the unserveable-slot branch:
if (!isServiceServeable(aiService)) {
if (denyAnonymous) return anonymousRefusal();
if (method === 'GET' && subPath === '/ai/agents') {
return { handled: true, response: deps.success({ agents: [] }) };
}
// 501, not 404: `/ai/*` IS mounted, so the request reached a handler
// with nothing behind it — see ./unavailable.ts.
return capabilityUnavailable(deps, 'ai');
}
and packages/runtime/src/domains/unavailable.ts returns deps.error(serviceUnavailableMessage(slot), 501), with a docblock that draws the 404-vs-501 distinction deliberately ("The route is not there" = 404, handled by the host router; "The route is there; the implementation is not" = 501).
So: every /ai/* route answers 501 with the same remedy sentence discovery reports under services.ai, except GET /ai/agents, which answers 200 with an empty list.
What two docblocks say
Both still describe the pre-capabilityUnavailable behaviour:
packages/client/src/index.ts:5129-5133 — "This repo's dispatcher only proxies /api/v1/ai/** to whatever buildAIRoutes() mounted, and 404s AI service is not configured when the service is absent (the open-source default)".
packages/runtime/src/route-ledger.ts:401 — "the dispatcher only proxies (or 404s "AI service is not configured")".
packages/spec/src/api/protocol.zod.ts:2736 carries the same sentence in a comment. The docs site is already correct: content/docs/api/client-sdk.mdx says "the route is mounted but unimplemented, so it answers 501 and discovery reports the slot unavailable with the same sentence".
Two further details the stale text loses: the message is no longer a local string at all (it comes from serviceUnavailableMessage, so the 501 body and the discovery entry cannot drift), and the GET /ai/agents empty-list courtesy is not mentioned anywhere in the client docblock, which is the one an SDK reader actually opens.
Why it matters
packages/client/src/index.ts is a published package's source, and its ai docblock is the SDK's own account of what a caller sees without the Cloud/EE service. A caller who codes if (res.status === 404) off that docblock does not handle the answer they will actually get — and 404-vs-501 is exactly the distinction unavailable.ts exists to make: 404 now means the path does not exist, which for /ai/* is false.
Executable criterion
grep -rn '404s' packages/client/src/index.ts packages/runtime/src/route-ledger.ts returns the two claims above; the branch they describe returns capabilityUnavailable(deps, 'ai') → 501. After the fix both read 501, and the GET /ai/agents exception is stated in the client docblock.
Found while working #16142: the card's replacement text needed the real behaviour of
/api/v1/ai/**when no AI service is mounted, and the two docblocks a reader would reach for disagree with the code. Recorded only — not claimed, no assignee. Out of scope for #16142, which is a README-only content fix.What the code does
packages/runtime/src/domains/ai.ts, the unserveable-slot branch:and
packages/runtime/src/domains/unavailable.tsreturnsdeps.error(serviceUnavailableMessage(slot), 501), with a docblock that draws the 404-vs-501 distinction deliberately ("The route is not there" = 404, handled by the host router; "The route is there; the implementation is not" = 501).So: every
/ai/*route answers 501 with the same remedy sentence discovery reports underservices.ai, exceptGET /ai/agents, which answers 200 with an empty list.What two docblocks say
Both still describe the pre-
capabilityUnavailablebehaviour:packages/client/src/index.ts:5129-5133— "This repo's dispatcher only proxies/api/v1/ai/**to whateverbuildAIRoutes()mounted, and 404sAI service is not configuredwhen the service is absent (the open-source default)".packages/runtime/src/route-ledger.ts:401— "the dispatcher only proxies (or 404s "AI service is not configured")".packages/spec/src/api/protocol.zod.ts:2736carries the same sentence in a comment. The docs site is already correct:content/docs/api/client-sdk.mdxsays "the route is mounted but unimplemented, so it answers 501 and discovery reports the slot unavailable with the same sentence".Two further details the stale text loses: the message is no longer a local string at all (it comes from
serviceUnavailableMessage, so the 501 body and the discovery entry cannot drift), and theGET /ai/agentsempty-list courtesy is not mentioned anywhere in the client docblock, which is the one an SDK reader actually opens.Why it matters
packages/client/src/index.tsis a published package's source, and itsaidocblock is the SDK's own account of what a caller sees without the Cloud/EE service. A caller who codesif (res.status === 404)off that docblock does not handle the answer they will actually get — and 404-vs-501 is exactly the distinctionunavailable.tsexists to make: 404 now means the path does not exist, which for/ai/*is false.Executable criterion
grep -rn '404s' packages/client/src/index.ts packages/runtime/src/route-ledger.tsreturns the two claims above; the branch they describe returnscapabilityUnavailable(deps, 'ai')→ 501. After the fix both read 501, and theGET /ai/agentsexception is stated in the client docblock.