@el4cteo/rbx-studio-mcp 0.4.6 → 0.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -167,8 +167,39 @@ Gotchas: during a playtest two sessions connect — pass `studioId` explicitly a
167
167
 
168
168
  Every call is logged with its latency, above a trace of the last forty. The badge names which agents are connected, with process id and uptime.
169
169
 
170
+ Under the header is a command line. Type a command, or type a sentence and it goes to a coding agent.
171
+
172
+ | | |
173
+ |---|---|
174
+ | `help` | list everything |
175
+ | `doctor` | check every part of the setup |
176
+ | `status` `version` `place` `clients` | what this session is |
177
+ | `studios` `use <n>` | which Studio window calls land on |
178
+ | `theme [name]` `visuals` `log [level]` `clear` `copy` | the panel itself |
179
+ | `port [n]` `reconnect` | the connection |
180
+ | `agent [use <id>\|new]` `stop` | which coding agent runs your prompts |
181
+ | anything else | sent to that agent |
182
+
183
+ Up and down walk the history, Tab completes.
184
+
185
+ **Prompts start a real agent.** The bridge runs whichever tool you have on PATH — Claude Code, Codex, opencode, DeepSeek Harness, Gemini, Cursor, Amp, Qwen Code, Factory Droid, goose, GitHub Copilot CLI, Aider, Crush — in headless mode. It connects back to this same bridge, drives the same Studio, and its work streams into the log line by line. It is a separate session from the terminal you may already have open, billed separately, and it is allowed the `rbx-studio` tools and nothing else. `stop` cancels it.
186
+
170
187
  Hover the tab on the right edge for eight themes — Lattice, Observatory, Orbit, Void, Nebula, Aurora, Phosphor, Blueprint. Each redraws the activity band, not just its colours. Your pick is remembered.
171
188
 
189
+ ## DeepSeek Harness (dsh)
190
+
191
+ dsh is "everything is a plugin", and this server becomes one through its MCP bridge. One overlay row, and the tools arrive as `mcp__rbx-studio__*` — the same names Claude Code and Codex use.
192
+
193
+ ```sh
194
+ dsh --profile headless --patch config/dsh.cordis.yml "add a spawn point"
195
+ ```
196
+
197
+ To keep it, append that row to your own patch layer — `$DSH_HOME/profiles/<name>/cordis.patch.yml` for one profile, `$DSH_HOME/cordis.patch.yml` for all of them. Don't overwrite the file; it may already hold unrelated patches.
198
+
199
+ `config/dsh.cordis.yml` in this repo is the row. The console panel also lists `dsh` as an agent, and passes the overlay itself, so a prompt typed there works before you have merged anything.
200
+
201
+ Two limits, both dsh's rather than ours: its headless profile prints the final answer instead of streaming, so the panel shows the reply at the end rather than step by step, and it has no `--resume`, so every prompt is a fresh conversation. It needs `DEEPSEEK_API_KEY`.
202
+
172
203
  ## Batching
173
204
 
174
205
  Every write tool takes an array — ten script edits is one call.
@@ -197,7 +228,7 @@ Each batch is one **Ctrl+Z**, and all-or-nothing: a failed match leaves the plac
197
228
  - **Property names are checked** against the running engine's API dump, so a typo comes back as `Anchorred` → `Anchored` instead of a runtime error.
198
229
  - **~16k tokens of schema**, cursor-paged and capped, with `detail: concise | standard | full`.
199
230
 
200
- Not built here: terrain, material generation.
231
+ Not built here: material generation.
201
232
 
202
233
  ## Security
203
234
 
@@ -1,4 +1,19 @@
1
1
  import { randomUUID } from "node:crypto";
