Repository navigation
feat: consented session pages, sandbox surfaces, bundled views and plugin themes - #476
Merged
Merged
Conversation
Sub-agents started through ctx.agents.run and delegate now capture the caller's visible World Model synchronously at admission, so a child sees staged characters and schemas instead of only committed state. Child property reads record producers in an isolated recorder and join the caller's run only after the child's output passes validation and the child is still live; failed or cancelled children contribute no edges. Plugin KV, recursive execution, privacy projections, depth and budget policies are unchanged. Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Session pages that hold a plugin's consent can now submit player actions, answer pending interactions, invoke their own runtime and stop the current execution through the host, instead of reading state only. - Consent covers each requested capability (session read, submit action, answer interaction, invoke own runtime, abort, open own panel), keyed by the canonical resolved spec including every root resource digest. - Served plugin assets are verified against the indexed content hash before any bytes are emitted, so a same-size edit cannot run under an old digest. - The bridge projects a version 2 public session with opaque interaction handles and minimal input context; no raw panel, runtime or concealed data is posted. - Runtime writes go through the normal runtime RPC and proposal commit; action and command RPC are not exposed on this bridge. - Port replay tracking, request size bounds and a per-connection request budget are enforced before any handler runs. BREAKING CHANGE: session bridge projection is version 2 and replaces the raw pending block list with public interaction DTOs and handles. Session pages built against version 1 must be updated. Earlier page consent grants are not carried over; pages ask again for each requested capability.
When a visible actor leaves the current stage cast, its last displayed sprite now fades out briefly while the authoritative cast updates at once. Explicit exit transitions keep their meaning, and reduced motion removes the fade. Segment templates can display and enlarge their resolved speaker's avatar through the CharacterAvatar component, which only receives the speaker already resolved for that segment. Scene Stage's dialogue template adopts it. Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Replacement writes derived from a read can now record the baseline the read observed and reject the commit when the stored value has changed. Suspended and pending runtime jobs persist the baseline evidence with their saved state. BREAKING CHANGE: Recreate unresolved development suspensions and pending runtime jobs captured without read-baseline evidence. Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
# Conflicts: # docs/en/internals/security.md # docs/internals/security.md # packages/runtime/src/function-runtime/world-model-view.ts # packages/shared/src/index.ts
Bundled graph and codex pages use the declarative plugin-view contract, and the DM console composes public story, cast and pending-interaction widgets with the session's ordinary composer. Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Add a strict contributes.ui.theme declaration for plugin theme packs with a shared scoped-CSS compiler, bitmap-only resource roots, and typed resolved theme discovery. Themes are isolated from views and webviews and never receive a bridge or script exception. Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
# Conflicts: # apps/web/src/components/session/plugin-panel.tsx # apps/web/src/components/session/plugin-view-host.tsx # docs/en/plugins/ui.md # docs/plugins/ui.md # packages/shared/tests/ui-spec.test.ts
# Conflicts: # docs/en/internals/security.md # docs/en/plugins/ui.md # docs/en/reference/plugins.md # docs/internals/security.md # docs/plugins/ui.md # docs/reference/plugins.md # packages/shared/src/index.ts
Replace the session main and composer regions with consented sandbox surfaces that have independent lifecycle, recovery and a protected host return control. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts: # apps/web/src/components/session/game-view.tsx # docs/en/internals/security.md # docs/en/plugins/ui.md # docs/en/reference/plugins.md # docs/en/reference/protocol.md # docs/en/reference/ui-panels.md # docs/internals/security.md # docs/plugins/ui.md # docs/reference/plugins.md # docs/reference/protocol.md # docs/reference/ui-panels.md
…ection updates # Conflicts: # apps/server/tests/api/declarative-plugins.test.ts # apps/web/src/components/session/__tests__/plugin-view-consent.test.tsx # apps/web/src/components/session/plugin-view-host.tsx # docs/builtin/index.md # docs/builtin/world.md # docs/en/builtin/index.md # docs/en/builtin/world.md # docs/en/internals/flow.md # docs/en/reference/ui-panels.md # docs/internals/flow.md # docs/reference/ui-panels.md # tests/e2e/declarative-plugins.acceptance.ts # tests/e2e/ensemble-orchestration.spec.ts
Plugin theme packs could be validated and published, but the web client never applied them, so a pack had no way to reach a player's session. Each session can now select a validated theme pack from its appearance settings. The selection is stored through the existing SettingsStore helpers and applied only as the compiled, protected style output, never as raw plugin CSS. Selections are per session, follow session switches and reloads, and fall back to the default appearance when a pack is missing, disabled or invalid, with a host recovery control that stays reachable at narrow widths and in immersive stage mode. The stage view, dialogs, chat renderer and plugin panels consume the same protected theme layer, and the bundled scene stage ships a reading theme as the first adopter. Tests cover selection persistence, theme fallback, atomic rejection of malicious styles, and that appearance changes issue no action requests during a running turn. Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Departing actors remounted their media when the exit began because the actor and visual React keys changed. The loaded portrait was discarded, so it flashed a loading placeholder and could fail to fade out before the exit deadline. Keep the keys stable across the live-to-outgoing transition; generation tokens still guard cleanup. Adds regressions that render the real Media component with a deferred media resolution to prove the loaded image stays visible during departure. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Automatic read-baseline capture derived the guard target from proposal payload fields spread after the authoritative kind and source plugin, so a raw proposal carrying extra pluginId or kind fields could move the guard to a different target than the write it actually performed. Target identities are now built from explicit namespace and key fields plus the authoritative source identity, and batch items are handled the same way. Target extraction also assumed well-formed ops arrays, so a malformed auxiliary proposal threw during preparation and failed the whole story execution. Extraction now tolerates invalid shapes and leaves rejection to ordinary proposal validation, isolating the auxiliary unit. A PreStateCommit veto replaced a guarded proposal with an error entry and lost the guard marker, so a vetoed guarded increment could let a derived merge commit against an unchanged base. Guarded-batch status is now carried from the original proposals through screening, so the veto rolls back the whole batch with no writes or success fan-out. Adds regressions for each case, plus agent suspension/resuspension coverage and a source-turn scoped retry after a baseline conflict that asserts seed operations run exactly once. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Writes issued by session plugin surfaces could dispatch after consent was revoked, because the lease check ran before the asynchronous credential lookup in the HTTP wrapper. The lease predicate now travels with the request and is rechecked immediately before dispatch, covering runtime and action writes, interaction submissions and approval grants. Work that already reached the server still settles. The composed privacy regression now connects the mounted host adapter to the real page script and captures every port message, so private output, concealed descendants and failure results are verified to stay out of the bridge while public content still arrives. The oversized-interaction test uses a valid choice list and the real handle issuer, so it fails when the 16 KiB limit is removed. Failed application-script loads are detected through capturing error handling on the frame, and the fixed page-error signal recovers only the affected region instead of leaving an inert replacement mounted. Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
# Conflicts: # apps/web/src/components/session/game-view.tsx # apps/web/src/components/session/stage/StageChoices.tsx # apps/web/src/components/session/stage/StageView.tsx # apps/web/src/i18n/locales/en-US.json # apps/web/src/i18n/locales/ru-RU.json # apps/web/src/i18n/locales/zh-CN.json # docs/en/internals/security.md # docs/en/plugins/ui.md # docs/en/reference/plugins.md # docs/en/reference/protocol.md # docs/en/reference/settings-store.md # docs/en/reference/ui-components.md # docs/internals/security.md # docs/plugins/ui.md # docs/reference/plugins.md # docs/reference/protocol.md # docs/reference/settings-store.md # docs/reference/ui-components.md
…efreshes Disabling one provider or changing language reset both session regions and the selected bundled page, because a temporary loading state was treated as permanent removal. Numbered choice keys also stopped working for console choices because the public message wrappers were bypassed. Healthy, authorized regions now survive same-session discovery refreshes, and only the affected region retires on confirmed removal. Public turn wrappers are kept so numbering and hotkeys work for console choices. Acceptance runs now use the production build under the strict host CSP, switch sessions through the app for the stale-owner race, and restore their notebook package during cleanup. Plugin reload approvals keep each entry's reload generation so approved imports load the reloaded module. Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
# Conflicts: # apps/web/src/components/session/game-view.tsx # apps/web/src/components/session/stage/StageView.tsx
A plugin discovery refresh advanced the theme state generations and cleared the saving flag, so a theme selection or Off write that was still in flight lost its result. The shown selection could then differ from storage, and a failed Off write lost its warning about the theme returning after restart. Save ownership is now independent of discovery refreshes: the confirmed choice is reconciled after the write settles and failures are still reported. Browser coverage combines an active theme with real sandbox surfaces, consent and revocation at both widths, and checks that private and concealed content never reaches the page's message port during streaming, failure, retry and resume. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The combined theme and sandbox browser case forwarded plugin assets to the fixture host's own port, so the frame violated the host's frame-ancestors policy, loaded an error page and was retired by the readiness deadline. Fetch the fixture response and fulfill it at the original app URL, keeping its bytes and CSP, and assert that one connected frame survives consent. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
At 360px the view permissions menu opened from the right edge of a trigger near the left screen edge, so its revoke control rendered off-screen. Shared session menus now cap their width and clamp horizontally to the viewport gutters, including after resize. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Reopening the sandbox consent dialog briefly leaves its content without pointer events while the open animation runs, so an immediate hit test landed on the overlay. The helper now waits for pointer readiness and finished dialog animations before the same center hit test, and a dialog test covers reopening a consent dialog. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ider The combined theme and streaming privacy case never saw streamed story text because its fixture plugin lacked the narrative schema its runtime needs, so the runtime did not load, and the write-service provider shared a package with the page so refusal and retry could not be exercised. Declare the schema, split the provider, correct the streamed prefix expectation and pin the fixture with a server test. Every port message is still checked for private and concealed content. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On narrow screens the interrupted-turn, narrative and reconnect notices could take the whole center column, collapsing a sandboxed main view to zero height. The main view now keeps a 20rem minimum, the host composer sits outside it and the column scrolls. The acceptance test also waits for Stop to settle before releasing the held player action. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
11 of 12 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary / 摘要
This PR lets plugins own more of the session screen without reaching past the host. Session pages can request player actions through a consented bridge. Sandboxed pages can replace the main area or the composer, and recover on their own. DM Console, Relationship Graph and Codex move into declarative session views, and plugins can ship restricted theme packs that players opt into per session. Two kernel guards close older gaps: sub-agents read the caller's visible world, and read-derived replacements are rejected when same-key data changed since the read. Each topic arrived as its own merge commit, followed by merges of fixes from a review of the whole set. Review them one merge at a time.
Consented session bridge writes
Sandbox surface replacement
Bundled session views
SessionMessagesandSessionComposerlet declarative views show the public story, pending interactions and the shared composer, with current-turn choice numbering and keyboard selection.Plugin theme packs
contributes.ui.themepublishes localized palettes, shared layout presets and CSS compiled from a closed AST allowlist. Backgrounds use content-bound public bitmap resources only.Stage renderer follow-ups
CharacterAvatar.Kernel guards
conflictif the same key changed before commit. Payload fields cannot redirect the guard, hooks cannot erase it, and vetoing a guarded prefix rolls back the batch. Auxiliary conflicts drop only their unit; story text survives.Smaller fixes found on the way
aliasesandremoveAliaseson character upserts, matching the kernel.Type of change / 变更类型
Breaking changes / 破坏性变更
No database schema change or migration is needed. Committed values, characters, sessions and appearance settings stay as they are.
Verification / 验证方式
All on the final branch head, Linux x64, Node 26.12.1, pnpm 12.14.0.
pnpm checkpnpm test:pgagainst a throwaway PostgreSQL container (96)pnpm e2e:smoke(8),pnpm e2e:extensions(11),pnpm e2etwice: 187/187 both runspnpm lint,pnpm schemas:generate(no diff),pnpm test:docs,pnpm test:docs:packages, docs buildpnpm validate:pluginfor the changed bundled plugins and the third-party probespnpm e2e:verifyThe theme and sandbox browser specs also passed three repeats in a row. After the phone layout fix, the sandbox 360px spec passed 8/8 and the extension suite twice (22/22) with the browser pinned to two CPUs, the setup that reproduced the CI failure.
Not verified / 未验证:
electron-builder --dir) but refused to start without--no-sandbox.pnpm e2e:verify(real-model harness) was not run. Sub-agent world views and read baselines are covered by scripted-model runtime and server tests only.Related issue / context / 关联
Follows #475.
Docs sync / 文档同步
docs/reference/updated (ui-panels, ui-components, protocol, api, plugins, plugin-extensions, extension-points, hooks, tools, settings-store, theme-packages, transactions, world-model, world-time, generated schemas)docs/en/session-bridge-actions,sandbox-surfaces,bundled-session-views,plugin-theme-packs,stage-renderer-followups,subagent-world-view,proposal-read-baselines,session-menu-viewport,sandbox-main-viewport, plus an update todeclarative-pluginsAGENTS.mdupdated: shared owns the pure theme CSS policy and its parser dependency, the loader owns files, web owns rendering🤖 Generated with Claude Code