mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-11-07 06:19:43 -10:00
32d22f950cf6f987eb8f06ac2722a02ca79fed2a
52
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
32d22f950c |
feat(managed-agent): support durable structured user questions (#16433)
Connect Hosted question actions, structured answers, durable continuation, and the WebShell question form. Cover canonical response replay and pending-question recovery across Harness and Java context restarts. Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
bc19325983 |
feat(web-shell): add host mount entries and align managed empty-state layout (#15928)
* feat(web-shell): 增加 managed-host 构建入口与托管会话页 为产品侧集成提供独立的 managed-host 产物(--mode managed-host)与托管会话页: 不挂 daemon workspace/session context,托管会话由 provider 直接渲染列表、会话与输入框。 包含:vite lib 构建模式、managed-host 渲染入口及测试、ManagedSessionsPage 托管分支与 测试、provider 类型调整、README 与中英文设计说明。本次不涉及宿主注入能力。 * feat(web-shell): 托管宿主可注入侧栏导航 props ManagedAgentWebShell 增加可选导航 props 并原样透传给 ManagedSessionsPage: navigationTarget / archivedNavigationTarget / onArchivedSessionsStatusChange / navigationActive / navigationSearch。 宿主据此决定是否把官方会话列表 portal 到自己的侧栏(navigationTarget 为 null 时 仍走 home 布局但不 portal),并同步搜索词与归档计数。类型复用 ManagedSessionsPage 现有 props,不新增重复定义;新增 2 个用例覆盖原样透传与未传时保持 undefined。 * feat(web-shell): add full-page app-host entry and align managed empty-state layout - Add app-host mount entry (renderWebShellApp) so a non-React host can render the complete WebShell (sidebar + welcome + composer) by injecting baseUrl/provider; no shell is hand-drawn and no backend address is hardcoded. - Center the managed empty state like the standalone chat (content shrinks so the chatPane centers it) instead of pinning the welcome to the top and composer to the bottom; gate the welcome header off when a failed session is selected. - Surface session failure and unconfirmed-submission notices together above the composer, dedupe the same message across channels, and style them as callouts. - A pending prompt owned by another session now offers a redirect to the owning conversation instead of retry/discard from a foreign session; reconcile tests. - Build app-host with global (unscoped) CSS so root-level state classes match. * test(web-shell): align managed status smoke assertions Co-authored-by: Qwen-Coder <[email protected]> * test(web-shell): reach the owning conversation before retrying a foreign pending A pending prompt owned by another conversation no longer offers retry in the foreign session: it renders "Open the original conversation" instead. The spec still clicked "Retry the same request" straight from the foreign session, so the two foreign variants timed out waiting for a button that is deliberately absent from this head while the owner variant passed. Follow the redirect first, then retry. Owner variant unchanged. Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmvwo8rnm58 * docs(web-shell): document the app-host entry and drop ignored e2e links The review on head |
||
|
|
4700dd2368 |
fix(web-shell): put the list-failure notice under the tabs, not above them (#16104)
* fix(web-shell): put the list-failure notice under the tabs, not above them #16082 merged with the notice as the nav's first child, so a failed session load rendered above the Sessions/Archived tab bar and pushed the list's own controls down - an error about the list appearing before the list's navigation. It now renders after the tabs and the title filter, in the region the sessions occupy. Also stops break-all from cutting the request id mid-token ('req' / 'uest-list-500'); break-words keeps it whole and wraps at a boundary. Observed in a real browser at 1280x800 against a 500 from /sessions/query: before: nav children = [alert y=52, tabs y=180, search y=212] after: nav children = [tabs y=52, search y=84, alert y=128] The file also carried two prettier violations from the squash merge; prettier --write normalises them, which is why the diff touches two unrelated indents. * style(web-shell): keep the diff to the move and the word break The previous commit ran a newer prettier than this repo pins, which reindented two unrelated call sites and does not satisfy the repo's own --check. Redone from the pristine file with the pinned prettier (3.6.1): the net diff against main is now only the block move and break-all -> break-words. --------- Co-authored-by: yiliang114 <[email protected]> |
||
|
|
9dde2f657d |
fix(web-shell): surface Managed session list failures (#16082)
* fix(web-shell): surface Managed session list failures Co-authored-by: Qwen-Coder <[email protected]> * test(web-shell): match the list-failure sentences, not the whole alert The list-failure alert wraps its message and its Retry button in the same role=alert div, so textContent reads 'Managed sessions could not be loaded.Retry loading sessions'. Two assertions compared that concatenation against the sentence alone, which made four cases fail with a message that looked identical on both sides. Assert the sentence is present, the way the sibling HTTP-500 case already does, and keep the alert count so the 'once' property is still pinned. Not verified locally: this checkout's core dist predates managed-runtime/managed-permission-rules.js, so the suite cannot load here. CI is the first real run. --------- Co-authored-by: Qwen-Coder <[email protected]> Co-authored-by: yiliang114 <[email protected]> |
||
|
|
4afe1a28fd |
feat(managed): add independent worktrees for main sessions (#15745)
* feat(managed): add independent worktrees for main sessions * fix(managed): reconcile session worktree contract guards Join worktree descriptors into session pages while preserving the existing query budgets. Keep schema, CSI inventory, surface captures and lock-order witnesses consistent with the new feature, and memoize cleanup blockers and maintenance operation kinds. Co-authored-by: Qwen-Coder <[email protected]> * fix(managed): require initial worktree checkout completion Record checkout completion only after Git succeeds and refuse reentry or verification without the matching durable receipt. Preserve incomplete directories and user edits. Co-authored-by: Qwen-Coder <[email protected]> * fix(managed): allow shared local project imports Keep registration metadata private to each local installation so existing projects can be imported without taking over the parent marker. Refuse operations that require exclusive authority over shared storage. Co-authored-by: Qwen-Coder <[email protected]> * feat(web-shell): streamline managed worktree controls Show the actual Git branch and use a one-click worktree choice that creates isolation on the first message. Keep ready worktree metadata in session details and match Local sidebar markers while preserving frozen retries and preparation feedback. Co-authored-by: Qwen-Coder <[email protected]> * fix(managed): address worktree lifecycle regressions Keep cancellation available before worktree readiness, bind persistence to the Managed catalog, use maintenance-safe cleanup IDs and isolate Git scan scheduling. Refuse new checkouts nested under their source repository while retaining existing cleanup, and repair the affected CI fixtures. Co-authored-by: Qwen-Coder <[email protected]> --------- Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
72834cb425 | fix(managed-agent)!: simplify Web Shell controls and retire cwd submissions (#15616) | ||
|
|
06d82d786a | feat(managed-agent): support Workspace file uploads (#15586) | ||
|
|
c1d37384b5 |
fix(managed): persist session Todo and project committed task lists (#15155)
* feat(serve): give managed workspace sessions web_fetch and todo_write (#15076) A Managed Workspace Session offered six tools, so a session that needed to read a changelog or an issue page had to run curl through run_shell_command and ask for approval every time, and a long multi-step task had no plan of record. qwen serve sessions already had both tools; the Managed path never declared them. The two tools join through new hosted-workspace-files/3 and hosted-workspace-shell/3 profiles rather than widened /1 or /2 ones. Admission rests on what a Session's own profile offered, so widening /2 would make a saved Session expose tools its earlier turns never had; this follows the glob precedent, which added /2 for the same reason. web_fetch runs in the Worker on the same outbound channel run_shell_command already uses, and its managed mode keeps the fetch to one transfer: no response cache, binary payloads refused instead of written to the Runtime's disk, and no fast-model side query, which is a second model call the Session's own accounting never sees. todo_write is Worker-local bookkeeping the model reads back, with no Session Store schema change and no client projection yet. Admission validates both tools' arguments before the Runtime is acquired, so a malformed call costs no durable work. Each approval mode pre-approves todo_write, which only writes its own list, and asks about web_fetch, which reaches a URL nobody in the Session chose; the Java action preview now carries both tools' input, so the URL is visible before the operator answers. Resolves #15076 * fix(managed-agent): narrow Hosted tool profiles to Todo Co-authored-by: Qwen-Coder <[email protected]> * fix(serve): narrow the managed tool profiles to todo_write (#15076) A maintainer ruling on the issue defers native web fetch, so this branch delivers the task-list half and drops the fetch half rather than carrying a contested network-policy decision inside a tool-profile change. Dropping the fetch is also the technical answer for a files-profile Session: it has no shell, so a fetch there would open an outbound path instead of reusing one, and nothing in the Hosted path constrains the destination — admission checks the scheme, credentials and length but not the host, and under yolo neither the Worker nor the Harness asks, which would let a Session reach link-local and metadata endpoints unattended. The core web-fetch source is back to its pre-PR bytes. Three review findings are fixed in the surviving half. The Worker now opts in to todoWriteEnabled explicitly, which core defaults off and the operator-facing path gates registration on, instead of registering a tool whose enablement flag says no. The plan-mode read set for the /3 profiles is gone: it was unreachable because the constructor admits public plan mode only for files/1, shell/1 and shell/2. And the task list no longer claims durability it does not have — the Worker's tool set is keyed by a runtime session id the Harness derives per turn, so the list is scoped to one turn, and the reminder a local Session shows is injected by the model loop in the Harness, which never sees the Worker's Config. The design docs record the delivered slice, why the fetch half is held, and the remaining work: a destination policy before fetch can return, a Harness-supplied stable session identity for a list that survives a turn, Java profile predicates for /3, and a client projection. Refs #15076 * feat(managed): persist Todo state in the Hosted Session Co-authored-by: Qwen-Coder <[email protected]> * fix(managed): pass the bare hex contentDigest for the Todo command writeHostedTodos built `sha256:${managedToolDigest(input)}`, but assertManagedSessionDigest accepts only /^[0-9a-f]{64}$/, so every todo_write commit was refused as managed_session_invalid_record and the turn surfaced it as "Hosted tool turn requires recovery; its original work was not released." The `sha256:` prefix belongs to the argsDigest family; every other commitDomainRecord caller passes bare hex. Verified locally on this head: hosted-workspace-tool-turn.test.ts went from 21 failed / 377 passed to 20 failed / 378 passed, eliminating exactly "persists Todo without a Worker and restores the latest state after reopen" and adding nothing. The remaining 20 all carry the single message "(0 , unescape) is not a function", this box's known minimatch baseline. No CI lane covers this branch, because ci.yml's pull_request filter is main/release/**/omni-experiment and the base here is feat/15076-managed-web-fetch-todo-write. Co-authored-by: Qwen-Coder <[email protected]> * test(managed): reconcile recovery coverage for Todo profiles * fix(serve): keep todo_write out of the provider worker lane The previous commit registered `todo_write` in both Worker tool sets. The provider lane is not the lane the hosted profiles use: it serves the resident and CSI sessions, whose manifests are a closed set of read, write and edit (plus shell), and the `/2` change that added `glob` left it untouched for exactly that reason. Registering the tool there widened every one of those sessions and broke 32 assertions across the resident manifest and the provider worker suites. Only the executor lane keeps the tool, which is the lane `createManagedToolSet` builds for a hosted `/3` session. The provider worker returns to its pre-change bytes, and both design documents now say which lane opts in. Refs #15076 * chore(serve): tighten the todo_write slice after an audit round No behaviour change. Four audit passes over the PR diff found seven wording and precision defects, each fixed here: - The `/3` profile comment still said the version adds `web_fetch` and `todo_write`; the fetch half is not delivered. - The approval comment implied a fetch tool exists in a profile and merely asks, rather than being absent. - The admission comment named "a blocked item's own state" as a deferred rule. The core tool has no such rule, and it does defer duplicate ids and duplicate `blockedBy` references, which the comment now states. - A single-case `it.each` became the plain test it is. - The approval test name read "never under yolo alone", which describes the opposite of what it asserts. - My Worker test block had been inserted between an unrelated comment and the `relativizeGlobText` describe it documents. - Both design documents described the removed fetch admission in the present tense, and neither said the `todoWriteEnabled` opt-in has no reader inside the Worker. Mutation checks on the fixed tree: forcing `declaresTodoTool()` false fails 11 cases, dropping the admission branch fails 9, dropping the pre-approval fails 1, and dropping `TodoWriteTool` from `ADMITTED_TOOL_NAMES` fails 1. Dropping `todoWriteEnabled` fails nothing, which is why the documents now record that the flag states intent rather than switching behaviour. Refs #15076 * fix(managed): close the two Critical gaps the audit round found on the Todo slice R1-2 -- packages/core/src/managed-runtime/managed-session-records.ts `todo_state` was added to MANAGED_SESSION_ENABLED_DOMAINS but not to MANAGED_SESSION_ENVELOPE_DOMAINS, and no Stage H body is registered for it (MANAGED_EXTENSION_RECORD_BODIES has no `todo_state` key). That broke the partition invariant: managed-extension-projection.test.ts > "partitions the enabled domains between the bodies and the envelope list" filters the enabled names by "has no body" and compares the result with toEqual against the envelope list, so the extra name made it deterministically red. `todo_state` commits through the envelope path -- hosted-todo-session.ts:38 calls session.authority.commitDomainRecord with { domain: 'todo_state' } -- which is exactly what that list documents, so the name belongs there. It is inserted at index 1 (after `goal_state`) rather than appended because the witness compares against MANAGED_SESSION_ENABLED_DOMAINS order, where `todo_state` follows `goal_state`. The envelope entry is only valid while no body is registered for the domain: managed-extension-projection.ts refuses to load over an envelope/body collision. Reproduced red before the change (1 failed | 169 skipped of 170, AssertionError: expected [ 'goal_state', 'todo_state', ...(3) ] to deeply equal [ 'goal_state', ...(3) ]), green after (170 passed of 170), and removing the entry reproduces the same AssertionError. R1-1 -- packages/cli/src/serve/hosted-workspace-tool-turn.ts The `/3` widening reached hostedApprovalForTurn and both branch gates in registerHostedHarnessSessionRoutes, including the one that admits a `planMode` selection, but not the HostedWorkspaceToolTurn constructor, which still listed only files/1, shell/1 and shell/2. A `hosted-workspace-files/3` Session with a Plan-mode selection therefore passed the boundary and then threw Error('Public Plan mode is unavailable for this Session.') at turn setup, where before the widening the same selection was refused cleanly with 400 invalid_hosted_approval_selection. The constructor now admits the same five profiles the boundary does. Widening alone would ship a contradictory round: declarations() filters the advertised set to exit_plan_mode plus HOSTED_PUBLIC_PLAN_READS (read_file/glob/skill) while planning, so todo_write is withheld, but todoInstructions() was gated only on declaresTodoTool() and would still inject "Keep it current with todo_write". Because the batch-wide refusal converts every call in a batch to an error response once any request carries a validationError, that instruction invites a call whose refusal also discards the legitimate reads issued beside it. todoInstructions() is now gated on !this.planning as well. HOSTED_PUBLIC_PLAN_READS is unchanged: todo_write writes, so it does not join the public plan-mode read set. The new test constructs a files/3 and a shell/3 turn with publicPlanMode, and asserts the constructor does not throw, todo_write is absent from declarations(), todoInstructions() is empty over seeded durable state, a todo_write call returns the Plan refusal, no Broker work is spent and the seeded list is still the latest afterwards. Both halves are pinned by a mutant: removing the added profiles reproduces the constructor throw (2 failed), and removing the planning gate reproduces the contradictory instruction (2 failed, "expected '# Current Session todo list...' to be ''"). Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmvk1uqs6qi * fix(web-shell): drop the dead execution_status guard and pin the flat blockedBy fallback Three review-driven fixes plus one merge regression caught by the core build: R1-18: the execution_status pre-filter in getLatestSuccessfulTodos was unreachable (tool.status already derives from the same field), so it could never be the deciding reject; delete it and let the completed/cancelled gate stand alone. R1-17: session-workflow.spec.ts claimed top-level blockedBy silently produces no edges, but todos.ts:43 now falls back to it; rewrite the comment to state _meta.qwenTodo wins when both are present. R1-16: every blockedBy fixture nested under _meta.qwenTodo, so the only path that renders Hosted dependency edges was untested; add flat-{blockedBy} and _meta-wins cases. The PR's move of the blockedBy-preservation block into preserveTodoDependencies also dropped the execute-scope oldTodosMap declaration, which core's TodoCompleted and TodoResultApplication hooks still read; restore it. * test(cli): drop unused hostedTodoInstructions import and reformat The R1-18 fix deleted the dead execution_status guard but left the now unused symbol in the import list, which fails tsc (TS6133) during the CI prepare step and therefore every TypeScript lane. Also rewrap the two assertions the same commit left over the 80-column limit. --------- Co-authored-by: wenshao <[email protected]> Co-authored-by: Shaojin Wen <[email protected]> Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
a4ce90270e | feat(managed): add durable mid-turn user message queue (#15449) | ||
|
|
08a06a46bd |
fix(web-shell): Prevent duplicate Managed composers (#15496)
Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
0d7518f0a2 | feat(managed): add session permission rules and auto approval (#15448) | ||
|
|
ab4b4920ee |
fix(web-shell): align Managed sidebar with daemon navigation (#15331)
Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
69df02bb9a |
fix(web-shell): preserve draft attachments through refresh and allocation (#15303)
Fix two correctness issues left by PR #15025 after its merge into main: Managed summary reloads could delete unsent attachment archives, and session allocation could retain an already accepted draft after removing a submitted attachment or normalizing touch references. Keep same-session capability knowledge during reload, reuse the existing removal-tolerant acceptance predicate for migration, and preserve reference identity when there is nothing to hoist. Keep strict host continuation checks and protection for newer input. Add focused regression coverage and synchronize the bilingual design. Co-authored-by: 钉萁 <[email protected]> |
||
|
|
f0a47611ab |
refactor(managed-agent): Remove pre-release compatibility paths (#15263)
* refactor(managed-agent): Simplify first-release contracts Remove unreleased compatibility paths across the managed runtime, Java control plane and WebShell. Keep one fresh V1 baseline and document that schema versions stay at V1 until the first formal release. Co-authored-by: Qwen-Coder <[email protected]> * test(managed-agent): Align remaining tool fixture with current contract Restore schema rationale comments while retaining the V1 baseline. Co-authored-by: Qwen-Coder <[email protected]> * fix(managed-agent): Align CSI proof with current checkpoint encoding Require the three current nullable checkpoint fields in the native proof, keep malformed and missing fields rejected, and synchronize retained fixture bytes and digest chains. Update tests that still expected retired pre-release state and replay compatibility. Co-authored-by: Qwen-Coder <[email protected]> --------- Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
f18decc7cc |
fix(web-shell): preserve composer draft attachments across views (#15025)
* fix(web-shell): preserve composer draft attachments across views * fix(web-shell): preserve draft edits during restore and acceptance * test: stabilize durable commit and standalone runtime checks * revert: drop unrelated core extension test change --------- Co-authored-by: 钉萁 <[email protected]> |
||
|
|
49f1f623c2 |
feat(managed-agent): Enable public subagent delegation by default (#15171)
Expose qualified public child Sessions with immutable launch policy, aggregated approvals, prompt-scoped stopping and recovery, and WebShell task and transcript views. Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
4ee2bb4e47 |
feat(managed-agent): manage MCP servers without recreating sessions (#15119)
* feat(managed-agent): compose Shell Sessions with pinned MCP servers * feat(managed): manage MCP servers within existing sessions |
||
|
|
9a83f1080e |
feat(managed-agent): add filtered session title queries (#15157)
* feat(managed-agent): Add session lifecycle UI and reading exports Expose durable lifecycle actions and archive navigation in Managed Web Shell, with identity-scoped browser recovery and immutable HTML/Markdown reading exports. Co-authored-by: Qwen-Coder <[email protected]> * fix(managed-agent): Resolve lifecycle review and smoke regressions Use the host React DOM server renderer, announce global errors once, and advance the OpenAPI version. Keep smoke selectors specific to session navigation and align the Workspace expectation with its existing Managed label. Co-authored-by: Qwen-Coder <[email protected]> * test(managed-agent): Preserve schema bindings in channel race fixture Reuse the production schema-aware JDBC template so repeated H2 queries keep binary identity parameters. Preserve the existing ownership race and its assertions. Co-authored-by: Qwen-Coder <[email protected]> * fix(managed-agent): Correct lifecycle retry and export handling Keep failed unadmitted archive intents idle until explicit retry, scope approval capability to each session profile, and preserve public export semantics and embedded downloads. Co-authored-by: Qwen-Coder <[email protected]> * fix(managed-agent): Clarify Markdown export format boundaries Explain original Markdown source and offline HTML in the shared localized export menu and synchronized designs. Preserve raw Markdown syntax in the export contract regression. Co-authored-by: Qwen-Coder <[email protected]> * fix(core): Remove duplicate history helper import Co-authored-by: Qwen-Coder <[email protected]> * feat(managed-agent): add filtered session title queries Co-authored-by: Qwen-Coder <[email protected]> * feat(web-shell): complete managed session title search Co-authored-by: Qwen-Coder <[email protected]> * test(ci): Observe lock announcement before blocking Co-authored-by: Qwen-Coder <[email protected]> * fix(managed-agent): align title query whitespace semantics Co-authored-by: Qwen-Coder <[email protected]> * test(web-shell): Disambiguate recovery session navigation Anchor recovery session selectors so lifecycle action buttons do not match the session row. Co-authored-by: Qwen-Coder <[email protected]> * chore(api): preserve source layout in merged contract Keep unchanged OpenAPI values byte-for-byte from the source branches without changing the merged contract. Co-authored-by: Qwen-Coder <[email protected]> --------- Co-authored-by: jinye.djy <[email protected]> Co-authored-by: Qwen-Coder <[email protected]> Co-authored-by: jinye <[email protected]> |
||
|
|
7773fa313a |
feat(managed-agent): Page checkpoint history and improve session feedback (#15170)
* feat(managed): Page durable checkpoint history Preserve complete logical receipts through bounded immutable history pages and shared SQL/native recovery validation. Co-authored-by: Qwen-Coder <[email protected]> * fix(managed): Restore workspace history maintenance Co-authored-by: Qwen-Coder <[email protected]> * fix(managed): Explain model API turn failures Co-authored-by: Qwen-Coder <[email protected]> * fix(web-shell): Show running status for managed sessions Reuse daemon sidebar indicators and retain confirmed session summaries across navigation and delayed catalog responses. Refresh known running background sessions until they settle. Co-authored-by: Qwen-Coder <[email protected]> * fix(web-shell): Remove persistent managed composer hints Keep loading, error and confirmation feedback while hiding routine model and approval explanations below the composer. Co-authored-by: Qwen-Coder <[email protected]> * docs(managed-agent): Clarify paging rollout and sidebar evidence Co-authored-by: Qwen-Coder <[email protected]> * test(managed-agent): Align packaged model failure expectations Co-authored-by: Qwen-Coder <[email protected]> --------- Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
51f1d2c8bb |
fix(managed): Align checkpoint limits and expose recovery blocks (#15094)
Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
605fd5ee17 |
feat(managed-agent): Add session lifecycle UI and reading exports (#15100)
* feat(managed-agent): Add session lifecycle UI and reading exports Expose durable lifecycle actions and archive navigation in Managed Web Shell, with identity-scoped browser recovery and immutable HTML/Markdown reading exports. Co-authored-by: Qwen-Coder <[email protected]> * fix(managed-agent): Resolve lifecycle review and smoke regressions Use the host React DOM server renderer, announce global errors once, and advance the OpenAPI version. Keep smoke selectors specific to session navigation and align the Workspace expectation with its existing Managed label. Co-authored-by: Qwen-Coder <[email protected]> * test(managed-agent): Preserve schema bindings in channel race fixture Reuse the production schema-aware JDBC template so repeated H2 queries keep binary identity parameters. Preserve the existing ownership race and its assertions. Co-authored-by: Qwen-Coder <[email protected]> * fix(managed-agent): Correct lifecycle retry and export handling Keep failed unadmitted archive intents idle until explicit retry, scope approval capability to each session profile, and preserve public export semantics and embedded downloads. Co-authored-by: Qwen-Coder <[email protected]> --------- Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
59d21e1c69 |
feat(managed-agent): Safely settle recovery and continue sessions (#15131)
* feat(managed-agent): Safely settle recovery and continue sessions Require original-input settlement, continuation readiness and completed cleanup before reopening a Managed Session. Add conservative recovery queries and rechecks, original-target cancellation, reload-safe operation tracking and editable bounded handoff drafts. Co-authored-by: Qwen-Coder <[email protected]> * test(managed-agent): Avoid initial fixture output backpressure Capture the native generator output in a JUnit temporary file so waiting for its exit cannot fill an unread pipe. Preserve the 30-second deadline and all recovery assertions, and reap the generator when an assertion or interruption ends the wait. Co-authored-by: Qwen-Coder <[email protected]> --------- Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
d03b3116da |
feat(managed-agent): Add durable manual context compression (#15104)
* feat(managed-agent): Add durable manual context compression Add idle-only public and WebShell compression operations with fenced native maintenance, atomic journal results and compressed model-history recovery. Preserve the raw transcript, pending input and tool results across retries. Co-authored-by: Qwen-Coder <[email protected]> * test(managed-agent): Align assertions and pin compression Hook recovery Match the permission tooltip and the existing Managed Workspace label while preserving authorization and registration assertions. Verify that an unknown Hook outcome retains the durable generating phase and recovery barrier. Co-authored-by: Qwen-Coder <[email protected]> --------- Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
bca0cf80b9 |
feat(web-shell): unify daemon and managed workspace navigation (#15061)
* feat(web-shell): unify daemon and managed workspace navigation Extract shared workspace navigation, task target selection and execution choice from the stacked integration so hosts can use them with the existing Managed provider on main. Co-authored-by: Qwen-Coder <[email protected]> * fix(web-shell): keep workspace directories stable during refresh Co-authored-by: Qwen-Coder <[email protected]> --------- Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
ed0d824e8c |
feat(web-shell): support managed session rename (#14987)
Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
d2f2895fab |
feat(managed-agent): import enabled skills at session creation (#14930)
* feat(managed-agent): import enabled Skills when creating sessions * fix(managed-agent): bump contract version for session skills * codex: address PR review feedback (#14930) * codex: address PR review feedback (#14930) * codex: address PR review feedback (#14930) --------- Co-authored-by: qwen-code-dev-bot <[email protected]> |
||
|
|
827bf1b76e |
fix(web-shell): align Managed Session titles, file access and the Workspace row (#14924)
* fix(web-shell): derive the title on Workspace-bound Session create The bound create path omitted the title, so the Session list and header fell back to a UUID prefix while the standard shell shows a prompt-derived title. Send the same bounded, control-character-stripped title the unbound create already uses. * fix(web-shell): treat unsupported Workspace file access as a capability A 409 workspace_files_unsupported from the mount-time probe is a deployment capability, not a transient failure: the composer simply has no file-reference actions. Stop storing the broker's English message and rendering it as a permanent alert; other probe failures still surface. * fix(web-shell): compact the bound Workspace row The home layout printed the workspace id and relative directory twice. Keep one summary line (id truncated in place with the full value in the title, then cwd, model and runtime) and leave only the cwd switch and the execution-unavailable notice in the body. The standalone layout keeps its labeled lines. * test(web-shell): pin the compact Workspace row * fix(web-shell): surface file-access refusals after the mount probe Only the mount probe treats workspace_files_unsupported as an absent capability; the same code also covers a broker that failed mid-request, so a refusal from a later read must still report. Clear the probe's failure instead of guarding the shared sink. * fix(web-shell): slice the Managed Session title cap by code point titleFor capped at 80 UTF-16 code units, so a prompt whose astral character started at code-unit index 76 was cut between its surrogates. Sending a title from the bound create made that cap reachable for bound Sessions: the server accepts a lone surrogate (validTitle rejects only length > 256 and control characters) and no store column keeps it, so the prompt tail was persisted as '?'. Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmvhb9cysmc * refactor(web-shell): reuse object() in the file-access guard isFileAccessUnsupported hand-rolled the unknown-to-record narrowing that object() already provides in this module, and dropped its array exclusion. Both forms answer identically for every value the provider can throw, so this only removes the second idiom. Also pin that the suppression keys on the error code rather than the 409 status. The same mount probe answers 409 workspace_file_unavailable for a fenced or draining Workspace, and that must still surface; nothing distinguished the two, because the only negative case varied status and code together. Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmvhb9cysmc * test(web-shell): pin that the compact summary stays out of standalone The standalone-layout case asserted only what that layout gains, so removing the home && guard on the summary kept the suite green while duplicating the compact row above the two labeled lines, inside a plain div. The home-layout case already asserts the summary exists. Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmvhb9cysmc --------- Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
26aaa35afe |
feat(managed-agent): Support approval changes during a turn (#14920)
* fix(web-shell): Select a named model for managed tasks Remove the redundant server-default entry when model presets are available and submit the displayed preset Agent. Co-authored-by: Qwen-Coder <[email protected]> * feat(managed-agent): Support approval changes during a turn Read the durable session policy at subsequent permission boundaries and release eligible pending actions through the existing decision journal. Keep Plan controls, admission snapshots, writer fencing, and legacy provider behavior intact. Co-authored-by: Qwen-Coder <[email protected]> --------- Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
888d80815b | feat(managed-agent): expose session MCP catalog in WebShell (#14916) | ||
|
|
d55aa8a3e8 |
fix(managed-agent): load saved tool details on expansion (#14604)
* fix(managed-agent): show bounded tool details in session history * fix(managed-agent): load saved tool details on expansion * fix(managed-agent): advance tool detail contract version * fix(managed-agent): remove tool detail feature switch * fix(managed-agent): preserve saved structured shell results |
||
|
|
fb8e9a4512 |
perf(web-shell): incrementally project managed session messages (#14562)
* perf(web-shell): incrementally project managed session messages Reuse trusted append prefixes while rebuilding replacement, prepend and sibling histories and preserving independent output snapshots. Co-authored-by: Qwen-Coder <[email protected]> * test(web-shell): pin the projector's unregistered-array rebuild The nextEpoch !== undefined guard in createManagedMessageProjector keeps two consecutive raw transcript arrays (equal length, no merge lineage) from being treated as one append chain: without it the projector reuses the first projection and shows stale text. use-managed-session's snapshot path stores transcript.events directly, so the second read is exactly such an unregistered array. No existing test failed when the guard was removed, so this pins it with the smallest failing-first regression: two raw arrays, same length, different text, the second content must win. --------- Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
084078b0d2 | feat(managed-agent): raise attachment input budget to 8 MiB (#14628) | ||
|
|
3e9d22f14b | feat(managed-agent): support workspace file references (#14639) | ||
|
|
0df419d898 | feat(managed-agent): load pinned Skills from the composer (#14602) | ||
|
|
97ebffaa70 | feat(managed-agent): support Plan mode and inline attachments (#14531) | ||
|
|
ea6d67390a |
feat(managed-agent): Improve workspace selection and tool progress (#14405)
* feat(managed-agent): Improve workspace selection and tool progress Allow selecting existing authorized Linux project directories, expose safe tool lifecycle progress, honor the configured model-round limit, and reuse daemon WebShell presentation. Co-authored-by: Qwen-Coder <[email protected]> * fix(managed): Allow late model rounds to persist and recover Remove obsolete round ceilings from persistence and continuation validation. Cover late rounds with recovery regressions and complete real-model prompt evidence. Co-authored-by: Qwen-Coder <[email protected]> * test(managed-agent): Order workspace contract imports Co-authored-by: Qwen-Coder <[email protected]> * fix(managed-agent): Correct directory truncation reporting Count an omitted eligible directory before marking the page truncated while preserving the raw-entry scan bound. Pin the unsafe-selection refusal codes and add directory limit regression coverage. Co-authored-by: Qwen-Coder <[email protected]> --------- Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
af9bd8ed05 |
feat(managed-agent): add Shell Full Access and session approval switching (#14323)
* feat(web-shell): integrate Managed session workflow * docs(managed-agent): align model-switch acceptance translations * fix(managed-agent): reconcile WebShell integration checks * fix(managed-agent): address WebShell review feedback * fix(managed-agent): align schema projections and CI probes * feat(managed-agent): add Shell Full Access and approval switching Freeze approval selection per Turn, preserve legacy retry identity, and allow owner-authorized idle policy changes with durable WebShell confirmation. Co-authored-by: Qwen-Coder <[email protected]> * feat(web-shell): integrate Managed session workflow * docs(managed-agent): align model-switch acceptance translations * fix(managed-agent): reconcile WebShell integration checks * fix(managed-agent): address WebShell review feedback * fix(managed-agent): align schema projections and CI probes * feat(web-shell): integrate Managed session workflow * docs(managed-agent): align model-switch acceptance translations * fix(managed-agent): reconcile WebShell integration checks * fix(managed-agent): address WebShell review feedback * fix(managed-agent): align schema projections and CI probes * fix(web-shell): scope Local approval overlays to the active backend * test(web-shell): refresh transcript size evidence after main sync * feat(web-shell): integrate Managed session workflow * docs(managed-agent): align model-switch acceptance translations * fix(managed-agent): reconcile WebShell integration checks * fix(managed-agent): address WebShell review feedback * fix(managed-agent): align schema projections and CI probes * fix(web-shell): scope Local approval overlays to the active backend * test(web-shell): refresh transcript size evidence after main sync --------- Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
f9df3df1ef |
feat(web-shell): integrate Managed sessions into the home workflow (#14316)
* feat(web-shell): integrate Managed session workflow * docs(managed-agent): align model-switch acceptance translations * fix(managed-agent): reconcile WebShell integration checks * fix(managed-agent): address WebShell review feedback * fix(managed-agent): align schema projections and CI probes |
||
|
|
72316e6608 |
feat(web-shell): Reuse standard UI for Managed Agents (#13973)
* feat(web-shell): Reuse standard UI for Managed Agents Reuse native chat, model and Workspace controls for Java Managed Agent sessions while preserving admission retries and scoped drafts. Co-authored-by: Qwen-Coder <[email protected]> * fix(web-shell): Clear accepted drafts after editor remount Keep accepted input ownership across session switches and response-loss retries. Snapshot original editor text in the local pending record so a reload can clear matching owner storage without an in-memory callback. Preserve original commits, foreign drafts and newer different drafts. Co-authored-by: Qwen-Coder <[email protected]> * fix(web-shell): Clarify abandoned first-input admission Warn that first input may already be running and a new request can repeat its effects. Refresh the owning Session list after local abandonment and show uncertain recovery controls only once the active request settles. Add the focused review regressions for composer input and standalone UI. Co-authored-by: Qwen-Coder <[email protected]> --------- Co-authored-by: Qwen-Coder <[email protected]> Co-authored-by: Shaojin Wen <[email protected]> |
||
|
|
b675c97461 |
feat(web-shell): add Managed child Agent task panel (#13936)
* feat(web-shell): add Managed child Agent task panel Co-authored-by: Qwen-Coder <[email protected]> * docs(web-shell): record child task panel acceptance evidence Co-authored-by: Qwen-Coder <[email protected]> * fix(web-shell): keep task panel copy out of transcript exports Co-authored-by: Qwen-Coder <[email protected]> * fix(web-shell): synchronize task cancellation settlement Publish validated task snapshots before clearing cancellation recovery, keep known terminal state against delayed reads, and clear obsolete admission contention once the task ends. Keep background task polling quiet and align detail timestamps and labels with the panel language. Co-authored-by: Qwen-Coder <[email protected]> --------- Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
6936c77e60 |
feat(managed-agent): support WebShell session cwd changes (#13760)
* feat(web-shell): support managed session cwd changes Co-authored-by: Qwen-Coder <[email protected]> * feat(managed-agent): advertise WebShell cwd change capability Co-authored-by: Qwen-Coder <[email protected]> * chore(managed-agent): consolidate W2 WebShell delivery Co-authored-by: Qwen-Coder <[email protected]> * fix(web-shell): compose abort signals without AbortSignal.any below Chrome 116 refreshSummary composed the caller signal with the hook lifetime via an unconditional AbortSignal.any, which is Chrome 116 / Safari 17.4 while the package floor is Chrome 111 / Safari 16.4. On those browsers a successful cwd change threw TypeError on the completion readback, was booked as unconfirmed, and left the composer locked for the lifetime of the tab. Mirror the composeAbortSignals feature-detect from acpTransportUtils and rebase onto main: the cwdChange capability becomes OpenAPI v1.36 (v1.34 is reserved for actor roles, v1.35 landed first). * refactor(managed-agent): address triage nits - managed.cwd.unconfirmedForbidden now tells the user sending stays blocked until the server settles the operation or a new tab is opened. - cwdShaped collects to HashSet explicitly since retainAll mutates it; Collectors.toSet makes no mutability promise. - Drop the never-read cwd field from ManagedSessionCwdControl basis state. * fix(web-shell): repair CI after rebase Prettier rejected the conflict-resolved OpenAPI JSON (the closing brace stayed glued to the description line), and the transcript entry grew past its byte ceiling: main measures 1,320,579 and this branch 1,323,120 against a 1,323,000 gate. The growth is the cwd slice's shared i18n strings; no interactive cwd code enters the transcript graph. Raise the ceiling to 1,324,000 with the measurements recorded, matching the gate's documented ratchet practice. * chore(managed-agent): renumber cwd capability to OpenAPI v1.37 Main took v1.36 for the replay-floor pass (#13621) while this branch was in review, so the cwdChange capability moves to v1.37 and the design docs follow. Also drop the pre-rebase SHA from the size-gate docblock; the measurement is unchanged at 1,323,120 on the rebased branch. * feat(managed-agent): converge cwd capability on the #13545 role model Main's #13545 moved cwd admission from creator identity to atLeast(OPERATOR) while this branch was in review. The capability predicate now mirrors exactly that: one role grant read covers the workspaces of the cwd- and Turn-shaped candidate union, the execution-facts read covers the union of both id sets, and the facts batch is skipped when the caller holds no OPERATOR grant — the same zero-query shape the submit path has. The OpenAPI entry becomes v1.38 (main took v1.37 for the roles slice). Capability tests move to the role vocabulary: a non-creator OPERATOR now sees cwdChange while a READER does not, and the no-facts-read cases run under a READER-only caller. Design docs record the convergence. * fix(managed-agent): drop duplicate registry-facts interface declarations Textual auto-merge with #13545 concatenated this branch's fail-closed default methods with main's abstract declarations of the same two AgentStateStore methods, breaking compilation. Main's abstract declarations win: ManagedAgentStore is the only implementation and overrides both. The design docs' fail-closed-default note goes with the removed methods. * fix(web-shell): release a parked cwd intent on definitive replay refusals A lost-ACK intent has no operationId, so Confirm is the only way out — but definitive was gated on initialSubmission, so a confirm replay could never classify a refusal as final and the composer stayed locked for the tab's lifetime. The store's actor-scoped idempotency lookup runs before the role, revision and busy gates, so a 4xx behind it proves the original submit was never recorded. Drop the initialSubmission conjunct and release with the real refusal code; 401 and the pre-lookup read-visibility 404 stay non-definitive (they prove nothing about recording), as do transient 408/429. The query path starts accepted=true, so transient mid-poll refusals still retain an admitted operation's intent. R1-12. * docs(managed-agent): align the cwdChange contract text with the role model The field description still said the caller must be the Session creator, contradicting the role-based predicate shipped in the same diff; the WebShell design docs still recorded #13545 as open with a pre-rebase baseline. Both now match the converged role model, and the generated client is refreshed. The composeSignals fallback test also pins the hook-lifetime half of the composition, not just the caller signal. R1-2, R1-3, R1-22. * chore(managed-agent): renumber cwd capability to OpenAPI v1.39 Main took v1.38 for the automation runtime (#13598); the cwdChange entry moves to v1.39 and the design docs follow. Transcript entry measures 1,323,162 against the 1,324,000 gate on the rebased branch. * codex: address PR review feedback (#13760) * codex: address PR review feedback (#13760) --------- Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
e9582993bf |
fix(managed-agent): harden the managed panel failure lifecycle and worker path containment (#13179)
* fix(managed-agent): harden commit retry, worker containment, panel polling Managed sessions fail-closed on journal write errors, but the plain transactions commit tolerated no uncertainty: one dropped receipt (timeout or 5xx) parked the session authority in a permanent write failure. The broker already dedupes commits by command key and replays their receipts, so retrying uncertain failures is safe; do it on both commit paths with one shared predicate. In the managed runtime worker, a relative file path was resolved against the session directory without verifying containment, while the shell directory argument already required the same admitted-workspace check. Reject paths that resolve outside the registered workspace. The managed panel ran three independent fixed-3000ms loops (bootstrap snapshot, event-stream reconnect, summary poll) with no ceiling, no jitter, and no stop for definite 4xx answers. Give them a shared backoff (3s floor, 30s cap, jittered) and stop on non-retryable client errors. Merging streamed events rebuilt and re-sorted the whole transcript per delta; append strictly-newer tails directly and keep the general dedupe-and-sort path only for overlapping or disordered input. * fix(managed-agent): defer the plain-commit retry until fault-gate drivers allow it The hosted fault-gate drivers pin a single commit attempt per targeted fault (hosted-store-failure-driver 'Harness must not retry the failed write', hosted-shell-output-driver injections counters), so the retry added for plain /transactions:commit must land together with the driver restatement of bounded, byte-identical, effect-once retry semantics. Tracked in the retry-terminal-states issue; revert it here to keep this PR small and green. Co-authored-by: Qwen-Coder <[email protected]> * fix(web-shell): reset managed stream backoff on delivered events When an SSE connection dies by proxy idle timeout it surfaces as a status-less throw, so the stream loop's end-of-iteration reset never ran and the shared failure counter climbed to the 30s cap for the life of the panel even though every reconnect succeeded and delivered events. Reset on progress inside the for-await body, matching the bootstrap and summary loops' notion of success. Also pin previously unwitnessed review invariants: the empty-incoming merge case (a load-bearing crash guard, not an optimization), the exact exponential backoff ladder under a deterministic random, the stream loop's non-retryable exit and its unchanged resubscribe cursor, and comments documenting the exact-3s first-failure floor and the merge fast path's sorted-input contract. Co-authored-by: Qwen-Coder <[email protected]> * fix(web-shell): track managed stream, snapshot, and poller health separately A connection delivering only replayed duplicates, or delivering nothing while simply staying alive, now also resets the reconnect ladder: the old progress-only reset treated every proxy idle-timeout death as a failure and let the delay climb to the cap on healthy quiet sessions. Gap-recovery snapshots climb their own failure ladder instead of being charged to (and constantly zeroed by) counter that stream delivery owns, and a terminal non-retryable stop is now recorded in a sticky stoppedReason that per-event updates cannot erase — previously a 4xx on the summary poll left the composer frozen with no banner after the next stream event cleared the transient error. Tests pin each merged-event guard clause (ascending input, strictly-newer boundary, and the empty-current short-circuit) with its own mutation witness, and the new mock generators declare the full subscribe options shape so they typecheck outside the repo's blind tsconfig gates. Co-authored-by: Qwen-Coder <[email protected]> * fix(web-shell): keep managed streams alive across definite snapshot answers A transcript-only 4xx from the gap-snapshot read no longer tears down a live event stream: the stop is recorded but the loop keeps resubscribing behind the snapshot ladder. The sticky terminal reason now retires when a later authoritative read succeeds instead of staying painted over a session that demonstrably recovered, and wins the alert chain over a newer transient error so a surviving loop cannot mask the hook's permanent decision. The four fail-classify-stop sites share one closure instead of four verbatim copies, and the hook specs mount through the shared reactHarness primitives with one deterministic-backoff helper. New pins: bootstrap and stream ladder growth across consecutive undelivered rejections, gap-snapshot ladder reset after a healed outage, gap-snapshot 4xx recorded while the stream stays subscribed, stop-reason precedence and persistence at the only production render site, and retirement of the reason by a later successful read. Co-authored-by: Qwen-Coder <[email protected]> * fix(web-shell): render managed action errors beside the terminal stop A sticky terminal stop in the alert chain silently swallowed every page-level action error: with the panel still interactive, a rejected submit, cancel, or list load replaced nothing, so the user saw the stale stop reason above a visibly live transcript with no trace of the fresh failure. The page's own error now renders as its sibling element — the terminal reason keeps the first positional slot that existing alerts read through, and the hook's transient error stays behind it. An expired credential is no longer terminal anywhere either: this client re-derives its headers on every request, so a short-lived-token lapse is answered fresh by the very next attempt once the host refreshes — the loops back off and recover instead of wedging behind a banner until a manual Refresh. Definite 4xx answers for session existence stay terminal and still self-retire when a later authoritative read succeeds. Co-authored-by: Qwen-Coder <[email protected]> * chore(vscode-ide-companion): restore main's NOTICES.txt The merge commit absorbed a NOTICES regeneration produced from my local worktree, whose pnpm store resolves a few transitive versions differently from CI's canonical frozen-lockfile environment (is-fullwidth-code-point, fdir, picomatch, get-east-asian-width). The lane regenerates from a clean install and therefore disagreed with the checked-in file. Co-authored-by: Qwen-Coder <[email protected]> * fix(web-shell): retire managed stream stops through their own authority R6's record-and-continue could park a terminal banner that nothing ever retired: a single definite 4xx on the summary poll stayed red over a visibly healthy stream for the life of the mount, and a session-leg stop during bootstrap left a live summary beside a permanently frozen stream with no warning. Every verdict now carries the authority that issued it, every successful read retires only its own leg's sticky reason (plus the transient error, so the page cannot display the same text twice), and a session-leg success after a terminal bootstrap stop re-arms the effect so subscribing restarts instead of silently staying dead. A transcript-leg verdict survives any number of session-leg successes — a live stream cannot certify history. Specs pin both shapes and the origin match itself; the positional page alert test's unmocked jitter is pinned to the maximum rung so the recovering poll lands after both positional reads. Co-authored-by: Qwen-Coder <[email protected]> * fix(web-shell): record managed health signals per answer authority The failure model kept one sticky slot plus one transient error, so a terminal verdict was retired by any successful read and a page-fetch failure could be swallowed once other signals arrived. Two review Criticals converged on the same root: health is per-authority, not global. - Keep one signal per answer authority (session / transcript / stream), each carrying its message, finality, and a sequence number. The exported stoppedReason/stoppedLeg derive from the newest final signal and the transient error from the newest non-final one, so a terminal verdict is cleared only by a later success of the same leg, and a newer transient can no longer hide a sticky verdict (or vice versa). - A failed older-page fetch now lands in the transcript leg's slot instead of a parallel error field that derivation would have hidden; the next successful page or gap resync retires it. The state-level error field is gone — the records are the single source. - The stream's own failures (subscribe rejections, stall verdicts) live in the stream leg; a resync that advances the cursor retires them. - Snapshot reads settle with Promise.allSettled and reject through a typed SnapshotLegError that forwards status/code, so the session leg's answer always decides bootstrap termination regardless of settle order, without mutating foreign error objects. Verification: managed suite 299/299 across 15 files; five mutants (status/code forwarding, classification order, stream catch leg, stream retirement on advance, page-failure leg) each redden exactly their witness spec; eslint, prettier, and tsc --build clean. * fix(web-shell): let the managed stream retire its own verdict on delivery Answering the remaining R8-1 gap the per-authority records left open: a definite stream answer is now retired when the stream itself recovers, and a standing verdict can no longer be displaced by a blip on a less specific authority. - A genuinely new delivered frame retires the stream leg's records — its own recovery retires its own verdict. Replays and gap frames are not recovery evidence and do not count. - The displayed sticky verdict is picked by authority precedence (stream > transcript > session) instead of newest-final-wins, so a definite 404 blip on the summary poll no longer forces a standing "history pruned" banner out of view for the blip's whole interval. The four R8-1 entrances are pinned one spec each, every one confirmed by its own mutation: a stream transient failure now stays visible through healthy summary polls (M: poll also retiring the stream leg reds it), a definite stream verdict clears when the reconnect delivers new data (M: no delivery retirement), a transcript verdict stands through a session-leg blip and after its recovery (M: poll also retiring the transcript leg), and a failed "Older history" click stays visible through later poll cadences (M: same), with a fifth spec pinning the precedence rule itself (M: newest-final-wins). Verification: managed suite 304/304 across 15 files; all nine mutants red exactly their witness spec and the restored tree is green; eslint, prettier, and tsc --build clean. * fix(web-shell): guard managed verdict finality and clear loading on every recorded failure Two R9 Criticals in the per-authority signal layer: - The fail-to-record rewrite dropped the loading:false the old helper carried, so no failure path cleared the bootstrap loading flag once a snapshot could never succeed: a terminal 404 (or a transcript leg retrying forever) left the transcript skeleton, the "Loading..." status, and the suppressed progress line up for the life of the mount. Every recorded failure now ends any in-flight read at the one shared writer. - The write layer had no monotonicity guard on final: a weaker later failure on the same leg (a network TypeError, 401/403, a failed "Older history" click, a stall assertion) overwrote a standing terminal verdict with a transient record, and retire() — the only clearing path — never re-asserted it. Verdicts are now monotonic at the write: a final entry cannot be downgraded by a non-final failure, and retire() stays the only downgrade path. The loadOlder catch, which lives outside the effect closure, applies the same classification and guard in its mirrored write instead of hardcoding final: false, and the stall assertion routes through the same classification helper as every other stream-leg failure. Pins: loading:false assertions in the three bootstrap-failure scenarios the review named plus the transcript-leg-only scenario where no success path ever runs (the named transcript-verdict spec keeps its assertion, but its success-first shape means it cannot red the mutant — the red-able transcript-leg pin is the new one); two monotonicity witnesses (a definite stream verdict surviving later transient failures; a resync verdict surviving a failed page fetch). Four mutants — unguarded record(), unguarded/unclassified loadOlder catch, no loading clear, session-leg page failure — each red every one of their witness specs. Verification: managed suite 306/306 across 15 files; eslint, prettier, and tsc --build clean. * fix(web-shell): let every managed read authority retire its own verdict on its own success evidence Two R10 Criticals against the per-authority signal layer: - A failed "Older history" click now stays transient even on a definite 4xx. Classifying it terminal had no heal path: the transcript leg retires only on a snapshot success or a later click, so a one-second store failover under a live panel produced a permanent "session not found" alert that also masked later genuine session verdicts by precedence. A click is one-off evidence with no ladder asking again; the poll certifies a genuinely gone session on its own leg. The monotonicity guard is untouched. - Verdicts expired on evidence narrower than "this leg answered successfully", so a terminal record outlived its failure. The retirement contract now separates the leg's success from the delivery of new data: a fulfilled transcript read retires its stale verdict even when the session leg of the same snapshot rejected (the read was previously discarded before either retirement site); a subscription attempt that stays error-free for the backoff-certifying duration expires a terminal stream verdict (replays and parked-idle connections count as answers; failed reconnects never do, and transient records are never expired); and an error-free clean close does the same for reconnect patterns that never idle long enough. Pins: a 404 page click now surfaces as a transient error only (mutant: classification restored reds it); a stale resync verdict clears on the next resync whose transcript leg fulfills (mutant: discarding the fulfilled read reds it); a terminal stream verdict expires on a replay-only reconnect past the proof-of-life point, and on an error-free clean close (mutants: no expiry on either path reds each). The final-monotonicity, loading-clear, and page-leg mutants from the prior round were re-run against the moved sites and still red their witnesses. Constraints held: non-final stream records survive replay parks, terminal verdicts survive later failed reconnects, and the session-first classification order plus the replay/not-new frame rule are unchanged. Verification: managed suite 310/310 across 15 files; eslint, prettier, and tsc --build clean. * fix(web-shell): retire managed verdicts on any answered read and restore them when the answering attempt fails Two review Criticals against the signal layer's retirement contract, plus the round-4 real-stack F1: - A leg's verdict still expired on evidence narrower than that leg answering. The transcript leg had no recurring success evidence at all: retire('transcript') ran only inside snapshot(), so once a gap resync took a definite 4xx and the stream healed by delivering new frames without ever gapping again, the verdict stood for the life of the mount and masked a later genuine session verdict by precedence. A genuinely new frame now retires the transcript leg too, which also clears a failed "Older history" click on the next delivery (the merge base's behavior). And a resync that answers but cannot advance the cursor is the stream leg answering: the stall branch expires a standing terminal stream verdict first, or the monotonicity guard would block the stall warning itself. - The proof-of-life timer measured from attempt start, so a reconnect that spent three seconds failing expired a standing terminal verdict on evidence the contract excludes ("failed reconnects never count"), and the removal disarmed the monotonicity guard that would have blocked the weaker transient write when the throw finally landed. The timer now captures the standing verdict from a synchronous mirror ref at fire time (a state updater runs at React's flush and can lose that race) and the stream catch restores it before recording the failure; a genuinely new frame in between drops the capture, since live data refutes the verdict for good. - Proof-of-life and error-free clean closes now expire the stream leg's transient records as well as terminal ones (F1: a recovered 502/401 line no longer waits for the next event), with the stall warning marked and exempt — an idle connection that never advances is exactly the condition it describes. Also pins the worker containment guard on the read side (round-1 follow-up): a read_file call escaping the workspace by traversal or by an in-workspace symlink is refused; reads never pass through file history, so the executor check is their only guard. Pins: four new hook specs (terminal verdict expiring across non-advancing resyncs; terminal verdict restored when a reconnect fails past the proof-of-life point; transient stream failure expiring on an error-free idle reconnect; stall warning surviving an idle connection) plus the read-side containment test. Each removal mutant reds its witness: no transcript retirement on delivery, no stall-branch expiry, no catch-side restore, final-only expiry, no stall exemption, and a read_file exemption from the guard. The round-9 resync-verdict spec now clicks before the reconnect: the guard half is unchanged, and the delivery half asserts the retirement this round makes the contract. Verification: managed suite 314/314 across 15 files; cli serve managed suites 2269 passed (4 pre-existing skips) across 21 files; npm run build, typecheck, and lint clean. * fix(web-shell): gate the managed proof-of-life verdict expiry on an answered attempt The proof-of-life timer expired the stream leg's standing verdict on wall time alone, so a reconnect that opened and went silent — a proxy black-holing the response body keeps the fetch in flight indefinitely — erased a terminal verdict or transient failure and left the panel rendering no alert over a dead stream. An open-but-silent connection is not the leg's success evidence: the expiry now fires only once the attempt has actually delivered a frame (a replay counts, since delivered is set before the replay guard), keeping the mirror capture and the catch-side restore unchanged. The transient expiry spec is restored to an answered reconnect (it had been rewritten to a quiet one, blessing the regression), and two new specs pin the unanswered polarity for both a terminal verdict and a transient failure; each goes red without the gate. Verification: managed suite 332/332; mutation probe (gate removed) reds exactly the two new specs; npm run build, typecheck, and lint clean. * fix(web-shell): expire stream verdicts on heartbeat-answered idle reconnects (#13179) On an idle Session the server sends no frames after the resume head, only :keepalive comments, which the client dropped — so the R11-1 proof-of-life gate (expire a standing stream verdict only once the reconnect has answered) never fired, and a one-off 404/502, an outage, or a 401 period kept its banner until the next event. streamEvents now reports each complete heartbeat/comment frame through a new onAlive callback (a trailing fragment is a mid-frame disconnect and is not reported), the Java provider forwards it, and the hook counts it as the attempt's answer: it marks the attempt delivered and, when the proof-of-life point has already passed, lands the expiry the timer was holding. The black-hole guarantee is unchanged — a silent connection never sends a heartbeat — and the catch-side verdict restore still covers a keep-alive-credited attempt that goes on to fail. * fix(web-shell): never resurrect a heartbeat-expired stream verdict (#13179) A keep-alive landed the proof-of-life expiry but left the attempt's verdict capture armed, so a later drop of that certified connection restored the terminal verdict the heartbeat had refuted, and the monotonicity guard then hid the real network error. Disarm the capture on every heartbeat and never arm it for a heartbeat-certified attempt. Also: a heartbeat no longer resets the stream reconnect ladder (it certifies the connection, not the data path), and the session page renders the hook's stoppedReason and error channels as sibling alerts instead of collapsing them, suppressing exact repeats so one server condition is announced once. Pins the catch-side verdict restore (replay shape), the new-frame disarm past the proof-of-life point, the verdict mirror clears, and a pre-proof-of-life heartbeat's early-expiry gate. * fix(web-shell): tighten the signal-leg ledger and alert de-duplication (#13179) * fix(web-shell): land the proof-of-life expiry on late frames and bound re-arms (#13179) A reconnect whose first frame lands past its own proof-of-life point — a slow accept replaying the tail from lastEventId — set delivered but never landed the expiry the point was holding, so a terminal verdict the stream had already refuted kept rendering over a live, answered connection. The frame path now mirrors the heartbeat path: a frame past the point lands the expiry (a replay counts) and it is never restored by that connection's later drop. Also: when both bootstrap reads reject, a definite transcript answer is now recorded on its own leg before the session error's throw discards it, provided the session leg is transient and will retry (a terminal session answer keeps owning the display); the session-leg re-arm is bounded per session so an alternating definite-4xx/success backend can no longer remount the effect every rung-zero cadence forever; the stall guard drops an operand no observation can reach (every stall re-assertion is preceded by the same pass's successful resync, which retires any competing record first); SnapshotLegError loses its never-read code field; the bootstrap catch reuses failed()'s terminality predicate; and the signals doc now states the cross-leg rule the frame path actually implements. Pins the late-replay expiry, the both-reject classification, the re-arm bound, the masked stream record in the paging spec, and the message-keyed alert node identity through the 2-to-1 transition. * fix(web-shell): keep the spent re-arm bound's verdict standing (#13179) Round 4 review follow-ups on the managed session hook. retire() deleted the standing terminal verdict even after the session-leg re-arm budget was spent, and cleared endedRef, so an alternating definite-4xx/success backend ended at a healthy-looking empty panel with no alert and no path back but reload(). Past the bound the verdict now survives the poll's successes. The bootstrap catch's endedRef write was the one cross-run shared-ref write without an abort guard: a superseded run's late definite session answer could arm the live run's re-arm path and tear its effect down on the next poll success. It is now guarded like its siblings. record() let an identical terminal answer re-stamp its record with a fresh seq, suppressing a newer live record on another leg; a repeat booking of the same final message is now a no-op. Also: the both-reads-reject spec's retry claim now counts the bootstrap's own transcript read instead of getSession, which the poll shares; the onAlive disarm and the late-replay landing disarm each get a witness (replay-armed capture, then a heartbeat or a late replay, then the connection's drop); the stall recovery beats now assert retired-not-escalated; and the proof-of-life comment names the late-frame disarm alongside the heartbeat's. * fix(web-shell): recover a spent re-arm bound on the slowest rung (#13179) --------- Co-authored-by: Qwen-Coder <[email protected]> Co-authored-by: qwen-code-ci-bot <[email protected]> Co-authored-by: qwen-code-dev-bot <[email protected]> Co-authored-by: qwen-code-autofix <[email protected]> Co-authored-by: qwen-code-autofix <qwen-code-autofix@localhost> |
||
|
|
5b1c701400 |
feat(managed-agent): enforce workspace actor roles across the bound-Session surface (#13545)
* feat(managed-agent): persist workspace actor roles and session owners
Slice B of #13535 (storage only, behavior-invisible):
- V48 migration replaces managed_workspace_access.can_read/can_create
with a single role column (READER/OPERATOR/OWNER, CHECK-enforced),
backfilling can_create -> OPERATOR and can_read-only -> READER, and
adds managed_agent_session.owner_actor_key backfilled from
creator_actor_key.
- WorkspaceAccess becomes NONE/READER/OPERATOR/OWNER (ordered, with an
atLeast helper); NONE remains the no-row decision, never storable.
- Every grant reader (canRead, findReadable, listReadable,
readableDefault, resolveForCreation, authorizePassiveAttachment, the
cwd-change registry-facts join, the listSessions EXISTS filter) keeps
its current truth table, re-derived from role.
- insertSession writes owner_actor_key = creator on both bound and
legacy creation paths; anonymous legacy creation leaves both NULL.
- Test fixtures move to the role column; revocations that previously
zeroed booleans now delete the grant row (the honest no-access
equivalent under the CHECK); a (FALSE,TRUE) fixture state that role
ordering makes unrepresentable becomes a grant-row deletion.
- New tests: V47->V48 migration shape (backfill, owner:=creator, CHECK
rejects NONE/invalid, post-V48 store-path creation writes owner) and
session-owner store test (bound/legacy owner write, anonymous NULL).
The full managed-agent-server suite differs from baseline only in the
new tests; ManagedSessionStoreIntegrationTest#holdsRestorePages
InsideThePerPageByteBudget errors identically on unmodified HEAD
(verified in a clean worktree at
|
||
|
|
bb66a52c96 |
fix(managed-agent): stop a bound Turn under refused authorization (#13163)
* feat(managed-agent): admit later Turns of a Workspace-bound Session for its creator G0 admits only the initial file-tool Turn created with a Workspace-bound Session; every later public submit is refused with workspace_unavailable, so a Hosted Session cannot hold a conversation. Admit a later Turn when the deployment's Workspace-files opt-in is on and the submitter created the Session. Execution already authorizes each Turn against the creator's Workspace grants (WorkspaceExecutionStore.authorize), so letting another actor submit would run tools under the creator's authority; every other actor keeps the existing refusal (404 without read access, 409 otherwise). The store admits a bound Session's later Turn only under the same opt-in. Cancel, rename, lifecycle and cwd operations stay gated. - ManagedWorkspaceRegistry.createdSession looks up the creator in managed_workspace_create_command. - HostedPublicWorkspaceIT: a reader who is not the creator is refused; the creator's second Turn runs write, edit and read again through the real Broker and worker and completes. - ManagedWorkspaceAdmissionTest: the enabled store admits the later Turn and the creator lookup distinguishes actors and tenants. - The Workspace-binding capability description (contract and generated WebShell types), the README and both G0 design documents describe the new boundary. The contract version is left for #13101, which takes 1.25. Not built or run locally. * feat(web-shell): let the creator send later Turns to a bound Session The server now admits later Turns from a Workspace-bound Session's creator, but the Managed panel still hid the composer for every bound Session and the Java provider forced canSend to false. - WebShellSessionCapabilities gains workspaceTurns, computed per Session and caller with the same rule the submit path enforces (opt-in on, caller can read, caller created the Session). The contract and the generated WebShell types describe it. - The Java provider sends for a bound Session only when the service reports workspaceTurns, and reports it in the summary only when true. - The Managed panel shows the composer for such a Session; cancel stays hidden for bound Sessions. - HostedPublicWorkspaceIT checks the capability per caller through the WebShell route; the contract test's WebShell capability shape and provider/page tests cover the client. * feat(managed-agent): let the bound Session creator cancel a running Turn Later Turns of a Workspace-bound Session could be submitted by the creator but not cancelled: the service and store refused the cancel, and the coordinator skipped bound Sessions entirely. Apply the same rule to cancellation. The creator, with the Workspace- files opt-in on, may cancel; the coordinator then cancels through the Hosted Harness like any other Turn, which aborts the Turn and settles its Runtime calls through their original identities. Other actors and deployments without the opt-in keep the existing refusal, and the coordinator still leaves a bound Session alone without the opt-in. - HarnessCoordinatorTest: without the opt-in only the opt-in is read; with it, the bound Turn is cancelled through the Harness. - HostedPublicWorkspaceIT: a later Turn is held in the model call; a reader's cancel is refused, the creator's cancel is accepted, and the Turn ends CANCELLED with no tool execution. - WebShell reports canCancel for such a Session when workspaceTurns is set, so the Managed panel shows Cancel. - Contract text, generated types, README and both G0 design documents now keep only lifecycle operations gated. * feat(managed-agent): let the bound Session creator rename it Rename of a Workspace-bound Session answered 409 until a role source decided who may change it (D4 4.9). #12867 settled the first version: the Session's creator is its only owner. Rename changes only the title through the Hosted Harness and touches no Runtime, so apply the same creator rule under the Workspace-files opt-in; the store admits only a RENAME mutation of a bound Session, and only under the opt-in. Close, archive, delete and unarchive stay gated: the embedded Broker's drain only stops warming a closed Session and has no Harness-level teardown yet, so closing a bound Session would leave its worker and any held Workspace lease behind. HostedPublicWorkspaceIT: a reader's rename is refused; the creator's rename returns the new title. * style(web-shell): keep the workspaceTurns expression on one line as Prettier prints it * test(managed-agent): read the renamed title from the public Session metadata PublicSession carries its title under metadata.title; the rename assertion read a top-level title and saw an empty string although the rename returned 200. * fix(web-shell): match the generated optional workspaceTurns capability * test(managed-agent): keep the approval run out of the later-Turn checks * docs(managed-agent): Align G0 gating statements with creator later-Turn admission The G0 section and Prerequisites still published the pre-follow-up gate ("later submit/cancel remain gated"), contradicting the boundary paragraph this PR adds; state the creator's submit/cancel/rename admission and keep close/archive/delete/unarchive and cwd gated. Scope the follow-up's refusal precisely: requireLegacyWorkspace answers workspace_unavailable only when the actor can read the Workspace, and session_not_found otherwise. Apply the same two corrections to the bilingual design doc and note the follow-up's composer UI change. Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmuoob5v90j * chore(managed-agent): Bump the public API contract to 1.27.0 The deferral resolved: #13101 merged and took 1.25.0, and #13117 took 1.26.0. Record this PR's contract change per the version-history convention: creator later-Turn admission and the workspaceTurns capability. Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmuoob5v90j * fix(web-shell): hide execution-unavailable banner line when creator can send later Turns The workspace-binding banner always stated that message execution is unavailable, contradicting the composer this PR enables for the creator of a bound Session with workspaceTurns capability. Gate the third banner paragraph on !capabilities.workspaceTurns so the page no longer states both. Pin both branches in ManagedSessionsPage tests. Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmuoqgbnv0l * test(sdk-java): pin bound-Session admission clauses with negative controls Three of the four clauses this PR adds to bound-Session admission could be deleted with the suite staying green: - ManagedWorkspaceRegistry.createdSession's session_id column: create a second bound Session by actor-b under a distinct idempotency key and assert createdSession(tenant, actor-a, otherSession) is false, so the lookup must match this Session, not any Session the actor created. - maySubmitWorkspaceTurn's harness.isWorkspaceFilesAvailable() clause: assert the bound-session web-shell GET reports capabilities.workspaceTurns == false while the opt-in is off. - boundRenameAllowed's kind conjunct: assert beginSessionMutation with UNARCHIVE on the enabled store throws workspace_unavailable, and add the positive insertCancelCommand control so the cancel path stays open for bound Sessions under the opt-in. Each new assertion was verified to go red under the corresponding mutation (session_id dropped, flag clause dropped, kind conjunct dropped, cancel conjunct dropped) and the suite returns green with the sources restored. Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmuoqgbnv0l * test(sdk-java): exercise later Turns under approval-mode=default and pin canRead HostedPublicWorkspaceIT changes: - register() now grants the reader in both runs (can_create only in the files run), removing the access-table primary-key collision that forced the approvals run to skip the later-Turn block. - The approvals run now drives a later Turn through answerActions and asserts it completes, so a later Turn admitted under approval-mode=default is covered; the cancel/rename probes keep their held model reply and stay in the files run to fit the method timeout. - proof.txt is reset to a sentinel just before the later-Turn submit, so the post-Turn "after" assertion can fail if the later Turn's write and edit stop reaching the bound root (R1-13). - After the rename check, revoking the creator's read grant asserts submit, cancel and PATCH all answer 404 session_not_found, pinning the canRead clause of maySubmitWorkspaceTurn (R1-12 clause 3). - Model-request counts become 16 (approvals) / 18 (files) and the approvals run now answers 8 actions across the two Turns. Compile-verified via mvn test-compile. Execution requires the bundled dist/cli.js plus a MySQL/hosted-Harness stack, which this environment cannot provide; behavior was traced against requireSubmitter / requireLegacyWorkspace / boundRenameAllowed and the fixture request model, and CI remains the executor. Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmuoqgbnv0l * fix(sdk-java): align bound-Session later-Turn admission with the execution authority Round-2 review of #13112 found the admission predicate certifying Turns that execution can never run. maySubmitWorkspaceTurn now also requires the predicates the cited authority fixes at creation (Session ACTIVE, the qwen-code agent, the frozen execution profile refs) and the creator's can_create on an ACTIVE registry, read through the same grant row shape; a can_read-revoked creator still falls through to the documented 404. Cancellation attaches passively so an abort no longer depends on the physical mount still verifying. renameSession answers a non-retryable refusal with its own status and code instead of a transient 503. requireSubmitter loads the Session once per call, and the session read paths skip the canRead probe their entry already established. The OpenAPI contract, README and both design docs now state the creator admission, its grants qualifier and the still-gated lifecycle/cwd operations consistently, and the generated WebShell types are regenerated from the corrected contract. Tests pin the bound rename admission, the two empty-creation profile escapes and the can_create-revoked arm. Co-authored-by: Qwen-Coder <[email protected]> * fix(sdk-java): cancel a live bound Turn through its running attachment |
||
|
|
b585508733 |
feat(hosted): show captured inputs in native approval cards (#13407)
* feat(hosted): show captured inputs in native approval cards Bind native approval options to the current captured input and include it in the first Action commit. Preserve the API preview through the managed provider and display literal fallback content with localized size notices. Co-authored-by: Qwen-Coder <[email protected]> * fix(web-shell): escape invisible format characters in the managed approval preview R1-1: action.inputPreview.text reached the approval dialog verbatim, so a command carrying U+202E rendered reordered while the approved bytes stayed unseen. Reuse the package's existing escapePreviewText invariant instead of adding a second character class, and pin it at the value the card renders. R1-7: the cross-call separation case passed an empty transcript, so the matching rule it names never ran. Give it two same-tool rows keyed by itemId with no Harness input; the tool-name mutant now fails it. R1-8: prefix the rendered-preview fixture with a leading space so the "must not be trimmed" half of the guarantee is pinned at the DOM boundary. R1-4: alias inputPreview to the generated WebShellActionInputPreview schema rather than hand-declaring the same three fields. Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmuwnio38a6 * fix(hosted): name the version 2 preview tool set and pin its restore path R1-2: the tools admitted to version 2 were an anonymous inline literal that has to stay byte-identical to the Java reader's closed PREVIEW_TOOLS. Give the set a name to grep, state the contract and both silent drift directions, pin it in a test, and assert the Shell Action really carries v2 plus its own input: dropping run_shell_command previously left the whole suite green. R1-3: the managed-action-options branch of nestedResourceRefs was byte-for-byte the fall-through of the generalised branch below it. Fold the kind into that condition so the parse arguments and the throw-versus-degrade choice live in one place instead of four lines apart in one function. R1-5: nestedResourceRefs has two callers and only the commit path was covered. Run the read-only snapshot inspector over a Session that raised a native approval, so the options -> input edge is pinned on the restore path too. R1-6: document the reader-side direction of the change - a CLI inspector from before this commit cannot verify a snapshot written after it - in both design docs, matching the precedent recorded for the journal change. Co-authored-by: Qwen-Coder <[email protected]> * test(acp): align deferred tool rejection assertions Preserve target-specific validation guidance and the bridge refusal marker in the two existing ACP expectations. Production behavior is unchanged. Co-authored-by: Qwen-Coder <[email protected]> --------- Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
d092d5b439 |
fix(web-shell): managed session UI correctness from the #12692 R2 review (#13342)
* fix(web-shell): managed session UI correctness from the #12692 R2 review
Ten R2 review follow-ups on #12692 in the web-shell managed sessions
UI, re-verified against current main (
|
||
|
|
2b15eac862 |
feat(managed-agent): let a Workspace-bound Session's creator submit, cancel and rename (#13112)
* feat(managed-agent): admit later Turns of a Workspace-bound Session for its creator
G0 admits only the initial file-tool Turn created with a Workspace-bound
Session; every later public submit is refused with workspace_unavailable,
so a Hosted Session cannot hold a conversation.
Admit a later Turn when the deployment's Workspace-files opt-in is on
and the submitter created the Session. Execution already authorizes
each Turn against the creator's Workspace grants
(WorkspaceExecutionStore.authorize), so letting another actor submit
would run tools under the creator's authority; every other actor keeps
the existing refusal (404 without read access, 409 otherwise). The
store admits a bound Session's later Turn only under the same opt-in.
Cancel, rename, lifecycle and cwd operations stay gated.
- ManagedWorkspaceRegistry.createdSession looks up the creator in
managed_workspace_create_command.
- HostedPublicWorkspaceIT: a reader who is not the creator is refused;
the creator's second Turn runs write, edit and read again through the
real Broker and worker and completes.
- ManagedWorkspaceAdmissionTest: the enabled store admits the later
Turn and the creator lookup distinguishes actors and tenants.
- The Workspace-binding capability description (contract and generated
WebShell types), the README and both G0 design documents describe the
new boundary. The contract version is left for #13101, which takes
1.25.
Not built or run locally.
* feat(web-shell): let the creator send later Turns to a bound Session
The server now admits later Turns from a Workspace-bound Session's
creator, but the Managed panel still hid the composer for every bound
Session and the Java provider forced canSend to false.
- WebShellSessionCapabilities gains workspaceTurns, computed per Session
and caller with the same rule the submit path enforces (opt-in on,
caller can read, caller created the Session). The contract and the
generated WebShell types describe it.
- The Java provider sends for a bound Session only when the service
reports workspaceTurns, and reports it in the summary only when true.
- The Managed panel shows the composer for such a Session; cancel stays
hidden for bound Sessions.
- HostedPublicWorkspaceIT checks the capability per caller through the
WebShell route; the contract test's WebShell capability shape and
provider/page tests cover the client.
* feat(managed-agent): let the bound Session creator cancel a running Turn
Later Turns of a Workspace-bound Session could be submitted by the
creator but not cancelled: the service and store refused the cancel,
and the coordinator skipped bound Sessions entirely.
Apply the same rule to cancellation. The creator, with the Workspace-
files opt-in on, may cancel; the coordinator then cancels through the
Hosted Harness like any other Turn, which aborts the Turn and settles
its Runtime calls through their original identities. Other actors and
deployments without the opt-in keep the existing refusal, and the
coordinator still leaves a bound Session alone without the opt-in.
- HarnessCoordinatorTest: without the opt-in only the opt-in is read;
with it, the bound Turn is cancelled through the Harness.
- HostedPublicWorkspaceIT: a later Turn is held in the model call; a
reader's cancel is refused, the creator's cancel is accepted, and the
Turn ends CANCELLED with no tool execution.
- WebShell reports canCancel for such a Session when workspaceTurns is
set, so the Managed panel shows Cancel.
- Contract text, generated types, README and both G0 design documents
now keep only lifecycle operations gated.
* feat(managed-agent): let the bound Session creator rename it
Rename of a Workspace-bound Session answered 409 until a role source
decided who may change it (D4 4.9). #12867 settled the first version:
the Session's creator is its only owner. Rename changes only the title
through the Hosted Harness and touches no Runtime, so apply the same
creator rule under the Workspace-files opt-in; the store admits only a
RENAME mutation of a bound Session, and only under the opt-in.
Close, archive, delete and unarchive stay gated: the embedded Broker's
drain only stops warming a closed Session and has no Harness-level
teardown yet, so closing a bound Session would leave its worker and any
held Workspace lease behind.
HostedPublicWorkspaceIT: a reader's rename is refused; the creator's
rename returns the new title.
* style(web-shell): keep the workspaceTurns expression on one line as Prettier prints it
* test(managed-agent): read the renamed title from the public Session metadata
PublicSession carries its title under metadata.title; the rename
assertion read a top-level title and saw an empty string although the
rename returned 200.
* fix(web-shell): match the generated optional workspaceTurns capability
* test(managed-agent): keep the approval run out of the later-Turn checks
* docs(managed-agent): Align G0 gating statements with creator later-Turn admission
The G0 section and Prerequisites still published the pre-follow-up gate
("later submit/cancel remain gated"), contradicting the boundary
paragraph this PR adds; state the creator's submit/cancel/rename
admission and keep close/archive/delete/unarchive and cwd gated. Scope
the follow-up's refusal precisely: requireLegacyWorkspace answers
workspace_unavailable only when the actor can read the Workspace, and
session_not_found otherwise. Apply the same two corrections to the
bilingual design doc and note the follow-up's composer UI change.
Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-pr-closeout/jmuoob5v90j
* chore(managed-agent): Bump the public API contract to 1.27.0
The deferral resolved: #13101 merged and took 1.25.0, and #13117 took
1.26.0. Record this PR's contract change per the version-history
convention: creator later-Turn admission and the workspaceTurns
capability.
Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-pr-closeout/jmuoob5v90j
* fix(web-shell): hide execution-unavailable banner line when creator can send later Turns
The workspace-binding banner always stated that message execution is
unavailable, contradicting the composer this PR enables for the creator
of a bound Session with workspaceTurns capability. Gate the third
banner paragraph on !capabilities.workspaceTurns so the page no longer
states both. Pin both branches in ManagedSessionsPage tests.
Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-pr-closeout/jmuoqgbnv0l
* test(sdk-java): pin bound-Session admission clauses with negative controls
Three of the four clauses this PR adds to bound-Session admission could
be deleted with the suite staying green:
- ManagedWorkspaceRegistry.createdSession's session_id column: create a
second bound Session by actor-b under a distinct idempotency key and
assert createdSession(tenant, actor-a, otherSession) is false, so the
lookup must match this Session, not any Session the actor created.
- maySubmitWorkspaceTurn's harness.isWorkspaceFilesAvailable() clause:
assert the bound-session web-shell GET reports
capabilities.workspaceTurns == false while the opt-in is off.
- boundRenameAllowed's kind conjunct: assert beginSessionMutation with
UNARCHIVE on the enabled store throws workspace_unavailable, and add
the positive insertCancelCommand control so the cancel path stays
open for bound Sessions under the opt-in.
Each new assertion was verified to go red under the corresponding
mutation (session_id dropped, flag clause dropped, kind conjunct
dropped, cancel conjunct dropped) and the suite returns green with the
sources restored.
Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-pr-closeout/jmuoqgbnv0l
* test(sdk-java): exercise later Turns under approval-mode=default and pin canRead
HostedPublicWorkspaceIT changes:
- register() now grants the reader in both runs (can_create only in the
files run), removing the access-table primary-key collision that
forced the approvals run to skip the later-Turn block.
- The approvals run now drives a later Turn through answerActions and
asserts it completes, so a later Turn admitted under
approval-mode=default is covered; the cancel/rename probes keep their
held model reply and stay in the files run to fit the method timeout.
- proof.txt is reset to a sentinel just before the later-Turn submit, so
the post-Turn "after" assertion can fail if the later Turn's write and
edit stop reaching the bound root (R1-13).
- After the rename check, revoking the creator's read grant asserts
submit, cancel and PATCH all answer 404 session_not_found, pinning
the canRead clause of maySubmitWorkspaceTurn (R1-12 clause 3).
- Model-request counts become 16 (approvals) / 18 (files) and the
approvals run now answers 8 actions across the two Turns.
Compile-verified via mvn test-compile. Execution requires the bundled
dist/cli.js plus a MySQL/hosted-Harness stack, which this environment
cannot provide; behavior was traced against requireSubmitter /
requireLegacyWorkspace / boundRenameAllowed and the fixture request
model, and CI remains the executor.
Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-pr-closeout/jmuoqgbnv0l
* fix(sdk-java): align bound-Session later-Turn admission with the execution authority
Round-2 review of #13112 found the admission predicate certifying Turns
that execution can never run. maySubmitWorkspaceTurn now also requires the
predicates the cited authority fixes at creation (Session ACTIVE, the
qwen-code agent, the frozen execution profile refs) and the creator's
can_create on an ACTIVE registry, read through the same grant row shape;
a can_read-revoked creator still falls through to the documented 404.
Cancellation attaches passively so an abort no longer depends on the
physical mount still verifying. renameSession answers a non-retryable
refusal with its own status and code instead of a transient 503.
requireSubmitter loads the Session once per call, and the session read
paths skip the canRead probe their entry already established. The OpenAPI
contract, README and both design docs now state the creator admission,
its grants qualifier and the still-gated lifecycle/cwd operations
consistently, and the generated WebShell types are regenerated from the
corrected contract. Tests pin the bound rename admission, the two
empty-creation profile escapes and the can_create-revoked arm.
Co-authored-by: Qwen-Coder <[email protected]>
* fix(sdk-java): cancel a live bound Turn through its running attachment
|
||
|
|
c15dd268c0 |
fix(web-shell): stop offering a Managed approval the viewer cannot answer (#13165)
* fix(web-shell): stop offering a Managed approval the viewer cannot answer After the service answers 403 action_forbidden, the approval card kept its Allow and Deny buttons enabled, so a viewer who is not the Session creator could send one refused request per click until the Action expired. Disable the card once that refusal arrives; the warning is scoped to the Action, so the next approval is offered again. * fix(web-shell): scope the creator-only approval latch to the Session Review on #13165 found three gaps in the Managed approval refusal: - action_forbidden is decided by the service from the (tenant, Session, actor) row on the respond path, but the disabled latch derived from the per-Action answerError, which the hook clears as soon as the refused Action leaves the pending list. A reader watching a Session that raises sequential approvals ate one guaranteed 403 per Action. The hook now keeps a Session-scoped respondForbidden flag, set on an action_forbidden answer failure and reset only when the selection changes, and the page disables the card from it; the per-Action warning keeps its scope. - The persistent disabled state had no visual affordance: the only :disabled rule in ToolApproval.module.css is goal-scoped, so a refused Managed approval looked clickable while swallowing clicks. A base .option:disabled rule (after .option:hover so equal specificity resolves by source order) dims it and shows the not-allowed cursor; the goal variant's stronger rule is untouched. - Every HTTP answer failure the client builds carries a string code, yet the only retry test used an uncoded Error, so nothing pinned that a coded but retryable failure stays answerable. A 503 negative control now fails if the action_forbidden condition is widened. Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmupzgp080a * style(web-shell): format ToolApproval.css.test.ts with Prettier The new stylesheet pinning test was committed without a prettier pass, so the Lint & Static "Run Prettier" step failed on it. Collapses one multi-line expect to the canonical single line; no behavior change. Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmupzgp080a * fix(web-shell): remember a creator-only refusal per Session The latch was one boolean that the `[sessionId]` effect cleared unconditionally, so leaving a refused Session and coming back re-enabled the card and re-admitted the guaranteed 403. Remember the refused Session ids instead and derive `respondForbidden` from the current selection. The latch was also set below the `if (!isPending()) throw failure;` guard, so a 403 landing after a re-read had already dropped the refused Action rethrew without latching. The service checks the creator before it checks that the Action still exists, so that refusal is reachable; record it above the guard, keyed by the Session the answer was aimed at rather than the one now selected. Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmuqa6hxd10 * fix(web-shell): keep the creator-only reason on screen while the latch holds The warning was scoped to the Action that failed, so from the second approval onward the card rendered disabled with no stated reason anywhere: a viewer could not tell a permission policy from a stuck or still-loading card, and the disabled options also drop out of sequential focus navigation. Render the reason from the Session-scoped latch as a status line, since it restates what the viewer was already told rather than announcing anything new, and keep the per-Action alert for the failure itself. Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmuqa6hxd10 * fix(web-shell): bound the creator-only reason to the card it describes The latch outlives the refused Action, so the reason line it now drives would stay on screen after the Session's last approval had left, explaining an approval that is not there. Render it only while a card is on screen; the per-Action alert branch is unchanged, because `answerError` is only exposed for the Action being shown. Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmuqa6hxd10 * fix(web-shell): let a dead Managed approval card state its reason to assistive tech Round-3 review findings, one batch: - R2-1: the creator-only reason rendered as a sibling of the alertdialog, so on a latch-only render a screen-reader user reached an inoperable dialog whose accessible description carried the question, the tool and the arguments caveat, but no cause. Route the reason's id into extraDescriptionId next to the caveat. aria-describedby is a space-separated IDREF list, so the prop stays a string, and one boolean now feeds both the element's mount condition and its id reference, so no IDREF can dangle. - R3-1: drop the inert `|| approvalForbidden` operand and the derivation that fed it. `answerError` is exposed only while `answerFailure.current` matches the Action on screen, which only holds after `isPending()` passed with the latched Session still selected, so `respondForbidden` is already true on every render where the deleted operand was true. - R3-2: pin that the latch records `target.sessionId` rather than the Session selected when the 403 lands. A live-read mutant survives all 27 shipped hook tests; the new case is the only thing that turns it red. Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmuqkwas11i --------- Co-authored-by: yiliang114 <[email protected]> Co-authored-by: yiliang114 <[email protected]> Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
f73f6c39ac |
feat(web-shell): show and answer Hosted tool approvals in the Managed panel (#13107)
* feat(managed-agent): Serve durable permission Actions (D6b)
* test(managed-agent): Avoid concurrent Action mock stubbing
* feat(web-shell): show and answer Hosted tool approvals in the Managed panel
The Managed panel passed pendingApproval={null}, so a Hosted Session
waiting on a D6a approval showed nothing to answer. Build on the D6b
Actions contract (#13101):
- The provider gains an optional Actions reader. The Java provider lists
requested permission Actions through actions/query and answers through
actions/respond with the Action's inputRevision and policyRevision and
a per-Action, per-option idempotency key. The Session summary reports
the actions capability only when the service does.
- action.updated projects to action_updated. It carries no Turn, so the
transcript skips it instead of settling the Turn being streamed.
- useManagedActions re-reads pending approvals on action_updated, on a
stream gap, after an expiry and after an answer. It hides an answered
approval and brings it back if the answer fails.
- The page maps the pending Action onto the shared approval card, with
allow/deny as allow_once/reject_once so labels are localized, and
attaches it to the tool row `${turnId}:${functionCallId}` so the row
expands and shows its arguments.
Not built, type-checked, linted or tested locally.
* feat(web-shell): export the pending Action type for custom Managed providers
* fix(managed-agent): renumber the Actions migration to V24 and bump the contract to 1.25
Merging main brought in #12946's V23__managed_mcp_records.sql next to this
PR's V23__managed_actions.sql; Flyway refuses two migrations with the same
version, so the server would not start. The Actions migration becomes V24
with its content unchanged. The contract takes 1.25.0 on top of main's
1.24.0 (#12946), with a v1.25 note for the Actions routes this PR serves.
* fix(web-shell): memoize pending actions to keep the respond callback stable
The conditional selected a fresh empty array on every render, so the
useCallback dependency changed identity each time and ESLint's
react-hooks/exhaustive-deps warning failed the lint gate.
* style(web-shell): format the Managed approvals page test with Prettier
* fix(web-shell): retry failed approval reads and name them as such
A transient failure of actions/query on the first read left a pending
approval invisible until a manual Refresh: summary polls do not re-read
Actions, and without an Action there is no expiry timer. The page then
showed "The approval answer could not be sent" although nothing was
sent.
- useManagedActions reports loadError and answerError separately, and
retries a failed read after 2, 5 and 10 seconds before giving up.
retry() reads again on demand and restarts the bound.
- The page shows "Pending approvals could not be loaded." with a Retry
button for a read failure, and keeps the answer message for a failed
answer.
- Tests cover the automatic recovery, the retry bound and the manual
retry.
* fix(web-shell): recover failed approval answers and bound expiry reads
* fix(web-shell): Show available managed approval arguments
* fix(web-shell): keep the approval card when an answer was not applied
* fix(web-shell): keep Managed approvals stable across reloads and ended Actions
Treat an unknown session summary as unknown rather than as no Actions
capability, so a reload keeps the shown approval answerable. Drop an
approval the service reports as ended instead of offering a retry, scope
answer failures to the approval that failed, stop retrying client errors,
restart the retry ladder whenever reads resume, and carry the
arguments-unavailable notice inside the approval card.
* fix(web-shell): land only the Critical R1-1 fix for Managed approvals
The previous commit also carried the review's Suggestions. The PR is past
its review-round budget, so only R1-1 stays here: an unknown session
summary keeps the shown approval instead of reading as no Actions
capability. The Suggestions move to the follow-up recorded in #12867.
* fix(web-shell): match Managed approvals to itemId-keyed tool rows
Since #13037, Managed tool rows are keyed ${turnId}:${itemId} and Java tool
Items always carry an itemId, so findManagedApprovalTool no longer found the
row for a pending Action: the card could not show that row's arguments or
expand it. Keep the producer's call ID on the row and match on it, still
within the Action's Turn.
Candidate patch from wenshao's round-4 real-stack verification (R4-1).
* test(web-shell): pin the page half of the Managed approval reload fix
Reverting the page gate back to detail.summary?.capabilities.actions === true
kept every test green (G9). Hold a reload's Session summary and assert the
shown approval stays and no extra Actions read happens.
Candidate test from wenshao's round-3 real-stack verification.
* test(web-shell): spell canCancel in the Managed approvals fixtures
The three approvals fixtures handed to `summary()` omitted
`capabilities.canCancel`, which is a required member, so each one was a
TS2741 that no gate in this package reports (both tsconfigs exclude
`client/**/*.test.tsx` and vitest does not typecheck). They also modelled a
summary the real mapper cannot emit, which always sets both flags.
Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-pr-closeout/jmupe138l16
* fix(web-shell): keep the Harness call title on Managed approval cards
`toManagedPermissionRequest` reads `tool.title` for the approval
`title`, but no producer ever set it: `managedEventsToMessages` only
assigned args/status/times/rawOutput, so the left branch was dead and the
card description degenerated into a restatement of the heading while the
Harness`s own per-call title was dropped.
Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-pr-closeout/jmupe138l16
* fix(web-shell): stop Managed approval state outliving its Action
Two lifecycle leaks in `useManagedActions`:
- a successful re-read cleared `loadError` but never `answerError`, so
"the answer could not be confirmed" stayed on screen after the Harness
ended the Action, and then labelled the next, unrelated card. The
warning now carries the Action it belongs to and is dropped once that
Action is no longer pending.
- a withdrawn reader cleared `pending` without resetting the retry budget,
so a reader restored after the ladder was exhausted got a single attempt
with no retry scheduled, contradicting the hook`s own comment.
Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-pr-closeout/jmupe138l16
* fix(web-shell): stop retrying approval reads the service already answered
R1-5: a 4xx other than 408/429 is the service's answer, not a hiccup, so a
deleted Session no longer costs four guaranteed-failing /actions/query
requests and a Retry that can never succeed. The classification the submit
path already used moves to managed-request-error.ts and is reused, so both
failure paths of the feature agree.
R1-4: the hook reports whether the pending approvals were read for this
Session, so a failed background re-read is named as a refresh instead of
claiming nothing could be loaded next to the loaded, actionable card.
R1-8: the "arguments are unavailable" caveat renders as a sibling of the
approval dialog, so ToolApproval takes an extra description id and the panel
describes the caveat too — otherwise a screen-reader user confirms a Hosted
tool call hearing only the tool name.
Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-pr-closeout/jmupe138l16
* test(web-shell): pin four Managed approval paths that no test observed
R1-10, four sites the review mutation-tested as unpinned:
1. the `stream_gap` arm of the re-read trigger — the durable transcript is
the only route that reports a gap here, since use-managed-session breaks
the live loop on a gap before merging it; the hook doc now says so.
2. the session-switch reset effect and the cross-session mask — the new test
switches Session while the previous card is displayed and its failed
answer is still flagged, so deleting the effect leaves the warning behind
and reverting the mask leaves the stale card answerable.
3. the `pendingApproval` wiring into MessageList — the page test's mock now
captures the prop, which is what keeps the turn owning the pending call
from being folded away.
4. the `setAnswerError(undefined)` clear on a successful answer — the retry
test now ends on the alert being gone, not just on the card.
Every assertion was checked to fail with the production line it covers
reverted (8/8 mutations RED).
Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-pr-closeout/jmupe138l16
* fix(web-shell): isolate late Managed approval replies
Ignore answer settlements once their Session or pending Action has changed,
and clear or display warnings only for the Action they belong to.
Pin late success and failure after Session switches and Action replacement.
* fix(web-shell): drop a Managed approval the service reports as ended
Answering an Action that already expired, was cancelled or was answered
elsewhere returns 409 with a code the contract defines as ended, and no
retry can succeed. Keep the card hidden, clear the warning and read the
list again instead of offering a retry. A read that still lists the Action
shows it again, so a wrong report cannot hide an approvable Action. The
403 actor_scope_mismatch is not an ended Action and keeps its handling.
---------
Co-authored-by: Shaojin Wen <[email protected]>
Co-authored-by: yiliang114 <[email protected]>
Co-authored-by: Shaojin Wen <[email protected]>
Co-authored-by: qwen-code-dev-bot <[email protected]>
Co-authored-by: yiliang114 <[email protected]>
Co-authored-by: Qwen-Coder <[email protected]>
|
||
|
|
6310dd38d5 |
feat(managed-agent): project durable tool results to WebShell (#13037)
* feat(managed-agent): project durable tool results to WebShell * codex: address PR review feedback (#13037) * fix(managed-agent): sync transcript exclusion text and localize forbidden output reads - Name item.tool_result.updated alongside the other transcript-excluded event types in the WebShell transcript contract description, the generated client comment, and the event-replay design notes, keeping the three surfaces in sync as the event-replay post-merge review required. - Explain the 3-byte UTF-8 page-boundary priming in ManagedToolResultPanel. - Map HTTP 403 to managed.result.forbidden in ResultError instead of rendering raw error text, with a regression test. * codex: address PR review feedback (#13037) * codex: address PR review feedback (#13037) * codex: address PR review feedback (#13037) |