源码快照 @ 2026-09-02 (source-only, 剔除非源码素材)

This commit is contained in:
agent-radar
2026-09-02 01:05:41 +08:00
commit 04bac1f6e2
2200 changed files with 489415 additions and 0 deletions
@@ -0,0 +1,67 @@
# Creation-owned Automation runtime placement
## Outcome
The surface that creates an Automation owns its execution placement for the
Automation's lifetime:
- Desktop creation produces a `desktop` Automation and continues to use the
authenticated desktop runner introduced by the existing Automations work.
- Web and Cloud Chat creation produce a `cloud` Automation. Den schedules the
occurrence, wakes the owner's existing OpenWork Cloud container when it is
stopped, and runs a native OpenWork thread headlessly inside that container.
Both surfaces read the same Den Automation and run history. Placement is shown
on list cards and receipts, but it is not an editable setting. Moving execution
between Desktop and Cloud requires creating a new Automation on that surface.
## Cloud lifecycle
A Cloud agent run reserves and persists its worker, workspace, native thread,
and deterministic user-message identity before it submits the prompt. Lease
recovery reattaches to that thread and observes-or-submits that exact message
instead of starting duplicate work. Den heartbeats the run while it waits,
requires an observed idle state after forwarding cancellation, records native
assistant/tool events and usage, and keeps active Cloud runs out of idle-stop
selection. Run deadlines include container wake-up and request admission.
Cloud creation requires the current owner to have an existing per-user Cloud
worker and an authorized model. It does not silently allocate a new Cloud
environment. A stopped worker is valid and is woken at execution time; a
missing or failed runtime is recorded as a durable failure and moves the
Automation to needs-attention. Timeouts are terminal receipts rather than
automatic retries because an agent may already have produced external side
effects; the user can inspect the native run and choose Run now explicitly.
Runtime readiness polling retains its pre-existing 120-second cap and terminal,
non-retryable outcome; signed-preview routing does not add another queued attempt.
Before prompt admission, Den probes and repairs the worker's persisted
`openwork-cloud` MCP registration for the selected model. A missing Connect
configuration or model projection becomes durable needs-attention rather than
a headless run that silently lacks the user's connected services.
The native thread client uses both the collaborator token and the internal
host credential when it talks directly to the resolved Cloud runtime. No provider
credential or worker token is copied into the Automation receipt.
## Relationship to Workflow Automations
Workflow Automations from the Workflow Artifacts work remain Cloud-owned
and keep their existing Den Code Mode executor. This feature adds ordinary
agent actions as a second Cloud execution kind; it does not change Script
snapshot validation or artifact result retention.
## Verification boundary
Focused tests cover the Cloud-only MCP creation contract, deterministic prompt
admission, terminal errors, cancellation observation, dual worker credentials,
Cloud wake/idle lifecycle behavior, runtime-specific agent guidance, and
cross-surface execution labels. Den Web provides once/daily/weekly create and
edit flows plus activate/deactivate, archive, run-now, cancel, native thread,
usage, and event receipt controls. The testkit acceptance tape covers the
Web/Desktop creation language and verifies that no placement picker is exposed.
The deployment-shaped Daytona journey—stop a real user's container, let a due
occurrence wake it, execute against live model and Connect configuration, then
observe idle shutdown again—remains required for the production rollout and
merge decision. It is intentionally not claimed by local or mocked proof.
@@ -0,0 +1,51 @@
# Desktop runner for Den-scheduled Automations
## Outcome
Den remains the durable scheduler and source of truth for Automation schedules,
occurrences, leases, notification cursors, ordered events, usage, and terminal
results. Den no longer launches OpenCode or stores an Automation execution
runtime in its container.
An authenticated desktop installation registers as a `desktop` runner and
maintains a Server-Sent Events stream. SSE carries only a wake-up type and a
resumable notification cursor. After connecting or receiving a wake-up, the
desktop discovers work over HTTP, atomically claims one occurrence, and reports
attempt-bound heartbeats, ordered events, cancellation, usage, and completion.
When a runner has no pending notifications, Den exponentially backs its cursor
query off from one second to 15 seconds. The poll resets to one second after
activity, and its sleep is capped by the existing 15-second presence keepalive.
This preserves the resumable SSE and bounded claim protocol while reducing an
idle runner's steady-state notification queries from 60 to four per minute.
The claimed Automation runs as a normal visible local OpenWork thread in the
desktop's active workspace. It uses the selected model and the same local
OpenCode tool and integration experience as a thread started by the user.
## Offline behavior
Scheduled desktop occurrences have a bounded claim window. If no eligible
desktop claims one before its deadline, Den durably records `skipped` with
`runner_unavailable`; the app displays **Missed — desktop runner unavailable**.
Run now fails immediately with **No desktop runner is online** while no desktop
SSE connection is present.
## Focused proof
The 2026-08-04 packaged macOS candidate was connected to the local Den test
profile and produced both required outcomes:
- `Desktop visible thread E2E 08:45:31` succeeded as a normal sidebar thread,
with result `DESKTOP_VISIBLE_THREAD_OK`, ordered user/assistant/usage/terminal
events, and token usage stored in Den.
- `Disconnected desktop final 08:46:51` was not claimed and became the explicit
missed desktop-runner-unavailable receipt. Run now while disconnected returned
the explicit no-runner-online response.
## Deferred hardening
Repository-wide suites, cross-platform packaged proof, multi-replica online
presence, exhaustive reconnect/race testing, fault injection, and broader
security review remain follow-up work. A future `sandbox` target can extend the
execution-target contract; it is not implemented here.
@@ -0,0 +1,91 @@
# Workflow Artifacts as MCP Apps
OpenWork exposes Workflow results as portable, standards-based
MCP Apps. A supporting MCP host can render an artifact inline; every other MCP
client still receives a useful Markdown result.
## Product flow
1. Code Mode produces data and a deterministic Markdown rendering.
2. The result can be saved as an immutable Workflow snapshot with a receipt,
schema validation, result digest, and exact Workflow version.
3. A person or an Automation may run the Workflow again. Automations only
refresh the data snapshot; they do not create or execute UI code.
4. The read-only `render_workflow_artifact` tool loads the latest successful
snapshot, or an exact receipt when requested.
`render_dynamic_artifact` remains as a deprecated alias for one release.
5. MCP Apps hosts resolve the linked `ui://` resource and inject the tool's
structured result. Other hosts show the Markdown fallback.
This keeps execution, scheduling, data, and presentation separate:
```text
Workflow ──run──> immutable snapshot ──read──> MCP tool result
▲ ▲ ├─ Markdown fallback
│ │ └─ ui:// MCP App
explicit run Automation refresh
```
The MCP App never runs a Workflow, mutates an artifact, or introduces another
scheduler. It only presents an already-authorized snapshot.
## MCP Apps conformance
The agent MCP server implements the `io.modelcontextprotocol/ui` extension and
the `2026-01-26` MCP Apps protocol:
- server capability: `extensions.io.modelcontextprotocol/ui.mimeTypes`
contains `text/html;profile=mcp-app`;
- tool metadata: `_meta.ui.resourceUri` points to
`ui://openwork/workflow-artifact/v1/view.html` (with the compatibility metadata
emitted by the official MCP Apps server helpers);
- resource delivery: `resources/read` returns one self-contained HTML5 document
with MIME type `text/html;profile=mcp-app`;
- view lifecycle: the document performs `ui/initialize`, sends
`ui/notifications/initialized`, receives tool input/result/cancellation and
host-context notifications, reports size changes, and acknowledges teardown;
- data delivery: the tool returns versioned `structuredContent`, lightweight
lineage in result `_meta`, and a complete text fallback;
- security: the resource declares an empty network/frame/base-URI CSP, loads no
external code, performs no network requests, and inserts artifact values with
DOM text APIs rather than HTML interpolation.
The shared payload schema lives in `@openwork/types/workflows` so a host
can validate the data contract independently of this presentation resource.
## UI behavior
The self-contained view is intentionally data-first and framework-neutral. It
adapts the Preview tab to common result shapes:
- arrays of records become a bounded table;
- flat objects become metric cards;
- nested or irregular values fall back to formatted JSON;
- Data always exposes the full structured result, with a rendering-size guard;
- Lineage shows the immutable receipt, Workflow/version IDs, source, digest,
renderer, and Automation run when present.
The view follows host theme/style context, uses responsive HTML, and caps large
tables and rendered JSON without changing the underlying tool result.
## Authorization and failure behavior
The renderer uses the same organization membership, team grants, and saved
Workflow authorization path as the existing artifact APIs. It does not accept a
plugin or result payload from the caller. The caller supplies only a saved
Workflow ID, an optional exact receipt, and an optional freshness threshold.
Only readable, successful, non-deleted snapshots can be rendered. Missing,
failed, deleted, or unauthorized results fail closed with a non-sensitive text
error. A stale snapshot remains renderable and is labeled stale; a failed
refresh leaves the last successful artifact available with a needs-attention
state.
## Next interoperable slice
The provider side is deliberately usable without an OpenWork-specific host.
The next slice is a generic MCP Apps host in the desktop conversation surface:
negotiate the UI extension with upstream MCP servers, preserve tool/resource
metadata and structured results through the runtime, sandbox `ui://` resources,
and bridge the standard JSON-RPC lifecycle. That host should consume the same
standard contract rather than adding a custom React artifact format.
@@ -0,0 +1,52 @@
# Local managed MCP OAuth
OpenWork desktop can own OAuth for a custom remote MCP and expose its tools to
the bundled OpenCode engine through an authenticated loopback MCP gateway. This
provides a compatibility path for providers whose OAuth flow works through
OpenWork's enterprise MCP client but not through OpenCode's direct MCP client.
## User flow
1. In a local desktop workspace, add a remote MCP and expand **OpenWork-managed
OAuth**.
2. Optionally enter a pre-registered client ID, client secret, and scopes. If
the provider supports dynamic client registration, those fields may remain
empty.
3. OpenWork performs OAuth discovery, DCR when needed, PKCE authorization, the
loopback callback, token exchange, and an authenticated `tools/list` check.
4. OpenCode receives a remote MCP entry pointing at the OpenWork loopback
gateway with `oauth: false`. It sees the provider tools but never receives
the provider access token, refresh token, or OAuth client secret.
Existing direct remote MCPs and local command MCPs keep their current paths.
The managed path is opt-in per connection and is currently desktop-only.
## Persistence and lifecycle
- Provider credentials, OAuth registrations, discovery state, and PKCE
transactions are encrypted with AES-256-GCM in OpenWork's runtime storage.
- OpenWork Desktop keeps the encryption key behind the operating system's
secure-storage service and persists only the protected key blob, separately
from the encrypted vault. A standalone server must set
`OPENWORK_ENCRYPTION_KEY`; there is no plaintext key-file fallback.
- The gateway bearer is scoped to a workspace and connection, generated from a
process-only secret, and rotated on every OpenWork server restart.
- Startup reconciliation rewrites managed runtime MCP entries with the current
loopback port and bearer while keeping the encrypted provider credential.
- Disconnect deletes the stored credential and disables the gateway entry.
## Network boundary
Outside explicit local development, managed providers must use HTTPS and
resolve only to public addresses. Redirects are revalidated, HTTPS downgrade is
blocked, and credential-bearing request bodies cannot cross origins. This
prevents a configured MCP URL or redirect from turning the desktop gateway into
an internal-network request proxy.
## Verification
Focused server coverage proves discovery and DCR, PKCE authorization, encrypted
credential persistence, OpenCode runtime registration, gateway tool listing and
invocation, refresh-token recovery, restart reconciliation with bearer
rotation, disconnect, and the outbound URL guard. The desktop and server
TypeScript projects and the compiled embedded server build are also checked.
+61
View File
@@ -0,0 +1,61 @@
# MCP Apps inline host
OpenWork Desktop can render the standard MCP Apps UI attached to a completed
MCP tool call. The normal text result remains visible and usable if the view is
absent, unsupported, or fails to initialize.
## Runtime flow
1. OpenCode calls the configured MCP tool once.
2. OpenWork's bundled engine plugin preserves that call's standard `content`,
`structuredContent`, and result `_meta` fields in the completed tool part.
3. The desktop asks its local OpenWork server to find the original projected
tool on the configured MCP server.
4. The server advertises the `io.modelcontextprotocol/ui` extension, reads the
tool's `_meta.ui.resourceUri`, and resolves the `ui://` resource through
`resources/read`.
5. The conversation mounts the returned `text/html;profile=mcp-app` resource in
an opaque sandboxed iframe and delivers the original tool input and result
over the official MCP Apps bridge.
The host never replays the originating tool call to reconstruct structured
data. MCP tools that return only standard text content can still attach a view.
## Security boundary
- Resource markup runs in an opaque `sandbox="allow-scripts"` iframe with no
referrer, forms, popups, same-origin access, top navigation, downloads, or
device permissions.
- The host injects a deny-by-default CSP before resource markup. It accepts at
most 16 fixed HTTPS origins per declared source list, with loopback HTTP
allowed for local development.
- Remote MCP connections must use HTTPS, except for loopback HTTP. Configured
MCP headers and credentials remain in the local server and are never exposed
to the iframe.
- Resource HTML is limited to 512 KiB, proxied tool results to 1 MiB, and tool
discovery to 2,048 tools across at most 32 pages.
- A view may call only tools from its originating configured MCP server. The
target must be visible to apps, explicitly `readOnlyHint: true`, not
destructive, and allowed by the workspace's MCP tool policy.
- Dedicated sandbox origins, camera, microphone, geolocation, clipboard
access, sampling, host messaging, external link opening, downloads, and
write-capable tool calls are not granted by this host slice.
## Current compatibility
The resource resolver currently supports configured remote Streamable HTTP MCP
servers, with SSE fallback for legacy remote servers. It does not yet resolve
resources directly from command/stdio MCP entries. OpenWork-managed OAuth
connections also require a follow-up adapter so resource discovery can reuse
their encrypted server-side credential path; ordinary remote connections with
configured server-side headers work today.
## Verification
Focused server tests cover extension negotiation, projected tool naming,
resource resolution, tool visibility, read-only same-server mediation, and
write rejection. App tests cover result preservation, session mapping, CSP
construction, and safe HTML injection. The Testkit scenario
`mcp-app-inline-host.e2e.test.ts` drives a deterministic OpenCode tool call,
resolves the declared `ui://` resource, mounts the sandboxed view, and captures
the visible inline card and fallback transcript.
+138
View File
@@ -0,0 +1,138 @@
# Native MCP Apps
OpenWork supports MCP Apps delivered by standard MCP servers connected through
OpenWork Connect. A server advertises the stable
`io.modelcontextprotocol/ui` extension, a tool binds an exact UI resource with
`_meta.ui.resourceUri`, the host reads that resource with `resources/read`, and
tool inputs and results move over the standard MCP Apps bridge.
This unit of value does **not** include installing a standalone App from an
HTML URL. URL-imported MCP Apps are deferred future work with a separate
product, security, lifecycle, and rollout contract.
Workflows remain executable `workflow` config objects. A Workflow's generated
views can be MCP resources, but that does not turn Workflow execution into
resource loading or a standalone URL-App installation path.
## Standard MCP server path
Connect continues to own server configuration, authentication, access grants,
per-member credentials, and tool policy. The OpenWork Cloud control server
publishes a member-scoped resource at:
```text
openwork://connect/mcp-servers/index.json
```
The signed-in Desktop session mints a separate short-lived App-host credential
with a non-public `mcp:app-host` scope. Desktop stores that credential and the
endpoint descriptors only in private App-host state; neither is projected into
OpenCode. It reads the index with that credential and advertises the
`mcp-app-host-v1` client capability. Den returns a non-empty provider index only
when the server-verified scope, client capability, and both rollout gates are
present. A normal model or legacy MCP token cannot unlock the index by spoofing
an audience or capability header. Desktop never writes `openwork-connect-*`
entries to the OpenCode runtime or any model-visible MCP registry. A connection
is proxied at:
```text
/mcp/agent/connections/{connectionId}
```
Current Desktop clients never register these provider descriptors in OpenCode;
the model discovers and invokes ordinary provider operations only through the
central `openwork-cloud` `search_capabilities` and `execute_capability` tools.
For stale published clients that still retain an old per-connection entry, the
proxy exposes only a compatibility pair named `search_capabilities` and
`execute_capability`. It never returns the provider catalog, MCP App launch
tools or metadata, resources, or templates, and it rejects every direct
provider call. This keeps ordinary operations bounded while old entries are
removed during reconciliation.
Desktop's local App host authenticates with the private scoped credential. Only
that transport receives tools with a valid `_meta.ui.resourceUri` whose
provider-declared visibility includes `app`, plus app-visible `search_capabilities` and
`execute_capability` scoped to the originating server. A native MCP App can
therefore render and use authorized tools from its regular MCP server without
placing the provider catalog in the model request.
The app-host view preserves:
- the App tool's exact name, input/output schemas, annotations, and UI binding;
- concrete descriptors and `resources/read` content only for resources bound
by exposed App tools;
- `content`, `structuredContent`, `_meta`, and `isError` from `tools/call`;
- the stable MCP Apps extension and `text/html;profile=mcp-app` resources;
- one server identity per Connect connection, preserving the same-server
tool-call boundary.
OpenWork access grants, disabled-tool policy, and approval rules still apply at
the proxy boundary. The App-host credential authorizes only this bounded proxy
surface; it is not a provider credential and grants no direct cross-server
access.
## Rollout
Native MCP Apps are enabled for every deployment and organization. The former
deployment gate (`DEN_REMOTE_MCP_APPS_ENABLED`) and the per-organization
**Native MCP Apps (preview)** capability were removed once the feature
stabilized; stale stored organization overrides are ignored. App launch
metadata (`kind: mcp_app`, `mcpApp.resourceUri`, `openwork/mcpApp` meta) is an
opaque binding published on every bounded search/execute result; clients that
do not host Apps ignore it and keep the normal tool result. The private
App-host index and per-connection provider proxy remain gated on the
client-advertised App-host capability header, so older Desktop clients keep
their bounded search/execute surface. Reconciliation also removes and
disconnects stale
`openwork-connect-*` OpenCode entries while preserving user-authored MCPs and
all durable Connect records.
The provider proxy advertises `listChanged: false` because the current
enterprise connector opens bounded request sessions rather than a durable
downstream notification stream. Catalog refresh happens on Connect
reconciliation, Desktop startup, engine refresh, or an explicit Cloud MCP
refresh. Forwarding downstream list-change notifications remains follow-up
interoperability work.
## Deferred: standalone URL-imported Apps
Installing a self-contained HTML App from a URL is intentionally outside this
change. In the current product:
- Den Web has no Add MCP App button, URL form, installed-App detail page, or
URL-App lifecycle entry point;
- the central MCP server does not register `import_remote_mcp_app` or any
standalone-App launch tool;
- capability search returns no standalone URL-App matches;
- model and App-host catalogs contain no standalone URL-App tools;
- no `ui://openwork/library-apps/...` resources are registered;
- member server indexes and launch metadata contain no standalone URL Apps;
- REST calls under `/v1/remote-mcp-apps` are not registered and are therefore
unavailable.
Existing database rows and cached revisions from earlier development remain
stored non-destructively. They are inactive and unreachable through the UI,
MCP catalogs, capability search, resources, launch metadata, and HTTP API. This
change performs no deletion and introduces no destructive migration.
The retained storage and validation implementation is not a supported runtime
surface. A future standalone URL-App unit of value must deliberately restore
its own API, UI, security review, lifecycle, testing, and rollout contract.
## Host security and compatibility
Desktop negotiates the stable extension, resolves the current tool definition,
reads the exact `ui://` resource even when it is absent from `resources/list`,
accepts text or base64 HTML, enforces MIME and size limits, validates CSP
origins, and loads the document through the isolated sandbox proxy. It sends
tool input and the preserved tool result after initialization, bounds size
changes, tears the bridge down on unmount, and contains resolution, handshake,
document, and runtime failures without hiding the normal tool result.
App-requested tools are resolved only on the originating regular MCP server
and must be visible to the app. When a tool carries `_meta.ui.resourceUri`,
Desktop also requires it to match the exact resource loaded in the calling
iframe. Workspace denies apply. Read-only capability search runs directly;
capability execution uses conservative mutation annotations and requires user
confirmation while provider authorization and audit still run server-side.
Cross-server iframe calls are not allowed.
@@ -0,0 +1,110 @@
# Native MCP Apps local demo
This runbook proves the native MCP Apps path with a deterministic local fixture.
It does not depend on a hosted gallery, production rollout flags, customer
connectors, or personal credentials.
The required fixture is Project Atlas from
`evals/specs/remote-mcp-apps.e2e.test.ts`. It runs as a local Streamable HTTP MCP
server, exposes `ui://project-atlas/view.html`, and has a same-server
`search_projects` operation. The tape starts real local Den API and Web
processes, a real Electron Desktop/local-server process, MySQL, Redis, and a
synthetic model server.
## Prerequisites
- Node.js 24 and pnpm 11.4.0 (the versions declared by the repository)
- Bun, Docker, and an available local MySQL port (`3306`)
- no real model/provider credentials in the shell
From a clean checkout of the exact branch under test:
```bash
pnpm install --frozen-lockfile
pnpm --dir evals install --frozen-lockfile
pnpm --filter @openwork/types build
pnpm --filter @openwork-ee/den-db build
pnpm --filter @openwork/email build
pnpm dev:den:mysql
```
## Run the exact-head demo tape
```bash
OPENWORK_EVAL_E2E_TESTS=1 pnpm evals:e2e remote-mcp-apps
```
A valid required proof ends with one passing test, zero failed tests, zero
skips, and `"verdict":"passed"`. Keep the JSON report path printed by the
runner with the local verification record.
The tape performs this demo sequence:
1. Starts an isolated Den organization, synthetic member, local Project Atlas
MCP server, and Desktop profile.
2. Adds Project Atlas as an organization Connect MCP server and waits for it to
become ready.
3. Confirms the model-visible surface contains only the central
`search_capabilities` and `execute_capability` tools.
4. Confirms stale per-connection compatibility exposes only that same bounded
pair, no resources/templates/App metadata, and no direct provider tools.
5. Confirms the private App host receives only the originating server's
app-visible tools and exact `ui://project-atlas/view.html` resource.
6. Searches and executes `search_projects` through the originating server's
App-host capability pair and observes the synthetic Atlas migration result.
7. Prompts the synthetic model to return the bound tool result, renders the App
in Desktop, reloads it, and confirms it renders again.
8. Restarts Desktop with the same isolated profile, revisits the session, and
confirms the App recovers.
Native MCP Apps are enabled for every deployment and organization; no
deployment or organization gate needs to be flipped for this demo.
## Focused security and compatibility checks
```bash
cd apps/server
bun --conditions=development test \
src/connect-mcp-server-catalog.test.ts \
src/mcp-app-host.test.ts \
src/mcp-app-sandbox.test.ts \
src/cloud-mcp-reconcile.e2e.test.ts
cd ../app
bun test \
tests/den-mcp-url.test.ts \
tests/mcp-app-frame.test.ts \
tests/session-mcp-maintenance.test.ts \
tests/cloud-mcp-maintenance-gate.test.ts
cd ../../ee/apps/den-api
bun test \
test/remote-mcp-app-rollout.test.ts \
test/external-connection-proxy.test.ts \
test/admin-organization-capabilities.test.ts \
test/organization-capabilities.test.ts
```
These checks cover exact trusted-origin matching, cross-origin rejection,
origin-bound App-host credentials, App-host credential privacy, sandbox/CSP
enforcement, same-server binding, rollout defaults, older responses without an
App-host credential, bounded stale-client compatibility, and preservation of
ordinary Connect when Apps are disabled.
## Expected failure categories
- A skipped tape is incomplete proof. Check the consent environment variable,
local placement, and MySQL availability.
- `missing local server credentials` is a Desktop startup/readiness problem.
- A resource error names the exact URI, MIME, size, or CSP validation failure.
- An origin error indicates that the Den/App-host endpoint pair is not an exact
trusted-origin match.
- A tool denial indicates wrong-server routing, app visibility, workspace
policy, provider authorization, or required mutation confirmation.
- A client that does not advertise the private App-host capability keeps the
bounded search/execute surface; that is expected, not a resource-loading
failure.
The hosted SOL gallery may be checked separately as an external observation,
but its reachability is not OpenWork compatibility proof and it is not part of
this required demo.