@rubytech/create-realagent 1.0.615 → 1.0.616
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/config/brand.json +4 -0
- 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/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 +196 -0
- package/payload/platform/plugins/cloudflare/mcp/__tests__/brand-load.test.ts +81 -0
- package/payload/platform/plugins/cloudflare/mcp/__tests__/manifest-scope.test.ts +65 -0
- package/payload/platform/plugins/cloudflare/mcp/__tests__/verify-scenario-0.test.ts +70 -0
- package/payload/platform/plugins/cloudflare/mcp/__tests__/verify-scenario-B.test.ts +124 -0
- package/payload/platform/plugins/cloudflare/mcp/dist/index.js +221 -200
- package/payload/platform/plugins/cloudflare/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/cloudflare/mcp/dist/lib/cloudflared.d.ts +174 -39
- package/payload/platform/plugins/cloudflare/mcp/dist/lib/cloudflared.d.ts.map +1 -1
- package/payload/platform/plugins/cloudflare/mcp/dist/lib/cloudflared.js +891 -194
- 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 +31 -32
- package/payload/platform/plugins/cloudflare/skills/setup-tunnel/SKILL.md +25 -3
- package/payload/platform/plugins/docs/PLUGIN.md +2 -0
- package/payload/platform/plugins/docs/references/cloudflare.md +68 -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 -71
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,13 +7671,13 @@ 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",
|
|
7555
|
-
"mcp__cloudflare__tunnel-create",
|
|
7556
7681
|
"mcp__cloudflare__tunnel-enable",
|
|
7557
7682
|
"mcp__cloudflare__tunnel-disable",
|
|
7558
7683
|
"mcp__cloudflare__tunnel-add-hostname",
|
|
@@ -8123,10 +8248,10 @@ var COMPACTION_PROMPT = `You are about to reach your context limit. Call session
|
|
|
8123
8248
|
|
|
8124
8249
|
Then respond with only: [COMPACTED]`;
|
|
8125
8250
|
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) });
|
|
8251
|
+
async function* runCompactionTurn(accountDir, accountId, systemPrompt, resumeSessionId, adminModel, conversationId, enabledPlugins) {
|
|
8252
|
+
const mcpConfig = JSON.stringify({ mcpServers: getMcpServers(accountId, conversationId, void 0, enabledPlugins) });
|
|
8128
8253
|
const specialistsDir = resolve6(accountDir, "specialists");
|
|
8129
|
-
if (!existsSync6(specialistsDir)) agentLogStream("claude-agent-compaction-stream", accountDir).write(`[${isoTs()}] [warn] specialists plugin dir missing: ${specialistsDir}
|
|
8254
|
+
if (!existsSync6(specialistsDir)) agentLogStream("claude-agent-compaction-stream", accountDir, conversationId).write(`[${isoTs()}] [warn] specialists plugin dir missing: ${specialistsDir}
|
|
8130
8255
|
`);
|
|
8131
8256
|
const args = [
|
|
8132
8257
|
"--print",
|
|
@@ -8155,15 +8280,23 @@ async function* runCompactionTurn(accountDir, accountId, systemPrompt, resumeSes
|
|
|
8155
8280
|
const proc = spawn2("claude", args, {
|
|
8156
8281
|
cwd: accountDir,
|
|
8157
8282
|
stdio: ["ignore", "pipe", "pipe"],
|
|
8158
|
-
env: {
|
|
8283
|
+
env: {
|
|
8284
|
+
...process.env,
|
|
8285
|
+
PLATFORM_ROOT: PLATFORM_ROOT4,
|
|
8286
|
+
ACCOUNT_DIR: accountDir,
|
|
8287
|
+
// Task 532: network traces on the compaction subprocess too — its tool
|
|
8288
|
+
// calls are part of the same conversation's record.
|
|
8289
|
+
NODE_DEBUG: "http,http2,net,tls,undici,dns"
|
|
8290
|
+
}
|
|
8159
8291
|
});
|
|
8160
|
-
const stderrLog = agentLogStream("claude-agent-compaction-stderr", accountDir);
|
|
8292
|
+
const stderrLog = agentLogStream("claude-agent-compaction-stderr", accountDir, conversationId);
|
|
8161
8293
|
stderrLog.on("error", () => {
|
|
8162
8294
|
});
|
|
8163
8295
|
proc.stderr?.pipe(stderrLog);
|
|
8164
|
-
const streamLog = agentLogStream("claude-agent-compaction-stream", accountDir);
|
|
8296
|
+
const streamLog = agentLogStream("claude-agent-compaction-stream", accountDir, conversationId);
|
|
8165
8297
|
streamLog.on("error", () => {
|
|
8166
8298
|
});
|
|
8299
|
+
teeProcStderrToStreamLog(proc, streamLog);
|
|
8167
8300
|
streamLog.write(`[${isoTs()}] [compaction-start] resumeSessionId=${resumeSessionId}
|
|
8168
8301
|
`);
|
|
8169
8302
|
proc.on("error", (err) => {
|
|
@@ -8271,6 +8404,83 @@ async function* parseClaudeStream(proc, streamLog, adminModel, conversationId) {
|
|
|
8271
8404
|
apiWaitTimer = void 0;
|
|
8272
8405
|
}
|
|
8273
8406
|
}
|
|
8407
|
+
const inflightTools = /* @__PURE__ */ new Map();
|
|
8408
|
+
const TOOL_WAIT_TICK_MS = 5e3;
|
|
8409
|
+
const TOOL_WAIT_DIAG_THRESHOLDS_SEC = [15, 30, 45, 60];
|
|
8410
|
+
const TOOL_WAIT_PROC_SEC = 30;
|
|
8411
|
+
const procPid = proc.pid;
|
|
8412
|
+
const toolWaitTimer = setInterval(() => {
|
|
8413
|
+
if (streamLog.destroyed) return;
|
|
8414
|
+
const now = Date.now();
|
|
8415
|
+
for (const [toolUseId, info] of inflightTools) {
|
|
8416
|
+
const elapsedSec = Math.round((now - info.startedAt) / 1e3);
|
|
8417
|
+
streamLog.write(`[${isoTs()}] [tool-wait]${convIdTag} name=${info.name} tool_use_id=${toolUseId} elapsed=${elapsedSec}s
|
|
8418
|
+
`);
|
|
8419
|
+
let nextThreshold = 0;
|
|
8420
|
+
for (const t of TOOL_WAIT_DIAG_THRESHOLDS_SEC) {
|
|
8421
|
+
if (elapsedSec >= t && t > info.lastProbeThresholdSec) nextThreshold = t;
|
|
8422
|
+
}
|
|
8423
|
+
if (nextThreshold > 0) {
|
|
8424
|
+
if (info.probeInFlight) {
|
|
8425
|
+
streamLog.write(`[${isoTs()}] [tool-wait-diag-skip]${convIdTag} tool_use_id=${toolUseId} reason=probe-in-flight threshold=${nextThreshold}s
|
|
8426
|
+
`);
|
|
8427
|
+
} else {
|
|
8428
|
+
info.lastProbeThresholdSec = nextThreshold;
|
|
8429
|
+
info.probeInFlight = true;
|
|
8430
|
+
const toolInput = toolIdToInput.get(toolUseId);
|
|
8431
|
+
runFailureDiagnostic(info.name, toolInput).then((diag) => {
|
|
8432
|
+
if (!streamLog.destroyed && !streamLog.writableEnded) {
|
|
8433
|
+
streamLog.write(`[${isoTs()}] [tool-wait-diag]${convIdTag} name=${info.name} tool_use_id=${toolUseId} elapsed=${elapsedSec}s ${diag}
|
|
8434
|
+
`);
|
|
8435
|
+
}
|
|
8436
|
+
}).catch((err) => {
|
|
8437
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
8438
|
+
if (!streamLog.destroyed && !streamLog.writableEnded) {
|
|
8439
|
+
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))}
|
|
8440
|
+
`);
|
|
8441
|
+
}
|
|
8442
|
+
}).finally(() => {
|
|
8443
|
+
const current = inflightTools.get(toolUseId);
|
|
8444
|
+
if (current) current.probeInFlight = false;
|
|
8445
|
+
});
|
|
8446
|
+
}
|
|
8447
|
+
}
|
|
8448
|
+
if (!info.procSampleEmitted && elapsedSec >= TOOL_WAIT_PROC_SEC) {
|
|
8449
|
+
info.procSampleEmitted = true;
|
|
8450
|
+
if (!procPid) {
|
|
8451
|
+
streamLog.write(`[${isoTs()}] [tool-wait-proc-skip]${convIdTag} tool_use_id=${toolUseId} reason=no-pid
|
|
8452
|
+
`);
|
|
8453
|
+
} else {
|
|
8454
|
+
const procLine = sampleProcState(procPid);
|
|
8455
|
+
streamLog.write(`[${isoTs()}] [tool-wait-proc]${convIdTag} name=${info.name} pid=${procPid} tool_use_id=${toolUseId} elapsed=${elapsedSec}s ${procLine}
|
|
8456
|
+
`);
|
|
8457
|
+
}
|
|
8458
|
+
}
|
|
8459
|
+
}
|
|
8460
|
+
}, TOOL_WAIT_TICK_MS);
|
|
8461
|
+
function trackToolUse(toolUseId, toolName) {
|
|
8462
|
+
if (!toolUseId) {
|
|
8463
|
+
streamLog.write(`[${isoTs()}] [tool-use-no-id]${convIdTag} name=${toolName} reason=missing-tool-use-id-cannot-track-wait
|
|
8464
|
+
`);
|
|
8465
|
+
return;
|
|
8466
|
+
}
|
|
8467
|
+
inflightTools.set(toolUseId, {
|
|
8468
|
+
name: toolName,
|
|
8469
|
+
startedAt: Date.now(),
|
|
8470
|
+
lastProbeThresholdSec: 0,
|
|
8471
|
+
probeInFlight: false,
|
|
8472
|
+
procSampleEmitted: false
|
|
8473
|
+
});
|
|
8474
|
+
}
|
|
8475
|
+
function untrackToolUse(toolUseId, reason) {
|
|
8476
|
+
if (!toolUseId) return;
|
|
8477
|
+
if (!inflightTools.delete(toolUseId)) {
|
|
8478
|
+
if (reason === "result") {
|
|
8479
|
+
streamLog.write(`[${isoTs()}] [tool-wait-late]${convIdTag} tool_use_id=${toolUseId} reason=result-without-matching-tool-use
|
|
8480
|
+
`);
|
|
8481
|
+
}
|
|
8482
|
+
}
|
|
8483
|
+
}
|
|
8274
8484
|
const readLines = async function* () {
|
|
8275
8485
|
for await (const chunk of proc.stdout) {
|
|
8276
8486
|
buffer += chunk.toString();
|
|
@@ -8449,6 +8659,7 @@ async function* parseClaudeStream(proc, streamLog, adminModel, conversationId) {
|
|
|
8449
8659
|
vncLog("mcp-tool", { name: block.name, input_preview: inputPreview });
|
|
8450
8660
|
}
|
|
8451
8661
|
}
|
|
8662
|
+
trackToolUse(block.id, block.name);
|
|
8452
8663
|
yield { type: "tool_use", name: block.name, input: block.input ?? {}, toolUseId: block.id };
|
|
8453
8664
|
}
|
|
8454
8665
|
}
|
|
@@ -8502,9 +8713,13 @@ async function* parseClaudeStream(proc, streamLog, adminModel, conversationId) {
|
|
|
8502
8713
|
`);
|
|
8503
8714
|
}
|
|
8504
8715
|
}
|
|
8716
|
+
} else if (block.is_error && !block.tool_use_id) {
|
|
8717
|
+
streamLog.write(`[${isoTs()}] [tool-failure-diag-skip]${convIdTag} name=${name} reason=no-tool-use-id-cannot-lookup-input
|
|
8718
|
+
`);
|
|
8505
8719
|
}
|
|
8506
8720
|
}
|
|
8507
8721
|
if (block.tool_use_id) toolIdToInput.delete(block.tool_use_id);
|
|
8722
|
+
untrackToolUse(block.tool_use_id, "result");
|
|
8508
8723
|
const renderDirectiveRe = /^__RENDER__(\{.+\})$/gm;
|
|
8509
8724
|
let renderMatch;
|
|
8510
8725
|
while ((renderMatch = renderDirectiveRe.exec(output)) !== null) {
|
|
@@ -8636,6 +8851,19 @@ async function* parseClaudeStream(proc, streamLog, adminModel, conversationId) {
|
|
|
8636
8851
|
}
|
|
8637
8852
|
} finally {
|
|
8638
8853
|
endApiWait();
|
|
8854
|
+
clearInterval(toolWaitTimer);
|
|
8855
|
+
for (const [toolUseId, info] of inflightTools) {
|
|
8856
|
+
const elapsedSec = Math.round((Date.now() - info.startedAt) / 1e3);
|
|
8857
|
+
if (!streamLog.destroyed && !streamLog.writableEnded) {
|
|
8858
|
+
streamLog.write(`[${isoTs()}] [tool-wait-late]${convIdTag} name=${info.name} tool_use_id=${toolUseId} elapsed=${elapsedSec}s reason=stream-ended-without-tool-result
|
|
8859
|
+
`);
|
|
8860
|
+
}
|
|
8861
|
+
}
|
|
8862
|
+
inflightTools.clear();
|
|
8863
|
+
if (buffer.length > 0 && !streamLog.destroyed && !streamLog.writableEnded) {
|
|
8864
|
+
streamLog.write(`[${isoTs()}] [stream-buffer-flush]${convIdTag} bytes=${buffer.length} reason=process-exit preview=${JSON.stringify(buffer.slice(0, 120))}
|
|
8865
|
+
`);
|
|
8866
|
+
}
|
|
8639
8867
|
}
|
|
8640
8868
|
if (!emittedDone) {
|
|
8641
8869
|
}
|
|
@@ -8895,17 +9123,21 @@ function buildAttachmentMetaText(attachments) {
|
|
|
8895
9123
|
async function* invokeAdminAgent(message, systemPrompt, accountDir, accountId, adminModel, sessionKey, maxTurns = 20, attachments = [], retryCount = 0, enabledPlugins, clientTimestamp) {
|
|
8896
9124
|
const userTimestamp = clientTimestamp ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
8897
9125
|
const resumeSessionId = sessionKey ? getAgentSessionId(sessionKey) : void 0;
|
|
9126
|
+
const spawnConvId = sessionKey ? getConversationIdForSession(sessionKey) : void 0;
|
|
9127
|
+
if (!spawnConvId) {
|
|
9128
|
+
throw new Error(`invokeAdminAgent: conversationId missing for sessionKey=${sessionKey?.slice(0, 8) ?? "none"} \u2014 ensureConversation must run before invoking the agent`);
|
|
9129
|
+
}
|
|
8898
9130
|
const cdpOk = await ensureCdp();
|
|
8899
9131
|
if (!cdpOk) {
|
|
8900
|
-
const cdpLog = agentLogStream("claude-agent-stream", accountDir);
|
|
9132
|
+
const cdpLog = agentLogStream("claude-agent-stream", accountDir, spawnConvId);
|
|
8901
9133
|
cdpLog.write(`[${isoTs()}] [warn] ensureCdp failed \u2014 browser-specialist degraded
|
|
8902
9134
|
`);
|
|
8903
9135
|
cdpLog.end();
|
|
8904
9136
|
}
|
|
8905
9137
|
const ccUserId = sessionKey ? getUserIdForSession(sessionKey) : void 0;
|
|
8906
|
-
const mcpConfig = JSON.stringify({ mcpServers: getMcpServers(accountId, ccUserId, enabledPlugins) });
|
|
9138
|
+
const mcpConfig = JSON.stringify({ mcpServers: getMcpServers(accountId, spawnConvId, ccUserId, enabledPlugins) });
|
|
8907
9139
|
const specialistsDir = resolve6(accountDir, "specialists");
|
|
8908
|
-
if (!existsSync6(specialistsDir)) agentLogStream("claude-agent-stream", accountDir).write(`[${isoTs()}] [warn] specialists plugin dir missing: ${specialistsDir}
|
|
9140
|
+
if (!existsSync6(specialistsDir)) agentLogStream("claude-agent-stream", accountDir, spawnConvId).write(`[${isoTs()}] [warn] specialists plugin dir missing: ${specialistsDir}
|
|
8909
9141
|
`);
|
|
8910
9142
|
const args = [
|
|
8911
9143
|
"--print",
|
|
@@ -8935,15 +9167,24 @@ async function* invokeAdminAgent(message, systemPrompt, accountDir, accountId, a
|
|
|
8935
9167
|
const proc = spawn2("claude", args, {
|
|
8936
9168
|
cwd: accountDir,
|
|
8937
9169
|
stdio: ["ignore", "pipe", "pipe"],
|
|
8938
|
-
env: {
|
|
9170
|
+
env: {
|
|
9171
|
+
...process.env,
|
|
9172
|
+
PLATFORM_ROOT: PLATFORM_ROOT4,
|
|
9173
|
+
ACCOUNT_DIR: accountDir,
|
|
9174
|
+
// Task 532: tee subprocess network activity into the per-conversation
|
|
9175
|
+
// stream log via the existing stderr pipe. Closes the Claude Code
|
|
9176
|
+
// "what was the tool actually doing?" black box during tool waits.
|
|
9177
|
+
NODE_DEBUG: "http,http2,net,tls,undici,dns"
|
|
9178
|
+
}
|
|
8939
9179
|
});
|
|
8940
|
-
const stderrLog = agentLogStream("claude-agent-stderr", accountDir);
|
|
9180
|
+
const stderrLog = agentLogStream("claude-agent-stderr", accountDir, spawnConvId);
|
|
8941
9181
|
stderrLog.on("error", () => {
|
|
8942
9182
|
});
|
|
8943
9183
|
proc.stderr?.pipe(stderrLog);
|
|
8944
|
-
const streamLog = agentLogStream("claude-agent-stream", accountDir);
|
|
9184
|
+
const streamLog = agentLogStream("claude-agent-stream", accountDir, spawnConvId);
|
|
8945
9185
|
streamLog.on("error", () => {
|
|
8946
9186
|
});
|
|
9187
|
+
teeProcStderrToStreamLog(proc, streamLog);
|
|
8947
9188
|
if (sessionKey) {
|
|
8948
9189
|
const prev = activeProcesses.get(sessionKey);
|
|
8949
9190
|
if (prev) {
|
|
@@ -8952,8 +9193,7 @@ async function* invokeAdminAgent(message, systemPrompt, accountDir, accountId, a
|
|
|
8952
9193
|
}
|
|
8953
9194
|
activeProcesses.set(sessionKey, { pid: proc.pid, spawnedAt: Date.now() });
|
|
8954
9195
|
}
|
|
8955
|
-
|
|
8956
|
-
streamLog.write(`[${isoTs()}] [spawn] pid=${proc.pid} resume=${resumeSessionId ?? "none"} sessionKey=${sessionKey ?? "none"} conversationId=${spawnConvId ?? "none"} pluginDir=${specialistsDir}
|
|
9196
|
+
streamLog.write(`[${isoTs()}] [spawn] pid=${proc.pid} resume=${resumeSessionId ?? "none"} sessionKey=${sessionKey ?? "none"} conversationId=${spawnConvId} pluginDir=${specialistsDir}
|
|
8957
9197
|
`);
|
|
8958
9198
|
streamLog.write(`[${isoTs()}] [stdin] len=${fullMessage.length} preview=${JSON.stringify(fullMessage.slice(0, 80))}
|
|
8959
9199
|
`);
|
|
@@ -9026,7 +9266,7 @@ async function* invokeAdminAgent(message, systemPrompt, accountDir, accountId, a
|
|
|
9026
9266
|
if (event.type === "usage" && sessionKey && currentAgentSessionId) {
|
|
9027
9267
|
const peakReqPct = event.peak_request_pct ?? 0;
|
|
9028
9268
|
if (peakReqPct >= COMPACTION_THRESHOLD) {
|
|
9029
|
-
const compactionIter = runCompactionTurn(accountDir, accountId, systemPrompt, currentAgentSessionId, adminModel, enabledPlugins);
|
|
9269
|
+
const compactionIter = runCompactionTurn(accountDir, accountId, systemPrompt, currentAgentSessionId, adminModel, spawnConvId, enabledPlugins);
|
|
9030
9270
|
let step = await compactionIter.next();
|
|
9031
9271
|
while (!step.done) {
|
|
9032
9272
|
yield { type: "status", message: step.value };
|
|
@@ -9197,6 +9437,10 @@ ${summary}`;
|
|
|
9197
9437
|
}
|
|
9198
9438
|
async function* invokeManagedAdminAgent(message, systemPrompt, accountDir, accountId, adminModel, sessionKey, maxTurns = 20, attachments = [], retryCount = 0, enabledPlugins, clientTimestamp) {
|
|
9199
9439
|
const userTimestamp = clientTimestamp ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
9440
|
+
const managedConvId = getConversationIdForSession(sessionKey);
|
|
9441
|
+
if (!managedConvId) {
|
|
9442
|
+
throw new Error(`invokeManagedAdminAgent: conversationId missing for sessionKey=${sessionKey.slice(0, 8)} \u2014 ensureConversation must run first`);
|
|
9443
|
+
}
|
|
9200
9444
|
const pendingTrimmed = consumePendingTrimmedMessages(sessionKey);
|
|
9201
9445
|
if (pendingTrimmed && pendingTrimmed.length > 0) {
|
|
9202
9446
|
const ok = await compactTrimmedMessages(accountId, pendingTrimmed);
|
|
@@ -9204,7 +9448,7 @@ async function* invokeManagedAdminAgent(message, systemPrompt, accountDir, accou
|
|
|
9204
9448
|
storePendingTrimmedMessages(sessionKey, pendingTrimmed);
|
|
9205
9449
|
}
|
|
9206
9450
|
}
|
|
9207
|
-
const streamLog = agentLogStream("claude-agent-stream", accountDir);
|
|
9451
|
+
const streamLog = agentLogStream("claude-agent-stream", accountDir, managedConvId);
|
|
9208
9452
|
streamLog.on("error", () => {
|
|
9209
9453
|
});
|
|
9210
9454
|
const systemPromptTokens = estimateTokens(systemPrompt);
|
|
@@ -9235,7 +9479,7 @@ async function* invokeManagedAdminAgent(message, systemPrompt, accountDir, accou
|
|
|
9235
9479
|
if (!cdpOk) streamLog.write(`[${isoTs()}] [warn] ensureCdp failed \u2014 browser-specialist degraded
|
|
9236
9480
|
`);
|
|
9237
9481
|
const managedUserId = getUserIdForSession(sessionKey);
|
|
9238
|
-
const mcpConfig = JSON.stringify({ mcpServers: getMcpServers(accountId, managedUserId, enabledPlugins) });
|
|
9482
|
+
const mcpConfig = JSON.stringify({ mcpServers: getMcpServers(accountId, managedConvId, managedUserId, enabledPlugins) });
|
|
9239
9483
|
const specialistsDir = resolve6(accountDir, "specialists");
|
|
9240
9484
|
if (!existsSync6(specialistsDir)) streamLog.write(`[${isoTs()}] [warn] specialists plugin dir missing: ${specialistsDir}
|
|
9241
9485
|
`);
|
|
@@ -9265,12 +9509,20 @@ async function* invokeManagedAdminAgent(message, systemPrompt, accountDir, accou
|
|
|
9265
9509
|
const proc = spawn2("claude", args, {
|
|
9266
9510
|
cwd: accountDir,
|
|
9267
9511
|
stdio: ["ignore", "pipe", "pipe"],
|
|
9268
|
-
env: {
|
|
9512
|
+
env: {
|
|
9513
|
+
...process.env,
|
|
9514
|
+
PLATFORM_ROOT: PLATFORM_ROOT4,
|
|
9515
|
+
ACCOUNT_DIR: accountDir,
|
|
9516
|
+
// Task 532: tee subprocess network traces into the per-conversation
|
|
9517
|
+
// stream log via the stderr pipe + dual-consume listener.
|
|
9518
|
+
NODE_DEBUG: "http,http2,net,tls,undici,dns"
|
|
9519
|
+
}
|
|
9269
9520
|
});
|
|
9270
|
-
const stderrLog = agentLogStream("claude-agent-stderr", accountDir);
|
|
9521
|
+
const stderrLog = agentLogStream("claude-agent-stderr", accountDir, managedConvId);
|
|
9271
9522
|
stderrLog.on("error", () => {
|
|
9272
9523
|
});
|
|
9273
9524
|
proc.stderr?.pipe(stderrLog);
|
|
9525
|
+
teeProcStderrToStreamLog(proc, streamLog);
|
|
9274
9526
|
if (sessionKey) {
|
|
9275
9527
|
const prev = activeProcesses.get(sessionKey);
|
|
9276
9528
|
if (prev) {
|
|
@@ -9279,14 +9531,13 @@ async function* invokeManagedAdminAgent(message, systemPrompt, accountDir, accou
|
|
|
9279
9531
|
}
|
|
9280
9532
|
activeProcesses.set(sessionKey, { pid: proc.pid, spawnedAt: Date.now() });
|
|
9281
9533
|
}
|
|
9282
|
-
|
|
9283
|
-
streamLog.write(`[${isoTs()}] [managed-spawn] pid=${proc.pid} sessionKey=${sessionKey} conversationId=${managedConvId ?? "none"} historyMessages=${history.length} pluginDir=${specialistsDir}
|
|
9534
|
+
streamLog.write(`[${isoTs()}] [managed-spawn] pid=${proc.pid} sessionKey=${sessionKey} conversationId=${managedConvId} historyMessages=${history.length} pluginDir=${specialistsDir}
|
|
9284
9535
|
`);
|
|
9285
9536
|
streamLog.write(`[${isoTs()}] [stdin] len=${fullMessage.length} preview=${JSON.stringify(fullMessage.slice(0, 80))}
|
|
9286
9537
|
`);
|
|
9287
9538
|
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
|
|
9539
|
+
console.log(`[process-exit] pid=${proc.pid} code=${code} signal=${signal} sessionKey=${sessionKey ?? "none"} conversationId=${managedConvId}`);
|
|
9540
|
+
if (!streamLog.destroyed && !streamLog.writableEnded) streamLog.write(`[${isoTs()}] [process-exit] pid=${proc.pid} code=${code} signal=${signal} conversationId=${managedConvId}
|
|
9290
9541
|
`);
|
|
9291
9542
|
if (sessionKey) activeProcesses.delete(sessionKey);
|
|
9292
9543
|
});
|
|
@@ -9507,7 +9758,11 @@ async function* invokePublicAgent(message, systemPrompt, accountId, accountDir,
|
|
|
9507
9758
|
yield { type: "done" };
|
|
9508
9759
|
return;
|
|
9509
9760
|
}
|
|
9510
|
-
const
|
|
9761
|
+
const publicConvId = sessionKey ? getConversationIdForSession(sessionKey) : void 0;
|
|
9762
|
+
if (!publicConvId) {
|
|
9763
|
+
throw new Error(`invokePublicAgent: conversationId missing for sessionKey=${sessionKey?.slice(0, 8) ?? "none"} \u2014 ensureConversation must run first`);
|
|
9764
|
+
}
|
|
9765
|
+
const streamLog = agentLogStream("public-agent-stream", accountDir, publicConvId);
|
|
9511
9766
|
streamLog.write(`[${isoTs()}] [public-user-message] ${JSON.stringify(message)}
|
|
9512
9767
|
`);
|
|
9513
9768
|
if (sessionKey) {
|
|
@@ -9780,6 +10035,8 @@ async function* compactSession(sessionKey) {
|
|
|
9780
10035
|
}
|
|
9781
10036
|
const currentSessionId = getAgentSessionId(sessionKey);
|
|
9782
10037
|
if (!currentSessionId) return { ok: false, reason: "no-session" };
|
|
10038
|
+
const compactionConvId = getConversationIdForSession(sessionKey);
|
|
10039
|
+
if (!compactionConvId) return { ok: false, reason: "no-conversation" };
|
|
9783
10040
|
const identity = readIdentity(account.accountDir, "admin");
|
|
9784
10041
|
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
10042
|
const baseSystemPrompt = identity ?? defaultSystemPrompt;
|
|
@@ -9787,7 +10044,7 @@ async function* compactSession(sessionKey) {
|
|
|
9787
10044
|
const systemPrompt = outputStyle === "explanatory" ? `${baseSystemPrompt}
|
|
9788
10045
|
|
|
9789
10046
|
${EXPLANATORY_STYLE_INSTRUCTIONS}` : baseSystemPrompt;
|
|
9790
|
-
const compactionIter = runCompactionTurn(account.accountDir, account.accountId, systemPrompt, currentSessionId, account.config.adminModel, account.config.enabledPlugins);
|
|
10047
|
+
const compactionIter = runCompactionTurn(account.accountDir, account.accountId, systemPrompt, currentSessionId, account.config.adminModel, compactionConvId, account.config.enabledPlugins);
|
|
9791
10048
|
let step = await compactionIter.next();
|
|
9792
10049
|
while (!step.done) {
|
|
9793
10050
|
yield step.value;
|
|
@@ -10204,6 +10461,38 @@ function defaultRules() {
|
|
|
10204
10461
|
thresholdWindowMinutes: 5,
|
|
10205
10462
|
scope: "session",
|
|
10206
10463
|
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.'
|
|
10464
|
+
},
|
|
10465
|
+
{
|
|
10466
|
+
// Task 532: closes the "60-second black-box tool wait" class. Fires when
|
|
10467
|
+
// a tool hits the 30-second mark of the mid-flight heartbeat. The
|
|
10468
|
+
// pattern anchors on elapsed=30s specifically (the tool-wait tick emits
|
|
10469
|
+
// one line per 5s per tool, so matching every tick would be noisy) and
|
|
10470
|
+
// excludes tools whose long runtime is expected: `Task`/`Agent` subagent
|
|
10471
|
+
// dispatch, `Bash` with explicit long timeouts.
|
|
10472
|
+
id: "tool-wait-long-stall",
|
|
10473
|
+
name: "Tool wait exceeds 30 seconds (possible stall)",
|
|
10474
|
+
type: "repeated-error",
|
|
10475
|
+
logSource: "system",
|
|
10476
|
+
pattern: "\\[tool-wait\\][^\\n]*name=(?!Task\\b|Agent\\b|Bash\\b)[A-Za-z0-9_]+[^\\n]*elapsed=30s",
|
|
10477
|
+
thresholdCount: 0,
|
|
10478
|
+
thresholdWindowMinutes: 0,
|
|
10479
|
+
scope: "session",
|
|
10480
|
+
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."
|
|
10481
|
+
},
|
|
10482
|
+
{
|
|
10483
|
+
// Task 533: surface every Cloudflare-plugin refusal. The plugin emits
|
|
10484
|
+
// exactly one [cloudflare:refuse] line per refusal with a structured
|
|
10485
|
+
// reason field; any single occurrence on a previously-clean device
|
|
10486
|
+
// means scope, account-binding, or post-flight FQDN drift — the
|
|
10487
|
+
// operator should run cf-verify before acting.
|
|
10488
|
+
id: "cloudflare-refuse",
|
|
10489
|
+
name: "Cloudflare plugin refusal",
|
|
10490
|
+
type: "silent-catch",
|
|
10491
|
+
logSource: "any",
|
|
10492
|
+
pattern: "\\[cloudflare:refuse\\]|\\[cloudflare:post-flight-mismatch\\]",
|
|
10493
|
+
thresholdCount: 0,
|
|
10494
|
+
thresholdWindowMinutes: 0,
|
|
10495
|
+
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
10496
|
}
|
|
10208
10497
|
];
|
|
10209
10498
|
}
|
|
@@ -10419,12 +10708,31 @@ function discoverSourceFiles(configDir2, accountLogDir2, logicalSource) {
|
|
|
10419
10708
|
}[logicalSource];
|
|
10420
10709
|
if (!existsSync8(accountLogDir2)) return [];
|
|
10421
10710
|
const files = [];
|
|
10711
|
+
let scanned = 0;
|
|
10712
|
+
let skippedPrefixMismatch = 0;
|
|
10713
|
+
let skippedNotLog = 0;
|
|
10422
10714
|
for (const entry of readdirSync3(accountLogDir2)) {
|
|
10423
|
-
|
|
10715
|
+
scanned += 1;
|
|
10716
|
+
const matchesPrefix = entry.startsWith(prefix);
|
|
10717
|
+
const isLog = entry.endsWith(".log");
|
|
10718
|
+
if (matchesPrefix && isLog) {
|
|
10424
10719
|
files.push({ logicalSource, filepath: join5(accountLogDir2, entry) });
|
|
10720
|
+
} else if (!matchesPrefix) {
|
|
10721
|
+
skippedPrefixMismatch += 1;
|
|
10722
|
+
} else {
|
|
10723
|
+
skippedNotLog += 1;
|
|
10425
10724
|
}
|
|
10426
10725
|
}
|
|
10427
|
-
files.sort((a, b) =>
|
|
10726
|
+
files.sort((a, b) => {
|
|
10727
|
+
try {
|
|
10728
|
+
return statSync5(b.filepath).mtimeMs - statSync5(a.filepath).mtimeMs;
|
|
10729
|
+
} catch {
|
|
10730
|
+
return a.filepath.localeCompare(b.filepath);
|
|
10731
|
+
}
|
|
10732
|
+
});
|
|
10733
|
+
if (skippedPrefixMismatch > 0 || skippedNotLog > 0) {
|
|
10734
|
+
console.error(`[review-scan-skip] dir=${accountLogDir2} source=${logicalSource} prefix=${prefix} scanned=${scanned} matched=${files.length} skipped_prefix=${skippedPrefixMismatch} skipped_non_log=${skippedNotLog}`);
|
|
10735
|
+
}
|
|
10428
10736
|
return files;
|
|
10429
10737
|
}
|
|
10430
10738
|
function discoverAllSources(configDir2, accountLogDir2) {
|
|
@@ -28308,8 +28616,9 @@ async function POST2(req) {
|
|
|
28308
28616
|
{ status: 500, headers: { "Content-Type": "application/json" } }
|
|
28309
28617
|
);
|
|
28310
28618
|
}
|
|
28311
|
-
const
|
|
28312
|
-
const
|
|
28619
|
+
const publicSseConvId = getConversationIdForSession(session_key);
|
|
28620
|
+
const sseLog = publicSseConvId ? agentLogStream("sse-events", account.accountDir, publicSseConvId) : preConversationLogStream("sse-events", account.accountDir);
|
|
28621
|
+
const sk = publicSseConvId?.slice(0, 8) ?? session_key.slice(0, 8);
|
|
28313
28622
|
const agentName = getAgentNameForSession(session_key);
|
|
28314
28623
|
if (!agentName) {
|
|
28315
28624
|
console.log(`[chat] no agent for session=${sk} \u2014 session expired or server restarted`);
|
|
@@ -30684,7 +30993,8 @@ async function POST21(req) {
|
|
|
30684
30993
|
try {
|
|
30685
30994
|
const parsed = JSON.parse(message);
|
|
30686
30995
|
if (parsed._lifecycle) {
|
|
30687
|
-
const
|
|
30996
|
+
const lifecycleConvId = getConversationIdForSession(session_key);
|
|
30997
|
+
const lifecycleLog = lifecycleConvId ? agentLogStream("component-lifecycle", account.accountDir, lifecycleConvId) : preConversationLogStream("component-lifecycle", account.accountDir);
|
|
30688
30998
|
const ts = (/* @__PURE__ */ new Date()).toISOString().slice(11, 23);
|
|
30689
30999
|
const detail = parsed.filePath ? ` filePath=${parsed.filePath}` : "";
|
|
30690
31000
|
lifecycleLog.write(`[${ts}] [component:${parsed.component ?? "unknown"}] ${parsed.event ?? "unknown"}${detail}
|
|
@@ -30708,7 +31018,8 @@ async function POST21(req) {
|
|
|
30708
31018
|
try {
|
|
30709
31019
|
const parsed = JSON.parse(message);
|
|
30710
31020
|
if (isComponentDone(parsed)) {
|
|
30711
|
-
const
|
|
31021
|
+
const componentConvId = getConversationIdForSession(session_key);
|
|
31022
|
+
const componentLog = componentConvId ? agentLogStream("component-lifecycle", account.accountDir, componentConvId) : preConversationLogStream("component-lifecycle", account.accountDir);
|
|
30712
31023
|
const ts = (/* @__PURE__ */ new Date()).toISOString().slice(11, 23);
|
|
30713
31024
|
message = transformComponentDone(parsed);
|
|
30714
31025
|
componentLog.write(`[${ts}] [component:${parsed.component}] componentDone \u2192 transformed
|
|
@@ -30736,8 +31047,9 @@ async function POST21(req) {
|
|
|
30736
31047
|
gatewayResult = await processInbound(message, "web-admin");
|
|
30737
31048
|
}
|
|
30738
31049
|
const encoder = new TextEncoder();
|
|
30739
|
-
const
|
|
30740
|
-
const
|
|
31050
|
+
const sseConvId = getConversationIdForSession(session_key);
|
|
31051
|
+
const sseLog = sseConvId ? agentLogStream("sse-events", account.accountDir, sseConvId) : preConversationLogStream("sse-events", account.accountDir);
|
|
31052
|
+
const sk = sseConvId?.slice(0, 8) ?? session_key.slice(0, 8);
|
|
30741
31053
|
const readable = new ReadableStream({
|
|
30742
31054
|
async start(controller) {
|
|
30743
31055
|
try {
|
|
@@ -30853,6 +31165,7 @@ async function GET9(request) {
|
|
|
30853
31165
|
const { searchParams } = new URL(request.url);
|
|
30854
31166
|
const fileParam = searchParams.get("file");
|
|
30855
31167
|
const typeParam = searchParams.get("type");
|
|
31168
|
+
const conversationIdParam = searchParams.get("conversationId");
|
|
30856
31169
|
const download = searchParams.get("download") === "1";
|
|
30857
31170
|
const account = resolveAccount();
|
|
30858
31171
|
const accountLogDir2 = account ? resolve19(account.accountDir, "logs") : null;
|
|
@@ -30872,18 +31185,21 @@ async function GET9(request) {
|
|
|
30872
31185
|
return Response.json({ error: `File not found: ${safe}` }, { status: 404 });
|
|
30873
31186
|
}
|
|
30874
31187
|
if (typeParam) {
|
|
30875
|
-
const
|
|
30876
|
-
|
|
30877
|
-
|
|
30878
|
-
|
|
30879
|
-
|
|
30880
|
-
|
|
30881
|
-
public: `public-agent-stream-${today}.log`
|
|
31188
|
+
const prefixMap = {
|
|
31189
|
+
stream: "claude-agent-stream",
|
|
31190
|
+
error: "claude-agent-stderr",
|
|
31191
|
+
session: "sse-events",
|
|
31192
|
+
sse: "sse-events",
|
|
31193
|
+
public: "public-agent-stream"
|
|
30882
31194
|
};
|
|
30883
|
-
const
|
|
30884
|
-
if (!
|
|
31195
|
+
const prefix = prefixMap[typeParam];
|
|
31196
|
+
if (!prefix) {
|
|
30885
31197
|
return Response.json({ error: `Unknown type: ${typeParam}. Valid: stream, error, session, sse, public` }, { status: 400 });
|
|
30886
31198
|
}
|
|
31199
|
+
if (!conversationIdParam) {
|
|
31200
|
+
return Response.json({ error: `type=${typeParam} requires conversationId (per-conversation log files, no daily fallback)` }, { status: 400 });
|
|
31201
|
+
}
|
|
31202
|
+
const fileName = `${prefix}-${conversationIdParam}.log`;
|
|
30887
31203
|
for (const dir of [accountLogDir2, LOG_DIR]) {
|
|
30888
31204
|
if (!dir) continue;
|
|
30889
31205
|
const filePath = resolve19(dir, fileName);
|
|
@@ -31589,7 +31905,7 @@ var brandLoginOpts = {
|
|
|
31589
31905
|
bodyFont: BRAND.defaultFonts?.body,
|
|
31590
31906
|
logoContainsName: !!BRAND.logoContainsName
|
|
31591
31907
|
};
|
|
31592
|
-
var ALIAS_DOMAINS_PATH = join13(
|
|
31908
|
+
var ALIAS_DOMAINS_PATH = join13(homedir4(), BRAND.configDir, "alias-domains.json");
|
|
31593
31909
|
function loadAliasDomains() {
|
|
31594
31910
|
try {
|
|
31595
31911
|
if (!existsSync26(ALIAS_DOMAINS_PATH)) return null;
|
|
@@ -32082,7 +32398,7 @@ function cachedHtml(file2) {
|
|
|
32082
32398
|
}
|
|
32083
32399
|
var brandedHtmlCache = /* @__PURE__ */ new Map();
|
|
32084
32400
|
function loadBrandingCache(agentSlug) {
|
|
32085
|
-
const configDir2 = join13(
|
|
32401
|
+
const configDir2 = join13(homedir4(), BRAND.configDir);
|
|
32086
32402
|
try {
|
|
32087
32403
|
const accountJsonPath = join13(configDir2, "account.json");
|
|
32088
32404
|
if (!existsSync26(accountJsonPath)) return null;
|
|
@@ -32098,7 +32414,7 @@ function loadBrandingCache(agentSlug) {
|
|
|
32098
32414
|
}
|
|
32099
32415
|
function resolveDefaultSlug() {
|
|
32100
32416
|
try {
|
|
32101
|
-
const configDir2 = join13(
|
|
32417
|
+
const configDir2 = join13(homedir4(), BRAND.configDir);
|
|
32102
32418
|
const accountJsonPath = join13(configDir2, "account.json");
|
|
32103
32419
|
if (!existsSync26(accountJsonPath)) return null;
|
|
32104
32420
|
const account = JSON.parse(readFileSync26(accountJsonPath, "utf-8"));
|