Repository navigation
feat(server): Prism stream clock for worker liveness, plus prism_cancel - #56
Merged
Merged
Conversation
Contributor
Thread transfer impact✅ Thread transfer remains within every enforced ceiling.
Baseline: Scenario and decoded snapshot size10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.
Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed. |
This was referenced Sep 27, 2026
Model Router measured worker silence from ~100-char message segments with a fixed window, so a dead turn went unnoticed for minutes and a slow but healthy model risked being killed. Prism also had no way to cancel a job. Every provider event already passes through the shared native and canonical event loggers, including each token, reasoning and tool output delta and Claude thinking_tokens. A new in-memory StreamClock wraps those loggers and stamps each thread's lastStreamAt, tracks open tool calls, and at each turn end records one hidden prism.stream-stats activity with time to first token, max gap while no tool was open, event count, provider, model and outcome. GET /api/prism/liveness?threadId= returns it with the server's now. prism_cancel checks the job belongs to the calling thread, then runs the Router CLI cancel and recover with a Prism token and returns the final state. Closes #55 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…op unused exports (#55) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
lukemaj
force-pushed
the
feat/55-stream-clock
branch
from
September 28, 2026 16:32
89dce17 to
191cafb
Compare
This was referenced Sep 28, 2026
This was referenced Oct 8, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #55
Problem
Model Router could only see worker silence through ~100-char message segments with a fixed 60 s window. A dead turn went unnoticed for minutes, and a slow but healthy model could be killed. Prism also had no cancel, so cancelled T3 jobs stayed
cancelling.Fix
apps/server/src/prism/streamClock.ts, new): every adapter already writes each native provider event (every token, reasoning and tool output delta, Claudethinking_tokens, Codexitem/*Delta, OpenCodemessage.part.delta) to the shared native event logger.ProviderServicewrites each canonical event to the canonical logger. The clock wraps both loggers (one layer swap inserver.ts), stampslastStreamAtper thread, and tracks open tool calls from canonicalitem.started/item.completed. It is in memory only. File logging behaves as before: deltas are still dropped from the log, and the clock keeps running when logging is off.GET /api/prism/liveness?threadId=,orchestration:read): returnsnow,lastStreamAt,silenceMs,openTooland the runningturn. It is a separate route rather than an addition to the snapshot, because the snapshot contract stays untouched.turn.completed/turn.aborted, oneprism.stream-statsthread activity records time to first token (first content delta), max inter-event gap while no tool was open, native event count, provider, model and outcome (completed/aborted/error, plus the raw state). Web and mobile hide it from the work log (one line each; allowlisted and mapped).prism_cancel: reads the job'splanner_t3_threadfrom Routerstatusand refuses unless it is the calling thread. It then runscancelandrecoverwith a Prism-issued token (1 h TTL, orchestration scopes) and returns the final job status.Not included: stall decisions in T3 (Router owns policy). Pending approvals and user-input requests do not count as open tools.
Proof
vp test runforsrc/prism,src/mcp,src/server.test.ts,ProviderService.test.tsandentrypoint.test.ts(server), plus the websession-logicand mobilethreadActivitytests: all pass.streamClock.test.tscover: native-only deltas advancing the clock, tool silence left out of the max gap, abort and error outcomes, an unknown thread, and logger tapping with logging off.handlers.test.tschecks the cancel sequence and token use with a fake router, and the refusal for another thread's job.tsc --noEmitpasses for contracts, server, web and mobile.vp lintandvp fmtare clean on the changed files.scripts/fork-check.shfails only on the stack size:mainalready has 20 commits on upstream, the limit is 20, and squash-merging this PR makes 21. With--max-commits 21it reportsfork-features: OK (17 features)andfork-check: OK. Raising the limit or consolidating the stack is a fork-maintenance decision and is not part of this PR.Live check (user, after installing this build)
docs/fork.md. Quit Chromeria first. Do not run it on the same T3 home as T3 Code (Alpha).TOKEN=$(node apps/server/src/bin.ts auth session issue --ttl 1h --label liveness-check --token-only). Note the backend port (3773 unless another app took it).while :; do curl -s -H "Authorization: Bearer $TOKEN" "http://127.0.0.1:3773/api/prism/liveness?threadId=$THREAD" | jq -c '{now,lastStreamAt,silenceMs,openTool:.openTool.itemType,turn:.turn.eventCount}'; sleep 1; donelastStreamAtadvances every second or so while it reasons, andsilenceMsstays small. During a shell command,openToolshowscommand_execution.lastStreamAtstops advancing,silenceMskeeps growing, andturnbecomesnull.prism.stream-statsactivity per finished turn, not shown in the work log. Check withsqlite3 ~/.t3/userdata/state.sqlite "select payload_json from orchestration_events where event_type='thread.activity-appended' and payload_json like '%prism.stream-stats%' order by sequence desc limit 3"(read-only).prism_cancel: from a planner thread, submit a small Prism job, then ask the agent to callprism_cancelwith its request id. Expected: statuscancelled(orcancellingif something is still alive; calling again retries), and the job's child threads stop. Calling it from another thread is refused.Model and harness: Claude Opus 5.5 (1M context) in Claude Code, run from T3 Code.
🤖 Generated with Claude Code