Repository navigation
Releases: yexrob/bingo
Release list
v0.6.6
Feishu answer metadata
- Add an English-language footer to finalized assistant answers with the model, reasoning effort, current-turn token and cache usage, and context utilization when available.
- Keep the footer within Feishu's text budget and outside code blocks, including when a long answer is clipped or a partial answer ends inside a Markdown fence.
Reliable long-running chats
- Renew streaming cards before Feishu's ten-minute timeout and recover both timed-out and closed streams.
- Apply the same renewal and recovery path to the final write, so a quiet long-running turn does not fall back to a duplicate plain message merely because its card expired.
Command results in the chat
- Display slash-command results and errors in the requesting chat, including commands queued behind an active turn.
- Render supported command tables as Markdown tables and keep LF, CRLF, and CR line endings within their cells.
- Apply platform length and formatting limits to command results and their fallback messages. Keep the new UI labels in English.
Thanks to @contsun for the Feishu contributions in #91 and #92.
Full changelog: v0.6.5...v0.6.6
v0.6.5
Bounded session transport
- Add opt-in, byte-bounded session heads, history pages, child discovery, snapshots, and live-event delivery for reference-aware RPC clients, including Rei v0.1.1.
- Retrieve oversized items, snapshot fields, and events explicitly through scoped, immutable references and UTF-8-aligned parts. Expired references, quota exhaustion, stale generations, and responses that cannot fit the requested budget fail explicitly.
- Keep existing TUI, print, legacy RPC behavior, and original journal content intact. This release bounds transport; cold resume still replays the stored journal.
Feishu channel controls
- Map
/newto a fresh conversation session and/stopto an interrupt, without sending those controls as model prompts. Configure additional aliases underchannels.feishu.commandMappings. - Persist released and renamed routing keys so a fresh session retains its route across restart and the previous session remains recoverable by ID.
- Preserve the previous streamed answer, including pending coalesced text, when switching sessions. Send stop notices separately and keep new-session confirmations in their originating thread.
- Retire old question cards when switching sessions.
Thanks to @contsun for the Feishu command-mapping contribution in #90.
Model configuration
- Honor explicitly selected thinking levels even when the model capability catalogue does not declare reasoning support.
Full changelog: v0.6.4...v0.6.5
v0.6.4
Scheduler recovery (M106)
- Replace the scheduler's PID sentinel with a permanent OS-locked file. Exiting or crashing releases ownership without leaving a stale modern lock behind.
- Already-running bingo processes stand by and retry once a second, automatically taking over when the owner leaves.
- Wait for an in-flight dispatch to finish before releasing ownership during shutdown. Cancelled scheduler and wake loops do not begin new deliveries.
- Distinguish a running owner, standby, migration barriers, and storage errors in scheduler status.
gateway doctor --fixpreserves modern lock files whether held or free.
One-time scheduler upgrade procedure
Older releases used a different locking protocol. Stop all bingo processes sharing the data directory before migrating an existing legacy schedules/runner.lock. Run one bingo gateway doctor --fix to remove a dead PID sentinel, then start the upgraded binaries. Inspect empty or unrecognized legacy files before removing them. Do not run concurrent repair commands or mix old and new scheduler binaries.
Once migrated, the modern runner.lock is permanent: do not delete or replace it while bingo is running. Normal exits and crashes release the OS lock automatically. This release does not change channel locks, the 60-second cross-process schedule-directory rescan, or expired one-shot creation behavior.
Settings and plugin controls (M97–M101)
- Read and write settings as TOML, preserving comments when updating a setting. Existing JSON settings migrate to TOML at startup, with the old file retained as a backup and the paths reported.
- Inspect and persist plugin switches through
/plugins(alias/modules) andbingo plugins list|enable|disable. Disabled external plugins are not spawned; plugins required for the current configuration cannot be disabled. - Add the configurable persona plugin and its
guide-personapage. - Complete command arguments after the verb, including plugin names, and update bundled guides for the new configuration format and controls.
Sub-agent management (M102)
- Stop a running child with
StopAgentor/agents stop, and dismiss an idle child withDismissAgentor/agents dismiss. - Reopen an existing child with
SpawnAgent { reopen: true }, retaining its session and memory. - Show each agent's model, message count, and token usage in the roster.
- Keep child transcripts across replayed close events and remove deleted children from the TUI tree.
Images and file access (M103–M105)
- Remove image payloads from subsequent model requests after three assistant responses, retaining a textual reference and the original journal/transcript record.
- Bound images before sending them to the model, reducing repeated large uploads.
- Report when
Readreturns a bounded rendering and identify the source file's type and byte size. UseRead { original: true }to inspect the original within the journal's size cap.
Terminal fixes
- Continue walking command history past a recalled command.
- Enable the required tmux passthrough mode for picture rendering.
Full changelog: v0.6.3...v0.6.4
v0.6.3
A request is cut for silence, not for size (M93, ADR-0056)
- A slow upload completes. Both HTTP providers used to end a request 60 s after
send()began, upload included, so a long session on a slow relay was cut mid-body and retried forever while the relay logged a 400 "Failed to read request body". Now connecting has its own bound (20 s) and everything after it is cut only when no byte has moved on the wire in either direction for 300 s. The body goes out metered in 64 KiB frames, keeps itsContent-Length, and the sign-in-again retry still works. - A reasoning model that is quiet for under five minutes is no longer cut either.
A sleep is not a wait (M94, ADR-0018 §9)
- A bare foreground
sleepis refused with the way round: end the turn and let the job or agent wake you, run the samesleepwithbackground: trueas a timer, or put the check in the same call when a pause must precede it. The Bash description and every job-start receipt now say that ending the turn is how you wait.
The window follows the stream (M96, ADR-0018 §8)
notify_allno longer waits a fixed thirty seconds. The quiet window starts at zero, so a slow stream wakes the session on every line; a burst grows the window by its observed rate (two seconds per line per second, capped at thirty); a line held inside the window goes out when the window ends instead of riding the next hit, so the delay is bounded by the window the notice names.- Every notice says its pace: the clock time, the lines and seconds since the last notice, and how soon the next can come.
notify_quiet(milliseconds) pins the window when the model would rather choose; it is refused withoutnotify_all.
The foot is a row away (M95)
- While the transcript is scrolled back, the activity band's first row carries
↓ 37 lines below · end or click to follow, centred on the raised bar. A click on it follows the tail again, asenddoes. It rides a row the frame already holds, so nothing moves.
v0.6.2
Rooms: one purpose, four verbs (M88, ADR-0053)
- A room says what it is for.
OpenRoomtakes apurposeand refuses a name that is already standing; reopening a room can no longer silently evict its members. The reading header shows it:[#design — <purpose>, since you last read]. - Seat, Unseat, CloseRoom. New tools for the holder or opener, each a signed post in the room; an unseated member is told.
/room close <name>in the TUI. A post into a closed room is refused. - A bounce is cheap to retry.
SendMessage { to, again: true }re-posts the latest bounced draft without regenerating it.
ACP agents keep their own context (M91, ADR-0055)
- An ACP agent that holds its context is measured and compacted by the agent, not by bingo: no more futile compactions or
2,367,503/191,808 · /compactfrom a turn bill read as context. The status line shows the agent's own count, the agent's compaction lands as one row, and/compactis refused in words (exit 1 in--print).
The selection follows the hand (M92)
- Dragging a selection past the top or bottom edge scrolls the transcript with it; releasing the mouse copies and says
copied N lines.?lists it asv · drag.
Also since v0.6.1
- Esc ends a turn at once — an interrupt is now seen at every await of a round, not only mid-stream; the multi-second stalls at turn start and between rounds are gone.
- A picture is a file (M87, ADR-0052): a pasted or Feishu picture is written to disk first, and the model is told its path.
- Every plugin writes its own page (M90, ADR-0054):
guideis a short map; each plugin's page is aguide-<name>skill (acp, agents, channels, experience, hooks, mcp, memory, permissions, rewind, rooms, schedule, skills, tasks, tui). - Prose fills the terminal (M89): the fixed text measure is gone;
tui.measurecaps it if you want one.
Fixed
- An MCP server's tools keep one order across reconnects.
v0.6.1
Feishu: the chat carries more than words (M86, ADR-0051)
- Every message is heard. Rich
postruns (links, code blocks, dividers, emoji, mentions), interactive cards, shared chats, merged forwards and stickers all reach the model as words; no message type is dropped any more. - Attachments in. A file, voice note or video lands under
<data_dir>/channels/feishu/attachments/<message>/<name>and the message carries its path; a text or markdown file up to 100 KiB is inlined. Merged forwards are fetched (im:messagescope) and flattened under the message. - Files out. A new
SendFile { path, caption? }tool posts a file from disk into the chat the session lives in; it exists only while the channel surface runs. Feishu routes pictures, opus audio, mp4, office documents and everything else to the right endpoint. - Who may speak.
channels.<adapter>.access— admins, a rule for direct chats, a rule for groups, per-chat overrides; policiesopen | allowlist | blocklist | admins | off, andmention: falseengages a group on every message. The default is what ran before. - A working sign. The message you sent gets a
Typingreaction while the bot works, loses it when done, and a ❌ marks a failed turn (im:message.reactions:write_only).
Fixed
- The permission card was refused by card JSON 2.0 (
actioncontainer), so every permission had fallen back to a numbered list; buttons are a column row now. - The streaming card never closed (
settingsmust be a JSON string), so every answer was posted a second time as plain text.
Also since v0.6.0
- Memory has one writer (M83, ADR-0049): the model writes memory; the extractor is gone.
- The worked row (M84):
✻ Worked for 15m 11s · done 13:48closes the transcript. - MCP servers that need signing in (M85, ADR-0050): OAuth 2.1 for HTTP MCP servers —
/mcp login|logout|tools,bingo mcp list|get|add|remove|login|logout. - A command lists the words its argument takes; the command completion channel is gone.
v0.6.0
Thirty-seven commits since v0.5.4. Two of them change what a model or a plugin sees, so the minor moves.
Agents
- The wait is the turn's end.
WaitAgentis gone. A parent that started a background agent waits by ending its turn; the reply wakes it. A join tool besideSpawnAgentwas what the model reached for first, and every call held the person's turn on a wait it did not need. Plan M81. - Say what an idle session is waiting on. Between turns, while agents the session started are still at work, the activity row reads
✻ Waiting for 2 background agents to finish, breathing at the pace a tool holds a turn. Before, the row left with the turn and the parent looked finished. Plan M82. SpawnAgentsays things once. Its model-facing text is 16 % shorter; nothing it taught is dropped. The room protocol (read at the head of a turn,@nameand@allwake you, post the result back) now comes from the rooms plugin itself, only to a session seated in a room, and is journalled so a compaction says it again.
Context
- Stable request prefixes. Project instructions and memory have stable knowledge baselines with explicit refresh boundaries; internal context stays in the journal with its provenance but out of ordinary UI; compaction reuses the parent request's prefix; OpenAI requests carry a session-derived
prompt_cache_keyand cache-write tokens are counted once. Shell guidance chooses foreground or background by dependency, not duration. Plan M80. - Experience stays on by default. Set
experience.enabled: falseand the plugin registers nothing: no tools, no command, no prompt lines.schedule.wakes: falselikewise unregisters theWaketool instead of offering one that refuses.
Fixes
- A root session the client fell behind on no longer ends the whole tree stream in silence.
- The bash plugin ends every background job when it stops, on the same grace a kill gets.
- The ACP proxy exits when its bridge closes instead of hanging on an outstanding stdin read.
- Print offers only the permission answers the interaction actually carries;
yon a prompt without an allow-once rung is a refusal, not an error from the kernel.
Inside
- The gateway daemon is its own crate,
bingo-gateway; the binary keeps only composition. - The plugin RPC wire holds at protocol 1 until a plugin outside this repository speaks it.
- The Windows
cargo checkjob is blocking; tests there stay advisory until the shell dialect is decided. - Nine ADRs say what is true now, with dated lines for what they said before.
v0.5.4
One change since v0.5.3.
- A run of thoughts is one thought. Some endpoints close a reasoning item and open the next as the model goes on thinking, so one long think arrived as ten
✻ Thought for Nsrows in a row. Adjacent thoughts now draw as one block:✻ Thinking…while the last is still being had,✻ Thought for 3m 21sonce it is over, the newest two rows of the whole run under it, one click orctrl+ofor all of it. The journal and every other surface still carry the items one by one. Plan M79.
v0.5.3
One change since v0.5.2.
- A finished thought holds where it ended. v0.5.2 fixed the height of the block under
✻ Thinking…while a thought streams; the close still took its two rows away, so everything above the block dropped by two when the thinking was over. Now the heading becomes✻ Thought for 2sand the two rows under it stay exactly as the last delta left them — no cut mark, no new row, nothing above moves. A click still goes round: the whole thought in place, then the row alone, then back to the two rows. Plan M78.
v0.5.2
Three changes since v0.5.1.
- A streaming thought holds its height. The two rows under
✻ Thinking…were the last two lines of the thought, wrapped afterwards — a paragraph is one line, so the block was any number of rows and changed on every delta, and the whole transcript above it moved with it (reported on Windows, where a narrower console made it worse; the same everywhere). The tail is now measured in rows as drawn: exactly two while a thought streams, three under a running tool, whatever the width. Plan M77. - A spawn names the level it thinks at.
SpawnAgenttakesthinkingbesidemodelandprovider, a child inherits the parent's level unless told otherwise,offis a level, and aSetThinkingtool lets an agent move its own or a child's mid-run. The level rides the session spec, sosession/openon the wire and the next turn agree. ADR-0047, plan M76. - An attached client may open the browser itself.
BINGO_BROWSER_MODE=clienttellsShowPageand login to hand the URL to the client instead of spawning the platform opener;BINGO_NO_BROWSERstill wins. ADR-0046.