Releases: oschina/mothx
Release list
v1.3.101-pre-3
Changelog (Current Version)
This file contains the changes for the current version only. The full history of all versions lives in docs/en/changelog.md.
v1.3.101
✨ New Features
-
Serve: Placeholder API Token Warnings
mothx serve init-configships a well-known template token, which is equivalent to publishing the API key once auth is enabled — and nothing said so. The token is now a single named constant (serve.PlaceholderAuthToken) with explicit detection (IsPlaceholderAuthToken/UsesPlaceholderAuthToken), and a replacement warning is printed wherever the template is created (mothx serve init-configand the--init-serveCLI path) and again onmothx servestartup whileapi.auth.enabledis true and the token is still in place.- The startup check only fires for an enabled auth block, so the default template (auth disabled, loopback only) stays quiet and a replaced token is never flagged.
serve init-confignow writes through the command's own stderr, so the warning travels with the command output.
-
Members Wait for Their Lead on Interactive Surfaces
- A session that can spawn members but is not bound to an expert team now keeps its run open for still-running members on interactive surfaces (TUI, Web UI, ACP), so a member's completion or question is handled within the same run instead of waiting for the next lead run. Headless and asynchronous sources (CLI, cron, WeChat/Feishu) still end the turn normally and deliver member notifications at their next iteration or next run; a bound expert team always waits.
-
Bound Teams Keep the Full Sub-Agent Tool Set
- A bound expert team always exposes the complete canonical sub-agent tool set (
subagent_spawn,subagent_status,subagent_send,subagent_wait,subagent_answer,subagent_destroy). Per-tool toggles that disable individual sub-agent tools apply only to non-team multi-agent sessions; the team capability is authoritative and never drops tools.
- A bound expert team always exposes the complete canonical sub-agent tool set (
-
New Gitee/Moark Model:
deepseek-v4.1-flash- Added
deepseek-v4.1-flashto thegiteeandmoarkproviders with a 1M context window and text+image input; no default max_tokens is sent.
- Added
-
New Agnes AI Providers (International + China)
- Added
agnes(https://apihub.agnes-ai.com/v1,${AGNES_API_KEY}) andagnes-cn(https://api.agnes-ai.cn/v1,${AGNES_CN_API_KEY}) through a new OpenAI-compatibleagnesvendor adapter. Both exposeagnes-2.5-flash(200K context),agnes-2.5-pro(256K context), andagnes-3.0-flash(512K context, 65535 max output tokens). - Every model is declared reasoning-capable and multimodal (
text,image).agnes-2.5-flashandagnes-2.5-prosend no defaultmax_tokensof their own, so the provider default applies;agnes-3.0-flashcaps output at 65535 tokens.
- Added
-
Cross-Process Notice When a Database Is Rebuilt
- A process that backs up and rebuilds a database after a schema migration failure now announces it over the existing advisory UDP bus (
database_rebuilt). Every other mothx process sharing that session directory retires its cached connection — otherwise it would keep reading and writing the replaced file through its open handle — logs the recovery, and shows the notice to the user in the TUI. The notice carries only the replaced file path; the reason and the backup stay with the recovering process. - The bus stays host-only: a directed broadcast on the loopback /8 network that only accepts loopback sources, so it reaches this host's other mothx processes and never leaves the machine.
- A process that backs up and rebuilds a database after a schema migration failure now announces it over the existing advisory UDP bus (
🐛 Bug Fixes
-
Session Runtime Leases Now Survive Transient Database Stalls
- Long runs could be interrupted by
session runtime lease was losteven when no other process held the session. The per-directory heartbeat scheduler stopped whenever a tick observed an empty directory, even if a lease admitted moments earlier was still live: it left a registered-but-stopped entry in the scheduler registry that no later acquire could replace, so the lease was never renewed again and a later execution-path write found it expired. This was introduced when the per-lease heartbeats were coalesced into one scheduler per session directory. - The scheduler now exits only after it has actually unregistered itself for an idle directory, and a renewal that merely times out (a busy or unreachable session database) is retried on the next heartbeat tick instead of being reported as ownership loss. Ownership is decided by the fenced
owner/epoch/tokenCAS, not by wall-clock expiry: taking over bumps the epoch, so a row that expired while still carrying our identity remains ours. Only a genuine fenced takeover or a released lease marks it lost. The heartbeat retry budget is derived from the sharedbusy_timeout(db.BusyTimeout) so a single tick can absorb a full contended begin, and lease loss is now logged with its reason.
- Long runs could be interrupted by
-
Web UI: Knowledge Base Scans Keep Their Status Across Reloads
- Starting a scan from the Web UI knowledge page blocked the HTTP request until the entire index finished and recorded nothing about the in-flight job, so the base read as merely "not indexed" and reloading the page lost the scanning state entirely.
- The serve handler now admits the scan as a background Runtime index job through the process-wide cached service and returns a running-job projection immediately, and list/get expose the live
indexingprogress (phase, files done/total) using the same projection ACP already publishes. The knowledge view renders the current phase and polls while a scan is running, so a reload resumes the status instead of dropping it.
-
Content-Inspected Images No Longer Kill the Session
- A provider content-policy refusal — for example DashScope/Qwen's
InternalError.Algo.DataInspectionFailed: Input image data may contain inappropriate content— arrives as an HTTP 400, but every 4xx was treated as retryable. The same rejected image was re-sent through the provider's backoff retries plus the agent's stream-failure retries (minutes of "Retrying…"), and because a refusal is permanent the run finally failed; worse, the offending image stayed in the persisted history and was replayed on every later turn, so nothing could continue. Only starting a brand-new session recovered — not even/clear, which reloads the same history. provider.IsContentRejectionErrornow classifies this narrow family (data inspection, content policy/moderation/filter, "inappropriate content") andIsRetryablereturns false for it, so the failure surfaces immediately instead of burning the retry budget. Agent Core then recovers in place: it strips the refused images — first from the current turn, then, if the refusal persists, from the whole conversation — replacing each with a model-visible note that explains the provider's content filter blocked the image and that the pixels are unavailable, and records an append-onlycontent_overridesession entry so replay (same process or after reload) never re-sends it. The run retries without the image and the session keeps working; a turn that already streamed output is healed but not re-run, so no output is duplicated.
- A provider content-policy refusal — for example DashScope/Qwen's
-
Serve: Config Saves No Longer Fail with "Access is denied" on Windows
- On Windows (including portable setups running from exFAT drives), enabling the WeChat/Feishu channel from the Web UI or saving any
serve.jsonchange failed withsync config directory: Access is denied. The atomic config writer fsynced the parent directory after the rename — a POSIX durability idiom — butFlushFileBufferson a read-only directory handle always returnsERROR_ACCESS_DENIEDon Windows, on every filesystem including exFAT. Because the failure happened after the file had already been swapped into place, the API reported an error while the new config was never applied to the runtime. - The post-rename directory flush is now skipped on Windows (matching etcd/bolt practice); the config file itself is still fsynced before the rename, so durability is preserved and Unix behavior is unchanged.
- On Windows (including portable setups running from exFAT drives), enabling the WeChat/Feishu channel from the Web UI or saving any
-
MCP: Image Tool Results Reach the Model Instead of a Placeholder
- An MCP tool that returned image content reached the model as the literal string
[image content: image/png]. The base64 payload was already present in the MCP response, but the client decoded every content block into text only, and the tool returned a text-only result, so a screenshot-style MCP server could report coordinates while the model never saw the picture.resources/readbinary resources were worse: theblobfield had no matching struct field, so the payload was dropped during decode and not even the placeholder appeared. tools/callandresources/readnow project image blocks intotools.ToolResult.Contentsas real provider image content, reusing the same provider-aware preprocessing as thereadandbrowserscreenshot tools, and decodeblob/uriresource fields. Results without images keep the historical text-only shape, so existing text MCP tools are unchanged. Malformed, oversized, or excess images (capped at 4 per result, matching the ACP projection limit) degrade to a text note instead of failing the call. The image capability gate in Agent Core still decides whether a non-vision model receives images at all.
- An MCP tool that returned image content reached the model as the literal string
-
SQLite: Transient Busy Transaction Begins Are Retried
- Several processes opening one session directory race onto the single writer lock. The DSN begins non-read-only transactions with
BEGIN IMMEDIATE, so a begin can outlast the connection'sbusy_timeoutwhile other processes keep committing undersynchronous(FULL), and a healthy database failed withdatabase is locked (5). - The retry policy now lives in
internal/dbnext to the DSN ownership:BeginTx(Bun),BeginSQLTx(raw*sql.DB), andRunInTxretry onlySQLITE_BUSY/SQLITE_LOCKEDinside a bounded budget (90 s) with exponential backoff (200 ms ...
- Several processes opening one session directory race onto the single writer lock. The DSN begins non-read-only transactions with
v1.3.101-pre-2
Changelog (Current Version)
This file contains the changes for the current version only. The full history of all versions lives in docs/en/changelog.md.
v1.3.101
✨ New Features
-
Serve: Placeholder API Token Warnings
mothx serve init-configships a well-known template token, which is equivalent to publishing the API key once auth is enabled — and nothing said so. The token is now a single named constant (serve.PlaceholderAuthToken) with explicit detection (IsPlaceholderAuthToken/UsesPlaceholderAuthToken), and a replacement warning is printed wherever the template is created (mothx serve init-configand the--init-serveCLI path) and again onmothx servestartup whileapi.auth.enabledis true and the token is still in place.- The startup check only fires for an enabled auth block, so the default template (auth disabled, loopback only) stays quiet and a replaced token is never flagged.
serve init-confignow writes through the command's own stderr, so the warning travels with the command output.
-
Members Wait for Their Lead on Interactive Surfaces
- A session that can spawn members but is not bound to an expert team now keeps its run open for still-running members on interactive surfaces (TUI, Web UI, ACP), so a member's completion or question is handled within the same run instead of waiting for the next lead run. Headless and asynchronous sources (CLI, cron, WeChat/Feishu) still end the turn normally and deliver member notifications at their next iteration or next run; a bound expert team always waits.
-
Bound Teams Keep the Full Sub-Agent Tool Set
- A bound expert team always exposes the complete canonical sub-agent tool set (
subagent_spawn,subagent_status,subagent_send,subagent_wait,subagent_answer,subagent_destroy). Per-tool toggles that disable individual sub-agent tools apply only to non-team multi-agent sessions; the team capability is authoritative and never drops tools.
- A bound expert team always exposes the complete canonical sub-agent tool set (
-
New Gitee/Moark Model:
deepseek-v4.1-flash- Added
deepseek-v4.1-flashto thegiteeandmoarkproviders with a 1M context window and text+image input; no default max_tokens is sent.
- Added
🐛 Bug Fixes
-
Content-Inspected Images No Longer Kill the Session
- A provider content-policy refusal — for example DashScope/Qwen's
InternalError.Algo.DataInspectionFailed: Input image data may contain inappropriate content— arrives as an HTTP 400, but every 4xx was treated as retryable. The same rejected image was re-sent through the provider's backoff retries plus the agent's stream-failure retries (minutes of "Retrying…"), and because a refusal is permanent the run finally failed; worse, the offending image stayed in the persisted history and was replayed on every later turn, so nothing could continue. Only starting a brand-new session recovered — not even/clear, which reloads the same history. provider.IsContentRejectionErrornow classifies this narrow family (data inspection, content policy/moderation/filter, "inappropriate content") andIsRetryablereturns false for it, so the failure surfaces immediately instead of burning the retry budget. Agent Core then recovers in place: it strips the refused images — first from the current turn, then, if the refusal persists, from the whole conversation — replacing each with a model-visible note that explains the provider's content filter blocked the image and that the pixels are unavailable, and records an append-onlycontent_overridesession entry so replay (same process or after reload) never re-sends it. The run retries without the image and the session keeps working; a turn that already streamed output is healed but not re-run, so no output is duplicated.
- A provider content-policy refusal — for example DashScope/Qwen's
-
Serve: Config Saves No Longer Fail with "Access is denied" on Windows
- On Windows (including portable setups running from exFAT drives), enabling the WeChat/Feishu channel from the Web UI or saving any
serve.jsonchange failed withsync config directory: Access is denied. The atomic config writer fsynced the parent directory after the rename — a POSIX durability idiom — butFlushFileBufferson a read-only directory handle always returnsERROR_ACCESS_DENIEDon Windows, on every filesystem including exFAT. Because the failure happened after the file had already been swapped into place, the API reported an error while the new config was never applied to the runtime. - The post-rename directory flush is now skipped on Windows (matching etcd/bolt practice); the config file itself is still fsynced before the rename, so durability is preserved and Unix behavior is unchanged.
- On Windows (including portable setups running from exFAT drives), enabling the WeChat/Feishu channel from the Web UI or saving any
-
MCP: Image Tool Results Reach the Model Instead of a Placeholder
- An MCP tool that returned image content reached the model as the literal string
[image content: image/png]. The base64 payload was already present in the MCP response, but the client decoded every content block into text only, and the tool returned a text-only result, so a screenshot-style MCP server could report coordinates while the model never saw the picture.resources/readbinary resources were worse: theblobfield had no matching struct field, so the payload was dropped during decode and not even the placeholder appeared. tools/callandresources/readnow project image blocks intotools.ToolResult.Contentsas real provider image content, reusing the same provider-aware preprocessing as thereadandbrowserscreenshot tools, and decodeblob/uriresource fields. Results without images keep the historical text-only shape, so existing text MCP tools are unchanged. Malformed, oversized, or excess images (capped at 4 per result, matching the ACP projection limit) degrade to a text note instead of failing the call. The image capability gate in Agent Core still decides whether a non-vision model receives images at all.
- An MCP tool that returned image content reached the model as the literal string
-
SQLite: Transient Busy Transaction Begins Are Retried
- Several processes opening one session directory race onto the single writer lock. The DSN begins non-read-only transactions with
BEGIN IMMEDIATE, so a begin can outlast the connection'sbusy_timeoutwhile other processes keep committing undersynchronous(FULL), and a healthy database failed withdatabase is locked (5). - The retry policy now lives in
internal/dbnext to the DSN ownership:BeginTx(Bun),BeginSQLTx(raw*sql.DB), andRunInTxretry onlySQLITE_BUSY/SQLITE_LOCKEDinside a bounded budget (90 s) with exponential backoff (200 ms up to a 2 s cap); non-transient errors are returned unchanged and a caller's context deadline still wins. - DAO
Begin/BeginTx/RunInTx(including the bindings helpers),internal/db.Write, and the session schema initialization/migration boundary all use it, so concurrent startup and ordinary writes no longer turn a transient writer conflict into a hard failure.
- Several processes opening one session directory race onto the single writer lock. The DSN begins non-read-only transactions with
-
Workflow: Runaway DSL Scripts Are Bounded by a Wall-Clock Budget
- A workflow source such as
while (true) {}could pin the process forever whenever its caller passed a context without a deadline. Source evaluation — which only builds the node graph, since worker agents run natively afterwards — now runs under two bounds: the caller's context and a wall-clock budget, whichever fires first interrupts the VM. - The budgets are 30 s for a workflow run and 5 s for the interactive
workflow_lintauthoring check, which must fail fast; a timeout surfaces as the sentinelErrJSEvaluationTimeout(the lint result carries a stable, readable error), while a caller cancellation keeps returning its context error.Runner.EvalTimeoutlets callers tighten the budget, and the zero value keeps the documented defaults.
- A workflow source such as
-
Cancelled or Expired Decisions No Longer Block Forking
- A session whose only decision had actually been cancelled or timed out was still treated as having a pending decision, so forking it was rejected as
source session is active. Every decision-ledger reader now shares one vocabulary, so cancelled and timed-out decisions (and the legacy channel request name) clear correctly and the fork proceeds. The durable decision event name and its{"decision": …}envelope also gained a single owner each, so cross-entry decision recovery reads the same records regardless of which surface wrote them.
- A session whose only decision had actually been cancelled or timed out was still treated as having a pending decision, so forking it was rejected as
-
Mid-Stream Network Failures Retry Automatically Instead of Ending the Reply
- A provider stream that died with a transient transport error (
connection reset by peer, unexpected EOF, gateway 5xx, ...) after text or thinking had already been streamed failed the whole run withstream read error: ...: provider-level retries only cover streams that break before any visible output, and the agent-level retry only covered idle-stream timeouts. - The agent loop now performs a bounded continuation retry (up to 2 attempts) for such transient errors. Already-streamed partial output is persisted into history and a continuation instruction quoting the exact suffix is injected, so the model resumes from the interruption point instead of duplicating what the user already saw; with no visible output yet, the turn simply re-runs. Turns with an already-emitted tool call, context overflow (dedicated compaction recovery), and idle-stream timeouts (dedicated timeout retry) keep their existing behavior, and Responses remote-state turns keep their existing failover path.
- A provider stream that died with a transient transport error (
🔧 Improvements
- SQLite: Three-Phase Write-Pressure Reduction for the Session Database
- The connection durability policy moves from
synchronous(FULL)to the WAL-recommendedsynchronous(NORMAL): a commit no longer fsyncs while holding the single writer lock (the fsync moves to checkpoint time), so writer-lock occupancy across processes sharing one session directory shrinks from fsync scale to page-cache scale, largely eliminating the recorded "another process keeps committing until begin exceeds busy_timeout and reports database is locked" scenario. ...
- The connection durability policy moves from
v1.3.101-pre-1
Changelog (Current Version)
This file contains the changes for the current version only. The full history of all versions lives in docs/en/changelog.md.
v1.3.101
✨ New Features
- Serve: Placeholder API Token Warnings
mothx serve init-configships a well-known template token, which is equivalent to publishing the API key once auth is enabled — and nothing said so. The token is now a single named constant (serve.PlaceholderAuthToken) with explicit detection (IsPlaceholderAuthToken/UsesPlaceholderAuthToken), and a replacement warning is printed wherever the template is created (mothx serve init-configand the--init-serveCLI path) and again onmothx servestartup whileapi.auth.enabledis true and the token is still in place.- The startup check only fires for an enabled auth block, so the default template (auth disabled, loopback only) stays quiet and a replaced token is never flagged.
serve init-confignow writes through the command's own stderr, so the warning travels with the command output.
🐛 Bug Fixes
-
MCP: Image Tool Results Reach the Model Instead of a Placeholder
- An MCP tool that returned image content reached the model as the literal string
[image content: image/png]. The base64 payload was already present in the MCP response, but the client decoded every content block into text only, and the tool returned a text-only result, so a screenshot-style MCP server could report coordinates while the model never saw the picture.resources/readbinary resources were worse: theblobfield had no matching struct field, so the payload was dropped during decode and not even the placeholder appeared. tools/callandresources/readnow project image blocks intotools.ToolResult.Contentsas real provider image content, reusing the same provider-aware preprocessing as thereadandbrowserscreenshot tools, and decodeblob/uriresource fields. Results without images keep the historical text-only shape, so existing text MCP tools are unchanged. Malformed, oversized, or excess images (capped at 4 per result, matching the ACP projection limit) degrade to a text note instead of failing the call. The image capability gate in Agent Core still decides whether a non-vision model receives images at all.
- An MCP tool that returned image content reached the model as the literal string
-
SQLite: Transient Busy Transaction Begins Are Retried
- Several processes opening one session directory race onto the single writer lock. The DSN begins non-read-only transactions with
BEGIN IMMEDIATE, so a begin can outlast the connection'sbusy_timeoutwhile other processes keep committing undersynchronous(FULL), and a healthy database failed withdatabase is locked (5). - The retry policy now lives in
internal/dbnext to the DSN ownership:BeginTx(Bun),BeginSQLTx(raw*sql.DB), andRunInTxretry onlySQLITE_BUSY/SQLITE_LOCKEDinside a bounded budget (90 s) with exponential backoff (200 ms up to a 2 s cap); non-transient errors are returned unchanged and a caller's context deadline still wins. - DAO
Begin/BeginTx/RunInTx(including the bindings helpers),internal/db.Write, and the session schema initialization/migration boundary all use it, so concurrent startup and ordinary writes no longer turn a transient writer conflict into a hard failure.
- Several processes opening one session directory race onto the single writer lock. The DSN begins non-read-only transactions with
-
Workflow: Runaway DSL Scripts Are Bounded by a Wall-Clock Budget
- A workflow source such as
while (true) {}could pin the process forever whenever its caller passed a context without a deadline. Source evaluation — which only builds the node graph, since worker agents run natively afterwards — now runs under two bounds: the caller's context and a wall-clock budget, whichever fires first interrupts the VM. - The budgets are 30 s for a workflow run and 5 s for the interactive
workflow_lintauthoring check, which must fail fast; a timeout surfaces as the sentinelErrJSEvaluationTimeout(the lint result carries a stable, readable error), while a caller cancellation keeps returning its context error.Runner.EvalTimeoutlets callers tighten the budget, and the zero value keeps the documented defaults.
- A workflow source such as
✅ Tests
- Database:
internal/dbpins the begin-retry policy — only SQLITE_BUSY/SQLITE_LOCKED are retried, other driver errors and an expiring context are surfaced unchanged, driver codes are classified through the error's ownCode()method, andRunInTxkeeps commit/rollback semantics. - Workflow: a runaway script is interrupted by a 50 ms budget (previously that test hung), a successful evaluation behaves exactly as before, and the lint path reports an invalid result with the timeout message.
- Serve: the generated template keeps the same constant the detection uses, a whitespace-padded placeholder is still recognized while a real or empty token is not, and the
serve init-configoutput must contain the warning. - Agent loop: ten real
bash echocalls run through one parallel batch (the rendezvous only closes once all ten workers are live), and ordered-start coverage pins the launch semantics — starts in the model's declared order, an in-flight call unaffected by an earlier approval wait, queued calls released when an earlier call fails, and the same ordered handle for background tool calls. - Runtime: the cross-process takeover test now retries the expire-then-takeover pair inside a bounded budget and gives helper startup a load-tolerant window, so a loaded machine fails setup with a clear diagnosis instead of failing the invariant under test.
v1.3.101-pre
Changelog (Current Version)
This file contains the changes for the current version only. The full history of all versions lives in docs/en/changelog.md.
v1.3.101
✨ New Features
- Serve: Placeholder API Token Warnings
mothx serve init-configships a well-known template token, which is equivalent to publishing the API key once auth is enabled — and nothing said so. The token is now a single named constant (serve.PlaceholderAuthToken) with explicit detection (IsPlaceholderAuthToken/UsesPlaceholderAuthToken), and a replacement warning is printed wherever the template is created (mothx serve init-configand the--init-serveCLI path) and again onmothx servestartup whileapi.auth.enabledis true and the token is still in place.- The startup check only fires for an enabled auth block, so the default template (auth disabled, loopback only) stays quiet and a replaced token is never flagged.
serve init-confignow writes through the command's own stderr, so the warning travels with the command output.
🐛 Bug Fixes
-
SQLite: Transient Busy Transaction Begins Are Retried
- Several processes opening one session directory race onto the single writer lock. The DSN begins non-read-only transactions with
BEGIN IMMEDIATE, so a begin can outlast the connection'sbusy_timeoutwhile other processes keep committing undersynchronous(FULL), and a healthy database failed withdatabase is locked (5). - The retry policy now lives in
internal/dbnext to the DSN ownership:BeginTx(Bun),BeginSQLTx(raw*sql.DB), andRunInTxretry onlySQLITE_BUSY/SQLITE_LOCKEDinside a bounded budget (90 s) with exponential backoff (200 ms up to a 2 s cap); non-transient errors are returned unchanged and a caller's context deadline still wins. - DAO
Begin/BeginTx/RunInTx(including the bindings helpers),internal/db.Write, and the session schema initialization/migration boundary all use it, so concurrent startup and ordinary writes no longer turn a transient writer conflict into a hard failure.
- Several processes opening one session directory race onto the single writer lock. The DSN begins non-read-only transactions with
-
Workflow: Runaway DSL Scripts Are Bounded by a Wall-Clock Budget
- A workflow source such as
while (true) {}could pin the process forever whenever its caller passed a context without a deadline. Source evaluation — which only builds the node graph, since worker agents run natively afterwards — now runs under two bounds: the caller's context and a wall-clock budget, whichever fires first interrupts the VM. - The budgets are 30 s for a workflow run and 5 s for the interactive
workflow_lintauthoring check, which must fail fast; a timeout surfaces as the sentinelErrJSEvaluationTimeout(the lint result carries a stable, readable error), while a caller cancellation keeps returning its context error.Runner.EvalTimeoutlets callers tighten the budget, and the zero value keeps the documented defaults.
- A workflow source such as
✅ Tests
- Database:
internal/dbpins the begin-retry policy — only SQLITE_BUSY/SQLITE_LOCKED are retried, other driver errors and an expiring context are surfaced unchanged, driver codes are classified through the error's ownCode()method, andRunInTxkeeps commit/rollback semantics. - Workflow: a runaway script is interrupted by a 50 ms budget (previously that test hung), a successful evaluation behaves exactly as before, and the lint path reports an invalid result with the timeout message.
- Serve: the generated template keeps the same constant the detection uses, a whitespace-padded placeholder is still recognized while a real or empty token is not, and the
serve init-configoutput must contain the warning. - Agent loop: ten real
bash echocalls run through one parallel batch (the rendezvous only closes once all ten workers are live), and ordered-start coverage pins the launch semantics — starts in the model's declared order, an in-flight call unaffected by an earlier approval wait, queued calls released when an earlier call fails, and the same ordered handle for background tool calls. - Runtime: the cross-process takeover test now retries the expire-then-takeover pair inside a bounded budget and gives helper startup a load-tolerant window, so a loaded machine fails setup with a clear diagnosis instead of failing the invariant under test.
v1.3.100
Changelog (Current Version)
This file contains the changes for the current version only. The full history of all versions lives in docs/en/changelog.md.
v1.2.100
✨ New Features
- WebUI: Slash Command Suggestions in the Chat Composer
- Typing
/in the chat input now shows a suggestion dropdown covering every supported slash command (/clear,/mode,/model,/defaultModel,/models,/sessions,/status,/compact,/delegate,/alloweditpath,/allowautoedit,/workflows,/skill,/skills,/rule,/esm,/help), with a dedicated subcommand filter for/esm(objective/edit/pause/resume/clear/guide). - Navigate with ↑/↓, complete with Tab or Enter (Enter sends the prompt when the input already matches the selection), dismiss with Esc, or click an entry; accepting a suggestion places the cursor at the end of the inserted command. The composer keeps proper combobox/listbox ARIA state (
aria-expanded,aria-activedescendant,aria-selected). - Suggestions are suppressed while a run is active, the API is disabled, or the input spans multiple lines.
- Typing
🐛 Bug Fixes
- TUI: Prompts Submitted During an Active Run Are Queued Instead of Replacing It
- A session allows exactly one foreground execution at a time. Previously, submitting input while a run was active replaced the in-memory run handle, orphaning the active run's terminal cleanup and its runtime lease. Such submissions are now queued in the TUI, and the next queued prompt starts only after the preceding run reaches its canonical terminal state and releases its lease — across every terminal branch (success, failure, incomplete, and cancellation).
- Queued prompts retain their Runtime-prepared attachments (
agentruntime.PreparedInput) and re-enter through the same input contract, so attachments survive the delay unchanged.
✅ Tests
- TUI: new coverage asserting that input during an active run queues without replacing the lease owner, and that the queued prompt starts only after the cancellation path finalizes the durable run and releases its lease.
v1.2.99
Changelog (Current Version)
This file contains the changes for the current version only. The full history of all versions lives in docs/en/changelog.md.
v1.2.99
✨ New Features
-
Unified Server-Resolved Model Catalog Across WebUI and TUI
- A new
GET /api/models/catalogendpoint resolves every selectable provider/model throughproviderfactory.ResolvedModels/SortProviderIDs— the same shared logic that builds the TUI's provider model lists — and returns canonical defaults plus the sorted provider list. The active provider stays selectable even when it comes from a built-in preset or a serve flag instead of the settings providers map. stores.jsmigratesmodels→modelCatalog; the Chat new-session picker now consumes the server catalog instead of merging raw settings JSON client-side, dropping the localbuildModelCatalog/settings-fallback derivation. Settings surfaces (default provider/model dropdowns, provider editor) consume the same store, and inherited built-in preset models get dedicated zh/en labels.- The TUI auth dialog's provider sort delegates to
providerfactory.ProviderSortPriority, so TUI dialogs and the WebUI catalog share one ordering logic.
- A new
-
Enable Supervisor Mode (ESM): Slash-Command Control, Shared Guidance, and Evidence Tracking
- WebUI ESM control moves to the same
/esmslash command as the TUI (/esm <objective>,/esm status|edit|pause|resume|clear|guide) instead of dedicated graphical controls — the 500-lineESMControlscomponent is removed, and chat input and the ESM REST API share one server-side objective path. - New guidance module:
/esm guide <text>queues user guidance stamped with the objective's current version; the Supervisor injects pending guidance into every non-recovery role prompt and consumes it exactly once after the role result is applied — one core-owned lifecycle shared by the TUI and WebUI adapters. - New evidence module: a shared
EvidenceTrackeraccumulates tool-call evidence per role run (unique tool-call IDs, per-tool counts/errors), so the "tool-backed evidence" checks inApplyWorkerResult/ApplyReviewResultcannot diverge between adapters. - ESM no longer enforces token/time budgets: the
budget_limitedstatus,SetBudget/budget prompts, and the TUI/esm budgetsubcommand are removed;TokensUsed/TimeUsedMSremain observability-only counters. The blocked-audit threshold is centralized asBlockedAuditLimit(the objective becomes blocked after 3 consecutive runs report the same blocker). - Unattended derived runs resolve their execution mode through
agentruntime.ResolveUnattendedMode: onlyosis inherited from the session mode and every other session mode falls back toyolo, so ESM role sub-agents never stop on interactive approval (hard high-risk-command protections remain mode-independent). - The Supervisor now runs continuations against the base Run ID (role runs use suffixed IDs derived from it), owns the terminal
FinishRuncall for both adapters, and clears stale streaks left by earlier continuations when a continuation ends.
- WebUI ESM control moves to the same
🐛 Bug Fixes
-
ESM: Failed Objectives Pause Instead of Silently Re-running
- A non-retryable role failure now pauses the objective and requires an explicit
/esm resumebefore it can run again — queued guidance or a future trigger can no longer silently re-run a failed task. Timeouts and retryable transport errors still take the recovery path bounded byRecoveryLimit. - Serve startup no longer replays historically persisted "active" ESM objectives: a role may have failed just before the process exited, so
Create/Edit/ResumeESMare now the only explicit execution entry points. esmCoordinatorgainsstop/stopAllwith done-channels and bounded waits; Serve shutdown cancels every ESM worker and waits for it to release session/runtime references (SessionRuntime.Shutdownremains the final resource boundary), and a closed coordinator refuses to start new workers.
- A non-retryable role failure now pauses the objective and requires an explicit
-
Native Directory Picker on Headless Servers
- The Unix native picker reports itself unavailable when
DISPLAY/WAYLAND_DISPLAYis absent instead of failing silently, letting the Web UI fall back to its built-in directory browser. Launch failures that print diagnostics on stderr now surface as errors instead of being mistaken for a dialog cancel.
- The Unix native picker reports itself unavailable when
🔧 Improvements
-
Directory Browser: Windows Drive Roots and Path-Aware Allowed Roots
/api/browseallowed-root resolution now takes the requested path, and Windows drive roots are listed through a virtual browse root (drive roots share no common parent for navigation).DirBrowsergains aninitialPathprop, one-shot open semantics, a server-providedselectableflag, and refresh support.
-
Tool Recovery Audit Trail
RequestToolExecutionRecoveryRecordsrecords explicit user confirmation and returns only matching interrupted calls; the records are retained as audit evidence while recovery starts as a fresh execution, and a new DAO listing (ListRequestedToolRecoveries) exposes requested recoveries to Serve. Terminal Runs are never reactivated to consume these records.
✅ Tests
- ESM: new/expanded coverage for the guidance lifecycle (version stamping, injection, one-time consumption), evidence tracking, pause-on-non-retryable-failure, base-Run-ID continuations, budget removal,
/esmslash-command parity, and coordinatorstop/stopAll(including closed-coordinator start refusal). - Serve: a process-level test asserts startup never replays historical ESM objectives; browse-root tests cover allowed-root resolution and Windows drive-root listing; native picker tests cover the headless-unavailable and stderr-diagnostic cases.
- Provider factory: catalog resolution and provider ordering tests; settings:
qwen3.8-max-0902preset assertions.
v1.2.98
Changelog (Current Version)
This file contains the changes for the current version only. The full history of all versions lives in docs/en/changelog.md.
v1.2.98
🔧 Improvements
- TUI: Dead Code Cleanup and Decision Lifecycle Alignment
- Removed unused functions (
renderLiveAssistantMessage,renderPlanPanel,formatPlanForDisplay,normalizeHistoryLineEndings,resolveESMStoreDir,updateViewportContentWithFollow) and trimmed the associated plan/ESM test coverage. - Question requests are now registered through
DecisionService, so pending questions persist and replay like approvals; duplicate question requests are rejected. - Terminal decision status is mapped from the actual run state instead of always marking pending decisions
cancelled— only an explicit cancellation recordscancelled, any other terminal outcome recordstimed_out. - The deferred print loop gained a
stopPrintLoopexit path used on quit and reload so queued transcript lines are drained before teardown. - The external status-line refresh is deferred to actual renders, coalescing event-dense bursts into a single refresh.
sessionsDelnow resolves the session directory through the defensivegetSessionDir()helper.
- Removed unused functions (
✅ Tests
- TUI: new tests cover question decision registration (pending kind, persistence, duplicate rejection) and ESM store directory resolution.
v1.2.97
Changelog (Current Version)
This file contains the changes for the current version only. The full history of all versions lives in docs/en/changelog.md.
v1.2.97
✨ New Features
-
Online Model Discovery for Providers
- Provider model discovery moved into
internal/provideras shared helpers (ModelsEndpoint,ResolveSecretRef,DiscoverModels) that fetch and normalize a provider's/modelslisting intoDiscoveredModels. The OpenAI-compatible/v1/provider/modelsand model-test endpoints now call these shared helpers instead of duplicating probe plumbing.
- Provider model discovery moved into
-
TUI: Fetch and Search Online Models in the Auth Dialog
- A new "Fetch Online Models" entry in the provider model list and model settings views runs discovery against the draft provider's Base URL / API type on a background command and opens an "Add Model · Online List" view where fetched models can be added to or removed from the draft. Nothing is persisted until the provider is saved, and stale results after closing the dialog or switching providers are dropped, with loading/empty/error states and zh/en labels.
- Typing in the online list filters fetched models, ranked exact > prefix > substring across model ID and display name while preserving discovery order within the same score; Esc clears the query, and a "No models match." hint shows when nothing matches.
🔧 Improvements
-
Public SDK Boundary:
agent/Stays Internal-Free- The provider bridge moved from the public
agent/package intobootstrap/(which external modules already blank-import); it registers the provider resolution hook plus the concrete provider factories at init time. agent.Builderno longer pre-resolves the platform session directory (the internal builder resolves the default at Build time) and reports a clear error when a hook is unregistered; the examples now blank-importbootstrapinstead of internal packages.
- The provider bridge moved from the public
-
Session Store Integrity Hardening
DeleteSessionnow prunes child tables without asession_idcolumn (delivery_operations,attachment_deliveries) through their session-owned parents, so deleting a session leaves no orphaned rows.- Schema migrations apply in ascending version order instead of slice order; forward table references no longer depend on FK enforcement being disabled.
- Non-terminal/terminal Run status sets are centralized in
run_store.goas the single source of truth; SQL literals, partial unique indexes, fork, trajectory, and recovery paths all derive from them. EndConversationTurnis idempotent for already-closed turns; conversation entry IDs grow to 64-bit;IdentityLocksdelegates to a ref-counted lock registry, and the runtime lease bus closes its UDP listener once the last handler unsubscribes.
🐛 Bug Fixes
-
TUI: Instant Submit for a Lone Enter
- A queued Enter was treated as line-break evidence, so every quick type-then-Enter send waited the full 120ms split-paste coalescing window. The extended idle window now applies only when the queue carries real paste evidence (newline-bearing rune chunks or Enter adjacent to text); a lone deferred Enter keeps the normal 16ms window and submits immediately.
-
Missing Error Reason on Abandoned Durable Runs
- A background run abandoned after interrupted tool execution could reach a terminal status without persisting the reason. A dedicated annotation boundary (
RunDAO.UpdateErrorIfEmpty→session.AnnotateSessionRunError→agentruntime.AnnotateDurableRunError) now sets the error only while it is still empty — without changing run status, reviving terminal runs, or touching active runs, keeping the first recorded reason authoritative. The Responses API abandon path persists the reason through this boundary.
- A background run abandoned after interrupted tool execution could reach a terminal status without persisting the reason. A dedicated annotation boundary (
-
Duplicate User Entry in the Background Run Coordinator
- The durable user entry appended during admission is already in replay state after the manager reload; the coordinator now matches the deterministic
RunUserEntryIDand reuses it as the continuation message instead of appending a duplicate to the transcript and the provider request. The check stays idempotent across retries, recovery, and process restarts.
- The durable user entry appended during admission is already in replay state after the manager reload; the coordinator now matches the deterministic
-
Channel Rotation Lease Target
AcquireRuntimeForRotatenow takes the session directory explicitly from the lifecycle owner (falling back to the dispatcher's configured directory when empty), so the mutation lease and the forced-release wait target the authoritative Session instead of whatever directory the dispatcher holds.
✅ Tests
- Architecture:
public_sdk_boundary_testfails if the publicagent/package orexample/modules import internal packages again. - New session tests: delete integrity (no orphaned rows), ascending migration order, Run status-set consistency, ref-counted lock registry, and lease-bus listener cleanup; widened orphan-recovery timing margins under
-race. - Regression tests for the fast lone-Enter submit path (split-paste continuation still protected) and durable-run error annotation.
v1.2.96
Changelog (Current Version)
This file contains the changes for the current version only. The full history of all versions lives in docs/en/changelog.md.
v1.2.96
✨ New Features
-
Runtime Workspace Input Materialization
- A single front-end-neutral input contract now owns user files for every entry point (CLI, TUI, WebUI/API, ACP, WeChat, Feishu). Adapters submit source streams;
internal/agentruntimematerializes accepted files into the project workspace and the first user message declares file paths plus metadata, letting the Agent decide how and whether to read each file. - Images are no longer auto-converted to provider image content on ingest; input resources persist via the
input_resourcestable with a Runtime-owned lifecycle (PrepareInput/AttachPreparedInput, discard/delete/cleanup,input_resource_events). - TUI
/paste-imagenow submits a stream the Runtime writes; the Web UI gained attachment upload/preview in chat; ACP prompt content (text/image/file/audio/video) and WeChat/Feishu inbound media all normalize through this same ingress.
- A single front-end-neutral input contract now owns user files for every entry point (CLI, TUI, WebUI/API, ACP, WeChat, Feishu). Adapters submit source streams;
-
Lease-Based Execution Admission and Orphaned Run Recovery
- The legacy
TryLockRuntime/TryLockRuntimespath was replaced with explicit, purpose- and run-bound runtime leases across CLI, TUI, ACP, Serve, channels, and cron: ref-counted durable lease guards (AcquireExecutionAdmission/AcquireFork/AcquireMutationsand run binding) plus recovery/reconciliation modes for stale or orphaned runs. - A new
RecoveryCoordinatorconverges orphaned runs lease-first through a startup scan and periodic/wake-driven retries, backed by thesession_run_recoveriestable with durable state, retry accounting, and idempotent replay. - The session runtime snapshot surfaces admission/recovery facts (
reserved,local,external,detached_remote,orphaned,recovery_failed,inconsistent); the Web UI shows matching status badges and disables delete/fork while a session is busy.
- The legacy
-
Durable Delivery Outbox
- Delivery intents and ordered operations (
delivery_intents/delivery_operations) with deterministicPlanDeliverysequences (caption/upload/send/fallback), a Runtime claim/fence/retry coordinator, terminal-state atomic commit of assistant message/Run/turn/event/intent, and service-start recovery. - WeChat (image/video/file) and Feishu (image/file) outbound media deliver natively through frozen transport contexts; published artifacts moved into a private store outside the work directory with integrity verification (size + SHA-256) on open.
- Delivery intents and ordered operations (
-
Idempotent Run Submissions
- New
runtime_submissionstable with reconcile-on-conflict handling: submit-key conflicts reuse the existing submission instead of creating duplicates, making Run admission retry-safe.
- New
🔧 Improvements
-
DAO-Only SQL Migration
internal/dbnow owns process-wide SQLite/Bun connection lifecycle and transaction boundaries; all session, cron, stats, ESM, and delivery SQL moved intointernal/daopersistence objects.- Removed the
internal/commondbcompatibility package and the delivery legacy bridge; the architecture guard enforces the DAO-only boundary with a minimal migration-owner allowlist.
-
Web UI Loading and State Stability
- Route views (Chat/Sessions/Stats/Cron/Skills/Settings/Login) are lazy-loaded so only the active route's chunk is fetched; lucide/bits-ui/svelte dependencies are grouped into stable vendor chunks.
- Session runtime state (load/PATCH/polling/mode switching) moved into a unit-testable manager, and loaded history snapshots merge field by field so stale or empty persisted projections never erase live assistant text.
🐛 Bug Fixes
-
Cached Input Token Double Charge
- Usage accounting now computes uncached input tokens (
UncachedInputTokens) instead of charging cache reads twice across Anthropic, OpenAI-compatible, and Google wire formats.
- Usage accounting now computes uncached input tokens (
-
ListSessionRuns Connection Deadlock
ListSessionRunsqueriedinput_resourcesinside thesession_runsrows loop, blocking forever on the single-connection pool and hanging TUI startup for continued sessions with run records. The outer rows are now drained before a single batched query, with a regression test asserting completion.
-
Durable-Run Terminal Event Stability
RunExecutor.Finalizeno longer publishes the terminal stream event for durable runs;FinalizeRunremains the single publisher afterFinishDurablecommits the assistant message, so WebUI history reloads cannot race the database write. Durable identity is recovered from the canonical Run row when the in-memory marker is gone, and already-closed conversation turns are tolerated so idempotent retries still commit the final entry and terminal event.
✅ Tests
- Architecture:
input_contract_guard_testenforces the single input contract across TUI, CLI, WebUI/API, ACP, and Channel entry points. - Extended admission/recovery tests for lease-first orphan convergence, execution snapshots, stop handling, idempotency, and cross-process lease behavior; delivery process integration tests for claim/fence/retry and coordinator recovery.
- Regression tests for cached-input-token accounting and the
ListSessionRunsdeadlock.
v1.2.95
Changelog (Current Version)
This file contains the changes for the current version only. The full history of all versions lives in docs/en/changelog.md.
v1.2.95
✨ New Features
-
Durable CLI Runs
- CLI
runPrintnow persists canonical durable runs viaagentruntime.ExecutionRuntime, aligning the CLI path with WebUI, channels, and ACP lifecycle tracking.
- CLI
-
UDP Runtime Lease Bus
- Added best-effort UDP
SessionLeaseBusfor local-process wake-up on runtime lease and run-state changes. - Uses directed loopback broadcast with deduplication; SQLite leases and durable rows remain the sole authority.
- Added best-effort UDP
-
Per-Run Provider/Model Selection (API & Web UI)
POST /v1/responsesruns accept an optionalproviderfield; qualifiedprovider/modelIDs are parsed and validated (with a structured mismatch error when the provider does not own the model).- The run executes with the requested provider's agent built through the shared
SessionRuntime; run policy snapshots and request fingerprints now record the provider. /v1/modelsnow reports each model's provider.
🔧 Improvements
-
Event Broker Resync
- Event broker now exposes
SubscribeWithResync; subscriber overflow closes the WebSocket so the client reconnects and replays durable SQLite cursors.
- Event broker now exposes
-
Runtime Lease Heartbeat
- Lease heartbeat now retries transient SQLite failures for a bounded interval and publishes
acquired/released/lostnotifications.
- Lease heartbeat now retries transient SQLite failures for a bounded interval and publishes
-
Session Capabilities (Sandbox/Browser/Web Search)
SessionRuntimegainsCapabilitySnapshot,ConfigureCapabilities, andSetCapabilityOption; browser and web-search capabilities persist viasession_capabilitiesand replay on load, with core tools synchronized accordingly.- ACP sessions restore persisted capabilities and additional directories under the runtime lease; sandbox remains process-policy-owned.
-
Web UI Provider-Aware Model Picker
- New searchable
ModelPickercomponent with modality icons (text/image/audio/video/file) replaces the old model menu. - Chat composes a provider-cascading model catalog from
/v1/modelsplus configured providers, so selecting a provider narrows to its models and submits the provider with each run.
- New searchable
-
ACP Session Extension Methods
- Added
session/forkandmothx/session/setTitlehandling, workspace-window negotiation (cwd/additional directories), a cascade delete across fork lineage, and anavailable_commands_updatenotification. - Optional editor context is injected as a bounded, untrusted context block; loading historical sessions with a released runtime lease no longer rewrites persisted bindings.
- Added
🐛 Bug Fixes
- Background Run Optimistic Concurrency
- Reloaded the shared session manager after durable admission so the background coordinator appends the user message to the new leaf instead of failing its optimistic concurrency check.
✅ Tests
- Acquired the runtime lease in
TestResponsesRunAPIAbandonMarksInterruptedToolsWithoutRetrybefore inspecting the abandoned tool record, matching the production recovery caller pattern. - Added ACP tests: loading a historical session with a released lease must not persist defaults, directory updates under the runtime lease, and title changes on historical sessions.
- Added serve tests for per-run provider selection, provider/model mismatch, and qualified-model parsing.