Carries the session seq-brand master sync forward; unions the seq/offset
imports with this branch's jobs augmentation in commands.ts and
regenerates the catalogs and zh ports over the merged source.
Absorbs the session seq-brand refactor and the turn-rail preview layer;
regenerates the catalogs and graph docs from the merged source and
re-ports the zh config/event rows this branch owns.
Carries the master sync forward (invariant-companion cleanup, alpha.3 bump);
regenerates the client api catalog so ISession lists both killJob and the new
loadThrough, and refreshes the new ptc-python-turn scenario's system-prompt
pin for this branch's bash timeout-promotion schema text.
Absorbs the repo-wide no-op invariant companion removal (ui-jobs loses its
placeholder companion, export, and devDep), the 0.1.2-alpha.3 release bump,
and the tool-subagent-report package retirement; regenerates the client slot
catalog for the shifted contract line numbers.
Observation (re)opens run on caller stacks — a React expand click, a
carrier retry — whose dynamic context has not declared remote.session, so
the ClientJobOutput wiring resolves the namespace to a concrete value while
the plugin's own context is current, exactly as the activity client did.
Caught by the live-job-stream web e2e, whose goldens (with the
background-job-list pair) now carry the ui-jobs list name; the client-apply
spec gains a jobOutput wiring smoke.
SessionJob rows carry outputTotal exactly for record-declaring jobs, and a
new session.observeJob stream serves one job's retained record from an
absolute offset (anchor, coalesced output frames, terminal status) with the
fenced read resolved from the request's session — the same authorization
shape as the jobs roster. The client half gains ctx.jobOutput: the
reference-counted per-job observation service and its accumulated view
model, ported from the activity controller this replaces.
loadThrough now assigns its low-water target only when it owns the loop
(retargeting stays inside the running-jump branch): a call refused while
a plain load-earlier pull holds the pager no longer leaves jumpTargetSeq
behind to drag a later jump all the way to the head. The loop also
carries the doOpen stale-pass guard so a mid-flight resync stops it
instead of paging the new stream generation toward the old target.
Session.loadThrough(seq) loops the existing prepend pager (200-message
pages) until the window covers the target seq, with a shared low-water
retarget for repeated calls, a no-progress guard against empty pages
still claiming history, and loadOlder's fail-soft error posture. Busy
state rides the existing loadingOlder snapshot bit.
- protocol: isDSHRemoteGatewayError -> isDSHRemoteError (the class is
protocol-wide, not Gateway-specific); remoteErrorOf drops its
instanceof Error precondition and tests the marker plus a string code
structurally, so a marked failure from another realm no longer reads
as a local defect.
- session-controller: the live-follow packed-record protocol violation
now throws a marked RemoteError('gateway/internal'), landing the
session in openState=error instead of an unhandled rejection;
pinned at the transport and session levels.
- util-time: correct the invariant companion's @module name.
- regenerate the tool-cordis catalog for the marker rename.
Single RemoteError with a merge-extensible, domain-prefixed code map;
owners throw at the failure point; streams surface marked failures;
clients consume ctx.remote directly with isRemoteFailure as the only
discrimination point and construct no failure instances.
A steer or follow-up accepted while a turn is closing is now claimed by a
fresh turn at the driver's clean exit instead of stranding in the inbox;
cancellation and pre-step rejection still park accepted work. Continuable
subagent follow-ups accept image parts: the wire is upload-shaped, the Host
admits and persists each batch before inbox acceptance, and delivery is
refused when the child model declines image input. The queue dock renders
durable image thumbnails instead of an [image] text marker.
Fixes#3186
- session.killJob maps job-not-found from the lookup only: jobs.get gates the
same synchronous span, so a producer-cancel throw now propagates per the
registry contract (job state unchanged) instead of masquerading as a lookup
failure (threads r3860226021, r3860226047).
- session.killJob applies the service-wide subagent ownership fence exactly as
cancel does; a subagent-owned live session rejects with the ownership error
and the child-owned job stays running (thread r3860226041).
- The stop control stays pending on an admitted kill: the unary response and
the jobs frames have no cross-carrier ordering, so only the authoritative
frame (the row leaving the killable set) releases the button, closing the
duplicate-kill window (thread r3860226028).
- One-line comment on the killReason assignment stating last-writer-wins is
intended, beside the first-claim-wins reported handling (thread r3860225999).
Host spec pins the producer-throw propagation and the fence; the component
spec pins pending-until-frame; the Agent Note records both refinements.
The web task list gains a two-press stop control on running job rows, built on
one seam change: JobRegistry.kill now takes { reason?, reported? } instead of a
positional reason. reported defaults to true (the model's job_kill claims the
terminal report exactly as before); the web path passes reported: false because
a human kill has no model-visible channel, so the settlement flows through the
existing tool-jobs completion notice (wakeup/inject, budgets, truncation all
unchanged). A recorded kill reason merges into a killed settlement's detail
(producer facts first: 'signal: SIGTERM; cancelled by the user'), so the notice
and the web row both say who stopped the work.
- session-controller: session.killJob Remote beside cancel, live-only Agent
lookup, job-not-found/jobs-unavailable rejections joined to the closed RPC
error table; ClientSession.killJob and the ISession face carry it to the UI.
- ui-activity: arm/confirm stop button (3s disarm, pending disable, failure
hint), rendered only on running job rows; standalone activities keep no kill
handle and the README limitation now says so.
- The background-job-list web e2e now drives the whole human path through the
button; goldens show the merged killed detail end to end.
- Agent Note 2026-08-26-human-job-kill records the decision against Kimi's
suppress-own-notification precedent and Claude Code's silent-kill failure;
the 2026-08-08 blocker note and the unified-task-list note point at it.