agent-workbench 0.4.0 → 0.4.1
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 +4 -2
- package/dist/server.js +195 -4
- package/dist/web/assets/{de-fq_F8zRo.js → de-CXvJ7uwt.js} +1 -1
- package/dist/web/assets/{es-BCB5Npb3.js → es-BrWQ_4kO.js} +1 -1
- package/dist/web/assets/{fr-ClL0BO43.js → fr-Cv_qM9E8.js} +1 -1
- package/dist/web/assets/{highlight-2xGvJy1W.js → highlight-nRm-DNTr.js} +1 -1
- package/dist/web/assets/index-BFpUqUWT.js +139 -0
- package/dist/web/assets/index-BnrUWT0F.css +32 -0
- package/dist/web/assets/{ja-C7KI4Gtm.js → ja-h9u8sCMK.js} +1 -1
- package/dist/web/assets/{ko-C8der8AK.js → ko-m0EzMLN6.js} +1 -1
- package/dist/web/assets/{pt-BR-rXhfKNdD.js → pt-BR-BAHKndFr.js} +1 -1
- package/dist/web/assets/{ru-BjrRxMeM.js → ru-D1S49VN_.js} +1 -1
- package/dist/web/assets/{zh-CN-DDY7nBK5.js → zh-CN-CH6r3KGh.js} +1 -1
- package/dist/web/index.html +2 -2
- package/package.json +1 -1
- package/dist/web/assets/index-BoXw3kE_.css +0 -32
- package/dist/web/assets/index-Bu0LGhCK.js +0 -139
package/README.md
CHANGED
|
@@ -31,7 +31,7 @@ the app touching it.
|
|
|
31
31
|
| **Tabs** | Several live sessions at once, from any of the CLIs, each in its own directory. They survive an `F5`: the processes live on the server, not in the browser tab. Each tab's dot tells you whether the agent is working, idle or waiting for an answer, on the CLIs that publish their status ([below](#multiple-clis)). |
|
|
32
32
|
| **Zero-cost startup** | When you open the app, tabs come back as **sleeping tabs**: you can read them in full, and they don't launch any CLI. You open the CLI with a button when you want to write to the agent. |
|
|
33
33
|
| **History** | Your projects and past conversations in the sidebar, with a filter, and those of all four CLIs together under each project, each with its badge. Opening one resumes it with its CLI, in the same session. **Archive history…** hides a CLI's sessions from before today in one go, and each project can be archived whole with one button: nothing is deleted, and both can be undone. |
|
|
34
|
-
| **Conversation** | The active session's messages, live, with tool calls collapsed and their results inside. Search, jump between matches, copy any message or code block and, depending on the CLI, answer the agent's questions from the chat. |
|
|
34
|
+
| **Conversation** | The active session's messages, live, with tool calls collapsed and their results inside. A row at the bottom tells you when your message reached the CLI, whether the agent is working and, with Claude Code, which subagents are still at it after the agent finished its turn. Search, jump between matches and back to the end, copy any message or code block and, depending on the CLI, answer the agent's questions from the chat. |
|
|
35
35
|
| **Continue with…** | With more than one CLI installed, a conversation can be continued with another CLI in the same folder. The new agent starts from a transcript of the last turns, not from the context the previous one had. |
|
|
36
36
|
| **Search everything** | With more than one CLI and something saved in the local copy, the sidebar filter also searches the text of every saved conversation, not just their titles. |
|
|
37
37
|
| **Context meter** | Tokens from the last request against the model's context window. Tokens, never money. |
|
|
@@ -383,7 +383,9 @@ The app captures exactly those combinations and no others. `Shift+Tab` only
|
|
|
383
383
|
when focus isn't on the terminal, because inside it's the key the CLI uses to
|
|
384
384
|
switch modes. Everything else —`Esc`, `Esc Esc`, `Ctrl+C`, `Ctrl+R`, `Ctrl+O`,
|
|
385
385
|
the arrow keys and above all `Alt+V`, which pastes images— reaches the CLI
|
|
386
|
-
untouched.
|
|
386
|
+
untouched. `Ctrl+V` too: what it does there is up to the CLI. Only in the
|
|
387
|
+
console at the bottom, on Windows, `Ctrl+V` pastes text, as in Windows
|
|
388
|
+
Terminal.
|
|
387
389
|
|
|
388
390
|
---
|
|
389
391
|
|
package/dist/server.js
CHANGED
|
@@ -170,6 +170,7 @@ function noticeText(part) {
|
|
|
170
170
|
return part.detail.length > 0 ? `La CLI devolvi\xF3 un error: ${part.detail}` : "La CLI devolvi\xF3 un error";
|
|
171
171
|
}
|
|
172
172
|
}
|
|
173
|
+
var MAX_SUBAGENT_DESCRIPTION = 120;
|
|
173
174
|
var EMPTY_CONTEXT_USAGE = {
|
|
174
175
|
lastRequestTokens: 0,
|
|
175
176
|
lastOutputTokens: 0,
|
|
@@ -738,6 +739,8 @@ function parseClientMessage(raw) {
|
|
|
738
739
|
if (files.length > 0) message.files = files;
|
|
739
740
|
}
|
|
740
741
|
if (record["send"] === false) message.send = false;
|
|
742
|
+
const requestId = asNonEmptyString(record["requestId"]);
|
|
743
|
+
if (requestId !== null) message.requestId = requestId;
|
|
741
744
|
return message;
|
|
742
745
|
}
|
|
743
746
|
case "agent.mode": {
|
|
@@ -5069,6 +5072,135 @@ function toTurnDuration(record) {
|
|
|
5069
5072
|
return { eventId, durationMs };
|
|
5070
5073
|
}
|
|
5071
5074
|
|
|
5075
|
+
// packages/server/src/agents/claude-code/subagent-tracker.ts
|
|
5076
|
+
var SUBAGENT_TOOLS = /* @__PURE__ */ new Set(["Agent", "Task"]);
|
|
5077
|
+
var MAX_TRACKED = 50;
|
|
5078
|
+
var NOTIFICATION = /<task-notification>([\s\S]*?)<\/task-notification>/g;
|
|
5079
|
+
var TOOL_USE_ID = /<tool-use-id>\s*([^<\s]+)\s*<\/tool-use-id>/;
|
|
5080
|
+
function labelOf(value) {
|
|
5081
|
+
if (typeof value !== "string") return "";
|
|
5082
|
+
return value.replace(/\s+/g, " ").trim().slice(0, MAX_SUBAGENT_DESCRIPTION);
|
|
5083
|
+
}
|
|
5084
|
+
function notifiedToolUseIds(text) {
|
|
5085
|
+
if (!text.includes("<task-notification>")) return [];
|
|
5086
|
+
const ids = [];
|
|
5087
|
+
for (const match of text.matchAll(NOTIFICATION)) {
|
|
5088
|
+
const id = TOOL_USE_ID.exec(match[1] ?? "")?.[1];
|
|
5089
|
+
if (id !== void 0) ids.push(id);
|
|
5090
|
+
}
|
|
5091
|
+
return ids;
|
|
5092
|
+
}
|
|
5093
|
+
function contentText(content) {
|
|
5094
|
+
if (typeof content === "string") return content;
|
|
5095
|
+
if (!Array.isArray(content)) return "";
|
|
5096
|
+
let text = "";
|
|
5097
|
+
for (const block of content) {
|
|
5098
|
+
const record = recordOf(block);
|
|
5099
|
+
if (record !== null && record["type"] === "text" && typeof record["text"] === "string") {
|
|
5100
|
+
text += record["text"];
|
|
5101
|
+
}
|
|
5102
|
+
}
|
|
5103
|
+
return text;
|
|
5104
|
+
}
|
|
5105
|
+
function launchedInBackground(toolUseResult) {
|
|
5106
|
+
const result = recordOf(toolUseResult);
|
|
5107
|
+
return result !== null && (result["status"] === "async_launched" || result["isAsync"] === true);
|
|
5108
|
+
}
|
|
5109
|
+
var SubagentTracker = class {
|
|
5110
|
+
/** Los que no terminaron, en orden de lanzamiento (`Map` conserva el de insercion). */
|
|
5111
|
+
pending = /* @__PURE__ */ new Map();
|
|
5112
|
+
/** Una linea del JSONL, ya parseada. Nunca lanza: el esquema cambia. */
|
|
5113
|
+
observe(record) {
|
|
5114
|
+
if (record["isSidechain"] === true) return;
|
|
5115
|
+
switch (record["type"]) {
|
|
5116
|
+
case "assistant":
|
|
5117
|
+
this.observeLaunches(record);
|
|
5118
|
+
return;
|
|
5119
|
+
case "user":
|
|
5120
|
+
this.observeResults(record);
|
|
5121
|
+
this.finish(notifiedToolUseIds(contentText(recordOf(record["message"])?.["content"])));
|
|
5122
|
+
return;
|
|
5123
|
+
case "queue-operation":
|
|
5124
|
+
if (record["operation"] === "enqueue" && typeof record["content"] === "string") {
|
|
5125
|
+
this.finish(notifiedToolUseIds(record["content"]));
|
|
5126
|
+
}
|
|
5127
|
+
return;
|
|
5128
|
+
case "attachment": {
|
|
5129
|
+
const attachment = recordOf(record["attachment"]);
|
|
5130
|
+
if (attachment?.["type"] === "queued_command" && typeof attachment["prompt"] === "string") {
|
|
5131
|
+
this.finish(notifiedToolUseIds(attachment["prompt"]));
|
|
5132
|
+
}
|
|
5133
|
+
return;
|
|
5134
|
+
}
|
|
5135
|
+
default:
|
|
5136
|
+
return;
|
|
5137
|
+
}
|
|
5138
|
+
}
|
|
5139
|
+
/**
|
|
5140
|
+
* Los que siguen trabajando y los lanzo el proceso de `launchedAt` o uno
|
|
5141
|
+
* posterior. Uno sin hora no se puede atribuir a ningun proceso y no cuenta.
|
|
5142
|
+
*/
|
|
5143
|
+
running(launchedAt) {
|
|
5144
|
+
const result = [];
|
|
5145
|
+
for (const subagent of this.pending.values()) {
|
|
5146
|
+
if (subagent.startedAt > 0 && subagent.startedAt >= launchedAt) result.push({ ...subagent });
|
|
5147
|
+
}
|
|
5148
|
+
return result;
|
|
5149
|
+
}
|
|
5150
|
+
/** El archivo se reemplazo: lo anotado era de otra lectura. */
|
|
5151
|
+
reset() {
|
|
5152
|
+
this.pending.clear();
|
|
5153
|
+
}
|
|
5154
|
+
observeLaunches(record) {
|
|
5155
|
+
const content = recordOf(record["message"])?.["content"];
|
|
5156
|
+
if (!Array.isArray(content)) return;
|
|
5157
|
+
const startedAt = readTimestamp(record["timestamp"]);
|
|
5158
|
+
for (const block of content) {
|
|
5159
|
+
const call = recordOf(block);
|
|
5160
|
+
if (call === null || call["type"] !== "tool_use") continue;
|
|
5161
|
+
const toolUseId = call["id"];
|
|
5162
|
+
if (typeof toolUseId !== "string" || toolUseId.length === 0) continue;
|
|
5163
|
+
if (typeof call["name"] !== "string" || !SUBAGENT_TOOLS.has(call["name"])) continue;
|
|
5164
|
+
if (this.pending.has(toolUseId)) continue;
|
|
5165
|
+
const input = recordOf(call["input"]);
|
|
5166
|
+
this.pending.set(toolUseId, {
|
|
5167
|
+
toolUseId,
|
|
5168
|
+
description: labelOf(input?.["description"]),
|
|
5169
|
+
background: input?.["run_in_background"] === true,
|
|
5170
|
+
startedAt
|
|
5171
|
+
});
|
|
5172
|
+
if (this.pending.size > MAX_TRACKED) {
|
|
5173
|
+
const oldest = this.pending.keys().next();
|
|
5174
|
+
if (!oldest.done) this.pending.delete(oldest.value);
|
|
5175
|
+
}
|
|
5176
|
+
}
|
|
5177
|
+
}
|
|
5178
|
+
/**
|
|
5179
|
+
* El resultado de la llamada. En segundo plano dice que quedo andando, y
|
|
5180
|
+
* sigue; si no, termino —o no llego a lanzarse, que para el hilo es lo mismo—.
|
|
5181
|
+
*/
|
|
5182
|
+
observeResults(record) {
|
|
5183
|
+
const content = recordOf(record["message"])?.["content"];
|
|
5184
|
+
if (!Array.isArray(content)) return;
|
|
5185
|
+
for (const block of content) {
|
|
5186
|
+
const result = recordOf(block);
|
|
5187
|
+
if (result === null || result["type"] !== "tool_result") continue;
|
|
5188
|
+
const toolUseId = result["tool_use_id"];
|
|
5189
|
+
if (typeof toolUseId !== "string") continue;
|
|
5190
|
+
const subagent = this.pending.get(toolUseId);
|
|
5191
|
+
if (subagent === void 0) continue;
|
|
5192
|
+
if (result["is_error"] !== true && launchedInBackground(record["toolUseResult"])) {
|
|
5193
|
+
subagent.background = true;
|
|
5194
|
+
} else {
|
|
5195
|
+
this.pending.delete(toolUseId);
|
|
5196
|
+
}
|
|
5197
|
+
}
|
|
5198
|
+
}
|
|
5199
|
+
finish(toolUseIds) {
|
|
5200
|
+
for (const toolUseId of toolUseIds) this.pending.delete(toolUseId);
|
|
5201
|
+
}
|
|
5202
|
+
};
|
|
5203
|
+
|
|
5072
5204
|
// packages/server/src/agents/claude-code/conversation-follower.ts
|
|
5073
5205
|
var MAX_ATTACHMENT_LINKS = 100;
|
|
5074
5206
|
var MAX_PLAN_REFS = 50;
|
|
@@ -5155,6 +5287,11 @@ var ConversationFollower = class {
|
|
|
5155
5287
|
* ninguna—, y ahi vale con que se lanzo la pestana (`LAUNCH_PERMISSION_MODE`).
|
|
5156
5288
|
*/
|
|
5157
5289
|
permissionMode = null;
|
|
5290
|
+
/**
|
|
5291
|
+
* Los subagentes que no terminaron (§4.15). Mira lineas que no son tarjetas
|
|
5292
|
+
* —el aviso de fin es una `queue-operation`—, asi que va antes de todo filtro.
|
|
5293
|
+
*/
|
|
5294
|
+
subagents = new SubagentTracker();
|
|
5158
5295
|
/** Los topes de cada parte. Los de transporte salvo que se lea para la copia propia. */
|
|
5159
5296
|
limits;
|
|
5160
5297
|
/** La pestana cuyos documentos se reconocen. Ver el constructor. */
|
|
@@ -5207,6 +5344,10 @@ var ConversationFollower = class {
|
|
|
5207
5344
|
getUsage() {
|
|
5208
5345
|
return this.usage;
|
|
5209
5346
|
}
|
|
5347
|
+
/** Los subagentes del proceso de `launchedAt` que siguen trabajando (§4.15). */
|
|
5348
|
+
runningSubagents(launchedAt) {
|
|
5349
|
+
return this.subagents.running(launchedAt);
|
|
5350
|
+
}
|
|
5210
5351
|
/** Ultimos `limit` eventos, que es lo que se quiere ver al abrir el panel. */
|
|
5211
5352
|
getTail(limit) {
|
|
5212
5353
|
return this.jsonl.getTail(limit);
|
|
@@ -5252,6 +5393,7 @@ var ConversationFollower = class {
|
|
|
5252
5393
|
this.permissionMode = null;
|
|
5253
5394
|
this.attachmentRoots.clear();
|
|
5254
5395
|
this.planFiles = [];
|
|
5396
|
+
this.subagents.reset();
|
|
5255
5397
|
}
|
|
5256
5398
|
/** true si la linea es la invocacion de `/model` que la CLI guarda. */
|
|
5257
5399
|
mentionsModelCommand(record) {
|
|
@@ -5268,6 +5410,7 @@ var ConversationFollower = class {
|
|
|
5268
5410
|
* los que ya guardo, para pegarle a uno anterior la duracion o una imagen.
|
|
5269
5411
|
*/
|
|
5270
5412
|
consume(record, lineNumber, events) {
|
|
5413
|
+
this.subagents.observe(record);
|
|
5271
5414
|
if (record["type"] === "cost-state") {
|
|
5272
5415
|
this.sessionVariants.observeCostState(record, Date.now());
|
|
5273
5416
|
this.recomputeModel();
|
|
@@ -5647,6 +5790,10 @@ var ClaudeCodeSessionFollower = class {
|
|
|
5647
5790
|
hasOpenToolCall() {
|
|
5648
5791
|
return false;
|
|
5649
5792
|
}
|
|
5793
|
+
/** Los subagentes del proceso de `launchedAt` que siguen trabajando (§4.15). */
|
|
5794
|
+
runningSubagents(launchedAt) {
|
|
5795
|
+
return this.inner.runningSubagents(launchedAt);
|
|
5796
|
+
}
|
|
5650
5797
|
};
|
|
5651
5798
|
|
|
5652
5799
|
// packages/server/src/agents/claude-code/session-scan.ts
|
|
@@ -10168,6 +10315,9 @@ var INITIAL_EVENT_LIMIT = 300;
|
|
|
10168
10315
|
var MAX_PAGE_LIMIT = 500;
|
|
10169
10316
|
var PENDING_QUESTION_LOOKBACK = 50;
|
|
10170
10317
|
var POLL_DEBOUNCE_MS = 150;
|
|
10318
|
+
function sameSubagents(a, b) {
|
|
10319
|
+
return a.length === b.length && a.every((item, index) => item.toolUseId === b[index]?.toolUseId && item.background === b[index]?.background);
|
|
10320
|
+
}
|
|
10171
10321
|
var ConversationHub = class extends EventEmitter {
|
|
10172
10322
|
constructor(registry, agents) {
|
|
10173
10323
|
super();
|
|
@@ -10175,7 +10325,10 @@ var ConversationHub = class extends EventEmitter {
|
|
|
10175
10325
|
this.agents = agents;
|
|
10176
10326
|
registry.on("session", (terminalId) => void this.rebind(terminalId));
|
|
10177
10327
|
registry.on("changed", () => {
|
|
10178
|
-
for (const [terminalId, entry] of this.entries)
|
|
10328
|
+
for (const [terminalId, entry] of this.entries) {
|
|
10329
|
+
this.refreshOpenToolCall(terminalId, entry);
|
|
10330
|
+
this.refreshSubagents(terminalId, entry);
|
|
10331
|
+
}
|
|
10179
10332
|
});
|
|
10180
10333
|
}
|
|
10181
10334
|
registry;
|
|
@@ -10199,6 +10352,7 @@ var ConversationHub = class extends EventEmitter {
|
|
|
10199
10352
|
usage: { ...EMPTY_CONTEXT_USAGE },
|
|
10200
10353
|
waitingFor: null,
|
|
10201
10354
|
openToolCall: false,
|
|
10355
|
+
subagents: [],
|
|
10202
10356
|
plans: []
|
|
10203
10357
|
};
|
|
10204
10358
|
}
|
|
@@ -10235,6 +10389,7 @@ var ConversationHub = class extends EventEmitter {
|
|
|
10235
10389
|
permissionMode: entry.observedMode ?? entry.assumedMode,
|
|
10236
10390
|
waitingFor: entry.waitingFor,
|
|
10237
10391
|
openToolCall: entry.openToolCall,
|
|
10392
|
+
subagents: entry.subagents,
|
|
10238
10393
|
plans: plans === null ? [] : await plans.describe(
|
|
10239
10394
|
{ cwd: entry.cwd, sessionId: entry.sessionId },
|
|
10240
10395
|
entry.follower.getPlanFiles()
|
|
@@ -10284,6 +10439,7 @@ var ConversationHub = class extends EventEmitter {
|
|
|
10284
10439
|
waitingFor: null,
|
|
10285
10440
|
statusActivity: null,
|
|
10286
10441
|
openToolCall: false,
|
|
10442
|
+
subagents: [],
|
|
10287
10443
|
primed: false,
|
|
10288
10444
|
stopWatchingStatus: null,
|
|
10289
10445
|
followTicker: null
|
|
@@ -10326,6 +10482,23 @@ var ConversationHub = class extends EventEmitter {
|
|
|
10326
10482
|
entry.openToolCall = open8;
|
|
10327
10483
|
this.emit("toolCall", terminalId, open8);
|
|
10328
10484
|
}
|
|
10485
|
+
/**
|
|
10486
|
+
* Recalcula los subagentes que siguen trabajando y avisa si cambiaron
|
|
10487
|
+
* (§4.15). Solo los del proceso vivo: sin pty no queda ninguno, y los de un
|
|
10488
|
+
* proceso anterior murieron con el sin escribir el aviso de fin. Una CLI cuyo
|
|
10489
|
+
* seguidor no los declara no tiene nunca.
|
|
10490
|
+
*/
|
|
10491
|
+
refreshSubagents(terminalId, entry) {
|
|
10492
|
+
const launchedAt = this.registry.launchedAtOf(terminalId);
|
|
10493
|
+
const next = launchedAt === null || entry.follower.runningSubagents === void 0 ? [] : [...entry.follower.runningSubagents(launchedAt)];
|
|
10494
|
+
if (sameSubagents(next, entry.subagents)) return;
|
|
10495
|
+
entry.subagents = next;
|
|
10496
|
+
this.emit("subagents", terminalId, next);
|
|
10497
|
+
}
|
|
10498
|
+
/** Los subagentes que siguen trabajando, lo ultimo avisado. [] si nadie sigue la pestana. */
|
|
10499
|
+
getSubagents(terminalId) {
|
|
10500
|
+
return this.entries.get(terminalId)?.subagents ?? [];
|
|
10501
|
+
}
|
|
10329
10502
|
/**
|
|
10330
10503
|
* La pestana cambio de sesion: se suelta el seguidor viejo y se sigue la
|
|
10331
10504
|
* nueva, sin perder a quien estaba mirando.
|
|
@@ -10506,6 +10679,7 @@ var ConversationHub = class extends EventEmitter {
|
|
|
10506
10679
|
permissionMode: this.resetMode(entry),
|
|
10507
10680
|
waitingFor: entry.waitingFor,
|
|
10508
10681
|
openToolCall: entry.openToolCall,
|
|
10682
|
+
subagents: entry.subagents,
|
|
10509
10683
|
// Este snapshot es sincronico y describir un plan pide el disco. Quien
|
|
10510
10684
|
// necesita los planes usa `subscribe`, que si puede esperar.
|
|
10511
10685
|
plans: []
|
|
@@ -10662,6 +10836,7 @@ var ConversationHub = class extends EventEmitter {
|
|
|
10662
10836
|
return;
|
|
10663
10837
|
}
|
|
10664
10838
|
this.refreshOpenToolCall(terminalId, entry);
|
|
10839
|
+
this.refreshSubagents(terminalId, entry);
|
|
10665
10840
|
if (options.silent) return;
|
|
10666
10841
|
const observed = entry.follower.getPermissionMode();
|
|
10667
10842
|
if (observed !== null) {
|
|
@@ -19331,6 +19506,7 @@ function attachTerminalSocket(options) {
|
|
|
19331
19506
|
const onConversationMode = (terminalId, mode) => broadcast({ type: "conversation.mode", terminalId, mode });
|
|
19332
19507
|
const onConversationWaiting = (terminalId, waitingFor) => broadcast({ type: "conversation.waiting", terminalId, waitingFor });
|
|
19333
19508
|
const onConversationToolCall = (terminalId, open8) => broadcast({ type: "conversation.toolCall", terminalId, open: open8 });
|
|
19509
|
+
const onConversationSubagents = (terminalId, subagents) => broadcast({ type: "conversation.subagents", terminalId, subagents });
|
|
19334
19510
|
const onConversationPlans = (terminalId, plans) => broadcast({ type: "conversation.plans", terminalId, plans });
|
|
19335
19511
|
const onConversationTurns = (terminalId, turns) => broadcast({ type: "conversation.turns", terminalId, turns });
|
|
19336
19512
|
const onConversationParts = (terminalId, updates) => broadcast({ type: "conversation.parts", terminalId, updates });
|
|
@@ -19394,7 +19570,8 @@ function attachTerminalSocket(options) {
|
|
|
19394
19570
|
waitingFor: snapshot.waitingFor,
|
|
19395
19571
|
// El de ahora y no el del snapshot: leer la configuracion es asincrono,
|
|
19396
19572
|
// y un aviso que salio mientras tanto no puede quedar pisado por uno viejo.
|
|
19397
|
-
openToolCall: conversations.isToolCallOpen(terminalId)
|
|
19573
|
+
openToolCall: conversations.isToolCallOpen(terminalId),
|
|
19574
|
+
subagents: conversations.getSubagents(terminalId)
|
|
19398
19575
|
});
|
|
19399
19576
|
});
|
|
19400
19577
|
};
|
|
@@ -19409,6 +19586,7 @@ function attachTerminalSocket(options) {
|
|
|
19409
19586
|
conversations.on("mode", onConversationMode);
|
|
19410
19587
|
conversations.on("waiting", onConversationWaiting);
|
|
19411
19588
|
conversations.on("toolCall", onConversationToolCall);
|
|
19589
|
+
conversations.on("subagents", onConversationSubagents);
|
|
19412
19590
|
conversations.on("turns", onConversationTurns);
|
|
19413
19591
|
conversations.on("plans", onConversationPlans);
|
|
19414
19592
|
conversations.on("parts", onConversationParts);
|
|
@@ -19578,6 +19756,12 @@ function attachTerminalSocket(options) {
|
|
|
19578
19756
|
*/
|
|
19579
19757
|
case "agent.submit": {
|
|
19580
19758
|
const { terminalId, text: text2, images } = message;
|
|
19759
|
+
let settled = false;
|
|
19760
|
+
const settle = (delivered) => {
|
|
19761
|
+
if (settled || message.requestId === void 0) return;
|
|
19762
|
+
settled = true;
|
|
19763
|
+
send(socket, { type: "agent.submitted", terminalId, requestId: message.requestId, delivered });
|
|
19764
|
+
};
|
|
19581
19765
|
const submittedOwner = draftOwnerOf(registry.get(terminalId));
|
|
19582
19766
|
if (submittedOwner !== null) drafts.delete(submittedOwner);
|
|
19583
19767
|
const files = message.files ?? [];
|
|
@@ -19587,6 +19771,7 @@ function attachTerminalSocket(options) {
|
|
|
19587
19771
|
"submit-failed",
|
|
19588
19772
|
serverText("filesPerMessage", { max: MAX_FILES_PER_SUBMIT })
|
|
19589
19773
|
);
|
|
19774
|
+
settle(false);
|
|
19590
19775
|
break;
|
|
19591
19776
|
}
|
|
19592
19777
|
if (images.length > MAX_IMAGES_PER_SUBMIT) {
|
|
@@ -19595,9 +19780,11 @@ function attachTerminalSocket(options) {
|
|
|
19595
19780
|
"submit-failed",
|
|
19596
19781
|
serverText("imagesPerMessage", { max: MAX_IMAGES_PER_SUBMIT })
|
|
19597
19782
|
);
|
|
19783
|
+
settle(false);
|
|
19598
19784
|
break;
|
|
19599
19785
|
}
|
|
19600
19786
|
if (images.length > 0 && imageStyleFor(socket, terminalId, serverText("imagesUnsupported")) === null) {
|
|
19787
|
+
settle(false);
|
|
19601
19788
|
break;
|
|
19602
19789
|
}
|
|
19603
19790
|
const submitting = adapterOf(terminalId);
|
|
@@ -19618,6 +19805,7 @@ function attachTerminalSocket(options) {
|
|
|
19618
19805
|
});
|
|
19619
19806
|
if (refusal2 !== null) {
|
|
19620
19807
|
sendError(socket, "submit-failed", refusal2);
|
|
19808
|
+
settle(false);
|
|
19621
19809
|
break;
|
|
19622
19810
|
}
|
|
19623
19811
|
}
|
|
@@ -19685,6 +19873,7 @@ function attachTerminalSocket(options) {
|
|
|
19685
19873
|
approvalGuard,
|
|
19686
19874
|
{ startAfterMs }
|
|
19687
19875
|
);
|
|
19876
|
+
settle(outcome === "written");
|
|
19688
19877
|
if (outcome === "interrupted") interrupted();
|
|
19689
19878
|
if (outcome === "blocked") {
|
|
19690
19879
|
const waitingNow = blocksWhileWaiting && registry.activityOf(terminalId) === "waiting";
|
|
@@ -19696,7 +19885,7 @@ function attachTerminalSocket(options) {
|
|
|
19696
19885
|
}
|
|
19697
19886
|
},
|
|
19698
19887
|
{ onDropped: interrupted }
|
|
19699
|
-
);
|
|
19888
|
+
).then(() => settle(false));
|
|
19700
19889
|
break;
|
|
19701
19890
|
}
|
|
19702
19891
|
/*
|
|
@@ -20014,7 +20203,8 @@ function attachTerminalSocket(options) {
|
|
|
20014
20203
|
permissionMode: snapshot.permissionMode,
|
|
20015
20204
|
defaults: await defaultsFor(message.terminalId),
|
|
20016
20205
|
waitingFor: snapshot.waitingFor,
|
|
20017
|
-
openToolCall: conversations.isToolCallOpen(message.terminalId)
|
|
20206
|
+
openToolCall: conversations.isToolCallOpen(message.terminalId),
|
|
20207
|
+
subagents: conversations.getSubagents(message.terminalId)
|
|
20018
20208
|
});
|
|
20019
20209
|
if (snapshot.plans.length > 0) {
|
|
20020
20210
|
send(socket, {
|
|
@@ -20680,6 +20870,7 @@ function attachTerminalSocket(options) {
|
|
|
20680
20870
|
conversations.off("mode", onConversationMode);
|
|
20681
20871
|
conversations.off("waiting", onConversationWaiting);
|
|
20682
20872
|
conversations.off("toolCall", onConversationToolCall);
|
|
20873
|
+
conversations.off("subagents", onConversationSubagents);
|
|
20683
20874
|
repos.off("status", onGitStatus);
|
|
20684
20875
|
memory.off("status", onMemoryStatus);
|
|
20685
20876
|
stopAgentChanges();
|