limen-ai 1.2.0 → 1.3.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.
Files changed (75) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/dist/api/convenience/convenience_init.d.ts +46 -0
  3. package/dist/api/convenience/convenience_init.d.ts.map +1 -0
  4. package/dist/api/convenience/convenience_init.js +111 -0
  5. package/dist/api/convenience/convenience_init.js.map +1 -0
  6. package/dist/api/convenience/convenience_layer.d.ts +59 -0
  7. package/dist/api/convenience/convenience_layer.d.ts.map +1 -0
  8. package/dist/api/convenience/convenience_layer.js +373 -0
  9. package/dist/api/convenience/convenience_layer.js.map +1 -0
  10. package/dist/api/convenience/convenience_prompt.d.ts +19 -0
  11. package/dist/api/convenience/convenience_prompt.d.ts.map +1 -0
  12. package/dist/api/convenience/convenience_prompt.js +76 -0
  13. package/dist/api/convenience/convenience_prompt.js.map +1 -0
  14. package/dist/api/convenience/convenience_types.d.ts +244 -0
  15. package/dist/api/convenience/convenience_types.d.ts.map +1 -0
  16. package/dist/api/convenience/convenience_types.js +30 -0
  17. package/dist/api/convenience/convenience_types.js.map +1 -0
  18. package/dist/api/facades/claim_api_impl.d.ts +11 -3
  19. package/dist/api/facades/claim_api_impl.d.ts.map +1 -1
  20. package/dist/api/facades/claim_api_impl.js +31 -3
  21. package/dist/api/facades/claim_api_impl.js.map +1 -1
  22. package/dist/api/facades/claim_facade.d.ts +5 -1
  23. package/dist/api/facades/claim_facade.d.ts.map +1 -1
  24. package/dist/api/facades/claim_facade.js +19 -0
  25. package/dist/api/facades/claim_facade.js.map +1 -1
  26. package/dist/api/index.d.ts +1 -1
  27. package/dist/api/index.d.ts.map +1 -1
  28. package/dist/api/index.js +102 -6
  29. package/dist/api/index.js.map +1 -1
  30. package/dist/api/interfaces/api.d.ts +78 -51
  31. package/dist/api/interfaces/api.d.ts.map +1 -1
  32. package/dist/api/migration/028_fts5_search.d.ts +25 -0
  33. package/dist/api/migration/028_fts5_search.d.ts.map +1 -0
  34. package/dist/api/migration/028_fts5_search.js +97 -0
  35. package/dist/api/migration/028_fts5_search.js.map +1 -0
  36. package/dist/api/migration/029_fts5_cjk.d.ts +24 -0
  37. package/dist/api/migration/029_fts5_cjk.d.ts.map +1 -0
  38. package/dist/api/migration/029_fts5_cjk.js +88 -0
  39. package/dist/api/migration/029_fts5_cjk.js.map +1 -0
  40. package/dist/api/migration/030_cognitive_metabolism.d.ts +20 -0
  41. package/dist/api/migration/030_cognitive_metabolism.d.ts.map +1 -0
  42. package/dist/api/migration/030_cognitive_metabolism.js +59 -0
  43. package/dist/api/migration/030_cognitive_metabolism.js.map +1 -0
  44. package/dist/claims/interfaces/claim_types.d.ts +75 -0
  45. package/dist/claims/interfaces/claim_types.d.ts.map +1 -1
  46. package/dist/claims/interfaces/claim_types.js +4 -0
  47. package/dist/claims/interfaces/claim_types.js.map +1 -1
  48. package/dist/claims/store/claim_stores.d.ts.map +1 -1
  49. package/dist/claims/store/claim_stores.js +258 -20
  50. package/dist/claims/store/claim_stores.js.map +1 -1
  51. package/dist/cognitive/access_tracker.d.ts +45 -0
  52. package/dist/cognitive/access_tracker.d.ts.map +1 -0
  53. package/dist/cognitive/access_tracker.js +134 -0
  54. package/dist/cognitive/access_tracker.js.map +1 -0
  55. package/dist/cognitive/decay.d.ts +48 -0
  56. package/dist/cognitive/decay.d.ts.map +1 -0
  57. package/dist/cognitive/decay.js +73 -0
  58. package/dist/cognitive/decay.js.map +1 -0
  59. package/dist/cognitive/freshness.d.ts +36 -0
  60. package/dist/cognitive/freshness.d.ts.map +1 -0
  61. package/dist/cognitive/freshness.js +41 -0
  62. package/dist/cognitive/freshness.js.map +1 -0
  63. package/dist/cognitive/stability.d.ts +47 -0
  64. package/dist/cognitive/stability.d.ts.map +1 -0
  65. package/dist/cognitive/stability.js +90 -0
  66. package/dist/cognitive/stability.js.map +1 -0
  67. package/dist/search/search_utils.d.ts +60 -0
  68. package/dist/search/search_utils.d.ts.map +1 -0
  69. package/dist/search/search_utils.js +93 -0
  70. package/dist/search/search_utils.js.map +1 -0
  71. package/package.json +12 -3
  72. package/dist/api/knowledge/knowledge_api.d.ts +0 -55
  73. package/dist/api/knowledge/knowledge_api.d.ts.map +0 -1
  74. package/dist/api/knowledge/knowledge_api.js +0 -89
  75. package/dist/api/knowledge/knowledge_api.js.map +0 -1
package/CHANGELOG.md CHANGED
@@ -5,6 +5,35 @@ All notable changes to Limen are documented in this file.
5
5
  Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
