@exulu/backend 2.0.1 → 2.1.0
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/{chunk-IJ4HNHOT.js → chunk-RVZWZNWG.js} +568 -274
- package/dist/{convert-exulu-tools-to-ai-sdk-tools-2PEDFZ2X.js → convert-exulu-tools-to-ai-sdk-tools-K3RHHLN6.js} +1 -1
- package/dist/index.cjs +1206 -438
- package/dist/index.d.cts +37 -2
- package/dist/index.d.ts +37 -2
- package/dist/index.js +653 -212
- package/ee/agentic-retrieval/pipeline/config.test.ts +15 -0
- package/ee/agentic-retrieval/pipeline/config.ts +6 -0
- package/ee/agentic-retrieval/pipeline/global-ids.ts +30 -0
- package/ee/agentic-retrieval/pipeline/index.test.ts +86 -1
- package/ee/agentic-retrieval/pipeline/index.ts +96 -46
- package/ee/agentic-retrieval/pipeline/project-scope.test.ts +73 -0
- package/ee/agentic-retrieval/pipeline/project-scope.ts +77 -0
- package/ee/agentic-retrieval/pipeline/search.test.ts +27 -0
- package/ee/agentic-retrieval/pipeline/search.ts +7 -0
- package/package.json +1 -1
|
@@ -1691,7 +1691,7 @@ var ExuluTool = class _ExuluTool {
|
|
|
1691
1691
|
});
|
|
1692
1692
|
providerapikey = resolved.apiKey;
|
|
1693
1693
|
}
|
|
1694
|
-
const { convertExuluToolsToAiSdkTools: convertExuluToolsToAiSdkTools2 } = await import("./convert-exulu-tools-to-ai-sdk-tools-
|
|
1694
|
+
const { convertExuluToolsToAiSdkTools: convertExuluToolsToAiSdkTools2 } = await import("./convert-exulu-tools-to-ai-sdk-tools-K3RHHLN6.js");
|
|
1695
1695
|
const tools = await convertExuluToolsToAiSdkTools2(
|
|
1696
1696
|
[this],
|
|
1697
1697
|
[],
|
|
@@ -1774,111 +1774,8 @@ var updateStatistic = async (statistic) => {
|
|
|
1774
1774
|
// src/templates/tools/convert-exulu-tools-to-ai-sdk-tools.ts
|
|
1775
1775
|
import CryptoJS5 from "crypto-js";
|
|
1776
1776
|
|
|
1777
|
-
// src/templates/tools/project-retrieval-tool.ts
|
|
1778
|
-
import { z as z2 } from "zod";
|
|
1779
|
-
var createProjectItemsRetrievalTool = async ({
|
|
1780
|
-
user,
|
|
1781
|
-
role,
|
|
1782
|
-
contexts,
|
|
1783
|
-
projectId
|
|
1784
|
-
}) => {
|
|
1785
|
-
let project;
|
|
1786
|
-
const { db: db2 } = await postgresClient();
|
|
1787
|
-
project = await db2.from("projects").where("id", projectId).first();
|
|
1788
|
-
if (!project) {
|
|
1789
|
-
return;
|
|
1790
|
-
}
|
|
1791
|
-
console.log("[EXULU] Project search tool created for project", project);
|
|
1792
|
-
if (!project.project_items?.length) {
|
|
1793
|
-
return;
|
|
1794
|
-
}
|
|
1795
|
-
const projectRetrievalTool = ExuluTool.internal({
|
|
1796
|
-
id: "context_search_in_knowledge_items_added_to_project_" + projectId,
|
|
1797
|
-
name: "context_search in knowledge items added to project " + project.name,
|
|
1798
|
-
description: "This tool retrieves information about a project from conversations and items that were added to the project " + project.name + ".",
|
|
1799
|
-
inputSchema: z2.object({
|
|
1800
|
-
query: z2.string().describe("The query to retrieve information about the project " + project.name + "."),
|
|
1801
|
-
keywords: z2.array(z2.string()).describe(
|
|
1802
|
-
"The most relevant keywords in the query, such as names of people, companies, products, etc. in the project " + project.name + "."
|
|
1803
|
-
)
|
|
1804
|
-
}),
|
|
1805
|
-
type: "context",
|
|
1806
|
-
category: "project",
|
|
1807
|
-
config: [],
|
|
1808
|
-
execute: async ({ query }) => {
|
|
1809
|
-
console.log("[EXULU] Project search tool searching for project", project);
|
|
1810
|
-
const items = project.project_items;
|
|
1811
|
-
const set = {};
|
|
1812
|
-
for (const item of items) {
|
|
1813
|
-
const context = item.split("/")[0];
|
|
1814
|
-
if (!context) {
|
|
1815
|
-
throw new Error(
|
|
1816
|
-
"The item added to the project does not have a valid gid with the context id as the prefix before the first slash."
|
|
1817
|
-
);
|
|
1818
|
-
}
|
|
1819
|
-
const id = item.split("/").slice(1).join("/");
|
|
1820
|
-
if (set[context]) {
|
|
1821
|
-
set[context].push(id);
|
|
1822
|
-
} else {
|
|
1823
|
-
set[context] = [id];
|
|
1824
|
-
}
|
|
1825
|
-
}
|
|
1826
|
-
console.log("[EXULU] Project search tool searching through contexts", Object.keys(set));
|
|
1827
|
-
const results = await Promise.all(
|
|
1828
|
-
Object.keys(set).map(async (contextName) => {
|
|
1829
|
-
const context = contexts.find((context2) => context2.id === contextName);
|
|
1830
|
-
if (!context) {
|
|
1831
|
-
console.error(
|
|
1832
|
-
"[EXULU] Context not found for project information retrieval tool.",
|
|
1833
|
-
contextName
|
|
1834
|
-
);
|
|
1835
|
-
return [];
|
|
1836
|
-
}
|
|
1837
|
-
const itemIds = set[contextName];
|
|
1838
|
-
console.log("[EXULU] Project search tool searching through items", itemIds);
|
|
1839
|
-
const result = await context.search({
|
|
1840
|
-
query,
|
|
1841
|
-
itemFilters: [
|
|
1842
|
-
{
|
|
1843
|
-
id: {
|
|
1844
|
-
in: itemIds
|
|
1845
|
-
}
|
|
1846
|
-
}
|
|
1847
|
-
],
|
|
1848
|
-
chunkFilters: [],
|
|
1849
|
-
user,
|
|
1850
|
-
role,
|
|
1851
|
-
method: "hybridSearch",
|
|
1852
|
-
sort: {
|
|
1853
|
-
field: "updatedAt",
|
|
1854
|
-
direction: "desc"
|
|
1855
|
-
},
|
|
1856
|
-
trigger: "tool",
|
|
1857
|
-
limit: 10,
|
|
1858
|
-
page: 1
|
|
1859
|
-
});
|
|
1860
|
-
return {
|
|
1861
|
-
result: result.chunks.map((chunk) => ({
|
|
1862
|
-
...chunk,
|
|
1863
|
-
context: {
|
|
1864
|
-
name: context.name,
|
|
1865
|
-
id: context.id
|
|
1866
|
-
}
|
|
1867
|
-
}))
|
|
1868
|
-
};
|
|
1869
|
-
})
|
|
1870
|
-
);
|
|
1871
|
-
console.log("[EXULU] Project search tool results", results);
|
|
1872
|
-
return {
|
|
1873
|
-
result: JSON.stringify(results.flat())
|
|
1874
|
-
};
|
|
1875
|
-
}
|
|
1876
|
-
});
|
|
1877
|
-
return projectRetrievalTool;
|
|
1878
|
-
};
|
|
1879
|
-
|
|
1880
1777
|
// src/templates/tools/session-items-retrieval-tool.ts
|
|
1881
|
-
import { z as
|
|
1778
|
+
import { z as z2 } from "zod";
|
|
1882
1779
|
var createSessionItemsRetrievalTool = async ({
|
|
1883
1780
|
user,
|
|
1884
1781
|
role,
|
|
@@ -1890,8 +1787,8 @@ var createSessionItemsRetrievalTool = async ({
|
|
|
1890
1787
|
id: "session_items_information_context_search",
|
|
1891
1788
|
name: "context_search in knowledge items added to session.",
|
|
1892
1789
|
description: "Context search in knowledge items added to session.",
|
|
1893
|
-
inputSchema:
|
|
1894
|
-
query:
|
|
1790
|
+
inputSchema: z2.object({
|
|
1791
|
+
query: z2.string().describe("The query to retrieve information from knowledge items added to the session.")
|
|
1895
1792
|
}),
|
|
1896
1793
|
type: "context",
|
|
1897
1794
|
category: "session",
|
|
@@ -1962,7 +1859,7 @@ var createSessionItemsRetrievalTool = async ({
|
|
|
1962
1859
|
};
|
|
1963
1860
|
|
|
1964
1861
|
// ee/agentic-retrieval/pipeline/index.ts
|
|
1965
|
-
import { z as
|
|
1862
|
+
import { z as z7 } from "zod";
|
|
1966
1863
|
|
|
1967
1864
|
// ee/entitlements.ts
|
|
1968
1865
|
var ENTITLEMENTS = {
|
|
@@ -2106,57 +2003,57 @@ async function resolveReranker(input) {
|
|
|
2106
2003
|
}
|
|
2107
2004
|
|
|
2108
2005
|
// ee/agentic-retrieval/pipeline/config.ts
|
|
2109
|
-
import { z as
|
|
2006
|
+
import { z as z3 } from "zod";
|
|
2110
2007
|
var KB_KINDS = ["documents", "conversations", "records"];
|
|
2111
2008
|
var DEFAULT_PREFILTER_CUTOFF = 2.5;
|
|
2112
2009
|
var RRF_K = 60;
|
|
2113
2010
|
var CHUNK_GROUP_MAX = 10;
|
|
2114
|
-
var kbProfileSchema =
|
|
2115
|
-
enabled:
|
|
2116
|
-
kind:
|
|
2117
|
-
instructions:
|
|
2118
|
-
overrides:
|
|
2119
|
-
limit:
|
|
2120
|
-
expand:
|
|
2121
|
-
multiQuery:
|
|
2122
|
-
hyde:
|
|
2011
|
+
var kbProfileSchema = z3.object({
|
|
2012
|
+
enabled: z3.boolean().default(true),
|
|
2013
|
+
kind: z3.enum(KB_KINDS).default("documents"),
|
|
2014
|
+
instructions: z3.string().default(""),
|
|
2015
|
+
overrides: z3.object({
|
|
2016
|
+
limit: z3.number().int().positive().optional(),
|
|
2017
|
+
expand: z3.number().int().min(0).optional(),
|
|
2018
|
+
multiQuery: z3.boolean().optional(),
|
|
2019
|
+
hyde: z3.boolean().optional()
|
|
2123
2020
|
}).default({})
|
|
2124
2021
|
});
|
|
2125
|
-
var knowledgeBasesSchema =
|
|
2126
|
-
var routingRuleSchema =
|
|
2127
|
-
id:
|
|
2128
|
-
label:
|
|
2129
|
-
description:
|
|
2130
|
-
main:
|
|
2131
|
-
fallback:
|
|
2022
|
+
var knowledgeBasesSchema = z3.record(z3.string(), kbProfileSchema);
|
|
2023
|
+
var routingRuleSchema = z3.object({
|
|
2024
|
+
id: z3.string(),
|
|
2025
|
+
label: z3.string(),
|
|
2026
|
+
description: z3.string(),
|
|
2027
|
+
main: z3.array(z3.string()),
|
|
2028
|
+
fallback: z3.array(z3.string()).default([])
|
|
2132
2029
|
});
|
|
2133
|
-
var routingSchema =
|
|
2134
|
-
var identifierSetSchema =
|
|
2135
|
-
name:
|
|
2136
|
-
description:
|
|
2137
|
-
examples:
|
|
2138
|
-
strategy:
|
|
2139
|
-
contexts:
|
|
2030
|
+
var routingSchema = z3.object({ rules: z3.array(routingRuleSchema).default([]) });
|
|
2031
|
+
var identifierSetSchema = z3.object({
|
|
2032
|
+
name: z3.string(),
|
|
2033
|
+
description: z3.string().default(""),
|
|
2034
|
+
examples: z3.array(z3.string()).default([]),
|
|
2035
|
+
strategy: z3.enum(["fuzzy", "exact"]),
|
|
2036
|
+
contexts: z3.array(z3.string()).default([])
|
|
2140
2037
|
});
|
|
2141
|
-
var vocabularySchema =
|
|
2142
|
-
glossary:
|
|
2143
|
-
identifiers:
|
|
2144
|
-
rewrites:
|
|
2145
|
-
styleHint:
|
|
2038
|
+
var vocabularySchema = z3.object({
|
|
2039
|
+
glossary: z3.array(z3.object({ term: z3.string(), meaning: z3.string() })).default([]),
|
|
2040
|
+
identifiers: z3.array(identifierSetSchema).default([]),
|
|
2041
|
+
rewrites: z3.array(z3.object({ find: z3.string(), replace: z3.string() })).default([]),
|
|
2042
|
+
styleHint: z3.string().default("")
|
|
2146
2043
|
});
|
|
2147
|
-
var memorySchema =
|
|
2148
|
-
enabled:
|
|
2149
|
-
override:
|
|
2150
|
-
filePrioritization:
|
|
2151
|
-
queryAugmentation:
|
|
2044
|
+
var memorySchema = z3.object({
|
|
2045
|
+
enabled: z3.boolean().default(true),
|
|
2046
|
+
override: z3.boolean().default(false),
|
|
2047
|
+
filePrioritization: z3.boolean().default(false),
|
|
2048
|
+
queryAugmentation: z3.boolean().default(true)
|
|
2152
2049
|
});
|
|
2153
|
-
var tuningSchema =
|
|
2154
|
-
topK:
|
|
2155
|
-
fallbackThreshold:
|
|
2156
|
-
pinBoost:
|
|
2157
|
-
identifierBoost:
|
|
2158
|
-
pageWindow:
|
|
2159
|
-
maxQueriesPerContext:
|
|
2050
|
+
var tuningSchema = z3.object({
|
|
2051
|
+
topK: z3.number().int().positive().default(5),
|
|
2052
|
+
fallbackThreshold: z3.number().min(0).max(1).default(0.95),
|
|
2053
|
+
pinBoost: z3.number().min(0).max(1).default(0.15),
|
|
2054
|
+
identifierBoost: z3.number().min(0).max(1).default(0.15),
|
|
2055
|
+
pageWindow: z3.number().int().min(0).default(1),
|
|
2056
|
+
maxQueriesPerContext: z3.number().int().positive().default(5)
|
|
2160
2057
|
});
|
|
2161
2058
|
var boolVal = (v) => v === true || v === "true" || v === 1;
|
|
2162
2059
|
var strVal = (v, fallback) => typeof v === "string" && v.length > 0 ? v : fallback;
|
|
@@ -2212,6 +2109,7 @@ function parsePipelineConfig(raw) {
|
|
|
2212
2109
|
managedContext: boolVal(r["managed_context"]),
|
|
2213
2110
|
requirePreselectedContexts: boolVal(r["require_preselected_contexts"]),
|
|
2214
2111
|
logging: boolVal(r["logging"]),
|
|
2112
|
+
projectSearch: r["project_search"] === void 0 || r["project_search"] === "" ? true : boolVal(r["project_search"]),
|
|
2215
2113
|
utilityModel: strVal(r["utility_model"], ""),
|
|
2216
2114
|
knowledgeBases: jsonVal("knowledge_bases", knowledgeBasesSchema, r["knowledge_bases"]),
|
|
2217
2115
|
routing: jsonVal("routing", routingSchema, r["routing"]),
|
|
@@ -2244,9 +2142,69 @@ function effectiveKbSettings(profile, ctx) {
|
|
|
2244
2142
|
};
|
|
2245
2143
|
}
|
|
2246
2144
|
|
|
2145
|
+
// ee/agentic-retrieval/pipeline/global-ids.ts
|
|
2146
|
+
function parsePreselectedItems(globalIds) {
|
|
2147
|
+
const map = /* @__PURE__ */ new Map();
|
|
2148
|
+
for (const gid of globalIds) {
|
|
2149
|
+
const slashIdx = gid.indexOf("/");
|
|
2150
|
+
if (slashIdx === -1) {
|
|
2151
|
+
if (gid) map.set(gid, null);
|
|
2152
|
+
continue;
|
|
2153
|
+
}
|
|
2154
|
+
const contextId = gid.slice(0, slashIdx);
|
|
2155
|
+
const itemId = gid.slice(slashIdx + 1);
|
|
2156
|
+
if (!contextId || !itemId) continue;
|
|
2157
|
+
if (map.get(contextId) === null) continue;
|
|
2158
|
+
const existing = map.get(contextId) ?? [];
|
|
2159
|
+
existing.push(itemId);
|
|
2160
|
+
map.set(contextId, existing);
|
|
2161
|
+
}
|
|
2162
|
+
return map;
|
|
2163
|
+
}
|
|
2164
|
+
|
|
2165
|
+
// ee/agentic-retrieval/pipeline/project-scope.ts
|
|
2166
|
+
function resolveProjectScope(opts) {
|
|
2167
|
+
const { scope, enabledContextIds, availableContextIds } = opts;
|
|
2168
|
+
if (!scope || scope.items.length === 0) return void 0;
|
|
2169
|
+
const itemsByContext = parsePreselectedItems(scope.items);
|
|
2170
|
+
const pinsByContext = /* @__PURE__ */ new Map();
|
|
2171
|
+
const scopedItemsByContext = /* @__PURE__ */ new Map();
|
|
2172
|
+
const addedContextIds = [];
|
|
2173
|
+
const allProjectContextIds = [];
|
|
2174
|
+
for (const [ctxId, itemIds] of itemsByContext) {
|
|
2175
|
+
if (!availableContextIds.has(ctxId)) {
|
|
2176
|
+
console.warn(
|
|
2177
|
+
`[EXULU pipeline] project "${scope.name}" references unknown context "${ctxId}" \u2014 skipping those items.`
|
|
2178
|
+
);
|
|
2179
|
+
continue;
|
|
2180
|
+
}
|
|
2181
|
+
allProjectContextIds.push(ctxId);
|
|
2182
|
+
if (enabledContextIds.has(ctxId)) {
|
|
2183
|
+
if (itemIds && itemIds.length > 0) pinsByContext.set(ctxId, new Set(itemIds));
|
|
2184
|
+
} else {
|
|
2185
|
+
scopedItemsByContext.set(ctxId, itemIds);
|
|
2186
|
+
addedContextIds.push(ctxId);
|
|
2187
|
+
}
|
|
2188
|
+
}
|
|
2189
|
+
if (allProjectContextIds.length === 0) return void 0;
|
|
2190
|
+
return { pinsByContext, scopedItemsByContext, addedContextIds, allProjectContextIds };
|
|
2191
|
+
}
|
|
2192
|
+
var TRANSCRIPTIONS_CONTEXT_ID = "transcriptions";
|
|
2193
|
+
function buildProjectKbProfileDefaults(items) {
|
|
2194
|
+
const defaults = {};
|
|
2195
|
+
for (const gid of items) {
|
|
2196
|
+
const slashIdx = gid.indexOf("/");
|
|
2197
|
+
const ctxId = slashIdx === -1 ? gid : gid.slice(0, slashIdx);
|
|
2198
|
+
if (ctxId === TRANSCRIPTIONS_CONTEXT_ID && !defaults[ctxId]) {
|
|
2199
|
+
defaults[ctxId] = { enabled: true, kind: "conversations", instructions: "", overrides: {} };
|
|
2200
|
+
}
|
|
2201
|
+
}
|
|
2202
|
+
return defaults;
|
|
2203
|
+
}
|
|
2204
|
+
|
|
2247
2205
|
// ee/agentic-retrieval/pipeline/routing.ts
|
|
2248
2206
|
import { generateText as generateText2, Output as Output2 } from "ai";
|
|
2249
|
-
import { z as
|
|
2207
|
+
import { z as z5 } from "zod";
|
|
2250
2208
|
|
|
2251
2209
|
// src/utils/with-retry.ts
|
|
2252
2210
|
async function withRetry(generateFn, maxRetries = 3) {
|
|
@@ -2269,7 +2227,7 @@ async function withRetry(generateFn, maxRetries = 3) {
|
|
|
2269
2227
|
// ee/agentic-retrieval/pipeline/prefilter.ts
|
|
2270
2228
|
import Fuse from "fuse.js";
|
|
2271
2229
|
import { generateText, Output } from "ai";
|
|
2272
|
-
import { z as
|
|
2230
|
+
import { z as z4 } from "zod";
|
|
2273
2231
|
|
|
2274
2232
|
// ee/agentic-retrieval/pipeline/text-utils.ts
|
|
2275
2233
|
var normalizeFileName = (fileName) => {
|
|
@@ -2514,9 +2472,9 @@ async function resolveIdentifierPins({
|
|
|
2514
2472
|
system: set.strategy === "exact" ? EXACT_EXTRACTION_PROMPT(set) : FUZZY_EXTRACTION_PROMPT(set),
|
|
2515
2473
|
messages: [{ role: "user", content: question }],
|
|
2516
2474
|
output: Output.object({
|
|
2517
|
-
schema:
|
|
2518
|
-
hasMatches:
|
|
2519
|
-
matches:
|
|
2475
|
+
schema: z4.object({
|
|
2476
|
+
hasMatches: z4.boolean(),
|
|
2477
|
+
matches: z4.array(z4.string()).optional()
|
|
2520
2478
|
})
|
|
2521
2479
|
}),
|
|
2522
2480
|
maxOutputTokens: 300
|
|
@@ -2627,11 +2585,11 @@ If explicit, return the knowledge base ids. If not, return an empty array.`;
|
|
|
2627
2585
|
system: buildDocPagePrompt(knownIdentifiers),
|
|
2628
2586
|
messages: [{ role: "user", content: question }],
|
|
2629
2587
|
output: Output2.object({
|
|
2630
|
-
schema:
|
|
2631
|
-
hasFilenameHint:
|
|
2632
|
-
filenameHints:
|
|
2633
|
-
hasPageHint:
|
|
2634
|
-
pageNumber:
|
|
2588
|
+
schema: z5.object({
|
|
2589
|
+
hasFilenameHint: z5.boolean(),
|
|
2590
|
+
filenameHints: z5.array(z5.string()).optional(),
|
|
2591
|
+
hasPageHint: z5.boolean(),
|
|
2592
|
+
pageNumber: z5.number().int().nullable().optional()
|
|
2635
2593
|
})
|
|
2636
2594
|
}),
|
|
2637
2595
|
maxOutputTokens: 300
|
|
@@ -2658,9 +2616,9 @@ If explicit, return the knowledge base ids. If not, return an empty array.`;
|
|
|
2658
2616
|
temperature: 0,
|
|
2659
2617
|
system: kbSystemPrompt,
|
|
2660
2618
|
output: Output2.object({
|
|
2661
|
-
schema:
|
|
2662
|
-
explicitlyRequestedKnowledgeBases:
|
|
2663
|
-
|
|
2619
|
+
schema: z5.object({
|
|
2620
|
+
explicitlyRequestedKnowledgeBases: z5.array(
|
|
2621
|
+
z5.enum(enabledContexts.map((c) => c.id))
|
|
2664
2622
|
)
|
|
2665
2623
|
})
|
|
2666
2624
|
}),
|
|
@@ -2758,9 +2716,9 @@ ${extraInstructions}
|
|
|
2758
2716
|
system: classifyPrompt,
|
|
2759
2717
|
messages: [{ role: "user", content: question }],
|
|
2760
2718
|
output: Output2.object({
|
|
2761
|
-
schema:
|
|
2762
|
-
ruleId:
|
|
2763
|
-
reason:
|
|
2719
|
+
schema: z5.object({
|
|
2720
|
+
ruleId: z5.enum(ruleIds),
|
|
2721
|
+
reason: z5.string()
|
|
2764
2722
|
})
|
|
2765
2723
|
}),
|
|
2766
2724
|
maxOutputTokens: 200
|
|
@@ -2822,7 +2780,7 @@ ${extraInstructions}
|
|
|
2822
2780
|
|
|
2823
2781
|
// ee/agentic-retrieval/pipeline/memory.ts
|
|
2824
2782
|
import { generateText as generateText3, Output as Output3 } from "ai";
|
|
2825
|
-
import { z as
|
|
2783
|
+
import { z as z6 } from "zod";
|
|
2826
2784
|
|
|
2827
2785
|
// ee/agentic-retrieval/pipeline/multi-query.ts
|
|
2828
2786
|
async function singleSearch({
|
|
@@ -3063,8 +3021,8 @@ async function runMemoryPhase({
|
|
|
3063
3021
|
}
|
|
3064
3022
|
],
|
|
3065
3023
|
output: Output3.object({
|
|
3066
|
-
schema:
|
|
3067
|
-
relevantChunkIds:
|
|
3024
|
+
schema: z6.object({
|
|
3025
|
+
relevantChunkIds: z6.array(z6.string()).describe(
|
|
3068
3026
|
"The chunk_ids (UUIDs at the start of each bullet) of chunks containing information relevant to the user's question. Empty array if none are relevant."
|
|
3069
3027
|
)
|
|
3070
3028
|
})
|
|
@@ -3180,17 +3138,17 @@ ${glossary.map((g) => `${g.term} : ${g.meaning}`).join("\n")}` : "";
|
|
|
3180
3138
|
}
|
|
3181
3139
|
],
|
|
3182
3140
|
output: Output3.object({
|
|
3183
|
-
schema:
|
|
3184
|
-
overrides:
|
|
3141
|
+
schema: z6.object({
|
|
3142
|
+
overrides: z6.boolean().describe(
|
|
3185
3143
|
"True ONLY if a memory chunk directly and sufficiently answers the user's question and should be authoritative over the documents. Be strict; when unsure, false."
|
|
3186
3144
|
),
|
|
3187
|
-
confidence:
|
|
3145
|
+
confidence: z6.enum(["high", "medium", "low"]).describe(
|
|
3188
3146
|
"Confidence that the selected memory chunk(s) fully and directly answer the question."
|
|
3189
3147
|
),
|
|
3190
|
-
authoritativeChunkIds:
|
|
3148
|
+
authoritativeChunkIds: z6.array(z6.string()).describe(
|
|
3191
3149
|
"The chunk_ids of the memory chunk(s) that directly answer the question. Empty if overrides is false."
|
|
3192
3150
|
),
|
|
3193
|
-
reason:
|
|
3151
|
+
reason: z6.string().describe(
|
|
3194
3152
|
"One short sentence: why this memory does or does not directly answer the question."
|
|
3195
3153
|
)
|
|
3196
3154
|
})
|
|
@@ -3221,9 +3179,9 @@ ${glossary.map((g) => `${g.term} : ${g.meaning}`).join("\n")}` : "";
|
|
|
3221
3179
|
system: "You are a helpful assistant that will strictly follow the user's instructions.",
|
|
3222
3180
|
messages: [{ role: "user", content: PROMPT_EXTRACT_PRIORITIZED_FILES }],
|
|
3223
3181
|
output: Output3.object({
|
|
3224
|
-
schema:
|
|
3225
|
-
shouldPrioritizeFiles:
|
|
3226
|
-
fileNameHints:
|
|
3182
|
+
schema: z6.object({
|
|
3183
|
+
shouldPrioritizeFiles: z6.boolean(),
|
|
3184
|
+
fileNameHints: z6.array(z6.string()).optional()
|
|
3227
3185
|
})
|
|
3228
3186
|
}),
|
|
3229
3187
|
maxOutputTokens: 300
|
|
@@ -3242,10 +3200,10 @@ ${glossary.map((g) => `${g.term} : ${g.meaning}`).join("\n")}` : "";
|
|
|
3242
3200
|
system: "You are a helpful assistant that will strictly follow the user's instructions.",
|
|
3243
3201
|
messages: [{ role: "user", content: QUERY_AUGMENTATION_PROMPT }],
|
|
3244
3202
|
output: Output3.object({
|
|
3245
|
-
schema:
|
|
3246
|
-
updatedUserQuestion:
|
|
3247
|
-
updatedRelevantKeywords:
|
|
3248
|
-
updatedImportantKeyword:
|
|
3203
|
+
schema: z6.object({
|
|
3204
|
+
updatedUserQuestion: z6.string(),
|
|
3205
|
+
updatedRelevantKeywords: z6.array(z6.string()),
|
|
3206
|
+
updatedImportantKeyword: z6.string()
|
|
3249
3207
|
})
|
|
3250
3208
|
}),
|
|
3251
3209
|
maxOutputTokens: 600
|
|
@@ -3426,6 +3384,7 @@ async function searchContexts(opts) {
|
|
|
3426
3384
|
role,
|
|
3427
3385
|
model,
|
|
3428
3386
|
preselectedItems,
|
|
3387
|
+
scopedItemsByContext,
|
|
3429
3388
|
identifierPinsByContext,
|
|
3430
3389
|
memoryPinnedItemIds,
|
|
3431
3390
|
userPinnedItemIdsByContext,
|
|
@@ -3452,6 +3411,8 @@ async function searchContexts(opts) {
|
|
|
3452
3411
|
let pinnedItemIds;
|
|
3453
3412
|
if (hasPreselection) {
|
|
3454
3413
|
pinnedItemIds = preselectedItems.get(ctxId) ?? [];
|
|
3414
|
+
} else if (scopedItemsByContext?.has(ctxId)) {
|
|
3415
|
+
pinnedItemIds = scopedItemsByContext.get(ctxId) ?? [];
|
|
3455
3416
|
} else if (!skipPrefilter) {
|
|
3456
3417
|
const identifierPins = identifierPinsByContext.get(ctxId) ?? /* @__PURE__ */ new Set();
|
|
3457
3418
|
let pins = new Set(identifierPins);
|
|
@@ -3659,24 +3620,6 @@ async function rerankResults(opts) {
|
|
|
3659
3620
|
}
|
|
3660
3621
|
|
|
3661
3622
|
// ee/agentic-retrieval/pipeline/index.ts
|
|
3662
|
-
function parsePreselectedItems(globalIds) {
|
|
3663
|
-
const map = /* @__PURE__ */ new Map();
|
|
3664
|
-
for (const gid of globalIds) {
|
|
3665
|
-
const slashIdx = gid.indexOf("/");
|
|
3666
|
-
if (slashIdx === -1) {
|
|
3667
|
-
if (gid) map.set(gid, null);
|
|
3668
|
-
continue;
|
|
3669
|
-
}
|
|
3670
|
-
const contextId = gid.slice(0, slashIdx);
|
|
3671
|
-
const itemId = gid.slice(slashIdx + 1);
|
|
3672
|
-
if (!contextId || !itemId) continue;
|
|
3673
|
-
if (map.get(contextId) === null) continue;
|
|
3674
|
-
const existing = map.get(contextId) ?? [];
|
|
3675
|
-
existing.push(itemId);
|
|
3676
|
-
map.set(contextId, existing);
|
|
3677
|
-
}
|
|
3678
|
-
return map;
|
|
3679
|
-
}
|
|
3680
3623
|
function addChunks(result, chunks) {
|
|
3681
3624
|
const seen = new Set(result.chunks.map((c) => c.chunk_id));
|
|
3682
3625
|
for (const chunk of chunks) {
|
|
@@ -3706,7 +3649,8 @@ function createAgenticRetrievalTool(opts) {
|
|
|
3706
3649
|
model,
|
|
3707
3650
|
instructions: adminInstructions,
|
|
3708
3651
|
preselected,
|
|
3709
|
-
memoryItems
|
|
3652
|
+
memoryItems,
|
|
3653
|
+
projectScope
|
|
3710
3654
|
} = opts;
|
|
3711
3655
|
const license = checkLicense();
|
|
3712
3656
|
if (!license["agentic-retrieval"]) {
|
|
@@ -3716,7 +3660,9 @@ function createAgenticRetrievalTool(opts) {
|
|
|
3716
3660
|
return ExuluTool.internal({
|
|
3717
3661
|
id: "agentic_context_search",
|
|
3718
3662
|
name: "Context Search",
|
|
3719
|
-
description: `Intelligent knowledge search across the available knowledge bases: ${contexts.map((c) => c.name || c.id).join(", ")}. Routes the question to the right sources, searches them with query expansion, and returns reranked passages. Results are exhaustive for the given query: do NOT repeat the call with a rephrased version of the same question \u2014 re-call only with genuinely new information (a different product or model, an explicitly named source or document, or new details from the user)
|
|
3663
|
+
description: `Intelligent knowledge search across the available knowledge bases: ${contexts.map((c) => c.name || c.id).join(", ")}. Routes the question to the right sources, searches them with query expansion, and returns reranked passages. Results are exhaustive for the given query: do NOT repeat the call with a rephrased version of the same question \u2014 re-call only with genuinely new information (a different product or model, an explicitly named source or document, or new details from the user).` + // Note: the description suffix intentionally remains even when the per-agent project_search
|
|
3664
|
+
// config is off — the config is only known at execute time, not at factory time.
|
|
3665
|
+
(projectScope ? ` Also searches the knowledge items attached to the project "${projectScope.name}".` : ""),
|
|
3720
3666
|
category: "contexts",
|
|
3721
3667
|
needsApproval: false,
|
|
3722
3668
|
type: "context",
|
|
@@ -3759,10 +3705,16 @@ function createAgenticRetrievalTool(opts) {
|
|
|
3759
3705
|
},
|
|
3760
3706
|
{
|
|
3761
3707
|
name: "max_steps",
|
|
3762
|
-
description: "Maximum
|
|
3708
|
+
description: "Maximum knowledge searches the agent may run for one message. Once spent, the search tool is disabled for the rest of the turn. 0 = no search-specific cap (the agent's overall tool-step budget still applies).",
|
|
3763
3709
|
type: "number",
|
|
3764
3710
|
default: 0
|
|
3765
3711
|
},
|
|
3712
|
+
{
|
|
3713
|
+
name: "project_search",
|
|
3714
|
+
description: "Automatically include items attached to the chat's project as an additional knowledge source (boosts them in shared sources, adds scoped search for others).",
|
|
3715
|
+
type: "boolean",
|
|
3716
|
+
default: true
|
|
3717
|
+
},
|
|
3766
3718
|
{
|
|
3767
3719
|
name: "knowledge_bases",
|
|
3768
3720
|
description: "Per-knowledge-base profiles: enabled, kind (documents | conversations | records), instructions, and per-KB overrides (limit, expand, multiQuery, hyde). JSON object keyed by context id.",
|
|
@@ -3794,11 +3746,11 @@ function createAgenticRetrievalTool(opts) {
|
|
|
3794
3746
|
default: '{"topK":5,"fallbackThreshold":0.95,"pinBoost":0.15,"identifierBoost":0.15,"pageWindow":1,"maxQueriesPerContext":5}'
|
|
3795
3747
|
}
|
|
3796
3748
|
],
|
|
3797
|
-
inputSchema:
|
|
3798
|
-
userQuery:
|
|
3799
|
-
relevantKeywords:
|
|
3800
|
-
importantKeyword:
|
|
3801
|
-
confirmedContextIds:
|
|
3749
|
+
inputSchema: z7.object({
|
|
3750
|
+
userQuery: z7.string().describe("The original unaltered question from the user"),
|
|
3751
|
+
relevantKeywords: z7.array(z7.string()).describe("Keywords extracted from the user's question relevant to the search"),
|
|
3752
|
+
importantKeyword: z7.string().describe("The single most important keyword from the user's question"),
|
|
3753
|
+
confirmedContextIds: z7.array(z7.string()).optional().describe(
|
|
3802
3754
|
"Knowledge base IDs explicitly confirmed by the user to be used in the retrieval. When present, only searches these contexts."
|
|
3803
3755
|
)
|
|
3804
3756
|
}),
|
|
@@ -3872,6 +3824,23 @@ function createAgenticRetrievalTool(opts) {
|
|
|
3872
3824
|
}
|
|
3873
3825
|
}
|
|
3874
3826
|
const preselectedItems = parsePreselectedItems(preselected ?? []);
|
|
3827
|
+
const availableContextsById = new Map(contexts.map((c) => [c.id, c]));
|
|
3828
|
+
const resolvedProject = cfg.projectSearch ? resolveProjectScope({
|
|
3829
|
+
scope: projectScope,
|
|
3830
|
+
enabledContextIds: new Set(enabledContexts.map((c) => c.id)),
|
|
3831
|
+
availableContextIds: new Set(availableContextsById.keys())
|
|
3832
|
+
}) : void 0;
|
|
3833
|
+
if (resolvedProject) {
|
|
3834
|
+
if (projectScope?.kbProfileDefaults) {
|
|
3835
|
+
for (const [ctxId, profile] of Object.entries(projectScope.kbProfileDefaults)) {
|
|
3836
|
+
if (!cfg.knowledgeBases[ctxId]) cfg.knowledgeBases[ctxId] = profile;
|
|
3837
|
+
}
|
|
3838
|
+
}
|
|
3839
|
+
enabledContexts = [
|
|
3840
|
+
...enabledContexts,
|
|
3841
|
+
...resolvedProject.addedContextIds.map((id) => availableContextsById.get(id)).filter((c) => Boolean(c))
|
|
3842
|
+
];
|
|
3843
|
+
}
|
|
3875
3844
|
const contextsById = new Map(enabledContexts.map((c) => [c.id, c]));
|
|
3876
3845
|
const kbKindById = new Map(
|
|
3877
3846
|
enabledContexts.map((c) => [
|
|
@@ -3882,7 +3851,12 @@ function createAgenticRetrievalTool(opts) {
|
|
|
3882
3851
|
const documentContexts = enabledContexts.filter(
|
|
3883
3852
|
(c) => (cfg.knowledgeBases[c.id]?.kind ?? "documents") === "documents"
|
|
3884
3853
|
);
|
|
3885
|
-
const extraInstructions = [
|
|
3854
|
+
const extraInstructions = [
|
|
3855
|
+
cfg.instructions,
|
|
3856
|
+
adminInstructions,
|
|
3857
|
+
resolvedProject && projectScope?.customInstructions ? `Instructions for the attached project "${projectScope.name}":
|
|
3858
|
+
${projectScope.customInstructions}` : ""
|
|
3859
|
+
].filter(Boolean).join("\n");
|
|
3886
3860
|
const [memResult, routResult] = await Promise.all([
|
|
3887
3861
|
runMemoryPhase({
|
|
3888
3862
|
memoryChunks: memoryItems ?? [],
|
|
@@ -3928,6 +3902,30 @@ function createAgenticRetrievalTool(opts) {
|
|
|
3928
3902
|
yield { result: "The user has requested to search in knowledge bases that are not part of the preselected knowledge bases: " + missing.join(", ") };
|
|
3929
3903
|
return;
|
|
3930
3904
|
}
|
|
3905
|
+
let effectiveMainContexts = mainContexts;
|
|
3906
|
+
if (resolvedProject) {
|
|
3907
|
+
const mainSet = new Set(mainContexts);
|
|
3908
|
+
const appended = resolvedProject.allProjectContextIds.filter(
|
|
3909
|
+
(id) => !mainSet.has(id) && contextsById.has(id)
|
|
3910
|
+
);
|
|
3911
|
+
if (appended.length > 0) {
|
|
3912
|
+
effectiveMainContexts = [...mainContexts, ...appended];
|
|
3913
|
+
result.steps.push({
|
|
3914
|
+
stepNumber: 1,
|
|
3915
|
+
text: `Including sources from project "${projectScope.name}": ${appended.join(", ")}`,
|
|
3916
|
+
toolCalls: [],
|
|
3917
|
+
chunks: [],
|
|
3918
|
+
tokens: 0
|
|
3919
|
+
});
|
|
3920
|
+
result.reasoning.push({
|
|
3921
|
+
text: `Including project sources: ${appended.join(", ")}`,
|
|
3922
|
+
tools: []
|
|
3923
|
+
});
|
|
3924
|
+
}
|
|
3925
|
+
}
|
|
3926
|
+
const fallbackContextsToSearch = fallbackContexts.filter(
|
|
3927
|
+
(id) => !effectiveMainContexts.includes(id)
|
|
3928
|
+
);
|
|
3931
3929
|
const {
|
|
3932
3930
|
updatedQuestion,
|
|
3933
3931
|
updatedKeywords,
|
|
@@ -3954,7 +3952,7 @@ function createAgenticRetrievalTool(opts) {
|
|
|
3954
3952
|
}
|
|
3955
3953
|
const [mainSearch, speculativeFallbackSearch] = await Promise.all([
|
|
3956
3954
|
searchContexts({
|
|
3957
|
-
contextIds:
|
|
3955
|
+
contextIds: effectiveMainContexts,
|
|
3958
3956
|
contextsById,
|
|
3959
3957
|
kbProfiles: cfg.knowledgeBases,
|
|
3960
3958
|
question: updatedQuestion,
|
|
@@ -3967,13 +3965,14 @@ function createAgenticRetrievalTool(opts) {
|
|
|
3967
3965
|
identifierPinsByContext,
|
|
3968
3966
|
memoryPinnedItemIds,
|
|
3969
3967
|
userPinnedItemIdsByContext,
|
|
3968
|
+
scopedItemsByContext: resolvedProject?.scopedItemsByContext,
|
|
3970
3969
|
rewrites: cfg.vocabulary.rewrites,
|
|
3971
3970
|
styleHint: cfg.vocabulary.styleHint,
|
|
3972
3971
|
maxQueries: cfg.tuning.maxQueriesPerContext,
|
|
3973
3972
|
skipPrefilter: false
|
|
3974
3973
|
}),
|
|
3975
|
-
|
|
3976
|
-
contextIds:
|
|
3974
|
+
fallbackContextsToSearch.length > 0 && !hasExplicitDocAndPage ? searchContexts({
|
|
3975
|
+
contextIds: fallbackContextsToSearch,
|
|
3977
3976
|
contextsById,
|
|
3978
3977
|
kbProfiles: cfg.knowledgeBases,
|
|
3979
3978
|
question: updatedQuestion,
|
|
@@ -3986,6 +3985,7 @@ function createAgenticRetrievalTool(opts) {
|
|
|
3986
3985
|
identifierPinsByContext,
|
|
3987
3986
|
memoryPinnedItemIds,
|
|
3988
3987
|
userPinnedItemIdsByContext,
|
|
3988
|
+
scopedItemsByContext: resolvedProject?.scopedItemsByContext,
|
|
3989
3989
|
rewrites: cfg.vocabulary.rewrites,
|
|
3990
3990
|
styleHint: cfg.vocabulary.styleHint,
|
|
3991
3991
|
maxQueries: cfg.tuning.maxQueriesPerContext,
|
|
@@ -3999,6 +3999,9 @@ function createAgenticRetrievalTool(opts) {
|
|
|
3999
3999
|
})(),
|
|
4000
4000
|
...(function* () {
|
|
4001
4001
|
for (const s of userPinnedItemIdsByContext.values()) yield* s;
|
|
4002
|
+
})(),
|
|
4003
|
+
...(function* () {
|
|
4004
|
+
if (resolvedProject) for (const s of resolvedProject.pinsByContext.values()) yield* s;
|
|
4002
4005
|
})()
|
|
4003
4006
|
]);
|
|
4004
4007
|
const userPinnedItemIds = new Set(
|
|
@@ -4066,15 +4069,15 @@ function createAgenticRetrievalTool(opts) {
|
|
|
4066
4069
|
result.reasoning.push({ text: "Literal lookup satisfied; skipping fallback.", tools: [] });
|
|
4067
4070
|
yield { result: serializeOutput(result) };
|
|
4068
4071
|
}
|
|
4069
|
-
if (!literalLookupSatisfied &&
|
|
4072
|
+
if (!literalLookupSatisfied && fallbackContextsToSearch.length > 0 && (reranker ? mainRerank.rerank_score_max_genuine < cfg.tuning.fallbackThreshold : mainRerank.limited_results.length < cfg.tuning.topK)) {
|
|
4070
4073
|
result.steps.push({
|
|
4071
4074
|
stepNumber: 1,
|
|
4072
|
-
text: `Using fallback search in ${
|
|
4075
|
+
text: `Using fallback search in ${fallbackContextsToSearch.join(", ")}`,
|
|
4073
4076
|
toolCalls: [],
|
|
4074
4077
|
chunks: [],
|
|
4075
4078
|
tokens: 0
|
|
4076
4079
|
});
|
|
4077
|
-
result.reasoning.push({ text: `Fallback search in ${
|
|
4080
|
+
result.reasoning.push({ text: `Fallback search in ${fallbackContextsToSearch.join(", ")}`, tools: [] });
|
|
4078
4081
|
yield { result: serializeOutput(result) };
|
|
4079
4082
|
const fallbackRerank = await rerankResults({
|
|
4080
4083
|
chunks: speculativeFallbackSearch.chunks,
|
|
@@ -4160,7 +4163,7 @@ function sanitizeToolName(name) {
|
|
|
4160
4163
|
}
|
|
4161
4164
|
|
|
4162
4165
|
// src/templates/tools/convert-exulu-tools-to-ai-sdk-tools.ts
|
|
4163
|
-
import { randomUUID as
|
|
4166
|
+
import { randomUUID as randomUUID4 } from "crypto";
|
|
4164
4167
|
|
|
4165
4168
|
// types/enums/statistics.ts
|
|
4166
4169
|
var STATISTICS_TYPE_ENUM = {
|
|
@@ -4176,12 +4179,12 @@ var STATISTICS_TYPE_ENUM = {
|
|
|
4176
4179
|
};
|
|
4177
4180
|
|
|
4178
4181
|
// src/templates/tools/memory-tool.ts
|
|
4179
|
-
import { z as
|
|
4182
|
+
import { z as z8 } from "zod";
|
|
4180
4183
|
var createNewMemoryItemTool = (agent, context) => {
|
|
4181
4184
|
const fields = {
|
|
4182
|
-
name:
|
|
4183
|
-
description:
|
|
4184
|
-
surroundingContext:
|
|
4185
|
+
name: z8.string().describe("The name of the item to create"),
|
|
4186
|
+
description: z8.string().describe("The description of the item to create"),
|
|
4187
|
+
surroundingContext: z8.string().describe("A description of the context surrounding this memory, for example if it relates to a question a user asked, a specific product, or entity etc...")
|
|
4185
4188
|
};
|
|
4186
4189
|
for (const field of context.fields) {
|
|
4187
4190
|
switch (field.type) {
|
|
@@ -4189,47 +4192,47 @@ var createNewMemoryItemTool = (agent, context) => {
|
|
|
4189
4192
|
case "longText":
|
|
4190
4193
|
case "shortText":
|
|
4191
4194
|
case "code":
|
|
4192
|
-
fields[field.name] =
|
|
4195
|
+
fields[field.name] = z8.string().describe("The " + field.name + " of the item to create");
|
|
4193
4196
|
break;
|
|
4194
4197
|
case "enum":
|
|
4195
4198
|
if (field.enumValues && field.enumValues.length > 0) {
|
|
4196
4199
|
const enumValues = field.enumValues;
|
|
4197
|
-
fields[field.name] =
|
|
4200
|
+
fields[field.name] = z8.preprocess(
|
|
4198
4201
|
(v) => typeof v === "string" ? v.toUpperCase() : v,
|
|
4199
|
-
|
|
4202
|
+
z8.enum(enumValues)
|
|
4200
4203
|
).describe(
|
|
4201
4204
|
"The " + field.name + " of the item to create. Must be one of: " + field.enumValues.join(", ")
|
|
4202
4205
|
);
|
|
4203
4206
|
} else {
|
|
4204
|
-
fields[field.name] =
|
|
4207
|
+
fields[field.name] = z8.string().describe("The " + field.name + " of the item to create");
|
|
4205
4208
|
}
|
|
4206
4209
|
break;
|
|
4207
4210
|
case "json":
|
|
4208
|
-
fields[field.name] =
|
|
4211
|
+
fields[field.name] = z8.string({}).describe(
|
|
4209
4212
|
"The " + field.name + " of the item to create, it should be a valid JSON string."
|
|
4210
4213
|
);
|
|
4211
4214
|
break;
|
|
4212
4215
|
case "markdown":
|
|
4213
|
-
fields[field.name] =
|
|
4216
|
+
fields[field.name] = z8.string().describe(
|
|
4214
4217
|
"The " + field.name + " of the item to create, it should be a valid Markdown string."
|
|
4215
4218
|
);
|
|
4216
4219
|
break;
|
|
4217
4220
|
case "number":
|
|
4218
|
-
fields[field.name] =
|
|
4221
|
+
fields[field.name] = z8.number().describe("The " + field.name + " of the item to create");
|
|
4219
4222
|
break;
|
|
4220
4223
|
case "boolean":
|
|
4221
|
-
fields[field.name] =
|
|
4224
|
+
fields[field.name] = z8.boolean().describe("The " + field.name + " of the item to create");
|
|
4222
4225
|
break;
|
|
4223
4226
|
case "file":
|
|
4224
4227
|
case "uuid":
|
|
4225
4228
|
case "date":
|
|
4226
4229
|
break;
|
|
4227
4230
|
default:
|
|
4228
|
-
fields[field.name] =
|
|
4231
|
+
fields[field.name] = z8.string().describe("The " + field.name + " of the item to create");
|
|
4229
4232
|
break;
|
|
4230
4233
|
}
|
|
4231
4234
|
}
|
|
4232
|
-
fields["visibility"] =
|
|
4235
|
+
fields["visibility"] = z8.enum(["private", "public"]).optional().describe(
|
|
4233
4236
|
"Whether this memory is private to the user or shared (public). Ask the user if unknown."
|
|
4234
4237
|
);
|
|
4235
4238
|
const toolName = "create_" + sanitizeName(context.name) + "_memory_item";
|
|
@@ -4239,7 +4242,7 @@ var createNewMemoryItemTool = (agent, context) => {
|
|
|
4239
4242
|
category: agent.name + "_memory",
|
|
4240
4243
|
description: "Create a new memory item in the " + agent.name + " memory context",
|
|
4241
4244
|
type: "function",
|
|
4242
|
-
inputSchema:
|
|
4245
|
+
inputSchema: z8.object(fields),
|
|
4243
4246
|
config: [],
|
|
4244
4247
|
execute: async (params) => {
|
|
4245
4248
|
const { name, description, surroundingContext, information, visibility, exuluConfig, user } = params;
|
|
@@ -5424,7 +5427,7 @@ ${body}`
|
|
|
5424
5427
|
// ee/invoke-skills/create-sandbox.ts
|
|
5425
5428
|
import { createBashTool } from "bash-tool";
|
|
5426
5429
|
import { tool as tool2 } from "ai";
|
|
5427
|
-
import { z as
|
|
5430
|
+
import { z as z9 } from "zod";
|
|
5428
5431
|
import CryptoJS4 from "crypto-js";
|
|
5429
5432
|
var getAllExuluVariables = async () => {
|
|
5430
5433
|
const { db: db2 } = await postgresClient();
|
|
@@ -5842,9 +5845,9 @@ Probe error: ${probe.reason ?? "(no detail)"}`
|
|
|
5842
5845
|
});
|
|
5843
5846
|
const writeFileTool = tool2({
|
|
5844
5847
|
description: 'Write content to a file in the sandbox. Creates parent directories if needed. Paths are always resolved against the session sandbox root \u2014 both relative paths ("skills/foo.md") and leading-slash paths ("/skills/foo.md") work and reach the same file. When the path is under the session artifact tree, the file is also uploaded to S3 and a short-lived presigned URL is returned in the tool output.',
|
|
5845
|
-
inputSchema:
|
|
5846
|
-
path:
|
|
5847
|
-
content:
|
|
5848
|
+
inputSchema: z9.object({
|
|
5849
|
+
path: z9.string().describe("The path where the file should be written. Relative paths and leading-slash paths are both resolved against the session sandbox root."),
|
|
5850
|
+
content: z9.string().describe("The content to write to the file")
|
|
5848
5851
|
}),
|
|
5849
5852
|
execute: async ({ path, content }) => {
|
|
5850
5853
|
const resolvedPath = resolveSessionPath(path, sessionDir);
|
|
@@ -5863,8 +5866,8 @@ Probe error: ${probe.reason ?? "(no detail)"}`
|
|
|
5863
5866
|
});
|
|
5864
5867
|
const readFileTool = tool2({
|
|
5865
5868
|
description: 'Read the contents of a file from the sandbox. Paths are always resolved against the session sandbox root \u2014 both relative paths ("skills/foo.md") and leading-slash paths ("/skills/foo.md") work and reach the same file. If the file does not exist, the error message is surfaced verbatim.',
|
|
5866
|
-
inputSchema:
|
|
5867
|
-
path:
|
|
5869
|
+
inputSchema: z9.object({
|
|
5870
|
+
path: z9.string().describe("The path of the file to read. Relative paths and leading-slash paths are both resolved against the session sandbox root.")
|
|
5868
5871
|
}),
|
|
5869
5872
|
execute: async ({ path }) => {
|
|
5870
5873
|
const resolvedPath = resolveSessionPath(path, sessionDir);
|
|
@@ -5875,8 +5878,8 @@ Probe error: ${probe.reason ?? "(no detail)"}`
|
|
|
5875
5878
|
const originalBashTool = tools.bash;
|
|
5876
5879
|
const bashTool = tool2({
|
|
5877
5880
|
description: originalBashTool.description ?? "",
|
|
5878
|
-
inputSchema:
|
|
5879
|
-
command:
|
|
5881
|
+
inputSchema: z9.object({
|
|
5882
|
+
command: z9.string().describe("The bash command to execute.")
|
|
5880
5883
|
}),
|
|
5881
5884
|
execute: async (args, opts) => {
|
|
5882
5885
|
const before = persistenceEnabled ? await snapshotSessionArtifacts() : null;
|
|
@@ -5948,9 +5951,9 @@ ${lines.join("\n")}`;
|
|
|
5948
5951
|
}
|
|
5949
5952
|
|
|
5950
5953
|
// src/utils/truncate-tool-output.ts
|
|
5951
|
-
var truncateToolOutput = (output, maxContextLength, toolName, tailFraction = 0.1) => {
|
|
5954
|
+
var truncateToolOutput = (output, maxContextLength, toolName, tailFraction = 0.1, charLimitOverride) => {
|
|
5952
5955
|
const effectiveCtx = maxContextLength != null && maxContextLength > 0 ? maxContextLength : 128e3;
|
|
5953
|
-
const charLimit = Math.floor(effectiveCtx * 0.25 * 4);
|
|
5956
|
+
const charLimit = charLimitOverride != null && charLimitOverride > 0 ? charLimitOverride : Math.floor(effectiveCtx * 0.25 * 4);
|
|
5954
5957
|
const clampedTail = Math.min(1, Math.max(0, tailFraction));
|
|
5955
5958
|
if (output.length <= charLimit) return output;
|
|
5956
5959
|
const headChars = Math.floor(charLimit * (1 - clampedTail));
|
|
@@ -5970,8 +5973,230 @@ var truncateToolOutput = (output, maxContextLength, toolName, tailFraction = 0.1
|
|
|
5970
5973
|
return head + marker + tail;
|
|
5971
5974
|
};
|
|
5972
5975
|
|
|
5976
|
+
// src/exulu/tool-output-offload.ts
|
|
5977
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
5978
|
+
|
|
5979
|
+
// src/exulu/context-budget.ts
|
|
5980
|
+
var DEFAULT_CONTEXT_WINDOW = 128e3;
|
|
5981
|
+
var deriveContextBudget = (contextWindowInput) => {
|
|
5982
|
+
const contextWindow = contextWindowInput != null && contextWindowInput > 0 ? contextWindowInput : DEFAULT_CONTEXT_WINDOW;
|
|
5983
|
+
const outputReserve = Math.min(32e3, Math.floor(contextWindow * 0.2));
|
|
5984
|
+
const usableWindow = contextWindow - outputReserve;
|
|
5985
|
+
return {
|
|
5986
|
+
contextWindow,
|
|
5987
|
+
outputReserve,
|
|
5988
|
+
usableWindow,
|
|
5989
|
+
warnThreshold: Math.floor(usableWindow * 0.8),
|
|
5990
|
+
blockThreshold: Math.floor(usableWindow * 0.95),
|
|
5991
|
+
toolOutputCapTokens: Math.min(25e3, Math.max(4e3, Math.floor(contextWindow * 0.1))),
|
|
5992
|
+
compactionTailTokens: Math.floor(usableWindow * 0.1),
|
|
5993
|
+
summaryBudgetTokens: Math.min(8e3, Math.floor(usableWindow * 0.05))
|
|
5994
|
+
};
|
|
5995
|
+
};
|
|
5996
|
+
var estimateTokens = (text) => text ? Math.ceil(text.length / 4) : 0;
|
|
5997
|
+
var estimateMessageTokens = (message) => estimateTokens(JSON.stringify(message));
|
|
5998
|
+
var getCompaction = (message) => message.metadata?.compaction;
|
|
5999
|
+
var sliceHistoryAtCheckpoint = (messages) => {
|
|
6000
|
+
let checkpointIdx = -1;
|
|
6001
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
6002
|
+
if (getCompaction(messages[i])) {
|
|
6003
|
+
checkpointIdx = i;
|
|
6004
|
+
break;
|
|
6005
|
+
}
|
|
6006
|
+
}
|
|
6007
|
+
if (checkpointIdx === -1) return messages;
|
|
6008
|
+
const checkpoint = messages[checkpointIdx];
|
|
6009
|
+
const coversUpTo = getCompaction(checkpoint).coversUpTo;
|
|
6010
|
+
const coversIdx = messages.findIndex((m) => m.id === coversUpTo);
|
|
6011
|
+
const boundary = coversIdx === -1 ? checkpointIdx : coversIdx;
|
|
6012
|
+
const after = messages.filter((m, i) => i > boundary && i !== checkpointIdx);
|
|
6013
|
+
return [checkpoint, ...after];
|
|
6014
|
+
};
|
|
6015
|
+
var contextOccupancy = (messages) => {
|
|
6016
|
+
let anchorIdx = -1;
|
|
6017
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
6018
|
+
const m = messages[i];
|
|
6019
|
+
const meta = m.metadata;
|
|
6020
|
+
if (getCompaction(m) || m.role === "assistant" && (typeof meta?.inputTokens === "number" || typeof meta?.lastStepInputTokens === "number")) {
|
|
6021
|
+
anchorIdx = i;
|
|
6022
|
+
break;
|
|
6023
|
+
}
|
|
6024
|
+
}
|
|
6025
|
+
let total = 0;
|
|
6026
|
+
let rest = messages;
|
|
6027
|
+
if (anchorIdx !== -1) {
|
|
6028
|
+
const anchor = messages[anchorIdx];
|
|
6029
|
+
const compaction = getCompaction(anchor);
|
|
6030
|
+
if (compaction) {
|
|
6031
|
+
total = compaction.occupancyEstimate;
|
|
6032
|
+
} else {
|
|
6033
|
+
const meta = anchor.metadata;
|
|
6034
|
+
total = typeof meta.lastStepInputTokens === "number" ? meta.lastStepInputTokens + (meta.lastStepOutputTokens ?? 0) : (meta.inputTokens ?? 0) + (meta.outputTokens ?? 0);
|
|
6035
|
+
}
|
|
6036
|
+
rest = messages.slice(anchorIdx + 1);
|
|
6037
|
+
}
|
|
6038
|
+
for (const m of rest) total += estimateMessageTokens(m);
|
|
6039
|
+
return total;
|
|
6040
|
+
};
|
|
6041
|
+
var CONTEXT_COMPACTION_REQUIRED = "CONTEXT_COMPACTION_REQUIRED";
|
|
6042
|
+
var COMPACTION_INSUFFICIENT = "COMPACTION_INSUFFICIENT";
|
|
6043
|
+
var ContextCompactionRequiredError = class extends Error {
|
|
6044
|
+
constructor(occupancy, budget) {
|
|
6045
|
+
super(
|
|
6046
|
+
JSON.stringify({
|
|
6047
|
+
code: CONTEXT_COMPACTION_REQUIRED,
|
|
6048
|
+
message: `This conversation no longer fits the model's context window (~${occupancy.toLocaleString("en-US")} of ${budget.usableWindow.toLocaleString("en-US")} usable tokens). Compact the conversation to continue.`,
|
|
6049
|
+
occupancy,
|
|
6050
|
+
usableWindow: budget.usableWindow,
|
|
6051
|
+
contextWindow: budget.contextWindow
|
|
6052
|
+
})
|
|
6053
|
+
);
|
|
6054
|
+
this.occupancy = occupancy;
|
|
6055
|
+
this.budget = budget;
|
|
6056
|
+
this.name = "ContextCompactionRequiredError";
|
|
6057
|
+
}
|
|
6058
|
+
};
|
|
6059
|
+
var PROVIDER_CONTEXT_ERROR_PATTERNS = [
|
|
6060
|
+
/ContextWindowExceededError/i,
|
|
6061
|
+
/context.?window/i,
|
|
6062
|
+
/context.?length/i,
|
|
6063
|
+
/maximum context/i,
|
|
6064
|
+
/prompt is too long/i,
|
|
6065
|
+
/input is too long/i,
|
|
6066
|
+
/token count exceeds/i,
|
|
6067
|
+
/too many tokens/i
|
|
6068
|
+
];
|
|
6069
|
+
var isProviderContextLengthError = (message) => PROVIDER_CONTEXT_ERROR_PATTERNS.some((re) => re.test(message));
|
|
6070
|
+
var mapStreamErrorMessage = (message) => isProviderContextLengthError(message) ? JSON.stringify({
|
|
6071
|
+
code: CONTEXT_COMPACTION_REQUIRED,
|
|
6072
|
+
message: "The model rejected the request because the conversation exceeds its context window. Compact the conversation to continue.",
|
|
6073
|
+
providerMessage: message.slice(0, 500)
|
|
6074
|
+
}) : message;
|
|
6075
|
+
|
|
6076
|
+
// src/exulu/tool-output-offload.ts
|
|
6077
|
+
var PREVIEW_CHARS = 4e3;
|
|
6078
|
+
var storeAsSessionFile = async (serialized, ctx) => {
|
|
6079
|
+
if (!ctx.sessionID || !ctx.exuluConfig?.fileUploads) return void 0;
|
|
6080
|
+
const safeTool = ctx.toolName.replace(/[^a-zA-Z0-9_-]/g, "_").slice(0, 40);
|
|
6081
|
+
const name = `tool-output-${safeTool}-${randomUUID3().slice(0, 8)}.txt`;
|
|
6082
|
+
try {
|
|
6083
|
+
await uploadFile(
|
|
6084
|
+
Buffer.from(serialized, "utf-8"),
|
|
6085
|
+
`sessions/${ctx.sessionID}/${name}`,
|
|
6086
|
+
ctx.exuluConfig,
|
|
6087
|
+
{ contentType: "text/plain" },
|
|
6088
|
+
ctx.user?.id
|
|
6089
|
+
);
|
|
6090
|
+
return name;
|
|
6091
|
+
} catch (err) {
|
|
6092
|
+
console.error("[EXULU] Failed to offload oversized tool output to session files.", err);
|
|
6093
|
+
return void 0;
|
|
6094
|
+
}
|
|
6095
|
+
};
|
|
6096
|
+
var buildNotice = (tokens, capTokens, sessionFile) => sessionFile ? `Tool output truncated: ~${tokens.toLocaleString("en-US")} tokens (limit ${capTokens.toLocaleString("en-US")}). The FULL output is saved as session file "${sessionFile}" \u2014 call read_session_file with { filename: "${sessionFile}", offset, limit } to read specific line ranges.` : `Tool output truncated: ~${tokens.toLocaleString("en-US")} tokens (limit ${capTokens.toLocaleString("en-US")}). The remainder was discarded \u2014 re-run the tool with narrower arguments.`;
|
|
6097
|
+
var guardToolOutput = async (value, ctx) => {
|
|
6098
|
+
if (value == null) return value;
|
|
6099
|
+
let serialized;
|
|
6100
|
+
try {
|
|
6101
|
+
serialized = typeof value === "string" ? value : JSON.stringify(value);
|
|
6102
|
+
} catch {
|
|
6103
|
+
return value;
|
|
6104
|
+
}
|
|
6105
|
+
if (typeof serialized !== "string") return value;
|
|
6106
|
+
const budget = deriveContextBudget(ctx.contextWindow);
|
|
6107
|
+
const tokens = estimateTokens(serialized);
|
|
6108
|
+
if (tokens <= budget.toolOutputCapTokens) return value;
|
|
6109
|
+
const sessionFile = await storeAsSessionFile(serialized, ctx);
|
|
6110
|
+
const result = {
|
|
6111
|
+
truncated: true,
|
|
6112
|
+
notice: buildNotice(tokens, budget.toolOutputCapTokens, sessionFile),
|
|
6113
|
+
...sessionFile ? { sessionFile } : {},
|
|
6114
|
+
preview: serialized.slice(0, PREVIEW_CHARS)
|
|
6115
|
+
};
|
|
6116
|
+
return result;
|
|
6117
|
+
};
|
|
6118
|
+
var guardExtractedFileText = async (filename, text, ctx) => {
|
|
6119
|
+
const budget = deriveContextBudget(ctx.contextWindow);
|
|
6120
|
+
const tokens = estimateTokens(text);
|
|
6121
|
+
if (tokens <= budget.toolOutputCapTokens) return text;
|
|
6122
|
+
const sessionFile = await storeAsSessionFile(text, { ...ctx, toolName: `upload-${filename}` });
|
|
6123
|
+
const notice = sessionFile ? `[Document "${filename}" truncated: ~${tokens.toLocaleString("en-US")} tokens. The full extracted text is saved as session file "${sessionFile}" \u2014 read specific parts with read_session_file (offset/limit).]` : `[Document "${filename}" truncated: ~${tokens.toLocaleString("en-US")} tokens \u2014 the remainder is unavailable.]`;
|
|
6124
|
+
return `${text.slice(0, PREVIEW_CHARS)}
|
|
6125
|
+
|
|
6126
|
+
${notice}`;
|
|
6127
|
+
};
|
|
6128
|
+
|
|
6129
|
+
// src/templates/tools/session-file-read-tool.ts
|
|
6130
|
+
import { z as z10 } from "zod";
|
|
6131
|
+
var DEFAULT_LIMIT = 250;
|
|
6132
|
+
var MAX_CONTENT_CHARS = 16e3;
|
|
6133
|
+
var createSessionFileReadTool = ({
|
|
6134
|
+
sessionID,
|
|
6135
|
+
user,
|
|
6136
|
+
exuluConfig
|
|
6137
|
+
}) => {
|
|
6138
|
+
if (!sessionID || !exuluConfig?.fileUploads?.s3Bucket) return void 0;
|
|
6139
|
+
const readSessionFileExecute = async ({ filename, offset, limit }) => {
|
|
6140
|
+
const safeName = String(filename ?? "").trim();
|
|
6141
|
+
if (!safeName || safeName.includes("..") || safeName.includes("/") || safeName.includes("\\")) {
|
|
6142
|
+
return {
|
|
6143
|
+
error: "Invalid filename \u2014 pass the bare file name exactly as listed in the session files (no paths)."
|
|
6144
|
+
};
|
|
6145
|
+
}
|
|
6146
|
+
const uploads = exuluConfig.fileUploads;
|
|
6147
|
+
const generalPrefix = uploads.s3prefix ? `${uploads.s3prefix.replace(/\/$/, "")}/` : "";
|
|
6148
|
+
const key = `${generalPrefix}user_${user?.id ?? "api"}/sessions/${sessionID}/${safeName}`;
|
|
6149
|
+
try {
|
|
6150
|
+
const url = await getPresignedUrl(uploads.s3Bucket, key, exuluConfig);
|
|
6151
|
+
const res = await fetch(url);
|
|
6152
|
+
if (!res.ok) {
|
|
6153
|
+
return { error: `Could not read session file "${safeName}" (status ${res.status}). Check the exact file name.` };
|
|
6154
|
+
}
|
|
6155
|
+
const textBody = await res.text();
|
|
6156
|
+
const lines = textBody.split("\n");
|
|
6157
|
+
const start = (offset ?? 1) - 1;
|
|
6158
|
+
const requested = limit ?? DEFAULT_LIMIT;
|
|
6159
|
+
const sliced = lines.slice(start, start + requested);
|
|
6160
|
+
let content = sliced.join("\n");
|
|
6161
|
+
let linesReturned = sliced.length;
|
|
6162
|
+
if (content.length > MAX_CONTENT_CHARS) {
|
|
6163
|
+
content = content.slice(0, MAX_CONTENT_CHARS);
|
|
6164
|
+
linesReturned = Math.max(1, content.split("\n").length - 1);
|
|
6165
|
+
content = content + "\n[slice truncated \u2014 request fewer lines]";
|
|
6166
|
+
}
|
|
6167
|
+
return {
|
|
6168
|
+
content,
|
|
6169
|
+
totalLines: lines.length,
|
|
6170
|
+
offset: start + 1,
|
|
6171
|
+
linesReturned
|
|
6172
|
+
};
|
|
6173
|
+
} catch (err) {
|
|
6174
|
+
return { error: `Failed to read session file "${safeName}": ${err instanceof Error ? err.message : "unknown error"}` };
|
|
6175
|
+
}
|
|
6176
|
+
};
|
|
6177
|
+
return ExuluTool.internal({
|
|
6178
|
+
id: "read_session_file",
|
|
6179
|
+
name: "read_session_file",
|
|
6180
|
+
needsApproval: false,
|
|
6181
|
+
description: "Read a line range from a file stored in this session's files \u2014 including offloaded tool outputs (tool-output-*.txt) and uploaded documents. Use offset (1-based line number) and limit to page through large files instead of reading everything at once.",
|
|
6182
|
+
inputSchema: z10.object({
|
|
6183
|
+
filename: z10.string().describe('Exact session file name as referenced in a truncation notice, e.g. "tool-output-web_search-a1b2c3d4.txt"'),
|
|
6184
|
+
offset: z10.number().int().min(1).optional().describe("1-based first line to read (default 1)"),
|
|
6185
|
+
limit: z10.number().int().min(1).max(1e3).optional().describe(`Number of lines to read (default ${DEFAULT_LIMIT})`)
|
|
6186
|
+
}),
|
|
6187
|
+
type: "function",
|
|
6188
|
+
category: "session",
|
|
6189
|
+
config: [],
|
|
6190
|
+
// ExuluTool's execute type is modeled on retrieval tools ({result/job/items});
|
|
6191
|
+
// internal utility tools return richer shapes (memory-tool has the same
|
|
6192
|
+
// mismatch). The AI SDK passes the object through verbatim, so cast.
|
|
6193
|
+
execute: readSessionFileExecute
|
|
6194
|
+
});
|
|
6195
|
+
};
|
|
6196
|
+
|
|
5973
6197
|
// src/templates/tools/convert-exulu-tools-to-ai-sdk-tools.ts
|
|
5974
|
-
var
|
|
6198
|
+
var OUTPUT_OFFLOAD_EXEMPT_TOOL_IDS = /* @__PURE__ */ new Set(["agentic_context_search"]);
|
|
6199
|
+
var generateS3Key = (filename) => `${randomUUID4()}-${filename}`;
|
|
5975
6200
|
var s3Client2;
|
|
5976
6201
|
var getMimeType = (type) => {
|
|
5977
6202
|
switch (type) {
|
|
@@ -6069,7 +6294,7 @@ var hydrateVariables = async (tool3) => {
|
|
|
6069
6294
|
await Promise.all(promises);
|
|
6070
6295
|
return tool3;
|
|
6071
6296
|
};
|
|
6072
|
-
var convertExuluToolsToAiSdkTools = async (currentTools, currentSkills, approvedTools, allExuluTools, configs, providerapikey, contexts, user, exuluConfig, sessionID, req, project, sessionItems, model, agent, memoryItems) => {
|
|
6297
|
+
var convertExuluToolsToAiSdkTools = async (currentTools, currentSkills, approvedTools, allExuluTools, configs, providerapikey, contexts, user, exuluConfig, sessionID, req, project, sessionItems, model, agent, memoryItems, contextWindow, disabledTools) => {
|
|
6073
6298
|
if (!currentTools) return {};
|
|
6074
6299
|
if (!allExuluTools) {
|
|
6075
6300
|
allExuluTools = [];
|
|
@@ -6077,6 +6302,8 @@ var convertExuluToolsToAiSdkTools = async (currentTools, currentSkills, approved
|
|
|
6077
6302
|
if (!contexts) {
|
|
6078
6303
|
contexts = [];
|
|
6079
6304
|
}
|
|
6305
|
+
const budget = deriveContextBudget(contextWindow);
|
|
6306
|
+
const toolOutputCharLimit = budget.toolOutputCapTokens * 4;
|
|
6080
6307
|
let sharedSessionSandbox;
|
|
6081
6308
|
if (sessionID && exuluConfig && agent?.sandbox_enabled === true) {
|
|
6082
6309
|
try {
|
|
@@ -6093,16 +6320,28 @@ var convertExuluToolsToAiSdkTools = async (currentTools, currentSkills, approved
|
|
|
6093
6320
|
);
|
|
6094
6321
|
}
|
|
6095
6322
|
}
|
|
6096
|
-
|
|
6097
|
-
|
|
6098
|
-
|
|
6099
|
-
|
|
6100
|
-
|
|
6101
|
-
|
|
6102
|
-
|
|
6103
|
-
|
|
6104
|
-
|
|
6105
|
-
|
|
6323
|
+
const disabled = new Set(disabledTools ?? []);
|
|
6324
|
+
let projectScope;
|
|
6325
|
+
if (project && !disabled.has("agentic_context_search")) {
|
|
6326
|
+
const { db: db2 } = await postgresClient();
|
|
6327
|
+
const projectRow = await db2.from("projects").where("id", project).first();
|
|
6328
|
+
let rawItems = projectRow?.project_items;
|
|
6329
|
+
if (typeof rawItems === "string") {
|
|
6330
|
+
try {
|
|
6331
|
+
rawItems = JSON.parse(rawItems);
|
|
6332
|
+
} catch {
|
|
6333
|
+
rawItems = void 0;
|
|
6334
|
+
}
|
|
6335
|
+
}
|
|
6336
|
+
if (projectRow && Array.isArray(rawItems) && rawItems.length > 0) {
|
|
6337
|
+
projectScope = {
|
|
6338
|
+
id: projectRow.id,
|
|
6339
|
+
name: projectRow.name,
|
|
6340
|
+
description: projectRow.description ?? void 0,
|
|
6341
|
+
customInstructions: projectRow.custom_instructions ?? void 0,
|
|
6342
|
+
items: rawItems,
|
|
6343
|
+
kbProfileDefaults: buildProjectKbProfileDefaults(rawItems)
|
|
6344
|
+
};
|
|
6106
6345
|
}
|
|
6107
6346
|
}
|
|
6108
6347
|
if (agent?.memory && contexts?.length) {
|
|
@@ -6113,7 +6352,7 @@ var convertExuluToolsToAiSdkTools = async (currentTools, currentSkills, approved
|
|
|
6113
6352
|
);
|
|
6114
6353
|
}
|
|
6115
6354
|
const createNewMemoryTool = createNewMemoryItemTool(agent, context);
|
|
6116
|
-
if (createNewMemoryTool) {
|
|
6355
|
+
if (createNewMemoryTool && !disabled.has(createNewMemoryTool.id)) {
|
|
6117
6356
|
if (!currentTools) {
|
|
6118
6357
|
currentTools = [];
|
|
6119
6358
|
}
|
|
@@ -6128,31 +6367,62 @@ var convertExuluToolsToAiSdkTools = async (currentTools, currentSkills, approved
|
|
|
6128
6367
|
contexts,
|
|
6129
6368
|
items: sessionItems
|
|
6130
6369
|
});
|
|
6131
|
-
if (sessionItemsRetrievalTool) {
|
|
6370
|
+
if (sessionItemsRetrievalTool && !disabled.has(sessionItemsRetrievalTool.id)) {
|
|
6132
6371
|
currentTools.push(sessionItemsRetrievalTool);
|
|
6133
6372
|
}
|
|
6134
6373
|
}
|
|
6374
|
+
const sessionFileReadTool = createSessionFileReadTool({ sessionID, user, exuluConfig });
|
|
6375
|
+
if (sessionFileReadTool && !disabled.has(sessionFileReadTool.id)) {
|
|
6376
|
+
currentTools.push(sessionFileReadTool);
|
|
6377
|
+
}
|
|
6135
6378
|
console.log("[EXULU] Creating agentic search tool", contexts?.length, model);
|
|
6136
|
-
if (contexts?.length && model) {
|
|
6137
|
-
const
|
|
6138
|
-
|
|
6139
|
-
|
|
6140
|
-
|
|
6141
|
-
|
|
6142
|
-
|
|
6143
|
-
|
|
6144
|
-
|
|
6145
|
-
|
|
6146
|
-
|
|
6147
|
-
|
|
6148
|
-
|
|
6149
|
-
|
|
6379
|
+
if (contexts?.length && model && !disabled.has("agentic_context_search")) {
|
|
6380
|
+
const index = currentTools.findIndex((tool3) => tool3.id === "agentic_context_search");
|
|
6381
|
+
const memoryContext = agent?.memory ? contexts.find((c) => c.id === agent.memory) : void 0;
|
|
6382
|
+
if (index !== -1) {
|
|
6383
|
+
const agenticSearchTool = createAgenticRetrievalTool({
|
|
6384
|
+
contexts: contexts.filter((context) => context.id !== agent?.memory),
|
|
6385
|
+
// memory is searched by the memory phase, not as a KB
|
|
6386
|
+
memoryContext,
|
|
6387
|
+
user,
|
|
6388
|
+
role: user?.role?.id,
|
|
6389
|
+
model,
|
|
6390
|
+
preselected: sessionItems,
|
|
6391
|
+
memoryItems,
|
|
6392
|
+
projectScope
|
|
6393
|
+
});
|
|
6394
|
+
if (agenticSearchTool) {
|
|
6150
6395
|
currentTools[index] = {
|
|
6151
6396
|
...currentTools[index],
|
|
6152
6397
|
// important to keep the original tool config
|
|
6153
6398
|
...agenticSearchTool
|
|
6154
6399
|
};
|
|
6155
6400
|
}
|
|
6401
|
+
} else if (projectScope) {
|
|
6402
|
+
const projectContextIds = new Set(
|
|
6403
|
+
projectScope.items.map((gid) => {
|
|
6404
|
+
const i = gid.indexOf("/");
|
|
6405
|
+
return i === -1 ? gid : gid.slice(0, i);
|
|
6406
|
+
})
|
|
6407
|
+
);
|
|
6408
|
+
const scopedContexts = contexts.filter(
|
|
6409
|
+
(c) => projectContextIds.has(c.id) && c.id !== agent?.memory
|
|
6410
|
+
);
|
|
6411
|
+
if (scopedContexts.length > 0) {
|
|
6412
|
+
const projectSearchTool = createAgenticRetrievalTool({
|
|
6413
|
+
contexts: scopedContexts,
|
|
6414
|
+
memoryContext,
|
|
6415
|
+
user,
|
|
6416
|
+
role: user?.role?.id,
|
|
6417
|
+
model,
|
|
6418
|
+
preselected: [...sessionItems ?? [], ...projectScope.items],
|
|
6419
|
+
memoryItems,
|
|
6420
|
+
projectScope
|
|
6421
|
+
});
|
|
6422
|
+
if (projectSearchTool) {
|
|
6423
|
+
currentTools.push(projectSearchTool);
|
|
6424
|
+
}
|
|
6425
|
+
}
|
|
6156
6426
|
}
|
|
6157
6427
|
} else {
|
|
6158
6428
|
const agenticSearchTool = currentTools.find((tool3) => tool3.id === "agentic_context_search");
|
|
@@ -6180,7 +6450,7 @@ var convertExuluToolsToAiSdkTools = async (currentTools, currentSkills, approved
|
|
|
6180
6450
|
if (typeof result?.content === "string") {
|
|
6181
6451
|
return {
|
|
6182
6452
|
...result,
|
|
6183
|
-
content: truncateToolOutput(result.content,
|
|
6453
|
+
content: truncateToolOutput(result.content, budget.contextWindow, "readFile", 0.05, toolOutputCharLimit)
|
|
6184
6454
|
};
|
|
6185
6455
|
}
|
|
6186
6456
|
return result;
|
|
@@ -6197,10 +6467,10 @@ var convertExuluToolsToAiSdkTools = async (currentTools, currentSkills, approved
|
|
|
6197
6467
|
return {
|
|
6198
6468
|
...result,
|
|
6199
6469
|
...typeof result?.stdout === "string" && {
|
|
6200
|
-
stdout: truncateToolOutput(result.stdout,
|
|
6470
|
+
stdout: truncateToolOutput(result.stdout, budget.contextWindow, "bash", 0.1, toolOutputCharLimit)
|
|
6201
6471
|
},
|
|
6202
6472
|
...typeof result?.stderr === "string" && {
|
|
6203
|
-
stderr: truncateToolOutput(result.stderr,
|
|
6473
|
+
stderr: truncateToolOutput(result.stderr, budget.contextWindow, "bash stderr", 0.4, toolOutputCharLimit)
|
|
6204
6474
|
}
|
|
6205
6475
|
};
|
|
6206
6476
|
}
|
|
@@ -6336,16 +6606,30 @@ var convertExuluToolsToAiSdkTools = async (currentTools, currentSkills, approved
|
|
|
6336
6606
|
user: user?.id,
|
|
6337
6607
|
role: user?.role?.id
|
|
6338
6608
|
});
|
|
6609
|
+
const guardCtx = {
|
|
6610
|
+
toolName: cur.name,
|
|
6611
|
+
contextWindow,
|
|
6612
|
+
sessionID,
|
|
6613
|
+
user,
|
|
6614
|
+
exuluConfig
|
|
6615
|
+
};
|
|
6616
|
+
const offloadExempt = OUTPUT_OFFLOAD_EXEMPT_TOOL_IDS.has(cur.id);
|
|
6339
6617
|
if (response && typeof response === "object" && Symbol.asyncIterator in response) {
|
|
6340
6618
|
let lastValue;
|
|
6341
6619
|
for await (const value of response) {
|
|
6342
6620
|
yield value;
|
|
6343
6621
|
lastValue = value;
|
|
6344
6622
|
}
|
|
6345
|
-
return lastValue;
|
|
6623
|
+
if (offloadExempt) return lastValue;
|
|
6624
|
+
const guarded = await guardToolOutput(lastValue, guardCtx);
|
|
6625
|
+
if (guarded !== lastValue) {
|
|
6626
|
+
yield guarded;
|
|
6627
|
+
}
|
|
6628
|
+
return guarded;
|
|
6346
6629
|
} else {
|
|
6347
|
-
|
|
6348
|
-
|
|
6630
|
+
const guarded = offloadExempt ? response : await guardToolOutput(response, guardCtx);
|
|
6631
|
+
yield guarded;
|
|
6632
|
+
return guarded;
|
|
6349
6633
|
}
|
|
6350
6634
|
}
|
|
6351
6635
|
}
|
|
@@ -6403,10 +6687,20 @@ export {
|
|
|
6403
6687
|
OAUTH_CALLBACK_PATH,
|
|
6404
6688
|
decryptOauthState,
|
|
6405
6689
|
exchangeCodeForTokens,
|
|
6406
|
-
createProjectItemsRetrievalTool,
|
|
6407
6690
|
sanitizeToolName,
|
|
6408
6691
|
reportSystemDependencies,
|
|
6409
6692
|
downloadKeyIntoSandbox,
|
|
6693
|
+
truncateToolOutput,
|
|
6694
|
+
DEFAULT_CONTEXT_WINDOW,
|
|
6695
|
+
deriveContextBudget,
|
|
6696
|
+
estimateTokens,
|
|
6697
|
+
estimateMessageTokens,
|
|
6698
|
+
sliceHistoryAtCheckpoint,
|
|
6699
|
+
contextOccupancy,
|
|
6700
|
+
COMPACTION_INSUFFICIENT,
|
|
6701
|
+
ContextCompactionRequiredError,
|
|
6702
|
+
mapStreamErrorMessage,
|
|
6703
|
+
guardExtractedFileText,
|
|
6410
6704
|
hydrateVariables,
|
|
6411
6705
|
convertExuluToolsToAiSdkTools,
|
|
6412
6706
|
ExuluTool,
|