proposal(artifacts): managed dynamic React runtime - #3230
Draft
reachjalil wants to merge 27 commits into
Draft
Conversation
# Conflicts: # apps/app/src/components/chat/message-list.tsx # apps/app/src/react-app/domains/session/chat/session-page.tsx # apps/app/src/react-app/domains/session/surface/session-surface.tsx # ee/apps/den-api/src/mcp/agent.ts # ee/packages/den-db/drizzle/meta/0048_snapshot.json # ee/packages/den-db/drizzle/meta/_journal.json # packaging/docker/Dockerfile.den
Contributor
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Contributor
|
@reachjalil is attempting to deploy a commit to the Different AI Team on Vercel. A member of the Team first needs to authorize it. |
# Conflicts: # apps/app/src/components/chat/message-list.tsx # apps/app/src/react-app/domains/session/chat/session-page.tsx
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.
Important
Proposal only. This draft is a concrete implementation sketch for design discussion and validation. It is not a request to merge, is not marked ready for review, and should not be treated as committed product architecture.
Summary
Depends on #3054.
This proposal explores turning chat-native UI artifacts into editable, reusable React projects with a managed end-to-end lifecycle:
metric,summary, orcollectionchat shapes with explicit density and action budgets.Storage, execution, and scheduling model
This proposal deliberately separates mutable project source, immutable compiled builds, per-instance interaction state, human-owned settings, and chat history. A chat attachment is a small pinned reference; it is not another copy of the source tree or bundle.
Workspace-local filesystem layout
All project-owned material lives under the root of the workspace served by the owning OpenWork server:
artifacts/<slug>/artifact-builds/<slug>/<revision>/bundle.jscontains the compiled module;build.jsoncontains the pinned manifest, styles, data, schema, compiler receipt, and digests. A different result may never overwrite the same project revision.artifact-state/<slug>/<instance-id>.jsonartifact-settings.jsonbuilderSkillEnabledand per-project enable/disable overrides. Disabling authoring changes capability, not stored source, builds, or state.workspaceId,slug,projectRevision,buildDigest,instanceId,buildPath,stateRevision, and presentation metadata. The conversation therefore points to an exact immutable build instead of followinglatest.The routes resolve the workspace path first and instantiate the filesystem-backed service against that root. A workspace represented as remote is rejected by this server: dynamic artifacts must be managed by the OpenWork server that owns that workspace locally. Project paths are constrained to lowercase kebab-case slugs and the five declared files; traversal, unexpected files, symlinks present during access, null bytes, and oversized inputs are rejected.
The master UI Artifacts preference is separate from this tree. When signed out it is cached in the app preference record in local browser storage; when signed in it synchronizes through Den. The builder and individual project controls remain workspace-local in
artifact-settings.json.From editable files to a pinned chat card
POST /workspace/:id/ui-artifacts/:slug/buildperforms the gated build.POST /workspace/:id/ui-artifacts/:slug/publishcalls the same build path, initializes or reuses an instance only when it is pinned to the same project revision, and returns the attachment. If the exact revision already has a valid build, the service returns it instead of compiling again.A validation or compiler failure returns structured diagnostics and does not replace a previous successful build. The source remains editable and the last known-good pinned attachment remains valid. Changing any of the five files produces a different project revision; publishing that revision creates a new immutable build and normally a new instance. Existing conversation cards stay on their original revision and state.
How generated code executes
Generated source is never executed directly from the editable project directory and it never executes inside the OpenWork server or agent process. Execution happens client-side only when the host mounts a pinned chat card or editor preview:
buildPath, fetches the exact pinned build and instance state, and verifies the workspace, slug, project revision, build digest, instance ID, and host-owned presentation shape.sandbox="allow-scripts",referrerpolicy="no-referrer", an empty permissions policy, and CSP that denies network connections. The generated component receives no host DOM, cookies, credentials, filesystem, provider, arbitrary tool, or direct network capability.MessageChannel. The compiled bundle becomes an in-memory Blob URL and is dynamically imported withReact.lazy; React itself is injected by the host.{ data, state, runtime }. The narrow runtime exposesreplaceState(next)andinvoke(intentId, payload).replaceStatesends an optimistic-concurrency update using the currentstateRevision; the server serializes writes for that instance and rejects stale updates.invokecan address only an intent declared by the pinned manifest. The server validates its payload and state revision, then returns a prompt for the host to stage visibly in the composer. The artifact never sends the prompt, runs a tool, or performs an external effect automatically; the user reviews and sends it.Triggering, ordering, and “scheduling”
There is no cron scheduler, durable job queue, background refresh loop, or autonomous artifact process in this PR. Artifact work is event-driven:
Ordering is enforced close to the data being changed rather than by a global worker:
Compilation itself is request-driven and content-addressed. Concurrent callers may ask for the same revision, but the immutable revision directory and digest check converge them on the same stored result. A new source revision does not schedule a silent rebuild, republish old messages, or rerun an artifact. Time-based refresh or scheduled execution would require a separate authorized scheduler that explicitly calls these APIs; it is outside this proposal.
Compact chat-shape contract (July 29 follow-up)
Generated artifacts are cards, not web pages. The manifest no longer accepts arbitrary width, height, or resizable flags. It declares an inline-capable placement and exactly one host-owned shape:
metricsummarycollectionThe host, rather than generated CSS, owns those dimensions. The renderer removes the
artifact.resizebridge, bounds the iframe and document roots, exposesdata-openwork-artifact-shapefor density-aware styling, and verifies that the pinned build and chat attachment agree on shape. The schema also caps declared intent actions by shape.The injected Artifact Builder skill now tells an agent to:
+ N moreinstead of growing the card.The human-created starter and Launch Radar example now use the same bounded roots, compact controls, row limits, and remainder pattern as the validated standard artifacts.
Note
The linked 23-frame Fraimz was produced from the July 28 baseline before this compact-shape follow-up. It is retained as baseline lifecycle and isolation evidence and may show the earlier wider/resizable presentation. It has not been regenerated for commit
0c6040191.Complete experience walkthrough
Open the narrated 23-frame Fraimz on Vercel. Every image below is also stored independently in Vercel Blob under the PR-specific evidence path.
1. Managed workspace authoring
The Generated catalog lists reusable projects and makes the injected Artifact Builder skill a visible, human-controlled workspace capability. The skill is available to agents only while the switch is enabled.
2. Live artifact attached in chat
A deterministic code-mode project is compiled, published as an immutable revision, and attached directly to the conversation as a bounded React artifact. The host provides compact refresh and editor affordances without letting generated content expand beyond its declared chat shape.
3. Isolated renderer
The component renders through an opaque-origin iframe with
sandbox="allow-scripts",referrerpolicy="no-referrer", an empty feature policy, and CSPconnect-src 'none'. It receives no host DOM, credential, network, filesystem, provider, or arbitrary tool authority.4. Bounded persistent interaction
A user can interact within the bounded card.
Watch launchcrosses the narrow state adapter, produces a new persisted state revision, and stays scoped to this artifact instance without turning the artifact into a scrolling application.5. Reusable five-file project editor
Open editor reveals the reusable workspace project and its complete file system:
src/App.tsx,styles.css,data.json,data.schema.json, andartifact.json, together with Library/Editor navigation and the pinned revision.6. Source, contract, revision, and preview together
The editor keeps code and data contracts beside a live isolated preview. The visible revision identifies the exact immutable build attached to the conversation.
7. Reload, reopen, and reuse
Reloading preserves the original project revision and watched-launch state. The project reopens from the Artifacts library instead of being trapped in one message or one process lifetime.
8. Per-project lifecycle control
A project can be disabled independently. New builds are blocked while its source, editor, immutable revision, and last known-good preview remain available.
9. Human-created artifacts use the same model
Create artifact is not agent-only. It asks for a human-readable name and purpose, previews the durable workspace slug, and starts from the same safe React project contract.
10. Workspace and project controls are independent
The injected skill switch and per-project build switch are separate, visible controls. Restoring one does not silently rewrite the other or mutate existing conversation attachments.
11. Prompt-only agent bridge
Ask agent about launch riskinvokes only a manifest-declared intent. The host stages a prompt in the composer with artifact, instance, effects, confirmation policy, payload, and state revision. It does not send the prompt or execute tools or external effects automatically.12. Versioned manifest contract
artifact.jsondeclares protocol and API versions, React entrypoint, style/data/schema paths, an inline-capable host-owned shape, intents, typed arguments, effect policy, and confirmation policy. Arbitrary dimensions and self-resizing are rejected.13. Typed JSX and narrow runtime adapter
src/App.tsxis ordinary typed JSX. Data and state arrive as props; the injected runtime exposes only bounded state replacement and declared intent invocation. The component has no directfetch, host, filesystem, or tool surface.14. Presentation remains independently expressive
styles.cssseparates compact layout, color, responsive presentation, and interaction states from the component, manifest, and data contract. The artifact remains expressive and forever enhanceable inside its declared chat-card budget.15. Portable data file
data.jsonmakes rendered values inspectable and independently editable. Enhancements can update data without reconstructing the component or weakening the manifest boundary.16. Build-time JSON Schema boundary
data.schema.jsondefines required fields, types, bounds, arrays, and additional-property rules. Data must satisfy this contract before a component becomes a pinned build.17. Unsafe generated code is rejected
An attempted unbounded loop is saved as an editable project revision but cannot be published. The compiler returns a structured line-and-column diagnostic and preserves the last known-good isolated preview.
18. Safe enhancement creates a new immutable revision
Restoring the safe component and changing Apollo readiness from 94 to 97 publishes a distinct immutable revision. The editor retains a visible compiler receipt with build digest, byte size, and zero diagnostics.
19. Existing chat history stays pinned
The original chat card remains on its original revision and state even after the reusable project evolves. Project enhancement never silently rewrites the history of a conversation.
20. Disable the enhanced project without data loss
Disabling the enhanced project blocks new publication while retaining the enhanced source, exact revision, editor, and last known-good preview.
21. Disable injected authoring without deleting work
Disabling the workspace Artifact Builder removes injected instructions from future agent context. Reusable projects remain listed, and already-pinned artifacts continue to render and interact.
22. Generated and standard artifacts coexist
Generated React projects and validated standard answer cards remain separate catalogs. Workspace brief, calendars, widgets, communication, inbox, attention, and approval artifacts retain their existing independent lifecycle controls.
23. Local-first master artifact preference
The master UI artifacts control remains usable without a cloud account. Signed-out workspaces persist the enabled state on this device and restore it after reload; signed-in workspaces synchronize the same setting to the active organization.
Integration and trust boundaries
Verification
July 29 compact-shape follow-up
0c6040191(level 0).git diff --checkpassed.July 28 baseline evidence
The earlier evidence pass:
git diff --checkpassed.Baseline implementation checks run before the compact-shape follow-up:
The app build retains the existing large-chunk warning.
Alpha security boundary
The generated component boundary is capability-isolated, not a CPU or memory quota. Obvious unbounded loops and unsafe globals are rejected, but pathological code can still consume renderer resources, so this alpha is not for arbitrary untrusted third-party JSX.
Filesystem authoring follows the trusted local-workspace model. Traversal and symlinks present during access are rejected, but Node path APIs cannot eliminate a same-user symlink-swap race without portable descriptor-relative filesystem operations. A concurrently hostile process with the same OS account is therefore outside this alpha boundary; generated components never receive filesystem access.
Proposal questions before adoption
metric,summary, andcollectionthe right durable shape vocabulary and size budgets for chat?