@theokit/sdk 4.59.0 → 4.61.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 +55 -0
- package/dist/{agent-VHRX7XGW.cjs → agent-M53C3PAA.cjs} +10 -9
- package/dist/{agent-VHRX7XGW.cjs.map → agent-M53C3PAA.cjs.map} +1 -1
- package/dist/{agent-ST27RIJE.js → agent-P42GA6RM.js} +9 -8
- package/dist/{agent-ST27RIJE.js.map → agent-P42GA6RM.js.map} +1 -1
- package/dist/{chunk-KKK3FZ4A.cjs → chunk-2DG7KW4L.cjs} +3 -3
- package/dist/{chunk-KKK3FZ4A.cjs.map → chunk-2DG7KW4L.cjs.map} +1 -1
- package/dist/chunk-2UCFUSPW.cjs +468 -0
- package/dist/chunk-2UCFUSPW.cjs.map +1 -0
- package/dist/{chunk-D5NWEOCO.js → chunk-532CSLYU.js} +3 -3
- package/dist/{chunk-D5NWEOCO.js.map → chunk-532CSLYU.js.map} +1 -1
- package/dist/{chunk-7GUIET73.cjs → chunk-5IT6DUOO.cjs} +4 -4
- package/dist/{chunk-7GUIET73.cjs.map → chunk-5IT6DUOO.cjs.map} +1 -1
- package/dist/{chunk-554J7UQH.cjs → chunk-6OBIWHDR.cjs} +14 -201
- package/dist/chunk-6OBIWHDR.cjs.map +1 -0
- package/dist/{chunk-GNT35C5U.cjs → chunk-6SBW4QR2.cjs} +2 -2
- package/dist/{chunk-GNT35C5U.cjs.map → chunk-6SBW4QR2.cjs.map} +1 -1
- package/dist/{chunk-XV4IZNV4.js → chunk-FUL2I7G5.js} +2 -2
- package/dist/{chunk-XV4IZNV4.js.map → chunk-FUL2I7G5.js.map} +1 -1
- package/dist/{chunk-7LOIUIQZ.js → chunk-GQZKDGZM.js} +168 -13
- package/dist/chunk-GQZKDGZM.js.map +1 -0
- package/dist/{chunk-43GFJ5SD.cjs → chunk-P5LCASTC.cjs} +19 -4
- package/dist/chunk-P5LCASTC.cjs.map +1 -0
- package/dist/{chunk-ZA255A62.js → chunk-QEKI3YKI.js} +7 -186
- package/dist/chunk-QEKI3YKI.js.map +1 -0
- package/dist/{chunk-SMUAG2DY.cjs → chunk-R7YIVL3K.cjs} +218 -63
- package/dist/chunk-R7YIVL3K.cjs.map +1 -0
- package/dist/chunk-WCLDJSMY.js +456 -0
- package/dist/chunk-WCLDJSMY.js.map +1 -0
- package/dist/{chunk-WG7R5W6R.js → chunk-YEXA3PGR.js} +3 -3
- package/dist/{chunk-WG7R5W6R.js.map → chunk-YEXA3PGR.js.map} +1 -1
- package/dist/{chunk-IACR5LEM.js → chunk-YXGAW7BB.js} +17 -2
- package/dist/chunk-YXGAW7BB.js.map +1 -0
- package/dist/{compact-session-YHFQIXV5.cjs → compact-session-GJXJD73F.cjs} +11 -11
- package/dist/{compact-session-YHFQIXV5.cjs.map → compact-session-GJXJD73F.cjs.map} +1 -1
- package/dist/{compact-session-QGDNP45U.js → compact-session-TSMQOIHO.js} +3 -3
- package/dist/{compact-session-QGDNP45U.js.map → compact-session-TSMQOIHO.js.map} +1 -1
- package/dist/cron.cjs +9 -8
- package/dist/cron.js +8 -7
- package/dist/eval.cjs +8 -7
- package/dist/eval.cjs.map +1 -1
- package/dist/eval.js +7 -6
- package/dist/eval.js.map +1 -1
- package/dist/{index-manager-A64I7KYV.js → index-manager-AHAYJ33H.js} +4 -3
- package/dist/{index-manager-A64I7KYV.js.map → index-manager-AHAYJ33H.js.map} +1 -1
- package/dist/{index-manager-A3XPHAWF.cjs → index-manager-RHPFVFSC.cjs} +5 -4
- package/dist/{index-manager-A3XPHAWF.cjs.map → index-manager-RHPFVFSC.cjs.map} +1 -1
- package/dist/index.cjs +77 -43
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +46 -12
- package/dist/index.js.map +1 -1
- package/dist/{inject-session-MLCJMYDG.cjs → inject-session-PPSO2IDD.cjs} +4 -4
- package/dist/{inject-session-MLCJMYDG.cjs.map → inject-session-PPSO2IDD.cjs.map} +1 -1
- package/dist/{inject-session-RBQZ45UM.js → inject-session-ZCQUB3IT.js} +3 -3
- package/dist/{inject-session-RBQZ45UM.js.map → inject-session-ZCQUB3IT.js.map} +1 -1
- package/dist/internal/memory/dreaming/phases.d.ts +21 -1
- package/dist/internal/memory/storage/chunk-markdown.d.cts +2 -0
- package/dist/internal/memory/storage/index.cjs +54 -0
- package/dist/internal/memory/storage/index.cjs.map +1 -0
- package/dist/internal/memory/storage/index.d.cts +18 -0
- package/dist/internal/memory/storage/index.d.ts +18 -0
- package/dist/internal/memory/storage/index.js +13 -0
- package/dist/internal/memory/storage/index.js.map +1 -0
- package/dist/internal/memory/storage/markdown-store.d.cts +77 -0
- package/dist/internal/memory/storage/markdown-store.d.ts +25 -1
- package/dist/internal/memory/storage/memory-file.d.cts +90 -0
- package/dist/internal/memory/storage/memory-file.d.ts +41 -5
- package/dist/internal/memory/storage/reader.d.cts +8 -0
- package/dist/internal/memory/storage/session-loader.d.cts +1 -0
- package/dist/internal/memory/storage/session-summary-writer.d.cts +2 -0
- package/dist/internal/memory/storage/threat-scan.d.cts +62 -0
- package/dist/internal/memory/storage/threat-scan.d.ts +62 -0
- package/dist/internal/memory/storage/transcript-store.d.cts +1 -0
- package/dist/internal/memory/storage/wiki-loader.d.cts +2 -0
- package/dist/internal/memory/types.d.ts +28 -0
- package/dist/internal/runtime/memory/select-facts.d.ts +63 -0
- package/dist/internal/runtime/system-prompt/sources/memory-provider.d.ts +6 -1
- package/dist/workflow.cjs +9 -9
- package/dist/workflow.js +1 -1
- package/docs/error-codes.md +3 -2
- package/docs/harness-capability-map.md +23 -7
- package/docs/memory-decisions.md +207 -0
- package/package.json +11 -1
- package/dist/chunk-43GFJ5SD.cjs.map +0 -1
- package/dist/chunk-554J7UQH.cjs.map +0 -1
- package/dist/chunk-7LOIUIQZ.js.map +0 -1
- package/dist/chunk-IACR5LEM.js.map +0 -1
- package/dist/chunk-SMUAG2DY.cjs.map +0 -1
- package/dist/chunk-ZA255A62.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function persistActiveMemoryTranscript(cwd: string, transcript: ActiveMemoryTranscript): Promise<void>;
|
|
@@ -15,12 +15,31 @@ export interface MemoryConfig {
|
|
|
15
15
|
*
|
|
16
16
|
* A kind is never INFERRED. A wrong kind is worse than none, because it makes retention and recall
|
|
17
17
|
* confident about the wrong thing — so a fact whose author did not say stays untyped.
|
|
18
|
+
*
|
|
19
|
+
* Four, not more, and deliberately: a wider vocabulary exists to drive differentiated retention, and
|
|
20
|
+
* there is no retention here to differentiate. See `packages/sdk/docs/memory-decisions.md` § 2.
|
|
18
21
|
*/
|
|
19
22
|
export type MemoryKind = "user" | "feedback" | "project" | "reference";
|
|
20
23
|
/** The four values {@link MemoryKind} admits, for runtime validation at the storage boundary. */
|
|
21
24
|
export declare const MEMORY_KINDS: readonly MemoryKind[];
|
|
22
25
|
export interface MemoryFact {
|
|
23
26
|
text: string;
|
|
27
|
+
/**
|
|
28
|
+
* A short concept name for this memory — what the index shows in its link, and what the file is
|
|
29
|
+
* named after.
|
|
30
|
+
*
|
|
31
|
+
* Optional because the common write path has only a sentence. When absent it is derived, and the
|
|
32
|
+
* derivation is mechanical on purpose: the interop partner's names are authored by a model that
|
|
33
|
+
* knows the subject, and a heuristic will not match that. An explicit field with a fallback is
|
|
34
|
+
* honest; a fallback presented as authorship is not.
|
|
35
|
+
*/
|
|
36
|
+
title?: string;
|
|
37
|
+
/**
|
|
38
|
+
* The one-line summary the index shows after the dash and the frontmatter carries.
|
|
39
|
+
*
|
|
40
|
+
* Absent means "same as `text`", which is what a single-sentence memory should produce.
|
|
41
|
+
*/
|
|
42
|
+
description?: string;
|
|
24
43
|
/**
|
|
25
44
|
* What this fact is (#389). Absent means untyped, which is what a hand-written bullet under
|
|
26
45
|
* `## Facts` stays — those files are already on disk in consumers' repositories and the store's
|
|
@@ -35,6 +54,15 @@ export interface MemoryFact {
|
|
|
35
54
|
* from four months ago. Absent on a fact written before this existed, or hand-added.
|
|
36
55
|
*/
|
|
37
56
|
modified?: string;
|
|
57
|
+
/**
|
|
58
|
+
* How many times this exact text has been recorded. Absent means one — uncorroborated.
|
|
59
|
+
*
|
|
60
|
+
* Gates CONFIDENCE, never presence: an uncorroborated fact is still recalled, and still
|
|
61
|
+
* reaches the model. It reaches it MARKED, so a single write cannot pass itself off as
|
|
62
|
+
* something the store has seen confirmed. Blocking it outright would break the system's
|
|
63
|
+
* central promise, which is that a fact written once is available in the next session.
|
|
64
|
+
*/
|
|
65
|
+
observations?: number;
|
|
38
66
|
}
|
|
39
67
|
export { redactSecrets } from "../security/index.js";
|
|
40
68
|
/**
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Selection for injection: rank the store and cut it, so what enters the prompt stops
|
|
3
|
+
* tracking what is on disk.
|
|
4
|
+
*
|
|
5
|
+
* Before this, `readMemoryForSend` returned every fact and the system prompt carried all of
|
|
6
|
+
* them, every turn. Measured across 99 real stores, injected bytes correlated with entry
|
|
7
|
+
* count at r = 0.958 — roughly 1,060 tokens per entry, crossing the 60 KB session budget at
|
|
8
|
+
* 16 facts. Ten stores were already past it; the largest injected ~72.6K tokens per turn,
|
|
9
|
+
* before the user's first message. That is not a ranking-quality problem, it is arithmetic.
|
|
10
|
+
*
|
|
11
|
+
* Ranking uses `modified`, which the store has always stamped, parsed and typed — and never
|
|
12
|
+
* read. The field's own doc comment says what it is for: "the whole point is to weigh a note
|
|
13
|
+
* from this morning against one from four months ago."
|
|
14
|
+
*
|
|
15
|
+
* WHY TWO BUCKETS, and not `(a.modified ?? "").localeCompare(...)`:
|
|
16
|
+
*
|
|
17
|
+
* A fact without `modified` is not old, it is UNDATED — written before the field existed, or
|
|
18
|
+
* hand-added to `MEMORY.md` by someone the store's own header invites to edit it. Sorting it
|
|
19
|
+
* as if it were from 1970 is inference wearing the costume of a default, and this codebase
|
|
20
|
+
* already rejects that reasoning one field over: "A kind is never INFERRED. A wrong kind is
|
|
21
|
+
* worse than none." The same holds here. So undated facts get a guaranteed share of the
|
|
22
|
+
* budget instead of a fabricated timestamp that buries them.
|
|
23
|
+
*/
|
|
24
|
+
import type { MemoryFact } from "../../memory/types.js";
|
|
25
|
+
/** Defaults are the session budget of the recall contract, not tuning knobs found by trial. */
|
|
26
|
+
export declare const DEFAULT_MAX_ENTRIES = 10;
|
|
27
|
+
/**
|
|
28
|
+
* Characters per token, measured over the real memory corpus (99 stores, 685 entries).
|
|
29
|
+
* Named rather than folded into the byte cap, because the first version of this file did fold
|
|
30
|
+
* it in and got the cap wrong: the contract's budget is 15,000 TOKENS, "60 KB" was the
|
|
31
|
+
* rounded-off char figure someone wrote next to it, and 60 * 1024 chars is 16,605 tokens —
|
|
32
|
+
* 11% over the ceiling it claimed to enforce. A budget stated in one unit and enforced in
|
|
33
|
+
* another is a budget nobody is enforcing.
|
|
34
|
+
*/
|
|
35
|
+
export declare const CHARS_PER_TOKEN = 3.7;
|
|
36
|
+
/** The recall contract's per-session ceiling, in the unit the contract states it in. */
|
|
37
|
+
export declare const DEFAULT_MAX_TOKENS = 15000;
|
|
38
|
+
/** Derived, never hand-rounded. */
|
|
39
|
+
export declare const DEFAULT_MAX_BYTES: number;
|
|
40
|
+
/** Share of `maxEntries` reserved for undated facts before dated ones may claim the rest. */
|
|
41
|
+
export declare const DEFAULT_UNDATED_SHARE = 0.5;
|
|
42
|
+
export interface SelectFactsOptions {
|
|
43
|
+
maxEntries?: number;
|
|
44
|
+
maxBytes?: number;
|
|
45
|
+
undatedShare?: number;
|
|
46
|
+
/**
|
|
47
|
+
* The turn's text. When given, facts are ranked by lexical relevance to it, fused with
|
|
48
|
+
* recency. When absent, ranking falls back to recency alone.
|
|
49
|
+
*
|
|
50
|
+
* Measured (T3, live model, 25-fact store): with recency-only ranking, a fact that answers
|
|
51
|
+
* the question is MISSED when it is the oldest entry and RECALLED when it is the newest.
|
|
52
|
+
* Recency is not relevance, and a store only has to outgrow the cap once for the difference
|
|
53
|
+
* to decide whether the agent can answer.
|
|
54
|
+
*/
|
|
55
|
+
query?: string;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Rank and cut. Returns at most `maxEntries` facts totalling at most `maxBytes`.
|
|
59
|
+
*
|
|
60
|
+
* Neither bucket starves the other: a quota one bucket cannot fill is available to the
|
|
61
|
+
* other, so a store of only dated facts still fills the whole budget.
|
|
62
|
+
*/
|
|
63
|
+
export declare function selectFactsForInjection(facts: readonly MemoryFact[], options?: SelectFactsOptions): MemoryFact[];
|
|
@@ -1 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
import type { SystemPromptAssemblyContext, SystemPromptProvider } from "../types.js";
|
|
2
|
+
export declare class MemoryPromptProvider implements SystemPromptProvider {
|
|
3
|
+
readonly id = "memory";
|
|
4
|
+
readonly priority = 30;
|
|
5
|
+
contribute(ctx: SystemPromptAssemblyContext): Promise<string | undefined>;
|
|
6
|
+
}
|
package/dist/workflow.cjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var chunk6SBW4QR2_cjs = require('./chunk-6SBW4QR2.cjs');
|
|
4
4
|
var chunkXWL6O3SW_cjs = require('./chunk-XWL6O3SW.cjs');
|
|
5
5
|
require('./chunk-VTYY7XL5.cjs');
|
|
6
6
|
require('./chunk-WJVHMTKB.cjs');
|
|
@@ -15,35 +15,35 @@ require('./chunk-NUKRL3I6.cjs');
|
|
|
15
15
|
|
|
16
16
|
Object.defineProperty(exports, "Workflow", {
|
|
17
17
|
enumerable: true,
|
|
18
|
-
get: function () { return
|
|
18
|
+
get: function () { return chunk6SBW4QR2_cjs.Workflow; }
|
|
19
19
|
});
|
|
20
20
|
Object.defineProperty(exports, "WorkflowBuilder", {
|
|
21
21
|
enumerable: true,
|
|
22
|
-
get: function () { return
|
|
22
|
+
get: function () { return chunk6SBW4QR2_cjs.WorkflowBuilder; }
|
|
23
23
|
});
|
|
24
24
|
Object.defineProperty(exports, "WorkflowToolError", {
|
|
25
25
|
enumerable: true,
|
|
26
|
-
get: function () { return
|
|
26
|
+
get: function () { return chunk6SBW4QR2_cjs.WorkflowToolError; }
|
|
27
27
|
});
|
|
28
28
|
Object.defineProperty(exports, "agentStep", {
|
|
29
29
|
enumerable: true,
|
|
30
|
-
get: function () { return
|
|
30
|
+
get: function () { return chunk6SBW4QR2_cjs.agentStep; }
|
|
31
31
|
});
|
|
32
32
|
Object.defineProperty(exports, "cloneWorkflow", {
|
|
33
33
|
enumerable: true,
|
|
34
|
-
get: function () { return
|
|
34
|
+
get: function () { return chunk6SBW4QR2_cjs.cloneWorkflow; }
|
|
35
35
|
});
|
|
36
36
|
Object.defineProperty(exports, "fn", {
|
|
37
37
|
enumerable: true,
|
|
38
|
-
get: function () { return
|
|
38
|
+
get: function () { return chunk6SBW4QR2_cjs.fn; }
|
|
39
39
|
});
|
|
40
40
|
Object.defineProperty(exports, "workflowAsTool", {
|
|
41
41
|
enumerable: true,
|
|
42
|
-
get: function () { return
|
|
42
|
+
get: function () { return chunk6SBW4QR2_cjs.workflowAsTool; }
|
|
43
43
|
});
|
|
44
44
|
Object.defineProperty(exports, "workflowStep", {
|
|
45
45
|
enumerable: true,
|
|
46
|
-
get: function () { return
|
|
46
|
+
get: function () { return chunk6SBW4QR2_cjs.workflowStep; }
|
|
47
47
|
});
|
|
48
48
|
Object.defineProperty(exports, "WorkflowAlreadyRunningError", {
|
|
49
49
|
enumerable: true,
|
package/dist/workflow.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { Workflow, WorkflowBuilder, WorkflowToolError, agentStep, cloneWorkflow, fn, workflowAsTool, workflowStep } from './chunk-
|
|
1
|
+
export { Workflow, WorkflowBuilder, WorkflowToolError, agentStep, cloneWorkflow, fn, workflowAsTool, workflowStep } from './chunk-FUL2I7G5.js';
|
|
2
2
|
export { WorkflowAlreadyRunningError, WorkflowCompensateNotImplementedError, WorkflowDuplicateStepIdError, WorkflowInputError, WorkflowMaxIterationsExceededError, WorkflowNestedError, WorkflowNotSerializableError, WorkflowOutputError, WorkflowParallelError, WorkflowResumeStepNotFoundError, WorkflowSnapshotNotFoundError, WorkflowStateError, __resetSnapshotStoresForTests } from './chunk-AQLGBKNT.js';
|
|
3
3
|
import './chunk-HY66GLM6.js';
|
|
4
4
|
import './chunk-4VPXM6UU.js';
|
package/docs/error-codes.md
CHANGED
|
@@ -6,7 +6,7 @@ Branch on `code`, never on the message: messages carry context (an id, a path, a
|
|
|
6
6
|
|
|
7
7
|
**Transport codes vs the rest.** `ErrorCode` in `errors.ts` is the small canonical union a provider failure maps onto — the codes marked *transport* below. Everything else is raised by a specific subsystem at a specific place, and a `catch` that only handles the union will meet them anyway.
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
205 distinct code(s).
|
|
10
10
|
|
|
11
11
|
| Code | Kind | Raised by | Sites |
|
|
12
12
|
|---|---|---|---|
|
|
@@ -88,7 +88,7 @@ Branch on `code`, never on the message: messages carry context (an id, a path, a
|
|
|
88
88
|
| `invalid_input` | domain | MemoryAdapterError | `packages/memory-honcho/src/adapter.ts:98` +9 |
|
|
89
89
|
| `invalid_max_iterations` | domain | ConfigurationError | `packages/sdk/src/internal/local-agent/real-local-run.ts:215` |
|
|
90
90
|
| `invalid_memory_backend` | domain | ConfigurationError | `packages/sdk/src/internal/memory/index-manager-dispatch.ts:24` +1 |
|
|
91
|
-
| `invalid_memory_kind` | domain | ConfigurationError | `packages/sdk/src/internal/memory/storage/markdown-store.ts:
|
|
91
|
+
| `invalid_memory_kind` | domain | ConfigurationError | `packages/sdk/src/internal/memory/storage/markdown-store.ts:233` |
|
|
92
92
|
| `invalid_model_selection` | domain | ConfigurationError | `packages/sdk/src/internal/runtime/model-selection.ts:21` |
|
|
93
93
|
| `invalid_request` | transport | — | `packages/sdk/src/internal/error-mappers/vertex.ts:52` +1 |
|
|
94
94
|
| `invalid_retry_config` | domain | ConfigurationError | `packages/sdk/src/internal/runtime/retry/with-retry.ts:67` |
|
|
@@ -114,6 +114,7 @@ Branch on `code`, never on the message: messages carry context (an id, a path, a
|
|
|
114
114
|
| `memory_context_missing_user_id` | domain | ConfigurationError | `packages/sdk/src/internal/local-agent/local-agent-memory-direct.ts:128` |
|
|
115
115
|
| `memory_path_escapes_root` | domain | ConfigurationError | `packages/sdk/src/internal/memory/tools.ts:109` +1 |
|
|
116
116
|
| `memory_path_traversal` | domain | ConfigurationError | `packages/sdk/src/internal/runtime/validation/validate-agent-options.ts:280` |
|
|
117
|
+
| `memory_threat_rejected` | domain | ConfigurationError | `packages/sdk/src/internal/memory/storage/markdown-store.ts:250` |
|
|
117
118
|
| `memory_tool_bad_args` | domain | ConfigurationError | `packages/sdk/src/internal/memory/tools.ts:132` +1 |
|
|
118
119
|
| `migration_destination_exists` | domain | ConfigurationError | `packages/sdk/src/internal/memory/migrate-sqlite-to-lance.ts:114` +1 |
|
|
119
120
|
| `missing_api_key` | domain | AuthenticationError, ConfigurationError | `packages/sdk/src/agent-helpers.ts:217` +2 |
|
|
@@ -4,7 +4,7 @@ Every public symbol the TheoKit workspace publishes, and the exact specifier to
|
|
|
4
4
|
|
|
5
5
|
A symbol listed under two specifiers is reachable from both, but that does NOT make the two interchangeable: a class emitted separately into a subpath entry is a distinct nominal type from the one in the root bundle, so passing one where the other is expected fails on a private field. When a symbol appears twice, import it and everything it is passed to from the SAME specifier.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
1137 export(s) across 46 entry point(s).
|
|
8
8
|
|
|
9
9
|
## `@theokit/acp`
|
|
10
10
|
|
|
@@ -527,8 +527,8 @@ A symbol listed under two specifiers is reachable from both, but that does NOT m
|
|
|
527
527
|
| `ActiveMemoryStatus` | type | Outcome of one recall attempt. |
|
|
528
528
|
| `ActiveMemoryTranscript` | interface | Optional on-disk persistence for Active Memory recall transcripts (ADR D6). |
|
|
529
529
|
| `appendDiaryEntry` | function | Append one entry to the dream diary, creating the file with a `# Dream Diary` header when it does not exist yet. |
|
|
530
|
-
| `appendFact` | function |
|
|
531
|
-
| `appendFactToMarkdown` | function |
|
|
530
|
+
| `appendFact` | function | Record a fact, honouring the `enabled` gate on {@link MemoryConfig } : when memory is disabled the call resolves without touching disk. |
|
|
531
|
+
| `appendFactToMarkdown` | function | Write a fact as its own memory file and point the `MEMORY.md` index at it. |
|
|
532
532
|
| `assertValidBackend` | function | EC-1: runtime guard for `opts.backend`. |
|
|
533
533
|
| `azureOpenAiMemoryEmbeddingProviderAdapter` | const | Azure OpenAI embeddings. |
|
|
534
534
|
| `buildErrorMetadata` | function | Build an `ErrorMetadata` object with all optional fields included conditionally (no `undefined` keys in the output). |
|
|
@@ -538,6 +538,7 @@ A symbol listed under two specifiers is reachable from both, but that does NOT m
|
|
|
538
538
|
| `ChunkMarkdownOptions` | interface | Split a markdown document into semantically meaningful chunks (ADR D1 of memory-system-peer-project-parity). |
|
|
539
539
|
| `CircuitBreaker` | class | Stops calling a recall path that keeps timing out. |
|
|
540
540
|
| `CircuitBreakerOptions` | interface | Consecutive-timeout circuit breaker for Active Memory recall. |
|
|
541
|
+
| `claudeProjectMemoryDir` | function | Where the Claude Code CLI keeps THIS project's memories. |
|
|
541
542
|
| `Cluster` | interface | A group of facts the REM phase judged related. |
|
|
542
543
|
| `ClusterResult` | interface | What {@link remPhase } produced. |
|
|
543
544
|
| `cohereMemoryEmbeddingProviderAdapter` | const | Cohere embeddings. |
|
|
@@ -608,12 +609,13 @@ A symbol listed under two specifiers is reachable from both, but that does NOT m
|
|
|
608
609
|
| `MemoryFileEntry` | interface | Lightweight reference to a markdown file in the memory corpus. |
|
|
609
610
|
| `MemoryGetToolOptions` | interface | Options for {@link createMemoryGetTool } . |
|
|
610
611
|
| `MemoryIndex` | interface | The four operations both backends implement, and the type every consumer should hold. |
|
|
611
|
-
| `memoryMdPath` | function | Path to `MEMORY.md`, the
|
|
612
|
+
| `memoryMdPath` | function | Path to `MEMORY.md`, the index that points at the per-memory files — and, in stores written before #389, the flat `## Facts` list itself. |
|
|
612
613
|
| `MemoryReadResult` | interface | Result of `reader.readFile`. |
|
|
613
614
|
| `MemorySearchHit` | interface | Memory index manager contract — leaf types shared by `index-manager.ts` (orchestrator), `index-manager-dispatch.ts` (backend dispatch), `lance-memory-adapter.ts` (Lance backend), and `memory-index.... |
|
|
614
615
|
| `MemorySearchToolOptions` | interface | Options for {@link createMemorySearchTool } . |
|
|
615
616
|
| `MemoryTool` | interface | A memory tool ready to hand to the agent loop: the JSON-serialisable description an LLM sees, plus the `execute` that runs it. |
|
|
616
617
|
| `MemoryToolJson` | interface | Memory tools (`memory_search` + `memory_get`) — ADR D5 of memory-system-peer-project-parity. |
|
|
618
|
+
| `memoryWriteDir` | function | Where a NEW fact should be written. |
|
|
617
619
|
| `META_KEY_DIMENSION` | const | `meta` table key holding the vector width the `embeddings` vec0 table was created with. |
|
|
618
620
|
| `META_KEY_MODEL` | const | `meta` table key holding the embedding model id the vectors were produced with. |
|
|
619
621
|
| `META_KEY_PROVIDER_ID` | const | `meta` table key holding the id of the embedding provider that produced the vectors currently on disk (for example `openai`, `ollama`). |
|
|
@@ -624,7 +626,7 @@ A symbol listed under two specifiers is reachable from both, but that does NOT m
|
|
|
624
626
|
| `MigrationResult` | interface | Outcome of {@link migrateLegacyJson } . |
|
|
625
627
|
| `mistralMemoryEmbeddingProviderAdapter` | const | Mistral embeddings, over the standard OpenAI wire. |
|
|
626
628
|
| `NoteFile` | interface | One note discovered under `notes/`: its file name without the `.md` suffix, and its absolute path. |
|
|
627
|
-
| `notesDir` | function | Path to `<memory root>/notes`, where per-topic notes and the consolidated notes
|
|
629
|
+
| `notesDir` | function | Path to `<memory root>/notes`, where per-topic notes and the consolidated notes a dreaming sweep writes live. |
|
|
628
630
|
| `ollamaMemoryEmbeddingProviderAdapter` | const | Embeddings from a local Ollama instance — the only adapter in the catalog with `transport: "local"`, and the one to choose when the corpus must not leave the machine or when there is no API key to ... |
|
|
629
631
|
| `OpenAiCompatibleConfig` | interface | What one provider adapter tells {@link createOpenAiCompatibleRuntime } about its wire: where to POST, which environment variables carry the key and the base URL, which model to use by default, and ... |
|
|
630
632
|
| `openAiMemoryEmbeddingProviderAdapter` | const | OpenAI embeddings — `text-embedding-3-small` (1536), `text-embedding-3-large` (3072) and `text-embedding-ada-002` (1536). |
|
|
@@ -640,8 +642,8 @@ A symbol listed under two specifiers is reachable from both, but that does NOT m
|
|
|
640
642
|
| `persistActiveMemoryTranscript` | function | Write one recall transcript to `<memory root>/transcripts/active-memory/<runId>.json`, creating the parent directories and replacing the file atomically. |
|
|
641
643
|
| `PRAGMA_STATEMENTS` | const | Non-WAL pragmas. |
|
|
642
644
|
| `readEmbeddingIdentity` | function | Read the embedding identity recorded in the `meta` table. |
|
|
643
|
-
| `readFacts` | function |
|
|
644
|
-
| `readFactsFromMarkdown` | function |
|
|
645
|
+
| `readFacts` | function | Every memory in the store, honouring the `enabled` gate on {@link MemoryConfig } : when memory is disabled the call resolves to `[]` without touching disk. |
|
|
646
|
+
| `readFactsFromMarkdown` | function | Every memory in the store: the per-memory files, plus any legacy `## Facts` bullets still in `MEMORY.md`. |
|
|
645
647
|
| `ReadFileOptions` | interface | Inputs for {@link readMemoryFileBounded } . |
|
|
646
648
|
| `readMemoryFileBounded` | function | Read a bounded slice of a text file and report what was left behind. |
|
|
647
649
|
| `redactSecrets` | function | Canonical credential-redaction primitive (ADR D68). |
|
|
@@ -1003,6 +1005,20 @@ A symbol listed under two specifiers is reachable from both, but that does NOT m
|
|
|
1003
1005
|
| `LruEmbeddingCache` | class | Bounded in-memory LRU cache for embeddings, keyed by `sha256(text)` (or any stable key the caller chooses). |
|
|
1004
1006
|
| `OpenAiCompatibleConfig` | interface | What one provider adapter tells {@link createOpenAiCompatibleRuntime } about its wire: where to POST, which environment variables carry the key and the base URL, which model to use by default, and ... |
|
|
1005
1007
|
|
|
1008
|
+
## `@theokit/sdk/internal/memory-store`
|
|
1009
|
+
|
|
1010
|
+
| Symbol | Kind | Summary |
|
|
1011
|
+
|---|---|---|
|
|
1012
|
+
| `appendFact` | function | Record a fact, honouring the `enabled` gate on {@link MemoryConfig } : when memory is disabled the call resolves without touching disk. |
|
|
1013
|
+
| `appendFactToMarkdown` | function | Write a fact as its own memory file and point the `MEMORY.md` index at it. |
|
|
1014
|
+
| `claudeProjectMemoryDir` | function | Where the Claude Code CLI keeps THIS project's memories. |
|
|
1015
|
+
| `memoryDir` | function | The memory root for a workspace: `<cwd>/.theokit/memory`. |
|
|
1016
|
+
| `memoryMdPath` | function | Path to `MEMORY.md`, the index that points at the per-memory files — and, in stores written before #389, the flat `## Facts` list itself. |
|
|
1017
|
+
| `memoryWriteDir` | function | Where a NEW fact should be written. |
|
|
1018
|
+
| `notesDir` | function | Path to `<memory root>/notes`, where per-topic notes and the consolidated notes a dreaming sweep writes live. |
|
|
1019
|
+
| `readFacts` | function | Every memory in the store, honouring the `enabled` gate on {@link MemoryConfig } : when memory is disabled the call resolves to `[]` without touching disk. |
|
|
1020
|
+
| `readFactsFromMarkdown` | function | Every memory in the store: the per-memory files, plus any legacy `## Facts` bullets still in `MEMORY.md`. |
|
|
1021
|
+
|
|
1006
1022
|
## `@theokit/sdk/internal/persistence`
|
|
1007
1023
|
|
|
1008
1024
|
| Symbol | Kind | Summary |
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
# Memory subsystem — decisions that are not obvious from the code
|
|
2
|
+
|
|
3
|
+
Five decisions that a reader would otherwise be right to call bugs. Each names where the behaviour
|
|
4
|
+
lives, so the next person to touch it can disagree on purpose rather than "fix" it by accident.
|
|
5
|
+
|
|
6
|
+
Written after a conformance audit against an external memory contract found that three of these
|
|
7
|
+
existed only in commit messages and one only in a conversation. A decision nobody can find is a
|
|
8
|
+
decision the next refactor deletes.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## 1. Running an agent creates `.theokit/`, even when memory goes to the Claude Code directory
|
|
13
|
+
|
|
14
|
+
**What happens.** Reading a Claude Code project creates nothing. Running an agent creates:
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
.theokit/agents/registry.json on Agent.create
|
|
18
|
+
.theokit/memory/.index/memory.sqlite on the first send, when memory is enabled
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
**Why it looks wrong.** With `local.sessionDir` set, memory *facts* are written to
|
|
22
|
+
`<sessionDir>/projects/<encoded-cwd>/memory/` — where the CLI reads them. So a project that never
|
|
23
|
+
adopted this SDK ends up with a `.theokit/` directory anyway, which reads like the "write where the
|
|
24
|
+
CLI reads" promise leaking.
|
|
25
|
+
|
|
26
|
+
**Why it is right.** What lands in `.theokit/` is this SDK's own state, and neither piece has a
|
|
27
|
+
Claude Code shape to be written in:
|
|
28
|
+
|
|
29
|
+
- `agents/registry.json` is the live-agent address book (`internal/runtime/registry/agent-registry.ts`).
|
|
30
|
+
The CLI has no equivalent.
|
|
31
|
+
- `memory/.index/memory.sqlite` is the search index (`sdk-memory/internal/index/index-db.ts:84-91`).
|
|
32
|
+
**The CLI has no index format**, so there is no CLI location that could hold it. Putting it beside
|
|
33
|
+
the facts in the CLI's directory would put a binary artefact the CLI does not understand inside a
|
|
34
|
+
directory the CLI manages.
|
|
35
|
+
|
|
36
|
+
The facts — the thing a user recorded and would lose — go where the CLI reads. The index is derived
|
|
37
|
+
data that can be rebuilt from them.
|
|
38
|
+
|
|
39
|
+
**How to check it still holds:** `tests/claude-code-e2e-compat.test.ts`, the pair
|
|
40
|
+
`test_reading_a_cli_project_creates_nothing_in_it` /
|
|
41
|
+
`test_running_an_agent_creates_theokit_and_only_sdk_state_in_it`. The second exists so the first is
|
|
42
|
+
not vacuous — it asserts the directory contains *only* `agents` and `memory`.
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## 2. Four memory kinds, and a kind is never inferred
|
|
47
|
+
|
|
48
|
+
`internal/memory/types.ts:32` declares `user | feedback | project | reference`, validated at the
|
|
49
|
+
storage boundary (`internal/memory/storage/markdown-store.ts:195`, `invalid_memory_kind`).
|
|
50
|
+
|
|
51
|
+
**Known divergence.** An external contract this SDK was audited against specifies **nine** kinds in
|
|
52
|
+
three retention buckets (atomic / consolidatable / lifecycle-managed). This SDK has four.
|
|
53
|
+
|
|
54
|
+
**Why it has not been widened.** The nine exist to drive differentiated retention, and **this SDK has
|
|
55
|
+
no retention at all** — no TTL, no pruning, no decay (searched `ttl|prune|expire|retention` across
|
|
56
|
+
both packages; the only hits are the recall result cache, which expires *results*, not entries).
|
|
57
|
+
Nine names over a regime where nothing expires are nine names for one behaviour. Retention has to
|
|
58
|
+
exist before a vocabulary that differentiates it is worth anything.
|
|
59
|
+
|
|
60
|
+
> **One of this argument's two legs has since been removed, and the note stays so the argument
|
|
61
|
+
> cannot be defended with it later.** An earlier version of this section also said "and no buckets".
|
|
62
|
+
> That stopped being true in `a655ac4d`: `dreaming/phases.ts:48-49` now has `ATOMIC_KINDS` and
|
|
63
|
+
> `CONSOLIDATABLE_KINDS`, and `dedupPolicy` grades three levels by them. The buckets exist; what
|
|
64
|
+
> still does not exist is the retention they would govern.
|
|
65
|
+
|
|
66
|
+
**The stronger reason, found after the first one weakened: the four are not a subset, they are the
|
|
67
|
+
partner's vocabulary.** This store's format is shared with the Claude Code CLI, and every memory the
|
|
68
|
+
CLI has written on one developer machine — 688 files, 2026-08 — carries one of exactly these four:
|
|
69
|
+
|
|
70
|
+
```
|
|
71
|
+
425 project 167 feedback 74 reference 7 user (15 with no type)
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Zero others. Adding a fifth value means writing files whose `type` the interop partner has never
|
|
75
|
+
emitted. That is evidence about what it WRITES, not proof about what it ACCEPTS on read — but it
|
|
76
|
+
means there is no precedent, and the burden belongs to whoever adds one unilaterally.
|
|
77
|
+
|
|
78
|
+
**`failure_heuristic` is the one the retention argument does not cover, and the interop argument
|
|
79
|
+
does.** It changes *what is written* (a trigger→resolution pair) rather than how an entry expires, so
|
|
80
|
+
"no retention to differentiate" says nothing about it — and nothing has adopted it (`grep
|
|
81
|
+
failure_heuristic packages/sdk/src` → zero). It is not declined on retention grounds; it is held by
|
|
82
|
+
the same shared-format question as the other four, and unblocking it means establishing what the CLI
|
|
83
|
+
does with a `type` it does not emit.
|
|
84
|
+
|
|
85
|
+
**The part that is not negotiable:** a kind is never inferred (`types.ts:29`). A wrong kind is worse
|
|
86
|
+
than none, because it makes retention and recall confident about the wrong thing.
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## 3. Re-recording a fact overwrites it; there is no `Invalidated` state
|
|
91
|
+
|
|
92
|
+
`markdown-store.ts:204` writes `<slug>.md` through `replaceFileAtomic`, and `nextIndex`
|
|
93
|
+
(`:222-235`) keeps exactly one index line per name.
|
|
94
|
+
|
|
95
|
+
**Known divergence.** The same external contract requires a contradiction to produce an
|
|
96
|
+
`Invalidated` entry plus a supersession chain, not an overwrite.
|
|
97
|
+
|
|
98
|
+
**Why the index is right as it is.** The index is a map from memory to file. Two lines for one file
|
|
99
|
+
is a map that disagrees with itself, and the CLI reads that index.
|
|
100
|
+
|
|
101
|
+
**What the entry is NAMED by changed, and it is worth knowing why.** The slug and the index title
|
|
102
|
+
used to be the fact's whole sentence. They are now a short topic name — `MemoryFact` carries optional
|
|
103
|
+
`title` and `description` so a writer can author them, and derives them when it does not. The
|
|
104
|
+
derivation is mechanical on purpose and does not pretend to be authorship, the same rule this store
|
|
105
|
+
applies to `kind`.
|
|
106
|
+
|
|
107
|
+
**Two distinct facts that share a subject now coexist; they used to overwrite each other.** A topic
|
|
108
|
+
slug is a lossy summary, and lossy summaries collide: `"fact A"`, `"fact B"` and `"fact C"` all
|
|
109
|
+
derive `fact`. Naming by the whole sentence made collisions rare by accident; naming by subject made
|
|
110
|
+
them ordinary. `resolveName` (`markdown-store.ts:280`) settles it by the only thing that can — the
|
|
111
|
+
text: the same text keeps the same file and increments corroboration, different text takes
|
|
112
|
+
`topic-2`. Found by the golden `multiple appends each get a file`, not by review — the failure was
|
|
113
|
+
silent data loss, and nothing about the reasoning would have surfaced it.
|
|
114
|
+
|
|
115
|
+
So the section heading stays true for what it describes, re-recording the *same* fact, and the case
|
|
116
|
+
it never covered is now covered.
|
|
117
|
+
|
|
118
|
+
That change closed #446, where a passphrase the model had just refused to store was written into the
|
|
119
|
+
**filename** and the index line. The reason it works is that it is not a rule about secrets: a rule
|
|
120
|
+
about secrets has to recognise one, and `redactSecrets` had already demonstrated it does not
|
|
121
|
+
recognise `sirius-zzq417`. Naming the memory by its **subject** drops the tail of the sentence
|
|
122
|
+
whatever the tail happens to be. Closed by construction, not by detection — which is the only kind
|
|
123
|
+
of closure available when the dangerous input is indistinguishable from a safe one.
|
|
124
|
+
|
|
125
|
+
**Why this is still a gap.** "The index names the current entry" and "the store keeps the
|
|
126
|
+
supersession chain" are not in conflict — they are different files. The resolution, when it is
|
|
127
|
+
built, is the index pointing at the current entry while the entry file carries its own chain. What
|
|
128
|
+
exists today is only the first half.
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## 4. The session transcript is a DAG, not a linear log
|
|
133
|
+
|
|
134
|
+
`internal/persistence/session-transcript.ts:4-11,77-78` — records carry `uuid`/`parentUuid`, and
|
|
135
|
+
`appendCompactBoundary` starts a new root.
|
|
136
|
+
|
|
137
|
+
**Deliberate and forced.** The format IS the Claude Code record shape. Bidirectional CLI
|
|
138
|
+
compatibility is a product requirement, so a linear session model is not available to choose: it
|
|
139
|
+
would make transcripts this SDK writes unreadable by the CLI and vice versa.
|
|
140
|
+
|
|
141
|
+
Where an external contract prescribes a linear session model, this is a documented middle-ground —
|
|
142
|
+
the contract is deciding a question that a SDK with an imposed wire format does not get to answer.
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
## 5. Recall is lexical by default; dense vectors only when a consumer asks for them
|
|
147
|
+
|
|
148
|
+
Two different things are called recall:
|
|
149
|
+
|
|
150
|
+
- **Default.** `internal/local-agent/local-agent-send.ts:257-272` reads the store, then ranks and
|
|
151
|
+
cuts it through `selectFactsForInjection` — lexical relevance against the user's message, fused
|
|
152
|
+
with recency, capped at 10 entries and a byte budget derived from 15,000 tokens. No embeddings on
|
|
153
|
+
this path at all (`runtime/memory/select-facts.ts:117-118`).
|
|
154
|
+
- **The `memory_search` tool.** Its index is NOT opt-in: `local-agent-memory.ts:82-98` opens
|
|
155
|
+
`IndexManager` whenever `memory.enabled` is true. The dense vectors are — `maybeCreateEmbeddingRuntime`
|
|
156
|
+
(`:198-200`) returns `undefined` unless `memory.index.embedding` is configured, so by default the
|
|
157
|
+
index runs text-only. The `vectorWeight ?? 0.6` blend at `index-manager-helpers.ts:21` weighs a
|
|
158
|
+
vector that, unconfigured, was never computed.
|
|
159
|
+
|
|
160
|
+
So no dense vector participates in scoring under a default configuration. One does only when a
|
|
161
|
+
consumer names an embedding provider.
|
|
162
|
+
|
|
163
|
+
**Known divergence,** on the opt-in path: the external contract forbids dense vectors in agent-memory
|
|
164
|
+
recall below a measured threshold (>1000 entries *and* demonstrated lexical degradation). Neither has
|
|
165
|
+
been measured here.
|
|
166
|
+
|
|
167
|
+
**The bigger gap WAS the default path, and it was not this divergence.** A contract arbitrating
|
|
168
|
+
*between* retrieval methods does not cover the absence of one — and until `721a311f` there was no
|
|
169
|
+
method on this path at all. That is fixed; the history is kept because the failure mode is worth
|
|
170
|
+
recognising again, and because it explains why the ranking signal has to depend on the question. A
|
|
171
|
+
first version cut by recency alone, and a live run showed the answering fact dropped for being old
|
|
172
|
+
rather than irrelevant. Bounding cost and choosing what survives are two jobs, and a cap does only
|
|
173
|
+
the first.
|
|
174
|
+
|
|
175
|
+
**What that default path used to inject, measured 2026-08 against the published artefact.** 100 real stores on one developer machine, 687
|
|
176
|
+
entries, ~3.2 KB per entry. Feeding the largest — 66 entries, 275 KB on disk — to
|
|
177
|
+
`readFactsFromMarkdown` returns all 66 and **246K characters, roughly 66K tokens injected into every
|
|
178
|
+
turn's system prompt**, before the user has said anything. Nine more stores are already past the
|
|
179
|
+
point where the injection exceeds a session budget.
|
|
180
|
+
|
|
181
|
+
Two things make this worse than a size problem. The CLI's store is read **whether or not
|
|
182
|
+
`local.sessionDir` is set**, so a consumer who never opted into CLI interop still gets it. And the
|
|
183
|
+
cost is invisible: nothing errors, the window just fills, and the tokens are not attributed to
|
|
184
|
+
memory. An earlier version of this document said "at 64 entries it does not hurt" — that was an
|
|
185
|
+
assumption, and measuring it is what disproved it.
|
|
186
|
+
|
|
187
|
+
`metadata.modified` is already written (`markdown-store.ts:210`) and parsed (`:177`) and consumed by
|
|
188
|
+
nothing — the data for a staleness signal is on disk today, unused.
|
|
189
|
+
|
|
190
|
+
---
|
|
191
|
+
|
|
192
|
+
## Known gaps, recorded so they are not rediscovered
|
|
193
|
+
|
|
194
|
+
Each row says what would close it, not only that it is open — the two invite very different work.
|
|
195
|
+
|
|
196
|
+
| gap | where | what would close it |
|
|
197
|
+
|---|---|---|
|
|
198
|
+
| ~~The default recall path has no selection at all~~ — **closed** in `721a311f` | `local-agent-send.ts:257-272` | Closed: lexical relevance + recency, capped. Measured end to end against the real model — a 67-entry store went from ~66K to **13,606 tokens on the wire**, and the answering fact is recalled whether it is the oldest or the newest entry. |
|
|
199
|
+
| The CLI's store is read whether or not `local.sessionDir` is set | `markdown-store.ts:114` — `claudeProjectMemoryDir(cwd)` is an unconditional read root | A decision, not a fix: it is deliberate (*write one, read all*, so a consumer's existing memories are never orphaned) and it means a consumer who never opted into CLI interop still receives what the CLI accumulated in that project. Cheap while the cap holds; revisit if the cap is ever removed. |
|
|
200
|
+
| No retention of any kind — no TTL, prune, or decay | searched `ttl\|prune\|expire\|retention` across both packages: zero | A per-kind TTL plus a prune step in the sweep. Needs the kind vocabulary to mean something first (§ 2), which is why this orders before widening it. |
|
|
201
|
+
| Quarantine marks but does not constrain | `memory-file.ts:96`, `memory-provider.ts:48` | Implemented: three states, and `[unconfirmed]` on entries the store counted once. **Measured against the real model and it does not close the hole** — a planted memory alone is acted on 5/5, and beside a corroborated contradiction it is still asserted ~62% of runs (n=32). Marking influences the model; it does not constrain it. The guarantee is not available at this layer: blocking an uncorroborated entry would break the promise that a fact written once is recallable next session. |
|
|
202
|
+
| A planted memory can make the agent ACT | measured: `RELEASE_OVERRIDE.txt` created in 2 of 6 runs | Register the permission layer — `PermissionPlugin.create(new PermissionEngine(…))` — which blocks it every time. This is the half that IS closable at the tool boundary; the informational half above is not. Any deployment where the memory directory is writable by anything other than this agent needs it. |
|
|
203
|
+
| `description` is written as a copy of the body | `markdown-store.ts:205-211` — the *reader* (`:174-177`) already handles a distinct description correctly | Stop writing one when nobody declared it. The role is a one-line recall aid; deriving it mechanically would be inferring the situation, which § 2's rule already forbids for `kind`. Absent is a valid state and the reader already falls back to the body. |
|
|
204
|
+
| The topic-name deriver filters English function words only | `memory-file.ts:73` | A store in another language keeps that language's function words in the slug and the index title — `de`, `do`, `para` survive, so the name is longer and noisier. **Never lossy:** collisions are caught by `resolveName` comparing text, not by the stopword list. Real stores here are bilingual, so this is a partial parity, not a complete one. Closes with a per-language list, or by not needing one. |
|
|
205
|
+
| The dream sweep never filters by kind before dedup | `dreaming/phases.ts:34` — `lightPhase` never reads `kind` | Partition by kind before `lightPhase`. Nothing is deleted today, but a consolidated note can blend two distinct entries, and the note is what search returns. |
|
|
206
|
+
| The dream sweep does not update the index | `dreaming/run.ts:53-96` writes `notes/` and never syncs | An `IndexManager.sync` after `writeConsolidatedNotes`. |
|
|
207
|
+
| Recall fires every turn, cached on query hash, not on store manifest | `internal/local-agent/local-agent-memory.ts:87`; `sdk-memory/internal/active-memory/active-memory-cache.ts:66` | A second skip condition beside the existing one — hash of the store manifest, so an unchanged store skips even when the question changes. The query cache is not wrong; it answers a different question. |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@theokit/sdk",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.61.0",
|
|
4
4
|
"description": "TypeScript SDK for the Theo agent harness — same surface, local or cloud.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"homepage": "https://github.com/usetheokit/theokit-sdk#readme",
|
|
@@ -288,6 +288,16 @@
|
|
|
288
288
|
"default": "./dist/internal/memory/adapters/index.cjs"
|
|
289
289
|
}
|
|
290
290
|
},
|
|
291
|
+
"./internal/memory-store": {
|
|
292
|
+
"import": {
|
|
293
|
+
"types": "./dist/internal/memory/storage/index.d.ts",
|
|
294
|
+
"default": "./dist/internal/memory/storage/index.js"
|
|
295
|
+
},
|
|
296
|
+
"require": {
|
|
297
|
+
"types": "./dist/internal/memory/storage/index.d.cts",
|
|
298
|
+
"default": "./dist/internal/memory/storage/index.cjs"
|
|
299
|
+
}
|
|
300
|
+
},
|
|
291
301
|
"./a2a": {
|
|
292
302
|
"import": {
|
|
293
303
|
"types": "./dist/a2a/index.d.ts",
|