Skip to main content

Package System

AILANG's package system enables multi-package projects with deterministic dependency resolution, export enforcement, and effect ceilings. Packages are the primary unit of autonomous coordination — they define what an agent can see, change, what authority it has, and what guarantees it must preserve.

Quick Start

# Create a package
ailang init package --name myorg/mylib
ailang init package --name sunholo/myapp --module-prefix myapp # for existing apps

# Add a dependency
ailang add --path ../shared-utils

# Resolve dependencies and generate lock file
ailang lock

# Validate package (cross-module type checking)
ailang check --package .

# Run tests
ailang test --package .

# View dependency tree
ailang tree

# Publish to registry
ailang publish --dry-run
ailang publish

# Run (package resolution is automatic)
ailang run main.ail

Package Structure

A package is defined by an ailang.toml manifest:

my-package/
ailang.toml # Package manifest (human-written)
ailang.lock # Resolved dependencies (machine-generated)
src/
core.ail # Source modules
helpers.ail
tests/
core_test.ail

Manifest (ailang.toml)

[package]
name = "sunholo/docparse"
version = "0.3.0"
edition = "1"
ailang = ">=0.9.5"
description = "Parse documents into structured blocks"
license = "MIT"

[exports]
modules = [
"sunholo/docparse/parser",
"sunholo/docparse/types"
]

[dependencies]
"sunholo/json" = { path = "../json" }

[effects]
max = ["IO", "FS"]

[stability]
level = "experimental"

Required Fields

  • [package].name — Two-level vendor/name format
  • [package].version — Semantic version
  • [package].edition — Language edition (currently "1")

Optional Fields

  • [package].ailang — Minimum AILANG version required (e.g., ">=0.9.5"). Auto-set by ailang init package. Enforced on ailang install — incompatible packages are rejected with a clear error message.
  • [package].module_prefix — Maps existing module paths to the package namespace without renaming source files. See Module Prefix below.

Module Prefix

Existing applications can adopt the package system without renaming all module declarations. If your project uses module myapp/services/api but you want to publish as sunholo/myapp, set module_prefix:

[package]
name = "sunholo/myapp"
module_prefix = "myapp"

[exports]
modules = ["myapp/services/api", "myapp/handlers/parse"]

Source files keep their existing module myapp/... declarations. Consumers import via the canonical path:

import pkg/sunholo/myapp/services/api (startServer)

The loader maps sunholo/myapp/services/apimyapp/services/api automatically.

Rules:

  • module_prefix must be a single segment (no slashes)
  • Exports can use either module_prefix/... or vendor/name/... prefixes
  • CLI: ailang init package --name sunholo/myapp --module-prefix myapp

Exports

Only modules listed in [exports].modules are accessible to dependents. Modules not in this list are package-private.

[exports]
modules = ["sunholo/docparse/parser"]
# sunholo/docparse/internal/helpers is NOT accessible from outside

Effect Ceiling

Packages declare their maximum allowed effects:

[effects]
max = ["IO"] # Only IO allowed; Net, FS, etc. would be a compile error

If a function in the package declares an effect not in max, compilation fails:

effect ceiling violation in package sunholo/mylib: effects [FS] not in max [IO]
Add missing effects to [effects].max in ailang.toml

An empty max = [] means the package is pure — no effects allowed.

Ghost effects (like Debug) are exempt from ceiling checks. Packages can use std/debug or sunholo/logging without listing Debug in [effects].max. Ghost effects are invisible to callers, auto-granted at runtime, and erased in --release mode.

Stability

[stability]
level = "experimental" # or "stable" | "frozen"

Lock File (ailang.lock)

Generated by ailang lock. Committed to version control. Contains:

  • Content hash (SHA256 of all .ail source files)
  • Interface hash (SHA256 of exports + effects — stays same on internal refactor)
  • Resolved dependency paths

The lock file is validated at build time — if a dependency's content has changed, you'll see:

Warning: dependency sunholo/json content changed (locked: sha256:a1b2c3... current: sha256:d4e5f6...)
Run 'ailang lock' to update

Importing from Packages

Use the pkg/ prefix for external package imports:

-- Stdlib (always available)
import std/io (println)

-- Local module (same project)
import myproject/parser (parse)

-- External package
import pkg/sunholo/json/parser (parseJson)

The pkg/ prefix tells the compiler to resolve against the lock file instead of the local filesystem.

Dependencies

Path Dependencies

Link to local packages:

[dependencies]
"shared/utils" = { path = "../utils" }
"shared/types" = { path = "../types" }

Git Dependencies

Pin to a specific tag or commit in a git repository:

[dependencies]
"sunholo/auth" = { git = "https://github.com/sunholo-data/ailang-packages", subdir = "packages/auth", tag = "main" }

Registry Dependencies

Install from the AILANG package registry:

[dependencies]
"sunholo/auth" = "0.1.0"

Resolution priority: path > git > registry.

CLI Commands

