@fangyb/ahchat-bridge 0.1.8 → 0.1.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +777 -276
- package/dist/index.js +721 -220
- package/package.json +32 -30
- package/dist/chunk-7SODRWIG.js +0 -4525
package/dist/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,57 @@ 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
|
-
|
|
3402
|
+
let cwd = agent.workingDirectory || path7.join(this.workspacesDir, agent.id);
|
|
3403
|
+
if (agent.workingDirectory) {
|
|
3404
|
+
try {
|
|
3405
|
+
await fs3.mkdir(cwd, { recursive: true });
|
|
3406
|
+
} catch {
|
|
3407
|
+
cwd = path7.join(this.workspacesDir, agent.id);
|
|
3408
|
+
logger7.warn("Stored workingDirectory inaccessible, falling back", {
|
|
3409
|
+
agentId: agent.id,
|
|
3410
|
+
stored: agent.workingDirectory,
|
|
3411
|
+
fallback: cwd
|
|
3412
|
+
});
|
|
3413
|
+
}
|
|
3414
|
+
}
|
|
3063
3415
|
await this.acquire(agent, { kind: "single" }, cwd);
|
|
3064
3416
|
warmed++;
|
|
3065
|
-
|
|
3417
|
+
logger7.info("Agent process pre-created for recovery", { agentId: agent.id });
|
|
3066
3418
|
} catch (err) {
|
|
3067
3419
|
if (err instanceof BridgeBusyError) {
|
|
3068
|
-
|
|
3420
|
+
logger7.warn("Recovery stopped: bridge busy", { agentId: agent.id });
|
|
3069
3421
|
break;
|
|
3070
3422
|
}
|
|
3071
|
-
|
|
3423
|
+
logger7.warn("Failed to pre-create Agent for recovery, clearing session", {
|
|
3072
3424
|
agentId: agent.id,
|
|
3073
3425
|
error: err
|
|
3074
3426
|
});
|
|
@@ -3092,7 +3444,7 @@ var AgentManager = class {
|
|
|
3092
3444
|
} catch (err) {
|
|
3093
3445
|
const errMsg = err.message ?? String(err);
|
|
3094
3446
|
const isResumeFail = /session|conversation.*not found/i.test(errMsg);
|
|
3095
|
-
|
|
3447
|
+
logger7.error("Agent query stream ended with error", {
|
|
3096
3448
|
agentId: runtime.agentId,
|
|
3097
3449
|
scope: scopeKey(runtime.scope),
|
|
3098
3450
|
isResumeFail,
|
|
@@ -3100,7 +3452,7 @@ var AgentManager = class {
|
|
|
3100
3452
|
error: err
|
|
3101
3453
|
});
|
|
3102
3454
|
this.sessionStore.delete(runtime.agentId, runtime.scope);
|
|
3103
|
-
|
|
3455
|
+
logger7.info("Cleared stale session after query crash", {
|
|
3104
3456
|
agentId: runtime.agentId,
|
|
3105
3457
|
scope: scopeKey(runtime.scope)
|
|
3106
3458
|
});
|
|
@@ -3161,8 +3513,50 @@ var AgentManager = class {
|
|
|
3161
3513
|
}
|
|
3162
3514
|
return [...ids];
|
|
3163
3515
|
}
|
|
3516
|
+
/**
|
|
3517
|
+
* Push a system notice to ALL active runtimes of the given agent.
|
|
3518
|
+
*
|
|
3519
|
+
* Working runtimes: injected mid-turn via InputController (merged into
|
|
3520
|
+
* the current turn, no extra result expected).
|
|
3521
|
+
* Ready/starting runtimes: dispatched as a lightweight task so any SDK
|
|
3522
|
+
* output has valid replyMessageId/conversationId to land on.
|
|
3523
|
+
*/
|
|
3524
|
+
broadcastScopeNotice(agentId, notice) {
|
|
3525
|
+
let notified = 0;
|
|
3526
|
+
for (const [, proc] of this.agents) {
|
|
3527
|
+
if (proc.agentId !== agentId || proc.status === "dead") continue;
|
|
3528
|
+
const runtime = this.asRuntime(proc);
|
|
3529
|
+
if (proc.status === "working") {
|
|
3530
|
+
runtime.inputController.push(notice, runtime.ccSessionId ?? "");
|
|
3531
|
+
logger7.info("Scope notice injected mid-turn", {
|
|
3532
|
+
agentId,
|
|
3533
|
+
scope: scopeKey(proc.scope),
|
|
3534
|
+
noticeLen: notice.length
|
|
3535
|
+
});
|
|
3536
|
+
} else if (proc.status === "ready" || proc.status === "starting") {
|
|
3537
|
+
const task = {
|
|
3538
|
+
content: notice,
|
|
3539
|
+
replyMessageId: `msg_scopenotice_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`,
|
|
3540
|
+
conversationId: proc.currentTask?.conversationId ?? "",
|
|
3541
|
+
traceId: `tr_scopenotice_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`,
|
|
3542
|
+
groupId: proc.scope.kind === "group" ? proc.scope.groupId : void 0
|
|
3543
|
+
};
|
|
3544
|
+
this.dispatchToSDK(runtime, task);
|
|
3545
|
+
logger7.info("Scope notice dispatched to idle runtime", {
|
|
3546
|
+
agentId,
|
|
3547
|
+
scope: scopeKey(proc.scope),
|
|
3548
|
+
replyMessageId: task.replyMessageId
|
|
3549
|
+
});
|
|
3550
|
+
}
|
|
3551
|
+
notified++;
|
|
3552
|
+
}
|
|
3553
|
+
logger7.info("broadcastScopeNotice completed", {
|
|
3554
|
+
agentId,
|
|
3555
|
+
notifiedCount: notified
|
|
3556
|
+
});
|
|
3557
|
+
}
|
|
3164
3558
|
async shutdownAll() {
|
|
3165
|
-
|
|
3559
|
+
logger7.info("Shutting down all Agent processes", { count: this.agents.size });
|
|
3166
3560
|
this.askQuestionRegistry.cancelAll("agent_aborted");
|
|
3167
3561
|
if (this.evictionTimer) {
|
|
3168
3562
|
clearInterval(this.evictionTimer);
|
|
@@ -3174,9 +3568,9 @@ var AgentManager = class {
|
|
|
3174
3568
|
runtime.inputController?.close();
|
|
3175
3569
|
runtime.query?.return(void 0);
|
|
3176
3570
|
proc.status = "dead";
|
|
3177
|
-
|
|
3571
|
+
logger7.info("Agent process shut down", { agentId: proc.agentId, scope: scopeKey(proc.scope), key });
|
|
3178
3572
|
} catch (err) {
|
|
3179
|
-
|
|
3573
|
+
logger7.error("Error shutting down Agent", { agentId: proc.agentId, error: err });
|
|
3180
3574
|
}
|
|
3181
3575
|
}
|
|
3182
3576
|
this.agents.clear();
|
|
@@ -3193,13 +3587,13 @@ var AgentManager = class {
|
|
|
3193
3587
|
}
|
|
3194
3588
|
}
|
|
3195
3589
|
if (!proc) {
|
|
3196
|
-
|
|
3590
|
+
logger7.warn("cancelReply: no active process for reply", { agentId, replyMessageId });
|
|
3197
3591
|
return;
|
|
3198
3592
|
}
|
|
3199
3593
|
const runtime = this.asRuntime(proc);
|
|
3200
3594
|
const key = runtimeKey(agentId, proc.scope);
|
|
3201
3595
|
if (!runtime.currentTask || runtime.currentTask.replyMessageId !== replyMessageId) {
|
|
3202
|
-
|
|
3596
|
+
logger7.warn("cancelReply: replyMessageId mismatch", {
|
|
3203
3597
|
agentId,
|
|
3204
3598
|
replyMessageId,
|
|
3205
3599
|
expected: runtime.currentTask?.replyMessageId
|
|
@@ -3228,7 +3622,7 @@ var AgentManager = class {
|
|
|
3228
3622
|
proc.status = "dead";
|
|
3229
3623
|
this.agents.delete(key);
|
|
3230
3624
|
this.lastUsedAt.delete(key);
|
|
3231
|
-
|
|
3625
|
+
logger7.info("cancelReply: process torn down", {
|
|
3232
3626
|
agentId,
|
|
3233
3627
|
scope: scopeKey(proc.scope),
|
|
3234
3628
|
conversationId,
|
|
@@ -3237,10 +3631,10 @@ var AgentManager = class {
|
|
|
3237
3631
|
try {
|
|
3238
3632
|
runtime.inputController.close();
|
|
3239
3633
|
} catch (err) {
|
|
3240
|
-
|
|
3634
|
+
logger7.error("cancelReply: inputController.close failed", { agentId, error: err });
|
|
3241
3635
|
}
|
|
3242
3636
|
runtime.query.return(void 0).catch((err) => {
|
|
3243
|
-
|
|
3637
|
+
logger7.warn("cancelReply: query.return threw", { agentId, error: err });
|
|
3244
3638
|
});
|
|
3245
3639
|
}
|
|
3246
3640
|
};
|
|
@@ -3257,7 +3651,7 @@ function buildInnerVoiceEnvelope(payload) {
|
|
|
3257
3651
|
}
|
|
3258
3652
|
|
|
3259
3653
|
// src/agentRegistry.ts
|
|
3260
|
-
var
|
|
3654
|
+
var logger8 = createModuleLogger("agent.registry");
|
|
3261
3655
|
var HttpAgentRegistry = class {
|
|
3262
3656
|
constructor(serverApiUrl) {
|
|
3263
3657
|
this.serverApiUrl = serverApiUrl;
|
|
@@ -3266,8 +3660,8 @@ var HttpAgentRegistry = class {
|
|
|
3266
3660
|
agents = /* @__PURE__ */ new Map();
|
|
3267
3661
|
apiUrl(suffix) {
|
|
3268
3662
|
const base = this.serverApiUrl.replace(/\/$/, "");
|
|
3269
|
-
const
|
|
3270
|
-
return `${base}${
|
|
3663
|
+
const path12 = suffix.startsWith("/") ? suffix : `/${suffix}`;
|
|
3664
|
+
return `${base}${path12}`;
|
|
3271
3665
|
}
|
|
3272
3666
|
async refresh() {
|
|
3273
3667
|
const attempt = async () => {
|
|
@@ -3285,17 +3679,17 @@ var HttpAgentRegistry = class {
|
|
|
3285
3679
|
recoveredAfterRetry = res != null;
|
|
3286
3680
|
}
|
|
3287
3681
|
if (!res) {
|
|
3288
|
-
|
|
3682
|
+
logger8.warn("Agent registry refresh unreachable after retry, keeping cache");
|
|
3289
3683
|
return;
|
|
3290
3684
|
}
|
|
3291
3685
|
if (!res.ok) {
|
|
3292
|
-
|
|
3686
|
+
logger8.warn("Agent registry refresh failed", { status: res.status });
|
|
3293
3687
|
return;
|
|
3294
3688
|
}
|
|
3295
3689
|
try {
|
|
3296
3690
|
const body = await res.json();
|
|
3297
3691
|
if (!Array.isArray(body)) {
|
|
3298
|
-
|
|
3692
|
+
logger8.warn("Agent registry refresh: expected array");
|
|
3299
3693
|
return;
|
|
3300
3694
|
}
|
|
3301
3695
|
this.agents.clear();
|
|
@@ -3305,9 +3699,9 @@ var HttpAgentRegistry = class {
|
|
|
3305
3699
|
this.agents.set(a.id, a);
|
|
3306
3700
|
}
|
|
3307
3701
|
}
|
|
3308
|
-
|
|
3702
|
+
logger8.info("Agent registry refreshed", { count: this.agents.size, recoveredAfterRetry });
|
|
3309
3703
|
} catch (e) {
|
|
3310
|
-
|
|
3704
|
+
logger8.warn("Agent registry refresh parse failed", { error: e });
|
|
3311
3705
|
}
|
|
3312
3706
|
}
|
|
3313
3707
|
getById(id) {
|
|
@@ -3321,17 +3715,17 @@ var HttpAgentRegistry = class {
|
|
|
3321
3715
|
try {
|
|
3322
3716
|
const res = await fetch(this.apiUrl(`/api/agents/${encodeURIComponent(id)}`));
|
|
3323
3717
|
if (!res.ok) {
|
|
3324
|
-
|
|
3718
|
+
logger8.warn("fetchById failed", { agentId: id, status: res.status });
|
|
3325
3719
|
return null;
|
|
3326
3720
|
}
|
|
3327
3721
|
const agent = await res.json();
|
|
3328
3722
|
if (agent && typeof agent.id === "string") {
|
|
3329
3723
|
this.agents.set(agent.id, agent);
|
|
3330
|
-
|
|
3724
|
+
logger8.info("Agent registry fetchById upserted", { agentId: id });
|
|
3331
3725
|
}
|
|
3332
3726
|
return agent;
|
|
3333
3727
|
} catch (e) {
|
|
3334
|
-
|
|
3728
|
+
logger8.warn("fetchById unreachable", { agentId: id, error: e });
|
|
3335
3729
|
return null;
|
|
3336
3730
|
}
|
|
3337
3731
|
}
|
|
@@ -3340,16 +3734,16 @@ var HttpAgentRegistry = class {
|
|
|
3340
3734
|
}
|
|
3341
3735
|
upsert(agent) {
|
|
3342
3736
|
this.agents.set(agent.id, agent);
|
|
3343
|
-
|
|
3737
|
+
logger8.debug("Agent registry upsert", { agentId: agent.id });
|
|
3344
3738
|
}
|
|
3345
3739
|
remove(agentId) {
|
|
3346
3740
|
this.agents.delete(agentId);
|
|
3347
|
-
|
|
3741
|
+
logger8.debug("Agent registry remove", { agentId });
|
|
3348
3742
|
}
|
|
3349
3743
|
};
|
|
3350
3744
|
|
|
3351
3745
|
// src/groupRegistry.ts
|
|
3352
|
-
var
|
|
3746
|
+
var logger9 = createModuleLogger("neural.groupRegistry");
|
|
3353
3747
|
var GroupRegistry = class {
|
|
3354
3748
|
groups = /* @__PURE__ */ new Map();
|
|
3355
3749
|
serverApiUrl;
|
|
@@ -3372,17 +3766,17 @@ var GroupRegistry = class {
|
|
|
3372
3766
|
recoveredAfterRetry = res != null;
|
|
3373
3767
|
}
|
|
3374
3768
|
if (!res) {
|
|
3375
|
-
|
|
3769
|
+
logger9.warn("GroupRegistry refresh unreachable after retry");
|
|
3376
3770
|
return;
|
|
3377
3771
|
}
|
|
3378
3772
|
if (!res.ok) {
|
|
3379
|
-
|
|
3773
|
+
logger9.warn("GroupRegistry refresh failed", { status: res.status });
|
|
3380
3774
|
return;
|
|
3381
3775
|
}
|
|
3382
3776
|
try {
|
|
3383
3777
|
const body = await res.json();
|
|
3384
3778
|
if (!Array.isArray(body)) {
|
|
3385
|
-
|
|
3779
|
+
logger9.warn("GroupRegistry refresh: expected array");
|
|
3386
3780
|
return;
|
|
3387
3781
|
}
|
|
3388
3782
|
this.groups.clear();
|
|
@@ -3397,14 +3791,25 @@ var GroupRegistry = class {
|
|
|
3397
3791
|
});
|
|
3398
3792
|
}
|
|
3399
3793
|
}
|
|
3400
|
-
|
|
3794
|
+
logger9.info("GroupRegistry refreshed", { count: this.groups.size, recoveredAfterRetry });
|
|
3401
3795
|
} catch (e) {
|
|
3402
|
-
|
|
3796
|
+
logger9.warn("GroupRegistry refresh parse failed", { error: e });
|
|
3403
3797
|
}
|
|
3404
3798
|
}
|
|
3405
3799
|
getById(groupId) {
|
|
3406
3800
|
return this.groups.get(groupId) ?? null;
|
|
3407
3801
|
}
|
|
3802
|
+
/**
|
|
3803
|
+
* Return the cached groups that the given agent is a member of.
|
|
3804
|
+
* Does NOT refresh — caller decides when to call refresh().
|
|
3805
|
+
*/
|
|
3806
|
+
getMyGroups(agentId) {
|
|
3807
|
+
const out = [];
|
|
3808
|
+
for (const g of this.groups.values()) {
|
|
3809
|
+
if (g.members.includes(agentId)) out.push(g);
|
|
3810
|
+
}
|
|
3811
|
+
return out;
|
|
3812
|
+
}
|
|
3408
3813
|
/**
|
|
3409
3814
|
* Fuzzy match by name (case-insensitive substring).
|
|
3410
3815
|
* Returns the first match.
|
|
@@ -3438,16 +3843,16 @@ var GroupRegistry = class {
|
|
|
3438
3843
|
try {
|
|
3439
3844
|
const res = await fetch(url);
|
|
3440
3845
|
if (res.status === 404) {
|
|
3441
|
-
|
|
3846
|
+
logger9.info("GroupRegistry resolveScope: group not found", { rawScope, suffix });
|
|
3442
3847
|
return null;
|
|
3443
3848
|
}
|
|
3444
3849
|
if (!res.ok) {
|
|
3445
|
-
|
|
3850
|
+
logger9.warn("GroupRegistry resolveScope: HTTP error", { rawScope, status: res.status });
|
|
3446
3851
|
return null;
|
|
3447
3852
|
}
|
|
3448
3853
|
const data = await res.json();
|
|
3449
3854
|
if (!data.groupId || !data.conversationId || !data.workingDirectory) {
|
|
3450
|
-
|
|
3855
|
+
logger9.warn("GroupRegistry resolveScope: incomplete response", {
|
|
3451
3856
|
rawScope,
|
|
3452
3857
|
hasGroupId: !!data.groupId,
|
|
3453
3858
|
hasConversationId: !!data.conversationId,
|
|
@@ -3455,7 +3860,7 @@ var GroupRegistry = class {
|
|
|
3455
3860
|
});
|
|
3456
3861
|
return null;
|
|
3457
3862
|
}
|
|
3458
|
-
|
|
3863
|
+
logger9.info("GroupRegistry resolved scope", {
|
|
3459
3864
|
rawScope,
|
|
3460
3865
|
resolvedGroupId: data.groupId,
|
|
3461
3866
|
resolvedName: data.name ?? "(none)",
|
|
@@ -3470,7 +3875,7 @@ var GroupRegistry = class {
|
|
|
3470
3875
|
workingDirectory: data.workingDirectory
|
|
3471
3876
|
};
|
|
3472
3877
|
} catch (e) {
|
|
3473
|
-
|
|
3878
|
+
logger9.error("GroupRegistry resolveScope error", { rawScope, error: e });
|
|
3474
3879
|
return null;
|
|
3475
3880
|
}
|
|
3476
3881
|
}
|
|
@@ -3491,12 +3896,12 @@ var GroupRegistry = class {
|
|
|
3491
3896
|
if (Array.isArray(body)) {
|
|
3492
3897
|
const single = body.find((c) => c.type === "single" && typeof c.id === "string");
|
|
3493
3898
|
if (single?.id) {
|
|
3494
|
-
|
|
3899
|
+
logger9.info("GroupRegistry resolved single conv", { agentId, conversationId: single.id });
|
|
3495
3900
|
return single.id;
|
|
3496
3901
|
}
|
|
3497
3902
|
}
|
|
3498
3903
|
} else {
|
|
3499
|
-
|
|
3904
|
+
logger9.warn("GroupRegistry resolveSingle: list failed", { agentId, status: res.status });
|
|
3500
3905
|
}
|
|
3501
3906
|
const created = await fetch(`${this.serverApiUrl}/api/conversations`, {
|
|
3502
3907
|
method: "POST",
|
|
@@ -3504,26 +3909,27 @@ var GroupRegistry = class {
|
|
|
3504
3909
|
body: JSON.stringify({ agentId })
|
|
3505
3910
|
});
|
|
3506
3911
|
if (!created.ok) {
|
|
3507
|
-
|
|
3912
|
+
logger9.warn("GroupRegistry resolveSingle: create failed", { agentId, status: created.status });
|
|
3508
3913
|
return null;
|
|
3509
3914
|
}
|
|
3510
3915
|
const conv = await created.json();
|
|
3511
3916
|
if (typeof conv.id !== "string") {
|
|
3512
|
-
|
|
3917
|
+
logger9.warn("GroupRegistry resolveSingle: created conv missing id", { agentId });
|
|
3513
3918
|
return null;
|
|
3514
3919
|
}
|
|
3515
|
-
|
|
3920
|
+
logger9.info("GroupRegistry created single conv", { agentId, conversationId: conv.id });
|
|
3516
3921
|
return conv.id;
|
|
3517
3922
|
} catch (e) {
|
|
3518
|
-
|
|
3923
|
+
logger9.error("GroupRegistry resolveSingle error", { agentId, error: e });
|
|
3519
3924
|
return null;
|
|
3520
3925
|
}
|
|
3521
3926
|
}
|
|
3522
3927
|
};
|
|
3523
3928
|
|
|
3524
3929
|
// src/connector.ts
|
|
3930
|
+
import os5 from "os";
|
|
3525
3931
|
import WebSocket from "ws";
|
|
3526
|
-
var
|
|
3932
|
+
var logger10 = createModuleLogger("ws.connector");
|
|
3527
3933
|
var ServerConnector = class {
|
|
3528
3934
|
ws = null;
|
|
3529
3935
|
reconnectAttempts = 0;
|
|
@@ -3553,19 +3959,19 @@ var ServerConnector = class {
|
|
|
3553
3959
|
url.searchParams.set("token", this.config.bridgeToken);
|
|
3554
3960
|
}
|
|
3555
3961
|
const wsUrl = url.toString();
|
|
3556
|
-
|
|
3962
|
+
logger10.info("Connecting to server", { url: wsUrl });
|
|
3557
3963
|
const ws = new WebSocket(wsUrl);
|
|
3558
3964
|
ws.on("open", () => {
|
|
3559
3965
|
this.ws = ws;
|
|
3560
3966
|
this.reconnectAttempts = 0;
|
|
3561
|
-
|
|
3967
|
+
logger10.info("Connected to server", { url: this.config.serverUrl });
|
|
3562
3968
|
void this.handleOpen();
|
|
3563
3969
|
});
|
|
3564
3970
|
ws.on("message", (data) => {
|
|
3565
3971
|
this.handleMessage(data);
|
|
3566
3972
|
});
|
|
3567
3973
|
ws.on("close", (code, reason) => {
|
|
3568
|
-
|
|
3974
|
+
logger10.warn("Disconnected from server", {
|
|
3569
3975
|
code,
|
|
3570
3976
|
reason: reason.toString()
|
|
3571
3977
|
});
|
|
@@ -3575,15 +3981,15 @@ var ServerConnector = class {
|
|
|
3575
3981
|
}
|
|
3576
3982
|
});
|
|
3577
3983
|
ws.on("error", (err) => {
|
|
3578
|
-
|
|
3984
|
+
logger10.error("WebSocket error", { error: err });
|
|
3579
3985
|
});
|
|
3580
3986
|
}
|
|
3581
3987
|
async handleOpen() {
|
|
3582
3988
|
try {
|
|
3583
3989
|
await this.onConnected();
|
|
3584
|
-
|
|
3990
|
+
logger10.info("Recovery complete, sending bridge:register");
|
|
3585
3991
|
} catch (err) {
|
|
3586
|
-
|
|
3992
|
+
logger10.error("Recovery failed, registering with degraded state", { error: err });
|
|
3587
3993
|
}
|
|
3588
3994
|
this.register();
|
|
3589
3995
|
}
|
|
@@ -3595,13 +4001,14 @@ var ServerConnector = class {
|
|
|
3595
4001
|
payload: {
|
|
3596
4002
|
bridgeId: this.config.bridgeId,
|
|
3597
4003
|
agents: ids,
|
|
4004
|
+
hostname: os5.hostname(),
|
|
3598
4005
|
queryConfig: {
|
|
3599
4006
|
maxActive: qc.maxActive,
|
|
3600
4007
|
idleTimeoutMs: qc.idleTimeoutMs
|
|
3601
4008
|
}
|
|
3602
4009
|
}
|
|
3603
4010
|
});
|
|
3604
|
-
|
|
4011
|
+
logger10.info("Sent bridge:register", {
|
|
3605
4012
|
bridgeId: this.config.bridgeId,
|
|
3606
4013
|
agents: ids
|
|
3607
4014
|
});
|
|
@@ -3612,7 +4019,7 @@ var ServerConnector = class {
|
|
|
3612
4019
|
const raw = typeof data === "string" ? data : data.toString("utf8");
|
|
3613
4020
|
msg = parseWSMessage(raw);
|
|
3614
4021
|
} catch (e) {
|
|
3615
|
-
|
|
4022
|
+
logger10.error("Invalid WS message from server", { error: e });
|
|
3616
4023
|
return;
|
|
3617
4024
|
}
|
|
3618
4025
|
wsMetrics.incRecv(msg.type);
|
|
@@ -3623,7 +4030,7 @@ var ServerConnector = class {
|
|
|
3623
4030
|
}
|
|
3624
4031
|
case "task:dispatch": {
|
|
3625
4032
|
void this.onTaskDispatch(msg.payload).catch((err) => {
|
|
3626
|
-
|
|
4033
|
+
logger10.error("Failed to handle task:dispatch", {
|
|
3627
4034
|
error: err,
|
|
3628
4035
|
traceId: msg.payload.traceId
|
|
3629
4036
|
});
|
|
@@ -3633,19 +4040,19 @@ var ServerConnector = class {
|
|
|
3633
4040
|
case "task:group_dispatch": {
|
|
3634
4041
|
if (this.onGroupTaskDispatch) {
|
|
3635
4042
|
void this.onGroupTaskDispatch(msg.payload).catch((err) => {
|
|
3636
|
-
|
|
4043
|
+
logger10.error("Failed to handle task:group_dispatch", {
|
|
3637
4044
|
error: err,
|
|
3638
4045
|
traceId: msg.payload.traceId
|
|
3639
4046
|
});
|
|
3640
4047
|
});
|
|
3641
4048
|
} else {
|
|
3642
|
-
|
|
4049
|
+
logger10.warn("Received task:group_dispatch but no handler registered");
|
|
3643
4050
|
}
|
|
3644
4051
|
return;
|
|
3645
4052
|
}
|
|
3646
4053
|
case "user:stop_generation": {
|
|
3647
4054
|
void this.onStopGeneration(msg.payload).catch((err) => {
|
|
3648
|
-
|
|
4055
|
+
logger10.error("Failed to handle user:stop_generation", {
|
|
3649
4056
|
error: err,
|
|
3650
4057
|
traceId: msg.payload.traceId
|
|
3651
4058
|
});
|
|
@@ -3659,16 +4066,17 @@ var ServerConnector = class {
|
|
|
3659
4066
|
case "agent:updated":
|
|
3660
4067
|
case "agent:deleted":
|
|
3661
4068
|
case "group:member_changed":
|
|
4069
|
+
case "group:updated":
|
|
3662
4070
|
case "user:answer_question": {
|
|
3663
4071
|
if (this.onServerPush) {
|
|
3664
4072
|
void Promise.resolve(this.onServerPush(msg)).catch((err) => {
|
|
3665
|
-
|
|
4073
|
+
logger10.error("onServerPush handler failed", { error: err, type: msg.type });
|
|
3666
4074
|
});
|
|
3667
4075
|
}
|
|
3668
4076
|
return;
|
|
3669
4077
|
}
|
|
3670
4078
|
default: {
|
|
3671
|
-
|
|
4079
|
+
logger10.warn("Unhandled server message type", {
|
|
3672
4080
|
type: msg.type
|
|
3673
4081
|
});
|
|
3674
4082
|
}
|
|
@@ -3676,7 +4084,7 @@ var ServerConnector = class {
|
|
|
3676
4084
|
}
|
|
3677
4085
|
send(msg) {
|
|
3678
4086
|
if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
|
|
3679
|
-
|
|
4087
|
+
logger10.warn("Cannot send: WebSocket not open", {
|
|
3680
4088
|
type: msg.type
|
|
3681
4089
|
});
|
|
3682
4090
|
return;
|
|
@@ -3685,14 +4093,14 @@ var ServerConnector = class {
|
|
|
3685
4093
|
this.ws.send(JSON.stringify(msg));
|
|
3686
4094
|
wsMetrics.incSend(msg.type);
|
|
3687
4095
|
} catch (e) {
|
|
3688
|
-
|
|
4096
|
+
logger10.error("Failed to send WS message", { error: e, type: msg.type });
|
|
3689
4097
|
}
|
|
3690
4098
|
}
|
|
3691
4099
|
scheduleReconnect() {
|
|
3692
4100
|
if (this.closing) return;
|
|
3693
4101
|
const delay = this.delays[Math.min(this.reconnectAttempts, this.delays.length - 1)];
|
|
3694
4102
|
this.reconnectAttempts++;
|
|
3695
|
-
|
|
4103
|
+
logger10.info("Scheduling reconnect", {
|
|
3696
4104
|
attempt: this.reconnectAttempts,
|
|
3697
4105
|
delayMs: delay
|
|
3698
4106
|
});
|
|
@@ -3708,11 +4116,11 @@ var ServerConnector = class {
|
|
|
3708
4116
|
try {
|
|
3709
4117
|
this.ws.close(1e3, "Bridge shutting down");
|
|
3710
4118
|
} catch (e) {
|
|
3711
|
-
|
|
4119
|
+
logger10.error("Error closing WebSocket", { error: e });
|
|
3712
4120
|
}
|
|
3713
4121
|
this.ws = null;
|
|
3714
4122
|
}
|
|
3715
|
-
|
|
4123
|
+
logger10.info("Connector closed");
|
|
3716
4124
|
}
|
|
3717
4125
|
get isConnected() {
|
|
3718
4126
|
return this.ws !== null && this.ws.readyState === WebSocket.OPEN;
|
|
@@ -3720,14 +4128,14 @@ var ServerConnector = class {
|
|
|
3720
4128
|
};
|
|
3721
4129
|
|
|
3722
4130
|
// src/modelQuerier.ts
|
|
3723
|
-
import
|
|
3724
|
-
import
|
|
3725
|
-
import
|
|
3726
|
-
var
|
|
4131
|
+
import fs4 from "fs/promises";
|
|
4132
|
+
import os6 from "os";
|
|
4133
|
+
import path8 from "path";
|
|
4134
|
+
var logger11 = createModuleLogger("bridge.modelQuerier");
|
|
3727
4135
|
async function listModels(queryFn, opts = {}) {
|
|
3728
4136
|
const t0 = Date.now();
|
|
3729
|
-
const cwd = opts.cwd ??
|
|
3730
|
-
await
|
|
4137
|
+
const cwd = opts.cwd ?? path8.join(os6.homedir(), ".ahchat", "workspaces", "_list_models");
|
|
4138
|
+
await fs4.mkdir(cwd, { recursive: true });
|
|
3731
4139
|
const fn = queryFn ?? (await import("@anthropic-ai/claude-agent-sdk")).query;
|
|
3732
4140
|
const ic = new InputController();
|
|
3733
4141
|
ic.push("Reply with exactly: PING", "");
|
|
@@ -3769,7 +4177,7 @@ async function listModels(queryFn, opts = {}) {
|
|
|
3769
4177
|
displayName: m.displayName,
|
|
3770
4178
|
description: m.description
|
|
3771
4179
|
}));
|
|
3772
|
-
|
|
4180
|
+
logger11.info("listModels done", { count: models.length, ms: Date.now() - t0 });
|
|
3773
4181
|
return models;
|
|
3774
4182
|
} finally {
|
|
3775
4183
|
try {
|
|
@@ -3784,9 +4192,9 @@ async function listModels(queryFn, opts = {}) {
|
|
|
3784
4192
|
}
|
|
3785
4193
|
|
|
3786
4194
|
// src/lockfile.ts
|
|
3787
|
-
import
|
|
3788
|
-
import
|
|
3789
|
-
var
|
|
4195
|
+
import fs5 from "fs";
|
|
4196
|
+
import path9 from "path";
|
|
4197
|
+
var logger12 = createModuleLogger("bridge.lockfile");
|
|
3790
4198
|
var lockPath = null;
|
|
3791
4199
|
function isProcessAlive(pid) {
|
|
3792
4200
|
try {
|
|
@@ -3799,32 +4207,32 @@ function isProcessAlive(pid) {
|
|
|
3799
4207
|
}
|
|
3800
4208
|
}
|
|
3801
4209
|
function acquireLock(dataDir) {
|
|
3802
|
-
const file =
|
|
4210
|
+
const file = path9.join(dataDir, "bridge.lock");
|
|
3803
4211
|
lockPath = file;
|
|
3804
|
-
if (
|
|
3805
|
-
const raw =
|
|
4212
|
+
if (fs5.existsSync(file)) {
|
|
4213
|
+
const raw = fs5.readFileSync(file, "utf-8").trim();
|
|
3806
4214
|
const pid = Number.parseInt(raw, 10);
|
|
3807
4215
|
if (Number.isFinite(pid) && pid > 0) {
|
|
3808
4216
|
if (isProcessAlive(pid)) {
|
|
3809
4217
|
throw new Error(`Bridge already running (PID: ${pid})`);
|
|
3810
4218
|
}
|
|
3811
|
-
|
|
4219
|
+
logger12.warn("Removing stale bridge.lock (process not found)", { pid, path: file });
|
|
3812
4220
|
}
|
|
3813
4221
|
}
|
|
3814
|
-
|
|
3815
|
-
|
|
3816
|
-
|
|
4222
|
+
fs5.mkdirSync(path9.dirname(file), { recursive: true });
|
|
4223
|
+
fs5.writeFileSync(file, String(process.pid), "utf-8");
|
|
4224
|
+
logger12.info("Acquired bridge lock", { path: file, pid: process.pid });
|
|
3817
4225
|
const release = () => {
|
|
3818
4226
|
try {
|
|
3819
|
-
if (lockPath &&
|
|
3820
|
-
const current =
|
|
4227
|
+
if (lockPath && fs5.existsSync(lockPath)) {
|
|
4228
|
+
const current = fs5.readFileSync(lockPath, "utf-8").trim();
|
|
3821
4229
|
if (current === String(process.pid)) {
|
|
3822
|
-
|
|
3823
|
-
|
|
4230
|
+
fs5.unlinkSync(lockPath);
|
|
4231
|
+
logger12.info("Released bridge lock", { path: lockPath });
|
|
3824
4232
|
}
|
|
3825
4233
|
}
|
|
3826
4234
|
} catch (e) {
|
|
3827
|
-
|
|
4235
|
+
logger12.error("Failed to release bridge lock", { error: e, path: lockPath });
|
|
3828
4236
|
} finally {
|
|
3829
4237
|
lockPath = null;
|
|
3830
4238
|
}
|
|
@@ -3927,9 +4335,9 @@ function buildGroupPrompt(payload) {
|
|
|
3927
4335
|
}
|
|
3928
4336
|
|
|
3929
4337
|
// src/messageHandler.ts
|
|
3930
|
-
var
|
|
4338
|
+
var logger13 = createModuleLogger("msg.handler");
|
|
3931
4339
|
function emitTaskAck(emit, ackId, agentId, traceId) {
|
|
3932
|
-
|
|
4340
|
+
logger13.info("Emitting task:ack", { ackId, agentId, traceId });
|
|
3933
4341
|
emit({
|
|
3934
4342
|
type: "task:ack",
|
|
3935
4343
|
payload: {
|
|
@@ -3942,7 +4350,7 @@ function emitTaskAck(emit, ackId, agentId, traceId) {
|
|
|
3942
4350
|
}
|
|
3943
4351
|
function createTaskDispatchHandler(agentManager, agentRegistry, emit) {
|
|
3944
4352
|
return async (payload) => {
|
|
3945
|
-
|
|
4353
|
+
logger13.info("Handling task:dispatch", {
|
|
3946
4354
|
agentId: payload.agentId,
|
|
3947
4355
|
messageId: payload.messageId,
|
|
3948
4356
|
ackId: payload.ackId,
|
|
@@ -3951,14 +4359,14 @@ function createTaskDispatchHandler(agentManager, agentRegistry, emit) {
|
|
|
3951
4359
|
emitTaskAck(emit, payload.ackId, payload.agentId, payload.traceId);
|
|
3952
4360
|
let agentConfig = agentRegistry.getById(payload.agentId);
|
|
3953
4361
|
if (!agentConfig) {
|
|
3954
|
-
|
|
4362
|
+
logger13.warn("Agent not in registry, attempting live fetch", {
|
|
3955
4363
|
agentId: payload.agentId,
|
|
3956
4364
|
traceId: payload.traceId
|
|
3957
4365
|
});
|
|
3958
4366
|
agentConfig = await agentRegistry.fetchById(payload.agentId);
|
|
3959
4367
|
}
|
|
3960
4368
|
if (!agentConfig) {
|
|
3961
|
-
|
|
4369
|
+
logger13.error("Agent not found for task:dispatch (after live fetch)", {
|
|
3962
4370
|
agentId: payload.agentId,
|
|
3963
4371
|
traceId: payload.traceId
|
|
3964
4372
|
});
|
|
@@ -3985,7 +4393,7 @@ function createTaskDispatchHandler(agentManager, agentRegistry, emit) {
|
|
|
3985
4393
|
traceId: payload.traceId
|
|
3986
4394
|
});
|
|
3987
4395
|
} catch (err) {
|
|
3988
|
-
|
|
4396
|
+
logger13.error("Failed to dispatch message to Agent", {
|
|
3989
4397
|
error: err,
|
|
3990
4398
|
agentId: payload.agentId,
|
|
3991
4399
|
traceId: payload.traceId
|
|
@@ -4005,7 +4413,7 @@ function createTaskDispatchHandler(agentManager, agentRegistry, emit) {
|
|
|
4005
4413
|
}
|
|
4006
4414
|
function createGroupTaskDispatchHandler(agentManager, agentRegistry, emit) {
|
|
4007
4415
|
return async (payload) => {
|
|
4008
|
-
|
|
4416
|
+
logger13.info("Handling task:group_dispatch", {
|
|
4009
4417
|
agentId: payload.agentId,
|
|
4010
4418
|
groupId: payload.groupId,
|
|
4011
4419
|
ackId: payload.ackId,
|
|
@@ -4017,14 +4425,14 @@ function createGroupTaskDispatchHandler(agentManager, agentRegistry, emit) {
|
|
|
4017
4425
|
emitTaskAck(emit, payload.ackId, payload.agentId, payload.traceId);
|
|
4018
4426
|
let agentConfig = agentRegistry.getById(payload.agentId);
|
|
4019
4427
|
if (!agentConfig) {
|
|
4020
|
-
|
|
4428
|
+
logger13.warn("Agent not in registry for group dispatch, attempting live fetch", {
|
|
4021
4429
|
agentId: payload.agentId,
|
|
4022
4430
|
traceId: payload.traceId
|
|
4023
4431
|
});
|
|
4024
4432
|
agentConfig = await agentRegistry.fetchById(payload.agentId);
|
|
4025
4433
|
}
|
|
4026
4434
|
if (!agentConfig) {
|
|
4027
|
-
|
|
4435
|
+
logger13.error("Agent not found for task:group_dispatch (after live fetch)", {
|
|
4028
4436
|
agentId: payload.agentId,
|
|
4029
4437
|
traceId: payload.traceId
|
|
4030
4438
|
});
|
|
@@ -4057,7 +4465,7 @@ function createGroupTaskDispatchHandler(agentManager, agentRegistry, emit) {
|
|
|
4057
4465
|
groupId: payload.groupId
|
|
4058
4466
|
});
|
|
4059
4467
|
} catch (err) {
|
|
4060
|
-
|
|
4468
|
+
logger13.error("Failed to dispatch group message to Agent", {
|
|
4061
4469
|
error: err,
|
|
4062
4470
|
agentId: payload.agentId,
|
|
4063
4471
|
groupId: payload.groupId,
|
|
@@ -4077,15 +4485,84 @@ function createGroupTaskDispatchHandler(agentManager, agentRegistry, emit) {
|
|
|
4077
4485
|
};
|
|
4078
4486
|
}
|
|
4079
4487
|
|
|
4488
|
+
// src/scopePushNotify.ts
|
|
4489
|
+
var logger14 = createModuleLogger("bridge");
|
|
4490
|
+
function buildMemberChangedScopeNotice(params) {
|
|
4491
|
+
const { groupId, groupLabel, action } = params;
|
|
4492
|
+
const verb = action === "added" ? "\u52A0\u5165" : "\u79FB\u51FA";
|
|
4493
|
+
return [
|
|
4494
|
+
`[\u7CFB\u7EDF\u901A\u77E5] \u4F60\u5DF2\u88AB${verb}\u7FA4\u300C${groupLabel}\u300D(group:${groupId})\u3002`,
|
|
4495
|
+
`\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`,
|
|
4496
|
+
`\u65E0\u9700\u56DE\u590D\u6B64\u901A\u77E5\u3002`
|
|
4497
|
+
].join("\n");
|
|
4498
|
+
}
|
|
4499
|
+
function buildGroupRenamedScopeNotice(params) {
|
|
4500
|
+
const { groupId, newName } = params;
|
|
4501
|
+
return [
|
|
4502
|
+
`[\u7CFB\u7EDF\u901A\u77E5] \u7FA4 (group:${groupId}) \u5DF2\u66F4\u540D\u4E3A\u300C${newName}\u300D\u3002`,
|
|
4503
|
+
`\u82E5\u4F60\u4E4B\u524D\u7528\u65E7\u7FA4\u540D\u8C03\u8FC7 neural_send\uFF0C\u8BF7\u66F4\u65B0\u4E3A\u65B0\u540D\u79F0\u3002`,
|
|
4504
|
+
`\u65E0\u9700\u56DE\u590D\u6B64\u901A\u77E5\u3002`
|
|
4505
|
+
].join("\n");
|
|
4506
|
+
}
|
|
4507
|
+
async function handleGroupMemberChangedPush(deps, payload) {
|
|
4508
|
+
const { groupId, action, agentId } = payload;
|
|
4509
|
+
logger14.info("group:member_changed received, refreshing GroupRegistry", {
|
|
4510
|
+
groupId,
|
|
4511
|
+
action,
|
|
4512
|
+
agentId
|
|
4513
|
+
});
|
|
4514
|
+
await deps.groupRegistry.refresh();
|
|
4515
|
+
logger14.info("GroupRegistry refreshed after member_changed", {
|
|
4516
|
+
groupId,
|
|
4517
|
+
action,
|
|
4518
|
+
registryGroupCount: deps.groupRegistry.getAll().length
|
|
4519
|
+
});
|
|
4520
|
+
const group = deps.groupRegistry.getById(groupId);
|
|
4521
|
+
const groupLabel = group?.name ?? groupId;
|
|
4522
|
+
const notice = buildMemberChangedScopeNotice({ groupId, groupLabel, action });
|
|
4523
|
+
deps.agentManager.broadcastScopeNotice(agentId, notice);
|
|
4524
|
+
logger14.info("Scope notice sent for member_changed", {
|
|
4525
|
+
agentId,
|
|
4526
|
+
groupId,
|
|
4527
|
+
groupLabel,
|
|
4528
|
+
action,
|
|
4529
|
+
noticeLen: notice.length
|
|
4530
|
+
});
|
|
4531
|
+
}
|
|
4532
|
+
async function handleGroupUpdatedPush(deps, payload) {
|
|
4533
|
+
const { groupId, name: newName, memberAgentIds } = payload;
|
|
4534
|
+
logger14.info("group:updated received, refreshing GroupRegistry", {
|
|
4535
|
+
groupId,
|
|
4536
|
+
newName,
|
|
4537
|
+
memberCount: memberAgentIds.length
|
|
4538
|
+
});
|
|
4539
|
+
await deps.groupRegistry.refresh();
|
|
4540
|
+
logger14.info("GroupRegistry refreshed after group:updated", {
|
|
4541
|
+
groupId,
|
|
4542
|
+
newName,
|
|
4543
|
+
registryGroupCount: deps.groupRegistry.getAll().length
|
|
4544
|
+
});
|
|
4545
|
+
const notice = buildGroupRenamedScopeNotice({ groupId, newName });
|
|
4546
|
+
for (const aid of memberAgentIds) {
|
|
4547
|
+
deps.agentManager.broadcastScopeNotice(aid, notice);
|
|
4548
|
+
}
|
|
4549
|
+
logger14.info("Scope notices sent for group:updated", {
|
|
4550
|
+
groupId,
|
|
4551
|
+
newName,
|
|
4552
|
+
memberCount: memberAgentIds.length,
|
|
4553
|
+
noticeLen: notice.length
|
|
4554
|
+
});
|
|
4555
|
+
}
|
|
4556
|
+
|
|
4080
4557
|
// src/sessionStore.ts
|
|
4081
|
-
import
|
|
4082
|
-
import
|
|
4083
|
-
var
|
|
4558
|
+
import fs6 from "fs";
|
|
4559
|
+
import path10 from "path";
|
|
4560
|
+
var logger15 = createModuleLogger("session.store");
|
|
4084
4561
|
var SessionStore = class {
|
|
4085
4562
|
filePath;
|
|
4086
4563
|
cache;
|
|
4087
4564
|
constructor(dataDir) {
|
|
4088
|
-
this.filePath =
|
|
4565
|
+
this.filePath = path10.join(dataDir, "sessions.json");
|
|
4089
4566
|
this.cache = this.loadFromDisk();
|
|
4090
4567
|
}
|
|
4091
4568
|
cacheKey(agentId, scope) {
|
|
@@ -4120,8 +4597,8 @@ var SessionStore = class {
|
|
|
4120
4597
|
}
|
|
4121
4598
|
loadFromDisk() {
|
|
4122
4599
|
try {
|
|
4123
|
-
if (!
|
|
4124
|
-
const raw =
|
|
4600
|
+
if (!fs6.existsSync(this.filePath)) return {};
|
|
4601
|
+
const raw = fs6.readFileSync(this.filePath, "utf-8");
|
|
4125
4602
|
const parsed = JSON.parse(raw);
|
|
4126
4603
|
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return {};
|
|
4127
4604
|
const map = parsed;
|
|
@@ -4131,7 +4608,7 @@ var SessionStore = class {
|
|
|
4131
4608
|
migrated[key] = sessionId;
|
|
4132
4609
|
} else {
|
|
4133
4610
|
migrated[`${key}::single`] = sessionId;
|
|
4134
|
-
|
|
4611
|
+
logger15.info("Migrated legacy session key to scoped key", {
|
|
4135
4612
|
legacyKey: key,
|
|
4136
4613
|
newKey: `${key}::single`
|
|
4137
4614
|
});
|
|
@@ -4139,29 +4616,29 @@ var SessionStore = class {
|
|
|
4139
4616
|
}
|
|
4140
4617
|
return migrated;
|
|
4141
4618
|
} catch (e) {
|
|
4142
|
-
|
|
4619
|
+
logger15.warn("Failed to load sessions file, starting fresh", { error: e, path: this.filePath });
|
|
4143
4620
|
return {};
|
|
4144
4621
|
}
|
|
4145
4622
|
}
|
|
4146
4623
|
saveToDisk() {
|
|
4147
4624
|
try {
|
|
4148
|
-
const dir =
|
|
4149
|
-
|
|
4150
|
-
|
|
4625
|
+
const dir = path10.dirname(this.filePath);
|
|
4626
|
+
fs6.mkdirSync(dir, { recursive: true });
|
|
4627
|
+
fs6.writeFileSync(this.filePath, JSON.stringify(this.cache, null, 2), "utf-8");
|
|
4151
4628
|
} catch (e) {
|
|
4152
|
-
|
|
4629
|
+
logger15.error("Failed to save sessions file", { error: e, path: this.filePath });
|
|
4153
4630
|
}
|
|
4154
4631
|
}
|
|
4155
4632
|
};
|
|
4156
4633
|
|
|
4157
4634
|
// src/start.ts
|
|
4158
|
-
var
|
|
4635
|
+
var logger16 = createModuleLogger("bridge");
|
|
4159
4636
|
async function startBridge(config) {
|
|
4160
4637
|
ensureDir(config.dataDir);
|
|
4161
4638
|
ensureDir(config.claudeConfigDir);
|
|
4162
4639
|
process.env.CLAUDE_CONFIG_DIR = config.claudeConfigDir;
|
|
4163
4640
|
acquireLock(config.dataDir);
|
|
4164
|
-
|
|
4641
|
+
logger16.info("Bridge starting", {
|
|
4165
4642
|
bridgeId: config.bridgeId,
|
|
4166
4643
|
serverUrl: config.serverUrl,
|
|
4167
4644
|
serverApiUrl: config.serverApiUrl,
|
|
@@ -4169,6 +4646,9 @@ async function startBridge(config) {
|
|
|
4169
4646
|
});
|
|
4170
4647
|
wsMetrics.start(5e3);
|
|
4171
4648
|
const sessionStore = new SessionStore(config.dataDir);
|
|
4649
|
+
const memoryRoot = path11.join(config.dataDir, "agent-memory");
|
|
4650
|
+
const memoryStore = new AgentMemoryStore(memoryRoot);
|
|
4651
|
+
logger16.info("Agent memory store initialized", { rootDir: memoryRoot });
|
|
4172
4652
|
const agentRegistry = new HttpAgentRegistry(config.serverApiUrl);
|
|
4173
4653
|
const groupRegistry = new GroupRegistry(config.serverApiUrl);
|
|
4174
4654
|
await agentRegistry.refresh();
|
|
@@ -4182,7 +4662,8 @@ async function startBridge(config) {
|
|
|
4182
4662
|
queryConfig: config.queryConfig,
|
|
4183
4663
|
claudeConfigDir: config.claudeConfigDir,
|
|
4184
4664
|
askQuestionRegistry,
|
|
4185
|
-
groupRegistry
|
|
4665
|
+
groupRegistry,
|
|
4666
|
+
memoryStore
|
|
4186
4667
|
});
|
|
4187
4668
|
const taskDispatchHandler = createTaskDispatchHandler(agentManager, agentRegistry, emit);
|
|
4188
4669
|
const groupTaskDispatchHandler = createGroupTaskDispatchHandler(agentManager, agentRegistry, emit);
|
|
@@ -4204,21 +4685,21 @@ async function startBridge(config) {
|
|
|
4204
4685
|
switch (msg.type) {
|
|
4205
4686
|
case "bridge:list_models_request": {
|
|
4206
4687
|
const { requestId } = msg.payload;
|
|
4207
|
-
|
|
4688
|
+
logger16.info("list_models request received", { requestId });
|
|
4208
4689
|
try {
|
|
4209
4690
|
const models = await listModels();
|
|
4210
4691
|
connector?.send({
|
|
4211
4692
|
type: "bridge:list_models_response",
|
|
4212
4693
|
payload: { requestId, models }
|
|
4213
4694
|
});
|
|
4214
|
-
|
|
4695
|
+
logger16.info("list_models response sent", { requestId, count: models.length });
|
|
4215
4696
|
} catch (e) {
|
|
4216
4697
|
const err = e instanceof Error ? e.message : String(e);
|
|
4217
4698
|
connector?.send({
|
|
4218
4699
|
type: "bridge:list_models_response",
|
|
4219
4700
|
payload: { requestId, error: err }
|
|
4220
4701
|
});
|
|
4221
|
-
|
|
4702
|
+
logger16.error("list_models failed", { requestId, error: e });
|
|
4222
4703
|
}
|
|
4223
4704
|
break;
|
|
4224
4705
|
}
|
|
@@ -4226,7 +4707,7 @@ async function startBridge(config) {
|
|
|
4226
4707
|
await agentManager.terminate(msg.payload.agentId);
|
|
4227
4708
|
break;
|
|
4228
4709
|
case "agent:terminate_scope":
|
|
4229
|
-
|
|
4710
|
+
logger16.info("agent:terminate_scope received", {
|
|
4230
4711
|
agentId: msg.payload.agentId,
|
|
4231
4712
|
scope: msg.payload.scope
|
|
4232
4713
|
});
|
|
@@ -4239,11 +4720,31 @@ async function startBridge(config) {
|
|
|
4239
4720
|
case "agent:deleted":
|
|
4240
4721
|
agentRegistry.remove(msg.payload.agentId);
|
|
4241
4722
|
break;
|
|
4723
|
+
case "group:member_changed":
|
|
4724
|
+
await handleGroupMemberChangedPush(
|
|
4725
|
+
{ groupRegistry, agentManager },
|
|
4726
|
+
{
|
|
4727
|
+
groupId: msg.payload.groupId,
|
|
4728
|
+
action: msg.payload.action,
|
|
4729
|
+
agentId: msg.payload.agentId
|
|
4730
|
+
}
|
|
4731
|
+
);
|
|
4732
|
+
break;
|
|
4733
|
+
case "group:updated":
|
|
4734
|
+
await handleGroupUpdatedPush(
|
|
4735
|
+
{ groupRegistry, agentManager },
|
|
4736
|
+
{
|
|
4737
|
+
groupId: msg.payload.groupId,
|
|
4738
|
+
name: msg.payload.name,
|
|
4739
|
+
memberAgentIds: msg.payload.memberAgentIds
|
|
4740
|
+
}
|
|
4741
|
+
);
|
|
4742
|
+
break;
|
|
4242
4743
|
case "user:answer_question": {
|
|
4243
4744
|
const p = msg.payload;
|
|
4244
4745
|
const answerText = formatAnswerForSDK(p);
|
|
4245
4746
|
const ok = askQuestionRegistry.resolve(p.questionId, answerText);
|
|
4246
|
-
|
|
4747
|
+
logger16.info("user:answer_question handled", {
|
|
4247
4748
|
questionId: p.questionId,
|
|
4248
4749
|
agentId: p.agentId,
|
|
4249
4750
|
resolved: ok,
|
|
@@ -4264,7 +4765,7 @@ async function startBridge(config) {
|
|
|
4264
4765
|
});
|
|
4265
4766
|
}, config.queryConfig.statusReportIntervalMs);
|
|
4266
4767
|
const shutdown = async (signal) => {
|
|
4267
|
-
|
|
4768
|
+
logger16.info("Shutdown signal received", { signal });
|
|
4268
4769
|
if (statusInterval) {
|
|
4269
4770
|
clearInterval(statusInterval);
|
|
4270
4771
|
statusInterval = null;
|
|
@@ -4272,7 +4773,7 @@ async function startBridge(config) {
|
|
|
4272
4773
|
wsMetrics.stop();
|
|
4273
4774
|
connector?.close();
|
|
4274
4775
|
await agentManager.shutdownAll();
|
|
4275
|
-
|
|
4776
|
+
logger16.info("Bridge stopped");
|
|
4276
4777
|
process.exit(0);
|
|
4277
4778
|
};
|
|
4278
4779
|
process.on("SIGINT", () => void shutdown("SIGINT"));
|
|
@@ -4280,8 +4781,8 @@ async function startBridge(config) {
|
|
|
4280
4781
|
}
|
|
4281
4782
|
|
|
4282
4783
|
// src/index.ts
|
|
4283
|
-
var
|
|
4784
|
+
var logger17 = createModuleLogger("bridge");
|
|
4284
4785
|
void startBridge(loadBridgeConfig()).catch((e) => {
|
|
4285
|
-
|
|
4786
|
+
logger17.error("Bridge failed to start", { error: e });
|
|
4286
4787
|
process.exit(1);
|
|
4287
4788
|
});
|