@hopgoldy/agent-bridge 0.2.1 → 0.3.0

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
@@ -78,6 +78,8 @@ agent-bridge remove <channel-name>
78
78
 
79
79
  Config file: `~/.config/agent-bridge/config.json`
80
80
 
81
+ Command usage across IM adapters: [`docs/command-system.md`](./docs/command-system.md)
82
+
81
83
  ## Development
82
84
 
83
85
  ```bash
@@ -409,6 +409,8 @@ var GatewayCore = class {
409
409
  agentSessionId,
410
410
  clientSessionId,
411
411
  toolName: "toolName" in event ? event.toolName : void 0,
412
+ toolCallId: "toolCallId" in event ? event.toolCallId : void 0,
413
+ toolLabel: "toolLabel" in event ? event.toolLabel : void 0,
412
414
  text: event.text
413
415
  });
414
416
  }
@@ -427,7 +429,7 @@ var GatewayCore = class {
427
429
  });
428
430
  }
429
431
  #isToolRelatedEvent(event) {
430
- return event.type === "assistant.tool.running" || event.type === "assistant.tool.done" || event.type === "assistant.tool.error" || event.type === "session.compacting";
432
+ return event.type === "assistant.tool.running" || event.type === "assistant.tool.update" || event.type === "assistant.tool.done" || event.type === "assistant.tool.error" || event.type === "session.compacting";
431
433
  }
432
434
  #touchRuntime(runtime) {
433
435
  runtime.lastActiveAt = Date.now();
@@ -906,6 +908,8 @@ var PiCodingAgentAdapter = class {
906
908
  #onOutput = null;
907
909
  #inputQueue = [];
908
910
  #processing = false;
911
+ #toolLabelByCallId = /* @__PURE__ */ new Map();
912
+ #toolInputByCallId = /* @__PURE__ */ new Map();
909
913
  constructor({
910
914
  agentSessionId,
911
915
  cwd,
@@ -948,6 +952,8 @@ var PiCodingAgentAdapter = class {
948
952
  }
949
953
  async stop() {
950
954
  this.#inputQueue.length = 0;
955
+ this.#toolLabelByCallId.clear();
956
+ this.#toolInputByCallId.clear();
951
957
  await this.#client?.stop();
952
958
  this.#client = null;
953
959
  this.#processing = false;
@@ -1068,25 +1074,128 @@ var PiCodingAgentAdapter = class {
1068
1074
  }
1069
1075
  if (rpcEvent.type === "tool_execution_start") {
1070
1076
  const toolName = typeof rpcEvent.toolName === "string" ? rpcEvent.toolName : "unknown";
1077
+ const toolCallId = typeof rpcEvent.toolCallId === "string" ? rpcEvent.toolCallId : void 0;
1078
+ const toolInput = "args" in rpcEvent ? rpcEvent.args : void 0;
1079
+ const toolLabel = this.#summarizeToolLabel(toolName, toolInput);
1080
+ if (toolCallId) {
1081
+ if (toolLabel) this.#toolLabelByCallId.set(toolCallId, toolLabel);
1082
+ if (toolInput !== void 0) this.#toolInputByCallId.set(toolCallId, toolInput);
1083
+ }
1071
1084
  await this.#emitProgress({
1072
1085
  type: "assistant.tool.running",
1073
1086
  agentSessionId: this.#agentSessionId,
1074
1087
  toolName,
1088
+ toolCallId,
1089
+ toolInput,
1090
+ toolLabel,
1091
+ text: `Running ${toolName}`
1092
+ });
1093
+ return;
1094
+ }
1095
+ if (rpcEvent.type === "tool_execution_update") {
1096
+ const toolName = typeof rpcEvent.toolName === "string" ? rpcEvent.toolName : "unknown";
1097
+ const toolCallId = typeof rpcEvent.toolCallId === "string" ? rpcEvent.toolCallId : void 0;
1098
+ const toolInput = "args" in rpcEvent ? rpcEvent.args : this.#toolInputForCall(toolCallId);
1099
+ const toolLabel = this.#toolLabelForCall(toolCallId, toolName, toolInput);
1100
+ if (toolCallId) {
1101
+ if (toolLabel) this.#toolLabelByCallId.set(toolCallId, toolLabel);
1102
+ if (toolInput !== void 0) this.#toolInputByCallId.set(toolCallId, toolInput);
1103
+ }
1104
+ await this.#emitProgress({
1105
+ type: "assistant.tool.update",
1106
+ agentSessionId: this.#agentSessionId,
1107
+ toolName,
1108
+ toolCallId,
1109
+ toolInput,
1110
+ toolLabel,
1111
+ partialResult: "partialResult" in rpcEvent ? rpcEvent.partialResult : void 0,
1075
1112
  text: `Running ${toolName}`
1076
1113
  });
1077
1114
  return;
1078
1115
  }
1079
1116
  if (rpcEvent.type === "tool_execution_end") {
1080
1117
  const toolName = typeof rpcEvent.toolName === "string" ? rpcEvent.toolName : "unknown";
1118
+ const toolCallId = typeof rpcEvent.toolCallId === "string" ? rpcEvent.toolCallId : void 0;
1081
1119
  const isError = Boolean(rpcEvent.isError);
1120
+ const toolInput = this.#toolInputForCall(toolCallId);
1121
+ const toolLabel = this.#toolLabelForCall(toolCallId, toolName, toolInput);
1082
1122
  await this.#emitProgress({
1083
1123
  type: isError ? "assistant.tool.error" : "assistant.tool.done",
1084
1124
  agentSessionId: this.#agentSessionId,
1085
1125
  toolName,
1126
+ toolCallId,
1127
+ toolInput,
1128
+ toolLabel,
1129
+ result: "result" in rpcEvent ? rpcEvent.result : void 0,
1086
1130
  text: isError ? `Failed ${toolName}` : `Finished ${toolName}`
1087
1131
  });
1132
+ if (toolCallId) {
1133
+ this.#toolLabelByCallId.delete(toolCallId);
1134
+ this.#toolInputByCallId.delete(toolCallId);
1135
+ }
1136
+ }
1137
+ }
1138
+ #toolInputForCall(toolCallId) {
1139
+ return toolCallId ? this.#toolInputByCallId.get(toolCallId) : void 0;
1140
+ }
1141
+ #toolLabelForCall(toolCallId, toolName, toolInput) {
1142
+ return (toolCallId ? this.#toolLabelByCallId.get(toolCallId) : void 0) ?? this.#summarizeToolLabel(toolName, toolInput);
1143
+ }
1144
+ #summarizeToolLabel(toolName, toolInput) {
1145
+ if (!toolInput || typeof toolInput !== "object" || Array.isArray(toolInput)) {
1146
+ return void 0;
1147
+ }
1148
+ const input = toolInput;
1149
+ const stringField = (key) => {
1150
+ const value = input[key];
1151
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : void 0;
1152
+ };
1153
+ const stringArrayField = (key) => {
1154
+ const value = input[key];
1155
+ if (!Array.isArray(value)) return void 0;
1156
+ const items = value.filter((item) => typeof item === "string" && item.trim().length > 0);
1157
+ return items.length > 0 ? items.map((item) => item.trim()) : void 0;
1158
+ };
1159
+ switch (toolName) {
1160
+ case "bash":
1161
+ return stringField("command");
1162
+ case "read":
1163
+ case "write":
1164
+ case "edit":
1165
+ case "find":
1166
+ case "ls":
1167
+ return stringField("path");
1168
+ case "grep": {
1169
+ const pattern = stringField("pattern");
1170
+ const path7 = stringField("path");
1171
+ if (pattern && path7) return `${pattern} in ${path7}`;
1172
+ return pattern ?? path7;
1173
+ }
1174
+ case "web_search": {
1175
+ const query = stringField("query");
1176
+ const queries = stringArrayField("queries");
1177
+ return query ?? (queries ? queries.join(" | ") : void 0);
1178
+ }
1179
+ case "fetch_content": {
1180
+ const url = stringField("url");
1181
+ const urls = stringArrayField("urls");
1182
+ return url ?? (urls ? urls.join(" | ") : void 0);
1183
+ }
1184
+ default:
1185
+ return this.#truncate(this.#safeJson(toolInput), 120);
1088
1186
  }
1089
1187
  }
1188
+ #safeJson(value) {
1189
+ try {
1190
+ return JSON.stringify(value);
1191
+ } catch {
1192
+ return void 0;
1193
+ }
1194
+ }
1195
+ #truncate(value, maxLength) {
1196
+ if (!value) return void 0;
1197
+ return value.length > maxLength ? `${value.slice(0, maxLength - 1)}\u2026` : value;
1198
+ }
1090
1199
  #isAssistantMessage(value) {
1091
1200
  if (!value || typeof value !== "object") {
1092
1201
  return false;
@@ -1215,12 +1324,13 @@ function getTypedAgentModule(config) {
1215
1324
 
1216
1325
  // src/modules/client/utils/progress-renderer.ts
1217
1326
  var DEFAULT_COLLAPSE_THRESHOLD = 10;
1327
+ var MAX_TOOL_LABEL_DISPLAY_LENGTH = 15;
1218
1328
  var NO_PROGRESS_MARKDOWN = "No progress yet.";
1219
1329
  var ProgressRenderer = class {
1220
1330
  #collapseThreshold;
1221
- #lines = [];
1331
+ #entries = /* @__PURE__ */ new Map();
1332
+ #order = [];
1222
1333
  #status = "running";
1223
- #collapsedCount = 0;
1224
1334
  constructor(options = {}) {
1225
1335
  this.#collapseThreshold = options.collapseThreshold ?? DEFAULT_COLLAPSE_THRESHOLD;
1226
1336
  }
@@ -1230,43 +1340,97 @@ var ProgressRenderer = class {
1230
1340
  }
1231
1341
  /** Records a progress event, formatting it into a line and collapsing older lines if needed. */
1232
1342
  takeProgressEvent(event) {
1233
- this.#lines.push(this.#formatProgressLine(event));
1234
- if (this.#lines.length > this.#collapseThreshold) {
1235
- const overflow = this.#lines.length - this.#collapseThreshold;
1236
- this.#collapsedCount += overflow;
1237
- this.#lines.splice(0, overflow);
1343
+ const toolEntryId = this.#toolEntryId(event);
1344
+ if (toolEntryId) {
1345
+ this.#upsertToolEntry(toolEntryId, event);
1346
+ } else {
1347
+ this.#appendLineEntry(this.#formatProgressLine(event));
1238
1348
  }
1239
1349
  this.#status = this.#progressStatus(event);
1240
1350
  }
1241
1351
  /** Returns the current rendered markdown, status, and collapsed-line count. */
1242
1352
  getCurrentProgress() {
1353
+ const collapsedCount = Math.max(0, this.#order.length - this.#collapseThreshold);
1243
1354
  return {
1244
- markdown: this.#renderMarkdown(),
1355
+ markdown: this.#renderMarkdown(collapsedCount),
1245
1356
  status: this.#status,
1246
- collapsedCount: this.#collapsedCount
1357
+ collapsedCount
1247
1358
  };
1248
1359
  }
1249
- #renderMarkdown() {
1360
+ #renderMarkdown(collapsedCount) {
1250
1361
  const contentLines = [];
1251
- if (this.#collapsedCount > 0) {
1252
- contentLines.push(`- Collapsed ${this.#collapsedCount} earlier updates.`);
1362
+ if (collapsedCount > 0) {
1363
+ contentLines.push(`- Collapsed ${collapsedCount} earlier updates.`);
1253
1364
  }
1254
- if (this.#lines.length > 0) {
1255
- contentLines.push(...this.#lines);
1365
+ for (const id of this.#visibleOrder()) {
1366
+ const entry = this.#entries.get(id);
1367
+ if (!entry) continue;
1368
+ contentLines.push(entry.kind === "line" ? entry.line : this.#formatToolEntry(entry));
1256
1369
  }
1257
1370
  return contentLines.length > 0 ? contentLines.join("\n") : NO_PROGRESS_MARKDOWN;
1258
1371
  }
1372
+ #visibleOrder() {
1373
+ return this.#order.slice(-this.#collapseThreshold);
1374
+ }
1375
+ #appendLineEntry(line) {
1376
+ const id = `line:${this.#order.length}`;
1377
+ this.#entries.set(id, { kind: "line", line });
1378
+ this.#order.push(id);
1379
+ }
1380
+ #upsertToolEntry(id, event) {
1381
+ if (!this.#entries.has(id)) {
1382
+ this.#order.push(id);
1383
+ } else {
1384
+ this.#order = this.#order.filter((entryId) => entryId !== id);
1385
+ this.#order.push(id);
1386
+ }
1387
+ this.#entries.set(id, {
1388
+ kind: "tool",
1389
+ toolName: event.toolName,
1390
+ toolLabel: event.toolLabel,
1391
+ status: event.type === "assistant.tool.error" ? "error" : event.type === "assistant.tool.done" ? "done" : "running",
1392
+ text: event.text
1393
+ });
1394
+ }
1395
+ #toolEntryId(event) {
1396
+ if (event.type === "assistant.tool.running" || event.type === "assistant.tool.update" || event.type === "assistant.tool.done" || event.type === "assistant.tool.error") {
1397
+ return event.toolCallId ? `tool:${event.toolCallId}` : null;
1398
+ }
1399
+ return null;
1400
+ }
1401
+ #formatToolEntry(entry) {
1402
+ const subject = this.#formatToolSubject(entry.toolName, entry.toolLabel);
1403
+ switch (entry.status) {
1404
+ case "running":
1405
+ return `- Running ${subject}`;
1406
+ case "done":
1407
+ return `- Finished ${subject}`;
1408
+ case "error":
1409
+ return this.#formatToolErrorLine(subject, entry.text);
1410
+ }
1411
+ }
1259
1412
  #formatProgressLine(event) {
