diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e0756e35..76a6e7faa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,27 @@ New entries are written as one fragment file per change in [changelog.d/](change ## [Unreleased] +## [0.2.28] - 2026-11-10 + +### Fixed + +- A window that takes over a workflow store after the other window closes no longer writes the copy it loaded while the other one was still running: the first read or write after it becomes the writer re-reads `workflows.json`, and it reaps the run the closed window left behind instead of leaving the lane blocked. A card that was only queued is kept and starts normally. See [#2704](https://github.com/ThinkFlowLab/FlyBot/issues/2704). +- `inflight.json` — the record that lets an interrupted turn resume — is now staged in a temp file and renamed over the old one, so a crash in the middle of the write leaves the previous complete notes instead of an empty or half-written file. Every turn and queued message the crash cut off is still picked up at the next start, where an empty file used to drop them all silently. A file that still cannot be parsed (a hand-edit, a disk error) is now reported once with its path and the reason instead of passing for "nothing was in flight", and a temp name a crashed write left behind is swept by the next host that writes. See [#2705](https://github.com/ThinkFlowLab/FlyBot/issues/2705). +- A timeline window that opens while another window is writing the trace no longer loses every span for the rest of its life: it asks for the writer lock again when it has something to write, begins persisting as soon as the other window closes, and keeps the segments already on disk. Until the lock is free a flush that wrote nothing answers `false` and names the reason once, instead of reporting success for a dropped span. See [#2706](https://github.com/ThinkFlowLab/FlyBot/issues/2706). +- Every message `read_own_chat` shows now names its part in the anchor, `part N #`, from the first message of a chat onward, so an anchor a Bot saved before the conversation moved to a second part still opens that same message afterwards. A bare `#` in an anchor written earlier keeps working and reads the part that is current now, but the answer says the part was guessed instead of reporting an exact hit — previously it silently returned a different message from another part and said `exact: true`. Anchors written the old way remain valid; nothing needs re-reading. See [#2710](https://github.com/ThinkFlowLab/FlyBot/issues/2710). +- `read_own_chat` no longer shows every message twice when a second window or a plugin activation indexes the same chat: the part's mark is read inside the transaction that writes the rows (so the second writer sees the first one's commit), and the index's `lines` table now refuses a second row for the same event. An index an older build already duplicated is rebuilt once from the Session logs on the next start, and the log says how many duplicated lines it found. A window another dsh keeps read-only now reads the existing `chat-index.sqlite` without creating, repairing or writing it, instead of writing the index behind the other window's back. See [#2711](https://github.com/ThinkFlowLab/FlyBot/issues/2711). +- A saved team whose group-chat record carries no `members` list (a hand edit, a restore from another machine, or a version-1 home from before the field) no longer makes FlyBot fail to start: the record's list is repaired to empty when the team is loaded, the room itself is kept, and the repaired team is saved, instead of every later load rejecting with no warning and no way to repair it from the UI. See [#2714](https://github.com/ThinkFlowLab/FlyBot/issues/2714). +- A saved team file whose fields that hold tables arrive as `null` — or as something else that is not a list or a record — no longer loads as a writable team that then fails every write. `exchanges` is repaired to an empty list and `exchangeTurns`, `questions`, `rooms`, `bots`, `prefs` and `agents` to an empty record when the team is loaded, and the repaired team is saved, so creating a Bot, renaming a room, posting in a group, a memory write and a delivery all work again instead of each throwing a stack trace that never named the team file as the problem. See [#2715](https://github.com/ThinkFlowLab/FlyBot/issues/2715). +- A team file that is empty or half written is now described as what it is instead of telling the person to update FlyBot. `state.json` holding `{}`, `null`, `[]` or an empty string parses but carries no `version`, and the read-only sentence used to read `saved by a newer FlyBot; update FlyBot` — a repair that cannot help an install already on the current version. It now reads `the team file has no version — it is incomplete (an empty or half-written file)`, so the first thing a broken install shows points at the file to move aside. The newer-version sentence is kept for a file whose `version` is above this build's, the file is still never changed, and a zero-byte or truncated file keeps its existing backup-and-load-fresh path. See [#2716](https://github.com/ThinkFlowLab/FlyBot/issues/2716). +- A Bot Social settings or identity file that exists but cannot be read or parsed is no longer treated as "nothing was ever saved". A torn `flybot-social-bots..json` used to make a server switch silently restart the Bot as a stranger — no member identity, no grants — and the next switch rewrote the only copy; the switch now refuses before anything moves, names the file and leaves its bytes alone. A torn `flybot-social-server.json` refuses at start instead of quietly connecting to the default server as if no server had been chosen, and a damaged credentials file of another server is warned about and left out of the known-server list. A file that is absent still means a first run. See [#2717](https://github.com/ThinkFlowLab/FlyBot/issues/2717). +- The decision log no longer shares one temp file between writers: the trim stages the retained log at `decisions.jsonl...tmp` and renames it over the log, so two hosts of one team folder cannot take each other's temp away and report a write that never landed — the `a decision was not written: … ENOENT …` warning, and the trim that was lost with it, are gone ([#2724](https://github.com/ThinkFlowLab/FlyBot/issues/2724)). A read-only window, the second host of one team folder, now writes nothing at all: `record_decision` there appends no line, trims nothing, fires no `decision` event, and answers `Not recorded: this window is read-only, …` instead of claiming a record that is not on disk. +- Pausing or resuming a workflow run while one of its steps was still dispatching no longer loses the control: both now land on the run record the lane holds, so a run paused during a dispatch stays paused when that dispatch returns and a run resumed during one stays resumed — the stage no longer reads `已暂停` while the run is `running` with its step deadline re-armed and no Resume pressed ([#2734](https://github.com/ThinkFlowLab/FlyBot/issues/2734)). +- A card parked behind the concurrency limit is no longer dropped when the host refuses to start a Bot for it once: the parked row goes back to the head of the queue and starts on the next drain, instead of leaving the queue while the stored claim still answered `already: true` — which left the card in nobody's hands, with no run naming it, until a human used `rematch` ([#2735](https://github.com/ThinkFlowLab/FlyBot/issues/2735)). +- A workflow store that parses but holds an entry that is not a workflow now answers `store-unreadable` instead of throwing out of every read of `workflow/state`: the panel gets the repair hint, and the damaged bytes are kept as `workflows.json.broken-` before the next write, instead of a raw `TypeError` escaping each render ([#2736](https://github.com/ThinkFlowLab/FlyBot/issues/2736)). +- A memory review answer that named a topic memory cannot use no longer throws the whole answer away: only that change is left out, the valid `add`, `edit` or `remove` beside it lands and the cursor moves past the lines that were read, and one warning per answer — not one per process — says how many this answer lost and why the first one was, so a later answer in the same Session still warns again while a secret keeps its silence and is not counted ([#2739](https://github.com/ThinkFlowLab/FlyBot/issues/2739)). +- The `/api/health` route, the one answer given without a session, now uses the connection's fence verdict as it comes instead of comparing it to `403`: a refusal with any other status (a newer code, or the `400` a connection answers for a request it cannot read) is written with that code and no health body leaves, and an answer that is not a status reaches the web server's `400` arm instead of being read as "not refused" ([#2740](https://github.com/ThinkFlowLab/FlyBot/issues/2740)). +- A Workflow step whose Bot turn the host stopped before it answered is now reported as failed, with the stop notice as its reason, instead of finishing as done and handing that notice downstream as its output. A step's ordinary answer that merely mentions "model call failed" is no longer read as a failure. ([#2741](https://github.com/ThinkFlowLab/FlyBot/issues/2741)) + ## [0.2.27] - 2026-11-09 ### Fixed @@ -1332,7 +1353,8 @@ If you used the legacy `dsh-bot` package, remove it before you install `ds-bot`. - 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](ds-bot/README.md#data-and-backups)). - Group chats use many tokens: each member reads the group and answers in turn. -[Unreleased]: https://github.com/ThinkFlowLab/FlyBot/compare/v0.2.27...HEAD +[Unreleased]: https://github.com/ThinkFlowLab/FlyBot/compare/v0.2.28...HEAD +[0.2.28]: https://github.com/ThinkFlowLab/FlyBot/compare/v0.2.27...v0.2.28 [0.2.27]: https://github.com/ThinkFlowLab/FlyBot/compare/v0.2.26...v0.2.27 [0.2.26]: https://github.com/ThinkFlowLab/FlyBot/compare/v0.2.25...v0.2.26 [0.2.25]: https://github.com/ThinkFlowLab/FlyBot/compare/v0.2.24...v0.2.25 diff --git a/changelog.d/2704-workflow-takeover.fixed.md b/changelog.d/2704-workflow-takeover.fixed.md deleted file mode 100644 index 73dfd6620..000000000 --- a/changelog.d/2704-workflow-takeover.fixed.md +++ /dev/null @@ -1 +0,0 @@ -A window that takes over a workflow store after the other window closes no longer writes the copy it loaded while the other one was still running: the first read or write after it becomes the writer re-reads `workflows.json`, and it reaps the run the closed window left behind instead of leaving the lane blocked. A card that was only queued is kept and starts normally. See [#2704](https://github.com/ThinkFlowLab/FlyBot/issues/2704). diff --git a/changelog.d/2705-inflight-atomic.fixed.md b/changelog.d/2705-inflight-atomic.fixed.md deleted file mode 100644 index 8a144f2b4..000000000 --- a/changelog.d/2705-inflight-atomic.fixed.md +++ /dev/null @@ -1 +0,0 @@ -`inflight.json` — the record that lets an interrupted turn resume — is now staged in a temp file and renamed over the old one, so a crash in the middle of the write leaves the previous complete notes instead of an empty or half-written file. Every turn and queued message the crash cut off is still picked up at the next start, where an empty file used to drop them all silently. A file that still cannot be parsed (a hand-edit, a disk error) is now reported once with its path and the reason instead of passing for "nothing was in flight", and a temp name a crashed write left behind is swept by the next host that writes. See [#2705](https://github.com/ThinkFlowLab/FlyBot/issues/2705). diff --git a/changelog.d/2706-trace-lock.fixed.md b/changelog.d/2706-trace-lock.fixed.md deleted file mode 100644 index d2ca1d75f..000000000 --- a/changelog.d/2706-trace-lock.fixed.md +++ /dev/null @@ -1 +0,0 @@ -A timeline window that opens while another window is writing the trace no longer loses every span for the rest of its life: it asks for the writer lock again when it has something to write, begins persisting as soon as the other window closes, and keeps the segments already on disk. Until the lock is free a flush that wrote nothing answers `false` and names the reason once, instead of reporting success for a dropped span. See [#2706](https://github.com/ThinkFlowLab/FlyBot/issues/2706). diff --git a/changelog.d/2710-chat-anchor.fixed.md b/changelog.d/2710-chat-anchor.fixed.md deleted file mode 100644 index f11307361..000000000 --- a/changelog.d/2710-chat-anchor.fixed.md +++ /dev/null @@ -1 +0,0 @@ -Every message `read_own_chat` shows now names its part in the anchor, `part N #`, from the first message of a chat onward, so an anchor a Bot saved before the conversation moved to a second part still opens that same message afterwards. A bare `#` in an anchor written earlier keeps working and reads the part that is current now, but the answer says the part was guessed instead of reporting an exact hit — previously it silently returned a different message from another part and said `exact: true`. Anchors written the old way remain valid; nothing needs re-reading. See [#2710](https://github.com/ThinkFlowLab/FlyBot/issues/2710). diff --git a/changelog.d/2711-chat-index-writer.fixed.md b/changelog.d/2711-chat-index-writer.fixed.md deleted file mode 100644 index cdb6b0c84..000000000 --- a/changelog.d/2711-chat-index-writer.fixed.md +++ /dev/null @@ -1 +0,0 @@ -`read_own_chat` no longer shows every message twice when a second window or a plugin activation indexes the same chat: the part's mark is read inside the transaction that writes the rows (so the second writer sees the first one's commit), and the index's `lines` table now refuses a second row for the same event. An index an older build already duplicated is rebuilt once from the Session logs on the next start, and the log says how many duplicated lines it found. A window another dsh keeps read-only now reads the existing `chat-index.sqlite` without creating, repairing or writing it, instead of writing the index behind the other window's back. See [#2711](https://github.com/ThinkFlowLab/FlyBot/issues/2711). diff --git a/changelog.d/2714-room-members-guard.fixed.md b/changelog.d/2714-room-members-guard.fixed.md deleted file mode 100644 index f11cc8126..000000000 --- a/changelog.d/2714-room-members-guard.fixed.md +++ /dev/null @@ -1 +0,0 @@ -A saved team whose group-chat record carries no `members` list (a hand edit, a restore from another machine, or a version-1 home from before the field) no longer makes FlyBot fail to start: the record's list is repaired to empty when the team is loaded, the room itself is kept, and the repaired team is saved, instead of every later load rejecting with no warning and no way to repair it from the UI. See [#2714](https://github.com/ThinkFlowLab/FlyBot/issues/2714). diff --git a/changelog.d/2715-exchanges-normalise.fixed.md b/changelog.d/2715-exchanges-normalise.fixed.md deleted file mode 100644 index faf9e27c8..000000000 --- a/changelog.d/2715-exchanges-normalise.fixed.md +++ /dev/null @@ -1 +0,0 @@ -A saved team file whose fields that hold tables arrive as `null` — or as something else that is not a list or a record — no longer loads as a writable team that then fails every write. `exchanges` is repaired to an empty list and `exchangeTurns`, `questions`, `rooms`, `bots`, `prefs` and `agents` to an empty record when the team is loaded, and the repaired team is saved, so creating a Bot, renaming a room, posting in a group, a memory write and a delivery all work again instead of each throwing a stack trace that never named the team file as the problem. See [#2715](https://github.com/ThinkFlowLab/FlyBot/issues/2715). diff --git a/changelog.d/2716-team-file-incomplete.fixed.md b/changelog.d/2716-team-file-incomplete.fixed.md deleted file mode 100644 index 3d3bb542e..000000000 --- a/changelog.d/2716-team-file-incomplete.fixed.md +++ /dev/null @@ -1 +0,0 @@ -A team file that is empty or half written is now described as what it is instead of telling the person to update FlyBot. `state.json` holding `{}`, `null`, `[]` or an empty string parses but carries no `version`, and the read-only sentence used to read `saved by a newer FlyBot; update FlyBot` — a repair that cannot help an install already on the current version. It now reads `the team file has no version — it is incomplete (an empty or half-written file)`, so the first thing a broken install shows points at the file to move aside. The newer-version sentence is kept for a file whose `version` is above this build's, the file is still never changed, and a zero-byte or truncated file keeps its existing backup-and-load-fresh path. See [#2716](https://github.com/ThinkFlowLab/FlyBot/issues/2716). diff --git a/changelog.d/2717-social-readjson.fixed.md b/changelog.d/2717-social-readjson.fixed.md deleted file mode 100644 index 61ef62b4b..000000000 --- a/changelog.d/2717-social-readjson.fixed.md +++ /dev/null @@ -1 +0,0 @@ -A Bot Social settings or identity file that exists but cannot be read or parsed is no longer treated as "nothing was ever saved". A torn `flybot-social-bots..json` used to make a server switch silently restart the Bot as a stranger — no member identity, no grants — and the next switch rewrote the only copy; the switch now refuses before anything moves, names the file and leaves its bytes alone. A torn `flybot-social-server.json` refuses at start instead of quietly connecting to the default server as if no server had been chosen, and a damaged credentials file of another server is warned about and left out of the known-server list. A file that is absent still means a first run. See [#2717](https://github.com/ThinkFlowLab/FlyBot/issues/2717). diff --git a/changelog.d/2724-decisions-tmp.fixed.md b/changelog.d/2724-decisions-tmp.fixed.md deleted file mode 100644 index b316864d0..000000000 --- a/changelog.d/2724-decisions-tmp.fixed.md +++ /dev/null @@ -1 +0,0 @@ -The decision log no longer shares one temp file between writers: the trim stages the retained log at `decisions.jsonl...tmp` and renames it over the log, so two hosts of one team folder cannot take each other's temp away and report a write that never landed — the `a decision was not written: … ENOENT …` warning, and the trim that was lost with it, are gone ([#2724](https://github.com/ThinkFlowLab/FlyBot/issues/2724)). A read-only window, the second host of one team folder, now writes nothing at all: `record_decision` there appends no line, trims nothing, fires no `decision` event, and answers `Not recorded: this window is read-only, …` instead of claiming a record that is not on disk. diff --git a/changelog.d/2734-workflow-pause-resume.fixed.md b/changelog.d/2734-workflow-pause-resume.fixed.md deleted file mode 100644 index 5db275f35..000000000 --- a/changelog.d/2734-workflow-pause-resume.fixed.md +++ /dev/null @@ -1 +0,0 @@ -Pausing or resuming a workflow run while one of its steps was still dispatching no longer loses the control: both now land on the run record the lane holds, so a run paused during a dispatch stays paused when that dispatch returns and a run resumed during one stays resumed — the stage no longer reads `已暂停` while the run is `running` with its step deadline re-armed and no Resume pressed ([#2734](https://github.com/ThinkFlowLab/FlyBot/issues/2734)). diff --git a/changelog.d/2735-workflow-parked-card.fixed.md b/changelog.d/2735-workflow-parked-card.fixed.md deleted file mode 100644 index d8e1fcc7e..000000000 --- a/changelog.d/2735-workflow-parked-card.fixed.md +++ /dev/null @@ -1 +0,0 @@ -A card parked behind the concurrency limit is no longer dropped when the host refuses to start a Bot for it once: the parked row goes back to the head of the queue and starts on the next drain, instead of leaving the queue while the stored claim still answered `already: true` — which left the card in nobody's hands, with no run naming it, until a human used `rematch` ([#2735](https://github.com/ThinkFlowLab/FlyBot/issues/2735)). diff --git a/changelog.d/2736-workflow-store-entry.fixed.md b/changelog.d/2736-workflow-store-entry.fixed.md deleted file mode 100644 index e83672d88..000000000 --- a/changelog.d/2736-workflow-store-entry.fixed.md +++ /dev/null @@ -1 +0,0 @@ -A workflow store that parses but holds an entry that is not a workflow now answers `store-unreadable` instead of throwing out of every read of `workflow/state`: the panel gets the repair hint, and the damaged bytes are kept as `workflows.json.broken-` before the next write, instead of a raw `TypeError` escaping each render ([#2736](https://github.com/ThinkFlowLab/FlyBot/issues/2736)). diff --git a/changelog.d/2739-memory-review-bad-topic.fixed.md b/changelog.d/2739-memory-review-bad-topic.fixed.md deleted file mode 100644 index 59aa95350..000000000 --- a/changelog.d/2739-memory-review-bad-topic.fixed.md +++ /dev/null @@ -1 +0,0 @@ -A memory review answer that named a topic memory cannot use no longer throws the whole answer away: only that change is left out, the valid `add`, `edit` or `remove` beside it lands and the cursor moves past the lines that were read, and one warning per answer — not one per process — says how many this answer lost and why the first one was, so a later answer in the same Session still warns again while a secret keeps its silence and is not counted ([#2739](https://github.com/ThinkFlowLab/FlyBot/issues/2739)). diff --git a/changelog.d/2740-fence-answer.fixed.md b/changelog.d/2740-fence-answer.fixed.md deleted file mode 100644 index 7b53c1323..000000000 --- a/changelog.d/2740-fence-answer.fixed.md +++ /dev/null @@ -1 +0,0 @@ -The `/api/health` route, the one answer given without a session, now uses the connection's fence verdict as it comes instead of comparing it to `403`: a refusal with any other status (a newer code, or the `400` a connection answers for a request it cannot read) is written with that code and no health body leaves, and an answer that is not a status reaches the web server's `400` arm instead of being read as "not refused" ([#2740](https://github.com/ThinkFlowLab/FlyBot/issues/2740)). diff --git a/changelog.d/2741-stepfail-abort.fixed.md b/changelog.d/2741-stepfail-abort.fixed.md deleted file mode 100644 index bac7509ef..000000000 --- a/changelog.d/2741-stepfail-abort.fixed.md +++ /dev/null @@ -1 +0,0 @@ -A Workflow step whose Bot turn the host stopped before it answered is now reported as failed, with the stop notice as its reason, instead of finishing as done and handing that notice downstream as its output. A step's ordinary answer that merely mentions "model call failed" is no longer read as a failure. ([#2741](https://github.com/ThinkFlowLab/FlyBot/issues/2741)) diff --git a/core/package-lock.json b/core/package-lock.json index fb966686d..6b84d51d2 100644 --- a/core/package-lock.json +++ b/core/package-lock.json @@ -1,12 +1,12 @@ { "name": "ds-bot", - "version": "0.2.27", + "version": "0.2.28", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "ds-bot", - "version": "0.2.27", + "version": "0.2.28", "license": "Apache-2.0", "dependencies": { "@larksuiteoapi/node-sdk": "1.76.0" diff --git a/core/package.json b/core/package.json index 1d730d9b7..171c89da7 100644 --- a/core/package.json +++ b/core/package.json @@ -1,6 +1,6 @@ { "name": "ds-bot", - "version": "0.2.27", + "version": "0.2.28", "description": "DS Bot: a team of long-lived Bots for DeepSeek Harness, with Main Bots, Bot-to-Bot messages and group chats", "keywords": [ "dsh-plugin", diff --git a/patches/ds-bot/0007-version-and-tooling.patch b/patches/ds-bot/0007-version-and-tooling.patch index 13f447b85..ad2eac475 100644 --- a/patches/ds-bot/0007-version-and-tooling.patch +++ b/patches/ds-bot/0007-version-and-tooling.patch @@ -95,14 +95,14 @@ diff --git a/package-lock.json b/package-lock.json { "name": "ds-bot", - "version": "0.1.5", -+ "version": "0.2.27", ++ "version": "0.2.28", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "ds-bot", - "version": "0.1.5", -+ "version": "0.2.27", ++ "version": "0.2.28", "license": "Apache-2.0", + "dependencies": { + "@larksuiteoapi/node-sdk": "1.76.0" @@ -733,7 +733,7 @@ diff --git a/package.json b/package.json { "name": "ds-bot", - "version": "0.1.5", -+ "version": "0.2.27", ++ "version": "0.2.28", "description": "DS Bot: a team of long-lived Bots for DeepSeek Harness, with Main Bots, Bot-to-Bot messages and group chats", "keywords": [ "dsh-plugin",