@circuitwall/jarela 1.2.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) 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 +1 -1
  15. package/.next/standalone/.next/server/app/_not-found.rsc +1 -1
  16. package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  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 +1 -1
  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 +1 -1
  22. package/.next/standalone/.next/server/app/api/v1/bridges/[id]/chats/route.js +3 -3
  23. package/.next/standalone/.next/server/app/api/v1/bridges/[id]/chats/route.js.nft.json +1 -1
  24. package/.next/standalone/.next/server/app/api/v1/bridges/[id]/lookup/route.js +3 -3
  25. package/.next/standalone/.next/server/app/api/v1/bridges/[id]/lookup/route.js.nft.json +1 -1
  26. package/.next/standalone/.next/server/app/api/v1/bridges/[id]/pair/route.js +3 -3
  27. package/.next/standalone/.next/server/app/api/v1/bridges/[id]/pair/route.js.nft.json +1 -1
  28. package/.next/standalone/.next/server/app/api/v1/bridges/[id]/route.js +3 -3
  29. package/.next/standalone/.next/server/app/api/v1/bridges/[id]/route.js.nft.json +1 -1
  30. package/.next/standalone/.next/server/app/api/v1/bridges/[id]/status/route.js +3 -3
  31. package/.next/standalone/.next/server/app/api/v1/bridges/[id]/status/route.js.nft.json +1 -1
  32. package/.next/standalone/.next/server/app/api/v1/builtin-tools/route.js +218 -7
  33. package/.next/standalone/.next/server/app/api/v1/builtin-tools/route.js.map +1 -1
  34. package/.next/standalone/.next/server/app/api/v1/events/route.js +3 -3
  35. package/.next/standalone/.next/server/app/api/v1/events/route.js.nft.json +1 -1
  36. package/.next/standalone/.next/server/app/api/v1/extension/agents/route.js +8 -1
  37. package/.next/standalone/.next/server/app/api/v1/extension/agents/route.js.map +1 -1
  38. package/.next/standalone/.next/server/app/api/v1/extension/fill/route.js +8 -1
  39. package/.next/standalone/.next/server/app/api/v1/extension/fill/route.js.map +1 -1
  40. package/.next/standalone/.next/server/app/api/v1/extension/refine/route.js +8 -1
  41. package/.next/standalone/.next/server/app/api/v1/extension/refine/route.js.map +1 -1
  42. package/.next/standalone/.next/server/app/api/v1/extension/turn/route.js +8 -1
  43. package/.next/standalone/.next/server/app/api/v1/extension/turn/route.js.map +1 -1
  44. package/.next/standalone/.next/server/app/api/v1/extensions/route.js +2 -2
  45. package/.next/standalone/.next/server/app/api/v1/extensions/tools/[name]/secrets/route.js +2 -2
  46. package/.next/standalone/.next/server/app/api/v1/tools/route.js +2 -2
  47. package/.next/standalone/.next/server/app/page.js +0 -16
  48. package/.next/standalone/.next/server/app/page.js.map +1 -1
  49. package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  50. package/.next/standalone/.next/server/app/setup/page_client-reference-manifest.js +1 -1
  51. package/.next/standalone/.next/server/chunks/210.js +1 -1
  52. package/.next/standalone/.next/server/chunks/239.js +5335 -5230
  53. package/.next/standalone/.next/server/chunks/239.js.map +1 -1
  54. package/.next/standalone/.next/server/chunks/{1683.js → 241.js} +210 -36
  55. package/.next/standalone/.next/server/chunks/241.js.map +1 -0
  56. package/.next/standalone/.next/server/chunks/{8135.js → 2539.js} +218 -36
  57. package/.next/standalone/.next/server/chunks/2539.js.map +1 -0
  58. package/.next/standalone/.next/server/chunks/4631.js +218 -7
  59. package/.next/standalone/.next/server/chunks/4631.js.map +1 -1
  60. package/.next/standalone/.next/server/chunks/8866.js +13389 -13073
  61. package/.next/standalone/.next/server/chunks/8866.js.map +1 -1
  62. package/.next/standalone/.next/server/chunks/9032.js +1 -1
  63. package/.next/standalone/.next/server/chunks/9032.js.map +1 -1
  64. package/.next/standalone/.next/server/middleware-build-manifest.js +2 -2
  65. package/.next/standalone/.next/server/pages/404.html +1 -1
  66. package/.next/standalone/.next/server/pages/500.html +1 -1
  67. package/.next/standalone/.next/server/server-reference-manifest.json +1 -1
  68. package/.next/standalone/.next/static/chunks/app/{page-62e0d5f2404b403b.js → page-2ab710949b62a638.js} +1 -17
  69. package/.next/standalone/.next/static/chunks/app/page-2ab710949b62a638.js.map +1 -0
  70. package/.next/standalone/package.json +1 -1
  71. package/CHANGELOG.md +74 -0
  72. package/components/ui/BootScreen.tsx +0 -10
  73. package/lib/agents/agent-turn.ts +9 -0
  74. package/lib/agents/prepare/request.ts +9 -0
  75. package/lib/agents/run-thread.ts +9 -1
  76. package/lib/api/extension-turn.ts +7 -0
  77. package/lib/bridges/attachment-store.test.ts +440 -0
  78. package/lib/bridges/attachment-store.ts +184 -0
  79. package/lib/bridges/whatsapp.ts +50 -32
  80. package/lib/tools/async-results-tool.ts +114 -0
  81. package/lib/tools/async-results.test.ts +481 -0
  82. package/lib/tools/async-results.ts +165 -0
  83. package/lib/tools/builtins.ts +1 -0
  84. package/lib/tools/wallclock.ts +114 -8
  85. package/package.json +1 -1
  86. package/.next/standalone/.next/server/chunks/1683.js.map +0 -1
  87. package/.next/standalone/.next/server/chunks/8135.js.map +0 -1
  88. package/.next/standalone/.next/static/chunks/app/page-62e0d5f2404b403b.js.map +0 -1
  89. /package/.next/standalone/.next/static/{2xWP8843jbntFGKLnHK6R → ZKy7LJ3KXj2TIyKOg_fBH}/_buildManifest.js +0 -0
  90. /package/.next/standalone/.next/static/{2xWP8843jbntFGKLnHK6R → ZKy7LJ3KXj2TIyKOg_fBH}/_ssgManifest.js +0 -0