1260
1413
  switch (event.type) {
1261
1414
  case "session.compacting":
1262
1415
  return `- Compacting session${event.text ? `: ${event.text}` : ""}`;
1263
1416
  case "assistant.tool.running":
1264
- return `- Running ${event.toolName}`;
1417
+ case "assistant.tool.update":
1418
+ return `- Running ${this.#formatToolSubject(event.toolName, event.toolLabel)}`;
1265
1419
  case "assistant.tool.done":
1266
- return `- Finished ${event.toolName}`;
1420
+ return `- Finished ${this.#formatToolSubject(event.toolName, event.toolLabel)}`;
1267
1421
  case "assistant.tool.error":
1268
- return this.#formatToolErrorLine(event.toolName, event.text);
1422
+ return this.#formatToolErrorLine(this.#formatToolSubject(event.toolName, event.toolLabel), event.text);
1423
+ }
1424
+ }
1425
+ #formatToolSubject(toolName, toolLabel) {
1426
+ const normalizedLabel = toolLabel?.trim();
1427
+ if (!normalizedLabel) {
1428
+ return toolName;
1269
1429
  }
1430
+ return `${toolName}: ${this.#truncateToolLabel(normalizedLabel)}`;
1431
+ }
1432
+ #truncateToolLabel(toolLabel) {
1433
+ return toolLabel.length > MAX_TOOL_LABEL_DISPLAY_LENGTH ? `${toolLabel.slice(0, MAX_TOOL_LABEL_DISPLAY_LENGTH)}\u2026` : toolLabel;
1270
1434
  }
