@hraness/sponge 0.2.3 → 0.2.5

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 (88) hide show
  1. package/README.md +53 -0
  2. package/dist/migrations/005.d.ts +3 -0
  3. package/dist/migrations/005.js +19 -0
  4. package/dist/migrations/006.d.ts +3 -0
  5. package/dist/migrations/006.js +9 -0
  6. package/dist/migrations/007.d.ts +3 -0
  7. package/dist/migrations/007.js +10 -0
  8. package/dist/src/acquisition/lock.d.ts +1 -1
  9. package/dist/src/acquisition/lock.js +1 -1
  10. package/dist/src/acquisition/pins.js +3 -3
  11. package/dist/src/annotations/quote.d.ts +32 -0
  12. package/dist/src/annotations/quote.js +135 -0
  13. package/dist/src/annotations/store.d.ts +50 -0
  14. package/dist/src/annotations/store.js +89 -0
  15. package/dist/src/app/source.d.ts +2 -0
  16. package/dist/src/app/source.js +13 -17
  17. package/dist/src/cli/arguments.js +24 -7
  18. package/dist/src/cli/digest.d.ts +10 -0
  19. package/dist/src/cli/digest.js +63 -0
  20. package/dist/src/cli/import.d.ts +11 -0
  21. package/dist/src/cli/import.js +10 -0
  22. package/dist/src/cli/indexing.d.ts +3 -0
  23. package/dist/src/cli/indexing.js +5 -2
  24. package/dist/src/cli/main.js +94 -8
  25. package/dist/src/cli/runtimes.d.ts +10 -0
  26. package/dist/src/cli/runtimes.js +32 -0
  27. package/dist/src/digest/editorial.d.ts +53 -0
  28. package/dist/src/digest/editorial.js +81 -0
  29. package/dist/src/digest/endpoint.d.ts +20 -0
  30. package/dist/src/digest/endpoint.js +103 -0
  31. package/dist/src/digest/pipeline.d.ts +148 -0
  32. package/dist/src/digest/pipeline.js +410 -0
  33. package/dist/src/digest/schema.d.ts +85 -0
  34. package/dist/src/digest/schema.js +118 -0
  35. package/dist/src/digest/store.d.ts +57 -0
  36. package/dist/src/digest/store.js +77 -0
  37. package/dist/src/identity/query-policy.d.ts +21 -0
  38. package/dist/src/identity/query-policy.js +55 -0
  39. package/dist/src/identity/uri.d.ts +30 -0
  40. package/dist/src/identity/uri.js +94 -0
  41. package/dist/src/identity/visibility.d.ts +24 -0
  42. package/dist/src/identity/visibility.js +75 -0
  43. package/dist/src/importers/apply.d.ts +10 -0
  44. package/dist/src/importers/apply.js +25 -22
  45. package/dist/src/importers/jungle-digest.d.ts +97 -0
  46. package/dist/src/importers/jungle-digest.js +221 -0
  47. package/dist/src/importers/jungle-store.d.ts +45 -0
  48. package/dist/src/importers/jungle-store.js +95 -0
  49. package/dist/src/importers/jungle.d.ts +54 -0
  50. package/dist/src/importers/jungle.js +134 -0
  51. package/dist/src/indexing/index.d.ts +7 -0
  52. package/dist/src/indexing/index.js +31 -8
  53. package/dist/src/indexing/prune-run.d.ts +22 -0
  54. package/dist/src/indexing/prune-run.js +67 -0
  55. package/dist/src/indexing/prune.d.ts +30 -0
  56. package/dist/src/indexing/prune.js +95 -0
  57. package/dist/src/indexing/runner.js +5 -5
  58. package/dist/src/inference/embedding/index.d.ts +25 -0
  59. package/dist/src/inference/embedding/index.js +11 -0
  60. package/dist/src/models/artifacts.js +175 -33
  61. package/dist/src/portability/database.js +3 -0
  62. package/dist/src/publish/component.d.ts +8 -0
  63. package/dist/src/publish/component.js +114 -0
  64. package/dist/src/publish/feed.d.ts +24 -0
  65. package/dist/src/publish/feed.js +77 -0
  66. package/dist/src/publish/identity.d.ts +6 -0
  67. package/dist/src/publish/identity.js +68 -0
  68. package/dist/src/publish/index.d.ts +34 -0
  69. package/dist/src/publish/index.js +137 -0
  70. package/dist/src/publish/projection.d.ts +29 -0
  71. package/dist/src/publish/projection.js +43 -0
  72. package/dist/src/search/hybrid.js +2 -2
  73. package/dist/src/search/lexical.d.ts +11 -0
  74. package/dist/src/search/lexical.js +6 -0
  75. package/dist/src/search/query.d.ts +7 -3
  76. package/dist/src/search/query.js +52 -33
  77. package/dist/src/search/vector.d.ts +1 -1
  78. package/dist/src/search/vector.js +6 -5
  79. package/dist/src/store/changes.d.ts +42 -0
  80. package/dist/src/store/changes.js +178 -0
  81. package/dist/src/store/foundation.d.ts +7 -0
  82. package/dist/src/store/foundation.js +7 -0
  83. package/dist/src/store/index.d.ts +69 -0
  84. package/dist/src/store/index.js +134 -51
  85. package/dist/src/worker/runtime-gc.d.ts +30 -0
  86. package/dist/src/worker/runtime-gc.js +296 -0
  87. package/native/runtime-manifest.json +4 -4
  88. package/package.json +1 -1
package/README.md CHANGED
@@ -101,6 +101,35 @@ full-text search. Use `index create --profile NAME` and `search QUERY --profile
101
101
  to keep separate search profiles. Model changes create separate index generations; a new one
102
102
  becomes active only after it covers the current library. `index rollback
103
103
  GENERATION` requires a complete generation compatible with the current revisions.
104
+ `index prune --dry-run` lists what pruning would delete; `index prune` deletes the
105
+ vectors of cancelled, failed and retired generations, keeping the active and any
106
+ building generation plus the most recent rollback-ready one per profile (`--keep N`,
107
+ 0-16). It refuses while index work, cleanup or a backup is running. Freed pages are
108
+ reused; `--vacuum` also shrinks the file after checking free space. Rollback to a
109
+ pruned generation needs a rebuild.
110
+
111
+ ### Digests
112
+
113
+ A digest is a one-sentence gist and 3–5 ideas, each with 1–2 verbatim quotes.
114
+ A local model proposes it; Sponge checks every rule against the captured text
115
+ and stores only a passing draft. Drafting is opt-in and needs a model server you
116
+ start yourself, such as llama.cpp `llama-server`, LM Studio or Ollama:
117
+
118
+ ```sh
119
+ llama-server -m gemma-4-12b-it-Q4_K_M.gguf --port 8080
120
+ sponge digest ITEM --endpoint http://127.0.0.1:8080 --model NAME
121
+ sponge digest ITEM
122
+ sponge review DIGEST --accept all --reject quote:1.0
123
+ ```
124
+
125
+ `--model` must match an id the server lists at `/v1/models`. Endpoints must be
126
+ literal loopback HTTP (`127.0.0.1` or `[::1]`); Sponge refuses anything else so
127
+ captured text never leaves the machine, and never starts or downloads a model.
128
+ Suggested models are Gemma 4 12B on 16 GB Macs and Gemma 4 E4B on 8 GB, with
129
+ Qwen3.5 9B or 4B as alternatives. Rerunning with the same inputs returns the stored
130
+ draft. Review targets are `all`, `gist`, `idea:N` and `quote:N.M` (0-based); only
131
+ accepted fields can be published. `--check-support` also requires each quote to
132
+ support its claim by local embedding similarity. See [digest drafting](src/digest/README.md).
104
133
 
105
134
  ### Export, recovery and background work
106
135
 
@@ -110,6 +139,7 @@ sponge backup --output /absolute/new/library.sponge
110
139
  sponge restore /absolute/library.sponge --into /absolute/new/library
111
140
  sponge gc --dry-run
112
141
  sponge gc --apply /absolute/path/to/report.json
142
+ sponge gc --runtimes --dry-run
113
143
  sponge worker install --language en
114
144
  sponge worker status
115
145
  sponge worker uninstall
@@ -119,6 +149,10 @@ Exports contain Markdown, images and saved notes. Backups preserve revisions,
119
149
  assets, reading state and notes. Restore requires a new directory, verifies every
120
150
  file and leaves connections unresolved; runtimes and credentials are excluded.
121
151
  Garbage collection removes only unreferenced blobs after rechecking the report.
152
+ `gc --runtimes` removes capture runtimes and model packages this Sponge no longer
153
+ pins, plus settled worker journals; `--dry-run` only lists them. It holds worker
154
+ custody, keeps models an active or building index generation needs, and refuses
155
+ symlinks and anything in use. Successful `setup` runs the same collection.
122
156
 
123
157
  The background worker is opt-in and processes work sequentially. Its LaunchAgent
124
158
  pins the installed command; after an upgrade, reinstall the worker to update that
@@ -126,6 +160,25 @@ binding. Uninstall stops the worker and preserves the library. In-session startu
126
160
  and cleanup are checked; fresh-login startup remains unqualified. See
127
161
  [importing existing captures](docs/importing.md) for a preview-first import flow.
128
162
 
163
+ ### Public reading list
164
+
165
+ ```sh
166
+ sponge publish --dry-run
167
+ sponge publish --output /absolute/site --title 'My reads' --base-url https://reads.example.com/ --min-age-days 7
168
+ ```
169
+
170
+ Items captured from public sources are public by default; `visibility ITEM private`
171
+ hides one. `publish --output` writes a static site: a JSON Feed (`feed.json`,
172
+ newest 100), monthly shards under `items/`, an Atom `feed.xml`, and an
173
+ `index.html` that uses the bundled `<sponge-list>` element. An Ed25519 key created
174
+ in the library on first publish signs a manifest of every file's hash; the
175
+ private key stays in the library. The same library state gives byte-identical
176
+ files. `--min-age-days N` holds back items listed in the last N days. Nothing is
177
+ uploaded. Embed the list elsewhere with
178
+ `<sponge-list src="https://reads.example.com/feed.json" verify></sponge-list>` and
179
+ `sponge-list.js`; the host must allow cross-origin reads, and `verify` needs
180
+ HTTPS. See [the decision record](docs/decisions/publish-feed.md).
181
+
129
182
  ### Selected authenticated connections
130
183
 
131
184
  The initial development interface accepts private `connections.json` in the
@@ -0,0 +1,3 @@
1
+ /** Additive sync and publishing foundation: shareable URIs, visibility, change log, annotations, content-addressed derivations. */
2
+ export declare const version = 5;
3
+ export declare const sql = "\nCREATE TABLE item_uris(item_id TEXT NOT NULL REFERENCES items(id),uri TEXT NOT NULL CHECK(length(uri) BETWEEN 3 AND 8192),role TEXT NOT NULL CHECK(role IN ('primary','alias')),resolvable INTEGER NOT NULL CHECK(resolvable IN (0,1)),origin TEXT NOT NULL CHECK(origin IN ('locator','revision','metadata','user')),PRIMARY KEY(item_id,uri)) STRICT;\nCREATE UNIQUE INDEX item_uris_primary ON item_uris(item_id) WHERE role='primary';\nCREATE INDEX item_uris_uri ON item_uris(uri);\nALTER TABLE items ADD COLUMN visibility TEXT CHECK(visibility IN ('public','private'));\nALTER TABLE annotations ADD COLUMN kind TEXT NOT NULL DEFAULT 'note' CHECK(kind IN ('note','highlight','comment'));\nALTER TABLE annotations ADD COLUMN parent_id TEXT REFERENCES annotations(id);\nALTER TABLE annotations ADD COLUMN updated_at TEXT;\nALTER TABLE annotations ADD COLUMN deleted_at TEXT;\nCREATE TABLE device(id INTEGER PRIMARY KEY CHECK(id=1),device_id TEXT NOT NULL CHECK(length(device_id)=32),clock_ms INTEGER NOT NULL DEFAULT 0 CHECK(clock_ms>=0),clock_counter INTEGER NOT NULL DEFAULT 0 CHECK(clock_counter>=0)) STRICT;\nINSERT INTO device(id,device_id) VALUES(1,lower(hex(randomblob(16))));\nCREATE TABLE changes(seq INTEGER PRIMARY KEY AUTOINCREMENT,id TEXT NOT NULL UNIQUE,device_id TEXT NOT NULL,hlc TEXT NOT NULL,entity TEXT NOT NULL CHECK(entity IN ('item','annotation','reading','derivation')),entity_id TEXT NOT NULL,field TEXT NOT NULL,value_json TEXT NOT NULL,created_at TEXT NOT NULL) STRICT;\nCREATE INDEX changes_entity ON changes(entity,entity_id,field,hlc);\nCREATE TABLE derivations(id TEXT PRIMARY KEY,item_id TEXT NOT NULL REFERENCES items(id),revision_id TEXT NOT NULL REFERENCES revisions(id),kind TEXT NOT NULL CHECK(kind IN ('digest')),schema_version INTEGER NOT NULL CHECK(schema_version>0),producer_json TEXT NOT NULL,input_hash TEXT NOT NULL CHECK(length(input_hash)=64),output_json TEXT NOT NULL,review_json TEXT NOT NULL DEFAULT '{}',created_at TEXT NOT NULL) STRICT;\nCREATE INDEX derivations_revision ON derivations(revision_id,kind,input_hash);\nCREATE INDEX derivations_item ON derivations(item_id,kind,created_at);\n";
@@ -0,0 +1,19 @@
1
+ /** Additive sync and publishing foundation: shareable URIs, visibility, change log, annotations, content-addressed derivations. */
2
+ export const version = 5;
3
+ export const sql = `
4
+ CREATE TABLE item_uris(item_id TEXT NOT NULL REFERENCES items(id),uri TEXT NOT NULL CHECK(length(uri) BETWEEN 3 AND 8192),role TEXT NOT NULL CHECK(role IN ('primary','alias')),resolvable INTEGER NOT NULL CHECK(resolvable IN (0,1)),origin TEXT NOT NULL CHECK(origin IN ('locator','revision','metadata','user')),PRIMARY KEY(item_id,uri)) STRICT;
5
+ CREATE UNIQUE INDEX item_uris_primary ON item_uris(item_id) WHERE role='primary';
6
+ CREATE INDEX item_uris_uri ON item_uris(uri);
7
+ ALTER TABLE items ADD COLUMN visibility TEXT CHECK(visibility IN ('public','private'));
8
+ ALTER TABLE annotations ADD COLUMN kind TEXT NOT NULL DEFAULT 'note' CHECK(kind IN ('note','highlight','comment'));
9
+ ALTER TABLE annotations ADD COLUMN parent_id TEXT REFERENCES annotations(id);
10
+ ALTER TABLE annotations ADD COLUMN updated_at TEXT;
11
+ ALTER TABLE annotations ADD COLUMN deleted_at TEXT;
12
+ CREATE TABLE device(id INTEGER PRIMARY KEY CHECK(id=1),device_id TEXT NOT NULL CHECK(length(device_id)=32),clock_ms INTEGER NOT NULL DEFAULT 0 CHECK(clock_ms>=0),clock_counter INTEGER NOT NULL DEFAULT 0 CHECK(clock_counter>=0)) STRICT;
13
+ INSERT INTO device(id,device_id) VALUES(1,lower(hex(randomblob(16))));
14
+ CREATE TABLE changes(seq INTEGER PRIMARY KEY AUTOINCREMENT,id TEXT NOT NULL UNIQUE,device_id TEXT NOT NULL,hlc TEXT NOT NULL,entity TEXT NOT NULL CHECK(entity IN ('item','annotation','reading','derivation')),entity_id TEXT NOT NULL,field TEXT NOT NULL,value_json TEXT NOT NULL,created_at TEXT NOT NULL) STRICT;
15
+ CREATE INDEX changes_entity ON changes(entity,entity_id,field,hlc);
16
+ CREATE TABLE derivations(id TEXT PRIMARY KEY,item_id TEXT NOT NULL REFERENCES items(id),revision_id TEXT NOT NULL REFERENCES revisions(id),kind TEXT NOT NULL CHECK(kind IN ('digest')),schema_version INTEGER NOT NULL CHECK(schema_version>0),producer_json TEXT NOT NULL,input_hash TEXT NOT NULL CHECK(length(input_hash)=64),output_json TEXT NOT NULL,review_json TEXT NOT NULL DEFAULT '{}',created_at TEXT NOT NULL) STRICT;
17
+ CREATE INDEX derivations_revision ON derivations(revision_id,kind,input_hash);
18
+ CREATE INDEX derivations_item ON derivations(item_id,kind,created_at);
19
+ `;
@@ -0,0 +1,3 @@
1
+ /** Storage diet: readable FTS keeps no text copy; representations.markdown and items.title stay authoritative. */
2
+ export declare const version = 6;
3
+ export declare const sql = "\nCREATE TABLE readable_fts_rows(id INTEGER PRIMARY KEY,item_id TEXT NOT NULL UNIQUE REFERENCES items(id),revision_id TEXT NOT NULL REFERENCES revisions(id)) STRICT;\nDROP TABLE readable_fts;\nCREATE VIRTUAL TABLE readable_fts USING fts5(title,markdown,content='',contentless_delete=1,tokenize='unicode61');\nINSERT INTO readable_fts_rows(item_id,revision_id) SELECT i.id,i.current_revision FROM items i WHERE i.deleted_at IS NULL AND i.current_revision IS NOT NULL AND EXISTS(SELECT 1 FROM representations p WHERE p.revision_id=i.current_revision AND p.kind='markdown' AND p.markdown IS NOT NULL) ORDER BY i.created_at,i.id;\nINSERT INTO readable_fts(rowid,title,markdown) SELECT m.id,i.title,(SELECT p.markdown FROM representations p WHERE p.revision_id=m.revision_id AND p.kind='markdown' AND p.markdown IS NOT NULL ORDER BY p.rowid LIMIT 1) FROM readable_fts_rows m JOIN items i ON i.id=m.item_id ORDER BY m.id;\n";
@@ -0,0 +1,9 @@
1
+ /** Storage diet: readable FTS keeps no text copy; representations.markdown and items.title stay authoritative. */
2
+ export const version = 6;
3
+ export const sql = `
4
+ CREATE TABLE readable_fts_rows(id INTEGER PRIMARY KEY,item_id TEXT NOT NULL UNIQUE REFERENCES items(id),revision_id TEXT NOT NULL REFERENCES revisions(id)) STRICT;
5
+ DROP TABLE readable_fts;
6
+ CREATE VIRTUAL TABLE readable_fts USING fts5(title,markdown,content='',contentless_delete=1,tokenize='unicode61');
7
+ INSERT INTO readable_fts_rows(item_id,revision_id) SELECT i.id,i.current_revision FROM items i WHERE i.deleted_at IS NULL AND i.current_revision IS NOT NULL AND EXISTS(SELECT 1 FROM representations p WHERE p.revision_id=i.current_revision AND p.kind='markdown' AND p.markdown IS NOT NULL) ORDER BY i.created_at,i.id;
8
+ INSERT INTO readable_fts(rowid,title,markdown) SELECT m.id,i.title,(SELECT p.markdown FROM representations p WHERE p.revision_id=m.revision_id AND p.kind='markdown' AND p.markdown IS NOT NULL ORDER BY p.rowid LIMIT 1) FROM readable_fts_rows m JOIN items i ON i.id=m.item_id ORDER BY m.id;
9
+ `;
@@ -0,0 +1,3 @@
1
+ /** Editorial derivations: a reviewed source note (Jungle Reading) kept losslessly beside Digest v1. SQLite cannot widen a CHECK in place, so the table is rebuilt; rows and ids are unchanged. */
2
+ export declare const version = 7;
3
+ export declare const sql = "\nCREATE TABLE derivations_v7(id TEXT PRIMARY KEY,item_id TEXT NOT NULL REFERENCES items(id),revision_id TEXT NOT NULL REFERENCES revisions(id),kind TEXT NOT NULL CHECK(kind IN ('digest','editorial')),schema_version INTEGER NOT NULL CHECK(schema_version>0),producer_json TEXT NOT NULL,input_hash TEXT NOT NULL CHECK(length(input_hash)=64),output_json TEXT NOT NULL,review_json TEXT NOT NULL DEFAULT '{}',created_at TEXT NOT NULL) STRICT;\nINSERT INTO derivations_v7(rowid,id,item_id,revision_id,kind,schema_version,producer_json,input_hash,output_json,review_json,created_at) SELECT rowid,id,item_id,revision_id,kind,schema_version,producer_json,input_hash,output_json,review_json,created_at FROM derivations ORDER BY rowid;\nDROP TABLE derivations;\nALTER TABLE derivations_v7 RENAME TO derivations;\nCREATE INDEX derivations_revision ON derivations(revision_id,kind,input_hash);\nCREATE INDEX derivations_item ON derivations(item_id,kind,created_at);\n";
@@ -0,0 +1,10 @@
1
+ /** Editorial derivations: a reviewed source note (Jungle Reading) kept losslessly beside Digest v1. SQLite cannot widen a CHECK in place, so the table is rebuilt; rows and ids are unchanged. */
2
+ export const version = 7;
3
+ export const sql = `
4
+ CREATE TABLE derivations_v7(id TEXT PRIMARY KEY,item_id TEXT NOT NULL REFERENCES items(id),revision_id TEXT NOT NULL REFERENCES revisions(id),kind TEXT NOT NULL CHECK(kind IN ('digest','editorial')),schema_version INTEGER NOT NULL CHECK(schema_version>0),producer_json TEXT NOT NULL,input_hash TEXT NOT NULL CHECK(length(input_hash)=64),output_json TEXT NOT NULL,review_json TEXT NOT NULL DEFAULT '{}',created_at TEXT NOT NULL) STRICT;
5
+ INSERT INTO derivations_v7(rowid,id,item_id,revision_id,kind,schema_version,producer_json,input_hash,output_json,review_json,created_at) SELECT rowid,id,item_id,revision_id,kind,schema_version,producer_json,input_hash,output_json,review_json,created_at FROM derivations ORDER BY rowid;
6
+ DROP TABLE derivations;
7
+ ALTER TABLE derivations_v7 RENAME TO derivations;
8
+ CREATE INDEX derivations_revision ON derivations(revision_id,kind,input_hash);
9
+ CREATE INDEX derivations_item ON derivations(item_id,kind,created_at);
10
+ `;