@fangyb/ahchat-bridge 0.1.8 → 0.1.9
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 +765 -276
- package/dist/index.js +709 -220
- package/package.json +32 -30
- package/dist/chunk-7SODRWIG.js +0 -4525
package/dist/index.js
CHANGED
|
@@ -665,11 +665,11 @@ var RotatingFileStream = class extends Writable {
|
|
|
665
665
|
timeout;
|
|
666
666
|
timeoutPromise;
|
|
667
667
|
constructor(generator, options) {
|
|
668
|
-
const { encoding, history, maxFiles, maxSize, path:
|
|
668
|
+
const { encoding, history, maxFiles, maxSize, path: path12 } = options;
|
|
669
669
|
super({ decodeStrings: true, defaultEncoding: encoding });
|
|
670
670
|
this.createGzip = createGzip;
|
|
671
671
|
this.exec = exec;
|
|
672
|
-
this.filename =
|
|
672
|
+
this.filename = path12 + generator(null);
|
|
673
673
|
this.fsCreateReadStream = createReadStream;
|
|
674
674
|
this.fsCreateWriteStream = createWriteStream;
|
|
675
675
|
this.fsOpen = open;
|
|
@@ -681,7 +681,7 @@ var RotatingFileStream = class extends Writable {
|
|
|
681
681
|
this.options = options;
|
|
682
682
|
this.stdout = process.stdout;
|
|
683
683
|
if (maxFiles || maxSize)
|
|
684
|
-
options.history =
|
|
684
|
+
options.history = path12 + (history ? history : this.generator(null) + ".txt");
|
|
685
685
|
this.on("close", () => this.finished ? null : this.emit("finish"));
|
|
686
686
|
this.on("finish", () => this.finished = this.clear());
|
|
687
687
|
(async () => {
|
|
@@ -809,9 +809,9 @@ var RotatingFileStream = class extends Writable {
|
|
|
809
809
|
return this.move();
|
|
810
810
|
}
|
|
811
811
|
async findName() {
|
|
812
|
-
const { interval, path:
|
|
812
|
+
const { interval, path: path12, intervalBoundary } = this.options;
|
|
813
813
|
for (let index = 1; index < 1e3; ++index) {
|
|
814
|
-
const filename =
|
|
814
|
+
const filename = path12 + this.generator(interval && intervalBoundary ? new Date(this.prev) : this.rotation, index);
|
|
815
815
|
if (!await exists(filename))
|
|
816
816
|
return filename;
|
|
817
817
|
}
|
|
@@ -841,11 +841,11 @@ var RotatingFileStream = class extends Writable {
|
|
|
841
841
|
return this.unlink(filename);
|
|
842
842
|
}
|
|
843
843
|
async classical() {
|
|
844
|
-
const { compress, path:
|
|
844
|
+
const { compress, path: path12, rotate } = this.options;
|
|
845
845
|
let rotatedName = "";
|
|
846
846
|
for (let count = rotate; count > 0; --count) {
|
|
847
|
-
const currName =
|
|
848
|
-
const prevName = count === 1 ? this.filename :
|
|
847
|
+
const currName = path12 + this.generator(count);
|
|
848
|
+
const prevName = count === 1 ? this.filename : path12 + this.generator(count - 1);
|
|
849
849
|
if (!await exists(prevName))
|
|
850
850
|
continue;
|
|
851
851
|
if (!rotatedName)
|
|
@@ -1383,10 +1383,82 @@ function createModuleLogger(module) {
|
|
|
1383
1383
|
});
|
|
1384
1384
|
}
|
|
1385
1385
|
|
|
1386
|
+
// src/start.ts
|
|
1387
|
+
import path11 from "path";
|
|
1388
|
+
|
|
1389
|
+
// src/agentMemoryStore.ts
|
|
1390
|
+
import fs2 from "fs";
|
|
1391
|
+
import path4 from "path";
|
|
1392
|
+
var logger = createModuleLogger("agent.memoryStore");
|
|
1393
|
+
var NOTEBOOK_FILE_NAME = "notebook.md";
|
|
1394
|
+
var AGENT_ID_PATTERN = /^[a-zA-Z0-9_-]+$/;
|
|
1395
|
+
var AgentMemoryStore = class {
|
|
1396
|
+
rootDir;
|
|
1397
|
+
constructor(rootDir) {
|
|
1398
|
+
this.rootDir = rootDir;
|
|
1399
|
+
}
|
|
1400
|
+
read(agentId) {
|
|
1401
|
+
this.validateAgentId(agentId);
|
|
1402
|
+
const filePath = this.notebookPath(agentId);
|
|
1403
|
+
try {
|
|
1404
|
+
if (!fs2.existsSync(filePath)) {
|
|
1405
|
+
logger.info("Notebook read", { agentId, exists: false, bytes: 0 });
|
|
1406
|
+
return "";
|
|
1407
|
+
}
|
|
1408
|
+
const content = fs2.readFileSync(filePath, "utf-8");
|
|
1409
|
+
logger.info("Notebook read", { agentId, exists: true, bytes: content.length });
|
|
1410
|
+
return content;
|
|
1411
|
+
} catch (e) {
|
|
1412
|
+
logger.error("Failed to read notebook, returning empty", {
|
|
1413
|
+
agentId,
|
|
1414
|
+
path: filePath,
|
|
1415
|
+
error: e
|
|
1416
|
+
});
|
|
1417
|
+
return "";
|
|
1418
|
+
}
|
|
1419
|
+
}
|
|
1420
|
+
write(agentId, content) {
|
|
1421
|
+
this.validateAgentId(agentId);
|
|
1422
|
+
const dir = this.notebookDir(agentId);
|
|
1423
|
+
const filePath = this.notebookPath(agentId);
|
|
1424
|
+
const tmpPath = `${filePath}.tmp`;
|
|
1425
|
+
try {
|
|
1426
|
+
fs2.mkdirSync(dir, { recursive: true });
|
|
1427
|
+
fs2.writeFileSync(tmpPath, content, "utf-8");
|
|
1428
|
+
fs2.renameSync(tmpPath, filePath);
|
|
1429
|
+
logger.info("Notebook written", { agentId, bytes: content.length });
|
|
1430
|
+
} catch (e) {
|
|
1431
|
+
logger.error("Failed to write notebook", {
|
|
1432
|
+
agentId,
|
|
1433
|
+
path: filePath,
|
|
1434
|
+
error: e
|
|
1435
|
+
});
|
|
1436
|
+
throw e;
|
|
1437
|
+
}
|
|
1438
|
+
}
|
|
1439
|
+
append(agentId, snippet) {
|
|
1440
|
+
if (snippet.length === 0) return;
|
|
1441
|
+
const existing = this.read(agentId);
|
|
1442
|
+
const joiner = existing.length > 0 && !existing.endsWith("\n") ? "\n" : "";
|
|
1443
|
+
this.write(agentId, existing + joiner + snippet);
|
|
1444
|
+
}
|
|
1445
|
+
notebookDir(agentId) {
|
|
1446
|
+
return path4.join(this.rootDir, agentId);
|
|
1447
|
+
}
|
|
1448
|
+
notebookPath(agentId) {
|
|
1449
|
+
return path4.join(this.notebookDir(agentId), NOTEBOOK_FILE_NAME);
|
|
1450
|
+
}
|
|
1451
|
+
validateAgentId(agentId) {
|
|
1452
|
+
if (!AGENT_ID_PATTERN.test(agentId)) {
|
|
1453
|
+
throw new Error(`AgentMemoryStore: unsafe agentId "${agentId}"`);
|
|
1454
|
+
}
|
|
1455
|
+
}
|
|
1456
|
+
};
|
|
1457
|
+
|
|
1386
1458
|
// src/agentManager.ts
|
|
1387
|
-
import
|
|
1459
|
+
import fs3 from "fs/promises";
|
|
1388
1460
|
import os4 from "os";
|
|
1389
|
-
import
|
|
1461
|
+
import path7 from "path";
|
|
1390
1462
|
|
|
1391
1463
|
// ../shared/src/constants.ts
|
|
1392
1464
|
var NO_REPLY_TOKEN = "<no-reply/>";
|
|
@@ -1437,6 +1509,14 @@ with its own context, but they are all you. You have one tool to talk between th
|
|
|
1437
1509
|
- Returns immediately with a delivery receipt. You do NOT wait for a reply.
|
|
1438
1510
|
- Whether/how the other-scope self responds is its own decision.
|
|
1439
1511
|
|
|
1512
|
+
- neural_list_scopes(): Return the list of scopes where "you" exist (the only
|
|
1513
|
+
ones neural_send can reach). This list is also injected at the top of your
|
|
1514
|
+
system prompt at runtime start; only call this tool if you suspect it's
|
|
1515
|
+
stale (e.g., the user mentions a group that's not in your snapshot).
|
|
1516
|
+
|
|
1517
|
+
You are ONLY a member of the scopes shown in your "# Your scopes" section
|
|
1518
|
+
(injected at runtime start). neural_send to any other group will be rejected.
|
|
1519
|
+
|
|
1440
1520
|
When YOU receive a message wrapped as "[\u5185\u5FC3\u72EC\u767D \u2014 \u6765\u81EA\u4F60\u5728\u300C<scope>\u300D\u7684\u5206\u8EAB]":
|
|
1441
1521
|
- That is literally you, talking to yourself from another scope. It's private \u2014
|
|
1442
1522
|
nobody else in this scope hears it. Do NOT echo or quote the envelope text.
|
|
@@ -1450,6 +1530,31 @@ Pick neural_send whenever the user asks you to "tell people in group X ...", "as
|
|
|
1450
1530
|
me in group X ...", "let me know what you've been doing in X", or anything that
|
|
1451
1531
|
requires the you-in-another-scope to do something or share something. There is no
|
|
1452
1532
|
separate "recall" or "relay" tool \u2014 neural_send is the only one.
|
|
1533
|
+
|
|
1534
|
+
# Personal notebook (self_note)
|
|
1535
|
+
You have a personal notebook that travels with you across every scope (your 1:1 with
|
|
1536
|
+
the user AND every group you're in). Whatever you write to it now will appear at the
|
|
1537
|
+
top of your system prompt on your next turn, in any scope. Treat it as your long-term
|
|
1538
|
+
memory \u2014 the only thing about "you" that survives across conversations.
|
|
1539
|
+
|
|
1540
|
+
- self_note(action, content?):
|
|
1541
|
+
- "append" \u2014 add a new entry at the bottom of the notebook (most common).
|
|
1542
|
+
- "write" \u2014 replace the whole notebook (use to compact, reorganize, or correct).
|
|
1543
|
+
- "read" \u2014 fetch current contents. Your notebook is already at the top of this
|
|
1544
|
+
prompt, so you rarely need this; use only when you want to verify what's actually
|
|
1545
|
+
persisted (e.g., you suspect the prompt copy is stale after you just wrote).
|
|
1546
|
+
|
|
1547
|
+
Write to your notebook when:
|
|
1548
|
+
- You make a commitment that will outlive this conversation.
|
|
1549
|
+
- The user shares a stable preference or fact about themselves.
|
|
1550
|
+
- You form a position on a recurring topic that you want to keep consistent across
|
|
1551
|
+
every group and 1:1.
|
|
1552
|
+
|
|
1553
|
+
Do NOT write to your notebook for:
|
|
1554
|
+
- Throwaway calculations, small talk, or one-shot Q&A.
|
|
1555
|
+
- Anything that won't matter tomorrow.
|
|
1556
|
+
- Verbose minutes of a conversation \u2014 your future self has to re-read this every
|
|
1557
|
+
turn forever, so keep it lean. Quality over quantity.
|
|
1453
1558
|
`.trim();
|
|
1454
1559
|
var FAN_OUT_TRACE_TTL_MS = 10 * 6e4;
|
|
1455
1560
|
|
|
@@ -1577,7 +1682,7 @@ var InputController = class {
|
|
|
1577
1682
|
};
|
|
1578
1683
|
|
|
1579
1684
|
// src/askQuestionRegistry.ts
|
|
1580
|
-
var
|
|
1685
|
+
var logger2 = createModuleLogger("askQuestionRegistry");
|
|
1581
1686
|
var ASK_QUESTION_TIMEOUT_MS = 12e4;
|
|
1582
1687
|
var TIMEOUT_ANSWER = "[User did not respond within 120 seconds. Please decide whether to proceed with reasonable defaults or skip this step.]";
|
|
1583
1688
|
var AskQuestionRegistry = class {
|
|
@@ -1588,27 +1693,27 @@ var AskQuestionRegistry = class {
|
|
|
1588
1693
|
const timer = setTimeout(() => {
|
|
1589
1694
|
if (!this.entries.has(questionId)) return;
|
|
1590
1695
|
this.entries.delete(questionId);
|
|
1591
|
-
|
|
1696
|
+
logger2.warn("AskQuestion timeout", { questionId, agentId, timeoutMs });
|
|
1592
1697
|
try {
|
|
1593
1698
|
onTimeout();
|
|
1594
1699
|
} catch (e) {
|
|
1595
|
-
|
|
1700
|
+
logger2.error("onTimeout cb threw", { error: e });
|
|
1596
1701
|
}
|
|
1597
1702
|
resolve(TIMEOUT_ANSWER);
|
|
1598
1703
|
}, timeoutMs);
|
|
1599
1704
|
this.entries.set(questionId, { resolve, timer, agentId, askedAt: Date.now() });
|
|
1600
|
-
|
|
1705
|
+
logger2.info("AskQuestion registered", { questionId, agentId, timeoutMs });
|
|
1601
1706
|
});
|
|
1602
1707
|
}
|
|
1603
1708
|
resolve(questionId, answerText) {
|
|
1604
1709
|
const entry = this.entries.get(questionId);
|
|
1605
1710
|
if (!entry) {
|
|
1606
|
-
|
|
1711
|
+
logger2.warn("AskQuestion resolve: id not found (may be timed out)", { questionId });
|
|
1607
1712
|
return false;
|
|
1608
1713
|
}
|
|
1609
1714
|
clearTimeout(entry.timer);
|
|
1610
1715
|
this.entries.delete(questionId);
|
|
1611
|
-
|
|
1716
|
+
logger2.info("AskQuestion resolved", {
|
|
1612
1717
|
questionId,
|
|
1613
1718
|
agentId: entry.agentId,
|
|
1614
1719
|
waitedMs: Date.now() - entry.askedAt,
|
|
@@ -1620,7 +1725,7 @@ var AskQuestionRegistry = class {
|
|
|
1620
1725
|
}
|
|
1621
1726
|
cancelAll(reason) {
|
|
1622
1727
|
if (this.entries.size === 0) return;
|
|
1623
|
-
|
|
1728
|
+
logger2.warn("AskQuestion cancelAll", { reason, count: this.entries.size });
|
|
1624
1729
|
for (const [, entry] of this.entries) {
|
|
1625
1730
|
clearTimeout(entry.timer);
|
|
1626
1731
|
entry.resolve(`[${reason}]`);
|
|
@@ -1649,7 +1754,7 @@ function runtimeKey(agentId, scope) {
|
|
|
1649
1754
|
}
|
|
1650
1755
|
|
|
1651
1756
|
// src/askUserQuestionGuard.ts
|
|
1652
|
-
var
|
|
1757
|
+
var logger3 = createModuleLogger("askUserQuestionGuard");
|
|
1653
1758
|
function formatAnswerForSDK(p) {
|
|
1654
1759
|
const parts = ["[User Response]"];
|
|
1655
1760
|
if (p.selectedLabels.length > 0) {
|
|
@@ -1667,16 +1772,16 @@ function makeAskUserQuestionGuard(deps) {
|
|
|
1667
1772
|
return async (input) => {
|
|
1668
1773
|
const task = deps.getCurrentTask();
|
|
1669
1774
|
if (!task) {
|
|
1670
|
-
|
|
1775
|
+
logger3.error("AskUserQuestion received but no currentTask", { agentId: deps.agentId });
|
|
1671
1776
|
return { behavior: "deny", message: "[Internal error: no active task context]" };
|
|
1672
1777
|
}
|
|
1673
1778
|
const questions = input.questions ?? [];
|
|
1674
1779
|
if (questions.length === 0) {
|
|
1675
|
-
|
|
1780
|
+
logger3.warn("AskUserQuestion called with empty questions array", { agentId: deps.agentId });
|
|
1676
1781
|
return { behavior: "deny", message: "[Internal error: empty questions]" };
|
|
1677
1782
|
}
|
|
1678
1783
|
if (questions.length > 1) {
|
|
1679
|
-
|
|
1784
|
+
logger3.warn("AskUserQuestion received multi questions, Plan A only handles questions[0]", {
|
|
1680
1785
|
agentId: deps.agentId,
|
|
1681
1786
|
count: questions.length
|
|
1682
1787
|
});
|
|
@@ -1689,7 +1794,7 @@ function makeAskUserQuestionGuard(deps) {
|
|
|
1689
1794
|
description: o.description
|
|
1690
1795
|
}));
|
|
1691
1796
|
const multiSelect = Boolean(q.multiSelect);
|
|
1692
|
-
|
|
1797
|
+
logger3.info("AskUserQuestion intercepted, emitting agent:ask_user_question", {
|
|
1693
1798
|
agentId: deps.agentId,
|
|
1694
1799
|
scope: scopeKey(deps.scope),
|
|
1695
1800
|
groupId: task.groupId,
|
|
@@ -1717,7 +1822,7 @@ function makeAskUserQuestionGuard(deps) {
|
|
|
1717
1822
|
traceId: task.traceId
|
|
1718
1823
|
}
|
|
1719
1824
|
});
|
|
1720
|
-
|
|
1825
|
+
logger3.info("AskUserQuestion agent status awaiting_user", {
|
|
1721
1826
|
agentId: deps.agentId,
|
|
1722
1827
|
questionId,
|
|
1723
1828
|
groupId: task.groupId,
|
|
@@ -1740,7 +1845,7 @@ function makeAskUserQuestionGuard(deps) {
|
|
|
1740
1845
|
}
|
|
1741
1846
|
});
|
|
1742
1847
|
});
|
|
1743
|
-
|
|
1848
|
+
logger3.info("AskUserQuestion agent status thinking (resume SDK)", {
|
|
1744
1849
|
agentId: deps.agentId,
|
|
1745
1850
|
questionId,
|
|
1746
1851
|
groupId: task.groupId,
|
|
@@ -1750,7 +1855,7 @@ function makeAskUserQuestionGuard(deps) {
|
|
|
1750
1855
|
type: "agent:status",
|
|
1751
1856
|
payload: { agentId: deps.agentId, status: "thinking" }
|
|
1752
1857
|
});
|
|
1753
|
-
|
|
1858
|
+
logger3.info("AskUserQuestion answered, returning deny+message to SDK", {
|
|
1754
1859
|
agentId: deps.agentId,
|
|
1755
1860
|
questionId,
|
|
1756
1861
|
replyMessageId: task.replyMessageId,
|
|
@@ -1762,17 +1867,17 @@ function makeAskUserQuestionGuard(deps) {
|
|
|
1762
1867
|
}
|
|
1763
1868
|
|
|
1764
1869
|
// src/permissionGuard.ts
|
|
1765
|
-
import
|
|
1870
|
+
import path6 from "path";
|
|
1766
1871
|
|
|
1767
1872
|
// ../shared/src/utils/pathSafety.ts
|
|
1768
|
-
import
|
|
1873
|
+
import path5 from "path";
|
|
1769
1874
|
function isPathInside(parent, child) {
|
|
1770
|
-
const resolvedParent =
|
|
1771
|
-
const resolvedChild =
|
|
1875
|
+
const resolvedParent = path5.resolve(parent);
|
|
1876
|
+
const resolvedChild = path5.resolve(child);
|
|
1772
1877
|
if (resolvedParent === resolvedChild) return true;
|
|
1773
|
-
const rel =
|
|
1878
|
+
const rel = path5.relative(resolvedParent, resolvedChild);
|
|
1774
1879
|
if (rel === "") return true;
|
|
1775
|
-
return !rel.startsWith("..") && !
|
|
1880
|
+
return !rel.startsWith("..") && !path5.isAbsolute(rel);
|
|
1776
1881
|
}
|
|
1777
1882
|
|
|
1778
1883
|
// src/permissionGuard.ts
|
|
@@ -1787,7 +1892,7 @@ function makeCwdPermissionGuard(cwd, agentId, scope, log) {
|
|
|
1787
1892
|
if (typeof raw !== "string" || raw.length === 0) {
|
|
1788
1893
|
return { behavior: "allow" };
|
|
1789
1894
|
}
|
|
1790
|
-
const abs =
|
|
1895
|
+
const abs = path6.isAbsolute(raw) ? raw : path6.resolve(cwd, raw);
|
|
1791
1896
|
if (isPathInside(cwd, abs)) {
|
|
1792
1897
|
return { behavior: "allow" };
|
|
1793
1898
|
}
|
|
@@ -1800,7 +1905,7 @@ function makeCwdPermissionGuard(cwd, agentId, scope, log) {
|
|
|
1800
1905
|
}
|
|
1801
1906
|
|
|
1802
1907
|
// src/neuralMcpServer.ts
|
|
1803
|
-
var
|
|
1908
|
+
var logger4 = createModuleLogger("neural.mcpServer");
|
|
1804
1909
|
function formatScopeLabel(key, groupName) {
|
|
1805
1910
|
if (key === "single") return "\u5355\u804A";
|
|
1806
1911
|
if (groupName) return `\u7FA4\u300C${groupName}\u300D`;
|
|
@@ -1824,7 +1929,7 @@ async function createNeuralMcpServer(deps) {
|
|
|
1824
1929
|
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')
|
|
1825
1930
|
},
|
|
1826
1931
|
async (args) => {
|
|
1827
|
-
|
|
1932
|
+
logger4.info("neural_send tool called", {
|
|
1828
1933
|
agentId: deps.agentId,
|
|
1829
1934
|
fromScope: currentScopeKey,
|
|
1830
1935
|
rawTargetScope: args.target_scope,
|
|
@@ -1845,12 +1950,12 @@ async function createNeuralMcpServer(deps) {
|
|
|
1845
1950
|
if (singleConvId) {
|
|
1846
1951
|
conversationId = singleConvId;
|
|
1847
1952
|
} else {
|
|
1848
|
-
|
|
1953
|
+
logger4.warn("neural_send: failed to resolve single conv", { agentId: deps.agentId });
|
|
1849
1954
|
}
|
|
1850
1955
|
} else if (args.target_scope.startsWith("group:")) {
|
|
1851
1956
|
const r = await deps.groupRegistry.resolveScope(args.target_scope);
|
|
1852
1957
|
if (!r) {
|
|
1853
|
-
|
|
1958
|
+
logger4.info("neural_send: target scope not found", { rawTargetScope: args.target_scope });
|
|
1854
1959
|
return {
|
|
1855
1960
|
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` }],
|
|
1856
1961
|
isError: true
|
|
@@ -1861,6 +1966,28 @@ async function createNeuralMcpServer(deps) {
|
|
|
1861
1966
|
groupId = r.groupId;
|
|
1862
1967
|
groupName = r.groupName;
|
|
1863
1968
|
targetCwd = r.workingDirectory;
|
|
1969
|
+
const cached = deps.groupRegistry.getById(r.groupId);
|
|
1970
|
+
if (!cached || !cached.members.includes(deps.agentId)) {
|
|
1971
|
+
logger4.info("neural_send: not a member of target group", {
|
|
1972
|
+
agentId: deps.agentId,
|
|
1973
|
+
groupId: r.groupId,
|
|
1974
|
+
groupName: r.groupName,
|
|
1975
|
+
cacheHit: !!cached,
|
|
1976
|
+
memberCount: cached?.members.length ?? 0
|
|
1977
|
+
});
|
|
1978
|
+
return {
|
|
1979
|
+
content: [{
|
|
1980
|
+
type: "text",
|
|
1981
|
+
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`
|
|
1982
|
+
}],
|
|
1983
|
+
isError: true
|
|
1984
|
+
};
|
|
1985
|
+
}
|
|
1986
|
+
logger4.info("neural_send: member check passed", {
|
|
1987
|
+
agentId: deps.agentId,
|
|
1988
|
+
groupId: r.groupId,
|
|
1989
|
+
groupName: r.groupName
|
|
1990
|
+
});
|
|
1864
1991
|
} else {
|
|
1865
1992
|
return {
|
|
1866
1993
|
content: [{ type: "text", text: '[neural_send] target_scope \u5FC5\u987B\u662F "single" \u6216 "group:<\u7FA4\u540D\u6216 ID>"\u3002' }],
|
|
@@ -1868,7 +1995,7 @@ async function createNeuralMcpServer(deps) {
|
|
|
1868
1995
|
};
|
|
1869
1996
|
}
|
|
1870
1997
|
if (resolvedKey === currentScopeKey) {
|
|
1871
|
-
|
|
1998
|
+
logger4.warn("neural_send: self-send refused", { agentId: deps.agentId, scope: currentScopeKey });
|
|
1872
1999
|
return {
|
|
1873
2000
|
content: [{ type: "text", text: "[neural_send] \u4E0D\u80FD\u628A\u6D88\u606F\u9001\u7ED9\u81EA\u5DF1\u5F53\u524D\u6240\u5728\u7684 scope\u3002" }],
|
|
1874
2001
|
isError: true
|
|
@@ -1886,7 +2013,7 @@ async function createNeuralMcpServer(deps) {
|
|
|
1886
2013
|
groupId,
|
|
1887
2014
|
targetCwd
|
|
1888
2015
|
});
|
|
1889
|
-
|
|
2016
|
+
logger4.info("neural_send delivered", {
|
|
1890
2017
|
agentId: deps.agentId,
|
|
1891
2018
|
fromScope: currentScopeKey,
|
|
1892
2019
|
toScope: resolvedKey,
|
|
@@ -1896,7 +2023,7 @@ async function createNeuralMcpServer(deps) {
|
|
|
1896
2023
|
content: [{ type: "text", text: `[neural_send] \u5DF2\u9001\u8FBE\u5230\u300C${toLabel}\u300D(scope: ${resolvedKey})\u3002` }]
|
|
1897
2024
|
};
|
|
1898
2025
|
} catch (err) {
|
|
1899
|
-
|
|
2026
|
+
logger4.error("neural_send dispatch failed", { agentId: deps.agentId, error: err });
|
|
1900
2027
|
return {
|
|
1901
2028
|
content: [{ type: "text", text: `[neural_send] \u9001\u8FBE\u5931\u8D25\uFF1A${err.message}` }],
|
|
1902
2029
|
isError: true
|
|
@@ -1905,21 +2032,160 @@ async function createNeuralMcpServer(deps) {
|
|
|
1905
2032
|
},
|
|
1906
2033
|
{}
|
|
1907
2034
|
);
|
|
2035
|
+
const selfNote = deps.memoryStore ? sdk.tool(
|
|
2036
|
+
"self_note",
|
|
2037
|
+
`\u5728\u4F60\u7684"\u968F\u8EAB\u7B14\u8BB0\u672C"\u4E0A\u5199/\u8BFB\u4E00\u6BB5\u5185\u5BB9\u3002
|
|
2038
|
+
\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
|
|
2039
|
+
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
|
|
2040
|
+
\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`,
|
|
2041
|
+
{
|
|
2042
|
+
action: z.string().describe(
|
|
2043
|
+
'\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'
|
|
2044
|
+
),
|
|
2045
|
+
content: z.string().optional().describe(
|
|
2046
|
+
'\u8981\u5199\u5165\u7684\u5185\u5BB9\u3002action="append" \u6216 "write" \u65F6\u5FC5\u586B\uFF1Baction="read" \u65F6\u5FFD\u7565\u3002'
|
|
2047
|
+
)
|
|
2048
|
+
},
|
|
2049
|
+
async (args) => {
|
|
2050
|
+
const action = args.action;
|
|
2051
|
+
const content = args.content;
|
|
2052
|
+
logger4.info("self_note tool called", {
|
|
2053
|
+
agentId: deps.agentId,
|
|
2054
|
+
scope: currentScopeKey,
|
|
2055
|
+
action,
|
|
2056
|
+
contentLen: typeof content === "string" ? content.length : 0
|
|
2057
|
+
});
|
|
2058
|
+
if (action === "read") {
|
|
2059
|
+
const current = deps.memoryStore.read(deps.agentId);
|
|
2060
|
+
if (current.length === 0) {
|
|
2061
|
+
logger4.info("self_note read empty", {
|
|
2062
|
+
agentId: deps.agentId,
|
|
2063
|
+
scope: currentScopeKey
|
|
2064
|
+
});
|
|
2065
|
+
return {
|
|
2066
|
+
content: [{ type: "text", text: "[self_note] \u4F60\u7684\u7B14\u8BB0\u672C\u76EE\u524D\u662F\u7A7A\u7684\u3002" }]
|
|
2067
|
+
};
|
|
2068
|
+
}
|
|
2069
|
+
logger4.info("self_note read ok", {
|
|
2070
|
+
agentId: deps.agentId,
|
|
2071
|
+
scope: currentScopeKey,
|
|
2072
|
+
notebookBytes: current.length
|
|
2073
|
+
});
|
|
2074
|
+
return {
|
|
2075
|
+
content: [{ type: "text", text: current }]
|
|
2076
|
+
};
|
|
2077
|
+
}
|
|
2078
|
+
if (action === "append" || action === "write") {
|
|
2079
|
+
if (typeof content !== "string" || content.length === 0) {
|
|
2080
|
+
logger4.warn("self_note missing content", {
|
|
2081
|
+
agentId: deps.agentId,
|
|
2082
|
+
scope: currentScopeKey,
|
|
2083
|
+
action
|
|
2084
|
+
});
|
|
2085
|
+
return {
|
|
2086
|
+
content: [{ type: "text", text: `[self_note] action="${action}" \u65F6 content \u5FC5\u586B\u4E14\u975E\u7A7A\u3002` }],
|
|
2087
|
+
isError: true
|
|
2088
|
+
};
|
|
2089
|
+
}
|
|
2090
|
+
try {
|
|
2091
|
+
if (action === "append") {
|
|
2092
|
+
deps.memoryStore.append(deps.agentId, content);
|
|
2093
|
+
} else {
|
|
2094
|
+
deps.memoryStore.write(deps.agentId, content);
|
|
2095
|
+
}
|
|
2096
|
+
const after = deps.memoryStore.read(deps.agentId);
|
|
2097
|
+
logger4.info("self_note persisted", {
|
|
2098
|
+
agentId: deps.agentId,
|
|
2099
|
+
scope: currentScopeKey,
|
|
2100
|
+
action,
|
|
2101
|
+
contentLen: content.length,
|
|
2102
|
+
notebookBytesAfter: after.length
|
|
2103
|
+
});
|
|
2104
|
+
return {
|
|
2105
|
+
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` }]
|
|
2106
|
+
};
|
|
2107
|
+
} catch (err) {
|
|
2108
|
+
logger4.error("self_note write failed", { agentId: deps.agentId, action, error: err });
|
|
2109
|
+
return {
|
|
2110
|
+
content: [{ type: "text", text: `[self_note] \u5199\u5165\u5931\u8D25\uFF1A${err.message}` }],
|
|
2111
|
+
isError: true
|
|
2112
|
+
};
|
|
2113
|
+
}
|
|
2114
|
+
}
|
|
2115
|
+
logger4.warn("self_note invalid action", {
|
|
2116
|
+
agentId: deps.agentId,
|
|
2117
|
+
scope: currentScopeKey,
|
|
2118
|
+
action: String(action)
|
|
2119
|
+
});
|
|
2120
|
+
return {
|
|
2121
|
+
content: [{ type: "text", text: `[self_note] \u672A\u77E5 action "${String(action)}"\u3002\u5FC5\u987B\u662F "read" / "append" / "write" \u4E4B\u4E00\u3002` }],
|
|
2122
|
+
isError: true
|
|
2123
|
+
};
|
|
2124
|
+
},
|
|
2125
|
+
{}
|
|
2126
|
+
) : null;
|
|
2127
|
+
const neuralListScopes = sdk.tool(
|
|
2128
|
+
"neural_list_scopes",
|
|
2129
|
+
`\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
|
|
2130
|
+
\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
|
|
2131
|
+
\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`,
|
|
2132
|
+
{},
|
|
2133
|
+
async () => {
|
|
2134
|
+
logger4.info("neural_list_scopes tool called", {
|
|
2135
|
+
agentId: deps.agentId,
|
|
2136
|
+
scope: currentScopeKey
|
|
2137
|
+
});
|
|
2138
|
+
await deps.groupRegistry.refresh();
|
|
2139
|
+
const myGroups = deps.groupRegistry.getMyGroups(deps.agentId);
|
|
2140
|
+
logger4.info("neural_list_scopes: registry refreshed", {
|
|
2141
|
+
agentId: deps.agentId,
|
|
2142
|
+
scope: currentScopeKey,
|
|
2143
|
+
myGroupCount: myGroups.length
|
|
2144
|
+
});
|
|
2145
|
+
const lines = [];
|
|
2146
|
+
const isCurSingle = currentScopeKey === "single";
|
|
2147
|
+
lines.push(`- single \u2014 \u4F60\u548C\u7528\u6237\u7684 1:1 \u5355\u804A${isCurSingle ? " (\u4F60\u5F53\u524D\u6240\u5728)" : ""}`);
|
|
2148
|
+
for (const g of myGroups) {
|
|
2149
|
+
const key = `group:${g.groupId}`;
|
|
2150
|
+
const here = key === currentScopeKey ? " (\u4F60\u5F53\u524D\u6240\u5728)" : "";
|
|
2151
|
+
lines.push(`- ${key} \u2014 ${g.name}${here}`);
|
|
2152
|
+
}
|
|
2153
|
+
const text = [
|
|
2154
|
+
`\u4F60\u76EE\u524D\u6709 ${1 + myGroups.length} \u4E2A"\u5206\u8EAB scope"\uFF1A`,
|
|
2155
|
+
"",
|
|
2156
|
+
...lines,
|
|
2157
|
+
"",
|
|
2158
|
+
'\u8C03 neural_send(target_scope="...") \u65F6\u8BF7\u7528\u4E0A\u9762\u5217\u51FA\u7684 scope \u5B57\u7B26\u4E32\u3002',
|
|
2159
|
+
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"
|
|
2160
|
+
].join("\n");
|
|
2161
|
+
logger4.info("neural_list_scopes returned", {
|
|
2162
|
+
agentId: deps.agentId,
|
|
2163
|
+
scope: currentScopeKey,
|
|
2164
|
+
groupCount: myGroups.length
|
|
2165
|
+
});
|
|
2166
|
+
return { content: [{ type: "text", text }] };
|
|
2167
|
+
},
|
|
2168
|
+
{}
|
|
2169
|
+
);
|
|
2170
|
+
const tools = [neuralSend, neuralListScopes];
|
|
2171
|
+
if (selfNote) tools.push(selfNote);
|
|
1908
2172
|
const neuralServer = sdk.createSdkMcpServer({
|
|
1909
2173
|
name: "neural",
|
|
1910
2174
|
version: "2.0.0",
|
|
1911
|
-
tools
|
|
2175
|
+
tools
|
|
1912
2176
|
});
|
|
1913
|
-
|
|
2177
|
+
const toolNames = ["neural_send", "neural_list_scopes"];
|
|
2178
|
+
if (selfNote) toolNames.push("self_note");
|
|
2179
|
+
logger4.info("Neural MCP server created", {
|
|
1914
2180
|
agentId: deps.agentId,
|
|
1915
2181
|
scope: currentScopeKey,
|
|
1916
|
-
tools:
|
|
2182
|
+
tools: toolNames
|
|
1917
2183
|
});
|
|
1918
2184
|
return neuralServer;
|
|
1919
2185
|
}
|
|
1920
2186
|
|
|
1921
2187
|
// src/sdkEventMapper.ts
|
|
1922
|
-
var
|
|
2188
|
+
var logger5 = createModuleLogger("sdk.mapper");
|
|
1923
2189
|
function getTaskBase(proc) {
|
|
1924
2190
|
const task = proc.currentTask;
|
|
1925
2191
|
if (!task) return null;
|
|
@@ -1989,7 +2255,7 @@ function emitGroupSegment(proc, emit, base, content, contentBlocks) {
|
|
|
1989
2255
|
const groupId = proc.currentTask?.groupId;
|
|
1990
2256
|
if (!groupId) return;
|
|
1991
2257
|
proc.segmentCount += 1;
|
|
1992
|
-
|
|
2258
|
+
logger5.info("Group segment emitted", {
|
|
1993
2259
|
agentId: base.agentId,
|
|
1994
2260
|
replyMessageId: base.replyMessageId,
|
|
1995
2261
|
groupId,
|
|
@@ -2019,7 +2285,7 @@ function flushTextSegmentOnBlockStop(proc, emit, base) {
|
|
|
2019
2285
|
emitGroupSegment(proc, emit, base, proc.segmentBuffer, proc.contentBlocks);
|
|
2020
2286
|
proc.contentBlocks = [];
|
|
2021
2287
|
} else {
|
|
2022
|
-
|
|
2288
|
+
logger5.info("Group text block flushed but skipped (no segment emitted)", {
|
|
2023
2289
|
agentId: base.agentId,
|
|
2024
2290
|
replyMessageId: base.replyMessageId,
|
|
2025
2291
|
groupId: proc.currentTask?.groupId,
|
|
@@ -2043,7 +2309,7 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted) {
|
|
|
2043
2309
|
if (proc.status === "starting") {
|
|
2044
2310
|
proc.status = "ready";
|
|
2045
2311
|
}
|
|
2046
|
-
|
|
2312
|
+
logger5.info("Agent session initialized", {
|
|
2047
2313
|
agentId: proc.agentId,
|
|
2048
2314
|
scope: proc.scope.kind === "single" ? "single" : proc.scope.groupId,
|
|
2049
2315
|
sessionId: initMsg.session_id,
|
|
@@ -2106,7 +2372,7 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted) {
|
|
|
2106
2372
|
}
|
|
2107
2373
|
} else if (delta.type === "text_delta" && typeof delta.text === "string") {
|
|
2108
2374
|
if (proc.accumulatedText.length === 0) {
|
|
2109
|
-
|
|
2375
|
+
logger5.info("Agent text stream started", {
|
|
2110
2376
|
agentId: proc.agentId,
|
|
2111
2377
|
replyMessageId: base.replyMessageId,
|
|
2112
2378
|
traceId: base.traceId,
|
|
@@ -2145,7 +2411,7 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted) {
|
|
|
2145
2411
|
try {
|
|
2146
2412
|
parsedInput = JSON.parse(proc.accumulatedToolInput);
|
|
2147
2413
|
} catch {
|
|
2148
|
-
|
|
2414
|
+
logger5.warn("Failed to parse tool input JSON", {
|
|
2149
2415
|
agentId: proc.agentId,
|
|
2150
2416
|
toolName: proc.currentToolName,
|
|
2151
2417
|
inputLen: proc.accumulatedToolInput.length,
|
|
@@ -2160,7 +2426,7 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted) {
|
|
|
2160
2426
|
if (proc.currentToolName === "TodoWrite") {
|
|
2161
2427
|
const todos = extractTodosFromInput(parsedInput);
|
|
2162
2428
|
if (todos) {
|
|
2163
|
-
|
|
2429
|
+
logger5.info("TodoWrite detected, emitting agent:todos_update", {
|
|
2164
2430
|
agentId: proc.agentId,
|
|
2165
2431
|
replyMessageId: base.replyMessageId,
|
|
2166
2432
|
groupId: proc.currentTask?.groupId,
|
|
@@ -2177,7 +2443,7 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted) {
|
|
|
2177
2443
|
}
|
|
2178
2444
|
});
|
|
2179
2445
|
} else {
|
|
2180
|
-
|
|
2446
|
+
logger5.info("TodoWrite detected with empty/cancel todos", {
|
|
2181
2447
|
agentId: proc.agentId,
|
|
2182
2448
|
replyMessageId: base.replyMessageId,
|
|
2183
2449
|
traceId: base.traceId
|
|
@@ -2254,7 +2520,7 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted) {
|
|
|
2254
2520
|
const groupMode = groupId != null;
|
|
2255
2521
|
const usage = extractUsage(successMsg);
|
|
2256
2522
|
if (trimmed === NO_REPLY_TOKEN) {
|
|
2257
|
-
|
|
2523
|
+
logger5.info("Agent chose not to reply", {
|
|
2258
2524
|
agentId: proc.agentId,
|
|
2259
2525
|
replyMessageId: base.replyMessageId,
|
|
2260
2526
|
traceId: base.traceId,
|
|
@@ -2279,13 +2545,13 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted) {
|
|
|
2279
2545
|
}
|
|
2280
2546
|
if (groupMode) {
|
|
2281
2547
|
if (usage.inputTokens && usage.inputTokens > 15e4) {
|
|
2282
|
-
|
|
2548
|
+
logger5.warn("Agent context window approaching limit", {
|
|
2283
2549
|
agentId: proc.agentId,
|
|
2284
2550
|
inputTokens: usage.inputTokens
|
|
2285
2551
|
});
|
|
2286
2552
|
}
|
|
2287
2553
|
if (proc.contentBlocks.length > 0) {
|
|
2288
|
-
|
|
2554
|
+
logger5.info("Group turn trailing audit segment", {
|
|
2289
2555
|
agentId: proc.agentId,
|
|
2290
2556
|
replyMessageId: base.replyMessageId,
|
|
2291
2557
|
blockCount: proc.contentBlocks.length,
|
|
@@ -2294,7 +2560,7 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted) {
|
|
|
2294
2560
|
emitGroupSegment(proc, emit, base, "", proc.contentBlocks);
|
|
2295
2561
|
proc.contentBlocks = [];
|
|
2296
2562
|
}
|
|
2297
|
-
|
|
2563
|
+
logger5.info("Group task turn complete", {
|
|
2298
2564
|
agentId: proc.agentId,
|
|
2299
2565
|
replyMessageId: base.replyMessageId,
|
|
2300
2566
|
groupId,
|
|
@@ -2319,13 +2585,13 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted) {
|
|
|
2319
2585
|
proc.contentBlocks.push({ type: "text", content: proc.accumulatedText });
|
|
2320
2586
|
}
|
|
2321
2587
|
if (usage.inputTokens && usage.inputTokens > 15e4) {
|
|
2322
|
-
|
|
2588
|
+
logger5.warn("Agent context window approaching limit", {
|
|
2323
2589
|
agentId: proc.agentId,
|
|
2324
2590
|
inputTokens: usage.inputTokens
|
|
2325
2591
|
});
|
|
2326
2592
|
}
|
|
2327
2593
|
carrierMessageId = createMessageId();
|
|
2328
|
-
|
|
2594
|
+
logger5.info("Agent task done, emitting agent:done", {
|
|
2329
2595
|
agentId: proc.agentId,
|
|
2330
2596
|
ackId: base.replyMessageId,
|
|
2331
2597
|
messageId: carrierMessageId,
|
|
@@ -2352,7 +2618,7 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted) {
|
|
|
2352
2618
|
} else {
|
|
2353
2619
|
const errorMsg = resultMsg;
|
|
2354
2620
|
const errorText = errorMsg.errors?.join("; ") ?? `Agent error: ${resultMsg.subtype}`;
|
|
2355
|
-
|
|
2621
|
+
logger5.warn("Agent task error, emitting agent:error", {
|
|
2356
2622
|
agentId: proc.agentId,
|
|
2357
2623
|
replyMessageId: base.replyMessageId,
|
|
2358
2624
|
subtype: resultMsg.subtype,
|
|
@@ -2371,7 +2637,7 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted) {
|
|
|
2371
2637
|
case "assistant":
|
|
2372
2638
|
break;
|
|
2373
2639
|
default:
|
|
2374
|
-
|
|
2640
|
+
logger5.warn("Unhandled SDK message type", {
|
|
2375
2641
|
type: message.type,
|
|
2376
2642
|
agentId: proc.agentId
|
|
2377
2643
|
});
|
|
@@ -2391,7 +2657,7 @@ function resetAccumulators(proc) {
|
|
|
2391
2657
|
|
|
2392
2658
|
// src/wsMetrics.ts
|
|
2393
2659
|
import { monitorEventLoopDelay } from "perf_hooks";
|
|
2394
|
-
var
|
|
2660
|
+
var logger6 = createModuleLogger("ws.metrics");
|
|
2395
2661
|
var WsMetrics = class {
|
|
2396
2662
|
recv = /* @__PURE__ */ new Map();
|
|
2397
2663
|
send = /* @__PURE__ */ new Map();
|
|
@@ -2438,7 +2704,7 @@ var WsMetrics = class {
|
|
|
2438
2704
|
const sendSum = [...this.send.values()].reduce((a, b) => a + b, 0);
|
|
2439
2705
|
const sdkSum = [...this.sdkOut.values()].reduce((a, b) => a + b, 0);
|
|
2440
2706
|
if (recvSum + sendSum + sdkSum === 0 && (stats.loopMaxMs ?? 0) < 50) return;
|
|
2441
|
-
|
|
2707
|
+
logger6.info("WS metrics", {
|
|
2442
2708
|
windowMs: intervalMs,
|
|
2443
2709
|
...stats,
|
|
2444
2710
|
sums: { recv: recvSum, send: sendSum, sdkOut: sdkSum },
|
|
@@ -2454,7 +2720,19 @@ var WsMetrics = class {
|
|
|
2454
2720
|
var wsMetrics = new WsMetrics();
|
|
2455
2721
|
|
|
2456
2722
|
// src/agentManager.ts
|
|
2457
|
-
var
|
|
2723
|
+
var logger7 = createModuleLogger("agent.manager");
|
|
2724
|
+
var NOTEBOOK_SECTION_HEADER = `# Your personal notebook
|
|
2725
|
+
The following is your own running notebook. It is private to you and follows
|
|
2726
|
+
you across every scope (single chat and every group). Treat it as your memory
|
|
2727
|
+
of what you've decided, learned, or committed to.`;
|
|
2728
|
+
var SCOPES_SECTION_HEADER = `# Your scopes (where "you" exist)
|
|
2729
|
+
You currently have a presence in the following conversations. Each is a separate
|
|
2730
|
+
runtime of "you" with its own context. neural_send can ONLY reach scopes in this
|
|
2731
|
+
list \u2014 you have no "self" anywhere else.
|
|
2732
|
+
|
|
2733
|
+
This snapshot was taken when this runtime started. If you suspect it's stale
|
|
2734
|
+
(e.g., the user just told you they pulled you into a new group), call
|
|
2735
|
+
neural_list_scopes() once to refresh.`;
|
|
2458
2736
|
var BridgeBusyError = class extends Error {
|
|
2459
2737
|
constructor(message = "Bridge busy: cannot evict an idle Agent query; all slots are working") {
|
|
2460
2738
|
super(message);
|
|
@@ -2468,6 +2746,7 @@ var AgentManager = class {
|
|
|
2468
2746
|
emit;
|
|
2469
2747
|
workspacesDir;
|
|
2470
2748
|
claudeConfigDir;
|
|
2749
|
+
memoryStore;
|
|
2471
2750
|
queryConfig;
|
|
2472
2751
|
askQuestionRegistry;
|
|
2473
2752
|
groupRegistry;
|
|
@@ -2479,18 +2758,20 @@ var AgentManager = class {
|
|
|
2479
2758
|
this.emit = emit;
|
|
2480
2759
|
if (typeof options === "function") {
|
|
2481
2760
|
this.queryFn = options;
|
|
2482
|
-
this.workspacesDir =
|
|
2483
|
-
this.claudeConfigDir =
|
|
2761
|
+
this.workspacesDir = path7.join(os4.homedir(), ".ahchat", "workspaces");
|
|
2762
|
+
this.claudeConfigDir = path7.join(os4.homedir(), ".ahchat", "claude-config");
|
|
2484
2763
|
this.queryConfig = DEFAULT_QUERY_CONFIG;
|
|
2485
2764
|
this.askQuestionRegistry = new AskQuestionRegistry();
|
|
2486
2765
|
this.groupRegistry = null;
|
|
2766
|
+
this.memoryStore = null;
|
|
2487
2767
|
} else {
|
|
2488
2768
|
this.queryFn = options?.queryFn ?? null;
|
|
2489
|
-
this.workspacesDir = options?.workspacesDir ??
|
|
2490
|
-
this.claudeConfigDir = options?.claudeConfigDir ??
|
|
2769
|
+
this.workspacesDir = options?.workspacesDir ?? path7.join(os4.homedir(), ".ahchat", "workspaces");
|
|
2770
|
+
this.claudeConfigDir = options?.claudeConfigDir ?? path7.join(os4.homedir(), ".ahchat", "claude-config");
|
|
2491
2771
|
this.queryConfig = options?.queryConfig ?? DEFAULT_QUERY_CONFIG;
|
|
2492
2772
|
this.askQuestionRegistry = options?.askQuestionRegistry ?? new AskQuestionRegistry();
|
|
2493
2773
|
this.groupRegistry = options?.groupRegistry ?? null;
|
|
2774
|
+
this.memoryStore = options?.memoryStore ?? null;
|
|
2494
2775
|
}
|
|
2495
2776
|
this.evictionTimer = setInterval(() => {
|
|
2496
2777
|
void this.evictIdle();
|
|
@@ -2523,7 +2804,7 @@ var AgentManager = class {
|
|
|
2523
2804
|
})
|
|
2524
2805
|
]);
|
|
2525
2806
|
} catch (e) {
|
|
2526
|
-
|
|
2807
|
+
logger7.warn("awaitQueryReturn finished with error/timeout", { agentId, error: e });
|
|
2527
2808
|
}
|
|
2528
2809
|
}
|
|
2529
2810
|
/**
|
|
@@ -2546,13 +2827,13 @@ var AgentManager = class {
|
|
|
2546
2827
|
const proc = this.agents.get(key);
|
|
2547
2828
|
if (!proc || proc.status === "dead") return;
|
|
2548
2829
|
if (!this.isEvictable(proc)) return;
|
|
2549
|
-
|
|
2830
|
+
logger7.info("Evicting idle Agent query", { agentId: proc.agentId, scope: scopeKey(proc.scope) });
|
|
2550
2831
|
const runtime = this.asRuntime(proc);
|
|
2551
2832
|
try {
|
|
2552
2833
|
runtime.inputController.close();
|
|
2553
2834
|
await this.awaitQueryReturn(runtime.query, 5e3, proc.agentId);
|
|
2554
2835
|
} catch (e) {
|
|
2555
|
-
|
|
2836
|
+
logger7.error("closeIdleQuery failed", { agentId: proc.agentId, error: e });
|
|
2556
2837
|
}
|
|
2557
2838
|
proc.status = "dead";
|
|
2558
2839
|
this.agents.delete(key);
|
|
@@ -2618,9 +2899,9 @@ var AgentManager = class {
|
|
|
2618
2899
|
const savedSessionId = this.sessionStore.get(agentConfig.id, scope);
|
|
2619
2900
|
const inputController = new InputController();
|
|
2620
2901
|
const agentCwd = cwd;
|
|
2621
|
-
await
|
|
2902
|
+
await fs3.mkdir(agentCwd, { recursive: true });
|
|
2622
2903
|
const cfg = parseAgentConfig(agentConfig.config);
|
|
2623
|
-
|
|
2904
|
+
logger7.info("Creating Agent query", {
|
|
2624
2905
|
agentId: agentConfig.id,
|
|
2625
2906
|
scope: scopeKey(scope),
|
|
2626
2907
|
cwd: agentCwd,
|
|
@@ -2631,7 +2912,7 @@ var AgentManager = class {
|
|
|
2631
2912
|
const queryFn = await this.getQueryFn();
|
|
2632
2913
|
let procRef = null;
|
|
2633
2914
|
const cwdGuard = makeCwdPermissionGuard(agentCwd, agentConfig.id, scope, (msg, meta) => {
|
|
2634
|
-
|
|
2915
|
+
logger7.warn(msg, meta);
|
|
2635
2916
|
});
|
|
2636
2917
|
const askGuard = makeAskUserQuestionGuard({
|
|
2637
2918
|
agentId: agentConfig.id,
|
|
@@ -2644,14 +2925,17 @@ var AgentManager = class {
|
|
|
2644
2925
|
agentId: agentConfig.id,
|
|
2645
2926
|
scope,
|
|
2646
2927
|
groupRegistry: this.groupRegistry,
|
|
2647
|
-
onSend: (payload) => this.deliverNeuralSend(agentConfig, payload)
|
|
2928
|
+
onSend: (payload) => this.deliverNeuralSend(agentConfig, payload),
|
|
2929
|
+
memoryStore: this.memoryStore
|
|
2648
2930
|
});
|
|
2931
|
+
const notebookSection = this.buildNotebookSection(agentConfig.id);
|
|
2932
|
+
const scopesSection = this.buildScopesSection(agentConfig.id, scope);
|
|
2649
2933
|
const options = {
|
|
2650
2934
|
cwd: agentCwd,
|
|
2651
2935
|
systemPrompt: {
|
|
2652
2936
|
type: "preset",
|
|
2653
2937
|
preset: "claude_code",
|
|
2654
|
-
append: [PLATFORM_AGENT_RULES, agentConfig.systemPrompt].filter((s) => typeof s === "string" && s.trim().length > 0).join("\n\n")
|
|
2938
|
+
append: [PLATFORM_AGENT_RULES, agentConfig.systemPrompt, notebookSection, scopesSection].filter((s) => typeof s === "string" && s.trim().length > 0).join("\n\n")
|
|
2655
2939
|
},
|
|
2656
2940
|
permissionMode: "bypassPermissions",
|
|
2657
2941
|
allowDangerouslySkipPermissions: true,
|
|
@@ -2663,7 +2947,9 @@ var AgentManager = class {
|
|
|
2663
2947
|
"Glob",
|
|
2664
2948
|
"Grep",
|
|
2665
2949
|
"AskUserQuestion",
|
|
2666
|
-
"mcp__neural__neural_send"
|
|
2950
|
+
"mcp__neural__neural_send",
|
|
2951
|
+
"mcp__neural__neural_list_scopes",
|
|
2952
|
+
"mcp__neural__self_note"
|
|
2667
2953
|
],
|
|
2668
2954
|
mcpServers: { neural: neuralServer },
|
|
2669
2955
|
includePartialMessages: true,
|
|
@@ -2677,12 +2963,14 @@ var AgentManager = class {
|
|
|
2677
2963
|
};
|
|
2678
2964
|
const userPromptTrimmed = (agentConfig.systemPrompt ?? "").trim();
|
|
2679
2965
|
const appendStr = options.systemPrompt.append;
|
|
2680
|
-
|
|
2966
|
+
logger7.info("Platform rules attached", {
|
|
2681
2967
|
agentId: agentConfig.id,
|
|
2682
2968
|
scope: scopeKey(scope),
|
|
2683
2969
|
platformRulesLen: PLATFORM_AGENT_RULES.length,
|
|
2684
2970
|
userPromptLen: userPromptTrimmed.length,
|
|
2685
2971
|
hasUserPrompt: userPromptTrimmed.length > 0,
|
|
2972
|
+
notebookLen: notebookSection.length,
|
|
2973
|
+
scopesLen: scopesSection.length,
|
|
2686
2974
|
appendLen: appendStr.length
|
|
2687
2975
|
});
|
|
2688
2976
|
if (cfg.model) {
|
|
@@ -2724,6 +3012,58 @@ var AgentManager = class {
|
|
|
2724
3012
|
this.consumeOutput(runtime);
|
|
2725
3013
|
return proc;
|
|
2726
3014
|
}
|
|
3015
|
+
buildNotebookSection(agentId) {
|
|
3016
|
+
if (!this.memoryStore) {
|
|
3017
|
+
logger7.info("Notebook injection skipped", { agentId, reason: "no_memory_store" });
|
|
3018
|
+
return "";
|
|
3019
|
+
}
|
|
3020
|
+
const raw = this.memoryStore.read(agentId);
|
|
3021
|
+
const trimmed = raw.trim();
|
|
3022
|
+
if (trimmed.length === 0) {
|
|
3023
|
+
logger7.info("Notebook injection skipped", {
|
|
3024
|
+
agentId,
|
|
3025
|
+
reason: raw.length === 0 ? "empty_or_missing" : "whitespace_only",
|
|
3026
|
+
rawBytes: raw.length
|
|
3027
|
+
});
|
|
3028
|
+
return "";
|
|
3029
|
+
}
|
|
3030
|
+
const section = `${NOTEBOOK_SECTION_HEADER}
|
|
3031
|
+
|
|
3032
|
+
${trimmed}`;
|
|
3033
|
+
logger7.info("Notebook injected into system prompt", {
|
|
3034
|
+
agentId,
|
|
3035
|
+
rawBytes: raw.length,
|
|
3036
|
+
trimmedBytes: trimmed.length,
|
|
3037
|
+
sectionLen: section.length
|
|
3038
|
+
});
|
|
3039
|
+
return section;
|
|
3040
|
+
}
|
|
3041
|
+
buildScopesSection(agentId, scope) {
|
|
3042
|
+
if (!this.groupRegistry) {
|
|
3043
|
+
logger7.info("Scopes injection skipped", { agentId, reason: "no_group_registry" });
|
|
3044
|
+
return "";
|
|
3045
|
+
}
|
|
3046
|
+
const myGroups = this.groupRegistry.getMyGroups(agentId);
|
|
3047
|
+
const curKey = scopeKey(scope);
|
|
3048
|
+
const lines = [];
|
|
3049
|
+
const isCurSingle = curKey === "single";
|
|
3050
|
+
lines.push(`- single \u2014 1:1 chat with the user${isCurSingle ? " (you are here)" : ""}`);
|
|
3051
|
+
for (const g of myGroups) {
|
|
3052
|
+
const key = `group:${g.groupId}`;
|
|
3053
|
+
const here = key === curKey ? " (you are here)" : "";
|
|
3054
|
+
lines.push(`- ${key} \u2014 ${g.name}${here}`);
|
|
3055
|
+
}
|
|
3056
|
+
const section = `${SCOPES_SECTION_HEADER}
|
|
3057
|
+
|
|
3058
|
+
${lines.join("\n")}`;
|
|
3059
|
+
logger7.info("Scopes injected into system prompt", {
|
|
3060
|
+
agentId,
|
|
3061
|
+
scope: curKey,
|
|
3062
|
+
groupCount: myGroups.length,
|
|
3063
|
+
sectionLen: section.length
|
|
3064
|
+
});
|
|
3065
|
+
return section;
|
|
3066
|
+
}
|
|
2727
3067
|
async sendMessage(task) {
|
|
2728
3068
|
const key = runtimeKey(task.agentId, task.scope);
|
|
2729
3069
|
const proc = this.agents.get(key);
|
|
@@ -2736,7 +3076,7 @@ var AgentManager = class {
|
|
|
2736
3076
|
return;
|
|
2737
3077
|
}
|
|
2738
3078
|
if (proc.status === "starting") {
|
|
2739
|
-
|
|
3079
|
+
logger7.info("Message dispatched to starting Agent (kickstart)", {
|
|
2740
3080
|
agentId: task.agentId,
|
|
2741
3081
|
replyMessageId: task.replyMessageId,
|
|
2742
3082
|
traceId: task.traceId
|
|
@@ -2749,7 +3089,7 @@ var AgentManager = class {
|
|
|
2749
3089
|
if (idx >= 0) {
|
|
2750
3090
|
runtime.injectedTasks.splice(idx, 1);
|
|
2751
3091
|
runtime.mergedTasks.push(task);
|
|
2752
|
-
|
|
3092
|
+
logger7.info("Injected task consumed by SDK (queued as merged until next result)", {
|
|
2753
3093
|
agentId: runtime.agentId,
|
|
2754
3094
|
replyMessageId: task.replyMessageId,
|
|
2755
3095
|
traceId: task.traceId,
|
|
@@ -2760,7 +3100,7 @@ var AgentManager = class {
|
|
|
2760
3100
|
};
|
|
2761
3101
|
runtime.inputController.push(task.content, runtime.ccSessionId ?? "", onYielded);
|
|
2762
3102
|
runtime.injectedTasks.push(task);
|
|
2763
|
-
|
|
3103
|
+
logger7.info("Message injected while Agent working", {
|
|
2764
3104
|
agentId: task.agentId,
|
|
2765
3105
|
replyMessageId: task.replyMessageId,
|
|
2766
3106
|
currentStatus: proc.status,
|
|
@@ -2773,7 +3113,7 @@ var AgentManager = class {
|
|
|
2773
3113
|
runtime.currentTask = task;
|
|
2774
3114
|
runtime.currentTaskStartedAt = Date.now();
|
|
2775
3115
|
runtime.inputController.push(task.content, runtime.ccSessionId ?? "");
|
|
2776
|
-
|
|
3116
|
+
logger7.info("Message pushed to Agent", {
|
|
2777
3117
|
agentId: runtime.agentId,
|
|
2778
3118
|
replyMessageId: task.replyMessageId,
|
|
2779
3119
|
traceId: task.traceId
|
|
@@ -2793,7 +3133,7 @@ var AgentManager = class {
|
|
|
2793
3133
|
const completedTask = proc.currentTask;
|
|
2794
3134
|
if (completedTask && runtime.mergedTasks.length > 0) {
|
|
2795
3135
|
const mergedBatch = [...runtime.mergedTasks];
|
|
2796
|
-
|
|
3136
|
+
logger7.info("Flushing merged tasks after result", {
|
|
2797
3137
|
agentId: proc.agentId,
|
|
2798
3138
|
carrierReplyMessageId: completedTask.replyMessageId,
|
|
2799
3139
|
mergedCount: mergedBatch.length,
|
|
@@ -2801,7 +3141,7 @@ var AgentManager = class {
|
|
|
2801
3141
|
traceId: completedTask.traceId
|
|
2802
3142
|
});
|
|
2803
3143
|
for (const merged of mergedBatch) {
|
|
2804
|
-
|
|
3144
|
+
logger7.info("Emitting agent:merged for task consumed in same turn", {
|
|
2805
3145
|
agentId: proc.agentId,
|
|
2806
3146
|
ackId: merged.replyMessageId,
|
|
2807
3147
|
mergedIntoAckId: completedTask.replyMessageId,
|
|
@@ -2823,7 +3163,7 @@ var AgentManager = class {
|
|
|
2823
3163
|
}
|
|
2824
3164
|
runtime.mergedTasks = [];
|
|
2825
3165
|
} else if (runtime.mergedTasks.length > 0) {
|
|
2826
|
-
|
|
3166
|
+
logger7.warn("mergedTasks non-empty but no currentTask; dropping", {
|
|
2827
3167
|
agentId: proc.agentId,
|
|
2828
3168
|
mergedCount: runtime.mergedTasks.length
|
|
2829
3169
|
});
|
|
@@ -2835,7 +3175,7 @@ var AgentManager = class {
|
|
|
2835
3175
|
proc.currentTask = next;
|
|
2836
3176
|
proc.status = "working";
|
|
2837
3177
|
proc.currentTaskStartedAt = Date.now();
|
|
2838
|
-
|
|
3178
|
+
logger7.info("Promoted next injected task after result", {
|
|
2839
3179
|
agentId: proc.agentId,
|
|
2840
3180
|
replyMessageId: next.replyMessageId,
|
|
2841
3181
|
remainingInjected: runtime.injectedTasks.length,
|
|
@@ -2879,7 +3219,7 @@ var AgentManager = class {
|
|
|
2879
3219
|
};
|
|
2880
3220
|
const key = runtimeKey(agentConfig.id, targetScope);
|
|
2881
3221
|
const existingProc = this.agents.get(key);
|
|
2882
|
-
|
|
3222
|
+
logger7.info("Neural send dispatching", {
|
|
2883
3223
|
agentId: agentConfig.id,
|
|
2884
3224
|
fromScope: payload.fromScopeKey,
|
|
2885
3225
|
toScope: payload.toScopeKey,
|
|
@@ -2892,7 +3232,7 @@ var AgentManager = class {
|
|
|
2892
3232
|
});
|
|
2893
3233
|
if (existingProc && existingProc.status !== "dead") {
|
|
2894
3234
|
if (existingProc.status === "ready" || existingProc.status === "starting") {
|
|
2895
|
-
|
|
3235
|
+
logger7.info("Neural send dispatched to idle runtime", {
|
|
2896
3236
|
agentId: agentConfig.id,
|
|
2897
3237
|
toScope: payload.toScopeKey,
|
|
2898
3238
|
replyMessageId: task.replyMessageId,
|
|
@@ -2903,7 +3243,7 @@ var AgentManager = class {
|
|
|
2903
3243
|
const runtime = this.asRuntime(existingProc);
|
|
2904
3244
|
runtime.inputController.push(task.content, runtime.ccSessionId ?? "");
|
|
2905
3245
|
runtime.injectedTasks.push(task);
|
|
2906
|
-
|
|
3246
|
+
logger7.info("Neural send injected mid-turn", {
|
|
2907
3247
|
agentId: agentConfig.id,
|
|
2908
3248
|
toScope: payload.toScopeKey,
|
|
2909
3249
|
replyMessageId: task.replyMessageId,
|
|
@@ -2915,7 +3255,7 @@ var AgentManager = class {
|
|
|
2915
3255
|
let cwd;
|
|
2916
3256
|
if (targetScope.kind === "group") {
|
|
2917
3257
|
if (!payload.targetCwd) {
|
|
2918
|
-
|
|
3258
|
+
logger7.error("Neural send abort: group target missing targetCwd", {
|
|
2919
3259
|
agentId: agentConfig.id,
|
|
2920
3260
|
toScope: payload.toScopeKey
|
|
2921
3261
|
});
|
|
@@ -2923,10 +3263,10 @@ var AgentManager = class {
|
|
|
2923
3263
|
}
|
|
2924
3264
|
cwd = payload.targetCwd;
|
|
2925
3265
|
} else {
|
|
2926
|
-
cwd = agentConfig.workingDirectory ||
|
|
3266
|
+
cwd = agentConfig.workingDirectory || path7.join(this.workspacesDir, agentConfig.id);
|
|
2927
3267
|
}
|
|
2928
3268
|
void this.acquire(agentConfig, targetScope, cwd).then(() => {
|
|
2929
|
-
|
|
3269
|
+
logger7.info("Neural send new runtime acquired", {
|
|
2930
3270
|
agentId: agentConfig.id,
|
|
2931
3271
|
toScope: payload.toScopeKey,
|
|
2932
3272
|
cwd,
|
|
@@ -2934,7 +3274,7 @@ var AgentManager = class {
|
|
|
2934
3274
|
});
|
|
2935
3275
|
return this.sendMessage({ ...task, agentId: agentConfig.id, scope: targetScope });
|
|
2936
3276
|
}).catch((err) => {
|
|
2937
|
-
|
|
3277
|
+
logger7.error("Neural send acquire failed", {
|
|
2938
3278
|
agentId: agentConfig.id,
|
|
2939
3279
|
toScope: payload.toScopeKey,
|
|
2940
3280
|
cwd,
|
|
@@ -2950,7 +3290,7 @@ var AgentManager = class {
|
|
|
2950
3290
|
(k) => k === agentId || k.startsWith(`${agentId}::`)
|
|
2951
3291
|
);
|
|
2952
3292
|
if (keys.length === 0) {
|
|
2953
|
-
|
|
3293
|
+
logger7.warn("terminate: no process for agent", { agentId });
|
|
2954
3294
|
this.sessionStore.deleteAllForAgent(agentId);
|
|
2955
3295
|
return;
|
|
2956
3296
|
}
|
|
@@ -2961,19 +3301,19 @@ var AgentManager = class {
|
|
|
2961
3301
|
}
|
|
2962
3302
|
}
|
|
2963
3303
|
this.sessionStore.deleteAllForAgent(agentId);
|
|
2964
|
-
|
|
3304
|
+
logger7.info("terminate: all scoped queries removed", { agentId, count: keys.length });
|
|
2965
3305
|
}
|
|
2966
3306
|
/** Stop one scoped SDK runtime (workdir change). */
|
|
2967
3307
|
async terminateScope(agentId, scope) {
|
|
2968
3308
|
const key = runtimeKey(agentId, scope);
|
|
2969
3309
|
const proc = this.agents.get(key);
|
|
2970
3310
|
if (!proc) {
|
|
2971
|
-
|
|
3311
|
+
logger7.info("terminateScope: no active runtime", { agentId, scope: scopeKey(scope) });
|
|
2972
3312
|
return;
|
|
2973
3313
|
}
|
|
2974
3314
|
await this.closeRuntime(proc, "terminateScope");
|
|
2975
3315
|
this.sessionStore.delete(agentId, scope);
|
|
2976
|
-
|
|
3316
|
+
logger7.info("terminateScope: scoped query removed", { agentId, scope: scopeKey(scope) });
|
|
2977
3317
|
}
|
|
2978
3318
|
async closeRuntime(proc, reason) {
|
|
2979
3319
|
const key = runtimeKey(proc.agentId, proc.scope);
|
|
@@ -3003,7 +3343,7 @@ var AgentManager = class {
|
|
|
3003
3343
|
runtime.mergedTasks = [];
|
|
3004
3344
|
for (const t of mergedAtClose) {
|
|
3005
3345
|
if (runtime.currentTask) {
|
|
3006
|
-
|
|
3346
|
+
logger7.info("Emitting agent:merged on runtime close", {
|
|
3007
3347
|
agentId: runtime.agentId,
|
|
3008
3348
|
replyMessageId: t.replyMessageId,
|
|
3009
3349
|
mergedInto: runtime.currentTask.replyMessageId,
|
|
@@ -3030,45 +3370,45 @@ var AgentManager = class {
|
|
|
3030
3370
|
runtime.inputController.close();
|
|
3031
3371
|
await this.awaitQueryReturn(runtime.query, 5e3, agentId);
|
|
3032
3372
|
} catch (e) {
|
|
3033
|
-
|
|
3373
|
+
logger7.error(`${reason}: close query failed`, { agentId, scope: scopeKey(proc.scope), error: e });
|
|
3034
3374
|
}
|
|
3035
3375
|
proc.status = "dead";
|
|
3036
3376
|
this.agents.delete(key);
|
|
3037
3377
|
this.lastUsedAt.delete(key);
|
|
3038
|
-
|
|
3378
|
+
logger7.info(`${reason}: keeping workspace dir intact (per project policy)`, {
|
|
3039
3379
|
agentId,
|
|
3040
3380
|
scope: scopeKey(proc.scope),
|
|
3041
3381
|
cwd: proc.cwd
|
|
3042
3382
|
});
|
|
3043
3383
|
}
|
|
3044
3384
|
async recoverFromRestart(agents) {
|
|
3045
|
-
|
|
3385
|
+
logger7.info("Recovering Agent sessions after restart", { count: agents.length });
|
|
3046
3386
|
const agentsWithSession = agents.filter((a) => {
|
|
3047
3387
|
const sessionId = this.sessionStore.get(a.id, { kind: "single" });
|
|
3048
3388
|
return !!sessionId;
|
|
3049
3389
|
});
|
|
3050
3390
|
if (agentsWithSession.length === 0) {
|
|
3051
|
-
|
|
3391
|
+
logger7.info("No Agent sessions to recover");
|
|
3052
3392
|
return;
|
|
3053
3393
|
}
|
|
3054
3394
|
let warmed = 0;
|
|
3055
3395
|
const cap = this.queryConfig.maxActive;
|
|
3056
3396
|
for (const agent of agentsWithSession) {
|
|
3057
3397
|
if (warmed >= cap) {
|
|
3058
|
-
|
|
3398
|
+
logger7.info("Recovery warm cap reached", { cap, skipped: agentsWithSession.length - warmed });
|
|
3059
3399
|
break;
|
|
3060
3400
|
}
|
|
3061
3401
|
try {
|
|
3062
|
-
const cwd = agent.workingDirectory ||
|
|
3402
|
+
const cwd = agent.workingDirectory || path7.join(this.workspacesDir, agent.id);
|
|
3063
3403
|
await this.acquire(agent, { kind: "single" }, cwd);
|
|
3064
3404
|
warmed++;
|
|
3065
|
-
|
|
3405
|
+
logger7.info("Agent process pre-created for recovery", { agentId: agent.id });
|
|
3066
3406
|
} catch (err) {
|
|
3067
3407
|
if (err instanceof BridgeBusyError) {
|
|
3068
|
-
|
|
3408
|
+
logger7.warn("Recovery stopped: bridge busy", { agentId: agent.id });
|
|
3069
3409
|
break;
|
|
3070
3410
|
}
|
|
3071
|
-
|
|
3411
|
+
logger7.warn("Failed to pre-create Agent for recovery, clearing session", {
|
|
3072
3412
|
agentId: agent.id,
|
|
3073
3413
|
error: err
|
|
3074
3414
|
});
|
|
@@ -3092,7 +3432,7 @@ var AgentManager = class {
|
|
|
3092
3432
|
} catch (err) {
|
|
3093
3433
|
const errMsg = err.message ?? String(err);
|
|
3094
3434
|
const isResumeFail = /session|conversation.*not found/i.test(errMsg);
|
|
3095
|
-
|
|
3435
|
+
logger7.error("Agent query stream ended with error", {
|
|
3096
3436
|
agentId: runtime.agentId,
|
|
3097
3437
|
scope: scopeKey(runtime.scope),
|
|
3098
3438
|
isResumeFail,
|
|
@@ -3100,7 +3440,7 @@ var AgentManager = class {
|
|
|
3100
3440
|
error: err
|
|
3101
3441
|
});
|
|
3102
3442
|
this.sessionStore.delete(runtime.agentId, runtime.scope);
|
|
3103
|
-
|
|
3443
|
+
logger7.info("Cleared stale session after query crash", {
|
|
3104
3444
|
agentId: runtime.agentId,
|
|
3105
3445
|
scope: scopeKey(runtime.scope)
|
|
3106
3446
|
});
|
|
@@ -3161,8 +3501,50 @@ var AgentManager = class {
|
|
|
3161
3501
|
}
|
|
3162
3502
|
return [...ids];
|
|
3163
3503
|
}
|
|
3504
|
+
/**
|
|
3505
|
+
* Push a system notice to ALL active runtimes of the given agent.
|
|
3506
|
+
*
|
|
3507
|
+
* Working runtimes: injected mid-turn via InputController (merged into
|
|
3508
|
+
* the current turn, no extra result expected).
|
|
3509
|
+
* Ready/starting runtimes: dispatched as a lightweight task so any SDK
|
|
3510
|
+
* output has valid replyMessageId/conversationId to land on.
|
|
3511
|
+
*/
|
|
3512
|
+
broadcastScopeNotice(agentId, notice) {
|
|
3513
|
+
let notified = 0;
|
|
3514
|
+
for (const [, proc] of this.agents) {
|
|
3515
|
+
if (proc.agentId !== agentId || proc.status === "dead") continue;
|
|
3516
|
+
const runtime = this.asRuntime(proc);
|
|
3517
|
+
if (proc.status === "working") {
|
|
3518
|
+
runtime.inputController.push(notice, runtime.ccSessionId ?? "");
|
|
3519
|
+
logger7.info("Scope notice injected mid-turn", {
|
|
3520
|
+
agentId,
|
|
3521
|
+
scope: scopeKey(proc.scope),
|
|
3522
|
+
noticeLen: notice.length
|
|
3523
|
+
});
|
|
3524
|
+
} else if (proc.status === "ready" || proc.status === "starting") {
|
|
3525
|
+
const task = {
|
|
3526
|
+
content: notice,
|
|
3527
|
+
replyMessageId: `msg_scopenotice_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`,
|
|
3528
|
+
conversationId: proc.currentTask?.conversationId ?? "",
|
|
3529
|
+
traceId: `tr_scopenotice_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`,
|
|
3530
|
+
groupId: proc.scope.kind === "group" ? proc.scope.groupId : void 0
|
|
3531
|
+
};
|
|
3532
|
+
this.dispatchToSDK(runtime, task);
|
|
3533
|
+
logger7.info("Scope notice dispatched to idle runtime", {
|
|
3534
|
+
agentId,
|
|
3535
|
+
scope: scopeKey(proc.scope),
|
|
3536
|
+
replyMessageId: task.replyMessageId
|
|
3537
|
+
});
|
|
3538
|
+
}
|
|
3539
|
+
notified++;
|
|
3540
|
+
}
|
|
3541
|
+
logger7.info("broadcastScopeNotice completed", {
|
|
3542
|
+
agentId,
|
|
3543
|
+
notifiedCount: notified
|
|
3544
|
+
});
|
|
3545
|
+
}
|
|
3164
3546
|
async shutdownAll() {
|
|
3165
|
-
|
|
3547
|
+
logger7.info("Shutting down all Agent processes", { count: this.agents.size });
|
|
3166
3548
|
this.askQuestionRegistry.cancelAll("agent_aborted");
|
|
3167
3549
|
if (this.evictionTimer) {
|
|
3168
3550
|
clearInterval(this.evictionTimer);
|
|
@@ -3174,9 +3556,9 @@ var AgentManager = class {
|
|
|
3174
3556
|
runtime.inputController?.close();
|
|
3175
3557
|
runtime.query?.return(void 0);
|
|
3176
3558
|
proc.status = "dead";
|
|
3177
|
-
|
|
3559
|
+
logger7.info("Agent process shut down", { agentId: proc.agentId, scope: scopeKey(proc.scope), key });
|
|
3178
3560
|
} catch (err) {
|
|
3179
|
-
|
|
3561
|
+
logger7.error("Error shutting down Agent", { agentId: proc.agentId, error: err });
|
|
3180
3562
|
}
|
|
3181
3563
|
}
|
|
3182
3564
|
this.agents.clear();
|
|
@@ -3193,13 +3575,13 @@ var AgentManager = class {
|
|
|
3193
3575
|
}
|
|
3194
3576
|
}
|
|
3195
3577
|
if (!proc) {
|
|
3196
|
-
|
|
3578
|
+
logger7.warn("cancelReply: no active process for reply", { agentId, replyMessageId });
|
|
3197
3579
|
return;
|
|
3198
3580
|
}
|
|
3199
3581
|
const runtime = this.asRuntime(proc);
|
|
3200
3582
|
const key = runtimeKey(agentId, proc.scope);
|
|
3201
3583
|
if (!runtime.currentTask || runtime.currentTask.replyMessageId !== replyMessageId) {
|
|
3202
|
-
|
|
3584
|
+
logger7.warn("cancelReply: replyMessageId mismatch", {
|
|
3203
3585
|
agentId,
|
|
3204
3586
|
replyMessageId,
|
|
3205
3587
|
expected: runtime.currentTask?.replyMessageId
|
|
@@ -3228,7 +3610,7 @@ var AgentManager = class {
|
|
|
3228
3610
|
proc.status = "dead";
|
|
3229
3611
|
this.agents.delete(key);
|
|
3230
3612
|
this.lastUsedAt.delete(key);
|
|
3231
|
-
|
|
3613
|
+
logger7.info("cancelReply: process torn down", {
|
|
3232
3614
|
agentId,
|
|
3233
3615
|
scope: scopeKey(proc.scope),
|
|
3234
3616
|
conversationId,
|
|
@@ -3237,10 +3619,10 @@ var AgentManager = class {
|
|
|
3237
3619
|
try {
|
|
3238
3620
|
runtime.inputController.close();
|
|
3239
3621
|
} catch (err) {
|
|
3240
|
-
|
|
3622
|
+
logger7.error("cancelReply: inputController.close failed", { agentId, error: err });
|
|
3241
3623
|
}
|
|
3242
3624
|
runtime.query.return(void 0).catch((err) => {
|
|
3243
|
-
|
|
3625
|
+
logger7.warn("cancelReply: query.return threw", { agentId, error: err });
|
|
3244
3626
|
});
|
|
3245
3627
|
}
|
|
3246
3628
|
};
|
|
@@ -3257,7 +3639,7 @@ function buildInnerVoiceEnvelope(payload) {
|
|
|
3257
3639
|
}
|
|
3258
3640
|
|
|
3259
3641
|
// src/agentRegistry.ts
|
|
3260
|
-
var
|
|
3642
|
+
var logger8 = createModuleLogger("agent.registry");
|
|
3261
3643
|
var HttpAgentRegistry = class {
|
|
3262
3644
|
constructor(serverApiUrl) {
|
|
3263
3645
|
this.serverApiUrl = serverApiUrl;
|
|
@@ -3266,8 +3648,8 @@ var HttpAgentRegistry = class {
|
|
|
3266
3648
|
agents = /* @__PURE__ */ new Map();
|
|
3267
3649
|
apiUrl(suffix) {
|
|
3268
3650
|
const base = this.serverApiUrl.replace(/\/$/, "");
|
|
3269
|
-
const
|
|
3270
|
-
return `${base}${
|
|
3651
|
+
const path12 = suffix.startsWith("/") ? suffix : `/${suffix}`;
|
|
3652
|
+
return `${base}${path12}`;
|
|
3271
3653
|
}
|
|
3272
3654
|
async refresh() {
|
|
3273
3655
|
const attempt = async () => {
|
|
@@ -3285,17 +3667,17 @@ var HttpAgentRegistry = class {
|
|
|
3285
3667
|
recoveredAfterRetry = res != null;
|
|
3286
3668
|
}
|
|
3287
3669
|
if (!res) {
|
|
3288
|
-
|
|
3670
|
+
logger8.warn("Agent registry refresh unreachable after retry, keeping cache");
|
|
3289
3671
|
return;
|
|
3290
3672
|
}
|
|
3291
3673
|
if (!res.ok) {
|
|
3292
|
-
|
|
3674
|
+
logger8.warn("Agent registry refresh failed", { status: res.status });
|
|
3293
3675
|
return;
|
|
3294
3676
|
}
|
|
3295
3677
|
try {
|
|
3296
3678
|
const body = await res.json();
|
|
3297
3679
|
if (!Array.isArray(body)) {
|
|
3298
|
-
|
|
3680
|
+
logger8.warn("Agent registry refresh: expected array");
|
|
3299
3681
|
return;
|
|
3300
3682
|
}
|
|
3301
3683
|
this.agents.clear();
|
|
@@ -3305,9 +3687,9 @@ var HttpAgentRegistry = class {
|
|
|
3305
3687
|
this.agents.set(a.id, a);
|
|
3306
3688
|
}
|
|
3307
3689
|
}
|
|
3308
|
-
|
|
3690
|
+
logger8.info("Agent registry refreshed", { count: this.agents.size, recoveredAfterRetry });
|
|
3309
3691
|
} catch (e) {
|
|
3310
|
-
|
|
3692
|
+
logger8.warn("Agent registry refresh parse failed", { error: e });
|
|
3311
3693
|
}
|
|
3312
3694
|
}
|
|
3313
3695
|
getById(id) {
|
|
@@ -3321,17 +3703,17 @@ var HttpAgentRegistry = class {
|
|
|
3321
3703
|
try {
|
|
3322
3704
|
const res = await fetch(this.apiUrl(`/api/agents/${encodeURIComponent(id)}`));
|
|
3323
3705
|
if (!res.ok) {
|
|
3324
|
-
|
|
3706
|
+
logger8.warn("fetchById failed", { agentId: id, status: res.status });
|
|
3325
3707
|
return null;
|
|
3326
3708
|
}
|
|
3327
3709
|
const agent = await res.json();
|
|
3328
3710
|
if (agent && typeof agent.id === "string") {
|
|
3329
3711
|
this.agents.set(agent.id, agent);
|
|
3330
|
-
|
|
3712
|
+
logger8.info("Agent registry fetchById upserted", { agentId: id });
|
|
3331
3713
|
}
|
|
3332
3714
|
return agent;
|
|
3333
3715
|
} catch (e) {
|
|
3334
|
-
|
|
3716
|
+
logger8.warn("fetchById unreachable", { agentId: id, error: e });
|
|
3335
3717
|
return null;
|
|
3336
3718
|
}
|
|
3337
3719
|
}
|
|
@@ -3340,16 +3722,16 @@ var HttpAgentRegistry = class {
|
|
|
3340
3722
|
}
|
|
3341
3723
|
upsert(agent) {
|
|
3342
3724
|
this.agents.set(agent.id, agent);
|
|
3343
|
-
|
|
3725
|
+
logger8.debug("Agent registry upsert", { agentId: agent.id });
|
|
3344
3726
|
}
|
|
3345
3727
|
remove(agentId) {
|
|
3346
3728
|
this.agents.delete(agentId);
|
|
3347
|
-
|
|
3729
|
+
logger8.debug("Agent registry remove", { agentId });
|
|
3348
3730
|
}
|
|
3349
3731
|
};
|
|
3350
3732
|
|
|
3351
3733
|
// src/groupRegistry.ts
|
|
3352
|
-
var
|
|
3734
|
+
var logger9 = createModuleLogger("neural.groupRegistry");
|
|
3353
3735
|
var GroupRegistry = class {
|
|
3354
3736
|
groups = /* @__PURE__ */ new Map();
|
|
3355
3737
|
serverApiUrl;
|
|
@@ -3372,17 +3754,17 @@ var GroupRegistry = class {
|
|
|
3372
3754
|
recoveredAfterRetry = res != null;
|
|
3373
3755
|
}
|
|
3374
3756
|
if (!res) {
|
|
3375
|
-
|
|
3757
|
+
logger9.warn("GroupRegistry refresh unreachable after retry");
|
|
3376
3758
|
return;
|
|
3377
3759
|
}
|
|
3378
3760
|
if (!res.ok) {
|
|
3379
|
-
|
|
3761
|
+
logger9.warn("GroupRegistry refresh failed", { status: res.status });
|
|
3380
3762
|
return;
|
|
3381
3763
|
}
|
|
3382
3764
|
try {
|
|
3383
3765
|
const body = await res.json();
|
|
3384
3766
|
if (!Array.isArray(body)) {
|
|
3385
|
-
|
|
3767
|
+
logger9.warn("GroupRegistry refresh: expected array");
|
|
3386
3768
|
return;
|
|
3387
3769
|
}
|
|
3388
3770
|
this.groups.clear();
|
|
@@ -3397,14 +3779,25 @@ var GroupRegistry = class {
|
|
|
3397
3779
|
});
|
|
3398
3780
|
}
|
|
3399
3781
|
}
|
|
3400
|
-
|
|
3782
|
+
logger9.info("GroupRegistry refreshed", { count: this.groups.size, recoveredAfterRetry });
|
|
3401
3783
|
} catch (e) {
|
|
3402
|
-
|
|
3784
|
+
logger9.warn("GroupRegistry refresh parse failed", { error: e });
|
|
3403
3785
|
}
|
|
3404
3786
|
}
|
|
3405
3787
|
getById(groupId) {
|
|
3406
3788
|
return this.groups.get(groupId) ?? null;
|
|
3407
3789
|
}
|
|
3790
|
+
/**
|
|
3791
|
+
* Return the cached groups that the given agent is a member of.
|
|
3792
|
+
* Does NOT refresh — caller decides when to call refresh().
|
|
3793
|
+
*/
|
|
3794
|
+
getMyGroups(agentId) {
|
|
3795
|
+
const out = [];
|
|
3796
|
+
for (const g of this.groups.values()) {
|
|
3797
|
+
if (g.members.includes(agentId)) out.push(g);
|
|
3798
|
+
}
|
|
3799
|
+
return out;
|
|
3800
|
+
}
|
|
3408
3801
|
/**
|
|
3409
3802
|
* Fuzzy match by name (case-insensitive substring).
|
|
3410
3803
|
* Returns the first match.
|
|
@@ -3438,16 +3831,16 @@ var GroupRegistry = class {
|
|
|
3438
3831
|
try {
|
|
3439
3832
|
const res = await fetch(url);
|
|
3440
3833
|
if (res.status === 404) {
|
|
3441
|
-
|
|
3834
|
+
logger9.info("GroupRegistry resolveScope: group not found", { rawScope, suffix });
|
|
3442
3835
|
return null;
|
|
3443
3836
|
}
|
|
3444
3837
|
if (!res.ok) {
|
|
3445
|
-
|
|
3838
|
+
logger9.warn("GroupRegistry resolveScope: HTTP error", { rawScope, status: res.status });
|
|
3446
3839
|
return null;
|
|
3447
3840
|
}
|
|
3448
3841
|
const data = await res.json();
|
|
3449
3842
|
if (!data.groupId || !data.conversationId || !data.workingDirectory) {
|
|
3450
|
-
|
|
3843
|
+
logger9.warn("GroupRegistry resolveScope: incomplete response", {
|
|
3451
3844
|
rawScope,
|
|
3452
3845
|
hasGroupId: !!data.groupId,
|
|
3453
3846
|
hasConversationId: !!data.conversationId,
|
|
@@ -3455,7 +3848,7 @@ var GroupRegistry = class {
|
|
|
3455
3848
|
});
|
|
3456
3849
|
return null;
|
|
3457
3850
|
}
|
|
3458
|
-
|
|
3851
|
+
logger9.info("GroupRegistry resolved scope", {
|
|
3459
3852
|
rawScope,
|
|
3460
3853
|
resolvedGroupId: data.groupId,
|
|
3461
3854
|
resolvedName: data.name ?? "(none)",
|
|
@@ -3470,7 +3863,7 @@ var GroupRegistry = class {
|
|
|
3470
3863
|
workingDirectory: data.workingDirectory
|
|
3471
3864
|
};
|
|
3472
3865
|
} catch (e) {
|
|
3473
|
-
|
|
3866
|
+
logger9.error("GroupRegistry resolveScope error", { rawScope, error: e });
|
|
3474
3867
|
return null;
|
|
3475
3868
|
}
|
|
3476
3869
|
}
|
|
@@ -3491,12 +3884,12 @@ var GroupRegistry = class {
|
|
|
3491
3884
|
if (Array.isArray(body)) {
|
|
3492
3885
|
const single = body.find((c) => c.type === "single" && typeof c.id === "string");
|
|
3493
3886
|
if (single?.id) {
|
|
3494
|
-
|
|
3887
|
+
logger9.info("GroupRegistry resolved single conv", { agentId, conversationId: single.id });
|
|
3495
3888
|
return single.id;
|
|
3496
3889
|
}
|
|
3497
3890
|
}
|
|
3498
3891
|
} else {
|
|
3499
|
-
|
|
3892
|
+
logger9.warn("GroupRegistry resolveSingle: list failed", { agentId, status: res.status });
|
|
3500
3893
|
}
|
|
3501
3894
|
const created = await fetch(`${this.serverApiUrl}/api/conversations`, {
|
|
3502
3895
|
method: "POST",
|
|
@@ -3504,26 +3897,27 @@ var GroupRegistry = class {
|
|
|
3504
3897
|
body: JSON.stringify({ agentId })
|
|
3505
3898
|
});
|
|
3506
3899
|
if (!created.ok) {
|
|
3507
|
-
|
|
3900
|
+
logger9.warn("GroupRegistry resolveSingle: create failed", { agentId, status: created.status });
|
|
3508
3901
|
return null;
|
|
3509
3902
|
}
|
|
3510
3903
|
const conv = await created.json();
|
|
3511
3904
|
if (typeof conv.id !== "string") {
|
|
3512
|
-
|
|
3905
|
+
logger9.warn("GroupRegistry resolveSingle: created conv missing id", { agentId });
|
|
3513
3906
|
return null;
|
|
3514
3907
|
}
|
|
3515
|
-
|
|
3908
|
+
logger9.info("GroupRegistry created single conv", { agentId, conversationId: conv.id });
|
|
3516
3909
|
return conv.id;
|
|
3517
3910
|
} catch (e) {
|
|
3518
|
-
|
|
3911
|
+
logger9.error("GroupRegistry resolveSingle error", { agentId, error: e });
|
|
3519
3912
|
return null;
|
|
3520
3913
|
}
|
|
3521
3914
|
}
|
|
3522
3915
|
};
|
|
3523
3916
|
|
|
3524
3917
|
// src/connector.ts
|
|
3918
|
+
import os5 from "os";
|
|
3525
3919
|
import WebSocket from "ws";
|
|
3526
|
-
var
|
|
3920
|
+
var logger10 = createModuleLogger("ws.connector");
|
|
3527
3921
|
var ServerConnector = class {
|
|
3528
3922
|
ws = null;
|
|
3529
3923
|
reconnectAttempts = 0;
|
|
@@ -3553,19 +3947,19 @@ var ServerConnector = class {
|
|
|
3553
3947
|
url.searchParams.set("token", this.config.bridgeToken);
|
|
3554
3948
|
}
|
|
3555
3949
|
const wsUrl = url.toString();
|
|
3556
|
-
|
|
3950
|
+
logger10.info("Connecting to server", { url: wsUrl });
|
|
3557
3951
|
const ws = new WebSocket(wsUrl);
|
|
3558
3952
|
ws.on("open", () => {
|
|
3559
3953
|
this.ws = ws;
|
|
3560
3954
|
this.reconnectAttempts = 0;
|
|
3561
|
-
|
|
3955
|
+
logger10.info("Connected to server", { url: this.config.serverUrl });
|
|
3562
3956
|
void this.handleOpen();
|
|
3563
3957
|
});
|
|
3564
3958
|
ws.on("message", (data) => {
|
|
3565
3959
|
this.handleMessage(data);
|
|
3566
3960
|
});
|
|
3567
3961
|
ws.on("close", (code, reason) => {
|
|
3568
|
-
|
|
3962
|
+
logger10.warn("Disconnected from server", {
|
|
3569
3963
|
code,
|
|
3570
3964
|
reason: reason.toString()
|
|
3571
3965
|
});
|
|
@@ -3575,15 +3969,15 @@ var ServerConnector = class {
|
|
|
3575
3969
|
}
|
|
3576
3970
|
});
|
|
3577
3971
|
ws.on("error", (err) => {
|
|
3578
|
-
|
|
3972
|
+
logger10.error("WebSocket error", { error: err });
|
|
3579
3973
|
});
|
|
3580
3974
|
}
|
|
3581
3975
|
async handleOpen() {
|
|
3582
3976
|
try {
|
|
3583
3977
|
await this.onConnected();
|
|
3584
|
-
|
|
3978
|
+
logger10.info("Recovery complete, sending bridge:register");
|
|
3585
3979
|
} catch (err) {
|
|
3586
|
-
|
|
3980
|
+
logger10.error("Recovery failed, registering with degraded state", { error: err });
|
|
3587
3981
|
}
|
|
3588
3982
|
this.register();
|
|
3589
3983
|
}
|
|
@@ -3595,13 +3989,14 @@ var ServerConnector = class {
|
|
|
3595
3989
|
payload: {
|
|
3596
3990
|
bridgeId: this.config.bridgeId,
|
|
3597
3991
|
agents: ids,
|
|
3992
|
+
hostname: os5.hostname(),
|
|
3598
3993
|
queryConfig: {
|
|
3599
3994
|
maxActive: qc.maxActive,
|
|
3600
3995
|
idleTimeoutMs: qc.idleTimeoutMs
|
|
3601
3996
|
}
|
|
3602
3997
|
}
|
|
3603
3998
|
});
|
|
3604
|
-
|
|
3999
|
+
logger10.info("Sent bridge:register", {
|
|
3605
4000
|
bridgeId: this.config.bridgeId,
|
|
3606
4001
|
agents: ids
|
|
3607
4002
|
});
|
|
@@ -3612,7 +4007,7 @@ var ServerConnector = class {
|
|
|
3612
4007
|
const raw = typeof data === "string" ? data : data.toString("utf8");
|
|
3613
4008
|
msg = parseWSMessage(raw);
|
|
3614
4009
|
} catch (e) {
|
|
3615
|
-
|
|
4010
|
+
logger10.error("Invalid WS message from server", { error: e });
|
|
3616
4011
|
return;
|
|
3617
4012
|
}
|
|
3618
4013
|
wsMetrics.incRecv(msg.type);
|
|
@@ -3623,7 +4018,7 @@ var ServerConnector = class {
|
|
|
3623
4018
|
}
|
|
3624
4019
|
case "task:dispatch": {
|
|
3625
4020
|
void this.onTaskDispatch(msg.payload).catch((err) => {
|
|
3626
|
-
|
|
4021
|
+
logger10.error("Failed to handle task:dispatch", {
|
|
3627
4022
|
error: err,
|
|
3628
4023
|
traceId: msg.payload.traceId
|
|
3629
4024
|
});
|
|
@@ -3633,19 +4028,19 @@ var ServerConnector = class {
|
|
|
3633
4028
|
case "task:group_dispatch": {
|
|
3634
4029
|
if (this.onGroupTaskDispatch) {
|
|
3635
4030
|
void this.onGroupTaskDispatch(msg.payload).catch((err) => {
|
|
3636
|
-
|
|
4031
|
+
logger10.error("Failed to handle task:group_dispatch", {
|
|
3637
4032
|
error: err,
|
|
3638
4033
|
traceId: msg.payload.traceId
|
|
3639
4034
|
});
|
|
3640
4035
|
});
|
|
3641
4036
|
} else {
|
|
3642
|
-
|
|
4037
|
+
logger10.warn("Received task:group_dispatch but no handler registered");
|
|
3643
4038
|
}
|
|
3644
4039
|
return;
|
|
3645
4040
|
}
|
|
3646
4041
|
case "user:stop_generation": {
|
|
3647
4042
|
void this.onStopGeneration(msg.payload).catch((err) => {
|
|
3648
|
-
|
|
4043
|
+
logger10.error("Failed to handle user:stop_generation", {
|
|
3649
4044
|
error: err,
|
|
3650
4045
|
traceId: msg.payload.traceId
|
|
3651
4046
|
});
|
|
@@ -3659,16 +4054,17 @@ var ServerConnector = class {
|
|
|
3659
4054
|
case "agent:updated":
|
|
3660
4055
|
case "agent:deleted":
|
|
3661
4056
|
case "group:member_changed":
|
|
4057
|
+
case "group:updated":
|
|
3662
4058
|
case "user:answer_question": {
|
|
3663
4059
|
if (this.onServerPush) {
|
|
3664
4060
|
void Promise.resolve(this.onServerPush(msg)).catch((err) => {
|
|
3665
|
-
|
|
4061
|
+
logger10.error("onServerPush handler failed", { error: err, type: msg.type });
|
|
3666
4062
|
});
|
|
3667
4063
|
}
|
|
3668
4064
|
return;
|
|
3669
4065
|
}
|
|
3670
4066
|
default: {
|
|
3671
|
-
|
|
4067
|
+
logger10.warn("Unhandled server message type", {
|
|
3672
4068
|
type: msg.type
|
|
3673
4069
|
});
|
|
3674
4070
|
}
|
|
@@ -3676,7 +4072,7 @@ var ServerConnector = class {
|
|
|
3676
4072
|
}
|
|
3677
4073
|
send(msg) {
|
|
3678
4074
|
if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
|
|
3679
|
-
|
|
4075
|
+
logger10.warn("Cannot send: WebSocket not open", {
|
|
3680
4076
|
type: msg.type
|
|
3681
4077
|
});
|
|
3682
4078
|
return;
|
|
@@ -3685,14 +4081,14 @@ var ServerConnector = class {
|
|
|
3685
4081
|
this.ws.send(JSON.stringify(msg));
|
|
3686
4082
|
wsMetrics.incSend(msg.type);
|
|
3687
4083
|
} catch (e) {
|
|
3688
|
-
|
|
4084
|
+
logger10.error("Failed to send WS message", { error: e, type: msg.type });
|
|
3689
4085
|
}
|
|
3690
4086
|
}
|
|
3691
4087
|
scheduleReconnect() {
|
|
3692
4088
|
if (this.closing) return;
|
|
3693
4089
|
const delay = this.delays[Math.min(this.reconnectAttempts, this.delays.length - 1)];
|
|
3694
4090
|
this.reconnectAttempts++;
|
|
3695
|
-
|
|
4091
|
+
logger10.info("Scheduling reconnect", {
|
|
3696
4092
|
attempt: this.reconnectAttempts,
|
|
3697
4093
|
delayMs: delay
|
|
3698
4094
|
});
|
|
@@ -3708,11 +4104,11 @@ var ServerConnector = class {
|
|
|
3708
4104
|
try {
|
|
3709
4105
|
this.ws.close(1e3, "Bridge shutting down");
|
|
3710
4106
|
} catch (e) {
|
|
3711
|
-
|
|
4107
|
+
logger10.error("Error closing WebSocket", { error: e });
|
|
3712
4108
|
}
|
|
3713
4109
|
this.ws = null;
|
|
3714
4110
|
}
|
|
3715
|
-
|
|
4111
|
+
logger10.info("Connector closed");
|
|
3716
4112
|
}
|
|
3717
4113
|
get isConnected() {
|
|
3718
4114
|
return this.ws !== null && this.ws.readyState === WebSocket.OPEN;
|
|
@@ -3720,14 +4116,14 @@ var ServerConnector = class {
|
|
|
3720
4116
|
};
|
|
3721
4117
|
|
|
3722
4118
|
// src/modelQuerier.ts
|
|
3723
|
-
import
|
|
3724
|
-
import
|
|
3725
|
-
import
|
|
3726
|
-
var
|
|
4119
|
+
import fs4 from "fs/promises";
|
|
4120
|
+
import os6 from "os";
|
|
4121
|
+
import path8 from "path";
|
|
4122
|
+
var logger11 = createModuleLogger("bridge.modelQuerier");
|
|
3727
4123
|
async function listModels(queryFn, opts = {}) {
|
|
3728
4124
|
const t0 = Date.now();
|
|
3729
|
-
const cwd = opts.cwd ??
|
|
3730
|
-
await
|
|
4125
|
+
const cwd = opts.cwd ?? path8.join(os6.homedir(), ".ahchat", "workspaces", "_list_models");
|
|
4126
|
+
await fs4.mkdir(cwd, { recursive: true });
|
|
3731
4127
|
const fn = queryFn ?? (await import("@anthropic-ai/claude-agent-sdk")).query;
|
|
3732
4128
|
const ic = new InputController();
|
|
3733
4129
|
ic.push("Reply with exactly: PING", "");
|
|
@@ -3769,7 +4165,7 @@ async function listModels(queryFn, opts = {}) {
|
|
|
3769
4165
|
displayName: m.displayName,
|
|
3770
4166
|
description: m.description
|
|
3771
4167
|
}));
|
|
3772
|
-
|
|
4168
|
+
logger11.info("listModels done", { count: models.length, ms: Date.now() - t0 });
|
|
3773
4169
|
return models;
|
|
3774
4170
|
} finally {
|
|
3775
4171
|
try {
|
|
@@ -3784,9 +4180,9 @@ async function listModels(queryFn, opts = {}) {
|
|
|
3784
4180
|
}
|
|
3785
4181
|
|
|
3786
4182
|
// src/lockfile.ts
|
|
3787
|
-
import
|
|
3788
|
-
import
|
|
3789
|
-
var
|
|
4183
|
+
import fs5 from "fs";
|
|
4184
|
+
import path9 from "path";
|
|
4185
|
+
var logger12 = createModuleLogger("bridge.lockfile");
|
|
3790
4186
|
var lockPath = null;
|
|
3791
4187
|
function isProcessAlive(pid) {
|
|
3792
4188
|
try {
|
|
@@ -3799,32 +4195,32 @@ function isProcessAlive(pid) {
|
|
|
3799
4195
|
}
|
|
3800
4196
|
}
|
|
3801
4197
|
function acquireLock(dataDir) {
|
|
3802
|
-
const file =
|
|
4198
|
+
const file = path9.join(dataDir, "bridge.lock");
|
|
3803
4199
|
lockPath = file;
|
|
3804
|
-
if (
|
|
3805
|
-
const raw =
|
|
4200
|
+
if (fs5.existsSync(file)) {
|
|
4201
|
+
const raw = fs5.readFileSync(file, "utf-8").trim();
|
|
3806
4202
|
const pid = Number.parseInt(raw, 10);
|
|
3807
4203
|
if (Number.isFinite(pid) && pid > 0) {
|
|
3808
4204
|
if (isProcessAlive(pid)) {
|
|
3809
4205
|
throw new Error(`Bridge already running (PID: ${pid})`);
|
|
3810
4206
|
}
|
|
3811
|
-
|
|
4207
|
+
logger12.warn("Removing stale bridge.lock (process not found)", { pid, path: file });
|
|
3812
4208
|
}
|
|
3813
4209
|
}
|
|
3814
|
-
|
|
3815
|
-
|
|
3816
|
-
|
|
4210
|
+
fs5.mkdirSync(path9.dirname(file), { recursive: true });
|
|
4211
|
+
fs5.writeFileSync(file, String(process.pid), "utf-8");
|
|
4212
|
+
logger12.info("Acquired bridge lock", { path: file, pid: process.pid });
|
|
3817
4213
|
const release = () => {
|
|
3818
4214
|
try {
|
|
3819
|
-
if (lockPath &&
|
|
3820
|
-
const current =
|
|
4215
|
+
if (lockPath && fs5.existsSync(lockPath)) {
|
|
4216
|
+
const current = fs5.readFileSync(lockPath, "utf-8").trim();
|
|
3821
4217
|
if (current === String(process.pid)) {
|
|
3822
|
-
|
|
3823
|
-
|
|
4218
|
+
fs5.unlinkSync(lockPath);
|
|
4219
|
+
logger12.info("Released bridge lock", { path: lockPath });
|
|
3824
4220
|
}
|
|
3825
4221
|
}
|
|
3826
4222
|
} catch (e) {
|
|
3827
|
-
|
|
4223
|
+
logger12.error("Failed to release bridge lock", { error: e, path: lockPath });
|
|
3828
4224
|
} finally {
|
|
3829
4225
|
lockPath = null;
|
|
3830
4226
|
}
|
|
@@ -3927,9 +4323,9 @@ function buildGroupPrompt(payload) {
|
|
|
3927
4323
|
}
|
|
3928
4324
|
|
|
3929
4325
|
// src/messageHandler.ts
|
|
3930
|
-
var
|
|
4326
|
+
var logger13 = createModuleLogger("msg.handler");
|
|
3931
4327
|
function emitTaskAck(emit, ackId, agentId, traceId) {
|
|
3932
|
-
|
|
4328
|
+
logger13.info("Emitting task:ack", { ackId, agentId, traceId });
|
|
3933
4329
|
emit({
|
|
3934
4330
|
type: "task:ack",
|
|
3935
4331
|
payload: {
|
|
@@ -3942,7 +4338,7 @@ function emitTaskAck(emit, ackId, agentId, traceId) {
|
|
|
3942
4338
|
}
|
|
3943
4339
|
function createTaskDispatchHandler(agentManager, agentRegistry, emit) {
|
|
3944
4340
|
return async (payload) => {
|
|
3945
|
-
|
|
4341
|
+
logger13.info("Handling task:dispatch", {
|
|
3946
4342
|
agentId: payload.agentId,
|
|
3947
4343
|
messageId: payload.messageId,
|
|
3948
4344
|
ackId: payload.ackId,
|
|
@@ -3951,14 +4347,14 @@ function createTaskDispatchHandler(agentManager, agentRegistry, emit) {
|
|
|
3951
4347
|
emitTaskAck(emit, payload.ackId, payload.agentId, payload.traceId);
|
|
3952
4348
|
let agentConfig = agentRegistry.getById(payload.agentId);
|
|
3953
4349
|
if (!agentConfig) {
|
|
3954
|
-
|
|
4350
|
+
logger13.warn("Agent not in registry, attempting live fetch", {
|
|
3955
4351
|
agentId: payload.agentId,
|
|
3956
4352
|
traceId: payload.traceId
|
|
3957
4353
|
});
|
|
3958
4354
|
agentConfig = await agentRegistry.fetchById(payload.agentId);
|
|
3959
4355
|
}
|
|
3960
4356
|
if (!agentConfig) {
|
|
3961
|
-
|
|
4357
|
+
logger13.error("Agent not found for task:dispatch (after live fetch)", {
|
|
3962
4358
|
agentId: payload.agentId,
|
|
3963
4359
|
traceId: payload.traceId
|
|
3964
4360
|
});
|
|
@@ -3985,7 +4381,7 @@ function createTaskDispatchHandler(agentManager, agentRegistry, emit) {
|
|
|
3985
4381
|
traceId: payload.traceId
|
|
3986
4382
|
});
|
|
3987
4383
|
} catch (err) {
|
|
3988
|
-
|
|
4384
|
+
logger13.error("Failed to dispatch message to Agent", {
|
|
3989
4385
|
error: err,
|
|
3990
4386
|
agentId: payload.agentId,
|
|
3991
4387
|
traceId: payload.traceId
|
|
@@ -4005,7 +4401,7 @@ function createTaskDispatchHandler(agentManager, agentRegistry, emit) {
|
|
|
4005
4401
|
}
|
|
4006
4402
|
function createGroupTaskDispatchHandler(agentManager, agentRegistry, emit) {
|
|
4007
4403
|
return async (payload) => {
|
|
4008
|
-
|
|
4404
|
+
logger13.info("Handling task:group_dispatch", {
|
|
4009
4405
|
agentId: payload.agentId,
|
|
4010
4406
|
groupId: payload.groupId,
|
|
4011
4407
|
ackId: payload.ackId,
|
|
@@ -4017,14 +4413,14 @@ function createGroupTaskDispatchHandler(agentManager, agentRegistry, emit) {
|
|
|
4017
4413
|
emitTaskAck(emit, payload.ackId, payload.agentId, payload.traceId);
|
|
4018
4414
|
let agentConfig = agentRegistry.getById(payload.agentId);
|
|
4019
4415
|
if (!agentConfig) {
|
|
4020
|
-
|
|
4416
|
+
logger13.warn("Agent not in registry for group dispatch, attempting live fetch", {
|
|
4021
4417
|
agentId: payload.agentId,
|
|
4022
4418
|
traceId: payload.traceId
|
|
4023
4419
|
});
|
|
4024
4420
|
agentConfig = await agentRegistry.fetchById(payload.agentId);
|
|
4025
4421
|
}
|
|
4026
4422
|
if (!agentConfig) {
|
|
4027
|
-
|
|
4423
|
+
logger13.error("Agent not found for task:group_dispatch (after live fetch)", {
|
|
4028
4424
|
agentId: payload.agentId,
|
|
4029
4425
|
traceId: payload.traceId
|
|
4030
4426
|
});
|
|
@@ -4057,7 +4453,7 @@ function createGroupTaskDispatchHandler(agentManager, agentRegistry, emit) {
|
|
|
4057
4453
|
groupId: payload.groupId
|
|
4058
4454
|
});
|
|
4059
4455
|
} catch (err) {
|
|
4060
|
-
|
|
4456
|
+
logger13.error("Failed to dispatch group message to Agent", {
|
|
4061
4457
|
error: err,
|
|
4062
4458
|
agentId: payload.agentId,
|
|
4063
4459
|
groupId: payload.groupId,
|
|
@@ -4077,15 +4473,84 @@ function createGroupTaskDispatchHandler(agentManager, agentRegistry, emit) {
|
|
|
4077
4473
|
};
|
|
4078
4474
|
}
|
|
4079
4475
|
|
|
4476
|
+
// src/scopePushNotify.ts
|
|
4477
|
+
var logger14 = createModuleLogger("bridge");
|
|
4478
|
+
function buildMemberChangedScopeNotice(params) {
|
|
4479
|
+
const { groupId, groupLabel, action } = params;
|
|
4480
|
+
const verb = action === "added" ? "\u52A0\u5165" : "\u79FB\u51FA";
|
|
4481
|
+
return [
|
|
4482
|
+
`[\u7CFB\u7EDF\u901A\u77E5] \u4F60\u5DF2\u88AB${verb}\u7FA4\u300C${groupLabel}\u300D(group:${groupId})\u3002`,
|
|
4483
|
+
`\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`,
|
|
4484
|
+
`\u65E0\u9700\u56DE\u590D\u6B64\u901A\u77E5\u3002`
|
|
4485
|
+
].join("\n");
|
|
4486
|
+
}
|
|
4487
|
+
function buildGroupRenamedScopeNotice(params) {
|
|
4488
|
+
const { groupId, newName } = params;
|
|
4489
|
+
return [
|
|
4490
|
+
`[\u7CFB\u7EDF\u901A\u77E5] \u7FA4 (group:${groupId}) \u5DF2\u66F4\u540D\u4E3A\u300C${newName}\u300D\u3002`,
|
|
4491
|
+
`\u82E5\u4F60\u4E4B\u524D\u7528\u65E7\u7FA4\u540D\u8C03\u8FC7 neural_send\uFF0C\u8BF7\u66F4\u65B0\u4E3A\u65B0\u540D\u79F0\u3002`,
|
|
4492
|
+
`\u65E0\u9700\u56DE\u590D\u6B64\u901A\u77E5\u3002`
|
|
4493
|
+
].join("\n");
|
|
4494
|
+
}
|
|
4495
|
+
async function handleGroupMemberChangedPush(deps, payload) {
|
|
4496
|
+
const { groupId, action, agentId } = payload;
|
|
4497
|
+
logger14.info("group:member_changed received, refreshing GroupRegistry", {
|
|
4498
|
+
groupId,
|
|
4499
|
+
action,
|
|
4500
|
+
agentId
|
|
4501
|
+
});
|
|
4502
|
+
await deps.groupRegistry.refresh();
|
|
4503
|
+
logger14.info("GroupRegistry refreshed after member_changed", {
|
|
4504
|
+
groupId,
|
|
4505
|
+
action,
|
|
4506
|
+
registryGroupCount: deps.groupRegistry.getAll().length
|
|
4507
|
+
});
|
|
4508
|
+
const group = deps.groupRegistry.getById(groupId);
|
|
4509
|
+
const groupLabel = group?.name ?? groupId;
|
|
4510
|
+
const notice = buildMemberChangedScopeNotice({ groupId, groupLabel, action });
|
|
4511
|
+
deps.agentManager.broadcastScopeNotice(agentId, notice);
|
|
4512
|
+
logger14.info("Scope notice sent for member_changed", {
|
|
4513
|
+
agentId,
|
|
4514
|
+
groupId,
|
|
4515
|
+
groupLabel,
|
|
4516
|
+
action,
|
|
4517
|
+
noticeLen: notice.length
|
|
4518
|
+
});
|
|
4519
|
+
}
|
|
4520
|
+
async function handleGroupUpdatedPush(deps, payload) {
|
|
4521
|
+
const { groupId, name: newName, memberAgentIds } = payload;
|
|
4522
|
+
logger14.info("group:updated received, refreshing GroupRegistry", {
|
|
4523
|
+
groupId,
|
|
4524
|
+
newName,
|
|
4525
|
+
memberCount: memberAgentIds.length
|
|
4526
|
+
});
|
|
4527
|
+
await deps.groupRegistry.refresh();
|
|
4528
|
+
logger14.info("GroupRegistry refreshed after group:updated", {
|
|
4529
|
+
groupId,
|
|
4530
|
+
newName,
|
|
4531
|
+
registryGroupCount: deps.groupRegistry.getAll().length
|
|
4532
|
+
});
|
|
4533
|
+
const notice = buildGroupRenamedScopeNotice({ groupId, newName });
|
|
4534
|
+
for (const aid of memberAgentIds) {
|
|
4535
|
+
deps.agentManager.broadcastScopeNotice(aid, notice);
|
|
4536
|
+
}
|
|
4537
|
+
logger14.info("Scope notices sent for group:updated", {
|
|
4538
|
+
groupId,
|
|
4539
|
+
newName,
|
|
4540
|
+
memberCount: memberAgentIds.length,
|
|
4541
|
+
noticeLen: notice.length
|
|
4542
|
+
});
|
|
4543
|
+
}
|
|
4544
|
+
|
|
4080
4545
|
// src/sessionStore.ts
|
|
4081
|
-
import
|
|
4082
|
-
import
|
|
4083
|
-
var
|
|
4546
|
+
import fs6 from "fs";
|
|
4547
|
+
import path10 from "path";
|
|
4548
|
+
var logger15 = createModuleLogger("session.store");
|
|
4084
4549
|
var SessionStore = class {
|
|
4085
4550
|
filePath;
|
|
4086
4551
|
cache;
|
|
4087
4552
|
constructor(dataDir) {
|
|
4088
|
-
this.filePath =
|
|
4553
|
+
this.filePath = path10.join(dataDir, "sessions.json");
|
|
4089
4554
|
this.cache = this.loadFromDisk();
|
|
4090
4555
|
}
|
|
4091
4556
|
cacheKey(agentId, scope) {
|
|
@@ -4120,8 +4585,8 @@ var SessionStore = class {
|
|
|
4120
4585
|
}
|
|
4121
4586
|
loadFromDisk() {
|
|
4122
4587
|
try {
|
|
4123
|
-
if (!
|
|
4124
|
-
const raw =
|
|
4588
|
+
if (!fs6.existsSync(this.filePath)) return {};
|
|
4589
|
+
const raw = fs6.readFileSync(this.filePath, "utf-8");
|
|
4125
4590
|
const parsed = JSON.parse(raw);
|
|
4126
4591
|
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return {};
|
|
4127
4592
|
const map = parsed;
|
|
@@ -4131,7 +4596,7 @@ var SessionStore = class {
|
|
|
4131
4596
|
migrated[key] = sessionId;
|
|
4132
4597
|
} else {
|
|
4133
4598
|
migrated[`${key}::single`] = sessionId;
|
|
4134
|
-
|
|
4599
|
+
logger15.info("Migrated legacy session key to scoped key", {
|
|
4135
4600
|
legacyKey: key,
|
|
4136
4601
|
newKey: `${key}::single`
|
|
4137
4602
|
});
|
|
@@ -4139,29 +4604,29 @@ var SessionStore = class {
|
|
|
4139
4604
|
}
|
|
4140
4605
|
return migrated;
|
|
4141
4606
|
} catch (e) {
|
|
4142
|
-
|
|
4607
|
+
logger15.warn("Failed to load sessions file, starting fresh", { error: e, path: this.filePath });
|
|
4143
4608
|
return {};
|
|
4144
4609
|
}
|
|
4145
4610
|
}
|
|
4146
4611
|
saveToDisk() {
|
|
4147
4612
|
try {
|
|
4148
|
-
const dir =
|
|
4149
|
-
|
|
4150
|
-
|
|
4613
|
+
const dir = path10.dirname(this.filePath);
|
|
4614
|
+
fs6.mkdirSync(dir, { recursive: true });
|
|
4615
|
+
fs6.writeFileSync(this.filePath, JSON.stringify(this.cache, null, 2), "utf-8");
|
|
4151
4616
|
} catch (e) {
|
|
4152
|
-
|
|
4617
|
+
logger15.error("Failed to save sessions file", { error: e, path: this.filePath });
|
|
4153
4618
|
}
|
|
4154
4619
|
}
|
|
4155
4620
|
};
|
|
4156
4621
|
|
|
4157
4622
|
// src/start.ts
|
|
4158
|
-
var
|
|
4623
|
+
var logger16 = createModuleLogger("bridge");
|
|
4159
4624
|
async function startBridge(config) {
|
|
4160
4625
|
ensureDir(config.dataDir);
|
|
4161
4626
|
ensureDir(config.claudeConfigDir);
|
|
4162
4627
|
process.env.CLAUDE_CONFIG_DIR = config.claudeConfigDir;
|
|
4163
4628
|
acquireLock(config.dataDir);
|
|
4164
|
-
|
|
4629
|
+
logger16.info("Bridge starting", {
|
|
4165
4630
|
bridgeId: config.bridgeId,
|
|
4166
4631
|
serverUrl: config.serverUrl,
|
|
4167
4632
|
serverApiUrl: config.serverApiUrl,
|
|
@@ -4169,6 +4634,9 @@ async function startBridge(config) {
|
|
|
4169
4634
|
});
|
|
4170
4635
|
wsMetrics.start(5e3);
|
|
4171
4636
|
const sessionStore = new SessionStore(config.dataDir);
|
|
4637
|
+
const memoryRoot = path11.join(config.dataDir, "agent-memory");
|
|
4638
|
+
const memoryStore = new AgentMemoryStore(memoryRoot);
|
|
4639
|
+
logger16.info("Agent memory store initialized", { rootDir: memoryRoot });
|
|
4172
4640
|
const agentRegistry = new HttpAgentRegistry(config.serverApiUrl);
|
|
4173
4641
|
const groupRegistry = new GroupRegistry(config.serverApiUrl);
|
|
4174
4642
|
await agentRegistry.refresh();
|
|
@@ -4182,7 +4650,8 @@ async function startBridge(config) {
|
|
|
4182
4650
|
queryConfig: config.queryConfig,
|
|
4183
4651
|
claudeConfigDir: config.claudeConfigDir,
|
|
4184
4652
|
askQuestionRegistry,
|
|
4185
|
-
groupRegistry
|
|
4653
|
+
groupRegistry,
|
|
4654
|
+
memoryStore
|
|
4186
4655
|
});
|
|
4187
4656
|
const taskDispatchHandler = createTaskDispatchHandler(agentManager, agentRegistry, emit);
|
|
4188
4657
|
const groupTaskDispatchHandler = createGroupTaskDispatchHandler(agentManager, agentRegistry, emit);
|
|
@@ -4204,21 +4673,21 @@ async function startBridge(config) {
|
|
|
4204
4673
|
switch (msg.type) {
|
|
4205
4674
|
case "bridge:list_models_request": {
|
|
4206
4675
|
const { requestId } = msg.payload;
|
|
4207
|
-
|
|
4676
|
+
logger16.info("list_models request received", { requestId });
|
|
4208
4677
|
try {
|
|
4209
4678
|
const models = await listModels();
|
|
4210
4679
|
connector?.send({
|
|
4211
4680
|
type: "bridge:list_models_response",
|
|
4212
4681
|
payload: { requestId, models }
|
|
4213
4682
|
});
|
|
4214
|
-
|
|
4683
|
+
logger16.info("list_models response sent", { requestId, count: models.length });
|
|
4215
4684
|
} catch (e) {
|
|
4216
4685
|
const err = e instanceof Error ? e.message : String(e);
|
|
4217
4686
|
connector?.send({
|
|
4218
4687
|
type: "bridge:list_models_response",
|
|
4219
4688
|
payload: { requestId, error: err }
|
|
4220
4689
|
});
|
|
4221
|
-
|
|
4690
|
+
logger16.error("list_models failed", { requestId, error: e });
|
|
4222
4691
|
}
|
|
4223
4692
|
break;
|
|
4224
4693
|
}
|
|
@@ -4226,7 +4695,7 @@ async function startBridge(config) {
|
|
|
4226
4695
|
await agentManager.terminate(msg.payload.agentId);
|
|
4227
4696
|
break;
|
|
4228
4697
|
case "agent:terminate_scope":
|
|
4229
|
-
|
|
4698
|
+
logger16.info("agent:terminate_scope received", {
|
|
4230
4699
|
agentId: msg.payload.agentId,
|
|
4231
4700
|
scope: msg.payload.scope
|
|
4232
4701
|
});
|
|
@@ -4239,11 +4708,31 @@ async function startBridge(config) {
|
|
|
4239
4708
|
case "agent:deleted":
|
|
4240
4709
|
agentRegistry.remove(msg.payload.agentId);
|
|
4241
4710
|
break;
|
|
4711
|
+
case "group:member_changed":
|
|
4712
|
+
await handleGroupMemberChangedPush(
|
|
4713
|
+
{ groupRegistry, agentManager },
|
|
4714
|
+
{
|
|
4715
|
+
groupId: msg.payload.groupId,
|
|
4716
|
+
action: msg.payload.action,
|
|
4717
|
+
agentId: msg.payload.agentId
|
|
4718
|
+
}
|
|
4719
|
+
);
|
|
4720
|
+
break;
|
|
4721
|
+
case "group:updated":
|
|
4722
|
+
await handleGroupUpdatedPush(
|
|
4723
|
+
{ groupRegistry, agentManager },
|
|
4724
|
+
{
|
|
4725
|
+
groupId: msg.payload.groupId,
|
|
4726
|
+
name: msg.payload.name,
|
|
4727
|
+
memberAgentIds: msg.payload.memberAgentIds
|
|
4728
|
+
}
|
|
4729
|
+
);
|
|
4730
|
+
break;
|
|
4242
4731
|
case "user:answer_question": {
|
|
4243
4732
|
const p = msg.payload;
|
|
4244
4733
|
const answerText = formatAnswerForSDK(p);
|
|
4245
4734
|
const ok = askQuestionRegistry.resolve(p.questionId, answerText);
|
|
4246
|
-
|
|
4735
|
+
logger16.info("user:answer_question handled", {
|
|
4247
4736
|
questionId: p.questionId,
|
|
4248
4737
|
agentId: p.agentId,
|
|
4249
4738
|
resolved: ok,
|
|
@@ -4264,7 +4753,7 @@ async function startBridge(config) {
|
|
|
4264
4753
|
});
|
|
4265
4754
|
}, config.queryConfig.statusReportIntervalMs);
|
|
4266
4755
|
const shutdown = async (signal) => {
|
|
4267
|
-
|
|
4756
|
+
logger16.info("Shutdown signal received", { signal });
|
|
4268
4757
|
if (statusInterval) {
|
|
4269
4758
|
clearInterval(statusInterval);
|
|
4270
4759
|
statusInterval = null;
|
|
@@ -4272,7 +4761,7 @@ async function startBridge(config) {
|
|
|
4272
4761
|
wsMetrics.stop();
|
|
4273
4762
|
connector?.close();
|
|
4274
4763
|
await agentManager.shutdownAll();
|
|
4275
|
-
|
|
4764
|
+
logger16.info("Bridge stopped");
|
|
4276
4765
|
process.exit(0);
|
|
4277
4766
|
};
|
|
4278
4767
|
process.on("SIGINT", () => void shutdown("SIGINT"));
|
|
@@ -4280,8 +4769,8 @@ async function startBridge(config) {
|
|
|
4280
4769
|
}
|
|
4281
4770
|
|
|
4282
4771
|
// src/index.ts
|
|
4283
|
-
var
|
|
4772
|
+
var logger17 = createModuleLogger("bridge");
|
|
4284
4773
|
void startBridge(loadBridgeConfig()).catch((e) => {
|
|
4285
|
-
|
|
4774
|
+
logger17.error("Bridge failed to start", { error: e });
|
|
4286
4775
|
process.exit(1);
|
|
4287
4776
|
});
|