2
+ /**
3
+ * Whether this process is serving an agent a console panel started.
4
+ *
5
+ * Read from the environment at load, and read HERE rather than travelling with
6
+ * the client description, because the description does not exist yet at the
7
+ * moment that matters. A process registers the instant it starts and only
8
+ * learns its client's name at the MCP handshake, so the arrival -- the thing
9
+ * the panel logs and badges -- is announced first and described second. Sending
10
+ * the flag with the description meant it always arrived one announcement too
11
+ * late, and every prompt still flashed "2 MCP clients connected".
12
+ *
13
+ * The variable is set by `harness.run` on the agent CLI it spawns, and reaches
14
+ * here because that CLI starts this server as its own child.
15
+ */
16
+ export const SPAWNED_BY_PANEL = process.env["RBX_STUDIO_MCP_SPAWNED"] === "1";
2
17
  /**
3
18
  * The bridge as seen from the process that actually holds the port.
4
19
  *
@@ -31,8 +46,8 @@ export class LocalBridge {
31
46
  // this class carries a client id at all: the process holding the port is
32
47
  // one agent among however many are connected, and it is connected from the
33
48
  // moment it starts, not from the moment it happens to ask for something.
34
- inner.noteClient(this.clientId);
35
- this.keepalive = setInterval(() => inner.noteClient(this.clientId), KEEPALIVE_MS);
49
+ inner.noteClient(this.clientId, { spawned: SPAWNED_BY_PANEL });
50
+ this.keepalive = setInterval(() => inner.noteClient(this.clientId, { spawned: SPAWNED_BY_PANEL }), KEEPALIVE_MS);
36
51
  this.keepalive.unref();
37
52
  }
38
53
  /** Drops this process from the bridge's client list on shutdown. */
@@ -60,7 +75,11 @@ export class LocalBridge {
60
75
  this.inner.notePlaceName(studioId, placeName, context);
61
76
  }
62
77
  describe(about) {
63
- this.inner.noteClient(this.clientId, { ...about, pid: process.pid });
78
+ this.inner.noteClient(this.clientId, {
79
+ ...about,
80
+ pid: process.pid,
81
+ spawned: SPAWNED_BY_PANEL,
82
+ });
64
83
  }
65
84
  }
66
85
  //# sourceMappingURL=api.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"api.js","sourceRoot":"","sources":["../../src/bridge/api.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAoEzC;;;;;;;GAOG;AACH;;;;;;;;;;;GAWG;AACH,MAAM,YAAY,GAAG,MAAM,CAAC;AAE5B,MAAM,OAAO,WAAW;IAKO,KAAK;IAJzB,OAAO,GAAG,IAAI,CAAC;IACP,QAAQ,GAAG,UAAU,EAAE,CAAC;IACxB,SAAS,CAAiB;IAE3C,YAA6B,KAAa;qBAAb,KAAK;QAChC,2EAA2E;QAC3E,yEAAyE;QACzE,2EAA2E;QAC3E,yEAAyE;QACzE,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAChC,IAAI,CAAC,SAAS,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,YAAY,CAAC,CAAC;QAClF,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,CAAC;IACzB,CAAC;IAED,oEAAoE;IACpE,OAAO;QACL,aAAa,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAC9B,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACzC,CAAC;IAED,IAAI,CACF,EAAU,EACV,MAAM,GAA4B,EAAE,EACpC,OAAO,GAA8C,EAAE;QAEvD,wEAAwE;QACxE,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACrC,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAI,EAAE,EAAE,MAAM,EAAE,EAAE,GAAG,OAAO,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;IACjF,CAAC;IAED,KAAK,CAAC,QAAQ;QACZ,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACrC,OAAO;YACL,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE;YACvB,QAAQ,EAAE,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC;YAC5C,cAAc,EAAE,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,IAAI,CAAC,QAAQ,CAAC;SACzD,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,QAAgB;QAC9B,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IAChD,CAAC;IAED,KAAK,CAAC,aAAa,CAAC,QAAgB,EAAE,SAAiB,EAAE,OAAgB;QACvE,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,QAAQ,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;IACzD,CAAC;IAED,QAAQ,CAAC,KAAwB;QAC/B,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,GAAG,KAAK,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;IACvE,CAAC;CACF"}
