@fangyb/ahchat-bridge 0.1.34 → 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 +902 -184
- package/dist/feedbackWorkerCli.cjs +99 -9
- package/dist/index.js +894 -184
- package/dist/seedanceMcpCli.cjs +1785 -134
- package/dist/seedreamMcpCli.cjs +1801 -144
- 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
|
}
|
|
@@ -5960,6 +6034,24 @@ function assertNumberPayloadField(type, payload, field) {
|
|
|
5960
6034
|
throw invalidWsMessage(type, field);
|
|
5961
6035
|
}
|
|
5962
6036
|
}
|
|
6037
|
+
function assertOptionalNumberPayloadField(type, payload, field) {
|
|
6038
|
+
if (payload[field] === void 0) return;
|
|
6039
|
+
assertNumberPayloadField(type, payload, field);
|
|
6040
|
+
}
|
|
6041
|
+
function assertNullableStringPayloadField(type, payload, field) {
|
|
6042
|
+
if (payload[field] === null) return;
|
|
6043
|
+
assertStringPayloadField(type, payload, field);
|
|
6044
|
+
}
|
|
6045
|
+
function assertStringPayloadOneOf(type, payload, field, allowed) {
|
|
6046
|
+
assertStringPayloadField(type, payload, field);
|
|
6047
|
+
if (!allowed.includes(payload[field])) {
|
|
6048
|
+
throw invalidWsMessage(type, field);
|
|
6049
|
+
}
|
|
6050
|
+
}
|
|
6051
|
+
function assertOptionalStringPayloadOneOf(type, payload, field, allowed) {
|
|
6052
|
+
if (payload[field] === void 0) return;
|
|
6053
|
+
assertStringPayloadOneOf(type, payload, field, allowed);
|
|
6054
|
+
}
|
|
5963
6055
|
function assertArrayPayloadField(type, payload, field) {
|
|
5964
6056
|
if (!Array.isArray(payload[field])) {
|
|
5965
6057
|
throw invalidWsMessage(type, field);
|
|
@@ -6040,6 +6132,25 @@ function validateWSMessageShape(msg) {
|
|
|
6040
6132
|
case "agent:error": {
|
|
6041
6133
|
assertPayloadRecord(type, payload);
|
|
6042
6134
|
validateRequiredStrings(type, payload, ["ackId", "agentId", "conversationId", "error", "traceId"]);
|
|
6135
|
+
assertOptionalStringPayloadOneOf(type, payload, "reason", ["user_quota_exceeded", "company_quota_exceeded"]);
|
|
6136
|
+
return;
|
|
6137
|
+
}
|
|
6138
|
+
case "directory:register": {
|
|
6139
|
+
assertPayloadRecord(type, payload);
|
|
6140
|
+
validateRequiredStrings(type, payload, ["handle", "viaChild", "traceId"]);
|
|
6141
|
+
assertStringPayloadOneOf(type, payload, "op", ["add", "remove", "move"]);
|
|
6142
|
+
return;
|
|
6143
|
+
}
|
|
6144
|
+
case "directory:resolve": {
|
|
6145
|
+
assertPayloadRecord(type, payload);
|
|
6146
|
+
validateRequiredStrings(type, payload, ["handle", "fromNode", "traceId"]);
|
|
6147
|
+
assertOptionalNumberPayloadField(type, payload, "hop");
|
|
6148
|
+
return;
|
|
6149
|
+
}
|
|
6150
|
+
case "directory:resolve_result": {
|
|
6151
|
+
assertPayloadRecord(type, payload);
|
|
6152
|
+
validateRequiredStrings(type, payload, ["handle", "traceId"]);
|
|
6153
|
+
assertNullableStringPayloadField(type, payload, "canonicalAddress");
|
|
6043
6154
|
return;
|
|
6044
6155
|
}
|
|
6045
6156
|
default:
|
|
@@ -8017,8 +8128,8 @@ var VOLCENGINE_SEEDANCE_MCP_PROVIDER = {
|
|
|
8017
8128
|
};
|
|
8018
8129
|
var OFFICIAL_MCP_PROVIDERS = [
|
|
8019
8130
|
...ALIYUN_IQS_MCP_PROVIDERS,
|
|
8020
|
-
|
|
8021
|
-
|
|
8131
|
+
VOLCENGINE_SEEDREAM_MCP_PROVIDER,
|
|
8132
|
+
VOLCENGINE_SEEDANCE_MCP_PROVIDER
|
|
8022
8133
|
];
|
|
8023
8134
|
var MCP_STORE_PROVIDERS = [
|
|
8024
8135
|
{
|
|
@@ -8860,8 +8971,7 @@ var AskQuestionRegistry = class {
|
|
|
8860
8971
|
questionId,
|
|
8861
8972
|
agentId: entry.agentId,
|
|
8862
8973
|
waitedMs: Date.now() - entry.askedAt,
|
|
8863
|
-
answerLen: answerText2.length
|
|
8864
|
-
answerSample: answerText2.slice(0, 200)
|
|
8974
|
+
answerLen: answerText2.length
|
|
8865
8975
|
});
|
|
8866
8976
|
entry.resolve(answerText2);
|
|
8867
8977
|
return true;
|
|
@@ -9002,7 +9112,7 @@ function makeAskUserQuestionGuard(deps) {
|
|
|
9002
9112
|
bundleIndex,
|
|
9003
9113
|
bundleSize,
|
|
9004
9114
|
replyMessageId: task.replyMessageId,
|
|
9005
|
-
|
|
9115
|
+
questionLen: q.question.length,
|
|
9006
9116
|
optionCount: options.length,
|
|
9007
9117
|
multiSelect,
|
|
9008
9118
|
traceId: task.traceId
|
|
@@ -9101,7 +9211,7 @@ function makeAskUserQuestionGuard(deps) {
|
|
|
9101
9211
|
bundleId,
|
|
9102
9212
|
bundleSize,
|
|
9103
9213
|
replyMessageId: task.replyMessageId,
|
|
9104
|
-
|
|
9214
|
+
combinedLen: combined.length,
|
|
9105
9215
|
traceId: task.traceId
|
|
9106
9216
|
});
|
|
9107
9217
|
return { behavior: "deny", message: combined };
|
|
@@ -24124,6 +24234,17 @@ var TEXT_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
|
24124
24234
|
".yaml",
|
|
24125
24235
|
".yml"
|
|
24126
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;
|
|
24127
24248
|
var DEFAULT_MAX_CHARS = 5e5;
|
|
24128
24249
|
var DEFAULT_TIMEOUT_MS = 45e3;
|
|
24129
24250
|
function isReadableDocumentPath(filePath) {
|
|
@@ -24143,9 +24264,6 @@ function resolveDocumentPath(inputPath, cwd) {
|
|
|
24143
24264
|
async function readDocumentAsMarkdown(inputPath, opts = {}) {
|
|
24144
24265
|
const resolvedPath = opts.cwd ? resolveDocumentPath(inputPath, opts.cwd) : path9.resolve(resolveUserPath(inputPath));
|
|
24145
24266
|
const ext = path9.extname(resolvedPath).toLowerCase();
|
|
24146
|
-
if (!isReadableDocumentPath(resolvedPath)) {
|
|
24147
|
-
throw new Error(`unsupported document type: ${ext || "(no extension)"}`);
|
|
24148
|
-
}
|
|
24149
24267
|
const stat3 = await fs4.stat(resolvedPath);
|
|
24150
24268
|
if (!stat3.isFile()) throw new Error("path is not a file");
|
|
24151
24269
|
const warnings = [];
|
|
@@ -24154,8 +24272,10 @@ async function readDocumentAsMarkdown(inputPath, opts = {}) {
|
|
|
24154
24272
|
markdown = await fs4.readFile(resolvedPath, "utf-8");
|
|
24155
24273
|
} else if (ext === ".xls") {
|
|
24156
24274
|
markdown = await convertLegacyExcelDocument(resolvedPath);
|
|
24157
|
-
} else {
|
|
24275
|
+
} else if (OFFICE_DOCUMENT_EXTENSIONS.has(ext)) {
|
|
24158
24276
|
markdown = await convertOfficeDocument(resolvedPath, opts.timeoutMs ?? DEFAULT_TIMEOUT_MS);
|
|
24277
|
+
} else {
|
|
24278
|
+
markdown = await readPlainTextDocument(resolvedPath, ext);
|
|
24159
24279
|
}
|
|
24160
24280
|
markdown = normalizeDocumentText(markdown);
|
|
24161
24281
|
const maxChars = opts.maxChars ?? DEFAULT_MAX_CHARS;
|
|
@@ -24294,6 +24414,14 @@ async function convertDocxWithOfficeCli(filePath, timeoutMs) {
|
|
|
24294
24414
|
});
|
|
24295
24415
|
return text;
|
|
24296
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
|
+
}
|
|
24297
24425
|
async function convertLegacyExcelDocument(filePath) {
|
|
24298
24426
|
const XLSX = await import("./xlsx-E4ZR5JHK.js");
|
|
24299
24427
|
const workbook = XLSX.readFile(filePath, { cellDates: true });
|
|
@@ -24763,8 +24891,7 @@ async function createNeuralMcpServer(deps) {
|
|
|
24763
24891
|
agentId: deps.agentId,
|
|
24764
24892
|
fromScope: currentScopeKey,
|
|
24765
24893
|
rawTargetScope: args.target_scope,
|
|
24766
|
-
messageLen: args.message.length
|
|
24767
|
-
messageSample: args.message.slice(0, 120)
|
|
24894
|
+
messageLen: args.message.length
|
|
24768
24895
|
});
|
|
24769
24896
|
const trimmed = args.message.trim();
|
|
24770
24897
|
if (!trimmed) {
|
|
@@ -24858,7 +24985,7 @@ async function createNeuralMcpServer(deps) {
|
|
|
24858
24985
|
toScope: resolvedKey,
|
|
24859
24986
|
repeatsInWindow: sendHistory.length,
|
|
24860
24987
|
windowMs: NEURAL_DEDUP_WINDOW_MS,
|
|
24861
|
-
|
|
24988
|
+
messageLen: trimmed.length
|
|
24862
24989
|
});
|
|
24863
24990
|
return {
|
|
24864
24991
|
content: [{
|
|
@@ -24885,8 +25012,7 @@ async function createNeuralMcpServer(deps) {
|
|
|
24885
25012
|
agentId: deps.agentId,
|
|
24886
25013
|
fromScope: currentScopeKey,
|
|
24887
25014
|
toScope: resolvedKey,
|
|
24888
|
-
messageLen: trimmed.length
|
|
24889
|
-
messageSample: trimmed.slice(0, 120)
|
|
25015
|
+
messageLen: trimmed.length
|
|
24890
25016
|
});
|
|
24891
25017
|
return {
|
|
24892
25018
|
content: [{ type: "text", text: `[neural_send] \u5DF2\u9001\u8FBE\u5230\u300C${toLabel}\u300D(scope: ${resolvedKey})\u3002` }]
|
|
@@ -25243,6 +25369,7 @@ action="append" \u8FFD\u52A0\u65B0\u5185\u5BB9\uFF08\u6700\u5E38\u7528\uFF0Ccont
|
|
|
25243
25369
|
`Read a document from the current working directory and return extracted Markdown text.
|
|
25244
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.
|
|
25245
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.
|
|
25246
25373
|
Pass either a relative path from the current working directory or an absolute path inside it.`,
|
|
25247
25374
|
{
|
|
25248
25375
|
path: external_exports.string().min(1).describe("Document path, relative to the current working directory or absolute inside it."),
|
|
@@ -27934,7 +28061,11 @@ function parseJsonRecord(text) {
|
|
|
27934
28061
|
try {
|
|
27935
28062
|
const parsed = JSON.parse(text);
|
|
27936
28063
|
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
|
|
27937
|
-
} catch {
|
|
28064
|
+
} catch (error51) {
|
|
28065
|
+
logger10.warn("SDK tool result output was not JSON", {
|
|
28066
|
+
error: error51,
|
|
28067
|
+
outputLength: text.length
|
|
28068
|
+
});
|
|
27938
28069
|
return null;
|
|
27939
28070
|
}
|
|
27940
28071
|
}
|
|
@@ -28296,6 +28427,9 @@ function emitUsageReported(proc, emit, base, usage, messageId) {
|
|
|
28296
28427
|
function isGroupTask(proc) {
|
|
28297
28428
|
return proc.currentTask?.groupId != null;
|
|
28298
28429
|
}
|
|
28430
|
+
function shouldStreamInternals(proc) {
|
|
28431
|
+
return !isGroupTask(proc) || proc.spectating === true;
|
|
28432
|
+
}
|
|
28299
28433
|
function extractTodosFromInput(input) {
|
|
28300
28434
|
if (!input || typeof input !== "object") return null;
|
|
28301
28435
|
const raw = input.todos;
|
|
@@ -28427,7 +28561,6 @@ function emitGroupSegment(proc, emit, base, content, contentBlocks, isSilent = f
|
|
|
28427
28561
|
contentLen: content.length,
|
|
28428
28562
|
blockCount: contentBlocks.length,
|
|
28429
28563
|
blockTypes: contentBlocks.map((b) => b.type),
|
|
28430
|
-
contentSample: content.slice(0, 200),
|
|
28431
28564
|
traceId: base.traceId,
|
|
28432
28565
|
isAuditOnly: content.length === 0,
|
|
28433
28566
|
isSilent
|
|
@@ -28483,9 +28616,24 @@ function flushTextSegmentOnBlockStop(proc, emit, base) {
|
|
|
28483
28616
|
}
|
|
28484
28617
|
proc.segmentBuffer = "";
|
|
28485
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
|
+
}
|
|
28486
28632
|
function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onProviderApiError) {
|
|
28487
28633
|
const emit = rawEmit;
|
|
28488
28634
|
proc.lastSdkEventAt = Date.now();
|
|
28635
|
+
proc.lastSdkEventInfo = describeSdkEvent(message);
|
|
28636
|
+
proc.stallWarned = false;
|
|
28489
28637
|
switch (message.type) {
|
|
28490
28638
|
case "system": {
|
|
28491
28639
|
const sysMsg = message;
|
|
@@ -28524,11 +28672,29 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onProv
|
|
|
28524
28672
|
sessionId: proc.ccSessionId
|
|
28525
28673
|
});
|
|
28526
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
|
+
}
|
|
28527
28686
|
logger10.info("SDK system subtype unhandled", {
|
|
28528
28687
|
agentId: proc.agentId,
|
|
28529
28688
|
scope: proc.scope.kind === "single" ? "single" : proc.scope.groupId,
|
|
28530
28689
|
subtype: sysMsg.subtype ?? "(none)",
|
|
28531
|
-
|
|
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)
|
|
28532
28698
|
});
|
|
28533
28699
|
}
|
|
28534
28700
|
break;
|
|
@@ -28554,13 +28720,14 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onProv
|
|
|
28554
28720
|
} else if (block.type === "tool_use") {
|
|
28555
28721
|
proc.currentBlockType = "tool_use";
|
|
28556
28722
|
proc.currentToolName = block.name ?? "unknown";
|
|
28723
|
+
proc.activeToolUseStartedAt = Date.now();
|
|
28557
28724
|
proc.accumulatedToolInput = "";
|
|
28558
28725
|
const toolName = block.name ?? "unknown";
|
|
28559
28726
|
proc.suppressCurrentToolUse = proc.officialMediaGenerationSatisfied === true && isOfficialMediaGenerationToolName(toolName);
|
|
28560
28727
|
const isMcpTool = parseMcpRuntimeToolName(toolName) != null;
|
|
28561
28728
|
proc.currentMcpInvocationId = isMcpTool ? createMcpToolInvocationId() : null;
|
|
28562
28729
|
proc.currentMcpInvocationStartedAt = isMcpTool ? (/* @__PURE__ */ new Date()).toISOString() : null;
|
|
28563
|
-
if (
|
|
28730
|
+
if (shouldStreamInternals(proc) && !proc.suppressCurrentToolUse && toolName !== "ExitPlanMode" && !isAskUserQuestionToolName(toolName)) {
|
|
28564
28731
|
emit({
|
|
28565
28732
|
type: "agent:tool_use",
|
|
28566
28733
|
payload: {
|
|
@@ -28585,7 +28752,7 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onProv
|
|
|
28585
28752
|
if (delta.type === "thinking_delta" && typeof delta.thinking === "string") {
|
|
28586
28753
|
if (proc.suppressCurrentThinking) break;
|
|
28587
28754
|
proc.accumulatedThinking += delta.thinking;
|
|
28588
|
-
if (
|
|
28755
|
+
if (shouldStreamInternals(proc)) {
|
|
28589
28756
|
emit({
|
|
28590
28757
|
type: "agent:thinking_chunk",
|
|
28591
28758
|
payload: { ...wireBase(base), chunk: delta.thinking }
|
|
@@ -28596,7 +28763,7 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onProv
|
|
|
28596
28763
|
if (typeof partial2 === "string") {
|
|
28597
28764
|
proc.accumulatedToolInput += partial2;
|
|
28598
28765
|
const liveInput = extractLiveToolInput(proc.currentToolName, proc.accumulatedToolInput);
|
|
28599
|
-
if (
|
|
28766
|
+
if (shouldStreamInternals(proc) && liveInput && proc.currentToolName != null) {
|
|
28600
28767
|
emit({
|
|
28601
28768
|
type: "agent:tool_input_update",
|
|
28602
28769
|
payload: {
|
|
@@ -28630,7 +28797,7 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onProv
|
|
|
28630
28797
|
}
|
|
28631
28798
|
case "content_block_stop": {
|
|
28632
28799
|
if (proc.currentBlockType === "thinking") {
|
|
28633
|
-
if (
|
|
28800
|
+
if (shouldStreamInternals(proc) && !proc.suppressCurrentThinking) {
|
|
28634
28801
|
emit({
|
|
28635
28802
|
type: "agent:thinking_done",
|
|
28636
28803
|
payload: wireBase(getTaskBase(proc))
|
|
@@ -28650,12 +28817,12 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onProv
|
|
|
28650
28817
|
if (proc.accumulatedToolInput.length > 0) {
|
|
28651
28818
|
try {
|
|
28652
28819
|
parsedInput = JSON.parse(proc.accumulatedToolInput);
|
|
28653
|
-
} catch {
|
|
28820
|
+
} catch (error51) {
|
|
28654
28821
|
logger10.warn("Failed to parse tool input JSON", {
|
|
28822
|
+
error: error51,
|
|
28655
28823
|
agentId: proc.agentId,
|
|
28656
28824
|
toolName: proc.currentToolName,
|
|
28657
|
-
inputLen: proc.accumulatedToolInput.length
|
|
28658
|
-
sample: proc.accumulatedToolInput.slice(0, 200)
|
|
28825
|
+
inputLen: proc.accumulatedToolInput.length
|
|
28659
28826
|
});
|
|
28660
28827
|
}
|
|
28661
28828
|
}
|
|
@@ -28664,7 +28831,7 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onProv
|
|
|
28664
28831
|
if (lastToolUse && lastToolUse.type === "tool_use") {
|
|
28665
28832
|
lastToolUse.input = parsedInput;
|
|
28666
28833
|
}
|
|
28667
|
-
if (
|
|
28834
|
+
if (shouldStreamInternals(proc) && proc.currentToolName != null && LIVE_INPUT_PREVIEW_TOOLS.has(proc.currentToolName) && Object.keys(parsedInput).length > 0) {
|
|
28668
28835
|
emit({
|
|
28669
28836
|
type: "agent:tool_input_update",
|
|
28670
28837
|
payload: {
|
|
@@ -28788,7 +28955,7 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onProv
|
|
|
28788
28955
|
blockTypes,
|
|
28789
28956
|
hasToolResult,
|
|
28790
28957
|
hasPlainText,
|
|
28791
|
-
|
|
28958
|
+
contentLen: typeof content === "string" ? content.length : JSON.stringify(content).length
|
|
28792
28959
|
});
|
|
28793
28960
|
break;
|
|
28794
28961
|
}
|
|
@@ -28797,7 +28964,7 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onProv
|
|
|
28797
28964
|
agentId: proc.agentId,
|
|
28798
28965
|
scope: proc.scope.kind === "single" ? "single" : proc.scope.groupId,
|
|
28799
28966
|
blockTypes,
|
|
28800
|
-
|
|
28967
|
+
contentLen: JSON.stringify(content).length,
|
|
28801
28968
|
replyMessageId: base.replyMessageId
|
|
28802
28969
|
});
|
|
28803
28970
|
}
|
|
@@ -28818,14 +28985,15 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onProv
|
|
|
28818
28985
|
});
|
|
28819
28986
|
proc.currentMcpInvocationId = null;
|
|
28820
28987
|
proc.currentMcpInvocationStartedAt = null;
|
|
28988
|
+
proc.activeToolUseStartedAt = void 0;
|
|
28821
28989
|
proc.currentToolName = null;
|
|
28822
28990
|
continue;
|
|
28823
28991
|
}
|
|
28824
28992
|
if (isSuccessfulOfficialMediaOutput(toolName, output)) {
|
|
28825
28993
|
proc.officialMediaGenerationSatisfied = true;
|
|
28826
|
-
proc.officialMediaSessionRecycleRequested = true;
|
|
28827
28994
|
}
|
|
28828
28995
|
if (isAskUserQuestionToolName(toolName)) {
|
|
28996
|
+
proc.activeToolUseStartedAt = void 0;
|
|
28829
28997
|
proc.currentToolName = null;
|
|
28830
28998
|
continue;
|
|
28831
28999
|
}
|
|
@@ -28853,7 +29021,7 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onProv
|
|
|
28853
29021
|
proc.currentMcpInvocationId = null;
|
|
28854
29022
|
proc.currentMcpInvocationStartedAt = null;
|
|
28855
29023
|
}
|
|
28856
|
-
if (
|
|
29024
|
+
if (shouldStreamInternals(proc)) {
|
|
28857
29025
|
emit({
|
|
28858
29026
|
type: "agent:tool_result",
|
|
28859
29027
|
payload: {
|
|
@@ -28877,6 +29045,7 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onProv
|
|
|
28877
29045
|
}
|
|
28878
29046
|
}
|
|
28879
29047
|
}
|
|
29048
|
+
proc.activeToolUseStartedAt = void 0;
|
|
28880
29049
|
}
|
|
28881
29050
|
}
|
|
28882
29051
|
}
|
|
@@ -28955,7 +29124,6 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onProv
|
|
|
28955
29124
|
groupId,
|
|
28956
29125
|
compactScheduled: proc.compactRequested === true,
|
|
28957
29126
|
fullTextLen: proc.accumulatedText.length,
|
|
28958
|
-
fullTextSample: proc.accumulatedText.slice(0, 200),
|
|
28959
29127
|
accumulatedBlockCount: proc.contentBlocks.length,
|
|
28960
29128
|
accumulatedBlockTypes: proc.contentBlocks.map((b) => b.type),
|
|
28961
29129
|
silentSegmentEmitted: groupMode
|
|
@@ -28999,7 +29167,6 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onProv
|
|
|
28999
29167
|
segmentCount: proc.segmentCount,
|
|
29000
29168
|
compactScheduled: proc.compactRequested === true,
|
|
29001
29169
|
fullTextLen: proc.accumulatedText.length,
|
|
29002
|
-
fullTextSample: proc.accumulatedText.slice(0, 200),
|
|
29003
29170
|
traceId: base.traceId
|
|
29004
29171
|
});
|
|
29005
29172
|
emit({
|
|
@@ -29050,7 +29217,6 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onProv
|
|
|
29050
29217
|
ackId: base.replyMessageId,
|
|
29051
29218
|
messageId: carrierMessageId,
|
|
29052
29219
|
textLen: proc.accumulatedText.length,
|
|
29053
|
-
textSample: proc.accumulatedText.slice(0, 200),
|
|
29054
29220
|
tokenCount: usage.tokenCount,
|
|
29055
29221
|
traceId: base.traceId
|
|
29056
29222
|
});
|
|
@@ -29243,8 +29409,7 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted, onProv
|
|
|
29243
29409
|
logger10.info("Captured non-streamed assistant message", {
|
|
29244
29410
|
agentId: proc.agentId,
|
|
29245
29411
|
scope: proc.scope.kind === "single" ? "single" : proc.scope.groupId,
|
|
29246
|
-
textLen: text.length
|
|
29247
|
-
textSample: text.slice(0, 100)
|
|
29412
|
+
textLen: text.length
|
|
29248
29413
|
});
|
|
29249
29414
|
} else {
|
|
29250
29415
|
proc.lastAssistantContentDescription = describeAssistantContent(am.message?.content);
|
|
@@ -29268,6 +29433,7 @@ function resetAccumulators(proc) {
|
|
|
29268
29433
|
proc.currentToolName = null;
|
|
29269
29434
|
proc.currentMcpInvocationId = null;
|
|
29270
29435
|
proc.currentMcpInvocationStartedAt = null;
|
|
29436
|
+
proc.activeToolUseStartedAt = void 0;
|
|
29271
29437
|
proc.segmentBuffer = "";
|
|
29272
29438
|
proc.segmentCount = 0;
|
|
29273
29439
|
proc.accumulatedToolInput = "";
|
|
@@ -29277,6 +29443,7 @@ function resetAccumulators(proc) {
|
|
|
29277
29443
|
proc.officialMediaGenerationSatisfied = false;
|
|
29278
29444
|
proc.suppressCurrentThinking = false;
|
|
29279
29445
|
proc.suppressCurrentToolUse = false;
|
|
29446
|
+
proc.activeSubagentTaskIds?.clear();
|
|
29280
29447
|
}
|
|
29281
29448
|
|
|
29282
29449
|
// src/forkHistoryReplay.ts
|
|
@@ -29464,7 +29631,7 @@ function missingSubscriptionMessage(subscriptionId) {
|
|
|
29464
29631
|
}
|
|
29465
29632
|
var NODE_USER_UID = 1e3;
|
|
29466
29633
|
var POST_MERGE_CONTINUATION_ROUTE_MS = 15e3;
|
|
29467
|
-
var SCOPE_PROMPT_FINGERPRINT_REVISION = "workdir-scope-prompt-
|
|
29634
|
+
var SCOPE_PROMPT_FINGERPRINT_REVISION = "workdir-scope-mcp-abi-prompt-v4";
|
|
29468
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;
|
|
29469
29636
|
var DOCUMENT_READING_RULES = `DOCUMENT READING:
|
|
29470
29637
|
- The built-in Read tool cannot read binary office documents such as .docx, .xls, .xlsx, .pptx, .pdf, .odt, .ods, .odp, or .rtf.
|
|
@@ -29479,6 +29646,16 @@ var MEDIA_GENERATION_RULES = `MEDIA GENERATION:
|
|
|
29479
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.
|
|
29480
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.
|
|
29481
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
|
+
}
|
|
29482
29659
|
function isRecoveryDispatchTask(task) {
|
|
29483
29660
|
return task.dispatchKind === "manual_continue" || task.dispatchKind === "regenerate";
|
|
29484
29661
|
}
|
|
@@ -29736,13 +29913,15 @@ var AgentManager = class {
|
|
|
29736
29913
|
agents = /* @__PURE__ */ new Map();
|
|
29737
29914
|
lastUsedAt = /* @__PURE__ */ new Map();
|
|
29738
29915
|
/** Scopes 被 zombie_watchdog 关闭后的"入睡"标记,acquire 重建时清除并 emit awake。 */
|
|
29739
|
-
dormantScopes = /* @__PURE__ */ new
|
|
29916
|
+
dormantScopes = /* @__PURE__ */ new Map();
|
|
29740
29917
|
/**
|
|
29741
29918
|
* zombie_watchdog 拆 runtime 时,把该 (agentId, scope) 的 groupInbox 快照到这里,
|
|
29742
29919
|
* 让下一次 getOrCreate 重建 runtime 时可以恢复未读消息。仅 in-memory;
|
|
29743
29920
|
* bridge 进程崩溃 / shutdownAll 时丢失,与现有 inbox 内存语义一致。
|
|
29744
29921
|
*/
|
|
29745
29922
|
dormantGroupInboxes = /* @__PURE__ */ new Map();
|
|
29923
|
+
/** Spectate requested before runtime existed; value = activatedAt epoch ms. */
|
|
29924
|
+
pendingSpectate = /* @__PURE__ */ new Map();
|
|
29746
29925
|
sessionStore;
|
|
29747
29926
|
dispatchMemory = new GroupDispatchMemoryStore();
|
|
29748
29927
|
dataDir;
|
|
@@ -29908,6 +30087,7 @@ var AgentManager = class {
|
|
|
29908
30087
|
}
|
|
29909
30088
|
async resolveRuntimeCwd(agentConfig, scope, requestedCwd) {
|
|
29910
30089
|
let cwd = this.remapServerWorkspaceCwd(agentConfig, scope, requestedCwd);
|
|
30090
|
+
let fallbackForensicsId;
|
|
29911
30091
|
if (!isFullyQualifiedAbsolutePath(cwd)) {
|
|
29912
30092
|
const fallback = path13.join(this.workspacesDir, this.localScopeDirName(agentConfig, scope));
|
|
29913
30093
|
logger13.error(
|
|
@@ -29922,6 +30102,23 @@ var AgentManager = class {
|
|
|
29922
30102
|
error: new Error("workdir_not_usable_on_this_machine")
|
|
29923
30103
|
}
|
|
29924
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
|
+
});
|
|
29925
30122
|
cwd = fallback;
|
|
29926
30123
|
}
|
|
29927
30124
|
if (isRunningAsRoot() && cwd.startsWith("/root/")) {
|
|
@@ -29940,6 +30137,21 @@ var AgentManager = class {
|
|
|
29940
30137
|
fallback,
|
|
29941
30138
|
error: e
|
|
29942
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
|
+
});
|
|
29943
30155
|
await fs6.mkdir(fallback, { recursive: true });
|
|
29944
30156
|
return fallback;
|
|
29945
30157
|
}
|
|
@@ -30162,17 +30374,24 @@ var AgentManager = class {
|
|
|
30162
30374
|
});
|
|
30163
30375
|
return null;
|
|
30164
30376
|
}
|
|
30165
|
-
scopePromptFingerprint(agentConfig, scope, agentCwd, scopesSection) {
|
|
30166
|
-
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");
|
|
30379
|
+
}
|
|
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 });
|
|
30167
30386
|
}
|
|
30168
|
-
discardSessionIfScopePromptChanged(agentConfig, scope, sessionId, fingerprint) {
|
|
30387
|
+
discardSessionIfScopePromptChanged(agentConfig, scope, sessionId, fingerprint, options = {}) {
|
|
30169
30388
|
const previous = this.sessionStore.getPromptFingerprint(agentConfig.id, scope);
|
|
30170
30389
|
if (!sessionId) {
|
|
30171
30390
|
this.sessionStore.setPromptFingerprint(agentConfig.id, scope, fingerprint);
|
|
30172
30391
|
return null;
|
|
30173
30392
|
}
|
|
30174
30393
|
if (previous === fingerprint) return sessionId;
|
|
30175
|
-
if (!previous && scope.kind === "single") {
|
|
30394
|
+
if (!previous && scope.kind === "single" && options.clearLegacySingleSession !== true) {
|
|
30176
30395
|
this.sessionStore.setPromptFingerprint(agentConfig.id, scope, fingerprint);
|
|
30177
30396
|
logger13.info("Retaining legacy single-scope session while recording prompt fingerprint", {
|
|
30178
30397
|
agentId: agentConfig.id,
|
|
@@ -30192,7 +30411,8 @@ var AgentManager = class {
|
|
|
30192
30411
|
sessionId,
|
|
30193
30412
|
previousFingerprint: previous,
|
|
30194
30413
|
nextFingerprint: fingerprint,
|
|
30195
|
-
revision: SCOPE_PROMPT_FINGERPRINT_REVISION
|
|
30414
|
+
revision: SCOPE_PROMPT_FINGERPRINT_REVISION,
|
|
30415
|
+
reason: options.reason ?? "scope_prompt_changed"
|
|
30196
30416
|
});
|
|
30197
30417
|
return null;
|
|
30198
30418
|
}
|
|
@@ -30241,6 +30461,7 @@ var AgentManager = class {
|
|
|
30241
30461
|
logger13.info("Evicting idle Agent query", { agentId: proc.agentId, scope: scopeKey(proc.scope) });
|
|
30242
30462
|
const runtime = this.asRuntime(proc);
|
|
30243
30463
|
this.clearQuietFlushTimer(runtime);
|
|
30464
|
+
this.teardownSpectate(runtime);
|
|
30244
30465
|
try {
|
|
30245
30466
|
runtime.inputController.close();
|
|
30246
30467
|
await this.awaitQueryReturn(runtime.query, 5e3, proc.agentId);
|
|
@@ -30260,6 +30481,7 @@ var AgentManager = class {
|
|
|
30260
30481
|
const runtime = this.asRuntime(proc);
|
|
30261
30482
|
const key = runtimeKey(proc.agentId, proc.scope);
|
|
30262
30483
|
this.clearQuietFlushTimer(runtime);
|
|
30484
|
+
this.teardownSpectate(runtime);
|
|
30263
30485
|
runtime.currentTask = null;
|
|
30264
30486
|
runtime.injectedTasks = [];
|
|
30265
30487
|
runtime.mergedTasks = [];
|
|
@@ -30296,6 +30518,7 @@ var AgentManager = class {
|
|
|
30296
30518
|
evictIdle() {
|
|
30297
30519
|
const now = Date.now();
|
|
30298
30520
|
const { idleTimeoutMs, workingSilenceTimeoutMs } = this.queryConfig;
|
|
30521
|
+
const stallWarnAfterMs = Math.min(9e4, this.queryConfig.replyStallTimeoutMs);
|
|
30299
30522
|
for (const [key, proc] of this.agents) {
|
|
30300
30523
|
if (!this.isEvictable(proc)) continue;
|
|
30301
30524
|
const runtime = this.asRuntime(proc);
|
|
@@ -30306,26 +30529,62 @@ var AgentManager = class {
|
|
|
30306
30529
|
for (const [, proc] of this.agents) {
|
|
30307
30530
|
if (proc.status !== "working") continue;
|
|
30308
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
|
+
}
|
|
30309
30563
|
const hasInjectedBacklog = runtime.injectedTasks.length > 0;
|
|
30310
|
-
const
|
|
30564
|
+
const baseCeilingMs = busy ? Math.max(workingSilenceTimeoutMs, this.queryConfig.busySilenceTimeoutMs ?? 0) : workingSilenceTimeoutMs;
|
|
30565
|
+
const effectiveTimeoutMs = hasInjectedBacklog ? baseCeilingMs * 2 : baseCeilingMs;
|
|
30311
30566
|
const silentMs = now - proc.lastSdkEventAt;
|
|
30312
30567
|
if (silentMs <= effectiveTimeoutMs) {
|
|
30313
|
-
if (
|
|
30314
|
-
logger13.warn(
|
|
30315
|
-
|
|
30316
|
-
|
|
30317
|
-
|
|
30318
|
-
|
|
30319
|
-
|
|
30320
|
-
|
|
30321
|
-
|
|
30322
|
-
|
|
30323
|
-
|
|
30324
|
-
|
|
30325
|
-
|
|
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
|
+
});
|
|
30326
30582
|
}
|
|
30327
30583
|
continue;
|
|
30328
30584
|
}
|
|
30585
|
+
const zombieOpenTool = this.latestOpenToolUse(proc);
|
|
30586
|
+
const watchdogDetectedAt = Date.now();
|
|
30587
|
+
const watchdogFallbackId = createFallbackId();
|
|
30329
30588
|
logger13.warn("Zombie watchdog: working runtime silent too long, tearing down", {
|
|
30330
30589
|
agentId: proc.agentId,
|
|
30331
30590
|
scope: scopeKey(proc.scope),
|
|
@@ -30333,12 +30592,46 @@ var AgentManager = class {
|
|
|
30333
30592
|
lastSdkEventAt: new Date(proc.lastSdkEventAt).toISOString(),
|
|
30334
30593
|
workingSilenceTimeoutMs,
|
|
30335
30594
|
effectiveTimeoutMs,
|
|
30595
|
+
baseCeilingMs,
|
|
30596
|
+
busy,
|
|
30597
|
+
busySilenceTimeoutMs: this.queryConfig.busySilenceTimeoutMs ?? null,
|
|
30336
30598
|
replyMessageId: proc.currentTask?.replyMessageId,
|
|
30337
30599
|
injectedTaskCount: runtime.injectedTasks.length,
|
|
30338
30600
|
hadInjectedBacklog: hasInjectedBacklog,
|
|
30339
|
-
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
|
|
30340
30634
|
});
|
|
30341
|
-
void this.closeRuntime(proc, "zombie_watchdog");
|
|
30342
30635
|
}
|
|
30343
30636
|
}
|
|
30344
30637
|
/**
|
|
@@ -30585,7 +30878,7 @@ ${cfg.instructions.trim()}` : "";
|
|
|
30585
30878
|
agentId: agentConfig.id,
|
|
30586
30879
|
capabilityTier: cfg.capabilityTier,
|
|
30587
30880
|
isSmith: smithAgent
|
|
30588
|
-
}) ?? { mcpServers: {}, allowedTools: [] };
|
|
30881
|
+
}) ?? { mcpServers: {}, allowedTools: [], toolAbi: [] };
|
|
30589
30882
|
logger13.info("External MCP resolved for runtime", {
|
|
30590
30883
|
agentId: agentConfig.id,
|
|
30591
30884
|
scope: scopeKey(scope),
|
|
@@ -30602,11 +30895,16 @@ ${cfg.instructions.trim()}` : "";
|
|
|
30602
30895
|
}
|
|
30603
30896
|
const notebookSection = this.buildNotebookSection(agentConfig.id);
|
|
30604
30897
|
const scopesSection = this.buildScopesSection(agentConfig, scope, agentCwd);
|
|
30898
|
+
const externalMcpFingerprint = this.externalMcpFingerprint(externalMcp);
|
|
30605
30899
|
savedSessionId = this.discardSessionIfScopePromptChanged(
|
|
30606
30900
|
agentConfig,
|
|
30607
30901
|
scope,
|
|
30608
30902
|
savedSessionId,
|
|
30609
|
-
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
|
+
}
|
|
30610
30908
|
);
|
|
30611
30909
|
let forkHistorySection = "";
|
|
30612
30910
|
if (!savedSessionId && scope.kind === "single") {
|
|
@@ -30627,6 +30925,8 @@ ${cfg.instructions.trim()}` : "";
|
|
|
30627
30925
|
}
|
|
30628
30926
|
}
|
|
30629
30927
|
const cronLockSnapshot = readCronLockSnapshot();
|
|
30928
|
+
const builtinWebSearchAllowed = this.queryConfig.allowBuiltinWebSearch;
|
|
30929
|
+
const disallowedToolsForRuntime = builtinWebSearchAllowed ? [] : ["WebSearch"];
|
|
30630
30930
|
logger13.info("Creating Agent query", {
|
|
30631
30931
|
agentId: agentConfig.id,
|
|
30632
30932
|
scope: scopeKey(scope),
|
|
@@ -30635,6 +30935,8 @@ ${cfg.instructions.trim()}` : "";
|
|
|
30635
30935
|
sessionId: savedSessionId,
|
|
30636
30936
|
forkHistoryReplay: forkHistorySection.length > 0,
|
|
30637
30937
|
model: cfg.model ?? "(default)",
|
|
30938
|
+
builtinWebSearchAllowed,
|
|
30939
|
+
disallowedTools: disallowedToolsForRuntime,
|
|
30638
30940
|
// Diagnostic: who currently owns Claude's global cron lock (~/.claude/scheduled_tasks.lock).
|
|
30639
30941
|
// Cron is process-internal but the binary uses this singleton lock to elect ONE scheduler
|
|
30640
30942
|
// among concurrent claude subprocesses. If lock is held by another session at spawn time,
|
|
@@ -30646,7 +30948,6 @@ ${cfg.instructions.trim()}` : "";
|
|
|
30646
30948
|
});
|
|
30647
30949
|
const planModeRef = { active: false, denyCount: 0 };
|
|
30648
30950
|
const mediaGenerationTurnGuard = createOfficialMediaGenerationTurnGuard();
|
|
30649
|
-
const builtinWebSearchAllowed = this.queryConfig.allowBuiltinWebSearch;
|
|
30650
30951
|
const options = {
|
|
30651
30952
|
cwd: agentCwd,
|
|
30652
30953
|
systemPrompt: {
|
|
@@ -30713,6 +31014,8 @@ ${cfg.instructions.trim()}` : "";
|
|
|
30713
31014
|
] : [],
|
|
30714
31015
|
...externalMcp.allowedTools
|
|
30715
31016
|
],
|
|
31017
|
+
// Server-side WebSearch bypasses canUseTool; disallowedTools removes it from model context.
|
|
31018
|
+
disallowedTools: disallowedToolsForRuntime,
|
|
30716
31019
|
mcpServers: { ...externalMcp.mcpServers, neural: neuralServer },
|
|
30717
31020
|
includePartialMessages: true,
|
|
30718
31021
|
// Plan mode custom workflow instructions. When setPermissionMode('plan') is
|
|
@@ -31039,6 +31342,7 @@ Do NOT use "..." as content \u2014 write specific, project-relevant content.`;
|
|
|
31039
31342
|
currentTask: null,
|
|
31040
31343
|
currentTaskStartedAt: 0,
|
|
31041
31344
|
lastSdkEventAt: Date.now(),
|
|
31345
|
+
model: (typeof options.model === "string" ? options.model : cfg.model) ?? null,
|
|
31042
31346
|
compactRequested: false,
|
|
31043
31347
|
compactInProgress: false,
|
|
31044
31348
|
contextOverflowLockedUntil: 0,
|
|
@@ -31050,13 +31354,18 @@ Do NOT use "..." as content \u2014 write specific, project-relevant content.`;
|
|
|
31050
31354
|
currentToolName: null,
|
|
31051
31355
|
currentMcpInvocationId: null,
|
|
31052
31356
|
currentMcpInvocationStartedAt: null,
|
|
31357
|
+
activeSubagentTaskIds: /* @__PURE__ */ new Set(),
|
|
31053
31358
|
mcpAuditRecorder: this.mcpAuditRecorder,
|
|
31054
31359
|
segmentBuffer: "",
|
|
31055
31360
|
segmentCount: 0,
|
|
31056
31361
|
accumulatedToolInput: "",
|
|
31057
31362
|
planModeRef,
|
|
31058
31363
|
mediaGenerationTurnGuard,
|
|
31059
|
-
groupInbox: []
|
|
31364
|
+
groupInbox: [],
|
|
31365
|
+
spectating: false,
|
|
31366
|
+
spectateActivatedAt: 0,
|
|
31367
|
+
spectateViewing: false,
|
|
31368
|
+
spectateTtlExpired: false
|
|
31060
31369
|
};
|
|
31061
31370
|
const runtime = Object.assign(proc, {
|
|
31062
31371
|
query: agentQuery,
|
|
@@ -31068,7 +31377,8 @@ Do NOT use "..." as content \u2014 write specific, project-relevant content.`;
|
|
|
31068
31377
|
createdAt: Date.now(),
|
|
31069
31378
|
supportsVision: modelInputMode === "vision" && cfg.supportsVision !== false,
|
|
31070
31379
|
modelInputMode,
|
|
31071
|
-
quietFlushTimer: null
|
|
31380
|
+
quietFlushTimer: null,
|
|
31381
|
+
spectateRevertTimer: null
|
|
31072
31382
|
});
|
|
31073
31383
|
logger13.info("Agent model input mode resolved", {
|
|
31074
31384
|
agentId: agentConfig.id,
|
|
@@ -31093,6 +31403,7 @@ Do NOT use "..." as content \u2014 write specific, project-relevant content.`;
|
|
|
31093
31403
|
} else {
|
|
31094
31404
|
this.dormantGroupInboxes.delete(key);
|
|
31095
31405
|
}
|
|
31406
|
+
const dormantMeta = this.dormantScopes.get(key);
|
|
31096
31407
|
if (this.dormantScopes.delete(key)) {
|
|
31097
31408
|
this.emit({
|
|
31098
31409
|
type: "agent:awake",
|
|
@@ -31104,7 +31415,44 @@ Do NOT use "..." as content \u2014 write specific, project-relevant content.`;
|
|
|
31104
31415
|
});
|
|
31105
31416
|
logger13.info("Agent scope awakened after dormant", {
|
|
31106
31417
|
agentId: agentConfig.id,
|
|
31107
|
-
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
|
|
31108
31456
|
});
|
|
31109
31457
|
}
|
|
31110
31458
|
if (proc.groupInbox.length > 0 && this.isRuntimeIdleForInboxFlush(runtime)) {
|
|
@@ -32242,7 +32590,6 @@ ${lines.join("\n")}`;
|
|
|
32242
32590
|
compactTrigger: "context_watermark",
|
|
32243
32591
|
injectedTasksWaiting: runtime.injectedTasks.length,
|
|
32244
32592
|
compactPromptLen: compactPrompt.length,
|
|
32245
|
-
promptSample: compactPrompt.slice(0, 80),
|
|
32246
32593
|
traceId: compactTraceId
|
|
32247
32594
|
});
|
|
32248
32595
|
runtime.inputController.push(compactPrompt, runtime.ccSessionId ?? "");
|
|
@@ -32524,7 +32871,7 @@ ${lines.join("\n")}`;
|
|
|
32524
32871
|
const enveloped = buildInnerVoiceEnvelope(payloadWithTrigger, ctx);
|
|
32525
32872
|
const task = {
|
|
32526
32873
|
content: enveloped,
|
|
32527
|
-
replyMessageId:
|
|
32874
|
+
replyMessageId: createNeuralSendReplyMessageId(),
|
|
32528
32875
|
conversationId: payload.conversationId,
|
|
32529
32876
|
traceId: createTraceId(),
|
|
32530
32877
|
groupId: payload.groupId
|
|
@@ -32770,7 +33117,7 @@ ${lines.join("\n")}`;
|
|
|
32770
33117
|
this.dormantScopes.delete(key);
|
|
32771
33118
|
this.dormantGroupInboxes.delete(key);
|
|
32772
33119
|
}
|
|
32773
|
-
for (const key of [...this.dormantScopes].filter(
|
|
33120
|
+
for (const key of [...this.dormantScopes.keys()].filter(
|
|
32774
33121
|
(k) => k === agentId || k.startsWith(`${agentId}::`)
|
|
32775
33122
|
)) {
|
|
32776
33123
|
this.dormantScopes.delete(key);
|
|
@@ -32818,7 +33165,7 @@ ${lines.join("\n")}`;
|
|
|
32818
33165
|
async reloadAgentScopes(agentId, reason) {
|
|
32819
33166
|
this.sessionStore.deleteAllForAgent(agentId);
|
|
32820
33167
|
this.dispatchMemory.deleteAllForAgent(agentId);
|
|
32821
|
-
for (const key of [...this.dormantScopes].filter(
|
|
33168
|
+
for (const key of [...this.dormantScopes.keys()].filter(
|
|
32822
33169
|
(k) => k === agentId || k.startsWith(`${agentId}::`)
|
|
32823
33170
|
)) {
|
|
32824
33171
|
this.dormantScopes.delete(key);
|
|
@@ -32970,6 +33317,125 @@ ${lines.join("\n")}`;
|
|
|
32970
33317
|
void this.terminateScope(proc.agentId, proc.scope);
|
|
32971
33318
|
}
|
|
32972
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
|
+
}
|
|
32973
33439
|
/** Stop one scoped SDK runtime (workdir change). */
|
|
32974
33440
|
async terminateScope(agentId, scope) {
|
|
32975
33441
|
const key = runtimeKey(agentId, scope);
|
|
@@ -32978,6 +33444,7 @@ ${lines.join("\n")}`;
|
|
|
32978
33444
|
logger13.info("terminateScope: no active runtime", { agentId, scope: scopeKey(scope) });
|
|
32979
33445
|
this.dormantScopes.delete(key);
|
|
32980
33446
|
this.dormantGroupInboxes.delete(key);
|
|
33447
|
+
this.pendingSpectate.delete(key);
|
|
32981
33448
|
this.sessionStore.delete(agentId, scope);
|
|
32982
33449
|
this.dispatchMemory.deleteScope(agentId, scope);
|
|
32983
33450
|
return;
|
|
@@ -33000,7 +33467,7 @@ ${lines.join("\n")}`;
|
|
|
33000
33467
|
this.dispatchMemory.deleteScope(agentId, scope);
|
|
33001
33468
|
logger13.info("terminateScope: scoped query removed", { agentId, scope: scopeKey(scope) });
|
|
33002
33469
|
}
|
|
33003
|
-
async closeRuntime(proc, reason) {
|
|
33470
|
+
async closeRuntime(proc, reason, watchdogForensics) {
|
|
33004
33471
|
const key = runtimeKey(proc.agentId, proc.scope);
|
|
33005
33472
|
if (proc.status === "dead") return;
|
|
33006
33473
|
const runtime = this.asRuntime(proc);
|
|
@@ -33077,12 +33544,13 @@ ${lines.join("\n")}`;
|
|
|
33077
33544
|
runtime.currentTask = null;
|
|
33078
33545
|
if (isWatchdog) {
|
|
33079
33546
|
const preservedInbox = proc.groupInbox;
|
|
33080
|
-
|
|
33547
|
+
const preservedInboxSize = preservedInbox.length;
|
|
33548
|
+
if (preservedInboxSize > 0) {
|
|
33081
33549
|
this.dormantGroupInboxes.set(key, [...preservedInbox]);
|
|
33082
33550
|
logger13.info("Preserving groupInbox for dormant agent", {
|
|
33083
33551
|
agentId,
|
|
33084
33552
|
scope: scopeKey(proc.scope),
|
|
33085
|
-
preservedInboxSize
|
|
33553
|
+
preservedInboxSize,
|
|
33086
33554
|
preservedEntries: preservedInbox.map((e) => ({
|
|
33087
33555
|
ackId: e.ackId,
|
|
33088
33556
|
sender: e.senderName,
|
|
@@ -33091,7 +33559,26 @@ ${lines.join("\n")}`;
|
|
|
33091
33559
|
}))
|
|
33092
33560
|
});
|
|
33093
33561
|
}
|
|
33094
|
-
|
|
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
|
+
});
|
|
33095
33582
|
this.emit({
|
|
33096
33583
|
type: "agent:dormant",
|
|
33097
33584
|
payload: {
|
|
@@ -33106,13 +33593,15 @@ ${lines.join("\n")}`;
|
|
|
33106
33593
|
agentId,
|
|
33107
33594
|
scope: scopeKey(proc.scope),
|
|
33108
33595
|
droppedTaskCount: droppedAckIds.length,
|
|
33109
|
-
preservedInboxSize: this.dormantGroupInboxes.get(key)?.length ?? 0
|
|
33596
|
+
preservedInboxSize: this.dormantGroupInboxes.get(key)?.length ?? 0,
|
|
33597
|
+
fallbackId: effectiveFallbackId
|
|
33110
33598
|
});
|
|
33111
33599
|
}
|
|
33112
33600
|
proc.status = "dead";
|
|
33113
33601
|
this.agents.delete(key);
|
|
33114
33602
|
this.lastUsedAt.delete(key);
|
|
33115
33603
|
this.clearQuietFlushTimer(runtime);
|
|
33604
|
+
this.teardownSpectate(runtime);
|
|
33116
33605
|
try {
|
|
33117
33606
|
runtime.inputController.close();
|
|
33118
33607
|
await this.awaitQueryReturn(runtime.query, 5e3, agentId);
|
|
@@ -33129,6 +33618,165 @@ ${lines.join("\n")}`;
|
|
|
33129
33618
|
cwd: proc.cwd
|
|
33130
33619
|
});
|
|
33131
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
|
+
}
|
|
33132
33780
|
async recoverFromRestart(agents) {
|
|
33133
33781
|
const lockSnapshot = readCronLockSnapshot();
|
|
33134
33782
|
logger13.info("Recovering Agent sessions after restart", {
|
|
@@ -33251,58 +33899,7 @@ ${lines.join("\n")}`;
|
|
|
33251
33899
|
this.lastUsedAt.delete(key);
|
|
33252
33900
|
const errorText = isResumeFail ? `\u4F1A\u8BDD\u5DF2\u8FC7\u671F\uFF0C\u8BF7\u91CD\u65B0\u53D1\u9001\u6D88\u606F\uFF08${errMsg}\uFF09` : `Agent query crashed: ${errMsg}`;
|
|
33253
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;
|
|
33254
|
-
|
|
33255
|
-
this.emit({
|
|
33256
|
-
type: "agent:error",
|
|
33257
|
-
payload: {
|
|
33258
|
-
agentId: runtime.agentId,
|
|
33259
|
-
conversationId: runtime.currentTask.conversationId,
|
|
33260
|
-
ackId: runtime.currentTask.replyMessageId,
|
|
33261
|
-
traceId: runtime.currentTask.traceId,
|
|
33262
|
-
error: emittedErrorText
|
|
33263
|
-
}
|
|
33264
|
-
});
|
|
33265
|
-
runtime.currentTask = null;
|
|
33266
|
-
}
|
|
33267
|
-
for (const task of runtime.injectedTasks) {
|
|
33268
|
-
this.emit({
|
|
33269
|
-
type: "agent:error",
|
|
33270
|
-
payload: {
|
|
33271
|
-
agentId: runtime.agentId,
|
|
33272
|
-
conversationId: task.conversationId,
|
|
33273
|
-
ackId: task.replyMessageId,
|
|
33274
|
-
traceId: task.traceId,
|
|
33275
|
-
error: emittedErrorText
|
|
33276
|
-
}
|
|
33277
|
-
});
|
|
33278
|
-
}
|
|
33279
|
-
runtime.injectedTasks = [];
|
|
33280
|
-
for (const task of runtime.mergedTasks) {
|
|
33281
|
-
this.emit({
|
|
33282
|
-
type: "agent:error",
|
|
33283
|
-
payload: {
|
|
33284
|
-
agentId: runtime.agentId,
|
|
33285
|
-
conversationId: task.conversationId,
|
|
33286
|
-
ackId: task.replyMessageId,
|
|
33287
|
-
traceId: task.traceId,
|
|
33288
|
-
error: emittedErrorText
|
|
33289
|
-
}
|
|
33290
|
-
});
|
|
33291
|
-
}
|
|
33292
|
-
runtime.mergedTasks = [];
|
|
33293
|
-
for (const task of runtime.planModeBuffer) {
|
|
33294
|
-
this.emit({
|
|
33295
|
-
type: "agent:error",
|
|
33296
|
-
payload: {
|
|
33297
|
-
agentId: runtime.agentId,
|
|
33298
|
-
conversationId: task.conversationId,
|
|
33299
|
-
ackId: task.replyMessageId,
|
|
33300
|
-
traceId: task.traceId,
|
|
33301
|
-
error: emittedErrorText
|
|
33302
|
-
}
|
|
33303
|
-
});
|
|
33304
|
-
}
|
|
33305
|
-
runtime.planModeBuffer = [];
|
|
33902
|
+
this.failPendingTasksWithError(runtime, emittedErrorText);
|
|
33306
33903
|
}
|
|
33307
33904
|
}
|
|
33308
33905
|
getStatus(agentId, scope = { kind: "single" }) {
|
|
@@ -33316,6 +33913,18 @@ ${lines.join("\n")}`;
|
|
|
33316
33913
|
}
|
|
33317
33914
|
return [...ids];
|
|
33318
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
|
+
}
|
|
33319
33928
|
latestOpenToolUse(proc) {
|
|
33320
33929
|
for (let i = proc.contentBlocks.length - 1; i >= 0; i -= 1) {
|
|
33321
33930
|
const block = proc.contentBlocks[i];
|
|
@@ -33464,7 +34073,7 @@ ${lines.join("\n")}`;
|
|
|
33464
34073
|
}
|
|
33465
34074
|
const task = {
|
|
33466
34075
|
content: notice,
|
|
33467
|
-
replyMessageId:
|
|
34076
|
+
replyMessageId: createScopeNoticeReplyMessageId(),
|
|
33468
34077
|
conversationId,
|
|
33469
34078
|
traceId: createTraceId(),
|
|
33470
34079
|
groupId: proc.scope.kind === "group" ? proc.scope.groupId : void 0
|
|
@@ -34546,6 +35155,7 @@ var HttpMcpRegistry = class {
|
|
|
34546
35155
|
buildForAgent(ctx) {
|
|
34547
35156
|
const mcpServers = {};
|
|
34548
35157
|
const allowedTools = [];
|
|
35158
|
+
const toolAbi = [];
|
|
34549
35159
|
const usedNames = /* @__PURE__ */ new Set();
|
|
34550
35160
|
for (const connection of this.allConnections()) {
|
|
34551
35161
|
if (!this.connectionAppliesToAgent(connection, ctx)) continue;
|
|
@@ -34554,12 +35164,18 @@ var HttpMcpRegistry = class {
|
|
|
34554
35164
|
const serverName = uniqueServerName(normalizeMcpServerName(connection.serverName), usedNames);
|
|
34555
35165
|
usedNames.add(serverName);
|
|
34556
35166
|
mcpServers[serverName] = sdkConfig;
|
|
34557
|
-
|
|
34558
|
-
|
|
34559
|
-
|
|
34560
|
-
|
|
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
|
+
});
|
|
34561
35177
|
}
|
|
34562
|
-
return { mcpServers, allowedTools };
|
|
35178
|
+
return { mcpServers, allowedTools, toolAbi };
|
|
34563
35179
|
}
|
|
34564
35180
|
allConnections() {
|
|
34565
35181
|
return [...this.serverConnections.values(), ...this.localConnections.values()];
|
|
@@ -34745,6 +35361,18 @@ function uniqueServerName(serverName, usedNames) {
|
|
|
34745
35361
|
while (usedNames.has(`${serverName}_${idx}`)) idx += 1;
|
|
34746
35362
|
return `${serverName}_${idx}`;
|
|
34747
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
|
+
}
|
|
34748
35376
|
function buildHeaders(authType, authSecret, customHeaders) {
|
|
34749
35377
|
const headers = {};
|
|
34750
35378
|
for (const header of customHeaders) {
|
|
@@ -35396,6 +36024,7 @@ var ServerConnector = class {
|
|
|
35396
36024
|
case "agent:terminate":
|
|
35397
36025
|
case "agent:runtime_reload":
|
|
35398
36026
|
case "agent:terminate_scope":
|
|
36027
|
+
case "spectate:set":
|
|
35399
36028
|
case "agent:created":
|
|
35400
36029
|
case "agent:updated":
|
|
35401
36030
|
case "agent:workdir-updated":
|
|
@@ -36371,24 +37000,6 @@ function normalizeLocalPath(targetPath) {
|
|
|
36371
37000
|
const expanded = trimmed === "~" || trimmed.startsWith("~/") || trimmed.startsWith("~\\") ? path18.join(os10.homedir(), trimmed.slice(2)) : trimmed;
|
|
36372
37001
|
return path18.normalize(path18.resolve(expanded));
|
|
36373
37002
|
}
|
|
36374
|
-
function isAbsoluteLocalPathCandidate(value) {
|
|
36375
|
-
if (process.platform === "win32") {
|
|
36376
|
-
return /^[a-zA-Z]:[\\/]/.test(value) || /^\\\\[^\\]/.test(value);
|
|
36377
|
-
}
|
|
36378
|
-
return value.startsWith("/");
|
|
36379
|
-
}
|
|
36380
|
-
function clipboardTextToPathCandidates(value) {
|
|
36381
|
-
return value.replace(/\0/g, "\n").split(/\r?\n/).map((line) => line.trim().replace(/^"(.+)"$/, "$1")).map((line) => {
|
|
36382
|
-
if (!line.toLowerCase().startsWith("file://")) return line;
|
|
36383
|
-
try {
|
|
36384
|
-
const url2 = new URL(line);
|
|
36385
|
-
return decodeURIComponent(url2.pathname.replace(/^\/([a-zA-Z]:\/)/, "$1"));
|
|
36386
|
-
} catch (e) {
|
|
36387
|
-
logger24.debug("Failed to parse clipboard file URL", { error: e });
|
|
36388
|
-
return "";
|
|
36389
|
-
}
|
|
36390
|
-
}).filter((line) => line.length > 0 && isAbsoluteLocalPathCandidate(line));
|
|
36391
|
-
}
|
|
36392
37003
|
function normalizeClipboardIdentityKey(value) {
|
|
36393
37004
|
return process.platform === "win32" ? value.toLowerCase() : value;
|
|
36394
37005
|
}
|
|
@@ -36426,18 +37037,15 @@ function mimeTypeForFileName(fileName) {
|
|
|
36426
37037
|
}
|
|
36427
37038
|
function parseWindowsClipboardResult(stdout) {
|
|
36428
37039
|
const raw = stdout.trim();
|
|
36429
|
-
if (!raw) return { files: []
|
|
37040
|
+
if (!raw) return { files: [] };
|
|
36430
37041
|
try {
|
|
36431
37042
|
const parsed = JSON.parse(raw);
|
|
36432
|
-
if (!isRecord5(parsed)) return { files: []
|
|
37043
|
+
if (!isRecord5(parsed)) return { files: [] };
|
|
36433
37044
|
const files = Array.isArray(parsed.files) ? parsed.files.filter((item) => typeof item === "string") : [];
|
|
36434
|
-
return {
|
|
36435
|
-
files,
|
|
36436
|
-
text: typeof parsed.text === "string" ? parsed.text : ""
|
|
36437
|
-
};
|
|
37045
|
+
return { files };
|
|
36438
37046
|
} catch (e) {
|
|
36439
37047
|
logger24.debug("Windows clipboard JSON parse skipped", { error: e });
|
|
36440
|
-
return { files:
|
|
37048
|
+
return { files: [] };
|
|
36441
37049
|
}
|
|
36442
37050
|
}
|
|
36443
37051
|
async function readWindowsClipboardPathCandidates() {
|
|
@@ -36450,9 +37058,7 @@ async function readWindowsClipboardPathCandidates() {
|
|
|
36450
37058
|
" $drop = [System.Windows.Forms.Clipboard]::GetFileDropList();",
|
|
36451
37059
|
" foreach ($file in $drop) { $files += [string]$file }",
|
|
36452
37060
|
"} catch {}",
|
|
36453
|
-
|
|
36454
|
-
"try { $text = [System.Windows.Forms.Clipboard]::GetText() } catch {}",
|
|
36455
|
-
"[pscustomobject]@{ files = $files; text = $text } | ConvertTo-Json -Compress;"
|
|
37061
|
+
"[pscustomobject]@{ files = $files } | ConvertTo-Json -Compress;"
|
|
36456
37062
|
].join(" ");
|
|
36457
37063
|
try {
|
|
36458
37064
|
const { stdout } = await execFileAsync2("powershell.exe", [
|
|
@@ -36470,7 +37076,7 @@ async function readWindowsClipboardPathCandidates() {
|
|
|
36470
37076
|
maxBuffer: 1024 * 1024
|
|
36471
37077
|
});
|
|
36472
37078
|
const result = parseWindowsClipboardResult(stdout);
|
|
36473
|
-
return
|
|
37079
|
+
return result.files;
|
|
36474
37080
|
} catch (e) {
|
|
36475
37081
|
logger24.debug("Windows clipboard file read skipped", { error: e });
|
|
36476
37082
|
return [];
|
|
@@ -36873,7 +37479,7 @@ async function readStreamText(filePath, start) {
|
|
|
36873
37479
|
function parseProcessedLines(raw, cursor, fileName, fingerprint) {
|
|
36874
37480
|
const lastNewline = raw.lastIndexOf("\n");
|
|
36875
37481
|
if (lastNewline < 0) {
|
|
36876
|
-
return { entries: [], nextCursor: cursor, advanced: false };
|
|
37482
|
+
return { entries: [], nextCursor: cursor, advanced: false, reason: "partial_line" };
|
|
36877
37483
|
}
|
|
36878
37484
|
const processed = raw.slice(0, lastNewline + 1);
|
|
36879
37485
|
const lines = processed.split(/\r?\n/);
|
|
@@ -36899,7 +37505,8 @@ function parseProcessedLines(raw, cursor, fileName, fingerprint) {
|
|
|
36899
37505
|
lineNum,
|
|
36900
37506
|
...fingerprint ? { fingerprint } : {}
|
|
36901
37507
|
},
|
|
36902
|
-
advanced: true
|
|
37508
|
+
advanced: true,
|
|
37509
|
+
reason: "advanced"
|
|
36903
37510
|
};
|
|
36904
37511
|
}
|
|
36905
37512
|
function chunkEntries(entries, size) {
|
|
@@ -36957,24 +37564,55 @@ var BridgeLogUploader = class {
|
|
|
36957
37564
|
async flushOnce() {
|
|
36958
37565
|
if (this.running || this.stopped) return;
|
|
36959
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
|
+
};
|
|
36960
37581
|
try {
|
|
36961
37582
|
const targets = await this.resolveTargets();
|
|
37583
|
+
summary.targetCount = targets.length;
|
|
36962
37584
|
for (const target of targets) {
|
|
36963
37585
|
try {
|
|
36964
37586
|
const cursor = await readCursor(target.cursorFile);
|
|
36965
37587
|
const batch = await this.readNewEntries(target, cursor);
|
|
36966
|
-
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;
|
|
36967
37596
|
if (batch.entries.length > 0) {
|
|
36968
|
-
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;
|
|
36969
37602
|
}
|
|
36970
37603
|
await writeCursor(target.cursorFile, batch.nextCursor);
|
|
36971
37604
|
} catch (e) {
|
|
37605
|
+
summary.failedTargetCount += 1;
|
|
36972
37606
|
logger27.warn("Bridge log upload target failed", { error: e, logFile: target.logFile });
|
|
36973
37607
|
}
|
|
36974
37608
|
}
|
|
36975
37609
|
} catch (e) {
|
|
36976
37610
|
logger27.warn("Bridge log upload cycle failed", { error: e });
|
|
36977
37611
|
} finally {
|
|
37612
|
+
logger27.info("Bridge log upload cycle summary", {
|
|
37613
|
+
...summary,
|
|
37614
|
+
durationMs: Date.now() - startedAt
|
|
37615
|
+
});
|
|
36978
37616
|
this.running = false;
|
|
36979
37617
|
}
|
|
36980
37618
|
}
|
|
@@ -36997,7 +37635,7 @@ var BridgeLogUploader = class {
|
|
|
36997
37635
|
} catch (e) {
|
|
36998
37636
|
if (e instanceof Error && "code" in e && e.code === "ENOENT") {
|
|
36999
37637
|
logger27.debug("Bridge log file not found for upload yet", { logFile: target.logFile });
|
|
37000
|
-
return { entries: [], nextCursor: cursor, advanced: false };
|
|
37638
|
+
return { entries: [], nextCursor: cursor, advanced: false, reason: "missing_file" };
|
|
37001
37639
|
}
|
|
37002
37640
|
throw e;
|
|
37003
37641
|
}
|
|
@@ -37005,13 +37643,19 @@ var BridgeLogUploader = class {
|
|
|
37005
37643
|
const samePhysicalFile = !fingerprint || !cursor.fingerprint || cursor.fingerprint === fingerprint;
|
|
37006
37644
|
const normalizedCursor = stat3.size < cursor.offset || !samePhysicalFile ? { offset: 0, lineNum: 0, ...fingerprint ? { fingerprint } : {} } : { ...cursor, ...fingerprint ? { fingerprint } : {} };
|
|
37007
37645
|
if (stat3.size <= normalizedCursor.offset) {
|
|
37008
|
-
return { entries: [], nextCursor: normalizedCursor, advanced: false };
|
|
37646
|
+
return { entries: [], nextCursor: normalizedCursor, advanced: false, reason: "no_new_entries" };
|
|
37009
37647
|
}
|
|
37010
37648
|
const raw = await readStreamText(target.logFile, normalizedCursor.offset);
|
|
37011
37649
|
return parseProcessedLines(raw, normalizedCursor, target.uploadedFileName, fingerprint);
|
|
37012
37650
|
}
|
|
37013
37651
|
async uploadEntries(entries) {
|
|
37014
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
|
+
};
|
|
37015
37659
|
for (const chunk of chunkEntries(bridgeEntries, this.options.batchSize)) {
|
|
37016
37660
|
const res = await fetch(`${this.options.serverApiUrl}/api/logs/upload`, {
|
|
37017
37661
|
method: "POST",
|
|
@@ -37026,14 +37670,18 @@ var BridgeLogUploader = class {
|
|
|
37026
37670
|
})
|
|
37027
37671
|
});
|
|
37028
37672
|
if (!res.ok) {
|
|
37029
|
-
const
|
|
37673
|
+
const body2 = await res.text().catch((e) => {
|
|
37030
37674
|
logger27.debug("Failed to read log upload error body", { error: e });
|
|
37031
37675
|
return "";
|
|
37032
37676
|
});
|
|
37033
|
-
throw new Error(`upload failed HTTP ${res.status}: ${
|
|
37677
|
+
throw new Error(`upload failed HTTP ${res.status}: ${body2.slice(0, 160)}`);
|
|
37034
37678
|
}
|
|
37035
|
-
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;
|
|
37036
37683
|
}
|
|
37684
|
+
return result;
|
|
37037
37685
|
}
|
|
37038
37686
|
};
|
|
37039
37687
|
|
|
@@ -38118,8 +38766,10 @@ function resolvePnpmRuntimeBinary(sdkDir, target) {
|
|
|
38118
38766
|
} catch {
|
|
38119
38767
|
return void 0;
|
|
38120
38768
|
}
|
|
38769
|
+
const matches = [];
|
|
38121
38770
|
for (const entry of entries) {
|
|
38122
38771
|
if (!entry.startsWith(`${encodedName}@`)) continue;
|
|
38772
|
+
const version2 = entry.slice(encodedName.length + 1);
|
|
38123
38773
|
const candidate = path27.join(
|
|
38124
38774
|
pnpmStoreDir,
|
|
38125
38775
|
entry,
|
|
@@ -38127,9 +38777,22 @@ function resolvePnpmRuntimeBinary(sdkDir, target) {
|
|
|
38127
38777
|
...target.packageName.split("/"),
|
|
38128
38778
|
target.binaryName
|
|
38129
38779
|
);
|
|
38130
|
-
if (existsSync2(candidate))
|
|
38780
|
+
if (existsSync2(candidate)) matches.push({ version: version2, candidate });
|
|
38131
38781
|
}
|
|
38132
|
-
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;
|
|
38133
38796
|
}
|
|
38134
38797
|
function resolveSdkRuntimeBinary(target) {
|
|
38135
38798
|
const directPath = safeResolve(`${target.packageName}/${target.binaryName}`);
|
|
@@ -39288,6 +39951,32 @@ function syncLocalRuntimeSkills(skillStore, localSkills, options = {}) {
|
|
|
39288
39951
|
]);
|
|
39289
39952
|
}
|
|
39290
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
|
+
|
|
39291
39980
|
// src/start.ts
|
|
39292
39981
|
var logger41 = createModuleLogger("bridge");
|
|
39293
39982
|
var NODE_USER_UID2 = 1e3;
|
|
@@ -39374,14 +40063,16 @@ async function startBridge(config2) {
|
|
|
39374
40063
|
const claudeRuntime = resolveClaudeRuntime();
|
|
39375
40064
|
logClaudeRuntimeResolution(claudeRuntime);
|
|
39376
40065
|
if (!claudeRuntime.ok || !claudeRuntime.path) {
|
|
39377
|
-
|
|
40066
|
+
safeWriteProcessOutput(
|
|
40067
|
+
process.stderr,
|
|
39378
40068
|
`
|
|
39379
40069
|
Claude runtime is unavailable.
|
|
39380
40070
|
|
|
39381
40071
|
${claudeRuntime.error ?? "Install Claude Code manually or use the bundled desktop runtime."}
|
|
39382
40072
|
|
|
39383
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.
|
|
39384
|
-
|
|
40074
|
+
`,
|
|
40075
|
+
(e) => logger41.error("Bridge process stderr write failed", { error: e })
|
|
39385
40076
|
);
|
|
39386
40077
|
process.exit(1);
|
|
39387
40078
|
}
|
|
@@ -39438,12 +40129,14 @@ Reinstall @fangyb/ahchat-bridge with npm optional dependencies, set AHCHAT_CLAUD
|
|
|
39438
40129
|
claudeRuntimeVersion: claudeRuntime.version ?? null
|
|
39439
40130
|
});
|
|
39440
40131
|
const shouldPrintRawBridgeToken = process.stdout.isTTY && process.env.AHCHAT_SUPPRESS_BRIDGE_TOKEN_STDOUT !== "1";
|
|
39441
|
-
|
|
40132
|
+
safeWriteProcessOutput(
|
|
40133
|
+
process.stdout,
|
|
39442
40134
|
`
|
|
39443
40135
|
Bridge token (register this machine at Settings \u2192 \u5DF2\u8FDE\u63A5\u7684\u673A\u5668):
|
|
39444
40136
|
${shouldPrintRawBridgeToken ? config2.bridgeToken : "***"}
|
|
39445
40137
|
|
|
39446
|
-
|
|
40138
|
+
`,
|
|
40139
|
+
(e) => logger41.error("Bridge process stdout write failed", { error: e })
|
|
39447
40140
|
);
|
|
39448
40141
|
wsMetrics.start(5e3);
|
|
39449
40142
|
const sessionStore = new SessionStore(config2.dataDir);
|
|
@@ -40242,6 +40935,19 @@ Bridge token (register this machine at Settings \u2192 \u5DF2\u8FDE\u63A5\u7684\
|
|
|
40242
40935
|
});
|
|
40243
40936
|
await agentManager.terminateScope(msg.payload.agentId, msg.payload.scope);
|
|
40244
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;
|
|
40245
40951
|
case "agent:created":
|
|
40246
40952
|
agentRegistry.upsert(msg.payload.agent);
|
|
40247
40953
|
ensureLocalWorkdirPath(msg.payload.agent.workingDirectory, "agent:created", {
|
|
@@ -40458,8 +41164,12 @@ function writeAlreadyRunningMessage(error51) {
|
|
|
40458
41164
|
` ${buildStopCommand(error51.pid)}`,
|
|
40459
41165
|
""
|
|
40460
41166
|
];
|
|
40461
|
-
|
|
40462
|
-
|
|
41167
|
+
safeWriteProcessOutput(
|
|
41168
|
+
process.stdout,
|
|
41169
|
+
`${lines.join("\n")}
|
|
41170
|
+
`,
|
|
41171
|
+
(e) => logger42.error("Bridge already-running message write failed", { error: e })
|
|
41172
|
+
);
|
|
40463
41173
|
}
|
|
40464
41174
|
function handleBridgeStartError(e, message) {
|
|
40465
41175
|
if (isBridgeAlreadyRunningError(e)) {
|