@circuitwall/jarela 0.7.2 → 0.7.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. package/.next/standalone/.next/BUILD_ID +1 -1
  2. package/.next/standalone/.next/build-manifest.json +2 -2
  3. package/.next/standalone/.next/prerender-manifest.json +3 -3
  4. package/.next/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  5. package/.next/standalone/.next/server/app/_global-error.html +1 -1
  6. package/.next/standalone/.next/server/app/_global-error.rsc +1 -1
  7. package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  8. package/.next/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  9. package/.next/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  10. package/.next/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  11. package/.next/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  12. package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  13. package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  14. package/.next/standalone/.next/server/app/_not-found.html +2 -2
  15. package/.next/standalone/.next/server/app/_not-found.rsc +2 -2
  16. package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
  17. package/.next/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  18. package/.next/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
  19. package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  20. package/.next/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  21. package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  22. package/.next/standalone/.next/server/app/api/v1/agents/[id]/compact/route.js +51 -35
  23. package/.next/standalone/.next/server/app/api/v1/agents/[id]/compact/route.js.map +1 -1
  24. package/.next/standalone/.next/server/app/api/v1/threads/[thread_id]/run/route.js +2 -2
  25. package/.next/standalone/.next/server/app/index.html +2 -2
  26. package/.next/standalone/.next/server/app/index.rsc +3 -3
  27. package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  28. package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +3 -3
  29. package/.next/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  30. package/.next/standalone/.next/server/app/index.segments/_index.segment.rsc +2 -2
  31. package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  32. package/.next/standalone/.next/server/app/page.js +515 -104
  33. package/.next/standalone/.next/server/app/page.js.map +1 -1
  34. package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  35. package/.next/standalone/.next/server/app/setup/page_client-reference-manifest.js +1 -1
  36. package/.next/standalone/.next/server/app/setup.html +1 -1
  37. package/.next/standalone/.next/server/app/setup.rsc +2 -2
  38. package/.next/standalone/.next/server/app/setup.segments/_full.segment.rsc +2 -2
  39. package/.next/standalone/.next/server/app/setup.segments/_head.segment.rsc +1 -1
  40. package/.next/standalone/.next/server/app/setup.segments/_index.segment.rsc +2 -2
  41. package/.next/standalone/.next/server/app/setup.segments/_tree.segment.rsc +2 -2
  42. package/.next/standalone/.next/server/app/setup.segments/setup/__PAGE__.segment.rsc +1 -1
  43. package/.next/standalone/.next/server/app/setup.segments/setup.segment.rsc +1 -1
  44. package/.next/standalone/.next/server/chunks/1683.js +26 -16
  45. package/.next/standalone/.next/server/chunks/1683.js.map +1 -1
  46. package/.next/standalone/.next/server/chunks/{317.js → 5432.js} +11100 -10858
  47. package/.next/standalone/.next/server/chunks/5432.js.map +1 -0
  48. package/.next/standalone/.next/server/chunks/7885.js +606 -353
  49. package/.next/standalone/.next/server/chunks/7885.js.map +1 -1
  50. package/.next/standalone/.next/server/chunks/8135.js +59 -16
  51. package/.next/standalone/.next/server/chunks/8135.js.map +1 -1
  52. package/.next/standalone/.next/server/chunks/9032.js +3 -3
  53. package/.next/standalone/.next/server/chunks/9032.js.map +1 -1
  54. package/.next/standalone/.next/server/instrumentation.js +3 -3
  55. package/.next/standalone/.next/server/instrumentation.js.map +1 -1
  56. package/.next/standalone/.next/server/middleware-build-manifest.js +2 -2
  57. package/.next/standalone/.next/server/pages/404.html +2 -2
  58. package/.next/standalone/.next/server/pages/500.html +1 -1
  59. package/.next/standalone/.next/server/server-reference-manifest.json +1 -1
  60. package/.next/standalone/.next/static/chunks/app/{page-a20902703c0a4f10.js → page-9fb006074fb13526.js} +582 -171
  61. package/.next/standalone/.next/static/chunks/app/page-9fb006074fb13526.js.map +1 -0
  62. package/.next/standalone/.next/static/css/5507dbe1cdc6c599.css +5 -0
  63. package/.next/standalone/.next/static/css/5507dbe1cdc6c599.css.map +1 -0
  64. package/.next/standalone/package.json +1 -1
  65. package/CHANGELOG.md +11 -0
  66. package/README.md +83 -1
  67. package/api/types.ts +7 -0
  68. package/app/api/v1/agents/[id]/compact/route.ts +2 -40
  69. package/components/bridges/BridgeEditor.tsx +8 -0
  70. package/components/chat/MessageBubble.tsx +3 -36
  71. package/components/models/ModelEditor.tsx +141 -0
  72. package/components/scheduled-tasks/ScheduledTasksPanel.tsx +5 -0
  73. package/components/scheduled-tasks/WatchersSection.tsx +5 -0
  74. package/lib/agents/context-budget.test.ts +128 -0
  75. package/lib/agents/context-budget.ts +128 -0
  76. package/lib/agents/conversation-summary.test.ts +68 -0
  77. package/lib/agents/conversation-summary.ts +51 -0
  78. package/lib/agents/run-thread.ts +112 -2
  79. package/lib/bridges/dispatcher.test.ts +134 -0
  80. package/lib/bridges/dispatcher.ts +34 -16
  81. package/lib/bridges/message-role.test.ts +83 -0
  82. package/lib/bridges/message-role.ts +46 -0
  83. package/lib/triggers/handlers/watcher.test.ts +23 -4
  84. package/lib/triggers/handlers/watcher.ts +56 -8
  85. package/package.json +1 -1
  86. package/.next/standalone/.next/server/chunks/317.js.map +0 -1
  87. package/.next/standalone/.next/static/chunks/app/page-a20902703c0a4f10.js.map +0 -1
  88. package/.next/standalone/.next/static/css/cc66c456aba91258.css +0 -5
  89. package/.next/standalone/.next/static/css/cc66c456aba91258.css.map +0 -1
  90. /package/.next/standalone/.next/static/{IauO0rNZkUVPX834k-SBa → AbCOWpaxP4v4lUSeFWWYz}/_buildManifest.js +0 -0
  91. /package/.next/standalone/.next/static/{IauO0rNZkUVPX834k-SBa → AbCOWpaxP4v4lUSeFWWYz}/_ssgManifest.js +0 -0