@@ -74,6 +74,8 @@ __webpack_require__.d(__webpack_exports__, {
74
74
  var schemas = __webpack_require__(49883);
75
75
  // EXTERNAL MODULE: ./node_modules/@langchain/core/dist/tools/index.js
76
76
  var tools = __webpack_require__(71890);
77
+ // EXTERNAL MODULE: ./lib/tools/async-results.ts
78
+ var async_results = __webpack_require__(95579);
77
79
  ;// ./lib/tools/wallclock.ts
78
80
  // Per-call wall-clock budgets for tools.
79
81
  //
@@ -98,19 +100,43 @@ var tools = __webpack_require__(71890);
98
100
  // mechanism.
99
101
 
100
102
 
103
+
101
104
  const DEFAULT_DEADLINE_MS = 120000;
102
- const DEADLINE_DESCRIPTION = "Optional wall-clock budget for this tool call in milliseconds (default 120000). " + "When the budget is exceeded the call returns a structured timeout result " + "and the turn continues so you can recover — pick a value that matches " + "the expected duration (5000-15000 for fast local ops, 30000-90000 for " + "network/web calls, larger for shell commands that may build or install).";
105
+ // Hard ceiling so a runaway `deadline_ms` from the LLM (or a typo of
106
+ // "10 minutes" as 100_000_000) can't park a single turn for hours. The
107
+ // operator can raise or lower this with JARELA_TOOL_MAX_DEADLINE_MS
108
+ // (integer milliseconds). Anything above the ceiling is clamped and a
109
+ // warning is logged once per call.
110
+ const DEFAULT_MAX_DEADLINE_MS = 30 * 60 * 1000;
111
+ function getMaxDeadlineMs() {
112
+ const raw = process.env.JARELA_TOOL_MAX_DEADLINE_MS;
113
+ if (!raw) return DEFAULT_MAX_DEADLINE_MS;
114
+ const n = Number(raw);
115
+ return Number.isFinite(n) && n > 0 ? Math.floor(n) : DEFAULT_MAX_DEADLINE_MS;
116
+ }
117
+ const DEADLINE_DESCRIPTION = "Optional wall-clock budget for this tool call in milliseconds (default 120000, hard ceiling 1800000 / 30 min). " + "When the budget is exceeded the call returns a structured timeout result " + "and the turn continues so you can recover — pick a value that matches " + "the expected duration (5000-15000 for fast local ops, 30000-90000 for " + "network/web calls, larger for shell commands that may build or install). " + "Values above the ceiling are clamped; use `async_run: true` for work that genuinely needs more.";
118
+ const ASYNC_RUN_DESCRIPTION = "Optional. Set true to fire this tool in the background and get back a " + "tracking key immediately instead of waiting for the result. Use for " + "long-running calls (large fetches, slow shell builds) when you want to " + "keep the conversation moving. Retrieve the result later by calling " + "`tool_result_get` with the returned key. The original `deadline_ms` " + "still applies — it just runs in the background.";
103
119
  function wrapWithWallclock(t) {
104
120
  // Only zod-object schemas can be extended with `deadline_ms`. Other
105
121
  // schema shapes (raw JSON Schema, ZodString) pass through unchanged.
106
122
  // Those tools still get the wallclock race using the default budget.
107
123
  const schema = t.schema;
108
124
  const extendedSchema = schema instanceof schemas/* ZodObject */.bv ? schema.extend({
109
- deadline_ms: schemas/* number */.ai().int().positive().optional().describe(DEADLINE_DESCRIPTION)
125
+ deadline_ms: schemas/* number */.ai().int().positive().optional().describe(DEADLINE_DESCRIPTION),
126
+ async_run: schemas/* boolean */.zM().optional().describe(ASYNC_RUN_DESCRIPTION)
110
127
  }) : null;
111
128
  const wrappedFunc = async (args, config)=>{
112
- const deadlineMs = readDeadlineMs(args) ?? DEFAULT_DEADLINE_MS;
113
- const innerArgs = stripDeadline(args);
129
+ const requested = readDeadlineMs(args) ?? DEFAULT_DEADLINE_MS;
130
+ const ceiling = getMaxDeadlineMs();
131
+ const deadlineMs = Math.min(requested, ceiling);
132
+ if (requested > ceiling) {
133
+ console.warn(`[wallclock] tool="${t.name}" requested deadline_ms=${requested} exceeds ceiling ${ceiling}; clamped. ` + `Use async_run: true for work that genuinely needs more, or raise JARELA_TOOL_MAX_DEADLINE_MS.`);
134
+ }
135
+ const asyncRun = readAsyncRun(args);
136
+ const innerArgs = stripWrapperFields(args);
137
+ if (asyncRun) {
138
+ return runAsync(t, innerArgs, config, deadlineMs);
139
+ }
114
140
  let timer;
115
141
  const timeoutPromise = new Promise((resolve)=>{
116
142
  timer = setTimeout(()=>{
@@ -152,12 +178,56 @@ function readDeadlineMs(args) {
152
178
  const v = args.deadline_ms;
153
179
  return typeof v === "number" && Number.isFinite(v) && v > 0 ? v : null;
154
180
  }
155
- function stripDeadline(args) {
181
+ function readAsyncRun(args) {
182
+ if (!args || typeof args !== "object") return false;
183
+ return args.async_run === true;
184
+ }
185
+ function stripWrapperFields(args) {
156
186
  if (!args || typeof args !== "object") return args;
157
- const { deadline_ms: _ignore, ...rest } = args;
158
- void _ignore;
187
+ const { deadline_ms: _d, async_run: _a, ...rest } = args;
188
+ void _d;
189
+ void _a;
159
190
  return rest;
160
191
  }
192
+ /**
193
+ * Fire the tool detached and return a pointer the agent can poll via
194
+ * `tool_result_get`. The same `deadline_ms` still applies — when the
195
+ * timer wins, the slot is marked errored with a timeout message.
196
+ */ function runAsync(t, innerArgs, config, deadlineMs) {
197
+ const key = (0,async_results/* startAsyncCall */.f3)(t.name);
198
+ const startedAt = Date.now();
199
+ let settled = false;
200
+ const timer = setTimeout(()=>{
201
+ if (settled) return;
202
+ settled = true;
203
+ (0,async_results/* failAsyncCall */.zc)(key, `Tool "${t.name}" exceeded its background wall-clock budget of ${deadlineMs}ms. ` + "The underlying operation may still be running but its result is discarded.");
204
+ }, deadlineMs);
205
+ timer.unref?.();
206
+ void (async ()=>{
207
+ try {
208
+ const work = t.invoke(innerArgs, config);
209
+ const result = await work;
210
+ if (settled) return;
211
+ settled = true;
212
+ clearTimeout(timer);
213
+ (0,async_results/* completeAsyncCall */.eN)(key, typeof result === "string" ? result : JSON.stringify(result));
214
+ } catch (err) {
215
+ if (settled) return;
216
+ settled = true;
217
+ clearTimeout(timer);
218
+ (0,async_results/* failAsyncCall */.zc)(key, err);
219
+ }
220
+ })();
221
+ return JSON.stringify({
222
+ ok: true,
223
+ async: true,
224
+ key,
225
+ tool: t.name,
226
+ started_at: startedAt,
227
+ deadline_ms: deadlineMs,
228
+ hint: `Call tool_result_get with key="${key}" to retrieve the result. Pass wait_ms to short-poll.`
229
+ });
230
+ }
161
231
  /** Exposed for the tests. */ const __DEFAULT_DEADLINE_MS = (/* unused pure expression or super */ null && (DEFAULT_DEADLINE_MS));
162
232
 
163
233
  ;// ./lib/tools/registry.ts
@@ -1438,6 +1508,147 @@ module.exports = require("next/dist/shared/lib/no-fallback-error.external");
1438
1508
 
1439
1509
  module.exports = require("events");
1440
1510
 
1511
+ /***/ }),
1512
+
1513
+ /***/ 95579:
1514
+ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
1515
+
1516
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
1517
+ /* harmony export */ H0: () => (/* binding */ listAsyncResults),
1518
+ /* harmony export */ Ss: () => (/* binding */ getAsyncResult),
1519
+ /* harmony export */ eN: () => (/* binding */ completeAsyncCall),
1520
+ /* harmony export */ f3: () => (/* binding */ startAsyncCall),
1521
+ /* harmony export */ m3: () => (/* binding */ consumeAsyncResult),
1522
+ /* harmony export */ zc: () => (/* binding */ failAsyncCall)
1523
+ /* harmony export */ });
1524
+ /* unused harmony exports DEFAULT_TTL_MS, MAX_ENTRIES, sweepExpired, __resetStore, __backdateFinished */
1525
+ /* harmony import */ var node_crypto__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(77598);
1526
+ /* harmony import */ var node_crypto__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(node_crypto__WEBPACK_IMPORTED_MODULE_0__);
1527
+ // In-process keyed store for async tool results.
1528
+ //
1529
+ // When the wallclock wrapper sees `async_run: true` on a tool call it
1530
+ // returns immediately with a key, kicks the real invocation off in the
1531
+ // background, and parks the eventual result here. The agent later
1532
+ // retrieves the result via the `tool_result_get` built-in.
1533
+ //
1534
+ // Scope is deliberately per-process, in-memory:
1535
+ // - The data lives only as long as the Next.js server process. A
1536
+ // restart wipes both the agent's in-context keys and these results
1537
+ // simultaneously, so we can't end up with the LLM holding a key
1538
+ // that survived the value.
1539
+ // - No on-disk persistence means no schema migration, no PII spill,
1540
+ // no risk of leaking long-running secrets between sessions.
1541
+ //
1542
+ // Memory hygiene:
1543
+ // - TTL (DEFAULT_TTL_MS) caps how long a finished result hangs around
1544
+ // unread. A background sweeper runs on a slow interval.
1545
+ // - Cap on concurrent entries (MAX_ENTRIES). When exceeded, the
1546
+ // oldest *finished* entry is evicted first; if none, the oldest
1547
+ // pending entry is dropped (with a console warn).
1548
+
1549
+ /** How long a finished result stays around if nobody reads it. */ const DEFAULT_TTL_MS = 10 * 60 * 1000;
1550
+ /** Soft cap on total entries (pending + finished). */ const MAX_ENTRIES = 256;
1551
+ /** How often the background sweeper runs. */ const SWEEP_INTERVAL_MS = 60 * 1000;
1552
+ const STORE = new Map();
1553
+ let sweeper = null;
1554
+ function ensureSweeper() {
1555
+ if (sweeper) return;
1556
+ sweeper = setInterval(()=>{
1557
+ sweepExpired(DEFAULT_TTL_MS);
1558
+ }, SWEEP_INTERVAL_MS);
1559
+ sweeper.unref?.();
1560
+ }
1561
+ /**
1562
+ * Carve out a slot for a new async tool call and return its key.
1563
+ * The key is opaque and URL-safe — the agent treats it as a token.
1564
+ */ function startAsyncCall(tool) {
1565
+ ensureSweeper();
1566
+ enforceCap();
1567
+ const key = `async_${node_crypto__WEBPACK_IMPORTED_MODULE_0___default().randomBytes(8).toString("hex")}`;
1568
+ STORE.set(key, {
1569
+ key,
1570
+ tool,
1571
+ status: "pending",
1572
+ started_at: Date.now(),
1573
+ finished_at: null,
1574
+ result: null,
1575
+ error: null
1576
+ });
1577
+ return key;
1578
+ }
1579
+ /** Mark a pending call as completed successfully. */ function completeAsyncCall(key, result) {
1580
+ const rec = STORE.get(key);
1581
+ if (!rec) return;
1582
+ rec.status = "done";
1583
+ rec.result = result;
1584
+ rec.finished_at = Date.now();
1585
+ }
1586
+ /** Mark a pending call as failed. */ function failAsyncCall(key, err) {
1587
+ const rec = STORE.get(key);
1588
+ if (!rec) return;
1589
+ rec.status = "error";
1590
+ rec.error = err instanceof Error ? err.message : String(err);
1591
+ rec.finished_at = Date.now();
1592
+ }
1593
+ /** Read a record without consuming it. */ function getAsyncResult(key) {
1594
+ return STORE.get(key) ?? null;
1595
+ }
1596
+ /** Read and immediately delete a record. */ function consumeAsyncResult(key) {
1597
+ const rec = STORE.get(key);
1598
+ if (!rec) return null;
1599
+ STORE.delete(key);
1600
+ return rec;
1601
+ }
1602
+ /** Snapshot of all current records (newest first). For tool_result_list. */ function listAsyncResults() {
1603
+ return [
1604
+ ...STORE.values()
1605
+ ].sort((a, b)=>b.started_at - a.started_at);
1606
+ }
1607
+ /**
1608
+ * Drop finished entries older than `ttlMs` (measured from `finished_at`).
1609
+ * Pending entries are never expired here — a stuck tool would otherwise
1610
+ * vanish out from under the agent.
1611
+ */ function sweepExpired(ttlMs) {
1612
+ const now = Date.now();
1613
+ let removed = 0;
1614
+ for (const [k, r] of STORE){
1615
+ if (r.status === "pending") continue;
1616
+ if (r.finished_at == null) continue;
1617
+ if (now - r.finished_at >= ttlMs) {
1618
+ STORE.delete(k);
1619
+ removed++;
1620
+ }
1621
+ }
1622
+ return removed;
1623
+ }
1624
+ function enforceCap() {
1625
+ if (STORE.size < MAX_ENTRIES) return;
1626
+ // Prefer evicting finished entries (oldest first). Only if every entry
1627
+ // is pending do we drop a pending one.
1628
+ const sorted = [
1629
+ ...STORE.values()
1630
+ ].sort((a, b)=>a.started_at - b.started_at);
1631
+ const finished = sorted.find((r)=>r.status !== "pending");
1632
+ const victim = finished ?? sorted[0];
1633
+ if (!victim) return;
1634
+ STORE.delete(victim.key);
1635
+ if (!finished) {
1636
+ console.warn(`[async-results] evicted pending entry ${victim.key} (tool=${victim.tool}) ` + `to make room — STORE cap of ${MAX_ENTRIES} hit.`);
1637
+ }
1638
+ }
1639
+ /** Test-only helper. */ function __resetStore() {
1640
+ STORE.clear();
1641
+ if (sweeper) {
1642
+ clearInterval(sweeper);
1643
+ sweeper = null;
1644
+ }
1645
+ }
1646
+ /** Test-only helper. */ function __backdateFinished(key, finishedAt) {
1647
+ const rec = STORE.get(key);
1648
+ if (rec) rec.finished_at = finishedAt;
1649
+ }
1650
+
1651
+
1441
1652
  /***/ })
1442
1653
 
1443
1654
  };