Skip to content

feat(cli): make the AI service opt-in via a declared dependency (Community-Edition support) - #2311

Merged
os-zhuang merged 2 commits into
mainfrom
feat/ce-ai-opt-in
Jun 25, 2026
Merged

feat(cli): make the AI service opt-in via a declared dependency (Community-Edition support)#2311
os-zhuang merged 2 commits into
mainfrom
feat/ce-ai-opt-in

Conversation

@os-zhuang

@os-zhuangos-zhuang commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Why

A self-host Community Edition runtime (framework + the MIT console, without the cloud AI package) should serve no AI but still support MCP. Today it can't: the CLI auto-registers the headless AIServicePlugin whenever the ai tier is enabled (default) and @objectstack/service-ai is merely resolvable. In a workspace/monorepo the package is hoist-resolvable even when an app doesn't declare it, so every app gets the AI service — discovery reports services.ai: available and the agent runtime serves any metadata-defined agents — even CE apps that ship no AI.

(Surfaced from the objectui side: the console gates its AI UI on the live agent catalog, but a CE runtime still reported services.ai available with agents served, so the AI surface didn't hide.)

What

1. AI is opt-in via a declared dependency (packages/cli/src/commands/serve.ts). Auto-register the AI service only when the host app declares @objectstack/service-aior@objectstack/service-ai-studio — gating on a declared dep, not mere resolvability, so it's reliable in a monorepo.

constwantsAiService=hostDeclaresDependency('@objectstack/service-ai')||hostDeclaresDependency('@objectstack/service-ai-studio');if(!hasAIPlugin&&tierEnabled('ai')&&wantsAiService){}
  • CE app declares neither → no AI service, no agents, services.ai: { enabled:false, status:'unavailable' } → the console hides its AI surface.
  • MCP unaffected — separate capability (requires: ['mcp']), no service-ai dependency, bridges data/metadata regardless; only optionally bridges the AI tool registry when the ai service is present.
  • Cloud path fixed — AI Studio attaches personas via the base service's ai:ready hook, so declaring Studio now implies (and loads) the base service. Previously a Studio-only app would have loaded neither.
  • app-showcase/app-crm now declare @objectstack/service-ai (they ship AI agents).

For real standalone apps (outside this monorepo) this is a no-op — they already had to declare deps to resolve them. Only in-monorepo apps relying on hoisting were affected (the two examples, now fixed).

2. config.tiers is now honored (packages/spec/src/stack.zod.ts). ObjectStackDefinitionSchema gains a tiers field, so defineStack no longer strips it. config.tiers (e.g. a list withoutai) now actually overrides the --preset default — previously silently dropped by schema validation, which made the --preset help text ("overridden by config.tiers if set") inaccurate. A second, in-place way to disable AI without touching dependencies.

Verified end-to-end (isolated build, fresh runtime)

Scenarioservices.ai/api/v1/ai/agentsAIServiceMCP
CE — declares neitherenabled:false, "unavailable"404not loadedloads + serves (OS_MCP_SERVER_ENABLED=true401 auth-gate = live)
declares service-aienabled:true, "available"serves showcase_assistantloaded
declares onlyservice-ai-studioenabled:true, "available"200loaded (base, for ai:ready)
config.tiers without ai (service-ai declared)enabled:false, "unavailable"404not loaded (tier wins)

@objectstack/cli + @objectstack/spec build clean (tsc). Changeset included (both minor).

🤖 Generated with Claude Code

@vercel

vercelBot commented Jun 25, 2026

Copy link
Copy Markdown

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

ProjectDeploymentActionsUpdated (UTC)
specReadyReadyPreview, CommentJun 25, 2026 2:30am

Request Review

@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation dependencies Pull requests that update a dependency file tooling labels Jun 25, 2026
@github-actions

github-actionsBot commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 3 package(s): @objectstack/cli, @objectstack/runtime, @objectstack/spec.

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

  • content/docs/concepts/architecture.mdx(via @objectstack/spec)
  • content/docs/concepts/cloud-artifact-api.mdx(via packages/cli, packages/runtime, packages/spec)
  • content/docs/concepts/cluster-semantics.mdx(via @objectstack/spec)
  • content/docs/concepts/design-principles.mdx(via packages/spec)
  • content/docs/concepts/implementation-status.mdx(via @objectstack/cli, @objectstack/runtime, @objectstack/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/runtime, packages/spec)
  • content/docs/concepts/packages.mdx(via @objectstack/cli, @objectstack/runtime, @objectstack/spec)
  • content/docs/concepts/setup-app.mdx(via @objectstack/spec)
  • content/docs/concepts/skills.mdx(via @objectstack/spec)
  • content/docs/concepts/webhook-delivery.mdx(via @objectstack/spec)
  • content/docs/getting-started/architecture.mdx(via @objectstack/spec)
  • content/docs/getting-started/cli.mdx(via @objectstack/cli, @objectstack/spec)
  • content/docs/getting-started/core-concepts.mdx(via @objectstack/spec)
  • content/docs/getting-started/examples.mdx(via @objectstack/spec)
  • content/docs/getting-started/quick-start.mdx(via @objectstack/cli, @objectstack/spec)
  • content/docs/guides/adding-a-metadata-type.mdx(via @objectstack/spec)
  • content/docs/guides/ai-capabilities.mdx(via @objectstack/spec)
  • content/docs/guides/airtable-dashboard-analysis.mdx(via @objectstack/spec)
  • content/docs/guides/analytics-datasets.mdx(via @objectstack/spec)
  • content/docs/guides/api-reference.mdx(via @objectstack/runtime, @objectstack/spec)
  • content/docs/guides/authentication.mdx(via @objectstack/cli, @objectstack/runtime)
  • content/docs/guides/business-logic.mdx(via @objectstack/spec)
  • content/docs/guides/cheatsheets/backward-compatibility.mdx(via @objectstack/spec)
  • content/docs/guides/cheatsheets/error-catalog.mdx(via @objectstack/spec)
  • content/docs/guides/cheatsheets/field-type-gallery.mdx(via @objectstack/spec)
  • content/docs/guides/cheatsheets/field-validation-rules.mdx(via @objectstack/spec)
  • content/docs/guides/cheatsheets/permissions-matrix.mdx(via @objectstack/spec)
  • content/docs/guides/cheatsheets/protocol-diagram.mdx(via packages/spec)
  • content/docs/guides/cheatsheets/query-cheat-sheet.mdx(via @objectstack/spec)
  • content/docs/guides/cheatsheets/quick-reference.mdx(via @objectstack/spec)
  • content/docs/guides/client-sdk.mdx(via @objectstack/cli, @objectstack/spec)
  • content/docs/guides/cloud-deployment.mdx(via @objectstack/runtime)
  • content/docs/guides/common-patterns.mdx(via @objectstack/spec)
  • content/docs/guides/contracts/auth-service.mdx(via packages/spec)
  • content/docs/guides/contracts/cache-service.mdx(via packages/spec)
  • content/docs/guides/contracts/data-engine.mdx(via @objectstack/spec)
  • content/docs/guides/contracts/index.mdx(via @objectstack/spec)
  • content/docs/guides/contracts/metadata-service.mdx(via packages/spec)
  • content/docs/guides/contracts/storage-service.mdx(via packages/spec)
  • content/docs/guides/data-modeling.mdx(via @objectstack/spec)
  • content/docs/guides/deployment-vercel.mdx(via @objectstack/runtime, @objectstack/spec)
  • content/docs/guides/driver-configuration.mdx(via @objectstack/runtime, @objectstack/spec)
  • content/docs/guides/error-handling-client.mdx(via @objectstack/spec)
  • content/docs/guides/error-handling-server.mdx(via @objectstack/spec)
  • content/docs/guides/external-datasources.mdx(via @objectstack/spec)
  • content/docs/guides/formula.mdx(via @objectstack/spec)
  • content/docs/guides/hook-bodies.mdx(via packages/cli, @objectstack/runtime, packages/spec)
  • content/docs/guides/kernel-services.mdx(via @objectstack/spec)
  • content/docs/guides/metadata/dashboard.mdx(via @objectstack/spec)
  • content/docs/guides/metadata/field.mdx(via @objectstack/spec)
  • content/docs/guides/metadata/flow.mdx(via @objectstack/spec)
  • content/docs/guides/metadata/index.mdx(via @objectstack/spec)
  • content/docs/guides/metadata/object.mdx(via @objectstack/spec)
  • content/docs/guides/metadata/validation.mdx(via @objectstack/spec)
  • content/docs/guides/metadata/workflow.mdx(via @objectstack/spec)
  • content/docs/guides/packages.mdx(via @objectstack/cli, @objectstack/runtime, @objectstack/spec)
  • content/docs/guides/plugin-chatbot-integration.mdx(via @objectstack/runtime)
  • content/docs/guides/plugin-development.mdx(via @objectstack/spec)
  • content/docs/guides/plugins.mdx(via @objectstack/spec)
  • content/docs/guides/production-readiness.mdx(via @objectstack/runtime)
  • content/docs/guides/project-scoping.mdx(via @objectstack/cli, @objectstack/spec)
  • content/docs/guides/public-forms.mdx(via @objectstack/spec)
  • content/docs/guides/runtime-services/data-service.mdx(via packages/cli)
  • content/docs/guides/runtime-services/email-service.mdx(via packages/spec)
  • content/docs/guides/runtime-services/index.mdx(via packages/cli, packages/spec)
  • content/docs/guides/runtime-services/queue-service.mdx(via packages/spec)
  • content/docs/guides/runtime-services/sharing-service.mdx(via packages/spec)
  • content/docs/guides/runtime-services/storage-service.mdx(via packages/spec)
  • content/docs/guides/security.mdx(via @objectstack/spec)
  • content/docs/guides/seed-data.mdx(via @objectstack/spec)
  • content/docs/guides/single-project-mode.mdx(via @objectstack/runtime)
  • content/docs/guides/skills.mdx(via packages/cli, @objectstack/spec)
  • content/docs/guides/standards.mdx(via @objectstack/spec)
  • content/docs/guides/troubleshooting.mdx(via @objectstack/spec)
  • content/docs/guides/validating-metadata.mdx(via @objectstack/spec)
  • content/docs/protocol/knowledge.mdx(via @objectstack/spec)
  • content/docs/protocol/objectos/config-resolution.mdx(via @objectstack/spec)
  • content/docs/protocol/objectos/http-protocol.mdx(via @objectstack/runtime)
  • content/docs/protocol/objectos/i18n-standard.mdx(via @objectstack/spec)
  • content/docs/protocol/objectos/index.mdx(via @objectstack/runtime)
  • content/docs/protocol/objectos/lifecycle.mdx(via @objectstack/runtime, @objectstack/spec)
  • content/docs/protocol/objectos/plugin-spec.mdx(via @objectstack/cli, @objectstack/spec)
  • content/docs/protocol/objectos/realtime-protocol.mdx(via @objectstack/cli)
  • content/docs/protocol/objectos/runtime-capabilities.mdx(via @objectstack/spec)
  • content/docs/protocol/objectql/index.mdx(via packages/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 packages/spec)
  • content/docs/protocol/objectui/record-alert.mdx(via @objectstack/spec)
  • content/docs/protocol/objectui/widget-contract.mdx(via @objectstack/spec)
  • content/docs/releases/index.mdx(via @objectstack/spec)
  • content/docs/releases/v9.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.

…nor config.tiers)
The CLI auto-registered the headless AIServicePlugin whenever the `ai` tier was
enabled (default) and @objectstack/service-ai was merely *resolvable*. In a
workspace/monorepo the package is hoist-resolvable even when an app doesn't
declare it, so every app got the AI service — discovery reported services.ai
available and the agent runtime served any metadata-defined agents — including
Community-Edition apps that ship no AI.
Make the DECLARED dependency the AI edition boundary: auto-register AIService
only when the host app declares @objectstack/service-ai OR
@objectstack/service-ai-studio (Studio attaches its personas via the base
service's `ai:ready` hook, so declaring Studio implies the base — load it even
when only Studio is in the deps). A CE app that declares neither gets no AI
service, no agents, and services.ai { enabled:false, status:'unavailable' } in
discovery — so the MIT console hides its AI surface — while MCP and every other
capability are unaffected.
Also honor `config.tiers`: ObjectStackDefinitionSchema gains a `tiers` field so
defineStack stops stripping it. `config.tiers` (e.g. a list without `ai`) now
actually overrides the `--preset` default — previously silently dropped by schema
validation, which made the `--preset` help text inaccurate. A second, in-place
lever to disable AI without touching dependencies.
Declare @objectstack/service-ai in the showcase and crm examples (they ship AI
agents) so they keep AI.
Verified e2e in an isolated build:
- app WITHOUT service-ai/studio → discovery AI unavailable, /api/v1/ai/agents
404, AIService not loaded; MCP server still starts + serves (401 auth-gate).
- app declaring service-ai (or studio-only) → services.ai available + agents served.
- config.tiers without `ai` → AIService not loaded even when service-ai IS declared.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@os-zhuangos-zhuang changed the title feat(cli): make the AI service opt-in via a declared @objectstack/service-ai dependency (Community-Edition support)feat(cli): make the AI service opt-in via a declared dependency (Community-Edition support)Jun 25, 2026
runtime/security/api-key.ts re-exports @objectstack/core's extractApiKey, which
accepts a Bearer token carrying the `osk_` api-key prefix (for remote MCP
clients — core's own test covers this). This duplicate test in runtime predated
that behavior and asserted the old "Bearer is never an api-key" rule, so it
failed whenever runtime's tests run.
It was latent on main: turbo only runs runtime's tests when runtime or a
dependency changes, and main's recent commits didn't. This PR's
@objectstack/spec change makes spec "changed", so every spec-dependent package's
tests now run — surfacing the pre-existing failure. Align the assertion with
core + the implementation (extractApiKey is re-exported, so they must agree).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@github-actionsgithub-actionsBot added tests and removed tests labels Jun 25, 2026
@os-zhuang
os-zhuang merged commit 4845c12 into mainJun 25, 2026
18 checks passed
@os-zhuang
os-zhuang deleted the feat/ce-ai-opt-in branch June 25, 2026 02:39
os-zhuang added a commit to objectstack-ai/objectui that referenced this pull request Jun 25, 2026
…he agent catalog (#1992)
useAiSurfaceEnabled now keys off discovery's `services.ai` (isAiEnabled) — i.e.
whether the enterprise @objectstack/service-ai capability is present — instead of
a non-empty agent catalog.
service-ai is an enterprise capability; a Community-Edition runtime doesn't ship
it, so the framework doesn't register the AI service and discovery reports
services.ai unavailable → the whole AI surface hides. An install that has
service-ai reports it available → AI shows. The presence of the CAPABILITY gates,
not whether a specific agent is configured yet.
The earlier catalog-based gating was a workaround for the headless service-ai
reporting itself available in CE. The framework now only registers the AI service
when the host app declares @objectstack/service-ai (objectstack-ai/objectstack#2311),
so discovery is an honest edition signal and the catalog detour is gone.
Everything else from the original gating stays: the centralized hook, the
RequireAiSurface /ai route guard, the gated top-bar link + designer "Ask AI"
buttons, and AiChatPage's graceful empty state.
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
os-zhuang added a commit that referenced this pull request Jun 25, 2026
…ability) (#2317)
* chore: bump objectui to 8a3b351a5a32
fix(console): gate the AI surface on the service-ai capability, not the agent catalog (#1992)
objectui@8a3b351a5a320e6769a1616e85a6fd05aabd3279
* chore(showcase): remove the AI agent — service-ai is an enterprise capability
The showcase defined a `showcase_assistant` agent + a skill + two tools, all of
which need the enterprise `@objectstack/service-ai` runtime to be served. The
open-source showcase shouldn't require an enterprise capability, so drop the AI
block entirely:
- delete `src/agents/index.ts` (agent + skill + tools) and its config wiring
(`agents` / `skills` / `tools`);
- remove the `@objectstack/service-ai` dependency (added in #2311 to back the
agent — no longer needed);
- drop the descriptive `ai` entry from the coverage map and update the seed test
(`agents.length` 1 → 0).
The showcase now boots as a Community-Edition shape: no AI service is registered,
discovery reports `services.ai` unavailable, and `/api/v1/ai/agents` 404s — so
the console hides its AI surface. `tsc --noEmit` + all 39 showcase tests pass.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
os-zhuang added a commit that referenced this pull request Jun 25, 2026
…ity (#2319)
Mirror the showcase change (#2317): the open-source CRM example shouldn't define
an AI agent that requires the enterprise @objectstack/service-ai runtime.
- delete src/agents/ (SalesAssistantAgent + DealManagementSkill + LookupContactTool)
and its config wiring (agents / skills) plus the unused referenceMetadata export;
- remove the @objectstack/service-ai dependency (added in #2311 to back the agent).
crm now boots as a Community-Edition shape: no AI service registered, discovery
reports services.ai unavailable, /api/v1/ai/agents 404. tsc --noEmit + all 29 crm
tests pass.
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
os-zhuang added a commit that referenced this pull request Jul 6, 2026
fix(components): also toast form submit errors so they're visible past the fold (#2311)
objectui@ffad2a13790c589de88323b932617a2cba93129c
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependenciesPull requests that update a dependency filedocumentationImprovements or additions to documentationsize/stooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@os-zhuang