@tangleai/context 0.21.1 → 0.25.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.
@@ -1,4 +1,3 @@
1
- //@ts-check
2
1
  /**
3
2
  * The ledger's four record schemas, as plain JSON Schema documents
4
3
  * compiled by `JarenValidator` when a ledger is constructed. A malformed
@@ -17,9 +16,7 @@
17
16
  * memory or skill may be proposed by a model (and must therefore carry
18
17
  * evidence), a slot is written by the harness and never proposed.
19
18
  */
20
-
21
- import { CLAIM_EVIDENCE_SCHEMA } from './evidence.js';
22
-
19
+ import { CLAIM_EVIDENCE_SCHEMA } from "./evidence.js";
23
20
  /**
24
21
  * A timestamp property: RFC 3339, the suite's only date representation.
25
22
  *
@@ -38,14 +35,12 @@ import { CLAIM_EVIDENCE_SCHEMA } from './evidence.js';
38
35
  * offset rather than banning it.
39
36
  */
40
37
  const AT = {
41
- type: 'string',
42
- format: 'date-time',
43
- pattern: '^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?(Z|[+-]\\d{2}:\\d{2})$',
38
+ type: 'string',
39
+ format: 'date-time',
40
+ pattern: '^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?(Z|[+-]\\d{2}:\\d{2})$',
44
41
  };
45
-
46
42
  /** A non-empty identifier. */
47
43
  const ID = { type: 'string', minLength: 1 };
48
-
49
44
  /**
50
45
  * A stored embedding: a plain array of numbers, never a typed array. A
51
46
  * `Float32Array` does not survive the storage boundary — JSON serializes
@@ -55,7 +50,6 @@ const ID = { type: 'string', minLength: 1 };
55
50
  * `@jarenjs/core/vector` in the same gate.
56
51
  */
57
52
  const EMBEDDING = { type: 'array', items: { type: 'number' }, minItems: 1 };
58
-
59
53
  /**
60
54
  * A vector's identity: which model produced it, at what width. Vectors
61
55
  * from two models are pairwise meaningless and compare into plausible
@@ -63,15 +57,14 @@ const EMBEDDING = { type: 'array', items: { type: 'number' }, minItems: 1 };
63
57
  * refuses to mix two.
64
58
  */
65
59
  const EMBEDDED_BY = {
66
- type: 'object',
67
- properties: {
68
- model: { type: 'string', minLength: 1 },
69
- dims: { type: 'integer', minimum: 1 },
70
- },
71
- required: ['model', 'dims'],
72
- additionalProperties: false,
60
+ type: 'object',
61
+ properties: {
62
+ model: { type: 'string', minLength: 1 },
63
+ dims: { type: 'integer', minimum: 1 },
64
+ },
65
+ required: ['model', 'dims'],
66
+ additionalProperties: false,
73
67
  };
74
-
75
68
  /**
76
69
  * `embedding` and `embeddedBy` are both-or-neither, and nothing requires
77
70
  * them: an un-embedded record is exactly as valid as it ever was. The
@@ -81,7 +74,6 @@ const EMBEDDED_BY = {
81
74
  * 2019-09 and later says the same thing).
82
75
  */
83
76
  const EMBEDDING_PAIR = { embedding: ['embeddedBy'], embeddedBy: ['embedding'] };
84
-
85
77
  /**
86
78
  * One active objective. Singular by construction: a second `setGoal`
87
79
  * supersedes this one and archives it, so "what am I doing" has exactly
@@ -92,49 +84,58 @@ const EMBEDDING_PAIR = { embedding: ['embeddedBy'], embeddedBy: ['embedding'] };
92
84
  * progress entry or decides when a goal is done.
93
85
  */
