Skip to content

[Enchancement] : Host-level shared state: every session on the same host can see the other sessions' digest (active sessions, message tail, todos, scheduled/background tasks, memory) #833

Description

@maxivillus

Summary

jcode sessions on the same host are islands: none of them knows what a sibling
session is doing, has done, or plans to do. A user working in several terminals
(or running background/ambient tasks) has no built-in way to ask "what were we
doing in the other session?" — the answer requires manual copy-paste or a
separate process.

We propose native host-level shared state (active sessions, recent message
digest, todos, scheduled tasks, background tasks, memory summary) that every
session can read without breaking the prompt cache, so any session can pick
up where another left off.

Impact / use cases

  1. Multi-terminal workflows. User runs 2–3 jcode sessions in parallel; wants
    "continue what the other terminal was doing" without re-explaining context.
  2. Background tasks. A task spawned from session A finishes while the user
    is in session B; B should see status, progress and output tail.
  3. Handoff / crash recovery. A session is closed or dies; a new session
    reconstructs context from the digest instead of a blank start.
  4. Autonomous work. Scheduled and ambient tasks leave state; any session (or
    a fresh one) can report on them.

Current state: external shim (reference implementation)

There is no native support. On this host we built an external shim that
already delivers the behavior and is battle-tested in real use:

  • A Python watcher (jcode-sync-watcher.py, attached to this issue) polls
    every 3 s while at least one session is alive and
    aggregates jcode's own on-disk state into ~/.jcode/sync/ (a git repo):
    • conversations.jsonldigest-only history: every message of every
      session as ≤300-char summaries (tool calls → [tool: name], results →
      [tool_result] + first 150 chars) with a ref field pointing at the full
      text in ~/.jcode/sessions/. Append-only, dedup by message_id, trimmed
      to the last 20000 records (~24 MB ceiling).
    • todos.json (mirror of ~/.jcode/todos/*.json), scheduled.json
      (ambient/queue.json), memory.json + initiatives.json (compact
      summaries), background.json (active sessions + event journal),
      background-tasks.json (mirror of $TMPDIR/jcode-bg-tasks/*.status.json
      • output tails).
    • overview.md — human-readable digest with a "last messages" tail.
  • Started via the existing [hooks] session_start hook (config.toml
    fire_session_lifecycle_hook("session_start", ...) at
    crates/jcode-app-core/src/agent.rs:372,432 and
    crates/jcode-app-core/src/agent/turn_execution.rs:643). The watcher
    self-exits ~15 s after the last live session closes (pid-liveness check on
    ~/.jcode/active_pids, 60 s startup grace against the session-registration
    race).
  • New sessions are told about it through ~/.jcode/prompt-overlay.md, loaded by
    load_prompt_overlay_files_from_dir (crates/jcode-base/src/prompt.rs:862),
    which instructs the model to read ~/.jcode/sync/overview.md and the
    conversations.jsonl tail before answering.

Why the shim is fragile — the gaps this issue wants closed:

  1. The overlay must stay static. Injecting dynamic state into the overlay
    breaks the prompt cache (a 3 s regenerated overlay caused ~97K tokens of
    system prompt re-sent per turn
    , cache_read → ~0). So live state cannot be
    auto-injected; the model must remember to read the sync files, and it is
    easy to skip.
  2. Duplicate of data jcode already holds. The shim re-aggregates sessions,
    todos and background tasks that the daemon already has in memory. It is a
    separate process with its own lifecycle (pid files, grace periods, lock)
    competing with the daemon.
  3. No privacy controls. conversations.jsonl contains message text. There
    is no per-session opt-out, redaction, or retention policy.
  4. Cross-host sync is manual git push/pull.

Proposed design (native)

  1. Daemon-owned shared store. jcode maintains a compact host-level store
    (e.g. ~/.jcode/state/shared.json), updated on events rather than by an
    external poller: session start/end, message appended, todo change,
    background-task status change, scheduled task due, memory write. Contents:
    active sessions, last N messages per session (configurable), todos/plan/goals,
    scheduled tasks, background tasks + output tails, memory/initiatives counts,
    timestamp. Written atomically (tmp+rename); no external watcher process.
  2. Cache-safe access. Two complementary paths:
    • Option A (tool, recommended): a read_session_state-style tool
      returning the digest on demand. Zero prompt-cache impact, explicit,
      model-driven.
    • Option B (pinned injection, opt-in): append a digest to the static
      prompt part once at session start, never refreshed mid-session (protects
      the KV cache, per the prompt-cache issue's "static by construction"
      principle); refresh only on new session or explicit reload.
  3. Native lifecycle. Shared-state writer lives and dies with the daemon: no
    watcher, no pid files, no grace periods.
  4. Privacy & retention. Per-session opt-out of transcript sharing;
    configurable depth (digest vs full text) and retention limit; local-only mode
    (no git).
  5. Cross-host sync (out of scope for v1). Later: reuse git or a future sync
    server; for now, the store stays on the host.

Verification (on the shim)

  • Cross-session Q&A: two concurrent sessions on one host; session B answered
    "what was session A doing / what was the last thing done there" from shared
    state alone, with no user copy-paste.
  • Background tasks visible: a native background task spawned in one session
    (status, progress %, output tail) shows up in overview.md and in the other
    session's digest while still running.
  • KV-cache preserved: the overlay is static (snapshot pinned in
    prompt-overlay.md); fresh data is read from ~/.jcode/sync/ files on
    demand.
  • History stays small: conversations.jsonl is digest-only (≤300 chars per
    message, ref to full text); overview tail reads only the last 64 KB of the
    file. A previously multi-MB transcript file is now ~KB-scale to re-read.

Acceptance criteria

  • Two concurrent sessions on one host: session B can answer "what is session A
    doing / what was the last thing done there" from shared state, with no user
    copy-paste.
  • Reading shared state never changes the cacheable static prefix mid-session
    (KV cache preserved; the 97K-token incident does not recur).
  • Background task status/progress/output tail started in any session is visible
    in any other session and in the TUI.
  • Privacy: a session with share_transcripts=false never contributes message
    text to the shared store.
  • The external watcher shim becomes removable: the native implementation covers
    the same surface.
  • jcode doctor / status widget reports shared-state health (age of last
    update, stale flag).

Environment

  • jcode v0.68.0 (fcf5390), Linux x86_64. Shim reference: ~/.jcode/sync/README.md
    and the attached jcode-sync-watcher.py (+ helper scripts jcode-sync.sh,
    jcode-sync-start.sh, jcode-sync-bg.sh).

Related

jcode-sync.sh
jcode-sync-bg.sh
jcode-sync-start.sh
jcode-sync-watcher.py

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions