Skip to content

[pull] main from lobehub:main - #105

Open
pull[bot] wants to merge 6439 commits into
XMethues:mainfrom
lobehub:main
Open

[pull] main from lobehub:main#105
pull[bot] wants to merge 6439 commits into
XMethues:mainfrom
lobehub:main

Conversation

@pull

@pull pull Bot commented Jan 27, 2026

Copy link
Copy Markdown

See Commits and Changes for more details.


Created by pull[bot] (v2.0.0-alpha.4)

Can you help keep this open source service alive? 💖 Please sponsor : )

@pull pull Bot locked and limited conversation to collaborators Jan 27, 2026
@pull pull Bot added ⤵️ pull merge-conflict Resolve conflicts manually labels Jan 27, 2026
Arvin Xu and others added 27 commits August 15, 2026 18:38
Add a dedicated `goals` table so a goal is an independent target entity
instead of a JSONB marker on `tasks.config.goal`.

- goals table owns its definition (title / requirement), budget
  (max_rounds / max_total_cost) and lifecycle state, decoupled from
  task.status
- execution carrier is an optional polymorphic link
  (subject_type / subject_id), so a goal can back a task-driven /goal
  flow today, a conversation-declared goal later, or stand alone —
  no hard dependency on tasks
- add goalStatuses / goalSubjectTypes consts exported via
  `@lobechat/const/goal`
- register goals id namespace (goal_ prefix) and schema export
- add drizzle migration 0141 + snapshot

Co-authored-by: Arvin Xu <arvin.x@lobehub.com>
* ✨ feat(expertise): add data schema

* ♻️ refactor(expertise): tighten lesson section types

* ♻️ refactor(expertise): use UUIDs for internal records

* ♻️ refactor(expertise): require domain owners

* 🐛 fix(expertise): enforce domain consistency

* ♻️ refactor(expertise): regenerate migration after rebase
* 🐛 fix: preserve session on provider key errors

* 🐛 fix: silence expected provider key errors
fix(docker): include swc helpers runtime
#18338)

Replace the Home-list dump in the agent/project header popover with a compact
searchable picker, and forward Popover trigger props so the select can open.
* ✨ feat(expertise): add model and ingestion backend

* ♻️ refactor: harden expertise generation and ingestion

* ✅ test: cover expertise scopes and workspace isolation
…18367)

* 🐛 fix(acceptance): pin review predictions to a vision-capable model

The predictor inherited the verifier's chat model via resolveVerifyModelConfig,
which in production resolved to deepseek-v4-pro — a text-only model. The
channel stripped the evidence screenshots ("Unsupported Image"), so the model
accepted every check on the prompt's missing-evidence rule at floor confidence,
and no proposal card ever surfaced.

Pin the predictor to gemini-3.6-flash@google — the model the offline baseline
was scored on, vision-capable and ~13x cheaper than kimi-k3. The pin also keeps
agreement stats comparable across acceptances instead of varying with whichever
verifier each run used. A regression test asserts the pinned model carries
vision: true in model-bank.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* ♻️ refactor(acceptance): source the review-predict model from business-const

Move the pinned model/provider values into @lobechat/business-const next to
DEFAULT_ONBOARDING_MODEL, so the cloud build overrides them by swapping the
stub package instead of patching the verify service. The vision guard test now
looks the pin up in LOBE_DEFAULT_MODEL_LIST by provider, so it keeps holding
whatever provider the constants point at.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…ning page (#18195)

* ✨ feat(expertise): add the self-evolving surface end to end

Model / router / page for the SCLPT expertise layer, behind a Labs toggle.

- ExpertiseModel: binding resolution (agent + workspace), latest snapshot per
  domain via DISTINCT ON, hit tiers computed server-side (relative cut, not an
  absolute threshold — one domain practised 47 times and another twice can't
  share one).
- expertise router: maturity is refused, not faked, when the fit is pending,
  low-confidence, or its τ hit the search ceiling. 6 of 9 backtests pinned at
  the boundary, and the earlier implementation reported all of them as normal
  percentages.
- /agent/:aid/self-learning: domain rail + learning curve (bars = rules learned
  that round, projection drawn only when the fit is credible), layer coverage
  with blank layers kept visible, and the rule base ordered by hits.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* 📝 docs(acceptance): record the shared-Postgres migration skip

The managed acceptance Postgres is shared by every worktree, and drizzle decides
what to apply from journal timestamps. A sibling worktree that applied its own
same-numbered migration minutes later makes yours get skipped in silence while
the command still prints "migration pass" — found while verifying #18195, where
all eight expertise tables were missing after a green migrate.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* 💄 style(expertise): rebuild self-evolving around the curve, not a rail

The rail made picking an expertise the first thing you do, when the first thing
you want is what state they are all in. Both levels now follow the prototypes.

L0 — every expertise's curve on one chart, normalized to maturity so a 3-rule
domain and a 34-rule one are comparable. The shape IS the conclusion, so colour
encodes shape (flat / rising / declining / stuck) rather than rank; a legend that
names four shapes while the colours track a leaderboard is worse than no colour.
Above it, one sentence of judgement; below it, insights and a list whose bars are
cumulative — a bar that grows to the top means done, with no "shrinking is good"
translation needed.

L1 — the full fit panel on its own route (deep links work): observed solid vs
projected dashed, today's divider, the 90% target, and four cards where r² sits
next to maturity on purpose. The pinned-τ backtests all had beautiful r² — what
they fit beautifully was the straight stretch, so "fits well" and "extrapolates
credibly" have to be readable as two different things.

Also fixes the count the last acceptance round caught: practice count came from
listRuns' length, so a 60-run domain read 50 in the detail and 60 in the list.
It is its own COUNT now.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* 💄 style(expertise): give the empty state the shared illustrated Empty

Acceptance feedback on round 1: two lines of centred text read as a rendering
hole rather than a state. Uses @lobehub/ui Empty with the feature's own icon,
the same shape every other empty surface in the app uses.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* 💄 style(expertise): draw the half of the fit chart nobody had seen

The extrapolation, the 90% target and "N more practices" only render when the
fit is credible AND the domain is still short of 90%. Neither seeded domain met
both, so the chart's whole right-hand side had never been exercised — it looked
finished while its main visual was untested.

Also wires the bars to hadHumanInLoop, which the schema has carried since day
one and nothing read: blue is the practices you were in. That contrast is itself
a finding — the rounds where someone was in the conversation are the rounds it
learned fastest — and a single-colour bar cannot say it.

Subhead now names the curve shape ("estimated ceiling 97, and the curve is still
climbing") rather than stopping at the number.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* ✨ feat(expertise): close the review round — two write paths and the copy

Round 3 review, nine items. The two that needed backend:

- The empty state now creates an expertise. The form is mostly one field —
  what counts as practice — because that filter is the only executable thing
  a expertise has before it has learned anything, and it is what stops it
  learning from everything. A filter a human wrote IS a chosen anchor, so it
  lands practisable rather than in a second "confirm the direction" step.
- "这里缺少可行的 action?" was right: the screen said pick a direction and
  offered nowhere to pick. Candidates now render with a button that applies
  the chosen one's layers, canon and filter. The rejected candidates stay in
  anchorCandidates — six months on someone will ask what the other branch
  would have looked like.

The rest: breadcrumb gains the 自进化 level; the projection note moves under
the chart title; formulas and statistics terms become sentences (P∞(1−e^(−n/τ))
→ "projected at the current pace", 学习率 1/τ → 学习速度 with "one cycle per
60 practices"); layer bars become fixed-width right-aligned tracks so the
module reads as the gap between layers rather than a wall per row; an
uncomputable fit hides its three empty metric cards instead of showing a row
of dashes; 算不出 → 暂不确定 in the list.

Two things the new write paths exposed: a never-practised expertise was drawn
as an empty grid with five "run 1" ticks, and was tagged 练了没学到 — which is
a failing grade for something that has not started. Both now have their own
state.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* ✨ feat(expertise): make the path visible, and the snapshot readable

Round 4 review. Two of the notes were about the whole thing rather than any
one screen, and those drove the work:

**"Missing a tracing of the full flow — from first seeing it, through
accumulating data, to checking state."** Every screen stood on its own but
nothing said how they connect, so a first-time visitor sees curves and has no
idea what to do to make them move. A three-step strip now sits under the
headline — pick a direction / let it do the work / read where it stands — with
the count of expertises that have reached each step. It is progress and
navigation at once: stuck on step two means the direction is set but nobody is
using it for real work.

**"Need something like a word cloud or domain snapshot — the counts read flat,
no sense of the whole."** They do read flat: 73 and 1 look the same weight in a
right-aligned column, you have to read line by line to compare. The coverage
block now scales type size and fill with rule density, so the skew IS the
picture. Zero layers keep a dashed outline — the gap is the part most worth
seeing.

The rest, item by item: creation collapses to one sentence with the name parsed
out server-side (the sentence itself stays the filter, unrewritten — rewriting a
user's criterion rewrites what the expertise will learn); empty-state and
"算不出" copy turn positive ("这里还没有积累", "还需时间积累"); rule bars adopt
the same fixed-width right-aligned track as the layers so both columns share one
ruler; the projection note drops its "observed a full cycle" tail; anchor-pending
gains a where-the-data-comes-from block; a lone expertise renders as a full card
instead of one thin row under a lot of white space.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* ✨ feat(expertise): simplify the self-learning review flow

* ♻️ refactor: require expertise direction upfront

* ✨ feat: ingest topic context into expertise

* ♻️ refactor: defer expertise ingestion to review

* ✨ feat: add expertise rules detail view

* ✨ feat: add expertise lesson drilldown

* 📝 docs: audit self-evolving UX surfaces

* 💄 style: refine self-evolving experience

* 🐛 fix: complete expertise ingestion flow

* 💄 style: clarify expertise learning hierarchy

* 🧪 test: support non-streaming Responses stub

* 🐛 fix: complete automatic expertise generation

* 💄 style: refine expertise lesson evidence layout

* 💄 style: align expertise detail metadata

* 💄 style: tighten expertise modal spacing

* ✨ feat: ingest historical topics into expertise

* 💄 style: compact expertise detail layouts

* 💄 style: align expertise detail fields

* 🐛 fix: align expertise UI with generated domains

* 🐛 fix: chart cumulative expertise learning

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
…#18348)

* ✨ feat(database): add resource transfer requests schema and migration

* ♻️ refactor(database): move transfer enums to @lobechat/types and drop schema-level enum typing

* 📝 docs(skill): add schema conventions (no pg enums for growing domains, no const exports from schemas) to db-migrations

* 🐛 fix(database): preserve transfer audit rows when actors are deleted (set null instead of cascade)

* 🐛 fix(database): exclude null-recipient rows from the pending-transfer unique index

* 📝 docs(skill): scope the schema-constant rule to domain literals in new/modified schemas
# 🚀 LobeHub Release (20260816)

**Release Date:** August 16, 2026
**Since v2.2.13:** 363 merged PRs · 21 contributors

> This cycle gives longer-horizon work a durable home: Projects and
Goals land as first-class containers for agent work, seven new CLI
coding agents join the heterogeneous runtime, a Local Sandbox executes
on your machine, and the platform opens up through a generated OpenAPI
spec and SDK.

---

## ✨ Highlights

- **Seven new CLI coding agents** — Cursor, CodeBuddy, Qoder, Kimi Code,
Pi, TRAE, and Grok Build (ACP) join the heterogeneous runtime, plus a
Codex app-server lab. (#18229, #18219, #17965, #18228, #17899, #18292,
#18254, #18275)
- **Local Sandbox** — A local execution environment for devices, with a
real working directory instead of a refusal. (#18143, #18180)
- **Public API & SDK** — `openapi.yml` generated from routes, a new
`@lobehub/sdk` package, expanded v1 endpoints, and scoped API keys.
(#17944, #17980, #18141, #18029)
- **Home customization** — A Customize modal with Minimal / Balanced /
Full presets, a day-paged daily brief, and a task-shaped task mode with
scheduled tasks. (#17912, #17948, #17958, #18173)
- **Desktop split views** — Split tab views, a tab strip reworked for
many-tab use, and a faster cold start. (#18004, #17809, #17811)

---

## 🏗️ Core Agent & Architecture

### Projects & Goals(Alpha)

- Project schema, backend and CLI, workspaces with conversation PoC, and
an identifier column. (#17996, #18006, #18020, #18034)
- Goal creation split from task creation with an explanatory empty
state; goals get their own table and AI-generated acceptance criteria.
(#18047, #18335, #18265)
- Goal launch requires confirmation even in auto-run mode. (#18111)

### Agents & Groups

- Group member permissions with personal model choice on shared
builtins. (#18122)
- Async agent transfer and copy, with history backfill and intact
membership lifecycle. (#17997, #18030, #18082, #18126)
- Sub-agents follow the parent model by default, with thinking controls.
(#17938)
- Agent quota gets a usage calendar and burnout projection. (#18117)

### Context & Memory

- Graph runtime context is injected continuously; container message
tokens count toward the context budget. (#17761, #17839)
- Every memory tool call renders a dedicated card; extraction prompts
align with their schemas. (#17891, #17988)
- Opt-in automatic topic summary workflow. (#17796, #18171)

---

## 📱 Platforms & Integrations

### Heterogeneous CLI Agents

- New runtimes: Cursor CLI with model selection, CodeBuddy, Qoder with
reasoning effort, Kimi Code, Pi, TRAE, and Grok Build over ACP; Codex
app-server session lifecycle completes as a lab. (#18229, #18244,
#18219, #17965, #18160, #18228, #17899, #18292, #18254, #17907, #18275)
- The model selector is driven by a capability table, with local CLI
descriptors centralized as a single source of truth. (#18214, #17979)
- Windows support hardened: CLI spawning, agents behind unknown shims
and stale PATH. (#18095, #18220)
- A heterogeneous error taxonomy classifies terminal errors; cloud auth
failures surface clearly. (#17887, #18040)

### Desktop

- Split tab views, a tab strip reworked for many-tab use, and pinned
tabs that travel. (#18004, #17809, #17857)
- Boot straight into the last active workspace; cold-start critical path
reduced and the local database prewarmed after navigation. (#17946,
#17811, #18218)
- Chromium zoom presets and update downgrades. (#17679, #18012)

### Messaging & Bots

- `/mode` command switches bot conversation mode; recent same-channel
history is injected. (#18197, #16608)
- Proactive messenger push with per-channel notification settings.
(#17791)
- Discord completes deferred forwarded interactions. (#18169)

### Models

- Gemini 3.7 Flash, GLM-5.3 with always-on thinking, Grok 4.6 reasoning
effort, and MiniMax-H3 video with official v2 API. (#18289, #18290,
#18301, #18225, #17827)
- Audio lands in multimodal understanding with input cost estimation;
Cerebras and Groq gain advanced reasoning parameters. (#17904, #17949,
#16469)

---

## 🖥️ User Experience

- Home: mine/team scope and author chips on recent topics, open goals in
the rail, and one-page dashboard scrolling with a persistent recents
cache. (#17908, #18121, #17836)
- Chat: web voice messages, editable opening questions, keyboard-driven
AskUserQuestion with select-to-submit, and a reachable, effective
error-card retry. (#17744, #18003, #17903, #18080)
- Agent identity: an avatar studio with one-click brand-style
generation, profile artwork with style presets, and personal names with
a dice-roll composer. (#18113, #17929, #17986, #17853, #17894)
- Workspace agents organize with labels and a shared sidebar; the
sidebar header becomes an identity switcher. (#17848, #18338)
- Resource library: path-based category routes with a Files category,
origin filtering, and restored document scrolling. (#18083, #18170,
#18345)
- Onboarding: Notion and X understanding sources, streamed generation
progress, and confirmed starter tasks that run immediately. (#18110,
#18118, #18105, #18224)

---

## 🔧 Tooling & API

- OpenAPI spec generated from routes via hono-openapi, shipped as the
generated `@lobehub/sdk`. (#17944, #17980)
- v1 API adds api-keys, evals, mcp-servers, and usage endpoints.
(#18141)
- API key scopes, with workspace keys scoped to member permissions.
(#18029, #18136)
- Jupyter notebook (.ipynb) upload with token-efficient markdown
conversion. (#17855)
- CI gates bundle size for web dist and desktop asar. (#17970)
- Agent tracing visualizes context composition. (#18114)

---

## 🔒 Security & Reliability

- **Security:** custom provider base URLs are validated (#18223);
restricted knowledge bases are soft-hidden from members (#18282);
deleting a connector unlinks remote accounts (#18270); auth cookies
support custom prefixes and subdomain sharing (#18358, #18200).
- **Reliability:** blocking Redis XREAD runs on a dedicated connection
(#17876); file parse cache writes are serialized (#17919); repeated
network-error toasts collapse into one (#17873); runs no longer strand
in `verifying` (#18164); sessions survive provider key errors (#18343).
- **Performance:** PDF parsing drops a 25MB native dependency (#17858);
agent page render cost is cut across sidebars, composer, and topic list
(#17991); recent-topic previews are batched instead of per-topic
subqueries (#17928).

---

## 👥 Contributors

Huge thanks to **21 contributors** who shipped **363 merged PRs** this
cycle.

@sxjeru · @Cyber-Yichen · @northword · @smbslt3 · @hardy-one ·
@orangeboyChen · @nightcityblade · @Max-Reisinger · @Mark-star-dot ·
@ImSingee · @BittuBarnwal7479 · @arvinxx · @Innei · @AmAzing129 ·
@tjx666 · @rdmclin2 · @neko · @lijian · @sudongyuer · @rivertwilight ·
@cy948

---

**Full Changelog**:
v2.2.13...release/weekly-20260816
…lock layout (#18375)

* 🐛 fix(home): exclude live schedules from recent tasks and align the block layout

The recent-tasks block now requests the list with automated: false, so
tasks that still fire on their own (cron schedules, heartbeats) appear
only in the scheduled block below instead of printing twice on one page.
The automation filter joins the cache key and the shared-list scope
reset, and refreshTaskList invalidates task:list variants by key root.

Layout polish on the same block: the task blocks share a 12px inline
inset so row hover pills stay inside the column, and row timestamps get
a fixed 56px right-aligned slot so times and avatars form straight
columns across both sections.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* 🐛 fix(home): hide completed tasks from the recent block

Finished work is history for the Tasks page, not part of "what is going
on", so the recent block now narrows the request to the non-completed
statuses. The include-list is derived from the canonical TASK_STATUSES
set rather than hand-listed, so a status added later shows up on Home by
default. The status filter joins the automation filter in one trailing
cache-key slot and in the shared-list scope reset.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* 💄 fix(home): show the animated running mark on live task rows

A task that is executing right now wears the shared RunningGlyph ring
instead of the static status dot — the same liveness signal running
topics and the home rail cards already use — in the recent-tasks block
and the sidebar recents.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…desktop (#18379)

* ✨ feat(billboard): open web onboarding for resetOnboarding action on desktop

* ✨ feat(billboard): restrict desktop resetOnboarding action to official cloud

* ♻️ refactor(billboard): resolve action availability via guard pipeline

* 🐛 fix(billboard): coerce isSyncActive selector to strict boolean
…livery-checker (#18376)

* ♻️ refactor(verify): unify acceptance criterion UI and remove lobe-delivery-checker

- Add shared CriterionList / CriterionRow / CriterionRequiredChip and a single
  CriterionEditor + openCriterionEditModal under src/features/Verify/CriterionList
- Migrate the /goal intervention card, CreateGoalModal, TaskVerifyConfig,
  PendingAcceptanceCheckList, TaskAcceptance and GoalDetailPage to the shared
  row/editor, deleting the two duplicated criterion edit modals
- Unify the required chip to a blue info tag labelled 必选 (verify:criterion.*)
  and drop the divergent 必要/必需/必选 key trio across chat/plugin namespaces
- Delete the unused builtin-tool-lobe-delivery-checker package with its server
  runtime, registry entries, i18n keys and the orphaned createPlanFromCriteria

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* 📝 docs(acceptance): note stale-cache fingerprint of a cross-wired browser session

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* ♻️ refactor(verify): apply acceptance review feedback on criterion editing

- Drop the pending-status icon and C{seq} anchor from goal-creation draft
  rows — those belong to the post-creation check list, not authoring
- Remove TaskVerifyConfig's edit mode (SortableList reorder, side drawer,
  Done button): every row now edits itself through the shared criterion
  modal, matching the goal-creation interaction; regenerate moves into the
  header overflow and add/save-as-template become always-visible footer
  actions
- Simplify the criterion edit modal title — no C{seq} interpolation

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* 📝 docs(acceptance): record ingest-side identifier/internal-id gap in probe patterns

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* ♻️ refactor(verify): apply round-2 acceptance review feedback

- Keep the C{seq} anchor on goal-creation draft rows; only the pending
  status circle stays out of authoring
- Drop the at-rest delete button from task criterion rows — removal lives
  inside the editor modal
- Move save-as-template from the footer into the header overflow menu
- Show the verifier tag and required chip on goal-detail check rows

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* ♻️ refactor(verify): fold task criterion row edit and delete into a per-row overflow

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* 🐛 fix(chat): restore embedded conversation toolbar

* 🐛 fix(chat): bind embedded toolbar topic state
* ✨ feat(chat): add realtime voice dictation

* 💄 style(chat): refine dictation listening control

* 💄 style(chat): round active dictation control

* 💄 style(chat): apply circular dictation radius

* 💄 style(chat): animate dictation listening halo

* ⚡️ perf: reduce dictation connection latency

* 🐛 fix(chat): preserve dictation control focus

* 🐛 fix: support keyboard dictation controls
* 💄 style(ui): align loading skeletons with page layouts

* 🐛 fix(ui): satisfy profile skeleton flex types
…#18390)

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…to the goals table (#18374)

* ♻️ refactor(goal): migrate goal implementation from task.config.goal to the goals table

Make the new `goals` table (#18335) the single source of truth for goal
identity, budget, requirement and lifecycle, replacing the JSONB marker on
`tasks.config.goal`.

- add GoalModel (create / findBySubject / listBySubjects / updateStatus with
  startedAt/completedAt stamping / deleteBySubject) + tests
- write paths: TaskService.createTask accepts a `goal` input and creates the
  bound goals row; the createGoal tool runtime (server + client executor) and
  the CreateGoalModal both go through it — the config-merge race between the
  goal marker and verify config is gone, and the unused originTopicId is
  dropped (task.context.origin.topicId already records it)
- read paths: settle / goalLoop / acceptanceService / taskLifecycle /
  buildTaskPrompt / deleteGoal resolve the goal via GoalModel.findBySubject;
  TaskModel.getGoalConfig and TaskGoalConfig are removed; the hasGoal list
  filters become an EXISTS on goals
- status machine: goals.status is now server-written — running on run start
  (planInstantiation) and loop rounds, verifying/review mirrored from the
  acceptance lifecycle, review on converged verify, paused on exhausted
  budget or reject-without-budget, failed on errored verify, achieved on
  accept, canceled when the carrier task is canceled
- reads attach the goal entity to task list/detail responses
  (groupList / getTaskDetail / ProjectModel.listTasks); the client consumes
  task.goal instead of casting config JSONB, and goalPresentation prefers
  goals.status over the task-status heuristic

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* 🐛 fix(goal): stop reading the planned acceptance phase as verifying

The verify plan is instantiated and confirmed at RUN START, so the
acceptance lifecycle sits in `planned` for the whole executing round.
Mapping that phase to `verifying` (both in the server-side goal-status
mirror and in the client presentation tiers) showed 验证中 for a goal
that was actually executing its round — caught by the E2E acceptance
run on a live goal task.

- acceptanceService.mirrorGoalStatus: drop the planned → verifying
  mirror; the goal stays `running` until verification actually runs
- goalPresentation.acceptanceStatusKey: let `planned` fall through to
  the goal-entity status tier instead of rendering 验证中
- regression tests on both layers

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* 📝 docs(acceptance): record the Goals page entry + Labs toggle probe recipe

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* 💄 fix(goal): apply acceptance review feedback on goal status visuals

Two circled-region review items from the acceptance round:

- the create-goal modal's criterion rows no longer show the dashed
  check-status circle — verification has not started at creation time,
  so the state glyph carried no meaning there
- a running goal now shows the same spinning ring as running task
  topics (shared RunningGlyph) instead of a static dot, via a new
  GoalStatusGlyph used by the list row, card view and detail header

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* 🐛 fix(verify): address codex review on the shared criterion components

- CriterionEditor: an existing criterion's judging rule lives in a linked
  document this editor neither loads nor can update (updateCriterion has no
  instruction field) — show a read-only note instead of an editable blank
  box that silently discards input
- CriterionRow: ignore Enter/Space bubbling from nested action controls so
  keyboard activation of an edit/delete button no longer also opens the row

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* 🐛 fix(goal): address codex review on goal-row lifecycle integrity

Two of the three codex findings are real integrity gaps in the FK-less
goals ↔ tasks link; the third (legacy config.goal backfill) is a
deliberate product decision documented on the PR.

- TaskService.createTask: compensate a failed goal insert by deleting
  the just-created task. Deliberately compensation instead of a
  transaction — TaskModel.create's identifier-conflict retry continues
  after a 23505, which an enclosing transaction would abort. Without
  this, a transient goal-insert failure left a ghost task that never
  lists on goal surfaces and stacks on retry.
- TaskModel.delete / deleteAll: sweep the bound goals rows inside the
  same transaction. deleteAll (the task.clearAll path) previously never
  swept at all, permanently orphaning every cleared goal; single delete
  swept in a second non-atomic statement.
- regression tests for the compensation path (service) and the bulk
  sweep with cross-user scoping (model, real DB)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* ✅ test(taskLifecycle): mock GoalModel in the onTopicComplete suite

onTopicComplete now asks the goals table whether the completed task carries
a goal (goal-loop rounds suppress the per-topic brief). The suite passes a
bare `{}` as the db, so the un-mocked GoalModel blew up on `db.query.goals`
in CI (Test Server shard 2/2). Mock it like VerifyRunModel — default: no goal.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Arvin Xu and others added 30 commits August 26, 2026 13:38
* 🧪 test: bootstrap local device gateway

* 🐛 fix: preserve goal acceptance contracts

* ♻️ refactor: keep goal criteria domain neutral

* 🐛 fix: collect hetero acceptance evidence server-side

* 🐛 fix: detect hetero evidence by provider

* 🐛 fix: require self-contained goal evidence

* 📝 docs: bound long-running health probes

* 🐛 fix: pin installable UI dependency

* 🐛 fix: pin installable UI in desktop workspace

* ⬆️ chore: unpin repaired UI dependency

* 🐛 fix: charge stale work before recovery

---------

Co-authored-by: Arvin Xu <arvin.x@lobehub.com>
…#18694)

* ✨ feat(desktop): gzip renderer OTA CAS and keep r0 plus last-2 deltas

Tree-diff with zstd --patch-from instead of re-downloading the full renderer,
gzip the original CAS objects, and generate deltas from the packaged r0 tree
plus the two most recent patch versions.

* ♻️ refactor(desktop): simplify renderer OTA lineage

* 🐛 fix(ci): publish renderer base after r0
Co-authored-by: Arvin Xu <arvin.x@lobehub.com>
…approval mode (#18705)

* fix: task verify config pannel

* 🐛 fix: stop stale multi-tab settings writes reverting tool approval mode

* 🐛 fix: make tool intervention merge atomic in a single SQL statement

* 🐛 fix: route builtin-tool uninstall writes through an atomic tool-column patch

* 🐛 fix: keep aborted settings columns pending and pin uninstall writes to the captured workspace
…me (#18672)

* 💄 style(acceptance): make the AI review round always report its outcome

The predict button's poll waited for proposal CARDS, but an agreeing verdict renders none — so on a clean delivery the button span the full two-minute timeout and then said nothing, indistinguishable from the feature being broken (which it recently was, making the two literally untellable apart). The mixed-decision case was broken in the other direction: already-decided checks counted toward the exit threshold, ending the poll before the batch finished.

The bundle now carries predictionStatus per check — the fact that the attempt finished (judged/skipped/errored), separate from the owner-only card gating which is unchanged. The poll exits when no pending check lacks a status, and the round always ends in a toast: N flagged / reviewed-and-agreed / could-not-judge / still-running. Zero cards becomes a stated result instead of silence.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* 🐛 fix(acceptance): reset the previous batch before re-requesting AI review

Found while planning the acceptance run: the predictor upserts on completion, so a second click left the previous batch's rows in place until each replacement landed — and a poll that waits for 'every check has a recorded attempt' saw that condition met on its first tick, ending with a stale summary before one new judgement had finished.

predictReviews now clears the current reviewer's unanswered rows before dispatch (adjudicated rows are labels and stay), so a missing row means 'not judged yet' for the whole batch. The bundle also reads only the current reviewer's rows: after the model pin changed, a stale row from the old model would otherwise satisfy the poll the moment the new one was cleared.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* 💄 style(acceptance): tone the AI-review toasts by outcome

A green tick on 'could not form an opinion' read as the review having passed — the exact ambiguity the toast exists to remove. Inconclusive is now a warning; nothing-pending and still-running are info; only real verdicts are success.

Also records the local verification recipe for the predictor (pinned Gemini vs the OpenAI-shaped stub, SSRF fetch-back of evidence images, no pending row state) in the acceptance probe living log.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Arvin Xu <arvin.x@lobehub.com>
…nt stacks (#18712)

- Add a desktop "Font" settings group with an Interface Font picker backed by
  the system font list, alongside the monospace picker moved out of "Terminal".
- Apply the monospace preference to antd's fontFamilyCode so it drives code
  blocks everywhere, not only the built-in terminal.
- Build the app font stack in src/const/font.ts and insert the UI language's own
  CJK block (JP / KR / TC) ahead of the SC families, so Japanese, Korean and
  Traditional Chinese no longer render Han glyphs with Simplified fonts.
…18719)

* 💄 style: add workspace waitlist landing link copy and official url

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* 🌐 style: add auto-translated locales for waitlist landing link

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* 💄 style: add new-tab hint copy for waitlist landing link (a11y)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…18680)

* ✨ feat(gateway): let a gateway pull the connections it should hold

A gateway keeps its registry in memory, so a restart leaves it holding
nothing while the database still knows what should exist. Recovering that
by having the server push on demand needs a lock, a cooldown and a
reconciliation protocol, all to answer one question — and the side doing
the asking still cannot see whether the connections came back.

Answer it as a read instead: the gateway asks for its list and builds it
itself, so the side that establishes the connections is the side that can
see the result. Retrying a read is free.

The response body is what the reconcile would have pushed, built by the
same function, so a connection does not depend on who established it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* 🐛 fix(gateway): do not hand over a connection its previous host still holds

Routing a platform to a host does not make its connections safe to build
there. Right after the routing changes, the host that owned them a moment
ago is still polling — hand one over before that host is drained and every
message is delivered twice until a later reconcile catches up.

The reconcile already enforces this. The pull path was bypassing it and
answering from routing alone, so a gateway restarting mid-migration would
rebuild the whole fleet alongside the old one.

Withheld entries make the answer partial, so the caller keeps asking. It
runs out of attempts long before a hand-off completes, which is correct:
finishing one is the reconcile's job, not this call's.

An unreadable host counts as drained, the same trade-off the reconcile
documents — a steady-state deployment has nothing on the wrong host, so
blocking recovery on an unrelated outage costs more than it saves.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* 🐛 fix(gateway): never move a platform to a host that cannot serve it

Routing a platform to a host is currently taken on faith. Point it at one
that cannot serve that platform and the result is not a no-op — it is an
outage: the stale pass drains the connections off the host that was
serving them, and the connect meant to replace them is rejected, so they
end up on neither.

Gateways already describe themselves. Ask, and refuse the move when the
destination says it does not serve that platform: leave the connections
running where they are and let the misrouting be corrected.

Only an explicit declaration counts. A gateway that does not describe
itself makes no claim, and silence must not be read as refusal — one of
ours has no such endpoint, and treating it as "serves nothing" would make
rollback impossible, which is far worse than the case being prevented.

Also names the platform entries that match nothing, whose only symptom is
otherwise that the change quietly does not happen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* 🔒 fix(gateway): let a caller pull only the host that rebuilds this way

`host` was caller-controlled while every gateway authenticates with the
same service token, so the token could not say which gateway was asking.
A token that leaked out of one of them could name the other and read its
credentials — which that token could not do before this endpoint existed,
since the admin surface returns ids and states, never credentials.

Only one host rebuilds by pulling; the other recovers from its own durable
registry. Asking for that one's slice can only be a caller reaching for
credentials that are not its own, so refuse it. Keeping exactly one
pull-capable host is what makes "who is asking" answerable without
per-host credentials; a second one would need them first.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* 🐛 fix(gateway): a half-seen host does not prove it released anything

The cross-host check treated any snapshot it got back as authoritative,
but a stats-only one — registered ids failed, live stats did not — omits
dormant registrations. An id missing from it says nothing about whether
that host still holds it, so a restart mid-migration could take over a
connection the other gateway is only sleeping on, and deliver twice when
it wakes.

Withhold what such a view does show, and mark the answer partial so the
caller keeps asking rather than settling for it. Absence is only absence
when the view was complete; finishing a hand-off stays the reconcile's job.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* 🔒 fix(gateway): derive the pulling host from its credential, not the request

The endpoint that hands back a host's credentials was authenticated with
the token every gateway shares, so the server could not tell which one was
asking and had to trust a `host` field the caller wrote. Blocking one value
narrowed that to "read your own", not "read at all" — the credentials are
on the side that legitimately pulls, and naming it was allowed.

Give the pulling host its own credential. One credential, one host, so
presenting it IS the claim of who is asking, and the request no longer
names anything. A second pull-capable host gets its own, never a share.

Keeping the shared token here was the wrong call and the reason was wrong
too: it is not a two-party secret. It is written into every WeChat connect
payload as `webhookToken` and persisted with the connection config, so it
lives across the fleet's storage. Manipulating connections with it is the
exposure already accepted; reading every credential in one request is not.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* 🐛 fix(gateway): hand over only what a complete peer view proves released

Marking the answer incomplete did not protect anything. The caller applies
every config it receives and only then asks again, so a flag lands after
the duplicate it was meant to prevent — the connections were still handed
over. A dormant registration missing from a stats-only snapshot could start
here while still running on the host that owns it.

Absence is evidence only when the view was complete. A snapshot that could
not be fetched and one that omits dormant registrations are the same claim:
nothing. Withhold in both cases.

That is stricter than the reconcile, deliberately. The reconcile owns
hand-offs and has the machinery for them — drain before connect, per-round
caps, ids tracked as they are released. This call has none of that; it is
an optimisation over the reconcile, so when it cannot be done safely the
right move is not to do it.

The test asserted the flag rather than the withholding, which is why the
gap survived the previous round.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* 🔒 fix(gateway): do not hand a host credentials for a platform it cannot serve

Routing a platform to a host does not mean it can run it. The reconcile
already refuses to move such a platform, but the pull handed its configs
over anyway and left the gateway to reject each one — functionally
contained, but it arms nothing and exposes the credentials for nothing.

The asymmetry was also the trap: a reader of the reconcile would take the
guard as enforced, while the path that actually returns credentials
skipped it. That is the same shape as the cross-host check this endpoint
was missing before.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
✨ feat(artifact): package local sites for hosted publishing
* ✨ feat: consolidate home promo surfaces

* ✅ test: stabilize home banner queue in CI
…18703)

The product surface is the Acceptance domain: every route is /acceptance, and what lived under features/Verify was either the acceptance page itself or one of its sub-objects — a round's report, the in-chat round card, the criteria editor. 'Verify' no longer named anything of its own, and the page sat at features/Verify/Acceptance, one level deeper than its siblings.

New layout, grouped by sub-object instead of nesting Acceptance/Acceptance:
- Viewer/        the acceptance page atoms (was Verify/Acceptance)
- Workspace/     the acceptance list panel (was Verify/Acceptance/Workspace) + useReportPanelExpand
- Report/        ReportViewer and the evidence/visualization renderers (was Verify/components)
- Run/           RunResult and CheckerDock, the in-chat round card
- CriterionList/, hooks, utils, routeMeta unchanged at the root

Removes the dead VerifyWorkspace / ReportListPanel / EmptyDetail trio (zero consumers since AcceptanceWorkspace replaced it). Pure move otherwise: git detects 90 renames; imports rewritten across src, apps/workbench and packages, plus the path literals in the workbench stub tests and two skill docs.

Co-authored-by: Arvin Xu <arvin.x@lobehub.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…th its cost (#18735)

* 💄 style(quota-calendar): lead each day with its cost and flatten the window list

The weekly window list repeated "Last observed window" on every past row and
labelled every meter "Capacity used", so five rows of chrome carried one row of
information. Each window is now a single line — span, meter, percentage, spend —
with only the live window named.

The day cells inverted the reading order: a coding agent's calendar is scanned
for money, so the cost now leads and the token count backs it up.

Token counts also stepped from M straight past a billion, printing "3136M"
where the rest of the product would say "3.1B".

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* 💄 style(quota-calendar): keep a lower-bound day cost inside its cell

Leading a day cell with its cost put "at least $836" where a seventh of the
panel had to hold it, and the label truncated to "at least $…". The cell now
wears the bound as a suffix — "$836+" — which also keeps every amount in the
grid starting on the same glyph.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* 📝 docs: record how to reach and seed the quota usage calendar

Verifying the calendar cost three dead ends worth writing down: the composer
only mounts the quota panel for a local-execution hetero agent, the ledger (not
the window rows) is what both the day cells and the per-window totals read, and
the modal resolves the account by the live CLI identity's externalAccountId.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* 💄 style(quota-calendar): make the window list one card of divided rows

Review feedback on the first round: single-line rows do not each need their own
fill, and the spelled-out "at least" inside a dense row is noise.

The rows now sit flush in the panel card separated by hairlines, and every
amount in the modal body wears the lower bound as a "+" suffix. The words move
to the row's tooltip, which the session grid already had and the list did not.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* 📝 docs: note that the live CLI reading overwrites a seeded current window

Opening the quota panel ingests the machine's real reading into the same account
row, so the current window's percentage is never the seeded one.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

---------

Co-authored-by: Arvin Xu <arvin.x@lobehub.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
* ✨ feat(database): persist heterogeneous interventions

* feat(database): generalize agent intervention approvals

* 🐛 fix(database): supersede partial approval batches atomically

* 🐛 fix(database): fence custom intervention execution retries

* 🐛 fix(database): harden intervention recovery boundaries

* 🐛 fix(database): merge plugin state atomically
* ✨ feat(agent): support durable heterogeneous interventions

* 🔒 fix(agent): keep review tokens out of query URLs

* ✨ feat(agent): align approval workflows with web

* ✅ test(agent): align approval runtime expectations

* ✅ test(agent): align client approval expectations

* 🐛 fix(agent): expose live activity batch terminality

* 🐛 fix(agent): harden durable approval recovery

* 🐛 fix(agent): reconcile approval claim state

* ✅ test(agent): type durable approval fixture
…base-ui (#18729)

The antd-based wrappers are deprecated since @lobehub/ui@5.33.0. This moves every call site to the base-ui subpath.

Beyond the import path, the migration required:

- ActionIcon now renders a native <button> instead of a div: dropped the hardcoded 'nativeButton: false' in ActionDropdown's trigger props (it overrode the library's own inference, so Base UI applied role/aria-disabled to a real button), and retyped the dictation control refs to HTMLButtonElement
- Avatar is no longer antd's: replaced src/icon with avatar, dropped gap (no equivalent)
- Tag is no longer antd's: dropped bordered={false}, which antd v6 already ignored whenever variant was set — and the wrapper always set it
- prop types (ActionIconProps, AvatarProps) now come from the base-ui subpath, since the legacy ones carry antd props and a div ref
- seven files that still imported Button from @lobehub/ui or antd are migrated too, since touching them surfaced the existing no-restricted-imports error: variant=filled -> type=fill, variant=outlined + color=default -> type=default

Verified: type-check clean, 503 tests pass across ChatInput/NavPanel/User, and the migrated surfaces render with no Base UI warnings.
* ✨ feat(heterogeneous-agent): support Grok provider bindings (#18737)

* ✨ feat(db): add workspace document comment schema (#18726)

* ✨ feat(db): add workspace document comment schema

* ♻️ refactor(db): use UUIDs for document comments

* 🐛 fix(db): sync comments during document transfers

* 🐛 fix(db): harden document comment transfers

* 🔨 chore(db): regenerate document comment migration

* 🐛 fix: align chat output cost estimate ratio (#18748)

* ♻️ refactor(desktop): migrate renderer OTA to V2 packs (#18745)

* ♻️ refactor(desktop): migrate renderer OTA to V2 packs

* ⚡️ perf(desktop): decode renderer packs asynchronously

* 💄 style(toast): align toast radius and offsets with desktop layout container (#18755)

* 👷 ci(desktop): read the overlay repository from a variable instead of hardcoding (#18759)

Claude-Session: https://claude.ai/code/session_01TQcoZnDo12NGCxWZDJpciZ

* 👷 ci(share): add PR-time verify workflow for the share micro app (#18757)

* 👷 ci(share): add PR-time verify workflow for the share micro app

Claude-Session: https://claude.ai/code/session_01TQcoZnDo12NGCxWZDJpciZ

* 👷 ci(share): build same-repo PR previews with the cloud overlay

Claude-Session: https://claude.ai/code/session_01TQcoZnDo12NGCxWZDJpciZ

* 📝 docs(skill): record dual-repo share verify and skeleton layout parity

Claude-Session: https://claude.ai/code/session_01TQcoZnDo12NGCxWZDJpciZ

* 👷 ci(share): keep overlay naming neutral in public workflow

Claude-Session: https://claude.ai/code/session_01TQcoZnDo12NGCxWZDJpciZ

* 👷 ci(share): reuse the existing overlay token secret

Claude-Session: https://claude.ai/code/session_01TQcoZnDo12NGCxWZDJpciZ

* ♻️ refactor: migrate lobe-cron to lobe-task (#18753)

* 💄 style(share): center skeleton hero to match hydrated layout (#18756)

Claude-Session: https://claude.ai/code/session_01TQcoZnDo12NGCxWZDJpciZ

* 💄 style: register dedicated skeletons for all lazy routes (#18752)

* 💄 style: add dedicated home route skeleton

* 💄 style: register route skeletons for all lazy routes

* ✅ test: align unit test mocks with base-ui component migration

* ✅ test: complete base-ui mock alignment for migrated components

Claude-Session: https://claude.ai/code/session_01UTDivfaszDuBjpPn7PV7gQ

* 📌 chore: pin @lobehub/ui to 5.33.1 until base-ui migration completes

Claude-Session: https://claude.ai/code/session_01UTDivfaszDuBjpPn7PV7gQ

* ♻️ refactor: finish ActionIcon, Avatar, Tag and Text base-ui migration

Completes #18729: migrates the 45 remaining call sites (plus type-only
imports) to @lobehub/ui/base-ui and drops the temporary 5.33.1 pin now
that the repo is clean under 5.33.3's expanded deprecation list.

Claude-Session: https://claude.ai/code/session_01UTDivfaszDuBjpPn7PV7gQ

* ♻️ refactor: migrate direct antd Tag and Avatar imports to base-ui

Claude-Session: https://claude.ai/code/session_01UTDivfaszDuBjpPn7PV7gQ

* ✅ test: stub base-ui ActionIcon in input banner queue case (#18760)

* ✅ test: stub base-ui ActionIcon in input banner queue case

The real base-ui ActionIcon requires the app-level MotionProvider, which
the unit env does not set up.

Claude-Session: https://claude.ai/code/session_01UTDivfaszDuBjpPn7PV7gQ

* ✅ test: render real base-ui components via a global MotionProvider stub

Redirect @lobehub/ui's internal MotionProvider module to a static stub in
vitest config so base-ui components render in unit tests without the
app-level ConfigProvider, and add composable canonical base-ui stubs
(~base-ui-stubs) for tests that want simplified DOM.

Claude-Session: https://claude.ai/code/session_01UTDivfaszDuBjpPn7PV7gQ

* ✅ test: unblock closed antd-style factories against migrated base-ui imports

Six antd-style mocks were closed factories missing createStaticStyles /
keyframes / createGlobalStyle that real base-ui style modules now need;
compose them over importOriginal. PluginTag and GroupItem retarget their
UI stubs to the base-ui import path the components migrated to.

Claude-Session: https://claude.ai/code/session_01UTDivfaszDuBjpPn7PV7gQ

* ✨ feat: support workspace document comments (#18750)

* ✨ feat: support workspace document comments

* ✨ feat: enhance document comments with mentions and attachments

* 🐛 fix: synchronize document comment activity

* 🐛 fix: guard document comment attachment uploads

* 🐛 fix: preserve comment synchronization metadata

* 🐛 fix: scope document comment API routes

* ✅ test: align base-ui component mocks

* ✅ test: complete base-ui mock migration

* ✅ test: clean up rebased component mock

* ♻️ refactor: align comment imports with base-ui

* ✅ test: align mocks with latest canary

* 🐛 fix(connect-agent): prevent device loading state flash (#18768)

* 🐛 fix(connector): canonicalize connector ownership (#18763)

* 🐛 fix(eval): rm eval hints in run topic titles (#18771)

* 🐛 fix(eval): isolate run topic titles

* 🐛 fix(eval): keep ordinal topic titles

* 🐛 fix(eval): retain run topic input

* 🐛 fix(eval): remove fallback case label

* 🐛 fix(agent): scope heterogeneous default names to shared workspaces (#18770)

* 👷 ci: upgrade pnpm setup action to v6 (#18777)

* 🐛 fix(queue): encode QStash deduplication IDs (#18778)

* 🐛 fix(agent): recover sub-agent bridge answer from isolation thread for hetero children (#18781)

* 🐛 fix(agent): recover the sub-agent bridge's answer from its isolation thread for hetero children

callAgent-invoked heterogeneous (CLI-driven) sub-agents backfill their
parent's placeholder tool message with "Sub-agent completed without a
textual answer." even when the CLI produced a real reply — always, in
production's queue mode (AGENT_RUNTIME_MODE=queue), not intermittently.

completeSubAgentBridge/completeGroupActionMember resolve content from
params.finalState, falling back to coordinator.loadAgentState(operationId)
(the standard runtime's Redis-backed step state) when finalState isn't
passed in-process. Both createSubAgentBridgeHook and
createGroupActionMemberBridgeHook's webhook config deliberately strip
finalState/lastAssistantContent from the QStash payload via `eventFields:
['operationId', 'reason', 'status']` (to keep the payload lean), so the
queue-mode callback always arrives with finalState undefined — and hetero
children never write into the coordinator's runtime state at all (confirmed
zero `saveAgentState` calls under services/heterogeneousAgent/), so the
fallback is structurally dead for them. heteroFinish already resolves the
real answer server-side (via topics.metadata.heteroCurrentMsgId ->
messageModel.findById) before dispatching, but that resolution never
reaches the callback.

Add resolveLastAssistantContentFromThread(threadId): queries the child's own
isolation thread directly (thread ids are globally unique, no agentId/topicId
needed to disambiguate) — the same source heteroFinish itself reads. Used as
a fallback in both completeSubAgentBridge and completeGroupActionMember
(isolated mode) when the existing finalState/coordinator resolution comes up
empty, before falling through to the "no textual answer" stub.

Two regression tests added (one per bridge method), reproducing the actual
hetero shape (loadAgentState resolves null, not a message-stripped state) —
verified to fail against the pre-fix code and pass with it. Full
AgentRuntimeService.test.ts suite (114 tests) still passes.

Fixes LOBE-13586

* 🐛 fix(agent): gate the thread content fallback on missing finalState

Address a P2 flagged by Codex review on 15a401e: the thread-fallback
guard checked only `!lastAssistantContent`, which also fires when a REAL,
authoritative finalState is present but its last turn is legitimately
textless (image-only, or the "preserve an empty leaf" case in
normalizeCompletionMessages). In that case the fallback would still query
the child's isolation thread — and a lagging read that surfaces an EARLIER
real reply from the same thread would silently show stale text instead of
the correct "no textual answer" stub.

Narrow the guard to `!finalState` in both completeSubAgentBridge and
completeGroupActionMember: this is exactly the heterogeneous-child case the
fallback exists for (finalState is never populated for them at all), and
`!finalState` already implies an empty lastAssistantContent, so nothing is
lost for the case the fallback is meant to cover.

Two regression tests added (one per bridge method) proving the fallback is
never invoked when a real finalState says the answer is textless — verified
to fail against the pre-fix (`!lastAssistantContent`) guard and pass with
the narrowed one. Full AgentRuntimeService.test.ts suite (116 tests) passes.

---------

Co-authored-by: AmAzing- <115673583+AmAzing129@users.noreply.github.com>
Co-authored-by: Rdmclin2 <rdmclin2@gmail.com>
Co-authored-by: YuTengjing <ytj2713151713@gmail.com>
Co-authored-by: Innei <i@innei.in>
Co-authored-by: Rylan Cai <67412196+cy948@users.noreply.github.com>
Co-authored-by: Tsuki <76603360+sudongyuer@users.noreply.github.com>
Co-authored-by: LiJian <onlyyoulove3@gmail.com>
# 🚀 LobeHub Release (20260827)

**Release Date:** August 27, 2026  
**Since v2.2.14:** 235 merged PRs · 13 contributors

> This cycle makes long-running Agent work easier to direct and recover,
expands provider-backed heterogeneous Agents, delivers safer
restart-free Desktop updates, and deepens Artifact, Workspace, and
acceptance workflows.

---

## ✨ Highlights

- **Provider-backed heterogeneous Agents** — Claude Code and Codex gain
deployment-backed model options, while Kimi Code and Pi add new provider
paths across web and Desktop. (#18468, #18558, #18609, #18658, #18720,
#18721)
- **Restart-free Desktop updates** — Signed renderer updates apply in
place with compatibility checks, compressed delta delivery,
release-scoped artifacts, and automatic rollback. (#18648, #18694,
#18716, #18724)
- **Agent approval and intervention flows** — Agent approvals now align
across surfaces, and generic interventions persist so long-running work
can resume with the right user decision. (#18417, #18645)
- **Agent self-learning** — A Labs-gated growth portrait shows learned
domains, rules, and practice history while operation-scoped expertise
improves future Work. (#18195, #18394, #18420)
- **Public Artifacts and Workbench** — Local HTML and site assets can be
packaged into versioned Artifacts, while acceptance and verification run
in a standalone SSR Workbench. (#18424, #18473, #18569, #18728)
- **Workspace collaboration** — Per-member Resource grants, transfer
requests, and a redesigned Notification Center make shared work easier
to control and act on. (#18389, #18432, #18536, #18545)
- **More adaptable everyday UI** — Global interface fonts, consolidated
Home promotions, clearer model availability, and refined recommendation
visuals make the app easier to personalize and navigate. (#18712,
#18722, #18730, #18741)

---

## 🏗️ Core Agent & Architecture

### Goals, Tasks & Acceptance

- Goal state moves to a durable graph with dependency-aware scheduling,
CLI controls, autonomous recovery, and terminal Acceptance Work.
(#18574, #18597, #18635)
- Scheduled Tasks get a dedicated view, successful subtasks complete
automatically, and Kanban grouping matches the list view with clearer
loading states. (#18586, #18591, #18631, #18707)
- Acceptance deliveries group by Project and can be merged without
losing checks, evidence, or prior review decisions; the review round now
always reports its outcome. (#18448, #18537, #18548, #18672)
- Acceptance features now share one consistent product boundary, while
abandoned Goal Work and stale scoped tool calls recover instead of
silently ending the run. (#18656, #18664, #18703)

### Agent Learning & Identity

- Self-learning gains dedicated domain creation, editable anchors,
operation-scoped Context, and safer lesson history management. (#18418,
#18420, #18517, #18556)
- Agent profiles gain a character sheet and reusable full-body artwork
that retains its style and direction. (#18446, #18464)
- Agent authors can restrict Topic sharing, while Agent transfers
preserve accumulated expertise. (#18445, #18557)
- Approval workflows align with web behavior, and generic interventions
persist across Agent runs. (#18417, #18645)

---

## 📱 Platforms & Integrations

### Heterogeneous Agents

- Claude Code supports local Provider bindings, while supported Claude
Code and Codex runs can use deployment-backed models with
operation-scoped authorization. (#18468, #18558, #18595, #18609, #18621)
- Kimi Code joins heterogeneous Agent provider bindings, and Pi gains a
dedicated Desktop provider path. (#18720, #18721)
- Each Topic keeps its own heterogeneous model choice instead of
rewriting the Agent default. (#18658)
- Cursor moves to a bidirectional ACP session with permission prompts,
questions, approvals, and safe resume recovery. (#18475)
- OpenClaw and Hermes detection now covers official install paths,
refreshed shell environments, and Windows launchers. (#18646)

### Desktop, Messaging & Gateway

- Desktop applies verified renderer updates without restarting, keeps
compact rollback deltas, and scopes OTA artifacts to the matching
release. (#18648, #18694, #18716, #18724)
- macOS communication notifications include Agent avatars, Workspace
windows retain their bounds, and the terminal adds links and split
panes. (#18430, #18433, #18441, #18500)
- Gateways can pull the connection assignments they should hold, while
message routing can still split platforms across deployments without
changing the Integration protocol. (#18628, #18680)
- Linked chat platforms receive Resource files and attachments with
per-platform budgets, while scheduled Task results and rich Agent pushes
appear in the Notification Center. (#18391, #18467, #18511, #18535,
#18561, #18608)

---

## 🖥️ User Experience

- Realtime voice dictation adds finalized speech to the editable draft
without auto-sending or disturbing existing text and attachments, and
its entry point now sits with the left-side chat actions. (#18132,
#18578, #18709)
- Home restores its marketing hierarchy, consolidates promotional
surfaces, sharpens recommendation visuals, and brings back richer model
shortcuts and the Apps catalog. (#18438, #18528, #18553, #18562, #18580,
#18722, #18741)
- A global interface font setting and locale-aware font stacks make
reading preferences consistent across the app. (#18712)
- GLM-5.3-Flash is available, disabled models can be re-enabled from
chat input, and model prices survive OAuth redirects. (#18723, #18727,
#18730)
- Dead Topic locks no longer swallow sent messages, and completed
gateway runs clear stale loading state in the sidebar. (#18497, #18682)
- Agent document edits recover malformed persisted content and reject
operations that would unexpectedly clear a non-empty document. (#18643)
- Settings search covers more destinations and matches user queries more
reliably. (#18390)

---

## 🔧 Tooling

- Agent Chaos adds deterministic timeout, cleanup, tracing, and
failure-injection contracts for long-running Agent tests. (#18667)
- Evaluation Run Topics restore Test Case history and environment
Context, and tool calls can be forwarded to mock servers for end-to-end
verification. (#18516, #18604)
- Workbench CI checks Worker bundle size and publishes PR previews after
assets reach R2. (#18492, #18592)
- Desktop release automation dispatches renderer OTA before native
releases, and shared UI primitives continue moving to the base component
layer. (#18716, #18729)
- Obsolete Orb repository setup scripts are removed from the development
path. (#18740)

---

## 🔒 Security & Reliability

- **Security:** Resource checks stay scoped to the active Workspace and
support per-member grants without bypassing privacy or role ceilings.
(#18396, #18406, #18525, #18536, #18545)
- **Security:** Server-default heterogeneous operations bind
authorization to the user, operation, Workspace, and allowed
capabilities. (#18558)
- **Reliability:** Context compression avoids repeat work, removes
compressed messages from queued payloads, and settles in-flight tool
calls when interrupted. (#18506, #18587, #18610, #18626)
- **Reliability:** Completed heterogeneous child Agents resume their
parked parent, while RouterRuntime preserves request Context and retries
the next configured channel on insufficient Provider balance. (#18596,
#18668)
- **Reliability:** Multi-tab settings changes no longer overwrite newer
tool approval choices with stale state. (#18705)
- **Performance:** Message-plugin and heterogeneous tool-call lookups
avoid broad scans, and Task lists use a leaner database path. (#18457,
#18519, #18618)

---

## 👥 Contributors

Huge thanks to **13 contributors** who shipped **235 merged PRs** this
cycle.

@ONLY-yours · @orangeboyChen · @jalenzz · @NickShee · @BittuBarnwal7479
· @arvinxx · @Innei · @AmAzing129 · @rdmclin2 · @tjx666 · @sudongyuer ·
@cy948 · @nekomeowww

---

**Full Changelog**:
v2.2.14...release/weekly-20260827
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

⤵️ pull merge-conflict Resolve conflicts manually

Projects

None yet

Development

Successfully merging this pull request may close these issues.