Skip to main content

One-file Extensions

CortexFS follows the same anti-framework extension rule as Pi: add behavior at stable edges (packages, executables, skills, modules, channel adapters) without a second root ABI or a resident plugin daemon. Product placement of those edges is in architecture.md (Extension points). This page is the shortest authoring path for existing compatibility surfaces.

The shortest way to add behavior is one package directory. Keep the program logic in normal executables; keep the wiring in one cortexfs.toml:

review-kit/
├── cortexfs.toml
└── bin/
├── review-agent
└── git-summary
schema = "cortexfs.package/v1"
name = "review-kit"
version = "0.1.0"

[[tools]]
name = "git.summary"
run = "bin/git-summary"
description = "Summarize the current Git worktree"
schema = { type = "object" }

[[agents]]
name = "kit_reviewer"
run = "bin/review-agent"
abi = "sdk-envelope-v1"
model = "main"
tools = ["git.summary"]
instructions = "Review changes, use the tool when useful, and cite evidence."
parent = "agent:architect"

The executable Agent SDK example above is a compatibility surface from the former self-hosted runtime. New hosted CLI integrations should prefer a thin process launch profile instead of adding new loop/provider/session behavior to sdk-envelope-v1.

Validate the complete package without writing a backing tree, then install it:

ctx install --check ./review-kit
ctx install ./review-kit

For a prebuilt package, bind each member to its distributed bytes by adding the exact lowercase digest next to run, then require every member to have one:

[[tools]]
name = "git.summary"
run = "bin/git-summary"
sha256 = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
ctx install --check --require-hashes ./review-kit

Replace the example digest with the output of sha256sum. Every declared sha256 is checked, even without --require-hashes; the flag additionally rejects a package when any tool or agent omits its digest.

ctx install --check finds cortexfs.toml, hashes every executable, renders and checks every strict object manifest, then exits before choosing or writing a backing tree. Normal ctx install repeats those checks before publishing each object through the existing atomic object installer. A matching digest binds the descriptor to the executable bytes, but does not authenticate a publisher or registry. Obtain the descriptor and expected digest through an authenticated trusted channel; cortexfs.package/v1 does not define signatures.

Use --source PATH when the mounted tree is backed by a specific generation, and use --tier user for tools that should only be visible to the current user. Agent objects are system-tier because their runtime socket is host-owned:

ctx install ./review-kit --source /var/lib/cortexfs/storage/current

Agent Unix identity is host authority, not package metadata. The installer derives it from its effective user and supplementary groups; package authors cannot select a uid, gid, or privileged group.

The run file is the extension point. A tool implements the Tool SDK and a legacy executable agent implements the Agent SDK; both are ordinary executable files, so a Rust, shell, or another host-language build can produce them. An SDK agent receives one hosted envelope on stdin and returns JSONL events. It may yield a tool call; the host performs the capability check and sends the observation back for the next step. This is compatibility behavior, not the target architecture for new Agent CLI integrations.

For a build-free compatibility agent, copy examples/extensions/shell-agent/. It uses POSIX shell plus jq, requires the seven core keys, permits the SDK-optional event/origin keys, rejects every unknown envelope key, preserves the runtime's forward-compatible origin extension behavior, enforces byte bounds, validates known present event/origin values, caps each response at the runtime's 256 KiB newline-inclusive limit, and rejects a wrong launch marker, sticky frames, mismatched run/step values, and continuations it does not implement. It then emits one canonical assistant message frame:

CTX_BIN=ctx ./examples/extensions/shell-agent/install.sh
CORTEXFS_EXTENSION_INSTALL=yes \
CTX_SOURCE=/var/lib/cortexfs/storage/current \
./examples/extensions/shell-agent/install.sh
ctx agent start field-notes --session demo --cwd /workspace
ctx agent send field-notes --session demo "record this decision"

The first command only validates. Installation remains an explicit second step. Run scripts/shell-agent-smoke.sh for the provider-free envelope test. Use cortexfs-agent-sdk only for existing compatibility consumers; do not grow new provider/model/session orchestration there when a hosted CLI already owns that behavior.

Official compatibility defaults remain overrideable:

loop=chat|react|coding|planner|research built-in behavior hint (default chat)
loop=<name> + loop.d/<name> custom loop driver executable
compact.strategy=truncate|summarize|<name> history rebuild strategy (default truncate)
compact.d/<name> custom compaction executable
invoke.strategy=default|cli|sdk|<name> tool invoke surface (default default)
invoke.d/<name> custom tool invoke executable
adapter=<family>|<name> channel adapter family or custom name
adapter.d/<name> custom channel socket driver executable
Agent SDK BuiltinLoop helpers interpret CTX_AGENT_LOOP inside your binary
Tool SDK InvokeMode helpers read CTX_TOOL_MODE / CTX_AUTHORIZED_OBJECT
Channel SDK DriverLaunchConfig read CORTEXFS_CHANNEL_* / CTX_CHANNEL_* env

Do not add new Agent-loop features to these compatibility controls during the hosted-CLI migration.

Topology is just the parent edge. Every agent names its parent as agent:NAME (optional session: and run: qualifiers remain available), so a tree is visible in the same control files that enforce ownership:

[[agents]]
name = "planner"
run = "bin/planner"
parent = "agent:architect"

[[agents]]
name = "builder"
run = "bin/builder"
parent = "agent:planner"

The package file is authoring input, not a second /ctx namespace. After installation the durable result is still only agent/<name>.d/*, tool/<name>.d/*, ordinary session files, and the existing sockets. The raw ctx object install command remains available for package builders that need full manifest control; most users do not need to see it.

Development/config activation is commit-bound. Commit the package or configuration first; a later process restart is ordinary lifecycle that may consume the already committed generation. Restart must never make uncommitted package/config changes authoritative. ctx install never starts a watcher, polling loop, or background plugin service.