weacpx 0.4.0-beta.1 → 0.4.0-beta.3

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.
@@ -56,6 +56,10 @@ function encodeBridgePromptSegmentEvent(event) {
56
56
  return `${JSON.stringify(event)}
57
57
  `;
58
58
  }
59
+ function encodeBridgePromptToolEvent(event) {
60
+ return `${JSON.stringify(event)}
61
+ `;
62
+ }
59
63
  function encodeBridgeSessionProgressEvent(event) {
60
64
  return `${JSON.stringify(event)}
61
65
  `;
@@ -343,8 +347,48 @@ var init_prompt_media = __esm(() => {
343
347
  };
344
348
  });
345
349
 
350
+ // src/transport/tool-event-mode.ts
351
+ function resolveToolEventMode(input) {
352
+ if (input?.toolEventMode !== undefined) {
353
+ return input.toolEventMode;
354
+ }
355
+ if (input?.onToolEvent !== undefined) {
356
+ return "structured";
357
+ }
358
+ return "text";
359
+ }
360
+
361
+ // src/transport/tool-kind-emoji.ts
362
+ var TOOL_KIND_EMOJI, DEFAULT_TOOL_EMOJI;
363
+ var init_tool_kind_emoji = __esm(() => {
364
+ TOOL_KIND_EMOJI = {
365
+ read: "\uD83D\uDCD6",
366
+ search: "\uD83D\uDD0D",
367
+ execute: "\uD83D\uDCBB",
368
+ edit: "✏️",
369
+ think: "\uD83E\uDDE0",
370
+ other: "\uD83D\uDD27"
371
+ };
372
+ DEFAULT_TOOL_EMOJI = TOOL_KIND_EMOJI.other;
373
+ });
374
+
346
375
  // src/transport/streaming-prompt.ts
