@gmickel/gno 1.20.0 → 1.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/README.md +29 -5
  2. package/assets/skill/SKILL.md +46 -15
  3. package/package.json +2 -1
  4. package/spec/cli.md +144 -0
  5. package/spec/db/schema.sql +170 -0
  6. package/spec/evals-agentic.md +48 -0
  7. package/spec/mcp.md +22 -0
  8. package/spec/output-schemas/capsule-reverified-event.schema.json +47 -0
  9. package/spec/output-schemas/changes.schema.json +280 -0
  10. package/spec/output-schemas/document-diff.schema.json +185 -0
  11. package/spec/output-schemas/impact.schema.json +122 -0
  12. package/spec/output-schemas/publish-artifact.schema.json +284 -0
  13. package/spec/output-schemas/saved-capsule-list.schema.json +16 -0
  14. package/spec/output-schemas/saved-capsule-registration.schema.json +172 -0
  15. package/spec/output-schemas/saved-capsule-reverification.schema.json +59 -0
  16. package/spec/output-schemas/saved-capsule-unwatch.schema.json +16 -0
  17. package/spec/output-schemas/saved-capsule-watch.schema.json +17 -0
  18. package/src/cli/commands/changes.ts +160 -0
  19. package/src/cli/commands/context-saved.ts +189 -0
  20. package/src/cli/options.ts +8 -0
  21. package/src/cli/program.ts +195 -0
  22. package/src/core/capsule-registry.ts +279 -0
  23. package/src/core/capsule-reverification-scheduler.ts +218 -0
  24. package/src/core/capsule-reverification.ts +289 -0
  25. package/src/core/change-diff.ts +182 -0
  26. package/src/core/change-journal.ts +228 -0
  27. package/src/core/knowledge-delta.ts +395 -0
  28. package/src/core/knowledge-impact.ts +202 -0
  29. package/src/ingestion/sync.ts +214 -165
  30. package/src/mcp/tools/changes.ts +80 -0
  31. package/src/mcp/tools/index.ts +29 -0
  32. package/src/publish/artifact-validation.ts +259 -0
  33. package/src/publish/artifact.ts +234 -118
  34. package/src/publish/export-service.ts +5 -9
  35. package/src/publish/metadata.ts +195 -0
  36. package/src/sdk/client.ts +42 -0
  37. package/src/sdk/index.ts +7 -0
  38. package/src/sdk/types.ts +22 -0
  39. package/src/serve/doc-events.ts +12 -1
  40. package/src/serve/resident-runtime.ts +22 -0
  41. package/src/serve/routes/api.ts +13 -0
  42. package/src/serve/routes/changes.ts +102 -0
  43. package/src/serve/server.ts +34 -0
  44. package/src/serve/watch-service.ts +9 -0
  45. package/src/store/index.ts +21 -0
  46. package/src/store/migrations/015-document-change-journal.ts +85 -0
  47. package/src/store/migrations/016-saved-capsules.ts +131 -0
  48. package/src/store/migrations/017-document-change-retention-counters.ts +33 -0
  49. package/src/store/migrations/018-saved-capsule-registration-epoch.ts +24 -0
  50. package/src/store/migrations/019-saved-capsule-registration-generation.ts +53 -0
  51. package/src/store/migrations/index.ts +10 -0
  52. package/src/store/sqlite/adapter.ts +291 -7
  53. package/src/store/sqlite/capsule-registry-store.ts +534 -0
  54. package/src/store/sqlite/change-journal-store.ts +473 -0
  55. package/src/store/types.ts +262 -0
@@ -35,6 +35,12 @@ import type {
35
35
  DocLinkInput,
36
36
  DocLinkRow,
37
37
  DocLinkSource,
38
+ DocumentChangeKind,
39
+ DocumentChangeListOptions,
40
+ DocumentChangePage,
41
+ DocumentChangePurgeResult,
42
+ DocumentChangeRetentionPolicy,
43
+ DocumentChangeRetentionResult,
38
44
  DocumentInput,
39
45
  DocumentRow,
40
46
  EmbeddingCleanupStats,
@@ -70,6 +76,13 @@ import type {
70
76
  RetrievalTraceRow,
71
77
  RetrievalTraceRunInput,
72
78
  RetrievalTraceTerminalStatus,
79
+ RenameDocumentOptions,
80
+ SavedCapsuleRegistrationInput,
81
+ SavedCapsuleRegistrationRecord,
82
+ SavedCapsuleRegistrationSnapshot,
83
+ SavedCapsuleReverificationState,
84
+ SavedCapsuleVerificationExpectation,
85
+ SavedCapsuleVerificationRecord,
73
86
  StorePort,
74
87
  StoreResult,
75
88
  TagCount,
@@ -95,6 +108,25 @@ import { getSchemaVersion, migrations, runMigrations } from "../migrations";
95
108
  import { err, ok } from "../types";
96
109
  import { getStoredEmbeddingFingerprint } from "../vector/freshness";
97
110
  import { modelTableName } from "../vector/sqlite-vec";
111
+ import {
112
+ deleteSavedCapsuleRegistration as deleteStoredSavedCapsuleRegistration,
113
+ getSavedCapsuleRegistration as getStoredSavedCapsuleRegistration,
114
+ getSavedCapsuleRegistrationSnapshot as getStoredSavedCapsuleRegistrationSnapshot,
115
+ getSavedCapsuleReverificationState as getStoredSavedCapsuleReverificationState,
116
+ getSavedCapsuleReverificationSequence as getStoredSavedCapsuleReverificationSequence,
117
+ listSavedCapsuleIdsAffectedByChanges as listStoredSavedCapsuleIdsAffectedByChanges,
118
+ listSavedCapsuleRegistrations as listStoredSavedCapsuleRegistrations,
119
+ setSavedCapsuleReverificationSequence as setStoredSavedCapsuleReverificationSequence,
120
+ upsertSavedCapsuleRegistration as upsertStoredSavedCapsuleRegistration,
121
+ upsertSavedCapsuleVerification as upsertStoredSavedCapsuleVerification,
122
+ } from "./capsule-registry-store";
123
+ import {
124
+ appendDocumentChange as appendStoredDocumentChange,
125
+ enforceDocumentChangeRetention as enforceStoredDocumentChangeRetention,
126
+ listDocumentChanges as listStoredDocumentChanges,
127
+ purgeDocumentChanges as purgeStoredDocumentChanges,
128
+ snapshotDocumentChange,
129
+ } from "./change-journal-store";
98
130
  import { loadFts5Snowball } from "./fts5-snowball";
99
131
  import {
100
132
  appendExportManifest as appendStoredTraceExportManifest,
@@ -940,6 +972,12 @@ export class SqliteAdapter implements StorePort, SqliteDbProvider {
940
972
  const uri = buildUri(doc.collection, doc.relPath);
941
973
 
942
974
  const transaction = db.transaction((): UpsertDocumentResult => {
975
+ const previousRow = db
976
+ .query<DbDocumentRow, [string, string]>(
977
+ "SELECT * FROM documents WHERE collection = ? AND rel_path = ?"
978
+ )
979
+ .get(doc.collection, doc.relPath);
980
+
943
981
  db.run(
944
982
  `
945
983
  INSERT INTO documents (
@@ -1030,6 +1068,41 @@ export class SqliteAdapter implements StorePort, SqliteDbProvider {
1030
1068
  ]);
1031
1069
  }
1032
1070
 
1071
+ if (doc.changeJournal !== false) {
1072
+ const nextRow = db
1073
+ .query<DbDocumentRow, [number]>(
1074
+ "SELECT * FROM documents WHERE id = ?"
1075
+ )
1076
+ .get(idRow.id);
1077
+ if (!nextRow) {
1078
+ throw new Error("Failed to read document after upsert");
1079
+ }
1080
+ const previous = previousRow
1081
+ ? snapshotDocumentChange(mapDocumentRow(previousRow))
1082
+ : null;
1083
+ const next = snapshotDocumentChange(mapDocumentRow(nextRow));
1084
+ const kind: DocumentChangeKind | null =
1085
+ previous === null
1086
+ ? "create"
1087
+ : !previous.active
1088
+ ? "reactivate"
1089
+ : previous.sourceHash !== next.sourceHash ||
1090
+ previous.mirrorHash !== next.mirrorHash
1091
+ ? "update"
1092
+ : null;
1093
+ if (kind) {
1094
+ appendStoredDocumentChange(db, {
1095
+ documentId: idRow.id,
1096
+ collection: doc.collection,
1097
+ kind,
1098
+ oldSnapshot: previous,
1099
+ newSnapshot: next,
1100
+ structureDelta: doc.changeJournal?.structureDelta,
1101
+ observedAtMs: doc.changeJournal?.observedAtMs ?? Date.now(),
1102
+ });
1103
+ }
1104
+ }
1105
+
1033
1106
  return { id: idRow.id, docid };
1034
1107
  });
1035
1108
 
@@ -1043,6 +1116,82 @@ export class SqliteAdapter implements StorePort, SqliteDbProvider {
1043
1116
  }
1044
1117
  }
1045
1118
 
1119
+ async renameDocument(
1120
+ collection: string,
1121
+ oldRelPath: string,
1122
+ newRelPath: string,
1123
+ options: RenameDocumentOptions = {}
1124
+ ): Promise<StoreResult<DocumentRow>> {
1125
+ try {
1126
+ if (!oldRelPath || !newRelPath) {
1127
+ return err("INVALID_INPUT", "Rename paths must be non-empty");
1128
+ }
1129
+ const db = this.ensureOpen();
1130
+ const transaction = db.transaction((): DocumentRow => {
1131
+ const oldRow = db
1132
+ .query<DbDocumentRow, [string, string]>(
1133
+ "SELECT * FROM documents WHERE collection = ? AND rel_path = ?"
1134
+ )
1135
+ .get(collection, oldRelPath);
1136
+ if (!oldRow) {
1137
+ throw new Error("DOCUMENT_RENAME_NOT_FOUND");
1138
+ }
1139
+ if (oldRelPath === newRelPath) {
1140
+ return mapDocumentRow(oldRow);
1141
+ }
1142
+
1143
+ const newUri = buildUri(collection, newRelPath);
1144
+ db.run(
1145
+ `UPDATE documents
1146
+ SET rel_path = ?, uri = ?, updated_at = datetime('now')
1147
+ WHERE id = ?`,
1148
+ [newRelPath, newUri, oldRow.id]
1149
+ );
1150
+ db.run("UPDATE documents_fts SET filepath = ? WHERE rowid = ?", [
1151
+ newRelPath,
1152
+ oldRow.id,
1153
+ ]);
1154
+ const nextRow = db
1155
+ .query<DbDocumentRow, [number]>(
1156
+ "SELECT * FROM documents WHERE id = ?"
1157
+ )
1158
+ .get(oldRow.id);
1159
+ if (!nextRow) {
1160
+ throw new Error("Failed to read document after rename");
1161
+ }
1162
+ const oldSnapshot = snapshotDocumentChange(mapDocumentRow(oldRow));
1163
+ const nextDocument = mapDocumentRow(nextRow);
1164
+ appendStoredDocumentChange(db, {
1165
+ documentId: oldRow.id,
1166
+ collection,
1167
+ kind: "rename",
1168
+ oldSnapshot,
1169
+ newSnapshot: snapshotDocumentChange(nextDocument),
1170
+ structureDelta: options.structureDelta,
1171
+ observedAtMs: options.observedAtMs ?? Date.now(),
1172
+ });
1173
+ return nextDocument;
1174
+ });
1175
+
1176
+ return ok(transaction());
1177
+ } catch (cause) {
1178
+ if (
1179
+ cause instanceof Error &&
1180
+ cause.message === "DOCUMENT_RENAME_NOT_FOUND"
1181
+ ) {
1182
+ return err(
1183
+ "NOT_FOUND",
1184
+ `Document not found for rename: ${collection}/${oldRelPath}`
1185
+ );
1186
+ }
1187
+ return err(
1188
+ "QUERY_FAILED",
1189
+ cause instanceof Error ? cause.message : "Failed to rename document",
1190
+ cause
1191
+ );
1192
+ }
1193
+ }
1194
+
1046
1195
  async getDocument(
1047
1196
  collection: string,
1048
1197
  relPath: string
@@ -1453,14 +1602,45 @@ export class SqliteAdapter implements StorePort, SqliteDbProvider {
1453
1602
  return ok(0);
1454
1603
  }
1455
1604
 
1456
- const placeholders = relPaths.map(() => "?").join(",");
1457
- const result = db.run(
1458
- `UPDATE documents SET active = 0, updated_at = datetime('now')
1459
- WHERE collection = ? AND rel_path IN (${placeholders})`,
1460
- [collection, ...relPaths]
1461
- );
1605
+ const uniquePaths = [...new Set(relPaths)];
1606
+ const placeholders = uniquePaths.map(() => "?").join(",");
1607
+ const transaction = db.transaction((): number => {
1608
+ const activeRows = db
1609
+ .query<DbDocumentRow, (string | number)[]>(
1610
+ `SELECT *
1611
+ FROM documents
1612
+ WHERE collection = ?
1613
+ AND active = 1
1614
+ AND rel_path IN (${placeholders})
1615
+ ORDER BY rel_path ASC, id ASC`
1616
+ )
1617
+ .all(collection, ...uniquePaths);
1618
+ if (activeRows.length === 0) {
1619
+ return 0;
1620
+ }
1621
+ const result = db.run(
1622
+ `UPDATE documents SET active = 0, updated_at = datetime('now')
1623
+ WHERE collection = ?
1624
+ AND active = 1
1625
+ AND rel_path IN (${placeholders})`,
1626
+ [collection, ...uniquePaths]
1627
+ );
1628
+ const observedAtMs = Date.now();
1629
+ for (const activeRow of activeRows) {
1630
+ const previous = snapshotDocumentChange(mapDocumentRow(activeRow));
1631
+ appendStoredDocumentChange(db, {
1632
+ documentId: activeRow.id,
1633
+ collection,
1634
+ kind: "inactivate",
1635
+ oldSnapshot: previous,
1636
+ newSnapshot: { ...previous, active: false },
1637
+ observedAtMs,
1638
+ });
1639
+ }
1640
+ return result.changes;
1641
+ });
1462
1642
 
1463
- return ok(result.changes);
1643
+ return ok(transaction());
1464
1644
  } catch (cause) {
1465
1645
  return err(
1466
1646
  "QUERY_FAILED",
@@ -1472,6 +1652,110 @@ export class SqliteAdapter implements StorePort, SqliteDbProvider {
1472
1652
  }
1473
1653
  }
1474
1654
 
1655
+ async listDocumentChanges(
1656
+ options: DocumentChangeListOptions = {}
1657
+ ): Promise<StoreResult<DocumentChangePage>> {
1658
+ return listStoredDocumentChanges(this.ensureOpen(), options);
1659
+ }
1660
+
1661
+ async enforceDocumentChangeRetention(
1662
+ policy: DocumentChangeRetentionPolicy,
1663
+ nowMs: number
1664
+ ): Promise<StoreResult<DocumentChangeRetentionResult>> {
1665
+ return enforceStoredDocumentChangeRetention(
1666
+ this.ensureOpen(),
1667
+ policy,
1668
+ nowMs
1669
+ );
1670
+ }
1671
+
1672
+ async purgeDocumentChanges(): Promise<
1673
+ StoreResult<DocumentChangePurgeResult>
1674
+ > {
1675
+ return purgeStoredDocumentChanges(this.ensureOpen());
1676
+ }
1677
+
1678
+ async upsertSavedCapsuleRegistration(
1679
+ input: SavedCapsuleRegistrationInput
1680
+ ): Promise<StoreResult<SavedCapsuleRegistrationRecord>> {
1681
+ return upsertStoredSavedCapsuleRegistration(this.ensureOpen(), input);
1682
+ }
1683
+
1684
+ async listSavedCapsuleRegistrations(): Promise<
1685
+ StoreResult<SavedCapsuleRegistrationRecord[]>
1686
+ > {
1687
+ return listStoredSavedCapsuleRegistrations(this.ensureOpen());
1688
+ }
1689
+
1690
+ async getSavedCapsuleRegistration(
1691
+ registrationId: string
1692
+ ): Promise<StoreResult<SavedCapsuleRegistrationRecord | null>> {
1693
+ return getStoredSavedCapsuleRegistration(this.ensureOpen(), registrationId);
1694
+ }
1695
+
1696
+ async getSavedCapsuleRegistrationSnapshot(
1697
+ registrationId: string
1698
+ ): Promise<StoreResult<SavedCapsuleRegistrationSnapshot | null>> {
1699
+ return getStoredSavedCapsuleRegistrationSnapshot(
1700
+ this.ensureOpen(),
1701
+ registrationId
1702
+ );
1703
+ }
1704
+
1705
+ async deleteSavedCapsuleRegistration(
1706
+ registrationId: string
1707
+ ): Promise<StoreResult<boolean>> {
1708
+ return deleteStoredSavedCapsuleRegistration(
1709
+ this.ensureOpen(),
1710
+ registrationId
1711
+ );
1712
+ }
1713
+
1714
+ async listSavedCapsuleIdsAffectedByChanges(
1715
+ afterSequence: number,
1716
+ throughSequence: number,
1717
+ limit: number
1718
+ ): Promise<StoreResult<{ registrationIds: string[]; truncated: boolean }>> {
1719
+ return listStoredSavedCapsuleIdsAffectedByChanges(
1720
+ this.ensureOpen(),
1721
+ afterSequence,
1722
+ throughSequence,
1723
+ limit
1724
+ );
1725
+ }
1726
+
1727
+ async upsertSavedCapsuleVerification(
1728
+ verification: SavedCapsuleVerificationRecord,
1729
+ expectedRegistration: SavedCapsuleVerificationExpectation
1730
+ ): Promise<StoreResult<boolean>> {
1731
+ return upsertStoredSavedCapsuleVerification(
1732
+ this.ensureOpen(),
1733
+ verification,
1734
+ expectedRegistration
1735
+ );
1736
+ }
1737
+
1738
+ async getSavedCapsuleReverificationSequence(): Promise<StoreResult<number>> {
1739
+ return getStoredSavedCapsuleReverificationSequence(this.ensureOpen());
1740
+ }
1741
+
1742
+ async getSavedCapsuleReverificationState(): Promise<
1743
+ StoreResult<SavedCapsuleReverificationState>
1744
+ > {
1745
+ return getStoredSavedCapsuleReverificationState(this.ensureOpen());
1746
+ }
1747
+
1748
+ async setSavedCapsuleReverificationSequence(
1749
+ sequence: number,
1750
+ expectedRegistrationEpoch: number
1751
+ ): Promise<StoreResult<boolean>> {
1752
+ return setStoredSavedCapsuleReverificationSequence(
1753
+ this.ensureOpen(),
1754
+ sequence,
1755
+ expectedRegistrationEpoch
1756
+ );
1757
+ }
1758
+
1475
1759
  // ─────────────────────────────────────────────────────────────────────────
1476
1760
  // Content
1477
1761
  // ─────────────────────────────────────────────────────────────────────────