1
+ {"version":3,"file":"api.js","sourceRoot":"","sources":["../../src/bridge/api.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AA8DzC;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,OAAO,CAAC,GAAG,CAAC,wBAAwB,CAAC,KAAK,GAAG,CAAC;AAQ9E;;;;;;;GAOG;AACH;;;;;;;;;;;GAWG;AACH,MAAM,YAAY,GAAG,MAAM,CAAC;AAE5B,MAAM,OAAO,WAAW;IAKO,KAAK;IAJzB,OAAO,GAAG,IAAI,CAAC;IACP,QAAQ,GAAG,UAAU,EAAE,CAAC;IACxB,SAAS,CAAiB;IAE3C,YAA6B,KAAa;qBAAb,KAAK;QAChC,2EAA2E;QAC3E,yEAAyE;QACzE,2EAA2E;QAC3E,yEAAyE;QACzE,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,OAAO,EAAE,gBAAgB,EAAE,CAAC,CAAC;QAC/D,IAAI,CAAC,SAAS,GAAG,WAAW,CAC1B,GAAG,EAAE,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,OAAO,EAAE,gBAAgB,EAAE,CAAC,EACpE,YAAY,CACb,CAAC;QACF,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,CAAC;IACzB,CAAC;IAED,oEAAoE;IACpE,OAAO;QACL,aAAa,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAC9B,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACzC,CAAC;IAED,IAAI,CACF,EAAU,EACV,MAAM,GAA4B,EAAE,EACpC,OAAO,GAA8C,EAAE;QAEvD,wEAAwE;QACxE,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACrC,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAI,EAAE,EAAE,MAAM,EAAE,EAAE,GAAG,OAAO,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;IACjF,CAAC;IAED,KAAK,CAAC,QAAQ;QACZ,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACrC,OAAO;YACL,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE;YACvB,QAAQ,EAAE,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC;YAC5C,cAAc,EAAE,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,IAAI,CAAC,QAAQ,CAAC;SACzD,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,QAAgB;QAC9B,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IAChD,CAAC;IAED,KAAK,CAAC,aAAa,CAAC,QAAgB,EAAE,SAAiB,EAAE,OAAgB;QACvE,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,QAAQ,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;IACzD,CAAC;IAED,QAAQ,CAAC,KAAwB;QAC/B,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,QAAQ,EAAE;YACnC,GAAG,KAAK;YACR,GAAG,EAAE,OAAO,CAAC,GAAG;YAChB,OAAO,EAAE,gBAAgB;SAC1B,CAAC,CAAC;IACL,CAAC;CACF"}
@@ -0,0 +1,348 @@
1
+ /**
2
+ * The half of the console panel's command line that Studio cannot answer.
3
+ *
4
+ * Most of what someone types into the panel is answered inside the plugin --
5
+ * `clear`, `theme`, `status` are all facts Studio already holds. The rest are
6
+ * questions about the machine: is the port really ours, is the installed plugin
7
+ * the one this package ships, which Studios are attached, and what happens when
8
+ * a free-text prompt is typed at an agent. Those live here.
9
+ *
10
+ * Answers come back as console rows rather than as prose, so the panel prints
11
+ * them with the same sigils, colours and columns as everything else it logs. A
12
+ * diagnostic that renders differently from the log it appears in reads as a
13
+ * foreign object pasted into the session.
14
+ */
15
+ import { collectChecks } from "../doctor.js";
16
+ import { find, installed, matchesClient, run, } from "./harness.js";
17
+ const agents = new Map();
18
+ function agentFor(studioId) {
19
+ let state = agents.get(studioId);
20
+ if (state === undefined) {
21
+ state = { harnessId: null, session: null, active: null };
22
+ agents.set(studioId, state);
23
+ }
24
+ return state;
25
+ }
26
+ /**
27
+ * Whether an agent is mid-run for this Studio.
28
+ *
29
+ * The panel dims its caret while one is, and the only other signal it gets is
30
+ * the `agent-run` event fired when the run ends. A prompt that never started --
31
+ * no harness installed, or a name that resolves to nothing -- fires no such
32
+ * event, so the panel would sit "busy" forever on the one failure it is most
33
+ * likely to hit first. Answered with the reply instead.
34
+ */
35
+ export function agentRunning(studioId) {
36
+ return agents.get(studioId)?.active !== null && agents.get(studioId)?.active !== undefined;
37
+ }
38
+ const MARK = {
39
+ ok: "ok",
40
+ warn: "warn",
41
+ bad: "error",
42
+ };
43
+ /**
44
+ * `doctor`, rendered for a log rather than for a terminal.
45
+ *
46
+ * The CLI prints a blank line between checks and indents the explanation under
47
+ * the title. Neither survives here: the console already separates rows and
48
+ * already indents continuations, so the same output pasted in would be double
49
+ * spaced and double indented. The checks are shared; only the shaping differs.
50
+ */
51
+ function renderChecks(checks) {
52
+ const lines = [];
53
+ for (const check of checks) {
54
+ lines.push({
55
+ level: MARK[check.status],
56
+ message: check.title,
57
+ // The CLI's own two-space indent, undone: it exists to hang the detail
58
+ // under the title in a flat stream of text, and this log does that itself.
59
+ detail: check.detail.split("\n").map((piece) => piece.trim()).join(" "),
60
+ });
61
+ }
62
+ const bad = checks.filter((check) => check.status === "bad").length;
63
+ const warn = checks.filter((check) => check.status === "warn").length;
64
+ lines.push({
65
+ level: bad > 0 ? "error" : warn > 0 ? "warn" : "ok",
66
+ message: `${checks.length - bad - warn} passed, ${warn} warning, ${bad} failure`,
67
+ });
68
+ return lines;
69
+ }
70
+ /** The Studios on this bridge, with the one calls land on marked. */
71
+ function renderStudios(bridge, asking) {
72
+ const sessions = bridge.list();
73
+ if (sessions.length === 0)
74
+ return [{ level: "dim", message: "no Studio is connected" }];
75
+ const active = bridge.activeId("panel");
76
+ return [
77
+ {
78
+ level: "info",
79
+ message: `${sessions.length} Studio${sessions.length === 1 ? "" : "s"} on this bridge`,
80
+ },
81
+ ...sessions.map((session, index) => {
82
+ const here = session.studioId === asking ? " (this panel)" : "";
83
+ return {
84
+ level: session.studioId === active ? "ok" : "dim",
85
+ message: ` ${index + 1}. ${session.placeName}${here}`,
86
+ detail: `${session.context ?? "edit"} ${session.transport} ${session.studioId.slice(0, 8)}`,
87
+ };
88
+ }),
89
+ ];
90
+ }
91
+ /**
92
+ * Resolves what someone typed after `use` into a studioId.
93
+ *
94
+ * Both forms are accepted because both are in front of them: `studios` prints a
95
+ * number, and the tools print an id. Requiring the id would mean reading a
96
+ * hex string off one line to type it into the next.
97
+ */
98
+ function resolveStudio(bridge, token) {
99
+ const sessions = bridge.list();
100
+ const index = Number(token);
101
+ if (Number.isInteger(index) && index >= 1 && index <= sessions.length) {
102
+ return sessions[index - 1]?.studioId ?? null;
103
+ }
104
+ const byId = sessions.find((session) => session.studioId.startsWith(token));
105
+ return byId?.studioId ?? null;
106
+ }
107
+ /** Which harnesses are installed, and which one prompts will go to. */
108
+ function renderAgents(state, bridge) {
109
+ const available = installed();
110
+ if (available.length === 0) {
111
+ return [
112
+ {
113
+ level: "warn",
114
+ message: "no coding agent found on PATH",
115
+ detail: "Install one (claude, codex, opencode, gemini, cursor-agent) and it " +
116
+ "will be picked up automatically. Commands still work without one.",
117
+ },
118
+ ];
119
+ }
120
+ //[[ Says what a prompt WOULD do, not what the registry contains.
121
+ //
122
+ // Listing an "(in use)" that the prompt then ignores is worse than not
123
+ // marking one at all -- that mismatch is what made the wrong agent answer
124
+ // look like a bug in the agent rather than in this choice. So the mark comes
125
+ // from the same function the prompt uses, and when that cannot decide,
126
+ // nothing is marked and the list says so.
127
+ //]]
128
+ const wanted = preferred(state, bridge, available);
129
+ const connected = (entry) => bridge.clientList().some((client) => matchesClient(entry, client.name));
130
+ return [
131
+ { level: "info", message: `${available.length} agent${available.length === 1 ? "" : "s"} available` },
132
+ ...available.map((entry) => {
133
+ const marks = [];
134
+ if (connected(entry))
135
+ marks.push("connected");
136
+ if (entry.id === wanted?.id)
137
+ marks.push(state.harnessId === null ? "would answer" : "in use");
138
+ return {
139
+ level: entry.id === wanted?.id ? "ok" : "dim",
140
+ message: ` ${entry.id}`,
141
+ detail: marks.length > 0 ? `${entry.label} (${marks.join(", ")})` : entry.label,
142
+ };
143
+ }),
144
+ wanted === undefined
145
+ ? { level: "warn", message: "several agents are connected -- `agent use <id>` to pick one" }
146
+ : { level: "dim", message: "type anything that is not a command to send it to the agent" },
147
+ ];
148
+ }
149
+ /**
150
+ * Which agent an unconfigured panel should use, or nothing when it cannot tell.
151
+ *
152
+ * The old rule was "the first one installed", which is alphabetical accident
153
+ * dressed up as a decision: someone with opencode open typed a prompt and
154
+ * Claude Code answered, because `claude` sits first in the registry. The answer
155
+ * was fine and came from the wrong program.
156
+ *
157
+ * The bridge knows more than the registry does. Every agent driving this Studio
158
+ * is connected to it and says its name, so the panel can answer with the one
159
+ * the user is actually working in. That settles the ordinary case, which is one
160
+ * agent attached to one Studio.
161
+ *
162
+ * When two are attached there is no signal that says which one the user meant.
163
+ * Recency does not: in the report that prompted this, opencode had been up a
164
+ * minute and Claude Code forty seconds, and the wanted answer was the older
165
+ * one. So this returns nothing and the caller asks -- the same shape as
166
+ * AMBIGUOUS_STUDIO, which refuses to guess between two open places for exactly
167
+ * this reason. The choice is remembered, so it is one question once.
168
+ */
169
+ function preferred(state, bridge, available) {
170
+ if (state.harnessId !== null)
171
+ return find(state.harnessId);
172
+ if (available.length === 1)
173
+ return available[0];
174
+ const matched = available.filter((harness) => bridge.clientList().some((client) => matchesClient(harness, client.name)));
175
+ if (matched.length === 1)
176
+ return matched[0];
177
+ // Nothing connected that we recognise: the registry order is as good a guess
178
+ // as any, and refusing here would block a panel whose user has no agent
179
+ // attached at all -- which is a normal way to use this.
180
+ return matched.length === 0 ? available[0] : undefined;
181
+ }
182
+ /** Asks which of several attached agents should answer, and how to say so. */
183
+ function askWhichAgent(bridge, available) {
184
+ const attached = available.filter((harness) => bridge.clientList().some((client) => matchesClient(harness, client.name)));
185
+ return [
186
+ {
187
+ level: "warn",
188
+ message: `${attached.length} agents are connected to this Studio -- which should answer?`,
189
+ },
190
+ ...attached.map((harness) => ({
191
+ level: "dim",
192
+ message: ` agent use ${harness.id}`,
193
+ detail: harness.label,
194
+ })),
195
+ { level: "dim", message: "asked once; the choice is remembered until you change it" },
196
+ ];
197
+ }
198
+ /**
199
+ * Says where the prompt came from, once at the start of a conversation.
200
+ *
201
+ * The agent is spawned in this repository's working directory, so its instinct
202
+ * for "create a simple script, then edit it" is a file on disk -- which is what
203
+ * happened the first time this was tried: it reached for Bash and `cat >`, was
204
+ * refused by its own permission mode, and reported the test impossible. The
205
+ * user was sitting in front of a Roblox place at the time.
206
+ *
207
+ * Nothing about the prompt itself says otherwise. It arrives as a bare line
208
+ * typed into a panel, with none of the context the panel has: that there is a
209
+ * place open, that the tools reaching it are already connected, and that
210
+ * "script" almost certainly means a Script instance rather than a source file.
211
+ *
212
+ * Only on the first turn. A continued session already has this in its history,
213
+ * and repeating it every message would spend context restating something the
214
+ * agent has not forgotten -- and would start reading as nagging.
215
+ */
216
+ export function frame(prompt) {
217
+ return ("You are answering someone typing into a console panel docked inside " +
218
+ "Roblox Studio, with a place open in front of them. The rbx-studio MCP " +
219
+ "tools are connected to that place: use them for anything about the " +
220
+ "game -- `script_create` and `script_edit` for code, `create`/`modify` " +
221
+ "for instances, `terrain` for terrain. \"A script\" means a Script " +
222
+ "instance in the place, not a file on disk. Reach for the filesystem " +
223
+ "only if they clearly mean this MCP server's own source.\n\n" +
224
+ prompt);
225
+ }
226
+ /**
227
+ * Starts an agent on `prompt` and streams its work into the asking panel.
228
+ *
229
+ * Returns immediately with the rows that describe the start. Everything the
230
+ * agent then does arrives over the Studio's own event stream, which is why this
231
+ * needs the bridge and not just a return value: a run takes minutes, and a
232
+ * request held open that long is a request that times out.
233
+ */
234
+ function startAgent(bridge, state, studioId, prompt) {
235
+ if (state.active !== null) {
236
+ return [{ level: "warn", message: "an agent is already running", detail: "type stop to cancel it" }];
237
+ }
238
+ const available = installed();
239
+ if (available.length === 0)
240
+ return renderAgents(state, bridge);
241
+ const wanted = preferred(state, bridge, available);
242
+ if (wanted === undefined) {
243
+ return state.harnessId === null
244
+ ? askWhichAgent(bridge, available)
245
+ : [{ level: "error", message: `unknown agent "${state.harnessId}"` }];
246
+ }
247
+ const emit = (line) => {
248
+ bridge.notify(studioId, { event: "console", ...line });
249
+ };
250
+ const started = run(wanted, state.session === null ? frame(prompt) : prompt, {
251
+ cwd: process.cwd(),
252
+ session: state.session,
253
+ emit,
254
+ });
255
+ state.active = started;
256
+ void started.done.then((session) => {
257
+ state.session = session;
258
+ state.active = null;
259
+ bridge.notify(studioId, { event: "agent-run", state: "idle" });
260
+ });
261
+ //[[ The panel already echoed the line; this says who picked it up.
262
+ //
263
+ // It used to repeat the prompt, which put the same sentence on screen twice
264
+ // in the same second under two different labels -- the second one reading as
265
+ // if the agent had said it back.
266
+ //]]
267
+ return [
268
+ {
269
+ level: "dim",
270
+ message: wanted.label,
271
+ detail: state.session === null ? "starting" : "continuing",
272
+ },
273
+ ];
274
+ }
275
+ /**
276
+ * Answers one line typed into a console panel.
277
+ *
278
+ * Unknown commands are not an error here: the plugin decides what is a command
279
+ * and what is a prompt before it ever reaches this file, so anything arriving
280
+ * with a name we do not know is a genuine mismatch between plugin and server
281
+ * versions, and saying so beats guessing.
282
+ */
283
+ export async function handleConsole(bridge, port, request) {
284
+ const state = agentFor(request.studioId);
285
+ switch (request.command) {
286
+ case "doctor":
287
+ return renderChecks(await collectChecks(port));
288
+ case "studios":
289
+ return renderStudios(bridge, request.studioId);
290
+ case "use": {
291
+ const token = request.args[0];
292
+ if (token === undefined) {
293
+ return [{ level: "dim", message: "usage: use <number|id>", detail: "run studios to see them" }];
294
+ }
295
+ const target = resolveStudio(bridge, token);
296
+ if (target === null)
297
+ return [{ level: "error", message: `no Studio matches "${token}"` }];
298
+ bridge.setActiveForAll(target);
299
+ const name = bridge.list().find((session) => session.studioId === target)?.placeName ?? target;
300
+ return [{ level: "ok", message: `calls now target ${name}` }];
301
+ }
302
+ case "agent": {
303
+ const verb = request.args[0];
304
+ if (verb === undefined || verb === "list")
305
+ return renderAgents(state, bridge);
306
+ if (verb === "use") {
307
+ const id = request.args[1];
308
+ if (id === undefined)
309
+ return [{ level: "dim", message: "usage: agent use <id>" }];
310
+ const wanted = find(id);
311
+ if (wanted === undefined)
312
+ return [{ level: "error", message: `unknown agent "${id}"` }];
313
+ state.harnessId = wanted.id;
314
+ // A new harness cannot continue another harness's conversation, and
315
+ // handing it a stranger's session id fails in a way that reads as the
316
+ // switch itself being broken.
317
+ state.session = null;
318
+ return [{ level: "ok", message: `prompts now go to ${wanted.label}` }];
319
+ }
320
+ if (verb === "new") {
321
+ state.session = null;
322
+ return [{ level: "ok", message: "next prompt starts a fresh conversation" }];
323
+ }
324
+ return [{ level: "dim", message: "usage: agent [list|use <id>|new]" }];
325
+ }
326
+ case "stop": {
327
+ if (state.active === null)
328
+ return [{ level: "dim", message: "nothing is running" }];
329
+ state.active.cancel();
330
+ return [{ level: "warn", message: "stopping the agent" }];
331
+ }
332
+ case "prompt": {
333
+ const prompt = request.line.trim();
334
+ if (prompt === "")
335
+ return [];
336
+ return startAgent(bridge, state, request.studioId, prompt);
337
+ }
338
+ default:
339
+ return [
340
+ {
341
+ level: "error",
342
+ message: `this bridge does not know "${request.command}"`,
343
+ detail: "the plugin and the server are probably different builds",
344
+ },
345
+ ];
346
+ }
347
+ }
348
+ //# sourceMappingURL=console.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"console.js","sourceRoot":"","sources":["../../src/bridge/console.ts"],"names":[],"mappings":"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"}