@siduri-x/core 1.0.5 → 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 (85) hide show
  1. package/dist/action-executor.d.ts +12 -0
  2. package/dist/action-executor.js +50 -0
  3. package/dist/action-policy.d.ts +45 -0
  4. package/dist/action-policy.js +219 -0
  5. package/dist/action-policy.test.d.ts +1 -0
  6. package/dist/action-policy.test.js +194 -0
  7. package/dist/action.d.ts +72 -0
  8. package/dist/action.js +2 -0
  9. package/dist/adversarial.test.d.ts +1 -0
  10. package/dist/adversarial.test.js +493 -0
  11. package/dist/architecture-boundary.test.d.ts +1 -0
  12. package/dist/architecture-boundary.test.js +117 -0
  13. package/dist/capability.d.ts +65 -0
  14. package/dist/capability.js +157 -0
  15. package/dist/capability.test.d.ts +1 -0
  16. package/dist/capability.test.js +269 -0
  17. package/dist/chat-contract.d.ts +81 -0
  18. package/dist/chat-contract.js +68 -0
  19. package/dist/cognition-planner.d.ts +15 -0
  20. package/dist/cognition-planner.js +23 -0
  21. package/dist/context-retriever.d.ts +24 -0
  22. package/dist/context-retriever.js +92 -0
  23. package/dist/context.d.ts +45 -0
  24. package/dist/context.js +72 -0
  25. package/dist/context.test.d.ts +1 -0
  26. package/dist/context.test.js +76 -0
  27. package/dist/dispatcher.d.ts +14 -0
  28. package/dist/dispatcher.js +40 -0
  29. package/dist/dispatcher.test.d.ts +1 -0
  30. package/dist/dispatcher.test.js +60 -0
  31. package/dist/ear-types.d.ts +33 -0
  32. package/dist/ear-types.js +2 -0
  33. package/dist/evidence.d.ts +76 -0
  34. package/dist/evidence.js +47 -0
  35. package/dist/evidence.test.d.ts +1 -0
  36. package/dist/evidence.test.js +101 -0
  37. package/dist/experience-emitter.d.ts +21 -0
  38. package/dist/experience-emitter.js +48 -0
  39. package/dist/experience.d.ts +57 -0
  40. package/dist/experience.js +79 -0
  41. package/dist/experience.test.d.ts +1 -0
  42. package/dist/experience.test.js +59 -0
  43. package/dist/gating.d.ts +46 -0
  44. package/dist/gating.js +185 -0
  45. package/dist/gating.test.d.ts +1 -0
  46. package/dist/gating.test.js +190 -0
  47. package/dist/index.d.ts +250 -0
  48. package/dist/index.js +43 -0
  49. package/dist/input-normalizer.d.ts +14 -0
  50. package/dist/input-normalizer.js +58 -0
  51. package/dist/input-normalizer.test.d.ts +1 -0
  52. package/dist/input-normalizer.test.js +39 -0
  53. package/dist/intent-classifier.d.ts +24 -0
  54. package/dist/intent-classifier.js +53 -0
  55. package/dist/intent-classifier.test.d.ts +1 -0
  56. package/dist/intent-classifier.test.js +68 -0
  57. package/dist/memory-settler.d.ts +27 -0
  58. package/dist/memory-settler.js +95 -0
  59. package/dist/mouth-types.d.ts +85 -0
  60. package/dist/mouth-types.js +2 -0
  61. package/dist/perception-cycle.test.d.ts +1 -0
  62. package/dist/perception-cycle.test.js +155 -0
  63. package/dist/prompt-compiler.d.ts +20 -0
  64. package/dist/prompt-compiler.js +57 -0
  65. package/dist/prompt-compiler.test.d.ts +1 -0
  66. package/dist/prompt-compiler.test.js +76 -0
  67. package/dist/proposals.d.ts +30 -0
  68. package/dist/proposals.js +2 -0
  69. package/dist/response-envelope.d.ts +25 -0
  70. package/dist/response-envelope.js +64 -0
  71. package/dist/runtime-facades.test.d.ts +1 -0
  72. package/dist/runtime-facades.test.js +69 -0
  73. package/dist/runtime.d.ts +114 -0
  74. package/dist/runtime.js +412 -0
  75. package/dist/session-history.d.ts +20 -0
  76. package/dist/session-history.js +55 -0
  77. package/dist/session-history.test.d.ts +1 -0
  78. package/dist/session-history.test.js +38 -0
  79. package/dist/sqlite-action-store.d.ts +20 -0
  80. package/dist/sqlite-action-store.js +225 -0
  81. package/dist/sqlite-action-store.test.d.ts +1 -0
  82. package/dist/sqlite-action-store.test.js +252 -0
  83. package/dist/teaching.d.ts +15 -0
  84. package/dist/teaching.js +159 -0
  85. package/package.json +1 -1
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generateCognitionPlan = generateCognitionPlan;
4
+ /**
5
+ * Invokes BrainOrgan to generate a structured ResponsePlan, or provides
6
+ * a graceful baseline response if Brain is absent or in headless passive mode.
7
+ */
8
+ async function generateCognitionPlan(params) {
9
+ const { companionName, brain, systemPrompt, contextPrompt, recentMessages, recipient, perceivedText, } = params;
10
+ if (brain && typeof brain.generatePlan === 'function') {
11
+ return brain.generatePlan({
12
+ systemPrompt,
13
+ contextPrompt,
14
+ recentMessages,
15
+ recipient,
16
+ });
17
+ }
18
+ // Graceful baseline response when Brain is not configured or in headless passive mode
19
+ return {
20
+ speech: `[Siduri ${companionName}] Acknowledged: ${perceivedText}`,
21
+ language: 'en',
22
+ };
23
+ }
@@ -0,0 +1,24 @@
1
+ import { KnowledgeOrgan, MemoryOrgan, KnowledgeItem, Claim, BehaviorDirective, EvidenceRecord, ResponseCitation, RequestContext } from './index';
2
+ export interface ContextRetrievalParams {
3
+ companionId: string;
4
+ perceivedText: string;
5
+ requestContext: RequestContext;
6
+ role: 'OWNER' | 'VIEWER' | 'OPERATOR';
7
+ isContextObject: boolean;
8
+ shouldQueryKnowledge: boolean;
9
+ knowledge?: KnowledgeOrgan;
10
+ memory?: MemoryOrgan;
11
+ }
12
+ export interface RetrievedContext {
13
+ knowledgeData: KnowledgeItem[];
14
+ memoryData: Claim[];
15
+ activeDirectives: BehaviorDirective[];
16
+ subsystemDiagnostics: Record<string, string>;
17
+ collectedEvidence: EvidenceRecord[];
18
+ citations: ResponseCitation[];
19
+ }
20
+ /**
21
+ * Concurrently queries Knowledge and Memory organs with graceful degradation,
22
+ * collecting diagnostics and synthesizing evidence records and citations.
23
+ */
24
+ export declare function retrieveRuntimeContext(params: ContextRetrievalParams): Promise<RetrievedContext>;
@@ -0,0 +1,92 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.retrieveRuntimeContext = retrieveRuntimeContext;
4
+ /**
5
+ * Concurrently queries Knowledge and Memory organs with graceful degradation,
6
+ * collecting diagnostics and synthesizing evidence records and citations.
7
+ */
8
+ async function retrieveRuntimeContext(params) {
9
+ const { companionId, perceivedText, requestContext, role, isContextObject, shouldQueryKnowledge, knowledge, memory, } = params;
10
+ const queryOptions = isContextObject
11
+ ? {
12
+ channel: requestContext.conversation.channel,
13
+ audienceId: requestContext.conversation.audienceId,
14
+ limit: 5,
15
+ }
16
+ : role;
17
+ const subsystemDiagnostics = {};
18
+ const [knowledgeData, memoryData, activeDirectives] = await Promise.all([
19
+ knowledge && shouldQueryKnowledge && typeof knowledge.search === 'function'
20
+ ? knowledge.search(perceivedText).catch((e) => {
21
+ console.error('[SiduriRuntime] Knowledge search failed:', e.message);
22
+ subsystemDiagnostics['knowledge'] = `UNAVAILABLE: ${e.message}`;
23
+ return [];
24
+ })
25
+ : Promise.resolve([]),
26
+ memory && typeof memory.searchClaims === 'function'
27
+ ? memory.searchClaims(perceivedText, queryOptions, 5).catch((e) => {
28
+ console.error('[SiduriRuntime] Memory search failed:', e.message);
29
+ subsystemDiagnostics['memory_claims'] = `UNAVAILABLE: ${e.message}`;
30
+ return [];
31
+ })
32
+ : Promise.resolve([]),
33
+ memory && typeof memory.getDirectives === 'function'
34
+ ? memory.getDirectives().catch((e) => {
35
+ console.error('[SiduriRuntime] Memory directives failed:', e.message);
36
+ subsystemDiagnostics['memory_directives'] = `UNAVAILABLE: ${e.message}`;
37
+ return [];
38
+ })
39
+ : Promise.resolve([]),
40
+ ]);
41
+ // Build evidence records from retrieved knowledge context
42
+ const collectedEvidence = [];
43
+ const citations = [];
44
+ if (knowledgeData.length > 0) {
45
+ for (const k of knowledgeData) {
46
+ if (k.evidenceRecord) {
47
+ const nativeRecord = {
48
+ ...k.evidenceRecord,
49
+ };
50
+ collectedEvidence.push(nativeRecord);
51
+ citations.push({
52
+ sourceId: nativeRecord.sourceId,
53
+ revision: nativeRecord.revision,
54
+ documentId: nativeRecord.documentId || k.citations?.[0]?.documentId,
55
+ chunkId: nativeRecord.chunkId || k.citations?.[0]?.chunkId,
56
+ locator: nativeRecord.locator || k.citations?.[0]?.locator,
57
+ });
58
+ }
59
+ else {
60
+ // Synthesize fallback evidence record with provenance
61
+ const evId = `ev-know-${Date.now()}-${Math.random().toString(36).slice(2, 7)}`;
62
+ const sourceId = k.provenance || 'configured-knowledge';
63
+ collectedEvidence.push({
64
+ evidenceId: evId,
65
+ sourceId,
66
+ revision: k.revision,
67
+ origin: 'knowledge',
68
+ trust: 'configured',
69
+ sensitivity: 'public',
70
+ companionId,
71
+ correlationId: requestContext.conversation.correlationId,
72
+ createdAt: new Date().toISOString(),
73
+ });
74
+ citations.push({
75
+ sourceId,
76
+ revision: k.revision,
77
+ documentId: k.citations?.[0]?.documentId,
78
+ chunkId: k.citations?.[0]?.chunkId,
79
+ locator: k.citations?.[0]?.locator,
80
+ });
81
+ }
82
+ }
83
+ }
84
+ return {
85
+ knowledgeData,
86
+ memoryData,
87
+ activeDirectives,
88
+ subsystemDiagnostics,
89
+ collectedEvidence,
90
+ citations,
91
+ };
92
+ }
@@ -0,0 +1,45 @@
1
+ export interface ActorContext {
2
+ actorId: string;
3
+ sessionId: string;
4
+ authenticated?: boolean;
5
+ capabilities?: string[];
6
+ [key: string]: unknown;
7
+ }
8
+ export interface ConversationContext {
9
+ correlationId: string;
10
+ sessionId?: string;
11
+ channel?: string;
12
+ audienceId?: string;
13
+ [key: string]: unknown;
14
+ }
15
+ export type SubjectKind = 'actor' | 'companion' | 'configured';
16
+ export interface SubjectRef {
17
+ subjectId: string;
18
+ kind: SubjectKind;
19
+ ownerActorId?: string;
20
+ }
21
+ export interface RequestContext {
22
+ companionId: string;
23
+ actor: ActorContext;
24
+ conversation: ConversationContext;
25
+ source?: 'local' | 'external' | string;
26
+ subject?: SubjectRef;
27
+ metadata?: Record<string, unknown>;
28
+ }
29
+ export type DiagnosticCode = 'legacy_role_removed' | 'anonymous_session_generated' | 'companion_default_mapped_for_bootstrap' | 'actor_scoped_subject_mapped';
30
+ export type ContextErrorCode = 'MISSING_CONTEXT' | 'INVALID_CONTEXT' | 'FORBIDDEN_CONTEXT' | 'LEGACY_PERSONAL_AUDIENCE' | 'AMBIGUOUS_CONTEXT' | 'UNAUTHORIZED_CAPABILITY';
31
+ export interface ContextError {
32
+ code: ContextErrorCode;
33
+ message?: string;
34
+ fields?: string[];
35
+ field?: string;
36
+ correlationId?: string;
37
+ }
38
+ export interface RequestContextValidationResult {
39
+ accepted: boolean;
40
+ context?: RequestContext;
41
+ diagnostics?: DiagnosticCode[];
42
+ error?: ContextError;
43
+ }
44
+ export declare function isValidSubjectKind(kind: unknown): kind is SubjectKind;
45
+ export declare function validateRequestContext(context: unknown): RequestContextValidationResult;
@@ -0,0 +1,72 @@
1
+ "use strict";
2
+ // Single-owner, single-machine context model
3
+ // Security perimeter is the local machine boundary (external vs internal).
4
+ // No internal audience, viewer, or owner role hierarchies.
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.isValidSubjectKind = isValidSubjectKind;
7
+ exports.validateRequestContext = validateRequestContext;
8
+ function isValidSubjectKind(kind) {
9
+ return kind === 'actor' || kind === 'companion' || kind === 'configured';
10
+ }
11
+ function validateRequestContext(context) {
12
+ if (!context || typeof context !== 'object') {
13
+ return {
14
+ accepted: false,
15
+ error: {
16
+ code: 'MISSING_CONTEXT',
17
+ fields: ['context'],
18
+ },
19
+ };
20
+ }
21
+ const ctx = context;
22
+ const missingFields = [];
23
+ if (!ctx.companionId || typeof ctx.companionId !== 'string' || ctx.companionId.trim() === '') {
24
+ missingFields.push('companionId');
25
+ }
26
+ if (!ctx.actor || typeof ctx.actor !== 'object') {
27
+ missingFields.push('actor');
28
+ }
29
+ else {
30
+ if (!ctx.actor.actorId || typeof ctx.actor.actorId !== 'string' || ctx.actor.actorId.trim() === '') {
31
+ missingFields.push('actor.actorId');
32
+ }
33
+ if (!ctx.actor.sessionId || typeof ctx.actor.sessionId !== 'string' || ctx.actor.sessionId.trim() === '') {
34
+ missingFields.push('actor.sessionId');
35
+ }
36
+ }
37
+ if (!ctx.conversation || typeof ctx.conversation !== 'object') {
38
+ missingFields.push('conversation');
39
+ }
40
+ else {
41
+ if (!ctx.conversation.correlationId || typeof ctx.conversation.correlationId !== 'string' || ctx.conversation.correlationId.trim() === '') {
42
+ missingFields.push('conversation.correlationId');
43
+ }
44
+ }
45
+ if (ctx.subject !== undefined) {
46
+ if (!ctx.subject || typeof ctx.subject !== 'object') {
47
+ missingFields.push('subject');
48
+ }
49
+ else {
50
+ if (!ctx.subject.subjectId || typeof ctx.subject.subjectId !== 'string' || ctx.subject.subjectId.trim() === '') {
51
+ missingFields.push('subject.subjectId');
52
+ }
53
+ if (!isValidSubjectKind(ctx.subject.kind)) {
54
+ missingFields.push('subject.kind');
55
+ }
56
+ }
57
+ }
58
+ if (missingFields.length > 0) {
59
+ return {
60
+ accepted: false,
61
+ error: {
62
+ code: 'MISSING_CONTEXT',
63
+ fields: missingFields,
64
+ correlationId: ctx.conversation?.correlationId,
65
+ },
66
+ };
67
+ }
68
+ return {
69
+ accepted: true,
70
+ context: ctx,
71
+ };
72
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,76 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const context_1 = require("./context");
4
+ describe('Core Context Contract (Single-Owner Single-Machine)', () => {
5
+ const validContext = {
6
+ companionId: 'companion-a',
7
+ actor: {
8
+ actorId: 'local-user',
9
+ sessionId: 'session-a',
10
+ authenticated: true,
11
+ capabilities: ['chat:interact'],
12
+ },
13
+ conversation: {
14
+ correlationId: 'corr-a',
15
+ },
16
+ subject: {
17
+ subjectId: 'actor:local-user',
18
+ kind: 'actor',
19
+ ownerActorId: 'local-user',
20
+ },
21
+ };
22
+ test('validates a correct RequestContext', () => {
23
+ const result = (0, context_1.validateRequestContext)(validContext);
24
+ expect(result.accepted).toBe(true);
25
+ expect(result.context).toEqual(validContext);
26
+ expect(result.error).toBeUndefined();
27
+ });
28
+ test('validates a correct RequestContext without subject', () => {
29
+ const { subject, ...contextWithoutSubject } = validContext;
30
+ const result = (0, context_1.validateRequestContext)(contextWithoutSubject);
31
+ expect(result.accepted).toBe(true);
32
+ expect(result.context?.subject).toBeUndefined();
33
+ });
34
+ test('rejects missing root context or non-object', () => {
35
+ const result = (0, context_1.validateRequestContext)(null);
36
+ expect(result.accepted).toBe(false);
37
+ expect(result.error?.code).toBe('MISSING_CONTEXT');
38
+ expect(result.error?.fields).toContain('context');
39
+ });
40
+ test('rejects missing companionId, actor, or conversation', () => {
41
+ const result = (0, context_1.validateRequestContext)({});
42
+ expect(result.accepted).toBe(false);
43
+ expect(result.error?.code).toBe('MISSING_CONTEXT');
44
+ expect(result.error?.fields).toEqual(expect.arrayContaining(['companionId', 'actor', 'conversation']));
45
+ });
46
+ test('validates subject kinds and constraints', () => {
47
+ expect((0, context_1.isValidSubjectKind)('actor')).toBe(true);
48
+ expect((0, context_1.isValidSubjectKind)('companion')).toBe(true);
49
+ expect((0, context_1.isValidSubjectKind)('configured')).toBe(true);
50
+ expect((0, context_1.isValidSubjectKind)('user')).toBe(false);
51
+ const invalidSubjectCtx = {
52
+ ...validContext,
53
+ subject: { subjectId: 'subject-1', kind: 'invalid_kind' },
54
+ };
55
+ const result = (0, context_1.validateRequestContext)(invalidSubjectCtx);
56
+ expect(result.accepted).toBe(false);
57
+ expect(result.error?.fields).toContain('subject.kind');
58
+ });
59
+ test('rejects missing correlationId and preserves correlationId in error if present', () => {
60
+ const missingCorr = {
61
+ ...validContext,
62
+ conversation: { ...validContext.conversation, correlationId: '' },
63
+ };
64
+ const result = (0, context_1.validateRequestContext)(missingCorr);
65
+ expect(result.accepted).toBe(false);
66
+ expect(result.error?.fields).toContain('conversation.correlationId');
67
+ const missingActorId = {
68
+ ...validContext,
69
+ actor: { ...validContext.actor, actorId: '' },
70
+ };
71
+ const result2 = (0, context_1.validateRequestContext)(missingActorId);
72
+ expect(result2.accepted).toBe(false);
73
+ expect(result2.error?.fields).toContain('actor.actorId');
74
+ expect(result2.error?.correlationId).toBe('corr-a');
75
+ });
76
+ });
@@ -0,0 +1,14 @@
1
+ import { ExperienceAdapter, ExperienceEvent, ExperienceAdapterResult } from './experience';
2
+ export interface DispatchSummary {
3
+ dispatched: boolean;
4
+ eventResults: {
5
+ event: ExperienceEvent;
6
+ result: ExperienceAdapterResult;
7
+ }[];
8
+ }
9
+ export declare class ExperienceDispatcher {
10
+ private readonly adapters;
11
+ private readonly dispatchedEventIds;
12
+ registerAdapter(adapter: ExperienceAdapter): void;
13
+ dispatchEvents(events: ExperienceEvent[]): Promise<DispatchSummary>;
14
+ }
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ExperienceDispatcher = void 0;
4
+ class ExperienceDispatcher {
5
+ adapters = [];
6
+ dispatchedEventIds = new Set();
7
+ registerAdapter(adapter) {
8
+ this.adapters.push(adapter);
9
+ }
10
+ async dispatchEvents(events) {
11
+ const eventResults = [];
12
+ for (const event of events) {
13
+ // Replay / duplicate dispatch protection: each eventId is dispatched only once
14
+ if (this.dispatchedEventIds.has(event.eventId)) {
15
+ eventResults.push({
16
+ event,
17
+ result: {
18
+ accepted: false,
19
+ eventId: event.eventId,
20
+ lifecycle: 'FAILED',
21
+ error: 'Duplicate event ID already dispatched',
22
+ reason: 'DUPLICATE_EVENT_DISPATCH',
23
+ },
24
+ });
25
+ continue;
26
+ }
27
+ this.dispatchedEventIds.add(event.eventId);
28
+ const matchingAdapters = this.adapters.filter((a) => a.kind === event.kind);
29
+ for (const adapter of matchingAdapters) {
30
+ const result = await adapter.handleEvent(event);
31
+ eventResults.push({ event, result });
32
+ }
33
+ }
34
+ return {
35
+ dispatched: eventResults.some((r) => r.result.accepted),
36
+ eventResults,
37
+ };
38
+ }
39
+ }
40
+ exports.ExperienceDispatcher = ExperienceDispatcher;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const dispatcher_1 = require("./dispatcher");
4
+ const experience_1 = require("./experience");
5
+ describe('T5 ExperienceDispatcher Contract Suite', () => {
6
+ let dispatcher;
7
+ let mockVoiceAdapter;
8
+ let mockAvatarAdapter;
9
+ beforeEach(() => {
10
+ dispatcher = new dispatcher_1.ExperienceDispatcher();
11
+ mockVoiceAdapter = {
12
+ kind: 'voice',
13
+ handleEvent: jest.fn().mockImplementation(async (event) => ({
14
+ accepted: true,
15
+ eventId: event.eventId,
16
+ lifecycle: 'STARTED',
17
+ })),
18
+ };
19
+ mockAvatarAdapter = {
20
+ kind: 'avatar',
21
+ handleEvent: jest.fn().mockImplementation(async (event) => ({
22
+ accepted: true,
23
+ eventId: event.eventId,
24
+ lifecycle: 'STARTED',
25
+ })),
26
+ };
27
+ dispatcher.registerAdapter(mockVoiceAdapter);
28
+ dispatcher.registerAdapter(mockAvatarAdapter);
29
+ });
30
+ test('dispatches experience events to matching adapters', async () => {
31
+ const events = (0, experience_1.createExperienceEvents)({
32
+ responseId: 'resp-1',
33
+ companionId: 'companion-a',
34
+ correlationId: 'corr-1',
35
+ channel: 'public',
36
+ audienceId: 'audience-public',
37
+ speech: 'Hello dispatch',
38
+ language: 'en',
39
+ });
40
+ const summary = await dispatcher.dispatchEvents(events);
41
+ expect(summary.dispatched).toBe(true);
42
+ expect(summary.eventResults.length).toBe(2);
43
+ expect(mockVoiceAdapter.handleEvent).toHaveBeenCalledTimes(1);
44
+ expect(mockAvatarAdapter.handleEvent).toHaveBeenCalledTimes(1);
45
+ });
46
+ test('does not dispatch when no matching adapters registered', async () => {
47
+ const emptyDispatcher = new dispatcher_1.ExperienceDispatcher();
48
+ const events = (0, experience_1.createExperienceEvents)({
49
+ responseId: 'resp-1',
50
+ companionId: 'companion-a',
51
+ correlationId: 'corr-1',
52
+ channel: 'public',
53
+ audienceId: 'audience-public',
54
+ speech: 'Hello empty',
55
+ });
56
+ const summary = await emptyDispatcher.dispatchEvents(events);
57
+ expect(summary.dispatched).toBe(false);
58
+ expect(summary.eventResults.length).toBe(0);
59
+ });
60
+ });
@@ -0,0 +1,33 @@
1
+ import { RequestContext } from './context';
2
+ import { EarPerception } from './index';
3
+ export interface EarLimitsConfig {
4
+ maxTextLength?: number;
5
+ maxAudioBytes?: number;
6
+ allowedAudioMimeTypes?: string[];
7
+ maxDurationSeconds?: number;
8
+ transcriptionTimeoutMs?: number;
9
+ }
10
+ export interface EarPerceptionMetadata extends Record<string, unknown> {
11
+ source?: string;
12
+ channel?: string;
13
+ modality?: 'text' | 'audio' | 'object' | 'system';
14
+ confidence?: number;
15
+ provenance?: string;
16
+ actorId?: string;
17
+ sessionId?: string;
18
+ correlationId?: string;
19
+ byteSize?: number;
20
+ durationSeconds?: number;
21
+ mimeType?: string;
22
+ }
23
+ export interface HardenedEarPerception extends EarPerception {
24
+ modality: 'text' | 'audio' | 'object' | 'system';
25
+ metadata?: EarPerceptionMetadata;
26
+ rawConfidence?: number;
27
+ }
28
+ export interface EarIngestOptions {
29
+ source?: string;
30
+ context?: RequestContext;
31
+ mimeType?: string;
32
+ durationSeconds?: number;
33
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,76 @@
1
+ import { MemoryProposal, BehaviorProposal } from './proposals';
2
+ export type EvidenceOrigin = 'knowledge' | 'observation' | 'ocr' | 'platform' | 'conversation';
3
+ export type EvidenceTrust = 'configured' | 'provider' | 'untrusted';
4
+ export type EvidenceSensitivity = 'public' | 'private' | 'restricted';
5
+ export interface EvidenceRecord {
6
+ evidenceId: string;
7
+ sourceId: string;
8
+ documentId?: string;
9
+ chunkId?: string;
10
+ locator?: string;
11
+ revision?: string;
12
+ origin: EvidenceOrigin;
13
+ confidence?: number;
14
+ uncertainty?: string;
15
+ createdAt: string;
16
+ expiresAt?: string;
17
+ trust: EvidenceTrust;
18
+ sensitivity?: EvidenceSensitivity;
19
+ allowedAudiences?: string[];
20
+ companionId: string;
21
+ correlationId: string;
22
+ [key: string]: unknown;
23
+ }
24
+ export type ResponseApprovalStatus = 'STAGED' | 'APPROVED' | 'REJECTED' | 'EXPIRED' | 'EMITTED';
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';
26
+ export interface ResponseCitation {
27
+ sourceId: string;
28
+ documentId?: string;
29
+ chunkId?: string;
30
+ locator?: string;
31
+ revision?: string;
32
+ }
33
+ export interface StagedResponsePlan {
34
+ responseId: string;
35
+ companionId: string;
36
+ correlationId: string;
37
+ channel?: string;
38
+ audienceId?: string;
39
+ speech: string;
40
+ language: string;
41
+ evidenceIds: string[];
42
+ citations: ResponseCitation[];
43
+ confidenceSummary: number;
44
+ uncertaintySummary?: string;
45
+ requiresApproval: boolean;
46
+ status: ResponseApprovalStatus;
47
+ createdAt: string;
48
+ expiresAt?: string;
49
+ memoryProposals?: MemoryProposal[];
50
+ behaviorProposals?: BehaviorProposal[];
51
+ internalMonologue?: string;
52
+ [key: string]: unknown;
53
+ }
54
+ export interface ResponseGateEvaluation {
55
+ admissible: boolean;
56
+ disposition: ResponseApprovalStatus;
57
+ reasonCode: ResponseGateReasonCode;
58
+ stagedPlan: StagedResponsePlan;
59
+ filteredEvidenceIds: string[];
60
+ filteredCitations: ResponseCitation[];
61
+ diagnostics?: Record<string, string>;
62
+ }
63
+ export interface EvidenceFilterOptions {
64
+ companionId: string;
65
+ channel?: string;
66
+ audienceId?: string;
67
+ now?: string | Date;
68
+ [key: string]: unknown;
69
+ }
70
+ export declare function filterEvidenceRecords(records: EvidenceRecord[], options: EvidenceFilterOptions): {
71
+ admitted: EvidenceRecord[];
72
+ excluded: {
73
+ record: EvidenceRecord;
74
+ reason: string;
75
+ }[];
76
+ };
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.filterEvidenceRecords = filterEvidenceRecords;
4
+ function filterEvidenceRecords(records, options) {
5
+ const admitted = [];
6
+ const excluded = [];
7
+ const nowTime = options.now ? new Date(options.now).getTime() : Date.now();
8
+ for (const record of records) {
9
+ // 1. Companion isolation
10
+ if (record.companionId !== options.companionId) {
11
+ excluded.push({ record, reason: 'companion_isolation_mismatch' });
12
+ continue;
13
+ }
14
+ // 2. Expiry
15
+ if (record.expiresAt) {
16
+ const expTime = new Date(record.expiresAt).getTime();
17
+ if (expTime <= nowTime) {
18
+ excluded.push({ record, reason: 'evidence_expired' });
19
+ continue;
20
+ }
21
+ }
22
+ // 3. Optional audience intersection (audience-public is accessible everywhere)
23
+ if (options.audienceId &&
24
+ record.allowedAudiences &&
25
+ record.allowedAudiences.length > 0 &&
26
+ !record.allowedAudiences.includes('audience-public') &&
27
+ !record.allowedAudiences.includes(options.audienceId)) {
28
+ excluded.push({ record, reason: 'audience_not_allowed' });
29
+ continue;
30
+ }
31
+ // 4. Optional channel sensitivity policy (if channel explicitly specified)
32
+ if (options.channel === 'public') {
33
+ if (record.sensitivity && record.sensitivity !== 'public') {
34
+ excluded.push({ record, reason: 'sensitivity_private_in_public_channel' });
35
+ continue;
36
+ }
37
+ }
38
+ else if (options.channel === 'direct') {
39
+ if (record.sensitivity === 'restricted') {
40
+ excluded.push({ record, reason: 'sensitivity_restricted_in_direct_channel' });
41
+ continue;
42
+ }
43
+ }
44
+ admitted.push(record);
45
+ }
46
+ return { admitted, excluded };
47
+ }
@@ -0,0 +1 @@
1
+ export {};