ObjectOS
Build

Agents

End-user AI assistants — Agent → Skill → Tool — wired from your data and actions.

Agents

Agents are the AI assistants your end users chat with — a help desk co-pilot, a sales BDR, an internal HR Q&A bot. They sit on top of the data and actions you've already defined; you don't write new code, you compose existing primitives into a persona.

Three-tier architecture, aligned with Salesforce Agentforce, Microsoft Copilot Studio, and ServiceNow Now Assist:

Agent  ──→  Skill  ──→  Tool
(persona)   (capability)  (callable function)
TierWhat it isExample
ToolOne callable function (action, query, knowledge search, MCP method)create_ticket, get_order_status, search_kb
SkillA named bundle of related tools with shared LLM instructionsticket_management = create + update + close + escalate
AgentA persona with a role, system prompt, attached skills, and knowledgetier1_support = empathetic, verifies identity, has ticket_management + kb_search

Define an Agent (one file)

// src/agents/tier1_support.agent.ts
import { defineAgent } from '@objectstack/spec/ai';

export const tier1Support = defineAgent({
  name: 'tier1_support',
  label: 'First Line Support',
  role: 'Help Desk Assistant',
  instructions: `
    You are a friendly first-line support agent.
    Always verify the user's identity before discussing account specifics.
    Escalate to tier 2 if the issue involves billing or security.
  `,
  skills: ['ticket_management', 'knowledge_search'],
  knowledge: {
    topics:  ['faq', 'policies'],
    indexes: ['support_docs'],
  },
  model: { provider: 'openai', model: 'gpt-4o', temperature: 0.3 },
  memory: { shortTerm: { maxMessages: 30 } },
});

Or in Console: Console → Agents → New Agent.

Or — and this is the point — say to the AI Builder:

"Create a tier-1 support agent that handles ticket management and searches the FAQ. It should verify identity before discussing account details."

Define a Skill

// src/skills/ticket_management.skill.ts
import { defineSkill } from '@objectstack/spec/ai';

export const ticketManagement = defineSkill({
  name: 'ticket_management',
  label: 'Ticket Management',
  instructions: `
    Always confirm the ticket subject and priority before creating one.
    Use 'urgent' priority sparingly — only for outages or security incidents.
  `,
  tools: [
    'create_ticket',
    'update_ticket',
    'close_ticket',
    'escalate_ticket',
    'action_*',         // wildcard: pick up any future actions on the active object
  ],
});

Skills are the right unit for reuse. One skill works across many agents.

Tools come from your declared metadata

Every *.action.ts you declare automatically materializes as an action_<name> tool — no separate wiring. So if you've already defined escalate_ticket as an Action on the support_ticket object, both the AI Builder and your Agents can call it. Permissions still apply: the agent calls the action as the user, so the user's permission set decides whether it succeeds.

You can also expose:

Tool typeSource
ActionAny *.action.ts in any installed package
FlowAny manual flow (type: 'manual')
QuerySaved ObjectQL queries (*.query.ts)
Knowledge searchAny knowledge index attached to the agent
MCP methodAnything exposed by an attached MCP server
Built-in metadata toolscreate_object, add_field, … — but only to admin agents

Ambient assistant pattern

If you want one chat box for the whole app (Claude Code / Agentforce style) instead of forcing the user to pick an agent, declare a defaultAgent on the App metadata and call the ambient chat endpoint with the app context:

POST /api/v1/ai/chat   { context: { appName: 'crm' }, ... }

When context.appName resolves to an app that declares a defaultAgent, the runtime auto-selects that agent — the user never picks from a list. Console's built-in AI panel uses this. The runtime resolves:

  1. The default agent for the active app (the app's defaultAgent), or the first agent the user has access to.
  2. The active skills — the agent's skills: list loaded from the Skill Registry, filtered by user permission set and the current object/record context.
  3. The knowledge attached to the agent.

You don't have to wire which agent is shown where. Declare an app, set its defaultAgent, and it appears.

Permissions

CapabilityPermission
Chat with an agentai:chat (and access to the agent's skills' tools)
Approve metadata changesai:approve
Define / edit agents and skillsai:author (typically Setup Administrator)
Read AI conversations (audit)ai:read

Conversations are scoped to the user — one user can't see another's chat history unless they have a delegated grant.

Memory and conversation state

The agent's memory block has two tiers:

FieldWhat it does
shortTerm.maxMessagesRecent messages kept in working memory (default 50)
shortTerm.maxTokensOptional token budget for the short-term context window
longTerm.enabledPersist memory across sessions (default false)
longTerm.storeBackend for persisted memory: vector (default), database, or redis
reflectionIntervalReflect every N interactions to refine behavior

Pruning of the live context window is governed by the conversation's token-budget strategy — sliding_window (default), fifo, importance, semantic, or summary.

Conversation rows live in ai_conversations. Tool-call results and pending actions cross-reference back to the originating conversation for audit.

Observability

Every agent run emits:

  • audit:ai:chat events (per turn)
  • audit:ai:tool events (per tool call, with inputs + outputs)
  • audit:ai:pending_action events (when a mutation queues)
  • token-count metrics (per model, per provider) into the audit log for chargeback

You can wire these into your usual observability stack — see Observability.

Multi-tenant note

Agents are per-Environment. Tenant A's tier1_support agent never sees tenant B's data, conversations, or knowledge — even if you ship the same agent definition in a marketplace package.

Where to go next

On this page