apps 0.0.1-beta.37 → 0.0.1-beta.39

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/framework-reference.json +1 -1
  2. package/js/{chunk-GHDZEEPO.js → chunk-27TDXHRX.js} +395 -82
  3. package/js/{chunk-KOOJUMLR.js → chunk-3MMG2H6M.js} +2 -2
  4. package/js/{chunk-LQBR7AI7.js → chunk-3V575NIW.js} +217 -19
  5. package/js/{chunk-U74JVKTQ.js → chunk-5NIEOXRZ.js} +7 -7
  6. package/js/{chunk-BWUHMKHQ.js → chunk-5TJA7364.js} +2 -2
  7. package/js/{chunk-7KEKFBV7.js → chunk-7CSHVXVR.js} +3 -3
  8. package/js/{chunk-3LRFKLUX.js → chunk-BO4US6DZ.js} +9 -49
  9. package/js/{chunk-A4O72Z3A.js → chunk-DKUEYTYD.js} +167 -30
  10. package/js/{chunk-ENJCQWVF.js → chunk-E4BWL7KQ.js} +3 -3
  11. package/js/{chunk-CAOHGMNW.js → chunk-F3TBO2U3.js} +7 -94
  12. package/js/{chunk-POOIHRPF.js → chunk-GHXRRFZA.js} +11 -34
  13. package/js/{chunk-RSNZDT4L.js → chunk-JTMDWPCH.js} +2 -2
  14. package/js/{chunk-YW333DH3.js → chunk-KANDIVIH.js} +4 -4
  15. package/js/{chunk-2FWXCU5I.js → chunk-KAQD3DTI.js} +1 -1
  16. package/js/{chunk-QQKHJ72Y.js → chunk-LVWX4UWK.js} +9 -9
  17. package/js/{chunk-KEZ4YZSP.js → chunk-MLHNVYHT.js} +2 -2
  18. package/js/{chunk-IJQ5LE74.js → chunk-MXDKXAY4.js} +363 -401
  19. package/js/{chunk-BI6R577B.js → chunk-P7MGDPFD.js} +2 -2
  20. package/js/{chunk-24EIFQYZ.js → chunk-PCV73KVK.js} +35 -10
  21. package/js/{chunk-JU7F4PRK.js → chunk-RIXVGVA6.js} +2 -2
  22. package/js/{chunk-D45FU4TZ.js → chunk-SDHD6J37.js} +3 -3
  23. package/js/{chunk-UCOMVHGW.js → chunk-U5PFZDVB.js} +2 -2
  24. package/js/{chunk-PHO7HXTC.js → chunk-VIU2XXA7.js} +294 -47
  25. package/js/{chunk-TADLIUEU.js → chunk-VJQXXOQ3.js} +2 -2
  26. package/js/{chunk-NHCFIWN5.js → chunk-W2NV54JD.js} +1 -1
  27. package/js/{chunk-BI45HZWM.js → chunk-YQ35E7KS.js} +1 -1
  28. package/js/{chunk-WDBUOIWV.js → chunk-ZUKBIHMK.js} +3 -3
  29. package/js/client.js +13 -17
  30. package/js/contracts.js +16 -21
  31. package/js/effect.js +19 -24
  32. package/js/graphql.js +21 -26
  33. package/js/host.js +19 -24
  34. package/js/index.js +13 -26
  35. package/js/mcp/effect.js +21 -26
  36. package/js/mcp/stdio.js +21 -26
  37. package/js/mcp.js +23 -28
  38. package/js/openapi.js +17 -21
  39. package/js/react.js +2 -3
  40. package/js/skills/effect.js +6 -7
  41. package/js/skills.js +15 -17
  42. package/js/storage/facet.js +1 -214
  43. package/js/ui/auth/contracts.js +2 -2
  44. package/js/ui/auth.js +5 -5
  45. package/js/ui/contracts.js +5 -6
  46. package/js/ui/serving.js +10 -11
  47. package/package.json +1 -2
  48. package/runtime.json +1 -1
  49. package/types/apps/src/contracts/app.d.ts +0 -2
  50. package/types/apps/src/contracts/context.d.ts +12 -11
  51. package/types/apps/src/contracts/host.d.ts +10 -8
  52. package/types/apps/src/contracts/live.d.ts +2 -0
  53. package/types/apps/src/contracts/operations.d.ts +3 -3
  54. package/types/apps/src/contracts/protocol-version.d.ts +2 -2
  55. package/types/apps/src/contracts/protocols/10.d.ts +1450 -0
  56. package/types/apps/src/contracts/protocols/4.d.ts +3 -3
  57. package/types/apps/src/contracts/protocols/5.d.ts +1 -1
  58. package/types/apps/src/contracts/protocols/6.d.ts +1 -1
  59. package/types/apps/src/contracts/protocols/7.d.ts +1 -1
  60. package/types/apps/src/contracts/sql.d.ts +58 -0
  61. package/types/apps/src/facet.d.ts +4 -5
  62. package/types/apps/src/implementation/openapi-compile.d.ts +2 -2
  63. package/types/apps/src/implementation/schema.d.ts +1 -10
  64. package/types/apps/src/implementation/sql.d.ts +66 -0
  65. package/types/apps/src/index.d.ts +1 -2
  66. package/types/telemetry/src/transport.d.ts +8 -1
  67. package/js/chunk-73AV3JVU.js +0 -186
  68. package/js/chunk-HDAQBRT5.js +0 -1254
  69. package/js/chunk-IU7CCCUC.js +0 -630
  70. package/js/chunk-NHUHTWAF.js +0 -0
  71. package/js/chunk-V5DWGYRK.js +0 -268
  72. package/js/chunk-Y3XN4X4Q.js +0 -19
  73. package/types/app-data/src/implementation/cursor.d.ts +0 -10
  74. package/types/app-data/src/implementation/index-key.d.ts +0 -7
  75. package/types/app-data/src/implementation/promise.d.ts +0 -68
  76. package/types/app-data/src/implementation/range.d.ts +0 -8
  77. package/types/app-data/src/implementation/schema.d.ts +0 -18
  78. package/types/app-data/src/implementation/sqlite.d.ts +0 -11
  79. package/types/app-data/src/index.d.ts +0 -4
  80. package/types/apps/src/contracts/storage.d.ts +0 -68
  81. package/types/apps/src/implementation/storage.d.ts +0 -15
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  makeCache
3
- } from "./chunk-JU7F4PRK.js";
3
+ } from "./chunk-RIXVGVA6.js";
4
4
  import {
5
5
  accountSecrets,
6
6
  boundFailureMessage,
@@ -11,35 +11,30 @@ import {
11
11
  parseSkillLoadFailed,
12
12
  readCatalog,
13
13
  routerSkills
14
- } from "./chunk-3LRFKLUX.js";
14
+ } from "./chunk-BO4US6DZ.js";
15
15
  import {
16
16
  appInvocationFetch
17
- } from "./chunk-D45FU4TZ.js";
17
+ } from "./chunk-SDHD6J37.js";
18
18
  import {
19
19
  folderSkillsEffect
20
- } from "./chunk-BWUHMKHQ.js";
20
+ } from "./chunk-5TJA7364.js";
21
21
  import {
22
22
  ResponseStatusError,
23
- authorDatabase,
24
23
  isApp,
25
- toEffectApp,
26
- unavailableStorage
27
- } from "./chunk-CAOHGMNW.js";
28
- import {
29
- parseDatabaseSchema
30
- } from "./chunk-IU7CCCUC.js";
24
+ toEffectApp
25
+ } from "./chunk-F3TBO2U3.js";
31
26
  import {
32
27
  declaredOperations,
33
28
  fromPromise,
34
29
  locate,
35
30
  nativeOperation,
36
31
  toPromise
37
- } from "./chunk-7KEKFBV7.js";
32
+ } from "./chunk-7CSHVXVR.js";
38
33
  import {
39
34
  accountProviderError,
40
35
  httpProviderError,
41
36
  parseProviderError
42
- } from "./chunk-ENJCQWVF.js";
37
+ } from "./chunk-E4BWL7KQ.js";
43
38
  import {
44
39
  AccountCheckResult,
45
40
  DeclaredProvider,
@@ -85,18 +80,20 @@ import {
85
80
  approvalElicitation,
86
81
  defaultWebhookTransportLimits,
87
82
  maxInputProblems,
83
+ migrationsDirectory,
84
+ reservedTablePrefix,
88
85
  workflowDurationMillis
89
- } from "./chunk-LQBR7AI7.js";
86
+ } from "./chunk-3V575NIW.js";
90
87
  import {
91
88
  CacheError
92
- } from "./chunk-BI6R577B.js";
89
+ } from "./chunk-P7MGDPFD.js";
93
90
  import {
94
91
  AppSkills,
95
92
  SkillFile
96
- } from "./chunk-KEZ4YZSP.js";
93
+ } from "./chunk-MLHNVYHT.js";
97
94
  import {
98
95
  captureTelemetry
99
- } from "./chunk-24EIFQYZ.js";
96
+ } from "./chunk-PCV73KVK.js";
100
97
  import {
101
98
  allowedValues,
102
99
  decoderOf,
@@ -107,11 +104,11 @@ import {
107
104
  maxAlternativeKeys,
108
105
  objectShape,
109
106
  unionShape
110
- } from "./chunk-POOIHRPF.js";
107
+ } from "./chunk-GHXRRFZA.js";
111
108
  import {
112
109
  JsonObject,
113
110
  JsonValue
114
- } from "./chunk-BI45HZWM.js";
111
+ } from "./chunk-YQ35E7KS.js";
115
112
  import {
116
113
  Base64_exports,
117
114
  Cause_exports,
@@ -124,7 +121,7 @@ import {
124
121
  SchemaIssue_exports,
125
122
  Schema_exports,
126
123
  Stream_exports
127
- } from "./chunk-IJQ5LE74.js";
124
+ } from "./chunk-MXDKXAY4.js";
128
125
 
