@fias/arche-sdk 2.21.0 → 2.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/dist/bridge.d.ts +33 -3
  2. package/dist/bridge.d.ts.map +1 -1
  3. package/dist/bridge.js +98 -15
  4. package/dist/bridge.js.map +1 -1
  5. package/dist/bridge.test.js +173 -0
  6. package/dist/bridge.test.js.map +1 -1
  7. package/dist/entity-ops.d.ts +3 -0
  8. package/dist/entity-ops.d.ts.map +1 -1
  9. package/dist/entity-ops.js +4 -1
  10. package/dist/entity-ops.js.map +1 -1
  11. package/dist/generated/permissions.d.ts +1 -1
  12. package/dist/generated/permissions.d.ts.map +1 -1
  13. package/dist/generated/permissions.js +4 -0
  14. package/dist/generated/permissions.js.map +1 -1
  15. package/dist/hooks.d.ts +82 -1
  16. package/dist/hooks.d.ts.map +1 -1
  17. package/dist/hooks.js +143 -20
  18. package/dist/hooks.js.map +1 -1
  19. package/dist/hooks.test.js +93 -1
  20. package/dist/hooks.test.js.map +1 -1
  21. package/dist/index.d.ts +3 -2
  22. package/dist/index.d.ts.map +1 -1
  23. package/dist/index.js +14 -2
  24. package/dist/index.js.map +1 -1
  25. package/dist/index.mjs +329 -36
  26. package/dist/invocation-params.d.ts +92 -0
  27. package/dist/invocation-params.d.ts.map +1 -0
  28. package/dist/invocation-params.js +180 -0
  29. package/dist/invocation-params.js.map +1 -0
  30. package/dist/invocation-params.test.d.ts +2 -0
  31. package/dist/invocation-params.test.d.ts.map +1 -0
  32. package/dist/invocation-params.test.js +140 -0
  33. package/dist/invocation-params.test.js.map +1 -0
  34. package/dist/panels.d.ts.map +1 -1
  35. package/dist/panels.js.map +1 -1
  36. package/dist/panels.test.js +1 -3
  37. package/dist/panels.test.js.map +1 -1
  38. package/dist/protocol.d.ts +8 -0
  39. package/dist/protocol.d.ts.map +1 -1
  40. package/dist/protocol.js +8 -0
  41. package/dist/protocol.js.map +1 -1
  42. package/dist/types.d.ts +219 -4
  43. package/dist/types.d.ts.map +1 -1
  44. package/package.json +1 -1
  45. package/templates/default/AGENTS.md +140 -4
  46. package/templates/default/CLAUDE.md +140 -4
package/dist/index.mjs CHANGED
@@ -114,19 +114,137 @@ function installSandboxDialogShims(win = typeof window === "undefined" ? void 0
114
114
  }
115
115
  }
116
116
 
117
+ // src/invocation-params.ts
118
+ var ENTITY_INVOCATION_HISTORY_LIMITS = {
119
+ maxTurns: 40,
120
+ maxTurnChars: 1e5,
121
+ maxTotalChars: 15e4
122
+ };
123
+ var ENTITY_INVOCATION_REQUEST_LIMITS = {
124
+ textBytes: 256 * 1024,
125
+ visionBytes: 1536 * 1024,
126
+ envelopeBytes: 1024
127
+ };
128
+ function serializedBytes(value) {
129
+ return new TextEncoder().encode(JSON.stringify(value)).length;
130
+ }
131
+ function carriesImages(params) {
132
+ return Array.isArray(params.images) && params.images.length > 0;
133
+ }
134
+ function invocationPayloadByteLimit(params) {
135
+ const { textBytes, visionBytes, envelopeBytes } = ENTITY_INVOCATION_REQUEST_LIMITS;
136
+ return (carriesImages(params) ? visionBytes : textBytes) - envelopeBytes;
137
+ }
138
+ function checkInvocationRequestSize(payload) {
139
+ const bytes = serializedBytes(payload);
140
+ const limit = invocationPayloadByteLimit(payload);
141
+ if (bytes > limit) {
142
+ return {
143
+ ok: false,
144
+ message: `The request is ${bytes} bytes; ${carriesImages(payload) ? "a call with images" : "a call without images"} may be at most ${limit}. Send less history (fitHistoryToLimits with { request }) or a shorter input.`
145
+ };
146
+ }
147
+ return { ok: true, value: bytes };
148
+ }
149
+ var REASONING_EFFORTS = ["off", "low", "medium", "high"];
150
+ function isPlainObject(value) {
151
+ return typeof value === "object" && value !== null && !Array.isArray(value);
152
+ }
153
+ function checkInvocationHistory(value) {
154
+ if (value === void 0) return { ok: true, value: void 0 };
155
+ const { maxTurns, maxTurnChars, maxTotalChars } = ENTITY_INVOCATION_HISTORY_LIMITS;
156
+ if (!Array.isArray(value)) return { ok: false, message: "history: must be an array" };
157
+ if (value.length > maxTurns) {
158
+ return { ok: false, message: `history: at most ${maxTurns} turns` };
159
+ }
160
+ let total = 0;
161
+ for (let i = 0; i < value.length; i++) {
162
+ const turn = value[i];
163
+ if (!isPlainObject(turn)) return { ok: false, message: `history.${i}: must be an object` };
164
+ const extra = Object.keys(turn).filter((k) => k !== "role" && k !== "content");
165
+ if (extra.length > 0) {
166
+ return { ok: false, message: `history.${i}: unknown field "${extra[0]}"` };
167
+ }
168
+ if (turn.role !== "user" && turn.role !== "assistant") {
169
+ return { ok: false, message: `history.${i}.role: must be "user" or "assistant"` };
170
+ }
171
+ if (typeof turn.content !== "string") {
172
+ return { ok: false, message: `history.${i}.content: must be a string` };
173
+ }
174
+ if (turn.content.length > maxTurnChars) {
175
+ return { ok: false, message: `history.${i}.content: at most ${maxTurnChars} characters` };
176
+ }
177
+ total += turn.content.length;
178
+ }
179
+ if (total > maxTotalChars) {
180
+ return { ok: false, message: `history: at most ${maxTotalChars} characters in total` };
181
+ }
182
+ return { ok: true, value };
183
+ }
184
+ function checkReasoningParameter(value) {
185
+ if (value === void 0) return { ok: true, value: void 0 };
186
+ if (!isPlainObject(value))
187
+ return { ok: false, message: "parameters.reasoning: must be an object" };
188
+ const extra = Object.keys(value).filter((k) => k !== "effort" && k !== "includeThinking");
189
+ if (extra.length > 0) {
190
+ return { ok: false, message: `parameters.reasoning: unknown field "${extra[0]}"` };
191
+ }
192
+ if (!REASONING_EFFORTS.includes(value.effort)) {
193
+ return {
194
+ ok: false,
195
+ message: `parameters.reasoning.effort: must be one of ${REASONING_EFFORTS.join(", ")}`
196
+ };
197
+ }
198
+ if (value.includeThinking !== void 0 && typeof value.includeThinking !== "boolean") {
199
+ return { ok: false, message: "parameters.reasoning.includeThinking: must be a boolean" };
200
+ }
201
+ return {
202
+ ok: true,
203
+ value: {
204
+ effort: value.effort,
205
+ includeThinking: value.includeThinking === true
206
+ }
207
+ };
208
+ }
209
+ function fitHistoryToLimits(turns, options = {}) {
210
+ const limits = ENTITY_INVOCATION_HISTORY_LIMITS;
211
+ const maxTurns = Math.min(options.maxTurns ?? limits.maxTurns, limits.maxTurns);
212
+ const request = options.request ?? {};
213
+ let bytesLeft = invocationPayloadByteLimit(request) - serializedBytes({ ...request, stream: true, history: [] });
214
+ const kept = [];
215
+ let total = 0;
216
+ for (let i = turns.length - 1; i >= 0 && kept.length < maxTurns; i--) {
217
+ const turn = turns[i];
218
+ const content = turn.content.length > limits.maxTurnChars ? turn.content.slice(turn.content.length - limits.maxTurnChars) : turn.content;
219
+ if (total + content.length > limits.maxTotalChars) break;
220
+ const bytes = serializedBytes({ role: turn.role, content }) + (kept.length > 0 ? 1 : 0);
221
+ if (bytes > bytesLeft) break;
222
+ bytesLeft -= bytes;
223
+ total += content.length;
224
+ kept.push({ role: turn.role, content });
225
+ }
226
+ return kept.reverse();
227
+ }
228
+
117
229
  // src/bridge.ts
118
230
  var messageCounter = 0;
119
231
  function generateMessageId() {
120
232
  return `msg_${Date.now()}_${++messageCounter}`;
121
233
  }
