la-machina-engine 0.6.0 → 0.7.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/README.md +196 -6
- package/dist/index.cjs +933 -112
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +255 -1
- package/dist/index.d.ts +255 -1
- package/dist/index.js +926 -110
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -127,34 +127,34 @@ var init_fetchData = __esm({
|
|
|
127
127
|
});
|
|
128
128
|
|
|
129
129
|
// src/orchestrator/types.ts
|
|
130
|
-
var
|
|
130
|
+
var import_zod28, PlanStepSchema, PlanSchema;
|
|
131
131
|
var init_types = __esm({
|
|
132
132
|
"src/orchestrator/types.ts"() {
|
|
133
133
|
"use strict";
|
|
134
134
|
init_cjs_shims();
|
|
135
|
-
|
|
136
|
-
PlanStepSchema =
|
|
137
|
-
id:
|
|
138
|
-
description:
|
|
139
|
-
action:
|
|
140
|
-
files:
|
|
141
|
-
spec:
|
|
142
|
-
dependsOn:
|
|
135
|
+
import_zod28 = require("zod");
|
|
136
|
+
PlanStepSchema = import_zod28.z.object({
|
|
137
|
+
id: import_zod28.z.string().min(1),
|
|
138
|
+
description: import_zod28.z.string().min(1),
|
|
139
|
+
action: import_zod28.z.enum(["research", "implement", "verify", "review", "custom"]),
|
|
140
|
+
files: import_zod28.z.array(import_zod28.z.string()).optional(),
|
|
141
|
+
spec: import_zod28.z.string().optional(),
|
|
142
|
+
dependsOn: import_zod28.z.array(import_zod28.z.string()).optional()
|
|
143
143
|
});
|
|
144
|
-
PlanSchema =
|
|
145
|
-
summary:
|
|
146
|
-
steps:
|
|
144
|
+
PlanSchema = import_zod28.z.object({
|
|
145
|
+
summary: import_zod28.z.string().min(1),
|
|
146
|
+
steps: import_zod28.z.array(PlanStepSchema).min(1)
|
|
147
147
|
});
|
|
148
148
|
}
|
|
149
149
|
});
|
|
150
150
|
|
|
151
151
|
// src/orchestrator/planParser.ts
|
|
152
152
|
function parsePlan(raw, maxSteps) {
|
|
153
|
-
const
|
|
154
|
-
if (
|
|
153
|
+
const json2 = extractJson(raw);
|
|
154
|
+
if (json2 === null) return null;
|
|
155
155
|
let parsed;
|
|
156
156
|
try {
|
|
157
|
-
parsed = JSON.parse(
|
|
157
|
+
parsed = JSON.parse(json2);
|
|
158
158
|
} catch {
|
|
159
159
|
return null;
|
|
160
160
|
}
|
|
@@ -164,23 +164,23 @@ function parsePlan(raw, maxSteps) {
|
|
|
164
164
|
if (plan.steps.length > maxSteps) return null;
|
|
165
165
|
return plan;
|
|
166
166
|
}
|
|
167
|
-
function extractJson(
|
|
168
|
-
const fenceMatch =
|
|
167
|
+
function extractJson(text2) {
|
|
168
|
+
const fenceMatch = text2.match(/```(?:json)?\s*\n?([\s\S]*?)\n?```/);
|
|
169
169
|
if (fenceMatch?.[1]) {
|
|
170
170
|
const inner = fenceMatch[1].trim();
|
|
171
171
|
if (inner.startsWith("{")) return inner;
|
|
172
172
|
}
|
|
173
173
|
let start = -1;
|
|
174
174
|
let depth = 0;
|
|
175
|
-
for (let i = 0; i <
|
|
176
|
-
const c =
|
|
175
|
+
for (let i = 0; i < text2.length; i++) {
|
|
176
|
+
const c = text2[i];
|
|
177
177
|
if (c === "{") {
|
|
178
178
|
if (depth === 0) start = i;
|
|
179
179
|
depth++;
|
|
180
180
|
} else if (c === "}") {
|
|
181
181
|
depth--;
|
|
182
182
|
if (depth === 0 && start !== -1) {
|
|
183
|
-
return
|
|
183
|
+
return text2.slice(start, i + 1);
|
|
184
184
|
}
|
|
185
185
|
}
|
|
186
186
|
}
|
|
@@ -869,6 +869,7 @@ __export(index_exports, {
|
|
|
869
869
|
WebhookDispatcher: () => WebhookDispatcher,
|
|
870
870
|
adaptMcpTool: () => adaptMcpTool,
|
|
871
871
|
buildForkedMessages: () => buildForkedMessages,
|
|
872
|
+
buildKnowledgeIndex: () => buildKnowledgeIndex,
|
|
872
873
|
buildPermissionPolicy: () => buildPermissionPolicy,
|
|
873
874
|
buildSchemaPrompt: () => buildSchemaPrompt,
|
|
874
875
|
buildSystemPrompt: () => buildSystemPrompt,
|
|
@@ -879,6 +880,8 @@ __export(index_exports, {
|
|
|
879
880
|
createFetchDataTool: () => createFetchDataTool,
|
|
880
881
|
createLogger: () => createLogger,
|
|
881
882
|
createModelAdapter: () => createModelAdapter,
|
|
883
|
+
createReadKnowledgeTool: () => createReadKnowledgeTool,
|
|
884
|
+
createSearchKnowledgeTool: () => createSearchKnowledgeTool,
|
|
882
885
|
createSendMessageTool: () => createSendMessageTool,
|
|
883
886
|
createSkillPageTool: () => createSkillPageTool,
|
|
884
887
|
createSmartMemory: () => createSmartMemory,
|
|
@@ -888,6 +891,7 @@ __export(index_exports, {
|
|
|
888
891
|
detectRuntime: () => detectRuntime,
|
|
889
892
|
getCoordinatorBasePrompt: () => getCoordinatorBasePrompt,
|
|
890
893
|
getCoordinatorSystemPrompt: () => getCoordinatorSystemPrompt,
|
|
894
|
+
getExtractor: () => getExtractor,
|
|
891
895
|
hasProcessLifecycle: () => hasProcessLifecycle,
|
|
892
896
|
initEngine: () => initEngine,
|
|
893
897
|
isCoordinatorMode: () => isCoordinatorMode,
|
|
@@ -910,7 +914,8 @@ __export(index_exports, {
|
|
|
910
914
|
toResponse: () => toResponse,
|
|
911
915
|
tryParseJSON: () => tryParseJSON2,
|
|
912
916
|
validateOutput: () => validateOutput,
|
|
913
|
-
withCapabilityCheck: () => withCapabilityCheck
|
|
917
|
+
withCapabilityCheck: () => withCapabilityCheck,
|
|
918
|
+
writeKnowledgeIndex: () => writeKnowledgeIndex
|
|
914
919
|
});
|
|
915
920
|
module.exports = __toCommonJS(index_exports);
|
|
916
921
|
init_cjs_shims();
|
|
@@ -1243,6 +1248,11 @@ var ApiConfigResolved = import_zod.z.object({
|
|
|
1243
1248
|
services: import_zod.z.array(ApiServiceSchema),
|
|
1244
1249
|
maxResponseBytes: import_zod.z.number().int().positive().optional()
|
|
1245
1250
|
}).strict();
|
|
1251
|
+
var KnowledgeConfigResolved = import_zod.z.object({
|
|
1252
|
+
enabled: import_zod.z.boolean(),
|
|
1253
|
+
maxSearchResults: import_zod.z.number().int().positive(),
|
|
1254
|
+
maxReadBytes: import_zod.z.number().int().positive()
|
|
1255
|
+
}).strict();
|
|
1246
1256
|
var RunnerConfigResolved = import_zod.z.object({
|
|
1247
1257
|
url: import_zod.z.string().url(),
|
|
1248
1258
|
secret: import_zod.z.string().min(1, "runner.secret cannot be empty")
|
|
@@ -1264,7 +1274,8 @@ var ResolvedConfigSchema = import_zod.z.object({
|
|
|
1264
1274
|
coordinator: CoordinatorConfigResolved,
|
|
1265
1275
|
orchestrator: OrchestratorConfigResolved,
|
|
1266
1276
|
runner: RunnerConfigResolved.optional(),
|
|
1267
|
-
api: ApiConfigResolved.optional()
|
|
1277
|
+
api: ApiConfigResolved.optional(),
|
|
1278
|
+
knowledge: KnowledgeConfigResolved.optional()
|
|
1268
1279
|
}).strict();
|
|
1269
1280
|
var R2ConfigUser = R2ConfigResolved.partial();
|
|
1270
1281
|
var ModelConfigUser = ModelConfigResolved.partial();
|
|
@@ -1275,6 +1286,7 @@ var StorageConfigUser = import_zod.z.object({
|
|
|
1275
1286
|
r2: R2ConfigUser.optional(),
|
|
1276
1287
|
r2Binding: R2BucketBindingShape.optional()
|
|
1277
1288
|
}).strict();
|
|
1289
|
+
var KnowledgeConfigUser = KnowledgeConfigResolved.partial();
|
|
1278
1290
|
var MemoryConfigUser = import_zod.z.object({
|
|
1279
1291
|
mode: MemoryModeEnum.optional(),
|
|
1280
1292
|
scope: MemoryScopeUserEnum.optional()
|
|
@@ -1339,7 +1351,8 @@ var UserConfigSchema = import_zod.z.object({
|
|
|
1339
1351
|
coordinator: CoordinatorConfigUser.optional(),
|
|
1340
1352
|
orchestrator: OrchestratorConfigUser.optional(),
|
|
1341
1353
|
runner: RunnerConfigUser.optional(),
|
|
1342
|
-
api: ApiConfigUser.optional()
|
|
1354
|
+
api: ApiConfigUser.optional(),
|
|
1355
|
+
knowledge: KnowledgeConfigUser.optional()
|
|
1343
1356
|
}).strict();
|
|
1344
1357
|
|
|
1345
1358
|
// src/config/merge.ts
|
|
@@ -1431,9 +1444,21 @@ function splitOffloadRuntime(user) {
|
|
|
1431
1444
|
};
|
|
1432
1445
|
return { stripped: clone, summarizer };
|
|
1433
1446
|
}
|
|
1447
|
+
var KNOWLEDGE_DEFAULTS = { maxSearchResults: 5, maxReadBytes: 1e4 };
|
|
1448
|
+
function fillKnowledgeDefaults(user) {
|
|
1449
|
+
const block = user.knowledge;
|
|
1450
|
+
if (block === void 0) return user;
|
|
1451
|
+
const filled = {
|
|
1452
|
+
enabled: block.enabled ?? false,
|
|
1453
|
+
maxSearchResults: block.maxSearchResults ?? KNOWLEDGE_DEFAULTS.maxSearchResults,
|
|
1454
|
+
maxReadBytes: block.maxReadBytes ?? KNOWLEDGE_DEFAULTS.maxReadBytes
|
|
1455
|
+
};
|
|
1456
|
+
return { ...user, knowledge: filled };
|
|
1457
|
+
}
|
|
1434
1458
|
function mergeConfig(user) {
|
|
1435
1459
|
const withCoercedScope = coerceDeprecatedMemoryScope(user);
|
|
1436
|
-
const
|
|
1460
|
+
const withKnowledge = fillKnowledgeDefaults(withCoercedScope);
|
|
1461
|
+
const { stripped: afterOffload, summarizer } = splitOffloadRuntime(withKnowledge);
|
|
1437
1462
|
const { stripped, runtime } = splitApiRuntime(afterOffload);
|
|
1438
1463
|
const validatedUser = UserConfigSchema.parse(stripped);
|
|
1439
1464
|
const merged = deepMerge(DEFAULTS, validatedUser);
|
|
@@ -2429,8 +2454,8 @@ function compactPreview(value) {
|
|
|
2429
2454
|
}
|
|
2430
2455
|
return str;
|
|
2431
2456
|
}
|
|
2432
|
-
function tryParseJSON(
|
|
2433
|
-
const trimmed =
|
|
2457
|
+
function tryParseJSON(text2) {
|
|
2458
|
+
const trimmed = text2.replace(/^\uFEFF/, "").trim();
|
|
2434
2459
|
if (trimmed.length === 0) return { ok: false };
|
|
2435
2460
|
const first = trimmed[0];
|
|
2436
2461
|
if (first !== "{" && first !== "[" && first !== '"') return { ok: false };
|
|
@@ -2514,8 +2539,8 @@ var INTERNAL_BLOCK_PATTERNS = [
|
|
|
2514
2539
|
/<git_signature>[\s\S]*?<\/git_signature>/g,
|
|
2515
2540
|
/<caller>[\s\S]*?<\/caller>/g
|
|
2516
2541
|
];
|
|
2517
|
-
function stripInternalBlocks(
|
|
2518
|
-
let result =
|
|
2542
|
+
function stripInternalBlocks(text2) {
|
|
2543
|
+
let result = text2;
|
|
2519
2544
|
for (const pattern of INTERNAL_BLOCK_PATTERNS) {
|
|
2520
2545
|
pattern.lastIndex = 0;
|
|
2521
2546
|
result = result.replace(pattern, "");
|
|
@@ -3163,8 +3188,8 @@ async function agentLoop(options) {
|
|
|
3163
3188
|
assistantContent.push({ type: "thinking", thinking });
|
|
3164
3189
|
}
|
|
3165
3190
|
}
|
|
3166
|
-
for (const
|
|
3167
|
-
if (
|
|
3191
|
+
for (const text2 of textBlocks) {
|
|
3192
|
+
if (text2.length > 0) assistantContent.push({ type: "text", text: text2 });
|
|
3168
3193
|
}
|
|
3169
3194
|
for (const tc of toolCalls) {
|
|
3170
3195
|
assistantContent.push({
|
|
@@ -3403,11 +3428,11 @@ function toAnthropicTool(tool) {
|
|
|
3403
3428
|
target: "jsonSchema7",
|
|
3404
3429
|
$refStrategy: "none"
|
|
3405
3430
|
});
|
|
3406
|
-
const { $schema: _schema, ...
|
|
3431
|
+
const { $schema: _schema, ...inputSchema20 } = schema;
|
|
3407
3432
|
return {
|
|
3408
3433
|
name: tool.name,
|
|
3409
3434
|
description: tool.description,
|
|
3410
|
-
input_schema:
|
|
3435
|
+
input_schema: inputSchema20
|
|
3411
3436
|
};
|
|
3412
3437
|
}
|
|
3413
3438
|
function partitionToolCalls(calls, registry) {
|
|
@@ -3542,8 +3567,8 @@ var RunContext = class {
|
|
|
3542
3567
|
this.episodes = options.episodes ?? null;
|
|
3543
3568
|
}
|
|
3544
3569
|
// ---------- message mutators ----------
|
|
3545
|
-
async addUserMessage(
|
|
3546
|
-
const content = [{ type: "text", text }];
|
|
3570
|
+
async addUserMessage(text2) {
|
|
3571
|
+
const content = [{ type: "text", text: text2 }];
|
|
3547
3572
|
this.messages.push({ role: "user", content });
|
|
3548
3573
|
await this.writeEntry({
|
|
3549
3574
|
type: "user",
|
|
@@ -3552,7 +3577,7 @@ var RunContext = class {
|
|
|
3552
3577
|
ts: this.now(),
|
|
3553
3578
|
message: { role: "user", content }
|
|
3554
3579
|
});
|
|
3555
|
-
this.episodes?.logTurn(this.turnCount, "user",
|
|
3580
|
+
this.episodes?.logTurn(this.turnCount, "user", text2);
|
|
3556
3581
|
}
|
|
3557
3582
|
async addAssistantMessage(content) {
|
|
3558
3583
|
this.messages.push({ role: "assistant", content });
|
|
@@ -5405,8 +5430,8 @@ Make a new WebFetch request with the redirect URL if you want to follow it.`,
|
|
|
5405
5430
|
isError: true
|
|
5406
5431
|
};
|
|
5407
5432
|
}
|
|
5408
|
-
const
|
|
5409
|
-
const { content, truncated } = truncate(
|
|
5433
|
+
const text2 = isHtml(contentType) ? htmlToText(raw) : raw;
|
|
5434
|
+
const { content, truncated } = truncate(text2, MAX_OUTPUT_BYTES2);
|
|
5410
5435
|
return {
|
|
5411
5436
|
content,
|
|
5412
5437
|
metadata: {
|
|
@@ -5422,24 +5447,24 @@ Make a new WebFetch request with the redirect URL if you want to follow it.`,
|
|
|
5422
5447
|
function isHtml(contentType) {
|
|
5423
5448
|
return contentType.toLowerCase().includes("html");
|
|
5424
5449
|
}
|
|
5425
|
-
function htmlToText(
|
|
5426
|
-
return
|
|
5450
|
+
function htmlToText(html2) {
|
|
5451
|
+
return html2.replace(/<script\b[^>]*>[\s\S]*?<\/script>/gi, "").replace(/<style\b[^>]*>[\s\S]*?<\/style>/gi, "").replace(/<[^>]+>/g, " ").replace(/ /g, " ").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, '"').replace(/'/g, "'").replace(/[ \t]+/g, " ").replace(/\n[ \t]+/g, "\n").replace(/\n{3,}/g, "\n\n").trim();
|
|
5427
5452
|
}
|
|
5428
|
-
function truncate(
|
|
5429
|
-
if (Buffer.byteLength(
|
|
5430
|
-
return { content:
|
|
5453
|
+
function truncate(text2, maxBytes) {
|
|
5454
|
+
if (Buffer.byteLength(text2, "utf8") <= maxBytes) {
|
|
5455
|
+
return { content: text2, truncated: false };
|
|
5431
5456
|
}
|
|
5432
5457
|
let lo = 0;
|
|
5433
|
-
let hi =
|
|
5458
|
+
let hi = text2.length;
|
|
5434
5459
|
while (lo < hi) {
|
|
5435
5460
|
const mid = lo + hi + 1 >>> 1;
|
|
5436
|
-
if (Buffer.byteLength(
|
|
5461
|
+
if (Buffer.byteLength(text2.slice(0, mid), "utf8") <= maxBytes) {
|
|
5437
5462
|
lo = mid;
|
|
5438
5463
|
} else {
|
|
5439
5464
|
hi = mid - 1;
|
|
5440
5465
|
}
|
|
5441
5466
|
}
|
|
5442
|
-
return { content:
|
|
5467
|
+
return { content: text2.slice(0, lo) + "\n... (truncated)", truncated: true };
|
|
5443
5468
|
}
|
|
5444
5469
|
async function safeText(response) {
|
|
5445
5470
|
try {
|
|
@@ -5503,17 +5528,17 @@ function createWebSearchTool() {
|
|
|
5503
5528
|
isError: true
|
|
5504
5529
|
};
|
|
5505
5530
|
}
|
|
5506
|
-
let
|
|
5531
|
+
let html2;
|
|
5507
5532
|
try {
|
|
5508
|
-
|
|
5533
|
+
html2 = await response.text();
|
|
5509
5534
|
} catch (err) {
|
|
5510
5535
|
return {
|
|
5511
5536
|
content: `Failed to read search response: ${err.message}`,
|
|
5512
5537
|
isError: true
|
|
5513
5538
|
};
|
|
5514
5539
|
}
|
|
5515
|
-
const
|
|
5516
|
-
const trimmed =
|
|
5540
|
+
const text2 = htmlToText2(html2);
|
|
5541
|
+
const trimmed = text2.slice(0, 8e3);
|
|
5517
5542
|
return {
|
|
5518
5543
|
content: `Search results for "${query}":
|
|
5519
5544
|
|
|
@@ -5523,8 +5548,8 @@ ${trimmed}`,
|
|
|
5523
5548
|
}
|
|
5524
5549
|
});
|
|
5525
5550
|
}
|
|
5526
|
-
function htmlToText2(
|
|
5527
|
-
return
|
|
5551
|
+
function htmlToText2(html2) {
|
|
5552
|
+
return html2.replace(/<script\b[^>]*>[\s\S]*?<\/script>/gi, "").replace(/<style\b[^>]*>[\s\S]*?<\/style>/gi, "").replace(/<[^>]+>/g, " ").replace(/ /g, " ").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, '"').replace(/'/g, "'").replace(/[ \t]+/g, " ").replace(/\n[ \t]+/g, "\n").replace(/\n{3,}/g, "\n\n").trim();
|
|
5528
5553
|
}
|
|
5529
5554
|
|
|
5530
5555
|
// src/tools/sleep.ts
|
|
@@ -5632,7 +5657,7 @@ function createMemorizeTool(memory) {
|
|
|
5632
5657
|
name: "Memorize",
|
|
5633
5658
|
description: 'Save a fact, rule, or lesson to persistent memory. Memories survive across runs and are included in the system prompt of future runs. Use "rule" for behavioral constraints (always/never do X). Use "lesson" for learnings and observations.',
|
|
5634
5659
|
inputSchema: inputSchema14,
|
|
5635
|
-
execute: async ({ text, kind, topic }) => {
|
|
5660
|
+
execute: async ({ text: text2, kind, topic }) => {
|
|
5636
5661
|
if (memory.mode === "off" || memory.mode === "read-only") {
|
|
5637
5662
|
return {
|
|
5638
5663
|
content: `Cannot memorize: memory mode is "${memory.mode}". Set config.memory.mode to "read-write" to enable.`,
|
|
@@ -5640,15 +5665,15 @@ function createMemorizeTool(memory) {
|
|
|
5640
5665
|
};
|
|
5641
5666
|
}
|
|
5642
5667
|
if (kind === "rule") {
|
|
5643
|
-
await memory.encodeRule(
|
|
5668
|
+
await memory.encodeRule(text2, "always");
|
|
5644
5669
|
return {
|
|
5645
|
-
content: `Memorized rule: "${
|
|
5670
|
+
content: `Memorized rule: "${text2.slice(0, 80)}${text2.length > 80 ? "\u2026" : ""}"`,
|
|
5646
5671
|
metadata: { kind: "rule" }
|
|
5647
5672
|
};
|
|
5648
5673
|
}
|
|
5649
|
-
await memory.encodeLesson(
|
|
5674
|
+
await memory.encodeLesson(text2, topic);
|
|
5650
5675
|
return {
|
|
5651
|
-
content: `Memorized lesson: "${
|
|
5676
|
+
content: `Memorized lesson: "${text2.slice(0, 80)}${text2.length > 80 ? "\u2026" : ""}"${topic ? ` (topic: ${topic})` : ""}`,
|
|
5652
5677
|
metadata: { kind: "lesson", topic }
|
|
5653
5678
|
};
|
|
5654
5679
|
}
|
|
@@ -6325,17 +6350,17 @@ var BindingHttpTransport = class {
|
|
|
6325
6350
|
}
|
|
6326
6351
|
if (response.status === 202 || response.status === 204) return;
|
|
6327
6352
|
if (!response.ok) {
|
|
6328
|
-
const
|
|
6353
|
+
const text2 = await safeText2(response);
|
|
6329
6354
|
const err = new Error(
|
|
6330
|
-
`BindingHttpTransport: HTTP ${String(response.status)} ${response.statusText}${
|
|
6355
|
+
`BindingHttpTransport: HTTP ${String(response.status)} ${response.statusText}${text2 ? ` \u2014 ${text2}` : ""}`
|
|
6331
6356
|
);
|
|
6332
6357
|
this.onerror?.(err);
|
|
6333
6358
|
throw err;
|
|
6334
6359
|
}
|
|
6335
6360
|
const contentType = response.headers.get("Content-Type") ?? "";
|
|
6336
6361
|
if (!contentType.includes("application/json")) {
|
|
6337
|
-
const
|
|
6338
|
-
const parsed2 = parseSseFrames(
|
|
6362
|
+
const text2 = await response.text();
|
|
6363
|
+
const parsed2 = parseSseFrames(text2);
|
|
6339
6364
|
for (const msg of parsed2) this.onmessage?.(msg);
|
|
6340
6365
|
return;
|
|
6341
6366
|
}
|
|
@@ -6402,9 +6427,9 @@ async function safeText2(r) {
|
|
|
6402
6427
|
return "";
|
|
6403
6428
|
}
|
|
6404
6429
|
}
|
|
6405
|
-
function parseSseFrames(
|
|
6430
|
+
function parseSseFrames(text2) {
|
|
6406
6431
|
const out = [];
|
|
6407
|
-
for (const block of
|
|
6432
|
+
for (const block of text2.split(/\r?\n\r?\n/)) {
|
|
6408
6433
|
const lines = block.split(/\r?\n/).filter((l) => l.startsWith("data:"));
|
|
6409
6434
|
if (lines.length === 0) continue;
|
|
6410
6435
|
const raw = lines.map((l) => l.slice(5).trimStart()).join("\n");
|
|
@@ -6691,8 +6716,8 @@ function normalizeToolList(response, serverName) {
|
|
|
6691
6716
|
const name = typeof obj.name === "string" ? obj.name : null;
|
|
6692
6717
|
if (name === null) continue;
|
|
6693
6718
|
const description = typeof obj.description === "string" ? obj.description : "";
|
|
6694
|
-
const
|
|
6695
|
-
out.push({ name, description, inputSchema:
|
|
6719
|
+
const inputSchema20 = obj.inputSchema !== null && typeof obj.inputSchema === "object" && !Array.isArray(obj.inputSchema) ? obj.inputSchema : { type: "object", properties: {} };
|
|
6720
|
+
out.push({ name, description, inputSchema: inputSchema20 });
|
|
6696
6721
|
}
|
|
6697
6722
|
return out;
|
|
6698
6723
|
}
|
|
@@ -7343,15 +7368,15 @@ var Hippocampus = class _Hippocampus {
|
|
|
7343
7368
|
return (content ?? "").trim();
|
|
7344
7369
|
}
|
|
7345
7370
|
// ---------- encode (write) ----------
|
|
7346
|
-
async encodeRule(
|
|
7371
|
+
async encodeRule(text2, kind, confidence = "medium", source = "llm") {
|
|
7347
7372
|
const ts = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
7348
7373
|
const metadata = `<!-- confidence:${confidence} source:${source} ts:${ts} -->`;
|
|
7349
|
-
const entry = `- ${
|
|
7374
|
+
const entry = `- ${text2} ${metadata}
|
|
7350
7375
|
`;
|
|
7351
7376
|
const sectionHeader = `## ${kind.charAt(0).toUpperCase()}${kind.slice(1)}`;
|
|
7352
7377
|
let content = await this.storage.readFile(this.rulesPath);
|
|
7353
7378
|
if (content === null) content = RULES_SKELETON;
|
|
7354
|
-
if (_Hippocampus.extractEntryTexts(content).has(
|
|
7379
|
+
if (_Hippocampus.extractEntryTexts(content).has(text2)) return;
|
|
7355
7380
|
const lines = content.split(/(?<=\n)/);
|
|
7356
7381
|
const out = [];
|
|
7357
7382
|
let inserted = false;
|
|
@@ -7384,17 +7409,17 @@ ${entry}`);
|
|
|
7384
7409
|
}
|
|
7385
7410
|
await this.storage.writeFile(this.rulesPath, out.join(""));
|
|
7386
7411
|
}
|
|
7387
|
-
async encodeLesson(
|
|
7412
|
+
async encodeLesson(text2, topic = "", source = "llm") {
|
|
7388
7413
|
const ts = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
7389
7414
|
const topicTag = topic ? ` topic:${topic}` : "";
|
|
7390
7415
|
const sourceTag = source !== "llm" ? ` source:${source}` : "";
|
|
7391
|
-
const entry = `- ${
|
|
7416
|
+
const entry = `- ${text2} <!--${topicTag}${sourceTag} ts:${ts} -->
|
|
7392
7417
|
`;
|
|
7393
7418
|
const existing = await this.storage.readFile(this.lessonsPath);
|
|
7394
7419
|
if (existing === null) {
|
|
7395
7420
|
await this.storage.writeFile(this.lessonsPath, `# Lessons
|
|
7396
7421
|
${entry}`);
|
|
7397
|
-
} else if (!_Hippocampus.extractEntryTexts(existing).has(
|
|
7422
|
+
} else if (!_Hippocampus.extractEntryTexts(existing).has(text2)) {
|
|
7398
7423
|
await this.storage.appendFile(this.lessonsPath, entry);
|
|
7399
7424
|
}
|
|
7400
7425
|
if (topic) {
|
|
@@ -7404,7 +7429,7 @@ ${entry}`);
|
|
|
7404
7429
|
if (topicContent === null) {
|
|
7405
7430
|
await this.storage.writeFile(topicPath, `# ${topic}
|
|
7406
7431
|
${entry}`);
|
|
7407
|
-
} else if (!_Hippocampus.extractEntryTexts(topicContent).has(
|
|
7432
|
+
} else if (!_Hippocampus.extractEntryTexts(topicContent).has(text2)) {
|
|
7408
7433
|
await this.storage.appendFile(topicPath, entry);
|
|
7409
7434
|
}
|
|
7410
7435
|
}
|
|
@@ -7460,12 +7485,12 @@ ${entries.map((e) => `- ${e}`).join("\n")}
|
|
|
7460
7485
|
}
|
|
7461
7486
|
/** Sanitize a topic name into a safe file slug. */
|
|
7462
7487
|
static sanitizeSlug(name) {
|
|
7463
|
-
let
|
|
7464
|
-
|
|
7465
|
-
|
|
7466
|
-
|
|
7467
|
-
|
|
7468
|
-
return
|
|
7488
|
+
let text2 = name.toLowerCase().trim();
|
|
7489
|
+
text2 = text2.replace(/[^a-z0-9\s_-]/g, "");
|
|
7490
|
+
text2 = text2.replace(/\s+/g, "-");
|
|
7491
|
+
text2 = text2.replace(/-+/g, "-");
|
|
7492
|
+
text2 = text2.replace(/^-|-$/g, "");
|
|
7493
|
+
return text2.length > 0 ? text2 : "general";
|
|
7469
7494
|
}
|
|
7470
7495
|
};
|
|
7471
7496
|
|
|
@@ -7497,13 +7522,13 @@ function createSmartMemory(options) {
|
|
|
7497
7522
|
if (!readsEnabled) return "";
|
|
7498
7523
|
return hippocampus.recallTopic(slug);
|
|
7499
7524
|
},
|
|
7500
|
-
async encodeRule(
|
|
7525
|
+
async encodeRule(text2, kind, confidence, source) {
|
|
7501
7526
|
if (!writesEnabled) return;
|
|
7502
|
-
await hippocampus.encodeRule(
|
|
7527
|
+
await hippocampus.encodeRule(text2, kind, confidence, source);
|
|
7503
7528
|
},
|
|
7504
|
-
async encodeLesson(
|
|
7529
|
+
async encodeLesson(text2, topic, source) {
|
|
7505
7530
|
if (!writesEnabled) return;
|
|
7506
|
-
await hippocampus.encodeLesson(
|
|
7531
|
+
await hippocampus.encodeLesson(text2, topic, source);
|
|
7507
7532
|
},
|
|
7508
7533
|
async rewriteIdentity(entries) {
|
|
7509
7534
|
if (!writesEnabled) return;
|
|
@@ -7869,8 +7894,8 @@ function buildSchemaPrompt(schema) {
|
|
|
7869
7894
|
}
|
|
7870
7895
|
return lines.join("\n");
|
|
7871
7896
|
}
|
|
7872
|
-
function tryParseJSON2(
|
|
7873
|
-
const trimmed =
|
|
7897
|
+
function tryParseJSON2(text2) {
|
|
7898
|
+
const trimmed = text2.trim();
|
|
7874
7899
|
try {
|
|
7875
7900
|
return { ok: true, value: JSON.parse(trimmed) };
|
|
7876
7901
|
} catch {
|
|
@@ -7998,7 +8023,7 @@ function createApiCallTool(opts) {
|
|
|
7998
8023
|
}
|
|
7999
8024
|
const fetchFn = opts.fetch ?? globalThis.fetch.bind(globalThis);
|
|
8000
8025
|
const maxResponseBytes = opts.maxResponseBytes ?? DEFAULT_MAX_RESPONSE_BYTES;
|
|
8001
|
-
const
|
|
8026
|
+
const inputSchema20 = import_zod25.z.object({
|
|
8002
8027
|
service: import_zod25.z.enum(serviceNames),
|
|
8003
8028
|
method: import_zod25.z.enum(["GET", "POST", "PUT", "PATCH", "DELETE"]),
|
|
8004
8029
|
path: import_zod25.z.string().regex(/^\//, "path must start with /"),
|
|
@@ -8010,7 +8035,7 @@ function createApiCallTool(opts) {
|
|
|
8010
8035
|
return defineTool({
|
|
8011
8036
|
name: opts.toolName ?? "ApiCall",
|
|
8012
8037
|
description,
|
|
8013
|
-
inputSchema:
|
|
8038
|
+
inputSchema: inputSchema20,
|
|
8014
8039
|
execute: async (input) => {
|
|
8015
8040
|
const svc = services.get(input.service);
|
|
8016
8041
|
if (!svc) {
|
|
@@ -8171,6 +8196,569 @@ async function invokeHook(hook, event) {
|
|
|
8171
8196
|
// src/engine/engine.ts
|
|
8172
8197
|
init_fetchData();
|
|
8173
8198
|
|
|
8199
|
+
// src/tools/searchKnowledge.ts
|
|
8200
|
+
init_cjs_shims();
|
|
8201
|
+
var import_zod26 = require("zod");
|
|
8202
|
+
init_contract();
|
|
8203
|
+
|
|
8204
|
+
// src/knowledge/scope.ts
|
|
8205
|
+
init_cjs_shims();
|
|
8206
|
+
var SAFE_PATH_RE = /^[a-zA-Z0-9_\-./]+$/;
|
|
8207
|
+
function parseFolderRef(raw) {
|
|
8208
|
+
if (typeof raw !== "string" || raw.length === 0) {
|
|
8209
|
+
throw new Error(`invalid knowledge folder ref: empty`);
|
|
8210
|
+
}
|
|
8211
|
+
if (raw.startsWith("/")) {
|
|
8212
|
+
throw new Error(`invalid knowledge folder ref: absolute paths not allowed ("${raw}")`);
|
|
8213
|
+
}
|
|
8214
|
+
const trimmed = raw.replace(/\/+$/g, "");
|
|
8215
|
+
if (trimmed.length === 0) {
|
|
8216
|
+
throw new Error(`invalid knowledge folder ref: "${raw}"`);
|
|
8217
|
+
}
|
|
8218
|
+
if (!SAFE_PATH_RE.test(trimmed)) {
|
|
8219
|
+
throw new Error(`invalid knowledge folder ref: unsafe characters in "${raw}"`);
|
|
8220
|
+
}
|
|
8221
|
+
if (trimmed.split("/").some((seg) => seg === ".." || seg === "." || seg === "")) {
|
|
8222
|
+
throw new Error(`invalid knowledge folder ref: traversal in "${raw}"`);
|
|
8223
|
+
}
|
|
8224
|
+
const segs = trimmed.split("/");
|
|
8225
|
+
const base = segs[0];
|
|
8226
|
+
const subPath = segs.slice(1).join("/");
|
|
8227
|
+
return { path: trimmed, base, subPath };
|
|
8228
|
+
}
|
|
8229
|
+
function relPathInScope(folder, relPath) {
|
|
8230
|
+
if (folder.subPath === "") return true;
|
|
8231
|
+
return relPath === folder.subPath || relPath.startsWith(`${folder.subPath}/`);
|
|
8232
|
+
}
|
|
8233
|
+
function parseKnowledgeRef(raw) {
|
|
8234
|
+
if (typeof raw !== "string" || raw.length === 0) {
|
|
8235
|
+
throw new Error("invalid knowledge ref: empty");
|
|
8236
|
+
}
|
|
8237
|
+
if (raw.startsWith("ext:")) {
|
|
8238
|
+
const name = raw.slice("ext:".length);
|
|
8239
|
+
if (!/^[a-zA-Z0-9_-]+$/.test(name) || name.length === 0) {
|
|
8240
|
+
throw new Error(`invalid knowledge ref: external name "${name}" has unsafe characters`);
|
|
8241
|
+
}
|
|
8242
|
+
return { kind: "ext", target: name };
|
|
8243
|
+
}
|
|
8244
|
+
if (raw.startsWith("/")) {
|
|
8245
|
+
throw new Error(`invalid knowledge ref: absolute paths not allowed ("${raw}")`);
|
|
8246
|
+
}
|
|
8247
|
+
const hashAt = raw.indexOf("#");
|
|
8248
|
+
const filePath = hashAt === -1 ? raw : raw.slice(0, hashAt);
|
|
8249
|
+
const section = hashAt === -1 ? void 0 : raw.slice(hashAt + 1);
|
|
8250
|
+
if (!SAFE_PATH_RE.test(filePath)) {
|
|
8251
|
+
throw new Error(`invalid knowledge ref: unsafe characters in "${filePath}"`);
|
|
8252
|
+
}
|
|
8253
|
+
if (filePath.split("/").some((seg) => seg === ".." || seg === "." || seg === "")) {
|
|
8254
|
+
throw new Error(`invalid knowledge ref: traversal in "${filePath}"`);
|
|
8255
|
+
}
|
|
8256
|
+
if (section !== void 0) {
|
|
8257
|
+
if (section.length > 0 && !/^[a-zA-Z0-9_-]+$/.test(section)) {
|
|
8258
|
+
throw new Error(`invalid knowledge ref: unsafe characters in section "${section}"`);
|
|
8259
|
+
}
|
|
8260
|
+
return { kind: "section", target: filePath, section };
|
|
8261
|
+
}
|
|
8262
|
+
return { kind: "file", target: filePath };
|
|
8263
|
+
}
|
|
8264
|
+
function refInScope(folders, filePath) {
|
|
8265
|
+
return folders.some((f) => {
|
|
8266
|
+
if (filePath === f.base || filePath.startsWith(`${f.base}/`)) {
|
|
8267
|
+
const relInBase = filePath === f.base ? "" : filePath.slice(f.base.length + 1);
|
|
8268
|
+
return relPathInScope(f, relInBase);
|
|
8269
|
+
}
|
|
8270
|
+
return false;
|
|
8271
|
+
});
|
|
8272
|
+
}
|
|
8273
|
+
|
|
8274
|
+
// src/knowledge/tokenize.ts
|
|
8275
|
+
init_cjs_shims();
|
|
8276
|
+
var STOP_WORDS = /* @__PURE__ */ new Set([
|
|
8277
|
+
"the",
|
|
8278
|
+
"and",
|
|
8279
|
+
"of",
|
|
8280
|
+
"to",
|
|
8281
|
+
"a",
|
|
8282
|
+
"in",
|
|
8283
|
+
"is",
|
|
8284
|
+
"it",
|
|
8285
|
+
"you",
|
|
8286
|
+
"that",
|
|
8287
|
+
"was",
|
|
8288
|
+
"for",
|
|
8289
|
+
"on",
|
|
8290
|
+
"are",
|
|
8291
|
+
"with",
|
|
8292
|
+
"as",
|
|
8293
|
+
"this",
|
|
8294
|
+
"by",
|
|
8295
|
+
"from",
|
|
8296
|
+
"or",
|
|
8297
|
+
"but",
|
|
8298
|
+
"not",
|
|
8299
|
+
"all",
|
|
8300
|
+
"an",
|
|
8301
|
+
"has",
|
|
8302
|
+
"have",
|
|
8303
|
+
"had",
|
|
8304
|
+
"will",
|
|
8305
|
+
"can",
|
|
8306
|
+
"do",
|
|
8307
|
+
"did",
|
|
8308
|
+
"be",
|
|
8309
|
+
"been",
|
|
8310
|
+
"being",
|
|
8311
|
+
"your",
|
|
8312
|
+
"our",
|
|
8313
|
+
"their",
|
|
8314
|
+
"his",
|
|
8315
|
+
"her",
|
|
8316
|
+
"my",
|
|
8317
|
+
"we",
|
|
8318
|
+
"they",
|
|
8319
|
+
"them",
|
|
8320
|
+
"than",
|
|
8321
|
+
"so",
|
|
8322
|
+
"if",
|
|
8323
|
+
"at",
|
|
8324
|
+
"no",
|
|
8325
|
+
"yes",
|
|
8326
|
+
"me",
|
|
8327
|
+
"us",
|
|
8328
|
+
"i",
|
|
8329
|
+
"he",
|
|
8330
|
+
"she"
|
|
8331
|
+
]);
|
|
8332
|
+
function tokenize(text2) {
|
|
8333
|
+
if (typeof text2 !== "string" || text2.length === 0) return [];
|
|
8334
|
+
const seen = /* @__PURE__ */ new Set();
|
|
8335
|
+
for (const raw of text2.toLowerCase().split(/[\W_]+/)) {
|
|
8336
|
+
if (raw.length < 2) continue;
|
|
8337
|
+
if (STOP_WORDS.has(raw)) continue;
|
|
8338
|
+
seen.add(raw);
|
|
8339
|
+
}
|
|
8340
|
+
return [...seen].sort();
|
|
8341
|
+
}
|
|
8342
|
+
function scoreOverlap(sectionWords, queryTokens) {
|
|
8343
|
+
const set = sectionWords instanceof Set ? sectionWords : new Set(sectionWords);
|
|
8344
|
+
let n = 0;
|
|
8345
|
+
for (const t of queryTokens) if (set.has(t)) n++;
|
|
8346
|
+
return n;
|
|
8347
|
+
}
|
|
8348
|
+
|
|
8349
|
+
// src/tools/searchKnowledge.ts
|
|
8350
|
+
var DEFAULT_MAX_RESULTS = 5;
|
|
8351
|
+
var inputSchema18 = import_zod26.z.object({
|
|
8352
|
+
query: import_zod26.z.string().min(1),
|
|
8353
|
+
maxResults: import_zod26.z.number().int().positive().optional()
|
|
8354
|
+
});
|
|
8355
|
+
function createSearchKnowledgeTool(opts) {
|
|
8356
|
+
const scoped = opts.folders.map(parseFolderRef);
|
|
8357
|
+
const indexCache = /* @__PURE__ */ new Map();
|
|
8358
|
+
const cap = opts.maxSearchResults ?? DEFAULT_MAX_RESULTS;
|
|
8359
|
+
const externals = opts.external;
|
|
8360
|
+
return defineTool({
|
|
8361
|
+
name: "SearchKnowledge",
|
|
8362
|
+
description: "Search the agent's knowledge base. Returns up to K ranked snippets matching the query (across all configured knowledge folders + any attached external file links). Each result carries a `ref` you can pass back to the `ReadKnowledge` tool to load the full content of that section or file.",
|
|
8363
|
+
inputSchema: inputSchema18,
|
|
8364
|
+
execute: async ({ query, maxResults }) => {
|
|
8365
|
+
const limit = Math.min(maxResults ?? cap, cap);
|
|
8366
|
+
const queryTokens = tokenize(query);
|
|
8367
|
+
if (queryTokens.length === 0) {
|
|
8368
|
+
return { content: "no searchable tokens in query", isError: false };
|
|
8369
|
+
}
|
|
8370
|
+
const sectionHits = [];
|
|
8371
|
+
const seenBases = /* @__PURE__ */ new Set();
|
|
8372
|
+
for (const folder of scoped) {
|
|
8373
|
+
if (!seenBases.has(folder.base)) {
|
|
8374
|
+
seenBases.add(folder.base);
|
|
8375
|
+
}
|
|
8376
|
+
}
|
|
8377
|
+
for (const baseName of seenBases) {
|
|
8378
|
+
const idx = await loadIndex(opts.adapter, baseName, indexCache);
|
|
8379
|
+
if (idx === null) continue;
|
|
8380
|
+
for (const section of idx.sections) {
|
|
8381
|
+
const eligible = scoped.some(
|
|
8382
|
+
(f) => f.base === baseName && relPathInScope(f, section.relPath)
|
|
8383
|
+
);
|
|
8384
|
+
if (!eligible) continue;
|
|
8385
|
+
const score = scoreOverlap(section.words, queryTokens);
|
|
8386
|
+
if (score > 0) sectionHits.push({ section, base: baseName, score });
|
|
8387
|
+
}
|
|
8388
|
+
}
|
|
8389
|
+
const externalHits = [];
|
|
8390
|
+
for (const link of externals) {
|
|
8391
|
+
const score = scoreOverlap(tokenize(link.description), queryTokens);
|
|
8392
|
+
if (score > 0) externalHits.push({ link, score });
|
|
8393
|
+
}
|
|
8394
|
+
const all = [
|
|
8395
|
+
...sectionHits.map((h) => ({
|
|
8396
|
+
kind: "knowledge",
|
|
8397
|
+
score: h.score,
|
|
8398
|
+
render: () => `[knowledge] ${h.base}/${h.section.relPath} \xA7"${h.section.heading || "(lead-in)"}"
|
|
8399
|
+
${truncatePreview(h.section.preview)}
|
|
8400
|
+
ref: ${h.base}/${h.section.slug}`
|
|
8401
|
+
})),
|
|
8402
|
+
...externalHits.map((h) => ({
|
|
8403
|
+
kind: "external",
|
|
8404
|
+
score: h.score,
|
|
8405
|
+
render: () => `[external] ${h.link.name} (${h.link.format})
|
|
8406
|
+
${h.link.description}
|
|
8407
|
+
ref: ext:${h.link.name}`
|
|
8408
|
+
}))
|
|
8409
|
+
];
|
|
8410
|
+
if (all.length === 0) {
|
|
8411
|
+
return { content: `no knowledge matches for "${query}"`, isError: false };
|
|
8412
|
+
}
|
|
8413
|
+
all.sort((a, b) => b.score - a.score);
|
|
8414
|
+
const top = all.slice(0, limit);
|
|
8415
|
+
const body = top.map((h, i) => `${i + 1}. ${h.render()}`).join("\n\n");
|
|
8416
|
+
return {
|
|
8417
|
+
content: `Found ${top.length} knowledge match${top.length === 1 ? "" : "es"} (of ${all.length} ranked):
|
|
8418
|
+
|
|
8419
|
+
${body}`,
|
|
8420
|
+
isError: false,
|
|
8421
|
+
metadata: { hits: all.length, returned: top.length }
|
|
8422
|
+
};
|
|
8423
|
+
}
|
|
8424
|
+
});
|
|
8425
|
+
}
|
|
8426
|
+
function truncatePreview(s) {
|
|
8427
|
+
if (s.length <= 200) return s;
|
|
8428
|
+
return s.slice(0, 200) + "\u2026";
|
|
8429
|
+
}
|
|
8430
|
+
async function loadIndex(adapter, base, cache) {
|
|
8431
|
+
const cached2 = cache.get(base);
|
|
8432
|
+
if (cached2 !== void 0) return cached2;
|
|
8433
|
+
let raw;
|
|
8434
|
+
try {
|
|
8435
|
+
raw = await adapter.readFile(`${base}/_index.json`);
|
|
8436
|
+
} catch {
|
|
8437
|
+
return null;
|
|
8438
|
+
}
|
|
8439
|
+
if (raw === null) return null;
|
|
8440
|
+
try {
|
|
8441
|
+
const parsed = JSON.parse(raw);
|
|
8442
|
+
cache.set(base, parsed);
|
|
8443
|
+
return parsed;
|
|
8444
|
+
} catch {
|
|
8445
|
+
return null;
|
|
8446
|
+
}
|
|
8447
|
+
}
|
|
8448
|
+
|
|
8449
|
+
// src/tools/readKnowledge.ts
|
|
8450
|
+
init_cjs_shims();
|
|
8451
|
+
var import_zod27 = require("zod");
|
|
8452
|
+
init_contract();
|
|
8453
|
+
|
|
8454
|
+
// src/knowledge/extractors.ts
|
|
8455
|
+
init_cjs_shims();
|
|
8456
|
+
var CSV_MAX_ROWS = 100;
|
|
8457
|
+
var HTML_TAG_RE = /<[^>]+>/g;
|
|
8458
|
+
var HTML_ENTITIES = {
|
|
8459
|
+
"&": "&",
|
|
8460
|
+
"<": "<",
|
|
8461
|
+
">": ">",
|
|
8462
|
+
""": '"',
|
|
8463
|
+
"'": "'",
|
|
8464
|
+
" ": " "
|
|
8465
|
+
};
|
|
8466
|
+
function decodeHtmlEntities(s) {
|
|
8467
|
+
return s.replace(/&(amp|lt|gt|quot|#39|nbsp);/g, (m) => HTML_ENTITIES[m] ?? m);
|
|
8468
|
+
}
|
|
8469
|
+
var text = {
|
|
8470
|
+
format: "txt",
|
|
8471
|
+
requiresNode: false,
|
|
8472
|
+
extract: async (raw) => raw
|
|
8473
|
+
};
|
|
8474
|
+
var md = {
|
|
8475
|
+
format: "md",
|
|
8476
|
+
requiresNode: false,
|
|
8477
|
+
extract: async (raw) => raw
|
|
8478
|
+
};
|
|
8479
|
+
var json = {
|
|
8480
|
+
format: "json",
|
|
8481
|
+
requiresNode: false,
|
|
8482
|
+
extract: async (raw) => {
|
|
8483
|
+
try {
|
|
8484
|
+
return JSON.stringify(JSON.parse(raw), null, 2);
|
|
8485
|
+
} catch {
|
|
8486
|
+
return raw;
|
|
8487
|
+
}
|
|
8488
|
+
}
|
|
8489
|
+
};
|
|
8490
|
+
var csv = {
|
|
8491
|
+
format: "csv",
|
|
8492
|
+
requiresNode: false,
|
|
8493
|
+
extract: async (raw) => {
|
|
8494
|
+
const lines = raw.split(/\r?\n/).filter((l) => l.length > 0);
|
|
8495
|
+
if (lines.length === 0) return "";
|
|
8496
|
+
if (lines.length <= CSV_MAX_ROWS + 1) return lines.join("\n");
|
|
8497
|
+
const head = lines.slice(0, CSV_MAX_ROWS + 1);
|
|
8498
|
+
const remaining = lines.length - head.length;
|
|
8499
|
+
return `${head.join("\n")}
|
|
8500
|
+
\u2026[${remaining.toLocaleString()} more row${remaining === 1 ? "" : "s"} truncated]`;
|
|
8501
|
+
}
|
|
8502
|
+
};
|
|
8503
|
+
var html = {
|
|
8504
|
+
format: "html",
|
|
8505
|
+
requiresNode: false,
|
|
8506
|
+
extract: async (raw) => {
|
|
8507
|
+
const stripped = raw.replace(/<script\b[^>]*>[\s\S]*?<\/script>/gi, "").replace(/<style\b[^>]*>[\s\S]*?<\/style>/gi, "").replace(HTML_TAG_RE, " ");
|
|
8508
|
+
return decodeHtmlEntities(stripped).replace(/\s+/g, " ").trim();
|
|
8509
|
+
}
|
|
8510
|
+
};
|
|
8511
|
+
var pdf = {
|
|
8512
|
+
format: "pdf",
|
|
8513
|
+
requiresNode: true,
|
|
8514
|
+
extract: async (raw) => {
|
|
8515
|
+
let pdfParse;
|
|
8516
|
+
try {
|
|
8517
|
+
const mod = await import("pdf-parse");
|
|
8518
|
+
pdfParse = mod.default ?? mod;
|
|
8519
|
+
} catch {
|
|
8520
|
+
throw new Error(
|
|
8521
|
+
"ERR_KNOWLEDGE_FORMAT_UNSUPPORTED: pdf-parse is not installed. Run `npm install pdf-parse` to enable PDF extraction."
|
|
8522
|
+
);
|
|
8523
|
+
}
|
|
8524
|
+
const buf = Buffer.from(raw, "binary");
|
|
8525
|
+
const result = await pdfParse(buf);
|
|
8526
|
+
return result.text;
|
|
8527
|
+
}
|
|
8528
|
+
};
|
|
8529
|
+
var docx = {
|
|
8530
|
+
format: "docx",
|
|
8531
|
+
requiresNode: true,
|
|
8532
|
+
extract: async (raw) => {
|
|
8533
|
+
let mammoth;
|
|
8534
|
+
try {
|
|
8535
|
+
mammoth = await import("mammoth");
|
|
8536
|
+
} catch {
|
|
8537
|
+
throw new Error(
|
|
8538
|
+
"ERR_KNOWLEDGE_FORMAT_UNSUPPORTED: mammoth is not installed. Run `npm install mammoth` to enable DOCX extraction."
|
|
8539
|
+
);
|
|
8540
|
+
}
|
|
8541
|
+
const buf = Buffer.from(raw, "binary");
|
|
8542
|
+
const result = await mammoth.extractRawText({ buffer: buf });
|
|
8543
|
+
return result.value;
|
|
8544
|
+
}
|
|
8545
|
+
};
|
|
8546
|
+
var EXTRACTORS = {
|
|
8547
|
+
md,
|
|
8548
|
+
txt: text,
|
|
8549
|
+
json,
|
|
8550
|
+
csv,
|
|
8551
|
+
html,
|
|
8552
|
+
pdf,
|
|
8553
|
+
docx
|
|
8554
|
+
};
|
|
8555
|
+
function getExtractor(format) {
|
|
8556
|
+
const e = EXTRACTORS[format];
|
|
8557
|
+
if (e === void 0) throw new Error(`ERR_KNOWLEDGE_FORMAT_UNSUPPORTED: ${format}`);
|
|
8558
|
+
return e;
|
|
8559
|
+
}
|
|
8560
|
+
|
|
8561
|
+
// src/tools/readKnowledge.ts
|
|
8562
|
+
var DEFAULT_MAX_READ_BYTES = 1e4;
|
|
8563
|
+
var inputSchema19 = import_zod27.z.object({
|
|
8564
|
+
ref: import_zod27.z.string().min(1)
|
|
8565
|
+
});
|
|
8566
|
+
function createReadKnowledgeTool(opts) {
|
|
8567
|
+
const scoped = opts.folders.map(parseFolderRef);
|
|
8568
|
+
const indexCache = /* @__PURE__ */ new Map();
|
|
8569
|
+
const fileCache = /* @__PURE__ */ new Map();
|
|
8570
|
+
const cap = opts.maxReadBytes ?? DEFAULT_MAX_READ_BYTES;
|
|
8571
|
+
const externalsByName = new Map(opts.external.map((e) => [e.name, e]));
|
|
8572
|
+
const fetchFn = opts.fetch ?? globalThis.fetch.bind(globalThis);
|
|
8573
|
+
return defineTool({
|
|
8574
|
+
name: "ReadKnowledge",
|
|
8575
|
+
description: "Load the full content of a knowledge ref returned by SearchKnowledge. Use the `ref` value verbatim from the search results \u2014 section refs return one section, file refs return the whole file (with format-specific extraction for non-markdown formats), and `ext:{name}` refs fetch a pre-registered external file.",
|
|
8576
|
+
inputSchema: inputSchema19,
|
|
8577
|
+
execute: async ({ ref }) => {
|
|
8578
|
+
let parsed;
|
|
8579
|
+
try {
|
|
8580
|
+
parsed = parseKnowledgeRef(ref);
|
|
8581
|
+
} catch (err) {
|
|
8582
|
+
return {
|
|
8583
|
+
content: `ERR_KNOWLEDGE_REF_INVALID: ${err instanceof Error ? err.message : String(err)}`,
|
|
8584
|
+
isError: true
|
|
8585
|
+
};
|
|
8586
|
+
}
|
|
8587
|
+
if (parsed.kind === "ext") {
|
|
8588
|
+
return readExternal(parsed.target, externalsByName, fetchFn, cap);
|
|
8589
|
+
}
|
|
8590
|
+
if (!refInScope(scoped, parsed.target)) {
|
|
8591
|
+
return {
|
|
8592
|
+
content: `ERR_KNOWLEDGE_FOLDER_NOT_ALLOWED: ref "${parsed.target}" is outside the run's allowed folders`,
|
|
8593
|
+
isError: true
|
|
8594
|
+
};
|
|
8595
|
+
}
|
|
8596
|
+
const base = parsed.target.split("/")[0];
|
|
8597
|
+
const relPath = parsed.target.slice(base.length + 1);
|
|
8598
|
+
if (parsed.kind === "section") {
|
|
8599
|
+
const idx = await loadIndex2(opts.adapter, base, indexCache);
|
|
8600
|
+
if (idx === null) {
|
|
8601
|
+
return {
|
|
8602
|
+
content: `ERR_KNOWLEDGE_INDEX_MISSING: no _index.json for base "${base}"`,
|
|
8603
|
+
isError: true
|
|
8604
|
+
};
|
|
8605
|
+
}
|
|
8606
|
+
const sectionSlug = parsed.section ?? "";
|
|
8607
|
+
const section = idx.sections.find(
|
|
8608
|
+
(s) => s.relPath === relPath && extractAnchor(s.slug) === sectionSlug
|
|
8609
|
+
);
|
|
8610
|
+
if (section === void 0) {
|
|
8611
|
+
return {
|
|
8612
|
+
content: `ERR_KNOWLEDGE_REF_NOT_FOUND: section ${parsed.target}#${sectionSlug} not in index for "${base}"`,
|
|
8613
|
+
isError: true
|
|
8614
|
+
};
|
|
8615
|
+
}
|
|
8616
|
+
const fullContent = await readFile(opts.adapter, parsed.target, fileCache);
|
|
8617
|
+
if (fullContent === null) {
|
|
8618
|
+
return {
|
|
8619
|
+
content: `ERR_KNOWLEDGE_REF_NOT_FOUND: file ${parsed.target} not found in storage`,
|
|
8620
|
+
isError: true
|
|
8621
|
+
};
|
|
8622
|
+
}
|
|
8623
|
+
const sliced = sliceLines(fullContent, section.startLine, section.endLine);
|
|
8624
|
+
return wrapResult(`[knowledge] ${parsed.target}#${sectionSlug}`, sliced, cap);
|
|
8625
|
+
}
|
|
8626
|
+
const fmt = inferFormat(parsed.target);
|
|
8627
|
+
if (fmt === null) {
|
|
8628
|
+
return {
|
|
8629
|
+
content: `ERR_KNOWLEDGE_FORMAT_UNSUPPORTED: cannot infer format for "${parsed.target}"`,
|
|
8630
|
+
isError: true
|
|
8631
|
+
};
|
|
8632
|
+
}
|
|
8633
|
+
const raw = await readFile(opts.adapter, parsed.target, fileCache);
|
|
8634
|
+
if (raw === null) {
|
|
8635
|
+
return {
|
|
8636
|
+
content: `ERR_KNOWLEDGE_REF_NOT_FOUND: file ${parsed.target} not found in storage`,
|
|
8637
|
+
isError: true
|
|
8638
|
+
};
|
|
8639
|
+
}
|
|
8640
|
+
let extracted;
|
|
8641
|
+
try {
|
|
8642
|
+
extracted = await getExtractor(fmt).extract(raw);
|
|
8643
|
+
} catch (err) {
|
|
8644
|
+
return {
|
|
8645
|
+
content: `ERR_KNOWLEDGE_EXTRACTOR_FAILED: ${err instanceof Error ? err.message : String(err)}`,
|
|
8646
|
+
isError: true
|
|
8647
|
+
};
|
|
8648
|
+
}
|
|
8649
|
+
return wrapResult(`[knowledge] ${parsed.target} (${fmt})`, extracted, cap);
|
|
8650
|
+
}
|
|
8651
|
+
});
|
|
8652
|
+
}
|
|
8653
|
+
async function readExternal(name, registry, fetchFn, cap) {
|
|
8654
|
+
const link = registry.get(name);
|
|
8655
|
+
if (link === void 0) {
|
|
8656
|
+
return {
|
|
8657
|
+
content: `ERR_KNOWLEDGE_REF_NOT_FOUND: no external link named "${name}"`,
|
|
8658
|
+
isError: true
|
|
8659
|
+
};
|
|
8660
|
+
}
|
|
8661
|
+
let res;
|
|
8662
|
+
try {
|
|
8663
|
+
res = await fetchFn(link.url, {
|
|
8664
|
+
headers: link.headers ?? {}
|
|
8665
|
+
});
|
|
8666
|
+
} catch (err) {
|
|
8667
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
8668
|
+
return {
|
|
8669
|
+
content: `ERR_KNOWLEDGE_NETWORK: ${msg.slice(0, 200)}`,
|
|
8670
|
+
isError: true
|
|
8671
|
+
};
|
|
8672
|
+
}
|
|
8673
|
+
if (!res.ok) {
|
|
8674
|
+
return {
|
|
8675
|
+
content: `ERR_KNOWLEDGE_NETWORK: HTTP ${res.status} fetching ext:${name}`,
|
|
8676
|
+
isError: true
|
|
8677
|
+
};
|
|
8678
|
+
}
|
|
8679
|
+
const raw = await res.text();
|
|
8680
|
+
let extracted;
|
|
8681
|
+
try {
|
|
8682
|
+
extracted = await getExtractor(link.format).extract(raw);
|
|
8683
|
+
} catch (err) {
|
|
8684
|
+
return {
|
|
8685
|
+
content: `ERR_KNOWLEDGE_EXTRACTOR_FAILED: ${err instanceof Error ? err.message : String(err)}`,
|
|
8686
|
+
isError: true
|
|
8687
|
+
};
|
|
8688
|
+
}
|
|
8689
|
+
return wrapResult(`[external] ${name} (${link.format})`, extracted, cap);
|
|
8690
|
+
}
|
|
8691
|
+
function wrapResult(header, body, cap) {
|
|
8692
|
+
let payload = body;
|
|
8693
|
+
if (body.length > cap) {
|
|
8694
|
+
payload = body.slice(0, cap) + `
|
|
8695
|
+
\u2026[+${body.length - cap} more chars truncated]`;
|
|
8696
|
+
}
|
|
8697
|
+
return {
|
|
8698
|
+
content: `${header}
|
|
8699
|
+
|
|
8700
|
+
${payload}`,
|
|
8701
|
+
isError: false,
|
|
8702
|
+
metadata: { bytes: body.length }
|
|
8703
|
+
};
|
|
8704
|
+
}
|
|
8705
|
+
async function loadIndex2(adapter, base, cache) {
|
|
8706
|
+
const cached2 = cache.get(base);
|
|
8707
|
+
if (cached2 !== void 0) return cached2;
|
|
8708
|
+
let raw;
|
|
8709
|
+
try {
|
|
8710
|
+
raw = await adapter.readFile(`${base}/_index.json`);
|
|
8711
|
+
} catch {
|
|
8712
|
+
return null;
|
|
8713
|
+
}
|
|
8714
|
+
if (raw === null) return null;
|
|
8715
|
+
try {
|
|
8716
|
+
const idx = JSON.parse(raw);
|
|
8717
|
+
cache.set(base, idx);
|
|
8718
|
+
return idx;
|
|
8719
|
+
} catch {
|
|
8720
|
+
return null;
|
|
8721
|
+
}
|
|
8722
|
+
}
|
|
8723
|
+
async function readFile(adapter, path, cache) {
|
|
8724
|
+
const cached2 = cache.get(path);
|
|
8725
|
+
if (cached2 !== void 0) return cached2;
|
|
8726
|
+
const content = await adapter.readFile(path).catch(() => null);
|
|
8727
|
+
if (content === null) return null;
|
|
8728
|
+
cache.set(path, content);
|
|
8729
|
+
return content;
|
|
8730
|
+
}
|
|
8731
|
+
function sliceLines(content, start, end) {
|
|
8732
|
+
const lines = content.split(/\r?\n/);
|
|
8733
|
+
return lines.slice(start - 1, end).join("\n");
|
|
8734
|
+
}
|
|
8735
|
+
function extractAnchor(slug) {
|
|
8736
|
+
const i = slug.indexOf("#");
|
|
8737
|
+
return i === -1 ? "" : slug.slice(i + 1);
|
|
8738
|
+
}
|
|
8739
|
+
function inferFormat(filePath) {
|
|
8740
|
+
const ext = (filePath.split(".").pop() ?? "").toLowerCase();
|
|
8741
|
+
switch (ext) {
|
|
8742
|
+
case "md":
|
|
8743
|
+
case "markdown":
|
|
8744
|
+
return "md";
|
|
8745
|
+
case "txt":
|
|
8746
|
+
return "txt";
|
|
8747
|
+
case "json":
|
|
8748
|
+
return "json";
|
|
8749
|
+
case "csv":
|
|
8750
|
+
return "csv";
|
|
8751
|
+
case "html":
|
|
8752
|
+
case "htm":
|
|
8753
|
+
return "html";
|
|
8754
|
+
case "pdf":
|
|
8755
|
+
return "pdf";
|
|
8756
|
+
case "docx":
|
|
8757
|
+
return "docx";
|
|
8758
|
+
}
|
|
8759
|
+
return null;
|
|
8760
|
+
}
|
|
8761
|
+
|
|
8174
8762
|
// src/skills/storageSkillSource.ts
|
|
8175
8763
|
init_cjs_shims();
|
|
8176
8764
|
var SAFE_NAME4 = /^[a-zA-Z0-9_-]+$/;
|
|
@@ -8310,9 +8898,9 @@ var InlineSkillSource = class {
|
|
|
8310
8898
|
`InlineSkillSource: fetch ${url} \u2192 HTTP ${String(response.status)} ${response.statusText}`
|
|
8311
8899
|
);
|
|
8312
8900
|
}
|
|
8313
|
-
const
|
|
8314
|
-
this.cache.set(cacheKey,
|
|
8315
|
-
return
|
|
8901
|
+
const text2 = await response.text();
|
|
8902
|
+
this.cache.set(cacheKey, text2);
|
|
8903
|
+
return text2;
|
|
8316
8904
|
}
|
|
8317
8905
|
assertUrlAllowed(url) {
|
|
8318
8906
|
const hosts = this.allowedHosts;
|
|
@@ -8784,47 +9372,60 @@ var R2BindingStorageAdapter = class {
|
|
|
8784
9372
|
// src/storage/factory.ts
|
|
8785
9373
|
var ENGINE_DATA_FOLDER = ".claude";
|
|
8786
9374
|
var WORKSPACES_FOLDER = "workspaces";
|
|
8787
|
-
|
|
9375
|
+
var KNOWLEDGE_FOLDER = "knowledge";
|
|
9376
|
+
async function createEngineStorage(config, options = {}) {
|
|
8788
9377
|
switch (config.provider) {
|
|
8789
9378
|
case "local":
|
|
8790
|
-
return createLocalStorage(config);
|
|
9379
|
+
return createLocalStorage(config, options);
|
|
8791
9380
|
case "r2":
|
|
8792
|
-
return createR2Storage(config);
|
|
9381
|
+
return createR2Storage(config, options);
|
|
8793
9382
|
case "r2-binding":
|
|
8794
|
-
return createR2BindingStorage(config);
|
|
9383
|
+
return createR2BindingStorage(config, options);
|
|
8795
9384
|
}
|
|
8796
9385
|
}
|
|
8797
|
-
async function createLocalStorage(config) {
|
|
9386
|
+
async function createLocalStorage(config, options) {
|
|
8798
9387
|
const path = await import("path");
|
|
8799
|
-
const
|
|
8800
|
-
|
|
8801
|
-
|
|
8802
|
-
|
|
8803
|
-
|
|
8804
|
-
|
|
8805
|
-
|
|
8806
|
-
|
|
8807
|
-
};
|
|
9388
|
+
const tenantRoot = path.join(config.rootPath, WORKSPACES_FOLDER, config.workspaceId);
|
|
9389
|
+
const workspaceRoot = path.join(tenantRoot, ENGINE_DATA_FOLDER);
|
|
9390
|
+
const out = { workspace: new LocalStorageAdapter(workspaceRoot) };
|
|
9391
|
+
if (options.withKnowledge) {
|
|
9392
|
+
const knowledgeRoot = path.join(tenantRoot, KNOWLEDGE_FOLDER);
|
|
9393
|
+
return { ...out, knowledge: new LocalStorageAdapter(knowledgeRoot) };
|
|
9394
|
+
}
|
|
9395
|
+
return out;
|
|
8808
9396
|
}
|
|
8809
|
-
function createR2Storage(config) {
|
|
9397
|
+
function createR2Storage(config, options) {
|
|
8810
9398
|
if (!config.r2) {
|
|
8811
9399
|
throw new StorageError('storage.r2 is required when storage.provider === "r2"');
|
|
8812
9400
|
}
|
|
8813
9401
|
const rootPrefix = config.rootPath.replace(/^\/+|\/+$/g, "");
|
|
8814
|
-
const
|
|
8815
|
-
|
|
8816
|
-
|
|
8817
|
-
|
|
9402
|
+
const tenantPrefix = `${rootPrefix}/${WORKSPACES_FOLDER}/${config.workspaceId}`;
|
|
9403
|
+
const workspacePrefix = `${tenantPrefix}/${ENGINE_DATA_FOLDER}`;
|
|
9404
|
+
const out = { workspace: new R2StorageAdapter(config.r2, workspacePrefix) };
|
|
9405
|
+
if (options.withKnowledge) {
|
|
9406
|
+
const knowledgePrefix = `${tenantPrefix}/${KNOWLEDGE_FOLDER}`;
|
|
9407
|
+
return { ...out, knowledge: new R2StorageAdapter(config.r2, knowledgePrefix) };
|
|
9408
|
+
}
|
|
9409
|
+
return out;
|
|
8818
9410
|
}
|
|
8819
|
-
function createR2BindingStorage(config) {
|
|
9411
|
+
function createR2BindingStorage(config, options) {
|
|
8820
9412
|
if (!config.r2Binding) {
|
|
8821
9413
|
throw new StorageError('storage.r2Binding is required when storage.provider === "r2-binding"');
|
|
8822
9414
|
}
|
|
8823
9415
|
const rootPrefix = config.rootPath.replace(/^\/+|\/+$/g, "");
|
|
8824
|
-
const
|
|
8825
|
-
|
|
9416
|
+
const tenantPrefix = `${rootPrefix}/${WORKSPACES_FOLDER}/${config.workspaceId}`;
|
|
9417
|
+
const workspacePrefix = `${tenantPrefix}/${ENGINE_DATA_FOLDER}`;
|
|
9418
|
+
const out = {
|
|
8826
9419
|
workspace: new R2BindingStorageAdapter(config.r2Binding, workspacePrefix)
|
|
8827
9420
|
};
|
|
9421
|
+
if (options.withKnowledge) {
|
|
9422
|
+
const knowledgePrefix = `${tenantPrefix}/${KNOWLEDGE_FOLDER}`;
|
|
9423
|
+
return {
|
|
9424
|
+
...out,
|
|
9425
|
+
knowledge: new R2BindingStorageAdapter(config.r2Binding, knowledgePrefix)
|
|
9426
|
+
};
|
|
9427
|
+
}
|
|
9428
|
+
return out;
|
|
8828
9429
|
}
|
|
8829
9430
|
|
|
8830
9431
|
// src/transcript/reader.ts
|
|
@@ -9329,6 +9930,7 @@ var Engine = class {
|
|
|
9329
9930
|
const skillList = skillSource !== void 0 ? await skillSource.list() : void 0;
|
|
9330
9931
|
const apiConfig = this.resolveApiConfig(options.api);
|
|
9331
9932
|
const offloadConfig = this.resolveOffloadConfig(options.compaction?.toolResultOffload);
|
|
9933
|
+
const knowledgeRuntime = this.resolveKnowledgeRuntime(options.knowledge, storage);
|
|
9332
9934
|
let systemPrompt = await buildSystemPrompt({
|
|
9333
9935
|
...coordinatorBase !== void 0 ? { base: coordinatorBase } : {},
|
|
9334
9936
|
memory,
|
|
@@ -9362,6 +9964,7 @@ var Engine = class {
|
|
|
9362
9964
|
...skillSource !== void 0 ? { skillSource } : {},
|
|
9363
9965
|
...apiConfig !== void 0 ? { apiConfig } : {},
|
|
9364
9966
|
...offloadConfig !== void 0 ? { toolResultOffload: offloadConfig } : {},
|
|
9967
|
+
...knowledgeRuntime !== void 0 ? { knowledge: knowledgeRuntime } : {},
|
|
9365
9968
|
...this.internals.fetch !== void 0 ? { fetch: this.internals.fetch } : {}
|
|
9366
9969
|
});
|
|
9367
9970
|
const writer = new TranscriptWriter({
|
|
@@ -9474,6 +10077,7 @@ var Engine = class {
|
|
|
9474
10077
|
const skillList = skillSource !== void 0 ? await skillSource.list() : void 0;
|
|
9475
10078
|
const apiConfig = this.resolveApiConfig(options.api);
|
|
9476
10079
|
const offloadConfig = this.resolveOffloadConfig(options.compaction?.toolResultOffload);
|
|
10080
|
+
const knowledgeRuntime = this.resolveKnowledgeRuntime(options.knowledge, storage);
|
|
9477
10081
|
let systemPrompt = await buildSystemPrompt({
|
|
9478
10082
|
...coordinatorBase !== void 0 ? { base: coordinatorBase } : {},
|
|
9479
10083
|
memory,
|
|
@@ -9507,6 +10111,7 @@ var Engine = class {
|
|
|
9507
10111
|
...skillSource !== void 0 ? { skillSource } : {},
|
|
9508
10112
|
...apiConfig !== void 0 ? { apiConfig } : {},
|
|
9509
10113
|
...offloadConfig !== void 0 ? { toolResultOffload: offloadConfig } : {},
|
|
10114
|
+
...knowledgeRuntime !== void 0 ? { knowledge: knowledgeRuntime } : {},
|
|
9510
10115
|
...this.internals.fetch !== void 0 ? { fetch: this.internals.fetch } : {}
|
|
9511
10116
|
});
|
|
9512
10117
|
const priorState = await loadWriterState(storage.workspace, logPath);
|
|
@@ -10149,6 +10754,14 @@ ${inputJson}
|
|
|
10149
10754
|
names.add("FetchData");
|
|
10150
10755
|
}
|
|
10151
10756
|
}
|
|
10757
|
+
if (this.config.knowledge?.enabled === true) {
|
|
10758
|
+
if (!disabled.has("SearchKnowledge") && (wantAll || enabled.has("SearchKnowledge"))) {
|
|
10759
|
+
names.add("SearchKnowledge");
|
|
10760
|
+
}
|
|
10761
|
+
if (!disabled.has("ReadKnowledge") && (wantAll || enabled.has("ReadKnowledge"))) {
|
|
10762
|
+
names.add("ReadKnowledge");
|
|
10763
|
+
}
|
|
10764
|
+
}
|
|
10152
10765
|
for (const tool of this.config.tools.custom) {
|
|
10153
10766
|
names.add(tool.name);
|
|
10154
10767
|
}
|
|
@@ -10340,6 +10953,36 @@ ${inputJson}
|
|
|
10340
10953
|
...summarizer !== void 0 ? { summarizer } : {}
|
|
10341
10954
|
};
|
|
10342
10955
|
}
|
|
10956
|
+
/**
|
|
10957
|
+
* Plan 023 — resolve the effective runtime knowledge bundle for a
|
|
10958
|
+
* single run.
|
|
10959
|
+
*
|
|
10960
|
+
* Engine-level config (`config.knowledge`) carries the capability
|
|
10961
|
+
* flag + scalar caps. The actual folders + external links are
|
|
10962
|
+
* RUNTIME-only, supplied via `RunOptions.knowledge`. Returning
|
|
10963
|
+
* `undefined` means "don't register the knowledge tools" — callers
|
|
10964
|
+
* skip the bundle entirely.
|
|
10965
|
+
*
|
|
10966
|
+
* Three gates must all pass:
|
|
10967
|
+
* 1. `config.knowledge.enabled === true` (engine opt-in)
|
|
10968
|
+
* 2. `storage.knowledge !== undefined` (adapter was built)
|
|
10969
|
+
* 3. There IS something to look at — at least one folder OR
|
|
10970
|
+
* one external link supplied for this run.
|
|
10971
|
+
*/
|
|
10972
|
+
resolveKnowledgeRuntime(override, storage) {
|
|
10973
|
+
if (this.config.knowledge?.enabled !== true) return void 0;
|
|
10974
|
+
if (storage.knowledge === void 0) return void 0;
|
|
10975
|
+
const folders = override?.folders ?? [];
|
|
10976
|
+
const external = override?.external ?? [];
|
|
10977
|
+
if (folders.length === 0 && external.length === 0) return void 0;
|
|
10978
|
+
return {
|
|
10979
|
+
adapter: storage.knowledge,
|
|
10980
|
+
folders,
|
|
10981
|
+
external,
|
|
10982
|
+
maxSearchResults: this.config.knowledge.maxSearchResults,
|
|
10983
|
+
maxReadBytes: this.config.knowledge.maxReadBytes
|
|
10984
|
+
};
|
|
10985
|
+
}
|
|
10343
10986
|
resolveApiConfig(override) {
|
|
10344
10987
|
const base = this.config.api;
|
|
10345
10988
|
if (override === void 0 && base === void 0) return void 0;
|
|
@@ -10402,7 +11045,9 @@ ${inputJson}
|
|
|
10402
11045
|
if (this.internals.buildStorage !== void 0) {
|
|
10403
11046
|
return this.internals.buildStorage();
|
|
10404
11047
|
}
|
|
10405
|
-
return createEngineStorage(this.config.storage
|
|
11048
|
+
return createEngineStorage(this.config.storage, {
|
|
11049
|
+
withKnowledge: this.config.knowledge?.enabled === true
|
|
11050
|
+
});
|
|
10406
11051
|
}
|
|
10407
11052
|
buildClient() {
|
|
10408
11053
|
return createModelAdapter(this.config.model, {
|
|
@@ -10523,6 +11168,30 @@ function buildToolRegistry(options) {
|
|
|
10523
11168
|
registry.register(fetchDataTool);
|
|
10524
11169
|
}
|
|
10525
11170
|
}
|
|
11171
|
+
if (options.knowledge !== void 0) {
|
|
11172
|
+
const k = options.knowledge;
|
|
11173
|
+
if (!disabled.has("SearchKnowledge") && (wantAll || enabled.has("SearchKnowledge"))) {
|
|
11174
|
+
const searchTool = createSearchKnowledgeTool({
|
|
11175
|
+
adapter: k.adapter,
|
|
11176
|
+
folders: k.folders,
|
|
11177
|
+
external: k.external,
|
|
11178
|
+
maxSearchResults: k.maxSearchResults
|
|
11179
|
+
});
|
|
11180
|
+
registry.register(searchTool);
|
|
11181
|
+
childRegistry.register(searchTool);
|
|
11182
|
+
}
|
|
11183
|
+
if (!disabled.has("ReadKnowledge") && (wantAll || enabled.has("ReadKnowledge"))) {
|
|
11184
|
+
const readTool = createReadKnowledgeTool({
|
|
11185
|
+
adapter: k.adapter,
|
|
11186
|
+
folders: k.folders,
|
|
11187
|
+
external: k.external,
|
|
11188
|
+
maxReadBytes: k.maxReadBytes,
|
|
11189
|
+
...options.fetch !== void 0 ? { fetch: options.fetch } : {}
|
|
11190
|
+
});
|
|
11191
|
+
registry.register(readTool);
|
|
11192
|
+
childRegistry.register(readTool);
|
|
11193
|
+
}
|
|
11194
|
+
}
|
|
10526
11195
|
const agentTool = createAgentTool({
|
|
10527
11196
|
storage: storage.workspace,
|
|
10528
11197
|
client,
|
|
@@ -10570,6 +11239,153 @@ function buildToolRegistry(options) {
|
|
|
10570
11239
|
// src/index.ts
|
|
10571
11240
|
init_contract();
|
|
10572
11241
|
init_fetchData();
|
|
11242
|
+
|
|
11243
|
+
// src/knowledge/indexer.ts
|
|
11244
|
+
init_cjs_shims();
|
|
11245
|
+
var HEADING_RE = /^(#{1,6})[ \t]+(.+?)\s*$/;
|
|
11246
|
+
var WIKI_LINK_RE = /\[\[([^\]|#]+)(?:[#|][^\]]*)?\]\]/g;
|
|
11247
|
+
var FORMAT_BY_EXT = {
|
|
11248
|
+
md: "md",
|
|
11249
|
+
markdown: "md",
|
|
11250
|
+
txt: "txt",
|
|
11251
|
+
json: "json",
|
|
11252
|
+
csv: "csv",
|
|
11253
|
+
html: "html",
|
|
11254
|
+
htm: "html",
|
|
11255
|
+
pdf: "pdf",
|
|
11256
|
+
docx: "docx"
|
|
11257
|
+
};
|
|
11258
|
+
var PREVIEW_CHARS = 200;
|
|
11259
|
+
async function buildKnowledgeIndex(options) {
|
|
11260
|
+
const { adapter, base } = options;
|
|
11261
|
+
const safeBase = base.replace(/^\/+|\/+$/g, "");
|
|
11262
|
+
if (safeBase.length === 0 || safeBase.includes("..")) {
|
|
11263
|
+
throw new Error(`buildKnowledgeIndex: invalid base "${base}"`);
|
|
11264
|
+
}
|
|
11265
|
+
const files = await listFilesRecursive(adapter, safeBase);
|
|
11266
|
+
const sections = [];
|
|
11267
|
+
const filesMeta = {};
|
|
11268
|
+
for (const fileRel of files) {
|
|
11269
|
+
if (fileRel === "_index.json") continue;
|
|
11270
|
+
const fullPath = `${safeBase}/${fileRel}`;
|
|
11271
|
+
const ext = (fileRel.split(".").pop() ?? "").toLowerCase();
|
|
11272
|
+
const format = FORMAT_BY_EXT[ext];
|
|
11273
|
+
if (format === void 0) continue;
|
|
11274
|
+
const raw = await adapter.readFile(fullPath);
|
|
11275
|
+
if (raw === null) continue;
|
|
11276
|
+
const sizeBytes = byteLength3(raw);
|
|
11277
|
+
const meta = { format, size: sizeBytes };
|
|
11278
|
+
if (format === "md" || format === "txt") {
|
|
11279
|
+
const fileSections = splitSections(raw, fileRel);
|
|
11280
|
+
sections.push(...fileSections);
|
|
11281
|
+
const wikiLinks = extractWikiLinks(raw);
|
|
11282
|
+
if (wikiLinks.length > 0) meta.wikiLinks = wikiLinks;
|
|
11283
|
+
}
|
|
11284
|
+
filesMeta[fileRel] = meta;
|
|
11285
|
+
}
|
|
11286
|
+
return {
|
|
11287
|
+
schema: "v1",
|
|
11288
|
+
base: safeBase,
|
|
11289
|
+
builtAt: options.nowIso ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
11290
|
+
fileCount: Object.keys(filesMeta).length,
|
|
11291
|
+
sections,
|
|
11292
|
+
files: filesMeta
|
|
11293
|
+
};
|
|
11294
|
+
}
|
|
11295
|
+
async function writeKnowledgeIndex(options) {
|
|
11296
|
+
const index = await buildKnowledgeIndex(options);
|
|
11297
|
+
await options.adapter.writeFile(`${index.base}/_index.json`, JSON.stringify(index, null, 2));
|
|
11298
|
+
return index;
|
|
11299
|
+
}
|
|
11300
|
+
async function listFilesRecursive(adapter, dir) {
|
|
11301
|
+
const out = [];
|
|
11302
|
+
const stack = [""];
|
|
11303
|
+
while (stack.length > 0) {
|
|
11304
|
+
const sub = stack.pop();
|
|
11305
|
+
const fullDir = sub === "" ? dir : `${dir}/${sub}`;
|
|
11306
|
+
let entries = [];
|
|
11307
|
+
try {
|
|
11308
|
+
entries = await adapter.listDir(fullDir);
|
|
11309
|
+
} catch {
|
|
11310
|
+
continue;
|
|
11311
|
+
}
|
|
11312
|
+
for (const name of entries) {
|
|
11313
|
+
const childRel = sub === "" ? name : `${sub}/${name}`;
|
|
11314
|
+
const childFull = `${dir}/${childRel}`;
|
|
11315
|
+
const isDir = await adapter.isDirectory(childFull).catch(() => false);
|
|
11316
|
+
if (isDir) {
|
|
11317
|
+
stack.push(childRel);
|
|
11318
|
+
} else {
|
|
11319
|
+
out.push(childRel);
|
|
11320
|
+
}
|
|
11321
|
+
}
|
|
11322
|
+
}
|
|
11323
|
+
return out.sort();
|
|
11324
|
+
}
|
|
11325
|
+
function splitSections(content, relPath) {
|
|
11326
|
+
const lines = content.split(/\r?\n/);
|
|
11327
|
+
const out = [];
|
|
11328
|
+
const heads = [];
|
|
11329
|
+
for (let i = 0; i < lines.length; i++) {
|
|
11330
|
+
const line = lines[i];
|
|
11331
|
+
const m = HEADING_RE.exec(line);
|
|
11332
|
+
if (m) heads.push({ line: i + 1, depth: m[1].length, heading: m[2].trim() });
|
|
11333
|
+
}
|
|
11334
|
+
const leadInEndLine = heads.length > 0 ? heads[0].line - 1 : lines.length;
|
|
11335
|
+
const leadInBody = lines.slice(0, leadInEndLine).join("\n").trim();
|
|
11336
|
+
if (leadInBody.length > 0) {
|
|
11337
|
+
out.push({
|
|
11338
|
+
relPath,
|
|
11339
|
+
heading: "",
|
|
11340
|
+
slug: `${relPath}#`,
|
|
11341
|
+
depth: 0,
|
|
11342
|
+
words: tokenize(leadInBody),
|
|
11343
|
+
preview: makePreview(leadInBody),
|
|
11344
|
+
startLine: 1,
|
|
11345
|
+
endLine: leadInEndLine
|
|
11346
|
+
});
|
|
11347
|
+
}
|
|
11348
|
+
for (let i = 0; i < heads.length; i++) {
|
|
11349
|
+
const h = heads[i];
|
|
11350
|
+
const startLine = h.line;
|
|
11351
|
+
const endLine = i + 1 < heads.length ? heads[i + 1].line - 1 : lines.length;
|
|
11352
|
+
const body = lines.slice(startLine - 1, endLine).join("\n");
|
|
11353
|
+
out.push({
|
|
11354
|
+
relPath,
|
|
11355
|
+
heading: h.heading,
|
|
11356
|
+
slug: `${relPath}#${slugify(h.heading)}`,
|
|
11357
|
+
depth: h.depth,
|
|
11358
|
+
words: tokenize(body),
|
|
11359
|
+
preview: makePreview(body),
|
|
11360
|
+
startLine,
|
|
11361
|
+
endLine
|
|
11362
|
+
});
|
|
11363
|
+
}
|
|
11364
|
+
return out;
|
|
11365
|
+
}
|
|
11366
|
+
function makePreview(body) {
|
|
11367
|
+
const trimmed = body.replace(/^#{1,6}\s+.+$\r?\n?/m, "").trim();
|
|
11368
|
+
if (trimmed.length <= PREVIEW_CHARS) return trimmed;
|
|
11369
|
+
return trimmed.slice(0, PREVIEW_CHARS) + "\u2026";
|
|
11370
|
+
}
|
|
11371
|
+
function slugify(text2) {
|
|
11372
|
+
return text2.toLowerCase().trim().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
11373
|
+
}
|
|
11374
|
+
function extractWikiLinks(text2) {
|
|
11375
|
+
const seen = /* @__PURE__ */ new Set();
|
|
11376
|
+
let m;
|
|
11377
|
+
WIKI_LINK_RE.lastIndex = 0;
|
|
11378
|
+
while ((m = WIKI_LINK_RE.exec(text2)) !== null) {
|
|
11379
|
+
const target = m[1].trim();
|
|
11380
|
+
if (target.length > 0) seen.add(target);
|
|
11381
|
+
}
|
|
11382
|
+
return [...seen].sort();
|
|
11383
|
+
}
|
|
11384
|
+
function byteLength3(s) {
|
|
11385
|
+
return new TextEncoder().encode(s).byteLength;
|
|
11386
|
+
}
|
|
11387
|
+
|
|
11388
|
+
// src/index.ts
|
|
10573
11389
|
init_orchestrate();
|
|
10574
11390
|
init_planParser();
|
|
10575
11391
|
init_retry();
|
|
@@ -10643,6 +11459,7 @@ function resolveApiKey(config) {
|
|
|
10643
11459
|
WebhookDispatcher,
|
|
10644
11460
|
adaptMcpTool,
|
|
10645
11461
|
buildForkedMessages,
|
|
11462
|
+
buildKnowledgeIndex,
|
|
10646
11463
|
buildPermissionPolicy,
|
|
10647
11464
|
buildSchemaPrompt,
|
|
10648
11465
|
buildSystemPrompt,
|
|
@@ -10653,6 +11470,8 @@ function resolveApiKey(config) {
|
|
|
10653
11470
|
createFetchDataTool,
|
|
10654
11471
|
createLogger,
|
|
10655
11472
|
createModelAdapter,
|
|
11473
|
+
createReadKnowledgeTool,
|
|
11474
|
+
createSearchKnowledgeTool,
|
|
10656
11475
|
createSendMessageTool,
|
|
10657
11476
|
createSkillPageTool,
|
|
10658
11477
|
createSmartMemory,
|
|
@@ -10662,6 +11481,7 @@ function resolveApiKey(config) {
|
|
|
10662
11481
|
detectRuntime,
|
|
10663
11482
|
getCoordinatorBasePrompt,
|
|
10664
11483
|
getCoordinatorSystemPrompt,
|
|
11484
|
+
getExtractor,
|
|
10665
11485
|
hasProcessLifecycle,
|
|
10666
11486
|
initEngine,
|
|
10667
11487
|
isCoordinatorMode,
|
|
@@ -10684,6 +11504,7 @@ function resolveApiKey(config) {
|
|
|
10684
11504
|
toResponse,
|
|
10685
11505
|
tryParseJSON,
|
|
10686
11506
|
validateOutput,
|
|
10687
|
-
withCapabilityCheck
|
|
11507
|
+
withCapabilityCheck,
|
|
11508
|
+
writeKnowledgeIndex
|
|
10688
11509
|
});
|
|
10689
11510
|
//# sourceMappingURL=index.cjs.map
|