@exulu/backend 2.2.0 → 2.3.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/index.cjs
CHANGED
|
@@ -3332,6 +3332,15 @@ var init_system_dependencies = __esm({
|
|
|
3332
3332
|
macos: "brew install poppler"
|
|
3333
3333
|
}
|
|
3334
3334
|
},
|
|
3335
|
+
{
|
|
3336
|
+
check: { kind: "binary", binary: "pdftotext" },
|
|
3337
|
+
displayName: "Poppler (pdftotext)",
|
|
3338
|
+
purpose: "parse_document tool: extracting page-marked text from PDFs",
|
|
3339
|
+
installHints: {
|
|
3340
|
+
debian: "apt-get install -y poppler-utils",
|
|
3341
|
+
macos: "brew install poppler"
|
|
3342
|
+
}
|
|
3343
|
+
},
|
|
3335
3344
|
{
|
|
3336
3345
|
check: { kind: "npm-global", packageName: "docx" },
|
|
3337
3346
|
displayName: "docx (npm global)",
|
|
@@ -4169,6 +4178,595 @@ var init_session_file_read_tool = __esm({
|
|
|
4169
4178
|
}
|
|
4170
4179
|
});
|
|
4171
4180
|
|
|
4181
|
+
// src/templates/tools/document-render-helpers.ts
|
|
4182
|
+
async function pdfToText(pdf) {
|
|
4183
|
+
const dir = await (0, import_promises2.mkdtemp)((0, import_node_path5.join)((0, import_node_os.tmpdir)(), "exulu-parse-"));
|
|
4184
|
+
try {
|
|
4185
|
+
const inputPath = (0, import_node_path5.join)(dir, "input.pdf");
|
|
4186
|
+
await (0, import_promises2.writeFile)(inputPath, pdf);
|
|
4187
|
+
const { stdout } = await execFileAsync("pdftotext", ["-layout", inputPath, "-"], {
|
|
4188
|
+
timeout: 6e4,
|
|
4189
|
+
maxBuffer: MAX_STDOUT_BYTES
|
|
4190
|
+
});
|
|
4191
|
+
return stdout;
|
|
4192
|
+
} finally {
|
|
4193
|
+
await (0, import_promises2.rm)(dir, { recursive: true, force: true });
|
|
4194
|
+
}
|
|
4195
|
+
}
|
|
4196
|
+
async function renderPdfPageToPng(pdf, page, scaleTo) {
|
|
4197
|
+
const dir = await (0, import_promises2.mkdtemp)((0, import_node_path5.join)((0, import_node_os.tmpdir)(), "exulu-render-"));
|
|
4198
|
+
try {
|
|
4199
|
+
const inputPath = (0, import_node_path5.join)(dir, "input.pdf");
|
|
4200
|
+
await (0, import_promises2.writeFile)(inputPath, pdf);
|
|
4201
|
+
try {
|
|
4202
|
+
await execFileAsync(
|
|
4203
|
+
"pdftoppm",
|
|
4204
|
+
["-png", "-f", String(page), "-l", String(page), "-scale-to", String(scaleTo), inputPath, (0, import_node_path5.join)(dir, "page")],
|
|
4205
|
+
{ timeout: 6e4, maxBuffer: MAX_STDOUT_BYTES }
|
|
4206
|
+
);
|
|
4207
|
+
} catch (err) {
|
|
4208
|
+
if (err?.code === 99) return null;
|
|
4209
|
+
throw err;
|
|
4210
|
+
}
|
|
4211
|
+
const produced = (await (0, import_promises2.readdir)(dir)).find((f) => f.startsWith("page") && f.endsWith(".png"));
|
|
4212
|
+
if (!produced) return null;
|
|
4213
|
+
return await (0, import_promises2.readFile)((0, import_node_path5.join)(dir, produced));
|
|
4214
|
+
} finally {
|
|
4215
|
+
await (0, import_promises2.rm)(dir, { recursive: true, force: true });
|
|
4216
|
+
}
|
|
4217
|
+
}
|
|
4218
|
+
var import_node_child_process4, import_node_util3, import_promises2, import_node_os, import_node_path5, execFileAsync, MAX_STDOUT_BYTES;
|
|
4219
|
+
var init_document_render_helpers = __esm({
|
|
4220
|
+
"src/templates/tools/document-render-helpers.ts"() {
|
|
4221
|
+
"use strict";
|
|
4222
|
+
init_cjs_shims();
|
|
4223
|
+
import_node_child_process4 = require("child_process");
|
|
4224
|
+
import_node_util3 = require("util");
|
|
4225
|
+
import_promises2 = require("fs/promises");
|
|
4226
|
+
import_node_os = require("os");
|
|
4227
|
+
import_node_path5 = require("path");
|
|
4228
|
+
execFileAsync = (0, import_node_util3.promisify)(import_node_child_process4.execFile);
|
|
4229
|
+
MAX_STDOUT_BYTES = 64 * 1024 * 1024;
|
|
4230
|
+
}
|
|
4231
|
+
});
|
|
4232
|
+
|
|
4233
|
+
// src/templates/tools/parse-document-tool.ts
|
|
4234
|
+
var import_zod6, import_node_path6, import_officeparser, DEFAULT_LIMIT2, MAX_CONTENT_CHARS2, MIN_CHARS_PER_PAGE, OFFICE_EXTENSIONS, pagesPattern, createParseDocumentTool;
|
|
4235
|
+
var init_parse_document_tool = __esm({
|
|
4236
|
+
"src/templates/tools/parse-document-tool.ts"() {
|
|
4237
|
+
"use strict";
|
|
4238
|
+
init_cjs_shims();
|
|
4239
|
+
import_zod6 = require("zod");
|
|
4240
|
+
import_node_path6 = require("path");
|
|
4241
|
+
import_officeparser = require("officeparser");
|
|
4242
|
+
init_tool();
|
|
4243
|
+
init_uppy();
|
|
4244
|
+
init_document_render_helpers();
|
|
4245
|
+
DEFAULT_LIMIT2 = 250;
|
|
4246
|
+
MAX_CONTENT_CHARS2 = 16e3;
|
|
4247
|
+
MIN_CHARS_PER_PAGE = 20;
|
|
4248
|
+
OFFICE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
4249
|
+
".docx",
|
|
4250
|
+
".doc",
|
|
4251
|
+
".xlsx",
|
|
4252
|
+
".xls",
|
|
4253
|
+
".pptx",
|
|
4254
|
+
".ppt",
|
|
4255
|
+
".odt",
|
|
4256
|
+
".ods",
|
|
4257
|
+
".odp",
|
|
4258
|
+
".rtf"
|
|
4259
|
+
]);
|
|
4260
|
+
pagesPattern = /^(\d+)(?:-(\d+))?$/;
|
|
4261
|
+
createParseDocumentTool = ({
|
|
4262
|
+
sessionID,
|
|
4263
|
+
user,
|
|
4264
|
+
exuluConfig
|
|
4265
|
+
}) => {
|
|
4266
|
+
if (!sessionID || !exuluConfig?.fileUploads?.s3Bucket) return void 0;
|
|
4267
|
+
const parseDocumentExecute = async ({
|
|
4268
|
+
filename,
|
|
4269
|
+
pages,
|
|
4270
|
+
offset,
|
|
4271
|
+
limit
|
|
4272
|
+
}) => {
|
|
4273
|
+
const safeName = String(filename ?? "").trim();
|
|
4274
|
+
if (!safeName || safeName.includes("..") || safeName.includes("/") || safeName.includes("\\")) {
|
|
4275
|
+
return {
|
|
4276
|
+
error: "Invalid filename \u2014 pass the bare file name exactly as listed in the session files (no paths)."
|
|
4277
|
+
};
|
|
4278
|
+
}
|
|
4279
|
+
const ext = (0, import_node_path6.extname)(safeName).toLowerCase();
|
|
4280
|
+
if (ext !== ".pdf" && !OFFICE_EXTENSIONS.has(ext)) {
|
|
4281
|
+
return {
|
|
4282
|
+
error: `Unsupported extension "${ext}" \u2014 parse_document handles PDF and Office formats. For plain-text files use read_session_file.`
|
|
4283
|
+
};
|
|
4284
|
+
}
|
|
4285
|
+
if (pages && ext !== ".pdf") {
|
|
4286
|
+
return { error: `The pages option is only supported for PDF files \u2014 "${ext}" documents are extracted whole.` };
|
|
4287
|
+
}
|
|
4288
|
+
const uploads = exuluConfig.fileUploads;
|
|
4289
|
+
const generalPrefix = uploads.s3prefix ? `${uploads.s3prefix.replace(/\/$/, "")}/` : "";
|
|
4290
|
+
const key = `${generalPrefix}user_${user?.id ?? "api"}/sessions/${sessionID}/${safeName}`;
|
|
4291
|
+
try {
|
|
4292
|
+
const url = await getPresignedUrl(uploads.s3Bucket, key, exuluConfig);
|
|
4293
|
+
const res = await fetch(url);
|
|
4294
|
+
if (!res.ok) {
|
|
4295
|
+
return { error: `Could not read session file "${safeName}" (status ${res.status}). Check the exact file name.` };
|
|
4296
|
+
}
|
|
4297
|
+
const bytes = Buffer.from(await res.arrayBuffer());
|
|
4298
|
+
let fullText;
|
|
4299
|
+
let totalPages;
|
|
4300
|
+
if (ext === ".pdf") {
|
|
4301
|
+
const raw = await pdfToText(bytes);
|
|
4302
|
+
const pageTexts = raw.replace(/\f$/, "").split("\f");
|
|
4303
|
+
totalPages = pageTexts.length;
|
|
4304
|
+
const nonWhitespace = raw.replace(/\s/g, "").length;
|
|
4305
|
+
if (nonWhitespace < totalPages * MIN_CHARS_PER_PAGE) {
|
|
4306
|
+
return {
|
|
4307
|
+
error: `"${safeName}" has no extractable text layer (likely a scan or image-based PDF). Use view_document_page to look at pages visually, or suggest the user add the document to a knowledge base with a document processor for full OCR.`
|
|
4308
|
+
};
|
|
4309
|
+
}
|
|
4310
|
+
let range = [1, totalPages];
|
|
4311
|
+
if (pages) {
|
|
4312
|
+
const m = pagesPattern.exec(pages.trim());
|
|
4313
|
+
if (!m) return { error: `Invalid pages "${pages}" \u2014 use "3" or "2-5".` };
|
|
4314
|
+
range = [Number(m[1]), Number(m[2] ?? m[1])];
|
|
4315
|
+
if (range[0] < 1 || range[0] > range[1]) {
|
|
4316
|
+
return { error: `Invalid pages "${pages}" \u2014 start must be at least 1 and not greater than the end.` };
|
|
4317
|
+
}
|
|
4318
|
+
if (range[0] > totalPages) {
|
|
4319
|
+
return { error: `Page range starts at ${range[0]} but "${safeName}" has only ${totalPages} page${totalPages === 1 ? "" : "s"}.` };
|
|
4320
|
+
}
|
|
4321
|
+
}
|
|
4322
|
+
fullText = pageTexts.map((text, i) => ({ page: i + 1, text })).filter(({ page }) => page >= range[0] && page <= range[1]).map(({ page, text }) => `--- page ${page} ---
|
|
4323
|
+
${text.trim()}`).join("\n");
|
|
4324
|
+
} else {
|
|
4325
|
+
const extracted = await (0, import_officeparser.parseOfficeAsync)(bytes, {
|
|
4326
|
+
outputErrorToConsole: false,
|
|
4327
|
+
newlineDelimiter: "\n"
|
|
4328
|
+
});
|
|
4329
|
+
fullText = String(extracted);
|
|
4330
|
+
}
|
|
4331
|
+
const lines = fullText.split("\n");
|
|
4332
|
+
const start = (offset ?? 1) - 1;
|
|
4333
|
+
const requested = limit ?? DEFAULT_LIMIT2;
|
|
4334
|
+
const sliced = lines.slice(start, start + requested);
|
|
4335
|
+
let content = sliced.join("\n");
|
|
4336
|
+
let linesReturned = sliced.length;
|
|
4337
|
+
if (content.length > MAX_CONTENT_CHARS2) {
|
|
4338
|
+
content = content.slice(0, MAX_CONTENT_CHARS2);
|
|
4339
|
+
linesReturned = Math.max(1, content.split("\n").length - 1);
|
|
4340
|
+
content = content + "\n[slice truncated \u2014 request fewer lines]";
|
|
4341
|
+
}
|
|
4342
|
+
return {
|
|
4343
|
+
content,
|
|
4344
|
+
...totalPages !== void 0 ? { totalPages } : {},
|
|
4345
|
+
totalLines: lines.length,
|
|
4346
|
+
offset: start + 1,
|
|
4347
|
+
linesReturned
|
|
4348
|
+
};
|
|
4349
|
+
} catch (err) {
|
|
4350
|
+
return { error: `Failed to parse "${safeName}": ${err instanceof Error ? err.message : "unknown error"}` };
|
|
4351
|
+
}
|
|
4352
|
+
};
|
|
4353
|
+
return ExuluTool.internal({
|
|
4354
|
+
id: "parse_document",
|
|
4355
|
+
name: "parse_document",
|
|
4356
|
+
needsApproval: false,
|
|
4357
|
+
description: `Extract the text of an uploaded PDF or Office document from this session's files, with "--- page N ---" markers for PDFs so you can locate content by page. Free and fast (no OCR): works only on documents with a real text layer. To SEE a page or an image inside a document, use view_document_page.`,
|
|
4358
|
+
inputSchema: import_zod6.z.object({
|
|
4359
|
+
filename: import_zod6.z.string().describe('Exact session file name, e.g. "report.pdf"'),
|
|
4360
|
+
pages: import_zod6.z.string().optional().describe('PDF page or range to extract, e.g. "2" or "1-5" (default: all pages) (PDF only)'),
|
|
4361
|
+
offset: import_zod6.z.number().int().min(1).optional().describe("1-based first output line to read (default 1)"),
|
|
4362
|
+
limit: import_zod6.z.number().int().min(1).max(1e3).optional().describe(`Number of lines to read (default ${DEFAULT_LIMIT2})`)
|
|
4363
|
+
}),
|
|
4364
|
+
type: "function",
|
|
4365
|
+
category: "session",
|
|
4366
|
+
config: [],
|
|
4367
|
+
// Same shape mismatch as read_session_file / memory-tool: internal utility
|
|
4368
|
+
// tools return richer objects than ExuluTool's retrieval-flavored execute
|
|
4369
|
+
// type; the AI SDK passes the object through verbatim.
|
|
4370
|
+
execute: parseDocumentExecute
|
|
4371
|
+
});
|
|
4372
|
+
};
|
|
4373
|
+
}
|
|
4374
|
+
});
|
|
4375
|
+
|
|
4376
|
+
// src/sessions/pdf-preview-cache.ts
|
|
4377
|
+
function sanitizeEtag(raw) {
|
|
4378
|
+
return raw.replace(/^"|"$/g, "").replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
4379
|
+
}
|
|
4380
|
+
async function getPdfPreviewBytes(opts) {
|
|
4381
|
+
const { sourceKey, etag, config } = opts;
|
|
4382
|
+
const safeEtag = sanitizeEtag(etag);
|
|
4383
|
+
if (!safeEtag) {
|
|
4384
|
+
throw new PreviewRenderError(`Invalid ETag for ${sourceKey}`);
|
|
4385
|
+
}
|
|
4386
|
+
const cachedPath = (0, import_node_path7.join)(CACHE_ROOT, `${safeEtag}.pdf`);
|
|
4387
|
+
if ((0, import_node_fs6.existsSync)(cachedPath)) {
|
|
4388
|
+
return (0, import_promises3.readFile)(cachedPath);
|
|
4389
|
+
}
|
|
4390
|
+
const existing = inFlight.get(safeEtag);
|
|
4391
|
+
if (existing) return existing;
|
|
4392
|
+
const promise = (async () => {
|
|
4393
|
+
try {
|
|
4394
|
+
await (0, import_promises3.mkdir)(CACHE_IN, { recursive: true });
|
|
4395
|
+
await (0, import_promises3.mkdir)(CACHE_OUT, { recursive: true });
|
|
4396
|
+
const ext = ((0, import_node_path7.extname)(sourceKey) || ".docx").toLowerCase();
|
|
4397
|
+
const inputPath = (0, import_node_path7.join)(CACHE_IN, `${safeEtag}${ext}`);
|
|
4398
|
+
const outputPath = (0, import_node_path7.join)(CACHE_OUT, `${safeEtag}.pdf`);
|
|
4399
|
+
try {
|
|
4400
|
+
const bytes = await getS3ObjectBytes(sourceKey, config);
|
|
4401
|
+
await (0, import_promises3.writeFile)(inputPath, bytes);
|
|
4402
|
+
try {
|
|
4403
|
+
await execAsync3(
|
|
4404
|
+
`soffice --headless --convert-to pdf "${inputPath}" --outdir "${CACHE_OUT}"`,
|
|
4405
|
+
{ timeout: 6e4, maxBuffer: 16 * 1024 * 1024 }
|
|
4406
|
+
);
|
|
4407
|
+
} catch (err) {
|
|
4408
|
+
throw new PreviewRenderError(
|
|
4409
|
+
`LibreOffice conversion failed for ${sourceKey} (etag ${etag}): ${err?.stderr ?? err?.message ?? "unknown error"}`
|
|
4410
|
+
);
|
|
4411
|
+
}
|
|
4412
|
+
if (!(0, import_node_fs6.existsSync)(outputPath)) {
|
|
4413
|
+
throw new PreviewRenderError(
|
|
4414
|
+
`LibreOffice produced no output for ${sourceKey} (etag ${etag})`
|
|
4415
|
+
);
|
|
4416
|
+
}
|
|
4417
|
+
await (0, import_promises3.rename)(outputPath, cachedPath);
|
|
4418
|
+
return await (0, import_promises3.readFile)(cachedPath);
|
|
4419
|
+
} finally {
|
|
4420
|
+
await (0, import_promises3.rm)(inputPath, { force: true });
|
|
4421
|
+
}
|
|
4422
|
+
} finally {
|
|
4423
|
+
inFlight.delete(safeEtag);
|
|
4424
|
+
}
|
|
4425
|
+
})();
|
|
4426
|
+
inFlight.set(safeEtag, promise);
|
|
4427
|
+
return promise;
|
|
4428
|
+
}
|
|
4429
|
+
var import_node_child_process5, import_node_fs6, import_promises3, import_node_path7, import_node_util4, execAsync3, CACHE_ROOT, CACHE_IN, CACHE_OUT, inFlight, PreviewRenderError;
|
|
4430
|
+
var init_pdf_preview_cache = __esm({
|
|
4431
|
+
"src/sessions/pdf-preview-cache.ts"() {
|
|
4432
|
+
"use strict";
|
|
4433
|
+
init_cjs_shims();
|
|
4434
|
+
import_node_child_process5 = require("child_process");
|
|
4435
|
+
import_node_fs6 = require("fs");
|
|
4436
|
+
import_promises3 = require("fs/promises");
|
|
4437
|
+
import_node_path7 = require("path");
|
|
4438
|
+
import_node_util4 = require("util");
|
|
4439
|
+
init_uppy();
|
|
4440
|
+
execAsync3 = (0, import_node_util4.promisify)(import_node_child_process5.exec);
|
|
4441
|
+
CACHE_ROOT = "/tmp/exulu-pdf-cache";
|
|
4442
|
+
CACHE_IN = (0, import_node_path7.join)(CACHE_ROOT, "_in");
|
|
4443
|
+
CACHE_OUT = (0, import_node_path7.join)(CACHE_ROOT, "_out");
|
|
4444
|
+
inFlight = /* @__PURE__ */ new Map();
|
|
4445
|
+
PreviewRenderError = class extends Error {
|
|
4446
|
+
constructor(message) {
|
|
4447
|
+
super(message);
|
|
4448
|
+
this.name = "PreviewRenderError";
|
|
4449
|
+
}
|
|
4450
|
+
};
|
|
4451
|
+
}
|
|
4452
|
+
});
|
|
4453
|
+
|
|
4454
|
+
// src/exulu/litellm/catalog.ts
|
|
4455
|
+
var catalog_exports = {};
|
|
4456
|
+
__export(catalog_exports, {
|
|
4457
|
+
__resetLiteLLMCatalogCacheForTesting: () => __resetLiteLLMCatalogCacheForTesting,
|
|
4458
|
+
fetchLiteLLMCatalog: () => fetchLiteLLMCatalog,
|
|
4459
|
+
findLiteLLMModel: () => findLiteLLMModel
|
|
4460
|
+
});
|
|
4461
|
+
var CACHE_TTL_MS, _cache, __resetLiteLLMCatalogCacheForTesting, fetchLiteLLMCatalog, findLiteLLMModel;
|
|
4462
|
+
var init_catalog = __esm({
|
|
4463
|
+
"src/exulu/litellm/catalog.ts"() {
|
|
4464
|
+
"use strict";
|
|
4465
|
+
init_cjs_shims();
|
|
4466
|
+
CACHE_TTL_MS = 3e4;
|
|
4467
|
+
__resetLiteLLMCatalogCacheForTesting = () => {
|
|
4468
|
+
_cache = void 0;
|
|
4469
|
+
};
|
|
4470
|
+
fetchLiteLLMCatalog = async () => {
|
|
4471
|
+
if (process.env.EXULU_USE_LITELLM !== "true") return [];
|
|
4472
|
+
if (_cache && _cache.expiresAt > Date.now()) {
|
|
4473
|
+
return _cache.items;
|
|
4474
|
+
}
|
|
4475
|
+
const host = process.env.LITELLM_HOST ?? "127.0.0.1";
|
|
4476
|
+
const port = process.env.LITELLM_PORT ?? "4000";
|
|
4477
|
+
const masterKey = process.env.LITELLM_MASTER_KEY;
|
|
4478
|
+
if (!masterKey) return [];
|
|
4479
|
+
try {
|
|
4480
|
+
const res = await fetch(`http://${host}:${port}/model/info`, {
|
|
4481
|
+
method: "GET",
|
|
4482
|
+
headers: { Authorization: `Bearer ${masterKey}` }
|
|
4483
|
+
});
|
|
4484
|
+
if (!res.ok) {
|
|
4485
|
+
console.error(
|
|
4486
|
+
`[EXULU] litellmCatalog: LiteLLM /model/info returned ${res.status}`
|
|
4487
|
+
);
|
|
4488
|
+
return [];
|
|
4489
|
+
}
|
|
4490
|
+
const json = await res.json();
|
|
4491
|
+
const items = (Array.isArray(json?.data) ? json.data : []).map((m) => ({
|
|
4492
|
+
// filter out trailing * from model_name
|
|
4493
|
+
model_name: m.model_name.replace(/\*$/, ""),
|
|
4494
|
+
upstream_model: m.litellm_params?.model ?? null,
|
|
4495
|
+
tags: Array.isArray(m.model_info?.tags) ? m.model_info.tags : [],
|
|
4496
|
+
brand: m.model_info?.brand ?? null,
|
|
4497
|
+
type: m.model_info?.type ?? null,
|
|
4498
|
+
region: m.model_info?.region ?? null,
|
|
4499
|
+
max_tokens: m.model_info?.max_tokens ?? null,
|
|
4500
|
+
max_input_tokens: m.model_info?.max_input_tokens ?? null,
|
|
4501
|
+
input_cost_per_million_tokens: m.model_info?.input_cost_per_token * 1e6,
|
|
4502
|
+
output_cost_per_million_tokens: m.model_info?.output_cost_per_token * 1e6,
|
|
4503
|
+
active: m.model_info?.active ?? true,
|
|
4504
|
+
max_output_tokens: m.model_info?.max_output_tokens ?? null,
|
|
4505
|
+
supports_vision: !!m.model_info?.supports_vision,
|
|
4506
|
+
supports_function_calling: !!m.model_info?.supports_function_calling,
|
|
4507
|
+
supports_pdf_input: !!m.model_info?.supports_pdf_input,
|
|
4508
|
+
supports_audio_input: !!m.model_info?.supports_audio_input,
|
|
4509
|
+
sizes: Array.isArray(m.model_info?.sizes) ? m.model_info.sizes : null,
|
|
4510
|
+
qualities: Array.isArray(m.model_info?.qualities) ? m.model_info.qualities : null,
|
|
4511
|
+
supports_edit: !!m.model_info?.supports_edit,
|
|
4512
|
+
max_n: typeof m.model_info?.max_n === "number" ? m.model_info.max_n : null
|
|
4513
|
+
}));
|
|
4514
|
+
const map = /* @__PURE__ */ new Map();
|
|
4515
|
+
for (const item of items) {
|
|
4516
|
+
const key = `${item.model_name}-${item.upstream_model}`;
|
|
4517
|
+
if (map.has(key)) {
|
|
4518
|
+
map.get(key).tags.push(...item.tags);
|
|
4519
|
+
} else {
|
|
4520
|
+
map.set(key, item);
|
|
4521
|
+
}
|
|
4522
|
+
}
|
|
4523
|
+
const uniqueItems = Array.from(map.values());
|
|
4524
|
+
_cache = { expiresAt: Date.now() + CACHE_TTL_MS, items: uniqueItems };
|
|
4525
|
+
return uniqueItems.filter((m) => m.type !== "speech_to_text" && m.type !== "text_to_speech");
|
|
4526
|
+
} catch (err) {
|
|
4527
|
+
console.error("[EXULU] litellmCatalog: failed to fetch /model/info:", err);
|
|
4528
|
+
return [];
|
|
4529
|
+
}
|
|
4530
|
+
};
|
|
4531
|
+
findLiteLLMModel = async (modelName) => {
|
|
4532
|
+
if (!modelName) return void 0;
|
|
4533
|
+
const items = await fetchLiteLLMCatalog();
|
|
4534
|
+
return items.find((m) => m.model_name === modelName);
|
|
4535
|
+
};
|
|
4536
|
+
}
|
|
4537
|
+
});
|
|
4538
|
+
|
|
4539
|
+
// src/exulu/tool-image-attachments.ts
|
|
4540
|
+
function sweep() {
|
|
4541
|
+
const cutoff = Date.now() - TTL_MS;
|
|
4542
|
+
for (const [id, entry] of stash) {
|
|
4543
|
+
if (entry.stashedAt < cutoff) stash.delete(id);
|
|
4544
|
+
}
|
|
4545
|
+
while (stash.size > MAX_ENTRIES) {
|
|
4546
|
+
const oldest = stash.keys().next().value;
|
|
4547
|
+
if (oldest === void 0) break;
|
|
4548
|
+
stash.delete(oldest);
|
|
4549
|
+
}
|
|
4550
|
+
let totalBytes = 0;
|
|
4551
|
+
for (const entry of stash.values()) totalBytes += entry.data.length;
|
|
4552
|
+
while (totalBytes > MAX_TOTAL_BYTES) {
|
|
4553
|
+
const oldest = stash.keys().next().value;
|
|
4554
|
+
if (oldest === void 0) break;
|
|
4555
|
+
totalBytes -= stash.get(oldest).data.length;
|
|
4556
|
+
stash.delete(oldest);
|
|
4557
|
+
}
|
|
4558
|
+
}
|
|
4559
|
+
function stashToolImage(toolCallId, image) {
|
|
4560
|
+
stash.set(toolCallId, { ...image, stashedAt: Date.now() });
|
|
4561
|
+
sweep();
|
|
4562
|
+
}
|
|
4563
|
+
function stashedIdsInMessage(message) {
|
|
4564
|
+
if (message?.role !== "tool" || !Array.isArray(message.content)) return [];
|
|
4565
|
+
return message.content.filter(
|
|
4566
|
+
(p) => p?.type === "tool-result" && typeof p.toolCallId === "string" && p.toolName === STASH_TOOL_NAME && stash.has(p.toolCallId)
|
|
4567
|
+
).map((p) => p.toolCallId);
|
|
4568
|
+
}
|
|
4569
|
+
function injectedTextFor(id, label) {
|
|
4570
|
+
return `${INJECTED_IMAGE_PREFIX}${id}: ${label}]`;
|
|
4571
|
+
}
|
|
4572
|
+
function firstTextPart(message) {
|
|
4573
|
+
if (message?.role !== "user" || !Array.isArray(message.content)) return void 0;
|
|
4574
|
+
const first = message.content[0];
|
|
4575
|
+
return first?.type === "text" ? first.text : void 0;
|
|
4576
|
+
}
|
|
4577
|
+
function imageAttachmentGuard() {
|
|
4578
|
+
return ({ messages }) => {
|
|
4579
|
+
sweep();
|
|
4580
|
+
if (!Array.isArray(messages) || messages.length === 0 || stash.size === 0) return void 0;
|
|
4581
|
+
let changed = false;
|
|
4582
|
+
const next = [];
|
|
4583
|
+
for (let i = 0; i < messages.length; i++) {
|
|
4584
|
+
const message = messages[i];
|
|
4585
|
+
next.push(message);
|
|
4586
|
+
const ids = stashedIdsInMessage(message);
|
|
4587
|
+
if (ids.length === 0) continue;
|
|
4588
|
+
const alreadyInjected = [];
|
|
4589
|
+
for (let j = i + 1; j < messages.length; j++) {
|
|
4590
|
+
const text = firstTextPart(messages[j]);
|
|
4591
|
+
if (text === void 0 || !text.startsWith(INJECTED_IMAGE_PREFIX)) break;
|
|
4592
|
+
alreadyInjected.push(text);
|
|
4593
|
+
}
|
|
4594
|
+
for (const id of ids) {
|
|
4595
|
+
if (alreadyInjected.some((text) => text.startsWith(`${INJECTED_IMAGE_PREFIX}${id}:`))) continue;
|
|
4596
|
+
const image = stash.get(id);
|
|
4597
|
+
changed = true;
|
|
4598
|
+
next.push({
|
|
4599
|
+
role: "user",
|
|
4600
|
+
content: [
|
|
4601
|
+
{ type: "text", text: injectedTextFor(id, image.label) },
|
|
4602
|
+
{ type: "image", image: image.data, mediaType: image.mediaType }
|
|
4603
|
+
]
|
|
4604
|
+
});
|
|
4605
|
+
}
|
|
4606
|
+
}
|
|
4607
|
+
return changed ? { messages: next } : void 0;
|
|
4608
|
+
};
|
|
4609
|
+
}
|
|
4610
|
+
var INJECTED_IMAGE_PREFIX, stash, MAX_ENTRIES, TTL_MS, MAX_TOTAL_BYTES, STASH_TOOL_NAME;
|
|
4611
|
+
var init_tool_image_attachments = __esm({
|
|
4612
|
+
"src/exulu/tool-image-attachments.ts"() {
|
|
4613
|
+
"use strict";
|
|
4614
|
+
init_cjs_shims();
|
|
4615
|
+
INJECTED_IMAGE_PREFIX = "[Image attached from tool call ";
|
|
4616
|
+
stash = /* @__PURE__ */ new Map();
|
|
4617
|
+
MAX_ENTRIES = 100;
|
|
4618
|
+
TTL_MS = 30 * 60 * 1e3;
|
|
4619
|
+
MAX_TOTAL_BYTES = 1e8;
|
|
4620
|
+
STASH_TOOL_NAME = "view_document_page";
|
|
4621
|
+
}
|
|
4622
|
+
});
|
|
4623
|
+
|
|
4624
|
+
// src/templates/tools/view-document-page-tool.ts
|
|
4625
|
+
var import_zod7, import_node_path8, MAX_IMAGE_BYTES, SCALE_PRIMARY, SCALE_FALLBACK, IMAGE_MEDIA_TYPES, OFFICE_EXTENSIONS2, createViewDocumentPageTool;
|
|
4626
|
+
var init_view_document_page_tool = __esm({
|
|
4627
|
+
"src/templates/tools/view-document-page-tool.ts"() {
|
|
4628
|
+
"use strict";
|
|
4629
|
+
init_cjs_shims();
|
|
4630
|
+
import_zod7 = require("zod");
|
|
4631
|
+
import_node_path8 = require("path");
|
|
4632
|
+
init_tool();
|
|
4633
|
+
init_uppy();
|
|
4634
|
+
init_pdf_preview_cache();
|
|
4635
|
+
init_catalog();
|
|
4636
|
+
init_tool_image_attachments();
|
|
4637
|
+
init_document_render_helpers();
|
|
4638
|
+
MAX_IMAGE_BYTES = 375e4;
|
|
4639
|
+
SCALE_PRIMARY = 1568;
|
|
4640
|
+
SCALE_FALLBACK = 1024;
|
|
4641
|
+
IMAGE_MEDIA_TYPES = {
|
|
4642
|
+
".png": "image/png",
|
|
4643
|
+
".jpg": "image/jpeg",
|
|
4644
|
+
".jpeg": "image/jpeg",
|
|
4645
|
+
".gif": "image/gif",
|
|
4646
|
+
".webp": "image/webp"
|
|
4647
|
+
};
|
|
4648
|
+
OFFICE_EXTENSIONS2 = /* @__PURE__ */ new Set([
|
|
4649
|
+
".docx",
|
|
4650
|
+
".doc",
|
|
4651
|
+
".xlsx",
|
|
4652
|
+
".xls",
|
|
4653
|
+
".pptx",
|
|
4654
|
+
".ppt",
|
|
4655
|
+
".odt",
|
|
4656
|
+
".ods",
|
|
4657
|
+
".odp",
|
|
4658
|
+
".rtf"
|
|
4659
|
+
]);
|
|
4660
|
+
createViewDocumentPageTool = ({
|
|
4661
|
+
sessionID,
|
|
4662
|
+
user,
|
|
4663
|
+
exuluConfig
|
|
4664
|
+
}) => {
|
|
4665
|
+
if (!sessionID || !exuluConfig?.fileUploads?.s3Bucket) return void 0;
|
|
4666
|
+
const viewDocumentPageExecute = async ({ filename, page, model }, options) => {
|
|
4667
|
+
const safeName = String(filename ?? "").trim();
|
|
4668
|
+
if (!safeName || safeName.includes("..") || safeName.includes("/") || safeName.includes("\\")) {
|
|
4669
|
+
return {
|
|
4670
|
+
error: "Invalid filename \u2014 pass the bare file name exactly as listed in the session files (no paths)."
|
|
4671
|
+
};
|
|
4672
|
+
}
|
|
4673
|
+
const ext = (0, import_node_path8.extname)(safeName).toLowerCase();
|
|
4674
|
+
const isImage = ext in IMAGE_MEDIA_TYPES;
|
|
4675
|
+
const isPdf = ext === ".pdf";
|
|
4676
|
+
const isOffice = OFFICE_EXTENSIONS2.has(ext);
|
|
4677
|
+
if (!isImage && !isPdf && !isOffice) {
|
|
4678
|
+
return { error: `Unsupported extension "${ext}" \u2014 view_document_page handles PDF, Office, and image files.` };
|
|
4679
|
+
}
|
|
4680
|
+
const modelId = typeof model === "string" ? model : model?.modelId;
|
|
4681
|
+
if (!modelId) {
|
|
4682
|
+
console.warn("[EXULU] view_document_page: no model id available for vision gating \u2014 proceeding optimistically.");
|
|
4683
|
+
}
|
|
4684
|
+
if (modelId) {
|
|
4685
|
+
try {
|
|
4686
|
+
const entry = await findLiteLLMModel(modelId);
|
|
4687
|
+
if (entry && entry.supports_vision === false) {
|
|
4688
|
+
return {
|
|
4689
|
+
error: `The current model "${modelId}" does not support images, so this page cannot be shown to you. Use parse_document for the text, or tell the user a vision-capable model is required.`
|
|
4690
|
+
};
|
|
4691
|
+
}
|
|
4692
|
+
} catch {
|
|
4693
|
+
}
|
|
4694
|
+
}
|
|
4695
|
+
const uploads = exuluConfig.fileUploads;
|
|
4696
|
+
const generalPrefix = uploads.s3prefix ? `${uploads.s3prefix.replace(/\/$/, "")}/` : "";
|
|
4697
|
+
const key = `${generalPrefix}user_${user?.id ?? "api"}/sessions/${sessionID}/${safeName}`;
|
|
4698
|
+
const pageNumber = page ?? 1;
|
|
4699
|
+
try {
|
|
4700
|
+
let imageBytes;
|
|
4701
|
+
let mediaType = "image/png";
|
|
4702
|
+
if (isImage) {
|
|
4703
|
+
imageBytes = await getS3ObjectBytes(key, exuluConfig);
|
|
4704
|
+
mediaType = IMAGE_MEDIA_TYPES[ext];
|
|
4705
|
+
if (imageBytes.length > MAX_IMAGE_BYTES) {
|
|
4706
|
+
return {
|
|
4707
|
+
error: `"${safeName}" is too large to attach (${imageBytes.length} bytes, max ${MAX_IMAGE_BYTES}). Ask the user for a smaller version of the image.`
|
|
4708
|
+
};
|
|
4709
|
+
}
|
|
4710
|
+
} else {
|
|
4711
|
+
let pdfBytes;
|
|
4712
|
+
if (isPdf) {
|
|
4713
|
+
pdfBytes = await getS3ObjectBytes(key, exuluConfig);
|
|
4714
|
+
} else {
|
|
4715
|
+
const etag = await getS3ObjectEtag(key, exuluConfig);
|
|
4716
|
+
if (!etag) {
|
|
4717
|
+
return { error: `Could not read session file "${safeName}". Check the exact file name.` };
|
|
4718
|
+
}
|
|
4719
|
+
pdfBytes = await getPdfPreviewBytes({ sourceKey: key, etag, config: exuluConfig });
|
|
4720
|
+
}
|
|
4721
|
+
let rendered = await renderPdfPageToPng(pdfBytes, pageNumber, SCALE_PRIMARY);
|
|
4722
|
+
if (!rendered) {
|
|
4723
|
+
return { error: `Could not render page ${pageNumber} of "${safeName}" \u2014 the document may have fewer pages.` };
|
|
4724
|
+
}
|
|
4725
|
+
if (rendered.length > MAX_IMAGE_BYTES) {
|
|
4726
|
+
rendered = await renderPdfPageToPng(pdfBytes, pageNumber, SCALE_FALLBACK);
|
|
4727
|
+
}
|
|
4728
|
+
if (!rendered || rendered.length > MAX_IMAGE_BYTES) {
|
|
4729
|
+
return { error: `Page ${pageNumber} of "${safeName}" is too complex to attach within the image size limit.` };
|
|
4730
|
+
}
|
|
4731
|
+
imageBytes = rendered;
|
|
4732
|
+
}
|
|
4733
|
+
if (!options?.toolCallId) {
|
|
4734
|
+
return { error: "Internal error: missing toolCallId \u2014 the image cannot be attached." };
|
|
4735
|
+
}
|
|
4736
|
+
stashToolImage(options.toolCallId, {
|
|
4737
|
+
data: imageBytes.toString("base64"),
|
|
4738
|
+
mediaType,
|
|
4739
|
+
label: isImage ? safeName : `${safeName} page ${pageNumber}`
|
|
4740
|
+
});
|
|
4741
|
+
return {
|
|
4742
|
+
attached: true,
|
|
4743
|
+
filename: safeName,
|
|
4744
|
+
page: pageNumber,
|
|
4745
|
+
note: "The rendered image follows this tool result as an attached user message \u2014 analyze it there. If no image message follows, the attachment has expired; call this tool again to re-render it."
|
|
4746
|
+
};
|
|
4747
|
+
} catch (err) {
|
|
4748
|
+
return { error: `Failed to render "${safeName}": ${err instanceof Error ? err.message : "unknown error"}` };
|
|
4749
|
+
}
|
|
4750
|
+
};
|
|
4751
|
+
return ExuluTool.internal({
|
|
4752
|
+
id: "view_document_page",
|
|
4753
|
+
name: "view_document_page",
|
|
4754
|
+
needsApproval: false,
|
|
4755
|
+
description: "LOOK at a page of an uploaded PDF/Office document, or at an uploaded image, from this session's files. The rendered image is attached as a user message directly after this tool result so you can visually analyze photos, charts, scans, and layouts. Use parse_document first to find which page you need. Requires a vision-capable model.",
|
|
4756
|
+
inputSchema: import_zod7.z.object({
|
|
4757
|
+
filename: import_zod7.z.string().describe('Exact session file name, e.g. "report.pdf" or "screenshot.png"'),
|
|
4758
|
+
page: import_zod7.z.number().int().min(1).optional().describe("Page number to render (default 1; ignored for image files)")
|
|
4759
|
+
}),
|
|
4760
|
+
type: "function",
|
|
4761
|
+
category: "session",
|
|
4762
|
+
config: [],
|
|
4763
|
+
// Same execute-shape cast as read_session_file / parse_document.
|
|
4764
|
+
execute: viewDocumentPageExecute
|
|
4765
|
+
});
|
|
4766
|
+
};
|
|
4767
|
+
}
|
|
4768
|
+
});
|
|
4769
|
+
|
|
4172
4770
|
// src/templates/tools/convert-exulu-tools-to-ai-sdk-tools.ts
|
|
4173
4771
|
var convert_exulu_tools_to_ai_sdk_tools_exports = {};
|
|
4174
4772
|
__export(convert_exulu_tools_to_ai_sdk_tools_exports, {
|
|
@@ -4197,6 +4795,8 @@ var init_convert_exulu_tools_to_ai_sdk_tools = __esm({
|
|
|
4197
4795
|
init_truncate_tool_output();
|
|
4198
4796
|
init_tool_output_offload();
|
|
4199
4797
|
init_session_file_read_tool();
|
|
4798
|
+
init_parse_document_tool();
|
|
4799
|
+
init_view_document_page_tool();
|
|
4200
4800
|
init_context_budget();
|
|
4201
4801
|
OUTPUT_OFFLOAD_EXEMPT_TOOL_IDS = /* @__PURE__ */ new Set(["agentic_context_search"]);
|
|
4202
4802
|
generateS3Key = (filename) => `${(0, import_node_crypto4.randomUUID)()}-${filename}`;
|
|
@@ -4377,6 +4977,14 @@ var init_convert_exulu_tools_to_ai_sdk_tools = __esm({
|
|
|
4377
4977
|
if (sessionFileReadTool && !disabled.has(sessionFileReadTool.id)) {
|
|
4378
4978
|
currentTools.push(sessionFileReadTool);
|
|
4379
4979
|
}
|
|
4980
|
+
const parseDocumentTool = createParseDocumentTool({ sessionID, user, exuluConfig });
|
|
4981
|
+
if (parseDocumentTool && !disabled.has(parseDocumentTool.id)) {
|
|
4982
|
+
currentTools.push(parseDocumentTool);
|
|
4983
|
+
}
|
|
4984
|
+
const viewDocumentPageTool = createViewDocumentPageTool({ sessionID, user, exuluConfig });
|
|
4985
|
+
if (viewDocumentPageTool && !disabled.has(viewDocumentPageTool.id)) {
|
|
4986
|
+
currentTools.push(viewDocumentPageTool);
|
|
4987
|
+
}
|
|
4380
4988
|
console.log("[EXULU] Creating agentic search tool", contexts?.length, model);
|
|
4381
4989
|
if (contexts?.length && model && !disabled.has("agentic_context_search")) {
|
|
4382
4990
|
const index = currentTools.findIndex((tool4) => tool4.id === "agentic_context_search");
|
|
@@ -4644,13 +5252,13 @@ var init_convert_exulu_tools_to_ai_sdk_tools = __esm({
|
|
|
4644
5252
|
});
|
|
4645
5253
|
|
|
4646
5254
|
// src/exulu/tool.ts
|
|
4647
|
-
var import_ai3,
|
|
5255
|
+
var import_ai3, import_zod8, import_node_crypto5, PUBLIC_TOOL_TYPES, ExuluTool;
|
|
4648
5256
|
var init_tool = __esm({
|
|
4649
5257
|
"src/exulu/tool.ts"() {
|
|
4650
5258
|
"use strict";
|
|
4651
5259
|
init_cjs_shims();
|
|
4652
5260
|
import_ai3 = require("ai");
|
|
4653
|
-
|
|
5261
|
+
import_zod8 = require("zod");
|
|
4654
5262
|
init_sanitize_name();
|
|
4655
5263
|
import_node_crypto5 = require("crypto");
|
|
4656
5264
|
init_singleton();
|
|
@@ -4707,7 +5315,7 @@ var init_tool = __esm({
|
|
|
4707
5315
|
this.type = type;
|
|
4708
5316
|
this.tool = (0, import_ai3.tool)({
|
|
4709
5317
|
description,
|
|
4710
|
-
inputSchema: inputSchema ||
|
|
5318
|
+
inputSchema: inputSchema || import_zod8.z.object({}),
|
|
4711
5319
|
execute: oauth ? wrapExecuteWithOauth(id, oauth, execute2) : execute2
|
|
4712
5320
|
});
|
|
4713
5321
|
}
|
|
@@ -5000,62 +5608,62 @@ function effectiveKbSettings(profile, ctx) {
|
|
|
5000
5608
|
keywordPrefilter: preset.keywordPrefilter
|
|
5001
5609
|
};
|
|
5002
5610
|
}
|
|
5003
|
-
var
|
|
5611
|
+
var import_zod9, KB_KINDS, DEFAULT_PREFILTER_CUTOFF, RRF_K, CHUNK_GROUP_MAX, kbProfileSchema, knowledgeBasesSchema, routingRuleSchema, routingSchema, identifierSetSchema, vocabularySchema, memorySchema, tuningSchema, boolVal, strVal, KIND_PRESETS;
|
|
5004
5612
|
var init_config = __esm({
|
|
5005
5613
|
"ee/agentic-retrieval/pipeline/config.ts"() {
|
|
5006
5614
|
"use strict";
|
|
5007
5615
|
init_cjs_shims();
|
|
5008
|
-
|
|
5616
|
+
import_zod9 = require("zod");
|
|
5009
5617
|
KB_KINDS = ["documents", "conversations", "records"];
|
|
5010
5618
|
DEFAULT_PREFILTER_CUTOFF = 2.5;
|
|
5011
5619
|
RRF_K = 60;
|
|
5012
5620
|
CHUNK_GROUP_MAX = 10;
|
|
5013
|
-
kbProfileSchema =
|
|
5014
|
-
enabled:
|
|
5015
|
-
kind:
|
|
5016
|
-
instructions:
|
|
5017
|
-
overrides:
|
|
5018
|
-
limit:
|
|
5019
|
-
expand:
|
|
5020
|
-
multiQuery:
|
|
5021
|
-
hyde:
|
|
5621
|
+
kbProfileSchema = import_zod9.z.object({
|
|
5622
|
+
enabled: import_zod9.z.boolean().default(true),
|
|
5623
|
+
kind: import_zod9.z.enum(KB_KINDS).default("documents"),
|
|
5624
|
+
instructions: import_zod9.z.string().default(""),
|
|
5625
|
+
overrides: import_zod9.z.object({
|
|
5626
|
+
limit: import_zod9.z.number().int().positive().optional(),
|
|
5627
|
+
expand: import_zod9.z.number().int().min(0).optional(),
|
|
5628
|
+
multiQuery: import_zod9.z.boolean().optional(),
|
|
5629
|
+
hyde: import_zod9.z.boolean().optional()
|
|
5022
5630
|
}).default({})
|
|
5023
5631
|
});
|
|
5024
|
-
knowledgeBasesSchema =
|
|
5025
|
-
routingRuleSchema =
|
|
5026
|
-
id:
|
|
5027
|
-
label:
|
|
5028
|
-
description:
|
|
5029
|
-
main:
|
|
5030
|
-
fallback:
|
|
5632
|
+
knowledgeBasesSchema = import_zod9.z.record(import_zod9.z.string(), kbProfileSchema);
|
|
5633
|
+
routingRuleSchema = import_zod9.z.object({
|
|
5634
|
+
id: import_zod9.z.string(),
|
|
5635
|
+
label: import_zod9.z.string(),
|
|
5636
|
+
description: import_zod9.z.string(),
|
|
5637
|
+
main: import_zod9.z.array(import_zod9.z.string()),
|
|
5638
|
+
fallback: import_zod9.z.array(import_zod9.z.string()).default([])
|
|
5031
5639
|
});
|
|
5032
|
-
routingSchema =
|
|
5033
|
-
identifierSetSchema =
|
|
5034
|
-
name:
|
|
5035
|
-
description:
|
|
5036
|
-
examples:
|
|
5037
|
-
strategy:
|
|
5038
|
-
contexts:
|
|
5640
|
+
routingSchema = import_zod9.z.object({ rules: import_zod9.z.array(routingRuleSchema).default([]) });
|
|
5641
|
+
identifierSetSchema = import_zod9.z.object({
|
|
5642
|
+
name: import_zod9.z.string(),
|
|
5643
|
+
description: import_zod9.z.string().default(""),
|
|
5644
|
+
examples: import_zod9.z.array(import_zod9.z.string()).default([]),
|
|
5645
|
+
strategy: import_zod9.z.enum(["fuzzy", "exact"]),
|
|
5646
|
+
contexts: import_zod9.z.array(import_zod9.z.string()).default([])
|
|
5039
5647
|
});
|
|
5040
|
-
vocabularySchema =
|
|
5041
|
-
glossary:
|
|
5042
|
-
identifiers:
|
|
5043
|
-
rewrites:
|
|
5044
|
-
styleHint:
|
|
5648
|
+
vocabularySchema = import_zod9.z.object({
|
|
5649
|
+
glossary: import_zod9.z.array(import_zod9.z.object({ term: import_zod9.z.string(), meaning: import_zod9.z.string() })).default([]),
|
|
5650
|
+
identifiers: import_zod9.z.array(identifierSetSchema).default([]),
|
|
5651
|
+
rewrites: import_zod9.z.array(import_zod9.z.object({ find: import_zod9.z.string(), replace: import_zod9.z.string() })).default([]),
|
|
5652
|
+
styleHint: import_zod9.z.string().default("")
|
|
5045
5653
|
});
|
|
5046
|
-
memorySchema =
|
|
5047
|
-
enabled:
|
|
5048
|
-
override:
|
|
5049
|
-
filePrioritization:
|
|
5050
|
-
queryAugmentation:
|
|
5654
|
+
memorySchema = import_zod9.z.object({
|
|
5655
|
+
enabled: import_zod9.z.boolean().default(true),
|
|
5656
|
+
override: import_zod9.z.boolean().default(false),
|
|
5657
|
+
filePrioritization: import_zod9.z.boolean().default(false),
|
|
5658
|
+
queryAugmentation: import_zod9.z.boolean().default(true)
|
|
5051
5659
|
});
|
|
5052
|
-
tuningSchema =
|
|
5053
|
-
topK:
|
|
5054
|
-
fallbackThreshold:
|
|
5055
|
-
pinBoost:
|
|
5056
|
-
identifierBoost:
|
|
5057
|
-
pageWindow:
|
|
5058
|
-
maxQueriesPerContext:
|
|
5660
|
+
tuningSchema = import_zod9.z.object({
|
|
5661
|
+
topK: import_zod9.z.number().int().positive().default(5),
|
|
5662
|
+
fallbackThreshold: import_zod9.z.number().min(0).max(1).default(0.95),
|
|
5663
|
+
pinBoost: import_zod9.z.number().min(0).max(1).default(0.15),
|
|
5664
|
+
identifierBoost: import_zod9.z.number().min(0).max(1).default(0.15),
|
|
5665
|
+
pageWindow: import_zod9.z.number().int().min(0).default(1),
|
|
5666
|
+
maxQueriesPerContext: import_zod9.z.number().int().positive().default(5)
|
|
5059
5667
|
});
|
|
5060
5668
|
boolVal = (v) => v === true || v === "true" || v === 1;
|
|
5061
5669
|
strVal = (v, fallback) => typeof v === "string" && v.length > 0 ? v : fallback;
|
|
@@ -5302,9 +5910,9 @@ async function resolveIdentifierPins({
|
|
|
5302
5910
|
system: set.strategy === "exact" ? EXACT_EXTRACTION_PROMPT(set) : FUZZY_EXTRACTION_PROMPT(set),
|
|
5303
5911
|
messages: [{ role: "user", content: question }],
|
|
5304
5912
|
output: import_ai4.Output.object({
|
|
5305
|
-
schema:
|
|
5306
|
-
hasMatches:
|
|
5307
|
-
matches:
|
|
5913
|
+
schema: import_zod10.z.object({
|
|
5914
|
+
hasMatches: import_zod10.z.boolean(),
|
|
5915
|
+
matches: import_zod10.z.array(import_zod10.z.string()).optional()
|
|
5308
5916
|
})
|
|
5309
5917
|
}),
|
|
5310
5918
|
maxOutputTokens: 300
|
|
@@ -5352,14 +5960,14 @@ async function resolveIdentifierPins({
|
|
|
5352
5960
|
);
|
|
5353
5961
|
return { pinsByContext, exactPinsByContext, steps };
|
|
5354
5962
|
}
|
|
5355
|
-
var import_fuse, import_ai4,
|
|
5963
|
+
var import_fuse, import_ai4, import_zod10, itemCaches, ensureItemsCache, FUZZY_EXTRACTION_PROMPT, EXACT_EXTRACTION_PROMPT;
|
|
5356
5964
|
var init_prefilter = __esm({
|
|
5357
5965
|
"ee/agentic-retrieval/pipeline/prefilter.ts"() {
|
|
5358
5966
|
"use strict";
|
|
5359
5967
|
init_cjs_shims();
|
|
5360
5968
|
import_fuse = __toESM(require("fuse.js"), 1);
|
|
5361
5969
|
import_ai4 = require("ai");
|
|
5362
|
-
|
|
5970
|
+
import_zod10 = require("zod");
|
|
5363
5971
|
init_with_retry();
|
|
5364
5972
|
init_text_utils();
|
|
5365
5973
|
init_config();
|
|
@@ -5444,11 +6052,11 @@ If explicit, return the knowledge base ids. If not, return an empty array.`;
|
|
|
5444
6052
|
system: buildDocPagePrompt(knownIdentifiers),
|
|
5445
6053
|
messages: [{ role: "user", content: question }],
|
|
5446
6054
|
output: import_ai5.Output.object({
|
|
5447
|
-
schema:
|
|
5448
|
-
hasFilenameHint:
|
|
5449
|
-
filenameHints:
|
|
5450
|
-
hasPageHint:
|
|
5451
|
-
pageNumber:
|
|
6055
|
+
schema: import_zod11.z.object({
|
|
6056
|
+
hasFilenameHint: import_zod11.z.boolean(),
|
|
6057
|
+
filenameHints: import_zod11.z.array(import_zod11.z.string()).optional(),
|
|
6058
|
+
hasPageHint: import_zod11.z.boolean(),
|
|
6059
|
+
pageNumber: import_zod11.z.number().int().nullable().optional()
|
|
5452
6060
|
})
|
|
5453
6061
|
}),
|
|
5454
6062
|
maxOutputTokens: 300
|
|
@@ -5475,9 +6083,9 @@ If explicit, return the knowledge base ids. If not, return an empty array.`;
|
|
|
5475
6083
|
temperature: 0,
|
|
5476
6084
|
system: kbSystemPrompt,
|
|
5477
6085
|
output: import_ai5.Output.object({
|
|
5478
|
-
schema:
|
|
5479
|
-
explicitlyRequestedKnowledgeBases:
|
|
5480
|
-
|
|
6086
|
+
schema: import_zod11.z.object({
|
|
6087
|
+
explicitlyRequestedKnowledgeBases: import_zod11.z.array(
|
|
6088
|
+
import_zod11.z.enum(enabledContexts.map((c) => c.id))
|
|
5481
6089
|
)
|
|
5482
6090
|
})
|
|
5483
6091
|
}),
|
|
@@ -5575,9 +6183,9 @@ ${extraInstructions}
|
|
|
5575
6183
|
system: classifyPrompt,
|
|
5576
6184
|
messages: [{ role: "user", content: question }],
|
|
5577
6185
|
output: import_ai5.Output.object({
|
|
5578
|
-
schema:
|
|
5579
|
-
ruleId:
|
|
5580
|
-
reason:
|
|
6186
|
+
schema: import_zod11.z.object({
|
|
6187
|
+
ruleId: import_zod11.z.enum(ruleIds),
|
|
6188
|
+
reason: import_zod11.z.string()
|
|
5581
6189
|
})
|
|
5582
6190
|
}),
|
|
5583
6191
|
maxOutputTokens: 200
|
|
@@ -5636,13 +6244,13 @@ ${extraInstructions}
|
|
|
5636
6244
|
};
|
|
5637
6245
|
}
|
|
5638
6246
|
}
|
|
5639
|
-
var import_ai5,
|
|
6247
|
+
var import_ai5, import_zod11, MAX_USER_PIN_MATCHES, buildDocPagePrompt;
|
|
5640
6248
|
var init_routing = __esm({
|
|
5641
6249
|
"ee/agentic-retrieval/pipeline/routing.ts"() {
|
|
5642
6250
|
"use strict";
|
|
5643
6251
|
init_cjs_shims();
|
|
5644
6252
|
import_ai5 = require("ai");
|
|
5645
|
-
|
|
6253
|
+
import_zod11 = require("zod");
|
|
5646
6254
|
init_with_retry();
|
|
5647
6255
|
init_prefilter();
|
|
5648
6256
|
init_text_utils();
|
|
@@ -5914,8 +6522,8 @@ async function runMemoryPhase({
|
|
|
5914
6522
|
}
|
|
5915
6523
|
],
|
|
5916
6524
|
output: import_ai6.Output.object({
|
|
5917
|
-
schema:
|
|
5918
|
-
relevantChunkIds:
|
|
6525
|
+
schema: import_zod12.z.object({
|
|
6526
|
+
relevantChunkIds: import_zod12.z.array(import_zod12.z.string()).describe(
|
|
5919
6527
|
"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."
|
|
5920
6528
|
)
|
|
5921
6529
|
})
|
|
@@ -6031,17 +6639,17 @@ ${glossary.map((g) => `${g.term} : ${g.meaning}`).join("\n")}` : "";
|
|
|
6031
6639
|
}
|
|
6032
6640
|
],
|
|
6033
6641
|
output: import_ai6.Output.object({
|
|
6034
|
-
schema:
|
|
6035
|
-
overrides:
|
|
6642
|
+
schema: import_zod12.z.object({
|
|
6643
|
+
overrides: import_zod12.z.boolean().describe(
|
|
6036
6644
|
"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."
|
|
6037
6645
|
),
|
|
6038
|
-
confidence:
|
|
6646
|
+
confidence: import_zod12.z.enum(["high", "medium", "low"]).describe(
|
|
6039
6647
|
"Confidence that the selected memory chunk(s) fully and directly answer the question."
|
|
6040
6648
|
),
|
|
6041
|
-
authoritativeChunkIds:
|
|
6649
|
+
authoritativeChunkIds: import_zod12.z.array(import_zod12.z.string()).describe(
|
|
6042
6650
|
"The chunk_ids of the memory chunk(s) that directly answer the question. Empty if overrides is false."
|
|
6043
6651
|
),
|
|
6044
|
-
reason:
|
|
6652
|
+
reason: import_zod12.z.string().describe(
|
|
6045
6653
|
"One short sentence: why this memory does or does not directly answer the question."
|
|
6046
6654
|
)
|
|
6047
6655
|
})
|
|
@@ -6072,9 +6680,9 @@ ${glossary.map((g) => `${g.term} : ${g.meaning}`).join("\n")}` : "";
|
|
|
6072
6680
|
system: "You are a helpful assistant that will strictly follow the user's instructions.",
|
|
6073
6681
|
messages: [{ role: "user", content: PROMPT_EXTRACT_PRIORITIZED_FILES }],
|
|
6074
6682
|
output: import_ai6.Output.object({
|
|
6075
|
-
schema:
|
|
6076
|
-
shouldPrioritizeFiles:
|
|
6077
|
-
fileNameHints:
|
|
6683
|
+
schema: import_zod12.z.object({
|
|
6684
|
+
shouldPrioritizeFiles: import_zod12.z.boolean(),
|
|
6685
|
+
fileNameHints: import_zod12.z.array(import_zod12.z.string()).optional()
|
|
6078
6686
|
})
|
|
6079
6687
|
}),
|
|
6080
6688
|
maxOutputTokens: 300
|
|
@@ -6093,10 +6701,10 @@ ${glossary.map((g) => `${g.term} : ${g.meaning}`).join("\n")}` : "";
|
|
|
6093
6701
|
system: "You are a helpful assistant that will strictly follow the user's instructions.",
|
|
6094
6702
|
messages: [{ role: "user", content: QUERY_AUGMENTATION_PROMPT }],
|
|
6095
6703
|
output: import_ai6.Output.object({
|
|
6096
|
-
schema:
|
|
6097
|
-
updatedUserQuestion:
|
|
6098
|
-
updatedRelevantKeywords:
|
|
6099
|
-
updatedImportantKeyword:
|
|
6704
|
+
schema: import_zod12.z.object({
|
|
6705
|
+
updatedUserQuestion: import_zod12.z.string(),
|
|
6706
|
+
updatedRelevantKeywords: import_zod12.z.array(import_zod12.z.string()),
|
|
6707
|
+
updatedImportantKeyword: import_zod12.z.string()
|
|
6100
6708
|
})
|
|
6101
6709
|
}),
|
|
6102
6710
|
maxOutputTokens: 600
|
|
@@ -6182,13 +6790,13 @@ ${glossary.map((g) => `${g.term} : ${g.meaning}`).join("\n")}` : "";
|
|
|
6182
6790
|
return neutralResult(question, keywords, importantKeyword);
|
|
6183
6791
|
}
|
|
6184
6792
|
}
|
|
6185
|
-
var import_ai6,
|
|
6793
|
+
var import_ai6, import_zod12, MEMORY_OVERRIDE_MIN_CONFIDENCE, MEMORY_SYNTHETIC_RERANK_SCORE, ITEM_CACHE_TTL_MS, memoryItemCache;
|
|
6186
6794
|
var init_memory = __esm({
|
|
6187
6795
|
"ee/agentic-retrieval/pipeline/memory.ts"() {
|
|
6188
6796
|
"use strict";
|
|
6189
6797
|
init_cjs_shims();
|
|
6190
6798
|
import_ai6 = require("ai");
|
|
6191
|
-
|
|
6799
|
+
import_zod12 = require("zod");
|
|
6192
6800
|
init_with_retry();
|
|
6193
6801
|
init_multi_query();
|
|
6194
6802
|
init_prefilter();
|
|
@@ -6684,11 +7292,11 @@ function createAgenticRetrievalTool(opts) {
|
|
|
6684
7292
|
default: '{"topK":5,"fallbackThreshold":0.95,"pinBoost":0.15,"identifierBoost":0.15,"pageWindow":1,"maxQueriesPerContext":5}'
|
|
6685
7293
|
}
|
|
6686
7294
|
],
|
|
6687
|
-
inputSchema:
|
|
6688
|
-
userQuery:
|
|
6689
|
-
relevantKeywords:
|
|
6690
|
-
importantKeyword:
|
|
6691
|
-
confirmedContextIds:
|
|
7295
|
+
inputSchema: import_zod13.z.object({
|
|
7296
|
+
userQuery: import_zod13.z.string().describe("The original unaltered question from the user"),
|
|
7297
|
+
relevantKeywords: import_zod13.z.array(import_zod13.z.string()).describe("Keywords extracted from the user's question relevant to the search"),
|
|
7298
|
+
importantKeyword: import_zod13.z.string().describe("The single most important keyword from the user's question"),
|
|
7299
|
+
confirmedContextIds: import_zod13.z.array(import_zod13.z.string()).optional().describe(
|
|
6692
7300
|
"Knowledge base IDs explicitly confirmed by the user to be used in the retrieval. When present, only searches these contexts."
|
|
6693
7301
|
)
|
|
6694
7302
|
}),
|
|
@@ -7079,12 +7687,12 @@ Verified answer:
|
|
|
7079
7687
|
}
|
|
7080
7688
|
});
|
|
7081
7689
|
}
|
|
7082
|
-
var
|
|
7690
|
+
var import_zod13;
|
|
7083
7691
|
var init_pipeline = __esm({
|
|
7084
7692
|
"ee/agentic-retrieval/pipeline/index.ts"() {
|
|
7085
7693
|
"use strict";
|
|
7086
7694
|
init_cjs_shims();
|
|
7087
|
-
|
|
7695
|
+
import_zod13 = require("zod");
|
|
7088
7696
|
init_tool();
|
|
7089
7697
|
init_entitlements();
|
|
7090
7698
|
init_resolve_reranker();
|
|
@@ -7102,91 +7710,6 @@ var init_pipeline = __esm({
|
|
|
7102
7710
|
}
|
|
7103
7711
|
});
|
|
7104
7712
|
|
|
7105
|
-
// src/exulu/litellm/catalog.ts
|
|
7106
|
-
var catalog_exports = {};
|
|
7107
|
-
__export(catalog_exports, {
|
|
7108
|
-
__resetLiteLLMCatalogCacheForTesting: () => __resetLiteLLMCatalogCacheForTesting,
|
|
7109
|
-
fetchLiteLLMCatalog: () => fetchLiteLLMCatalog,
|
|
7110
|
-
findLiteLLMModel: () => findLiteLLMModel
|
|
7111
|
-
});
|
|
7112
|
-
var CACHE_TTL_MS, _cache, __resetLiteLLMCatalogCacheForTesting, fetchLiteLLMCatalog, findLiteLLMModel;
|
|
7113
|
-
var init_catalog = __esm({
|
|
7114
|
-
"src/exulu/litellm/catalog.ts"() {
|
|
7115
|
-
"use strict";
|
|
7116
|
-
init_cjs_shims();
|
|
7117
|
-
CACHE_TTL_MS = 3e4;
|
|
7118
|
-
__resetLiteLLMCatalogCacheForTesting = () => {
|
|
7119
|
-
_cache = void 0;
|
|
7120
|
-
};
|
|
7121
|
-
fetchLiteLLMCatalog = async () => {
|
|
7122
|
-
if (process.env.EXULU_USE_LITELLM !== "true") return [];
|
|
7123
|
-
if (_cache && _cache.expiresAt > Date.now()) {
|
|
7124
|
-
return _cache.items;
|
|
7125
|
-
}
|
|
7126
|
-
const host = process.env.LITELLM_HOST ?? "127.0.0.1";
|
|
7127
|
-
const port = process.env.LITELLM_PORT ?? "4000";
|
|
7128
|
-
const masterKey = process.env.LITELLM_MASTER_KEY;
|
|
7129
|
-
if (!masterKey) return [];
|
|
7130
|
-
try {
|
|
7131
|
-
const res = await fetch(`http://${host}:${port}/model/info`, {
|
|
7132
|
-
method: "GET",
|
|
7133
|
-
headers: { Authorization: `Bearer ${masterKey}` }
|
|
7134
|
-
});
|
|
7135
|
-
if (!res.ok) {
|
|
7136
|
-
console.error(
|
|
7137
|
-
`[EXULU] litellmCatalog: LiteLLM /model/info returned ${res.status}`
|
|
7138
|
-
);
|
|
7139
|
-
return [];
|
|
7140
|
-
}
|
|
7141
|
-
const json = await res.json();
|
|
7142
|
-
const items = (Array.isArray(json?.data) ? json.data : []).map((m) => ({
|
|
7143
|
-
// filter out trailing * from model_name
|
|
7144
|
-
model_name: m.model_name.replace(/\*$/, ""),
|
|
7145
|
-
upstream_model: m.litellm_params?.model ?? null,
|
|
7146
|
-
tags: Array.isArray(m.model_info?.tags) ? m.model_info.tags : [],
|
|
7147
|
-
brand: m.model_info?.brand ?? null,
|
|
7148
|
-
type: m.model_info?.type ?? null,
|
|
7149
|
-
region: m.model_info?.region ?? null,
|
|
7150
|
-
max_tokens: m.model_info?.max_tokens ?? null,
|
|
7151
|
-
max_input_tokens: m.model_info?.max_input_tokens ?? null,
|
|
7152
|
-
input_cost_per_million_tokens: m.model_info?.input_cost_per_token * 1e6,
|
|
7153
|
-
output_cost_per_million_tokens: m.model_info?.output_cost_per_token * 1e6,
|
|
7154
|
-
active: m.model_info?.active ?? true,
|
|
7155
|
-
max_output_tokens: m.model_info?.max_output_tokens ?? null,
|
|
7156
|
-
supports_vision: !!m.model_info?.supports_vision,
|
|
7157
|
-
supports_function_calling: !!m.model_info?.supports_function_calling,
|
|
7158
|
-
supports_pdf_input: !!m.model_info?.supports_pdf_input,
|
|
7159
|
-
supports_audio_input: !!m.model_info?.supports_audio_input,
|
|
7160
|
-
sizes: Array.isArray(m.model_info?.sizes) ? m.model_info.sizes : null,
|
|
7161
|
-
qualities: Array.isArray(m.model_info?.qualities) ? m.model_info.qualities : null,
|
|
7162
|
-
supports_edit: !!m.model_info?.supports_edit,
|
|
7163
|
-
max_n: typeof m.model_info?.max_n === "number" ? m.model_info.max_n : null
|
|
7164
|
-
}));
|
|
7165
|
-
const map = /* @__PURE__ */ new Map();
|
|
7166
|
-
for (const item of items) {
|
|
7167
|
-
const key = `${item.model_name}-${item.upstream_model}`;
|
|
7168
|
-
if (map.has(key)) {
|
|
7169
|
-
map.get(key).tags.push(...item.tags);
|
|
7170
|
-
} else {
|
|
7171
|
-
map.set(key, item);
|
|
7172
|
-
}
|
|
7173
|
-
}
|
|
7174
|
-
const uniqueItems = Array.from(map.values());
|
|
7175
|
-
_cache = { expiresAt: Date.now() + CACHE_TTL_MS, items: uniqueItems };
|
|
7176
|
-
return uniqueItems.filter((m) => m.type !== "speech_to_text" && m.type !== "text_to_speech");
|
|
7177
|
-
} catch (err) {
|
|
7178
|
-
console.error("[EXULU] litellmCatalog: failed to fetch /model/info:", err);
|
|
7179
|
-
return [];
|
|
7180
|
-
}
|
|
7181
|
-
};
|
|
7182
|
-
findLiteLLMModel = async (modelName) => {
|
|
7183
|
-
if (!modelName) return void 0;
|
|
7184
|
-
const items = await fetchLiteLLMCatalog();
|
|
7185
|
-
return items.find((m) => m.model_name === modelName);
|
|
7186
|
-
};
|
|
7187
|
-
}
|
|
7188
|
-
});
|
|
7189
|
-
|
|
7190
7713
|
// src/index.ts
|
|
7191
7714
|
var index_exports = {};
|
|
7192
7715
|
__export(index_exports, {
|
|
@@ -18596,79 +19119,8 @@ async function parseSkillFrontmatter(zipBytes) {
|
|
|
18596
19119
|
return { name: fm.name, description: fm.description };
|
|
18597
19120
|
}
|
|
18598
19121
|
|
|
18599
|
-
// src/sessions/pdf-preview-cache.ts
|
|
18600
|
-
init_cjs_shims();
|
|
18601
|
-
var import_node_child_process4 = require("child_process");
|
|
18602
|
-
var import_node_fs6 = require("fs");
|
|
18603
|
-
var import_promises2 = require("fs/promises");
|
|
18604
|
-
var import_node_path5 = require("path");
|
|
18605
|
-
var import_node_util3 = require("util");
|
|
18606
|
-
init_uppy();
|
|
18607
|
-
var execAsync3 = (0, import_node_util3.promisify)(import_node_child_process4.exec);
|
|
18608
|
-
var CACHE_ROOT = "/tmp/exulu-pdf-cache";
|
|
18609
|
-
var CACHE_IN = (0, import_node_path5.join)(CACHE_ROOT, "_in");
|
|
18610
|
-
var CACHE_OUT = (0, import_node_path5.join)(CACHE_ROOT, "_out");
|
|
18611
|
-
var inFlight = /* @__PURE__ */ new Map();
|
|
18612
|
-
var PreviewRenderError = class extends Error {
|
|
18613
|
-
constructor(message) {
|
|
18614
|
-
super(message);
|
|
18615
|
-
this.name = "PreviewRenderError";
|
|
18616
|
-
}
|
|
18617
|
-
};
|
|
18618
|
-
function sanitizeEtag(raw) {
|
|
18619
|
-
return raw.replace(/^"|"$/g, "").replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
18620
|
-
}
|
|
18621
|
-
async function getPdfPreviewBytes(opts) {
|
|
18622
|
-
const { sourceKey, etag, config } = opts;
|
|
18623
|
-
const safeEtag = sanitizeEtag(etag);
|
|
18624
|
-
if (!safeEtag) {
|
|
18625
|
-
throw new PreviewRenderError(`Invalid ETag for ${sourceKey}`);
|
|
18626
|
-
}
|
|
18627
|
-
const cachedPath = (0, import_node_path5.join)(CACHE_ROOT, `${safeEtag}.pdf`);
|
|
18628
|
-
if ((0, import_node_fs6.existsSync)(cachedPath)) {
|
|
18629
|
-
return (0, import_promises2.readFile)(cachedPath);
|
|
18630
|
-
}
|
|
18631
|
-
const existing = inFlight.get(safeEtag);
|
|
18632
|
-
if (existing) return existing;
|
|
18633
|
-
const promise = (async () => {
|
|
18634
|
-
try {
|
|
18635
|
-
await (0, import_promises2.mkdir)(CACHE_IN, { recursive: true });
|
|
18636
|
-
await (0, import_promises2.mkdir)(CACHE_OUT, { recursive: true });
|
|
18637
|
-
const ext = ((0, import_node_path5.extname)(sourceKey) || ".docx").toLowerCase();
|
|
18638
|
-
const inputPath = (0, import_node_path5.join)(CACHE_IN, `${safeEtag}${ext}`);
|
|
18639
|
-
const outputPath = (0, import_node_path5.join)(CACHE_OUT, `${safeEtag}.pdf`);
|
|
18640
|
-
try {
|
|
18641
|
-
const bytes = await getS3ObjectBytes(sourceKey, config);
|
|
18642
|
-
await (0, import_promises2.writeFile)(inputPath, bytes);
|
|
18643
|
-
try {
|
|
18644
|
-
await execAsync3(
|
|
18645
|
-
`soffice --headless --convert-to pdf "${inputPath}" --outdir "${CACHE_OUT}"`,
|
|
18646
|
-
{ timeout: 6e4, maxBuffer: 16 * 1024 * 1024 }
|
|
18647
|
-
);
|
|
18648
|
-
} catch (err) {
|
|
18649
|
-
throw new PreviewRenderError(
|
|
18650
|
-
`LibreOffice conversion failed for ${sourceKey} (etag ${etag}): ${err?.stderr ?? err?.message ?? "unknown error"}`
|
|
18651
|
-
);
|
|
18652
|
-
}
|
|
18653
|
-
if (!(0, import_node_fs6.existsSync)(outputPath)) {
|
|
18654
|
-
throw new PreviewRenderError(
|
|
18655
|
-
`LibreOffice produced no output for ${sourceKey} (etag ${etag})`
|
|
18656
|
-
);
|
|
18657
|
-
}
|
|
18658
|
-
await (0, import_promises2.rename)(outputPath, cachedPath);
|
|
18659
|
-
return await (0, import_promises2.readFile)(cachedPath);
|
|
18660
|
-
} finally {
|
|
18661
|
-
await (0, import_promises2.rm)(inputPath, { force: true });
|
|
18662
|
-
}
|
|
18663
|
-
} finally {
|
|
18664
|
-
inFlight.delete(safeEtag);
|
|
18665
|
-
}
|
|
18666
|
-
})();
|
|
18667
|
-
inFlight.set(safeEtag, promise);
|
|
18668
|
-
return promise;
|
|
18669
|
-
}
|
|
18670
|
-
|
|
18671
19122
|
// src/exulu/routes.ts
|
|
19123
|
+
init_pdf_preview_cache();
|
|
18672
19124
|
init_create_sandbox();
|
|
18673
19125
|
var import_utils5 = require("@apollo/utils.keyvaluecache");
|
|
18674
19126
|
var import_body_parser = __toESM(require("body-parser"), 1);
|
|
@@ -18814,6 +19266,7 @@ function composePrepareSteps(...guards) {
|
|
|
18814
19266
|
}
|
|
18815
19267
|
|
|
18816
19268
|
// src/exulu/provider.ts
|
|
19269
|
+
init_tool_image_attachments();
|
|
18817
19270
|
init_sanitize_tool_name();
|
|
18818
19271
|
|
|
18819
19272
|
// src/exulu/auto-decline-stale-approvals.ts
|
|
@@ -18845,7 +19298,7 @@ var autoDeclineStaleApprovals = (messages) => {
|
|
|
18845
19298
|
};
|
|
18846
19299
|
|
|
18847
19300
|
// src/exulu/provider.ts
|
|
18848
|
-
var
|
|
19301
|
+
var import_zod14 = require("zod");
|
|
18849
19302
|
init_tool();
|
|
18850
19303
|
init_resolve_model();
|
|
18851
19304
|
init_statistics2();
|
|
@@ -18865,7 +19318,7 @@ init_check_record_access();
|
|
|
18865
19318
|
init_client();
|
|
18866
19319
|
init_statistics();
|
|
18867
19320
|
init_convert_exulu_tools_to_ai_sdk_tools();
|
|
18868
|
-
var
|
|
19321
|
+
var import_officeparser2 = require("officeparser");
|
|
18869
19322
|
init_singleton();
|
|
18870
19323
|
init_entitlements();
|
|
18871
19324
|
|
|
@@ -18994,9 +19447,9 @@ var ExuluProvider = class {
|
|
|
18994
19447
|
name: `${agent.name}`,
|
|
18995
19448
|
type: "agent",
|
|
18996
19449
|
category: "agents",
|
|
18997
|
-
inputSchema:
|
|
18998
|
-
prompt:
|
|
18999
|
-
information:
|
|
19450
|
+
inputSchema: import_zod14.z.object({
|
|
19451
|
+
prompt: import_zod14.z.string().describe("The prompt (usually a question for the agent) to send to the agent."),
|
|
19452
|
+
information: import_zod14.z.string().describe("A summary of relevant context / information from the current session")
|
|
19000
19453
|
}),
|
|
19001
19454
|
description: `This tool calls an agent named: ${agent.name}. The agent does the following: ${agent.description}.`,
|
|
19002
19455
|
config: [],
|
|
@@ -19303,7 +19756,7 @@ When a tool execution is not approved by the user, do not retry it unless explic
|
|
|
19303
19756
|
// Stop after the image_generation tool fires — the widget IS the
|
|
19304
19757
|
// assistant's response, no follow-up text turn is wanted (same
|
|
19305
19758
|
// reasoning as question_ask: the UI artifact is the message).
|
|
19306
|
-
prepareStep: composePrepareSteps(contextGuard(contextWindow), retrievalGuard, finalAnswerGuard(turnBudget)),
|
|
19759
|
+
prepareStep: composePrepareSteps(contextGuard(contextWindow), retrievalGuard, finalAnswerGuard(turnBudget), imageAttachmentGuard()),
|
|
19307
19760
|
stopWhen: [(0, import_ai11.stepCountIs)(turnBudget), (0, import_ai11.hasToolCall)("image_generation")]
|
|
19308
19761
|
});
|
|
19309
19762
|
console.log("[EXULU] Output: " + JSON.stringify(output, null, 2));
|
|
@@ -19366,7 +19819,7 @@ When a tool execution is not approved by the user, do not retry it unless explic
|
|
|
19366
19819
|
}),
|
|
19367
19820
|
maxRetries: 2,
|
|
19368
19821
|
tools,
|
|
19369
|
-
prepareStep: composePrepareSteps(contextGuard(contextWindow), retrievalGuard, finalAnswerGuard(turnBudget)),
|
|
19822
|
+
prepareStep: composePrepareSteps(contextGuard(contextWindow), retrievalGuard, finalAnswerGuard(turnBudget), imageAttachmentGuard()),
|
|
19370
19823
|
stopWhen: [(0, import_ai11.stepCountIs)(turnBudget), (0, import_ai11.hasToolCall)("image_generation")]
|
|
19371
19824
|
});
|
|
19372
19825
|
if (statistics) {
|
|
@@ -19460,7 +19913,7 @@ When a tool execution is not approved by the user, do not retry it unless explic
|
|
|
19460
19913
|
};
|
|
19461
19914
|
}
|
|
19462
19915
|
const arrayBuffer = await response.arrayBuffer();
|
|
19463
|
-
const extractedText = await (0,
|
|
19916
|
+
const extractedText = await (0, import_officeparser2.parseOfficeAsync)(arrayBuffer, {
|
|
19464
19917
|
outputErrorToConsole: false,
|
|
19465
19918
|
newlineDelimiter: "\n"
|
|
19466
19919
|
});
|
|
@@ -19781,7 +20234,7 @@ When a tool execution is not approved by the user, do not retry it unless explic
|
|
|
19781
20234
|
);
|
|
19782
20235
|
},
|
|
19783
20236
|
// todo allow configuring the step budget per skill
|
|
19784
|
-
prepareStep: composePrepareSteps(contextGuard(contextWindow), retrievalGuard, finalAnswerGuard(turnBudget)),
|
|
20237
|
+
prepareStep: composePrepareSteps(contextGuard(contextWindow), retrievalGuard, finalAnswerGuard(turnBudget), imageAttachmentGuard()),
|
|
19785
20238
|
stopWhen: [(0, import_ai11.stepCountIs)(turnBudget), (0, import_ai11.hasToolCall)("image_generation")]
|
|
19786
20239
|
});
|
|
19787
20240
|
return {
|
|
@@ -19836,12 +20289,12 @@ var saveChat = async ({
|
|
|
19836
20289
|
// src/exulu/suggestions.ts
|
|
19837
20290
|
init_cjs_shims();
|
|
19838
20291
|
var import_ai12 = require("ai");
|
|
19839
|
-
var
|
|
20292
|
+
var import_zod15 = require("zod");
|
|
19840
20293
|
var SUGGESTIONS_SYSTEM_PROMPT = "You generate short follow-up message suggestions for the user. You are NOT continuing the conversation as the assistant \u2014 you are predicting what the user might want to say next. Suggest up to 3 short follow-up questions or messages the user might want to send next. Each suggestion must be written from the user's perspective (first person) and be 12 words or fewer. You MUST submit your answer by calling the `submit_suggestions` tool exactly once. Do not emit any plain text \u2014 only the tool call.";
|
|
19841
20294
|
var submitSuggestionsTool = (0, import_ai12.tool)({
|
|
19842
20295
|
description: "Submit the final list of follow-up message suggestions for the user. Must be called exactly once. Each suggestion is written from the user's perspective (first person) and is 12 words or fewer.",
|
|
19843
|
-
inputSchema:
|
|
19844
|
-
suggestions:
|
|
20296
|
+
inputSchema: import_zod15.z.object({
|
|
20297
|
+
suggestions: import_zod15.z.array(import_zod15.z.string()).max(3)
|
|
19845
20298
|
})
|
|
19846
20299
|
});
|
|
19847
20300
|
var MAX_CHARS_PER_MESSAGE = 1e4;
|
|
@@ -20403,7 +20856,7 @@ See docs/superpowers/specs/2026-05-31-in-chat-image-generation-design.md for the
|
|
|
20403
20856
|
};
|
|
20404
20857
|
|
|
20405
20858
|
// src/exulu/routes.ts
|
|
20406
|
-
var
|
|
20859
|
+
var import_node_path9 = require("path");
|
|
20407
20860
|
init_tags();
|
|
20408
20861
|
init_admin_client();
|
|
20409
20862
|
init_env();
|
|
@@ -20532,6 +20985,7 @@ init_resolve_model();
|
|
|
20532
20985
|
init_sanitize_tool_name();
|
|
20533
20986
|
init_context_budget();
|
|
20534
20987
|
init_supervisor();
|
|
20988
|
+
init_tool_image_attachments();
|
|
20535
20989
|
function convertOpenAIToolsToAiSdkTools(tools) {
|
|
20536
20990
|
return Object.fromEntries(
|
|
20537
20991
|
tools.map((t) => {
|
|
@@ -20938,7 +21392,7 @@ ${project.description}` : ""}` : "",
|
|
|
20938
21392
|
messages: coreMessages,
|
|
20939
21393
|
tools: hasTools ? activeTools : void 0,
|
|
20940
21394
|
maxRetries: 2,
|
|
20941
|
-
prepareStep: clientTools.length > 0 ? void 0 : composePrepareSteps(contextGuard(contextWindow), gatewayRetrievalGuard, finalAnswerGuard(turnBudget)),
|
|
21395
|
+
prepareStep: clientTools.length > 0 ? void 0 : composePrepareSteps(contextGuard(contextWindow), gatewayRetrievalGuard, finalAnswerGuard(turnBudget), imageAttachmentGuard()),
|
|
20942
21396
|
stopWhen: clientTools.length > 0 ? void 0 : [(0, import_ai14.stepCountIs)(turnBudget)],
|
|
20943
21397
|
onError: (error) => {
|
|
20944
21398
|
console.error("[OPENAI GATEWAY] stream error:", error);
|
|
@@ -20978,7 +21432,7 @@ ${project.description}` : ""}` : "",
|
|
|
20978
21432
|
messages: coreMessages,
|
|
20979
21433
|
tools: hasTools ? activeTools : void 0,
|
|
20980
21434
|
maxRetries: 2,
|
|
20981
|
-
prepareStep: clientTools.length > 0 ? void 0 : composePrepareSteps(contextGuard(contextWindow), gatewayRetrievalGuard, finalAnswerGuard(turnBudget)),
|
|
21435
|
+
prepareStep: clientTools.length > 0 ? void 0 : composePrepareSteps(contextGuard(contextWindow), gatewayRetrievalGuard, finalAnswerGuard(turnBudget), imageAttachmentGuard()),
|
|
20982
21436
|
stopWhen: clientTools.length > 0 ? void 0 : [(0, import_ai14.stepCountIs)(turnBudget)]
|
|
20983
21437
|
});
|
|
20984
21438
|
res.json(transformCompletion(text, usage.inputTokens ?? 0, usage.outputTokens ?? 0, ctx));
|
|
@@ -21212,7 +21666,7 @@ async function filterReadableSkills(db2, skills, user) {
|
|
|
21212
21666
|
return out;
|
|
21213
21667
|
}
|
|
21214
21668
|
|
|
21215
|
-
// src/skills/bootstrap/
|
|
21669
|
+
// src/skills/bootstrap/imp-skills.ts
|
|
21216
21670
|
init_cjs_shims();
|
|
21217
21671
|
|
|
21218
21672
|
// src/skills/bootstrap/clients.ts
|
|
@@ -21270,35 +21724,35 @@ var CLIENT_MANIFEST = [
|
|
|
21270
21724
|
// exception: nested under agent/
|
|
21271
21725
|
];
|
|
21272
21726
|
|
|
21273
|
-
// src/skills/bootstrap/
|
|
21727
|
+
// src/skills/bootstrap/imp-sh.generated.ts
|
|
21274
21728
|
init_cjs_shims();
|
|
21275
|
-
var EXULU_SH_B64 = "IyEvYmluL3NoCiMgZXh1bHUg4oCUIGhlbHBlciBmb3IgdGhlIEV4dWx1IGNlbnRyYWwgc2tpbGwgbGlicmFyeS4gVGhlIGFnZW50IGludm9rZXMgdGhpcwojIChuZXZlciByYXcgY3VybCk6IHRoZSB0b2tlbiBpcyByZWFkIGZyb20gdGhlIGNvbmZpZyBmaWxlIGhlcmUgYW5kIHNlbnQgdmlhCiMgYHgtYXBpLWtleTogQmVhcmVyYCwgc28gaXQgbmV2ZXIgZW50ZXJzIHRoZSBtb2RlbCBjb250ZXh0LiBDbGllbnQgZmFuLW91dAojIChjb3B5L3N5bWxpbmsgYWNyb3NzIGFnZW50IGNsaWVudHMpIGlzIGRldGVybWluaXN0aWMuCnNldCAtZXUKCkNPTkZJR19ESVI9IiRIT01FLy5jb25maWcvZXh1bHUiCkNPTkZJR19GSUxFPSIkQ09ORklHX0RJUi9za2lsbHMuanNvbiIKCmRpZSgpIHsgcHJpbnRmICdleHVsdTogJXNcbicgIiQqIiA+JjI7IGV4aXQgMTsgfQppbmZvKCkgeyBwcmludGYgJyVzXG4nICIkKiIgPiYyOyB9Cgpqc29uX3N0cigpIHsgIyBqc29uX3N0ciA8a2V5PiA8ZmlsZT4g4oCUIGZsYXQgImtleSI6InZhbHVlIgogIHNlZCAtbiAicy8uKlwiJDFcIltbOnNwYWNlOl1dKjpbWzpzcGFjZTpdXSpcIlxcKFteXCJdKlxcKVwiLiovXFwxL3AiICIkMiIgfCBoZWFkIC1uMQp9CgpbIC1mICIkQ09ORklHX0ZJTEUiIF0gfHwgZGllICJub3QgY29uZmlndXJlZCDigJQgcnVuOiBjdXJsIC1mc1NMIDxiYXNlX3VybD4vYXBpL3NraWxscy9pbnN0YWxsLnNoIHwgc2giCgpCQVNFX1VSTD0iJChqc29uX3N0ciBiYXNlX3VybCAiJENPTkZJR19GSUxFIikiCkJBQ0tFTkQ9IiQoanNvbl9zdHIgYmFja2VuZCAiJENPTkZJR19GSUxFIikiCkFQSV9LRVk9IiQoanNvbl9zdHIgYXBpX2tleSAiJENPTkZJR19GSUxFIikiCkxJTktfTU9ERT0iJChqc29uX3N0ciBsaW5rX21vZGUgIiRDT05GSUdfRklMRSIpIgpTQ09QRT0iJChqc29uX3N0ciBzY29wZSAiJENPTkZJR19GSUxFIikiCkNMSUVOVFM9IiQoc2VkIC1uICdzLy4qImNsaWVudHMiW1s6c3BhY2U6XV0qOltbOnNwYWNlOl1dKlxbXChbXl1dKlwpXF0uKi9cMS9wJyAiJENPTkZJR19GSUxFIiB8IHRyICcsJyAnICcgfCB0ciAtZCAnIicgfCB0ciAtcyAnICcpIgoKWyAtbiAiJENMSUVOVFMiIF0gfHwgQ0xJRU5UUz0iYWdlbnRzIgpbIC1uICIkTElOS19NT0RFIiBdIHx8IExJTktfTU9ERT0iY29weSIKWyAtbiAiJFNDT1BFIiBdIHx8IFNDT1BFPSJwcm9qZWN0IgoKaWYgWyAteiAiJEJBQ0tFTkQiIF07IHRoZW4KICBbIC1uICIkQkFTRV9VUkwiIF0gfHwgZGllICJjb25maWcgaGFzIG5vIGJhY2tlbmQgYW5kIG5vIGJhc2VfdXJsIgogIEJBQ0tFTkQ9IiQoY3VybCAtZnNTTCAiJEJBU0VfVVJML2FwaS9jb25maWciIHwgc2VkIC1uICdzLy4qImJhY2tlbmQiW1s6c3BhY2U6XV0qOltbOnNwYWNlOl1dKiJcKFteIl0qXCkiLiovXDEvcCcgfCBoZWFkIC1uMSkiCiAgWyAtbiAiJEJBQ0tFTkQiIF0gfHwgZGllICJjb3VsZCBub3QgcmVzb2x2ZSBiYWNrZW5kIGZyb20gJEJBU0VfVVJML2FwaS9jb25maWciCmZpCkJBQ0tFTkQ9IiQocHJpbnRmICclcycgIiRCQUNLRU5EIiB8IHNlZCAnczovKiQ6OicpIgoKUk9PVD0iJFBXRCIKWyAiJFNDT1BFIiA9ICJob21lIiBdICYmIFJPT1Q9IiRIT01FIgoKIyBkaXJfZm9yIENMSUVOVF9JRCAtPiByZWxhdGl2ZSBza2lsbCBkaXIgKGdlbmVyYXRlZCBmcm9tIHRoZSBtYW5pZmVzdCBpbiB0aGUKIyByZWFsIGJ1aWxkOyBhIHJlcHJlc2VudGF0aXZlIHN1YnNldCBoZXJlIGZvciBsb2NhbCB0ZXN0aW5nKS4KZGlyX2ZvcigpIHsKICBjYXNlICIkMSIgaW4KX19ESVJfRk9SX0NBU0VTX18KICAgICopIHJldHVybiAxIDs7CiAgZXNhYwp9CgphcGkoKSB7ICMgYXBpIDxNRVRIT0Q+IDxwYXRoPiBbZXh0cmEgY3VybCBhcmdzLi4uXSAtPiBib2R5IG9uIHN0ZG91dAogIG09IiQxIjsgcD0iJDIiOyBzaGlmdCAyCiAgY3VybCAtZnNTIC1YICIkbSIgIiRCQUNLRU5EJHAiIC1IICJ4LWFwaS1rZXk6IEJlYXJlciAkQVBJX0tFWSIgIiRAIgp9CgptZXRhX3ZlcnNpb24oKSB7ICMgbWV0YV92ZXJzaW9uIDxuYW1lPiAtPiBjdXJyZW50X3ZlcnNpb24gZnJvbSByZWdpc3RyeQogIGFwaSBHRVQgIi9za2lsbHMvcmVnaXN0cnkvJDEiIDI+L2Rldi9udWxsIFwKICAgIHwgc2VkIC1uICdzLy4qImN1cnJlbnRfdmVyc2lvbiJbWzpzcGFjZTpdXSo6W1s6c3BhY2U6XV0qXChbMC05XVswLTldKlwpLiovXDEvcCcgfCBoZWFkIC1uMQp9CgpfbWFuYWdlZCgpIHsgIyBfbWFuYWdlZCA8ZGlyPiAtPiAwIGlmIHNhZmUgdG8gb3ZlcndyaXRlIChvdXJzIG9yIHN5bWxpbmsgb3IgYWJzZW50KQogIGQ9IiQxIgogIGlmIFsgLWUgIiRkIiBdICYmIFsgISAtTCAiJGQiIF0gJiYgWyAhIC1mICIkZC8uZXh1bHUtc2tpbGwuanNvbiIgXTsgdGhlbgogICAgaW5mbyAic2tpcCAkZCAoZXhpc3RzLCBub3QgbWFuYWdlZCBieSBleHVsdSkiOyByZXR1cm4gMQogIGZpCiAgcmV0dXJuIDAKfQoKX3B1dF9yZWFsKCkgeyAjIF9wdXRfcmVhbCA8ZGVzdD4gPHNyY2Rpcj4gPG1hcmtlci1qc29uPgogIF9tYW5hZ2VkICIkMSIgfHwgcmV0dXJuIDAKICBybSAtcmYgIiQxIjsgbWtkaXIgLXAgIiQxIjsgY3AgLVIgIiQyLy4iICIkMS8iCiAgcHJpbnRmICclc1xuJyAiJDMiID4gIiQxLy5leHVsdS1za2lsbC5qc29uIgp9CgpwbGFjZV9za2lsbCgpIHsgIyBwbGFjZV9za2lsbCA8bmFtZT4gPHNyY2Rpcj4gPHZlcnNpb24+CiAgcG5hbWU9IiQxIjsgcHNyYz0iJDIiOyBwdmVyPSIkezM6LTF9IgogIG1hcmtlcj0neyAibmFtZSI6ICInIiRwbmFtZSInIiwgInZlcnNpb24iOiAnIiRwdmVyIicsICJzb3VyY2UiOiAiJyIkQkFDS0VORCInIiB9JwogIGNhbm9uPSIkUk9PVC8uYWdlbnRzL3NraWxscy8kcG5hbWUiCiAgWyAiJExJTktfTU9ERSIgPSAic3ltbGluayIgXSAmJiBfcHV0X3JlYWwgIiRjYW5vbiIgIiRwc3JjIiAiJG1hcmtlciIKICBmb3IgaWQgaW4gJENMSUVOVFM7IGRvCiAgICBkPSIkKGRpcl9mb3IgIiRpZCIpIiB8fCB7IGluZm8gInVua25vd24gY2xpZW50OiAkaWQiOyBjb250aW51ZTsgfQogICAgcGFyZW50PSIkUk9PVC8kZCI7IGRlc3Q9IiRwYXJlbnQvJHBuYW1lIgogICAgaWYgWyAiJExJTktfTU9ERSIgPSAic3ltbGluayIgXSAmJiBbICIkaWQiICE9ICJhZ2VudHMiIF07IHRoZW4KICAgICAgX21hbmFnZWQgIiRkZXN0IiB8fCBjb250aW51ZQogICAgICBta2RpciAtcCAiJHBhcmVudCI7IHJtIC1yZiAiJGRlc3QiCiAgICAgIGlmIGxuIC1zICIkY2Fub24iICIkZGVzdCIgMj4vZGV2L251bGw7IHRoZW4KICAgICAgICBpbmZvICJsaW5rZWQgJGRlc3QgLT4gJGNhbm9uIgogICAgICBlbHNlCiAgICAgICAgaW5mbyAic3ltbGluayB1bnN1cHBvcnRlZCBhdCAkZGVzdDsgY29weWluZyIKICAgICAgICBfcHV0X3JlYWwgIiRkZXN0IiAiJHBzcmMiICIkbWFya2VyIgogICAgICBmaQogICAgZWxpZiBbICIkTElOS19NT0RFIiA9ICJjb3B5IiBdOyB0aGVuCiAgICAgIF9wdXRfcmVhbCAiJGRlc3QiICIkcHNyYyIgIiRtYXJrZXIiCiAgICBmaQogICAgIyBzeW1saW5rICsgYWdlbnRzOiBhbHJlYWR5IHBsYWNlZCBhcyB0aGUgY2Fub25pY2FsIHN0b3JlIGFib3ZlLgogIGRvbmUKfQoKaW5zdGFsbGVkX25hbWVzKCkgeyAjIHVuaXF1ZSBza2lsbCBuYW1lcyB0aGF0IGNhcnJ5IG91ciBtYXJrZXIgdW5kZXIgUk9PVAogIHsgZmluZCAiJFJPT1QvLmFnZW50cy9za2lsbHMiIC1tYXhkZXB0aCAyIC1uYW1lIC5leHVsdS1za2lsbC5qc29uIDI+L2Rldi9udWxsCiAgICBmb3IgaWQgaW4gJENMSUVOVFM7IGRvCiAgICAgIGQ9IiQoZGlyX2ZvciAiJGlkIikiIHx8IGNvbnRpbnVlCiAgICAgIGZpbmQgIiRST09ULyRkIiAtbWF4ZGVwdGggMiAtbmFtZSAuZXh1bHUtc2tpbGwuanNvbiAyPi9kZXYvbnVsbAogICAgZG9uZQogIH0gfCB3aGlsZSByZWFkIC1yIG07IGRvIGpzb25fc3RyIG5hbWUgIiRtIjsgZG9uZSB8IHNvcnQgLXUKfQoKbWFya2VyX3ZlcnNpb24oKSB7ICMgbWFya2VyX3ZlcnNpb24gPG5hbWU+CiAgZm9yIGJhc2UgaW4gIiRST09ULy5hZ2VudHMvc2tpbGxzIjsgZG8KICAgIFsgLWYgIiRiYXNlLyQxLy5leHVsdS1za2lsbC5qc29uIiBdICYmIHsganNvbl9zdHIgdmVyc2lvbiAiJGJhc2UvJDEvLmV4dWx1LXNraWxsLmpzb24iOyByZXR1cm47IH0KICBkb25lCiAgZm9yIGlkIGluICRDTElFTlRTOyBkbwogICAgZD0iJChkaXJfZm9yICIkaWQiKSIgfHwgY29udGludWUKICAgIGY9IiRST09ULyRkLyQxLy5leHVsdS1za2lsbC5qc29uIgogICAgWyAtZiAiJGYiIF0gJiYgeyBqc29uX3N0ciB2ZXJzaW9uICIkZiI7IHJldHVybjsgfQogIGRvbmUKfQoKZG9faW5zdGFsbCgpIHsgIyBkb19pbnN0YWxsIDxuYW1lPgogIG5hbWU9IiQxIgogIFRNUD0iJChta3RlbXAgLWQpIgogIGFwaSBHRVQgIi9za2lsbHMvcmVnaXN0cnkvJG5hbWUvZG93bmxvYWQiIC0tb3V0cHV0ICIkVE1QL3NraWxsLnppcCIgXAogICAgfHwgeyBybSAtcmYgIiRUTVAiOyBkaWUgImRvd25sb2FkIGZhaWxlZCBmb3IgJyRuYW1lJyAoNDAzID0gbm8gYWNjZXNzLCA0MDQgPSB1bmtub3duKSI7IH0KICBta2RpciAtcCAiJFRNUC94IgogIHVuemlwIC1xICIkVE1QL3NraWxsLnppcCIgLWQgIiRUTVAveCIgfHwgeyBybSAtcmYgIiRUTVAiOyBkaWUgImJhZCBhcmNoaXZlIGZvciAnJG5hbWUnIjsgfQogIHNyYz0iJChmaW5kICIkVE1QL3giIC1taW5kZXB0aCAxIC1tYXhkZXB0aCAxIC10eXBlIGQgfCBoZWFkIC1uMSkiCiAgWyAtbiAiJHNyYyIgXSB8fCB7IHJtIC1yZiAiJFRNUCI7IGRpZSAidW5leHBlY3RlZCBhcmNoaXZlIGxheW91dCBmb3IgJyRuYW1lJyI7IH0KICB2ZXI9IiQobWV0YV92ZXJzaW9uICIkbmFtZSIpIgogIHBsYWNlX3NraWxsICIkbmFtZSIgIiRzcmMiICIke3ZlcjotMX0iCiAgcm0gLXJmICIkVE1QIgogIGluZm8gImluc3RhbGxlZCAkbmFtZSAodiR7dmVyOi0xfSkgWyRMSU5LX01PREVdIGludG86ICRDTElFTlRTIgp9Cgp1c2FnZSgpIHsKICBjYXQgPiYyIDw8RU9GCmV4dWx1IOKAlCBFeHVsdSBza2lsbCBsaWJyYXJ5IGhlbHBlcgogIGV4dWx1IGxpc3QgICAgICAgICAgICAgICAgIGxpc3Qgc2tpbGxzIHlvdSBjYW4gYWNjZXNzIChKU09OKQogIGV4dWx1IGdldCA8bmFtZT4gICAgICAgICAgIHNob3cgb25lIHNraWxsJ3MgbWV0YWRhdGEgKEpTT04pCiAgZXh1bHUgaW5zdGFsbCA8bmFtZT4gICAgICAgaW5zdGFsbC9yZWZyZXNoIGEgc2tpbGwgaW50byB5b3VyIGFnZW50IGNsaWVudHMKICBleHVsdSB1cGRhdGUgWzxuYW1lPl0gICAgICB1cGRhdGUgaW5zdGFsbGVkIHNraWxscyAoYWxsLCBvciBvbmUpIHRvIGxhdGVzdAogIGV4dWx1IHB1Ymxpc2ggPG5hbWU+IDxkaXI+IHB1Ymxpc2ggYSBsb2NhbCBza2lsbCBmb2xkZXIgYXMgPG5hbWU+CiAgZXh1bHUgY29uZmlnICAgICAgICAgICAgICAgc2hvdyByZXNvbHZlZCBiYWNrZW5kIC8gc2NvcGUgLyBjbGllbnRzIChubyBzZWNyZXRzKQpFT0YKfQoKY21kPSIkezE6LWhlbHB9IgpbICQjIC1ndCAwIF0gJiYgc2hpZnQgfHwgdHJ1ZQoKY2FzZSAiJGNtZCIgaW4KICBsaXN0KSBhcGkgR0VUICIvc2tpbGxzL3JlZ2lzdHJ5IiA7OwogIGdldCkgWyAkIyAtZ2UgMSBdIHx8IGRpZSAidXNhZ2U6IGV4dWx1IGdldCA8bmFtZT4iOyBhcGkgR0VUICIvc2tpbGxzL3JlZ2lzdHJ5LyQxIiA7OwogIGluc3RhbGwpIFsgJCMgLWdlIDEgXSB8fCBkaWUgInVzYWdlOiBleHVsdSBpbnN0YWxsIDxuYW1lPiI7IGRvX2luc3RhbGwgIiQxIiA7OwogIHVwZGF0ZSkKICAgIGlmIFsgJCMgLWdlIDEgXTsgdGhlbiBuYW1lcz0iJDEiOyBlbHNlIG5hbWVzPSIkKGluc3RhbGxlZF9uYW1lcykiOyBmaQogICAgWyAtbiAiJG5hbWVzIiBdIHx8IHsgaW5mbyAibm8gZXh1bHUtbWFuYWdlZCBza2lsbHMgZm91bmQgdW5kZXIgJFJPT1QiOyBleGl0IDA7IH0KICAgIGZvciBuIGluICRuYW1lczsgZG8KICAgICAgY3VyPSIkKG1hcmtlcl92ZXJzaW9uICIkbiIpIgogICAgICBsYXRlc3Q9IiQobWV0YV92ZXJzaW9uICIkbiIpIgogICAgICBbIC1uICIkbGF0ZXN0IiBdIHx8IHsgaW5mbyAic2tpcCAkbiAobm90IGluIHJlZ2lzdHJ5KSI7IGNvbnRpbnVlOyB9CiAgICAgIGlmIFsgLXogIiRjdXIiIF0gfHwgWyAiJGxhdGVzdCIgLWd0ICIkY3VyIiBdIDI+L2Rldi9udWxsOyB0aGVuCiAgICAgICAgaW5mbyAidXBkYXRpbmcgJG46IHYke2N1cjotP30gLT4gdiRsYXRlc3QiOyBkb19pbnN0YWxsICIkbiIKICAgICAgZWxzZQogICAgICAgIGluZm8gIiRuIHVwIHRvIGRhdGUgKHYkY3VyKSIKICAgICAgZmkKICAgIGRvbmUKICAgIDs7CiAgcHVibGlzaCkKICAgIFsgJCMgLWdlIDIgXSB8fCBkaWUgInVzYWdlOiBleHVsdSBwdWJsaXNoIDxuYW1lPiA8ZGlyPiIKICAgIG5hbWU9IiQxIjsgZm9sZGVyPSIkMiIKICAgIFsgLWQgIiRmb2xkZXIiIF0gfHwgZGllICJubyBzdWNoIGZvbGRlcjogJGZvbGRlciIKICAgIFsgLWYgIiRmb2xkZXIvU0tJTEwubWQiIF0gfHwgZGllICIkZm9sZGVyIGhhcyBubyBTS0lMTC5tZCBhdCBpdHMgcm9vdCIKICAgIGNvbW1hbmQgLXYgemlwID4vZGV2L251bGwgMj4mMSB8fCBkaWUgInRoZSAnemlwJyBjb21tYW5kIGlzIHJlcXVpcmVkIHRvIHB1Ymxpc2giCiAgICBUTVA9IiQobWt0ZW1wIC1kKSIKICAgICggY2QgIiQoZGlybmFtZSAiJGZvbGRlciIpIiBcCiAgICAgICYmIHppcCAtcSAtciAtWCAiJFRNUC9za2lsbC56aXAiICIkKGJhc2VuYW1lICIkZm9sZGVyIikiIFwKICAgICAgICAgICAteCAnKi8uZXh1bHUtc2tpbGwuanNvbicgJyovLmdpdC8qJyAnKi5EU19TdG9yZScgJyovX19NQUNPU1gvKicgKSBcCiAgICAgIHx8IHsgcm0gLXJmICIkVE1QIjsgZGllICJjb3VsZCBub3QgemlwICRmb2xkZXIiOyB9CiAgICBhcGkgUE9TVCAiL3NraWxscy9yZWdpc3RyeS8kbmFtZSIgLUggIkNvbnRlbnQtVHlwZTogYXBwbGljYXRpb24vemlwIiBcCiAgICAgIC0tZGF0YS1iaW5hcnkgQCIkVE1QL3NraWxsLnppcCIgXAogICAgICB8fCB7IHJtIC1yZiAiJFRNUCI7IGRpZSAicHVibGlzaCBmYWlsZWQgKDQwMyA9IG5vIHdyaXRlIGFjY2VzcywgNDA5ID0gbmFtZSB0YWtlbikiOyB9CiAgICBybSAtcmYgIiRUTVAiCiAgICBpbmZvICJwdWJsaXNoZWQgJG5hbWUiCiAgICA7OwogIGNvbmZpZykKICAgIHByaW50ZiAnYmFja2VuZD0lc1xuc2NvcGU9JXNcbmxpbmtfbW9kZT0lc1xuY2xpZW50cz0lc1xuYXBpX2tleT0lc1xuJyBcCiAgICAgICIkQkFDS0VORCIgIiRTQ09QRSIgIiRMSU5LX01PREUiICIkQ0xJRU5UUyIgXAogICAgICAiJChbIC1uICIkQVBJX0tFWSIgXSAmJiBlY2hvIHNldCB8fCBlY2hvIE1JU1NJTkcpIgogICAgOzsKICBoZWxwfC0taGVscHwtaCkgdXNhZ2UgOzsKICAqKSBpbmZvICJ1bmtub3duIGNvbW1hbmQ6ICRjbWQiOyB1c2FnZTsgZXhpdCAyIDs7CmVzYWMK";
|
|
21729
|
+
var IMP_SH_B64 = "IyEvYmluL3NoCiMgaW1wIOKAlCBoZWxwZXIgZm9yIHRoZSBjZW50cmFsIHNraWxsIGxpYnJhcnkuIFRoZSBhZ2VudCBpbnZva2VzIHRoaXMKIyAobmV2ZXIgcmF3IGN1cmwpOiB0aGUgdG9rZW4gaXMgcmVhZCBmcm9tIHRoZSBjb25maWcgZmlsZSBoZXJlIGFuZCBzZW50IHZpYQojIGB4LWFwaS1rZXk6IEJlYXJlcmAsIHNvIGl0IG5ldmVyIGVudGVycyB0aGUgbW9kZWwgY29udGV4dC4gQ2xpZW50IGZhbi1vdXQKIyAoY29weS9zeW1saW5rIGFjcm9zcyBhZ2VudCBjbGllbnRzKSBpcyBkZXRlcm1pbmlzdGljLgpzZXQgLWV1CgpDT05GSUdfRElSPSIkSE9NRS8uY29uZmlnL2ltcCIKQ09ORklHX0ZJTEU9IiRDT05GSUdfRElSL3NraWxscy5qc29uIgoKZGllKCkgeyBwcmludGYgJ2ltcDogJXNcbicgIiQqIiA+JjI7IGV4aXQgMTsgfQppbmZvKCkgeyBwcmludGYgJyVzXG4nICIkKiIgPiYyOyB9Cgpqc29uX3N0cigpIHsgIyBqc29uX3N0ciA8a2V5PiA8ZmlsZT4g4oCUIGZsYXQgImtleSI6InZhbHVlIgogIHNlZCAtbiAicy8uKlwiJDFcIltbOnNwYWNlOl1dKjpbWzpzcGFjZTpdXSpcIlxcKFteXCJdKlxcKVwiLiovXFwxL3AiICIkMiIgfCBoZWFkIC1uMQp9CgpbIC1mICIkQ09ORklHX0ZJTEUiIF0gfHwgZGllICJub3QgY29uZmlndXJlZCDigJQgcnVuOiBjdXJsIC1mc1NMIDxiYXNlX3VybD4vYXBpL3NraWxscy9pbnN0YWxsLnNoIHwgc2giCgpCQVNFX1VSTD0iJChqc29uX3N0ciBiYXNlX3VybCAiJENPTkZJR19GSUxFIikiCkJBQ0tFTkQ9IiQoanNvbl9zdHIgYmFja2VuZCAiJENPTkZJR19GSUxFIikiCkFQSV9LRVk9IiQoanNvbl9zdHIgYXBpX2tleSAiJENPTkZJR19GSUxFIikiCkxJTktfTU9ERT0iJChqc29uX3N0ciBsaW5rX21vZGUgIiRDT05GSUdfRklMRSIpIgpTQ09QRT0iJChqc29uX3N0ciBzY29wZSAiJENPTkZJR19GSUxFIikiCkNMSUVOVFM9IiQoc2VkIC1uICdzLy4qImNsaWVudHMiW1s6c3BhY2U6XV0qOltbOnNwYWNlOl1dKlxbXChbXl1dKlwpXF0uKi9cMS9wJyAiJENPTkZJR19GSUxFIiB8IHRyICcsJyAnICcgfCB0ciAtZCAnIicgfCB0ciAtcyAnICcpIgoKWyAtbiAiJENMSUVOVFMiIF0gfHwgQ0xJRU5UUz0iYWdlbnRzIgpbIC1uICIkTElOS19NT0RFIiBdIHx8IExJTktfTU9ERT0iY29weSIKWyAtbiAiJFNDT1BFIiBdIHx8IFNDT1BFPSJwcm9qZWN0IgoKaWYgWyAteiAiJEJBQ0tFTkQiIF07IHRoZW4KICBbIC1uICIkQkFTRV9VUkwiIF0gfHwgZGllICJjb25maWcgaGFzIG5vIGJhY2tlbmQgYW5kIG5vIGJhc2VfdXJsIgogIEJBQ0tFTkQ9IiQoY3VybCAtZnNTTCAiJEJBU0VfVVJML2FwaS9jb25maWciIHwgc2VkIC1uICdzLy4qImJhY2tlbmQiW1s6c3BhY2U6XV0qOltbOnNwYWNlOl1dKiJcKFteIl0qXCkiLiovXDEvcCcgfCBoZWFkIC1uMSkiCiAgWyAtbiAiJEJBQ0tFTkQiIF0gfHwgZGllICJjb3VsZCBub3QgcmVzb2x2ZSBiYWNrZW5kIGZyb20gJEJBU0VfVVJML2FwaS9jb25maWciCmZpCkJBQ0tFTkQ9IiQocHJpbnRmICclcycgIiRCQUNLRU5EIiB8IHNlZCAnczovKiQ6OicpIgoKUk9PVD0iJFBXRCIKWyAiJFNDT1BFIiA9ICJob21lIiBdICYmIFJPT1Q9IiRIT01FIgoKIyBkaXJfZm9yIENMSUVOVF9JRCAtPiByZWxhdGl2ZSBza2lsbCBkaXIgKGdlbmVyYXRlZCBmcm9tIHRoZSBtYW5pZmVzdCBpbiB0aGUKIyByZWFsIGJ1aWxkOyBhIHJlcHJlc2VudGF0aXZlIHN1YnNldCBoZXJlIGZvciBsb2NhbCB0ZXN0aW5nKS4KZGlyX2ZvcigpIHsKICBjYXNlICIkMSIgaW4KX19ESVJfRk9SX0NBU0VTX18KICAgICopIHJldHVybiAxIDs7CiAgZXNhYwp9CgojIE9uZSAiLi4vIiBwZXIgcGF0aCBjb21wb25lbnQgb2YgJDEgKGEgY2xpZW50IGRpciByZWxhdGl2ZSB0byBST09UKSwgc28KIyBzeW1saW5rcyBhcmUgcmVsYXRpdmUgYW5kIHN1cnZpdmUgYmVpbmcgY29tbWl0dGVkIGFuZCBjaGVja2VkIG91dCBlbHNld2hlcmUuCnJlbF90b19yb290KCkgewogIF91cD0iIjsgX29sZGlmcz0kSUZTOyBJRlM9LwogIGZvciBfc2VnIGluICQxOyBkbyBbIC1uICIkX3NlZyIgXSAmJiBfdXA9Ii4uLyRfdXAiOyBkb25lCiAgSUZTPSRfb2xkaWZzOyBwcmludGYgJyVzJyAiJF91cCIKfQoKYXBpKCkgeyAjIGFwaSA8TUVUSE9EPiA8cGF0aD4gW2V4dHJhIGN1cmwgYXJncy4uLl0gLT4gYm9keSBvbiBzdGRvdXQKICBtPSIkMSI7IHA9IiQyIjsgc2hpZnQgMgogIGN1cmwgLWZzUyAtWCAiJG0iICIkQkFDS0VORCRwIiAtSCAieC1hcGkta2V5OiBCZWFyZXIgJEFQSV9LRVkiICIkQCIKfQoKbWV0YV92ZXJzaW9uKCkgeyAjIG1ldGFfdmVyc2lvbiA8bmFtZT4gLT4gY3VycmVudF92ZXJzaW9uIGZyb20gcmVnaXN0cnkKICBhcGkgR0VUICIvc2tpbGxzL3JlZ2lzdHJ5LyQxIiAyPi9kZXYvbnVsbCBcCiAgICB8IHNlZCAtbiAncy8uKiJjdXJyZW50X3ZlcnNpb24iW1s6c3BhY2U6XV0qOltbOnNwYWNlOl1dKlwoWzAtOV1bMC05XSpcKS4qL1wxL3AnIHwgaGVhZCAtbjEKfQoKX21hbmFnZWQoKSB7ICMgX21hbmFnZWQgPGRpcj4gLT4gMCBpZiBzYWZlIHRvIG92ZXJ3cml0ZSAob3VycyBvciBzeW1saW5rIG9yIGFic2VudCkKICBkPSIkMSIKICBpZiBbIC1lICIkZCIgXSAmJiBbICEgLUwgIiRkIiBdICYmIFsgISAtZiAiJGQvLmltcC1za2lsbC5qc29uIiBdOyB0aGVuCiAgICBpbmZvICJza2lwICRkIChleGlzdHMsIG5vdCBtYW5hZ2VkIGJ5IGltcCkiOyByZXR1cm4gMQogIGZpCiAgcmV0dXJuIDAKfQoKX3B1dF9yZWFsKCkgeyAjIF9wdXRfcmVhbCA8ZGVzdD4gPHNyY2Rpcj4gPG1hcmtlci1qc29uPgogIF9tYW5hZ2VkICIkMSIgfHwgcmV0dXJuIDAKICBybSAtcmYgIiQxIjsgbWtkaXIgLXAgIiQxIjsgY3AgLVIgIiQyLy4iICIkMS8iCiAgcHJpbnRmICclc1xuJyAiJDMiID4gIiQxLy5pbXAtc2tpbGwuanNvbiIKfQoKcGxhY2Vfc2tpbGwoKSB7ICMgcGxhY2Vfc2tpbGwgPG5hbWU+IDxzcmNkaXI+IDx2ZXJzaW9uPgogIHBuYW1lPSIkMSI7IHBzcmM9IiQyIjsgcHZlcj0iJHszOi0xfSIKICBtYXJrZXI9J3sgIm5hbWUiOiAiJyIkcG5hbWUiJyIsICJ2ZXJzaW9uIjogJyIkcHZlciInLCAic291cmNlIjogIiciJEJBQ0tFTkQiJyIgfScKICBjYW5vbj0iJFJPT1QvLmFnZW50cy9za2lsbHMvJHBuYW1lIgogIFsgIiRMSU5LX01PREUiID0gInN5bWxpbmsiIF0gJiYgX3B1dF9yZWFsICIkY2Fub24iICIkcHNyYyIgIiRtYXJrZXIiCiAgZm9yIGlkIGluICRDTElFTlRTOyBkbwogICAgZD0iJChkaXJfZm9yICIkaWQiKSIgfHwgeyBpbmZvICJ1bmtub3duIGNsaWVudDogJGlkIjsgY29udGludWU7IH0KICAgIHBhcmVudD0iJFJPT1QvJGQiOyBkZXN0PSIkcGFyZW50LyRwbmFtZSIKICAgIGlmIFsgIiRMSU5LX01PREUiID0gInN5bWxpbmsiIF0gJiYgWyAiJGlkIiAhPSAiYWdlbnRzIiBdOyB0aGVuCiAgICAgIF9tYW5hZ2VkICIkZGVzdCIgfHwgY29udGludWUKICAgICAgbWtkaXIgLXAgIiRwYXJlbnQiOyBybSAtcmYgIiRkZXN0IgogICAgICByZWw9IiQocmVsX3RvX3Jvb3QgIiRkIikuYWdlbnRzL3NraWxscy8kcG5hbWUiCiAgICAgIGlmIGxuIC1zICIkcmVsIiAiJGRlc3QiIDI+L2Rldi9udWxsOyB0aGVuCiAgICAgICAgaW5mbyAibGlua2VkICRkZXN0IC0+ICRyZWwiCiAgICAgIGVsc2UKICAgICAgICBpbmZvICJzeW1saW5rIHVuc3VwcG9ydGVkIGF0ICRkZXN0OyBjb3B5aW5nIgogICAgICAgIF9wdXRfcmVhbCAiJGRlc3QiICIkcHNyYyIgIiRtYXJrZXIiCiAgICAgIGZpCiAgICBlbGlmIFsgIiRMSU5LX01PREUiID0gImNvcHkiIF07IHRoZW4KICAgICAgX3B1dF9yZWFsICIkZGVzdCIgIiRwc3JjIiAiJG1hcmtlciIKICAgIGZpCiAgICAjIHN5bWxpbmsgKyBhZ2VudHM6IGFscmVhZHkgcGxhY2VkIGFzIHRoZSBjYW5vbmljYWwgc3RvcmUgYWJvdmUuCiAgZG9uZQp9CgppbnN0YWxsZWRfbmFtZXMoKSB7ICMgdW5pcXVlIHNraWxsIG5hbWVzIHRoYXQgY2Fycnkgb3VyIG1hcmtlciB1bmRlciBST09UCiAgeyBmaW5kICIkUk9PVC8uYWdlbnRzL3NraWxscyIgLW1heGRlcHRoIDIgLW5hbWUgLmltcC1za2lsbC5qc29uIDI+L2Rldi9udWxsCiAgICBmb3IgaWQgaW4gJENMSUVOVFM7IGRvCiAgICAgIGQ9IiQoZGlyX2ZvciAiJGlkIikiIHx8IGNvbnRpbnVlCiAgICAgIGZpbmQgIiRST09ULyRkIiAtbWF4ZGVwdGggMiAtbmFtZSAuaW1wLXNraWxsLmpzb24gMj4vZGV2L251bGwKICAgIGRvbmUKICB9IHwgd2hpbGUgcmVhZCAtciBtOyBkbyBqc29uX3N0ciBuYW1lICIkbSI7IGRvbmUgfCBzb3J0IC11Cn0KCm1hcmtlcl92ZXJzaW9uKCkgeyAjIG1hcmtlcl92ZXJzaW9uIDxuYW1lPgogIGZvciBiYXNlIGluICIkUk9PVC8uYWdlbnRzL3NraWxscyI7IGRvCiAgICBbIC1mICIkYmFzZS8kMS8uaW1wLXNraWxsLmpzb24iIF0gJiYgeyBqc29uX3N0ciB2ZXJzaW9uICIkYmFzZS8kMS8uaW1wLXNraWxsLmpzb24iOyByZXR1cm47IH0KICBkb25lCiAgZm9yIGlkIGluICRDTElFTlRTOyBkbwogICAgZD0iJChkaXJfZm9yICIkaWQiKSIgfHwgY29udGludWUKICAgIGY9IiRST09ULyRkLyQxLy5pbXAtc2tpbGwuanNvbiIKICAgIFsgLWYgIiRmIiBdICYmIHsganNvbl9zdHIgdmVyc2lvbiAiJGYiOyByZXR1cm47IH0KICBkb25lCn0KCmRvX2luc3RhbGwoKSB7ICMgZG9faW5zdGFsbCA8bmFtZT4KICBuYW1lPSIkMSIKICBUTVA9IiQobWt0ZW1wIC1kKSIKICBhcGkgR0VUICIvc2tpbGxzL3JlZ2lzdHJ5LyRuYW1lL2Rvd25sb2FkIiAtLW91dHB1dCAiJFRNUC9za2lsbC56aXAiIFwKICAgIHx8IHsgcm0gLXJmICIkVE1QIjsgZGllICJkb3dubG9hZCBmYWlsZWQgZm9yICckbmFtZScgKDQwMyA9IG5vIGFjY2VzcywgNDA0ID0gdW5rbm93bikiOyB9CiAgbWtkaXIgLXAgIiRUTVAveCIKICB1bnppcCAtcSAiJFRNUC9za2lsbC56aXAiIC1kICIkVE1QL3giIHx8IHsgcm0gLXJmICIkVE1QIjsgZGllICJiYWQgYXJjaGl2ZSBmb3IgJyRuYW1lJyI7IH0KICBzcmM9IiQoZmluZCAiJFRNUC94IiAtbWluZGVwdGggMSAtbWF4ZGVwdGggMSAtdHlwZSBkIHwgaGVhZCAtbjEpIgogIFsgLW4gIiRzcmMiIF0gfHwgeyBybSAtcmYgIiRUTVAiOyBkaWUgInVuZXhwZWN0ZWQgYXJjaGl2ZSBsYXlvdXQgZm9yICckbmFtZSciOyB9CiAgdmVyPSIkKG1ldGFfdmVyc2lvbiAiJG5hbWUiKSIKICBwbGFjZV9za2lsbCAiJG5hbWUiICIkc3JjIiAiJHt2ZXI6LTF9IgogIHJtIC1yZiAiJFRNUCIKICBpbmZvICJpbnN0YWxsZWQgJG5hbWUgKHYke3ZlcjotMX0pIFskTElOS19NT0RFXSBpbnRvOiAkQ0xJRU5UUyIKfQoKdXNhZ2UoKSB7CiAgY2F0ID4mMiA8PEVPRgppbXAg4oCUIHNraWxsIGxpYnJhcnkgaGVscGVyCiAgaW1wIGxpc3QgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsaXN0IHNraWxscyB5b3UgY2FuIGFjY2VzcyAoSlNPTikKICBpbXAgZ2V0IDxuYW1lPiAgICAgICAgICAgICAgICAgICAgICAgICAgIHNob3cgb25lIHNraWxsJ3MgbWV0YWRhdGEgKEpTT04pCiAgaW1wIGluc3RhbGwgPG5hbWU+ICAgICAgICAgICAgICAgICAgICAgICBpbnN0YWxsL3JlZnJlc2ggYSBza2lsbCBpbnRvIHlvdXIgYWdlbnQgY2xpZW50cwogIGltcCB1cGRhdGUgWzxuYW1lPl0gICAgICAgICAgICAgICAgICAgICAgdXBkYXRlIGluc3RhbGxlZCBza2lsbHMgKGFsbCwgb3Igb25lKSB0byBsYXRlc3QKICBpbXAgcHVibGlzaCA8bmFtZT4gPGRpcj4gPHB1YmxpY3xwcml2YXRlPiBwdWJsaXNoIGEgbG9jYWwgc2tpbGwgZm9sZGVyIGFzIDxuYW1lPgogIGltcCBjb25maWcgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2hvdyByZXNvbHZlZCBiYWNrZW5kIC8gc2NvcGUgLyBjbGllbnRzIChubyBzZWNyZXRzKQpFT0YKfQoKY21kPSIkezE6LWhlbHB9IgpbICQjIC1ndCAwIF0gJiYgc2hpZnQgfHwgdHJ1ZQoKY2FzZSAiJGNtZCIgaW4KICBsaXN0KSBhcGkgR0VUICIvc2tpbGxzL3JlZ2lzdHJ5IiA7OwogIGdldCkgWyAkIyAtZ2UgMSBdIHx8IGRpZSAidXNhZ2U6IGltcCBnZXQgPG5hbWU+IjsgYXBpIEdFVCAiL3NraWxscy9yZWdpc3RyeS8kMSIgOzsKICBpbnN0YWxsKSBbICQjIC1nZSAxIF0gfHwgZGllICJ1c2FnZTogaW1wIGluc3RhbGwgPG5hbWU+IjsgZG9faW5zdGFsbCAiJDEiIDs7CiAgdXBkYXRlKQogICAgaWYgWyAkIyAtZ2UgMSBdOyB0aGVuIG5hbWVzPSIkMSI7IGVsc2UgbmFtZXM9IiQoaW5zdGFsbGVkX25hbWVzKSI7IGZpCiAgICBbIC1uICIkbmFtZXMiIF0gfHwgeyBpbmZvICJubyBpbXAtbWFuYWdlZCBza2lsbHMgZm91bmQgdW5kZXIgJFJPT1QiOyBleGl0IDA7IH0KICAgIGZvciBuIGluICRuYW1lczsgZG8KICAgICAgY3VyPSIkKG1hcmtlcl92ZXJzaW9uICIkbiIpIgogICAgICBsYXRlc3Q9IiQobWV0YV92ZXJzaW9uICIkbiIpIgogICAgICBbIC1uICIkbGF0ZXN0IiBdIHx8IHsgaW5mbyAic2tpcCAkbiAobm90IGluIHJlZ2lzdHJ5KSI7IGNvbnRpbnVlOyB9CiAgICAgIGlmIFsgLXogIiRjdXIiIF0gfHwgWyAiJGxhdGVzdCIgLWd0ICIkY3VyIiBdIDI+L2Rldi9udWxsOyB0aGVuCiAgICAgICAgaW5mbyAidXBkYXRpbmcgJG46IHYke2N1cjotP30gLT4gdiRsYXRlc3QiOyBkb19pbnN0YWxsICIkbiIKICAgICAgZWxzZQogICAgICAgIGluZm8gIiRuIHVwIHRvIGRhdGUgKHYkY3VyKSIKICAgICAgZmkKICAgIGRvbmUKICAgIDs7CiAgcHVibGlzaCkKICAgIFsgJCMgLWdlIDMgXSB8fCBkaWUgInVzYWdlOiBpbXAgcHVibGlzaCA8bmFtZT4gPGRpcj4gPHB1YmxpY3xwcml2YXRlPiIKICAgIG5hbWU9IiQxIjsgZm9sZGVyPSIkMiI7IHZpc2liaWxpdHk9IiQzIgogICAgY2FzZSAiJHZpc2liaWxpdHkiIGluCiAgICAgIHB1YmxpY3xwcml2YXRlKSA7OwogICAgICAqKSBkaWUgInZpc2liaWxpdHkgbXVzdCBiZSAncHVibGljJyBvciAncHJpdmF0ZScgKGFzayB0aGUgdXNlciB3aGljaCB0aGV5IHdhbnQpIiA7OwogICAgZXNhYwogICAgWyAtZCAiJGZvbGRlciIgXSB8fCBkaWUgIm5vIHN1Y2ggZm9sZGVyOiAkZm9sZGVyIgogICAgWyAtZiAiJGZvbGRlci9TS0lMTC5tZCIgXSB8fCBkaWUgIiRmb2xkZXIgaGFzIG5vIFNLSUxMLm1kIGF0IGl0cyByb290IgogICAgY29tbWFuZCAtdiB6aXAgPi9kZXYvbnVsbCAyPiYxIHx8IGRpZSAidGhlICd6aXAnIGNvbW1hbmQgaXMgcmVxdWlyZWQgdG8gcHVibGlzaCIKICAgIFRNUD0iJChta3RlbXAgLWQpIgogICAgKCBjZCAiJChkaXJuYW1lICIkZm9sZGVyIikiIFwKICAgICAgJiYgemlwIC1xIC1yIC1YICIkVE1QL3NraWxsLnppcCIgIiQoYmFzZW5hbWUgIiRmb2xkZXIiKSIgXAogICAgICAgICAgIC14ICcqLy5pbXAtc2tpbGwuanNvbicgJyovLmdpdC8qJyAnKi5EU19TdG9yZScgJyovX19NQUNPU1gvKicgKSBcCiAgICAgIHx8IHsgcm0gLXJmICIkVE1QIjsgZGllICJjb3VsZCBub3QgemlwICRmb2xkZXIiOyB9CiAgICBhcGkgUE9TVCAiL3NraWxscy9yZWdpc3RyeS8kbmFtZT92aXNpYmlsaXR5PSR2aXNpYmlsaXR5IiAtSCAiQ29udGVudC1UeXBlOiBhcHBsaWNhdGlvbi96aXAiIFwKICAgICAgLS1kYXRhLWJpbmFyeSBAIiRUTVAvc2tpbGwuemlwIiBcCiAgICAgIHx8IHsgcm0gLXJmICIkVE1QIjsgZGllICJwdWJsaXNoIGZhaWxlZCAoNDAzID0gbm8gd3JpdGUgYWNjZXNzLCA0MDkgPSBuYW1lIHRha2VuKSI7IH0KICAgIHJtIC1yZiAiJFRNUCIKICAgIGluZm8gInB1Ymxpc2hlZCAkbmFtZSAoJHZpc2liaWxpdHkpIgogICAgOzsKICBjb25maWcpCiAgICBwcmludGYgJ2JhY2tlbmQ9JXNcbnNjb3BlPSVzXG5saW5rX21vZGU9JXNcbmNsaWVudHM9JXNcbmFwaV9rZXk9JXNcbicgXAogICAgICAiJEJBQ0tFTkQiICIkU0NPUEUiICIkTElOS19NT0RFIiAiJENMSUVOVFMiIFwKICAgICAgIiQoWyAtbiAiJEFQSV9LRVkiIF0gJiYgZWNobyBzZXQgfHwgZWNobyBNSVNTSU5HKSIKICAgIDs7CiAgaGVscHwtLWhlbHB8LWgpIHVzYWdlIDs7CiAgKikgaW5mbyAidW5rbm93biBjb21tYW5kOiAkY21kIjsgdXNhZ2U7IGV4aXQgMiA7Owplc2FjCg==";
|
|
21276
21730
|
|
|
21277
|
-
// src/skills/bootstrap/
|
|
21731
|
+
// src/skills/bootstrap/imp-skills.ts
|
|
21278
21732
|
var BOOTSTRAP_CLIENTS_JSON = JSON.stringify(CLIENT_MANIFEST, null, 2);
|
|
21279
21733
|
var DIR_FOR_CASES = CLIENT_MANIFEST.map(
|
|
21280
21734
|
(c) => ` ${c.id}) printf '%s' '${c.dir}' ;;`
|
|
21281
21735
|
).join("\n");
|
|
21282
|
-
var
|
|
21736
|
+
var BOOTSTRAP_IMP_SH = Buffer.from(IMP_SH_B64, "base64").toString("utf8").replace("__DIR_FOR_CASES__", DIR_FOR_CASES);
|
|
21283
21737
|
var BOOTSTRAP_SKILL_MD = `---
|
|
21284
|
-
name:
|
|
21285
|
-
description: Install, update, and publish skills from this
|
|
21738
|
+
name: imp-skills
|
|
21739
|
+
description: Install, update, and publish skills from this instance's central skill library. Use when the user asks to install a skill, get the latest version of a skill, list IMP skills, or publish a skill to IMP.
|
|
21286
21740
|
---
|
|
21287
21741
|
|
|
21288
|
-
#
|
|
21742
|
+
# IMP Skills
|
|
21289
21743
|
|
|
21290
|
-
Bridge to the
|
|
21744
|
+
Bridge to the IMP central skill library. **All operations go through the
|
|
21291
21745
|
bundled helper script \u2014 do not hand-write curl or copy files yourself.** The
|
|
21292
21746
|
script reads the API token from config (keeping it out of this conversation) and
|
|
21293
21747
|
handles the multi-client copy/symlink fan-out deterministically.
|
|
21294
21748
|
|
|
21295
21749
|
## The helper
|
|
21296
21750
|
|
|
21297
|
-
Run the script next to this file, \`scripts/
|
|
21751
|
+
Run the script next to this file, \`scripts/imp\`, with \`sh\` and the absolute
|
|
21298
21752
|
path of this skill's directory:
|
|
21299
21753
|
|
|
21300
21754
|
\`\`\`
|
|
21301
|
-
sh "<this-skill-dir>/scripts/
|
|
21755
|
+
sh "<this-skill-dir>/scripts/imp" <command>
|
|
21302
21756
|
\`\`\`
|
|
21303
21757
|
|
|
21304
21758
|
Commands:
|
|
@@ -21306,25 +21760,27 @@ Commands:
|
|
|
21306
21760
|
- \`get <name>\` \u2014 one skill's metadata (JSON)
|
|
21307
21761
|
- \`install <name>\` \u2014 install/refresh a skill into the user's agent clients
|
|
21308
21762
|
- \`update [<name>]\` \u2014 update every installed skill, or just \`<name>\`, to latest
|
|
21309
|
-
- \`publish <name> <folder>\` \u2014 publish a local skill folder as \`<name>\`
|
|
21763
|
+
- \`publish <name> <folder> <public|private>\` \u2014 publish a local skill folder as \`<name>\`
|
|
21310
21764
|
- \`config\` \u2014 show resolved backend / scope / clients (prints no secrets)
|
|
21311
21765
|
|
|
21312
21766
|
The token, backend URL, target clients, copy-vs-symlink mode, and scope all come
|
|
21313
|
-
from \`~/.config/
|
|
21767
|
+
from \`~/.config/imp/skills.json\` (written by the installer). Never print the
|
|
21314
21768
|
\`api_key\` or read it into your reply \u2014 the script uses it internally.
|
|
21315
21769
|
|
|
21316
21770
|
## Requests \u2192 commands
|
|
21317
21771
|
|
|
21318
|
-
- "list / search skills" \u2192 \`
|
|
21319
|
-
- "install skill X" / "add the X skill" \u2192 \`
|
|
21320
|
-
- "update / get the latest version [of X]" \u2192 \`
|
|
21772
|
+
- "list / search skills" \u2192 \`imp list\`, then filter the JSON for the user.
|
|
21773
|
+
- "install skill X" / "add the X skill" \u2192 \`imp install X\`.
|
|
21774
|
+
- "update / get the latest version [of X]" \u2192 \`imp update [X]\`.
|
|
21321
21775
|
- "publish / upload this skill as X" \u2192 confirm the target name with the user; for
|
|
21322
|
-
an existing skill run \`
|
|
21323
|
-
|
|
21776
|
+
an existing skill run \`imp get X\` first and confirm a new version is intended.
|
|
21777
|
+
For a NEW skill you MUST ask the user whether it should be \`public\` (visible
|
|
21778
|
+
to everyone on the instance) or \`private\` (only them) \u2014 never pick a
|
|
21779
|
+
visibility yourself; then \`imp publish X <folder> <public|private>\`.
|
|
21324
21780
|
|
|
21325
21781
|
## Not configured yet?
|
|
21326
21782
|
|
|
21327
|
-
If \`
|
|
21783
|
+
If \`imp config\` reports it's not configured (or \`~/.config/imp/skills.json\`
|
|
21328
21784
|
is missing), tell the user to run the installer \u2014 it sets everything up
|
|
21329
21785
|
interactively (base URL, API key, target clients, copy/symlink):
|
|
21330
21786
|
|
|
@@ -21332,14 +21788,15 @@ interactively (base URL, API key, target clients, copy/symlink):
|
|
|
21332
21788
|
curl -fsSL <base_url>/api/skills/install.sh | sh
|
|
21333
21789
|
\`\`\`
|
|
21334
21790
|
|
|
21335
|
-
\`<base_url>\` is their
|
|
21336
|
-
create an API key at \`<base_url>/token\`.
|
|
21791
|
+
\`<base_url>\` is their instance's frontend URL (e.g. https://ai.open.de). They
|
|
21792
|
+
can create an API key at \`<base_url>/token\`.
|
|
21337
21793
|
|
|
21338
21794
|
## Errors
|
|
21339
21795
|
|
|
21340
21796
|
- install: \`403\` = no access to that skill; \`404\` = unknown name.
|
|
21341
|
-
- publish: \`
|
|
21342
|
-
|
|
21797
|
+
- publish: \`400\` = missing/invalid visibility (new skills need public|private);
|
|
21798
|
+
\`403\` = you can see it but lack write access; \`409\` = the name is taken by a
|
|
21799
|
+
skill you can't access.
|
|
21343
21800
|
`;
|
|
21344
21801
|
|
|
21345
21802
|
// src/exulu/routes.ts
|
|
@@ -22370,7 +22827,7 @@ ${customInstructions}` : agent.instructions;
|
|
|
22370
22827
|
const imageModelsByName = (() => {
|
|
22371
22828
|
if (!isLiteLLMEnabled() || !config?.fileUploads) return /* @__PURE__ */ new Map();
|
|
22372
22829
|
try {
|
|
22373
|
-
const configPath = process.env.LITELLM_CONFIG_PATH ?? (0,
|
|
22830
|
+
const configPath = process.env.LITELLM_CONFIG_PATH ?? (0, import_node_path9.resolve)(process.cwd(), "./config.litellm.yaml");
|
|
22374
22831
|
const models2 = parseImageGenerationModels(configPath);
|
|
22375
22832
|
return new Map(models2.map((m) => [m.model_name, m]));
|
|
22376
22833
|
} catch (err) {
|
|
@@ -23648,9 +24105,9 @@ ${style.markdown}` : params.prompt;
|
|
|
23648
24105
|
app.get("/skills/agent/bootstrap", async (_req, res) => {
|
|
23649
24106
|
try {
|
|
23650
24107
|
const zip = new import_jszip3.default();
|
|
23651
|
-
zip.file("
|
|
23652
|
-
zip.file("
|
|
23653
|
-
zip.file("
|
|
24108
|
+
zip.file("imp-skills/SKILL.md", BOOTSTRAP_SKILL_MD);
|
|
24109
|
+
zip.file("imp-skills/references/clients.json", BOOTSTRAP_CLIENTS_JSON);
|
|
24110
|
+
zip.file("imp-skills/scripts/imp", BOOTSTRAP_IMP_SH, {
|
|
23654
24111
|
unixPermissions: 493
|
|
23655
24112
|
});
|
|
23656
24113
|
const buffer = await zip.generateAsync({
|
|
@@ -23658,7 +24115,7 @@ ${style.markdown}` : params.prompt;
|
|
|
23658
24115
|
platform: "UNIX"
|
|
23659
24116
|
});
|
|
23660
24117
|
res.setHeader("Content-Type", "application/zip");
|
|
23661
|
-
res.setHeader("Content-Disposition", 'attachment; filename="
|
|
24118
|
+
res.setHeader("Content-Disposition", 'attachment; filename="imp-skills.zip"');
|
|
23662
24119
|
res.send(buffer);
|
|
23663
24120
|
} catch (err) {
|
|
23664
24121
|
console.error("[SKILLS] Failed to build bootstrap zip", err);
|
|
@@ -23779,6 +24236,13 @@ ${style.markdown}` : params.prompt;
|
|
|
23779
24236
|
return;
|
|
23780
24237
|
}
|
|
23781
24238
|
}
|
|
24239
|
+
const visibility = req.query.visibility;
|
|
24240
|
+
if (visibility !== "public" && visibility !== "private") {
|
|
24241
|
+
res.status(400).json({
|
|
24242
|
+
detail: "Missing or invalid 'visibility'. New skills require ?visibility=public or ?visibility=private \u2014 ask the user which they want."
|
|
24243
|
+
});
|
|
24244
|
+
return;
|
|
24245
|
+
}
|
|
23782
24246
|
const meta = await parseSkillFrontmatter(bytes);
|
|
23783
24247
|
const skillId = (0, import_node_crypto9.randomUUID)();
|
|
23784
24248
|
try {
|
|
@@ -23805,7 +24269,7 @@ ${style.markdown}` : params.prompt;
|
|
|
23805
24269
|
history: JSON.stringify([
|
|
23806
24270
|
{ version: 1, created_at: (/* @__PURE__ */ new Date()).toISOString(), label: "Published from agent" }
|
|
23807
24271
|
]),
|
|
23808
|
-
rights_mode:
|
|
24272
|
+
rights_mode: visibility,
|
|
23809
24273
|
created_by: authResult.user.id
|
|
23810
24274
|
});
|
|
23811
24275
|
} catch (err) {
|
|
@@ -24912,7 +25376,7 @@ init_check_record_access();
|
|
|
24912
25376
|
init_resolve_model();
|
|
24913
25377
|
init_supervisor();
|
|
24914
25378
|
init_client();
|
|
24915
|
-
var
|
|
25379
|
+
var import_zod16 = require("zod");
|
|
24916
25380
|
init_convert_exulu_tools_to_ai_sdk_tools();
|
|
24917
25381
|
init_singleton();
|
|
24918
25382
|
var SESSION_ID_HEADER = "mcp-session-id";
|
|
@@ -24986,7 +25450,7 @@ var ExuluMCP = class {
|
|
|
24986
25450
|
title: tool4.name + " agent",
|
|
24987
25451
|
description: tool4.description,
|
|
24988
25452
|
inputSchema: {
|
|
24989
|
-
inputs: tool4.inputSchema ||
|
|
25453
|
+
inputs: tool4.inputSchema || import_zod16.z.object({})
|
|
24990
25454
|
}
|
|
24991
25455
|
},
|
|
24992
25456
|
async ({ inputs }, args) => {
|
|
@@ -25038,7 +25502,7 @@ var ExuluMCP = class {
|
|
|
25038
25502
|
title: "Get List of Prompt Templates",
|
|
25039
25503
|
description: "Retrieves a list of prompt templates available for this agent. Returns the name, description, and ID of each template.",
|
|
25040
25504
|
inputSchema: {
|
|
25041
|
-
inputs:
|
|
25505
|
+
inputs: import_zod16.z.object({})
|
|
25042
25506
|
}
|
|
25043
25507
|
},
|
|
25044
25508
|
async ({ inputs }, args) => {
|
|
@@ -25084,8 +25548,8 @@ var ExuluMCP = class {
|
|
|
25084
25548
|
title: "Get Prompt Template Details",
|
|
25085
25549
|
description: "Retrieves the full details of a specific prompt template by ID, including the actual template content with variables.",
|
|
25086
25550
|
inputSchema: {
|
|
25087
|
-
inputs:
|
|
25088
|
-
id:
|
|
25551
|
+
inputs: import_zod16.z.object({
|
|
25552
|
+
id: import_zod16.z.string().describe("The ID of the prompt template to retrieve")
|
|
25089
25553
|
})
|
|
25090
25554
|
}
|
|
25091
25555
|
},
|
|
@@ -26200,7 +26664,7 @@ var ExuluEval = class {
|
|
|
26200
26664
|
// src/templates/evals/index.ts
|
|
26201
26665
|
init_resolve_model();
|
|
26202
26666
|
init_singleton();
|
|
26203
|
-
var
|
|
26667
|
+
var import_zod17 = require("zod");
|
|
26204
26668
|
var import_ai17 = require("ai");
|
|
26205
26669
|
var llmAsJudgeEval = () => {
|
|
26206
26670
|
if (process.env.REDIS_HOST?.length && process.env.REDIS_PORT?.length) {
|
|
@@ -26253,8 +26717,8 @@ var llmAsJudgeEval = () => {
|
|
|
26253
26717
|
prompt,
|
|
26254
26718
|
maxRetries: 2,
|
|
26255
26719
|
output: import_ai17.Output.object({
|
|
26256
|
-
schema:
|
|
26257
|
-
score:
|
|
26720
|
+
schema: import_zod17.z.object({
|
|
26721
|
+
score: import_zod17.z.number().min(0).max(100).describe("The score between 0 and 100.")
|
|
26258
26722
|
})
|
|
26259
26723
|
})
|
|
26260
26724
|
});
|
|
@@ -26485,15 +26949,15 @@ Usage:
|
|
|
26485
26949
|
- If no todos exist yet, an empty list will be returned`;
|
|
26486
26950
|
|
|
26487
26951
|
// src/templates/tools/todo/todo.ts
|
|
26488
|
-
var
|
|
26952
|
+
var import_zod18 = __toESM(require("zod"), 1);
|
|
26489
26953
|
init_tool();
|
|
26490
26954
|
init_check_record_access();
|
|
26491
26955
|
init_client();
|
|
26492
|
-
var TodoSchema =
|
|
26493
|
-
content:
|
|
26494
|
-
status:
|
|
26495
|
-
priority:
|
|
26496
|
-
id:
|
|
26956
|
+
var TodoSchema = import_zod18.default.object({
|
|
26957
|
+
content: import_zod18.default.string().describe("Brief description of the task"),
|
|
26958
|
+
status: import_zod18.default.string().describe("Current status of the task: pending, in_progress, completed, cancelled"),
|
|
26959
|
+
priority: import_zod18.default.string().describe("Priority level of the task: high, medium, low"),
|
|
26960
|
+
id: import_zod18.default.string().describe("Unique identifier for the todo item")
|
|
26497
26961
|
});
|
|
26498
26962
|
var TodoWriteTool = new ExuluTool({
|
|
26499
26963
|
id: "todo_write",
|
|
@@ -26509,8 +26973,8 @@ var TodoWriteTool = new ExuluTool({
|
|
|
26509
26973
|
default: todowrite_default
|
|
26510
26974
|
}
|
|
26511
26975
|
],
|
|
26512
|
-
inputSchema:
|
|
26513
|
-
todos:
|
|
26976
|
+
inputSchema: import_zod18.default.object({
|
|
26977
|
+
todos: import_zod18.default.array(TodoSchema).describe("The updated todo list")
|
|
26514
26978
|
}),
|
|
26515
26979
|
execute: async (inputs) => {
|
|
26516
26980
|
const { sessionID, todos, user } = inputs;
|
|
@@ -26545,7 +27009,7 @@ var TodoReadTool = new ExuluTool({
|
|
|
26545
27009
|
id: "todo_read",
|
|
26546
27010
|
name: "Todo Read",
|
|
26547
27011
|
description: "Use this tool to read your todo list",
|
|
26548
|
-
inputSchema:
|
|
27012
|
+
inputSchema: import_zod18.default.object({}),
|
|
26549
27013
|
type: "function",
|
|
26550
27014
|
category: "todo",
|
|
26551
27015
|
config: [
|
|
@@ -26590,7 +27054,7 @@ init_cjs_shims();
|
|
|
26590
27054
|
var questionread_default = 'Use this tool to read questions you\'ve asked and check if they\'ve been answered by the user. This tool helps you track the status of questions and retrieve the user\'s selected answers.\n\n## When to Use This Tool\n\nUse this tool proactively in these situations:\n- After asking a question to check if the user has responded\n- To retrieve the user\'s answer before proceeding with implementation\n- To review all questions and answers in the current session\n- When you need to reference a previous answer\n\n## How It Works\n\n- This tool takes no parameters (leave the input blank or empty)\n- Returns an array of all questions in the session\n- Each question includes:\n - `id`: Unique identifier for the question\n - `question`: The question text\n - `answerOptions`: Array of answer options with their IDs and text\n - `status`: Either "pending" (not answered) or "answered"\n - `selectedAnswerId`: The ID of the chosen answer (only present if answered)\n\n## Usage Pattern\n\nTypically you\'ll:\n1. Use Question Ask to pose a question\n2. Wait for the user to respond\n3. Use Question Read to check the answer\n4. Find the selected answer by matching the `selectedAnswerId` with an option in `answerOptions`\n5. Proceed with implementation based on the user\'s choice\n\n## Example Response\n\n```json\n[\n {\n "id": "question123",\n "question": "Which authentication method would you like to implement?",\n "answerOptions": [\n { "id": "ans1", "text": "JWT tokens" },\n { "id": "ans2", "text": "OAuth 2.0" },\n { "id": "ans3", "text": "Session-based auth" },\n { "id": "ans4", "text": "None of the above..." }\n ],\n "status": "answered",\n "selectedAnswerId": "ans1"\n }\n]\n```\n\nIn this example, the user selected "JWT tokens" (id: ans1).\n\n## Important Notes\n\n- If no questions exist in the session, an empty array will be returned\n- Questions remain in the session even after being answered for reference\n- Use the `selectedAnswerId` to find which answer option the user chose by matching it against the `id` field in `answerOptions`\n';
|
|
26591
27055
|
|
|
26592
27056
|
// src/templates/tools/question/question.ts
|
|
26593
|
-
var
|
|
27057
|
+
var import_zod20 = __toESM(require("zod"), 1);
|
|
26594
27058
|
init_tool();
|
|
26595
27059
|
init_client();
|
|
26596
27060
|
|
|
@@ -26682,21 +27146,21 @@ After asking a question, use the Question Read tool to check if the user has ans
|
|
|
26682
27146
|
`;
|
|
26683
27147
|
|
|
26684
27148
|
// src/templates/tools/question/question-ask.ts
|
|
26685
|
-
var
|
|
27149
|
+
var import_zod19 = __toESM(require("zod"), 1);
|
|
26686
27150
|
init_tool();
|
|
26687
27151
|
init_check_record_access();
|
|
26688
27152
|
init_client();
|
|
26689
27153
|
var import_node_crypto11 = require("crypto");
|
|
26690
|
-
var AnswerOptionSchema =
|
|
26691
|
-
id:
|
|
26692
|
-
text:
|
|
27154
|
+
var AnswerOptionSchema = import_zod19.default.object({
|
|
27155
|
+
id: import_zod19.default.string().describe("Unique identifier for the answer option"),
|
|
27156
|
+
text: import_zod19.default.string().describe("The text of the answer option")
|
|
26693
27157
|
});
|
|
26694
|
-
var _QuestionSchema =
|
|
26695
|
-
id:
|
|
26696
|
-
question:
|
|
26697
|
-
answerOptions:
|
|
26698
|
-
selectedAnswerId:
|
|
26699
|
-
status:
|
|
27158
|
+
var _QuestionSchema = import_zod19.default.object({
|
|
27159
|
+
id: import_zod19.default.string().describe("Unique identifier for the question"),
|
|
27160
|
+
question: import_zod19.default.string().describe("The question to ask the user"),
|
|
27161
|
+
answerOptions: import_zod19.default.array(AnswerOptionSchema).describe("Array of possible answer options"),
|
|
27162
|
+
selectedAnswerId: import_zod19.default.string().optional().describe("The ID of the answer option selected by the user"),
|
|
27163
|
+
status: import_zod19.default.enum(["pending", "answered"]).describe("Status of the question: pending or answered")
|
|
26700
27164
|
});
|
|
26701
27165
|
var QuestionAskTool = new ExuluTool({
|
|
26702
27166
|
id: "question_ask",
|
|
@@ -26713,9 +27177,9 @@ var QuestionAskTool = new ExuluTool({
|
|
|
26713
27177
|
default: questionask_default
|
|
26714
27178
|
}
|
|
26715
27179
|
],
|
|
26716
|
-
inputSchema:
|
|
26717
|
-
question:
|
|
26718
|
-
answerOptions:
|
|
27180
|
+
inputSchema: import_zod19.default.object({
|
|
27181
|
+
question: import_zod19.default.string().describe("The question to ask the user"),
|
|
27182
|
+
answerOptions: import_zod19.default.array(import_zod19.default.string()).describe("Array of possible answer options (strings)")
|
|
26719
27183
|
}),
|
|
26720
27184
|
execute: async (inputs) => {
|
|
26721
27185
|
const { sessionID, question, answerOptions, user } = inputs;
|
|
@@ -26788,7 +27252,7 @@ var QuestionReadTool = new ExuluTool({
|
|
|
26788
27252
|
name: "Question Read",
|
|
26789
27253
|
needsApproval: false,
|
|
26790
27254
|
description: "Use this tool to read questions and their answers",
|
|
26791
|
-
inputSchema:
|
|
27255
|
+
inputSchema: import_zod20.default.object({}),
|
|
26792
27256
|
type: "function",
|
|
26793
27257
|
category: "question",
|
|
26794
27258
|
config: [
|
|
@@ -26820,15 +27284,15 @@ var questionTools = [QuestionAskTool, QuestionReadTool];
|
|
|
26820
27284
|
// src/templates/tools/perplexity.ts
|
|
26821
27285
|
init_cjs_shims();
|
|
26822
27286
|
init_tool();
|
|
26823
|
-
var
|
|
27287
|
+
var import_zod21 = __toESM(require("zod"), 1);
|
|
26824
27288
|
var import_perplexity_ai = __toESM(require("@perplexity-ai/perplexity_ai"), 1);
|
|
26825
27289
|
var internetSearchTool = new ExuluTool({
|
|
26826
27290
|
id: "internet_search",
|
|
26827
27291
|
name: "Internet Search",
|
|
26828
27292
|
description: "Search the internet for information.",
|
|
26829
|
-
inputSchema:
|
|
26830
|
-
query:
|
|
26831
|
-
search_recency_filter:
|
|
27293
|
+
inputSchema: import_zod21.default.object({
|
|
27294
|
+
query: import_zod21.default.string().describe("The query to the tool."),
|
|
27295
|
+
search_recency_filter: import_zod21.default.enum(["day", "week", "month", "year"]).optional().describe("The recency filter for the search, can be day, week, month or year.")
|
|
26832
27296
|
}),
|
|
26833
27297
|
category: "internet_search",
|
|
26834
27298
|
type: "web_search",
|
|
@@ -26923,7 +27387,7 @@ var perplexityTools = [internetSearchTool];
|
|
|
26923
27387
|
init_cjs_shims();
|
|
26924
27388
|
init_tool();
|
|
26925
27389
|
var nodemailer = __toESM(require("nodemailer"), 1);
|
|
26926
|
-
var
|
|
27390
|
+
var import_zod22 = require("zod");
|
|
26927
27391
|
var transporter = null;
|
|
26928
27392
|
function getTransporter(config) {
|
|
26929
27393
|
if (!transporter) {
|
|
@@ -26947,11 +27411,11 @@ var emailTool = new ExuluTool({
|
|
|
26947
27411
|
id: "email",
|
|
26948
27412
|
name: "Email",
|
|
26949
27413
|
description: "Send an email.",
|
|
26950
|
-
inputSchema:
|
|
26951
|
-
recipient:
|
|
26952
|
-
subject:
|
|
26953
|
-
html:
|
|
26954
|
-
text:
|
|
27414
|
+
inputSchema: import_zod22.z.object({
|
|
27415
|
+
recipient: import_zod22.z.string().describe("The recipient of the email."),
|
|
27416
|
+
subject: import_zod22.z.string().describe("The subject of the email."),
|
|
27417
|
+
html: import_zod22.z.string().describe("The HTML body of the email."),
|
|
27418
|
+
text: import_zod22.z.string().describe("The text body of the email.")
|
|
26955
27419
|
}),
|
|
26956
27420
|
type: "function",
|
|
26957
27421
|
config: [{
|
|
@@ -27022,7 +27486,7 @@ init_tool();
|
|
|
27022
27486
|
init_supervisor();
|
|
27023
27487
|
init_client();
|
|
27024
27488
|
init_check_record_access();
|
|
27025
|
-
var
|
|
27489
|
+
var import_zod23 = require("zod");
|
|
27026
27490
|
var _cachedImageModels;
|
|
27027
27491
|
var setCachedImageModels = (models2) => {
|
|
27028
27492
|
_cachedImageModels = models2;
|
|
@@ -27083,8 +27547,8 @@ var createImageGenerationWidgetTool = (models2) => {
|
|
|
27083
27547
|
needsApproval: false,
|
|
27084
27548
|
type: "function",
|
|
27085
27549
|
config: [],
|
|
27086
|
-
inputSchema:
|
|
27087
|
-
prompt:
|
|
27550
|
+
inputSchema: import_zod23.z.object({
|
|
27551
|
+
prompt: import_zod23.z.string().describe(
|
|
27088
27552
|
"Initial image prompt. The user can edit it before generating."
|
|
27089
27553
|
)
|
|
27090
27554
|
}),
|
|
@@ -27123,7 +27587,7 @@ var createImageGenerationWidgetTool = (models2) => {
|
|
|
27123
27587
|
};
|
|
27124
27588
|
|
|
27125
27589
|
// src/exulu/app/index.ts
|
|
27126
|
-
var
|
|
27590
|
+
var import_node_path10 = require("path");
|
|
27127
27591
|
|
|
27128
27592
|
// src/validators/postgres-name.ts
|
|
27129
27593
|
init_cjs_shims();
|
|
@@ -27509,7 +27973,7 @@ var ExuluApp = class {
|
|
|
27509
27973
|
const imageGenerationTools = [];
|
|
27510
27974
|
const s3Configured = !!config?.fileUploads && !!config.fileUploads.s3region && !!config.fileUploads.s3key && !!config.fileUploads.s3secret && !!config.fileUploads.s3Bucket;
|
|
27511
27975
|
if (isLiteLLMEnabled() && s3Configured) {
|
|
27512
|
-
const configPath = process.env.LITELLM_CONFIG_PATH ?? (0,
|
|
27976
|
+
const configPath = process.env.LITELLM_CONFIG_PATH ?? (0, import_node_path10.resolve)(process.cwd(), "./config.litellm.yaml");
|
|
27513
27977
|
const imageModels = parseImageGenerationModels(configPath);
|
|
27514
27978
|
if (imageModels.length > 0) {
|
|
27515
27979
|
console.log(
|
|
@@ -28969,8 +29433,8 @@ init_cjs_shims();
|
|
|
28969
29433
|
// src/exulu/litellm/db-init.ts
|
|
28970
29434
|
init_cjs_shims();
|
|
28971
29435
|
var import_node_fs9 = require("fs");
|
|
28972
|
-
var
|
|
28973
|
-
var
|
|
29436
|
+
var import_node_path11 = require("path");
|
|
29437
|
+
var import_node_child_process6 = require("child_process");
|
|
28974
29438
|
var import_pg = require("pg");
|
|
28975
29439
|
|
|
28976
29440
|
// src/exulu/litellm/db-setup-check.ts
|
|
@@ -29039,7 +29503,7 @@ ${WARNING_BANNER}`);
|
|
|
29039
29503
|
};
|
|
29040
29504
|
var log5 = (line) => console.log(`[EXULU-LITELLM] ${line}`);
|
|
29041
29505
|
var initLiteLLMDatabase = async (packageRoot) => {
|
|
29042
|
-
const configPath = process.env.LITELLM_CONFIG_PATH ?? (0,
|
|
29506
|
+
const configPath = process.env.LITELLM_CONFIG_PATH ?? (0, import_node_path11.resolve)(process.cwd(), "./config.litellm.yaml");
|
|
29043
29507
|
const safety = checkLiteLLMDatabaseSafety(configPath);
|
|
29044
29508
|
if (safety.ok && safety.reason === "no-litellm-db-mode") return;
|
|
29045
29509
|
if (!safety.ok && safety.reason === "unparseable-url") {
|
|
@@ -29177,9 +29641,9 @@ var initLiteLLMDatabase = async (packageRoot) => {
|
|
|
29177
29641
|
]);
|
|
29178
29642
|
return;
|
|
29179
29643
|
}
|
|
29180
|
-
const venvBin = (0,
|
|
29181
|
-
const prismaCli = (0,
|
|
29182
|
-
const venvLibDir = (0,
|
|
29644
|
+
const venvBin = (0, import_node_path11.resolve)(packageRoot, "ee/python/.venv/bin");
|
|
29645
|
+
const prismaCli = (0, import_node_path11.resolve)(venvBin, "prisma");
|
|
29646
|
+
const venvLibDir = (0, import_node_path11.resolve)(packageRoot, "ee/python/.venv/lib");
|
|
29183
29647
|
const pythonVersionDir = (0, import_node_fs9.existsSync)(venvLibDir) ? (0, import_node_fs9.readdirSync)(venvLibDir).find((entry) => /^python3\.\d+$/.test(entry)) : void 0;
|
|
29184
29648
|
if (!pythonVersionDir) {
|
|
29185
29649
|
warn2([
|
|
@@ -29189,12 +29653,12 @@ var initLiteLLMDatabase = async (packageRoot) => {
|
|
|
29189
29653
|
]);
|
|
29190
29654
|
return;
|
|
29191
29655
|
}
|
|
29192
|
-
const litellmProxyDir = (0,
|
|
29656
|
+
const litellmProxyDir = (0, import_node_path11.resolve)(
|
|
29193
29657
|
venvLibDir,
|
|
29194
29658
|
pythonVersionDir,
|
|
29195
29659
|
"site-packages/litellm/proxy"
|
|
29196
29660
|
);
|
|
29197
|
-
const schemaPath = (0,
|
|
29661
|
+
const schemaPath = (0, import_node_path11.resolve)(litellmProxyDir, "schema.prisma");
|
|
29198
29662
|
if (!(0, import_node_fs9.existsSync)(prismaCli)) {
|
|
29199
29663
|
warn2([
|
|
29200
29664
|
`Prisma CLI not found at ${prismaCli}.`,
|
|
@@ -29211,7 +29675,7 @@ var initLiteLLMDatabase = async (packageRoot) => {
|
|
|
29211
29675
|
return;
|
|
29212
29676
|
}
|
|
29213
29677
|
log5("Running `prisma db push` against LiteLLM's schema\u2026");
|
|
29214
|
-
const result = (0,
|
|
29678
|
+
const result = (0, import_node_child_process6.spawnSync)(prismaCli, ["db", "push", "--skip-generate"], {
|
|
29215
29679
|
cwd: litellmProxyDir,
|
|
29216
29680
|
env: {
|
|
29217
29681
|
...process.env,
|
|
@@ -29783,14 +30247,14 @@ init_cjs_shims();
|
|
|
29783
30247
|
var fs4 = __toESM(require("fs"), 1);
|
|
29784
30248
|
var path = __toESM(require("path"), 1);
|
|
29785
30249
|
var import_ai18 = require("ai");
|
|
29786
|
-
var
|
|
30250
|
+
var import_zod24 = require("zod");
|
|
29787
30251
|
var import_p_limit = __toESM(require("p-limit"), 1);
|
|
29788
30252
|
var import_crypto2 = require("crypto");
|
|
29789
30253
|
init_with_retry();
|
|
29790
30254
|
var mammoth = __toESM(require("mammoth"), 1);
|
|
29791
30255
|
var import_turndown = __toESM(require("turndown"), 1);
|
|
29792
30256
|
var import_word_extractor = __toESM(require("word-extractor"), 1);
|
|
29793
|
-
var
|
|
30257
|
+
var import_officeparser3 = require("officeparser");
|
|
29794
30258
|
init_entitlements();
|
|
29795
30259
|
|
|
29796
30260
|
// src/utils/python-executor.ts
|
|
@@ -30250,15 +30714,15 @@ If the page contains a flow-chart, schematic, technical drawing or control board
|
|
|
30250
30714
|
const result = await (0, import_ai18.generateText)({
|
|
30251
30715
|
model,
|
|
30252
30716
|
output: import_ai18.Output.object({
|
|
30253
|
-
schema:
|
|
30254
|
-
needs_correction:
|
|
30255
|
-
corrected_text:
|
|
30256
|
-
current_page_table:
|
|
30257
|
-
headers:
|
|
30258
|
-
is_continuation:
|
|
30717
|
+
schema: import_zod24.z.object({
|
|
30718
|
+
needs_correction: import_zod24.z.boolean(),
|
|
30719
|
+
corrected_text: import_zod24.z.string().nullable(),
|
|
30720
|
+
current_page_table: import_zod24.z.object({
|
|
30721
|
+
headers: import_zod24.z.array(import_zod24.z.string()),
|
|
30722
|
+
is_continuation: import_zod24.z.boolean()
|
|
30259
30723
|
}).nullable(),
|
|
30260
|
-
confidence:
|
|
30261
|
-
reasoning:
|
|
30724
|
+
confidence: import_zod24.z.enum(["high", "medium", "low"]),
|
|
30725
|
+
reasoning: import_zod24.z.string()
|
|
30262
30726
|
})
|
|
30263
30727
|
}),
|
|
30264
30728
|
messages: [
|
|
@@ -30507,7 +30971,7 @@ ${setupResult.output || ""}`);
|
|
|
30507
30971
|
const jsonContent = await fs4.promises.readFile(paths.json, "utf-8");
|
|
30508
30972
|
json = JSON.parse(jsonContent);
|
|
30509
30973
|
} else if (config?.processor.name === "officeparser") {
|
|
30510
|
-
const text = await (0,
|
|
30974
|
+
const text = await (0, import_officeparser3.parseOfficeAsync)(buffer, {
|
|
30511
30975
|
outputErrorToConsole: false,
|
|
30512
30976
|
newlineDelimiter: "\n"
|
|
30513
30977
|
});
|