@rubytech/create-realagent 1.0.613 → 1.0.615
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +42 -8
- package/package.json +1 -1
- package/payload/platform/plugins/admin/mcp/dist/lib/review-tools.d.ts.map +1 -1
- package/payload/platform/plugins/admin/mcp/dist/lib/review-tools.js +2 -0
- package/payload/platform/plugins/admin/mcp/dist/lib/review-tools.js.map +1 -1
- package/payload/platform/plugins/cloudflare/mcp/dist/index.js +32 -4
- package/payload/platform/plugins/cloudflare/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/cloudflare/mcp/dist/lib/cloudflared.d.ts +18 -2
- package/payload/platform/plugins/cloudflare/mcp/dist/lib/cloudflared.d.ts.map +1 -1
- package/payload/platform/plugins/cloudflare/mcp/dist/lib/cloudflared.js +106 -19
- package/payload/platform/plugins/cloudflare/mcp/dist/lib/cloudflared.js.map +1 -1
- package/payload/platform/plugins/cloudflare/references/setup-guide.md +6 -0
- package/payload/platform/plugins/docs/references/troubleshooting.md +2 -0
- package/payload/platform/plugins/email/mcp/dist/lib/providers.d.ts +9 -2
- package/payload/platform/plugins/email/mcp/dist/lib/providers.d.ts.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/lib/providers.js +545 -92
- package/payload/platform/plugins/email/mcp/dist/lib/providers.js.map +1 -1
- package/payload/platform/templates/agents/admin/IDENTITY.md +6 -0
- package/payload/platform/templates/agents/admin/SOUL.md +20 -0
- package/payload/platform/templates/agents/public/IDENTITY.md +1 -0
- package/payload/platform/templates/agents/public/SOUL.md +12 -0
- package/payload/platform/templates/specialists/agents/content-producer.md +4 -0
- package/payload/platform/templates/specialists/agents/personal-assistant.md +4 -0
- package/payload/platform/templates/specialists/agents/project-manager.md +4 -0
- package/payload/platform/templates/specialists/agents/research-assistant.md +4 -0
- package/payload/server/server.js +332 -59
package/payload/server/server.js
CHANGED
|
@@ -4165,7 +4165,9 @@ import Anthropic2 from "@anthropic-ai/sdk";
|
|
|
4165
4165
|
import { spawn as spawn2 } from "child_process";
|
|
4166
4166
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
4167
4167
|
import { resolve as resolve6, join as join4 } from "path";
|
|
4168
|
-
import { readFileSync as readFileSync7, writeFileSync as writeFileSync5, readdirSync as readdirSync2, existsSync as existsSync6, mkdirSync as mkdirSync4, createWriteStream, statSync as
|
|
4168
|
+
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
|
+
import { lookup as dnsLookup } from "dns/promises";
|
|
4170
|
+
import { createConnection as netConnect } from "net";
|
|
4169
4171
|
|
|
4170
4172
|
// ../lib/models/src/index.ts
|
|
4171
4173
|
var OPUS_MODEL = "claude-opus-4-7";
|
|
@@ -4465,7 +4467,7 @@ function buildX11Env(chromiumWrapperPath, transport = "vnc") {
|
|
|
4465
4467
|
import neo4j from "neo4j-driver";
|
|
4466
4468
|
import { randomUUID } from "crypto";
|
|
4467
4469
|
import { spawn } from "child_process";
|
|
4468
|
-
import { readFileSync as readFileSync6, readdirSync, existsSync as existsSync5 } from "fs";
|
|
4470
|
+
import { readFileSync as readFileSync6, readdirSync, existsSync as existsSync5, openSync, readSync, closeSync, statSync as statSync2 } from "fs";
|
|
4469
4471
|
import { resolve as resolve5 } from "path";
|
|
4470
4472
|
var PLATFORM_ROOT3 = process.env.MAXY_PLATFORM_ROOT ?? resolve5(process.cwd(), "..");
|
|
4471
4473
|
var driver = null;
|
|
@@ -5455,7 +5457,45 @@ Expertise: ${typeof profile.expertise === "string" ? profile.expertise : JSON.st
|
|
|
5455
5457
|
var MAX_SESSION_TASKS = 20;
|
|
5456
5458
|
var MAX_SESSION_REVIEW_ALERTS = 5;
|
|
5457
5459
|
var MAX_SESSION_PROJECTS = 5;
|
|
5458
|
-
|
|
5460
|
+
var MAX_RECENT_TOOL_FAILURES = 3;
|
|
5461
|
+
var RECENT_FAILURES_TAIL_BYTES = 10 * 1024;
|
|
5462
|
+
function readRecentToolFailures(accountId, conversationId) {
|
|
5463
|
+
try {
|
|
5464
|
+
const platformRoot3 = process.env.MAXY_PLATFORM_ROOT ?? resolve5(process.cwd(), "..");
|
|
5465
|
+
const logDir = resolve5(platformRoot3, "..", "data/accounts", accountId, "logs");
|
|
5466
|
+
const date5 = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
5467
|
+
const logPath2 = resolve5(logDir, `claude-agent-stream-${date5}.log`);
|
|
5468
|
+
if (!existsSync5(logPath2)) return [];
|
|
5469
|
+
const st = statSync2(logPath2);
|
|
5470
|
+
const size = st.size;
|
|
5471
|
+
const readBytes = Math.min(size, RECENT_FAILURES_TAIL_BYTES);
|
|
5472
|
+
const position = size - readBytes;
|
|
5473
|
+
const fd = openSync(logPath2, "r");
|
|
5474
|
+
try {
|
|
5475
|
+
const buf = Buffer.alloc(readBytes);
|
|
5476
|
+
readSync(fd, buf, 0, readBytes, position);
|
|
5477
|
+
const text = buf.toString("utf-8");
|
|
5478
|
+
const convPrefix = conversationId.slice(0, 8);
|
|
5479
|
+
const lines = text.split("\n");
|
|
5480
|
+
const matches = [];
|
|
5481
|
+
for (let i = lines.length - 1; i >= 0 && matches.length < MAX_RECENT_TOOL_FAILURES; i--) {
|
|
5482
|
+
const line = lines[i];
|
|
5483
|
+
if (line.includes("[tool-failure-diag]") && line.includes(`conversationId=${convPrefix}`)) {
|
|
5484
|
+
matches.push(line);
|
|
5485
|
+
}
|
|
5486
|
+
}
|
|
5487
|
+
return matches.reverse();
|
|
5488
|
+
} finally {
|
|
5489
|
+
closeSync(fd);
|
|
5490
|
+
}
|
|
5491
|
+
} catch (err) {
|
|
5492
|
+
console.error(
|
|
5493
|
+
`[session-context] recent-tool-failures read failed: ${err instanceof Error ? err.message : String(err)}`
|
|
5494
|
+
);
|
|
5495
|
+
return [];
|
|
5496
|
+
}
|
|
5497
|
+
}
|
|
5498
|
+
async function loadSessionContext(accountId, conversationId) {
|
|
5459
5499
|
const session = getSession();
|
|
5460
5500
|
try {
|
|
5461
5501
|
const digestResult = await session.run(
|
|
@@ -5625,9 +5665,21 @@ ${projectLines.join("\n")}`);
|
|
|
5625
5665
|
sections.push(`## Active Review Alerts (${alertsResult.records.length})
|
|
5626
5666
|
${alertLines.join("\n")}`);
|
|
5627
5667
|
}
|
|
5668
|
+
let recentFailuresCount = 0;
|
|
5669
|
+
if (conversationId) {
|
|
5670
|
+
const failureLines = readRecentToolFailures(accountId, conversationId);
|
|
5671
|
+
if (failureLines.length > 0) {
|
|
5672
|
+
recentFailuresCount = failureLines.length;
|
|
5673
|
+
const guidance = "These tool calls failed in the current conversation. Inspect the diagnostic fields before retrying the same tool against the same target \u2014 if you do retry, narrate explicitly why the diagnostic suggests a retry will now succeed. A second identical failure is evidence the path is broken, not evidence another attempt is warranted.";
|
|
5674
|
+
sections.push(`## Recent Tool Failures (${failureLines.length})
|
|
5675
|
+
${guidance}
|
|
5676
|
+
|
|
5677
|
+
${failureLines.map((l) => `- ${l.trim()}`).join("\n")}`);
|
|
5678
|
+
}
|
|
5679
|
+
}
|
|
5628
5680
|
if (sections.length === 0) return null;
|
|
5629
5681
|
console.error(
|
|
5630
|
-
`[session-context] Loaded for ${accountId.slice(0, 8)}\u2026: digest=${digestResult.records.length > 0 ? "yes" : "no"}, tasks=${tasksResult.records.length}, projects=${projectsCount}, reviewAlerts=${alertsResult.records.length}, pendingActions=${pendingCount}`
|
|
5682
|
+
`[session-context] Loaded for ${accountId.slice(0, 8)}\u2026: digest=${digestResult.records.length > 0 ? "yes" : "no"}, tasks=${tasksResult.records.length}, projects=${projectsCount}, reviewAlerts=${alertsResult.records.length}, pendingActions=${pendingCount}, recentFailures=${recentFailuresCount}`
|
|
5631
5683
|
);
|
|
5632
5684
|
return `<previous-context>
|
|
5633
5685
|
${sections.join("\n\n")}
|
|
@@ -5991,6 +6043,133 @@ var BROWSER_TOOL_PREFIXES = [
|
|
|
5991
6043
|
function isBrowserTool(name) {
|
|
5992
6044
|
return BROWSER_TOOL_PREFIXES.some((p) => name.startsWith(p));
|
|
5993
6045
|
}
|
|
6046
|
+
var DIAG_HARD_CAP_MS = 5e3;
|
|
6047
|
+
var DIAG_DNS_TIMEOUT_MS = 2e3;
|
|
6048
|
+
var DIAG_TCP_TIMEOUT_MS = 3e3;
|
|
6049
|
+
var DIAG_HTTP_TIMEOUT_MS = 4e3;
|
|
6050
|
+
function quoteDiag(value) {
|
|
6051
|
+
return JSON.stringify(value);
|
|
6052
|
+
}
|
|
6053
|
+
function extractUrl(toolName, input) {
|
|
6054
|
+
if (input === null || typeof input !== "object") return void 0;
|
|
6055
|
+
const obj = input;
|
|
6056
|
+
if (toolName === "WebFetch" && typeof obj.url === "string") return obj.url;
|
|
6057
|
+
if (isBrowserTool(toolName) && typeof obj.url === "string") return obj.url;
|
|
6058
|
+
if (typeof obj.url === "string" && /^https?:\/\//.test(obj.url)) return obj.url;
|
|
6059
|
+
return void 0;
|
|
6060
|
+
}
|
|
6061
|
+
var FULL_REDACT_ENV_VARS = /* @__PURE__ */ new Set(["HTTPS_PROXY", "HTTP_PROXY", "NO_PROXY"]);
|
|
6062
|
+
function redactEnvField(name) {
|
|
6063
|
+
const value = process.env[name];
|
|
6064
|
+
if (!value) return `${name.toLowerCase()}=absent`;
|
|
6065
|
+
if (FULL_REDACT_ENV_VARS.has(name)) {
|
|
6066
|
+
return `${name.toLowerCase()}=present`;
|
|
6067
|
+
}
|
|
6068
|
+
const suffix = value.length > 40 ? value.slice(-40) : value;
|
|
6069
|
+
return `${name.toLowerCase()}=present suffix=${quoteDiag(suffix)}`;
|
|
6070
|
+
}
|
|
6071
|
+
async function probeDns(host, family) {
|
|
6072
|
+
const label = family === 4 ? "dns_a" : "dns_aaaa";
|
|
6073
|
+
const start = Date.now();
|
|
6074
|
+
let timer;
|
|
6075
|
+
try {
|
|
6076
|
+
const result = await Promise.race([
|
|
6077
|
+
dnsLookup(host, { family, verbatim: true }),
|
|
6078
|
+
new Promise((_, reject) => {
|
|
6079
|
+
timer = setTimeout(() => reject(new Error("timeout")), DIAG_DNS_TIMEOUT_MS);
|
|
6080
|
+
})
|
|
6081
|
+
]);
|
|
6082
|
+
if (timer) clearTimeout(timer);
|
|
6083
|
+
const ms = Date.now() - start;
|
|
6084
|
+
return `${label}=${result.address} ${label}_ms=${ms}`;
|
|
6085
|
+
} catch (err) {
|
|
6086
|
+
if (timer) clearTimeout(timer);
|
|
6087
|
+
const ms = Date.now() - start;
|
|
6088
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
6089
|
+
return `${label}=err ${label}_err=${quoteDiag(msg.slice(0, 60))} ${label}_ms=${ms}`;
|
|
6090
|
+
}
|
|
6091
|
+
}
|
|
6092
|
+
async function probeTcp(host, port2) {
|
|
6093
|
+
const start = Date.now();
|
|
6094
|
+
return new Promise((resolvePromise) => {
|
|
6095
|
+
let settled = false;
|
|
6096
|
+
const sock = netConnect({ host, port: port2, family: 0 });
|
|
6097
|
+
const finish = (result) => {
|
|
6098
|
+
if (settled) return;
|
|
6099
|
+
settled = true;
|
|
6100
|
+
try {
|
|
6101
|
+
sock.destroy();
|
|
6102
|
+
} catch {
|
|
6103
|
+
}
|
|
6104
|
+
resolvePromise(result);
|
|
6105
|
+
};
|
|
6106
|
+
const timer = setTimeout(() => finish(`tcp=timeout tcp_ms=${Date.now() - start}`), DIAG_TCP_TIMEOUT_MS);
|
|
6107
|
+
sock.once("connect", () => {
|
|
6108
|
+
clearTimeout(timer);
|
|
6109
|
+
finish(`tcp=ok tcp_ms=${Date.now() - start}`);
|
|
6110
|
+
});
|
|
6111
|
+
sock.once("error", (err) => {
|
|
6112
|
+
clearTimeout(timer);
|
|
6113
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
6114
|
+
finish(`tcp=err tcp_err=${quoteDiag(msg.slice(0, 60))} tcp_ms=${Date.now() - start}`);
|
|
6115
|
+
});
|
|
6116
|
+
});
|
|
6117
|
+
}
|
|
6118
|
+
async function probeHttp(url2) {
|
|
6119
|
+
const start = Date.now();
|
|
6120
|
+
const controller = new AbortController();
|
|
6121
|
+
const timer = setTimeout(() => controller.abort(), DIAG_HTTP_TIMEOUT_MS);
|
|
6122
|
+
try {
|
|
6123
|
+
const res = await fetch(url2, { method: "HEAD", redirect: "manual", signal: controller.signal });
|
|
6124
|
+
clearTimeout(timer);
|
|
6125
|
+
return `http_status=${res.status} http_ms=${Date.now() - start}`;
|
|
6126
|
+
} catch (err) {
|
|
6127
|
+
clearTimeout(timer);
|
|
6128
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
6129
|
+
return `http_status=err http_err=${quoteDiag(msg.slice(0, 60))} http_ms=${Date.now() - start}`;
|
|
6130
|
+
}
|
|
6131
|
+
}
|
|
6132
|
+
async function runFailureDiagnostic(toolName, toolInput) {
|
|
6133
|
+
const inputKeys = toolInput !== null && typeof toolInput === "object" ? Object.keys(toolInput).join(",") : "";
|
|
6134
|
+
const envFields = [
|
|
6135
|
+
redactEnvField("HTTPS_PROXY"),
|
|
6136
|
+
redactEnvField("HTTP_PROXY"),
|
|
6137
|
+
redactEnvField("NO_PROXY"),
|
|
6138
|
+
redactEnvField("NODE_OPTIONS")
|
|
6139
|
+
].join(" ");
|
|
6140
|
+
const url2 = extractUrl(toolName, toolInput);
|
|
6141
|
+
if (!url2) {
|
|
6142
|
+
return `diag_url=none input_keys=[${inputKeys}] ${envFields}`;
|
|
6143
|
+
}
|
|
6144
|
+
let host;
|
|
6145
|
+
let port2;
|
|
6146
|
+
try {
|
|
6147
|
+
const parsed = new URL(url2);
|
|
6148
|
+
host = parsed.hostname;
|
|
6149
|
+
port2 = parsed.port ? Number(parsed.port) : parsed.protocol === "https:" ? 443 : 80;
|
|
6150
|
+
} catch {
|
|
6151
|
+
return `diag_url=unparseable input_keys=[${inputKeys}] ${envFields}`;
|
|
6152
|
+
}
|
|
6153
|
+
const probes = Promise.allSettled([
|
|
6154
|
+
probeDns(host, 4),
|
|
6155
|
+
probeDns(host, 6),
|
|
6156
|
+
probeTcp(host, port2),
|
|
6157
|
+
probeHttp(url2)
|
|
6158
|
+
]);
|
|
6159
|
+
let capTimer;
|
|
6160
|
+
const capped = await Promise.race([
|
|
6161
|
+
probes,
|
|
6162
|
+
new Promise((resolvePromise) => {
|
|
6163
|
+
capTimer = setTimeout(() => resolvePromise("__diag_timeout__"), DIAG_HARD_CAP_MS);
|
|
6164
|
+
})
|
|
6165
|
+
]);
|
|
6166
|
+
if (capTimer) clearTimeout(capTimer);
|
|
6167
|
+
if (capped === "__diag_timeout__") {
|
|
6168
|
+
return `diag_host=${host} diag_port=${port2} diag_timeout=true input_keys=[${inputKeys}] ${envFields}`;
|
|
6169
|
+
}
|
|
6170
|
+
const fields = capped.map((r) => r.status === "fulfilled" ? r.value : `probe_err=${quoteDiag(String(r.reason).slice(0, 40))}`).join(" ");
|
|
6171
|
+
return `diag_host=${host} diag_port=${port2} ${fields} input_keys=[${inputKeys}] ${envFields}`;
|
|
6172
|
+
}
|
|
5994
6173
|
function agentLogStream(name, accountDir) {
|
|
5995
6174
|
const logDir = resolve6(accountDir, "logs");
|
|
5996
6175
|
mkdirSync4(logDir, { recursive: true });
|
|
@@ -6000,7 +6179,7 @@ function agentLogStream(name, accountDir) {
|
|
|
6000
6179
|
for (const file2 of readdirSync2(logDir)) {
|
|
6001
6180
|
if (!file2.startsWith(`${name}-`)) continue;
|
|
6002
6181
|
const filePath = resolve6(logDir, file2);
|
|
6003
|
-
if (
|
|
6182
|
+
if (statSync3(filePath).mtimeMs < cutoff) unlinkSync(filePath);
|
|
6004
6183
|
}
|
|
6005
6184
|
} catch {
|
|
6006
6185
|
}
|
|
@@ -6182,8 +6361,8 @@ function resolveAgentConfig(accountDir, agentName) {
|
|
|
6182
6361
|
const hasKnowledge = existsSync6(knowledgePath);
|
|
6183
6362
|
const hasSummary = existsSync6(summaryPath);
|
|
6184
6363
|
if (hasKnowledge && hasSummary) {
|
|
6185
|
-
const knowledgeMtime =
|
|
6186
|
-
const summaryMtime =
|
|
6364
|
+
const knowledgeMtime = statSync3(knowledgePath).mtimeMs;
|
|
6365
|
+
const summaryMtime = statSync3(summaryPath).mtimeMs;
|
|
6187
6366
|
if (summaryMtime >= knowledgeMtime) {
|
|
6188
6367
|
knowledge = readFileSync7(summaryPath, "utf-8");
|
|
6189
6368
|
} else {
|
|
@@ -6862,7 +7041,7 @@ ${specialist}: ${plugins.join(", ")}`);
|
|
|
6862
7041
|
for (const entry of readdirSync2(current)) {
|
|
6863
7042
|
const full = resolve6(current, entry);
|
|
6864
7043
|
try {
|
|
6865
|
-
const stat4 =
|
|
7044
|
+
const stat4 = statSync3(full);
|
|
6866
7045
|
if (stat4.isDirectory()) {
|
|
6867
7046
|
walk(full, `${rel}${entry}/`);
|
|
6868
7047
|
} else if (entry.endsWith(".md")) {
|
|
@@ -6993,6 +7172,15 @@ function resolveUserAccounts(userId) {
|
|
|
6993
7172
|
var sessionStore = /* @__PURE__ */ new Map();
|
|
6994
7173
|
setSessionStoreRef(sessionStore);
|
|
6995
7174
|
function registerSession(sessionKey, agentType, accountId, agentName, userId, userName) {
|
|
7175
|
+
const existing = sessionStore.get(sessionKey);
|
|
7176
|
+
if (existing) {
|
|
7177
|
+
existing.agentType = agentType;
|
|
7178
|
+
existing.accountId = accountId;
|
|
7179
|
+
existing.agentName = agentName ?? existing.agentName;
|
|
7180
|
+
existing.userId = userId ?? existing.userId;
|
|
7181
|
+
existing.userName = userName ?? existing.userName;
|
|
7182
|
+
return;
|
|
7183
|
+
}
|
|
6996
7184
|
sessionStore.set(sessionKey, { createdAt: Date.now(), agentType, accountId, agentName, userId, userName });
|
|
6997
7185
|
}
|
|
6998
7186
|
function registerResumedSession(sessionKey, accountId, agentName, conversationId, messages) {
|
|
@@ -7057,6 +7245,9 @@ function storeAgentSessionId(sessionKey, agentSessionId) {
|
|
|
7057
7245
|
const session = sessionStore.get(sessionKey);
|
|
7058
7246
|
if (session) {
|
|
7059
7247
|
session.agentSessionId = agentSessionId;
|
|
7248
|
+
console.error(`[session-store] storeAgentSessionId sessionKey=${sessionKey.slice(0, 12)}\u2026 sessionId=${agentSessionId.slice(0, 8)}\u2026`);
|
|
7249
|
+
} else {
|
|
7250
|
+
console.error(`[session-store] storeAgentSessionId SKIPPED \u2014 no session entry sessionKey=${sessionKey.slice(0, 12)}\u2026 sessionId=${agentSessionId.slice(0, 8)}\u2026`);
|
|
7060
7251
|
}
|
|
7061
7252
|
}
|
|
7062
7253
|
function getAgentSessionId(sessionKey) {
|
|
@@ -8033,14 +8224,19 @@ async function* runCompactionTurn(accountDir, accountId, systemPrompt, resumeSes
|
|
|
8033
8224
|
streamLog.end();
|
|
8034
8225
|
return { ok: capturedSummary !== void 0 && !timedOut, summary: capturedSummary };
|
|
8035
8226
|
}
|
|
8036
|
-
function clearAgentSessionId(sessionKey) {
|
|
8227
|
+
function clearAgentSessionId(sessionKey, reason) {
|
|
8037
8228
|
const session = sessionStore.get(sessionKey);
|
|
8038
8229
|
if (session) {
|
|
8039
8230
|
session.agentSessionId = void 0;
|
|
8231
|
+
console.error(`[session-store] clearAgentSessionId sessionKey=${sessionKey.slice(0, 12)}\u2026 reason=${reason}`);
|
|
8232
|
+
} else {
|
|
8233
|
+
console.error(`[session-store] clearAgentSessionId SKIPPED \u2014 no session entry sessionKey=${sessionKey.slice(0, 12)}\u2026 reason=${reason}`);
|
|
8040
8234
|
}
|
|
8041
8235
|
}
|
|
8042
|
-
async function* parseClaudeStream(proc, streamLog, adminModel) {
|
|
8236
|
+
async function* parseClaudeStream(proc, streamLog, adminModel, conversationId) {
|
|
8043
8237
|
const toolIdToName = /* @__PURE__ */ new Map();
|
|
8238
|
+
const toolIdToInput = /* @__PURE__ */ new Map();
|
|
8239
|
+
const convIdTag = conversationId ? ` conversationId=${conversationId.slice(0, 8)}` : "";
|
|
8044
8240
|
let emittedDone = false;
|
|
8045
8241
|
let buffer = "";
|
|
8046
8242
|
const modelCtxWindow = contextWindow(adminModel);
|
|
@@ -8236,15 +8432,18 @@ async function* parseClaudeStream(proc, streamLog, adminModel) {
|
|
|
8236
8432
|
yield { type: "session_resume", conversationId: resumeConversationId };
|
|
8237
8433
|
}
|
|
8238
8434
|
} else {
|
|
8239
|
-
if (block.id)
|
|
8435
|
+
if (block.id) {
|
|
8436
|
+
toolIdToName.set(block.id, block.name);
|
|
8437
|
+
toolIdToInput.set(block.id, block.input ?? {});
|
|
8438
|
+
}
|
|
8240
8439
|
const inputPreview = JSON.stringify(block.input ?? {}).slice(0, 200);
|
|
8241
8440
|
if (block.name === "Agent") {
|
|
8242
8441
|
const subType = block.input?.subagent_type ?? "general-purpose";
|
|
8243
8442
|
const desc = block.input?.description ?? "";
|
|
8244
|
-
streamLog.write(`[${isoTs()}] [agent-dispatch] subagent_type=${JSON.stringify(subType)} description=${JSON.stringify(desc)} input=${inputPreview}
|
|
8443
|
+
streamLog.write(`[${isoTs()}] [agent-dispatch]${convIdTag} subagent_type=${JSON.stringify(subType)} description=${JSON.stringify(desc)} input=${inputPreview}
|
|
8245
8444
|
`);
|
|
8246
8445
|
} else {
|
|
8247
|
-
streamLog.write(`[${isoTs()}] [tool-use] name=${block.name} input=${inputPreview}
|
|
8446
|
+
streamLog.write(`[${isoTs()}] [tool-use]${convIdTag} name=${block.name} input=${inputPreview}
|
|
8248
8447
|
`);
|
|
8249
8448
|
if (isBrowserTool(block.name)) {
|
|
8250
8449
|
vncLog("mcp-tool", { name: block.name, input_preview: inputPreview });
|
|
@@ -8276,10 +8475,10 @@ async function* parseClaudeStream(proc, streamLog, adminModel) {
|
|
|
8276
8475
|
}
|
|
8277
8476
|
const outputPreview = output.slice(0, 300);
|
|
8278
8477
|
if (name === "Agent") {
|
|
8279
|
-
streamLog.write(`[${isoTs()}] [agent-return] error=${!!block.is_error} output=${JSON.stringify(outputPreview)}
|
|
8478
|
+
streamLog.write(`[${isoTs()}] [agent-return]${convIdTag} error=${!!block.is_error} output=${JSON.stringify(outputPreview)}
|
|
8280
8479
|
`);
|
|
8281
8480
|
} else {
|
|
8282
|
-
streamLog.write(`[${isoTs()}] [tool-result] name=${name} error=${!!block.is_error} output=${JSON.stringify(outputPreview)}
|
|
8481
|
+
streamLog.write(`[${isoTs()}] [tool-result]${convIdTag} name=${name} error=${!!block.is_error} output=${JSON.stringify(outputPreview)}
|
|
8283
8482
|
`);
|
|
8284
8483
|
if (isBrowserTool(name)) {
|
|
8285
8484
|
vncLog("mcp-tool-result", {
|
|
@@ -8288,7 +8487,24 @@ async function* parseClaudeStream(proc, streamLog, adminModel) {
|
|
|
8288
8487
|
output_preview: outputPreview
|
|
8289
8488
|
});
|
|
8290
8489
|
}
|
|
8490
|
+
if (block.is_error && block.tool_use_id) {
|
|
8491
|
+
const toolInput = toolIdToInput.get(block.tool_use_id);
|
|
8492
|
+
try {
|
|
8493
|
+
const diag = await runFailureDiagnostic(name, toolInput);
|
|
8494
|
+
if (!streamLog.destroyed) {
|
|
8495
|
+
streamLog.write(`[${isoTs()}] [tool-failure-diag]${convIdTag} name=${name} ${diag}
|
|
8496
|
+
`);
|
|
8497
|
+
}
|
|
8498
|
+
} catch (err) {
|
|
8499
|
+
if (!streamLog.destroyed) {
|
|
8500
|
+
const msg2 = err instanceof Error ? err.message : String(err);
|
|
8501
|
+
streamLog.write(`[${isoTs()}] [tool-failure-diag]${convIdTag} name=${name} diag_err=${JSON.stringify(msg2.slice(0, 80))}
|
|
8502
|
+
`);
|
|
8503
|
+
}
|
|
8504
|
+
}
|
|
8505
|
+
}
|
|
8291
8506
|
}
|
|
8507
|
+
if (block.tool_use_id) toolIdToInput.delete(block.tool_use_id);
|
|
8292
8508
|
const renderDirectiveRe = /^__RENDER__(\{.+\})$/gm;
|
|
8293
8509
|
let renderMatch;
|
|
8294
8510
|
while ((renderMatch = renderDirectiveRe.exec(output)) !== null) {
|
|
@@ -8758,7 +8974,7 @@ async function* invokeAdminAgent(message, systemPrompt, accountDir, accountId, a
|
|
|
8758
8974
|
const toolCalls = [];
|
|
8759
8975
|
const pendingToolCalls = /* @__PURE__ */ new Map();
|
|
8760
8976
|
let capturedTokens;
|
|
8761
|
-
for await (const event of withSubagentHeartbeat(parseClaudeStream(proc, streamLog, adminModel), streamLog)) {
|
|
8977
|
+
for await (const event of withSubagentHeartbeat(parseClaudeStream(proc, streamLog, adminModel, spawnConvId), streamLog)) {
|
|
8762
8978
|
if (event.type === "session_init") {
|
|
8763
8979
|
currentAgentSessionId = event.sessionId;
|
|
8764
8980
|
if (sessionKey) storeAgentSessionId(sessionKey, event.sessionId);
|
|
@@ -8826,7 +9042,7 @@ async function* invokeAdminAgent(message, systemPrompt, accountDir, accountId, a
|
|
|
8826
9042
|
console.error(`[profile-reflection] Unhandled error: ${err instanceof Error ? err.message : String(err)}`);
|
|
8827
9043
|
});
|
|
8828
9044
|
}
|
|
8829
|
-
clearAgentSessionId(sessionKey);
|
|
9045
|
+
clearAgentSessionId(sessionKey, "compaction-complete");
|
|
8830
9046
|
const convId = sessionStore.get(sessionKey)?.conversationId;
|
|
8831
9047
|
if (convId) {
|
|
8832
9048
|
yield { type: "status", message: "Recovering context..." };
|
|
@@ -8890,7 +9106,7 @@ async function* invokeAdminAgent(message, systemPrompt, accountDir, accountId, a
|
|
|
8890
9106
|
} else if (event.type === "subagent_stalled") {
|
|
8891
9107
|
if (sessionKey) {
|
|
8892
9108
|
storeStalledSubagent(sessionKey, { name: event.name, task: event.task, prompt: event.prompt, elapsed_ms: event.elapsed_ms, tool_uses: event.tool_uses, total_tokens: event.total_tokens, last_tool_name: event.last_tool_name });
|
|
8893
|
-
clearAgentSessionId(sessionKey);
|
|
9109
|
+
clearAgentSessionId(sessionKey, "subagent-stalled");
|
|
8894
9110
|
}
|
|
8895
9111
|
const taskPreview = (event.task || "").length > 120 ? event.task.slice(0, 120) + "\u2026" : event.task || "";
|
|
8896
9112
|
streamLog.write(`[${isoTs()}] [stall-recovery] name=${event.name} task=${JSON.stringify(taskPreview)} \u2014 informing user and closing stream
|
|
@@ -8905,7 +9121,7 @@ async function* invokeAdminAgent(message, systemPrompt, accountDir, accountId, a
|
|
|
8905
9121
|
streamLog.write(`[${isoTs()}] [main-stream-stall-recovery] elapsed=${elapsedSec}s last_event=${event.last_event_type} \u2014 killing process and closing stream
|
|
8906
9122
|
`);
|
|
8907
9123
|
proc.kill("SIGTERM");
|
|
8908
|
-
if (sessionKey) clearAgentSessionId(sessionKey);
|
|
9124
|
+
if (sessionKey) clearAgentSessionId(sessionKey, "main-stream-stalled");
|
|
8909
9125
|
yield { type: "text", content: `The response stalled after ${elapsedSec} seconds of silence. Tap Retry to try again.` };
|
|
8910
9126
|
yield { type: "component", name: "action-buttons", data: { buttons: [{ label: "Retry", value: "Please retry my last request." }] } };
|
|
8911
9127
|
gotDone = true;
|
|
@@ -8915,7 +9131,7 @@ async function* invokeAdminAgent(message, systemPrompt, accountDir, accountId, a
|
|
|
8915
9131
|
if (event.subtype === "error_max_turns" && event.stop_reason === "tool_use") {
|
|
8916
9132
|
streamLog.write(`[${isoTs()}] [max-turns-interrupted] subtype=${event.subtype} stop_reason=${event.stop_reason} \u2014 entering recovery
|
|
8917
9133
|
`);
|
|
8918
|
-
if (sessionKey) clearAgentSessionId(sessionKey);
|
|
9134
|
+
if (sessionKey) clearAgentSessionId(sessionKey, "max-turns-interrupted");
|
|
8919
9135
|
} else {
|
|
8920
9136
|
gotDone = true;
|
|
8921
9137
|
if (!sessionWasReset) {
|
|
@@ -8951,7 +9167,7 @@ async function* invokeAdminAgent(message, systemPrompt, accountDir, accountId, a
|
|
|
8951
9167
|
yield event;
|
|
8952
9168
|
}
|
|
8953
9169
|
if (!gotDone) {
|
|
8954
|
-
if (sessionKey) clearAgentSessionId(sessionKey);
|
|
9170
|
+
if (sessionKey) clearAgentSessionId(sessionKey, "context-overflow-recovery");
|
|
8955
9171
|
const hasWork = responseText.length > 0 || toolCalls.length > 0;
|
|
8956
9172
|
if (hasWork && retryCount === 0 && sessionKey) {
|
|
8957
9173
|
streamLog.write(`[${isoTs()}] [context-overflow-recovery] detected sessionKey=${sessionKey.slice(0, 8)}\u2026 responseTextLen=${responseText.length} toolCallCount=${toolCalls.length}
|
|
@@ -9086,7 +9302,7 @@ async function* invokeManagedAdminAgent(message, systemPrompt, accountDir, accou
|
|
|
9086
9302
|
const pendingToolCalls = /* @__PURE__ */ new Map();
|
|
9087
9303
|
const renderedComponents = [];
|
|
9088
9304
|
let capturedTokens;
|
|
9089
|
-
for await (const event of withSubagentHeartbeat(parseClaudeStream(proc, streamLog, adminModel), streamLog)) {
|
|
9305
|
+
for await (const event of withSubagentHeartbeat(parseClaudeStream(proc, streamLog, adminModel, managedConvId), streamLog)) {
|
|
9090
9306
|
if (event.type === "text") {
|
|
9091
9307
|
responseText += event.content;
|
|
9092
9308
|
} else if (event.type === "usage") {
|
|
@@ -9588,7 +9804,7 @@ ${EXPLANATORY_STYLE_INSTRUCTIONS}` : baseSystemPrompt;
|
|
|
9588
9804
|
console.error(`[profile-reflection] Unhandled error: ${err instanceof Error ? err.message : String(err)}`);
|
|
9589
9805
|
});
|
|
9590
9806
|
}
|
|
9591
|
-
clearAgentSessionId(sessionKey);
|
|
9807
|
+
clearAgentSessionId(sessionKey, "session-compact-complete");
|
|
9592
9808
|
return { ok: step.value.ok };
|
|
9593
9809
|
}
|
|
9594
9810
|
async function* invokeAgent(config2, message, sessionKey, attachments = [], userTimestamp, gatewayResult) {
|
|
@@ -9658,8 +9874,9 @@ $ACCOUNT_DIR is ${account.accountDir}
|
|
|
9658
9874
|
${profileSummary}`;
|
|
9659
9875
|
}
|
|
9660
9876
|
}
|
|
9877
|
+
const invokeConvId = sessionKey ? getConversationIdForSession(sessionKey) : void 0;
|
|
9661
9878
|
const [sessionContext, onboardingStep] = await Promise.all([
|
|
9662
|
-
loadSessionContext(accountId),
|
|
9879
|
+
loadSessionContext(accountId, invokeConvId),
|
|
9663
9880
|
loadOnboardingStep(accountId)
|
|
9664
9881
|
]);
|
|
9665
9882
|
if (sessionContext) {
|
|
@@ -9874,7 +10091,7 @@ ${block}`;
|
|
|
9874
10091
|
import { basename as basename2 } from "path";
|
|
9875
10092
|
|
|
9876
10093
|
// app/lib/review-detector/rules.ts
|
|
9877
|
-
import { readFileSync as readFileSync8, writeFileSync as writeFileSync6, existsSync as existsSync7, statSync as
|
|
10094
|
+
import { readFileSync as readFileSync8, writeFileSync as writeFileSync6, existsSync as existsSync7, statSync as statSync4, mkdirSync as mkdirSync5, renameSync } from "fs";
|
|
9878
10095
|
import { resolve as resolve7, dirname as dirname2 } from "path";
|
|
9879
10096
|
var DEFAULT_SCAN_INTERVAL_MS = 5e3;
|
|
9880
10097
|
var RATE_LIMIT_PATTERN = "rate[- ]?limit(?:ed| reached| hit)|(?:HTTP|status)[^a-z]{0,3}429|too many requests";
|
|
@@ -9898,6 +10115,7 @@ var VALID_SOURCES = /* @__PURE__ */ new Set([
|
|
|
9898
10115
|
"mcp",
|
|
9899
10116
|
"config-dir"
|
|
9900
10117
|
]);
|
|
10118
|
+
var VALID_SCOPES = /* @__PURE__ */ new Set(["global", "session"]);
|
|
9901
10119
|
function defaultRules() {
|
|
9902
10120
|
return [
|
|
9903
10121
|
{
|
|
@@ -9972,6 +10190,20 @@ function defaultRules() {
|
|
|
9972
10190
|
thresholdCount: 0,
|
|
9973
10191
|
thresholdWindowMinutes: 0,
|
|
9974
10192
|
suggestedAction: "An external-facing tool executed with auto-executed approval state. Check account.json approvalPolicy \u2014 if this tool should require review, the gating hook may have been bypassed or the policy was changed."
|
|
10193
|
+
},
|
|
10194
|
+
{
|
|
10195
|
+
// Task 530: catches the bridgeai-style class where a single conversation
|
|
10196
|
+
// sees the same tool error repeatedly and the agent silently falls back.
|
|
10197
|
+
// Session-scoped so cross-conversation coincidence doesn't trigger it.
|
|
10198
|
+
id: "tool-result-recurring-errors",
|
|
10199
|
+
name: "Tool result errors recurring in a conversation",
|
|
10200
|
+
type: "repeated-error",
|
|
10201
|
+
logSource: "system",
|
|
10202
|
+
pattern: "\\[tool-result\\].*error=true",
|
|
10203
|
+
thresholdCount: 2,
|
|
10204
|
+
thresholdWindowMinutes: 5,
|
|
10205
|
+
scope: "session",
|
|
10206
|
+
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.'
|
|
9975
10207
|
}
|
|
9976
10208
|
];
|
|
9977
10209
|
}
|
|
@@ -10006,7 +10238,7 @@ function loadRules(configDir2) {
|
|
|
10006
10238
|
function rulesFileMtime(configDir2) {
|
|
10007
10239
|
const path2 = rulesFilePath(configDir2);
|
|
10008
10240
|
try {
|
|
10009
|
-
return
|
|
10241
|
+
return statSync4(path2).mtimeMs;
|
|
10010
10242
|
} catch {
|
|
10011
10243
|
return null;
|
|
10012
10244
|
}
|
|
@@ -10117,6 +10349,12 @@ function validateRule(input, label, seenIds) {
|
|
|
10117
10349
|
if (typeof r.watchPath === "string") rule.watchPath = r.watchPath;
|
|
10118
10350
|
if (typeof r.staleHours === "number") rule.staleHours = r.staleHours;
|
|
10119
10351
|
if (typeof r.suppressedUntil === "string") rule.suppressedUntil = r.suppressedUntil;
|
|
10352
|
+
if (r.scope !== void 0) {
|
|
10353
|
+
if (typeof r.scope !== "string" || !VALID_SCOPES.has(r.scope)) {
|
|
10354
|
+
throw new Error(`${label}: scope must be one of ${[...VALID_SCOPES].join(", ")}`);
|
|
10355
|
+
}
|
|
10356
|
+
rule.scope = r.scope;
|
|
10357
|
+
}
|
|
10120
10358
|
if (rule.type === "file-write-storm" || rule.type === "stale-log") {
|
|
10121
10359
|
if (!rule.watchPath) {
|
|
10122
10360
|
throw new Error(`${label}: ${rule.type} rules require watchPath`);
|
|
@@ -10131,7 +10369,7 @@ function validateRule(input, label, seenIds) {
|
|
|
10131
10369
|
}
|
|
10132
10370
|
|
|
10133
10371
|
// app/lib/review-detector/sources.ts
|
|
10134
|
-
import { existsSync as existsSync8, readdirSync as readdirSync3, statSync as
|
|
10372
|
+
import { existsSync as existsSync8, readdirSync as readdirSync3, statSync as statSync5, writeFileSync as writeFileSync7, renameSync as renameSync2, mkdirSync as mkdirSync6, openSync as openSync2, readSync as readSync2, closeSync as closeSync2, readFileSync as readFileSync9 } from "fs";
|
|
10135
10373
|
import { resolve as resolve8, join as join5, basename, dirname as dirname3 } from "path";
|
|
10136
10374
|
function tailStatePath(configDir2) {
|
|
10137
10375
|
return resolve8(configDir2, "review-state.json");
|
|
@@ -10202,7 +10440,7 @@ function discoverAllSources(configDir2, accountLogDir2) {
|
|
|
10202
10440
|
}
|
|
10203
10441
|
function readNewLines(filepath, prev) {
|
|
10204
10442
|
if (!existsSync8(filepath)) return null;
|
|
10205
|
-
const stat4 =
|
|
10443
|
+
const stat4 = statSync5(filepath);
|
|
10206
10444
|
const size = stat4.size;
|
|
10207
10445
|
const inode = stat4.ino;
|
|
10208
10446
|
let startOffset = 0;
|
|
@@ -10227,12 +10465,12 @@ function readNewLines(filepath, prev) {
|
|
|
10227
10465
|
truncated
|
|
10228
10466
|
};
|
|
10229
10467
|
}
|
|
10230
|
-
const fd =
|
|
10468
|
+
const fd = openSync2(filepath, "r");
|
|
10231
10469
|
try {
|
|
10232
10470
|
const bufSize = Math.max(0, size - startOffset);
|
|
10233
10471
|
const buf = Buffer.alloc(bufSize);
|
|
10234
10472
|
if (bufSize > 0) {
|
|
10235
|
-
|
|
10473
|
+
readSync2(fd, buf, 0, bufSize, startOffset);
|
|
10236
10474
|
}
|
|
10237
10475
|
const text = buf.toString("utf-8");
|
|
10238
10476
|
const lines = text.length > 0 ? text.split("\n") : [];
|
|
@@ -10250,7 +10488,7 @@ function readNewLines(filepath, prev) {
|
|
|
10250
10488
|
truncated
|
|
10251
10489
|
};
|
|
10252
10490
|
} finally {
|
|
10253
|
-
|
|
10491
|
+
closeSync2(fd);
|
|
10254
10492
|
}
|
|
10255
10493
|
}
|
|
10256
10494
|
function countRecentWrites(dir, sinceMs) {
|
|
@@ -10259,7 +10497,7 @@ function countRecentWrites(dir, sinceMs) {
|
|
|
10259
10497
|
for (const entry of readdirSync3(dir, { withFileTypes: true })) {
|
|
10260
10498
|
if (!entry.isFile()) continue;
|
|
10261
10499
|
try {
|
|
10262
|
-
const st =
|
|
10500
|
+
const st = statSync5(join5(dir, entry.name));
|
|
10263
10501
|
if (st.mtimeMs >= sinceMs) count += 1;
|
|
10264
10502
|
} catch {
|
|
10265
10503
|
}
|
|
@@ -10269,7 +10507,7 @@ function countRecentWrites(dir, sinceMs) {
|
|
|
10269
10507
|
function fileLastWriteMs(path2) {
|
|
10270
10508
|
if (!existsSync8(path2)) return null;
|
|
10271
10509
|
try {
|
|
10272
|
-
return
|
|
10510
|
+
return statSync5(path2).mtimeMs;
|
|
10273
10511
|
} catch {
|
|
10274
10512
|
return null;
|
|
10275
10513
|
}
|
|
@@ -10282,7 +10520,7 @@ function sourceKey(file2) {
|
|
|
10282
10520
|
}
|
|
10283
10521
|
|
|
10284
10522
|
// app/lib/review-detector/writer.ts
|
|
10285
|
-
import { appendFileSync as appendFileSync2, existsSync as existsSync9, mkdirSync as mkdirSync7, readFileSync as readFileSync10, writeFileSync as writeFileSync8, renameSync as renameSync3, statSync as
|
|
10523
|
+
import { appendFileSync as appendFileSync2, existsSync as existsSync9, mkdirSync as mkdirSync7, readFileSync as readFileSync10, writeFileSync as writeFileSync8, renameSync as renameSync3, statSync as statSync6 } from "fs";
|
|
10286
10524
|
import { resolve as resolve9, dirname as dirname4 } from "path";
|
|
10287
10525
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
10288
10526
|
function reviewLogPath(configDir2) {
|
|
@@ -10548,6 +10786,12 @@ import { resolve as resolve10 } from "path";
|
|
|
10548
10786
|
|
|
10549
10787
|
// app/lib/review-detector/evaluator.ts
|
|
10550
10788
|
var SAMPLE_MAX_CHARS = 500;
|
|
10789
|
+
var CONV_ID_REGEX = /conversationId=([a-f0-9]{8})/;
|
|
10790
|
+
function scopeKeyFor(rule, line) {
|
|
10791
|
+
if (rule.scope !== "session") return "";
|
|
10792
|
+
const m = CONV_ID_REGEX.exec(line);
|
|
10793
|
+
return m ? m[1] : "";
|
|
10794
|
+
}
|
|
10551
10795
|
var compiledRegexCache = /* @__PURE__ */ new Map();
|
|
10552
10796
|
function compileRegex(pattern) {
|
|
10553
10797
|
let re = compiledRegexCache.get(pattern);
|
|
@@ -10568,31 +10812,60 @@ function toSample(line) {
|
|
|
10568
10812
|
return line.slice(0, SAMPLE_MAX_CHARS) + "\u2026";
|
|
10569
10813
|
}
|
|
10570
10814
|
function newRuleState() {
|
|
10571
|
-
return {
|
|
10815
|
+
return {
|
|
10816
|
+
matchTimestamps: [],
|
|
10817
|
+
matchTimestampsByScope: /* @__PURE__ */ new Map(),
|
|
10818
|
+
lastAlertAt: null,
|
|
10819
|
+
cumulativeSinceLastAlert: 0,
|
|
10820
|
+
lastSeenAt: null
|
|
10821
|
+
};
|
|
10572
10822
|
}
|
|
10573
10823
|
function evaluateTextRule(rule, lines, state, nowMs) {
|
|
10574
10824
|
if (isSuppressed(rule, nowMs)) return { match: null, state };
|
|
10575
10825
|
if (rule.pattern.length === 0) return { match: null, state };
|
|
10576
10826
|
const regex = compileRegex(rule.pattern);
|
|
10577
|
-
|
|
10578
|
-
let
|
|
10827
|
+
const matchesByScope = /* @__PURE__ */ new Map();
|
|
10828
|
+
let firstSample = null;
|
|
10579
10829
|
for (const line of lines) {
|
|
10580
|
-
if (regex.test(line))
|
|
10581
|
-
|
|
10582
|
-
|
|
10583
|
-
|
|
10584
|
-
|
|
10585
|
-
|
|
10830
|
+
if (!regex.test(line)) continue;
|
|
10831
|
+
if (!firstSample) firstSample = line;
|
|
10832
|
+
const key = scopeKeyFor(rule, line);
|
|
10833
|
+
const existing = matchesByScope.get(key) ?? [];
|
|
10834
|
+
existing.push(line);
|
|
10835
|
+
matchesByScope.set(key, existing);
|
|
10836
|
+
}
|
|
10837
|
+
if (matchesByScope.size === 0) return { match: null, state };
|
|
10838
|
+
const windowStart = rule.thresholdWindowMinutes > 0 ? nowMs - rule.thresholdWindowMinutes * 6e4 : -Infinity;
|
|
10586
10839
|
const updated = {
|
|
10587
10840
|
...state,
|
|
10588
|
-
matchTimestamps: [...state.matchTimestamps]
|
|
10589
|
-
|
|
10590
|
-
|
|
10591
|
-
|
|
10592
|
-
|
|
10841
|
+
matchTimestamps: [...state.matchTimestamps],
|
|
10842
|
+
matchTimestampsByScope: new Map(state.matchTimestampsByScope ?? [])
|
|
10843
|
+
};
|
|
10844
|
+
let firingSample = null;
|
|
10845
|
+
let fires = false;
|
|
10846
|
+
const isCountZero = rule.thresholdCount === 0;
|
|
10847
|
+
if (rule.scope === "session") {
|
|
10848
|
+
for (const [key, hits] of matchesByScope) {
|
|
10849
|
+
const prior = updated.matchTimestampsByScope.get(key) ?? [];
|
|
10850
|
+
const merged = [...prior, ...hits.map(() => nowMs)].filter((t) => t >= windowStart);
|
|
10851
|
+
if (merged.length === 0) {
|
|
10852
|
+
updated.matchTimestampsByScope.delete(key);
|
|
10853
|
+
} else {
|
|
10854
|
+
updated.matchTimestampsByScope.set(key, merged);
|
|
10855
|
+
}
|
|
10856
|
+
if (!fires && (isCountZero || merged.length >= rule.thresholdCount)) {
|
|
10857
|
+
fires = true;
|
|
10858
|
+
firingSample = hits[0];
|
|
10859
|
+
}
|
|
10860
|
+
}
|
|
10861
|
+
} else {
|
|
10862
|
+
for (const hits of matchesByScope.values()) {
|
|
10863
|
+
for (const _ of hits) updated.matchTimestamps.push(nowMs);
|
|
10864
|
+
}
|
|
10593
10865
|
updated.matchTimestamps = updated.matchTimestamps.filter((t) => t >= windowStart);
|
|
10866
|
+
fires = isCountZero || updated.matchTimestamps.length >= rule.thresholdCount;
|
|
10867
|
+
if (fires) firingSample = firstSample;
|
|
10594
10868
|
}
|
|
10595
|
-
const fires = rule.thresholdCount === 0 ? true : updated.matchTimestamps.length >= rule.thresholdCount;
|
|
10596
10869
|
if (!fires) {
|
|
10597
10870
|
return { match: null, state: updated };
|
|
10598
10871
|
}
|
|
@@ -10600,7 +10873,7 @@ function evaluateTextRule(rule, lines, state, nowMs) {
|
|
|
10600
10873
|
ruleId: rule.id,
|
|
10601
10874
|
ruleName: rule.name,
|
|
10602
10875
|
matchedAt: nowMs,
|
|
10603
|
-
sampleEvidence: toSample(
|
|
10876
|
+
sampleEvidence: toSample(firingSample ?? firstSample ?? lines[0] ?? ""),
|
|
10604
10877
|
suggestedAction: rule.suggestedAction
|
|
10605
10878
|
};
|
|
10606
10879
|
return { match: match2, state: updated };
|
|
@@ -29925,7 +30198,7 @@ async function POST16(req) {
|
|
|
29925
30198
|
|
|
29926
30199
|
// app/api/onboarding/claude-auth/route.ts
|
|
29927
30200
|
import { spawn as spawn3, execFileSync as execFileSync2 } from "child_process";
|
|
29928
|
-
import { openSync as
|
|
30201
|
+
import { openSync as openSync3, closeSync as closeSync3, writeFileSync as writeFileSync12, writeSync } from "fs";
|
|
29929
30202
|
function checkAuthStatus() {
|
|
29930
30203
|
try {
|
|
29931
30204
|
execFileSync2("claude", ["auth", "status"], { encoding: "utf-8", timeout: 5e3 });
|
|
@@ -29996,7 +30269,7 @@ async function POST17(req, remoteAddress) {
|
|
|
29996
30269
|
const chromiumWrapper = writeChromiumWrapper();
|
|
29997
30270
|
const x11Env = buildX11Env(chromiumWrapper, transport);
|
|
29998
30271
|
vncLog("claude-auth", { action: "start", transport });
|
|
29999
|
-
const claudeAuthLogFd =
|
|
30272
|
+
const claudeAuthLogFd = openSync3(logPath("claude-auth"), "a");
|
|
30000
30273
|
const claudeProc = spawn3("claude", ["auth", "login"], {
|
|
30001
30274
|
env: x11Env,
|
|
30002
30275
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -30005,7 +30278,7 @@ async function POST17(req, remoteAddress) {
|
|
|
30005
30278
|
const onClaudeOutput = (chunk) => writeSync(claudeAuthLogFd, chunk);
|
|
30006
30279
|
claudeProc.stdout?.on("data", onClaudeOutput);
|
|
30007
30280
|
claudeProc.stderr?.on("data", onClaudeOutput);
|
|
30008
|
-
claudeProc.once("close", () =>
|
|
30281
|
+
claudeProc.once("close", () => closeSync3(claudeAuthLogFd));
|
|
30009
30282
|
await waitForAuthPage(2e4);
|
|
30010
30283
|
return Response.json({ started: true, transport });
|
|
30011
30284
|
}
|
|
@@ -30573,7 +30846,7 @@ async function POST22(req) {
|
|
|
30573
30846
|
}
|
|
30574
30847
|
|
|
30575
30848
|
// app/api/admin/logs/route.ts
|
|
30576
|
-
import { existsSync as existsSync19, readdirSync as readdirSync5, readFileSync as readFileSync20, statSync as
|
|
30849
|
+
import { existsSync as existsSync19, readdirSync as readdirSync5, readFileSync as readFileSync20, statSync as statSync7 } from "fs";
|
|
30577
30850
|
import { resolve as resolve19, basename as basename5 } from "path";
|
|
30578
30851
|
var TAIL_BYTES = 8192;
|
|
30579
30852
|
async function GET9(request) {
|
|
@@ -30634,7 +30907,7 @@ async function GET9(request) {
|
|
|
30634
30907
|
} catch {
|
|
30635
30908
|
continue;
|
|
30636
30909
|
}
|
|
30637
|
-
files.filter((f) => !seen.has(f)).map((f) => ({ name: f, mtime:
|
|
30910
|
+
files.filter((f) => !seen.has(f)).map((f) => ({ name: f, mtime: statSync7(resolve19(dir, f)).mtimeMs })).sort((a, b) => b.mtime - a.mtime).forEach(({ name }) => {
|
|
30638
30911
|
seen.add(name);
|
|
30639
30912
|
try {
|
|
30640
30913
|
const content = readFileSync20(resolve19(dir, name));
|
|
@@ -30894,7 +31167,7 @@ async function GET13() {
|
|
|
30894
31167
|
|
|
30895
31168
|
// app/api/admin/version/upgrade/route.ts
|
|
30896
31169
|
import { spawn as spawn4 } from "child_process";
|
|
30897
|
-
import { existsSync as existsSync24, statSync as
|
|
31170
|
+
import { existsSync as existsSync24, statSync as statSync8, writeFileSync as writeFileSync16, readFileSync as readFileSync24, openSync as openSync4, closeSync as closeSync4 } from "fs";
|
|
30898
31171
|
import { resolve as resolve25, join as join11 } from "path";
|
|
30899
31172
|
var PLATFORM_ROOT10 = process.env.MAXY_PLATFORM_ROOT ?? resolve25(process.cwd(), "..");
|
|
30900
31173
|
var upgradePkg = "@rubytech/create-maxy";
|
|
@@ -30914,7 +31187,7 @@ var LOCK_MAX_AGE_MS = 20 * 60 * 1e3;
|
|
|
30914
31187
|
function isLockFresh() {
|
|
30915
31188
|
if (!existsSync24(LOCK_FILE)) return false;
|
|
30916
31189
|
try {
|
|
30917
|
-
const stat4 =
|
|
31190
|
+
const stat4 = statSync8(LOCK_FILE);
|
|
30918
31191
|
return Date.now() - stat4.mtimeMs < LOCK_MAX_AGE_MS;
|
|
30919
31192
|
} catch {
|
|
30920
31193
|
return false;
|
|
@@ -30943,7 +31216,7 @@ async function POST23(req) {
|
|
|
30943
31216
|
console.error("[admin/version/upgrade] failed to write lock file:", err);
|
|
30944
31217
|
}
|
|
30945
31218
|
try {
|
|
30946
|
-
const logFd =
|
|
31219
|
+
const logFd = openSync4(LOG_FILE, "w");
|
|
30947
31220
|
const child = spawn4("systemd-run", [
|
|
30948
31221
|
"--user",
|
|
30949
31222
|
"--scope",
|
|
@@ -30959,7 +31232,7 @@ async function POST23(req) {
|
|
|
30959
31232
|
cwd: resolve25(process.cwd(), "..")
|
|
30960
31233
|
});
|
|
30961
31234
|
child.unref();
|
|
30962
|
-
|
|
31235
|
+
closeSync4(logFd);
|
|
30963
31236
|
console.log(`[admin/version/upgrade] spawned upgrade process (pid ${child.pid})`);
|
|
30964
31237
|
return Response.json({ ok: true, started: true });
|
|
30965
31238
|
} catch (err) {
|