Python API reference¶
Everything public lives on the top-level synapse_channel package surface (70
exported names). This page starts with the handful you actually call, then the
full generated reference follows. For the compatibility promise on every symbol
here, see API and wire stability.
Two entry points¶
Almost every integration uses one of two classes:
SynapseAgent— the client. Connect an agent to a running hub, then issue coordination verbs (claim, release, task updates, messaging, checkpoints). This is what most callers reach for.SynapseHub— the hub itself. Run the authoritative coordination process, usually from thesynapse hubCLI, but embeddable in-process for tests or bundled deployments. Configure it withHubConfig.
from synapse_channel import SynapseAgent, SynapseHub, HubConfig
The client in one flow¶
A minimal SynapseAgent session — connect, wait for the hub's welcome, claim a
file scope, update the task, and release — is the whole daily loop in code:
import asyncio
from synapse_channel import SynapseAgent
async def main() -> None:
agent = SynapseAgent("ALPHA", uri="ws://localhost:8876")
session = asyncio.create_task(agent.connect()) # one long-lived session
# Wait for the hub's welcome before issuing verbs; fail loudly if it is down.
if not await agent.wait_until_ready():
raise RuntimeError("could not reach the hub — is `synapse hub` running?")
# The hub refuses a request that overlaps another live file-scope claim.
await agent.claim("refactor-parser", note="splitting the tokenizer", paths=["src/parser"])
await agent.save_checkpoint("refactor-parser", "step=2")
await agent.update_task("refactor-parser", status="working")
await agent.release("refactor-parser")
agent.running = False
session.cancel()
asyncio.run(main())
Pass on_message_callback= to SynapseAgent(...) to react to inbound frames
(chat, task events, release grants). The full worked example — with an event
callback that waits on checkpoint and release confirmations — is in the
quick start.
The verbs you will use most¶
Grouped by what they coordinate (all are async methods on SynapseAgent):
- Work claims —
claim(task_id, paths=..., note=...)andrelease(task_id): file-scope mutual exclusion, the one thing that gates a mutation. - Task lifecycle —
update_task(task_id, status=...)drives the typed task state on the shared blackboard. - Checkpoints —
save_checkpoint(task_id, data)records resumable progress that survives a restart. - Messaging — send to everyone, a named group (
A,B), or one agent; an idle agent catches up from its durable inbox on reconnect.
For the exact signatures of every method, read the generated reference below.
Embedding a hub¶
To run the hub in-process (tests, a bundled tool), construct SynapseHub from a
HubConfig. HubConfig().to_kwargs() maps one-to-one onto the SynapseHub
constructor — a contract the test suite enforces — so config built one way is
always accepted by the hub.
from synapse_channel import SynapseHub, HubConfig
hub = SynapseHub(**HubConfig().to_kwargs())
Supporting surfaces¶
The remaining exports fall into a few families you reach for as needed:
- Model workers —
SynapseLLMWorker,OpenAIChatClient,TieredChatClient, and the offlineRuleBasedClientlet agents reply on-channel through any OpenAI-compatible endpoint with a deterministic fallback. - Team helpers —
plan_team(...)/run_team(...)script a small fleet. - Coordination primitives —
Blackboard,EventStore,TaskClaim,TaskStatus,MessageType, and the*Configtypes. - Pure predicates —
paths_overlap,scopes_conflict,would_create_cycle,is_directed,is_recipient, and friends: no I/O, safe to call anywhere.
Everything is re-exported from the package root, so from synapse_channel import X
works for any name below.
Full generated reference¶
The reference below is generated from the source docstrings for every public symbol.
synapse_channel
¶
SYNAPSE CHANNEL — local-first multi-agent coordination bus.
A small WebSocket fabric that lets several agents share presence, claim and
release units of work, chat, and advertise resources through one authoritative
hub. The pieces compose: :class:~synapse_channel.core.hub.SynapseHub routes,
:class:~synapse_channel.client.agent.SynapseAgent connects, and
:class:~synapse_channel.client.llm_worker.SynapseLLMWorker answers on-channel through a
pluggable :mod:~synapse_channel.client.chat_backends backend. The synapse console
command (see :mod:synapse_channel.cli) drives all of it.
The public names below resolve lazily (:pep:562): the submodule behind a name
is imported on first attribute access, so import synapse_channel stays
cheap for consumers that touch only a slice of the surface — the CLI in
particular no longer pays for the whole facade at start-up.
DEFAULT_HUB_URI = 'ws://localhost:8876'
module-attribute
¶
Default hub URI; matches the hub's default bind port.
HUB_URI_ENV_VAR = 'SYNAPSE_URI'
module-attribute
¶
Environment variable that overrides the default hub URI for the CLI.
MEMORY_KINDS = frozenset({EventKind.RECALL, EventKind.FINDING, EventKind.CHECKPOINT, EventKind.HANDOFF})
module-attribute
¶
The durable event kinds the persistent-memory read-side ingests.
The query-stream (recall), the authored atoms (finding), and the
highest-signal episodic state (checkpoint/handoff) — the subset of the
log a downstream memory adapter reads through the seq-cursored ingest seam. The
pure coordination kinds (claim/release/task_update/resource/the
ledger kinds) are excluded; chat is filtered read-side, not here.
PRIORITY_SENDERS = frozenset({'CEO'})
module-attribute
¶
Senders whose message wakes a directed-only waiter even on a broadcast.
The CEO command session directs the fleet; a broadcast from it is never merely routine peer chatter, so it must reach a quiet waiter promptly.
SynapseAgent
¶
Bases: AgentLifecycleMixin, AgentDispatchMixin, AgentOutboundMixin, AgentQueryMixin
An async client that maintains one connection to the Synapse hub.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
Unique agent name presented to the hub. |
required |
on_message_callback
|
MessageCallback or None
|
Preferred: an |
None
|
uri
|
str
|
Hub WebSocket URI. Defaults to :data: |
DEFAULT_HUB_URI
|
heartbeat_interval
|
float
|
Seconds between keepalive heartbeats, clamped up to
:data: |
20.0
|
verbose
|
bool
|
When |
True
|
token
|
str or None
|
Shared-secret token presented on the registration message when the hub
requires authentication. |
None
|
takeover
|
bool
|
When |
False
|
roles
|
tuple of str
|
Full |
()
|
mailbox
|
bool
|
When |
False
|
mailbox_since_seq
|
int
|
The durable journal |
0
|
mailbox_for
|
str
|
The identity whose backlog to replay, when it differs from |
''
|
mailbox_advance
|
Callable or None
|
Gate consulted before the mailbox cursor advances past a chat frame (and
before a live or replayed frame is acknowledged). A waiter that surfaces only a
FILTERED subset of frames passes its wake filter here, so a frame it will
never show cannot be silently consumed: an unadvanced cursor leaves the
frame pending and a later (or correctly bound) waiter still receives it
on replay. |
None
|
wake_capability
|
str
|
Receiver capability declared on the registration heartbeat. Ordinary agents
default to |
WAKE_DIRECT
|
request_lease
|
bool
|
When |
False
|
owner_lease
|
str
|
The lease token to present for the bound name, persisted from an earlier
grant (see :mod: |
''
|
on_lease_granted
|
Callable[[str], None] or None
|
Called with the token the moment the hub grants a lease, so the caller
can persist it before the process exits. |
None
|
machine_identity
|
bool
|
Present the zero-config trust-on-first-use machine key when no explicit
|
True
|
per_message_auth_key_id
|
str or None
|
Key id used to sign mutating frames with per-message authentication.
|
None
|
per_message_auth_secret
|
str or bytes or None
|
HMAC secret paired with |
None
|
capability_card_key_path
|
str or None
|
Owner-only Ed25519 PEM used only to sign capability advertisements. |
None
|
capability_card_key_id
|
str
|
Public id of |
''
|
capability_card_project
|
str
|
Optional assertion of the namespace prefix in |
''
|
capability_card_lifetime_seconds
|
float
|
Lifetime recorded in each signed advertisement. |
DEFAULT_CAPABILITY_CARD_LIFETIME_SECONDS
|
ping_interval
|
float
|
Seconds between client keepalive pings, so a half-open connection — a hub
that was killed, an ungraceful restart, or an eviction whose close frame
never arrived — is detected and :meth: |
20.0
|
ping_timeout
|
float
|
Seconds to wait for a ping reply before dropping the connection. Defaults
to |
20.0
|
mailbox_cursor
property
¶
Return the highest durable journal seq this agent has processed.
Starts at the seeded mailbox_since_seq and advances as the agent sees
chat frames, so a caller that persists it across reconnects — a waiter
re-armed as a fresh process — can seed the next agent's mailbox_since_seq
and resume the backlog from where this one stopped rather than from zero.
ChatBackend
¶
Bases: Protocol
Structural type for anything that generates a reply from two prompts.
generate(*, system_prompt, user_prompt)
¶
Return a reply for the given system and user prompts.
OpenAIChatClient
¶
Backend for any OpenAI-compatible /v1/chat/completions endpoint.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
api_key
|
str
|
Bearer token. Local servers (e.g. Ollama) accept any non-empty value. |
required |
model
|
str
|
Model identifier passed in the request body. |
required |
base_url
|
str
|
Base URL of the OpenAI-compatible API; a trailing slash is stripped.
Only |
required |
timeout_seconds
|
float
|
Per-request timeout, clamped up to |
required |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
generate(*, system_prompt, user_prompt)
¶
Request a completion and return the sanitised reply text.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
system_prompt
|
str
|
System role content steering the model. |
required |
user_prompt
|
str
|
User role content the model responds to. |
required |
Returns:
| Type | Description |
|---|---|
str
|
The assistant message content, whitespace-collapsed and truncated. |
Raises:
| Type | Description |
|---|---|
RuntimeError
|
On an HTTP error status, a connection failure, or a response whose shape does not contain the expected completion content. |
RuleBasedClient
¶
Deterministic offline backend that acknowledges receipt.
The reply carries no sender prefix: the wire envelope already records the author, so the hub and every reader render the name once.
generate(*, system_prompt, user_prompt)
¶
Return a fixed acknowledgement, ignoring both prompts.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
system_prompt
|
str
|
Unused; present to satisfy :class: |
required |
user_prompt
|
str
|
Unused; present to satisfy :class: |
required |
Returns:
| Type | Description |
|---|---|
str
|
A constant on-channel acknowledgement. |
SynapseLLMWorker
¶
A hub agent that answers addressed messages via a chat backend.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
Agent name presented on the channel. |
required |
uri
|
str
|
Hub URI. Defaults to :data: |
DEFAULT_HUB_URI
|
provider
|
str
|
Backend provider: |
'ollama'
|
model
|
str
|
Model identifier for HTTP providers. Defaults to |
'llama3'
|
base_url
|
str
|
OpenAI-compatible base URL. Defaults to the local Ollama endpoint. |
DEFAULT_OLLAMA_BASE_URL
|
api_key_env
|
str
|
Environment variable holding the API key. Defaults to |
'OPENAI_API_KEY'
|
max_context
|
int
|
Number of recent messages retained for prompt context (floored at 2). |
8
|
reply_target_mode
|
str
|
|
'all'
|
min_reply_interval
|
float
|
Minimum seconds between replies (floored at 0). Defaults to |
0.7
|
ready_timeout
|
float
|
Seconds to wait for the hub handshake in :meth: |
5.0
|
token
|
str or None
|
Shared-secret token presented to a hub that requires authentication;
|
None
|
task_classes
|
tuple[str, ...] or list[str]
|
Routing classes this worker advertises on its capability card; defaults
to |
('chat',)
|
heavy_model
|
str
|
Model used for the |
''
|
capability_card_key_path
|
str
|
Separate Ed25519 card-signing credential and project binding. All are opt-in; unsigned advisory discovery remains the default. |
None
|
capability_card_key_id
|
str
|
Separate Ed25519 card-signing credential and project binding. All are opt-in; unsigned advisory discovery remains the default. |
None
|
capability_card_project
|
str
|
Separate Ed25519 card-signing credential and project binding. All are opt-in; unsigned advisory discovery remains the default. |
None
|
capability_card_lifetime_seconds
|
float
|
Lifetime recorded in each signed advertisement. |
DEFAULT_CAPABILITY_CARD_LIFETIME_SECONDS
|
on_message(data)
async
¶
Filter an inbound message and queue it when a reply is warranted.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
dict[str, Any]
|
A decoded inbound message envelope. |
required |
run()
async
¶
Connect, wait for the handshake, and run the worker loop.
The connection and worker tasks run concurrently; when either finishes the other is cancelled and any terminal error is reported.
TaskClass
¶
The coarse routing classes a request can fall into.
TieredChatClient
¶
A chat backend that routes :meth:generate to a per-class backend.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
backends
|
Mapping[str, ChatBackend]
|
One backend per task class. The |
required |
default_class
|
str
|
Class used when the classifier picks one with no registered backend.
Defaults to :attr: |
SLM
|
classifier
|
Callable[[str], str]
|
The prompt classifier; defaults to :func: |
classify
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
route(prompt)
¶
Return the task class :func:classify assigns to prompt.
generate(*, system_prompt, user_prompt)
¶
Classify user_prompt and delegate to the matching backend.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
system_prompt
|
str
|
System prompt forwarded to the chosen backend. |
required |
user_prompt
|
str
|
User prompt, both classified and forwarded. |
required |
Returns:
| Type | Description |
|---|---|
str
|
The chosen backend's reply. |
Intervention
dataclass
¶
A single action the supervisor decided to take on a task.
Attributes:
| Name | Type | Description |
|---|---|---|
task_id |
str
|
The task the intervention concerns. |
action |
str
|
What to do; currently always |
reason |
str
|
Human-readable explanation, recorded with the re-offer. |
StallPolicy
dataclass
¶
Operator-tunable policy for stall detection.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
idle_seconds
|
float
|
Fixed no-activity ceiling for in-progress tasks. Values below one second are clamped to one second. |
DEFAULT_IDLE_SECONDS
|
predictive
|
bool
|
Whether completed-task history may lower the effective in-progress
threshold. Defaults to |
True
|
history_multiplier
|
float
|
Multiplier applied to the median historical activity gap. Values below one are clamped to one. |
DEFAULT_HISTORY_MULTIPLIER
|
min_history_samples
|
int
|
Minimum number of historical gaps required before prediction is used. Values below one are clamped to one. |
DEFAULT_MIN_HISTORY_SAMPLES
|
min_predictive_idle_seconds
|
float
|
Floor for the predictive threshold. Values below one second are clamped to one second. |
DEFAULT_MIN_PREDICTIVE_IDLE_SECONDS
|
history_task_limit
|
int
|
Number of recent terminal tasks to inspect for cadence. Values below one are clamped to one. |
DEFAULT_HISTORY_TASK_LIMIT
|
__post_init__()
¶
Clamp policy values so the detector has no invalid runtime states.
SupervisorWorker
¶
An on-channel agent that polls the board and re-offers stalled tasks.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
Agent name presented on the channel. Defaults to |
'SUPERVISOR'
|
uri
|
str
|
Hub URI. Defaults to :data: |
DEFAULT_HUB_URI
|
idle_seconds
|
float
|
Fixed no-activity ceiling passed to :func: |
DEFAULT_IDLE_SECONDS
|
predictive_stall
|
bool
|
Whether completed-task history may lower the effective no-activity threshold. |
True
|
history_multiplier
|
float
|
Multiplier applied to the historical median activity gap. |
DEFAULT_HISTORY_MULTIPLIER
|
min_history_samples
|
int
|
Minimum historical gaps required before prediction is used. |
DEFAULT_MIN_HISTORY_SAMPLES
|
min_predictive_idle_seconds
|
float
|
Floor for the predictive threshold. |
DEFAULT_MIN_PREDICTIVE_IDLE_SECONDS
|
interval
|
float
|
Seconds between passes (floored at 1). |
DEFAULT_INTERVAL_SECONDS
|
settle_seconds
|
float
|
Pause after requesting the board to let the snapshot arrive. |
DEFAULT_SETTLE_SECONDS
|
ready_timeout
|
float
|
Seconds to wait for the hub handshake in :meth: |
5.0
|
token
|
str or None
|
Shared-secret token for a secured hub. |
None
|
clock
|
Callable[[], float]
|
Wall-clock source; injectable for deterministic tests. |
time
|
on_message(data)
async
¶
Capture the latest board snapshot the hub sends back.
evaluate_and_apply()
async
¶
Run the stall policy on the latest board and apply each re-offer.
Returns:
| Type | Description |
|---|---|
list[Intervention]
|
The interventions applied (empty when no board has arrived yet or nothing is stalled). |
run()
async
¶
Connect, wait for the handshake, and supervise until the link ends.
TokenAuthenticator
¶
Validates a shared-secret token, optionally bound to agent names.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
tokens
|
Mapping[str, Iterable[str]] or Iterable[str]
|
Either a mapping of |
required |
Notes
An authenticator constructed with no usable tokens denies every connection;
pass None to :class:~synapse_channel.core.hub.SynapseHub to leave the hub
open instead.
is_empty
property
¶
Whether no usable token is configured (so every connection is denied).
authenticate(token, agent)
¶
Check a presented token for a connecting agent.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
token
|
str
|
The secret the agent presented; an empty value is always refused. |
required |
agent
|
str
|
The agent name the connection claims, checked against any name binding on the matched token. |
required |
Returns:
| Type | Description |
|---|---|
tuple[bool, str]
|
|
authenticate_with_principal(token, agent)
¶
Authenticate and return the stable quota bucket for the credential.
The principal is a domain-separated SHA-256 fingerprint of the matched connect token. It is deliberately not the asserted agent name: every name admitted by one credential shares one claim budget, so reconnecting under aliases cannot multiply the quota. The raw token is never returned or logged. Callers must keep the fingerprint internal; it is persisted only in private claim snapshots so a restart preserves the same budget.
Returns:
| Type | Description |
|---|---|
tuple[bool, str, str or None]
|
Authentication verdict, human-readable reason, and the stable quota
principal on success. Refusals always carry |
CapabilityCard
dataclass
¶
A small, A2A-shaped description an agent advertises about itself.
Attributes:
| Name | Type | Description |
|---|---|---|
agent |
str
|
Name of the advertising agent. |
description |
str
|
Free-form summary of what the agent does. |
skills |
tuple[str, ...]
|
Capability tags the agent claims (free-form). |
task_classes |
tuple[str, ...]
|
Routing classes the agent can take (e.g. |
model |
str
|
Optional model identifier backing the agent. |
project |
str
|
Hub-resolved project namespace bound into a signed card. |
manifest_digest |
str
|
Optional digest of a package/tool manifest this advertisement describes. |
contracts |
tuple[CapabilityContract, ...]
|
Declarative input/output contracts keyed by task class. |
meta |
dict[str, Any]
|
Arbitrary descriptive metadata. |
signature |
dict[str, Any]
|
Optional Ed25519 card-signature envelope. |
verification |
CapabilityCardVerification
|
Explicit advisory verification result; never an execution grant. |
advertised_at |
float
|
Wall-clock time, in seconds, when the card was last refreshed. |
as_dict()
¶
Return a JSON-serialisable snapshot of this card.
CapabilityRegistry
¶
One capability card per agent, exposed as a queryable manifest.
The registry is single-threaded and synchronous; the hub owns one instance.
Live cards are kept fresh by re-advertising and are dropped on disconnect or
when they pass the soft TTL; persistent dispatch registrations
(:meth:advertise_persistent) survive disconnects and expire only when not
refreshed within persistent_ttl_seconds.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
ttl_seconds
|
float
|
Liveness window after which an un-refreshed card is expired. Defaults to
:data: |
DEFAULT_CARD_TTL_SECONDS
|
persistent_ttl_seconds
|
float
|
Refresh window for persistent dispatch registrations. Defaults to
:data: |
PERSISTENT_CARD_TTL_SECONDS
|
advertise(agent, *, description='', skills=(), task_classes=(), model='', project='', manifest_digest='', contracts=(), meta=None, signature=None, now=None)
¶
Store or refresh an agent's capability card.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
agent
|
str
|
Name of the advertising agent. |
required |
description
|
str
|
Free-form summary. |
''
|
skills
|
Iterable[str]
|
Capability tags; stripped, de-duplicated, blanks dropped. |
()
|
task_classes
|
Iterable[str]
|
Routing classes; stripped, de-duplicated, blanks dropped. |
()
|
model
|
str
|
Backing model identifier. |
''
|
project
|
str
|
Hub-resolved project namespace. Signed cards require a non-empty value. |
''
|
manifest_digest
|
str
|
Digest of the tool/package manifest this card describes. |
''
|
contracts
|
object
|
Contract mappings or :class: |
()
|
meta
|
dict[str, Any] or None
|
Descriptive metadata; |
None
|
signature
|
object
|
Candidate signed-card envelope. Malformed values remain visible through the verification result but are not echoed as a trusted mapping. |
None
|
now
|
float or None
|
Override for the current wall-clock time, in seconds. |
None
|
Returns:
| Type | Description |
|---|---|
CapabilityCard
|
The stored card. |
advertise_persistent(agent, *, dispatchable=True, description='', skills=(), task_classes=(), model='', project='', manifest_digest='', contracts=(), meta=None, signature=None, now=None)
¶
Register or refresh a persistent dispatch card for agent.
The registration survives disconnects and expires only when it is not
refreshed within persistent_ttl_seconds. Fields and verification
are identical to :meth:advertise.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
agent
|
str
|
Name of the registering agent (a project-scoped seat identity). |
required |
dispatchable
|
bool
|
Whether automated dispatchers may consider this seat. Defaults to
|
True
|
now
|
float or None
|
Override for the current wall-clock time, in seconds. |
None
|
Returns:
| Type | Description |
|---|---|
CapabilityCard
|
The stored card payload. |
forget(agent)
¶
Drop an agent's live card, e.g. when it disconnects.
A persistent registration is deliberately kept: it means the seat may be woken for work even while no interactive session is live.
forget_persistent(agent)
¶
Drop an agent's persistent dispatch registration (opt-out).
get(agent)
¶
Return an agent's card, or None when it has none.
get_persistent(agent)
¶
Return an agent's persistent registration, or None.
expire(now=None)
¶
Drop every card not refreshed within the TTL of now.
manifest(now=None)
¶
Return all live cards as dicts, sorted by agent name.
One entry is emitted per agent. A persistent registration adds the
additive persistent/dispatchable keys to that agent's entry —
merged onto the fresher live card when both exist — so consumers see a
single, unambiguous card per agent and legacy consumers simply ignore
the new keys.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
now
|
float or None
|
Override for the current wall-clock time used to expire stale cards. |
None
|
Returns:
| Type | Description |
|---|---|
list[dict[str, Any]]
|
One card mapping per live agent. |
for_task_class(task_class, now=None)
¶
Return the agents that advertise a given task class, sorted by name.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
task_class
|
str
|
The routing class to match against each card's |
required |
now
|
float or None
|
Override for the current wall-clock time used to expire stale cards. |
None
|
Returns:
| Type | Description |
|---|---|
list[str]
|
Names of live agents that can take the task class. |
CapabilityContract
dataclass
¶
Declarative contract for one advertised task class.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
task_class
|
str
|
Routing class the contract describes, for example |
required |
input_schema
|
dict[str, Any]
|
JSON Schema style mapping describing accepted input. |
dict()
|
output_schema
|
dict[str, Any]
|
JSON Schema style mapping describing produced output. |
dict()
|
preconditions
|
tuple[str, ...]
|
Declarative checks that should hold before the task is invoked. |
()
|
postconditions
|
tuple[str, ...]
|
Declarative checks that should hold after the task completes. |
()
|
as_dict()
¶
Return the canonical manifest representation of the contract.
CompactionResult
dataclass
¶
What one :func:compact sweep removed.
Attributes:
| Name | Type | Description |
|---|---|---|
checkpoints_removed |
int
|
Number of superseded checkpoint events deleted. |
findings_removed |
int
|
Number of expired finding events deleted. |
floor_seq |
int
|
The floor the sweep honoured; no event above it was considered. |
corrupt_rows_removed |
int
|
Number of explicitly quarantined rows deleted. |
total_removed
property
¶
Return the total number of events the sweep deleted.
RetentionPolicy
dataclass
¶
How much of the durable memory log to keep.
The three knobs are independent and additive — a sweep applies whichever are
set. A policy with neither set is a no-op (:attr:is_noop).
Attributes:
| Name | Type | Description |
|---|---|---|
max_checkpoints_per_task |
int or None
|
Keep only the latest this-many resume checkpoints per task; older
checkpoints below the floor are removed. |
finding_grace_seconds |
float or None
|
Remove a finding whose validity window closed ( |
drop_corrupt_rows |
bool
|
Explicitly delete quarantined rows at or below the settled floor. The CLI requires an archive report for this destructive recovery operation. |
Decision
dataclass
¶
The outcome of running a finding through the emit gate.
Attributes:
| Name | Type | Description |
|---|---|---|
verdict |
str
|
One of :data: |
finding |
Finding or None
|
The admitted record — the original on accept, the lowered record on
floor, |
reasons |
tuple[str, ...]
|
Why the record was floored or rejected; empty on a clean accept. |
ClaimStatus
¶
The epistemic standing of the claim.
EvidenceKind
¶
What kind of evidence backs the assertion.
Finding
dataclass
¶
One emit-time memory atom: an assertion placed on the three honesty axes.
Construct from the wire with :meth:from_dict (forward-tolerant) and stamp
the hub-attested fields with :meth:attested before journalling. The emit
gate decides whether the record is admitted, floored, or rejected.
Attributes:
| Name | Type | Description |
|---|---|---|
statement |
str
|
The assertion being remembered. |
subkind |
str
|
The episodic category; an unknown value is carried opaque. |
evidence_kind |
str or None
|
What backs the claim; |
claim_status |
str or None
|
The epistemic standing; |
freshness |
str
|
How recently the reference was re-checked at source. |
evidence_ref |
str or None
|
A reference to the evidence (file:line, commit, command output). |
provenance |
Provenance or None
|
Origin coordinates; |
validity |
Validity or None
|
Bi-temporal window; |
lifecycle |
str
|
Whether the atom is current, superseded, or retracted. |
supersedes |
str or None
|
Identifier of the atom this one replaces, if any. |
verified_at_source |
SourceCheck
|
The producer-asserted re-check with hub-attested origin. |
producer_confidence |
float or None
|
Advisory producer confidence; never gates recall. |
execution_substrate |
str or None
|
Where the result was produced, when relevant. |
entities |
tuple[str, ...]
|
Named entities the finding concerns (read-side routing hooks). |
tags |
tuple[str, ...]
|
Free-form tags (read-side hierarchy hooks). |
from_dict(raw)
classmethod
¶
Parse a finding from a wire mapping without raising.
Missing optionals become None, malformed fields become their empty
form, and an unknown enum member is carried as-is. The freshness axis
is derived from the re-check recency and reference signals only when the
producer leaves it unset.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
raw
|
dict[str, Any]
|
The decoded finding message body. |
required |
Returns:
| Type | Description |
|---|---|
Finding
|
The parsed record, ready for the emit gate. |
attested(*, by, at, project_fallback='')
¶
Return a copy with the hub-attested identity and time stamped in.
The producing identity (by) and receive-time (at) come from the
connection, not the producer, so they cannot be forged. provenance.actor
is overwritten with by; provenance.ts and validity.valid_from
are anchored to at when the producer left them unset; provenance.project
falls back to project_fallback when blank; and verified_at_source
carries the attested by/at.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
by
|
str
|
The hub-attested producing identity. |
required |
at
|
float
|
The hub-attested receive-time, in seconds. |
required |
project_fallback
|
str
|
Project to record when the producer named none. |
''
|
Returns:
| Type | Description |
|---|---|
Finding
|
A new record with the attested fields stamped in. |
as_dict()
¶
Return a JSON-serialisable snapshot of the whole record.
Freshness
¶
How recently the supporting reference was re-checked at source.
The recency-of-re-check axis, orthogonal to how the claim is known
(:class:EvidenceKind): a measured fact can be re-checked this session or
left unverified for months. It gates validation — only a source-verified
claim renders validated — so a reference that exists but was never re-checked
cannot pass for one that was.
Lifecycle
¶
Whether the atom is current, replaced, or withdrawn — orthogonal to status.
Subkind
¶
What a finding is about — its episodic category.
SynapseHub
¶
Routing core that maintains presence, history, and coordination state.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
default_ttl_seconds
|
float
|
Lease TTL passed to the underlying :class: |
3600.0
|
hub_id
|
str or None
|
Stable hub identifier stamped on outgoing system messages. When |
None
|
journal
|
EventStore or None
|
When given, authoritative mutations are appended to this durable log and
the hub's state is rebuilt from it on construction, so a restart resumes
live leases and history instead of an empty registry. When |
None
|
rate_limiter
|
RateLimiter or None
|
When given, non-heartbeat messages from an agent over its limit are
refused, so one runaway agent cannot swamp the single hub. |
None
|
host_rate_limiter
|
RateLimiter or None
|
When given, every inbound frame — heartbeats included — is charged to a
bucket keyed by the connection's remote host, so a single host cannot flood
the hub by cycling agent names or with bare heartbeats. Independent of and
additional to |
None
|
durable_ingress_quota
|
DurableIngressQuota or None
|
When given, each accepted chat is charged to the connection's
server-derived quota principal (events and serialized chat-frame bytes in a sliding
window). Over-quota chats are refused before history or journal growth so
one principal cannot fill the durable log; |
None
|
max_history
|
int
|
Maximum chat messages retained in memory; the oldest are dropped beyond
this bound so history cannot grow without limit. The durable log (when a
journal is attached) still records every message. Defaults to
:data: |
DEFAULT_MAX_HISTORY
|
relay_log
|
str or Path or None
|
When given, every broadcast message is also mirrored to this newline-
delimited log in the compact lite format (see
:func: |
None
|
relay_max_lines
|
int
|
Upper bound on the relay log: it is trimmed back to its last this-many
lines once it grows that far past the bound, so the mirror cannot grow
without limit. Defaults to :data: |
DEFAULT_RELAY_MAX_LINES
|
max_progress
|
int
|
Maximum progress notes retained on the shared blackboard; the oldest are
dropped beyond this bound. The durable log (when attached) still records
every note. Defaults to :data: |
DEFAULT_MAX_PROGRESS
|
max_progress_per_author
|
int
|
Maximum progress notes retained for one author on the shared blackboard.
Defaults to :data: |
DEFAULT_MAX_PROGRESS_PER_AUTHOR
|
max_progress_per_task
|
int
|
Maximum progress notes retained for one task id on the shared blackboard.
Defaults to :data: |
DEFAULT_MAX_PROGRESS_PER_TASK
|
board_task_cap
|
int or None
|
Bound on the tasks served per board snapshot (floored at |
None
|
max_findings_per_agent
|
int
|
Maximum durable findings one agent may admit before new findings are
privately rejected. Defaults to :data: |
DEFAULT_MAX_FINDINGS_PER_AGENT
|
compact_hint_threshold
|
int
|
Record count past which a hub started on a durable log emits a one-off
startup hint to run |
DEFAULT_COMPACT_HINT_THRESHOLD
|
dead_letter_escalation_threshold
|
int
|
Escalate a dead-letter blackhole every this-many undelivered directed messages to one
target — the hub broadcasts a one-line notice and journals an audit event when the count
reaches the threshold and each further multiple, so a growing blackhole becomes an active
signal rather than a passive snapshot entry. It never re-delivers a message (the ledger
holds no bodies). |
DEFAULT_DEAD_LETTER_ESCALATION_THRESHOLD
|
dead_letter_forwarder
|
DeadLetterForwarder or None
|
The seam that hands a dead-letter blackhole signal to the peer hub whose domain owns the
target, when an escalation fires for a target this hub's namespace-ownership and relay
routes resolve to a peer. The origin always journals an audit-only forwarding event
(counts and names, never a message body) and transmits the pointer to the owning hub
best-effort. Defaults to
:func: |
forward_dead_letter
|
authenticator
|
TokenAuthenticator or None
|
When given, a connecting agent must present a valid shared-secret token
on its first message or the hub refuses and closes the socket. |
None
|
enable_metrics
|
bool
|
When |
False
|
auth_timeout
|
float
|
Seconds to wait for a name-binding first frame before closing the socket
(code |
DEFAULT_AUTH_TIMEOUT
|
max_unauth_clients
|
int or None
|
On a secured hub, the most sockets allowed in their pre-auth window at once;
a further connect is closed with code |
None
|
max_connections_per_host
|
int or None
|
Maximum simultaneous sockets admitted from one remote host. This is
distinct from the total |
DEFAULT_MAX_CONNECTIONS_PER_HOST
|
shutdown_close_timeout
|
float
|
Seconds allowed for active WebSocket close handshakes after |
DEFAULT_SHUTDOWN_CLOSE_TIMEOUT
|
metrics_token
|
str or None
|
When set (and |
None
|
metrics_query_token_ok
|
bool
|
Also accept the token as a |
False
|
insecure_off_loopback
|
bool
|
Bind a non-loopback host even when it would be reachable unauthenticated.
Off by default the hub refuses such a bind — raising
:class: |
False
|
insecure_plaintext_at_rest
|
bool
|
Bind a non-loopback host with a plaintext |
False
|
per_message_auth_keys
|
Mapping[str, MessageAuthKey] or list[MessageAuthKey] or None
|
HMAC keys accepted for opt-in per-message authentication. |
None
|
require_per_message_auth
|
bool
|
When |
False
|
per_message_auth_window_seconds
|
float
|
Timestamp window used for signed-frame freshness and replay-cache
eviction. Defaults to
:data: |
DEFAULT_MESSAGE_AUTH_WINDOW_SECONDS
|
per_message_auth_replay_capacity
|
int
|
Maximum in-memory nonce entries retained for replay detection.
Defaults to |
4096
|
signed_event_trust_bundle
|
EventSignatureTrustBundle or None
|
Ed25519 trust bundle accepted as an alternative signed-event
verification path when |
None
|
capability_card_trust_bundle
|
CapabilityCardTrustBundle or None
|
Separate Ed25519 trust and bounded lifecycle state used only to label capability-card advertisements. Verification stays advisory and default-off. |
None
|
multihub_serving_policy
|
MultiHubServingPolicy or None
|
Deny-by-default gate for serving the event log to peer hubs over a multi-hub pull.
|
None
|
namespace_ownership
|
NamespaceOwnership or None
|
Single-authoritative-hub map that routes claims by namespace ownership. |
None
|
claim_peers
|
Mapping[str, ClaimForwardPeer] or None
|
How to reach each owning hub to forward a claim it owns, keyed by owning hub id. |
None
|
claim_forwarder
|
ClaimForwarder
|
The seam that forwards a claim to an owning hub; defaults to the network
:func: |
forward_claim
|
relay_peers
|
Mapping[str, OperatorRelayPeer] or None
|
How to reach each owning hub to relay a governed operator action into a namespace it
owns, keyed by owning hub id — separate from |
None
|
relay_forwarder
|
RelayForwarder
|
The seam that relays an operator action to an owning hub; defaults to the network
:func: |
relay_operator_action
|
require_relay_reason
|
bool
|
Whether this hub refuses an operator relay that carries no reason. |
False
|
require_two_person_relay
|
bool
|
Whether an authorised operator relay needs a second, different operator before it applies.
|
False
|
observed_asserting_hubs
|
Callable[[str], Iterable[str]] or None
|
A runtime feed of the hub ids observed asserting authority over a namespace, consulted
when resolving ownership so a partition — a peer seen owning a namespace this hub also
believes it owns — refuses every grant until it is re-established. |
None
|
federation_bundle
|
FederationBundle or None
|
Deny-by-default policy composing a peered remote domain's coordination frames into the
live authorisation path. |
None
|
federation_cert_source
|
PeerCertificateSource
|
Reads the peer's live certificate for the federation gate; defaults to
:func: |
live_peer_certificate_der
|
federation_offer_path
|
str or Path or None
|
Path to this domain's own federation-bundle material, answered to a peer operator's
|
None
|
anti_rollback_checkpoint
|
bool
|
When |
True
|
checkpoint_store_path
|
str or Path or None
|
Override for the checkpoint database location; defaults to
|
None
|
finding_quota
property
¶
Return the copyable finding quota used by transactional memory writes.
from_config(config=None)
classmethod
¶
Construct a hub from a grouped :class:HubConfig record.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
HubConfig or None
|
The grouped configuration; |
None
|
reserve_finding_slot(agent)
¶
Reserve one durable-finding quota slot for agent (handler surface).
online_agents()
¶
Return the sorted names of currently registered agents.
set_agent_roles(name, roles)
¶
Bind the roles an agent answers to, as declared on its registration heartbeat.
permitted_role_claims(name, roles)
¶
Return the subset of declared roles name is permitted to bind.
With role-claim enforcement off — the default open/loopback posture — every
declared role is permitted, so a single-user dev hub binds roles exactly as
before. With --require-role-claim on, a role is kept when either:
- the role-grant store (
synapse role/--role-grants) authorisesnamefor it, or - the loaded ACL policy grants
role-claimon target kindrolefor that role value (namespace-scoped like every other ACL rule).
An unauthorised role is dropped and logged as a squatting attempt rather
than dropping the socket. Enforcement with no store and no matching ACL
rule denies the claim (fail closed). The gate keys off the self-reported
name, so pair it with a connect token and identity binding to be a real
boundary.
roles_of(name)
¶
Return the roles name currently answers to (empty tuple if none).
set_wake_capability(name, capability)
¶
Bind the receiver wake capability declared on an identity's registration.
wake_capability_of(name)
¶
Return the declared receiver wake capability for name.
observing_identities(target)
¶
Return connected identities the ACL policy grants observe on target.
Under directed-message routing an observer (a live monitor or auditor) still
receives a directed message it is not a party to only when it holds an
observe grant. With no ACL policy configured there are no observers, so
directed routing narrows to the recipients alone; the grant is scoped to the
observer's own namespace, so an operator designates observers without opening
the traffic to everyone.
recipients_without_live_waiter(recipients)
¶
Present recipients with no proof of liveness — the ones to warn about.
Thin wrapper over
:meth:~synapse_channel.core.hub_liveness.HubLivenessView.recipients_without_live_waiter,
kept because the chat handler and tests call hub.recipients_without_live_waiter.
roster_liveness()
¶
Per-agent liveness annotation for the /who roster (handler surface).
Thin wrapper over
:meth:~synapse_channel.core.hub_liveness.HubLivenessView.roster_liveness, kept
because the who-snapshot handler and tests call hub.roster_liveness.
uptime_seconds()
¶
Return seconds elapsed since the hub was constructed.
handle_message(raw_message, websocket)
async
¶
Parse and route one inbound frame.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
raw_message
|
str or bytes
|
The raw frame received from a client socket. |
required |
websocket
|
Any
|
The socket the frame arrived on. |
required |
handler(websocket)
async
¶
Serve one client connection from registration to disconnect.
Thin wrapper over
:meth:~synapse_channel.core.hub_connection.HubConnection.handler, kept as
the entry point :meth:serve hands to the modern asyncio server API.
websockets 13.0 invokes this callback after process_request has
already returned a non-upgrade HTTP response; later releases skip it.
Ignore that closed probe connection instead of registering it as an agent.
serve(host=DEFAULT_HOST, port=DEFAULT_PORT, *, ssl_context=None)
async
¶
Run the hub's WebSocket server until cancelled.
Always installs :meth:_process_request so Origin/Host handshake policy
applies to every upgrade. With :attr:enable_metrics set, the same port
also answers HTTP GET /metrics and GET /health.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
host
|
str
|
Bind address. Defaults to :data: |
DEFAULT_HOST
|
port
|
int
|
Bind port. Defaults to :data: |
DEFAULT_PORT
|
ssl_context
|
SSLContext or None
|
Server-side TLS context. When supplied, the hub serves native
|
None
|
FederationConfig
dataclass
¶
Cross-domain federation: the peering bundle, certificate reader, and served offer.
HubAuthConfig
dataclass
¶
Connection authentication, per-message authentication, and ACL enforcement.
Everything here defaults to the open loopback posture: no token, no signed frames, no ACL. Each mechanism is opt-in and composes with the others exactly as the flat keyword surface documents.
HubConfig
dataclass
¶
Complete, grouped construction record for one :class:SynapseHub.
The direct fields cover the hub's identity and collaborators; the nested
family records cover the opt-in surfaces. HubConfig() reproduces a
bare SynapseHub() exactly.
to_kwargs()
¶
Flatten the record into the keyword arguments SynapseHub accepts.
Returns:
| Type | Description |
|---|---|
dict[str, Any]
|
One entry per |
from_kwargs(kwargs)
classmethod
¶
Re-group flat SynapseHub keyword arguments into a record.
The inverse of :meth:to_kwargs: each family field regroups under its
family, every other key is a direct field, and an omitted key takes its
default — so a caller can hand over the partial keyword set it
actually assembled (for example the CLI's subset of the ~40 parameters)
and still get a complete record to fingerprint or reconstruct from. On
the full key set it round-trips with :meth:to_kwargs.
Raises:
| Type | Description |
|---|---|
TypeError
|
If |
HubLimits
dataclass
¶
Every ceiling the hub enforces: retention, quotas, and transport bounds.
Field names and defaults match the SynapseHub.__init__ keyword
parameters of the same names; see that signature's documentation for the
meaning and failure mode of each bound.
HubMetricsConfig
dataclass
¶
The optional HTTP /metrics and /health endpoint and its token.
MultiHubConfig
dataclass
¶
Multi-hub routing: serving policy, namespace ownership, claim and relay forwarding.
TakeoverDamping
dataclass
¶
Damping and ownership rules applied when one agent name is contested.
Cooldown blunts a single eviction storm; the oscillation window and threshold detect two waiters at war over one name; quarantine pins a thrashing name to its current owner. The lease offline TTL bounds how long a name's ownership lease outlives its holder's disconnect, so a re-arming owner re-takes its name and a stranger cannot squat it in the gap.
Blackboard
¶
The team's shared plan: a task ledger plus an append-only progress stream.
The board is single-threaded and synchronous; the hub owns one instance and mutates it from its event loop. Posting a task is an upsert — the same id re-declares the task and replaces its planning fields — so a planner can refine the plan idempotently.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
max_progress
|
int
|
Maximum progress notes retained; the oldest are dropped beyond this
bound so the stream cannot grow without limit. Clamped up to |
DEFAULT_MAX_PROGRESS
|
max_progress_per_author
|
int
|
Maximum progress notes retained for one author. Clamped up to |
DEFAULT_MAX_PROGRESS_PER_AUTHOR
|
max_progress_per_task
|
int
|
Maximum progress notes retained for one task id. Clamped up to |
DEFAULT_MAX_PROGRESS_PER_TASK
|
publish_from(candidate)
¶
Publish one fully prepared board candidate without changing this identity.
post_task(*, task_id, title, author, description='', depends_on=(), suggested_owner='', project='', expected_version=None, now=None)
¶
Declare or re-declare a task on the plan (an upsert).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
task_id
|
str
|
Identifier and short name; both are required (whitespace-stripped). |
required |
title
|
str
|
Identifier and short name; both are required (whitespace-stripped). |
required |
author
|
str
|
Agent declaring the task; recorded as |
required |
description
|
str
|
Longer description. |
''
|
depends_on
|
tuple[str, ...] or list[str]
|
Prerequisite task ids; self-references and duplicates are dropped. |
()
|
suggested_owner
|
str
|
Advisory proposed owner. |
''
|
project
|
str
|
Project namespace. On a first declaration |
''
|
expected_version
|
int or None
|
Compare-and-set guard: when given, the mutation is refused unless
the task's current |
None
|
now
|
float or None
|
Override for the current wall-clock time, in seconds. |
None
|
Returns:
| Type | Description |
|---|---|
tuple[bool, str]
|
|
update_task(task_id, *, status=None, suggested_owner=None, project=None, expected_version=None, now=None)
¶
Change a declared task's planning status, owner, or project scope.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
task_id
|
str
|
Identifier of the task to update. |
required |
status
|
str or None
|
New planning status; must be in :data: |
None
|
suggested_owner
|
str or None
|
Replacement advisory owner ( |
None
|
project
|
str or None
|
Replacement project scope ( |
None
|
expected_version
|
int or None
|
Compare-and-set guard: when given, the update is refused unless the
task's current |
None
|
now
|
float or None
|
Override for the current wall-clock time, in seconds. |
None
|
Returns:
| Type | Description |
|---|---|
tuple[bool, str]
|
|
post_progress(*, task_id, author, text, kind='note', now=None)
¶
Append a structured progress note, dropping the oldest past the bound.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
task_id
|
str
|
Task the note concerns; |
required |
author
|
str
|
Agent posting the note. |
required |
text
|
str
|
Body of the note. |
required |
kind
|
str
|
One of :data: |
'note'
|
now
|
float or None
|
Override for the current wall-clock time, in seconds. |
None
|
Returns:
| Type | Description |
|---|---|
tuple[bool, ProgressNote or str]
|
|
note(*, task_id, author, text, now=None)
¶
Append a plain note-kind progress entry, returning it directly.
A convenience over :meth:post_progress for callers that always use the
note kind (so the kind cannot be rejected) and want the appended
:class:ProgressNote without unpacking a result tuple.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
task_id
|
str
|
Task the note concerns; |
required |
author
|
str
|
Agent posting the note. |
required |
text
|
str
|
Body of the note. |
required |
now
|
float or None
|
Override for the current wall-clock time, in seconds. |
None
|
Returns:
| Type | Description |
|---|---|
ProgressNote
|
The appended note. |
restore_progress(note)
¶
Restore one persisted progress note while applying retention bounds.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
note
|
ProgressNote
|
Persisted note to insert into the retained progress stream. |
required |
Returns:
| Type | Description |
|---|---|
ProgressNote
|
The restored note. |
blocking_dependencies(task_id)
¶
Return the unmet dependencies of a task, in declaration order.
A dependency is unmet when the prerequisite is absent from the board or has not reached a terminal status. Returns an empty list for an unknown task.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
task_id
|
str
|
Identifier of the task to inspect. |
required |
Returns:
| Type | Description |
|---|---|
list[str]
|
Task ids that still block this task. |
ready_tasks()
¶
Return open tasks whose every dependency has reached a terminal status.
Returns:
| Type | Description |
|---|---|
list[LedgerTask]
|
Tasks with planning status |
snapshot(*, task_cap=None)
¶
Return a consistent view of the plan and the recent progress stream.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
task_cap
|
int or None
|
When set, bound the served |
None
|
Returns:
| Type | Description |
|---|---|
dict[str, Any]
|
Mapping with |
LedgerTask
dataclass
¶
A declared unit of work on the shared plan.
Attributes:
| Name | Type | Description |
|---|---|---|
task_id |
str
|
Stable identifier, shared with any claim taken on the task. |
title |
str
|
Short human-readable name of the work. |
description |
str
|
Optional longer description or acceptance notes. |
depends_on |
tuple[str, ...]
|
Task ids that must reach a terminal status before this task is ready. |
status |
str
|
Coarse planning status from :data: |
suggested_owner |
str
|
Optional agent name proposed to take the task; advisory only. |
project |
str
|
Optional project namespace the task belongs to. |
version |
int
|
Monotonic mutation counter: |
created_by |
str
|
Agent that first declared the task. |
created_at |
float
|
Wall-clock seconds when the task was first declared. |
updated_at |
float
|
Wall-clock seconds when the task was last changed. |
as_dict()
¶
Return a JSON-serialisable snapshot of this task.
ProgressNote
dataclass
¶
One structured entry in the append-only progress ledger.
Attributes:
| Name | Type | Description |
|---|---|---|
task_id |
str
|
Task the note concerns; |
author |
str
|
Agent that posted the note. |
kind |
str
|
One of :data: |
text |
str
|
Free-form body of the note. |
posted_at |
float
|
Wall-clock seconds when the note was posted. |
as_dict()
¶
Return a JSON-serialisable snapshot of this note.
TaskStatus
¶
The legal status values for a claimed task.
CLAIMED is the entry state stamped when a lease is granted; DONE and
FAILED are terminal. Values are the literal strings carried on the wire.
Metric
dataclass
¶
One Prometheus sample: a named gauge or counter with help text.
Attributes:
| Name | Type | Description |
|---|---|---|
name |
str
|
Metric name, e.g. |
documentation |
str
|
Human-readable |
metric_type |
str
|
|
value |
float
|
The current sample value. |
EventStore
¶
Append-only SQLite event log in WAL mode.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
str or Path
|
Database file path. |
required |
key_file
|
str or Path or None
|
Owner-only 32-byte key file. When set, the store opens through SQLCipher
( |
None
|
key
|
bytes or None
|
Raw 32-byte key material (tests and programmatic callers). When set,
takes precedence over |
None
|
encrypted
property
¶
Return whether this store opened through SQLCipher page encryption.
append(kind, payload, *, ts=None, durable=False)
¶
Append one event to the log and return its assigned sequence number.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
kind
|
str
|
Event kind tag. |
required |
payload
|
dict[str, Any]
|
JSON-serialisable event body. |
required |
ts
|
float or None
|
Event timestamp, in seconds; the system clock is used when |
None
|
durable
|
bool
|
When |
False
|
Returns:
| Type | Description |
|---|---|
int
|
The monotonic |
Notes
A failed database write is rolled back. Durable attempts restore the
connection to synchronous=NORMAL. If that cleanup alone fails after
COMMIT, the append remains successful and the cleanup failure is logged;
reporting a write failure at that point would contradict durable truth.
append_batch(events, *, ts=None, durable=False)
¶
Append several events in one SQLite transaction.
The whole batch commits or rolls back as one unit. All rows share one timestamp so adjacent state and provenance events describe the same authoritative transition.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
events
|
iterable[tuple[str, Mapping[str, Any]]]
|
Ordered |
required |
ts
|
float or None
|
Shared timestamp; the system clock is used when omitted. |
None
|
durable
|
bool
|
Use |
False
|
Returns:
| Type | Description |
|---|---|
tuple[int, ...]
|
Assigned sequence numbers in input order. An empty input returns an empty tuple without opening a transaction. |
get_operation(operation_key)
¶
Return one completed operation without exposing the key in errors.
read_operations()
¶
Return durable operation records in commit order for cache seeding.
pending_operation_outbox_count()
¶
Return the number of committed operation intents awaiting projection.
pending_operation_intents(*, limit=100)
¶
Return committed evidence intents awaiting idempotent local projection.
mark_operation_intent_delivered(operation_key, receipt_id)
¶
Bind one operation intent to a value-free local projection receipt.
commit_delivery_receipt_request(*, chat, requested, stage_hook=None)
¶
Atomically commit a directed chat and its requested-receipt aggregate.
The chat sequence is assigned inside the transaction and injected into the requested event and aggregate. A crash therefore leaves either both facts or neither; it can never retain a directed body without the fact that its sender requested a receipt.
commit_delivery_receipt_transition(*, kind, payload, notification, state, companion=None, stage_hook=None)
¶
Commit one receipt transition and its stable notification outbox row.
The outbox identity is deterministic for (message_seq, state). A
process crash after WebSocket acceptance but before acknowledgement may
therefore replay the same frame, and the sender can deduplicate it
without the hub pretending to know whether a model consumed it.
delivery_receipt_aggregate(message_seq)
¶
Return the current durable receipt aggregate for one chat sequence.
pending_delivery_notifications(*, sender=None, limit=100)
¶
Return undelivered receipt frames in stable insertion order.
mark_delivery_notification_attempt(notification_id, *, delivered)
¶
Record one sender-delivery attempt and settle it only on transport success.
commit_operation(*, operation_key, request_digest, response, events, intent, response_event_seq_field=None, stage_hook=None)
¶
Atomically commit a keyed mutation, exact response, and evidence intent.
pending_aef_events(*, limit=100)
¶
Return queued legacy rows awaiting native AEF reconciliation.
mark_aef_delivered(legacy_seq, receipt_id)
¶
Durably bind one queued legacy row to its emitted AEF receipt.
aef_delivery(legacy_seq)
¶
Return the delivered receipt id, or None for pending/absent rows.
read_all()
¶
Return every event in insertion order.
Returns:
| Type | Description |
|---|---|
list[StoredEvent]
|
All persisted events, ordered by ascending sequence number. |
iter_events(*, through_seq=None, kinds=None)
¶
Yield events in ascending sequence order without materialising the log.
This is the bounded-memory read seam for whole-log folds (the Merkle commitment, causality reconstruction): rows stream off the SQLite cursor one at a time, so the peak footprint is one event, not the log. A kind filter is applied inside SQLite, so uninterested kinds (bulk chat on a long-lived hub) never cross into Python at all.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
through_seq
|
int or None
|
Inclusive sequence ceiling; events after it are not yielded. |
None
|
kinds
|
Iterable[str] or None
|
When given, restrict the stream to these event kinds; an empty
iterable yields nothing. |
None
|
Yields:
| Type | Description |
|---|---|
StoredEvent
|
Each matching event at or below the ceiling, by ascending sequence. |
read_since(after_seq, *, kinds=None, limit=None)
¶
Return events whose sequence is greater than a cursor, in order.
This is the durable, presence-free ingest seam a downstream
persistent-memory adapter polls: it tracks the last sequence it consumed,
calls :meth:read_since with it, processes the batch, and advances —
resuming with no loss or duplication across hub restarts, because the
sequence is a monotonic primary key.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
after_seq
|
int
|
Exclusive lower bound; only events with |
required |
kinds
|
Iterable[str] or None
|
When given, restrict the result to these event kinds (e.g.
:data: |
None
|
limit
|
int or None
|
Cap the batch size (floored at |
None
|
Returns:
| Type | Description |
|---|---|
list[StoredEvent]
|
Matching events ordered by ascending sequence number. |
read_window(*, min_seq=None, max_seq=None, since_ts=None, until_ts=None, kinds=None, limit=None)
¶
Return events inside an inclusive sequence/time window, in order.
This is the selective-read seam the event-query layer uses to avoid
loading an unbounded event store for every point-in-time or windowed
query: the bounds are pushed into SQLite so only candidate rows are
deserialised. Every bound is optional and inclusive; omitting all of them
is equivalent to :meth:read_all.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
min_seq
|
int or None
|
Inclusive lower and upper sequence bounds ( |
None
|
max_seq
|
int or None
|
Inclusive lower and upper sequence bounds ( |
None
|
since_ts
|
float or None
|
Inclusive lower and upper timestamp bounds ( |
None
|
until_ts
|
float or None
|
Inclusive lower and upper timestamp bounds ( |
None
|
kinds
|
Iterable[str] or None
|
Restrict to these event kinds; an empty iterable returns nothing. |
None
|
limit
|
int or None
|
Cap the number of rows returned after ordering (floored at |
None
|
Returns:
| Type | Description |
|---|---|
list[StoredEvent]
|
Matching events ordered by ascending sequence number. |
corrupt_rows(*, through_seq=None)
¶
Return safe forensic markers for every malformed row in sequence order.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
through_seq
|
int or None
|
Inclusive sequence ceiling. |
None
|
Returns:
| Type | Description |
|---|---|
tuple[CorruptEventRow, ...]
|
Markers contain reasons and a raw-payload digest, never raw payload bytes. This scan is the operator seam used by explicit compaction. |
count()
¶
Return the number of events currently stored.
max_seq()
¶
Return the highest sequence number stored, or 0 when the log is empty.
Useful as a fully-settled compaction floor: with no read-side consumer
lagging behind, the whole log up to the latest sequence may be compacted
(see :mod:synapse_channel.core.compaction).
latest_at_or_before(through_seq)
¶
Return the newest retained event at or below through_seq.
A direct descending primary-key lookup keeps state-at projections from
decoding the whole journal merely to obtain their deterministic clock.
Sequence gaps left by retention are handled by selecting the preceding
retained event; an empty prefix returns None.
delete(seqs)
¶
Delete the events with these sequence numbers; return how many were removed.
A maintenance primitive for retention/compaction
(:mod:synapse_channel.core.compaction). A deleted sequence is never
reused — the AUTOINCREMENT primary key only ever increases — so a
downstream :meth:read_since cursor stays correct across a compaction: a
removed sequence simply becomes a gap the cursor walks past. The delete
commits at NORMAL durability; a delete lost to an OS crash is harmless
because re-running compaction removes the same rows again.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
seqs
|
Iterable[int]
|
Sequence numbers to remove; an empty iterable is a no-op. |
required |
Returns:
| Type | Description |
|---|---|
int
|
The number of rows actually deleted. |
vacuum()
¶
Reclaim free pages left by deletes, shrinking the database file on disk.
A DELETE marks pages free for reuse but does not return them to the
filesystem, so a large retention sweep leaves the file the same size until
VACUUM rewrites the database to release the free pages. It rewrites the
whole database, so call it from a maintenance path, not the hot loop.
close()
¶
Close the underlying database connection.
__enter__()
¶
Enter a context manager that closes the store on exit.
__exit__(exc_type, exc, tb)
¶
Close the store when leaving the context.
MessageType
¶
String constants for every Synapse message type.
The upper group is sent by agents to the hub; the lower group is emitted by the hub back to agents. Values are the literal strings that travel on the wire — never rename a value without migrating every peer.
ResourceOffer
dataclass
¶
A capability that an agent advertises to the rest of the team.
Attributes:
| Name | Type | Description |
|---|---|---|
agent |
str
|
Name of the offering agent. |
kind |
str
|
Category of the resource, e.g. |
name |
str
|
Concrete resource identifier, e.g. a model name or device handle. |
capacity |
int
|
How many concurrent consumers the offer can serve (minimum 1). |
meta |
dict[str, Any]
|
Arbitrary descriptive metadata about the offer. |
offered_at |
float
|
Wall-clock time, in seconds, when the offer was last refreshed. |
SynapseState
¶
Authoritative registry of presence, claims, tasks, and resources.
The registry is single-threaded and synchronous; the hub owns one instance and mutates it from its event loop. Every mutating call refreshes the caller's heartbeat and lazily expires stale leases and offers, so liveness is maintained without a background timer.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
default_ttl_seconds
|
float
|
Lease duration applied to a claim when the caller does not request an
explicit TTL. Clamped into
|
3600.0
|
max_claims_per_agent
|
int
|
Most live claims one agent may hold. Clamped up to |
MAX_CLAIMS_PER_AGENT
|
max_offers_per_agent
|
int
|
Most live resource offers one agent may register. Clamped up to |
MAX_OFFERS_PER_AGENT
|
max_paths_per_claim
|
int
|
Most distinct paths a single claim may declare before its scope is
widened to the whole worktree. Clamped up to |
MAX_DECLARED_PATHS
|
publish_from(candidate)
¶
Atomically publish a privately mutated candidate state.
Durable hub mutations are prepared on a deep copy while the current state remains visible to readers. After the matching journal append commits, the event-loop mutation actor calls this synchronous method; because it contains no await, readers observe either the complete old state or the complete committed state, never a provisional mutation.
last_seen is updated in place because the hub liveness view retains
that mapping by reference. The other registries are reached through
hub.state on every read and can therefore be replaced wholesale.
Configuration is immutable for a running hub and must agree between the
authoritative state and its clone.
reindex_leases()
¶
Rebuild the lease-expiry heap from the live claims.
Discards every superseded heap entry in one pass. Used after a bulk load
that assigns claims directly — a journal replay — and as the churn-bound
rebuild in :meth:_track_lease.
heartbeat(agent, now=None)
¶
Record that agent is alive and expire anything now stale.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
agent
|
str
|
Name of the agent reporting liveness. |
required |
now
|
float or None
|
Override for the current wall-clock time, in seconds. When |
None
|
claim(agent, task_id, note='', ttl_seconds=None, now=None, *, quota_principal=None, worktree=DEFAULT_WORKTREE, paths=(), path_identity=None, git=None)
¶
Acquire or renew a scoped lease on a task.
An owner may freely renew its own live claim, and any agent may take over
a task whose lease has expired. A live claim held by another agent blocks
the request. Beyond the task id, a claim may declare a file scope
(worktree + paths); the request is also refused when that scope
contends with another agent's live claim, which is how the bus refuses
overlapping scoped authority. Every successful claim or renewal
is stamped with a fresh, strictly-increasing :attr:TaskClaim.epoch.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
agent
|
str
|
Name of the agent attempting the claim. |
required |
task_id
|
str
|
Identifier of the task; surrounding whitespace is stripped. |
required |
note
|
str
|
Human-readable context stored with the claim. |
''
|
ttl_seconds
|
float or None
|
Requested lease duration, clamped into
|
None
|
now
|
float or None
|
Override for the current wall-clock time, in seconds. |
None
|
worktree
|
str
|
Worktree label; different worktree identities do not contend. |
DEFAULT_WORKTREE
|
paths
|
tuple[str, ...] or list[str]
|
Declared file/directory paths; empty claims the whole worktree. |
()
|
path_identity
|
ClaimScopeIdentity or None
|
Client-derived filesystem-canonical identity aligned one-to-one with
|
None
|
git
|
GitContext or None
|
Branch context to attach to the claim; |
None
|
quota_principal
|
str or None
|
Stable server-derived identity bucket charged for the claim. |
None
|
Returns:
| Type | Description |
|---|---|
tuple[bool, str]
|
|
update_task(agent, task_id, *, status=None, note=None, data_ref=None, epoch=None, expected_version=None, now=None)
¶
Update the status, note, or artefact reference of an owned task.
Only the claim owner may mutate it. Fields left as None are
untouched; a non-empty status must be a legal lifecycle transition
(see :func:synapse_channel.core.lifecycle.can_transition). When epoch is
supplied it must match the claim's current epoch (lease guard), and when
expected_version is supplied it must match the claim's current version
(optimistic-concurrency guard against lost updates). A successful update
bumps the version.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
agent
|
str
|
Name of the agent issuing the update; must own the claim. |
required |
task_id
|
str
|
Identifier of the task to update. |
required |
status
|
str or None
|
New lifecycle status, applied only when truthy and the transition is legal. |
None
|
note
|
str or None
|
Replacement note; stripped before storage. |
None
|
data_ref
|
str or None
|
Replacement artefact reference; stripped before storage. |
None
|
epoch
|
int or None
|
Expected lease generation; when given and stale, the update is refused. |
None
|
expected_version
|
int or None
|
Expected field version; when given and mismatched, the update is refused so a stale writer cannot clobber a newer value. |
None
|
now
|
float or None
|
Override for the current wall-clock time, in seconds. |
None
|
Returns:
| Type | Description |
|---|---|
tuple[bool, str]
|
|
save_checkpoint(agent, task_id, checkpoint, *, epoch=None, now=None)
¶
Save a resume token on an owned task so it can continue after expiry.
Only the owner may save, and the checkpoint persists with the claim: if the lease later expires, a new claimant of the same task inherits it.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
agent
|
str
|
The owner saving the checkpoint. |
required |
task_id
|
str
|
Identifier of the owned task; whitespace is stripped. |
required |
checkpoint
|
str
|
Opaque resume token to store. |
required |
epoch
|
int or None
|
Expected lease generation; a stale epoch is refused. |
None
|
now
|
float or None
|
Override for the current wall-clock time, in seconds. |
None
|
Returns:
| Type | Description |
|---|---|
tuple[bool, str]
|
|
release(agent, task_id, now=None, *, epoch=None)
¶
Release a task held by agent.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
agent
|
str
|
Name of the agent releasing the claim; must be the owner. |
required |
task_id
|
str
|
Identifier of the task; surrounding whitespace is stripped. |
required |
now
|
float or None
|
Override for the current wall-clock time, in seconds. |
None
|
epoch
|
int or None
|
Expected lease generation; when given and stale, the release is refused so an agent cannot drop a lease that has since been superseded. |
None
|
Returns:
| Type | Description |
|---|---|
tuple[bool, str]
|
|
force_release(task_id, *, by)
¶
Release a task regardless of who holds it, on externally verified authority.
Unlike :meth:release, this does not require the caller to be the lease owner:
the authority to revoke another agent's claim is established before this is
called — a governed cross-hub operator relay whose peer, scope, and namespace
ownership have all been verified deny-closed. This method only executes the
revocation the caller is already entitled to make, so it never checks ownership
itself; it must not be reachable except behind that authorisation.
The revocation is otherwise identical to a self-release: the lease is dropped and any retained checkpoint discarded, so a later unrelated claim of the same id does not resurrect stale resume state. It does not touch the operator's heartbeat — the operator is remote and holds no presence on this hub.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
task_id
|
str
|
Identifier of the task to revoke; surrounding whitespace is stripped. |
required |
by
|
str
|
The operator identity the revocation is attributed to, named in the message for the audit trail. |
required |
Returns:
| Type | Description |
|---|---|
tuple[bool, str]
|
|
handoff(agent, task_id, to_agent, *, note=None, epoch=None, now=None)
¶
Transfer an owned task to another agent in one atomic step.
Ownership moves directly from the holder to to_agent with no
release/re-claim window in which a third agent could grab the task. The
task keeps its file scope, status, and artefact reference (its working
context) and is stamped with a fresh epoch and a full lease, so the
previous owner's epoch becomes stale and cannot act on the moved task.
The version counter resets for the new owner.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
agent
|
str
|
The current owner requesting the handoff. |
required |
task_id
|
str
|
Identifier of the task to hand off; whitespace is stripped. |
required |
to_agent
|
str
|
The agent to receive the task; whitespace is stripped. |
required |
note
|
str or None
|
Replacement note for the moved claim; the existing note is kept when
|
None
|
epoch
|
int or None
|
Expected lease generation; a stale epoch is refused. |
None
|
now
|
float or None
|
Override for the current wall-clock time, in seconds. |
None
|
Returns:
| Type | Description |
|---|---|
tuple[bool, str]
|
|
offer_resource(agent, *, kind, name, capacity=1, meta=None, now=None)
¶
Advertise a resource the agent can provide, keyed by agent/kind/name.
Re-offering the same triple refreshes the offer's liveness timestamp. A new
offer is refused once the agent already holds
:data:MAX_OFFERS_PER_AGENT live offers, so a runaway agent cannot bloat
the registry; refreshing an existing offer is always allowed.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
agent
|
str
|
Name of the offering agent. |
required |
kind
|
str
|
Resource category, e.g. |
required |
name
|
str
|
Concrete resource identifier. |
required |
capacity
|
int
|
Concurrent-consumer capacity, clamped up to |
1
|
meta
|
dict[str, Any] or None
|
Descriptive metadata; |
None
|
now
|
float or None
|
Override for the current wall-clock time, in seconds. |
None
|
Returns:
| Type | Description |
|---|---|
str or None
|
The registry key |
query_resources(kind=None)
¶
List currently offered resources, optionally filtered by kind.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
kind
|
str or None
|
When given, only offers of this category are returned. |
None
|
Returns:
| Type | Description |
|---|---|
list[dict[str, Any]]
|
Offer mappings sorted by |
snapshot(now=None)
¶
Return a consistent view of claims, agents, and resources.
Stale claims and offers are expired before the view is built, so the snapshot never reports leases that have already lapsed.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
now
|
float or None
|
Override for the current wall-clock time, in seconds. |
None
|
Returns:
| Type | Description |
|---|---|
dict[str, Any]
|
Mapping with |
TaskClaim
dataclass
¶
A lease held by one agent over a named unit of work.
The owner keeps the claim until it is explicitly released or its lease expires. While the lease is live, other agents are refused the same task.
Attributes:
| Name | Type | Description |
|---|---|---|
task_id |
str
|
Stable identifier of the claimed task. |
owner |
str
|
Name of the agent currently holding the claim. |
note |
str
|
Free-form human-readable context for the claim. |
claimed_at |
float
|
Wall-clock time, in seconds, when the claim was last (re)acquired. |
lease_expires_at |
float
|
Wall-clock time, in seconds, after which the claim auto-expires. |
quota_principal |
str
|
Internal stable bucket charged for this claim. It is intentionally absent
from public snapshots and wire grants; durable journal writers use
:meth: |
status |
str
|
Exact claim lifecycle marker: |
data_ref |
str
|
Optional pointer to produced artefacts (e.g. a memory key or file path). |
worktree |
str
|
Worktree label the work happens in; claims in different worktrees never contend for files. |
paths |
tuple[str, ...]
|
Declared file/directory paths the claim intends to touch; empty means the whole worktree. |
path_identity |
ClaimScopeIdentity or None
|
Optional client-derived canonical identity aligned with |
epoch |
int
|
Strictly-increasing lease generation. A mutation carrying a stale epoch is rejected, so a paused/expired agent cannot act on a superseded claim. |
version |
int
|
Optimistic-concurrency counter bumped on every field update, used for compare-and-swap so a stale update is rejected. Reset on (re)claim. |
checkpoint |
str
|
Opaque resume token the owner saves so the work can continue from where it stopped. It survives lease expiry: a later claimant of the same task inherits the last checkpoint instead of restarting. |
git |
GitContext or None
|
The branch context the claim is scoped to, set by a git-aware client;
|
as_dict()
¶
Return a JSON-serialisable snapshot of this claim.
Returns:
| Type | Description |
|---|---|
dict[str, Any]
|
A mapping with the claim's public fields, safe to embed in a wire message or state snapshot. |
as_persisted_dict()
¶
Return a durable snapshot including private quota accounting.
Public state and protocol views use :meth:as_dict, which omits the
credential-derived principal. The append-only journal needs the bucket so
replay preserves quota enforcement across a hub restart.
default_hub_uri()
¶
Return the hub URI a command should use when --uri is not given.
Reads the SYNAPSE_URI environment variable so an operator can point the
whole CLI at a non-default hub — a remote coordinator, a second local hub on
another port — without repeating --uri on every command. A blank or unset
variable falls back to :data:DEFAULT_HUB_URI, the loopback hub. Resolved
each time a parser is built, so every fresh CLI process reads the current
environment; an explicit --uri on the command line still wins.
sanitize_text(text, max_len=400)
¶
Collapse runs of whitespace and truncate to max_len characters.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
text
|
str
|
Raw text to clean. Non-string input is coerced via |
required |
max_len
|
int
|
Maximum length of the returned string. Defaults to |
400
|
Returns:
| Type | Description |
|---|---|
str
|
Single-spaced, length-bounded text. |
plan_team(port, *, no_workers=False, fast_model=None, reason_model=None, prefix='', detect=detect_model)
¶
Plan the child processes for a team without spawning anything.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
port
|
int
|
Hub port; the worker URI is derived from it. |
required |
no_workers
|
bool
|
When |
False
|
fast_model
|
str or None
|
Explicit model overrides; when |
None
|
reason_model
|
str or None
|
Explicit model overrides; when |
None
|
prefix
|
str
|
Namespace prepended to every worker name, so a team can run per project
without clashing with another project's roster. Defaults to |
''
|
detect
|
ModelDetector
|
Model-detection callable, injectable for testing. |
detect_model
|
Returns:
| Type | Description |
|---|---|
list[ProcessSpec]
|
The hub spec followed by zero, one, or two worker specs. A second worker is added only when the reasoning model differs from the fast one. |
run_team(port=8876, *, no_workers=False, fast_model=None, reason_model=None, prefix='', popen=subprocess.Popen, sleep=time.sleep, detect=detect_model, is_hub_ready=_hub_is_listening, shutdown_timeout_seconds=SHUTDOWN_TIMEOUT_SECONDS)
¶
Spawn a hub and workers, then monitor them until one exits.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
port
|
int
|
Hub port. Defaults to |
8876
|
no_workers
|
bool
|
When |
False
|
fast_model
|
str or None
|
Explicit model overrides; auto-detected when |
None
|
reason_model
|
str or None
|
Explicit model overrides; auto-detected when |
None
|
prefix
|
str
|
Namespace prepended to every worker name. Defaults to |
''
|
popen
|
Callable
|
|
Popen
|
sleep
|
Callable
|
|
sleep
|
detect
|
ModelDetector
|
Model-detection callable, injectable for testing. |
detect_model
|
is_hub_ready
|
Callable
|
Predicate that reports whether the hub is accepting connections on the port, injectable for testing. Defaults to a real TCP probe. |
_hub_is_listening
|
shutdown_timeout_seconds
|
float
|
Seconds to wait during shutdown before killing a child process. |
SHUTDOWN_TIMEOUT_SECONDS
|
Returns:
| Type | Description |
|---|---|
int
|
|
is_service_message(sender, payload, msg_type='chat')
¶
Return whether a message is system/sidecar noise the worker should skip.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
sender
|
str
|
Name of the message sender. |
required |
payload
|
str
|
Message text. |
required |
msg_type
|
str
|
Message type. Defaults to |
'chat'
|
Returns:
| Type | Description |
|---|---|
bool
|
|
classify(prompt, *, rule_max_chars=DEFAULT_RULE_MAX_CHARS, heavy_min_chars=DEFAULT_HEAVY_MIN_CHARS)
¶
Classify a prompt into a routing task class.
The policy is deterministic: a short prompt is rule; a long prompt or one
containing a heavy keyword is heavy; everything else is slm.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
prompt
|
str
|
The user prompt to classify; leading/trailing whitespace is ignored. |
required |
rule_max_chars
|
int
|
Length at or below which a prompt is |
DEFAULT_RULE_MAX_CHARS
|
heavy_min_chars
|
int
|
Length at or above which a prompt is |
DEFAULT_HEAVY_MIN_CHARS
|
Returns:
| Type | Description |
|---|---|
str
|
One of :attr: |
detect_stalls(board, *, now, idle_seconds=DEFAULT_IDLE_SECONDS, policy=None)
¶
Decide which tasks on a board snapshot should be re-offered.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
board
|
dict[str, Any]
|
A blackboard snapshot as returned by
:meth: |
required |
now
|
float
|
Current wall-clock time, in seconds, used to age in-progress tasks. |
required |
idle_seconds
|
float
|
Backwards-compatible fixed no-activity window. Ignored when |
DEFAULT_IDLE_SECONDS
|
policy
|
StallPolicy or None
|
Full operator policy. |
None
|
Returns:
| Type | Description |
|---|---|
list[Intervention]
|
One re-offer per stalled task, sorted by |
compact(store, policy, *, floor_seq, now=None, dry_run=False)
¶
Apply a retention policy to the durable log, deleting only below the floor.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
store
|
EventStore
|
The event log to compact in place. |
required |
policy
|
RetentionPolicy
|
Which retention knobs to apply; a no-op policy deletes nothing. |
required |
floor_seq
|
int
|
Compaction only considers events with |
required |
now
|
float or None
|
Wall-clock time used to age out findings; the system clock is used when
|
None
|
dry_run
|
bool
|
Calculate and return the exact deletion counts without changing the store. Used to persist a recovery archive before destructive removal. |
False
|
Returns:
| Type | Description |
|---|---|
CompactionResult
|
Counts of what was removed and the floor that was honoured. |
would_create_cycle(waits, claims, waiter, holder)
¶
Return whether waiter waiting for holder would close a cycle.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
waits
|
Mapping[str, set[str]]
|
The current wait-for graph mapping each waiting agent to the tasks it waits for. |
required |
claims
|
Mapping[str, Any]
|
The live claims registry (task id → claim object or mapping with an
|
required |
waiter
|
str
|
The agent that wants to start waiting. |
required |
holder
|
str
|
The agent currently holding what |
required |
Returns:
| Type | Description |
|---|---|
bool
|
|
admit(finding)
¶
Decide whether a finding is admitted, floored, or rejected.
Rejections are checked first — structural omissions and the one hard contradiction, falsified evidence claiming reference-validated (LOCK-4). A record that fails any is refused outright. Otherwise the claim status and freshness are lowered where the evidence cannot support them, in an order that preserves honesty: falsified evidence renders the claim refuted (INV-2), then producer-asserted testimony is stripped of any source-verified freshness and capped at the boundary (INV-6), then a reference-validated claim that lacks a reference or a source-verified freshness is floored (INV-1) — so a contradiction is resolved to its most honest standing exactly once.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
finding
|
Finding
|
The parsed record to admit. |
required |
Returns:
| Type | Description |
|---|---|
Decision
|
The verdict, the admitted record (or |
can_transition(current, target)
¶
Return whether a task may move from current to target.
A move to an unknown status is never allowed. Re-affirming the same status is always allowed (it lets an owner update other fields without changing state). Otherwise the move must appear in the transition table.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
current
|
str
|
The task's present status. |
required |
target
|
str
|
The requested next status. |
required |
Returns:
| Type | Description |
|---|---|
bool
|
|
collect_hub_metrics(hub)
¶
Read the hub's live counters into a list of metrics.
Only the hub's in-memory state is inspected — no I/O — so this is safe to call from the event loop on every scrape.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
hub
|
SynapseHub
|
The hub to read. |
required |
Returns:
| Type | Description |
|---|---|
list[Metric]
|
A constant |
health_snapshot(hub)
¶
Return a small JSON-serialisable health document for the hub.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
hub
|
SynapseHub
|
The hub to summarise. |
required |
Returns:
| Type | Description |
|---|---|
dict[str, Any]
|
|
render_prometheus(metrics)
¶
Render metrics in the Prometheus text exposition format.
Each metric emits a # HELP line, a # TYPE line, and one sample line.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
metrics
|
Iterable[Metric]
|
The samples to render, in order. |
required |
Returns:
| Type | Description |
|---|---|
str
|
The exposition text, terminated by a trailing newline. |
addresses_project(target, project)
¶
Return whether a message to target reaches any agent in project.
Matches a broadcast, the project name itself, and any <project>/... identity
or group glob — so a returning terminal catches up everything for its repo
regardless of which instance id it now runs as.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
target
|
str
|
The recipient field of a message. |
required |
project
|
str
|
The project (repo) name, e.g. |
required |
Returns:
| Type | Description |
|---|---|
bool
|
|
build_envelope(sender, msg_type, *, target='all', payload='', now=None, **extra)
¶
Build the agent-side message envelope sent to the hub.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
sender
|
str
|
Name of the sending agent. |
required |
msg_type
|
str
|
One of the :class: |
required |
target
|
str
|
Recipient agent name, or |
'all'
|
payload
|
str
|
Free-form text body of the message. |
''
|
now
|
float or None
|
Override timestamp, in seconds. |
None
|
**extra
|
Any
|
Additional protocol fields (e.g. |
{}
|
Returns:
| Type | Description |
|---|---|
dict[str, Any]
|
A JSON-serialisable envelope ready to hand to |
is_directed(target, name, roles=())
¶
Return whether target names name specifically rather than broadcasting.
Stricter than :func:is_recipient in two ways: "all" (and an empty target) is
not a match, and a target part must match the full name (or one of its
roles) rather than its bare project. So a bare <project> directs the waiter
armed as that project, but is a routine broadcast — not a wake — for a
<project>/<seat> sub-seat. A reader uses this to wake only on messages addressed
to it, a role it holds, or a group glob it is in, and to treat both all broadcasts
and project-level traffic as read-when-convenient.
This is the WAKE question, deliberately distinct from the INBOX question
(:func:is_recipient): a sub-seat still receives a bare-project message, it just is
not woken by one (which is why a multi-seat project's convene traffic no longer wakes
every seat). A sole agent that wants project-addressed messages to wake it arms
--for <project> (the bare project), not a <project>/<id> sub-identity. A message
to a role the waiter holds is a directed wake, so addressing a role reaches its
current holder promptly.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
target
|
str
|
The recipient field. |
required |
name
|
str
|
The reader's own agent name. |
required |
roles
|
Iterable[str]
|
Additional full |
()
|
Returns:
| Type | Description |
|---|---|
bool
|
|
is_recipient(target, name, roles=())
¶
Return whether name is an addressee of a message sent to target.
The hub broadcasts every chat to every connected client and carries the
intended recipient in target; a reader uses this predicate to keep only
the messages meant for it.
Beyond its own name, an identity may also answer to one or more roles
— full <project>/<role> names it has bound (e.g. "quantum/coordinator")
— so a message addressed to a role reaches whichever instance currently holds
it. A role is matched exactly like a name (a case-sensitive glob), so a role
target is exact and a group glob still covers it.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
target
|
str
|
The recipient field: the broadcast keyword |
required |
name
|
str
|
The reader's own agent name, e.g. |
required |
roles
|
Iterable[str]
|
Additional full |
()
|
Returns:
| Type | Description |
|---|---|
bool
|
|
system_message(payload, *, hub_id, msg_type=MessageType.SYSTEM, target='all', now=None, **extra)
¶
Build a hub-originated system message.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
payload
|
str
|
Human-readable body of the system message. |
required |
hub_id
|
str
|
Identifier of the emitting hub, stamped into the envelope. |
required |
msg_type
|
str
|
One of the hub-side :class: |
SYSTEM
|
target
|
str
|
Recipient agent name, or |
'all'
|
now
|
float or None
|
Override timestamp, in seconds. |
None
|
**extra
|
Any
|
Additional fields (e.g. |
{}
|
Returns:
| Type | Description |
|---|---|
dict[str, Any]
|
A JSON-serialisable envelope with |
wakes(target, name, *, directed_only, sender='', priority=False, roles=())
¶
Return whether a chat to target should wake a waiter listening for name.
In the default mode any recipient match wakes (:func:is_recipient). In
directed-only mode only a directed match wakes (:func:is_directed) — except
that a priority-flagged message, or one from a :data:PRIORITY_SENDERS sender,
also wakes when it still reaches this waiter (a broadcast, or a message addressed
to it). So an "all" broadcast that genuinely matters (a CEO directive, a
flagged announcement) reaches a quiet waiter promptly, while routine peer
broadcasts stay suppressed — and a priority or CEO message directed to a different
agent does not wake one it was never addressed to. Directed-only means "no
routine broadcast wakes me", not "every priority message anywhere wakes me".
A message addressed to a role this waiter holds is a directed wake, so a
directed-only waiter still wakes promptly when its role — not just its instance
name — is addressed.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
target
|
str
|
The recipient field of the message. |
required |
name
|
str
|
The waiter's own identity. |
required |
directed_only
|
bool
|
When |
required |
sender
|
str
|
The message's sender, matched against :data: |
''
|
priority
|
bool
|
Whether the message carries an explicit priority flag. |
False
|
roles
|
Iterable[str]
|
Full |
()
|
Returns:
| Type | Description |
|---|---|
bool
|
Whether the waiter should wake on this message. |
decode_lite(lite)
¶
Reconstruct a full message envelope from a compact relay event.
Version-2 rows retain structured payloads and non-core fields. Legacy
version-1 rows keep their historical string-payload semantics and have no
extension data to reconstruct. Because both versions store timestamps as
millisecond integers, the reconstructed timestamp is precise only to
the millisecond. Missing or malformed short keys fall back to the same core
defaults :func:encode_lite emits.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
lite
|
dict[str, Any]
|
A short-key envelope as produced by :func: |
required |
Returns:
| Type | Description |
|---|---|
dict[str, Any]
|
A full envelope with the core fields and, for a version-2 row, every
non-core field encoded under |
encode_lite(message)
¶
Pack a full Synapse message into a short-key relay envelope.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
message
|
dict[str, Any]
|
A full message envelope as produced by
:func: |
required |
Returns:
| Type | Description |
|---|---|
dict[str, Any]
|
A mapping with compact keys: |
paths_overlap(a, b)
¶
Return whether two declared paths cover any common file.
Two paths overlap when, after normalisation, they are equal or one is an ancestor directory of the other. The empty (root) path covers the whole tree and therefore overlaps everything.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
a
|
str
|
Declared file or directory paths. |
required |
b
|
str
|
Declared file or directory paths. |
required |
Returns:
| Type | Description |
|---|---|
bool
|
|
scopes_conflict(worktree_a, paths_a, worktree_b, paths_b)
¶
Return whether two claim scopes contend for the same files.
Scopes in different worktrees never conflict. Within the same worktree, an empty path set means the claim owns the whole tree (conflicts with any other claim there); otherwise the scopes conflict when any declared path of one overlaps any declared path of the other.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
worktree_a
|
str
|
Worktree labels of the two claims. |
required |
worktree_b
|
str
|
Worktree labels of the two claims. |
required |
paths_a
|
Sequence[str]
|
Declared paths of the two claims (empty means the whole worktree). |
required |
paths_b
|
Sequence[str]
|
Declared paths of the two claims (empty means the whole worktree). |
required |
Returns:
| Type | Description |
|---|---|
bool
|
|
__getattr__(name)
¶
Resolve a public name on first access and cache it on the package.