@@ -43,8 +43,8 @@ function __resetTriggerRegistry() {
43
43
  var threads = __webpack_require__(83042);
44
44
  // EXTERNAL MODULE: ./lib/stores/agent-configs.ts
45
45
  var agent_configs = __webpack_require__(41720);
46
- // EXTERNAL MODULE: ./lib/agents/run-thread.ts + 11 modules
47
- var run_thread = __webpack_require__(91396);
46
+ // EXTERNAL MODULE: ./lib/agents/run-thread.ts + 12 modules
47
+ var run_thread = __webpack_require__(76644);
48
48
  // EXTERNAL MODULE: ./lib/agents/stream-collector.ts
49
49
  var stream_collector = __webpack_require__(30685);
50
50
  // EXTERNAL MODULE: ./lib/triggers/scripts.ts
@@ -276,6 +276,8 @@ var external_node_crypto_ = __webpack_require__(77598);
276
276
  var watchers = __webpack_require__(96638);
277
277
  // EXTERNAL MODULE: ./lib/tools/registry.ts
278
278
  var registry = __webpack_require__(83085);
279
+ // EXTERNAL MODULE: ./lib/utils/text.ts
280
+ var utils_text = __webpack_require__(46723);
279
281
  ;// ./lib/triggers/handlers/watcher.ts
280
282
  // Watcher trigger handler (ADR-0027). Sibling of scheduled-task.ts.
281
283
  //
@@ -285,7 +287,7 @@ var registry = __webpack_require__(83085);
285
287
  // tools must be context-free).
286
288
  // 3. Hash the stringified result and compare to last_fingerprint.
287
289
  // 4. If the hash differs from the previous run, return a TriggerFiring
288
- // whose prompt embeds {previous, current} as context for the agent.
290
+ // whose prompt embeds a compact previous->current diff for the agent.
289
291
  // If it matches (or this is the first run with no previous), record
290
292
  // the fingerprint and skip — no LLM call, no firing.
291
293
  // 5. Either way the watcher's next_run_at is advanced by
@@ -298,6 +300,7 @@ var registry = __webpack_require__(83085);
298
300
 
299
301
 
300
302
 
303
+
301
304
  const WATCHER_KIND = "watcher";
302
305
  function fingerprint(s) {
303
306
  return (0,external_node_crypto_.createHash)("sha256").update(s).digest("hex");
@@ -311,6 +314,47 @@ function stringifyResult(value) {
311
314
  }
312
315
  }
313
316
  const DEFAULT_REACTION_DIRECTIVE = `Summarise what changed and decide whether the user needs to know. ` + `If nothing material changed, you may stay silent.`;
317
+ // Watcher tool outputs can be very large (full JSON payloads, long lists).
318
+ // Keep the diff context bounded so one firing cannot consume most of an
319
+ // agent's prompt budget.
320
+ const MAX_DIFF_CONTEXT_BYTES = 3500;
321
+ function normalizeForDiff(raw) {
322
+ try {
323
+ return JSON.stringify(JSON.parse(raw), null, 2);
324
+ } catch {
325
+ return raw;
326
+ }
327
+ }
328
+ function buildDiffForPrompt(previous, current) {
329
+ if (previous === null) return "+ (first observation baseline established; no diff available)";
330
+ const prev = normalizeForDiff(previous).split(/\r?\n/);
331
+ const curr = normalizeForDiff(current).split(/\r?\n/);
332
+ let start = 0;
333
+ while(start < prev.length && start < curr.length && prev[start] === curr[start]){
334
+ start += 1;
335
+ }
336
+ let prevEnd = prev.length - 1;
337
+ let currEnd = curr.length - 1;
338
+ while(prevEnd >= start && currEnd >= start && prev[prevEnd] === curr[currEnd]){
339
+ prevEnd -= 1;
340
+ currEnd -= 1;
341
+ }
342
+ const removed = prev.slice(start, prevEnd + 1);
343
+ const added = curr.slice(start, currEnd + 1);
344
+ if (removed.length === 0 && added.length === 0) {
345
+ return "(no textual diff after normalization)";
346
+ }
347
+ const hunkHeader = `@@ old:${start + 1}-${Math.max(start, prevEnd + 1)} new:${start + 1}-${Math.max(start, currEnd + 1)} @@`;
348
+ const raw = [
349
+ hunkHeader,
350
+ ...removed.map((l)=>`- ${l}`),
351
+ ...added.map((l)=>`+ ${l}`)
352
+ ].join("\n");
353
+ const bytes = Buffer.byteLength(raw, "utf8");
354
+ const clipped = (0,utils_text/* truncateBytes */.Q)(raw, MAX_DIFF_CONTEXT_BYTES);
355
+ if (!clipped.truncated) return raw;
356
+ return `${clipped.text}\n… [diff truncated: showing ${MAX_DIFF_CONTEXT_BYTES} of ${bytes} bytes; full values retained in watcher state]`;
357
+ }
314
358
  function buildFiringPrompt(watcher, previous, current) {
315
359
  const argsPretty = (()=>{
316
360
  try {
@@ -320,8 +364,8 @@ function buildFiringPrompt(watcher, previous, current) {
320
364
  }
321
365
  })();
322
366
  // ADR-0030: a non-null reaction_prompt swaps in for the default directive.
323
- // The diff envelope (label/tool/args/previous/current) is unchanged so the
324
- // agent always has the change context regardless of the user's instruction.
367
+ // The diff envelope (label/tool/args/diff) is unchanged so the agent
368
+ // always has the change context regardless of the user's instruction.
325
369
  const directive = watcher.reaction_prompt?.trim() || DEFAULT_REACTION_DIRECTIVE;
326
370
  return [
327
371
  `Watcher "${watcher.label}" detected a change.`,
@@ -329,11 +373,8 @@ function buildFiringPrompt(watcher, previous, current) {
329
373
  `Tool: ${watcher.tool_name}`,
330
374
  `Args: ${argsPretty}`,
331
375
  ``,
332
- `--- Previous result ---`,
333
- previous ?? "(none — first observation)",
334
- ``,
335
- `--- Current result ---`,
336
- current,
376
+ `--- Diff (previous -> current) ---`,
377
+ buildDiffForPrompt(previous, current),
337
378
  ``,
338
379
  directive
339
380
  ].join("\n");
@@ -1649,6 +1690,52 @@ async function tick() {
1649
1690
  }
1650
1691
 
1651
1692
 
1693
+ /***/ }),
1694
+
1695
+ /***/ 15393:
1696
+ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
1697
+
1698
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
1699
+ /* harmony export */ K: () => (/* binding */ transcriptText),
1700
+ /* harmony export */ S: () => (/* binding */ summarizeTranscript)
1701
+ /* harmony export */ });
1702
+ function transcriptText(raw) {
1703
+ if (!raw.startsWith("[")) return raw;
1704
+ try {
1705
+ const parsed = JSON.parse(raw);
1706
+ if (!Array.isArray(parsed)) return raw;
1707
+ return parsed.map((p)=>{
1708
+ if (p.type === "text") return p.text;
1709
+ if (p.type === "image") return `[image attachment: ${p.media_type}]`;
1710
+ if (p.type === "file") return `[file attachment: ${p.name} (${p.media_type})]`;
1711
+ return "";
1712
+ }).filter(Boolean).join(" ").trim();
1713
+ } catch {
1714
+ return raw;
1715
+ }
1716
+ }
1717
+ function summaryMessages(transcript) {
1718
+ return [
1719
+ {
1720
+ role: "system",
1721
+ content: "You are a concise summarizer. Summarize the conversation below in 3-7 bullet points, capturing key facts, decisions, and context that would be useful to remember later."
1722
+ },
1723
+ {
1724
+ role: "user",
1725
+ content: `Conversation to summarize:\n\n${transcript}`
1726
+ }
1727
+ ];
1728
+ }
1729
+ async function summarizeTranscript(provider, modelId, providerParams, transcript) {
1730
+ const trimmed = transcript.trim();
1731
+ if (!trimmed) return "";
1732
+ const { stream } = await provider.chat(modelId, summaryMessages(trimmed), providerParams);
1733
+ let summary = "";
1734
+ for await (const chunk of stream)summary += chunk;
1735
+ return summary.trim();
1736
+ }
1737
+
1738
+
1652
1739
  /***/ }),
1653
1740
 
1654
1741
  /***/ 18689:
@@ -5728,356 +5815,54 @@ function markTaskRan(id, kind, schedule, error) {
5728
5815
 
5729
5816
  /***/ }),
5730
5817
 
5731
- /***/ 79876:
5818
+ /***/ 76644:
5732
5819
  /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
5733
5820
 
5734
- /* harmony export */ __webpack_require__.d(__webpack_exports__, {
5735
- /* harmony export */ IT: () => (/* binding */ getScript),
5736
- /* harmony export */ Pl: () => (/* binding */ REACTION_SCRIPT_PREFIX),
5737
- /* harmony export */ S$: () => (/* binding */ listReactionScripts),
5738
- /* harmony export */ tX: () => (/* binding */ registerScript),
5739
- /* harmony export */ z3: () => (/* binding */ isReactionScript)
5740
- /* harmony export */ });
5741
- /* unused harmony exports listScripts, __resetScriptRegistry */
5742
- /* harmony import */ var _lib_utils_global_state__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(15559);
5743
- // ADR-0028. In-process script registry for ScriptFiring.
5744
- //
5745
- // Trust model: only built-ins are registered, in code, at module load
5746
- // time. There is NO mechanism to register a script from DB, user input,
5747
- // or shell-out — `script` on a ScriptFiring is just a key into this
5748
- // map. This keeps scripted triggers safe even though they bypass the
5749
- // LLM/thread machinery that gates prompt firings.
5750
5821
 
5751
- const state = (0,_lib_utils_global_state__WEBPACK_IMPORTED_MODULE_0__/* .getOrCreateGlobal */ .X)("__jarela_trigger_scripts", ()=>({
5752
- scripts: new Map()
5753
- }));
5754
- function registerScript(name, fn) {
5755
- // Idempotent re-registration is fine HMR and repeated module imports
5756
- // in dev re-execute the registration calls. We just overwrite with the
5757
- // latest function, matching the trigger handler registry's behaviour.
5758
- state.scripts.set(name, fn);
5759
- }
5760
- function getScript(name) {
5761
- return state.scripts.get(name);
5762
- }
5763
- function listScripts() {
5764
- return Array.from(state.scripts.keys()).sort();
5765
- }
5766
- // ADR-0031 — names beginning with this prefix are user-attachable
5767
- // reaction scripts. Internal plumbing scripts (e.g.
5768
- // `documents.reindex_local_file`) live under other namespaces and are
5769
- // excluded so the watcher schedule surface can't accidentally invoke
5770
- // them.
5771
- const REACTION_SCRIPT_PREFIX = "reaction.";
5772
- function listReactionScripts() {
5773
- return listScripts().filter((n)=>n.startsWith(REACTION_SCRIPT_PREFIX));
5774
- }
5775
- function isReactionScript(name) {
5776
- return name.startsWith(REACTION_SCRIPT_PREFIX) && state.scripts.has(name);
5777
- }
5778
- /** Test-only helper. */ function __resetScriptRegistry() {
5779
- state.scripts.clear();
5780
- }
5822
+ // EXPORTS
5823
+ __webpack_require__.d(__webpack_exports__, {
5824
+ op: () => (/* binding */ RunThreadError),
5825
+ _w: () => (/* binding */ persistAssistantMessage),
5826
+ fn: () => (/* binding */ prepareThreadRun),
5827
+ ib: () => (/* binding */ shouldEmitChunk)
5828
+ });
5781
5829
 
5830
+ // UNUSED EXPORTS: contentText, looksLikeStall
5782
5831
 
5783
- /***/ }),
5832
+ // EXTERNAL MODULE: ./node_modules/@langchain/langgraph/dist/prebuilt/index.js + 6 modules
5833
+ var prebuilt = __webpack_require__(67462);
5834
+ // EXTERNAL MODULE: ./node_modules/@langchain/core/dist/messages/index.js + 4 modules
5835
+ var dist_messages = __webpack_require__(12016);
5836
+ // EXTERNAL MODULE: ./lib/providers/index.ts + 8 modules
5837
+ var providers = __webpack_require__(68866);
5838
+ // EXTERNAL MODULE: ./lib/stores/model-config.ts
5839
+ var model_config = __webpack_require__(80937);
5840
+ // EXTERNAL MODULE: ./lib/tools/index.ts + 17 modules
5841
+ var lib_tools = __webpack_require__(36185);
5842
+ // EXTERNAL MODULE: ./node_modules/@langchain/core/dist/language_models/chat_models.js + 1 modules
5843
+ var chat_models = __webpack_require__(87233);
5844
+ // EXTERNAL MODULE: ./node_modules/@langchain/core/dist/outputs.js
5845
+ var outputs = __webpack_require__(40964);
5846
+ // EXTERNAL MODULE: ./node_modules/@langchain/core/dist/utils/function_calling.js
5847
+ var function_calling = __webpack_require__(46329);
5848
+ ;// ./lib/providers/jarela-chat-model.ts
5784
5849
 
5785
- /***/ 81753:
5786
- /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
5787
5850
 
5788
- /* harmony export */ __webpack_require__.d(__webpack_exports__, {
5789
- /* harmony export */ Fp: () => (/* binding */ describeToolSecrets),
5790
- /* harmony export */ eg: () => (/* binding */ deleteToolSecret),
5791
- /* harmony export */ pq: () => (/* binding */ getToolSecret),
5792
- /* harmony export */ zR: () => (/* binding */ setToolSecret)
5793
- /* harmony export */ });
5794
- /* unused harmony export purgeToolSecrets */
5795
- /* harmony import */ var _lib_stores_memory__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(80956);
5796
- // Encrypted-at-rest secrets for external tools (ADR-0023).
5797
- //
5798
- // External tools declare secret "slots" in their module.exports:
5799
- // secrets: [{ key: "api_key", label: "OpenWeather API key", required: true }]
5800
- //
5801
- // Values are persisted in the `tool-secrets` namespace of the memory store
5802
- // (which is flagged sensitive in lib/crypto/sensitive.ts, so envelope
5803
- // encryption applies — same primitive as the integrations store).
5804
- //
5805
- // Keys are stored as `<toolName>:<slotKey>`. Tool names and slot keys are
5806
- // validated to a conservative character set so they can't collide with the
5807
- // namespace delimiter or smuggle SQL/JSON metacharacters.
5808
- //
5809
- // Note on isolation: external tools run in-process with full Node privileges
5810
- // (ADR-0013), so a malicious tool could read another tool's secrets by going
5811
- // around this API (e.g. directly opening the SQLite file). The per-tool
5812
- // scoping here is a *convention* surfaced through `ctx.getSecret`, not a
5813
- // sandbox boundary.
5814
5851
 
