@electric-ax/agents 0.4.13 → 0.4.14

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.
@@ -4,7 +4,7 @@ import { Agent, cacheStores, interceptors, setGlobalDispatcher } from "undici";
4
4
  import fs from "node:fs";
5
5
  import pino from "pino";
6
6
  import { fileURLToPath } from "node:url";
7
- import { MOONSHOT_API_BASE_URL, MOONSHOT_PROVIDER, appendPathToUrl, completeWithLowCostModel, createEntityRegistry, createPullWakeRunner, createRuntimeHandler, createSkillTools, createSkillsRegistry, db, detectAvailableProviders, getMoonshotApiKey, getMoonshotModel, getMoonshotModels, readCodexAccessToken, registerToolProvider, unregisterToolProvider } from "@electric-ax/agents-runtime";
7
+ import { MOONSHOT_API_BASE_URL, MOONSHOT_PROVIDER, appendPathToUrl, buildSkillSlashCommands, completeWithLowCostModel, createContextSkillLoader, createEntityRegistry, createPullWakeRunner, createRuntimeHandler, createSkillsRegistry, db, detectAvailableProviders, getMoonshotApiKey, getMoonshotModel, getMoonshotModels, readCodexAccessToken, registerToolProvider, unregisterToolProvider } from "@electric-ax/agents-runtime";
8
8
  import { braveSearchTool, createBashTool, createEditTool, createEventSourceTools, createFetchUrlTool, createReadFileTool, createSendTool, createWriteTool } from "@electric-ax/agents-runtime/tools";
9
9
  import { chooseDefaultSandbox, isE2BAvailable, remoteSandbox } from "@electric-ax/agents-runtime/sandbox";
10
10
  import { z } from "zod";
@@ -989,6 +989,8 @@ function modelInputSchemaDefs(catalog) {
989
989
  //#region src/agents/horton.ts
990
990
  const TITLE_SYSTEM_PROMPT = "You generate concise chat session titles in 3-5 words. Respond with only the title, no quotes, no punctuation, no preamble.";
991
991
  const TITLE_USER_PROMPT = (userMessage) => `User request:\n${userMessage}`;
992
+ const TITLE_GENERATION_TIMEOUT_MS = 8e3;
993
+ const HORTON_SKILLS_SLASH_COMMAND_OWNER = `horton:skills`;
992
994
  const TITLE_STOP_WORDS = new Set([
993
995
  `a`,
994
996
  `an`,
@@ -1068,6 +1070,17 @@ function createConfiguredTitleCall(catalog, modelConfig, logPrefix) {
1068
1070
  maxTokens: 64
1069
1071
  });
1070
1072
  }
1073
+ function withTimeout(promise, ms, description) {
1074
+ let timeout;
1075
+ const timeoutPromise = new Promise((_resolve, reject) => {
1076
+ timeout = setTimeout(() => {
1077
+ reject(new Error(`${description} timed out after ${ms}ms`));
1078
+ }, ms);
1079
+ });
1080
+ return Promise.race([promise, timeoutPromise]).finally(() => {
1081
+ if (timeout) clearTimeout(timeout);
1082
+ });
1083
+ }
1071
1084
  async function generateTitle(userMessage, llmCall, onFallback) {
1072
1085
  try {
1073
1086
  const raw = await llmCall(TITLE_USER_PROMPT(userMessage));
@@ -1197,8 +1210,12 @@ function payloadToTitleText(payload) {
1197
1210
  if (typeof payload === `string`) return payload;
1198
1211
  if (payload == null) return ``;
1199
1212
  if (typeof payload === `object`) {
1200
- const text = payload.text;
1201
- return typeof text === `string` ? text : JSON.stringify(payload);
1213
+ const record = payload;
1214
+ const text = record.text;
1215
+ if (typeof text === `string`) return text;
1216
+ const source = record.source;
1217
+ if (typeof source === `string`) return source;
1218
+ return JSON.stringify(payload);
1202
1219
  }
1203
1220
  return String(payload);
1204
1221
  }
@@ -1256,8 +1273,10 @@ async function readAgentsMd(sandbox) {
1256
1273
  }
1257
1274
  function createAssistantHandler(options) {
1258
1275
  const { streamFn, docsSupport, docsSearchTool, skillsRegistry, modelCatalog, docsUrl } = options;
1259
- const hasSkills = Boolean(skillsRegistry && skillsRegistry.catalog.size > 0);
1276
+ const skillLoader = createContextSkillLoader(skillsRegistry, { slashCommandOwner: HORTON_SKILLS_SLASH_COMMAND_OWNER });
1277
+ const hasSkills = skillLoader.hasSkills;
1260
1278
  return async function assistantHandler(ctx, wake) {
1279
+ const loadedSkills = await skillLoader.load(ctx);
1261
1280
  const readSet = new Set();
1262
1281
  const modelConfig = resolveBuiltinModelConfig(modelCatalog, ctx.args);
1263
1282
  const sourceBudget = resolveBuiltinModelSourceBudget(modelConfig);
@@ -1271,7 +1290,7 @@ function createAssistantHandler(options) {
1271
1290
  modelCatalog,
1272
1291
  logPrefix: `[horton ${ctx.entityUrl}]`
1273
1292
  }),
1274
- ...skillsRegistry && skillsRegistry.catalog.size > 0 ? createSkillTools(skillsRegistry, ctx) : [],
1293
+ ...loadedSkills.tools,
1275
1294
  ...mcp.tools()
1276
1295
  ];
1277
1296
  const hasEventSourceTools = tools.some((tool) => getToolName(tool) === `list_event_sources`);
@@ -1280,15 +1299,16 @@ function createAssistantHandler(options) {
1280
1299
  if (!firstUserMessage) return;
1281
1300
  let title = null;
1282
1301
  try {
1283
- const result = await generateTitle(firstUserMessage, createConfiguredTitleCall(modelCatalog, modelConfig, `[horton ${ctx.entityUrl}]`), (reason) => {
1302
+ const result = await generateTitle(firstUserMessage, (prompt) => withTimeout(createConfiguredTitleCall(modelCatalog, modelConfig, `[horton ${ctx.entityUrl}]`)(prompt), TITLE_GENERATION_TIMEOUT_MS, `title generation`), (reason) => {
1284
1303
  serverLog.warn(`[horton ${ctx.entityUrl}] title generation fell back to local title: ${reason}`);
1285
1304
  });
1286
1305
  if (result.length > 0) title = result;
1287
1306
  } catch (err) {
1288
1307
  serverLog.warn(`[horton ${ctx.entityUrl}] title generation failed: ${err instanceof Error ? err.message : String(err)}`);
1308
+ title = buildFallbackTitle(firstUserMessage);
1289
1309
  }
1290
1310
  if (title !== null) try {
1291
- await ctx.setTag(`title`, title);
1311
+ await withTimeout(ctx.setTag(`title`, title), TITLE_GENERATION_TIMEOUT_MS, `set title tag`);
1292
1312
  } catch (err) {
1293
1313
  serverLog.warn(`[horton ${ctx.entityUrl}] setTag failed: ${err instanceof Error ? err.message : String(err)}`);
1294
1314
  }
@@ -1315,21 +1335,13 @@ function createAssistantHandler(options) {
1315
1335
  max: 2e4,
1316
1336
  cache: `stable`
1317
1337
  } } : {},
1318
- ...skillsRegistry && skillsRegistry.catalog.size > 0 ? { skills_catalog: {
1319
- content: () => skillsRegistry.renderCatalog(2e3),
1320
- max: 2e3,
1321
- cache: `stable`
1322
- } } : {}
1338
+ ...skillsRegistry && skillsRegistry.catalog.size > 0 ? loadedSkills.sources : {}
1323
1339
  }
1324
1340
  });
1325
1341
  else if (skillsRegistry && skillsRegistry.catalog.size > 0) ctx.useContext({
1326
1342
  sourceBudget,
1327
1343
  sources: {
1328
- skills_catalog: {
1329
- content: () => skillsRegistry.renderCatalog(2e3),
1330
- max: 2e3,
1331
- cache: `stable`
1332
- },
1344
+ ...loadedSkills.sources,
1333
1345
  conversation: {
1334
1346
  content: () => ctx.timelineMessages(),
1335
1347
  cache: `volatile`
@@ -1408,6 +1420,7 @@ function registerHorton(registry, options) {
1408
1420
  subject_value: `user`,
1409
1421
  permission: `manage`
1410
1422
  }],
1423
+ slashCommands: buildSkillSlashCommands(skillsRegistry),
1411
1424
  handler: assistantHandler
1412
1425
  });
1413
1426
  return [`horton`];
package/dist/index.cjs CHANGED
@@ -1002,6 +1002,8 @@ function modelInputSchemaDefs(catalog) {
1002
1002
  const HORTON_MODEL = `claude-sonnet-4-6`;
1003
1003
  const TITLE_SYSTEM_PROMPT = "You generate concise chat session titles in 3-5 words. Respond with only the title, no quotes, no punctuation, no preamble.";
1004
1004
  const TITLE_USER_PROMPT = (userMessage) => `User request:\n${userMessage}`;
1005
+ const TITLE_GENERATION_TIMEOUT_MS = 8e3;
1006
+ const HORTON_SKILLS_SLASH_COMMAND_OWNER = `horton:skills`;
1005
1007
  const TITLE_STOP_WORDS = new Set([
1006
1008
  `a`,
1007
1009
  `an`,
@@ -1081,6 +1083,17 @@ function createConfiguredTitleCall(catalog, modelConfig, logPrefix) {
1081
1083
  maxTokens: 64
1082
1084
  });
1083
1085
  }
1086
+ function withTimeout(promise, ms, description) {
1087
+ let timeout;
1088
+ const timeoutPromise = new Promise((_resolve, reject) => {
1089
+ timeout = setTimeout(() => {
1090
+ reject(new Error(`${description} timed out after ${ms}ms`));
1091
+ }, ms);
1092
+ });
1093
+ return Promise.race([promise, timeoutPromise]).finally(() => {
1094
+ if (timeout) clearTimeout(timeout);
1095
+ });
1096
+ }
1084
1097
  async function generateTitle(userMessage, llmCall, onFallback) {
1085
1098
  try {
1086
1099
  const raw = await llmCall(TITLE_USER_PROMPT(userMessage));
@@ -1210,8 +1223,12 @@ function payloadToTitleText(payload) {
1210
1223
  if (typeof payload === `string`) return payload;
1211
1224
  if (payload == null) return ``;
1212
1225
  if (typeof payload === `object`) {
1213
- const text = payload.text;
1214
- return typeof text === `string` ? text : JSON.stringify(payload);
1226
+ const record = payload;
1227
+ const text = record.text;
1228
+ if (typeof text === `string`) return text;
1229
+ const source = record.source;
1230
+ if (typeof source === `string`) return source;
1231
+ return JSON.stringify(payload);
1215
1232
  }
1216
1233
  return String(payload);
1217
1234
  }
@@ -1269,8 +1286,10 @@ async function readAgentsMd(sandbox) {
1269
1286
  }
1270
1287
  function createAssistantHandler(options) {
1271
1288
  const { streamFn, docsSupport, docsSearchTool, skillsRegistry, modelCatalog, docsUrl } = options;
1272
- const hasSkills = Boolean(skillsRegistry && skillsRegistry.catalog.size > 0);
1289
+ const skillLoader = (0, __electric_ax_agents_runtime.createContextSkillLoader)(skillsRegistry, { slashCommandOwner: HORTON_SKILLS_SLASH_COMMAND_OWNER });
1290
+ const hasSkills = skillLoader.hasSkills;
1273
1291
  return async function assistantHandler(ctx, wake) {
1292
+ const loadedSkills = await skillLoader.load(ctx);
1274
1293
  const readSet = new Set();
1275
1294
  const modelConfig = resolveBuiltinModelConfig(modelCatalog, ctx.args);
1276
1295
  const sourceBudget = resolveBuiltinModelSourceBudget(modelConfig);
@@ -1284,7 +1303,7 @@ function createAssistantHandler(options) {
1284
1303
  modelCatalog,
1285
1304
  logPrefix: `[horton ${ctx.entityUrl}]`
1286
1305
  }),
1287
- ...skillsRegistry && skillsRegistry.catalog.size > 0 ? (0, __electric_ax_agents_runtime.createSkillTools)(skillsRegistry, ctx) : [],
1306
+ ...loadedSkills.tools,
1288
1307
  ...__electric_ax_agents_mcp.mcp.tools()
1289
1308
  ];
1290
1309
  const hasEventSourceTools = tools.some((tool) => getToolName(tool) === `list_event_sources`);
@@ -1293,15 +1312,16 @@ function createAssistantHandler(options) {
1293
1312
  if (!firstUserMessage) return;
1294
1313
  let title = null;
1295
1314
  try {
1296
- const result = await generateTitle(firstUserMessage, createConfiguredTitleCall(modelCatalog, modelConfig, `[horton ${ctx.entityUrl}]`), (reason) => {
1315
+ const result = await generateTitle(firstUserMessage, (prompt) => withTimeout(createConfiguredTitleCall(modelCatalog, modelConfig, `[horton ${ctx.entityUrl}]`)(prompt), TITLE_GENERATION_TIMEOUT_MS, `title generation`), (reason) => {
1297
1316
  serverLog.warn(`[horton ${ctx.entityUrl}] title generation fell back to local title: ${reason}`);
1298
1317
  });
1299
1318
  if (result.length > 0) title = result;
1300
1319
  } catch (err) {
1301
1320
  serverLog.warn(`[horton ${ctx.entityUrl}] title generation failed: ${err instanceof Error ? err.message : String(err)}`);
1321
+ title = buildFallbackTitle(firstUserMessage);
1302
1322
  }
1303
1323
  if (title !== null) try {
1304
- await ctx.setTag(`title`, title);
1324
+ await withTimeout(ctx.setTag(`title`, title), TITLE_GENERATION_TIMEOUT_MS, `set title tag`);
1305
1325
  } catch (err) {
1306
1326
  serverLog.warn(`[horton ${ctx.entityUrl}] setTag failed: ${err instanceof Error ? err.message : String(err)}`);
1307
1327
  }
@@ -1328,21 +1348,13 @@ function createAssistantHandler(options) {
1328
1348
  max: 2e4,
1329
1349
  cache: `stable`
1330
1350
  } } : {},
1331
- ...skillsRegistry && skillsRegistry.catalog.size > 0 ? { skills_catalog: {
1332
- content: () => skillsRegistry.renderCatalog(2e3),
1333
- max: 2e3,
1334
- cache: `stable`
1335
- } } : {}
1351
+ ...skillsRegistry && skillsRegistry.catalog.size > 0 ? loadedSkills.sources : {}
1336
1352
  }
1337
1353
  });
1338
1354
  else if (skillsRegistry && skillsRegistry.catalog.size > 0) ctx.useContext({
1339
1355
  sourceBudget,
1340
1356
  sources: {
1341
- skills_catalog: {
1342
- content: () => skillsRegistry.renderCatalog(2e3),
1343
- max: 2e3,
1344
- cache: `stable`
1345
- },
1357
+ ...loadedSkills.sources,
1346
1358
  conversation: {
1347
1359
  content: () => ctx.timelineMessages(),
1348
1360
  cache: `volatile`
@@ -1421,6 +1433,7 @@ function registerHorton(registry, options) {
1421
1433
  subject_value: `user`,
1422
1434
  permission: `manage`
1423
1435
  }],
1436
+ slashCommands: (0, __electric_ax_agents_runtime.buildSkillSlashCommands)(skillsRegistry),
1424
1437
  handler: assistantHandler
1425
1438
  });
1426
1439
  return [`horton`];
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { mergeElectricPrincipalHeader } from "./server-headers-KD5yHFYT.js";
2
2
  import path from "node:path";
3
3
  import { fileURLToPath } from "node:url";
4
- import { MOONSHOT_API_BASE_URL, MOONSHOT_PROVIDER, appendPathToUrl, completeWithLowCostModel, createEntityRegistry, createPullWakeRunner, createRuntimeHandler, createSkillTools, createSkillsRegistry, db, detectAvailableProviders, getMoonshotApiKey, getMoonshotModel, getMoonshotModels, readCodexAccessToken, registerToolProvider, unregisterToolProvider } from "@electric-ax/agents-runtime";
4
+ import { MOONSHOT_API_BASE_URL, MOONSHOT_PROVIDER, appendPathToUrl, buildSkillSlashCommands, completeWithLowCostModel, createContextSkillLoader, createEntityRegistry, createPullWakeRunner, createRuntimeHandler, createSkillsRegistry, db, detectAvailableProviders, getMoonshotApiKey, getMoonshotModel, getMoonshotModels, readCodexAccessToken, registerToolProvider, unregisterToolProvider } from "@electric-ax/agents-runtime";
5
5
  import { braveSearchTool, braveSearchTool as braveSearchTool$1, createBashTool, createEditTool, createEventSourceTools, createFetchUrlTool, createReadFileTool, createSendTool, createWriteTool } from "@electric-ax/agents-runtime/tools";
6
6
  import { chooseDefaultSandbox, isE2BAvailable, remoteSandbox } from "@electric-ax/agents-runtime/sandbox";
7
7
  import fsSync from "node:fs";
@@ -978,6 +978,8 @@ function modelInputSchemaDefs(catalog) {
978
978
  const HORTON_MODEL = `claude-sonnet-4-6`;
979
979
  const TITLE_SYSTEM_PROMPT = "You generate concise chat session titles in 3-5 words. Respond with only the title, no quotes, no punctuation, no preamble.";
980
980
  const TITLE_USER_PROMPT = (userMessage) => `User request:\n${userMessage}`;
981
+ const TITLE_GENERATION_TIMEOUT_MS = 8e3;
982
+ const HORTON_SKILLS_SLASH_COMMAND_OWNER = `horton:skills`;
981
983
  const TITLE_STOP_WORDS = new Set([
982
984
  `a`,
983
985
  `an`,
@@ -1057,6 +1059,17 @@ function createConfiguredTitleCall(catalog, modelConfig, logPrefix) {
1057
1059
  maxTokens: 64
1058
1060
  });
1059
1061
  }
1062
+ function withTimeout(promise, ms, description) {
1063
+ let timeout;
1064
+ const timeoutPromise = new Promise((_resolve, reject) => {
1065
+ timeout = setTimeout(() => {
1066
+ reject(new Error(`${description} timed out after ${ms}ms`));
1067
+ }, ms);
1068
+ });
1069
+ return Promise.race([promise, timeoutPromise]).finally(() => {
1070
+ if (timeout) clearTimeout(timeout);
1071
+ });
1072
+ }
1060
1073
  async function generateTitle(userMessage, llmCall, onFallback) {
1061
1074
  try {
1062
1075
  const raw = await llmCall(TITLE_USER_PROMPT(userMessage));
@@ -1186,8 +1199,12 @@ function payloadToTitleText(payload) {
1186
1199
  if (typeof payload === `string`) return payload;
1187
1200
  if (payload == null) return ``;
1188
1201
  if (typeof payload === `object`) {
1189
- const text = payload.text;
1190
- return typeof text === `string` ? text : JSON.stringify(payload);
1202
+ const record = payload;
1203
+ const text = record.text;
1204
+ if (typeof text === `string`) return text;
1205
+ const source = record.source;
1206
+ if (typeof source === `string`) return source;
1207
+ return JSON.stringify(payload);
1191
1208
  }
1192
1209
  return String(payload);
1193
1210
  }
@@ -1245,8 +1262,10 @@ async function readAgentsMd(sandbox) {
1245
1262
  }
1246
1263
  function createAssistantHandler(options) {
1247
1264
  const { streamFn, docsSupport, docsSearchTool, skillsRegistry, modelCatalog, docsUrl } = options;
1248
- const hasSkills = Boolean(skillsRegistry && skillsRegistry.catalog.size > 0);
1265
+ const skillLoader = createContextSkillLoader(skillsRegistry, { slashCommandOwner: HORTON_SKILLS_SLASH_COMMAND_OWNER });
1266
+ const hasSkills = skillLoader.hasSkills;
1249
1267
  return async function assistantHandler(ctx, wake) {
1268
+ const loadedSkills = await skillLoader.load(ctx);
1250
1269
  const readSet = new Set();
1251
1270
  const modelConfig = resolveBuiltinModelConfig(modelCatalog, ctx.args);
1252
1271
  const sourceBudget = resolveBuiltinModelSourceBudget(modelConfig);
@@ -1260,7 +1279,7 @@ function createAssistantHandler(options) {
1260
1279
  modelCatalog,
1261
1280
  logPrefix: `[horton ${ctx.entityUrl}]`
1262
1281
  }),
1263
- ...skillsRegistry && skillsRegistry.catalog.size > 0 ? createSkillTools(skillsRegistry, ctx) : [],
1282
+ ...loadedSkills.tools,
1264
1283
  ...mcp.tools()
1265
1284
  ];
1266
1285
  const hasEventSourceTools = tools.some((tool) => getToolName(tool) === `list_event_sources`);
@@ -1269,15 +1288,16 @@ function createAssistantHandler(options) {
1269
1288
  if (!firstUserMessage) return;
1270
1289
  let title = null;
1271
1290
  try {
1272
- const result = await generateTitle(firstUserMessage, createConfiguredTitleCall(modelCatalog, modelConfig, `[horton ${ctx.entityUrl}]`), (reason) => {
1291
+ const result = await generateTitle(firstUserMessage, (prompt) => withTimeout(createConfiguredTitleCall(modelCatalog, modelConfig, `[horton ${ctx.entityUrl}]`)(prompt), TITLE_GENERATION_TIMEOUT_MS, `title generation`), (reason) => {
1273
1292
  serverLog.warn(`[horton ${ctx.entityUrl}] title generation fell back to local title: ${reason}`);
1274
1293
  });
1275
1294
  if (result.length > 0) title = result;
1276
1295
  } catch (err) {
1277
1296
  serverLog.warn(`[horton ${ctx.entityUrl}] title generation failed: ${err instanceof Error ? err.message : String(err)}`);
1297
+ title = buildFallbackTitle(firstUserMessage);
1278
1298
  }
1279
1299
  if (title !== null) try {
1280
- await ctx.setTag(`title`, title);
1300
+ await withTimeout(ctx.setTag(`title`, title), TITLE_GENERATION_TIMEOUT_MS, `set title tag`);
1281
1301
  } catch (err) {
1282
1302
  serverLog.warn(`[horton ${ctx.entityUrl}] setTag failed: ${err instanceof Error ? err.message : String(err)}`);
1283
1303
  }
@@ -1304,21 +1324,13 @@ function createAssistantHandler(options) {
1304
1324
  max: 2e4,
1305
1325
  cache: `stable`
1306
1326
  } } : {},
1307
- ...skillsRegistry && skillsRegistry.catalog.size > 0 ? { skills_catalog: {
1308
- content: () => skillsRegistry.renderCatalog(2e3),
1309
- max: 2e3,
1310
- cache: `stable`
1311
- } } : {}
1327
+ ...skillsRegistry && skillsRegistry.catalog.size > 0 ? loadedSkills.sources : {}
1312
1328
  }
1313
1329
  });
1314
1330
  else if (skillsRegistry && skillsRegistry.catalog.size > 0) ctx.useContext({
1315
1331
  sourceBudget,
1316
1332
  sources: {
1317
- skills_catalog: {
1318
- content: () => skillsRegistry.renderCatalog(2e3),
1319
- max: 2e3,
1320
- cache: `stable`
1321
- },
1333
+ ...loadedSkills.sources,
1322
1334
  conversation: {
1323
1335
  content: () => ctx.timelineMessages(),
1324
1336
  cache: `volatile`
@@ -1397,6 +1409,7 @@ function registerHorton(registry, options) {
1397
1409
  subject_value: `user`,
1398
1410
  permission: `manage`
1399
1411
  }],
1412
+ slashCommands: buildSkillSlashCommands(skillsRegistry),
1400
1413
  handler: assistantHandler
1401
1414
  });
1402
1415
  return [`horton`];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@electric-ax/agents",
3
- "version": "0.4.13",
3
+ "version": "0.4.14",
4
4
  "description": "Built-in Electric Agents runtimes such as Horton and worker",
5
5
  "repository": {
6
6
  "type": "git",
@@ -50,7 +50,7 @@
50
50
  "undici": "^7.24.7",
51
51
  "zod": "^4.3.6",
52
52
  "@electric-ax/agents-mcp": "0.2.2",
53
- "@electric-ax/agents-runtime": "0.3.9"
53
+ "@electric-ax/agents-runtime": "0.3.10"
54
54
  },
55
55
  "devDependencies": {
56
56
  "@types/better-sqlite3": "^7.6.13",