Files
deepseek-harness/packages/api/job-controller
Yichen Jiang b48c05d117 refactor(jobs): type the producer face by the record declaration, tidy review residue
- `JobStart` is a union discriminated by `record`: `RecordingJobStart.run`
  receives `RecordingJob` (with `append`), `PlainJobStart.run` receives the
  plain `RunningJob`, and the registry builds the plain face without the
  method, so the former warn-and-drop path is unrepresentable.
- Remove the duplicated JSDoc opener in the registry seam and correct the
  job-list slot comment (order 20 sits before the subagent catalog's 30).
- Restore the `cordis-tool-round` session fixture whose only change was a
  re-recorded `createdAt`.
- Update the 2026-08-08 job-display note's stale service-disposal and slot
  ordering facts; archive the superseded 2026-08-25 unified-task-list note
  and point its remaining inbound links at the archived snapshot.
2026-09-03 13:15:23 +08:00
..

description, kind
description kind
Host and Client job observation: stream one background job's output record to the browser without touching the model's consuming cursor. package-reference

Job Controller

English | 中文

Summary

@deepseek-ai/dsh-api-job-controller owns the Host ctx.jobController service and the generated Client ctx.remote.job namespace. Its one Remote stream, job.observe, delivers a record-declaring job's retained output from an absolute byte offset; the Client half installs ctx.jobOutput, the reference-counted per-job observation service whose accumulated views the session-header job list renders. The roster itself is not here: SessionJob rows still ride the session control stream owned by dsh-api-session-controller, and a row's record says whether this controller has anything to stream for it.

Table of Contents


Use this package

The Host controller requires the live Agent registry and the job registry (dsh-jobs-local in the shipped compositions) and fails to load without them. job.observe({ sessionId?, jobId, from? }) resolves the fenced-read caller from the request's session — an unowned job needs no session — and yields one opened anchor, coalesced output frames, then one terminal status once the job has settled and its record is drained, after which the stream closes normally. Record reads are non-consuming: the model-facing job_output cursor and completion-notice state never observe them. A job the session does not own, a job without a record, or an unknown job rejects the stream. Reconnecting callers resume by passing the last frame's next as from; a from below the oldest retained byte gets a lossy first frame instead of an error.

The Client entry provides ClientJobOutput (ctx.jobOutput) over ClientJobOutputModel. observe(sessionId, jobId) opens one Gateway stream per job however many viewers expand it, keeps a bounded render tail per job with gapBefore marking eviction or resume gaps, records the terminal status or a stream failure on the view, and drops the view after the last viewer releases. The plugin resolves the Gateway stream factory and the job namespace while its own context is current, because observation (re)opens run on caller stacks that have not declared remote.job.

Config

Field Default Meaning
observeFlushMs 100 Coalescing window after new record output before an observation read, in milliseconds
observeMaxFrameBytes 65,536 Soft byte budget per observation output frame; one larger chunk ships whole

The generated configuration catalog is the exhaustive source for accepted fields and their JSDoc.


Model Experience

None, as job observation is browser and Host control state; it registers no prompt, tool, or session event. The model's own view of the same work stays with dsh-tool-jobs.

KV Cache effect

No direct effect; observation reads never touch model requests.

Known Limitations and Deferred Work

  • The record is a best-effort live preview, not a terminal transcript: producers copy stdout and stderr per poll round, so the two streams' writes inside one poll window reach the record stdout first, and the client concatenates chunks regardless of channel.
  • The stream is process-local: a Host restart loses every record, and a resumed observation then anchors on an empty registry.
  • Per-session fencing is enforced by the registry read; the Remote layer itself serves any connected browser, matching the session control stream's jobsBySession broadcast.

Dev Note

Working context for maintainers — click to expand

None.

Runtime invariant: No companion is published. The controller is a stateless projection of ctx.jobs reads; the registry's own @deepseek-ai/dsh-jobs/invariant owns the offset relations this stream forwards.