@siduri-x/core 1.0.4 → 1.0.7

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 (61) hide show
  1. package/dist/action-executor.d.ts +12 -0
  2. package/dist/action-executor.js +50 -0
  3. package/dist/action-policy.js +4 -9
  4. package/dist/architecture-boundary.test.js +1 -0
  5. package/dist/capability.d.ts +8 -0
  6. package/dist/capability.js +31 -4
  7. package/dist/chat-contract.d.ts +5 -1
  8. package/dist/chat-contract.js +26 -23
  9. package/dist/cognition-planner.d.ts +15 -0
  10. package/dist/cognition-planner.js +23 -0
  11. package/dist/context-retriever.d.ts +24 -0
  12. package/dist/context-retriever.js +92 -0
  13. package/dist/context.d.ts +11 -13
  14. package/dist/context.js +3 -23
  15. package/dist/context.test.js +7 -40
  16. package/dist/evidence.d.ts +12 -8
  17. package/dist/evidence.js +6 -4
  18. package/dist/experience-emitter.d.ts +21 -0
  19. package/dist/experience-emitter.js +48 -0
  20. package/dist/experience.d.ts +6 -5
  21. package/dist/experience.js +3 -2
  22. package/dist/experience.test.js +5 -4
  23. package/dist/gating.d.ts +3 -2
  24. package/dist/gating.js +4 -8
  25. package/dist/index.d.ts +27 -43
  26. package/dist/index.js +14 -0
  27. package/dist/input-normalizer.d.ts +14 -0
  28. package/dist/input-normalizer.js +58 -0
  29. package/dist/input-normalizer.test.d.ts +1 -0
  30. package/dist/input-normalizer.test.js +39 -0
  31. package/dist/intent-classifier.d.ts +24 -0
  32. package/dist/intent-classifier.js +53 -0
  33. package/dist/intent-classifier.test.d.ts +1 -0
  34. package/dist/intent-classifier.test.js +68 -0
  35. package/dist/memory-settler.d.ts +27 -0
  36. package/dist/memory-settler.js +95 -0
  37. package/dist/mouth-types.d.ts +85 -0
  38. package/dist/mouth-types.js +2 -0
  39. package/dist/perception-cycle.test.d.ts +1 -0
  40. package/dist/perception-cycle.test.js +155 -0
  41. package/dist/prompt-compiler.d.ts +20 -0
  42. package/dist/prompt-compiler.js +57 -0
  43. package/dist/prompt-compiler.test.d.ts +1 -0
  44. package/dist/prompt-compiler.test.js +76 -0
  45. package/dist/proposals.d.ts +30 -0
  46. package/dist/proposals.js +2 -0
  47. package/dist/response-envelope.d.ts +25 -0
  48. package/dist/response-envelope.js +64 -0
  49. package/dist/runtime-facades.test.d.ts +1 -0
  50. package/dist/runtime-facades.test.js +69 -0
  51. package/dist/runtime.d.ts +79 -15
  52. package/dist/runtime.js +327 -326
  53. package/dist/session-history.d.ts +20 -0
  54. package/dist/session-history.js +55 -0
  55. package/dist/session-history.test.d.ts +1 -0
  56. package/dist/session-history.test.js +38 -0
  57. package/dist/sqlite-action-store.d.ts +20 -0
  58. package/dist/sqlite-action-store.js +225 -0
  59. package/dist/sqlite-action-store.test.d.ts +1 -0
  60. package/dist/sqlite-action-store.test.js +252 -0
  61. package/package.json +1 -1
@@ -1,3 +1,4 @@
1
+ import { MemoryProposal, BehaviorProposal } from './proposals';
1
2
  export type EvidenceOrigin = 'knowledge' | 'observation' | 'ocr' | 'platform' | 'conversation';
2
3
  export type EvidenceTrust = 'configured' | 'provider' | 'untrusted';
3
4
  export type EvidenceSensitivity = 'public' | 'private' | 'restricted';
@@ -14,10 +15,11 @@ export interface EvidenceRecord {
14
15
  createdAt: string;
15
16
  expiresAt?: string;
16
17
  trust: EvidenceTrust;
17
- sensitivity: EvidenceSensitivity;
18
- allowedAudiences: string[];
18
+ sensitivity?: EvidenceSensitivity;
19
+ allowedAudiences?: string[];
19
20
  companionId: string;
20
21
  correlationId: string;
22
+ [key: string]: unknown;
21
23
  }
22
24
  export type ResponseApprovalStatus = 'STAGED' | 'APPROVED' | 'REJECTED' | 'EXPIRED' | 'EMITTED';
23
25
  export type ResponseGateReasonCode = 'APPROVED_DIRECT' | 'APPROVAL_REQUIRED' | 'UNKNOWN_APPROVAL_ID' | 'APPROVAL_ID_MISMATCH' | 'COMPANION_MISMATCH' | 'AUDIENCE_MISMATCH' | 'EVIDENCE_EXPIRED' | 'EVIDENCE_SENSITIVITY_EXCLUDED' | 'UNRESOLVED_LOW_CONFIDENCE' | 'EMPTY_SPEECH' | 'PROPOSAL_VALIDATION_FAILED' | 'EXPLICITLY_REJECTED';
@@ -32,8 +34,8 @@ export interface StagedResponsePlan {
32
34
  responseId: string;
33
35
  companionId: string;
34
36
  correlationId: string;
35
- channel: 'public' | 'direct' | 'private' | 'operator';
36
- audienceId: string;
37
+ channel?: string;
38
+ audienceId?: string;
37
39
  speech: string;
38
40
  language: string;
39
41
  evidenceIds: string[];
@@ -44,9 +46,10 @@ export interface StagedResponsePlan {
44
46
  status: ResponseApprovalStatus;
45
47
  createdAt: string;
46
48
  expiresAt?: string;
47
- memoryProposals?: any[];
48
- behaviorProposals?: any[];
49
+ memoryProposals?: MemoryProposal[];
50
+ behaviorProposals?: BehaviorProposal[];
49
51
  internalMonologue?: string;
52
+ [key: string]: unknown;
50
53
  }
51
54
  export interface ResponseGateEvaluation {
52
55
  admissible: boolean;
@@ -59,9 +62,10 @@ export interface ResponseGateEvaluation {
59
62
  }
60
63
  export interface EvidenceFilterOptions {
61
64
  companionId: string;
62
- channel: 'public' | 'direct' | 'private' | 'operator';
63
- audienceId: string;
65
+ channel?: string;
66
+ audienceId?: string;
64
67
  now?: string | Date;
68
+ [key: string]: unknown;
65
69
  }
66
70
  export declare function filterEvidenceRecords(records: EvidenceRecord[], options: EvidenceFilterOptions): {
67
71
  admitted: EvidenceRecord[];
package/dist/evidence.js CHANGED
@@ -19,16 +19,18 @@ function filterEvidenceRecords(records, options) {
19
19
  continue;
20
20
  }
21
21
  }
22
- // 3. Audience intersection
23
- if (record.allowedAudiences &&
22
+ // 3. Optional audience intersection (audience-public is accessible everywhere)
23
+ if (options.audienceId &&
24
+ record.allowedAudiences &&
24
25
  record.allowedAudiences.length > 0 &&
26
+ !record.allowedAudiences.includes('audience-public') &&
25
27
  !record.allowedAudiences.includes(options.audienceId)) {
26
28
  excluded.push({ record, reason: 'audience_not_allowed' });
27
29
  continue;
28
30
  }
29
- // 4. Sensitivity policy based on channel
31
+ // 4. Optional channel sensitivity policy (if channel explicitly specified)
30
32
  if (options.channel === 'public') {
31
- if (record.sensitivity !== 'public') {
33
+ if (record.sensitivity && record.sensitivity !== 'public') {
32
34
  excluded.push({ record, reason: 'sensitivity_private_in_public_channel' });
33
35
  continue;
34
36
  }
@@ -0,0 +1,21 @@
1
+ import { RequestContext, StagedResponsePlan, ResponseGateEvaluation, ExperienceDispatcher, ExperienceEvent, ExperienceAdapter, VoiceOrgan, BodyOrgan } from './index';
2
+ export interface ExperienceEmissionParams {
3
+ companionId: string;
4
+ requestContext: RequestContext;
5
+ stagedPlan: StagedResponsePlan;
6
+ gateEval: ResponseGateEvaluation;
7
+ speech: string;
8
+ language: string;
9
+ dispatcher: ExperienceDispatcher;
10
+ voice?: VoiceOrgan | ExperienceAdapter;
11
+ body?: BodyOrgan | ExperienceAdapter;
12
+ }
13
+ export interface ExperienceEmissionResult {
14
+ experienceEvents: ExperienceEvent[];
15
+ speechId?: string;
16
+ }
17
+ /**
18
+ * Creates ExperienceEvents, dispatches them through the ExperienceDispatcher,
19
+ * and coordinates backward-compatible fallbacks for legacy Voice/Body adapters.
20
+ */
21
+ export declare function emitExperienceEvents(params: ExperienceEmissionParams): Promise<ExperienceEmissionResult>;
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.emitExperienceEvents = emitExperienceEvents;
4
+ const index_1 = require("./index");
5
+ /**
6
+ * Creates ExperienceEvents, dispatches them through the ExperienceDispatcher,
7
+ * and coordinates backward-compatible fallbacks for legacy Voice/Body adapters.
8
+ */
9
+ async function emitExperienceEvents(params) {
10
+ const { companionId, requestContext, stagedPlan, gateEval, speech, language, dispatcher, voice, body, } = params;
11
+ const experienceEvents = (0, index_1.createExperienceEvents)({
12
+ responseId: stagedPlan.responseId,
13
+ companionId,
14
+ correlationId: requestContext.conversation.correlationId,
15
+ channel: requestContext.conversation?.channel,
16
+ audienceId: requestContext.conversation?.audienceId,
17
+ speech,
18
+ language: language || 'ja',
19
+ evidenceIds: gateEval.filteredEvidenceIds,
20
+ citations: gateEval.filteredCitations,
21
+ expression: 'neutral',
22
+ action: 'talk',
23
+ expiresAt: stagedPlan.expiresAt,
24
+ });
25
+ const dispatchResult = await dispatcher.dispatchEvents(experienceEvents);
26
+ let speechId;
27
+ const voiceResult = dispatchResult.eventResults.find((r) => r.event.kind === 'voice');
28
+ if (voiceResult?.result?.metadata?.speechId) {
29
+ speechId = voiceResult.result.metadata.speechId;
30
+ }
31
+ else if (voice &&
32
+ typeof voice.handleEvent !== 'function' &&
33
+ typeof voice.enqueueSpeech === 'function') {
34
+ speechId = voice.enqueueSpeech(speech, language || 'ja', 1);
35
+ }
36
+ if (body && typeof body.handleEvent !== 'function') {
37
+ if (typeof body.setExpression === 'function') {
38
+ body.setExpression('neutral');
39
+ }
40
+ if (typeof body.act === 'function') {
41
+ body.act('talk');
42
+ }
43
+ }
44
+ return {
45
+ experienceEvents,
46
+ speechId,
47
+ };
48
+ }
@@ -1,4 +1,3 @@
1
- import { Channel } from './context';
2
1
  import { ResponseCitation } from './evidence';
3
2
  export type ExperienceEventKind = 'voice' | 'caption' | 'avatar' | 'platform_action';
4
3
  export type ExperienceEventLifecycle = 'STARTED' | 'PROGRESS' | 'COMPLETED' | 'FAILED';
@@ -7,8 +6,8 @@ export interface ExperienceEvent {
7
6
  companionId: string;
8
7
  responseId: string;
9
8
  correlationId: string;
10
- channel: Channel;
11
- audienceId: string;
9
+ channel?: string;
10
+ audienceId?: string;
12
11
  approval: 'APPROVED';
13
12
  kind: ExperienceEventKind;
14
13
  lifecycle: ExperienceEventLifecycle;
@@ -20,6 +19,7 @@ export interface ExperienceEvent {
20
19
  expression?: string;
21
20
  createdAt: string;
22
21
  expiresAt?: string;
22
+ [key: string]: unknown;
23
23
  }
24
24
  export interface ExperienceAdapterResult {
25
25
  accepted: boolean;
@@ -38,8 +38,8 @@ export interface CreateExperienceEventsOptions {
38
38
  responseId: string;
39
39
  companionId: string;
40
40
  correlationId: string;
41
- channel: Channel;
42
- audienceId: string;
41
+ channel?: string;
42
+ audienceId?: string;
43
43
  speech: string;
44
44
  language?: string;
45
45
  evidenceIds?: string[];
@@ -48,6 +48,7 @@ export interface CreateExperienceEventsOptions {
48
48
  action?: string;
49
49
  expiresAt?: string;
50
50
  now?: string | Date;
51
+ [key: string]: unknown;
51
52
  }
52
53
  export declare function createExperienceEvents(options: CreateExperienceEventsOptions): ExperienceEvent[];
53
54
  export declare function validateExperienceEvent(event: unknown): {
@@ -62,8 +62,9 @@ function validateExperienceEvent(event) {
62
62
  return { valid: false, error: 'Missing or invalid responseId' };
63
63
  if (!e.correlationId || typeof e.correlationId !== 'string')
64
64
  return { valid: false, error: 'Missing or invalid correlationId' };
65
- if (!e.audienceId || typeof e.audienceId !== 'string')
66
- return { valid: false, error: 'Missing or invalid audienceId' };
65
+ if (e.audienceId !== undefined && typeof e.audienceId !== 'string') {
66
+ return { valid: false, error: 'Invalid audienceId: must be a string' };
67
+ }
67
68
  if (e.approval !== 'APPROVED')
68
69
  return { valid: false, error: 'Event approval must be APPROVED' };
69
70
  if (!['voice', 'caption', 'avatar', 'platform_action'].includes(e.kind)) {
@@ -50,9 +50,10 @@ describe('T5 Experience Event Contract Suite', () => {
50
50
  const events = (0, experience_1.createExperienceEvents)(baseEventOptions);
51
51
  const missingCompanion = { ...events[0], companionId: '' };
52
52
  expect((0, experience_1.validateExperienceEvent)(missingCompanion).valid).toBe(false);
53
- const missingCorr = { ...events[0], correlationId: '' };
54
- expect((0, experience_1.validateExperienceEvent)(missingCorr).valid).toBe(false);
55
- const missingAudience = { ...events[0], audienceId: '' };
56
- expect((0, experience_1.validateExperienceEvent)(missingAudience).valid).toBe(false);
53
+ const missingResponseId = { ...events[0], responseId: '' };
54
+ expect((0, experience_1.validateExperienceEvent)(missingResponseId).valid).toBe(false);
55
+ const withoutAudience = { ...events[0] };
56
+ delete withoutAudience.audienceId;
57
+ expect((0, experience_1.validateExperienceEvent)(withoutAudience).valid).toBe(true);
57
58
  });
58
59
  });
package/dist/gating.d.ts CHANGED
@@ -1,12 +1,13 @@
1
1
  import { RequestContext } from './context';
2
+ import { MemoryProposal, BehaviorProposal } from './proposals';
2
3
  import { EvidenceRecord, StagedResponsePlan, ResponseGateEvaluation, ResponseCitation } from './evidence';
3
4
  export interface StageResponseOptions {
4
5
  requestContext: RequestContext;
5
6
  candidateSpeech: string;
6
7
  candidateLanguage: string;
7
8
  internalMonologue?: string;
8
- memoryProposals?: any[];
9
- behaviorProposals?: any[];
9
+ memoryProposals?: MemoryProposal[];
10
+ behaviorProposals?: BehaviorProposal[];
10
11
  evidenceRecords?: EvidenceRecord[];
11
12
  citations?: ResponseCitation[];
12
13
  requiresApproval?: boolean;
package/dist/gating.js CHANGED
@@ -34,14 +34,13 @@ class ResponseGatingEngine {
34
34
  }
35
35
  const requiresApproval = options.requiresApproval !== undefined
36
36
  ? options.requiresApproval
37
- : requestContext.conversation.channel === 'operator' ||
38
- evidenceRecords.some((e) => e.origin === 'ocr' || e.trust === 'untrusted');
37
+ : evidenceRecords.some((e) => e.origin === 'ocr' || e.trust === 'untrusted');
39
38
  const staged = {
40
39
  responseId: generateId('resp'),
41
40
  companionId: requestContext.companionId,
42
41
  correlationId: requestContext.conversation.correlationId,
43
- channel: requestContext.conversation.channel,
44
- audienceId: requestContext.conversation.audienceId,
42
+ channel: requestContext.conversation?.channel,
43
+ audienceId: requestContext.conversation?.audienceId,
45
44
  speech: options.candidateSpeech,
46
45
  language: options.candidateLanguage,
47
46
  evidenceIds,
@@ -118,7 +117,7 @@ class ResponseGatingEngine {
118
117
  filteredEvidenceIds: admittedEvidenceIds,
119
118
  filteredCitations,
120
119
  diagnostics: {
121
- detail: 'Response plan requires operator approval before external emission',
120
+ detail: 'Response plan requires approval before external emission',
122
121
  excludedEvidenceCount: String(excluded.length),
123
122
  },
124
123
  };
@@ -147,9 +146,6 @@ class ResponseGatingEngine {
147
146
  if (plan.correlationId !== options.correlationId) {
148
147
  return { success: false, reason: 'APPROVAL_ID_MISMATCH' };
149
148
  }
150
- if (options.audienceId && plan.audienceId !== options.audienceId) {
151
- return { success: false, reason: 'AUDIENCE_MISMATCH' };
152
- }
153
149
  if (plan.status === 'EXPIRED') {
154
150
  return { success: false, reason: 'EVIDENCE_EXPIRED' };
155
151
  }
package/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export * from './context';
2
+ export * from './proposals';
2
3
  export * from './evidence';
3
4
  export * from './gating';
4
5
  export * from './experience';
@@ -7,14 +8,28 @@ export * from './action';
7
8
  export * from './action-policy';
8
9
  export * from './ear-types';
9
10
  export * from './capability';
11
+ export * from './sqlite-action-store';
10
12
  export * from './teaching';
11
13
  export * from './runtime';
12
14
  export * from './chat-contract';
15
+ export * from './input-normalizer';
16
+ export * from './intent-classifier';
17
+ export * from './context-retriever';
18
+ export * from './prompt-compiler';
19
+ export * from './cognition-planner';
20
+ export * from './memory-settler';
21
+ export * from './action-executor';
22
+ export * from './experience-emitter';
23
+ export * from './response-envelope';
24
+ export * from './session-history';
25
+ import { EvidenceRecord } from './evidence';
13
26
  import { ActionIntent } from './action';
27
+ import { RequestContext } from './context';
14
28
  import { EarIngestOptions } from './ear-types';
29
+ import { ClaimType, ClaimAuthority, ClaimStatus, SourceEvent, MemoryProposal, BehaviorProposal } from './proposals';
15
30
  export interface OrganConfig {
16
31
  provider: string;
17
- [key: string]: any;
32
+ [key: string]: unknown;
18
33
  }
19
34
  export interface CompanionConfig {
20
35
  id: string;
@@ -38,36 +53,6 @@ export interface BrainContext {
38
53
  recentMessages: Message[];
39
54
  recipient?: MemoryScope;
40
55
  }
41
- export type ClaimType = 'semantic' | 'preference' | 'episodic' | 'relationship';
42
- export type ClaimAuthority = 'user_explicit' | 'user_correction' | 'import' | 'repeated_dialogue' | 'inference' | 'observation';
43
- export type ClaimStatus = 'PENDING' | 'APPROVED' | 'REJECTED' | 'SESSION_ONLY' | 'EXPIRED' | 'SUPERSEDED' | 'REVOKED';
44
- export interface SourceEvent {
45
- id: string;
46
- sourceType: string;
47
- occurredAt: string;
48
- payload: Record<string, unknown>;
49
- schemaVersion?: number;
50
- }
51
- export interface MemoryProposal {
52
- subject: string;
53
- predicate: string;
54
- value: string;
55
- content?: string;
56
- provenance?: string;
57
- claimType?: ClaimType;
58
- sensitivity?: string;
59
- allowedAudiences?: string[];
60
- sourceEventId?: string;
61
- }
62
- export interface BehaviorProposal {
63
- directive: string;
64
- priority: number;
65
- subject?: string;
66
- predicate?: string;
67
- value?: string;
68
- memoryClass?: 'identity' | 'relationship' | 'behavioral' | 'semantic' | 'episodic';
69
- sourceEventId?: string;
70
- }
71
56
  export interface ResponsePlan {
72
57
  speech: string;
73
58
  language: string;
@@ -79,7 +64,7 @@ export interface ResponsePlan {
79
64
  export interface BrainOrgan {
80
65
  generatePlan(context: BrainContext): Promise<ResponsePlan>;
81
66
  }
82
- export type MemoryScope = 'OWNER' | 'VIEWER' | 'OPERATOR' | 'PUBLIC';
67
+ export type MemoryScope = 'companion' | 'user' | string;
83
68
  export interface Claim {
84
69
  id: string;
85
70
  subject: string;
@@ -87,7 +72,7 @@ export interface Claim {
87
72
  value: string;
88
73
  status: ClaimStatus;
89
74
  evidence?: string[];
90
- scope: MemoryScope;
75
+ scope?: MemoryScope;
91
76
  companionId: string;
92
77
  provenance?: string;
93
78
  sourceEventId?: string;
@@ -95,19 +80,18 @@ export interface Claim {
95
80
  authority?: ClaimAuthority;
96
81
  userConfirmation?: 'explicit' | 'implied' | 'none';
97
82
  sensitivity?: string;
98
- allowedAudiences?: string[];
99
83
  confidence?: number;
100
84
  assertedAt?: string;
101
85
  validFrom?: string;
102
86
  validUntil?: string;
103
87
  supersedes?: string;
104
88
  replaces?: string;
89
+ [key: string]: unknown;
105
90
  }
106
91
  export interface BehaviorDirective {
107
92
  id: string;
108
93
  companionId: string;
109
94
  directive: string;
110
- scopeMatcher: string[];
111
95
  priority: number;
112
96
  status: 'PENDING' | 'ACTIVE' | 'DISABLED' | 'SUPERSEDED' | 'REJECTED' | 'REVOKED' | 'EXPIRED';
113
97
  supersedesId?: string;
@@ -115,22 +99,21 @@ export interface BehaviorDirective {
115
99
  subject?: string;
116
100
  predicate?: string;
117
101
  value?: string;
118
- allowedAudiences?: string[];
119
102
  validFrom?: string;
120
103
  validUntil?: string;
104
+ [key: string]: unknown;
121
105
  }
122
106
  export interface MemoryQueryOptions {
123
- channel?: 'public' | 'direct' | 'private' | 'operator';
124
- audienceId?: string;
125
107
  sensitivity?: string;
126
108
  limit?: number;
127
109
  minConfidence?: number;
128
110
  now?: string | Date;
111
+ [key: string]: unknown;
129
112
  }
130
113
  export interface MemoryOrgan {
131
114
  initialize(companionId: string): Promise<void>;
132
115
  proposeClaim(claim: Omit<Claim, 'id' | 'status' | 'companionId'>): Promise<Claim>;
133
- searchClaims(query: string, scopeOrOptions: MemoryScope | MemoryQueryOptions, limit?: number): Promise<Claim[]>;
116
+ searchClaims(query: string, scopeOrOptions?: MemoryScope | MemoryQueryOptions, limit?: number): Promise<Claim[]>;
134
117
  getClaims(limit?: number): Promise<Claim[]>;
135
118
  getPendingClaims(limit?: number): Promise<Claim[]>;
136
119
  approveClaim(id: string): Promise<void>;
@@ -146,7 +129,7 @@ export interface MemoryOrgan {
146
129
  disableDirective(id: string): Promise<void>;
147
130
  expireDirective?(id: string): Promise<void>;
148
131
  supersedeClaim?(id: string, replacement: Omit<Claim, 'id' | 'status' | 'companionId'>): Promise<Claim>;
149
- updateClaim?(id: string, updates: Partial<Pick<Claim, 'subject' | 'predicate' | 'value' | 'scope' | 'sensitivity' | 'confidence' | 'validFrom' | 'validUntil' | 'allowedAudiences'>>): Promise<Claim>;
132
+ updateClaim?(id: string, updates: Partial<Pick<Claim, 'subject' | 'predicate' | 'value' | 'scope' | 'sensitivity' | 'confidence' | 'validFrom' | 'validUntil'>>): Promise<Claim>;
150
133
  resetMemory?(): Promise<void>;
151
134
  addSourceEvent?(event: SourceEvent): Promise<SourceEvent>;
152
135
  getSourceEvent?(id: string): Promise<SourceEvent | undefined>;
@@ -179,14 +162,12 @@ export interface ActiveSelfProjection {
179
162
  render(): string;
180
163
  }
181
164
  export interface BehaviorContext {
182
- activeRole: string;
183
165
  directives: BehaviorDirective[];
184
166
  companionId?: string;
185
- channel?: 'public' | 'direct' | 'private' | 'operator';
186
- audienceId?: string;
187
167
  actorId?: string;
188
168
  sessionId?: string;
189
169
  now?: string;
170
+ [key: string]: unknown;
190
171
  }
191
172
  export interface BehaviorOrgan {
192
173
  compile(context: BehaviorContext): Promise<string>;
@@ -197,6 +178,7 @@ export interface KnowledgeItem {
197
178
  provenance: string;
198
179
  revision: string;
199
180
  citations: KnowledgeCitation[];
181
+ evidenceRecord?: EvidenceRecord;
200
182
  }
201
183
  export interface KnowledgeCitation {
202
184
  sourceId: string;
@@ -218,6 +200,7 @@ export interface EarPerception {
218
200
  export interface EarOrgan {
219
201
  listen(source: string, payload: unknown, options?: EarIngestOptions): Promise<EarPerception>;
220
202
  transcribeAudio?(audio: Uint8Array): Promise<string>;
203
+ classifyIntent?(text: string, context?: RequestContext): Promise<Record<string, unknown>> | Record<string, unknown>;
221
204
  }
222
205
  export interface BodyOrgan {
223
206
  setExpression(expression: string): void;
@@ -264,3 +247,4 @@ export interface HealthProbeResult {
264
247
  details?: Record<string, unknown>;
265
248
  }
266
249
  export type HealthProbeFn = (context: HealthProbeContext) => Promise<HealthProbeResult> | HealthProbeResult;
250
+ export * from './mouth-types';
package/dist/index.js CHANGED
@@ -16,6 +16,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
17
  // Export neutral context types and validator
18
18
  __exportStar(require("./context"), exports);
19
+ __exportStar(require("./proposals"), exports);
19
20
  __exportStar(require("./evidence"), exports);
20
21
  __exportStar(require("./gating"), exports);
21
22
  __exportStar(require("./experience"), exports);
@@ -24,6 +25,19 @@ __exportStar(require("./action"), exports);
24
25
  __exportStar(require("./action-policy"), exports);
25
26
  __exportStar(require("./ear-types"), exports);
26
27
  __exportStar(require("./capability"), exports);
28
+ __exportStar(require("./sqlite-action-store"), exports);
27
29
  __exportStar(require("./teaching"), exports);
28
30
  __exportStar(require("./runtime"), exports);
29
31
  __exportStar(require("./chat-contract"), exports);
32
+ __exportStar(require("./input-normalizer"), exports);
33
+ __exportStar(require("./intent-classifier"), exports);
34
+ __exportStar(require("./context-retriever"), exports);
35
+ __exportStar(require("./prompt-compiler"), exports);
36
+ __exportStar(require("./cognition-planner"), exports);
37
+ __exportStar(require("./memory-settler"), exports);
38
+ __exportStar(require("./action-executor"), exports);
39
+ __exportStar(require("./experience-emitter"), exports);
40
+ __exportStar(require("./response-envelope"), exports);
41
+ __exportStar(require("./session-history"), exports);
42
+ // Mouth (Communication & Output Delivery)
43
+ __exportStar(require("./mouth-types"), exports);
@@ -0,0 +1,14 @@
1
+ import { Message, RequestContext, EarOrgan } from './index';
2
+ export interface NormalizedInput {
3
+ perceivedText: string;
4
+ sanitizedText: string;
5
+ requestContext: RequestContext;
6
+ role: 'OWNER' | 'VIEWER' | 'OPERATOR';
7
+ isContextObject: boolean;
8
+ boundedHistory: Message[];
9
+ }
10
+ /**
11
+ * Validates raw user input, synthesizes RequestContext, normalizes history,
12
+ * and routes input through EarOrgan if available.
13
+ */
14
+ export declare function normalizeUserInput(message: string, roleOrContext: ("OWNER" | "VIEWER" | "OPERATOR" | RequestContext | string) | undefined, history: Message[] | undefined, companionId: string, ear?: EarOrgan): Promise<NormalizedInput>;
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.normalizeUserInput = normalizeUserInput;
4
+ /**
5
+ * Validates raw user input, synthesizes RequestContext, normalizes history,
6
+ * and routes input through EarOrgan if available.
7
+ */
8
+ async function normalizeUserInput(message, roleOrContext = 'OWNER', history = [], companionId, ear) {
9
+ if (typeof message !== 'string' || !message.trim() || message.length > 4000) {
10
+ throw new Error('message must be a non-empty string of at most 4000 characters');
11
+ }
12
+ if (!Array.isArray(history) ||
13
+ history.length > 20 ||
14
+ history.some((item) => !item || !['user', 'assistant'].includes(item.role) || typeof item.content !== 'string')) {
15
+ throw new Error('history must contain at most 20 user/assistant messages');
16
+ }
17
+ const isContextObject = typeof roleOrContext === 'object' && roleOrContext !== null;
18
+ const role = isContextObject
19
+ ? (roleOrContext.actor?.authorizationRole === 'viewer' ? 'VIEWER' : 'OWNER')
20
+ : roleOrContext;
21
+ const requestContext = isContextObject
22
+ ? roleOrContext
23
+ : {
24
+ companionId,
25
+ actor: {
26
+ actorId: role === 'VIEWER' ? 'anonymous-session' : 'owner-user',
27
+ sessionId: `sess-${companionId}`,
28
+ authorizationRole: role === 'VIEWER' ? 'viewer' : 'administrator',
29
+ capabilities: role === 'VIEWER' ? ['chat'] : ['chat', 'memory:approve', 'action:execute'],
30
+ authenticated: role !== 'VIEWER',
31
+ },
32
+ conversation: {
33
+ channel: 'direct',
34
+ correlationId: `corr-${Date.now()}`,
35
+ },
36
+ source: 'local',
37
+ };
38
+ // Universal Perception: Route user input through EarOrgan if available
39
+ let perceivedText = message;
40
+ if (ear && typeof ear.listen === 'function') {
41
+ const perception = await ear.listen('text_chat', message, {
42
+ context: requestContext,
43
+ });
44
+ perceivedText = perception.text || message;
45
+ }
46
+ const boundedHistory = history.map((item) => ({
47
+ role: item.role,
48
+ content: item.content.slice(0, 2000).replace(/\0/g, ''),
49
+ }));
50
+ return {
51
+ perceivedText,
52
+ sanitizedText: message,
53
+ requestContext,
54
+ role,
55
+ isContextObject,
56
+ boundedHistory,
57
+ };
58
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const input_normalizer_1 = require("./input-normalizer");
4
+ describe('InputNormalizer', () => {
5
+ test('rejects empty or oversized messages', async () => {
6
+ await expect((0, input_normalizer_1.normalizeUserInput)('', 'OWNER', [], 'comp-1')).rejects.toThrow('message must be a non-empty string of at most 4000 characters');
7
+ await expect((0, input_normalizer_1.normalizeUserInput)(' ', 'OWNER', [], 'comp-1')).rejects.toThrow('message must be a non-empty string of at most 4000 characters');
8
+ const oversized = 'a'.repeat(4001);
9
+ await expect((0, input_normalizer_1.normalizeUserInput)(oversized, 'OWNER', [], 'comp-1')).rejects.toThrow('message must be a non-empty string of at most 4000 characters');
10
+ });
11
+ test('rejects oversized history or invalid message roles', async () => {
12
+ const invalidHistory = Array(21).fill({ role: 'user', content: 'hi' });
13
+ await expect((0, input_normalizer_1.normalizeUserInput)('hi', 'OWNER', invalidHistory, 'comp-1')).rejects.toThrow('history must contain at most 20 user/assistant messages');
14
+ const malformedRole = [{ role: 'bad_role', content: 'test' }];
15
+ await expect((0, input_normalizer_1.normalizeUserInput)('hi', 'OWNER', malformedRole, 'comp-1')).rejects.toThrow('history must contain at most 20 user/assistant messages');
16
+ });
17
+ test('normalizes role string into full RequestContext and strips null bytes from history', async () => {
18
+ const historyWithNulls = [{ role: 'user', content: 'hello\0world' }];
19
+ const result = await (0, input_normalizer_1.normalizeUserInput)('hello', 'OWNER', historyWithNulls, 'comp-1');
20
+ expect(result.role).toBe('OWNER');
21
+ expect(result.requestContext.companionId).toBe('comp-1');
22
+ expect(result.requestContext.actor.authorizationRole).toBe('administrator');
23
+ expect(result.requestContext.actor.authenticated).toBe(true);
24
+ expect(result.boundedHistory[0].content).toBe('helloworld');
25
+ });
26
+ test('routes input through EarOrgan when present', async () => {
27
+ const mockEar = {
28
+ listen: jest.fn().mockResolvedValue({
29
+ id: 'perc-1',
30
+ source: 'text_chat',
31
+ text: 'transcribed text',
32
+ timestamp: new Date().toISOString(),
33
+ }),
34
+ };
35
+ const result = await (0, input_normalizer_1.normalizeUserInput)('raw text', 'VIEWER', [], 'comp-1', mockEar);
36
+ expect(mockEar.listen).toHaveBeenCalledWith('text_chat', 'raw text', expect.anything());
37
+ expect(result.perceivedText).toBe('transcribed text');
38
+ });
39
+ });
@@ -0,0 +1,24 @@
1
+ import { RequestContext } from './index';
2
+ import { extractDeterministicTeaching } from './teaching';
3
+ export interface IntentClassification {
4
+ normalizedMessage: string;
5
+ explicitTeaching: ReturnType<typeof extractDeterministicTeaching>;
6
+ isTeachingLike: boolean;
7
+ isSelfIdentityRequest: boolean;
8
+ isGreeting: boolean;
9
+ shouldQueryKnowledge: boolean;
10
+ confidence?: number;
11
+ classifierOrigin?: 'heuristic' | 'cognitive' | 'ear';
12
+ }
13
+ export type CognitiveIntentClassifier = (text: string, context: RequestContext) => Promise<Partial<IntentClassification>> | Partial<IntentClassification>;
14
+ /**
15
+ * Evaluates domain heuristics and intent classification for user messages.
16
+ * Determines if deterministic teaching is present and whether external knowledge
17
+ * retrieval is appropriate.
18
+ */
19
+ export declare function classifyInputIntent(text: string, context: RequestContext, overrides?: Partial<IntentClassification>): IntentClassification;
20
+ /**
21
+ * Asynchronously classifies intent, allowing an EarOrgan or cognitive model
22
+ * to provide semantic classification before falling back to heuristics.
23
+ */
24
+ export declare function classifyInputIntentAsync(text: string, context: RequestContext, classifier?: CognitiveIntentClassifier): Promise<IntentClassification>;