1271
1435
  #formatToolErrorLine(toolName, text2) {
1272
1436
  const normalizedText = text2?.trim();
@@ -1275,7 +1439,7 @@ var ProgressRenderer = class {
1275
1439
  }
1276
1440
  const lowerText = normalizedText.toLowerCase();
1277
1441
  const lowerToolName = toolName.toLowerCase();
1278
- if (lowerText === lowerToolName || lowerText === `failed ${lowerToolName}`) {
1442
+ if (lowerText === lowerToolName || lowerText === `failed ${lowerToolName}` || lowerText === `running ${lowerToolName}` || lowerText === `finished ${lowerToolName}`) {
1279
1443
  return `- ${this.#humanizeToolError(toolName)}`;
1280
1444
  }
1281
1445
  return `- ${this.#humanizeToolError(toolName)}: ${normalizedText}`;
@@ -1297,10 +1461,12 @@ var ProgressRenderer = class {
1297
1461
 
1298
1462
  // src/modules/client/utils/slash-commands.ts
1299
1463
  function parseSlashCommand(text2, clientSessionId) {
1300
- switch (text2) {
1464
+ switch (text2.toLowerCase()) {
1301
1465
  case "/new":
1466
+ case "/n":
1302
1467
  return { type: "command.session.new", clientSessionId };
1303
1468
  case "/compact":
1469
+ case "/c":
1304
1470
  return { type: "command.session.compact", clientSessionId };
1305
1471
  case "/stop":
1306
1472
  return { type: "command.session.stop", clientSessionId };
package/dist/cli.js CHANGED
@@ -409,6 +409,8 @@ var GatewayCore = class {
409
409
  agentSessionId,
410
410
  clientSessionId,
411
411
  toolName: "toolName" in event ? event.toolName : void 0,
412
+ toolCallId: "toolCallId" in event ? event.toolCallId : void 0,
413
+ toolLabel: "toolLabel" in event ? event.toolLabel : void 0,
412
414
  text: event.text
413
415
  });
414
416
  }
@@ -427,7 +429,7 @@ var GatewayCore = class {
427
429
  });
428
430
  }
429
431
  #isToolRelatedEvent(event) {
430
- return event.type === "assistant.tool.running" || event.type === "assistant.tool.done" || event.type === "assistant.tool.error" || event.type === "session.compacting";
432
+ return event.type === "assistant.tool.running" || event.type === "assistant.tool.update" || event.type === "assistant.tool.done" || event.type === "assistant.tool.error" || event.type === "session.compacting";
431
433
  }