94
86
  export const GOAL_SCHEMA = {
95
- $id: 'https://jarenjs.github.io/schemas/ai/ledger-goal.json',
96
- type: 'object',
97
- properties: {
98
- objective: { type: 'string', minLength: 1 },
99
- createdAt: AT,
100
- status: { enum: ['active', 'done', 'abandoned', 'superseded'] },
101
- progress: {
102
- type: 'array',
103
- items: {
104
- type: 'object',
105
- properties: {
106
- at: AT,
107
- id: ID,
108
- note: { type: 'string', minLength: 1 },
109
- // an unevidenced progress note is a claim, not a record
110
- evidence: { type: 'string', minLength: 1 },
87
+ $id: 'https://jarenjs.github.io/schemas/ai/ledger-goal.json',
88
+ type: 'object',
89
+ properties: {
90
+ objective: { type: 'string', minLength: 1 },
91
+ createdAt: AT,
92
+ status: { enum: ['active', 'done', 'abandoned', 'superseded'] },
93
+ progress: {
94
+ type: 'array',
95
+ items: {
96
+ type: 'object',
97
+ properties: {
98
+ at: AT,
99
+ id: ID,
100
+ note: { type: 'string', minLength: 1 },
101
+ // an unevidenced progress note is a claim, not a record
102
+ evidence: { type: 'string', minLength: 1 },
103
+ },
104
+ required: ['at', 'note', 'evidence'],
105
+ additionalProperties: false,
106
+ },
107
+ },
108
+ checkpoint: {
109
+ type: 'object',
110
+ properties: {
111
+ version: { const: 1 },
112
+ records: {
113
+ type: 'array', items: {
114
+ type: 'object', properties: {
115
+ note: ID, evidence: ID,
116
+ }, required: ['note', 'evidence'], additionalProperties: false
117
+ }
118
+ },
119
+ sources: {
120
+ type: 'array', items: {
121
+ type: 'object', properties: {
122
+ id: ID, at: AT, record: { type: 'integer', minimum: 0 },
123
+ }, required: ['id', 'at', 'record'], additionalProperties: false
124
+ }
125
+ },
126
+ },
127
+ required: ['version', 'records', 'sources'], additionalProperties: false,
128
+ },
129
+ retention: {
130
+ type: 'object', properties: {
131
+ version: { const: 1 }, reason: { const: 'goal-budget' },
132
+ retired: { type: 'array', items: ID, uniqueItems: true },
133
+ }, required: ['version', 'reason', 'retired'], additionalProperties: false
111
134
  },
112
- required: ['at', 'note', 'evidence'],
113
- additionalProperties: false,
114
- },
115
- },
116
- checkpoint: {
117
- type: 'object',
118
- properties: {
119
- version: { const: 1 },
120
- records: { type: 'array', items: { type: 'object', properties: {
121
- note: ID, evidence: ID,
122
- }, required: ['note', 'evidence'], additionalProperties: false } },
123
- sources: { type: 'array', items: { type: 'object', properties: {
124
- id: ID, at: AT, record: { type: 'integer', minimum: 0 },
125
- }, required: ['id', 'at', 'record'], additionalProperties: false } },
126
- },
127
- required: ['version', 'records', 'sources'], additionalProperties: false,
128
135
  },
129
- retention: { type: 'object', properties: {
130
- version: { const: 1 }, reason: { const: 'goal-budget' },
131
- retired: { type: 'array', items: ID, uniqueItems: true },
132
- }, required: ['version', 'reason', 'retired'], additionalProperties: false },
133
- },
134
- required: ['objective', 'createdAt', 'status', 'progress'],
135
- additionalProperties: false,
136
+ required: ['objective', 'createdAt', 'status', 'progress'],
137
+ additionalProperties: false,
136
138
  };
137
-
138
139
  /**
139
140
  * A fact worth carrying past this context window.
140
141
  *
@@ -150,66 +151,63 @@ export const GOAL_SCHEMA = {
150
151
  * before, and ranked recall reports it as skipped rather than scoring it.
151
152
  */
152
153
  export const MEMORY_SCHEMA = {
153
- $id: 'https://jarenjs.github.io/schemas/ai/ledger-memory.json',
154
- type: 'object',
155
- properties: {
156
- id: ID,
157
- text: { type: 'string', minLength: 1 },
158
- evidence: { anyOf: [{ type: 'string', minLength: 1 }, CLAIM_EVIDENCE_SCHEMA] },
159
- tags: { type: 'array', items: { type: 'string', minLength: 1 } },
160
- at: AT,
161
- // OPTIONAL, as a pair: the vector `recall({ near })` ranks by, and
162
- // the identity that makes it comparable
163
- embedding: EMBEDDING,
164
- embeddedBy: EMBEDDED_BY,
165
- },
166
- required: ['id', 'text', 'evidence', 'tags', 'at'],
167
- dependencies: EMBEDDING_PAIR,
168
- additionalProperties: false,
154
+ $id: 'https://jarenjs.github.io/schemas/ai/ledger-memory.json',
155
+ type: 'object',
156
+ properties: {
157
+ id: ID,
158
+ text: { type: 'string', minLength: 1 },
159
+ evidence: { anyOf: [{ type: 'string', minLength: 1 }, CLAIM_EVIDENCE_SCHEMA] },
160
+ tags: { type: 'array', items: { type: 'string', minLength: 1 } },
161
+ at: AT,
162
+ // OPTIONAL, as a pair: the vector `recall({ near })` ranks by, and
163
+ // the identity that makes it comparable
164
+ embedding: EMBEDDING,
165
+ embeddedBy: EMBEDDED_BY,
166
+ },
167
+ required: ['id', 'text', 'evidence', 'tags', 'at'],
168
+ dependencies: EMBEDDING_PAIR,
169
+ additionalProperties: false,
169
170
  };
170
-
171
171
  /**
172
172
  * A reusable recipe: when it applies, what to do, and which tools it
173
173
  * needs. Retrieved like a memory and composed into a system prompt by
174
174
  * whatever drives the agent — the ledger stores it and nothing more.
175
175
  */
176
176
  export const PROGRAM_SKILL_SCHEMA = {
177
- type: 'object',
178
- properties: {
179
- version: { const: 1 },
180
- question: { type: 'string', minLength: 1 },
181
- fingerprint: { type: 'string', pattern: '^[a-f0-9]{64}$' },
182
- environmentId: { type: 'string', minLength: 1 },
183
- schemaVersion: { type: 'string', minLength: 1 },
184
- document: { type: 'object' },
185
- evidence: { type: 'string', minLength: 1 },
186
- checked: { const: true },
187
- },
188
- required: ['version', 'question', 'fingerprint', 'environmentId', 'schemaVersion', 'document', 'evidence', 'checked'],
189
- additionalProperties: false,
177
+ type: 'object',
178
+ properties: {
179
+ version: { const: 1 },
180
+ question: { type: 'string', minLength: 1 },
181
+ fingerprint: { type: 'string', pattern: '^[a-f0-9]{64}$' },
182
+ environmentId: { type: 'string', minLength: 1 },
183
+ schemaVersion: { type: 'string', minLength: 1 },
184
+ document: { type: 'object' },
185
+ evidence: { type: 'string', minLength: 1 },
186
+ checked: { const: true },
187
+ },
188
+ required: ['version', 'question', 'fingerprint', 'environmentId', 'schemaVersion', 'document', 'evidence', 'checked'],
189
+ additionalProperties: false,
190
190
  };
191
-
192
191
  export const SKILL_SCHEMA = {
193
- $id: 'https://jarenjs.github.io/schemas/ai/ledger-skill.json',
194
- type: 'object',
195
- properties: {
196
- id: ID,
197
- name: { type: 'string', minLength: 1 },
198
- when: { type: 'string', minLength: 1 },
199
- instructions: { type: 'string', minLength: 1 },
200
- program: PROGRAM_SKILL_SCHEMA,
201
- tools: { type: 'array', items: { type: 'string', minLength: 1 } },
202
- at: AT,
203
- // the same optional pair as a memory; the text a skill is embedded
204
- // from is its name, when and instructions together
205
- embedding: EMBEDDING,
206
- embeddedBy: EMBEDDED_BY,
207
- },
208
- required: ['id', 'name', 'when', 'instructions', 'tools', 'at'],
209
- dependencies: EMBEDDING_PAIR,
210
- additionalProperties: false,
192
+ $id: 'https://jarenjs.github.io/schemas/ai/ledger-skill.json',
193
+ type: 'object',
194
+ properties: {
195
+ id: ID,
196
+ name: { type: 'string', minLength: 1 },
197
+ when: { type: 'string', minLength: 1 },
198
+ instructions: { type: 'string', minLength: 1 },
199
+ program: PROGRAM_SKILL_SCHEMA,
200
+ tools: { type: 'array', items: { type: 'string', minLength: 1 } },
201
+ at: AT,
202
+ // the same optional pair as a memory; the text a skill is embedded
203
+ // from is its name, when and instructions together
204
+ embedding: EMBEDDING,
205
+ embeddedBy: EMBEDDED_BY,
206
+ },
207
+ required: ['id', 'name', 'when', 'instructions', 'tools', 'at'],
208
+ dependencies: EMBEDDING_PAIR,
209
+ additionalProperties: false,
211
210
  };
212
-
213
211
  /**
214
212
  * An addressable blob's METADATA. The content lives under a separate
215
213
  * storage key and never travels with the metadata — that separation is
@@ -217,141 +215,30 @@ export const SKILL_SCHEMA = {
217
215
  * for a hundred slots without carrying one slot's content.
218
216
  */
219
217
  export const SLOT_SCHEMA = {
220
- $id: 'https://jarenjs.github.io/schemas/ai/ledger-slot.json',
221
- type: 'object',
222
- properties: {
223
- name: ID,
224
- kind: { type: 'string', minLength: 1 },
225
- size: { type: 'integer', minimum: 0 },
226
- excerpt: { type: 'string' },
227
- at: AT,
228
- // OPTIONAL, and optional on purpose: `size` is what a slot costs and
229
- // is always known, while `count` is what it CONTAINS — lines,
230
- // records, pieces — which only the writer knows and only sometimes.
231
- // A root view listing a hundred slots is far more useful with "1 240
232
- // lines" beside a size, and a slot whose writer could not say is
233
- // better off saying nothing than guessing.
234
- count: { type: 'integer', minimum: 0 },
235
- pinned: { type: 'boolean' },
236
- },
237
- required: ['name', 'kind', 'size', 'excerpt', 'at'],
238
- additionalProperties: false,
218
+ $id: 'https://jarenjs.github.io/schemas/ai/ledger-slot.json',
219
+ type: 'object',
220
+ properties: {
221
+ name: ID,
222
+ kind: { type: 'string', minLength: 1 },
223
+ size: { type: 'integer', minimum: 0 },
224
+ excerpt: { type: 'string' },
225
+ at: AT,
226
+ // OPTIONAL, and optional on purpose: `size` is what a slot costs and
227
+ // is always known, while `count` is what it CONTAINS — lines,
228
+ // records, pieces — which only the writer knows and only sometimes.
229
+ // A root view listing a hundred slots is far more useful with "1 240
230
+ // lines" beside a size, and a slot whose writer could not say is
231
+ // better off saying nothing than guessing.
232
+ count: { type: 'integer', minimum: 0 },
233
+ pinned: { type: 'boolean' },
234
+ },
235
+ required: ['name', 'kind', 'size', 'excerpt', 'at'],
236
+ additionalProperties: false,
239
237
  };
240
-
241
238
  /** Every ledger schema by kind — what `createLedger` compiles at construction. */
242
239
  export const LEDGER_SCHEMAS = {
243
- goal: GOAL_SCHEMA,
244
- memory: MEMORY_SCHEMA,
245
- skill: SKILL_SCHEMA,
246
- slot: SLOT_SCHEMA,
240
+ goal: GOAL_SCHEMA,
241
+ memory: MEMORY_SCHEMA,
242
+ skill: SKILL_SCHEMA,
243
+ slot: SLOT_SCHEMA,
247
244
  };
248
-
249
- /*
250
- * The record shapes, as named types beside the schemas that enforce them.
251
- *
252
- * These exist because a strictly-typed consumer (the first one was the
253
- * tangleai rebuild, 2026-08-24) otherwise hand-writes its own copy of
254
- * "what addMemory returns" and the copy drifts. The schema stays the
255
- * runtime contract; the typedef is the same statement made to the
256
- * compiler, and the two live in one file so a change to one is a diff
257
- * touching the other's neighbourhood.
258
- */
259
-
260
- /**
261
- * One evidenced progress entry on the active goal.
262
- * @typedef {object} LedgerProgressEntry
263
- * @property {string} [id]
264
- * @property {string} at RFC 3339
265
- * @property {string} note
266
- * @property {string} evidence
267
- */
268
-
269
- /**
270
- * The active (or archived) objective — see {@link GOAL_SCHEMA}.
271
- * @typedef {object} LedgerGoal
272
- * @property {string} objective
273
- * @property {string} createdAt RFC 3339
274
- * @property {'active'|'done'|'abandoned'|'superseded'} status
275
- * @property {LedgerProgressEntry[]} progress
276
- * @property {{ version: 1, records: { note: string, evidence: string }[], sources: { id: string, at: string, record: number }[] }} [checkpoint]
277
- * @property {{ version: 1, reason: 'goal-budget', retired: string[] }} [retention]
278
- */
279
-
280
- /**
281
- * A vector's identity — see {@link EMBEDDED_BY}. What `recall({ near })`
282
- * compares against the query embedder's `{ model, dims }` before any
283
- * arithmetic happens.
284
- * @typedef {object} LedgerEmbeddedBy
285
- * @property {string} model
286
- * @property {number} dims
287
- */
288
-
289
- /**
290
- * The vector pair, both-or-neither — the schemas' `dependencies` rule
291
- * (see {@link EMBEDDING_PAIR}) stated in the type: a record either
292
- * carries `embedding` AND `embeddedBy`, or neither. `embedding` is the
293
- * vector `recall({ near })` ranks by — plain numbers, never a typed
294
- * array — and `embeddedBy` its identity. Narrow on either member and
295
- * the other follows: `if (record.embedding) record.embeddedBy.dims`.
296
- * An orphan vector does not type, just as the ledger refuses it.
297
- * @typedef {{ embedding?: undefined, embeddedBy?: undefined }
298
- * | { embedding: number[], embeddedBy: LedgerEmbeddedBy }} LedgerEmbeddingPair
299
- */
300
-
301
- /**
302
- * A stored memory's own members — see {@link MEMORY_SCHEMA}; the
303
- * record is these plus {@link LedgerEmbeddingPair}.
304
- * @typedef {object} LedgerMemoryFields
305
- * @property {string} id
306
- * @property {string} text
307
- * @property {string | import('./evidence.js').ClaimEvidenceEnvelope} evidence
308
- * @property {string[]} tags
309
- * @property {string} at RFC 3339
310
- */
311
-
312
- /**
313
- * A stored memory — see {@link MEMORY_SCHEMA}.
314
- * @typedef {LedgerMemoryFields & LedgerEmbeddingPair} LedgerMemory
315
- */
316
-
317
- /**
318
- * A stored skill's own members — see {@link SKILL_SCHEMA}; the record
319
- * is these plus {@link LedgerEmbeddingPair}.
320
- * @typedef {object} LedgerSkillFields
321
- * @property {string} id
322
- * @property {string} name
323
- * @property {string} when
324
- * @property {string} instructions
325
- * @property {string[]} tools
326
- * @property {string} at RFC 3339
327
- * @property {{ version: 1, question: string, fingerprint: string, environmentId: string,
328
- * schemaVersion: string, document: any, evidence: string, checked: true }} [program]
329
- */
330
-
331
- /**
332
- * A stored skill — see {@link SKILL_SCHEMA}.
333
- * @typedef {LedgerSkillFields & LedgerEmbeddingPair} LedgerSkill
334
- */
335
-
336
- /**
337
- * A slot's metadata — see {@link SLOT_SCHEMA}. Never the content.
338
- * @typedef {object} LedgerSlot
339
- * @property {string} name
340
- * @property {string} kind
341
- * @property {number} size
342
- * @property {string} excerpt
343
- * @property {string} at RFC 3339
344
- * @property {number} [count]
345
- * @property {boolean} [pinned]
346
- */
347
-
348
- /**
349
- * The one rejection shape every schema-guarded write answers with —
350
- * produced by `invalidInput` in `check.js`, named here because this is
351
- * where a consumer of the ledger's API goes looking for "what comes
352
- * back when a write is refused".
353
- * @typedef {object} LedgerRejection
354
- * @property {string} error
355
- * @property {any[]} errors
356
- * @property {any} inputSchema
357
- */