okami.mom/albedo

git clone https://git.nkp.pet/okami.mom/albedo

nekomimi.petsession: publish text and thinking deltas once a frame, cheaper per-delta bookkeeping, replay window fits one response6b74a53

main
6.4 KiB92 linesraw

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 mail.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=False means 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, marked unreviewed.
  • 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, 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.
  • 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],
10                              deliverable="notes/wakes.md")   # returns at once
11await mail.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
24  preparation with an empty transcript; neither observation proves spawn failed.
25  End 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.