@ethosagent/core 0.4.2 → 0.4.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +122 -5
- package/dist/index.js +396 -77
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
package/dist/index.js
CHANGED
|
@@ -355,6 +355,7 @@ import { createHash } from "crypto";
|
|
|
355
355
|
import { join, resolve, sep } from "path";
|
|
356
356
|
|
|
357
357
|
// ../storage-fs/src/fs-storage.ts
|
|
358
|
+
import { existsSync as fsExistsSync } from "fs";
|
|
358
359
|
import {
|
|
359
360
|
appendFile,
|
|
360
361
|
chmod,
|
|
@@ -664,6 +665,26 @@ var DefaultContextEngineRegistry = class {
|
|
|
664
665
|
}
|
|
665
666
|
};
|
|
666
667
|
|
|
668
|
+
// src/context-store.ts
|
|
669
|
+
var ContextStore = class {
|
|
670
|
+
store = /* @__PURE__ */ new Map();
|
|
671
|
+
get(key) {
|
|
672
|
+
return this.store.get(key);
|
|
673
|
+
}
|
|
674
|
+
set(key, value) {
|
|
675
|
+
this.store.set(key, value);
|
|
676
|
+
}
|
|
677
|
+
clear() {
|
|
678
|
+
this.store.clear();
|
|
679
|
+
}
|
|
680
|
+
asContextMethods() {
|
|
681
|
+
return {
|
|
682
|
+
getContext: (key) => this.get(key),
|
|
683
|
+
setContext: (key, value) => this.set(key, value)
|
|
684
|
+
};
|
|
685
|
+
}
|
|
686
|
+
};
|
|
687
|
+
|
|
667
688
|
// src/defaults/in-memory-session.ts
|
|
668
689
|
var InMemorySessionStore = class {
|
|
669
690
|
sessions = /* @__PURE__ */ new Map();
|
|
@@ -972,6 +993,101 @@ var DefaultHookRegistry = class {
|
|
|
972
993
|
}
|
|
973
994
|
};
|
|
974
995
|
|
|
996
|
+
// src/simple-completion.ts
|
|
997
|
+
var SimpleCompletionImpl = class {
|
|
998
|
+
constructor(provider, defaultModel, onUsage) {
|
|
999
|
+
this.provider = provider;
|
|
1000
|
+
this.defaultModel = defaultModel;
|
|
1001
|
+
this.onUsage = onUsage;
|
|
1002
|
+
}
|
|
1003
|
+
provider;
|
|
1004
|
+
defaultModel;
|
|
1005
|
+
onUsage;
|
|
1006
|
+
async complete(prompt, options) {
|
|
1007
|
+
const model = options?.model ?? this.defaultModel;
|
|
1008
|
+
let text = "";
|
|
1009
|
+
let inputTokens = 0;
|
|
1010
|
+
let outputTokens = 0;
|
|
1011
|
+
const stream = this.provider.complete([{ role: "user", content: prompt }], [], {
|
|
1012
|
+
system: options?.systemPrompt,
|
|
1013
|
+
maxTokens: options?.maxTokens ?? 1024,
|
|
1014
|
+
modelOverride: model !== this.provider.model ? model : void 0
|
|
1015
|
+
});
|
|
1016
|
+
for await (const chunk of stream) {
|
|
1017
|
+
if (chunk.type === "text_delta") text += chunk.text;
|
|
1018
|
+
if (chunk.type === "usage") {
|
|
1019
|
+
inputTokens += chunk.usage.inputTokens;
|
|
1020
|
+
outputTokens += chunk.usage.outputTokens;
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
1023
|
+
this.onUsage({ input: inputTokens, output: outputTokens });
|
|
1024
|
+
return text;
|
|
1025
|
+
}
|
|
1026
|
+
};
|
|
1027
|
+
|
|
1028
|
+
// src/capability-validator.ts
|
|
1029
|
+
function hostMatchesPattern(host, pattern) {
|
|
1030
|
+
if (pattern === host) return true;
|
|
1031
|
+
if (pattern === "*") return true;
|
|
1032
|
+
if (pattern.startsWith("*.")) {
|
|
1033
|
+
const suffix = pattern.slice(1);
|
|
1034
|
+
return host.endsWith(suffix) && host.length > suffix.length;
|
|
1035
|
+
}
|
|
1036
|
+
return false;
|
|
1037
|
+
}
|
|
1038
|
+
function validateRegistration(tool, personality) {
|
|
1039
|
+
const caps = tool.capabilities;
|
|
1040
|
+
if (!caps) return [];
|
|
1041
|
+
const errors = [];
|
|
1042
|
+
if (caps.network) {
|
|
1043
|
+
const allowed = personality.safety?.network?.allow;
|
|
1044
|
+
if (allowed && allowed.length > 0) {
|
|
1045
|
+
for (const host of caps.network.allowedHosts) {
|
|
1046
|
+
if (host === "*") continue;
|
|
1047
|
+
const covered = allowed.some((pattern) => hostMatchesPattern(host, pattern));
|
|
1048
|
+
if (!covered) {
|
|
1049
|
+
errors.push({
|
|
1050
|
+
tool: tool.name,
|
|
1051
|
+
capability: "network",
|
|
1052
|
+
message: `host "${host}" is not in personality network allow list`
|
|
1053
|
+
});
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
if (caps.fs_reach) {
|
|
1059
|
+
const personalityRead = personality.fs_reach?.read ?? [];
|
|
1060
|
+
const personalityWrite = personality.fs_reach?.write ?? [];
|
|
1061
|
+
if (caps.fs_reach.read && caps.fs_reach.read !== "from-personality") {
|
|
1062
|
+
for (const toolPath of caps.fs_reach.read) {
|
|
1063
|
+
const covered = personalityRead.some((p) => toolPath === p || toolPath.startsWith(`${p}/`));
|
|
1064
|
+
if (!covered) {
|
|
1065
|
+
errors.push({
|
|
1066
|
+
tool: tool.name,
|
|
1067
|
+
capability: "fs_reach.read",
|
|
1068
|
+
message: `path "${toolPath}" is not covered by personality fs_reach.read`
|
|
1069
|
+
});
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
1072
|
+
}
|
|
1073
|
+
if (caps.fs_reach.write && caps.fs_reach.write !== "from-personality") {
|
|
1074
|
+
for (const toolPath of caps.fs_reach.write) {
|
|
1075
|
+
const covered = personalityWrite.some(
|
|
1076
|
+
(p) => toolPath === p || toolPath.startsWith(`${p}/`)
|
|
1077
|
+
);
|
|
1078
|
+
if (!covered) {
|
|
1079
|
+
errors.push({
|
|
1080
|
+
tool: tool.name,
|
|
1081
|
+
capability: "fs_reach.write",
|
|
1082
|
+
message: `path "${toolPath}" is not covered by personality fs_reach.write`
|
|
1083
|
+
});
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
}
|
|
1088
|
+
return errors;
|
|
1089
|
+
}
|
|
1090
|
+
|
|
975
1091
|
// src/scoped/scoped-attachments.ts
|
|
976
1092
|
var ScopedAttachmentsImpl = class {
|
|
977
1093
|
attachments;
|
|
@@ -1518,68 +1634,55 @@ function resolveCapabilities(toolName, capabilities, scopeIds, backends) {
|
|
|
1518
1634
|
return result;
|
|
1519
1635
|
}
|
|
1520
1636
|
|
|
1521
|
-
// src/
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
return host.endsWith(suffix) && host.length > suffix.length;
|
|
1528
|
-
}
|
|
1529
|
-
return false;
|
|
1530
|
-
}
|
|
1531
|
-
function validateRegistration(tool, personality) {
|
|
1532
|
-
const caps = tool.capabilities;
|
|
1533
|
-
if (!caps) return [];
|
|
1534
|
-
const errors = [];
|
|
1535
|
-
if (caps.network) {
|
|
1536
|
-
const allowed = personality.safety?.network?.allow;
|
|
1537
|
-
if (allowed && allowed.length > 0) {
|
|
1538
|
-
for (const host of caps.network.allowedHosts) {
|
|
1539
|
-
if (host === "*") continue;
|
|
1540
|
-
const covered = allowed.some((pattern) => hostMatchesPattern(host, pattern));
|
|
1541
|
-
if (!covered) {
|
|
1542
|
-
errors.push({
|
|
1543
|
-
tool: tool.name,
|
|
1544
|
-
capability: "network",
|
|
1545
|
-
message: `host "${host}" is not in personality network allow list`
|
|
1546
|
-
});
|
|
1547
|
-
}
|
|
1548
|
-
}
|
|
1549
|
-
}
|
|
1637
|
+
// src/local-tool-transport.ts
|
|
1638
|
+
var LocalToolTransport = class {
|
|
1639
|
+
constructor(lookup, backends, getLiveCtx) {
|
|
1640
|
+
this.lookup = lookup;
|
|
1641
|
+
this.backends = backends;
|
|
1642
|
+
this.getLiveCtx = getLiveCtx;
|
|
1550
1643
|
}
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
errors.push({
|
|
1559
|
-
tool: tool.name,
|
|
1560
|
-
capability: "fs_reach.read",
|
|
1561
|
-
message: `path "${toolPath}" is not covered by personality fs_reach.read`
|
|
1562
|
-
});
|
|
1563
|
-
}
|
|
1564
|
-
}
|
|
1644
|
+
lookup;
|
|
1645
|
+
backends;
|
|
1646
|
+
getLiveCtx;
|
|
1647
|
+
async execute(request, signal) {
|
|
1648
|
+
const tool = this.lookup(request.name);
|
|
1649
|
+
if (!tool) {
|
|
1650
|
+
return { ok: false, error: `Tool '${request.name}' not found`, code: "not_available" };
|
|
1565
1651
|
}
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1652
|
+
const live = this.getLiveCtx?.();
|
|
1653
|
+
const ctx = {
|
|
1654
|
+
sessionId: request.sessionId,
|
|
1655
|
+
sessionKey: request.sessionKey,
|
|
1656
|
+
platform: request.platform,
|
|
1657
|
+
workingDir: request.workingDir,
|
|
1658
|
+
personalityId: request.personalityId,
|
|
1659
|
+
teamId: request.teamId,
|
|
1660
|
+
agentId: request.agentId,
|
|
1661
|
+
memoryScopeId: request.memoryScopeId,
|
|
1662
|
+
userScopeId: request.userScopeId,
|
|
1663
|
+
currentTurn: request.currentTurn,
|
|
1664
|
+
messageCount: request.messageCount,
|
|
1665
|
+
resultBudgetChars: request.resultBudgetChars,
|
|
1666
|
+
networkPolicy: request.networkPolicy,
|
|
1667
|
+
dryRun: request.dryRun,
|
|
1668
|
+
abortSignal: signal,
|
|
1669
|
+
emit: live?.emit ?? (() => {
|
|
1670
|
+
}),
|
|
1671
|
+
readMtimes: live?.readMtimes,
|
|
1672
|
+
storage: live?.storage
|
|
1673
|
+
};
|
|
1674
|
+
if (tool.capabilities && this.backends) {
|
|
1675
|
+
const resolved = resolveCapabilities(
|
|
1676
|
+
tool.name,
|
|
1677
|
+
tool.capabilities,
|
|
1678
|
+
{ sessionId: request.sessionId, personalityId: request.personalityId },
|
|
1679
|
+
{ ...this.backends, inboundAttachments: live?.inboundAttachments }
|
|
1680
|
+
);
|
|
1681
|
+
Object.assign(ctx, resolved);
|
|
1579
1682
|
}
|
|
1683
|
+
return tool.execute(request.args, ctx);
|
|
1580
1684
|
}
|
|
1581
|
-
|
|
1582
|
-
}
|
|
1685
|
+
};
|
|
1583
1686
|
|
|
1584
1687
|
// src/tool-registry.ts
|
|
1585
1688
|
function needsBackends(caps) {
|
|
@@ -1620,13 +1723,53 @@ function safeReduce(r, result, ctx) {
|
|
|
1620
1723
|
return result;
|
|
1621
1724
|
}
|
|
1622
1725
|
}
|
|
1726
|
+
var DEFAULT_CACHE_TTL_MS = 3e5;
|
|
1727
|
+
var MAX_CACHE_ENTRIES = 1e3;
|
|
1623
1728
|
var DefaultToolRegistry = class {
|
|
1624
1729
|
tools = /* @__PURE__ */ new Map();
|
|
1730
|
+
resultCache = /* @__PURE__ */ new Map();
|
|
1625
1731
|
backends;
|
|
1626
1732
|
reducers;
|
|
1627
|
-
|
|
1733
|
+
transport;
|
|
1734
|
+
// Per-turn live context — updated by executeParallel before dispatching.
|
|
1735
|
+
turnLiveCtx = { emit: () => {
|
|
1736
|
+
} };
|
|
1737
|
+
constructor(backends, reducers, transport) {
|
|
1628
1738
|
this.backends = backends;
|
|
1629
1739
|
this.reducers = reducers;
|
|
1740
|
+
this.transport = transport ?? new LocalToolTransport(
|
|
1741
|
+
(name) => this.tools.get(name)?.tool,
|
|
1742
|
+
backends,
|
|
1743
|
+
() => this.turnLiveCtx
|
|
1744
|
+
);
|
|
1745
|
+
}
|
|
1746
|
+
cacheGet(tool, args, ctx) {
|
|
1747
|
+
if (!tool.cache) return null;
|
|
1748
|
+
const opts = tool.cache === true ? {} : tool.cache;
|
|
1749
|
+
const keyPart = opts.keyFn ? opts.keyFn(args) : JSON.stringify(args);
|
|
1750
|
+
const key = `${tool.name}:${ctx.sessionId}:${ctx.personalityId ?? ""}:${keyPart}`;
|
|
1751
|
+
const entry = this.resultCache.get(key);
|
|
1752
|
+
if (!entry) return null;
|
|
1753
|
+
if (entry.expiresAt !== null && Date.now() > entry.expiresAt) {
|
|
1754
|
+
this.resultCache.delete(key);
|
|
1755
|
+
return null;
|
|
1756
|
+
}
|
|
1757
|
+
return entry.result;
|
|
1758
|
+
}
|
|
1759
|
+
cacheSet(tool, args, result, ctx) {
|
|
1760
|
+
if (!tool.cache) return;
|
|
1761
|
+
const opts = tool.cache === true ? {} : tool.cache;
|
|
1762
|
+
const keyPart = opts.keyFn ? opts.keyFn(args) : JSON.stringify(args);
|
|
1763
|
+
const key = `${tool.name}:${ctx.sessionId}:${ctx.personalityId ?? ""}:${keyPart}`;
|
|
1764
|
+
const ttl = opts.ttlMs ?? DEFAULT_CACHE_TTL_MS;
|
|
1765
|
+
const expiresAt = Date.now() + ttl;
|
|
1766
|
+
this.resultCache.set(key, { result, expiresAt });
|
|
1767
|
+
if (this.resultCache.size > MAX_CACHE_ENTRIES) {
|
|
1768
|
+
const oldest = this.resultCache.keys().next().value;
|
|
1769
|
+
if (oldest !== void 0) {
|
|
1770
|
+
this.resultCache.delete(oldest);
|
|
1771
|
+
}
|
|
1772
|
+
}
|
|
1630
1773
|
}
|
|
1631
1774
|
register(tool, opts) {
|
|
1632
1775
|
this.tools.set(tool.name, { tool, pluginId: opts?.pluginId });
|
|
@@ -1665,6 +1808,10 @@ var DefaultToolRegistry = class {
|
|
|
1665
1808
|
getForToolset(toolset) {
|
|
1666
1809
|
return this.getAvailable().filter((t) => t.toolset === toolset);
|
|
1667
1810
|
}
|
|
1811
|
+
/** v2.2 — Return the plugin id that registered a tool, if any. */
|
|
1812
|
+
getPluginId(name) {
|
|
1813
|
+
return this.tools.get(name)?.pluginId;
|
|
1814
|
+
}
|
|
1668
1815
|
toDefinitions(allowedTools, filterOpts) {
|
|
1669
1816
|
const entries = [...this.tools.values()].filter(
|
|
1670
1817
|
(e) => !e.tool.isAvailable || e.tool.isAvailable()
|
|
@@ -1717,8 +1864,34 @@ var DefaultToolRegistry = class {
|
|
|
1717
1864
|
// Runs all tool calls in parallel. Results are returned in input order.
|
|
1718
1865
|
// Budget is split evenly across parallel calls; each result is post-trimmed to budget.
|
|
1719
1866
|
// allowedTools + filterOpts enforce tool access at execution time (belt-and-suspenders).
|
|
1720
|
-
async
|
|
1867
|
+
async applyFilters(filters, tool, args, ctx, meta, execute) {
|
|
1868
|
+
const matching = filters.filter(
|
|
1869
|
+
(f) => (!f.toolName || (Array.isArray(f.toolName) ? f.toolName.includes(meta.toolName) : f.toolName === meta.toolName)) && (!f.toolset || tool.toolset === f.toolset)
|
|
1870
|
+
);
|
|
1871
|
+
let shortCircuit = null;
|
|
1872
|
+
for (const f of matching) {
|
|
1873
|
+
if (f.before) {
|
|
1874
|
+
const r = await f.before(args, ctx, meta);
|
|
1875
|
+
if (r !== null) {
|
|
1876
|
+
shortCircuit = r;
|
|
1877
|
+
break;
|
|
1878
|
+
}
|
|
1879
|
+
}
|
|
1880
|
+
}
|
|
1881
|
+
let result = shortCircuit ?? await execute();
|
|
1882
|
+
for (const f of matching) {
|
|
1883
|
+
if (f.after) result = await f.after(result, ctx, meta);
|
|
1884
|
+
}
|
|
1885
|
+
return result;
|
|
1886
|
+
}
|
|
1887
|
+
async executeParallel(calls, ctx, allowedTools, filterOpts, turnAttachments, filters) {
|
|
1721
1888
|
const perCallBudget = Math.floor(ctx.resultBudgetChars / Math.max(calls.length, 1));
|
|
1889
|
+
this.turnLiveCtx = {
|
|
1890
|
+
emit: ctx.emit,
|
|
1891
|
+
readMtimes: ctx.readMtimes,
|
|
1892
|
+
storage: ctx.storage,
|
|
1893
|
+
inboundAttachments: turnAttachments
|
|
1894
|
+
};
|
|
1722
1895
|
const results = await Promise.allSettled(
|
|
1723
1896
|
calls.map(async (call) => {
|
|
1724
1897
|
const entry = this.tools.get(call.name);
|
|
@@ -1767,6 +1940,10 @@ var DefaultToolRegistry = class {
|
|
|
1767
1940
|
}
|
|
1768
1941
|
};
|
|
1769
1942
|
}
|
|
1943
|
+
const cached = this.cacheGet(entry.tool, call.args, ctx);
|
|
1944
|
+
if (cached) {
|
|
1945
|
+
return { toolCallId: call.toolCallId, name: call.name, result: cached };
|
|
1946
|
+
}
|
|
1770
1947
|
if (needsBackends(entry.tool.capabilities) && !this.backends) {
|
|
1771
1948
|
return {
|
|
1772
1949
|
toolCallId: call.toolCallId,
|
|
@@ -1786,32 +1963,49 @@ var DefaultToolRegistry = class {
|
|
|
1786
1963
|
result: synthesizeDryRunResult2(call.name, call.args)
|
|
1787
1964
|
};
|
|
1788
1965
|
}
|
|
1789
|
-
const
|
|
1790
|
-
const toolCtx = { ...ctx, resultBudgetChars: budget };
|
|
1966
|
+
const cappedBudget = Math.min(perCallBudget, entry.tool.maxResultChars ?? perCallBudget);
|
|
1791
1967
|
try {
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1968
|
+
const request = {
|
|
1969
|
+
toolCallId: call.toolCallId,
|
|
1970
|
+
name: call.name,
|
|
1971
|
+
args: call.args,
|
|
1972
|
+
sessionId: ctx.sessionId,
|
|
1973
|
+
sessionKey: ctx.sessionKey,
|
|
1974
|
+
platform: ctx.platform,
|
|
1975
|
+
workingDir: ctx.workingDir,
|
|
1976
|
+
personalityId: ctx.personalityId,
|
|
1977
|
+
teamId: ctx.teamId,
|
|
1978
|
+
agentId: ctx.agentId,
|
|
1979
|
+
memoryScopeId: ctx.memoryScopeId,
|
|
1980
|
+
userScopeId: ctx.userScopeId,
|
|
1981
|
+
currentTurn: ctx.currentTurn,
|
|
1982
|
+
messageCount: ctx.messageCount,
|
|
1983
|
+
resultBudgetChars: cappedBudget,
|
|
1984
|
+
networkPolicy: ctx.networkPolicy,
|
|
1985
|
+
dryRun: ctx.dryRun
|
|
1986
|
+
};
|
|
1987
|
+
const rawResult = filters && filters.length > 0 ? await this.applyFilters(
|
|
1988
|
+
filters,
|
|
1989
|
+
entry.tool,
|
|
1990
|
+
call.args,
|
|
1991
|
+
ctx,
|
|
1992
|
+
{ toolName: call.name, toolCallId: call.toolCallId },
|
|
1993
|
+
() => this.transport.execute(request, ctx.abortSignal)
|
|
1994
|
+
) : await this.transport.execute(request, ctx.abortSignal);
|
|
1802
1995
|
const reducer = this.reducers?.get(call.name);
|
|
1803
1996
|
const result = reducer ? safeReduce(reducer, rawResult, { args: call.args, turnCount: ctx.currentTurn ?? 0 }) : rawResult;
|
|
1804
|
-
if (result.ok && result.value.length >
|
|
1997
|
+
if (result.ok && result.value.length > cappedBudget) {
|
|
1805
1998
|
return {
|
|
1806
1999
|
toolCallId: call.toolCallId,
|
|
1807
2000
|
name: call.name,
|
|
1808
2001
|
result: {
|
|
1809
2002
|
ok: true,
|
|
1810
|
-
value: `${result.value.slice(0,
|
|
2003
|
+
value: `${result.value.slice(0, cappedBudget)}
|
|
1811
2004
|
[truncated \u2014 ${result.value.length} chars total]`
|
|
1812
2005
|
}
|
|
1813
2006
|
};
|
|
1814
2007
|
}
|
|
2008
|
+
this.cacheSet(entry.tool, call.args, result, ctx);
|
|
1815
2009
|
return { toolCallId: call.toolCallId, name: call.name, result };
|
|
1816
2010
|
} catch (err) {
|
|
1817
2011
|
return {
|
|
@@ -1854,7 +2048,12 @@ var KNOWN_AGENT_EVENT_TYPES = [
|
|
|
1854
2048
|
"done",
|
|
1855
2049
|
"context_meta",
|
|
1856
2050
|
"run_start",
|
|
1857
|
-
"dry_run_summary"
|
|
2051
|
+
"dry_run_summary",
|
|
2052
|
+
"tool_approval_required",
|
|
2053
|
+
"tool_approval_response",
|
|
2054
|
+
"evaluators_complete",
|
|
2055
|
+
"credential_required",
|
|
2056
|
+
"notification_received"
|
|
1858
2057
|
];
|
|
1859
2058
|
function isKnownAgentEvent(event) {
|
|
1860
2059
|
return KNOWN_AGENT_EVENT_TYPES.includes(event.type);
|
|
@@ -1896,10 +2095,16 @@ var AgentLoop = class {
|
|
|
1896
2095
|
teamId;
|
|
1897
2096
|
/** Per-personality MCP tool policy from mcp.yaml (NOT on PersonalityConfig). */
|
|
1898
2097
|
mcpPolicy;
|
|
2098
|
+
/** v2.2 — Callback to emit per-tool invocation metrics to the diagnostic store. */
|
|
2099
|
+
onToolMetric;
|
|
2100
|
+
/** v2.2 — Pre-turn credential check callback. */
|
|
2101
|
+
credentialCheck;
|
|
1899
2102
|
/** Per-session accumulated spend in USD. Keyed by sessionKey. Reset via resetSessionCost(). */
|
|
1900
2103
|
sessionCosts = /* @__PURE__ */ new Map();
|
|
1901
2104
|
/** FW-28 — per-session mtime registry. Keyed by sessionKey → (absPath → record). */
|
|
1902
2105
|
sessionReadMtimes = /* @__PURE__ */ new Map();
|
|
2106
|
+
/** v2: per-run key/value store threaded into ToolContext for plugin communication. */
|
|
2107
|
+
contextStore = new ContextStore();
|
|
1903
2108
|
constructor(config) {
|
|
1904
2109
|
this.llm = config.llm;
|
|
1905
2110
|
this.tools = config.tools ?? new DefaultToolRegistry();
|
|
@@ -1928,6 +2133,8 @@ var AgentLoop = class {
|
|
|
1928
2133
|
if (config.clarifyBridge) this.clarifyBridge = config.clarifyBridge;
|
|
1929
2134
|
if (config.requestDumpStore) this.requestDumpStore = config.requestDumpStore;
|
|
1930
2135
|
if (config.mcpPolicy) this.mcpPolicy = config.mcpPolicy;
|
|
2136
|
+
if (config.onToolMetric) this.onToolMetric = config.onToolMetric;
|
|
2137
|
+
if (config.credentialCheck) this.credentialCheck = config.credentialCheck;
|
|
1931
2138
|
this.contextEngines = config.contextEngines ?? new DefaultContextEngineRegistry();
|
|
1932
2139
|
}
|
|
1933
2140
|
/**
|
|
@@ -2061,6 +2268,26 @@ var AgentLoop = class {
|
|
|
2061
2268
|
},
|
|
2062
2269
|
allowedPlugins
|
|
2063
2270
|
);
|
|
2271
|
+
if (this.credentialCheck) {
|
|
2272
|
+
const missing = await this.credentialCheck(sessionKey, text);
|
|
2273
|
+
if (missing) {
|
|
2274
|
+
if (traceId) this.observability?.endTrace(traceId, "error");
|
|
2275
|
+
this.observability?.flush();
|
|
2276
|
+
yield {
|
|
2277
|
+
type: "credential_required",
|
|
2278
|
+
pluginId: missing.pluginId,
|
|
2279
|
+
credentialKey: missing.credentialKey,
|
|
2280
|
+
kind: missing.kind,
|
|
2281
|
+
label: missing.label,
|
|
2282
|
+
description: missing.description,
|
|
2283
|
+
authUrl: missing.authUrl,
|
|
2284
|
+
sessionKey,
|
|
2285
|
+
pendingUserMessage: text
|
|
2286
|
+
};
|
|
2287
|
+
yield { type: "done", text: "", turnCount: 0 };
|
|
2288
|
+
return;
|
|
2289
|
+
}
|
|
2290
|
+
}
|
|
2064
2291
|
const attachmentAnnotation = buildAttachmentAnnotation(opts.attachments ?? []);
|
|
2065
2292
|
const annotatedText = attachmentAnnotation ? `${attachmentAnnotation}
|
|
2066
2293
|
${text}` : text;
|
|
@@ -2154,6 +2381,8 @@ ${text}` : text;
|
|
|
2154
2381
|
if (promptCtx.meta && Object.keys(promptCtx.meta).length > 0) {
|
|
2155
2382
|
yield { type: "context_meta", data: promptCtx.meta };
|
|
2156
2383
|
}
|
|
2384
|
+
const rawSkills = promptCtx.meta?.skillFilesUsed;
|
|
2385
|
+
const activeSkillFiles = Array.isArray(rawSkills) && rawSkills.every((s) => typeof s === "string") ? rawSkills : void 0;
|
|
2157
2386
|
if (memSnapshot && memSnapshot.entries.length > 0) {
|
|
2158
2387
|
const blocks = [];
|
|
2159
2388
|
const orderHints = {
|
|
@@ -2516,6 +2745,7 @@ ${rendered.slice(-MEMORY_MAX_CHARS)}`;
|
|
|
2516
2745
|
sessionMtimes = /* @__PURE__ */ new Map();
|
|
2517
2746
|
this.sessionReadMtimes.set(sessionKey, sessionMtimes);
|
|
2518
2747
|
}
|
|
2748
|
+
this.contextStore.clear();
|
|
2519
2749
|
const toolCtxBase = {
|
|
2520
2750
|
sessionId,
|
|
2521
2751
|
sessionKey,
|
|
@@ -2541,7 +2771,12 @@ ${rendered.slice(-MEMORY_MAX_CHARS)}`;
|
|
|
2541
2771
|
resultBudgetChars: this.resultBudgetChars,
|
|
2542
2772
|
readMtimes: sessionMtimes,
|
|
2543
2773
|
...scopedStorage ? { storage: scopedStorage } : {},
|
|
2544
|
-
...personality.safety?.network ? { networkPolicy: personality.safety.network } : {}
|
|
2774
|
+
...personality.safety?.network ? { networkPolicy: personality.safety.network } : {},
|
|
2775
|
+
...this.contextStore.asContextMethods(),
|
|
2776
|
+
llm: new SimpleCompletionImpl(this.llm, effectiveModel, ({ input, output }) => {
|
|
2777
|
+
llmInputTokens += input;
|
|
2778
|
+
llmOutputTokens += output;
|
|
2779
|
+
})
|
|
2545
2780
|
};
|
|
2546
2781
|
const prepped = [];
|
|
2547
2782
|
const spanIds = /* @__PURE__ */ new Map();
|
|
@@ -2659,6 +2894,15 @@ ${rendered.slice(-MEMORY_MAX_CHARS)}`;
|
|
|
2659
2894
|
obsConfig
|
|
2660
2895
|
});
|
|
2661
2896
|
spanIds.set(tc.toolCallId, spanId ?? "");
|
|
2897
|
+
const reqApprovalTool = this.tools.get(tc.toolName);
|
|
2898
|
+
if (reqApprovalTool?.requiresApproval) {
|
|
2899
|
+
yield {
|
|
2900
|
+
type: "tool_approval_required",
|
|
2901
|
+
toolCallId: tc.toolCallId,
|
|
2902
|
+
toolName: tc.toolName,
|
|
2903
|
+
args: effectiveArgs
|
|
2904
|
+
};
|
|
2905
|
+
}
|
|
2662
2906
|
observe({ type: "tool_start", toolName: tc.toolName, args: effectiveArgs });
|
|
2663
2907
|
yield {
|
|
2664
2908
|
type: "tool_start",
|
|
@@ -2686,6 +2930,42 @@ ${rendered.slice(-MEMORY_MAX_CHARS)}`;
|
|
|
2686
2930
|
opts.attachments
|
|
2687
2931
|
) : [];
|
|
2688
2932
|
const execResultMap = new Map(execResults.map((r) => [r.toolCallId, r]));
|
|
2933
|
+
const directResult = execResults.find((r) => {
|
|
2934
|
+
const t = this.tools.get(r.name);
|
|
2935
|
+
return r.result.ok && t?.returnDirect;
|
|
2936
|
+
});
|
|
2937
|
+
if (directResult?.result.ok) {
|
|
2938
|
+
for (const p of prepped) {
|
|
2939
|
+
const execResult = execResultMap.get(p.toolCallId);
|
|
2940
|
+
const result = p.rejected ? { ok: false, error: p.rejected, code: "execution_failed" } : execResult?.result ?? {
|
|
2941
|
+
ok: false,
|
|
2942
|
+
error: "Tool result missing",
|
|
2943
|
+
code: "execution_failed"
|
|
2944
|
+
};
|
|
2945
|
+
await this.session.appendMessage({
|
|
2946
|
+
sessionId,
|
|
2947
|
+
role: "tool_result",
|
|
2948
|
+
content: result.ok ? result.value : result.error,
|
|
2949
|
+
toolCallId: p.toolCallId,
|
|
2950
|
+
toolName: p.name
|
|
2951
|
+
});
|
|
2952
|
+
}
|
|
2953
|
+
for (const r of execResults) {
|
|
2954
|
+
yield {
|
|
2955
|
+
type: "tool_end",
|
|
2956
|
+
toolCallId: r.toolCallId,
|
|
2957
|
+
toolName: r.name,
|
|
2958
|
+
ok: r.result.ok,
|
|
2959
|
+
durationMs: Date.now() - startedAt,
|
|
2960
|
+
result: r.result.ok ? r.result.value : r.result.error
|
|
2961
|
+
};
|
|
2962
|
+
}
|
|
2963
|
+
fullText = directResult.result.value;
|
|
2964
|
+
if (traceId) this.observability?.endTrace(traceId, "ok");
|
|
2965
|
+
this.observability?.flush();
|
|
2966
|
+
yield { type: "done", text: fullText, turnCount };
|
|
2967
|
+
return;
|
|
2968
|
+
}
|
|
2689
2969
|
if (opts.dryRun) {
|
|
2690
2970
|
for (const input of execInputs) {
|
|
2691
2971
|
dryRunPlan.push({
|
|
@@ -2768,6 +3048,19 @@ ${rendered.slice(-MEMORY_MAX_CHARS)}`;
|
|
|
2768
3048
|
},
|
|
2769
3049
|
allowedPlugins
|
|
2770
3050
|
);
|
|
3051
|
+
if (this.onToolMetric) {
|
|
3052
|
+
const pluginId = this.tools.getPluginId?.(p.name);
|
|
3053
|
+
if (pluginId) {
|
|
3054
|
+
this.onToolMetric({
|
|
3055
|
+
pluginId,
|
|
3056
|
+
toolName: p.name,
|
|
3057
|
+
ok: result.ok,
|
|
3058
|
+
durationMs,
|
|
3059
|
+
sessionId,
|
|
3060
|
+
turnId: String(turnCount)
|
|
3061
|
+
});
|
|
3062
|
+
}
|
|
3063
|
+
}
|
|
2771
3064
|
const touchedPath = extractFilePath(p.args);
|
|
2772
3065
|
if (touchedPath !== void 0) {
|
|
2773
3066
|
await this.hooks.fireVoid(
|
|
@@ -2853,7 +3146,8 @@ ${rendered.slice(-MEMORY_MAX_CHARS)}`;
|
|
|
2853
3146
|
successfulToolCalls,
|
|
2854
3147
|
totalToolCalls,
|
|
2855
3148
|
toolNames: [...toolNameCounts.keys()],
|
|
2856
|
-
initialPrompt: text
|
|
3149
|
+
initialPrompt: text,
|
|
3150
|
+
activeSkillFiles
|
|
2857
3151
|
},
|
|
2858
3152
|
allowedPlugins
|
|
2859
3153
|
);
|
|
@@ -3658,6 +3952,27 @@ var LastWriteWinsPolicy = class {
|
|
|
3658
3952
|
}
|
|
3659
3953
|
};
|
|
3660
3954
|
|
|
3955
|
+
// src/notification-router.ts
|
|
3956
|
+
var DefaultNotificationRouter = class {
|
|
3957
|
+
adapters = /* @__PURE__ */ new Map();
|
|
3958
|
+
async route(pluginId, opts) {
|
|
3959
|
+
if (opts.sessionKey === "*") return;
|
|
3960
|
+
const adapter = this.adapters.get(opts.sessionKey);
|
|
3961
|
+
if (!adapter) return;
|
|
3962
|
+
if (opts.startTurn) {
|
|
3963
|
+
await adapter.injectUserMessage(opts.message);
|
|
3964
|
+
} else {
|
|
3965
|
+
await adapter.send(opts.message, opts.payload);
|
|
3966
|
+
}
|
|
3967
|
+
}
|
|
3968
|
+
register(sessionKey, adapter) {
|
|
3969
|
+
this.adapters.set(sessionKey, adapter);
|
|
3970
|
+
}
|
|
3971
|
+
deregister(sessionKey) {
|
|
3972
|
+
this.adapters.delete(sessionKey);
|
|
3973
|
+
}
|
|
3974
|
+
};
|
|
3975
|
+
|
|
3661
3976
|
// src/path-boundary.ts
|
|
3662
3977
|
import { resolve as resolve5, sep as sep2 } from "path";
|
|
3663
3978
|
function assertWithinBase(base, target) {
|
|
@@ -4056,10 +4371,12 @@ export {
|
|
|
4056
4371
|
ClarifyBusyError,
|
|
4057
4372
|
ClarifyNoSurfaceError,
|
|
4058
4373
|
ClarifyTimedOutNoDefaultError,
|
|
4374
|
+
ContextStore,
|
|
4059
4375
|
DefaultContextEngineRegistry,
|
|
4060
4376
|
DefaultHookRegistry,
|
|
4061
4377
|
DefaultLLMProviderRegistry,
|
|
4062
4378
|
DefaultMemoryProviderRegistry,
|
|
4379
|
+
DefaultNotificationRouter,
|
|
4063
4380
|
DefaultPersonalityRegistry,
|
|
4064
4381
|
DefaultToolRegistry,
|
|
4065
4382
|
DefaultToolResultReducerRegistry,
|
|
@@ -4071,6 +4388,7 @@ export {
|
|
|
4071
4388
|
KNOWN_AGENT_EVENT_TYPES,
|
|
4072
4389
|
LastWriteWinsPolicy,
|
|
4073
4390
|
LazyOnDemandPolicy,
|
|
4391
|
+
LocalToolTransport,
|
|
4074
4392
|
MemoryConflictError2 as MemoryConflictError,
|
|
4075
4393
|
NoopMemoryProvider,
|
|
4076
4394
|
PluginRegistry,
|
|
@@ -4080,6 +4398,7 @@ export {
|
|
|
4080
4398
|
ScopedProcessImpl,
|
|
4081
4399
|
ScopedSecretsImpl,
|
|
4082
4400
|
SemanticSummaryEngine,
|
|
4401
|
+
SimpleCompletionImpl,
|
|
4083
4402
|
SsrfError,
|
|
4084
4403
|
applyTemporalDecay,
|
|
4085
4404
|
assertWithinBase,
|