okami.mom/albedo
git clone https://git.nkp.pet/okami.mom/albedo
6b74a53
main
agents
a subagent is a session with a parent. a message is mail. both are daemon primitives; the mail and agents extensions (on by default) give the model its python surface.
python
m = await agents .models ()# required once before spawning kid = await agents .self .spawn ("map every wake path" ,name = "scout" ,model = m [0 ],deliverable = "notes/wakes.md" )# returns at once await submit (kid ,"also cover schedules" )# or "parent", a name, a session id kids = await agents .self .children ()# snapshots: running, closed await kid .cancel ()# stop its turn, keep everything await kid .cancel (tree = True )# ...and the turns of everything beneath it await agents .cancel_all ()# every child and descendant (tree=True by default) await kid .close ()# done: keep messages + files, free the kernel other = await agents .get ("a1b2c3" )# "parent", a family name, or any session id page = await other .messages (seq = 0 )# page of its rows: content, next_offset hits = await kid .search_messages ("error" )# rows by seq, with previews await agents .progress ("on turn.gleam now" )# shows in /agents, starts no turn
running=Falsemeans no active turn. A new child can be waiting for kernel preparation with an empty transcript; neither observation proves spawn failed. End the parent turn and wait for mail instead of cancelling on that snapshot.- spawn never returns the child's answer: the answer arrives later as
<mail>and starts the parent's next turn. a child whose turn ends without answering forwards its last message, markedunreviewed. - names resolve inside the family (children, siblings, parent); anything else takes a session id.
- any session may look up and read any other:
get,messages, andsearch_messagesresolve names and ids the way mail does, and closed agents stay readable. cancel and close work on your own children only.cancel(tree=True)andcancel_all()stop parents before their children, so a stopped agent cannot answer by spawning, and answer the sessions that were running; they never close or delete anything. the model cannot delete an agent; it asks the user. - limits: 3 deep, 12 open children per parent, 1 MiB per letter, 1000 undelivered letters per session.
mail delivery
a letter is stored first and marked delivered in the same transaction that writes it into the recipient's transcript. an idle recipient accepts it for a turn (kernel preparation may still be pending); a running one reads it at its next step.
the Python receipt delivered means accepted by an idle recipient, not that a provider request has started; queued means it waits behind a turn, and pending means it is stored for retry. undelivered letters retry every 15 seconds, so a restart or a session that is not running yet loses nothing. Closed children keep their undelivered letters but do not run them; close also prevents pending kernel preparation from admitting them. webhook deliveries are letters from outside, and wait for an idle session.
the orchestrator view
ctrl+o or /agents in a session shows its whole tree live: running agents pulse with their token rate, mail travels the edges as blocks. tab picks an agent, enter opens it (typing there is you, as the user), text + enter sends to it, /spawn <name> <task> starts a child under it.
The selected agent's card shows its status, latest delivered task or message from its parent, and latest progress. Request and progress previews are bounded to 512 characters. A new parent request clears the previous progress; peer mail does not replace the request. Progress starts no turn and is replaced in place. Idle means idle, including after restart; it does not imply the task is complete. Narrow terminals show the card below the graph. Mail animation deduplicates IDs with a bounded cache, while distinct messages with identical text remain visible.
The view reads GET /sessions?scope=family&root_id={root_id} and subscribes
to the same collection with Accept: text/event-stream. Child creation and
messages use the core session and input resources. The
HTTP contract
defines activity replacements, mail animation metadata, and invalidations.
The Go adapter decodes known events before delivery. Malformed known events
fail the stream; unknown event kinds are ignored. The daemon supplies bounded
tool progress under tool_progress: 1. Clients render it without reconstructing
arguments or guessing Python intent.
Stream overflow and refresh
The initial batch contains ready and reset controls. Subscriber byte and event limits apply before payloads enter mailboxes; wakes are coalesced. Publication does not wait for a client to read its socket.
Overflow discards queued deltas, emits an overflow control when the socket can accept it, and closes the stream. The client resubscribes and refreshes the authoritative family. That refresh replaces names, relationships, closed state, status, and activity. Replies from an older attachment cannot repopulate the view. Disconnect releases subscriber state without another publication. Drafts and pending action results survive recovery. Terminal failures remain visible; ctrl+l starts a fresh attachment.
swarm overhead
kernel boots queue behind four slots. each boot owns only its session's composition, and each kernel's host callback captures only its routes, store handle, and session id—not a snapshot of the other agents. run waits on process-exit notifications, not periodic exit checks; descendant cleanup and deadlines still apply.
session replay retains at most 256 events or 992 kib, evicting incrementally rather than copying the full window per token. missing events require a transcript reset. the orchestrator feed batches every 100 ms and skips activity serialization when nobody is watching.
run jobs start at once, at low priority (nice, and utility QoS on macOS), so a
busy swarm yields to the person at the machine. jobs never pause for admission,
and their deadlines count wall time after the program starts.
deletion failures
Direct parent deletion requires a successful child lookup. Tree deletion collects the child-first walk before deleting any session. A failed lookup returns an error and leaves the sessions intact. A failure during deletion still reports how many sessions were already deleted.
1# agents 2 3a subagent is a session with a parent. a message is mail. both are daemon primitives; the `mail` and `agents` extensions (on by default) give the model its python surface. 4 5## python 6 7``` python 8m = await agents . models () # required once before spawning 9kid = await agents . self . spawn ( "map every wake path" , name = "scout" , model = m [ 0 ], 10deliverable = "notes/wakes.md" ) # returns at once 11await . submit ( kid , "also cover schedules" ) # or "parent", a name, a session id 12kids = await agents . self . children () # snapshots: running, closed 13await kid . cancel () # stop its turn, keep everything 14await kid . cancel ( tree = True ) # ...and the turns of everything beneath it 15await agents . cancel_all () # every child and descendant (tree=True by default) 16await kid . close () # done: keep messages + files, free the kernel 17other = await agents . get ( "a1b2c3" ) # "parent", a family name, or any session id 18page = await other . messages ( seq = 0 ) # page of its rows: content, next_offset 19hits = await kid . search_messages ( "error" ) # rows by seq, with previews 20await agents . progress ( "on turn.gleam now" ) # shows in /agents, starts no turn 21``` 22 23- `running=False` means no active turn. A new child can be waiting for kernel 24preparation with an empty transcript; neither observation proves spawn failed. 25End the parent turn and wait for mail instead of cancelling on that snapshot. 26- spawn never returns the child's answer: the answer arrives later as `<mail>` and starts the parent's next turn. a child whose turn ends without answering forwards its last message, marked `unreviewed`. 27- names resolve inside the family (children, siblings, parent); anything else takes a session id. 28- any session may look up and read any other: `get`, `messages`, and `search_messages` resolve names and ids the way mail does, and closed agents stay readable. cancel and close work on your own children only. `cancel(tree=True)` and `cancel_all()` stop parents before their children, so a stopped agent cannot answer by spawning, and answer the sessions that were running; they never close or delete anything. the model cannot delete an agent; it asks the user. 29- limits: 3 deep, 12 open children per parent, 1 MiB per letter, 1000 undelivered letters per session. 30 31## mail delivery 32 33a letter is stored first and marked delivered in the same transaction that writes it into the recipient's transcript. an idle recipient accepts it for a turn (kernel preparation may still be pending); a running one reads it at its next step. 34 35the Python receipt `delivered` means accepted by an idle recipient, not that a provider request has started; `queued` means it waits behind a turn, and `pending` means it is stored for retry. undelivered letters retry every 15 seconds, so a restart or a session that is not running yet loses nothing. Closed children keep their undelivered letters but do not run them; close also prevents pending kernel preparation from admitting them. webhook deliveries are letters from outside, and wait for an idle session. 36 37## the orchestrator view 38 39`ctrl+o` or `/agents` in a session shows its whole tree live: running agents pulse with their token rate, mail travels the edges as blocks. tab picks an agent, enter opens it (typing there is you, as the user), text + enter sends to it, `/spawn <name> <task>` starts a child under it. 40 41The selected agent's card shows its status, latest delivered task or message 42from its parent, and latest progress. Request and progress previews are bounded 43to 512 characters. A new parent request clears the previous progress; peer mail 44does not replace the request. Progress starts no turn and is replaced in place. 45Idle means idle, including after restart; it does not imply the task is complete. 46Narrow terminals show the card below the graph. Mail animation deduplicates IDs 47with a bounded cache, while distinct messages with identical text remain visible. 48 49The view reads `GET /sessions?scope=family&root_id={root_id}` and subscribes 50to the same collection with `Accept: text/event-stream`. Child creation and 51messages use the core session and input resources. The 52[HTTP contract](../docs/http-api-design.md#collection-watch-and-overload) 53defines activity replacements, mail animation metadata, and invalidations. 54 55The Go adapter decodes known events before delivery. Malformed known events 56fail the stream; unknown event kinds are ignored. The daemon supplies bounded 57tool progress under `tool_progress: 1`. Clients render it without reconstructing 58arguments or guessing Python intent. 59 60## Stream overflow and refresh 61 62The initial batch contains ready and reset controls. Subscriber byte and event 63limits apply before payloads enter mailboxes; wakes are coalesced. Publication 64does not wait for a client to read its socket. 65 66Overflow discards queued deltas, emits an overflow control when the socket can 67accept it, and closes the stream. The client resubscribes and refreshes the 68authoritative family. That refresh replaces names, relationships, closed state, 69status, and activity. Replies from an older attachment cannot repopulate the 70view. Disconnect releases subscriber state without another publication. 71Drafts and pending action results survive recovery. Terminal failures remain 72visible; ctrl+l starts a fresh attachment. 73 74## swarm overhead 75 76kernel boots queue behind four slots. each boot owns only its session's composition, 77and each kernel's host callback captures only its routes, store handle, and session 78id—not a snapshot of the other agents. run waits on process-exit notifications, 79not periodic exit checks; descendant cleanup and deadlines still apply. 80 81session replay retains at most 256 events or 992 kib, evicting incrementally rather 82than copying the full window per token. missing events require a transcript reset. 83the orchestrator feed batches every 100 ms and skips activity serialization when 84nobody is watching. 85 86run jobs start at once, at low priority (`nice`, and utility QoS on macOS), so a 87busy swarm yields to the person at the machine. jobs never pause for admission, 88and their deadlines count wall time after the program starts. 89 90## deletion failures 91 92Direct parent deletion requires a successful child lookup. Tree deletion collects the child-first walk before deleting any session. A failed lookup returns an error and leaves the sessions intact. A failure during deletion still reports how many sessions were already deleted.