6
6
  Versioning follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [1.3.0] - 2026-03-31
9
+
10
+ ### Added
11
+ - **Convenience API** (`remember`, `recall`, `forget`, `connect`, `reflect`, `promptInstructions`) — the thesis becomes accessible. 3 lines to store a belief. Full engineering controls: Design Source, Breaker pass, Certifier gate.
12
+ - **FTS5 Full-Text Search** (`search`) — find beliefs by content. Primary index (`unicode61` tokenizer) for Latin/Cyrillic, secondary index (`trigram`) for CJK and substring matching. External content mode (zero data duplication). Tenant-scoped. FTS5 query injection sanitized.
13
+ - **Cognitive Metabolism** — beliefs that breathe. FSRS power-decay formula `R(t) = (1 + t/(9*S))^(-1)` computed on every read, never stored. `effective_confidence` returned alongside raw `confidence`. Freshness classification (Fresh/Aging/Stale). Access tracking with batched flush. `minConfidence` filters by decayed confidence.
14
+ - **Wrongness containment** — `maxAutoConfidence` ceiling (default 0.7) prevents confidence laundering. Auto-extracted claims cannot start above 0.7 unless human-verified via `evidence_path` grounding.
15
+ - **Stability per claim** — predicate-based stability assignment: governance 365d, architectural 180d, finding 90d, warning 30d, ephemeral 7d, preference 120d. Configurable patterns.
16
+ - `retractClaim` on `ClaimApi` — programmatic claim retraction with audit trail.
17
+ - `searchClaims` on `ClaimApi` — programmatic FTS5 search with BM25 ranking.
18
+ - Migrations v37 (FTS5 primary + triggers), v38 (FTS5 CJK trigram + triggers), v39 (cognitive metabolism columns).
19
+ - 235 new tests (3,188 → 3,423). Every test through A21 dual-path (success + rejection).
20
+
21
+ ### Changed
22
+ - npm description updated to "Governed knowledge engine for AI agents."
23
+ - npm keywords expanded for discoverability.
24
+ - `BeliefView` extended with `effectiveConfidence`, `freshness`, `stability`, `lastAccessedAt`, `accessCount`.
25
+ - `SearchResult.score` now uses `effectiveConfidence * BM25` — old claims rank lower than fresh ones.
26
+ - `recall()` and `search()` return decay-aware results by default.
27
+
28
+ ### Removed
29
+ - **Knowledge API stub** (`KnowledgeApi`, `KnowledgeApiImpl`, MCP knowledge tools) — dead code that returned `{ memoriesCreated: 0 }`. Replaced by the convenience API built through full engineering controls.
30
+
31
+ ## [1.2.0] - 2026-03-28
32
+
33
+ ### Added
34
+ - `setDefaultAgent` API for library-mode agent identity.
35
+ - MCP server: session adapter with knowledge tools (subsequently removed in v1.3.0).
36
+
8
37
  ## [1.1.0] - 2026-03-24
9
38
 
