@fangyb/ahchat-bridge 0.1.8 → 0.1.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +777 -276
- package/dist/index.js +721 -220
- package/package.json +32 -30
- package/dist/chunk-7SODRWIG.js +0 -4525
package/dist/cli.js
CHANGED
|
@@ -772,11 +772,11 @@ var RotatingFileStream = class extends Writable {
|
|
|
772
772
|
timeout;
|
|
773
773
|
timeoutPromise;
|
|
774
774
|
constructor(generator, options) {
|
|
775
|
-
const { encoding, history, maxFiles, maxSize, path:
|
|
775
|
+
const { encoding, history, maxFiles, maxSize, path: path13 } = options;
|
|
776
776
|
super({ decodeStrings: true, defaultEncoding: encoding });
|
|
777
777
|
this.createGzip = createGzip;
|
|
778
778
|
this.exec = exec;
|
|
779
|
-
this.filename =
|
|
779
|
+
this.filename = path13 + generator(null);
|
|
780
780
|
this.fsCreateReadStream = createReadStream;
|
|
781
781
|
this.fsCreateWriteStream = createWriteStream;
|
|
782
782
|
this.fsOpen = open;
|
|
@@ -788,7 +788,7 @@ var RotatingFileStream = class extends Writable {
|
|
|
788
788
|
this.options = options;
|
|
789
789
|
this.stdout = process.stdout;
|
|
790
790
|
if (maxFiles || maxSize)
|
|
791
|
-
options.history =
|
|
791
|
+
options.history = path13 + (history ? history : this.generator(null) + ".txt");
|
|
792
792
|
this.on("close", () => this.finished ? null : this.emit("finish"));
|
|
793
793
|
this.on("finish", () => this.finished = this.clear());
|
|
794
794
|
(async () => {
|
|
@@ -916,9 +916,9 @@ var RotatingFileStream = class extends Writable {
|
|
|
916
916
|
return this.move();
|
|
917
917
|
}
|
|
918
918
|
async findName() {
|
|
919
|
-
const { interval, path:
|
|
919
|
+
const { interval, path: path13, intervalBoundary } = this.options;
|
|
920
920
|
for (let index = 1; index < 1e3; ++index) {
|
|
921
|
-
const filename =
|
|
921
|
+
const filename = path13 + this.generator(interval && intervalBoundary ? new Date(this.prev) : this.rotation, index);
|
|
922
922
|
if (!await exists(filename))
|
|
923
923
|
return filename;
|
|
924
924
|
}
|
|
@@ -948,11 +948,11 @@ var RotatingFileStream = class extends Writable {
|
|
|
948
948
|
return this.unlink(filename);
|
|
949
949
|
}
|
|
950
950
|
async classical() {
|
|
951
|
-
const { compress, path:
|
|
951
|
+
const { compress, path: path13, rotate } = this.options;
|
|
952
952
|
let rotatedName = "";
|
|
953
953
|
for (let count = rotate; count > 0; --count) {
|
|
954
|
-
const currName =
|
|
955
|
-
const prevName = count === 1 ? this.filename :
|
|
954
|
+
const currName = path13 + this.generator(count);
|
|
955
|
+
const prevName = count === 1 ? this.filename : path13 + this.generator(count - 1);
|
|
956
956
|
if (!await exists(prevName))
|
|
957
957
|
continue;
|
|
958
958
|
if (!rotatedName)
|
|
@@ -1388,10 +1388,82 @@ function createModuleLogger(module) {
|
|
|
1388
1388
|
});
|
|
1389
1389
|
}
|
|
1390
1390
|
|
|
1391
|
+
// src/start.ts
|
|
1392
|
+
import path11 from "path";
|
|
1393
|
+
|
|
1394
|
+
// src/agentMemoryStore.ts
|
|
1395
|
+
import fs2 from "fs";
|
|
1396
|
+
import path4 from "path";
|
|
1397
|
+
var logger = createModuleLogger("agent.memoryStore");
|
|
1398
|
+
var NOTEBOOK_FILE_NAME = "notebook.md";
|
|
1399
|
+
var AGENT_ID_PATTERN = /^[a-zA-Z0-9_-]+$/;
|
|
1400
|
+
var AgentMemoryStore = class {
|
|
1401
|
+
rootDir;
|
|
1402
|
+
constructor(rootDir) {
|
|
1403
|
+
this.rootDir = rootDir;
|
|
1404
|
+
}
|
|
1405
|
+
read(agentId) {
|
|
1406
|
+
this.validateAgentId(agentId);
|
|
1407
|
+
const filePath = this.notebookPath(agentId);
|
|
1408
|
+
try {
|
|
1409
|
+
if (!fs2.existsSync(filePath)) {
|
|
1410
|
+
logger.info("Notebook read", { agentId, exists: false, bytes: 0 });
|
|
1411
|
+
return "";
|
|
1412
|
+
}
|
|
1413
|
+
const content = fs2.readFileSync(filePath, "utf-8");
|
|
1414
|
+
logger.info("Notebook read", { agentId, exists: true, bytes: content.length });
|
|
1415
|
+
return content;
|
|
1416
|
+
} catch (e) {
|
|
1417
|
+
logger.error("Failed to read notebook, returning empty", {
|
|
1418
|
+
agentId,
|
|
1419
|
+
path: filePath,
|
|
1420
|
+
error: e
|
|
1421
|
+
});
|
|
1422
|
+
return "";
|
|
1423
|
+
}
|
|
1424
|
+
}
|
|
1425
|
+
write(agentId, content) {
|
|
1426
|
+
this.validateAgentId(agentId);
|
|
1427
|
+
const dir = this.notebookDir(agentId);
|
|
1428
|
+
const filePath = this.notebookPath(agentId);
|
|
1429
|
+
const tmpPath = `${filePath}.tmp`;
|
|
1430
|
+
try {
|
|
1431
|
+
fs2.mkdirSync(dir, { recursive: true });
|
|
1432
|
+
fs2.writeFileSync(tmpPath, content, "utf-8");
|
|
1433
|
+
fs2.renameSync(tmpPath, filePath);
|
|
1434
|
+
logger.info("Notebook written", { agentId, bytes: content.length });
|
|
1435
|
+
} catch (e) {
|
|
1436
|
+
logger.error("Failed to write notebook", {
|
|
1437
|
+
agentId,
|
|
1438
|
+
path: filePath,
|
|
1439
|
+
error: e
|
|
1440
|
+
});
|
|
1441
|
+
throw e;
|
|
1442
|
+
}
|
|
1443
|
+
}
|
|
1444
|
+
append(agentId, snippet) {
|
|
1445
|
+
if (snippet.length === 0) return;
|
|
1446
|
+
const existing = this.read(agentId);
|
|
1447
|
+
const joiner = existing.length > 0 && !existing.endsWith("\n") ? "\n" : "";
|
|
1448
|
+
this.write(agentId, existing + joiner + snippet);
|
|
1449
|
+
}
|
|
1450
|
+
notebookDir(agentId) {
|
|
1451
|
+
return path4.join(this.rootDir, agentId);
|
|
1452
|
+
}
|
|
1453
|
+
notebookPath(agentId) {
|
|
1454
|
+
return path4.join(this.notebookDir(agentId), NOTEBOOK_FILE_NAME);
|
|
1455
|
+
}
|
|
1456
|
+
validateAgentId(agentId) {
|
|
1457
|
+
if (!AGENT_ID_PATTERN.test(agentId)) {
|
|
1458
|
+
throw new Error(`AgentMemoryStore: unsafe agentId "${agentId}"`);
|
|
1459
|
+
}
|
|
1460
|
+
}
|
|
1461
|
+
};
|
|
1462
|
+
|
|
1391
1463
|
// src/agentManager.ts
|
|
1392
|
-
import
|
|
1464
|
+
import fs3 from "fs/promises";
|
|
1393
1465
|
import os4 from "os";
|
|
1394
|
-
import
|
|
1466
|
+
import path7 from "path";
|
|
1395
1467
|
|
|
1396
1468
|
// ../shared/src/constants.ts
|
|
1397
1469
|
var NO_REPLY_TOKEN = "<no-reply/>";
|
|
@@ -1442,6 +1514,14 @@ with its own context, but they are all you. You have one tool to talk between th
|
|
|
1442
1514
|
- Returns immediately with a delivery receipt. You do NOT wait for a reply.
|
|
1443
1515
|
- Whether/how the other-scope self responds is its own decision.
|
|
1444
1516
|
|
|
1517
|
+
- neural_list_scopes(): Return the list of scopes where "you" exist (the only
|
|
1518
|
+
ones neural_send can reach). This list is also injected at the top of your
|
|
1519
|
+
system prompt at runtime start; only call this tool if you suspect it's
|
|
1520
|
+
stale (e.g., the user mentions a group that's not in your snapshot).
|
|
1521
|
+
|
|
1522
|
+
You are ONLY a member of the scopes shown in your "# Your scopes" section
|
|
1523
|
+
(injected at runtime start). neural_send to any other group will be rejected.
|
|
1524
|
+
|
|
1445
1525
|
When YOU receive a message wrapped as "[\u5185\u5FC3\u72EC\u767D \u2014 \u6765\u81EA\u4F60\u5728\u300C<scope>\u300D\u7684\u5206\u8EAB]":
|
|
1446
1526
|
- That is literally you, talking to yourself from another scope. It's private \u2014
|
|
1447
1527
|
nobody else in this scope hears it. Do NOT echo or quote the envelope text.
|
|
@@ -1455,6 +1535,31 @@ Pick neural_send whenever the user asks you to "tell people in group X ...", "as
|
|
|
1455
1535
|
me in group X ...", "let me know what you've been doing in X", or anything that
|
|
1456
1536
|
requires the you-in-another-scope to do something or share something. There is no
|
|
1457
1537
|
separate "recall" or "relay" tool \u2014 neural_send is the only one.
|
|
1538
|
+
|
|
1539
|
+
# Personal notebook (self_note)
|
|
1540
|
+
You have a personal notebook that travels with you across every scope (your 1:1 with
|
|
1541
|
+
the user AND every group you're in). Whatever you write to it now will appear at the
|
|
1542
|
+
top of your system prompt on your next turn, in any scope. Treat it as your long-term
|
|
1543
|
+
memory \u2014 the only thing about "you" that survives across conversations.
|
|
1544
|
+
|
|
1545
|
+
- self_note(action, content?):
|
|
1546
|
+
- "append" \u2014 add a new entry at the bottom of the notebook (most common).
|
|
1547
|
+
- "write" \u2014 replace the whole notebook (use to compact, reorganize, or correct).
|
|
1548
|
+
- "read" \u2014 fetch current contents. Your notebook is already at the top of this
|
|
1549
|
+
prompt, so you rarely need this; use only when you want to verify what's actually
|
|
1550
|
+
persisted (e.g., you suspect the prompt copy is stale after you just wrote).
|
|
1551
|
+
|
|
1552
|
+
Write to your notebook when:
|
|
1553
|
+
- You make a commitment that will outlive this conversation.
|
|
1554
|
+
- The user shares a stable preference or fact about themselves.
|
|
1555
|
+
- You form a position on a recurring topic that you want to keep consistent across
|
|
1556
|
+
every group and 1:1.
|
|
1557
|
+
|
|
1558
|
+
Do NOT write to your notebook for:
|
|
1559
|
+
- Throwaway calculations, small talk, or one-shot Q&A.
|
|
1560
|
+
- Anything that won't matter tomorrow.
|
|
1561
|
+
- Verbose minutes of a conversation \u2014 your future self has to re-read this every
|
|
1562
|
+
turn forever, so keep it lean. Quality over quantity.
|
|
1458
1563
|
`.trim();
|
|
1459
1564
|
var FAN_OUT_TRACE_TTL_MS = 10 * 6e4;
|
|
1460
1565
|
|
|
@@ -1582,7 +1687,7 @@ var InputController = class {
|
|
|
1582
1687
|
};
|
|
1583
1688
|
|
|
1584
1689
|
// src/askQuestionRegistry.ts
|
|
1585
|
-
var
|
|
1690
|
+
var logger2 = createModuleLogger("askQuestionRegistry");
|
|
1586
1691
|
var ASK_QUESTION_TIMEOUT_MS = 12e4;
|
|
1587
1692
|
var TIMEOUT_ANSWER = "[User did not respond within 120 seconds. Please decide whether to proceed with reasonable defaults or skip this step.]";
|
|
1588
1693
|
var AskQuestionRegistry = class {
|
|
@@ -1593,27 +1698,27 @@ var AskQuestionRegistry = class {
|
|
|
1593
1698
|
const timer = setTimeout(() => {
|
|
1594
1699
|
if (!this.entries.has(questionId)) return;
|
|
1595
1700
|
this.entries.delete(questionId);
|
|
1596
|
-
|
|
1701
|
+
logger2.warn("AskQuestion timeout", { questionId, agentId, timeoutMs });
|
|
1597
1702
|
try {
|
|
1598
1703
|
onTimeout();
|
|
1599
1704
|
} catch (e) {
|
|
1600
|
-
|
|
1705
|
+
logger2.error("onTimeout cb threw", { error: e });
|
|
1601
1706
|
}
|
|
1602
1707
|
resolve(TIMEOUT_ANSWER);
|
|
1603
1708
|
}, timeoutMs);
|
|
1604
1709
|
this.entries.set(questionId, { resolve, timer, agentId, askedAt: Date.now() });
|
|
1605
|
-
|
|
1710
|
+
logger2.info("AskQuestion registered", { questionId, agentId, timeoutMs });
|
|
1606
1711
|
});
|
|
1607
1712
|
}
|
|
1608
1713
|
resolve(questionId, answerText) {
|
|
1609
1714
|
const entry = this.entries.get(questionId);
|
|
1610
1715
|
if (!entry) {
|
|
1611
|
-
|
|
1716
|
+
logger2.warn("AskQuestion resolve: id not found (may be timed out)", { questionId });
|
|
1612
1717
|
return false;
|
|
1613
1718
|
}
|
|
1614
1719
|
clearTimeout(entry.timer);
|
|
1615
1720
|
this.entries.delete(questionId);
|
|
1616
|
-
|
|
1721
|
+
logger2.info("AskQuestion resolved", {
|
|
1617
1722
|
questionId,
|
|
1618
1723
|
agentId: entry.agentId,
|
|
1619
1724
|
waitedMs: Date.now() - entry.askedAt,
|
|
@@ -1625,7 +1730,7 @@ var AskQuestionRegistry = class {
|
|
|
1625
1730
|
}
|
|
1626
1731
|
cancelAll(reason) {
|
|
1627
1732
|
if (this.entries.size === 0) return;
|
|
1628
|
-
|
|
1733
|
+
logger2.warn("AskQuestion cancelAll", { reason, count: this.entries.size });
|
|
1629
1734
|
for (const [, entry] of this.entries) {
|
|
1630
1735
|
clearTimeout(entry.timer);
|
|
1631
1736
|
entry.resolve(`[${reason}]`);
|
|
@@ -1654,7 +1759,7 @@ function runtimeKey(agentId, scope) {
|
|
|
1654
1759
|
}
|
|
1655
1760
|
|
|
1656
1761
|
// src/askUserQuestionGuard.ts
|
|
1657
|
-
var
|
|
1762
|
+
var logger3 = createModuleLogger("askUserQuestionGuard");
|
|
1658
1763
|
function formatAnswerForSDK(p) {
|
|
1659
1764
|
const parts = ["[User Response]"];
|
|
1660
1765
|
if (p.selectedLabels.length > 0) {
|
|
@@ -1672,16 +1777,16 @@ function makeAskUserQuestionGuard(deps) {
|
|
|
1672
1777
|
return async (input) => {
|
|
1673
1778
|
const task = deps.getCurrentTask();
|
|
1674
1779
|
if (!task) {
|
|
1675
|
-
|
|
1780
|
+
logger3.error("AskUserQuestion received but no currentTask", { agentId: deps.agentId });
|
|
1676
1781
|
return { behavior: "deny", message: "[Internal error: no active task context]" };
|
|
1677
1782
|
}
|
|
1678
1783
|
const questions = input.questions ?? [];
|
|
1679
1784
|
if (questions.length === 0) {
|
|
1680
|
-
|
|
1785
|
+
logger3.warn("AskUserQuestion called with empty questions array", { agentId: deps.agentId });
|
|
1681
1786
|
return { behavior: "deny", message: "[Internal error: empty questions]" };
|
|
1682
1787
|
}
|
|
1683
1788
|
if (questions.length > 1) {
|
|
1684
|
-
|
|
1789
|
+
logger3.warn("AskUserQuestion received multi questions, Plan A only handles questions[0]", {
|
|
1685
1790
|
agentId: deps.agentId,
|
|
1686
1791
|
count: questions.length
|
|
1687
1792
|
});
|
|
@@ -1694,7 +1799,7 @@ function makeAskUserQuestionGuard(deps) {
|
|
|
1694
1799
|
description: o.description
|
|
1695
1800
|
}));
|
|
1696
1801
|
const multiSelect = Boolean(q.multiSelect);
|
|
1697
|
-
|
|
1802
|
+
logger3.info("AskUserQuestion intercepted, emitting agent:ask_user_question", {
|
|
1698
1803
|
agentId: deps.agentId,
|
|
1699
1804
|
scope: scopeKey(deps.scope),
|
|
1700
1805
|
groupId: task.groupId,
|
|
@@ -1722,7 +1827,7 @@ function makeAskUserQuestionGuard(deps) {
|
|
|
1722
1827
|
traceId: task.traceId
|
|
1723
1828
|
}
|
|
1724
1829
|
});
|
|
1725
|
-
|
|
1830
|
+
logger3.info("AskUserQuestion agent status awaiting_user", {
|
|
1726
1831
|
agentId: deps.agentId,
|
|
1727
1832
|
questionId,
|
|
1728
1833
|
groupId: task.groupId,
|
|
@@ -1745,7 +1850,7 @@ function makeAskUserQuestionGuard(deps) {
|
|
|
1745
1850
|
}
|
|
1746
1851
|
});
|
|
1747
1852
|
});
|
|
1748
|
-
|
|
1853
|
+
logger3.info("AskUserQuestion agent status thinking (resume SDK)", {
|
|
1749
1854
|
agentId: deps.agentId,
|
|
1750
1855
|
questionId,
|
|
1751
1856
|
groupId: task.groupId,
|
|
@@ -1755,7 +1860,7 @@ function makeAskUserQuestionGuard(deps) {
|
|
|
1755
1860
|
type: "agent:status",
|
|
1756
1861
|
payload: { agentId: deps.agentId, status: "thinking" }
|
|
1757
1862
|
});
|
|
1758
|
-
|
|
1863
|
+
logger3.info("AskUserQuestion answered, returning deny+message to SDK", {
|
|
1759
1864
|
agentId: deps.agentId,
|
|
1760
1865
|
questionId,
|
|
1761
1866
|
replyMessageId: task.replyMessageId,
|
|
@@ -1767,17 +1872,17 @@ function makeAskUserQuestionGuard(deps) {
|
|
|
1767
1872
|
}
|
|
1768
1873
|
|
|
1769
1874
|
// src/permissionGuard.ts
|
|
1770
|
-
import
|
|
1875
|
+
import path6 from "path";
|
|
1771
1876
|
|
|
1772
1877
|
// ../shared/src/utils/pathSafety.ts
|
|
1773
|
-
import
|
|
1878
|
+
import path5 from "path";
|
|
1774
1879
|
function isPathInside(parent, child) {
|
|
1775
|
-
const resolvedParent =
|
|
1776
|
-
const resolvedChild =
|
|
1880
|
+
const resolvedParent = path5.resolve(parent);
|
|
1881
|
+
const resolvedChild = path5.resolve(child);
|
|
1777
1882
|
if (resolvedParent === resolvedChild) return true;
|
|
1778
|
-
const rel =
|
|
1883
|
+
const rel = path5.relative(resolvedParent, resolvedChild);
|
|
1779
1884
|
if (rel === "") return true;
|
|
1780
|
-
return !rel.startsWith("..") && !
|
|
1885
|
+
return !rel.startsWith("..") && !path5.isAbsolute(rel);
|
|
1781
1886
|
}
|
|
1782
1887
|
|
|
1783
1888
|
// src/permissionGuard.ts
|
|
@@ -1792,7 +1897,7 @@ function makeCwdPermissionGuard(cwd, agentId, scope, log) {
|
|
|
1792
1897
|
if (typeof raw !== "string" || raw.length === 0) {
|
|
1793
1898
|
return { behavior: "allow" };
|
|
1794
1899
|
}
|
|
1795
|
-
const abs =
|
|
1900
|
+
const abs = path6.isAbsolute(raw) ? raw : path6.resolve(cwd, raw);
|
|
1796
1901
|
if (isPathInside(cwd, abs)) {
|
|
1797
1902
|
return { behavior: "allow" };
|
|
1798
1903
|
}
|
|
@@ -1805,7 +1910,7 @@ function makeCwdPermissionGuard(cwd, agentId, scope, log) {
|
|
|
1805
1910
|
}
|
|
1806
1911
|
|
|
1807
1912
|
// src/neuralMcpServer.ts
|
|
1808
|
-
var
|
|
1913
|
+
var logger4 = createModuleLogger("neural.mcpServer");
|
|
1809
1914
|
function formatScopeLabel(key, groupName) {
|
|
1810
1915
|
if (key === "single") return "\u5355\u804A";
|
|
1811
1916
|
if (groupName) return `\u7FA4\u300C${groupName}\u300D`;
|
|
@@ -1829,7 +1934,7 @@ async function createNeuralMcpServer(deps) {
|
|
|
1829
1934
|
message: z.string().describe('\u8981\u4F20\u7ED9\u76EE\u6807\u5206\u8EAB\u7684\u4E00\u6BB5\u81EA\u7136\u8BED\u8A00\u3002\u5B83\u4F1A\u4F5C\u4E3A\u4F60\u7684"\u5185\u5FC3\u72EC\u767D"\u51FA\u73B0\u5728\u90A3\u4E2A scope\u3002')
|
|
1830
1935
|
},
|
|
1831
1936
|
async (args) => {
|
|
1832
|
-
|
|
1937
|
+
logger4.info("neural_send tool called", {
|
|
1833
1938
|
agentId: deps.agentId,
|
|
1834
1939
|
fromScope: currentScopeKey,
|
|
1835
1940
|
rawTargetScope: args.target_scope,
|
|
@@ -1850,12 +1955,12 @@ async function createNeuralMcpServer(deps) {
|
|
|
1850
1955
|
if (singleConvId) {
|
|
1851
1956
|
conversationId = singleConvId;
|
|
1852
1957
|
} else {
|
|
1853
|
-
|
|
1958
|
+
logger4.warn("neural_send: failed to resolve single conv", { agentId: deps.agentId });
|
|
1854
1959
|
}
|
|
1855
1960
|
} else if (args.target_scope.startsWith("group:")) {
|
|
1856
1961
|
const r = await deps.groupRegistry.resolveScope(args.target_scope);
|
|
1857
1962
|
if (!r) {
|
|
1858
|
-
|
|
1963
|
+
logger4.info("neural_send: target scope not found", { rawTargetScope: args.target_scope });
|
|
1859
1964
|
return {
|
|
1860
1965
|
content: [{ type: "text", text: `[neural_send] \u627E\u4E0D\u5230\u7FA4\u300C${args.target_scope.slice(6)}\u300D\u3002\u8BF7\u786E\u8BA4\u7FA4\u540D\u662F\u5426\u6B63\u786E\u3002` }],
|
|
1861
1966
|
isError: true
|
|
@@ -1866,6 +1971,28 @@ async function createNeuralMcpServer(deps) {
|
|
|
1866
1971
|
groupId = r.groupId;
|
|
1867
1972
|
groupName = r.groupName;
|
|
1868
1973
|
targetCwd = r.workingDirectory;
|
|
1974
|
+
const cached = deps.groupRegistry.getById(r.groupId);
|
|
1975
|
+
if (!cached || !cached.members.includes(deps.agentId)) {
|
|
1976
|
+
logger4.info("neural_send: not a member of target group", {
|
|
1977
|
+
agentId: deps.agentId,
|
|
1978
|
+
groupId: r.groupId,
|
|
1979
|
+
groupName: r.groupName,
|
|
1980
|
+
cacheHit: !!cached,
|
|
1981
|
+
memberCount: cached?.members.length ?? 0
|
|
1982
|
+
});
|
|
1983
|
+
return {
|
|
1984
|
+
content: [{
|
|
1985
|
+
type: "text",
|
|
1986
|
+
text: `[neural_send] \u4F60\u4E0D\u662F\u300C${r.groupName}\u300D\u7684\u6210\u5458\uFF0C\u65E0\u6CD5\u5411\u90A3\u91CC\u53D1\u6D88\u606F\uFF08\u4F60\u5728\u90A3\u91CC\u6CA1\u6709"\u5206\u8EAB"\u53EF\u89E6\u8FBE\uFF09\u3002\u53EF\u8C03 neural_list_scopes() \u67E5\u770B\u4F60\u5F53\u524D\u7684\u5168\u90E8 scope\u3002`
|
|
1987
|
+
}],
|
|
1988
|
+
isError: true
|
|
1989
|
+
};
|
|
1990
|
+
}
|
|
1991
|
+
logger4.info("neural_send: member check passed", {
|
|
1992
|
+
agentId: deps.agentId,
|
|
1993
|
+
groupId: r.groupId,
|
|
1994
|
+
groupName: r.groupName
|
|
1995
|
+
});
|
|
1869
1996
|
} else {
|
|
1870
1997
|
return {
|
|
1871
1998
|
content: [{ type: "text", text: '[neural_send] target_scope \u5FC5\u987B\u662F "single" \u6216 "group:<\u7FA4\u540D\u6216 ID>"\u3002' }],
|
|
@@ -1873,7 +2000,7 @@ async function createNeuralMcpServer(deps) {
|
|
|
1873
2000
|
};
|
|
1874
2001
|
}
|
|
1875
2002
|
if (resolvedKey === currentScopeKey) {
|
|
1876
|
-
|
|
2003
|
+
logger4.warn("neural_send: self-send refused", { agentId: deps.agentId, scope: currentScopeKey });
|
|
1877
2004
|
return {
|
|
1878
2005
|
content: [{ type: "text", text: "[neural_send] \u4E0D\u80FD\u628A\u6D88\u606F\u9001\u7ED9\u81EA\u5DF1\u5F53\u524D\u6240\u5728\u7684 scope\u3002" }],
|
|
1879
2006
|
isError: true
|
|
@@ -1891,7 +2018,7 @@ async function createNeuralMcpServer(deps) {
|
|
|
1891
2018
|
groupId,
|
|
1892
2019
|
targetCwd
|
|
1893
2020
|
});
|
|
1894
|
-
|
|
2021
|
+
logger4.info("neural_send delivered", {
|
|
1895
2022
|
agentId: deps.agentId,
|
|
1896
2023
|
fromScope: currentScopeKey,
|
|
1897
2024
|
toScope: resolvedKey,
|
|
@@ -1901,7 +2028,7 @@ async function createNeuralMcpServer(deps) {
|
|
|
1901
2028
|
content: [{ type: "text", text: `[neural_send] \u5DF2\u9001\u8FBE\u5230\u300C${toLabel}\u300D(scope: ${resolvedKey})\u3002` }]
|
|
1902
2029
|
};
|
|
1903
2030
|
} catch (err) {
|
|
1904
|
-
|
|
2031
|
+
logger4.error("neural_send dispatch failed", { agentId: deps.agentId, error: err });
|
|
1905
2032
|
return {
|
|
1906
2033
|
content: [{ type: "text", text: `[neural_send] \u9001\u8FBE\u5931\u8D25\uFF1A${err.message}` }],
|
|
1907
2034
|
isError: true
|
|
@@ -1910,21 +2037,160 @@ async function createNeuralMcpServer(deps) {
|
|
|
1910
2037
|
},
|
|
1911
2038
|
{}
|
|
1912
2039
|
);
|
|
2040
|
+
const selfNote = deps.memoryStore ? sdk.tool(
|
|
2041
|
+
"self_note",
|
|
2042
|
+
`\u5728\u4F60\u7684"\u968F\u8EAB\u7B14\u8BB0\u672C"\u4E0A\u5199/\u8BFB\u4E00\u6BB5\u5185\u5BB9\u3002
|
|
2043
|
+
\u8FD9\u672C\u7B14\u8BB0\u672C\u8DE8\u6240\u6709 scope\uFF08\u5355\u804A + \u6240\u6709\u7FA4\uFF09\u5171\u4EAB\uFF0C\u5E76\u5728\u4F60\u6BCF\u6B21\u5F00\u59CB\u65B0\u4E00\u8F6E SDK turn \u65F6\u81EA\u52A8\u52A0\u8F7D\u5230\u4F60\u7684 system prompt \u9876\u90E8\u3002\u4E5F\u5C31\u662F\u8BF4\uFF1A\u4F60\u73B0\u5728\u5199\u7684\u5185\u5BB9\uFF0C\u4E0B\u4E00\u8F6E\u4F60\uFF08\u6216\u4F60\u5728\u522B\u7684 scope \u91CC\u7684\u5206\u8EAB\uFF09\u5C31\u4F1A"\u81EA\u7136\u8BB0\u5F97"\uFF0C\u65E0\u9700\u518D read\u3002
|
|
2044
|
+
action="append" \u8FFD\u52A0\u65B0\u5185\u5BB9\uFF08\u6700\u5E38\u7528\uFF0Ccontent \u5FC5\u586B\uFF09\uFF1B"write" \u6574\u6BB5\u8986\u76D6\u4EE5\u538B\u7F29/\u91CD\u6574\uFF08content \u5FC5\u586B\uFF09\uFF1B"read" \u53D6\u5F53\u524D\u5168\u6587\uFF08\u901A\u5E38\u4E0D\u9700\u8981\uFF0C\u56E0\u4E3A\u5185\u5BB9\u5DF2\u5728 prompt \u91CC\uFF09\u3002
|
|
2045
|
+
\u53EA\u8BB0\u8DE8\u5BF9\u8BDD\u6709\u4EF7\u503C\u7684\u4E1C\u897F\uFF1A\u627F\u8BFA\u3001\u7ACB\u573A\u3001\u7528\u6237\u957F\u671F\u504F\u597D\u3001\u9879\u76EE\u80CC\u666F\u3002\u4E0D\u8981\u8BB0\u4E00\u6B21\u6027\u95F2\u804A\u3001\u4E34\u65F6\u8BA1\u7B97\u3001\u5F53\u4E0B\u5C31\u591F\u7528\u7684\u4E0A\u4E0B\u6587\u3002`,
|
|
2046
|
+
{
|
|
2047
|
+
action: z.string().describe(
|
|
2048
|
+
'\u64CD\u4F5C\u7C7B\u578B\uFF0C\u5FC5\u987B\u662F\u4EE5\u4E0B\u4E4B\u4E00\uFF1A"read"\uFF08\u53D6\u5168\u6587\uFF09\u3001"append"\uFF08\u5728\u672B\u5C3E\u8FFD\u52A0 content\uFF09\u3001"write"\uFF08\u7528 content \u6574\u6BB5\u8986\u76D6\uFF09\u3002'
|
|
2049
|
+
),
|
|
2050
|
+
content: z.string().optional().describe(
|
|
2051
|
+
'\u8981\u5199\u5165\u7684\u5185\u5BB9\u3002action="append" \u6216 "write" \u65F6\u5FC5\u586B\uFF1Baction="read" \u65F6\u5FFD\u7565\u3002'
|
|
2052
|
+
)
|
|
2053
|
+
},
|
|
2054
|
+
async (args) => {
|
|
2055
|
+
const action = args.action;
|
|
2056
|
+
const content = args.content;
|
|
2057
|
+
logger4.info("self_note tool called", {
|
|
2058
|
+
agentId: deps.agentId,
|
|
2059
|
+
scope: currentScopeKey,
|
|
2060
|
+
action,
|
|
2061
|
+
contentLen: typeof content === "string" ? content.length : 0
|
|
2062
|
+
});
|
|
2063
|
+
if (action === "read") {
|
|
2064
|
+
const current = deps.memoryStore.read(deps.agentId);
|
|
2065
|
+
if (current.length === 0) {
|
|
2066
|
+
logger4.info("self_note read empty", {
|
|
2067
|
+
agentId: deps.agentId,
|
|
2068
|
+
scope: currentScopeKey
|
|
2069
|
+
});
|
|
2070
|
+
return {
|
|
2071
|
+
content: [{ type: "text", text: "[self_note] \u4F60\u7684\u7B14\u8BB0\u672C\u76EE\u524D\u662F\u7A7A\u7684\u3002" }]
|
|
2072
|
+
};
|
|
2073
|
+
}
|
|
2074
|
+
logger4.info("self_note read ok", {
|
|
2075
|
+
agentId: deps.agentId,
|
|
2076
|
+
scope: currentScopeKey,
|
|
2077
|
+
notebookBytes: current.length
|
|
2078
|
+
});
|
|
2079
|
+
return {
|
|
2080
|
+
content: [{ type: "text", text: current }]
|
|
2081
|
+
};
|
|
2082
|
+
}
|
|
2083
|
+
if (action === "append" || action === "write") {
|
|
2084
|
+
if (typeof content !== "string" || content.length === 0) {
|
|
2085
|
+
logger4.warn("self_note missing content", {
|
|
2086
|
+
agentId: deps.agentId,
|
|
2087
|
+
scope: currentScopeKey,
|
|
2088
|
+
action
|
|
2089
|
+
});
|
|
2090
|
+
return {
|
|
2091
|
+
content: [{ type: "text", text: `[self_note] action="${action}" \u65F6 content \u5FC5\u586B\u4E14\u975E\u7A7A\u3002` }],
|
|
2092
|
+
isError: true
|
|
2093
|
+
};
|
|
2094
|
+
}
|
|
2095
|
+
try {
|
|
2096
|
+
if (action === "append") {
|
|
2097
|
+
deps.memoryStore.append(deps.agentId, content);
|
|
2098
|
+
} else {
|
|
2099
|
+
deps.memoryStore.write(deps.agentId, content);
|
|
2100
|
+
}
|
|
2101
|
+
const after = deps.memoryStore.read(deps.agentId);
|
|
2102
|
+
logger4.info("self_note persisted", {
|
|
2103
|
+
agentId: deps.agentId,
|
|
2104
|
+
scope: currentScopeKey,
|
|
2105
|
+
action,
|
|
2106
|
+
contentLen: content.length,
|
|
2107
|
+
notebookBytesAfter: after.length
|
|
2108
|
+
});
|
|
2109
|
+
return {
|
|
2110
|
+
content: [{ type: "text", text: `[self_note] \u5DF2${action === "append" ? "\u8FFD\u52A0" : "\u8986\u76D6"}\uFF0C\u5F53\u524D\u7B14\u8BB0\u672C\u5171 ${after.length} \u5B57\u7B26\u3002` }]
|
|
2111
|
+
};
|
|
2112
|
+
} catch (err) {
|
|
2113
|
+
logger4.error("self_note write failed", { agentId: deps.agentId, action, error: err });
|
|
2114
|
+
return {
|
|
2115
|
+
content: [{ type: "text", text: `[self_note] \u5199\u5165\u5931\u8D25\uFF1A${err.message}` }],
|
|
2116
|
+
isError: true
|
|
2117
|
+
};
|
|
2118
|
+
}
|
|
2119
|
+
}
|
|
2120
|
+
logger4.warn("self_note invalid action", {
|
|
2121
|
+
agentId: deps.agentId,
|
|
2122
|
+
scope: currentScopeKey,
|
|
2123
|
+
action: String(action)
|
|
2124
|
+
});
|
|
2125
|
+
return {
|
|
2126
|
+
content: [{ type: "text", text: `[self_note] \u672A\u77E5 action "${String(action)}"\u3002\u5FC5\u987B\u662F "read" / "append" / "write" \u4E4B\u4E00\u3002` }],
|
|
2127
|
+
isError: true
|
|
2128
|
+
};
|
|
2129
|
+
},
|
|
2130
|
+
{}
|
|
2131
|
+
) : null;
|
|
2132
|
+
const neuralListScopes = sdk.tool(
|
|
2133
|
+
"neural_list_scopes",
|
|
2134
|
+
`\u5217\u51FA\u4F60\u5F53\u524D\u7684\u5168\u90E8"\u5206\u8EAB scope"\u2014\u2014\u4E5F\u5C31\u662F neural_send \u80FD\u89E6\u8FBE\u7684\u6240\u6709\u5730\u65B9\u3002
|
|
2135
|
+
\u8FD4\u56DE\u7ED3\u6784\u662F\u4E00\u6BB5 Markdown\uFF1A\u5305\u542B "single"\uFF08\u4F60\u548C\u7528\u6237\u7684 1:1\uFF09\u4EE5\u53CA\u4F60\u4F5C\u4E3A\u6210\u5458\u7684\u6BCF\u4E2A\u7FA4\u3002
|
|
2136
|
+
\u901A\u5E38\u4F60\u4E0D\u9700\u8981\u4E3B\u52A8\u8C03\u2014\u2014\u8FD9\u4EFD\u5217\u8868\u5DF2\u7ECF\u5728\u4F60\u7684 system prompt \u9876\u90E8\u9759\u6001\u6CE8\u5165\u8FC7\u3002\u4EC5\u5728\u4F60\u6000\u7591\u5217\u8868\u8FC7\u65F6\uFF08\u6BD4\u5982\u7528\u6237\u521A\u8BF4\u81EA\u5DF1\u521A\u62C9\u4F60\u8FDB\u4E86\u4E00\u4E2A\u7FA4\uFF0C\u4F46\u4F60\u7684\u5FEB\u7167\u91CC\u6CA1\u6709\uFF09\u65F6\u5237\u65B0\u4E00\u6B21\u3002`,
|
|
2137
|
+
{},
|
|
2138
|
+
async () => {
|
|
2139
|
+
logger4.info("neural_list_scopes tool called", {
|
|
2140
|
+
agentId: deps.agentId,
|
|
2141
|
+
scope: currentScopeKey
|
|
2142
|
+
});
|
|
2143
|
+
await deps.groupRegistry.refresh();
|
|
2144
|
+
const myGroups = deps.groupRegistry.getMyGroups(deps.agentId);
|
|
2145
|
+
logger4.info("neural_list_scopes: registry refreshed", {
|
|
2146
|
+
agentId: deps.agentId,
|
|
2147
|
+
scope: currentScopeKey,
|
|
2148
|
+
myGroupCount: myGroups.length
|
|
2149
|
+
});
|
|
2150
|
+
const lines = [];
|
|
2151
|
+
const isCurSingle = currentScopeKey === "single";
|
|
2152
|
+
lines.push(`- single \u2014 \u4F60\u548C\u7528\u6237\u7684 1:1 \u5355\u804A${isCurSingle ? " (\u4F60\u5F53\u524D\u6240\u5728)" : ""}`);
|
|
2153
|
+
for (const g of myGroups) {
|
|
2154
|
+
const key = `group:${g.groupId}`;
|
|
2155
|
+
const here = key === currentScopeKey ? " (\u4F60\u5F53\u524D\u6240\u5728)" : "";
|
|
2156
|
+
lines.push(`- ${key} \u2014 ${g.name}${here}`);
|
|
2157
|
+
}
|
|
2158
|
+
const text = [
|
|
2159
|
+
`\u4F60\u76EE\u524D\u6709 ${1 + myGroups.length} \u4E2A"\u5206\u8EAB scope"\uFF1A`,
|
|
2160
|
+
"",
|
|
2161
|
+
...lines,
|
|
2162
|
+
"",
|
|
2163
|
+
'\u8C03 neural_send(target_scope="...") \u65F6\u8BF7\u7528\u4E0A\u9762\u5217\u51FA\u7684 scope \u5B57\u7B26\u4E32\u3002',
|
|
2164
|
+
myGroups.length === 0 ? "\uFF08\u4F60\u4E0D\u662F\u4EFB\u4F55\u7FA4\u7684\u6210\u5458\uFF1B\u53EF\u4E0E\u7528\u6237\u5728 single \u901A\u8BDD\uFF0C\u4F46\u6682\u65F6\u6CA1\u6709\u8DE8\u7FA4\u5206\u8EAB\u53EF\u89E6\u8FBE\u3002\uFF09" : "\u82E5\u7528\u6237\u63D0\u5230\u7684\u7FA4\u540D\u4E0D\u5728\u5217\u8868\u91CC\uFF0C\u8BF4\u660E\u4F60\u4E0D\u662F\u8BE5\u7FA4\u6210\u5458\u2014\u2014\u522B\u5C1D\u8BD5 neural_send \u5230\u90A3\u4E2A\u7FA4\uFF08\u4F1A\u88AB\u62D2\uFF09\u3002"
|
|
2165
|
+
].join("\n");
|
|
2166
|
+
logger4.info("neural_list_scopes returned", {
|
|
2167
|
+
agentId: deps.agentId,
|
|
2168
|
+
scope: currentScopeKey,
|
|
2169
|
+
groupCount: myGroups.length
|
|
2170
|
+
});
|
|
2171
|
+
return { content: [{ type: "text", text }] };
|
|
2172
|
+
},
|
|
2173
|
+
{}
|
|
2174
|
+
);
|
|
2175
|
+
const tools = [neuralSend, neuralListScopes];
|
|
2176
|
+
if (selfNote) tools.push(selfNote);
|
|
1913
2177
|
const neuralServer = sdk.createSdkMcpServer({
|
|
1914
2178
|
name: "neural",
|
|
1915
2179
|
version: "2.0.0",
|
|
1916
|
-
tools
|
|
2180
|
+
tools
|
|
1917
2181
|
});
|
|
1918
|
-
|
|
2182
|
+
const toolNames = ["neural_send", "neural_list_scopes"];
|
|
2183
|
+
if (selfNote) toolNames.push("self_note");
|
|
2184
|
+
logger4.info("Neural MCP server created", {
|
|
1919
2185
|
agentId: deps.agentId,
|
|
1920
2186
|
scope: currentScopeKey,
|
|
1921
|
-
tools:
|
|
2187
|
+
tools: toolNames
|
|
1922
2188
|
});
|
|
1923
2189
|
return neuralServer;
|
|
1924
2190
|
}
|
|
1925
2191
|
|
|
1926
2192
|
// src/sdkEventMapper.ts
|
|
1927
|
-
var
|
|
2193
|
+
var logger5 = createModuleLogger("sdk.mapper");
|
|
1928
2194
|
function getTaskBase(proc) {
|
|
1929
2195
|
const task = proc.currentTask;
|
|
1930
2196
|
if (!task) return null;
|
|
@@ -1994,7 +2260,7 @@ function emitGroupSegment(proc, emit, base, content, contentBlocks) {
|
|
|
1994
2260
|
const groupId = proc.currentTask?.groupId;
|
|
1995
2261
|
if (!groupId) return;
|
|
1996
2262
|
proc.segmentCount += 1;
|
|
1997
|
-
|
|
2263
|
+
logger5.info("Group segment emitted", {
|
|
1998
2264
|
agentId: base.agentId,
|
|
1999
2265
|
replyMessageId: base.replyMessageId,
|
|
2000
2266
|
groupId,
|
|
@@ -2024,7 +2290,7 @@ function flushTextSegmentOnBlockStop(proc, emit, base) {
|
|
|
2024
2290
|
emitGroupSegment(proc, emit, base, proc.segmentBuffer, proc.contentBlocks);
|
|
2025
2291
|
proc.contentBlocks = [];
|
|
2026
2292
|
} else {
|
|
2027
|
-
|
|
2293
|
+
logger5.info("Group text block flushed but skipped (no segment emitted)", {
|
|
2028
2294
|
agentId: base.agentId,
|
|
2029
2295
|
replyMessageId: base.replyMessageId,
|
|
2030
2296
|
groupId: proc.currentTask?.groupId,
|
|
@@ -2048,7 +2314,7 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted) {
|
|
|
2048
2314
|
if (proc.status === "starting") {
|
|
2049
2315
|
proc.status = "ready";
|
|
2050
2316
|
}
|
|
2051
|
-
|
|
2317
|
+
logger5.info("Agent session initialized", {
|
|
2052
2318
|
agentId: proc.agentId,
|
|
2053
2319
|
scope: proc.scope.kind === "single" ? "single" : proc.scope.groupId,
|
|
2054
2320
|
sessionId: initMsg.session_id,
|
|
@@ -2111,7 +2377,7 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted) {
|
|
|
2111
2377
|
}
|
|
2112
2378
|
} else if (delta.type === "text_delta" && typeof delta.text === "string") {
|
|
2113
2379
|
if (proc.accumulatedText.length === 0) {
|
|
2114
|
-
|
|
2380
|
+
logger5.info("Agent text stream started", {
|
|
2115
2381
|
agentId: proc.agentId,
|
|
2116
2382
|
replyMessageId: base.replyMessageId,
|
|
2117
2383
|
traceId: base.traceId,
|
|
@@ -2150,7 +2416,7 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted) {
|
|
|
2150
2416
|
try {
|
|
2151
2417
|
parsedInput = JSON.parse(proc.accumulatedToolInput);
|
|
2152
2418
|
} catch {
|
|
2153
|
-
|
|
2419
|
+
logger5.warn("Failed to parse tool input JSON", {
|
|
2154
2420
|
agentId: proc.agentId,
|
|
2155
2421
|
toolName: proc.currentToolName,
|
|
2156
2422
|
inputLen: proc.accumulatedToolInput.length,
|
|
@@ -2165,7 +2431,7 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted) {
|
|
|
2165
2431
|
if (proc.currentToolName === "TodoWrite") {
|
|
2166
2432
|
const todos = extractTodosFromInput(parsedInput);
|
|
2167
2433
|
if (todos) {
|
|
2168
|
-
|
|
2434
|
+
logger5.info("TodoWrite detected, emitting agent:todos_update", {
|
|
2169
2435
|
agentId: proc.agentId,
|
|
2170
2436
|
replyMessageId: base.replyMessageId,
|
|
2171
2437
|
groupId: proc.currentTask?.groupId,
|
|
@@ -2182,7 +2448,7 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted) {
|
|
|
2182
2448
|
}
|
|
2183
2449
|
});
|
|
2184
2450
|
} else {
|
|
2185
|
-
|
|
2451
|
+
logger5.info("TodoWrite detected with empty/cancel todos", {
|
|
2186
2452
|
agentId: proc.agentId,
|
|
2187
2453
|
replyMessageId: base.replyMessageId,
|
|
2188
2454
|
traceId: base.traceId
|
|
@@ -2259,7 +2525,7 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted) {
|
|
|
2259
2525
|
const groupMode = groupId != null;
|
|
2260
2526
|
const usage = extractUsage(successMsg);
|
|
2261
2527
|
if (trimmed === NO_REPLY_TOKEN) {
|
|
2262
|
-
|
|
2528
|
+
logger5.info("Agent chose not to reply", {
|
|
2263
2529
|
agentId: proc.agentId,
|
|
2264
2530
|
replyMessageId: base.replyMessageId,
|
|
2265
2531
|
traceId: base.traceId,
|
|
@@ -2284,13 +2550,13 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted) {
|
|
|
2284
2550
|
}
|
|
2285
2551
|
if (groupMode) {
|
|
2286
2552
|
if (usage.inputTokens && usage.inputTokens > 15e4) {
|
|
2287
|
-
|
|
2553
|
+
logger5.warn("Agent context window approaching limit", {
|
|
2288
2554
|
agentId: proc.agentId,
|
|
2289
2555
|
inputTokens: usage.inputTokens
|
|
2290
2556
|
});
|
|
2291
2557
|
}
|
|
2292
2558
|
if (proc.contentBlocks.length > 0) {
|
|
2293
|
-
|
|
2559
|
+
logger5.info("Group turn trailing audit segment", {
|
|
2294
2560
|
agentId: proc.agentId,
|
|
2295
2561
|
replyMessageId: base.replyMessageId,
|
|
2296
2562
|
blockCount: proc.contentBlocks.length,
|
|
@@ -2299,7 +2565,7 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted) {
|
|
|
2299
2565
|
emitGroupSegment(proc, emit, base, "", proc.contentBlocks);
|
|
2300
2566
|
proc.contentBlocks = [];
|
|
2301
2567
|
}
|
|
2302
|
-
|
|
2568
|
+
logger5.info("Group task turn complete", {
|
|
2303
2569
|
agentId: proc.agentId,
|
|
2304
2570
|
replyMessageId: base.replyMessageId,
|
|
2305
2571
|
groupId,
|
|
@@ -2324,13 +2590,13 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted) {
|
|
|
2324
2590
|
proc.contentBlocks.push({ type: "text", content: proc.accumulatedText });
|
|
2325
2591
|
}
|
|
2326
2592
|
if (usage.inputTokens && usage.inputTokens > 15e4) {
|
|
2327
|
-
|
|
2593
|
+
logger5.warn("Agent context window approaching limit", {
|
|
2328
2594
|
agentId: proc.agentId,
|
|
2329
2595
|
inputTokens: usage.inputTokens
|
|
2330
2596
|
});
|
|
2331
2597
|
}
|
|
2332
2598
|
carrierMessageId = createMessageId();
|
|
2333
|
-
|
|
2599
|
+
logger5.info("Agent task done, emitting agent:done", {
|
|
2334
2600
|
agentId: proc.agentId,
|
|
2335
2601
|
ackId: base.replyMessageId,
|
|
2336
2602
|
messageId: carrierMessageId,
|
|
@@ -2357,7 +2623,7 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted) {
|
|
|
2357
2623
|
} else {
|
|
2358
2624
|
const errorMsg = resultMsg;
|
|
2359
2625
|
const errorText = errorMsg.errors?.join("; ") ?? `Agent error: ${resultMsg.subtype}`;
|
|
2360
|
-
|
|
2626
|
+
logger5.warn("Agent task error, emitting agent:error", {
|
|
2361
2627
|
agentId: proc.agentId,
|
|
2362
2628
|
replyMessageId: base.replyMessageId,
|
|
2363
2629
|
subtype: resultMsg.subtype,
|
|
@@ -2376,7 +2642,7 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted) {
|
|
|
2376
2642
|
case "assistant":
|
|
2377
2643
|
break;
|
|
2378
2644
|
default:
|
|
2379
|
-
|
|
2645
|
+
logger5.warn("Unhandled SDK message type", {
|
|
2380
2646
|
type: message.type,
|
|
2381
2647
|
agentId: proc.agentId
|
|
2382
2648
|
});
|
|
@@ -2396,7 +2662,7 @@ function resetAccumulators(proc) {
|
|
|
2396
2662
|
|
|
2397
2663
|
// src/wsMetrics.ts
|
|
2398
2664
|
import { monitorEventLoopDelay } from "perf_hooks";
|
|
2399
|
-
var
|
|
2665
|
+
var logger6 = createModuleLogger("ws.metrics");
|
|
2400
2666
|
var WsMetrics = class {
|
|
2401
2667
|
recv = /* @__PURE__ */ new Map();
|
|
2402
2668
|
send = /* @__PURE__ */ new Map();
|
|
@@ -2443,7 +2709,7 @@ var WsMetrics = class {
|
|
|
2443
2709
|
const sendSum = [...this.send.values()].reduce((a, b) => a + b, 0);
|
|
2444
2710
|
const sdkSum = [...this.sdkOut.values()].reduce((a, b) => a + b, 0);
|
|
2445
2711
|
if (recvSum + sendSum + sdkSum === 0 && (stats.loopMaxMs ?? 0) < 50) return;
|
|
2446
|
-
|
|
2712
|
+
logger6.info("WS metrics", {
|
|
2447
2713
|
windowMs: intervalMs,
|
|
2448
2714
|
...stats,
|
|
2449
2715
|
sums: { recv: recvSum, send: sendSum, sdkOut: sdkSum },
|
|
@@ -2459,7 +2725,19 @@ var WsMetrics = class {
|
|
|
2459
2725
|
var wsMetrics = new WsMetrics();
|
|
2460
2726
|
|
|
2461
2727
|
// src/agentManager.ts
|
|
2462
|
-
var
|
|
2728
|
+
var logger7 = createModuleLogger("agent.manager");
|
|
2729
|
+
var NOTEBOOK_SECTION_HEADER = `# Your personal notebook
|
|
2730
|
+
The following is your own running notebook. It is private to you and follows
|
|
2731
|
+
you across every scope (single chat and every group). Treat it as your memory
|
|
2732
|
+
of what you've decided, learned, or committed to.`;
|
|
2733
|
+
var SCOPES_SECTION_HEADER = `# Your scopes (where "you" exist)
|
|
2734
|
+
You currently have a presence in the following conversations. Each is a separate
|
|
2735
|
+
runtime of "you" with its own context. neural_send can ONLY reach scopes in this
|
|
2736
|
+
list \u2014 you have no "self" anywhere else.
|
|
2737
|
+
|
|
2738
|
+
This snapshot was taken when this runtime started. If you suspect it's stale
|
|
2739
|
+
(e.g., the user just told you they pulled you into a new group), call
|
|
2740
|
+
neural_list_scopes() once to refresh.`;
|
|
2463
2741
|
var BridgeBusyError = class extends Error {
|
|
2464
2742
|
constructor(message = "Bridge busy: cannot evict an idle Agent query; all slots are working") {
|
|
2465
2743
|
super(message);
|
|
@@ -2473,6 +2751,7 @@ var AgentManager = class {
|
|
|
2473
2751
|
emit;
|
|
2474
2752
|
workspacesDir;
|
|
2475
2753
|
claudeConfigDir;
|
|
2754
|
+
memoryStore;
|
|
2476
2755
|
queryConfig;
|
|
2477
2756
|
askQuestionRegistry;
|
|
2478
2757
|
groupRegistry;
|
|
@@ -2484,18 +2763,20 @@ var AgentManager = class {
|
|
|
2484
2763
|
this.emit = emit;
|
|
2485
2764
|
if (typeof options === "function") {
|
|
2486
2765
|
this.queryFn = options;
|
|
2487
|
-
this.workspacesDir =
|
|
2488
|
-
this.claudeConfigDir =
|
|
2766
|
+
this.workspacesDir = path7.join(os4.homedir(), ".ahchat", "workspaces");
|
|
2767
|
+
this.claudeConfigDir = path7.join(os4.homedir(), ".ahchat", "claude-config");
|
|
2489
2768
|
this.queryConfig = DEFAULT_QUERY_CONFIG;
|
|
2490
2769
|
this.askQuestionRegistry = new AskQuestionRegistry();
|
|
2491
2770
|
this.groupRegistry = null;
|
|
2771
|
+
this.memoryStore = null;
|
|
2492
2772
|
} else {
|
|
2493
2773
|
this.queryFn = options?.queryFn ?? null;
|
|
2494
|
-
this.workspacesDir = options?.workspacesDir ??
|
|
2495
|
-
this.claudeConfigDir = options?.claudeConfigDir ??
|
|
2774
|
+
this.workspacesDir = options?.workspacesDir ?? path7.join(os4.homedir(), ".ahchat", "workspaces");
|
|
2775
|
+
this.claudeConfigDir = options?.claudeConfigDir ?? path7.join(os4.homedir(), ".ahchat", "claude-config");
|
|
2496
2776
|
this.queryConfig = options?.queryConfig ?? DEFAULT_QUERY_CONFIG;
|
|
2497
2777
|
this.askQuestionRegistry = options?.askQuestionRegistry ?? new AskQuestionRegistry();
|
|
2498
2778
|
this.groupRegistry = options?.groupRegistry ?? null;
|
|
2779
|
+
this.memoryStore = options?.memoryStore ?? null;
|
|
2499
2780
|
}
|
|
2500
2781
|
this.evictionTimer = setInterval(() => {
|
|
2501
2782
|
void this.evictIdle();
|
|
@@ -2528,7 +2809,7 @@ var AgentManager = class {
|
|
|
2528
2809
|
})
|
|
2529
2810
|
]);
|
|
2530
2811
|
} catch (e) {
|
|
2531
|
-
|
|
2812
|
+
logger7.warn("awaitQueryReturn finished with error/timeout", { agentId, error: e });
|
|
2532
2813
|
}
|
|
2533
2814
|
}
|
|
2534
2815
|
/**
|
|
@@ -2551,13 +2832,13 @@ var AgentManager = class {
|
|
|
2551
2832
|
const proc = this.agents.get(key);
|
|
2552
2833
|
if (!proc || proc.status === "dead") return;
|
|
2553
2834
|
if (!this.isEvictable(proc)) return;
|
|
2554
|
-
|
|
2835
|
+
logger7.info("Evicting idle Agent query", { agentId: proc.agentId, scope: scopeKey(proc.scope) });
|
|
2555
2836
|
const runtime = this.asRuntime(proc);
|
|
2556
2837
|
try {
|
|
2557
2838
|
runtime.inputController.close();
|
|
2558
2839
|
await this.awaitQueryReturn(runtime.query, 5e3, proc.agentId);
|
|
2559
2840
|
} catch (e) {
|
|
2560
|
-
|
|
2841
|
+
logger7.error("closeIdleQuery failed", { agentId: proc.agentId, error: e });
|
|
2561
2842
|
}
|
|
2562
2843
|
proc.status = "dead";
|
|
2563
2844
|
this.agents.delete(key);
|
|
@@ -2623,9 +2904,9 @@ var AgentManager = class {
|
|
|
2623
2904
|
const savedSessionId = this.sessionStore.get(agentConfig.id, scope);
|
|
2624
2905
|
const inputController = new InputController();
|
|
2625
2906
|
const agentCwd = cwd;
|
|
2626
|
-
await
|
|
2907
|
+
await fs3.mkdir(agentCwd, { recursive: true });
|
|
2627
2908
|
const cfg = parseAgentConfig(agentConfig.config);
|
|
2628
|
-
|
|
2909
|
+
logger7.info("Creating Agent query", {
|
|
2629
2910
|
agentId: agentConfig.id,
|
|
2630
2911
|
scope: scopeKey(scope),
|
|
2631
2912
|
cwd: agentCwd,
|
|
@@ -2636,7 +2917,7 @@ var AgentManager = class {
|
|
|
2636
2917
|
const queryFn = await this.getQueryFn();
|
|
2637
2918
|
let procRef = null;
|
|
2638
2919
|
const cwdGuard = makeCwdPermissionGuard(agentCwd, agentConfig.id, scope, (msg, meta) => {
|
|
2639
|
-
|
|
2920
|
+
logger7.warn(msg, meta);
|
|
2640
2921
|
});
|
|
2641
2922
|
const askGuard = makeAskUserQuestionGuard({
|
|
2642
2923
|
agentId: agentConfig.id,
|
|
@@ -2649,14 +2930,17 @@ var AgentManager = class {
|
|
|
2649
2930
|
agentId: agentConfig.id,
|
|
2650
2931
|
scope,
|
|
2651
2932
|
groupRegistry: this.groupRegistry,
|
|
2652
|
-
onSend: (payload) => this.deliverNeuralSend(agentConfig, payload)
|
|
2933
|
+
onSend: (payload) => this.deliverNeuralSend(agentConfig, payload),
|
|
2934
|
+
memoryStore: this.memoryStore
|
|
2653
2935
|
});
|
|
2936
|
+
const notebookSection = this.buildNotebookSection(agentConfig.id);
|
|
2937
|
+
const scopesSection = this.buildScopesSection(agentConfig.id, scope);
|
|
2654
2938
|
const options = {
|
|
2655
2939
|
cwd: agentCwd,
|
|
2656
2940
|
systemPrompt: {
|
|
2657
2941
|
type: "preset",
|
|
2658
2942
|
preset: "claude_code",
|
|
2659
|
-
append: [PLATFORM_AGENT_RULES, agentConfig.systemPrompt].filter((s) => typeof s === "string" && s.trim().length > 0).join("\n\n")
|
|
2943
|
+
append: [PLATFORM_AGENT_RULES, agentConfig.systemPrompt, notebookSection, scopesSection].filter((s) => typeof s === "string" && s.trim().length > 0).join("\n\n")
|
|
2660
2944
|
},
|
|
2661
2945
|
permissionMode: "bypassPermissions",
|
|
2662
2946
|
allowDangerouslySkipPermissions: true,
|
|
@@ -2668,7 +2952,9 @@ var AgentManager = class {
|
|
|
2668
2952
|
"Glob",
|
|
2669
2953
|
"Grep",
|
|
2670
2954
|
"AskUserQuestion",
|
|
2671
|
-
"mcp__neural__neural_send"
|
|
2955
|
+
"mcp__neural__neural_send",
|
|
2956
|
+
"mcp__neural__neural_list_scopes",
|
|
2957
|
+
"mcp__neural__self_note"
|
|
2672
2958
|
],
|
|
2673
2959
|
mcpServers: { neural: neuralServer },
|
|
2674
2960
|
includePartialMessages: true,
|
|
@@ -2682,12 +2968,14 @@ var AgentManager = class {
|
|
|
2682
2968
|
};
|
|
2683
2969
|
const userPromptTrimmed = (agentConfig.systemPrompt ?? "").trim();
|
|
2684
2970
|
const appendStr = options.systemPrompt.append;
|
|
2685
|
-
|
|
2971
|
+
logger7.info("Platform rules attached", {
|
|
2686
2972
|
agentId: agentConfig.id,
|
|
2687
2973
|
scope: scopeKey(scope),
|
|
2688
2974
|
platformRulesLen: PLATFORM_AGENT_RULES.length,
|
|
2689
2975
|
userPromptLen: userPromptTrimmed.length,
|
|
2690
2976
|
hasUserPrompt: userPromptTrimmed.length > 0,
|
|
2977
|
+
notebookLen: notebookSection.length,
|
|
2978
|
+
scopesLen: scopesSection.length,
|
|
2691
2979
|
appendLen: appendStr.length
|
|
2692
2980
|
});
|
|
2693
2981
|
if (cfg.model) {
|
|
@@ -2729,6 +3017,58 @@ var AgentManager = class {
|
|
|
2729
3017
|
this.consumeOutput(runtime);
|
|
2730
3018
|
return proc;
|
|
2731
3019
|
}
|
|
3020
|
+
buildNotebookSection(agentId) {
|
|
3021
|
+
if (!this.memoryStore) {
|
|
3022
|
+
logger7.info("Notebook injection skipped", { agentId, reason: "no_memory_store" });
|
|
3023
|
+
return "";
|
|
3024
|
+
}
|
|
3025
|
+
const raw = this.memoryStore.read(agentId);
|
|
3026
|
+
const trimmed = raw.trim();
|
|
3027
|
+
if (trimmed.length === 0) {
|
|
3028
|
+
logger7.info("Notebook injection skipped", {
|
|
3029
|
+
agentId,
|
|
3030
|
+
reason: raw.length === 0 ? "empty_or_missing" : "whitespace_only",
|
|
3031
|
+
rawBytes: raw.length
|
|
3032
|
+
});
|
|
3033
|
+
return "";
|
|
3034
|
+
}
|
|
3035
|
+
const section = `${NOTEBOOK_SECTION_HEADER}
|
|
3036
|
+
|
|
3037
|
+
${trimmed}`;
|
|
3038
|
+
logger7.info("Notebook injected into system prompt", {
|
|
3039
|
+
agentId,
|
|
3040
|
+
rawBytes: raw.length,
|
|
3041
|
+
trimmedBytes: trimmed.length,
|
|
3042
|
+
sectionLen: section.length
|
|
3043
|
+
});
|
|
3044
|
+
return section;
|
|
3045
|
+
}
|
|
3046
|
+
buildScopesSection(agentId, scope) {
|
|
3047
|
+
if (!this.groupRegistry) {
|
|
3048
|
+
logger7.info("Scopes injection skipped", { agentId, reason: "no_group_registry" });
|
|
3049
|
+
return "";
|
|
3050
|
+
}
|
|
3051
|
+
const myGroups = this.groupRegistry.getMyGroups(agentId);
|
|
3052
|
+
const curKey = scopeKey(scope);
|
|
3053
|
+
const lines = [];
|
|
3054
|
+
const isCurSingle = curKey === "single";
|
|
3055
|
+
lines.push(`- single \u2014 1:1 chat with the user${isCurSingle ? " (you are here)" : ""}`);
|
|
3056
|
+
for (const g of myGroups) {
|
|
3057
|
+
const key = `group:${g.groupId}`;
|
|
3058
|
+
const here = key === curKey ? " (you are here)" : "";
|
|
3059
|
+
lines.push(`- ${key} \u2014 ${g.name}${here}`);
|
|
3060
|
+
}
|
|
3061
|
+
const section = `${SCOPES_SECTION_HEADER}
|
|
3062
|
+
|
|
3063
|
+
${lines.join("\n")}`;
|
|
3064
|
+
logger7.info("Scopes injected into system prompt", {
|
|
3065
|
+
agentId,
|
|
3066
|
+
scope: curKey,
|
|
3067
|
+
groupCount: myGroups.length,
|
|
3068
|
+
sectionLen: section.length
|
|
3069
|
+
});
|
|
3070
|
+
return section;
|
|
3071
|
+
}
|
|
2732
3072
|
async sendMessage(task) {
|
|
2733
3073
|
const key = runtimeKey(task.agentId, task.scope);
|
|
2734
3074
|
const proc = this.agents.get(key);
|
|
@@ -2741,7 +3081,7 @@ var AgentManager = class {
|
|
|
2741
3081
|
return;
|
|
2742
3082
|
}
|
|
2743
3083
|
if (proc.status === "starting") {
|
|
2744
|
-
|
|
3084
|
+
logger7.info("Message dispatched to starting Agent (kickstart)", {
|
|
2745
3085
|
agentId: task.agentId,
|
|
2746
3086
|
replyMessageId: task.replyMessageId,
|
|
2747
3087
|
traceId: task.traceId
|
|
@@ -2754,7 +3094,7 @@ var AgentManager = class {
|
|
|
2754
3094
|
if (idx >= 0) {
|
|
2755
3095
|
runtime.injectedTasks.splice(idx, 1);
|
|
2756
3096
|
runtime.mergedTasks.push(task);
|
|
2757
|
-
|
|
3097
|
+
logger7.info("Injected task consumed by SDK (queued as merged until next result)", {
|
|
2758
3098
|
agentId: runtime.agentId,
|
|
2759
3099
|
replyMessageId: task.replyMessageId,
|
|
2760
3100
|
traceId: task.traceId,
|
|
@@ -2765,7 +3105,7 @@ var AgentManager = class {
|
|
|
2765
3105
|
};
|
|
2766
3106
|
runtime.inputController.push(task.content, runtime.ccSessionId ?? "", onYielded);
|
|
2767
3107
|
runtime.injectedTasks.push(task);
|
|
2768
|
-
|
|
3108
|
+
logger7.info("Message injected while Agent working", {
|
|
2769
3109
|
agentId: task.agentId,
|
|
2770
3110
|
replyMessageId: task.replyMessageId,
|
|
2771
3111
|
currentStatus: proc.status,
|
|
@@ -2778,7 +3118,7 @@ var AgentManager = class {
|
|
|
2778
3118
|
runtime.currentTask = task;
|
|
2779
3119
|
runtime.currentTaskStartedAt = Date.now();
|
|
2780
3120
|
runtime.inputController.push(task.content, runtime.ccSessionId ?? "");
|
|
2781
|
-
|
|
3121
|
+
logger7.info("Message pushed to Agent", {
|
|
2782
3122
|
agentId: runtime.agentId,
|
|
2783
3123
|
replyMessageId: task.replyMessageId,
|
|
2784
3124
|
traceId: task.traceId
|
|
@@ -2798,7 +3138,7 @@ var AgentManager = class {
|
|
|
2798
3138
|
const completedTask = proc.currentTask;
|
|
2799
3139
|
if (completedTask && runtime.mergedTasks.length > 0) {
|
|
2800
3140
|
const mergedBatch = [...runtime.mergedTasks];
|
|
2801
|
-
|
|
3141
|
+
logger7.info("Flushing merged tasks after result", {
|
|
2802
3142
|
agentId: proc.agentId,
|
|
2803
3143
|
carrierReplyMessageId: completedTask.replyMessageId,
|
|
2804
3144
|
mergedCount: mergedBatch.length,
|
|
@@ -2806,7 +3146,7 @@ var AgentManager = class {
|
|
|
2806
3146
|
traceId: completedTask.traceId
|
|
2807
3147
|
});
|
|
2808
3148
|
for (const merged of mergedBatch) {
|
|
2809
|
-
|
|
3149
|
+
logger7.info("Emitting agent:merged for task consumed in same turn", {
|
|
2810
3150
|
agentId: proc.agentId,
|
|
2811
3151
|
ackId: merged.replyMessageId,
|
|
2812
3152
|
mergedIntoAckId: completedTask.replyMessageId,
|
|
@@ -2828,7 +3168,7 @@ var AgentManager = class {
|
|
|
2828
3168
|
}
|
|
2829
3169
|
runtime.mergedTasks = [];
|
|
2830
3170
|
} else if (runtime.mergedTasks.length > 0) {
|
|
2831
|
-
|
|
3171
|
+
logger7.warn("mergedTasks non-empty but no currentTask; dropping", {
|
|
2832
3172
|
agentId: proc.agentId,
|
|
2833
3173
|
mergedCount: runtime.mergedTasks.length
|
|
2834
3174
|
});
|
|
@@ -2840,7 +3180,7 @@ var AgentManager = class {
|
|
|
2840
3180
|
proc.currentTask = next;
|
|
2841
3181
|
proc.status = "working";
|
|
2842
3182
|
proc.currentTaskStartedAt = Date.now();
|
|
2843
|
-
|
|
3183
|
+
logger7.info("Promoted next injected task after result", {
|
|
2844
3184
|
agentId: proc.agentId,
|
|
2845
3185
|
replyMessageId: next.replyMessageId,
|
|
2846
3186
|
remainingInjected: runtime.injectedTasks.length,
|
|
@@ -2884,7 +3224,7 @@ var AgentManager = class {
|
|
|
2884
3224
|
};
|
|
2885
3225
|
const key = runtimeKey(agentConfig.id, targetScope);
|
|
2886
3226
|
const existingProc = this.agents.get(key);
|
|
2887
|
-
|
|
3227
|
+
logger7.info("Neural send dispatching", {
|
|
2888
3228
|
agentId: agentConfig.id,
|
|
2889
3229
|
fromScope: payload.fromScopeKey,
|
|
2890
3230
|
toScope: payload.toScopeKey,
|
|
@@ -2897,7 +3237,7 @@ var AgentManager = class {
|
|
|
2897
3237
|
});
|
|
2898
3238
|
if (existingProc && existingProc.status !== "dead") {
|
|
2899
3239
|
if (existingProc.status === "ready" || existingProc.status === "starting") {
|
|
2900
|
-
|
|
3240
|
+
logger7.info("Neural send dispatched to idle runtime", {
|
|
2901
3241
|
agentId: agentConfig.id,
|
|
2902
3242
|
toScope: payload.toScopeKey,
|
|
2903
3243
|
replyMessageId: task.replyMessageId,
|
|
@@ -2908,7 +3248,7 @@ var AgentManager = class {
|
|
|
2908
3248
|
const runtime = this.asRuntime(existingProc);
|
|
2909
3249
|
runtime.inputController.push(task.content, runtime.ccSessionId ?? "");
|
|
2910
3250
|
runtime.injectedTasks.push(task);
|
|
2911
|
-
|
|
3251
|
+
logger7.info("Neural send injected mid-turn", {
|
|
2912
3252
|
agentId: agentConfig.id,
|
|
2913
3253
|
toScope: payload.toScopeKey,
|
|
2914
3254
|
replyMessageId: task.replyMessageId,
|
|
@@ -2920,7 +3260,7 @@ var AgentManager = class {
|
|
|
2920
3260
|
let cwd;
|
|
2921
3261
|
if (targetScope.kind === "group") {
|
|
2922
3262
|
if (!payload.targetCwd) {
|
|
2923
|
-
|
|
3263
|
+
logger7.error("Neural send abort: group target missing targetCwd", {
|
|
2924
3264
|
agentId: agentConfig.id,
|
|
2925
3265
|
toScope: payload.toScopeKey
|
|
2926
3266
|
});
|
|
@@ -2928,10 +3268,10 @@ var AgentManager = class {
|
|
|
2928
3268
|
}
|
|
2929
3269
|
cwd = payload.targetCwd;
|
|
2930
3270
|
} else {
|
|
2931
|
-
cwd = agentConfig.workingDirectory ||
|
|
3271
|
+
cwd = agentConfig.workingDirectory || path7.join(this.workspacesDir, agentConfig.id);
|
|
2932
3272
|
}
|
|
2933
3273
|
void this.acquire(agentConfig, targetScope, cwd).then(() => {
|
|
2934
|
-
|
|
3274
|
+
logger7.info("Neural send new runtime acquired", {
|
|
2935
3275
|
agentId: agentConfig.id,
|
|
2936
3276
|
toScope: payload.toScopeKey,
|
|
2937
3277
|
cwd,
|
|
@@ -2939,7 +3279,7 @@ var AgentManager = class {
|
|
|
2939
3279
|
});
|
|
2940
3280
|
return this.sendMessage({ ...task, agentId: agentConfig.id, scope: targetScope });
|
|
2941
3281
|
}).catch((err) => {
|
|
2942
|
-
|
|
3282
|
+
logger7.error("Neural send acquire failed", {
|
|
2943
3283
|
agentId: agentConfig.id,
|
|
2944
3284
|
toScope: payload.toScopeKey,
|
|
2945
3285
|
cwd,
|
|
@@ -2955,7 +3295,7 @@ var AgentManager = class {
|
|
|
2955
3295
|
(k) => k === agentId || k.startsWith(`${agentId}::`)
|
|
2956
3296
|
);
|
|
2957
3297
|
if (keys.length === 0) {
|
|
2958
|
-
|
|
3298
|
+
logger7.warn("terminate: no process for agent", { agentId });
|
|
2959
3299
|
this.sessionStore.deleteAllForAgent(agentId);
|
|
2960
3300
|
return;
|
|
2961
3301
|
}
|
|
@@ -2966,19 +3306,19 @@ var AgentManager = class {
|
|
|
2966
3306
|
}
|
|
2967
3307
|
}
|
|
2968
3308
|
this.sessionStore.deleteAllForAgent(agentId);
|
|
2969
|
-
|
|
3309
|
+
logger7.info("terminate: all scoped queries removed", { agentId, count: keys.length });
|
|
2970
3310
|
}
|
|
2971
3311
|
/** Stop one scoped SDK runtime (workdir change). */
|
|
2972
3312
|
async terminateScope(agentId, scope) {
|
|
2973
3313
|
const key = runtimeKey(agentId, scope);
|
|
2974
3314
|
const proc = this.agents.get(key);
|
|
2975
3315
|
if (!proc) {
|
|
2976
|
-
|
|
3316
|
+
logger7.info("terminateScope: no active runtime", { agentId, scope: scopeKey(scope) });
|
|
2977
3317
|
return;
|
|
2978
3318
|
}
|
|
2979
3319
|
await this.closeRuntime(proc, "terminateScope");
|
|
2980
3320
|
this.sessionStore.delete(agentId, scope);
|
|
2981
|
-
|
|
3321
|
+
logger7.info("terminateScope: scoped query removed", { agentId, scope: scopeKey(scope) });
|
|
2982
3322
|
}
|
|
2983
3323
|
async closeRuntime(proc, reason) {
|
|
2984
3324
|
const key = runtimeKey(proc.agentId, proc.scope);
|
|
@@ -3008,7 +3348,7 @@ var AgentManager = class {
|
|
|
3008
3348
|
runtime.mergedTasks = [];
|
|
3009
3349
|
for (const t of mergedAtClose) {
|
|
3010
3350
|
if (runtime.currentTask) {
|
|
3011
|
-
|
|
3351
|
+
logger7.info("Emitting agent:merged on runtime close", {
|
|
3012
3352
|
agentId: runtime.agentId,
|
|
3013
3353
|
replyMessageId: t.replyMessageId,
|
|
3014
3354
|
mergedInto: runtime.currentTask.replyMessageId,
|
|
@@ -3035,45 +3375,57 @@ var AgentManager = class {
|
|
|
3035
3375
|
runtime.inputController.close();
|
|
3036
3376
|
await this.awaitQueryReturn(runtime.query, 5e3, agentId);
|
|
3037
3377
|
} catch (e) {
|
|
3038
|
-
|
|
3378
|
+
logger7.error(`${reason}: close query failed`, { agentId, scope: scopeKey(proc.scope), error: e });
|
|
3039
3379
|
}
|
|
3040
3380
|
proc.status = "dead";
|
|
3041
3381
|
this.agents.delete(key);
|
|
3042
3382
|
this.lastUsedAt.delete(key);
|
|
3043
|
-
|
|
3383
|
+
logger7.info(`${reason}: keeping workspace dir intact (per project policy)`, {
|
|
3044
3384
|
agentId,
|
|
3045
3385
|
scope: scopeKey(proc.scope),
|
|
3046
3386
|
cwd: proc.cwd
|
|
3047
3387
|
});
|
|
3048
3388
|
}
|
|
3049
3389
|
async recoverFromRestart(agents) {
|
|
3050
|
-
|
|
3390
|
+
logger7.info("Recovering Agent sessions after restart", { count: agents.length });
|
|
3051
3391
|
const agentsWithSession = agents.filter((a) => {
|
|
3052
3392
|
const sessionId = this.sessionStore.get(a.id, { kind: "single" });
|
|
3053
3393
|
return !!sessionId;
|
|
3054
3394
|
});
|
|
3055
3395
|
if (agentsWithSession.length === 0) {
|
|
3056
|
-
|
|
3396
|
+
logger7.info("No Agent sessions to recover");
|
|
3057
3397
|
return;
|
|
3058
3398
|
}
|
|
3059
3399
|
let warmed = 0;
|
|
3060
3400
|
const cap = this.queryConfig.maxActive;
|
|
3061
3401
|
for (const agent of agentsWithSession) {
|
|
3062
3402
|
if (warmed >= cap) {
|
|
3063
|
-
|
|
3403
|
+
logger7.info("Recovery warm cap reached", { cap, skipped: agentsWithSession.length - warmed });
|
|
3064
3404
|
break;
|
|
3065
3405
|
}
|
|
3066
3406
|
try {
|
|
3067
|
-
|
|
3407
|
+
let cwd = agent.workingDirectory || path7.join(this.workspacesDir, agent.id);
|
|
3408
|
+
if (agent.workingDirectory) {
|
|
3409
|
+
try {
|
|
3410
|
+
await fs3.mkdir(cwd, { recursive: true });
|
|
3411
|
+
} catch {
|
|
3412
|
+
cwd = path7.join(this.workspacesDir, agent.id);
|
|
3413
|
+
logger7.warn("Stored workingDirectory inaccessible, falling back", {
|
|
3414
|
+
agentId: agent.id,
|
|
3415
|
+
stored: agent.workingDirectory,
|
|
3416
|
+
fallback: cwd
|
|
3417
|
+
});
|
|
3418
|
+
}
|
|
3419
|
+
}
|
|
3068
3420
|
await this.acquire(agent, { kind: "single" }, cwd);
|
|
3069
3421
|
warmed++;
|
|
3070
|
-
|
|
3422
|
+
logger7.info("Agent process pre-created for recovery", { agentId: agent.id });
|
|
3071
3423
|
} catch (err) {
|
|
3072
3424
|
if (err instanceof BridgeBusyError) {
|
|
3073
|
-
|
|
3425
|
+
logger7.warn("Recovery stopped: bridge busy", { agentId: agent.id });
|
|
3074
3426
|
break;
|
|
3075
3427
|
}
|
|
3076
|
-
|
|
3428
|
+
logger7.warn("Failed to pre-create Agent for recovery, clearing session", {
|
|
3077
3429
|
agentId: agent.id,
|
|
3078
3430
|
error: err
|
|
3079
3431
|
});
|
|
@@ -3097,7 +3449,7 @@ var AgentManager = class {
|
|
|
3097
3449
|
} catch (err) {
|
|
3098
3450
|
const errMsg = err.message ?? String(err);
|
|
3099
3451
|
const isResumeFail = /session|conversation.*not found/i.test(errMsg);
|
|
3100
|
-
|
|
3452
|
+
logger7.error("Agent query stream ended with error", {
|
|
3101
3453
|
agentId: runtime.agentId,
|
|
3102
3454
|
scope: scopeKey(runtime.scope),
|
|
3103
3455
|
isResumeFail,
|
|
@@ -3105,7 +3457,7 @@ var AgentManager = class {
|
|
|
3105
3457
|
error: err
|
|
3106
3458
|
});
|
|
3107
3459
|
this.sessionStore.delete(runtime.agentId, runtime.scope);
|
|
3108
|
-
|
|
3460
|
+
logger7.info("Cleared stale session after query crash", {
|
|
3109
3461
|
agentId: runtime.agentId,
|
|
3110
3462
|
scope: scopeKey(runtime.scope)
|
|
3111
3463
|
});
|
|
@@ -3166,8 +3518,50 @@ var AgentManager = class {
|
|
|
3166
3518
|
}
|
|
3167
3519
|
return [...ids];
|
|
3168
3520
|
}
|
|
3521
|
+
/**
|
|
3522
|
+
* Push a system notice to ALL active runtimes of the given agent.
|
|
3523
|
+
*
|
|
3524
|
+
* Working runtimes: injected mid-turn via InputController (merged into
|
|
3525
|
+
* the current turn, no extra result expected).
|
|
3526
|
+
* Ready/starting runtimes: dispatched as a lightweight task so any SDK
|
|
3527
|
+
* output has valid replyMessageId/conversationId to land on.
|
|
3528
|
+
*/
|
|
3529
|
+
broadcastScopeNotice(agentId, notice) {
|
|
3530
|
+
let notified = 0;
|
|
3531
|
+
for (const [, proc] of this.agents) {
|
|
3532
|
+
if (proc.agentId !== agentId || proc.status === "dead") continue;
|
|
3533
|
+
const runtime = this.asRuntime(proc);
|
|
3534
|
+
if (proc.status === "working") {
|
|
3535
|
+
runtime.inputController.push(notice, runtime.ccSessionId ?? "");
|
|
3536
|
+
logger7.info("Scope notice injected mid-turn", {
|
|
3537
|
+
agentId,
|
|
3538
|
+
scope: scopeKey(proc.scope),
|
|
3539
|
+
noticeLen: notice.length
|
|
3540
|
+
});
|
|
3541
|
+
} else if (proc.status === "ready" || proc.status === "starting") {
|
|
3542
|
+
const task = {
|
|
3543
|
+
content: notice,
|
|
3544
|
+
replyMessageId: `msg_scopenotice_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`,
|
|
3545
|
+
conversationId: proc.currentTask?.conversationId ?? "",
|
|
3546
|
+
traceId: `tr_scopenotice_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`,
|
|
3547
|
+
groupId: proc.scope.kind === "group" ? proc.scope.groupId : void 0
|
|
3548
|
+
};
|
|
3549
|
+
this.dispatchToSDK(runtime, task);
|
|
3550
|
+
logger7.info("Scope notice dispatched to idle runtime", {
|
|
3551
|
+
agentId,
|
|
3552
|
+
scope: scopeKey(proc.scope),
|
|
3553
|
+
replyMessageId: task.replyMessageId
|
|
3554
|
+
});
|
|
3555
|
+
}
|
|
3556
|
+
notified++;
|
|
3557
|
+
}
|
|
3558
|
+
logger7.info("broadcastScopeNotice completed", {
|
|
3559
|
+
agentId,
|
|
3560
|
+
notifiedCount: notified
|
|
3561
|
+
});
|
|
3562
|
+
}
|
|
3169
3563
|
async shutdownAll() {
|
|
3170
|
-
|
|
3564
|
+
logger7.info("Shutting down all Agent processes", { count: this.agents.size });
|
|
3171
3565
|
this.askQuestionRegistry.cancelAll("agent_aborted");
|
|
3172
3566
|
if (this.evictionTimer) {
|
|
3173
3567
|
clearInterval(this.evictionTimer);
|
|
@@ -3179,9 +3573,9 @@ var AgentManager = class {
|
|
|
3179
3573
|
runtime.inputController?.close();
|
|
3180
3574
|
runtime.query?.return(void 0);
|
|
3181
3575
|
proc.status = "dead";
|
|
3182
|
-
|
|
3576
|
+
logger7.info("Agent process shut down", { agentId: proc.agentId, scope: scopeKey(proc.scope), key });
|
|
3183
3577
|
} catch (err) {
|
|
3184
|
-
|
|
3578
|
+
logger7.error("Error shutting down Agent", { agentId: proc.agentId, error: err });
|
|
3185
3579
|
}
|
|
3186
3580
|
}
|
|
3187
3581
|
this.agents.clear();
|
|
@@ -3198,13 +3592,13 @@ var AgentManager = class {
|
|
|
3198
3592
|
}
|
|
3199
3593
|
}
|
|
3200
3594
|
if (!proc) {
|
|
3201
|
-
|
|
3595
|
+
logger7.warn("cancelReply: no active process for reply", { agentId, replyMessageId });
|
|
3202
3596
|
return;
|
|
3203
3597
|
}
|
|
3204
3598
|
const runtime = this.asRuntime(proc);
|
|
3205
3599
|
const key = runtimeKey(agentId, proc.scope);
|
|
3206
3600
|
if (!runtime.currentTask || runtime.currentTask.replyMessageId !== replyMessageId) {
|
|
3207
|
-
|
|
3601
|
+
logger7.warn("cancelReply: replyMessageId mismatch", {
|
|
3208
3602
|
agentId,
|
|
3209
3603
|
replyMessageId,
|
|
3210
3604
|
expected: runtime.currentTask?.replyMessageId
|
|
@@ -3233,7 +3627,7 @@ var AgentManager = class {
|
|
|
3233
3627
|
proc.status = "dead";
|
|
3234
3628
|
this.agents.delete(key);
|
|
3235
3629
|
this.lastUsedAt.delete(key);
|
|
3236
|
-
|
|
3630
|
+
logger7.info("cancelReply: process torn down", {
|
|
3237
3631
|
agentId,
|
|
3238
3632
|
scope: scopeKey(proc.scope),
|
|
3239
3633
|
conversationId,
|
|
@@ -3242,10 +3636,10 @@ var AgentManager = class {
|
|
|
3242
3636
|
try {
|
|
3243
3637
|
runtime.inputController.close();
|
|
3244
3638
|
} catch (err) {
|
|
3245
|
-
|
|
3639
|
+
logger7.error("cancelReply: inputController.close failed", { agentId, error: err });
|
|
3246
3640
|
}
|
|
3247
3641
|
runtime.query.return(void 0).catch((err) => {
|
|
3248
|
-
|
|
3642
|
+
logger7.warn("cancelReply: query.return threw", { agentId, error: err });
|
|
3249
3643
|
});
|
|
3250
3644
|
}
|
|
3251
3645
|
};
|
|
@@ -3262,7 +3656,7 @@ function buildInnerVoiceEnvelope(payload) {
|
|
|
3262
3656
|
}
|
|
3263
3657
|
|
|
3264
3658
|
// src/agentRegistry.ts
|
|
3265
|
-
var
|
|
3659
|
+
var logger8 = createModuleLogger("agent.registry");
|
|
3266
3660
|
var HttpAgentRegistry = class {
|
|
3267
3661
|
constructor(serverApiUrl) {
|
|
3268
3662
|
this.serverApiUrl = serverApiUrl;
|
|
@@ -3271,8 +3665,8 @@ var HttpAgentRegistry = class {
|
|
|
3271
3665
|
agents = /* @__PURE__ */ new Map();
|
|
3272
3666
|
apiUrl(suffix) {
|
|
3273
3667
|
const base = this.serverApiUrl.replace(/\/$/, "");
|
|
3274
|
-
const
|
|
3275
|
-
return `${base}${
|
|
3668
|
+
const path13 = suffix.startsWith("/") ? suffix : `/${suffix}`;
|
|
3669
|
+
return `${base}${path13}`;
|
|
3276
3670
|
}
|
|
3277
3671
|
async refresh() {
|
|
3278
3672
|
const attempt = async () => {
|
|
@@ -3290,17 +3684,17 @@ var HttpAgentRegistry = class {
|
|
|
3290
3684
|
recoveredAfterRetry = res != null;
|
|
3291
3685
|
}
|
|
3292
3686
|
if (!res) {
|
|
3293
|
-
|
|
3687
|
+
logger8.warn("Agent registry refresh unreachable after retry, keeping cache");
|
|
3294
3688
|
return;
|
|
3295
3689
|
}
|
|
3296
3690
|
if (!res.ok) {
|
|
3297
|
-
|
|
3691
|
+
logger8.warn("Agent registry refresh failed", { status: res.status });
|
|
3298
3692
|
return;
|
|
3299
3693
|
}
|
|
3300
3694
|
try {
|
|
3301
3695
|
const body = await res.json();
|
|
3302
3696
|
if (!Array.isArray(body)) {
|
|
3303
|
-
|
|
3697
|
+
logger8.warn("Agent registry refresh: expected array");
|
|
3304
3698
|
return;
|
|
3305
3699
|
}
|
|
3306
3700
|
this.agents.clear();
|
|
@@ -3310,9 +3704,9 @@ var HttpAgentRegistry = class {
|
|
|
3310
3704
|
this.agents.set(a.id, a);
|
|
3311
3705
|
}
|
|
3312
3706
|
}
|
|
3313
|
-
|
|
3707
|
+
logger8.info("Agent registry refreshed", { count: this.agents.size, recoveredAfterRetry });
|
|
3314
3708
|
} catch (e) {
|
|
3315
|
-
|
|
3709
|
+
logger8.warn("Agent registry refresh parse failed", { error: e });
|
|
3316
3710
|
}
|
|
3317
3711
|
}
|
|
3318
3712
|
getById(id) {
|
|
@@ -3326,17 +3720,17 @@ var HttpAgentRegistry = class {
|
|
|
3326
3720
|
try {
|
|
3327
3721
|
const res = await fetch(this.apiUrl(`/api/agents/${encodeURIComponent(id)}`));
|
|
3328
3722
|
if (!res.ok) {
|
|
3329
|
-
|
|
3723
|
+
logger8.warn("fetchById failed", { agentId: id, status: res.status });
|
|
3330
3724
|
return null;
|
|
3331
3725
|
}
|
|
3332
3726
|
const agent = await res.json();
|
|
3333
3727
|
if (agent && typeof agent.id === "string") {
|
|
3334
3728
|
this.agents.set(agent.id, agent);
|
|
3335
|
-
|
|
3729
|
+
logger8.info("Agent registry fetchById upserted", { agentId: id });
|
|
3336
3730
|
}
|
|
3337
3731
|
return agent;
|
|
3338
3732
|
} catch (e) {
|
|
3339
|
-
|
|
3733
|
+
logger8.warn("fetchById unreachable", { agentId: id, error: e });
|
|
3340
3734
|
return null;
|
|
3341
3735
|
}
|
|
3342
3736
|
}
|
|
@@ -3345,16 +3739,16 @@ var HttpAgentRegistry = class {
|
|
|
3345
3739
|
}
|
|
3346
3740
|
upsert(agent) {
|
|
3347
3741
|
this.agents.set(agent.id, agent);
|
|
3348
|
-
|
|
3742
|
+
logger8.debug("Agent registry upsert", { agentId: agent.id });
|
|
3349
3743
|
}
|
|
3350
3744
|
remove(agentId) {
|
|
3351
3745
|
this.agents.delete(agentId);
|
|
3352
|
-
|
|
3746
|
+
logger8.debug("Agent registry remove", { agentId });
|
|
3353
3747
|
}
|
|
3354
3748
|
};
|
|
3355
3749
|
|
|
3356
3750
|
// src/groupRegistry.ts
|
|
3357
|
-
var
|
|
3751
|
+
var logger9 = createModuleLogger("neural.groupRegistry");
|
|
3358
3752
|
var GroupRegistry = class {
|
|
3359
3753
|
groups = /* @__PURE__ */ new Map();
|
|
3360
3754
|
serverApiUrl;
|
|
@@ -3377,17 +3771,17 @@ var GroupRegistry = class {
|
|
|
3377
3771
|
recoveredAfterRetry = res != null;
|
|
3378
3772
|
}
|
|
3379
3773
|
if (!res) {
|
|
3380
|
-
|
|
3774
|
+
logger9.warn("GroupRegistry refresh unreachable after retry");
|
|
3381
3775
|
return;
|
|
3382
3776
|
}
|
|
3383
3777
|
if (!res.ok) {
|
|
3384
|
-
|
|
3778
|
+
logger9.warn("GroupRegistry refresh failed", { status: res.status });
|
|
3385
3779
|
return;
|
|
3386
3780
|
}
|
|
3387
3781
|
try {
|
|
3388
3782
|
const body = await res.json();
|
|
3389
3783
|
if (!Array.isArray(body)) {
|
|
3390
|
-
|
|
3784
|
+
logger9.warn("GroupRegistry refresh: expected array");
|
|
3391
3785
|
return;
|
|
3392
3786
|
}
|
|
3393
3787
|
this.groups.clear();
|
|
@@ -3402,14 +3796,25 @@ var GroupRegistry = class {
|
|
|
3402
3796
|
});
|
|
3403
3797
|
}
|
|
3404
3798
|
}
|
|
3405
|
-
|
|
3799
|
+
logger9.info("GroupRegistry refreshed", { count: this.groups.size, recoveredAfterRetry });
|
|
3406
3800
|
} catch (e) {
|
|
3407
|
-
|
|
3801
|
+
logger9.warn("GroupRegistry refresh parse failed", { error: e });
|
|
3408
3802
|
}
|
|
3409
3803
|
}
|
|
3410
3804
|
getById(groupId) {
|
|
3411
3805
|
return this.groups.get(groupId) ?? null;
|
|
3412
3806
|
}
|
|
3807
|
+
/**
|
|
3808
|
+
* Return the cached groups that the given agent is a member of.
|
|
3809
|
+
* Does NOT refresh — caller decides when to call refresh().
|
|
3810
|
+
*/
|
|
3811
|
+
getMyGroups(agentId) {
|
|
3812
|
+
const out = [];
|
|
3813
|
+
for (const g of this.groups.values()) {
|
|
3814
|
+
if (g.members.includes(agentId)) out.push(g);
|
|
3815
|
+
}
|
|
3816
|
+
return out;
|
|
3817
|
+
}
|
|
3413
3818
|
/**
|
|
3414
3819
|
* Fuzzy match by name (case-insensitive substring).
|
|
3415
3820
|
* Returns the first match.
|
|
@@ -3443,16 +3848,16 @@ var GroupRegistry = class {
|
|
|
3443
3848
|
try {
|
|
3444
3849
|
const res = await fetch(url);
|
|
3445
3850
|
if (res.status === 404) {
|
|
3446
|
-
|
|
3851
|
+
logger9.info("GroupRegistry resolveScope: group not found", { rawScope, suffix });
|
|
3447
3852
|
return null;
|
|
3448
3853
|
}
|
|
3449
3854
|
if (!res.ok) {
|
|
3450
|
-
|
|
3855
|
+
logger9.warn("GroupRegistry resolveScope: HTTP error", { rawScope, status: res.status });
|
|
3451
3856
|
return null;
|
|
3452
3857
|
}
|
|
3453
3858
|
const data = await res.json();
|
|
3454
3859
|
if (!data.groupId || !data.conversationId || !data.workingDirectory) {
|
|
3455
|
-
|
|
3860
|
+
logger9.warn("GroupRegistry resolveScope: incomplete response", {
|
|
3456
3861
|
rawScope,
|
|
3457
3862
|
hasGroupId: !!data.groupId,
|
|
3458
3863
|
hasConversationId: !!data.conversationId,
|
|
@@ -3460,7 +3865,7 @@ var GroupRegistry = class {
|
|
|
3460
3865
|
});
|
|
3461
3866
|
return null;
|
|
3462
3867
|
}
|
|
3463
|
-
|
|
3868
|
+
logger9.info("GroupRegistry resolved scope", {
|
|
3464
3869
|
rawScope,
|
|
3465
3870
|
resolvedGroupId: data.groupId,
|
|
3466
3871
|
resolvedName: data.name ?? "(none)",
|
|
@@ -3475,7 +3880,7 @@ var GroupRegistry = class {
|
|
|
3475
3880
|
workingDirectory: data.workingDirectory
|
|
3476
3881
|
};
|
|
3477
3882
|
} catch (e) {
|
|
3478
|
-
|
|
3883
|
+
logger9.error("GroupRegistry resolveScope error", { rawScope, error: e });
|
|
3479
3884
|
return null;
|
|
3480
3885
|
}
|
|
3481
3886
|
}
|
|
@@ -3496,12 +3901,12 @@ var GroupRegistry = class {
|
|
|
3496
3901
|
if (Array.isArray(body)) {
|
|
3497
3902
|
const single = body.find((c) => c.type === "single" && typeof c.id === "string");
|
|
3498
3903
|
if (single?.id) {
|
|
3499
|
-
|
|
3904
|
+
logger9.info("GroupRegistry resolved single conv", { agentId, conversationId: single.id });
|
|
3500
3905
|
return single.id;
|
|
3501
3906
|
}
|
|
3502
3907
|
}
|
|
3503
3908
|
} else {
|
|
3504
|
-
|
|
3909
|
+
logger9.warn("GroupRegistry resolveSingle: list failed", { agentId, status: res.status });
|
|
3505
3910
|
}
|
|
3506
3911
|
const created = await fetch(`${this.serverApiUrl}/api/conversations`, {
|
|
3507
3912
|
method: "POST",
|
|
@@ -3509,26 +3914,27 @@ var GroupRegistry = class {
|
|
|
3509
3914
|
body: JSON.stringify({ agentId })
|
|
3510
3915
|
});
|
|
3511
3916
|
if (!created.ok) {
|
|
3512
|
-
|
|
3917
|
+
logger9.warn("GroupRegistry resolveSingle: create failed", { agentId, status: created.status });
|
|
3513
3918
|
return null;
|
|
3514
3919
|
}
|
|
3515
3920
|
const conv = await created.json();
|
|
3516
3921
|
if (typeof conv.id !== "string") {
|
|
3517
|
-
|
|
3922
|
+
logger9.warn("GroupRegistry resolveSingle: created conv missing id", { agentId });
|
|
3518
3923
|
return null;
|
|
3519
3924
|
}
|
|
3520
|
-
|
|
3925
|
+
logger9.info("GroupRegistry created single conv", { agentId, conversationId: conv.id });
|
|
3521
3926
|
return conv.id;
|
|
3522
3927
|
} catch (e) {
|
|
3523
|
-
|
|
3928
|
+
logger9.error("GroupRegistry resolveSingle error", { agentId, error: e });
|
|
3524
3929
|
return null;
|
|
3525
3930
|
}
|
|
3526
3931
|
}
|
|
3527
3932
|
};
|
|
3528
3933
|
|
|
3529
3934
|
// src/connector.ts
|
|
3935
|
+
import os5 from "os";
|
|
3530
3936
|
import WebSocket from "ws";
|
|
3531
|
-
var
|
|
3937
|
+
var logger10 = createModuleLogger("ws.connector");
|
|
3532
3938
|
var ServerConnector = class {
|
|
3533
3939
|
ws = null;
|
|
3534
3940
|
reconnectAttempts = 0;
|
|
@@ -3558,19 +3964,19 @@ var ServerConnector = class {
|
|
|
3558
3964
|
url.searchParams.set("token", this.config.bridgeToken);
|
|
3559
3965
|
}
|
|
3560
3966
|
const wsUrl = url.toString();
|
|
3561
|
-
|
|
3967
|
+
logger10.info("Connecting to server", { url: wsUrl });
|
|
3562
3968
|
const ws = new WebSocket(wsUrl);
|
|
3563
3969
|
ws.on("open", () => {
|
|
3564
3970
|
this.ws = ws;
|
|
3565
3971
|
this.reconnectAttempts = 0;
|
|
3566
|
-
|
|
3972
|
+
logger10.info("Connected to server", { url: this.config.serverUrl });
|
|
3567
3973
|
void this.handleOpen();
|
|
3568
3974
|
});
|
|
3569
3975
|
ws.on("message", (data) => {
|
|
3570
3976
|
this.handleMessage(data);
|
|
3571
3977
|
});
|
|
3572
3978
|
ws.on("close", (code, reason) => {
|
|
3573
|
-
|
|
3979
|
+
logger10.warn("Disconnected from server", {
|
|
3574
3980
|
code,
|
|
3575
3981
|
reason: reason.toString()
|
|
3576
3982
|
});
|
|
@@ -3580,15 +3986,15 @@ var ServerConnector = class {
|
|
|
3580
3986
|
}
|
|
3581
3987
|
});
|
|
3582
3988
|
ws.on("error", (err) => {
|
|
3583
|
-
|
|
3989
|
+
logger10.error("WebSocket error", { error: err });
|
|
3584
3990
|
});
|
|
3585
3991
|
}
|
|
3586
3992
|
async handleOpen() {
|
|
3587
3993
|
try {
|
|
3588
3994
|
await this.onConnected();
|
|
3589
|
-
|
|
3995
|
+
logger10.info("Recovery complete, sending bridge:register");
|
|
3590
3996
|
} catch (err) {
|
|
3591
|
-
|
|
3997
|
+
logger10.error("Recovery failed, registering with degraded state", { error: err });
|
|
3592
3998
|
}
|
|
3593
3999
|
this.register();
|
|
3594
4000
|
}
|
|
@@ -3600,13 +4006,14 @@ var ServerConnector = class {
|
|
|
3600
4006
|
payload: {
|
|
3601
4007
|
bridgeId: this.config.bridgeId,
|
|
3602
4008
|
agents: ids,
|
|
4009
|
+
hostname: os5.hostname(),
|
|
3603
4010
|
queryConfig: {
|
|
3604
4011
|
maxActive: qc.maxActive,
|
|
3605
4012
|
idleTimeoutMs: qc.idleTimeoutMs
|
|
3606
4013
|
}
|
|
3607
4014
|
}
|
|
3608
4015
|
});
|
|
3609
|
-
|
|
4016
|
+
logger10.info("Sent bridge:register", {
|
|
3610
4017
|
bridgeId: this.config.bridgeId,
|
|
3611
4018
|
agents: ids
|
|
3612
4019
|
});
|
|
@@ -3617,7 +4024,7 @@ var ServerConnector = class {
|
|
|
3617
4024
|
const raw = typeof data === "string" ? data : data.toString("utf8");
|
|
3618
4025
|
msg = parseWSMessage(raw);
|
|
3619
4026
|
} catch (e) {
|
|
3620
|
-
|
|
4027
|
+
logger10.error("Invalid WS message from server", { error: e });
|
|
3621
4028
|
return;
|
|
3622
4029
|
}
|
|
3623
4030
|
wsMetrics.incRecv(msg.type);
|
|
@@ -3628,7 +4035,7 @@ var ServerConnector = class {
|
|
|
3628
4035
|
}
|
|
3629
4036
|
case "task:dispatch": {
|
|
3630
4037
|
void this.onTaskDispatch(msg.payload).catch((err) => {
|
|
3631
|
-
|
|
4038
|
+
logger10.error("Failed to handle task:dispatch", {
|
|
3632
4039
|
error: err,
|
|
3633
4040
|
traceId: msg.payload.traceId
|
|
3634
4041
|
});
|
|
@@ -3638,19 +4045,19 @@ var ServerConnector = class {
|
|
|
3638
4045
|
case "task:group_dispatch": {
|
|
3639
4046
|
if (this.onGroupTaskDispatch) {
|
|
3640
4047
|
void this.onGroupTaskDispatch(msg.payload).catch((err) => {
|
|
3641
|
-
|
|
4048
|
+
logger10.error("Failed to handle task:group_dispatch", {
|
|
3642
4049
|
error: err,
|
|
3643
4050
|
traceId: msg.payload.traceId
|
|
3644
4051
|
});
|
|
3645
4052
|
});
|
|
3646
4053
|
} else {
|
|
3647
|
-
|
|
4054
|
+
logger10.warn("Received task:group_dispatch but no handler registered");
|
|
3648
4055
|
}
|
|
3649
4056
|
return;
|
|
3650
4057
|
}
|
|
3651
4058
|
case "user:stop_generation": {
|
|
3652
4059
|
void this.onStopGeneration(msg.payload).catch((err) => {
|
|
3653
|
-
|
|
4060
|
+
logger10.error("Failed to handle user:stop_generation", {
|
|
3654
4061
|
error: err,
|
|
3655
4062
|
traceId: msg.payload.traceId
|
|
3656
4063
|
});
|
|
@@ -3664,16 +4071,17 @@ var ServerConnector = class {
|
|
|
3664
4071
|
case "agent:updated":
|
|
3665
4072
|
case "agent:deleted":
|
|
3666
4073
|
case "group:member_changed":
|
|
4074
|
+
case "group:updated":
|
|
3667
4075
|
case "user:answer_question": {
|
|
3668
4076
|
if (this.onServerPush) {
|
|
3669
4077
|
void Promise.resolve(this.onServerPush(msg)).catch((err) => {
|
|
3670
|
-
|
|
4078
|
+
logger10.error("onServerPush handler failed", { error: err, type: msg.type });
|
|
3671
4079
|
});
|
|
3672
4080
|
}
|
|
3673
4081
|
return;
|
|
3674
4082
|
}
|
|
3675
4083
|
default: {
|
|
3676
|
-
|
|
4084
|
+
logger10.warn("Unhandled server message type", {
|
|
3677
4085
|
type: msg.type
|
|
3678
4086
|
});
|
|
3679
4087
|
}
|
|
@@ -3681,7 +4089,7 @@ var ServerConnector = class {
|
|
|
3681
4089
|
}
|
|
3682
4090
|
send(msg) {
|
|
3683
4091
|
if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
|
|
3684
|
-
|
|
4092
|
+
logger10.warn("Cannot send: WebSocket not open", {
|
|
3685
4093
|
type: msg.type
|
|
3686
4094
|
});
|
|
3687
4095
|
return;
|
|
@@ -3690,14 +4098,14 @@ var ServerConnector = class {
|
|
|
3690
4098
|
this.ws.send(JSON.stringify(msg));
|
|
3691
4099
|
wsMetrics.incSend(msg.type);
|
|
3692
4100
|
} catch (e) {
|
|
3693
|
-
|
|
4101
|
+
logger10.error("Failed to send WS message", { error: e, type: msg.type });
|
|
3694
4102
|
}
|
|
3695
4103
|
}
|
|
3696
4104
|
scheduleReconnect() {
|
|
3697
4105
|
if (this.closing) return;
|
|
3698
4106
|
const delay = this.delays[Math.min(this.reconnectAttempts, this.delays.length - 1)];
|
|
3699
4107
|
this.reconnectAttempts++;
|
|
3700
|
-
|
|
4108
|
+
logger10.info("Scheduling reconnect", {
|
|
3701
4109
|
attempt: this.reconnectAttempts,
|
|
3702
4110
|
delayMs: delay
|
|
3703
4111
|
});
|
|
@@ -3713,11 +4121,11 @@ var ServerConnector = class {
|
|
|
3713
4121
|
try {
|
|
3714
4122
|
this.ws.close(1e3, "Bridge shutting down");
|
|
3715
4123
|
} catch (e) {
|
|
3716
|
-
|
|
4124
|
+
logger10.error("Error closing WebSocket", { error: e });
|
|
3717
4125
|
}
|
|
3718
4126
|
this.ws = null;
|
|
3719
4127
|
}
|
|
3720
|
-
|
|
4128
|
+
logger10.info("Connector closed");
|
|
3721
4129
|
}
|
|
3722
4130
|
get isConnected() {
|
|
3723
4131
|
return this.ws !== null && this.ws.readyState === WebSocket.OPEN;
|
|
@@ -3725,14 +4133,14 @@ var ServerConnector = class {
|
|
|
3725
4133
|
};
|
|
3726
4134
|
|
|
3727
4135
|
// src/modelQuerier.ts
|
|
3728
|
-
import
|
|
3729
|
-
import
|
|
3730
|
-
import
|
|
3731
|
-
var
|
|
4136
|
+
import fs4 from "fs/promises";
|
|
4137
|
+
import os6 from "os";
|
|
4138
|
+
import path8 from "path";
|
|
4139
|
+
var logger11 = createModuleLogger("bridge.modelQuerier");
|
|
3732
4140
|
async function listModels(queryFn, opts = {}) {
|
|
3733
4141
|
const t0 = Date.now();
|
|
3734
|
-
const cwd = opts.cwd ??
|
|
3735
|
-
await
|
|
4142
|
+
const cwd = opts.cwd ?? path8.join(os6.homedir(), ".ahchat", "workspaces", "_list_models");
|
|
4143
|
+
await fs4.mkdir(cwd, { recursive: true });
|
|
3736
4144
|
const fn = queryFn ?? (await import("@anthropic-ai/claude-agent-sdk")).query;
|
|
3737
4145
|
const ic = new InputController();
|
|
3738
4146
|
ic.push("Reply with exactly: PING", "");
|
|
@@ -3774,7 +4182,7 @@ async function listModels(queryFn, opts = {}) {
|
|
|
3774
4182
|
displayName: m.displayName,
|
|
3775
4183
|
description: m.description
|
|
3776
4184
|
}));
|
|
3777
|
-
|
|
4185
|
+
logger11.info("listModels done", { count: models.length, ms: Date.now() - t0 });
|
|
3778
4186
|
return models;
|
|
3779
4187
|
} finally {
|
|
3780
4188
|
try {
|
|
@@ -3789,9 +4197,9 @@ async function listModels(queryFn, opts = {}) {
|
|
|
3789
4197
|
}
|
|
3790
4198
|
|
|
3791
4199
|
// src/lockfile.ts
|
|
3792
|
-
import
|
|
3793
|
-
import
|
|
3794
|
-
var
|
|
4200
|
+
import fs5 from "fs";
|
|
4201
|
+
import path9 from "path";
|
|
4202
|
+
var logger12 = createModuleLogger("bridge.lockfile");
|
|
3795
4203
|
var lockPath = null;
|
|
3796
4204
|
function isProcessAlive(pid) {
|
|
3797
4205
|
try {
|
|
@@ -3804,32 +4212,32 @@ function isProcessAlive(pid) {
|
|
|
3804
4212
|
}
|
|
3805
4213
|
}
|
|
3806
4214
|
function acquireLock(dataDir) {
|
|
3807
|
-
const file =
|
|
4215
|
+
const file = path9.join(dataDir, "bridge.lock");
|
|
3808
4216
|
lockPath = file;
|
|
3809
|
-
if (
|
|
3810
|
-
const raw =
|
|
4217
|
+
if (fs5.existsSync(file)) {
|
|
4218
|
+
const raw = fs5.readFileSync(file, "utf-8").trim();
|
|
3811
4219
|
const pid = Number.parseInt(raw, 10);
|
|
3812
4220
|
if (Number.isFinite(pid) && pid > 0) {
|
|
3813
4221
|
if (isProcessAlive(pid)) {
|
|
3814
4222
|
throw new Error(`Bridge already running (PID: ${pid})`);
|
|
3815
4223
|
}
|
|
3816
|
-
|
|
4224
|
+
logger12.warn("Removing stale bridge.lock (process not found)", { pid, path: file });
|
|
3817
4225
|
}
|
|
3818
4226
|
}
|
|
3819
|
-
|
|
3820
|
-
|
|
3821
|
-
|
|
4227
|
+
fs5.mkdirSync(path9.dirname(file), { recursive: true });
|
|
4228
|
+
fs5.writeFileSync(file, String(process.pid), "utf-8");
|
|
4229
|
+
logger12.info("Acquired bridge lock", { path: file, pid: process.pid });
|
|
3822
4230
|
const release = () => {
|
|
3823
4231
|
try {
|
|
3824
|
-
if (lockPath &&
|
|
3825
|
-
const current =
|
|
4232
|
+
if (lockPath && fs5.existsSync(lockPath)) {
|
|
4233
|
+
const current = fs5.readFileSync(lockPath, "utf-8").trim();
|
|
3826
4234
|
if (current === String(process.pid)) {
|
|
3827
|
-
|
|
3828
|
-
|
|
4235
|
+
fs5.unlinkSync(lockPath);
|
|
4236
|
+
logger12.info("Released bridge lock", { path: lockPath });
|
|
3829
4237
|
}
|
|
3830
4238
|
}
|
|
3831
4239
|
} catch (e) {
|
|
3832
|
-
|
|
4240
|
+
logger12.error("Failed to release bridge lock", { error: e, path: lockPath });
|
|
3833
4241
|
} finally {
|
|
3834
4242
|
lockPath = null;
|
|
3835
4243
|
}
|
|
@@ -3932,9 +4340,9 @@ function buildGroupPrompt(payload) {
|
|
|
3932
4340
|
}
|
|
3933
4341
|
|
|
3934
4342
|
// src/messageHandler.ts
|
|
3935
|
-
var
|
|
4343
|
+
var logger13 = createModuleLogger("msg.handler");
|
|
3936
4344
|
function emitTaskAck(emit, ackId, agentId, traceId) {
|
|
3937
|
-
|
|
4345
|
+
logger13.info("Emitting task:ack", { ackId, agentId, traceId });
|
|
3938
4346
|
emit({
|
|
3939
4347
|
type: "task:ack",
|
|
3940
4348
|
payload: {
|
|
@@ -3947,7 +4355,7 @@ function emitTaskAck(emit, ackId, agentId, traceId) {
|
|
|
3947
4355
|
}
|
|
3948
4356
|
function createTaskDispatchHandler(agentManager, agentRegistry, emit) {
|
|
3949
4357
|
return async (payload) => {
|
|
3950
|
-
|
|
4358
|
+
logger13.info("Handling task:dispatch", {
|
|
3951
4359
|
agentId: payload.agentId,
|
|
3952
4360
|
messageId: payload.messageId,
|
|
3953
4361
|
ackId: payload.ackId,
|
|
@@ -3956,14 +4364,14 @@ function createTaskDispatchHandler(agentManager, agentRegistry, emit) {
|
|
|
3956
4364
|
emitTaskAck(emit, payload.ackId, payload.agentId, payload.traceId);
|
|
3957
4365
|
let agentConfig = agentRegistry.getById(payload.agentId);
|
|
3958
4366
|
if (!agentConfig) {
|
|
3959
|
-
|
|
4367
|
+
logger13.warn("Agent not in registry, attempting live fetch", {
|
|
3960
4368
|
agentId: payload.agentId,
|
|
3961
4369
|
traceId: payload.traceId
|
|
3962
4370
|
});
|
|
3963
4371
|
agentConfig = await agentRegistry.fetchById(payload.agentId);
|
|
3964
4372
|
}
|
|
3965
4373
|
if (!agentConfig) {
|
|
3966
|
-
|
|
4374
|
+
logger13.error("Agent not found for task:dispatch (after live fetch)", {
|
|
3967
4375
|
agentId: payload.agentId,
|
|
3968
4376
|
traceId: payload.traceId
|
|
3969
4377
|
});
|
|
@@ -3990,7 +4398,7 @@ function createTaskDispatchHandler(agentManager, agentRegistry, emit) {
|
|
|
3990
4398
|
traceId: payload.traceId
|
|
3991
4399
|
});
|
|
3992
4400
|
} catch (err) {
|
|
3993
|
-
|
|
4401
|
+
logger13.error("Failed to dispatch message to Agent", {
|
|
3994
4402
|
error: err,
|
|
3995
4403
|
agentId: payload.agentId,
|
|
3996
4404
|
traceId: payload.traceId
|
|
@@ -4010,7 +4418,7 @@ function createTaskDispatchHandler(agentManager, agentRegistry, emit) {
|
|
|
4010
4418
|
}
|
|
4011
4419
|
function createGroupTaskDispatchHandler(agentManager, agentRegistry, emit) {
|
|
4012
4420
|
return async (payload) => {
|
|
4013
|
-
|
|
4421
|
+
logger13.info("Handling task:group_dispatch", {
|
|
4014
4422
|
agentId: payload.agentId,
|
|
4015
4423
|
groupId: payload.groupId,
|
|
4016
4424
|
ackId: payload.ackId,
|
|
@@ -4022,14 +4430,14 @@ function createGroupTaskDispatchHandler(agentManager, agentRegistry, emit) {
|
|
|
4022
4430
|
emitTaskAck(emit, payload.ackId, payload.agentId, payload.traceId);
|
|
4023
4431
|
let agentConfig = agentRegistry.getById(payload.agentId);
|
|
4024
4432
|
if (!agentConfig) {
|
|
4025
|
-
|
|
4433
|
+
logger13.warn("Agent not in registry for group dispatch, attempting live fetch", {
|
|
4026
4434
|
agentId: payload.agentId,
|
|
4027
4435
|
traceId: payload.traceId
|
|
4028
4436
|
});
|
|
4029
4437
|
agentConfig = await agentRegistry.fetchById(payload.agentId);
|
|
4030
4438
|
}
|
|
4031
4439
|
if (!agentConfig) {
|
|
4032
|
-
|
|
4440
|
+
logger13.error("Agent not found for task:group_dispatch (after live fetch)", {
|
|
4033
4441
|
agentId: payload.agentId,
|
|
4034
4442
|
traceId: payload.traceId
|
|
4035
4443
|
});
|
|
@@ -4062,7 +4470,7 @@ function createGroupTaskDispatchHandler(agentManager, agentRegistry, emit) {
|
|
|
4062
4470
|
groupId: payload.groupId
|
|
4063
4471
|
});
|
|
4064
4472
|
} catch (err) {
|
|
4065
|
-
|
|
4473
|
+
logger13.error("Failed to dispatch group message to Agent", {
|
|
4066
4474
|
error: err,
|
|
4067
4475
|
agentId: payload.agentId,
|
|
4068
4476
|
groupId: payload.groupId,
|
|
@@ -4082,15 +4490,84 @@ function createGroupTaskDispatchHandler(agentManager, agentRegistry, emit) {
|
|
|
4082
4490
|
};
|
|
4083
4491
|
}
|
|
4084
4492
|
|
|
4493
|
+
// src/scopePushNotify.ts
|
|
4494
|
+
var logger14 = createModuleLogger("bridge");
|
|
4495
|
+
function buildMemberChangedScopeNotice(params) {
|
|
4496
|
+
const { groupId, groupLabel, action } = params;
|
|
4497
|
+
const verb = action === "added" ? "\u52A0\u5165" : "\u79FB\u51FA";
|
|
4498
|
+
return [
|
|
4499
|
+
`[\u7CFB\u7EDF\u901A\u77E5] \u4F60\u5DF2\u88AB${verb}\u7FA4\u300C${groupLabel}\u300D(group:${groupId})\u3002`,
|
|
4500
|
+
`\u4F60\u7684\u53EF\u8FBE scope \u5DF2\u53D8\u66F4\u3002\u4E0B\u6B21\u4F7F\u7528 neural_send \u524D\u8BF7\u4EE5\u6B64\u4E3A\u51C6\uFF0C\u6216\u8C03 neural_list_scopes() \u786E\u8BA4\u6700\u65B0\u5217\u8868\u3002`,
|
|
4501
|
+
`\u65E0\u9700\u56DE\u590D\u6B64\u901A\u77E5\u3002`
|
|
4502
|
+
].join("\n");
|
|
4503
|
+
}
|
|
4504
|
+
function buildGroupRenamedScopeNotice(params) {
|
|
4505
|
+
const { groupId, newName } = params;
|
|
4506
|
+
return [
|
|
4507
|
+
`[\u7CFB\u7EDF\u901A\u77E5] \u7FA4 (group:${groupId}) \u5DF2\u66F4\u540D\u4E3A\u300C${newName}\u300D\u3002`,
|
|
4508
|
+
`\u82E5\u4F60\u4E4B\u524D\u7528\u65E7\u7FA4\u540D\u8C03\u8FC7 neural_send\uFF0C\u8BF7\u66F4\u65B0\u4E3A\u65B0\u540D\u79F0\u3002`,
|
|
4509
|
+
`\u65E0\u9700\u56DE\u590D\u6B64\u901A\u77E5\u3002`
|
|
4510
|
+
].join("\n");
|
|
4511
|
+
}
|
|
4512
|
+
async function handleGroupMemberChangedPush(deps, payload) {
|
|
4513
|
+
const { groupId, action, agentId } = payload;
|
|
4514
|
+
logger14.info("group:member_changed received, refreshing GroupRegistry", {
|
|
4515
|
+
groupId,
|
|
4516
|
+
action,
|
|
4517
|
+
agentId
|
|
4518
|
+
});
|
|
4519
|
+
await deps.groupRegistry.refresh();
|
|
4520
|
+
logger14.info("GroupRegistry refreshed after member_changed", {
|
|
4521
|
+
groupId,
|
|
4522
|
+
action,
|
|
4523
|
+
registryGroupCount: deps.groupRegistry.getAll().length
|
|
4524
|
+
});
|
|
4525
|
+
const group = deps.groupRegistry.getById(groupId);
|
|
4526
|
+
const groupLabel = group?.name ?? groupId;
|
|
4527
|
+
const notice = buildMemberChangedScopeNotice({ groupId, groupLabel, action });
|
|
4528
|
+
deps.agentManager.broadcastScopeNotice(agentId, notice);
|
|
4529
|
+
logger14.info("Scope notice sent for member_changed", {
|
|
4530
|
+
agentId,
|
|
4531
|
+
groupId,
|
|
4532
|
+
groupLabel,
|
|
4533
|
+
action,
|
|
4534
|
+
noticeLen: notice.length
|
|
4535
|
+
});
|
|
4536
|
+
}
|
|
4537
|
+
async function handleGroupUpdatedPush(deps, payload) {
|
|
4538
|
+
const { groupId, name: newName, memberAgentIds } = payload;
|
|
4539
|
+
logger14.info("group:updated received, refreshing GroupRegistry", {
|
|
4540
|
+
groupId,
|
|
4541
|
+
newName,
|
|
4542
|
+
memberCount: memberAgentIds.length
|
|
4543
|
+
});
|
|
4544
|
+
await deps.groupRegistry.refresh();
|
|
4545
|
+
logger14.info("GroupRegistry refreshed after group:updated", {
|
|
4546
|
+
groupId,
|
|
4547
|
+
newName,
|
|
4548
|
+
registryGroupCount: deps.groupRegistry.getAll().length
|
|
4549
|
+
});
|
|
4550
|
+
const notice = buildGroupRenamedScopeNotice({ groupId, newName });
|
|
4551
|
+
for (const aid of memberAgentIds) {
|
|
4552
|
+
deps.agentManager.broadcastScopeNotice(aid, notice);
|
|
4553
|
+
}
|
|
4554
|
+
logger14.info("Scope notices sent for group:updated", {
|
|
4555
|
+
groupId,
|
|
4556
|
+
newName,
|
|
4557
|
+
memberCount: memberAgentIds.length,
|
|
4558
|
+
noticeLen: notice.length
|
|
4559
|
+
});
|
|
4560
|
+
}
|
|
4561
|
+
|
|
4085
4562
|
// src/sessionStore.ts
|
|
4086
|
-
import
|
|
4087
|
-
import
|
|
4088
|
-
var
|
|
4563
|
+
import fs6 from "fs";
|
|
4564
|
+
import path10 from "path";
|
|
4565
|
+
var logger15 = createModuleLogger("session.store");
|
|
4089
4566
|
var SessionStore = class {
|
|
4090
4567
|
filePath;
|
|
4091
4568
|
cache;
|
|
4092
4569
|
constructor(dataDir) {
|
|
4093
|
-
this.filePath =
|
|
4570
|
+
this.filePath = path10.join(dataDir, "sessions.json");
|
|
4094
4571
|
this.cache = this.loadFromDisk();
|
|
4095
4572
|
}
|
|
4096
4573
|
cacheKey(agentId, scope) {
|
|
@@ -4125,8 +4602,8 @@ var SessionStore = class {
|
|
|
4125
4602
|
}
|
|
4126
4603
|
loadFromDisk() {
|
|
4127
4604
|
try {
|
|
4128
|
-
if (!
|
|
4129
|
-
const raw =
|
|
4605
|
+
if (!fs6.existsSync(this.filePath)) return {};
|
|
4606
|
+
const raw = fs6.readFileSync(this.filePath, "utf-8");
|
|
4130
4607
|
const parsed = JSON.parse(raw);
|
|
4131
4608
|
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return {};
|
|
4132
4609
|
const map = parsed;
|
|
@@ -4136,7 +4613,7 @@ var SessionStore = class {
|
|
|
4136
4613
|
migrated[key] = sessionId;
|
|
4137
4614
|
} else {
|
|
4138
4615
|
migrated[`${key}::single`] = sessionId;
|
|
4139
|
-
|
|
4616
|
+
logger15.info("Migrated legacy session key to scoped key", {
|
|
4140
4617
|
legacyKey: key,
|
|
4141
4618
|
newKey: `${key}::single`
|
|
4142
4619
|
});
|
|
@@ -4144,29 +4621,29 @@ var SessionStore = class {
|
|
|
4144
4621
|
}
|
|
4145
4622
|
return migrated;
|
|
4146
4623
|
} catch (e) {
|
|
4147
|
-
|
|
4624
|
+
logger15.warn("Failed to load sessions file, starting fresh", { error: e, path: this.filePath });
|
|
4148
4625
|
return {};
|
|
4149
4626
|
}
|
|
4150
4627
|
}
|
|
4151
4628
|
saveToDisk() {
|
|
4152
4629
|
try {
|
|
4153
|
-
const dir =
|
|
4154
|
-
|
|
4155
|
-
|
|
4630
|
+
const dir = path10.dirname(this.filePath);
|
|
4631
|
+
fs6.mkdirSync(dir, { recursive: true });
|
|
4632
|
+
fs6.writeFileSync(this.filePath, JSON.stringify(this.cache, null, 2), "utf-8");
|
|
4156
4633
|
} catch (e) {
|
|
4157
|
-
|
|
4634
|
+
logger15.error("Failed to save sessions file", { error: e, path: this.filePath });
|
|
4158
4635
|
}
|
|
4159
4636
|
}
|
|
4160
4637
|
};
|
|
4161
4638
|
|
|
4162
4639
|
// src/start.ts
|
|
4163
|
-
var
|
|
4640
|
+
var logger16 = createModuleLogger("bridge");
|
|
4164
4641
|
async function startBridge(config) {
|
|
4165
4642
|
ensureDir(config.dataDir);
|
|
4166
4643
|
ensureDir(config.claudeConfigDir);
|
|
4167
4644
|
process.env.CLAUDE_CONFIG_DIR = config.claudeConfigDir;
|
|
4168
4645
|
acquireLock(config.dataDir);
|
|
4169
|
-
|
|
4646
|
+
logger16.info("Bridge starting", {
|
|
4170
4647
|
bridgeId: config.bridgeId,
|
|
4171
4648
|
serverUrl: config.serverUrl,
|
|
4172
4649
|
serverApiUrl: config.serverApiUrl,
|
|
@@ -4174,6 +4651,9 @@ async function startBridge(config) {
|
|
|
4174
4651
|
});
|
|
4175
4652
|
wsMetrics.start(5e3);
|
|
4176
4653
|
const sessionStore = new SessionStore(config.dataDir);
|
|
4654
|
+
const memoryRoot = path11.join(config.dataDir, "agent-memory");
|
|
4655
|
+
const memoryStore = new AgentMemoryStore(memoryRoot);
|
|
4656
|
+
logger16.info("Agent memory store initialized", { rootDir: memoryRoot });
|
|
4177
4657
|
const agentRegistry = new HttpAgentRegistry(config.serverApiUrl);
|
|
4178
4658
|
const groupRegistry = new GroupRegistry(config.serverApiUrl);
|
|
4179
4659
|
await agentRegistry.refresh();
|
|
@@ -4187,7 +4667,8 @@ async function startBridge(config) {
|
|
|
4187
4667
|
queryConfig: config.queryConfig,
|
|
4188
4668
|
claudeConfigDir: config.claudeConfigDir,
|
|
4189
4669
|
askQuestionRegistry,
|
|
4190
|
-
groupRegistry
|
|
4670
|
+
groupRegistry,
|
|
4671
|
+
memoryStore
|
|
4191
4672
|
});
|
|
4192
4673
|
const taskDispatchHandler = createTaskDispatchHandler(agentManager, agentRegistry, emit);
|
|
4193
4674
|
const groupTaskDispatchHandler = createGroupTaskDispatchHandler(agentManager, agentRegistry, emit);
|
|
@@ -4209,21 +4690,21 @@ async function startBridge(config) {
|
|
|
4209
4690
|
switch (msg.type) {
|
|
4210
4691
|
case "bridge:list_models_request": {
|
|
4211
4692
|
const { requestId } = msg.payload;
|
|
4212
|
-
|
|
4693
|
+
logger16.info("list_models request received", { requestId });
|
|
4213
4694
|
try {
|
|
4214
4695
|
const models = await listModels();
|
|
4215
4696
|
connector?.send({
|
|
4216
4697
|
type: "bridge:list_models_response",
|
|
4217
4698
|
payload: { requestId, models }
|
|
4218
4699
|
});
|
|
4219
|
-
|
|
4700
|
+
logger16.info("list_models response sent", { requestId, count: models.length });
|
|
4220
4701
|
} catch (e) {
|
|
4221
4702
|
const err = e instanceof Error ? e.message : String(e);
|
|
4222
4703
|
connector?.send({
|
|
4223
4704
|
type: "bridge:list_models_response",
|
|
4224
4705
|
payload: { requestId, error: err }
|
|
4225
4706
|
});
|
|
4226
|
-
|
|
4707
|
+
logger16.error("list_models failed", { requestId, error: e });
|
|
4227
4708
|
}
|
|
4228
4709
|
break;
|
|
4229
4710
|
}
|
|
@@ -4231,7 +4712,7 @@ async function startBridge(config) {
|
|
|
4231
4712
|
await agentManager.terminate(msg.payload.agentId);
|
|
4232
4713
|
break;
|
|
4233
4714
|
case "agent:terminate_scope":
|
|
4234
|
-
|
|
4715
|
+
logger16.info("agent:terminate_scope received", {
|
|
4235
4716
|
agentId: msg.payload.agentId,
|
|
4236
4717
|
scope: msg.payload.scope
|
|
4237
4718
|
});
|
|
@@ -4244,11 +4725,31 @@ async function startBridge(config) {
|
|
|
4244
4725
|
case "agent:deleted":
|
|
4245
4726
|
agentRegistry.remove(msg.payload.agentId);
|
|
4246
4727
|
break;
|
|
4728
|
+
case "group:member_changed":
|
|
4729
|
+
await handleGroupMemberChangedPush(
|
|
4730
|
+
{ groupRegistry, agentManager },
|
|
4731
|
+
{
|
|
4732
|
+
groupId: msg.payload.groupId,
|
|
4733
|
+
action: msg.payload.action,
|
|
4734
|
+
agentId: msg.payload.agentId
|
|
4735
|
+
}
|
|
4736
|
+
);
|
|
4737
|
+
break;
|
|
4738
|
+
case "group:updated":
|
|
4739
|
+
await handleGroupUpdatedPush(
|
|
4740
|
+
{ groupRegistry, agentManager },
|
|
4741
|
+
{
|
|
4742
|
+
groupId: msg.payload.groupId,
|
|
4743
|
+
name: msg.payload.name,
|
|
4744
|
+
memberAgentIds: msg.payload.memberAgentIds
|
|
4745
|
+
}
|
|
4746
|
+
);
|
|
4747
|
+
break;
|
|
4247
4748
|
case "user:answer_question": {
|
|
4248
4749
|
const p = msg.payload;
|
|
4249
4750
|
const answerText = formatAnswerForSDK(p);
|
|
4250
4751
|
const ok = askQuestionRegistry.resolve(p.questionId, answerText);
|
|
4251
|
-
|
|
4752
|
+
logger16.info("user:answer_question handled", {
|
|
4252
4753
|
questionId: p.questionId,
|
|
4253
4754
|
agentId: p.agentId,
|
|
4254
4755
|
resolved: ok,
|
|
@@ -4269,7 +4770,7 @@ async function startBridge(config) {
|
|
|
4269
4770
|
});
|
|
4270
4771
|
}, config.queryConfig.statusReportIntervalMs);
|
|
4271
4772
|
const shutdown = async (signal) => {
|
|
4272
|
-
|
|
4773
|
+
logger16.info("Shutdown signal received", { signal });
|
|
4273
4774
|
if (statusInterval) {
|
|
4274
4775
|
clearInterval(statusInterval);
|
|
4275
4776
|
statusInterval = null;
|
|
@@ -4277,7 +4778,7 @@ async function startBridge(config) {
|
|
|
4277
4778
|
wsMetrics.stop();
|
|
4278
4779
|
connector?.close();
|
|
4279
4780
|
await agentManager.shutdownAll();
|
|
4280
|
-
|
|
4781
|
+
logger16.info("Bridge stopped");
|
|
4281
4782
|
process.exit(0);
|
|
4282
4783
|
};
|
|
4283
4784
|
process.on("SIGINT", () => void shutdown("SIGINT"));
|
|
@@ -4286,19 +4787,19 @@ async function startBridge(config) {
|
|
|
4286
4787
|
|
|
4287
4788
|
// src/protocol.ts
|
|
4288
4789
|
import { execSync } from "child_process";
|
|
4289
|
-
import
|
|
4290
|
-
import
|
|
4291
|
-
import
|
|
4790
|
+
import fs7 from "fs";
|
|
4791
|
+
import os7 from "os";
|
|
4792
|
+
import path12 from "path";
|
|
4292
4793
|
import { fileURLToPath } from "url";
|
|
4293
|
-
var
|
|
4794
|
+
var logger17 = createModuleLogger("bridge.protocol");
|
|
4294
4795
|
var __filename = fileURLToPath(import.meta.url);
|
|
4295
|
-
var __dirname =
|
|
4796
|
+
var __dirname = path12.dirname(__filename);
|
|
4296
4797
|
function getBridgeExePath() {
|
|
4297
|
-
const pkgDir =
|
|
4298
|
-
return
|
|
4798
|
+
const pkgDir = path12.resolve(__dirname, "..");
|
|
4799
|
+
return path12.join(pkgDir, "dist", "cli.js");
|
|
4299
4800
|
}
|
|
4300
4801
|
function registerProtocolHandler() {
|
|
4301
|
-
const platform =
|
|
4802
|
+
const platform = os7.platform();
|
|
4302
4803
|
if (platform === "win32") {
|
|
4303
4804
|
registerWindows();
|
|
4304
4805
|
} else if (platform === "darwin") {
|
|
@@ -4306,15 +4807,15 @@ function registerProtocolHandler() {
|
|
|
4306
4807
|
} else {
|
|
4307
4808
|
registerLinux();
|
|
4308
4809
|
}
|
|
4309
|
-
|
|
4810
|
+
logger17.info("Protocol handler registered", { platform });
|
|
4310
4811
|
}
|
|
4311
4812
|
function registerWindows() {
|
|
4312
4813
|
const exe = getBridgeExePath();
|
|
4313
4814
|
const nodeExe = process.execPath;
|
|
4314
|
-
const ahchatDir =
|
|
4315
|
-
const urlFilePath =
|
|
4316
|
-
|
|
4317
|
-
const psScriptPath =
|
|
4815
|
+
const ahchatDir = path12.join(os7.homedir(), ".ahchat");
|
|
4816
|
+
const urlFilePath = path12.join(ahchatDir, ".bridge-launch-url");
|
|
4817
|
+
fs7.mkdirSync(ahchatDir, { recursive: true });
|
|
4818
|
+
const psScriptPath = path12.join(ahchatDir, "launch-bridge.ps1");
|
|
4318
4819
|
const psContent = `param([string]$url)
|
|
4319
4820
|
if (-not $url) {
|
|
4320
4821
|
if (Test-Path '${urlFilePath}') {
|
|
@@ -4327,7 +4828,7 @@ if (-not $url) {
|
|
|
4327
4828
|
}
|
|
4328
4829
|
& '${nodeExe}' '${exe}' launch --url $url
|
|
4329
4830
|
`;
|
|
4330
|
-
|
|
4831
|
+
fs7.writeFileSync(psScriptPath, psContent);
|
|
4331
4832
|
const handler = `powershell -ExecutionPolicy Bypass -File "${psScriptPath}" -url "%1"`;
|
|
4332
4833
|
const regCommands = [
|
|
4333
4834
|
`REG ADD "HKCU\\Software\\Classes\\ahchat" /ve /d "URL:ahchat" /f`,
|
|
@@ -4339,19 +4840,19 @@ if (-not $url) {
|
|
|
4339
4840
|
try {
|
|
4340
4841
|
execSync(cmd, { stdio: "pipe" });
|
|
4341
4842
|
} catch (e) {
|
|
4342
|
-
|
|
4843
|
+
logger17.error("Failed to register Windows protocol handler", { error: e, cmd });
|
|
4343
4844
|
throw new Error(`Failed to register protocol handler: ${cmd}`);
|
|
4344
4845
|
}
|
|
4345
4846
|
}
|
|
4346
|
-
|
|
4847
|
+
logger17.info("Windows protocol handler registered", { psScriptPath });
|
|
4347
4848
|
}
|
|
4348
4849
|
function registerMacOS() {
|
|
4349
|
-
const appDir =
|
|
4350
|
-
const contentsDir =
|
|
4351
|
-
const macosDir =
|
|
4352
|
-
const resourcesDir =
|
|
4353
|
-
|
|
4354
|
-
|
|
4850
|
+
const appDir = path12.join(os7.homedir(), "Applications", "AHChatBridge.app");
|
|
4851
|
+
const contentsDir = path12.join(appDir, "Contents");
|
|
4852
|
+
const macosDir = path12.join(contentsDir, "MacOS");
|
|
4853
|
+
const resourcesDir = path12.join(contentsDir, "Resources");
|
|
4854
|
+
fs7.mkdirSync(macosDir, { recursive: true });
|
|
4855
|
+
fs7.mkdirSync(resourcesDir, { recursive: true });
|
|
4355
4856
|
const infoPlist = `<?xml version="1.0" encoding="UTF-8"?>
|
|
4356
4857
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
4357
4858
|
<plist version="1.0">
|
|
@@ -4384,10 +4885,10 @@ function registerMacOS() {
|
|
|
4384
4885
|
const launchScript = `#!/bin/bash
|
|
4385
4886
|
URL="$1"
|
|
4386
4887
|
exec "${process.execPath}" "${getBridgeExePath()}" launch --url "$URL"`;
|
|
4387
|
-
|
|
4388
|
-
|
|
4389
|
-
|
|
4390
|
-
|
|
4888
|
+
fs7.writeFileSync(path12.join(contentsDir, "Info.plist"), infoPlist);
|
|
4889
|
+
fs7.writeFileSync(path12.join(macosDir, "launch.sh"), launchScript);
|
|
4890
|
+
fs7.chmodSync(path12.join(macosDir, "launch.sh"), 493);
|
|
4891
|
+
logger17.info("macOS protocol handler registered", { appDir });
|
|
4391
4892
|
}
|
|
4392
4893
|
function registerLinux() {
|
|
4393
4894
|
const desktopFile = `[Desktop Entry]
|
|
@@ -4396,54 +4897,54 @@ Exec=${process.execPath} ${getBridgeExePath()} launch --url %u
|
|
|
4396
4897
|
Type=Application
|
|
4397
4898
|
NoDisplay=true
|
|
4398
4899
|
MimeType=x-scheme-handler/ahchat;`;
|
|
4399
|
-
const desktopPath =
|
|
4400
|
-
|
|
4401
|
-
|
|
4900
|
+
const desktopPath = path12.join(os7.homedir(), ".local", "share", "applications", "ahchat-bridge.desktop");
|
|
4901
|
+
fs7.mkdirSync(path12.dirname(desktopPath), { recursive: true });
|
|
4902
|
+
fs7.writeFileSync(desktopPath, desktopFile);
|
|
4402
4903
|
try {
|
|
4403
4904
|
execSync("update-desktop-database ~/.local/share/applications/", { stdio: "pipe" });
|
|
4404
4905
|
} catch {
|
|
4405
4906
|
}
|
|
4406
|
-
|
|
4907
|
+
logger17.info("Linux protocol handler registered", { desktopPath });
|
|
4407
4908
|
}
|
|
4408
4909
|
function unregisterProtocolHandler() {
|
|
4409
|
-
const platform =
|
|
4910
|
+
const platform = os7.platform();
|
|
4410
4911
|
if (platform === "win32") {
|
|
4411
4912
|
try {
|
|
4412
4913
|
execSync('REG DELETE "HKCU\\Software\\Classes\\ahchat" /f', { stdio: "pipe" });
|
|
4413
|
-
const psScriptPath =
|
|
4414
|
-
const urlFilePath =
|
|
4914
|
+
const psScriptPath = path12.join(os7.homedir(), ".ahchat", "launch-bridge.ps1");
|
|
4915
|
+
const urlFilePath = path12.join(os7.homedir(), ".ahchat", ".bridge-launch-url");
|
|
4415
4916
|
try {
|
|
4416
|
-
|
|
4917
|
+
fs7.unlinkSync(psScriptPath);
|
|
4417
4918
|
} catch {
|
|
4418
4919
|
}
|
|
4419
4920
|
try {
|
|
4420
|
-
|
|
4921
|
+
fs7.unlinkSync(urlFilePath);
|
|
4421
4922
|
} catch {
|
|
4422
4923
|
}
|
|
4423
|
-
|
|
4924
|
+
logger17.info("Windows protocol handler unregistered");
|
|
4424
4925
|
} catch (e) {
|
|
4425
|
-
|
|
4926
|
+
logger17.warn("Failed to unregister Windows protocol handler", { error: e });
|
|
4426
4927
|
}
|
|
4427
4928
|
} else if (platform === "darwin") {
|
|
4428
|
-
const appDir =
|
|
4929
|
+
const appDir = path12.join(os7.homedir(), "Applications", "AHChatBridge.app");
|
|
4429
4930
|
try {
|
|
4430
|
-
|
|
4431
|
-
|
|
4931
|
+
fs7.rmSync(appDir, { recursive: true, force: true });
|
|
4932
|
+
logger17.info("macOS protocol handler unregistered");
|
|
4432
4933
|
} catch (e) {
|
|
4433
|
-
|
|
4934
|
+
logger17.warn("Failed to unregister macOS protocol handler", { error: e });
|
|
4434
4935
|
}
|
|
4435
4936
|
} else {
|
|
4436
|
-
const desktopPath =
|
|
4937
|
+
const desktopPath = path12.join(os7.homedir(), ".local", "share", "applications", "ahchat-bridge.desktop");
|
|
4437
4938
|
try {
|
|
4438
|
-
|
|
4439
|
-
|
|
4939
|
+
fs7.unlinkSync(desktopPath);
|
|
4940
|
+
logger17.info("Linux protocol handler unregistered");
|
|
4440
4941
|
} catch (e) {
|
|
4441
|
-
|
|
4942
|
+
logger17.warn("Failed to unregister Linux protocol handler", { error: e });
|
|
4442
4943
|
}
|
|
4443
4944
|
}
|
|
4444
4945
|
}
|
|
4445
4946
|
function isProtocolRegistered() {
|
|
4446
|
-
const platform =
|
|
4947
|
+
const platform = os7.platform();
|
|
4447
4948
|
if (platform === "win32") {
|
|
4448
4949
|
try {
|
|
4449
4950
|
execSync('REG QUERY "HKCU\\Software\\Classes\\ahchat" /ve', { stdio: "pipe" });
|
|
@@ -4452,16 +4953,16 @@ function isProtocolRegistered() {
|
|
|
4452
4953
|
return false;
|
|
4453
4954
|
}
|
|
4454
4955
|
} else if (platform === "darwin") {
|
|
4455
|
-
const appDir =
|
|
4456
|
-
return
|
|
4956
|
+
const appDir = path12.join(os7.homedir(), "Applications", "AHChatBridge.app");
|
|
4957
|
+
return fs7.existsSync(path12.join(appDir, "Contents", "Info.plist"));
|
|
4457
4958
|
} else {
|
|
4458
|
-
const desktopPath =
|
|
4459
|
-
return
|
|
4959
|
+
const desktopPath = path12.join(os7.homedir(), ".local", "share", "applications", "ahchat-bridge.desktop");
|
|
4960
|
+
return fs7.existsSync(desktopPath);
|
|
4460
4961
|
}
|
|
4461
4962
|
}
|
|
4462
4963
|
|
|
4463
4964
|
// src/cli.ts
|
|
4464
|
-
var
|
|
4965
|
+
var logger18 = createModuleLogger("bridge");
|
|
4465
4966
|
function parseAhchatUrl(url) {
|
|
4466
4967
|
try {
|
|
4467
4968
|
if (!url.startsWith("ahchat://")) return null;
|
|
@@ -4481,12 +4982,12 @@ function parseAhchatUrl(url) {
|
|
|
4481
4982
|
}
|
|
4482
4983
|
async function run(args) {
|
|
4483
4984
|
let config = loadBridgeConfig();
|
|
4484
|
-
if (args.serverUrl)
|
|
4485
|
-
|
|
4486
|
-
const
|
|
4487
|
-
|
|
4488
|
-
config = { ...config, serverUrl: url.toString() };
|
|
4985
|
+
if (args.serverUrl) {
|
|
4986
|
+
const wsUrl = new URL(args.serverUrl);
|
|
4987
|
+
const httpBase = `${wsUrl.protocol === "wss:" ? "https" : "http"}://${wsUrl.host}`;
|
|
4988
|
+
config = { ...config, serverUrl: args.serverUrl, serverApiUrl: httpBase };
|
|
4489
4989
|
}
|
|
4990
|
+
if (args.token) config = { ...config, bridgeToken: args.token };
|
|
4490
4991
|
if (args.dataDir) config = { ...config, dataDir: args.dataDir };
|
|
4491
4992
|
if (args.logLevel) config = { ...config, logLevel: args.logLevel };
|
|
4492
4993
|
await startBridge(config);
|
|
@@ -4494,7 +4995,7 @@ async function run(args) {
|
|
|
4494
4995
|
var cli = cac("ahchat-bridge");
|
|
4495
4996
|
cli.command("run", "Start the bridge and connect to server").option("--server-url <url>", "WebSocket URL of the AHChat server").option("--token <token>", "Auth token for server registration").option("--data-dir <dir>", "Data directory (default: ~/.ahchat)").option("--log-level <level>", "Log level (default: INFO)").action((args) => {
|
|
4496
4997
|
void run(args).catch((e) => {
|
|
4497
|
-
|
|
4998
|
+
logger18.error("Bridge failed to start", { error: e });
|
|
4498
4999
|
process.exit(1);
|
|
4499
5000
|
});
|
|
4500
5001
|
});
|
|
@@ -4505,7 +5006,7 @@ cli.command("launch", "Launch bridge from ahchat:// URL (called by OS)").option(
|
|
|
4505
5006
|
process.exit(1);
|
|
4506
5007
|
}
|
|
4507
5008
|
void run({ serverUrl: parsed.serverUrl, token: parsed.token }).catch((e) => {
|
|
4508
|
-
|
|
5009
|
+
logger18.error("Bridge failed to start from URL", { error: e });
|
|
4509
5010
|
process.exit(1);
|
|
4510
5011
|
});
|
|
4511
5012
|
});
|