@circuitwall/jarela 0.9.3 → 0.10.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.
Files changed (94) hide show
  1. package/.next/standalone/.next/BUILD_ID +1 -1
  2. package/.next/standalone/.next/app-path-routes-manifest.json +2 -2
  3. package/.next/standalone/.next/build-manifest.json +2 -2
  4. package/.next/standalone/.next/prerender-manifest.json +3 -3
  5. package/.next/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  6. package/.next/standalone/.next/server/app/_global-error.html +1 -1
  7. package/.next/standalone/.next/server/app/_global-error.rsc +1 -1
  8. package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  9. package/.next/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  10. package/.next/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  11. package/.next/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  12. package/.next/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  13. package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  14. package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  15. package/.next/standalone/.next/server/app/_not-found.html +2 -2
  16. package/.next/standalone/.next/server/app/_not-found.rsc +2 -2
  17. package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
  18. package/.next/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  19. package/.next/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
  20. package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  21. package/.next/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  22. package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  23. package/.next/standalone/.next/server/app/api/v1/dashboard/metrics/route.js +72 -5
  24. package/.next/standalone/.next/server/app/api/v1/dashboard/metrics/route.js.map +1 -1
  25. package/.next/standalone/.next/server/app/api/v1/extensions/route.js +2 -2
  26. package/.next/standalone/.next/server/app/api/v1/extensions/tools/[name]/secrets/route.js +2 -2
  27. package/.next/standalone/.next/server/app/api/v1/threads/[thread_id]/run/route.js +136 -26
  28. package/.next/standalone/.next/server/app/api/v1/threads/[thread_id]/run/route.js.map +1 -1
  29. package/.next/standalone/.next/server/app/api/v1/tools/route.js +2 -2
  30. package/.next/standalone/.next/server/app/index.html +2 -2
  31. package/.next/standalone/.next/server/app/index.rsc +3 -3
  32. package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  33. package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +3 -3
  34. package/.next/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  35. package/.next/standalone/.next/server/app/index.segments/_index.segment.rsc +2 -2
  36. package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  37. package/.next/standalone/.next/server/app/page.js +266 -40
  38. package/.next/standalone/.next/server/app/page.js.map +1 -1
  39. package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  40. package/.next/standalone/.next/server/app/setup/page_client-reference-manifest.js +1 -1
  41. package/.next/standalone/.next/server/app/setup.html +1 -1
  42. package/.next/standalone/.next/server/app/setup.rsc +2 -2
  43. package/.next/standalone/.next/server/app/setup.segments/_full.segment.rsc +2 -2
  44. package/.next/standalone/.next/server/app/setup.segments/_head.segment.rsc +1 -1
  45. package/.next/standalone/.next/server/app/setup.segments/_index.segment.rsc +2 -2
  46. package/.next/standalone/.next/server/app/setup.segments/_tree.segment.rsc +2 -2
  47. package/.next/standalone/.next/server/app/setup.segments/setup/__PAGE__.segment.rsc +1 -1
  48. package/.next/standalone/.next/server/app/setup.segments/setup.segment.rsc +1 -1
  49. package/.next/standalone/.next/server/app-paths-manifest.json +2 -2
  50. package/.next/standalone/.next/server/chunks/210.js +1 -1
  51. package/.next/standalone/.next/server/chunks/2151.js +60 -2
  52. package/.next/standalone/.next/server/chunks/2151.js.map +1 -1
  53. package/.next/standalone/.next/server/chunks/614.js +336 -93
  54. package/.next/standalone/.next/server/chunks/614.js.map +1 -1
  55. package/.next/standalone/.next/server/chunks/6765.js +35 -0
  56. package/.next/standalone/.next/server/chunks/6765.js.map +1 -1
  57. package/.next/standalone/.next/server/chunks/8697.js +15246 -15002
  58. package/.next/standalone/.next/server/chunks/8697.js.map +1 -1
  59. package/.next/standalone/.next/server/middleware-build-manifest.js +2 -2
  60. package/.next/standalone/.next/server/pages/404.html +2 -2
  61. package/.next/standalone/.next/server/pages/500.html +1 -1
  62. package/.next/standalone/.next/server/server-reference-manifest.json +1 -1
  63. package/.next/standalone/.next/static/chunks/{3741-344e2bfc5028b9c8.js → 3741-2d64471ff763b8fa.js} +36 -1
  64. package/.next/standalone/.next/static/chunks/3741-2d64471ff763b8fa.js.map +1 -0
  65. package/.next/standalone/.next/static/chunks/app/{page-c77ab600642bbfc2.js → page-318743bf47fac345.js} +267 -41
  66. package/.next/standalone/.next/static/chunks/app/page-318743bf47fac345.js.map +1 -0
  67. package/.next/standalone/.next/static/css/b6b85b0f13bc0e98.css +5 -0
  68. package/.next/standalone/.next/static/css/b6b85b0f13bc0e98.css.map +1 -0
  69. package/.next/standalone/package.json +1 -1
  70. package/CHANGELOG.md +48 -0
  71. package/README.md +2 -0
  72. package/api/client.ts +37 -1
  73. package/api/types.ts +18 -0
  74. package/app/api/v1/threads/[thread_id]/run/route.ts +69 -22
  75. package/components/agents/AgentEditor.tsx +7 -4
  76. package/components/chat/MessageBubble.tsx +108 -1
  77. package/components/dashboard/DashboardPanel.tsx +79 -21
  78. package/hooks/useSSE.ts +22 -9
  79. package/lib/agents/prepare/system-prompt.ts +30 -0
  80. package/lib/agents/run-registry.test.ts +94 -0
  81. package/lib/agents/run-registry.ts +60 -1
  82. package/lib/stores/dashboard-metrics.test.ts +33 -0
  83. package/lib/stores/dashboard-metrics.ts +93 -1
  84. package/lib/tools/exec.ts +9 -5
  85. package/lib/tools/files.ts +6 -0
  86. package/lib/tools/safety.test.ts +95 -0
  87. package/lib/tools/safety.ts +147 -0
  88. package/package.json +1 -1
  89. package/.next/standalone/.next/static/chunks/3741-344e2bfc5028b9c8.js.map +0 -1
  90. package/.next/standalone/.next/static/chunks/app/page-c77ab600642bbfc2.js.map +0 -1
  91. package/.next/standalone/.next/static/css/53f85613a5500253.css +0 -5
  92. package/.next/standalone/.next/static/css/53f85613a5500253.css.map +0 -1
  93. /package/.next/standalone/.next/static/{6uLoytvvEtLKIblEB53e0 → 8qTBpUDFnSMYwe3Zc0bGV}/_buildManifest.js +0 -0
  94. /package/.next/standalone/.next/static/{6uLoytvvEtLKIblEB53e0 → 8qTBpUDFnSMYwe3Zc0bGV}/_ssgManifest.js +0 -0