CommandDescription
ailang init package --name vendor/nameCreate ailang.toml
ailang add --path ../depAdd path dependency
ailang add --git URL --subdir DIR --tag TAGAdd git dependency
ailang install vendor/nameInstall latest from registry
ailang install vendor/name@versionInstall exact version from registry
ailang lockResolve deps, generate ailang.lock
ailang treeDisplay dependency tree
ailang search "query"Search registry packages
ailang publishPublish to registry
ailang pkg-docs vendor/nameView package AGENT.md

Registry

The AILANG package registry is hosted on GCP at https://storage.googleapis.com/ailang-registry.

Publishing

cd my-package/
ailang publish # Upload to registry (requires AILANG_REGISTRY_API_KEY)
ailang publish --dry-run # Preview without uploading

The validator service automatically:

  • Compiles your package (ailang check)
  • Verifies effect ceilings match [effects].max
  • Runs contract verification (ailang verify, best-effort)
  • Computes content + interface + tarball hashes
  • Rejects duplicate versions (immutable once published)

Searching

ailang search "auth" # Keyword search on name, ai_summary, tags
ailang search --tag gcp # Filter by tag
ailang search # List all packages

Installing

ailang install sunholo/auth # Install latest version
ailang install sunholo/auth@latest # Same as above
ailang install sunholo/auth@0.1.0 # Install exact version

The resolved exact version is always written to ailang.toml. Semver ranges (^, ~, >=) are not supported — AILANG requires exact versions for deterministic builds.

Lock File Portability

ailang.lock is portable across machines and containers — it does not contain absolute paths. Cache paths for registry and git packages are resolved at runtime. This means you can COPY ailang.lock into Docker and run ailang install to populate the cache.

Version Conflict Detection

AILANG uses flat dependencies — one version per package name. If a transitive dependency requires a different version than your root manifest pins, ailang lock fails with a structured error:

version conflict: sunholo/firestore
root requires: 0.2.0
already resolved: 0.1.0
transitive requires: 0.1.0 (via sunholo/billing_store)

resolution aborted

suggestion:
- republish sunholo/billing_store against sunholo/firestore@0.2.0
- or change root dependency to sunholo/firestore@0.1.0 explicitly

Direct dependencies are authoritative — the resolver never silently downgrades them. To fix a conflict, either republish the transitive package or change your root pin to match.

Package Documentation

ailang pkg-docs sunholo/auth # Display AGENT.md usage guide

Each package can include an AGENT.md — a structured guide for AI agents with quick start, exported functions, and common patterns.

Environment Variables

