Packages
The AgentScript monorepo is organized as a set of layered packages. Each layer builds on the one below it. Understanding the stack helps you pick the right entry point for your use case.
Architecture
┌──────────────────────────────────────────────────────────┐
│ UI Playground (React + Vite) │
│ Monaco editor · graph view · CST explorer · examples │
├──────────────────────────────────────────────────────────┤
│ Editor Integrations │
│ @sf-agentscript/vscode · @sf-agentscript/monaco │
├──────────────────────────────────────────────────────────┤
│ LSP Layer │
│ lsp (core) · lsp-server (Node.js) · lsp-browser (Worker)│
├──────────────────────────────────────────────────────────┤
│ SDK (@sf-agentscript/agentforce) │
│ parse · Document · compileSource · parseComponent │
├──────────────────────────────────────────────────────────┤
│ Compiler (@sf-agentscript/compiler) │
│ AST → Salesforce runtime spec + source maps │
├──────────────────────────────────────────────────────────┤
│ Dialect Layer │
│ agentscript (base) · agentforce · agentfabric │
├──────────────────────────────────────────────────────────┤
│ Core Language (@sf-agentscript/language) │
│ AST types · scope resolution · linting framework │
├──────────────────────────────────────────────────────────┤
│ Parsers │
│ parser-javascript (TypeScript) · parser-tree-sitter (C) │
├──────────────────────────────────────────────────────────┤
│ Foundation (@sf-agentscript/types) │
│ SyntaxNode · Diagnostic · Range — zero dependencies │
└──────────────────────────────────────────────────────────┘
Packages are published to npm under the @sf-agentscript/* scope. The @agentscript/* names used internally in the monorepo are workspace aliases — they resolve to the same packages.
Foundation Layer
Shared primitive types used across every other package. Zero dependencies — safe to import anywhere.
SyntaxNode, Diagnostic, Range, Position, and other primitives shared across all packages. No runtime dependencies.Key types
// Position in source
interface Position { line: number; character: number }
interface Range { start: Position; end: Position }
// Parse/lint diagnostic
interface Diagnostic {
message: string
severity: 'error' | 'warning' | 'info' | 'hint'
range: Range
code?: string
}
// Raw syntax node from parser
interface SyntaxNode {
type: string
text: string
range: Range
children: SyntaxNode[]
}
Core Language
parser-javascript by default. Swap to parser-tree-sitter via conditional exports. Consumer code imports from this package — not the parser implementations directly.Parser selection
The JavaScript parser is the default and recommended choice for most use cases. The Tree-sitter parser is the authoritative grammar and is used for syntax highlighting queries:
// Default: resolves to parser-javascript
import { parse } from '@sf-agentscript/parser'
// Explicit tree-sitter (via package.json exports conditions)
// Set "agentscript:parser" = "tree-sitter" in your build config
Dialect Layer
Dialects extend the base schema with platform-specific blocks, fields, and lint rules. Each dialect exports a DialectConfig object that is injected into the parser, language service, and LSP.
system, config, variables, actions, start_agent, subagent, reasoning, before_reasoning, after_reasoning, connected_subagent. Base lint rules.modality, security, model_config, related_agent, language. Adds topic as an alias for subagent. Includes its own compiler.Dialect schema structure
Schema definitions use factory functions from @sf-agentscript/language:
import { Block, TypedMap, NamedBlock, CollectionBlock } from '@sf-agentscript/language'
// Fixed-field block: only declared keys are valid
const ConfigBlock = Block('ConfigBlock', {
agent_name: StringValue,
description: StringValue,
})
// Variadic map: any user-defined keys, all same type
const VariablesBlock = TypedMap('VariablesBlock', VariablePropertiesBlock)
// Named block: accepts an instance name (subagent foo:)
const SubagentBlock = NamedBlock('SubagentBlock', {
description: StringValue,
reasoning: ReasoningBlock,
before_reasoning: ProcedureBlock,
after_reasoning: ProcedureBlock,
})
// Collection: map of named blocks
const ActionsBlock = CollectionBlock(ActionBlock)
Compiler
import { compile } from '@sf-agentscript/compiler'
const result = compile(ast, { dialect: agentforceDialect })
console.log(result.spec) // JSON runtime specification
console.log(result.maps) // source maps (position → spec location)
SDK — @sf-agentscript/agentforce
The batteries-included SDK. Combines parser, language service, compiler, and dialects into a single import. The recommended entry point for most use cases. Works in Node.js and browsers.
npm install @sf-agentscript/agentforce
parse(source)
Parses and analyzes an AgentScript source string. Returns a Document containing diagnostics, the AST, and mutation methods.
import { parse } from '@sf-agentscript/agentforce'
const doc = parse(`
system:
instructions: "You are a helpful agent."
start_agent main:
reasoning:
instructions: ->
| Help the user.
`)
doc.hasErrors // boolean — true if any error-level diagnostics
doc.diagnostics // Diagnostic[] — all parse, lint, and schema errors
doc.emit() // string — formatted source (round-trip safe)
Document
The Document class represents a parsed, analyzed agent script with mutation support and undo/redo history:
import { parse } from '@sf-agentscript/agentforce'
const doc = parse(source)
// Read
doc.hasErrors // boolean
doc.diagnostics // Diagnostic[]
doc.emit() // formatted source string
// Mutate (creates new history entry)
doc.set(['config', 'agent_name'], 'UpdatedName')
doc.set(['variables', 'order_id', 'default'], '"ORD-999"')
// History
doc.undo() // revert last mutation
doc.redo() // reapply
doc.canUndo // boolean
doc.canRedo // boolean
// After mutation
doc.emit() // reflects the change
compileSource(source)
Parses and compiles a source string to the Salesforce runtime specification in one step:
import { compileSource } from '@sf-agentscript/agentforce'
const result = compileSource(source)
result.spec // object — JSON runtime specification
result.maps // source maps
result.diagnostics // compile-time diagnostics (includes parse + lint)
parseComponent() / emitComponent()
Work with individual agent components (subagent blocks, action definitions, etc.) in isolation:
import { parseComponent, emitComponent } from '@sf-agentscript/agentforce'
// Parse a single component block
const component = parseComponent('subagent', `
billing:
description: "Handles billing"
reasoning:
instructions: "Help with billing."
`)
// Emit back to source
const source = emitComponent(component)
LSP Layer
The Language Server Protocol layer provides IDE features for any LSP-compatible editor. All providers are dialect-agnostic — platform logic is injected via config.
@sf-agentscript/lsp. Ships the agentscript-lsp CLI binary used by the VS Code extension and other editors via their LSP client.LSP capabilities
| Capability | Description |
|---|---|
| Diagnostics | Parse errors, schema validation, lint warnings, compile errors — all in one pass |
| Hover | Type info and documentation for variables, actions, blocks, and built-ins |
| Completions | Field names, namespace members (@variables.*, @actions.*), block keywords |
| Go to Definition | Jump to variable or action declaration from any reference |
| Find References | Locate all uses of a variable, action, or subagent |
| Rename | Refactor identifiers across the entire document |
| Document Symbols | Outline view showing all blocks and named declarations |
| Code Actions | Quick-fix suggestions for lint warnings |
| Semantic Tokens | Tree-sitter query-based syntax highlighting |
Dialect detection
The LSP selects the dialect from a comment in the first 10 lines of the file:
# @dialect: agentforce=2.2
Falls back to defaultDialect from the server config, or the first registered dialect.
Editor Integrations
.agent files. Launches agentscript-lsp as a language server and activates all LSP features. Includes syntax highlighting via Tree-sitter highlight queries.VS Code setup
The extension is installed from the VS Code marketplace (or .vsix locally). It activates automatically for .agent files and requires no additional configuration:
# Build the extension locally
pnpm build
cd packages/vscode
pnpm package # produces agentscript-*.vsix
# Install locally
code --install-extension agentscript-*.vsix
Applications
pnpm ui:dev at localhost:27002.pnpm docs:dev at localhost:27000. Generate API docs with pnpm docs:typedoc.Building a Custom Dialect
Dialects are plain TypeScript packages that export a DialectConfig. There is no central registry — consumers import dialects directly.
-
Create the dialect package
mkdir -p dialect/mydialect/src cd dialect/mydialect # package.json with @sf-agentscript/language as dependency -
Define your schema
// dialect/mydialect/src/schema.ts import { Block, StringValue, BooleanValue } from '@sf-agentscript/language' export const MyCustomBlock = Block('MyCustomBlock', { my_field: StringValue, my_flag: BooleanValue, }) -
Add lint rules (optional)
// dialect/mydialect/src/lint/passes/my-rule.ts import type { LintPass } from '@sf-agentscript/language' export const myRule: LintPass = { id: 'my-dialect/my-rule', run(doc, report) { // walk doc.ast, call report() for issues } } -
Export the DialectConfig
// dialect/mydialect/src/index.ts import type { DialectConfig } from '@sf-agentscript/language' import { MyCustomBlock } from './schema.js' import { myRule } from './lint/passes/my-rule.js' export const myDialect: DialectConfig = { name: 'mydialect', version: '1.0', blocks: { my_custom: MyCustomBlock }, lintPasses: [myRule], } -
Register in consumers
Import and pass your dialect to the LSP server config, the SDK, or the UI playground's dialect registry. No central registration step needed.
import { parse } from '@sf-agentscript/agentforce' import { myDialect } from './dialect/mydialect/src/index.js' const doc = parse(source, { dialect: myDialect })
Build & Development
| Command | Description |
|---|---|
pnpm build | Build all packages via Turbo (parallel, incremental) |
pnpm dev | Watch mode for all packages |
pnpm test | Run all tests (Vitest) |
pnpm lint | Lint all packages (ESLint) |
pnpm typecheck | Type-check all packages |
pnpm format | Format with Prettier |
pnpm ui:dev | Run UI playground at localhost:27002 |
pnpm docs:dev | Run docs site at localhost:27000 |
pnpm docs:typedoc | Generate TypeDoc API reference |
Toolchain
| Tool | Purpose |
|---|---|
| pnpm workspaces | Monorepo package management |
| Turbo | Build orchestration — parallel builds, caching |
| TypeScript | All packages are fully typed |
| Vitest | Unit testing |
| ESLint + Prettier | Linting and formatting |
| mise | Toolchain version management (Node.js, tree-sitter CLI) |
Repository layout
agentscript/
├── packages/ # Core toolchain packages
│ ├── types/
│ ├── parser-javascript/
│ ├── parser-tree-sitter/
│ ├── parser/
│ ├── language/
│ ├── compiler/
│ ├── agentforce/ # SDK
│ ├── lsp/
│ ├── lsp-server/
│ ├── lsp-browser/
│ ├── vscode/
│ └── monaco/
├── dialect/ # Dialect implementations
│ ├── agentscript/
│ ├── agentforce/
│ └── agentfabric/
├── apps/
│ ├── ui/ # React playground
│ └── docs/ # Docusaurus site
├── SPEC.md # Formal language specification
└── CONTRIBUTING.md