@@ -1562,95 +1562,7 @@ function loadExternalTools(builtinNames) {
1562
1562
 
1563
1563
  /***/ }),
1564
1564
 
1565
- /***/ 30685:
1566
- /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
1567
-
1568
- /* harmony export */ __webpack_require__.d(__webpack_exports__, {
1569
- /* harmony export */ V: () => (/* binding */ collectStream)
1570
- /* harmony export */ });
1571
- // Shared collector for agent stream consumers.
1572
- //
1573
- // Previously three near-identical drain loops lived in:
1574
- // - app/api/v1/threads/[thread_id]/run/route.ts (HTTP run)
1575
- // - lib/scheduler/index.ts (cron tasks)
1576
- // - lib/bridges/dispatcher.ts (WhatsApp / bridges)
1577
- //
1578
- // They all read `prepared.stream`, accumulate `text_delta` into a single
1579
- // assistant string, record `tool_call`/`tool_result` events, stop on
1580
- // `done`/`error`, and surface the terminal state. This helper unifies that.
1581
- /** Drain an async stream of `StreamChunk` into a single `CollectedRun`. */ async function collectStream(stream, opts = {}) {
1582
- const result = {
1583
- assistantContent: "",
1584
- usedTools: [],
1585
- toolEvents: [],
1586
- terminal: "done"
1587
- };
1588
- try {
1589
- for await (const chunk of stream){
1590
- opts.onChunk?.(chunk);
1591
- switch(chunk.type){
1592
- case "text_delta":
1593
- {
1594
- result.assistantContent += chunk.data.delta ?? "";
1595
- break;
1596
- }
1597
- case "tool_call":
1598
- {
1599
- const d = chunk.data;
1600
- if (d.name) result.usedTools.push(d.name);
1601
- result.toolEvents.push({
1602
- id: d.id ?? `call-${result.toolEvents.length}`,
1603
- phase: "call",
1604
- name: d.name ?? "",
1605
- payload: d.arguments
1606
- });
1607
- break;
1608
- }
1609
- case "tool_result":
1610
- {
1611
- const d = chunk.data;
1612
- result.toolEvents.push({
1613
- id: d.id ?? `result-${result.toolEvents.length}`,
1614
- phase: "result",
1615
- name: d.name ?? "",
1616
- payload: d.result
1617
- });
1618
- break;
1619
- }
1620
- case "error":
1621
- {
1622
- result.terminal = "error";
1623
- const msg = chunk.data?.message;
1624
- if (typeof msg === "string") result.errorMessage = msg;
1625
- return result;
1626
- }
1627
- case "done":
1628
- {
1629
- const d = chunk.data;
1630
- if (d?.usage && d.provider && d.model_id && d.usage.source === "provider") {
1631
- result.usage = {
1632
- input_tokens: d.usage.input_tokens ?? 0,
1633
- output_tokens: d.usage.output_tokens ?? 0,
1634
- provider: d.provider,
1635
- model_id: d.model_id,
1636
- model_config_name: d.model_config_name ?? null
1637
- };
1638
- }
1639
- return result;
1640
- }
1641
- }
1642
- }
1643
- } catch (err) {
1644
- result.terminal = "error";
1645
- result.errorMessage = err instanceof Error ? err.message : String(err);
1646
- }
1647
- return result;
1648
- }
1649
-
1650
-
1651
- /***/ }),
1652
-
1653
- /***/ 33934:
1565
+ /***/ 23696:
1654
1566
  /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
