Getting Started
AgentScript is an open agent specification language. Define agents as single .agent files with a declarative, block-based syntax for state, execution flow, instructions, and deterministic control hooks.
Overview
An AgentScript file describes what an agent is — its identity, state variables, available actions, and reasoning instructions. The runtime handles how it executes, meaning the same script can run on increasingly capable runtimes without any changes.
The language was developed for Salesforce Agentforce but is designed to apply to agents in general. The toolchain — parser, linter, compiler, LSP, and editor integrations — is fully open source under Apache 2.0.
What's in the repo
- Parser — two backends: a hand-written TypeScript parser and a Tree-sitter C/WASM parser
- Language service — scope resolution, linting (18+ passes), Language Service API
- Compiler — transforms AST → Salesforce runtime specification with source maps
- LSP server — Node.js and browser workers for IDE integration
- VS Code extension — diagnostics, completions, hover, go-to-definition, rename
- Monaco integration — for browser-based editors
- UI Playground — React + Vite playground with graph view and CST explorer
The runtime is Salesforce-internal and not open sourced. You can parse, lint, compile, and build tooling around AgentScript, but running agents requires Salesforce's runtime environment.
Installation
Prerequisites
The recommended setup uses mise, which installs the exact toolchain versions pinned in mise.toml:
mise install
Or install manually:
- Node.js 22 (minimum
>=18) - pnpm 10 — run
corepack enable pnpmonce - tree-sitter CLI 0.25.x — required to build the
parser-tree-sitterpackage
macOS: brew install tree-sitter installs the C library only, not the CLI. Use mise install (recommended) or brew install tree-sitter-cli.
Clone and build
git clone https://github.com/salesforce/agentscript.git
cd agentscript
pnpm install
pnpm build
Quick Start
Run the UI playground
pnpm ui:dev
Opens the AgentScript playground at http://localhost:27002. It includes a Monaco editor, graph view, CST/AST explorer, and preloaded examples.
Install the SDK
Packages are published to npm under the @sf-agentscript/* scope:
npm install @sf-agentscript/agentforce
# or
pnpm add @sf-agentscript/agentforce
import { parse } from '@sf-agentscript/agentforce';
const doc = parse(`
system:
instructions: "You are a helpful agent."
start_agent main:
reasoning:
instructions: ->
| Help the user with any questions.
`);
console.log(doc.hasErrors); // false
console.log(doc.diagnostics); // []
console.log(doc.emit()); // formatted source
Your First Agent
Create a file named my_agent.agent. Every agent needs at least a start_agent block with a reasoning section:
system:
instructions: "You are a helpful customer support agent."
messages:
welcome: "Hello! How can I help you today?"
error: "Sorry, something went wrong."
config:
agent_name: "SupportBot"
variables:
customer_name: mutable string = ""
description: "The customer's name"
issue_resolved: mutable boolean = False
description: "Whether the issue has been resolved"
start_agent support:
description: "Handles customer support inquiries"
reasoning:
instructions: ->
| Greet the customer and ask how you can help.
if @variables.customer_name != "":
| Address them by name: {!@variables.customer_name}
| Always be concise and professional.
actions:
capture_name: @utils.setVariables
description: "Capture customer name from conversation"
with customer_name=...
escalate: @utils.escalate
description: "Hand off to a human agent"
Key elements explained
system— global instructions inherited by all subagents, plus standard messages.config— agent metadata like name and description.variables— typed state that persists across turns.mutablevariables can be written;linkedvariables are read-only runtime values.start_agent— the entry point. Exactly one per script. Functions identically tosubagent.reasoning.instructions— a procedure that builds the LLM's prompt. Re-evaluated each turn, so it can reflect current variable state.reasoning.actions— action bindings available to the LLM during reasoning.@utils.setVariables— built-in utility for slot-filling: the LLM extracts values from conversation and sets them.
Using the SDK
The @sf-agentscript/agentforce package is the batteries-included SDK that combines the parser, language service, and compiler:
import { parse, compileSource } from '@sf-agentscript/agentforce';
// Parse and analyze
const doc = parse(source);
if (doc.hasErrors) {
for (const diag of doc.diagnostics) {
console.error(`${diag.severity}: ${diag.message} at line ${diag.range.start.line}`);
}
}
// Get formatted source
const formatted = doc.emit();
// Compile to runtime spec (Agentforce dialect)
const compiled = compileSource(source);
console.log(compiled.spec); // JSON runtime specification
console.log(compiled.maps); // source maps
Document mutation
The Document class supports mutation with undo/redo:
import { parse } from '@sf-agentscript/agentforce';
const doc = parse(source);
// Mutate
doc.set(['config', 'agent_name'], 'NewName');
// Undo / redo
doc.undo();
doc.redo();
// Get updated source
console.log(doc.emit());
UI Playground
The browser playground at http://localhost:27002 (run with pnpm ui:dev) includes:
- Monaco editor — full LSP experience: diagnostics, completions, hover, go-to-definition
- Graph view — visualize the agent's topic/subagent flow as a node graph
- CST/AST explorer — inspect the concrete and abstract syntax trees in real time
- Compiled output — see the JSON runtime specification the compiler produces
- Multiple dialects — switch between
agentscript,agentforce, andagentfabric - Preloaded examples — hello world, order tracking, lead qualification, weather assistant
Blocks
Everything in AgentScript is a block. Blocks compose to form an agent. There are two categories:
- Configuration blocks (
config,system,language) — set up the agent's identity and behavior. - Execution blocks (
start_agent,subagent,topic) — define how the agent runs. These have implied execution — the runtime's ReAct loop is handled behind the scenes.
Blocks are indentation-sensitive, like Python or YAML. A block is a key followed by :, with its children indented below:
config:
agent_name: "Support Bot"
description: "An AI assistant for customer support"
Some blocks accept an instance name after the block type:
subagent billing:
description: "Handles billing inquiries"
start_agent router:
description: "Routes users to the right subagent"
Determinism vs. Autonomy
AgentScript does not prescribe how much control you take. The language gives you the tools — how you use them is up to you.
| More deterministic | More autonomous |
|---|---|
Use before_reasoning to gate transitions and guard against bad state |
Write only reasoning.instructions and let the LLM decide everything |
Use if/else in instructions to focus the LLM's context |
Provide broad instructions and a rich action set |
Use set in after_reasoning to explicitly drive state |
Use @utils.setVariables and let the LLM slot-fill |
Use run in before_reasoning to fetch data before the LLM reasons |
Expose actions in reasoning and let the LLM call them |
Most real agents sit somewhere in between. The key principle: execution is decoupled from specification. More procedures = more determinism. Fewer procedures = more autonomy.
Dialects
A dialect is a collection of block types, their schemas, and lint rules. Dialects extend the base AgentScript schema with platform-specific blocks and constraints.
| Dialect | Description |
|---|---|
agentscript |
Base dialect. Core blocks: system, config, variables, actions, start_agent, subagent, reasoning, etc. |
agentforce |
Salesforce Agentforce. Adds modality, security, model_config, related_agent, language. Has its own compiler. |
agentfabric |
AgentFabric platform. Alternative schema, lint rules, and compiler output. |
To select a dialect in a script, add a comment in the first 10 lines:
# @dialect: agentforce=2.2
If no dialect comment is present, the LSP/parser uses defaultDialect or the first registered dialect.
Custom dialects can be built by creating a DialectConfig object with a schema and lint passes, then registering it in the LSP server and UI. See Packages → Dialect Layer for details.
Next steps
- Language Guide — complete reference for all syntax and blocks
- Examples — real-world agents with annotated code
- Packages — SDK and toolchain package reference