@fangyb/ahchat-bridge 0.1.35 → 0.1.37
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/cli.cjs +968 -184
- package/dist/feedbackWorkerCli.cjs +165 -8
- package/dist/index.js +963 -184
- package/dist/seedanceMcpCli.cjs +53 -7
- package/dist/seedreamMcpCli.cjs +53 -7
- package/package.json +3 -2
package/dist/index.js
CHANGED
|
@@ -3635,6 +3635,8 @@ var DEFAULT_QUERY_CONFIG = {
|
|
|
3635
3635
|
maxActive: 5040,
|
|
3636
3636
|
idleTimeoutMs: 6e5,
|
|
3637
3637
|
workingSilenceTimeoutMs: 12e5,
|
|
3638
|
+
replyStallTimeoutMs: 3e5,
|
|
3639
|
+
busySilenceTimeoutMs: 18e5,
|
|
3638
3640
|
evictionIntervalMs: 6e4,
|
|
3639
3641
|
statusReportIntervalMs: 6e4,
|
|
3640
3642
|
allowBuiltinWebSearch: false,
|
|
@@ -3692,6 +3694,14 @@ function mergeQueryConfig(file2) {
|
|
|
3692
3694
|
"AHCHAT_BRIDGE_WORKING_SILENCE_TIMEOUT_MS",
|
|
3693
3695
|
q?.workingSilenceTimeoutMs ?? DEFAULT_QUERY_CONFIG.workingSilenceTimeoutMs
|
|
3694
3696
|
),
|
|
3697
|
+
replyStallTimeoutMs: readEnvInt(
|
|
3698
|
+
"AHCHAT_BRIDGE_REPLY_STALL_TIMEOUT_MS",
|
|
3699
|
+
q?.replyStallTimeoutMs ?? DEFAULT_QUERY_CONFIG.replyStallTimeoutMs
|
|
3700
|
+
),
|
|
3701
|
+
busySilenceTimeoutMs: readEnvInt(
|
|
3702
|
+
"AHCHAT_BRIDGE_BUSY_SILENCE_TIMEOUT_MS",
|
|
3703
|
+
q?.busySilenceTimeoutMs ?? DEFAULT_QUERY_CONFIG.busySilenceTimeoutMs ?? 18e5
|
|
3704
|
+
),
|
|
3695
3705
|
evictionIntervalMs: readEnvInt(
|
|
3696
3706
|
"AHCHAT_BRIDGE_EVICTION_INTERVAL_MS",
|
|
3697
3707
|
q?.evictionIntervalMs ?? DEFAULT_QUERY_CONFIG.evictionIntervalMs
|
|
@@ -3772,7 +3782,11 @@ function loadBridgeConfig(opts) {
|
|
|
3772
3782
|
) || null,
|
|
3773
3783
|
logUploadIntervalMs: readEnvInt(
|
|
3774
3784
|
"AHCHAT_LOG_UPLOAD_INTERVAL_MS",
|
|
3775
|
-
|
|
3785
|
+
// Flush every 60s instead of once a day. Daily flushing let logs pile up for hours,
|
|
3786
|
+
// then dumped tens of thousands of entries in one cycle on the next process start,
|
|
3787
|
+
// blowing past the server's per-minute upload quota (3000 entries / 3MB) and getting
|
|
3788
|
+
// 429'd. Small frequent batches stay well under that ceiling.
|
|
3789
|
+
fileConfig.logUploadIntervalMs ?? 6e4
|
|
3776
3790
|
),
|
|
3777
3791
|
queryConfig: mergeQueryConfig(fileConfig)
|
|
3778
3792
|
};
|
|
@@ -4485,8 +4499,32 @@ ${entry.error.stack}` : ""}`
|
|
|
4485
4499
|
return `${ts} ${level} ${scope} ${entry.msg}${data}${trace}${errPart}`;
|
|
4486
4500
|
};
|
|
4487
4501
|
|
|
4502
|
+
// ../logger/src/fallback.ts
|
|
4503
|
+
function logFallback(logger43, event) {
|
|
4504
|
+
const payload = {
|
|
4505
|
+
...event.traceId ? { traceId: event.traceId } : {},
|
|
4506
|
+
fallback: {
|
|
4507
|
+
fallbackId: event.fallbackId,
|
|
4508
|
+
type: event.type,
|
|
4509
|
+
phase: event.phase,
|
|
4510
|
+
expected: event.expected,
|
|
4511
|
+
...event.context ? { context: event.context } : {},
|
|
4512
|
+
...event.outcome ? { outcome: event.outcome } : {}
|
|
4513
|
+
}
|
|
4514
|
+
};
|
|
4515
|
+
const msg = `[FALLBACK] ${event.type}:${event.phase}`;
|
|
4516
|
+
const useDebug = event.expected && event.phase !== "outcome";
|
|
4517
|
+
if (useDebug) {
|
|
4518
|
+
logger43.debug(msg, payload);
|
|
4519
|
+
} else {
|
|
4520
|
+
logger43.warn(msg, payload);
|
|
4521
|
+
}
|
|
4522
|
+
}
|
|
4523
|
+
|
|
4488
4524
|
// ../logger/src/transports/console.ts
|
|
4489
4525
|
var protectedStreams = /* @__PURE__ */ new WeakSet();
|
|
4526
|
+
function ignoreWriteError(_error) {
|
|
4527
|
+
}
|
|
4490
4528
|
function defaultStream(kind) {
|
|
4491
4529
|
const maybeGlobal = globalThis;
|
|
4492
4530
|
return maybeGlobal.process?.[kind];
|
|
@@ -4499,12 +4537,13 @@ function safeWriteLine(stream, line, fallback) {
|
|
|
4499
4537
|
if (stream.destroyed || stream.writableEnded) return;
|
|
4500
4538
|
if (typeof stream === "object" && typeof stream.on === "function" && !protectedStreams.has(stream)) {
|
|
4501
4539
|
protectedStreams.add(stream);
|
|
4502
|
-
stream.on("error",
|
|
4540
|
+
stream.on("error", ignoreWriteError);
|
|
4503
4541
|
}
|
|
4504
4542
|
try {
|
|
4505
4543
|
stream.write(`${line}
|
|
4506
|
-
`,
|
|
4507
|
-
} catch {
|
|
4544
|
+
`, ignoreWriteError);
|
|
4545
|
+
} catch (e) {
|
|
4546
|
+
ignoreWriteError(e);
|
|
4508
4547
|
}
|
|
4509
4548
|
}
|
|
4510
4549
|
function consoleTransport(opts) {
|
|
@@ -5137,8 +5176,31 @@ function parseSize(maxSize) {
|
|
|
5137
5176
|
return trimmed;
|
|
5138
5177
|
}
|
|
5139
5178
|
var streamCache = /* @__PURE__ */ new Map();
|
|
5179
|
+
var droppedEntryCount = 0;
|
|
5180
|
+
var lastReportedDroppedTotal = 0;
|
|
5181
|
+
function buildLogDroppedSentinel(fmt, source, droppedTotal) {
|
|
5182
|
+
const entry = {
|
|
5183
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5184
|
+
level: "WARN",
|
|
5185
|
+
source,
|
|
5186
|
+
module: "logger.file",
|
|
5187
|
+
msg: "log_dropped",
|
|
5188
|
+
data: { droppedTotal }
|
|
5189
|
+
};
|
|
5190
|
+
return fmt(entry);
|
|
5191
|
+
}
|
|
5192
|
+
function writeWithDroppedSentinel(stream, fmt, line, source) {
|
|
5193
|
+
stream.write(`${line}
|
|
5194
|
+
`);
|
|
5195
|
+
if (droppedEntryCount > lastReportedDroppedTotal) {
|
|
5196
|
+
lastReportedDroppedTotal = droppedEntryCount;
|
|
5197
|
+
stream.write(`${buildLogDroppedSentinel(fmt, source, droppedEntryCount)}
|
|
5198
|
+
`);
|
|
5199
|
+
}
|
|
5200
|
+
}
|
|
5140
5201
|
function fileTransport(opts) {
|
|
5141
5202
|
const fmt = opts.formatter ?? jsonFormatter;
|
|
5203
|
+
const logSource = opts.source ?? "server";
|
|
5142
5204
|
const resolved = path2.resolve(opts.path);
|
|
5143
5205
|
let cached2 = streamCache.get(resolved);
|
|
5144
5206
|
if (!cached2) {
|
|
@@ -5153,11 +5215,14 @@ function fileTransport(opts) {
|
|
|
5153
5215
|
streamCache.set(resolved, cached2);
|
|
5154
5216
|
}
|
|
5155
5217
|
return (entry) => {
|
|
5156
|
-
if (cached2.closed || cached2.stream.destroyed || cached2.stream.writableEnded)
|
|
5218
|
+
if (cached2.closed || cached2.stream.destroyed || cached2.stream.writableEnded) {
|
|
5219
|
+
droppedEntryCount += 1;
|
|
5220
|
+
return;
|
|
5221
|
+
}
|
|
5157
5222
|
try {
|
|
5158
|
-
cached2.stream
|
|
5159
|
-
`);
|
|
5223
|
+
writeWithDroppedSentinel(cached2.stream, fmt, fmt(entry), logSource);
|
|
5160
5224
|
} catch {
|
|
5225
|
+
droppedEntryCount += 1;
|
|
5161
5226
|
}
|
|
5162
5227
|
};
|
|
5163
5228
|
}
|
|
@@ -5907,6 +5972,9 @@ function createMessageId() {
|
|
|
5907
5972
|
function createTraceId() {
|
|
5908
5973
|
return `tr_${Date.now().toString(36)}_${nanoid(6)}`;
|
|
5909
5974
|
}
|
|
5975
|
+
function createFallbackId() {
|
|
5976
|
+
return `flb_${Date.now().toString(36)}_${nanoid(6)}`;
|
|
5977
|
+
}
|
|
5910
5978
|
function createRequestId() {
|
|
5911
5979
|
return `req_${Date.now().toString(36)}_${nanoid(6)}`;
|
|
5912
5980
|
}
|
|
@@ -5916,6 +5984,12 @@ function createCronReplyMessageId() {
|
|
|
5916
5984
|
function createInboxFlushReplyMessageId() {
|
|
5917
5985
|
return `msg_inbox_${Date.now().toString(36)}_${nanoid(6)}`;
|
|
5918
5986
|
}
|
|
5987
|
+
function createNeuralSendReplyMessageId() {
|
|
5988
|
+
return `msg_nsend_${Date.now().toString(36)}_${nanoid(6)}`;
|
|
5989
|
+
}
|
|
5990
|
+
function createScopeNoticeReplyMessageId() {
|
|
5991
|
+
return `msg_scopenotice_${Date.now().toString(36)}_${nanoid(6)}`;
|
|
5992
|
+
}
|
|
5919
5993
|
function createCronTraceId() {
|
|
5920
5994
|
return `tr_cron_${Date.now().toString(36)}_${nanoid(6)}`;
|
|
5921
5995
|
}
|
|
@@ -5974,6 +6048,10 @@ function assertStringPayloadOneOf(type, payload, field, allowed) {
|
|
|
5974
6048
|
throw invalidWsMessage(type, field);
|
|
5975
6049
|
}
|
|
5976
6050
|
}
|
|
6051
|
+
function assertOptionalStringPayloadOneOf(type, payload, field, allowed) {
|
|
6052
|
+
if (payload[field] === void 0) return;
|
|
6053
|
+
assertStringPayloadOneOf(type, payload, field, allowed);
|
|
6054
|
+
}
|
|
5977
6055
|
function assertArrayPayloadField(type, payload, field) {
|
|
5978
6056
|
if (!Array.isArray(payload[field])) {
|
|
5979
6057
|
throw invalidWsMessage(type, field);
|
|
@@ -6054,6 +6132,7 @@ function validateWSMessageShape(msg) {
|
|
|
6054
6132
|
case "agent:error": {
|
|
6055
6133
|
assertPayloadRecord(type, payload);
|
|
6056
6134
|
validateRequiredStrings(type, payload, ["ackId", "agentId", "conversationId", "error", "traceId"]);
|
|
6135
|
+
assertOptionalStringPayloadOneOf(type, payload, "reason", ["user_quota_exceeded", "company_quota_exceeded"]);
|
|
6057
6136
|
return;
|
|
6058
6137
|
}
|
|
6059
6138
|
case "directory:register": {
|
|
@@ -7641,6 +7720,60 @@ var readpageScrapeTool = {
|
|
|
7641
7720
|
enabled: true,
|
|
7642
7721
|
permissionPolicy: "always_allow"
|
|
7643
7722
|
};
|
|
7723
|
+
var seedreamGenerateImageTool = {
|
|
7724
|
+
name: "generate_image",
|
|
7725
|
+
displayName: "Seedream \u751F\u56FE",
|
|
7726
|
+
description: "\u4F7F\u7528\u706B\u5C71\u65B9\u821F Seedream \u751F\u6210\u5355\u5F20\u56FE\u7247\uFF0C\u652F\u6301\u6587\u672C\u751F\u56FE\u4E0E\u53C2\u8003\u56FE\u751F\u56FE\uFF0C\u4F1A\u4EA7\u751F\u516C\u53F8 Ark \u8D26\u53F7\u7528\u91CF\u3002",
|
|
7727
|
+
category: "media",
|
|
7728
|
+
riskLevel: "medium",
|
|
7729
|
+
enabled: true,
|
|
7730
|
+
permissionPolicy: "always_ask"
|
|
7731
|
+
};
|
|
7732
|
+
var seedreamEditImageTool = {
|
|
7733
|
+
name: "edit_image",
|
|
7734
|
+
displayName: "Seedream \u6539\u56FE",
|
|
7735
|
+
description: "\u4F7F\u7528\u706B\u5C71\u65B9\u821F Seedream \u6839\u636E\u53C2\u8003\u56FE\u548C\u63D0\u793A\u8BCD\u7F16\u8F91\u6216\u91CD\u7ED8\u5355\u5F20\u56FE\u7247\uFF0C\u4F1A\u4EA7\u751F\u516C\u53F8 Ark \u8D26\u53F7\u7528\u91CF\u3002",
|
|
7736
|
+
category: "media",
|
|
7737
|
+
riskLevel: "medium",
|
|
7738
|
+
enabled: true,
|
|
7739
|
+
permissionPolicy: "always_ask"
|
|
7740
|
+
};
|
|
7741
|
+
var seedreamGenerateImageGroupTool = {
|
|
7742
|
+
name: "generate_image_group",
|
|
7743
|
+
displayName: "Seedream \u5355\u56FE",
|
|
7744
|
+
description: "\u517C\u5BB9\u65E7\u540D\u79F0\u7684 Seedream \u5355\u56FE\u751F\u6210\u5DE5\u5177\uFF0C\u4F1A\u4EA7\u751F\u516C\u53F8 Ark \u8D26\u53F7\u7528\u91CF\u3002\u5F53\u524D\u6BCF\u6B21\u8BF7\u6C42\u53EA\u751F\u6210 1 \u5F20\u56FE\u7247\u3002",
|
|
7745
|
+
category: "media",
|
|
7746
|
+
riskLevel: "medium",
|
|
7747
|
+
enabled: true,
|
|
7748
|
+
permissionPolicy: "always_ask"
|
|
7749
|
+
};
|
|
7750
|
+
var seedanceUsageGuideTool = {
|
|
7751
|
+
name: "seedance_usage_guide",
|
|
7752
|
+
displayName: "Seedance \u4F7F\u7528\u8BF4\u660E",
|
|
7753
|
+
description: "\u67E5\u770B Seedance \u89C6\u9891\u751F\u6210\u6D41\u7A0B\u4E0E\u53C2\u6570\u8BF4\u660E\uFF0C\u4E0D\u521B\u5EFA\u65B0\u7684\u751F\u6210\u4EFB\u52A1\u3002",
|
|
7754
|
+
category: "media",
|
|
7755
|
+
riskLevel: "low",
|
|
7756
|
+
enabled: true,
|
|
7757
|
+
permissionPolicy: "always_allow"
|
|
7758
|
+
};
|
|
7759
|
+
var seedanceCreateTaskTool = {
|
|
7760
|
+
name: "seedance_create_task",
|
|
7761
|
+
displayName: "Seedance \u751F\u89C6\u9891",
|
|
7762
|
+
description: "\u4F7F\u7528\u706B\u5C71\u65B9\u821F Seedance \u521B\u5EFA\u5355\u4E2A\u89C6\u9891\u751F\u6210\u4EFB\u52A1\uFF0C\u4F1A\u4EA7\u751F\u516C\u53F8 Ark \u8D26\u53F7\u7528\u91CF\u3002\u5F53\u524D\u6BCF\u6B21\u8BF7\u6C42\u53EA\u751F\u6210 1 \u4E2A\u89C6\u9891\u3002",
|
|
7763
|
+
category: "media",
|
|
7764
|
+
riskLevel: "high",
|
|
7765
|
+
enabled: true,
|
|
7766
|
+
permissionPolicy: "always_ask"
|
|
7767
|
+
};
|
|
7768
|
+
var seedanceCheckTaskTool = {
|
|
7769
|
+
name: "seedance_check_task",
|
|
7770
|
+
displayName: "Seedance \u67E5\u7ED3\u679C",
|
|
7771
|
+
description: "\u67E5\u8BE2 Seedance \u89C6\u9891\u751F\u6210\u4EFB\u52A1\u72B6\u6001\u4E0E\u89C6\u9891 URL\uFF0C\u4E0D\u521B\u5EFA\u65B0\u7684\u751F\u6210\u4EFB\u52A1\u3002",
|
|
7772
|
+
category: "media",
|
|
7773
|
+
riskLevel: "low",
|
|
7774
|
+
enabled: true,
|
|
7775
|
+
permissionPolicy: "always_allow"
|
|
7776
|
+
};
|
|
7644
7777
|
var context7ResolveLibraryTool = {
|
|
7645
7778
|
name: "resolve-library-id",
|
|
7646
7779
|
displayName: "\u89E3\u6790\u6587\u6863\u5E93",
|
|
@@ -7947,8 +8080,56 @@ var ALIYUN_IQS_MCP_PROVIDERS = [
|
|
|
7947
8080
|
tools: [readpageBasicTool, readpageScrapeTool]
|
|
7948
8081
|
}
|
|
7949
8082
|
];
|
|
8083
|
+
var VOLCENGINE_SEEDREAM_MCP_PROVIDER = {
|
|
8084
|
+
providerId: "volcengine_seedream",
|
|
8085
|
+
name: "Volcengine Seedream",
|
|
8086
|
+
summary: "\u706B\u5C71\u65B9\u821F Seedream \u56FE\u7247\u751F\u6210/\u7F16\u8F91 MCP\uFF0C\u4F7F\u7528\u516C\u53F8\u7EDF\u4E00 Ark Key\uFF0C\u5E76\u8FDB\u5165 AHChat \u8C03\u7528\u5BA1\u8BA1\u3002",
|
|
8087
|
+
serverName: "seedream",
|
|
8088
|
+
transport: "stdio",
|
|
8089
|
+
url: null,
|
|
8090
|
+
command: "ahchat-builtin",
|
|
8091
|
+
args: ["seedream-mcp"],
|
|
8092
|
+
env: {
|
|
8093
|
+
ARK_API_KEY: "",
|
|
8094
|
+
ARK_BASE_URL: "https://ark.cn-beijing.volces.com/api/v3"
|
|
8095
|
+
},
|
|
8096
|
+
authType: "x_api_key",
|
|
8097
|
+
customHeaders: [],
|
|
8098
|
+
enabled: true,
|
|
8099
|
+
alwaysLoad: true,
|
|
8100
|
+
tools: [
|
|
8101
|
+
seedreamGenerateImageTool,
|
|
8102
|
+
seedreamEditImageTool,
|
|
8103
|
+
seedreamGenerateImageGroupTool
|
|
8104
|
+
]
|
|
8105
|
+
};
|
|
8106
|
+
var VOLCENGINE_SEEDANCE_MCP_PROVIDER = {
|
|
8107
|
+
providerId: "volcengine_seedance",
|
|
8108
|
+
name: "Volcengine Seedance",
|
|
8109
|
+
summary: "\u706B\u5C71\u65B9\u821F Seedance \u89C6\u9891\u751F\u6210 MCP\uFF0C\u4F7F\u7528\u516C\u53F8\u7EDF\u4E00 Ark Key\uFF0C\u5E76\u8FDB\u5165 AHChat \u8C03\u7528\u5BA1\u8BA1\u3002",
|
|
8110
|
+
serverName: "seedance",
|
|
8111
|
+
transport: "stdio",
|
|
8112
|
+
url: null,
|
|
8113
|
+
command: "ahchat-builtin",
|
|
8114
|
+
args: ["seedance-mcp"],
|
|
8115
|
+
env: {
|
|
8116
|
+
ARK_API_KEY: "",
|
|
8117
|
+
ARK_BASE_URL: "https://ark.cn-beijing.volces.com/api/v3"
|
|
8118
|
+
},
|
|
8119
|
+
authType: "x_api_key",
|
|
8120
|
+
customHeaders: [],
|
|
8121
|
+
enabled: true,
|
|
8122
|
+
alwaysLoad: true,
|
|
8123
|
+
tools: [
|
|
8124
|
+
seedanceUsageGuideTool,
|
|
8125
|
+
seedanceCreateTaskTool,
|
|
8126
|
+
seedanceCheckTaskTool
|
|
8127
|
+
]
|
|
8128
|
+
};
|
|
7950
8129
|
var OFFICIAL_MCP_PROVIDERS = [
|
|
7951
|
-
...ALIYUN_IQS_MCP_PROVIDERS
|
|
8130
|
+
...ALIYUN_IQS_MCP_PROVIDERS,
|
|
8131
|
+
VOLCENGINE_SEEDREAM_MCP_PROVIDER,
|
|
8132
|
+
VOLCENGINE_SEEDANCE_MCP_PROVIDER
|
|
7952
8133
|
];
|
|
7953
8134
|
var MCP_STORE_PROVIDERS = [
|
|
7954
8135
|
{
|
|
@@ -8790,8 +8971,7 @@ var AskQuestionRegistry = class {
|
|
|
8790
8971
|
questionId,
|
|
8791
8972
|
agentId: entry.agentId,
|
|
8792
8973
|
waitedMs: Date.now() - entry.askedAt,
|
|
8793
|
-
answerLen: answerText2.length
|
|
8794
|
-
answerSample: answerText2.slice(0, 200)
|
|
8974
|
+
answerLen: answerText2.length
|
|
8795
8975
|
});
|
|
8796
8976
|
entry.resolve(answerText2);
|
|
8797
8977
|
return true;
|
|
@@ -8932,7 +9112,7 @@ function makeAskUserQuestionGuard(deps) {
|
|
|
8932
9112
|
bundleIndex,
|
|
8933
9113
|
bundleSize,
|
|
8934
9114
|
replyMessageId: task.replyMessageId,
|
|
8935
|
-
|
|
9115
|
+
questionLen: q.question.length,
|
|
8936
9116
|
optionCount: options.length,
|
|
8937
9117
|
multiSelect,
|
|
8938
9118
|
traceId: task.traceId
|
|
@@ -9031,7 +9211,7 @@ function makeAskUserQuestionGuard(deps) {
|
|
|
9031
9211
|
bundleId,
|
|
9032
9212
|
bundleSize,
|
|
9033
9213
|
replyMessageId: task.replyMessageId,
|
|
9034
|
-
|
|
9214
|
+
combinedLen: combined.length,
|
|
9035
9215
|
traceId: task.traceId
|
|
9036
9216
|
});
|
|
9037
9217
|
return { behavior: "deny", message: combined };
|
|
@@ -24054,6 +24234,17 @@ var TEXT_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
|
24054
24234
|
".yaml",
|
|
24055
24235
|
".yml"
|
|
24056
24236
|
]);
|
|
24237
|
+
var OFFICE_DOCUMENT_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
24238
|
+
".docx",
|
|
24239
|
+
".xlsx",
|
|
24240
|
+
".pptx",
|
|
24241
|
+
".pdf",
|
|
24242
|
+
".odt",
|
|
24243
|
+
".ods",
|
|
24244
|
+
".odp",
|
|
24245
|
+
".rtf"
|
|
24246
|
+
]);
|
|
24247
|
+
var PLAIN_TEXT_BINARY_SNIFF_BYTES = 8192;
|
|
24057
24248
|
var DEFAULT_MAX_CHARS = 5e5;
|
|
24058
24249
|
var DEFAULT_TIMEOUT_MS = 45e3;
|
|
24059
24250
|
function isReadableDocumentPath(filePath) {
|
|
@@ -24073,9 +24264,6 @@ function resolveDocumentPath(inputPath, cwd) {
|
|
|
24073
24264
|
async function readDocumentAsMarkdown(inputPath, opts = {}) {
|
|
24074
24265
|
const resolvedPath = opts.cwd ? resolveDocumentPath(inputPath, opts.cwd) : path9.resolve(resolveUserPath(inputPath));
|
|
24075
24266
|
const ext = path9.extname(resolvedPath).toLowerCase();
|
|
24076
|
-
if (!isReadableDocumentPath(resolvedPath)) {
|
|
24077
|
-
throw new Error(`unsupported document type: ${ext || "(no extension)"}`);
|
|
24078
|
-
}
|
|
24079
24267
|
const stat3 = await fs4.stat(resolvedPath);
|
|
24080
24268
|
if (!stat3.isFile()) throw new Error("path is not a file");
|
|
24081
24269
|
const warnings = [];
|
|
@@ -24084,8 +24272,10 @@ async function readDocumentAsMarkdown(inputPath, opts = {}) {
|
|
|
24084
24272
|
markdown = await fs4.readFile(resolvedPath, "utf-8");
|
|
24085
24273
|
} else if (ext === ".xls") {
|
|
24086
24274
|
markdown = await convertLegacyExcelDocument(resolvedPath);
|
|
24087
|
-
} else {
|
|
24275
|
+
} else if (OFFICE_DOCUMENT_EXTENSIONS.has(ext)) {
|
|
24088
24276
|
markdown = await convertOfficeDocument(resolvedPath, opts.timeoutMs ?? DEFAULT_TIMEOUT_MS);
|
|
24277
|
+
} else {
|
|
24278
|
+
markdown = await readPlainTextDocument(resolvedPath, ext);
|
|
24089
24279
|
}
|
|
24090
24280
|
markdown = normalizeDocumentText(markdown);
|
|
24091
24281
|
const maxChars = opts.maxChars ?? DEFAULT_MAX_CHARS;
|
|
@@ -24224,6 +24414,14 @@ async function convertDocxWithOfficeCli(filePath, timeoutMs) {
|
|
|
24224
24414
|
});
|
|
24225
24415
|
return text;
|
|
24226
24416
|
}
|
|
24417
|
+
async function readPlainTextDocument(filePath, ext) {
|
|
24418
|
+
const buffer = await fs4.readFile(filePath);
|
|
24419
|
+
const sample = buffer.subarray(0, Math.min(buffer.length, PLAIN_TEXT_BINARY_SNIFF_BYTES));
|
|
24420
|
+
if (sample.includes(0)) {
|
|
24421
|
+
throw new Error(`unsupported document type: ${ext || "(no extension)"}`);
|
|
24422
|
+
}
|
|
24423
|
+
return buffer.toString("utf-8");
|
|
24424
|
+
}
|
|
24227
24425
|
async function convertLegacyExcelDocument(filePath) {
|
|
24228
24426
|
const XLSX = await import("./xlsx-E4ZR5JHK.js");
|
|
24229
24427
|
const workbook = XLSX.readFile(filePath, { cellDates: true });
|
|
@@ -24693,8 +24891,7 @@ async function createNeuralMcpServer(deps) {
|
|
|
24693
24891
|
agentId: deps.agentId,
|
|
24694
24892
|
fromScope: currentScopeKey,
|
|
24695
24893
|
rawTargetScope: args.target_scope,
|
|
24696
|
-
messageLen: args.message.length
|
|
24697
|
-
messageSample: args.message.slice(0, 120)
|
|
24894
|
+
messageLen: args.message.length
|
|
24698
24895
|
});
|
|
24699
24896
|
const trimmed = args.message.trim();
|
|
24700
24897
|
if (!trimmed) {
|
|
@@ -24788,7 +24985,7 @@ async function createNeuralMcpServer(deps) {
|
|
|
24788
24985
|
toScope: resolvedKey,
|
|
24789
24986
|
repeatsInWindow: sendHistory.length,
|
|
24790
24987
|
windowMs: NEURAL_DEDUP_WINDOW_MS,
|
|
24791
|
-
|
|
24988
|
+
messageLen: trimmed.length
|
|
24792
24989
|
});
|
|
24793
24990
|
return {
|
|
24794
24991
|
content: [{
|
|
@@ -24815,8 +25012,7 @@ async function createNeuralMcpServer(deps) {
|
|
|
24815
25012
|
agentId: deps.agentId,
|
|
24816
25013
|
fromScope: currentScopeKey,
|
|
24817
25014
|
toScope: resolvedKey,
|
|
24818
|
-
messageLen: trimmed.length
|
|
24819
|
-
messageSample: trimmed.slice(0, 120)
|
|
25015
|
+
messageLen: trimmed.length
|
|
24820
25016
|
});
|
|
24821
25017
|
return {
|
|
24822
25018
|
content: [{ type: "text", text: `[neural_send] \u5DF2\u9001\u8FBE\u5230\u300C${toLabel}\u300D(scope: ${resolvedKey})\u3002` }]
|
|
@@ -25173,6 +25369,7 @@ action="append" \u8FFD\u52A0\u65B0\u5185\u5BB9\uFF08\u6700\u5E38\u7528\uFF0Ccont
|
|
|
25173
25369
|
`Read a document from the current working directory and return extracted Markdown text.
|
|
25174
25370
|
Use this instead of the built-in Read tool for binary documents such as .docx, .xls, .xlsx, .pptx, .pdf, .odt, .ods, .odp, or .rtf.
|
|
25175
25371
|
Also supports text-like files such as .csv, .md, .txt, .json, .xml, .yaml, and .html.
|
|
25372
|
+
Plain-text and source/config files (e.g. .py, .ts, Makefile, Dockerfile, and other extension-less text files) are read as-is; only true binaries are rejected.
|
|
25176
25373
|
Pass either a relative path from the current working directory or an absolute path inside it.`,
|
|
25177
25374
|
{
|
|
25178
25375
|
path: external_exports.string().min(1).describe("Document path, relative to the current working directory or absolute inside it."),
|
|
@@ -27890,9 +28087,12 @@ function isAuthFailureText(text, sdkError) {
|
|
|
27890
28087
|
function isProviderApiErrorText(text) {
|
|
27891
28088
|
return /^API Error:\s*\d+/i.test(text.trim());
|
|
27892
28089
|
}
|
|
27893
|
-
function
|
|
28090
|
+
function isSdkSyntheticNoResponseText(normalizedText) {
|
|
28091
|
+
return normalizedText.replace(/[.!。]+$/g, "") === "no response requested";
|
|
28092
|
+
}
|
|
28093
|
+
function isNoReplyText(text, opts) {
|
|
27894
28094
|
const normalized = text.trim().replace(/^`+|`+$/g, "").trim().toLowerCase();
|
|
27895
|
-
return normalized === NO_REPLY_TOKEN || normalized === "<no-reply>" || normalized === "no-reply" || normalized === "no_reply" || normalized === "no reply";
|
|
28095
|
+
return normalized === NO_REPLY_TOKEN || normalized === "<no-reply>" || normalized === "no-reply" || normalized === "no_reply" || normalized === "no reply" || opts?.allowSdkSyntheticNoResponse === true && isSdkSyntheticNoResponseText(normalized);
|
|
27896
28096
|
}
|
|
27897
28097
|
function decodeJsonStringFragment(raw) {
|
|
27898
28098
|
let out = "";
|
|
@@ -28230,6 +28430,9 @@ function emitUsageReported(proc, emit, base, usage, messageId) {
|
|
|
28230
28430
|
function isGroupTask(proc) {
|
|
28231
28431
|
return proc.currentTask?.groupId != null;
|
|
28232
28432
|
}
|
|
28433
|
+
function shouldStreamInternals(proc) {
|
|
28434
|
+
return !isGroupTask(proc) || proc.spectating === true;
|
|
28435
|
+
}
|
|
28233
28436
|
function extractTodosFromInput(input) {
|
|
28234
28437
|
if (!input || typeof input !== "object") return null;
|
|
28235
28438
|
const raw = input.todos;
|
|
@@ -28361,7 +28564,6 @@ function emitGroupSegment(proc, emit, base, content, contentBlocks, isSilent = f
|
|
|
28361
28564
|
contentLen: content.length,
|
|
28362
28565
|
blockCount: contentBlocks.length,
|
|
28363
28566
|
blockTypes: contentBlocks.map((b) => b.type),
|
|
28364
|
-
contentSample: content.slice(0, 200),
|
|
28365
28567
|
traceId: base.traceId,
|
|
28366
28568
|
isAuditOnly: content.length === 0,
|
|
28367
28569
|
isSilent
|
|
@@ -28380,7 +28582,7 @@ function emitGroupSegment(proc, emit, base, content, contentBlocks, isSilent = f
|
|
|
28380
28582
|
}
|
|
28381
28583
|
function flushTextSegmentOnBlockStop(proc, emit, base) {
|
|
28382
28584
|
const trimmed = proc.segmentBuffer.trim();
|
|
28383
|
-
const isSilent = isNoReplyText(trimmed);
|
|
28585
|
+
const isSilent = isNoReplyText(trimmed, { allowSdkSyntheticNoResponse: true });
|
|
28384
28586
|
const isEmpty = trimmed.length === 0;
|
|
28385
28587
|
if (!isEmpty) {
|
|
28386
28588
|
const blocksForSegment = [...proc.contentBlocks, { type: "text", content: proc.segmentBuffer }];
|
|
@@ -28417,9 +28619,24 @@ function flushTextSegmentOnBlockStop(proc, emit, base) {
|
|
|
28417
28619
|
}
|
|
28418
28620
|
proc.segmentBuffer = "";
|
|
28419
28621
|
}
|
|
28622
|
+
function describeSdkEvent(message) {
|
|
28623
|
+
const rec = message;
|
|
28624
|
+
const str = (v) => typeof v === "string" && v.length > 0 ? v : void 0;
|
|
28625
|
+
return {
|
|
28626
|
+
type: str(rec.type) ?? "unknown",
|
|
28627
|
+
subtype: str(rec.subtype),
|
|
28628
|
+
toolName: str(rec.last_tool_name),
|
|
28629
|
+
subagentType: str(rec.subagent_type),
|
|
28630
|
+
toolUseId: str(rec.tool_use_id),
|
|
28631
|
+
taskId: str(rec.task_id),
|
|
28632
|
+
at: Date.now()
|
|
28633
|
+
};
|
|
28634
|
+
}
|
|
28420
28635
|
function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onProviderApiError) {
|
|
28421
28636
|
const emit = rawEmit;
|
|
28422
28637
|
proc.lastSdkEventAt = Date.now();
|
|
28638
|
+
proc.lastSdkEventInfo = describeSdkEvent(message);
|
|
28639
|
+
proc.stallWarned = false;
|
|
28423
28640
|
switch (message.type) {
|
|
28424
28641
|
case "system": {
|
|
28425
28642
|
const sysMsg = message;
|
|
@@ -28458,11 +28675,29 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onProv
|
|
|
28458
28675
|
sessionId: proc.ccSessionId
|
|
28459
28676
|
});
|
|
28460
28677
|
} else {
|
|
28678
|
+
const sysRec = sysMsg;
|
|
28679
|
+
const pick2 = (k) => typeof sysRec[k] === "string" || typeof sysRec[k] === "number" ? sysRec[k] : void 0;
|
|
28680
|
+
const descriptionLen = typeof sysRec.description === "string" ? sysRec.description.length : void 0;
|
|
28681
|
+
const subagentTaskId = typeof sysRec.task_id === "string" ? sysRec.task_id : void 0;
|
|
28682
|
+
if (subagentTaskId) {
|
|
28683
|
+
if (sysMsg.subtype === "task_started") {
|
|
28684
|
+
(proc.activeSubagentTaskIds ??= /* @__PURE__ */ new Set()).add(subagentTaskId);
|
|
28685
|
+
} else if (sysMsg.subtype === "task_notification") {
|
|
28686
|
+
proc.activeSubagentTaskIds?.delete(subagentTaskId);
|
|
28687
|
+
}
|
|
28688
|
+
}
|
|
28461
28689
|
logger10.info("SDK system subtype unhandled", {
|
|
28462
28690
|
agentId: proc.agentId,
|
|
28463
28691
|
scope: proc.scope.kind === "single" ? "single" : proc.scope.groupId,
|
|
28464
28692
|
subtype: sysMsg.subtype ?? "(none)",
|
|
28465
|
-
|
|
28693
|
+
taskId: pick2("task_id"),
|
|
28694
|
+
toolUseId: pick2("tool_use_id"),
|
|
28695
|
+
subagentType: pick2("subagent_type"),
|
|
28696
|
+
taskType: pick2("task_type"),
|
|
28697
|
+
lastToolName: pick2("last_tool_name"),
|
|
28698
|
+
hasDescription: descriptionLen != null,
|
|
28699
|
+
descriptionLen,
|
|
28700
|
+
keys: Object.keys(sysMsg).slice(0, 16)
|
|
28466
28701
|
});
|
|
28467
28702
|
}
|
|
28468
28703
|
break;
|
|
@@ -28488,13 +28723,14 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onProv
|
|
|
28488
28723
|
} else if (block.type === "tool_use") {
|
|
28489
28724
|
proc.currentBlockType = "tool_use";
|
|
28490
28725
|
proc.currentToolName = block.name ?? "unknown";
|
|
28726
|
+
proc.activeToolUseStartedAt = Date.now();
|
|
28491
28727
|
proc.accumulatedToolInput = "";
|
|
28492
28728
|
const toolName = block.name ?? "unknown";
|
|
28493
28729
|
proc.suppressCurrentToolUse = proc.officialMediaGenerationSatisfied === true && isOfficialMediaGenerationToolName(toolName);
|
|
28494
28730
|
const isMcpTool = parseMcpRuntimeToolName(toolName) != null;
|
|
28495
28731
|
proc.currentMcpInvocationId = isMcpTool ? createMcpToolInvocationId() : null;
|
|
28496
28732
|
proc.currentMcpInvocationStartedAt = isMcpTool ? (/* @__PURE__ */ new Date()).toISOString() : null;
|
|
28497
|
-
if (
|
|
28733
|
+
if (shouldStreamInternals(proc) && !proc.suppressCurrentToolUse && toolName !== "ExitPlanMode" && !isAskUserQuestionToolName(toolName)) {
|
|
28498
28734
|
emit({
|
|
28499
28735
|
type: "agent:tool_use",
|
|
28500
28736
|
payload: {
|
|
@@ -28519,7 +28755,7 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onProv
|
|
|
28519
28755
|
if (delta.type === "thinking_delta" && typeof delta.thinking === "string") {
|
|
28520
28756
|
if (proc.suppressCurrentThinking) break;
|
|
28521
28757
|
proc.accumulatedThinking += delta.thinking;
|
|
28522
|
-
if (
|
|
28758
|
+
if (shouldStreamInternals(proc)) {
|
|
28523
28759
|
emit({
|
|
28524
28760
|
type: "agent:thinking_chunk",
|
|
28525
28761
|
payload: { ...wireBase(base), chunk: delta.thinking }
|
|
@@ -28530,7 +28766,7 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onProv
|
|
|
28530
28766
|
if (typeof partial2 === "string") {
|
|
28531
28767
|
proc.accumulatedToolInput += partial2;
|
|
28532
28768
|
const liveInput = extractLiveToolInput(proc.currentToolName, proc.accumulatedToolInput);
|
|
28533
|
-
if (
|
|
28769
|
+
if (shouldStreamInternals(proc) && liveInput && proc.currentToolName != null) {
|
|
28534
28770
|
emit({
|
|
28535
28771
|
type: "agent:tool_input_update",
|
|
28536
28772
|
payload: {
|
|
@@ -28564,7 +28800,7 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onProv
|
|
|
28564
28800
|
}
|
|
28565
28801
|
case "content_block_stop": {
|
|
28566
28802
|
if (proc.currentBlockType === "thinking") {
|
|
28567
|
-
if (
|
|
28803
|
+
if (shouldStreamInternals(proc) && !proc.suppressCurrentThinking) {
|
|
28568
28804
|
emit({
|
|
28569
28805
|
type: "agent:thinking_done",
|
|
28570
28806
|
payload: wireBase(getTaskBase(proc))
|
|
@@ -28589,8 +28825,7 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onProv
|
|
|
28589
28825
|
error: error51,
|
|
28590
28826
|
agentId: proc.agentId,
|
|
28591
28827
|
toolName: proc.currentToolName,
|
|
28592
|
-
inputLen: proc.accumulatedToolInput.length
|
|
28593
|
-
sample: proc.accumulatedToolInput.slice(0, 200)
|
|
28828
|
+
inputLen: proc.accumulatedToolInput.length
|
|
28594
28829
|
});
|
|
28595
28830
|
}
|
|
28596
28831
|
}
|
|
@@ -28599,7 +28834,7 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onProv
|
|
|
28599
28834
|
if (lastToolUse && lastToolUse.type === "tool_use") {
|
|
28600
28835
|
lastToolUse.input = parsedInput;
|
|
28601
28836
|
}
|
|
28602
|
-
if (
|
|
28837
|
+
if (shouldStreamInternals(proc) && proc.currentToolName != null && LIVE_INPUT_PREVIEW_TOOLS.has(proc.currentToolName) && Object.keys(parsedInput).length > 0) {
|
|
28603
28838
|
emit({
|
|
28604
28839
|
type: "agent:tool_input_update",
|
|
28605
28840
|
payload: {
|
|
@@ -28723,7 +28958,7 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onProv
|
|
|
28723
28958
|
blockTypes,
|
|
28724
28959
|
hasToolResult,
|
|
28725
28960
|
hasPlainText,
|
|
28726
|
-
|
|
28961
|
+
contentLen: typeof content === "string" ? content.length : JSON.stringify(content).length
|
|
28727
28962
|
});
|
|
28728
28963
|
break;
|
|
28729
28964
|
}
|
|
@@ -28732,7 +28967,7 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onProv
|
|
|
28732
28967
|
agentId: proc.agentId,
|
|
28733
28968
|
scope: proc.scope.kind === "single" ? "single" : proc.scope.groupId,
|
|
28734
28969
|
blockTypes,
|
|
28735
|
-
|
|
28970
|
+
contentLen: JSON.stringify(content).length,
|
|
28736
28971
|
replyMessageId: base.replyMessageId
|
|
28737
28972
|
});
|
|
28738
28973
|
}
|
|
@@ -28753,6 +28988,7 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onProv
|
|
|
28753
28988
|
});
|
|
28754
28989
|
proc.currentMcpInvocationId = null;
|
|
28755
28990
|
proc.currentMcpInvocationStartedAt = null;
|
|
28991
|
+
proc.activeToolUseStartedAt = void 0;
|
|
28756
28992
|
proc.currentToolName = null;
|
|
28757
28993
|
continue;
|
|
28758
28994
|
}
|
|
@@ -28760,6 +28996,7 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onProv
|
|
|
28760
28996
|
proc.officialMediaGenerationSatisfied = true;
|
|
28761
28997
|
}
|
|
28762
28998
|
if (isAskUserQuestionToolName(toolName)) {
|
|
28999
|
+
proc.activeToolUseStartedAt = void 0;
|
|
28763
29000
|
proc.currentToolName = null;
|
|
28764
29001
|
continue;
|
|
28765
29002
|
}
|
|
@@ -28787,7 +29024,7 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onProv
|
|
|
28787
29024
|
proc.currentMcpInvocationId = null;
|
|
28788
29025
|
proc.currentMcpInvocationStartedAt = null;
|
|
28789
29026
|
}
|
|
28790
|
-
if (
|
|
29027
|
+
if (shouldStreamInternals(proc)) {
|
|
28791
29028
|
emit({
|
|
28792
29029
|
type: "agent:tool_result",
|
|
28793
29030
|
payload: {
|
|
@@ -28811,6 +29048,7 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onProv
|
|
|
28811
29048
|
}
|
|
28812
29049
|
}
|
|
28813
29050
|
}
|
|
29051
|
+
proc.activeToolUseStartedAt = void 0;
|
|
28814
29052
|
}
|
|
28815
29053
|
}
|
|
28816
29054
|
}
|
|
@@ -28867,7 +29105,7 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onProv
|
|
|
28867
29105
|
const groupMode = groupId != null;
|
|
28868
29106
|
const usage = extractUsage(successMsg);
|
|
28869
29107
|
const watermarkUsage = proc.peakContextUsage ?? usage;
|
|
28870
|
-
if (isNoReplyText(trimmed)) {
|
|
29108
|
+
if (isNoReplyText(trimmed, { allowSdkSyntheticNoResponse: groupMode })) {
|
|
28871
29109
|
checkInputTokenWatermark(proc, watermarkUsage, base.traceId);
|
|
28872
29110
|
emitUsageReported(proc, emit, base, usage);
|
|
28873
29111
|
if (groupMode && proc.contentBlocks.length > 0) {
|
|
@@ -28889,7 +29127,6 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onProv
|
|
|
28889
29127
|
groupId,
|
|
28890
29128
|
compactScheduled: proc.compactRequested === true,
|
|
28891
29129
|
fullTextLen: proc.accumulatedText.length,
|
|
28892
|
-
fullTextSample: proc.accumulatedText.slice(0, 200),
|
|
28893
29130
|
accumulatedBlockCount: proc.contentBlocks.length,
|
|
28894
29131
|
accumulatedBlockTypes: proc.contentBlocks.map((b) => b.type),
|
|
28895
29132
|
silentSegmentEmitted: groupMode
|
|
@@ -28933,7 +29170,6 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onProv
|
|
|
28933
29170
|
segmentCount: proc.segmentCount,
|
|
28934
29171
|
compactScheduled: proc.compactRequested === true,
|
|
28935
29172
|
fullTextLen: proc.accumulatedText.length,
|
|
28936
|
-
fullTextSample: proc.accumulatedText.slice(0, 200),
|
|
28937
29173
|
traceId: base.traceId
|
|
28938
29174
|
});
|
|
28939
29175
|
emit({
|
|
@@ -28984,7 +29220,6 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onProv
|
|
|
28984
29220
|
ackId: base.replyMessageId,
|
|
28985
29221
|
messageId: carrierMessageId,
|
|
28986
29222
|
textLen: proc.accumulatedText.length,
|
|
28987
|
-
textSample: proc.accumulatedText.slice(0, 200),
|
|
28988
29223
|
tokenCount: usage.tokenCount,
|
|
28989
29224
|
traceId: base.traceId
|
|
28990
29225
|
});
|
|
@@ -29177,8 +29412,7 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onProv
|
|
|
29177
29412
|
logger10.info("Captured non-streamed assistant message", {
|
|
29178
29413
|
agentId: proc.agentId,
|
|
29179
29414
|
scope: proc.scope.kind === "single" ? "single" : proc.scope.groupId,
|
|
29180
|
-
textLen: text.length
|
|
29181
|
-
textSample: text.slice(0, 100)
|
|
29415
|
+
textLen: text.length
|
|
29182
29416
|
});
|
|
29183
29417
|
} else {
|
|
29184
29418
|
proc.lastAssistantContentDescription = describeAssistantContent(am.message?.content);
|
|
@@ -29202,6 +29436,7 @@ function resetAccumulators(proc) {
|
|
|
29202
29436
|
proc.currentToolName = null;
|
|
29203
29437
|
proc.currentMcpInvocationId = null;
|
|
29204
29438
|
proc.currentMcpInvocationStartedAt = null;
|
|
29439
|
+
proc.activeToolUseStartedAt = void 0;
|
|
29205
29440
|
proc.segmentBuffer = "";
|
|
29206
29441
|
proc.segmentCount = 0;
|
|
29207
29442
|
proc.accumulatedToolInput = "";
|
|
@@ -29211,6 +29446,7 @@ function resetAccumulators(proc) {
|
|
|
29211
29446
|
proc.officialMediaGenerationSatisfied = false;
|
|
29212
29447
|
proc.suppressCurrentThinking = false;
|
|
29213
29448
|
proc.suppressCurrentToolUse = false;
|
|
29449
|
+
proc.activeSubagentTaskIds?.clear();
|
|
29214
29450
|
}
|
|
29215
29451
|
|
|
29216
29452
|
// src/forkHistoryReplay.ts
|
|
@@ -29398,7 +29634,7 @@ function missingSubscriptionMessage(subscriptionId) {
|
|
|
29398
29634
|
}
|
|
29399
29635
|
var NODE_USER_UID = 1e3;
|
|
29400
29636
|
var POST_MERGE_CONTINUATION_ROUTE_MS = 15e3;
|
|
29401
|
-
var SCOPE_PROMPT_FINGERPRINT_REVISION = "workdir-scope-prompt-
|
|
29637
|
+
var SCOPE_PROMPT_FINGERPRINT_REVISION = "workdir-scope-mcp-abi-prompt-v4";
|
|
29402
29638
|
var BINARY_ATTACHMENT_EXT_RE = /\.(?:7z|bmp|csv|doc|docx|gif|jpeg|jpg|m4a|mov|mp3|mp4|pdf|png|ppt|pptx|rar|rtf|wav|webm|webp|xls|xlsx|zip)$/i;
|
|
29403
29639
|
var DOCUMENT_READING_RULES = `DOCUMENT READING:
|
|
29404
29640
|
- The built-in Read tool cannot read binary office documents such as .docx, .xls, .xlsx, .pptx, .pdf, .odt, .ods, .odp, or .rtf.
|
|
@@ -29413,6 +29649,16 @@ var MEDIA_GENERATION_RULES = `MEDIA GENERATION:
|
|
|
29413
29649
|
- Keep media replies short. Do not print raw media URLs, request_id, task_id, polling logs, or "let me check again" narration unless the user explicitly asks for diagnostics.
|
|
29414
29650
|
- When a media task is submitted or completed, write only a natural one-line note such as "\u5DF2\u5F00\u59CB\u751F\u6210\uFF0C\u6211\u4F1A\u5728\u8FD9\u91CC\u66F4\u65B0\u7ED3\u679C\u3002" or "\u751F\u6210\u597D\u4E86\uFF0C\u53EF\u4EE5\u5728\u5361\u7247\u91CC\u67E5\u770B\u3002"; let the media card show status, preview, download, copy, and regenerate actions.
|
|
29415
29651
|
- If the user asks whether a Seedance task is ready, call mcp__seedance__seedance_check_task once and answer from that result. Do not loop, sleep, or invent external Seedance API endpoints.`;
|
|
29652
|
+
function stableFingerprintValue(value) {
|
|
29653
|
+
if (Array.isArray(value)) return value.map(stableFingerprintValue);
|
|
29654
|
+
if (!value || typeof value !== "object") return value;
|
|
29655
|
+
const out = {};
|
|
29656
|
+
for (const key of Object.keys(value).sort()) {
|
|
29657
|
+
const normalized = stableFingerprintValue(value[key]);
|
|
29658
|
+
if (normalized !== void 0) out[key] = normalized;
|
|
29659
|
+
}
|
|
29660
|
+
return out;
|
|
29661
|
+
}
|
|
29416
29662
|
function isRecoveryDispatchTask(task) {
|
|
29417
29663
|
return task.dispatchKind === "manual_continue" || task.dispatchKind === "regenerate";
|
|
29418
29664
|
}
|
|
@@ -29670,13 +29916,15 @@ var AgentManager = class {
|
|
|
29670
29916
|
agents = /* @__PURE__ */ new Map();
|
|
29671
29917
|
lastUsedAt = /* @__PURE__ */ new Map();
|
|
29672
29918
|
/** Scopes 被 zombie_watchdog 关闭后的"入睡"标记,acquire 重建时清除并 emit awake。 */
|
|
29673
|
-
dormantScopes = /* @__PURE__ */ new
|
|
29919
|
+
dormantScopes = /* @__PURE__ */ new Map();
|
|
29674
29920
|
/**
|
|
29675
29921
|
* zombie_watchdog 拆 runtime 时,把该 (agentId, scope) 的 groupInbox 快照到这里,
|
|
29676
29922
|
* 让下一次 getOrCreate 重建 runtime 时可以恢复未读消息。仅 in-memory;
|
|
29677
29923
|
* bridge 进程崩溃 / shutdownAll 时丢失,与现有 inbox 内存语义一致。
|
|
29678
29924
|
*/
|
|
29679
29925
|
dormantGroupInboxes = /* @__PURE__ */ new Map();
|
|
29926
|
+
/** Spectate requested before runtime existed; value = activatedAt epoch ms. */
|
|
29927
|
+
pendingSpectate = /* @__PURE__ */ new Map();
|
|
29680
29928
|
sessionStore;
|
|
29681
29929
|
dispatchMemory = new GroupDispatchMemoryStore();
|
|
29682
29930
|
dataDir;
|
|
@@ -29842,6 +30090,7 @@ var AgentManager = class {
|
|
|
29842
30090
|
}
|
|
29843
30091
|
async resolveRuntimeCwd(agentConfig, scope, requestedCwd) {
|
|
29844
30092
|
let cwd = this.remapServerWorkspaceCwd(agentConfig, scope, requestedCwd);
|
|
30093
|
+
let fallbackForensicsId;
|
|
29845
30094
|
if (!isFullyQualifiedAbsolutePath(cwd)) {
|
|
29846
30095
|
const fallback = path13.join(this.workspacesDir, this.localScopeDirName(agentConfig, scope));
|
|
29847
30096
|
logger13.error(
|
|
@@ -29856,6 +30105,23 @@ var AgentManager = class {
|
|
|
29856
30105
|
error: new Error("workdir_not_usable_on_this_machine")
|
|
29857
30106
|
}
|
|
29858
30107
|
);
|
|
30108
|
+
fallbackForensicsId = createFallbackId();
|
|
30109
|
+
logFallback(logger13, {
|
|
30110
|
+
fallbackId: fallbackForensicsId,
|
|
30111
|
+
type: "cwd_sandbox",
|
|
30112
|
+
phase: "applied",
|
|
30113
|
+
expected: false,
|
|
30114
|
+
context: {
|
|
30115
|
+
agentId: agentConfig.id,
|
|
30116
|
+
scope: scopeKey(scope),
|
|
30117
|
+
platform: process.platform,
|
|
30118
|
+
requested: requestedCwd,
|
|
30119
|
+
resolved: cwd,
|
|
30120
|
+
reason: "not_fully_qualified",
|
|
30121
|
+
fallback
|
|
30122
|
+
},
|
|
30123
|
+
outcome: { result: "sandbox_fallback" }
|
|
30124
|
+
});
|
|
29859
30125
|
cwd = fallback;
|
|
29860
30126
|
}
|
|
29861
30127
|
if (isRunningAsRoot() && cwd.startsWith("/root/")) {
|
|
@@ -29874,6 +30140,21 @@ var AgentManager = class {
|
|
|
29874
30140
|
fallback,
|
|
29875
30141
|
error: e
|
|
29876
30142
|
});
|
|
30143
|
+
const fbId = fallbackForensicsId ?? createFallbackId();
|
|
30144
|
+
logFallback(logger13, {
|
|
30145
|
+
fallbackId: fbId,
|
|
30146
|
+
type: "cwd_sandbox",
|
|
30147
|
+
phase: "applied",
|
|
30148
|
+
expected: false,
|
|
30149
|
+
context: {
|
|
30150
|
+
agentId: agentConfig.id,
|
|
30151
|
+
scope: scopeKey(scope),
|
|
30152
|
+
reason: "mkdir_failed",
|
|
30153
|
+
requested: cwd,
|
|
30154
|
+
fallback
|
|
30155
|
+
},
|
|
30156
|
+
outcome: { result: "second_layer_fallback" }
|
|
30157
|
+
});
|
|
29877
30158
|
await fs6.mkdir(fallback, { recursive: true });
|
|
29878
30159
|
return fallback;
|
|
29879
30160
|
}
|
|
@@ -30096,17 +30377,24 @@ var AgentManager = class {
|
|
|
30096
30377
|
});
|
|
30097
30378
|
return null;
|
|
30098
30379
|
}
|
|
30099
|
-
scopePromptFingerprint(agentConfig, scope, agentCwd, scopesSection) {
|
|
30100
|
-
return createHash("sha256").update(SCOPE_PROMPT_FINGERPRINT_REVISION).update("\0").update(agentConfig.id).update("\0").update(agentConfig.name).update("\0").update(scopeKey(scope)).update("\0").update(path13.normalize(agentCwd)).update("\0").update(scopesSection).digest("hex");
|
|
30380
|
+
scopePromptFingerprint(agentConfig, scope, agentCwd, scopesSection, externalMcpFingerprint) {
|
|
30381
|
+
return createHash("sha256").update(SCOPE_PROMPT_FINGERPRINT_REVISION).update("\0").update(agentConfig.id).update("\0").update(agentConfig.name).update("\0").update(scopeKey(scope)).update("\0").update(path13.normalize(agentCwd)).update("\0").update(scopesSection).update("\0").update(externalMcpFingerprint).digest("hex");
|
|
30101
30382
|
}
|
|
30102
|
-
|
|
30383
|
+
externalMcpFingerprint(externalMcp) {
|
|
30384
|
+
const serverNames = Object.keys(externalMcp.mcpServers).sort();
|
|
30385
|
+
const allowedTools = [...externalMcp.allowedTools].sort();
|
|
30386
|
+
const toolAbi = [...externalMcp.toolAbi ?? []].sort((a, b) => a.serverName.localeCompare(b.serverName)).map(stableFingerprintValue);
|
|
30387
|
+
if (serverNames.length === 0 && allowedTools.length === 0 && toolAbi.length === 0) return "";
|
|
30388
|
+
return JSON.stringify({ serverNames, allowedTools, toolAbi });
|
|
30389
|
+
}
|
|
30390
|
+
discardSessionIfScopePromptChanged(agentConfig, scope, sessionId, fingerprint, options = {}) {
|
|
30103
30391
|
const previous = this.sessionStore.getPromptFingerprint(agentConfig.id, scope);
|
|
30104
30392
|
if (!sessionId) {
|
|
30105
30393
|
this.sessionStore.setPromptFingerprint(agentConfig.id, scope, fingerprint);
|
|
30106
30394
|
return null;
|
|
30107
30395
|
}
|
|
30108
30396
|
if (previous === fingerprint) return sessionId;
|
|
30109
|
-
if (!previous && scope.kind === "single") {
|
|
30397
|
+
if (!previous && scope.kind === "single" && options.clearLegacySingleSession !== true) {
|
|
30110
30398
|
this.sessionStore.setPromptFingerprint(agentConfig.id, scope, fingerprint);
|
|
30111
30399
|
logger13.info("Retaining legacy single-scope session while recording prompt fingerprint", {
|
|
30112
30400
|
agentId: agentConfig.id,
|
|
@@ -30126,7 +30414,8 @@ var AgentManager = class {
|
|
|
30126
30414
|
sessionId,
|
|
30127
30415
|
previousFingerprint: previous,
|
|
30128
30416
|
nextFingerprint: fingerprint,
|
|
30129
|
-
revision: SCOPE_PROMPT_FINGERPRINT_REVISION
|
|
30417
|
+
revision: SCOPE_PROMPT_FINGERPRINT_REVISION,
|
|
30418
|
+
reason: options.reason ?? "scope_prompt_changed"
|
|
30130
30419
|
});
|
|
30131
30420
|
return null;
|
|
30132
30421
|
}
|
|
@@ -30175,6 +30464,7 @@ var AgentManager = class {
|
|
|
30175
30464
|
logger13.info("Evicting idle Agent query", { agentId: proc.agentId, scope: scopeKey(proc.scope) });
|
|
30176
30465
|
const runtime = this.asRuntime(proc);
|
|
30177
30466
|
this.clearQuietFlushTimer(runtime);
|
|
30467
|
+
this.teardownSpectate(runtime);
|
|
30178
30468
|
try {
|
|
30179
30469
|
runtime.inputController.close();
|
|
30180
30470
|
await this.awaitQueryReturn(runtime.query, 5e3, proc.agentId);
|
|
@@ -30194,6 +30484,7 @@ var AgentManager = class {
|
|
|
30194
30484
|
const runtime = this.asRuntime(proc);
|
|
30195
30485
|
const key = runtimeKey(proc.agentId, proc.scope);
|
|
30196
30486
|
this.clearQuietFlushTimer(runtime);
|
|
30487
|
+
this.teardownSpectate(runtime);
|
|
30197
30488
|
runtime.currentTask = null;
|
|
30198
30489
|
runtime.injectedTasks = [];
|
|
30199
30490
|
runtime.mergedTasks = [];
|
|
@@ -30230,6 +30521,7 @@ var AgentManager = class {
|
|
|
30230
30521
|
evictIdle() {
|
|
30231
30522
|
const now = Date.now();
|
|
30232
30523
|
const { idleTimeoutMs, workingSilenceTimeoutMs } = this.queryConfig;
|
|
30524
|
+
const stallWarnAfterMs = Math.min(9e4, this.queryConfig.replyStallTimeoutMs);
|
|
30233
30525
|
for (const [key, proc] of this.agents) {
|
|
30234
30526
|
if (!this.isEvictable(proc)) continue;
|
|
30235
30527
|
const runtime = this.asRuntime(proc);
|
|
@@ -30240,26 +30532,62 @@ var AgentManager = class {
|
|
|
30240
30532
|
for (const [, proc] of this.agents) {
|
|
30241
30533
|
if (proc.status !== "working") continue;
|
|
30242
30534
|
const runtime = this.asRuntime(proc);
|
|
30535
|
+
if (runtime.currentTask) {
|
|
30536
|
+
const sinceEventMs = now - proc.lastSdkEventAt;
|
|
30537
|
+
if (sinceEventMs > stallWarnAfterMs && !proc.stallWarned) {
|
|
30538
|
+
proc.stallWarned = true;
|
|
30539
|
+
const openTool = this.latestOpenToolUse(proc);
|
|
30540
|
+
logger13.warn("Reply stall onset: in-flight reply silent", {
|
|
30541
|
+
agentId: proc.agentId,
|
|
30542
|
+
scope: scopeKey(proc.scope),
|
|
30543
|
+
sinceEventMs,
|
|
30544
|
+
replyStallTimeoutMs: this.queryConfig.replyStallTimeoutMs,
|
|
30545
|
+
workingSilenceTimeoutMs,
|
|
30546
|
+
busySilenceTimeoutMs: this.queryConfig.busySilenceTimeoutMs ?? null,
|
|
30547
|
+
replyMessageId: runtime.currentTask.replyMessageId,
|
|
30548
|
+
model: proc.model ?? "(unknown)",
|
|
30549
|
+
lastSdkEvent: proc.lastSdkEventInfo,
|
|
30550
|
+
hasActiveToolUse: runtime.activeToolUseStartedAt != null || openTool != null,
|
|
30551
|
+
activeToolUseAgeMs: runtime.activeToolUseStartedAt != null ? now - runtime.activeToolUseStartedAt : null,
|
|
30552
|
+
openToolName: openTool?.toolName ?? proc.currentToolName ?? null,
|
|
30553
|
+
openMcpInvocationId: proc.currentMcpInvocationId ?? null,
|
|
30554
|
+
subagentInFlight: (runtime.activeSubagentTaskIds?.size ?? 0) > 0,
|
|
30555
|
+
activeSubagentCount: runtime.activeSubagentTaskIds?.size ?? 0,
|
|
30556
|
+
busyReason: this.busyReason(proc)
|
|
30557
|
+
});
|
|
30558
|
+
}
|
|
30559
|
+
}
|
|
30560
|
+
const busyReason = this.busyReason(runtime);
|
|
30561
|
+
const busy = busyReason !== null;
|
|
30562
|
+
if (runtime.currentTask && !busy && now - proc.lastSdkEventAt > this.queryConfig.replyStallTimeoutMs) {
|
|
30563
|
+
void this.recoverStalledReply(proc, now - proc.lastSdkEventAt);
|
|
30564
|
+
continue;
|
|
30565
|
+
}
|
|
30243
30566
|
const hasInjectedBacklog = runtime.injectedTasks.length > 0;
|
|
30244
|
-
const
|
|
30567
|
+
const baseCeilingMs = busy ? Math.max(workingSilenceTimeoutMs, this.queryConfig.busySilenceTimeoutMs ?? 0) : workingSilenceTimeoutMs;
|
|
30568
|
+
const effectiveTimeoutMs = hasInjectedBacklog ? baseCeilingMs * 2 : baseCeilingMs;
|
|
30245
30569
|
const silentMs = now - proc.lastSdkEventAt;
|
|
30246
30570
|
if (silentMs <= effectiveTimeoutMs) {
|
|
30247
|
-
if (
|
|
30248
|
-
logger13.warn(
|
|
30249
|
-
|
|
30250
|
-
|
|
30251
|
-
|
|
30252
|
-
|
|
30253
|
-
|
|
30254
|
-
|
|
30255
|
-
|
|
30256
|
-
|
|
30257
|
-
|
|
30258
|
-
|
|
30259
|
-
|
|
30571
|
+
if (silentMs > workingSilenceTimeoutMs) {
|
|
30572
|
+
logger13.warn("Zombie watchdog: working runtime silent past base timeout; granting extended grace", {
|
|
30573
|
+
agentId: proc.agentId,
|
|
30574
|
+
scope: scopeKey(proc.scope),
|
|
30575
|
+
silentMs,
|
|
30576
|
+
baseTimeoutMs: workingSilenceTimeoutMs,
|
|
30577
|
+
baseCeilingMs,
|
|
30578
|
+
busySilenceTimeoutMs: this.queryConfig.busySilenceTimeoutMs ?? null,
|
|
30579
|
+
effectiveTimeoutMs,
|
|
30580
|
+
busy,
|
|
30581
|
+
busyReason,
|
|
30582
|
+
injectedTaskCount: runtime.injectedTasks.length,
|
|
30583
|
+
replyMessageId: proc.currentTask?.replyMessageId
|
|
30584
|
+
});
|
|
30260
30585
|
}
|
|
30261
30586
|
continue;
|
|
30262
30587
|
}
|
|
30588
|
+
const zombieOpenTool = this.latestOpenToolUse(proc);
|
|
30589
|
+
const watchdogDetectedAt = Date.now();
|
|
30590
|
+
const watchdogFallbackId = createFallbackId();
|
|
30263
30591
|
logger13.warn("Zombie watchdog: working runtime silent too long, tearing down", {
|
|
30264
30592
|
agentId: proc.agentId,
|
|
30265
30593
|
scope: scopeKey(proc.scope),
|
|
@@ -30267,12 +30595,46 @@ var AgentManager = class {
|
|
|
30267
30595
|
lastSdkEventAt: new Date(proc.lastSdkEventAt).toISOString(),
|
|
30268
30596
|
workingSilenceTimeoutMs,
|
|
30269
30597
|
effectiveTimeoutMs,
|
|
30598
|
+
baseCeilingMs,
|
|
30599
|
+
busy,
|
|
30600
|
+
busySilenceTimeoutMs: this.queryConfig.busySilenceTimeoutMs ?? null,
|
|
30270
30601
|
replyMessageId: proc.currentTask?.replyMessageId,
|
|
30271
30602
|
injectedTaskCount: runtime.injectedTasks.length,
|
|
30272
30603
|
hadInjectedBacklog: hasInjectedBacklog,
|
|
30273
|
-
inboxSize: proc.groupInbox.length
|
|
30604
|
+
inboxSize: proc.groupInbox.length,
|
|
30605
|
+
fallbackId: watchdogFallbackId,
|
|
30606
|
+
// Breadcrumb: what the turn was waiting on when it timed out. A hung subagent
|
|
30607
|
+
// (open `Task` tool_use) lands here now that the fast path skips active tools.
|
|
30608
|
+
model: proc.model ?? "(unknown)",
|
|
30609
|
+
lastSdkEvent: proc.lastSdkEventInfo,
|
|
30610
|
+
openToolName: zombieOpenTool?.toolName ?? proc.currentToolName ?? null,
|
|
30611
|
+
activeToolUseAgeMs: runtime.activeToolUseStartedAt != null ? now - runtime.activeToolUseStartedAt : null,
|
|
30612
|
+
openMcpInvocationId: proc.currentMcpInvocationId ?? null,
|
|
30613
|
+
subagentInFlight: (runtime.activeSubagentTaskIds?.size ?? 0) > 0,
|
|
30614
|
+
activeSubagentCount: runtime.activeSubagentTaskIds?.size ?? 0,
|
|
30615
|
+
busyReason: this.busyReason(proc)
|
|
30616
|
+
});
|
|
30617
|
+
logFallback(logger13, {
|
|
30618
|
+
fallbackId: watchdogFallbackId,
|
|
30619
|
+
type: "zombie_watchdog",
|
|
30620
|
+
phase: "detected",
|
|
30621
|
+
expected: false,
|
|
30622
|
+
traceId: proc.currentTask?.traceId,
|
|
30623
|
+
context: {
|
|
30624
|
+
agentId: proc.agentId,
|
|
30625
|
+
scope: scopeKey(proc.scope),
|
|
30626
|
+
silentMs,
|
|
30627
|
+
replyMessageId: proc.currentTask?.replyMessageId ?? null,
|
|
30628
|
+
openToolName: zombieOpenTool?.toolName ?? proc.currentToolName ?? null,
|
|
30629
|
+
lastSdkEventType: proc.lastSdkEventInfo?.type ?? null,
|
|
30630
|
+
injectedTaskCount: runtime.injectedTasks.length,
|
|
30631
|
+
inboxSize: proc.groupInbox.length
|
|
30632
|
+
}
|
|
30633
|
+
});
|
|
30634
|
+
void this.closeRuntime(proc, "zombie_watchdog", {
|
|
30635
|
+
fallbackId: watchdogFallbackId,
|
|
30636
|
+
detectedAt: watchdogDetectedAt
|
|
30274
30637
|
});
|
|
30275
|
-
void this.closeRuntime(proc, "zombie_watchdog");
|
|
30276
30638
|
}
|
|
30277
30639
|
}
|
|
30278
30640
|
/**
|
|
@@ -30519,7 +30881,7 @@ ${cfg.instructions.trim()}` : "";
|
|
|
30519
30881
|
agentId: agentConfig.id,
|
|
30520
30882
|
capabilityTier: cfg.capabilityTier,
|
|
30521
30883
|
isSmith: smithAgent
|
|
30522
|
-
}) ?? { mcpServers: {}, allowedTools: [] };
|
|
30884
|
+
}) ?? { mcpServers: {}, allowedTools: [], toolAbi: [] };
|
|
30523
30885
|
logger13.info("External MCP resolved for runtime", {
|
|
30524
30886
|
agentId: agentConfig.id,
|
|
30525
30887
|
scope: scopeKey(scope),
|
|
@@ -30536,11 +30898,16 @@ ${cfg.instructions.trim()}` : "";
|
|
|
30536
30898
|
}
|
|
30537
30899
|
const notebookSection = this.buildNotebookSection(agentConfig.id);
|
|
30538
30900
|
const scopesSection = this.buildScopesSection(agentConfig, scope, agentCwd);
|
|
30901
|
+
const externalMcpFingerprint = this.externalMcpFingerprint(externalMcp);
|
|
30539
30902
|
savedSessionId = this.discardSessionIfScopePromptChanged(
|
|
30540
30903
|
agentConfig,
|
|
30541
30904
|
scope,
|
|
30542
30905
|
savedSessionId,
|
|
30543
|
-
this.scopePromptFingerprint(agentConfig, scope, agentCwd, scopesSection)
|
|
30906
|
+
this.scopePromptFingerprint(agentConfig, scope, agentCwd, scopesSection, externalMcpFingerprint),
|
|
30907
|
+
{
|
|
30908
|
+
clearLegacySingleSession: externalMcpFingerprint.length > 0,
|
|
30909
|
+
reason: externalMcpFingerprint.length > 0 ? "external_mcp_abi_changed" : "scope_prompt_changed"
|
|
30910
|
+
}
|
|
30544
30911
|
);
|
|
30545
30912
|
let forkHistorySection = "";
|
|
30546
30913
|
if (!savedSessionId && scope.kind === "single") {
|
|
@@ -30561,6 +30928,8 @@ ${cfg.instructions.trim()}` : "";
|
|
|
30561
30928
|
}
|
|
30562
30929
|
}
|
|
30563
30930
|
const cronLockSnapshot = readCronLockSnapshot();
|
|
30931
|
+
const builtinWebSearchAllowed = this.queryConfig.allowBuiltinWebSearch;
|
|
30932
|
+
const disallowedToolsForRuntime = builtinWebSearchAllowed ? [] : ["WebSearch"];
|
|
30564
30933
|
logger13.info("Creating Agent query", {
|
|
30565
30934
|
agentId: agentConfig.id,
|
|
30566
30935
|
scope: scopeKey(scope),
|
|
@@ -30569,6 +30938,8 @@ ${cfg.instructions.trim()}` : "";
|
|
|
30569
30938
|
sessionId: savedSessionId,
|
|
30570
30939
|
forkHistoryReplay: forkHistorySection.length > 0,
|
|
30571
30940
|
model: cfg.model ?? "(default)",
|
|
30941
|
+
builtinWebSearchAllowed,
|
|
30942
|
+
disallowedTools: disallowedToolsForRuntime,
|
|
30572
30943
|
// Diagnostic: who currently owns Claude's global cron lock (~/.claude/scheduled_tasks.lock).
|
|
30573
30944
|
// Cron is process-internal but the binary uses this singleton lock to elect ONE scheduler
|
|
30574
30945
|
// among concurrent claude subprocesses. If lock is held by another session at spawn time,
|
|
@@ -30580,7 +30951,6 @@ ${cfg.instructions.trim()}` : "";
|
|
|
30580
30951
|
});
|
|
30581
30952
|
const planModeRef = { active: false, denyCount: 0 };
|
|
30582
30953
|
const mediaGenerationTurnGuard = createOfficialMediaGenerationTurnGuard();
|
|
30583
|
-
const builtinWebSearchAllowed = this.queryConfig.allowBuiltinWebSearch;
|
|
30584
30954
|
const options = {
|
|
30585
30955
|
cwd: agentCwd,
|
|
30586
30956
|
systemPrompt: {
|
|
@@ -30647,6 +31017,8 @@ ${cfg.instructions.trim()}` : "";
|
|
|
30647
31017
|
] : [],
|
|
30648
31018
|
...externalMcp.allowedTools
|
|
30649
31019
|
],
|
|
31020
|
+
// Server-side WebSearch bypasses canUseTool; disallowedTools removes it from model context.
|
|
31021
|
+
disallowedTools: disallowedToolsForRuntime,
|
|
30650
31022
|
mcpServers: { ...externalMcp.mcpServers, neural: neuralServer },
|
|
30651
31023
|
includePartialMessages: true,
|
|
30652
31024
|
// Plan mode custom workflow instructions. When setPermissionMode('plan') is
|
|
@@ -30973,6 +31345,7 @@ Do NOT use "..." as content \u2014 write specific, project-relevant content.`;
|
|
|
30973
31345
|
currentTask: null,
|
|
30974
31346
|
currentTaskStartedAt: 0,
|
|
30975
31347
|
lastSdkEventAt: Date.now(),
|
|
31348
|
+
model: (typeof options.model === "string" ? options.model : cfg.model) ?? null,
|
|
30976
31349
|
compactRequested: false,
|
|
30977
31350
|
compactInProgress: false,
|
|
30978
31351
|
contextOverflowLockedUntil: 0,
|
|
@@ -30984,13 +31357,18 @@ Do NOT use "..." as content \u2014 write specific, project-relevant content.`;
|
|
|
30984
31357
|
currentToolName: null,
|
|
30985
31358
|
currentMcpInvocationId: null,
|
|
30986
31359
|
currentMcpInvocationStartedAt: null,
|
|
31360
|
+
activeSubagentTaskIds: /* @__PURE__ */ new Set(),
|
|
30987
31361
|
mcpAuditRecorder: this.mcpAuditRecorder,
|
|
30988
31362
|
segmentBuffer: "",
|
|
30989
31363
|
segmentCount: 0,
|
|
30990
31364
|
accumulatedToolInput: "",
|
|
30991
31365
|
planModeRef,
|
|
30992
31366
|
mediaGenerationTurnGuard,
|
|
30993
|
-
groupInbox: []
|
|
31367
|
+
groupInbox: [],
|
|
31368
|
+
spectating: false,
|
|
31369
|
+
spectateActivatedAt: 0,
|
|
31370
|
+
spectateViewing: false,
|
|
31371
|
+
spectateTtlExpired: false
|
|
30994
31372
|
};
|
|
30995
31373
|
const runtime = Object.assign(proc, {
|
|
30996
31374
|
query: agentQuery,
|
|
@@ -31002,7 +31380,8 @@ Do NOT use "..." as content \u2014 write specific, project-relevant content.`;
|
|
|
31002
31380
|
createdAt: Date.now(),
|
|
31003
31381
|
supportsVision: modelInputMode === "vision" && cfg.supportsVision !== false,
|
|
31004
31382
|
modelInputMode,
|
|
31005
|
-
quietFlushTimer: null
|
|
31383
|
+
quietFlushTimer: null,
|
|
31384
|
+
spectateRevertTimer: null
|
|
31006
31385
|
});
|
|
31007
31386
|
logger13.info("Agent model input mode resolved", {
|
|
31008
31387
|
agentId: agentConfig.id,
|
|
@@ -31027,6 +31406,7 @@ Do NOT use "..." as content \u2014 write specific, project-relevant content.`;
|
|
|
31027
31406
|
} else {
|
|
31028
31407
|
this.dormantGroupInboxes.delete(key);
|
|
31029
31408
|
}
|
|
31409
|
+
const dormantMeta = this.dormantScopes.get(key);
|
|
31030
31410
|
if (this.dormantScopes.delete(key)) {
|
|
31031
31411
|
this.emit({
|
|
31032
31412
|
type: "agent:awake",
|
|
@@ -31038,7 +31418,44 @@ Do NOT use "..." as content \u2014 write specific, project-relevant content.`;
|
|
|
31038
31418
|
});
|
|
31039
31419
|
logger13.info("Agent scope awakened after dormant", {
|
|
31040
31420
|
agentId: agentConfig.id,
|
|
31041
|
-
scope: scopeKey(scope)
|
|
31421
|
+
scope: scopeKey(scope),
|
|
31422
|
+
...dormantMeta ? {
|
|
31423
|
+
fallbackId: dormantMeta.fallbackId,
|
|
31424
|
+
dormantDurationMs: Date.now() - dormantMeta.detectedAt,
|
|
31425
|
+
dataLossSuspected: dormantMeta.droppedTaskCount > 0
|
|
31426
|
+
} : {}
|
|
31427
|
+
});
|
|
31428
|
+
if (dormantMeta) {
|
|
31429
|
+
logFallback(logger13, {
|
|
31430
|
+
fallbackId: dormantMeta.fallbackId,
|
|
31431
|
+
type: "zombie_watchdog",
|
|
31432
|
+
phase: "outcome",
|
|
31433
|
+
expected: false,
|
|
31434
|
+
context: {
|
|
31435
|
+
agentId: agentConfig.id,
|
|
31436
|
+
scope: scopeKey(scope)
|
|
31437
|
+
},
|
|
31438
|
+
outcome: {
|
|
31439
|
+
result: "recovered_rebuilt",
|
|
31440
|
+
durationMs: Date.now() - dormantMeta.detectedAt,
|
|
31441
|
+
dataLossSuspected: dormantMeta.droppedTaskCount > 0
|
|
31442
|
+
}
|
|
31443
|
+
});
|
|
31444
|
+
}
|
|
31445
|
+
}
|
|
31446
|
+
const pendingSpectateAt = this.pendingSpectate.get(key);
|
|
31447
|
+
if (pendingSpectateAt != null) {
|
|
31448
|
+
this.pendingSpectate.delete(key);
|
|
31449
|
+
runtime.spectating = true;
|
|
31450
|
+
runtime.spectateViewing = false;
|
|
31451
|
+
runtime.spectateActivatedAt = pendingSpectateAt;
|
|
31452
|
+
runtime.spectateTtlExpired = false;
|
|
31453
|
+
this.armSpectateTimer(runtime);
|
|
31454
|
+
this.emitSpectateState(runtime, true, "started");
|
|
31455
|
+
logger13.info("Applied pending spectate on runtime create", {
|
|
31456
|
+
agentId: agentConfig.id,
|
|
31457
|
+
scope: scopeKey(scope),
|
|
31458
|
+
activatedAt: pendingSpectateAt
|
|
31042
31459
|
});
|
|
31043
31460
|
}
|
|
31044
31461
|
if (proc.groupInbox.length > 0 && this.isRuntimeIdleForInboxFlush(runtime)) {
|
|
@@ -32176,7 +32593,6 @@ ${lines.join("\n")}`;
|
|
|
32176
32593
|
compactTrigger: "context_watermark",
|
|
32177
32594
|
injectedTasksWaiting: runtime.injectedTasks.length,
|
|
32178
32595
|
compactPromptLen: compactPrompt.length,
|
|
32179
|
-
promptSample: compactPrompt.slice(0, 80),
|
|
32180
32596
|
traceId: compactTraceId
|
|
32181
32597
|
});
|
|
32182
32598
|
runtime.inputController.push(compactPrompt, runtime.ccSessionId ?? "");
|
|
@@ -32458,7 +32874,7 @@ ${lines.join("\n")}`;
|
|
|
32458
32874
|
const enveloped = buildInnerVoiceEnvelope(payloadWithTrigger, ctx);
|
|
32459
32875
|
const task = {
|
|
32460
32876
|
content: enveloped,
|
|
32461
|
-
replyMessageId:
|
|
32877
|
+
replyMessageId: createNeuralSendReplyMessageId(),
|
|
32462
32878
|
conversationId: payload.conversationId,
|
|
32463
32879
|
traceId: createTraceId(),
|
|
32464
32880
|
groupId: payload.groupId
|
|
@@ -32704,7 +33120,7 @@ ${lines.join("\n")}`;
|
|
|
32704
33120
|
this.dormantScopes.delete(key);
|
|
32705
33121
|
this.dormantGroupInboxes.delete(key);
|
|
32706
33122
|
}
|
|
32707
|
-
for (const key of [...this.dormantScopes].filter(
|
|
33123
|
+
for (const key of [...this.dormantScopes.keys()].filter(
|
|
32708
33124
|
(k) => k === agentId || k.startsWith(`${agentId}::`)
|
|
32709
33125
|
)) {
|
|
32710
33126
|
this.dormantScopes.delete(key);
|
|
@@ -32752,7 +33168,7 @@ ${lines.join("\n")}`;
|
|
|
32752
33168
|
async reloadAgentScopes(agentId, reason) {
|
|
32753
33169
|
this.sessionStore.deleteAllForAgent(agentId);
|
|
32754
33170
|
this.dispatchMemory.deleteAllForAgent(agentId);
|
|
32755
|
-
for (const key of [...this.dormantScopes].filter(
|
|
33171
|
+
for (const key of [...this.dormantScopes.keys()].filter(
|
|
32756
33172
|
(k) => k === agentId || k.startsWith(`${agentId}::`)
|
|
32757
33173
|
)) {
|
|
32758
33174
|
this.dormantScopes.delete(key);
|
|
@@ -32904,6 +33320,125 @@ ${lines.join("\n")}`;
|
|
|
32904
33320
|
void this.terminateScope(proc.agentId, proc.scope);
|
|
32905
33321
|
}
|
|
32906
33322
|
}
|
|
33323
|
+
/** Control spectate capture/push for one scoped runtime. */
|
|
33324
|
+
async setSpectate(agentId, scope, action) {
|
|
33325
|
+
const key = runtimeKey(agentId, scope);
|
|
33326
|
+
const proc = this.agents.get(key);
|
|
33327
|
+
if (!proc || proc.status === "dead") {
|
|
33328
|
+
if (action === "start") {
|
|
33329
|
+
this.pendingSpectate.set(key, Date.now());
|
|
33330
|
+
logger13.info("setSpectate: runtime missing, pending start", { agentId, scope: scopeKey(scope) });
|
|
33331
|
+
}
|
|
33332
|
+
return;
|
|
33333
|
+
}
|
|
33334
|
+
const runtime = this.asRuntime(proc);
|
|
33335
|
+
switch (action) {
|
|
33336
|
+
case "start":
|
|
33337
|
+
runtime.spectating = true;
|
|
33338
|
+
runtime.spectateViewing = true;
|
|
33339
|
+
runtime.spectateActivatedAt = Date.now();
|
|
33340
|
+
runtime.spectateTtlExpired = false;
|
|
33341
|
+
this.pendingSpectate.delete(key);
|
|
33342
|
+
this.armSpectateTimer(runtime);
|
|
33343
|
+
this.emitSpectateState(runtime, true, "started");
|
|
33344
|
+
logger13.info("Spectate started", { agentId, scope: scopeKey(scope) });
|
|
33345
|
+
break;
|
|
33346
|
+
case "enter_view":
|
|
33347
|
+
runtime.spectateViewing = true;
|
|
33348
|
+
logger13.info("Spectate enter_view", { agentId, scope: scopeKey(scope) });
|
|
33349
|
+
break;
|
|
33350
|
+
case "leave_view":
|
|
33351
|
+
runtime.spectateViewing = false;
|
|
33352
|
+
logger13.info("Spectate leave_view", {
|
|
33353
|
+
agentId,
|
|
33354
|
+
scope: scopeKey(scope),
|
|
33355
|
+
ttlExpired: runtime.spectateTtlExpired
|
|
33356
|
+
});
|
|
33357
|
+
if (runtime.spectateTtlExpired) {
|
|
33358
|
+
this.stopSpectate(runtime, "ttl_expired");
|
|
33359
|
+
}
|
|
33360
|
+
break;
|
|
33361
|
+
case "stop":
|
|
33362
|
+
this.stopSpectate(runtime, "stopped");
|
|
33363
|
+
this.pendingSpectate.delete(key);
|
|
33364
|
+
logger13.info("Spectate stopped", { agentId, scope: scopeKey(scope) });
|
|
33365
|
+
break;
|
|
33366
|
+
default:
|
|
33367
|
+
break;
|
|
33368
|
+
}
|
|
33369
|
+
}
|
|
33370
|
+
spectateTtlMs() {
|
|
33371
|
+
return Number(process.env.AHCHAT_BRIDGE_SPECTATE_TTL_MS) || 36e5;
|
|
33372
|
+
}
|
|
33373
|
+
armSpectateTimer(runtime) {
|
|
33374
|
+
this.clearSpectateTimer(runtime);
|
|
33375
|
+
if (!runtime.spectating || runtime.spectateActivatedAt <= 0) return;
|
|
33376
|
+
const ttlMs = this.spectateTtlMs();
|
|
33377
|
+
const elapsed = Date.now() - runtime.spectateActivatedAt;
|
|
33378
|
+
const delay = Math.max(0, ttlMs - elapsed);
|
|
33379
|
+
runtime.spectateRevertTimer = setTimeout(() => {
|
|
33380
|
+
runtime.spectateRevertTimer = null;
|
|
33381
|
+
runtime.spectateTtlExpired = true;
|
|
33382
|
+
logger13.info("Spectate TTL expired", {
|
|
33383
|
+
agentId: runtime.agentId,
|
|
33384
|
+
scope: scopeKey(runtime.scope),
|
|
33385
|
+
viewing: runtime.spectateViewing
|
|
33386
|
+
});
|
|
33387
|
+
if (!runtime.spectateViewing) {
|
|
33388
|
+
this.stopSpectate(runtime, "ttl_expired");
|
|
33389
|
+
}
|
|
33390
|
+
}, delay);
|
|
33391
|
+
}
|
|
33392
|
+
clearSpectateTimer(runtime) {
|
|
33393
|
+
if (runtime.spectateRevertTimer != null) {
|
|
33394
|
+
clearTimeout(runtime.spectateRevertTimer);
|
|
33395
|
+
runtime.spectateRevertTimer = null;
|
|
33396
|
+
}
|
|
33397
|
+
}
|
|
33398
|
+
stopSpectate(runtime, reason) {
|
|
33399
|
+
const wasActive = runtime.spectating;
|
|
33400
|
+
this.clearSpectateTimer(runtime);
|
|
33401
|
+
runtime.spectating = false;
|
|
33402
|
+
runtime.spectateViewing = false;
|
|
33403
|
+
runtime.spectateTtlExpired = false;
|
|
33404
|
+
runtime.spectateActivatedAt = 0;
|
|
33405
|
+
if (wasActive) {
|
|
33406
|
+
this.emitSpectateState(runtime, false, reason);
|
|
33407
|
+
logger13.info("Spectate deactivated", {
|
|
33408
|
+
agentId: runtime.agentId,
|
|
33409
|
+
scope: scopeKey(runtime.scope),
|
|
33410
|
+
reason
|
|
33411
|
+
});
|
|
33412
|
+
}
|
|
33413
|
+
}
|
|
33414
|
+
emitSpectateState(runtime, active, reason) {
|
|
33415
|
+
const scopePayload = runtime.scope.kind === "single" ? { kind: "single" } : { kind: "group", groupId: runtime.scope.groupId };
|
|
33416
|
+
this.emit({
|
|
33417
|
+
type: "spectate:state",
|
|
33418
|
+
payload: {
|
|
33419
|
+
agentId: runtime.agentId,
|
|
33420
|
+
scope: scopePayload,
|
|
33421
|
+
active,
|
|
33422
|
+
expiresAt: active ? runtime.spectateActivatedAt + this.spectateTtlMs() : void 0,
|
|
33423
|
+
reason,
|
|
33424
|
+
traceId: createTraceId()
|
|
33425
|
+
}
|
|
33426
|
+
});
|
|
33427
|
+
logger13.info("Spectate state emitted", {
|
|
33428
|
+
agentId: runtime.agentId,
|
|
33429
|
+
scope: scopeKey(runtime.scope),
|
|
33430
|
+
active,
|
|
33431
|
+
reason,
|
|
33432
|
+
expiresAt: active ? runtime.spectateActivatedAt + this.spectateTtlMs() : void 0
|
|
33433
|
+
});
|
|
33434
|
+
}
|
|
33435
|
+
teardownSpectate(runtime) {
|
|
33436
|
+
if (runtime.spectating) {
|
|
33437
|
+
this.stopSpectate(runtime, "runtime_gone");
|
|
33438
|
+
} else {
|
|
33439
|
+
this.clearSpectateTimer(runtime);
|
|
33440
|
+
}
|
|
33441
|
+
}
|
|
32907
33442
|
/** Stop one scoped SDK runtime (workdir change). */
|
|
32908
33443
|
async terminateScope(agentId, scope) {
|
|
32909
33444
|
const key = runtimeKey(agentId, scope);
|
|
@@ -32912,6 +33447,7 @@ ${lines.join("\n")}`;
|
|
|
32912
33447
|
logger13.info("terminateScope: no active runtime", { agentId, scope: scopeKey(scope) });
|
|
32913
33448
|
this.dormantScopes.delete(key);
|
|
32914
33449
|
this.dormantGroupInboxes.delete(key);
|
|
33450
|
+
this.pendingSpectate.delete(key);
|
|
32915
33451
|
this.sessionStore.delete(agentId, scope);
|
|
32916
33452
|
this.dispatchMemory.deleteScope(agentId, scope);
|
|
32917
33453
|
return;
|
|
@@ -32934,7 +33470,7 @@ ${lines.join("\n")}`;
|
|
|
32934
33470
|
this.dispatchMemory.deleteScope(agentId, scope);
|
|
32935
33471
|
logger13.info("terminateScope: scoped query removed", { agentId, scope: scopeKey(scope) });
|
|
32936
33472
|
}
|
|
32937
|
-
async closeRuntime(proc, reason) {
|
|
33473
|
+
async closeRuntime(proc, reason, watchdogForensics) {
|
|
32938
33474
|
const key = runtimeKey(proc.agentId, proc.scope);
|
|
32939
33475
|
if (proc.status === "dead") return;
|
|
32940
33476
|
const runtime = this.asRuntime(proc);
|
|
@@ -33011,12 +33547,13 @@ ${lines.join("\n")}`;
|
|
|
33011
33547
|
runtime.currentTask = null;
|
|
33012
33548
|
if (isWatchdog) {
|
|
33013
33549
|
const preservedInbox = proc.groupInbox;
|
|
33014
|
-
|
|
33550
|
+
const preservedInboxSize = preservedInbox.length;
|
|
33551
|
+
if (preservedInboxSize > 0) {
|
|
33015
33552
|
this.dormantGroupInboxes.set(key, [...preservedInbox]);
|
|
33016
33553
|
logger13.info("Preserving groupInbox for dormant agent", {
|
|
33017
33554
|
agentId,
|
|
33018
33555
|
scope: scopeKey(proc.scope),
|
|
33019
|
-
preservedInboxSize
|
|
33556
|
+
preservedInboxSize,
|
|
33020
33557
|
preservedEntries: preservedInbox.map((e) => ({
|
|
33021
33558
|
ackId: e.ackId,
|
|
33022
33559
|
sender: e.senderName,
|
|
@@ -33025,7 +33562,26 @@ ${lines.join("\n")}`;
|
|
|
33025
33562
|
}))
|
|
33026
33563
|
});
|
|
33027
33564
|
}
|
|
33028
|
-
|
|
33565
|
+
const effectiveFallbackId = watchdogForensics?.fallbackId ?? createFallbackId();
|
|
33566
|
+
logFallback(logger13, {
|
|
33567
|
+
fallbackId: effectiveFallbackId,
|
|
33568
|
+
type: "zombie_watchdog",
|
|
33569
|
+
phase: "applied",
|
|
33570
|
+
expected: false,
|
|
33571
|
+
traceId: dormantTraceId,
|
|
33572
|
+
context: {
|
|
33573
|
+
agentId,
|
|
33574
|
+
scope: scopeKey(proc.scope),
|
|
33575
|
+
droppedTaskCount: droppedAckIds.length,
|
|
33576
|
+
preservedInboxSize,
|
|
33577
|
+
sessionDeleted: false
|
|
33578
|
+
}
|
|
33579
|
+
});
|
|
33580
|
+
this.dormantScopes.set(key, {
|
|
33581
|
+
fallbackId: effectiveFallbackId,
|
|
33582
|
+
detectedAt: watchdogForensics?.detectedAt ?? Date.now(),
|
|
33583
|
+
droppedTaskCount: droppedAckIds.length
|
|
33584
|
+
});
|
|
33029
33585
|
this.emit({
|
|
33030
33586
|
type: "agent:dormant",
|
|
33031
33587
|
payload: {
|
|
@@ -33040,13 +33596,15 @@ ${lines.join("\n")}`;
|
|
|
33040
33596
|
agentId,
|
|
33041
33597
|
scope: scopeKey(proc.scope),
|
|
33042
33598
|
droppedTaskCount: droppedAckIds.length,
|
|
33043
|
-
preservedInboxSize: this.dormantGroupInboxes.get(key)?.length ?? 0
|
|
33599
|
+
preservedInboxSize: this.dormantGroupInboxes.get(key)?.length ?? 0,
|
|
33600
|
+
fallbackId: effectiveFallbackId
|
|
33044
33601
|
});
|
|
33045
33602
|
}
|
|
33046
33603
|
proc.status = "dead";
|
|
33047
33604
|
this.agents.delete(key);
|
|
33048
33605
|
this.lastUsedAt.delete(key);
|
|
33049
33606
|
this.clearQuietFlushTimer(runtime);
|
|
33607
|
+
this.teardownSpectate(runtime);
|
|
33050
33608
|
try {
|
|
33051
33609
|
runtime.inputController.close();
|
|
33052
33610
|
await this.awaitQueryReturn(runtime.query, 5e3, agentId);
|
|
@@ -33063,6 +33621,165 @@ ${lines.join("\n")}`;
|
|
|
33063
33621
|
cwd: proc.cwd
|
|
33064
33622
|
});
|
|
33065
33623
|
}
|
|
33624
|
+
/**
|
|
33625
|
+
* Emit `agent:error` for the active reply and every queued/merged/buffered task,
|
|
33626
|
+
* then clear those queues. Used by both the SDK stream-crash path and the
|
|
33627
|
+
* reply-stall watchdog so a torn-down runtime never leaves a carrier reply
|
|
33628
|
+
* stuck in-flight on the server (which would keep absorbing new user messages
|
|
33629
|
+
* as steers of a dead turn).
|
|
33630
|
+
*/
|
|
33631
|
+
failPendingTasksWithError(runtime, errorText, fallbackId) {
|
|
33632
|
+
const pending = [];
|
|
33633
|
+
if (runtime.currentTask) pending.push(runtime.currentTask);
|
|
33634
|
+
pending.push(...runtime.injectedTasks, ...runtime.mergedTasks, ...runtime.planModeBuffer);
|
|
33635
|
+
runtime.currentTask = null;
|
|
33636
|
+
runtime.injectedTasks = [];
|
|
33637
|
+
runtime.mergedTasks = [];
|
|
33638
|
+
runtime.planModeBuffer = [];
|
|
33639
|
+
if (pending.length === 0) return { pendingCount: 0 };
|
|
33640
|
+
const carrier = pending[0];
|
|
33641
|
+
const mergedTasks = pending.slice(1);
|
|
33642
|
+
logger13.warn("Pending tasks failure consolidated", {
|
|
33643
|
+
agentId: runtime.agentId,
|
|
33644
|
+
scope: scopeKey(runtime.scope),
|
|
33645
|
+
pendingCount: pending.length,
|
|
33646
|
+
carrierAckId: carrier.replyMessageId,
|
|
33647
|
+
mergedAckIds: mergedTasks.map((t) => t.replyMessageId),
|
|
33648
|
+
traceId: carrier.traceId,
|
|
33649
|
+
...fallbackId ? { fallbackId } : {}
|
|
33650
|
+
});
|
|
33651
|
+
this.emit({
|
|
33652
|
+
type: "agent:error",
|
|
33653
|
+
payload: {
|
|
33654
|
+
agentId: runtime.agentId,
|
|
33655
|
+
conversationId: carrier.conversationId,
|
|
33656
|
+
ackId: carrier.replyMessageId,
|
|
33657
|
+
traceId: carrier.traceId,
|
|
33658
|
+
error: errorText
|
|
33659
|
+
}
|
|
33660
|
+
});
|
|
33661
|
+
for (const task of mergedTasks) {
|
|
33662
|
+
this.emit({
|
|
33663
|
+
type: "agent:merged",
|
|
33664
|
+
payload: {
|
|
33665
|
+
agentId: runtime.agentId,
|
|
33666
|
+
conversationId: task.conversationId,
|
|
33667
|
+
ackId: task.replyMessageId,
|
|
33668
|
+
mergedIntoAckId: carrier.replyMessageId,
|
|
33669
|
+
groupId: task.groupId,
|
|
33670
|
+
traceId: task.traceId
|
|
33671
|
+
}
|
|
33672
|
+
});
|
|
33673
|
+
}
|
|
33674
|
+
return { pendingCount: pending.length };
|
|
33675
|
+
}
|
|
33676
|
+
/**
|
|
33677
|
+
* Recover an in-flight reply that started but went silent past
|
|
33678
|
+
* `replyStallTimeoutMs` (see the reply-stall fast path in `evictIdle`). The
|
|
33679
|
+
* underlying SDK turn is wedged with no observable progress and no error, so:
|
|
33680
|
+
* 1. clear the (likely interrupted/dangling) session so the next dispatch
|
|
33681
|
+
* starts fresh instead of resuming the same wedged transcript;
|
|
33682
|
+
* 2. release the carrier reply + queued steers via `agent:error` so the
|
|
33683
|
+
* client stops waiting and the next user message starts a brand-new reply;
|
|
33684
|
+
* 3. tear the wedged runtime down.
|
|
33685
|
+
*/
|
|
33686
|
+
async recoverStalledReply(proc, silentMs) {
|
|
33687
|
+
if (proc.status === "dead") return;
|
|
33688
|
+
const runtime = this.asRuntime(proc);
|
|
33689
|
+
const key = runtimeKey(proc.agentId, proc.scope);
|
|
33690
|
+
const replyStallFallbackId = createFallbackId();
|
|
33691
|
+
const stallTraceId = proc.currentTask?.traceId;
|
|
33692
|
+
logger13.warn("Reply stall watchdog: in-flight reply silent too long, recovering", {
|
|
33693
|
+
agentId: proc.agentId,
|
|
33694
|
+
scope: scopeKey(proc.scope),
|
|
33695
|
+
silentMs,
|
|
33696
|
+
replyStallTimeoutMs: this.queryConfig.replyStallTimeoutMs,
|
|
33697
|
+
replyMessageId: proc.currentTask?.replyMessageId,
|
|
33698
|
+
injectedTaskCount: runtime.injectedTasks.length,
|
|
33699
|
+
lastSdkEventAt: new Date(proc.lastSdkEventAt).toISOString(),
|
|
33700
|
+
fallbackId: replyStallFallbackId,
|
|
33701
|
+
// Breadcrumb: what the wedged turn was doing the instant it went silent.
|
|
33702
|
+
// (subagent Task call? mid tool_use? which provider?) — the difference
|
|
33703
|
+
// between a one-off and a systemic provider/tool stall.
|
|
33704
|
+
model: proc.model ?? "(unknown)",
|
|
33705
|
+
lastSdkEvent: proc.lastSdkEventInfo,
|
|
33706
|
+
currentBlockType: proc.currentBlockType,
|
|
33707
|
+
currentToolName: proc.currentToolName,
|
|
33708
|
+
openMcpInvocationId: proc.currentMcpInvocationId ?? null
|
|
33709
|
+
});
|
|
33710
|
+
logFallback(logger13, {
|
|
33711
|
+
fallbackId: replyStallFallbackId,
|
|
33712
|
+
type: "reply_stall",
|
|
33713
|
+
phase: "detected",
|
|
33714
|
+
expected: false,
|
|
33715
|
+
traceId: stallTraceId,
|
|
33716
|
+
context: {
|
|
33717
|
+
agentId: proc.agentId,
|
|
33718
|
+
scope: scopeKey(proc.scope),
|
|
33719
|
+
silentMs,
|
|
33720
|
+
model: proc.model ?? "(unknown)",
|
|
33721
|
+
replyMessageId: proc.currentTask?.replyMessageId ?? null,
|
|
33722
|
+
currentToolName: proc.currentToolName ?? null,
|
|
33723
|
+
lastSdkEventType: proc.lastSdkEventInfo?.type ?? null,
|
|
33724
|
+
injectedTaskCount: runtime.injectedTasks.length,
|
|
33725
|
+
mergedTaskCount: runtime.mergedTasks.length,
|
|
33726
|
+
planModeBufferCount: runtime.planModeBuffer.length
|
|
33727
|
+
}
|
|
33728
|
+
});
|
|
33729
|
+
this.sessionStore.delete(proc.agentId, proc.scope);
|
|
33730
|
+
this.dispatchMemory.deleteScope(proc.agentId, proc.scope);
|
|
33731
|
+
const failSummary = this.failPendingTasksWithError(
|
|
33732
|
+
runtime,
|
|
33733
|
+
"\u56DE\u590D\u957F\u65F6\u95F4\u65E0\u54CD\u5E94\uFF0C\u5DF2\u91CD\u7F6E\u8BE5\u4F1A\u8BDD\uFF0C\u8BF7\u91CD\u65B0\u53D1\u9001\u6D88\u606F\u3002",
|
|
33734
|
+
replyStallFallbackId
|
|
33735
|
+
);
|
|
33736
|
+
proc.status = "dead";
|
|
33737
|
+
this.agents.delete(key);
|
|
33738
|
+
this.lastUsedAt.delete(key);
|
|
33739
|
+
this.clearQuietFlushTimer(runtime);
|
|
33740
|
+
let queryCloseOk = true;
|
|
33741
|
+
try {
|
|
33742
|
+
runtime.inputController.close();
|
|
33743
|
+
await this.awaitQueryReturn(runtime.query, 5e3, proc.agentId);
|
|
33744
|
+
} catch (e) {
|
|
33745
|
+
queryCloseOk = false;
|
|
33746
|
+
logger13.error("reply_stall: close query failed", {
|
|
33747
|
+
agentId: proc.agentId,
|
|
33748
|
+
scope: scopeKey(proc.scope),
|
|
33749
|
+
error: e
|
|
33750
|
+
});
|
|
33751
|
+
}
|
|
33752
|
+
logFallback(logger13, {
|
|
33753
|
+
fallbackId: replyStallFallbackId,
|
|
33754
|
+
type: "reply_stall",
|
|
33755
|
+
phase: "applied",
|
|
33756
|
+
expected: false,
|
|
33757
|
+
traceId: stallTraceId,
|
|
33758
|
+
context: {
|
|
33759
|
+
agentId: proc.agentId,
|
|
33760
|
+
scope: scopeKey(proc.scope),
|
|
33761
|
+
sessionDeleted: true,
|
|
33762
|
+
failedTaskCount: failSummary.pendingCount,
|
|
33763
|
+
queryClosed: queryCloseOk
|
|
33764
|
+
}
|
|
33765
|
+
});
|
|
33766
|
+
logFallback(logger13, {
|
|
33767
|
+
fallbackId: replyStallFallbackId,
|
|
33768
|
+
type: "reply_stall",
|
|
33769
|
+
phase: "outcome",
|
|
33770
|
+
expected: false,
|
|
33771
|
+
traceId: stallTraceId,
|
|
33772
|
+
context: {
|
|
33773
|
+
agentId: proc.agentId,
|
|
33774
|
+
scope: scopeKey(proc.scope),
|
|
33775
|
+
failedTaskCount: failSummary.pendingCount
|
|
33776
|
+
},
|
|
33777
|
+
outcome: {
|
|
33778
|
+
result: "session_reset_awaiting_user",
|
|
33779
|
+
dataLossSuspected: failSummary.pendingCount > 0
|
|
33780
|
+
}
|
|
33781
|
+
});
|
|
33782
|
+
}
|
|
33066
33783
|
async recoverFromRestart(agents) {
|
|
33067
33784
|
const lockSnapshot = readCronLockSnapshot();
|
|
33068
33785
|
logger13.info("Recovering Agent sessions after restart", {
|
|
@@ -33185,58 +33902,7 @@ ${lines.join("\n")}`;
|
|
|
33185
33902
|
this.lastUsedAt.delete(key);
|
|
33186
33903
|
const errorText = isResumeFail ? `\u4F1A\u8BDD\u5DF2\u8FC7\u671F\uFF0C\u8BF7\u91CD\u65B0\u53D1\u9001\u6D88\u606F\uFF08${errMsg}\uFF09` : `Agent query crashed: ${errMsg}`;
|
|
33187
33904
|
const emittedErrorText = isUnsupportedVisionInput ? "Current model/backend does not support image multimodal input. This image message failed; AHChat has cleared this scope session and future messages will send attachments as paths/text references." : errorText;
|
|
33188
|
-
|
|
33189
|
-
this.emit({
|
|
33190
|
-
type: "agent:error",
|
|
33191
|
-
payload: {
|
|
33192
|
-
agentId: runtime.agentId,
|
|
33193
|
-
conversationId: runtime.currentTask.conversationId,
|
|
33194
|
-
ackId: runtime.currentTask.replyMessageId,
|
|
33195
|
-
traceId: runtime.currentTask.traceId,
|
|
33196
|
-
error: emittedErrorText
|
|
33197
|
-
}
|
|
33198
|
-
});
|
|
33199
|
-
runtime.currentTask = null;
|
|
33200
|
-
}
|
|
33201
|
-
for (const task of runtime.injectedTasks) {
|
|
33202
|
-
this.emit({
|
|
33203
|
-
type: "agent:error",
|
|
33204
|
-
payload: {
|
|
33205
|
-
agentId: runtime.agentId,
|
|
33206
|
-
conversationId: task.conversationId,
|
|
33207
|
-
ackId: task.replyMessageId,
|
|
33208
|
-
traceId: task.traceId,
|
|
33209
|
-
error: emittedErrorText
|
|
33210
|
-
}
|
|
33211
|
-
});
|
|
33212
|
-
}
|
|
33213
|
-
runtime.injectedTasks = [];
|
|
33214
|
-
for (const task of runtime.mergedTasks) {
|
|
33215
|
-
this.emit({
|
|
33216
|
-
type: "agent:error",
|
|
33217
|
-
payload: {
|
|
33218
|
-
agentId: runtime.agentId,
|
|
33219
|
-
conversationId: task.conversationId,
|
|
33220
|
-
ackId: task.replyMessageId,
|
|
33221
|
-
traceId: task.traceId,
|
|
33222
|
-
error: emittedErrorText
|
|
33223
|
-
}
|
|
33224
|
-
});
|
|
33225
|
-
}
|
|
33226
|
-
runtime.mergedTasks = [];
|
|
33227
|
-
for (const task of runtime.planModeBuffer) {
|
|
33228
|
-
this.emit({
|
|
33229
|
-
type: "agent:error",
|
|
33230
|
-
payload: {
|
|
33231
|
-
agentId: runtime.agentId,
|
|
33232
|
-
conversationId: task.conversationId,
|
|
33233
|
-
ackId: task.replyMessageId,
|
|
33234
|
-
traceId: task.traceId,
|
|
33235
|
-
error: emittedErrorText
|
|
33236
|
-
}
|
|
33237
|
-
});
|
|
33238
|
-
}
|
|
33239
|
-
runtime.planModeBuffer = [];
|
|
33905
|
+
this.failPendingTasksWithError(runtime, emittedErrorText);
|
|
33240
33906
|
}
|
|
33241
33907
|
}
|
|
33242
33908
|
getStatus(agentId, scope = { kind: "single" }) {
|
|
@@ -33250,6 +33916,18 @@ ${lines.join("\n")}`;
|
|
|
33250
33916
|
}
|
|
33251
33917
|
return [...ids];
|
|
33252
33918
|
}
|
|
33919
|
+
/** Unified signal: is the turn legitimately waiting on a live external call that emits no
|
|
33920
|
+
* parent heartbeat? Used by BOTH the reply-stall fast path and the zombie watchdog so neither
|
|
33921
|
+
* tears down a turn that is merely slow. Returns the reason for diagnostics, or null when idle.
|
|
33922
|
+
* - open_tool: regular tool, MCP tool, AskUserQuestion wait, or ExitPlanMode (tool_use open).
|
|
33923
|
+
* - subagent: Task/Agent in flight (its inner tool_results clear activeToolUseStartedAt).
|
|
33924
|
+
* - compact: bridge-injected /compact running. */
|
|
33925
|
+
busyReason(proc) {
|
|
33926
|
+
if (proc.activeToolUseStartedAt != null || this.latestOpenToolUse(proc) != null) return "open_tool";
|
|
33927
|
+
if ((proc.activeSubagentTaskIds?.size ?? 0) > 0) return "subagent";
|
|
33928
|
+
if (proc.compactInProgress === true) return "compact";
|
|
33929
|
+
return null;
|
|
33930
|
+
}
|
|
33253
33931
|
latestOpenToolUse(proc) {
|
|
33254
33932
|
for (let i = proc.contentBlocks.length - 1; i >= 0; i -= 1) {
|
|
33255
33933
|
const block = proc.contentBlocks[i];
|
|
@@ -33398,7 +34076,7 @@ ${lines.join("\n")}`;
|
|
|
33398
34076
|
}
|
|
33399
34077
|
const task = {
|
|
33400
34078
|
content: notice,
|
|
33401
|
-
replyMessageId:
|
|
34079
|
+
replyMessageId: createScopeNoticeReplyMessageId(),
|
|
33402
34080
|
conversationId,
|
|
33403
34081
|
traceId: createTraceId(),
|
|
33404
34082
|
groupId: proc.scope.kind === "group" ? proc.scope.groupId : void 0
|
|
@@ -34480,6 +35158,7 @@ var HttpMcpRegistry = class {
|
|
|
34480
35158
|
buildForAgent(ctx) {
|
|
34481
35159
|
const mcpServers = {};
|
|
34482
35160
|
const allowedTools = [];
|
|
35161
|
+
const toolAbi = [];
|
|
34483
35162
|
const usedNames = /* @__PURE__ */ new Set();
|
|
34484
35163
|
for (const connection of this.allConnections()) {
|
|
34485
35164
|
if (!this.connectionAppliesToAgent(connection, ctx)) continue;
|
|
@@ -34488,12 +35167,18 @@ var HttpMcpRegistry = class {
|
|
|
34488
35167
|
const serverName = uniqueServerName(normalizeMcpServerName(connection.serverName), usedNames);
|
|
34489
35168
|
usedNames.add(serverName);
|
|
34490
35169
|
mcpServers[serverName] = sdkConfig;
|
|
34491
|
-
|
|
34492
|
-
|
|
34493
|
-
|
|
34494
|
-
|
|
35170
|
+
const visibleTools = connection.tools.filter((tool2) => tool2.enabled && tool2.permissionPolicy !== "always_deny");
|
|
35171
|
+
for (const tool2 of visibleTools) allowedTools.push(mcpRuntimeToolName(serverName, tool2.name));
|
|
35172
|
+
toolAbi.push({
|
|
35173
|
+
serverName,
|
|
35174
|
+
providerId: connection.providerId,
|
|
35175
|
+
transport: connection.transport,
|
|
35176
|
+
alwaysLoad: connection.alwaysLoad,
|
|
35177
|
+
isBuiltin: connection.isBuiltin,
|
|
35178
|
+
tools: visibleTools.map((tool2) => runtimeToolAbi(serverName, tool2)).sort((a, b) => a.runtimeToolName.localeCompare(b.runtimeToolName))
|
|
35179
|
+
});
|
|
34495
35180
|
}
|
|
34496
|
-
return { mcpServers, allowedTools };
|
|
35181
|
+
return { mcpServers, allowedTools, toolAbi };
|
|
34497
35182
|
}
|
|
34498
35183
|
allConnections() {
|
|
34499
35184
|
return [...this.serverConnections.values(), ...this.localConnections.values()];
|
|
@@ -34679,6 +35364,18 @@ function uniqueServerName(serverName, usedNames) {
|
|
|
34679
35364
|
while (usedNames.has(`${serverName}_${idx}`)) idx += 1;
|
|
34680
35365
|
return `${serverName}_${idx}`;
|
|
34681
35366
|
}
|
|
35367
|
+
function runtimeToolAbi(serverName, tool2) {
|
|
35368
|
+
return {
|
|
35369
|
+
name: tool2.name,
|
|
35370
|
+
runtimeToolName: mcpRuntimeToolName(serverName, tool2.name),
|
|
35371
|
+
displayName: tool2.displayName,
|
|
35372
|
+
description: tool2.description,
|
|
35373
|
+
category: tool2.category,
|
|
35374
|
+
riskLevel: tool2.riskLevel,
|
|
35375
|
+
permissionPolicy: tool2.permissionPolicy,
|
|
35376
|
+
...tool2.inputSchema !== void 0 ? { inputSchema: tool2.inputSchema } : {}
|
|
35377
|
+
};
|
|
35378
|
+
}
|
|
34682
35379
|
function buildHeaders(authType, authSecret, customHeaders) {
|
|
34683
35380
|
const headers = {};
|
|
34684
35381
|
for (const header of customHeaders) {
|
|
@@ -35330,6 +36027,7 @@ var ServerConnector = class {
|
|
|
35330
36027
|
case "agent:terminate":
|
|
35331
36028
|
case "agent:runtime_reload":
|
|
35332
36029
|
case "agent:terminate_scope":
|
|
36030
|
+
case "spectate:set":
|
|
35333
36031
|
case "agent:created":
|
|
35334
36032
|
case "agent:updated":
|
|
35335
36033
|
case "agent:workdir-updated":
|
|
@@ -36305,24 +37003,6 @@ function normalizeLocalPath(targetPath) {
|
|
|
36305
37003
|
const expanded = trimmed === "~" || trimmed.startsWith("~/") || trimmed.startsWith("~\\") ? path18.join(os10.homedir(), trimmed.slice(2)) : trimmed;
|
|
36306
37004
|
return path18.normalize(path18.resolve(expanded));
|
|
36307
37005
|
}
|
|
36308
|
-
function isAbsoluteLocalPathCandidate(value) {
|
|
36309
|
-
if (process.platform === "win32") {
|
|
36310
|
-
return /^[a-zA-Z]:[\\/]/.test(value) || /^\\\\[^\\]/.test(value);
|
|
36311
|
-
}
|
|
36312
|
-
return value.startsWith("/");
|
|
36313
|
-
}
|
|
36314
|
-
function clipboardTextToPathCandidates(value) {
|
|
36315
|
-
return value.replace(/\0/g, "\n").split(/\r?\n/).map((line) => line.trim().replace(/^"(.+)"$/, "$1")).map((line) => {
|
|
36316
|
-
if (!line.toLowerCase().startsWith("file://")) return line;
|
|
36317
|
-
try {
|
|
36318
|
-
const url2 = new URL(line);
|
|
36319
|
-
return decodeURIComponent(url2.pathname.replace(/^\/([a-zA-Z]:\/)/, "$1"));
|
|
36320
|
-
} catch (e) {
|
|
36321
|
-
logger24.debug("Failed to parse clipboard file URL", { error: e });
|
|
36322
|
-
return "";
|
|
36323
|
-
}
|
|
36324
|
-
}).filter((line) => line.length > 0 && isAbsoluteLocalPathCandidate(line));
|
|
36325
|
-
}
|
|
36326
37006
|
function normalizeClipboardIdentityKey(value) {
|
|
36327
37007
|
return process.platform === "win32" ? value.toLowerCase() : value;
|
|
36328
37008
|
}
|
|
@@ -36360,18 +37040,15 @@ function mimeTypeForFileName(fileName) {
|
|
|
36360
37040
|
}
|
|
36361
37041
|
function parseWindowsClipboardResult(stdout) {
|
|
36362
37042
|
const raw = stdout.trim();
|
|
36363
|
-
if (!raw) return { files: []
|
|
37043
|
+
if (!raw) return { files: [] };
|
|
36364
37044
|
try {
|
|
36365
37045
|
const parsed = JSON.parse(raw);
|
|
36366
|
-
if (!isRecord5(parsed)) return { files: []
|
|
37046
|
+
if (!isRecord5(parsed)) return { files: [] };
|
|
36367
37047
|
const files = Array.isArray(parsed.files) ? parsed.files.filter((item) => typeof item === "string") : [];
|
|
36368
|
-
return {
|
|
36369
|
-
files,
|
|
36370
|
-
text: typeof parsed.text === "string" ? parsed.text : ""
|
|
36371
|
-
};
|
|
37048
|
+
return { files };
|
|
36372
37049
|
} catch (e) {
|
|
36373
37050
|
logger24.debug("Windows clipboard JSON parse skipped", { error: e });
|
|
36374
|
-
return { files:
|
|
37051
|
+
return { files: [] };
|
|
36375
37052
|
}
|
|
36376
37053
|
}
|
|
36377
37054
|
async function readWindowsClipboardPathCandidates() {
|
|
@@ -36384,9 +37061,7 @@ async function readWindowsClipboardPathCandidates() {
|
|
|
36384
37061
|
" $drop = [System.Windows.Forms.Clipboard]::GetFileDropList();",
|
|
36385
37062
|
" foreach ($file in $drop) { $files += [string]$file }",
|
|
36386
37063
|
"} catch {}",
|
|
36387
|
-
|
|
36388
|
-
"try { $text = [System.Windows.Forms.Clipboard]::GetText() } catch {}",
|
|
36389
|
-
"[pscustomobject]@{ files = $files; text = $text } | ConvertTo-Json -Compress;"
|
|
37064
|
+
"[pscustomobject]@{ files = $files } | ConvertTo-Json -Compress;"
|
|
36390
37065
|
].join(" ");
|
|
36391
37066
|
try {
|
|
36392
37067
|
const { stdout } = await execFileAsync2("powershell.exe", [
|
|
@@ -36404,7 +37079,7 @@ async function readWindowsClipboardPathCandidates() {
|
|
|
36404
37079
|
maxBuffer: 1024 * 1024
|
|
36405
37080
|
});
|
|
36406
37081
|
const result = parseWindowsClipboardResult(stdout);
|
|
36407
|
-
return
|
|
37082
|
+
return result.files;
|
|
36408
37083
|
} catch (e) {
|
|
36409
37084
|
logger24.debug("Windows clipboard file read skipped", { error: e });
|
|
36410
37085
|
return [];
|
|
@@ -36807,7 +37482,7 @@ async function readStreamText(filePath, start) {
|
|
|
36807
37482
|
function parseProcessedLines(raw, cursor, fileName, fingerprint) {
|
|
36808
37483
|
const lastNewline = raw.lastIndexOf("\n");
|
|
36809
37484
|
if (lastNewline < 0) {
|
|
36810
|
-
return { entries: [], nextCursor: cursor, advanced: false };
|
|
37485
|
+
return { entries: [], nextCursor: cursor, advanced: false, reason: "partial_line" };
|
|
36811
37486
|
}
|
|
36812
37487
|
const processed = raw.slice(0, lastNewline + 1);
|
|
36813
37488
|
const lines = processed.split(/\r?\n/);
|
|
@@ -36833,7 +37508,8 @@ function parseProcessedLines(raw, cursor, fileName, fingerprint) {
|
|
|
36833
37508
|
lineNum,
|
|
36834
37509
|
...fingerprint ? { fingerprint } : {}
|
|
36835
37510
|
},
|
|
36836
|
-
advanced: true
|
|
37511
|
+
advanced: true,
|
|
37512
|
+
reason: "advanced"
|
|
36837
37513
|
};
|
|
36838
37514
|
}
|
|
36839
37515
|
function chunkEntries(entries, size) {
|
|
@@ -36891,24 +37567,55 @@ var BridgeLogUploader = class {
|
|
|
36891
37567
|
async flushOnce() {
|
|
36892
37568
|
if (this.running || this.stopped) return;
|
|
36893
37569
|
this.running = true;
|
|
37570
|
+
const startedAt = Date.now();
|
|
37571
|
+
const summary = {
|
|
37572
|
+
targetCount: 0,
|
|
37573
|
+
advancedTargetCount: 0,
|
|
37574
|
+
missingTargetCount: 0,
|
|
37575
|
+
idleTargetCount: 0,
|
|
37576
|
+
partialLineTargetCount: 0,
|
|
37577
|
+
failedTargetCount: 0,
|
|
37578
|
+
parsedEntryCount: 0,
|
|
37579
|
+
bridgeEntryCount: 0,
|
|
37580
|
+
uploadedChunkCount: 0,
|
|
37581
|
+
accepted: 0,
|
|
37582
|
+
skipped: 0
|
|
37583
|
+
};
|
|
36894
37584
|
try {
|
|
36895
37585
|
const targets = await this.resolveTargets();
|
|
37586
|
+
summary.targetCount = targets.length;
|
|
36896
37587
|
for (const target of targets) {
|
|
36897
37588
|
try {
|
|
36898
37589
|
const cursor = await readCursor(target.cursorFile);
|
|
36899
37590
|
const batch = await this.readNewEntries(target, cursor);
|
|
36900
|
-
if (!batch.advanced)
|
|
37591
|
+
if (!batch.advanced) {
|
|
37592
|
+
summary.idleTargetCount += 1;
|
|
37593
|
+
if (batch.reason === "missing_file") summary.missingTargetCount += 1;
|
|
37594
|
+
if (batch.reason === "partial_line") summary.partialLineTargetCount += 1;
|
|
37595
|
+
continue;
|
|
37596
|
+
}
|
|
37597
|
+
summary.advancedTargetCount += 1;
|
|
37598
|
+
summary.parsedEntryCount += batch.entries.length;
|
|
36901
37599
|
if (batch.entries.length > 0) {
|
|
36902
|
-
await this.uploadEntries(batch.entries);
|
|
37600
|
+
const result = await this.uploadEntries(batch.entries);
|
|
37601
|
+
summary.bridgeEntryCount += result.bridgeEntryCount;
|
|
37602
|
+
summary.uploadedChunkCount += result.uploadedChunkCount;
|
|
37603
|
+
summary.accepted += result.accepted;
|
|
37604
|
+
summary.skipped += result.skipped;
|
|
36903
37605
|
}
|
|
36904
37606
|
await writeCursor(target.cursorFile, batch.nextCursor);
|
|
36905
37607
|
} catch (e) {
|
|
37608
|
+
summary.failedTargetCount += 1;
|
|
36906
37609
|
logger27.warn("Bridge log upload target failed", { error: e, logFile: target.logFile });
|
|
36907
37610
|
}
|
|
36908
37611
|
}
|
|
36909
37612
|
} catch (e) {
|
|
36910
37613
|
logger27.warn("Bridge log upload cycle failed", { error: e });
|
|
36911
37614
|
} finally {
|
|
37615
|
+
logger27.info("Bridge log upload cycle summary", {
|
|
37616
|
+
...summary,
|
|
37617
|
+
durationMs: Date.now() - startedAt
|
|
37618
|
+
});
|
|
36912
37619
|
this.running = false;
|
|
36913
37620
|
}
|
|
36914
37621
|
}
|
|
@@ -36931,7 +37638,7 @@ var BridgeLogUploader = class {
|
|
|
36931
37638
|
} catch (e) {
|
|
36932
37639
|
if (e instanceof Error && "code" in e && e.code === "ENOENT") {
|
|
36933
37640
|
logger27.debug("Bridge log file not found for upload yet", { logFile: target.logFile });
|
|
36934
|
-
return { entries: [], nextCursor: cursor, advanced: false };
|
|
37641
|
+
return { entries: [], nextCursor: cursor, advanced: false, reason: "missing_file" };
|
|
36935
37642
|
}
|
|
36936
37643
|
throw e;
|
|
36937
37644
|
}
|
|
@@ -36939,13 +37646,19 @@ var BridgeLogUploader = class {
|
|
|
36939
37646
|
const samePhysicalFile = !fingerprint || !cursor.fingerprint || cursor.fingerprint === fingerprint;
|
|
36940
37647
|
const normalizedCursor = stat3.size < cursor.offset || !samePhysicalFile ? { offset: 0, lineNum: 0, ...fingerprint ? { fingerprint } : {} } : { ...cursor, ...fingerprint ? { fingerprint } : {} };
|
|
36941
37648
|
if (stat3.size <= normalizedCursor.offset) {
|
|
36942
|
-
return { entries: [], nextCursor: normalizedCursor, advanced: false };
|
|
37649
|
+
return { entries: [], nextCursor: normalizedCursor, advanced: false, reason: "no_new_entries" };
|
|
36943
37650
|
}
|
|
36944
37651
|
const raw = await readStreamText(target.logFile, normalizedCursor.offset);
|
|
36945
37652
|
return parseProcessedLines(raw, normalizedCursor, target.uploadedFileName, fingerprint);
|
|
36946
37653
|
}
|
|
36947
37654
|
async uploadEntries(entries) {
|
|
36948
37655
|
const bridgeEntries = entries.filter((entry) => entry.source === "bridge");
|
|
37656
|
+
const result = {
|
|
37657
|
+
bridgeEntryCount: bridgeEntries.length,
|
|
37658
|
+
uploadedChunkCount: 0,
|
|
37659
|
+
accepted: 0,
|
|
37660
|
+
skipped: 0
|
|
37661
|
+
};
|
|
36949
37662
|
for (const chunk of chunkEntries(bridgeEntries, this.options.batchSize)) {
|
|
36950
37663
|
const res = await fetch(`${this.options.serverApiUrl}/api/logs/upload`, {
|
|
36951
37664
|
method: "POST",
|
|
@@ -36960,14 +37673,18 @@ var BridgeLogUploader = class {
|
|
|
36960
37673
|
})
|
|
36961
37674
|
});
|
|
36962
37675
|
if (!res.ok) {
|
|
36963
|
-
const
|
|
37676
|
+
const body2 = await res.text().catch((e) => {
|
|
36964
37677
|
logger27.debug("Failed to read log upload error body", { error: e });
|
|
36965
37678
|
return "";
|
|
36966
37679
|
});
|
|
36967
|
-
throw new Error(`upload failed HTTP ${res.status}: ${
|
|
37680
|
+
throw new Error(`upload failed HTTP ${res.status}: ${body2.slice(0, 160)}`);
|
|
36968
37681
|
}
|
|
36969
|
-
await res.json();
|
|
37682
|
+
const body = await res.json();
|
|
37683
|
+
result.uploadedChunkCount += 1;
|
|
37684
|
+
result.accepted += typeof body.accepted === "number" ? body.accepted : 0;
|
|
37685
|
+
result.skipped += typeof body.skipped === "number" ? body.skipped : 0;
|
|
36970
37686
|
}
|
|
37687
|
+
return result;
|
|
36971
37688
|
}
|
|
36972
37689
|
};
|
|
36973
37690
|
|
|
@@ -38052,8 +38769,10 @@ function resolvePnpmRuntimeBinary(sdkDir, target) {
|
|
|
38052
38769
|
} catch {
|
|
38053
38770
|
return void 0;
|
|
38054
38771
|
}
|
|
38772
|
+
const matches = [];
|
|
38055
38773
|
for (const entry of entries) {
|
|
38056
38774
|
if (!entry.startsWith(`${encodedName}@`)) continue;
|
|
38775
|
+
const version2 = entry.slice(encodedName.length + 1);
|
|
38057
38776
|
const candidate = path27.join(
|
|
38058
38777
|
pnpmStoreDir,
|
|
38059
38778
|
entry,
|
|
@@ -38061,9 +38780,22 @@ function resolvePnpmRuntimeBinary(sdkDir, target) {
|
|
|
38061
38780
|
...target.packageName.split("/"),
|
|
38062
38781
|
target.binaryName
|
|
38063
38782
|
);
|
|
38064
|
-
if (existsSync2(candidate))
|
|
38783
|
+
if (existsSync2(candidate)) matches.push({ version: version2, candidate });
|
|
38065
38784
|
}
|
|
38066
|
-
return void 0;
|
|
38785
|
+
if (matches.length === 0) return void 0;
|
|
38786
|
+
matches.sort((a, b) => compareRuntimeVersion(b.version, a.version));
|
|
38787
|
+
return matches[0].candidate;
|
|
38788
|
+
}
|
|
38789
|
+
function compareRuntimeVersion(a, b) {
|
|
38790
|
+
const parse3 = (v) => v.split(/[.+_-]/).map((n) => Number.parseInt(n, 10));
|
|
38791
|
+
const pa = parse3(a);
|
|
38792
|
+
const pb = parse3(b);
|
|
38793
|
+
for (let i = 0; i < Math.max(pa.length, pb.length); i += 1) {
|
|
38794
|
+
const da = Number.isFinite(pa[i]) ? pa[i] : 0;
|
|
38795
|
+
const db = Number.isFinite(pb[i]) ? pb[i] : 0;
|
|
38796
|
+
if (da !== db) return da - db;
|
|
38797
|
+
}
|
|
38798
|
+
return 0;
|
|
38067
38799
|
}
|
|
38068
38800
|
function resolveSdkRuntimeBinary(target) {
|
|
38069
38801
|
const directPath = safeResolve(`${target.packageName}/${target.binaryName}`);
|
|
@@ -39222,6 +39954,32 @@ function syncLocalRuntimeSkills(skillStore, localSkills, options = {}) {
|
|
|
39222
39954
|
]);
|
|
39223
39955
|
}
|
|
39224
39956
|
|
|
39957
|
+
// src/processOutput.ts
|
|
39958
|
+
var protectedStreams2 = /* @__PURE__ */ new WeakSet();
|
|
39959
|
+
var reportedErrors = /* @__PURE__ */ new WeakSet();
|
|
39960
|
+
function reportWriteError(error51, onError) {
|
|
39961
|
+
if (typeof error51 === "object" && error51 !== null) {
|
|
39962
|
+
if (reportedErrors.has(error51)) return;
|
|
39963
|
+
reportedErrors.add(error51);
|
|
39964
|
+
}
|
|
39965
|
+
onError(error51);
|
|
39966
|
+
}
|
|
39967
|
+
function safeWriteProcessOutput(stream, text, onError) {
|
|
39968
|
+
if (!stream) return;
|
|
39969
|
+
if (stream.destroyed || stream.writableEnded) return;
|
|
39970
|
+
if (typeof stream === "object" && typeof stream.on === "function" && !protectedStreams2.has(stream)) {
|
|
39971
|
+
protectedStreams2.add(stream);
|
|
39972
|
+
stream.on("error", (e) => reportWriteError(e, onError));
|
|
39973
|
+
}
|
|
39974
|
+
try {
|
|
39975
|
+
stream.write(text, (error51) => {
|
|
39976
|
+
if (error51) reportWriteError(error51, onError);
|
|
39977
|
+
});
|
|
39978
|
+
} catch (e) {
|
|
39979
|
+
reportWriteError(e, onError);
|
|
39980
|
+
}
|
|
39981
|
+
}
|
|
39982
|
+
|
|
39225
39983
|
// src/start.ts
|
|
39226
39984
|
var logger41 = createModuleLogger("bridge");
|
|
39227
39985
|
var NODE_USER_UID2 = 1e3;
|
|
@@ -39308,14 +40066,16 @@ async function startBridge(config2) {
|
|
|
39308
40066
|
const claudeRuntime = resolveClaudeRuntime();
|
|
39309
40067
|
logClaudeRuntimeResolution(claudeRuntime);
|
|
39310
40068
|
if (!claudeRuntime.ok || !claudeRuntime.path) {
|
|
39311
|
-
|
|
40069
|
+
safeWriteProcessOutput(
|
|
40070
|
+
process.stderr,
|
|
39312
40071
|
`
|
|
39313
40072
|
Claude runtime is unavailable.
|
|
39314
40073
|
|
|
39315
40074
|
${claudeRuntime.error ?? "Install Claude Code manually or use the bundled desktop runtime."}
|
|
39316
40075
|
|
|
39317
40076
|
Reinstall @fangyb/ahchat-bridge with npm optional dependencies, set AHCHAT_CLAUDE_EXECUTABLE to a valid Claude Code binary path, install Claude Code, or use ALL-CAN Desktop with its bundled runtime.
|
|
39318
|
-
|
|
40077
|
+
`,
|
|
40078
|
+
(e) => logger41.error("Bridge process stderr write failed", { error: e })
|
|
39319
40079
|
);
|
|
39320
40080
|
process.exit(1);
|
|
39321
40081
|
}
|
|
@@ -39372,12 +40132,14 @@ Reinstall @fangyb/ahchat-bridge with npm optional dependencies, set AHCHAT_CLAUD
|
|
|
39372
40132
|
claudeRuntimeVersion: claudeRuntime.version ?? null
|
|
39373
40133
|
});
|
|
39374
40134
|
const shouldPrintRawBridgeToken = process.stdout.isTTY && process.env.AHCHAT_SUPPRESS_BRIDGE_TOKEN_STDOUT !== "1";
|
|
39375
|
-
|
|
40135
|
+
safeWriteProcessOutput(
|
|
40136
|
+
process.stdout,
|
|
39376
40137
|
`
|
|
39377
40138
|
Bridge token (register this machine at Settings \u2192 \u5DF2\u8FDE\u63A5\u7684\u673A\u5668):
|
|
39378
40139
|
${shouldPrintRawBridgeToken ? config2.bridgeToken : "***"}
|
|
39379
40140
|
|
|
39380
|
-
|
|
40141
|
+
`,
|
|
40142
|
+
(e) => logger41.error("Bridge process stdout write failed", { error: e })
|
|
39381
40143
|
);
|
|
39382
40144
|
wsMetrics.start(5e3);
|
|
39383
40145
|
const sessionStore = new SessionStore(config2.dataDir);
|
|
@@ -40176,6 +40938,19 @@ Bridge token (register this machine at Settings \u2192 \u5DF2\u8FDE\u63A5\u7684\
|
|
|
40176
40938
|
});
|
|
40177
40939
|
await agentManager.terminateScope(msg.payload.agentId, msg.payload.scope);
|
|
40178
40940
|
break;
|
|
40941
|
+
case "spectate:set":
|
|
40942
|
+
logger41.info("spectate:set received", {
|
|
40943
|
+
agentId: msg.payload.agentId,
|
|
40944
|
+
scope: msg.payload.scope,
|
|
40945
|
+
action: msg.payload.action,
|
|
40946
|
+
traceId: msg.payload.traceId
|
|
40947
|
+
});
|
|
40948
|
+
await agentManager.setSpectate(
|
|
40949
|
+
msg.payload.agentId,
|
|
40950
|
+
msg.payload.scope,
|
|
40951
|
+
msg.payload.action
|
|
40952
|
+
);
|
|
40953
|
+
break;
|
|
40179
40954
|
case "agent:created":
|
|
40180
40955
|
agentRegistry.upsert(msg.payload.agent);
|
|
40181
40956
|
ensureLocalWorkdirPath(msg.payload.agent.workingDirectory, "agent:created", {
|
|
@@ -40392,8 +41167,12 @@ function writeAlreadyRunningMessage(error51) {
|
|
|
40392
41167
|
` ${buildStopCommand(error51.pid)}`,
|
|
40393
41168
|
""
|
|
40394
41169
|
];
|
|
40395
|
-
|
|
40396
|
-
|
|
41170
|
+
safeWriteProcessOutput(
|
|
41171
|
+
process.stdout,
|
|
41172
|
+
`${lines.join("\n")}
|
|
41173
|
+
`,
|
|
41174
|
+
(e) => logger42.error("Bridge already-running message write failed", { error: e })
|
|
41175
|
+
);
|
|
40397
41176
|
}
|
|
40398
41177
|
function handleBridgeStartError(e, message) {
|
|
40399
41178
|
if (isBridgeAlreadyRunningError(e)) {
|