@monkagoras/dawl
v0.2.1
Published
Typed deterministic agent language with portable agent definitions, guard policies, replay, and adapter linking
Downloads
283
Maintainers
Readme
DAWL
DAWL is a deterministic language for defining agents, typed capabilities, guard policies, and auditable multi-agent workflows.
The workflow owns control flow. Agents may reason, call allowlisted tools, and return typed data, but they do not silently choose the next workflow node. DAWL compiles the source before execution, links agent contracts to adapters, validates structured values, enforces policy boundaries, and emits a replayable event history.
What DAWL 0.2 provides
- First-class
agent,tool,activity,scope,guard,type,flow, andfndeclarations. - Named imports, exports, re-exports, relative modules, and manifest-resolved DAWL packages.
- Structural types compiled to JSON Schema 2020-12 compatible contracts.
- Typed agent input and output validation in the DAWL runtime.
- Explicit tool capabilities with effects and idempotency metadata.
- Input, output, and tool-call guard policies with
allow,ask, anddenydecisions. - Explicit agent-as-tool delegation with cycle detection.
- Bounded retries with explicit loop-carried state.
- Deterministic activity and agent replay identities.
- Runtime enforcement for wall-clock time, tool-call limits, transport retries, tool retries, and output repair retries.
dawl.json,dawl.lock, and.dawl/catalog.jsonwith separate intent, resolution, and environment responsibilities.- Canonical Agent IR, public descriptors, A2A Agent Card export, and MCP tool normalization.
- Pre-execution graph compilation and live terminal diagrams.
- Zero runtime dependencies.
Install
DAWL requires Node.js 22 or newer.
npm install @monkagoras/dawlFor local development:
npm install
npm run verifyCore syntax
use "./contracts.dawl" {
Task,
Finding,
ChangeSet,
Review,
RepoRead,
RepoEdit
}
guard WorkspacePolicy on tool.call {
deny when tool.effect == open-world
ask when tool.effect == destructive
and tool.path outside workspace
allow otherwise
}
export agent Developer(
task: Task,
findings: [Finding] = []
) -> ChangeSet {
describe "Implements one bounded repository task"
instruct """
Implement the supplied task.
Address every review finding.
Return only the declared ChangeSet structure.
"""
model coding {
require [tool-use, structured-output]
prefer [reasoning, long-context]
}
tools [RepoRead, RepoEdit]
guards { tool WorkspacePolicy }
session fresh
limits {
tool-calls 40
time "10m"
turns 24
tokens 80000
concurrency 1
}
retry {
transport 2
tool 1
output 2
}
observe {
trace standard
content redact
}
}
export agent Reviewer(change: ChangeSet) -> Review {
describe "Performs a read-only review"
instruct "Review the change and return pass plus concrete findings."
model review { require [tool-use, structured-output] }
tools [RepoRead]
guards { tool WorkspacePolicy }
limits { tool-calls 20 time "5m" }
retry { transport 2 output 2 }
}
export flow review(
task: Task,
M: int = 3
) -> "approved" | "failedReview" {
work:
return approve(task, M)
}
fn approve(
task: Task,
M: int
) -> "approved" | "failedReview" {
repeat M fresh with findings = [] {
review = Developer(task, findings) -> Reviewer
review.pass yes -> return "approved"
no -> retry with review.findings
}
return "failedReview"
}The important retry rule is explicit: only values declared after with cross fresh attempt boundaries. retry with review.findings supplies the next value. No hidden caller state is inherited by a function.
Types
DAWL supports a deliberately small JSON-compatible type system:
export type Finding {
severity: "low" | "medium" | "high"
message: string
file: string?
line: int?
}
export type Result = "approved" | "failed"
export type Findings = [Finding]Built-in types include:
bool
int
number
string
text
bytes
json
null
[T]
T?
record types
literal unionsA non-text agent output is validated by DAWL after the adapter returns it. Invalid output may be repaired only within the declared retry output budget. There is no silent fallback from typed output to raw text.
Agents
An agent declaration owns the portable semantic contract:
- name and description
- typed input and output
- instructions and instruction digest
- model profile plus required and preferred capabilities
- exact logical tool allowlist
- delegated agent allowlist
- session mode
- guard bindings
- limits and retry budgets
- observability metadata
Provider IDs, credentials, host paths, and concrete tool implementations do not belong in .dawl source. They are deployment bindings in dawl.json and adapters.
Defaulted agent parameters are materialized before adapter invocation and are included as JSON Schema default annotations in Agent IR. Defaults must be JSON constants.
Agent delegation
A parent may expose another agent as a typed tool:
agent Researcher(topic: string) -> Research {
instruct "Research the topic."
model research { require [structured-output] }
}
agent Manager(task: string) -> Plan {
instruct "Use the Researcher when evidence is needed."
model planning { require [structured-output] }
delegate [Researcher]
}The delegated agent keeps its own model profile, tools, guards, limits, and output validation. DAWL rejects calls to undeclared delegates and rejects circular delegation graphs.
This is not a handoff. The parent retains control and receives a typed result.
Capabilities
DAWL keeps four external concepts distinct.
Tool
A model-selectable operation inside an agent:
export tool RepoRead(path: string) -> text {
effects [workspace.read]
idempotency idempotent
}Activity
A workflow-selected external operation:
export activity PublishRelease(release: json) -> json {
effects [open-world, destructive]
}Scope
A resource or lifecycle boundary around nested workflow execution:
export scope Worktree(issue: string) -> json provides jsonAgent
A model-driven loop with typed input, typed output, capabilities, policies, and budgets.
Unresolved calls are compile errors. Capitalization is style only and does not decide whether a symbol is an agent.
Guard policies
Policies are deterministic declarations evaluated by DAWL, not prompt hints.
guard WorkspacePolicy on tool.call {
deny when tool.effect == open-world
ask when tool.effect == destructive
and tool.path outside workspace
allow otherwise
}Supported lifecycle events are:
agent.input
agent.output
tool.callExample lifecycle guards:
guard InputPolicy on agent.input {
deny when input.task == "blocked"
allow otherwise
}
guard OutputPolicy on agent.output {
ask when output.status == "needs-approval"
deny when output.status == "unsafe"
allow otherwise
}Bind them in an agent:
guards {
input InputPolicy
tool WorkspacePolicy
output OutputPolicy
}Multiple guards compose conservatively. deny wins over ask, and ask wins over allow. A guard bound to the wrong lifecycle event is rejected during project checking.
MCP effect annotations are treated as untrusted hints by default. They become enforceable effects only when the caller explicitly marks the MCP source as trusted.
Modifiers
fresh
repeat M fresh creates attempt-local state. Only declared loop state crosses attempts.
once
once gives a call an at-most-once identity at its source call site across repeat attempts. A successful result is reused within the run and remains replayable from recorded history.
summary = Summarizer(results) oncereadonly
For declared activities and agents, readonly rejects write, destructive, or open-world effects before execution.
summary = Summarizer(results) readonlyLegacy unresolved activities still receive the modifier for adapter compatibility, but DAWL cannot prove their effects. Security-sensitive projects should declare every activity and tool.
Runtime budgets
DAWL core enforces the limits it can independently observe:
limits {
tool-calls 40
time "10m"
}
retry {
transport 2
tool 1
output 2
}timeapplies to the complete agent invocation and passes an abort signal to the adapter.tool-callscounts every actual tool attempt, including retries.transportretries adapter invocation failures.toolretries tool implementation or tool output validation failures.outputretries invalid agent structured output.
turns, tokens, and concurrency remain part of Agent IR and must be enforced or reported by an adapter because the DAWL core cannot reliably observe provider-internal accounting. Silent adapter noncompliance is not acceptable for production adapters.
Project files
dawl.json
Human-authored intent and version ranges:
{
"$schema": "https://dawl.dev/schemas/project-v1.json",
"language": "^0.2",
"entries": ["flows/review.dawl"],
"packages": {
"@dawl/repository": {
"package": "@dawl/repository",
"entry": "index.dawl",
"version": "^1.0.0"
}
},
"adapters": {
"pi": {
"package": "@dawl/pi-adapter",
"version": "^1.0.0",
"protocol": "^1"
}
},
"profiles": {
"coding": {
"adapter": "pi",
"model": "provider/coding-model"
},
"review": {
"adapter": "pi",
"model": "provider/review-model"
}
},
"bindings": {
"RepoRead": "pi:read",
"RepoEdit": "pi:edit"
},
"policy": {
"requireLock": true,
"denyUnresolvedTools": true
}
}A local DAWL package may use path instead of package:
{
"packages": {
"@company/contracts": {
"path": "./packages/contracts",
"entry": "index.dawl",
"version": "1.0.0"
}
}
}dawl.lock
Machine-authored approved resolution:
- manifest digest
- catalog digest
- exact adapter protocol and version data
- adapter and package integrity digests
- model profiles
- tool bindings
- project policy
Commit this file.
.dawl/catalog.json
Machine-local discovery data:
- installed adapters
- adapter capabilities
- available models
- concrete tools
- DAWL packages
- schema and file integrity data
This file describes the current environment and is normally ignored by Git.
CLI
dawl sync
dawl install
dawl update
dawl check flows/review.dawl
dawl check flows/review.dawl --locked
dawl build flows/review.dawl
dawl inspect agents flows/review.dawl
dawl run flows/review.dawl --flow review --input '{"task":{"id":"42","description":"Fix it"}}'
dawl run flows/review.dawl --approve
dawl diagram flows/review.dawl --flow review --plainCommand responsibilities:
syncdiscovers adapters, tools, models, and DAWL packages into the catalog. It does not alter the lock.installinstalls missing adapters and packages, refreshes the catalog, and writes the lock.updateintentionally refreshes installed resolution and the lock.checkparses, links, type-checks, and validates environment compatibility.buildemits canonical content-addressed Agent IR and workflow graphs.inspect agentsemits public exported agent descriptors.runvalidates the lock and environment before adapter execution.diagramrenders a static terminal graph.
Build artifacts
dawl build writes:
.dawl/build/<digest>/module.dawlir.json
.dawl/build/<digest>/agents.json
.dawl/build/<digest>/graphs.jsonAdapters consume linked Agent IR, never DAWL source text.
Adapter protocol
A minimal adapter implements:
export function createAdapter(options) {
return {
async initialize(request) {
return {
name: "example",
version: "1.0.0",
protocolVersion: "1"
};
},
async catalog() {
return {
capabilities: {
"tool-use": "native",
"structured-output": "native"
},
tools: [{ name: "example:read" }],
models: [{ id: "example-model" }]
};
},
async validate(agentIR) {
return [];
},
async invokeAgent(request) {
const {
agent,
input,
attempt,
repair,
runId,
path,
callTool,
signal
} = request;
return { status: "ok" };
}
};
}The adapter must create the concrete model session, expose only the linked tools and delegates, honor required capabilities, and return the final JSON-compatible value. DAWL validates the result again.
Interoperability
A2A
import { toAgentCard } from "@monkagoras/dawl";
const card = toAgentCard(publicDescriptor, {
url: "https://agents.example/reviewer"
});The exporter targets A2A Agent Card protocol version 1.0.0 and preserves DAWL input schema, output schema, tools, and digest in namespaced skill metadata.
MCP
import { normalizeMcpTool, toMcpTool } from "@monkagoras/dawl";
const untrusted = normalizeMcpTool(mcpTool);
const trusted = normalizeMcpTool(mcpTool, { trusted: true });
const exported = toMcpTool(dawlToolIR);Untrusted MCP annotations are retained as reportedEffects but are not promoted to enforceable effects.
JavaScript API
import {
adapterCatalog,
applyDiagramEvent,
buildAgentIR,
checkProject,
compile,
createDiagramState,
createProjectResolver,
createRuntime,
loadProject,
normalizeMcpTool,
parse,
publicAgentDescriptor,
renderDiagram,
schemaForType,
toAgentCard,
toMcpTool,
validateValue
} from "@monkagoras/dawl";See examples/agent-project for a complete project with contracts, agents, guard policies, profiles, bindings, an adapter, and a typed review flow.
Determinism and replay
DAWL distinguishes several guarantees:
- Control determinism: source controls sequence, branch, retry, parallel structure, and scope.
- Resolution determinism: imports, adapters, packages, profiles, bindings, and integrity data are locked.
- Contract determinism: input and output schemas are canonical and hashed.
- Replay determinism: completed activities and agents can be reused from event history.
- Live model determinism: not guaranteed by DAWL for hosted models.
A replay identity includes source digest, call path, arguments or typed input, attempt identity unless once, and agent digest where applicable.
Security model
DAWL enforces capability reduction, schema validation, policy decisions, and effect-aware readonly checks. It is not an operating-system sandbox.
- Tool allowlists are exact.
- Guard policy is evaluated outside the model.
- Open-world and destructive effects can be denied or approved.
- Agent delegation is exact and cycle-free.
- Adapter-reported capabilities are checked before execution.
- MCP annotations are untrusted by default.
- Tool implementations still run with the permissions of their process.
Strong multi-tenant isolation requires containers, VMs, or OS-level sandboxing. See ROADMAP.md.
Development
npm run quality
npm test
npm run build
npm run verify
npm run bench
npm run example
npm pack --dry-runThe quality gate enforces source files below 200 lines, functions at or below 20 lines, and low cyclomatic complexity.
Documentation
docs/research/architecture.mdrecords the research basis and architecture decisions.docs/superpowers/plans/2026-08-06-agent-language.mdrecords the implementation plan.ROADMAP.mdseparates intentionally deferred work from the 0.2 contract.
