@4fu/arbor-notes-sqlite 2026.1106.0 → 2026.1107.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/README.md +14 -6
- package/index.d.ts +3 -1
- package/index.js +3 -1
- package/notes.d.ts +71 -2
- package/notes.js +298 -54
- package/package.json +17 -5
- package/schema.d.ts +8 -3
- package/schema.js +60 -10
- package/search.d.ts +49 -16
- package/search.js +109 -32
- package/semantic.d.ts +29 -0
- package/semantic.js +57 -0
package/README.md
CHANGED
|
@@ -27,9 +27,11 @@ everything that takes judgment, such as git, merges and conflicts, to the agent.
|
|
|
27
27
|
guarantees what an agent cannot guarantee for itself - the record, isolation,
|
|
28
28
|
exactly-once effects, the checks, dispatch. Everything that takes judgment,
|
|
29
29
|
such as git, merges and conflicts, is the agent's.
|
|
30
|
-
- **A
|
|
31
|
-
|
|
32
|
-
|
|
30
|
+
- **A shared workspace with per-window checkouts.** The default workspace is
|
|
31
|
+
ovnfs: one shared folder every window reads, and a window opens its own
|
|
32
|
+
copy-on-write checkout of the project it needs, works there and merges back
|
|
33
|
+
with `apply`. It falls back to a plain directory per window when ovnfs is not
|
|
34
|
+
installed. It is not a sandbox: ovnfs isolates files, not processes.
|
|
33
35
|
- **A self-changing environment.** The model adds and changes skills, MCP
|
|
34
36
|
servers and resident services itself; a `reload` validates the change and
|
|
35
37
|
broadcasts it to every live window as a delta of names and descriptions.
|
|
@@ -54,7 +56,9 @@ npm install @4fu/arbor-core @4fu/arbor-journal-sqlite @4fu/arbor-notes-sqlite \
|
|
|
54
56
|
```
|
|
55
57
|
|
|
56
58
|
`@4fu/arbor-plugin-http` is optional; it is the example plugin below.
|
|
57
|
-
`@4fu/arbor-segmenter-jieba` is an optional peer of the
|
|
59
|
+
`@4fu/arbor-segmenter-jieba` is an optional peer of the lexical package, and
|
|
60
|
+
`@4fu/arbor-search-zvec` an optional peer of the journal: absent either one, the
|
|
61
|
+
journal searches lexically and nothing else changes.
|
|
58
62
|
|
|
59
63
|
## A minimal host
|
|
60
64
|
|
|
@@ -118,7 +122,10 @@ window gets, and how a continuation keeps working in it, are in
|
|
|
118
122
|
| [`@4fu/arbor-journal-sqlite`](packages/journal-sqlite) | the default `Journal` and `BlobStore` on SQLite |
|
|
119
123
|
| [`@4fu/arbor-notes-sqlite`](packages/notes-sqlite) | the default `NoteStore` on SQLite, beside the journal |
|
|
120
124
|
| [`@4fu/arbor-segmenter-jieba`](packages/segmenter-jieba) | the default `Segmenter`: jieba's dictionary segmentation for the word index, optional |
|
|
121
|
-
| [`@4fu/arbor-
|
|
125
|
+
| [`@4fu/arbor-lexical`](packages/lexical) | the lexical search the journal and notes share: word segmentation, the FTS5 helpers and the merge order |
|
|
126
|
+
| [`@4fu/arbor-search-zvec`](packages/search-zvec) | the optional semantic channel: event text embedded and searched by meaning with zvec |
|
|
127
|
+
| [`@4fu/arbor-workspace-dir`](packages/workspace-dir) | the fallback `Workspace`: one directory per window, with a continuation working in its predecessor's directory in place |
|
|
128
|
+
| [`@4fu/arbor-workspace-ovnfs`](packages/workspace-ovnfs) | the default `Workspace` when ovnfs is installed: one shared folder, per-window copy-on-write checkouts the model opens and applies back |
|
|
122
129
|
| [`@4fu/arbor-environment-dir`](packages/environment-dir) | the default `Environment`: one `/env` directory, a revision per item, validation and deltas |
|
|
123
130
|
| [`@4fu/arbor-supervisor-process`](packages/supervisor-process) | the default `Supervisor`: one host process per service, restart with backoff, logs |
|
|
124
131
|
| [`@4fu/arbor-judge-jev`](packages/judge-jev) | the default `Judge`: Jev, TypeSafe AI's System One classifier, answering the dispatch decisions |
|
|
@@ -151,7 +158,7 @@ deployment can see what it would be taking on before it adopts one.
|
|
|
151
158
|
| [docs/coordination.md](docs/coordination.md) | the board, expectations, deliveries and broadcasts, staying current, effects |
|
|
152
159
|
| [docs/environment.md](docs/environment.md) | skills, MCP servers and services: using, changing, reload, built-in skills, deltas |
|
|
153
160
|
| [docs/memory.md](docs/memory.md) | notes, the active set, anchors, links |
|
|
154
|
-
| [docs/workspaces.md](docs/workspaces.md) | per-window
|
|
161
|
+
| [docs/workspaces.md](docs/workspaces.md) | the shared folder and per-window checkouts: isolation, continuity, what the agent does |
|
|
155
162
|
| [docs/tools.md](docs/tools.md) | direct tools and code mode modules |
|
|
156
163
|
| [docs/prompting.md](docs/prompting.md) | how every model-facing text is written: layers, notes, deliveries, errors, judge questions, terms |
|
|
157
164
|
| [docs/release.md](docs/release.md) | calendar versioning, the manual release steps, how a package is published |
|
|
@@ -168,6 +175,7 @@ tarball would contain ([docs/release.md](docs/release.md)).
|
|
|
168
175
|
| `packages/errors` | the error shape every refusal uses: the rule and the next action |
|
|
169
176
|
| `packages/journal-sqlite` | the default `Journal` and `BlobStore` on SQLite |
|
|
170
177
|
| `packages/segmenter-jieba` | the default `Segmenter`: jieba's dictionary segmentation for the word index, optional |
|
|
178
|
+
| `packages/lexical` | the lexical search the journal and notes share: word segmentation, the FTS5 helpers and the merge order |
|
|
171
179
|
| `packages/notes-sqlite` | the default `NoteStore` on SQLite, beside the journal |
|
|
172
180
|
| `packages/judge-jev` | the default `Judge`: Jev, TypeSafe AI's System One classifier, answering the dispatch decisions |
|
|
173
181
|
| `packages/workspace-dir` | the default `Workspace`: one directory per window, with a continuation working in its predecessor's directory in place, and the lookup of another window's directory it contributes to the catalog |
|
package/index.d.ts
CHANGED
|
@@ -4,10 +4,12 @@
|
|
|
4
4
|
export { SqliteNoteStore, SUMMARY_LIMIT, DEFAULT_BODY_LIMIT, MAX_BODY_LIMIT } from "./notes.ts";
|
|
5
5
|
export { DEFAULT_MIN_INTERVAL_MS, DEFAULT_ACTIVE_BUDGET, DEFAULT_JUDGE_SHARE, DEFAULT_HALF_LIFE_MS, deriveSummary, } from "./notes.ts";
|
|
6
6
|
export type { SqliteNoteStoreOptions } from "./notes.ts";
|
|
7
|
-
export { SCHEMA_VERSION, FTS_TABLE, createSchema } from "./schema.ts";
|
|
7
|
+
export { SCHEMA_VERSION, FTS_TABLE, WORD_TABLE, createSchema } from "./schema.ts";
|
|
8
8
|
export { noteIdOf, noteRefOf, parseNoteId, parseNoteRef, noteRefForms, storedWindowRef, storedEventRef, } from "./refs.ts";
|
|
9
|
+
export { fallbackSegmenter, resolveSegmenter, indexText } from "@4fu/arbor-lexical";
|
|
9
10
|
export { USE_WEIGHTS, MAX_CREDIT_HOPS, decay, estimateTokens, linkedCredit, loadCredit, loadEdges, pickByBudget, applyJudgeShare, scoreNotes, } from "./score.ts";
|
|
10
11
|
export type { ScoredNote } from "./score.ts";
|
|
11
12
|
export { MIN_TRIGRAM_CHARS, substringClause, holdingIds, escapeLike, likePattern, quoteFts, } from "./search.ts";
|
|
12
13
|
export type { SubstringClause } from "./search.ts";
|
|
13
14
|
export { bodyDiff } from "./diff.ts";
|
|
15
|
+
export { resolveSemanticIndex } from "./semantic.ts";
|
package/index.js
CHANGED
|
@@ -3,8 +3,10 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export { SqliteNoteStore, SUMMARY_LIMIT, DEFAULT_BODY_LIMIT, MAX_BODY_LIMIT } from "./notes.js";
|
|
5
5
|
export { DEFAULT_MIN_INTERVAL_MS, DEFAULT_ACTIVE_BUDGET, DEFAULT_JUDGE_SHARE, DEFAULT_HALF_LIFE_MS, deriveSummary, } from "./notes.js";
|
|
6
|
-
export { SCHEMA_VERSION, FTS_TABLE, createSchema } from "./schema.js";
|
|
6
|
+
export { SCHEMA_VERSION, FTS_TABLE, WORD_TABLE, createSchema } from "./schema.js";
|
|
7
7
|
export { noteIdOf, noteRefOf, parseNoteId, parseNoteRef, noteRefForms, storedWindowRef, storedEventRef, } from "./refs.js";
|
|
8
|
+
export { fallbackSegmenter, resolveSegmenter, indexText } from "@4fu/arbor-lexical";
|
|
8
9
|
export { USE_WEIGHTS, MAX_CREDIT_HOPS, decay, estimateTokens, linkedCredit, loadCredit, loadEdges, pickByBudget, applyJudgeShare, scoreNotes, } from "./score.js";
|
|
9
10
|
export { MIN_TRIGRAM_CHARS, substringClause, holdingIds, escapeLike, likePattern, quoteFts, } from "./search.js";
|
|
10
11
|
export { bodyDiff } from "./diff.js";
|
|
12
|
+
export { resolveSemanticIndex } from "./semantic.js";
|
package/notes.d.ts
CHANGED
|
@@ -15,7 +15,11 @@
|
|
|
15
15
|
* count and token budget, and frozen with the epoch's version, so every
|
|
16
16
|
* window opened in one epoch has the same prefix and later windows see the
|
|
17
17
|
* changes as a delta;
|
|
18
|
-
* - every write recorded in the journal as a `note_revision` event
|
|
18
|
+
* - every write recorded in the journal as a `note_revision` event;
|
|
19
|
+
* - lexical search: a word index with BM25, a trigram index for exact
|
|
20
|
+
* substrings, and a `LIKE` scan for one- and two-character queries, merged
|
|
21
|
+
* with exact substring hits first - and a semantic channel, note text embedded
|
|
22
|
+
* and searched by meaning, when `@4fu/arbor-search-zvec` is installed.
|
|
19
23
|
*
|
|
20
24
|
* It is synchronous inside: one `DatabaseSync`, one transaction per write, so
|
|
21
25
|
* the revision a write checks and the revision it writes are one atomic step.
|
|
@@ -23,7 +27,7 @@
|
|
|
23
27
|
* database and the journal. The interface is asynchronous because a `NoteStore`
|
|
24
28
|
* may live behind a process boundary.
|
|
25
29
|
*/
|
|
26
|
-
import type { ActiveCandidate, ActiveSetBudget, Journal, Note, NoteBody, NoteContext, NoteDelta, NoteQuery, NoteRef, NoteRevision, NoteSummary, NoteUseInput, NoteWriteInput, NotesSnapshot, NoteStore, Page, SnapshotOptions } from "@4fu/arbor-interfaces";
|
|
30
|
+
import type { ActiveCandidate, ActiveSetBudget, Journal, Note, NoteBody, NoteContext, NoteDelta, NoteQuery, NoteRef, NoteRevision, NoteSummary, NoteUseInput, NoteWriteInput, NotesSnapshot, NoteStore, Page, Segmenter, SemanticIndex, SnapshotOptions } from "@4fu/arbor-interfaces";
|
|
27
31
|
/** A summary is at most this many characters (docs/memory.md). */
|
|
28
32
|
export declare const SUMMARY_LIMIT = 200;
|
|
29
33
|
/** How many characters of a body one read returns. */
|
|
@@ -46,6 +50,22 @@ export interface SqliteNoteStoreOptions {
|
|
|
46
50
|
now?: () => number;
|
|
47
51
|
/** Usage loses half of its weight this often. 24 hours. */
|
|
48
52
|
usageHalfLifeMs?: number;
|
|
53
|
+
/** Word segmentation for the word index. Defaults to jieba, or the approximation. */
|
|
54
|
+
segmenter?: Segmenter;
|
|
55
|
+
/**
|
|
56
|
+
* The semantic channel: note text embedded and searched by meaning
|
|
57
|
+
* (docs/memory.md#search). Omitted, the zvec channel is used when
|
|
58
|
+
* `@4fu/arbor-search-zvec` is installed and its store opens, and there is no
|
|
59
|
+
* channel otherwise. `null` forces lexical search even when the package is
|
|
60
|
+
* installed.
|
|
61
|
+
*/
|
|
62
|
+
semantic?: SemanticIndex | null;
|
|
63
|
+
/**
|
|
64
|
+
* One fixed string from the deployment's text table, so this implementation
|
|
65
|
+
* writes none of its own (docs/prompting.md#language-and-the-text-table).
|
|
66
|
+
* Defaults to the framework's own Chinese table.
|
|
67
|
+
*/
|
|
68
|
+
text?: (key: string, args?: Record<string, string | number>) => string;
|
|
49
69
|
}
|
|
50
70
|
/** The opening of a body, when a write carries no summary of its own. */
|
|
51
71
|
export declare function deriveSummary(body: string): string;
|
|
@@ -54,9 +74,17 @@ export declare class SqliteNoteStore implements NoteStore {
|
|
|
54
74
|
private readonly db;
|
|
55
75
|
private readonly journal;
|
|
56
76
|
private readonly clock;
|
|
77
|
+
private readonly segmenter;
|
|
57
78
|
private readonly halfLifeMs;
|
|
79
|
+
private readonly semantic;
|
|
80
|
+
/** The temporary directory an in-memory store's vector collection lives in. */
|
|
81
|
+
private readonly ownedVectorDirectory;
|
|
82
|
+
/** The table every refusal this store raises is written from. */
|
|
83
|
+
private readonly text;
|
|
58
84
|
/** One write at a time: a write spans the notes database and the journal. */
|
|
59
85
|
private writeQueue;
|
|
86
|
+
private semanticReady;
|
|
87
|
+
private semanticCaughtUp;
|
|
60
88
|
private closed;
|
|
61
89
|
constructor(options: SqliteNoteStoreOptions);
|
|
62
90
|
write(input: NoteWriteInput): Promise<Note>;
|
|
@@ -92,7 +120,44 @@ export declare class SqliteNoteStore implements NoteStore {
|
|
|
92
120
|
}): Promise<Page<ActiveCandidate>>;
|
|
93
121
|
recordUse(input: NoteUseInput): Promise<void>;
|
|
94
122
|
close(): Promise<void>;
|
|
123
|
+
/**
|
|
124
|
+
* Index the notes the semantic channel does not hold yet, and say how many.
|
|
125
|
+
*
|
|
126
|
+
* A restart over an existing database, a channel that was not ready when the
|
|
127
|
+
* notes were written, and a write whose indexing failed all leave notes to
|
|
128
|
+
* catch up on. A channel that is not ready yet holds what it was given and
|
|
129
|
+
* indexes that itself, so this answers 0 until it is.
|
|
130
|
+
*/
|
|
131
|
+
catchUpSemantic(): Promise<number>;
|
|
95
132
|
private ensureOpen;
|
|
133
|
+
/**
|
|
134
|
+
* Hand one note's text to the semantic channel, when there is one.
|
|
135
|
+
*
|
|
136
|
+
* The note is embedded as one text: its title, its summary and its body, in
|
|
137
|
+
* the order the lexical index weights them. The vector channel holds one
|
|
138
|
+
* vector per note and cannot weight a note's parts against each other the way
|
|
139
|
+
* BM25 weights the three columns, so the title - the note's name - leads the
|
|
140
|
+
* text and the body follows it, and a query that means either reaches the
|
|
141
|
+
* note (docs/memory.md#search).
|
|
142
|
+
*/
|
|
143
|
+
private addToSemantic;
|
|
144
|
+
/** Take one note's text back out of the semantic channel, as a delete does. */
|
|
145
|
+
private removeFromSemantic;
|
|
146
|
+
/**
|
|
147
|
+
* The semantic channel's candidates, as ranks in the order it gave them.
|
|
148
|
+
*
|
|
149
|
+
* An absent channel, one that is not ready yet, and one that refuses all
|
|
150
|
+
* answer with nothing, and the search is exactly the lexical one. The note
|
|
151
|
+
* read's own filters decide which candidates count, so a channel that filters
|
|
152
|
+
* less than the store does still answers the read it was asked for.
|
|
153
|
+
*/
|
|
154
|
+
private semanticRanks;
|
|
155
|
+
/**
|
|
156
|
+
* The notes among `ids` the read's own filters allow, in the order given. A
|
|
157
|
+
* deleted note is not among them, so a vector that outlived its note never
|
|
158
|
+
* reaches a page.
|
|
159
|
+
*/
|
|
160
|
+
private allowedNoteIds;
|
|
96
161
|
private noteRow;
|
|
97
162
|
private revisionRow;
|
|
98
163
|
private latestRevisionRow;
|
|
@@ -100,6 +165,8 @@ export declare class SqliteNoteStore implements NoteStore {
|
|
|
100
165
|
/** The current summary and revision of several notes, in one read. */
|
|
101
166
|
private noteStatesFor;
|
|
102
167
|
private summaryPageBySql;
|
|
168
|
+
/** One page of an ordered id list, in the order the list gives. */
|
|
169
|
+
private summaryPage;
|
|
103
170
|
private summaryPageByIds;
|
|
104
171
|
/**
|
|
105
172
|
* The notes about a target, or linked to a ref: the notes the events on that
|
|
@@ -121,6 +188,8 @@ export declare class SqliteNoteStore implements NoteStore {
|
|
|
121
188
|
* the changes since reach it as a delta.
|
|
122
189
|
*/
|
|
123
190
|
private epochAt;
|
|
191
|
+
/** What the active notes of one epoch cost, as the epoch froze them. */
|
|
192
|
+
private epochTokens;
|
|
124
193
|
/** The active notes one epoch froze, by summary, sorted by id. */
|
|
125
194
|
private epochSummaries;
|
|
126
195
|
}
|