22 Commits

Author SHA1 Message Date
NI0317
8940282aee feat(session-export): add command and Header action 2026-08-12 17:41:36 +08:00
Chinesezjc
526febc44d feat(feedback): add the Web surface for message feedback
Consume the durable message-feedback sidecar from #2217 in the browser:
per-message Like/Dislike with an optional note, contributed through a
declared assistant-actions slot.

- carry MessageId on finalized AssistantMessageNode so a target is nameable
- declare conversation.chat.assistant-actions and render it in the
  IconActions row between copy and branch
- hold one FeedbackController per Session with per-item ifVersion CAS,
  reconciling a version-conflict from the reply's authoritative item
- mount messageFeedbackRemote alongside goalsRemote
2026-08-11 17:14:21 +08:00
Hypatia May
2b2899b4ce Merge remote-tracking branch 'origin/master' into fix/continuable-subagent-policy-inheritance
# Conflicts:
#	docs/module-graph.i18n.yaml
#	docs/module-graph.md
#	docs/module-graph.zh.md
#	packages/subagent/subagent-inprocess/src/index.ts
#	packages/subagent/subagent/package.json
#	packages/subagent/subagent/src/child-agent.ts
#	packages/subagent/subagent/src/continuation.ts
#	packages/subagent/subagent/tsconfig.json
2026-08-10 21:42:45 +08:00
Yichen Jiang
b90cb1b0b0 test(web): re-record the subagent goldens against a rebuilt client
The child session now shows the preset badge its parent shows, which is the
visible consequence of recording the composition it runs. The first recording
of these two goldens was taken against a dist built before the master merge,
so it captured the fallback Chinese label instead of the English one
`newEnglishPage` pins — the web lane replays the BUILT client, and a stale
build reads as a product difference. Re-recorded after `pnpm run build`.
2026-08-10 20:55:32 +08:00
Hypatia May
1478d3f806 test(web): refresh subagent conversation goldens for the pinned-approval Custom chip
The delegation-pinned approval/policy: never makes a child session's knobs
match no preset, so the child conversation's Access chip truthfully reads
Custom.
2026-08-10 20:14:16 +08:00
Yichen Jiang
2f481fa352 fix(apiproxy): echo the preset a created session runs, not its header
`session.create` also adopts an already-live session, and the preceding
commit newly allows adopting one under the preset it switched to while
blank. Its response still echoed `header.agentPreset`, so that adoption
answered with the preset the session had just left — contradicting the
request it had accepted and the row `session.list` serves for the same
session from `resolveSessionPreset()`. The echo now resolves the same way.

The `assertPresetUnchanged` parameter doc said `existing` was the preset
the session was created under; both callers now pass what it runs.

`composeFrom()` was documented as "infallible" and "cannot fail" beside two
`@throws`. It has no composition failure mode — no roster read, no mount, no
file — but it does reject a caller error, and the wording now says which.

The package-level "switched preset" test re-linked to the same preset id,
so it could not tell reading the parent's live scope chain from reading its
creation header. A second fixture preset makes the switch real.

The Web browser lane's subagent goldens gain the preset badge a child now
shows, which is the visible consequence of recording its composition. That
lane runs only under DSH_EXAMPLE_MODE=lib and was missed before.

The Agent Note records two limits found in review: a cold-resumed
continuable child joins its parent's current composition rather than the one
its header names, and `toolFilter` does not constrain a joined child. The
latter is a regression from the agent-plane move rather than anything this
change introduces — with the same tools in the global layer the filter
applies normally — and is tracked in #2185.

Refs #2185
2026-08-10 19:28:40 +08:00
creatixchu
84a6bae1c7 fix(web): drop the branch action from user and steering bubbles
The branch control on user and consumed-steering bubbles could enable
only when a completed turn ended with no node after the message at all,
so readers saw a permanently unavailable control whose tooltip promised
a state it could not reach. An enabled one would mislead anyway: a fork
at a message seq cuts at the containing turn/end and takes the answer
along, the opposite of the branch-to-re-ask reading a control on one's
own bubble suggests.

MessageItem loses its fork props, PendingSteeringBubble loses the
showBranch special case, and messageBranchSeqs narrows to
assistantBranchSeqs: only a completed turn's transcript tail that is
the turn's own content-text assistant may fork. A steered turn keeps
its fork point under the settled answer, because fork is a log-prefix
cut and the steer is model-visible history the child inherits.

Web aria goldens drop the user-bubble disabled-branch row and its
hidden explanation text; the nested-subagent golden also loses the one
enabled user-tail fork handle, a loss the decision note accepts.
2026-08-06 17:46:18 +08:00
creatixchu
851fe793c4 Merge remote-tracking branch 'origin/master' into worktree/context-source-cards
# Conflicts:
#	packages/client/runtime/src/client/sessions/transcript-adapter.ts
#	packages/client/ui-conversation/README.i18n.yaml
2026-08-06 12:03:15 +08:00
Yichen Jiang
96c44c83d4 test(web): stabilize and refresh the aria goldens for the speed readings
The TTFT and tok/s readings divide by measured wall time, so they are not
reproducible: the same replayed scenario yielded 69 and 70 tok/s on
consecutive local runs, and a 3 ms replayed stream reads 26333 tok/s. Baking
those into committed goldens made the Web lane flaky by construction, and the
goldens for the readings themselves were never refreshed.

Three fixes, then a refresh:

The footer's decorative dots are `aria-hidden`, so the readings concatenated
into one accessible string — `Ran for 13sTTFT 0.2s12 tok/s`. That is a real
defect on its own (a reader hears one run-on instead of three facts) and it
also denied `{{duration}}` the word boundary it matches on, so even the
previously-stable `Ran for` duration started leaking raw. The separators now
carry flanking spaces.

`normalizeAria` gains `{{throughput}}` beside `{{duration}}`, and its duration
alternation accepts the stats line's compact `2m42s` as well as the
message-chrome template's `2m 42s` — the compact form had no pattern at all,
which is why `LLM 382m39s` survived the first refresh.

Refreshed 17 goldens. They also record that the stats line's `LLM` group now
renders at all: it folds assistant `timing`, which the live transcript adapter
only began attaching in this branch, so the group was previously dead in Chat.

Verified by running the lane in replay three times after the refresh: 41/41
files green each time, goldens untouched. Before this change two consecutive
runs disagreed on both the values and the failure count.
2026-08-05 17:55:49 +08:00
creatixchu
fab131a366 fix(web): align the context separator with ToolRow and name the caption
Review follow-ups on the context provenance row:

- the producer name now follows ToolRow's aria-hidden separator dot instead
  of a literal middot inside the text, so the two disclosure rows expose one
  accessible-name shape;
- the English steering caption becomes the noun `Interjection`, parallel to
  the Chinese 插话 and to the label this bubble carried before;
- the history fold gets its own provenance assertion, so the mapping it
  declares separately from TranscriptAdapter is pinned on both sides;
- the superseded-in-part bullet on the no-interjection-chrome note now
  enumerates the third Decision bullet too, whose no-badge and no-IconActions
  clauses had both lapsed.
2026-08-04 18:25:44 +08:00
creatixchu
de96087923 feat(web): name context sources and mark steering in the transcript
Every logged non-user user/message collapsed into one identical
「上下文注入」 row, and mid-turn steering rendered in exactly the bubble a
turn-opening prompt uses, so the transcript could not say what had been
added or which message interrupted a running turn.

TranscriptAdapter and the history fold now attach a provenance view to
each context node, computed by contextProvenance() from the durable
source alone: a role (inject, or recall for a cross-session snapshot)
and a producer name read out of the log — instruction paths for
workspace-instructions, session titles for session-reference, the plugin
id for a plugin source, and the bare kind for any other. No client-side
table of producer names, so a renamed or newly mounted producer stays
identifiable without a client release and a foreign log projects like a
live one. ContextInjectionRow titles itself from the role and shows the
name beside it; MessageItem captions durable and pending steering
bubbles.

The caption reverses one clause of the no-interjection-chrome decision,
which removed it because the composer could not steer; composer steering
shipped afterwards without amending that note, so this change supplies
the product decision its reintroduction clause required and corrects the
stale facts left in it.

Fixes #1291
2026-08-04 17:44:18 +08:00
Yif
9813159973 test(web): refresh aria goldens for the run-time footer label 2026-08-03 19:59:00 +08:00
kingwl
c5050f018e fix(web): keep ineligible message forks disabled 2026-08-03 16:19:49 +08:00
kingwl
038b6063ee Merge remote-tracking branch 'origin/master' into codex/pr-1150-conflict-fix
# Conflicts:
#	packages/client/ui-conversation/README.i18n.yaml
2026-08-03 11:34:26 +08:00
Tianyi Cui
3fc4142c04 fix(web): pluralize singular subagent counts
The localized catalog exposed one count.total and one count.running string for every cardinality. The English dictionary therefore rendered both the visible trigger and its accessibility label as 1 subagents, and the assembled Web golden had begun preserving that grammar error.

Split both count families into explicit one and other keys, following the existing client locale convention. SubagentCatalogAction selects the pair from the effective descendant count; English uses subagent for one and subagents otherwise, while Chinese keeps its unchanged classifier text under the same key domain.

Add a component regression proving a single running descendant selects both singular keys. Update the real Web E2E locator and keyless assembled aria golden from 1 subagents to 1 subagent. Both ui-subagent test files pass all 28 tests and the package TypeScript project builds cleanly.
2026-08-02 20:17:34 +08:00
kingwl
d9a370e185 test(web): refresh completed-turn-tail snapshots 2026-08-02 17:53:38 +08:00
Tianyi Cui
5c98cbd8f6 test(web): run the subagent-conversation e2e against the locale-aware copy
The ui-subagent catalog and read-only composer copy moved from hardcoded
Chinese to the locale-aware `subagent` namespace, so an en-US headless
browser now renders English. The e2e's selectors and goldens still
asserted the old hardcoded Chinese strings, leaving the scenario unable
to find the catalog trigger.

Convert the selectors to the default (en-US) render and re-record the
catalog goldens (ui, tree, nested) in English. The locale-aware parts of
the remaining goldens were already English (recorded under the en-US
default), so sidebar and fork are untouched.
2026-08-02 14:05:37 +08:00
imccyu
5d56019d22 feat: revert crumbs navigation for subagents 2026-08-02 12:51:11 +08:00
imccyu
1aedb23ca6 test(web): align subagent access mode golden 2026-08-02 12:51:09 +08:00
imccyu
5ef5feb01a fix(web): reconcile rebased subagent contracts 2026-08-02 12:51:09 +08:00
Dudu-0223
8a518e353b feat(web): rewrite subagent conversations for FIFO activation 2026-08-02 12:51:09 +08:00
Dudu-0223
16ffd63115 feat(web): add nested subagent conversations 2026-08-02 12:51:09 +08:00