@fangyb/ahchat-bridge 0.1.35 → 0.1.36
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 +961 -180
- package/dist/feedbackWorkerCli.cjs +165 -8
- package/dist/index.js +956 -180
- 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."),
|
|
@@ -28230,6 +28427,9 @@ function emitUsageReported(proc, emit, base, usage, messageId) {
|
|
|
28230
28427
|
function isGroupTask(proc) {
|
|
28231
28428
|
return proc.currentTask?.groupId != null;
|
|
28232
28429
|
}
|
|
28430
|
+
function shouldStreamInternals(proc) {
|
|
28431
|
+
return !isGroupTask(proc) || proc.spectating === true;
|
|
28432
|
+
}
|
|
28233
28433
|
function extractTodosFromInput(input) {
|
|
28234
28434
|
if (!input || typeof input !== "object") return null;
|
|
28235
28435
|
const raw = input.todos;
|
|
@@ -28361,7 +28561,6 @@ function emitGroupSegment(proc, emit, base, content, contentBlocks, isSilent = f
|
|
|
28361
28561
|
contentLen: content.length,
|
|
28362
28562
|
blockCount: contentBlocks.length,
|
|
28363
28563
|
blockTypes: contentBlocks.map((b) => b.type),
|
|
28364
|
-
contentSample: content.slice(0, 200),
|
|
28365
28564
|
traceId: base.traceId,
|
|
28366
28565
|
isAuditOnly: content.length === 0,
|
|
28367
28566
|
isSilent
|
|
@@ -28417,9 +28616,24 @@ function flushTextSegmentOnBlockStop(proc, emit, base) {
|
|
|
28417
28616
|
}
|
|
28418
28617
|
proc.segmentBuffer = "";
|
|
28419
28618
|
}
|
|
28619
|
+
function describeSdkEvent(message) {
|
|
28620
|
+
const rec = message;
|
|
28621
|
+
const str = (v) => typeof v === "string" && v.length > 0 ? v : void 0;
|
|
28622
|
+
return {
|
|
28623
|
+
type: str(rec.type) ?? "unknown",
|
|
28624
|
+
subtype: str(rec.subtype),
|
|
28625
|
+
toolName: str(rec.last_tool_name),
|
|
28626
|
+
subagentType: str(rec.subagent_type),
|
|
28627
|
+
toolUseId: str(rec.tool_use_id),
|
|
28628
|
+
taskId: str(rec.task_id),
|
|
28629
|
+
at: Date.now()
|
|
28630
|
+
};
|
|
28631
|
+
}
|
|
28420
28632
|
function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onProviderApiError) {
|
|
28421
28633
|
const emit = rawEmit;
|
|
28422
28634
|
proc.lastSdkEventAt = Date.now();
|
|
28635
|
+
proc.lastSdkEventInfo = describeSdkEvent(message);
|
|
28636
|
+
proc.stallWarned = false;
|
|
28423
28637
|
switch (message.type) {
|
|
28424
28638
|
case "system": {
|
|
28425
28639
|
const sysMsg = message;
|
|
@@ -28458,11 +28672,29 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onProv
|
|
|
28458
28672
|
sessionId: proc.ccSessionId
|
|
28459
28673
|
});
|
|
28460
28674
|
} else {
|
|
28675
|
+
const sysRec = sysMsg;
|
|
28676
|
+
const pick2 = (k) => typeof sysRec[k] === "string" || typeof sysRec[k] === "number" ? sysRec[k] : void 0;
|
|
28677
|
+
const descriptionLen = typeof sysRec.description === "string" ? sysRec.description.length : void 0;
|
|
28678
|
+
const subagentTaskId = typeof sysRec.task_id === "string" ? sysRec.task_id : void 0;
|
|
28679
|
+
if (subagentTaskId) {
|
|
28680
|
+
if (sysMsg.subtype === "task_started") {
|
|
28681
|
+
(proc.activeSubagentTaskIds ??= /* @__PURE__ */ new Set()).add(subagentTaskId);
|
|
28682
|
+
} else if (sysMsg.subtype === "task_notification") {
|
|
28683
|
+
proc.activeSubagentTaskIds?.delete(subagentTaskId);
|
|
28684
|
+
}
|
|
28685
|
+
}
|
|
28461
28686
|
logger10.info("SDK system subtype unhandled", {
|
|
28462
28687
|
agentId: proc.agentId,
|
|
28463
28688
|
scope: proc.scope.kind === "single" ? "single" : proc.scope.groupId,
|
|
28464
28689
|
subtype: sysMsg.subtype ?? "(none)",
|
|
28465
|
-
|
|
28690
|
+
taskId: pick2("task_id"),
|
|
28691
|
+
toolUseId: pick2("tool_use_id"),
|
|
28692
|
+
subagentType: pick2("subagent_type"),
|
|
28693
|
+
taskType: pick2("task_type"),
|
|
28694
|
+
lastToolName: pick2("last_tool_name"),
|
|
28695
|
+
hasDescription: descriptionLen != null,
|
|
28696
|
+
descriptionLen,
|
|
28697
|
+
keys: Object.keys(sysMsg).slice(0, 16)
|
|
28466
28698
|
});
|
|
28467
28699
|
}
|
|
28468
28700
|
break;
|
|
@@ -28488,13 +28720,14 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onProv
|
|
|
28488
28720
|
} else if (block.type === "tool_use") {
|
|
28489
28721
|
proc.currentBlockType = "tool_use";
|
|
28490
28722
|
proc.currentToolName = block.name ?? "unknown";
|
|
28723
|
+
proc.activeToolUseStartedAt = Date.now();
|
|
28491
28724
|
proc.accumulatedToolInput = "";
|
|
28492
28725
|
const toolName = block.name ?? "unknown";
|
|
28493
28726
|
proc.suppressCurrentToolUse = proc.officialMediaGenerationSatisfied === true && isOfficialMediaGenerationToolName(toolName);
|
|
28494
28727
|
const isMcpTool = parseMcpRuntimeToolName(toolName) != null;
|
|
28495
28728
|
proc.currentMcpInvocationId = isMcpTool ? createMcpToolInvocationId() : null;
|
|
28496
28729
|
proc.currentMcpInvocationStartedAt = isMcpTool ? (/* @__PURE__ */ new Date()).toISOString() : null;
|
|
28497
|
-
if (
|
|
28730
|
+
if (shouldStreamInternals(proc) && !proc.suppressCurrentToolUse && toolName !== "ExitPlanMode" && !isAskUserQuestionToolName(toolName)) {
|
|
28498
28731
|
emit({
|
|
28499
28732
|
type: "agent:tool_use",
|
|
28500
28733
|
payload: {
|
|
@@ -28519,7 +28752,7 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onProv
|
|
|
28519
28752
|
if (delta.type === "thinking_delta" && typeof delta.thinking === "string") {
|
|
28520
28753
|
if (proc.suppressCurrentThinking) break;
|
|
28521
28754
|
proc.accumulatedThinking += delta.thinking;
|
|
28522
|
-
if (
|
|
28755
|
+
if (shouldStreamInternals(proc)) {
|
|
28523
28756
|
emit({
|
|
28524
28757
|
type: "agent:thinking_chunk",
|
|
28525
28758
|
payload: { ...wireBase(base), chunk: delta.thinking }
|
|
@@ -28530,7 +28763,7 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onProv
|
|
|
28530
28763
|
if (typeof partial2 === "string") {
|
|
28531
28764
|
proc.accumulatedToolInput += partial2;
|
|
28532
28765
|
const liveInput = extractLiveToolInput(proc.currentToolName, proc.accumulatedToolInput);
|
|
28533
|
-
if (
|
|
28766
|
+
if (shouldStreamInternals(proc) && liveInput && proc.currentToolName != null) {
|
|
28534
28767
|
emit({
|
|
28535
28768
|
type: "agent:tool_input_update",
|
|
28536
28769
|
payload: {
|
|
@@ -28564,7 +28797,7 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onProv
|
|
|
28564
28797
|
}
|
|
28565
28798
|
case "content_block_stop": {
|
|
28566
28799
|
if (proc.currentBlockType === "thinking") {
|
|
28567
|
-
if (
|
|
28800
|
+
if (shouldStreamInternals(proc) && !proc.suppressCurrentThinking) {
|
|
28568
28801
|
emit({
|
|
28569
28802
|
type: "agent:thinking_done",
|
|
28570
28803
|
payload: wireBase(getTaskBase(proc))
|
|
@@ -28589,8 +28822,7 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onProv
|
|
|
28589
28822
|
error: error51,
|
|
28590
28823
|
agentId: proc.agentId,
|
|
28591
28824
|
toolName: proc.currentToolName,
|
|
28592
|
-
inputLen: proc.accumulatedToolInput.length
|
|
28593
|
-
sample: proc.accumulatedToolInput.slice(0, 200)
|
|
28825
|
+
inputLen: proc.accumulatedToolInput.length
|
|
28594
28826
|
});
|
|
28595
28827
|
}
|
|
28596
28828
|
}
|
|
@@ -28599,7 +28831,7 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onProv
|
|
|
28599
28831
|
if (lastToolUse && lastToolUse.type === "tool_use") {
|
|
28600
28832
|
lastToolUse.input = parsedInput;
|
|
28601
28833
|
}
|
|
28602
|
-
if (
|
|
28834
|
+
if (shouldStreamInternals(proc) && proc.currentToolName != null && LIVE_INPUT_PREVIEW_TOOLS.has(proc.currentToolName) && Object.keys(parsedInput).length > 0) {
|
|
28603
28835
|
emit({
|
|
28604
28836
|
type: "agent:tool_input_update",
|
|
28605
28837
|
payload: {
|
|
@@ -28723,7 +28955,7 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onProv
|
|
|
28723
28955
|
blockTypes,
|
|
28724
28956
|
hasToolResult,
|
|
28725
28957
|
hasPlainText,
|
|
28726
|
-
|
|
28958
|
+
contentLen: typeof content === "string" ? content.length : JSON.stringify(content).length
|
|
28727
28959
|
});
|
|
28728
28960
|
break;
|
|
28729
28961
|
}
|
|
@@ -28732,7 +28964,7 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onProv
|
|
|
28732
28964
|
agentId: proc.agentId,
|
|
28733
28965
|
scope: proc.scope.kind === "single" ? "single" : proc.scope.groupId,
|
|
28734
28966
|
blockTypes,
|
|
28735
|
-
|
|
28967
|
+
contentLen: JSON.stringify(content).length,
|
|
28736
28968
|
replyMessageId: base.replyMessageId
|
|
28737
28969
|
});
|
|
28738
28970
|
}
|
|
@@ -28753,6 +28985,7 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onProv
|
|
|
28753
28985
|
});
|
|
28754
28986
|
proc.currentMcpInvocationId = null;
|
|
28755
28987
|
proc.currentMcpInvocationStartedAt = null;
|
|
28988
|
+
proc.activeToolUseStartedAt = void 0;
|
|
28756
28989
|
proc.currentToolName = null;
|
|
28757
28990
|
continue;
|
|
28758
28991
|
}
|
|
@@ -28760,6 +28993,7 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onProv
|
|
|
28760
28993
|
proc.officialMediaGenerationSatisfied = true;
|
|
28761
28994
|
}
|
|
28762
28995
|
if (isAskUserQuestionToolName(toolName)) {
|
|
28996
|
+
proc.activeToolUseStartedAt = void 0;
|
|
28763
28997
|
proc.currentToolName = null;
|
|
28764
28998
|
continue;
|
|
28765
28999
|
}
|
|
@@ -28787,7 +29021,7 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onProv
|
|
|
28787
29021
|
proc.currentMcpInvocationId = null;
|
|
28788
29022
|
proc.currentMcpInvocationStartedAt = null;
|
|
28789
29023
|
}
|
|
28790
|
-
if (
|
|
29024
|
+
if (shouldStreamInternals(proc)) {
|
|
28791
29025
|
emit({
|
|
28792
29026
|
type: "agent:tool_result",
|
|
28793
29027
|
payload: {
|
|
@@ -28811,6 +29045,7 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onProv
|
|
|
28811
29045
|
}
|
|
28812
29046
|
}
|
|
28813
29047
|
}
|
|
29048
|
+
proc.activeToolUseStartedAt = void 0;
|
|
28814
29049
|
}
|
|
28815
29050
|
}
|
|
28816
29051
|
}
|
|
@@ -28889,7 +29124,6 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onProv
|
|
|
28889
29124
|
groupId,
|
|
28890
29125
|
compactScheduled: proc.compactRequested === true,
|
|
28891
29126
|
fullTextLen: proc.accumulatedText.length,
|
|
28892
|
-
fullTextSample: proc.accumulatedText.slice(0, 200),
|
|
28893
29127
|
accumulatedBlockCount: proc.contentBlocks.length,
|
|
28894
29128
|
accumulatedBlockTypes: proc.contentBlocks.map((b) => b.type),
|
|
28895
29129
|
silentSegmentEmitted: groupMode
|
|
@@ -28933,7 +29167,6 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onProv
|
|
|
28933
29167
|
segmentCount: proc.segmentCount,
|
|
28934
29168
|
compactScheduled: proc.compactRequested === true,
|
|
28935
29169
|
fullTextLen: proc.accumulatedText.length,
|
|
28936
|
-
fullTextSample: proc.accumulatedText.slice(0, 200),
|
|
28937
29170
|
traceId: base.traceId
|
|
28938
29171
|
});
|
|
28939
29172
|
emit({
|
|
@@ -28984,7 +29217,6 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onProv
|
|
|
28984
29217
|
ackId: base.replyMessageId,
|
|
28985
29218
|
messageId: carrierMessageId,
|
|
28986
29219
|
textLen: proc.accumulatedText.length,
|
|
28987
|
-
textSample: proc.accumulatedText.slice(0, 200),
|
|
28988
29220
|
tokenCount: usage.tokenCount,
|
|
28989
29221
|
traceId: base.traceId
|
|
28990
29222
|
});
|
|
@@ -29177,8 +29409,7 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onProv
|
|
|
29177
29409
|
logger10.info("Captured non-streamed assistant message", {
|
|
29178
29410
|
agentId: proc.agentId,
|
|
29179
29411
|
scope: proc.scope.kind === "single" ? "single" : proc.scope.groupId,
|
|
29180
|
-
textLen: text.length
|
|
29181
|
-
textSample: text.slice(0, 100)
|
|
29412
|
+
textLen: text.length
|
|
29182
29413
|
});
|
|
29183
29414
|
} else {
|
|
29184
29415
|
proc.lastAssistantContentDescription = describeAssistantContent(am.message?.content);
|
|
@@ -29202,6 +29433,7 @@ function resetAccumulators(proc) {
|
|
|
29202
29433
|
proc.currentToolName = null;
|
|
29203
29434
|
proc.currentMcpInvocationId = null;
|
|
29204
29435
|
proc.currentMcpInvocationStartedAt = null;
|
|
29436
|
+
proc.activeToolUseStartedAt = void 0;
|
|
29205
29437
|
proc.segmentBuffer = "";
|
|
29206
29438
|
proc.segmentCount = 0;
|
|
29207
29439
|
proc.accumulatedToolInput = "";
|
|
@@ -29211,6 +29443,7 @@ function resetAccumulators(proc) {
|
|
|
29211
29443
|
proc.officialMediaGenerationSatisfied = false;
|
|
29212
29444
|
proc.suppressCurrentThinking = false;
|
|
29213
29445
|
proc.suppressCurrentToolUse = false;
|
|
29446
|
+
proc.activeSubagentTaskIds?.clear();
|
|
29214
29447
|
}
|
|
29215
29448
|
|
|
29216
29449
|
// src/forkHistoryReplay.ts
|
|
@@ -29398,7 +29631,7 @@ function missingSubscriptionMessage(subscriptionId) {
|
|
|
29398
29631
|
}
|
|
29399
29632
|
var NODE_USER_UID = 1e3;
|
|
29400
29633
|
var POST_MERGE_CONTINUATION_ROUTE_MS = 15e3;
|
|
29401
|
-
var SCOPE_PROMPT_FINGERPRINT_REVISION = "workdir-scope-prompt-
|
|
29634
|
+
var SCOPE_PROMPT_FINGERPRINT_REVISION = "workdir-scope-mcp-abi-prompt-v4";
|
|
29402
29635
|
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
29636
|
var DOCUMENT_READING_RULES = `DOCUMENT READING:
|
|
29404
29637
|
- The built-in Read tool cannot read binary office documents such as .docx, .xls, .xlsx, .pptx, .pdf, .odt, .ods, .odp, or .rtf.
|
|
@@ -29413,6 +29646,16 @@ var MEDIA_GENERATION_RULES = `MEDIA GENERATION:
|
|
|
29413
29646
|
- 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
29647
|
- 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
29648
|
- 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.`;
|
|
29649
|
+
function stableFingerprintValue(value) {
|
|
29650
|
+
if (Array.isArray(value)) return value.map(stableFingerprintValue);
|
|
29651
|
+
if (!value || typeof value !== "object") return value;
|
|
29652
|
+
const out = {};
|
|
29653
|
+
for (const key of Object.keys(value).sort()) {
|
|
29654
|
+
const normalized = stableFingerprintValue(value[key]);
|
|
29655
|
+
if (normalized !== void 0) out[key] = normalized;
|
|
29656
|
+
}
|
|
29657
|
+
return out;
|
|
29658
|
+
}
|
|
29416
29659
|
function isRecoveryDispatchTask(task) {
|
|
29417
29660
|
return task.dispatchKind === "manual_continue" || task.dispatchKind === "regenerate";
|
|
29418
29661
|
}
|
|
@@ -29670,13 +29913,15 @@ var AgentManager = class {
|
|
|
29670
29913
|
agents = /* @__PURE__ */ new Map();
|
|
29671
29914
|
lastUsedAt = /* @__PURE__ */ new Map();
|
|
29672
29915
|
/** Scopes 被 zombie_watchdog 关闭后的"入睡"标记,acquire 重建时清除并 emit awake。 */
|
|
29673
|
-
dormantScopes = /* @__PURE__ */ new
|
|
29916
|
+
dormantScopes = /* @__PURE__ */ new Map();
|
|
29674
29917
|
/**
|
|
29675
29918
|
* zombie_watchdog 拆 runtime 时,把该 (agentId, scope) 的 groupInbox 快照到这里,
|
|
29676
29919
|
* 让下一次 getOrCreate 重建 runtime 时可以恢复未读消息。仅 in-memory;
|
|
29677
29920
|
* bridge 进程崩溃 / shutdownAll 时丢失,与现有 inbox 内存语义一致。
|
|
29678
29921
|
*/
|
|
29679
29922
|
dormantGroupInboxes = /* @__PURE__ */ new Map();
|
|
29923
|
+
/** Spectate requested before runtime existed; value = activatedAt epoch ms. */
|
|
29924
|
+
pendingSpectate = /* @__PURE__ */ new Map();
|
|
29680
29925
|
sessionStore;
|
|
29681
29926
|
dispatchMemory = new GroupDispatchMemoryStore();
|
|
29682
29927
|
dataDir;
|
|
@@ -29842,6 +30087,7 @@ var AgentManager = class {
|
|
|
29842
30087
|
}
|
|
29843
30088
|
async resolveRuntimeCwd(agentConfig, scope, requestedCwd) {
|
|
29844
30089
|
let cwd = this.remapServerWorkspaceCwd(agentConfig, scope, requestedCwd);
|
|
30090
|
+
let fallbackForensicsId;
|
|
29845
30091
|
if (!isFullyQualifiedAbsolutePath(cwd)) {
|
|
29846
30092
|
const fallback = path13.join(this.workspacesDir, this.localScopeDirName(agentConfig, scope));
|
|
29847
30093
|
logger13.error(
|
|
@@ -29856,6 +30102,23 @@ var AgentManager = class {
|
|
|
29856
30102
|
error: new Error("workdir_not_usable_on_this_machine")
|
|
29857
30103
|
}
|
|
29858
30104
|
);
|
|
30105
|
+
fallbackForensicsId = createFallbackId();
|
|
30106
|
+
logFallback(logger13, {
|
|
30107
|
+
fallbackId: fallbackForensicsId,
|
|
30108
|
+
type: "cwd_sandbox",
|
|
30109
|
+
phase: "applied",
|
|
30110
|
+
expected: false,
|
|
30111
|
+
context: {
|
|
30112
|
+
agentId: agentConfig.id,
|
|
30113
|
+
scope: scopeKey(scope),
|
|
30114
|
+
platform: process.platform,
|
|
30115
|
+
requested: requestedCwd,
|
|
30116
|
+
resolved: cwd,
|
|
30117
|
+
reason: "not_fully_qualified",
|
|
30118
|
+
fallback
|
|
30119
|
+
},
|
|
30120
|
+
outcome: { result: "sandbox_fallback" }
|
|
30121
|
+
});
|
|
29859
30122
|
cwd = fallback;
|
|
29860
30123
|
}
|
|
29861
30124
|
if (isRunningAsRoot() && cwd.startsWith("/root/")) {
|
|
@@ -29874,6 +30137,21 @@ var AgentManager = class {
|
|
|
29874
30137
|
fallback,
|
|
29875
30138
|
error: e
|
|
29876
30139
|
});
|
|
30140
|
+
const fbId = fallbackForensicsId ?? createFallbackId();
|
|
30141
|
+
logFallback(logger13, {
|
|
30142
|
+
fallbackId: fbId,
|
|
30143
|
+
type: "cwd_sandbox",
|
|
30144
|
+
phase: "applied",
|
|
30145
|
+
expected: false,
|
|
30146
|
+
context: {
|
|
30147
|
+
agentId: agentConfig.id,
|
|
30148
|
+
scope: scopeKey(scope),
|
|
30149
|
+
reason: "mkdir_failed",
|
|
30150
|
+
requested: cwd,
|
|
30151
|
+
fallback
|
|
30152
|
+
},
|
|
30153
|
+
outcome: { result: "second_layer_fallback" }
|
|
30154
|
+
});
|
|
29877
30155
|
await fs6.mkdir(fallback, { recursive: true });
|
|
29878
30156
|
return fallback;
|
|
29879
30157
|
}
|
|
@@ -30096,17 +30374,24 @@ var AgentManager = class {
|
|
|
30096
30374
|
});
|
|
30097
30375
|
return null;
|
|
30098
30376
|
}
|
|
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");
|
|
30377
|
+
scopePromptFingerprint(agentConfig, scope, agentCwd, scopesSection, externalMcpFingerprint) {
|
|
30378
|
+
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
30379
|
}
|
|
30102
|
-
|
|
30380
|
+
externalMcpFingerprint(externalMcp) {
|
|
30381
|
+
const serverNames = Object.keys(externalMcp.mcpServers).sort();
|
|
30382
|
+
const allowedTools = [...externalMcp.allowedTools].sort();
|
|
30383
|
+
const toolAbi = [...externalMcp.toolAbi ?? []].sort((a, b) => a.serverName.localeCompare(b.serverName)).map(stableFingerprintValue);
|
|
30384
|
+
if (serverNames.length === 0 && allowedTools.length === 0 && toolAbi.length === 0) return "";
|
|
30385
|
+
return JSON.stringify({ serverNames, allowedTools, toolAbi });
|
|
30386
|
+
}
|
|
30387
|
+
discardSessionIfScopePromptChanged(agentConfig, scope, sessionId, fingerprint, options = {}) {
|
|
30103
30388
|
const previous = this.sessionStore.getPromptFingerprint(agentConfig.id, scope);
|
|
30104
30389
|
if (!sessionId) {
|
|
30105
30390
|
this.sessionStore.setPromptFingerprint(agentConfig.id, scope, fingerprint);
|
|
30106
30391
|
return null;
|
|
30107
30392
|
}
|
|
30108
30393
|
if (previous === fingerprint) return sessionId;
|
|
30109
|
-
if (!previous && scope.kind === "single") {
|
|
30394
|
+
if (!previous && scope.kind === "single" && options.clearLegacySingleSession !== true) {
|
|
30110
30395
|
this.sessionStore.setPromptFingerprint(agentConfig.id, scope, fingerprint);
|
|
30111
30396
|
logger13.info("Retaining legacy single-scope session while recording prompt fingerprint", {
|
|
30112
30397
|
agentId: agentConfig.id,
|
|
@@ -30126,7 +30411,8 @@ var AgentManager = class {
|
|
|
30126
30411
|
sessionId,
|
|
30127
30412
|
previousFingerprint: previous,
|
|
30128
30413
|
nextFingerprint: fingerprint,
|
|
30129
|
-
revision: SCOPE_PROMPT_FINGERPRINT_REVISION
|
|
30414
|
+
revision: SCOPE_PROMPT_FINGERPRINT_REVISION,
|
|
30415
|
+
reason: options.reason ?? "scope_prompt_changed"
|
|
30130
30416
|
});
|
|
30131
30417
|
return null;
|
|
30132
30418
|
}
|
|
@@ -30175,6 +30461,7 @@ var AgentManager = class {
|
|
|
30175
30461
|
logger13.info("Evicting idle Agent query", { agentId: proc.agentId, scope: scopeKey(proc.scope) });
|
|
30176
30462
|
const runtime = this.asRuntime(proc);
|
|
30177
30463
|
this.clearQuietFlushTimer(runtime);
|
|
30464
|
+
this.teardownSpectate(runtime);
|
|
30178
30465
|
try {
|
|
30179
30466
|
runtime.inputController.close();
|
|
30180
30467
|
await this.awaitQueryReturn(runtime.query, 5e3, proc.agentId);
|
|
@@ -30194,6 +30481,7 @@ var AgentManager = class {
|
|
|
30194
30481
|
const runtime = this.asRuntime(proc);
|
|
30195
30482
|
const key = runtimeKey(proc.agentId, proc.scope);
|
|
30196
30483
|
this.clearQuietFlushTimer(runtime);
|
|
30484
|
+
this.teardownSpectate(runtime);
|
|
30197
30485
|
runtime.currentTask = null;
|
|
30198
30486
|
runtime.injectedTasks = [];
|
|
30199
30487
|
runtime.mergedTasks = [];
|
|
@@ -30230,6 +30518,7 @@ var AgentManager = class {
|
|
|
30230
30518
|
evictIdle() {
|
|
30231
30519
|
const now = Date.now();
|
|
30232
30520
|
const { idleTimeoutMs, workingSilenceTimeoutMs } = this.queryConfig;
|
|
30521
|
+
const stallWarnAfterMs = Math.min(9e4, this.queryConfig.replyStallTimeoutMs);
|
|
30233
30522
|
for (const [key, proc] of this.agents) {
|
|
30234
30523
|
if (!this.isEvictable(proc)) continue;
|
|
30235
30524
|
const runtime = this.asRuntime(proc);
|
|
@@ -30240,26 +30529,62 @@ var AgentManager = class {
|
|
|
30240
30529
|
for (const [, proc] of this.agents) {
|
|
30241
30530
|
if (proc.status !== "working") continue;
|
|
30242
30531
|
const runtime = this.asRuntime(proc);
|
|
30532
|
+
if (runtime.currentTask) {
|
|
30533
|
+
const sinceEventMs = now - proc.lastSdkEventAt;
|
|
30534
|
+
if (sinceEventMs > stallWarnAfterMs && !proc.stallWarned) {
|
|
30535
|
+
proc.stallWarned = true;
|
|
30536
|
+
const openTool = this.latestOpenToolUse(proc);
|
|
30537
|
+
logger13.warn("Reply stall onset: in-flight reply silent", {
|
|
30538
|
+
agentId: proc.agentId,
|
|
30539
|
+
scope: scopeKey(proc.scope),
|
|
30540
|
+
sinceEventMs,
|
|
30541
|
+
replyStallTimeoutMs: this.queryConfig.replyStallTimeoutMs,
|
|
30542
|
+
workingSilenceTimeoutMs,
|
|
30543
|
+
busySilenceTimeoutMs: this.queryConfig.busySilenceTimeoutMs ?? null,
|
|
30544
|
+
replyMessageId: runtime.currentTask.replyMessageId,
|
|
30545
|
+
model: proc.model ?? "(unknown)",
|
|
30546
|
+
lastSdkEvent: proc.lastSdkEventInfo,
|
|
30547
|
+
hasActiveToolUse: runtime.activeToolUseStartedAt != null || openTool != null,
|
|
30548
|
+
activeToolUseAgeMs: runtime.activeToolUseStartedAt != null ? now - runtime.activeToolUseStartedAt : null,
|
|
30549
|
+
openToolName: openTool?.toolName ?? proc.currentToolName ?? null,
|
|
30550
|
+
openMcpInvocationId: proc.currentMcpInvocationId ?? null,
|
|
30551
|
+
subagentInFlight: (runtime.activeSubagentTaskIds?.size ?? 0) > 0,
|
|
30552
|
+
activeSubagentCount: runtime.activeSubagentTaskIds?.size ?? 0,
|
|
30553
|
+
busyReason: this.busyReason(proc)
|
|
30554
|
+
});
|
|
30555
|
+
}
|
|
30556
|
+
}
|
|
30557
|
+
const busyReason = this.busyReason(runtime);
|
|
30558
|
+
const busy = busyReason !== null;
|
|
30559
|
+
if (runtime.currentTask && !busy && now - proc.lastSdkEventAt > this.queryConfig.replyStallTimeoutMs) {
|
|
30560
|
+
void this.recoverStalledReply(proc, now - proc.lastSdkEventAt);
|
|
30561
|
+
continue;
|
|
30562
|
+
}
|
|
30243
30563
|
const hasInjectedBacklog = runtime.injectedTasks.length > 0;
|
|
30244
|
-
const
|
|
30564
|
+
const baseCeilingMs = busy ? Math.max(workingSilenceTimeoutMs, this.queryConfig.busySilenceTimeoutMs ?? 0) : workingSilenceTimeoutMs;
|
|
30565
|
+
const effectiveTimeoutMs = hasInjectedBacklog ? baseCeilingMs * 2 : baseCeilingMs;
|
|
30245
30566
|
const silentMs = now - proc.lastSdkEventAt;
|
|
30246
30567
|
if (silentMs <= effectiveTimeoutMs) {
|
|
30247
|
-
if (
|
|
30248
|
-
logger13.warn(
|
|
30249
|
-
|
|
30250
|
-
|
|
30251
|
-
|
|
30252
|
-
|
|
30253
|
-
|
|
30254
|
-
|
|
30255
|
-
|
|
30256
|
-
|
|
30257
|
-
|
|
30258
|
-
|
|
30259
|
-
|
|
30568
|
+
if (silentMs > workingSilenceTimeoutMs) {
|
|
30569
|
+
logger13.warn("Zombie watchdog: working runtime silent past base timeout; granting extended grace", {
|
|
30570
|
+
agentId: proc.agentId,
|
|
30571
|
+
scope: scopeKey(proc.scope),
|
|
30572
|
+
silentMs,
|
|
30573
|
+
baseTimeoutMs: workingSilenceTimeoutMs,
|
|
30574
|
+
baseCeilingMs,
|
|
30575
|
+
busySilenceTimeoutMs: this.queryConfig.busySilenceTimeoutMs ?? null,
|
|
30576
|
+
effectiveTimeoutMs,
|
|
30577
|
+
busy,
|
|
30578
|
+
busyReason,
|
|
30579
|
+
injectedTaskCount: runtime.injectedTasks.length,
|
|
30580
|
+
replyMessageId: proc.currentTask?.replyMessageId
|
|
30581
|
+
});
|
|
30260
30582
|
}
|
|
30261
30583
|
continue;
|
|
30262
30584
|
}
|
|
30585
|
+
const zombieOpenTool = this.latestOpenToolUse(proc);
|
|
30586
|
+
const watchdogDetectedAt = Date.now();
|
|
30587
|
+
const watchdogFallbackId = createFallbackId();
|
|
30263
30588
|
logger13.warn("Zombie watchdog: working runtime silent too long, tearing down", {
|
|
30264
30589
|
agentId: proc.agentId,
|
|
30265
30590
|
scope: scopeKey(proc.scope),
|
|
@@ -30267,12 +30592,46 @@ var AgentManager = class {
|
|
|
30267
30592
|
lastSdkEventAt: new Date(proc.lastSdkEventAt).toISOString(),
|
|
30268
30593
|
workingSilenceTimeoutMs,
|
|
30269
30594
|
effectiveTimeoutMs,
|
|
30595
|
+
baseCeilingMs,
|
|
30596
|
+
busy,
|
|
30597
|
+
busySilenceTimeoutMs: this.queryConfig.busySilenceTimeoutMs ?? null,
|
|
30270
30598
|
replyMessageId: proc.currentTask?.replyMessageId,
|
|
30271
30599
|
injectedTaskCount: runtime.injectedTasks.length,
|
|
30272
30600
|
hadInjectedBacklog: hasInjectedBacklog,
|
|
30273
|
-
inboxSize: proc.groupInbox.length
|
|
30601
|
+
inboxSize: proc.groupInbox.length,
|
|
30602
|
+
fallbackId: watchdogFallbackId,
|
|
30603
|
+
// Breadcrumb: what the turn was waiting on when it timed out. A hung subagent
|
|
30604
|
+
// (open `Task` tool_use) lands here now that the fast path skips active tools.
|
|
30605
|
+
model: proc.model ?? "(unknown)",
|
|
30606
|
+
lastSdkEvent: proc.lastSdkEventInfo,
|
|
30607
|
+
openToolName: zombieOpenTool?.toolName ?? proc.currentToolName ?? null,
|
|
30608
|
+
activeToolUseAgeMs: runtime.activeToolUseStartedAt != null ? now - runtime.activeToolUseStartedAt : null,
|
|
30609
|
+
openMcpInvocationId: proc.currentMcpInvocationId ?? null,
|
|
30610
|
+
subagentInFlight: (runtime.activeSubagentTaskIds?.size ?? 0) > 0,
|
|
30611
|
+
activeSubagentCount: runtime.activeSubagentTaskIds?.size ?? 0,
|
|
30612
|
+
busyReason: this.busyReason(proc)
|
|
30613
|
+
});
|
|
30614
|
+
logFallback(logger13, {
|
|
30615
|
+
fallbackId: watchdogFallbackId,
|
|
30616
|
+
type: "zombie_watchdog",
|
|
30617
|
+
phase: "detected",
|
|
30618
|
+
expected: false,
|
|
30619
|
+
traceId: proc.currentTask?.traceId,
|
|
30620
|
+
context: {
|
|
30621
|
+
agentId: proc.agentId,
|
|
30622
|
+
scope: scopeKey(proc.scope),
|
|
30623
|
+
silentMs,
|
|
30624
|
+
replyMessageId: proc.currentTask?.replyMessageId ?? null,
|
|
30625
|
+
openToolName: zombieOpenTool?.toolName ?? proc.currentToolName ?? null,
|
|
30626
|
+
lastSdkEventType: proc.lastSdkEventInfo?.type ?? null,
|
|
30627
|
+
injectedTaskCount: runtime.injectedTasks.length,
|
|
30628
|
+
inboxSize: proc.groupInbox.length
|
|
30629
|
+
}
|
|
30630
|
+
});
|
|
30631
|
+
void this.closeRuntime(proc, "zombie_watchdog", {
|
|
30632
|
+
fallbackId: watchdogFallbackId,
|
|
30633
|
+
detectedAt: watchdogDetectedAt
|
|
30274
30634
|
});
|
|
30275
|
-
void this.closeRuntime(proc, "zombie_watchdog");
|
|
30276
30635
|
}
|
|
30277
30636
|
}
|
|
30278
30637
|
/**
|
|
@@ -30519,7 +30878,7 @@ ${cfg.instructions.trim()}` : "";
|
|
|
30519
30878
|
agentId: agentConfig.id,
|
|
30520
30879
|
capabilityTier: cfg.capabilityTier,
|
|
30521
30880
|
isSmith: smithAgent
|
|
30522
|
-
}) ?? { mcpServers: {}, allowedTools: [] };
|
|
30881
|
+
}) ?? { mcpServers: {}, allowedTools: [], toolAbi: [] };
|
|
30523
30882
|
logger13.info("External MCP resolved for runtime", {
|
|
30524
30883
|
agentId: agentConfig.id,
|
|
30525
30884
|
scope: scopeKey(scope),
|
|
@@ -30536,11 +30895,16 @@ ${cfg.instructions.trim()}` : "";
|
|
|
30536
30895
|
}
|
|
30537
30896
|
const notebookSection = this.buildNotebookSection(agentConfig.id);
|
|
30538
30897
|
const scopesSection = this.buildScopesSection(agentConfig, scope, agentCwd);
|
|
30898
|
+
const externalMcpFingerprint = this.externalMcpFingerprint(externalMcp);
|
|
30539
30899
|
savedSessionId = this.discardSessionIfScopePromptChanged(
|
|
30540
30900
|
agentConfig,
|
|
30541
30901
|
scope,
|
|
30542
30902
|
savedSessionId,
|
|
30543
|
-
this.scopePromptFingerprint(agentConfig, scope, agentCwd, scopesSection)
|
|
30903
|
+
this.scopePromptFingerprint(agentConfig, scope, agentCwd, scopesSection, externalMcpFingerprint),
|
|
30904
|
+
{
|
|
30905
|
+
clearLegacySingleSession: externalMcpFingerprint.length > 0,
|
|
30906
|
+
reason: externalMcpFingerprint.length > 0 ? "external_mcp_abi_changed" : "scope_prompt_changed"
|
|
30907
|
+
}
|
|
30544
30908
|
);
|
|
30545
30909
|
let forkHistorySection = "";
|
|
30546
30910
|
if (!savedSessionId && scope.kind === "single") {
|
|
@@ -30561,6 +30925,8 @@ ${cfg.instructions.trim()}` : "";
|
|
|
30561
30925
|
}
|
|
30562
30926
|
}
|
|
30563
30927
|
const cronLockSnapshot = readCronLockSnapshot();
|
|
30928
|
+
const builtinWebSearchAllowed = this.queryConfig.allowBuiltinWebSearch;
|
|
30929
|
+
const disallowedToolsForRuntime = builtinWebSearchAllowed ? [] : ["WebSearch"];
|
|
30564
30930
|
logger13.info("Creating Agent query", {
|
|
30565
30931
|
agentId: agentConfig.id,
|
|
30566
30932
|
scope: scopeKey(scope),
|
|
@@ -30569,6 +30935,8 @@ ${cfg.instructions.trim()}` : "";
|
|
|
30569
30935
|
sessionId: savedSessionId,
|
|
30570
30936
|
forkHistoryReplay: forkHistorySection.length > 0,
|
|
30571
30937
|
model: cfg.model ?? "(default)",
|
|
30938
|
+
builtinWebSearchAllowed,
|
|
30939
|
+
disallowedTools: disallowedToolsForRuntime,
|
|
30572
30940
|
// Diagnostic: who currently owns Claude's global cron lock (~/.claude/scheduled_tasks.lock).
|
|
30573
30941
|
// Cron is process-internal but the binary uses this singleton lock to elect ONE scheduler
|
|
30574
30942
|
// among concurrent claude subprocesses. If lock is held by another session at spawn time,
|
|
@@ -30580,7 +30948,6 @@ ${cfg.instructions.trim()}` : "";
|
|
|
30580
30948
|
});
|
|
30581
30949
|
const planModeRef = { active: false, denyCount: 0 };
|
|
30582
30950
|
const mediaGenerationTurnGuard = createOfficialMediaGenerationTurnGuard();
|
|
30583
|
-
const builtinWebSearchAllowed = this.queryConfig.allowBuiltinWebSearch;
|
|
30584
30951
|
const options = {
|
|
30585
30952
|
cwd: agentCwd,
|
|
30586
30953
|
systemPrompt: {
|
|
@@ -30647,6 +31014,8 @@ ${cfg.instructions.trim()}` : "";
|
|
|
30647
31014
|
] : [],
|
|
30648
31015
|
...externalMcp.allowedTools
|
|
30649
31016
|
],
|
|
31017
|
+
// Server-side WebSearch bypasses canUseTool; disallowedTools removes it from model context.
|
|
31018
|
+
disallowedTools: disallowedToolsForRuntime,
|
|
30650
31019
|
mcpServers: { ...externalMcp.mcpServers, neural: neuralServer },
|
|
30651
31020
|
includePartialMessages: true,
|
|
30652
31021
|
// Plan mode custom workflow instructions. When setPermissionMode('plan') is
|
|
@@ -30973,6 +31342,7 @@ Do NOT use "..." as content \u2014 write specific, project-relevant content.`;
|
|
|
30973
31342
|
currentTask: null,
|
|
30974
31343
|
currentTaskStartedAt: 0,
|
|
30975
31344
|
lastSdkEventAt: Date.now(),
|
|
31345
|
+
model: (typeof options.model === "string" ? options.model : cfg.model) ?? null,
|
|
30976
31346
|
compactRequested: false,
|
|
30977
31347
|
compactInProgress: false,
|
|
30978
31348
|
contextOverflowLockedUntil: 0,
|
|
@@ -30984,13 +31354,18 @@ Do NOT use "..." as content \u2014 write specific, project-relevant content.`;
|
|
|
30984
31354
|
currentToolName: null,
|
|
30985
31355
|
currentMcpInvocationId: null,
|
|
30986
31356
|
currentMcpInvocationStartedAt: null,
|
|
31357
|
+
activeSubagentTaskIds: /* @__PURE__ */ new Set(),
|
|
30987
31358
|
mcpAuditRecorder: this.mcpAuditRecorder,
|
|
30988
31359
|
segmentBuffer: "",
|
|
30989
31360
|
segmentCount: 0,
|
|
30990
31361
|
accumulatedToolInput: "",
|
|
30991
31362
|
planModeRef,
|
|
30992
31363
|
mediaGenerationTurnGuard,
|
|
30993
|
-
groupInbox: []
|
|
31364
|
+
groupInbox: [],
|
|
31365
|
+
spectating: false,
|
|
31366
|
+
spectateActivatedAt: 0,
|
|
31367
|
+
spectateViewing: false,
|
|
31368
|
+
spectateTtlExpired: false
|
|
30994
31369
|
};
|
|
30995
31370
|
const runtime = Object.assign(proc, {
|
|
30996
31371
|
query: agentQuery,
|
|
@@ -31002,7 +31377,8 @@ Do NOT use "..." as content \u2014 write specific, project-relevant content.`;
|
|
|
31002
31377
|
createdAt: Date.now(),
|
|
31003
31378
|
supportsVision: modelInputMode === "vision" && cfg.supportsVision !== false,
|
|
31004
31379
|
modelInputMode,
|
|
31005
|
-
quietFlushTimer: null
|
|
31380
|
+
quietFlushTimer: null,
|
|
31381
|
+
spectateRevertTimer: null
|
|
31006
31382
|
});
|
|
31007
31383
|
logger13.info("Agent model input mode resolved", {
|
|
31008
31384
|
agentId: agentConfig.id,
|
|
@@ -31027,6 +31403,7 @@ Do NOT use "..." as content \u2014 write specific, project-relevant content.`;
|
|
|
31027
31403
|
} else {
|
|
31028
31404
|
this.dormantGroupInboxes.delete(key);
|
|
31029
31405
|
}
|
|
31406
|
+
const dormantMeta = this.dormantScopes.get(key);
|
|
31030
31407
|
if (this.dormantScopes.delete(key)) {
|
|
31031
31408
|
this.emit({
|
|
31032
31409
|
type: "agent:awake",
|
|
@@ -31038,7 +31415,44 @@ Do NOT use "..." as content \u2014 write specific, project-relevant content.`;
|
|
|
31038
31415
|
});
|
|
31039
31416
|
logger13.info("Agent scope awakened after dormant", {
|
|
31040
31417
|
agentId: agentConfig.id,
|
|
31041
|
-
scope: scopeKey(scope)
|
|
31418
|
+
scope: scopeKey(scope),
|
|
31419
|
+
...dormantMeta ? {
|
|
31420
|
+
fallbackId: dormantMeta.fallbackId,
|
|
31421
|
+
dormantDurationMs: Date.now() - dormantMeta.detectedAt,
|
|
31422
|
+
dataLossSuspected: dormantMeta.droppedTaskCount > 0
|
|
31423
|
+
} : {}
|
|
31424
|
+
});
|
|
31425
|
+
if (dormantMeta) {
|
|
31426
|
+
logFallback(logger13, {
|
|
31427
|
+
fallbackId: dormantMeta.fallbackId,
|
|
31428
|
+
type: "zombie_watchdog",
|
|
31429
|
+
phase: "outcome",
|
|
31430
|
+
expected: false,
|
|
31431
|
+
context: {
|
|
31432
|
+
agentId: agentConfig.id,
|
|
31433
|
+
scope: scopeKey(scope)
|
|
31434
|
+
},
|
|
31435
|
+
outcome: {
|
|
31436
|
+
result: "recovered_rebuilt",
|
|
31437
|
+
durationMs: Date.now() - dormantMeta.detectedAt,
|
|
31438
|
+
dataLossSuspected: dormantMeta.droppedTaskCount > 0
|
|
31439
|
+
}
|
|
31440
|
+
});
|
|
31441
|
+
}
|
|
31442
|
+
}
|
|
31443
|
+
const pendingSpectateAt = this.pendingSpectate.get(key);
|
|
31444
|
+
if (pendingSpectateAt != null) {
|
|
31445
|
+
this.pendingSpectate.delete(key);
|
|
31446
|
+
runtime.spectating = true;
|
|
31447
|
+
runtime.spectateViewing = false;
|
|
31448
|
+
runtime.spectateActivatedAt = pendingSpectateAt;
|
|
31449
|
+
runtime.spectateTtlExpired = false;
|
|
31450
|
+
this.armSpectateTimer(runtime);
|
|
31451
|
+
this.emitSpectateState(runtime, true, "started");
|
|
31452
|
+
logger13.info("Applied pending spectate on runtime create", {
|
|
31453
|
+
agentId: agentConfig.id,
|
|
31454
|
+
scope: scopeKey(scope),
|
|
31455
|
+
activatedAt: pendingSpectateAt
|
|
31042
31456
|
});
|
|
31043
31457
|
}
|
|
31044
31458
|
if (proc.groupInbox.length > 0 && this.isRuntimeIdleForInboxFlush(runtime)) {
|
|
@@ -32176,7 +32590,6 @@ ${lines.join("\n")}`;
|
|
|
32176
32590
|
compactTrigger: "context_watermark",
|
|
32177
32591
|
injectedTasksWaiting: runtime.injectedTasks.length,
|
|
32178
32592
|
compactPromptLen: compactPrompt.length,
|
|
32179
|
-
promptSample: compactPrompt.slice(0, 80),
|
|
32180
32593
|
traceId: compactTraceId
|
|
32181
32594
|
});
|
|
32182
32595
|
runtime.inputController.push(compactPrompt, runtime.ccSessionId ?? "");
|
|
@@ -32458,7 +32871,7 @@ ${lines.join("\n")}`;
|
|
|
32458
32871
|
const enveloped = buildInnerVoiceEnvelope(payloadWithTrigger, ctx);
|
|
32459
32872
|
const task = {
|
|
32460
32873
|
content: enveloped,
|
|
32461
|
-
replyMessageId:
|
|
32874
|
+
replyMessageId: createNeuralSendReplyMessageId(),
|
|
32462
32875
|
conversationId: payload.conversationId,
|
|
32463
32876
|
traceId: createTraceId(),
|
|
32464
32877
|
groupId: payload.groupId
|
|
@@ -32704,7 +33117,7 @@ ${lines.join("\n")}`;
|
|
|
32704
33117
|
this.dormantScopes.delete(key);
|
|
32705
33118
|
this.dormantGroupInboxes.delete(key);
|
|
32706
33119
|
}
|
|
32707
|
-
for (const key of [...this.dormantScopes].filter(
|
|
33120
|
+
for (const key of [...this.dormantScopes.keys()].filter(
|
|
32708
33121
|
(k) => k === agentId || k.startsWith(`${agentId}::`)
|
|
32709
33122
|
)) {
|
|
32710
33123
|
this.dormantScopes.delete(key);
|
|
@@ -32752,7 +33165,7 @@ ${lines.join("\n")}`;
|
|
|
32752
33165
|
async reloadAgentScopes(agentId, reason) {
|
|
32753
33166
|
this.sessionStore.deleteAllForAgent(agentId);
|
|
32754
33167
|
this.dispatchMemory.deleteAllForAgent(agentId);
|
|
32755
|
-
for (const key of [...this.dormantScopes].filter(
|
|
33168
|
+
for (const key of [...this.dormantScopes.keys()].filter(
|
|
32756
33169
|
(k) => k === agentId || k.startsWith(`${agentId}::`)
|
|
32757
33170
|
)) {
|
|
32758
33171
|
this.dormantScopes.delete(key);
|
|
@@ -32904,6 +33317,125 @@ ${lines.join("\n")}`;
|
|
|
32904
33317
|
void this.terminateScope(proc.agentId, proc.scope);
|
|
32905
33318
|
}
|
|
32906
33319
|
}
|
|
33320
|
+
/** Control spectate capture/push for one scoped runtime. */
|
|
33321
|
+
async setSpectate(agentId, scope, action) {
|
|
33322
|
+
const key = runtimeKey(agentId, scope);
|
|
33323
|
+
const proc = this.agents.get(key);
|
|
33324
|
+
if (!proc || proc.status === "dead") {
|
|
33325
|
+
if (action === "start") {
|
|
33326
|
+
this.pendingSpectate.set(key, Date.now());
|
|
33327
|
+
logger13.info("setSpectate: runtime missing, pending start", { agentId, scope: scopeKey(scope) });
|
|
33328
|
+
}
|
|
33329
|
+
return;
|
|
33330
|
+
}
|
|
33331
|
+
const runtime = this.asRuntime(proc);
|
|
33332
|
+
switch (action) {
|
|
33333
|
+
case "start":
|
|
33334
|
+
runtime.spectating = true;
|
|
33335
|
+
runtime.spectateViewing = true;
|
|
33336
|
+
runtime.spectateActivatedAt = Date.now();
|
|
33337
|
+
runtime.spectateTtlExpired = false;
|
|
33338
|
+
this.pendingSpectate.delete(key);
|
|
33339
|
+
this.armSpectateTimer(runtime);
|
|
33340
|
+
this.emitSpectateState(runtime, true, "started");
|
|
33341
|
+
logger13.info("Spectate started", { agentId, scope: scopeKey(scope) });
|
|
33342
|
+
break;
|
|
33343
|
+
case "enter_view":
|
|
33344
|
+
runtime.spectateViewing = true;
|
|
33345
|
+
logger13.info("Spectate enter_view", { agentId, scope: scopeKey(scope) });
|
|
33346
|
+
break;
|
|
33347
|
+
case "leave_view":
|
|
33348
|
+
runtime.spectateViewing = false;
|
|
33349
|
+
logger13.info("Spectate leave_view", {
|
|
33350
|
+
agentId,
|
|
33351
|
+
scope: scopeKey(scope),
|
|
33352
|
+
ttlExpired: runtime.spectateTtlExpired
|
|
33353
|
+
});
|
|
33354
|
+
if (runtime.spectateTtlExpired) {
|
|
33355
|
+
this.stopSpectate(runtime, "ttl_expired");
|
|
33356
|
+
}
|
|
33357
|
+
break;
|
|
33358
|
+
case "stop":
|
|
33359
|
+
this.stopSpectate(runtime, "stopped");
|
|
33360
|
+
this.pendingSpectate.delete(key);
|
|
33361
|
+
logger13.info("Spectate stopped", { agentId, scope: scopeKey(scope) });
|
|
33362
|
+
break;
|
|
33363
|
+
default:
|
|
33364
|
+
break;
|
|
33365
|
+
}
|
|
33366
|
+
}
|
|
33367
|
+
spectateTtlMs() {
|
|
33368
|
+
return Number(process.env.AHCHAT_BRIDGE_SPECTATE_TTL_MS) || 36e5;
|
|
33369
|
+
}
|
|
33370
|
+
armSpectateTimer(runtime) {
|
|
33371
|
+
this.clearSpectateTimer(runtime);
|
|
33372
|
+
if (!runtime.spectating || runtime.spectateActivatedAt <= 0) return;
|
|
33373
|
+
const ttlMs = this.spectateTtlMs();
|
|
33374
|
+
const elapsed = Date.now() - runtime.spectateActivatedAt;
|
|
33375
|
+
const delay = Math.max(0, ttlMs - elapsed);
|
|
33376
|
+
runtime.spectateRevertTimer = setTimeout(() => {
|
|
33377
|
+
runtime.spectateRevertTimer = null;
|
|
33378
|
+
runtime.spectateTtlExpired = true;
|
|
33379
|
+
logger13.info("Spectate TTL expired", {
|
|
33380
|
+
agentId: runtime.agentId,
|
|
33381
|
+
scope: scopeKey(runtime.scope),
|
|
33382
|
+
viewing: runtime.spectateViewing
|
|
33383
|
+
});
|
|
33384
|
+
if (!runtime.spectateViewing) {
|
|
33385
|
+
this.stopSpectate(runtime, "ttl_expired");
|
|
33386
|
+
}
|
|
33387
|
+
}, delay);
|
|
33388
|
+
}
|
|
33389
|
+
clearSpectateTimer(runtime) {
|
|
33390
|
+
if (runtime.spectateRevertTimer != null) {
|
|
33391
|
+
clearTimeout(runtime.spectateRevertTimer);
|
|
33392
|
+
runtime.spectateRevertTimer = null;
|
|
33393
|
+
}
|
|
33394
|
+
}
|
|
33395
|
+
stopSpectate(runtime, reason) {
|
|
33396
|
+
const wasActive = runtime.spectating;
|
|
33397
|
+
this.clearSpectateTimer(runtime);
|
|
33398
|
+
runtime.spectating = false;
|
|
33399
|
+
runtime.spectateViewing = false;
|
|
33400
|
+
runtime.spectateTtlExpired = false;
|
|
33401
|
+
runtime.spectateActivatedAt = 0;
|
|
33402
|
+
if (wasActive) {
|
|
33403
|
+
this.emitSpectateState(runtime, false, reason);
|
|
33404
|
+
logger13.info("Spectate deactivated", {
|
|
33405
|
+
agentId: runtime.agentId,
|
|
33406
|
+
scope: scopeKey(runtime.scope),
|
|
33407
|
+
reason
|
|
33408
|
+
});
|
|
33409
|
+
}
|
|
33410
|
+
}
|
|
33411
|
+
emitSpectateState(runtime, active, reason) {
|
|
33412
|
+
const scopePayload = runtime.scope.kind === "single" ? { kind: "single" } : { kind: "group", groupId: runtime.scope.groupId };
|
|
33413
|
+
this.emit({
|
|
33414
|
+
type: "spectate:state",
|
|
33415
|
+
payload: {
|
|
33416
|
+
agentId: runtime.agentId,
|
|
33417
|
+
scope: scopePayload,
|
|
33418
|
+
active,
|
|
33419
|
+
expiresAt: active ? runtime.spectateActivatedAt + this.spectateTtlMs() : void 0,
|
|
33420
|
+
reason,
|
|
33421
|
+
traceId: createTraceId()
|
|
33422
|
+
}
|
|
33423
|
+
});
|
|
33424
|
+
logger13.info("Spectate state emitted", {
|
|
33425
|
+
agentId: runtime.agentId,
|
|
33426
|
+
scope: scopeKey(runtime.scope),
|
|
33427
|
+
active,
|
|
33428
|
+
reason,
|
|
33429
|
+
expiresAt: active ? runtime.spectateActivatedAt + this.spectateTtlMs() : void 0
|
|
33430
|
+
});
|
|
33431
|
+
}
|
|
33432
|
+
teardownSpectate(runtime) {
|
|
33433
|
+
if (runtime.spectating) {
|
|
33434
|
+
this.stopSpectate(runtime, "runtime_gone");
|
|
33435
|
+
} else {
|
|
33436
|
+
this.clearSpectateTimer(runtime);
|
|
33437
|
+
}
|
|
33438
|
+
}
|
|
32907
33439
|
/** Stop one scoped SDK runtime (workdir change). */
|
|
32908
33440
|
async terminateScope(agentId, scope) {
|
|
32909
33441
|
const key = runtimeKey(agentId, scope);
|
|
@@ -32912,6 +33444,7 @@ ${lines.join("\n")}`;
|
|
|
32912
33444
|
logger13.info("terminateScope: no active runtime", { agentId, scope: scopeKey(scope) });
|
|
32913
33445
|
this.dormantScopes.delete(key);
|
|
32914
33446
|
this.dormantGroupInboxes.delete(key);
|
|
33447
|
+
this.pendingSpectate.delete(key);
|
|
32915
33448
|
this.sessionStore.delete(agentId, scope);
|
|
32916
33449
|
this.dispatchMemory.deleteScope(agentId, scope);
|
|
32917
33450
|
return;
|
|
@@ -32934,7 +33467,7 @@ ${lines.join("\n")}`;
|
|
|
32934
33467
|
this.dispatchMemory.deleteScope(agentId, scope);
|
|
32935
33468
|
logger13.info("terminateScope: scoped query removed", { agentId, scope: scopeKey(scope) });
|
|
32936
33469
|
}
|
|
32937
|
-
async closeRuntime(proc, reason) {
|
|
33470
|
+
async closeRuntime(proc, reason, watchdogForensics) {
|
|
32938
33471
|
const key = runtimeKey(proc.agentId, proc.scope);
|
|
32939
33472
|
if (proc.status === "dead") return;
|
|
32940
33473
|
const runtime = this.asRuntime(proc);
|
|
@@ -33011,12 +33544,13 @@ ${lines.join("\n")}`;
|
|
|
33011
33544
|
runtime.currentTask = null;
|
|
33012
33545
|
if (isWatchdog) {
|
|
33013
33546
|
const preservedInbox = proc.groupInbox;
|
|
33014
|
-
|
|
33547
|
+
const preservedInboxSize = preservedInbox.length;
|
|
33548
|
+
if (preservedInboxSize > 0) {
|
|
33015
33549
|
this.dormantGroupInboxes.set(key, [...preservedInbox]);
|
|
33016
33550
|
logger13.info("Preserving groupInbox for dormant agent", {
|
|
33017
33551
|
agentId,
|
|
33018
33552
|
scope: scopeKey(proc.scope),
|
|
33019
|
-
preservedInboxSize
|
|
33553
|
+
preservedInboxSize,
|
|
33020
33554
|
preservedEntries: preservedInbox.map((e) => ({
|
|
33021
33555
|
ackId: e.ackId,
|
|
33022
33556
|
sender: e.senderName,
|
|
@@ -33025,7 +33559,26 @@ ${lines.join("\n")}`;
|
|
|
33025
33559
|
}))
|
|
33026
33560
|
});
|
|
33027
33561
|
}
|
|
33028
|
-
|
|
33562
|
+
const effectiveFallbackId = watchdogForensics?.fallbackId ?? createFallbackId();
|
|
33563
|
+
logFallback(logger13, {
|
|
33564
|
+
fallbackId: effectiveFallbackId,
|
|
33565
|
+
type: "zombie_watchdog",
|
|
33566
|
+
phase: "applied",
|
|
33567
|
+
expected: false,
|
|
33568
|
+
traceId: dormantTraceId,
|
|
33569
|
+
context: {
|
|
33570
|
+
agentId,
|
|
33571
|
+
scope: scopeKey(proc.scope),
|
|
33572
|
+
droppedTaskCount: droppedAckIds.length,
|
|
33573
|
+
preservedInboxSize,
|
|
33574
|
+
sessionDeleted: false
|
|
33575
|
+
}
|
|
33576
|
+
});
|
|
33577
|
+
this.dormantScopes.set(key, {
|
|
33578
|
+
fallbackId: effectiveFallbackId,
|
|
33579
|
+
detectedAt: watchdogForensics?.detectedAt ?? Date.now(),
|
|
33580
|
+
droppedTaskCount: droppedAckIds.length
|
|
33581
|
+
});
|
|
33029
33582
|
this.emit({
|
|
33030
33583
|
type: "agent:dormant",
|
|
33031
33584
|
payload: {
|
|
@@ -33040,13 +33593,15 @@ ${lines.join("\n")}`;
|
|
|
33040
33593
|
agentId,
|
|
33041
33594
|
scope: scopeKey(proc.scope),
|
|
33042
33595
|
droppedTaskCount: droppedAckIds.length,
|
|
33043
|
-
preservedInboxSize: this.dormantGroupInboxes.get(key)?.length ?? 0
|
|
33596
|
+
preservedInboxSize: this.dormantGroupInboxes.get(key)?.length ?? 0,
|
|
33597
|
+
fallbackId: effectiveFallbackId
|
|
33044
33598
|
});
|
|
33045
33599
|
}
|
|
33046
33600
|
proc.status = "dead";
|
|
33047
33601
|
this.agents.delete(key);
|
|
33048
33602
|
this.lastUsedAt.delete(key);
|
|
33049
33603
|
this.clearQuietFlushTimer(runtime);
|
|
33604
|
+
this.teardownSpectate(runtime);
|
|
33050
33605
|
try {
|
|
33051
33606
|
runtime.inputController.close();
|
|
33052
33607
|
await this.awaitQueryReturn(runtime.query, 5e3, agentId);
|
|
@@ -33063,6 +33618,165 @@ ${lines.join("\n")}`;
|
|
|
33063
33618
|
cwd: proc.cwd
|
|
33064
33619
|
});
|
|
33065
33620
|
}
|
|
33621
|
+
/**
|
|
33622
|
+
* Emit `agent:error` for the active reply and every queued/merged/buffered task,
|
|
33623
|
+
* then clear those queues. Used by both the SDK stream-crash path and the
|
|
33624
|
+
* reply-stall watchdog so a torn-down runtime never leaves a carrier reply
|
|
33625
|
+
* stuck in-flight on the server (which would keep absorbing new user messages
|
|
33626
|
+
* as steers of a dead turn).
|
|
33627
|
+
*/
|
|
33628
|
+
failPendingTasksWithError(runtime, errorText, fallbackId) {
|
|
33629
|
+
const pending = [];
|
|
33630
|
+
if (runtime.currentTask) pending.push(runtime.currentTask);
|
|
33631
|
+
pending.push(...runtime.injectedTasks, ...runtime.mergedTasks, ...runtime.planModeBuffer);
|
|
33632
|
+
runtime.currentTask = null;
|
|
33633
|
+
runtime.injectedTasks = [];
|
|
33634
|
+
runtime.mergedTasks = [];
|
|
33635
|
+
runtime.planModeBuffer = [];
|
|
33636
|
+
if (pending.length === 0) return { pendingCount: 0 };
|
|
33637
|
+
const carrier = pending[0];
|
|
33638
|
+
const mergedTasks = pending.slice(1);
|
|
33639
|
+
logger13.warn("Pending tasks failure consolidated", {
|
|
33640
|
+
agentId: runtime.agentId,
|
|
33641
|
+
scope: scopeKey(runtime.scope),
|
|
33642
|
+
pendingCount: pending.length,
|
|
33643
|
+
carrierAckId: carrier.replyMessageId,
|
|
33644
|
+
mergedAckIds: mergedTasks.map((t) => t.replyMessageId),
|
|
33645
|
+
traceId: carrier.traceId,
|
|
33646
|
+
...fallbackId ? { fallbackId } : {}
|
|
33647
|
+
});
|
|
33648
|
+
this.emit({
|
|
33649
|
+
type: "agent:error",
|
|
33650
|
+
payload: {
|
|
33651
|
+
agentId: runtime.agentId,
|
|
33652
|
+
conversationId: carrier.conversationId,
|
|
33653
|
+
ackId: carrier.replyMessageId,
|
|
33654
|
+
traceId: carrier.traceId,
|
|
33655
|
+
error: errorText
|
|
33656
|
+
}
|
|
33657
|
+
});
|
|
33658
|
+
for (const task of mergedTasks) {
|
|
33659
|
+
this.emit({
|
|
33660
|
+
type: "agent:merged",
|
|
33661
|
+
payload: {
|
|
33662
|
+
agentId: runtime.agentId,
|
|
33663
|
+
conversationId: task.conversationId,
|
|
33664
|
+
ackId: task.replyMessageId,
|
|
33665
|
+
mergedIntoAckId: carrier.replyMessageId,
|
|
33666
|
+
groupId: task.groupId,
|
|
33667
|
+
traceId: task.traceId
|
|
33668
|
+
}
|
|
33669
|
+
});
|
|
33670
|
+
}
|
|
33671
|
+
return { pendingCount: pending.length };
|
|
33672
|
+
}
|
|
33673
|
+
/**
|
|
33674
|
+
* Recover an in-flight reply that started but went silent past
|
|
33675
|
+
* `replyStallTimeoutMs` (see the reply-stall fast path in `evictIdle`). The
|
|
33676
|
+
* underlying SDK turn is wedged with no observable progress and no error, so:
|
|
33677
|
+
* 1. clear the (likely interrupted/dangling) session so the next dispatch
|
|
33678
|
+
* starts fresh instead of resuming the same wedged transcript;
|
|
33679
|
+
* 2. release the carrier reply + queued steers via `agent:error` so the
|
|
33680
|
+
* client stops waiting and the next user message starts a brand-new reply;
|
|
33681
|
+
* 3. tear the wedged runtime down.
|
|
33682
|
+
*/
|
|
33683
|
+
async recoverStalledReply(proc, silentMs) {
|
|
33684
|
+
if (proc.status === "dead") return;
|
|
33685
|
+
const runtime = this.asRuntime(proc);
|
|
33686
|
+
const key = runtimeKey(proc.agentId, proc.scope);
|
|
33687
|
+
const replyStallFallbackId = createFallbackId();
|
|
33688
|
+
const stallTraceId = proc.currentTask?.traceId;
|
|
33689
|
+
logger13.warn("Reply stall watchdog: in-flight reply silent too long, recovering", {
|
|
33690
|
+
agentId: proc.agentId,
|
|
33691
|
+
scope: scopeKey(proc.scope),
|
|
33692
|
+
silentMs,
|
|
33693
|
+
replyStallTimeoutMs: this.queryConfig.replyStallTimeoutMs,
|
|
33694
|
+
replyMessageId: proc.currentTask?.replyMessageId,
|
|
33695
|
+
injectedTaskCount: runtime.injectedTasks.length,
|
|
33696
|
+
lastSdkEventAt: new Date(proc.lastSdkEventAt).toISOString(),
|
|
33697
|
+
fallbackId: replyStallFallbackId,
|
|
33698
|
+
// Breadcrumb: what the wedged turn was doing the instant it went silent.
|
|
33699
|
+
// (subagent Task call? mid tool_use? which provider?) — the difference
|
|
33700
|
+
// between a one-off and a systemic provider/tool stall.
|
|
33701
|
+
model: proc.model ?? "(unknown)",
|
|
33702
|
+
lastSdkEvent: proc.lastSdkEventInfo,
|
|
33703
|
+
currentBlockType: proc.currentBlockType,
|
|
33704
|
+
currentToolName: proc.currentToolName,
|
|
33705
|
+
openMcpInvocationId: proc.currentMcpInvocationId ?? null
|
|
33706
|
+
});
|
|
33707
|
+
logFallback(logger13, {
|
|
33708
|
+
fallbackId: replyStallFallbackId,
|
|
33709
|
+
type: "reply_stall",
|
|
33710
|
+
phase: "detected",
|
|
33711
|
+
expected: false,
|
|
33712
|
+
traceId: stallTraceId,
|
|
33713
|
+
context: {
|
|
33714
|
+
agentId: proc.agentId,
|
|
33715
|
+
scope: scopeKey(proc.scope),
|
|
33716
|
+
silentMs,
|
|
33717
|
+
model: proc.model ?? "(unknown)",
|
|
33718
|
+
replyMessageId: proc.currentTask?.replyMessageId ?? null,
|
|
33719
|
+
currentToolName: proc.currentToolName ?? null,
|
|
33720
|
+
lastSdkEventType: proc.lastSdkEventInfo?.type ?? null,
|
|
33721
|
+
injectedTaskCount: runtime.injectedTasks.length,
|
|
33722
|
+
mergedTaskCount: runtime.mergedTasks.length,
|
|
33723
|
+
planModeBufferCount: runtime.planModeBuffer.length
|
|
33724
|
+
}
|
|
33725
|
+
});
|
|
33726
|
+
this.sessionStore.delete(proc.agentId, proc.scope);
|
|
33727
|
+
this.dispatchMemory.deleteScope(proc.agentId, proc.scope);
|
|
33728
|
+
const failSummary = this.failPendingTasksWithError(
|
|
33729
|
+
runtime,
|
|
33730
|
+
"\u56DE\u590D\u957F\u65F6\u95F4\u65E0\u54CD\u5E94\uFF0C\u5DF2\u91CD\u7F6E\u8BE5\u4F1A\u8BDD\uFF0C\u8BF7\u91CD\u65B0\u53D1\u9001\u6D88\u606F\u3002",
|
|
33731
|
+
replyStallFallbackId
|
|
33732
|
+
);
|
|
33733
|
+
proc.status = "dead";
|
|
33734
|
+
this.agents.delete(key);
|
|
33735
|
+
this.lastUsedAt.delete(key);
|
|
33736
|
+
this.clearQuietFlushTimer(runtime);
|
|
33737
|
+
let queryCloseOk = true;
|
|
33738
|
+
try {
|
|
33739
|
+
runtime.inputController.close();
|
|
33740
|
+
await this.awaitQueryReturn(runtime.query, 5e3, proc.agentId);
|
|
33741
|
+
} catch (e) {
|
|
33742
|
+
queryCloseOk = false;
|
|
33743
|
+
logger13.error("reply_stall: close query failed", {
|
|
33744
|
+
agentId: proc.agentId,
|
|
33745
|
+
scope: scopeKey(proc.scope),
|
|
33746
|
+
error: e
|
|
33747
|
+
});
|
|
33748
|
+
}
|
|
33749
|
+
logFallback(logger13, {
|
|
33750
|
+
fallbackId: replyStallFallbackId,
|
|
33751
|
+
type: "reply_stall",
|
|
33752
|
+
phase: "applied",
|
|
33753
|
+
expected: false,
|
|
33754
|
+
traceId: stallTraceId,
|
|
33755
|
+
context: {
|
|
33756
|
+
agentId: proc.agentId,
|
|
33757
|
+
scope: scopeKey(proc.scope),
|
|
33758
|
+
sessionDeleted: true,
|
|
33759
|
+
failedTaskCount: failSummary.pendingCount,
|
|
33760
|
+
queryClosed: queryCloseOk
|
|
33761
|
+
}
|
|
33762
|
+
});
|
|
33763
|
+
logFallback(logger13, {
|
|
33764
|
+
fallbackId: replyStallFallbackId,
|
|
33765
|
+
type: "reply_stall",
|
|
33766
|
+
phase: "outcome",
|
|
33767
|
+
expected: false,
|
|
33768
|
+
traceId: stallTraceId,
|
|
33769
|
+
context: {
|
|
33770
|
+
agentId: proc.agentId,
|
|
33771
|
+
scope: scopeKey(proc.scope),
|
|
33772
|
+
failedTaskCount: failSummary.pendingCount
|
|
33773
|
+
},
|
|
33774
|
+
outcome: {
|
|
33775
|
+
result: "session_reset_awaiting_user",
|
|
33776
|
+
dataLossSuspected: failSummary.pendingCount > 0
|
|
33777
|
+
}
|
|
33778
|
+
});
|
|
33779
|
+
}
|
|
33066
33780
|
async recoverFromRestart(agents) {
|
|
33067
33781
|
const lockSnapshot = readCronLockSnapshot();
|
|
33068
33782
|
logger13.info("Recovering Agent sessions after restart", {
|
|
@@ -33185,58 +33899,7 @@ ${lines.join("\n")}`;
|
|
|
33185
33899
|
this.lastUsedAt.delete(key);
|
|
33186
33900
|
const errorText = isResumeFail ? `\u4F1A\u8BDD\u5DF2\u8FC7\u671F\uFF0C\u8BF7\u91CD\u65B0\u53D1\u9001\u6D88\u606F\uFF08${errMsg}\uFF09` : `Agent query crashed: ${errMsg}`;
|
|
33187
33901
|
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 = [];
|
|
33902
|
+
this.failPendingTasksWithError(runtime, emittedErrorText);
|
|
33240
33903
|
}
|
|
33241
33904
|
}
|
|
33242
33905
|
getStatus(agentId, scope = { kind: "single" }) {
|
|
@@ -33250,6 +33913,18 @@ ${lines.join("\n")}`;
|
|
|
33250
33913
|
}
|
|
33251
33914
|
return [...ids];
|
|
33252
33915
|
}
|
|
33916
|
+
/** Unified signal: is the turn legitimately waiting on a live external call that emits no
|
|
33917
|
+
* parent heartbeat? Used by BOTH the reply-stall fast path and the zombie watchdog so neither
|
|
33918
|
+
* tears down a turn that is merely slow. Returns the reason for diagnostics, or null when idle.
|
|
33919
|
+
* - open_tool: regular tool, MCP tool, AskUserQuestion wait, or ExitPlanMode (tool_use open).
|
|
33920
|
+
* - subagent: Task/Agent in flight (its inner tool_results clear activeToolUseStartedAt).
|
|
33921
|
+
* - compact: bridge-injected /compact running. */
|
|
33922
|
+
busyReason(proc) {
|
|
33923
|
+
if (proc.activeToolUseStartedAt != null || this.latestOpenToolUse(proc) != null) return "open_tool";
|
|
33924
|
+
if ((proc.activeSubagentTaskIds?.size ?? 0) > 0) return "subagent";
|
|
33925
|
+
if (proc.compactInProgress === true) return "compact";
|
|
33926
|
+
return null;
|
|
33927
|
+
}
|
|
33253
33928
|
latestOpenToolUse(proc) {
|
|
33254
33929
|
for (let i = proc.contentBlocks.length - 1; i >= 0; i -= 1) {
|
|
33255
33930
|
const block = proc.contentBlocks[i];
|
|
@@ -33398,7 +34073,7 @@ ${lines.join("\n")}`;
|
|
|
33398
34073
|
}
|
|
33399
34074
|
const task = {
|
|
33400
34075
|
content: notice,
|
|
33401
|
-
replyMessageId:
|
|
34076
|
+
replyMessageId: createScopeNoticeReplyMessageId(),
|
|
33402
34077
|
conversationId,
|
|
33403
34078
|
traceId: createTraceId(),
|
|
33404
34079
|
groupId: proc.scope.kind === "group" ? proc.scope.groupId : void 0
|
|
@@ -34480,6 +35155,7 @@ var HttpMcpRegistry = class {
|
|
|
34480
35155
|
buildForAgent(ctx) {
|
|
34481
35156
|
const mcpServers = {};
|
|
34482
35157
|
const allowedTools = [];
|
|
35158
|
+
const toolAbi = [];
|
|
34483
35159
|
const usedNames = /* @__PURE__ */ new Set();
|
|
34484
35160
|
for (const connection of this.allConnections()) {
|
|
34485
35161
|
if (!this.connectionAppliesToAgent(connection, ctx)) continue;
|
|
@@ -34488,12 +35164,18 @@ var HttpMcpRegistry = class {
|
|
|
34488
35164
|
const serverName = uniqueServerName(normalizeMcpServerName(connection.serverName), usedNames);
|
|
34489
35165
|
usedNames.add(serverName);
|
|
34490
35166
|
mcpServers[serverName] = sdkConfig;
|
|
34491
|
-
|
|
34492
|
-
|
|
34493
|
-
|
|
34494
|
-
|
|
35167
|
+
const visibleTools = connection.tools.filter((tool2) => tool2.enabled && tool2.permissionPolicy !== "always_deny");
|
|
35168
|
+
for (const tool2 of visibleTools) allowedTools.push(mcpRuntimeToolName(serverName, tool2.name));
|
|
35169
|
+
toolAbi.push({
|
|
35170
|
+
serverName,
|
|
35171
|
+
providerId: connection.providerId,
|
|
35172
|
+
transport: connection.transport,
|
|
35173
|
+
alwaysLoad: connection.alwaysLoad,
|
|
35174
|
+
isBuiltin: connection.isBuiltin,
|
|
35175
|
+
tools: visibleTools.map((tool2) => runtimeToolAbi(serverName, tool2)).sort((a, b) => a.runtimeToolName.localeCompare(b.runtimeToolName))
|
|
35176
|
+
});
|
|
34495
35177
|
}
|
|
34496
|
-
return { mcpServers, allowedTools };
|
|
35178
|
+
return { mcpServers, allowedTools, toolAbi };
|
|
34497
35179
|
}
|
|
34498
35180
|
allConnections() {
|
|
34499
35181
|
return [...this.serverConnections.values(), ...this.localConnections.values()];
|
|
@@ -34679,6 +35361,18 @@ function uniqueServerName(serverName, usedNames) {
|
|
|
34679
35361
|
while (usedNames.has(`${serverName}_${idx}`)) idx += 1;
|
|
34680
35362
|
return `${serverName}_${idx}`;
|
|
34681
35363
|
}
|
|
35364
|
+
function runtimeToolAbi(serverName, tool2) {
|
|
35365
|
+
return {
|
|
35366
|
+
name: tool2.name,
|
|
35367
|
+
runtimeToolName: mcpRuntimeToolName(serverName, tool2.name),
|
|
35368
|
+
displayName: tool2.displayName,
|
|
35369
|
+
description: tool2.description,
|
|
35370
|
+
category: tool2.category,
|
|
35371
|
+
riskLevel: tool2.riskLevel,
|
|
35372
|
+
permissionPolicy: tool2.permissionPolicy,
|
|
35373
|
+
...tool2.inputSchema !== void 0 ? { inputSchema: tool2.inputSchema } : {}
|
|
35374
|
+
};
|
|
35375
|
+
}
|
|
34682
35376
|
function buildHeaders(authType, authSecret, customHeaders) {
|
|
34683
35377
|
const headers = {};
|
|
34684
35378
|
for (const header of customHeaders) {
|
|
@@ -35330,6 +36024,7 @@ var ServerConnector = class {
|
|
|
35330
36024
|
case "agent:terminate":
|
|
35331
36025
|
case "agent:runtime_reload":
|
|
35332
36026
|
case "agent:terminate_scope":
|
|
36027
|
+
case "spectate:set":
|
|
35333
36028
|
case "agent:created":
|
|
35334
36029
|
case "agent:updated":
|
|
35335
36030
|
case "agent:workdir-updated":
|
|
@@ -36305,24 +37000,6 @@ function normalizeLocalPath(targetPath) {
|
|
|
36305
37000
|
const expanded = trimmed === "~" || trimmed.startsWith("~/") || trimmed.startsWith("~\\") ? path18.join(os10.homedir(), trimmed.slice(2)) : trimmed;
|
|
36306
37001
|
return path18.normalize(path18.resolve(expanded));
|
|
36307
37002
|
}
|
|
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
37003
|
function normalizeClipboardIdentityKey(value) {
|
|
36327
37004
|
return process.platform === "win32" ? value.toLowerCase() : value;
|
|
36328
37005
|
}
|
|
@@ -36360,18 +37037,15 @@ function mimeTypeForFileName(fileName) {
|
|
|
36360
37037
|
}
|
|
36361
37038
|
function parseWindowsClipboardResult(stdout) {
|
|
36362
37039
|
const raw = stdout.trim();
|
|
36363
|
-
if (!raw) return { files: []
|
|
37040
|
+
if (!raw) return { files: [] };
|
|
36364
37041
|
try {
|
|
36365
37042
|
const parsed = JSON.parse(raw);
|
|
36366
|
-
if (!isRecord5(parsed)) return { files: []
|
|
37043
|
+
if (!isRecord5(parsed)) return { files: [] };
|
|
36367
37044
|
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
|
-
};
|
|
37045
|
+
return { files };
|
|
36372
37046
|
} catch (e) {
|
|
36373
37047
|
logger24.debug("Windows clipboard JSON parse skipped", { error: e });
|
|
36374
|
-
return { files:
|
|
37048
|
+
return { files: [] };
|
|
36375
37049
|
}
|
|
36376
37050
|
}
|
|
36377
37051
|
async function readWindowsClipboardPathCandidates() {
|
|
@@ -36384,9 +37058,7 @@ async function readWindowsClipboardPathCandidates() {
|
|
|
36384
37058
|
" $drop = [System.Windows.Forms.Clipboard]::GetFileDropList();",
|
|
36385
37059
|
" foreach ($file in $drop) { $files += [string]$file }",
|
|
36386
37060
|
"} catch {}",
|
|
36387
|
-
|
|
36388
|
-
"try { $text = [System.Windows.Forms.Clipboard]::GetText() } catch {}",
|
|
36389
|
-
"[pscustomobject]@{ files = $files; text = $text } | ConvertTo-Json -Compress;"
|
|
37061
|
+
"[pscustomobject]@{ files = $files } | ConvertTo-Json -Compress;"
|
|
36390
37062
|
].join(" ");
|
|
36391
37063
|
try {
|
|
36392
37064
|
const { stdout } = await execFileAsync2("powershell.exe", [
|
|
@@ -36404,7 +37076,7 @@ async function readWindowsClipboardPathCandidates() {
|
|
|
36404
37076
|
maxBuffer: 1024 * 1024
|
|
36405
37077
|
});
|
|
36406
37078
|
const result = parseWindowsClipboardResult(stdout);
|
|
36407
|
-
return
|
|
37079
|
+
return result.files;
|
|
36408
37080
|
} catch (e) {
|
|
36409
37081
|
logger24.debug("Windows clipboard file read skipped", { error: e });
|
|
36410
37082
|
return [];
|
|
@@ -36807,7 +37479,7 @@ async function readStreamText(filePath, start) {
|
|
|
36807
37479
|
function parseProcessedLines(raw, cursor, fileName, fingerprint) {
|
|
36808
37480
|
const lastNewline = raw.lastIndexOf("\n");
|
|
36809
37481
|
if (lastNewline < 0) {
|
|
36810
|
-
return { entries: [], nextCursor: cursor, advanced: false };
|
|
37482
|
+
return { entries: [], nextCursor: cursor, advanced: false, reason: "partial_line" };
|
|
36811
37483
|
}
|
|
36812
37484
|
const processed = raw.slice(0, lastNewline + 1);
|
|
36813
37485
|
const lines = processed.split(/\r?\n/);
|
|
@@ -36833,7 +37505,8 @@ function parseProcessedLines(raw, cursor, fileName, fingerprint) {
|
|
|
36833
37505
|
lineNum,
|
|
36834
37506
|
...fingerprint ? { fingerprint } : {}
|
|
36835
37507
|
},
|
|
36836
|
-
advanced: true
|
|
37508
|
+
advanced: true,
|
|
37509
|
+
reason: "advanced"
|
|
36837
37510
|
};
|
|
36838
37511
|
}
|
|
36839
37512
|
function chunkEntries(entries, size) {
|
|
@@ -36891,24 +37564,55 @@ var BridgeLogUploader = class {
|
|
|
36891
37564
|
async flushOnce() {
|
|
36892
37565
|
if (this.running || this.stopped) return;
|
|
36893
37566
|
this.running = true;
|
|
37567
|
+
const startedAt = Date.now();
|
|
37568
|
+
const summary = {
|
|
37569
|
+
targetCount: 0,
|
|
37570
|
+
advancedTargetCount: 0,
|
|
37571
|
+
missingTargetCount: 0,
|
|
37572
|
+
idleTargetCount: 0,
|
|
37573
|
+
partialLineTargetCount: 0,
|
|
37574
|
+
failedTargetCount: 0,
|
|
37575
|
+
parsedEntryCount: 0,
|
|
37576
|
+
bridgeEntryCount: 0,
|
|
37577
|
+
uploadedChunkCount: 0,
|
|
37578
|
+
accepted: 0,
|
|
37579
|
+
skipped: 0
|
|
37580
|
+
};
|
|
36894
37581
|
try {
|
|
36895
37582
|
const targets = await this.resolveTargets();
|
|
37583
|
+
summary.targetCount = targets.length;
|
|
36896
37584
|
for (const target of targets) {
|
|
36897
37585
|
try {
|
|
36898
37586
|
const cursor = await readCursor(target.cursorFile);
|
|
36899
37587
|
const batch = await this.readNewEntries(target, cursor);
|
|
36900
|
-
if (!batch.advanced)
|
|
37588
|
+
if (!batch.advanced) {
|
|
37589
|
+
summary.idleTargetCount += 1;
|
|
37590
|
+
if (batch.reason === "missing_file") summary.missingTargetCount += 1;
|
|
37591
|
+
if (batch.reason === "partial_line") summary.partialLineTargetCount += 1;
|
|
37592
|
+
continue;
|
|
37593
|
+
}
|
|
37594
|
+
summary.advancedTargetCount += 1;
|
|
37595
|
+
summary.parsedEntryCount += batch.entries.length;
|
|
36901
37596
|
if (batch.entries.length > 0) {
|
|
36902
|
-
await this.uploadEntries(batch.entries);
|
|
37597
|
+
const result = await this.uploadEntries(batch.entries);
|
|
37598
|
+
summary.bridgeEntryCount += result.bridgeEntryCount;
|
|
37599
|
+
summary.uploadedChunkCount += result.uploadedChunkCount;
|
|
37600
|
+
summary.accepted += result.accepted;
|
|
37601
|
+
summary.skipped += result.skipped;
|
|
36903
37602
|
}
|
|
36904
37603
|
await writeCursor(target.cursorFile, batch.nextCursor);
|
|
36905
37604
|
} catch (e) {
|
|
37605
|
+
summary.failedTargetCount += 1;
|
|
36906
37606
|
logger27.warn("Bridge log upload target failed", { error: e, logFile: target.logFile });
|
|
36907
37607
|
}
|
|
36908
37608
|
}
|
|
36909
37609
|
} catch (e) {
|
|
36910
37610
|
logger27.warn("Bridge log upload cycle failed", { error: e });
|
|
36911
37611
|
} finally {
|
|
37612
|
+
logger27.info("Bridge log upload cycle summary", {
|
|
37613
|
+
...summary,
|
|
37614
|
+
durationMs: Date.now() - startedAt
|
|
37615
|
+
});
|
|
36912
37616
|
this.running = false;
|
|
36913
37617
|
}
|
|
36914
37618
|
}
|
|
@@ -36931,7 +37635,7 @@ var BridgeLogUploader = class {
|
|
|
36931
37635
|
} catch (e) {
|
|
36932
37636
|
if (e instanceof Error && "code" in e && e.code === "ENOENT") {
|
|
36933
37637
|
logger27.debug("Bridge log file not found for upload yet", { logFile: target.logFile });
|
|
36934
|
-
return { entries: [], nextCursor: cursor, advanced: false };
|
|
37638
|
+
return { entries: [], nextCursor: cursor, advanced: false, reason: "missing_file" };
|
|
36935
37639
|
}
|
|
36936
37640
|
throw e;
|
|
36937
37641
|
}
|
|
@@ -36939,13 +37643,19 @@ var BridgeLogUploader = class {
|
|
|
36939
37643
|
const samePhysicalFile = !fingerprint || !cursor.fingerprint || cursor.fingerprint === fingerprint;
|
|
36940
37644
|
const normalizedCursor = stat3.size < cursor.offset || !samePhysicalFile ? { offset: 0, lineNum: 0, ...fingerprint ? { fingerprint } : {} } : { ...cursor, ...fingerprint ? { fingerprint } : {} };
|
|
36941
37645
|
if (stat3.size <= normalizedCursor.offset) {
|
|
36942
|
-
return { entries: [], nextCursor: normalizedCursor, advanced: false };
|
|
37646
|
+
return { entries: [], nextCursor: normalizedCursor, advanced: false, reason: "no_new_entries" };
|
|
36943
37647
|
}
|
|
36944
37648
|
const raw = await readStreamText(target.logFile, normalizedCursor.offset);
|
|
36945
37649
|
return parseProcessedLines(raw, normalizedCursor, target.uploadedFileName, fingerprint);
|
|
36946
37650
|
}
|
|
36947
37651
|
async uploadEntries(entries) {
|
|
36948
37652
|
const bridgeEntries = entries.filter((entry) => entry.source === "bridge");
|
|
37653
|
+
const result = {
|
|
37654
|
+
bridgeEntryCount: bridgeEntries.length,
|
|
37655
|
+
uploadedChunkCount: 0,
|
|
37656
|
+
accepted: 0,
|
|
37657
|
+
skipped: 0
|
|
37658
|
+
};
|
|
36949
37659
|
for (const chunk of chunkEntries(bridgeEntries, this.options.batchSize)) {
|
|
36950
37660
|
const res = await fetch(`${this.options.serverApiUrl}/api/logs/upload`, {
|
|
36951
37661
|
method: "POST",
|
|
@@ -36960,14 +37670,18 @@ var BridgeLogUploader = class {
|
|
|
36960
37670
|
})
|
|
36961
37671
|
});
|
|
36962
37672
|
if (!res.ok) {
|
|
36963
|
-
const
|
|
37673
|
+
const body2 = await res.text().catch((e) => {
|
|
36964
37674
|
logger27.debug("Failed to read log upload error body", { error: e });
|
|
36965
37675
|
return "";
|
|
36966
37676
|
});
|
|
36967
|
-
throw new Error(`upload failed HTTP ${res.status}: ${
|
|
37677
|
+
throw new Error(`upload failed HTTP ${res.status}: ${body2.slice(0, 160)}`);
|
|
36968
37678
|
}
|
|
36969
|
-
await res.json();
|
|
37679
|
+
const body = await res.json();
|
|
37680
|
+
result.uploadedChunkCount += 1;
|
|
37681
|
+
result.accepted += typeof body.accepted === "number" ? body.accepted : 0;
|
|
37682
|
+
result.skipped += typeof body.skipped === "number" ? body.skipped : 0;
|
|
36970
37683
|
}
|
|
37684
|
+
return result;
|
|
36971
37685
|
}
|
|
36972
37686
|
};
|
|
36973
37687
|
|
|
@@ -38052,8 +38766,10 @@ function resolvePnpmRuntimeBinary(sdkDir, target) {
|
|
|
38052
38766
|
} catch {
|
|
38053
38767
|
return void 0;
|
|
38054
38768
|
}
|
|
38769
|
+
const matches = [];
|
|
38055
38770
|
for (const entry of entries) {
|
|
38056
38771
|
if (!entry.startsWith(`${encodedName}@`)) continue;
|
|
38772
|
+
const version2 = entry.slice(encodedName.length + 1);
|
|
38057
38773
|
const candidate = path27.join(
|
|
38058
38774
|
pnpmStoreDir,
|
|
38059
38775
|
entry,
|
|
@@ -38061,9 +38777,22 @@ function resolvePnpmRuntimeBinary(sdkDir, target) {
|
|
|
38061
38777
|
...target.packageName.split("/"),
|
|
38062
38778
|
target.binaryName
|
|
38063
38779
|
);
|
|
38064
|
-
if (existsSync2(candidate))
|
|
38780
|
+
if (existsSync2(candidate)) matches.push({ version: version2, candidate });
|
|
38065
38781
|
}
|
|
38066
|
-
return void 0;
|
|
38782
|
+
if (matches.length === 0) return void 0;
|
|
38783
|
+
matches.sort((a, b) => compareRuntimeVersion(b.version, a.version));
|
|
38784
|
+
return matches[0].candidate;
|
|
38785
|
+
}
|
|
38786
|
+
function compareRuntimeVersion(a, b) {
|
|
38787
|
+
const parse3 = (v) => v.split(/[.+_-]/).map((n) => Number.parseInt(n, 10));
|
|
38788
|
+
const pa = parse3(a);
|
|
38789
|
+
const pb = parse3(b);
|
|
38790
|
+
for (let i = 0; i < Math.max(pa.length, pb.length); i += 1) {
|
|
38791
|
+
const da = Number.isFinite(pa[i]) ? pa[i] : 0;
|
|
38792
|
+
const db = Number.isFinite(pb[i]) ? pb[i] : 0;
|
|
38793
|
+
if (da !== db) return da - db;
|
|
38794
|
+
}
|
|
38795
|
+
return 0;
|
|
38067
38796
|
}
|
|
38068
38797
|
function resolveSdkRuntimeBinary(target) {
|
|
38069
38798
|
const directPath = safeResolve(`${target.packageName}/${target.binaryName}`);
|
|
@@ -39222,6 +39951,32 @@ function syncLocalRuntimeSkills(skillStore, localSkills, options = {}) {
|
|
|
39222
39951
|
]);
|
|
39223
39952
|
}
|
|
39224
39953
|
|
|
39954
|
+
// src/processOutput.ts
|
|
39955
|
+
var protectedStreams2 = /* @__PURE__ */ new WeakSet();
|
|
39956
|
+
var reportedErrors = /* @__PURE__ */ new WeakSet();
|
|
39957
|
+
function reportWriteError(error51, onError) {
|
|
39958
|
+
if (typeof error51 === "object" && error51 !== null) {
|
|
39959
|
+
if (reportedErrors.has(error51)) return;
|
|
39960
|
+
reportedErrors.add(error51);
|
|
39961
|
+
}
|
|
39962
|
+
onError(error51);
|
|
39963
|
+
}
|
|
39964
|
+
function safeWriteProcessOutput(stream, text, onError) {
|
|
39965
|
+
if (!stream) return;
|
|
39966
|
+
if (stream.destroyed || stream.writableEnded) return;
|
|
39967
|
+
if (typeof stream === "object" && typeof stream.on === "function" && !protectedStreams2.has(stream)) {
|
|
39968
|
+
protectedStreams2.add(stream);
|
|
39969
|
+
stream.on("error", (e) => reportWriteError(e, onError));
|
|
39970
|
+
}
|
|
39971
|
+
try {
|
|
39972
|
+
stream.write(text, (error51) => {
|
|
39973
|
+
if (error51) reportWriteError(error51, onError);
|
|
39974
|
+
});
|
|
39975
|
+
} catch (e) {
|
|
39976
|
+
reportWriteError(e, onError);
|
|
39977
|
+
}
|
|
39978
|
+
}
|
|
39979
|
+
|
|
39225
39980
|
// src/start.ts
|
|
39226
39981
|
var logger41 = createModuleLogger("bridge");
|
|
39227
39982
|
var NODE_USER_UID2 = 1e3;
|
|
@@ -39308,14 +40063,16 @@ async function startBridge(config2) {
|
|
|
39308
40063
|
const claudeRuntime = resolveClaudeRuntime();
|
|
39309
40064
|
logClaudeRuntimeResolution(claudeRuntime);
|
|
39310
40065
|
if (!claudeRuntime.ok || !claudeRuntime.path) {
|
|
39311
|
-
|
|
40066
|
+
safeWriteProcessOutput(
|
|
40067
|
+
process.stderr,
|
|
39312
40068
|
`
|
|
39313
40069
|
Claude runtime is unavailable.
|
|
39314
40070
|
|
|
39315
40071
|
${claudeRuntime.error ?? "Install Claude Code manually or use the bundled desktop runtime."}
|
|
39316
40072
|
|
|
39317
40073
|
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
|
-
|
|
40074
|
+
`,
|
|
40075
|
+
(e) => logger41.error("Bridge process stderr write failed", { error: e })
|
|
39319
40076
|
);
|
|
39320
40077
|
process.exit(1);
|
|
39321
40078
|
}
|
|
@@ -39372,12 +40129,14 @@ Reinstall @fangyb/ahchat-bridge with npm optional dependencies, set AHCHAT_CLAUD
|
|
|
39372
40129
|
claudeRuntimeVersion: claudeRuntime.version ?? null
|
|
39373
40130
|
});
|
|
39374
40131
|
const shouldPrintRawBridgeToken = process.stdout.isTTY && process.env.AHCHAT_SUPPRESS_BRIDGE_TOKEN_STDOUT !== "1";
|
|
39375
|
-
|
|
40132
|
+
safeWriteProcessOutput(
|
|
40133
|
+
process.stdout,
|
|
39376
40134
|
`
|
|
39377
40135
|
Bridge token (register this machine at Settings \u2192 \u5DF2\u8FDE\u63A5\u7684\u673A\u5668):
|
|
39378
40136
|
${shouldPrintRawBridgeToken ? config2.bridgeToken : "***"}
|
|
39379
40137
|
|
|
39380
|
-
|
|
40138
|
+
`,
|
|
40139
|
+
(e) => logger41.error("Bridge process stdout write failed", { error: e })
|
|
39381
40140
|
);
|
|
39382
40141
|
wsMetrics.start(5e3);
|
|
39383
40142
|
const sessionStore = new SessionStore(config2.dataDir);
|
|
@@ -40176,6 +40935,19 @@ Bridge token (register this machine at Settings \u2192 \u5DF2\u8FDE\u63A5\u7684\
|
|
|
40176
40935
|
});
|
|
40177
40936
|
await agentManager.terminateScope(msg.payload.agentId, msg.payload.scope);
|
|
40178
40937
|
break;
|
|
40938
|
+
case "spectate:set":
|
|
40939
|
+
logger41.info("spectate:set received", {
|
|
40940
|
+
agentId: msg.payload.agentId,
|
|
40941
|
+
scope: msg.payload.scope,
|
|
40942
|
+
action: msg.payload.action,
|
|
40943
|
+
traceId: msg.payload.traceId
|
|
40944
|
+
});
|
|
40945
|
+
await agentManager.setSpectate(
|
|
40946
|
+
msg.payload.agentId,
|
|
40947
|
+
msg.payload.scope,
|
|
40948
|
+
msg.payload.action
|
|
40949
|
+
);
|
|
40950
|
+
break;
|
|
40179
40951
|
case "agent:created":
|
|
40180
40952
|
agentRegistry.upsert(msg.payload.agent);
|
|
40181
40953
|
ensureLocalWorkdirPath(msg.payload.agent.workingDirectory, "agent:created", {
|
|
@@ -40392,8 +41164,12 @@ function writeAlreadyRunningMessage(error51) {
|
|
|
40392
41164
|
` ${buildStopCommand(error51.pid)}`,
|
|
40393
41165
|
""
|
|
40394
41166
|
];
|
|
40395
|
-
|
|
40396
|
-
|
|
41167
|
+
safeWriteProcessOutput(
|
|
41168
|
+
process.stdout,
|
|
41169
|
+
`${lines.join("\n")}
|
|
41170
|
+
`,
|
|
41171
|
+
(e) => logger42.error("Bridge already-running message write failed", { error: e })
|
|
41172
|
+
);
|
|
40397
41173
|
}
|
|
40398
41174
|
function handleBridgeStartError(e, message) {
|
|
40399
41175
|
if (isBridgeAlreadyRunningError(e)) {
|