10
39
  ### Added
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Phase 1: Convenience API eager initialization.
3
+ *
4
+ * Registers the "limen-convenience" agent and creates the convenience mission
5
+ * during createLimen(). This provides the missionId and taskId needed for
6
+ * all convenience API claim operations.
7
+ *
8
+ * Design Source: Decision 1 (MissionId for Convenience Claims)
9
+ * Invariants: I-CONV-01 (immediately usable), I-CONV-02 (created once, cached)
10
+ *
11
+ * Trade-off: Adds ~30ms to createLimen() (already async). All convenience
12
+ * methods are synchronous Result<T> after engine creation.
13
+ */
14
+ import type { AgentId, MissionId, TaskId } from '../../kernel/interfaces/index.js';
15
+ import type { TimeProvider } from '../../kernel/interfaces/time.js';
16
+ import type { AgentApi, MissionApi } from '../interfaces/api.js';
17
+ /**
18
+ * Result of convenience initialization.
19
+ * Contains the cached IDs needed for all convenience operations.
20
+ */
21
+ export interface ConvenienceInitResult {
22
+ readonly agentId: AgentId;
23
+ readonly missionId: MissionId;
24
+ readonly taskId: TaskId | null;
25
+ }
26
+ /** Well-known agent name for convenience API */
27
+ export declare const CONVENIENCE_AGENT_NAME = "limen-convenience";
28
+ /** Well-known mission objective for convenience API */
29
+ export declare const CONVENIENCE_MISSION_OBJECTIVE = "limen-convenience-api";
30
+ /**
31
+ * Initialize the convenience API infrastructure.
32
+ *
33
+ * 1. Register (or retrieve) the "limen-convenience" agent
34
+ * 2. Create the convenience mission with standard config
35
+ * 3. Propose a task graph with a single task
36
+ * 4. Return cached IDs for all subsequent operations
37
+ *
38
+ * This runs during createLimen() (already async). All failures
39
+ * propagate as exceptions to the createLimen() caller.
40
+ *
41
+ * @param agents - The AgentApi for agent registration
42
+ * @param missions - The MissionApi for mission creation
43
+ * @param setDefaultAgent - Function to set the default agent identity
44
+ */
45
+ export declare function initializeConvenience(agents: AgentApi, missions: MissionApi, setDefaultAgent: (agentId: AgentId) => void, time: TimeProvider): Promise<ConvenienceInitResult>;
46
+ //# sourceMappingURL=convenience_init.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"convenience_init.d.ts","sourceRoot":"","sources":["../../../src/api/convenience/convenience_init.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,kCAAkC,CAAC;AACnF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iCAAiC,CAAC;AACpE,OAAO,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAEjE;;;GAGG;AACH,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC;IAC9B,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC;AAED,gDAAgD;AAChD,eAAO,MAAM,sBAAsB,sBAAsB,CAAC;AAE1D,uDAAuD;AACvD,eAAO,MAAM,6BAA6B,0BAA0B,CAAC;AAErE;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,qBAAqB,CACzC,MAAM,EAAE,QAAQ,EAChB,QAAQ,EAAE,UAAU,EACpB,eAAe,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,EAC3C,IAAI,EAAE,YAAY,GACjB,OAAO,CAAC,qBAAqB,CAAC,CAkFhC"}
@@ -0,0 +1,111 @@
1
+ /**
2
+ * Phase 1: Convenience API eager initialization.
3
+ *
4
+ * Registers the "limen-convenience" agent and creates the convenience mission
5
+ * during createLimen(). This provides the missionId and taskId needed for
6
+ * all convenience API claim operations.
7
+ *
8
+ * Design Source: Decision 1 (MissionId for Convenience Claims)
9
+ * Invariants: I-CONV-01 (immediately usable), I-CONV-02 (created once, cached)
10
+ *
11
+ * Trade-off: Adds ~30ms to createLimen() (already async). All convenience
12
+ * methods are synchronous Result<T> after engine creation.
13
+ */
14
+ /** Well-known agent name for convenience API */
15
+ export const CONVENIENCE_AGENT_NAME = 'limen-convenience';
16
+ /** Well-known mission objective for convenience API */
17
+ export const CONVENIENCE_MISSION_OBJECTIVE = 'limen-convenience-api';
18
+ /**
19
+ * Initialize the convenience API infrastructure.
20
+ *
21
+ * 1. Register (or retrieve) the "limen-convenience" agent
22
+ * 2. Create the convenience mission with standard config
23
+ * 3. Propose a task graph with a single task
24
+ * 4. Return cached IDs for all subsequent operations
25
+ *
26
+ * This runs during createLimen() (already async). All failures
27
+ * propagate as exceptions to the createLimen() caller.
28
+ *
29
+ * @param agents - The AgentApi for agent registration
30
+ * @param missions - The MissionApi for mission creation
31
+ * @param setDefaultAgent - Function to set the default agent identity
32
+ */
33
+ export async function initializeConvenience(agents, missions, setDefaultAgent, time) {
34
+ // 1. Register agent (idempotent -- if already exists, retrieve it)
35
+ let agentId;
36
+ try {
37
+ const agent = await agents.register({
38
+ name: CONVENIENCE_AGENT_NAME,
39
+ domains: ['convenience'],
40
+ capabilities: ['remember', 'recall', 'forget', 'connect', 'reflect'],
41
+ });
42
+ agentId = agent.id;
43
+ }
44
+ catch (regErr) {
45
+ // Agent may already exist from a previous createLimen() with the same dataDir.
46
+ // Try to retrieve it instead.
47
+ const existing = await agents.get(CONVENIENCE_AGENT_NAME);
48
+ if (existing) {
49
+ agentId = existing.id;
50
+ }
51
+ else {
52
+ throw regErr;
53
+ }
54
+ }
55
+ // 2. Set default agent so all ClaimApi calls carry this agentId
56
+ setDefaultAgent(agentId);
57
+ // 3. Create convenience mission
58
+ // Deadline: 1 year from now. Budget: 1,000,000 tokens.
59
+ const deadline = new Date(time.nowMs() + 365 * 24 * 60 * 60 * 1000).toISOString();
60
+ const missionHandle = await missions.create({
61
+ agent: CONVENIENCE_AGENT_NAME,
62
+ objective: CONVENIENCE_MISSION_OBJECTIVE,
63
+ constraints: {
64
+ tokenBudget: 1_000_000,
65
+ deadline,
66
+ },
67
+ });
68
+ const missionId = missionHandle.id;
69
+ // 4. Propose a task graph with a single convenience task
70
+ await missionHandle.proposeTaskGraph({
71
+ missionId,
72
+ tasks: [{
73
+ id: 'convenience-task',
74
+ description: 'Convenience API operations',
75
+ executionMode: 'deterministic',
76
+ estimatedTokens: 1_000_000,
77
+ }],
78
+ dependencies: [],
79
+ objectiveAlignment: 'Convenience API thin delegation layer for remember/recall/forget/connect/reflect',
80
+ });
81
+ // 5. Get the task ID from the graph
82
+ // The task graph creates tasks with IDs that are mission-scoped.
83
+ // We need to query the mission's tasks to find the one we created.
84
+ // The task ID format is typically the mission's task graph ID.
85
+ // For simplicity, we look up the mission to get the task.
86
+ const missionState = await missions.get(missionId);
87
+ if (!missionState) {
88
+ throw new Error(`Convenience mission ${missionId} not found after creation`);
89
+ }
90
+ // Propose task execution to get the actual taskId
91
+ // The taskId from the graph is a string, but we need the internal TaskId.
92
+ // We'll use the graph's task count to derive it.
93
+ // Actually, looking at the orchestration, tasks are created by proposeTaskGraph
94
+ // and their IDs are returned. But proposeTaskGraph returns TaskGraphOutput
95
+ // which has taskCount but not individual task IDs.
96
+ //
97
+ // For the convenience API, the missionId is the critical piece.
98
+ // The taskId can be null for convenience claims. Let me verify...
99
+ // Looking at ClaimCreateInput: taskId is TaskId | null.
100
+ // MissionId is required (not null). TaskId can be null.
101
+ //
102
+ // Decision: Use null for taskId. The missionId is sufficient for
103
+ // SC-11 validation. This avoids the complexity of retrieving the
104
+ // internal TaskId from the task graph.
105
+ return {
106
+ agentId,
107
+ missionId,
108
+ taskId: null,
109
+ };
110
+ }
111
+ //# sourceMappingURL=convenience_init.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"convenience_init.js","sourceRoot":"","sources":["../../../src/api/convenience/convenience_init.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAgBH,gDAAgD;AAChD,MAAM,CAAC,MAAM,sBAAsB,GAAG,mBAAmB,CAAC;AAE1D,uDAAuD;AACvD,MAAM,CAAC,MAAM,6BAA6B,GAAG,uBAAuB,CAAC;AAErE;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,MAAgB,EAChB,QAAoB,EACpB,eAA2C,EAC3C,IAAkB;IAElB,mEAAmE;IACnE,IAAI,OAAgB,CAAC;IACrB,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,MAAM,MAAM,CAAC,QAAQ,CAAC;YAClC,IAAI,EAAE,sBAAsB;YAC5B,OAAO,EAAE,CAAC,aAAa,CAAC;YACxB,YAAY,EAAE,CAAC,UAAU,EAAE,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,CAAC;SACrE,CAAC,CAAC;QACH,OAAO,GAAG,KAAK,CAAC,EAAE,CAAC;IACrB,CAAC;IAAC,OAAO,MAAe,EAAE,CAAC;QACzB,+EAA+E;QAC/E,8BAA8B;QAC9B,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,GAAG,CAAC,sBAAsB,CAAC,CAAC;QAC1D,IAAI,QAAQ,EAAE,CAAC;YACb,OAAO,GAAG,QAAQ,CAAC,EAAE,CAAC;QACxB,CAAC;aAAM,CAAC;YACN,MAAM,MAAM,CAAC;QACf,CAAC;IACH,CAAC;IAED,gEAAgE;IAChE,eAAe,CAAC,OAAO,CAAC,CAAC;IAEzB,gCAAgC;IAChC,uDAAuD;IACvD,MAAM,QAAQ,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,GAAG,GAAG,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC;IAClF,MAAM,aAAa,GAAG,MAAM,QAAQ,CAAC,MAAM,CAAC;QAC1C,KAAK,EAAE,sBAAsB;QAC7B,SAAS,EAAE,6BAA6B;QACxC,WAAW,EAAE;YACX,WAAW,EAAE,SAAS;YACtB,QAAQ;SACT;KACF,CAAC,CAAC;IAEH,MAAM,SAAS,GAAG,aAAa,CAAC,EAAE,CAAC;IAEnC,yDAAyD;IACzD,MAAM,aAAa,CAAC,gBAAgB,CAAC;QACnC,SAAS;QACT,KAAK,EAAE,CAAC;gBACN,EAAE,EAAE,kBAAkB;gBACtB,WAAW,EAAE,4BAA4B;gBACzC,aAAa,EAAE,eAAe;gBAC9B,eAAe,EAAE,SAAS;aAC3B,CAAC;QACF,YAAY,EAAE,EAAE;QAChB,kBAAkB,EAAE,kFAAkF;KACvG,CAAC,CAAC;IAEH,oCAAoC;IACpC,iEAAiE;IACjE,mEAAmE;IACnE,+DAA+D;IAC/D,0DAA0D;IAC1D,MAAM,YAAY,GAAG,MAAM,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IACnD,IAAI,CAAC,YAAY,EAAE,CAAC;QAClB,MAAM,IAAI,KAAK,CAAC,uBAAuB,SAAS,2BAA2B,CAAC,CAAC;IAC/E,CAAC;IAED,kDAAkD;IAClD,0EAA0E;IAC1E,iDAAiD;IACjD,gFAAgF;IAChF,2EAA2E;IAC3E,mDAAmD;IACnD,EAAE;IACF,gEAAgE;IAChE,kEAAkE;IAClE,wDAAwD;IACxD,wDAAwD;IACxD,EAAE;IACF,iEAAiE;IACjE,iEAAiE;IACjE,uCAAuC;IAEvC,OAAO;QACL,OAAO;QACP,SAAS;QACT,MAAM,EAAE,IAAI;KACb,CAAC;AACJ,CAAC"}
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Phase 1 Convenience API Implementation.
3
+ *
4
+ * Thin delegation layer that translates cognitive operations (remember, recall,
5
+ * forget, connect, reflect) into ClaimApi system calls. No new system calls.
6
+ * No new database tables. No new schema.
7
+ *
8
+ * Design Source: docs/sprints/PHASE-1-DESIGN-SOURCE.md
9
+ * Invariants: I-CONV-01 through I-CONV-18
10
+ *
11
+ * Architecture: This is the "store" layer in the three-file pattern.
12
+ * convenience_types.ts (contract) -> convenience_layer.ts (implementation)
13
+ *
14
+ * Governance boundary (I-17): Only imports ClaimApi, never ClaimSystem/ClaimStore.
15
+ */
16
+ import type { Result } from '../../kernel/interfaces/index.js';
17
+ import type { MissionId, TaskId } from '../../kernel/interfaces/index.js';
18
+ import type { DatabaseConnection } from '../../kernel/interfaces/database.js';
19
+ import type { TimeProvider } from '../../kernel/interfaces/time.js';
20
+ import type { ClaimApi } from '../interfaces/api.js';
21
+ import type { RememberOptions, RememberResult, RecallOptions, BeliefView, ReflectEntry, ReflectResult, SearchOptions, SearchResult } from './convenience_types.js';
22
+ /**
23
+ * Dependencies for the convenience layer.
24
+ * Injected during createLimen() -- no direct access to internals.
25
+ */
26
+ export interface ConvenienceLayerDeps {
27
+ readonly claims: ClaimApi;
28
+ readonly getConnection: () => DatabaseConnection;
29
+ readonly time: TimeProvider;
30
+ readonly missionId: MissionId;
31
+ readonly taskId: TaskId | null;
32
+ readonly maxAutoConfidence: number;
33
+ }
34
+ /**
35
+ * The convenience layer interface -- methods added to the Limen object.
36
+ */
37
+ export interface ConvenienceLayer {
38
+ remember(subject: string, predicate: string, value: string, options?: RememberOptions): Result<RememberResult>;
39
+ remember(text: string, options?: RememberOptions): Result<RememberResult>;
40
+ remember(subjectOrText: string, predicateOrOptions?: string | RememberOptions, value?: string, options?: RememberOptions): Result<RememberResult>;
41
+ recall(subject?: string, predicate?: string, options?: RecallOptions): Result<readonly BeliefView[]>;
42
+ forget(claimId: string, reason?: string): Result<void>;
43
+ connect(claimId1: string, claimId2: string, type: 'supports' | 'contradicts' | 'supersedes' | 'derived_from'): Result<void>;
44
+ reflect(entries: readonly ReflectEntry[]): Result<ReflectResult>;
45
+ promptInstructions(): string;
46
+ /** Phase 2 §2.4: Full-text search across claim content. */
47
+ search(query: string, options?: SearchOptions): Result<readonly SearchResult[]>;
48
+ }
49
+ /**
50
+ * Create the convenience layer.
51
+ *
52
+ * Returns an object with all convenience methods, ready to be spread
53
+ * onto the Limen engine object.
54
+ *
55
+ * All methods are synchronous Result<T> (I-CONV-01: immediately usable).
56
+ * Closure captures deps -- survives Object.freeze (I-CONV-15).
57
+ */
58
+ export declare function createConvenienceLayer(deps: ConvenienceLayerDeps): ConvenienceLayer;
59
+ //# sourceMappingURL=convenience_layer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"convenience_layer.d.ts","sourceRoot":"","sources":["../../../src/api/convenience/convenience_layer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAIH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kCAAkC,CAAC;AAC/D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,kCAAkC,CAAC;AAC1E,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,qCAAqC,CAAC;AAC9E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iCAAiC,CAAC;AASpE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAErD,OAAO,KAAK,EACV,eAAe,EACf,cAAc,EACd,aAAa,EACb,UAAU,EACV,YAAY,EACZ,aAAa,EACb,aAAa,EACb,YAAY,EACb,MAAM,wBAAwB,CAAC;AAuChC;;;GAGG;AACH,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC;IAC1B,QAAQ,CAAC,aAAa,EAAE,MAAM,kBAAkB,CAAC;IACjD,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC;IAC5B,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC;IAC9B,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;CACpC;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,MAAM,CAAC,cAAc,CAAC,CAAC;IAC/G,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,MAAM,CAAC,cAAc,CAAC,CAAC;IAC1E,QAAQ,CAAC,aAAa,EAAE,MAAM,EAAE,kBAAkB,CAAC,EAAE,MAAM,GAAG,eAAe,EAAE,KAAK,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,MAAM,CAAC,cAAc,CAAC,CAAC;IAClJ,MAAM,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,MAAM,CAAC,SAAS,UAAU,EAAE,CAAC,CAAC;IACrG,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC;IACvD,OAAO,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,aAAa,GAAG,YAAY,GAAG,cAAc,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC;IAC5H,OAAO,CAAC,OAAO,EAAE,SAAS,YAAY,EAAE,GAAG,MAAM,CAAC,aAAa,CAAC,CAAC;IACjE,kBAAkB,IAAI,MAAM,CAAC;IAC7B,2DAA2D;IAC3D,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,MAAM,CAAC,SAAS,YAAY,EAAE,CAAC,CAAC;CACjF;AAED;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,oBAAoB,GAAG,gBAAgB,CAqYnF"}
@@ -0,0 +1,373 @@
1
+ /**
2
+ * Phase 1 Convenience API Implementation.
3
+ *
4
+ * Thin delegation layer that translates cognitive operations (remember, recall,
5
+ * forget, connect, reflect) into ClaimApi system calls. No new system calls.
6
+ * No new database tables. No new schema.
7
+ *
8
+ * Design Source: docs/sprints/PHASE-1-DESIGN-SOURCE.md
9
+ * Invariants: I-CONV-01 through I-CONV-18
10
+ *
11
+ * Architecture: This is the "store" layer in the three-file pattern.
12
+ * convenience_types.ts (contract) -> convenience_layer.ts (implementation)
13
+ *
14
+ * Governance boundary (I-17): Only imports ClaimApi, never ClaimSystem/ClaimStore.
15
+ */
16
+ import { createHash } from 'node:crypto';
17
+ import { VALID_RELATIONSHIP_TYPES, VALID_CATEGORIES, MAX_STATEMENT_LENGTH, MAX_REFLECT_ENTRIES, DEFAULT_RECALL_LIMIT, DEFAULT_SEARCH_LIMIT, MAX_SEARCH_LIMIT, } from './convenience_types.js';
18
+ import { generatePromptInstructions } from './convenience_prompt.js';
19
+ // ── Result Helpers ──
20
+ function ok(value) {
21
+ return { ok: true, value };
22
+ }
23
+ function err(code, message) {
24
+ return { ok: false, error: { code, message, spec: 'Phase-1' } };
25
+ }
26
+ // ── SHA-256 Subject Generation ──
27
+ /**
28
+ * Generate a subject URN from text content using SHA-256.
29
+ * Format: entity:observation:<sha256-hex-first-12>
30
+ *
31
+ * I-CONV-13: Satisfies CCP subject validation (3 colon-separated segments).
32
+ */
33
+ function hashSubject(text) {
34
+ const hash = createHash('sha256').update(text, 'utf8').digest('hex');
35
+ return `entity:observation:${hash.substring(0, 12)}`;
36
+ }
37
+ /**
38
+ * Create the convenience layer.
39
+ *
40
+ * Returns an object with all convenience methods, ready to be spread
41
+ * onto the Limen engine object.
42
+ *
43
+ * All methods are synchronous Result<T> (I-CONV-01: immediately usable).
44
+ * Closure captures deps -- survives Object.freeze (I-CONV-15).
45
+ */
46
+ export function createConvenienceLayer(deps) {
47
+ const { claims, getConnection, time, missionId, taskId, maxAutoConfidence } = deps;
48
+ /**
49
+ * Compute effective confidence, applying the maxAutoConfidence cap
50
+ * unless evidence_path grounding with non-empty evidenceRefs is provided.
51
+ *
52
+ * I-CONV-04 (CONSTITUTIONAL): Cap enforcement.
53
+ * I-CONV-05 (CONSTITUTIONAL): Bypass with evidence.
54
+ */
55
+ function effectiveConfidence(requestedConfidence, groundingMode, evidenceRefs) {
56
+ const confidence = requestedConfidence ?? maxAutoConfidence;
57
+ // Bypass: evidence_path with non-empty evidenceRefs
58
+ if (groundingMode === 'evidence_path' && evidenceRefs && evidenceRefs.length > 0) {
59
+ return confidence;
60
+ }
61
+ // Cap at maxAutoConfidence
62
+ return Math.min(confidence, maxAutoConfidence);
63
+ }
64
+ /**
65
+ * 3-param remember: explicit subject, predicate, value.
66
+ */
67
+ function remember3(subject, predicate, value, options) {
68
+ // Validate confidence if provided
69
+ if (options?.confidence !== undefined) {
70
+ if (!Number.isFinite(options.confidence) || options.confidence < 0 || options.confidence > 1) {
71
+ return err('CONV_INVALID_CONFIDENCE', `Confidence must be in [0.0, 1.0], got ${options.confidence}`);
72
+ }
73
+ }
74
+ const groundingMode = options?.groundingMode ?? 'runtime_witness';
75
+ const evidenceRefs = options?.evidenceRefs ?? [];
76
+ const confidence = effectiveConfidence(options?.confidence, groundingMode, evidenceRefs);
77
+ const validAt = options?.validAt ?? time.nowISO();
78
+ const objectType = options?.objectType ?? 'string';
79
+ // Design Source §Grounding Mode Decision:
80
+ // evidence_path with empty evidenceRefs falls back to runtime_witness behavior.
81
+ // This prevents SC-11 rejection AND prevents confidence laundering.
82
+ const effectiveGroundingMode = (groundingMode === 'evidence_path' && evidenceRefs.length > 0)
83
+ ? 'evidence_path'
84
+ : 'runtime_witness';
85
+ const input = {
86
+ subject,
87
+ predicate,
88
+ object: { type: objectType, value },
89
+ confidence,
90
+ validAt,
91
+ missionId,
92
+ taskId,
93
+ evidenceRefs: effectiveGroundingMode === 'evidence_path'
94
+ ? evidenceRefs.map(ref => ({ type: ref.type, id: ref.id }))
95
+ : [],
96
+ groundingMode: effectiveGroundingMode,
97
+ ...(effectiveGroundingMode === 'runtime_witness' ? {
98
+ runtimeWitness: {
99
+ witnessType: 'convenience',
100
+ witnessedValues: { source: 'remember' },
101
+ witnessTimestamp: validAt,
102
+ },
103
+ } : {}),
104
+ };
105
+ const result = claims.assertClaim(input);
106
+ if (!result.ok)
107
+ return result;
108
+ return ok({
109
+ claimId: result.value.claim.id,
110
+ confidence: result.value.claim.confidence,
111
+ });
112
+ }
113
+ /**
114
+ * 1-param remember: auto-generate subject from text hash.
115
+ */
116
+ function remember1(text, options) {
117
+ // Validate text is non-empty and not whitespace-only (I-CONV-09)
118
+ if (!text || text.trim().length === 0) {
119
+ return err('CONV_INVALID_TEXT', 'Text must be non-empty and not whitespace-only');
120
+ }
121
+ const subject = hashSubject(text);
122
+ return remember3(subject, 'observation.note', text, options);
123
+ }
124
+ return {
125
+ /**
126
+ * Phase 1 §1.1/§1.2: Store a belief.
127
+ *
128
+ * Overload resolution (I-CONV-12):
129
+ * typeof secondArg === 'string' -> 3-param form
130
+ * otherwise -> 1-param form
131
+ */
132
+ remember(subjectOrText, predicateOrOptions, value, options) {
133
+ if (typeof predicateOrOptions === 'string') {
134
+ // 3-param form: (subject, predicate, value, options?)
135
+ return remember3(subjectOrText, predicateOrOptions, value, options);
136
+ }
137
+ else {
138
+ // 1-param form: (text, options?)
139
+ return remember1(subjectOrText, predicateOrOptions);
140
+ }
141
+ },
142
+ /**
143
+ * Phase 1 §1.3: Retrieve beliefs.
144
+ *
145
+ * I-CONV-08: Excludes superseded claims by default.
146
+ */
147
+ recall(subject, predicate, options) {
148
+ const input = {
149
+ ...(subject ? { subject } : {}),
150
+ ...(predicate ? { predicate } : {}),
151
+ status: 'active',
152
+ ...(options?.minConfidence !== undefined ? { minConfidence: options.minConfidence } : {}),
153
+ limit: options?.limit ?? DEFAULT_RECALL_LIMIT,
154
+ includeEvidence: false,
155
+ includeRelationships: false,
156
+ };
157
+ const result = claims.queryClaims(input);
158
+ if (!result.ok)
159
+ return result;
160
+ // Map ClaimQueryResultItem -> BeliefView
161
+ let items = result.value.claims;
162
+ // I-CONV-08: Filter superseded unless explicitly included
163
+ if (!options?.includeSuperseded) {
164
+ items = items.filter(item => !item.superseded);
165
+ }
166
+ const beliefs = items.map(item => ({
167
+ claimId: item.claim.id,
168
+ subject: item.claim.subject,
169
+ predicate: item.claim.predicate,
170
+ value: String(item.claim.object.value),
171
+ confidence: item.claim.confidence,
172
+ validAt: item.claim.validAt,
173
+ createdAt: item.claim.createdAt,
174
+ superseded: item.superseded,
175
+ disputed: item.disputed,
176
+ // Phase 3: Cognitive Metabolism fields
177
+ effectiveConfidence: item.effectiveConfidence,
178
+ freshness: item.freshness,
179
+ stability: item.claim.stability,
180
+ lastAccessedAt: item.claim.lastAccessedAt,
181
+ accessCount: item.claim.accessCount,
182
+ }));
183
+ return ok(beliefs);
184
+ },
185
+ /**
186
+ * Phase 1 §1.4: Retract a belief.
187
+ *
188
+ * I-CONV-11: Delegates to ClaimApi.retractClaim().
189
+ * I-CONV-14: Maps system-call errors to convenience error codes.
190
+ */
191
+ forget(claimId, reason) {
192
+ const input = {
193
+ claimId: claimId,
194
+ reason: reason ?? 'Retracted via forget()',
195
+ };
196
+ const result = claims.retractClaim(input);
197
+ if (!result.ok) {
198
+ // Map well-known error codes
199
+ if (result.error.code === 'CLAIM_NOT_FOUND') {
200
+ return err('CONV_CLAIM_NOT_FOUND', result.error.message);
201
+ }
202
+ if (result.error.code === 'CLAIM_ALREADY_RETRACTED') {
203
+ return err('CONV_ALREADY_RETRACTED', result.error.message);
204
+ }
205
+ return result;
206
+ }
207
+ return ok(undefined);
208
+ },
209
+ /**
210
+ * Phase 1 §1.5: Create a relationship between two claims.
211
+ *
212
+ * DC-P1-804: Validates relationship type.
213
+ * DC-P1-805: Rejects self-reference.
214
+ */
215
+ connect(claimId1, claimId2, type) {
216
+ // Validate relationship type
217
+ if (!VALID_RELATIONSHIP_TYPES.includes(type)) {
218
+ return err('CONV_INVALID_RELATIONSHIP', `Invalid relationship type: ${type}. Must be one of: ${VALID_RELATIONSHIP_TYPES.join(', ')}`);
219
+ }
220
+ const input = {
221
+ fromClaimId: claimId1,
222
+ toClaimId: claimId2,
223
+ type: type,
224
+ missionId,
225
+ };
226
+ const result = claims.relateClaims(input);
227
+ if (!result.ok) {
228
+ // Map well-known error codes
229
+ if (result.error.code === 'SELF_REFERENCE') {
230
+ return err('CONV_SELF_REFERENCE', result.error.message);
231
+ }
232
+ return result;
233
+ }
234
+ return ok(undefined);
235
+ },
236
+ /**
237
+ * Phase 1 §1.6: Batch-store categorized learnings.
238
+ *
239
+ * I-CONV-10: All-or-nothing transaction semantics.
240
+ * Uses SQLite BEGIN/COMMIT/ROLLBACK via getConnection().
241
+ */
242
+ reflect(entries) {
243
+ // Validate: non-empty entries
244
+ if (!entries || entries.length === 0) {
245
+ return err('CONV_EMPTY_ENTRIES', 'reflect() requires at least one entry');
246
+ }
247
+ // Validate: entries count limit (F-P1-008: DoS protection)
248
+ if (entries.length > MAX_REFLECT_ENTRIES) {
249
+ return err('CONV_ENTRIES_LIMIT', `reflect() accepts at most ${MAX_REFLECT_ENTRIES} entries, got ${entries.length}`);
250
+ }
251
+ // Pre-validate all entries before starting transaction
252
+ for (let i = 0; i < entries.length; i++) {
253
+ const entry = entries[i];
254
+ // Validate category
255
+ if (!VALID_CATEGORIES.includes(entry.category)) {
256
+ return err('CONV_INVALID_CATEGORY', `Entry ${i}: invalid category '${entry.category}'. Must be one of: ${VALID_CATEGORIES.join(', ')}`);
257
+ }
258
+ // Validate statement length
259
+ if (entry.statement.length > MAX_STATEMENT_LENGTH) {
260
+ return err('CONV_STATEMENT_TOO_LONG', `Entry ${i}: statement exceeds ${MAX_STATEMENT_LENGTH} characters (${entry.statement.length})`);
261
+ }
262
+ // Validate confidence if provided
263
+ if (entry.confidence !== undefined) {
264
+ if (!Number.isFinite(entry.confidence) || entry.confidence < 0 || entry.confidence > 1) {
265
+ return err('CONV_INVALID_CONFIDENCE', `Entry ${i}: confidence must be in [0.0, 1.0], got ${entry.confidence}`);
266
+ }
267
+ }
268
+ }
269
+ // Transaction: all-or-nothing
270
+ const conn = getConnection();
271
+ conn.run('BEGIN');
272
+ try {
273
+ const claimIds = [];
274
+ for (const entry of entries) {
275
+ const subject = hashSubject(entry.statement);
276
+ const confidence = Math.min(entry.confidence ?? maxAutoConfidence, maxAutoConfidence);
277
+ const validAt = time.nowISO();
278
+ const input = {
279
+ subject,
280
+ predicate: `reflection.${entry.category}`,
281
+ object: { type: 'string', value: entry.statement },
282
+ confidence,
283
+ validAt,
284
+ missionId,
285
+ taskId,
286
+ evidenceRefs: [],
287
+ groundingMode: 'runtime_witness',
288
+ runtimeWitness: {
289
+ witnessType: 'convenience',
290
+ witnessedValues: { source: 'reflect', category: entry.category },
291
+ witnessTimestamp: validAt,
292
+ },
293
+ };
294
+ const result = claims.assertClaim(input);
295
+ if (!result.ok) {
296
+ conn.run('ROLLBACK');
297
+ return result;
298
+ }
299
+ claimIds.push(result.value.claim.id);
300
+ }
301
+ conn.run('COMMIT');
302
+ return ok({ stored: claimIds.length, claimIds });
303
+ }
304
+ catch (catchErr) {
305
+ try {
306
+ conn.run('ROLLBACK');
307
+ }
308
+ catch { /* already rolled back */ }
309
+ return err('CONV_BATCH_PARTIAL', `reflect() failed: ${String(catchErr)}`);
310
+ }
311
+ },
312
+ /**
313
+ * Phase 1 §1.7: Get system prompt instructions.
314
+ *
315
+ * I-CONV-18: Pure function, no I/O, deterministic.
316
+ */
317
+ promptInstructions() {
318
+ return generatePromptInstructions();
319
+ },
320
+ /**
321
+ * Phase 2 §2.4: Full-text search across claim content.
322
+ *
323
+ * Delegates to ClaimApi.searchClaims(). Maps SearchClaimResultItem -> SearchResult.
324
+ *
325
+ * Invariants: I-P2-02 (tenant isolation via facade), I-P2-05 (score), I-P2-07 (input validation)
326
+ * DCs: DC-P2-012 (limit validation), DC-P2-013 (empty query validation)
327
+ */
328
+ search(query, options) {
329
+ // I-P2-07: Validate query is non-empty
330
+ if (!query || query.trim().length === 0) {
331
+ return err('CONV_SEARCH_EMPTY_QUERY', 'Search query must be non-empty and not whitespace-only');
332
+ }
333
+ // I-P2-07: Validate limit
334
+ const limit = options?.limit ?? DEFAULT_SEARCH_LIMIT;
335
+ if (limit <= 0 || limit > MAX_SEARCH_LIMIT) {
336
+ return err('CONV_SEARCH_INVALID_LIMIT', `Search limit must be in [1, ${MAX_SEARCH_LIMIT}], got ${limit}`);
337
+ }
338
+ const input = {
339
+ query: query.trim(),
340
+ ...(options?.minConfidence !== undefined ? { minConfidence: options.minConfidence } : {}),
341
+ limit,
342
+ ...(options?.includeSuperseded !== undefined ? { includeSuperseded: options.includeSuperseded } : {}),
343
+ };
344
+ const result = claims.searchClaims(input);
345
+ if (!result.ok)
346
+ return result;
347
+ // Map SearchClaimResultItem -> SearchResult (convenience type)
348
+ const searchResults = result.value.results.map(item => ({
349
+ belief: {
350
+ claimId: item.claim.id,
351
+ subject: item.claim.subject,
352
+ predicate: item.claim.predicate,
353
+ value: String(item.claim.object.value),
354
+ confidence: item.claim.confidence,
355
+ validAt: item.claim.validAt,
356
+ createdAt: item.claim.createdAt,
357
+ superseded: item.superseded,
358
+ disputed: item.disputed,
359
+ // Phase 3: Cognitive Metabolism fields
360
+ effectiveConfidence: item.effectiveConfidence,
361
+ freshness: item.freshness,
362
+ stability: item.claim.stability,
363
+ lastAccessedAt: item.claim.lastAccessedAt,
364
+ accessCount: item.claim.accessCount,
365
+ },
366
+ relevance: item.relevance,
367
+ score: item.score,
368
+ }));
369
+ return ok(searchResults);
370
+ },
371
+ };
372
+ }
373
+ //# sourceMappingURL=convenience_layer.js.map