@siduri-x/core 2.0.3 → 2.0.4

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.
@@ -13,6 +13,7 @@ export interface ChatRequest {
13
13
  history?: Message[];
14
14
  medium?: MouthMedium;
15
15
  signal?: AbortSignal;
16
+ subtitleLanguage?: string;
16
17
  [key: string]: any;
17
18
  }
18
19
  export interface ChatResponseMetadataEvent {
@@ -30,6 +31,9 @@ export interface ChatResponsePlan {
30
31
  subtitle_ja: string;
31
32
  subtitle_en: string;
32
33
  spoken_ja?: string;
34
+ subtitle?: string;
35
+ subtitle_language?: string;
36
+ subtitles?: Record<string, string>;
33
37
  evidence_ids?: string[];
34
38
  }
35
39
  export interface ChatResponseMetadata {
@@ -21,8 +21,9 @@ async function dispatchCompanionChat(runtime, payload) {
21
21
  else {
22
22
  roleOrContext = 'OWNER';
23
23
  }
24
- const runtimeResult = (payload.medium || payload.signal)
25
- ? await runner.handleUserMessage(userMessage, roleOrContext, history, payload.medium, payload.signal)
24
+ const requestedSubtitleLang = payload.subtitleLanguage || payload.subtitle_language;
25
+ const runtimeResult = (payload.medium || payload.signal || requestedSubtitleLang)
26
+ ? await runner.handleUserMessage(userMessage, roleOrContext, history, payload.medium, payload.signal, requestedSubtitleLang)
26
27
  : await runner.handleUserMessage(userMessage, roleOrContext, history);
27
28
  const delivery = runtimeResult?.delivery;
28
29
  // Normalize response plan
@@ -41,6 +42,17 @@ async function dispatchCompanionChat(runtime, payload) {
41
42
  expression = avatarEvent.expression;
42
43
  }
43
44
  }
45
+ const resolvedSubtitles = {
46
+ ...(runtimeResult?.response?.subtitles || {}),
47
+ ...(delivery?.subtitles || {}),
48
+ };
49
+ const subtitle = (requestedSubtitleLang && resolvedSubtitles[requestedSubtitleLang]) ||
50
+ runtimeResult?.response?.subtitle ||
51
+ (requestedSubtitleLang === 'ja' ? (delivery?.subtitles?.ja ?? runtimeResult?.response?.subtitle_ja) : undefined) ||
52
+ (requestedSubtitleLang === 'en' ? (delivery?.subtitles?.en ?? runtimeResult?.response?.subtitle_en) : undefined);
53
+ if (subtitle && requestedSubtitleLang) {
54
+ resolvedSubtitles[requestedSubtitleLang] = subtitle;
55
+ }
44
56
  // Ensure both spoken_ja and subtitle_en are accessible alongside speech_id and evidence_ids
45
57
  const responsePlan = {
46
58
  speech_id: runtimeResult?.response?.speech_id,
@@ -48,6 +60,9 @@ async function dispatchCompanionChat(runtime, payload) {
48
60
  subtitle_ja: delivery?.subtitles?.ja ?? runtimeResult?.response?.subtitle_ja ?? speech,
49
61
  subtitle_en: delivery?.subtitles?.en ?? runtimeResult?.response?.subtitle_en ?? speech,
50
62
  spoken_ja: delivery?.subtitles?.spoken ?? runtimeResult?.response?.spoken_ja ?? runtimeResult?.response?.subtitle_ja ?? speech,
63
+ subtitle,
64
+ subtitle_language: requestedSubtitleLang,
65
+ subtitles: resolvedSubtitles,
51
66
  evidence_ids: runtimeResult?.metadata?.evidence_ids ?? runtimeResult?.response?.evidence_ids ?? [],
52
67
  };
53
68
  const metadata = {
package/dist/index.d.ts CHANGED
@@ -31,7 +31,7 @@ import { EvidenceRecord } from './evidence';
31
31
  import { ActionIntent } from './action';
32
32
  import { RequestContext } from './context';
33
33
  import { EarIngestOptions } from './ear-types';
34
- import { ClaimType, ClaimAuthority, ClaimStatus, SourceEvent, MemoryProposal, BehaviorProposal } from './proposals';
34
+ import { ClaimType, ClaimAuthority, ClaimStatus, DirectiveStatus, SourceEvent, MemoryProposal, BehaviorProposal } from './proposals';
35
35
  export interface OrganConfig {
36
36
  provider: string;
37
37
  [key: string]: unknown;
@@ -61,6 +61,8 @@ export interface BrainContext {
61
61
  export interface ResponsePlan {
62
62
  speech: string;
63
63
  language: string;
64
+ subtitle?: string;
65
+ subtitles?: Record<string, string>;
64
66
  memoryProposals?: MemoryProposal[];
65
67
  behaviorProposals?: BehaviorProposal[];
66
68
  actionIntents?: ActionIntent[];
@@ -98,7 +100,7 @@ export interface BehaviorDirective {
98
100
  companionId: string;
99
101
  directive: string;
100
102
  priority: number;
101
- status: 'PENDING' | 'ACTIVE' | 'DISABLED' | 'SUPERSEDED' | 'REJECTED' | 'REVOKED' | 'EXPIRED';
103
+ status: DirectiveStatus;
102
104
  supersedesId?: string;
103
105
  memoryClass?: 'identity' | 'relationship' | 'behavioral';
104
106
  subject?: string;
@@ -91,7 +91,7 @@ async function settleMemoryProposals(params) {
91
91
  subject: p.subject,
92
92
  predicate: p.predicate,
93
93
  value: p.value,
94
- status: p.status,
94
+ status: (p.status || 'pending').toLowerCase().replace(/_/g, '-'),
95
95
  }));
96
96
  return {
97
97
  createdMemoryProposals,
@@ -17,6 +17,9 @@ export interface MouthUtterance {
17
17
  subtitleJa?: string;
18
18
  subtitleEn?: string;
19
19
  spokenJa?: string;
20
+ subtitle?: string;
21
+ subtitleLanguage?: string;
22
+ subtitles?: Record<string, string>;
20
23
  expression?: string;
21
24
  action?: string;
22
25
  medium?: MouthMedium;
@@ -35,10 +38,12 @@ export interface FormattedMouthOutput {
35
38
  ssml?: string;
36
39
  visemes?: MouthVisemeCue[];
37
40
  subtitles?: {
38
- ja: string;
39
- en: string;
41
+ ja?: string;
42
+ en?: string;
40
43
  spoken?: string;
44
+ [lang: string]: string | undefined;
41
45
  };
46
+ subtitle?: string;
42
47
  audioUrl?: string;
43
48
  audioBuffer?: Uint8Array;
44
49
  expression?: string;
@@ -14,6 +14,7 @@ export interface CompanionPerception {
14
14
  context?: RequestContext;
15
15
  history?: Message[];
16
16
  medium?: MouthMedium;
17
+ subtitleLanguage?: string;
17
18
  metadata?: Record<string, unknown>;
18
19
  signal?: AbortSignal;
19
20
  }
@@ -93,6 +93,7 @@ const promptCompilationStage = async (context) => {
93
93
  memoryData: context.contextRetrieval.memoryData,
94
94
  lifeContext: context.contextRetrieval.lifeContext,
95
95
  effectiveMode: context.intent?.effectiveMode,
96
+ subtitleLanguage: context.perception.subtitleLanguage,
96
97
  });
97
98
  context.prompts = prompts;
98
99
  };
@@ -198,6 +199,9 @@ const mouthDeliveryStage = async (context) => {
198
199
  subtitleJa: context.plan.speech,
199
200
  subtitleEn: context.plan.speech,
200
201
  spokenJa: context.plan.speech,
202
+ subtitle: context.plan.subtitle,
203
+ subtitles: context.plan.subtitles,
204
+ subtitleLanguage: context.perception.subtitleLanguage,
201
205
  expression: avatarEvent?.expression,
202
206
  medium: context.perception.medium,
203
207
  signal: context.perception.signal,
@@ -226,6 +230,9 @@ const envelopeAssemblyStage = async (context) => {
226
230
  stagedPlan: context.stagedPlan,
227
231
  speech: context.plan.speech,
228
232
  language: context.plan.language,
233
+ subtitle: context.plan.subtitle,
234
+ subtitles: context.plan.subtitles,
235
+ subtitleLanguage: context.perception.subtitleLanguage,
229
236
  speechId: context.experienceEmission?.speechId,
230
237
  createdMemoryProposals: context.memorySettlement.createdMemoryProposals,
231
238
  memoryProposalReceipts: context.memorySettlement.memoryProposalReceipts,
@@ -11,6 +11,7 @@ export interface PromptCompilationParams {
11
11
  memoryData: Claim[];
12
12
  lifeContext?: string[];
13
13
  effectiveMode?: InteractionMode;
14
+ subtitleLanguage?: string;
14
15
  }
15
16
  export interface CompiledPrompts {
16
17
  systemPrompt: string;
@@ -5,7 +5,7 @@ exports.compilePrompts = compilePrompts;
5
5
  * Compiles neutral system prompt and formatted context prompt from structured data.
6
6
  */
7
7
  async function compilePrompts(params) {
8
- const { companionName, companionId, role, requestContext, behavior, activeDirectives, subsystemDiagnostics, knowledgeData, memoryData, lifeContext, effectiveMode, } = params;
8
+ const { companionName, companionId, role, requestContext, behavior, activeDirectives, subsystemDiagnostics, knowledgeData, memoryData, lifeContext, effectiveMode, subtitleLanguage, } = params;
9
9
  let contextPrompt = '';
10
10
  if (Object.keys(subsystemDiagnostics).length > 0) {
11
11
  contextPrompt +=
@@ -48,9 +48,13 @@ async function compilePrompts(params) {
48
48
  : effectiveMode === 'teach'
49
49
  ? 'Operating Mode: Teach Mode (Active learning session - accurately capture user preferences and proposed boundaries for operator review).'
50
50
  : undefined;
51
+ const subtitleInstruction = subtitleLanguage && subtitleLanguage !== 'off'
52
+ ? `Requested Subtitle Language: "${subtitleLanguage}". Along with your primary speech, provide a natural subtitle translation in "${subtitleLanguage}" in the subtitle field.`
53
+ : undefined;
51
54
  const systemPrompt = [
52
55
  `You are ${companionName}.`,
53
56
  modeInstruction,
57
+ subtitleInstruction,
54
58
  'This is a neutral conversation context.',
55
59
  'Use only approved, permitted memory as factual personal context.',
56
60
  'Do not claim prior personal knowledge when no approved memory supports it.',
@@ -1,6 +1,7 @@
1
1
  export type ClaimType = 'semantic' | 'preference' | 'episodic' | 'relationship';
2
2
  export type ClaimAuthority = 'user_explicit' | 'user_correction' | 'import' | 'repeated_dialogue' | 'inference' | 'observation';
3
- export type ClaimStatus = 'PENDING' | 'APPROVED' | 'REJECTED' | 'SESSION_ONLY' | 'EXPIRED' | 'SUPERSEDED' | 'REVOKED';
3
+ export type ClaimStatus = 'pending' | 'approved' | 'rejected' | 'session-only' | 'expired' | 'superseded' | 'revoked' | 'PENDING' | 'APPROVED' | 'REJECTED' | 'SESSION_ONLY' | 'EXPIRED' | 'SUPERSEDED' | 'REVOKED';
4
+ export type DirectiveStatus = 'pending' | 'active' | 'disabled' | 'superseded' | 'rejected' | 'revoked' | 'expired' | 'confirmed' | 'PENDING' | 'ACTIVE' | 'DISABLED' | 'SUPERSEDED' | 'REJECTED' | 'REVOKED' | 'EXPIRED';
4
5
  export interface SourceEvent {
5
6
  id: string;
6
7
  sourceType: string;
@@ -5,6 +5,9 @@ export interface AssembleResponseEnvelopeParams {
5
5
  stagedPlan: StagedResponsePlan;
6
6
  speech: string;
7
7
  language?: string;
8
+ subtitle?: string;
9
+ subtitles?: Record<string, string>;
10
+ subtitleLanguage?: string;
8
11
  speechId?: string;
9
12
  createdMemoryProposals: Claim[];
10
13
  memoryProposalReceipts: MemoryProposalReceipt[];
@@ -29,7 +29,14 @@ function createGateRejectionEnvelope(stagedPlan, gateEval) {
29
29
  * Assembles the standardized response structure for approved companion responses.
30
30
  */
31
31
  function assembleResponseEnvelope(params) {
32
- const { stagedPlan, speech, language, speechId, createdMemoryProposals, memoryProposalReceipts, actionResults, filteredEvidenceIds, filteredCitations, subsystemDiagnostics, experienceEvents, mouthDelivery, effectiveMode, } = params;
32
+ const { stagedPlan, speech, language, subtitle, subtitles, subtitleLanguage, speechId, createdMemoryProposals, memoryProposalReceipts, actionResults, filteredEvidenceIds, filteredCitations, subsystemDiagnostics, experienceEvents, mouthDelivery, effectiveMode, } = params;
33
+ const resolvedSubtitles = {
34
+ ...(mouthDelivery?.subtitles || {}),
35
+ ...(subtitles || {}),
36
+ };
37
+ if (subtitle && subtitleLanguage) {
38
+ resolvedSubtitles[subtitleLanguage] = subtitle;
39
+ }
33
40
  return {
34
41
  status: 'APPROVED',
35
42
  response_id: stagedPlan.responseId,
@@ -37,8 +44,11 @@ function assembleResponseEnvelope(params) {
37
44
  response: {
38
45
  speech_id: speechId,
39
46
  audio_url: mouthDelivery?.audioUrl ?? (speechId ? `/voice/stream?id=${speechId}` : undefined),
40
- subtitle_ja: mouthDelivery?.subtitles?.ja ?? speech,
41
- subtitle_en: mouthDelivery?.subtitles?.en ?? speech,
47
+ subtitle_ja: mouthDelivery?.subtitles?.ja ?? resolvedSubtitles['ja'] ?? speech,
48
+ subtitle_en: mouthDelivery?.subtitles?.en ?? resolvedSubtitles['en'] ?? speech,
49
+ subtitle: subtitle ?? (subtitleLanguage ? resolvedSubtitles[subtitleLanguage] : undefined),
50
+ subtitle_language: subtitleLanguage,
51
+ subtitles: resolvedSubtitles,
42
52
  },
43
53
  delivery: mouthDelivery,
44
54
  metadata: {
package/dist/runtime.d.ts CHANGED
@@ -46,5 +46,5 @@ export declare class SiduriRuntime {
46
46
  /**
47
47
  * Primary entrypoint for text chat messages.
48
48
  */
49
- handleUserMessage(message: string, roleOrContext?: 'OWNER' | 'VIEWER' | 'OPERATOR' | RequestContext | string, history?: Message[], medium?: MouthMedium, signal?: AbortSignal): Promise<any>;
49
+ handleUserMessage(message: string, roleOrContext?: 'OWNER' | 'VIEWER' | 'OPERATOR' | RequestContext | string, history?: Message[], medium?: MouthMedium, signal?: AbortSignal, subtitleLanguage?: string): Promise<any>;
50
50
  }
package/dist/runtime.js CHANGED
@@ -81,7 +81,7 @@ class SiduriRuntime {
81
81
  /**
82
82
  * Primary entrypoint for text chat messages.
83
83
  */
84
- async handleUserMessage(message, roleOrContext = 'OWNER', history = [], medium, signal) {
84
+ async handleUserMessage(message, roleOrContext = 'OWNER', history = [], medium, signal, subtitleLanguage) {
85
85
  return this.processPerception({
86
86
  source: 'text_chat',
87
87
  text: message,
@@ -89,6 +89,7 @@ class SiduriRuntime {
89
89
  history,
90
90
  medium,
91
91
  signal,
92
+ subtitleLanguage,
92
93
  });
93
94
  }
94
95
  }
@@ -1,3 +1,4 @@
1
+ import { ClaimStatus, DirectiveStatus } from './proposals';
1
2
  export interface SelfIdentity {
2
3
  companionId: string;
3
4
  name: string;
@@ -19,7 +20,7 @@ export interface SelfDirective {
19
20
  companionId: string;
20
21
  priority?: number;
21
22
  directive: string;
22
- status: 'PENDING' | 'ACTIVE' | 'DISABLED' | 'SUPERSEDED' | 'REJECTED' | 'REVOKED' | 'EXPIRED';
23
+ status: DirectiveStatus;
23
24
  category: 'behavioral' | 'guardrail' | 'relational' | string;
24
25
  scopeActor?: string;
25
26
  supersedesId?: string;
@@ -90,7 +91,7 @@ export interface MemoryClaim {
90
91
  subject: string;
91
92
  predicate: string;
92
93
  value: string;
93
- status: 'PENDING' | 'APPROVED' | 'REJECTED' | 'SESSION_ONLY' | 'SUPERSEDED' | 'REVOKED' | 'EXPIRED';
94
+ status: ClaimStatus;
94
95
  confidence: number;
95
96
  validFrom?: string;
96
97
  validUntil?: string;
@@ -99,6 +100,7 @@ export interface MemoryClaim {
99
100
  supersedes?: string;
100
101
  sourceEventId?: string;
101
102
  }
103
+ export declare function normalizeStatus(status?: string, defaultStatus?: string): string;
102
104
  export interface SiduriDatabaseOptions {
103
105
  dbPath?: string;
104
106
  }
@@ -111,7 +113,9 @@ export declare class SiduriDatabase {
111
113
  setIdentity(identity: SelfIdentity): void;
112
114
  getPersonality(companionId: string): PersonalityTraits | undefined;
113
115
  setPersonality(companionId: string, traits: PersonalityTraits): void;
116
+ private rowToSelfDirective;
114
117
  getActiveDirectives(companionId: string): SelfDirective[];
118
+ getAllDirectives(companionId: string): SelfDirective[];
115
119
  commitDirective(directive: SelfDirective): void;
116
120
  getDirective(id: string, companionId?: string): SelfDirective | undefined;
117
121
  approveDirective(id: string, companionId?: string): void;
@@ -135,11 +139,13 @@ export declare class SiduriDatabase {
135
139
  recordEvent(event: EpisodicEvent): void;
136
140
  getRecentEvents(companionId: string, limit?: number): EpisodicEvent[];
137
141
  getEvent(id: string): EpisodicEvent | undefined;
142
+ private rowToMemoryClaim;
138
143
  proposeClaim(claim: Omit<MemoryClaim, 'status' | 'confidence' | 'assertedAt'> & {
139
144
  confidence?: number;
140
145
  assertedAt?: string;
141
146
  supersedes?: string;
142
147
  sourceEventId?: string;
148
+ status?: string;
143
149
  }): MemoryClaim;
144
150
  approveClaim(id: string, companionId?: string): void;
145
151
  rejectClaim(id: string, companionId?: string): void;
@@ -149,6 +155,7 @@ export declare class SiduriDatabase {
149
155
  searchClaims(companionId: string, query: string, limit?: number): MemoryClaim[];
150
156
  getPendingClaims(companionId: string, limit?: number): MemoryClaim[];
151
157
  getApprovedClaims(companionId: string, limit?: number): MemoryClaim[];
158
+ getAllClaims(companionId?: string, limit?: number): MemoryClaim[];
152
159
  getClaim(id: string): MemoryClaim | undefined;
153
160
  resetMemory(companionId: string): void;
154
161
  }
package/dist/siduri-db.js CHANGED
@@ -1,10 +1,16 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.SiduriDatabase = void 0;
4
+ exports.normalizeStatus = normalizeStatus;
4
5
  // eslint-disable-next-line @typescript-eslint/no-var-requires
5
6
  const crypto = require('crypto');
6
7
  // eslint-disable-next-line @typescript-eslint/no-var-requires
7
8
  const { DatabaseSync } = require('node:sqlite');
9
+ function normalizeStatus(status, defaultStatus = 'pending') {
10
+ if (!status)
11
+ return defaultStatus;
12
+ return status.toLowerCase().replace(/_/g, '-');
13
+ }
8
14
  class SiduriDatabase {
9
15
  db;
10
16
  constructor(options = {}) {
@@ -41,7 +47,7 @@ class SiduriDatabase {
41
47
  companion_id TEXT NOT NULL,
42
48
  priority INTEGER DEFAULT 50,
43
49
  directive TEXT NOT NULL,
44
- status TEXT DEFAULT 'ACTIVE',
50
+ status TEXT DEFAULT 'active',
45
51
  category TEXT DEFAULT 'behavioral',
46
52
  scope_actor TEXT,
47
53
  supersedes_id TEXT,
@@ -123,7 +129,7 @@ class SiduriDatabase {
123
129
  subject TEXT NOT NULL,
124
130
  predicate TEXT NOT NULL,
125
131
  value TEXT NOT NULL,
126
- status TEXT DEFAULT 'PENDING',
132
+ status TEXT DEFAULT 'pending',
127
133
  confidence REAL DEFAULT 1.0,
128
134
  valid_from TEXT,
129
135
  valid_until TEXT,
@@ -272,30 +278,41 @@ class SiduriDatabase {
272
278
  `);
273
279
  stmt.run(companionId, traits.warmth, traits.formality, traits.sarcasm, traits.verbosity, traits.curiosity);
274
280
  }
275
- getActiveDirectives(companionId) {
276
- const stmt = this.db.prepare(`
277
- SELECT * FROM self_directives
278
- WHERE companion_id = ? AND status = 'ACTIVE'
279
- ORDER BY priority DESC, created_at ASC
280
- `);
281
- return stmt.all(companionId).map((row) => ({
281
+ rowToSelfDirective(row) {
282
+ return {
282
283
  id: row.id,
283
284
  companionId: row.companion_id,
284
285
  priority: row.priority,
285
286
  directive: row.directive,
286
- status: row.status,
287
+ status: normalizeStatus(row.status, 'active'),
287
288
  category: row.category,
288
289
  scopeActor: row.scope_actor || undefined,
289
290
  supersedesId: row.supersedes_id || undefined,
290
- createdAt: row.created_at
291
- }));
291
+ createdAt: row.created_at,
292
+ };
293
+ }
294
+ getActiveDirectives(companionId) {
295
+ const stmt = this.db.prepare(`
296
+ SELECT * FROM self_directives
297
+ WHERE companion_id = ? AND LOWER(status) = 'active'
298
+ ORDER BY priority DESC, created_at ASC
299
+ `);
300
+ return stmt.all(companionId).map((row) => this.rowToSelfDirective(row));
301
+ }
302
+ getAllDirectives(companionId) {
303
+ const stmt = this.db.prepare(`
304
+ SELECT * FROM self_directives
305
+ WHERE companion_id = ?
306
+ ORDER BY priority DESC, created_at ASC
307
+ `);
308
+ return stmt.all(companionId).map((row) => this.rowToSelfDirective(row));
292
309
  }
293
310
  commitDirective(directive) {
294
311
  const stmt = this.db.prepare(`
295
312
  INSERT INTO self_directives (id, companion_id, priority, directive, status, category, scope_actor, supersedes_id, created_at)
296
313
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
297
314
  `);
298
- stmt.run(directive.id, directive.companionId, directive.priority !== undefined ? directive.priority : 50, directive.directive, directive.status, directive.category || 'behavioral', directive.scopeActor || null, directive.supersedesId || null, directive.createdAt || new Date().toISOString());
315
+ stmt.run(directive.id, directive.companionId, directive.priority !== undefined ? directive.priority : 50, directive.directive, normalizeStatus(directive.status, 'active'), directive.category || 'behavioral', directive.scopeActor || null, directive.supersedesId || null, directive.createdAt || new Date().toISOString());
299
316
  }
300
317
  getDirective(id, companionId) {
301
318
  const stmt = companionId
@@ -304,17 +321,7 @@ class SiduriDatabase {
304
321
  const row = (companionId ? stmt.get(id, companionId) : stmt.get(id));
305
322
  if (!row)
306
323
  return undefined;
307
- return {
308
- id: row.id,
309
- companionId: row.companion_id,
310
- priority: row.priority,
311
- directive: row.directive,
312
- status: row.status,
313
- category: row.category,
314
- scopeActor: row.scope_actor || undefined,
315
- supersedesId: row.supersedes_id || undefined,
316
- createdAt: row.created_at,
317
- };
324
+ return this.rowToSelfDirective(row);
318
325
  }
319
326
  approveDirective(id, companionId) {
320
327
  const findStmt = companionId
@@ -324,28 +331,28 @@ class SiduriDatabase {
324
331
  if (!row) {
325
332
  return;
326
333
  }
327
- if (row.status !== 'PENDING') {
328
- throw new Error(`Cannot approve directive '${id}': invalid transition from status '${row.status}' to 'ACTIVE' (only PENDING directives can be approved)`);
334
+ if (normalizeStatus(row.status) !== 'pending') {
335
+ throw new Error(`Cannot approve directive '${id}': invalid transition from status '${row.status}' to 'active' (only pending directives can be approved)`);
329
336
  }
330
- // If this directive supersedes an earlier directive, transition that prior directive to SUPERSEDED
337
+ // If this directive supersedes an earlier directive, transition that prior directive to superseded
331
338
  if (row.supersedes_id) {
332
339
  const supersededId = row.supersedes_id;
333
340
  const effectiveCompanionId = companionId || row.companion_id;
334
341
  if (effectiveCompanionId) {
335
- const supersedeStmt = this.db.prepare("UPDATE self_directives SET status = 'SUPERSEDED' WHERE id = ? AND companion_id = ?");
342
+ const supersedeStmt = this.db.prepare("UPDATE self_directives SET status = 'superseded' WHERE id = ? AND companion_id = ?");
336
343
  supersedeStmt.run(supersededId, effectiveCompanionId);
337
344
  }
338
345
  else {
339
- const supersedeStmt = this.db.prepare("UPDATE self_directives SET status = 'SUPERSEDED' WHERE id = ?");
346
+ const supersedeStmt = this.db.prepare("UPDATE self_directives SET status = 'superseded' WHERE id = ?");
340
347
  supersedeStmt.run(supersededId);
341
348
  }
342
349
  }
343
350
  if (companionId) {
344
- const stmt = this.db.prepare("UPDATE self_directives SET status = 'ACTIVE' WHERE id = ? AND companion_id = ? AND status = 'PENDING'");
351
+ const stmt = this.db.prepare("UPDATE self_directives SET status = 'active' WHERE id = ? AND companion_id = ? AND LOWER(status) = 'pending'");
345
352
  stmt.run(id, companionId);
346
353
  }
347
354
  else {
348
- const stmt = this.db.prepare("UPDATE self_directives SET status = 'ACTIVE' WHERE id = ? AND status = 'PENDING'");
355
+ const stmt = this.db.prepare("UPDATE self_directives SET status = 'active' WHERE id = ? AND LOWER(status) = 'pending'");
349
356
  stmt.run(id);
350
357
  }
351
358
  }
@@ -357,45 +364,45 @@ class SiduriDatabase {
357
364
  if (!row) {
358
365
  return;
359
366
  }
360
- if (row.status !== 'PENDING') {
361
- throw new Error(`Cannot reject directive '${id}': invalid transition from status '${row.status}' to 'REJECTED' (only PENDING directives can be rejected)`);
367
+ if (normalizeStatus(row.status) !== 'pending') {
368
+ throw new Error(`Cannot reject directive '${id}': invalid transition from status '${row.status}' to 'rejected' (only pending directives can be rejected)`);
362
369
  }
363
370
  if (companionId) {
364
- const stmt = this.db.prepare("UPDATE self_directives SET status = 'REJECTED' WHERE id = ? AND companion_id = ? AND status = 'PENDING'");
371
+ const stmt = this.db.prepare("UPDATE self_directives SET status = 'rejected' WHERE id = ? AND companion_id = ? AND LOWER(status) = 'pending'");
365
372
  stmt.run(id, companionId);
366
373
  }
367
374
  else {
368
- const stmt = this.db.prepare("UPDATE self_directives SET status = 'REJECTED' WHERE id = ? AND status = 'PENDING'");
375
+ const stmt = this.db.prepare("UPDATE self_directives SET status = 'rejected' WHERE id = ? AND LOWER(status) = 'pending'");
369
376
  stmt.run(id);
370
377
  }
371
378
  }
372
379
  revokeDirective(id, companionId) {
373
380
  if (companionId) {
374
- const stmt = this.db.prepare("UPDATE self_directives SET status = 'REVOKED' WHERE id = ? AND companion_id = ?");
381
+ const stmt = this.db.prepare("UPDATE self_directives SET status = 'revoked' WHERE id = ? AND companion_id = ?");
375
382
  stmt.run(id, companionId);
376
383
  }
377
384
  else {
378
- const stmt = this.db.prepare("UPDATE self_directives SET status = 'REVOKED' WHERE id = ?");
385
+ const stmt = this.db.prepare("UPDATE self_directives SET status = 'revoked' WHERE id = ?");
379
386
  stmt.run(id);
380
387
  }
381
388
  }
382
389
  expireDirective(id, companionId) {
383
390
  if (companionId) {
384
- const stmt = this.db.prepare("UPDATE self_directives SET status = 'EXPIRED' WHERE id = ? AND companion_id = ?");
391
+ const stmt = this.db.prepare("UPDATE self_directives SET status = 'expired' WHERE id = ? AND companion_id = ?");
385
392
  stmt.run(id, companionId);
386
393
  }
387
394
  else {
388
- const stmt = this.db.prepare("UPDATE self_directives SET status = 'EXPIRED' WHERE id = ?");
395
+ const stmt = this.db.prepare("UPDATE self_directives SET status = 'expired' WHERE id = ?");
389
396
  stmt.run(id);
390
397
  }
391
398
  }
392
399
  disableDirective(id, companionId) {
393
400
  if (companionId) {
394
- const stmt = this.db.prepare("UPDATE self_directives SET status = 'DISABLED' WHERE id = ? AND companion_id = ?");
401
+ const stmt = this.db.prepare("UPDATE self_directives SET status = 'disabled' WHERE id = ? AND companion_id = ?");
395
402
  stmt.run(id, companionId);
396
403
  }
397
404
  else {
398
- const stmt = this.db.prepare("UPDATE self_directives SET status = 'DISABLED' WHERE id = ?");
405
+ const stmt = this.db.prepare("UPDATE self_directives SET status = 'disabled' WHERE id = ?");
399
406
  stmt.run(id);
400
407
  }
401
408
  }
@@ -603,9 +610,26 @@ class SiduriDatabase {
603
610
  payload: JSON.parse(row.payload)
604
611
  };
605
612
  }
613
+ rowToMemoryClaim(row) {
614
+ return {
615
+ id: row.id,
616
+ companionId: row.companion_id,
617
+ subject: row.subject,
618
+ predicate: row.predicate,
619
+ value: row.value,
620
+ status: normalizeStatus(row.status, 'pending'),
621
+ confidence: row.confidence,
622
+ validFrom: row.valid_from || undefined,
623
+ validUntil: row.valid_until || undefined,
624
+ evidence: row.evidence ? (typeof row.evidence === 'string' ? JSON.parse(row.evidence) : row.evidence) : undefined,
625
+ assertedAt: row.asserted_at,
626
+ supersedes: row.supersedes || undefined,
627
+ sourceEventId: row.source_event_id || undefined,
628
+ };
629
+ }
606
630
  proposeClaim(claim) {
607
631
  const id = claim.id || crypto.randomUUID();
608
- const status = 'PENDING';
632
+ const status = normalizeStatus(claim.status, 'pending');
609
633
  const confidence = claim.confidence ?? 1.0;
610
634
  const assertedAt = claim.assertedAt || new Date().toISOString();
611
635
  const stmt = this.db.prepare(`
@@ -616,7 +640,7 @@ class SiduriDatabase {
616
640
  return {
617
641
  ...claim,
618
642
  id,
619
- status,
643
+ status: status,
620
644
  confidence,
621
645
  assertedAt,
622
646
  supersedes: claim.supersedes,
@@ -631,67 +655,67 @@ class SiduriDatabase {
631
655
  if (!row) {
632
656
  return;
633
657
  }
634
- if (row.status !== 'PENDING') {
635
- throw new Error(`Cannot approve claim '${id}': invalid transition from status '${row.status}' to 'APPROVED' (only PENDING claims can be approved)`);
658
+ if (normalizeStatus(row.status) !== 'pending') {
659
+ throw new Error(`Cannot approve claim '${id}': invalid transition from status '${row.status}' to 'approved' (only pending claims can be approved)`);
636
660
  }
637
- // If this claim supersedes an earlier claim, transition that prior claim to SUPERSEDED
661
+ // If this claim supersedes an earlier claim, transition that prior claim to superseded
638
662
  if (row.supersedes) {
639
663
  const supersededId = row.supersedes;
640
664
  if (companionId) {
641
- const supersedeStmt = this.db.prepare("UPDATE memory_claims SET status = 'SUPERSEDED' WHERE id = ? AND companion_id = ?");
665
+ const supersedeStmt = this.db.prepare("UPDATE memory_claims SET status = 'superseded' WHERE id = ? AND companion_id = ?");
642
666
  supersedeStmt.run(supersededId, companionId);
643
667
  }
644
668
  else {
645
- const supersedeStmt = this.db.prepare("UPDATE memory_claims SET status = 'SUPERSEDED' WHERE id = ?");
669
+ const supersedeStmt = this.db.prepare("UPDATE memory_claims SET status = 'superseded' WHERE id = ?");
646
670
  supersedeStmt.run(supersededId);
647
671
  }
648
672
  }
649
673
  if (companionId) {
650
- const stmt = this.db.prepare("UPDATE memory_claims SET status = 'APPROVED' WHERE id = ? AND companion_id = ? AND status = 'PENDING'");
674
+ const stmt = this.db.prepare("UPDATE memory_claims SET status = 'approved' WHERE id = ? AND companion_id = ? AND LOWER(status) = 'pending'");
651
675
  stmt.run(id, companionId);
652
676
  }
653
677
  else {
654
- const stmt = this.db.prepare("UPDATE memory_claims SET status = 'APPROVED' WHERE id = ? AND status = 'PENDING'");
678
+ const stmt = this.db.prepare("UPDATE memory_claims SET status = 'approved' WHERE id = ? AND LOWER(status) = 'pending'");
655
679
  stmt.run(id);
656
680
  }
657
681
  }
658
682
  rejectClaim(id, companionId) {
659
683
  if (companionId) {
660
- const stmt = this.db.prepare("UPDATE memory_claims SET status = 'REJECTED' WHERE id = ? AND companion_id = ?");
684
+ const stmt = this.db.prepare("UPDATE memory_claims SET status = 'rejected' WHERE id = ? AND companion_id = ?");
661
685
  stmt.run(id, companionId);
662
686
  }
663
687
  else {
664
- const stmt = this.db.prepare("UPDATE memory_claims SET status = 'REJECTED' WHERE id = ?");
688
+ const stmt = this.db.prepare("UPDATE memory_claims SET status = 'rejected' WHERE id = ?");
665
689
  stmt.run(id);
666
690
  }
667
691
  }
668
692
  revokeClaim(id, companionId) {
669
693
  if (companionId) {
670
- const stmt = this.db.prepare("UPDATE memory_claims SET status = 'REVOKED' WHERE id = ? AND companion_id = ?");
694
+ const stmt = this.db.prepare("UPDATE memory_claims SET status = 'revoked' WHERE id = ? AND companion_id = ?");
671
695
  stmt.run(id, companionId);
672
696
  }
673
697
  else {
674
- const stmt = this.db.prepare("UPDATE memory_claims SET status = 'REVOKED' WHERE id = ?");
698
+ const stmt = this.db.prepare("UPDATE memory_claims SET status = 'revoked' WHERE id = ?");
675
699
  stmt.run(id);
676
700
  }
677
701
  }
678
702
  expireClaim(id, companionId) {
679
703
  if (companionId) {
680
- const stmt = this.db.prepare("UPDATE memory_claims SET status = 'EXPIRED' WHERE id = ? AND companion_id = ?");
704
+ const stmt = this.db.prepare("UPDATE memory_claims SET status = 'expired' WHERE id = ? AND companion_id = ?");
681
705
  stmt.run(id, companionId);
682
706
  }
683
707
  else {
684
- const stmt = this.db.prepare("UPDATE memory_claims SET status = 'EXPIRED' WHERE id = ?");
708
+ const stmt = this.db.prepare("UPDATE memory_claims SET status = 'expired' WHERE id = ?");
685
709
  stmt.run(id);
686
710
  }
687
711
  }
688
712
  markClaimSessionOnly(id, companionId) {
689
713
  if (companionId) {
690
- const stmt = this.db.prepare("UPDATE memory_claims SET status = 'SESSION_ONLY' WHERE id = ? AND companion_id = ?");
714
+ const stmt = this.db.prepare("UPDATE memory_claims SET status = 'session-only' WHERE id = ? AND companion_id = ?");
691
715
  stmt.run(id, companionId);
692
716
  }
693
717
  else {
694
- const stmt = this.db.prepare("UPDATE memory_claims SET status = 'SESSION_ONLY' WHERE id = ?");
718
+ const stmt = this.db.prepare("UPDATE memory_claims SET status = 'session-only' WHERE id = ?");
695
719
  stmt.run(id);
696
720
  }
697
721
  }
@@ -699,82 +723,33 @@ class SiduriDatabase {
699
723
  const stmt = this.db.prepare(`
700
724
  SELECT c.* FROM memory_claims c
701
725
  JOIN memory_search s ON c.rowid = s.rowid
702
- WHERE c.companion_id = ? AND c.status = 'APPROVED' AND memory_search MATCH ?
726
+ WHERE c.companion_id = ? AND LOWER(c.status) = 'approved' AND memory_search MATCH ?
703
727
  ORDER BY rank
704
728
  LIMIT ?
705
729
  `);
706
- return stmt.all(companionId, query, limit).map((row) => ({
707
- id: row.id,
708
- companionId: row.companion_id,
709
- subject: row.subject,
710
- predicate: row.predicate,
711
- value: row.value,
712
- status: row.status,
713
- confidence: row.confidence,
714
- validFrom: row.valid_from || undefined,
715
- validUntil: row.valid_until || undefined,
716
- evidence: row.evidence ? JSON.parse(row.evidence) : undefined,
717
- assertedAt: row.asserted_at,
718
- supersedes: row.supersedes || undefined,
719
- sourceEventId: row.source_event_id || undefined,
720
- }));
730
+ return stmt.all(companionId, query, limit).map((row) => this.rowToMemoryClaim(row));
721
731
  }
722
732
  getPendingClaims(companionId, limit = 50) {
723
- const stmt = this.db.prepare("SELECT * FROM memory_claims WHERE companion_id = ? AND status = 'PENDING' ORDER BY asserted_at DESC LIMIT ?");
724
- return stmt.all(companionId, limit).map((row) => ({
725
- id: row.id,
726
- companionId: row.companion_id,
727
- subject: row.subject,
728
- predicate: row.predicate,
729
- value: row.value,
730
- status: row.status,
731
- confidence: row.confidence,
732
- validFrom: row.valid_from || undefined,
733
- validUntil: row.valid_until || undefined,
734
- evidence: row.evidence ? JSON.parse(row.evidence) : undefined,
735
- assertedAt: row.asserted_at,
736
- supersedes: row.supersedes || undefined,
737
- sourceEventId: row.source_event_id || undefined,
738
- }));
733
+ const stmt = this.db.prepare("SELECT * FROM memory_claims WHERE companion_id = ? AND LOWER(status) = 'pending' ORDER BY asserted_at DESC LIMIT ?");
734
+ return stmt.all(companionId, limit).map((row) => this.rowToMemoryClaim(row));
739
735
  }
740
736
  getApprovedClaims(companionId, limit = 50) {
741
- const stmt = this.db.prepare("SELECT * FROM memory_claims WHERE companion_id = ? AND status = 'APPROVED' ORDER BY asserted_at DESC LIMIT ?");
742
- return stmt.all(companionId, limit).map((row) => ({
743
- id: row.id,
744
- companionId: row.companion_id,
745
- subject: row.subject,
746
- predicate: row.predicate,
747
- value: row.value,
748
- status: row.status,
749
- confidence: row.confidence,
750
- validFrom: row.valid_from || undefined,
751
- validUntil: row.valid_until || undefined,
752
- evidence: row.evidence ? JSON.parse(row.evidence) : undefined,
753
- assertedAt: row.asserted_at,
754
- supersedes: row.supersedes || undefined,
755
- sourceEventId: row.source_event_id || undefined,
756
- }));
737
+ const stmt = this.db.prepare("SELECT * FROM memory_claims WHERE companion_id = ? AND LOWER(status) = 'approved' ORDER BY asserted_at DESC LIMIT ?");
738
+ return stmt.all(companionId, limit).map((row) => this.rowToMemoryClaim(row));
739
+ }
740
+ getAllClaims(companionId, limit = 100) {
741
+ const stmt = companionId
742
+ ? this.db.prepare("SELECT * FROM memory_claims WHERE companion_id = ? ORDER BY asserted_at DESC LIMIT ?")
743
+ : this.db.prepare("SELECT * FROM memory_claims ORDER BY asserted_at DESC LIMIT ?");
744
+ const rows = companionId ? stmt.all(companionId, limit) : stmt.all(limit);
745
+ return rows.map((row) => this.rowToMemoryClaim(row));
757
746
  }
758
747
  getClaim(id) {
759
748
  const stmt = this.db.prepare("SELECT * FROM memory_claims WHERE id = ?");
760
749
  const row = stmt.get(id);
761
750
  if (!row)
762
751
  return undefined;
763
- return {
764
- id: row.id,
765
- companionId: row.companion_id,
766
- subject: row.subject,
767
- predicate: row.predicate,
768
- value: row.value,
769
- status: row.status,
770
- confidence: row.confidence,
771
- validFrom: row.valid_from || undefined,
772
- validUntil: row.valid_until || undefined,
773
- evidence: row.evidence ? JSON.parse(row.evidence) : undefined,
774
- assertedAt: row.asserted_at,
775
- supersedes: row.supersedes || undefined,
776
- sourceEventId: row.source_event_id || undefined,
777
- };
752
+ return this.rowToMemoryClaim(row);
778
753
  }
779
754
  resetMemory(companionId) {
780
755
  const deleteClaims = this.db.prepare("DELETE FROM memory_claims WHERE companion_id = ?");
@@ -370,7 +370,7 @@ describe('SiduriDatabase', () => {
370
370
  evidence: ['lore book chapter 3'],
371
371
  assertedAt: new Date().toISOString(),
372
372
  });
373
- expect(claim.status).toBe('PENDING');
373
+ expect(claim.status).toBe('pending');
374
374
  expect(claim.subject).toBe('Kur');
375
375
  expect(claim.predicate).toBe('is');
376
376
  expect(claim.value).toBe('a dark entity from the underworld');
@@ -401,7 +401,7 @@ describe('SiduriDatabase', () => {
401
401
  const approved = db.getApprovedClaims('siduri-test');
402
402
  expect(approved).toHaveLength(1);
403
403
  expect(approved[0].id).toBe(claim1.id);
404
- expect(approved[0].status).toBe('APPROVED');
404
+ expect(approved[0].status).toBe('approved');
405
405
  });
406
406
  it('searches claims using FTS5 full-text search', () => {
407
407
  db = new siduri_db_1.SiduriDatabase({ dbPath });
@@ -617,7 +617,7 @@ describe('SiduriDatabase', () => {
617
617
  expect(db2.getInventory(cId)).toHaveLength(1);
618
618
  expect(db2.getInventory(cId)[0].entityName).toBe('Aged Wine');
619
619
  const claims = db2.searchClaims(cId, 'wine');
620
- expect(claims.some((c) => c.id === claim.id && c.status === 'APPROVED')).toBe(true);
620
+ expect(claims.some((c) => c.id === claim.id && c.status === 'approved')).toBe(true);
621
621
  db2.close();
622
622
  // Prevent afterEach from double-closing
623
623
  db = null;
@@ -691,7 +691,7 @@ describe('SiduriDatabase', () => {
691
691
  const active = db.getActiveDirectives(cId);
692
692
  expect(active).toHaveLength(1);
693
693
  expect(active[0].id).toBe(dId);
694
- expect(active[0].status).toBe('ACTIVE');
694
+ expect(active[0].status).toBe('active');
695
695
  // 3. Revoke directive
696
696
  db.revokeDirective(dId);
697
697
  expect(db.getActiveDirectives(cId)).toHaveLength(0);
@@ -702,7 +702,7 @@ describe('SiduriDatabase', () => {
702
702
  companionId: cId,
703
703
  priority: 50,
704
704
  directive: 'Unsafe rule',
705
- status: 'PENDING',
705
+ status: 'pending',
706
706
  category: 'behavioral',
707
707
  });
708
708
  db.rejectDirective(d2Id);
@@ -717,22 +717,22 @@ describe('SiduriDatabase', () => {
717
717
  companionId: cId,
718
718
  priority: 50,
719
719
  directive: 'Rejected directive',
720
- status: 'REJECTED',
720
+ status: 'rejected',
721
721
  category: 'behavioral',
722
722
  });
723
- expect(() => db.approveDirective('dir-rejected-1')).toThrow(/invalid transition from status 'REJECTED' to 'ACTIVE'/);
723
+ expect(() => db.approveDirective('dir-rejected-1')).toThrow(/invalid transition from status 'rejected' to 'active'/i);
724
724
  // 2. Commit directive in ACTIVE state
725
725
  db.commitDirective({
726
726
  id: 'dir-active-1',
727
727
  companionId: cId,
728
728
  priority: 50,
729
729
  directive: 'Already active directive',
730
- status: 'ACTIVE',
730
+ status: 'active',
731
731
  category: 'behavioral',
732
732
  });
733
- expect(() => db.approveDirective('dir-active-1')).toThrow(/invalid transition from status 'ACTIVE' to 'ACTIVE'/);
733
+ expect(() => db.approveDirective('dir-active-1')).toThrow(/invalid transition from status 'active' to 'active'/i);
734
734
  // 3. Rejecting an already ACTIVE directive throws
735
- expect(() => db.rejectDirective('dir-active-1')).toThrow(/invalid transition from status 'ACTIVE' to 'REJECTED'/);
735
+ expect(() => db.rejectDirective('dir-active-1')).toThrow(/invalid transition from status 'active' to 'rejected'/i);
736
736
  });
737
737
  it('automatically marks prior directive as SUPERSEDED when approving superseding directive', () => {
738
738
  db = new siduri_db_1.SiduriDatabase({ dbPath });
@@ -766,7 +766,7 @@ describe('SiduriDatabase', () => {
766
766
  expect(active).toHaveLength(1);
767
767
  expect(active[0].id).toBe('dir-replacement-1');
768
768
  const original = db.getDirective('dir-original-1', cId);
769
- expect(original?.status).toBe('SUPERSEDED');
769
+ expect(original?.status).toBe('superseded');
770
770
  });
771
771
  it('enforces companion isolation on directive approval', () => {
772
772
  db = new siduri_db_1.SiduriDatabase({ dbPath });
@@ -777,14 +777,14 @@ describe('SiduriDatabase', () => {
777
777
  companionId: cIdB,
778
778
  priority: 50,
779
779
  directive: 'Beta private rule',
780
- status: 'PENDING',
780
+ status: 'pending',
781
781
  category: 'behavioral',
782
782
  });
783
783
  // Alpha attempts to approve Beta's directive scoped to Alpha
784
784
  db.approveDirective('dir-beta-1', cIdA);
785
- // Beta's directive must remain PENDING and unapproved
785
+ // Beta's directive must remain pending and unapproved
786
786
  const betaDirective = db.getDirective('dir-beta-1', cIdB);
787
- expect(betaDirective?.status).toBe('PENDING');
787
+ expect(betaDirective?.status).toBe('pending');
788
788
  expect(db.getActiveDirectives(cIdB)).toHaveLength(0);
789
789
  // Beta approves its own directive successfully
790
790
  db.approveDirective('dir-beta-1', cIdB);
@@ -800,7 +800,7 @@ describe('SiduriDatabase', () => {
800
800
  predicate: 'likes',
801
801
  value: 'matcha',
802
802
  });
803
- expect(claim.status).toBe('PENDING');
803
+ expect(claim.status).toBe('pending');
804
804
  expect(db.getApprovedClaims(cId)).toHaveLength(0);
805
805
  // Approve
806
806
  db.approveClaim('claim-1');
@@ -835,7 +835,7 @@ describe('SiduriDatabase', () => {
835
835
  });
836
836
  db.rejectClaim(rejectedClaim.id);
837
837
  // Attempting to approve a REJECTED claim must throw
838
- expect(() => db.approveClaim(rejectedClaim.id)).toThrow(/invalid transition from status 'REJECTED' to 'APPROVED'/);
838
+ expect(() => db.approveClaim(rejectedClaim.id)).toThrow(/invalid transition from status 'rejected' to 'approved'/i);
839
839
  // 2. Propose and approve claim, then revoke
840
840
  const revokedClaim = db.proposeClaim({
841
841
  id: 'claim-revoked',
@@ -847,7 +847,7 @@ describe('SiduriDatabase', () => {
847
847
  db.approveClaim(revokedClaim.id);
848
848
  db.revokeClaim(revokedClaim.id);
849
849
  // Attempting to approve a REVOKED claim must throw
850
- expect(() => db.approveClaim(revokedClaim.id)).toThrow(/invalid transition from status 'REVOKED' to 'APPROVED'/);
850
+ expect(() => db.approveClaim(revokedClaim.id)).toThrow(/invalid transition from status 'revoked' to 'approved'/i);
851
851
  // 3. Propose and expire claim
852
852
  const expiredClaim = db.proposeClaim({
853
853
  id: 'claim-expired',
@@ -858,7 +858,7 @@ describe('SiduriDatabase', () => {
858
858
  });
859
859
  db.expireClaim(expiredClaim.id);
860
860
  // Attempting to approve an EXPIRED claim must throw
861
- expect(() => db.approveClaim(expiredClaim.id)).toThrow(/invalid transition from status 'EXPIRED' to 'APPROVED'/);
861
+ expect(() => db.approveClaim(expiredClaim.id)).toThrow(/invalid transition from status 'expired' to 'approved'/i);
862
862
  });
863
863
  });
864
864
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@siduri-x/core",
3
- "version": "2.0.3",
3
+ "version": "2.0.4",
4
4
  "description": "Core runtime types, evidence protocol, action dispatcher, capability validation, and SiduriRuntime protocol",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {