@anuma/sdk 3.0.0 → 3.1.1

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.
@@ -43,956 +43,6 @@ var __decorateClass = (decorators, target, key, kind) => {
43
43
  return result;
44
44
  };
45
45
 
46
- // src/client/core/bodySerializer.gen.ts
47
- var jsonBodySerializer;
48
- var init_bodySerializer_gen = __esm({
49
- "src/client/core/bodySerializer.gen.ts"() {
50
- "use strict";
51
- jsonBodySerializer = {
52
- bodySerializer: (body) => JSON.stringify(
53
- body,
54
- (_key, value) => typeof value === "bigint" ? value.toString() : value
55
- )
56
- };
57
- }
58
- });
59
-
60
- // src/client/core/serverSentEvents.gen.ts
61
- var createSseClient;
62
- var init_serverSentEvents_gen = __esm({
63
- "src/client/core/serverSentEvents.gen.ts"() {
64
- "use strict";
65
- createSseClient = ({
66
- onRequest,
67
- onSseError,
68
- onSseEvent,
69
- responseTransformer,
70
- responseValidator,
71
- sseDefaultRetryDelay,
72
- sseMaxRetryAttempts,
73
- sseMaxRetryDelay,
74
- sseSleepFn,
75
- url,
76
- ...options
77
- }) => {
78
- let lastEventId;
79
- const sleep4 = sseSleepFn ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
80
- const createStream = async function* () {
81
- let retryDelay = sseDefaultRetryDelay ?? 3e3;
82
- let attempt = 0;
83
- const signal = options.signal ?? new AbortController().signal;
84
- while (true) {
85
- if (signal.aborted) break;
86
- attempt++;
87
- const headers = options.headers instanceof Headers ? options.headers : new Headers(options.headers);
88
- if (lastEventId !== void 0) {
89
- headers.set("Last-Event-ID", lastEventId);
90
- }
91
- try {
92
- const requestInit = {
93
- redirect: "follow",
94
- ...options,
95
- body: options.serializedBody,
96
- headers,
97
- signal
98
- };
99
- let request = new Request(url, requestInit);
100
- if (onRequest) {
101
- request = await onRequest(url, requestInit);
102
- }
103
- const _fetch = options.fetch ?? globalThis.fetch;
104
- const response = await _fetch(request);
105
- if (!response.ok)
106
- throw new Error(
107
- `SSE failed: ${response.status} ${response.statusText}`
108
- );
109
- if (!response.body) throw new Error("No body in SSE response");
110
- const reader = response.body.pipeThrough(new TextDecoderStream()).getReader();
111
- let buffer = "";
112
- const abortHandler = () => {
113
- try {
114
- reader.cancel();
115
- } catch {
116
- }
117
- };
118
- signal.addEventListener("abort", abortHandler);
119
- try {
120
- while (true) {
121
- const { done, value } = await reader.read();
122
- if (done) break;
123
- buffer += value;
124
- const chunks = buffer.split("\n\n");
125
- buffer = chunks.pop() ?? "";
126
- for (const chunk of chunks) {
127
- const lines = chunk.split("\n");
128
- const dataLines = [];
129
- let eventName;
130
- for (const line of lines) {
131
- if (line.startsWith("data:")) {
132
- dataLines.push(line.replace(/^data:\s*/, ""));
133
- } else if (line.startsWith("event:")) {
134
- eventName = line.replace(/^event:\s*/, "");
135
- } else if (line.startsWith("id:")) {
136
- lastEventId = line.replace(/^id:\s*/, "");
137
- } else if (line.startsWith("retry:")) {
138
- const parsed = Number.parseInt(
139
- line.replace(/^retry:\s*/, ""),
140
- 10
141
- );
142
- if (!Number.isNaN(parsed)) {
143
- retryDelay = parsed;
144
- }
145
- }
146
- }
147
- let data;
148
- let parsedJson = false;
149
- if (dataLines.length) {
150
- const rawData = dataLines.join("\n");
151
- try {
152
- data = JSON.parse(rawData);
153
- parsedJson = true;
154
- } catch {
155
- data = rawData;
156
- }
157
- }
158
- if (parsedJson) {
159
- if (responseValidator) {
160
- await responseValidator(data);
161
- }
162
- if (responseTransformer) {
163
- data = await responseTransformer(data);
164
- }
165
- }
166
- onSseEvent?.({
167
- data,
168
- event: eventName,
169
- id: lastEventId,
170
- retry: retryDelay
171
- });
172
- if (dataLines.length) {
173
- yield data;
174
- }
175
- }
176
- }
177
- } finally {
178
- signal.removeEventListener("abort", abortHandler);
179
- reader.releaseLock();
180
- }
181
- break;
182
- } catch (error) {
183
- onSseError?.(error);
184
- if (sseMaxRetryAttempts !== void 0 && attempt >= sseMaxRetryAttempts) {
185
- break;
186
- }
187
- const backoff = Math.min(
188
- retryDelay * 2 ** (attempt - 1),
189
- sseMaxRetryDelay ?? 3e4
190
- );
191
- await sleep4(backoff);
192
- }
193
- }
194
- };
195
- const stream = createStream();
196
- return { stream };
197
- };
198
- }
199
- });
200
-
201
- // src/client/core/pathSerializer.gen.ts
202
- var separatorArrayExplode, separatorArrayNoExplode, separatorObjectExplode, serializeArrayParam, serializePrimitiveParam, serializeObjectParam;
203
- var init_pathSerializer_gen = __esm({
204
- "src/client/core/pathSerializer.gen.ts"() {
205
- "use strict";
206
- separatorArrayExplode = (style) => {
207
- switch (style) {
208
- case "label":
209
- return ".";
210
- case "matrix":
211
- return ";";
212
- case "simple":
213
- return ",";
214
- default:
215
- return "&";
216
- }
217
- };
218
- separatorArrayNoExplode = (style) => {
219
- switch (style) {
220
- case "form":
221
- return ",";
222
- case "pipeDelimited":
223
- return "|";
224
- case "spaceDelimited":
225
- return "%20";
226
- default:
227
- return ",";
228
- }
229
- };
230
- separatorObjectExplode = (style) => {
231
- switch (style) {
232
- case "label":
233
- return ".";
234
- case "matrix":
235
- return ";";
236
- case "simple":
237
- return ",";
238
- default:
239
- return "&";
240
- }
241
- };
242
- serializeArrayParam = ({
243
- allowReserved,
244
- explode,
245
- name,
246
- style,
247
- value
248
- }) => {
249
- if (!explode) {
250
- const joinedValues2 = (allowReserved ? value : value.map((v) => encodeURIComponent(v))).join(separatorArrayNoExplode(style));
251
- switch (style) {
252
- case "label":
253
- return `.${joinedValues2}`;
254
- case "matrix":
255
- return `;${name}=${joinedValues2}`;
256
- case "simple":
257
- return joinedValues2;
258
- default:
259
- return `${name}=${joinedValues2}`;
260
- }
261
- }
262
- const separator = separatorArrayExplode(style);
263
- const joinedValues = value.map((v) => {
264
- if (style === "label" || style === "simple") {
265
- return allowReserved ? v : encodeURIComponent(v);
266
- }
267
- return serializePrimitiveParam({
268
- allowReserved,
269
- name,
270
- value: v
271
- });
272
- }).join(separator);
273
- return style === "label" || style === "matrix" ? separator + joinedValues : joinedValues;
274
- };
275
- serializePrimitiveParam = ({
276
- allowReserved,
277
- name,
278
- value
279
- }) => {
280
- if (value === void 0 || value === null) {
281
- return "";
282
- }
283
- if (typeof value === "object") {
284
- throw new Error(
285
- "Deeply-nested arrays/objects aren\u2019t supported. Provide your own `querySerializer()` to handle these."
286
- );
287
- }
288
- return `${name}=${allowReserved ? value : encodeURIComponent(value)}`;
289
- };
290
- serializeObjectParam = ({
291
- allowReserved,
292
- explode,
293
- name,
294
- style,
295
- value,
296
- valueOnly
297
- }) => {
298
- if (value instanceof Date) {
299
- return valueOnly ? value.toISOString() : `${name}=${value.toISOString()}`;
300
- }
301
- if (style !== "deepObject" && !explode) {
302
- let values = [];
303
- Object.entries(value).forEach(([key, v]) => {
304
- values = [
305
- ...values,
306
- key,
307
- allowReserved ? v : encodeURIComponent(v)
308
- ];
309
- });
310
- const joinedValues2 = values.join(",");
311
- switch (style) {
312
- case "form":
313
- return `${name}=${joinedValues2}`;
314
- case "label":
315
- return `.${joinedValues2}`;
316
- case "matrix":
317
- return `;${name}=${joinedValues2}`;
318
- default:
319
- return joinedValues2;
320
- }
321
- }
322
- const separator = separatorObjectExplode(style);
323
- const joinedValues = Object.entries(value).map(
324
- ([key, v]) => serializePrimitiveParam({
325
- allowReserved,
326
- name: style === "deepObject" ? `${name}[${key}]` : key,
327
- value: v
328
- })
329
- ).join(separator);
330
- return style === "label" || style === "matrix" ? separator + joinedValues : joinedValues;
331
- };
332
- }
333
- });
334
-
335
- // src/client/core/utils.gen.ts
336
- function getValidRequestBody(options) {
337
- const hasBody = options.body !== void 0;
338
- const isSerializedBody = hasBody && options.bodySerializer;
339
- if (isSerializedBody) {
340
- if ("serializedBody" in options) {
341
- const hasSerializedBody = options.serializedBody !== void 0 && options.serializedBody !== "";
342
- return hasSerializedBody ? options.serializedBody : null;
343
- }
344
- return options.body !== "" ? options.body : null;
345
- }
346
- if (hasBody) {
347
- return options.body;
348
- }
349
- return void 0;
350
- }
351
- var init_utils_gen = __esm({
352
- "src/client/core/utils.gen.ts"() {
353
- "use strict";
354
- }
355
- });
356
-
357
- // src/client/core/auth.gen.ts
358
- var getAuthToken;
359
- var init_auth_gen = __esm({
360
- "src/client/core/auth.gen.ts"() {
361
- "use strict";
362
- getAuthToken = async (auth, callback) => {
363
- const token = typeof callback === "function" ? await callback(auth) : callback;
364
- if (!token) {
365
- return;
366
- }
367
- if (auth.scheme === "bearer") {
368
- return `Bearer ${token}`;
369
- }
370
- if (auth.scheme === "basic") {
371
- return `Basic ${btoa(token)}`;
372
- }
373
- return token;
374
- };
375
- }
376
- });
377
-
378
- // src/client/client/utils.gen.ts
379
- var PATH_PARAM_RE, defaultPathSerializer, createQuerySerializer, getParseAs, checkForExistence, setAuthParams, buildUrl, getUrl, mergeConfigs, headersEntries, mergeHeaders, Interceptors, createInterceptors, defaultQuerySerializer, defaultHeaders, createConfig;
380
- var init_utils_gen2 = __esm({
381
- "src/client/client/utils.gen.ts"() {
382
- "use strict";
383
- init_auth_gen();
384
- init_bodySerializer_gen();
385
- init_pathSerializer_gen();
386
- PATH_PARAM_RE = /\{[^{}]+\}/g;
387
- defaultPathSerializer = ({ path, url: _url }) => {
388
- let url = _url;
389
- const matches = _url.match(PATH_PARAM_RE);
390
- if (matches) {
391
- for (const match of matches) {
392
- let explode = false;
393
- let name = match.substring(1, match.length - 1);
394
- let style = "simple";
395
- if (name.endsWith("*")) {
396
- explode = true;
397
- name = name.substring(0, name.length - 1);
398
- }
399
- if (name.startsWith(".")) {
400
- name = name.substring(1);
401
- style = "label";
402
- } else if (name.startsWith(";")) {
403
- name = name.substring(1);
404
- style = "matrix";
405
- }
406
- const value = path[name];
407
- if (value === void 0 || value === null) {
408
- continue;
409
- }
410
- if (Array.isArray(value)) {
411
- url = url.replace(
412
- match,
413
- serializeArrayParam({ explode, name, style, value })
414
- );
415
- continue;
416
- }
417
- if (typeof value === "object") {
418
- url = url.replace(
419
- match,
420
- serializeObjectParam({
421
- explode,
422
- name,
423
- style,
424
- value,
425
- valueOnly: true
426
- })
427
- );
428
- continue;
429
- }
430
- if (style === "matrix") {
431
- url = url.replace(
432
- match,
433
- `;${serializePrimitiveParam({
434
- name,
435
- value
436
- })}`
437
- );
438
- continue;
439
- }
440
- const replaceValue = encodeURIComponent(
441
- style === "label" ? `.${value}` : value
442
- );
443
- url = url.replace(match, replaceValue);
444
- }
445
- }
446
- return url;
447
- };
448
- createQuerySerializer = ({
449
- parameters = {},
450
- ...args
451
- } = {}) => {
452
- const querySerializer = (queryParams) => {
453
- const search = [];
454
- if (queryParams && typeof queryParams === "object") {
455
- for (const name in queryParams) {
456
- const value = queryParams[name];
457
- if (value === void 0 || value === null) {
458
- continue;
459
- }
460
- const options = parameters[name] || args;
461
- if (Array.isArray(value)) {
462
- const serializedArray = serializeArrayParam({
463
- allowReserved: options.allowReserved,
464
- explode: true,
465
- name,
466
- style: "form",
467
- value,
468
- ...options.array
469
- });
470
- if (serializedArray) search.push(serializedArray);
471
- } else if (typeof value === "object") {
472
- const serializedObject = serializeObjectParam({
473
- allowReserved: options.allowReserved,
474
- explode: true,
475
- name,
476
- style: "deepObject",
477
- value,
478
- ...options.object
479
- });
480
- if (serializedObject) search.push(serializedObject);
481
- } else {
482
- const serializedPrimitive = serializePrimitiveParam({
483
- allowReserved: options.allowReserved,
484
- name,
485
- value
486
- });
487
- if (serializedPrimitive) search.push(serializedPrimitive);
488
- }
489
- }
490
- }
491
- return search.join("&");
492
- };
493
- return querySerializer;
494
- };
495
- getParseAs = (contentType) => {
496
- if (!contentType) {
497
- return "stream";
498
- }
499
- const cleanContent = contentType.split(";")[0]?.trim();
500
- if (!cleanContent) {
501
- return;
502
- }
503
- if (cleanContent.startsWith("application/json") || cleanContent.endsWith("+json")) {
504
- return "json";
505
- }
506
- if (cleanContent === "multipart/form-data") {
507
- return "formData";
508
- }
509
- if (["application/", "audio/", "image/", "video/"].some(
510
- (type) => cleanContent.startsWith(type)
511
- )) {
512
- return "blob";
513
- }
514
- if (cleanContent.startsWith("text/")) {
515
- return "text";
516
- }
517
- return;
518
- };
519
- checkForExistence = (options, name) => {
520
- if (!name) {
521
- return false;
522
- }
523
- if (options.headers.has(name) || options.query?.[name] || options.headers.get("Cookie")?.includes(`${name}=`)) {
524
- return true;
525
- }
526
- return false;
527
- };
528
- setAuthParams = async ({
529
- security,
530
- ...options
531
- }) => {
532
- for (const auth of security) {
533
- if (checkForExistence(options, auth.name)) {
534
- continue;
535
- }
536
- const token = await getAuthToken(auth, options.auth);
537
- if (!token) {
538
- continue;
539
- }
540
- const name = auth.name ?? "Authorization";
541
- switch (auth.in) {
542
- case "query":
543
- if (!options.query) {
544
- options.query = {};
545
- }
546
- options.query[name] = token;
547
- break;
548
- case "cookie":
549
- options.headers.append("Cookie", `${name}=${token}`);
550
- break;
551
- case "header":
552
- default:
553
- options.headers.set(name, token);
554
- break;
555
- }
556
- }
557
- };
558
- buildUrl = (options) => {
559
- const url = getUrl({
560
- baseUrl: options.baseUrl,
561
- path: options.path,
562
- query: options.query,
563
- querySerializer: typeof options.querySerializer === "function" ? options.querySerializer : createQuerySerializer(options.querySerializer),
564
- url: options.url
565
- });
566
- return url;
567
- };
568
- getUrl = ({
569
- baseUrl,
570
- path,
571
- query,
572
- querySerializer,
573
- url: _url
574
- }) => {
575
- const pathUrl = _url.startsWith("/") ? _url : `/${_url}`;
576
- let url = (baseUrl ?? "") + pathUrl;
577
- if (path) {
578
- url = defaultPathSerializer({ path, url });
579
- }
580
- let search = query ? querySerializer(query) : "";
581
- if (search.startsWith("?")) {
582
- search = search.substring(1);
583
- }
584
- if (search) {
585
- url += `?${search}`;
586
- }
587
- return url;
588
- };
589
- mergeConfigs = (a, b) => {
590
- const config = { ...a, ...b };
591
- if (config.baseUrl?.endsWith("/")) {
592
- config.baseUrl = config.baseUrl.substring(0, config.baseUrl.length - 1);
593
- }
594
- config.headers = mergeHeaders(a.headers, b.headers);
595
- return config;
596
- };
597
- headersEntries = (headers) => {
598
- const entries = [];
599
- headers.forEach((value, key) => {
600
- entries.push([key, value]);
601
- });
602
- return entries;
603
- };
604
- mergeHeaders = (...headers) => {
605
- const mergedHeaders = new Headers();
606
- for (const header of headers) {
607
- if (!header || typeof header !== "object") {
608
- continue;
609
- }
610
- const iterator = header instanceof Headers ? headersEntries(header) : Object.entries(header);
611
- for (const [key, value] of iterator) {
612
- if (value === null) {
613
- mergedHeaders.delete(key);
614
- } else if (Array.isArray(value)) {
615
- for (const v of value) {
616
- mergedHeaders.append(key, v);
617
- }
618
- } else if (value !== void 0) {
619
- mergedHeaders.set(
620
- key,
621
- typeof value === "object" ? JSON.stringify(value) : value
622
- );
623
- }
624
- }
625
- }
626
- return mergedHeaders;
627
- };
628
- Interceptors = class {
629
- constructor() {
630
- this.fns = [];
631
- }
632
- clear() {
633
- this.fns = [];
634
- }
635
- eject(id) {
636
- const index = this.getInterceptorIndex(id);
637
- if (this.fns[index]) {
638
- this.fns[index] = null;
639
- }
640
- }
641
- exists(id) {
642
- const index = this.getInterceptorIndex(id);
643
- return Boolean(this.fns[index]);
644
- }
645
- getInterceptorIndex(id) {
646
- if (typeof id === "number") {
647
- return this.fns[id] ? id : -1;
648
- }
649
- return this.fns.indexOf(id);
650
- }
651
- update(id, fn) {
652
- const index = this.getInterceptorIndex(id);
653
- if (this.fns[index]) {
654
- this.fns[index] = fn;
655
- return id;
656
- }
657
- return false;
658
- }
659
- use(fn) {
660
- this.fns.push(fn);
661
- return this.fns.length - 1;
662
- }
663
- };
664
- createInterceptors = () => ({
665
- error: new Interceptors(),
666
- request: new Interceptors(),
667
- response: new Interceptors()
668
- });
669
- defaultQuerySerializer = createQuerySerializer({
670
- allowReserved: false,
671
- array: {
672
- explode: true,
673
- style: "form"
674
- },
675
- object: {
676
- explode: true,
677
- style: "deepObject"
678
- }
679
- });
680
- defaultHeaders = {
681
- "Content-Type": "application/json"
682
- };
683
- createConfig = (override = {}) => ({
684
- ...jsonBodySerializer,
685
- headers: defaultHeaders,
686
- parseAs: "auto",
687
- querySerializer: defaultQuerySerializer,
688
- ...override
689
- });
690
- }
691
- });
692
-
693
- // src/client/client/client.gen.ts
694
- var createClient;
695
- var init_client_gen = __esm({
696
- "src/client/client/client.gen.ts"() {
697
- "use strict";
698
- init_serverSentEvents_gen();
699
- init_utils_gen();
700
- init_utils_gen2();
701
- createClient = (config = {}) => {
702
- let _config = mergeConfigs(createConfig(), config);
703
- const getConfig = () => ({ ..._config });
704
- const setConfig = (config2) => {
705
- _config = mergeConfigs(_config, config2);
706
- return getConfig();
707
- };
708
- const interceptors = createInterceptors();
709
- const beforeRequest = async (options) => {
710
- const opts = {
711
- ..._config,
712
- ...options,
713
- fetch: options.fetch ?? _config.fetch ?? globalThis.fetch,
714
- headers: mergeHeaders(_config.headers, options.headers),
715
- serializedBody: void 0
716
- };
717
- if (opts.security) {
718
- await setAuthParams({
719
- ...opts,
720
- security: opts.security
721
- });
722
- }
723
- if (opts.requestValidator) {
724
- await opts.requestValidator(opts);
725
- }
726
- if (opts.body !== void 0 && opts.bodySerializer) {
727
- opts.serializedBody = opts.bodySerializer(opts.body);
728
- }
729
- if (opts.body === void 0 || opts.serializedBody === "") {
730
- opts.headers.delete("Content-Type");
731
- }
732
- const url = buildUrl(opts);
733
- return { opts, url };
734
- };
735
- const request = async (options) => {
736
- const { opts, url } = await beforeRequest(options);
737
- for (const fn of interceptors.request.fns) {
738
- if (fn) {
739
- await fn(opts);
740
- }
741
- }
742
- const _fetch = opts.fetch;
743
- const requestInit = {
744
- ...opts,
745
- body: getValidRequestBody(opts)
746
- };
747
- let response = await _fetch(url, requestInit);
748
- for (const fn of interceptors.response.fns) {
749
- if (fn) {
750
- response = await fn(response, opts);
751
- }
752
- }
753
- const result = {
754
- response
755
- };
756
- if (response.ok) {
757
- const parseAs = (opts.parseAs === "auto" ? getParseAs(response.headers.get("Content-Type")) : opts.parseAs) ?? "json";
758
- if (response.status === 204 || response.headers.get("Content-Length") === "0") {
759
- let emptyData;
760
- switch (parseAs) {
761
- case "arrayBuffer":
762
- case "blob":
763
- case "text":
764
- emptyData = await response[parseAs]();
765
- break;
766
- case "formData":
767
- emptyData = new FormData();
768
- break;
769
- case "stream":
770
- emptyData = response.body;
771
- break;
772
- case "json":
773
- default:
774
- emptyData = {};
775
- break;
776
- }
777
- return {
778
- data: emptyData,
779
- ...result
780
- };
781
- }
782
- let data;
783
- switch (parseAs) {
784
- case "arrayBuffer":
785
- case "blob":
786
- case "formData":
787
- case "json":
788
- case "text":
789
- data = await response[parseAs]();
790
- break;
791
- case "stream":
792
- return {
793
- data: response.body,
794
- ...result
795
- };
796
- }
797
- if (parseAs === "json") {
798
- if (opts.responseValidator) {
799
- await opts.responseValidator(data);
800
- }
801
- if (opts.responseTransformer) {
802
- data = await opts.responseTransformer(data);
803
- }
804
- }
805
- return {
806
- data,
807
- ...result
808
- };
809
- }
810
- const textError = await response.text();
811
- let jsonError;
812
- try {
813
- jsonError = JSON.parse(textError);
814
- } catch {
815
- }
816
- const error = jsonError ?? textError;
817
- let finalError = error;
818
- for (const fn of interceptors.error.fns) {
819
- if (fn) {
820
- finalError = await fn(error, response, opts);
821
- }
822
- }
823
- finalError = finalError || {};
824
- if (opts.throwOnError) {
825
- throw finalError;
826
- }
827
- return {
828
- error: finalError,
829
- ...result
830
- };
831
- };
832
- const makeMethodFn = (method) => (options) => request({ ...options, method });
833
- const makeSseFn = (method) => async (options) => {
834
- const { opts, url } = await beforeRequest(options);
835
- return createSseClient({
836
- ...opts,
837
- body: opts.body,
838
- headers: opts.headers,
839
- method,
840
- onRequest: async (url2, init) => {
841
- let request2 = new Request(url2, init);
842
- const requestInit = {
843
- ...init,
844
- method: init.method,
845
- url: url2
846
- };
847
- for (const fn of interceptors.request.fns) {
848
- if (fn) {
849
- await fn(requestInit);
850
- request2 = new Request(requestInit.url, requestInit);
851
- }
852
- }
853
- return request2;
854
- },
855
- url
856
- });
857
- };
858
- return {
859
- buildUrl,
860
- connect: makeMethodFn("CONNECT"),
861
- delete: makeMethodFn("DELETE"),
862
- get: makeMethodFn("GET"),
863
- getConfig,
864
- head: makeMethodFn("HEAD"),
865
- interceptors,
866
- options: makeMethodFn("OPTIONS"),
867
- patch: makeMethodFn("PATCH"),
868
- post: makeMethodFn("POST"),
869
- put: makeMethodFn("PUT"),
870
- request,
871
- setConfig,
872
- sse: {
873
- connect: makeSseFn("CONNECT"),
874
- delete: makeSseFn("DELETE"),
875
- get: makeSseFn("GET"),
876
- head: makeSseFn("HEAD"),
877
- options: makeSseFn("OPTIONS"),
878
- patch: makeSseFn("PATCH"),
879
- post: makeSseFn("POST"),
880
- put: makeSseFn("PUT"),
881
- trace: makeSseFn("TRACE")
882
- },
883
- trace: makeMethodFn("TRACE")
884
- };
885
- };
886
- }
887
- });
888
-
889
- // src/client/client/index.ts
890
- var init_client = __esm({
891
- "src/client/client/index.ts"() {
892
- "use strict";
893
- init_client_gen();
894
- init_utils_gen2();
895
- }
896
- });
897
-
898
- // src/clientConfig.ts
899
- var clientConfig_exports = {};
900
- __export(clientConfig_exports, {
901
- BASE_URL: () => BASE_URL,
902
- MCP_R2_DOMAIN: () => MCP_R2_DOMAIN,
903
- createClientConfig: () => createClientConfig,
904
- resolveBaseUrl: () => resolveBaseUrl
905
- });
906
- function readGlobal(key) {
907
- if (typeof globalThis === "undefined") return void 0;
908
- const value = globalThis[key];
909
- return typeof value === "string" && value.length > 0 ? value : void 0;
910
- }
911
- function resolveBaseUrl() {
912
- const fromProcess = typeof process !== "undefined" ? process.env.API_URL || process.env.NEXT_PUBLIC_API_URL || process.env.EXPO_PUBLIC_API_URL : void 0;
913
- const fromGlobal = readGlobal("API_URL") ?? readGlobal("NEXT_PUBLIC_API_URL") ?? readGlobal("EXPO_PUBLIC_API_URL");
914
- return (fromProcess || fromGlobal || DEFAULT_BASE_URL).replace(/\/+$/, "");
915
- }
916
- var DEFAULT_BASE_URL, BASE_URL, MCP_R2_DOMAIN, createClientConfig;
917
- var init_clientConfig = __esm({
918
- "src/clientConfig.ts"() {
919
- "use strict";
920
- DEFAULT_BASE_URL = "https://portal.anuma-dev.ai";
921
- BASE_URL = resolveBaseUrl();
922
- MCP_R2_DOMAIN = "4cf0e0ea50b97e72386fcf2f92a2d4e8.r2.cloudflarestorage.com";
923
- createClientConfig = (config) => ({
924
- ...config,
925
- baseUrl: BASE_URL
926
- });
927
- }
928
- });
929
-
930
- // src/client/client.gen.ts
931
- var client;
932
- var init_client_gen2 = __esm({
933
- "src/client/client.gen.ts"() {
934
- "use strict";
935
- init_client();
936
- init_clientConfig();
937
- client = createClient(createClientConfig(createConfig()));
938
- }
939
- });
940
-
941
- // src/client/sdk.gen.ts
942
- var getApiV1CreditsBalance, getApiV1CreditsPacks, postApiV1CreditsPurchase, postApiV1Embeddings, getApiV1Models;
943
- var init_sdk_gen = __esm({
944
- "src/client/sdk.gen.ts"() {
945
- "use strict";
946
- init_client_gen2();
947
- getApiV1CreditsBalance = (options) => {
948
- return (options?.client ?? client).get({
949
- url: "/api/v1/credits/balance",
950
- ...options
951
- });
952
- };
953
- getApiV1CreditsPacks = (options) => {
954
- return (options?.client ?? client).get({
955
- url: "/api/v1/credits/packs",
956
- ...options
957
- });
958
- };
959
- postApiV1CreditsPurchase = (options) => {
960
- return (options.client ?? client).post({
961
- url: "/api/v1/credits/purchase",
962
- ...options,
963
- headers: {
964
- "Content-Type": "application/json",
965
- ...options.headers
966
- }
967
- });
968
- };
969
- postApiV1Embeddings = (options) => {
970
- return (options.client ?? client).post({
971
- url: "/api/v1/embeddings",
972
- ...options,
973
- headers: {
974
- "Content-Type": "application/json",
975
- ...options.headers
976
- }
977
- });
978
- };
979
- getApiV1Models = (options) => {
980
- return (options?.client ?? client).get({
981
- url: "/api/v1/models",
982
- ...options
983
- });
984
- };
985
- }
986
- });
987
-
988
- // src/client/index.ts
989
- var init_client2 = __esm({
990
- "src/client/index.ts"() {
991
- "use strict";
992
- init_sdk_gen();
993
- }
994
- });
995
-
996
46
  // src/lib/logger.ts