122
234
  var FiasBridgeError = class extends Error {
123
- constructor(message, code) {
235
+ constructor(message, code, requestId) {
124
236
  super(message);
125
237
  this.name = "FiasBridgeError";
126
238
  this.code = code;
239
+ if (requestId) this.requestId = requestId;
127
240
  }
128
241
  toJSON() {
129
- return { name: "FiasBridgeError", code: this.code, message: this.message };
242
+ return {
243
+ name: "FiasBridgeError",
244
+ code: this.code,
245
+ message: this.message,
246
+ ...this.requestId ? { requestId: this.requestId } : {}
247
+ };
130
248
  }
131
249
  };
132
250
  function mergeThemeWithDefaults(incoming) {
@@ -141,6 +259,8 @@ function mergeThemeWithDefaults(incoming) {
141
259
  };
142
260
  }
143
261
  var DEFAULT_TIMEOUT_MS = 3e4;
262
+ var STREAM_IDLE_TIMEOUT_MS = 12e4;
263
+ var STREAM_MAX_DURATION_MS = 15 * 6e4;
144
264
  var FiasBridge = class {
145
265
  constructor() {
146
266
  this.pendingRequests = /* @__PURE__ */ new Map();
@@ -151,6 +271,8 @@ var FiasBridge = class {
151
271
  this.permissions = [];
152
272
  this.theme = null;
153
273
  this.vendoredAssets = null;
274
+ this.textModels = null;
275
+ this.textModelListeners = /* @__PURE__ */ new Set();
154
276
  this.currentPath = "";
155
277
  this.hostOrigin = "";
156
278
  this.themeListeners = /* @__PURE__ */ new Set();
@@ -204,6 +326,7 @@ var FiasBridge = class {
204
326
  this.permissions = initMsg.payload.permissions;
205
327
  this.theme = mergeThemeWithDefaults(initMsg.payload.theme);
206
328
  this.vendoredAssets = initMsg.payload.vendoredAssets ?? null;
329
+ this.textModels = initMsg.payload.textModels ?? null;
207
330
  this.currentPath = initMsg.payload.currentPath;
208
331
  this.initialized = true;
209
332
  this.initResolve?.();
@@ -220,6 +343,15 @@ var FiasBridge = class {
220
343
  }
221
344
  return;
222
345
  }
346
+ if (data.type === "text_models_update") {
347
+ if (Array.isArray(data.payload)) {
348
+ this.textModels = data.payload;
349
+ for (const listener of this.textModelListeners) {
350
+ listener(this.textModels);
351
+ }
352
+ }
353
+ return;
354
+ }
223
355
  if (data.type === "navigate_update") {
224
356
  this.currentPath = data.payload;
225
357
  for (const listener of this.navigationListeners) {
@@ -254,10 +386,15 @@ var FiasBridge = class {
254
386
  return;
255
387
  }
256
388
  if (data.type === "stream_token") {
257
- const { messageId, text } = data;
389
+ const { messageId, text, channel } = data;
258
390
  const pending = this.pendingRequests.get(messageId);
259
- if (pending?.onToken && typeof text === "string") {
260
- pending.onToken(text);
391
+ if (!pending) return;
392
+ pending.onActivity?.();
393
+ if (typeof text !== "string" || channel === "keepalive") return;
394
+ if (channel === "thinking") {
395
+ pending.onThinking?.(text);
396
+ } else {
397
+ pending.onToken?.(text);
261
398
  }
262
399
  return;
263
400
  }
@@ -326,12 +463,14 @@ var FiasBridge = class {
326
463
  const pending = this.pendingRequests.get(response.messageId);
327
464
  if (!pending) return;
328
465
  clearTimeout(pending.timer);
466
+ pending.cleanup?.();
329
467
  this.pendingRequests.delete(response.messageId);
330
468
  if (response.error) {
331
469
  pending.reject(
332
470
  new FiasBridgeError(
333
471
  response.error || "Bridge request failed",
334
- response.errorCode ?? "BRIDGE_ERROR"
472
+ response.errorCode ?? "BRIDGE_ERROR",
473
+ response.errorRequestId
335
474
  )
336
475
  );
337
476
  } else {
@@ -447,6 +586,17 @@ var FiasBridge = class {
447
586
  getCurrentPath() {
448
587
  return this.currentPath;
449
588
  }
589
+ /**
590
+ * The live text-model catalog, or null until the host has delivered it (or
591
+ * when the plugin lacks `entities:invoke`). See `useTextModels`.
592
+ */
593
+ getTextModels() {
594
+ return this.textModels;
595
+ }
596
+ onTextModelsUpdate(listener) {
597
+ this.textModelListeners.add(listener);
598
+ return () => this.textModelListeners.delete(listener);
599
+ }
450
600
  onThemeUpdate(listener) {
451
601
  this.themeListeners.add(listener);
452
602
  return () => this.themeListeners.delete(listener);
@@ -500,21 +650,62 @@ var FiasBridge = class {
500
650
  * resolves with the final EntityInvocationResult when the host sends the
501
651
  * normal response message.
502
652
  */
503
- async requestStream(payload, onToken) {
653
+ async requestStream(payload, callbacks) {
654
+ const { onToken, onThinking, signal } = typeof callbacks === "function" ? { onToken: callbacks } : callbacks;
504
655
  await this.waitForInit();
656
+ const outbound = { ...payload, stream: true };
657
+ const size = checkInvocationRequestSize(outbound);
658
+ if (!size.ok) {
659
+ throw new FiasBridgeError(size.message, "REQUEST_TOO_LARGE");
660
+ }
505
661
  return new Promise((resolve, reject) => {
662
+ if (signal?.aborted) {
663
+ reject(new FiasBridgeError("Stream aborted before it started", "ABORTED"));
664
+ return;
665
+ }
506
666
  const messageId = generateMessageId();
507
- const timer = setTimeout(() => {
667
+ const fail = (error) => {
668
+ const pending2 = this.pendingRequests.get(messageId);
669
+ if (!pending2) return;
670
+ clearTimeout(pending2.timer);
671
+ pending2.cleanup?.();
508
672
  this.pendingRequests.delete(messageId);
509
- reject(new FiasBridgeError("Bridge streaming request timed out: entity_invoke", "TIMEOUT"));
510
- }, 12e4);
511
- this.pendingRequests.set(messageId, {
673
+ reject(error);
674
+ };
675
+ const idleTimer = () => setTimeout(
676
+ () => fail(
677
+ new FiasBridgeError("Bridge streaming request timed out: entity_invoke", "TIMEOUT")
678
+ ),
679
+ STREAM_IDLE_TIMEOUT_MS
680
+ );
681
+ const maxTimer = setTimeout(
682
+ () => fail(
683
+ new FiasBridgeError(
684
+ "Bridge streaming request exceeded its maximum duration: entity_invoke",
685
+ "TIMEOUT"
686
+ )
687
+ ),
688
+ STREAM_MAX_DURATION_MS
689
+ );
690
+ const onAbort = () => fail(new FiasBridgeError("Stream aborted", "ABORTED"));
691
+ signal?.addEventListener("abort", onAbort, { once: true });
692
+ const pending = {
512
693
  resolve,
513
694
  reject,
514
- timer,
515
- onToken
516
- });
517
- this.send("entity_invoke", { ...payload, stream: true }, messageId);
695
+ timer: idleTimer(),
696
+ onToken,
697
+ onThinking,
698
+ onActivity: () => {
699
+ clearTimeout(pending.timer);
700
+ pending.timer = idleTimer();
701
+ },
702
+ cleanup: () => {
703
+ clearTimeout(maxTimer);
704
+ signal?.removeEventListener("abort", onAbort);
705
+ }
706
+ };
707
+ this.pendingRequests.set(messageId, pending);
708
+ this.send("entity_invoke", outbound, messageId);
518
709
  });
519
710
  }
520
711
  /**
@@ -528,10 +719,12 @@ var FiasBridge = class {
528
719
  }
529
720
  for (const pending of this.pendingRequests.values()) {
530
721
  clearTimeout(pending.timer);
722
+ pending.cleanup?.();
531
723
  pending.reject(new FiasBridgeError("Bridge destroyed", "BRIDGE_DESTROYED"));
532
724
  }
533
725
  this.pendingRequests.clear();
534
726
  this.themeListeners.clear();
727
+ this.textModelListeners.clear();
535
728
  this.navigationListeners.clear();
536
729
  this.stepNavigationListeners.clear();
537
730
  this.previewStateProviders.clear();
@@ -877,6 +1070,7 @@ var VAULT_USER_DOCUMENTS_ENTITY_ID = "ent_intg_vault_documents_0000000";
877
1070
  var VAULT_ARCHE_DOCUMENTS_ENTITY_ID = "ent_intg_vault_arche_docs_000000";
878
1071
  var COMMUNITY_ASSETS_ENTITY_ID = "ent_intg_community_assets_000000";
879
1072
  var DOCUMENT_SIGN_ENTITY_ID = "ent_intg_document_sign_000000000";
1073
+ var COMPANY_EMAIL_SEND_ENTITY_ID = "ent_intg_company_email_send_0000";
880
1074
  var COMMUNITY_ASSET_MAX_URL_BATCH = 50;
881
1075
  var COMMUNITY_ASSET_MAX_PUBLISH_BATCH = 20;
882
1076
  async function invokeEntityOp(bridge, entityId, op, args = {}) {
@@ -1016,34 +1210,54 @@ function useFiasStorage() {
1016
1210
  }
1017
1211
  function useEntityInvocation() {
1018
1212
  const bridge = useBridge();
1019
- const [isLoading, setIsLoading] = useState2(false);
1213
+ const [inFlight, setInFlight] = useState2(0);
1020
1214
  const [result, setResult] = useState2(null);
1021
1215
  const [error, setError] = useState2(null);
1022
1216
  const [streamingText, setStreamingText] = useState2("");
1023
1217
  const invoke = useCallback(
1024
- async (params) => {
1025
- setIsLoading(true);
1026
- setError(null);
1027
- setStreamingText("");
1218
+ async (params, options) => {
1219
+ const usesSharedSlots = !options?.onToken;
1220
+ setInFlight((n) => n + 1);
1221
+ if (usesSharedSlots) {
1222
+ setError(null);
1223
+ setStreamingText("");
1224
+ }
1028
1225
  try {
1029
- const res = await bridge.requestStream(params, (token) => {
1030
- flushSync(() => {
1031
- setStreamingText((prev) => prev + token);
1032
- });
1226
+ const res = await bridge.requestStream(params, {
1227
+ onToken: (token) => {
1228
+ if (options?.onToken) {
1229
+ options.onToken(token);
1230
+ return;
1231
+ }
1232
+ flushSync(() => {
1233
+ setStreamingText((prev) => prev + token);
1234
+ });
1235
+ },
1236
+ onThinking: options?.onThinking,
1237
+ signal: options?.signal
1033
1238
  });
1034
- setResult(res);
1239
+ if (usesSharedSlots) setResult(res);
1035
1240
  return res;
1036
1241
  } catch (err) {
1037
1242
  const e = err instanceof Error ? err : new Error(String(err));
1038
- setError(e);
1243
+ if (usesSharedSlots) setError(e);
1039
1244
  throw e;
1040
1245
  } finally {
1041
- setIsLoading(false);
1246
+ setInFlight((n) => n - 1);
1042
1247
  }
1043
1248
  },
1044
1249
  [bridge]
1045
1250
  );
1046
- return { invoke, isLoading, result, error, streamingText };
1251
+ return { invoke, isLoading: inFlight > 0, result, error, streamingText };
1252
+ }
1253
+ function useTextModels() {
1254
+ const bridge = useBridge();
1255
+ const [models, setModels] = useState2(bridge.getTextModels() ?? []);
1256
+ useEffect2(() => {
1257
+ setModels(bridge.getTextModels() ?? []);
1258
+ return bridge.onTextModelsUpdate(setModels);
1259
+ }, [bridge]);
1260
+ return models;
1047
1261
  }
1048
1262
  function useImageGeneration() {
1049
1263
  const bridge = useBridge();
@@ -1237,8 +1451,8 @@ function useFiasNavigation() {
1237
1451
  };
1238
1452
  }, [bridge]);
1239
1453
  const navigateTo = useCallback(
1240
- (path) => {
1241
- bridge.request("navigate", { path });
1454
+ (path, opts) => {
1455
+ bridge.request("navigate", { path, ...opts?.replace ? { replace: true } : {} });
1242
1456
  },
1243
1457
  [bridge]
1244
1458
  );
@@ -1968,7 +2182,7 @@ function useVaultDocuments() {
1968
2182
  );
1969
2183
  }
1970
2184
  var SAVE_CONTENT_BODY_LANE_MAX_BYTES = 2e5;
1971
- var BRIDGE_BODY_TEXT_MAX_SERIALIZED_BYTES = 256 * 1024 - 16 * 1024;
2185
+ var BRIDGE_BODY_TEXT_MAX_SERIALIZED_BYTES = ENTITY_INVOCATION_REQUEST_LIMITS.textBytes - 16 * 1024;
1972
2186
  function fitsBridgeBodyAsJsonText(text, utf8Bytes) {
1973
2187
  if (text.length * 6 <= BRIDGE_BODY_TEXT_MAX_SERIALIZED_BYTES) return true;
1974
2188
  const escapeOverhead = JSON.stringify(text).length - text.length;
@@ -2336,9 +2550,54 @@ function useVaultUserDocuments() {
2336
2550
  },
2337
2551
  [bridge]
2338
2552
  );
2553
+ const workspace = useMemo2(
2554
+ () => ({
2555
+ list: async (options) => {
2556
+ const res = await invokeEntityOp(
2557
+ bridge,
2558
+ VAULT_USER_DOCUMENTS_ENTITY_ID,
2559
+ "list_workspace_documents",
2560
+ { limit: options?.limit, offset: options?.offset, search: options?.search }
2561
+ );
2562
+ return res.documents;
2563
+ },
2564
+ get: (documentId, options) => invokeEntityOp(
2565
+ bridge,
2566
+ VAULT_USER_DOCUMENTS_ENTITY_ID,
2567
+ "get_workspace_document",
2568
+ { documentId, includeContent: options?.includeContent }
2569
+ ),
2570
+ getDownloadUrl: async (documentId) => {
2571
+ const res = await invokeEntityOp(bridge, VAULT_USER_DOCUMENTS_ENTITY_ID, "get_workspace_document_url", { documentId });
2572
+ return {
2573
+ url: res.url,
2574
+ expiresAt: new Date(res.expiresAt).getTime(),
2575
+ contentType: res.contentType
2576
+ };
2577
+ },
2578
+ getBytes: async (documentId) => {
2579
+ const res = await invokeEntityOp(bridge, VAULT_USER_DOCUMENTS_ENTITY_ID, "get_workspace_document_bytes", { documentId });
2580
+ return {
2581
+ document: res.document,
2582
+ bytes: base64ToBytes(res.bytesBase64),
2583
+ contentType: res.contentType
2584
+ };
2585
+ }
2586
+ }),
2587
+ [bridge]
2588
+ );
2339
2589
  return useMemo2(
2340
- () => ({ pick, requestEditAccess, saveContent, list, get, getDownloadUrl, getBytes }),
2341
- [pick, requestEditAccess, saveContent, list, get, getDownloadUrl, getBytes]
2590
+ () => ({
2591
+ pick,
2592
+ requestEditAccess,
2593
+ saveContent,
2594
+ list,
2595
+ get,
2596
+ getDownloadUrl,
2597
+ getBytes,
2598
+ workspace
2599
+ }),
2600
+ [pick, requestEditAccess, saveContent, list, get, getDownloadUrl, getBytes, workspace]
2342
2601
  );
2343
2602
  }
2344
2603
  var COMMUNITY_ASSET_URL_REFRESH_BUFFER_MS = 3e4;
@@ -2398,6 +2657,20 @@ async function fetchCommunityAssetUrls(bridge, assetIds) {
2398
2657
  }
2399
2658
  return hits;
2400
2659
  }
2660
+ function useCompanyEmail() {
2661
+ const bridge = useBridge();
2662
+ const send = useCallback(
2663
+ async (params) => {
2664
+ const res = await bridge.request("entity_invoke", {
2665
+ entityId: COMPANY_EMAIL_SEND_ENTITY_ID,
2666
+ payload: params
2667
+ });
2668
+ return res.output;
2669
+ },
2670
+ [bridge]
2671
+ );
2672
+ return useMemo2(() => ({ send }), [send]);
2673
+ }
2401
2674
  function useCommunityAssets() {
2402
2675
  const bridge = useBridge();
2403
2676
  const publish = useCallback(
@@ -2670,6 +2943,7 @@ var VALID_PLUGIN_PERMISSIONS = [
2670
2943
  "data:store",
2671
2944
  "data:workspace",
2672
2945
  "docsign:envelopes:read",
2946
+ "email:company:send",
2673
2947
  "entities:audio_generate",
2674
2948
  "entities:client_invoke",
2675
2949
  "entities:image_edit",
@@ -2687,7 +2961,8 @@ var VALID_PLUGIN_PERMISSIONS = [
2687
2961
  "vault:documents:write",
2688
2962
  "vault:user-documents:edit",
2689
2963
  "vault:user-documents:read",
2690
- "vault:user-documents:write"
2964
+ "vault:user-documents:write",
2965
+ "vault:workspace-documents:read"
2691
2966
  ];
2692
2967
  function isValidPluginPermission(perm) {
2693
2968
  return VALID_PLUGIN_PERMISSIONS.includes(perm);
@@ -2701,6 +2976,7 @@ var PLUGIN_PERMISSION_DESCRIPTIONS = {
2701
2976
  "data:store": "Save structured data in this arche's own private database.",
2702
2977
  "data:workspace": "Create team workspaces and manage their members.",
2703
2978
  "docsign:envelopes:read": "Check on signature requests it started for you \u2014 who has signed, and what is still outstanding.",
2979
+ "email:company:send": "Send email as your Company from its own address (e.g. team@yourcompany.com) \u2014 only after a Company admin allows this app to, and only to the people they choose. Each email is charged to the person using the app.",
2704
2980
  "entities:audio_generate": "Generate audio with platform AI \u2014 costs credits.",
2705
2981
  "entities:client_invoke": "Use free on-device tools (grammar check, translation) in your browser.",
2706
2982
  "entities:image_edit": "Edit images with platform AI \u2014 costs credits.",
@@ -2718,7 +2994,8 @@ var PLUGIN_PERMISSION_DESCRIPTIONS = {
2718
2994
  "vault:documents:write": "Create and manage its own files, in its own folder of your Vault.",
2719
2995
  "vault:user-documents:edit": "Save changes to files of yours that you let it edit \u2014 you choose each file, earlier versions are kept unless you turned version history off, and you can remove access any time in Vault \u2192 Arche Access.",
2720
2996
  "vault:user-documents:read": "Read documents YOU choose to share with it from your Vault \u2014 you pick each document, and you can remove access any time in Vault \u2192 Arche Access.",
2721
- "vault:user-documents:write": "Save its work to your Documents \u2014 you choose the folder and the name each time."
2997
+ "vault:user-documents:write": "Save its work to your Documents \u2014 you choose the folder and the name each time.",
2998
+ "vault:workspace-documents:read": "Read every document you can see in a team workspace \u2014 only after that workspace's owner or admin approves it there, and never files marked proprietary."
2722
2999
  };
2723
3000
 
2724
3001
  // src/fias.ts
@@ -5059,7 +5336,13 @@ function validateAnimation(value, path, issues) {
5059
5336
  }
5060
5337
  rejectUnknownKeys(value, ["type", "durationMs", "easing"], path, issues);
5061
5338
  checkOptionalEnum(value.type, `${path}.type`, issues, ANIMATION_TYPES);
5062
- checkOptionalIntRange(value.durationMs, `${path}.durationMs`, issues, 0, ANIMATION_MAX_DURATION_MS);
5339
+ checkOptionalIntRange(
5340
+ value.durationMs,
5341
+ `${path}.durationMs`,
5342
+ issues,
5343
+ 0,
5344
+ ANIMATION_MAX_DURATION_MS
5345
+ );
5063
5346
  checkOptionalEnum(value.easing, `${path}.easing`, issues, ANIMATION_EASINGS);
5064
5347
  }
5065
5348
  var THEME_COLOR_FIELDS = ["bg", "fg", "border", "accent", "accentFg", "scrim"];
@@ -5201,6 +5484,8 @@ export {
5201
5484
  AUDIO_VOICE_CHANGER_SURFACE_KEY,
5202
5485
  AUDIO_VOICE_DESIGN_SURFACE_KEY,
5203
5486
  DARK_THEME,
5487
+ ENTITY_INVOCATION_HISTORY_LIMITS,
5488
+ ENTITY_INVOCATION_REQUEST_LIMITS,
5204
5489
  FONT_CATALOG,
5205
5490
  FONT_PAIRINGS,
5206
5491
  FiasBridge,
@@ -5240,17 +5525,23 @@ export {
5240
5525
  PANEL_TITLE_MAX_LENGTH,
5241
5526
  PLUGIN_CONTENT_ENTITY_ID,
5242
5527
  PLUGIN_PERMISSION_DESCRIPTIONS,
5528
+ REASONING_EFFORTS,
5243
5529
  SDK_BRIDGE_PROTOCOL_VERSION,
5244
5530
  SDK_REQUIRES_HOST_PROTOCOL_VERSION,
5245
5531
  THEME_CATALOG,
5246
5532
  VALID_PLUGIN_PERMISSIONS,
5533
+ checkInvocationHistory,
5534
+ checkInvocationRequestSize,
5535
+ checkReasoningParameter,
5247
5536
  ensureFontLoaded,
5248
5537
  fetchVaultDocumentDownloadUrl,
5249
5538
  fias,
5539
+ fitHistoryToLimits,
5250
5540
  getBridge,
5251
5541
  getDefaultTheme,
5252
5542
  getFontPairingById,
5253
5543
  getThemeById,
5544
+ invocationPayloadByteLimit,
5254
5545
  isValidPluginPermission,
5255
5546
  resetBridge,
5256
5547
  useArcheAssets,
@@ -5258,6 +5549,7 @@ export {
5258
5549
  useAudioGeneration,
5259
5550
  useClientEntity,
5260
5551
  useCommunityAssets,
5552
+ useCompanyEmail,
5261
5553
  useDataSubscription,
5262
5554
  useDocumentSign,
5263
5555
  useElevenLabsAudioIsolation,
@@ -5299,6 +5591,7 @@ export {
5299
5591
  useSmartVectorize,
5300
5592
  useStepNavigation,
5301
5593
  useSurface,
5594
+ useTextModels,
5302
5595
  useVaultDocuments,
5303
5596
  useVaultUserDocuments,
5304
5597
  validatePanelAction,
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Limits and validation for the multi-turn fields of an AI `entity_invoke`:
3
+ * `history` (prior conversation turns) and `parameters.reasoning`.
4
+ *
5
+ * ONE implementation, deliberately placed in the SDK. It is dependency-free, so
6
+ * it can run in three places that must never disagree:
7
+ * - the platform bridges, which reject a malformed call with `INVALID_PARAMS`;
8
+ * - the dev harness's MOCK mode, which must reject the same calls offline
9
+ * (the harness is published to npm and may only depend on this SDK);
10
+ * - plugins, which use `fitHistoryToLimits` to trim a conversation BEFORE
11
+ * sending it rather than finding out from a rejected call.
12
+ */
13
+ import type { EntityInvocationParams, EntityInvocationTurn, ReasoningEffort } from './types';
14
+ /**
15
+ * Bounds on `EntityInvocationParams.history`, in characters. They are the
16
+ * validator's rules, not a promise that a call fits: the request must ALSO fit
17
+ * the bridge's byte limit (`ENTITY_INVOCATION_REQUEST_LIMITS`), and a character
18
+ * can serialize to up to 4 bytes (CJK 3, emoji 4, a newline or quote 2). Trim
19
+ * with `fitHistoryToLimits(turns, { request })`, which applies both.
20
+ */
21
+ export declare const ENTITY_INVOCATION_HISTORY_LIMITS: {
22
+ readonly maxTurns: 40;
23
+ readonly maxTurnChars: 100000;
24
+ readonly maxTotalChars: 150000;
25
+ };
26
+ /**
27
+ * The platform bridge's request-size ceilings, in bytes of the serialized
28
+ * (UTF-8 JSON) request. A text-only `entity_invoke` travels on the 256 KB
29
+ * route; one carrying images on the 1.5 MB vision route. Mirrors the server's
30
+ * `PLUGIN_BRIDGE_BODY_LIMIT_BYTES` / `PLUGIN_BRIDGE_VISION_BODY_LIMIT_BYTES`
31
+ * (pinned by `tests/architecture/invocation-params-parity.test.ts`).
32
+ * `envelopeBytes` is kept free for what the host wraps around the payload.
33
+ */
34
+ export declare const ENTITY_INVOCATION_REQUEST_LIMITS: {
35
+ readonly textBytes: number;
36
+ readonly visionBytes: number;
37
+ readonly envelopeBytes: 1024;
38
+ };
39
+ /**
40
+ * The most bytes an `entity_invoke` payload may serialize to: the limit of the
41
+ * route it will travel on, less the host's envelope.
42
+ */
43
+ export declare function invocationPayloadByteLimit(params: {
44
+ images?: unknown;
45
+ }): number;
46
+ /**
47
+ * Whether an `entity_invoke` payload fits the bridge's request limit. The SDK
48
+ * checks this before sending, so an oversized call fails with
49
+ * `REQUEST_TOO_LARGE` instead of an opaque HTTP 413 from the server.
50
+ */
51
+ export declare function checkInvocationRequestSize(payload: Record<string, unknown>): InvocationParamCheck<number>;
52
+ export declare const REASONING_EFFORTS: readonly ReasoningEffort[];
53
+ export type InvocationParamCheck<T> = {
54
+ ok: true;
55
+ value: T | undefined;
56
+ } | {
57
+ ok: false;
58
+ message: string;
59
+ };
60
+ /**
61
+ * Validate a `history` value. `undefined` is valid (no history). Roles are
62
+ * `user` / `assistant` only, so a caller can never inject a second system
63
+ * message; unknown turn fields are rejected, not dropped.
64
+ */
65
+ export declare function checkInvocationHistory(value: unknown): InvocationParamCheck<EntityInvocationTurn[]>;
66
+ /**
67
+ * Validate `parameters.reasoning`. `undefined` is valid (no reasoning request);
68
+ * `includeThinking` defaults to `false`.
69
+ */
70
+ export declare function checkReasoningParameter(value: unknown): InvocationParamCheck<{
71
+ effort: ReasoningEffort;
72
+ includeThinking: boolean;
73
+ }>;
74
+ /**
75
+ * The newest suffix of `turns` that fits: the character caps in
76
+ * `ENTITY_INVOCATION_HISTORY_LIMITS` AND the bridge's byte limit, optionally
77
+ * capped at `maxTurns` (e.g. a "last 20 messages" window).
78
+ *
79
+ * Pass the rest of the call as `request` (input, systemPrompt, images,
80
+ * parameters — everything but `history`) so the byte budget is what that call
81
+ * leaves over on the route it will take. Without it the budget assumes the
82
+ * history is all there is, which a real call can still exceed.
83
+ *
84
+ * Older turns are dropped first; a single turn longer than the per-turn cap
85
+ * keeps its most recent text. Every turn sent is billed as input on every call,
86
+ * so a smaller window is also cheaper.
87
+ */
88
+ export declare function fitHistoryToLimits(turns: readonly EntityInvocationTurn[], options?: {
89
+ maxTurns?: number;
90
+ request?: Omit<EntityInvocationParams, 'history'>;
91
+ }): EntityInvocationTurn[];
92
+ //# sourceMappingURL=invocation-params.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"invocation-params.d.ts","sourceRoot":"","sources":["../src/invocation-params.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,KAAK,EAAE,sBAAsB,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE7F;;;;;;GAMG;AACH,eAAO,MAAM,gCAAgC;;;;CAInC,CAAC;AAEX;;;;;;;GAOG;AACH,eAAO,MAAM,gCAAgC;;;;CAInC,CAAC;AAWX;;;GAGG;AACH,wBAAgB,0BAA0B,CAAC,MAAM,EAAE;IAAE,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,MAAM,CAG/E;AAED;;;;GAIG;AACH,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC/B,oBAAoB,CAAC,MAAM,CAAC,CAY9B;AAED,eAAO,MAAM,iBAAiB,EAAE,SAAS,eAAe,EAAqC,CAAC;AAE9F,MAAM,MAAM,oBAAoB,CAAC,CAAC,IAC9B;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,CAAC,GAAG,SAAS,CAAA;CAAE,GAClC;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAMnC;;;;GAIG;AACH,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,OAAO,GACb,oBAAoB,CAAC,oBAAoB,EAAE,CAAC,CA8B9C;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,OAAO,GACb,oBAAoB,CAAC;IAAE,MAAM,EAAE,eAAe,CAAC;IAAC,eAAe,EAAE,OAAO,CAAA;CAAE,CAAC,CAwB7E;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,SAAS,oBAAoB,EAAE,EACtC,OAAO,GAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,IAAI,CAAC,sBAAsB,EAAE,SAAS,CAAC,CAAA;CAAO,GACrF,oBAAoB,EAAE,CA0BxB"}