Skip to content

feat(chat): resumable turns survive deploys (durable runs, takeover + continue) (v0.3.59) - #353

Merged
ralyodio merged 3 commits into
masterfrom
feat/chat-runs-durable
Nov 12, 2026
Merged

ralyodio merged 3 commits into
masterfrom
feat/chat-runs-durable

Conversation

@ralyodio

Copy link
Copy Markdown
Collaborator

With this PR, resumable chat turns survive a deploy. Until now, an answer still streaming when c0mpute.com redeployed was lost. The process that generated it was killed (after Docker's 10 s stop grace), and so was the standby container that served the deploy window.

How it works

  • Durable runs. The chat_runs and chat_run_chunks tables (SQLite Cloud) hold each run's request, its output and a heartbeat.
    • The request holds the prompts, so it is sealed with the conversation key.
    • The owning server appends output and refreshes the heartbeat about once a second.
  • Takeover. On boot (instrumentation.ts → runManager()) and every 15 s, each server claims runs whose owner hasn't written for 20 s. The claim is a conditional UPDATE on the old owner, so only one server wins.
    • Plain answer: continued from the saved partial text. The request is history + {assistant: partial} + a "continue exactly where it stopped" instruction.
    • Plugin turn: runs again, without its approvals, so an approved destructive tool call is never executed twice; it asks again.
    • After 3 deaths, or past 20 minutes, the run ends with a clear error instead of looping.
  • Works from any server. A reattach or DELETE that lands on a server that doesn't own the run reads it from the database (polled). A cancel is recorded there, and the owner stops at its next tick. A server that finds its run taken over stops quietly.
  • Finished runs are deleted after an hour.
  • Without a database or a key, everything stays in memory, as before.
  • The client contract is unchanged: same headers, events and routes. Web, TUI and CLI already reattach on a dropped stream, so they pick up the continued answer with no client changes.

Tests

Web src/lib/chat: 119 pass. The only failure is chat music > node progress…, which fails on master too.

New durable tests, run with two simulated servers on one SQLite database and a shared clock:

  • A server SIGKILLed mid-answer: another takes the run over after it goes stale and continues from the saved partial. The replay reads as one answer, and the stored request has no plain-text prompt.
  • Only one of two racing servers wins a takeover.
  • A server that doesn't own a run follows it from the database to the end.
  • A cancel on another server stops the owner.
  • A plugin turn re-runs without approvals.
  • A run that keeps dying is given up with a message.

tsc is clean in every touched file, including instrumentation.ts. Version bumped to v0.3.59.

🤖 Generated with Claude Code

ralyodio and others added 3 commits November 12, 2026 08:49
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

ThreatCrush Security Scan

16 finding(s)

HIGH/CRITICAL: 1 | MEDIUM: 11 | LOW: 4

Severity Rule Location
HIGH js-ssrf-outbound-request apps/web/public/sw.js:24
MEDIUM js-unescaped-html-sink apps/web/src/app/blog/[slug]/page.tsx:53
MEDIUM js-unescaped-html-sink apps/web/src/app/layout.tsx:109
MEDIUM sql-template-interpolation apps/web/src/lib/chat/convo-store.ts:116
MEDIUM sql-template-interpolation apps/web/src/lib/chat/convo-store.ts:117
MEDIUM sql-template-interpolation apps/web/src/lib/chat/push.ts:476
MEDIUM sh-remote-script-execution scripts/dev-setup.sh:25
MEDIUM sh-eval-expansion scripts/dev-setup.sh:35
MEDIUM sh-remote-script-execution scripts/install.sh:185
MEDIUM sh-remote-script-execution scripts/install.sh:210
MEDIUM sh-remote-script-execution scripts/install.sh:408
MEDIUM sh-remote-script-execution scripts/install.sh:450
LOW py-ssrf-outbound-request examples/business-strategist-agent/chat.py:37
LOW py-ssrf-outbound-request examples/business-strategist-agent/train.py:54
LOW secret-generic-credential node/crates/c0mpute-core/src/webhook.rs:1171
LOW py-path-traversal node/crates/c0mpute-core/tests/fixtures/fake-infernet-text:29

Snippets are redacted; ThreatCrush never prints matched credential material.

@ralyodio
ralyodio merged commit e38b638 into master Nov 12, 2026
14 of 15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant