Skip to content

DRU-376 - Operator chat - #517

Open
chaosk wants to merge 9 commits into
mainfrom
integration/chat
Open

DRU-376 - Operator chat#517
chaosk wants to merge 9 commits into
mainfrom
integration/chat

Conversation

@chaosk

@chaosk chaosk commented Sep 10, 2026

Copy link
Copy Markdown
Collaborator

Linear ticket: DRU-376

Dashboard chat that keeps several conversations, reuses the sandbox across turns instead of provisioning on every send, and can operate this Druks appliance as that operator through the existing MCP tool catalog. Autonomy (propose / confirm / full) is a per-conversation setting.

This is not a live socket into a running harness and not a 14-day parked VM. A turn is still a durable gate. While the operator is actively chatting, the host lease stays up so the next line does not wait for a new machine.

What the operator gets

  • Chat as a bundled app (backend/druks/contrib/chat/), same packaging as Issues. Not a druks.chat namespace and not a dist/ SPA.
  • Several conversations per signed-in operator; each lists only theirs.
  • One Talk run per conversation: agent turn → assistant message → optional confirm gate → ChatTurn.wait(hold_sandbox=15m) → send or stop. Stop ends the run and does not write a message.
  • Pages: list, new, thread, autonomy setting. Untitled threads take a title from the first user line. Prompt history is bounded.
  • In-sandbox MCP as the signed-in operator, with a call-scoped credential that dies with the agent call. Propose lists read tools only; confirm stashes writes until the operator approves; full runs them as account_id.
  • Docs and druks doctor: Chat is always installed; /mcp is probed (or skipped when sandbox execution is off).

Sandbox hold

Drukbox already has POST /hosts/{id}/renew. The SDK already exposes SandboxAPI.renew_host. This branch wraps that as Client.set_expiry. Gate.wait(..., hold_sandbox=…) clips the lease instead of reaping. review() still reaps. After the clipped lease, the next line provisions cold.

Tickets (landed on integration/chat)

  • DRU-377 — clip / extend host lease (Drukbox)
  • DRU-378 — SDK renew_host (drukbox-python-sdk)
  • DRU-379 — Druks Client.set_expiry (merged locally; GitHub merge conflicted after client.py moved)
  • DRU-380Gate.wait sandbox hold
  • DRU-381 — Conversation + Message
  • DRU-382 — Talk + ChatTurn
  • DRU-383 — list, new, thread pages
  • DRU-384 — MCP-as-operator
  • DRU-385 — prompt, title, stop
  • DRU-386 — docs and doctor

Out of scope

Mid-call interrupt / stdin into a live harness. Holding a VM for the 14-day gate TTL. Persistent leaseless workstations. A druks.chat namespace or a second SSE system. A 150-minute Drukbox renew ceiling. Replacing external Claude/Codex MCP.

Risk

Alembic for chat conversation/message tables.

Replay: parks with hold_sandbox clip the lease; review() and hold_sandbox=False still delete the host. Workflow finally still reaps on terminal exit. Call-scoped MCP tokens do not outlive the agent call.

Verification

  • uv run ruff check backend
  • uv run ruff format --check backend
  • uv pip install -e backend/tests/druks-field_notes
  • uv run pytest backend/
  • npm --prefix frontend run lint
  • npm --prefix frontend test
  • npm --prefix frontend run build
  • An operator can open several conversations; each lists only theirs.
  • Sending a second line inside the hold window does not provision a new host.
  • After the clipped lease, the next line provisions cold and still works.
  • review() and any hold_sandbox=False park still delete the host immediately.
  • Stopping a conversation reaps the host.
  • Propose cannot hit mutating tools; confirm executes only after the confirm gate; full runs as that account.

@chaosk
chaosk requested a review from czpython as a code owner September 10, 2026 21:18
druks-operator-treadstone Bot and others added 7 commits September 10, 2026 23:27
…it (#421)

Co-authored-by: druks-operator-treadstone[bot] <322217521+druks-operator-treadstone[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Krzysztof Socha <ksocha@ksocha.com>
Each thread belongs to one account; messages are rows, not events. Talk lands later, so there is no workflow yet.

Co-authored-by: Cursor <cursoragent@cursor.com>
#415)

The first POST creates the thread and starts the run; later lines answer the gate. A fifteen-minute hold keeps the warm VM across turns.

Co-authored-by: Cursor <cursoragent@cursor.com>
…rt and follow conversations from the dashboard. (#416)

Co-authored-by: Cursor <cursoragent@cursor.com>
…hrough a call-scoped MCP token. (#417)

* Let Chat operate this appliance as the signed-in operator through a call-scoped MCP token.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Stop advertising mutating /mcp response schemas so confirm can defer a create.

Confirm returns a deferred stub, not the route's 201 body. MCP outputSchema
validation rejected that stub on any write whose model requires fields like
identifier, so the tool failed and the write never reached the operator gate.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
…gate. (#418)

Talk was feeding the whole thread and parking a review-shaped turn. Bound the prompt, fill an empty title from the first user line, and answer ChatTurn through send/stop so stop ends the run without a message row.

Co-authored-by: Cursor <cursoragent@cursor.com>
…operator chat app. (#518)

Co-authored-by: Cursor <cursoragent@cursor.com>
@mintlify

mintlify Bot commented Sep 10, 2026

Copy link
Copy Markdown

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated
druks 🟢 Ready View Preview Sep 10, 2026, 10:09 PM

💡 Tip: Enable Automations to automatically generate PRs for you.

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