runa/vscode/README.md

560 lines
33 KiB
Markdown

# Runa VS Code client
This is a development client for a trusted localhost Runa image. It uses
the [version 5 protocol](../docs/protocol.md) over WebSockets with the `runa-debug`
subprotocol, not Rust APIs, and never
hosts actors or starts its own image.
## Run from source
Requirements: VS Code 1.96+, Node.js 22+, npm, and the repository's Rust toolchain.
To package and install the extension in your regular local VS Code instance,
run from the repository root:
```sh
npm --prefix vscode ci
mise run install-extension
```
This builds `runa-dap`, compiles the extension, packages `vscode/runa.vsix`, and
installs it with `code --install-extension ... --force`. Reload the VS Code window
after installation. Set `VSCODE_CLI` to a different CLI executable if necessary,
for example `VSCODE_CLI=code-insiders mise run install-extension`.
Set `runa.debugAdapterPath` to the absolute path of `target/debug/runa-dap`
to enable debugging (or install `runa-dap` on PATH). Installation does not change
your VS Code settings or start an image. The VSIX uses the local extension ID
`runa.runa`; packaging is local-only and skips the currently absent license and
repository metadata checks, not publication.
To generate the installable file without installing it:
```sh
mise run package-extension
```
This compiles the extension and writes `vscode/runa.vsix`. Install it using
VS Code's **Extensions: Install from VSIX...** command or
`code --install-extension vscode/runa.vsix`. The VSIX does not bundle the Rust
debug adapter; build or install `runa-dap` separately for debugging.
From the repository root:
```sh
npm --prefix vscode ci
mise run start
```
Open the repository in VS Code. In Run and Debug, select **Run Runa Extension**
and press F5. The launch task compiles TypeScript before opening an Extension
Development Host.
In that host:
1. Run **Runa: Connect to Local Image**. The default is `127.0.0.1:3737`;
`runa.address` changes the suggested endpoint.
2. Open **Runa** in the Activity Bar. Its **Image Sources** view automatically shows the image's
`source` directory (see [Image source files](#image-source-files)).
Right-click a `.rn` file and choose **Spawn** (initial state `null`) or
**Spawn with State...** (enter JSON in the input field, for example `0` for
`actors/counter`). **Runa: Spawn Actor** in **Actors** prompts for a source
path and state. The command reports and returns the new actor reference.
Mailbox capacity is 64.
3. Expand an actor directory to open its metadata, configuration, state,
mailbox, or log files. **Runa: Inspect Actor** also opens a combined JSON snapshot.
4. Right-click an actor to send a JSON message or a system message: pause,
resume, stop, kill, restart, reload, or become another source path. These
actions are also available in the Command Palette with an actor picker.
5. Use the **Advance Scheduler** button in the Actors toolbar (or
**Runa: Advance Scheduler**) to execute an explicit number of ticks
(instruction budget 32 per actor). Send only queues a message; it does not
execute it. For the bundled counter, send `{"op":"inc"}`, then advance.
It defaults missing state to zero on its first message and queues further
increments to itself. Pause/resume controls its loop.
6. Run **Runa: Disconnect** to close only this client connection.
JSON input supports null, booleans, signed 64-bit integers, strings, arrays,
and objects. Floats and out-of-range integers are rejected rather than rounded.
Use `{"$actor":"@<21-character-reference>"}` for a typed actor reference and
`{"$record":{"$actor":"literal"}}` to escape a marker-shaped object.
REPL-local names are not shared with the extension. Input uses the same
depth/node/string limits as the runtime; the full wire request must also fit
1 MiB. Cancelling a prompt sends no mutation. Changing connections during a
prompt rejects the action instead of applying it to the new image.
Connection prompts accept numeric IPv4 loopback addresses (`127.x.x.x`) or
`[::1]:port`. DNS names, wildcard addresses, remote addresses, and IPv4-mapped
IPv6 addresses are rejected. There is no authentication: connect only to a
trusted local server. In a remote VS Code window, localhost refers to the
machine running the extension host, not necessarily the desktop.
## Snapshot semantics
### Disk image save/restore
To reconnect to a saved image after stopping the server, start it with
`cargo run -p runa -- --restore /path/to/image.runa.json` or set `RUNA_RESTORE`
when using `mise run start`, then connect normally. Startup restoration adds
no bundled sources and executes no automatic ticks.
**Runa: Save Image...** prompts for a new file on the server's filesystem.
It saves the whole image at a handler boundary and leaves runnable actors
paused. Unfinished handlers, including paused/awaiting execution, reject saving
without changing the image. Existing destination files require confirmation
before atomic replacement.
Unsaved editor buffers are not part of the image snapshot; save desired source
or actor edits first.
**Runa: Restore Image...** prompts for a server-local snapshot and requires
confirmation before replacing the entire image. Validation/compilation errors
leave the current image unchanged; unfinished handlers also prevent replacement.
Restored actors previously eligible to run resume eligibility automatically,
while previously paused and failed actors retain their status. Scheduler ticks
are still explicit. Open actor files refresh and old-incarnation debug sessions
terminate. Sources/history, temporary behaviors, state, mailbox data, log
configuration and retained logs are included; editor buffers, breakpoints and
client session state are not. These snapshots do not serialize execution stacks
or provide automatic crash recovery. See
[runtime guarantees](../docs/runtime.md#image-save-and-restore).
### Live actor inspection
- The tree shows actor directories with references, lifecycle status, and queued/reserved counts.
Tooltips include implementation ID, incarnation, and failure text.
- Actor directories contain:
- `meta.json`: read-only identity, lifecycle, execution telemetry, source path
(`behavior_path`), pending source path, and temporary-behavior flag.
- `config.json`: `{"log_level":"info"}`, editable even while running.
- `state.json`: pretty-printed natural JSON state with two-space indentation,
using `$actor`/`$record` markers as needed and preserving exact 64-bit integers.
- `mailbox.json`: `{"reserved":[],"queued":[]}`, with at most one reserved
message. State and mailbox are writable only when paused or failed with no
active invocation. JSON uses two-space indentation. Saving never resumes the actor.
- `actor.log`: read-only retained entries, automatically refreshed on image changes,
recognized by VS Code's built-in Log language for syntax highlighting.
Entries use an RFC 5424-style syslog header:
`<134>1 - - runa <actor-ref> <sequence> - [INFO] message`.
Priorities use the local0 facility and mapped severity (trace/debug: debug,
info: informational, warn: warning, error: error, fatal: critical).
Timestamp and hostname are `-` because the image does not record them;
sequence IDs preserve ordering without inventing timestamps. Embedded
CR/LF characters are escaped so each entry occupies one line. Only the server's
latest 1,000 accepted entries per actor are available.
- `behavior.rn`: available for every actor; opens its loaded behavior or
actor-specific temporary script. Saving compiles an actor-local override.
These files use connection-scoped `runa-actor:` URIs. Removed/disconnected
actors' open JSON files retain their last values and become read-only; already
opened log/behavior files retain cached text.
- Inspect fetches a fresh snapshot through `inspect_actor`. Documents show
committed state, FIFO queued messages, the reserved message, status, and
active-execution telemetry. Tagged values distinguish actor references from
strings and preserve signed 64-bit integers exactly.
- Documents can be edited and saved while the actor is paused or failed
without an active invocation. Edit the tagged `actor.state`, `actor.reserved`,
`actor.queued`, and `actor.log_level` fields. Saving applies changed writable
fields; changes to identity, status, telemetry, connection metadata, and other
protected fields are discarded with a warning. The editor then reloads the
canonical server snapshot. Invalid writable fields reject the save.
Running, idle, suspended, and disconnected actors are read-only.
Unsaved edits are not replaced by automatic refreshes; saving rechecks the
current actor status. State/message edits and log-level changes are separate
server operations, not one transaction.
The timestamp records when this client received the data, not a runtime clock.
Server `image_changed` notifications automatically update the tree and open
documents. Bursts are coalesced, and changes arriving during a fetch trigger
a follow-up fetch. **Refresh Image** is also available on demand.
- Open documents are retained and marked `connected: false` on disconnect.
- Stop removes the actor after graceful shutdown (advance the scheduler); kill
removes it immediately, including failed actors. Failure alone does not remove
an actor. Removal discards state, mailbox, and retained logs. Removed actors
disappear from the tree; open snapshots keep their last values with
`removed: true` and become read-only. Restart preserves a live/failed actor's
identity but cannot recover a removed actor.
- Explicit disconnect, reconnect, and extension deactivation complete the
WebSocket close handshake. Deactivation waits for outstanding closes; an
unresponsive peer is forcibly disconnected after five seconds. Protocol
corruption and request failures still terminate the connection without retry.
Each connection gets distinct document URIs; reconnecting never replaces
snapshots from an earlier connection or another image. Snapshots are kept
for the extension session, not persisted across VS Code restarts.
- There is no auto-connect, polling, automatic reconnect, or request retry.
Timeout, peer disconnect, malformed data, and version/correlation failures
invalidate the connection and are surfaced in a notification and the **Runa**
output channel. Server-reported operation errors leave the connection usable.
- The client never ticks the scheduler implicitly or drains `get_events`.
Notifications are non-destructive invalidations, not a replayable runtime
event stream; reconnect fetches the current image rather than replaying history.
## Execution stack inspection
Right-click an actor and choose **Inspect Execution Stack...** (also available
in the Command Palette). Select a frame to open the exact loaded Rune source
in a read-only `runa-execution:` document and highlight its source span.
The picker identifies the incarnation, completed VM steps, functions, and source
positions. The current frame points to the next instruction, or the pending
await instruction at the last safe snapshot; callers point to call sites, not
their saved return addresses. At most 64 innermost frames are shown, with an
explicit truncation notice. Missing debug locations are labeled unavailable.
This never advances the scheduler or pauses the actor. Pause first for a stable
snapshot; running inspection is a point-in-time view. Idle/failed actors have no
active stack, and removed actors cannot be inspected. Pending source/temporary
behavior changes do not replace the loaded code. Open loaded documents retain
their text after edits, actor removal, and disconnect; connections use distinct
URIs. These are not source editors or temporary behavior documents.
Only the head Rune VM's visible frames are available, not native Rust frames or
stacks hidden inside nested futures. There are no locals, post-mortem stacks,
or Debug Adapter Protocol support yet.
For automation, `runa.inspectExecution` accepts `{actor_ref: string}` and an
optional zero-based frame index to navigate without the picker.
### Source breakpoints and stepping
Use the actor context menu or Command Palette:
1. **Set Actor Breakpoints...** accepts 1-based lines separated by commas in the
actor's **loaded source**, not a newer working/temporary buffer. It replaces
the actor's list; blank input clears it. Up to 128 lines are accepted. Lines
without executable entries are reported as unverified, never moved silently.
2. Queue a message and **Advance Scheduler**. A breakpoint pauses that actor
before a mapped line entry, preserving draft state and buffered sends.
**Inspect Execution Stack...** shows the `breakpoint` stop reason and opens
the exact loaded code. Other actors continue on ordinary ticks.
3. **Step Into Source Line** advances only a paused actor with an active
invocation, up to 1,000 instruction polls. It stops at another mapped line,
call-depth change, breakpoint, pending await, completion/failure, or budget.
It automatically opens the current frame. The Runa output channel records
the reason; budget/await/completion outcomes also produce a notification.
4. **Resume Actor** and advance the scheduler to continue normally. Resume
crosses the stopped breakpoint once; a loop can hit it again.
**Step Over Source Line** skips deeper function calls and stops at the next
mapped line in the original frame or at a mapped caller location.
**Step Out of Function** runs toward a mapped caller location; from the outermost
handler frame it runs toward completion. Both still stop at nested breakpoints,
pending awaits, failures, or the 1,000-instruction budget. Every command uses the
current frame/line as its origin: after an interrupted step, the next command
starts a new step rather than resuming a hidden plan. All three commands open
the exact loaded-source location and leave surviving nonfailed actors paused.
An idle actor cannot be stepped: set a breakpoint, send, then tick first, or
pause an already active handler. A completed step commits normally and leaves
the actor paused at the handler boundary, without consuming another message.
Failure is surfaced with its error text; successful shutdown may remove the actor.
Same-line loops are bounded and pending awaits return immediately.
Installation clears breakpoints to prevent stale source mappings; pending edits
do not. Breakpoints belong to the actor/image, so disconnect does not clear
them. Restart on unchanged code retains them. These commands also remain
available independently of the native debugger described below. Conditions,
hit counts, and live frame locals remain planned.
For automation, `runa.setBreakpoints` accepts `{actor_ref: string}` and optional
comma-separated 1-based line text (including `""` to clear). `runa.stepSource`
accepts `{actor_ref: string}` and returns its stop reason.
`runa.stepOver` and `runa.stepOut` accept the same argument and return stop reasons.
Use a server built with mode-aware stepping for these new commands.
### Native actor debugger
Connecting to an image automatically starts an image-wide debugger. Existing and
newly spawned actors appear as threads in Call Stack without being paused,
resumed, or otherwise executed by attachment. Set breakpoints directly in an
actor's `behavior.rn`, or select a thread and use Pause and the debug toolbar.
Right-clicking **Debug Actor** focuses and pauses only that actor in the existing
image session; failed actors are inspected without retrying them.
Disconnecting from the image ends its debug session without changing actors.
Set `runa.autoAttachDebugger` to `false` to disable automatic attachment on the
next connection. Debugger startup errors are reported without dropping the image
connection; check `runa.debugAdapterPath` if the executable cannot be launched.
With automatic attachment disabled, right-click an actor and choose **Debug Actor**.
Alternatively choose **Runa Actor** in Run and Debug and use an attach
configuration (omit `actorRef` to choose an actor):
```json
{
"type": "runa",
"request": "attach",
"name": "Attach to Runa Actor"
}
```
**Debug Actor** does not save open editors. Starting an attach configuration
directly through Run and Debug uses VS Code's `debug.saveBeforeStart` setting.
The extension launches the standalone Rust `runa-dap` Debug Adapter Protocol
server over standard input/output. Build it with `cargo build -p runa-dap`
and set `runa.debugAdapterPath` to the executable's absolute path (for example
`/path/to/runa/target/debug/runa-dap`), or install it on PATH with
`cargo install --path crates/runa-dap` from the repository root.
The adapter connects to the selected image over loopback WebSocket; it does not
start an image, spawn an actor, or open a separate debug port.
An image-wide session discovers one thread per actor incarnation. Removal,
restart, or a new loaded implementation retires only that actor's old thread
and its inspection handles; the image session and other actors remain usable.
Explicit single-actor sessions are still bound to their installed implementation
and incarnation, and end when either changes. Overlapping image/single-actor
sessions are rejected. Single-actor attach pauses a live nonfailed actor, retaining its active
invocation if any. An idle actor can be attached, but has no frames until it
starts a handler. An already-failed actor can also be attached without changing
its status, incarnation, state or mailbox.
For a DAP client connecting over TCP, start
`runa-dap --listen 127.0.0.1:4711 --connect 127.0.0.1:3737`.
The optional `RUNA_DAP_LISTEN` environment variable sets the same bind address.
The listener is unauthenticated and restricted to loopback; it serves one
session at a time. Port `0` selects an available port and prints it on stderr.
The extension continues to launch its own stdio adapter by default.
- Set gutter breakpoints in each actor's `behavior.rn`, or its immutable loaded source, opened by the
command or Call Stack view. The debugger serves the exact compiled text,
including temporary/older implementations, using source references.
Breakpoints in the image source editor are accepted only when both the saved
image file and any open editor buffer exactly match the loaded text. Other,
stale, or unsaved-different files are unverified, not silently mapped to code.
Changing a working document's breakpoint list removes that document's old
locations if it no longer matches. Loaded and matching working documents'
breakpoint groups are combined, with at most 128 distinct lines per actor.
Blank/nonexecutable lines remain unverified. Conditions, hit counts, and
logpoints are unsupported.
- **Continue** resumes scheduling eligibility but does **not** execute image
ticks. Send a message and use **Runa: Advance Scheduler** to run it toward a
breakpoint. Ordinary ticks can advance all eligible actors. **Pause** pauses
only the selected actor between ticks.
- Toolbar **Step Into**, **Step Over**, and **Step Out** use the existing bounded
source steps (1,000 instruction polls) and advance only this actor. A pending
await, breakpoint, budget, completion, or failure ends the request. The Debug
Console reports the exact reason; a surviving nonfailed actor stays paused.
Steps require an active invocation and never consume the next queued message.
- Call Stack displays at most the innermost 64 head-VM frames; truncation and
last-safe-await snapshots are identified in the stop description. Unmapped
frames have no invented source location. At handler completion the stack is
empty, state commits normally, and the actor remains paused. A failure shows
an exception stop with its error text, not a post-mortem stack. The adapter's
DAP `exceptionInfo` response supplies the sampled failure text with the
`runa.actorFailure` identifier and `unhandled` break mode. No exception class,
failed instruction location or stack trace is inferred from that text.
- Variables shows an **Actor snapshot (committed; not frame locals)** scope for
each active frame. All frames share that actor snapshot, with expandable
`committed_state`, `mailbox` (`reserved` as a zero/one-element list and
`queued`), `config`, and `metadata`. A reserved null message is distinct from
no reservation. Integers retain their exact signed/unsigned values; typed
actor references display as `@reference`, distinct from quoted strings.
Records and lists expose named/indexed children and support DAP paging.
String previews longer than 256 UTF-16 code units are explicitly abbreviated;
full strings remain available in the actor JSON files.
- The scope is read-only and contains server-sampled **committed** data, not
uncommitted state mutations, buffered sends, Rune locals or native objects.
Expansion never evaluates code, ticks the scheduler, or reads live VM slots.
Handles remain stable within a snapshot and expire on stepping, resume,
external snapshot changes or disconnect. A paused config/mailbox change
refreshes the stop and scope rather than changing data behind existing handles.
Handler boundaries and post-mortem failures have no frames, hence no DAP scopes;
use the actor files there.
- **Inspecting failures:** choose **Debug Actor** on a failed actor, or leave
the debugger attached when an invocation fails. The session reports an
exception stop and writes a failure/recovery explanation to the Debug Console.
Use `actor/metadata/failure`, `actor/committed_state` and
`actor/mailbox/reserved` in Watch or Debug Console to inspect retained data,
even though Call Stack is empty. Continue, Pause and all stepping controls
reject failed actors explicitly; they never retry, resume, or restart them.
State/mailbox repair, temporary-behavior edits and **Restart Actor** remain
separate explicit actions. Repair-only changes refresh the sampled data while
preserving failure; a new implementation or incarnation retires the actor's
thread in image mode, or ends an explicit single-actor session.
Detaching leaves the failed actor untouched. Exception filters and
first-chance/pre-unwind pauses are not supported.
- Watch and Debug Console support **snapshot paths**, not Rune expressions:
- `actor` expands the whole committed snapshot.
- `actor/committed_state` reads the last committed state.
- `actor/committed_state/items/0` reads a list element.
- `actor/mailbox/reserved/0` reads the reserved message, if present.
- `actor/config/log_level` and `actor/metadata/actor_ref` read configuration
and identity.
Use **Add to Watch** on a variable to generate its path automatically.
Path segments follow JSON Pointer escaping: `/` in a record key becomes
`~1`, and `~` becomes `~0`. An empty segment selects an empty record key.
List indices are canonical zero-based decimals (`0`, `1`, ...), not
expressions or negative indices. Paths are limited to 4,096 UTF-16 code
units and 64 segments after `actor`.
Results share the Variables view's previews, exact integers, expandable
children and read-only hints. No functions, operators, assignments,
property getters, implicit conversion, or actor execution are supported.
Missing keys, invalid indices/escapes, malformed paths and unsupported
contexts fail explicitly. Hover evaluation is not offered.
Watches resolve against the latest sampled paused snapshot, including
handler boundaries with no frame. Requests with a frame ID require a
current frame; after resume, snapshot lookup is unavailable until another
pause. Expanded result handles expire just like Variables handles.
- Frame locals, Rune expression evaluation, set-variable/set-expression, restart, instruction-level stepping,
exception filters, and native/hidden async frames are not implemented. Rune
native calls remain nonpreemptible. Unsupported requests fail explicitly.
- Disconnecting/stopping the debug session leaves the actor's current status
and breakpoint list unchanged: it does not kill, resume, or restart the actor.
Actor removal, implementation installation, or restart/incarnation change
retires only that actor's old thread in image mode; explicit single-actor
sessions terminate instead. Image disconnect ends the whole session.
Pending code edits alone do not change the loaded version.
Breakpoint lists are actor/image state shared with other clients and the
standalone **Set Actor Breakpoints...** command; a later writer replaces the
runtime list. Native verification updates when an external change removes or
restores a session breakpoint. Disconnect does not clear them.
For automation, `runa.debugActor` accepts `{actor_ref: string}` and returns
whether VS Code started the session.
For automation, the `runa.connect` command also accepts an explicit endpoint
string, and `runa.inspect` accepts an object with an `actor_ref` field.
**Copy Actor Reference** in the actor context menu or inline button copies the
raw reference to the clipboard.
`runa.spawn` accepts `{ path: string, state: string }`, where state is
natural JSON text, and resolves to the new reference. `runa.spawnSource` and
`runa.spawnSourceWithState` accept a source file URI (the latter also optional
JSON state text). `runa.send` accepts `{ actor_ref: string }` and JSON message
text. `runa.pause`, `runa.resume`, `runa.stop`, `runa.kill`, `runa.restart`, and
`runa.reload` accept `{ actor_ref: string }`; `runa.become` also accepts a
target source path.
`runa.tick` accepts a positive integer tick count. Command failures are reported
in the Runa output channel and an error notification; commands are never retried.
## Image source files
Actor context menus also offer **Edit Actor State...** and
**Edit Reserved and Queued Messages...**. They prefill JSON from the current
snapshot; editing requires a paused or failed actor with no active
execution. Message input uses `{"reserved":[],"queued":[]}` with zero or one
reserved message. Clearing the reservation removes the failed input that would
otherwise retry on restart. Edits do not resume actors. **Set Actor Log Level...**
selects trace/debug/info/warn/error/fatal (default info); snapshots show the level.
The actor's `actor.log` file shows retained output; CLI `logs [-f] [actor...]` also
supports a live event subscription.
### Actor-specific behaviors
**Edit Current Behavior** opens the actor's loaded script, not the potentially
newer working source. Saving compiles and schedules a temporary actor-specific
implementation for the next safe handler boundary; it never mutates active
frames, state, mailbox, shared source, or commit history. Compilation errors
reject the save and keep the previous behavior. Repeating the action reuses the
same document and temporary text. Other clients can read/edit the same temporary
behavior through the server; stale saves fail with a source-identity conflict.
Temporary behaviors survive actor restart, but not actor removal or server exit.
For failed actors, the new implementation is installed without clearing failure
or the reserved message; restart explicitly to retry.
**Reload Actor Source** clears the override and schedules current shared source.
**Go to Behavior** opens the original image source. **Update Behavior...** asks
before copying the temporary script into shared working source, checking both
source identities and the actor's source path for concurrent changes. It does
not commit or reload other actors. Unsaved temporary editor text is not copied.
The dedicated **Runa** Activity Bar sidebar contains **Image Sources** first,
followed by **Actors**. Both views are visible by default and do not require
an open workspace. The source tree uses native VS Code tree items, file icons,
directory expansion, editor opening, and file context menus.
The image owns actor source text. On connect, **Image Sources** automatically
shows its virtual filesystem beneath `source` (`runa-image://<endpoint>/source`).
No workspace changes, folder prompt, or extension restart is required.
Opening a file reads it from the image; saving writes back to the image.
The extension never adds image folders to the workspace. VS Code's public API
does not support overlaying virtual files onto the built-in workspace file tree,
so image files appear in the Runa sidebar's source tree backed by the filesystem provider.
Directories are derived from
source paths: `actors/counter` appears as `source/actors/counter.rn`. Source
paths are canonical and exclude the `source/` prefix and `.rn` suffix.
- **Save** writes the image's working source with compare-and-swap against the
last observed `source_id`. If another client changed the file, the save fails
instead of overwriting; reopen or revert to load the image's text. Saving
never commits. **New Source File...** in the toolbar or a directory context
menu creates a file at a relative path (including nested directories).
- **Rename or Move Source...** changes a file's path and moves its commit history.
If installed or pending actor behaviors use it, a warning asks to update their
paths atomically. Declining cancels the operation. Loaded code does not change;
future reloads follow the new path. Existing destinations are not overwritten.
- **Delete Source** confirms deletion of a file and its history. The server
rejects deletion if any nonfailed actor still uses its installed or pending
path, including paused actors: stop them first. Failed actors may retain loaded
code after deletion, but reload/go-to-source then fail until the path is recreated.
Directory rename/delete and cross-image moves are not supported.
- **Spawn** uses the latest saved working source, committed or not. If the
editor has unsaved changes, spawn and commit ask to save first; cancelling
does nothing. Unsaved editor text is never used for spawning/committing;
automatic diagnostics do send it to the connected trusted image for validation.
- **Spawn with State...** opens a JSON input field; cancelling the field does
not spawn an actor.
- **Runa: Commit Source** records the saved working source as a new revision
and reports its revision string.
- **Runa: Source History** lists revisions for the file and opens the selected
one as a read-only `runa-source:` document. Revision documents are cached per
connection session, never change, and remain open after disconnect.
- **Runa: Roll Back Source** asks for confirmation and replaces the working
source with the selected revision. It does not change running actors; use
**Reload** or **Become** to install source into an actor.
Server notifications refresh the source tree and clean open editors. For
automation, `runa.commitSource` accepts a source URI and returns the revision;
`runa.sourceHistory` accepts a URI and optional revision and returns the opened
document URI; `runa.rollbackSource` accepts a URI and optional revision and
returns the new `source_id` after confirmation.
## Compilation diagnostics
Open image sources and actor-specific `behavior.rn` documents are checked after a
300 ms edit debounce. Rune errors and warnings appear inline and in **Problems**,
with zero-based UTF-16 ranges converted from Rune's UTF-8 byte spans. Non-ASCII
text and CRLF line endings retain correct editor locations. Diagnostics without
a source location are displayed at the start of the document.
Validation compiles the current editor text through the connected image's native
context; it never executes the script, saves a source, commits, installs an
implementation, or changes actor state/mailboxes. Validation artifacts are
discarded rather than filling the image's compile cache on every keystroke.
Shared source may still be saved/committed with compilation errors; spawn and
reload require successful compilation. Temporary behavior saves still reject
invalid code without replacing the previously installed implementation.
Checks are serialized and edits coalesced. Responses for old document versions,
closed editors, or disconnected sessions are discarded. Closing a document or
disconnecting clears its diagnostics; reconnect validates current open image
sources but not old-session actor documents. Compiler errors are normal
validation results, not connection failures. Transport and compiler-service
failures remain explicit notifications in the Runa output channel.
Compilation checking is single-source, not an LSP: completion, cross-file
import resolution through image source paths, and quick fixes remain planned.
Debugger controls and committed actor-snapshot scopes are described separately
above; live Rune local-variable inspection remains unimplemented.
## Development and validation
From the repository root:
```sh
mise run format
mise run lint
mise run test
mise run test-extension-host
```
The normal test task covers portable-value validation, framing and UTF-8
fragmentation, subprotocol negotiation, interleaved notifications, refresh
coalescing, timeout/disconnect behavior, exact 64-bit integer display, and
interoperation with the real Rust server on an ephemeral loopback port.
The separate host test downloads VS Code 1.96.4 to the ignored `.vscode-test/`
cache in a temporary multi-root workspace and runs source folder save/spawn/
commit/history/rollback, spawn/send/system-message/tick, connection, inspection,
disconnect, and reconnect commands
against a test-owned Rust server. It verifies automatic document updates after
another client sends messages, pauses, and advances an actor into failure, as
well as session isolation. On headless Linux:
```sh
xvfb-run -a mise run test-extension-host
```
Detailed runtime event subscriptions, packaging for
distribution, and debugging remain future work.