Skip to content

DSCODE 0.7.48

Latest

Choose a tag to compare

@github-actions github-actions released this 15 Oct 06:08
· 3 commits to main since this release

DSCODE 0.7.48

The runtime can now run on a server while the terminal runs on your machine: dscode server serves it over TCP with a token, and dscode config --runtime and dscode runtime <alias> attach to it. /reload applies MCP and configuration edits without restarting the runtime.

Remote runtime

0.7.47 split the terminal from the runtime but kept the runtime on the same machine, behind an owner-only Unix socket. dscode server --port PORT now runs that same host in the foreground with a second listener on TCP (--bind, default 0.0.0.0) that carries the same routes. Every TCP request must present Authorization: Bearer <token>. The token is minted on first start (dsk_ plus 24 random bytes), stored owner-only in run/server-token and reused; --token sets it and --new-token replaces it. A wrong or missing token is refused with dscode/unauthorized before any route runs. The local socket stays, so dscode on the server attaches to the same runtime and can share a session with a remote terminal. The server prints the exact dscode config line to run on the client, and it never exits for being idle. An idle background host is stopped before the server starts; a host with attached terminals or running turns makes the server refuse instead.

The listener is not encrypted. This was the chosen design: token authentication only, for networks that are already private (VPN, Tailscale, LAN) or an SSH tunnel. The server's banner and the guide say so.

On the client, dscode config --runtime HOST:PORT --token … --alias NAME [--cwd DIR] [--set-default] records a server in an owner-only runtimes.json and reports at once whether it answers. --default, --remove and --list manage the list, and the listing never prints a token. dscode runtime NAME attaches to one, bare dscode attaches to the default, and dscode runtime local uses this machine. Before starting the terminal, the launcher checks three things: the server answers, it accepts the token, and it runs the same DSCODE version. A refusal is reported, and the launcher never falls back to the local runtime. The session folder is a path on the server: --cwd, else the alias's folder, else the folder the server was started in.

A terminal that does not share the runtime's files

Several terminal features read the local disk and assumed it was the runtime's disk. They now go through the runtime:

  • Attachments. A pasted screenshot or dropped file the terminal can read is uploaded as content through new attachments/*Data operations, which apply the same count, size and format limits as before. A path the terminal cannot read is still passed to the runtime to resolve in the session's folder. A message that mixes the two checks each group against the limits separately.
  • Git. /diff, /review and the status-line branch run on the runtime in every mode, as new workspace/* operations.
  • /export. It writes into the session's folder on the server.

With a local runtime these features behave as before.

Reconnecting

A network drops connections that a local socket never does, so the terminal now reconnects instead of failing until restart. This applies to the local host as well as remote servers. The drop and the recovery are shown as notices, and retries back off from 250 ms to every 5 s. After reconnecting, the transcript is filled in from history pages with every event committed during the gap. Status, notice, catalog and subagent streams reopen, and approvals or questions that were still pending are offered again. Live text that streamed during the gap is not recovered; it appears once its message is committed, as when joining a turn in progress. After a reconnect the terminal checks the runtime's version again. dscode host status now also reports the host's version and default folder.

Behaviour stops there. There is no encryption and there are no user accounts: a token holder controls the runtime, including its tools and credentials. dscode exec, acp, trigger and host act on the local machine only. An editor or file link opened from the terminal opens locally. A provider login that redirects to localhost has to be completed from a terminal on the server.

/reload

Since 0.7.47 the host outlives the terminal. Quitting and starting dscode again therefore no longer reloaded configuration, and an MCP server added to config/mcp.local.yml stayed unavailable until the host restarted. /reload applies such edits to the running runtime.

  • Overlays. It rebuilds the overlay list in the launcher's order: the local overlays that exist now, including one created after start, then the other --patch files the runtime started with. It parses all of them before changing anything, then hands the complete patch generation to upstream's reconcileProfilePatches, the mechanism its own hot reload and configuration editor use. MCP servers that were added start, removed ones stop, changed ones restart and unchanged ones keep their connections. The profile context's overlays are updated too, so a later upstream reload does not revert them. A generation that fails to activate is rolled back.
  • The session's workspace. On a host, /reload then remounts the calling session's workspace as one unit: instructions from AGENTS.md/CLAUDE.md above the project, .dsh/skills, .agents/skills and .claude/skills folders above the project, and the merged hook layers. The old mount is removed first, because a scope holds one filesystem skill provider. SessionStart hooks do not run again.
  • Output and limits. It reports the files read, the MCP changes, each server's state and tool count, and the number of available skills. Like /mcp enable, it refuses while any agent on the runtime is running.

Skills added to the project's or the user's skill folders already appeared without a reload, because the skill provider watches those folders. A test on a live host session confirmed this before the change. MCP servers and overlays are shared by every session on a runtime, so /reload changes them for all sessions. The workspace remount applies only to the session that ran it. In single-process mode, /reload applies the overlays; ancestor instructions, ancestor skill folders and hooks there still need a restart.

Fixes

Four defects were found by the checks before release and fixed:

  • dscode config --runtime host:80 rejected an explicit port 80, because URL parsing drops the scheme's default port. The port is now read from the address as typed.
  • The carrier's optional tcp block was declared with required fields, so a Host without TCP, which is every local dscode, failed to load it.
  • A terminal quitting while its Host shut down kept waiting to reconnect. Retry timers no longer keep the process alive.
  • /reload failed with profile reload requires the root Include entry when another copy of dsh-app-boot was installed beside the one DSH booted with. Upstream registers the root Include entry in a module-level map, so /reload now loads the package from the running DSH entry. The test runtime's patched copy exposed this; upstream's plugin-manager reload could not stand in, because it does nothing without hot reload.

Verification

On macOS (arm64), npm run check passed:

  • lint and both TypeScript configurations;
  • 1,267 unit and component cases, with 80.27% first-party .mjs line coverage;
  • every integration verifier, the package checks and 63 offline evaluations.

verify-host.mjs gained a /reload scenario. A live session first answers that it sees no ancestor instructions. Then, after it started, the scenario writes an ancestor AGENTS.md, creates a skill folder above the project, and adds an MCP stdio server to one of the host's overlays. The scenario checks:

  • the new skill is not visible before /reload;
  • /reload reports the server added and active with its three tools;
  • the skill appears and /mcp tools lists the server's tools;
  • the model's next request carries the ancestor instruction;
  • after the server is taken out of the overlay, /reload reports it removed.

The fixture model now reports any MARKER_* instruction it receives. tests/reload.test.mjs covers the overlay order (including a local overlay created after start), the MCP change report, the profile requirement and the guarantee that a file that does not parse changes nothing.

scripts/verify-remote.mjs is new and part of the integration checks. It runs a real dscode server and a terminal launched through the real launcher, each with its own state directory, over loopback TCP. It covers:

  • token refusal on all three routes;
  • dscode config saving with owner-only permissions and reporting the server version, with the listing never showing the token;
  • the full sixteen-step terminal scenario through bare dscode on the default runtime, starting no local Host, with every session in the server's folder;
  • /export landing in the server's folder and not the terminal's, and the git branch read from the server's repository;
  • the server stopped and restarted mid-session, with the terminal announcing the drop and the reconnection and then completing a new turn;
  • content attachments;
  • a wrong token and an unknown alias refused without falling back to this machine.

The probe gained a remote scenario. verify-tui-runner.mjs still passes in all three modes. tests/remote-runtime.test.mjs covers the runtime list, address parsing, the server token and arguments, the routing between content and paths and its limits, and stream reopening. tests/local-carrier.test.mjs adds bearer-token checks on every TCP route, the socket without a token beside it, and the version refusal.

Not covered by an automated case:

  • a client and server on different machines (the verifier uses loopback);
  • an idle stream crossing real NAT;
  • an approval answered across a reconnect;
  • a message mixing local and server paths against the per-message limits;
  • the opt-in status-line branch item as rendered;
  • /reload restarting a changed (not added or removed) MCP server;
  • /reload reading a config/mcp.local.yml created after start end to end (unit-tested only, because the verifier must not write the repository's config folder);
  • hook layers remounted by /reload;
  • /reload in single-process mode;
  • live model calls;
  • Intel macOS and Linux runs of the new verifier (release CI qualifies those packages).

npm run doctor, verify-standalone.mjs and the Linux end-to-end run were not repeated locally for this release.