1655
1567
 
1656
1568
 
@@ -2037,12 +1949,211 @@ const documentsIndexUrl = (0,tools/* tool */.z6)(async ({ input })=>{
2037
1949
  var external_node_child_process_ = __webpack_require__(31421);
2038
1950
  // EXTERNAL MODULE: ./lib/env/allowlist.ts
2039
1951
  var allowlist = __webpack_require__(23593);
1952
+ ;// ./lib/tools/safety.ts
1953
+ // Safety mode for destructive built-in tools (exec + filesystem writes).
1954
+ //
1955
+ // Resolved once per call from `JARELA_TOOL_SAFETY`. Three tiers:
1956
+ //
1957
+ // "safe" — read-only. Exec accepts only an allowlisted set of
1958
+ // inspection commands (ls, git status, …); filesystem
1959
+ // tools refuse every write, edit, move, copy, delete,
1960
+ // or mkdir. Per-call `allow_unsafe` is IGNORED.
1961
+ // "mostly_safe" — default. Exec blocks the obviously-dangerous pattern
1962
+ // list (rm -rf /, shutdown, fork bomb, …); filesystem
1963
+ // tools refuse credential paths and the Jarela data dir.
1964
+ // Per-call `allow_unsafe=true` lifts the exec block for
1965
+ // that single call.
1966
+ // "bypass" — every guard off. For local development on a machine
1967
+ // you control and trust completely. NOT for use behind
1968
+ // a tunnel or with untrusted prompt sources.
1969
+ //
1970
+ // The mode is process-wide so prompt injection cannot escalate by
1971
+ // passing arguments — the LLM can only ever *downgrade* (via
1972
+ // `allow_unsafe=false` semantics, which is just "don't try to bypass").
1973
+ function resolveSafetyMode() {
1974
+ const raw = (process.env.JARELA_TOOL_SAFETY ?? "").trim().toLowerCase();
1975
+ if (raw === "safe") return "safe";
1976
+ if (raw === "bypass" || raw === "unsafe") return "bypass";
1977
+ return "mostly_safe";
1978
+ }
1979
+ // Inspection-only commands allowed in `safe` mode. Matched as the FIRST
1980
+ // token (after stripping leading whitespace) — pipelines, redirections,
1981
+ // command substitution, &&, ;, etc. are all rejected because we cannot
1982
+ // reason about what the right-hand side will do.
1983
+ const SAFE_EXEC_ALLOWLIST = new Set([
1984
+ "ls",
1985
+ "dir",
1986
+ "pwd",
1987
+ "cd",
1988
+ "echo",
1989
+ "cat",
1990
+ "type",
1991
+ "head",
1992
+ "tail",
1993
+ "wc",
1994
+ "stat",
1995
+ "file",
1996
+ "which",
1997
+ "where",
1998
+ "whoami",
1999
+ "hostname",
2000
+ "date",
2001
+ "uname",
2002
+ "df",
2003
+ "du",
2004
+ "ps",
2005
+ "env",
2006
+ "printenv",
2007
+ "git",
2008
+ "node",
2009
+ "npm",
2010
+ "npx",
2011
+ "deno",
2012
+ "python",
2013
+ "python3",
2014
+ "pip",
2015
+ "pip3"
2016
+ ]);
2017
+ // Subcommands considered read-only for tools that take a verb. We only
2018
+ // need to enumerate the dangerous tools here — anything not listed falls
2019
+ // back to "the whole tool is read-only" (e.g. `cat`, `ls`).
2020
+ const SAFE_SUBCOMMANDS = {
2021
+ git: new Set([
2022
+ "status",
2023
+ "log",
2024
+ "diff",
2025
+ "show",
2026
+ "blame",
2027
+ "branch",
2028
+ "tag",
2029
+ "remote",
2030
+ "ls-files",
2031
+ "ls-tree",
2032
+ "config",
2033
+ "rev-parse",
2034
+ "describe",
2035
+ "shortlog",
2036
+ "reflog"
2037
+ ]),
2038
+ npm: new Set([
2039
+ "ls",
2040
+ "list",
2041
+ "view",
2042
+ "info",
2043
+ "outdated",
2044
+ "config",
2045
+ "whoami",
2046
+ "ping",
2047
+ "doctor"
2048
+ ]),
2049
+ npx: new Set([]),
2050
+ node: new Set([]),
2051
+ python: new Set([]),
2052
+ python3: new Set([]),
2053
+ deno: new Set([
2054
+ "info",
2055
+ "doc"
2056
+ ]),
2057
+ pip: new Set([
2058
+ "list",
2059
+ "show",
2060
+ "freeze",
2061
+ "config"
2062
+ ]),
2063
+ pip3: new Set([
2064
+ "list",
2065
+ "show",
2066
+ "freeze",
2067
+ "config"
2068
+ ])
2069
+ };
2070
+ // Shell metacharacters that compose commands or redirect IO. Their
2071
+ // presence in `safe` mode is grounds for rejection because the
2072
+ // allowlist check only inspects the first token.
2073
+ const COMPOSER_RE = /[|&;`$<>]|\$\(|\|\||&&/;
2074
+ function checkExecAllowed(command, opts) {
2075
+ if (opts.mode === "bypass") return {
2076
+ allowed: true
2077
+ };
2078
+ if (opts.mode === "mostly_safe") {
2079
+ if (opts.blockedByPattern && !opts.allowUnsafe) {
2080
+ return {
2081
+ allowed: false,
2082
+ reason: "Command blocked by safety policy (mode=mostly_safe). Pass allow_unsafe=true only when you fully trust the command."
2083
+ };
2084
+ }
2085
+ return {
2086
+ allowed: true
2087
+ };
2088
+ }
2089
+ // safe mode
2090
+ const trimmed = command.trim();
2091
+ if (!trimmed) return {
2092
+ allowed: false,
2093
+ reason: "command is required"
2094
+ };
2095
+ if (COMPOSER_RE.test(trimmed)) {
2096
+ return {
2097
+ allowed: false,
2098
+ reason: "safe mode rejects pipelines, redirection, command substitution, &&, and ;. " + "Set JARELA_TOOL_SAFETY=mostly_safe (or bypass) to allow composite commands."
2099
+ };
2100
+ }
2101
+ const tokens = trimmed.split(/\s+/);
2102
+ const head = tokens[0]?.toLowerCase();
2103
+ if (!head || !SAFE_EXEC_ALLOWLIST.has(head)) {
2104
+ return {
2105
+ allowed: false,
2106
+ reason: `safe mode allows only inspection commands (${[
2107
+ ...SAFE_EXEC_ALLOWLIST
2108
+ ].sort().join(", ")}). ` + "Set JARELA_TOOL_SAFETY=mostly_safe to enable the broader policy."
2109
+ };
2110
+ }
2111
+ const subAllowlist = SAFE_SUBCOMMANDS[head];
2112
+ if (subAllowlist) {
2113
+ const sub = tokens[1]?.toLowerCase().replace(/^--?/, "");
2114
+ // Allow bare invocations that are themselves read-only (e.g. `git`
2115
+ // alone prints help). Reject if the subcommand is missing for tools
2116
+ // that need one to be safe (node/python/npx → arbitrary code).
2117
+ if (subAllowlist.size === 0) {
2118
+ return {
2119
+ allowed: false,
2120
+ reason: `safe mode refuses '${head}' because it can execute arbitrary code. Use mostly_safe or bypass.`
2121
+ };
2122
+ }
2123
+ if (sub && !subAllowlist.has(sub)) {
2124
+ return {
2125
+ allowed: false,
2126
+ reason: `safe mode allows '${head}' only for: ${[
2127
+ ...subAllowlist
2128
+ ].sort().join(", ")}. ` + "Use mostly_safe or bypass for other subcommands."
2129
+ };
2130
+ }
2131
+ }
2132
+ return {
2133
+ allowed: true
2134
+ };
2135
+ }
2136
+ function checkFsAllowed(op, opts) {
2137
+ if (opts.mode === "bypass" || opts.mode === "mostly_safe") return {
2138
+ allowed: true
2139
+ };
2140
+ // safe mode: reads are fine, writes are not.
2141
+ if (op === "read") return {
2142
+ allowed: true
2143
+ };
2144
+ return {
2145
+ allowed: false,
2146
+ reason: "safe mode refuses filesystem mutations (write/edit/move/copy/delete/mkdir). " + "Set JARELA_TOOL_SAFETY=mostly_safe to enable writes outside credential dirs."
2147
+ };
2148
+ }
2149
+
2040
2150
  ;// ./lib/tools/exec.ts
2041
2151
 
2042
2152
 
2043
2153
 
2044
2154
 
2045
2155
 
2156
+
2046
2157
  const MAX_OUTPUT_BYTES = 8000;
2047
2158
  const DEFAULT_TIMEOUT_MS = 10000;
2048
2159
  const MAX_TIMEOUT_MS = 60000;
@@ -2074,10 +2185,17 @@ function runLocalCommand(command, options) {
2074
2185
  });
2075
2186
  }
2076
2187
  const timeout = Math.min(options.timeout_ms ?? DEFAULT_TIMEOUT_MS, MAX_TIMEOUT_MS);
2077
- if (!options.allow_unsafe && isBlockedCommand(command)) {
2188
+ const mode = resolveSafetyMode();
2189
+ const gate = checkExecAllowed(command, {
2190
+ mode,
2191
+ allowUnsafe: options.allow_unsafe,
2192
+ blockedByPattern: isBlockedCommand(command)
2193
+ });
2194
+ if (!gate.allowed) {
2078
2195
  return JSON.stringify({
2079
2196
  exit_code: 126,
2080
- stderr: "Command blocked by safety policy. Pass allow_unsafe=true only when you fully trust the command."
2197
+ stderr: gate.reason,
2198
+ safety_mode: mode
2081
2199
  });
2082
2200
  }
2083
2201
  const cwd = options.cwd?.trim() ? options.cwd : process.cwd();
@@ -2169,6 +2287,7 @@ var external_node_os_default = /*#__PURE__*/__webpack_require__.n(external_node_
2169
2287
 
2170
2288
 
2171
2289
 
2290
+
2172
2291
  // Dedicated file tools. Agents previously had to drive every edit through
2173
2292
  // `local_exec` / `shell_exec`, which works for "create a new file with this
2174
2293
  // content" (echo / Set-Content) but is hostile to in-place edits: quoting
@@ -2250,6 +2369,13 @@ function jarelaDataDir() {
2250
2369
  return process.env.JARELA_DB_DIR ? external_node_path_default().resolve(process.env.JARELA_DB_DIR) : external_node_path_default().join(external_node_os_default().homedir(), ".jarela");
2251
2370
  }
2252
2371
  function assertSafePath(abs, op) {
2372
+ const mode = resolveSafetyMode();
2373
+ const gate = checkFsAllowed(op, {
2374
+ mode
2375
+ });
2376
+ if (!gate.allowed) throw new Error(gate.reason);
2377
+ // bypass mode disables every guard, including the credential denylist.
2378
+ if (mode === "bypass") return;
2253
2379
  if (process.env.JARELA_ALLOW_SENSITIVE_FILES === "1") return;
2254
2380
  for (const base of sensitiveBase()){
2255
2381
  if (isInside(abs, base)) {
@@ -5056,6 +5182,94 @@ function initTools() {
5056
5182
  }
5057
5183
 
5058
5184
 
5185
+ /***/ }),
5186
+
5187
+ /***/ 30685:
5188
+ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
5189
+
5190
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
5191
+ /* harmony export */ V: () => (/* binding */ collectStream)
5192
+ /* harmony export */ });
5193
+ // Shared collector for agent stream consumers.
5194
+ //
5195
+ // Previously three near-identical drain loops lived in:
5196
+ // - app/api/v1/threads/[thread_id]/run/route.ts (HTTP run)
5197
+ // - lib/scheduler/index.ts (cron tasks)
5198
+ // - lib/bridges/dispatcher.ts (WhatsApp / bridges)
5199
+ //
5200
+ // They all read `prepared.stream`, accumulate `text_delta` into a single
5201
+ // assistant string, record `tool_call`/`tool_result` events, stop on
5202
+ // `done`/`error`, and surface the terminal state. This helper unifies that.
5203
+ /** Drain an async stream of `StreamChunk` into a single `CollectedRun`. */ async function collectStream(stream, opts = {}) {
5204
+ const result = {
5205
+ assistantContent: "",
5206
+ usedTools: [],
5207
+ toolEvents: [],
5208
+ terminal: "done"
5209
+ };
5210
+ try {
5211
+ for await (const chunk of stream){
5212
+ opts.onChunk?.(chunk);
5213
+ switch(chunk.type){
5214
+ case "text_delta":
5215
+ {
5216
+ result.assistantContent += chunk.data.delta ?? "";
5217
+ break;
5218
+ }
5219
+ case "tool_call":
5220
+ {
5221
+ const d = chunk.data;
5222
+ if (d.name) result.usedTools.push(d.name);
5223
+ result.toolEvents.push({
5224
+ id: d.id ?? `call-${result.toolEvents.length}`,
5225
+ phase: "call",
5226
+ name: d.name ?? "",
5227
+ payload: d.arguments
5228
+ });
5229
+ break;
5230
+ }
5231
+ case "tool_result":
5232
+ {
5233
+ const d = chunk.data;
5234
+ result.toolEvents.push({
5235
+ id: d.id ?? `result-${result.toolEvents.length}`,
5236
+ phase: "result",
5237
+ name: d.name ?? "",
5238
+ payload: d.result
5239
+ });
5240
+ break;
5241
+ }
5242
+ case "error":
5243
+ {
5244
+ result.terminal = "error";
5245
+ const msg = chunk.data?.message;
5246
+ if (typeof msg === "string") result.errorMessage = msg;
5247
+ return result;
5248
+ }
5249
+ case "done":
5250
+ {
5251
+ const d = chunk.data;
5252
+ if (d?.usage && d.provider && d.model_id && d.usage.source === "provider") {
5253
+ result.usage = {
5254
+ input_tokens: d.usage.input_tokens ?? 0,
5255
+ output_tokens: d.usage.output_tokens ?? 0,
5256
+ provider: d.provider,
5257
+ model_id: d.model_id,
5258
+ model_config_name: d.model_config_name ?? null
5259
+ };
5260
+ }
5261
+ return result;
5262
+ }
5263
+ }
5264
+ }
5265
+ } catch (err) {
5266
+ result.terminal = "error";
5267
+ result.errorMessage = err instanceof Error ? err.message : String(err);
5268
+ }
5269
+ return result;
5270
+ }
5271
+
5272
+
5059
5273
  /***/ }),
5060
5274
 
5061
5275
  /***/ 38710:
@@ -5081,8 +5295,8 @@ var dist_messages = __webpack_require__(12016);
5081
5295
  var providers = __webpack_require__(68866);
5082
5296
  // EXTERNAL MODULE: ./lib/stores/model-config.ts
5083
5297
  var model_config = __webpack_require__(80937);
5084
- // EXTERNAL MODULE: ./lib/tools/index.ts + 18 modules
5085
- var lib_tools = __webpack_require__(33934);
5298
+ // EXTERNAL MODULE: ./lib/tools/index.ts + 19 modules
5299
+ var lib_tools = __webpack_require__(23696);
5086
5300
  // EXTERNAL MODULE: ./node_modules/@langchain/core/dist/language_models/chat_models.js + 1 modules
5087
5301
  var chat_models = __webpack_require__(87233);
5088
5302
  // EXTERNAL MODULE: ./node_modules/@langchain/core/dist/outputs.js
@@ -6551,6 +6765,8 @@ var external_node_os_default = /*#__PURE__*/__webpack_require__.n(external_node_
6551
6765
  var user_profile = __webpack_require__(67308);
6552
6766
  // EXTERNAL MODULE: ./lib/stores/integrations.ts
6553
6767
  var integrations = __webpack_require__(78228);
6768
+ // EXTERNAL MODULE: ./lib/stores/document-sources.ts
6769
+ var document_sources = __webpack_require__(91441);
6554
6770
  // EXTERNAL MODULE: ./lib/agents/adaptive-persona-presets.ts
6555
6771
  var adaptive_persona_presets = __webpack_require__(66849);
6556
6772
  ;// ./lib/agents/adaptive-persona.ts
@@ -6875,6 +7091,7 @@ var app_config = __webpack_require__(48954);
6875
7091
 
6876
7092
 
6877
7093
 
7094
+
6878
7095
  const APP_NAME = (0,app_config/* getAppName */.fj)();
6879
7096
  function buildSystemPrompt(ctx) {
6880
7097
  const { agentCfg, trimmedMessage, budget, recallCtx, warmSummaryCtx, factsCtx, experienceMode, delegateRosterLines } = ctx;
@@ -6894,6 +7111,7 @@ function buildSystemPrompt(ctx) {
6894
7111
  adaptivePersonaCtx,
6895
7112
  buildUserContext(),
6896
7113
  buildIntegrationsContext(),
7114
+ buildDocumentsContext(),
6897
7115
  harnessParts.capabilities,
6898
7116
  harnessParts.plan_first,
6899
7117
  harnessParts.presentation,
@@ -7001,6 +7219,31 @@ function buildDelegatesContext(lines) {
7001
7219
  ...lines
7002
7220
  ].join("\n");
7003
7221
  }
7222
+ // Surface indexed Documents so the model knows the RAG corpus exists.
7223
+ // Without this nudge agents almost never call `documents_search` — they have
7224
+ // no signal that any local content is searchable. Gated on actually-indexed
7225
+ // chunks (not just configured sources) so an empty/erroring source doesn't
7226
+ // produce false advertising.
7227
+ function buildDocumentsContext() {
7228
+ const sources = (0,document_sources/* listEnabledDocumentSources */.vU)();
7229
+ if (sources.length === 0) return "";
7230
+ let totalChunks = 0;
7231
+ const lines = [];
7232
+ for (const s of sources){
7233
+ const stats = (0,document_sources/* getDocumentSourceStats */.Io)(s.id);
7234
+ if (stats.chunk_count === 0) continue;
7235
+ totalChunks += stats.chunk_count;
7236
+ const label = s.label ?? s.path;
7237
+ lines.push(`- ${label} (${s.kind}, ${stats.chunk_count} chunks)`);
7238
+ }
7239
+ if (totalChunks === 0) return "";
7240
+ return [
7241
+ "--- Indexed documents ---",
7242
+ `The user has ${totalChunks} indexed chunks across ${lines.length} document source(s) available to you:`,
7243
+ ...lines,
7244
+ "Call `documents_search` whenever the user asks about local files, notes, project docs, or any content that sounds like it lives in one of these sources. Prefer it over guessing from training data. Use `documents_list_sources` to enumerate, and pass `source_id` to scope a search."
7245
+ ].join("\n");
7246
+ }
7004
7247
 
7005
7248
  // EXTERNAL MODULE: ./lib/providers/known-context-windows.ts
7006
7249
  var known_context_windows = __webpack_require__(45552);