5815
- const NAMESPACE = "tool-secrets";
5816
- const NAME_RE = /^[a-z0-9_-]+$/i;
5817
- function validName(s) {
5818
- return typeof s === "string" && s.length > 0 && s.length <= 64 && NAME_RE.test(s);
5819
- }
5820
- function compoundKey(toolName, key) {
5821
- return `${toolName}:${key}`;
5822
- }
5823
- function parseStored(raw) {
5824
- try {
5825
- const v = JSON.parse(raw);
5826
- return typeof v === "string" ? v : null;
5827
- } catch {
5828
- return null;
5852
+
5853
+ class JarelaChatModel extends chat_models/* BaseChatModel */.xV {
5854
+ constructor(fields){
5855
+ super({}), this.lc_namespace = [
5856
+ "jarela",
5857
+ "chat_models"
5858
+ ], this.lc_serializable = false;
5859
+ this._provider = fields.provider;
5860
+ this._modelId = fields.modelId;
5861
+ this._params = fields.params;
5862
+ this._boundTools = fields.boundTools ?? [];
5829
5863
  }
5830
- }
5831
- function getToolSecret(toolName, key) {
5832
- if (!validName(toolName) || !validName(key)) return null;
5833
- const row = (0,_lib_stores_memory__WEBPACK_IMPORTED_MODULE_0__/* .getMemory */ .So)(NAMESPACE, compoundKey(toolName, key));
5834
- if (!row) return null;
5835
- return parseStored(row.value);
5836
- }
5837
- function setToolSecret(toolName, key, value) {
5838
- if (!validName(toolName)) throw new Error(`invalid tool name: ${toolName}`);
5839
- if (!validName(key)) throw new Error(`invalid secret key: ${key}`);
5840
- if (typeof value !== "string") throw new Error("secret value must be a string");
5841
- (0,_lib_stores_memory__WEBPACK_IMPORTED_MODULE_0__/* .putMemory */ .Ri)(NAMESPACE, compoundKey(toolName, key), value);
5842
- }
5843
- function deleteToolSecret(toolName, key) {
5844
- if (!validName(toolName) || !validName(key)) return false;
5845
- return (0,_lib_stores_memory__WEBPACK_IMPORTED_MODULE_0__/* .deleteMemory */ .h0)(NAMESPACE, compoundKey(toolName, key));
5846
- }
5847
- // Returns which declared slots have a value persisted. Never returns the
5848
- // plaintext — that only escapes via `ctx.getSecret` inside the tool's own
5849
- // run loop.
5850
- function describeToolSecrets(toolName, slots) {
5851
- if (!validName(toolName)) return slots.map((s)=>({
5852
- ...s,
5853
- is_set: false
5854
- }));
5855
- return slots.map((s)=>({
5856
- ...s,
5857
- is_set: getToolSecret(toolName, s.key) !== null
5858
- }));
5859
- }
5860
- // Delete every persisted secret for a tool — used when an external tool
5861
- // file is removed and the operator wants to clear stale credentials.
5862
- function purgeToolSecrets(toolName) {
5863
- if (!validName(toolName)) return 0;
5864
- const prefix = `${toolName}:`;
5865
- const rows = listMemory(NAMESPACE, undefined, 1000).filter((r)=>r.key.startsWith(prefix));
5866
- let n = 0;
5867
- for (const r of rows){
5868
- if (deleteMemory(NAMESPACE, r.key)) n++;
5869
- }
5870
- return n;
5871
- }
5872
-
5873
-
5874
- /***/ }),
5875
-
5876
- /***/ 83042:
5877
- /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
5878
-
5879
- /* harmony export */ __webpack_require__.d(__webpack_exports__, {
5880
- /* harmony export */ Iz: () => (/* binding */ listThreadsByAgent),
5881
- /* harmony export */ KX: () => (/* binding */ deleteThread),
5882
- /* harmony export */ Mi: () => (/* binding */ listThreads),
5883
- /* harmony export */ Nn: () => (/* binding */ getMessagesAfter),
5884
- /* harmony export */ Nw: () => (/* binding */ createThread),
5885
- /* harmony export */ S0: () => (/* binding */ getRecentMessagesWindow),
5886
- /* harmony export */ VL: () => (/* binding */ getMessages),
5887
- /* harmony export */ az: () => (/* binding */ clearThreadMessages),
5888
- /* harmony export */ fG: () => (/* binding */ getThread),
5889
- /* harmony export */ mI: () => (/* binding */ touchThread),
5890
- /* harmony export */ qI: () => (/* binding */ getMessagesPage),
5891
- /* harmony export */ tj: () => (/* binding */ addMessage),
5892
- /* harmony export */ xH: () => (/* binding */ getOrCreateAgentThread)
5893
- /* harmony export */ });
5894
- /* harmony import */ var node_crypto__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(77598);
5895
- /* harmony import */ var node_crypto__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(node_crypto__WEBPACK_IMPORTED_MODULE_0__);
5896
- /* harmony import */ var _lib_db__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(46);
5897
- /* harmony import */ var _lib_embeddings__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(21839);
5898
-
5899
-
5900
-
5901
- const now = ()=>new Date().toISOString();
5902
- // Explicit column list for message reads — omits `embedding` (~20KB of
5903
- // JSON-encoded float[] per row) which only the embeddings module reads.
5904
- // Avoids dragging it through the chat-history result set on every call.
5905
- const MSG_COLS_SQL = "SELECT msg_id, thread_id, role, content, created_at, tool_events, category FROM messages";
5906
- function listThreads(limit = 50, offset = 0) {
5907
- return (0,_lib_db__WEBPACK_IMPORTED_MODULE_1__/* .getDb */ .Lf)().prepare("SELECT * FROM threads ORDER BY updated_at DESC LIMIT ? OFFSET ?").all(limit, offset);
5908
- }
5909
- function listThreadsByAgent(agent_id, limit = 50) {
5910
- return (0,_lib_db__WEBPACK_IMPORTED_MODULE_1__/* .getDb */ .Lf)().prepare("SELECT * FROM threads WHERE agent_id=? ORDER BY updated_at DESC LIMIT ?").all(agent_id, limit);
5911
- }
5912
- function getThread(thread_id) {
5913
- return (0,_lib_db__WEBPACK_IMPORTED_MODULE_1__/* .getDb */ .Lf)().prepare("SELECT * FROM threads WHERE thread_id=?").get(thread_id) ?? null;
5914
- }
5915
- function createThread(agent_id, title) {
5916
- const t = now();
5917
- const thread_id = (0,node_crypto__WEBPACK_IMPORTED_MODULE_0__.randomUUID)();
5918
- (0,_lib_db__WEBPACK_IMPORTED_MODULE_1__/* .getDb */ .Lf)().prepare("INSERT INTO threads (thread_id,agent_id,title,created_at,updated_at,message_count) VALUES (?,?,?,?,?,0)").run(thread_id, agent_id, title ?? null, t, t);
5919
- return {
5920
- thread_id,
5921
- agent_id,
5922
- title: title ?? null,
5923
- created_at: t,
5924
- updated_at: t,
5925
- message_count: 0
5926
- };
5927
- }
5928
- function deleteThread(thread_id) {
5929
- const db = (0,_lib_db__WEBPACK_IMPORTED_MODULE_1__/* .getDb */ .Lf)();
5930
- db.prepare("DELETE FROM messages WHERE thread_id=?").run(thread_id);
5931
- const r = db.prepare("DELETE FROM threads WHERE thread_id=?").run(thread_id);
5932
- return r.changes > 0;
5933
- }
5934
- function getMessages(thread_id) {
5935
- return (0,_lib_db__WEBPACK_IMPORTED_MODULE_1__/* .getDb */ .Lf)().prepare(MSG_COLS_SQL + " WHERE thread_id=? ORDER BY created_at ASC").all(thread_id);
5936
- }
5937
- // Pull the latest N messages within a time window. Used to build the LLM
5938
- // context — keeps prompt size bounded as threads grow indefinitely.
5939
- // limit: 0 or negative = unlimited
5940
- // sinceISO: undefined = no time bound
5941
- // Returns chronological order (oldest first) so it can be appended to the prompt directly.
5942
- function getRecentMessagesWindow(thread_id, limit, sinceISO) {
5943
- const db = (0,_lib_db__WEBPACK_IMPORTED_MODULE_1__/* .getDb */ .Lf)();
5944
- const params = [
5945
- thread_id
5946
- ];
5947
- let sql = MSG_COLS_SQL + " WHERE thread_id=?";
5948
- if (sinceISO) {
5949
- sql += " AND created_at >= ?";
5950
- params.push(sinceISO);
5951
- }
5952
- sql += " ORDER BY created_at DESC";
5953
- if (limit > 0) {
5954
- sql += " LIMIT ?";
5955
- params.push(limit);
5956
- }
5957
- const rows = db.prepare(sql).all(...params);
5958
- return rows.reverse();
5959
- }
5960
- // Forward-fetch — return messages strictly newer than `afterISO`, oldest
5961
- // first, capped at `limit`. Used by the chat view to pull only the
5962
- // freshly-persisted user+assistant pair after a run completes, instead of
5963
- // re-fetching the whole most-recent page.
5964
- function getMessagesAfter(thread_id, afterISO, limit = 50) {
5965
- return (0,_lib_db__WEBPACK_IMPORTED_MODULE_1__/* .getDb */ .Lf)().prepare(MSG_COLS_SQL + " WHERE thread_id=? AND created_at > ? ORDER BY created_at ASC LIMIT ?").all(thread_id, afterISO, limit);
5966
- }
5967
- // Pagination for the chat UI. Returns the latest N messages strictly older
5968
- // than `beforeISO` (cursor). Caller passes the oldest already-loaded message's
5969
- // created_at as the cursor; first page omits beforeISO.
5970
- function getMessagesPage(thread_id, limit, beforeISO) {
5971
- const db = (0,_lib_db__WEBPACK_IMPORTED_MODULE_1__/* .getDb */ .Lf)();
5972
- const params = [
5973
- thread_id
5974
- ];
5975
- let sql = MSG_COLS_SQL + " WHERE thread_id=?";
5976
- if (beforeISO) {
5977
- sql += " AND created_at < ?";
5978
- params.push(beforeISO);
5979
- }
5980
- sql += " ORDER BY created_at DESC LIMIT ?";
5981
- params.push(limit + 1); // fetch one extra to detect if there's more
5982
- const rows = db.prepare(sql).all(...params);
5983
- const has_more = rows.length > limit;
5984
- return {
5985
- messages: rows.slice(0, limit).reverse(),
5986
- has_more
5987
- };
5988
- }
5989
- function addMessage(thread_id, role, content, toolEvents, category = null) {
5990
- const msg_id = (0,node_crypto__WEBPACK_IMPORTED_MODULE_0__.randomUUID)();
5991
- const t = now();
5992
- const db = (0,_lib_db__WEBPACK_IMPORTED_MODULE_1__/* .getDb */ .Lf)();
5993
- const toolEventsJson = toolEvents && toolEvents.length > 0 ? JSON.stringify(toolEvents) : null;
5994
- db.prepare("INSERT INTO messages (msg_id,thread_id,role,content,created_at,tool_events,category) VALUES (?,?,?,?,?,?,?)").run(msg_id, thread_id, role, content, t, toolEventsJson, category);
5995
- db.prepare("UPDATE threads SET message_count=message_count+1 WHERE thread_id=?").run(thread_id);
5996
- // Best-effort: embed the message so semantic recall can pull it back later.
5997
- // Skip empty / very short content (greetings have no useful signal).
5998
- if (content.trim().length >= 12) {
5999
- (0,_lib_embeddings__WEBPACK_IMPORTED_MODULE_2__/* .embedOne */ ._L)(content).then((vec)=>{
6000
- if (vec) {
6001
- (0,_lib_db__WEBPACK_IMPORTED_MODULE_1__/* .getDb */ .Lf)().prepare("UPDATE messages SET embedding=? WHERE msg_id=?").run(JSON.stringify(vec), msg_id);
6002
- }
6003
- }).catch(()=>{});
6004
- }
6005
- return {
6006
- msg_id,
6007
- thread_id,
6008
- role,
6009
- content,
6010
- created_at: t,
6011
- tool_events: toolEventsJson,
6012
- category
6013
- };
6014
- }
6015
- function getOrCreateAgentThread(agentId) {
6016
- const existing = (0,_lib_db__WEBPACK_IMPORTED_MODULE_1__/* .getDb */ .Lf)().prepare("SELECT * FROM threads WHERE agent_id=? LIMIT 1").get(agentId);
6017
- if (existing) return existing;
6018
- return createThread(agentId);
6019
- }
6020
- function clearThreadMessages(threadId) {
6021
- const db = (0,_lib_db__WEBPACK_IMPORTED_MODULE_1__/* .getDb */ .Lf)();
6022
- db.prepare("DELETE FROM messages WHERE thread_id=?").run(threadId);
6023
- db.prepare("UPDATE threads SET message_count=0, updated_at=? WHERE thread_id=?").run(new Date().toISOString(), threadId);
6024
- }
6025
- function touchThread(thread_id, firstMsg) {
6026
- const t = now();
6027
- (0,_lib_db__WEBPACK_IMPORTED_MODULE_1__/* .getDb */ .Lf)().prepare("UPDATE threads SET updated_at=?, title=COALESCE(title,?) WHERE thread_id=?").run(t, firstMsg ? firstMsg.slice(0, 80) : null, thread_id);
6028
- }
6029
-
6030
-
6031
- /***/ }),
6032
-
6033
- /***/ 91396:
6034
- /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
6035
-
6036
-
6037
- // EXPORTS
6038
- __webpack_require__.d(__webpack_exports__, {
6039
- op: () => (/* binding */ RunThreadError),
6040
- _w: () => (/* binding */ persistAssistantMessage),
6041
- fn: () => (/* binding */ prepareThreadRun),
6042
- ib: () => (/* binding */ shouldEmitChunk)
6043
- });
6044
-
6045
- // UNUSED EXPORTS: contentText, looksLikeStall
6046
-
6047
- // EXTERNAL MODULE: ./node_modules/@langchain/langgraph/dist/prebuilt/index.js + 6 modules
6048
- var prebuilt = __webpack_require__(67462);
6049
- // EXTERNAL MODULE: ./node_modules/@langchain/core/dist/messages/index.js + 4 modules
6050
- var dist_messages = __webpack_require__(12016);
6051
- // EXTERNAL MODULE: ./lib/providers/index.ts + 8 modules
6052
- var providers = __webpack_require__(68866);
6053
- // EXTERNAL MODULE: ./lib/stores/model-config.ts
6054
- var model_config = __webpack_require__(80937);
6055
- // EXTERNAL MODULE: ./lib/tools/index.ts + 17 modules
6056
- var lib_tools = __webpack_require__(36185);
6057
- // EXTERNAL MODULE: ./node_modules/@langchain/core/dist/language_models/chat_models.js + 1 modules
6058
- var chat_models = __webpack_require__(87233);
6059
- // EXTERNAL MODULE: ./node_modules/@langchain/core/dist/outputs.js
6060
- var outputs = __webpack_require__(40964);
6061
- // EXTERNAL MODULE: ./node_modules/@langchain/core/dist/utils/function_calling.js
6062
- var function_calling = __webpack_require__(46329);
6063
- ;// ./lib/providers/jarela-chat-model.ts
6064
-
6065
-
6066
-
6067
-
6068
- class JarelaChatModel extends chat_models/* BaseChatModel */.xV {
6069
- constructor(fields){
6070
- super({}), this.lc_namespace = [
6071
- "jarela",
6072
- "chat_models"
6073
- ], this.lc_serializable = false;
6074
- this._provider = fields.provider;
6075
- this._modelId = fields.modelId;
6076
- this._params = fields.params;
6077
- this._boundTools = fields.boundTools ?? [];
6078
- }
6079
- _llmType() {
6080
- return `jarela_${this._provider?.name ?? "chat"}`;
5864
+ _llmType() {
5865
+ return `jarela_${this._provider?.name ?? "chat"}`;
6081
5866
  }
6082
5867
  // createReactAgent calls bindTools() to attach the tool list before streaming.
6083
5868
  bindTools(tools, _kwargs) {
@@ -7553,6 +7338,101 @@ var app_config = __webpack_require__(48954);
7553
7338
  // EXTERNAL MODULE: external "node:os"
7554
7339
  var external_node_os_ = __webpack_require__(48161);
7555
7340
  var external_node_os_default = /*#__PURE__*/__webpack_require__.n(external_node_os_);
7341
+ // EXTERNAL MODULE: ./lib/agents/conversation-summary.ts
7342
+ var conversation_summary = __webpack_require__(15393);
7343
+ ;// ./lib/agents/context-budget.ts
7344
+
7345
+ const DEFAULT_CONTEXT_WINDOW_TOKENS = 8192;
7346
+ const DEFAULT_OVERHEAD_TOKENS = 1200;
7347
+ const DEFAULT_OUTPUT_RESERVE_RATIO = 0.2;
7348
+ const DEFAULT_TIER_PRIORITY = [
7349
+ "hot",
7350
+ "warm",
7351
+ "facts"
7352
+ ];
7353
+ const DEFAULT_TIER_PROPORTIONS = {
7354
+ hot: 0.6,
7355
+ warm: 0.25,
7356
+ facts: 0.15
7357
+ };
7358
+ function estimateTokens(text) {
7359
+ const trimmed = text.trim();
7360
+ if (!trimmed) return 0;
7361
+ return Math.max(1, Math.ceil(trimmed.length / 4));
7362
+ }
7363
+ function normalizeTierPriority(value) {
7364
+ if (!Array.isArray(value)) return DEFAULT_TIER_PRIORITY;
7365
+ const tiers = value.filter((v)=>v === "hot" || v === "warm" || v === "facts");
7366
+ if (tiers.length !== 3) return DEFAULT_TIER_PRIORITY;
7367
+ if (new Set(tiers).size !== 3) return DEFAULT_TIER_PRIORITY;
7368
+ return [
7369
+ tiers[0],
7370
+ tiers[1],
7371
+ tiers[2]
7372
+ ];
7373
+ }
7374
+ function normalizeTierProportions(value) {
7375
+ const hot = toPositiveNumber(value?.hot, DEFAULT_TIER_PROPORTIONS.hot);
7376
+ const warm = toPositiveNumber(value?.warm, DEFAULT_TIER_PROPORTIONS.warm);
7377
+ const facts = toPositiveNumber(value?.facts, DEFAULT_TIER_PROPORTIONS.facts);
7378
+ const sum = hot + warm + facts;
7379
+ if (sum <= 0) return DEFAULT_TIER_PROPORTIONS;
7380
+ return {
7381
+ hot: hot / sum,
7382
+ warm: warm / sum,
7383
+ facts: facts / sum
7384
+ };
7385
+ }
7386
+ function computeContextBudget(config) {
7387
+ const contextWindowTokens = Math.max(1, Math.floor(config.context_window_tokens ?? DEFAULT_CONTEXT_WINDOW_TOKENS));
7388
+ const outputReserveTokens = Math.max(256, Math.min(contextWindowTokens - 1, Math.floor(config.max_tokens ?? contextWindowTokens * DEFAULT_OUTPUT_RESERVE_RATIO)));
7389
+ const overheadTokens = Math.max(0, Math.min(DEFAULT_OVERHEAD_TOKENS, contextWindowTokens - outputReserveTokens));
7390
+ const inputBudgetTokens = Math.max(0, contextWindowTokens - outputReserveTokens - overheadTokens);
7391
+ const proportions = normalizeTierProportions(config.context_tier_proportions);
7392
+ const tierPriority = normalizeTierPriority(config.context_tier_priority);
7393
+ const tierBudgets = {
7394
+ hot: Math.floor(inputBudgetTokens * proportions.hot),
7395
+ warm: Math.floor(inputBudgetTokens * proportions.warm),
7396
+ facts: Math.max(0, inputBudgetTokens - Math.floor(inputBudgetTokens * proportions.hot) - Math.floor(inputBudgetTokens * proportions.warm))
7397
+ };
7398
+ return {
7399
+ contextWindowTokens,
7400
+ outputReserveTokens,
7401
+ inputBudgetTokens,
7402
+ overheadTokens,
7403
+ tierBudgets,
7404
+ tierPriority
7405
+ };
7406
+ }
7407
+ function takeRecentMessagesWithinBudget(messages, tokenBudget) {
7408
+ if (tokenBudget <= 0 || messages.length === 0) return [];
7409
+ const chosen = [];
7410
+ let used = 0;
7411
+ for(let i = messages.length - 1; i >= 0; i -= 1){
7412
+ const msg = messages[i];
7413
+ const tokens = estimateTokens((0,conversation_summary/* transcriptText */.K)(msg.content));
7414
+ if (chosen.length > 0 && used + tokens > tokenBudget) break;
7415
+ chosen.push(msg);
7416
+ used += tokens;
7417
+ if (used >= tokenBudget) break;
7418
+ }
7419
+ return chosen.reverse();
7420
+ }
7421
+ function formatContextBudgetSummary(budget) {
7422
+ const parts = [
7423
+ `window ${budget.contextWindowTokens} tokens`,
7424
+ `output reserve ${budget.outputReserveTokens}`,
7425
+ `input budget ${budget.inputBudgetTokens}`,
7426
+ `hot ${budget.tierBudgets.hot}`,
7427
+ `warm ${budget.tierBudgets.warm}`,
7428
+ `facts ${budget.tierBudgets.facts}`
7429
+ ];
7430
+ return parts.join(" · ");
7431
+ }
7432
+ function toPositiveNumber(value, fallback) {
7433
+ return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : fallback;
7434
+ }
7435
+
7556
7436
  ;// ./lib/agents/run-thread.ts
7557
7437
 
7558
7438
 
@@ -7567,6 +7447,11 @@ var external_node_os_default = /*#__PURE__*/__webpack_require__.n(external_node_
7567
7447
 
7568
7448
 
7569
7449
 
7450
+
7451
+
7452
+
7453
+
7454
+
7570
7455
  // Resolve the app name once at module load. Forks set NEXT_PUBLIC_APP_NAME to
7571
7456
  // rebrand the user-visible name the LLM echoes in chat replies; default
7572
7457
  // "Jarela" for upstream. Per-turn pieces of the system prompt (user profile,
@@ -7666,7 +7551,24 @@ userCategory = null) {
7666
7551
  const limit = agentCfg.history_limit ?? 50;
7667
7552
  const windowHours = agentCfg.history_window_hours ?? 8;
7668
7553
  const sinceISO = windowHours > 0 ? new Date(Date.now() - windowHours * 3600000).toISOString() : undefined;
7669
- const history = (0,threads/* getRecentMessagesWindow */.S0)(thread_id, limit, sinceISO).map((m)=>({
7554
+ const allWindowMessages = (0,threads/* getRecentMessagesWindow */.S0)(thread_id, limit, sinceISO);
7555
+ const modelCfg = agentCfg.model_config_name ? (0,model_config/* getModelConfig */.Ac)(agentCfg.model_config_name) : (0,model_config/* getDefaultModelConfig */.lB)();
7556
+ let providerParams = {};
7557
+ if (modelCfg) {
7558
+ try {
7559
+ providerParams = JSON.parse(modelCfg.params);
7560
+ } catch {
7561
+ providerParams = {};
7562
+ }
7563
+ }
7564
+ const budget = computeContextBudget({
7565
+ context_window_tokens: typeof providerParams.context_window_tokens === "number" ? providerParams.context_window_tokens : undefined,
7566
+ max_tokens: typeof providerParams.max_tokens === "number" ? providerParams.max_tokens : undefined,
7567
+ context_tier_proportions: typeof providerParams.context_tier_proportions === "object" && providerParams.context_tier_proportions ? providerParams.context_tier_proportions : undefined,
7568
+ context_tier_priority: providerParams.context_tier_priority
7569
+ });
7570
+ const hotMessages = takeRecentMessagesWithinBudget(allWindowMessages, budget.tierBudgets.hot);
7571
+ const history = hotMessages.map((m)=>({
7670
7572
  role: m.role,
7671
7573
  content: parseContent(m.content)
7672
7574
  }));
@@ -7752,11 +7654,19 @@ userCategory = null) {
7752
7654
  const memoryCtx = [
7753
7655
  "--- Memory & recall ---",
7754
7656
  "You have long-term memory across sessions and a fresh recall pass on every turn.",
7755
- `- The recent ${limit} messages from the last ${windowHours}h are already in your context above.`,
7657
+ `- Hot conversation history is budgeted by model context size: ${formatContextBudgetSummary(budget)}.`,
7756
7658
  "- A semantic search over all stored memory entries + past chat messages was run against the user's turn; matching items appear under \"Relevant context\" below.",
7757
7659
  "- Use memory_write proactively when the user shares a fact, preference, or decision worth remembering. Use memory_read / memory_list to recall stored facts on demand.",
7758
7660
  "- If you want detail from outside the recent window, the user can scroll up — but for facts you've stored explicitly, prefer recall over guessing."
7759
7661
  ].join("\n");
7662
+ const warmSummaryCtx = await buildWarmSummaryContext(allWindowMessages, hotMessages.length, modelCfg?.provider, modelCfg?.model_id, providerParams, budget.tierBudgets.warm);
7663
+ const factsCtx = buildFactsContext(trimmed, budget.tierBudgets.facts);
7664
+ const tierCtxByName = {
7665
+ hot: "",
7666
+ warm: warmSummaryCtx,
7667
+ facts: factsCtx
7668
+ };
7669
+ const tierOrderCtx = budget.tierPriority.map((tier)=>tierCtxByName[tier]).filter(Boolean);
7760
7670
  // Semantic recall: pull in long-term memory + past messages relevant to this turn.
7761
7671
  // Skip messages from the current thread that are already in the windowed history.
7762
7672
  // Capped at RECALL_BUDGET_MS — if the embedding round-trip is slower than
@@ -7780,6 +7690,7 @@ userCategory = null) {
7780
7690
  envCtx,
7781
7691
  harnessParts.self_config,
7782
7692
  memoryCtx,
7693
+ ...tierOrderCtx,
7783
7694
  recallCtx
7784
7695
  ].filter(Boolean);
7785
7696
  let allowedTools = [];
@@ -7800,6 +7711,46 @@ userCategory = null) {
7800
7711
  thread_id
7801
7712
  };
7802
7713
  }
7714
+ async function buildWarmSummaryContext(allWindowMessages, hotCount, providerName, modelId, providerParams, warmBudgetTokens) {
7715
+ if (warmBudgetTokens <= 32) return "";
7716
+ if (!providerName || !modelId) return "";
7717
+ const warmMessages = allWindowMessages.slice(0, Math.max(0, allWindowMessages.length - hotCount));
7718
+ if (warmMessages.length < 2) return "";
7719
+ // Keep summary input bounded by the warm budget to avoid recursive prompt bloat.
7720
+ const summaryInputChars = Math.max(0, warmBudgetTokens * 4);
7721
+ const transcript = warmMessages.map((m)=>`${m.role === "user" ? "User" : "Assistant"}: ${(0,conversation_summary/* transcriptText */.K)(m.content)}`).join("\n\n").slice(-summaryInputChars);
7722
+ if (!transcript.trim()) return "";
7723
+ try {
7724
+ const provider = (0,providers/* getProvider */.sO)(providerName);
7725
+ const summary = await (0,conversation_summary/* summarizeTranscript */.S)(provider, modelId, providerParams, transcript);
7726
+ if (!summary) return "";
7727
+ return [
7728
+ "--- Warm context summary ---",
7729
+ "Compressed recap of earlier messages outside the hot window:",
7730
+ summary
7731
+ ].join("\n");
7732
+ } catch {
7733
+ return "";
7734
+ }
7735
+ }
7736
+ function buildFactsContext(query, factsBudgetTokens) {
7737
+ if (factsBudgetTokens <= 16) return "";
7738
+ const charBudget = factsBudgetTokens * 4;
7739
+ const rows = (0,memory/* listMemory */.Q_)("facts", query.slice(0, 120), 12);
7740
+ if (rows.length === 0) return "";
7741
+ const lines = [
7742
+ "--- Facts memory ---",
7743
+ "Durable fact entries from memory_store namespace=facts:"
7744
+ ];
7745
+ let used = 0;
7746
+ for (const row of rows){
7747
+ const line = `- ${row.key}: ${String(row.value).slice(0, 220)}`;
7748
+ if (used > 0 && used + line.length > charBudget) break;
7749
+ lines.push(line);
7750
+ used += line.length;
7751
+ }
7752
+ return lines.length > 2 ? lines.join("\n") : "";
7753
+ }
7803
7754
  // Wraps the raw agent stream with stall-retry logic. Chunks pass through
7804
7755
  // LIVE to the consumer (so the chat UI sees deltas as they arrive); we only
7805
7756
  // hold the terminal `done` chunk so we can decide whether to retry. If the
@@ -8029,6 +7980,308 @@ function shouldEmitChunk(chunkType, options) {
8029
7980
 
8030
7981
 
8031
7982
 
7983
+ /***/ }),
7984
+
7985
+ /***/ 79876:
7986
+ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
7987
+
7988
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
7989
+ /* harmony export */ IT: () => (/* binding */ getScript),
7990
+ /* harmony export */ Pl: () => (/* binding */ REACTION_SCRIPT_PREFIX),
7991
+ /* harmony export */ S$: () => (/* binding */ listReactionScripts),
7992
+ /* harmony export */ tX: () => (/* binding */ registerScript),
7993
+ /* harmony export */ z3: () => (/* binding */ isReactionScript)
7994
+ /* harmony export */ });
7995
+ /* unused harmony exports listScripts, __resetScriptRegistry */
7996
+ /* harmony import */ var _lib_utils_global_state__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(15559);
7997
+ // ADR-0028. In-process script registry for ScriptFiring.
7998
+ //
7999
+ // Trust model: only built-ins are registered, in code, at module load
8000
+ // time. There is NO mechanism to register a script from DB, user input,
8001
+ // or shell-out — `script` on a ScriptFiring is just a key into this
8002
+ // map. This keeps scripted triggers safe even though they bypass the
8003
+ // LLM/thread machinery that gates prompt firings.
8004
+
8005
+ const state = (0,_lib_utils_global_state__WEBPACK_IMPORTED_MODULE_0__/* .getOrCreateGlobal */ .X)("__jarela_trigger_scripts", ()=>({
8006
+ scripts: new Map()
8007
+ }));
8008
+ function registerScript(name, fn) {
8009
+ // Idempotent re-registration is fine — HMR and repeated module imports
8010
+ // in dev re-execute the registration calls. We just overwrite with the
8011
+ // latest function, matching the trigger handler registry's behaviour.
8012
+ state.scripts.set(name, fn);
8013
+ }
8014
+ function getScript(name) {
8015
+ return state.scripts.get(name);
8016
+ }
8017
+ function listScripts() {
8018
+ return Array.from(state.scripts.keys()).sort();
8019
+ }
8020
+ // ADR-0031 — names beginning with this prefix are user-attachable
8021
+ // reaction scripts. Internal plumbing scripts (e.g.
8022
+ // `documents.reindex_local_file`) live under other namespaces and are
8023
+ // excluded so the watcher schedule surface can't accidentally invoke
8024
+ // them.
8025
+ const REACTION_SCRIPT_PREFIX = "reaction.";
8026
+ function listReactionScripts() {
8027
+ return listScripts().filter((n)=>n.startsWith(REACTION_SCRIPT_PREFIX));
8028
+ }
8029
+ function isReactionScript(name) {
8030
+ return name.startsWith(REACTION_SCRIPT_PREFIX) && state.scripts.has(name);
8031
+ }
8032
+ /** Test-only helper. */ function __resetScriptRegistry() {
8033
+ state.scripts.clear();
8034
+ }
8035
+
8036
+
8037
+ /***/ }),
8038
+
8039
+ /***/ 81753:
8040
+ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
8041
+
8042
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
8043
+ /* harmony export */ Fp: () => (/* binding */ describeToolSecrets),
8044
+ /* harmony export */ eg: () => (/* binding */ deleteToolSecret),
8045
+ /* harmony export */ pq: () => (/* binding */ getToolSecret),
8046
+ /* harmony export */ zR: () => (/* binding */ setToolSecret)
8047
+ /* harmony export */ });
8048
+ /* unused harmony export purgeToolSecrets */
8049
+ /* harmony import */ var _lib_stores_memory__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(80956);
8050
+ // Encrypted-at-rest secrets for external tools (ADR-0023).
8051
+ //
8052
+ // External tools declare secret "slots" in their module.exports:
8053
+ // secrets: [{ key: "api_key", label: "OpenWeather API key", required: true }]
8054
+ //
8055
+ // Values are persisted in the `tool-secrets` namespace of the memory store
8056
+ // (which is flagged sensitive in lib/crypto/sensitive.ts, so envelope
8057
+ // encryption applies — same primitive as the integrations store).
8058
+ //
8059
+ // Keys are stored as `<toolName>:<slotKey>`. Tool names and slot keys are
8060
+ // validated to a conservative character set so they can't collide with the
8061
+ // namespace delimiter or smuggle SQL/JSON metacharacters.
8062
+ //
8063
+ // Note on isolation: external tools run in-process with full Node privileges
8064
+ // (ADR-0013), so a malicious tool could read another tool's secrets by going
8065
+ // around this API (e.g. directly opening the SQLite file). The per-tool
8066
+ // scoping here is a *convention* surfaced through `ctx.getSecret`, not a
8067
+ // sandbox boundary.
8068
+
8069
+ const NAMESPACE = "tool-secrets";
8070
+ const NAME_RE = /^[a-z0-9_-]+$/i;
8071
+ function validName(s) {
8072
+ return typeof s === "string" && s.length > 0 && s.length <= 64 && NAME_RE.test(s);
8073
+ }
8074
+ function compoundKey(toolName, key) {
8075
+ return `${toolName}:${key}`;
8076
+ }
8077
+ function parseStored(raw) {
8078
+ try {
8079
+ const v = JSON.parse(raw);
8080
+ return typeof v === "string" ? v : null;
8081
+ } catch {
8082
+ return null;
8083
+ }
8084
+ }
8085
+ function getToolSecret(toolName, key) {
8086
+ if (!validName(toolName) || !validName(key)) return null;
8087
+ const row = (0,_lib_stores_memory__WEBPACK_IMPORTED_MODULE_0__/* .getMemory */ .So)(NAMESPACE, compoundKey(toolName, key));
8088
+ if (!row) return null;
8089
+ return parseStored(row.value);
8090
+ }
8091
+ function setToolSecret(toolName, key, value) {
8092
+ if (!validName(toolName)) throw new Error(`invalid tool name: ${toolName}`);
8093
+ if (!validName(key)) throw new Error(`invalid secret key: ${key}`);
8094
+ if (typeof value !== "string") throw new Error("secret value must be a string");
8095
+ (0,_lib_stores_memory__WEBPACK_IMPORTED_MODULE_0__/* .putMemory */ .Ri)(NAMESPACE, compoundKey(toolName, key), value);
8096
+ }
8097
+ function deleteToolSecret(toolName, key) {
8098
+ if (!validName(toolName) || !validName(key)) return false;
8099
+ return (0,_lib_stores_memory__WEBPACK_IMPORTED_MODULE_0__/* .deleteMemory */ .h0)(NAMESPACE, compoundKey(toolName, key));
8100
+ }
8101
+ // Returns which declared slots have a value persisted. Never returns the
8102
+ // plaintext — that only escapes via `ctx.getSecret` inside the tool's own
8103
+ // run loop.
8104
+ function describeToolSecrets(toolName, slots) {
8105
+ if (!validName(toolName)) return slots.map((s)=>({
8106
+ ...s,
8107
+ is_set: false
8108
+ }));
8109
+ return slots.map((s)=>({
8110
+ ...s,
8111
+ is_set: getToolSecret(toolName, s.key) !== null
8112
+ }));
8113
+ }
8114
+ // Delete every persisted secret for a tool — used when an external tool
8115
+ // file is removed and the operator wants to clear stale credentials.
8116
+ function purgeToolSecrets(toolName) {
8117
+ if (!validName(toolName)) return 0;
8118
+ const prefix = `${toolName}:`;
8119
+ const rows = listMemory(NAMESPACE, undefined, 1000).filter((r)=>r.key.startsWith(prefix));
8120
+ let n = 0;
8121
+ for (const r of rows){
8122
+ if (deleteMemory(NAMESPACE, r.key)) n++;
8123
+ }
8124
+ return n;
8125
+ }
8126
+
8127
+
8128
+ /***/ }),
8129
+
8130
+ /***/ 83042:
8131
+ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
8132
+
8133
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
8134
+ /* harmony export */ Iz: () => (/* binding */ listThreadsByAgent),
8135
+ /* harmony export */ KX: () => (/* binding */ deleteThread),
8136
+ /* harmony export */ Mi: () => (/* binding */ listThreads),
8137
+ /* harmony export */ Nn: () => (/* binding */ getMessagesAfter),
8138
+ /* harmony export */ Nw: () => (/* binding */ createThread),
8139
+ /* harmony export */ S0: () => (/* binding */ getRecentMessagesWindow),
8140
+ /* harmony export */ VL: () => (/* binding */ getMessages),
8141
+ /* harmony export */ az: () => (/* binding */ clearThreadMessages),
8142
+ /* harmony export */ fG: () => (/* binding */ getThread),
8143
+ /* harmony export */ mI: () => (/* binding */ touchThread),
8144
+ /* harmony export */ qI: () => (/* binding */ getMessagesPage),
8145
+ /* harmony export */ tj: () => (/* binding */ addMessage),
8146
+ /* harmony export */ xH: () => (/* binding */ getOrCreateAgentThread)
8147
+ /* harmony export */ });
8148
+ /* harmony import */ var node_crypto__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(77598);
8149
+ /* harmony import */ var node_crypto__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(node_crypto__WEBPACK_IMPORTED_MODULE_0__);
8150
+ /* harmony import */ var _lib_db__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(46);
8151
+ /* harmony import */ var _lib_embeddings__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(21839);
8152
+
8153
+
8154
+
8155
+ const now = ()=>new Date().toISOString();
8156
+ // Explicit column list for message reads — omits `embedding` (~20KB of
8157
+ // JSON-encoded float[] per row) which only the embeddings module reads.
8158
+ // Avoids dragging it through the chat-history result set on every call.
8159
+ const MSG_COLS_SQL = "SELECT msg_id, thread_id, role, content, created_at, tool_events, category FROM messages";
8160
+ function listThreads(limit = 50, offset = 0) {
8161
+ return (0,_lib_db__WEBPACK_IMPORTED_MODULE_1__/* .getDb */ .Lf)().prepare("SELECT * FROM threads ORDER BY updated_at DESC LIMIT ? OFFSET ?").all(limit, offset);
8162
+ }
8163
+ function listThreadsByAgent(agent_id, limit = 50) {
8164
+ return (0,_lib_db__WEBPACK_IMPORTED_MODULE_1__/* .getDb */ .Lf)().prepare("SELECT * FROM threads WHERE agent_id=? ORDER BY updated_at DESC LIMIT ?").all(agent_id, limit);
8165
+ }
8166
+ function getThread(thread_id) {
8167
+ return (0,_lib_db__WEBPACK_IMPORTED_MODULE_1__/* .getDb */ .Lf)().prepare("SELECT * FROM threads WHERE thread_id=?").get(thread_id) ?? null;
8168
+ }
8169
+ function createThread(agent_id, title) {
8170
+ const t = now();
8171
+ const thread_id = (0,node_crypto__WEBPACK_IMPORTED_MODULE_0__.randomUUID)();
8172
+ (0,_lib_db__WEBPACK_IMPORTED_MODULE_1__/* .getDb */ .Lf)().prepare("INSERT INTO threads (thread_id,agent_id,title,created_at,updated_at,message_count) VALUES (?,?,?,?,?,0)").run(thread_id, agent_id, title ?? null, t, t);
8173
+ return {
8174
+ thread_id,
8175
+ agent_id,
8176
+ title: title ?? null,
8177
+ created_at: t,
8178
+ updated_at: t,
8179
+ message_count: 0
8180
+ };
8181
+ }
8182
+ function deleteThread(thread_id) {
8183
+ const db = (0,_lib_db__WEBPACK_IMPORTED_MODULE_1__/* .getDb */ .Lf)();
8184
+ db.prepare("DELETE FROM messages WHERE thread_id=?").run(thread_id);
8185
+ const r = db.prepare("DELETE FROM threads WHERE thread_id=?").run(thread_id);
8186
+ return r.changes > 0;
8187
+ }
8188
+ function getMessages(thread_id) {
8189
+ return (0,_lib_db__WEBPACK_IMPORTED_MODULE_1__/* .getDb */ .Lf)().prepare(MSG_COLS_SQL + " WHERE thread_id=? ORDER BY created_at ASC").all(thread_id);
8190
+ }
8191
+ // Pull the latest N messages within a time window. Used to build the LLM
8192
+ // context — keeps prompt size bounded as threads grow indefinitely.
8193
+ // limit: 0 or negative = unlimited
8194
+ // sinceISO: undefined = no time bound
8195
+ // Returns chronological order (oldest first) so it can be appended to the prompt directly.
8196
+ function getRecentMessagesWindow(thread_id, limit, sinceISO) {
8197
+ const db = (0,_lib_db__WEBPACK_IMPORTED_MODULE_1__/* .getDb */ .Lf)();
8198
+ const params = [
8199
+ thread_id
8200
+ ];
8201
+ let sql = MSG_COLS_SQL + " WHERE thread_id=?";
8202
+ if (sinceISO) {
8203
+ sql += " AND created_at >= ?";
8204
+ params.push(sinceISO);
8205
+ }
8206
+ sql += " ORDER BY created_at DESC";
8207
+ if (limit > 0) {
8208
+ sql += " LIMIT ?";
8209
+ params.push(limit);
8210
+ }
8211
+ const rows = db.prepare(sql).all(...params);
8212
+ return rows.reverse();
8213
+ }
8214
+ // Forward-fetch — return messages strictly newer than `afterISO`, oldest
8215
+ // first, capped at `limit`. Used by the chat view to pull only the
8216
+ // freshly-persisted user+assistant pair after a run completes, instead of
8217
+ // re-fetching the whole most-recent page.
8218
+ function getMessagesAfter(thread_id, afterISO, limit = 50) {
8219
+ return (0,_lib_db__WEBPACK_IMPORTED_MODULE_1__/* .getDb */ .Lf)().prepare(MSG_COLS_SQL + " WHERE thread_id=? AND created_at > ? ORDER BY created_at ASC LIMIT ?").all(thread_id, afterISO, limit);
8220
+ }
8221
+ // Pagination for the chat UI. Returns the latest N messages strictly older
8222
+ // than `beforeISO` (cursor). Caller passes the oldest already-loaded message's
8223
+ // created_at as the cursor; first page omits beforeISO.
8224
+ function getMessagesPage(thread_id, limit, beforeISO) {
8225
+ const db = (0,_lib_db__WEBPACK_IMPORTED_MODULE_1__/* .getDb */ .Lf)();
8226
+ const params = [
8227
+ thread_id
8228
+ ];
8229
+ let sql = MSG_COLS_SQL + " WHERE thread_id=?";
8230
+ if (beforeISO) {
8231
+ sql += " AND created_at < ?";
8232
+ params.push(beforeISO);
8233
+ }
8234
+ sql += " ORDER BY created_at DESC LIMIT ?";
8235
+ params.push(limit + 1); // fetch one extra to detect if there's more
8236
+ const rows = db.prepare(sql).all(...params);
8237
+ const has_more = rows.length > limit;
8238
+ return {
8239
+ messages: rows.slice(0, limit).reverse(),
8240
+ has_more
8241
+ };
8242
+ }
8243
+ function addMessage(thread_id, role, content, toolEvents, category = null) {
8244
+ const msg_id = (0,node_crypto__WEBPACK_IMPORTED_MODULE_0__.randomUUID)();
8245
+ const t = now();
8246
+ const db = (0,_lib_db__WEBPACK_IMPORTED_MODULE_1__/* .getDb */ .Lf)();
8247
+ const toolEventsJson = toolEvents && toolEvents.length > 0 ? JSON.stringify(toolEvents) : null;
8248
+ db.prepare("INSERT INTO messages (msg_id,thread_id,role,content,created_at,tool_events,category) VALUES (?,?,?,?,?,?,?)").run(msg_id, thread_id, role, content, t, toolEventsJson, category);
8249
+ db.prepare("UPDATE threads SET message_count=message_count+1 WHERE thread_id=?").run(thread_id);
8250
+ // Best-effort: embed the message so semantic recall can pull it back later.
8251
+ // Skip empty / very short content (greetings have no useful signal).
8252
+ if (content.trim().length >= 12) {
8253
+ (0,_lib_embeddings__WEBPACK_IMPORTED_MODULE_2__/* .embedOne */ ._L)(content).then((vec)=>{
8254
+ if (vec) {
8255
+ (0,_lib_db__WEBPACK_IMPORTED_MODULE_1__/* .getDb */ .Lf)().prepare("UPDATE messages SET embedding=? WHERE msg_id=?").run(JSON.stringify(vec), msg_id);
8256
+ }
8257
+ }).catch(()=>{});
8258
+ }
8259
+ return {
8260
+ msg_id,
8261
+ thread_id,
8262
+ role,
8263
+ content,
8264
+ created_at: t,
8265
+ tool_events: toolEventsJson,
8266
+ category
8267
+ };
8268
+ }
8269
+ function getOrCreateAgentThread(agentId) {
8270
+ const existing = (0,_lib_db__WEBPACK_IMPORTED_MODULE_1__/* .getDb */ .Lf)().prepare("SELECT * FROM threads WHERE agent_id=? LIMIT 1").get(agentId);
8271
+ if (existing) return existing;
8272
+ return createThread(agentId);
8273
+ }
8274
+ function clearThreadMessages(threadId) {
8275
+ const db = (0,_lib_db__WEBPACK_IMPORTED_MODULE_1__/* .getDb */ .Lf)();
8276
+ db.prepare("DELETE FROM messages WHERE thread_id=?").run(threadId);
8277
+ db.prepare("UPDATE threads SET message_count=0, updated_at=? WHERE thread_id=?").run(new Date().toISOString(), threadId);
8278
+ }
8279
+ function touchThread(thread_id, firstMsg) {
8280
+ const t = now();
8281
+ (0,_lib_db__WEBPACK_IMPORTED_MODULE_1__/* .getDb */ .Lf)().prepare("UPDATE threads SET updated_at=?, title=COALESCE(title,?) WHERE thread_id=?").run(t, firstMsg ? firstMsg.slice(0, 80) : null, thread_id);
8282
+ }
8283
+
8284
+
8032
8285
  /***/ }),
8033
8286
 
8034
8287
  /***/ 93348: