Skip to content

Milpa

milpa/app-runtime

The agent runtime a Milpa app installs instead of copying.

What an agent is allowed to do inside your app, what your app knows how to do, and the two surfaces you drive it from — the CLI and the agent screen. All of it arrives by version.

Why this package exists

Because it used to live inside the template, and that meant it never reached anyone.

milpa/framework is type: project. When you run composer create-project, its src/ is copied into your app and from that moment it is yours. That is exactly right for the example plugin you are going to delete. It is exactly wrong for the agent runtime, which improves every week and which nobody ever edits.

The symptom that exposed it, measured: an app created one day earlier did not receive the permission-question buttons, or the indicator that pulses on every real event, or agent:board — even after updating everything. And the worst case was the quiet one: it did receive the new milpa/live-tui, which knows how to paint what the system said in a different colour from what the model said, and saw no change at all — because its copied screen never emitted the markers that trigger that painting. Half the improvement landed, half didn't, and nothing said so.

The rule that came out of it, and that this package applies: you copy what you are going to edit; you install what you are going to use. A template that copies files nobody will touch is a package in disguise — all of a package's cost, none of its benefit.

What's in it

The gates — what an agent may do

piece what it decides
SessionToolGate whether a call proceeds: permission, intent contract, sterile loop, ordering
SubAgentSpawner delegating to a child session and resuming it — with fresh context, not re-delegating
TreeBudget how many steps the tree spends, not each child: bounding the child does not bound the tree
SterileLoopGuard not repeating a call that already failed the same way twice — on by default: at its home tolerance it would have refused 81 of a sick run's 89 calls and none of the healthy runs'. Opt out with agent.sterileLoopGuard: false; an integer sets the tolerance
PrerequisiteGate an ordering obligation, executed: until the required thing runs, the rest does not. The system renews a session's standing obligation with a cheap read of its own state (agent_show) — orientation, not curation: a turn opened by bookkeeping becomes a bookkeeping turn (measured, twice). agent.renewalTool names another tool; false disables renewal, declared — never silent
SessionOptionTable withdrawing a tool from a session's catalogue — forbidding, not asking
BroadcastingEventStore · SurfaceBroadcaster · MercureBroadcaster getting what happens to the live surfaces while it happens
SessionBookkeeping · SessionPlanBoard the session's plan and to-dos, bound to its id

The operations — what your app knows how to do

AgentOperations, SessionOperations, CapabilityOperations and TokenOperations are the operation groups a Milpa app registers. They are returned, never self-registered: whoever assembles the registry decides which groups get in and with what authority, and a group that registered itself would take that decision away.

Containing what an agent may reach

An agent runs contained from the CLI, not only when a parent delegates to it. The withdrawal is a fact of the session, recorded in its stream — not a sentence in the prompt asking nicely:

# by name, when you know exactly which tools to take away
php coa agent "review this app and report" --session=review --deny=plugins:enable,make

# by effect class, which covers what a list of names forgets
php coa agent "review this app and report" --session=review --denyEffects=mutating

Classes are mutating, external, irreversible and authority, resolved against the live catalogue — an operation added tomorrow is covered the day it exists. An operation that never declared its effects is denied, not waved through: unknown ranks above known-bad, so a catalogue nobody classified withdraws entirely, and when that happens the command refuses and says so rather than handing back a mute agent.

--deny needs --session: the option table lives in the session, and a prohibition that cannot be recorded would not survive the first step.

Why a class and not a list: a measurement (settlement-q-p20p.md) put an agent under a task it could not finish without mutating, took five tools away by name, and watched it reach for a sixth that mutates — three times out of three. The list is worth exactly what whoever wrote it remembered.

The surfaces — where you drive it from

Console\Application is the single door of the CLI: coa on its own, a named command, the TUI, a one-shot chat. Tui\AgentScreen renders the agent screen as text — the actor markers travel inside the text, so a painter can colour by origin and the same screen still works where there is no colour.

Web\BoardPage renders the session's work as a live Kanban board in a browser: four columns, and exactly one write — answering the question that paused the session, through two buttons born disabled. They arm only when a token with the agent:answer scope is pasted; the token travels in the Authorization header — never in a URL, never in browser storage — and the server refuses any caller without a verified actor, showing the refusal verbatim. The page never folds the stream client-side — the fold is agent:board, shared with the CLI — and when the live bridge pushes a fact the page repaints the activity line and fetches the fold again, so reconnecting is catching up. A card born already done is set apart, never animated as if it had crossed; a card held by an open question sits in blocked saying why. Serve agent:board and agent:answer over HTTP (config/http.php), point the page at your Mercure hub, and with no hub it says so instead of pretending to be live.

Growing the app — capabilities, capabilities:refresh, capabilities:enable

The capability→package index is derived from what the registry publishes, never written by hand: every announcing package declares "type": "milpa-capability" on Packagist with its full contract (extra.milpa.capability), and capabilities:refresh turns that into a dated artifact under var/. Three authorities answer «what exists» and the rank is executed, not implied: installed.json (what IS) over the derived index (what EXISTS, dated) over a small offline floor — and every answer names which one it used. After capabilities:enable installs, what the registry promised is compared with what arrived, and any difference is recorded: a package's declaration about itself is a claim, not a classification.

Most of these exist because a measurement said they were needed, not because they seemed like a good idea. The settlements live in the monorepo (docs/library/settlement-q-*.md) and the docblocks cite which one.

Install

composer require milpa/app-runtime

A host composes it: this package boots nothing on its own and knows nothing about your app. It receives the session store, the operation catalogue and the model credential from whoever builds it — which is whoever holds the kernel.

Optional packages widen what it offers, and their absence is handled rather than assumed: milpa/auth for token verification, milpa/data for persisting them, milpa/devtools for coa doctor, coa repair and coa update. Without them those surfaces are simply not offered — the app never promises what it cannot do.

License

Apache-2.0 · © Rodrigo Vicente — TeamX Agency


Milpa is designed, built, and maintained by Rodrigo Vicente - TeamX Agency.

About

The agent runtime a Milpa app installs instead of copying: session gate, sub-agent delegation, tree budget and the live bridge.

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages