Skip to content

Latest commit

 

History

History
664 lines (529 loc) · 157 KB

CHANGELOG.md

File metadata and controls

664 lines (529 loc) · 157 KB

Changelog

All notable changes to FlyBot (earlier: DS Bot) are listed here. The format follows Keep a Changelog, and the versions follow Semantic Versioning. Before 1.0, a minor version can contain breaking changes. Entries from the historical 0.1.0-dsbot preview and earlier describe the pre-FlyBot DS Bot lineage.

New entries are written as one fragment file per change in changelog.d/ and folded into a release section at release time; [Unreleased] below holds entries written before that.

[0.2.11] - 2026-10-27

Added

  • Settings has a Workflows page: the factory presets, your own definitions as workflows/<id>.json in the workspace, and which one this project uses. A definition is edited as a node table — id, name, role, inputs, outputs, guards, refusal — with the flowchart drawn above it from the same table and each guard showing the sentence it would print right now, or saying the fact could not be read. Customize copies a preset into a new file and never edits the shipped one, and saving an unedited definition writes identical bytes (#1272, step 3).

Changed

  • A finished task card leaves the task board's active list, and the Done column shows the newest 20 archived cards; a group's overall card now completes when its last child finishes, is cancelled by a rejected approval, or fails because its Bot was deleted, and a Bot's board asks for that Bot's own finished cards (#1252).
  • Feishu (Lark) is now connected on Settings → Connectors (左下角「连接应用」), not through the Secrets page or a config file. The one Feishu row offers two modes — a custom-bot webhook (outbound only, as before) or a self-built app with its long connection — and renders one field per credential: the App ID is kept in the record because it is not a secret, the App Secret only in secrets.json. App mode proves the pair with one tenant token, opens the socket from the row, answers a message back into the same Feishu chat as the app, and offers Choose Bots; the row also carries 允许的飞书用户 — nobody by default, the sender who was turned away listed with Allow, and a field to paste an open_id — so a stranger's message never spends the owner's tokens. Disconnect closes the socket and clears both credentials. A replayed event_id still lands once, and a half-saved pair dials nothing and says so. The app pair is no longer read from FEISHU_APP_ID on the Secrets page, and feishu is now a reserved capability name. (#1266, #1292)

Fixed

  • Claiming or assigning a card in a group now posts the one public line the room notice asks for — 接了 #1253,第一步是 … — in the claiming member's voice. The line goes into the room log without opening a round or waking anyone, takes the card's own note as the first step, and is not repeated for the same card within 30 minutes; progress stays on the card. (#1253)

[0.2.10] - 2026-10-27

Added

  • A group's board can now be a pull queue: the lead enqueues an item for a role (track_tasks with queue and role, no assignee), an idle member of that role claims it with claim and one item is handed to exactly one member, and a member that cannot finish returns it with a one-line reason and a retryAt; the room board shows one line per role with its depth and oldest wait (#1247).
  • The delivery board now reads the repository this instance actually works in, and the header says which one it read and where the name came from: delivery.repo in the instance settings first, then the workspace's git origin, then the package's own repository as a labelled default (#1250). An instance with none of the three refuses with no repository — set delivery.repo instead of showing another repository's numbers.
  • Feishu (Lark) connector, phase 1: paste a group's custom-bot webhook URL on Settings → Connectors and this instance can post a text message into that group. The row is a sender — no Bot tools, no per-Bot switch — it proves the webhook with one test message when you connect, shows the reason on the row when Feishu refuses, and sends on demand with the existing retry policy (a rate limit is retried, a refused signature is not). The URL is kept as a secret and never appears in state.json, the view or a log line. (#1263)
  • The Delivery board and the task board now draw their cards through one component, and a card says which side it came from: a repository card is keyed owner/name#number — one key for an issue and the pull request that closes it — and a task card task:<id> (#1265, first step).
  • The Delivery board can read more than one repository: delivery.repos names a list (the older delivery.repo still works as a list of one), every name resolves with its own source, and the page draws one block per repository with its own state. A repository that cannot be read leaves the others on a partial board instead of emptying it (#1265, step 4).
  • Feishu (Lark) connector, phase 2: a message the owner types in Feishu can start a turn here over the long connection. The app pair FEISHU_APP_ID / FEISHU_APP_SECRET is saved through secret-set and stays out of state.json and the view; the connection is off until feishu.enabled is true, dials only from an app pair that is complete, and only the owner's messages start a turn. A replayed event_id lands once — the id is remembered only after the message reached its chat, so a handover that failed is retried rather than dropped — the message arrives through the same chat seam a friend's Bot uses, and the answer leaves again on the same FEISHU_WEBHOOK the connector already posts with, so one chat has one way in and one way out. (#1266)

Changed

  • A group turn now shows who is working: one line per member that holds an open card on that room's board, with the card number, column, progress and how long it has been open, and an explicit idle (no open card) line for a member that holds none — read from the board at the turn, with nothing new written and no one having to post a status (#1213).
  • A Bot condenses its conversation earlier: the steward's check line is now 45% of the model's usable window instead of 80%. Every call re-sends the fixed block and the conversation is most of each prompt, so condensing sooner cuts the prompt of a long turn by about a quarter; the cost is that a checkpoint arrives sooner and a condensed conversation keeps less detail. checkRatio still overrides it in the profile (#1233).
  • A round-trip now exists to bring back a fact the room did not have: independent reads go in one call, a fact already read is quoted with its anchor instead of fetched again, and the same message carries the tip sha, the CI state and the files read, because every avoidable round-trip re-sends the whole context (#796, #1235).
  • A group chat now carries neither the composio tools nor the four schedule_* tools: eight tool schemas, about 20.4k characters re-sent on every call of every group turn, that a room round does not use. A Bot reaches an outside app and manages its schedules in its own chat, and the refusal says so. The github tools and every built-in tool stay in the group, and a Bot's own chat is unchanged (#1237).
  • A connector call refused in a group room now says the tool is not available there, instead of pointing the caller at its own chat — a friend's Bot has no composio tools in its chat either (#1271).

Fixed

  • The delivery header prints its "as of" time once while today is still running: the partial-day hint now follows the single time instead of repeating it from a second clock rendering, in both English and Chinese. A day whose runs the host could only count by their total now shows the same red share in the header row as the point drawn on the line beside it, instead of a dash next to a dot (#1244).
  • A part handoff keeps a repeating task exactly where it was: the same id, the same delivery history and the same instant it was already due, instead of re-anchoring the next run to the handoff and hiding the move in a reset history. (#1245)
  • The delivery board reads delivery.repo however the instance wrote it — nested (delivery: { repo: … }), as a literal dotted key, or flat (deliveryRepo) — instead of only the flat one, so the setting the page and the hint name really names the repository (#1258). A workspace that is a linked git worktree now resolves its origin through the worktree's commondir rather than falling back to the package default, and a repository name with a . or .. segment is refused instead of being turned into /repos/../../issues.
  • A line sent into a group chat while a member is speaking now reaches that member inside its turn: the round watches the same Session queue it drains, logs the line at once and steers it into the speaker's running turn with a header saying it arrived after the turn started, so the member's next step answers it instead of reading it after a long turn as if it had been there all along. The line leaves the relay queue, so it is neither logged nor answered twice. The one exception is an explicit stop (停, 停下, 停止, 别做了, 不要做了, stop, stop it): the speaking slot is marked stopped by the user, its turn is cancelled and its answer is not posted, and the line stays queued to be answered as a fresh line by the room (#1267).
  • A Bot whose tool list has to be rebuilt for a group room no longer ends up with every tool listed when one name in the mask cannot be restricted: the host keeps the narrowing it can enforce and warns, instead of dropping the whole mask after lifting it (#1271).
  • A turn no longer stays open because making room before its next step never returns: the pre-step compaction now carries a deadline (COMPACT_DEADLINE_MS, config.compactDeadlineMs, five minutes) beside the turn's own signal, and a summary request still retrying when it passes ends that turn with a warning instead of parking it — turn/end runs, the queued messages stay for the next turn, and stop_bot is no longer the only way out (#1285).

[0.2.9] - 2026-10-26

Added

  • The delivery board draws the last seven days instead of two numbers per metric. New issues, Closed issues, New pull requests, Merged pull requests and the CI red share all get a 168×28 line beside the existing Today / 7 days columns, where a count's week number is a Σ total and the CI share, a ratio, never takes one. Every point carries a title, a day that could not be read breaks the line instead of reading as zero, and a total over an incomplete window wears a star the footnote explains. Below 900px the line moves under its row, and a snapshot without the series says so in one line instead of drawing empty charts. (#1212)
  • The delivery board's numbers now come with the seven days behind them: metrics.daily carries one entry per Asia/Shanghai day, oldest first and today last, each with the issues opened and closed, the pull requests opened and merged, and the CI share of the runs that actually counted that day. A day the host could not read is null rather than 0, so a gap stays a gap instead of reading as a quiet day, and a week that could not be read at all leaves the whole series null instead of drawing seven zeros. The seven-day CI share is read over the same week its own row covers — it used to be a week out of step, because the CI windows were walked backwards from the start of the week. (#1222)

Changed

  • A claimed card now gets one line in the room — Claimed #1234 — first step: … — so a member who is working is not read as idle; progress still lives on the task board and nothing else is narrated (#1213).

Fixed

  • A delivery board that cannot read part of GitHub now says which part, why, and what to do about it, instead of looking like an instance that was never connected: the first load counts its seconds, a failed refresh keeps the old board with the reason beside it, a round that runs past 90 seconds lands with what it has, and a CI number that could not be read is unknown rather than 0. A 诊断 button and core/scripts/delivery-probe.mjs print the same five endpoint statuses, and after a failure the next attempt backs off 10 s, 30 s, then two minutes without dropping the reason. (#1211)
  • A window that only reads the Bot team — because another dsh holds it — can open the delivery board and its probe again instead of being told bot/read-only; both are reads, and every write in that window is still refused. (#1225)
  • The failure screen now counts down instead of freezing: Retrying in 120 s drops a second at a time while a retry is really scheduled, and a fresh or loading board starts no timer at all. The rate-limit and timeout hints also say who retries — while this page is open it retries on its own — because the host keeps no timer and a closed page retries nothing. (#1227)

[0.2.8] - 2026-10-26

Added

  • Each Bot keeps a task list it updates from the conversation, and a board shows Not started, In progress and Done for that Bot, a child Bot, and — for a group admin — the tasks member Bots are tracking in the group (#1058).
  • Settings → Bot Social now lists friends and links each of your Bots to People with access, so sharing and revoke are findable without opening each Bot first. Add friend stays on the + menu, and the same panel is also on that page (#1074).
  • A group's Tasks board shows one overall task and the per-Bot subtasks under it. Bots record that plan with track_tasks, continue the highest-priority unfinished task, and ask the user or the group when a task is waiting on an answer (#1099).
  • A Bot with unfinished conversation tasks keeps working without a new message. The host starts a continue turn for the next task, in that Bot's own chat or in the group session that owns the subtask, and stops when the work is done or waiting on an answer. In a group, members with their own subtasks are woken in the same pass (#1109).
  • A task that names the issue or PR it came from now says where your answer goes, and the Bot writes it there itself (#1154): the result card shows a Comment goes to … link, accepting or sending back leaves one comment for the Bot to post with its own GitHub connector, and a comment that could not be posted says so once on the card instead of failing silently.
  • The team can set quiet hours — a window (off by default) that a new or edited scheduled task's time must fall outside, checked when it is saved rather than at delivery: a task inside the window is refused with a sentence naming when it ends, and nothing is created or moved. Repeat and cron tasks are still allowed and the page says they may run inside the window, because DSH decides when they fire (#1166).
  • The task board dialog gains a Delivery view (#1173): one read-only snapshot of the repository's GitHub issues, pull requests and CI runs, in five columns — original issue, ready to start, in progress, in review, and done today — with a verdict/CI badge per pull request and a header that reports today against the last 7 days. It is repository-wide, so the per-Bot rail does not appear, and it never writes to GitHub: the host serves a cached snapshot built only from GET requests, and the token stays host-side. The view replaces the board with one honest line when no GitHub connection is set up.
  • A CI scan (.github/scripts/check-mojibake.mjs) now fails a pull request whose tracked text files contain U+FFFD — the three bytes EF BF BD a lossy decode writes in place of every character it could not read — and names each file with its occurrence and line counts, so the fix reads as "regenerate", not "retype the text". Binary assets are skipped by content (a NUL byte in the first 8 KiB), which is the rule that makes the gate usable at all: decoding the tree instead reports 22 binary files and ~2,065,422 replacement characters on a clean main. AGENTS.md now says why a file rewritten from an outside source, or regenerated, is re-read as bytes and re-encoded by its generator. See #1188.
  • A Bot can be spoken for by a supervised external process in rooms (#198): create_bot takes an external block (driver: subprocess, an argv array, optional cwd and env), and room-participant.js routes that member to a new driver while every other member keeps its DSH Session. The process is started per room and Bot, answers one JSON line per turn, and its start, clean exit, crash and timeout are explicit states — a crash is posted as the Bot's own line in the room, and a turn that reaches a dead process ends with a failure instead of silence.
  • A finished task can now be accepted or sent back right in the inbox (#401): the result card shows the Bot's evidence links and model, Accept closes the task as done, and Send back asks for one line about what should change and returns the same task to the Bot.

Changed

  • Removing one of a friend's Bots now says which link keeps the friend in the list: the question names the Bots of theirs that are still shared ("Bob stays a friend: still shared through Writer."), instead of only saying the owner is not unfriended and leaving the row unexplained (#1001).
  • The new-chat menu offers joining and starting as two rows, so one label no longer names both: Join by number or link carries the number and the invite link, and Start a face-to-face room opens the same screen under its own name without the link and scan fields, because those are ways into a group that already exists (#1021).
  • A fresh 6-digit code no longer answers with an empty-result line. When nobody is waiting, the dialog's primary line is Start a new room with … and Start a new room is the first button; the note under it still says to ask for an invite link if that room already opened (#1053).
  • A face-room line the server sends without an event_id is still keyed by the line itself, but the key now carries a bounded digest of the text instead of the whole sentence: seen lives in the owner's settings, so on a server that stamps no ids it would otherwise fill up with a copy of the conversation (#1062). The key stays stable across polls, so the duplicate #1045 fixed does not come back.
  • A Verdict: now counts only when the workstation that claims to have written it signed it: each reviewing workstation holds one Ed25519 private key, its public key sits in .github/agent/signers.json, and the gates verify Signed-by: / Nonce: / Signature: offline over the pull request, the full tip SHA, the verdict and the workstation — so a PASS written in a Reviewer's name on the shared account is no longer a conclusion, it is listed as unverified (#1110).
  • The social panel's group and dock surfaces are usable at phone width: below 640 px its buttons and inputs (including the invite-preview and waiting-room footers) are 40 px tall, its row controls at least 32 px, a long unbroken run in a held reply wraps inside its card, the dock clears the home indicator, and the phone block now closes the stylesheet so no later rule of equal specificity can override it (#1147, #1149).
  • A group picks one task lead to divide the board. That Bot, or the admin when none is picked, assigns each part; the card is claimed and the assignee starts it in the group without waiting for a confirmation. A question stays in the group (#1182).
  • The merge gate now refuses a tip whose only review is a comment from the pull request author's own GitHub account. A PASS from that login counts when the comment header carries Attestation: <full sha> role=<Bot> for this tip, the role matches Written-by, and the role is not an Owner of the pull request. A review posted by a different GitHub account is unchanged (#644).

Fixed

  • Accepting a friend's invite again now shares your Bot back with them (#1000): the two of you can message each other right away instead of one direction staying silently absent until they share again.
  • Coming back to a group nobody has spoken in yet shows the invite link the room already has, instead of minting another one on every visit — the strip now reads the room's links first, as the Members card does, counts a dated link live until its deadline passes, and never keeps the previous room's link when the dock moves to another group (#1019).
  • Re-accepting an invite from a Bot Social server whose invite info names no bot_id now grants to the peer id list_friends lists instead of a friend_home the server never receives (#1024): the pair becomes two-way again, and when no listed friend names that home the accept no longer claims a share that did not happen.
  • A window that showed the Bot team read-only because another dsh held state.lock now takes the lock on its next write once that window closes, instead of staying read-only until a state read happened to refresh it (#1027). New work that changes the state outside a request belongs in rt.mutate(fn), which follows what the other host left before applying the change, so neither side's work is lost.
  • The Add friend result no longer stacks a second "Your" on a Bot whose owner already named it that way — a Bot called Your Bot reads as "Your Bot is shared back, so they can reach you too." instead of "Your Your Bot …" (#1037).
  • The group strip no longer offers Invite by link before it has read the room's own links, so a fast click cannot mint a second ticket the panel was about to discover, and a chat that draws no strip (a face room, a Bot chat, a chat with turns) no longer issues that read at all (#1038).
  • A turn that dies before writing anything is no longer invisible: when a model call is refused (a provider budget cap, a 429), the chat shows one line naming the provider's own error above the composer, and a run of them says how many turns in a row died, so a quota outage reads as an outage instead of a Bot that chose to stay silent. A turn that already wrote text adds no line, and a completed turn clears the count (#1044).
  • A face-room line the server sends without an event_id is posted into the room once: the poll keys it by the line itself (type, sender, server time, text), instead of appending a second copy on the next poll — the duplicate the room drew as the same sentence twice, often seconds after both people had left (#1045).
  • A window that was read-only only because the other dsh had the team open now takes the free lock for the writes that were still inline, instead of dropping them or refusing with the read-only error: a friend's first line and the unread mark, a team exchange, and every DSH Agent change (add, adopt, archive, remove). The change the other host left survives, because the write adopts and re-resolves its records before it applies (#1048).
  • Typing a room number that is not exactly six digits keeps Join disabled and shows "Enter 6 digits" under the field (Chinese: 请输入 6 位数字). An empty field stays quiet, and six digits still join (#1052).
  • When someone accepts your invite, the sidebar says who joined and People with access lists them even if the grant list is still empty, with one step to ask them to share a Bot back (#1054).
  • Two identical lines from one person inside the same second in a face room now both appear: a line the server stamps in whole seconds is told apart by its own cursor (or, without one, by its place in the snapshot) instead of being read as the same line twice, so the second one is no longer dropped (#1063).
  • A group invite link seats the guest's Bots when they claim it, even if the accepting knock never arrives, and the group opens in the guest's sidebar. A claim the host cannot finish is named on the group's Members card (#1064).
  • A friend DM shows when the other home's Bot could not reply (for example it has no API key) instead of staying on "Weighing the options…" (#1068).
  • A friend's Bot that shares a local Bot's display name no longer wears that local Bot's picture: two default Chiefs both store the blue whale, and the friend's row now steps to a different shape while the local Chief keeps its own look (#1069).
  • A face room that Bot Social no longer lists is marked Closed on the hosted room, so it leaves the active sidebar list and stays as one Past rooms row. When Bot Social sends closed_at, that time is the row's time on every home (#1070).
  • People with access asks before Revoke (Revoke <name>? with Revoke and Keep, naming the rooms that share is used in), and the row separates the handle from "In use in N rooms" with a middle dot (@handle · In use in 3 rooms) instead of running the two together (#1073).
  • Enter chat in a face-to-face room no longer shows "internal error" after the other person has joined. The start request sends an empty JSON body, which Bot Social accepts (#1085).
  • Dismissing an Enter-chat error and typing in the room composer no longer shifts the window sideways. The page and the app frame cannot scroll horizontally, and the error toast stays inside the viewport (#1086).
  • A friend's direct chat says "Waiting for their approval" (等对方批准) while you wait for their reply, instead of a generic thinking line such as "Sorting out the details…", so a reply their home is holding for approval is not presented as this side still working it out (#1087).
  • A friend's Bot in the @ menu and in a mention chip uses the same picture as its sidebar row. A face-room Chief no longer shows a handle-seeded green moon there while the sidebar shows the stored blue whale (#1088).
  • A face room you start alone shows your Chief's own avatar in the room header, instead of a blank grey circle, until someone else joins (#1089).
  • A message no longer disappears when the team's next write fails. The friend-line mark and the exchange record are written before the message is handed to the Bot, so a failed write (another window took the team over, or the disk refused) used to drop the whole line; now it is logged once and the message still arrives (#1096).
  • Face lobby names and face-room titles stay readable. Labels under the avatars wrap, and the sidebar, header, and session title show both names instead of cutting the second one to an ellipsis (#1102).
  • When the other side leaves a face room and that leave closes the room, the room says so once: the "left the room. Room closed." banner is the only line, so the short platform leave no longer appears next to it. A leave that leaves the room open still posts its short line (#1103).
  • Ordinary group lines to a friend's Bot no longer keep stopping on "Waiting for approval" after Auto-send is on for that pair (the card's Auto-send from now on, or Sharing → People with access). A line Bot Social held only because the text looked like an instruction (a word such as "token", with no secret, file, or instruction-override) goes out; a real finding still waits for approval (#1113).
  • An ordinary @ or reply in a shared group no longer stops on "Waiting for the owner's approval" when the other home has not turned Auto-send on. Seating both Bots in the room is the consent for that text. A file, a DLP finding, a risk flag, or an instruction-override still waits for approval. A face-room line held as policy still waits for the owner (#1118).
  • Starting FlyBot on DSH no longer stops before the Main Bot is ready: the task drive registers its handle with Cordis provide instead of a property write the host rejects (#1120).
  • A Bot that already shares a live local group, including a group a link seated, can @ the other home's seated Bot with the @…home mention, and each successive line is delivered in the group, without a separate Sharing grant. A direct message to a Bot outside every shared room is still refused, and leaving the group restores that refusal (#1126).
  • A Bot's existing group sessions follow a model change, including when the record already says that model is applied, so a turn no longer asks a provider the Bot was moved off (#1127).
  • Share with a friend used to report success for a Bot the other home no longer runs. A row whose Bot is gone from the roster that home is still listed in now says so — <Bot> was removed from that home. Sharing with it cannot reach anyone. — and its Share is disabled; a click that still reaches the host is refused with bot/removed-bot and sends no grant, so the list stops claiming a share that would reach nobody. The row is judged on both the peer id and the handle (list_remote_bots entries carry both), and a home missing from the cached roster, or a roster read that failed or came back empty, decides nothing — it shares as before rather than guessing (#1128).
  • Needs you rows in the Inbox can be answered there. An ask_user card shows its options and a short answer — one option sends at once, a multi-select toggles until Submit — a task that is waiting on input takes a short answer, and a listed tool approval still clears with Allow once or Reject, so Open is not the only way through a group that has no card (#1140).
  • A group @ inserts the seated Bot's name, and Bots that share a live group can message each other without a separate friendship. A deleted friend home is dropped from the room and its chat is hidden (#1141).
  • Two controls in the social panel that stayed at their desk size on a phone are now touch-sized: the face-to-face room header's small buttons are 40 px instead of 22 px, and the account menu's links keep a 40 px hit area instead of collapsing to a 12 px text run (#1156).
  • A group invite link seats the joiner even when the host's FlyBot has been open, and the joiner's sidebar shows the group. The host was re-reading every open link on each inbox poll and, once Bot Social answered "Too many attempts. Try again in 10 minutes.", previewing again before that cool-down elapsed, so a new claim was never seen; a room share whose text was outside 1–4000 characters was refused, and a join that stayed on an approval card was treated as already sent. (#1161)
  • The external room driver now ends a stop it is ignored: after { type: 'stop' } and a SIGTERM grace it escalates to SIGKILL, and a seat whose stop is still in flight is never handed the next turn, so a child cannot outlive its membership. A crash line carries the last ~200 characters of the child's stderr (flattened and capped) instead of only exit N, and the room-turn timeout reads as "timed out" rather than "process timeout" (#1164).
  • Removing someone's Bot from a shared group now drops that room on their home. The other home was still listing the room and could keep receiving and sending in it, because the leave was sent only after the seat had already gone and a later line could open the room again. (#1167)
  • A decision comment that never went out now stops asking for it (#1172): after two wakes the reminder ends, and a day later the card says the comment could not be confirmed instead of the Bot being woken about it for ever. A task that is still real work keeps waking the same way.
  • Pasting a group invite link for a room this home already holds no longer ends on "Asked to join": the preview now says the group is already in the sidebar and draws no Join card, when the link seated the room and when the host added one of these Bots to it by add-member (#1176).
  • Needs you cards in the Inbox show the whole ask. The title and the reason wrap, including on a phone, so a sentence is not cut off before the in-place answer (#1177).
  • The delivery board links an issue to a pull request only when the pull request closes it, and a verdict past the first hundred comments still decides the badge (#1189).
  • The quiet-hours warning on the Scheduled tasks page now renders the host's quietUnchecked flag instead of scanning the task list a second time (#1199), so the line and the check cannot drift apart, and it still appears at once after the window is switched on. The subsystem page no longer calls the window one team-wide clock: a task's due time is read in that task's own zone.
  • A group turn shows a character-budgeted room history, with long CI and merge essays collapsed, and a compact task-board snapshot. Assigning, claiming, and progress go through that board (overall task, subtask, assignee, column, and progress); @ only wakes a member (#1203).
  • A group card wakes its assignee in that Bot's own chat. Assigning, progress, and handoff are board writes; the group stays quiet, and a Bot @ whose open card already shows the line passes (#1205).
  • An @ that could not reach the other home used to be swallowed: when a person's line had to go out as the old envelope on a server that stamps typed lines, the other home read it as a Bot's line, so the named Bot was never called and only a log line said so. The room now gets one quiet notice naming the friend's Bot whose call was lost (No call went out for @x: this connector token may not attest typed lines, so the other home reads this line as a Bot's.); the words still go out, a probe that did not answer adds no notice, and a server that cannot stamp keeps the old behaviour where the labels still call the Bot (#997).

[0.2.7] - 2026-10-25

Added

  • A group made with Create group chat now shows its own strip above the composer while it has not been used yet — the group's name, who is in it, and Invite by link — instead of only a sidebar row; the first message hands the top of the chat back to the session header, so the two never appear together (#998).

Fixed

  • A room number is now exactly the six digits stored for a room: a stored value that is not one (1234567) is no number at all — cut to six it would name a different room — so the approval card, the waiting card and the room title fall back to the room's own name or to the code you typed. Two live rooms with no number and the same name no longer share one label: each card adds a short, fixed tag from the room id (the room Loft · a3f1), the same room keeping its tag on every render (#1005).
  • People with access now lists one row per Bot instead of one per person, named Bot (Owner) and carrying that Bot's handle, so a friend who runs two Bots shows two rows and each Revoke takes back only the row it sits on — before this, one Revoke removed every grant that friend had (#995).
  • A six-digit number whose room has already opened no longer answers "Nobody is using 482913 yet." — the prompt now says Nobody is waiting under 482913., points a late joiner at an invite link when the room already opened, and its button reads Start a new room, so nobody is led into opening a second room while their friend waits in the first (#999).

[0.2.6] - 2026-10-24

Added

  • Pasting or scanning a room invite now shows the room before anyone joins it: the join dialog asks the host for the preview and draws the room's name, its lead, how many people are in it, and how long the link lasts, with Join and Clear on that card. The name is cleaned before it is shown; a preview that cannot be fetched (a 404 or a timeout) drops the card and joins the way it did before, and an older Bot Social says invites need a newer server instead of trying a route it does not serve (#798).
  • A PASS posted as a review body whose Written-by: names someone outside writers in .github/agent/review-policy.json was silent: #797 made the pass label job say so for a comment, but a review body fired no job at all, so the reviewer saw no label and no note while review-gate and merge-gate both refused the conclusion. A separate read-only workflow (permissions: contents: read, the script from main, no secrets) now prints one warning and the same sentence in the run summary, naming the writer, the tip and the file to edit, and stays green (#801).
  • The sidebar gets an Archived (N) section: archived Bots still stay out of the default Bot list and out of the tree, but one collapsed list opens their chats like any other row, and says archived · merged into <name> for a Bot a merge replaced (#810).
  • Two create_child_bot calls that are in flight at the same time and belong to the same project or domain become one group: a single lead for that group, reused when it exists, with both new Bots under it, instead of each landing in whatever broader group it fits (#810).
  • Merging on the code fork is now refused by GitHub, not by a script somebody remembered to run: a new required check, enqueue-guard, is written onto every pull request head from the same decision node .github/scripts/enqueue-guard.mjs --pr <n> prints — a hold, a tip with no PASS naming it, a PASS that lists no Ran:, a missing agent-pr or a body that names no issue all turn it red, and it flips green without a new commit when the label or the conclusion arrives. .github/agent/merge-gate-ruleset.json is the ruleset that requires it next to tests-ok (branch refs/heads/main, bypass_actors: [], strict checks off so every open pull request does not have to rebase); node .github/scripts/gate-ruleset.mjs --check reports the live state and --rollback removes it (#812).
  • The upstream fast-forward is written down and checked instead of remembered (#815). docs/release-runbook.md records the one-window-per-version sequence — the release commit as the tag target, the dry-run's five gates, PUT to disable the ruleset, a force: false fast-forward, the restore in a finally and the read-back that proves it, then upstream's own release with the same four assets — with every read and write as a node snippet, because the box that runs it has no curl and no jq. node .github/scripts/upstream-sync.mjs --check answers the two questions the window needs, read-only: both main SHAs with ahead_by/behind_by, and the ruleset's enforcement with its bypass list; it exits 0 only when the two mains are the same commit and the ruleset is active with no bypass actor, and it exits nonzero naming the condition that failed. --commits names the commits the fast-forward would carry.
  • A friend's Bot that is in a group now answers in that group: a line that names it travels on the room's share envelope carrying the handles it calls, the receiving FlyBot opens one turn for the Bot those handles name, and the answer goes back as a line of the same room instead of landing in the friend's Bot's own chat (#825). Bots can answer each other's lines in the room up to the room's hop limit (3 by default) and a line is answered once, so two Bots cannot call each other forever; an answer the friend's owner has not approved yet, or refuses, still says so in the group in one line. The old "sent — its replies appear in its own chat" receipt is gone, because it is no longer true for a Bot in the room.
  • A room's invite link can now be used as an invitation: the link carries a ticket, the inviter's FlyBot answers with the room it belongs to, and only the accepting side is admitted, one home per ticket (#852). Nothing in the invitation can create a room, seat a member, or use up the ticket, and a refused seat gives the ticket back. One field now reads both link shapes — this server's /r/<ticket> and a room on another FlyBot, /r/@handle/<ticket> — and a link into another FlyBot's room is answered as itself ("That link opens a room on another FlyBot") instead of being sent to Bot Social as a ticket it never issued.
  • A local group’s Members card has an Invite by link row: make a link, show it as a QR code, copy it, watch its countdown, or withdraw it. Someone who is not a friend can paste or scan that link in the Face-to-face dialog, see the group first, and join it with the Bots they pick.
  • A room's invite link now has the endpoints the Members card works through: read the live link, mint one, withdraw it, and knock at a link someone else handed you — first for the preview, then again with accept: true when Join is clicked (#876). The knock leaves from one of your own Bots and names the Bot to knock on from the link, never from the request body; a link into a room on another FlyBot, a bare ticket, and a ticket outside the 16–64 character shape are each refused in one sentence and send nothing.
  • FlyBot now talks to Bot Social's record of a room link (#883): making a link for a room that lives in your FlyBot registers it with Bot Social, a guest's Join claims it there, and the room's own messages travel between your Bot and the guest's Bots under that room only. Anything else to a Bot that is not your friend — a plain message, another room, another Bot — is still refused.
  • A room that lives in your FlyBot can now seat someone who is not your friend: a link Bot Social records admits one home with the Bots it brings, all of them in one step, after this FlyBot reads the claim back from the server (#884, #878). A room can have several links live at once, one per person invited, and each is withdrawn on its own. A guest's Bots get the room's lines and nothing else of yours — no friendship and no sidebar entry — and lose the room when its link is withdrawn, they leave, or the room is deleted.
  • The merge gate now reports a silent revert: a pull request built on an old copy of main re-uploads whole files, so work main merged after the branch was cut disappears even though everything is green — #887 deleted #869's 84 test lines the same hour, and #907 deleted #909's whole fix 40 minutes after it merged, both mergeable with tests-ok passing (#914). It compares the lines main added after the merge base with the lines the pull request deletes from main and writes one warning per affected file plus a total line into the merge-gate job summary. Report only: it never comments, labels, blocks or merges, and a pull request already rebased onto main reports nothing.

Changed

  • Live E2E no longer fails every night when the repository secret DEEPSEEK_API_KEY is not configured: the steps that need the key are skipped and the run warns instead, and its summary says plainly that nothing ran and nothing was measured. A failure inside the suite still fails the run (#794).
  • AGENTS.md no longer gives a Main Bot a removal right it has no tool for: it gets create_bot and update_bot, stop_bot only ends a running turn, and deleting a Bot stays the person's action in the UI (delete-bot). The team paragraph now says that, and points at #638 for the open decision on a retire tool (#841).
  • A group's invite link no longer expires and is no longer single-use: anyone you give it to can join with their Bot until you withdraw it, and withdrawing stops new joins without removing people who already joined. The Members card's Invite by link section lines up with the card, the note on who leads the group sits with the members again, the + menu entry reads "Join or create by number or link", and a group joined by link shows its own name in the sidebar.
  • Group chats use fewer tokens per turn: a member's group conversation now reads the group's notice, members and rules from its standing instructions instead of receiving them again with every turn, and a turn after a notice edit says only that the notice changed (#894).

Fixed

  • The main view no longer jumps to the Main Bot when the roster drops a Bot whose chat is still open (#261): landingPart asks the session list whether that conversation exists, so a revoked share or a removed Bot leaves the open chat (or its blank chat) in place, while a New Session the user opened and a session archived with its Bot still go to the Main Bot.
  • The Session header shows the subagent catalog again (#803). FlyBot claimed the shell's whole Session header as a single slot, so the child seats that header renders — the lineage title and the subagent catalog button among them — were never drawn and a started subagent had no way in from the header. The Bot pill now sits in the header's utilities list beside those seats instead of replacing the header, and the floating header style FlyBot drew over the Session header is gone with it (that change is visible in every chat). Because the shell draws the utilities — and the pill with them — only while its chrome shows, everything the pill does to the page (the friend-Bot empty-chat hint, the visit ledger, the composer placeholder, the room and face flags) is now installed once from the client plugin and follows the Session being looked at, so a Session without a first message keeps all of it. Nothing is claimed for it: conversation.session.header.corner, the other seat the shell draws in every state, already belongs to the shell's own right-sidebar button, and a second registration there fails the plugin.
  • In a group, a member's @Admin is now always answered: the per-member turn cap used to drop it whenever the admin had already spoken twice in that round, so the one message that says "I am free, give me work" vanished with no notice and the coordinator went on believing the member was busy (#804). The cap still bounds a chain of Bots naming each other; it no longer bounds a report to the room's admin, and the round's own turn limit still bounds the round.
  • The tree no longer asks the host for the Bot list twice per refresh: the sidebar's structure entry and the archived-Bots list share one feed, so a mounted sidebar reads the roster once every 10 s instead of once per consumer, and the poll stops when the last of them goes away (#816). The README now says the archived list is registered at order: 88 and the reorganization notice at order: 89, and a slot renders ascending — so the archived Bots sit above the notice instead of being pushed down by it.
  • Joining a room with a link now asks the host once for every Bot picked in the join dialog; before, only the first Bot was put in the room and the others were silently left out. A Bot that cannot join is listed by name while the ones that did still bring the room up. The link asks once for the whole ticket when nothing can change for the next Bot (an unknown, expired or withdrawn invite, a room that has ended, a room that is full) and once per Bot when the refusal is only about that Bot (already in the room, its token revoked) (#817).
  • The sidebar no longer draws a separator for a section that has nothing to show: the Archived (N) group, and the reorganisation notice beside it, left an empty wrapper behind that drew a 1 px line with 8 px of margin over nothing, so a fresh install showed two rules with nothing between them (#818). The archived section also reads the roster the sidebar already has instead of fetching it a second time on its own 10 s timer, so a tick is one roster read rather than two; an expanded group stays expanded across a tick.
  • A lead created for two simultaneous create_child_bot calls no longer claims a Bot it does not hold: a call that only reuses an existing Bot creates nothing, so it is no longer counted as a member of the group, and a burst of one new Bot plus one reused one makes no lead at all (#819).
  • The invite card now strips untrusted fence markers from a room's name and lead until the string stops changing, as the server's stripFaceMarkers does. One pass could remove a match and assemble a new one — a nested <untrusted-<untrusted-filemessage> lost its inner match and left a complete <untrusted-message> behind — so a remote room could put marker text into the card a person reads (#822).
  • An invite card no longer shows an unknown member count as 0 / 8 in the room: an empty or non-numeric value is treated as missing, while a real 0 still shows as a real count (#823).
  • Pressing Join with link again while the preview card for that link is already on screen no longer asks the server for the same room twice: the card stays up with its countdown, a different link in the field previews once, and Clear lets the same link be looked up again (#824).
  • upstream-sync.mjs --check now fails, naming the read and the HTTP status, when it cannot ask how far apart the two main refs are, instead of reporting CHECK ok on equal SHAs (#826).
  • The live-e2e secret guard now decides whether a step reads the key from the value YAML parses, not from quotes that happen to appear on the line: a plain run: where # opens a comment no longer counts as a carrier, and a double-quoted value with an escaped quote before # (in run: and in an env: value) is counted again, so a step that really receives the key cannot pass the check ungated (#834).
  • A burst of two create_child_bot calls with the same name and role made one lead whose role text named that Bot twice; the lead now names it once, and a burst with different names still lists every member once (#840).
  • A face-room invite preview now names the room lead instead of printing Lead: object Object: Bot Social sends the lead as a member object, and the card reads it with the same label the member list uses — Lead: Chief (SelfTest A2) — while a string answer keeps working (#846).
  • A friend's Bot added to a local group chat now reaches that friend's FlyBot with no approval card: the host clears Bot Social's blanket hold on the room's own FLYBOT-ROOM-SHARE envelope (the same allow-list a face-room line uses), so the invited home sees the shared room as soon as the Bot is added and each later group line goes out with it. A hold with a finding of its own keeps its card, and a card that does appear reads one plain sentence (the invite, or the group message) instead of the raw envelope and its local ids (#847).
  • A group created from the sidebar now names the person who invited a friend's Bot, not "a friend": the social plugin keeps the owner-level name and home on each Bot's member record (from whoami), and the share envelope reads them when no human participant is in the room. A room that does have a human participant still names that human, and a room mirrored from an inbound share forwards its original inviter instead of this home's owner (#848).
  • A held group line now reads as that line even when its text carries a } or a quote: the approval card's envelope scan knows JSON strings and escapes, so it no longer stops inside the text and falls back to the generic Group update for … in a group chat sentence. A broken face-room tail also keeps its To the room: prefix off the card (#855).
  • A face-room invite preview now names what kind of room it is when Bot Social has no name for it: the card's first line reads Face-to-face room instead of A room with no name, which was the first thing a person saw before deciding to join. The lead, the n / 8 in the room count and the deadline are unchanged (#859).
  • A room-only Bot Social identity that is promoted by a person grant keeps the owner-level name and home the share envelope reads, instead of dropping them and leaving the next group invitation saying a friend (with an empty inviterHome) until the process restarts. When the record never had either, promotion fills them from whoami right away (#863).
  • Auto-send decides on Bot Social's hold_reason instead of guessing from reasons (#865): the two outbound paths clear only blanket_outbound, and risk, attachment, policy and any value this build does not know keep their card for the owner. The face-room path also clears face_room_auto_send_off, which is nothing but that Bot's own room Auto-send switch on a clean line, so the switch the owner turned on keeps sending (#457). A server that does not send the field keeps the old rule, and when the field and reasons disagree the field wins.
  • A room shared into a FlyBot home is now named after the person who invited it, not after the Bot that carried the invitation across Bot Social. The receiving side reads the inviter from the share envelope (inviterName / inviterHome) and falls back to the transport only when the envelope names nobody, so the room reads Shared room from Alice instead of Shared room from Chief. A projection recorded under the transport's identity before this fix is still recognised as the same room rather than appearing twice, and chat lines keep their own speaker (#873).
  • A Bot that only shares a room with a friend's Bot can message it again: both send paths name the room on send_as_member when the two Bots are live in one face room this home has not left, so Bot Social decides on the room-scoped reach the two already hold instead of answering that they are not friends. Lines outside a room, into a local room, and after leaving the room name no room, and a room-scoped refusal now reads as the room's own sentence ("that room is closed between these Bots now…", "the room no longer lets these Bots reach each other…") instead of "you are not friends with this Bot" (#881, #866).
  • A link withdrawn on Bot Social (a 204 answer) no longer ends the FlyBot process, and a guest reads the inviting owner’s name rather than the host’s own “You”.
  • In a group joined by link, the guest can write too, under their own name, and lines arrive as written. The guest’s Bot no longer drops out of the inviter’s group after the first message. A ticket the guest never claimed opens nothing.
  • A tree coordinator no longer gets its whole system prompt re-sent every time one of its children starts or ends a turn: children's load is read with list_children instead of a prompt line that changed at each boundary (#895).
  • A Bot chat whose earlier part had an armed scheduled task now archives that part when it hands off: the switch waits for the task move to finish before archiving, and if a task could not be moved it archives the finished part with that task stopped, instead of leaving it active forever with a warning only in the log; a part held by a running job or turn is left alone, as before. The task keeps running on the new part when it moves (#896).
  • A group link now works when the host's or the guest's Bot used a face-to-face room before: that room-only identity is promoted before the link is made or joined (and for links made earlier, when FlyBot starts), and the Join dialog says "already joined" only when the group is really in your sidebar; otherwise it says who you are waiting for and Join asks again (#899).
  • A group joined through an invite link now lists the host's Bots instead of only your own: the join notice carries the host room's Bot roster, the Members card shows each Bot by name and owner, and a message keeps the Bot that wrote it. A Bot the host has not connected to Bot Social shows as "Not connected", and a host Bot this side cannot confirm yet shows as "Unconfirmed" rather than as a friend's Bot.
  • A group round's closing report no longer folds a member that was addressed with @ and never answered into the same sentence as a member that read the line and passed: the two now get separate sentences, so "he had nothing to add" can be told from "he was called and said nothing". A member the line did not name still appears under Stayed silent: (#913).
  • A conversation that is still in the middle of its work stays on the same part after a checkpoint. The context meter was still counting messages the checkpoint had already replaced, so the part looked too long and moved on anyway (#917).
  • A group that a friend's Bot joined through an invite link now reaches that guest instead of asking Bot Social about a friendship the two homes never had: the seat the link admitted is the one pair the link opened, read from the seat itself or from the link's own record of who it admitted, so a group message or update goes out under the room rather than sitting on an approval that could not be approved. A Bot added to the group by hand is not a link pair and is unchanged (#923).
  • A shared room whose invite link was accepted before the link recorded which homes it seated now reaches that guest again: the pair is read from the link's own record of who it admitted, so a room that predates homes no longer falls back to a friendship the two homes never had (#929).
  • A Bot that names another member with @Name now gets that member called in the shape that used to lose the call: a line posted into the group while a round is running calls the members it names inside that same round (under the usual per-member and per-round caps, exactly once — the re-delivered line calls nobody again), and a late line after the round limit still calls the member it names instead of letting quiet swallow the name while the admin answers. A name outside the group, the sender's own name, and a member's failure or stop notice still call nobody (#932).
  • A group joined through an invite link now shows the other home's messages in the conversation, on both sides, including the host's recent lines from before the guest joined (#939). The member count includes the remote Bots the roster already lists.
  • A Bot that joined a face-to-face room by its number can write its 1:1 line to the seat it shares that room with: the room is the only consent such a Bot has, so the host's owner policy now reads it instead of refusing a Bot that holds no person-grant, and the line goes out naming that room so Bot Social checks the same consent — with no shared room the refusal is the same sentence as before (#948).
  • An @ of a Bot in a room joined by invite link now starts that Bot's turn. The name the @ menu inserts (Chief (owner), Data (Friend)) is a call, including for a Bot that has no Bot Social handle; a fenced or quoted @ stays literal text (#949).
  • A message to a friend's Bot that no longer exists now reads one sentence — the Bot is no longer shared with you, so ask its owner for a new invite — instead of repeating "no such remote bot. Try again. Try again.", and the failed hold it leaves offers no Retry, because sending again cannot succeed (#954).
  • People with access no longer prints every room a share is used in on one endless line: the hint names at most three rooms, says how many are left (+12 more) and keeps the full list in its tooltip, and a room the grant row already named is not repeated under This room only (#955).
  • A friend's Bot that leaves your shared list no longer takes its whole conversation off the sidebar: the row stays, marked as no longer shared, with its history and its way back, and it returns as an ordinary chat when the share comes back. Hiding a chat yourself still removes the row, and the two states stay apart in the state file (#956).
  • Your own Bot keeps its color after a friend with the same name is added. The @ mention, the group icon, and that Bot's messages stay on the local look; the friend's Bot stays on its own (#963).
  • A friend's Bot picked from the @ menu now inserts the name people read — @Chief (Bob), the same string as its sidebar row — and the sent message draws it as an @ chip in the bubble and the preview instead of a bare handle. The handle stays the routing key underneath, so the message reaches the same Bot, and a line written before this change keeps its chip. Two friends' Bots with the same name stay apart in the label, and a friend the core does not list yet, offered by Bot Social's own @ row, inserts that name too. (#967)
  • A failed card that can never succeed no longer offers Retry on any surface: the chat bubble and the dock now ask one shared predicate instead of reading only "failed", so a Bot that is gone or no longer shared, or a send refused as not permitted, shows the reason without a button that cannot work, while temporary failures (busy, rate-limited, timed out) keep theirs (#968).
  • A send to a friend's Bot whose owner took the share back now reads as one short line — the same "not shared with your Bots" sentence the app already used — instead of Bot Social's peer has not granted chat.public followed by a Try again. that could never work. Both that refusal and the earlier bot_not_shared are now marked as not retryable, so no card offers a Retry for them (#971).
  • Share with a friend now shows one row per Bot of a friend instead of one row per person: each row is named Bot (Owner) with that Bot's handle, and the Share click grants to that row's Bot, so the receipt says which Bot got it. Before, a friend with two Bots collapsed to a single row labelled with the person's name while the grant went to whichever Bot was listed first (#973).
  • Share with a friend filters per Bot, not per person: a friend who runs two Bots used to disappear from the list entirely once either Bot had yours, and the panel then said they already had it — true for that Bot and false for the other one. The other Bot is offered again, under its own name, and the click grants that Bot (#981).
  • Share with a friend no longer drops to "No friends left to share with" when reading the friend list fails or is overtaken by a Revoke: the rows the last successful read drew stay on screen, a failed read is named with a Try again, and the empty sentence now prints only after a read that succeeded and actually returned nobody (#984).
  • When sharing a Bot with a friend fails, the Sharing card keeps that message as the share's own: it no longer offers a Try again that only reloads the friend list, and a later successful reload no longer wipes it. The friend-list read keeps its retry and its empty state, and the two errors no longer stand in for each other (#987).
  • A refusal that a peer has not granted a capability other than chat, such as profile.read, stays a normal error you can retry, instead of the "not shared with your Bots" line. A withdrawn chat share (peer has not granted chat.public) still reads as that one line, with no Retry (#988).
  • A held face-to-face line's approval card now names the room it will go to: Chief → the room 482913 when this home knows the number the room was joined by, else Chief → the room <title> for a room entered by link, so two open rooms never both read the room while the owner is deciding which one gets the line (#991).
  • The Sharing card now shows both facts at once: a failed Share and a failed friend-list read each keep their own message, so clicking Share no longer erases the list error and its Try again, and the five-second refresh no longer erases what the share told you. A successful refresh settles only the list error, and a successful share settles only its own line (#992).
  • The share receipt names the Bot the click granted — Shared Chief with Writer (SelfTest B2). — using the label the row already shows, instead of falling back to the friend's home slug (Shared Chief with selftestb2.) when Bot Social's answer carries no names; a caller that sends no label gets a plain with your friend, never the slug (#994).
  • The enqueue guard now accepts a cross-repository issue reference, Fixes owner/repo#<n>, next to the bare Fixes #<n> it already knew (#809). Pull requests are opened in the code fork while issues stay upstream, so the bare spelling there points at a number the fork does not have; every such pull request was refused at the last step before the queue. The refusal text now names both spellings.

Security

  • An inbound room share and a room-scoped line are now trusted by the transport that delivered them, not by the envelope that describes them: a share naming a room of yours is refused (not-inviter) unless it arrives from the home the room belongs to, a line for a room nobody shared no longer opens one (unknown-room), and a @handle signature is shown only when it traces to the sending Bot, a remote seat of that room or its inviter — otherwise the line is kept and attributed to the Bot that delivered it, marked unverified (#909).
  • A shared room no longer takes an inviter's handle from another home as that inviter's signature: a line that presents the inviter handle but arrives from a different home is kept and shown under the Bot that delivered it, marked unverified, instead of being signed as the inviter (#918). Only a join records which home invited a room — a line can no longer rewrite it, which is what made the signature check agree with itself.

[0.2.5] - 2026-10-24

Added

  • A face-to-face room now shows its 6-digit number as a QR code, and FlyBot can scan one instead of typing: the waiting dialog and the room panel draw FLYBOT- plus the number locally (byte mode, error correction M, version 1 — every module computed in the browser, no request and no image service), the code disappears when joining ends or the room opens, a QR from anywhere else is refused in one sentence, and a scan ends in the same social/face-join the number field uses. A browser without BarcodeDetector says so before it asks for the camera, a refused camera gets one sentence, and typing the number keeps working either way (#773). An open room also offers Invite by link (data-flybot=face-invite): Create link asks Bot Social for one and draws the link itself as the QR, so a phone camera opens the room and FlyBot's scan joins with the same ticket; the card shows the link, Copy link, the deadline, and Withdraw link. Pasting or scanning accepts only https://…/r/<ticket>, and each refusal — expired, withdrawn, full, closed, already in it — is one sentence. The card stays off on a server without the face_room_invites feature, which says Invites need a newer Bot Social. The 6-digit number still works.
  • core/dev/token-report.mjs reads the usage ledger into the numbers #796 asks for: per Bot, per Session and per day, with fresh input, cache read, cache write and their sum (prompt) as separate columns, per-call prompt percentiles, the top Sessions by prompt, and each Session's prompt growth between consecutive calls, splitting at a compaction or a new part instead of counting it as a negative delta. It opens the ledger read-only, defaults --db to $DSH_HOME/usage.sqlite, and exits non-zero naming the path when it is missing. The first report is docs/reports/2026-10-23-token-spend.md (#799).

Fixed

  • create_team now refuses a team before it writes anything when the lead's name or a member's name is already taken anywhere on this home: it names every clashing name and the path of the Bot that holds it (#764). Previously it created the lead first, so a member whose name was taken was dropped and the team was left half-built with a childless <team> Lead.
  • A PASS whose Written-by: names someone outside writers in .github/agent/review-policy.json is refused by review-gate and merge-gate, but until now only a log line said so: the reviewer assumed a conclusion that counted, nobody enqueued, and the pull request waited with every check green. The pass label job now leaves one comment per tip naming the author, the tip and the file to edit, and still adds no label for it (#767).
  • An image that no model can read now arrives as a placeholder that names its file on disk (<home>/attachments/v1/objects/<first two>/<sha256>), so a Bot can pick the bytes up with its own file tools instead of asking the user to describe the picture. The path is given only when the file is really there, and only when the file sandbox can open it; a chat and a group relay share one placeholder (#779).
  • Handing the Main role to another Bot now moves the tree's root with it on the next turn: exactly one root remains, the previous Chief is demoted and placed in the tree, and the new Main gets the tree tools instead of waiting for a restart (#748). A tree that already sits at maxDepth stays inside it on the swap: the demoted root's branch is re-attached under the new root instead of being placed one level too deep.

[0.2.4] - 2026-10-23

Added

  • A plugin the host allows to make a model call can now also get its own default model: flybot.modelFor ({ '<plugin>': '<provider/model>' }) is used when the call does not name one, so a host can keep a cheap plugin cheap while the rest of the team stays on the team default. A plugin the host did not name is unchanged, a model the call names still wins, and an entry that is not a non-empty string is ignored rather than passed through (#579).
  • A Copy report button on the timeline turns the window into a short Markdown run report — per Bot its turns and failures, busy share, tokens, top tools and unanswered requests, then the team rollup and the longest open wait — and copies it with one click, in the same hours the panel shows (#582).
  • A new read-only timeline/alerts endpoint turns the timeline window into durable alerts: a Bot that fails most of its turns (errors), a Bot whose request went unanswered (waiting) or a handoff that has waited past 15 minutes (stuck) opens one alert that records when it started, and the alert resolves itself when the condition clears and stays readable for a minute after. Polling again never opens a second copy, a look-back window never resolves a live alert, and nothing new is written to the trace (#584).
  • bot_schedules lists every scheduled task of the calling Bot across all of its sessions — its own chat, the earlier parts of it, and the group chats it speaks in — with each task's next run, state and owning session, so a task the owner sees in the UI can no longer look missing from the one session a Bot happens to be in (#628).

Changed

  • Every one of your own Bots now gets your saved keys (such as $DSH_SECRET_GITHUB_TOKEN) and your connector tools (GitHub, Composio) on every turn: group rounds, Bot-to-Bot messages, scheduled turns, turns of a Bot you share with a friend, and work a Bot does in a sub-agent. The "also when others use it" connector switch is gone; each Bot keeps one on/off switch per connector. A friend's Bot still never gets your keys or connectors, and a key you set to Chosen Bots still goes only to those Bots. Bots now use the GitHub connector first and the shell token only as a fallback, for example for git push (#601).
  • Typing two or three short lines in a row in a Bot's own chat now opens one turn instead of one turn per line: the host holds the lines for 1.5 seconds and hands them over together, in the order they were typed, each on its own line. A line sent while the Bot is already answering still lands on its own, and a group chat is unchanged (#642).
  • Making a Bot or a group chat no longer starts a turn: a new Bot is sent no greeting and waits in the sidebar until you write to it or a parent Bot messages it, a new group posts no opening round, and picking a Bot's model applies it without speaking to the Bot. Text you type in the composer before creating a Bot is sent to it as your first message, so it answers you, and it is never saved as the Bot's instructions; making a team costs no model turn (#645).
  • Bots stay idle until you address them, they are @-mentioned in a group, or their parent Bot gives them a task: the prompt no longer tells a coordinator to dispatch work or a Bot to report and claim on its own, so a team with nothing to do stops producing status traffic, while a Bot that is addressed answers exactly as before (#699).
  • list_children now describes the routing fields it already answers (#733): load for every child, and openForMs/stalled for a child with an open turn, so a coordinator can send new work to an idle child instead of learning the shape from the implementation.

Fixed

  • list_bots tags a Bot whose outbound message waits on the owner's decision with a count (Writer (Write, 1 to approve)), so a coordinator sees who is blocked instead of an idle-looking roster. The tag is a count only — never the held message's text — and clears once the owner decides (#587).
  • turn-origin now treats a message source kind it does not recognise as other instead of "nobody's request", so an unknown or future source arriving in a Bot's own chat no longer inherits the owner's standing approval: only DSH's own notice kinds and the documented mention/handoff cases carry no verdict (#620).
  • A Bot-to-Bot message that repeats what the same sender already delivered to that Session within the last minute now makes one turn instead of two, and the drop is recorded in the host log as delivery: dedupe <sender> -> <session>; a relayed conclusion no longer stacks up in a coordinator's queue twice, while the same line to another Session (a group copy, another Bot) is still delivered (#641).
  • A Bot's shell now says which saved key it withheld and why, instead of looking like the key was never saved: DSH_SECRETS_WITHHELD lists NAME:reason (never a value), list_secrets and request_secret name the same reason, and a key the owner scoped to one Bot still reaches that Bot's own scheduled turn while it is shared (#651).
  • A Bot whose saved key is withheld on a turn can run its shell again: DSH_SECRETS_WITHHELD is now declared to the shell environment, so the bash call no longer fails with "undeclared key" (#653).
  • On the newer DeepSeek Harness (0.2.1-alpha.1) the composer's run-stats footer is no longer a single stats entry — the turn and step counts moved into an activity pill — so FlyBot's "1 turn" / "1 step" wording stopped replacing the shell's "1 turns 1 steps". FlyBot now wraps whichever counts pill the installed shell publishes, on both versions (#659).
  • A message to a friend's Bot right after that friendship was removed is now refused at once with the usual not a Bot a friend shared with you reason, and the Bot leaves the list, instead of being held for approval and then lost when the approval is clicked: the send policy asks Bot Social about the friendship itself rather than trusting the cached roster, and a removal event drops that cache (#666). The same check runs again when a held card is approved, so a line whose friendship was removed while the card waited fails with a reason instead of being sent into a refusal.
  • Creating a Bot from the composer now always opens the new Bot's chat: if sending the line you typed as its first message fails, you land on that chat with the failure shown instead of on a card that answers "a Bot with that name already exists". An older team's setup line ([Bot team setup]) is no longer drawn as a message from the parent Bot in the current part of its chat, matching how the earlier parts already hid it (#698).
  • The multi-pane layout no longer overflows a narrow window: the Details panel shrinks before it starves the conversation, every pane can shrink, and long values wrap, so nothing is clipped and no blank strip is left between the panes (#705).
  • Deleting a Bot now drops its open-turn records from the tree's load tracker. The tracker used to keep counting the deleted Bot forever, so a coordinator could read a Bot that no longer exists as busy (#732).
  • A hold that arrives with the status pending_approval — the spelling Bot Social's own outbound hold already uses — is now treated as waiting by both places that draw it: the held-reply card keeps its Approve / Reject buttons, and the approvals overlay offers the same decisions instead of drawing a settled "Not sent" note, so such a hold can no longer become unanswerable. One shared rule (pending and pending_approval wait; approved, auto, failed and a missing status do not) decides it, and a failed send still reads "Not sent: delivery failed" with Retry (#734).
  • A Bot whose Auto-send is on for a friend no longer goes silent: a line the Bot sends is delivered, instead of waiting on Bot Social's own outbound approval with no card anywhere to decide it. The send path now clears that hold itself, the way a typed line and a face-room Auto-send line already did, and only when Bot Social named no DLP finding on it — a flagged line still waits on its owner's card, exactly as with Auto-send off (#737).
  • A hold whose status is pending_approval is now answered by the host the same way the client draws it: Approve and Reject go through instead of being refused as "already decided", the reply's bubble stays a waiting card instead of reading "Not sent", the hold counts against the approval cap, is not cleaned up while it waits, and counts toward the Bot's wait badge. Host and client now read that judgment from one module, so the two spellings of "waiting" cannot be added on one side only (#754).
  • Durations in the run report no longer overflow their own unit: a wait of 59.6 s reads 1m instead of 60s, and 1h59m50s reads 2h00m instead of 1h60m (#761).
  • The first Main Bot created during onboarding, and a duplicated Bot, are now announced to plugins like any other new Bot, so @flybot/tree adopts them immediately instead of only after a restart (or not at all, if the server had been running before the first open).
  • Auto-send now clears a Bot Social hold only when Bot Social held the line for its blanket approval rule alone: a reason it does not know, dlp.blocked, or any risk flag keeps the line on its card instead of sending it without the owner, and a hold the server has not listed yet is logged with its approval id instead of failing silently (#766).
  • Hiding a friend's Bot chat from the sidebar ("Hide from sidebar") now sticks: the remote-chat sync no longer clears that hide on the next publish of the friends' list, and a hide the user set now survives the Bot leaving the published list and coming back, while a chat whose Bot left still disappears and a returning Bot still reappears (#611, #631).
  • A part switch now saves the new segment, the pointer and the rescued mark before the handoff turn can start, so a failure in that turn can no longer begin a second switch against an uncommitted part — which lost the previous part's handoff note and dropped the messages the second move should have left out; a failed handoff still rolls back to the old part, keeps its input and can be rescued again (#751).
  • A part switch that fails (a save error, or the fresh Session cannot take the handoff) now leaves the old part current and hands the stopped turn's input on there, instead of opening an empty fresh part with no handoff package and dropping the input (#740).
  • A turn that finished while the plugin was not listening (a plugin reload, or a crash right after DSH recorded the end) is no longer picked up as "restarted while you were working" and repeated: the durable Session log decides, and only a turn the restart really cut is resumed.
  • A restart's pickup no longer loses the work it was about to resume when a delivery fails or the app stops during the pickup: the inflight notes stay until each turn or queued message is handed over successfully (#747).
  • Approving every waiting card now works for Bot Social holds too (the button was missing or skipped them), and a local hold whose send failed reads Not sent with its error and a Retry instead of offering Approve / Reject that silently do nothing.
  • The Bot Social settings file no longer grows forever with one entry per approval hold ever seen: a decided, rejected or gone hold drops its waiting-notice entry, so the ledger stays as small as the pending queue.
  • A Bot's role label for a face-to-face room is retried when the room refuses it (a dropped request or a server error) instead of being counted as sent and never synced; a room that is forgotten clears its per-seat role marks.
  • Sharing a Bot with an existing friend now works on a Bot that has never been invited or shared: opening Share with a friend registers the Bot's Bot Social identity on demand instead of failing with “has no Bot Social identity yet”.
  • Switching the Bot Social server now turns that server's face-to-face rooms into read-only past rooms instead of leaving them open and accepting lines that can never be delivered, and a line sent to an ended room is refused with a clear message (#729). Only a change of address counts: renaming this home on the same server leaves that server's rooms open, and a switch that fails ends none of them.
  • OTLP export no longer drops a span whose record shares the millisecond the previous batch ended on (a tool call that ends at the same millisecond it started, or a turn ending at the last record's time): the export cursor re-reads records at its last timestamp and deduplicates spans by id.
  • A strict OpenAI-compatible gateway no longer rejects the whole chat: every tool FlyBot registers now sends an explicit required (empty when nothing is mandatory) and properties (empty when it takes no parameters), so an argument-less tool such as bot_schedules or list_bots cannot turn the tool list into Invalid schema for function 'bot_schedules': null is not of type "array". Tools whose schema comes from an MCP server are passed through by DSH and are not covered by this change (#768).
  • Deleting a former Main Bot (after handing the role to another Bot) no longer flattens the team: its children move to the current root (or a fresh Root when there is none), so exactly one root remains and the new Chief's list_children and replies keep working (#725).
  • Renaming a Bot (or editing its role) now reaches the tree prompts on the next turn: a coordinator no longer calls a child by its old name, and a renamed Bot sees its new name in its own prompt, instead of waiting for a restart.

[0.2.3] - 2026-10-22

Added

  • A question a Bot asks about work handed out from a group now shows in that group's sidebar row (waiting dot and "Waiting for your answer") and in the Agent inbox as a Needs your answer card whose Open takes you to the group, where the card takes the answer (#552).
  • The flybot service can now report approval activity: flybot.approvals.stats() returns, read-only, how many of each Bot's outbound sends were held and how many were approved, rejected or went stale, plus a team total. A plugin or a settings view can show it; nothing is changed by reading it.
  • A Bot can now record a decision that mattered — an option it considered and whether it used or rejected it, and why — with record_decision. It goes into a local, append-only log (<home>/decisions.jsonl, capped) that a reviewer can read to see near-misses and judgment drift, and it is fired on the activity feed as decision so the Timeline or an audit view can show it. Off by default (decisions: true turns it on); a key, token or password is refused.
  • When the tree merges two duplicate Bots, the archived Bot's own memory now moves into the Bot that keeps it, so a merge no longer drops what the duplicate had learned. It is capped at 20 entries and tagged source: merged. This uses a new plugin seam, flybot.memory (read a Bot's entries, append one), which is off unless the host names the plugin in flybot.memoryPlugins; without it the merge is unchanged.
  • Plugins can now make a short, sanctioned model call. flybot.model.ask({ prompt, system?, maxTokens? }) resolves { ok: true, text } or { ok: false, code } for a plugin the host allows — usably for a small classification, a title or a one-line summary that a plugin needs of its own. It is off by default (the host names the allowed plugins in flybot.modelPlugins), capped in input, output and rate, streams through the host's own model, returns text only (never a provider message) and refuses on a read-only host.
  • The Timeline window now reports how each Bot's turns ended — completed, aborted, error or lost — and lists its recent failed or aborted turns with their error code, so the panel or a tool can see a Bot's reliability at a glance. It is read-only and part of the existing timeline/window answer, so nothing new needs configuring.
  • The timeline window now also reports the open requests waiting longest (#469) and the tools each Bot used with their failures (#359), and timeline/health reduces the same window to per-Bot health signals and flags (#357).
  • The Timeline window now reports the token spend per Bot — how many model calls, and the input, output and cached tokens they used, when the adapter reports them — so where the team's model quota goes is visible next to the turns it ran. Read-only, counts only (never message text), and part of the existing timeline/window answer.
  • The tree can now group a Bot by meaning, not only by the words in its name and role. With modelClassify: true in the tree config (and the host allow-listing flybot-tree in flybot.modelPlugins), a Bot whose role the built-in vocabulary cannot place — a title it does not hold, a brief in another language — is sent to the model once for a single category, which is stored on the Bot and used the next time the tree reorganizes. It is off by default, asks at most once per role change, and the rules still win where they already found a category.

Changed

  • A Bot session on workspace-write without a usable sandbox backend now has a one-command preflight (node .github/scripts/sandbox-preflight.mjs) that names the missing backend and prints the unlock in one line, and the agent instructions say to report the block once instead of retrying every shell call (#607).
  • The integration guide now names the two plugin seams — flybot.model.ask and flybot.memory — with the host config that enables each (flybot.modelPlugins / flybot.memoryPlugins), so a plugin author finds them without reading the host source.

Fixed

  • Scheduled tasks no longer stop overnight in a group chat: the limit that let Bots start only three rounds there until you spoke again is turned off, because it blocked owner-set recurring runs without showing anything. Bot-to-Bot chain and per-round limits still apply (#557).
  • Sharing a Bot with a friend on a Bot Social server that lacks grant_bot_to_friend now tries the REST route and, when that is missing too, says Bot Social needs an update to share with a friend. Invite links still work. instead of showing the raw MCP error -32602: Tool grant_bot_to_friend not found (#563).
  • Sharing a Bot with a friend works again: Share with a friend names the friend by the friend's Bot id (the id list_friends returns) instead of an owner id the server never lists, the fallback route now matches Bot Social (POST /v1/bots/:id/grants), and a server that refuses the request says so in plain words instead of showing the raw MCP error -32602 text (#592).
  • The approval card in a group's details panel wraps its buttons, so 拒绝 and 允许一次 stay reachable at common desktop widths instead of overflowing off the left edge of the panel (#593).
  • A Main Bot the tree adopts only after the server started (onboarding creates it while dsh web runs) now gets the tree's team-building tools (create_team, create_child_bot, and the other gated tools) on its first turn instead of only after a restart; the plugin-tool mask is recomputed when a turn starts, not only when the Agent is created (#594).
  • A group member Bot's line that arrives while the group's turn is still running no longer shows up in the message bar above the composer with the internal [Group post from …] header; it appears at once as that Bot's own bubble, and the round still gets it when the turn ends (#595).
  • A Bot's own failure, stop or resume notice that lands while its group chat is still in a round no longer flashes FlyBot's internal [Group post from …] routing header in the queued-message bar above the composer; it waits until the round is done and the round still shows it as that event (#595).
  • The decision log (decisions.jsonl) is now bounded on disk: the host trims it to the newest 2000 entries and at most 1 MB as it appends, and a read parses at most that byte cap instead of the whole file, so the log no longer grows without bound (#603).
  • "Always allow" now keeps working when DSH opens its own retry turn after the owner answered: a turn with no message verdict in a Bot's own chat uses the Bot's standing approval instead of showing the same card again, and a grant that a turn cannot use says why once in the log (#608).
  • The sandbox preflight now answers for the file policy it is given (--mode, or FLYBOT_SANDBOX_MODE) instead of assuming workspace-write, never reports a danger-full-access session as blocked, reports the Windows ACL runner as unknown rather than unavailable, and separates "proven blocked" (exit 3) from "cannot tell" (exit 4), so an agent no longer stops shell work on a probe it cannot complete (#615).
  • The answers to deleting a Bot, setting a Main Bot, removing the Agent and deleting a group chat now carry only the signed-in member's own scope, like every other team view, instead of the whole team.
  • When someone has signed in, the one DSH Agent (and its Sessions: new session, adopt, rename, archive, remove) stays usable to that member while the team is alone, instead of every call being refused before it runs; with more than one member it follows the same shared-resource rule as adding the Agent.
  • After switching the Bot Social server, the stopped social layer can no longer restart its poll from a face poll that was still in flight, so the old server is not polled (and does not deliver messages) after the switch.
  • A hold on a send now blocks the task that was started last even when two tasks were started in the same millisecond: task order uses a strictly increasing stamp instead of the bare clock, so which task an untagged hold blocks no longer depends on timing.
  • The team-health view no longer calls a Bot idle while it is busy in one long turn that began before the window, and its failure rate is never reported as more failures than turns; a Bot busy through the window is flagged busy.
  • The timeline panel no longer issues a duplicate request when a window change supersedes a poll in flight and the tab is looked at again: a superseded answer no longer clears the in-flight guard of the request that replaced it.

Security

  • set-main no longer lets a member take another owner's Main Bot's place: the endpoint checks the Bot named in replace too, and setMain refuses a replace across owners, so a member cannot demote another owner's Main Bot or promote their own Bot over it (#613).
  • Untrusted-content fences are neutralized more strictly: a marker written with whitespace inside the tag (< / untrusted-message>) is now stripped too, since a parser that ignores whitespace reads the same closing marker. Text from friends' Bots and other untrusted sources stays data, never instructions.

[0.2.2] - 2026-10-21

Added

  • An Inbox row at the top of the sidebar opens one list of your own Bots' work: Needs you (tool approvals, held messages, tasks waiting for you or failed), In progress (running turns and tasks) and Done (the last 7 days). Each card has one clear status and an Open button to its chat; tool approvals from a group chat can be answered right there with the same buttons as in the chat (#349).
  • FlyBot can run in a container that you build yourself: a Dockerfile and compose.yaml start dsh web with the FlyBot core and plugins as a normal user, with one data volume, no keys in the image, the port on the host's 127.0.0.1 only and a health check. A guide in English and Chinese, Run FlyBot in Docker, covers start, stop, upgrade, roll back and back up; CI builds the image and runs a start check, and nothing is published (#352).
  • FlyBot now sends a system notification when one of your own Bots finishes or fails a turn while you are in another chat or app (Coder finished, Coder failed: <reason>), alongside the existing approval-waiting one; clicking it opens that chat, and it never approves, rejects or sends anything. Settings → General has a Notifications switch, on by default, that turns all of them off (#353).
  • Scheduled tasks can be edited and paused: in Settings → Scheduled tasks and a Bot's card, Edit opens the task in its form, filled in, and Pause / Resume stop and restart a task without deleting it. The docs now say what happens to a run missed while the app was closed: a recurring task runs once, for its latest missed time, never once per missed time (#354).
  • Settings → Connectors: pick Composio apps by searching Composio's app list instead of typing app names, and see for each picked app whether it is ready or still needs a sign-in (#359).
  • On a phone browser (about 400px wide) you can skim a chat, press Approve / Reject on a held reply and work the Agent inbox: approval buttons become full-size touch targets, floating approval cards span the screen beside the icon rail, and inbox cards put their buttons under the text so titles stay readable. Wider screens are unchanged (#362).
  • You can now see which model each turn used and which model each Bot runs on: a Timeline turn's tooltip ends with the model that turn ran on (· deepseek-v4-pro), and hovering a Bot in the sidebar tree or Bot list shows its model (Coder · DeepSeek V4 Pro). Nothing new stores keys; models are still chosen per Bot in its settings (#366).
  • Chief now places new Bots sensibly up front instead of piling them flat under itself. Before creating a Bot it can search the whole tree with find_bots and reuses a Bot that already has the same role, at any depth, instead of making a duplicate. create_team sets up a project as one lead with its roles under it (for example Chief → FlyBot Lead → Dev, Test, Maintenance, Repo), which takes one slot under Chief, and create_child_bot can put a Bot under one of the caller's own descendants; a later Bot of a group goes under that group's lead. A Bot whose parent is gone is moved back into the tree by role. A full level, orphans and duplicates stay the job of the tree's automatic reorganization (#447).
  • A guide, Set a DeepSeek API key, explains the ways to give DeepSeek Harness your key (the key dialog, its credentials file, or DEEPSEEK_API_KEY), and the missing-key error and the no-model setup choices link to it (#451).
  • Notifications go further: an approval that is still waiting after 15 minutes gets one reminder (Coder has waited 15 min for your approval), Settings → General → Notifications has a separate switch for waiting approvals, finished turns and failed turns, and each chat keeps a single notification that a newer one replaces and that counts its updates (#469).
  • The Agent inbox now lists a turn of your own Bot that failed under Needs you, with the reason, until the Bot finishes a turn in that chat again, from the same events as the finished/failed notifications; and each inbox card shows a one-line Latest: row with what happened last in its chat (#471).
  • Each scheduled task in Settings → Scheduled tasks now says when it last ran and how it went (still running, finished, stopped, or failed with the reason). A run fails only when DSH could not deliver it or the Bot's turn errored; a failed run gets Retry, which runs the task once now with the same prompt, and the failure also shows up in notifications (#473).
  • Face-to-face rooms have one room lead (总负责人), a Bot from any home. The person who created the room picks it with the Room lead button in the room's details; until then it is the first Bot of the creator's home that joined, and everyone else sees it read-only. The lead sees everyone in the room with their roles, writes the one plan and gives out the parts; every other Bot, a Main Bot too, does only its own part or answers when named. With an older Bot Social server the default lead is used and cannot be changed (#476).
  • Share a Bot with an existing friend in one click from Sharing (no invite link); the new share appears in People with access and can be Revoked or declined as usual (#500).
  • Opt-in state journal for debugging multi-Bot bugs (#511): with stateJournal: true in the bot config, every change to the saved team is recorded on this computer only (<home>/journal/state.jsonl, private to the user, kept 7 days or 20 MB), and the dev script core/scripts/replay.mjs rebuilds the team as it was after any recorded step. Off by default.
  • Tool approval cards in a group and in the Inbox add Always allow for this Bot and Always allow for this group next to Reject / Allow once, so a Bot stops asking for the same kind of permission (for example a bash sandbox escalation to full access). Standing approvals are kept across restarts and listed under Always allowed in Bot settings and Group settings, each with Remove (#540).
  • Memory recall now finds entries by meaning, and shows what memory links. recall gains semantic: "true", which ranks entries with a local, dependency-free TF-IDF score over words, word stems and CJK n-grams, so an entry whose wording differs from the query still matches; and related, which lists the topics a [[topic]] links to and who wrote a memory. The default exact-word recall is unchanged.
  • The Timeline plugin can now be asked for an export on demand. timeline/export returns the current trace as one OpenTelemetry OTLP/HTTP JSON payload — the same mapping the timer exporter uses — so a tool or the panel can pull it now instead of waiting for the next flush. It is read-only, sends Bot and room ids only unless the caller sets names: true, and needs no configuration.
  • The Timeline plugin can now export its trace as OpenTelemetry spans. Set exporter: "otlp" with an otlpUrl (https, or http on localhost only) to POST OTLP/HTTP JSON to any collector (Jaeger, Phoenix, Langfuse…), or exporter: "json" to append dir/export.jsonl. Finished Bot turns become invoke_agent spans and their tool calls execute_tool spans beneath them. Off by default; it exports only the same metadata the panel shows (never message text, tool arguments or output), sends Bot and room ids only unless exportNames: true, and an open turn is held until it ends rather than exported as finished.
  • A tree coordinator can now see how busy its children are. Each Bot's running turns are counted from the activity feed, shown as one line in the coordinator's prompt (Child load right now — Work: busy (2 running), Travel: idle) and a load count on each entry of list_children, so a new part goes to a free child instead of a busy one. Nothing is stored; the counts start from zero on each start.

Fixed

  • Face-to-face rooms: a Bot now looks the same on every home and in every place (waiting tiles, chat bubbles, Members, sidebar). A Bot with no look of its own sends the look its home generates for it to Bot Social, before it joins, so the other home no longer invents a different one; your own Bot in the waiting dialog uses its roster look right after joining; a friend's Bot speaks in the room with the look its owner gave it. Other people show their initial on a color picked from their name instead of a grey silhouette (#182).
  • Past rooms list everyone who was in a face-to-face room (a home that stayed no longer drops the person who left), each row shows when the room ended, clicking anywhere on the row opens it, and the Past rooms fold stays open or closed across a reload (#304).
  • Face-to-face rooms: another person's line, or another home's approved Bot reply, that arrives while your Bots are answering now shows right away as that person's or Bot's bubble, never as a queued "[Group post from …]" row above the composer that Cmd/Ctrl+Enter could steer; a person's line breaks are kept on the receiving side (#455).
  • Face-to-face rooms: a Bot reply held for your approval shows only on its approval card, greyed, and becomes a room bubble only after you approve it; a rejected reply collapses to one grey "Not sent" line, also after a reload and in Past rooms. A decided card leaves no "Approved" / "Rejected" stub, the card names the Bot with its person ("Researcher (Alice) → the room"), and each Bot answers a person's line at most once (#456).
  • Face-to-face rooms are person-first: one tile and one Members row per person (Members counts people, and only people are "(you)"), every Bot named "Bot (person)" including your own, a mention of a friend's Bot stays with that Bot after it leaves, bubble headers show just the name with the handle on hover, and your own typed line keeps your name and colour. Auto-send in a face room is now one switch per Bot, off by default (#457).
  • Face-to-face rooms: a number nobody is using no longer silently opens a room. Join says "Nobody is using 111111 yet." with Cancel and Start a room, and a room nobody else ever joined (left, or run out) leaves no Past rooms entry. A room stays open while two or more people are in it; when you are the last one, it reads "Room closed." with one Close button, and Leave (or Close) takes you back to the chat you were in without opening Bot settings. Both homes count down to Bot Social's one deadline, even when a device clock is off or a tab was in the background. A room's turns no longer take over its Bots' sidebar preview, the room row leads with its number ("482915 · Alice, Bob"), Enter chat steps aside while "Leave this room?" asks, Join waits for all 6 digits, and a click outside the dialog closes it like Escape (#458).
  • Face-to-face rooms: a friend's plain line is now answered like your own: your Bots decide who answers it (usually one, the Bot that spoke last first), and a Bot's @mention in its answer no longer calls another Bot on, so Bots in two homes cannot set each other off (#462).
  • Face-to-face rooms: when a friend's Bot has already answered or posted a plan for a line, your Bots no longer have to answer it again (no forced "nobody answered" reply), a Bot that still speaks to a line for everyone adds only its own part, and a friend's Bot writing @all / @所有人 no longer wakes all of your Bots; its @YourBot still calls that Bot (#475).
  • People with access keeps listing a friend after the two of you joined each other's invite, so the owner can still see and Revoke that share on both homes; FlyBot now uses Bot Social's reply_only mark to tell a real share from a talk-through grant (#495).
  • Accepting an invite for a friend's Bot you already have no longer shows red error text claiming the Bot is back. It now says You already have Chief (SelfA1233). in neutral text, and says … is back in your list. only when a Bot you had removed really comes back. The Remove this Bot confirm now says what it does: Remove Chief (SelfB1233) from your list? Your own Bots stay shared. SelfB1233 can share it again. (#496).
  • A friend's message to one of your Bots, the reply waiting for your approval, the Inbox count and the unread badge now show up in an open FlyBot window within a few seconds, instead of up to 15 seconds later, which looked like it needed a reload (#497).
  • A friend's Bot now has one name everywhere in a friend chat, on both homes: the sidebar row, chat header, tab, composer placeholder and @ menu all read Chief (SelfB1233), from one shared formatter, and the @ menu no longer shows the handle as the Bot's name. A held message's approval card names the person who gets it (Chief → SelfB1233), the same before and after a reload, instead of Chief → Chief (SelfB1233) (#498).
  • Invites and friend chats are tidier. A friend's Bot chat always reads Message Chief (Alice) in the composer, from the first open and after every send, never the coding-agent copy. Add friend and a Bot's Sharing use one invite control: Copy invite link for Chief makes and copies the link, which then shows with Copy link, Copy with instructions and New link, and every copy says which Bot it is for (Invite link for Chief copied.). The Add friend paste box wraps, so a whole pasted link is readable; typing clears an old error, and after an accepted invite the panel shows the result for a moment, then closes and opens the new friend's Bot. The Sharing hint mentions Revoke only once someone has access (#499).
  • Host and zh copy that still said Bot settings → Invite to this Bot after #499/#504 now point at Bot settings → Sharing → Copy invite link.
  • A friend's Bot in a group chat or face-to-face room is now named like everywhere else, Bot name plus its owner's username: Reviewer (flybot682b8a43) instead of Reviewer · FlyBot 682b8a43 (flybot682b8a43). An owner name made up from the install id (FlyBot <id>) gives way to the username in member lists, message headers, @ menu labels and join, leave and room-lead lines; the full handle stays in the tooltip (#526).
  • A key you save, including the GitHub token and the Composio key a connector saves, is now for all of your Bots unless you narrow it; keys that were "No Bot" only because of the old connector default move to all of your Bots once. Settings → Secrets offers All my Bots, Chosen Bots or No Bot for each key (#545).
  • Group chats: every Bot at work shows as one row of the same shape in one place above the message box, the current speaker included, instead of one row on the left and another further right. Each row says what the Bot is doing and on what (for example Running shell commands: npm test), names a tool it has no phrase for instead of "On it", and keeps its elapsed time (#546).
  • Group chats: when a Bot works on something handed out in a group and needs to ask you a question, the question card now shows in that group instead of in the Bot's private chat. Answer it there and the Bot carries on with the work, reporting back in the group; the Bot's private chat stays untouched (#550).
  • Group chats and face rooms: the status rows above the message box and the member list show only work in that room, including work handed out from it. A Bot busy in another group or its own chat no longer shows there, and that room's commands no longer appear in a room with different members (#553).

[0.2.1] - 2026-10-20

Added

  • Bot settings → Model has a Test connection button: one click sends one short request to that Bot's model on the same route a run uses, then shows that it works or a short reason (no or rejected API key, model not found, cannot reach the endpoint, timeout, rate limit). The request may be billed, and the row says so; keys and provider text are never shown (#409).
  • A Bot's reply that went out on its own because Auto-send is on for that pair now says Auto-sent to … under its bubble, so you can see in the chat that it left without your approval (part of #417).

Changed

  • README and README.zh now claim the friend-DM Name is thinking… line (fixed in #360 / #332), matching face rooms.
  • Add friend shows Accept an invite and Invite a friend as two labelled groups: the invite Bot picker is labelled Share and shows the whole name, the button reads Copy invite link for {Bot}, and Done sits at the bottom (#343).
  • A friend's Bot has one name everywhere: the Bot's name, then its owner in parentheses, like Helper (Bob), and a friend's Chief the same way, Chief (Bob) (no more Bob's Chief or Helper · Bob's Bot). The same name shows in the chat header, tab title, composer placeholder, row tooltip, @ menu, approval cards, face-room Members panel, waiting tiles, bubble header and mention chips; in a face room your own Bot reads Chief (you). The @ menu lists friends' Bots in its Bots section only, with the handle as the second line. Lines typed with the older names still call the same Bot (#384, #305).
  • The account menu and command palette name each place once (#392, #393, #394): Settings opens the Settings dialog (it no longer says Bot settings, which is one Bot's own page), Connect apps matches the footer button, Scheduled tasks opens Settings → Scheduled tasks like a Bot's details do, and the switch to classic Agent mode is only in the account menu, so Settings → General is Appearance only.
  • Friend chats no longer run out of turns, and there is no Extend any more (owner decision 2026-10-19). If Bot Social pauses a chat, the chat says so quietly, and your next message simply starts a new chat. Before, Extend said it worked but the next identical message (say, a second Hi) was paused again at once (#404, Bot Social #28).
  • A run that fails because the Bot's model has no API key now says so in plain words and offers the setup right under the error (Enter a DeepSeek API key, sign in where available, or open the model settings), instead of naming an environment variable (#405).
  • Settings polish (#406): a Bot's Sharing (invite and People with access) is now one titled card right after Instructions instead of the end of the page, a group chat's last card is called More like a Bot's, and the message box shows a clearly darker border while you type in it.
  • Auto-send from now on (it was Always approve) on a held reply now turns on Auto-send for that one pair, your Bot → that person, instead of a hidden switch for all your Bots. Each person row in a Bot's Sharing → People with access shows that Auto-send with a checkbox next to Revoke, so it can be seen and turned off; the toast names the person and that place. The friend's Bot settings checkbox now says it covers all your Bots: Auto-send my Bots' replies to … (#417).

Fixed

  • The NEW divider no longer sits in a chat for the whole visit. Once you have seen it and the chat moves on (a friend's next line, or your own message), it goes, the same as when you leave the chat and come back. This also covers a friend's typed line in one of your Bots' own chats (#275).
  • A face-to-face room's @ menu lists only the room's people and Bots: DSH's own Sessions rows and their section title no longer show there, arrow keys skip them and Enter never picks one; elsewhere a group's Everyone row counts the Bots it reaches, seated friends' Bots too, as 1 member / 2 members (#305).
  • When a friend's Bot tries to start a chat with one of your Bots that is not shared with them, Friends' Bots now shows a "Share this Bot?" card with Share… (opens that Bot's Sharing) and Dismiss; nothing is shared automatically. The sender sees that the message was not delivered because the Bot is not shared with them yet, instead of a silent "delivered" (#336).
  • A friend's Bot row uses the same full-width hover and selected band as an own Bot (the settings gear no longer shrinks the highlight, including at 150% zoom), and a friend's Bot fallback avatar never matches the exact shape and color of one of your own Bots on any surface (sidebar, header, palette, details, transcript) until its real look syncs (#384).
  • A paused friend chat, and any other failed send to a friend's Bot, no longer tells you to open the Bot Social console (which a FlyBot owner cannot sign in to); it names only what you can do in FlyBot: approve a waiting message, or remove and re-add the friend (part of #404).
  • Models and providers… and Manage models now open settings on the Models page instead of General settings (#407).
  • A Bot that the Main Bot (or a tree parent) creates without naming a model now starts on its creator's model and thinking level before its first request, so it no longer fails first on an unconfigured default model (#410).
  • A Bot's reply to a friend that waits for your approval now looks held where it is: its bubble is dashed, and Approve / Reject (with Always approve) sit on it instead of a separate card above the composer. The card names the person who gets the reply, and afterwards the bubble says Sent to … or Not sent, so a rejected reply never looks sent; the chat list preview no longer shows a reply that did not go, and the Bot's row shows the wait badge until you decide (#418).
  • A friend's Bot chat is quieter (#419): no Tool removed: create_child_bot · … line from plugin tool masks (in any chat), no local turns/steps/tok/s footer (the friend's Bot runs on its owner's home), and the composer keeps Message <Bot> after a send instead of switching to the coding label.
  • The run-stats footer under the composer says 1 turn 1 step instead of 1 turns 1 steps (#419).
  • A friend's line in one of your Bots' own chats is no longer easy to miss: the Bot's row (and the Main Bot's tile) gets the unread dot until you open that chat, also when the line came while FlyBot was closed, and a sender's first line opens with a quiet note such as SelfB1238 is talking to Chief through Bot Social. Add friend now also lists friendships and requests made through a per-Bot invite (#420).
  • Settings polish (#421): Settings → Bot Social names what the server supports in words (Supports: typing as you, @mentions, online status; the raw ids only in the tooltip), Harness settings ↗ says it opens DeepSeek Harness's own settings in a separate window, and opening a Bot's settings from its sidebar gear no longer leaves the sidebar or the page shifted sideways.
  • A message sent right after joining an invite is no longer silently lost: the receiving FlyBot records the new friend's grant from Bot Social's join event before it reads the first message, syncs grants once and checks again if a message is still refused, and otherwise tells the sender, whose chat shows a "Not sent" line (#442; the join event and the notice need Bot Social with Birfy/bot-social#31).
  • Friend chat avatar and thinking row (#443): in a friend's 1:1 chat, the friend's Bot's replies now carry its mark beside the bubble, the same one its sidebar row shows. One thinking row with that mark stays up from your send until the reply lands. It no longer flashes away after the send, and no second "… is thinking…" line appears below the reply.
  • Bots made outside the tree (Chief's create_bot, the Bot list) now always join the tree under the same maxChildren and maxDepth limits as create_child_bot, instead of staying outside while the sidebar showed them under Chief. The tree organizes itself by role: a new Bot goes into the group that matches its role, and when a level is full, and once a day, related Bots are grouped under a suitable existing lead or a new lead with a clear role (for example "FlyBot Lead" or "Life Lead"); it never puts a Bot under an unrelated one. The daily pass also merges duplicate Bots and marks long-idle ones idle, archiving them after 30 days; nothing is deleted. Every change is logged, the sidebar shows what the last reorganization did with one-click Undo, and Chief knows about it (#446).
  • Tree reorganization moves / reparents existing Bots; it never renames them to 归档- and recreates. Archived Bots are hidden from the default Bot list. A one-shot repair restores leftover 归档-* / Archive-* names.

0.2.0 - 2026-10-19

First release under real semantic versioning (earlier cuts were 0.1.0 and the core package's 0.1.5-flybot.1). The core package, the tag and this section now carry the same version, 0.2.0; patch releases follow when changes land. Assets: ds-bot (the core) plus the @flybot/social, @flybot/tree and @flybot/timeline plugin tarballs.

Highlights

  • Sharing is per Bot, and friends live in your sidebar. There is no global "Published to friends" switch: every share is a grant from one of your Bots to one person (optionally scoped to a group), default deny, Chief included. Invite a friend to a Bot, or paste an invite (a link, a link without https://, or a bare code) under Accept invite; Sharing and People with access list each person once, with Revoke. Friends' Bots and live face-to-face rooms sit in the same sidebar list as your own Bots and groups; the separate Friends' Bots section is gone. A friend's Bot has its own compact settings sheet (Auto-send, Remove this Bot) opened from a gear on its row. (#90, #145, #160, #343, #384)
  • Approvals happen where the conversation is. A message Bot Social holds for you shows as a compact card in the chat (Approve / Always approve / Reject; only the sender Bot's owner can decide), with a wait dot on the Bot's row and one pill in the corner. A line you type yourself goes out at once, as you. New friends start with Auto-send off. (#52, #114, #140, #260)
  • Face-to-face rooms. People next to each other join one group by typing the same 6-digit number, each with one or more of their Bots (a Bot with no identity is registered for that room only). The room has a 10-minute countdown, Enter chat, one-click Leave (it asks first and lands on your main Bot), and Past rooms with a read-only transcript. A plain line gets one answer (each Bot decides whether it is for it; a follow-up goes to the Bot that spoke last; a line to everyone gets a short answer from each); @Bot works across homes and is sent to the friend's FlyBot as an explicit list. A friend's Bot shows is thinking… while it works. Lines you type are attested as yours (typed_by_owner), not as your Bot's. (#83, #92, #287, #293, #297, #299)
  • Connectors and Composio. Settings → Connectors has a Composio row: paste one API key, pick apps (Gmail, Google Calendar, Slack, Notion, Linear and more), and every Bot can use them. Connector tools (GitHub, Composio) are on by default for you in every Bot, new and existing. When someone else drives a Bot (a friend, another room member, another owner's Bot), they are off unless you turn on that Bot's separate also when others use it switch. (#311, #335, #369)
  • Automation tasks work out of the box. The first time FlyBot runs in a DSH profile it switches on DSH's Automation tasks bundle, so Settings → Scheduled tasks works at once; switching it off on the Plugins page keeps it off, and automationTasks: false opts out. (#378)
  • Bot Social server setting. Settings → Bot Social shows the server FlyBot uses and its connection, and switches to another https:// server (checked against /v1/platform, with a confirm because a new server is a new identity) without editing YAML or restarting. (#337)
  • About FlyBot describes FlyBot: Star count, Source code, Release notes and Report on GitHub point at ThinkFlowLab/FlyBot, the version line shows the branch and short commit of a git install (and how far behind it is), and the DSH-update check reads FlyBot's own declaration. (#374, #379)
  • Bot Social, owner-chosen look and tracked tasks. A Bot's avatar and color sync to Bot Social and show on friends' Bots, groups and rooms (#137). Requests can be tracked tasks with a stable id, acceptance criteria and a visible state (claimed, worked, blocked on an approval, returned, accepted or cancelled) in the task API; task views in the chat come later (#54, #202, #207).
  • @flybot/timeline (optional). A new plugin records Bot turns, tool calls and Bot-to-Bot deliveries (ids, times and outcomes only, never message text) and draws swimlanes, a who-waits-on-whom map and per-Bot utilization. It is off by default: nothing is recorded or shown until the profile sets enabled: true (and sidebarSection: true for its sidebar row). Plugins also get the flybot activity feed (turn.*, tool.*, exchange.delivered), flybot.warn for the diagnostics log and the flybot.settings.page slot. (#68, #241, #281, #337)

Changed

  • Sidebar + menu is a short list of actions with a mark and a one-line hint each: New chat, New Bot, New group chat, Face-to-face room, Add friend, Add coding agent. Add friend names you (You are @handle) and lets you pick which Bot an invite shares. Your own handle with Copy sits at the top of the account menu. A Bot's right-click menu has Copy invite link, and renaming is in Edit Bot. (#131, #173, #181, #195, #385)
  • Create Bot starts on the model your Main Bot runs on, then the DeepSeek Harness default, then the first available model. (#230)
  • The sidebar footer button reads Connect apps and opens Settings → Connectors; the plugin panel is under Plugins in the account menu.
  • Branding. The web UI says FlyBot (sidebar header, settings, About, update popups, Chinese copy) and uses the FlyBot mark. (#108)
  • Friends' Bots on a slow link paint a cached roster at once with a small Refreshing… mark, load their data in parallel and poll approvals every 15 s (paused while hidden). Face rooms poll about every 4 s while live. (#96, #133)
  • Face-to-face UI. Pick-and-wait tiles use each Bot's own avatar; rooms are one row each (name, avatar stack, status chip, Open, Leave); titles carry the people and the room number or time; the join dialog explains a wrong number and counts down as m:ss. (#107, #115, #132, #306)
  • Tree Bots may create children directly under themselves (create_child_bot; maxChildren and depth limits stay), and a child is usable only by its creator's owner until it is granted. (#16)
  • list_bots lists only your owner's Bots, Bots granted to that owner and Bots in the current room. (#53)
  • Settings polish: no repeated titles on the Model and Main Bot cards, one back control in Create Bot, Bot Sharing shows the link first with Copy link. (#230)
  • Internally, DSH group execution runs through a participant driver; room scheduling and permissions are unchanged.
  • README: demos are re-recorded on the current UI, and face-to-face rooms, typed_by_owner / mentions / presence, Sharing grants and the FlyBot → Bot Social setup are documented.

Fixed

  • Remove this Bot is immediate and stays removed (a hide is forgotten only after two clean roster results); removing declines the grant on Bot Social, and joining again restores it. (#139)
  • Accepting an invite shows the shared Bot at once, never lists the joiner's talk-through Bot as shared back, and gives plain messages for an unknown, used, expired or own invite. (#161, #228, #343, #347, #383)
  • A friend's Bot reply that waits for its owner always has a visible card (the overlay no longer crashes, and one click leaves the message at once). A held line shows its text once, and a hold shows only in the room it came from. (#188, #254, #258, #306, #371)
  • Face-to-face rooms keep consistent titles, times, status chips and Past rooms on every home; a Bot you bring in no longer gets a stray "left" line; a friend's multi-paragraph line arrives whole; the composer's @ lists each room Bot once; @ chips look the same on both homes; typing in a room never starts a Bot turn unless a Bot is mentioned. (#122-#125, #129, #158, #167, #170-#172, #182, #191-#194, #236, #302-#306)
  • The web UI no longer stops at "Failed to load plugins", the main view stays on its chat when the session list reloads (#261), the tab title follows rooms and groups, and the raw "Tool removed: ask_user_question" line is gone.
  • Bot Social identity. A taken home name retries FlyBot2, FlyBot3, … at first start (#136), and @flybot/social loads from a packaged install (#153).
  • Friend DMs. Chat previews show a friend's words without the <untrusted-message> wrap; a paused conversation says so; a reply to a friend who wrote to a shared Bot is held for approval instead of dropped; a Bot that may not message a friend's Bot says why; the raw @objectobject access labels are gone. (#72, #130, #164, #272, #273)
  • Groups hold friends' Bots and show up as shared rooms on their owner's FlyBot, and a group whose model did not apply the first time is repaired on the next refresh. (#289)
  • Tasks: a held send blocks only its own task, and a returned result can be cancelled before it is accepted. (#111, #202)
  • About no longer says "Up to date with main" after a reset or force-push, and shows the commit of a git install even when DSH hoists it. (#374, #379)
  • Timeline shows a friend's Bot as a friend lane, no longer stays on Loading… in a background tab, and has clearer lane names, waits and a range footnote. (#248, #249, #255, #259, #269, #279)
  • Smaller fixes to sidebar rows, the tree chevron hit target and the + menu popover (#177, #276, #283).

Security

  • Private context stays home in shared rooms. A Bot carries its owner's chat brief into a room with another owner only when the owner shares it for that room; memory reviews keep entries in the Bot's own memory; referencing a chat you cannot read is refused. (#53)
  • Held sends are bound to their owner. Only the sender Bot's owner can approve, reject or retry a hold, you see only your own holds, and a decision is voided (never sent) when the request or the authority behind it changed. (#52)
  • Chat-reference chips are author-checked, and Bot-authored posts drop them, so a private chat cannot ride into another owner's Bot. (#106)
  • Friend Bot lines are fenced in every shared room, and peer-published names are sanitized so a display name cannot pose as another member. (#75, #98)
  • Face-to-face lines show the authenticated sender, not a name the client chose; a Bot's room message cannot pass as its owner's typed line; and accepting a per-Bot invite grants only that Bot. (#118, #138, #179)
  • Connector tools are decided per Bot and per turn, so a friend who reaches your Bot cannot use your mail, calendar or repositories through it unless you allow it. Composio's session address is no longer stored in state.json. (#335, #369)

Upgrading

  • No Bot is shared until it is granted: sharing from earlier builds does not carry over.
  • Connector tools that an earlier build left off are on again for you; an explicit off you set stays off.
  • Timeline needs enabled: true in the profile to record anything.
  • Presence (is thinking…), typed_by_owner and @ mentions need a Bot Social server that supports them; against an older server FlyBot falls back to the previous behaviour.

0.1.0 - 2026-10-14

First public FlyBot-branded cut (core package remains 0.1.5-flybot.1).

Highlights

  • Peer plugins @flybot/tree and @flybot/social under plugins/
  • Pristine ds-bot + FlyBot patches → core/ at 0.1.5-flybot.1
  • Send middleware priorities ROUTING / DEFAULT / APPROVAL
  • types/flybot.d.ts documents the browser flybot service

Changed

  • The core is now pristine upstream ds-bot (ds-bot/, pinned at Fei2411/DeepSeek-Bot@2b10c70) plus FlyBot's ordered patch series (patches/ds-bot/), applied into the committed core/ (#34). Install paths move from path:/ds-bot to path:/core and path:/plugins/{social,tree}. The core's version is 0.1.5-flybot.1.
  • Send middleware can set a priority (ROUTING 100, DEFAULT 500, APPROVAL 900; flybot.bot.send.priority); names in before/after still work and win over priority. @flybot/social runs at APPROVAL.
  • types/flybot.d.ts documents the browser flybot service and its slots.
  • The plugins are peer packages at the repo root: @flybot/tree (plugins/tree, by recursive-bot) and @flybot/social (plugins/social, moved out of core/plugins/social). Each needs ds-bot >=0.1.5-flybot.1, tests itself against core/, and has its own CI job (plugins (tree), plugins (social), both required by tests-ok). Reinstall the social plugin from path:/plugins/social.

0.1.5 - 2026-10-13

Added

  • Working folder: a Bot can work directly in a folder you choose instead of the team workspace. Set it in the Bot's settings or details panel: type a path, pick one of your DSH projects, or browse. A group chat can set one folder for all its members there. You can also ask a Bot to change its own folder, or a Main Bot to change any Bot's or group's folder. A Bot that sets a folder outside the team workspace, or adds a Bot to a group that works in one, asks for your approval first when those Bots can only write in their folder. After a change, the Bot's chat goes on in a fresh part that starts in the new folder.

Fixed

  • A new Bot asks where to start in its first reply instead of on a question card, and follows its own instructions where they say otherwise.
  • Bots no longer use DeepSeek Harness's own question card, which held the Bot until you answered it while messages from other Bots waited.
  • A Bot that messaged a Bot stuck on earlier work was told "Delivered" every time, with no sign that nothing was read. It is now told how many earlier messages wait unread and when the other Bot waits for your approval, and after 10 unread messages message_bot fails and tells it to report the stuck Bot to you.
  • On model providers that need every request to name its conversation, such as OpenCode Go, the memory panel, automatic memory updates, the context check of long chats and image reading for text-only models failed. They now work there too.
  • The memory panel's summary, its "ask or update" and the memory review no longer stop when the Bot's model refuses the call or runs out of output budget: they try once more on the profile's default model. After you change the Bot's model, a failed summary is written again automatically. (#116)
  • With the ptc agent preset, the team view and a Bot's character show the tool the Bot runs from its code, an approval card says what that tool does, and a question card asked from code is kept in chat search and in the next part of the chat. In a group chat, a member that only reads the chat or checks the team no longer leaves the round as if it had gone off to work.
  • Switching from Bot mode back to Agent mode no longer leaves parts of the Bot interface on screen, such as the chat header or Bot messages without their styles, when one part fails to close. The other parts still close, and Bot mode opens normally again.
  • DS Bot no longer sets DeepSeek Harness's work-details mode (Settings → General) to the most detailed one, and no longer hides that setting. Bot chats stay flat in every mode, and your choice applies again outside them. If you had chosen another mode before, choose it again once: DS Bot cannot tell which one it was.
  • Your own messages in a Bot chat stay readable under dark skins that recolor message bubbles.
  • A Bot chat now shows the cards of the files a Bot hands over with DeepSeek Harness's present tool, and of the files a turn changed, as DSH's own chat does. A file path in the reply links to the file when the turn made or delivered it.
  • With the ptc agent preset, the chat shows the tools a Bot runs from its code again: group replies, question and key cards, and the lines for messages, Bots, groups and memory.
  • A question or key card whose call is not on screen, such as one asked in an earlier part of the chat or in a Bot-to-Bot turn, waits above the message box. A card shows in one place only.
  • With DSH_HOME empty or starting with ~, DS Bot now keeps its data in bot/ inside the folder dsh uses. Before, it used a folder relative to the one dsh was started in; to keep a team made that way, close dsh and move that bot/ folder into dsh's folder.

0.1.4 - 2026-10-13

Added

  • Settings → Capabilities: share MCP servers and skills with the team, group them into packs, and choose which capabilities each Bot can use.
  • A Bot that was working when DeepSeek Harness closed or restarted picks its work back up when DS Bot starts again. A group chat shows which member was cut off, and a Bot that was answering another Bot still sends its answer.
  • The sidebar marks a Bot or group chat that waits for your approval. Where the system allows notifications, a notification also names the Bot.
  • In a group chat, any Bot can hand work to another with @Name. The Bot does the work without holding up the group, and the group, the sidebar and the Bot's own chat show what it is doing. When it is done, it posts the result in the group. You can stop the work in the group's details.

Changed

  • What a group member posts after its round moved on is answered by the Bot that asked for the work, and no longer by the admin. Work you asked for yourself is left for you to read.

Fixed

  • When a model wrote its tool call as text instead of making it, nothing ran and the Bot stopped as if it were done. The Bot is now asked once to make the call, and the text of the call is kept out of group chats.

0.1.3 - 2026-10-12

Added

  • In a Bot's chat or a group chat, @ lists the Bots you can mention first: a group's members and Everyone, or the other Bots. Picking one writes @Name.
  • Reference a chat in the composer's + menu adds another Bot's chat, or a recent conversation outside the team, to your message as context.
  • A Main Bot can stop another Bot's running turn. The Bot is told why, and its reply goes back to the Main Bot.
  • In a group chat, a member that asks for an approval no longer holds up the round. You allow or reject the request in the group's details panel, and a line above the composer names the members who are waiting or at work. What a member says after the round moved on is posted in the group.

Changed

  • When a Bot messages another Bot that is working, the message reaches it at its next step instead of waiting until its current work is done. It answers once that work is done, and the reply comes back. A Bot can still queue a separate task behind the current work.
  • Bots give a brief progress message when starting a multi-step task in their own chat with the user, and at meaningful milestones on long tasks.
  • Every Bot can change its own name, role, color, model and instructions; only a Main Bot changes other Bots.
  • Updating a copy installed from GitHub uses the repository's current address.
  • The Usage page groups its statistics, chart, rankings and activity into one panel.
  • Hover over an Activity day to see its token count.

Fixed

  • When DS Bot cannot save the Bot team, the change now fails with an error instead of being lost silently.
  • A Bot team file that cannot be read is no longer replaced by an empty team; a damaged file is kept as a backup.
  • Warnings in the host log hide keys and tokens.
  • Bots receive the current time on their first model step and refresh it after ten minutes, even when their DSH preset does not supply a clock.
  • In a group chat, a conversation referenced in a message reached none of the members, and a reference whose title started with a member's name called that member. Now the members who answer the message get the referenced conversation.
  • In a group chat, typing @ and a Bot's name and pressing Enter inserted a conversation reference instead of the Bot's name.
  • Deleting a Bot or group chat also removes its scheduled tasks, including ones left on the Bot's earlier chat parts. Before, armed tasks stayed active and kept running the deleted Session; the archive that would have stopped them was refused and the failure was silent.
  • A long conversation's fresh part keeps the Bot's model and thinking level. Before, the new part fell back to the default model.
  • A provider adapter that cannot measure tokens no longer fails every turn. The context size is estimated instead, with one warning in the log.
  • When the recent errors were too long for Report on GitHub, the new issue got no diagnostics at all. Now it keeps the versions, the system and the models, and only the errors go by the clipboard.
  • The memory panel's summary works on models that think by default. The call asks for the model's lowest thinking level, and when the answer is cut off at the output limit it is written once more with a larger budget instead of failing or being saved half-written.
  • A message sent to a group chat while a round is running joins that round before the next member speaks. Everyone still to speak sees it, and the members it is for answer it in the same round. Before, it waited for the whole round to end. A message with images or a referenced conversation still waits for its own round.
  • In your own messages, @Name of a Bot shows as that Bot and opens its chat, instead of a file named after the first word. A group chat calls exactly the Bots a message shows as called: code, links, web addresses and file references such as @docs/guide.md call nobody.
  • A Bot chat no longer stays stuck when the model keeps turning down its requests, for example over an image it cannot read. Images from finished turns are no longer sent with every later request. When a request is turned down on two turns in a row, the Bot moves on to a fresh conversation part and tells you that the last message could not be sent.

0.1.2 - 2026-10-11

Added

  • Settings → About DS Bot: the DS Bot and DSH versions, Check for updates and the update buttons, and links to the source code, the release notes and the feedback page. A Star button opens the GitHub repository and shows its star count.

Fixed

  • The update card offers a DSH update only when a DS Bot release names a verified DSH version. DS Bot 0.1.1 offered DSH 0.2.1-alpha.1, which was not verified. In DSH Desktop the card shows a DSH update only when the update the app offers is the verified version.

0.1.1 - 2026-10-11

Tested with DeepSeek Harness 0.2.1-alpha.1 (DSH Desktop and DSH Web).

Added

  • Voice chat uses the local speech recognition of the official DSH Voice Input plugin (SenseVoice) when the plugin is on, and the browser's speech recognition otherwise. When the plugin has no speech model yet, the voice panel opens its settings.
  • The update card also offers DSH updates, but only the DSH version that the newest DS Bot release was verified with (dsBot.verifiedDsh). A DSH installed with npm updates in one click; DSH Desktop opens its own update panel.
  • Each Bot can pick a thinking level with its model, in the model menu or when the Bot is created. Default uses the model's own level.
  • A permission setting in each Bot's details panel: Read only, Workspace or Full access, from the presets DSH offers. The Bot keeps it in its own chat, its group chats and its new conversation parts.

Fixed

  • A handoff note whose headings the model translated (for example ## 目标) is kept, with the headings put back in English, instead of being written again. A note with a repeated or missing heading is still written again. Handoff notes keep file paths in full.
  • Group the first model choices by provider, so models with the same name can be distinguished. Providers with the same display name also show their provider IDs.
  • A turn with many steps shows one "Thought it through" row instead of one per step. The row holds the thinking of every step in order.
  • The Usage page counts the calls of subagent Sessions too. Before, each scan skipped them and wrote one warning per subagent Session to the terminal.

0.1.0-dsbot - 2026-10-10

The pre-FlyBot DS Bot preview lineage, retitled so its version anchor does not collide with FlyBot's 0.1.0.

The first public preview. Tested with DeepSeek Harness 0.2.1-alpha.1 (DSH Desktop and DSH Web).

Added

  • A team of long-lived Bots. Each Bot is a DSH Session with its own name, role label, instructions, model and character. Every Bot runs on one model of its own, chosen from the models configured in DSH (the list_models tool lists them). Creating a Bot opens a form: pick its look, its model (the DSH default model comes first) and its name.
  • Main Bots that create, change and remove other Bots. You can have more than one Main Bot, change the Main Bot in one step on Settings → Bots, and move one to the front; there is always at least one. A Main Bot wears a large star on its avatar.
  • Messages between Bots, questions to you that do not stop the other Bots, and a view of what each Bot is doing.
  • Group chats in which members speak in turn, can be called with @name and stay silent when they have nothing new to add. A new group chat starts with its admin, who wears a small star and manages the group. Every group chat has its own settings page: name, notice, who answers, members and admin, pin and hide, and delete.
  • The DSH Agent: one plain DSH entry in the sidebar beside the Bots, with its own Sessions on the native DSH conversation page and none of the team tools.
  • Long conversations that stay inside the model's window. The context lines follow the model's contextWindow (500,000 tokens when a model reports none), and the check line is a share of it (checkRatio, default 0.8). When a turn ends past the check line, a small separate model call decides once: condense the conversation in place into a checkpoint (a handoff summary plus the user's recent messages word for word) while the work is under way, or let the Bot write a handoff note and continue in a new part when the work has reached a stopping point. Neither leaves a marker in the chat. DSH's own compaction does not run on Bot conversations. Earlier parts and condensed messages stay in a local full-text index that the Bot can search.
  • Memory. Each Bot keeps notes across all its conversations (MEMORY.md and topic files in the Agent Memory Repo format), and the team shares one more memory that Main Bots and group admins write. Bots use the remember, forget and recall tools, and after each turn the Bot's model reviews what is new and keeps, updates or removes entries itself (memoryReview: false turns this off). Each conversation reads the memory once, when it starts, so the prompt cache holds. Sizes are fixed, keys and tokens are refused, and memory/journal.jsonl keeps every change. memory: false turns memory off.
  • The memory panel, opened from the Memory row in a Bot's details panel or settings, or from "Memory updated" in its chat: a summary that the Bot's model writes from the entries, every entry with remove, and "Ask or update" to ask about the memory or change it in plain words.
  • Scheduled tasks for Bots and group chats: once, every day, every week on chosen days, or every so many minutes, hours or days. Add them from Settings → Scheduled tasks or from Scheduled task in the composer's + menu. A task sends its message to the chat when it is due. The tasks run on DSH's Automation tasks.
  • A Usage page in Settings: token usage over the last 24 hours, 7 days, 30 days, 12 weeks or 12 months, or over days picked on a calendar, with the change from the period before, a chart by Bot or by model, a ranking of Bots and models, and a year of daily activity. Each Bot and each model opens its own page. The counts are kept in usage.sqlite. Tokens only, no cost.
  • A Connectors page in Settings, with GitHub as the first connector: paste a GitHub token to use GitHub's tools for repositories, issues and pull requests. The token is kept as the GITHUB_TOKEN secret. github: false hides it.
  • The auto web-search provider: the native search of the current model route, with a keyless fallback to public results pages.
  • A Bot browser in DSH Desktop: each Bot has its own browser with tabs and its own storage partition, in the same side panel as the Bot's details. The Bot reads the open page as structured Markdown (headings, lists, tables, code blocks, open Shadow DOM, same-origin frames) and never reads password, card or one-time-code values.
  • Secret cards: a Bot asks for an API key or token in its own chat, you choose which Bots may use it, and the Bot gets it only as a shell environment variable. Saved values are hidden as [secret:<NAME>] in tool results and model requests. Settings → Secrets manages them.
  • Text descriptions of images for models that cannot read images, made by a configured model that declares image input.
  • Customization of the name, role label, instructions, model, character, theme and accent color. The first Main Bot is a whale in DeepSeek blue.
  • Avatar animation in three levels (Settings → Bots → Animation): Quiet, Normal (the default) and Lively. Panels, menus and controls move on springs.
  • Settings with pages for General, Bots, Group chats, Connectors, Scheduled tasks, Usage, Secrets and Feedback.
  • The interface follows the DSH language (English or Chinese).
  • Classic Agent mode (Settings → General) gives the window back to the native DSH interface; the DS Bot button at the bottom of the sidebar brings the team view back.
  • Settings → Feedback: the DS Bot and DSH versions, the system and the recent errors, with Report on GitHub, Copy, Download and Clear. Keys, tokens, saved secrets and the home folder are removed from the text. Errors are also written to <home>/logs/diagnostics.jsonl (diagnosticsFile: false keeps them in memory only), and a failed request shows a short notice with a Details link.
  • Update checks: DS Bot looks for a newer release where it was installed from (the npm registry, or GitHub for a versioned git install) and shows a cloud button at the sidebar's top right when one exists. One click installs it; the new version runs from the next DSH start. updateCheck: false turns the checks off.
  • One writer per team: when DSH Web and DSH Desktop share a DSH home, the second one opens the team read-only, says why, and takes over when the first one quits.

If you used the legacy dsh-bot package, remove it before you install ds-bot. The team data stays in <DSH_HOME>/bot, and the theme, Bot colors and browser settings move over the first time DS Bot opens.

Known limitations

  • Tested with DSH 0.2.1-alpha.1, mainly on Linux and in DSH Web. DSH is an alpha, and a newer DSH can break the plugin.
  • The Bot browser is in DSH Desktop only, and Bots can only read pages (Bot browser).
  • Secrets are stored without encryption, and only exact copies of a value are hidden (Secrets).
  • Only one DSH can change a team at a time; a second DSH on the same DSH home opens it read-only (Data and backups).
  • Group chats use many tokens: each member reads the group and answers in turn.