What is actually in jcode/
The manifest describes itself, without irony, as “Possibly the greatest coding agent ever built — blazing-fast TUI, multi-model, swarm coordination, 30+ tools.” Whatever one makes of that, the shape is unambiguous: this is a full terminal coding agent with providers, a TUI, a memory subsystem, session persistence, MCP, telemetry and a mobile client, of which Always drives one headless mode.
- jcode/src
- 308,417636 .rs files
- jcode/crates
- 75,393136 .rs files, 50 crates
- jcode/tests
- 5,98013 integration files
- total Rust
- 390,138786 files
- ratio to first-party
- 6.7×vs. 58,200 lines of Python + TS
src/tui tree —
is dead code from Always's point of view, yet two of the four patched files live inside it, because
the slash-command parser is a TUI concern. Second, the release profile is optimised for rebuild
speed rather than runtime speed, so a voice deployment built with cargo build --release
is running opt-level = 1 code.jcode serve, and nothing else
The worker's _build_serve_command() constructs the process line and nothing about it is
guessed — every flag below is a global flag on the root clap parser, and the three flags that
serve itself defines are all marked hide = true and are internal to
temporary-server lifetimes. There are no public serve options; the socket location comes
from the global --socket or the JCODE_SOCKET environment variable.
$ pgrep -a -f 'jcode serve' 3184240 /home/user/.local/bin/jcode serve --no-update --quiet \ --socket /home/user/.hermes/voice-daemon/jcode-server.sock \ -C /home/user -p openai-compatible -m qwen3.6-35b # with JCODE_VOICE_PROVIDER_PROFILE set, --provider-profile replaces -p entirely: 3184240 /home/user/.local/bin/jcode serve --no-update --quiet \ --socket …/jcode-server.sock -C /home/user \ --provider-profile bifrost -m qwen3.6-35b # the one-shot path, used for slash commands the model cannot emit as tool calls: $ jcode debug --socket …/jcode-server.sock --no-update --quiet -C /home/user \ -S <session_id> -w message '/subagent --output-mode background …' … # the process is spawned under a PTY, not a pipe: $ ls -l /proc/3184240/fd/0 lrwx------ 1 ianflow ianflow 64 … /proc/3184240/fd/0 -> /dev/pts/7
jcode/src/cli/args.rs:
--no-update (“Skip the automatic update check”), --quiet
(“Suppress non-error CLI/status output for scripting and wrappers”),
--socket, -C/--cwd, -p/--provider,
--provider-profile, -m/--model; and on debug,
-S/--session and -w/--wait. Process ids differ per run.
The PTY is deliberate. pty.openpty() is called before subprocess.Popen and
all three standard streams are attached to the slave descriptor, with
start_new_session=True. A drain thread consumes the master. The worker also prefers
not to spawn at all: _attach_existing_server() runs first, and if both the server
socket and its sibling debug socket already exist it reuses them and records
owns_server = False — which means stop_server() will not kill a process it
did not start.
Readiness is polled, not signalled
After spawning, the worker polls _server_ready() every 0.2 s against a 15-second
deadline, and on failure raises TimeoutError("server did not publish debug socket").
If the child exits during startup it re-attempts _attach_existing_server() once, to
cover the race where two workers start simultaneously and one loses.
Two enums, one file, 2,114 lines
Both halves of the socket protocol are serde-tagged enums in
crates/jcode-protocol/src/lib.rs: Request (client to server, roughly seventy
variants, every one carrying a u64 id) and ServerEvent (server to client,
roughly sixty-three). More than half of Request is multi-agent coordination —
comm_share, comm_propose_plan, comm_assign_task and forty-odd
siblings — none of which Always touches.
| Direction | Wire type | Sent / consumed by | Why |
|---|---|---|---|
| → | subscribe | chat client, ambient listener | first message on any new connection |
| → | resume_session | chat client, ambient listener | attach to an existing conversation; must complete before message |
| → | message | chat client only | one user turn |
| → | set_model | chat client | sent fire-and-forget; the protocol emits no acknowledgement |
| → | set_reasoning_effort | chat client | one of none, low, medium, high, xhigh; waited on for up to 15 s |
| → | ping | JcodeServerClient.ping() | on a throwaway socket, expects pong with a matching id |
| ← | text_delta | both readers | the answer, and the ambient narration |
| ← | message_end | both readers | ambient flush trigger |
| ← | tool_call / tool_call_start / tool_use / tool_result / tool_call_end | both readers | rendered as activity in the browser |
| ← | swarm_status | chat client | fallback session-id discovery when exactly one member is present |
| ← | tokens | chat client | input / output / cache-read counts |
| ← | soft_interrupt_injected | ambient listener only | logged as a marker; not narrated |
| ← | done / error | chat client | terminate the drain loop for a given id |
One event the Python code forwards does not exist
Both the worker's FORWARDED set and the listener's DEFAULT_FORWARDED set
include reasoning_delta, and the gateway has a browser frame type for it.
grep for reasoning_delta, ReasoningDelta or
Thinking in crates/jcode-protocol/src/lib.rs returns nothing — the enum has
no such variant. Reasoning appears in the protocol only as the
reasoning_effort_changed configuration event. The forwarding entry is inert.
/subagent --output-mode background
The model does not need this flag — an LLM-driven subagent call emits a JSON tool input with
output_mode set directly. The slash command exists so the mode can be triggered by hand
from the TUI or, as the ambient smoke test does, through jcode debug. Its parser is a
hand-written token loop, and its rejection messages are exact:
> /subagent --output-mode backgruond --model local/qwen27 count to five Invalid --output-mode `backgruond`. Expected one of: answer, compact, full_transcript, background. > /subagent --depth 3 count to five Unknown flag `--depth`. > /subagent --type research Missing prompt. Add text after `/subagent`. > /subagent --output-mode Missing value for `--output-mode`. > /subagent --mode background --model local/qwen27 count from 1 to 5 slowly Subagent started in background. Task ID: 184177a3f9 Name: count from 1 to 5 (@general subagent · local/qwen27) Output file: /tmp/jcode-bg-tasks/184177a3f9.output Status file: /tmp/jcode-bg-tasks/184177a3f9.status.json The host will be woken automatically when the subagent completes; the final answer is auto-injected into the next turn. To poll early: use `bg` with action="status" and task_id="184177a3f9".
format! templates from the parser. The success
block is the ToolOutput body built at src/tool/task.rs:387; only the task
id and the derived display name vary. Task ids are ten characters — six digits of the millisecond
timestamp plus four from a 36-character alphabet. The directory is
std::env::temp_dir().join("jcode-bg-tasks").
| Flag | Default | Effect |
|---|---|---|
--type <name> | "general" | subagent type, used in the display title |
--model <id> | inherited | resolution order: explicit, then existing, then parent, then provider default |
--continue <session> | none | run inside an existing child session |
--output-mode, --mode | answer | one of answer, compact, full_transcript, background |
everything else beginning -- | — | rejected; the first non-flag token starts the prompt and consumes the rest of the line |
One detail in execute_background is easy to miss and matters for safety: before forking,
it copies the registry's tool names and removes subagent, task,
todo, todowrite and todoread. A background subagent therefore
cannot spawn another background subagent, and the fork depth is capped at one by construction rather
than by a counter.
Failures are results, not errors
The spawned closure never returns Err. A child agent that fails writes a payload
beginning “Subagent failed: …” — followed by the same
<subagent_metadata> block carrying session id, model and duration — and returns
TaskResult::failed(Some(1), …). The status file always reaches a terminal state,
the notification always formats, and the host is always woken. A failed delegation is reported to
you out loud rather than vanishing.
Three thousand tests, none on the patch
The harness is heavily tested by attribute count: over 3,000 #[test] and
#[tokio::test] functions across src, crates and
tests. The background-task machinery Always leans on has real coverage of its own —
six async tests in src/background/tests.rs exercising delivery flags, progress
persistence and the three wait() exit conditions, plus three in
src/server/tests.rs that drive dispatch_background_task_completion directly,
including one named background_task_notify_without_wake_does_not_queue_soft_interrupt.
What has no coverage is precisely the Always-specific part:
execute_background— no test in the tree constructsSubagentOutputMode::Background. The subagent test module imports five symbols and the enum variant is not among the ones it exercises.- The full-answer inline branch —
src/message/tests.rshas five tests forformat_background_task_notification_markdown, covering the preview, the empty case, failure highlighting, superseded status and the progress variant. None passestool_name == "subagent". - The 256,000-byte cap — inherits the same gap, and the literal appears exactly once in the whole tree.
The end-to-end check that was written for this feature is
scripts/test_ambient_drain.py, on the Python side, and it can no longer pass. See
Drift.