432
434
  #touchRuntime(runtime) {
433
435
  runtime.lastActiveAt = Date.now();
@@ -906,6 +908,8 @@ var PiCodingAgentAdapter = class {
906
908
  #onOutput = null;
907
909
  #inputQueue = [];
908
910
  #processing = false;
911
+ #toolLabelByCallId = /* @__PURE__ */ new Map();
912
+ #toolInputByCallId = /* @__PURE__ */ new Map();
909
913
  constructor({
910
914
  agentSessionId,
911
915
  cwd,
@@ -948,6 +952,8 @@ var PiCodingAgentAdapter = class {
948
952
  }
949
953
  async stop() {
950
954
  this.#inputQueue.length = 0;
955
+ this.#toolLabelByCallId.clear();
956
+ this.#toolInputByCallId.clear();
951
957
  await this.#client?.stop();
952
958
  this.#client = null;
953
959
  this.#processing = false;
@@ -1068,25 +1074,128 @@ var PiCodingAgentAdapter = class {
1068
1074
  }
1069
1075
  if (rpcEvent.type === "tool_execution_start") {
1070
1076
  const toolName = typeof rpcEvent.toolName === "string" ? rpcEvent.toolName : "unknown";
1077
+ const toolCallId = typeof rpcEvent.toolCallId === "string" ? rpcEvent.toolCallId : void 0;
1078
+ const toolInput = "args" in rpcEvent ? rpcEvent.args : void 0;
1079
+ const toolLabel = this.#summarizeToolLabel(toolName, toolInput);
1080
+ if (toolCallId) {
1081
+ if (toolLabel) this.#toolLabelByCallId.set(toolCallId, toolLabel);
1082
+ if (toolInput !== void 0) this.#toolInputByCallId.set(toolCallId, toolInput);
1083
+ }
1071
1084
  await this.#emitProgress({
1072
1085
  type: "assistant.tool.running",
1073
1086
  agentSessionId: this.#agentSessionId,
1074
1087
  toolName,
1088
+ toolCallId,
1089
+ toolInput,
1090
+ toolLabel,
1091
+ text: `Running ${toolName}`
1092
+ });
1093
+ return;
1094
+ }
1095
+ if (rpcEvent.type === "tool_execution_update") {
1096
+ const toolName = typeof rpcEvent.toolName === "string" ? rpcEvent.toolName : "unknown";
1097
+ const toolCallId = typeof rpcEvent.toolCallId === "string" ? rpcEvent.toolCallId : void 0;
1098
+ const toolInput = "args" in rpcEvent ? rpcEvent.args : this.#toolInputForCall(toolCallId);
1099
+ const toolLabel = this.#toolLabelForCall(toolCallId, toolName, toolInput);
1100
+ if (toolCallId) {
1101
+ if (toolLabel) this.#toolLabelByCallId.set(toolCallId, toolLabel);
1102
+ if (toolInput !== void 0) this.#toolInputByCallId.set(toolCallId, toolInput);
1103
+ }
1104
+ await this.#emitProgress({
1105
+ type: "assistant.tool.update",
1106
+ agentSessionId: this.#agentSessionId,
1107
+ toolName,
1108
+ toolCallId,
1109
+ toolInput,
1110
+ toolLabel,
1111
+ partialResult: "partialResult" in rpcEvent ? rpcEvent.partialResult : void 0,
1075
1112
  text: `Running ${toolName}`
1076
1113
  });
1077
1114
  return;
1078
1115
  }
