Commit Graph
675 Commits
Author SHA1 Message Date
Yichen Jiang b05b0a80fd Merge worktree-llm-web-config (939, with latest master) into worktree/onboarding-deepseek-config 2026-07-30 23:33:14 +08:00
Yichen Jiang 8c0d626cd5 Merge worktree-llm-dynamic-config (884, with latest master) into worktree-llm-web-config
# Conflicts:
#	docs/cordis-catalog/services.md
#	docs/core-data-structures/core.i18n.yaml
#	packages/client/runtime/README.i18n.yaml
#	packages/host/apiproxy/README.i18n.yaml
#	packages/host/apiproxy/README.md
#	packages/host/apiproxy/README.zh.md
2026-07-30 23:32:31 +08:00
imccyu da9f02221e Merge remote-tracking branch 'origin/master' into worktree-forkweb 2026-07-30 22:53:07 +08:00
Yichen Jiang e5d7fa4cef Merge worktree-llm-web-config (939, with latest master) into worktree/onboarding-deepseek-config 2026-07-30 21:52:25 +08:00
imccyu 3b370549a1 feat(client): increment forked session titles 2026-07-30 21:28:17 +08:00
imccyu 213de9a737 feat(ui-conversation): wire assistant session forks 2026-07-30 21:28:17 +08:00
imccyu 2f8f47e500 feat(ui-workspace): flatten session fork rows 2026-07-30 21:28:17 +08:00
imccyu 36f1489398 fix(web): address session fork review findings 2026-07-30 21:28:13 +08:00
imccyu 57fb5b488e feat: fork 2026-07-30 21:27:14 +08:00
Yichen Jiang 7022ab4a75 Merge worktree-llm-dynamic-config (884) into worktree-llm-web-config 2026-07-30 21:19:17 +08:00
Yichen Jiang b6130b178d Merge remote-tracking branch 'origin/worktree-llm-web-config' into worktree/onboarding-deepseek-config
# Conflicts:
#	packages/client/ui-models/README.i18n.yaml
#	packages/client/ui-models/README.md
#	packages/client/ui-models/README.zh.md
#	packages/client/ui-models/src/client/store.ts
#	packages/client/ui-models/tests/components.spec.tsx
2026-07-30 20:55:12 +08:00
Yichen Jiang f5d21af60b test(ui-models): cover the failure paths, and share the one message reader
The per-file coverage gate caught three uncovered paths in the error handling
this round added: the page banner for a failed row removal, the editor card's
transport-rejection catch, and `store.fail` itself.

Two of them are one click each — Remove with a rejecting write, Apply with a
rejecting write — so they are covered through the UI rather than by calling
the helpers directly. The third was a duplicated `error instanceof Error ?
error.message : String(error)` in two files; it becomes one exported
`messageOf`, which removes the branch from both call sites and gives the
fallback arm a home a direct unit test can reach (the lint rule forbids
rejecting a promise with a non-Error, so a rejection cannot exercise it).
2026-07-30 20:36:38 +08:00
Yichen Jiang 3957ce007a Merge branch 'worktree-llm-dynamic-config' into worktree-llm-web-config
# Conflicts:
#	apps/cli/cordis.yml
#	apps/cli/package.json
#	apps/cli/tests/tui-keyless-smoke.e2e.ts
#	apps/web/tests/details-session-lifecycle.e2e.ts
#	apps/web/tests/snapshots/code-mode-round/ui.expected.md
#	apps/web/tests/snapshots/cordis-tool-round/ui.expected.md
#	apps/web/tests/snapshots/fresh-round-trip/ui.expected.md
#	apps/web/tests/snapshots/lifecycle-chrome/hero.expected.md
#	apps/web/tests/snapshots/lifecycle-chrome/reloaded.expected.md
#	apps/web/tests/snapshots/live-interactions/cancel.expected.md
#	apps/web/tests/snapshots/live-interactions/error-auth.expected.md
#	apps/web/tests/snapshots/live-interactions/retry.expected.md
#	apps/web/tests/snapshots/message-actions/ui.expected.md
#	apps/web/tests/snapshots/question-composer/answered.expected.md
#	apps/web/tests/snapshots/seeded-history/ui.expected.md
#	apps/web/tests/snapshots/steering/mid-steer.expected.md
#	apps/web/tests/snapshots/steering/settled.expected.md
#	docs/cordis-catalog/events.md
#	docs/cordis-catalog/services.md
#	docs/event-producer-consumer.md
#	docs/user/guide/config.i18n.yaml
#	docs/user/guide/config.md
#	docs/user/guide/config.zh.md
#	docs/user/guide/index.i18n.yaml
#	docs/user/guide/index.md
#	docs/user/guide/index.zh.md
#	examples/acp-agent/tests/snapshots/subagent-fork/session.1.jsonl
#	examples/acp-agent/tests/snapshots/subagent-mixed/session.2.jsonl
#	examples/cordis-agent/cordis.yml
#	examples/cordis-agent/tests/cordis-tools.e2e.ts
#	examples/headless-agent/tests/semantic-checkpoint-snapshots/tool-outcome-unknown/session.expected.jsonl
#	examples/headless-agent/tests/subagent-inheritance-snapshots/parent-override/parent.expected.jsonl
#	examples/tui-agent/code-mode.cordis.yml
#	examples/tui-agent/cordis.yml
#	packages/examples/tui-demo/README.md
#	packages/examples/tui-demo/README.zh.md
#	packages/host/apiproxy/README.i18n.yaml
#	packages/pty/tool-bash-persistent/README.i18n.yaml
#	packages/ui/tui/tests/snapshots/status-diagnostics-narrow.expected.txt
#	packages/ui/tui/tests/snapshots/status-diagnostics.expected.txt
#	pnpm-lock.yaml
#	scripts/snapshots/python-sdk-single-exe/advanced/result.json
#	scripts/snapshots/python-sdk-single-exe/advanced/session.1.jsonl
#	scripts/snapshots/python-sdk-single-exe/advanced/session.2.jsonl
#	scripts/snapshots/python-sdk-single-exe/advanced/session.jsonl
2026-07-30 20:15:40 +08:00
kingwl 29d8ca5ee5 Merge remote-tracking branch 'origin/master' into codex/queue-collapse
# Conflicts:
#	packages/client/ui-conversation/README.i18n.yaml
2026-07-30 20:11:46 +08:00
Yichen Jiang 4395268cc1 fix(ui-models): contain the card's credential probe rejection
The review named this call site with the other two, and the previous pass
missed it: the editor card's mount-time `credentials.describe` had only a
fulfillment handler, so a transport failure reached the browser as an
unhandled rejection.

The probe is a placeholder hint ("already configured"), never a precondition
for editing, so it now renders without the hint rather than failing. Covered
by a test that fails without the handler.
2026-07-30 19:29:30 +08:00
Yichen Jiang e6483f0afc feat(settings): detect stale writers with a revision, and announce raw changes
The remaining P1 from the #939 review, plus the P2 it shares a mechanism with.

Nothing carried a version, so two tabs editing one namespace silently
overwrote each other — reproduced as tab B's `reasoning` lost to tab A's
older draft. The seam's per-namespace write queue orders writes; it cannot
tell a fresh writer from one replaying a snapshot a predecessor superseded.

Each namespace now carries a monotonic `revision` over its RAW section. A
write may send `expectedRevision`, checked at the FRONT of the queue (not at
call time, which would race the very predecessor it guards against); a
mismatch rejects with `SettingsConflictError` → `settings-conflict` on the
wire, carrying both revisions. The editor captures the revision it opened at
and, on conflict, asks the user to reopen rather than replaying its snapshot.

The same counter fixes the missing broadcast. `settings/updated` is gated on
the resolved value — correct for consumers, wrong for configuration surfaces:
storing an override equal to the composition base leaves the resolved value
alone while changing what the document says (the field is now overridden, not
inherited) and moving every open editor's revision. `settings/document-updated
(ns, revision)` fires on any raw-section change, in-process or external, and
`host/settings-changed` now rides it.

That event also closes the stale model picker: editing a provider's `models`
changes no route, so `llm/adapters-updated` never fired and an open picker
kept serving the old catalog. A change to an exposed provider namespace now
emits `host/models-changed` too — that namespace holds the catalog.

Docs: both sides of the five touched README pairs, a type-equiv block for
`SettingsPathOp`, and an Agent Note recording what the plane exposes and who
may overwrite what. The deferred wire-redaction gaps (secrets behind
union/intersection/transform, `.default(...)` in the served envelope, schema
text in rejection messages, `new Function` rehydration, pi-ai's `headers`) are
recorded as TODO(settings-wire-redaction) and in Known Limitations rather than
half-fixed.
2026-07-30 19:24:21 +08:00
imccyu a1f1f9c29a Merge branch 'master' into codex/figma-context-injection-row 2026-07-30 19:20:21 +08:00
kingwl dec8173c43 Merge remote-tracking branch 'origin/master' into codex/queue-collapse 2026-07-30 19:20:20 +08:00
kingwl 5f87c7a89c fix(web): close queue collapse review gaps 2026-07-30 19:20:13 +08:00
Yichen Jiang fc8f992cde fix(web): address onboarding review feedback 2026-07-30 18:56:56 +08:00
Yichen Jiang 234018032d Merge remote-tracking branch 'origin/worktree-llm-web-config' into worktree/onboarding-deepseek-config
# Conflicts:
#	packages/client/ui-models/README.i18n.yaml
#	packages/client/ui-primitives/src/Modal.tsx
#	packages/client/ui-settings/README.i18n.yaml
2026-07-30 18:43:06 +08:00
Yichen Jiang 9f996be8e3 fix(web-config): close the wire boundary, the redacted-replace data loss, and three P2s
Five findings from the #939 review, each reproduced before being fixed.

**Configuration reads are as privileged as writes.** `settings.describe`
returns every exposed namespace's configuration and `credentials.describe`
reports whether an arbitrary environment-variable name is configured and from
where — reconnaissance no anonymous caller should have. Both join
PRIVILEGED_METHODS, so the whole configuration plane is loopback-only until
real authentication exists; `trustedHosts` was never authentication. The model
catalog stays reachable: it carries no endpoints or key state, and a LAN
client's model picker legitimately needs it. Asserted over a real HTTP server,
because the Host header a browser actually sends is what decides this.

**The proxy serves only namespaces a registered model provider addresses.**
The settings seam is general — any plugin may register one — but the Web
configuration plane is the model-provider surface. Without the gate, every
future `settings.register()` would silently become remotely readable and
writable configuration. An unregistered namespace and an unexposed one answer
identically, so no caller can enumerate the registry one probe at a time.

**Path-addressed writes replace the redacted-document rebuild.** The editor
reads the REDACTED descriptor, so rebuilding a section from it and replacing
wholesale deleted every literal secret the wire never returned — reproduced as
`{baseURL, reasoning}` in, stored `apiKey` gone out. `settings.mutate` applies
set/unset ops to the section as it stands at the front of the seam's write
queue, and the client names only fields it can see, so an unseen secret is
untouched by construction rather than by care.

P2s in the same pass: `llm/adapters-updated` now contains async listener
rejections (an uncontained one escaped as unhandledRejection, contradicting
the documented "observer failures are contained"); llm-deepseek's retry-policy
swap uses the atomic `registration.replace` instead of dispose-then-register,
which published `[]` then `["deepseek-official"]` so an observer saw the
provider disappear and come back; and a transport rejection no longer strands
the page in `loading` or a card in `busy`, with removal failures surfaced on
the page banner instead of swallowed.
2026-07-30 18:30:15 +08:00
kingwl 1f5d09c2d7 Merge origin/master into codex/figma-context-injection-row
# Conflicts:
#	packages/client/ui-conversation/README.i18n.yaml
2026-07-30 18:27:49 +08:00
kingwl cdd1074805 Merge remote-tracking branch 'origin/master' into codex/queue-collapse
# Conflicts:
#	packages/client/ui-conversation/README.i18n.yaml
2026-07-30 18:21:07 +08:00
Tianyi Cui 53ff5c6ef2 Merge branch 'master' into feature/shared-cli-config-foundation 2026-07-30 18:02:07 +08:00
Tianyi Cui 66e182b516 Merge branch 'master' into feature/shared-cli-config-foundation 2026-07-30 17:35:47 +08:00
kingwl 3d6cacc59b Merge remote-tracking branch 'origin/master' into codex/composer-dock-stacking
# Conflicts:
#	packages/client/ui-conversation/README.i18n.yaml
2026-07-30 17:35:22 +08:00
kingwl 0f0564b3d0 Merge origin/master into codex/figma-context-injection-row 2026-07-30 17:35:04 +08:00
kingwl f5228e7c71 Merge origin/master into codex/figma-context-injection-row
# Conflicts:
#	packages/client/ui-conversation/README.i18n.yaml
#	packages/client/ui-conversation/src/client/chat/ToolRow.module.css
#	packages/client/ui-conversation/src/client/chat/ToolRow.tsx
2026-07-30 17:34:14 +08:00
kingwl abf498b8dc fix(web): align composer context stack 2026-07-30 17:26:44 +08:00
imccyu 4cd5745b94 Merge branch 'master' into codex/details-default-closed 2026-07-30 17:14:51 +08:00
Tianyi Cui abd7d57358 Merge branch 'master' into feature/shared-cli-config-foundation 2026-07-30 17:08:42 +08:00
imccyu 34c124585a fix: test 2026-07-30 16:46:50 +08:00
kingwl 0a25c0d237 feat(web): add queue collapse control 2026-07-30 16:41:55 +08:00
Yichen Jiang 8707f324c6 refactor(ui-models): render the curated fields from a narrowed adapter family
The effort field's existence check was unreachable — EFFORT_FIELD is
total over the two known families — and a coverage exemption was papering
over the branch, which the merged toolchain no longer honored. Taking the
narrowed family as a parameter makes the lookup total at the type level,
so the check and its exemption both disappear. The rendered output is
unchanged: the browser goldens replay byte-identical.
2026-07-30 16:41:44 +08:00
kingwl d7641ec028 Merge origin/master into codex/figma-context-injection-row 2026-07-30 16:20:47 +08:00
kingwl 17893c503e feat(web): match context injection disclosure design 2026-07-30 16:20:34 +08:00
imccyu 17fea637d5 Merge branch 'master' into codex/details-default-closed 2026-07-30 15:40:36 +08:00
imccyu e184b9561a Merge branch 'master' into feat/ask-question-gui 2026-07-30 15:36:22 +08:00
imccyu 925cb0b315 feat(web): plan chip as an always-visible pressed-state toggle
fix: plan button add label
2026-07-30 15:33:45 +08:00
Turtle da5f1878c0 Merge remote-tracking branch 'origin/master' into feature/shared-cli-config-foundation 2026-07-30 15:29:47 +08:00
imccyu 245aeb414c feat(web): workspace header hover card with cwd and creation time 2026-07-30 15:29:12 +08:00
imccyu 4c33ca8bec fix: hero layout when no-workspace 2026-07-30 15:25:50 +08:00
Yichen Jiang 483199d47a Merge branch 'worktree-llm-dynamic-config' into worktree-llm-web-config
# Conflicts:
#	apps/cli/cordis.yml
#	apps/web/tests/snapshots/code-mode-round/session.jsonl
#	apps/web/tests/snapshots/cordis-tool-round/session.jsonl
#	apps/web/tests/snapshots/fresh-round-trip/session.jsonl
#	apps/web/tests/snapshots/lifecycle-chrome/session.jsonl
#	apps/web/tests/snapshots/live-interactions/session.jsonl
#	apps/web/tests/snapshots/navigation-panes/seed.jsonl
#	apps/web/tests/snapshots/question-composer/session.jsonl
#	apps/web/tests/snapshots/seeded-history/seed.jsonl
#	apps/web/tests/snapshots/steering/session.jsonl
#	docs/cordis-catalog/events.md
#	docs/cordis-catalog/services.md
#	docs/core-data-structures/core.i18n.yaml
#	docs/core-data-structures/settings.i18n.yaml
#	docs/event-producer-consumer.md
#	docs/module-graph.md
#	examples/acp-agent/tests/snapshots/workspace-context/session.jsonl
#	packages/client/connection/README.i18n.yaml
#	packages/client/connection/src/index.ts
#	packages/client/connection/tests/node-half.spec.ts
#	packages/client/runtime/README.i18n.yaml
#	packages/client/runtime/README.md
#	packages/client/runtime/README.zh.md
#	packages/client/runtime/src/client/index.ts
#	packages/client/runtime/tests/fake-api.ts
#	packages/client/ui-models/README.i18n.yaml
#	packages/examples/tui-demo/README.i18n.yaml
#	packages/host/apiproxy/README.i18n.yaml
#	packages/host/apiproxy/package.json
#	packages/host/apiproxy/src/api-proxy.ts
#	packages/host/apiproxy/src/api/rpc.schema.ts
#	packages/host/apiproxy/src/api/rpc.ts
#	packages/llm/llm-deepseek/README.i18n.yaml
#	packages/llm/llm-deepseek/README.zh.md
#	packages/llm/llm-pi-ai/README.i18n.yaml
#	packages/llm/llm/README.i18n.yaml
#	packages/llm/llm/README.zh.md
#	packages/sdk/sdk-client/README.i18n.yaml
#	packages/settings/settings/README.i18n.yaml
#	packages/settings/settings/README.md
#	packages/settings/settings/README.zh.md
#	packages/subagent/subagent-dsh-sdk/README.i18n.yaml
#	packages/subagent/subagent-dsh-sdk/README.zh.md
#	packages/support/llm-replay/README.i18n.yaml
#	packages/ui/jsonrpc/README.i18n.yaml
#	packages/ui/jsonrpc/README.zh.md
#	packages/ui/tui/tests/snapshots/model-selector.expected.txt
#	packages/ui/tui/tests/snapshots/model-switching.expected.txt
#	packages/ui/tui/tests/snapshots/resume-sessions.expected.txt
#	packages/ui/tui/tests/snapshots/status-diagnostics-narrow.expected.txt
#	packages/ui/tui/tests/snapshots/status-diagnostics.expected.txt
#	packages/ui/tui/tests/tui.snapshot.ts
#	pnpm-lock.yaml
#	python/sdk/README.i18n.yaml
#	scripts/snapshots/translation-prompt-v4/request-response.expected.json
2026-07-30 15:18:26 +08:00
imccyu 6ea1238714 docs(web): the composer bar is one session-maybe slot, not a swapped pair
fix(web): keep one composer bar DOM across the no-workspace transition

The composer.bar slot moves from session to session-maybe scope: with no
current session the entry still mounts, the machine faces (keyboard, stop,
command) arrive undefined, and the bar renders its normal DOM inert via the
disabled owner prop. DisabledInputBar and its parallel tree are gone, so the
textarea node survives the cold-start workspace pick instead of flashing
through a remount. A blank session whose workspace was deleted takes the
same inert path through owner props.

test(web): add the composer DOM-continuity acceptance probe

Drives a real dsh web server with headless chromium through the cold-start
-> pick-workspace -> type flow and asserts the composer textarea is the
same DOM node throughout (a marker property must survive). Rerun
prerequisites are in the header comment.

docs(web): session-maybe identity is adoption, not hold-forever

fix(web): session-maybe entries adopt the first session, then remount like strict entries

A session-maybe entry used to keep one React instance across every
transition, so component-local state leaked between sessions once the
composer bar moved to that scope (PermissionSelect's optimistic pick, the
IME composition guard). Identity is now adoption: an incarnation born
session-less holds through the arrival of the first session (the blank
shell's DOM survives the workspace pick), and afterwards behaves exactly
like a strict session entry — a switch or a drop to no-session remounts,
clearing local state by construction. The child key is an incarnation
counter kept in the stable outlet wrapper via render-phase setState.

chore: knip knows the root acceptance probe's playwright dependency

scripts/hero-composer-dom-continuity.mjs resolves playwright through
apps/web's devDependency tree (createRequire), which knip cannot follow;
ignore it at the root workspace.
2026-07-30 15:18:06 +08:00
imccyu 6c7520c652 Merge remote-tracking branch 'origin/master' into mergebot/pr883
# Conflicts:
#	apps/web/tests/snapshots/question-composer/answered.expected.md
#	apps/web/tests/snapshots/question-composer/ui.expected.md
2026-07-30 15:04:59 +08:00
imccyu 03936cb320 Merge remote-tracking branch 'origin/master' into mergebot/pr883
# Conflicts:
#	packages/client/ui-question/package.json
#	packages/client/ui-question/src/client/QuestionComposer.tsx
#	packages/client/ui-question/src/client/contract/slots.ts
#	packages/client/ui-question/src/client/index.ts
#	packages/client/ui-question/src/client/locales.ts
#	packages/client/ui-question/tests/browser-plugin.spec.ts
#	packages/client/ui-question/tests/question-composer.spec.tsx
2026-07-30 15:01:10 +08:00
Turtle 39efc3dd73 Merge remote-tracking branch 'origin/master' into feature/shared-cli-config-foundation
# Conflicts:
#	.agents/notes/implemented/testing/2026-07-24-web-gui-browser-e2e-lane.i18n.yaml
#	docs/testing.i18n.yaml
2026-07-30 15:00:42 +08:00
Turtle 36f6d8fd05 Merge remote-tracking branch 'origin/master' into feature/shared-cli-config-foundation
# Conflicts:
#	packages/ui/tui/README.i18n.yaml
2026-07-30 14:58:10 +08:00
imccyu 5c2fc03d8f Merge branch 'master' into codex/details-default-closed 2026-07-30 14:50:50 +08:00