weacpx 0.4.0-beta.2 → 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.
- package/dist/bridge/bridge-main.js +144 -22
- package/dist/channels/types.d.ts +14 -0
- package/dist/cli.js +305 -54
- package/dist/plugin-api.d.ts +1 -1
- package/dist/weixin/agent/interface.d.ts +3 -0
- package/package.json +1 -1
|
@@ -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
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
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 =
|
|
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
|
|
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
|
-
|
|
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, {
|
|
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
|
|
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) {
|
package/dist/channels/types.d.ts
CHANGED
|
@@ -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
|
+
}
|
package/dist/cli.js
CHANGED
|
@@ -14153,7 +14153,7 @@ import { dirname as dirname6, join as join3 } from "node:path";
|
|
|
14153
14153
|
import { homedir as homedir3 } from "node:os";
|
|
14154
14154
|
function createWeixinConsumerLock(options = {}) {
|
|
14155
14155
|
const lockFilePath = options.lockFilePath ?? join3(homedir3(), ".weacpx", "runtime", "weixin-consumer.lock.json");
|
|
14156
|
-
const isProcessRunning = options.isProcessRunning ??
|
|
14156
|
+
const isProcessRunning = options.isProcessRunning ?? defaultIsProcessRunning4;
|
|
14157
14157
|
const onDiagnostic = options.onDiagnostic;
|
|
14158
14158
|
return {
|
|
14159
14159
|
async acquire(meta2) {
|
|
@@ -14240,7 +14240,7 @@ async function loadLockMetadata(path13) {
|
|
|
14240
14240
|
return null;
|
|
14241
14241
|
}
|
|
14242
14242
|
}
|
|
14243
|
-
function
|
|
14243
|
+
function defaultIsProcessRunning4(pid) {
|
|
14244
14244
|
try {
|
|
14245
14245
|
process.kill(pid, 0);
|
|
14246
14246
|
return true;
|
|
@@ -16569,7 +16569,7 @@ async function handleSessionRemove(context, chatKey, alias) {
|
|
|
16569
16569
|
return { text: lines.join(`
|
|
16570
16570
|
`) };
|
|
16571
16571
|
}
|
|
16572
|
-
async function promptWithSession(context, session, chatKey, text, reply, replyContextToken, accountId, media, abortSignal) {
|
|
16572
|
+
async function promptWithSession(context, session, chatKey, text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent) {
|
|
16573
16573
|
const effectiveReplyMode = session.replyMode ?? context.config?.channel.replyMode ?? "verbose";
|
|
16574
16574
|
if (!session.replyMode)
|
|
16575
16575
|
session.replyMode = effectiveReplyMode;
|
|
@@ -16594,7 +16594,7 @@ async function promptWithSession(context, session, chatKey, text, reply, replyCo
|
|
|
16594
16594
|
const { promptText, taskIds, groupIds, claimHumanReply } = await preparePromptWithFallback(context, session, chatKey, text, replyContextToken, accountId);
|
|
16595
16595
|
try {
|
|
16596
16596
|
const replyContext = transportReply && context.quota && getChannelIdFromChatKey(chatKey) === "weixin" ? { chatKey, quota: context.quota } : undefined;
|
|
16597
|
-
const result = await context.interaction.promptTransportSession(session, promptText, transportReply, replyContext, media, abortSignal);
|
|
16597
|
+
const result = await context.interaction.promptTransportSession(session, promptText, transportReply, replyContext, media, abortSignal, onToolEvent);
|
|
16598
16598
|
if (claimHumanReply) {
|
|
16599
16599
|
try {
|
|
16600
16600
|
await context.orchestration?.claimActiveHumanReply?.(claimHumanReply);
|
|
@@ -16614,17 +16614,17 @@ async function promptWithSession(context, session, chatKey, text, reply, replyCo
|
|
|
16614
16614
|
throw error2;
|
|
16615
16615
|
}
|
|
16616
16616
|
}
|
|
16617
|
-
async function handlePrompt(context, chatKey, text, reply, replyContextToken, accountId, media, abortSignal) {
|
|
16617
|
+
async function handlePrompt(context, chatKey, text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent) {
|
|
16618
16618
|
const session = await context.sessions.getCurrentSession(chatKey);
|
|
16619
16619
|
if (!session) {
|
|
16620
16620
|
return { text: NO_CURRENT_SESSION_TEXT };
|
|
16621
16621
|
}
|
|
16622
16622
|
try {
|
|
16623
|
-
return await promptWithSession(context, session, chatKey, text, reply, replyContextToken, accountId, media, abortSignal);
|
|
16623
|
+
return await promptWithSession(context, session, chatKey, text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent);
|
|
16624
16624
|
} catch (error2) {
|
|
16625
16625
|
const recovered = await context.recovery.tryRecoverMissingSession(session, error2);
|
|
16626
16626
|
if (recovered) {
|
|
16627
|
-
return await promptWithSession(context, recovered, chatKey, text, reply, replyContextToken, accountId, media, abortSignal);
|
|
16627
|
+
return await promptWithSession(context, recovered, chatKey, text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent);
|
|
16628
16628
|
}
|
|
16629
16629
|
return context.recovery.renderTransportError(session, error2);
|
|
16630
16630
|
}
|
|
@@ -18150,7 +18150,7 @@ class CommandRouter {
|
|
|
18150
18150
|
this.quota = quota;
|
|
18151
18151
|
this.logger = logger2 ?? createNoopAppLogger();
|
|
18152
18152
|
}
|
|
18153
|
-
async handle(chatKey, input, reply, replyContextToken, accountId, media, metadata, abortSignal) {
|
|
18153
|
+
async handle(chatKey, input, reply, replyContextToken, accountId, media, metadata, abortSignal, onToolEvent) {
|
|
18154
18154
|
const startedAt = Date.now();
|
|
18155
18155
|
const command = parseCommand(input);
|
|
18156
18156
|
await this.logger.debug("command.parsed", "parsed inbound command", {
|
|
@@ -18264,7 +18264,7 @@ class CommandRouter {
|
|
|
18264
18264
|
case "task.cancel":
|
|
18265
18265
|
return await handleTaskCancel(this.createHandlerContext(), chatKey, command.taskId);
|
|
18266
18266
|
case "prompt":
|
|
18267
|
-
return await handlePrompt(this.createSessionHandlerContext(), chatKey, command.text, reply, replyContextToken, accountId, media, abortSignal);
|
|
18267
|
+
return await handlePrompt(this.createSessionHandlerContext(), chatKey, command.text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent);
|
|
18268
18268
|
}
|
|
18269
18269
|
});
|
|
18270
18270
|
}
|
|
@@ -18316,7 +18316,7 @@ class CommandRouter {
|
|
|
18316
18316
|
return {
|
|
18317
18317
|
setModeTransportSession: (session, modeId) => this.setModeTransportSession(session, modeId),
|
|
18318
18318
|
cancelTransportSession: (session) => this.cancelTransportSession(session),
|
|
18319
|
-
promptTransportSession: (session, text, reply, replyContext, media, abortSignal) => this.promptTransportSession(session, text, reply, replyContext, media, abortSignal)
|
|
18319
|
+
promptTransportSession: (session, text, reply, replyContext, media, abortSignal, onToolEvent) => this.promptTransportSession(session, text, reply, replyContext, media, abortSignal, onToolEvent)
|
|
18320
18320
|
};
|
|
18321
18321
|
}
|
|
18322
18322
|
createSessionRenderRecoveryOps() {
|
|
@@ -18479,7 +18479,7 @@ class CommandRouter {
|
|
|
18479
18479
|
async checkTransportSession(session) {
|
|
18480
18480
|
return await this.measureTransportCall("has_session", session, () => this.transport.hasSession(session));
|
|
18481
18481
|
}
|
|
18482
|
-
async promptTransportSession(session, text, reply, replyContext, media, abortSignal) {
|
|
18482
|
+
async promptTransportSession(session, text, reply, replyContext, media, abortSignal, onToolEvent) {
|
|
18483
18483
|
session.mcpCoordinatorSession ??= session.transportSession;
|
|
18484
18484
|
let done = false;
|
|
18485
18485
|
let cancelOnAbort;
|
|
@@ -18516,7 +18516,10 @@ class CommandRouter {
|
|
|
18516
18516
|
abortSignal.addEventListener("abort", cancelOnAbort, { once: true });
|
|
18517
18517
|
}
|
|
18518
18518
|
try {
|
|
18519
|
-
return await this.measureTransportCall("prompt", session, () => this.transport.prompt(session, text, reply, replyContext,
|
|
18519
|
+
return await this.measureTransportCall("prompt", session, () => this.transport.prompt(session, text, reply, replyContext, {
|
|
18520
|
+
...media ? { media } : {},
|
|
18521
|
+
...onToolEvent ? { onToolEvent } : {}
|
|
18522
|
+
}));
|
|
18520
18523
|
} finally {
|
|
18521
18524
|
done = true;
|
|
18522
18525
|
if (cancelOnAbort && abortSignal) {
|
|
@@ -18667,7 +18670,7 @@ class ConsoleAgent {
|
|
|
18667
18670
|
mimeType: m.mimeType,
|
|
18668
18671
|
...m.fileName ? { fileName: m.fileName } : {}
|
|
18669
18672
|
})) : undefined;
|
|
18670
|
-
return await this.router.handle(request.conversationId, request.text, request.reply, request.replyContextToken, request.accountId, promptMedia, request.metadata, request.abortSignal);
|
|
18673
|
+
return await this.router.handle(request.conversationId, request.text, request.reply, request.replyContextToken, request.accountId, promptMedia, request.metadata, request.abortSignal, request.onToolEvent);
|
|
18671
18674
|
}
|
|
18672
18675
|
isKnownCommand(text) {
|
|
18673
18676
|
return isKnownWeacpxCommandText(text);
|
|
@@ -22767,6 +22770,10 @@ function encodeBridgePromptSegmentEvent(event) {
|
|
|
22767
22770
|
return `${JSON.stringify(event)}
|
|
22768
22771
|
`;
|
|
22769
22772
|
}
|
|
22773
|
+
function encodeBridgePromptToolEvent(event) {
|
|
22774
|
+
return `${JSON.stringify(event)}
|
|
22775
|
+
`;
|
|
22776
|
+
}
|
|
22770
22777
|
function encodeBridgeSessionProgressEvent(event) {
|
|
22771
22778
|
return `${JSON.stringify(event)}
|
|
22772
22779
|
`;
|
|
@@ -22834,6 +22841,11 @@ class AcpxBridgeClient {
|
|
|
22834
22841
|
type: "prompt.segment",
|
|
22835
22842
|
text: message.text
|
|
22836
22843
|
});
|
|
22844
|
+
} else if (message.event === "prompt.tool_event") {
|
|
22845
|
+
pending.onEvent?.({
|
|
22846
|
+
type: "prompt.tool_event",
|
|
22847
|
+
event: message.toolEvent
|
|
22848
|
+
});
|
|
22837
22849
|
} else if (message.event === "session.progress") {
|
|
22838
22850
|
pending.onEvent?.({
|
|
22839
22851
|
type: "session.progress",
|
|
@@ -23157,6 +23169,17 @@ var init_quota_gated_reply_sink = __esm(() => {
|
|
|
23157
23169
|
ADAPTIVE_WINDOW_SCHEDULE_MS = [3000, 6000, 12000, 24000, 48000, 60000];
|
|
23158
23170
|
});
|
|
23159
23171
|
|
|
23172
|
+
// src/transport/tool-event-mode.ts
|
|
23173
|
+
function resolveToolEventMode(input) {
|
|
23174
|
+
if (input?.toolEventMode !== undefined) {
|
|
23175
|
+
return input.toolEventMode;
|
|
23176
|
+
}
|
|
23177
|
+
if (input?.onToolEvent !== undefined) {
|
|
23178
|
+
return "structured";
|
|
23179
|
+
}
|
|
23180
|
+
return "text";
|
|
23181
|
+
}
|
|
23182
|
+
|
|
23160
23183
|
// src/transport/acpx-bridge/acpx-bridge-transport.ts
|
|
23161
23184
|
class AcpxBridgeTransport {
|
|
23162
23185
|
client;
|
|
@@ -23179,10 +23202,18 @@ class AcpxBridgeTransport {
|
|
|
23179
23202
|
}) : null;
|
|
23180
23203
|
let segmentError;
|
|
23181
23204
|
let segmentChain = Promise.resolve();
|
|
23205
|
+
let toolEventError;
|
|
23206
|
+
let toolEventChain = Promise.resolve();
|
|
23207
|
+
let toolEventMode = resolveToolEventMode(options);
|
|
23208
|
+
if ((toolEventMode === "structured" || toolEventMode === "both") && !options?.onToolEvent) {
|
|
23209
|
+
toolEventMode = "text";
|
|
23210
|
+
}
|
|
23182
23211
|
const result = await this.client.request("prompt", {
|
|
23183
23212
|
...this.toParams(session),
|
|
23184
23213
|
text,
|
|
23185
|
-
...options?.media ? { media: options.media } : {}
|
|
23214
|
+
...options?.media ? { media: options.media } : {},
|
|
23215
|
+
...toolEventMode === "structured" || toolEventMode === "both" ? { toolEvents: true } : {},
|
|
23216
|
+
toolEventMode
|
|
23186
23217
|
}, (event) => {
|
|
23187
23218
|
if (event.type === "prompt.segment") {
|
|
23188
23219
|
const onSegment = options?.onSegment;
|
|
@@ -23193,9 +23224,21 @@ class AcpxBridgeTransport {
|
|
|
23193
23224
|
});
|
|
23194
23225
|
}
|
|
23195
23226
|
sink?.feedSegment(event.text);
|
|
23227
|
+
return;
|
|
23228
|
+
}
|
|
23229
|
+
if (event.type === "prompt.tool_event") {
|
|
23230
|
+
const onToolEvent = options?.onToolEvent;
|
|
23231
|
+
if (onToolEvent) {
|
|
23232
|
+
const toolEvent = event.event;
|
|
23233
|
+
toolEventChain = toolEventChain.then(() => onToolEvent(toolEvent)).catch((error2) => {
|
|
23234
|
+
toolEventError ??= error2;
|
|
23235
|
+
});
|
|
23236
|
+
}
|
|
23237
|
+
return;
|
|
23196
23238
|
}
|
|
23197
23239
|
});
|
|
23198
23240
|
await segmentChain;
|
|
23241
|
+
await toolEventChain;
|
|
23199
23242
|
if (sink) {
|
|
23200
23243
|
const { overflowCount } = sink.finalize();
|
|
23201
23244
|
await sink.drain({ timeoutMs: 30000 });
|
|
@@ -23207,6 +23250,9 @@ class AcpxBridgeTransport {
|
|
|
23207
23250
|
if (segmentError) {
|
|
23208
23251
|
throw segmentError;
|
|
23209
23252
|
}
|
|
23253
|
+
if (toolEventError) {
|
|
23254
|
+
throw toolEventError;
|
|
23255
|
+
}
|
|
23210
23256
|
return { text: summary ? `${summary}
|
|
23211
23257
|
|
|
23212
23258
|
${result.text}` : "" };
|
|
@@ -23214,6 +23260,9 @@ ${result.text}` : "" };
|
|
|
23214
23260
|
if (segmentError) {
|
|
23215
23261
|
throw segmentError;
|
|
23216
23262
|
}
|
|
23263
|
+
if (toolEventError) {
|
|
23264
|
+
throw toolEventError;
|
|
23265
|
+
}
|
|
23217
23266
|
return result;
|
|
23218
23267
|
}
|
|
23219
23268
|
async setMode(session, modeId) {
|
|
@@ -23400,8 +23449,37 @@ var init_prompt_media = __esm(() => {
|
|
|
23400
23449
|
};
|
|
23401
23450
|
});
|
|
23402
23451
|
|
|
23452
|
+
// src/transport/tool-kind-emoji.ts
|
|
23453
|
+
var TOOL_KIND_EMOJI, DEFAULT_TOOL_EMOJI;
|
|
23454
|
+
var init_tool_kind_emoji = __esm(() => {
|
|
23455
|
+
TOOL_KIND_EMOJI = {
|
|
23456
|
+
read: "\uD83D\uDCD6",
|
|
23457
|
+
search: "\uD83D\uDD0D",
|
|
23458
|
+
execute: "\uD83D\uDCBB",
|
|
23459
|
+
edit: "✏️",
|
|
23460
|
+
think: "\uD83E\uDDE0",
|
|
23461
|
+
other: "\uD83D\uDD27"
|
|
23462
|
+
};
|
|
23463
|
+
DEFAULT_TOOL_EMOJI = TOOL_KIND_EMOJI.other;
|
|
23464
|
+
});
|
|
23465
|
+
|
|
23403
23466
|
// src/transport/streaming-prompt.ts
|
|
23404
|
-
function createStreamingPromptState(formatToolCalls = false) {
|
|
23467
|
+
function createStreamingPromptState(formatToolCalls = false, options) {
|
|
23468
|
+
let toolEventMode;
|
|
23469
|
+
let onToolEvent;
|
|
23470
|
+
if (options === undefined) {
|
|
23471
|
+
toolEventMode = "text";
|
|
23472
|
+
onToolEvent = undefined;
|
|
23473
|
+
} else if (typeof options === "function") {
|
|
23474
|
+
onToolEvent = options;
|
|
23475
|
+
toolEventMode = "structured";
|
|
23476
|
+
} else {
|
|
23477
|
+
onToolEvent = options.onToolEvent;
|
|
23478
|
+
toolEventMode = resolveToolEventMode({
|
|
23479
|
+
toolEventMode: options.mode,
|
|
23480
|
+
onToolEvent
|
|
23481
|
+
});
|
|
23482
|
+
}
|
|
23405
23483
|
return {
|
|
23406
23484
|
buffer: "",
|
|
23407
23485
|
segments: [],
|
|
@@ -23409,6 +23487,8 @@ function createStreamingPromptState(formatToolCalls = false) {
|
|
|
23409
23487
|
pendingLine: "",
|
|
23410
23488
|
formatToolCalls,
|
|
23411
23489
|
emittedToolCallIds: new Set,
|
|
23490
|
+
toolEventMode,
|
|
23491
|
+
onToolEvent,
|
|
23412
23492
|
finalize() {
|
|
23413
23493
|
if (this.pendingLine.trim().length > 0) {
|
|
23414
23494
|
parseStreamingChunks(this, this.pendingLine);
|
|
@@ -23446,15 +23526,24 @@ function parseStreamingChunks(state, line) {
|
|
|
23446
23526
|
if (!update)
|
|
23447
23527
|
return;
|
|
23448
23528
|
if (state.formatToolCalls && (update.sessionUpdate === "tool_call" || update.sessionUpdate === "tool_call_update")) {
|
|
23449
|
-
const
|
|
23450
|
-
|
|
23451
|
-
|
|
23452
|
-
|
|
23453
|
-
|
|
23454
|
-
|
|
23455
|
-
|
|
23529
|
+
const wantsStructured = state.toolEventMode === "structured" || state.toolEventMode === "both";
|
|
23530
|
+
const wantsText = state.toolEventMode === "text" || state.toolEventMode === "both";
|
|
23531
|
+
if (wantsStructured && state.onToolEvent) {
|
|
23532
|
+
const toolEvent = buildToolUseEvent(update);
|
|
23533
|
+
if (toolEvent)
|
|
23534
|
+
state.onToolEvent(toolEvent);
|
|
23535
|
+
}
|
|
23536
|
+
if (wantsText) {
|
|
23537
|
+
const formatted = formatToolCallEvent(update, update.sessionUpdate);
|
|
23538
|
+
if (formatted) {
|
|
23539
|
+
const toolCallId = update.toolCallId;
|
|
23540
|
+
if (toolCallId) {
|
|
23541
|
+
if (state.emittedToolCallIds.has(toolCallId))
|
|
23542
|
+
return;
|
|
23543
|
+
state.emittedToolCallIds.add(toolCallId);
|
|
23544
|
+
}
|
|
23545
|
+
state.segments.push(formatted);
|
|
23456
23546
|
}
|
|
23457
|
-
state.segments.push(formatted);
|
|
23458
23547
|
}
|
|
23459
23548
|
return;
|
|
23460
23549
|
}
|
|
@@ -23484,16 +23573,51 @@ function formatToolCallEvent(update, sessionUpdate) {
|
|
|
23484
23573
|
const title = update.title ?? "";
|
|
23485
23574
|
if (title.length === 0)
|
|
23486
23575
|
return null;
|
|
23487
|
-
const emoji2 =
|
|
23488
|
-
const inputSummary = summarizeToolInput(update.rawInput);
|
|
23576
|
+
const emoji2 = TOOL_KIND_EMOJI[kind] ?? DEFAULT_TOOL_EMOJI;
|
|
23577
|
+
const inputSummary = summarizeToolInput(update.rawInput, title);
|
|
23489
23578
|
const status = readString(update, "status");
|
|
23579
|
+
if (!inputSummary && status === "pending")
|
|
23580
|
+
return null;
|
|
23490
23581
|
if (!inputSummary && isGenericToolTitle(kind, title))
|
|
23491
23582
|
return null;
|
|
23492
|
-
const summaryText = inputSummary ? `: ${truncateToolDisplay(inputSummary)}` : "";
|
|
23583
|
+
const summaryText = inputSummary && inputSummary !== title ? `: ${truncateToolDisplay(inputSummary)}` : "";
|
|
23493
23584
|
const statusText = status ? ` (${status})` : "";
|
|
23494
23585
|
return `${emoji2} ${title}${statusText}${summaryText}`;
|
|
23495
23586
|
}
|
|
23496
|
-
function
|
|
23587
|
+
function buildToolUseEvent(update) {
|
|
23588
|
+
if (!update)
|
|
23589
|
+
return null;
|
|
23590
|
+
const toolCallId = update.toolCallId;
|
|
23591
|
+
if (!toolCallId)
|
|
23592
|
+
return null;
|
|
23593
|
+
const kindRaw = update.kind ?? "";
|
|
23594
|
+
const kind = (() => {
|
|
23595
|
+
switch (kindRaw) {
|
|
23596
|
+
case "read":
|
|
23597
|
+
case "search":
|
|
23598
|
+
case "execute":
|
|
23599
|
+
case "edit":
|
|
23600
|
+
case "think":
|
|
23601
|
+
return kindRaw;
|
|
23602
|
+
default:
|
|
23603
|
+
return "other";
|
|
23604
|
+
}
|
|
23605
|
+
})();
|
|
23606
|
+
const title = (update.title ?? "").trim();
|
|
23607
|
+
const toolName = title || "Tool";
|
|
23608
|
+
const summaryRaw = summarizeToolInput(update.rawInput, title);
|
|
23609
|
+
const summary = summaryRaw && summaryRaw !== title ? summaryRaw : undefined;
|
|
23610
|
+
const statusRaw = readString(update, "status");
|
|
23611
|
+
const status = statusRaw === "completed" || statusRaw === "success" ? "success" : statusRaw === "failed" || statusRaw === "error" ? "error" : "running";
|
|
23612
|
+
return {
|
|
23613
|
+
toolCallId,
|
|
23614
|
+
toolName,
|
|
23615
|
+
kind,
|
|
23616
|
+
...summary ? { summary } : {},
|
|
23617
|
+
status
|
|
23618
|
+
};
|
|
23619
|
+
}
|
|
23620
|
+
function summarizeToolInput(rawInput, title = "") {
|
|
23497
23621
|
if (rawInput == null)
|
|
23498
23622
|
return;
|
|
23499
23623
|
if (typeof rawInput === "string" || typeof rawInput === "number" || typeof rawInput === "boolean") {
|
|
@@ -23501,6 +23625,9 @@ function summarizeToolInput(rawInput) {
|
|
|
23501
23625
|
}
|
|
23502
23626
|
if (!isRecord3(rawInput))
|
|
23503
23627
|
return;
|
|
23628
|
+
const taskSummary = summarizeTaskInput(rawInput, title);
|
|
23629
|
+
if (taskSummary)
|
|
23630
|
+
return taskSummary;
|
|
23504
23631
|
const command = readFirstString(rawInput, ["command", "cmd", "program"]);
|
|
23505
23632
|
const args = readFirstStringArray(rawInput, ["args", "arguments"]);
|
|
23506
23633
|
if (command) {
|
|
@@ -23523,6 +23650,7 @@ function summarizeToolInput(rawInput) {
|
|
|
23523
23650
|
"file",
|
|
23524
23651
|
"filePath",
|
|
23525
23652
|
"filepath",
|
|
23653
|
+
"file_path",
|
|
23526
23654
|
"target",
|
|
23527
23655
|
"uri",
|
|
23528
23656
|
"url",
|
|
@@ -23534,6 +23662,16 @@ function summarizeToolInput(rawInput) {
|
|
|
23534
23662
|
"description"
|
|
23535
23663
|
]);
|
|
23536
23664
|
}
|
|
23665
|
+
function summarizeTaskInput(rawInput, title) {
|
|
23666
|
+
const subagentType = readFirstString(rawInput, ["subagent_type", "subagentType", "agent", "agentType"]);
|
|
23667
|
+
const description = readFirstString(rawInput, ["description", "task", "summary"]);
|
|
23668
|
+
if (subagentType && description) {
|
|
23669
|
+
return description === title ? subagentType : `${subagentType}: ${description}`;
|
|
23670
|
+
}
|
|
23671
|
+
if (subagentType)
|
|
23672
|
+
return subagentType;
|
|
23673
|
+
return;
|
|
23674
|
+
}
|
|
23537
23675
|
function readFirstString(record3, keys) {
|
|
23538
23676
|
for (const key of keys) {
|
|
23539
23677
|
const value = record3[key];
|
|
@@ -23580,14 +23718,8 @@ function isGenericToolTitle(kind, title) {
|
|
|
23580
23718
|
}
|
|
23581
23719
|
return false;
|
|
23582
23720
|
}
|
|
23583
|
-
var KIND_EMOJI;
|
|
23584
23721
|
var init_streaming_prompt = __esm(() => {
|
|
23585
|
-
|
|
23586
|
-
read: "\uD83D\uDCD6",
|
|
23587
|
-
search: "\uD83D\uDD0D",
|
|
23588
|
-
execute: "\uD83D\uDCBB",
|
|
23589
|
-
edit: "✏️"
|
|
23590
|
-
};
|
|
23722
|
+
init_tool_kind_emoji();
|
|
23591
23723
|
});
|
|
23592
23724
|
|
|
23593
23725
|
// src/transport/acpx-cli/node-pty-helper.ts
|
|
@@ -23897,7 +24029,8 @@ class AcpxCliTransport {
|
|
|
23897
24029
|
runCommand;
|
|
23898
24030
|
runPtyCommand;
|
|
23899
24031
|
queueOwnerLauncher;
|
|
23900
|
-
|
|
24032
|
+
streamingHooks;
|
|
24033
|
+
constructor(options, runCommand = defaultRunner, runPtyCommand = defaultPtyRunner, queueOwnerLauncher, streamingHooks = {}) {
|
|
23901
24034
|
this.command = options.command ?? "acpx";
|
|
23902
24035
|
this.sessionInitTimeoutMs = options.sessionInitTimeoutMs ?? 120000;
|
|
23903
24036
|
this.permissionMode = options.permissionMode ?? "approve-all";
|
|
@@ -23907,6 +24040,7 @@ class AcpxCliTransport {
|
|
|
23907
24040
|
this.queueOwnerLauncher = queueOwnerLauncher ?? new AcpxQueueOwnerLauncher({
|
|
23908
24041
|
acpxCommand: this.command
|
|
23909
24042
|
});
|
|
24043
|
+
this.streamingHooks = streamingHooks;
|
|
23910
24044
|
}
|
|
23911
24045
|
async ensureSession(session, _onProgress) {
|
|
23912
24046
|
const args = this.buildArgs(session, [
|
|
@@ -23925,9 +24059,13 @@ class AcpxCliTransport {
|
|
|
23925
24059
|
const structuredPrompt = await createStructuredPromptFile(text, options?.media);
|
|
23926
24060
|
const args = this.buildPromptArgs(session, text, structuredPrompt?.filePath);
|
|
23927
24061
|
try {
|
|
23928
|
-
if (reply || options?.onSegment) {
|
|
24062
|
+
if (reply || options?.onSegment || options?.onToolEvent) {
|
|
23929
24063
|
const formatToolCalls = (session.replyMode ?? "verbose") === "verbose";
|
|
23930
|
-
|
|
24064
|
+
let toolEventMode = resolveToolEventMode(options);
|
|
24065
|
+
if ((toolEventMode === "structured" || toolEventMode === "both") && !options?.onToolEvent) {
|
|
24066
|
+
toolEventMode = "text";
|
|
24067
|
+
}
|
|
24068
|
+
const { result: result2, overflowCount } = await this.runStreamingPrompt(this.command, args, reply, formatToolCalls, toolEventMode, replyContext, options?.onSegment, options?.onToolEvent);
|
|
23931
24069
|
const baseText = getPromptText(result2);
|
|
23932
24070
|
if (!reply) {
|
|
23933
24071
|
return { text: baseText };
|
|
@@ -24064,16 +24202,35 @@ ${baseText}` : "" };
|
|
|
24064
24202
|
})
|
|
24065
24203
|
]);
|
|
24066
24204
|
}
|
|
24067
|
-
async runStreamingPrompt(command, args, reply,
|
|
24205
|
+
async runStreamingPrompt(command, args, reply, formatToolCalls = false, toolEventMode = "text", replyContext, onSegment, onToolEvent) {
|
|
24206
|
+
const hooks = this.streamingHooks;
|
|
24207
|
+
const doSpawn = hooks.spawnPrompt ?? ((cmd, spawnArgs) => spawn8(cmd, spawnArgs, { stdio: ["ignore", "pipe", "pipe"] }));
|
|
24208
|
+
const setIntervalFn = hooks.setIntervalFn ?? ((fn, delay) => setInterval(fn, delay));
|
|
24209
|
+
const clearIntervalFn = hooks.clearIntervalFn ?? ((timer) => clearInterval(timer));
|
|
24210
|
+
const maxSegmentWaitMs = hooks.maxSegmentWaitMs ?? 30000;
|
|
24211
|
+
const flushCheckIntervalMs = hooks.flushCheckIntervalMs ?? 5000;
|
|
24212
|
+
const now = hooks.now ?? (() => Date.now());
|
|
24068
24213
|
return await new Promise((resolve3, reject) => {
|
|
24069
24214
|
const spawnSpec = resolveSpawnCommand(command, args);
|
|
24070
|
-
const child =
|
|
24215
|
+
const child = doSpawn(spawnSpec.command, spawnSpec.args);
|
|
24071
24216
|
let stdout2 = "";
|
|
24072
24217
|
let stderr = "";
|
|
24073
|
-
|
|
24074
|
-
let lastReplyAt = Date.now();
|
|
24218
|
+
let lastReplyAt = now();
|
|
24075
24219
|
let segmentChain = Promise.resolve();
|
|
24076
24220
|
let segmentError;
|
|
24221
|
+
let toolEventChain = Promise.resolve();
|
|
24222
|
+
let toolEventError;
|
|
24223
|
+
const userOnToolEvent = onToolEvent;
|
|
24224
|
+
const state = createStreamingPromptState(formatToolCalls, {
|
|
24225
|
+
mode: toolEventMode,
|
|
24226
|
+
...userOnToolEvent ? {
|
|
24227
|
+
onToolEvent: (event) => {
|
|
24228
|
+
toolEventChain = toolEventChain.then(() => userOnToolEvent(event)).catch((error2) => {
|
|
24229
|
+
toolEventError ??= error2;
|
|
24230
|
+
});
|
|
24231
|
+
}
|
|
24232
|
+
} : {}
|
|
24233
|
+
});
|
|
24077
24234
|
const sink = reply ? createQuotaGatedReplySink({
|
|
24078
24235
|
reply,
|
|
24079
24236
|
...replyContext ? { replyContext } : {}
|
|
@@ -24085,7 +24242,7 @@ ${baseText}` : "" };
|
|
|
24085
24242
|
});
|
|
24086
24243
|
}
|
|
24087
24244
|
sink?.feedSegment(segment);
|
|
24088
|
-
lastReplyAt =
|
|
24245
|
+
lastReplyAt = now();
|
|
24089
24246
|
};
|
|
24090
24247
|
const flushBuffer = () => {
|
|
24091
24248
|
const remaining = state.buffer.trim();
|
|
@@ -24094,11 +24251,11 @@ ${baseText}` : "" };
|
|
|
24094
24251
|
feedSegment(remaining);
|
|
24095
24252
|
}
|
|
24096
24253
|
};
|
|
24097
|
-
const timer =
|
|
24098
|
-
if (state.buffer.trim().length > 0 &&
|
|
24254
|
+
const timer = setIntervalFn(() => {
|
|
24255
|
+
if (state.buffer.trim().length > 0 && now() - lastReplyAt >= maxSegmentWaitMs) {
|
|
24099
24256
|
flushBuffer();
|
|
24100
24257
|
}
|
|
24101
|
-
},
|
|
24258
|
+
}, flushCheckIntervalMs);
|
|
24102
24259
|
child.stdout.setEncoding("utf8");
|
|
24103
24260
|
child.stdout.on("data", (chunk) => {
|
|
24104
24261
|
stdout2 += String(chunk);
|
|
@@ -24111,11 +24268,11 @@ ${baseText}` : "" };
|
|
|
24111
24268
|
stderr += String(chunk);
|
|
24112
24269
|
});
|
|
24113
24270
|
child.on("error", (err) => {
|
|
24114
|
-
|
|
24271
|
+
clearIntervalFn(timer);
|
|
24115
24272
|
reject(err);
|
|
24116
24273
|
});
|
|
24117
24274
|
child.on("close", (code) => {
|
|
24118
|
-
|
|
24275
|
+
clearIntervalFn(timer);
|
|
24119
24276
|
const remaining = state.finalize();
|
|
24120
24277
|
if (remaining.length > 0) {
|
|
24121
24278
|
feedSegment(remaining);
|
|
@@ -24123,7 +24280,8 @@ ${baseText}` : "" };
|
|
|
24123
24280
|
const { overflowCount } = sink?.finalize() ?? { overflowCount: 0 };
|
|
24124
24281
|
Promise.all([
|
|
24125
24282
|
sink?.drain({ timeoutMs: 30000 }) ?? Promise.resolve(),
|
|
24126
|
-
segmentChain
|
|
24283
|
+
segmentChain,
|
|
24284
|
+
toolEventChain
|
|
24127
24285
|
]).then(() => {
|
|
24128
24286
|
const deferred = sink?.getPendingError();
|
|
24129
24287
|
if (deferred) {
|
|
@@ -24134,6 +24292,10 @@ ${baseText}` : "" };
|
|
|
24134
24292
|
reject(segmentError);
|
|
24135
24293
|
return;
|
|
24136
24294
|
}
|
|
24295
|
+
if (toolEventError) {
|
|
24296
|
+
reject(toolEventError);
|
|
24297
|
+
return;
|
|
24298
|
+
}
|
|
24137
24299
|
resolve3({
|
|
24138
24300
|
result: { code: code ?? 1, stdout: stdout2, stderr },
|
|
24139
24301
|
overflowCount
|
|
@@ -25120,7 +25282,7 @@ async function checkDaemon(options = {}) {
|
|
|
25120
25282
|
cliEntryPath: options.cliEntryPath ?? resolveCliEntryPath(),
|
|
25121
25283
|
cwd: options.cwd ?? process.cwd(),
|
|
25122
25284
|
env: options.env ?? process.env,
|
|
25123
|
-
isProcessRunning: options.isProcessRunning ??
|
|
25285
|
+
isProcessRunning: options.isProcessRunning ?? defaultIsProcessRunning5
|
|
25124
25286
|
});
|
|
25125
25287
|
try {
|
|
25126
25288
|
const status = await controller.getStatus();
|
|
@@ -25191,7 +25353,7 @@ async function checkDaemon(options = {}) {
|
|
|
25191
25353
|
};
|
|
25192
25354
|
}
|
|
25193
25355
|
}
|
|
25194
|
-
function
|
|
25356
|
+
function defaultIsProcessRunning5(pid) {
|
|
25195
25357
|
try {
|
|
25196
25358
|
process.kill(pid, 0);
|
|
25197
25359
|
return true;
|
|
@@ -39205,6 +39367,82 @@ async function resolveMcpIdentity(server, options) {
|
|
|
39205
39367
|
}
|
|
39206
39368
|
throw new McpError(ErrorCode.InvalidRequest, "weacpx MCP identity is not configured; run through `weacpx mcp-stdio` or provide --coordinator-session");
|
|
39207
39369
|
}
|
|
39370
|
+
function installMcpStdioShutdownHooks(options) {
|
|
39371
|
+
const platform = options.platform ?? process.platform;
|
|
39372
|
+
const signalSource = options.signalSource ?? process;
|
|
39373
|
+
const isProcessRunning = options.isProcessRunning ?? defaultIsProcessRunning3;
|
|
39374
|
+
const setIntervalFn = options.setIntervalFn ?? ((callback, ms) => setInterval(callback, ms));
|
|
39375
|
+
const clearIntervalFn = options.clearIntervalFn ?? ((handle) => clearInterval(handle));
|
|
39376
|
+
const parentPid = options.parentPid ?? process.ppid;
|
|
39377
|
+
const parentCheckIntervalMs = options.parentCheckIntervalMs ?? parseParentCheckIntervalMs(process.env.WEACPX_MCP_PARENT_CHECK_INTERVAL_MS);
|
|
39378
|
+
let disposed = false;
|
|
39379
|
+
const triggerShutdown = (reason, context) => {
|
|
39380
|
+
if (disposed)
|
|
39381
|
+
return;
|
|
39382
|
+
options.onDiagnostic?.("mcp.stdio.shutdown", { reason, ...context ?? {} });
|
|
39383
|
+
options.shutdown();
|
|
39384
|
+
};
|
|
39385
|
+
const onStreamEnd = () => triggerShutdown("stdin.end");
|
|
39386
|
+
const onStreamClose = () => triggerShutdown("stdin.close");
|
|
39387
|
+
const onStdinError = (error2) => triggerShutdown("stdin.error", errorContext(error2));
|
|
39388
|
+
const onStdoutError = (error2) => triggerShutdown("stdout.error", errorContext(error2));
|
|
39389
|
+
const onSignal = (signal) => triggerShutdown("signal", { signal });
|
|
39390
|
+
options.stdin.on("end", onStreamEnd);
|
|
39391
|
+
options.stdin.on("close", onStreamClose);
|
|
39392
|
+
options.stdin.on("error", onStdinError);
|
|
39393
|
+
options.stdout.on("error", onStdoutError);
|
|
39394
|
+
const signals = platform === "win32" ? ["SIGINT", "SIGTERM", "SIGBREAK"] : ["SIGINT", "SIGTERM", "SIGHUP"];
|
|
39395
|
+
const signalListeners = signals.map((signal) => ({ signal, listener: () => onSignal(signal) }));
|
|
39396
|
+
for (const { signal, listener } of signalListeners) {
|
|
39397
|
+
signalSource.on(signal, listener);
|
|
39398
|
+
}
|
|
39399
|
+
let parentTimer;
|
|
39400
|
+
if (parentPid > 1 && parentCheckIntervalMs > 0) {
|
|
39401
|
+
parentTimer = setIntervalFn(() => {
|
|
39402
|
+
if (!isProcessRunning(parentPid)) {
|
|
39403
|
+
triggerShutdown("parent_dead", { parentPid });
|
|
39404
|
+
}
|
|
39405
|
+
}, parentCheckIntervalMs);
|
|
39406
|
+
parentTimer.unref?.();
|
|
39407
|
+
}
|
|
39408
|
+
return () => {
|
|
39409
|
+
if (disposed)
|
|
39410
|
+
return;
|
|
39411
|
+
disposed = true;
|
|
39412
|
+
options.stdin.off("end", onStreamEnd);
|
|
39413
|
+
options.stdin.off("close", onStreamClose);
|
|
39414
|
+
options.stdin.off("error", onStdinError);
|
|
39415
|
+
options.stdout.off("error", onStdoutError);
|
|
39416
|
+
for (const { signal, listener } of signalListeners) {
|
|
39417
|
+
signalSource.off(signal, listener);
|
|
39418
|
+
}
|
|
39419
|
+
if (parentTimer) {
|
|
39420
|
+
clearIntervalFn(parentTimer);
|
|
39421
|
+
}
|
|
39422
|
+
};
|
|
39423
|
+
}
|
|
39424
|
+
function parseParentCheckIntervalMs(raw) {
|
|
39425
|
+
if (raw === undefined || raw.trim().length === 0)
|
|
39426
|
+
return 5000;
|
|
39427
|
+
const parsed = Number(raw);
|
|
39428
|
+
return Number.isFinite(parsed) && parsed >= 0 ? parsed : 5000;
|
|
39429
|
+
}
|
|
39430
|
+
function errorContext(error2) {
|
|
39431
|
+
const record3 = error2;
|
|
39432
|
+
return {
|
|
39433
|
+
...typeof record3?.code === "string" ? { code: record3.code } : {},
|
|
39434
|
+
...typeof record3?.message === "string" ? { message: record3.message } : {}
|
|
39435
|
+
};
|
|
39436
|
+
}
|
|
39437
|
+
function defaultIsProcessRunning3(pid) {
|
|
39438
|
+
try {
|
|
39439
|
+
process.kill(pid, 0);
|
|
39440
|
+
return true;
|
|
39441
|
+
} catch (error2) {
|
|
39442
|
+
const code = error2?.code;
|
|
39443
|
+
return code !== "ESRCH";
|
|
39444
|
+
}
|
|
39445
|
+
}
|
|
39208
39446
|
async function runWeacpxMcpServer(options) {
|
|
39209
39447
|
const transport = options.transport ?? createOrchestrationTransport(options.endpoint ?? resolveDefaultOrchestrationEndpoint(process.env, process.platform));
|
|
39210
39448
|
const server = createWeacpxMcpServer({
|
|
@@ -39215,11 +39453,14 @@ async function runWeacpxMcpServer(options) {
|
|
|
39215
39453
|
...options.availableAgents ? { availableAgents: options.availableAgents } : {}
|
|
39216
39454
|
});
|
|
39217
39455
|
const stdio = new StdioServerTransport(stdin, stdout);
|
|
39456
|
+
let cleanupShutdownHooks;
|
|
39218
39457
|
let shuttingDown = false;
|
|
39219
39458
|
const shutdown = async () => {
|
|
39220
39459
|
if (shuttingDown)
|
|
39221
39460
|
return;
|
|
39222
39461
|
shuttingDown = true;
|
|
39462
|
+
cleanupShutdownHooks?.();
|
|
39463
|
+
options.onDiagnostic?.("mcp.stdio.stopping");
|
|
39223
39464
|
const forceExit = setTimeout(() => process.exit(0), 3000);
|
|
39224
39465
|
forceExit.unref();
|
|
39225
39466
|
try {
|
|
@@ -39227,11 +39468,16 @@ async function runWeacpxMcpServer(options) {
|
|
|
39227
39468
|
await stdio.close();
|
|
39228
39469
|
} catch {}
|
|
39229
39470
|
clearTimeout(forceExit);
|
|
39471
|
+
options.onDiagnostic?.("mcp.stdio.stopped");
|
|
39230
39472
|
process.exit(0);
|
|
39231
39473
|
};
|
|
39232
|
-
|
|
39233
|
-
|
|
39234
|
-
|
|
39474
|
+
options.onDiagnostic?.("mcp.stdio.start", { parentPid: process.ppid, platform: process.platform });
|
|
39475
|
+
cleanupShutdownHooks = installMcpStdioShutdownHooks({
|
|
39476
|
+
stdin,
|
|
39477
|
+
stdout,
|
|
39478
|
+
shutdown,
|
|
39479
|
+
onDiagnostic: options.onDiagnostic
|
|
39480
|
+
});
|
|
39235
39481
|
await server.connect(stdio);
|
|
39236
39482
|
}
|
|
39237
39483
|
function normalizeInputSchemaJson(schema) {
|
|
@@ -41023,7 +41269,12 @@ async function defaultMcpStdio(args, deps = {}) {
|
|
|
41023
41269
|
...coordinatorSession ? { coordinatorSession } : {},
|
|
41024
41270
|
...sourceHandle ? { sourceHandle } : {},
|
|
41025
41271
|
...identityResolver ? { resolveIdentity: identityResolver } : {},
|
|
41026
|
-
...availableAgents ? { availableAgents } : {}
|
|
41272
|
+
...availableAgents ? { availableAgents } : {},
|
|
41273
|
+
onDiagnostic: (event, context) => {
|
|
41274
|
+
const suffix = context && Object.keys(context).length > 0 ? ` ${JSON.stringify(context)}` : "";
|
|
41275
|
+
(deps.stderr ?? ((text) => process.stderr.write(text)))(`[weacpx:mcp] ${event}${suffix}
|
|
41276
|
+
`);
|
|
41277
|
+
}
|
|
41027
41278
|
});
|
|
41028
41279
|
return 0;
|
|
41029
41280
|
}
|
package/dist/plugin-api.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export type { ChannelPluginDefinition } from "./channels/plugin.js";
|
|
2
2
|
export type { ChannelFactory, CreateChannelDeps } from "./channels/create-channel.js";
|
|
3
|
-
export type { ChannelStartInput, ConsumerLock, ConsumerLockMetadata, ConsumerLockOptions, CoordinatorMessageInput, MessageChannelRuntime, OrchestrationDeliveryCallbacks, OutboundQuota, } from "./channels/types.js";
|
|
3
|
+
export type { ChannelStartInput, ConsumerLock, ConsumerLockMetadata, ConsumerLockOptions, CoordinatorMessageInput, MessageChannelRuntime, OrchestrationDeliveryCallbacks, OutboundQuota, ToolUseEvent, ToolUseKind, ToolUseStatus, } from "./channels/types.js";
|
|
4
4
|
export type { ChannelCliInput, ChannelCliIo, ChannelCliParseResult, ChannelCliProvider, ChannelCliValidationIssue, } from "./channels/cli/provider.js";
|
|
5
5
|
export type { ChannelRuntimeConfig } from "./config/types.js";
|
|
6
6
|
export type { AppLogger } from "./logging/app-logger.js";
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ChannelMediaAttachment, OutboundChannelMedia } from "../../channels/media-types.js";
|
|
2
|
+
import type { ToolUseEvent } from "../../channels/types.js";
|
|
2
3
|
/**
|
|
3
4
|
* Agent interface — any AI backend that can handle a chat message.
|
|
4
5
|
*
|
|
@@ -42,6 +43,8 @@ export interface ChatRequest {
|
|
|
42
43
|
* output produced after abort.
|
|
43
44
|
*/
|
|
44
45
|
abortSignal?: AbortSignal;
|
|
46
|
+
/** Structured tool-use side-channel; see PromptOptions.onToolEvent. */
|
|
47
|
+
onToolEvent?: (event: ToolUseEvent) => void | Promise<void>;
|
|
45
48
|
}
|
|
46
49
|
export interface ChatRequestMetadata {
|
|
47
50
|
channel?: string;
|