XDOG

Tools for building AI agents and managing LLM deployments.

GitHub →

xdog / Packages / agent / Design

Agent owns one conversation/tool loop. Applications own admission, permissions, resource lifetimes, durable storage and child execution. There is no second loop inside tool registration, skill discovery or capability assembly.

One loop and explicit request adapters

agent_loop streams a model, validates tool calls, executes them, appends results and repeats until the turn ends. Its internal outer/inner scheduling handles follow-ups and steering; these are not separate Agent runtimes. Tool execution is parallel by default, with serial preparation/finalization hooks.

StreamFn decouples the loop from provider authentication and wire formats. stream_fn_from_provider() bridges the actual AI provider/runtime and its cached model metadata, or a test double. Agent does not choose fallback credentials. AgentState is replaced rather than mutated in place; events and input queues let hosts observe/control execution without taking ownership from the Agent.

Tool definitions, bindings and MCP are distinct

AgentTool supplies an async executor. ToolDef, Param and @action offer a declarative multi-action layer. A host constructs its own ToolRepo, which creates actual bash/filesystem/current_time tools by default; builtin=False starts empty. register(*tools) atomically registers actual tools and returns a close receipt; explicit unregister(name) is required before replacement. select(names) preserves actual identity in a new subset. publish(agent) updates ordinary inventory only at the original idle owner. Imports do not mutate a global registry or authorize tool use.

submit_result is opt-in through its concrete tool constructor. Bare Agent supplies no default shell/filesystem tools. Injected web_search_fn/embed_fn add their respective tools. The host's pre-tool policy must authorize the actual call after transformations; generic tools are not a security boundary.

McpRegistry separately owns configured MCP connections and fenced proxies. Agent consumes it via mcp_registry=..., merging visible tools for the same model/tool loop and hooks. MCP proxies do not enter ordinary ToolRepo. Declaring a server is inert; connection is explicit. Duplicate names fail rather than overriding another source. set_tools() replaces only ordinary inventory; publication never widens an explicit active-tool selection. Agent does not close a borrowed MCP registry, load its credentials from snapshots or replay calls.

Capabilities and skills do not grant authority

Trusted installed xdog.capabilities entry points declare optional tools, skills, resources and extension hooks on actual application/session/group owners. Discovery is inert. Configure CapabilitySet(scope, tool_repo=..., skill_repo=...) then .select(names).open(host) before startup: only selected providers open. Application scope may omit host; session/group need actual owner ports. CapabilityAssembly.create_agent(tool_repo=..., skill_repo=...) adds repos by union to capability and Set contributions; supplemental empty selections cannot remove those contributions. Conflicts fail; there are no separate enable lists. Native Agent keeps tools / skills. Close Agents before their Sets; assembly owns no extra runtime, permission gate, child scheduler or event loop.

SkillRepo(builtin=True, packaged=None) discovers packaged skills by default; .register(directory=path, name="host") adds local sources and is chainable. Different same-slug definitions and directory-name conflicts fail, rather than silently overriding another source. from_packaged / select / merge are SDK operations, not custom host loaders. The read-only skill entry returns metadata and instructions on demand without activating them or granting permissions. Flow/Eyes/Goal bodies are not activated by default. Explicit active_skills / set_active_skills() still control session/turn lifetimes independently of tools. Extensions retain hooks, commands, basic UI and providers, not model tools.

Opening selected Eyes ensures its independent daemon; closing detaches, not daemon stop. Flow CLI execution does not inherit host sandbox, approval hooks or tool allowlists; workflow execution is trusted code, not OS isolation.

Snapshots and opt-in compaction

dump() exports versioned JSON at a settled checkpoint; restore() validates before atomic adoption without starting a model/tool. Hosts reinject adapters, tools, skill repos, hooks, parent handles, MCP registries and compaction policy. Snapshot data does not import code or restore authority. For long histories, dump_metadata()/restore_parts() separate metadata from bounded message parts while retaining complete tool pairing. Recovery dumps are diagnostic, not replay authority for unconfirmed effects. Snapshot events are not disk-commit ACKs.

Automatic compaction defaults off (CompactionConfig.auto=False). Explicit opt-in uses the executing adapter's real limits or trusted host capacity/output reserves, not a guessed fallback window. Manual compact() requires idle, paired history and does not enable auto mode. Summary/cleanup failure retains original history; archival and persistence are host-owned. Compaction manages context, not task lifetime or cumulative work budgets.

Cancellation and children

Steering/follow-up queues are Agent-owned; identified inputs expose selected and consumed states. abort() is a cooperative cross-thread request. On the original loop, cancel_and_wait() cancels/joins the producer; aclose() settles owned cleanup. Cancellation cannot kill blocked native code, undo tool effects, revoke approval or justify automatic replay.

ChildClient, ChildHandle, ChildBackend, ChildSpec and ParentHandle are inert SDK contracts/data access. Coding owns its child runtime/loop; Claw owns its child runtime and borrows its application loop. Their stores, workers and grants are not an Agent SDK supervisor service. Children require current parent-subset authority; saved records, role labels and skill text grant no access.

See reference, registry contract and capability SDK example.