Skip to content

feat(client,spec): ai.agents.* and ai.pendingActions.* — the AI routes the SDK could not reach (#3718) - #3888

Merged
os-zhuang merged 2 commits into
mainfrom
claude/ai-namespace-unreachable-0qbyx2
Jul 28, 2026
Merged

feat(client,spec): ai.agents.* and ai.pendingActions.* — the AI routes the SDK could not reach (#3718)#3888
os-zhuang merged 2 commits into
mainfrom
claude/ai-namespace-unreachable-0qbyx2

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

#3718 deleted three client.ai.* methods whose URLs no route had ever mounted, then expressed the surface that does exist. It expressed one builder's worth of it.

service-ai mounts seven. Widening its ledger (objectstack-ai/cloud#903) counted ten routes the SDK cannot reach — nine of which had simply never been counted, because nothing enumerated the other six builders.

This closes the six with the strongest evidence: objectui already ships product on them, over URLs it builds by hand because there is nothing to call.

before after
ai.* methods 10 17
AI routes the SDK can reach 12 18
gap rows in cloud's ledger 10 4 (after the follow-up)

ai.agents

/ai/chat talks to the environment's default agent; these talk to one you name.

  • agents.list() — the agents this caller may chat with. The route filters by the caller's permissions (ADR-0049), so an empty list is a legitimate answer for a seat-less user, not an error to retry.
  • agents.chat(name, request) — forces stream: false, for the same reason ai.chat does: the route streams by default, so leaving the flag to the caller makes the JSON path the one you have to remember.
  • agents.chatStream(name, request) — same route, streaming mode. One route, two methods, mirroring ai.chat / ai.chatStream rather than inventing a third shape for the same endpoint.

ai.pendingActions

The human-in-the-loop approval queue. When a tool call needs a human decision the turn parks an action instead of executing it, and an app embedding the chat has to render and resolve that queue.

  • list(options?)status, conversationId, limit only. AIService.listPendingActions also accepts objectName, but the route never forwards it; typing it here would offer a filter that silently does nothing.
  • get(id)
  • approve(id) — approves and executes. Check the returned status: a tool that fails after approval comes back { status: 'failed', error } on HTTP 200, because the approval succeeded even though the execution did not. Code reading only res.ok reports a failed write as a success.
  • reject(id, reason?) — executes nothing.

Reads and decisions are separately permissioned server-side (ai:read vs ai:approve), so a caller that can list the queue may still be refused on approve. One does not imply the other.

Typed from what the routes return

Not from what a client might like them to — the failure #3718 exists to punish. Every shape here was read off the handler in cloud:

  • Pending actions are the persisted row, snake_case on the wire because that is what it is, not a style slip.
  • Agent rows require capabilities — that object is what tells a UI which affordances to render, so a row without it is not a row this route returns.

Why the capstone still holds

client-url-conformance.test.ts exempts /api/v1/ai/ by prefix and says the evidence lives on the other side of the repo boundary. It does — and it now reaches these: cloud's ledger drives every ai.* method against the tables its builders really return, and since #903 that means all seven.

The comment there still described the one-builder version, under which buildAgentRoutes() and buildPendingActionRoutes() were invisible. Updated, because a comment that outlives its fact is how an exemption quietly becomes a wave-through — which is the original #3718 bug, not a hypothetical.

Verification

  • client 184/184, spec 6836/6836.
  • protocol.test.ts extended with the new shapes including the negative cases: an agent row without capabilities, a non-enum status filter, approve yielding "rejected" (that is the other route).
  • Mutation-checked: pointing ai.pendingActions.get off the AI prefix fails the capstone by name — proving the new methods are genuinely in its sweep rather than silently absent, which is the way a guard like this fails.
  • Generated artifacts regenerated: api-surface.json (+20 exports, 0 breaking) and json-schema.manifest.json.

Follow-up, not in this PR

cloud's ledger flips those six rows gap → sdk and drops the ratchets to 0 — it needs this merged and the pin bumped first, the same sequencing as #3840 → cloud#902. objectui then replaces its hand-built URLs with client.ai.*; that one can be scheduled independently.

🤖 Generated with Claude Code

https://claude.ai/code/session_01WJX6GnuNix7HisBc92THMN


Generated by Claude Code

…outes the SDK could not reach (#3718)

#3718 deleted three `client.ai.*` methods whose URLs no route had ever mounted,
then expressed the surface that does exist. It expressed ONE builder's worth of
it. `service-ai` mounts seven; widening its ledger (objectstack-ai/cloud#903)
counted ten routes the SDK cannot reach, nine of which had never been counted.

This closes the six with the strongest evidence: objectui already ships product
on them, over URLs it hand-builds because there is nothing to call.

ai.agents — `/ai/chat` talks to the default agent; these talk to a named one.
  list()                      — agents this CALLER may chat with; the route
                                filters by permission (ADR-0049), so empty is a
                                legitimate answer, not an error to retry
  chat(name, req)             — forces `stream: false`, same reason `ai.chat`
                                does: the route streams by default
  chatStream(name, req)       — same route, streaming mode. One route, two
                                methods, mirroring chat/chatStream rather than
                                inventing a third shape

ai.pendingActions — the HITL approval queue an embedding app must render.
  list(options?)              — status/conversationId/limit ONLY. The service
                                also accepts objectName; the route never
                                forwards it, so typing it would offer a filter
                                that silently does nothing
  get(id)
  approve(id)                 — approves AND executes. `{status:'failed'}` comes
                                back on HTTP 200: the approval succeeded, the
                                execution did not. Reading only `res.ok` reports
                                a failed write as a success
  reject(id, reason?)         — executes nothing

Typed from what the routes RETURN, not from what a client might like them to —
the failure #3718 exists to punish. Pending actions are the persisted row,
snake_case on the wire because that is what it is; agent rows require
`capabilities` because that object is what tells a UI what to render.

The capstone's `/api/v1/ai/` prefix exemption says the evidence lives across the
repo boundary. It does, and it now reaches these: cloud's ledger drives every
`ai.*` method against the tables its builders really return, and since #903 that
means all seven. Comment updated there — it still described the one-builder
version, under which `buildAgentRoutes()` and `buildPendingActionRoutes()` were
invisible.

Verification: client 184/184, spec 6836/6836 (24 in protocol.test.ts, extended
with the new shapes incl. the negative cases — an agent row without
capabilities, a non-enum status filter, `approve` yielding "rejected").
Mutation-checked: pointing `ai.pendingActions.get` off the AI prefix fails the
capstone by name, proving the new methods are really in its sweep rather than
silently absent. Generated artifacts regenerated: api-surface.json (+20 exports,
0 breaking) and json-schema.manifest.json.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WJX6GnuNix7HisBc92THMN
@vercel

vercel Bot commented Jul 28, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectstack Ignored Ignored Jul 28, 2026 2:34pm

Request Review

@github-actions github-actions Bot added size/m documentation Improvements or additions to documentation tests tooling labels Jul 28, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 2 package(s): @objectstack/client, @objectstack/spec.

108 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/ai/agents.mdx (via @objectstack/spec)
  • content/docs/ai/skills-reference.mdx (via packages/client, @objectstack/spec)
  • content/docs/ai/skills.mdx (via @objectstack/spec)
  • content/docs/api/client-sdk.mdx (via @objectstack/client, @objectstack/spec)
  • content/docs/api/data-flow.mdx (via @objectstack/client)
  • content/docs/api/environment-routing.mdx (via @objectstack/client, @objectstack/spec)
  • content/docs/api/error-catalog.mdx (via @objectstack/client, @objectstack/spec)
  • content/docs/api/error-handling-client.mdx (via @objectstack/spec)
  • content/docs/api/error-handling-server.mdx (via @objectstack/spec)
  • content/docs/api/index.mdx (via @objectstack/spec)
  • content/docs/automation/approvals.mdx (via packages/spec)
  • content/docs/automation/flows.mdx (via @objectstack/spec)
  • content/docs/automation/hook-bodies.mdx (via packages/spec)
  • content/docs/automation/hooks.mdx (via @objectstack/spec)
  • content/docs/automation/index.mdx (via @objectstack/spec)
  • content/docs/automation/webhooks.mdx (via @objectstack/spec)
  • content/docs/automation/workflows.mdx (via @objectstack/spec)
  • content/docs/concepts/architecture.mdx (via @objectstack/spec)
  • content/docs/concepts/design-principles.mdx (via packages/spec)
  • content/docs/concepts/index.mdx (via @objectstack/spec)
  • content/docs/concepts/metadata-driven.mdx (via @objectstack/spec)
  • content/docs/concepts/metadata-lifecycle.mdx (via packages/spec)
  • content/docs/concepts/north-star.mdx (via packages/spec)
  • content/docs/data-modeling/analytics.mdx (via @objectstack/spec)
  • content/docs/data-modeling/drivers.mdx (via @objectstack/spec)
  • content/docs/data-modeling/external-datasources.mdx (via @objectstack/spec)
  • content/docs/data-modeling/field-types.mdx (via @objectstack/spec)
  • content/docs/data-modeling/fields.mdx (via @objectstack/spec)
  • content/docs/data-modeling/formulas.mdx (via @objectstack/spec)
  • content/docs/data-modeling/index.mdx (via @objectstack/spec)
  • content/docs/data-modeling/objects.mdx (via @objectstack/spec)
  • content/docs/data-modeling/queries.mdx (via @objectstack/spec)
  • content/docs/data-modeling/schema-design.mdx (via @objectstack/spec)
  • content/docs/data-modeling/seed-data.mdx (via @objectstack/spec)
  • content/docs/data-modeling/validation-rules.mdx (via @objectstack/spec)
  • content/docs/data-modeling/validation.mdx (via @objectstack/spec)
  • content/docs/deployment/cli.mdx (via @objectstack/spec)
  • content/docs/deployment/troubleshooting.mdx (via @objectstack/spec)
  • content/docs/deployment/validating-metadata.mdx (via @objectstack/spec)
  • content/docs/getting-started/build-with-claude-code.mdx (via @objectstack/spec)
  • content/docs/getting-started/common-patterns.mdx (via @objectstack/spec)
  • content/docs/getting-started/examples.mdx (via @objectstack/spec)
  • content/docs/getting-started/quick-reference.mdx (via @objectstack/spec)
  • content/docs/getting-started/quick-start.mdx (via @objectstack/spec)
  • content/docs/getting-started/your-first-project.mdx (via @objectstack/client, @objectstack/spec)
  • content/docs/kernel/cluster.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/auth-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/cache-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/data-engine.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/index.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/metadata-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/storage-service.mdx (via packages/spec)
  • content/docs/kernel/index.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/data-service.mdx (via packages/client)
  • content/docs/kernel/runtime-services/email-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/index.mdx (via packages/client, packages/spec)
  • content/docs/kernel/runtime-services/queue-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/sharing-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/sms-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/storage-service.mdx (via packages/spec)
  • content/docs/kernel/services-checklist.mdx (via @objectstack/spec)
  • content/docs/permissions/authentication.mdx (via @objectstack/client)
  • content/docs/permissions/authorization.mdx (via @objectstack/spec)
  • content/docs/permissions/permission-sets.mdx (via @objectstack/spec)
  • content/docs/permissions/permissions-matrix.mdx (via @objectstack/spec)
  • content/docs/permissions/positions.mdx (via @objectstack/spec)
  • content/docs/permissions/rls.mdx (via @objectstack/spec)
  • content/docs/permissions/sharing-rules.mdx (via @objectstack/spec)
  • content/docs/plugins/adding-a-metadata-type.mdx (via @objectstack/spec)
  • content/docs/plugins/development.mdx (via @objectstack/spec)
  • content/docs/plugins/index.mdx (via @objectstack/spec)
  • content/docs/plugins/packages.mdx (via @objectstack/client, @objectstack/spec)
  • content/docs/protocol/backward-compatibility.mdx (via @objectstack/spec)
  • content/docs/protocol/diagram.mdx (via packages/spec)
  • content/docs/protocol/kernel/config-resolution.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/i18n-standard.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/index.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/lifecycle.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/plugin-spec.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/realtime-protocol.mdx (via @objectstack/client)
  • content/docs/protocol/kernel/runtime-capabilities.mdx (via @objectstack/spec)
  • content/docs/protocol/knowledge.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/index.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/query-syntax.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/schema.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/security.mdx (via packages/spec)
  • content/docs/protocol/objectql/state-machine.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/actions.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/concept.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/index.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/layout-dsl.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/record-alert.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/widget-contract.mdx (via @objectstack/spec)
  • content/docs/releases/implementation-status.mdx (via @objectstack/client, @objectstack/spec)
  • content/docs/releases/index.mdx (via @objectstack/spec)
  • content/docs/releases/v12.mdx (via @objectstack/spec)
  • content/docs/releases/v13.mdx (via @objectstack/spec)
  • content/docs/releases/v16.mdx (via @objectstack/client, @objectstack/spec)
  • content/docs/releases/v9.mdx (via @objectstack/spec)
  • content/docs/ui/actions.mdx (via @objectstack/spec)
  • content/docs/ui/create-vs-edit-form.mdx (via @objectstack/spec)
  • content/docs/ui/dashboards.mdx (via @objectstack/spec)
  • content/docs/ui/forms.mdx (via @objectstack/spec)
  • content/docs/ui/index.mdx (via @objectstack/spec)
  • content/docs/ui/public-data-collection.mdx (via @objectstack/spec)
  • content/docs/ui/setup-app.mdx (via @objectstack/spec)
  • content/docs/ui/translations.mdx (via @objectstack/spec)
  • content/docs/ui/views.mdx (via @objectstack/spec)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

… new ai methods

`check:docs` regenerates `content/docs/references` from the schemas and fails
when the committed copy drifts. The seven new AI schemas produce new reference
entries, so that check went red on the first push — the artifact half of the
same commit, not a separate defect.

Also updates the hand-written SDK doc, which enumerates the whole `client.ai.*`
surface and carried the #3718 history note. Leaving it at ten methods while
shipping seventeen would be the exact drift this line of work keeps closing —
and the docs-drift check flagged `content/docs/api/client-sdk.mdx` as affected,
which on inspection it genuinely was (the other 107 files it lists are
package-level fan-out, unrelated to this diff).

The added block documents the two things a caller gets wrong by default: an
access-filtered agent catalog is legitimately EMPTY for a seat-less user, and
`pendingActions.approve` returns `{ status: 'failed' }` on HTTP 200 when the
tool fails after approval — reading only `res.ok` calls that a success.

check:docs / check:api-surface / check:spec-changes / check:upgrade-guide all
pass; 250 generated files in sync.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WJX6GnuNix7HisBc92THMN
@github-actions github-actions Bot added size/l and removed size/m labels Jul 28, 2026
@os-zhuang
os-zhuang marked this pull request as ready for review July 28, 2026 14:46
@os-zhuang
os-zhuang merged commit 6fdc5c6 into main Jul 28, 2026
17 checks passed
@os-zhuang
os-zhuang deleted the claude/ai-namespace-unreachable-0qbyx2 branch July 28, 2026 14:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/l tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants