Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 59 additions & 32 deletions docs/design/standalone-daemon-sessions.md
Original file line number Diff line number Diff line change
Expand Up @@ -1213,32 +1213,59 @@ Estimated size: 300-500 production lines and 450-800 test lines.
Exit criterion: consumers use the complete lifecycle without constructing
routes or supplying internal cwd.

### PR5: Explicit WebUI context

Suggested title: `feat(webui): Add explicit daemon session contexts`

Dependency: PR4. [PR #8882](https://github.com/QwenLM/qwen-code/pull/8882) is
merged; re-audit its final API and extend its transaction rather than
duplicating it.

- Add `standalone | workspace { cwd } | live` to connection and transition
state.
- Classify from persisted source plus validated ownership, never cwd/runtime
kind alone.
- Atomically commit or roll back client, transcript, internal cwd, product
context, warnings, and deferred intent.
- Accept legacy `workspaceCwd` only at the workspace compatibility boundary,
normalize it immediately, and reject conflicts. It never selects standalone.
- Add directory-recreated/missing/compromised and outcome-unknown notice state.

Verification covers all #8882 failure and supersession cases plus cross-context
switching, capability absence, legacy source, outcome recovery, warning
rollback, and no-primary-fallback.

Estimated size: 350-650 production lines and 650-1,100 test lines.

Exit criterion: WebUI represents and switches all contexts explicitly while
existing visible WebShell behavior remains unchanged.
### PR5: Explicit WebShell session context

Suggested title: `feat(web-shell): Add explicit daemon session contexts`

Dependency: PR4. PR5 targets the current `main` ownership boundary: WebShell
continues to consume the daemon React provider from `@qwen-code/webui`, so the
provider changes land there and WebShell-facing types remain exported through
the existing `daemon-react-sdk` entry. The later WebShell cutover can carry the
same files by rename; it is not a prerequisite.

- Add `standalone | workspace { cwd } | live` to provider props, connection
state, and transition state. Use a distinct `sessionContext` name because
`connection.context` already stores model context-window status.
- Classify from an explicit requested context plus the authoritative restore
path. Standalone uses the dedicated capability-gated SDK methods. Workspace
uses its exact ordinary runtime cwd. Live resolves exactly one trusted
capability-advertised Live runtime and then relies on the daemon's persisted
source and ownership validation. Source strings or cwd alone never select a
product context.
- Follow the loading-skeleton switching model restored by
[PR #9129](https://github.com/QwenLM/qwen-code/pull/9129): publish the target
context, clear the old transcript, and keep the failed target visible with an
explicit error. Do not restore the transaction or roll back to the previous
conversation. A generation guard prevents a superseded completion from
publishing its client, transcript, warnings, or context.
- Accept legacy `workspaceCwd` only at one compatibility boundary, normalize it
immediately to `{ kind: 'workspace', cwd }`, and reject conflicts with an
explicit context. It never selects standalone or Live. Existing callers that
provide neither field retain the current primary-workspace behavior.
- Keep daemon-internal routing cwd private. `connection.workspaceCwd` remains a
product workspace only and is absent for standalone and Live sessions.
Standalone working-directory state and outcome-unknown recovery remain in
target-scoped standalone connection state.
- Skip workspace providers, Git, preheat, and workspace event invalidation for
standalone and Live contexts. Session-scoped commands, model context, Goal,
transcript, prompt, and permission behavior remain shared.
- Standalone creation awaits the SDK operation directly so the SDK can complete
its single exact recovery lookup. It is never wrapped in the provider's
shorter generic action timeout and is never retried automatically.

Verification covers normalization conflicts, exact workspace/standalone/Live
dispatch, cross-context switching, capability absence, ambiguous Live runtime,
legacy callers, reconnect and reload, outcome recovery, target-scoped directory
warnings, supersession, and no-primary-fallback.

Audited implementation footprint: approximately 910 added production lines and
1,250 added test lines. Most production churn is the explicit routing,
transition, reconnect, and target-scoped error handling inside the existing
provider rather than new abstraction surface.

Exit criterion: the daemon React provider represents and switches all contexts
explicitly without changing visible WebShell entry points. PR6 owns global New
Chat, Recents, lifecycle controls, deep links, and project-control visibility.

### PR6: WebShell product UI

Expand Down Expand Up @@ -1278,15 +1305,15 @@ flowchart LR
PR1 --> PR2["PR2 standalone core"]
PR2 --> PR3["PR3 complete daemon API"]
PR3 --> PR4["PR4 SDK"]
PR4 --> PR5["PR5 WebUI context"]
T["PR #8882 transactional switching"] --> PR5
PR4 --> PR5["PR5 WebShell session context"]
S["PR #9129 loading-skeleton switching"] --> PR5
PR5 --> PR6["PR6 WebShell"]
```

PR0 through PR6 are the required feature sequence. PR5 builds on the final API
merged by PR #8882. PR #8874 (workspace uploads) and PR #8817 (fork/move
foundations) are follow-up dependencies rather than MVP blockers. No capability
is advertised before PR3.
PR0 through PR6 are the required feature sequence. PR5 builds on the
loading-skeleton switching model restored by PR #9129. PR #8874 (workspace
uploads) and PR #8817 (fork/move foundations) are follow-up dependencies rather
than MVP blockers. No capability is advertised before PR3.

Expected total implementation size is approximately 3,800-6,170 production
lines plus 7,600-11,800 test lines. The companion document is excluded from
Expand Down
86 changes: 86 additions & 0 deletions docs/plans/2026-08-28-standalone-pr5-webshell-context.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# Standalone PR5 WebShell Session Context Plan

## Goal

Make the daemon React provider represent and switch workspace, standalone, and Live sessions explicitly while keeping current WebShell entry points unchanged. The implementation starts from `main`, where WebShell consumes the provider through `@qwen-code/webui/daemon-react-sdk`. It does not copy the in-flight WebShell cutover; a later rename can carry the provider changes unchanged.

## Product Context Contract

```ts
type DaemonProductSessionContext =
| { kind: 'workspace'; cwd: string }
| { kind: 'standalone' }
| { kind: 'live' };
```

`sessionContext` is the product and routing authority. `connection.context` remains the existing model context-window snapshot. `connection.workspaceCwd` is set only for a product workspace; the internal Conversations runtime cwd is never exposed as a project.

Legacy callers may continue supplying `workspaceCwd`. The provider converts it once to a workspace context. An explicit workspace context must match a simultaneously supplied legacy cwd after path normalization. Standalone and Live contexts reject a supplied legacy cwd. If neither value is supplied, existing primary-workspace behavior is preserved.

## Restore and Create Dispatch

| Product context | Create | Load and resume | Ownership proof |
| --------------- | -------------------------------------- | ------------------------------------------------------------------ | ------------------------------------------------------------------------ |
| Workspace | Existing generic create with exact cwd | Existing generic load/resume with exact cwd | Exact ordinary runtime selected by the caller/provider |
| Standalone | `DaemonSessionClient.createStandalone` | `loadStandalone` / `resumeStandalone` | SDK runtime validation of the dedicated standalone response |
| Live | Not created by this provider | Generic load/resume against the one trusted `kind: 'live'` runtime | Capability runtime identity plus daemon-side persisted-source validation |

An explicit context never falls back to the primary workspace. A missing standalone capability, a missing/untrusted/ambiguous Live runtime, or a context/cwd conflict fails before a session request is sent.

## Switching Semantics

The provider retains the post-#9129 loading-skeleton model:

1. Capture a new transition generation and publish the target session id and context.
2. Detach the previous client, clear the old transcript, and show the target loading state.
3. Commit the restored client, replay, standalone working-directory state, and warnings only if the transition is still current.
4. On failure, keep the target id/context visible with the structured error. Do not restore the previous conversation.
5. Detach a stale successful client and discard its replay, warnings, and recovery state.

Reload, reconnect, live-journal repair, and invalid-client reattachment reuse the stored context. They never reconstruct context from an absent cwd.

## Standalone State

Successful standalone create/load stores the SDK-provided `projectlessOutputDirectory` and `workingDirectory` under a standalone-specific connection field. A `recreated` warning belongs only to that target and is discarded if another transition supersedes it.

`DaemonStandaloneCreationOutcomeUnknownError` is rethrown intact. When the create owns an empty connection, the generated UUID and exact-lookup recovery result are also recorded in connection state. A detached create beside an active session leaves that active connection untouched and carries recovery only on the structured error. The provider does not retry create. It does not wrap standalone create in the generic 30-second action timeout because that could reject before the SDK's required exact lookup completes.

Standalone directory error codes are copied from structured daemon error bodies into connection state so PR6 can present repair or terminal guidance without parsing strings.

## Workspace Isolation

For standalone and Live sessions, the provider skips session-less workspace providers, skills, ACP preheat, Git status, and workspace event invalidation. Session-scoped supported commands, context/model status, Goal state, transcript replay, prompts, permissions, and heartbeat remain available.

The current visible WebShell has no standalone entry point in PR5. PR6 must gate App-level workspace features before wiring Global New Chat and Recents to the new context. PR5 nevertheless exposes enough typed state for that gating without interpreting the internal runtime cwd as a workspace.

## Compatibility and Migration

- Existing Provider props and action calls remain valid.
- Workspace behavior and primary fallback remain unchanged only for callers without an explicit context.
- The public daemon React SDK exports the context and standalone state types.
- No new daemon route or SDK validator is added.
- The later WebShell provider cutover moves these files without architectural changes.
- Before publishing a WebShell package that directly requires the standalone SDK methods, align its SDK peer minimum with the first released SDK version containing PR4.

## Verification

Focused tests cover:

- context normalization and conflict rejection;
- exact route selection for workspace, standalone, and Live;
- missing standalone capability and zero/multiple/untrusted Live runtime failures with no fallback request;
- workspace to standalone to workspace/Live switching;
- rapid supersession, stale-client detach, and target-only replay/warnings;
- loading-skeleton failure semantics with no rollback;
- reload and reconnect using the stored context;
- outcome-unknown recovery states without create retry or outer timeout masking;
- recreated-directory state and structured directory error codes;
- no workspace providers, skills, Git, preheat, or event invalidation for non-workspace contexts;
- unchanged legacy workspace callers and controlled Provider transitions;
- public type exports and browser bundles.

Run the focused WebUI provider/action tests, WebUI build/typecheck/lint, WebShell provider integration tests, WebShell build/typecheck/lint/format, root build and typecheck, and the SDK/WebShell public-surface and browser-bundle checks.

## Scope Boundary

PR5 does not add Global New Chat, Recents, lifecycle menus, archive/delete/repair controls, deep links, standalone uploads, or project-control hiding. Those visible product flows remain PR6. It does not change daemon lifecycle behavior or recreate the reverted transactional session-switch coordinator.
4 changes: 4 additions & 0 deletions packages/webui/src/daemon-react-sdk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,10 @@ export type {
DaemonTokenUsage,
/** Model descriptor: id, display label, context window size. */
DaemonModelInfo,
/** Explicit workspace, standalone, or Live product session context. */
DaemonProductSessionContext,
/** Standalone working-directory and create-recovery state. */
DaemonStandaloneConnectionState,
/** Classified notice category for host-owned UI routing. */
DaemonNoticeCategory,
/** Fine-grained operation associated with a session notice. */
Expand Down
2 changes: 2 additions & 0 deletions packages/webui/src/daemon/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ export type {
DaemonConnectionState,
DaemonConnectionStatus,
DaemonModelInfo,
DaemonProductSessionContext,
DaemonStandaloneConnectionState,
DaemonNoticeCategory,
DaemonNoticeOperation,
DaemonNoticeSeverity,
Expand Down
Loading
Loading