@rubytech/create-realagent 1.0.615 → 1.0.617
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/package.json +1 -1
- package/payload/platform/lib/mcp-stderr-tee/dist/index.d.ts +23 -13
- package/payload/platform/lib/mcp-stderr-tee/dist/index.d.ts.map +1 -1
- package/payload/platform/lib/mcp-stderr-tee/dist/index.js +86 -89
- package/payload/platform/lib/mcp-stderr-tee/dist/index.js.map +1 -1
- package/payload/platform/lib/mcp-stderr-tee/src/index.ts +86 -101
- package/payload/platform/package-lock.json +1547 -1
- package/payload/platform/plugins/admin/mcp/dist/index.js +33 -2
- package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/admin/skills/stream-log-review/SKILL.md +22 -8
- package/payload/platform/plugins/cloudflare/PLUGIN.md +5 -4
- package/payload/platform/plugins/cloudflare/mcp/__tests__/auth-binding.test.ts +195 -0
- package/payload/platform/plugins/cloudflare/mcp/dist/index.js +160 -214
- package/payload/platform/plugins/cloudflare/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/cloudflare/mcp/dist/lib/cloudflared.d.ts +203 -42
- package/payload/platform/plugins/cloudflare/mcp/dist/lib/cloudflared.d.ts.map +1 -1
- package/payload/platform/plugins/cloudflare/mcp/dist/lib/cloudflared.js +623 -195
- package/payload/platform/plugins/cloudflare/mcp/dist/lib/cloudflared.js.map +1 -1
- package/payload/platform/plugins/cloudflare/mcp/package.json +5 -2
- package/payload/platform/plugins/cloudflare/mcp/vitest.config.ts +10 -0
- package/payload/platform/plugins/cloudflare/references/setup-guide.md +26 -30
- package/payload/platform/plugins/cloudflare/skills/setup-tunnel/SKILL.md +28 -4
- package/payload/platform/plugins/docs/PLUGIN.md +2 -0
- package/payload/platform/plugins/docs/references/cloudflare.md +51 -0
- package/payload/platform/plugins/docs/references/plugins-guide.md +8 -6
- package/payload/platform/scripts/logs-read.sh +114 -54
- package/payload/platform/templates/specialists/agents/personal-assistant.md +12 -8
- package/payload/server/server.js +387 -70
package/payload/server/server.js
CHANGED
|
@@ -2898,7 +2898,7 @@ var serveStatic = (options = { root: "" }) => {
|
|
|
2898
2898
|
// server/index.ts
|
|
2899
2899
|
import { readFileSync as readFileSync26, existsSync as existsSync26, watchFile } from "fs";
|
|
2900
2900
|
import { resolve as resolve27, join as join13, basename as basename6 } from "path";
|
|
2901
|
-
import { homedir as
|
|
2901
|
+
import { homedir as homedir4 } from "os";
|
|
2902
2902
|
|
|
2903
2903
|
// app/lib/vnc-logger.ts
|
|
2904
2904
|
import { appendFileSync, mkdirSync } from "fs";
|
|
@@ -4162,12 +4162,14 @@ function keyFilePath() {
|
|
|
4162
4162
|
|
|
4163
4163
|
// app/lib/claude-agent.ts
|
|
4164
4164
|
import Anthropic2 from "@anthropic-ai/sdk";
|
|
4165
|
-
import { spawn as spawn2 } from "child_process";
|
|
4165
|
+
import { spawn as spawn2, spawnSync as spawnSync2 } from "child_process";
|
|
4166
4166
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
4167
4167
|
import { resolve as resolve6, join as join4 } from "path";
|
|
4168
|
+
import { platform as osPlatform } from "os";
|
|
4168
4169
|
import { readFileSync as readFileSync7, writeFileSync as writeFileSync5, readdirSync as readdirSync2, existsSync as existsSync6, mkdirSync as mkdirSync4, createWriteStream, statSync as statSync3, unlinkSync, cpSync, rmSync } from "fs";
|
|
4169
4170
|
import { lookup as dnsLookup } from "dns/promises";
|
|
4170
4171
|
import { createConnection as netConnect } from "net";
|
|
4172
|
+
import { StringDecoder } from "string_decoder";
|
|
4171
4173
|
|
|
4172
4174
|
// ../lib/models/src/index.ts
|
|
4173
4175
|
var OPUS_MODEL = "claude-opus-4-7";
|
|
@@ -5463,9 +5465,11 @@ function readRecentToolFailures(accountId, conversationId) {
|
|
|
5463
5465
|
try {
|
|
5464
5466
|
const platformRoot3 = process.env.MAXY_PLATFORM_ROOT ?? resolve5(process.cwd(), "..");
|
|
5465
5467
|
const logDir = resolve5(platformRoot3, "..", "data/accounts", accountId, "logs");
|
|
5466
|
-
const
|
|
5467
|
-
|
|
5468
|
-
|
|
5468
|
+
const logPath2 = resolve5(logDir, `claude-agent-stream-${conversationId}.log`);
|
|
5469
|
+
if (!existsSync5(logPath2)) {
|
|
5470
|
+
console.error(`[review-tail-skip] path=${logPath2} reason=file-missing \u2014 first turn of conversation, subprocess not yet spawned, or log rotated`);
|
|
5471
|
+
return [];
|
|
5472
|
+
}
|
|
5469
5473
|
const st = statSync2(logPath2);
|
|
5470
5474
|
const size = st.size;
|
|
5471
5475
|
const readBytes = Math.min(size, RECENT_FAILURES_TAIL_BYTES);
|
|
@@ -5475,12 +5479,11 @@ function readRecentToolFailures(accountId, conversationId) {
|
|
|
5475
5479
|
const buf = Buffer.alloc(readBytes);
|
|
5476
5480
|
readSync(fd, buf, 0, readBytes, position);
|
|
5477
5481
|
const text = buf.toString("utf-8");
|
|
5478
|
-
const convPrefix = conversationId.slice(0, 8);
|
|
5479
5482
|
const lines = text.split("\n");
|
|
5480
5483
|
const matches = [];
|
|
5481
5484
|
for (let i = lines.length - 1; i >= 0 && matches.length < MAX_RECENT_TOOL_FAILURES; i--) {
|
|
5482
5485
|
const line = lines[i];
|
|
5483
|
-
if (line.includes("[tool-failure-diag]")
|
|
5486
|
+
if (line.includes("[tool-failure-diag]")) {
|
|
5484
5487
|
matches.push(line);
|
|
5485
5488
|
}
|
|
5486
5489
|
}
|
|
@@ -6170,20 +6173,137 @@ async function runFailureDiagnostic(toolName, toolInput) {
|
|
|
6170
6173
|
const fields = capped.map((r) => r.status === "fulfilled" ? r.value : `probe_err=${quoteDiag(String(r.reason).slice(0, 40))}`).join(" ");
|
|
6171
6174
|
return `diag_host=${host} diag_port=${port2} ${fields} input_keys=[${inputKeys}] ${envFields}`;
|
|
6172
6175
|
}
|
|
6173
|
-
function agentLogStream(name, accountDir) {
|
|
6176
|
+
function agentLogStream(name, accountDir, conversationId) {
|
|
6177
|
+
if (!conversationId) {
|
|
6178
|
+
throw new Error(`agentLogStream: conversationId is required (name=${name}) \u2014 use preConversationLogStream for pre-session events`);
|
|
6179
|
+
}
|
|
6180
|
+
const logDir = resolve6(accountDir, "logs");
|
|
6181
|
+
mkdirSync4(logDir, { recursive: true });
|
|
6182
|
+
purgeOldLogs(logDir, `${name}-`);
|
|
6183
|
+
return createWriteStream(resolve6(logDir, `${name}-${conversationId}.log`), { flags: "a" });
|
|
6184
|
+
}
|
|
6185
|
+
function preConversationLogStream(name, accountDir) {
|
|
6174
6186
|
const logDir = resolve6(accountDir, "logs");
|
|
6175
6187
|
mkdirSync4(logDir, { recursive: true });
|
|
6176
6188
|
const date5 = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
6189
|
+
purgeOldLogs(logDir, `preconversation-${name}-`);
|
|
6190
|
+
return createWriteStream(resolve6(logDir, `preconversation-${name}-${date5}.log`), { flags: "a" });
|
|
6191
|
+
}
|
|
6192
|
+
function purgeOldLogs(logDir, prefix) {
|
|
6177
6193
|
const cutoff = Date.now() - LOG_RETENTION_DAYS * 24 * 60 * 60 * 1e3;
|
|
6194
|
+
let entries;
|
|
6178
6195
|
try {
|
|
6179
|
-
|
|
6180
|
-
|
|
6181
|
-
|
|
6196
|
+
entries = readdirSync2(logDir);
|
|
6197
|
+
} catch (err) {
|
|
6198
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
6199
|
+
console.error(`[log-purge-err] readdir dir=${logDir} prefix=${prefix} reason=${msg}`);
|
|
6200
|
+
return;
|
|
6201
|
+
}
|
|
6202
|
+
for (const file2 of entries) {
|
|
6203
|
+
if (!file2.startsWith(prefix)) continue;
|
|
6204
|
+
const filePath = resolve6(logDir, file2);
|
|
6205
|
+
try {
|
|
6182
6206
|
if (statSync3(filePath).mtimeMs < cutoff) unlinkSync(filePath);
|
|
6207
|
+
} catch (err) {
|
|
6208
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
6209
|
+
console.error(`[log-purge-err] file=${file2} reason=${msg}`);
|
|
6183
6210
|
}
|
|
6184
|
-
} catch {
|
|
6185
6211
|
}
|
|
6186
|
-
|
|
6212
|
+
}
|
|
6213
|
+
function teeProcStderrToStreamLog(proc, streamLog) {
|
|
6214
|
+
if (!proc.stderr) {
|
|
6215
|
+
streamLog.write(`[${isoTs()}] [subproc-stderr-skip] reason=no-stderr
|
|
6216
|
+
`);
|
|
6217
|
+
return;
|
|
6218
|
+
}
|
|
6219
|
+
const utf8 = new StringDecoder("utf8");
|
|
6220
|
+
let buffer = "";
|
|
6221
|
+
proc.stderr.on("data", (chunk) => {
|
|
6222
|
+
const text = typeof chunk === "string" ? chunk : utf8.write(chunk);
|
|
6223
|
+
buffer += text;
|
|
6224
|
+
let idx;
|
|
6225
|
+
while ((idx = buffer.indexOf("\n")) !== -1) {
|
|
6226
|
+
const line = buffer.slice(0, idx);
|
|
6227
|
+
buffer = buffer.slice(idx + 1);
|
|
6228
|
+
if (line.length === 0) continue;
|
|
6229
|
+
if (streamLog.destroyed || streamLog.writableEnded) continue;
|
|
6230
|
+
streamLog.write(`[${isoTs()}] [subproc-stderr] ${line}
|
|
6231
|
+
`);
|
|
6232
|
+
}
|
|
6233
|
+
});
|
|
6234
|
+
proc.stderr.on("end", () => {
|
|
6235
|
+
const tail = (buffer + utf8.end()).trim();
|
|
6236
|
+
if (tail.length > 0 && !streamLog.destroyed && !streamLog.writableEnded) {
|
|
6237
|
+
streamLog.write(`[${isoTs()}] [subproc-stderr] ${tail}
|
|
6238
|
+
`);
|
|
6239
|
+
}
|
|
6240
|
+
buffer = "";
|
|
6241
|
+
});
|
|
6242
|
+
}
|
|
6243
|
+
function sampleProcState(pid) {
|
|
6244
|
+
try {
|
|
6245
|
+
if (osPlatform() === "linux") {
|
|
6246
|
+
const fdDir = `/proc/${pid}/fd`;
|
|
6247
|
+
let openFds2 = 0;
|
|
6248
|
+
try {
|
|
6249
|
+
openFds2 = readdirSync2(fdDir).length;
|
|
6250
|
+
} catch (err) {
|
|
6251
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
6252
|
+
return `proc_err=${JSON.stringify(msg.slice(0, 60))}`;
|
|
6253
|
+
}
|
|
6254
|
+
let established2 = 0;
|
|
6255
|
+
let connecting2 = 0;
|
|
6256
|
+
let sockets2 = 0;
|
|
6257
|
+
for (const tcpFile of ["/proc/" + pid + "/net/tcp", "/proc/" + pid + "/net/tcp6"]) {
|
|
6258
|
+
try {
|
|
6259
|
+
const raw2 = readFileSync7(tcpFile, "utf-8");
|
|
6260
|
+
const lines2 = raw2.split("\n");
|
|
6261
|
+
for (let i = 1; i < lines2.length; i++) {
|
|
6262
|
+
const line = lines2[i].trim();
|
|
6263
|
+
if (!line) continue;
|
|
6264
|
+
const parts = line.split(/\s+/);
|
|
6265
|
+
const state = parts[3];
|
|
6266
|
+
sockets2 += 1;
|
|
6267
|
+
if (state === "01") established2 += 1;
|
|
6268
|
+
else if (state === "02" || state === "03") connecting2 += 1;
|
|
6269
|
+
}
|
|
6270
|
+
} catch {
|
|
6271
|
+
}
|
|
6272
|
+
}
|
|
6273
|
+
let rssMb = 0;
|
|
6274
|
+
try {
|
|
6275
|
+
const statm = readFileSync7(`/proc/${pid}/statm`, "utf-8").trim().split(/\s+/);
|
|
6276
|
+
const rssPages = parseInt(statm[1] ?? "0", 10);
|
|
6277
|
+
if (Number.isFinite(rssPages)) rssMb = Math.round(rssPages * 4096 / (1024 * 1024));
|
|
6278
|
+
} catch {
|
|
6279
|
+
}
|
|
6280
|
+
return `open_fds=${openFds2} socket_count=${sockets2} tcp_established=${established2} tcp_connecting=${connecting2} rss_mb=${rssMb}`;
|
|
6281
|
+
}
|
|
6282
|
+
const result = spawnSync2("lsof", ["-p", String(pid), "-nP"], { timeout: 500, encoding: "utf-8" });
|
|
6283
|
+
if (result.error || result.status !== 0) {
|
|
6284
|
+
const reason = result.error instanceof Error ? result.error.message : `exit=${result.status}`;
|
|
6285
|
+
return `proc_err=${JSON.stringify(reason.slice(0, 60))}`;
|
|
6286
|
+
}
|
|
6287
|
+
const lines = result.stdout.split("\n");
|
|
6288
|
+
let openFds = 0;
|
|
6289
|
+
let sockets = 0;
|
|
6290
|
+
let established = 0;
|
|
6291
|
+
let connecting = 0;
|
|
6292
|
+
for (let i = 1; i < lines.length; i++) {
|
|
6293
|
+
const line = lines[i];
|
|
6294
|
+
if (!line) continue;
|
|
6295
|
+
openFds += 1;
|
|
6296
|
+
if (line.includes("IPv4") || line.includes("IPv6") || line.includes("TCP")) {
|
|
6297
|
+
sockets += 1;
|
|
6298
|
+
if (/ESTABLISHED/.test(line)) established += 1;
|
|
6299
|
+
else if (/SYN_SENT|SYN_RCVD/.test(line)) connecting += 1;
|
|
6300
|
+
}
|
|
6301
|
+
}
|
|
6302
|
+
return `open_fds=${openFds} socket_count=${sockets} tcp_established=${established} tcp_connecting=${connecting} rss_mb=unknown`;
|
|
6303
|
+
} catch (err) {
|
|
6304
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
6305
|
+
return `proc_err=${JSON.stringify(msg.slice(0, 60))}`;
|
|
6306
|
+
}
|
|
6187
6307
|
}
|
|
6188
6308
|
var PLATFORM_ROOT4 = process.env.MAXY_PLATFORM_ROOT ?? resolve6(process.cwd(), "..");
|
|
6189
6309
|
var ACCOUNTS_DIR = resolve6(PLATFORM_ROOT4, "..", "data/accounts");
|
|
@@ -7377,43 +7497,48 @@ function consumeStalledSubagents(sessionKey) {
|
|
|
7377
7497
|
delete session.stalledSubagents;
|
|
7378
7498
|
return stalls && stalls.length > 0 ? stalls : void 0;
|
|
7379
7499
|
}
|
|
7380
|
-
function getMcpServers(accountId, userId, enabledPlugins) {
|
|
7500
|
+
function getMcpServers(accountId, conversationId, userId, enabledPlugins) {
|
|
7501
|
+
if (!conversationId) {
|
|
7502
|
+
throw new Error(`getMcpServers: conversationId is required (accountId=${accountId.slice(0, 8)})`);
|
|
7503
|
+
}
|
|
7381
7504
|
const LOG_DIR2 = resolve6(ACCOUNTS_DIR, accountId, "logs");
|
|
7505
|
+
const STREAM_LOG_PATH = resolve6(LOG_DIR2, `claude-agent-stream-${conversationId}.log`);
|
|
7506
|
+
const baseEnv = { ACCOUNT_ID: accountId, PLATFORM_ROOT: PLATFORM_ROOT4, LOG_DIR: LOG_DIR2, STREAM_LOG_PATH };
|
|
7382
7507
|
const servers = {
|
|
7383
7508
|
"memory": {
|
|
7384
7509
|
command: "node",
|
|
7385
7510
|
args: [resolve6(PLATFORM_ROOT4, "plugins/memory/mcp/dist/index.js")],
|
|
7386
|
-
env: {
|
|
7511
|
+
env: { ...baseEnv, ...userId ? { USER_ID: userId } : {} }
|
|
7387
7512
|
},
|
|
7388
7513
|
"contacts": {
|
|
7389
7514
|
command: "node",
|
|
7390
7515
|
args: [resolve6(PLATFORM_ROOT4, "plugins/contacts/mcp/dist/index.js")],
|
|
7391
|
-
env: {
|
|
7516
|
+
env: { ...baseEnv }
|
|
7392
7517
|
},
|
|
7393
7518
|
"whatsapp": {
|
|
7394
7519
|
command: "node",
|
|
7395
7520
|
args: [resolve6(PLATFORM_ROOT4, "plugins/whatsapp/mcp/dist/index.js")],
|
|
7396
|
-
env: {
|
|
7521
|
+
env: { ...baseEnv, PLATFORM_PORT: process.env.PORT ?? "19200" }
|
|
7397
7522
|
},
|
|
7398
7523
|
"admin": {
|
|
7399
7524
|
command: "node",
|
|
7400
7525
|
args: [resolve6(PLATFORM_ROOT4, "plugins/admin/mcp/dist/index.js")],
|
|
7401
|
-
env: {
|
|
7526
|
+
env: { ...baseEnv, PLATFORM_PORT: process.env.PORT ?? "19200", ...userId ? { USER_ID: userId } : {} }
|
|
7402
7527
|
},
|
|
7403
7528
|
"scheduling": {
|
|
7404
7529
|
command: "node",
|
|
7405
7530
|
args: [resolve6(PLATFORM_ROOT4, "plugins/scheduling/mcp/dist/index.js")],
|
|
7406
|
-
env: {
|
|
7531
|
+
env: { ...baseEnv }
|
|
7407
7532
|
},
|
|
7408
7533
|
"tasks": {
|
|
7409
7534
|
command: "node",
|
|
7410
7535
|
args: [resolve6(PLATFORM_ROOT4, "plugins/tasks/mcp/dist/index.js")],
|
|
7411
|
-
env: {
|
|
7536
|
+
env: { ...baseEnv }
|
|
7412
7537
|
},
|
|
7413
7538
|
"email": {
|
|
7414
7539
|
command: "node",
|
|
7415
7540
|
args: [resolve6(PLATFORM_ROOT4, "plugins/email/mcp/dist/index.js")],
|
|
7416
|
-
env: {
|
|
7541
|
+
env: { ...baseEnv }
|
|
7417
7542
|
},
|
|
7418
7543
|
// Playwright MCP server — browser automation for browser-specialist.
|
|
7419
7544
|
// Key matches Claude Code's plugin naming: plugin_{plugin}_{server} so tools
|
|
@@ -7432,7 +7557,7 @@ function getMcpServers(accountId, userId, enabledPlugins) {
|
|
|
7432
7557
|
servers["telegram"] = {
|
|
7433
7558
|
command: "node",
|
|
7434
7559
|
args: [resolve6(PLATFORM_ROOT4, "plugins/telegram/mcp/dist/index.js")],
|
|
7435
|
-
env: {
|
|
7560
|
+
env: { ...baseEnv, TELEGRAM_BOT_TOKEN: tgBotToken }
|
|
7436
7561
|
};
|
|
7437
7562
|
} else {
|
|
7438
7563
|
console.error("[plugins] telegram MCP: skipped (no bot token in account.json telegram config)");
|
|
@@ -7440,7 +7565,7 @@ function getMcpServers(accountId, userId, enabledPlugins) {
|
|
|
7440
7565
|
servers["cloudflare"] = {
|
|
7441
7566
|
command: "node",
|
|
7442
7567
|
args: [resolve6(PLATFORM_ROOT4, "plugins/cloudflare/mcp/dist/index.js")],
|
|
7443
|
-
env: {
|
|
7568
|
+
env: { ...baseEnv, PLATFORM_PORT: process.env.PORT ?? "19200" }
|
|
7444
7569
|
};
|
|
7445
7570
|
if (Array.isArray(enabledPlugins) && enabledPlugins.length > 0) {
|
|
7446
7571
|
const pluginsDir = resolve6(PLATFORM_ROOT4, "plugins");
|
|
@@ -7471,7 +7596,7 @@ function getMcpServers(accountId, userId, enabledPlugins) {
|
|
|
7471
7596
|
servers[dir] = {
|
|
7472
7597
|
command: "node",
|
|
7473
7598
|
args: [mcpEntry],
|
|
7474
|
-
env: {
|
|
7599
|
+
env: { ...baseEnv }
|
|
7475
7600
|
};
|
|
7476
7601
|
console.log(`[plugins] optional MCP server started: ${dir}`);
|
|
7477
7602
|
}
|
|
@@ -7546,9 +7671,10 @@ var ADMIN_CORE_TOOLS = [
|
|
|
7546
7671
|
"mcp__admin__action-reject",
|
|
7547
7672
|
"mcp__admin__action-edit",
|
|
7548
7673
|
"mcp__cloudflare__cloudflare-setup",
|
|
7549
|
-
"mcp__cloudflare__cf-set-token",
|
|
7550
7674
|
"mcp__cloudflare__cf-add-zone",
|
|
7551
7675
|
"mcp__cloudflare__cf-zone-status",
|
|
7676
|
+
"mcp__cloudflare__cf-verify",
|
|
7677
|
+
"mcp__cloudflare__cf-rebuild",
|
|
7552
7678
|
"mcp__cloudflare__tunnel-status",
|
|
7553
7679
|
"mcp__cloudflare__tunnel-install",
|
|
7554
7680
|
"mcp__cloudflare__tunnel-login",
|
|
@@ -8123,10 +8249,10 @@ var COMPACTION_PROMPT = `You are about to reach your context limit. Call session
|
|
|
8123
8249
|
|
|
8124
8250
|
Then respond with only: [COMPACTED]`;
|
|
8125
8251
|
var COMPACTION_TIMEOUT_MS = 45e3;
|
|
8126
|
-
async function* runCompactionTurn(accountDir, accountId, systemPrompt, resumeSessionId, adminModel, enabledPlugins) {
|
|
8127
|
-
const mcpConfig = JSON.stringify({ mcpServers: getMcpServers(accountId, void 0, enabledPlugins) });
|
|
8252
|
+
async function* runCompactionTurn(accountDir, accountId, systemPrompt, resumeSessionId, adminModel, conversationId, enabledPlugins) {
|
|
8253
|
+
const mcpConfig = JSON.stringify({ mcpServers: getMcpServers(accountId, conversationId, void 0, enabledPlugins) });
|
|
8128
8254
|
const specialistsDir = resolve6(accountDir, "specialists");
|
|
8129
|
-
if (!existsSync6(specialistsDir)) agentLogStream("claude-agent-compaction-stream", accountDir).write(`[${isoTs()}] [warn] specialists plugin dir missing: ${specialistsDir}
|
|
8255
|
+
if (!existsSync6(specialistsDir)) agentLogStream("claude-agent-compaction-stream", accountDir, conversationId).write(`[${isoTs()}] [warn] specialists plugin dir missing: ${specialistsDir}
|
|
8130
8256
|
`);
|
|
8131
8257
|
const args = [
|
|
8132
8258
|
"--print",
|
|
@@ -8155,15 +8281,23 @@ async function* runCompactionTurn(accountDir, accountId, systemPrompt, resumeSes
|
|
|
8155
8281
|
const proc = spawn2("claude", args, {
|
|
8156
8282
|
cwd: accountDir,
|
|
8157
8283
|
stdio: ["ignore", "pipe", "pipe"],
|
|
8158
|
-
env: {
|
|
8284
|
+
env: {
|
|
8285
|
+
...process.env,
|
|
8286
|
+
PLATFORM_ROOT: PLATFORM_ROOT4,
|
|
8287
|
+
ACCOUNT_DIR: accountDir,
|
|
8288
|
+
// Task 532: network traces on the compaction subprocess too — its tool
|
|
8289
|
+
// calls are part of the same conversation's record.
|
|
8290
|
+
NODE_DEBUG: "http,http2,net,tls,undici,dns"
|
|
8291
|
+
}
|
|
8159
8292
|
});
|
|
8160
|
-
const stderrLog = agentLogStream("claude-agent-compaction-stderr", accountDir);
|
|
8293
|
+
const stderrLog = agentLogStream("claude-agent-compaction-stderr", accountDir, conversationId);
|
|
8161
8294
|
stderrLog.on("error", () => {
|
|
8162
8295
|
});
|
|
8163
8296
|
proc.stderr?.pipe(stderrLog);
|
|
8164
|
-
const streamLog = agentLogStream("claude-agent-compaction-stream", accountDir);
|
|
8297
|
+
const streamLog = agentLogStream("claude-agent-compaction-stream", accountDir, conversationId);
|
|
8165
8298
|
streamLog.on("error", () => {
|
|
8166
8299
|
});
|
|
8300
|
+
teeProcStderrToStreamLog(proc, streamLog);
|
|
8167
8301
|
streamLog.write(`[${isoTs()}] [compaction-start] resumeSessionId=${resumeSessionId}
|
|
8168
8302
|
`);
|
|
8169
8303
|
proc.on("error", (err) => {
|
|
@@ -8271,6 +8405,83 @@ async function* parseClaudeStream(proc, streamLog, adminModel, conversationId) {
|
|
|
8271
8405
|
apiWaitTimer = void 0;
|
|
8272
8406
|
}
|
|
8273
8407
|
}
|
|
8408
|
+
const inflightTools = /* @__PURE__ */ new Map();
|
|
8409
|
+
const TOOL_WAIT_TICK_MS = 5e3;
|
|
8410
|
+
const TOOL_WAIT_DIAG_THRESHOLDS_SEC = [15, 30, 45, 60];
|
|
8411
|
+
const TOOL_WAIT_PROC_SEC = 30;
|
|
8412
|
+
const procPid = proc.pid;
|
|
8413
|
+
const toolWaitTimer = setInterval(() => {
|
|
8414
|
+
if (streamLog.destroyed) return;
|
|
8415
|
+
const now = Date.now();
|
|
8416
|
+
for (const [toolUseId, info] of inflightTools) {
|
|
8417
|
+
const elapsedSec = Math.round((now - info.startedAt) / 1e3);
|
|
8418
|
+
streamLog.write(`[${isoTs()}] [tool-wait]${convIdTag} name=${info.name} tool_use_id=${toolUseId} elapsed=${elapsedSec}s
|
|
8419
|
+
`);
|
|
8420
|
+
let nextThreshold = 0;
|
|
8421
|
+
for (const t of TOOL_WAIT_DIAG_THRESHOLDS_SEC) {
|
|
8422
|
+
if (elapsedSec >= t && t > info.lastProbeThresholdSec) nextThreshold = t;
|
|
8423
|
+
}
|
|
8424
|
+
if (nextThreshold > 0) {
|
|
8425
|
+
if (info.probeInFlight) {
|
|
8426
|
+
streamLog.write(`[${isoTs()}] [tool-wait-diag-skip]${convIdTag} tool_use_id=${toolUseId} reason=probe-in-flight threshold=${nextThreshold}s
|
|
8427
|
+
`);
|
|
8428
|
+
} else {
|
|
8429
|
+
info.lastProbeThresholdSec = nextThreshold;
|
|
8430
|
+
info.probeInFlight = true;
|
|
8431
|
+
const toolInput = toolIdToInput.get(toolUseId);
|
|
8432
|
+
runFailureDiagnostic(info.name, toolInput).then((diag) => {
|
|
8433
|
+
if (!streamLog.destroyed && !streamLog.writableEnded) {
|
|
8434
|
+
streamLog.write(`[${isoTs()}] [tool-wait-diag]${convIdTag} name=${info.name} tool_use_id=${toolUseId} elapsed=${elapsedSec}s ${diag}
|
|
8435
|
+
`);
|
|
8436
|
+
}
|
|
8437
|
+
}).catch((err) => {
|
|
8438
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
8439
|
+
if (!streamLog.destroyed && !streamLog.writableEnded) {
|
|
8440
|
+
streamLog.write(`[${isoTs()}] [tool-wait-diag]${convIdTag} name=${info.name} tool_use_id=${toolUseId} elapsed=${elapsedSec}s diag_err=${JSON.stringify(msg.slice(0, 80))}
|
|
8441
|
+
`);
|
|
8442
|
+
}
|
|
8443
|
+
}).finally(() => {
|
|
8444
|
+
const current = inflightTools.get(toolUseId);
|
|
8445
|
+
if (current) current.probeInFlight = false;
|
|
8446
|
+
});
|
|
8447
|
+
}
|
|
8448
|
+
}
|
|
8449
|
+
if (!info.procSampleEmitted && elapsedSec >= TOOL_WAIT_PROC_SEC) {
|
|
8450
|
+
info.procSampleEmitted = true;
|
|
8451
|
+
if (!procPid) {
|
|
8452
|
+
streamLog.write(`[${isoTs()}] [tool-wait-proc-skip]${convIdTag} tool_use_id=${toolUseId} reason=no-pid
|
|
8453
|
+
`);
|
|
8454
|
+
} else {
|
|
8455
|
+
const procLine = sampleProcState(procPid);
|
|
8456
|
+
streamLog.write(`[${isoTs()}] [tool-wait-proc]${convIdTag} name=${info.name} pid=${procPid} tool_use_id=${toolUseId} elapsed=${elapsedSec}s ${procLine}
|
|
8457
|
+
`);
|
|
8458
|
+
}
|
|
8459
|
+
}
|
|
8460
|
+
}
|
|
8461
|
+
}, TOOL_WAIT_TICK_MS);
|
|
8462
|
+
function trackToolUse(toolUseId, toolName) {
|
|
8463
|
+
if (!toolUseId) {
|
|
8464
|
+
streamLog.write(`[${isoTs()}] [tool-use-no-id]${convIdTag} name=${toolName} reason=missing-tool-use-id-cannot-track-wait
|
|
8465
|
+
`);
|
|
8466
|
+
return;
|
|
8467
|
+
}
|
|
8468
|
+
inflightTools.set(toolUseId, {
|
|
8469
|
+
name: toolName,
|
|
8470
|
+
startedAt: Date.now(),
|
|
8471
|
+
lastProbeThresholdSec: 0,
|
|
8472
|
+
probeInFlight: false,
|
|
8473
|
+
procSampleEmitted: false
|
|
8474
|
+
});
|
|
8475
|
+
}
|
|
8476
|
+
function untrackToolUse(toolUseId, reason) {
|
|
8477
|
+
if (!toolUseId) return;
|
|
8478
|
+
if (!inflightTools.delete(toolUseId)) {
|
|
8479
|
+
if (reason === "result") {
|
|
8480
|
+
streamLog.write(`[${isoTs()}] [tool-wait-late]${convIdTag} tool_use_id=${toolUseId} reason=result-without-matching-tool-use
|
|
8481
|
+
`);
|
|
8482
|
+
}
|
|
8483
|
+
}
|
|
8484
|
+
}
|
|
8274
8485
|
const readLines = async function* () {
|
|
8275
8486
|
for await (const chunk of proc.stdout) {
|
|
8276
8487
|
buffer += chunk.toString();
|
|
@@ -8449,6 +8660,7 @@ async function* parseClaudeStream(proc, streamLog, adminModel, conversationId) {
|
|
|
8449
8660
|
vncLog("mcp-tool", { name: block.name, input_preview: inputPreview });
|
|
8450
8661
|
}
|
|
8451
8662
|
}
|
|
8663
|
+
trackToolUse(block.id, block.name);
|
|
8452
8664
|
yield { type: "tool_use", name: block.name, input: block.input ?? {}, toolUseId: block.id };
|
|
8453
8665
|
}
|
|
8454
8666
|
}
|
|
@@ -8502,9 +8714,13 @@ async function* parseClaudeStream(proc, streamLog, adminModel, conversationId) {
|
|
|
8502
8714
|
`);
|
|
8503
8715
|
}
|
|
8504
8716
|
}
|
|
8717
|
+
} else if (block.is_error && !block.tool_use_id) {
|
|
8718
|
+
streamLog.write(`[${isoTs()}] [tool-failure-diag-skip]${convIdTag} name=${name} reason=no-tool-use-id-cannot-lookup-input
|
|
8719
|
+
`);
|
|
8505
8720
|
}
|
|
8506
8721
|
}
|
|
8507
8722
|
if (block.tool_use_id) toolIdToInput.delete(block.tool_use_id);
|
|
8723
|
+
untrackToolUse(block.tool_use_id, "result");
|
|
8508
8724
|
const renderDirectiveRe = /^__RENDER__(\{.+\})$/gm;
|
|
8509
8725
|
let renderMatch;
|
|
8510
8726
|
while ((renderMatch = renderDirectiveRe.exec(output)) !== null) {
|
|
@@ -8636,6 +8852,19 @@ async function* parseClaudeStream(proc, streamLog, adminModel, conversationId) {
|
|
|
8636
8852
|
}
|
|
8637
8853
|
} finally {
|
|
8638
8854
|
endApiWait();
|
|
8855
|
+
clearInterval(toolWaitTimer);
|
|
8856
|
+
for (const [toolUseId, info] of inflightTools) {
|
|
8857
|
+
const elapsedSec = Math.round((Date.now() - info.startedAt) / 1e3);
|
|
8858
|
+
if (!streamLog.destroyed && !streamLog.writableEnded) {
|
|
8859
|
+
streamLog.write(`[${isoTs()}] [tool-wait-late]${convIdTag} name=${info.name} tool_use_id=${toolUseId} elapsed=${elapsedSec}s reason=stream-ended-without-tool-result
|
|
8860
|
+
`);
|
|
8861
|
+
}
|
|
8862
|
+
}
|
|
8863
|
+
inflightTools.clear();
|
|
8864
|
+
if (buffer.length > 0 && !streamLog.destroyed && !streamLog.writableEnded) {
|
|
8865
|
+
streamLog.write(`[${isoTs()}] [stream-buffer-flush]${convIdTag} bytes=${buffer.length} reason=process-exit preview=${JSON.stringify(buffer.slice(0, 120))}
|
|
8866
|
+
`);
|
|
8867
|
+
}
|
|
8639
8868
|
}
|
|
8640
8869
|
if (!emittedDone) {
|
|
8641
8870
|
}
|
|
@@ -8895,17 +9124,21 @@ function buildAttachmentMetaText(attachments) {
|
|
|
8895
9124
|
async function* invokeAdminAgent(message, systemPrompt, accountDir, accountId, adminModel, sessionKey, maxTurns = 20, attachments = [], retryCount = 0, enabledPlugins, clientTimestamp) {
|
|
8896
9125
|
const userTimestamp = clientTimestamp ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
8897
9126
|
const resumeSessionId = sessionKey ? getAgentSessionId(sessionKey) : void 0;
|
|
9127
|
+
const spawnConvId = sessionKey ? getConversationIdForSession(sessionKey) : void 0;
|
|
9128
|
+
if (!spawnConvId) {
|
|
9129
|
+
throw new Error(`invokeAdminAgent: conversationId missing for sessionKey=${sessionKey?.slice(0, 8) ?? "none"} \u2014 ensureConversation must run before invoking the agent`);
|
|
9130
|
+
}
|
|
8898
9131
|
const cdpOk = await ensureCdp();
|
|
8899
9132
|
if (!cdpOk) {
|
|
8900
|
-
const cdpLog = agentLogStream("claude-agent-stream", accountDir);
|
|
9133
|
+
const cdpLog = agentLogStream("claude-agent-stream", accountDir, spawnConvId);
|
|
8901
9134
|
cdpLog.write(`[${isoTs()}] [warn] ensureCdp failed \u2014 browser-specialist degraded
|
|
8902
9135
|
`);
|
|
8903
9136
|
cdpLog.end();
|
|
8904
9137
|
}
|
|
8905
9138
|
const ccUserId = sessionKey ? getUserIdForSession(sessionKey) : void 0;
|
|
8906
|
-
const mcpConfig = JSON.stringify({ mcpServers: getMcpServers(accountId, ccUserId, enabledPlugins) });
|
|
9139
|
+
const mcpConfig = JSON.stringify({ mcpServers: getMcpServers(accountId, spawnConvId, ccUserId, enabledPlugins) });
|
|
8907
9140
|
const specialistsDir = resolve6(accountDir, "specialists");
|
|
8908
|
-
if (!existsSync6(specialistsDir)) agentLogStream("claude-agent-stream", accountDir).write(`[${isoTs()}] [warn] specialists plugin dir missing: ${specialistsDir}
|
|
9141
|
+
if (!existsSync6(specialistsDir)) agentLogStream("claude-agent-stream", accountDir, spawnConvId).write(`[${isoTs()}] [warn] specialists plugin dir missing: ${specialistsDir}
|
|
8909
9142
|
`);
|
|
8910
9143
|
const args = [
|
|
8911
9144
|
"--print",
|
|
@@ -8935,15 +9168,24 @@ async function* invokeAdminAgent(message, systemPrompt, accountDir, accountId, a
|
|
|
8935
9168
|
const proc = spawn2("claude", args, {
|
|
8936
9169
|
cwd: accountDir,
|
|
8937
9170
|
stdio: ["ignore", "pipe", "pipe"],
|
|
8938
|
-
env: {
|
|
9171
|
+
env: {
|
|
9172
|
+
...process.env,
|
|
9173
|
+
PLATFORM_ROOT: PLATFORM_ROOT4,
|
|
9174
|
+
ACCOUNT_DIR: accountDir,
|
|
9175
|
+
// Task 532: tee subprocess network activity into the per-conversation
|
|
9176
|
+
// stream log via the existing stderr pipe. Closes the Claude Code
|
|
9177
|
+
// "what was the tool actually doing?" black box during tool waits.
|
|
9178
|
+
NODE_DEBUG: "http,http2,net,tls,undici,dns"
|
|
9179
|
+
}
|
|
8939
9180
|
});
|
|
8940
|
-
const stderrLog = agentLogStream("claude-agent-stderr", accountDir);
|
|
9181
|
+
const stderrLog = agentLogStream("claude-agent-stderr", accountDir, spawnConvId);
|
|
8941
9182
|
stderrLog.on("error", () => {
|
|
8942
9183
|
});
|
|
8943
9184
|
proc.stderr?.pipe(stderrLog);
|
|
8944
|
-
const streamLog = agentLogStream("claude-agent-stream", accountDir);
|
|
9185
|
+
const streamLog = agentLogStream("claude-agent-stream", accountDir, spawnConvId);
|
|
8945
9186
|
streamLog.on("error", () => {
|
|
8946
9187
|
});
|
|
9188
|
+
teeProcStderrToStreamLog(proc, streamLog);
|
|
8947
9189
|
if (sessionKey) {
|
|
8948
9190
|
const prev = activeProcesses.get(sessionKey);
|
|
8949
9191
|
if (prev) {
|
|
@@ -8952,8 +9194,7 @@ async function* invokeAdminAgent(message, systemPrompt, accountDir, accountId, a
|
|
|
8952
9194
|
}
|
|
8953
9195
|
activeProcesses.set(sessionKey, { pid: proc.pid, spawnedAt: Date.now() });
|
|
8954
9196
|
}
|
|
8955
|
-
|
|
8956
|
-
streamLog.write(`[${isoTs()}] [spawn] pid=${proc.pid} resume=${resumeSessionId ?? "none"} sessionKey=${sessionKey ?? "none"} conversationId=${spawnConvId ?? "none"} pluginDir=${specialistsDir}
|
|
9197
|
+
streamLog.write(`[${isoTs()}] [spawn] pid=${proc.pid} resume=${resumeSessionId ?? "none"} sessionKey=${sessionKey ?? "none"} conversationId=${spawnConvId} pluginDir=${specialistsDir}
|
|
8957
9198
|
`);
|
|
8958
9199
|
streamLog.write(`[${isoTs()}] [stdin] len=${fullMessage.length} preview=${JSON.stringify(fullMessage.slice(0, 80))}
|
|
8959
9200
|
`);
|
|
@@ -9026,7 +9267,7 @@ async function* invokeAdminAgent(message, systemPrompt, accountDir, accountId, a
|
|
|
9026
9267
|
if (event.type === "usage" && sessionKey && currentAgentSessionId) {
|
|
9027
9268
|
const peakReqPct = event.peak_request_pct ?? 0;
|
|
9028
9269
|
if (peakReqPct >= COMPACTION_THRESHOLD) {
|
|
9029
|
-
const compactionIter = runCompactionTurn(accountDir, accountId, systemPrompt, currentAgentSessionId, adminModel, enabledPlugins);
|
|
9270
|
+
const compactionIter = runCompactionTurn(accountDir, accountId, systemPrompt, currentAgentSessionId, adminModel, spawnConvId, enabledPlugins);
|
|
9030
9271
|
let step = await compactionIter.next();
|
|
9031
9272
|
while (!step.done) {
|
|
9032
9273
|
yield { type: "status", message: step.value };
|
|
@@ -9197,6 +9438,10 @@ ${summary}`;
|
|
|
9197
9438
|
}
|
|
9198
9439
|
async function* invokeManagedAdminAgent(message, systemPrompt, accountDir, accountId, adminModel, sessionKey, maxTurns = 20, attachments = [], retryCount = 0, enabledPlugins, clientTimestamp) {
|
|
9199
9440
|
const userTimestamp = clientTimestamp ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
9441
|
+
const managedConvId = getConversationIdForSession(sessionKey);
|
|
9442
|
+
if (!managedConvId) {
|
|
9443
|
+
throw new Error(`invokeManagedAdminAgent: conversationId missing for sessionKey=${sessionKey.slice(0, 8)} \u2014 ensureConversation must run first`);
|
|
9444
|
+
}
|
|
9200
9445
|
const pendingTrimmed = consumePendingTrimmedMessages(sessionKey);
|
|
9201
9446
|
if (pendingTrimmed && pendingTrimmed.length > 0) {
|
|
9202
9447
|
const ok = await compactTrimmedMessages(accountId, pendingTrimmed);
|
|
@@ -9204,7 +9449,7 @@ async function* invokeManagedAdminAgent(message, systemPrompt, accountDir, accou
|
|
|
9204
9449
|
storePendingTrimmedMessages(sessionKey, pendingTrimmed);
|
|
9205
9450
|
}
|
|
9206
9451
|
}
|
|
9207
|
-
const streamLog = agentLogStream("claude-agent-stream", accountDir);
|
|
9452
|
+
const streamLog = agentLogStream("claude-agent-stream", accountDir, managedConvId);
|
|
9208
9453
|
streamLog.on("error", () => {
|
|
9209
9454
|
});
|
|
9210
9455
|
const systemPromptTokens = estimateTokens(systemPrompt);
|
|
@@ -9235,7 +9480,7 @@ async function* invokeManagedAdminAgent(message, systemPrompt, accountDir, accou
|
|
|
9235
9480
|
if (!cdpOk) streamLog.write(`[${isoTs()}] [warn] ensureCdp failed \u2014 browser-specialist degraded
|
|
9236
9481
|
`);
|
|
9237
9482
|
const managedUserId = getUserIdForSession(sessionKey);
|
|
9238
|
-
const mcpConfig = JSON.stringify({ mcpServers: getMcpServers(accountId, managedUserId, enabledPlugins) });
|
|
9483
|
+
const mcpConfig = JSON.stringify({ mcpServers: getMcpServers(accountId, managedConvId, managedUserId, enabledPlugins) });
|
|
9239
9484
|
const specialistsDir = resolve6(accountDir, "specialists");
|
|
9240
9485
|
if (!existsSync6(specialistsDir)) streamLog.write(`[${isoTs()}] [warn] specialists plugin dir missing: ${specialistsDir}
|
|
9241
9486
|
`);
|
|
@@ -9265,12 +9510,20 @@ async function* invokeManagedAdminAgent(message, systemPrompt, accountDir, accou
|
|
|
9265
9510
|
const proc = spawn2("claude", args, {
|
|
9266
9511
|
cwd: accountDir,
|
|
9267
9512
|
stdio: ["ignore", "pipe", "pipe"],
|
|
9268
|
-
env: {
|
|
9513
|
+
env: {
|
|
9514
|
+
...process.env,
|
|
9515
|
+
PLATFORM_ROOT: PLATFORM_ROOT4,
|
|
9516
|
+
ACCOUNT_DIR: accountDir,
|
|
9517
|
+
// Task 532: tee subprocess network traces into the per-conversation
|
|
9518
|
+
// stream log via the stderr pipe + dual-consume listener.
|
|
9519
|
+
NODE_DEBUG: "http,http2,net,tls,undici,dns"
|
|
9520
|
+
}
|
|
9269
9521
|
});
|
|
9270
|
-
const stderrLog = agentLogStream("claude-agent-stderr", accountDir);
|
|
9522
|
+
const stderrLog = agentLogStream("claude-agent-stderr", accountDir, managedConvId);
|
|
9271
9523
|
stderrLog.on("error", () => {
|
|
9272
9524
|
});
|
|
9273
9525
|
proc.stderr?.pipe(stderrLog);
|
|
9526
|
+
teeProcStderrToStreamLog(proc, streamLog);
|
|
9274
9527
|
if (sessionKey) {
|
|
9275
9528
|
const prev = activeProcesses.get(sessionKey);
|
|
9276
9529
|
if (prev) {
|
|
@@ -9279,14 +9532,13 @@ async function* invokeManagedAdminAgent(message, systemPrompt, accountDir, accou
|
|
|
9279
9532
|
}
|
|
9280
9533
|
activeProcesses.set(sessionKey, { pid: proc.pid, spawnedAt: Date.now() });
|
|
9281
9534
|
}
|
|
9282
|
-
|
|
9283
|
-
streamLog.write(`[${isoTs()}] [managed-spawn] pid=${proc.pid} sessionKey=${sessionKey} conversationId=${managedConvId ?? "none"} historyMessages=${history.length} pluginDir=${specialistsDir}
|
|
9535
|
+
streamLog.write(`[${isoTs()}] [managed-spawn] pid=${proc.pid} sessionKey=${sessionKey} conversationId=${managedConvId} historyMessages=${history.length} pluginDir=${specialistsDir}
|
|
9284
9536
|
`);
|
|
9285
9537
|
streamLog.write(`[${isoTs()}] [stdin] len=${fullMessage.length} preview=${JSON.stringify(fullMessage.slice(0, 80))}
|
|
9286
9538
|
`);
|
|
9287
9539
|
proc.on("exit", (code, signal) => {
|
|
9288
|
-
console.log(`[process-exit] pid=${proc.pid} code=${code} signal=${signal} sessionKey=${sessionKey ?? "none"} conversationId=${managedConvId
|
|
9289
|
-
if (!streamLog.destroyed && !streamLog.writableEnded) streamLog.write(`[${isoTs()}] [process-exit] pid=${proc.pid} code=${code} signal=${signal} conversationId=${managedConvId
|
|
9540
|
+
console.log(`[process-exit] pid=${proc.pid} code=${code} signal=${signal} sessionKey=${sessionKey ?? "none"} conversationId=${managedConvId}`);
|
|
9541
|
+
if (!streamLog.destroyed && !streamLog.writableEnded) streamLog.write(`[${isoTs()}] [process-exit] pid=${proc.pid} code=${code} signal=${signal} conversationId=${managedConvId}
|
|
9290
9542
|
`);
|
|
9291
9543
|
if (sessionKey) activeProcesses.delete(sessionKey);
|
|
9292
9544
|
});
|
|
@@ -9507,7 +9759,11 @@ async function* invokePublicAgent(message, systemPrompt, accountId, accountDir,
|
|
|
9507
9759
|
yield { type: "done" };
|
|
9508
9760
|
return;
|
|
9509
9761
|
}
|
|
9510
|
-
const
|
|
9762
|
+
const publicConvId = sessionKey ? getConversationIdForSession(sessionKey) : void 0;
|
|
9763
|
+
if (!publicConvId) {
|
|
9764
|
+
throw new Error(`invokePublicAgent: conversationId missing for sessionKey=${sessionKey?.slice(0, 8) ?? "none"} \u2014 ensureConversation must run first`);
|
|
9765
|
+
}
|
|
9766
|
+
const streamLog = agentLogStream("public-agent-stream", accountDir, publicConvId);
|
|
9511
9767
|
streamLog.write(`[${isoTs()}] [public-user-message] ${JSON.stringify(message)}
|
|
9512
9768
|
`);
|
|
9513
9769
|
if (sessionKey) {
|
|
@@ -9780,6 +10036,8 @@ async function* compactSession(sessionKey) {
|
|
|
9780
10036
|
}
|
|
9781
10037
|
const currentSessionId = getAgentSessionId(sessionKey);
|
|
9782
10038
|
if (!currentSessionId) return { ok: false, reason: "no-session" };
|
|
10039
|
+
const compactionConvId = getConversationIdForSession(sessionKey);
|
|
10040
|
+
if (!compactionConvId) return { ok: false, reason: "no-conversation" };
|
|
9783
10041
|
const identity = readIdentity(account.accountDir, "admin");
|
|
9784
10042
|
const defaultSystemPrompt = "You are an admin agent. Full access. Professional, concise. British English. Be proactive \u2014 tell the user what needs doing and do it.";
|
|
9785
10043
|
const baseSystemPrompt = identity ?? defaultSystemPrompt;
|
|
@@ -9787,7 +10045,7 @@ async function* compactSession(sessionKey) {
|
|
|
9787
10045
|
const systemPrompt = outputStyle === "explanatory" ? `${baseSystemPrompt}
|
|
9788
10046
|
|
|
9789
10047
|
${EXPLANATORY_STYLE_INSTRUCTIONS}` : baseSystemPrompt;
|
|
9790
|
-
const compactionIter = runCompactionTurn(account.accountDir, account.accountId, systemPrompt, currentSessionId, account.config.adminModel, account.config.enabledPlugins);
|
|
10048
|
+
const compactionIter = runCompactionTurn(account.accountDir, account.accountId, systemPrompt, currentSessionId, account.config.adminModel, compactionConvId, account.config.enabledPlugins);
|
|
9791
10049
|
let step = await compactionIter.next();
|
|
9792
10050
|
while (!step.done) {
|
|
9793
10051
|
yield step.value;
|
|
@@ -10204,6 +10462,38 @@ function defaultRules() {
|
|
|
10204
10462
|
thresholdWindowMinutes: 5,
|
|
10205
10463
|
scope: "session",
|
|
10206
10464
|
suggestedAction: 'The same conversation has logged multiple tool failures. Use the admin `logs-read` MCP tool with `type: "system"` (or the `logs-read.sh` script with the conversationId) to retrieve the adjacent [tool-failure-diag] lines and identify whether the cause is DNS, TCP, HTTP, or the tool\'s internal pipeline \u2014 then adapt the next attempt to match. Never retry the same tool against the same target without diagnostic-grounded reasoning.'
|
|
10465
|
+
},
|
|
10466
|
+
{
|
|
10467
|
+
// Task 532: closes the "60-second black-box tool wait" class. Fires when
|
|
10468
|
+
// a tool hits the 30-second mark of the mid-flight heartbeat. The
|
|
10469
|
+
// pattern anchors on elapsed=30s specifically (the tool-wait tick emits
|
|
10470
|
+
// one line per 5s per tool, so matching every tick would be noisy) and
|
|
10471
|
+
// excludes tools whose long runtime is expected: `Task`/`Agent` subagent
|
|
10472
|
+
// dispatch, `Bash` with explicit long timeouts.
|
|
10473
|
+
id: "tool-wait-long-stall",
|
|
10474
|
+
name: "Tool wait exceeds 30 seconds (possible stall)",
|
|
10475
|
+
type: "repeated-error",
|
|
10476
|
+
logSource: "system",
|
|
10477
|
+
pattern: "\\[tool-wait\\][^\\n]*name=(?!Task\\b|Agent\\b|Bash\\b)[A-Za-z0-9_]+[^\\n]*elapsed=30s",
|
|
10478
|
+
thresholdCount: 0,
|
|
10479
|
+
thresholdWindowMinutes: 0,
|
|
10480
|
+
scope: "session",
|
|
10481
|
+
suggestedAction: "A tool call has been pending for 30 seconds without a result. Read the adjacent [tool-wait-diag] and [tool-wait-proc] lines in the conversation's stream log to determine whether the network remained healthy, the subprocess held active sockets, and the HTTP request reached the wire. If diag shows a healthy network but the subprocess has no [subproc-stderr] UNDICI/HTTP activity during the wait window, the tool's internal pipeline is stalled \u2014 do not retry the same request against the same target without a change in approach."
|
|
10482
|
+
},
|
|
10483
|
+
{
|
|
10484
|
+
// Task 533: surface every Cloudflare-plugin refusal. The plugin emits
|
|
10485
|
+
// exactly one [cloudflare:refuse] line per refusal with a structured
|
|
10486
|
+
// reason field; any single occurrence on a previously-clean device
|
|
10487
|
+
// means scope, account-binding, or post-flight FQDN drift — the
|
|
10488
|
+
// operator should run cf-verify before acting.
|
|
10489
|
+
id: "cloudflare-refuse",
|
|
10490
|
+
name: "Cloudflare plugin refusal",
|
|
10491
|
+
type: "silent-catch",
|
|
10492
|
+
logSource: "any",
|
|
10493
|
+
pattern: "\\[cloudflare:refuse\\]|\\[cloudflare:post-flight-mismatch\\]",
|
|
10494
|
+
thresholdCount: 0,
|
|
10495
|
+
thresholdWindowMinutes: 0,
|
|
10496
|
+
suggestedAction: "The Cloudflare plugin refused an operation or detected a post-flight FQDN mismatch. Run `cf-verify` to inspect current device state. If the reason is `account-drift` or `unbound-device`, run `tunnel-login force=true` to clear cert + binding and re-authenticate. If the reason is `scope-mismatch`, the requested hostname is outside the brand's declared zones \u2014 fix at brand.json + republish. If `post-flight-fqdn-mismatch`, run `cf-rebuild` to reconcile."
|
|
10207
10497
|
}
|
|
10208
10498
|
];
|
|
10209
10499
|
}
|
|
@@ -10419,12 +10709,31 @@ function discoverSourceFiles(configDir2, accountLogDir2, logicalSource) {
|
|
|
10419
10709
|
}[logicalSource];
|
|
10420
10710
|
if (!existsSync8(accountLogDir2)) return [];
|
|
10421
10711
|
const files = [];
|
|
10712
|
+
let scanned = 0;
|
|
10713
|
+
let skippedPrefixMismatch = 0;
|
|
10714
|
+
let skippedNotLog = 0;
|
|
10422
10715
|
for (const entry of readdirSync3(accountLogDir2)) {
|
|
10423
|
-
|
|
10716
|
+
scanned += 1;
|
|
10717
|
+
const matchesPrefix = entry.startsWith(prefix);
|
|
10718
|
+
const isLog = entry.endsWith(".log");
|
|
10719
|
+
if (matchesPrefix && isLog) {
|
|
10424
10720
|
files.push({ logicalSource, filepath: join5(accountLogDir2, entry) });
|
|
10721
|
+
} else if (!matchesPrefix) {
|
|
10722
|
+
skippedPrefixMismatch += 1;
|
|
10723
|
+
} else {
|
|
10724
|
+
skippedNotLog += 1;
|
|
10425
10725
|
}
|
|
10426
10726
|
}
|
|
10427
|
-
files.sort((a, b) =>
|
|
10727
|
+
files.sort((a, b) => {
|
|
10728
|
+
try {
|
|
10729
|
+
return statSync5(b.filepath).mtimeMs - statSync5(a.filepath).mtimeMs;
|
|
10730
|
+
} catch {
|
|
10731
|
+
return a.filepath.localeCompare(b.filepath);
|
|
10732
|
+
}
|
|
10733
|
+
});
|
|
10734
|
+
if (skippedPrefixMismatch > 0 || skippedNotLog > 0) {
|
|
10735
|
+
console.error(`[review-scan-skip] dir=${accountLogDir2} source=${logicalSource} prefix=${prefix} scanned=${scanned} matched=${files.length} skipped_prefix=${skippedPrefixMismatch} skipped_non_log=${skippedNotLog}`);
|
|
10736
|
+
}
|
|
10428
10737
|
return files;
|
|
10429
10738
|
}
|
|
10430
10739
|
function discoverAllSources(configDir2, accountLogDir2) {
|
|
@@ -28308,8 +28617,9 @@ async function POST2(req) {
|
|
|
28308
28617
|
{ status: 500, headers: { "Content-Type": "application/json" } }
|
|
28309
28618
|
);
|
|
28310
28619
|
}
|
|
28311
|
-
const
|
|
28312
|
-
const
|
|
28620
|
+
const publicSseConvId = getConversationIdForSession(session_key);
|
|
28621
|
+
const sseLog = publicSseConvId ? agentLogStream("sse-events", account.accountDir, publicSseConvId) : preConversationLogStream("sse-events", account.accountDir);
|
|
28622
|
+
const sk = publicSseConvId?.slice(0, 8) ?? session_key.slice(0, 8);
|
|
28313
28623
|
const agentName = getAgentNameForSession(session_key);
|
|
28314
28624
|
if (!agentName) {
|
|
28315
28625
|
console.log(`[chat] no agent for session=${sk} \u2014 session expired or server restarted`);
|
|
@@ -30684,7 +30994,8 @@ async function POST21(req) {
|
|
|
30684
30994
|
try {
|
|
30685
30995
|
const parsed = JSON.parse(message);
|
|
30686
30996
|
if (parsed._lifecycle) {
|
|
30687
|
-
const
|
|
30997
|
+
const lifecycleConvId = getConversationIdForSession(session_key);
|
|
30998
|
+
const lifecycleLog = lifecycleConvId ? agentLogStream("component-lifecycle", account.accountDir, lifecycleConvId) : preConversationLogStream("component-lifecycle", account.accountDir);
|
|
30688
30999
|
const ts = (/* @__PURE__ */ new Date()).toISOString().slice(11, 23);
|
|
30689
31000
|
const detail = parsed.filePath ? ` filePath=${parsed.filePath}` : "";
|
|
30690
31001
|
lifecycleLog.write(`[${ts}] [component:${parsed.component ?? "unknown"}] ${parsed.event ?? "unknown"}${detail}
|
|
@@ -30708,7 +31019,8 @@ async function POST21(req) {
|
|
|
30708
31019
|
try {
|
|
30709
31020
|
const parsed = JSON.parse(message);
|
|
30710
31021
|
if (isComponentDone(parsed)) {
|
|
30711
|
-
const
|
|
31022
|
+
const componentConvId = getConversationIdForSession(session_key);
|
|
31023
|
+
const componentLog = componentConvId ? agentLogStream("component-lifecycle", account.accountDir, componentConvId) : preConversationLogStream("component-lifecycle", account.accountDir);
|
|
30712
31024
|
const ts = (/* @__PURE__ */ new Date()).toISOString().slice(11, 23);
|
|
30713
31025
|
message = transformComponentDone(parsed);
|
|
30714
31026
|
componentLog.write(`[${ts}] [component:${parsed.component}] componentDone \u2192 transformed
|
|
@@ -30736,8 +31048,9 @@ async function POST21(req) {
|
|
|
30736
31048
|
gatewayResult = await processInbound(message, "web-admin");
|
|
30737
31049
|
}
|
|
30738
31050
|
const encoder = new TextEncoder();
|
|
30739
|
-
const
|
|
30740
|
-
const
|
|
31051
|
+
const sseConvId = getConversationIdForSession(session_key);
|
|
31052
|
+
const sseLog = sseConvId ? agentLogStream("sse-events", account.accountDir, sseConvId) : preConversationLogStream("sse-events", account.accountDir);
|
|
31053
|
+
const sk = sseConvId?.slice(0, 8) ?? session_key.slice(0, 8);
|
|
30741
31054
|
const readable = new ReadableStream({
|
|
30742
31055
|
async start(controller) {
|
|
30743
31056
|
try {
|
|
@@ -30853,6 +31166,7 @@ async function GET9(request) {
|
|
|
30853
31166
|
const { searchParams } = new URL(request.url);
|
|
30854
31167
|
const fileParam = searchParams.get("file");
|
|
30855
31168
|
const typeParam = searchParams.get("type");
|
|
31169
|
+
const conversationIdParam = searchParams.get("conversationId");
|
|
30856
31170
|
const download = searchParams.get("download") === "1";
|
|
30857
31171
|
const account = resolveAccount();
|
|
30858
31172
|
const accountLogDir2 = account ? resolve19(account.accountDir, "logs") : null;
|
|
@@ -30872,18 +31186,21 @@ async function GET9(request) {
|
|
|
30872
31186
|
return Response.json({ error: `File not found: ${safe}` }, { status: 404 });
|
|
30873
31187
|
}
|
|
30874
31188
|
if (typeParam) {
|
|
30875
|
-
const
|
|
30876
|
-
|
|
30877
|
-
|
|
30878
|
-
|
|
30879
|
-
|
|
30880
|
-
|
|
30881
|
-
public: `public-agent-stream-${today}.log`
|
|
31189
|
+
const prefixMap = {
|
|
31190
|
+
stream: "claude-agent-stream",
|
|
31191
|
+
error: "claude-agent-stderr",
|
|
31192
|
+
session: "sse-events",
|
|
31193
|
+
sse: "sse-events",
|
|
31194
|
+
public: "public-agent-stream"
|
|
30882
31195
|
};
|
|
30883
|
-
const
|
|
30884
|
-
if (!
|
|
31196
|
+
const prefix = prefixMap[typeParam];
|
|
31197
|
+
if (!prefix) {
|
|
30885
31198
|
return Response.json({ error: `Unknown type: ${typeParam}. Valid: stream, error, session, sse, public` }, { status: 400 });
|
|
30886
31199
|
}
|
|
31200
|
+
if (!conversationIdParam) {
|
|
31201
|
+
return Response.json({ error: `type=${typeParam} requires conversationId (per-conversation log files, no daily fallback)` }, { status: 400 });
|
|
31202
|
+
}
|
|
31203
|
+
const fileName = `${prefix}-${conversationIdParam}.log`;
|
|
30887
31204
|
for (const dir of [accountLogDir2, LOG_DIR]) {
|
|
30888
31205
|
if (!dir) continue;
|
|
30889
31206
|
const filePath = resolve19(dir, fileName);
|
|
@@ -31589,7 +31906,7 @@ var brandLoginOpts = {
|
|
|
31589
31906
|
bodyFont: BRAND.defaultFonts?.body,
|
|
31590
31907
|
logoContainsName: !!BRAND.logoContainsName
|
|
31591
31908
|
};
|
|
31592
|
-
var ALIAS_DOMAINS_PATH = join13(
|
|
31909
|
+
var ALIAS_DOMAINS_PATH = join13(homedir4(), BRAND.configDir, "alias-domains.json");
|
|
31593
31910
|
function loadAliasDomains() {
|
|
31594
31911
|
try {
|
|
31595
31912
|
if (!existsSync26(ALIAS_DOMAINS_PATH)) return null;
|
|
@@ -32082,7 +32399,7 @@ function cachedHtml(file2) {
|
|
|
32082
32399
|
}
|
|
32083
32400
|
var brandedHtmlCache = /* @__PURE__ */ new Map();
|
|
32084
32401
|
function loadBrandingCache(agentSlug) {
|
|
32085
|
-
const configDir2 = join13(
|
|
32402
|
+
const configDir2 = join13(homedir4(), BRAND.configDir);
|
|
32086
32403
|
try {
|
|
32087
32404
|
const accountJsonPath = join13(configDir2, "account.json");
|
|
32088
32405
|
if (!existsSync26(accountJsonPath)) return null;
|
|
@@ -32098,7 +32415,7 @@ function loadBrandingCache(agentSlug) {
|
|
|
32098
32415
|
}
|
|
32099
32416
|
function resolveDefaultSlug() {
|
|
32100
32417
|
try {
|
|
32101
|
-
const configDir2 = join13(
|
|
32418
|
+
const configDir2 = join13(homedir4(), BRAND.configDir);
|
|
32102
32419
|
const accountJsonPath = join13(configDir2, "account.json");
|
|
32103
32420
|
if (!existsSync26(accountJsonPath)) return null;
|
|
32104
32421
|
const account = JSON.parse(readFileSync26(accountJsonPath, "utf-8"));
|