Repository navigation
Releases: fro-bot/space-bus
Release list
v0.15.0
Minor Changes
-
fe0cc42:
@fro.bot/space-bus/coregains a browser-safe message-correlation primitive for dispatched prompts, plus typed partial-failure handles for dispatch:createDispatchMessageId()— generates an OpenCode-compatible ascending user message id (msg_+ a 12-character lowercase-hex timestamp/counter prefix + 14 random base62 characters), using onlyDate.now()and Web Crypto'scrypto.getRandomValues— no Node builtins, no dependency. IDs generated in the same millisecond sort ascending by an internal per-millisecond counter, matching OpenCode's own id ordering.dispatch()/toDispatchArgs()accept an optionalmessageIdonDispatchArgs.toDispatchArgsvalidates a caller-supplied id against the exactmsg_+ 12-hex + 14-alphanumeric shape and rejects anything else — including oversized or injection-like input — with a single stable, genericResulterror that never echoes the rejected value.dispatch()re-validates independently as defense in depth for callers that constructDispatchArgsdirectly.- When
messageIdis supplied,newandfollow-updispatch modes include it as the OpenCodemessageIDfield alongside the prompt'spartsand echo it back asmessageIdon the result. The key is omitted entirely — not sent asnull— whenever nomessageIdis given, andblocked/question-replyresults never carry amessageIdsince no ordinary prompt message is sent on those branches. - New
DispatchFailuretype and optionaldispatchFailurefield on dispatch'sResulterror branch give callers a typed handle for partial failures:phase: "not_sent"for failures verified to precede any mutation (e.g. pending-question verification failing under theblockedpolicy), andphase: "indeterminate"for failures where a mutating request (session create, prompt send, question reply) may already have reached OpenCode despite an error response. Known-safe fields (project, andsessionId/messageIdwhen known) are included; unknown fields are omitted rather than sent asundefined. Every other core function'sResulterror shape is unchanged. dispatchMetadata()(shared by the MCP and plugin tool surfaces) and thebus_taskMCP tool'soutputSchemagain the same optionalmessageId, included fornew/follow-upwhen supplied and omitted otherwise.bus_task's plugin and MCPmessageIdinput parameter is optional and validated the same way asdispatch(). Human-readable dispatch text output is unchanged.
All additions preserve the existing
Result<T>/DispatchResultcontract and the browser-safe/coreimport lane.
v0.14.0
Minor Changes
-
8802338: Add browser-safe session-content primitives to
@fro.bot/space-bus/coreso consumers (e.g. Mothership'side_*MCP surface) don't need to duplicate OpenCode message/question HTTP behavior:messages(sessionId, { context, limit? })— bounded full-message read. Resolves session ownership from the roster (never a caller-supplied directory) and returns{ sessionId, project, messages: [{ id?, role, createdAt?, parts }] }— stable message identity/ordering metadata plus parsed parts, no unknown envelope fields.limitdefaults to 20 and is capped at a hard maximum of 200;0, negative, fractional,NaN, andInfinityvalues are rejected before any fetch.questions(target, { context })— complete project- or session-scoped pending-question read (target: { project } | { sessionId }, exactly one — both-present or neither-present is rejected before any fetch). Returns one entry per pending request with its full nested subquestion list (requestId,sessionId,questions: [{ header?, question, multiple, custom, options: [{ label, description? }] }]) — a single request can carry more than one subquestion, and every subquestion's header/question/multiple/custom/options round-trips.answerQuestion({ sessionId, requestId, answers }, { context })— explicit question answer. Runtime-validatesanswersis a non-emptystring[][]before any fetch, verifiesrequestIdbelongs to a pending question onsessionId(arequestIdfor a different session is refused with no mutation), and verifiesanswers.lengthmatches that request's subquestion count before sending (mismatched cardinality is refused with no mutation).dispatch()gains an optional, backward-compatibleargs.onPendingQuestion: "question-reply" | "blocked", validated and preserved bytoDispatchArgs(invalid values are rejected). The default preserves 0.13.1's fail-open question-reply behavior for existing callers, including its behavior when/questionis unreadable."blocked"is fail-closed: it returns a typed{ mode: "blocked", requestId }result with no reply and no follow-up prompt when a question is pending, and returns a stableResulterror — instead of guessing — when pending-question state can't be verified (a non-2xx or unparseableGET /questionresponse), for callers that must never silently reinterpret a follow-up prompt as a question answer or dispatch under ambiguous state.
All additions preserve the existing
Result<T>/BusContextcontract, the per-call context validation gate, the localhost guard, internal session-to-directory resolution, and the browser-safe/coreimport lane (no Node builtins, no ambient env reads).
Patch Changes
- f599505: Fix the CLI's
--configflag leaking intoprocess.env.SPACE_BUS_CONFIG.resolveRoster()insrc/cli.tspreviously mutated the process environment as a side effect of resolving an explicit--configpath, which persisted for the remainder of the process and could cause later ambient (env-based) roster resolution in the same process to resolve a stale/deleted path.resolveRosterPath()insrc/config.tsnow accepts an optional explicit override argument, so the CLI can thread--configthrough without touchingprocess.env.
v0.13.1
Patch Changes
- b35b839: Fix
bus_registryMCP tool responses: every successful action now returnsstructuredContent({ rosters: [...] }forlist,{}for all other actions), matching the tool's declared output schema. Previously, non-listactions (register,create,unregister,set-default,use,add-project,remove-project,update-project) omittedstructuredContent, causing MCP clients to reject the response with error -32602 even though the underlying mutation had already succeeded.
v0.13.0
Minor Changes
-
7444781: Per-call roster addressing and the
bus_registrymanagement tool (multi-roster Phase B).All five
bus_*tools accept an optionalrosterparameter (a registry name from@fro.bot/space-bus/registry) to target any registered roster instead of the ambient workspace/SPACE_BUS_CONFIGresolution — and every tool result now opens with aroster:header naming the roster it resolved, on both success AND error results — so cross-roster calls (and cross-roster errors) are always visible. A sixth tool,bus_registry, manages the machine's roster registry from both surfaces: list rosters, create a roster in-app, register/unregister/set-default, and add/remove/update roster projects. On the MCP surface,bus_registry useselects a session-active roster (ephemeral, per connection) that omitted-rostercalls resolve to; unregistering the active roster clears the session's selection (falls back to ambient resolution). WhenSPACE_BUS_CONFIGis unset, MCP ambient resolution now also falls back to the registry default (bus_registry set-default) before erroring —set-defaultgains a new routing role beyond bookkeeping. Plugin calls remain workspace-directory-first.Note: every tool's text output now begins with a
roster:line — consumers parsing raw tool text must account for the new first line (structuredbus_taskmetadata is unchanged).
v0.12.0
Minor Changes
-
9897976: Multi-roster substrate: roster registry + mutation library on a new
/registrysubpath.- Roster registry — a per-user, machine-level registry (
$XDG_CONFIG_HOME/space-bus/rosters.json, else~/.config/space-bus/rosters.json) mapping human-readable names to roster paths:readRegistry,registerRoster,unregisterRoster,setDefaultRoster,resolveRosterByName. Names are validated ([a-z0-9-], case-insensitive-unique), paths are canonicalized with symlinked entries rejected, and the registry never stores credentials. The registry is additive and optional —SPACE_BUS_CONFIGand<directory>/spacebus.jsonresolution are unchanged. - Roster mutation — programmatic
spacebus.jsonediting:createRoster(write + register in one op),addProject,removeProject,updateProject,editServer. Every edit is read-validate-mutate-validate-write with atomic replacement; an invalid edit (schema violation or non-loopbackbaseUrl) leaves the file byte-identical and returnsok:false. - Discovery
rosterPath— managed-daemon discovery files now record their roster path at spawn (optional field; pre-field files parse unchanged), enabling future reconciliation to name which roster a running daemon belongs to.
Node-only surface (
import ... from "@fro.bot/space-bus/registry"); the browser-safe lanes (/core,/contract,/format,/attach) are unchanged. Tool surfaces are unchanged — per-call roster addressing ships in a follow-up release. - Roster registry — a per-user, machine-level registry (
v0.11.0
Minor Changes
- e1718a5: Add
space-bus service install|uninstall|status|stop|start— generates a per-user launchd agent wrappingserve --foregroundso a roster's managed daemon survives reboots and crashes. macOS v1 (fails fast on other platforms); restart-on-abnormal-exit with a 10s throttle; idempotent reinstall; full uninstall reversal.
v0.10.1
Patch Changes
- d3b2631: Fix browser-safe subpath artifacts (
./core,./contract,./format) shipping a Node-onlycreateRequire/node:moduleprelude that broke Vite/browser bundling (e.g. Mothership). These entrypoints are now built with a browser-targeted Bun.build call, matching./attach. Added a dist-level browser-safety test asserting the published artifacts contain nonode:imports, closing the gap where the existing src-level browser-safety test passed while the published dist was broken.
v0.10.0
Minor Changes
-
2cb9245: Remap the
./serversubpath to the plugin entry: OpenCode's plugin loader resolvesexports["./server"]beforemain, so the managed-server lifecycle API published there broke plugin loading withPlugin export is not a function(affects 0.6.0–0.9.0 when loaded from npm). The lifecycle API (ensureServer,serverStatus,stopServer,superviseServer, …) moves to@fro.bot/space-bus/managed-server.Migration: direct importers of
@fro.bot/space-bus/servermust switch to@fro.bot/space-bus/managed-serverbefore upgrading — after this release,/serverresolves to the plugin factory and the old lifecycle imports fail silently (missing properties), not with an error.
v0.9.0
Minor Changes
-
6e57bff: Add asynchronous-delegation foundation: a normalized session-state enum and the
bus_waittool.- Normalized state enum (
running | blocked | complete | failed | not_found) plus aresultAvailablesignal, derived once in core (deriveSessionState) and emitted identically bybus_status, the librarysnapshot(), andbus_wait— callers no longer infer state frombusy/blocked. Exported on the/coreand/contractsubpaths (SessionState,SessionStateInfo). bus_wait(fifth tool, both plugin and MCP surfaces): blocks until any watched session needs attention (completes, blocks on a question, fails, or is not found) or a bounded timeout elapses, then returns each session's normalized state plus which session(s) woke the wait. Level-triggered, stateless (a bounded long-poll within the single call), watches sessions across directories, and never throws across the boundary. Replaces the poll-bus_status-in-a-loop pattern with one call that makes progress.
Fire-and-forget push notification (get notified as tasks complete without holding a call open) is a deferred follow-on — this release delivers the blocking-wait foundation and the shared state contract it builds on.
- Normalized state enum (
v0.8.1
Patch Changes
- fb5bc47: Fix:
space-bus serve --foregroundnow reaps a surviving daemon child on the supervision death path. The managed daemon is aharnesswrapper plus anopencodechild in the same process group; when the wrapper died but the child survived, the supervisor exited fail-closed without signaling the group, leaving the child orphaned and holding its port. The death path now group-signals the surviving members before exiting, guarded so it never signals a process that recycled the wrapper pid (it fires only when the leader is dead and its group is still alive, which cannot be a recycled pgid). - 930d698: Fix:
stopServernow reaps a surviving orphaned child when the recorded managed wrapper has already died (identity verification fails). Previously this branch cleaned up the discovery record but never reaped the process group, leaking the wrapper'sopencodechild (still holding its port) whenever the wrapper died alone before a stop was requested. This brings the dead-wrapper branch to parity with the existing supervision died-path reap, reusing the same guardedreapSurvivingGroup(no-op for a live/recycled leader; no-op if the whole group is already dead).stopServer's return value is unchanged (stopped: false) — reaping an orphan is best-effort cleanup, not a verified stop.