Aller au contenu

Maia Agent architecture

Status: Source candidate: local maia agent on the official Pi SDK 1.0.0 with digest-pinned resources from .maia/maia.json, the maia-agent-worker managed worker, Node's two Pi Session adapters and maia login <model-provider> (current implementation). Planned: the context and resource contract (R1, R11 AG1–AG3), owner-aware Sessions and delegation (R3), maia session attach (S12), coordinator learning (R4), agent-callable evaluation (R10a) and the daily Pi-harness replacement (R11). Everything outside "Current implementation" is target design.

Outcome

maia agent is Pi rebranded for Maia: the official Pi SDK interaction and extension model behind Maia's one executable (decision). One kernel serves two uses:

  • a local CLI process for Pi-style work;
  • the worker that Maia Node runs for durable Agent Sessions.

Rules:

  • Pi extensions are the only customization boundary. They register tools, slash commands, CLI flags, shortcuts, renderers, providers and lifecycle hooks through the upstream Pi API. Maia's own kernel extensions use the same API. There is no parallel plugin system.
  • At runtime Maia never reads or writes ~/.pi. Pi Sessions use the Pi package bundled with Maia. Only the separately authorized Pi RPC runtime probe runs an installed pi, after matching it to a human-approved pi-rpc integration authorization. The R11 inventory is an explicitly authorized, read-only migration inspection outside runtime.
  • Maia owns its configuration and state paths, resource admission, authentication references, safety policy, receipts and lifecycle.
  • Maia Node owns durability, Maia Swarm owns multi-Project orchestration and Providers owns Connections and login. Launch flags, interactive commands, model tools and JSON results are specified once in the CLI reference.
  • There is no separate Fleet, Workflow, Task-graph or Subagent product. task is a Session-local work ledger, not Swarm scheduling truth. A sub-agent is a normal Agent Session linked by parentSessionId.
  • v1 workers are vendor executors (Codex CLI, Claude Code) (decision). Maia Agent becomes the native worker backend in later slices; its Node adapters are source candidates today.
  • The daily Pi-harness replacement is a reviewed feature port, not a profile import: see the R11 migration procedure. The capability disposition is the only feature mapping.

Agent nouns (Workspace, Project and execution nouns are in product vocabulary):

ConceptMeaning
AgentVersioned model policy, tools, project-instruction policy, eligible Skills, limits and peer permissions
Agent SessionOne stateful Agent instance with an exact resolved snapshot and an optional parent Session
RoutineManual or cron trigger that starts an Agent Session or wakes one exact idle Session

Current implementation

The Agent README is the package overview. The launch grammar is in the CLI reference.

  • Every entry sets PI_OFFLINE=1 and PI_SKIP_VERSION_CHECK=1. That stops Pi's update, telemetry and tool-download traffic, not the model provider the user selects. Embed the package in a process dedicated to the agent.
  • Project extension code runs with the agent process's operating-system permissions, so review it before pinning its digest. Extensions cannot bypass the Node's authorization, confirmation, compare-and-set, idempotency or receipts.

Local maia agent

  • A terminal runs Pi InteractiveMode. --print and --json-events run Pi print mode with text or JSON event output.
  • MAIA_HOME (default ~/.maia) holds auth.json (provider credentials) and config.toml ([agent] model sets the default model) (decision).
  • MAIA_AGENT_HOME (default $MAIA_HOME/agent) is Pi's agent directory. It holds models.json, local session files under sessions/<sha256 of the working directory>/ and task/goal state under state/sessions/<session id>/.
  • --continue resumes the most recent local session for the working directory. --no-session keeps the session in memory. Local session files are not Node Sessions: no recovery, background execution, ordered events or receipts.
  • maia agent login opens the interactive agent so the user can type /login. maia login <model-provider> (source candidate) uses the same Pi provider sign-in and stores one credential per provider in $MAIA_HOME/auth.json (decision).

Resource loading

  • Pi resource discovery is off: no discovered extensions, Skills, prompt templates, themes, context files or .pi/SYSTEM.md and APPEND_SYSTEM.md prompt files. Settings are in memory, and Pi's agent directory is the Maia-owned one, never ~/.pi/agent.
  • Pi's built-in codemode, llama.cpp, MCP and tool-search extensions never load: Maia does not register them and requests none (decision).
  • Kernel extensions always load: destructive-command safety guard, task, goal, process and bounds on Pi's find and grep (80 hits by default, 200 at most, above 80 only with a path or glob), which stay inactive until Maia owns their fd and rg binaries. The standalone maia agent also gets the /goal and /tasks commands, bounded maia_goal and maia_tasks prompt sections (untrusted, digest-framed, at most 2 KiB each) and naming of an unnamed session from its first request; the managed worker leaves commands, context and Session metadata to the Node. These are reviewed ports with provenance (port record).
  • The nearest ancestor with .maia/maia.json is the Project root. The file must set schemaVersion: 1, agent.runtime: "pi" and agent.transport: "managed". agent.resources.skills lists Skill paths under .maia/. Unless -ne is set, agent.resources must also set extensionPolicy: "explicit", extensionRoot (relative, inside .maia/) and an extensions list of paths under that root.
  • Every listed path is relative, stays inside its root, contains no symbolic links and matches its sha256: digest. A mismatch or an extension load error stops startup. maia agent extension digest <path> prints a digest.
  • -e <path> adds an explicit extension. -ne skips the configured Project extensions without resolving them; kernel extensions, explicit -e paths, Skills and AGENTS.md still load.
  • AGENTS.md: every AGENTS.md from the Project root down to the working directory loads at startup. Without .maia/maia.json, the root is the nearest .git ancestor, else the working directory. This differs from the target: one root file plus at most one nearest nested file.
  • Agent definitions: S49's Agent definition is the one public contract. The managed worker's protocol maia.agent-definition/v1, a compatibility contract that R11 AG1 maps onto S49 or retires, has key, name, prompt (at most 16 KiB), provenance, up to 32 requestedOperationIds, up to 32 skillRefs and version. Node resolves only the shipped maia v1 definition. maia session create --agent-definition <key> --agent-definition-version <version> --agent-definition-digest <sha256> records that exact reference as metadata; starting a Session bound to a definition is refused with command_not_implemented.

Managed worker and Node adapters

  • maia-agent-worker (packages/agent/src/managed-worker/) runs one official Pi session over NDJSON protocol version 1. The Pi session and credentials stay in memory; durable history comes from Maia; the worker never writes auth.json. task and goal state go under the Node-supplied agent directory. Protocol v1 bounds input frames to 64 KiB, prompts to 48 KiB, responses to 32 KiB and output to 1 MiB; the target worker protocol replaces it.
  • Node picks a Pi adapter by host (harness README):
    • macOS: harness/pi-rpc/ runs the bundled official @earendil-works/pi-coding-agent 1.0.0 CLI in --mode rpc under the cooperative cooperative_pi_rpc_conversation tier, with tools, extensions (Pi's built-in ones included), Skills, prompt templates, themes and context files disabled. Node issues each launch authorization itself.
    • Linux x64 and Windows x64: harness/pi-managed/ launches the packaged maia-agent-worker under hard containment.
  • Without a tool projection the worker builds the same kernel session as local mode. The internal session.show/v1 selector instead gives it only a zero-argument session_show tool backed by an operation port to Node. Node-bound activation stays denied pending policy integration.

Runtime compositions

Local mode

terminal -> maia agent -> Maia Agent kernel -> Pi SDK session -> model and tools

Local mode uses the same Pi interaction and extension contracts as managed mode. Its session files never claim Node recovery, background execution, ordered events or receipts.

Managed mode

maia CLI/SDK -> Maia Node durable Agent Session -> disposable worker -> Maia Agent kernel -> Pi SDK session

The worker receives one exact activation, rebuilds bounded history from the agent's own Pi session through its reader, checked against the Node's trace (AG3a, D-168), streams structured events and closes the Pi session. It never owns durable identity or SQLite state.

Peer and coordinator Sessions (R3)

  • An Agent may start same-Project child or eligible peer Sessions through the registry-backed agent tools (start_agent and its siblings) (one peer-agent protocol). Node and Swarm admit and persist managed requests. Local in-process supervision claims no durability or isolation.
  • Delegation only narrows authority. Swarm's peer-agent protocol owns the admission limits; the Agent definition narrows them further.
  • Cross-owner work uses parent-mediated delegation, never a sibling grant. Maia is the coordination hub through which agents and harnesses delegate (decision).
  • Maia, Workspace and Project coordinators reuse the kernel as separate owners: each has its own principal, Session, context, instructions, Skills, tool manifest, credential handles and budgets (decision). Reuse never shares a conversation. A Project reads no sibling Project's history, titles, memory or content, and discovers siblings only through the bounded peer directory when both Projects allow Workspace reach (D-166); a Workspace sees only its own Projects. Workspace and global variants need explicit schemas before activation. Runtime isolation also needs the Node containment contract.
  • Ordinary Workspace Sessions follow the ownership contract. Activation binds the real Workspace owner, never a fabricated Project. A conversation-only activation grants no shell or file access.

Agent-callable evaluation (R10a)

Explicitly granted coordinators and bots may call the shared evaluation service. Execution workers get no evaluation tools; central services use it on their behalf. Rules are in agent use and bounded composition. No tool name is claimed until the protocol-to-agent slice passes.

Agent definitions

Target definitions live in .maia/agents/<id>/agent.jsonc (layout, schemas). How the target schema relates to the current maia.agent-definition/v1 (requestedOperationIds versus toolGrants, skillRefs versus skills.eligibleRefs) is open for R3 to decide. Target fields:

  • schemaVersion, id and displayName identify the definition.
  • model selects an allowed connectionRef and a modelId resolved at validation.
  • role describes the Agent; it grants nothing.
  • toolGrants lists exact operation IDs. Each agent tool registers only when its mapped session.* operation is granted.
  • skills.eligibleRefs lists exact Skill references. A definition cannot preload Skill bodies.
  • peerPolicy lists mayStartRefs and mayMessageRefs.
  • homeSession: true asks Swarm for one canonical home Session per exact owner and definition (home Sessions).
  • childCompletionPolicy is wait (default), request-stop or detach-to-project. A child may only narrow it (detach-to-project to request-stop to wait). On parent completion Node records session.child.waiting, session.child.stop-requested or session.child.detached with both Session revisions and a receipt. Detach clears parentSessionId only after Project admission succeeds; on failure the child stays attached and parent completion is blocked.
  • limits sets maxChildDepth, maxLiveChildren, maxWallTimeMs and maxOutputBytes.

EVAL.md (planned; scheduled by roadmap slice S62) sits beside an Agent or Routine definition. It is evaluator-only and never enters operational context. Its closed frontmatter declares schemaVersion, subject, traceClasses, sampling (algorithm, maxCases) and rubric (id, weight). Unknown trace classes, unbounded sampling or executable content fail validation. Evaluation runs are owned by Node.

Resolution order is fixed:

discover explicit roots -> parse closed schemas -> canonicalize and contain paths
-> resolve exact namespaced references -> apply typed narrowing
-> validate hard gates -> compile context and resource manifests
-> freeze snapshot digest -> authorize and admit or reject

Configuration resolution

Schemas and the resource resolution order are in configuration contracts. Authority layers apply in this order:

  1. immutable Maia safety and default contract;
  2. Node operator policy;
  3. admitted user preferences and explicitly referenced user resources from ~/.maia/;
  4. Project configuration and resources;
  5. exact activation overrides.

Merge rules:

  • Later layers may narrow authority and choose among allowed Connections and models. They cannot raise operator limits.
  • Scalar preferences take the highest allowed layer. A resource reference resolves to one exact namespaced resource and digest; definitions never deep-merge.
  • Permission and capability sets intersect. Limits take the strictest value. Secrets stay references.
  • A live or queued Session never changes snapshot. A config change creates a new snapshot.
  • User Agents and Skills under ~/.maia/ are indexed as metadata only; nothing from the user scope enters context without an explicit reference. ~/.maia/projects.jsonc is Swarm locator input, never Agent context.
  • Hard schema and safety failures block activation. Local mode uses the same resolver in memory.

Minimal-context contract

Context is a scarce resource: every loaded byte needs an owner and a reason. The prompt contract is one root AGENTS.md plus progressively loaded Skills, with no rules/ tree (decision).

Instruction sources

Only these sources may enter a native Agent context:

  1. a small versioned Maia core system prompt;
  2. the resolved Agent definition summary;
  3. the repository root AGENTS.md;
  4. at most one nearest nested AGENTS.md for the active path scope, only when the Project enables it;
  5. bodies of explicitly selected Skills;
  6. the current user or Routine instruction;
  7. bounded, labelled runtime projections from typed services.

No Markdown glob, rules/ traversal, Skill-body preload, extension-owned global append or home-directory discovery. Nested AGENTS.md loading is off by default; when enabled, Maia resolves the nearest file once for the turn's declared path scope and records it in the manifest. It never walks the tree.

What belongs where:

  • Core prompt: runtime identity, trust model, tool protocol, authority invariants.
  • AGENTS.md: stable repository constraints, conventions and verification rules; no long procedures, tool grants or credentials.
  • Skill: one task procedure and its reviewed resources; no hidden authority or automatic execution.
  • Agent definition: model, tool and Skill allowlists and limits; no credentials or unbounded prompt text.
  • Routine instruction: desired outcome and trigger context; no scripted model steps or escalation.

Trust classes, as recorded in each manifest source entry:

SourcecontentDispositionHandling
Maia core contractinstructionimmutable versioned system section; cannot replace server policy
Agent definition, AGENTS.md, Skill, Routine/user instructionuntrusted-instructionattributed delimiters; guides reasoning only
repository file, tool output, web, MCP, memoryuntrusted-dataopaque data delimiters; never a grant or configuration
peer/A2A message, Cursor event or artifactuntrusted-external-messagesender attribution, bounded and redacted
trace, EVAL.md, evaluation evidenceuntrusted-evaluation-dataevaluator isolation; no operational tools

Only the core contract has trust: "system" and authorityClass: "authority-contract"; every other source has trust: "untrusted" and authorityClass: "none". Every loader declares one class; an unknown or mismatched class fails compilation. No text can select Connections, add tools, change budgets, confirm mutations or activate configuration. Authorization stays outside the model.

Context lint reports a block whose normalized digest appears in both AGENTS.md and a Skill. Conflicting values for a closed-schema field are an error unless a narrower scope may override. Natural-language contradiction checks are optional advisories and never block activation.

Progressive Skill loading

This section owns the loader. What a Skill is, its scopes, pins and updates: Skills.

  • Startup indexes only bounded Skill metadata: ID, description, source, digest and eligibility.
  • A Skill body loads only by an explicit eligible Skill ID from the user, a Routine or the Agent, through the typed Skill loader. The Agent may pick a Skill from metadata without the user naming it; picking never grants eligibility.
  • Supporting resources load one at a time, only when the selected Skill references them.
  • A Skill never executes. The Agent reasons over it and calls typed tools.
  • Built-in Skills are exceptional: only those needed to operate Maia.
  • Selection, body load and resource load each emit a context-manifest event.
  • R4 adds learned Skills under a separate eligibility class and provenance variant; the coordinator learning contract owns them. The baseline schema must deny that path.

Every Skill and extension resolves to a provenance record before indexing:

interface ResourceProvenanceV1 {
  readonly resourceId: string;
  readonly kind: "skill" | "extension" | "instruction" | "supporting-resource";
  readonly source: "builtin" | "user" | "project";
  readonly canonicalLocator: string;
  readonly contentDigest: string;
  readonly manifestDigest: string;
  readonly signerKeyId: string | null;
  readonly trustDecision: "reviewed-builtin" | "explicitly-pinned" | "rejected";
  readonly reviewedAt: string | null;
}

Built-ins are release-artifact files covered by the package digest. User and Project resources need an exact configured digest; a signature never replaces explicit admission. Directory traversal, symlink escape, duplicate canonical ID, mutable remote URL, digest drift or unsupported executable content rejects the resource. An executable Pi extension runs only in a worker whose execution profile admits its digest; managed mode never loads ambient or unsigned extension code.

Prompt composition order

  1. core system prompt;
  2. tool names and compact descriptions;
  3. Agent limits and model policy;
  4. root AGENTS.md;
  5. active path-scoped AGENTS.md, if any;
  6. selected Skill bodies, then individually requested Skill resources;
  7. bounded runtime projections framed as untrusted data: at most one active Goal summary, a hot-task summary, pending interaction and message counts, requested status evidence and, with R4, policy-selected KB and memory items with selection reasons;
  8. bounded conversation history;
  9. current instruction.

The full task ledger, memory store, raw history, traces, Project catalog and peer transcripts never preload; the model reads them through bounded tools. Later context cannot grant capabilities.

Context manifest

Every activation produces a redacted maia.context-manifest.v1:

  • activationId, agentSnapshotDigest and totals (bytes, estimatedTokens);
  • one sources entry per loaded source: kind, source, reason, digest, bytes, estimatedTokens, trust, authorityClass, contentDisposition, pathScope, loaderId, causationId;
  • warnings.

It holds digests and measurements, never secret content. Target commands: maia context inspect --json returns it; maia context lint --json reports excess size, duplicates, conflicts, undiscoverable Skills, invalid nesting and unauthorized sources.

Initial budgets

A Project may only lower these limits:

ContributionWarningHard failure
Root AGENTS.md4 KiB8 KiB
One nested AGENTS.md2 KiB4 KiB
One loaded Skill body6 KiB12 KiB
All loaded Skill bodies12 KiB24 KiB
One dynamic runtime projection2 KiB8 KiB
All dynamic runtime projections8 KiB16 KiB
All instructions before conversation history24 KiB40 KiB
Total initial activation before model call48 KiB64 KiB
  • Byte limits are deterministic guards, not tokenizer claims. The manifest reports exact token counts where an approved tokenizer exists and labelled estimates otherwise.
  • The total activation also stays below 25% of the selected model's context window.
  • Only Node operator policy may raise a hard limit, through an explicit budget-exception operation with a receipt.
  • Release qualification measures baseline startup tokens and latency and lowers warning thresholds when evidence permits.

Agent activation

Managed admission freezes one activation:

interface AgentActivationV1 {
  readonly schemaVersion: "maia.agent-activation.v1";
  readonly activationId: string;
  readonly attemptId: string;
  readonly agentSessionId: string;
  readonly actorId: string;
  readonly projectSnapshotId: string;
  readonly projectSnapshotRevision: number;
  readonly policyRevision: number;
  readonly expectedRevision: number;
  readonly agentSnapshotDigest: string;
  readonly providerConnectionId: string;
  readonly providerCapabilityEvidenceDigest: string;
  readonly modelId: string;
  readonly workspace: {
    readonly projectId: string;
    readonly root: string;
    readonly writableRoots: ReadonlyArray<string>;
  };
  readonly tools: ReadonlyArray<ResolvedToolGrant>;
  readonly executionProfile: ResolvedExecutionProfile | null;
  readonly peerPolicy: ResolvedPeerPolicy;
  readonly contextManifest: ContextManifestV1;
  readonly history: ReadonlyArray<RedactedHistoryItem>;
  readonly instruction: string;
  readonly wakeCause:
    | { readonly type: "human"; readonly messageId: string }
    | { readonly type: "peer"; readonly messageId: string }
    | { readonly type: "routine"; readonly tickId: string }
    | { readonly type: "recovery"; readonly priorAttemptId: string };
  readonly reservationId: string;
  readonly assignmentEpoch: number;
  readonly workerProtocolVersion: "maia.worker/v1";
  readonly idempotencyKey: string;
  readonly deadline: string | null;
}
  • The nested types are closed Protocol schemas, not local TypeScript types.
  • The activation is canonical JSON. Its digest is recorded on the attempt, worker handshake, terminal event and receipt. A retry creates a new attempt and assignment epoch but keeps the activation and command lineage.
  • Credentials travel only in a single-activation envelope over the private worker channel. They never appear in the manifest, transcript, prompt, event, log or receipt.
  • run_command and the process tools are absent unless an enforceable execution profile is resolved. Pi is not a sandbox. If Node cannot enforce the profile, activation fails. Unsandboxed local execution is an operator-only mode that Project files, Skills or prompts cannot select.

Agent interfaces

Three surfaces stay separate even when they project the same operation (CLI reference):

  • Agent CLI: maia agent ... from a shell. Flags configure one invocation; they never become model tools or prompt text. Durable Sessions use maia session ... through Node.
  • Interactive commands: /command lines typed by an attached human, parsed before chat input. They are never sent to the model as prose, and the model cannot call them. A command that projects an operation builds its own actor-bound request (command list).
  • Model tools: JSON Schemas registered for one activation, callable only by the LLM (catalogue).

Tool registration follows one sequence:

Agent tool grants -> Project/operator narrowing -> adapter and execution-profile availability
-> exact operation authorization -> tool schema set in the manifest digest -> Pi model request
  • Project text, AGENTS.md, Skills, prompts and tool output cannot add a tool or widen a grant. An unavailable tool is omitted, not described.
  • Every tool action maps to exactly one registry operation, except maia__tool, whose admitted target risk class selects its read or mutation operation (D-172). Local mode uses an in-process implementation of the same authorization, revision, idempotency, result and receipt contracts. Consequential operations keep preview and confirmation.
  • Target results normalize to maia.tool-result.v1; today's Pi details objects are not stable schemas. The target replaces bash with the bounded argv run_command. Web, MCP and memory adapters are absent by default.

Client attachment is not a Session state (attachments):

  • /detach, terminal EOF or transport loss closes only that attachment. The Session continues headless; stopping work needs session.stop.
  • Reconnect opens a new attachment from the last acknowledged cursor; it never creates another Session.
  • v1 has no maia session detach <id>. Automation uses session show, session events list and cursors.
  • /detach exists only in a managed attached renderer, not in local maia agent.
  • Client detachment is unrelated to detach-to-project, which changes a parent/child relation under Swarm policy.

External context and tool adapters

Web, MCP and memory adapters (R11 AG3) are absent by default. S71 owns tool-server admission, credentials and transport; these adapters project its admitted servers and never run a second MCP broker. Configuration outside the prompt resolves each into an admitted grant:

interface ExternalAdapterGrantV1 {
  readonly adapterId: string;
  readonly kind: "web" | "mcp" | "memory";
  readonly revision: number;
  readonly ownerActorId: string;
  readonly projectId: string;
  readonly operationIds: ReadonlyArray<string>;
  readonly schemaDigest: string;
  readonly riskPolicyDigest: string;
  readonly dataClasses: ReadonlyArray<string>;
  readonly networkAllowlist: ReadonlyArray<string>;
  readonly maxRequestBytes: number;
  readonly maxResponseBytes: number;
  readonly timeoutMs: number;
  readonly expiresAt: string | null;
}
  • MCP supplies tools and context only (decision). An MCP entry also pins server identity, transport, tool-name allowlist, each tool schema digest, authentication secret reference, upstream idempotency support, mutation class and response redaction. Schema or server drift removes the tool until re-admission.
  • OAuth and API secrets stay in SecretStore. Prompts and tool arguments never contain credential locators.
  • web_fetch rejects non-HTTP(S) URLs, credentials in URLs, and loopback, link-local, private, metadata or disallowed addresses before and after every DNS resolution and redirect. It enforces host allowlists, response type, size and time limits, and DNS-rebinding protection.
  • Memory reads need exact actor and owner scope, classification access, provenance, retention state and bounded content. A Project cannot discover siblings; a Workspace cannot discover other Workspaces.
  • The memory adapter is a read projection only; R4 records and their write path belong to the coordinator learning contract. Its Project-only schema must be extended explicitly before Workspace reads.
  • All returned material is untrusted data and cannot authorize a follow-up operation.

Services, state and source map

Kernel services are Effect services with Layer, Scope, typed errors and interruption: AgentDefinitionResolver, ProjectInstructionResolver, SkillIndex, SkillLoader, ContextCompiler, ContextAuditor, ToolRegistry, ToolAuthorization, ModelProviderAdapter, PeerAgentPort, PiSessionAdapter and AgentEventSink. Pi SDK and native APIs are wrapped at the smallest boundary.

State:

  • Node's SQLite state is authoritative for managed lifecycle. Its trace is append-only, bounded and redacted; transcript content stays in the agent's own session files and is read through its reader (D-168). The trace excludes credentials, authorization headers, environment dumps, hidden reasoning and raw audio.
  • Every managed Session streams through Node. The worker reports turn, tool and final-message transitions through its fenced control protocol and offers deltas, progress and debug records as bounded diagnostic batches. It never owns a client socket. Pressure may coalesce deltas with an explicit gap; final-message metadata, effect outcomes, usage and terminal state stay durable. Stream design: live Session item stream.

Source map (current folders; new code follows the source organization contract):

ConcernLocation
Local agent and Pi constructionpackages/agent/src/runner/, runtime/, resources/, extensions/, auth/, login/, home/
Managed workerpackages/agent/src/managed-worker/
Agent definitions and grantspackages/protocol/src/agent/, services/node/src/agent/
Pi Session adaptersservices/node/src/harness/pi-rpc/, pi-managed/, shared scheduler in harness/conversation/
CLIapps/cli/src/ and the command tree in apps/cli/src/command-tree/

Each slice changes protocol, SDK, CLI, Node, worker, docs and contract tests together.

Roadmap and acceptance

Agent workRoadmap slice
Context manifests, context inspect/lintR1
Admitted definitions, prompts, Skills, resource reloadR11 AG1
Tool contract, shell/process profiles, Session work itemsR2, R11 AG2
Bounded recall, compaction, web/MCP/memory adaptersR11 AG3
Peer Sessions, owner-aware coordinators, ordinary Workspace SessionsR3, S17, S18
maia session attach rendererS12
Learned KB, memory and SkillsR4
Agent-callable evaluationR10a

Acceptance gates:

  • A useful local Agent runs with no Node and no ambient Pi files.
  • The same activation runs durably through Node without changing domain semantics.
  • Startup loads no Skill body and no nested instruction file unless needed.
  • Context inspection accounts for every source, byte, token estimate, reason, digest and trust class.
  • Context lint fails closed on hard budgets and conflicting instructions.
  • Every tool and command maps to one operation owner and one JSON contract.
  • Child Agents are normal Agent Sessions and cannot widen authority.
  • The terminal UI owns no durable state, orchestration, authorization or telemetry truth.
  • No Fleet, Workflow, Task-graph or Subagent runtime is required.

Terminal surfaces (S12)

Local maia agent keeps Pi's InteractiveMode, so extension commands, shortcuts, renderers and UI hooks work natively. Durable maia session attach is a narrower renderer over Node-owned state (decision). Neither is the product control plane.

  • The durable renderer is a PiTuiRenderer adapter over @earendil-works/pi-tui, pinned to the Pi SDK version.
  • That dependency lives only in apps/cli; Agent, Node, SDK and Protocol never import it. It is added only in the TUI slice.
  • The adapter is replaceable because the package is deprecated. A replacement changes rendering only and must pass the same renderer contract.

Screen:

Session <id>  Project <id>  state/turn/attempt  model  tokens/cost  evidence
----------------------------------------------------------------------------
causal timeline: messages, assistant deltas, tools, interactions, receipts
----------------------------------------------------------------------------
current activity: queue/worker/tool/effect/waiting reason and elapsed time
----------------------------------------------------------------------------
> input or /command                                      connection/health
  • No rooms, panels, graph editor, workflow builder, terminal emulator, file browser, fleet dashboard or configuration studio.
  • Tool results collapse to status, duration, bounded preview and artifact or receipt IDs. The user can expand one item without loading unbounded history.
  • evidence=live: timeline current through the shown watermark. evidence=lagging: shows record lag; input stays available only if Node policy permits. evidence=blocked: read-only, with the exact health diagnostic.
InputBehavior
text + Entersession.send with behavior reply and current expected revision
/detach or Ctrl-Dclose this attachment and return to the shell
/interruptpreview and apply session.interrupt for the current activation
/statusshow the exact Session, activation, attempt and evidence snapshot
/tracetoggle the bounded causal metadata view
/toolslist eligible tool schemas and current grants
/helpshow only commands available in this attachment mode
Ctrl-Ccancel input; twice within two seconds requests interrupt
terminal close/transport lossexpire the attachment; never stop or change the Session
  • Model or Connection changes apply to a future activation through a registered operation, never as renderer state.
  • The renderer acknowledges the highest fully rendered sequence every two seconds or 64 events. On overflow or cursor reset it discards its projection and requests a fresh snapshot. Heartbeat and buffer limits are in the attachment contract.
  • Output sanitation strips terminal control sequences from model, tool and provider content. Only renderer-owned ANSI sequences are emitted. Clipboard, OSC links, image protocols, mouse capture and escape passthrough are off in v1.

Acceptance:

  • attach during idle, running, waiting, blocked and terminal states;
  • show every turn boundary, tool and effect lifecycle, attempt change, receipt and evidence watermark without duplicates;
  • send, detach, reconnect from a cursor, forced cursor reset and slow-consumer disconnect;
  • lose terminal, network, worker and Node independently without stopping the Session or inventing state;
  • sanitize hostile ANSI/OSC content and enforce input and output bounds;
  • local InteractiveMode and PiTuiRenderer project the same normalized events for a shared fixture corpus.

Imported from Maia OS docs at 3dc3e07