From 744a7cd8495802e9e8972619b4d45ac62fb493d3 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 2 Sep 2026 07:42:23 +0000 Subject: [PATCH 1/5] skills(ai): cut the closed agent surface, make the flagship example resolve, teach open-edition MCP Restructures skills/objectstack-ai/SKILL.md against the read-only audit: the package spent 26% of its budget teaching the agent surface it declares closed to its own readers, its flagship defineSkill example named four tools that resolve to nothing, and the one AI path that executes without a cloud licence (MCP) had zero coverage. Deletions (each one a construct the reader can still reach): - AI-B-02 agent Required/Optional/Example -> two retirement rows plus a pointer to references/_index.md. - AI-C-01 the "runtime is cloud/EE, open is MCP" fact, restated 8x -> one anchor blockquote defining a bare cloud marker used on the affected headings. - AI-D-03 Structured Output (an agent-only field the file itself calls "declared only"), AI-D-01 "Why Three Tiers?", AI-D-02 competitive positioning, AI-B-01 "When to Use This Skill", AI-C-02 the cloud ops callout (ai_usage_daily has no open-repo object), AI-D-04 the vendor model catalogue, AI-D-05 knowledge best-practices (merged into a hygiene column), AI-D-06 generic prompt-engineering pitfalls, AI-F-04 the defineTool section demoted to a not-the-default-path note. Rewrites: - AI-E-01 the flagship defineSkill example now names query_records, get_record and action_escalate_case, all three of which resolve on the ladder in validate-ai-tool-references.ts:148-171. - AI-A-02 the Model Selection / Temperature taste tables are replaced by the enforced contract (temperature min 0 max 2, agent.zod.ts:38). - Falsehood 1: agent.tools is a retiredKey() tombstone (parse error), not "supported but legacy". - Falsehood 2: the CEL example pointed at a tool availability condition that ToolSchema does not declare; the AI-domain CEL carrier is a model-registry promptTemplate. - AI-G-03 outputSchema is flagged experimental, matching how the file already flags every other declared-only field. Additions, each paid by a deletion in the same file: - Open-edition MCP wiring: MCPServerPlugin, POST /api/v1/mcp, and the tool names a client actually sees, read from packages/mcp/src. - The skill.tools[] resolution ladder and the 30-name platform tool registry. - The trigger-condition operator/value-shape column (a mismatch is a parse error, skill.zod.ts:37-51,139-202). - The KnowledgeServicePlugin wiring the RAG example could not be run without. AI-B-03 reorder: the first customer-authorable section is now first; the closed agent surface is a reference section at the bottom. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1 --- skills/objectstack-ai/SKILL.md | 707 +++++++++++++-------------------- 1 file changed, 267 insertions(+), 440 deletions(-) diff --git a/skills/objectstack-ai/SKILL.md b/skills/objectstack-ai/SKILL.md index b33bf9b6c9..2872ab3213 100644 --- a/skills/objectstack-ai/SKILL.md +++ b/skills/objectstack-ai/SKILL.md @@ -1,56 +1,32 @@ --- name: objectstack-ai description: > - Design ObjectStack AI skills, tools, knowledge sources, conversations, - model registry entries, and MCP integrations. Use when the user is adding - `*.skill.ts` / `*.tool.ts`, configuring an LLM provider, wiring agent - tools, or indexing ObjectStack data as a knowledge source for RAG. Agents - themselves are platform-internal (`ask` / `build`) — third parties extend - them via skills and tools, not by authoring `*.agent.ts`. Do not use for - general LLM prompting questions unrelated to ObjectStack metadata. + Design ObjectStack AI skills, tools, knowledge sources, and the open-edition + MCP server surface. Use when the user is adding `*.skill.ts` / `*.tool.ts`, + configuring an LLM provider, wiring the tools a skill grants, or indexing + ObjectStack data as a knowledge source for RAG. Agents themselves are + platform-internal (`ask` / `build`) — third parties extend them via skills, + not by authoring `*.agent.ts`. Do not use for general LLM prompting questions + unrelated to ObjectStack metadata. license: Apache-2.0 compatibility: Requires @objectstack/spec 17.x (Zod v4 schemas) metadata: author: objectstack-ai - version: "1.3" + version: "1.4" domain: ai - tags: agent, tool, skill, conversation, llm, embedding, mcp + tags: agent, tool, skill, knowledge, llm, embedding, mcp --- # AI Agent Design — ObjectStack AI Protocol -Expert instructions for designing AI skills, tools, and knowledge sources — -and the platform agents they plug into — using the ObjectStack specification. -This skill covers the Agent → Skill → Tool three-tier architecture aligned with -Salesforce Agentforce, Microsoft Copilot Studio, and ServiceNow Now Assist -patterns. - -> **Edition boundary (`service-ai` → cloud; open = MCP-only).** -> The in-UI AI **runtime** — the `ask` / `build` agents, in-product chat, and the -> `/api/v1/ai/*` routes (`@objectstack/service-ai`) — ships in the **cloud / -> Enterprise** distribution, not the open framework. The agent / skill / tool -> **schemas** in `@objectstack/spec/ai` stay open, so you author `*.skill.ts` / -> `*.tool.ts` as source either way (`*.agent.ts` is platform-internal) — but -> they only execute in a cloud / EE host. On the **open edition** there is no in-product agent: expose the -> app to your own AI via `@objectstack/mcp` (BYO-AI) for data query, and author -> metadata in **source mode** with an AI coding agent (Claude Code, Cursor). - ---- - -## When to Use This Skill - -- You need to define **skills** — bundles of related tools bound to the - `ask` / `build` surfaces. -- You are configuring **tools** for data queries, actions, or integrations. -- You want to index ObjectStack data as a **knowledge source** for RAG - retrieval. -- You are choosing and configuring **LLM models** (model registry). -- You need to read or review **agent** configuration — platform-internal; - third parties extend agents via skills, not by authoring them. - ---- - -## Three-Tier Architecture +> **Edition boundary. ☁️ marks a cloud / Enterprise surface, and is not restated +> below.** The in-UI AI **runtime** — the `ask` / `build` agents, in-product +> chat, `/api/v1/ai/*` (`@objectstack/service-ai`) — ships cloud / EE, not in the +> open framework. The **schemas** in `@objectstack/spec/ai` stay open, so you +> author `*.skill.ts` / `*.tool.ts` as source either way, but they only execute +> in a cloud / EE host. On the **open edition** there is no in-product agent: +> expose the app to your own AI over **MCP** (below), and author metadata in +> **source mode** with an AI coding agent (Claude Code, Cursor). ``` Agent → Skill → Tool @@ -60,190 +36,57 @@ Agent → Skill → Tool └─ Autonomous actor with role, instructions, and guardrails ``` -### Why Three Tiers? - -| Tier | Analogy | Reuse Level | -|:-----|:--------|:------------| -| **Agent** | Job role (e.g., "Help Desk Agent") | Per use-case | -| **Skill** | Competency (e.g., "Case Management") | Across agents | -| **Tool** | Specific operation (e.g., "create_record") | Across skills | - -> **Best practice:** Always model via Skills first. Direct tool assignment to -> agents is supported but considered legacy. Skills provide better -> discoverability, instruction scoping, and reuse. - -### Built-in agents: `ask` & `build` (ADR-0063 / ADR-0064) - -The runtime ships **exactly two** platform agents, bound by *surface* — the user -never picks from a roster; the surface they are in selects the agent: - -- **`ask`** — the **data product** (≈ Claude Chat). Conversational read / query / - explore over records, plus running the business **actions** the app already - exposes. End-user audience, RLS-bounded. Canonical id `ask` (`ASK_AGENT_NAME`). - **Cloud / Enterprise** — the `ask` runtime ships in the closed cloud AI runtime - (`@objectstack/service-ai`); it is the implicit copilot for any cloud / EE app - that does not pin `app.defaultAgent`. (Open editions have no in-product `ask`; - use MCP.) -- **`build`** — the **authoring product** (≈ Claude Code). Agentic authoring of - *metadata* (objects, fields, views, flows) through plan → draft → verify → - publish. Builder audience, governance-gated. Canonical id `build`. Cloud-only · - paid — ships in the cloud AI Studio plugin; Studio pins it via `app.defaultAgent`. - -There is **no per-turn intent classifier**: a `build`-shaped request arriving at -`ask` is declined and redirected to the Builder, never silently re-routed into -authoring (ADR-0063 §1/§5). - -> **Legacy names are aliases only.** `data_chat`→`ask` and -> `metadata_assistant`→`build` resolve through the alias table for old bookmarks -> and persisted `agent_id`s; they are **not** vocabulary — always write `ask` / -> `build`. `*.agent.ts` is closed to third parties (`agent` type is -> `allowRuntimeCreate:false, allowOrgOverride:false`): you extend the platform -> with **skills**, never by authoring an agent (ADR-0063 §2). - -#### Skill → agent affinity: the `surface` field (ADR-0063 §3) - -Every skill declares which surface it binds to via -`surface: 'ask' | 'build' | 'both'` (defaults to `'ask'`). A skill may bind only -to an agent whose surface it matches; `'both'` binds to either. The runtime -enforces this in `resolveActiveSkills` at load time — an incompatible binding is a -**fast load error**, not a silent mis-scope. An agent's tool set is the **union of -its surface-compatible skills' tools** — there is no global fall-through -(ADR-0064), so `ask` cannot author by construction. - -The built-in skills and their affinities: - -| Skill | `surface` | Owns | -|---|---|---| -| `schema_reader` | `both` | `list_objects`, `describe_object`, `query_data` | -| `data_explorer` | `ask` | `query_records`, `get_record`, `aggregate_data`, `visualize_data` | -| `actions_executor` | `ask` | `action_*` (the business actions an object exposes) | -| `metadata_authoring` + `solution_design` | `build` | metadata draft / verify / publish + blueprint propose / apply | - -To grant data exploration to your own (platform-internal) agent, add -`data_explorer` / `schema_reader` to its `skills[]`; deactivating a skill -(`active: false`) revokes that capability for every agent that references it. - -> **All four are cloud-runtime skills — none resolves on OSS.** The open -> framework ships *no* in-product agent (there is no `service-ai` package), so -> `surface` / `tools` / `triggerConditions` are authored for cloud and inert. -> What a skill DOES serve on the open edition is its `instructions`, projected -> onto the MCP `prompts` primitive by `@objectstack/mcp`: a skill with blank -> `instructions`, or `active: false`, is not listed as a prompt at all. - -> **`visualize_data`:** the only built-in tool that draws a chart — -> it aggregates an object and emits an inline `data-chart` part. Auto-registered -> **only** when an analytics service (`IAnalyticsService`) is wired; `query_data` / -> `aggregate_data` return numbers, not charts. - -> **Ops:** set `AI_DAILY_USER_MESSAGES=` to cap user turns per user per day -> (backed by the `ai_usage_daily` object; no-op if unset). Adapter health is -> observable at `GET /api/v1/ai/status`; invalid `ai` settings are rejected at -> save time. - ---- - -## Agent Configuration - -> **Reference only — third parties do not author agents.** The `agent` type is -> closed (`allowRuntimeCreate:false`; ADR-0063 §2): the platform ships exactly -> `ask` and `build`, maintained by platform / cloud plugin authors. You extend -> the platform with **skills + tools** (and knowledge sources) — never by -> adding an agent. This section documents `AgentSchema` for reading existing -> agents and for platform-internal work. - -### Required Properties - -| Property | Type | Description | -|:---------|:-----|:------------| -| `name` | `snake_case` | Unique agent identifier | -| `label` | string | Human-readable name | -| `role` | string | Agent's persona/role description | -| `instructions` | string | System prompt — detailed behavioural guidance | - -### Important Optional Properties - -| Property | Purpose | -|:---------|:--------| -| `skills` | Array of skill names — **the** capability model (ADR-0064) | -| ~~`tools`~~ | REMOVED in protocol 17 — the inline slot resolved names against the *full* registry with no surface check. A parse error now; move each reference into a skill | -| `surface` | `'ask' \| 'build'` — the product surface this agent is (default `'ask'`) | -| `model` | LLM model configuration — `provider`, `model`, `temperature`, `maxTokens`, `topP` | -| ~~`knowledge`~~ | REMOVED in protocol 17 — declaring sources/indexes on an agent never scoped retrieval (`search_knowledge` takes `sourceIds` from the LLM's tool-call arguments). Restrict at the knowledge-service/source level; describe intended grounding in `instructions` | -| `guardrails` | `maxTokensPerInvocation`, `maxExecutionTimeSec`, `blockedTopics` — declared only | -| `structuredOutput` | Output format (JSON schema, regex, etc.) — declared only | -| `planning` | Autonomous reasoning — `maxIterations` (default 10) | -| `memory` | `longTerm` persistence + `reflectionInterval` — declared only | -| `permissions` | Permission-set capabilities required to use the agent | -| `active` | Enable/disable the agent | - -There is **no top-level `temperature` / `maxTokens`** on an agent — sampling -parameters live under `model` (`AIModelConfigSchema`). - -### Agent Example - - -```typescript -import { defineAgent } from '@objectstack/spec'; - -export default defineAgent({ - name: 'support_tier_1', - label: 'First Line Support', - role: 'Help Desk Assistant for customer support cases', - instructions: ` - You are a friendly and professional help desk assistant. - - RULES: - - Always greet the customer by name if available. - - Search the knowledge base before creating a new case. - - Escalate to a human agent if the issue is critical or security-related. - - Never share internal system details with customers. - - Respond in the customer's preferred language. - `, - skills: ['case_management', 'knowledge_search'], - model: { - provider: 'openai', - model: 'gpt-4o', - temperature: 0.3, - }, - guardrails: { // declared only — nothing reads these yet - blockedTopics: ['internal_pricing', 'employee_data'], - maxTokensPerInvocation: 8000, - maxExecutionTimeSec: 60, - }, -}); -``` +Agents are platform-internal (☁️ `ask` / `build`; reference section at the +bottom). **Skills are your entry point** — you extend the platform by authoring +skills, and a skill reaches tools by naming them. --- ## Skill Configuration -A **Skill** is a named bundle of tools with dedicated instructions and -trigger conditions. - -### Required Properties +A **Skill** is a named bundle of tools with dedicated instructions and trigger +conditions. -| Property | Type | Description | +| Required | Type | Description | |:---------|:-----|:------------| -| `name` | `snake_case` | Unique skill identifier (`/^[a-z_][a-z0-9_]*$/`) | -| `label` | string | Human-readable name | -| `tools` | `string[]` | Tool names this skill grants access to (trailing wildcard allowed, e.g. `action_*`) | - -### Important Optional Properties +| `name` | `snake_case` | Unique skill id (`/^[a-z_][a-z0-9_]*$/`) | +| `label` | string | Display name | +| `tools` | `string[]` | Tool names this skill grants (trailing wildcard allowed, e.g. `action_*`) — every name must resolve; see the ladder below | -| Property | Purpose | +| Optional | Purpose | |:---------|:--------| -| `surface` | `'ask' \| 'build' \| 'both'` — agent surface affinity (default `'ask'`; see above) | -| `description` | What the skill does — helps the agent decide when to use it | -| `instructions` | LLM prompt guidance specific to this skill's context | -| `triggerConditions` | Programmatic activation rules | -| `active` | Is the skill enabled (default: `true`) | - -> A skill has **no `permissions` key** — removed in 17.0.0. Skill invocation -> was never gated by it (the registry reads only `active` / `triggerConditions` / -> `tools`), and a security-shaped field that enforces nothing is worse than no -> field at all. Gate access at the **agent** instead — `access` / `permissions` on -> `defineAgent` are enforced at the chat route — or on the underlying actions the -> skill's tools call (permission sets, ADR-0066). +| `surface` | `'ask' \| 'build' \| 'both'` — which platform agent may bind this skill (default `'ask'`). Enforced in `resolveActiveSkills` at load: an incompatible binding is a **fast load error** (ADR-0063 §3) | +| `description` | What the skill does — the agent reads it to decide when to use the skill | +| `instructions` | Prompt guidance for this skill's context. **The one field the open edition reads**: `@objectstack/mcp` projects it onto the MCP `prompts` primitive, so a skill with blank `instructions` — or `active: false` — is not listed as a prompt at all | +| `triggerConditions` | Programmatic activation rules (below) | +| `active` | Enabled (default `true`) | + +> A skill has **no `permissions` key** — removed in 17.0.0 because it gated +> nothing. Gate at the **agent** (`access` / `permissions` on `defineAgent`, +> enforced at the chat route) or on the actions the tools call (ADR-0066). + +### Naming the tools: the resolution ladder + +A `skill.tools[]` entry resolves against the union of three sources +(`packages/lint/src/validate-ai-tool-references.ts:148-171`): + +1. **`stack.tools[].name`** — records your own stack declares. ADR-0109: the + default third-party path declares **none** (see *Tool metadata* below). +2. **`PLATFORM_PROVIDED_TOOL_NAMES`** — the 30 statically-registered platform + tools, grouped by owning package in + `packages/spec/src/system/constants/platform-tool-names.ts:38-82` — 6 data / + knowledge tools from `service-ai` (`query_records`, `get_record`, + `query_data`, `aggregate_data`, `search_knowledge`, `visualize_data`) and 24 + schema / metadata / package tools from `service-ai-studio`. Read that file for + the exact set: it is the registry the lint rule checks against. +3. **`action_`** — one tool per AI-exposed Action of your own stack + (`PLATFORM_TOOL_FAMILY_PREFIXES`). **The default path** for anything your app + does. + +A name in none of the three raises **`ai-skill-tool-unresolved`** (warning, +ADR-0078 advisory-first). The rule exists because one audited app shipped 10 +fictional tools across 6 skills through `validate` / `lint` clean, and became a +copilot claiming abilities it did not have. ### Skill Example @@ -254,19 +97,16 @@ import { defineSkill } from '@objectstack/spec'; export default defineSkill({ name: 'case_management', label: 'Case Management', - description: 'Create, update, query, and escalate support cases.', + description: 'Triage, query, and escalate support cases.', instructions: ` When managing cases: - Always check for duplicate cases before creating a new one. - - Set priority based on customer tier: Enterprise → High, Pro → Medium, Free → Low. - - Escalated cases must include a summary of actions already taken. + - Set priority by customer tier: Enterprise → High, Pro → Medium, Free → Low. + - Escalated cases must summarise the actions already taken. `, - tools: [ - 'query_support_case', - 'create_support_case', - 'update_support_case', - 'escalate_case', - ], + // Every name resolves: two platform tools (rung 2) plus one action tool + // materialised from this stack's own `escalate_case` Action (rung 3). + tools: ['query_records', 'get_record', 'action_escalate_case'], triggerConditions: [ { field: 'objectName', operator: 'eq', value: 'support_case' }, ], @@ -276,121 +116,111 @@ export default defineSkill({ ### Trigger Conditions -| Operator | Meaning | -|:---------|:--------| -| `eq` | Equals | -| `neq` | Not equals | -| `in` | Value is in array | -| `not_in` | Value is not in array | -| `contains` | String contains substring | - ---- - -## Tool Configuration - -Tools are the atomic operations that skills expose to agents. - -### First-Class Tool Metadata (`defineTool`) - -A tool authored as metadata (`type: 'tool'`, `*.tool.ts`) is validated by -`ToolSchema`: required `name` / `label` / `description`, a **JSON Schema** -`parameters` object, plus optional `objectName` and `outputSchema`. `ToolSchema` -is **strict** — an unknown key (a typo, or a retired key) is a parse error, not -a silent strip. Retired in protocol 17: `category`, `permissions`, -`active` and `builtIn` (all were authorable and inert; `permissions` gated -nothing and `active: false` withdrew nothing — the rejection message carries -each key's replacement), joining `requiresConfirmation`. - - -```typescript -import { defineTool } from '@objectstack/spec'; - -export default defineTool({ - name: 'create_case', - label: 'Create Support Case', - description: 'Creates a new support case record', - parameters: { - type: 'object', - properties: { - subject: { type: 'string', description: 'Case subject' }, - priority: { type: 'string', enum: ['low', 'medium', 'high'] }, - }, - required: ['subject'], - }, - objectName: 'support_case', -}); -``` +Each condition is `{ field, operator, value }`, ANDed with its siblings. The +shape of `value` is **coupled to `operator`** — a mismatch is a parse error, not +a coercion (`packages/spec/src/ai/skill.zod.ts:37-51, 139-202`): -To gate what a tool can do, gate the underlying action -(`action.requiredPermissions`, ADR-0066) or the objects it touches; to withdraw -a tool, remove it from the skills/agents that reference it. Categorization, if -you need it, belongs on the action side (`action.ai.category` — live, but -listing-only: carried onto the tool, never sent to the model). +| Operator | Meaning | `value` must be | +|:---------|:--------|:----------------| +| `eq` | Equals | a **string** — an array is reference identity, so it never matches | +| `neq` | Not equals | a **string** — an array would always fire | +| `in` | In list | an **array** (empty `[]` is allowed and is a real predicate) | +| `not_in` | Not in list | an **array** | +| `contains` | Substring, or array subset | **either** — both spellings execute | -> **Tool metadata is a read-only projection — not an execution entry point.** -> `ToolSchema` has no `handler` / `implementation` field, and no framework -> executor loads a metadata-authored tool. The runtime executes a -> separately-registered `AIToolDefinition` (cloud `@objectstack/service-ai`); -> tool metadata is a one-way projection for Studio / discovery. Do not expect a -> hand-authored tool to run in the open edition. +Nothing matches phrases: write routing as `triggerConditions` and +natural-language intent in `description` / `instructions`, where the model reads it. -### Actions as AI Tools — opt-in +--- -> **Cloud / EE runtime.** `registerActionsAsTools()`, `AIServicePlugin`, and -> the HITL approval queue below ship in `@objectstack/service-ai` — the closed -> cloud / Enterprise runtime, not an open package. On the open edition, expose -> actions to your own AI via `@objectstack/mcp` instead. +## Actions as AI Tools — the default path ☁️ -You usually **don't author tool definitions by hand** for action invocation. An `Action` you attach to an object via `defineObject({ actions: [...] })` becomes a tool named `action_` **only when it opts in** — `ai.exposed: true` (default `false`) plus an `ai.description` of ≥ 40 chars, refused by the parse without it (ADR-0011). `registerActionsAsTools()` (from `AIServicePlugin`) walks the opted-in ones; exposure is never automatic, and there is no opt-out key. +You usually **don't author tool definitions by hand** for action invocation. An +`Action` attached to an object via `defineObject({ actions: [...] })` becomes a +tool named `action_` **only when it opts in** — `ai.exposed: true` +(default `false`) plus an `ai.description` of ≥ 40 chars, refused by the parse +without it (ADR-0011). `registerActionsAsTools()` walks the opted-in ones; +exposure is never automatic, and there is no opt-out key. Three action types dispatch headlessly: | `action.type` | Dispatch | Wiring | |:---|:---|:---| | `script` | `IDataEngine.executeAction(object, target, ctx)` — same as Studio's row toolbar | none | -| `api` | HTTP call to `action.target` (`fetch`-based by default) | `AIServicePlugin({ apiActionBaseUrl, apiActionHeaders })` or custom `apiClient` | +| `api` | HTTP call to `action.target` (`fetch`-based by default) | `AIServicePlugin({ apiActionBaseUrl, apiActionHeaders })` or a custom `apiClient` | | `flow` | `IAutomationService.execute(target, { triggerData })` | `automation` service registered with the kernel | -**Skipped even when opted in:** -- UI-only types (`url`, `modal`, `form`). -- Dangerous variants — the declared `mode: 'delete'` / `variant: 'danger'` only (`confirmText` is dialog copy, *not* a destructive signal; `ai.requiresConfirmation` overrides either way) — **unless** the plugin is started with `enableActionApproval: true`, in which case they route through the HITL approval queue (see below). - -**`type:'api'` body assembly** (last wins): user params → `recordIdParam` (using `recordIdField`, default `'id'`) → `bodyExtra`. `bodyShape: { wrap: 'data' }` nests user params under `data` while keeping `recordIdParam` flat. +**Skipped even when opted in:** UI-only types (`url`, `modal`, `form`); and +dangerous variants — the declared `mode: 'delete'` / `variant: 'danger'` only +(`confirmText` is dialog copy, *not* a destructive signal; `ai.requiresConfirmation` +overrides either way) — **unless** the plugin runs with `enableActionApproval: true`, +which routes them through the approval queue instead. + +**`type:'api'` body assembly** (last wins): user params → `recordIdParam` (using +`recordIdField`, default `'id'`) → `bodyExtra`. `bodyShape: { wrap: 'data' }` +nests user params under `data` while keeping `recordIdParam` flat. + +**Human-in-the-loop.** `AIServicePlugin({ enableActionApproval: true })` (default +`false`) persists an `ai_pending_actions` row instead of running a dangerous +action and returns `{ status: 'pending_approval', pendingActionId }`; an operator +approves or rejects in Studio's **AI Pending Actions** inbox (or via +`GET/POST /api/v1/ai/pending-actions/...`). Whether *your* action is held is +decided entirely by the skip rule above — `actionSkipReason(action, ctx)` and +`actionRequiresApproval(action)` (from `@objectstack/service-ai`) answer both +questions, and Studio's "AI exposure" diagnostics use the same predicates. -Use `actionSkipReason(action, ctx)` (exported from `@objectstack/service-ai` — cloud-only, not importable on the open edition) when authoring an action and you want to know *why* it isn't surfacing in chat. Studio's "AI exposure" diagnostics use the same predicate. Pair with `actionRequiresApproval(action)` to know whether a registered action will be routed through HITL. +--- -### Human-In-The-Loop approval +## MCP — the open-edition AI path -> **Cloud / EE runtime.** The HITL approval queue is part of -> `@objectstack/service-ai` and is not available in the open framework. +`@objectstack/mcp` publishes your app over the Model Context Protocol, so an +external agent (Claude, Cursor, Codex, …) can read it and act on it with **no +cloud licence**. (Consuming *external* MCP servers is the inbound sibling, +`@objectstack/connector-mcp`.) ```ts -kernel.use(new AIServicePlugin({ - enableActionApproval: true, // opt in; default is false - apiActionBaseUrl: process.env.OS_AI_ACTION_API_BASE_URL, -})); +import { MCPServerPlugin } from '@objectstack/mcp'; + +kernel.use(new MCPServerPlugin()); ``` -Flow: -1. LLM picks `action_delete_task` → runtime persists an `ai_pending_actions` row and returns `{ status: 'pending_approval', pendingActionId }`. -2. Operator triages via Studio's **AI Pending Actions** inbox (or the REST endpoints: `GET/POST /api/v1/ai/pending-actions/...`). -3. Approve → service re-runs the action via the pre-registered bypass-approval dispatcher; row transitions to `executed` / `failed`. -4. Reject → row transitions to `rejected` with an optional reason. +The HTTP surface is **default-on**, served per-request by the runtime dispatcher +at **`POST /api/v1/mcp`** — nothing to start (`OS_MCP_SERVER_ENABLED=false` opts +out). A long-lived **stdio** transport is a separate opt-in +(`autoStart: true`, or `OS_MCP_SERVER_ENABLED=true`). + +The tools a client then sees — from `registerObjectTools` / `registerActionTools`, +each family gated by the caller's OAuth scopes, every call under the caller's +permissions and row-level security: -Programmatic API on `IAIService`: `proposePendingAction`, `approvePendingAction`, `rejectPendingAction`, `listPendingActions`. All are optional (returns clear error when no `IDataEngine` is wired). +| Tools | What they do | +|:--|:--| +| `list_objects`, `describe_object` | schema introspection (system objects filtered out by default) | +| `query_records`, `get_record` | reads — filter, field selection, sorting, pagination | +| `aggregate_records` | GROUP BY totals; registered only when the data bridge implements `aggregate` | +| `validate_expression` | build-accurate CEL check before a formula is authored into metadata | +| `create_record`, `update_record`, `delete_record` | writes; `delete_record` is annotated destructive | +| `list_actions`, `run_action` | your AI-exposed business Actions — the same `ai.exposed` opt-in as above | + +Your skills' `instructions` reach this surface too: the same package projects +them onto the MCP `prompts` primitive. --- ## Knowledge Sources (RAG) -The platform's RAG primitive is the **KnowledgeSource** -(`KnowledgeSourceSchema` in `@objectstack/spec/ai`): declarative metadata -pairing *what to index* with the id of an `IKnowledgeAdapter` that does the -work. Sources are registered at runtime via -`IKnowledgeService.registerSource()` (there is no `defineStack` collection for -them), and the `search_knowledge` tool exposes registered sources to agents. +The RAG primitive is the **KnowledgeSource** (`KnowledgeSourceSchema` in +`@objectstack/spec/ai`): declarative metadata pairing *what to index* with the id +of an `IKnowledgeAdapter` that does the work. There is no `defineStack` +collection for them — they are registered at runtime, and the ☁️ +`search_knowledge` tool exposes registered sources to agents. -### KnowledgeSource Structure +**Wiring (open packages).** `KnowledgeServicePlugin({ sources })` from +`@objectstack/service-knowledge` registers `IKnowledgeService` with the kernel and +each source at boot (`service.registerSource` still works later). The `adapter` +id resolves against an adapter plugin — `@objectstack/knowledge-memory` +(`'memory'`, dev/test) or `@objectstack/knowledge-ragflow` (`'ragflow'`); +without one the id is unresolvable. | Property | Purpose | |:---------|:--------| @@ -399,20 +229,16 @@ them), and the `search_knowledge` tool exposes registered sources to agents. | `adapter` | Adapter id (e.g. `'ragflow'`, `'memory'`), resolved via `IKnowledgeService.registerAdapter` | | `adapterConfig` | Adapter-specific configuration (opaque to the service) | | `source` | What gets indexed — discriminated on `kind`: `'object'` \| `'file'` \| `'http'` | -| `embedding` | Optional `EmbeddingModelSchema` ref (`provider`, `model`, `dimensions`) — adapters that manage embeddings internally (RAGFlow, Dify, Vectara) may ignore it | +| `embedding` | Optional `EmbeddingModelSchema` ref (`provider`, `model`, `dimensions`) — adapters managing embeddings internally (RAGFlow, Dify, Vectara) may ignore it | | `vectorStore` | Optional `VectorStoreSchema` ref (`provider`, `collection`) — same caveat | -| `refresh` | `onRecordChange` (default `true` for object sources) + optional `cron` (surfaced for an external scheduler, not self-scheduled) | -| `aiExposed` | Whether `search_knowledge` may expose this source to agents (default `true`) | - -Source kinds: - -| `source.kind` | Fields | -|:--------------|:-------| -| `object` | `object`, `contentFields[]` (min 1; `*` = every readable text field), `metadataFields?`, `where?` (ObjectQL `where` syntax) | -| `file` | `prefix` (storage prefix, e.g. `kb/handbooks/`), `mimeTypes?` | -| `http` | `urls[]`, `userAgent?` | +| `refresh` | `onRecordChange` (default `true` for object sources) + optional `cron` (for an external scheduler; not self-scheduled) | +| `aiExposed` | May `search_knowledge` expose this source to agents (default `true`) — `false` indexes it without making it agent-searchable | -### Knowledge Source Example +| `source.kind` | Fields | Hygiene | +|:--------------|:-------|:--------| +| `object` | `object`, `contentFields[]` (min 1; `*` = every readable text field), `metadataFields?`, `where?` (ObjectQL `where` syntax) | `where` keeps drafts and archived rows out of the index; `contentFields` takes meaningful text only (never system fields or ids, `*` sparingly); project what you will filter on at query time (`status`, `owner_id`, `tags`) into `metadataFields` | +| `file` | `prefix` (storage prefix, e.g. `kb/handbooks/`), `mimeTypes?` | narrow the prefix instead of indexing a whole bucket | +| `http` | `urls[]`, `userAgent?` | enumerate URLs explicitly — there is no crawler | ```typescript @@ -433,153 +259,155 @@ export const supportKb: KnowledgeSource = KnowledgeSourceSchema.parse({ }); ``` -> **Chunking, top-K, score thresholds, and rerankers are NOT platform -> metadata.** The spec deliberately scopes them out (`embedding.zod.ts`): -> chunking strategies, retrieval pipelines, and RAG orchestration belong to -> the adapter (`adapterConfig`) or application code. The platform only carries -> the embed + vector primitives so any RAG strategy can be built on top. - -### Knowledge Source Best Practices - -1. **Filter with `where`.** Index only published/active records - (`where: { published: true }`) so draft or archived content never enters - the index. -2. **Index only meaningful text via `contentFields`.** Do not include system - fields or IDs; use `*` (all readable text fields) sparingly. -3. **Project filter fields into `metadataFields`** (e.g. `status`, `owner_id`, - `tags`) so searches can be narrowed at query time. -4. **Hide with `aiExposed: false`** when a source should be indexed but not - agent-searchable. -5. **Tune relevance in the adapter, not the metadata.** Top-K, thresholds, and - reranking are configured in your RAG backend (via `adapterConfig`), not in - ObjectStack metadata. +> **Chunking, top-K, score thresholds and rerankers are NOT platform metadata.** +> The spec scopes them out deliberately (`embedding.zod.ts`): they belong to the +> adapter (`adapterConfig`) or application code. The platform carries only the +> embed + vector primitives, so any RAG strategy can be built on top. --- -## Model Configuration - -### Supported Providers - -| Provider | Models | Use Case | -|:---------|:-------|:---------| -| `openai` | GPT-4o, GPT-4o-mini, o1, o3-mini | General purpose, reasoning | -| `anthropic` | Claude Sonnet 4, Claude Haiku | Long context, safety | -| `azure_openai` | Same as OpenAI, enterprise managed | Compliance, data residency | -| `local` | Ollama, vLLM, llama.cpp | On-premise, air-gapped | - -> The inline agent `model.provider` enum is the narrow set above -> (`openai` / `azure_openai` / `anthropic` / `local`). **Model-registry** entries -> (`ModelProviderSchema`) accept a wider set: also `google`, `cohere`, -> `huggingface`, `custom`. - -### Model Selection Guidelines - -| Scenario | Recommended | -|:---------|:------------| -| Complex reasoning, multi-step planning | GPT-4o / Claude Sonnet 4 | -| High-volume, low-latency | GPT-4o-mini / Claude Haiku | -| Sensitive data, on-premise | Local models via Ollama | -| Structured data extraction | Any model + `structuredOutput` config | - -### Temperature Guidelines - -| Value | Use Case | -|:------|:---------| -| `0.0–0.3` | Factual Q&A, data extraction, code generation | -| `0.3–0.7` | Conversational agents, customer support | -| `0.7–1.0` | Creative writing, brainstorming | -| `> 1.0` | Experimental / highly creative (use with caution) | +## Tool metadata (`defineTool`) — not the default path + +**Default: declare no tool records.** A skill names a platform tool or an +`action_` (ladder above). ADR-0109: a `stack.tools` record "exists only for +AI-presentation refinement (Phase 2: LLM description, parameter narrowing, flow +exposure) and has no runtime reader until that lands" +(`packages/spec/src/stack.zod.ts:595-602`). Author one only to refine how an +existing capability is *presented* — never to add one, and never to make +something executable: `ToolSchema` has no `handler` / `implementation` field, and +no framework executor loads a metadata-authored tool. + +`ToolSchema` is **strict** — an unknown key (a typo, or a retired one) is a parse +error, not a silent strip. Required `name` / `label` / `description` and a JSON +Schema `parameters` object; optional `objectName` and `outputSchema` (⚠️ +experimental — its top-level keys are folded into the tool description shown to +the model, and outputs are **not** validated). Retired in protocol 17: +`category`, `permissions`, `active`, `builtIn` — all authorable and inert — +joining `requiresConfirmation`; each rejection carries its replacement. + +Gate what a tool can do on the underlying action (`action.requiredPermissions`, +ADR-0066) or the objects it touches; to withdraw one, remove it from the skills +referencing it. Categorization belongs on the action (`action.ai.category` — +live, but listing-only: carried onto the tool, never sent to the model). --- -## Structured Output - -Force the agent to respond in a specific format: +## Model Configuration -```typescript -structuredOutput: { - format: 'json_schema', - schema: { - type: 'object', - properties: { - summary: { type: 'string' }, - priority: { type: 'string', enum: ['low', 'medium', 'high'] }, - action_items: { type: 'array', items: { type: 'string' } }, - }, - required: ['summary', 'priority'], - }, - strict: true, // enforce exact schema compliance (default: false) - maxRetries: 3, // max retries on validation failure (default: 3) -} -``` +Sampling parameters live under an agent's `model` (`AIModelConfigSchema`) — +there is **no top-level `temperature` / `maxTokens`** on an agent +(`packages/spec/src/ai/agent.zod.ts:36-40`): -On validation failure the runtime retries by default -(`retryOnValidationFailure: true`). Optional extras: `fallbackFormat` and a -`transformPipeline` of post-processing steps (`trim`, `parse_json`, -`validate`, `coerce_types`). There is no `retry` object — the knobs are -`retryOnValidationFailure` + `maxRetries`. +| Key | Contract | +|:--|:--| +| `provider` | Inline enum `openai` \| `azure_openai` \| `anthropic` \| `local` (default `openai`). **Model-registry** entries (`ModelProviderSchema`) accept a wider set: also `google`, `cohere`, `huggingface`, `custom` | +| `model` | Free string — the provider's own model name | +| `temperature` | `min(0).max(2)`, default `0.7`. **Outside 0–2 is a parse error** | +| `maxTokens` / `topP` | Optional numbers, unbounded by the schema | --- ## Common Pitfalls -1. **Overly broad instructions.** Agents with vague instructions hallucinate - more. Be specific about what the agent should and should not do. -2. **Too many tools per skill.** Keep skills focused (3–8 tools). If a skill - has 15+ tools, split it. -3. **Mistaking `guardrails` for a gate.** `guardrails` / `memory` / - `structuredOutput` are declared only — no runtime reads them, and real - limits come from the quota service. For a gate that is **enforced**, use - `enableActionApproval: true` (HITL queue, cloud) for AI-exposed actions, - `ai.requiresConfirmation` on the **action**, or `approval: 'always'` on an - MCP tool binding. AI metadata edits are already gated: they land as drafts a - human must publish (ADR-0033). - ⚠️ `requiresConfirmation` on the **tool** was REMOVED (ADR-0033 §2) — - it was read by no execution path, so it produced no pause. `ToolSchema` is - strict, so authoring it now fails the parse with the migration attached. - There is no `requireApprovalFor` field. -4. **Ignoring tool descriptions.** The LLM uses tool `description` to decide - when to call it. Poor descriptions = wrong tool selection. -5. **Expecting a phrase to activate a skill.** Nothing matches phrases; write - routing as `triggerConditions` and intent in `description`/`instructions`. -6. **Indexing everything.** A knowledge source without a `where` filter and +1. **Mistaking `guardrails` for a gate.** `guardrails` / `memory` / + `structuredOutput` are declared only — no runtime reads them, and real limits + come from the quota service. For a gate that is **enforced**, use + `enableActionApproval: true` (approval queue ☁️), `ai.requiresConfirmation` on + the **action**, or `approval: 'always'` on an MCP tool binding. AI metadata + edits are already gated: they land as drafts a human must publish (ADR-0033). + ⚠️ `requiresConfirmation` on the **tool** was REMOVED (ADR-0033 §2) — no + execution path read it, so it produced no pause; `ToolSchema` is strict, so + authoring it now fails the parse. There is no `requireApprovalFor` field. +2. **Indexing everything.** A knowledge source without a `where` filter and curated `contentFields` fills the index with drafts and boilerplate that pollute retrieval. Source hygiene is the metadata's job; relevance tuning - (top-K, thresholds, reranking) belongs to the adapter. + belongs to the adapter. --- -## App AI Blueprint (Skills + Tools + Knowledge) - -Reference layout for a scaffolded app: +## App AI Blueprint | Layer | File | Pattern | |:--|:--|:--| -| Reusable skill | `src/skills/lead-qualification.skill.ts` | `defineSkill` — `instructions` + trigger conditions + bounded toolset; pick a `surface` | -| Tool metadata | `src/tools/query-leads.tool.ts` | `defineTool` — JSON-Schema `parameters`; a discovery projection, not an executor (see caveat above) | -| Knowledge source | `src/knowledge/sales-kb.ts` | `KnowledgeSourceSchema` metadata, registered at runtime via `IKnowledgeService.registerSource()` | -| Central registration | `defineStack({ skills: [...], tools: [...] })` | `agents` / `tools` / `skills` are the only AI stack collections — knowledge sources have none; agents are platform-supplied | +| Reusable skill | `src/skills/lead-qualification.skill.ts` | `defineSkill` — `instructions` + trigger conditions + a toolset where every name resolves; pick a `surface` | +| Knowledge source | `src/knowledge/sales-kb.ts` | `KnowledgeSourceSchema` metadata, registered at boot via `KnowledgeServicePlugin({ sources })` | +| Central registration | `defineStack({ skills: [...] })` | `agents` / `tools` / `skills` are the only AI stack collections — knowledge sources have none, agents are platform-supplied, and `tools` is not the default path | -Default for metadata apps: push business capability logic into **skills**, keep -tools atomic, and wire domain knowledge through **knowledge sources**. +Default for metadata apps: push business capability into **skills**, expose +business logic as **AI-exposed Actions**, wire domain knowledge through +**knowledge sources**. + +--- + +## Platform agents — reference only ☁️ + +The `agent` type is **closed** to third parties (`allowRuntimeCreate:false`, +`allowOrgOverride:false`; ADR-0063 §2) and `os g agent` is retired: a +stack-authored agent is filtered out of `listAgents()`, refused outright by +`loadAgent()`, and 404s on chat. You extend the platform with **skills**, never +by adding an agent. `AgentSchema`'s full field list is in +[references/_index.md](./references/_index.md). + +The runtime ships **exactly two** agents, bound by *surface* — the user never +picks from a roster, the surface they are in selects the agent. **`ask`** is the +data product (≈ Claude Chat): conversational read / query / explore over records +plus running the business actions the app exposes, end-user audience, +RLS-bounded, canonical id `ask` (`ASK_AGENT_NAME`), and the implicit copilot for +any app that does not pin `app.defaultAgent`. **`build`** is the authoring +product (≈ Claude Code): agentic authoring of *metadata* through plan → draft → +verify → publish, builder audience, governance-gated, pinned by Studio via +`app.defaultAgent`. There is **no per-turn intent classifier** — a `build`-shaped +request arriving at `ask` is declined and redirected, never silently re-routed +into authoring (ADR-0063 §1/§5). `data_chat`→`ask` and +`metadata_assistant`→`build` resolve through the alias table for old bookmarks +and persisted `agent_id`s; they are **not** vocabulary — always write `ask` / +`build`. + +An agent's tool set is the **union of its surface-compatible skills' tools** — +there is no global fall-through (ADR-0064), so `ask` cannot author by +construction; deactivating a skill (`active: false`) revokes that capability for +every agent referencing it. The built-in skills: + +| Skill | `surface` | Owns | +|---|---|---| +| `schema_reader` | `both` | `list_objects`, `describe_object`, `query_data` | +| `data_explorer` | `ask` | `query_records`, `get_record`, `aggregate_data`, `visualize_data` | +| `actions_executor` | `ask` | `action_*` (the business actions an object exposes) | +| `metadata_authoring` + `solution_design` | `build` | metadata draft / verify / publish + blueprint propose / apply | + +All four are cloud-runtime skills — none resolves on the open edition, so +`surface` / `tools` / `triggerConditions` on your own skills are authored for +cloud and inert there; their `instructions` are what MCP serves. +`visualize_data` is the only built-in that draws a chart (an inline `data-chart` +part), auto-registered **only** when an analytics service (`IAnalyticsService`) +is wired — `query_data` / `aggregate_data` return numbers, not charts. + +Two `AgentSchema` keys are **tombstones**, not legacy options — authoring either +is a parse error carrying its migration +(`packages/spec/src/ai/agent.zod.ts:234, :251`): + +| Retired key | REMOVED in protocol 17 — why, and what replaces it | +|:--|:--| +| `agent.tools` | The inline slot resolved names against the *full* registry with no surface check. Not a rename: the migration deletes the key and names each tool that was listed; re-declare each in a skill (ADR-0064) | +| `agent.knowledge` | Declaring sources/indexes on an agent never scoped retrieval — `search_knowledge` takes `sourceIds` from the model's tool-call arguments. Restrict at the knowledge-service / source level; describe intended grounding in `instructions` | --- ## Verify your work -After authoring a `*.skill.ts` / `*.tool.ts` (or platform-internal -`*.agent.ts`) or a model-registry entry, run the author-time gate before -reporting done: +After authoring a `*.skill.ts` / `*.tool.ts` or a model-registry entry, run the +author-time gate before reporting done: ```bash os validate # Zod schema + CEL predicate validation + bindings (no artifact) # or: os build # the same gates, plus emits dist/ ``` -It confirms the agent/tool/model metadata conforms to the protocol and that any -CEL predicate (e.g. a tool's availability condition) parses and resolves. In a -scaffolded project the gate is `npm run validate`. See objectstack-platform → -**Verify your work**. +It confirms the skill / tool / model metadata conforms to the protocol and that +any CEL predicate parses and resolves — in this domain that is a model-registry +`promptTemplate.system` / `.user`; `ToolSchema` carries no expression field of +any kind. In a scaffolded project the gate is `npm run validate`. See +objectstack-platform → **Verify your work**. --- @@ -589,4 +417,3 @@ See [references/_index.md](./references/_index.md) for the full list of Zod schemas (with one-line descriptions) — pointers into `node_modules/@objectstack/spec/src/`. Always `Read` the source for exact field shapes; do not rely on memory of property names. - From e1be4c7b4153e799d92e51133ef7b3808a073b46 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 2 Sep 2026 07:42:23 +0000 Subject: [PATCH 2/5] skills(ai): regenerate the skill catalog index for the new description Generator output only (`pnpm --filter @objectstack/spec gen:skill-docs`) after the AI-A-01 frontmatter edit. No hand edit to skills/README.md prose. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1 --- content/docs/ai/skills-reference.mdx | 8 ++++---- skills/README.md | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/content/docs/ai/skills-reference.mdx b/content/docs/ai/skills-reference.mdx index 270b9894f5..8ea1b7a74b 100644 --- a/content/docs/ai/skills-reference.mdx +++ b/content/docs/ai/skills-reference.mdx @@ -48,7 +48,7 @@ ObjectStack ships **11 skills** — one per authoring domain, plus process skill | 3 | [Query](#query) | `query` | `skills/objectstack-query/` | Construct ObjectQL queries — filters, sorting, pagination, aggregation, relation expansion, and full-text search. | | 4 | [UI](#ui) | `ui` | `skills/objectstack-ui/` | Author ObjectStack UI metadata — Views (list/form/kanban/calendar/gantt), Apps (navigation), Pages (structured plus the HTML and React source-authoring tiers, ADR-0080/0081), Dashboards, Reports, Charts, Actions, and package Docs (`src/docs/*.md`). | | 5 | [Automation](#automation) | `automation` | `skills/objectstack-automation/` | Design ObjectStack automation — Flows (visual logic), Triggers, Approvals, state machines, scheduled jobs, and webhooks. | -| 6 | [AI](#ai) | `ai` | `skills/objectstack-ai/` | Design ObjectStack AI skills, tools, knowledge sources, conversations, model registry entries, and MCP integrations. | +| 6 | [AI](#ai) | `ai` | `skills/objectstack-ai/` | Design ObjectStack AI skills, tools, knowledge sources, and the open-edition MCP server surface. | | 7 | [API](#api) | `api` | `skills/objectstack-api/` | Design the server-side API surface that an ObjectStack runtime exposes — REST endpoints, auth providers, realtime channels, error envelopes, batch/versioning contracts. | | 8 | [i18n](#i18n) | `i18n` | `skills/objectstack-i18n/` | Author ObjectStack translation bundles — object/field labels, view text, app navigation strings, automation messages — and configure locale fallback, coverage reporting, and the per-locale source layout. | | 9 | [Formula](#formula) | `expression` | `skills/objectstack-formula/` | Author CEL expressions used across ObjectStack — formula fields, field conditional rules (`visibleWhen`, `readonlyWhen`, `requiredWhen`), validation / sharing / visibility predicates, flow conditions, and dynamic seed values. | @@ -131,13 +131,13 @@ Do not use for data lifecycle hooks at the object layer (see objectstack-data) o **Domain** `ai` · **Path** `skills/objectstack-ai/` -Design ObjectStack AI skills, tools, knowledge sources, conversations, model registry entries, and MCP integrations. +Design ObjectStack AI skills, tools, knowledge sources, and the open-edition MCP server surface. -Use when the user is adding `*.skill.ts` / `*.tool.ts`, configuring an LLM provider, wiring agent tools, or indexing ObjectStack data as a knowledge source for RAG. Agents themselves are platform-internal (`ask` / `build`) — third parties extend them via skills and tools, not by authoring `*.agent.ts`. +Use when the user is adding `*.skill.ts` / `*.tool.ts`, configuring an LLM provider, wiring the tools a skill grants, or indexing ObjectStack data as a knowledge source for RAG. Agents themselves are platform-internal (`ask` / `build`) — third parties extend them via skills, not by authoring `*.agent.ts`. Do not use for general LLM prompting questions unrelated to ObjectStack metadata. -**Tags:** `agent`, `tool`, `skill`, `conversation`, `llm`, `embedding`, `mcp` +**Tags:** `agent`, `tool`, `skill`, `knowledge`, `llm`, `embedding`, `mcp` --- diff --git a/skills/README.md b/skills/README.md index dd1f67036e..44cd035037 100644 --- a/skills/README.md +++ b/skills/README.md @@ -34,7 +34,7 @@ apps too). | [Query](./objectstack-query/SKILL.md) | `query` | Construct ObjectQL queries — filters, sorting, pagination, aggregation, relation expansion, and full-text search. | | [UI](./objectstack-ui/SKILL.md) | `ui` | Author ObjectStack UI metadata — Views (list/form/kanban/calendar/gantt), Apps (navigation), Pages (structured plus the HTML and React source-authoring tiers, ADR-0080/0081), Dashboards, Reports, Charts, Actions, and package Docs (`src/docs/*.md`). | | [Automation](./objectstack-automation/SKILL.md) | `automation` | Design ObjectStack automation — Flows (visual logic), Triggers, Approvals, state machines, scheduled jobs, and webhooks. | -| [AI](./objectstack-ai/SKILL.md) | `ai` | Design ObjectStack AI skills, tools, knowledge sources, conversations, model registry entries, and MCP integrations. | +| [AI](./objectstack-ai/SKILL.md) | `ai` | Design ObjectStack AI skills, tools, knowledge sources, and the open-edition MCP server surface. | | [API](./objectstack-api/SKILL.md) | `api` | Design the server-side API surface that an ObjectStack runtime exposes — REST endpoints, auth providers, realtime channels, error envelopes, batch/versioning contracts. | | [i18n](./objectstack-i18n/SKILL.md) | `i18n` | Author ObjectStack translation bundles — object/field labels, view text, app navigation strings, automation messages — and configure locale fallback, coverage reporting, and the per-locale source layout. | | [Formula](./objectstack-formula/SKILL.md) | `expression` | Author CEL expressions used across ObjectStack — formula fields, field conditional rules (`visibleWhen`, `readonlyWhen`, `requiredWhen`), validation / sharing / visibility predicates, flow conditions, and dynamic seed values. | From e0fdaecbb547d7198e0668fa9384202a6c10e082 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 2 Sep 2026 07:49:34 +0000 Subject: [PATCH 3/5] skills(ai): ratchet down the role-word baseline for the rewritten SKILL.md Mechanical: `node scripts/check-role-word.mjs --update`. The rewrite dropped the file's role-word count 5 -> 1, and the gate's ratchet-DOWN remedy is the author's own. Only that one line changes. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1 --- scripts/role-word-baseline.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/role-word-baseline.json b/scripts/role-word-baseline.json index 37a4411672..c720be531c 100644 --- a/scripts/role-word-baseline.json +++ b/scripts/role-word-baseline.json @@ -33,7 +33,7 @@ "content/docs/releases/v16.mdx": 5, "content/docs/releases/v17.mdx": 2, "content/docs/ui/forms.mdx": 3, - "skills/objectstack-ai/SKILL.md": 5, + "skills/objectstack-ai/SKILL.md": 1, "skills/objectstack-api/SKILL.md": 1, "skills/objectstack-automation/SKILL.md": 1, "skills/objectstack-data/SKILL.md": 2, From 5dabfd2a829fd02ad93df4cf7cc94804e2f57e57 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 2 Sep 2026 08:17:12 +0000 Subject: [PATCH 4/5] skills(ai): name the live stdio switch OS_MCP_STDIO_ENABLED, not the deprecated alias Contract review round 1, FAIL 1. The MCP section taught `OS_MCP_SERVER_ENABLED=true` as the way to start the long-lived stdio transport. That is the LEGACY trigger: `resolveMcpStdioAutoStart()` (packages/types/src/env.ts:333-345) reads `OS_MCP_STDIO_ENABLED` first and returns it clean, while the old var returns `viaDeprecatedAlias: true`, on which `MCPServerPlugin.start()` (packages/mcp/src/plugin.ts:234-239) logs "Starting the stdio transport via OS_MCP_SERVER_ENABLED=true is DEPRECATED". An author copying the sentence shipped a boot-time warning. Now: `autoStart: true`, or `OS_MCP_STDIO_ENABLED=true`, with one clause noting the old var still starts it and warns. Also states that stdio defaults off, which the previous wording only implied. Paid in-file, not by re-wrap: the section's closing sentence restated the `instructions` row of the Skill Configuration table (that `@objectstack/mcp` projects `instructions` onto MCP prompts) and is deleted under AI-C-01's own rule. 5,481 -> 5,476 tokens. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1 --- skills/objectstack-ai/SKILL.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/skills/objectstack-ai/SKILL.md b/skills/objectstack-ai/SKILL.md index 2872ab3213..ecb3ce78f0 100644 --- a/skills/objectstack-ai/SKILL.md +++ b/skills/objectstack-ai/SKILL.md @@ -186,8 +186,9 @@ kernel.use(new MCPServerPlugin()); The HTTP surface is **default-on**, served per-request by the runtime dispatcher at **`POST /api/v1/mcp`** — nothing to start (`OS_MCP_SERVER_ENABLED=false` opts -out). A long-lived **stdio** transport is a separate opt-in -(`autoStart: true`, or `OS_MCP_SERVER_ENABLED=true`). +out). The long-lived **stdio** transport has its own switch and defaults off: +`autoStart: true`, or `OS_MCP_STDIO_ENABLED=true`. (`OS_MCP_SERVER_ENABLED=true` +still starts it — deprecated, and it warns at boot.) The tools a client then sees — from `registerObjectTools` / `registerActionTools`, each family gated by the caller's OAuth scopes, every call under the caller's @@ -202,9 +203,6 @@ permissions and row-level security: | `create_record`, `update_record`, `delete_record` | writes; `delete_record` is annotated destructive | | `list_actions`, `run_action` | your AI-exposed business Actions — the same `ai.exposed` opt-in as above | -Your skills' `instructions` reach this surface too: the same package projects -them onto the MCP `prompts` primitive. - --- ## Knowledge Sources (RAG) From c107eb262dfed48599d6e5ddcc8f52663d2b07ce Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 2 Sep 2026 14:14:24 +0000 Subject: [PATCH 5/5] chore(spec): regenerate the skill catalog and ratchet the role-word baseline after the merge Discharges the merge's deferred regeneration. Both catalog files are regenerated wholesale from the merged tree by `gen:skill-docs` (never hand-resolved), so every sibling flight's row stands as origin/main authored it and only this package's rows return. The role-word baseline is rewritten by the gate's own `--update` remedy: exactly one row moves, and downward (skills/objectstack-ai/SKILL.md 5 -> 1). No content change to skills/objectstack-ai/SKILL.md. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01489YWhZEoHT9oXshiyywQy --- content/docs/ai/skills-reference.mdx | 8 ++++---- scripts/role-word-baseline.json | 2 +- skills/README.md | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/content/docs/ai/skills-reference.mdx b/content/docs/ai/skills-reference.mdx index ca5368fc22..6c379974fd 100644 --- a/content/docs/ai/skills-reference.mdx +++ b/content/docs/ai/skills-reference.mdx @@ -48,7 +48,7 @@ ObjectStack ships **11 skills** — one per authoring domain, plus process skill | 3 | [Query](#query) | `query` | `skills/objectstack-query/` | Construct ObjectQL queries — filters, sorting, pagination, aggregation, relation expansion, and full-text search. | | 4 | [UI](#ui) | `ui` | `skills/objectstack-ui/` | Author ObjectStack UI metadata — Views (list/form/kanban/calendar/gantt), Apps (navigation), Pages (structured plus the HTML and React source-authoring tiers, ADR-0080/0081), Dashboards, Reports, Charts, Actions, and package Docs (`src/docs/*.md`). | | 5 | [Automation](#automation) | `automation` | `skills/objectstack-automation/` | Design ObjectStack automation — Flows (visual logic), Triggers, Approvals, state machines, and the `jobs` (`defineJob`) / `webhooks` (`defineWebhook`) stack collections. | -| 6 | [AI](#ai) | `ai` | `skills/objectstack-ai/` | Design ObjectStack AI skills, tools, knowledge sources, conversations, model registry entries, and MCP integrations. | +| 6 | [AI](#ai) | `ai` | `skills/objectstack-ai/` | Design ObjectStack AI skills, tools, knowledge sources, and the open-edition MCP server surface. | | 7 | [API](#api) | `api` | `skills/objectstack-api/` | Design the server-side API surface that an ObjectStack runtime exposes — REST endpoints, auth providers, realtime channels, error envelopes, batch contracts. | | 8 | [i18n](#i18n) | `i18n` | `skills/objectstack-i18n/` | Author ObjectStack translation bundles — object/field labels, view text, app navigation strings, automation messages — and configure locale fallback, coverage reporting, and the per-locale source layout. | | 9 | [Formula](#formula) | `expression` | `skills/objectstack-formula/` | Author CEL expressions used across ObjectStack — formula fields, field conditional rules (`visibleWhen`, `readonlyWhen`, `requiredWhen`), validation / sharing / visibility predicates, flow conditions, and dynamic seed values. This is the companion skill that objectstack-data, -ui, -api and -automation each tell you to load alongside them. | @@ -131,13 +131,13 @@ Do not use for data lifecycle hooks at the object layer (see objectstack-data) o **Domain** `ai` · **Path** `skills/objectstack-ai/` -Design ObjectStack AI skills, tools, knowledge sources, conversations, model registry entries, and MCP integrations. +Design ObjectStack AI skills, tools, knowledge sources, and the open-edition MCP server surface. -Use when the user is adding `*.skill.ts` / `*.tool.ts`, configuring an LLM provider, wiring agent tools, or indexing ObjectStack data as a knowledge source for RAG. Agents themselves are platform-internal (`ask` / `build`) — third parties extend them via skills and tools, not by authoring `*.agent.ts`. +Use when the user is adding `*.skill.ts` / `*.tool.ts`, configuring an LLM provider, wiring the tools a skill grants, or indexing ObjectStack data as a knowledge source for RAG. Agents themselves are platform-internal (`ask` / `build`) — third parties extend them via skills, not by authoring `*.agent.ts`. Do not use for general LLM prompting questions unrelated to ObjectStack metadata. -**Tags:** `agent`, `tool`, `skill`, `conversation`, `llm`, `embedding`, `mcp` +**Tags:** `agent`, `tool`, `skill`, `knowledge`, `llm`, `embedding`, `mcp` --- diff --git a/scripts/role-word-baseline.json b/scripts/role-word-baseline.json index e68a0e0573..1b1e040421 100644 --- a/scripts/role-word-baseline.json +++ b/scripts/role-word-baseline.json @@ -33,7 +33,7 @@ "content/docs/releases/v16.mdx": 5, "content/docs/releases/v17.mdx": 2, "content/docs/ui/forms.mdx": 3, - "skills/objectstack-ai/SKILL.md": 5, + "skills/objectstack-ai/SKILL.md": 1, "skills/objectstack-automation/SKILL.md": 1, "skills/objectstack-data/SKILL.md": 2, "skills/objectstack-data/rules/relationships.md": 1, diff --git a/skills/README.md b/skills/README.md index 8111a0462e..71f04a8461 100644 --- a/skills/README.md +++ b/skills/README.md @@ -34,7 +34,7 @@ apps too). | [Query](./objectstack-query/SKILL.md) | `query` | Construct ObjectQL queries — filters, sorting, pagination, aggregation, relation expansion, and full-text search. | | [UI](./objectstack-ui/SKILL.md) | `ui` | Author ObjectStack UI metadata — Views (list/form/kanban/calendar/gantt), Apps (navigation), Pages (structured plus the HTML and React source-authoring tiers, ADR-0080/0081), Dashboards, Reports, Charts, Actions, and package Docs (`src/docs/*.md`). | | [Automation](./objectstack-automation/SKILL.md) | `automation` | Design ObjectStack automation — Flows (visual logic), Triggers, Approvals, state machines, and the `jobs` (`defineJob`) / `webhooks` (`defineWebhook`) stack collections. | -| [AI](./objectstack-ai/SKILL.md) | `ai` | Design ObjectStack AI skills, tools, knowledge sources, conversations, model registry entries, and MCP integrations. | +| [AI](./objectstack-ai/SKILL.md) | `ai` | Design ObjectStack AI skills, tools, knowledge sources, and the open-edition MCP server surface. | | [API](./objectstack-api/SKILL.md) | `api` | Design the server-side API surface that an ObjectStack runtime exposes — REST endpoints, auth providers, realtime channels, error envelopes, batch contracts. | | [i18n](./objectstack-i18n/SKILL.md) | `i18n` | Author ObjectStack translation bundles — object/field labels, view text, app navigation strings, automation messages — and configure locale fallback, coverage reporting, and the per-locale source layout. | | [Formula](./objectstack-formula/SKILL.md) | `expression` | Author CEL expressions used across ObjectStack — formula fields, field conditional rules (`visibleWhen`, `readonlyWhen`, `requiredWhen`), validation / sharing / visibility predicates, flow conditions, and dynamic seed values. This is the companion skill that objectstack-data, -ui, -api and -automation each tell you to load alongside them. |