cyberine
Usage

Durable teammate

Enables two complementary approaches so a teammate can receive and act on messages even after it has gone idle or exited. Both share the same core handler (src/store/durableTeammate.ts). Default OFF — nothing changes for existing users unless they opt in.

Record correction (k7hma, 2026-09-04). The archived task for this capability (T489) still carries its original note: DEFER (L5), written before the work landed and never updated — read alone it says durable teammates were deferred. They were BUILT: cyberine teammate <team> dispatches option A (src/cyberine.ts → src/commands/teammate.ts → runStandbyTeammate), and cyberine daemon runs option B's scanner when CYBERINE_WAKE_ON_MESSAGE=1 (startWakeOnMessageTicker, src/supervisor/daemon.ts). The archive is frozen by project rule (.claude/rules/frozen-archive.md) — never edited, never authority for a live decision — so the correction lives here, where "what is true now" is supposed to be read.

Who can be addressed, and for how long

Precise boundary, because "send a message to a teammate" hides three different lifetimes:

  • A live process (chat session, running run, standby teammate) is addressable by handle, name, role or team for as long as it heartbeats. Stale means now - lastBeat > ttlMs (60s default, one missed 30s heartbeat). Past that, and after a graceful exit tombstones its record, it is gone from the roster: /team send <handle> and the planner's message_session both fail rather than queue, and the handle is free for the next claimant. There is no registry of past handles.
  • A finished run has no live process at all, so nothing reads its inbox — UNLESS option B's daemon scanner is on, which turns a message to a terminal run's inbox into a NEW background run seeded with that text. That is a respawn, not a delivery: the original run does not resume.
  • A standby teammate is durable only because it is a long-lived process; kill it and it is as unaddressable as any other exited session until restarted.

Regression guard for the first bullet: npm run selftest:session-handle asserts a tombstoned session stops resolving and its handle becomes claimable again.

Option A — standby subscriber (cyberine teammate)

A long-lived process that registers presence in a team, subscribes to the team broadcast channel and its own mailbox, and dispatches a background run for every inbound note/steer message. Heartbeats presence every 30s; tombstones on SIGTERM/SIGINT.

# Start a standby teammate in team "eng" (runs until SIGTERM)
cyberine teammate eng

# With optional labels (visible in roster / cyberine agents)
cyberine teammate eng --role worker --name coder-1 --capabilities coding,review

# Discover live teammates in that team
cyberine agents --once          # fleet view shows presence records

The teammate's presence is queryable via resolveAddr({ team: 'eng' }) and roster({ team: 'eng' }) for capability-based routing.

Option B — wake-on-message respawn (daemon scanner)

Add CYBERINE_WAKE_ON_MESSAGE=1 to .env (or export it) before starting cyberine daemon. The daemon then polls terminal run inboxes every 10s and dispatches a new background run for each unseen note/steer message it finds.

# Enable the scanner in the daemon
CYBERINE_WAKE_ON_MESSAGE=1 cyberine daemon

# Tune the scan interval (default 10s)
CYBERINE_WAKE_ON_MESSAGE=1 CYBERINE_WAKE_SCAN_MS=5000 cyberine daemon

# Send a message to a finished run's inbox (triggers respawn on next scan)
# From cyberine MCP tools or busTools:
#   sendBusMessage({ kind: 'run', id: '<terminalRunId>' }, 'retry the failing test suite')

Dispatched runs carry parentRunId lineage to the original terminal run so cyberine agents shows the chain.