347
- function createStreamingPromptState(formatToolCalls = false) {
376
+ function createStreamingPromptState(formatToolCalls = false, options) {
377
+ let toolEventMode;
378
+ let onToolEvent;
379
+ if (options === undefined) {
380
+ toolEventMode = "text";
381
+ onToolEvent = undefined;
382
+ } else if (typeof options === "function") {
383
+ onToolEvent = options;
384
+ toolEventMode = "structured";
385
+ } else {
386
+ onToolEvent = options.onToolEvent;
387
+ toolEventMode = resolveToolEventMode({
388
+ toolEventMode: options.mode,
389
+ onToolEvent
390
+ });
391
+ }
348
392
  return {
349
393
  buffer: "",
350
394
  segments: [],
@@ -352,6 +396,8 @@ function createStreamingPromptState(formatToolCalls = false) {
352
396
  pendingLine: "",
353
397
  formatToolCalls,
354
398
  emittedToolCallIds: new Set,
399
+ toolEventMode,
400
+ onToolEvent,
355
401
  finalize() {
356
402
  if (this.pendingLine.trim().length > 0) {
357
403
  parseStreamingChunks(this, this.pendingLine);
@@ -389,15 +435,24 @@ function parseStreamingChunks(state, line) {
389
435
  if (!update)
390
436
  return;
391
437
  if (state.formatToolCalls && (update.sessionUpdate === "tool_call" || update.sessionUpdate === "tool_call_update")) {
392
- const formatted = formatToolCallEvent(update, update.sessionUpdate);
393
- if (formatted) {
394
- const toolCallId = update.toolCallId;
395
- if (toolCallId) {
396
- if (state.emittedToolCallIds.has(toolCallId))
397
- return;
398
- state.emittedToolCallIds.add(toolCallId);
438
+ const wantsStructured = state.toolEventMode === "structured" || state.toolEventMode === "both";
439
+ const wantsText = state.toolEventMode === "text" || state.toolEventMode === "both";
440
+ if (wantsStructured && state.onToolEvent) {
441
+ const toolEvent = buildToolUseEvent(update);
442
+ if (toolEvent)
443
+ state.onToolEvent(toolEvent);
444
+ }
445
+ if (wantsText) {
446
+ const formatted = formatToolCallEvent(update, update.sessionUpdate);
447
+ if (formatted) {
448
+ const toolCallId = update.toolCallId;
449
+ if (toolCallId) {
450
+ if (state.emittedToolCallIds.has(toolCallId))
451
+ return;
452
+ state.emittedToolCallIds.add(toolCallId);
453
+ }
454
+ state.segments.push(formatted);
399
455
  }
400
- state.segments.push(formatted);
401
456
  }
402
457
  return;
403
458
  }
@@ -427,27 +482,71 @@ function formatToolCallEvent(update, sessionUpdate) {
427
482
  const title = update.title ?? "";
428
483
  if (title.length === 0)
429
484
  return null;
430
- const emoji = KIND_EMOJI[kind] ?? "\uD83D\uDD27";
431
- const command = getToolDisplayCommand(update);
432
- if (command) {
433
- return `${emoji} ${truncateToolDisplay(command)}`;
434
- }
435
- if (sessionUpdate === "tool_call_update" || isGenericToolTitle(kind, title))
485
+ const emoji = TOOL_KIND_EMOJI[kind] ?? DEFAULT_TOOL_EMOJI;
486
+ const inputSummary = summarizeToolInput(update.rawInput, title);
487
+ const status = readString(update, "status");
488
+ if (!inputSummary && status === "pending")
489
+ return null;
490
+ if (!inputSummary && isGenericToolTitle(kind, title))
436
491
  return null;
437
- return `${emoji} ${title}`;
492
+ const summaryText = inputSummary && inputSummary !== title ? `: ${truncateToolDisplay(inputSummary)}` : "";
493
+ const statusText = status ? ` (${status})` : "";
494
+ return `${emoji} ${title}${statusText}${summaryText}`;
438
495
  }
439
- function getToolDisplayCommand(update) {
496
+ function buildToolUseEvent(update) {
440
497
  if (!update)
441
498
  return null;
442
- const command = update.rawInput?.command;
443
- if (typeof command === "string" && command.length > 0) {
444
- return command;
499
+ const toolCallId = update.toolCallId;
500
+ if (!toolCallId)
501
+ return null;
502
+ const kindRaw = update.kind ?? "";
503
+ const kind = (() => {
504
+ switch (kindRaw) {
505
+ case "read":
506
+ case "search":
507
+ case "execute":
508
+ case "edit":
509
+ case "think":
510
+ return kindRaw;
511
+ default:
512
+ return "other";
513
+ }
514
+ })();
515
+ const title = (update.title ?? "").trim();
516
+ const toolName = title || "Tool";
517
+ const summaryRaw = summarizeToolInput(update.rawInput, title);
518
+ const summary = summaryRaw && summaryRaw !== title ? summaryRaw : undefined;
519
+ const statusRaw = readString(update, "status");
520
+ const status = statusRaw === "completed" || statusRaw === "success" ? "success" : statusRaw === "failed" || statusRaw === "error" ? "error" : "running";
521
+ return {
522
+ toolCallId,
523
+ toolName,
524
+ kind,
525
+ ...summary ? { summary } : {},
526
+ status
527
+ };
528
+ }
529
+ function summarizeToolInput(rawInput, title = "") {
530
+ if (rawInput == null)
531
+ return;
532
+ if (typeof rawInput === "string" || typeof rawInput === "number" || typeof rawInput === "boolean") {
533
+ return String(rawInput);
445
534
  }
446
- const parsedCmd = update.rawInput?.parsed_cmd;
447
- if (parsedCmd && parsedCmd.length > 0) {
535
+ if (!isRecord(rawInput))
536
+ return;
537
+ const taskSummary = summarizeTaskInput(rawInput, title);
538
+ if (taskSummary)
539
+ return taskSummary;
540
+ const command = readFirstString(rawInput, ["command", "cmd", "program"]);
541
+ const args = readFirstStringArray(rawInput, ["args", "arguments"]);
542
+ if (command) {
543
+ return [command, ...args ?? []].join(" ");
544
+ }
545
+ const parsedCmd = rawInput.parsed_cmd;
546
+ if (Array.isArray(parsedCmd) && parsedCmd.length > 0) {
448
547
  const parts = [];
449
548
  for (const entry of parsedCmd) {
450
- if (entry && typeof entry.cmd === "string" && entry.cmd.length > 0) {
549
+ if (isRecord(entry) && typeof entry.cmd === "string" && entry.cmd.length > 0) {
451
550
  parts.push(entry.cmd);
452
551
  }
453
552
  }
@@ -455,7 +554,62 @@ function getToolDisplayCommand(update) {
455
554
  return parts.join(" ");
456
555
  }
457
556
  }
458
- return null;
557
+ return readFirstString(rawInput, [
558
+ "path",
559
+ "file",
560
+ "filePath",
561
+ "filepath",
562
+ "file_path",
563
+ "target",
564
+ "uri",
565
+ "url",
566
+ "query",
567
+ "pattern",
568
+ "text",
569
+ "search",
570
+ "name",
571
+ "description"
572
+ ]);
573
+ }
574
+ function summarizeTaskInput(rawInput, title) {
575
+ const subagentType = readFirstString(rawInput, ["subagent_type", "subagentType", "agent", "agentType"]);
576
+ const description = readFirstString(rawInput, ["description", "task", "summary"]);
577
+ if (subagentType && description) {
578
+ return description === title ? subagentType : `${subagentType}: ${description}`;
579
+ }
580
+ if (subagentType)
581
+ return subagentType;
582
+ return;
583
+ }
584
+ function readFirstString(record, keys) {
585
+ for (const key of keys) {
586
+ const value = record[key];
587
+ if (typeof value === "string" && value.trim().length > 0) {
588
+ return value.trim();
589
+ }
590
+ }
591
+ return;
592
+ }
593
+ function readFirstStringArray(record, keys) {
594
+ for (const key of keys) {
595
+ const value = record[key];
596
+ if (!Array.isArray(value))
597
+ continue;
598
+ const entries = value.map((entry) => typeof entry === "string" && entry.trim().length > 0 ? entry.trim() : undefined).filter((entry) => entry !== undefined);
599
+ if (entries.length > 0) {
600
+ return entries;
601
+ }
602
+ }
603
+ return;
604
+ }
605
+ function isRecord(value) {
606
+ return typeof value === "object" && value !== null && !Array.isArray(value);
607
+ }
608
+ function readString(rawInput, key) {
609
+ if (!isRecord(rawInput))
610
+ return;
611
+ const value = rawInput[key];
612
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : undefined;
459
613
  }
460
614
  function truncateToolDisplay(text) {
461
615
  return text.length > 60 ? `${text.slice(0, 57)}...` : text;
@@ -473,14 +627,8 @@ function isGenericToolTitle(kind, title) {
473
627
  }
474
628
  return false;
475
629
  }
476
- var KIND_EMOJI;
477
630
  var init_streaming_prompt = __esm(() => {
478
- KIND_EMOJI = {
479
- read: "\uD83D\uDCD6",
480
- search: "\uD83D\uDD0D",
481
- execute: "\uD83D\uDCBB",
482
- edit: "✏️"
483
- };
631
+ init_tool_kind_emoji();
484
632
  });
485
633
 
486
634
  // src/recovery/discover-parent-package-paths.ts
@@ -1072,9 +1220,13 @@ class BridgeRuntime {
1072
1220
  input.name,
1073
1221
  ...structuredPrompt ? ["--file", structuredPrompt.filePath] : [input.text]
1074
1222
  ]));
1075
- const formatToolCalls = input.replyMode === "verbose";
1223
+ const formatToolCalls = (input.replyMode ?? "verbose") === "verbose";
1224
+ const toolEventMode = input.toolEventMode ?? (input.toolEvents === true ? "structured" : "text");
1076
1225
  try {
1077
- const result = onEvent ? await this.runPromptCommand(spawnSpec.command, spawnSpec.args, onEvent, { formatToolCalls }) : await this.run(spawnSpec.command, spawnSpec.args);
1226
+ const result = onEvent ? await this.runPromptCommand(spawnSpec.command, spawnSpec.args, onEvent, {
1227
+ formatToolCalls,
1228
+ toolEventMode
1229
+ }) : await this.run(spawnSpec.command, spawnSpec.args);
1078
1230
  return { text: getPromptText(result) };
1079
1231
  } finally {
1080
1232
  try {
@@ -1253,7 +1405,11 @@ async function runStreamingPrompt(command, args, onEvent, options = {}) {
1253
1405
  const child = spawnPrompt(command, args);
1254
1406
  let stdout = "";
1255
1407
  let stderr = "";
1256
- const state = createStreamingPromptState(options.formatToolCalls ?? false);
1408
+ const toolEventMode = options.toolEventMode ?? "text";
1409
+ const state = createStreamingPromptState(options.formatToolCalls ?? false, {
1410
+ mode: toolEventMode,
1411
+ ...onEvent && (toolEventMode === "structured" || toolEventMode === "both") ? { onToolEvent: (toolEvent) => onEvent({ type: "prompt.tool_event", event: toolEvent }) } : {}
1412
+ });
1257
1413
  let lastReplyAt = now();
1258
1414
  const flushBuffer = () => {
1259
1415
  const remaining = state.buffer.trim();
@@ -1475,6 +1631,7 @@ class BridgeServer {
1475
1631
  });
1476
1632
  case "prompt":
1477
1633
  const media = asOptionalPromptMediaInput(params.media);
1634
+ const resolvedToolEventMode = asOptionalToolEventMode(params.toolEventMode);
1478
1635
  return await this.runtime.prompt({
1479
1636
  agent: requireString(params, "agent"),
1480
1637
  agentCommand: asOptionalString(params.agentCommand),
@@ -1484,6 +1641,8 @@ class BridgeServer {
1484
1641
  mcpSourceHandle: asOptionalString(params.mcpSourceHandle),
1485
1642
  text: requirePromptText(params, media),
1486
1643
  replyMode: asOptionalReplyMode(params.replyMode),
1644
+ toolEvents: params.toolEvents === true,
1645
+ ...resolvedToolEventMode ? { toolEventMode: resolvedToolEventMode } : {},
1487
1646
  media
1488
1647
  }, (event) => {
1489
1648
  if (event.type === "prompt.segment") {
@@ -1492,6 +1651,12 @@ class BridgeServer {
1492
1651
  event: "prompt.segment",
1493
1652
  text: event.text
1494
1653
  }));
1654
+ } else if (event.type === "prompt.tool_event") {
1655
+ writeLine?.(encodeBridgePromptToolEvent({
1656
+ id: requestId,
1657
+ event: "prompt.tool_event",
1658
+ toolEvent: event.event
1659
+ }));
1495
1660
  }
1496
1661
  });
1497
1662
  case "setMode":
@@ -1657,6 +1822,13 @@ function asOptionalReplyMode(value) {
1657
1822
  }
1658
1823
  return value;
1659
1824
  }
1825
+ var VALID_TOOL_EVENT_MODES = new Set(["text", "structured", "both"]);
1826
+ function asOptionalToolEventMode(value) {
1827
+ if (typeof value !== "string" || !VALID_TOOL_EVENT_MODES.has(value)) {
1828
+ return;
1829
+ }
1830
+ return value;
1831
+ }
1660
1832
 
1661
1833
  // src/bridge/bridge-main.ts
1662
1834
  async function processBridgeInput(options) {
@@ -59,3 +59,17 @@ export interface MessageChannelRuntime {
59
59
  notifyTaskProgress(task: OrchestrationTaskRecord, text: string): Promise<void>;
60
60
  sendCoordinatorMessage(input: CoordinatorMessageInput): Promise<void>;
61
61
  }
62
+ export type ToolUseStatus = "running" | "success" | "error";
63
+ export type ToolUseKind = "read" | "search" | "execute" | "edit" | "think" | "other";
64
+ export interface ToolUseEvent {
65
+ toolCallId: string;
66
+ /** Free-form tool name from the agent (e.g. "Read File", "Bash"). */
67
+ toolName: string;
68
+ /** Coarse classifier produced by the transport from the agent's tool kind; channels use it to pick an icon. */
69
+ kind: ToolUseKind;
70
+ /** Best-effort one-line summary derived from `rawInput`. */
71
+ summary?: string;
72
+ status: ToolUseStatus;
73
+ /** Set when status transitions out of "running". */
74
+ durationMs?: number;
75
+ }