@theokit/sdk 4.61.0 → 4.63.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/CHANGELOG.md +139 -0
- package/dist/{agent-P42GA6RM.js → agent-536OOESZ.js} +8 -6
- package/dist/{agent-P42GA6RM.js.map → agent-536OOESZ.js.map} +1 -1
- package/dist/{agent-M53C3PAA.cjs → agent-5KYMG63I.cjs} +9 -7
- package/dist/{agent-M53C3PAA.cjs.map → agent-5KYMG63I.cjs.map} +1 -1
- package/dist/{agent-DIu6FooJ.d.ts → agent-BFno7Sfn.d.ts} +36 -21
- package/dist/{agent-D3Xr_-6Z.d.cts → agent-X0DKA943.d.cts} +36 -21
- package/dist/chunk-A4RAL2ER.cjs +51 -0
- package/dist/chunk-A4RAL2ER.cjs.map +1 -0
- package/dist/chunk-A4VHSE56.cjs +21 -0
- package/dist/chunk-A4VHSE56.cjs.map +1 -0
- package/dist/{chunk-CQ2TQ32Y.js → chunk-AMFXSENK.js} +3 -48
- package/dist/chunk-AMFXSENK.js.map +1 -0
- package/dist/chunk-C7QPML3L.js +831 -0
- package/dist/chunk-C7QPML3L.js.map +1 -0
- package/dist/{chunk-5IT6DUOO.cjs → chunk-CASUEY42.cjs} +4 -4
- package/dist/{chunk-5IT6DUOO.cjs.map → chunk-CASUEY42.cjs.map} +1 -1
- package/dist/{chunk-GQZKDGZM.js → chunk-CHKH5452.js} +90 -70
- package/dist/chunk-CHKH5452.js.map +1 -0
- package/dist/chunk-EHJEZAOO.cjs +864 -0
- package/dist/chunk-EHJEZAOO.cjs.map +1 -0
- package/dist/chunk-IRCJ7EHV.cjs +201 -0
- package/dist/chunk-IRCJ7EHV.cjs.map +1 -0
- package/dist/chunk-LW7G5DYW.js +19 -0
- package/dist/chunk-LW7G5DYW.js.map +1 -0
- package/dist/{chunk-532CSLYU.js → chunk-MG3SOM3M.js} +3 -3
- package/dist/{chunk-532CSLYU.js.map → chunk-MG3SOM3M.js.map} +1 -1
- package/dist/{chunk-R7YIVL3K.cjs → chunk-NLXOGBRJ.cjs} +98 -78
- package/dist/chunk-NLXOGBRJ.cjs.map +1 -0
- package/dist/{chunk-SKXBJ2NU.cjs → chunk-NXH4GPAQ.cjs} +2 -49
- package/dist/chunk-NXH4GPAQ.cjs.map +1 -0
- package/dist/chunk-R6TA2DRJ.js +194 -0
- package/dist/chunk-R6TA2DRJ.js.map +1 -0
- package/dist/{chunk-6OBIWHDR.cjs → chunk-UUP3MUZ6.cjs} +20 -361
- package/dist/chunk-UUP3MUZ6.cjs.map +1 -0
- package/dist/chunk-WE22OXQA.js +48 -0
- package/dist/chunk-WE22OXQA.js.map +1 -0
- package/dist/{chunk-QEKI3YKI.js → chunk-YTR3RBUZ.js} +13 -348
- package/dist/chunk-YTR3RBUZ.js.map +1 -0
- package/dist/{cron-BcWmzWzT.d.cts → cron-BuiRPrvt.d.cts} +1 -1
- package/dist/{cron-De6hzWCF.d.ts → cron-C2SIi31n.d.ts} +1 -1
- package/dist/cron.cjs +8 -6
- package/dist/cron.d.cts +2 -2
- package/dist/cron.d.ts +2 -2
- package/dist/cron.js +7 -5
- package/dist/eval.cjs +7 -5
- package/dist/eval.cjs.map +1 -1
- package/dist/eval.js +6 -4
- package/dist/eval.js.map +1 -1
- package/dist/{index-manager-RHPFVFSC.cjs → index-manager-6TEEIWM7.cjs} +7 -6
- package/dist/{index-manager-RHPFVFSC.cjs.map → index-manager-6TEEIWM7.cjs.map} +1 -1
- package/dist/{index-manager-AHAYJ33H.js → index-manager-ACEQG3IJ.js} +6 -5
- package/dist/{index-manager-AHAYJ33H.js.map → index-manager-ACEQG3IJ.js.map} +1 -1
- package/dist/index.cjs +35 -208
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +8 -3
- package/dist/index.d.ts +8 -3
- package/dist/index.js +22 -195
- package/dist/index.js.map +1 -1
- package/dist/internal/memory/active-memory.d.ts +8 -1
- package/dist/internal/memory/dreaming/diary.d.ts +39 -2
- package/dist/internal/memory/index-db.d.ts +44 -1
- package/dist/internal/memory/index-manager-contract.d.ts +10 -0
- package/dist/internal/memory/index-manager-helpers.d.ts +20 -1
- package/dist/internal/memory/index-manager.d.ts +1 -1
- package/dist/internal/memory/lance-index.d.ts +15 -1
- package/dist/internal/memory/migrate-sqlite-to-lance.d.ts +43 -0
- package/dist/internal/memory/storage/index.cjs +95 -12
- package/dist/internal/memory/storage/index.d.cts +11 -1
- package/dist/internal/memory/storage/index.d.ts +11 -1
- package/dist/internal/memory/storage/index.js +4 -1
- package/dist/internal/memory/storage/markdown-store.d.cts +13 -42
- package/dist/internal/memory/storage/markdown-store.d.ts +13 -42
- package/dist/internal/memory/storage/memory-root.d.cts +93 -0
- package/dist/internal/memory/storage/memory-root.d.ts +93 -0
- package/dist/internal/memory/storage/session-loader.d.cts +35 -1
- package/dist/internal/memory/storage/session-loader.d.ts +35 -1
- package/dist/internal/memory/storage/session-summary-writer.d.cts +50 -2
- package/dist/internal/memory/storage/session-summary-writer.d.ts +50 -2
- package/dist/internal/memory/storage/transcript-store.d.cts +38 -1
- package/dist/internal/memory/storage/transcript-store.d.ts +38 -1
- package/dist/internal/memory/storage/wiki-loader.d.cts +27 -2
- package/dist/internal/memory/storage/wiki-loader.d.ts +27 -2
- package/dist/internal/memory/tools.d.ts +8 -1
- package/dist/internal/memory/types.d.ts +5 -0
- package/dist/internal/persistence/index.cjs +12 -11
- package/dist/internal/persistence/index.cjs.map +1 -1
- package/dist/internal/persistence/index.js +2 -1
- package/dist/internal/persistence/index.js.map +1 -1
- package/dist/internal/persistence/session-dir.d.cts +0 -15
- package/dist/internal/persistence/session-dir.d.ts +0 -15
- package/dist/internal/runtime/memory/memory-store.d.ts +2 -2
- package/dist/persistence.cjs +9 -8
- package/dist/persistence.cjs.map +1 -1
- package/dist/persistence.js +2 -1
- package/dist/persistence.js.map +1 -1
- package/dist/subagents-loader.d.cts +1 -1
- package/dist/subagents-loader.d.ts +1 -1
- package/dist/types/agent.d.ts +11 -0
- package/dist/types/memory-provider.d.ts +8 -0
- package/docs/error-codes.md +10 -9
- package/docs/harness-capability-map.md +57 -17
- package/docs/memory-decisions.md +44 -7
- package/package.json +1 -1
- package/dist/chunk-2UCFUSPW.cjs +0 -468
- package/dist/chunk-2UCFUSPW.cjs.map +0 -1
- package/dist/chunk-6OBIWHDR.cjs.map +0 -1
- package/dist/chunk-CQ2TQ32Y.js.map +0 -1
- package/dist/chunk-GQZKDGZM.js.map +0 -1
- package/dist/chunk-QEKI3YKI.js.map +0 -1
- package/dist/chunk-R7YIVL3K.cjs.map +0 -1
- package/dist/chunk-SKXBJ2NU.cjs.map +0 -1
- package/dist/chunk-WCLDJSMY.js +0 -456
- package/dist/chunk-WCLDJSMY.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,144 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 4.63.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 6410e27: **BREAKING (narrow):** `local.sessionDir` no longer decides where memory is written. Use the new
|
|
8
|
+
`memory.directory` option. Facts already recorded are not moved and stay readable — the Claude Code
|
|
9
|
+
store is a read root unconditionally — so a consumer who relied on the old coupling gets their new
|
|
10
|
+
facts in the project store until they set `memory.directory`.
|
|
11
|
+
|
|
12
|
+
Memory now has ONE answer to "where does this agent's memory live?", and every path derives from it.
|
|
13
|
+
|
|
14
|
+
Fourteen places computed a memory path from `cwd`, and one of them computed a different one.
|
|
15
|
+
`appendFact` relocated when `local.sessionDir` was set; the indexer, the `memory_get` path guard,
|
|
16
|
+
`MEMORY.md`, `sessions/`, `notes/`, `wiki/`, the dream diary and the index database did not — the
|
|
17
|
+
last of those spelled the default layout out again as a string literal, so no search for the shared
|
|
18
|
+
helper would have found it. A relocated fact was therefore written, never indexed, unreadable by the
|
|
19
|
+
tool whose job is reading memory, and shadowed by a second `MEMORY.md` in the store it had left.
|
|
20
|
+
|
|
21
|
+
- **New `memory.directory`.** Absolute or `~/`-prefixed. Point it at
|
|
22
|
+
`~/.claude/projects/<encoded-cwd>/memory` to write where the Claude Code CLI reads. A relative
|
|
23
|
+
value is refused with `invalid_memory_directory` rather than resolved: the workspace and the
|
|
24
|
+
process cwd are both plausible bases, and picking one silently is how a store ends up split
|
|
25
|
+
across both.
|
|
26
|
+
- **One resolver.** `resolveMemoryRoot` is the only producer of a root, and it returns a branded
|
|
27
|
+
`MemoryRoot` that every path helper now requires. The brand is STRUCTURAL rather than a
|
|
28
|
+
`unique symbol`: the d.ts bundler inlines a `unique symbol` declaration into every package that
|
|
29
|
+
re-exports it, so `@theokit/sdk-memory` ended up with a `MemoryRoot` its own compiler rejected
|
|
30
|
+
against the SDK's, on values that were the same string. A structural tag refuses a bare `string`
|
|
31
|
+
exactly as well and survives the package boundary. A cwd and a root are both strings, so the brand
|
|
32
|
+
is what makes "every path derives from one resolution" a compiler rule instead of a convention —
|
|
33
|
+
and it is what surfaced the three call sites that were silently reading the wrong directory.
|
|
34
|
+
- **`local.sessionDir` means one thing again:** where session transcripts go.
|
|
35
|
+
- **Unchanged:** WRITE ONE, READ ALL. Recall still covers the configured root, the project store and
|
|
36
|
+
the CLI's store, so relocating the write orphans nothing.
|
|
37
|
+
|
|
38
|
+
Everything under the root follows it: `MEMORY.md`, the per-memory files, `notes/`, `sessions/`,
|
|
39
|
+
`wiki/`, `transcripts/`, `dream-diary.md`, `.index/memory.sqlite` and the Lance store. Two of those
|
|
40
|
+
were found by the brand rather than by reading — `index-db.ts` and `lance-index.ts` each spelled
|
|
41
|
+
`.theokit/memory` out again as a string literal, so no search for the shared helper would have
|
|
42
|
+
reached them.
|
|
43
|
+
|
|
44
|
+
`Memory.runDreamingSweep` and the SQLite→Lance migration take a `directory` for the same reason: a
|
|
45
|
+
sweep that consolidated notes into the default store while the facts lived elsewhere would be the
|
|
46
|
+
same defect one function over.
|
|
47
|
+
|
|
48
|
+
`internal/memory/storage` (semver-exempt sub-path) drops `memoryDir` and `memoryWriteDir` and gains
|
|
49
|
+
`resolveMemoryRoot`, `projectMemoryDir`, `memoryReadRoots`, `asMemoryRoot` and `MemoryRoot`.
|
|
50
|
+
`RecordSessionSummaryArgs` gains a required `memoryRoot`, supplied by the kernel — an implementor
|
|
51
|
+
consumes those args and never constructs them.
|
|
52
|
+
|
|
53
|
+
**The `MEMORY.md` budget is a statement about the interop partner, and only that.** The Claude Code
|
|
54
|
+
CLI loads the first 200 lines / 25 KB of an index into every session and drops the rest in silence.
|
|
55
|
+
This SDK never loads the index at all — the `<memory>` block is built from the per-memory FILES,
|
|
56
|
+
ranked and capped — so our recall does not degrade as the index grows. `indexBudgetWarning` therefore
|
|
57
|
+
speaks ONLY when `memory.directory` points at the store the CLI reads, says what is true (the CLI
|
|
58
|
+
drops entries) rather than what is not (memory stops working), and never throws: the fact file and
|
|
59
|
+
the index rewrite are one atomic operation, so refusing the second would lose the first.
|
|
60
|
+
|
|
61
|
+
**Two things deliberately do NOT follow the option.** The index DATABASE stays in the project store
|
|
62
|
+
even when the facts move: `memory.directory` may name the directory the Claude Code CLI manages, and
|
|
63
|
+
that CLI has no index format — a binary artefact it does not understand does not belong in a
|
|
64
|
+
directory it owns. What gets INDEXED is still the configured root; only the file's location is held
|
|
65
|
+
back (`docs/memory-decisions.md` § 1). And one path: `legacyMemoryJsonPath`, which locates the
|
|
66
|
+
pre-#389 JSON store. That store was written before the option existed, so pointing it at a
|
|
67
|
+
configured root would look for a legacy file where a legacy file cannot be.
|
|
68
|
+
|
|
69
|
+
## 4.62.0
|
|
70
|
+
|
|
71
|
+
### Minor Changes
|
|
72
|
+
|
|
73
|
+
- 36bb21f: Memory recall is scored with BM25 instead of Jaccard, and rank fusion is damped for stores of
|
|
74
|
+
tens rather than hundreds.
|
|
75
|
+
|
|
76
|
+
**What changes for you:** which memories are selected when several are plausible. The store, the
|
|
77
|
+
budget and the API are unchanged; the ordering is not.
|
|
78
|
+
|
|
79
|
+
Measured on LongMemEval-S — 500 questions, 54 sessions each — through a public eval harness, with
|
|
80
|
+
its tokenised-substring `grep` adapter as the floor:
|
|
81
|
+
|
|
82
|
+
| | hit@5 | P@5 | R@5 | p50 |
|
|
83
|
+
| ----------------------- | --------- | --------- | --------- | -------- |
|
|
84
|
+
| Jaccard (4.61.0) | 80.0% | 0.236 | 0.670 | 205ms |
|
|
85
|
+
| `grep` (floor) | 89.0% | 0.295 | 0.807 | 2ms |
|
|
86
|
+
| **BM25 (this release)** | **96.8%** | **0.329** | **0.904** | **20ms** |
|
|
87
|
+
|
|
88
|
+
Jaccard lost to a naive substring match. The mechanism was isolated before it was fixed: on a
|
|
89
|
+
preference query, the term that discriminates appeared in 2 of 15 documents while a noise term
|
|
90
|
+
appeared in 14 — and Jaccard weighted them identically. Almost every document scored above zero,
|
|
91
|
+
fusion flattened what ordering remained, and recency decided a relevance question. IDF is the
|
|
92
|
+
whole fix, and the gain concentrates where that predicts:
|
|
93
|
+
|
|
94
|
+
```
|
|
95
|
+
single-session-preference 46.7% -> 86.7% (+40.0)
|
|
96
|
+
single-session-assistant 89.3% -> 100.0% (+10.7)
|
|
97
|
+
temporal-reasoning 85.7% -> 96.2% (+10.5)
|
|
98
|
+
multi-session 94.7% -> 96.2% (+1.5)
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Also **10x faster**: the previous implementation called its scoring function from inside sort
|
|
102
|
+
comparators, re-tokenising every fact O(n log n) times per query.
|
|
103
|
+
|
|
104
|
+
Rank fusion damping moves from k = 60 to k = 5. Swept rather than chosen — the 500-question corpus
|
|
105
|
+
is insensitive to k once terms are IDF-weighted, while a 15-session corpus goes from 14/15 to
|
|
106
|
+
15/15. The sweep cannot separate 5 from 1, so the tie is broken on the principle that at k = 1
|
|
107
|
+
damping is nearly gone and fusion stops fusing.
|
|
108
|
+
|
|
109
|
+
**This improves the reliability of memory poisoning as well as of recall**, and the two cannot be
|
|
110
|
+
separated: the property that makes a planted entry work is the property that makes a real one
|
|
111
|
+
useful. See the accompanying patch note for the re-measured figures.
|
|
112
|
+
|
|
113
|
+
### Patch Changes
|
|
114
|
+
|
|
115
|
+
- Corrects a security figure published with 4.61.0, and adds the rule that produced the error.
|
|
116
|
+
|
|
117
|
+
The 4.61.0 notes said a planted memory entry made the agent perform the action it described in
|
|
118
|
+
**2 of 6 runs**. Re-measured against the published 4.61.0 itself, it is **6 of 6**. Registering
|
|
119
|
+
the permission engine still blocks it — 6 of 6, with zero errors.
|
|
120
|
+
|
|
121
|
+
The old figure was not a smaller version of the same risk; it was a measurement of a different
|
|
122
|
+
thing. It was taken against a retrieval path that did not recall the planted entry at all: on
|
|
123
|
+
4.60.0 the agent answered "Done." and never saw it, while on 4.61.0 it recites the entry
|
|
124
|
+
verbatim. Nothing about the attack changed between those runs — the recall path did.
|
|
125
|
+
|
|
126
|
+
**A poisoning rate measured against a retrieval path that does not recall the plant is a
|
|
127
|
+
measurement of how often the attack reached the model, not of how often the model resisted it.**
|
|
128
|
+
Any such figure has to record whether the plant was recalled, or it cannot be compared across
|
|
129
|
+
versions.
|
|
130
|
+
|
|
131
|
+
The consequence for anyone depending on this: improving recall is a change to the threat model,
|
|
132
|
+
not something orthogonal to it. The property that makes a planted memory work is the property
|
|
133
|
+
that makes a real one useful. **If anything other than your agent's own deliberate writes can
|
|
134
|
+
reach the memory directory, register the permission engine.**
|
|
135
|
+
|
|
136
|
+
Separately, the original proof constructed the engine as `new PermissionEngine({ rules: [] })`.
|
|
137
|
+
The constructor takes the rules positionally, so that was never a rule list, and nothing checked
|
|
138
|
+
because the script was JavaScript. A crash inside the engine and a gated call produce the same
|
|
139
|
+
observation. Re-run in TypeScript with `new PermissionEngine([])`, it gates: 6 of 6 blocked, 0
|
|
140
|
+
runs threw.
|
|
141
|
+
|
|
3
142
|
## 4.61.0
|
|
4
143
|
|
|
5
144
|
### Minor Changes
|
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
export { Agent } from './chunk-
|
|
1
|
+
export { Agent } from './chunk-CHKH5452.js';
|
|
2
2
|
import './chunk-SUKXXLWD.js';
|
|
3
3
|
import './chunk-YEXA3PGR.js';
|
|
4
4
|
import './chunk-K2BQQ445.js';
|
|
5
|
-
import './chunk-
|
|
5
|
+
import './chunk-YTR3RBUZ.js';
|
|
6
6
|
import './chunk-MZ2FGBLZ.js';
|
|
7
7
|
import './chunk-2SFBB54R.js';
|
|
8
8
|
import './chunk-XN7NOENA.js';
|
|
9
9
|
import './chunk-H73MEMQB.js';
|
|
10
|
-
import './chunk-
|
|
10
|
+
import './chunk-LW7G5DYW.js';
|
|
11
|
+
import './chunk-C7QPML3L.js';
|
|
11
12
|
import './chunk-YJDH3CKG.js';
|
|
12
13
|
import './chunk-YXGAW7BB.js';
|
|
13
14
|
import './chunk-3E77SX4H.js';
|
|
@@ -33,7 +34,8 @@ import './chunk-UC3HT2S4.js';
|
|
|
33
34
|
import './chunk-OC4NTGMN.js';
|
|
34
35
|
import './chunk-44JAAH4X.js';
|
|
35
36
|
import './chunk-FKMUFNQE.js';
|
|
36
|
-
import './chunk-
|
|
37
|
+
import './chunk-WE22OXQA.js';
|
|
38
|
+
import './chunk-AMFXSENK.js';
|
|
37
39
|
import './chunk-CV7XMBHP.js';
|
|
38
40
|
import './chunk-Q5EWJPRY.js';
|
|
39
41
|
import './chunk-RUDY2GTT.js';
|
|
@@ -51,5 +53,5 @@ import './chunk-3OR54XG4.js';
|
|
|
51
53
|
import './chunk-IDCKSLYH.js';
|
|
52
54
|
import './chunk-T7XEKOVW.js';
|
|
53
55
|
import './chunk-T7O6K6PX.js';
|
|
54
|
-
//# sourceMappingURL=agent-
|
|
55
|
-
//# sourceMappingURL=agent-
|
|
56
|
+
//# sourceMappingURL=agent-536OOESZ.js.map
|
|
57
|
+
//# sourceMappingURL=agent-536OOESZ.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":[],"names":[],"mappings":"","file":"agent-
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"agent-536OOESZ.js"}
|
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var chunkNLXOGBRJ_cjs = require('./chunk-NLXOGBRJ.cjs');
|
|
4
4
|
require('./chunk-NGESVVJN.cjs');
|
|
5
5
|
require('./chunk-2DG7KW4L.cjs');
|
|
6
6
|
require('./chunk-BUUUWQMB.cjs');
|
|
7
|
-
require('./chunk-
|
|
7
|
+
require('./chunk-UUP3MUZ6.cjs');
|
|
8
8
|
require('./chunk-P6H23T43.cjs');
|
|
9
9
|
require('./chunk-BV2MWEMV.cjs');
|
|
10
10
|
require('./chunk-EI2Q7SJ5.cjs');
|
|
11
11
|
require('./chunk-GHX4P3V2.cjs');
|
|
12
|
-
require('./chunk-
|
|
12
|
+
require('./chunk-A4VHSE56.cjs');
|
|
13
|
+
require('./chunk-EHJEZAOO.cjs');
|
|
13
14
|
require('./chunk-QI7WE46I.cjs');
|
|
14
15
|
require('./chunk-P5LCASTC.cjs');
|
|
15
16
|
require('./chunk-F3YZMOAU.cjs');
|
|
@@ -35,7 +36,8 @@ require('./chunk-ILCGLTSA.cjs');
|
|
|
35
36
|
require('./chunk-CQGYNZ3K.cjs');
|
|
36
37
|
require('./chunk-4I55V454.cjs');
|
|
37
38
|
require('./chunk-BUIK7GUA.cjs');
|
|
38
|
-
require('./chunk-
|
|
39
|
+
require('./chunk-A4RAL2ER.cjs');
|
|
40
|
+
require('./chunk-NXH4GPAQ.cjs');
|
|
39
41
|
require('./chunk-2ADR2GSO.cjs');
|
|
40
42
|
require('./chunk-ZF2LDKQQ.cjs');
|
|
41
43
|
require('./chunk-2C72DXQF.cjs');
|
|
@@ -58,7 +60,7 @@ require('./chunk-NUKRL3I6.cjs');
|
|
|
58
60
|
|
|
59
61
|
Object.defineProperty(exports, "Agent", {
|
|
60
62
|
enumerable: true,
|
|
61
|
-
get: function () { return
|
|
63
|
+
get: function () { return chunkNLXOGBRJ_cjs.Agent; }
|
|
62
64
|
});
|
|
63
|
-
//# sourceMappingURL=agent-
|
|
64
|
-
//# sourceMappingURL=agent-
|
|
65
|
+
//# sourceMappingURL=agent-5KYMG63I.cjs.map
|
|
66
|
+
//# sourceMappingURL=agent-5KYMG63I.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":[],"names":[],"mappings":"","file":"agent-
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"agent-5KYMG63I.cjs"}
|
|
@@ -295,28 +295,24 @@ interface BudgetTracker {
|
|
|
295
295
|
}
|
|
296
296
|
|
|
297
297
|
/**
|
|
298
|
-
*
|
|
299
|
-
*
|
|
300
|
-
*
|
|
301
|
-
*
|
|
302
|
-
*
|
|
303
|
-
*
|
|
304
|
-
*
|
|
305
|
-
*
|
|
306
|
-
*
|
|
307
|
-
*
|
|
308
|
-
*
|
|
309
|
-
*
|
|
310
|
-
*
|
|
311
|
-
*
|
|
312
|
-
* Layered model (mirrors Budget):
|
|
313
|
-
* - `MemoryAdapter` (in `types/memory-adapter.ts`) — LOW-LEVEL data port:
|
|
314
|
-
* write / recall / delete primitives.
|
|
315
|
-
* - `MemoryProvider` (THIS FILE) — HIGH-LEVEL lifecycle port: init,
|
|
316
|
-
* tool factories, active memory pass, embedding runtime selection.
|
|
317
|
-
*
|
|
318
|
-
* @public — surface-level interface; impls are internal-but-replaceable.
|
|
298
|
+
* A path that {@link resolveMemoryRoot} produced — the only thing the subsystem's path helpers
|
|
299
|
+
* accept.
|
|
300
|
+
*
|
|
301
|
+
* The brand is what makes "every path derives from one resolution" a compiler rule rather than a
|
|
302
|
+
* convention. Both a `cwd` and a root are strings, so without it the helpers would go on accepting
|
|
303
|
+
* either, and the next one added would have the same even chance of taking the wrong one that
|
|
304
|
+
* produced #463 in the first place. It costs one cast, at the one place a caller legitimately has a
|
|
305
|
+
* directory that did not come from here.
|
|
306
|
+
*
|
|
307
|
+
* STRUCTURAL, not a `unique symbol`. A `unique symbol` brand is identity-based, and the d.ts
|
|
308
|
+
* bundler inlines the declaration into each package that re-exports it — so `@theokit/sdk-memory`
|
|
309
|
+
* ended up with a `MemoryRoot` its own compiler considered incompatible with the SDK's, on values
|
|
310
|
+
* that were the same string. A structural tag refuses a bare `string` exactly as well and survives
|
|
311
|
+
* the package boundary, which is where this type has to work.
|
|
319
312
|
*/
|
|
313
|
+
type MemoryRoot = string & {
|
|
314
|
+
readonly __memoryRoot: "resolved";
|
|
315
|
+
};
|
|
320
316
|
|
|
321
317
|
/** Result of `MemoryProvider.runActivePass(...)` — what the kernel injects into the LLM call. */
|
|
322
318
|
interface ActiveMemoryPassResult {
|
|
@@ -360,6 +356,14 @@ interface RecordSessionSummaryArgs {
|
|
|
360
356
|
* compute the markdown file path.
|
|
361
357
|
*/
|
|
362
358
|
readonly cwd: string;
|
|
359
|
+
/**
|
|
360
|
+
* The memory root the kernel resolved for this agent, from `memory.directory` or the default.
|
|
361
|
+
*
|
|
362
|
+
* Supplied by the kernel, never constructed by an implementor — which is why it is required
|
|
363
|
+
* rather than optional. An implementation that recomputed it from `cwd` would write the summary
|
|
364
|
+
* into a different directory than the one the rest of the subsystem uses (#463).
|
|
365
|
+
*/
|
|
366
|
+
readonly memoryRoot: MemoryRoot;
|
|
363
367
|
/** Run id used as the filename key. */
|
|
364
368
|
readonly runId: string;
|
|
365
369
|
/** Agent identity for scope (foldering). */
|
|
@@ -1067,6 +1071,17 @@ interface MemorySettings {
|
|
|
1067
1071
|
userId?: string;
|
|
1068
1072
|
scope?: "agent" | "user" | "team";
|
|
1069
1073
|
storePath?: string;
|
|
1074
|
+
/**
|
|
1075
|
+
* Where this agent's memory lives. Default `<cwd>/.theokit/memory`.
|
|
1076
|
+
*
|
|
1077
|
+
* Must be an absolute path or start with `~/` — a relative value is refused rather than
|
|
1078
|
+
* resolved, because the two plausible bases put the store in two different places.
|
|
1079
|
+
*
|
|
1080
|
+
* Point it at `~/.claude/projects/<encoded-cwd>/memory` to WRITE where the Claude Code CLI
|
|
1081
|
+
* reads. That store is READ unconditionally either way, so setting this is only needed to
|
|
1082
|
+
* share the writes.
|
|
1083
|
+
*/
|
|
1084
|
+
directory?: string;
|
|
1070
1085
|
/**
|
|
1071
1086
|
* Whether the SDK auto-injects recalled facts as a `<memory>` block in the
|
|
1072
1087
|
* LLM system prompt. Default `true`.
|
|
@@ -295,28 +295,24 @@ interface BudgetTracker {
|
|
|
295
295
|
}
|
|
296
296
|
|
|
297
297
|
/**
|
|
298
|
-
*
|
|
299
|
-
*
|
|
300
|
-
*
|
|
301
|
-
*
|
|
302
|
-
*
|
|
303
|
-
*
|
|
304
|
-
*
|
|
305
|
-
*
|
|
306
|
-
*
|
|
307
|
-
*
|
|
308
|
-
*
|
|
309
|
-
*
|
|
310
|
-
*
|
|
311
|
-
*
|
|
312
|
-
* Layered model (mirrors Budget):
|
|
313
|
-
* - `MemoryAdapter` (in `types/memory-adapter.ts`) — LOW-LEVEL data port:
|
|
314
|
-
* write / recall / delete primitives.
|
|
315
|
-
* - `MemoryProvider` (THIS FILE) — HIGH-LEVEL lifecycle port: init,
|
|
316
|
-
* tool factories, active memory pass, embedding runtime selection.
|
|
317
|
-
*
|
|
318
|
-
* @public — surface-level interface; impls are internal-but-replaceable.
|
|
298
|
+
* A path that {@link resolveMemoryRoot} produced — the only thing the subsystem's path helpers
|
|
299
|
+
* accept.
|
|
300
|
+
*
|
|
301
|
+
* The brand is what makes "every path derives from one resolution" a compiler rule rather than a
|
|
302
|
+
* convention. Both a `cwd` and a root are strings, so without it the helpers would go on accepting
|
|
303
|
+
* either, and the next one added would have the same even chance of taking the wrong one that
|
|
304
|
+
* produced #463 in the first place. It costs one cast, at the one place a caller legitimately has a
|
|
305
|
+
* directory that did not come from here.
|
|
306
|
+
*
|
|
307
|
+
* STRUCTURAL, not a `unique symbol`. A `unique symbol` brand is identity-based, and the d.ts
|
|
308
|
+
* bundler inlines the declaration into each package that re-exports it — so `@theokit/sdk-memory`
|
|
309
|
+
* ended up with a `MemoryRoot` its own compiler considered incompatible with the SDK's, on values
|
|
310
|
+
* that were the same string. A structural tag refuses a bare `string` exactly as well and survives
|
|
311
|
+
* the package boundary, which is where this type has to work.
|
|
319
312
|
*/
|
|
313
|
+
type MemoryRoot = string & {
|
|
314
|
+
readonly __memoryRoot: "resolved";
|
|
315
|
+
};
|
|
320
316
|
|
|
321
317
|
/** Result of `MemoryProvider.runActivePass(...)` — what the kernel injects into the LLM call. */
|
|
322
318
|
interface ActiveMemoryPassResult {
|
|
@@ -360,6 +356,14 @@ interface RecordSessionSummaryArgs {
|
|
|
360
356
|
* compute the markdown file path.
|
|
361
357
|
*/
|
|
362
358
|
readonly cwd: string;
|
|
359
|
+
/**
|
|
360
|
+
* The memory root the kernel resolved for this agent, from `memory.directory` or the default.
|
|
361
|
+
*
|
|
362
|
+
* Supplied by the kernel, never constructed by an implementor — which is why it is required
|
|
363
|
+
* rather than optional. An implementation that recomputed it from `cwd` would write the summary
|
|
364
|
+
* into a different directory than the one the rest of the subsystem uses (#463).
|
|
365
|
+
*/
|
|
366
|
+
readonly memoryRoot: MemoryRoot;
|
|
363
367
|
/** Run id used as the filename key. */
|
|
364
368
|
readonly runId: string;
|
|
365
369
|
/** Agent identity for scope (foldering). */
|
|
@@ -1067,6 +1071,17 @@ interface MemorySettings {
|
|
|
1067
1071
|
userId?: string;
|
|
1068
1072
|
scope?: "agent" | "user" | "team";
|
|
1069
1073
|
storePath?: string;
|
|
1074
|
+
/**
|
|
1075
|
+
* Where this agent's memory lives. Default `<cwd>/.theokit/memory`.
|
|
1076
|
+
*
|
|
1077
|
+
* Must be an absolute path or start with `~/` — a relative value is refused rather than
|
|
1078
|
+
* resolved, because the two plausible bases put the store in two different places.
|
|
1079
|
+
*
|
|
1080
|
+
* Point it at `~/.claude/projects/<encoded-cwd>/memory` to WRITE where the Claude Code CLI
|
|
1081
|
+
* reads. That store is READ unconditionally either way, so setting this is only needed to
|
|
1082
|
+
* share the writes.
|
|
1083
|
+
*/
|
|
1084
|
+
directory?: string;
|
|
1070
1085
|
/**
|
|
1071
1086
|
* Whether the SDK auto-injects recalled facts as a `<memory>` block in the
|
|
1072
1087
|
* LLM system prompt. Default `true`.
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/internal/persistence/fts5-sanitize.ts
|
|
4
|
+
var PHRASE_OPEN = "";
|
|
5
|
+
var PHRASE_CLOSE = "";
|
|
6
|
+
function sanitizeFts5Query(query) {
|
|
7
|
+
if (query.length === 0) return query;
|
|
8
|
+
const phrases = [];
|
|
9
|
+
let text = query.replace(/"[^"]+"/g, (match) => {
|
|
10
|
+
phrases.push(match);
|
|
11
|
+
return `${PHRASE_OPEN}${phrases.length - 1}${PHRASE_CLOSE}`;
|
|
12
|
+
});
|
|
13
|
+
text = text.replace(/[[\]{}()"^]/g, " ");
|
|
14
|
+
text = text.replace(/\*+/g, "*");
|
|
15
|
+
text = text.replace(/^\s*(AND|OR|NOT)\s+/i, "");
|
|
16
|
+
text = text.replace(/\s+(AND|OR|NOT)\s*$/i, "");
|
|
17
|
+
text = text.replace(/\b\w+[-._]\w[\w\-._]*\b/g, (match) => `"${match}"`);
|
|
18
|
+
for (let i = 0; i < phrases.length; i += 1) {
|
|
19
|
+
text = text.replace(`${PHRASE_OPEN}${i}${PHRASE_CLOSE}`, phrases[i] ?? "");
|
|
20
|
+
}
|
|
21
|
+
return text.trim();
|
|
22
|
+
}
|
|
23
|
+
var CJK_RANGES = [
|
|
24
|
+
[12288, 12351],
|
|
25
|
+
// CJK Symbols and Punctuation
|
|
26
|
+
[12352, 12447],
|
|
27
|
+
// Hiragana
|
|
28
|
+
[12448, 12543],
|
|
29
|
+
// Katakana
|
|
30
|
+
[13312, 19903],
|
|
31
|
+
// CJK Unified Ideographs Extension A
|
|
32
|
+
[19968, 40959],
|
|
33
|
+
// CJK Unified Ideographs
|
|
34
|
+
[44032, 55215]
|
|
35
|
+
// Hangul Syllables
|
|
36
|
+
];
|
|
37
|
+
function containsCjk(text) {
|
|
38
|
+
for (const char of text) {
|
|
39
|
+
const cp = char.codePointAt(0);
|
|
40
|
+
if (cp === void 0) continue;
|
|
41
|
+
for (const [lo, hi] of CJK_RANGES) {
|
|
42
|
+
if (cp >= lo && cp <= hi) return true;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
exports.containsCjk = containsCjk;
|
|
49
|
+
exports.sanitizeFts5Query = sanitizeFts5Query;
|
|
50
|
+
//# sourceMappingURL=chunk-A4RAL2ER.cjs.map
|
|
51
|
+
//# sourceMappingURL=chunk-A4RAL2ER.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/internal/persistence/fts5-sanitize.ts"],"names":[],"mappings":";;;AAsBA,IAAM,WAAA,GAAc,GAAA;AACpB,IAAM,YAAA,GAAe,GAAA;AAUd,SAAS,kBAAkB,KAAA,EAAuB;AACvD,EAAA,IAAI,KAAA,CAAM,MAAA,KAAW,CAAA,EAAG,OAAO,KAAA;AAG/B,EAAA,MAAM,UAAoB,EAAC;AAC3B,EAAA,IAAI,IAAA,GAAO,KAAA,CAAM,OAAA,CAAQ,UAAA,EAAY,CAAC,KAAA,KAAU;AAC9C,IAAA,OAAA,CAAQ,KAAK,KAAK,CAAA;AAClB,IAAA,OAAO,GAAG,WAAW,CAAA,EAAG,QAAQ,MAAA,GAAS,CAAC,GAAG,YAAY,CAAA,CAAA;AAAA,EAC3D,CAAC,CAAA;AAGD,EAAA,IAAA,GAAO,IAAA,CAAK,OAAA,CAAQ,cAAA,EAAgB,GAAG,CAAA;AAGvC,EAAA,IAAA,GAAO,IAAA,CAAK,OAAA,CAAQ,MAAA,EAAQ,GAAG,CAAA;AAG/B,EAAA,IAAA,GAAO,IAAA,CAAK,OAAA,CAAQ,sBAAA,EAAwB,EAAE,CAAA;AAC9C,EAAA,IAAA,GAAO,IAAA,CAAK,OAAA,CAAQ,sBAAA,EAAwB,EAAE,CAAA;AAI9C,EAAA,IAAA,GAAO,KAAK,OAAA,CAAQ,0BAAA,EAA4B,CAAC,KAAA,KAAU,CAAA,CAAA,EAAI,KAAK,CAAA,CAAA,CAAG,CAAA;AAGvE,EAAA,KAAA,IAAS,IAAI,CAAA,EAAG,CAAA,GAAI,OAAA,CAAQ,MAAA,EAAQ,KAAK,CAAA,EAAG;AAC1C,IAAA,IAAA,GAAO,IAAA,CAAK,OAAA,CAAQ,CAAA,EAAG,WAAW,CAAA,EAAG,CAAC,CAAA,EAAG,YAAY,CAAA,CAAA,EAAI,OAAA,CAAQ,CAAC,CAAA,IAAK,EAAE,CAAA;AAAA,EAC3E;AAEA,EAAA,OAAO,KAAK,IAAA,EAAK;AACnB;AAIA,IAAM,UAAA,GAAuD;AAAA,EAC3D,CAAC,OAAQ,KAAM,CAAA;AAAA;AAAA,EACf,CAAC,OAAQ,KAAM,CAAA;AAAA;AAAA,EACf,CAAC,OAAQ,KAAM,CAAA;AAAA;AAAA,EACf,CAAC,OAAQ,KAAM,CAAA;AAAA;AAAA,EACf,CAAC,OAAQ,KAAM,CAAA;AAAA;AAAA,EACf,CAAC,OAAQ,KAAM;AAAA;AACjB,CAAA;AAqBO,SAAS,YAAY,IAAA,EAAuB;AACjD,EAAA,KAAA,MAAW,QAAQ,IAAA,EAAM;AACvB,IAAA,MAAM,EAAA,GAAK,IAAA,CAAK,WAAA,CAAY,CAAC,CAAA;AAC7B,IAAA,IAAI,OAAO,MAAA,EAAW;AACtB,IAAA,KAAA,MAAW,CAAC,EAAA,EAAI,EAAE,CAAA,IAAK,UAAA,EAAY;AACjC,MAAA,IAAI,EAAA,IAAM,EAAA,IAAM,EAAA,IAAM,EAAA,EAAI,OAAO,IAAA;AAAA,IACnC;AAAA,EACF;AACA,EAAA,OAAO,KAAA;AACT","file":"chunk-A4RAL2ER.cjs","sourcesContent":["/**\n * FTS5 query sanitization + CJK detection (ADR D64).\n *\n * Port of Hermes' 6-step sanitizer (`hermes_state.py:1797-1847`). Prevents\n * crashes on user inputs with hyphens, dots, underscores, and unmatched\n * specials. Auto-quotes identifier-shaped tokens so `error-code` finds\n * literal `error-code` instead of `error AND code`.\n *\n * CJK detection returns true for inputs containing characters in the main\n * CJK Unicode ranges (Chinese, Japanese, Korean). CJK trigram routing is\n * deferred to v1.4 — callers receiving `containsCjk === true` should\n * short-circuit to an empty result or LIKE fallback for v1.3.\n *\n * @internal\n */\n\n// Control-char sentinels for the phrase-preservation placeholder. U+0001\n// and U+0002 are essentially never present in real user queries, and\n// crucially do NOT form word boundaries / `\\w` characters, so Step 5's\n// auto-quote regex (`\\b\\w+[-._]\\w[\\w\\-._]*\\b`) cannot match them.\n// Choosing `__PHRASE_N__` instead would re-trigger Step 5 on the second\n// sanitize pass and break idempotence.\nconst PHRASE_OPEN = \"\u0001\";\nconst PHRASE_CLOSE = \"\u0002\";\n\n/**\n * Six-step FTS5 query sanitizer. Returns a query string safe for passing\n * to `WHERE <fts5_table> MATCH ?` as a parameter.\n *\n * Returns empty string when the input contains only specials (caller MUST\n * short-circuit to avoid runtime SQL error — EC-3).\n *\n */\nexport function sanitizeFts5Query(query: string): string {\n if (query.length === 0) return query;\n\n // Step 1: preserve \"quoted phrases\" via control-char placeholders.\n const phrases: string[] = [];\n let text = query.replace(/\"[^\"]+\"/g, (match) => {\n phrases.push(match);\n return `${PHRASE_OPEN}${phrases.length - 1}${PHRASE_CLOSE}`;\n });\n\n // Step 2: strip unmatched specials (brackets, braces, parens, double quotes, caret).\n text = text.replace(/[[\\]{}()\"^]/g, \" \");\n\n // Step 3: collapse repeated asterisks (FTS5 prefix operator is single `*`).\n text = text.replace(/\\*+/g, \"*\");\n\n // Step 4: strip dangling boolean operators (AND/OR/NOT) at start/end.\n text = text.replace(/^\\s*(AND|OR|NOT)\\s+/i, \"\");\n text = text.replace(/\\s+(AND|OR|NOT)\\s*$/i, \"\");\n\n // Step 5: auto-quote identifier-shaped tokens that contain `-`, `.`, or `_`.\n // FTS5 tokenizer would otherwise split them as boolean conjunctions.\n text = text.replace(/\\b\\w+[-._]\\w[\\w\\-._]*\\b/g, (match) => `\"${match}\"`);\n\n // Step 6: restore preserved phrases.\n for (let i = 0; i < phrases.length; i += 1) {\n text = text.replace(`${PHRASE_OPEN}${i}${PHRASE_CLOSE}`, phrases[i] ?? \"\");\n }\n\n return text.trim();\n}\n\n// CJK code-point ranges. Coverage matches Hermes' coarse detection for v1.3;\n// fine-grained trigram routing comes in v1.4.\nconst CJK_RANGES: ReadonlyArray<readonly [number, number]> = [\n [0x3000, 0x303f], // CJK Symbols and Punctuation\n [0x3040, 0x309f], // Hiragana\n [0x30a0, 0x30ff], // Katakana\n [0x3400, 0x4dbf], // CJK Unified Ideographs Extension A\n [0x4e00, 0x9fff], // CJK Unified Ideographs\n [0xac00, 0xd7af], // Hangul Syllables\n];\n\n/**\n * Report whether `text` holds at least one Chinese, Japanese or Korean character.\n *\n * Iterates by code point, so surrogate pairs are handled correctly, and returns on the first hit.\n * An empty string is `false`.\n *\n * Coverage is deliberately coarse: CJK Symbols and Punctuation, Hiragana, Katakana, Hangul\n * Syllables, and CJK Unified Ideographs including Extension A. Everything beyond that — the\n * higher ideograph extensions, Halfwidth and Fullwidth Forms, Hangul Jamo — reads as `false`, so\n * a `false` is not proof that the text is CJK-free.\n *\n * The reason to ask is FTS5: the default tokenizer splits on whitespace, which CJK text does not\n * use, so a matching query against it returns nothing rather than failing. A caller seeing `true`\n * should short-circuit to an empty result or a LIKE fallback instead of running the search and\n * reporting no matches.\n *\n * Semver-exempt: reachable via the `@theokit/sdk/internal/persistence` sub-path, which the package\n * declares in `exports` but does NOT cover with its semver contract.\n */\nexport function containsCjk(text: string): boolean {\n for (const char of text) {\n const cp = char.codePointAt(0);\n if (cp === undefined) continue;\n for (const [lo, hi] of CJK_RANGES) {\n if (cp >= lo && cp <= hi) return true;\n }\n }\n return false;\n}\n"]}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var chunkI6TGFUCO_cjs = require('./chunk-I6TGFUCO.cjs');
|
|
4
|
+
var chunkNUKRL3I6_cjs = require('./chunk-NUKRL3I6.cjs');
|
|
5
|
+
var path = require('path');
|
|
6
|
+
|
|
7
|
+
async function persistActiveMemoryTranscript(root, transcript) {
|
|
8
|
+
try {
|
|
9
|
+
const dir = path.join(root, "transcripts", "active-memory");
|
|
10
|
+
const file = path.join(dir, `${transcript.runId}.json`);
|
|
11
|
+
await chunkI6TGFUCO_cjs.atomicWriteJson(file, transcript);
|
|
12
|
+
} catch (cause) {
|
|
13
|
+
const message = cause instanceof Error ? cause.message : String(cause);
|
|
14
|
+
chunkNUKRL3I6_cjs.diag(`[theokit-sdk] active-memory transcript persist failed: ${message}
|
|
15
|
+
`);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
exports.persistActiveMemoryTranscript = persistActiveMemoryTranscript;
|
|
20
|
+
//# sourceMappingURL=chunk-A4VHSE56.cjs.map
|
|
21
|
+
//# sourceMappingURL=chunk-A4VHSE56.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/internal/memory/storage/transcript-store.ts"],"names":["join","atomicWriteJson","diag"],"mappings":";;;;;;AA2CA,eAAsB,6BAAA,CACpB,MACA,UAAA,EACe;AACf,EAAA,IAAI;AACF,IAAA,MAAM,GAAA,GAAMA,SAAA,CAAK,IAAA,EAAM,aAAA,EAAe,eAAe,CAAA;AACrD,IAAA,MAAM,OAAOA,SAAA,CAAK,GAAA,EAAK,CAAA,EAAG,UAAA,CAAW,KAAK,CAAA,KAAA,CAAO,CAAA;AAGjD,IAAA,MAAMC,iCAAA,CAAgB,MAAM,UAAU,CAAA;AAAA,EACxC,SAAS,KAAA,EAAO;AACd,IAAA,MAAM,UAAU,KAAA,YAAiB,KAAA,GAAQ,KAAA,CAAM,OAAA,GAAU,OAAO,KAAK,CAAA;AACrE,IAAAC,sBAAA,CAAK,0DAA0D,OAAO;AAAA,CAAI,CAAA;AAAA,EAC5E;AACF","file":"chunk-A4VHSE56.cjs","sourcesContent":["import { join } from \"node:path\";\nimport { diag } from \"../../diagnostics.js\";\nimport { atomicWriteJson } from \"../../persistence/atomic-write.js\";\nimport type { MemoryRoot } from \"./memory-root.js\";\n\n/**\n * Optional on-disk persistence for Active Memory recall transcripts (ADR D6).\n *\n * Writes one JSON file per run under\n * `.theokit/memory/transcripts/active-memory/<runId>.json` when the agent\n * passes `persistTranscripts: true`. Failures are swallowed with a stderr\n * warning so transcript IO never crashes the agent run.\n *\n * Shared with `@theokit/sdk-memory` through the semver-exempt `internal/memory-store`\n * sub-path, so it carries no internal-visibility tag. `stripInternal` matches that tag as TEXT\n * anywhere in the block, so naming it here — even in backticks, even to say it is absent — deletes\n * this symbol from the published declarations and forces the satellite back onto a copy. Measured:\n * the first draft of this very note did exactly that. See #430 and #463.\n */\n\nexport interface ActiveMemoryTranscript {\n runId: string;\n startedAtMs: number;\n userText: string;\n queryMode: string;\n status: string;\n durationMs: number;\n summary: string | undefined;\n hits: ReadonlyArray<{\n path: string;\n startLine: number;\n endLine: number;\n score: number;\n snippet: string;\n }>;\n}\n\n/**\n * Write one active-memory recall transcript under `<memory root>/transcripts/active-memory`.\n *\n * Never throws. Transcript IO is observability, and observability must not break the run it merely\n * observes — a failure is reported through the diagnostics sink and swallowed.\n */\nexport async function persistActiveMemoryTranscript(\n root: MemoryRoot,\n transcript: ActiveMemoryTranscript,\n): Promise<void> {\n try {\n const dir = join(root, \"transcripts\", \"active-memory\");\n const file = join(dir, `${transcript.runId}.json`);\n // atomicWriteJson auto-creates the parent directory + writes atomically\n // (no torn writes during crash).\n await atomicWriteJson(file, transcript);\n } catch (cause) {\n const message = cause instanceof Error ? cause.message : String(cause);\n diag(`[theokit-sdk] active-memory transcript persist failed: ${message}\\n`);\n }\n}\n"]}
|
|
@@ -3,51 +3,6 @@ import { diag } from './chunk-T7O6K6PX.js';
|
|
|
3
3
|
import { mkdir, rename } from 'fs/promises';
|
|
4
4
|
import { dirname } from 'path';
|
|
5
5
|
|
|
6
|
-
// src/internal/persistence/fts5-sanitize.ts
|
|
7
|
-
var PHRASE_OPEN = "";
|
|
8
|
-
var PHRASE_CLOSE = "";
|
|
9
|
-
function sanitizeFts5Query(query) {
|
|
10
|
-
if (query.length === 0) return query;
|
|
11
|
-
const phrases = [];
|
|
12
|
-
let text = query.replace(/"[^"]+"/g, (match) => {
|
|
13
|
-
phrases.push(match);
|
|
14
|
-
return `${PHRASE_OPEN}${phrases.length - 1}${PHRASE_CLOSE}`;
|
|
15
|
-
});
|
|
16
|
-
text = text.replace(/[[\]{}()"^]/g, " ");
|
|
17
|
-
text = text.replace(/\*+/g, "*");
|
|
18
|
-
text = text.replace(/^\s*(AND|OR|NOT)\s+/i, "");
|
|
19
|
-
text = text.replace(/\s+(AND|OR|NOT)\s*$/i, "");
|
|
20
|
-
text = text.replace(/\b\w+[-._]\w[\w\-._]*\b/g, (match) => `"${match}"`);
|
|
21
|
-
for (let i = 0; i < phrases.length; i += 1) {
|
|
22
|
-
text = text.replace(`${PHRASE_OPEN}${i}${PHRASE_CLOSE}`, phrases[i] ?? "");
|
|
23
|
-
}
|
|
24
|
-
return text.trim();
|
|
25
|
-
}
|
|
26
|
-
var CJK_RANGES = [
|
|
27
|
-
[12288, 12351],
|
|
28
|
-
// CJK Symbols and Punctuation
|
|
29
|
-
[12352, 12447],
|
|
30
|
-
// Hiragana
|
|
31
|
-
[12448, 12543],
|
|
32
|
-
// Katakana
|
|
33
|
-
[13312, 19903],
|
|
34
|
-
// CJK Unified Ideographs Extension A
|
|
35
|
-
[19968, 40959],
|
|
36
|
-
// CJK Unified Ideographs
|
|
37
|
-
[44032, 55215]
|
|
38
|
-
// Hangul Syllables
|
|
39
|
-
];
|
|
40
|
-
function containsCjk(text) {
|
|
41
|
-
for (const char of text) {
|
|
42
|
-
const cp = char.codePointAt(0);
|
|
43
|
-
if (cp === void 0) continue;
|
|
44
|
-
for (const [lo, hi] of CJK_RANGES) {
|
|
45
|
-
if (cp >= lo && cp <= hi) return true;
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
return false;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
6
|
// src/internal/persistence/sqlite-wal.ts
|
|
52
7
|
var warnedLabels = /* @__PURE__ */ new Set();
|
|
53
8
|
function applyWalWithFallback(db, label) {
|
|
@@ -161,6 +116,6 @@ async function renameAside(filePath, label) {
|
|
|
161
116
|
);
|
|
162
117
|
}
|
|
163
118
|
|
|
164
|
-
export { applyWalWithFallback,
|
|
165
|
-
//# sourceMappingURL=chunk-
|
|
166
|
-
//# sourceMappingURL=chunk-
|
|
119
|
+
export { applyWalWithFallback, isCorruptionError, openSqliteResilient };
|
|
120
|
+
//# sourceMappingURL=chunk-AMFXSENK.js.map
|
|
121
|
+
//# sourceMappingURL=chunk-AMFXSENK.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/internal/persistence/sqlite-wal.ts","../src/internal/persistence/sqlite-open.ts"],"names":[],"mappings":"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","file":"chunk-AMFXSENK.js","sourcesContent":["/**\n * SQLite WAL mode helper with NFS/SMB/FUSE fallback to DELETE (ADR D63).\n *\n * WAL is faster (concurrent readers + one writer) but unsupported on some\n * network/FUSE filesystems. Try WAL; if the pragma returns something else\n * or throws, fall back to DELETE journal mode. Warn one time per label.\n *\n * This block is the MODULE header and must stay at offset 0. Sitting below the import it became a\n * leading comment of the first declaration instead, which is how its tag deleted `PragmaCapable`\n * from the emitted `.d.ts` while `applyWalWithFallback` — which names it — survived.\n */\n\nimport { diag } from \"../diagnostics.js\";\n\n/**\n * The narrow slice of a SQLite handle this module needs: anything with a `pragma()` method, which\n * `better-sqlite3` satisfies without being named here.\n *\n * Not exported, but it appears in the signature of `applyWalWithFallback`, so it is emitted into\n * the published declarations and a consumer can structurally satisfy it.\n */\ninterface PragmaCapable {\n pragma: (statement: string, options?: { simple?: boolean }) => unknown;\n}\n\n/**\n * What journal mode a connection ended up in after `applyWalWithFallback`.\n *\n * `mode` is the mode actually in effect, never the one that was requested. `fellBack` is `true`\n * only when WAL was attempted and refused — either the pragma threw or it reported a mode other\n * than `wal` — and the connection was put into DELETE instead.\n *\n * `fellBack: true` is normal on NFS, SMB and FUSE, where WAL needs shared memory the filesystem\n * does not provide. It is not an error and nothing further is required of the caller; the\n * consequence is slower concurrent access, since DELETE mode does not allow readers alongside a\n * writer. Treat it as a signal about the storage, not about the database.\n *\n * @public — re-exported from the semver-protected `@theokit/sdk/persistence` barrel, and (for\n * back-compat) from the semver-exempt `@theokit/sdk/internal/persistence` alias.\n */\nexport interface WalApplyResult {\n /** Final journal_mode actually in effect. */\n mode: \"wal\" | \"delete\";\n /** True if we wanted WAL but the filesystem refused. */\n fellBack: boolean;\n}\n\nconst warnedLabels = new Set<string>();\n\n/**\n * Apply WAL mode with DELETE fallback. Idempotent — safe to call multiple\n * times on the same connection.\n *\n * @param db any `pragma()`-capable SQLite handle (e.g., `better-sqlite3`)\n * @param label short identifier used in the warning (e.g., \"memory-index\")\n *\n */\nexport function applyWalWithFallback(db: PragmaCapable, label: string): WalApplyResult {\n try {\n const result = db.pragma(\"journal_mode = WAL\", { simple: true });\n if (typeof result === \"string\" && result.toLowerCase() === \"wal\") {\n return { mode: \"wal\", fellBack: false };\n }\n logFallback(label, `got \"${String(result)}\" instead of \"wal\"`);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n logFallback(label, msg);\n }\n\n db.pragma(\"journal_mode = DELETE\");\n return { mode: \"delete\", fellBack: true };\n}\n\nfunction logFallback(label: string, reason: string): void {\n if (warnedLabels.has(label)) return;\n warnedLabels.add(label);\n diag(\n `[theokit-sdk] ${label}: WAL unavailable (${reason}); using DELETE journal mode. ` +\n \"This is normal on NFS/SMB/FUSE; expect slightly slower concurrent access.\\n\",\n );\n}\n\n/**\n * Test helper — clears the warn-once registry.\n *\n * @internal\n */\nexport function _resetWalWarnings(): void {\n warnedLabels.clear();\n}\n","/**\n * Resilient SQLite open (plan m0-foundation-expose-primitives, M0-5).\n *\n * Generalizes the driver-load + WAL-apply + corruption-recovery logic that was\n * duplicated (byte-identical) across `sdk/internal/memory/index-db.ts` and\n * `sdk-memory/internal/index/index-db.ts`. Schema-agnostic: the caller applies\n * its own PRAGMA/SCHEMA via the `onOpen` callback.\n *\n * Corruption recovery (EC-7): when opening fails with a \"malformed\" / \"not a\n * database\" / \"encrypted\" error and `recoverCorrupt` is not false, the file is\n * renamed aside to `<path>.corrupt-<ts>` (plus its WAL/SHM siblings) and a fresh\n * DB is opened. The corrupt file is renamed, NOT backed up — the timestamped\n * `.corrupt-*` file is kept for manual recovery.\n *\n * @internal — public via `@theokit/sdk/internal/persistence` (semver-exempt)\n */\n\nimport { mkdir, rename } from \"node:fs/promises\";\nimport { dirname } from \"node:path\";\n\nimport { ConfigurationError } from \"../../errors.js\";\nimport { diag } from \"../diagnostics.js\";\nimport { applyWalWithFallback } from \"./sqlite-wal.js\";\n\n/** Minimal SQLite handle surface every driver (`better-sqlite3`) exposes. */\nexport interface ResilientSqliteDb {\n /** SQLite `pragma()` access (used by `applyWalWithFallback`). */\n pragma(statement: string, options?: { simple?: boolean }): unknown;\n exec(sql: string): void;\n close(): void;\n}\n\n/**\n * Input to {@link openSqliteResilient}.\n *\n * The trap is `recoverCorrupt`, whose default is ON. When the driver reports a corrupt or encrypted\n * database, the file is renamed aside and a FRESH one is opened — so the call SUCCEEDS and hands\n * back an EMPTY database. The old bytes survive on disk under the renamed path, but a caller that\n * treats a resolved promise as \"my data is here\" is wrong exactly when it matters. Pass\n * `recoverCorrupt: false` to get the corruption error thrown instead.\n *\n * Semver-exempt: reachable via the '@theokit/sdk/internal/persistence' sub-path, which the package\n * declares in 'exports' but does NOT cover with its semver contract.\n *\n * @typeParam T - the concrete DB handle the driver returns; defaults to {@link ResilientSqliteDb}\n */\nexport interface OpenSqliteResilientOptions<T extends ResilientSqliteDb> {\n /** Absolute path to the SQLite file. Parent directories are created. */\n filePath: string;\n /**\n * Called after the driver is open and WAL is applied, before the handle is\n * returned. Apply PRAGMA/SCHEMA statements here. Errors propagate.\n */\n onOpen?: (db: T) => void | Promise<void>;\n /** Label used in the WAL-fallback warning and corruption-recovery log. Default \"sqlite\". */\n label?: string;\n /** When true (default) a corruption error renames the file aside and rebuilds. */\n recoverCorrupt?: boolean;\n}\n\n/**\n * Open a SQLite file with WAL (+ DELETE fallback) and corruption recovery.\n *\n * @typeParam T - the concrete DB handle type the driver returns (defaults to the\n * minimal {@link ResilientSqliteDb} surface)\n */\nexport async function openSqliteResilient<T extends ResilientSqliteDb = ResilientSqliteDb>(\n options: OpenSqliteResilientOptions<T>,\n): Promise<T> {\n await mkdir(dirname(options.filePath), { recursive: true });\n try {\n return await openConcrete(options);\n } catch (cause) {\n if (options.recoverCorrupt !== false && isCorruptionError(cause)) {\n await renameAside(options.filePath, options.label ?? \"sqlite\");\n return await openConcrete(options);\n }\n throw cause;\n }\n}\n\nasync function openConcrete<T extends ResilientSqliteDb>(\n options: OpenSqliteResilientOptions<T>,\n): Promise<T> {\n const db = await loadDriver<T>(options.filePath);\n // Apply WAL with NFS/SMB/FUSE fallback BEFORE schema so the journal mode is\n // set for the whole session.\n applyWalWithFallback(db, options.label ?? \"sqlite\");\n await options.onOpen?.(db);\n return db;\n}\n\n/** Injectable driver loaders (tests simulate a consumer env without better-sqlite3). */\ninterface DriverLoaders {\n betterSqlite3?: () => Promise<unknown>;\n nodeSqlite?: () => Promise<unknown>;\n}\nlet driverLoaderOverrides: DriverLoaders | undefined;\n\n/** Test-only. @internal */\nexport function _setDriverLoadersForTests(overrides: DriverLoaders | undefined): void {\n driverLoaderOverrides = overrides;\n}\n\n/**\n * Adapt `node:sqlite`'s `DatabaseSync` to the better-sqlite3 surface this module's callers use\n * (`prepare/get/all/run`, `exec`, `close`, `pragma`, `loadExtension`). The error message below has\n * ALWAYS promised this fallback (\"or run on Node 22.5+ for built-in node:sqlite\") — before the\n * flicker-bug fix the promise was fabricated: only better-sqlite3 was ever tried, so every consumer\n * without the optional native dep lost memory tools AND got a per-turn stderr WARN.\n */\nfunction adaptNodeSqlite(db: {\n prepare(sql: string): unknown;\n exec(sql: string): void;\n close(): void;\n loadExtension?: (path: string) => void;\n}): ResilientSqliteDb {\n const pragma = (statement: string, options?: { simple?: boolean }): unknown => {\n const stmt = db.prepare(`PRAGMA ${statement}`) as {\n get(): Record<string, unknown> | undefined;\n };\n const row = stmt.get();\n if (options?.simple === true) {\n return row === undefined ? undefined : Object.values(row)[0];\n }\n return row === undefined ? [] : [row];\n };\n return new Proxy(db as unknown as ResilientSqliteDb, {\n get(target, prop, receiver) {\n if (prop === \"pragma\") return pragma;\n if (prop === \"loadExtension\" && typeof db.loadExtension !== \"function\") {\n return () => {\n throw new Error(\n \"SQLite extension loading is unavailable on the node:sqlite fallback — install better-sqlite3 for sqlite-vec\",\n );\n };\n }\n const value = Reflect.get(target, prop, receiver) as unknown;\n return typeof value === \"function\"\n ? (value as (...a: unknown[]) => unknown).bind(target)\n : value;\n },\n });\n}\n\n// PRE-EXISTING debt, exposed when M75 fixed the Biome config that used to abort before\n// sweeping these files (a nested root under refactor/). It is not new code and was not touched\n// by M75; refactoring SDK internals without review would trade a visible problem for a diff\n// risky. Tracked in usetheodev/theokit-sdk#151.\n// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: see the reason just above\nasync function loadDriver<T extends ResilientSqliteDb>(filePath: string): Promise<T> {\n let betterSqliteCause: unknown;\n try {\n const mod = (await (driverLoaderOverrides?.betterSqlite3?.() ?? import(\"better-sqlite3\"))) as {\n default?: unknown;\n };\n const Ctor = mod.default ?? mod;\n if (typeof Ctor !== \"function\") {\n throw new Error(`better-sqlite3 export is not a constructor (got ${typeof Ctor})`);\n }\n return new (Ctor as new (path: string) => unknown)(filePath) as T;\n } catch (cause) {\n betterSqliteCause = cause;\n }\n // Fallback: the Node 22.5+ built-in driver (the path the error message documents).\n // `process.getBuiltinModule` (Node 22.3+) instead of `import(\"node:sqlite\")` — bundlers that\n // predate the sqlite builtin rewrite the import specifier to a bare \"sqlite\" package (proven in\n // the published dist: \"Cannot find package 'sqlite'\"), while getBuiltinModule is opaque to them.\n try {\n const mod = (await (driverLoaderOverrides?.nodeSqlite?.() ??\n Promise.resolve(\n (process as { getBuiltinModule?: (id: string) => unknown }).getBuiltinModule?.(\n \"node:sqlite\",\n ) ??\n (() => {\n throw new Error(\"node:sqlite built-in unavailable (Node < 22.3)\");\n })(),\n ))) as {\n DatabaseSync: new (\n path: string,\n ) => {\n prepare(sql: string): unknown;\n exec(sql: string): void;\n close(): void;\n };\n };\n return adaptNodeSqlite(new mod.DatabaseSync(filePath)) as T;\n } catch (nodeSqliteCause) {\n const b =\n betterSqliteCause instanceof Error ? betterSqliteCause.message : String(betterSqliteCause);\n const n = nodeSqliteCause instanceof Error ? nodeSqliteCause.message : String(nodeSqliteCause);\n throw new ConfigurationError(\n `Failed to load SQLite driver. Install \\`better-sqlite3\\` or run on Node 22.5+ for built-in \\`node:sqlite\\`. better-sqlite3: ${b}; node:sqlite: ${n}`,\n { code: \"sqlite_driver_unavailable\", cause: nodeSqliteCause },\n );\n }\n}\n\n/** True when an open error indicates an unreadable / corrupt database file. */\nexport function isCorruptionError(cause: unknown): boolean {\n if (!(cause instanceof Error)) return false;\n const msg = cause.message.toLowerCase();\n return (\n msg.includes(\"malformed\") ||\n msg.includes(\"not a database\") ||\n msg.includes(\"encrypted\") ||\n msg.includes(\"disk image is malformed\")\n );\n}\n\nasync function renameAside(filePath: string, label: string): Promise<void> {\n const asidePath = `${filePath}.corrupt-${Date.now()}`;\n await rename(filePath, asidePath).catch(() => undefined);\n await rename(`${filePath}-wal`, `${asidePath}-wal`).catch(() => undefined);\n await rename(`${filePath}-shm`, `${asidePath}-shm`).catch(() => undefined);\n diag(\n `[theokit-sdk] ${label} database corrupt; renamed aside to ${asidePath} and rebuilt schema\\n`,\n );\n}\n"]}
|