@synapsor/dsl
v1.10.0
Published
SQL-like Synapsor authoring frontend for canonical @synapsor/spec contracts.
Maintainers
Readme
@synapsor/dsl
@synapsor/dsl is the SQL-like authoring frontend for canonical Synapsor contracts.
The DSL is not the source of truth. It compiles to @synapsor/spec JSON, and
the generated JSON is validated by @synapsor/spec.
Part of the Synapsor OSS toolchain:
@synapsor/runner: local MCP runtime that serves compiled contracts.@synapsor/spec: canonical contract schemas, types, and validation.- Source and issues on GitHub.
Example
CREATE AGENT CONTEXT local_operator
BIND tenant_id FROM ENVIRONMENT SYNAPSOR_TENANT_ID REQUIRED
BIND principal FROM ENVIRONMENT SYNAPSOR_PRINCIPAL REQUIRED
TENANT BINDING tenant_id
PRINCIPAL BINDING principal
END
CREATE CAPABILITY billing.inspect_invoice
DESCRIPTION 'Inspect one invoice in the trusted tenant before proposing a waiver.'
RETURNS HINT 'Returns reviewed invoice fields plus evidence/query-audit handles.'
USING CONTEXT local_operator
SOURCE local_postgres
ON public.invoices
PRIMARY KEY id
TENANT KEY tenant_id
PRINCIPAL SCOPE KEY assigned_to
CONFLICT GUARD updated_at
LOOKUP invoice_id BY id
ARG invoice_id STRING REQUIRED MAX LENGTH 128 DESCRIPTION 'Invoice id such as INV-3001.'
ALLOW READ id, tenant_id, status, late_fee_cents, updated_at
REQUIRE EVIDENCE
MAX ROWS 1
ENDA longer worked contract lives in
examples/billing-late-fee.synapsor.sql,
and the runner README walks the full compile → validate → bundle → serve flow.
For same-tenant owner/assignee isolation, see
principal-row-scope.synapsor.sql.
The clause always narrows mandatory tenant scope with the required trusted
principal binding; the model cannot supply or override its value.
Use .synapsor.sql for authored DSL files so editors recognize the file as
SQL and provide generic SQL highlighting. Existing .synapsor files remain
supported for compatibility. The filename suffix does not change DSL semantics
or generated canonical JSON; this repository does not provide Synapsor-specific
semantic editor highlighting.
CLI
synapsor-dsl validate ./contract.synapsor.sql [--strict] [--target canonical|runner]
synapsor-dsl compile ./contract.synapsor.sql --out ./synapsor.contract.json [--strict] [--target canonical|runner]Runner also exposes:
synapsor-runner dsl validate ./contract.synapsor.sql [--strict]
synapsor-runner dsl compile ./contract.synapsor.sql --out ./synapsor.contract.json [--strict]--strict treats safety warnings as errors. Use it in CI for reviewed proposal
contracts. --target runner also rejects canonical constructs that Runner does
not execute, including FROM SESSION. The synapsor-runner dsl commands select
the Runner target automatically.
Continue from authored DSL to a local/Cloud-compatible contract with:
synapsor-dsl compile ./contract.synapsor.sql --out ./synapsor.contract.json --strict
synapsor-spec validate ./synapsor.contract.json
synapsor-runner contract bundle ./synapsor.contract.json --out ./synapsor-runner-bundle
synapsor-runner cloud push ./synapsor.contract.json --dry-runThe generated synapsor.contract.json is the portable source of truth. Runner
configuration supplies local database env names and transport settings; Cloud
stores immutable normalized versions and never needs database credentials in
the contract.
Programmatic API
import { compileAgentDsl, parseAgentDsl, validateAgentDsl, formatAgentDsl, AgentDslError } from "@synapsor/dsl";
import { assertValidContract, normalizeContract } from "@synapsor/spec";
const source = `CREATE AGENT CONTEXT ...`;
const result = validateAgentDsl(source); // { ok, errors, warnings } with line/column entries
const ast = parseAgentDsl(source); // AST with line/column spans
try {
const contract = compileAgentDsl(source); // @synapsor/spec contract JSON
assertValidContract(normalizeContract(contract));
} catch (error) {
if (error instanceof AgentDslError) {
console.error(`${error.message} at ${error.line}:${error.column}`);
}
}formatAgentDsl(source) returns a canonically formatted copy of the DSL text.
Supported Constructs
CREATE AGENT CONTEXTCREATE CAPABILITYCREATE AGENT WORKFLOWBIND ... FROM SESSION|ENVIRONMENT|CLOUD_SESSION|STATIC_DEV|HTTP_CLAIM|REVIEWED_ORGANIZATIONin the language-neutral grammar; Runner rejectsSESSIONrather than treating it as environment inputUSING CONTEXTDESCRIPTIONRETURNS HINTON schema.tablePRIMARY KEYTENANT KEYPROTECTED SINGLE ORGANIZATION 'organization-id' ACKNOWLEDGEDas the tenant-key alternative emitted only from a digest-bound reviewed single-organization boundaryPRINCIPAL SCOPE KEYfor a tenant-additive trusted owner/assignee row lock- exact
CONFLICT GUARD column - explicit legacy
CONFLICT GUARD WEAK ROW HASH ACKNOWLEDGEDfor ordinary single-row source-DB UPDATE only; omission fails and the weak form warns that projection hashing can miss changes outside the captured fields ARGARG ... DESCRIPTIONARG ... MIN ... MAX ...forNUMBERARG ... MAX LENGTH ...forSTRING/TEXTARG ... ENUM(...)for reviewed string, number, or boolean choicesLOOKUPALLOW READKEEP OUTREQUIRE EVIDENCEAGGREGATE READ COUNT ROWS|COUNT NON NULL column|SUM column|AVG columnMIN GROUP SIZE nfor a reviewed aggregate threshold from 1 upward;1disables small-group suppression and permits groups of one, so use it only after an explicit human disclosure decisionPROTECTED READ ROWS|AGGREGATEfor a named capability produced from a human-reviewed local Explore planBOUNDARY DIGESTandGENERATION LOCKfor exact reviewed authority- legacy one-hop
PROTECTED RELATIONSHIP name ON ... REFERENCES ... - additive
PROTECTED RELATIONSHIP name LINK 1|2|3 ON ... REFERENCES ... UNMATCHED EXCLUDE|KEEP NULLfor up to three reviewed paths containing at most two catalog-proven many-to-one links each - fixed
PROTECTED FILTER, reviewedMEASURE,GROUP DIMENSION,TIME DIMENSION, fixedPROTECTED TIME WINDOW field FROM FIXED '<UTC>' TO FIXED '<UTC>',COMPARE RANGE, aggregate ordering/top-N, row ordering, andPROTECTED LIMITS; protected aggregates may use a separately reviewedRANKED GROUPScandidate ceiling and, with exactly two comparison ranges, order by signedABSOLUTE CHANGEorPERCENTAGE CHANGE
PROTECTED TIME WINDOW is deliberately fixed. Protect resolves a relative
Explore request first and emits its canonical half-open UTC range as literals;
the timestamps cannot be ARG values. A moving protected capability such as
"previous month whenever invoked" is not part of this grammar.
PROPOSE ACTIONPROPOSE ACTION name UPDATE|INSERT|DELETE(operation defaults toUPDATE)PROPOSE ACTION name UPDATE|INSERT|DELETE SETfor bounded-set authoringREVERSIBLEfor opt-in, direct-SQL reviewed compensation; it creates no model-facing revert or automatic rollback path- fixed literal
SELECT WHERE term [AND term]for set UPDATE/DELETE MAX ROWS nplusMAX TOTAL column BEFORE|AFTER|ABSOLUTE DELTA maximumARG name ROWS MAX nand typedITEM FIELDdeclarations for batch INSERTBATCH ITEMS FROM ARG name,ITEM fieldpatches, andITEM fielddedup keysDEDUP KEY column = TRUSTED TENANT|PROPOSAL ID|FIXED valueforINSERTADVANCE VERSION column USING INTEGER INCREMENT|DATABASE GENERATEDfor Runner-ledgerUPDATEALLOW WRITEPATCHBOUNDTRANSITIONAPPROVAL ROLEREQUIRE n APPROVALSfor a 1..10 distinct-reviewer quorumAUTO APPROVE WHEN field <= integerLIMIT count PER DAYafter auto-approvalLIMIT TOTAL integer PER DAYafter auto-approval- optional
PER OBJECT DAYscope for count/total limits WRITEBACK DIRECT SQL|APP HANDLER|CLOUD WORKER|NONE- workflow
ALLOW CAPABILITY
Bounded sets have a hard 100-row ceiling, freeze exact members before review, and require human/operator approval. The compiler rejects policy auto-approval, missing fixed selection, missing aggregate bounds, and model-supplied predicates. See the bounded-set guide.
Multi-term selections are fixed equality clauses, not free-form SQL:
SELECT WHERE risk_level = 'high' AND case_status = 'active'AND may join up to eight literal equality terms. OR, parentheses,
inequalities, ranges, and model-authored predicates are rejected explicitly.
Quoted literals may contain the word AND without becoming another term. See
the packaged
bounded-set-multi-term.synapsor.sql
example.
Reviewed relationship paths are fixed authority, not a model-facing join
grammar. name is a user-reviewed path identifier; LINK, ON,
REFERENCES, PRIMARY KEY, TENANT KEY, and UNMATCHED are DSL keywords.
Link numbers must be contiguous, and every compiled link has
cardinality: "many_to_one" plus max_fan_out: 1. The model may reference an
activated path by name but cannot supply table names, keys, join types, or add
authority. See the reviewed relationship
guide.
Reviewed reversible writes require direct SQL, human/operator approval, and
operation-specific exact guards. After apply, synapsor-runner revert creates
a separate proposal. See the reviewed compensation
guide.
Unsupported Cloud-generated clauses such as ROOT EXTERNAL,
JOIN EXTERNAL, RETURN ANSWER WITH CITATIONS, AUTO BRANCH, or AUTO MERGE
fail explicitly instead of being ignored.
The complete clause grammar and constraints, including primary-key-only
LOOKUP, fixed-string PATCH, workflow declarations, and writeback forms, are
in the Synapsor DSL Reference.
