@fias/arche-sdk 2.19.1 → 2.19.2

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.
package/dist/index.mjs CHANGED
@@ -1008,7 +1008,10 @@ function useFiasStorage() {
1008
1008
  },
1009
1009
  [bridge]
1010
1010
  );
1011
- return { readFile, writeFile, listFiles, deleteFile };
1011
+ return useMemo2(
1012
+ () => ({ readFile, writeFile, listFiles, deleteFile }),
1013
+ [readFile, writeFile, listFiles, deleteFile]
1014
+ );
1012
1015
  }
1013
1016
  function useEntityInvocation() {
1014
1017
  const bridge = useBridge();
@@ -1243,21 +1246,30 @@ function useFiasNavigation() {
1243
1246
  bridge.request("open_arche", {
1244
1247
  archeId,
1245
1248
  newTab: opts?.newTab === true,
1246
- ...opts?.path ? { path: opts.path } : {}
1249
+ ...opts?.path ? { path: opts.path } : {},
1250
+ ...opts?.payload ? { payload: opts.payload } : {}
1247
1251
  });
1248
1252
  },
1249
1253
  [bridge]
1250
1254
  );
1251
- return { navigateTo, openArche, currentPath };
1255
+ return useMemo2(
1256
+ () => ({ navigateTo, openArche, currentPath }),
1257
+ [navigateTo, openArche, currentPath]
1258
+ );
1252
1259
  }
1253
1260
  function usePersistentState(key, initialValue) {
1254
1261
  const bridge = useBridge();
1255
1262
  const [value, setValueInternal] = useState2(initialValue);
1256
1263
  const initializedRef = useRef(false);
1257
1264
  const valueRef = useRef(initialValue);
1265
+ const setByPluginRef = useRef(false);
1258
1266
  const writer = useMemo2(() => createDebouncedWriter(bridge), [bridge]);
1259
1267
  useEffect2(() => {
1260
1268
  invokeEntityOp(bridge, PLUGIN_STORAGE_ENTITY_ID, "read", { path: `__state/${key}` }).then((result) => {
1269
+ if (setByPluginRef.current) {
1270
+ initializedRef.current = true;
1271
+ return;
1272
+ }
1261
1273
  if (result.exists && result.content !== null) {
1262
1274
  try {
1263
1275
  const parsed = JSON.parse(result.content);
@@ -1277,6 +1289,7 @@ function usePersistentState(key, initialValue) {
1277
1289
  const setValue = useCallback(
1278
1290
  (next) => {
1279
1291
  const resolved = typeof next === "function" ? next(valueRef.current) : next;
1292
+ setByPluginRef.current = true;
1280
1293
  valueRef.current = resolved;
1281
1294
  setValueInternal(resolved);
1282
1295
  writer.schedule(`__state/${key}`, JSON.stringify(resolved));
@@ -1740,9 +1753,10 @@ function useVaultDocuments() {
1740
1753
  );
1741
1754
  const read = useCallback(
1742
1755
  async (documentId) => {
1743
- return bridge.request("vault_documents_read", {
1744
- documentId
1745
- });
1756
+ return bridge.request(
1757
+ "vault_documents_read",
1758
+ { documentId }
1759
+ );
1746
1760
  },
1747
1761
  [bridge]
1748
1762
  );
@@ -1767,16 +1781,29 @@ function useVaultDocuments() {
1767
1781
  );
1768
1782
  const saveContent = useCallback(
1769
1783
  async (params) => {
1770
- const res = await invokeEntityOp(
1771
- bridge,
1772
- VAULT_ARCHE_DOCUMENTS_ENTITY_ID,
1773
- "save_own_document_content",
1774
- {
1775
- documentId: params.documentId,
1776
- content: params.content,
1777
- contentType: params.contentType
1778
- }
1779
- );
1784
+ const routed = await routeSaveContent(params.content);
1785
+ let res;
1786
+ if (routed.lane === "body") {
1787
+ res = await invokeEntityOp(
1788
+ bridge,
1789
+ VAULT_ARCHE_DOCUMENTS_ENTITY_ID,
1790
+ "save_own_document_content",
1791
+ {
1792
+ documentId: params.documentId,
1793
+ content: routed.text,
1794
+ contentType: params.contentType,
1795
+ expectedRevision: params.expectedRevision
1796
+ }
1797
+ );
1798
+ } else {
1799
+ res = await bridge.request("vault_documents_upload", {
1800
+ saveToDocumentId: params.documentId,
1801
+ bytes: bytesToBase64(routed.bytes),
1802
+ mimeType: params.contentType,
1803
+ contentKind: routed.contentKind,
1804
+ expectedRevision: params.expectedRevision
1805
+ });
1806
+ }
1780
1807
  downloadUrlCache.delete(params.documentId);
1781
1808
  return res;
1782
1809
  },
@@ -1784,6 +1811,12 @@ function useVaultDocuments() {
1784
1811
  );
1785
1812
  const write = useCallback(
1786
1813
  async (params) => {
1814
+ if (params.content.length > SAVE_CONTENT_BODY_LANE_MAX_BYTES || !fitsBridgeBodyAsJsonText(params.content)) {
1815
+ throw new FiasBridgeError(
1816
+ "write() takes small text only: at most 200,000 characters, and small enough to travel in one bridge request once JSON-escaped (quotes, backslashes and newlines double). Use upload() for larger documents.",
1817
+ "INVALID_PARAMS"
1818
+ );
1819
+ }
1787
1820
  return bridge.request("vault_documents_write", {
1788
1821
  name: params.name,
1789
1822
  content: params.content,
@@ -1807,6 +1840,17 @@ function useVaultDocuments() {
1807
1840
  },
1808
1841
  [bridge]
1809
1842
  );
1843
+ const promoteToUserFiles = useCallback(
1844
+ async (documentId) => {
1845
+ const res = await bridge.request("vault_documents_update", {
1846
+ documentId,
1847
+ userVisible: true
1848
+ });
1849
+ downloadUrlCache.delete(documentId);
1850
+ return res;
1851
+ },
1852
+ [bridge]
1853
+ );
1810
1854
  const deleteDocument = useCallback(
1811
1855
  async (documentId) => {
1812
1856
  await bridge.request("vault_documents_delete", { documentId });
@@ -1895,6 +1939,7 @@ function useVaultDocuments() {
1895
1939
  getDownloadUrl,
1896
1940
  write,
1897
1941
  update,
1942
+ promoteToUserFiles,
1898
1943
  delete: deleteDocument,
1899
1944
  search,
1900
1945
  attach,
@@ -1911,6 +1956,7 @@ function useVaultDocuments() {
1911
1956
  getDownloadUrl,
1912
1957
  write,
1913
1958
  update,
1959
+ promoteToUserFiles,
1914
1960
  deleteDocument,
1915
1961
  search,
1916
1962
  attach,
@@ -1921,6 +1967,39 @@ function useVaultDocuments() {
1921
1967
  ]
1922
1968
  );
1923
1969
  }
1970
+ var SAVE_CONTENT_BODY_LANE_MAX_BYTES = 2e5;
1971
+ var BRIDGE_BODY_TEXT_MAX_SERIALIZED_BYTES = 256 * 1024 - 16 * 1024;
1972
+ function fitsBridgeBodyAsJsonText(text, utf8Bytes) {
1973
+ if (text.length * 6 <= BRIDGE_BODY_TEXT_MAX_SERIALIZED_BYTES) return true;
1974
+ const escapeOverhead = JSON.stringify(text).length - text.length;
1975
+ const bytes = utf8Bytes ?? new TextEncoder().encode(text).length;
1976
+ return bytes + escapeOverhead <= BRIDGE_BODY_TEXT_MAX_SERIALIZED_BYTES;
1977
+ }
1978
+ var SAVE_CONTENT_MAX_BYTES = 15 * 1024 * 1024;
1979
+ async function routeSaveContent(content) {
1980
+ let routed;
1981
+ if (typeof content === "string") {
1982
+ if (content.length * 6 <= SAVE_CONTENT_BODY_LANE_MAX_BYTES) {
1983
+ return { lane: "body", text: content };
1984
+ }
1985
+ const bytes = new TextEncoder().encode(content);
1986
+ routed = bytes.length <= SAVE_CONTENT_BODY_LANE_MAX_BYTES && fitsBridgeBodyAsJsonText(content, bytes.length) ? { lane: "body", text: content } : { lane: "host", bytes, contentKind: "text" };
1987
+ } else {
1988
+ routed = { lane: "host", bytes: await toUint8Array(content), contentKind: "binary" };
1989
+ }
1990
+ if (routed.lane === "host" && routed.bytes.length > SAVE_CONTENT_MAX_BYTES) {
1991
+ throw new FiasBridgeError(
1992
+ `Content is ${routed.bytes.length} bytes; saveContent accepts at most ${SAVE_CONTENT_MAX_BYTES} bytes (15 MB).`,
1993
+ "OWN_DOCUMENT_SAVE_TOO_LARGE"
1994
+ );
1995
+ }
1996
+ return routed;
1997
+ }
1998
+ async function toUint8Array(content) {
1999
+ if (content instanceof Uint8Array) return content;
2000
+ if (content instanceof ArrayBuffer) return new Uint8Array(content);
2001
+ return new Uint8Array(await content.arrayBuffer());
2002
+ }
1924
2003
  function base64ToBytes(base64) {
1925
2004
  const binary = atob(base64);
1926
2005
  const bytes = new Uint8Array(binary.length);
@@ -2053,7 +2132,43 @@ function useVaultUserDocuments() {
2053
2132
  const pick = useCallback(
2054
2133
  async (options) => {
2055
2134
  return bridge.request("vault_documents_pick", {
2056
- maxDocuments: options?.maxDocuments
2135
+ maxDocuments: options?.maxDocuments,
2136
+ access: options?.access
2137
+ });
2138
+ },
2139
+ [bridge]
2140
+ );
2141
+ const requestEditAccess = useCallback(
2142
+ async (documentId) => {
2143
+ return bridge.request("vault_documents_pick", {
2144
+ maxDocuments: 1,
2145
+ access: "write",
2146
+ documentId
2147
+ });
2148
+ },
2149
+ [bridge]
2150
+ );
2151
+ const saveContent = useCallback(
2152
+ async (params) => {
2153
+ const routed = await routeSaveContent(params.content);
2154
+ if (routed.lane === "body") {
2155
+ return invokeEntityOp(
2156
+ bridge,
2157
+ VAULT_USER_DOCUMENTS_ENTITY_ID,
2158
+ "save_consented_document_content",
2159
+ {
2160
+ documentId: params.documentId,
2161
+ content: routed.text,
2162
+ expectedRevision: params.expectedRevision
2163
+ }
2164
+ );
2165
+ }
2166
+ return bridge.request("vault_documents_upload", {
2167
+ saveToDocumentId: params.documentId,
2168
+ saveTarget: "user",
2169
+ bytes: bytesToBase64(routed.bytes),
2170
+ contentKind: routed.contentKind,
2171
+ expectedRevision: params.expectedRevision
2057
2172
  });
2058
2173
  },
2059
2174
  [bridge]
@@ -2104,8 +2219,8 @@ function useVaultUserDocuments() {
2104
2219
  [bridge]
2105
2220
  );
2106
2221
  return useMemo2(
2107
- () => ({ pick, list, get, getDownloadUrl, getBytes }),
2108
- [pick, list, get, getDownloadUrl, getBytes]
2222
+ () => ({ pick, requestEditAccess, saveContent, list, get, getDownloadUrl, getBytes }),
2223
+ [pick, requestEditAccess, saveContent, list, get, getDownloadUrl, getBytes]
2109
2224
  );
2110
2225
  }
2111
2226
  var COMMUNITY_ASSET_URL_REFRESH_BUFFER_MS = 3e4;
@@ -2423,6 +2538,7 @@ var VALID_PLUGIN_PERMISSIONS = [
2423
2538
  "user:profile:read",
2424
2539
  "vault:documents:read",
2425
2540
  "vault:documents:write",
2541
+ "vault:user-documents:edit",
2426
2542
  "vault:user-documents:read",
2427
2543
  "vault:user-documents:write"
2428
2544
  ];
@@ -2451,6 +2567,7 @@ var PLUGIN_PERMISSION_DESCRIPTIONS = {
2451
2567
  "user:profile:read": "See your display name and avatar.",
2452
2568
  "vault:documents:read": "Read documents this app itself created in your Vault.",
2453
2569
  "vault:documents:write": "Create and manage its own documents in your Vault.",
2570
+ "vault:user-documents:edit": "Save changes to files of yours that you let it edit \u2014 you choose each file, earlier versions are kept, and you can remove access any time in Vault \u2192 App Access.",
2454
2571
  "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 App Access.",
2455
2572
  "vault:user-documents:write": "Save its work to your Fias files, where you can find and reuse it. You confirm each save."
2456
2573
  };
package/dist/types.d.ts CHANGED
@@ -149,7 +149,25 @@ export interface EntityInvocationParams {
149
149
  * applies). Bounded per call — keep images small (downscale before sending).
150
150
  */
151
151
  images?: EntityInvocationImage[];
152
- parameters?: Record<string, unknown>;
152
+ /**
153
+ * Sampling knobs for the resolved model. Out-of-range values are clamped
154
+ * rather than rejected.
155
+ *
156
+ * - `temperature` — 0–2. **A no-op on models that do not accept sampling
157
+ * parameters**, which includes the whole Claude 5 family (Sonnet 5,
158
+ * Opus 5, Fable 5) and Opus 4.7/4.8: Anthropic removed the parameter
159
+ * there, so the platform drops it rather than letting the provider 400.
160
+ * Only the fast tier (Haiku) and the Google models honour it.
161
+ * - `maxTokens` — may only LOWER the resolved model's output ceiling, never
162
+ * raise it. Worth setting when you know the reply is short: it shrinks the
163
+ * credit hold taken up front.
164
+ *
165
+ * Unknown keys are ignored.
166
+ */
167
+ parameters?: {
168
+ temperature?: number;
169
+ maxTokens?: number;
170
+ };
153
171
  /** System prompt to send to the AI model. The arche/plugin provides context; the entity provides the capability. */
154
172
  systemPrompt?: string;
155
173
  /**
@@ -638,13 +656,58 @@ export interface FiasNavigationApi {
638
656
  * @param opts.newTab Best-effort open in a new tab (keeps the current arche
639
657
  * open). Browsers may block the popup since user activation does not cross
640
658
  * the iframe→host boundary; the host falls back to same-tab navigation.
659
+ * Ignored when `opts.payload` is present — navigation state cannot cross
660
+ * into a new tab, so the handoff would arrive empty.
661
+ * @param opts.payload A typed handoff payload to hand the target arche —
662
+ * e.g. `{ kind: 'sign_document', requestId, documentId, recipients }` to
663
+ * open Document Sign on a draft seeded from one of the user's Vault
664
+ * documents. The host validates it against the target's intake and
665
+ * REJECTS a payload that arche would not accept, so a mistargeted handoff
666
+ * is an error you can see rather than a silent no-op.
667
+ *
668
+ * A handoff only ever PRE-FILLS a UI — it never causes an effect. The
669
+ * user reviews, edits, and acts themself.
670
+ *
671
+ * Mint a fresh `requestId` (uuid) per user intent, not per call: the host
672
+ * keys idempotency on it, so re-presenting one resolves to the SAME draft
673
+ * instead of creating a duplicate. Reusing a stale one reopens the old
674
+ * draft.
641
675
  */
642
676
  openArche: (archeId: string, opts?: {
643
677
  newTab?: boolean;
644
678
  path?: string;
679
+ payload?: ArcheHandoffPayloadInput;
645
680
  }) => void;
646
681
  currentPath: string;
647
682
  }
683
+ /**
684
+ * Handoff payloads a plugin may hand to another arche via `openArche`.
685
+ *
686
+ * Structural rather than a re-export of the platform's Zod-inferred union:
687
+ * the SDK ships to plugin authors and cannot depend on workspace packages.
688
+ * The host re-validates against the canonical contract, so a shape that is
689
+ * wrong here is refused there with a message naming the problem.
690
+ */
691
+ export type ArcheHandoffPayloadInput = {
692
+ kind: 'open_document';
693
+ documentId: string;
694
+ } | {
695
+ kind: 'sign_document';
696
+ /** Idempotency key for this signing request. One uuid per user intent. */
697
+ requestId: string;
698
+ /** A Vault document the user can open — PDFs only. */
699
+ documentId: string;
700
+ title?: string;
701
+ message?: string;
702
+ signingOrder?: 'parallel' | 'sequential';
703
+ recipients?: Array<{
704
+ email: string;
705
+ name?: string;
706
+ role?: 'signer' | 'viewer' | 'approver';
707
+ }>;
708
+ /** Opaque metadata echoed back on the result. Never interpreted. */
709
+ correlationId?: string;
710
+ };
648
711
  /**
649
712
  * A document owned by the calling arche in the user's Vault.
650
713
  *
@@ -671,6 +734,14 @@ export interface VaultDocumentSummary {
671
734
  * scoped by `source_arche_id`, not folder).
672
735
  */
673
736
  folderId: string | null;
737
+ /**
738
+ * Opaque token naming the document's current CONTENT — pass it as
739
+ * `saveContent({ expectedRevision })` so a save is refused
740
+ * (`VERSION_CONFLICT`) if the content changed since you read it. Never
741
+ * parse it. Optional: absent on hosts that predate in-place-save
742
+ * preconditions.
743
+ */
744
+ revision?: string;
674
745
  }
675
746
  export interface VaultDocumentsListOptions {
676
747
  search?: string;
@@ -731,12 +802,26 @@ export interface VaultDocumentUpdateParams {
731
802
  }
732
803
  /** Input to `useVaultDocuments().saveContent`. */
733
804
  export interface VaultDocumentSaveContentParams {
734
- /** The document to overwrite — one THIS arche created with `write`. */
805
+ /** The document to overwrite — one THIS arche created with `write` / `upload`. */
735
806
  documentId: string;
736
- /** New UTF-8 text content (≤ 200 KB encoded). */
737
- content: string;
807
+ /**
808
+ * The new content, up to 15 MB. A `string` is saved as UTF-8 text (the
809
+ * document must be — or become, via `contentType` — a text type); bytes
810
+ * (`Uint8Array` / `ArrayBuffer` / `Blob`) are saved as-is, for any type.
811
+ * Transport is chosen for you: small text rides the bridge message, and
812
+ * everything else is uploaded by the host — there is one method either way.
813
+ */
814
+ content: string | Uint8Array | ArrayBuffer | Blob;
738
815
  /** New MIME type; omitted keeps the document's current one. */
739
816
  contentType?: string;
817
+ /**
818
+ * The `revision` you last saw for this document (from `list`, `readBytes`,
819
+ * or a previous `saveContent` result). When supplied, the save is refused
820
+ * with `VERSION_CONFLICT` if the document has changed since — so a second
821
+ * tab, or another device, cannot be silently overwritten. Omit for
822
+ * last-write-wins.
823
+ */
824
+ expectedRevision?: string;
740
825
  }
741
826
  /** Result of `useVaultDocuments().saveContent`. */
742
827
  export interface VaultDocumentSaveContentResult {
@@ -745,6 +830,35 @@ export interface VaultDocumentSaveContentResult {
745
830
  sizeBytes: number;
746
831
  /** ISO timestamp of the save. */
747
832
  updatedAt: string;
833
+ /**
834
+ * Opaque token naming the content just saved — pass it as
835
+ * `expectedRevision` on the next save. Optional, like every other
836
+ * `revision` on this hook: a platform that predates revisions saves the
837
+ * text lane without returning one (the next save is then last-write-wins).
838
+ */
839
+ revision?: string;
840
+ }
841
+ /**
842
+ * Result of `useVaultUserDocuments().saveContent`. Here `revision` is ALWAYS
843
+ * present — a platform that can save to the user's files at all returns it —
844
+ * which matters because that save REQUIRES the next `expectedRevision`.
845
+ */
846
+ export interface VaultUserDocumentSaveContentResult extends VaultDocumentSaveContentResult {
847
+ revision: string;
848
+ }
849
+ /** Result of `useVaultDocuments().promoteToUserFiles`. */
850
+ export interface VaultDocumentPromoteResult {
851
+ documentId: string;
852
+ visibility: 'user-vault';
853
+ /**
854
+ * Whether the user let your app KEEP EDITING the file (a checkbox on the
855
+ * host's confirmation, offered only when your manifest declares
856
+ * `vault:user-documents:edit`). `true`: keep saving it with
857
+ * `useVaultUserDocuments().saveContent`. `false` or absent: it is read-only
858
+ * to you now — open it read-only and ask with `requestEditAccess` if the
859
+ * user starts editing.
860
+ */
861
+ editAccess?: boolean;
748
862
  }
749
863
  export interface VaultDocumentSearchOptions {
750
864
  /** Number of results to return. 1..50, default 10. */
@@ -788,9 +902,39 @@ export interface OpenDocumentHandoff {
788
902
  kind: 'open_document';
789
903
  documentId: string;
790
904
  }
791
- /** Every handoff payload a plugin can receive. One kind today; the union is
792
- * the extension point, so switch on `kind` rather than assuming. */
793
- export type ArcheHandoffPayload = OpenDocumentHandoff;
905
+ /**
906
+ * The REPLY to a `sign_document` handoff your app sent — delivered when the
907
+ * user leaves Document Sign and comes back to you.
908
+ *
909
+ * Contract, and it matters:
910
+ *
911
+ * - It is a POINTER, not a record. It tells you which envelope to look at;
912
+ * it is not evidence of what happened to it. Anything you would act on —
913
+ * who signed, what is outstanding — comes from reading the envelope, never
914
+ * from this payload.
915
+ * - `outcome` is the fact, not `envelopeId`. An envelope id exists from the
916
+ * moment a DRAFT is created, so its presence proves a draft exists and
917
+ * nothing more. A cancellation structurally carries none.
918
+ * - Match it to an OUTSTANDING request by `requestId` and consume it once.
919
+ * Never re-point an existing association because a `correlationId`
920
+ * matched — that is your own metadata, echoed back unverified.
921
+ */
922
+ export type SignDocumentResultHandoff = {
923
+ kind: 'sign_document_result';
924
+ outcome: 'cancelled';
925
+ requestId: string;
926
+ correlationId?: string;
927
+ } | {
928
+ kind: 'sign_document_result';
929
+ outcome: 'saved_draft' | 'sent';
930
+ requestId: string;
931
+ envelopeId: string;
932
+ correlationId?: string;
933
+ };
934
+ /** Every handoff payload a plugin can receive. Switch on `kind` — the union
935
+ * is the extension point, and a kind you do not handle must be ignored
936
+ * rather than assumed. */
937
+ export type ArcheHandoffPayload = OpenDocumentHandoff | SignDocumentResultHandoff;
794
938
  export interface VaultDocumentUploadParams {
795
939
  /** Document display name. */
796
940
  name: string;
@@ -842,6 +986,12 @@ export interface VaultDocumentUploadResult {
842
986
  /** Status after finalize — `'uploaded'`, then `'available'` once the
843
987
  * extraction worker indexes the document for search. */
844
988
  status: string;
989
+ /**
990
+ * Only on `upload({ userVisible: true })`: whether the user let your app
991
+ * keep editing the file it just saved into their files. See
992
+ * `VaultDocumentPromoteResult.editAccess`.
993
+ */
994
+ editAccess?: boolean;
845
995
  }
846
996
  export interface VaultDocumentReferenceResult {
847
997
  referenceId: string;
@@ -875,6 +1025,7 @@ export interface VaultDocumentsApi {
875
1025
  read: (documentId: string) => Promise<{
876
1026
  content: string;
877
1027
  contentType: string;
1028
+ revision?: string;
878
1029
  }>;
879
1030
  /**
880
1031
  * Read the raw bytes of a document THIS ARCHE created (any content
@@ -903,6 +1054,8 @@ export interface VaultDocumentsApi {
903
1054
  sensitivity: string;
904
1055
  createdAt: string;
905
1056
  updatedAt: string;
1057
+ /** See `VaultDocumentSummary.revision`. */
1058
+ revision?: string;
906
1059
  };
907
1060
  bytes: Uint8Array;
908
1061
  contentType: string | null;
@@ -912,9 +1065,17 @@ export interface VaultDocumentsApi {
912
1065
  * `documentId`, bytes replaced, no version history. The path for an arche
913
1066
  * that saves continuously as the user works (a spreadsheet's autosave);
914
1067
  * to publish a distinct new version instead, use
915
- * `write({ replacesDocumentId })`, which keeps the chain. UTF-8 text only,
916
- * ≤ 200 KB encoded (OWN_DOCUMENT_SAVE_TOO_LARGE names the cap) — larger
917
- * documents need `upload`. Only arche-private documents qualify: a
1068
+ * `write({ replacesDocumentId })`, which keeps the chain. Accepts text OR
1069
+ * bytes, up to 15 MB (OWN_DOCUMENT_SAVE_TOO_LARGE names the cap — it is
1070
+ * the same ceiling `readBytes` serves, so anything you save you can
1071
+ * reopen): a `string` is saved as UTF-8 text and the document must be a
1072
+ * text type; a `Uint8Array` / `ArrayBuffer` / `Blob` is saved as-is, for
1073
+ * any type (a PDF editor saving its PDF). Transport is automatic — small
1074
+ * text rides the bridge message, everything else is uploaded by the host —
1075
+ * so there is no second method to choose. Pass `expectedRevision` to
1076
+ * refuse the save (VERSION_CONFLICT) when another tab or device changed
1077
+ * the document first; re-read, merge, and retry with the new revision.
1078
+ * Only arche-private documents qualify: a
918
1079
  * document the user can see in their own files, even one this arche
919
1080
  * created, rejects with DOCUMENT_NOT_FOUND. A document whose upload never
920
1081
  * finished or that is mid-extraction rejects with
@@ -932,13 +1093,45 @@ export interface VaultDocumentsApi {
932
1093
  * results until re-extraction exists.
933
1094
  */
934
1095
  saveContent: (params: VaultDocumentSaveContentParams) => Promise<VaultDocumentSaveContentResult>;
1096
+ /**
1097
+ * Move a document THIS ARCHE created out of its private space and into the
1098
+ * user's own Fias files — the SAME document, not a copy: same `documentId`,
1099
+ * no second upload, nothing double-counted against storage. Use it to hand
1100
+ * over a finished working file instead of `upload({ userVisible: true })`,
1101
+ * which would mint a duplicate.
1102
+ *
1103
+ * The user decides. The host shows them its own confirmation naming the
1104
+ * file and where it will go; this resolves once they agree, and rejects
1105
+ * with `SAVE_DECLINED` if they don't (or if the host cannot ask — the dev
1106
+ * harness and the builder preview can't). Nothing the plugin sends can skip
1107
+ * that step. The file lands in your app's folder under My Fias; you cannot
1108
+ * choose another.
1109
+ *
1110
+ * Requires `vault:user-documents:write` in your manifest (it escalates to
1111
+ * human review) and a registered folder for your arche — the same two
1112
+ * things `upload({ userVisible: true })` needs.
1113
+ *
1114
+ * ONE-WAY, and it changes what you can do: once the file is the user's,
1115
+ * THIS hook's `saveContent`, `update` and `delete` reject — it is theirs
1116
+ * now. To keep saving it, declare `vault:user-documents:edit`: the
1117
+ * confirmation then offers the user "let this app keep editing", the result
1118
+ * carries `editAccess`, and you save through
1119
+ * `useVaultUserDocuments().saveContent` instead. Without that, promote when
1120
+ * the user is DONE. There is no plugin-side demote.
1121
+ *
1122
+ * After a `SAVE_DECLINED`, re-`list()` before assuming nothing moved. The
1123
+ * host performs the move itself, so in the rare case its page is torn down
1124
+ * mid-move (the user navigates away) you are told "declined" for a promote
1125
+ * that completed. The document's `visibility` is the truth.
1126
+ */
1127
+ promoteToUserFiles: (documentId: string) => Promise<VaultDocumentPromoteResult>;
935
1128
  /**
936
1129
  * Get a fresh presigned download URL for a documentId. The result is
937
1130
  * cached client-side and refreshed automatically before expiry.
938
1131
  */
939
1132
  getDownloadUrl: (documentId: string) => Promise<VaultDocumentDownloadUrl>;
940
- /** Create a small text document inline (UTF-8, ≤1 MB). For binaries
941
- * or larger documents use `upload`. */
1133
+ /** Create a small text document inline (UTF-8, ≤200 KB — the content rides
1134
+ * the bridge request body). For binaries or larger documents use `upload`. */
942
1135
  write: (params: VaultDocumentWriteParams) => Promise<{
943
1136
  documentId: string;
944
1137
  }>;