1079
1116
  if (rpcEvent.type === "tool_execution_end") {
1080
1117
  const toolName = typeof rpcEvent.toolName === "string" ? rpcEvent.toolName : "unknown";
1118
+ const toolCallId = typeof rpcEvent.toolCallId === "string" ? rpcEvent.toolCallId : void 0;
1081
1119
  const isError = Boolean(rpcEvent.isError);
1120
+ const toolInput = this.#toolInputForCall(toolCallId);
1121
+ const toolLabel = this.#toolLabelForCall(toolCallId, toolName, toolInput);
1082
1122
  await this.#emitProgress({
1083
1123
  type: isError ? "assistant.tool.error" : "assistant.tool.done",
1084
1124
  agentSessionId: this.#agentSessionId,
1085
1125
  toolName,
1126
+ toolCallId,
1127
+ toolInput,
1128
+ toolLabel,
1129
+ result: "result" in rpcEvent ? rpcEvent.result : void 0,
1086
1130
  text: isError ? `Failed ${toolName}` : `Finished ${toolName}`
1087
1131
  });
1132
+ if (toolCallId) {
1133
+ this.#toolLabelByCallId.delete(toolCallId);
1134
+ this.#toolInputByCallId.delete(toolCallId);
1135
+ }
1136
+ }
1137
+ }
1138
+ #toolInputForCall(toolCallId) {
1139
+ return toolCallId ? this.#toolInputByCallId.get(toolCallId) : void 0;
1140
+ }
1141
+ #toolLabelForCall(toolCallId, toolName, toolInput) {
1142
+ return (toolCallId ? this.#toolLabelByCallId.get(toolCallId) : void 0) ?? this.#summarizeToolLabel(toolName, toolInput);
1143
+ }
1144
+ #summarizeToolLabel(toolName, toolInput) {
1145
+ if (!toolInput || typeof toolInput !== "object" || Array.isArray(toolInput)) {
1146
+ return void 0;
1147
+ }
1148
+ const input = toolInput;
1149
+ const stringField = (key) => {
1150
+ const value = input[key];
1151
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : void 0;
1152
+ };
1153
+ const stringArrayField = (key) => {
1154
+ const value = input[key];
1155
+ if (!Array.isArray(value)) return void 0;
1156
+ const items = value.filter((item) => typeof item === "string" && item.trim().length > 0);
1157
+ return items.length > 0 ? items.map((item) => item.trim()) : void 0;
1158
+ };
1159
+ switch (toolName) {
1160
+ case "bash":
1161
+ return stringField("command");
1162
+ case "read":
1163
+ case "write":
1164
+ case "edit":
1165
+ case "find":
1166
+ case "ls":
1167
+ return stringField("path");
1168
+ case "grep": {
1169
+ const pattern = stringField("pattern");
1170
+ const path7 = stringField("path");
1171
+ if (pattern && path7) return `${pattern} in ${path7}`;
1172
+ return pattern ?? path7;
1173
+ }
1174
+ case "web_search": {
1175
+ const query = stringField("query");
1176
+ const queries = stringArrayField("queries");
1177
+ return query ?? (queries ? queries.join(" | ") : void 0);
1178
+ }
1179
+ case "fetch_content": {
1180
+ const url = stringField("url");
1181
+ const urls = stringArrayField("urls");
1182
+ return url ?? (urls ? urls.join(" | ") : void 0);
1183
+ }
1184
+ default:
1185
+ return this.#truncate(this.#safeJson(toolInput), 120);
1088
1186
  }