997
47
  function setLogger(logger) {
998
48
  currentLogger = logger;
@@ -1894,6 +944,12 @@ function getToolCallEvents(r) {
1894
944
  }
1895
945
  return r.tool_call_events;
1896
946
  }
947
+ function getToolsChecksum(r) {
948
+ if (isChatCompletionResponse(r)) {
949
+ return r.portal?.tools_checksum ?? r.tools_checksum;
950
+ }
951
+ return r.tools_checksum;
952
+ }
1897
953
  function getCostMicroUsd(r) {
1898
954
  if (isChatCompletionResponse(r)) {
1899
955
  return r.portal?.cost_micro_usd ?? r.usage?.cost_micro_usd;
@@ -2980,7 +2036,39 @@ var init_constants = __esm({
2980
2036
  }
2981
2037
  });
2982
2038
 
2983
- // src/lib/memoryEngine/embeddings.ts
2039
+ // src/clientConfig.ts
2040
+ var clientConfig_exports = {};
2041
+ __export(clientConfig_exports, {
2042
+ BASE_URL: () => BASE_URL,
2043
+ MCP_R2_DOMAIN: () => MCP_R2_DOMAIN,
2044
+ createClientConfig: () => createClientConfig,
2045
+ resolveBaseUrl: () => resolveBaseUrl
2046
+ });
2047
+ function readGlobal(key) {
2048
+ if (typeof globalThis === "undefined") return void 0;
2049
+ const value = globalThis[key];
2050
+ return typeof value === "string" && value.length > 0 ? value : void 0;
2051
+ }
2052
+ function resolveBaseUrl() {
2053
+ const fromProcess = typeof process !== "undefined" ? process.env.API_URL || process.env.NEXT_PUBLIC_API_URL || process.env.EXPO_PUBLIC_API_URL : void 0;
2054
+ const fromGlobal = readGlobal("API_URL") ?? readGlobal("NEXT_PUBLIC_API_URL") ?? readGlobal("EXPO_PUBLIC_API_URL");
2055
+ return (fromProcess || fromGlobal || DEFAULT_BASE_URL).replace(/\/+$/, "");
2056
+ }
2057
+ var DEFAULT_BASE_URL, BASE_URL, MCP_R2_DOMAIN, createClientConfig;
2058
+ var init_clientConfig = __esm({
2059
+ "src/clientConfig.ts"() {
2060
+ "use strict";
2061
+ DEFAULT_BASE_URL = "https://portal.anuma-dev.ai";
2062
+ BASE_URL = resolveBaseUrl();
2063
+ MCP_R2_DOMAIN = "4cf0e0ea50b97e72386fcf2f92a2d4e8.r2.cloudflarestorage.com";
2064
+ createClientConfig = (config) => ({
2065
+ ...config,
2066
+ baseUrl: BASE_URL
2067
+ });
2068
+ }
2069
+ });
2070
+
2071
+ // src/lib/memoryEngine/generate.ts
2984
2072
  async function withEmbeddingRetry(call) {
2985
2073
  let last;
2986
2074
  let lastThrown;
@@ -3031,7 +2119,7 @@ async function generateEmbedding(text12, options) {
3031
2119
  throw new Error("Either apiKey or getToken must be provided");
3032
2120
  }
3033
2121
  const response = await withEmbeddingRetry(
3034
- () => postApiV1Embeddings({
2122
+ () => (0, import_client.postApiV1Embeddings)({
3035
2123
  baseUrl,
3036
2124
  body: {
3037
2125
  // Mask PII from the request body only — the cache above still keys on
@@ -3063,7 +2151,7 @@ async function generateEmbedding(text12, options) {
3063
2151
  }
3064
2152
  async function generateEmbeddingsBatch(texts, headers, baseUrl, model, onUsage, maskInput) {
3065
2153
  const response = await withEmbeddingRetry(
3066
- () => postApiV1Embeddings({
2154
+ () => (0, import_client.postApiV1Embeddings)({
3067
2155
  baseUrl,
3068
2156
  // Mask PII from the request body only; cache/order still key on originals.
3069
2157
  body: { input: maskInput ? texts.map(maskInput) : texts, model },
@@ -3162,8 +2250,29 @@ async function generateEmbeddings(texts, options) {
3162
2250
  cache.set(uncachedTexts[i], f32Embedding);
3163
2251
  }
3164
2252
  }
3165
- return results;
3166
- }
2253
+ return results;
2254
+ }
2255
+ var import_client, EMBED_MAX_ATTEMPTS, EmbeddingHttpError, DEFAULT_EMBEDDING_BATCH_SIZE, DEFAULT_EMBEDDING_BATCH_CONCURRENCY;
2256
+ var init_generate = __esm({
2257
+ "src/lib/memoryEngine/generate.ts"() {
2258
+ "use strict";
2259
+ import_client = require("@anuma/sdk");
2260
+ init_clientConfig();
2261
+ init_constants();
2262
+ EMBED_MAX_ATTEMPTS = 4;
2263
+ EmbeddingHttpError = class extends Error {
2264
+ constructor(message, status) {
2265
+ super(message);
2266
+ this.name = "EmbeddingHttpError";
2267
+ this.status = status;
2268
+ }
2269
+ };
2270
+ DEFAULT_EMBEDDING_BATCH_SIZE = 100;
2271
+ DEFAULT_EMBEDDING_BATCH_CONCURRENCY = 3;
2272
+ }
2273
+ });
2274
+
2275
+ // src/lib/memoryEngine/embeddings.ts
3167
2276
  async function embedMessage(ctx, messageId, options) {
3168
2277
  let message;
3169
2278
  const conversations = await getConversationsOp(ctx);
@@ -3319,27 +2428,17 @@ async function chunkAndEmbedAllMessages(ctx, options, filter) {
3319
2428
  }
3320
2429
  return embeddedCount;
3321
2430
  }
3322
- var DEFAULT_MIN_CONTENT_LENGTH, EMBED_MAX_ATTEMPTS, EmbeddingHttpError, DEFAULT_EMBEDDING_BATCH_SIZE, DEFAULT_EMBEDDING_BATCH_CONCURRENCY;
2431
+ var DEFAULT_MIN_CONTENT_LENGTH;
3323
2432
  var init_embeddings = __esm({
3324
2433
  "src/lib/memoryEngine/embeddings.ts"() {
3325
2434
  "use strict";
3326
- init_client2();
3327
- init_clientConfig();
3328
2435
  init_operations();
3329
2436
  init_logger();
3330
2437
  init_chunking();
3331
2438
  init_constants();
2439
+ init_generate();
2440
+ init_generate();
3332
2441
  DEFAULT_MIN_CONTENT_LENGTH = 10;
3333
- EMBED_MAX_ATTEMPTS = 4;
3334
- EmbeddingHttpError = class extends Error {
3335
- constructor(message, status) {
3336
- super(message);
3337
- this.name = "EmbeddingHttpError";
3338
- this.status = status;
3339
- }
3340
- };
3341
- DEFAULT_EMBEDDING_BATCH_SIZE = 100;
3342
- DEFAULT_EMBEDDING_BATCH_CONCURRENCY = 3;
3343
2442
  }
3344
2443
  });
3345
2444
 
@@ -4750,27 +3849,25 @@ async function stampTopicsExtractedAtOp(ctx, memoryIds, extractedAt, version = T
4750
3849
  for (let i = 0; i < uniqueIds.length; i += CHUNK) {
4751
3850
  const chunkIds = uniqueIds.slice(i, i + CHUNK);
4752
3851
  await ctx.database.write(async () => {
4753
- const prepared = [];
3852
+ const records = [];
4754
3853
  for (const id of chunkIds) {
4755
- let record;
4756
3854
  try {
4757
- record = await ctx.vaultMemoryCollection.find(id);
3855
+ records.push(await ctx.vaultMemoryCollection.find(id));
4758
3856
  } catch {
4759
- continue;
4760
- }
4761
- if (record.isDeleted || !isOwnedByCtxUser(ctx, record) || record.topicsUserManaged) {
4762
- continue;
4763
3857
  }
4764
- const originalUpdatedAt = record.updatedAt.getTime();
4765
- prepared.push(
4766
- record.prepareUpdate((r) => {
4767
- r._setRaw("topics_extracted_at", extractedAt);
4768
- r._setRaw("topics_extracted_version", version);
4769
- r._setRaw("updated_at", originalUpdatedAt);
4770
- })
4771
- );
4772
- stamped.push(id);
4773
3858
  }
3859
+ const eligible = records.filter(
3860
+ (record) => !record.isDeleted && isOwnedByCtxUser(ctx, record) && !record.topicsUserManaged
3861
+ );
3862
+ const prepared = eligible.map((record) => {
3863
+ const originalUpdatedAt = record.updatedAt.getTime();
3864
+ return record.prepareUpdate((r) => {
3865
+ r._setRaw("topics_extracted_at", extractedAt);
3866
+ r._setRaw("topics_extracted_version", version);
3867
+ r._setRaw("updated_at", originalUpdatedAt);
3868
+ });
3869
+ });
3870
+ for (const record of eligible) stamped.push(record.id);
4774
3871
  if (prepared.length > 0) await ctx.database.batch(...prepared);
4775
3872
  });
4776
3873
  }
@@ -7492,8 +6589,142 @@ function createWebSearchPreProcessor(options = {}) {
7492
6589
  // src/lib/chat/resumeStream.ts
7493
6590
  init_clientConfig();
7494
6591
 
6592
+ // src/client/core/serverSentEvents.gen.ts
6593
+ var createSseClient = ({
6594
+ onRequest,
6595
+ onSseError,
6596
+ onSseEvent,
6597
+ responseTransformer,
6598
+ responseValidator,
6599
+ sseDefaultRetryDelay,
6600
+ sseMaxRetryAttempts,
6601
+ sseMaxRetryDelay,
6602
+ sseSleepFn,
6603
+ url,
6604
+ ...options
6605
+ }) => {
6606
+ let lastEventId;
6607
+ const sleep4 = sseSleepFn ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
6608
+ const createStream = async function* () {
6609
+ let retryDelay = sseDefaultRetryDelay ?? 3e3;
6610
+ let attempt = 0;
6611
+ const signal = options.signal ?? new AbortController().signal;
6612
+ while (true) {
6613
+ if (signal.aborted) break;
6614
+ attempt++;
6615
+ const headers = options.headers instanceof Headers ? options.headers : new Headers(options.headers);
6616
+ if (lastEventId !== void 0) {
6617
+ headers.set("Last-Event-ID", lastEventId);
6618
+ }
6619
+ try {
6620
+ const requestInit = {
6621
+ redirect: "follow",
6622
+ ...options,
6623
+ body: options.serializedBody,
6624
+ headers,
6625
+ signal
6626
+ };
6627
+ let request = new Request(url, requestInit);
6628
+ if (onRequest) {
6629
+ request = await onRequest(url, requestInit);
6630
+ }
6631
+ const _fetch = options.fetch ?? globalThis.fetch;
6632
+ const response = await _fetch(request);
6633
+ if (!response.ok)
6634
+ throw new Error(
6635
+ `SSE failed: ${response.status} ${response.statusText}`
6636
+ );
6637
+ if (!response.body) throw new Error("No body in SSE response");
6638
+ const reader = response.body.pipeThrough(new TextDecoderStream()).getReader();
6639
+ let buffer = "";
6640
+ const abortHandler = () => {
6641
+ try {
6642
+ reader.cancel();
6643
+ } catch {
6644
+ }
6645
+ };
6646
+ signal.addEventListener("abort", abortHandler);
6647
+ try {
6648
+ while (true) {
6649
+ const { done, value } = await reader.read();
6650
+ if (done) break;
6651
+ buffer += value;
6652
+ const chunks = buffer.split("\n\n");
6653
+ buffer = chunks.pop() ?? "";
6654
+ for (const chunk of chunks) {
6655
+ const lines = chunk.split("\n");
6656
+ const dataLines = [];
6657
+ let eventName;
6658
+ for (const line of lines) {
6659
+ if (line.startsWith("data:")) {
6660
+ dataLines.push(line.replace(/^data:\s*/, ""));
6661
+ } else if (line.startsWith("event:")) {
6662
+ eventName = line.replace(/^event:\s*/, "");
6663
+ } else if (line.startsWith("id:")) {
6664
+ lastEventId = line.replace(/^id:\s*/, "");
6665
+ } else if (line.startsWith("retry:")) {
6666
+ const parsed = Number.parseInt(
6667
+ line.replace(/^retry:\s*/, ""),
6668
+ 10
6669
+ );
6670
+ if (!Number.isNaN(parsed)) {
6671
+ retryDelay = parsed;
6672
+ }
6673
+ }
6674
+ }
6675
+ let data;
6676
+ let parsedJson = false;
6677
+ if (dataLines.length) {
6678
+ const rawData = dataLines.join("\n");
6679
+ try {
6680
+ data = JSON.parse(rawData);
6681
+ parsedJson = true;
6682
+ } catch {
6683
+ data = rawData;
6684
+ }
6685
+ }
6686
+ if (parsedJson) {
6687
+ if (responseValidator) {
6688
+ await responseValidator(data);
6689
+ }
6690
+ if (responseTransformer) {
6691
+ data = await responseTransformer(data);
6692
+ }
6693
+ }
6694
+ onSseEvent?.({
6695
+ data,
6696
+ event: eventName,
6697
+ id: lastEventId,
6698
+ retry: retryDelay
6699
+ });
6700
+ if (dataLines.length) {
6701
+ yield data;
6702
+ }
6703
+ }
6704
+ }
6705
+ } finally {
6706
+ signal.removeEventListener("abort", abortHandler);
6707
+ reader.releaseLock();
6708
+ }
6709
+ break;
6710
+ } catch (error) {
6711
+ onSseError?.(error);
6712
+ if (sseMaxRetryAttempts !== void 0 && attempt >= sseMaxRetryAttempts) {
6713
+ break;
6714
+ }
6715
+ const backoff = Math.min(
6716
+ retryDelay * 2 ** (attempt - 1),
6717
+ sseMaxRetryDelay ?? 3e4
6718
+ );
6719
+ await sleep4(backoff);
6720
+ }
6721
+ }
6722
+ };
6723
+ const stream = createStream();
6724
+ return { stream };
6725
+ };
6726
+
7495
6727
  // src/lib/chat/toolLoop.ts
7496
- init_serverSentEvents_gen();
7497
6728
  init_clientConfig();
7498
6729
  init_embeddings();
7499
6730
  init_redactor();
@@ -10928,26 +10159,722 @@ var xhrTransport = (options) => {
10928
10159
  push({ type: "error", error: makeAbortError() });
10929
10160
  finished = true;
10930
10161
  };
10931
- xhr.send(options.body !== void 0 ? JSON.stringify(options.body) : void 0);
10932
- }
10933
- async function* createStream() {
10934
- if (options.signal?.aborted) throw makeAbortError();
10935
- while (true) {
10936
- await waitForItem();
10937
- while (queue.length > 0) {
10938
- const item = queue.shift();
10939
- if (item.type === "done") return;
10940
- if (item.type === "error") throw item.error;
10941
- yield item.value;
10162
+ xhr.send(options.body !== void 0 ? JSON.stringify(options.body) : void 0);
10163
+ }
10164
+ async function* createStream() {
10165
+ if (options.signal?.aborted) throw makeAbortError();
10166
+ while (true) {
10167
+ await waitForItem();
10168
+ while (queue.length > 0) {
10169
+ const item = queue.shift();
10170
+ if (item.type === "done") return;
10171
+ if (item.type === "error") throw item.error;
10172
+ yield item.value;
10173
+ }
10174
+ }
10175
+ }
10176
+ return { stream: createStream() };
10177
+ };
10178
+
10179
+ // src/react/useCredits.ts
10180
+ var import_react = require("react");
10181
+
10182
+ // src/client/core/bodySerializer.gen.ts
10183
+ var jsonBodySerializer = {
10184
+ bodySerializer: (body) => JSON.stringify(
10185
+ body,
10186
+ (_key, value) => typeof value === "bigint" ? value.toString() : value
10187
+ )
10188
+ };
10189
+
10190
+ // src/client/core/pathSerializer.gen.ts
10191
+ var separatorArrayExplode = (style) => {
10192
+ switch (style) {
10193
+ case "label":
10194
+ return ".";
10195
+ case "matrix":
10196
+ return ";";
10197
+ case "simple":
10198
+ return ",";
10199
+ default:
10200
+ return "&";
10201
+ }
10202
+ };
10203
+ var separatorArrayNoExplode = (style) => {
10204
+ switch (style) {
10205
+ case "form":
10206
+ return ",";
10207
+ case "pipeDelimited":
10208
+ return "|";
10209
+ case "spaceDelimited":
10210
+ return "%20";
10211
+ default:
10212
+ return ",";
10213
+ }
10214
+ };
10215
+ var separatorObjectExplode = (style) => {
10216
+ switch (style) {
10217
+ case "label":
10218
+ return ".";
10219
+ case "matrix":
10220
+ return ";";
10221
+ case "simple":
10222
+ return ",";
10223
+ default:
10224
+ return "&";
10225
+ }
10226
+ };
10227
+ var serializeArrayParam = ({
10228
+ allowReserved,
10229
+ explode,
10230
+ name,
10231
+ style,
10232
+ value
10233
+ }) => {
10234
+ if (!explode) {
10235
+ const joinedValues2 = (allowReserved ? value : value.map((v) => encodeURIComponent(v))).join(separatorArrayNoExplode(style));
10236
+ switch (style) {
10237
+ case "label":
10238
+ return `.${joinedValues2}`;
10239
+ case "matrix":
10240
+ return `;${name}=${joinedValues2}`;
10241
+ case "simple":
10242
+ return joinedValues2;
10243
+ default:
10244
+ return `${name}=${joinedValues2}`;
10245
+ }
10246
+ }
10247
+ const separator = separatorArrayExplode(style);
10248
+ const joinedValues = value.map((v) => {
10249
+ if (style === "label" || style === "simple") {
10250
+ return allowReserved ? v : encodeURIComponent(v);
10251
+ }
10252
+ return serializePrimitiveParam({
10253
+ allowReserved,
10254
+ name,
10255
+ value: v
10256
+ });
10257
+ }).join(separator);
10258
+ return style === "label" || style === "matrix" ? separator + joinedValues : joinedValues;
10259
+ };
10260
+ var serializePrimitiveParam = ({
10261
+ allowReserved,
10262
+ name,
10263
+ value
10264
+ }) => {
10265
+ if (value === void 0 || value === null) {
10266
+ return "";
10267
+ }
10268
+ if (typeof value === "object") {
10269
+ throw new Error(
10270
+ "Deeply-nested arrays/objects aren\u2019t supported. Provide your own `querySerializer()` to handle these."
10271
+ );
10272
+ }
10273
+ return `${name}=${allowReserved ? value : encodeURIComponent(value)}`;
10274
+ };
10275
+ var serializeObjectParam = ({
10276
+ allowReserved,
10277
+ explode,
10278
+ name,
10279
+ style,
10280
+ value,
10281
+ valueOnly
10282
+ }) => {
10283
+ if (value instanceof Date) {
10284
+ return valueOnly ? value.toISOString() : `${name}=${value.toISOString()}`;
10285
+ }
10286
+ if (style !== "deepObject" && !explode) {
10287
+ let values = [];
10288
+ Object.entries(value).forEach(([key, v]) => {
10289
+ values = [
10290
+ ...values,
10291
+ key,
10292
+ allowReserved ? v : encodeURIComponent(v)
10293
+ ];
10294
+ });
10295
+ const joinedValues2 = values.join(",");
10296
+ switch (style) {
10297
+ case "form":
10298
+ return `${name}=${joinedValues2}`;
10299
+ case "label":
10300
+ return `.${joinedValues2}`;
10301
+ case "matrix":
10302
+ return `;${name}=${joinedValues2}`;
10303
+ default:
10304
+ return joinedValues2;
10305
+ }
10306
+ }
10307
+ const separator = separatorObjectExplode(style);
10308
+ const joinedValues = Object.entries(value).map(
10309
+ ([key, v]) => serializePrimitiveParam({
10310
+ allowReserved,
10311
+ name: style === "deepObject" ? `${name}[${key}]` : key,
10312
+ value: v
10313
+ })
10314
+ ).join(separator);
10315
+ return style === "label" || style === "matrix" ? separator + joinedValues : joinedValues;
10316
+ };
10317
+
10318
+ // src/client/core/utils.gen.ts
10319
+ function getValidRequestBody(options) {
10320
+ const hasBody = options.body !== void 0;
10321
+ const isSerializedBody = hasBody && options.bodySerializer;
10322
+ if (isSerializedBody) {
10323
+ if ("serializedBody" in options) {
10324
+ const hasSerializedBody = options.serializedBody !== void 0 && options.serializedBody !== "";
10325
+ return hasSerializedBody ? options.serializedBody : null;
10326
+ }
10327
+ return options.body !== "" ? options.body : null;
10328
+ }
10329
+ if (hasBody) {
10330
+ return options.body;
10331
+ }
10332
+ return void 0;
10333
+ }
10334
+
10335
+ // src/client/core/auth.gen.ts
10336
+ var getAuthToken = async (auth, callback) => {
10337
+ const token = typeof callback === "function" ? await callback(auth) : callback;
10338
+ if (!token) {
10339
+ return;
10340
+ }
10341
+ if (auth.scheme === "bearer") {
10342
+ return `Bearer ${token}`;
10343
+ }
10344
+ if (auth.scheme === "basic") {
10345
+ return `Basic ${btoa(token)}`;
10346
+ }
10347
+ return token;
10348
+ };
10349
+
10350
+ // src/client/client/utils.gen.ts
10351
+ var PATH_PARAM_RE = /\{[^{}]+\}/g;
10352
+ var defaultPathSerializer = ({ path, url: _url }) => {
10353
+ let url = _url;
10354
+ const matches = _url.match(PATH_PARAM_RE);
10355
+ if (matches) {
10356
+ for (const match of matches) {
10357
+ let explode = false;
10358
+ let name = match.substring(1, match.length - 1);
10359
+ let style = "simple";
10360
+ if (name.endsWith("*")) {
10361
+ explode = true;
10362
+ name = name.substring(0, name.length - 1);
10363
+ }
10364
+ if (name.startsWith(".")) {
10365
+ name = name.substring(1);
10366
+ style = "label";
10367
+ } else if (name.startsWith(";")) {
10368
+ name = name.substring(1);
10369
+ style = "matrix";
10370
+ }
10371
+ const value = path[name];
10372
+ if (value === void 0 || value === null) {
10373
+ continue;
10374
+ }
10375
+ if (Array.isArray(value)) {
10376
+ url = url.replace(
10377
+ match,
10378
+ serializeArrayParam({ explode, name, style, value })
10379
+ );
10380
+ continue;
10381
+ }
10382
+ if (typeof value === "object") {
10383
+ url = url.replace(
10384
+ match,
10385
+ serializeObjectParam({
10386
+ explode,
10387
+ name,
10388
+ style,
10389
+ value,
10390
+ valueOnly: true
10391
+ })
10392
+ );
10393
+ continue;
10394
+ }
10395
+ if (style === "matrix") {
10396
+ url = url.replace(
10397
+ match,
10398
+ `;${serializePrimitiveParam({
10399
+ name,
10400
+ value
10401
+ })}`
10402
+ );
10403
+ continue;
10404
+ }
10405
+ const replaceValue = encodeURIComponent(
10406
+ style === "label" ? `.${value}` : value
10407
+ );
10408
+ url = url.replace(match, replaceValue);
10409
+ }
10410
+ }
10411
+ return url;
10412
+ };
10413
+ var createQuerySerializer = ({
10414
+ parameters = {},
10415
+ ...args
10416
+ } = {}) => {
10417
+ const querySerializer = (queryParams) => {
10418
+ const search = [];
10419
+ if (queryParams && typeof queryParams === "object") {
10420
+ for (const name in queryParams) {
10421
+ const value = queryParams[name];
10422
+ if (value === void 0 || value === null) {
10423
+ continue;
10424
+ }
10425
+ const options = parameters[name] || args;
10426
+ if (Array.isArray(value)) {
10427
+ const serializedArray = serializeArrayParam({
10428
+ allowReserved: options.allowReserved,
10429
+ explode: true,
10430
+ name,
10431
+ style: "form",
10432
+ value,
10433
+ ...options.array
10434
+ });
10435
+ if (serializedArray) search.push(serializedArray);
10436
+ } else if (typeof value === "object") {
10437
+ const serializedObject = serializeObjectParam({
10438
+ allowReserved: options.allowReserved,
10439
+ explode: true,
10440
+ name,
10441
+ style: "deepObject",
10442
+ value,
10443
+ ...options.object
10444
+ });
10445
+ if (serializedObject) search.push(serializedObject);
10446
+ } else {
10447
+ const serializedPrimitive = serializePrimitiveParam({
10448
+ allowReserved: options.allowReserved,
10449
+ name,
10450
+ value
10451
+ });
10452
+ if (serializedPrimitive) search.push(serializedPrimitive);
10453
+ }
10454
+ }
10455
+ }
10456
+ return search.join("&");
10457
+ };
10458
+ return querySerializer;
10459
+ };
10460
+ var getParseAs = (contentType) => {
10461
+ if (!contentType) {
10462
+ return "stream";
10463
+ }
10464
+ const cleanContent = contentType.split(";")[0]?.trim();
10465
+ if (!cleanContent) {
10466
+ return;
10467
+ }
10468
+ if (cleanContent.startsWith("application/json") || cleanContent.endsWith("+json")) {
10469
+ return "json";
10470
+ }
10471
+ if (cleanContent === "multipart/form-data") {
10472
+ return "formData";
10473
+ }
10474
+ if (["application/", "audio/", "image/", "video/"].some(
10475
+ (type) => cleanContent.startsWith(type)
10476
+ )) {
10477
+ return "blob";
10478
+ }
10479
+ if (cleanContent.startsWith("text/")) {
10480
+ return "text";
10481
+ }
10482
+ return;
10483
+ };
10484
+ var checkForExistence = (options, name) => {
10485
+ if (!name) {
10486
+ return false;
10487
+ }
10488
+ if (options.headers.has(name) || options.query?.[name] || options.headers.get("Cookie")?.includes(`${name}=`)) {
10489
+ return true;
10490
+ }
10491
+ return false;
10492
+ };
10493
+ var setAuthParams = async ({
10494
+ security,
10495
+ ...options
10496
+ }) => {
10497
+ for (const auth of security) {
10498
+ if (checkForExistence(options, auth.name)) {
10499
+ continue;
10500
+ }
10501
+ const token = await getAuthToken(auth, options.auth);
10502
+ if (!token) {
10503
+ continue;
10504
+ }
10505
+ const name = auth.name ?? "Authorization";
10506
+ switch (auth.in) {
10507
+ case "query":
10508
+ if (!options.query) {
10509
+ options.query = {};
10510
+ }
10511
+ options.query[name] = token;
10512
+ break;
10513
+ case "cookie":
10514
+ options.headers.append("Cookie", `${name}=${token}`);
10515
+ break;
10516
+ case "header":
10517
+ default:
10518
+ options.headers.set(name, token);
10519
+ break;
10520
+ }
10521
+ }
10522
+ };
10523
+ var buildUrl = (options) => {
10524
+ const url = getUrl({
10525
+ baseUrl: options.baseUrl,
10526
+ path: options.path,
10527
+ query: options.query,
10528
+ querySerializer: typeof options.querySerializer === "function" ? options.querySerializer : createQuerySerializer(options.querySerializer),
10529
+ url: options.url
10530
+ });
10531
+ return url;
10532
+ };
10533
+ var getUrl = ({
10534
+ baseUrl,
10535
+ path,
10536
+ query,
10537
+ querySerializer,
10538
+ url: _url
10539
+ }) => {
10540
+ const pathUrl = _url.startsWith("/") ? _url : `/${_url}`;
10541
+ let url = (baseUrl ?? "") + pathUrl;
10542
+ if (path) {
10543
+ url = defaultPathSerializer({ path, url });
10544
+ }
10545
+ let search = query ? querySerializer(query) : "";
10546
+ if (search.startsWith("?")) {
10547
+ search = search.substring(1);
10548
+ }
10549
+ if (search) {
10550
+ url += `?${search}`;
10551
+ }
10552
+ return url;
10553
+ };
10554
+ var mergeConfigs = (a, b) => {
10555
+ const config = { ...a, ...b };
10556
+ if (config.baseUrl?.endsWith("/")) {
10557
+ config.baseUrl = config.baseUrl.substring(0, config.baseUrl.length - 1);
10558
+ }
10559
+ config.headers = mergeHeaders(a.headers, b.headers);
10560
+ return config;
10561
+ };
10562
+ var headersEntries = (headers) => {
10563
+ const entries = [];
10564
+ headers.forEach((value, key) => {
10565
+ entries.push([key, value]);
10566
+ });
10567
+ return entries;
10568
+ };
10569
+ var mergeHeaders = (...headers) => {
10570
+ const mergedHeaders = new Headers();
10571
+ for (const header of headers) {
10572
+ if (!header || typeof header !== "object") {
10573
+ continue;
10574
+ }
10575
+ const iterator = header instanceof Headers ? headersEntries(header) : Object.entries(header);
10576
+ for (const [key, value] of iterator) {
10577
+ if (value === null) {
10578
+ mergedHeaders.delete(key);
10579
+ } else if (Array.isArray(value)) {
10580
+ for (const v of value) {
10581
+ mergedHeaders.append(key, v);
10582
+ }
10583
+ } else if (value !== void 0) {
10584
+ mergedHeaders.set(
10585
+ key,
10586
+ typeof value === "object" ? JSON.stringify(value) : value
10587
+ );
10588
+ }
10589
+ }
10590
+ }
10591
+ return mergedHeaders;
10592
+ };
10593
+ var Interceptors = class {
10594
+ constructor() {
10595
+ this.fns = [];
10596
+ }
10597
+ clear() {
10598
+ this.fns = [];
10599
+ }
10600
+ eject(id) {
10601
+ const index = this.getInterceptorIndex(id);
10602
+ if (this.fns[index]) {
10603
+ this.fns[index] = null;
10604
+ }
10605
+ }
10606
+ exists(id) {
10607
+ const index = this.getInterceptorIndex(id);
10608
+ return Boolean(this.fns[index]);
10609
+ }
10610
+ getInterceptorIndex(id) {
10611
+ if (typeof id === "number") {
10612
+ return this.fns[id] ? id : -1;
10613
+ }
10614
+ return this.fns.indexOf(id);
10615
+ }
10616
+ update(id, fn) {
10617
+ const index = this.getInterceptorIndex(id);
10618
+ if (this.fns[index]) {
10619
+ this.fns[index] = fn;
10620
+ return id;
10621
+ }
10622
+ return false;
10623
+ }
10624
+ use(fn) {
10625
+ this.fns.push(fn);
10626
+ return this.fns.length - 1;
10627
+ }
10628
+ };
10629
+ var createInterceptors = () => ({
10630
+ error: new Interceptors(),
10631
+ request: new Interceptors(),
10632
+ response: new Interceptors()
10633
+ });
10634
+ var defaultQuerySerializer = createQuerySerializer({
10635
+ allowReserved: false,
10636
+ array: {
10637
+ explode: true,
10638
+ style: "form"
10639
+ },
10640
+ object: {
10641
+ explode: true,
10642
+ style: "deepObject"
10643
+ }
10644
+ });
10645
+ var defaultHeaders = {
10646
+ "Content-Type": "application/json"
10647
+ };
10648
+ var createConfig = (override = {}) => ({
10649
+ ...jsonBodySerializer,
10650
+ headers: defaultHeaders,
10651
+ parseAs: "auto",
10652
+ querySerializer: defaultQuerySerializer,
10653
+ ...override
10654
+ });
10655
+
10656
+ // src/client/client/client.gen.ts
10657
+ var createClient = (config = {}) => {
10658
+ let _config = mergeConfigs(createConfig(), config);
10659
+ const getConfig = () => ({ ..._config });
10660
+ const setConfig = (config2) => {
10661
+ _config = mergeConfigs(_config, config2);
10662
+ return getConfig();
10663
+ };
10664
+ const interceptors = createInterceptors();
10665
+ const beforeRequest = async (options) => {
10666
+ const opts = {
10667
+ ..._config,
10668
+ ...options,
10669
+ fetch: options.fetch ?? _config.fetch ?? globalThis.fetch,
10670
+ headers: mergeHeaders(_config.headers, options.headers),
10671
+ serializedBody: void 0
10672
+ };
10673
+ if (opts.security) {
10674
+ await setAuthParams({
10675
+ ...opts,
10676
+ security: opts.security
10677
+ });
10678
+ }
10679
+ if (opts.requestValidator) {
10680
+ await opts.requestValidator(opts);
10681
+ }
10682
+ if (opts.body !== void 0 && opts.bodySerializer) {
10683
+ opts.serializedBody = opts.bodySerializer(opts.body);
10684
+ }
10685
+ if (opts.body === void 0 || opts.serializedBody === "") {
10686
+ opts.headers.delete("Content-Type");
10687
+ }
10688
+ const url = buildUrl(opts);
10689
+ return { opts, url };
10690
+ };
10691
+ const request = async (options) => {
10692
+ const { opts, url } = await beforeRequest(options);
10693
+ for (const fn of interceptors.request.fns) {
10694
+ if (fn) {
10695
+ await fn(opts);
10696
+ }
10697
+ }
10698
+ const _fetch = opts.fetch;
10699
+ const requestInit = {
10700
+ ...opts,
10701
+ body: getValidRequestBody(opts)
10702
+ };
10703
+ let response = await _fetch(url, requestInit);
10704
+ for (const fn of interceptors.response.fns) {
10705
+ if (fn) {
10706
+ response = await fn(response, opts);
10707
+ }
10708
+ }
10709
+ const result = {
10710
+ response
10711
+ };
10712
+ if (response.ok) {
10713
+ const parseAs = (opts.parseAs === "auto" ? getParseAs(response.headers.get("Content-Type")) : opts.parseAs) ?? "json";
10714
+ if (response.status === 204 || response.headers.get("Content-Length") === "0") {
10715
+ let emptyData;
10716
+ switch (parseAs) {
10717
+ case "arrayBuffer":
10718
+ case "blob":
10719
+ case "text":
10720
+ emptyData = await response[parseAs]();
10721
+ break;
10722
+ case "formData":
10723
+ emptyData = new FormData();
10724
+ break;
10725
+ case "stream":
10726
+ emptyData = response.body;
10727
+ break;
10728
+ case "json":
10729
+ default:
10730
+ emptyData = {};
10731
+ break;
10732
+ }
10733
+ return {
10734
+ data: emptyData,
10735
+ ...result
10736
+ };
10737
+ }
10738
+ let data;
10739
+ switch (parseAs) {
10740
+ case "arrayBuffer":
10741
+ case "blob":
10742
+ case "formData":
10743
+ case "json":
10744
+ case "text":
10745
+ data = await response[parseAs]();
10746
+ break;
10747
+ case "stream":
10748
+ return {
10749
+ data: response.body,
10750
+ ...result
10751
+ };
10752
+ }
10753
+ if (parseAs === "json") {
10754
+ if (opts.responseValidator) {
10755
+ await opts.responseValidator(data);
10756
+ }
10757
+ if (opts.responseTransformer) {
10758
+ data = await opts.responseTransformer(data);
10759
+ }
10942
10760
  }
10761
+ return {
10762
+ data,
10763
+ ...result
10764
+ };
10943
10765
  }
10944
- }
10945
- return { stream: createStream() };
10766
+ const textError = await response.text();
10767
+ let jsonError;
10768
+ try {
10769
+ jsonError = JSON.parse(textError);
10770
+ } catch {
10771
+ }
10772
+ const error = jsonError ?? textError;
10773
+ let finalError = error;
10774
+ for (const fn of interceptors.error.fns) {
10775
+ if (fn) {
10776
+ finalError = await fn(error, response, opts);
10777
+ }
10778
+ }
10779
+ finalError = finalError || {};
10780
+ if (opts.throwOnError) {
10781
+ throw finalError;
10782
+ }
10783
+ return {
10784
+ error: finalError,
10785
+ ...result
10786
+ };
10787
+ };
10788
+ const makeMethodFn = (method) => (options) => request({ ...options, method });
10789
+ const makeSseFn = (method) => async (options) => {
10790
+ const { opts, url } = await beforeRequest(options);
10791
+ return createSseClient({
10792
+ ...opts,
10793
+ body: opts.body,
10794
+ headers: opts.headers,
10795
+ method,
10796
+ onRequest: async (url2, init) => {
10797
+ let request2 = new Request(url2, init);
10798
+ const requestInit = {
10799
+ ...init,
10800
+ method: init.method,
10801
+ url: url2
10802
+ };
10803
+ for (const fn of interceptors.request.fns) {
10804
+ if (fn) {
10805
+ await fn(requestInit);
10806
+ request2 = new Request(requestInit.url, requestInit);
10807
+ }
10808
+ }
10809
+ return request2;
10810
+ },
10811
+ url
10812
+ });
10813
+ };
10814
+ return {
10815
+ buildUrl,
10816
+ connect: makeMethodFn("CONNECT"),
10817
+ delete: makeMethodFn("DELETE"),
10818
+ get: makeMethodFn("GET"),
10819
+ getConfig,
10820
+ head: makeMethodFn("HEAD"),
10821
+ interceptors,
10822
+ options: makeMethodFn("OPTIONS"),
10823
+ patch: makeMethodFn("PATCH"),
10824
+ post: makeMethodFn("POST"),
10825
+ put: makeMethodFn("PUT"),
10826
+ request,
10827
+ setConfig,
10828
+ sse: {
10829
+ connect: makeSseFn("CONNECT"),
10830
+ delete: makeSseFn("DELETE"),
10831
+ get: makeSseFn("GET"),
10832
+ head: makeSseFn("HEAD"),
10833
+ options: makeSseFn("OPTIONS"),
10834
+ patch: makeSseFn("PATCH"),
10835
+ post: makeSseFn("POST"),
10836
+ put: makeSseFn("PUT"),
10837
+ trace: makeSseFn("TRACE")
10838
+ },
10839
+ trace: makeMethodFn("TRACE")
10840
+ };
10841
+ };
10842
+
10843
+ // src/client/client.gen.ts
10844
+ init_clientConfig();
10845
+ var client = createClient(createClientConfig(createConfig()));
10846
+
10847
+ // src/client/sdk.gen.ts
10848
+ var getApiV1CreditsBalance = (options) => {
10849
+ return (options?.client ?? client).get({
10850
+ url: "/api/v1/credits/balance",
10851
+ ...options
10852
+ });
10853
+ };
10854
+ var getApiV1CreditsPacks = (options) => {
10855
+ return (options?.client ?? client).get({
10856
+ url: "/api/v1/credits/packs",
10857
+ ...options
10858
+ });
10859
+ };
10860
+ var postApiV1CreditsPurchase = (options) => {
10861
+ return (options.client ?? client).post({
10862
+ url: "/api/v1/credits/purchase",
10863
+ ...options,
10864
+ headers: {
10865
+ "Content-Type": "application/json",
10866
+ ...options.headers
10867
+ }
10868
+ });
10869
+ };
10870
+ var getApiV1Models = (options) => {
10871
+ return (options?.client ?? client).get({
10872
+ url: "/api/v1/models",
10873
+ ...options
10874
+ });
10946
10875
  };
10947
10876
 
10948
10877
  // src/react/useCredits.ts
10949
- var import_react = require("react");
10950
- init_sdk_gen();
10951
10878
  init_clientConfig();
10952
10879
  function useCredits(options = {}) {
10953
10880
  const { getToken, baseUrl = BASE_URL, autoFetch = true, onError } = options;
@@ -11106,7 +11033,6 @@ function useCredits(options = {}) {
11106
11033
 
11107
11034
  // src/react/useModels.ts
11108
11035
  var import_react2 = require("react");
11109
- init_sdk_gen();
11110
11036
  init_clientConfig();
11111
11037
  function useModels(options = {}) {
11112
11038
  const { getToken, baseUrl = BASE_URL, provider, autoFetch = true } = options;
@@ -11345,6 +11271,7 @@ function useChat(options) {
11345
11271
  memoryContext,
11346
11272
  searchContext,
11347
11273
  fileContext,
11274
+ toolGuidance,
11348
11275
  // Responses API options
11349
11276
  temperature,
11350
11277
  maxOutputTokens,
@@ -11416,6 +11343,13 @@ function useChat(options) {
11416
11343
  };
11417
11344
  messagesWithContext = [fileSystemMessage, ...messagesWithContext];
11418
11345
  }
11346
+ if (toolGuidance) {
11347
+ const toolGuidanceMessage = {
11348
+ role: "system",
11349
+ content: [{ type: "text", text: toolGuidance }]
11350
+ };
11351
+ messagesWithContext = [toolGuidanceMessage, ...messagesWithContext];
11352
+ }
11419
11353
  const result = await runToolLoop({
11420
11354
  messages: messagesWithContext,
11421
11355
  model,
@@ -13958,8 +13892,12 @@ init_recall();
13958
13892
 
13959
13893
  // src/lib/memory/recallTool.ts
13960
13894
  init_recall();
13895
+
13896
+ // src/lib/memory/recallConstants.ts
13961
13897
  var RECALL_TOOL_NAME = "recall_memory";
13962
13898
  var RECALL_MAX_LIMIT = 50;
13899
+
13900
+ // src/lib/memory/recallTool.ts
13963
13901
  var DEFAULT_LIMIT2 = 8;
13964
13902
  var DEFAULT_BUDGET2 = "low";
13965
13903
  function formatEventTime(start, end, kind) {
@@ -14751,12 +14689,121 @@ function createMemoryVaultTool(vaultCtx, options, embeddingOptions, cache) {
14751
14689
  // src/expo/useChatStorage.ts
14752
14690
  init_redactor();
14753
14691
 
14692
+ // src/lib/tools/clientToolSelection.ts
14693
+ init_generate();
14694
+
14695
+ // src/tools/appBuilderPrompt.ts
14696
+ var APP_BUILDER_PROMPT = `App Builder tools (create_file, patch_file, and the rest described below) are available this turn, but they are OPTIONAL \u2014 having them does not mean you should build anything.
14697
+
14698
+ Build ONLY when the user gives an explicit instruction to build, create, generate, or change an app, tool, or UI \u2014 e.g. "build a todo app", "make a snake game", "add a footer to the app", "change the background to blue". A bare app name or feature reference is NOT a build instruction: if the user just says "todo app", "a snake game", or "qr code", or asks a question about an app, do NOT generate anything \u2014 reply normally, or ask what they'd like you to build. The same holds for greetings, questions, stories, explanations, advice, or code that isn't a runnable app: respond normally and do not create files. When it's unclear whether the user actually wants an app built, ask instead of building.
14699
+
14700
+ Once the user HAS given an explicit build instruction, you are in App Builder mode: you produce polished, production-quality React apps that feel designed \u2014 not generic.
14701
+
14702
+ DESIGN: on the initial build, before writing code, briefly state (2-3 sentences) what you want this app to feel like \u2014 name the specific fonts, the palette in hex, and one signature detail. Be concrete: "Fraunces display + Inter body + JetBrains Mono metadata, terracotta #b75432 accent on bone #f3efe6, paper-grain texture via inline SVG noise" is the kind of specificity that anchors the build. Default to nothing: don't pick "safe modern productivity" unless the brief literally asks for utilitarian. On later turns, state a design brief only when the change is visual (a restyle, new theme, or new UI); for a logic, data, or behavior-only change, skip the brief and just make the change.
14703
+
14704
+ WORKFLOW:
14705
+ 1. NEVER output code as text or markdown. ALWAYS use tools.
14706
+ 2. Initial build: state your design brief, then create_file with a "files" array writing ALL files in one call. The preview renders automatically.
14707
+ 3. patch_file requires read_file for that path earlier in the conversation (or create_file in the same conversation, which counts). read_file output is numbered "42: <text>" \u2014 the prefix is display-only; never copy it into "find". Each "find" must match exactly one location \u2014 include 2-3 lines of surrounding context. Pass multiple patches per call to change distinct locations.
14708
+ 4. Prefer patch_file for changes to existing files \u2014 surgical, reviewable, preserves surrounding code. Reserve create_file overwrites for substantial restructuring. Inserts: find the line before, replace with that line + the new line. Deletes: empty replace.
14709
+ 5. After substantial changes, call critique_design first (taste, hierarchy, distinctiveness, weakest decision) and then audit_design (mechanical: tokens, focus states, aria-labels, alt, heading order). Patch what each surfaces. critique catches taste issues no checklist would; audit catches mechanics you'd miss. Run both; act on both.
14710
+ 6. Before declaring done, call verify_app. The host's preview runtime reports whether your app actually mounted and surfaces any errors it captured \u2014 hallucinated imports, syntax the bundler rejected, undefined components, runtime crashes. If \`rendered\` is false or \`errors\` is non-empty, fix every error before the next message. If the response includes a \`note\` that verify_app isn't wired in this host, skip \u2014 audit + critique are your feedback in that case.
14711
+ 7. Keep responses to a sentence or two. Exception: when responding to critique_design, write a substantive paragraph per rubric question.
14712
+
14713
+ STRUCTURE:
14714
+ - App.js: default-export React component. For non-trivial apps, define small co-located helpers (Header, CardList, Card, EditDialog, \u2026) above the default export rather than one giant App.
14715
+ - App.css: all styles, imported in App.js. No inline style objects.
14716
+ - package.json: list ALL imported packages including react. No CDN script tags. Versions are auto-pinned.
14717
+ - Do NOT create index.js or index.html (auto-generated).
14718
+
14719
+ STATE & PERSISTENCE:
14720
+ - The code runs in the user's browser \u2014 standard browser APIs available (localStorage, URL hash, Date, fetch).
14721
+ - Persist user-created data (todos, cards, notes, settings) with localStorage so it survives refresh. Use a versioned key ("kanban.v1") and JSON.parse inside try/catch on read.
14722
+ - Treat persisted data as untrusted and possibly stale: a saved record may predate a field you just added (a timestamp, id, or setting), so default-fill or skip missing fields on read \u2014 never assume a loaded record has every field the current code expects. Bump the key version when the shape changes.
14723
+ - Lift state to App when multiple children need it; keep it local otherwise. Extract non-trivial logic into custom hooks (useTodos, useTimer).
14724
+ - useReducer for state shapes with several interrelated fields (cards + columns + filters + drag state).
14725
+
14726
+ FILE & IMAGE UPLOAD:
14727
+ - Picker: \`<input type="file" accept="image/*" multiple>\` (vary accept by use case: \`.csv,text/csv\`, \`.txt,.md,text/plain\`, etc.).
14728
+ - Drag-and-drop: call \`e.preventDefault()\` in onDragOver (otherwise drop never fires); read files from \`event.dataTransfer.files\`. Drop and picker should coexist.
14729
+ - Previews: \`<img src={URL.createObjectURL(file)} />\`. Call \`URL.revokeObjectURL\` on unmount or before replacing \u2014 otherwise the browser leaks memory.
14730
+ - localStorage cannot hold large binary. Persist only metadata for big files; re-upload on next visit (or use IndexedDB if you really need durable binary storage).
14731
+ - Validate size and type before processing; reject files larger than the app can hold in memory.
14732
+
14733
+ AI CAPABILITIES \u2014 \`window.app.complete(prompt: string): Promise<string>\` is available in this environment. Build for the real call.
14734
+ - When the app's core purpose is conversation, generation, grading, recommendation, or any natural-language task, this IS the implementation \u2014 call it. NEVER fake it: no hardcoded or canned replies, no random or \`setTimeout\` "thinking", no demo-mode stub. A chat app, tutor, or assistant that doesn't call \`window.app.complete\` is wrong, not a placeholder.
14735
+ - Don't force it where it isn't needed \u2014 a calculator, todo list, or timer has no reason to call it. Match the implementation to what the app actually does.
14736
+ - Async: wrap in try/catch, show a loading state, and disable dependent inputs while waiting. Surface failures gracefully ("Couldn't reach the AI \u2014 try again").
14737
+ - Structure prompts with clear sections (Role / Context / Task / Format) and pass the relevant state (e.g. the running conversation) as context. Render the response as untrusted text: \`white-space: pre-wrap\`, never dangerouslySetInnerHTML.
14738
+ - Skip the call only when the answer is genuinely static \u2014 no language or reasoning needed. Cache repeated prompts in component state when sensible.
14739
+
14740
+ CODE QUALITY \u2014 write as a senior engineer would:
14741
+ - Handle edge cases (empty inputs, division by zero, invalid data). Show helpful error states, not crashes.
14742
+ - Never let formatting throw during render: guard \`new Date(x)\` / \`Intl\` / \`toLocaleString\` / \`.toFixed\` against missing or invalid values (e.g. \`Number.isNaN(d.getTime())\`) and return a fallback \u2014 one bad value from storage or state must not blank the whole app.
14743
+ - Semantic HTML: real headings, labels, buttons (not divs with onClick), form elements.
14744
+ - Accessible: aria-labels on icon buttons, focus-visible styles, keyboard navigable.
14745
+ - Extract into custom hooks or helpers when a component grows past ~80 lines.
14746
+
14747
+ STYLING \u2014 Tailwind is the CHASSIS, custom CSS carries the IDENTITY:
14748
+ - Tailwind (via Play CDN) for layout, spacing, state (hover/focus), responsive prefixes \u2014 uniform structure.
14749
+ - App.css carries the identity: @import the Google Fonts, declare CSS custom properties (--bg, --panel, --card, --ink, --muted, --accent, --hairline, --radius, --radius-sm, --shadow-card, --shadow-card-hi) in :root, gradients/textures/signature animations Tailwind can't express. Reference tokens from Tailwind via arbitrary values (\`className="bg-[var(--bg)]"\`) when needed. A well-designed app has 60-200 lines of App.css \u2014 not zero.
14750
+ - Modern CSS is how apps stop reading as generic-AI-output: \`color-mix(in oklab, var(--accent) 14%, transparent)\` for tinting, \`oklch()\` / \`oklab()\` for perceptually-uniform color, \`:has()\`, \`conic-gradient\` / \`radial-gradient\`, \`backdrop-filter\`, \`@container\` queries, inline SVG \`feTurbulence\` for texture.
14751
+ - Don't pull additional CSS frameworks. Tailwind + custom CSS is enough.
14752
+
14753
+ VISUAL DESIGN \u2014 every app should look like a real product, not a wireframe:
14754
+ - Let the chosen aesthetic drive every decision. "Editorial" wants italic display type and restrained color; "retro arcade" wants chunky shadows and saturated gradients. No safe-modern defaults.
14755
+ - Atmospheric depth: subtle radial wash, paper grain (inline SVG noise), warm/cool tint \u2014 every canvas should feel like a designed space, not a #fff rectangle.
14756
+ - Three shadow tiers (rest / hover / active). Pair drop shadows with inset highlights (\`box-shadow: 0 1px 0 rgba(255,255,255,.9) inset, 0 6px 14px -10px ...\`) for tactile feel.
14757
+ - Typography hierarchy: display font for headlines (italic accents on key words via \`<em>\`), body font for content, mono for metadata/labels \u2014 different families telegraph different information types.
14758
+ - Interactive polish: hover/active/focus on every clickable element, 200-300ms cubic-bezier transitions, keyboard shortcuts where natural (Esc to cancel, Cmd/Ctrl+Enter to commit), focus-visible rings keyed to the accent.
14759
+ - Light/dark mode if reasonable: a single CSS variable inversion under \`[data-theme="dark"]\`.
14760
+ - Customization affordance for multi-session apps: small "Tweaks" panel with theme / density / accent. Skip for throwaway demos.
14761
+ - Responsive: Tailwind responsive prefixes (\`sm:\`, \`md:\`, \`lg:\`) and \`@media\` in App.css. Mobile-first.`;
14762
+
14763
+ // src/tools/document/documentBuilderPrompt.ts
14764
+ var DOCUMENT_BUILDER_PROMPT = `Document tools (create_document, read_document, patch_document) are available this turn, but they are OPTIONAL \u2014 having them does not mean you should produce a document.
14765
+
14766
+ Use them ONLY when the user explicitly asks you to draft, write, compose, or generate a document \u2014 a contract, agreement, NDA, letter, cover letter, memo, essay, report, brief, proposal, or resume \u2014 e.g. "draft me a service agreement", "write a cover letter for this job", "make a one-page memo about X". A bare topic, a question, a request for an explanation, or a request for a slide deck or an interactive app is NOT a document instruction: respond normally and do not call these tools. When it's unclear whether the user wants a finished document, ask instead.
14767
+
14768
+ When you DO author a document, the document body goes entirely into the tool \u2014 NEVER write the document text as chat prose. The tool renders your source to a real, paginated PDF and attaches it to your reply automatically. AFTER the tool returns, briefly confirm in ONE short sentence what you made (e.g. "Drafted a cover letter for the product manager role \u2014 see the attached PDF."). Do not restate the document's contents.
14769
+
14770
+ AUTHORING \u2014 write the "source" as a single @react-pdf/renderer JSX expression (this is react-pdf, NOT HTML and NOT a slide deck):
14771
+ - Root is exactly one <Document> with at least one <Page>. Its only children are <Page> elements. Add more <Page> elements for longer documents \u2014 react-pdf also paginates overflow automatically.
14772
+ - Inside a <Page>: use <View> as flex containers and <Text> for ALL text. Raw text is only allowed inside <Text> (and <Link>); a <View> or <Page> may not contain bare text.
14773
+ - <Text> may nest <Text>, <Link>, or <Note> for inline styling, hyperlinks, and (rarely) a PDF comment annotation. A <Link> src must be an ordinary web link \u2014 http(s), mailto, tel, or a relative/anchor link; javascript:/data:/file: links are rejected. <Image> is self-closing and its src MUST be an inline "data:" URI \u2014 remote image URLs are rejected for privacy. Vector primitives (<Rect>/<Circle>/<Path>/etc.) must live inside an <Svg>.
14774
+ - Styling is react-pdf style objects only: style={{ marginTop: 12, fontSize: 11, fontFamily: "Times-Roman", flexDirection: "column" }}. Flexbox only \u2014 there is NO CSS grid, float, or box-shadow. Use camelCase react-pdf keys (margin/padding, flex*, width/height, color/backgroundColor, border*, text*, etc.).
14775
+ - fontFamily must be a built-in PDF font: Helvetica, Times-Roman, or Courier (with -Bold / -Oblique / -Italic / -BoldOblique variants), or Symbol / ZapfDingbats. Custom fonts are not supported.
14776
+ - LITERAL VALUES ONLY: no variables, no JS expressions, no .map(), no event handlers (onClick, \u2026), no spreads. Every attribute is a literal string, number, boolean, or a style object of literals.
14777
+
14778
+ A minimal example:
14779
+
14780
+ <Document>
14781
+ <Page size="A4" style={{ paddingVertical: 56, paddingHorizontal: 64, fontFamily: "Times-Roman", fontSize: 11, lineHeight: 1.5 }}>
14782
+ <Text style={{ fontSize: 18, fontFamily: "Times-Bold", marginBottom: 16, textAlign: "center" }}>MUTUAL NON-DISCLOSURE AGREEMENT</Text>
14783
+ <Text style={{ marginBottom: 10 }}>This Mutual Non-Disclosure Agreement (the "Agreement") is entered into as of the date of last signature below.</Text>
14784
+ <View style={{ marginTop: 14 }}>
14785
+ <Text style={{ fontFamily: "Times-Bold", marginBottom: 4 }}>1. Confidential Information</Text>
14786
+ <Text>Each party may disclose certain confidential and proprietary information to the other party.</Text>
14787
+ </View>
14788
+ </Page>
14789
+ </Document>
14790
+
14791
+ WORKFLOW:
14792
+ 1. To create or fully restructure a document, call create_document with the complete source.
14793
+ 2. To revise an existing document, call read_document first (its output is line-numbered "42: <text>" \u2014 the prefix is display-only, never copy it into a "find"), then patch_document with small find/replace edits. Each "find" must match exactly one location; add 2-3 lines of surrounding context if needed. Prefer patching over rewriting.
14794
+ 3. To attach several separate documents in one reply, call create_document multiple times with distinct documentId slugs (e.g. "nda", "cover-letter"). Each renders to its own PDF.
14795
+ 4. If a tool reports a DSL error (with line:col) or a render failure, fix the source and retry \u2014 do not give up or fall back to writing the document as chat text.`;
14796
+
14754
14797
  // src/lib/tools/serverTools.ts
14755
14798
  init_logger();
14756
14799
  init_vector();
14757
14800
  var DEFAULT_CACHE_EXPIRATION_MS = 24 * 60 * 60 * 1e3;
14758
14801
  var SERVER_TOOLS_CACHE_KEY = "sdk_server_tools_cache";
14759
14802
  var CACHE_VERSION = "1.3";
14803
+ var MIN_CONTENT_LENGTH_FOR_TOOLS = 5;
14804
+ var MAX_CLIENT_TOOLS_AFTER_FILTER = 10;
14805
+ var CLIENT_TOOLS_MIN_SIMILARITY = 0.53;
14806
+ var CLIENT_TOOLS_RELEVANCE_RATIO = 0.75;
14760
14807
  function isNewToolFormat(tool) {
14761
14808
  return "schema" in tool && tool.schema !== void 0;
14762
14809
  }
@@ -14816,7 +14863,7 @@ function getCachedServerTools() {
14816
14863
  if (!cached) return null;
14817
14864
  const parsed = JSON.parse(cached);
14818
14865
  if (parsed.version !== CACHE_VERSION) {
14819
- clearServerToolsCache();
14866
+ removeLocalStorageCache();
14820
14867
  return null;
14821
14868
  }
14822
14869
  return parsed;
@@ -14828,24 +14875,46 @@ function isCacheExpired(cache, expirationMs = DEFAULT_CACHE_EXPIRATION_MS) {
14828
14875
  if (!cache) return true;
14829
14876
  return Date.now() - cache.timestamp > expirationMs;
14830
14877
  }
14831
- function cacheServerTools(tools, checksum) {
14832
- if (typeof localStorage === "undefined") return;
14833
- const cacheData = {
14878
+ function buildCacheEntry(tools, checksum) {
14879
+ return {
14834
14880
  tools,
14835
14881
  timestamp: Date.now(),
14836
14882
  version: CACHE_VERSION,
14837
14883
  ...checksum && { checksum }
14838
14884
  };
14885
+ }
14886
+ function writeLocalStorageCache(entry) {
14887
+ if (typeof localStorage === "undefined") return;
14839
14888
  try {
14840
- localStorage.setItem(SERVER_TOOLS_CACHE_KEY, JSON.stringify(cacheData));
14889
+ localStorage.setItem(SERVER_TOOLS_CACHE_KEY, JSON.stringify(entry));
14841
14890
  } catch (error) {
14842
14891
  getLogger().warn("[serverTools] Failed to cache tools:", error);
14843
14892
  }
14844
14893
  }
14845
- function clearServerToolsCache() {
14894
+ function removeLocalStorageCache() {
14846
14895
  if (typeof localStorage === "undefined") return;
14847
14896
  localStorage.removeItem(SERVER_TOOLS_CACHE_KEY);
14848
14897
  }
14898
+ var localStorageToolsCache = {
14899
+ get: getCachedServerTools,
14900
+ set: writeLocalStorageCache,
14901
+ clear: removeLocalStorageCache
14902
+ };
14903
+ function clearServerToolsCache(cache = localStorageToolsCache) {
14904
+ return cache.clear?.();
14905
+ }
14906
+ function getToolsChecksum2(cache = localStorageToolsCache) {
14907
+ const cached = cache.get();
14908
+ return cached instanceof Promise ? cached.then((c) => c?.checksum) : cached?.checksum;
14909
+ }
14910
+ function shouldRefreshTools(responseChecksum, cache = localStorageToolsCache) {
14911
+ if (!responseChecksum) {
14912
+ return false;
14913
+ }
14914
+ const decide = (cachedChecksum2) => !cachedChecksum2 || cachedChecksum2 !== responseChecksum;
14915
+ const cachedChecksum = getToolsChecksum2(cache);
14916
+ return cachedChecksum instanceof Promise ? cachedChecksum.then(decide) : decide(cachedChecksum);
14917
+ }
14849
14918
  async function fetchServerToolsFromApi(baseUrl, token) {
14850
14919
  const response = await fetch(`${baseUrl}/api/v1/tools`, {
14851
14920
  method: "GET",
@@ -14866,9 +14935,23 @@ async function getServerTools(options) {
14866
14935
  cacheExpirationMs = DEFAULT_CACHE_EXPIRATION_MS,
14867
14936
  forceRefresh = false,
14868
14937
  getToken,
14869
- apiKey
14938
+ apiKey,
14939
+ cache = localStorageToolsCache
14870
14940
  } = options;
14871
- const cached = getCachedServerTools();
14941
+ const persistCache = async (entry) => {
14942
+ try {
14943
+ await cache.set(entry);
14944
+ } catch (error) {
14945
+ getLogger().warn("[serverTools] Cache write failed (non-fatal):", error);
14946
+ }
14947
+ };
14948
+ let rawCached = null;
14949
+ try {
14950
+ rawCached = await cache.get();
14951
+ } catch (error) {
14952
+ getLogger().warn("[serverTools] Cache read failed; falling through to server fetch:", error);
14953
+ }
14954
+ const cached = rawCached && rawCached.version === CACHE_VERSION ? rawCached : null;
14872
14955
  const cacheValid = !isCacheExpired(cached, cacheExpirationMs);
14873
14956
  if (cached && cacheValid && !forceRefresh) {
14874
14957
  return cached.tools;
@@ -14889,7 +14972,7 @@ async function getServerTools(options) {
14889
14972
  }
14890
14973
  const data = await response.json();
14891
14974
  const { tools: tools2, checksum: checksum2 } = convertServerToolsResponse(data);
14892
- cacheServerTools(tools2, checksum2);
14975
+ await persistCache(buildCacheEntry(tools2, checksum2));
14893
14976
  return tools2;
14894
14977
  }
14895
14978
  if (!getToken) {
@@ -14902,7 +14985,7 @@ async function getServerTools(options) {
14902
14985
  return cached?.tools ?? [];
14903
14986
  }
14904
14987
  const { tools, checksum } = await fetchServerToolsFromApi(effectiveBaseUrl, token);
14905
- cacheServerTools(tools, checksum);
14988
+ await persistCache(buildCacheEntry(tools, checksum));
14906
14989
  return tools;
14907
14990
  } catch (error) {
14908
14991
  getLogger().error("[serverTools] Failed to fetch server tools:", error);
@@ -15118,6 +15201,158 @@ function scoreTools(promptEmbeddings, tools) {
15118
15201
  }
15119
15202
  return scores;
15120
15203
  }
15204
+ var BUILT_IN_TOOL_SETS = [
15205
+ {
15206
+ name: "app-generation",
15207
+ // Appended to the base prompt (not replacing it) whenever this set
15208
+ // activates, so the App Builder guidance — including the window.app.complete
15209
+ // runtime-AI contract — rides in with the app-gen tools via the same
15210
+ // semantic selection that includes them. Collected by toolSetSystemPrompts.
15211
+ systemPrompt: APP_BUILDER_PROMPT,
15212
+ // Must stay in sync with APP_FILE_TOOL_NAMES in src/tools/appGeneration.ts.
15213
+ // If a new app-gen tool ships there but isn't added here, semantic
15214
+ // selection will exclude it on every request — the model literally
15215
+ // doesn't see it. The quality tools (audit_design / critique_design /
15216
+ // verify_app) are non-obvious to a user prompt ("build me a kanban")
15217
+ // and rely entirely on set expansion to reach the model.
15218
+ members: [
15219
+ "create_file",
15220
+ "patch_file",
15221
+ "delete_file",
15222
+ "read_file",
15223
+ "list_files",
15224
+ "audit_design",
15225
+ "critique_design",
15226
+ "verify_app"
15227
+ ],
15228
+ // Anchors stay limited to the primary entry-point tools. Users phrase
15229
+ // app intent as "build / make / fix" → semantic match on create_file
15230
+ // or patch_file → full set pulled in. The quality tools fire later in
15231
+ // the workflow and don't anchor on their own; including them as
15232
+ // anchors would risk pulling the set in on unrelated "audit my code"
15233
+ // prompts that have nothing to do with app generation.
15234
+ anchors: ["create_file", "patch_file"],
15235
+ // 0.55: above the global filter floor (0.53) so a barely-relevant match
15236
+ // can't pull in the whole toolkit, but no higher — and we tried higher.
15237
+ // create_file scores chitchat ("what's up" ~0.56, "...about programming"
15238
+ // ~0.57) in the SAME band as legitimate app-EDIT prompts: "Edit the app to
15239
+ // change the background…" measured create_file 0.56 on one e2e run. Raising
15240
+ // the floor to 0.575 to exclude the chitchat therefore also stripped the
15241
+ // file tools from real edit requests — leaving the model unable to touch the
15242
+ // app at all. The bands genuinely overlap; there is no clean cut. So we keep
15243
+ // 0.55 (max recall) and rely on two safety nets instead: APP_BUILDER_PROMPT
15244
+ // is conditional (it no-ops unless the user is actually asking for an app),
15245
+ // and very short greetings ("hey") are filtered out earlier by the
15246
+ // MIN_CONTENT_LENGTH_FOR_TOOLS length gate before embeddings even run.
15247
+ anchorMinSimilarity: 0.55
15248
+ },
15249
+ {
15250
+ name: "slides",
15251
+ members: ["plan_deck", "add_slide", "read_slides", "patch_slides"],
15252
+ anchors: ["plan_deck", "patch_slides"],
15253
+ // Match the client-tool floor (0.53). Short colloquial prompts like
15254
+ // "make me a powerpoint about X" score plan_deck around 0.535 — above
15255
+ // the global floor but inside any anchor gap. plan_deck and patch_slides
15256
+ // are specific enough names that 0.53 won't false-positive in practice.
15257
+ anchorMinSimilarity: 0.53
15258
+ },
15259
+ {
15260
+ name: "documents",
15261
+ // Conditional rider (no-ops unless the user actually asks for a document),
15262
+ // attached so the Document Builder guidance rides in with the document
15263
+ // tools via the same semantic selection that includes them.
15264
+ systemPrompt: DOCUMENT_BUILDER_PROMPT,
15265
+ members: ["create_document", "read_document", "patch_document"],
15266
+ // Entry-point tools anchor; read_document rides in via set expansion.
15267
+ anchors: ["create_document", "patch_document"],
15268
+ // 0.53: match the client-tool floor. "draft me a contract", "write a cover
15269
+ // letter", "make a memo" should pull the set in. Like app-gen's create_file,
15270
+ // document intent ("write me X") overlaps generic chitchat in this band, so
15271
+ // the same two safety nets apply: DOCUMENT_BUILDER_PROMPT is conditional and
15272
+ // the MIN_CONTENT_LENGTH_FOR_TOOLS gate drops bare greetings before
15273
+ // embeddings run. Tune empirically against the tool-selection parity suite.
15274
+ anchorMinSimilarity: 0.53
15275
+ },
15276
+ {
15277
+ name: "github",
15278
+ members: ["github_get_authenticated_user", "github_api"],
15279
+ anchors: ["github_api"],
15280
+ anchorMinSimilarity: 0.55
15281
+ },
15282
+ // Connector sets (#587). Connector chat tools belong to no set, so per-message
15283
+ // semantic filtering silently prunes them on vague prompts ("check my email").
15284
+ // Grouping each connector's tools behind a search/action anchor keeps the
15285
+ // whole connector reachable once any one of its tools matches. Mirrors github:
15286
+ // no systemPrompt rider — per-set prompt guidance is a follow-up.
15287
+ {
15288
+ name: "gmail",
15289
+ members: [
15290
+ "gmail_search_messages",
15291
+ "gmail_get_message",
15292
+ "gmail_create_draft",
15293
+ "gmail_send_message"
15294
+ ],
15295
+ anchors: ["gmail_search_messages", "gmail_send_message"],
15296
+ anchorMinSimilarity: 0.53
15297
+ },
15298
+ {
15299
+ name: "google-calendar",
15300
+ members: [
15301
+ "google_calendar_list_events",
15302
+ "google_calendar_create_event",
15303
+ "google_calendar_update_event"
15304
+ ],
15305
+ anchors: ["google_calendar_list_events", "google_calendar_create_event"],
15306
+ anchorMinSimilarity: 0.53
15307
+ },
15308
+ {
15309
+ name: "google-drive",
15310
+ members: [
15311
+ "google_drive_search",
15312
+ "google_drive_list_recent",
15313
+ "google_drive_get_content",
15314
+ "google_drive_create_file",
15315
+ "google_drive_update_file"
15316
+ ],
15317
+ anchors: ["google_drive_search", "google_drive_get_content", "google_drive_create_file"],
15318
+ anchorMinSimilarity: 0.53
15319
+ },
15320
+ {
15321
+ name: "notion",
15322
+ members: ["notion-search", "notion-fetch", "notion-create-pages", "notion-update-page"],
15323
+ anchors: ["notion-search", "notion-create-pages"],
15324
+ anchorMinSimilarity: 0.53
15325
+ },
15326
+ {
15327
+ name: "x",
15328
+ members: ["x_get_me", "x_get_my_posts"],
15329
+ anchors: ["x_get_me", "x_get_my_posts"],
15330
+ anchorMinSimilarity: 0.53
15331
+ },
15332
+ {
15333
+ name: "slack",
15334
+ members: [
15335
+ "slack_get_me",
15336
+ "slack_list_channels",
15337
+ "slack_search_messages",
15338
+ "slack_list_users",
15339
+ "slack_get_channel_history",
15340
+ "slack_get_thread_replies",
15341
+ "slack_post_message"
15342
+ ],
15343
+ // search is the primary entry point; list_channels also anchors so "what
15344
+ // channels am I in" reaches the set without going through search. post_message
15345
+ // anchors too so post-only prompts surface the write tool past the cutoff.
15346
+ anchors: ["slack_search_messages", "slack_list_channels", "slack_post_message"],
15347
+ anchorMinSimilarity: 0.53
15348
+ },
15349
+ {
15350
+ name: "dropbox",
15351
+ members: ["dropbox_list_folders", "dropbox_get_file_content", "dropbox_search"],
15352
+ anchors: ["dropbox_list_folders", "dropbox_search"],
15353
+ anchorMinSimilarity: 0.53
15354
+ }
15355
+ ];
15121
15356
  function expandToolSetsAdditive(matchedNames, availableNames, scores, toolSets, activeSetNames) {
15122
15357
  const result = new Set(matchedNames);
15123
15358
  for (const ts of toolSets) {
@@ -15135,6 +15370,35 @@ function expandToolSetsAdditive(matchedNames, availableNames, scores, toolSets,
15135
15370
  }
15136
15371
  return result;
15137
15372
  }
15373
+ function activatedToolSetNames(scores, toolSets = BUILT_IN_TOOL_SETS, activeSetNames) {
15374
+ const activated = /* @__PURE__ */ new Set();
15375
+ for (const ts of toolSets) {
15376
+ if (activeSetNames?.has(ts.name)) {
15377
+ activated.add(ts.name);
15378
+ continue;
15379
+ }
15380
+ const minSim = ts.anchorMinSimilarity ?? 0.6;
15381
+ if (ts.anchors.some((a) => (scores.get(a) ?? 0) >= minSim)) {
15382
+ activated.add(ts.name);
15383
+ }
15384
+ }
15385
+ return activated;
15386
+ }
15387
+ function toolSetSystemPrompts(selectedToolNames, toolSets = BUILT_IN_TOOL_SETS, activatedSetNames) {
15388
+ const selected = selectedToolNames instanceof Set ? selectedToolNames : new Set(selectedToolNames);
15389
+ const prompts = [];
15390
+ const seen = /* @__PURE__ */ new Set();
15391
+ for (const ts of toolSets) {
15392
+ const prompt = ts.systemPrompt;
15393
+ if (!prompt || seen.has(prompt)) continue;
15394
+ const active = activatedSetNames ? activatedSetNames.has(ts.name) : ts.anchors.some((anchor) => selected.has(anchor));
15395
+ if (active) {
15396
+ prompts.push(prompt);
15397
+ seen.add(prompt);
15398
+ }
15399
+ }
15400
+ return prompts;
15401
+ }
15138
15402
  function createServerToolsFilter(options = {}) {
15139
15403
  const exclude = new Set(options.excludeTools ?? []);
15140
15404
  const sets = options.toolSets ?? [];
@@ -15219,11 +15483,129 @@ var defaultServerToolsFilter = createServerToolsFilter({
15219
15483
  toolSets: SERVER_TOOL_DEPENDENCY_SETS
15220
15484
  });
15221
15485
 
15486
+ // src/lib/tools/clientToolSelection.ts
15487
+ function getToolName2(t) {
15488
+ const fn = t.function;
15489
+ return fn?.name || t.name || "";
15490
+ }
15491
+ function getToolDescription(t) {
15492
+ const fn = t.function;
15493
+ return fn?.description || t.description || getToolName2(t);
15494
+ }
15495
+ function computeToolGuidance(selectedServerTools, selectedClientTools, extraToolSets, activatedSetNames) {
15496
+ const names = [
15497
+ ...selectedServerTools.map((t) => t.name),
15498
+ ...(selectedClientTools ?? []).map(getToolName2)
15499
+ ].filter(Boolean);
15500
+ const toolSets = extraToolSets.length > 0 ? [...BUILT_IN_TOOL_SETS, ...extraToolSets] : BUILT_IN_TOOL_SETS;
15501
+ const prompts = toolSetSystemPrompts(names, toolSets, activatedSetNames);
15502
+ return prompts.length > 0 ? prompts.join("\n\n") : void 0;
15503
+ }
15504
+ async function autoFilterClientTools(clientTools, promptEmbeddings, cache, embeddingOptions, extraToolSets = [], activeToolSets = [], noEmbeddingsReason = "short-prompt") {
15505
+ const isMemoryTool = (t) => {
15506
+ const name = getToolName2(t);
15507
+ return name.startsWith("memory_vault_") || name === RECALL_TOOL_NAME;
15508
+ };
15509
+ const alwaysInclude = clientTools.filter(isMemoryTool);
15510
+ const filterCandidates = clientTools.filter((t) => !isMemoryTool(t));
15511
+ if (filterCandidates.length === 0) {
15512
+ return { tools: clientTools, activatedSetNames: /* @__PURE__ */ new Set() };
15513
+ }
15514
+ if (!promptEmbeddings && noEmbeddingsReason === "error") {
15515
+ return { tools: clientTools, activatedSetNames: /* @__PURE__ */ new Set() };
15516
+ }
15517
+ if (!promptEmbeddings) {
15518
+ if (activeToolSets.length === 0) {
15519
+ return { tools: [], activatedSetNames: /* @__PURE__ */ new Set() };
15520
+ }
15521
+ const allSets = extraToolSets.length > 0 ? [...BUILT_IN_TOOL_SETS, ...extraToolSets] : BUILT_IN_TOOL_SETS;
15522
+ const activeSets = allSets.filter((s) => activeToolSets.includes(s.name));
15523
+ const stickyMembers = new Set(activeSets.flatMap((s) => s.members));
15524
+ return {
15525
+ tools: [
15526
+ ...alwaysInclude,
15527
+ ...filterCandidates.filter((t) => stickyMembers.has(getToolName2(t)))
15528
+ ],
15529
+ activatedSetNames: new Set(activeSets.map((s) => s.name))
15530
+ };
15531
+ }
15532
+ const toolsNeedingEmbeddings = [];
15533
+ for (const t of filterCandidates) {
15534
+ const name = getToolName2(t);
15535
+ if (name && !cache.has(name)) {
15536
+ toolsNeedingEmbeddings.push({ name, description: getToolDescription(t) });
15537
+ }
15538
+ }
15539
+ if (toolsNeedingEmbeddings.length > 0) {
15540
+ try {
15541
+ const descriptions = toolsNeedingEmbeddings.map((t) => t.description);
15542
+ const embeddings = await generateEmbeddings(descriptions, embeddingOptions);
15543
+ for (let i = 0; i < toolsNeedingEmbeddings.length; i++) {
15544
+ cache.set(toolsNeedingEmbeddings[i].name, embeddings[i]);
15545
+ }
15546
+ } catch {
15547
+ return { tools: clientTools, activatedSetNames: /* @__PURE__ */ new Set() };
15548
+ }
15549
+ }
15550
+ const pseudoServerTools = [];
15551
+ for (const t of filterCandidates) {
15552
+ const name = getToolName2(t);
15553
+ const embedding = cache.get(name);
15554
+ if (!embedding) continue;
15555
+ const fn = t.function;
15556
+ const params = fn?.parameters || fn?.arguments || {
15557
+ type: "object",
15558
+ properties: {},
15559
+ required: []
15560
+ };
15561
+ pseudoServerTools.push({
15562
+ type: "function",
15563
+ name,
15564
+ description: getToolDescription(t),
15565
+ parameters: params,
15566
+ embedding
15567
+ });
15568
+ }
15569
+ const matches = findMatchingTools(promptEmbeddings, pseudoServerTools, {
15570
+ limit: MAX_CLIENT_TOOLS_AFTER_FILTER,
15571
+ minSimilarity: CLIENT_TOOLS_MIN_SIMILARITY,
15572
+ filterAmbiguous: true,
15573
+ // See CLIENT_TOOLS_RELEVANCE_RATIO: 0.75 admits the second intent of a
15574
+ // multi-intent prompt (which lands ~75-80% of the dominant match), while
15575
+ // still trimming the loose tail. The earlier 0.9 made a second intent
15576
+ // structurally unselectable; the anchorMinSimilarity gates (0.53-0.55)
15577
+ // remain the guard against borderline anchors activating a whole set.
15578
+ relevanceRatio: CLIENT_TOOLS_RELEVANCE_RATIO
15579
+ });
15580
+ const matchedNames = new Set(matches.map((m) => m.tool.name));
15581
+ const scores = scoreTools(promptEmbeddings, pseudoServerTools);
15582
+ const availableNames = new Set(filterCandidates.map(getToolName2));
15583
+ const toolSets = extraToolSets.length > 0 ? [...BUILT_IN_TOOL_SETS, ...extraToolSets] : BUILT_IN_TOOL_SETS;
15584
+ const activeSetNames = activeToolSets.length > 0 ? new Set(activeToolSets) : void 0;
15585
+ const finalNames = expandToolSetsAdditive(
15586
+ matchedNames,
15587
+ availableNames,
15588
+ scores,
15589
+ toolSets,
15590
+ activeSetNames
15591
+ );
15592
+ const activatedSetNames = activatedToolSetNames(scores, toolSets, activeSetNames);
15593
+ if (finalNames.size === 0) {
15594
+ return { tools: alwaysInclude, activatedSetNames };
15595
+ }
15596
+ const filtered = filterCandidates.filter((t) => {
15597
+ const name = getToolName2(t);
15598
+ return name && finalNames.has(name);
15599
+ });
15600
+ return { tools: [...alwaysInclude, ...filtered], activatedSetNames };
15601
+ }
15602
+
15222
15603
  // src/expo/useChatStorage.ts
15223
15604
  init_useEncryption();
15224
15605
  var CONVERSATION_REDACTOR_LIMIT = 50;
15225
15606
  var NO_CONVERSATION_KEY = "__no_conversation__";
15226
15607
  var conversationRedactors = /* @__PURE__ */ new Map();
15608
+ var clientToolFilterCache = /* @__PURE__ */ new Map();
15227
15609
  function getConversationRedactor(conversationId, nerDetector) {
15228
15610
  const key = conversationId ?? NO_CONVERSATION_KEY;
15229
15611
  const cached = conversationRedactors.get(key);
@@ -15339,6 +15721,8 @@ function useChatStorage(options) {
15339
15721
  getWalletAddress,
15340
15722
  enableQueue = true,
15341
15723
  autoFlushOnKeyAvailable = true,
15724
+ extraToolSets,
15725
+ activeToolSets,
15342
15726
  serverTools: serverToolsConfig,
15343
15727
  autoEmbedMessages = true,
15344
15728
  embeddingModel = DEFAULT_API_EMBEDDING_MODEL,
@@ -15347,6 +15731,7 @@ function useChatStorage(options) {
15347
15731
  resumable = false,
15348
15732
  onCancelResult,
15349
15733
  onStreamMeta,
15734
+ onToolSelection,
15350
15735
  piiRedaction,
15351
15736
  onPiiRedacted,
15352
15737
  nerDetector,
@@ -15561,12 +15946,24 @@ function useChatStorage(options) {
15561
15946
  if (!autoEmbedMessages || !getToken) return;
15562
15947
  if (message.content.length < minContentLength) return;
15563
15948
  try {
15564
- const embedding = await generateEmbedding(mask(message.content), {
15565
- getToken,
15566
- baseUrl,
15567
- model: embeddingModel
15568
- });
15569
- await updateMessageEmbeddingOp(storageCtx, message.uniqueId, embedding, embeddingModel);
15949
+ const opts = { getToken, baseUrl, model: embeddingModel };
15950
+ if (shouldChunkMessage(message.content, DEFAULT_CHUNK_SIZE)) {
15951
+ const textChunks = chunkText(message.content);
15952
+ const embeddings = await generateEmbeddings(
15953
+ textChunks.map((c) => mask(c.text)),
15954
+ opts
15955
+ );
15956
+ const messageChunks = textChunks.map((chunk, i) => ({
15957
+ text: chunk.text,
15958
+ vector: embeddings[i],
15959
+ startOffset: chunk.startOffset,
15960
+ endOffset: chunk.endOffset
15961
+ }));
15962
+ await updateMessageChunksOp(storageCtx, message.uniqueId, messageChunks, embeddingModel);
15963
+ } else {
15964
+ const embedding = await generateEmbedding(mask(message.content), opts);
15965
+ await updateMessageEmbeddingOp(storageCtx, message.uniqueId, embedding, embeddingModel);
15966
+ }
15570
15967
  } catch (err) {
15571
15968
  getLogger().warn("[useChatStorage] Failed to embed message:", err);
15572
15969
  }
@@ -15916,6 +16313,10 @@ function useChatStorage(options) {
15916
16313
  }
15917
16314
  throw new Error("No conversation ID provided and autoCreateConversation is disabled");
15918
16315
  }, [currentConversationId, getConversation, autoCreateConversation, createConversation]);
16316
+ const activeToolSetsRef = (0, import_react4.useRef)(activeToolSets);
16317
+ activeToolSetsRef.current = activeToolSets;
16318
+ const getTokenRef = (0, import_react4.useRef)(getToken);
16319
+ getTokenRef.current = getToken;
15919
16320
  const sendMessage = (0, import_react4.useCallback)(
15920
16321
  async (args) => {
15921
16322
  const {
@@ -15942,6 +16343,7 @@ function useChatStorage(options) {
15942
16343
  maxOutputTokens,
15943
16344
  clientTools,
15944
16345
  serverTools: serverToolsFilter,
16346
+ clientToolsFilter,
15945
16347
  toolChoice,
15946
16348
  reasoning,
15947
16349
  thinking,
@@ -15968,6 +16370,13 @@ function useChatStorage(options) {
15968
16370
  };
15969
16371
  pendingResumeRef.current = null;
15970
16372
  const effectiveAssistantUniqueId = assistantUniqueId ?? (resumable ? `msg_${(0, import_uuid5.v7)()}` : void 0);
16373
+ const embedToolText = (text12, mask, token) => {
16374
+ const opts = { getToken: token, baseUrl, model: embeddingModel };
16375
+ return shouldChunkMessage(text12, DEFAULT_CHUNK_SIZE) ? generateEmbeddings(
16376
+ chunkText(text12).map((c) => mask(c.text)),
16377
+ opts
16378
+ ) : generateEmbedding(mask(text12), opts);
16379
+ };
15971
16380
  if (walletAddress && signMessage && !hasEncryptionKey(walletAddress)) {
15972
16381
  try {
15973
16382
  await requestEncryptionKey(walletAddress, signMessage, embeddedWalletSigner);
@@ -15978,40 +16387,95 @@ function useChatStorage(options) {
15978
16387
  const effectiveApiType2 = resolveApiType(requestApiType ?? apiType ?? "auto", model);
15979
16388
  const { callPiiRedaction: callPiiRedaction2, maskForCall: maskForCall2 } = resolvePiiForCall(currentConversationId);
15980
16389
  let mergedTools2 = void 0;
15981
- let filteredServerTools = [];
16390
+ let filteredServerTools2 = [];
15982
16391
  const isServerToolsFunction2 = typeof serverToolsFilter === "function";
15983
- if (getToken && effectiveApiType2 === "responses" && !(Array.isArray(serverToolsFilter) && serverToolsFilter.length === 0)) {
16392
+ const extracted2 = extractUserMessageFromMessages(messages);
16393
+ const messageContent = resolveStoredUserContent(
16394
+ storedUserContent,
16395
+ extracted2?.content ?? ""
16396
+ );
16397
+ let skipUserEmbedding;
16398
+ let skipEmbeddingFailed = false;
16399
+ if (getTokenRef.current && effectiveApiType2 === "responses" && !(Array.isArray(serverToolsFilter) && serverToolsFilter.length === 0)) {
15984
16400
  try {
15985
16401
  const allServerTools = await getServerTools({
15986
16402
  baseUrl,
15987
16403
  cacheExpirationMs: serverToolsConfig?.cacheExpirationMs,
15988
- getToken
16404
+ getToken: getTokenRef.current,
16405
+ cache: serverToolsConfig?.cache
15989
16406
  });
15990
- if (isServerToolsFunction2) {
15991
- const extracted2 = extractUserMessageFromMessages(messages);
15992
- const messageContent = resolveStoredUserContent(
15993
- storedUserContent,
15994
- extracted2?.content ?? ""
15995
- );
15996
- if (messageContent.length >= minContentLength) {
15997
- const embedding = await generateEmbedding(maskForCall2(messageContent), {
15998
- getToken,
15999
- baseUrl,
16000
- model: embeddingModel
16001
- });
16002
- const toolNames = serverToolsFilter(embedding, allServerTools);
16003
- filteredServerTools = filterServerTools(allServerTools, toolNames);
16004
- } else {
16005
- filteredServerTools = allServerTools;
16407
+ if (serverToolsConfig?.deferLoading?.enabled) {
16408
+ filteredServerTools2 = allServerTools;
16409
+ } else if (isServerToolsFunction2) {
16410
+ if (messageContent.length >= MIN_CONTENT_LENGTH_FOR_TOOLS) {
16411
+ skipUserEmbedding = await embedToolText(
16412
+ messageContent,
16413
+ maskForCall2,
16414
+ getTokenRef.current
16415
+ );
16416
+ const toolNames = serverToolsFilter(skipUserEmbedding, allServerTools);
16417
+ filteredServerTools2 = filterServerTools(allServerTools, toolNames);
16006
16418
  }
16007
16419
  } else {
16008
- filteredServerTools = filterServerTools(allServerTools, serverToolsFilter);
16420
+ filteredServerTools2 = filterServerTools(allServerTools, serverToolsFilter);
16009
16421
  }
16010
16422
  } catch {
16011
16423
  }
16012
16424
  }
16013
- if (filteredServerTools.length > 0 || clientTools && clientTools.length > 0) {
16014
- mergedTools2 = mergeTools(filteredServerTools, clientTools, effectiveApiType2);
16425
+ let narrowedClientTools2 = clientTools;
16426
+ let clientActivatedSetNames2;
16427
+ if (clientTools?.length) {
16428
+ try {
16429
+ if (!skipUserEmbedding && getTokenRef.current && messageContent.length >= MIN_CONTENT_LENGTH_FOR_TOOLS) {
16430
+ try {
16431
+ skipUserEmbedding = await embedToolText(
16432
+ messageContent,
16433
+ maskForCall2,
16434
+ getTokenRef.current
16435
+ );
16436
+ } catch {
16437
+ skipEmbeddingFailed = true;
16438
+ }
16439
+ }
16440
+ if (typeof clientToolsFilter === "function") {
16441
+ if (!skipEmbeddingFailed) {
16442
+ const keep = new Set(clientToolsFilter(skipUserEmbedding ?? null, clientTools));
16443
+ narrowedClientTools2 = clientTools.filter((t) => keep.has(getToolName2(t)));
16444
+ }
16445
+ } else if (getTokenRef.current) {
16446
+ const { tools: autoTools, activatedSetNames } = await autoFilterClientTools(
16447
+ clientTools,
16448
+ skipUserEmbedding ?? null,
16449
+ clientToolFilterCache,
16450
+ { getToken: getTokenRef.current, baseUrl, model: embeddingModel },
16451
+ extraToolSets ?? [],
16452
+ activeToolSetsRef.current ?? [],
16453
+ skipEmbeddingFailed ? "error" : "short-prompt"
16454
+ );
16455
+ narrowedClientTools2 = autoTools;
16456
+ clientActivatedSetNames2 = activatedSetNames;
16457
+ }
16458
+ } catch (error) {
16459
+ getLogger().warn("[useChatStorage] client tool filtering failed (skipStorage):", error);
16460
+ }
16461
+ }
16462
+ if (filteredServerTools2.length > 0 || narrowedClientTools2 && narrowedClientTools2.length > 0) {
16463
+ mergedTools2 = mergeTools(
16464
+ filteredServerTools2,
16465
+ narrowedClientTools2,
16466
+ effectiveApiType2,
16467
+ serverToolsConfig?.deferLoading
16468
+ );
16469
+ }
16470
+ if (onToolSelection) {
16471
+ try {
16472
+ onToolSelection({
16473
+ prompt: messageContent,
16474
+ clientToolNames: (narrowedClientTools2 ?? []).map(getToolName2).filter(Boolean),
16475
+ serverToolNames: filteredServerTools2.map((t) => t.name)
16476
+ });
16477
+ } catch {
16478
+ }
16015
16479
  }
16016
16480
  const result2 = await baseSendMessage({
16017
16481
  messages,
@@ -16021,6 +16485,12 @@ function useChatStorage(options) {
16021
16485
  memoryContext,
16022
16486
  searchContext,
16023
16487
  fileContext,
16488
+ toolGuidance: computeToolGuidance(
16489
+ filteredServerTools2,
16490
+ narrowedClientTools2,
16491
+ extraToolSets ?? [],
16492
+ clientActivatedSetNames2
16493
+ ),
16024
16494
  temperature,
16025
16495
  maxOutputTokens,
16026
16496
  tools: mergedTools2,
@@ -16048,6 +16518,24 @@ function useChatStorage(options) {
16048
16518
  error: result2.error || "Unknown error"
16049
16519
  };
16050
16520
  }
16521
+ const skipRefreshGetToken = getTokenRef.current;
16522
+ const skipRefreshData = result2.data;
16523
+ if (skipRefreshGetToken) {
16524
+ Promise.resolve().then(
16525
+ () => shouldRefreshTools(getToolsChecksum(skipRefreshData), serverToolsConfig?.cache)
16526
+ ).then((refresh) => {
16527
+ if (refresh) {
16528
+ return getServerTools({
16529
+ baseUrl,
16530
+ getToken: skipRefreshGetToken,
16531
+ forceRefresh: true,
16532
+ cache: serverToolsConfig?.cache
16533
+ });
16534
+ }
16535
+ }).catch((err) => {
16536
+ getLogger().warn("[useChatStorage] Failed to refresh server tools cache:", err);
16537
+ });
16538
+ }
16051
16539
  return {
16052
16540
  data: result2.data,
16053
16541
  error: null,
@@ -16086,12 +16574,12 @@ function useChatStorage(options) {
16086
16574
  if (includeHistory) {
16087
16575
  const validMessages = storedMessages.filter((msg) => !msg.error);
16088
16576
  const limitedMessages = validMessages.slice(-maxHistoryMessages);
16089
- if (summarizeHistory && !getToken) {
16577
+ if (summarizeHistory && !getTokenRef.current) {
16090
16578
  getLogger().warn(
16091
16579
  "[summarize] summarizeHistory is enabled but getToken is not provided \u2014 summarization will be skipped"
16092
16580
  );
16093
16581
  }
16094
- const summaryToken = summarizeHistory && getToken ? await getToken() : null;
16582
+ const summaryToken = summarizeHistory && getTokenRef.current ? await getTokenRef.current() : null;
16095
16583
  const { messagesToConvert, summarySystemMessage } = await maybeSummarizeHistory({
16096
16584
  database,
16097
16585
  conversationId: convId,
@@ -16150,50 +16638,131 @@ function useChatStorage(options) {
16150
16638
  const effectiveApiType = resolveApiType(requestApiType ?? apiType ?? "auto", model);
16151
16639
  let mergedTools = clientTools;
16152
16640
  let userMessageEmbedding;
16641
+ let userMessageEmbeddingFailed = false;
16642
+ let filteredServerTools = [];
16153
16643
  const isServerToolsFunction = typeof serverToolsFilter === "function";
16154
- if (getToken && !(Array.isArray(serverToolsFilter) && serverToolsFilter.length === 0)) {
16644
+ if (getTokenRef.current && !(Array.isArray(serverToolsFilter) && serverToolsFilter.length === 0)) {
16155
16645
  try {
16156
16646
  const allServerTools = await getServerTools({
16157
16647
  baseUrl,
16158
16648
  cacheExpirationMs: serverToolsConfig?.cacheExpirationMs,
16159
- getToken
16649
+ getToken: getTokenRef.current,
16650
+ cache: serverToolsConfig?.cache
16160
16651
  });
16161
- let filteredServerTools;
16162
- if (isServerToolsFunction) {
16163
- if (contentForStorage.length >= minContentLength) {
16164
- userMessageEmbedding = await generateEmbedding(maskForCall(contentForStorage), {
16165
- getToken,
16166
- baseUrl,
16167
- model: embeddingModel
16168
- });
16652
+ if (serverToolsConfig?.deferLoading?.enabled && effectiveApiType === "responses") {
16653
+ filteredServerTools = allServerTools;
16654
+ } else if (isServerToolsFunction) {
16655
+ if (contentForStorage.length >= MIN_CONTENT_LENGTH_FOR_TOOLS) {
16656
+ userMessageEmbedding = await embedToolText(
16657
+ contentForStorage,
16658
+ maskForCall,
16659
+ getTokenRef.current
16660
+ );
16169
16661
  const toolNames = serverToolsFilter(userMessageEmbedding, allServerTools);
16170
16662
  filteredServerTools = filterServerTools(allServerTools, toolNames);
16171
- } else {
16172
- filteredServerTools = allServerTools;
16173
16663
  }
16174
16664
  } else {
16175
16665
  filteredServerTools = filterServerTools(allServerTools, serverToolsFilter);
16176
16666
  }
16177
16667
  if (filteredServerTools.length > 0) {
16178
- mergedTools = mergeTools(filteredServerTools, clientTools, effectiveApiType);
16668
+ mergedTools = mergeTools(
16669
+ filteredServerTools,
16670
+ clientTools,
16671
+ effectiveApiType,
16672
+ serverToolsConfig?.deferLoading
16673
+ );
16179
16674
  }
16180
16675
  } catch (error) {
16181
16676
  getLogger().warn("[useChatStorage] Failed to fetch server tools:", error);
16182
16677
  }
16183
16678
  }
16679
+ let narrowedClientTools = clientTools;
16680
+ let clientActivatedSetNames;
16681
+ if (clientTools?.length) {
16682
+ try {
16683
+ if (!userMessageEmbedding && getTokenRef.current && contentForStorage.length >= MIN_CONTENT_LENGTH_FOR_TOOLS) {
16684
+ try {
16685
+ userMessageEmbedding = await embedToolText(
16686
+ contentForStorage,
16687
+ maskForCall,
16688
+ getTokenRef.current
16689
+ );
16690
+ } catch {
16691
+ userMessageEmbeddingFailed = true;
16692
+ }
16693
+ }
16694
+ if (typeof clientToolsFilter === "function") {
16695
+ if (!userMessageEmbeddingFailed) {
16696
+ const keep = new Set(clientToolsFilter(userMessageEmbedding ?? null, clientTools));
16697
+ narrowedClientTools = clientTools.filter((t) => keep.has(getToolName2(t)));
16698
+ }
16699
+ } else if (getTokenRef.current) {
16700
+ const { tools: autoTools, activatedSetNames } = await autoFilterClientTools(
16701
+ clientTools,
16702
+ userMessageEmbedding ?? null,
16703
+ clientToolFilterCache,
16704
+ { getToken: getTokenRef.current, baseUrl, model: embeddingModel },
16705
+ extraToolSets ?? [],
16706
+ activeToolSetsRef.current ?? [],
16707
+ userMessageEmbeddingFailed ? "error" : "short-prompt"
16708
+ );
16709
+ narrowedClientTools = autoTools;
16710
+ clientActivatedSetNames = activatedSetNames;
16711
+ }
16712
+ mergedTools = filteredServerTools.length > 0 || (narrowedClientTools?.length ?? 0) > 0 ? mergeTools(
16713
+ filteredServerTools,
16714
+ narrowedClientTools,
16715
+ effectiveApiType,
16716
+ serverToolsConfig?.deferLoading
16717
+ ) : void 0;
16718
+ } catch (error) {
16719
+ getLogger().warn("[useChatStorage] client tool filtering failed:", error);
16720
+ }
16721
+ }
16184
16722
  if (!userMsgQueueId) {
16185
16723
  if (userMessageEmbedding && autoEmbedMessages) {
16186
- updateMessageEmbeddingOp(
16187
- storageCtx,
16188
- storedUserMessage.uniqueId,
16189
- userMessageEmbedding,
16190
- embeddingModel
16191
- ).catch(() => {
16192
- });
16724
+ if (Array.isArray(userMessageEmbedding[0])) {
16725
+ const textChunks = chunkText(contentForStorage);
16726
+ const messageChunks = textChunks.map((chunk, i) => ({
16727
+ text: chunk.text,
16728
+ vector: userMessageEmbedding[i],
16729
+ startOffset: chunk.startOffset,
16730
+ endOffset: chunk.endOffset
16731
+ }));
16732
+ updateMessageChunksOp(
16733
+ storageCtx,
16734
+ storedUserMessage.uniqueId,
16735
+ messageChunks,
16736
+ embeddingModel
16737
+ ).catch((err) => {
16738
+ getLogger().warn(
16739
+ "[useChatStorage] Failed to persist chunked embeddings for user message:",
16740
+ err
16741
+ );
16742
+ });
16743
+ } else {
16744
+ updateMessageEmbeddingOp(
16745
+ storageCtx,
16746
+ storedUserMessage.uniqueId,
16747
+ userMessageEmbedding,
16748
+ embeddingModel
16749
+ ).catch(() => {
16750
+ });
16751
+ }
16193
16752
  } else {
16194
16753
  void embedMessageAsync(storedUserMessage, maskForCall);
16195
16754
  }
16196
16755
  }
16756
+ if (onToolSelection) {
16757
+ try {
16758
+ onToolSelection({
16759
+ prompt: contentForStorage,
16760
+ clientToolNames: (narrowedClientTools ?? []).map(getToolName2).filter(Boolean),
16761
+ serverToolNames: filteredServerTools.map((t) => t.name)
16762
+ });
16763
+ } catch {
16764
+ }
16765
+ }
16197
16766
  const result = await baseSendMessage({
16198
16767
  messages: messagesToSend,
16199
16768
  model,
@@ -16202,6 +16771,12 @@ function useChatStorage(options) {
16202
16771
  memoryContext,
16203
16772
  searchContext,
16204
16773
  fileContext,
16774
+ toolGuidance: computeToolGuidance(
16775
+ filteredServerTools,
16776
+ narrowedClientTools,
16777
+ extraToolSets ?? [],
16778
+ clientActivatedSetNames
16779
+ ),
16205
16780
  apiType: requestApiType,
16206
16781
  // Responses API options
16207
16782
  temperature,
@@ -16403,6 +16978,21 @@ function useChatStorage(options) {
16403
16978
  userMessage: storedUserMessage
16404
16979
  };
16405
16980
  }
16981
+ const refreshGetToken = getTokenRef.current;
16982
+ if (refreshGetToken) {
16983
+ Promise.resolve().then(() => shouldRefreshTools(getToolsChecksum(responseData), serverToolsConfig?.cache)).then((refresh) => {
16984
+ if (refresh) {
16985
+ return getServerTools({
16986
+ baseUrl,
16987
+ getToken: refreshGetToken,
16988
+ forceRefresh: true,
16989
+ cache: serverToolsConfig?.cache
16990
+ });
16991
+ }
16992
+ }).catch((err) => {
16993
+ getLogger().warn("[useChatStorage] Failed to refresh server tools cache:", err);
16994
+ });
16995
+ }
16406
16996
  return {
16407
16997
  data: responseData,
16408
16998
  error: null,
@@ -17970,6 +18560,7 @@ async function updateVaultFolderOp(ctx, id, opts) {
17970
18560
  if (record.isDeleted) return null;
17971
18561
  const scopeChanged = opts.scope !== void 0 && opts.scope !== record.scope;
17972
18562
  const updated = await ctx.database.write(async () => {
18563
+ const memories = scopeChanged ? await ctx.vaultMemoryCollection.query(import_watermelondb22.Q.where("folder_id", id), import_watermelondb22.Q.where("is_deleted", false)).fetch() : [];
17973
18564
  const updates = [];
17974
18565
  updates.push(
17975
18566
  record.prepareUpdate((r) => {
@@ -17977,15 +18568,12 @@ async function updateVaultFolderOp(ctx, id, opts) {
17977
18568
  if (opts.scope !== void 0) r._setRaw("scope", opts.scope);
17978
18569
  })
17979
18570
  );
17980
- if (scopeChanged) {
17981
- const memories = await ctx.vaultMemoryCollection.query(import_watermelondb22.Q.where("folder_id", id), import_watermelondb22.Q.where("is_deleted", false)).fetch();
17982
- for (const memory of memories) {
17983
- updates.push(
17984
- memory.prepareUpdate((r) => {
17985
- r._setRaw("scope", opts.scope);
17986
- })
17987
- );
17988
- }
18571
+ for (const memory of memories) {
18572
+ updates.push(
18573
+ memory.prepareUpdate((r) => {
18574
+ r._setRaw("scope", opts.scope);
18575
+ })
18576
+ );
17989
18577
  }
17990
18578
  await ctx.database.batch(...updates);
17991
18579
  return ctx.vaultFolderCollection.find(id);