busabase-sdk 0.52.1 → 0.54.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.
package/dist/airapp.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { t as BusabaseClient } from "./client-SOzg2cGv.js";
1
+ import { t as BusabaseClient } from "./client-CaeGnNfw.js";
2
2
  //#region src/airapp.d.ts
3
3
  type NodeChangeRequestInput = Parameters<BusabaseClient["nodes"]["createChangeRequest"]>[0];
4
4
  type NodeOperationInput = NodeChangeRequestInput["operations"][number];
@@ -57,8 +57,8 @@ interface AirAppFolderDeclaration {
57
57
  * an app's Folder and Bases are plain data-schema resources, safe to bring
58
58
  * into existence unattended, but an AirApp is a bundle of code the viewer's
59
59
  * browser will execute, so bringing it into existence always goes through
60
- * `publishAirApp`'s separate, always-review-first ChangeRequest instead of
61
- * riding along on the same `autoMerge: true` request as the data layer.
60
+ * `publishAirApp`'s own separate request instead of riding along on the data
61
+ * layer's structure request.
62
62
  * Declaring it here matters for a second reason regardless: without it, an
63
63
  * unstamped Folder holding the app's own AirApp would look like it holds an
64
64
  * unattributable stranger, and the legacy claim would be refused.
@@ -222,14 +222,31 @@ interface AirAppFileInput {
222
222
  content: string;
223
223
  mimeType?: string;
224
224
  }
225
+ /**
226
+ * What one publish did. `merged` is the question a caller actually has — did
227
+ * this bundle go live, or is it waiting for someone?
228
+ *
229
+ * A publish is permission-aware like every other write: it merges when the
230
+ * app's credential holds `write` on the Folder, and falls back to a pending
231
+ * ChangeRequest when it does not. The CREATE path has no change request at all
232
+ * once it merges (the node is inserted directly), which is why that branch
233
+ * carries `nodeId` instead of `changeRequestId`.
234
+ */
225
235
  type AirAppPublishResult = {
226
236
  status: "created";
237
+ merged: true;
238
+ nodeId: string;
239
+ } | {
240
+ status: "created";
241
+ merged: false;
227
242
  changeRequestId: string;
228
243
  } | {
229
244
  status: "updated";
245
+ merged: boolean;
230
246
  changeRequestId: string;
231
247
  } | {
232
248
  status: "pending";
249
+ merged: false;
233
250
  changeRequestId: string;
234
251
  };
235
252
  /**
@@ -244,9 +261,10 @@ declare function buildAirAppFileOperations(localFiles: AirAppFileInput[], deploy
244
261
  /**
245
262
  * Publish the app's own AirApp bundle: create it under the Folder when this
246
263
  * Space has never had it, or propose the local files as an update when it
247
- * already exists. Always a separate, always-review-first ChangeRequest from
248
- * the data layer's `provisionDeclaredResources` — see the note on
249
- * `AirAppNodeDeclaration` for why the two must never share a request.
264
+ * already exists. Always a separate request from the data layer's
265
+ * `provisionDeclaredResources` — see the note on `AirAppNodeDeclaration` for
266
+ * why the two must never share a request. Permission-aware: it merges when the
267
+ * app's credential can write to the Folder, and waits for review when it cannot.
250
268
  *
251
269
  * Call after `provisionDeclaredResources` has confirmed the Folder exists.
252
270
  * Every call proposes the full local file list, even when nothing actually
package/dist/airapp.js CHANGED
@@ -1,4 +1,4 @@
1
- import { t as APP_ROOT_RESOURCE_KEY } from "./template-C5oO6Au_.js";
1
+ import { t as APP_ROOT_RESOURCE_KEY } from "./template-D10kjpd-.js";
2
2
  //#region src/airapp.ts
3
3
  /**
4
4
  * AirApp resource provisioning — how an app claims (or creates) the Folder and
@@ -439,9 +439,10 @@ async function findPendingAirAppCreate(client, slug) {
439
439
  /**
440
440
  * Publish the app's own AirApp bundle: create it under the Folder when this
441
441
  * Space has never had it, or propose the local files as an update when it
442
- * already exists. Always a separate, always-review-first ChangeRequest from
443
- * the data layer's `provisionDeclaredResources` — see the note on
444
- * `AirAppNodeDeclaration` for why the two must never share a request.
442
+ * already exists. Always a separate request from the data layer's
443
+ * `provisionDeclaredResources` — see the note on `AirAppNodeDeclaration` for
444
+ * why the two must never share a request. Permission-aware: it merges when the
445
+ * app's credential can write to the Folder, and waits for review when it cannot.
445
446
  *
446
447
  * Call after `provisionDeclaredResources` has confirmed the Folder exists.
447
448
  * Every call proposes the full local file list, even when nothing actually
@@ -464,38 +465,43 @@ async function publishAirApp(client, config, files) {
464
465
  const pendingChangeRequestId = await findPendingAirAppCreate(client, airApp.slug);
465
466
  if (pendingChangeRequestId) return {
466
467
  status: "pending",
468
+ merged: false,
467
469
  changeRequestId: pendingChangeRequestId
468
470
  };
469
- const changeRequest = await client.fileTrees.create({
471
+ const result = await client.fileTrees.create({
470
472
  type: "airapp",
471
473
  parentNodeId: current.folder.nodeId,
472
474
  slug: airApp.slug,
473
475
  name: airApp.name,
474
476
  description: airApp.description ?? "",
475
477
  files,
476
- mergeMode: "replace",
477
- autoMerge: false
478
+ mergeMode: "replace"
478
479
  });
479
- if (changeRequest.materialized) throw setupError("SCHEMA_INCOMPLETE", "AirApp create unexpectedly materialized despite autoMerge: false");
480
- return {
480
+ return result.materialized ? {
481
+ status: "created",
482
+ merged: true,
483
+ nodeId: result.node.id
484
+ } : {
481
485
  status: "created",
482
- changeRequestId: changeRequest.id
486
+ merged: false,
487
+ changeRequestId: result.id
483
488
  };
484
489
  }
485
490
  const operations = buildAirAppFileOperations(files, (await client.fileTrees.listFiles({
486
491
  nodeId: current.airApp.nodeId,
487
492
  type: "airapp"
488
493
  })).map((file) => file.path));
494
+ const changeRequest = await client.fileTrees.createChangeRequest({
495
+ nodeId: current.airApp.nodeId,
496
+ type: "airapp",
497
+ operations,
498
+ message: `Publish ${config.appName} AirApp`,
499
+ submittedBy: config.appId
500
+ });
489
501
  return {
490
502
  status: "updated",
491
- changeRequestId: (await client.fileTrees.createChangeRequest({
492
- nodeId: current.airApp.nodeId,
493
- type: "airapp",
494
- operations,
495
- message: `Publish ${config.appName} AirApp`,
496
- submittedBy: config.appId,
497
- autoMerge: false
498
- })).id
503
+ merged: changeRequest.status === "merged",
504
+ changeRequestId: changeRequest.id
499
505
  };
500
506
  }
501
507
  //#endregion
@@ -39,6 +39,7 @@ declare const BUILTIN_NODE_TYPES: readonly [{
39
39
  readonly capabilities: {
40
40
  readonly container: true;
41
41
  readonly creatable: true;
42
+ readonly commonlyCreated: true;
42
43
  readonly hasDetail: true;
43
44
  readonly publicAccess: "detail";
44
45
  };
@@ -50,6 +51,7 @@ declare const BUILTIN_NODE_TYPES: readonly [{
50
51
  readonly capabilities: {
51
52
  readonly hasDetail: true;
52
53
  readonly creatable: true;
54
+ readonly commonlyCreated: true;
53
55
  readonly publicAccess: "detail";
54
56
  };
55
57
  readonly operations: readonly [{
@@ -206,6 +208,7 @@ declare const BUILTIN_NODE_TYPES: readonly [{
206
208
  readonly capabilities: {
207
209
  readonly hasDetail: true;
208
210
  readonly creatable: true;
211
+ readonly commonlyCreated: true;
209
212
  readonly publicAccess: "detail";
210
213
  };
211
214
  readonly operations: readonly [];
@@ -216,6 +219,7 @@ declare const BUILTIN_NODE_TYPES: readonly [{
216
219
  readonly capabilities: {
217
220
  readonly hasDetail: true;
218
221
  readonly creatable: true;
222
+ readonly commonlyCreated: true;
219
223
  readonly publicAccess: "detail";
220
224
  };
221
225
  readonly operations: readonly [{
@@ -231,6 +235,7 @@ declare const BUILTIN_NODE_TYPES: readonly [{
231
235
  readonly hasDetail: true;
232
236
  readonly creatable: true;
233
237
  readonly publicAccess: "submit";
238
+ readonly hidden: true;
234
239
  };
235
240
  readonly operations: readonly [];
236
241
  }, {
@@ -476,6 +481,16 @@ declare const cloudContract: {
476
481
  nodes: "nodes";
477
482
  records: "records";
478
483
  }>]>, z.ZodTransform<("files" | "names" | "nodes" | "records")[], "files" | "names" | "nodes" | "records" | ("files" | "names" | "nodes" | "records")[]>>>;
484
+ sort: z.ZodDefault<z.ZodOptional<z.ZodEnum<{
485
+ created_asc: "created_asc";
486
+ created_desc: "created_desc";
487
+ relevance: "relevance";
488
+ updated_asc: "updated_asc";
489
+ updated_desc: "updated_desc";
490
+ }>>>;
491
+ updatedAfter: z.ZodOptional<z.ZodString>;
492
+ updatedBefore: z.ZodOptional<z.ZodString>;
493
+ inNodeId: z.ZodOptional<z.ZodString>;
479
494
  }, z.core.$strip>, z.ZodObject<{
480
495
  query: z.ZodString;
481
496
  limit: z.ZodNumber;
@@ -615,6 +630,21 @@ declare const cloudContract: {
615
630
  slug: z.ZodString;
616
631
  path: z.ZodString;
617
632
  updatedAt: z.ZodString;
633
+ icon: z.ZodOptional<z.ZodNullable<z.ZodDiscriminatedUnion<[z.ZodObject<{
634
+ type: z.ZodLiteral<"emoji">;
635
+ value: z.ZodString;
636
+ }, z.core.$strip>, z.ZodObject<{
637
+ type: z.ZodLiteral<"attachment">;
638
+ url: z.ZodString;
639
+ attachmentId: z.ZodString;
640
+ originalUrl: z.ZodOptional<z.ZodString>;
641
+ originalAttachmentId: z.ZodOptional<z.ZodString>;
642
+ crop: z.ZodOptional<z.ZodObject<{
643
+ x: z.ZodNumber;
644
+ y: z.ZodNumber;
645
+ zoom: z.ZodNumber;
646
+ }, z.core.$strip>>;
647
+ }, z.core.$strip>], "type">>>;
618
648
  }, z.core.$strip>>, import("@orpc/contract").MergedErrorMap<Record<never, never>, Record<never, never>>, Record<never, never>>;
619
649
  isDescendant: import("@orpc/contract").ContractProcedure<z.ZodObject<{
620
650
  nodeId: z.ZodString;
@@ -10723,6 +10753,7 @@ declare const cloudContract: {
10723
10753
  targetBaseId: z.ZodOptional<z.ZodString>;
10724
10754
  targetBaseSlug: z.ZodOptional<z.ZodString>;
10725
10755
  }, z.core.$strip>>>;
10756
+ type: z.ZodOptional<z.ZodNever>;
10726
10757
  }, z.core.$strip>;
10727
10758
  message: z.ZodOptional<z.ZodString>;
10728
10759
  submittedBy: z.ZodDefault<z.ZodOptional<z.ZodString>>;
@@ -11711,6 +11742,54 @@ declare const cloudContract: {
11711
11742
  }, z.core.$strip>, import("@orpc/contract").MergedErrorMap<Record<never, never>, Record<never, never>>, Record<never, never>>;
11712
11743
  };
11713
11744
  fileTrees: {
11745
+ previewConfig: import("@orpc/contract").ContractProcedure<import("@orpc/contract").Schema<unknown, unknown>, z.ZodObject<{
11746
+ provider: z.ZodEnum<{
11747
+ builtin: "builtin";
11748
+ previewfile: "previewfile";
11749
+ }>;
11750
+ status: z.ZodEnum<{
11751
+ invalid_configuration: "invalid_configuration";
11752
+ not_configured: "not_configured";
11753
+ ready: "ready";
11754
+ }>;
11755
+ credentialSource: z.ZodEnum<{
11756
+ environment: "environment";
11757
+ none: "none";
11758
+ vault: "vault";
11759
+ }>;
11760
+ credentialConfigured: z.ZodBoolean;
11761
+ maxFileSizeBytes: z.ZodNumber;
11762
+ sessionTtlMinutes: z.ZodNumber;
11763
+ vaultEncryptionConfigured: z.ZodNullable<z.ZodBoolean>;
11764
+ }, z.core.$strip>, import("@orpc/contract").MergedErrorMap<Record<never, never>, Record<never, never>>, Record<never, never>>;
11765
+ preparePreview: import("@orpc/contract").ContractProcedure<z.ZodObject<{
11766
+ nodeId: z.ZodString;
11767
+ filePath: z.ZodString;
11768
+ type: z.ZodLiteral<"drive">;
11769
+ }, z.core.$strip>, z.ZodDiscriminatedUnion<[z.ZodObject<{
11770
+ state: z.ZodLiteral<"builtin">;
11771
+ provider: z.ZodLiteral<"builtin">;
11772
+ }, z.core.$strip>, z.ZodObject<{
11773
+ state: z.ZodLiteral<"ready">;
11774
+ provider: z.ZodLiteral<"previewfile">;
11775
+ previewUrl: z.ZodString;
11776
+ expiresAt: z.ZodString;
11777
+ }, z.core.$strip>, z.ZodObject<{
11778
+ state: z.ZodLiteral<"unavailable">;
11779
+ provider: z.ZodLiteral<"previewfile">;
11780
+ reason: z.ZodEnum<{
11781
+ authentication_failed: "authentication_failed";
11782
+ file_too_large: "file_too_large";
11783
+ invalid_configuration: "invalid_configuration";
11784
+ invalid_response: "invalid_response";
11785
+ not_configured: "not_configured";
11786
+ rate_limited: "rate_limited";
11787
+ service_unavailable: "service_unavailable";
11788
+ timeout: "timeout";
11789
+ unsupported: "unsupported";
11790
+ }>;
11791
+ retryable: z.ZodBoolean;
11792
+ }, z.core.$strip>], "state">, import("@orpc/contract").MergedErrorMap<Record<never, never>, Record<never, never>>, Record<never, never>>;
11714
11793
  create: import("@orpc/contract").ContractProcedure<z.ZodObject<{
11715
11794
  parentNodeId: z.ZodOptional<z.ZodString>;
11716
11795
  slug: z.ZodString;
@@ -14076,7 +14155,11 @@ declare const cloudContract: {
14076
14155
  nodeId: z.ZodString;
14077
14156
  }, z.core.$strip>, z.ZodObject<{
14078
14157
  changeRequestId: z.ZodString;
14079
- status: z.ZodLiteral<"pending_review">;
14158
+ status: z.ZodEnum<{
14159
+ merged: "merged";
14160
+ pending_review: "pending_review";
14161
+ }>;
14162
+ recordId: z.ZodOptional<z.ZodString>;
14080
14163
  }, z.core.$strip>, import("@orpc/contract").MergedErrorMap<Record<never, never>, Record<never, never>>, Record<never, never>>;
14081
14164
  };
14082
14165
  assets: {
@@ -14788,6 +14871,15 @@ declare const cloudContract: {
14788
14871
  createdAt: z.ZodString;
14789
14872
  lastActivityAt: z.ZodString;
14790
14873
  error: z.ZodDefault<z.ZodNullable<z.ZodString>>;
14874
+ modelOption: z.ZodDefault<z.ZodNullable<z.ZodObject<{
14875
+ id: z.ZodString;
14876
+ name: z.ZodString;
14877
+ currentValue: z.ZodString;
14878
+ options: z.ZodArray<z.ZodObject<{
14879
+ value: z.ZodString;
14880
+ name: z.ZodString;
14881
+ }, z.core.$strip>>;
14882
+ }, z.core.$strip>>>;
14791
14883
  }, z.core.$strip>>;
14792
14884
  ownedByCurrentUser: z.ZodBoolean;
14793
14885
  }, z.core.$strip>>, import("@orpc/contract").MergedErrorMap<Record<never, never>, Record<never, never>>, Record<never, never>>;
@@ -14812,6 +14904,15 @@ declare const cloudContract: {
14812
14904
  createdAt: z.ZodString;
14813
14905
  lastActivityAt: z.ZodString;
14814
14906
  error: z.ZodDefault<z.ZodNullable<z.ZodString>>;
14907
+ modelOption: z.ZodDefault<z.ZodNullable<z.ZodObject<{
14908
+ id: z.ZodString;
14909
+ name: z.ZodString;
14910
+ currentValue: z.ZodString;
14911
+ options: z.ZodArray<z.ZodObject<{
14912
+ value: z.ZodString;
14913
+ name: z.ZodString;
14914
+ }, z.core.$strip>>;
14915
+ }, z.core.$strip>>>;
14815
14916
  }, z.core.$strip>>, import("@orpc/contract").MergedErrorMap<Record<never, never>, Record<never, never>>, Record<never, never>>;
14816
14917
  create: import("@orpc/contract").ContractProcedure<z.ZodObject<{
14817
14918
  slug: z.ZodString;
@@ -14834,6 +14935,15 @@ declare const cloudContract: {
14834
14935
  createdAt: z.ZodString;
14835
14936
  lastActivityAt: z.ZodString;
14836
14937
  error: z.ZodDefault<z.ZodNullable<z.ZodString>>;
14938
+ modelOption: z.ZodDefault<z.ZodNullable<z.ZodObject<{
14939
+ id: z.ZodString;
14940
+ name: z.ZodString;
14941
+ currentValue: z.ZodString;
14942
+ options: z.ZodArray<z.ZodObject<{
14943
+ value: z.ZodString;
14944
+ name: z.ZodString;
14945
+ }, z.core.$strip>>;
14946
+ }, z.core.$strip>>>;
14837
14947
  }, z.core.$strip>, import("@orpc/contract").MergedErrorMap<Record<never, never>, Record<never, never>>, Record<never, never>>;
14838
14948
  prompt: import("@orpc/contract").ContractProcedure<z.ZodObject<{
14839
14949
  sessionId: z.ZodString;
@@ -14848,10 +14958,19 @@ declare const cloudContract: {
14848
14958
  mimeType: z.ZodString;
14849
14959
  filename: z.ZodOptional<z.ZodString>;
14850
14960
  }, z.core.$strip>>>;
14961
+ }, z.core.$strip>, z.ZodDiscriminatedUnion<[z.ZodObject<{
14962
+ accepted: z.ZodLiteral<true>;
14963
+ sessionId: z.ZodString;
14851
14964
  }, z.core.$strip>, z.ZodObject<{
14852
- accepted: z.ZodBoolean;
14965
+ accepted: z.ZodLiteral<false>;
14853
14966
  sessionId: z.ZodString;
14854
- }, z.core.$strip>, import("@orpc/contract").MergedErrorMap<Record<never, never>, Record<never, never>>, Record<never, never>>;
14967
+ status: z.ZodEnum<{
14968
+ ended: "ended";
14969
+ failed: "failed";
14970
+ }>;
14971
+ promptRecorded: z.ZodBoolean;
14972
+ message: z.ZodString;
14973
+ }, z.core.$strip>], "accepted">, import("@orpc/contract").MergedErrorMap<Record<never, never>, Record<never, never>>, Record<never, never>>;
14855
14974
  cancel: import("@orpc/contract").ContractProcedure<z.ZodObject<{
14856
14975
  sessionId: z.ZodString;
14857
14976
  }, z.core.$strip>, z.ZodObject<{
@@ -14869,6 +14988,39 @@ declare const cloudContract: {
14869
14988
  }, z.core.$strip>, z.ZodObject<{
14870
14989
  ok: z.ZodBoolean;
14871
14990
  }, z.core.$strip>, import("@orpc/contract").MergedErrorMap<Record<never, never>, Record<never, never>>, Record<never, never>>;
14991
+ setConfigOption: import("@orpc/contract").ContractProcedure<z.ZodObject<{
14992
+ sessionId: z.ZodString;
14993
+ configId: z.ZodString;
14994
+ value: z.ZodString;
14995
+ }, z.core.$strip>, z.ZodObject<{
14996
+ id: z.ZodString;
14997
+ slug: z.ZodString;
14998
+ agentName: z.ZodString;
14999
+ transport: z.ZodEnum<{
15000
+ "local-subprocess": "local-subprocess";
15001
+ "remote-websocket": "remote-websocket";
15002
+ }>;
15003
+ status: z.ZodEnum<{
15004
+ busy: "busy";
15005
+ connecting: "connecting";
15006
+ ended: "ended";
15007
+ failed: "failed";
15008
+ idle: "idle";
15009
+ waiting_permission: "waiting_permission";
15010
+ }>;
15011
+ createdAt: z.ZodString;
15012
+ lastActivityAt: z.ZodString;
15013
+ error: z.ZodDefault<z.ZodNullable<z.ZodString>>;
15014
+ modelOption: z.ZodDefault<z.ZodNullable<z.ZodObject<{
15015
+ id: z.ZodString;
15016
+ name: z.ZodString;
15017
+ currentValue: z.ZodString;
15018
+ options: z.ZodArray<z.ZodObject<{
15019
+ value: z.ZodString;
15020
+ name: z.ZodString;
15021
+ }, z.core.$strip>>;
15022
+ }, z.core.$strip>>>;
15023
+ }, z.core.$strip>, import("@orpc/contract").MergedErrorMap<Record<never, never>, Record<never, never>>, Record<never, never>>;
14872
15024
  subscribe: import("@orpc/contract").ContractProcedure<z.ZodObject<{
14873
15025
  sessionId: z.ZodString;
14874
15026
  afterSeq: z.ZodDefault<z.ZodNumber>;
@@ -15602,6 +15754,52 @@ declare const cloudContract: {
15602
15754
  pendingChangeRequests: z.ZodNumber;
15603
15755
  warnings: z.ZodDefault<z.ZodArray<z.ZodString>>;
15604
15756
  }, z.core.$strip>, import("@orpc/contract").MergedErrorMap<Record<never, never>, Record<never, never>>, Record<never, never>>;
15757
+ fromGithubStream: import("@orpc/contract").ContractProcedure<z.ZodObject<{
15758
+ repoUrl: z.ZodString;
15759
+ intoFolder: z.ZodOptional<z.ZodString>;
15760
+ rename: z.ZodOptional<z.ZodBoolean>;
15761
+ autoMerge: z.ZodOptional<z.ZodBoolean>;
15762
+ }, z.core.$strip>, import("@orpc/contract").Schema<AsyncIteratorObject<{
15763
+ kind: "progress";
15764
+ message: string;
15765
+ } | {
15766
+ kind: "done";
15767
+ result: {
15768
+ targetFolderSlug: string;
15769
+ targetFolderNodeId: string;
15770
+ created: {
15771
+ folders: number;
15772
+ docs: number;
15773
+ bases: number;
15774
+ views: number;
15775
+ records: number;
15776
+ fileTreeNodes: number;
15777
+ files: number;
15778
+ };
15779
+ pendingChangeRequests: number;
15780
+ warnings?: string[] | undefined;
15781
+ };
15782
+ }, unknown, void>, AsyncIteratorClass<{
15783
+ kind: "progress";
15784
+ message: string;
15785
+ } | {
15786
+ kind: "done";
15787
+ result: {
15788
+ targetFolderSlug: string;
15789
+ targetFolderNodeId: string;
15790
+ created: {
15791
+ folders: number;
15792
+ docs: number;
15793
+ bases: number;
15794
+ views: number;
15795
+ records: number;
15796
+ fileTreeNodes: number;
15797
+ files: number;
15798
+ };
15799
+ pendingChangeRequests: number;
15800
+ warnings: string[];
15801
+ };
15802
+ }, unknown, void>>, import("@orpc/contract").MergedErrorMap<Record<never, never>, Record<never, never>>, Record<never, never>>;
15605
15803
  };
15606
15804
  templates: {
15607
15805
  list: import("@orpc/contract").ContractProcedure<z.ZodDefault<z.ZodOptional<z.ZodObject<{
@@ -15641,6 +15839,7 @@ declare const cloudContract: {
15641
15839
  }>>;
15642
15840
  tags: z.ZodDefault<z.ZodArray<z.ZodString>>;
15643
15841
  screenshots: z.ZodDefault<z.ZodArray<z.ZodString>>;
15842
+ video: z.ZodOptional<z.ZodString>;
15644
15843
  agentPrompts: z.ZodDefault<z.ZodArray<z.ZodString>>;
15645
15844
  version: z.ZodOptional<z.ZodString>;
15646
15845
  author: z.ZodOptional<z.ZodString>;
@@ -23110,10 +23309,24 @@ declare const SubmitFormInputSchema: z.ZodObject<{
23110
23309
  captchaToken: z.ZodOptional<z.ZodString>;
23111
23310
  }, z.core.$strip>;
23112
23311
  type SubmitFormDTO = z.input<typeof SubmitFormInputSchema>;
23113
- /** What the submit endpoint returns — the pending ChangeRequest id, never data. */
23312
+ /**
23313
+ * What the submit endpoint returns — a ChangeRequest id and what happened to it,
23314
+ * never the record data.
23315
+ *
23316
+ * `status` is permission-aware, like every other write: `merged` when the
23317
+ * submitter holds `write` on the target Base and the submission landed straight
23318
+ * away, `pending_review` when it is waiting for a human. An ANONYMOUS visitor can
23319
+ * never reach `merged`: permission is resolved against the target Base, which a
23320
+ * form does not share publicly, and a public-link request is capped at `read`
23321
+ * even where it is shared. So a public form still always waits.
23322
+ */
23114
23323
  declare const FormSubmitResultSchema: z.ZodObject<{
23115
23324
  changeRequestId: z.ZodString;
23116
- status: z.ZodLiteral<"pending_review">;
23325
+ status: z.ZodEnum<{
23326
+ merged: "merged";
23327
+ pending_review: "pending_review";
23328
+ }>;
23329
+ recordId: z.ZodOptional<z.ZodString>;
23117
23330
  }, z.core.$strip>;
23118
23331
  type FormSubmitResultVO = z.infer<typeof FormSubmitResultSchema>;
23119
23332
  //#endregion
@@ -24789,6 +25002,33 @@ declare const NodeDetailVOSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
24789
25002
  type NodeDetailVO = z.infer<typeof NodeDetailVOSchema>;
24790
25003
  //#endregion
24791
25004
  //#region ../../packages/busabase-contract/src/domains/filetree/types.d.ts
25005
+ type FilePreviewProvider = "builtin" | "previewfile";
25006
+ type FilePreviewCredentialSource = "environment" | "vault" | "none";
25007
+ type FilePreviewConfigurationStatus = "ready" | "not_configured" | "invalid_configuration";
25008
+ type FilePreviewUnavailableReason = "not_configured" | "invalid_configuration" | "file_too_large" | "unsupported" | "authentication_failed" | "rate_limited" | "timeout" | "service_unavailable" | "invalid_response";
25009
+ interface FilePreviewConfigVO {
25010
+ provider: FilePreviewProvider;
25011
+ status: FilePreviewConfigurationStatus;
25012
+ credentialSource: FilePreviewCredentialSource;
25013
+ credentialConfigured: boolean;
25014
+ maxFileSizeBytes: number;
25015
+ sessionTtlMinutes: number;
25016
+ vaultEncryptionConfigured: boolean | null;
25017
+ }
25018
+ type FilePreviewVO = {
25019
+ state: "builtin";
25020
+ provider: "builtin";
25021
+ } | {
25022
+ state: "ready";
25023
+ provider: "previewfile";
25024
+ previewUrl: string;
25025
+ expiresAt: string;
25026
+ } | {
25027
+ state: "unavailable";
25028
+ provider: "previewfile";
25029
+ reason: FilePreviewUnavailableReason;
25030
+ retryable: boolean;
25031
+ };
24792
25032
  interface FileTreeFileVO {
24793
25033
  path: string;
24794
25034
  name: string;
@@ -25087,6 +25327,10 @@ declare const UpdateVaultSettingsInputSchema: z.ZodObject<{
25087
25327
  }, z.core.$strip>>;
25088
25328
  }, z.core.$strip>;
25089
25329
  type UpdateVaultSettingsDTO = z.infer<typeof UpdateVaultSettingsInputSchema>;
25330
+ declare const UpdatePreviewFileCredentialInputSchema: z.ZodObject<{
25331
+ apiKey: z.ZodNullable<z.ZodString>;
25332
+ }, z.core.$strip>;
25333
+ type UpdatePreviewFileCredentialDTO = z.infer<typeof UpdatePreviewFileCredentialInputSchema>;
25090
25334
  declare const VaultItemVOSchema: z.ZodObject<{
25091
25335
  kind: z.ZodEnum<{
25092
25336
  secret: "secret";
@@ -25203,6 +25447,8 @@ interface NodeSearchResultVO {
25203
25447
  slug: string;
25204
25448
  path: string;
25205
25449
  updatedAt: string;
25450
+ /** Optional: absent on an older server response. See `NodeVO.icon`. */
25451
+ icon?: NodeIcon | null;
25206
25452
  }
25207
25453
  interface NodeVO {
25208
25454
  id: string;
@@ -25591,4 +25837,4 @@ declare function resolveConfig(config?: BusabaseConfig): ResolvedConfig;
25591
25837
  */
25592
25838
  declare function createBusabaseClient(config?: BusabaseConfig): BusabaseClient;
25593
25839
  //#endregion
25594
- export { AssetUsageVO as $, NodeVO as A, ViewConfigVO as At, UpdateVaultSettingsDTO as B, NodeIconSchema as Bt, CommentVO as C, GalleryCardSize as Ct, MentionInboxItemVO as D, RecordVO as Dt, LookupRollup as E, RecordLinkVO as Et, SearchResponseVO as F, ViewVO as Ft, VaultItemVO as G, VaultEnvironment as H, CreatableNodeType as Ht, SearchResultKind as I, AttachmentRef as It, VaultSettingsVO as J, VaultRuntimeEnv as K, SearchResultVO as L, CloudContract as Lt, OperationVO as M, ViewFilterVO as Mt, ReviewVO as N, ViewSortVO as Nt, MentionInboxPageVO as O, VIEW_FIELD_MAX_WIDTH as Ot, ReviewVerdict as P, ViewType as Pt, AssetTextStatus as Q, SourceAttributionVO as R, cloudContract as Rt, CommentSubjectType as S, BaseVO as St, FieldType as T, GanttScale as Tt, VaultItemInput as U, NodeType as Ut, VaultAccessPolicy as V, CREATABLE_NODE_TYPES as Vt, VaultItemKind as W, OperationKind as Wt, FileNodeVO as X, FileNodeMetadata as Y, AssetDetailVO as Z, ChangeRequestVO as _, ListFormsVO as _t, createBusabaseClient as a, FileTreeReadFileVO as at, CommentMentionTargetType as b, AssetAttachmentRef as bt, AuditAction as c, CreateFormDTO as ct, ChangeRequestBatchFailureVO as d, FormPageSourceVO as dt, AssetVO as et, ChangeRequestCountsVO as f, FormShareVO as ft, ChangeRequestTargetType as g, ListFormsDTO as gt, ChangeRequestStatus as h, FormVO as ht, ResolvedConfig as i, FileTreeNodeVO as it, OperationStatus as j, ViewFilterOperator as jt, NodeSearchResultVO as k, VIEW_FIELD_MIN_WIDTH as kt, AuditEventVO as l, FormBoundFieldVO as lt, ChangeRequestReviewBatchResultVO as m, FormThemeVO as mt, BusabaseConfig as n, GrepResultVO as nt, resolveConfig as o, NodeDetailVO as ot, ChangeRequestMergeBatchResultVO as p, FormSubmitResultVO as pt, VaultScopeType as q, DEFAULT_BASE_URL as r, FileTreeFileVO as rt, AgentTaskVO as s, ActivityItemVO as st, BusabaseClient as t, GrepInputDTO as tt, BusabaseSourceChannel as u, FormFieldBindingVO as ut, CommentMentionDispatchStatus as v, SubmitFormDTO as vt, CommitVO as w, GalleryCoverFit as wt, CommentMentionVO as x, BaseFieldVO as xt, CommentMentionInputDTO as y, UpdateFormDTO as yt, UserRefVO as z, NodeIcon as zt };
25840
+ export { AssetTextStatus as $, NodeVO as A, GalleryCardSize as At, UpdatePreviewFileCredentialDTO as B, ViewSortVO as Bt, CommentVO as C, ListFormsDTO as Ct, MentionInboxItemVO as D, AssetAttachmentRef as Dt, LookupRollup as E, UpdateFormDTO as Et, SearchResponseVO as F, VIEW_FIELD_MAX_WIDTH as Ft, VaultItemKind as G, cloudContract as Gt, VaultAccessPolicy as H, ViewVO as Ht, SearchResultKind as I, VIEW_FIELD_MIN_WIDTH as It, VaultScopeType as J, CREATABLE_NODE_TYPES as Jt, VaultItemVO as K, NodeIcon as Kt, SearchResultVO as L, ViewConfigVO as Lt, OperationVO as M, GanttScale as Mt, ReviewVO as N, RecordLinkVO as Nt, MentionInboxPageVO as O, BaseFieldVO as Ot, ReviewVerdict as P, RecordVO as Pt, AssetDetailVO as Q, SourceAttributionVO as R, ViewFilterOperator as Rt, CommentSubjectType as S, FormVO as St, FieldType as T, SubmitFormDTO as Tt, VaultEnvironment as U, AttachmentRef as Ut, UpdateVaultSettingsDTO as V, ViewType as Vt, VaultItemInput as W, CloudContract as Wt, FileNodeMetadata as X, NodeType as Xt, VaultSettingsVO as Y, CreatableNodeType as Yt, FileNodeVO as Z, OperationKind as Zt, ChangeRequestVO as _, FormFieldBindingVO as _t, createBusabaseClient as a, FilePreviewConfigurationStatus as at, CommentMentionTargetType as b, FormSubmitResultVO as bt, AuditAction as c, FilePreviewUnavailableReason as ct, ChangeRequestBatchFailureVO as d, FileTreeNodeVO as dt, AssetUsageVO as et, ChangeRequestCountsVO as f, FileTreeReadFileVO as ft, ChangeRequestTargetType as g, FormBoundFieldVO as gt, ChangeRequestStatus as h, CreateFormDTO as ht, ResolvedConfig as i, FilePreviewConfigVO as it, OperationStatus as j, GalleryCoverFit as jt, NodeSearchResultVO as k, BaseVO as kt, AuditEventVO as l, FilePreviewVO as lt, ChangeRequestReviewBatchResultVO as m, ActivityItemVO as mt, BusabaseConfig as n, GrepInputDTO as nt, resolveConfig as o, FilePreviewCredentialSource as ot, ChangeRequestMergeBatchResultVO as p, NodeDetailVO as pt, VaultRuntimeEnv as q, NodeIconSchema as qt, DEFAULT_BASE_URL as r, GrepResultVO as rt, AgentTaskVO as s, FilePreviewProvider as st, BusabaseClient as t, AssetVO as tt, BusabaseSourceChannel as u, FileTreeFileVO as ut, CommentMentionDispatchStatus as v, FormPageSourceVO as vt, CommitVO as w, ListFormsVO as wt, CommentMentionVO as x, FormThemeVO as xt, CommentMentionInputDTO as y, FormShareVO as yt, UserRefVO as z, ViewFilterVO as zt };
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { $ as AssetUsageVO, A as NodeVO, At as ViewConfigVO, B as UpdateVaultSettingsDTO, Bt as NodeIconSchema, C as CommentVO, Ct as GalleryCardSize, D as MentionInboxItemVO, Dt as RecordVO, E as LookupRollup, Et as RecordLinkVO, F as SearchResponseVO, Ft as ViewVO, G as VaultItemVO, H as VaultEnvironment, Ht as CreatableNodeType, I as SearchResultKind, It as AttachmentRef, J as VaultSettingsVO, K as VaultRuntimeEnv, L as SearchResultVO, Lt as CloudContract, M as OperationVO, Mt as ViewFilterVO, N as ReviewVO, Nt as ViewSortVO, O as MentionInboxPageVO, Ot as VIEW_FIELD_MAX_WIDTH, P as ReviewVerdict, Pt as ViewType, Q as AssetTextStatus, R as SourceAttributionVO, Rt as cloudContract, S as CommentSubjectType, St as BaseVO, T as FieldType, Tt as GanttScale, U as VaultItemInput, Ut as NodeType, V as VaultAccessPolicy, Vt as CREATABLE_NODE_TYPES, W as VaultItemKind, Wt as OperationKind, X as FileNodeVO, Y as FileNodeMetadata, Z as AssetDetailVO, _ as ChangeRequestVO, _t as ListFormsVO, a as createBusabaseClient, at as FileTreeReadFileVO, b as CommentMentionTargetType, bt as AssetAttachmentRef, c as AuditAction, ct as CreateFormDTO, d as ChangeRequestBatchFailureVO, dt as FormPageSourceVO, et as AssetVO, f as ChangeRequestCountsVO, ft as FormShareVO, g as ChangeRequestTargetType, gt as ListFormsDTO, h as ChangeRequestStatus, ht as FormVO, i as ResolvedConfig, it as FileTreeNodeVO, j as OperationStatus, jt as ViewFilterOperator, k as NodeSearchResultVO, kt as VIEW_FIELD_MIN_WIDTH, l as AuditEventVO, lt as FormBoundFieldVO, m as ChangeRequestReviewBatchResultVO, mt as FormThemeVO, n as BusabaseConfig, nt as GrepResultVO, o as resolveConfig, ot as NodeDetailVO, p as ChangeRequestMergeBatchResultVO, pt as FormSubmitResultVO, q as VaultScopeType, r as DEFAULT_BASE_URL, rt as FileTreeFileVO, s as AgentTaskVO, st as ActivityItemVO, t as BusabaseClient, tt as GrepInputDTO, u as BusabaseSourceChannel, ut as FormFieldBindingVO, v as CommentMentionDispatchStatus, vt as SubmitFormDTO, w as CommitVO, wt as GalleryCoverFit, x as CommentMentionVO, xt as BaseFieldVO, y as CommentMentionInputDTO, yt as UpdateFormDTO, z as UserRefVO, zt as NodeIcon } from "./client-SOzg2cGv.js";
1
+ import { $ as AssetTextStatus, A as NodeVO, At as GalleryCardSize, B as UpdatePreviewFileCredentialDTO, Bt as ViewSortVO, C as CommentVO, Ct as ListFormsDTO, D as MentionInboxItemVO, Dt as AssetAttachmentRef, E as LookupRollup, Et as UpdateFormDTO, F as SearchResponseVO, Ft as VIEW_FIELD_MAX_WIDTH, G as VaultItemKind, Gt as cloudContract, H as VaultAccessPolicy, Ht as ViewVO, I as SearchResultKind, It as VIEW_FIELD_MIN_WIDTH, J as VaultScopeType, Jt as CREATABLE_NODE_TYPES, K as VaultItemVO, Kt as NodeIcon, L as SearchResultVO, Lt as ViewConfigVO, M as OperationVO, Mt as GanttScale, N as ReviewVO, Nt as RecordLinkVO, O as MentionInboxPageVO, Ot as BaseFieldVO, P as ReviewVerdict, Pt as RecordVO, Q as AssetDetailVO, R as SourceAttributionVO, Rt as ViewFilterOperator, S as CommentSubjectType, St as FormVO, T as FieldType, Tt as SubmitFormDTO, U as VaultEnvironment, Ut as AttachmentRef, V as UpdateVaultSettingsDTO, Vt as ViewType, W as VaultItemInput, Wt as CloudContract, X as FileNodeMetadata, Xt as NodeType, Y as VaultSettingsVO, Yt as CreatableNodeType, Z as FileNodeVO, Zt as OperationKind, _ as ChangeRequestVO, _t as FormFieldBindingVO, a as createBusabaseClient, at as FilePreviewConfigurationStatus, b as CommentMentionTargetType, bt as FormSubmitResultVO, c as AuditAction, ct as FilePreviewUnavailableReason, d as ChangeRequestBatchFailureVO, dt as FileTreeNodeVO, et as AssetUsageVO, f as ChangeRequestCountsVO, ft as FileTreeReadFileVO, g as ChangeRequestTargetType, gt as FormBoundFieldVO, h as ChangeRequestStatus, ht as CreateFormDTO, i as ResolvedConfig, it as FilePreviewConfigVO, j as OperationStatus, jt as GalleryCoverFit, k as NodeSearchResultVO, kt as BaseVO, l as AuditEventVO, lt as FilePreviewVO, m as ChangeRequestReviewBatchResultVO, mt as ActivityItemVO, n as BusabaseConfig, nt as GrepInputDTO, o as resolveConfig, ot as FilePreviewCredentialSource, p as ChangeRequestMergeBatchResultVO, pt as NodeDetailVO, q as VaultRuntimeEnv, qt as NodeIconSchema, r as DEFAULT_BASE_URL, rt as GrepResultVO, s as AgentTaskVO, st as FilePreviewProvider, t as BusabaseClient, tt as AssetVO, u as BusabaseSourceChannel, ut as FileTreeFileVO, v as CommentMentionDispatchStatus, vt as FormPageSourceVO, w as CommitVO, wt as ListFormsVO, x as CommentMentionVO, xt as FormThemeVO, y as CommentMentionInputDTO, yt as FormShareVO, z as UserRefVO, zt as ViewFilterVO } from "./client-CaeGnNfw.js";
2
2
  import { z } from "zod";
3
3
  //#region src/url.d.ts
4
4
  /**
@@ -426,4 +426,4 @@ declare class Busabase {
426
426
  }>>>>;
427
427
  }
428
428
  //#endregion
429
- export { type ActivityItemVO, type AgentTaskVO, type FileTreeFileVO as AirAppFileVO, type FileTreeFileVO as DriveFileVO, type FileTreeFileVO, type FileTreeFileVO as SkillFileVO, type FileTreeReadFileVO as AirAppReadFileVO, type FileTreeReadFileVO as DriveReadFileVO, type FileTreeReadFileVO, type FileTreeReadFileVO as SkillReadFileVO, type FileTreeNodeVO as AirAppVO, type FileTreeNodeVO as DriveVO, type FileTreeNodeVO, type FileTreeNodeVO as SkillVO, type AssetAttachmentRef, type AssetDetailVO, type AssetTextStatus, type AssetUsageVO, type AssetVO, type AttachmentRef, type AuditAction, type AuditEventVO, type BaseFieldVO, type BaseVO, Busabase, type BusabaseAssetsClient, BusabaseChangeRequestsClient, type BusabaseClient, type BusabaseConfig, type BusabaseRecordsClient, type BusabaseSourceChannel, CREATABLE_NODE_TYPES, type ChangeRequestBatchFailureVO, type ChangeRequestCountsVO, type ChangeRequestMergeBatchResultVO, type ChangeRequestReviewBatchResultVO, type ChangeRequestStatus, type ChangeRequestTargetType, type ChangeRequestVO, type CloudContract, type CommentMentionDispatchStatus, type CommentMentionInputDTO, type CommentMentionTargetType, type CommentMentionVO, type CommentSubjectType, type CommentVO, type CommitVO, type CreatableNodeType, type CreateFormDTO, DEFAULT_BASE_URL, type FieldType, type FileNodeMetadata, type FileNodeVO, type FormBoundFieldVO, type FormFieldBindingVO, type FormPageSourceVO, type FormShareVO, type FormSubmitResultVO, type FormThemeVO, type FormVO, type GalleryCardSize, type GalleryCoverFit, type GanttScale, type ListFormsDTO, type ListFormsVO, type LookupRollup, type MentionInboxItemVO, type MentionInboxPageVO, type NodeDetailVO, type NodeIcon, type NodeIconSchema, type NodeSearchResultVO, type NodeType, type NodeVO, type NodeWebUrlInput, type OperationKind, type OperationStatus, type OperationVO, type RecordByFieldInput, type RecordLinkVO, type RecordVO, type ResolvedConfig, type ReviewVO, type ReviewVerdict, type SearchResponseVO, type SearchResultKind, type SearchResultVO, type SourceAttributionVO, type SubmitFormDTO, type UpdateFormDTO, type UpdateVaultSettingsDTO, type UserRefVO, type VIEW_FIELD_MAX_WIDTH, type VIEW_FIELD_MIN_WIDTH, type VaultAccessPolicy, type VaultEnvironment, type VaultItemInput, type VaultItemKind, type VaultItemVO, type VaultRuntimeEnv, type VaultScopeType, type VaultSettingsVO, type ViewConfigVO, type ViewFilterOperator, type ViewFilterVO, type ViewSortVO, type ViewType, type ViewVO, cloudContract, createBusabaseClient, getRecordByField, grepAssets, nodeWebUrl, normalizeBaseUrl, resolveConfig, toFilesOnlyGrepResult, toUnifiedFilesGrepInput };
429
+ export { type ActivityItemVO, type AgentTaskVO, type FileTreeFileVO as AirAppFileVO, type FileTreeFileVO as DriveFileVO, type FileTreeFileVO, type FileTreeFileVO as SkillFileVO, type FileTreeReadFileVO as AirAppReadFileVO, type FileTreeReadFileVO as DriveReadFileVO, type FileTreeReadFileVO, type FileTreeReadFileVO as SkillReadFileVO, type FileTreeNodeVO as AirAppVO, type FileTreeNodeVO as DriveVO, type FileTreeNodeVO, type FileTreeNodeVO as SkillVO, type AssetAttachmentRef, type AssetDetailVO, type AssetTextStatus, type AssetUsageVO, type AssetVO, type AttachmentRef, type AuditAction, type AuditEventVO, type BaseFieldVO, type BaseVO, Busabase, type BusabaseAssetsClient, BusabaseChangeRequestsClient, type BusabaseClient, type BusabaseConfig, type BusabaseRecordsClient, type BusabaseSourceChannel, CREATABLE_NODE_TYPES, type ChangeRequestBatchFailureVO, type ChangeRequestCountsVO, type ChangeRequestMergeBatchResultVO, type ChangeRequestReviewBatchResultVO, type ChangeRequestStatus, type ChangeRequestTargetType, type ChangeRequestVO, type CloudContract, type CommentMentionDispatchStatus, type CommentMentionInputDTO, type CommentMentionTargetType, type CommentMentionVO, type CommentSubjectType, type CommentVO, type CommitVO, type CreatableNodeType, type CreateFormDTO, DEFAULT_BASE_URL, type FieldType, type FileNodeMetadata, type FileNodeVO, type FilePreviewConfigVO, type FilePreviewConfigurationStatus, type FilePreviewCredentialSource, type FilePreviewProvider, type FilePreviewUnavailableReason, type FilePreviewVO, type FormBoundFieldVO, type FormFieldBindingVO, type FormPageSourceVO, type FormShareVO, type FormSubmitResultVO, type FormThemeVO, type FormVO, type GalleryCardSize, type GalleryCoverFit, type GanttScale, type ListFormsDTO, type ListFormsVO, type LookupRollup, type MentionInboxItemVO, type MentionInboxPageVO, type NodeDetailVO, type NodeIcon, type NodeIconSchema, type NodeSearchResultVO, type NodeType, type NodeVO, type NodeWebUrlInput, type OperationKind, type OperationStatus, type OperationVO, type RecordByFieldInput, type RecordLinkVO, type RecordVO, type ResolvedConfig, type ReviewVO, type ReviewVerdict, type SearchResponseVO, type SearchResultKind, type SearchResultVO, type SourceAttributionVO, type SubmitFormDTO, type UpdateFormDTO, type UpdatePreviewFileCredentialDTO, type UpdateVaultSettingsDTO, type UserRefVO, type VIEW_FIELD_MAX_WIDTH, type VIEW_FIELD_MIN_WIDTH, type VaultAccessPolicy, type VaultEnvironment, type VaultItemInput, type VaultItemKind, type VaultItemVO, type VaultRuntimeEnv, type VaultScopeType, type VaultSettingsVO, type ViewConfigVO, type ViewFilterOperator, type ViewFilterVO, type ViewSortVO, type ViewType, type ViewVO, cloudContract, createBusabaseClient, getRecordByField, grepAssets, nodeWebUrl, normalizeBaseUrl, resolveConfig, toFilesOnlyGrepResult, toUnifiedFilesGrepInput };
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { n as TemplateRiskLevelSchema } from "./template-C5oO6Au_.js";
1
+ import { n as TemplateRiskLevelSchema } from "./template-D10kjpd-.js";
2
2
  import { t as normalizeBaseUrl } from "./url-B8GMXalA.js";
3
3
  import { ORPCError, createORPCClient } from "@orpc/client";
4
4
  import { OpenAPILink } from "@orpc/openapi-client/fetch";
@@ -124,6 +124,25 @@ const AgentPermissionRequestVOSchema = z.object({
124
124
  description: z.string().optional(),
125
125
  options: z.array(AgentPermissionOptionVOSchema)
126
126
  });
127
+ /**
128
+ * The agent's `category: "model"` ACP config option, reduced to what the UI
129
+ * needs to render a picker: which value is selected, and what it can become.
130
+ *
131
+ * Deliberately not the raw ACP `SessionConfigOption` — that type carries a
132
+ * `boolean` variant and grouped select options this domain has no use for,
133
+ * and re-exporting it here would leak an SDK shape across the contract
134
+ * boundary for a feature that only needs "id, current value, flat choices".
135
+ */
136
+ const AgentSessionModelOptionVOSchema = z.object({
137
+ /** The ACP `configId` to send back on `session/set_config_option`. */
138
+ id: z.string(),
139
+ name: z.string(),
140
+ currentValue: z.string(),
141
+ options: z.array(z.object({
142
+ value: z.string(),
143
+ name: z.string()
144
+ }))
145
+ });
127
146
  const AgentSessionVOSchema = z.object({
128
147
  /** Busabase's own id for the session; not the agent's ACP sessionId. */
129
148
  id: z.string(),
@@ -135,7 +154,15 @@ const AgentSessionVOSchema = z.object({
135
154
  createdAt: z.string(),
136
155
  lastActivityAt: z.string(),
137
156
  /** Set when status is "failed"; surfaced verbatim to the user. */
138
- error: z.string().nullable().default(null)
157
+ error: z.string().nullable().default(null),
158
+ /**
159
+ * Present only while the agent's `session/new` (or a later
160
+ * `config_option_update`) has advertised a `category: "model"` select.
161
+ * `null` for agents that offer no model choice, and for every session
162
+ * loaded from history — a finished process cannot take a config change,
163
+ * so there is nothing to render a picker for.
164
+ */
165
+ modelOption: AgentSessionModelOptionVOSchema.nullable().default(null)
139
166
  });
140
167
  /** One connected agent backend visible in the requested workspace scope. */
141
168
  const AgentConnectionVOSchema = z.object({
@@ -221,6 +248,12 @@ const RespondToAgentPermissionInputSchema = z.object({
221
248
  requestId: z.string().min(1),
222
249
  optionId: z.string().min(1)
223
250
  });
251
+ /** Change the session's `category: "model"` config option via `session/set_config_option`. */
252
+ const SetAgentSessionConfigOptionInputSchema = z.object({
253
+ sessionId: z.string().min(1),
254
+ configId: z.string().min(1),
255
+ value: z.string().min(1)
256
+ });
224
257
  //#endregion
225
258
  //#region ../../packages/busabase-contract/src/domains/agents/contract.ts
226
259
  /**
@@ -245,13 +278,20 @@ list: oc.input(ListAgentConnectionsInputSchema).output(AgentConnectionVOSchema.a
245
278
  list: oc.output(AgentSessionVOSchema.array()),
246
279
  create: oc.input(CreateAgentSessionInputSchema).output(AgentSessionVOSchema),
247
280
  /**
248
- * Send a message. Returns as soon as the turn is accepted the reply arrives
249
- * on `subscribe`, not here, so a slow agent never blocks the caller.
281
+ * Send a message. A terminal session returns `accepted: false` instead of
282
+ * relying on error text; `promptRecorded` tells the caller whether automatic
283
+ * continuation can resend without duplicating a server echo.
250
284
  */
251
- prompt: oc.input(PromptAgentSessionInputSchema).output(z.object({
252
- accepted: z.boolean(),
285
+ prompt: oc.input(PromptAgentSessionInputSchema).output(z.discriminatedUnion("accepted", [z.object({
286
+ accepted: z.literal(true),
253
287
  sessionId: z.string()
254
- })),
288
+ }), z.object({
289
+ accepted: z.literal(false),
290
+ sessionId: z.string(),
291
+ status: AgentSessionStatusSchema.extract(["ended", "failed"]),
292
+ promptRecorded: z.boolean(),
293
+ message: z.string()
294
+ })])),
255
295
  cancel: oc.input(AgentSessionIdInputSchema).output(z.object({ ok: z.boolean() })),
256
296
  close: oc.input(AgentSessionIdInputSchema).output(z.object({ ok: z.boolean() })),
257
297
  /**
@@ -261,6 +301,12 @@ list: oc.input(ListAgentConnectionsInputSchema).output(AgentConnectionVOSchema.a
261
301
  */
262
302
  respondToPermission: oc.input(RespondToAgentPermissionInputSchema).output(z.object({ ok: z.boolean() })),
263
303
  /**
304
+ * Change the session's advertised model via ACP `session/set_config_option`.
305
+ * `value` is validated against the session's currently advertised options
306
+ * server-side — this is not a passthrough to the agent.
307
+ */
308
+ setConfigOption: oc.input(SetAgentSessionConfigOptionInputSchema).output(AgentSessionVOSchema),
309
+ /**
264
310
  * Live event stream for one session. Replays buffered events from `afterSeq`
265
311
  * first so a client that reconnects mid-turn does not lose the tokens it
266
312
  * missed, then follows live.
@@ -560,7 +606,26 @@ const updateFieldChangeRequestInputSchema = z.object({
560
606
  patch: z.object({
561
607
  name: fieldNameSchema.optional(),
562
608
  required: z.boolean().optional(),
563
- options: fieldOptionsSchema.optional()
609
+ options: fieldOptionsSchema.optional(),
610
+ /**
611
+ * Not a patch key — rejected on purpose, and the only key here that is.
612
+ *
613
+ * `update` cannot change a field's type; `convert` does, after
614
+ * `previewFieldConversion` has shown what happens to the stored values.
615
+ * But `patch` is a plain (non-strict) object, so `{ type: "markdown" }`
616
+ * used to be stripped silently: the request validated, the change request
617
+ * merged, `ok: true` came back, and the field was still whatever it was.
618
+ * A caller reaching for the obvious-but-wrong shape got a successful
619
+ * no-op, which reads exactly like a successful conversion.
620
+ *
621
+ * Blanket `.strict()` is not the fix here — see `contract/auto-merge.ts`
622
+ * on why these schemas stay open: the SDK ships on its own cadence
623
+ * against self-hosted servers, so a newer client sending a newer optional
624
+ * key is normal traffic, and strictness would 400 all of them to catch
625
+ * this one. Naming the single key that will never be legitimate keeps
626
+ * that forward compatibility intact.
627
+ */
628
+ type: z.never({ message: "A field type cannot be changed with `update`. Use operation: \"convert\" with `newType`, and call `previewFieldConversion` first to see how many stored values survive it." }).optional()
564
629
  }),
565
630
  message: z.string().optional(),
566
631
  submittedBy: z.string().optional().default("local-editor"),
@@ -727,6 +792,7 @@ const baseNodeType = {
727
792
  capabilities: {
728
793
  hasDetail: true,
729
794
  creatable: true,
795
+ commonlyCreated: true,
730
796
  publicAccess: "detail"
731
797
  },
732
798
  operations: [
@@ -831,6 +897,7 @@ const docNodeType = {
831
897
  capabilities: {
832
898
  hasDetail: true,
833
899
  creatable: true,
900
+ commonlyCreated: true,
834
901
  publicAccess: "detail"
835
902
  },
836
903
  operations: [{
@@ -862,6 +929,7 @@ const fileNodeType = {
862
929
  capabilities: {
863
930
  hasDetail: true,
864
931
  creatable: true,
932
+ commonlyCreated: true,
865
933
  publicAccess: "detail"
866
934
  },
867
935
  operations: []
@@ -877,6 +945,7 @@ const folderNodeType = {
877
945
  capabilities: {
878
946
  container: true,
879
947
  creatable: true,
948
+ commonlyCreated: true,
880
949
  hasDetail: true,
881
950
  publicAccess: "detail"
882
951
  },
@@ -887,9 +956,10 @@ const folderNodeType = {
887
956
  /**
888
957
  * Form node: an agent-authored, sandboxed web page bound to a Base via an
889
958
  * explicit field-binding contract. A submission does NOT write a record
890
- * directly — it produces a review-first record-create ChangeRequest on the
891
- * target Base (so the submitting act reuses the base's `record_create` op, not a
892
- * form-specific one). The form's own config (bindings/page/share) is owner-
959
+ * directly — it produces a record-create ChangeRequest on the target Base (so
960
+ * the submitting act reuses the base's `record_create` op, not a form-specific
961
+ * one), which then merges immediately or waits for review according to the
962
+ * submitter's permission on that Base. The form's own config (bindings/page/share) is owner-
893
963
  * managed and edited directly, so this node contributes no CR operations of its
894
964
  * own for now.
895
965
  */
@@ -897,10 +967,23 @@ const formNodeType = {
897
967
  type: "form",
898
968
  label: "Form",
899
969
  icon: "form",
970
+ /**
971
+ * `hidden` until a Form can be created from a create surface at all.
972
+ *
973
+ * `busabase_forms.target_base_id` is NOT NULL and `form` registers no
974
+ * `node_create` materializer, so a Form built through the generic New-item
975
+ * flow (which only collects name/slug/description) is a node row with no form
976
+ * config behind it — it opens to a dead end, every time, for everyone. The
977
+ * type stays fully `creatable` so `forms.create` (which does take a target
978
+ * Base) and the REST/MCP surface are untouched; it just no longer offers an
979
+ * entry point that cannot succeed. Drop this once the New-item flow asks for
980
+ * the target Base and a materializer writes the config row.
981
+ */
900
982
  capabilities: {
901
983
  hasDetail: true,
902
984
  creatable: true,
903
- publicAccess: "submit"
985
+ publicAccess: "submit",
986
+ hidden: true
904
987
  },
905
988
  operations: []
906
989
  };
@@ -1135,12 +1218,20 @@ const customPromptBodySchema = iStringSchema.refine((value) => iStringLocaleValu
1135
1218
  * One custom scenario prompt. `body`'s `{target}` placeholder is substituted at
1136
1219
  * render time with the same target string `PromptDef.body(target)` receives
1137
1220
  * today (see `node-agent-prompts.ts`) — this schema does not interpolate it.
1221
+ *
1222
+ * The placeholder is OPTIONAL and chooses placement only: a `body` that never
1223
+ * mentions `{target}` gets the target line prepended as its first paragraph, so
1224
+ * a custom prompt can never reach an agent without naming the node it acts on.
1225
+ * That is why the schema does not require it — forgetting it is not an error to
1226
+ * reject, it is a default to supply.
1138
1227
  */
1139
1228
  const customPromptDefSchema = z.object({
1140
1229
  /** Stable id, unique within this node's custom list. */
1141
1230
  key: z.string().trim().min(1, { message: "key must not be empty" }),
1142
1231
  /** Defaults to `change` (same default the curated prompts use) so a prompt
1143
- * cannot silently bypass the approval-first policy by omission. */
1232
+ * cannot silently opt out of the change-request path by omission — whether that
1233
+ * path then merges immediately or waits for review is the permission layer's
1234
+ * call, not the prompt's. */
1144
1235
  intent: customPromptIntentSchema.optional(),
1145
1236
  /** Short title shown in the dialog's left list. */
1146
1237
  label: customPromptLabelSchema,
@@ -1327,7 +1418,14 @@ const nodeSearchResultSchema = z.object({
1327
1418
  name: z.string(),
1328
1419
  slug: z.string(),
1329
1420
  path: z.string(),
1330
- updatedAt: z.string()
1421
+ updatedAt: z.string(),
1422
+ /**
1423
+ * The node's own custom avatar, same shape as `NodeVO.icon`. Optional so an
1424
+ * older server that predates this field is still a valid response — a
1425
+ * caller that doesn't know it falls back to the type icon exactly as it
1426
+ * always has.
1427
+ */
1428
+ icon: NodeIconSchema.nullable().optional()
1331
1429
  });
1332
1430
  const userRefSchema = z.object({
1333
1431
  id: z.string(),
@@ -1819,6 +1917,13 @@ const SEARCH_SOURCES = [
1819
1917
  "names",
1820
1918
  "nodes"
1821
1919
  ];
1920
+ const SearchSortSchema = z.enum([
1921
+ "relevance",
1922
+ "updated_desc",
1923
+ "updated_asc",
1924
+ "created_desc",
1925
+ "created_asc"
1926
+ ]);
1822
1927
  const searchInputSchema = z.object({
1823
1928
  query: z.string().default("").describe("Full-text query. An empty string matches nothing."),
1824
1929
  limit: z.coerce.number().int().min(1).max(100).optional().default(20).describe("Results per page. Capped at 100; note the default is 20, not 50."),
@@ -1832,7 +1937,26 @@ const searchInputSchema = z.object({
1832
1937
  * (`?sources=records&sources=files`) becomes an array. Accept both shapes
1833
1938
  * and normalize to an array.
1834
1939
  */
1835
- sources: z.union([z.array(z.enum(SEARCH_SOURCES)), z.enum(SEARCH_SOURCES)]).transform((value) => Array.isArray(value) ? value : [value]).optional().describe("Restrict which content is searched. Omitting it searches ALL sources. Repeat the parameter to pass several (`?sources=records&sources=files`); a single occurrence is accepted as a bare value.")
1940
+ sources: z.union([z.array(z.enum(SEARCH_SOURCES)), z.enum(SEARCH_SOURCES)]).transform((value) => Array.isArray(value) ? value : [value]).optional().describe("Restrict which content is searched. Omitting it searches ALL sources. Repeat the parameter to pass several (`?sources=records&sources=files`); a single occurrence is accepted as a bare value."),
1941
+ sort: SearchSortSchema.optional().default("relevance").describe("Result order. `relevance` (default) keeps each source's own ranking — for records that is the full-text rank, for everything else most-recently-updated first. The four explicit orders sort every source by the same column so a mixed result set is comparable."),
1942
+ /**
1943
+ * Both bounds are inclusive ISO 8601 instants, and both are optional — one
1944
+ * on its own is an open-ended range, which is what "since last Monday" and
1945
+ * "before the migration" each need.
1946
+ *
1947
+ * Filters the same timestamp `sort` orders by, so "edited this week, newest
1948
+ * first" reads as one coherent question rather than two unrelated knobs.
1949
+ */
1950
+ updatedAfter: z.string().datetime({ offset: true }).optional().describe("Inclusive lower bound, ISO 8601. A UTC `Z` or an explicit offset; not a bare local time."),
1951
+ updatedBefore: z.string().datetime({ offset: true }).optional().describe("Inclusive upper bound, ISO 8601. A UTC `Z` or an explicit offset; not a bare local time."),
1952
+ /**
1953
+ * Restrict to a subtree: this node and everything beneath it.
1954
+ *
1955
+ * Resolved by walking the tree in application code (`collectSubtreeIds`),
1956
+ * the same way `isDescendantOf` and permanent-delete already do — workspace
1957
+ * trees are shallow and this repo has no recursive-CTE precedent.
1958
+ */
1959
+ inNodeId: z.string().optional().describe("Limit to this node and its descendants.")
1836
1960
  });
1837
1961
  const authSpaceSchema = z.object({
1838
1962
  id: z.string(),
@@ -1998,17 +2122,84 @@ const fileTreeNodeTypeSchema = z.enum([
1998
2122
  "drive",
1999
2123
  "airapp"
2000
2124
  ]);
2125
+ const filePreviewProviderSchema = z.enum(["builtin", "previewfile"]);
2126
+ const filePreviewCredentialSourceSchema = z.enum([
2127
+ "environment",
2128
+ "vault",
2129
+ "none"
2130
+ ]);
2131
+ const filePreviewConfigurationStatusSchema = z.enum([
2132
+ "ready",
2133
+ "not_configured",
2134
+ "invalid_configuration"
2135
+ ]);
2136
+ const filePreviewUnavailableReasonSchema = z.enum([
2137
+ "not_configured",
2138
+ "invalid_configuration",
2139
+ "file_too_large",
2140
+ "unsupported",
2141
+ "authentication_failed",
2142
+ "rate_limited",
2143
+ "timeout",
2144
+ "service_unavailable",
2145
+ "invalid_response"
2146
+ ]);
2147
+ const filePreviewConfigSchema = z.object({
2148
+ provider: filePreviewProviderSchema,
2149
+ status: filePreviewConfigurationStatusSchema,
2150
+ credentialSource: filePreviewCredentialSourceSchema,
2151
+ credentialConfigured: z.boolean(),
2152
+ maxFileSizeBytes: z.number().int().positive(),
2153
+ sessionTtlMinutes: z.number().int().positive(),
2154
+ vaultEncryptionConfigured: z.boolean().nullable()
2155
+ });
2156
+ const filePreviewSchema = z.discriminatedUnion("state", [
2157
+ z.object({
2158
+ state: z.literal("builtin"),
2159
+ provider: z.literal("builtin")
2160
+ }),
2161
+ z.object({
2162
+ state: z.literal("ready"),
2163
+ provider: z.literal("previewfile"),
2164
+ previewUrl: z.string().url(),
2165
+ expiresAt: z.string().datetime()
2166
+ }),
2167
+ z.object({
2168
+ state: z.literal("unavailable"),
2169
+ provider: z.literal("previewfile"),
2170
+ reason: filePreviewUnavailableReasonSchema,
2171
+ retryable: z.boolean()
2172
+ })
2173
+ ]);
2001
2174
  const fileTreeRefSchema = z.object({
2002
2175
  nodeId: z.string().describe("A node id OR a slug. A slug is only unique WITHIN a type, so pass `type` alongside one; a node id needs no hint."),
2003
2176
  type: fileTreeNodeTypeSchema.optional().describe("Disambiguates a slug. Unnecessary — and ignored — when `nodeId` is an id.")
2004
2177
  });
2005
2178
  const fileTreeContract = {
2179
+ previewConfig: oc.route({
2180
+ method: "GET",
2181
+ path: "/file-trees/preview-config",
2182
+ tags: ["File Trees"],
2183
+ summary: "Get Drive file preview configuration",
2184
+ successDescription: "Resolved preview provider state without exposing the configured API key."
2185
+ }).output(filePreviewConfigSchema),
2186
+ preparePreview: oc.route({
2187
+ method: "POST",
2188
+ path: "/file-trees/{nodeId}/preview",
2189
+ tags: ["File Trees"],
2190
+ summary: "Prepare a Drive file preview",
2191
+ successDescription: "Returns the built-in provider, a short-lived PreviewFile URL, or a recoverable provider failure."
2192
+ }).input(z.object({
2193
+ nodeId: z.string().min(1),
2194
+ filePath: z.string().min(1),
2195
+ type: z.literal("drive")
2196
+ })).output(filePreviewSchema),
2006
2197
  create: oc.route({
2007
2198
  method: "POST",
2008
2199
  path: "/file-trees",
2009
2200
  tags: ["File Trees"],
2010
2201
  summary: "Create file-tree node",
2011
- successDescription: "Review-first by default: a pending ChangeRequest proposing the node (`materialized: false`). Returns the materialized node instead (`materialized: true`) when `autoMerge: true` is passed."
2202
+ successDescription: "Merged in the same call when the actor has write access on the target node — the materialized node comes back (`materialized: true`). Review-first when the actor lacks write access or passes `autoMerge: false`: a pending ChangeRequest proposing the node (`materialized: false`)."
2012
2203
  }).input(createFileTreeInputSchema.extend({ type: fileTreeNodeTypeSchema })).output(z.union([fileTreeNodeSchema.extend({ materialized: z.literal(true) }), changeRequestSchema.extend({ materialized: z.literal(false) })])),
2013
2204
  listFiles: oc.route({
2014
2205
  method: "GET",
@@ -3029,14 +3220,14 @@ const baseContract = {
3029
3220
  path: "/bases",
3030
3221
  tags: ["Bases"],
3031
3222
  summary: "Create Base",
3032
- successDescription: "Review-first by default: a pending ChangeRequest proposing the Base (`materialized: false`). Returns the materialized Base instead (`materialized: true`) when `autoMerge: true` is passed."
3223
+ successDescription: "Merged in the same call when the actor has write access on the parent node — the materialized Base comes back (`materialized: true`). Review-first when the actor lacks write access or passes `autoMerge: false`: a pending ChangeRequest proposing the Base (`materialized: false`)."
3033
3224
  }).input(createBaseInputSchema).output(z.union([baseSchema.extend({ materialized: z.literal(true) }), changeRequestSchema.extend({ materialized: z.literal(false) })])),
3034
3225
  createChangeRequest: oc.route({
3035
3226
  method: "POST",
3036
3227
  path: "/bases/{baseId}/change-requests",
3037
3228
  tags: ["Bases", "Change Requests"],
3038
3229
  summary: "Create Change Request in Base",
3039
- successDescription: "Review-first by default: a pending ChangeRequest proposing the record (`materialized: false`). Returns the materialized record instead (`materialized: true`) when `autoMerge: true` is passed."
3230
+ successDescription: "Merged in the same call when the actor has write access on the Base's node — the materialized record comes back (`materialized: true`). Review-first when the actor lacks write access or passes `autoMerge: false`: a pending ChangeRequest proposing the record (`materialized: false`)."
3040
3231
  }).input(createChangeRequestInputSchema.extend({ baseId: z.string() })).output(z.union([recordSchema.extend({ materialized: z.literal(true) }), changeRequestSchema.extend({ materialized: z.literal(false) })])),
3041
3232
  createBulkChangeRequest: oc.route({
3042
3233
  method: "POST",
@@ -3194,7 +3385,7 @@ const docContract = { create: oc.route({
3194
3385
  path: "/docs",
3195
3386
  tags: ["Docs"],
3196
3387
  summary: "Create Doc node",
3197
- successDescription: "Review-first by default: a pending ChangeRequest proposing the Doc (`materialized: false`). Returns the materialized Doc node instead (`materialized: true`) when `autoMerge: true` is passed."
3388
+ successDescription: "Merged in the same call when the actor has write access on the parent node — the materialized Doc node comes back (`materialized: true`). Review-first when the actor lacks write access or passes `autoMerge: false`: a pending ChangeRequest proposing the Doc (`materialized: false`)."
3198
3389
  }).input(createDocInputSchema).output(z.union([docSchema.extend({ materialized: z.literal(true) }), changeRequestSchema.extend({ materialized: z.literal(false) })])) };
3199
3390
  //#endregion
3200
3391
  //#region ../../packages/busabase-contract/src/domains/doc/types.ts
@@ -3456,7 +3647,7 @@ const fileContract = { create: oc.route({
3456
3647
  path: "/files",
3457
3648
  tags: ["Files"],
3458
3649
  summary: "Create File node",
3459
- successDescription: "Review-first by default: a pending ChangeRequest proposing the File node (`materialized: false`). Returns the materialized File node instead (`materialized: true`) when `autoMerge: true` is passed."
3650
+ successDescription: "Merged in the same call when the actor has write access on the parent node — the materialized File node comes back (`materialized: true`). Review-first when the actor lacks write access or passes `autoMerge: false`: a pending ChangeRequest proposing the File node (`materialized: false`)."
3460
3651
  }).input(createFileNodeInputSchema).output(z.union([FileNodeVOSchema.extend({ materialized: z.literal(true) }), changeRequestSchema.extend({ materialized: z.literal(false) })])) };
3461
3652
  //#endregion
3462
3653
  //#region ../../packages/busabase-contract/src/domains/form/types.ts
@@ -3565,10 +3756,22 @@ const SubmitFormInputSchema = z.object({
3565
3756
  values: z.record(z.string(), z.unknown()),
3566
3757
  captchaToken: z.string().optional()
3567
3758
  });
3568
- /** What the submit endpoint returns — the pending ChangeRequest id, never data. */
3759
+ /**
3760
+ * What the submit endpoint returns — a ChangeRequest id and what happened to it,
3761
+ * never the record data.
3762
+ *
3763
+ * `status` is permission-aware, like every other write: `merged` when the
3764
+ * submitter holds `write` on the target Base and the submission landed straight
3765
+ * away, `pending_review` when it is waiting for a human. An ANONYMOUS visitor can
3766
+ * never reach `merged`: permission is resolved against the target Base, which a
3767
+ * form does not share publicly, and a public-link request is capped at `read`
3768
+ * even where it is shared. So a public form still always waits.
3769
+ */
3569
3770
  const FormSubmitResultSchema = z.object({
3570
3771
  changeRequestId: z.string(),
3571
- status: z.literal("pending_review")
3772
+ status: z.enum(["pending_review", "merged"]),
3773
+ /** The created record's id, present only when `status` is `merged`. */
3774
+ recordId: z.string().optional()
3572
3775
  });
3573
3776
  //#endregion
3574
3777
  //#region ../../packages/busabase-contract/src/domains/form/contract.ts
@@ -3606,7 +3809,7 @@ const formContract = {
3606
3809
  path: "/forms/{nodeId}/submit",
3607
3810
  tags: ["Forms"],
3608
3811
  summary: "Submit a filled-in form",
3609
- successDescription: "Creates a review-first record-create ChangeRequest on the target Base a form submission always waits for a human, whoever submitted it."
3812
+ successDescription: "Creates a record-create ChangeRequest on the target Base. Merged in the same call when the submitter holds write access on that Base (`status: \"merged\"`); otherwise it waits for a reviewer (`status: \"pending_review\"`). A visitor arriving through the form's public link is capped at read and therefore always waits."
3610
3813
  }).input(SubmitFormInputSchema.extend({ nodeId: z.string() })).output(FormSubmitResultSchema)
3611
3814
  };
3612
3815
  //#endregion
@@ -3757,9 +3960,10 @@ const InstallPlanVOSchema = z.object({
3757
3960
  warnings: z.array(z.string()).default([]),
3758
3961
  /**
3759
3962
  * True when a record carries a relation VALUE. A relation stores the ids of the
3760
- * records it points at, and those exist only once the records are merged — so a
3761
- * review-first install would land every relation empty. `autoMerge` is required
3762
- * in that case, and the UI must say why.
3963
+ * records it points at, and those exist only once the records are merged — so an
3964
+ * install left for review would land every relation empty. Such a package cannot
3965
+ * be installed by a caller whose content would queue (no write access, or an
3966
+ * explicit `autoMerge: false`), and the UI must say why.
3763
3967
  */
3764
3968
  requiresAutoMerge: z.boolean(),
3765
3969
  /**
@@ -3769,10 +3973,13 @@ const InstallPlanVOSchema = z.object({
3769
3973
  *
3770
3974
  * It is therefore an answer to "what happens if I install like this", not a
3771
3975
  * property of the package — a package whose records carry relation values
3772
- * reports `applicable: false` when planned WITHOUT `autoMerge` and true WITH
3773
- * it. A client that offers an auto-merge toggle must re-plan when it changes
3774
- * (the same way it re-plans when `rename` or `intoFolder` change), rather than
3775
- * treating one plan's `applicable` as final.
3976
+ * reports `applicable: false` when this caller's content would QUEUE and true
3977
+ * when it would merge. Note the plan resolves that the same permission-aware
3978
+ * way the install itself does, so omitting `autoMerge` reports what the caller
3979
+ * would actually get rather than assuming review. A client that offers an
3980
+ * auto-merge toggle must re-plan when it changes (the same way it re-plans when
3981
+ * `rename` or `intoFolder` change), rather than treating one plan's
3982
+ * `applicable` as final.
3776
3983
  *
3777
3984
  * There is deliberately no `blockedReason` string here: the reason is already
3778
3985
  * carried structurally by `collisions[]` (with `renamedTo`) and
@@ -3803,6 +4010,28 @@ const InstallResultVOSchema = z.object({
3803
4010
  pendingChangeRequests: z.number().int().min(0),
3804
4011
  warnings: z.array(z.string()).default([])
3805
4012
  });
4013
+ /**
4014
+ * One event from a streaming install.
4015
+ *
4016
+ * A whole install in a single response is what makes the plain `fromGithub`
4017
+ * route time out at a gateway: the work is minutes of GitHub fetch, node
4018
+ * creation, file upload and record writes, and until it finishes the connection
4019
+ * carries no bytes. A proxy in front of the app sees an idle socket and closes
4020
+ * it — the install itself keeps running server-side, so the user is told it
4021
+ * failed while it actually succeeded, which is the worst of both.
4022
+ *
4023
+ * `applyInstall` already reports its progress (`onProgress`); the non-streaming
4024
+ * route simply discarded it. Streaming those same messages keeps bytes moving
4025
+ * AND gives the user something truthful to look at during a long install.
4026
+ */
4027
+ const InstallEventVOSchema = z.discriminatedUnion("kind", [z.object({
4028
+ kind: z.literal("progress"),
4029
+ /** Human-readable, already localized by the caller's own copy — display as-is. */
4030
+ message: z.string()
4031
+ }), z.object({
4032
+ kind: z.literal("done"),
4033
+ result: InstallResultVOSchema
4034
+ })]);
3806
4035
  z.object({
3807
4036
  /** Which of the five passes stopped, e.g. `Pass 4/5 (sample records)`. */
3808
4037
  phase: z.string(),
@@ -3833,7 +4062,7 @@ const InstallFromGithubDTOSchema = z.object({
3833
4062
  repoUrl: repoUrlField,
3834
4063
  intoFolder: intoFolderField,
3835
4064
  rename: renameField,
3836
- autoMerge: z.boolean().optional().describe("Merge the content change requests on the spot instead of leaving them for review. This trusts the package author: a package can carry skills and AirApps, i.e. code this space's agents will execute.")
4065
+ autoMerge: z.boolean().optional().describe("Whether the package's content change requests merge on the spot. Omitted defaults to merging immediately when the caller holds write access (installing is already a space owner/admin operation, so normally they do), otherwise leaving them for review; pass explicit false to force review even with write access. Either way, installing trusts the package author: a package can carry skills and AirApps, i.e. code this space's agents will execute.")
3837
4066
  });
3838
4067
  //#endregion
3839
4068
  //#region ../../packages/busabase-contract/src/domains/install/contract.ts
@@ -3865,7 +4094,18 @@ const installContract = {
3865
4094
  tags: ["Install"],
3866
4095
  summary: "Install a package from a GitHub repo",
3867
4096
  successDescription: "Created counts plus the number of change requests left for review. Structure (folders, Bases, fields, views) is created immediately — a pending Base has no id to attach a view or record to; content (records, docs, skills, AirApps) lands as change requests unless `autoMerge` is set."
3868
- }).input(InstallFromGithubDTOSchema).output(InstallResultVOSchema)
4097
+ }).input(InstallFromGithubDTOSchema).output(InstallResultVOSchema),
4098
+ /**
4099
+ * The same install, streamed.
4100
+ *
4101
+ * Kept alongside `fromGithub` rather than replacing it: that route is in the
4102
+ * public OpenAPI surface and a plain request/response is the right shape for
4103
+ * a script. This one exists for a human waiting at a dashboard, where the
4104
+ * install is long enough that a silent connection gets closed by whatever
4105
+ * proxy sits in front of the app — and long enough that a progress line is
4106
+ * worth showing regardless.
4107
+ */
4108
+ fromGithubStream: oc.input(InstallFromGithubDTOSchema).output(eventIterator(InstallEventVOSchema))
3869
4109
  };
3870
4110
  //#endregion
3871
4111
  //#region ../../packages/busabase-contract/src/domains/templates/types.ts
@@ -3923,6 +4163,11 @@ const TemplateCardVOSchema = z.object({
3923
4163
  * accidentally point at a different ref than the one it installs.
3924
4164
  */
3925
4165
  screenshots: z.array(z.string()).default([]),
4166
+ /**
4167
+ * Absolute URL of the demo clip, or absent. Resolved against a different host
4168
+ * than the screenshots — see `videoUrl` in the catalog logic.
4169
+ */
4170
+ video: z.string().optional(),
3926
4171
  agentPrompts: z.array(z.string()).default([]),
3927
4172
  version: z.string().optional(),
3928
4173
  author: z.string().optional(),
@@ -4012,6 +4257,7 @@ const VaultItemInputSchema = z.object({
4012
4257
  })
4013
4258
  });
4014
4259
  const UpdateVaultSettingsInputSchema = z.object({ items: z.array(VaultItemInputSchema).max(200) });
4260
+ const UpdatePreviewFileCredentialInputSchema = z.object({ apiKey: VaultItemValueSchema.trim().min(1).nullable() });
4015
4261
  const VaultItemVOSchema = VaultItemInputSchema.extend({
4016
4262
  id: z.string(),
4017
4263
  scopeId: z.string().nullable(),
@@ -4043,6 +4289,13 @@ const vaultContract = {
4043
4289
  summary: "Replace local Vault settings",
4044
4290
  successDescription: "Updated local Vault secrets and variables."
4045
4291
  }).input(UpdateVaultSettingsInputSchema).output(VaultSettingsVOSchema),
4292
+ updatePreviewFileCredential: oc.route({
4293
+ method: "PUT",
4294
+ path: "/vault/previewfile",
4295
+ tags: ["Vault"],
4296
+ summary: "Set or remove the local PreviewFile credential",
4297
+ successDescription: "The credential was updated without returning its value."
4298
+ }).input(UpdatePreviewFileCredentialInputSchema).output(VaultSuccessSchema),
4046
4299
  clear: oc.route({
4047
4300
  method: "DELETE",
4048
4301
  path: "/vault",
@@ -59,6 +59,19 @@ z.object({
59
59
  /** Card/detail screenshots, package-relative (`assets/screenshots/overview.webp`). */
60
60
  screenshots: z.array(z.string()).default([]),
61
61
  /**
62
+ * Optional demo clip, package-relative (`assets/recordings/busa-crm.mp4`).
63
+ *
64
+ * No companion poster field on purpose: the detail page uses
65
+ * `screenshots[0]`, which the catalog already requires to be the cover. One
66
+ * declared path instead of two that can disagree with each other.
67
+ *
68
+ * Note for anyone adding a sibling field here: this is a plain `z.object`, so
69
+ * an unrecognized key in `busabase.json` is silently stripped rather than
70
+ * rejected. A template cannot declare a field ahead of the schema landing —
71
+ * it just vanishes, with no error from `busabase-cli check`.
72
+ */
73
+ video: z.string().optional(),
74
+ /**
62
75
  * Ready-made prompts shown after install ("Ask agent" prefills the first).
63
76
  *
64
77
  * They are the difference between a folder of tables and something a user can
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "busabase-sdk",
3
- "version": "0.52.1",
3
+ "version": "0.54.0",
4
4
  "description": "Typed TypeScript/JavaScript SDK for the Busabase OpenAPI REST API. Talks to a local or remote `busabase server` (or Busabase Cloud).",
5
5
  "license": "MIT",
6
6
  "homepage": "https://github.com/busabase/busabase/tree/main/apps/busabase-sdk",
@@ -67,9 +67,9 @@
67
67
  "tsx": "^4.20.5",
68
68
  "typescript": "^7.0.2",
69
69
  "vitest": "^4.1.11",
70
- "busabase-contract": "0.52.1",
71
70
  "open-domains": "0.0.2",
72
- "openlib": "0.1.1"
71
+ "openlib": "0.1.1",
72
+ "busabase-contract": "0.54.0"
73
73
  },
74
74
  "engines": {
75
75
  "node": ">=24.18.0"