VariablePurpose
AILANG_REGISTRYRegistry URL (default: https://storage.googleapis.com/ailang-registry)
AILANG_REGISTRY_VALIDATORValidator service URL (for ailang publish)
AILANG_REGISTRY_API_KEYAPI key for publishing
AILANG_CLOUD_PROJECTGCP project hosting the cascade Pub/Sub topic (e.g. ailang-multivac-dev). When unset, ailang publish skips the cascade-topic publish and only emits legacy inbox messages.
AILANG_TOPIC_PREFIXPrefix for cascade topic names (default: ailang). Cloud envs use the env-specific prefix (ailang-dev, ailang-test, ailang). The full topic name is <prefix>-cascade.

Cascade env vars: Both AILANG_CLOUD_PROJECT and AILANG_TOPIC_PREFIX must match the cloud coordinator's deployment. For the public ailang-dev environment:

export AILANG_CLOUD_PROJECT=ailang-multivac-dev
export AILANG_TOPIC_PREFIX=ailang-dev

Without these, ailang publish still uploads to the registry and notifies dependent inboxes, but the cloud coordinator never wakes up to open repair PRs — the cascade is effectively local-only.

Admin: Rebuild Index

If the registry index becomes corrupted, an admin can rebuild it from all published metadata:

curl -X POST https://<validator-url>/rebuild-index \
-H "X-API-Key: $AILANG_REGISTRY_API_KEY"

This scans all metadata.json files in the bucket and reconstructs index.json. Same API key auth as publish.

Available Packages

PackageDescriptionEffects
sunholo/authAPI key validation, HMAC hashing, bearer tokensPure
sunholo/gcp_authGCP ADC OAuth2 token exchange, project detectionFS, Net, Env
sunholo/http_helpersHTTP request builders, auth headers, JSON response parsingNet
sunholo/loggingStructured JSON logging for Cloud RunIO
sunholo/configConfig loading from env vars with validationEnv
sunholo/testing_utilsTest assertion helpersPure
sunholo/registry_validatorPackage validator written in AILANG (dogfooding)IO, FS

Source: github.com/sunholo-data/ailang-packages

Dual Hash Model

Each package in the lock file has two hashes:

HashChanges WhenPurpose
Content hashAny source file changesReproducibility
Interface hashExports or effects changeCoordination — internal refactors don't cascade

Backward Compatibility

Projects without ailang.toml work exactly as before. The package system is opt-in — existing module imports (std/..., local modules) are unaffected.

Workspace Pattern

Multi-package repos use path dependencies without needing a registry:

workspace/
types/
ailang.toml # name = "myorg/types"
parser/
ailang.toml # name = "myorg/parser", depends on types via path
app/
ailang.toml # depends on types + parser via path

Each package is independently valid. Workspaces are emergent from path-linked packages, not a separate manifest type.

AI Agent Coordination

The registry is more than a package manager — it's the coordination layer through which AI agents discover, share, and compose capabilities.

Why This Matters for Multi-Agent Workflows

When multiple AI agents work on a project (or across projects), the registry provides the shared vocabulary:

  • Effect ceilings — An agent can inspect [effects].max to know exactly what authority a package has. A package with max = ["IO"] cannot access the network, filesystem, or environment. This lets agents reason about trust boundaries without reading source code.

  • Interface hashes — The interface hash changes only when exports or effects change. An internal refactor (same exports, same effects) produces a new content hash but the same interface hash. Downstream agents can skip re-verification when only content changes.

  • AGENT.md discovery — Each package can include an AGENT.md — a structured guide written for AI consumption, not human prose. The registry indexes whether a package has one (has_agent_doc: true), and ailang pkg-docs vendor/name serves it directly.

  • Immutability — Once published, a version can never be overwritten. This means agents working concurrently can safely depend on a version without worrying about it changing underneath them.

  • Machine-readable metadataai_summary, tags, effects, and has_agent_doc are all in the registry index as structured JSON. Agents can programmatically search and filter without parsing documentation.

Typical Agent Workflow

# 1. Discover what's available
ailang search "auth" --tag gcp

# 2. Read the AI-specific documentation
ailang pkg-docs sunholo/gcp-auth

# 3. Install with hash verification
ailang install sunholo/gcp-auth@0.1.0

# 4. Use in code
# import pkg/sunholo/gcp-auth/token (getAccessToken)

# 5. Publish reusable work back to the registry
ailang publish --dry-run # Validate first
ailang publish # Share with other agents

Writing Good AGENT.md

Structure your AGENT.md for machine consumption:

  • Quick Start — minimal import + usage example (copy-pasteable)
  • Exported Functions — signatures with effect requirements
  • Common Patterns — typical workflows with the package
  • Effect Requirements — what --caps flags the consumer needs

Package Coordination Messages

When packages are published or upgraded, the system emits structured coordination messages to notify affected workspaces. This enables multi-agent coordination at package scale.

Auto-Emitted Events

Running ailang publish automatically emits:

  • upgrade-available — new version with change class (A=internal, B=additive, C=contract)
  • interface-change-notice — when exported API (interface hash) changed
  • effect-widening-warning — when effect ceiling expanded (e.g., added Net)

Package-Scoped Inboxes

Messages route to typed inboxes: pkg:sunholo/auth, workspace:docparse, team:registry-admin.

# Notify downstream consumers
ailang pkg notify-upgrade sunholo/auth@0.2.0 --summary "Tightened validation"

# Find who depends on your package
ailang pkg affected-by sunholo/auth

# View coordination messages
ailang messages list --inbox pkg:sunholo/auth

For AI Agents

When upgrading a dependency, check for coordination messages:

  1. ailang messages list --inbox workspace:<your-workspace> --unread
  2. Review any upgrade-available or interface-change-notice messages
  3. Run compatibility checks
  4. Send a compatibility-report back to the package inbox

See Agent Messaging Guide for full details.

Troubleshooting

Common Errors

ErrorCauseFix
409 Conflict on publishVersion already existsBump version in ailang.toml
401 UnauthorizedMissing or invalid API keySet AILANG_REGISTRY_API_KEY
hash mismatch on installCorrupted download or tampered packageRetry; if persistent, report to registry admin
effect ceiling violationPackage uses effects not in [effects].maxAdd missing effects to ailang.toml (note: Debug is exempt as a ghost effect)
package not foundWrong name or not yet publishedCheck spelling with ailang search
IMP010: symbol not exportedType missing export keywordAdd export type Foo = ...
LDR001: module not foundMissing dependency or wrong importAdd dep + ailang lock
cannot unify type constructor X with TRecordType alias not exportedAdd export type in defining module
PAR_HYPHEN_IN_MODULEHyphen in module pathUse underscores: billing_store

Common Pitfalls

1. Hyphens in module names: Directory names can use hyphens (billing-store/) but module paths must use underscores (module sunholo/billing_store/...). Hyphens parse as subtraction.

2. Missing export type: Any type used by another package must have export type, not just type. This includes record types AND ADTs.

3. Use ./ for intra-package siblings, pkg/ for external: Three-way import distinction:

import ./plan (Plan, lookupPlan) -- LOCAL: sibling in same package
import pkg/sunholo/firestore/client (getDoc) -- EXTERNAL: different package
import std/result (Ok, Err) -- STDLIB: bundled

./ resolves in module namespace: if current module is a/b/c, then ./d means a/b/d.

4. Ok/Err not in prelude: Every file that uses Result needs import std/result (Ok, Err).

5. Run ailang lock after changing dependencies: The lockfile must be regenerated whenever [dependencies] changes.