-
Notifications
You must be signed in to change notification settings - Fork 2
Host integration
The host owns storage, transports, model clients, HTTP routes, authorization, rendering, and the choice of async executor. tinyhivemind owns none of that and never will. What it asks for is four ports, and three of them are optional.
Required for anything in the runtime crate.
pub trait SessionLog: Send + Sync {
fn read_before(&self, before: Option<Sequence>, limit: usize) -> SessionFuture<'_>;
}Contract:
- return rows newest first;
- treat
beforeas an exclusive sequence bound; - honour
limit; - keep sequences unique.
The trait is object safe and returns a boxed future, so it picks no executor for you. Violating the ordering, uniqueness, size, or cursor contract produces a typed page-validation error rather than a plausible-looking history, which is deliberate: a silently wrong transcript is worse than a failed turn.
Optional. Needed only if some desk uses ResponderMode::Auto.
pub trait Selector: Send + Sync {
fn select<'a>(&'a self, request: &'a SelectionRequest) -> SelectorFuture<'a>;
}It receives a raw message, a canonical desk id, and a bounded candidate list.
No transcript, no tools, no host handles. It returns text intended to name one
candidate id, and accept_selection accepts it only if it does.
A selector that is unavailable, disabled, or ambiguous falls back to the deterministic first effective member. Nothing breaks when the model is down.
See Responder ladder.
Optional. Needed only if you enable mention dispatch.
pub trait MentionTurnQueue: Send + Sync {
fn enqueue_once(&self, request: MentionTurnRequest) -> MentionTurnFuture<'_>;
}This is the only idempotency
boundary in the system, and the contract is the strictest of the three. One
atomic transaction keyed by the bound conversation plus
request.key.trigger_sequence. Inside it, re-read the stored committed agent
reply and verify its source, content, conversation and sequence; revalidate
live feature policy, authorization and target availability; then durably
enqueue at most one child turn. Return Already for a duplicate and Refused
for an expected live rejection. A rollback must leave neither an idempotency
record nor a child turn.
The library owns no journal and does not retry a failure or a refusal.
Optional, and needed only if you enable cross-desk referral.
pub trait ReferralQueue: Send + Sync {
fn enqueue_once(&self, referral: Referral) -> ReferralFuture<'_>;
}Everything MentionTurnQueue requires, plus one thing that follows from the
only difference: the child turn may run on a different conversation from the
one that triggered it. A Referral therefore names two — from, where the
trigger was committed, and to, where the child turn runs — and the
idempotency key is referral.from plus referral.key.trigger_sequence.
Revalidate authorization and target availability on both conversations. A crossing referral writes into a channel its author is not a member of, so a transaction that authorizes only the source desk has authorized nothing.
One more obligation is yours and has no equivalent in the other ports: when you
run a child turn that carried a ReferralOrigin, pass that origin back in the
next ReferralInput, or the answer has no way home. The library does not
remember it.
Prompting. The trace grammar is a parser, not a teacher. Live runs showed
that models split a topic across two names, restate their own last line,
!commit during deliberation, and drop the # sigil. Every one of those is
fixed in the prompt, and each fix is a host obligation. See
Trace grammar for the four and what to do about them, and the
taxonomy of multi-agent failures for how
ordinary they are.
Two more, from the federated live runs, if you enable referral. A move that is
not in the list of moves is not offered: two runs and fifty turns produced
zero cross-channel messages while @#deskid was explained above the marker
list rather than placed in it. And an answering turn needs the answerer's
own private facts — without them the only thing a desk can send across a
channel is its opinion, and in one run a desk exported its wrong hypothesis to
the one desk that had been reasoning correctly.
Committing state. EpisodeState and SharingState are returned, never
applied. Commit after the turn is durably appended, so a crash leaves the
watermark behind rather than ahead.
Policy. Hop limits, quorum thresholds, turn budgets, and whether mention dispatch is on at all are yours. The library ships conservative defaults and adds no hidden ceiling of its own.
Breadth, where depth is not enough. max_hops bounds how deep a referral
chain goes. Nothing bounds how many channels one desk may ask, because only you
know what a question costs you. The benchmark's host caps it at one question per
peer desk, and that cap is the host's, not the library's.
Timing, when a room can decide before an answer lands. A desk whose members share a blind spot reaches quorum inside its own blind opening round, so a cross-channel answer arriving after that is information the desk has already voted past. Whether your agents ask before or after backing something is the single largest effect the federated benchmark measured, and it is entirely yours. See Cross-desk referral.
- A message lands. You journal it.
-
resolvereads its mentions against the current roster and desk set. -
responder_planreturns a decision, or a selector request with a deterministic fallback. - If it returned a request, call your
Selectoronce and run the output throughaccept_selection. -
prepare_deltatells you what the chosen agent has not yet seen, or asks for a full re-initialization with a named reason. - The agent takes its turn. You append the reply.
- Commit the sharing state.
- If mention dispatch is enabled,
dispatch_mentiondecides whether that reply may enqueue exactly one more turn, and calls your queue at most once.
For a deliberating room, steps 3 and 5 are replaced by a single step call
that returns the authorized turn along with the state to commit at step 7. See
Hive episodes.
Every crate is publish = false. You pin the repository as a submodule and
take the crates as path dependencies, so the pinned commit is the version.
Work lands here first, then the pointer bump lands in your repository. A submodule cannot point at an unmerged commit, which is what makes the dependency direction self-enforcing.
tinyhivemind is GPL-3.0-only. Built by @senamakel.
Start here
The algebra
- Shared medium
- Desks and rosters
- Mentions
- Cross-desk referral
- Transcript projection
- Threads
- Recall
- Responder ladder
Hive mechanics
Working on it
Reference