Both ds-review-bot criticals verified and fixed: - ClientActivityFeed release closures resolved the entries map at release time, not the entry they were minted for. A late release of a superseded observation decremented (and could tear down) the successor's stream, and the post-dispose model clear could land after a re-observation inside the dispose round-trip re-anchored the view — blanking the panel until the popover reopened (React StrictMode's double effect hits the same window). Releasers now capture their entry, reconcile the map only when it still holds that entry, and skip the view clear whenever a successor owns the key. Both regressions fail on the previous code. - The background observation tap mapped the process outcome as soon as the pump settled — including its failure path, where the process is usually still running: processOutcome requires a settled handle, so an early mapping froze 'exit code: 0' onto a live task and first-wins end discarded the real terminal state. The tap now awaits proc.done before mapping (the promise never rejects) and swallows mapping failures per the module's contract, in bash and its pwsh mirror. Also applied from the same review: the Remote tier's cross-session visibility is now stated in the controller README, ActivityRead.next documents its chunk-boundary nature that client concatenation relies on, mergeRows records the one-activity-per-job assumption, and the 08-24 Agent Note now reflects the shipped unified task list.
31 lines
2.6 KiB
Markdown
31 lines
2.6 KiB
Markdown
# @deepseek-ai/dsh-api-activity-controller
|
|
|
|
English | [中文](README.zh.md)
|
|
|
|
Activity Remote streams over the optional `ctx.activities` registry, plus their React-free client model. A pure read surface: it starts nothing, cancels nothing, and never touches the model-facing consuming cursors — the wire path reads only the registry's non-consuming offsets.
|
|
|
|
The Host `ActivityController` (namespace `activity`) exposes two `@Remote({ mode: 'stream' })` methods:
|
|
|
|
- `control(signal)` — one complete roster baseline, then whole-bucket replacement frames per owner session on every registry change (the self-healing shape the session control stream uses for jobs). A composition without the registry serves an empty baseline. The Remote tier deliberately serves every session's rows to any connected browser, and `observe` satisfies the registry's owner fence with the mirrored owner: the per-session fence is an in-process contract, and this single-user local BFF reads across it exactly as the session control stream does for `jobsBySession`.
|
|
- `observe({ activityId, from? }, signal)` — one `opened` anchor, coalesced `output` frames (`flushMs` window, `maxFrameBytes` soft budget; one larger chunk ships whole), then one terminal `status` after the settled activity is drained, after which the generation closes normally. Reconnecting callers resume by passing the last frame's `next`; a resume behind the retained window arrives flagged `lossy`. Status rides the same stream as output, so settlement can never race a still-open output channel.
|
|
|
|
| Config | Default | Meaning |
|
|
|---|---|---|
|
|
| `flushMs` | `100` | Coalescing window after new output before an observation read. |
|
|
| `maxFrameBytes` | `65536` | Soft byte budget per observation output frame. |
|
|
|
|
The client half (`./client`) installs `ctx.activityFeed`: `ClientActivityModel` mirrors the roster (last-wins buckets, generation replace) and accumulates each observed activity's bounded render tail, and `ClientActivityFeed.observe(id)` opens reference-counted observation streams that resume from the model's cursor across carrier generations.
|
|
|
|
## Model Experience
|
|
|
|
None, as activity transport is browser and Host observation state and registers no prompt, tool, or session event.
|
|
|
|
#### KV Cache effect
|
|
|
|
None; the package never assembles or alters provider requests.
|
|
|
|
## Known Limitations and Deferred Work
|
|
|
|
- **Registry presence is sampled at construction** — a registry mounted after the controller stays unobserved until the controller reloads.
|
|
- **The roster names no watermark** — an observation stream anchors itself; correlating roster rows against observation offsets is client policy.
|