129
126
  // src/implementation/elicitation.ts
130
127
  var prepareElicitation = (input) => Effect_exports.gen(function* () {
@@ -575,6 +572,333 @@ var dispatchWebhook = (definition, command, context, accounts) => Effect_exports
575
572
  );
576
573
  });
577
574
 
575
+ // src/implementation/sql.ts
576
+ var receipts = `${reservedTablePrefix}step_receipts`;
577
+ var migrationsTable = `${reservedTablePrefix}migrations`;
578
+ var legacyRows = `${reservedTablePrefix}legacy_rows`;
579
+ var materialize = (cursor) => {
580
+ const rows = cursor.toArray();
581
+ return {
582
+ columnNames: cursor.columnNames,
583
+ rowsRead: cursor.rowsRead,
584
+ rowsWritten: cursor.rowsWritten,
585
+ toArray: () => [...rows],
586
+ one: () => {
587
+ if (rows.length !== 1)
588
+ throw new Error(`Expected exactly one row, but the statement returned ${rows.length}.`);
589
+ return rows[0];
590
+ },
591
+ [Symbol.iterator]: () => rows[Symbol.iterator]()
592
+ };
593
+ };
594
+ var isThenable = (value) => (typeof value === "object" || typeof value === "function") && value !== null && "then" in value && typeof value.then === "function";
595
+ var missingDatabase = () => {
596
+ throw new Error(
597
+ `This app has no database. Add its first migration, such as ${migrationsDirectory}/0001_create_notes.sql with a CREATE TABLE statement, and deploy again.`
598
+ );
599
+ };
600
+ var noDatabase = { exec: missingDatabase, transaction: missingDatabase };
601
+ var settings = [
602
+ "foreign_keys",
603
+ "defer_foreign_keys",
604
+ "ignore_check_constraints",
605
+ "legacy_alter_table",
606
+ "recursive_triggers",
607
+ "reverse_unordered_selects"
608
+ ];
609
+ var readSettings = (storage) => new Map(
610
+ settings.map((name) => [name, Number(storage.sql.exec(`PRAGMA ${name}`).toArray()[0]?.[name])])
611
+ );
612
+ var changedSetting = (storage, baseline) => {
613
+ const current = readSettings(storage);
614
+ return settings.find((name) => current.get(name) !== baseline.get(name));
615
+ };
616
+ var restoreSettings = (storage, baseline) => {
617
+ const current = readSettings(storage);
618
+ for (const [name, value] of baseline)
619
+ if (current.get(name) !== value) storage.sql.exec(`PRAGMA ${name} = ${value}`);
620
+ storage.sql.exec("PRAGMA case_sensitive_like = OFF");
621
+ };
622
+ var ReadRolledBack = class extends Error {
623
+ };
624
+ var authorSql = (storage, options) => {
625
+ let closed = false;
626
+ const live = () => {
627
+ if (closed || options.signal.aborted)
628
+ throw new Error("This call has ended, so its ctx.sql no longer runs statements.");
629
+ };
630
+ let open = false;
631
+ let transactions = 0;
632
+ const baseline = readSettings(storage);
633
+ const checkSettings = () => {
634
+ const changed = changedSetting(storage, baseline);
635
+ if (changed !== void 0)
636
+ throw refuse(
637
+ new Error(
638
+ `SQLite connection settings belong to Executor, so app code cannot change PRAGMA ${changed}; the statement was rolled back. Migrations may change settings while they run.`
639
+ )
640
+ );
641
+ };
642
+ const changes = () => Number(storage.sql.exec("SELECT total_changes() AS n").toArray()[0]?.n);
643
+ let violation;
644
+ const refuse = (error) => {
645
+ if (open) violation ??= error;
646
+ return error;
647
+ };
648
+ const run = (query, bindings, mode) => {
649
+ try {
650
+ return runStatement(query, bindings, mode);
651
+ } finally {
652
+ restoreSettings(storage, baseline);
653
+ }
654
+ };
655
+ const runStatement = (query, bindings, mode) => {
656
+ live();
657
+ if (mode === "write") {
658
+ const statement = () => {
659
+ const cursor = materialize(storage.sql.exec(query, ...bindings));
660
+ checkSettings();
661
+ return cursor;
662
+ };
663
+ if (!open) return storage.transactionSync(statement);
664
+ try {
665
+ return statement();
666
+ } catch (error) {
667
+ refuse(error instanceof Error ? error : new Error(String(error)));
668
+ throw error;
669
+ }
670
+ }
671
+ if (open) {
672
+ return runStatement(query, bindings, "write");
673
+ }
674
+ const before = changes();
675
+ let read;
676
+ let changed = false;
677
+ try {
678
+ storage.transactionSync(() => {
679
+ read = materialize(storage.sql.exec(query, ...bindings));
680
+ changed = changes() !== before;
681
+ checkSettings();
682
+ throw new ReadRolledBack();
683
+ });
684
+ } catch (error) {
685
+ if (!(error instanceof ReadRolledBack)) throw error;
686
+ }
687
+ if (changed || read === void 0)
688
+ throw new Error(
689
+ options.step !== void 0 ? "A workflow step mutation writes only inside ctx.sql.transaction(...), so a retried step applies its writes once." : "Queries are read-only. Change data from a mutation."
690
+ );
691
+ return read;
692
+ };
693
+ const reader = {
694
+ exec: (query, ...bindings) => run(query, bindings, "read")
695
+ };
696
+ const transactionHandle = () => {
697
+ let active = true;
698
+ const tx = {
699
+ exec: (query, ...bindings) => {
700
+ if (!active)
701
+ throw new Error("This transaction has ended, so its tx no longer runs statements.");
702
+ return run(query, bindings, "write");
703
+ }
704
+ };
705
+ return {
706
+ tx,
707
+ end: () => {
708
+ active = false;
709
+ }
710
+ };
711
+ };
712
+ const transaction = (work) => {
713
+ live();
714
+ if (open) throw new Error("ctx.sql.transaction(...) cannot be nested.");
715
+ const step = options.step;
716
+ if (step !== void 0 && transactions > 0)
717
+ throw new Error(
718
+ "A workflow step mutation opens at most one ctx.sql.transaction(...), which records the step's result."
719
+ );
720
+ transactions += 1;
721
+ violation = void 0;
722
+ const handle = transactionHandle();
723
+ return storage.transactionSync(() => {
724
+ open = true;
725
+ try {
726
+ const complete = () => {
727
+ const value = work(handle.tx);
728
+ if (isThenable(value))
729
+ throw new Error(
730
+ "ctx.sql.transaction(...) callbacks are synchronous and cannot await. Call outside services before or after the transaction."
731
+ );
732
+ if (violation !== void 0) throw violation;
733
+ return value;
734
+ };
735
+ if (step === void 0) return complete();
736
+ storage.sql.exec(
737
+ `CREATE TABLE IF NOT EXISTS ${receipts} (id TEXT PRIMARY KEY, fingerprint TEXT NOT NULL, result TEXT NOT NULL) WITHOUT ROWID`
738
+ );
739
+ const saved = storage.sql.exec(`SELECT fingerprint, result FROM ${receipts} WHERE id = ?`, step.key).toArray()[0];
740
+ if (saved !== void 0) {
741
+ if (saved.fingerprint !== step.fingerprint)
742
+ throw new Error("This workflow step already ran with different input.");
743
+ return JSON.parse(String(saved.result));
744
+ }
745
+ const encoded = JSON.stringify(complete() ?? null);
746
+ storage.sql.exec(
747
+ `INSERT INTO ${receipts} (id, fingerprint, result) VALUES (?, ?, ?)`,
748
+ step.key,
749
+ step.fingerprint,
750
+ encoded
751
+ );
752
+ return JSON.parse(encoded);
753
+ } finally {
754
+ open = false;
755
+ handle.end();
756
+ }
757
+ });
758
+ };
759
+ const writer = {
760
+ // A step's writes go through its one transaction, which also records its receipt.
761
+ exec: (query, ...bindings) => run(query, bindings, options.step ? "read" : "write"),
762
+ transaction
763
+ };
764
+ return {
765
+ reader,
766
+ writer,
767
+ close: () => {
768
+ closed = true;
769
+ }
770
+ };
771
+ };
772
+ var MigrationFileInvalid = class extends Schema_exports.TaggedError()(
773
+ "MigrationFileInvalid",
774
+ { path: Schema_exports.String, reason: Schema_exports.String }
775
+ ) {
776
+ get message() {
777
+ return `${this.path}: ${this.reason}`;
778
+ }
779
+ };
780
+ var MigrationHistoryChanged = class extends Schema_exports.TaggedError()(
781
+ "MigrationHistoryChanged",
782
+ { reason: Schema_exports.String }
783
+ ) {
784
+ get message() {
785
+ return `${this.reason} Applied migrations cannot change: restore the original file and add a new migration instead. Nothing was applied; the previous deployment is still active.`;
786
+ }
787
+ };
788
+ var MigrationFailed = class extends Schema_exports.TaggedError()("MigrationFailed", {
789
+ path: Schema_exports.String,
790
+ reason: Schema_exports.String
791
+ }) {
792
+ get message() {
793
+ return `${this.path} failed: ${this.reason}. No migrations were applied; the previous deployment is still active.`;
794
+ }
795
+ };
796
+ var migrationName = /^(\d+)_[A-Za-z0-9_-]+\.sql$/;
797
+ var sha256 = (text) => Effect_exports.promise(() => crypto.subtle.digest("SHA-256", new TextEncoder().encode(text))).pipe(
798
+ Effect_exports.map(
799
+ (digest) => Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, "0")).join("")
800
+ )
801
+ );
802
+ var hasMigrations = (files) => files.some(
803
+ (file) => file.path.startsWith(`${migrationsDirectory}/`) && file.path.endsWith(".sql")
804
+ );
805
+ var migrationsOf = (files) => Effect_exports.gen(function* () {
806
+ const found = [];
807
+ for (const file of files) {
808
+ if (!file.path.startsWith(`${migrationsDirectory}/`) || !file.path.endsWith(".sql")) continue;
809
+ const name = file.path.slice(migrationsDirectory.length + 1);
810
+ const match = migrationName.exec(name);
811
+ if (name.includes("/") || match === null)
812
+ return yield* new MigrationFileInvalid({
813
+ path: file.path,
814
+ reason: "migrations are files directly in migrations/ named like 0001_create_notes.sql: a number that orders them, an underscore, then a name."
815
+ });
816
+ const content = file.content.replaceAll("\r\n", "\n");
817
+ found.push({
818
+ path: file.path,
819
+ order: Number(match[1]),
820
+ content,
821
+ hash: yield* sha256(content)
822
+ });
823
+ }
824
+ const ordered = [...found].sort((left, right) => left.order - right.order);
825
+ for (const [index, migration] of ordered.entries()) {
826
+ const previous = ordered[index - 1];
827
+ if (previous !== void 0 && previous.order === migration.order)
828
+ return yield* new MigrationFileInvalid({
829
+ path: migration.path,
830
+ reason: `it has the same number as ${previous.path}; give each migration its own number.`
831
+ });
832
+ }
833
+ return ordered;
834
+ });
835
+ var History = Schema_exports.Array(
836
+ Schema_exports.Struct({ id: Schema_exports.Number, path: Schema_exports.String, hash: Schema_exports.String })
837
+ );
838
+ var migrate = (storage, files) => Effect_exports.gen(function* () {
839
+ const migrations = yield* migrationsOf(files);
840
+ const baseline = readSettings(storage);
841
+ return yield* Effect_exports.sync(
842
+ () => storage.transactionSync(() => {
843
+ storage.sql.exec(
844
+ `CREATE TABLE IF NOT EXISTS ${migrationsTable} (id INTEGER PRIMARY KEY, path TEXT NOT NULL, hash TEXT NOT NULL, applied_at TEXT NOT NULL)`
845
+ );
846
+ const stored = storage.sql.exec("SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'app_rows'").toArray().length > 0;
847
+ storage.sql.exec(`DROP VIEW IF EXISTS ${legacyRows}`);
848
+ storage.sql.exec(
849
+ `CREATE VIEW ${legacyRows} (table_name, id, body) AS ${stored ? "SELECT table_name, id, body FROM app_rows" : "SELECT '', '', '' WHERE 0"}`
850
+ );
851
+ const history = Schema_exports.decodeUnknownSync(History)(
852
+ storage.sql.exec(`SELECT id, path, hash FROM ${migrationsTable} ORDER BY id`).toArray()
853
+ );
854
+ for (const [index, applied] of history.entries()) {
855
+ const current = migrations[index];
856
+ if (current === void 0)
857
+ throw new MigrationHistoryChanged({
858
+ reason: `${applied.path} was applied to this app's database but is no longer in migrations/.`
859
+ });
860
+ if (current.path !== applied.path)
861
+ throw new MigrationHistoryChanged({
862
+ reason: `Migration ${index + 1} was applied as ${applied.path}, but the build has ${current.path} in its place.`
863
+ });
864
+ if (current.hash !== applied.hash)
865
+ throw new MigrationHistoryChanged({
866
+ reason: `${applied.path} changed after it was applied to this app's database.`
867
+ });
868
+ }
869
+ const pending = migrations.slice(history.length);
870
+ const appliedAt = (/* @__PURE__ */ new Date()).toISOString();
871
+ for (const [offset, migration] of pending.entries()) {
872
+ try {
873
+ storage.sql.exec(migration.content);
874
+ } catch (error) {
875
+ throw new MigrationFailed({
876
+ path: migration.path,
877
+ reason: error instanceof Error ? error.message : String(error)
878
+ });
879
+ }
880
+ storage.sql.exec(
881
+ `INSERT INTO ${migrationsTable} (id, path, hash, applied_at) VALUES (?, ?, ?, ?)`,
882
+ history.length + offset + 1,
883
+ migration.path,
884
+ migration.hash,
885
+ appliedAt
886
+ );
887
+ }
888
+ return pending.map((migration, offset) => ({
889
+ id: history.length + offset + 1,
890
+ name: migration.path
891
+ }));
892
+ })
893
+ ).pipe(
894
+ // Both are thrown inside the transaction so that it rolls back; they are expected failures.
895
+ Effect_exports.catchDefect(
896
+ (defect) => Schema_exports.is(MigrationFailed)(defect) || Schema_exports.is(MigrationHistoryChanged)(defect) ? Effect_exports.fail(defect) : Effect_exports.die(defect)
897
+ ),
898
+ Effect_exports.ensuring(Effect_exports.sync(() => restoreSettings(storage, baseline)))
899
+ );
900
+ });
901
+
578
902
  // src/implementation/cache.ts
579
903
  var unavailableCache = {
580
904
  transport: () => Effect_exports.fail(new CacheError({ reason: "unavailable" })),
@@ -720,7 +1044,7 @@ function providerDeclaration(provider) {
720
1044
  `Account provider "${provider.name}" has an invalid declaration`
721
1045
  );
722
1046
  }
723
- function requirements(slots, database) {
1047
+ function requirements(slots, sql) {
724
1048
  return Effect_exports.gen(function* () {
725
1049
  const accounts = /* @__PURE__ */ new Map();
726
1050
  for (const [slot, selection] of Object.entries(slots)) {
@@ -734,10 +1058,10 @@ function requirements(slots, database) {
734
1058
  return yield* Schema_exports.decodeUnknownEffect(DeclaredRequirements)({
735
1059
  accounts: Object.fromEntries(accounts),
736
1060
  capabilities: { skills: true, toolIndex: true, skillSources: true, scheduledTools: true },
737
- ...database === void 0 ? {} : { database }
1061
+ ...sql ? { sql: true } : {}
738
1062
  }).pipe(
739
1063
  Effect_exports.mapError(
740
- (cause) => declarationInvalid("The app's account or database declarations are invalid", cause)
1064
+ (cause) => declarationInvalid("The app's account declarations are invalid", cause)
741
1065
  )
742
1066
  );
743
1067
  });
@@ -842,28 +1166,51 @@ function dispatch(app, request, context, signal) {
842
1166
  );
843
1167
  const native = toEffectApp(app);
844
1168
  const secrets = accountSecrets(context.accounts);
845
- const storageFailure = (error) => Effect_exports.fail(new HostOperationFailed(failureDetail(error, secrets)));
846
- const declared = yield* requirements(native.accounts, native.database?.schema);
847
- if (request.operation === "requirements") {
848
- if (native.database !== void 0)
849
- yield* parseDatabaseSchema(native.database.schema).pipe(
850
- Effect_exports.catchTag(
851
- "AppDatabaseError",
852
- (cause) => Effect_exports.fail(
853
- declarationInvalid("The app's account or database declarations are invalid", cause)
854
- )
855
- )
856
- );
1169
+ const ownsSql = hasMigrations(context.files ?? []);
1170
+ const declared = yield* requirements(native.accounts, ownsSql);
1171
+ if (request.operation === "requirements")
857
1172
  return yield* safe2(
858
1173
  () => Schema_exports.decodeUnknownEffect(JsonValue)(declared),
859
1174
  new HostDeclarationInvalid()
860
1175
  );
1176
+ if (request.operation === "migrate") {
1177
+ if (!ownsSql) return yield* new HostOperationNotFound();
1178
+ const storage2 = context.storage;
1179
+ if (storage2 === void 0)
1180
+ return yield* new HostOperationFailed(
1181
+ failureDetail(new Error("This host gave the app no SQL storage."), secrets)
1182
+ );
1183
+ const sources = yield* Schema_exports.decodeUnknownEffect(Schema_exports.Array(SkillFile))(
1184
+ context.files ?? []
1185
+ ).pipe(Effect_exports.mapError(() => new HostDeclarationInvalid()));
1186
+ const applied = yield* migrate(storage2, sources).pipe(
1187
+ Effect_exports.mapError((error) => new HostOperationFailed(failureDetail(error, secrets))),
1188
+ Effect_exports.withSpan("app.sql.migrate")
1189
+ );
1190
+ return yield* safe2(
1191
+ () => Schema_exports.decodeUnknownEffect(JsonValue)(applied),
1192
+ new HostOutputInvalid()
1193
+ );
861
1194
  }
862
1195
  const lifetime = yield* Effect_exports.acquireRelease(
863
1196
  Effect_exports.sync(() => new AbortController()),
864
1197
  (controller) => Effect_exports.sync(() => controller.abort())
865
1198
  );
866
1199
  const invocationSignal = AbortSignal.any([signal, lifetime.signal]);
1200
+ const storage = context.storage;
1201
+ const sqlSession = (step) => !ownsSql ? Effect_exports.succeed({ reader: noDatabase, writer: noDatabase }) : storage === void 0 ? Effect_exports.fail(
1202
+ new HostOperationFailed(
1203
+ failureDetail(new Error("This host gave the app no SQL storage."), secrets)
1204
+ )
1205
+ ) : Effect_exports.acquireRelease(
1206
+ Effect_exports.sync(
1207
+ () => authorSql(storage, {
1208
+ signal: invocationSignal,
1209
+ ...step === void 0 ? {} : { step }
1210
+ })
1211
+ ),
1212
+ (session2) => Effect_exports.sync(session2.close)
1213
+ );
867
1214
  const deadline = context.deadline === void 0 ? void 0 : yield* safe2(
868
1215
  () => Schema_exports.decodeUnknownEffect(InvocationDeadline)(context.deadline),
869
1216
  new HostInputInvalid()
@@ -894,9 +1241,8 @@ function dispatch(app, request, context, signal) {
894
1241
  deadline
895
1242
  ).pipe(withinDeadline);
896
1243
  let running;
897
- let transactionOpen = false;
898
1244
  const delivery = (request2, signal2) => Effect_exports.suspend(
899
- () => transactionOpen ? Effect_exports.fail(new ElicitationFailed({ reason: "transaction" })) : running !== void 0 && context.elicitation !== void 0 ? context.elicitation(request2, signal2).pipe(
1245
+ () => running !== void 0 && context.elicitation !== void 0 ? context.elicitation(request2, signal2).pipe(
900
1246
  Effect_exports.withSpan("app.tool.elicitation"),
901
1247
  Effect_exports.provideContext(running.context)
902
1248
  ) : Effect_exports.fail(new ElicitationFailed({ reason: "unavailable" }))
@@ -1075,7 +1421,8 @@ function dispatch(app, request, context, signal) {
1075
1421
  );
1076
1422
  }
1077
1423
  if (request.operation === "webhook-complete" || request.operation === "webhook-validate" || request.operation === "webhooks" || request.operation === "webhook-register" || request.operation === "webhook-handle" || request.operation === "webhook-unregister") {
1078
- const executeWebhook = (db) => dispatchWebhook(
1424
+ const session2 = yield* sqlSession();
1425
+ return yield* dispatchWebhook(
1079
1426
  definition,
1080
1427
  request,
1081
1428
  {
@@ -1085,23 +1432,10 @@ function dispatch(app, request, context, signal) {
1085
1432
  workflows: workflowControls,
1086
1433
  signal: bound.signal,
1087
1434
  fetch: bound.fetch,
1088
- ...db === void 0 || native.database === void 0 ? {} : {
1089
- db: authorDatabase(native.database.tables, db, invocationSignal, true)
1090
- }
1435
+ sql: session2.writer
1091
1436
  },
1092
1437
  Redacted_exports.value(context.accounts)
1093
1438
  );
1094
- if (native.database === void 0 || !["webhook-register", "webhook-handle", "webhook-unregister"].includes(request.operation))
1095
- return yield* executeWebhook();
1096
- const storage2 = context.storage ?? unavailableStorage;
1097
- return yield* storage2.mutate(native.database.schema, executeWebhook).pipe(
1098
- Effect_exports.catchTags({
1099
- AppDatabaseError: storageFailure,
1100
- DatabaseLimitExceeded: (error) => Effect_exports.fail(error),
1101
- AppStorageUnavailable: storageFailure,
1102
- AppStorageError: storageFailure
1103
- })
1104
- );
1105
1439
  }
1106
1440
  const toolName = request.operation === "call" ? request.tool : request.name;
1107
1441
  const location = locate(definition.tools, toolName);
@@ -1161,8 +1495,13 @@ function dispatch(app, request, context, signal) {
1161
1495
  Match_exports.exhaustive
1162
1496
  );
1163
1497
  }
1164
- const execute = (db) => Effect_exports.gen(function* () {
1165
- transactionOpen = db !== void 0;
1498
+ const replay = context.replay === void 0 ? void 0 : yield* safe2(
1499
+ () => Schema_exports.decodeUnknownEffect(WorkflowReplay)(context.replay),
1500
+ new HostInputInvalid()
1501
+ );
1502
+ if (replay !== void 0 && kind !== "mutate") return yield* new HostInputInvalid();
1503
+ const session = yield* sqlSession(replay);
1504
+ const execute = () => Effect_exports.gen(function* () {
1166
1505
  const output = yield* Effect_exports.gen(function* () {
1167
1506
  running = yield* captureTelemetry;
1168
1507
  const fetch = yield* appInvocationFetch(fetching.signal);
@@ -1171,14 +1510,7 @@ function dispatch(app, request, context, signal) {
1171
1510
  ...bound,
1172
1511
  fetch,
1173
1512
  workflows: kind === "mutate" ? workflowControls : workflowReads,
1174
- ...db === void 0 || native.database === void 0 ? {} : {
1175
- db: authorDatabase(
1176
- native.database.tables,
1177
- db,
1178
- invocationSignal,
1179
- kind === "mutate"
1180
- )
1181
- }
1513
+ sql: kind === "mutate" ? session.writer : session.reader
1182
1514
  },
1183
1515
  input
1184
1516
  );
@@ -1218,26 +1550,7 @@ function dispatch(app, request, context, signal) {
1218
1550
  new HostOutputInvalid()
1219
1551
  );
1220
1552
  });
1221
- const replay = context.replay === void 0 ? void 0 : yield* safe2(
1222
- () => Schema_exports.decodeUnknownEffect(WorkflowReplay)(context.replay),
1223
- new HostInputInvalid()
1224
- );
1225
- if (replay !== void 0 && kind !== "mutate") return yield* new HostInputInvalid();
1226
- if (native.database === void 0) return yield* withinDeadline(execute());
1227
- const storage = context.storage ?? unavailableStorage;
1228
- return yield* storage[kind === "query" ? "read" : "mutate"](
1229
- native.database.schema,
1230
- (db) => withinDeadline(
1231
- replay === void 0 ? execute(db) : db.once(replay.key, replay.fingerprint, () => execute(db))
1232
- )
1233
- ).pipe(
1234
- Effect_exports.catchTags({
1235
- AppDatabaseError: storageFailure,
1236
- DatabaseLimitExceeded: (error) => Effect_exports.fail(error),
1237
- AppStorageUnavailable: storageFailure,
1238
- AppStorageError: storageFailure
1239
- })
1240
- );
1553
+ return yield* withinDeadline(execute());
1241
1554
  })
1242
1555
  );
1243
1556
  }
@@ -160,7 +160,7 @@ import {
160
160
  pipeThroughChannel,
161
161
  provide,
162
162
  provide2,
163
- provideContext2 as provideContext,
163
+ provideContext,
164
164
  provideMerge,
165
165
  redact,
166
166
  remove,
@@ -211,7 +211,7 @@ import {
211
211
  withFiber,
212
212
  withParentSpan,
213
213
  zero2 as zero
214
- } from "./chunk-IJQ5LE74.js";
214
+ } from "./chunk-MXDKXAY4.js";
215
215
  import {
216
216
  __export
217
217
  } from "./chunk-CZ7CSFO4.js";