1089
1187
  }
1188
+ #safeJson(value) {
1189
+ try {
1190
+ return JSON.stringify(value);
1191
+ } catch {
1192
+ return void 0;
1193
+ }
1194
+ }
1195
+ #truncate(value, maxLength) {
1196
+ if (!value) return void 0;
1197
+ return value.length > maxLength ? `${value.slice(0, maxLength - 1)}\u2026` : value;
1198
+ }
1090
1199
  #isAssistantMessage(value) {
1091
1200
  if (!value || typeof value !== "object") {
1092
1201
  return false;
@@ -1215,12 +1324,13 @@ function getTypedAgentModule(config) {
1215
1324
 
1216
1325
  // src/modules/client/utils/progress-renderer.ts
1217
1326
  var DEFAULT_COLLAPSE_THRESHOLD = 10;
1327
+ var MAX_TOOL_LABEL_DISPLAY_LENGTH = 15;
1218
1328
  var NO_PROGRESS_MARKDOWN = "No progress yet.";
1219
1329
  var ProgressRenderer = class {
1220
1330
  #collapseThreshold;
1221
- #lines = [];
1331
+ #entries = /* @__PURE__ */ new Map();
1332
+ #order = [];
1222
1333
  #status = "running";
1223
- #collapsedCount = 0;
1224
1334
  constructor(options = {}) {
1225
1335
  this.#collapseThreshold = options.collapseThreshold ?? DEFAULT_COLLAPSE_THRESHOLD;
1226
1336
  }
@@ -1230,43 +1340,97 @@ var ProgressRenderer = class {
1230
1340
  }
1231
1341
  /** Records a progress event, formatting it into a line and collapsing older lines if needed. */
1232
1342
  takeProgressEvent(event) {
1233
- this.#lines.push(this.#formatProgressLine(event));
1234
- if (this.#lines.length > this.#collapseThreshold) {
1235
- const overflow = this.#lines.length - this.#collapseThreshold;
1236
- this.#collapsedCount += overflow;
1237
- this.#lines.splice(0, overflow);
1343
+ const toolEntryId = this.#toolEntryId(event);
1344
+ if (toolEntryId) {
1345
+ this.#upsertToolEntry(toolEntryId, event);
1346
+ } else {
1347
+ this.#appendLineEntry(this.#formatProgressLine(event));
1238
1348
  }
1239
1349
  this.#status = this.#progressStatus(event);
1240
1350
  }
1241
1351
  /** Returns the current rendered markdown, status, and collapsed-line count. */
1242
1352
  getCurrentProgress() {
1353
+ const collapsedCount = Math.max(0, this.#order.length - this.#collapseThreshold);
1243
1354
  return {
1244
- markdown: this.#renderMarkdown(),
1355
+ markdown: this.#renderMarkdown(collapsedCount),
1245
1356
  status: this.#status,
1246
- collapsedCount: this.#collapsedCount
1357
+ collapsedCount
1247
1358
  };
1248
1359
  }
1249
- #renderMarkdown() {
1360
+ #renderMarkdown(collapsedCount) {
1250
1361
  const contentLines = [];
1251
- if (this.#collapsedCount > 0) {
1252
- contentLines.push(`- Collapsed ${this.#collapsedCount} earlier updates.`);
1362
+ if (collapsedCount > 0) {
1363
+ contentLines.push(`- Collapsed ${collapsedCount} earlier updates.`);
1253
1364
  }
1254
- if (this.#lines.length > 0) {
1255
- contentLines.push(...this.#lines);
1365
+ for (const id of this.#visibleOrder()) {
1366
+ const entry = this.#entries.get(id);
1367
+ if (!entry) continue;
1368
+ contentLines.push(entry.kind === "line" ? entry.line : this.#formatToolEntry(entry));
1256
1369
  }
1257
1370
  return contentLines.length > 0 ? contentLines.join("\n") : NO_PROGRESS_MARKDOWN;
1258
1371
  }
1372
+ #visibleOrder() {
1373
+ return this.#order.slice(-this.#collapseThreshold);
1374
+ }
1375
+ #appendLineEntry(line) {
1376
+ const id = `line:${this.#order.length}`;
1377
+ this.#entries.set(id, { kind: "line", line });
1378
+ this.#order.push(id);
1379
+ }
1380
+ #upsertToolEntry(id, event) {
1381
+ if (!this.#entries.has(id)) {
1382
+ this.#order.push(id);
1383
+ } else {
1384
+ this.#order = this.#order.filter((entryId) => entryId !== id);
1385
+ this.#order.push(id);
1386
+ }
1387
+ this.#entries.set(id, {
1388
+ kind: "tool",
1389
+ toolName: event.toolName,
1390
+ toolLabel: event.toolLabel,
1391
+ status: event.type === "assistant.tool.error" ? "error" : event.type === "assistant.tool.done" ? "done" : "running",
1392
+ text: event.text
1393
+ });
1394
+ }
1395
+ #toolEntryId(event) {
1396
+ if (event.type === "assistant.tool.running" || event.type === "assistant.tool.update" || event.type === "assistant.tool.done" || event.type === "assistant.tool.error") {
1397
+ return event.toolCallId ? `tool:${event.toolCallId}` : null;
1398
+ }
1399
+ return null;
1400
+ }
1401
+ #formatToolEntry(entry) {
1402
+ const subject = this.#formatToolSubject(entry.toolName, entry.toolLabel);
1403
+ switch (entry.status) {
1404
+ case "running":
1405
+ return `- Running ${subject}`;
1406
+ case "done":
1407
+ return `- Finished ${subject}`;
1408
+ case "error":
1409
+ return this.#formatToolErrorLine(subject, entry.text);
1410
+ }
1411
+ }
1259
1412
  #formatProgressLine(event) {
1260
1413
  switch (event.type) {
1261
1414
  case "session.compacting":
1262
1415
  return `- Compacting session${event.text ? `: ${event.text}` : ""}`;
1263
1416
  case "assistant.tool.running":
1264
- return `- Running ${event.toolName}`;
1417
+ case "assistant.tool.update":
1418
+ return `- Running ${this.#formatToolSubject(event.toolName, event.toolLabel)}`;
1265
1419
  case "assistant.tool.done":
1266
- return `- Finished ${event.toolName}`;
1420
+ return `- Finished ${this.#formatToolSubject(event.toolName, event.toolLabel)}`;
1267
1421
  case "assistant.tool.error":
1268
- return this.#formatToolErrorLine(event.toolName, event.text);
1422
+ return this.#formatToolErrorLine(this.#formatToolSubject(event.toolName, event.toolLabel), event.text);
1423
+ }
1424
+ }
1425
+ #formatToolSubject(toolName, toolLabel) {
1426
+ const normalizedLabel = toolLabel?.trim();
1427
+ if (!normalizedLabel) {
1428
+ return toolName;
1269
1429
  }
