* feat(daemon): persist session artifact metadata * fix(daemon): address artifact restore review findings * fix(daemon): harden artifact persistence restore * fix(daemon): align artifact persistence review decisions * fix(daemon): address artifact persistence review gaps * fix(daemon): harden artifact persistence recovery * fix(daemon): align artifact ownership capability * fix(daemon): preserve marker identity during fork * fix(daemon): roll back durable replacement removals * fix(daemon): surface artifact rollback warnings * fix(daemon): surface restore warning details * fix(daemon): preserve artifact marker metadata safely * fix(daemon): sanitize fork marker metadata * fix(daemon): harden artifact restore boundaries * fix(daemon): omit orphaned sticky snapshot markers * fix(daemon): preserve artifact tombstone and rewind warnings * fix(daemon): address artifact fork review blockers --------- Co-authored-by: 秦奇 <gary.gq@alibaba-inc.com> Co-authored-by: 易良 <1204183885@qq.com> Co-authored-by: qwen-code-dev-bot <qwen-code-dev-bot@users.noreply.github.com> |
||
|---|---|---|
| .. | ||
| src | ||
| package.json | ||
| README.md | ||
| tsconfig.json | ||
| vitest.config.ts | ||
@qwen-code/acp-bridge
Shared ACP bridge primitives consumed by qwen serve, channels, IDE, TUI,
and remote-control adapters. Lives in the monorepo, not published to npm.
Lift history (#4175 Mode B daemon roadmap):
| Slice | Scope | Status |
|---|---|---|
| PR 22a (#4295) | Skeleton + EventBus + inMemoryChannel + AcpChannel types + PermissionMediator type-only stub |
✅ merged |
| PR 22b/1 (#4298) | Lift status + workspacePaths + bridgeErrors + bridgeTypes |
✅ merged |
| PR 22b/2 (#4304) | Lift BridgeOptions + new DaemonStatusProvider injection seam |
✅ merged |
| F1 (this PR) | Lift defaultSpawnChannelFactory + BridgeClient + createHttpAcpBridge factory closure + new BridgeFileSystem injection seam (22b' scope) |
✅ in this PR |
| F3 PR 24 | Implement the four PermissionMediator strategies (first-responder, designated, consensus, local-only) + pair-token revocation + audit log |
F3 in the feature-cohesive plan |
What's here today
eventBus— per-session NDJSON pub/sub with bounded ring replay,Last-Event-IDreconnect, and slow-client backpressure (slow_client_warning→client_evicted).inMemoryChannel— paired NDJSON streams without spawning a child; used for in-process bridge tests and the parked Mode A (qwen --serve) path.channel—AcpChannel/AcpChannelExitInfo/ChannelFactorytype contract thatcreateHttpAcpBridge(now in this package) plus the channels / VSCode IDE companion's own-spawn paths consume viaBridgeOptions.channelFactory.permission— type-onlyPermissionMediatorinterface,PermissionPolicyliteral union (4 strategies), andPermissionResolutiondiscriminated union. No implementation yet — first-responder voting still lives inBridgeClient.requestPermission(inbridgeClient.tsafter F1). F3 PR 24 will move that and add the other three policies behind this interface.status(PR 22b/1) — wire-contract status types for/workspace/{mcp,skills,providers,env,preflight}and/session/:id/{context,supported-commands,tasks}routes, theSTATUS_SCHEMA_VERSION/SERVE_*_EXT_METHODSconstants,BridgeTimeoutError/MissingCliEntryError/BridgeChannelClosedErrortyped exceptions, and themapDomainErrorToErrorKindclassifier (regex →instanceofafter #4299 / #4300). The 27-symbol contractacp-integration/acpAgent.tsconsumes lives here.workspacePaths(PR 22b/1) —canonicalizeWorkspace(the cross-module BX9_q contract used byconfig.ts/settings.ts/sandbox.ts/ bridge to collapse boot-time + per-request workspace paths to one canonical key) plusMAX_WORKSPACE_PATH_LENGTH.bridgeErrors(PR 22b/1) — 11 typedErrorsubclasses the bridge throws (SessionNotFoundError,WorkspaceMismatchError,RestoreInProgressError, etc.); HTTP route layerinstanceof-branches on these to map to specific status codes.bridgeTypes(PR 22b/1) — public bridge contract types:BridgeSpawnRequest,BridgeSession,BridgeRestoreSessionRequest,BridgeSessionState,BridgeRestoredSession,BridgeSessionSummary,SessionMetadataUpdate,BridgeClientRequestContext,BridgeHeartbeatResult,BridgeHeartbeatState, plus theHttpAcpBridgeinterface itself (~30-method facade).bridgeOptions(PR 22b/2) —BridgeOptionsinterface (factory construction contract:boundWorkspace,channelFactory,maxSessions,eventRingSize,permissionResponseTimeoutMs, persistence callbacks, etc.) plus theDaemonStatusProviderinjection seam for daemon-host env / preflight cells (production impl incli/src/serve/daemon-status-provider.ts) and the F1BridgeFileSysteminjection seam for the ACP fs proxy.spawnChannel(F1) —defaultSpawnChannelFactory+killChild+SCRUBBED_CHILD_ENV_KEYSdenylist +scrubChildEnvpure env-policy helper (exported for adapter reuse + unit-test access; isolates the scrub + override + defense-in-depth ordering invariant the security argument relies on). Production spawn of theqwen --acpchild with stderr prefix-and-forward, kill cascade, and env passthrough. Channels (packages/channels/base/AcpBridge.ts) and the VSCode IDE companion consume this directly instead of each reimplementing the child lifecycle.bridgeClient(F1) —BridgeClientclass implementing the ACPClientsurface: first-responder permission flow, session-update fan-out intoEventBus, child-sideextNotificationrouting, early-event buffer + tombstone bookkeeping, inline fs proxy forwriteTextFile/readTextFile. Exports the supportingPendingPermission/PermissionResolutionRecord/BridgeClientSessionEntrytypes +MAX_RESOLVED_PERMISSION_RECORDScap that the factory's bookkeeping maps consume.bridge(F1) —createHttpAcpBridgefactory closure (~3000 LOC)ChannelInfo/SessionEntryinterfaces + factory-only helpers (withTimeout,canonicalizeExistingAncestor,verifyParentWithinWorkspace, debug log helpers,hasControlCharacter) + factory constants. Builds the bookkeeping closures (resolveEntry,registerPending, etc.) and wires them intoBridgeClient.
bridgeFileSystem(F1) —BridgeFileSysteminterface for the ACP fs proxy. When wired throughBridgeOptions.fileSystem,BridgeClient.readTextFile/BridgeClient.writeTextFiledelegate to it instead of the inlinefs.realpath/fs.writeFile/fs.readFileproxy. Productionqwen servefollow-up wraps PR 18'sWorkspaceFileSystemhere so writes get TOCTOU + symlink + trust-gate + audit guarantees.
Imports — root vs subpaths
The package exposes both a barrel root (@qwen-code/acp-bridge) and
per-module subpaths (/eventBus, /inMemoryChannel, /channel,
/permission, /status, /workspacePaths, /bridgeErrors,
/bridgeTypes, /bridgeOptions, /spawnChannel, /bridgeClient,
/bridge, /bridgeFileSystem). They re-export the same symbols, so
either form resolves to the same module at runtime. Pick by intent:
- Root for application/test code that uses several primitives at
once — concise and matches how
serve/imports landed today. - Subpaths for client adapters (TUI / channels / IDE / future
remoteControl) that only consume one slice — keeps the dependency surface explicit and lets bundlers tree-shake the rest.
Both variants are stable across the F1 lift.
Backward compatibility
CLI code imports event-bus and in-memory channel primitives directly from
@qwen-code/acp-bridge/eventBus and
@qwen-code/acp-bridge/inMemoryChannel.
packages/cli/src/serve/acp-session-bridge.ts remains as the CLI-local
compatibility facade for the broader bridge surface, forwarding previously
exported symbols (createHttpAcpBridge, defaultSpawnChannelFactory,
BridgeClient, typed errors, and type aliases) from the lifted subpaths.
See also
- #4175 Mode B daemon roadmap (feature-cohesive F1-F5 plan targeting
daemon_mode_b_main) - #3803
Stage 1.5-prereq AcpChannel lift(chiga0's original framing) - F3 PR 24 will replace the inline first-responder logic in
BridgeClient.requestPermissionwith the fourPermissionMediatorstrategies declared inpermission.ts.