@circuitwall/jarela 1.2.0 → 1.4.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 (96) 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/page-capture/route.js +37 -3
  47. package/.next/standalone/.next/server/app/api/v1/page-capture/route.js.map +1 -1
  48. package/.next/standalone/.next/server/app/api/v1/tools/route.js +2 -2
  49. package/.next/standalone/.next/server/app/page.js +10 -18
  50. package/.next/standalone/.next/server/app/page.js.map +1 -1
  51. package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  52. package/.next/standalone/.next/server/app/setup/page_client-reference-manifest.js +1 -1
  53. package/.next/standalone/.next/server/chunks/210.js +1 -1
  54. package/.next/standalone/.next/server/chunks/239.js +5335 -5230
  55. package/.next/standalone/.next/server/chunks/239.js.map +1 -1
  56. package/.next/standalone/.next/server/chunks/{1683.js → 241.js} +210 -36
  57. package/.next/standalone/.next/server/chunks/241.js.map +1 -0
  58. package/.next/standalone/.next/server/chunks/{8135.js → 2539.js} +218 -36
  59. package/.next/standalone/.next/server/chunks/2539.js.map +1 -0
  60. package/.next/standalone/.next/server/chunks/4631.js +218 -7
  61. package/.next/standalone/.next/server/chunks/4631.js.map +1 -1
  62. package/.next/standalone/.next/server/chunks/8866.js +13389 -13073
  63. package/.next/standalone/.next/server/chunks/8866.js.map +1 -1
  64. package/.next/standalone/.next/server/chunks/9032.js +1 -1
  65. package/.next/standalone/.next/server/chunks/9032.js.map +1 -1
  66. package/.next/standalone/.next/server/middleware-build-manifest.js +2 -2
  67. package/.next/standalone/.next/server/pages/404.html +1 -1
  68. package/.next/standalone/.next/server/pages/500.html +1 -1
  69. package/.next/standalone/.next/server/server-reference-manifest.json +1 -1
  70. package/.next/standalone/.next/static/chunks/app/{page-62e0d5f2404b403b.js → page-74846c864241b96d.js} +11 -19
  71. package/.next/standalone/.next/static/chunks/app/page-74846c864241b96d.js.map +1 -0
  72. package/.next/standalone/package.json +2 -1
  73. package/CHANGELOG.md +98 -0
  74. package/README.md +51 -26
  75. package/components/chat/InputBar.tsx +10 -1
  76. package/components/ui/BootScreen.tsx +0 -10
  77. package/lib/agents/agent-turn.ts +9 -0
  78. package/lib/agents/prepare/request.ts +9 -0
  79. package/lib/agents/run-thread.ts +9 -1
  80. package/lib/api/extension-turn.ts +7 -0
  81. package/lib/api/page-capture.test.ts +58 -0
  82. package/lib/api/page-capture.ts +31 -1
  83. package/lib/bridges/attachment-store.test.ts +440 -0
  84. package/lib/bridges/attachment-store.ts +184 -0
  85. package/lib/bridges/whatsapp.ts +50 -32
  86. package/lib/tools/async-results-tool.ts +114 -0
  87. package/lib/tools/async-results.test.ts +481 -0
  88. package/lib/tools/async-results.ts +165 -0
  89. package/lib/tools/builtins.ts +1 -0
  90. package/lib/tools/wallclock.ts +114 -8
  91. package/package.json +2 -1
  92. package/.next/standalone/.next/server/chunks/1683.js.map +0 -1
  93. package/.next/standalone/.next/server/chunks/8135.js.map +0 -1
  94. package/.next/standalone/.next/static/chunks/app/page-62e0d5f2404b403b.js.map +0 -1
  95. /package/.next/standalone/.next/static/{2xWP8843jbntFGKLnHK6R → AV5AO0yTRABo-NgwxhDe7}/_buildManifest.js +0 -0
  96. /package/.next/standalone/.next/static/{2xWP8843jbntFGKLnHK6R → AV5AO0yTRABo-NgwxhDe7}/_ssgManifest.js +0 -0
