weacpx 0.4.0-beta.2 → 0.4.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.
@@ -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,16 +482,51 @@ 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 inputSummary = summarizeToolInput(update.rawInput);
485
+ const emoji = TOOL_KIND_EMOJI[kind] ?? DEFAULT_TOOL_EMOJI;
486
+ const inputSummary = summarizeToolInput(update.rawInput, title);
432
487
  const status = readString(update, "status");
488
+ if (!inputSummary && status === "pending")
489
+ return null;
433
490
  if (!inputSummary && isGenericToolTitle(kind, title))
434
491
  return null;
435
- const summaryText = inputSummary ? `: ${truncateToolDisplay(inputSummary)}` : "";
492
+ const summaryText = inputSummary && inputSummary !== title ? `: ${truncateToolDisplay(inputSummary)}` : "";
436
493
  const statusText = status ? ` (${status})` : "";
437
494
  return `${emoji} ${title}${statusText}${summaryText}`;
438
495
  }
439
- function summarizeToolInput(rawInput) {
496
+ function buildToolUseEvent(update) {
497
+ if (!update)
498
+ return null;
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 = "") {
440
530
  if (rawInput == null)
441
531
  return;
442
532
  if (typeof rawInput === "string" || typeof rawInput === "number" || typeof rawInput === "boolean") {
@@ -444,6 +534,9 @@ function summarizeToolInput(rawInput) {
444
534
  }
445
535
  if (!isRecord(rawInput))
446
536
  return;
537
+ const taskSummary = summarizeTaskInput(rawInput, title);
538
+ if (taskSummary)
539
+ return taskSummary;
447
540
  const command = readFirstString(rawInput, ["command", "cmd", "program"]);
448
541
  const args = readFirstStringArray(rawInput, ["args", "arguments"]);
449
542
  if (command) {
@@ -466,6 +559,7 @@ function summarizeToolInput(rawInput) {
466
559
  "file",
467
560
  "filePath",
468
561
  "filepath",
562
+ "file_path",
469
563
  "target",
470
564
  "uri",
471
565
  "url",
@@ -477,6 +571,16 @@ function summarizeToolInput(rawInput) {
477
571
  "description"
478
572
  ]);
479
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
+ }
480
584
  function readFirstString(record, keys) {
481
585
  for (const key of keys) {
482
586
  const value = record[key];
@@ -523,14 +627,8 @@ function isGenericToolTitle(kind, title) {
523
627
  }
524
628
  return false;
525
629
  }
526
- var KIND_EMOJI;
527
630
  var init_streaming_prompt = __esm(() => {
528
- KIND_EMOJI = {
529
- read: "\uD83D\uDCD6",
530
- search: "\uD83D\uDD0D",
531
- execute: "\uD83D\uDCBB",
532
- edit: "✏️"
533
- };
631
+ init_tool_kind_emoji();
534
632
  });
535
633
 
536
634
  // src/recovery/discover-parent-package-paths.ts
@@ -1123,8 +1221,12 @@ class BridgeRuntime {
1123
1221
  ...structuredPrompt ? ["--file", structuredPrompt.filePath] : [input.text]
1124
1222
  ]));
1125
1223
  const formatToolCalls = (input.replyMode ?? "verbose") === "verbose";
1224
+ const toolEventMode = input.toolEventMode ?? (input.toolEvents === true ? "structured" : "text");
1126
1225
  try {
1127
- 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);
1128
1230
  return { text: getPromptText(result) };
1129
1231
  } finally {
1130
1232
  try {
@@ -1303,7 +1405,11 @@ async function runStreamingPrompt(command, args, onEvent, options = {}) {
1303
1405
  const child = spawnPrompt(command, args);
1304
1406
  let stdout = "";
1305
1407
  let stderr = "";
1306
- 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
+ });
1307
1413
  let lastReplyAt = now();
1308
1414
  const flushBuffer = () => {
1309
1415
  const remaining = state.buffer.trim();
@@ -1525,6 +1631,7 @@ class BridgeServer {
1525
1631
  });
1526
1632
  case "prompt":
1527
1633
  const media = asOptionalPromptMediaInput(params.media);
1634
+ const resolvedToolEventMode = asOptionalToolEventMode(params.toolEventMode);
1528
1635
  return await this.runtime.prompt({
1529
1636
  agent: requireString(params, "agent"),
1530
1637
  agentCommand: asOptionalString(params.agentCommand),
@@ -1534,6 +1641,8 @@ class BridgeServer {
1534
1641
  mcpSourceHandle: asOptionalString(params.mcpSourceHandle),
1535
1642
  text: requirePromptText(params, media),
1536
1643
  replyMode: asOptionalReplyMode(params.replyMode),
1644
+ toolEvents: params.toolEvents === true,
1645
+ ...resolvedToolEventMode ? { toolEventMode: resolvedToolEventMode } : {},
1537
1646
  media
1538
1647
  }, (event) => {
1539
1648
  if (event.type === "prompt.segment") {
@@ -1542,6 +1651,12 @@ class BridgeServer {
1542
1651
  event: "prompt.segment",
1543
1652
  text: event.text
1544
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
+ }));
1545
1660
  }
1546
1661
  });
1547
1662
  case "setMode":
@@ -1707,6 +1822,13 @@ function asOptionalReplyMode(value) {
1707
1822
  }
1708
1823
  return value;
1709
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
+ }
1710
1832
 
1711
1833
  // src/bridge/bridge-main.ts
1712
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
+ }