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.
- package/dist/bridge/bridge-main.js +144 -22
- package/dist/channels/types.d.ts +14 -0
- package/dist/cli.js +395 -145
- package/dist/plugin-api.d.ts +1 -1
- package/dist/weixin/agent/interface.d.ts +3 -0
- package/dist/weixin/storage/ensure-dir.d.ts +1 -0
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -9898,6 +9898,22 @@ var init_registry = __esm(() => {
|
|
|
9898
9898
|
cliProviders = new Map;
|
|
9899
9899
|
});
|
|
9900
9900
|
|
|
9901
|
+
// src/weixin/storage/ensure-dir.ts
|
|
9902
|
+
import fs2 from "node:fs";
|
|
9903
|
+
function ensureDirSync(dir) {
|
|
9904
|
+
try {
|
|
9905
|
+
fs2.mkdirSync(dir, { recursive: true });
|
|
9906
|
+
return;
|
|
9907
|
+
} catch (err) {
|
|
9908
|
+
try {
|
|
9909
|
+
if (fs2.statSync(dir).isDirectory())
|
|
9910
|
+
return;
|
|
9911
|
+
} catch {}
|
|
9912
|
+
throw err;
|
|
9913
|
+
}
|
|
9914
|
+
}
|
|
9915
|
+
var init_ensure_dir = () => {};
|
|
9916
|
+
|
|
9901
9917
|
// src/weixin/storage/state-dir.ts
|
|
9902
9918
|
import os from "node:os";
|
|
9903
9919
|
import path3 from "node:path";
|
|
@@ -9907,7 +9923,7 @@ function resolveStateDir() {
|
|
|
9907
9923
|
var init_state_dir = () => {};
|
|
9908
9924
|
|
|
9909
9925
|
// src/weixin/auth/accounts.ts
|
|
9910
|
-
import
|
|
9926
|
+
import fs3 from "node:fs";
|
|
9911
9927
|
import path4 from "node:path";
|
|
9912
9928
|
function normalizeAccountId(raw) {
|
|
9913
9929
|
return raw.trim().toLowerCase().replace(/[@.]/g, "-");
|
|
@@ -9930,9 +9946,9 @@ function resolveAccountIndexPath() {
|
|
|
9930
9946
|
function listIndexedWeixinAccountIds() {
|
|
9931
9947
|
const filePath = resolveAccountIndexPath();
|
|
9932
9948
|
try {
|
|
9933
|
-
if (!
|
|
9949
|
+
if (!fs3.existsSync(filePath))
|
|
9934
9950
|
return [];
|
|
9935
|
-
const raw =
|
|
9951
|
+
const raw = fs3.readFileSync(filePath, "utf-8");
|
|
9936
9952
|
const parsed = JSON.parse(raw);
|
|
9937
9953
|
if (!Array.isArray(parsed))
|
|
9938
9954
|
return [];
|
|
@@ -9943,8 +9959,8 @@ function listIndexedWeixinAccountIds() {
|
|
|
9943
9959
|
}
|
|
9944
9960
|
function registerWeixinAccountId(accountId) {
|
|
9945
9961
|
const dir = resolveWeixinStateDir();
|
|
9946
|
-
|
|
9947
|
-
|
|
9962
|
+
ensureDirSync(dir);
|
|
9963
|
+
fs3.writeFileSync(resolveAccountIndexPath(), JSON.stringify([accountId], null, 2), "utf-8");
|
|
9948
9964
|
}
|
|
9949
9965
|
function resolveAccountsDir() {
|
|
9950
9966
|
return path4.join(resolveWeixinStateDir(), "accounts");
|
|
@@ -9955,9 +9971,9 @@ function resolveAccountPath(accountId) {
|
|
|
9955
9971
|
function loadLegacyToken() {
|
|
9956
9972
|
const legacyPath = path4.join(resolveStateDir(), "credentials", "openclaw-weixin", "credentials.json");
|
|
9957
9973
|
try {
|
|
9958
|
-
if (!
|
|
9974
|
+
if (!fs3.existsSync(legacyPath))
|
|
9959
9975
|
return;
|
|
9960
|
-
const raw =
|
|
9976
|
+
const raw = fs3.readFileSync(legacyPath, "utf-8");
|
|
9961
9977
|
const parsed = JSON.parse(raw);
|
|
9962
9978
|
return typeof parsed.token === "string" ? parsed.token : undefined;
|
|
9963
9979
|
} catch {
|
|
@@ -9966,8 +9982,8 @@ function loadLegacyToken() {
|
|
|
9966
9982
|
}
|
|
9967
9983
|
function readAccountFile(filePath) {
|
|
9968
9984
|
try {
|
|
9969
|
-
if (
|
|
9970
|
-
return JSON.parse(
|
|
9985
|
+
if (fs3.existsSync(filePath)) {
|
|
9986
|
+
return JSON.parse(fs3.readFileSync(filePath, "utf-8"));
|
|
9971
9987
|
}
|
|
9972
9988
|
} catch {}
|
|
9973
9989
|
return null;
|
|
@@ -9989,7 +10005,7 @@ function loadWeixinAccount(accountId) {
|
|
|
9989
10005
|
}
|
|
9990
10006
|
function saveWeixinAccount(accountId, update) {
|
|
9991
10007
|
const dir = resolveAccountsDir();
|
|
9992
|
-
|
|
10008
|
+
ensureDirSync(dir);
|
|
9993
10009
|
const existing = loadWeixinAccount(accountId) ?? {};
|
|
9994
10010
|
const token = update.token?.trim() || existing.token;
|
|
9995
10011
|
const baseUrl = update.baseUrl?.trim() || existing.baseUrl;
|
|
@@ -10000,14 +10016,14 @@ function saveWeixinAccount(accountId, update) {
|
|
|
10000
10016
|
...userId ? { userId } : {}
|
|
10001
10017
|
};
|
|
10002
10018
|
const filePath = resolveAccountPath(accountId);
|
|
10003
|
-
|
|
10019
|
+
fs3.writeFileSync(filePath, JSON.stringify(data, null, 2), "utf-8");
|
|
10004
10020
|
try {
|
|
10005
|
-
|
|
10021
|
+
fs3.chmodSync(filePath, 384);
|
|
10006
10022
|
} catch {}
|
|
10007
10023
|
}
|
|
10008
10024
|
function clearWeixinAccount(accountId) {
|
|
10009
10025
|
try {
|
|
10010
|
-
|
|
10026
|
+
fs3.unlinkSync(resolveAccountPath(accountId));
|
|
10011
10027
|
} catch {}
|
|
10012
10028
|
}
|
|
10013
10029
|
function clearAllWeixinAccounts() {
|
|
@@ -10016,7 +10032,7 @@ function clearAllWeixinAccounts() {
|
|
|
10016
10032
|
clearWeixinAccount(id);
|
|
10017
10033
|
}
|
|
10018
10034
|
try {
|
|
10019
|
-
|
|
10035
|
+
fs3.writeFileSync(resolveAccountIndexPath(), "[]", "utf-8");
|
|
10020
10036
|
} catch {}
|
|
10021
10037
|
}
|
|
10022
10038
|
function resolveConfigPath() {
|
|
@@ -10028,9 +10044,9 @@ function resolveConfigPath() {
|
|
|
10028
10044
|
function loadConfigRouteTag(accountId) {
|
|
10029
10045
|
try {
|
|
10030
10046
|
const configPath = resolveConfigPath();
|
|
10031
|
-
if (!
|
|
10047
|
+
if (!fs3.existsSync(configPath))
|
|
10032
10048
|
return;
|
|
10033
|
-
const raw =
|
|
10049
|
+
const raw = fs3.readFileSync(configPath, "utf-8");
|
|
10034
10050
|
const cfg = JSON.parse(raw);
|
|
10035
10051
|
const channels = cfg.channels;
|
|
10036
10052
|
const section = channels?.["openclaw-weixin"];
|
|
@@ -10074,11 +10090,12 @@ function resolveWeixinAccount(accountId) {
|
|
|
10074
10090
|
}
|
|
10075
10091
|
var DEFAULT_BASE_URL = "https://ilinkai.weixin.qq.com", CDN_BASE_URL = "https://novac2c.cdn.weixin.qq.com/c2c";
|
|
10076
10092
|
var init_accounts = __esm(() => {
|
|
10093
|
+
init_ensure_dir();
|
|
10077
10094
|
init_state_dir();
|
|
10078
10095
|
});
|
|
10079
10096
|
|
|
10080
10097
|
// src/weixin/util/logger.ts
|
|
10081
|
-
import
|
|
10098
|
+
import fs4 from "node:fs";
|
|
10082
10099
|
import os2 from "node:os";
|
|
10083
10100
|
import path5 from "node:path";
|
|
10084
10101
|
function resolveMinLevel() {
|
|
@@ -10128,10 +10145,10 @@ function writeLog(level, message, accountId) {
|
|
|
10128
10145
|
});
|
|
10129
10146
|
try {
|
|
10130
10147
|
if (!logDirEnsured) {
|
|
10131
|
-
|
|
10148
|
+
fs4.mkdirSync(MAIN_LOG_DIR, { recursive: true });
|
|
10132
10149
|
logDirEnsured = true;
|
|
10133
10150
|
}
|
|
10134
|
-
|
|
10151
|
+
fs4.appendFileSync(resolveMainLogPath(), `${entry}
|
|
10135
10152
|
`, "utf-8");
|
|
10136
10153
|
} catch {}
|
|
10137
10154
|
}
|
|
@@ -12103,7 +12120,7 @@ var init_media_store = __esm(() => {
|
|
|
12103
12120
|
});
|
|
12104
12121
|
|
|
12105
12122
|
// src/channels/outbound-media-safety.ts
|
|
12106
|
-
import
|
|
12123
|
+
import fs5 from "node:fs/promises";
|
|
12107
12124
|
import path7 from "node:path";
|
|
12108
12125
|
async function resolveSafeOutboundMediaPath(mediaPath, allowedRoots) {
|
|
12109
12126
|
if (mediaPath.startsWith("http://") || mediaPath.startsWith("https://")) {
|
|
@@ -12114,7 +12131,7 @@ async function resolveSafeOutboundMediaPath(mediaPath, allowedRoots) {
|
|
|
12114
12131
|
if (!realCandidate) {
|
|
12115
12132
|
return null;
|
|
12116
12133
|
}
|
|
12117
|
-
const stat2 = await
|
|
12134
|
+
const stat2 = await fs5.stat(realCandidate).catch(() => null);
|
|
12118
12135
|
if (!stat2?.isFile()) {
|
|
12119
12136
|
return null;
|
|
12120
12137
|
}
|
|
@@ -12128,7 +12145,7 @@ async function resolveSafeOutboundMediaPath(mediaPath, allowedRoots) {
|
|
|
12128
12145
|
}
|
|
12129
12146
|
async function realpathOrNull(filePath) {
|
|
12130
12147
|
try {
|
|
12131
|
-
return await
|
|
12148
|
+
return await fs5.realpath(filePath);
|
|
12132
12149
|
} catch {
|
|
12133
12150
|
return null;
|
|
12134
12151
|
}
|
|
@@ -12824,10 +12841,10 @@ var init_cdn_upload = __esm(() => {
|
|
|
12824
12841
|
|
|
12825
12842
|
// src/weixin/cdn/upload.ts
|
|
12826
12843
|
import crypto3 from "node:crypto";
|
|
12827
|
-
import
|
|
12844
|
+
import fs6 from "node:fs/promises";
|
|
12828
12845
|
async function uploadMediaToCdn(params) {
|
|
12829
12846
|
const { filePath, toUserId, opts, cdnBaseUrl, mediaType, label } = params;
|
|
12830
|
-
const plaintext = await
|
|
12847
|
+
const plaintext = await fs6.readFile(filePath);
|
|
12831
12848
|
const rawsize = plaintext.length;
|
|
12832
12849
|
const rawfilemd5 = crypto3.createHash("md5").update(plaintext).digest("hex");
|
|
12833
12850
|
const filesize = aesEcbPaddedSize(rawsize);
|
|
@@ -12947,14 +12964,14 @@ var init_send_media = __esm(() => {
|
|
|
12947
12964
|
});
|
|
12948
12965
|
|
|
12949
12966
|
// src/weixin/messaging/debug-mode.ts
|
|
12950
|
-
import
|
|
12967
|
+
import fs7 from "node:fs";
|
|
12951
12968
|
import path10 from "node:path";
|
|
12952
12969
|
function resolveDebugModePath() {
|
|
12953
12970
|
return path10.join(resolveStateDir(), "openclaw-weixin", "debug-mode.json");
|
|
12954
12971
|
}
|
|
12955
12972
|
function loadState() {
|
|
12956
12973
|
try {
|
|
12957
|
-
const raw =
|
|
12974
|
+
const raw = fs7.readFileSync(resolveDebugModePath(), "utf-8");
|
|
12958
12975
|
const parsed = JSON.parse(raw);
|
|
12959
12976
|
if (parsed && typeof parsed.accounts === "object")
|
|
12960
12977
|
return parsed;
|
|
@@ -12963,8 +12980,8 @@ function loadState() {
|
|
|
12963
12980
|
}
|
|
12964
12981
|
function saveState(state) {
|
|
12965
12982
|
const filePath = resolveDebugModePath();
|
|
12966
|
-
|
|
12967
|
-
|
|
12983
|
+
ensureDirSync(path10.dirname(filePath));
|
|
12984
|
+
fs7.writeFileSync(filePath, JSON.stringify(state, null, 2), "utf-8");
|
|
12968
12985
|
}
|
|
12969
12986
|
function toggleDebugMode(accountId) {
|
|
12970
12987
|
const state = loadState();
|
|
@@ -12978,6 +12995,7 @@ function toggleDebugMode(accountId) {
|
|
|
12978
12995
|
return next;
|
|
12979
12996
|
}
|
|
12980
12997
|
var init_debug_mode = __esm(() => {
|
|
12998
|
+
init_ensure_dir();
|
|
12981
12999
|
init_state_dir();
|
|
12982
13000
|
init_logger();
|
|
12983
13001
|
});
|
|
@@ -13130,7 +13148,7 @@ function normalizeMediaArray(media) {
|
|
|
13130
13148
|
|
|
13131
13149
|
// src/weixin/messaging/handle-weixin-message-turn.ts
|
|
13132
13150
|
import crypto4 from "node:crypto";
|
|
13133
|
-
import
|
|
13151
|
+
import fs8 from "node:fs/promises";
|
|
13134
13152
|
import { tmpdir } from "node:os";
|
|
13135
13153
|
import path11 from "node:path";
|
|
13136
13154
|
function utf8ByteLength(s) {
|
|
@@ -13230,7 +13248,7 @@ function createSaveMediaBuffer(mediaTempDir) {
|
|
|
13230
13248
|
throw new Error(`media exceeds ${maxBytes} bytes`);
|
|
13231
13249
|
}
|
|
13232
13250
|
const dir = path11.join(resolveMediaTempDir(mediaTempDir), subdir ?? "");
|
|
13233
|
-
await
|
|
13251
|
+
await fs8.mkdir(dir, { recursive: true });
|
|
13234
13252
|
let ext = ".bin";
|
|
13235
13253
|
if (originalFilename) {
|
|
13236
13254
|
ext = path11.extname(originalFilename) || ".bin";
|
|
@@ -13239,7 +13257,7 @@ function createSaveMediaBuffer(mediaTempDir) {
|
|
|
13239
13257
|
}
|
|
13240
13258
|
const name = `${Date.now()}-${crypto4.randomBytes(4).toString("hex")}${ext}`;
|
|
13241
13259
|
const filePath = path11.join(dir, name);
|
|
13242
|
-
await
|
|
13260
|
+
await fs8.writeFile(filePath, buffer);
|
|
13243
13261
|
return { path: filePath };
|
|
13244
13262
|
};
|
|
13245
13263
|
}
|
|
@@ -13379,7 +13397,7 @@ async function handleWeixinMessageTurn(full, deps) {
|
|
|
13379
13397
|
continue;
|
|
13380
13398
|
}
|
|
13381
13399
|
try {
|
|
13382
|
-
const buffer = await
|
|
13400
|
+
const buffer = await fs8.readFile(filePath);
|
|
13383
13401
|
const mimeType = downloaded.fileMediaType ?? downloaded.voiceMediaType ?? defaultWeixinMime(descriptor.kind);
|
|
13384
13402
|
media.push(await mediaStore.saveMediaBuffer({
|
|
13385
13403
|
channelId: "weixin",
|
|
@@ -13393,7 +13411,7 @@ async function handleWeixinMessageTurn(full, deps) {
|
|
|
13393
13411
|
maxBytes: descriptor.kind === "image" ? DEFAULT_IMAGE_MAX_BYTES : DEFAULT_ATTACHMENT_MAX_BYTES
|
|
13394
13412
|
}));
|
|
13395
13413
|
} finally {
|
|
13396
|
-
await
|
|
13414
|
+
await fs8.rm(filePath, { force: true }).catch(() => {});
|
|
13397
13415
|
}
|
|
13398
13416
|
} catch (err) {
|
|
13399
13417
|
deps.errLog(`media download failed: ${String(err)}`);
|
|
@@ -13565,7 +13583,7 @@ var init_handle_weixin_message_turn = __esm(() => {
|
|
|
13565
13583
|
});
|
|
13566
13584
|
|
|
13567
13585
|
// src/weixin/storage/sync-buf.ts
|
|
13568
|
-
import
|
|
13586
|
+
import fs9 from "node:fs";
|
|
13569
13587
|
import path12 from "node:path";
|
|
13570
13588
|
function resolveAccountsDir2() {
|
|
13571
13589
|
return path12.join(resolveStateDir(), "openclaw-weixin", "accounts");
|
|
@@ -13578,7 +13596,7 @@ function getLegacySyncBufDefaultJsonPath() {
|
|
|
13578
13596
|
}
|
|
13579
13597
|
function readSyncBufFile(filePath) {
|
|
13580
13598
|
try {
|
|
13581
|
-
const raw =
|
|
13599
|
+
const raw = fs9.readFileSync(filePath, "utf-8");
|
|
13582
13600
|
const data = JSON.parse(raw);
|
|
13583
13601
|
if (typeof data.get_updates_buf === "string") {
|
|
13584
13602
|
return data.get_updates_buf;
|
|
@@ -13602,11 +13620,12 @@ function loadGetUpdatesBuf(filePath) {
|
|
|
13602
13620
|
}
|
|
13603
13621
|
function saveGetUpdatesBuf(filePath, getUpdatesBuf) {
|
|
13604
13622
|
const dir = path12.dirname(filePath);
|
|
13605
|
-
|
|
13606
|
-
|
|
13623
|
+
ensureDirSync(dir);
|
|
13624
|
+
fs9.writeFileSync(filePath, JSON.stringify({ get_updates_buf: getUpdatesBuf }, null, 0), "utf-8");
|
|
13607
13625
|
}
|
|
13608
13626
|
var init_sync_buf = __esm(() => {
|
|
13609
13627
|
init_accounts();
|
|
13628
|
+
init_ensure_dir();
|
|
13610
13629
|
init_state_dir();
|
|
13611
13630
|
});
|
|
13612
13631
|
|
|
@@ -14153,7 +14172,7 @@ import { dirname as dirname6, join as join3 } from "node:path";
|
|
|
14153
14172
|
import { homedir as homedir3 } from "node:os";
|
|
14154
14173
|
function createWeixinConsumerLock(options = {}) {
|
|
14155
14174
|
const lockFilePath = options.lockFilePath ?? join3(homedir3(), ".weacpx", "runtime", "weixin-consumer.lock.json");
|
|
14156
|
-
const isProcessRunning = options.isProcessRunning ??
|
|
14175
|
+
const isProcessRunning = options.isProcessRunning ?? defaultIsProcessRunning4;
|
|
14157
14176
|
const onDiagnostic = options.onDiagnostic;
|
|
14158
14177
|
return {
|
|
14159
14178
|
async acquire(meta2) {
|
|
@@ -14240,7 +14259,7 @@ async function loadLockMetadata(path13) {
|
|
|
14240
14259
|
return null;
|
|
14241
14260
|
}
|
|
14242
14261
|
}
|
|
14243
|
-
function
|
|
14262
|
+
function defaultIsProcessRunning4(pid) {
|
|
14244
14263
|
try {
|
|
14245
14264
|
process.kill(pid, 0);
|
|
14246
14265
|
return true;
|
|
@@ -16569,7 +16588,7 @@ async function handleSessionRemove(context, chatKey, alias) {
|
|
|
16569
16588
|
return { text: lines.join(`
|
|
16570
16589
|
`) };
|
|
16571
16590
|
}
|
|
16572
|
-
async function promptWithSession(context, session, chatKey, text, reply, replyContextToken, accountId, media, abortSignal) {
|
|
16591
|
+
async function promptWithSession(context, session, chatKey, text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent) {
|
|
16573
16592
|
const effectiveReplyMode = session.replyMode ?? context.config?.channel.replyMode ?? "verbose";
|
|
16574
16593
|
if (!session.replyMode)
|
|
16575
16594
|
session.replyMode = effectiveReplyMode;
|
|
@@ -16594,7 +16613,7 @@ async function promptWithSession(context, session, chatKey, text, reply, replyCo
|
|
|
16594
16613
|
const { promptText, taskIds, groupIds, claimHumanReply } = await preparePromptWithFallback(context, session, chatKey, text, replyContextToken, accountId);
|
|
16595
16614
|
try {
|
|
16596
16615
|
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);
|
|
16616
|
+
const result = await context.interaction.promptTransportSession(session, promptText, transportReply, replyContext, media, abortSignal, onToolEvent);
|
|
16598
16617
|
if (claimHumanReply) {
|
|
16599
16618
|
try {
|
|
16600
16619
|
await context.orchestration?.claimActiveHumanReply?.(claimHumanReply);
|
|
@@ -16614,17 +16633,17 @@ async function promptWithSession(context, session, chatKey, text, reply, replyCo
|
|
|
16614
16633
|
throw error2;
|
|
16615
16634
|
}
|
|
16616
16635
|
}
|
|
16617
|
-
async function handlePrompt(context, chatKey, text, reply, replyContextToken, accountId, media, abortSignal) {
|
|
16636
|
+
async function handlePrompt(context, chatKey, text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent) {
|
|
16618
16637
|
const session = await context.sessions.getCurrentSession(chatKey);
|
|
16619
16638
|
if (!session) {
|
|
16620
16639
|
return { text: NO_CURRENT_SESSION_TEXT };
|
|
16621
16640
|
}
|
|
16622
16641
|
try {
|
|
16623
|
-
return await promptWithSession(context, session, chatKey, text, reply, replyContextToken, accountId, media, abortSignal);
|
|
16642
|
+
return await promptWithSession(context, session, chatKey, text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent);
|
|
16624
16643
|
} catch (error2) {
|
|
16625
16644
|
const recovered = await context.recovery.tryRecoverMissingSession(session, error2);
|
|
16626
16645
|
if (recovered) {
|
|
16627
|
-
return await promptWithSession(context, recovered, chatKey, text, reply, replyContextToken, accountId, media, abortSignal);
|
|
16646
|
+
return await promptWithSession(context, recovered, chatKey, text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent);
|
|
16628
16647
|
}
|
|
16629
16648
|
return context.recovery.renderTransportError(session, error2);
|
|
16630
16649
|
}
|
|
@@ -18150,7 +18169,7 @@ class CommandRouter {
|
|
|
18150
18169
|
this.quota = quota;
|
|
18151
18170
|
this.logger = logger2 ?? createNoopAppLogger();
|
|
18152
18171
|
}
|
|
18153
|
-
async handle(chatKey, input, reply, replyContextToken, accountId, media, metadata, abortSignal) {
|
|
18172
|
+
async handle(chatKey, input, reply, replyContextToken, accountId, media, metadata, abortSignal, onToolEvent) {
|
|
18154
18173
|
const startedAt = Date.now();
|
|
18155
18174
|
const command = parseCommand(input);
|
|
18156
18175
|
await this.logger.debug("command.parsed", "parsed inbound command", {
|
|
@@ -18264,7 +18283,7 @@ class CommandRouter {
|
|
|
18264
18283
|
case "task.cancel":
|
|
18265
18284
|
return await handleTaskCancel(this.createHandlerContext(), chatKey, command.taskId);
|
|
18266
18285
|
case "prompt":
|
|
18267
|
-
return await handlePrompt(this.createSessionHandlerContext(), chatKey, command.text, reply, replyContextToken, accountId, media, abortSignal);
|
|
18286
|
+
return await handlePrompt(this.createSessionHandlerContext(), chatKey, command.text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent);
|
|
18268
18287
|
}
|
|
18269
18288
|
});
|
|
18270
18289
|
}
|
|
@@ -18316,7 +18335,7 @@ class CommandRouter {
|
|
|
18316
18335
|
return {
|
|
18317
18336
|
setModeTransportSession: (session, modeId) => this.setModeTransportSession(session, modeId),
|
|
18318
18337
|
cancelTransportSession: (session) => this.cancelTransportSession(session),
|
|
18319
|
-
promptTransportSession: (session, text, reply, replyContext, media, abortSignal) => this.promptTransportSession(session, text, reply, replyContext, media, abortSignal)
|
|
18338
|
+
promptTransportSession: (session, text, reply, replyContext, media, abortSignal, onToolEvent) => this.promptTransportSession(session, text, reply, replyContext, media, abortSignal, onToolEvent)
|
|
18320
18339
|
};
|
|
18321
18340
|
}
|
|
18322
18341
|
createSessionRenderRecoveryOps() {
|
|
@@ -18479,7 +18498,7 @@ class CommandRouter {
|
|
|
18479
18498
|
async checkTransportSession(session) {
|
|
18480
18499
|
return await this.measureTransportCall("has_session", session, () => this.transport.hasSession(session));
|
|
18481
18500
|
}
|
|
18482
|
-
async promptTransportSession(session, text, reply, replyContext, media, abortSignal) {
|
|
18501
|
+
async promptTransportSession(session, text, reply, replyContext, media, abortSignal, onToolEvent) {
|
|
18483
18502
|
session.mcpCoordinatorSession ??= session.transportSession;
|
|
18484
18503
|
let done = false;
|
|
18485
18504
|
let cancelOnAbort;
|
|
@@ -18516,7 +18535,10 @@ class CommandRouter {
|
|
|
18516
18535
|
abortSignal.addEventListener("abort", cancelOnAbort, { once: true });
|
|
18517
18536
|
}
|
|
18518
18537
|
try {
|
|
18519
|
-
return await this.measureTransportCall("prompt", session, () => this.transport.prompt(session, text, reply, replyContext,
|
|
18538
|
+
return await this.measureTransportCall("prompt", session, () => this.transport.prompt(session, text, reply, replyContext, {
|
|
18539
|
+
...media ? { media } : {},
|
|
18540
|
+
...onToolEvent ? { onToolEvent } : {}
|
|
18541
|
+
}));
|
|
18520
18542
|
} finally {
|
|
18521
18543
|
done = true;
|
|
18522
18544
|
if (cancelOnAbort && abortSignal) {
|
|
@@ -18667,7 +18689,7 @@ class ConsoleAgent {
|
|
|
18667
18689
|
mimeType: m.mimeType,
|
|
18668
18690
|
...m.fileName ? { fileName: m.fileName } : {}
|
|
18669
18691
|
})) : undefined;
|
|
18670
|
-
return await this.router.handle(request.conversationId, request.text, request.reply, request.replyContextToken, request.accountId, promptMedia, request.metadata, request.abortSignal);
|
|
18692
|
+
return await this.router.handle(request.conversationId, request.text, request.reply, request.replyContextToken, request.accountId, promptMedia, request.metadata, request.abortSignal, request.onToolEvent);
|
|
18671
18693
|
}
|
|
18672
18694
|
isKnownCommand(text) {
|
|
18673
18695
|
return isKnownWeacpxCommandText(text);
|
|
@@ -22767,6 +22789,10 @@ function encodeBridgePromptSegmentEvent(event) {
|
|
|
22767
22789
|
return `${JSON.stringify(event)}
|
|
22768
22790
|
`;
|
|
22769
22791
|
}
|
|
22792
|
+
function encodeBridgePromptToolEvent(event) {
|
|
22793
|
+
return `${JSON.stringify(event)}
|
|
22794
|
+
`;
|
|
22795
|
+
}
|
|
22770
22796
|
function encodeBridgeSessionProgressEvent(event) {
|
|
22771
22797
|
return `${JSON.stringify(event)}
|
|
22772
22798
|
`;
|
|
@@ -22834,6 +22860,11 @@ class AcpxBridgeClient {
|
|
|
22834
22860
|
type: "prompt.segment",
|
|
22835
22861
|
text: message.text
|
|
22836
22862
|
});
|
|
22863
|
+
} else if (message.event === "prompt.tool_event") {
|
|
22864
|
+
pending.onEvent?.({
|
|
22865
|
+
type: "prompt.tool_event",
|
|
22866
|
+
event: message.toolEvent
|
|
22867
|
+
});
|
|
22837
22868
|
} else if (message.event === "session.progress") {
|
|
22838
22869
|
pending.onEvent?.({
|
|
22839
22870
|
type: "session.progress",
|
|
@@ -23157,6 +23188,17 @@ var init_quota_gated_reply_sink = __esm(() => {
|
|
|
23157
23188
|
ADAPTIVE_WINDOW_SCHEDULE_MS = [3000, 6000, 12000, 24000, 48000, 60000];
|
|
23158
23189
|
});
|
|
23159
23190
|
|
|
23191
|
+
// src/transport/tool-event-mode.ts
|
|
23192
|
+
function resolveToolEventMode(input) {
|
|
23193
|
+
if (input?.toolEventMode !== undefined) {
|
|
23194
|
+
return input.toolEventMode;
|
|
23195
|
+
}
|
|
23196
|
+
if (input?.onToolEvent !== undefined) {
|
|
23197
|
+
return "structured";
|
|
23198
|
+
}
|
|
23199
|
+
return "text";
|
|
23200
|
+
}
|
|
23201
|
+
|
|
23160
23202
|
// src/transport/acpx-bridge/acpx-bridge-transport.ts
|
|
23161
23203
|
class AcpxBridgeTransport {
|
|
23162
23204
|
client;
|
|
@@ -23179,10 +23221,18 @@ class AcpxBridgeTransport {
|
|
|
23179
23221
|
}) : null;
|
|
23180
23222
|
let segmentError;
|
|
23181
23223
|
let segmentChain = Promise.resolve();
|
|
23224
|
+
let toolEventError;
|
|
23225
|
+
let toolEventChain = Promise.resolve();
|
|
23226
|
+
let toolEventMode = resolveToolEventMode(options);
|
|
23227
|
+
if ((toolEventMode === "structured" || toolEventMode === "both") && !options?.onToolEvent) {
|
|
23228
|
+
toolEventMode = "text";
|
|
23229
|
+
}
|
|
23182
23230
|
const result = await this.client.request("prompt", {
|
|
23183
23231
|
...this.toParams(session),
|
|
23184
23232
|
text,
|
|
23185
|
-
...options?.media ? { media: options.media } : {}
|
|
23233
|
+
...options?.media ? { media: options.media } : {},
|
|
23234
|
+
...toolEventMode === "structured" || toolEventMode === "both" ? { toolEvents: true } : {},
|
|
23235
|
+
toolEventMode
|
|
23186
23236
|
}, (event) => {
|
|
23187
23237
|
if (event.type === "prompt.segment") {
|
|
23188
23238
|
const onSegment = options?.onSegment;
|
|
@@ -23193,9 +23243,21 @@ class AcpxBridgeTransport {
|
|
|
23193
23243
|
});
|
|
23194
23244
|
}
|
|
23195
23245
|
sink?.feedSegment(event.text);
|
|
23246
|
+
return;
|
|
23247
|
+
}
|
|
23248
|
+
if (event.type === "prompt.tool_event") {
|
|
23249
|
+
const onToolEvent = options?.onToolEvent;
|
|
23250
|
+
if (onToolEvent) {
|
|
23251
|
+
const toolEvent = event.event;
|
|
23252
|
+
toolEventChain = toolEventChain.then(() => onToolEvent(toolEvent)).catch((error2) => {
|
|
23253
|
+
toolEventError ??= error2;
|
|
23254
|
+
});
|
|
23255
|
+
}
|
|
23256
|
+
return;
|
|
23196
23257
|
}
|
|
23197
23258
|
});
|
|
23198
23259
|
await segmentChain;
|
|
23260
|
+
await toolEventChain;
|
|
23199
23261
|
if (sink) {
|
|
23200
23262
|
const { overflowCount } = sink.finalize();
|
|
23201
23263
|
await sink.drain({ timeoutMs: 30000 });
|
|
@@ -23207,6 +23269,9 @@ class AcpxBridgeTransport {
|
|
|
23207
23269
|
if (segmentError) {
|
|
23208
23270
|
throw segmentError;
|
|
23209
23271
|
}
|
|
23272
|
+
if (toolEventError) {
|
|
23273
|
+
throw toolEventError;
|
|
23274
|
+
}
|
|
23210
23275
|
return { text: summary ? `${summary}
|
|
23211
23276
|
|
|
23212
23277
|
${result.text}` : "" };
|
|
@@ -23214,6 +23279,9 @@ ${result.text}` : "" };
|
|
|
23214
23279
|
if (segmentError) {
|
|
23215
23280
|
throw segmentError;
|
|
23216
23281
|
}
|
|
23282
|
+
if (toolEventError) {
|
|
23283
|
+
throw toolEventError;
|
|
23284
|
+
}
|
|
23217
23285
|
return result;
|
|
23218
23286
|
}
|
|
23219
23287
|
async setMode(session, modeId) {
|
|
@@ -23400,8 +23468,37 @@ var init_prompt_media = __esm(() => {
|
|
|
23400
23468
|
};
|
|
23401
23469
|
});
|
|
23402
23470
|
|
|
23471
|
+
// src/transport/tool-kind-emoji.ts
|
|
23472
|
+
var TOOL_KIND_EMOJI, DEFAULT_TOOL_EMOJI;
|
|
23473
|
+
var init_tool_kind_emoji = __esm(() => {
|
|
23474
|
+
TOOL_KIND_EMOJI = {
|
|
23475
|
+
read: "\uD83D\uDCD6",
|
|
23476
|
+
search: "\uD83D\uDD0D",
|
|
23477
|
+
execute: "\uD83D\uDCBB",
|
|
23478
|
+
edit: "✏️",
|
|
23479
|
+
think: "\uD83E\uDDE0",
|
|
23480
|
+
other: "\uD83D\uDD27"
|
|
23481
|
+
};
|
|
23482
|
+
DEFAULT_TOOL_EMOJI = TOOL_KIND_EMOJI.other;
|
|
23483
|
+
});
|
|
23484
|
+
|
|
23403
23485
|
// src/transport/streaming-prompt.ts
|
|
23404
|
-
function createStreamingPromptState(formatToolCalls = false) {
|
|
23486
|
+
function createStreamingPromptState(formatToolCalls = false, options) {
|
|
23487
|
+
let toolEventMode;
|
|
23488
|
+
let onToolEvent;
|
|
23489
|
+
if (options === undefined) {
|
|
23490
|
+
toolEventMode = "text";
|
|
23491
|
+
onToolEvent = undefined;
|
|
23492
|
+
} else if (typeof options === "function") {
|
|
23493
|
+
onToolEvent = options;
|
|
23494
|
+
toolEventMode = "structured";
|
|
23495
|
+
} else {
|
|
23496
|
+
onToolEvent = options.onToolEvent;
|
|
23497
|
+
toolEventMode = resolveToolEventMode({
|
|
23498
|
+
toolEventMode: options.mode,
|
|
23499
|
+
onToolEvent
|
|
23500
|
+
});
|
|
23501
|
+
}
|
|
23405
23502
|
return {
|
|
23406
23503
|
buffer: "",
|
|
23407
23504
|
segments: [],
|
|
@@ -23409,6 +23506,8 @@ function createStreamingPromptState(formatToolCalls = false) {
|
|
|
23409
23506
|
pendingLine: "",
|
|
23410
23507
|
formatToolCalls,
|
|
23411
23508
|
emittedToolCallIds: new Set,
|
|
23509
|
+
toolEventMode,
|
|
23510
|
+
onToolEvent,
|
|
23412
23511
|
finalize() {
|
|
23413
23512
|
if (this.pendingLine.trim().length > 0) {
|
|
23414
23513
|
parseStreamingChunks(this, this.pendingLine);
|
|
@@ -23446,15 +23545,24 @@ function parseStreamingChunks(state, line) {
|
|
|
23446
23545
|
if (!update)
|
|
23447
23546
|
return;
|
|
23448
23547
|
if (state.formatToolCalls && (update.sessionUpdate === "tool_call" || update.sessionUpdate === "tool_call_update")) {
|
|
23449
|
-
const
|
|
23450
|
-
|
|
23451
|
-
|
|
23452
|
-
|
|
23453
|
-
|
|
23454
|
-
|
|
23455
|
-
|
|
23548
|
+
const wantsStructured = state.toolEventMode === "structured" || state.toolEventMode === "both";
|
|
23549
|
+
const wantsText = state.toolEventMode === "text" || state.toolEventMode === "both";
|
|
23550
|
+
if (wantsStructured && state.onToolEvent) {
|
|
23551
|
+
const toolEvent = buildToolUseEvent(update);
|
|
23552
|
+
if (toolEvent)
|
|
23553
|
+
state.onToolEvent(toolEvent);
|
|
23554
|
+
}
|
|
23555
|
+
if (wantsText) {
|
|
23556
|
+
const formatted = formatToolCallEvent(update, update.sessionUpdate);
|
|
23557
|
+
if (formatted) {
|
|
23558
|
+
const toolCallId = update.toolCallId;
|
|
23559
|
+
if (toolCallId) {
|
|
23560
|
+
if (state.emittedToolCallIds.has(toolCallId))
|
|
23561
|
+
return;
|
|
23562
|
+
state.emittedToolCallIds.add(toolCallId);
|
|
23563
|
+
}
|
|
23564
|
+
state.segments.push(formatted);
|
|
23456
23565
|
}
|
|
23457
|
-
state.segments.push(formatted);
|
|
23458
23566
|
}
|
|
23459
23567
|
return;
|
|
23460
23568
|
}
|
|
@@ -23484,16 +23592,51 @@ function formatToolCallEvent(update, sessionUpdate) {
|
|
|
23484
23592
|
const title = update.title ?? "";
|
|
23485
23593
|
if (title.length === 0)
|
|
23486
23594
|
return null;
|
|
23487
|
-
const emoji2 =
|
|
23488
|
-
const inputSummary = summarizeToolInput(update.rawInput);
|
|
23595
|
+
const emoji2 = TOOL_KIND_EMOJI[kind] ?? DEFAULT_TOOL_EMOJI;
|
|
23596
|
+
const inputSummary = summarizeToolInput(update.rawInput, title);
|
|
23489
23597
|
const status = readString(update, "status");
|
|
23598
|
+
if (!inputSummary && status === "pending")
|
|
23599
|
+
return null;
|
|
23490
23600
|
if (!inputSummary && isGenericToolTitle(kind, title))
|
|
23491
23601
|
return null;
|
|
23492
|
-
const summaryText = inputSummary ? `: ${truncateToolDisplay(inputSummary)}` : "";
|
|
23602
|
+
const summaryText = inputSummary && inputSummary !== title ? `: ${truncateToolDisplay(inputSummary)}` : "";
|
|
23493
23603
|
const statusText = status ? ` (${status})` : "";
|
|
23494
23604
|
return `${emoji2} ${title}${statusText}${summaryText}`;
|
|
23495
23605
|
}
|
|
23496
|
-
function
|
|
23606
|
+
function buildToolUseEvent(update) {
|
|
23607
|
+
if (!update)
|
|
23608
|
+
return null;
|
|
23609
|
+
const toolCallId = update.toolCallId;
|
|
23610
|
+
if (!toolCallId)
|
|
23611
|
+
return null;
|
|
23612
|
+
const kindRaw = update.kind ?? "";
|
|
23613
|
+
const kind = (() => {
|
|
23614
|
+
switch (kindRaw) {
|
|
23615
|
+
case "read":
|
|
23616
|
+
case "search":
|
|
23617
|
+
case "execute":
|
|
23618
|
+
case "edit":
|
|
23619
|
+
case "think":
|
|
23620
|
+
return kindRaw;
|
|
23621
|
+
default:
|
|
23622
|
+
return "other";
|
|
23623
|
+
}
|
|
23624
|
+
})();
|
|
23625
|
+
const title = (update.title ?? "").trim();
|
|
23626
|
+
const toolName = title || "Tool";
|
|
23627
|
+
const summaryRaw = summarizeToolInput(update.rawInput, title);
|
|
23628
|
+
const summary = summaryRaw && summaryRaw !== title ? summaryRaw : undefined;
|
|
23629
|
+
const statusRaw = readString(update, "status");
|
|
23630
|
+
const status = statusRaw === "completed" || statusRaw === "success" ? "success" : statusRaw === "failed" || statusRaw === "error" ? "error" : "running";
|
|
23631
|
+
return {
|
|
23632
|
+
toolCallId,
|
|
23633
|
+
toolName,
|
|
23634
|
+
kind,
|
|
23635
|
+
...summary ? { summary } : {},
|
|
23636
|
+
status
|
|
23637
|
+
};
|
|
23638
|
+
}
|
|
23639
|
+
function summarizeToolInput(rawInput, title = "") {
|
|
23497
23640
|
if (rawInput == null)
|
|
23498
23641
|
return;
|
|
23499
23642
|
if (typeof rawInput === "string" || typeof rawInput === "number" || typeof rawInput === "boolean") {
|
|
@@ -23501,6 +23644,9 @@ function summarizeToolInput(rawInput) {
|
|
|
23501
23644
|
}
|
|
23502
23645
|
if (!isRecord3(rawInput))
|
|
23503
23646
|
return;
|
|
23647
|
+
const taskSummary = summarizeTaskInput(rawInput, title);
|
|
23648
|
+
if (taskSummary)
|
|
23649
|
+
return taskSummary;
|
|
23504
23650
|
const command = readFirstString(rawInput, ["command", "cmd", "program"]);
|
|
23505
23651
|
const args = readFirstStringArray(rawInput, ["args", "arguments"]);
|
|
23506
23652
|
if (command) {
|
|
@@ -23523,6 +23669,7 @@ function summarizeToolInput(rawInput) {
|
|
|
23523
23669
|
"file",
|
|
23524
23670
|
"filePath",
|
|
23525
23671
|
"filepath",
|
|
23672
|
+
"file_path",
|
|
23526
23673
|
"target",
|
|
23527
23674
|
"uri",
|
|
23528
23675
|
"url",
|
|
@@ -23534,6 +23681,16 @@ function summarizeToolInput(rawInput) {
|
|
|
23534
23681
|
"description"
|
|
23535
23682
|
]);
|
|
23536
23683
|
}
|
|
23684
|
+
function summarizeTaskInput(rawInput, title) {
|
|
23685
|
+
const subagentType = readFirstString(rawInput, ["subagent_type", "subagentType", "agent", "agentType"]);
|
|
23686
|
+
const description = readFirstString(rawInput, ["description", "task", "summary"]);
|
|
23687
|
+
if (subagentType && description) {
|
|
23688
|
+
return description === title ? subagentType : `${subagentType}: ${description}`;
|
|
23689
|
+
}
|
|
23690
|
+
if (subagentType)
|
|
23691
|
+
return subagentType;
|
|
23692
|
+
return;
|
|
23693
|
+
}
|
|
23537
23694
|
function readFirstString(record3, keys) {
|
|
23538
23695
|
for (const key of keys) {
|
|
23539
23696
|
const value = record3[key];
|
|
@@ -23580,14 +23737,8 @@ function isGenericToolTitle(kind, title) {
|
|
|
23580
23737
|
}
|
|
23581
23738
|
return false;
|
|
23582
23739
|
}
|
|
23583
|
-
var KIND_EMOJI;
|
|
23584
23740
|
var init_streaming_prompt = __esm(() => {
|
|
23585
|
-
|
|
23586
|
-
read: "\uD83D\uDCD6",
|
|
23587
|
-
search: "\uD83D\uDD0D",
|
|
23588
|
-
execute: "\uD83D\uDCBB",
|
|
23589
|
-
edit: "✏️"
|
|
23590
|
-
};
|
|
23741
|
+
init_tool_kind_emoji();
|
|
23591
23742
|
});
|
|
23592
23743
|
|
|
23593
23744
|
// src/transport/acpx-cli/node-pty-helper.ts
|
|
@@ -23897,7 +24048,8 @@ class AcpxCliTransport {
|
|
|
23897
24048
|
runCommand;
|
|
23898
24049
|
runPtyCommand;
|
|
23899
24050
|
queueOwnerLauncher;
|
|
23900
|
-
|
|
24051
|
+
streamingHooks;
|
|
24052
|
+
constructor(options, runCommand = defaultRunner, runPtyCommand = defaultPtyRunner, queueOwnerLauncher, streamingHooks = {}) {
|
|
23901
24053
|
this.command = options.command ?? "acpx";
|
|
23902
24054
|
this.sessionInitTimeoutMs = options.sessionInitTimeoutMs ?? 120000;
|
|
23903
24055
|
this.permissionMode = options.permissionMode ?? "approve-all";
|
|
@@ -23907,6 +24059,7 @@ class AcpxCliTransport {
|
|
|
23907
24059
|
this.queueOwnerLauncher = queueOwnerLauncher ?? new AcpxQueueOwnerLauncher({
|
|
23908
24060
|
acpxCommand: this.command
|
|
23909
24061
|
});
|
|
24062
|
+
this.streamingHooks = streamingHooks;
|
|
23910
24063
|
}
|
|
23911
24064
|
async ensureSession(session, _onProgress) {
|
|
23912
24065
|
const args = this.buildArgs(session, [
|
|
@@ -23925,9 +24078,13 @@ class AcpxCliTransport {
|
|
|
23925
24078
|
const structuredPrompt = await createStructuredPromptFile(text, options?.media);
|
|
23926
24079
|
const args = this.buildPromptArgs(session, text, structuredPrompt?.filePath);
|
|
23927
24080
|
try {
|
|
23928
|
-
if (reply || options?.onSegment) {
|
|
24081
|
+
if (reply || options?.onSegment || options?.onToolEvent) {
|
|
23929
24082
|
const formatToolCalls = (session.replyMode ?? "verbose") === "verbose";
|
|
23930
|
-
|
|
24083
|
+
let toolEventMode = resolveToolEventMode(options);
|
|
24084
|
+
if ((toolEventMode === "structured" || toolEventMode === "both") && !options?.onToolEvent) {
|
|
24085
|
+
toolEventMode = "text";
|
|
24086
|
+
}
|
|
24087
|
+
const { result: result2, overflowCount } = await this.runStreamingPrompt(this.command, args, reply, formatToolCalls, toolEventMode, replyContext, options?.onSegment, options?.onToolEvent);
|
|
23931
24088
|
const baseText = getPromptText(result2);
|
|
23932
24089
|
if (!reply) {
|
|
23933
24090
|
return { text: baseText };
|
|
@@ -24064,16 +24221,35 @@ ${baseText}` : "" };
|
|
|
24064
24221
|
})
|
|
24065
24222
|
]);
|
|
24066
24223
|
}
|
|
24067
|
-
async runStreamingPrompt(command, args, reply,
|
|
24224
|
+
async runStreamingPrompt(command, args, reply, formatToolCalls = false, toolEventMode = "text", replyContext, onSegment, onToolEvent) {
|
|
24225
|
+
const hooks = this.streamingHooks;
|
|
24226
|
+
const doSpawn = hooks.spawnPrompt ?? ((cmd, spawnArgs) => spawn8(cmd, spawnArgs, { stdio: ["ignore", "pipe", "pipe"] }));
|
|
24227
|
+
const setIntervalFn = hooks.setIntervalFn ?? ((fn, delay) => setInterval(fn, delay));
|
|
24228
|
+
const clearIntervalFn = hooks.clearIntervalFn ?? ((timer) => clearInterval(timer));
|
|
24229
|
+
const maxSegmentWaitMs = hooks.maxSegmentWaitMs ?? 30000;
|
|
24230
|
+
const flushCheckIntervalMs = hooks.flushCheckIntervalMs ?? 5000;
|
|
24231
|
+
const now = hooks.now ?? (() => Date.now());
|
|
24068
24232
|
return await new Promise((resolve3, reject) => {
|
|
24069
24233
|
const spawnSpec = resolveSpawnCommand(command, args);
|
|
24070
|
-
const child =
|
|
24234
|
+
const child = doSpawn(spawnSpec.command, spawnSpec.args);
|
|
24071
24235
|
let stdout2 = "";
|
|
24072
24236
|
let stderr = "";
|
|
24073
|
-
|
|
24074
|
-
let lastReplyAt = Date.now();
|
|
24237
|
+
let lastReplyAt = now();
|
|
24075
24238
|
let segmentChain = Promise.resolve();
|
|
24076
24239
|
let segmentError;
|
|
24240
|
+
let toolEventChain = Promise.resolve();
|
|
24241
|
+
let toolEventError;
|
|
24242
|
+
const userOnToolEvent = onToolEvent;
|
|
24243
|
+
const state = createStreamingPromptState(formatToolCalls, {
|
|
24244
|
+
mode: toolEventMode,
|
|
24245
|
+
...userOnToolEvent ? {
|
|
24246
|
+
onToolEvent: (event) => {
|
|
24247
|
+
toolEventChain = toolEventChain.then(() => userOnToolEvent(event)).catch((error2) => {
|
|
24248
|
+
toolEventError ??= error2;
|
|
24249
|
+
});
|
|
24250
|
+
}
|
|
24251
|
+
} : {}
|
|
24252
|
+
});
|
|
24077
24253
|
const sink = reply ? createQuotaGatedReplySink({
|
|
24078
24254
|
reply,
|
|
24079
24255
|
...replyContext ? { replyContext } : {}
|
|
@@ -24085,7 +24261,7 @@ ${baseText}` : "" };
|
|
|
24085
24261
|
});
|
|
24086
24262
|
}
|
|
24087
24263
|
sink?.feedSegment(segment);
|
|
24088
|
-
lastReplyAt =
|
|
24264
|
+
lastReplyAt = now();
|
|
24089
24265
|
};
|
|
24090
24266
|
const flushBuffer = () => {
|
|
24091
24267
|
const remaining = state.buffer.trim();
|
|
@@ -24094,11 +24270,11 @@ ${baseText}` : "" };
|
|
|
24094
24270
|
feedSegment(remaining);
|
|
24095
24271
|
}
|
|
24096
24272
|
};
|
|
24097
|
-
const timer =
|
|
24098
|
-
if (state.buffer.trim().length > 0 &&
|
|
24273
|
+
const timer = setIntervalFn(() => {
|
|
24274
|
+
if (state.buffer.trim().length > 0 && now() - lastReplyAt >= maxSegmentWaitMs) {
|
|
24099
24275
|
flushBuffer();
|
|
24100
24276
|
}
|
|
24101
|
-
},
|
|
24277
|
+
}, flushCheckIntervalMs);
|
|
24102
24278
|
child.stdout.setEncoding("utf8");
|
|
24103
24279
|
child.stdout.on("data", (chunk) => {
|
|
24104
24280
|
stdout2 += String(chunk);
|
|
@@ -24111,11 +24287,11 @@ ${baseText}` : "" };
|
|
|
24111
24287
|
stderr += String(chunk);
|
|
24112
24288
|
});
|
|
24113
24289
|
child.on("error", (err) => {
|
|
24114
|
-
|
|
24290
|
+
clearIntervalFn(timer);
|
|
24115
24291
|
reject(err);
|
|
24116
24292
|
});
|
|
24117
24293
|
child.on("close", (code) => {
|
|
24118
|
-
|
|
24294
|
+
clearIntervalFn(timer);
|
|
24119
24295
|
const remaining = state.finalize();
|
|
24120
24296
|
if (remaining.length > 0) {
|
|
24121
24297
|
feedSegment(remaining);
|
|
@@ -24123,7 +24299,8 @@ ${baseText}` : "" };
|
|
|
24123
24299
|
const { overflowCount } = sink?.finalize() ?? { overflowCount: 0 };
|
|
24124
24300
|
Promise.all([
|
|
24125
24301
|
sink?.drain({ timeoutMs: 30000 }) ?? Promise.resolve(),
|
|
24126
|
-
segmentChain
|
|
24302
|
+
segmentChain,
|
|
24303
|
+
toolEventChain
|
|
24127
24304
|
]).then(() => {
|
|
24128
24305
|
const deferred = sink?.getPendingError();
|
|
24129
24306
|
if (deferred) {
|
|
@@ -24134,6 +24311,10 @@ ${baseText}` : "" };
|
|
|
24134
24311
|
reject(segmentError);
|
|
24135
24312
|
return;
|
|
24136
24313
|
}
|
|
24314
|
+
if (toolEventError) {
|
|
24315
|
+
reject(toolEventError);
|
|
24316
|
+
return;
|
|
24317
|
+
}
|
|
24137
24318
|
resolve3({
|
|
24138
24319
|
result: { code: code ?? 1, stdout: stdout2, stderr },
|
|
24139
24320
|
overflowCount
|
|
@@ -25120,7 +25301,7 @@ async function checkDaemon(options = {}) {
|
|
|
25120
25301
|
cliEntryPath: options.cliEntryPath ?? resolveCliEntryPath(),
|
|
25121
25302
|
cwd: options.cwd ?? process.cwd(),
|
|
25122
25303
|
env: options.env ?? process.env,
|
|
25123
|
-
isProcessRunning: options.isProcessRunning ??
|
|
25304
|
+
isProcessRunning: options.isProcessRunning ?? defaultIsProcessRunning5
|
|
25124
25305
|
});
|
|
25125
25306
|
try {
|
|
25126
25307
|
const status = await controller.getStatus();
|
|
@@ -25191,7 +25372,7 @@ async function checkDaemon(options = {}) {
|
|
|
25191
25372
|
};
|
|
25192
25373
|
}
|
|
25193
25374
|
}
|
|
25194
|
-
function
|
|
25375
|
+
function defaultIsProcessRunning5(pid) {
|
|
25195
25376
|
try {
|
|
25196
25377
|
process.kill(pid, 0);
|
|
25197
25378
|
return true;
|
|
@@ -38712,7 +38893,7 @@ async function asToolResult(action) {
|
|
|
38712
38893
|
text: "Outbound budget exhausted; the action has been recorded as pending and will retry automatically after the next user inbound resets the quota window. No further action required."
|
|
38713
38894
|
}
|
|
38714
38895
|
],
|
|
38715
|
-
structuredContent: { status: "deferred_quota"
|
|
38896
|
+
structuredContent: { status: "deferred_quota" },
|
|
38716
38897
|
isError: false
|
|
38717
38898
|
};
|
|
38718
38899
|
}
|
|
@@ -38923,7 +39104,9 @@ function renderCoordinatorReviewContestedResultSuccess(task, decision) {
|
|
|
38923
39104
|
}
|
|
38924
39105
|
function formatToolError(error2) {
|
|
38925
39106
|
const message = error2 instanceof Error ? error2.message : String(error2);
|
|
38926
|
-
|
|
39107
|
+
const code = typeof error2 === "object" && error2 !== null && "code" in error2 ? error2.code : undefined;
|
|
39108
|
+
const isConnectionError = code === "ECONNREFUSED" || code === "ENOENT" || code === "ECONNRESET" || code === "EPIPE" || /server closed without a response|socket hang up/i.test(message);
|
|
39109
|
+
if (isConnectionError) {
|
|
38927
39110
|
return `Failed to connect to the orchestration daemon: ${message}`;
|
|
38928
39111
|
}
|
|
38929
39112
|
return message;
|
|
@@ -39084,9 +39267,9 @@ function createOrchestrationTransport(endpoint, deps = {}) {
|
|
|
39084
39267
|
sourceHandle: input.sourceHandle ?? input.coordinatorSession,
|
|
39085
39268
|
targetAgent: input.targetAgent,
|
|
39086
39269
|
task: input.task,
|
|
39087
|
-
...input.workingDirectory ? { cwd: input.workingDirectory } : {},
|
|
39088
|
-
...input.role ? { role: input.role } : {},
|
|
39089
|
-
...input.groupId ? { groupId: input.groupId } : {}
|
|
39270
|
+
...input.workingDirectory !== undefined ? { cwd: input.workingDirectory } : {},
|
|
39271
|
+
...input.role !== undefined ? { role: input.role } : {},
|
|
39272
|
+
...input.groupId !== undefined ? { groupId: input.groupId } : {}
|
|
39090
39273
|
}),
|
|
39091
39274
|
createGroup: async (input) => await client.createGroup(input),
|
|
39092
39275
|
getGroup: async (input) => await client.getGroup(input),
|
|
@@ -39095,10 +39278,10 @@ function createOrchestrationTransport(endpoint, deps = {}) {
|
|
|
39095
39278
|
getTask: async (input) => await client.getTaskForCoordinator(input),
|
|
39096
39279
|
listTasks: async (input) => await client.listTasks({
|
|
39097
39280
|
coordinatorSession: input.coordinatorSession,
|
|
39098
|
-
...input.status ? { status: input.status } : {},
|
|
39281
|
+
...input.status !== undefined ? { status: input.status } : {},
|
|
39099
39282
|
...input.stuck !== undefined ? { stuck: input.stuck } : {},
|
|
39100
|
-
...input.sort ? { sort: input.sort } : {},
|
|
39101
|
-
...input.order ? { order: input.order } : {}
|
|
39283
|
+
...input.sort !== undefined ? { sort: input.sort } : {},
|
|
39284
|
+
...input.order !== undefined ? { order: input.order } : {}
|
|
39102
39285
|
}),
|
|
39103
39286
|
approveTask: async (input) => await client.approveTask(input),
|
|
39104
39287
|
rejectTask: async (input) => await client.rejectTask(input),
|
|
@@ -39205,6 +39388,84 @@ async function resolveMcpIdentity(server, options) {
|
|
|
39205
39388
|
}
|
|
39206
39389
|
throw new McpError(ErrorCode.InvalidRequest, "weacpx MCP identity is not configured; run through `weacpx mcp-stdio` or provide --coordinator-session");
|
|
39207
39390
|
}
|
|
39391
|
+
function installMcpStdioShutdownHooks(options) {
|
|
39392
|
+
const platform = options.platform ?? process.platform;
|
|
39393
|
+
const signalSource = options.signalSource ?? process;
|
|
39394
|
+
const isProcessRunning = options.isProcessRunning ?? defaultIsProcessRunning3;
|
|
39395
|
+
const setIntervalFn = options.setIntervalFn ?? ((callback, ms) => setInterval(callback, ms));
|
|
39396
|
+
const clearIntervalFn = options.clearIntervalFn ?? ((handle) => clearInterval(handle));
|
|
39397
|
+
const parentPid = options.parentPid ?? process.ppid;
|
|
39398
|
+
const parentCheckIntervalMs = options.parentCheckIntervalMs ?? parseParentCheckIntervalMs(process.env.WEACPX_MCP_PARENT_CHECK_INTERVAL_MS);
|
|
39399
|
+
let disposed = false;
|
|
39400
|
+
let triggered = false;
|
|
39401
|
+
const triggerShutdown = (reason, context) => {
|
|
39402
|
+
if (disposed || triggered)
|
|
39403
|
+
return;
|
|
39404
|
+
triggered = true;
|
|
39405
|
+
options.onDiagnostic?.("mcp.stdio.shutdown", { reason, ...context ?? {} });
|
|
39406
|
+
options.shutdown();
|
|
39407
|
+
};
|
|
39408
|
+
const onStreamEnd = () => triggerShutdown("stdin.end");
|
|
39409
|
+
const onStreamClose = () => triggerShutdown("stdin.close");
|
|
39410
|
+
const onStdinError = (error2) => triggerShutdown("stdin.error", errorContext(error2));
|
|
39411
|
+
const onStdoutError = (error2) => triggerShutdown("stdout.error", errorContext(error2));
|
|
39412
|
+
const onSignal = (signal) => triggerShutdown("signal", { signal });
|
|
39413
|
+
options.stdin.on("end", onStreamEnd);
|
|
39414
|
+
options.stdin.on("close", onStreamClose);
|
|
39415
|
+
options.stdin.on("error", onStdinError);
|
|
39416
|
+
options.stdout.on("error", onStdoutError);
|
|
39417
|
+
const signals = platform === "win32" ? ["SIGINT", "SIGTERM", "SIGBREAK"] : ["SIGINT", "SIGTERM", "SIGHUP"];
|
|
39418
|
+
const signalListeners = signals.map((signal) => ({ signal, listener: () => onSignal(signal) }));
|
|
39419
|
+
for (const { signal, listener } of signalListeners) {
|
|
39420
|
+
signalSource.on(signal, listener);
|
|
39421
|
+
}
|
|
39422
|
+
let parentTimer;
|
|
39423
|
+
if (parentPid > 1 && parentCheckIntervalMs > 0) {
|
|
39424
|
+
parentTimer = setIntervalFn(() => {
|
|
39425
|
+
if (!isProcessRunning(parentPid)) {
|
|
39426
|
+
triggerShutdown("parent_dead", { parentPid });
|
|
39427
|
+
}
|
|
39428
|
+
}, parentCheckIntervalMs);
|
|
39429
|
+
parentTimer.unref?.();
|
|
39430
|
+
}
|
|
39431
|
+
return () => {
|
|
39432
|
+
if (disposed)
|
|
39433
|
+
return;
|
|
39434
|
+
disposed = true;
|
|
39435
|
+
options.stdin.off("end", onStreamEnd);
|
|
39436
|
+
options.stdin.off("close", onStreamClose);
|
|
39437
|
+
options.stdin.off("error", onStdinError);
|
|
39438
|
+
options.stdout.off("error", onStdoutError);
|
|
39439
|
+
for (const { signal, listener } of signalListeners) {
|
|
39440
|
+
signalSource.off(signal, listener);
|
|
39441
|
+
}
|
|
39442
|
+
if (parentTimer) {
|
|
39443
|
+
clearIntervalFn(parentTimer);
|
|
39444
|
+
}
|
|
39445
|
+
};
|
|
39446
|
+
}
|
|
39447
|
+
function parseParentCheckIntervalMs(raw) {
|
|
39448
|
+
if (raw === undefined || raw.trim().length === 0)
|
|
39449
|
+
return 5000;
|
|
39450
|
+
const parsed = Number(raw);
|
|
39451
|
+
return Number.isFinite(parsed) && parsed >= 0 ? parsed : 5000;
|
|
39452
|
+
}
|
|
39453
|
+
function errorContext(error2) {
|
|
39454
|
+
const record3 = error2;
|
|
39455
|
+
return {
|
|
39456
|
+
...typeof record3?.code === "string" ? { code: record3.code } : {},
|
|
39457
|
+
...typeof record3?.message === "string" ? { message: record3.message } : {}
|
|
39458
|
+
};
|
|
39459
|
+
}
|
|
39460
|
+
function defaultIsProcessRunning3(pid) {
|
|
39461
|
+
try {
|
|
39462
|
+
process.kill(pid, 0);
|
|
39463
|
+
return true;
|
|
39464
|
+
} catch (error2) {
|
|
39465
|
+
const code = error2?.code;
|
|
39466
|
+
return code !== "ESRCH";
|
|
39467
|
+
}
|
|
39468
|
+
}
|
|
39208
39469
|
async function runWeacpxMcpServer(options) {
|
|
39209
39470
|
const transport = options.transport ?? createOrchestrationTransport(options.endpoint ?? resolveDefaultOrchestrationEndpoint(process.env, process.platform));
|
|
39210
39471
|
const server = createWeacpxMcpServer({
|
|
@@ -39215,11 +39476,14 @@ async function runWeacpxMcpServer(options) {
|
|
|
39215
39476
|
...options.availableAgents ? { availableAgents: options.availableAgents } : {}
|
|
39216
39477
|
});
|
|
39217
39478
|
const stdio = new StdioServerTransport(stdin, stdout);
|
|
39479
|
+
let cleanupShutdownHooks;
|
|
39218
39480
|
let shuttingDown = false;
|
|
39219
39481
|
const shutdown = async () => {
|
|
39220
39482
|
if (shuttingDown)
|
|
39221
39483
|
return;
|
|
39222
39484
|
shuttingDown = true;
|
|
39485
|
+
cleanupShutdownHooks?.();
|
|
39486
|
+
options.onDiagnostic?.("mcp.stdio.stopping");
|
|
39223
39487
|
const forceExit = setTimeout(() => process.exit(0), 3000);
|
|
39224
39488
|
forceExit.unref();
|
|
39225
39489
|
try {
|
|
@@ -39227,11 +39491,16 @@ async function runWeacpxMcpServer(options) {
|
|
|
39227
39491
|
await stdio.close();
|
|
39228
39492
|
} catch {}
|
|
39229
39493
|
clearTimeout(forceExit);
|
|
39494
|
+
options.onDiagnostic?.("mcp.stdio.stopped");
|
|
39230
39495
|
process.exit(0);
|
|
39231
39496
|
};
|
|
39232
|
-
|
|
39233
|
-
|
|
39234
|
-
|
|
39497
|
+
options.onDiagnostic?.("mcp.stdio.start", { parentPid: process.ppid, platform: process.platform });
|
|
39498
|
+
cleanupShutdownHooks = installMcpStdioShutdownHooks({
|
|
39499
|
+
stdin,
|
|
39500
|
+
stdout,
|
|
39501
|
+
shutdown,
|
|
39502
|
+
onDiagnostic: options.onDiagnostic
|
|
39503
|
+
});
|
|
39235
39504
|
await server.connect(stdio);
|
|
39236
39505
|
}
|
|
39237
39506
|
function normalizeInputSchemaJson(schema) {
|
|
@@ -39257,18 +39526,18 @@ function sanitizeMcpClientName(input) {
|
|
|
39257
39526
|
return normalized.length > 0 ? normalized : "mcp-host";
|
|
39258
39527
|
}
|
|
39259
39528
|
|
|
39260
|
-
// src/mcp/parse-
|
|
39261
|
-
function
|
|
39529
|
+
// src/mcp/parse-string-flag.ts
|
|
39530
|
+
function parseStringFlag(args, env, options) {
|
|
39262
39531
|
let fromFlag = null;
|
|
39263
39532
|
for (let index = 0;index < args.length; index += 1) {
|
|
39264
|
-
if (args[index] ===
|
|
39533
|
+
if (args[index] === options.flag) {
|
|
39265
39534
|
const value = args[index + 1];
|
|
39266
39535
|
if (value === undefined) {
|
|
39267
|
-
throw new Error(
|
|
39536
|
+
throw new Error(`${options.flag} requires a non-empty value`);
|
|
39268
39537
|
}
|
|
39269
39538
|
const trimmedValue = value.trim();
|
|
39270
39539
|
if (trimmedValue.length === 0 || trimmedValue.startsWith("-")) {
|
|
39271
|
-
throw new Error(
|
|
39540
|
+
throw new Error(`${options.flag} requires a non-empty value`);
|
|
39272
39541
|
}
|
|
39273
39542
|
fromFlag = value;
|
|
39274
39543
|
}
|
|
@@ -39277,56 +39546,32 @@ function parseCoordinatorWorkspace(args, env = process.env) {
|
|
|
39277
39546
|
if (trimmedFlag && trimmedFlag.length > 0) {
|
|
39278
39547
|
return trimmedFlag;
|
|
39279
39548
|
}
|
|
39280
|
-
const trimmedEnv = env.
|
|
39549
|
+
const trimmedEnv = env[options.envKey]?.trim();
|
|
39281
39550
|
return trimmedEnv && trimmedEnv.length > 0 ? trimmedEnv : null;
|
|
39282
39551
|
}
|
|
39283
39552
|
|
|
39553
|
+
// src/mcp/parse-coordinator-workspace.ts
|
|
39554
|
+
function parseCoordinatorWorkspace(args, env = process.env) {
|
|
39555
|
+
return parseStringFlag(args, env, {
|
|
39556
|
+
flag: "--workspace",
|
|
39557
|
+
envKey: "WEACPX_COORDINATOR_WORKSPACE"
|
|
39558
|
+
});
|
|
39559
|
+
}
|
|
39560
|
+
|
|
39284
39561
|
// src/mcp/parse-coordinator-session.ts
|
|
39285
39562
|
function parseCoordinatorSession(args, env = process.env) {
|
|
39286
|
-
|
|
39287
|
-
|
|
39288
|
-
|
|
39289
|
-
|
|
39290
|
-
if (value === undefined) {
|
|
39291
|
-
throw new Error("--coordinator-session requires a non-empty value");
|
|
39292
|
-
}
|
|
39293
|
-
const trimmedValue = value.trim();
|
|
39294
|
-
if (trimmedValue.length === 0 || trimmedValue.startsWith("-")) {
|
|
39295
|
-
throw new Error("--coordinator-session requires a non-empty value");
|
|
39296
|
-
}
|
|
39297
|
-
fromFlag = value;
|
|
39298
|
-
}
|
|
39299
|
-
}
|
|
39300
|
-
const trimmedFlag = fromFlag?.trim();
|
|
39301
|
-
if (trimmedFlag && trimmedFlag.length > 0) {
|
|
39302
|
-
return trimmedFlag;
|
|
39303
|
-
}
|
|
39304
|
-
const trimmedEnv = env.WEACPX_COORDINATOR_SESSION?.trim();
|
|
39305
|
-
return trimmedEnv && trimmedEnv.length > 0 ? trimmedEnv : null;
|
|
39563
|
+
return parseStringFlag(args, env, {
|
|
39564
|
+
flag: "--coordinator-session",
|
|
39565
|
+
envKey: "WEACPX_COORDINATOR_SESSION"
|
|
39566
|
+
});
|
|
39306
39567
|
}
|
|
39307
39568
|
|
|
39308
39569
|
// src/mcp/parse-source-handle.ts
|
|
39309
39570
|
function parseSourceHandle(args, env = process.env) {
|
|
39310
|
-
|
|
39311
|
-
|
|
39312
|
-
|
|
39313
|
-
|
|
39314
|
-
if (value === undefined) {
|
|
39315
|
-
throw new Error("--source-handle requires a non-empty value");
|
|
39316
|
-
}
|
|
39317
|
-
const trimmedValue = value.trim();
|
|
39318
|
-
if (trimmedValue.length === 0 || trimmedValue.startsWith("-")) {
|
|
39319
|
-
throw new Error("--source-handle requires a non-empty value");
|
|
39320
|
-
}
|
|
39321
|
-
fromFlag = value;
|
|
39322
|
-
}
|
|
39323
|
-
}
|
|
39324
|
-
const trimmedFlag = fromFlag?.trim();
|
|
39325
|
-
if (trimmedFlag && trimmedFlag.length > 0) {
|
|
39326
|
-
return trimmedFlag;
|
|
39327
|
-
}
|
|
39328
|
-
const trimmedEnv = env.WEACPX_SOURCE_HANDLE?.trim();
|
|
39329
|
-
return trimmedEnv && trimmedEnv.length > 0 ? trimmedEnv : null;
|
|
39571
|
+
return parseStringFlag(args, env, {
|
|
39572
|
+
flag: "--source-handle",
|
|
39573
|
+
envKey: "WEACPX_SOURCE_HANDLE"
|
|
39574
|
+
});
|
|
39330
39575
|
}
|
|
39331
39576
|
|
|
39332
39577
|
// src/cli.ts
|
|
@@ -41023,7 +41268,12 @@ async function defaultMcpStdio(args, deps = {}) {
|
|
|
41023
41268
|
...coordinatorSession ? { coordinatorSession } : {},
|
|
41024
41269
|
...sourceHandle ? { sourceHandle } : {},
|
|
41025
41270
|
...identityResolver ? { resolveIdentity: identityResolver } : {},
|
|
41026
|
-
...availableAgents ? { availableAgents } : {}
|
|
41271
|
+
...availableAgents ? { availableAgents } : {},
|
|
41272
|
+
onDiagnostic: (event, context) => {
|
|
41273
|
+
const suffix = context && Object.keys(context).length > 0 ? ` ${JSON.stringify(context)}` : "";
|
|
41274
|
+
(deps.stderr ?? ((text) => process.stderr.write(text)))(`[weacpx:mcp] ${event}${suffix}
|
|
41275
|
+
`);
|
|
41276
|
+
}
|
|
41027
41277
|
});
|
|
41028
41278
|
return 0;
|
|
41029
41279
|
}
|