1430
+ return `${toolName}: ${this.#truncateToolLabel(normalizedLabel)}`;
1431
+ }
1432
+ #truncateToolLabel(toolLabel) {
1433
+ return toolLabel.length > MAX_TOOL_LABEL_DISPLAY_LENGTH ? `${toolLabel.slice(0, MAX_TOOL_LABEL_DISPLAY_LENGTH)}\u2026` : toolLabel;
1270
1434
  }
1271
1435
  #formatToolErrorLine(toolName, text2) {
1272
1436
  const normalizedText = text2?.trim();
@@ -1275,7 +1439,7 @@ var ProgressRenderer = class {
1275
1439
  }
1276
1440
  const lowerText = normalizedText.toLowerCase();
1277
1441
  const lowerToolName = toolName.toLowerCase();
1278
- if (lowerText === lowerToolName || lowerText === `failed ${lowerToolName}`) {
1442
+ if (lowerText === lowerToolName || lowerText === `failed ${lowerToolName}` || lowerText === `running ${lowerToolName}` || lowerText === `finished ${lowerToolName}`) {
1279
1443
  return `- ${this.#humanizeToolError(toolName)}`;
1280
1444
  }
1281
1445
  return `- ${this.#humanizeToolError(toolName)}: ${normalizedText}`;
@@ -1297,10 +1461,12 @@ var ProgressRenderer = class {
1297
1461
 
1298
1462
  // src/modules/client/utils/slash-commands.ts
1299
1463
  function parseSlashCommand(text2, clientSessionId) {
1300
- switch (text2) {
1464
+ switch (text2.toLowerCase()) {
1301
1465
  case "/new":
1466
+ case "/n":
1302
1467
  return { type: "command.session.new", clientSessionId };
1303
1468
  case "/compact":
1469
+ case "/c":
1304
1470
  return { type: "command.session.compact", clientSessionId };
1305
1471
  case "/stop":
1306
1472
  return { type: "command.session.stop", clientSessionId };
package/dist/index.d.ts CHANGED
@@ -24,6 +24,13 @@ interface OutboundAttachment {
24
24
  fileName?: string;
25
25
  caption?: string;
26
26
  }
27
+ type ToolProgressPayload = {
28
+ toolName: string;
29
+ toolCallId?: string;
30
+ toolInput?: unknown;
31
+ toolLabel?: string;
32
+ text?: string;
33
+ };
27
34
  type AgentOutputPayload = {
28
35
  type: "assistant.message";
29
36
  text: string;
@@ -31,19 +38,18 @@ type AgentOutputPayload = {
31
38
  } | {
32
39
  type: "assistant.thinking";
33
40
  text?: string;
34
- } | {
41
+ } | ({
35
42
  type: "assistant.tool.running";
36
- toolName: string;
37
- text?: string;
38
- } | {
43
+ } & ToolProgressPayload) | ({
44
+ type: "assistant.tool.update";
45
+ partialResult?: unknown;
46
+ } & ToolProgressPayload) | ({
39
47
  type: "assistant.tool.done";
40
- toolName: string;
41
- text?: string;
42
- } | {
48
+ result?: unknown;
49
+ } & ToolProgressPayload) | ({
43
50
  type: "assistant.tool.error";
44
- toolName: string;
45
- text?: string;
46
- } | {
51
+ result?: unknown;
52
+ } & ToolProgressPayload) | {
47
53
  type: "session.compacting";
48
54
  text?: string;
49
55
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hopgoldy/agent-bridge",
3
- "version": "0.2.1",
3
+ "version": "0.3.0",
4
4
  "private": false,
5
5
  "description": "IM to Agent bridge with dual-adapter architecture",
6
6
  "packageManager": "pnpm@10.33.0",