@@ -47,6 +47,8 @@ __webpack_require__.d(__webpack_exports__, {
47
47
  var schemas = __webpack_require__(49883);
48
48
  // EXTERNAL MODULE: ./node_modules/@langchain/core/dist/tools/index.js
49
49
  var tools = __webpack_require__(71890);
50
+ // EXTERNAL MODULE: ./lib/tools/async-results.ts
51
+ var async_results = __webpack_require__(95579);
50
52
  ;// ./lib/tools/wallclock.ts
51
53
  // Per-call wall-clock budgets for tools.
52
54
  //
@@ -71,19 +73,43 @@ var tools = __webpack_require__(71890);
71
73
  // mechanism.
72
74
 
73
75
 
76
+
74
77
  const DEFAULT_DEADLINE_MS = 120000;
75
- 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).";
78
+ // Hard ceiling so a runaway `deadline_ms` from the LLM (or a typo of
79
+ // "10 minutes" as 100_000_000) can't park a single turn for hours. The
80
+ // operator can raise or lower this with JARELA_TOOL_MAX_DEADLINE_MS
81
+ // (integer milliseconds). Anything above the ceiling is clamped and a
82
+ // warning is logged once per call.
83
+ const DEFAULT_MAX_DEADLINE_MS = 30 * 60 * 1000;
84
+ function getMaxDeadlineMs() {
85
+ const raw = process.env.JARELA_TOOL_MAX_DEADLINE_MS;
86
+ if (!raw) return DEFAULT_MAX_DEADLINE_MS;
87
+ const n = Number(raw);
88
+ return Number.isFinite(n) && n > 0 ? Math.floor(n) : DEFAULT_MAX_DEADLINE_MS;
89
+ }
90
+ 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.";
91
+ 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.";
76
92
  function wrapWithWallclock(t) {
77
93
  // Only zod-object schemas can be extended with `deadline_ms`. Other
78
94
  // schema shapes (raw JSON Schema, ZodString) pass through unchanged.
79
95
  // Those tools still get the wallclock race using the default budget.
80
96
  const schema = t.schema;
81
97
  const extendedSchema = schema instanceof schemas/* ZodObject */.bv ? schema.extend({
82
- deadline_ms: schemas/* number */.ai().int().positive().optional().describe(DEADLINE_DESCRIPTION)
98
+ deadline_ms: schemas/* number */.ai().int().positive().optional().describe(DEADLINE_DESCRIPTION),
99
+ async_run: schemas/* boolean */.zM().optional().describe(ASYNC_RUN_DESCRIPTION)
83
100
  }) : null;
84
101
  const wrappedFunc = async (args, config)=>{
85
- const deadlineMs = readDeadlineMs(args) ?? DEFAULT_DEADLINE_MS;
86
- const innerArgs = stripDeadline(args);
102
+ const requested = readDeadlineMs(args) ?? DEFAULT_DEADLINE_MS;
103
+ const ceiling = getMaxDeadlineMs();
104
+ const deadlineMs = Math.min(requested, ceiling);
105
+ if (requested > ceiling) {
106
+ 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.`);
107
+ }
108
+ const asyncRun = readAsyncRun(args);
109
+ const innerArgs = stripWrapperFields(args);
110
+ if (asyncRun) {
111
+ return runAsync(t, innerArgs, config, deadlineMs);
112
+ }
87
113
  let timer;
88
114
  const timeoutPromise = new Promise((resolve)=>{
89
115
  timer = setTimeout(()=>{
@@ -125,12 +151,56 @@ function readDeadlineMs(args) {
125
151
  const v = args.deadline_ms;
126
152
  return typeof v === "number" && Number.isFinite(v) && v > 0 ? v : null;
127
153
  }
128
- function stripDeadline(args) {
154
+ function readAsyncRun(args) {
155
+ if (!args || typeof args !== "object") return false;
156
+ return args.async_run === true;
157
+ }
158
+ function stripWrapperFields(args) {
129
159
  if (!args || typeof args !== "object") return args;
130
- const { deadline_ms: _ignore, ...rest } = args;
131
- void _ignore;
160
+ const { deadline_ms: _d, async_run: _a, ...rest } = args;
161
+ void _d;
162
+ void _a;
132
163
  return rest;
133
164
  }
165
+ /**
166
+ * Fire the tool detached and return a pointer the agent can poll via
167
+ * `tool_result_get`. The same `deadline_ms` still applies — when the
168
+ * timer wins, the slot is marked errored with a timeout message.
169
+ */ function runAsync(t, innerArgs, config, deadlineMs) {
170
+ const key = (0,async_results/* startAsyncCall */.f3)(t.name);
171
+ const startedAt = Date.now();
172
+ let settled = false;
173
+ const timer = setTimeout(()=>{
174
+ if (settled) return;
175
+ settled = true;
176
+ (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.");
177
+ }, deadlineMs);
178
+ timer.unref?.();
179
+ void (async ()=>{
180
+ try {
181
+ const work = t.invoke(innerArgs, config);
182
+ const result = await work;
183
+ if (settled) return;
184
+ settled = true;
185
+ clearTimeout(timer);
186
+ (0,async_results/* completeAsyncCall */.eN)(key, typeof result === "string" ? result : JSON.stringify(result));
187
+ } catch (err) {
188
+ if (settled) return;
189
+ settled = true;
190
+ clearTimeout(timer);
191
+ (0,async_results/* failAsyncCall */.zc)(key, err);
192
+ }
193
+ })();
194
+ return JSON.stringify({
195
+ ok: true,
196
+ async: true,
197
+ key,
198
+ tool: t.name,
199
+ started_at: startedAt,
200
+ deadline_ms: deadlineMs,
201
+ hint: `Call tool_result_get with key="${key}" to retrieve the result. Pass wait_ms to short-poll.`
202
+ });
203
+ }
134
204
  /** Exposed for the tests. */ const __DEFAULT_DEADLINE_MS = (/* unused pure expression or super */ null && (DEFAULT_DEADLINE_MS));
135
205
 
136
206
  ;// ./lib/tools/registry.ts
@@ -6539,6 +6609,147 @@ function getDocumentSourceStats(sourceId) {
6539
6609
  }
6540
6610
 
6541
6611
 
6612
+ /***/ }),
6613
+
6614
+ /***/ 95579:
6615
+ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
6616
+
6617
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
6618
+ /* harmony export */ H0: () => (/* binding */ listAsyncResults),
6619
+ /* harmony export */ Ss: () => (/* binding */ getAsyncResult),
6620
+ /* harmony export */ eN: () => (/* binding */ completeAsyncCall),
6621
+ /* harmony export */ f3: () => (/* binding */ startAsyncCall),
6622
+ /* harmony export */ m3: () => (/* binding */ consumeAsyncResult),
6623
+ /* harmony export */ zc: () => (/* binding */ failAsyncCall)
6624
+ /* harmony export */ });
6625
+ /* unused harmony exports DEFAULT_TTL_MS, MAX_ENTRIES, sweepExpired, __resetStore, __backdateFinished */
6626
+ /* harmony import */ var node_crypto__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(77598);
6627
+ /* harmony import */ var node_crypto__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(node_crypto__WEBPACK_IMPORTED_MODULE_0__);
6628
+ // In-process keyed store for async tool results.
6629
+ //
6630
+ // When the wallclock wrapper sees `async_run: true` on a tool call it
6631
+ // returns immediately with a key, kicks the real invocation off in the
6632
+ // background, and parks the eventual result here. The agent later
6633
+ // retrieves the result via the `tool_result_get` built-in.
6634
+ //
6635
+ // Scope is deliberately per-process, in-memory:
6636
+ // - The data lives only as long as the Next.js server process. A
6637
+ // restart wipes both the agent's in-context keys and these results
6638
+ // simultaneously, so we can't end up with the LLM holding a key
6639
+ // that survived the value.
6640
+ // - No on-disk persistence means no schema migration, no PII spill,
6641
+ // no risk of leaking long-running secrets between sessions.
6642
+ //
6643
+ // Memory hygiene:
6644
+ // - TTL (DEFAULT_TTL_MS) caps how long a finished result hangs around
6645
+ // unread. A background sweeper runs on a slow interval.
6646
+ // - Cap on concurrent entries (MAX_ENTRIES). When exceeded, the
6647
+ // oldest *finished* entry is evicted first; if none, the oldest
6648
+ // pending entry is dropped (with a console warn).
6649
+
6650
+ /** How long a finished result stays around if nobody reads it. */ const DEFAULT_TTL_MS = 10 * 60 * 1000;
6651
+ /** Soft cap on total entries (pending + finished). */ const MAX_ENTRIES = 256;
6652
+ /** How often the background sweeper runs. */ const SWEEP_INTERVAL_MS = 60 * 1000;
6653
+ const STORE = new Map();
6654
+ let sweeper = null;
6655
+ function ensureSweeper() {
6656
+ if (sweeper) return;
6657
+ sweeper = setInterval(()=>{
6658
+ sweepExpired(DEFAULT_TTL_MS);
6659
+ }, SWEEP_INTERVAL_MS);
6660
+ sweeper.unref?.();
6661
+ }
6662
+ /**
6663
+ * Carve out a slot for a new async tool call and return its key.
6664
+ * The key is opaque and URL-safe — the agent treats it as a token.
6665
+ */ function startAsyncCall(tool) {
6666
+ ensureSweeper();
6667
+ enforceCap();
6668
+ const key = `async_${node_crypto__WEBPACK_IMPORTED_MODULE_0___default().randomBytes(8).toString("hex")}`;
6669
+ STORE.set(key, {
6670
+ key,
6671
+ tool,
6672
+ status: "pending",
6673
+ started_at: Date.now(),
6674
+ finished_at: null,
6675
+ result: null,
6676
+ error: null
6677
+ });
6678
+ return key;
6679
+ }
6680
+ /** Mark a pending call as completed successfully. */ function completeAsyncCall(key, result) {
6681
+ const rec = STORE.get(key);
6682
+ if (!rec) return;
6683
+ rec.status = "done";
6684
+ rec.result = result;
6685
+ rec.finished_at = Date.now();
6686
+ }
6687
+ /** Mark a pending call as failed. */ function failAsyncCall(key, err) {
6688
+ const rec = STORE.get(key);
6689
+ if (!rec) return;
6690
+ rec.status = "error";
6691
+ rec.error = err instanceof Error ? err.message : String(err);
6692
+ rec.finished_at = Date.now();
6693
+ }
6694
+ /** Read a record without consuming it. */ function getAsyncResult(key) {
6695
+ return STORE.get(key) ?? null;
6696
+ }
6697
+ /** Read and immediately delete a record. */ function consumeAsyncResult(key) {
6698
+ const rec = STORE.get(key);
6699
+ if (!rec) return null;
6700
+ STORE.delete(key);
6701
+ return rec;
6702
+ }
6703
+ /** Snapshot of all current records (newest first). For tool_result_list. */ function listAsyncResults() {
6704
+ return [
6705
+ ...STORE.values()
6706
+ ].sort((a, b)=>b.started_at - a.started_at);
6707
+ }
6708
+ /**
6709
+ * Drop finished entries older than `ttlMs` (measured from `finished_at`).
6710
+ * Pending entries are never expired here — a stuck tool would otherwise
6711
+ * vanish out from under the agent.
6712
+ */ function sweepExpired(ttlMs) {
6713
+ const now = Date.now();
6714
+ let removed = 0;
6715
+ for (const [k, r] of STORE){
6716
+ if (r.status === "pending") continue;
6717
+ if (r.finished_at == null) continue;
6718
+ if (now - r.finished_at >= ttlMs) {
6719
+ STORE.delete(k);
6720
+ removed++;
6721
+ }
6722
+ }
6723
+ return removed;
6724
+ }
6725
+ function enforceCap() {
6726
+ if (STORE.size < MAX_ENTRIES) return;
6727
+ // Prefer evicting finished entries (oldest first). Only if every entry
6728
+ // is pending do we drop a pending one.
6729
+ const sorted = [
6730
+ ...STORE.values()
6731
+ ].sort((a, b)=>a.started_at - b.started_at);
6732
+ const finished = sorted.find((r)=>r.status !== "pending");
6733
+ const victim = finished ?? sorted[0];
6734
+ if (!victim) return;
6735
+ STORE.delete(victim.key);
6736
+ if (!finished) {
6737
+ console.warn(`[async-results] evicted pending entry ${victim.key} (tool=${victim.tool}) ` + `to make room — STORE cap of ${MAX_ENTRIES} hit.`);
6738
+ }
6739
+ }
6740
+ /** Test-only helper. */ function __resetStore() {
6741
+ STORE.clear();
6742
+ if (sweeper) {
6743
+ clearInterval(sweeper);
6744
+ sweeper = null;
6745
+ }
6746
+ }
6747
+ /** Test-only helper. */ function __backdateFinished(key, finishedAt) {
6748
+ const rec = STORE.get(key);
6749
+ if (rec) rec.finished_at = finishedAt;
6750
+ }
6751
+
6752
+
6542
6753
  /***/ })
6543
6754
 
6544
6755
  };