@sidurijs/core 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. package/dist/action-executor.d.ts +12 -0
  2. package/dist/action-executor.js +54 -0
  3. package/dist/action-policy.d.ts +79 -0
  4. package/dist/action-policy.js +439 -0
  5. package/dist/action-policy.test.d.ts +1 -0
  6. package/dist/action-policy.test.js +363 -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 +491 -0
  11. package/dist/architecture-boundary.test.d.ts +1 -0
  12. package/dist/architecture-boundary.test.js +108 -0
  13. package/dist/capability.d.ts +78 -0
  14. package/dist/capability.js +168 -0
  15. package/dist/capability.test.d.ts +1 -0
  16. package/dist/capability.test.js +311 -0
  17. package/dist/chat-contract.d.ts +94 -0
  18. package/dist/chat-contract.js +104 -0
  19. package/dist/cognition-planner.d.ts +15 -0
  20. package/dist/cognition-planner.js +23 -0
  21. package/dist/container.d.ts +74 -0
  22. package/dist/container.js +81 -0
  23. package/dist/context-retriever.d.ts +33 -0
  24. package/dist/context-retriever.js +260 -0
  25. package/dist/context.d.ts +46 -0
  26. package/dist/context.js +85 -0
  27. package/dist/context.test.d.ts +1 -0
  28. package/dist/context.test.js +76 -0
  29. package/dist/conversational-teach.test.d.ts +1 -0
  30. package/dist/conversational-teach.test.js +941 -0
  31. package/dist/database.d.ts +1 -0
  32. package/dist/database.js +17 -0
  33. package/dist/dispatcher.d.ts +14 -0
  34. package/dist/dispatcher.js +40 -0
  35. package/dist/dispatcher.test.d.ts +1 -0
  36. package/dist/dispatcher.test.js +58 -0
  37. package/dist/ear-types.d.ts +33 -0
  38. package/dist/ear-types.js +2 -0
  39. package/dist/evidence.d.ts +76 -0
  40. package/dist/evidence.js +27 -0
  41. package/dist/evidence.test.d.ts +1 -0
  42. package/dist/evidence.test.js +75 -0
  43. package/dist/experience-emitter.d.ts +21 -0
  44. package/dist/experience-emitter.js +47 -0
  45. package/dist/experience.d.ts +55 -0
  46. package/dist/experience.js +74 -0
  47. package/dist/experience.test.d.ts +1 -0
  48. package/dist/experience.test.js +55 -0
  49. package/dist/gating.d.ts +45 -0
  50. package/dist/gating.js +185 -0
  51. package/dist/gating.test.d.ts +1 -0
  52. package/dist/gating.test.js +186 -0
  53. package/dist/index.d.ts +367 -0
  54. package/dist/index.js +56 -0
  55. package/dist/input-normalizer.d.ts +14 -0
  56. package/dist/input-normalizer.js +76 -0
  57. package/dist/input-normalizer.test.d.ts +1 -0
  58. package/dist/input-normalizer.test.js +48 -0
  59. package/dist/intent-classifier.d.ts +37 -0
  60. package/dist/intent-classifier.js +150 -0
  61. package/dist/intent-classifier.test.d.ts +1 -0
  62. package/dist/intent-classifier.test.js +118 -0
  63. package/dist/memory-settler.d.ts +49 -0
  64. package/dist/memory-settler.js +161 -0
  65. package/dist/mouth-types.d.ts +90 -0
  66. package/dist/mouth-types.js +2 -0
  67. package/dist/perception-cycle.test.d.ts +1 -0
  68. package/dist/perception-cycle.test.js +301 -0
  69. package/dist/perception-pipeline.d.ts +77 -0
  70. package/dist/perception-pipeline.js +304 -0
  71. package/dist/perception-pipeline.test.d.ts +1 -0
  72. package/dist/perception-pipeline.test.js +65 -0
  73. package/dist/prompt-compiler.d.ts +26 -0
  74. package/dist/prompt-compiler.js +81 -0
  75. package/dist/prompt-compiler.test.d.ts +1 -0
  76. package/dist/prompt-compiler.test.js +90 -0
  77. package/dist/proposals.d.ts +33 -0
  78. package/dist/proposals.js +2 -0
  79. package/dist/response-envelope.d.ts +30 -0
  80. package/dist/response-envelope.js +90 -0
  81. package/dist/runtime-facades.test.d.ts +1 -0
  82. package/dist/runtime-facades.test.js +95 -0
  83. package/dist/runtime.d.ts +116 -0
  84. package/dist/runtime.js +778 -0
  85. package/dist/schema-validator.d.ts +9 -0
  86. package/dist/schema-validator.js +107 -0
  87. package/dist/schema-validator.test.d.ts +1 -0
  88. package/dist/schema-validator.test.js +124 -0
  89. package/dist/session-history.d.ts +20 -0
  90. package/dist/session-history.js +55 -0
  91. package/dist/session-history.test.d.ts +1 -0
  92. package/dist/session-history.test.js +38 -0
  93. package/dist/siduri-db.d.ts +235 -0
  94. package/dist/siduri-db.js +1405 -0
  95. package/dist/siduri-db.test.d.ts +1 -0
  96. package/dist/siduri-db.test.js +1021 -0
  97. package/dist/sqlite-action-store.d.ts +21 -0
  98. package/dist/sqlite-action-store.js +281 -0
  99. package/dist/sqlite-action-store.test.d.ts +1 -0
  100. package/dist/sqlite-action-store.test.js +261 -0
  101. package/dist/teaching.d.ts +14 -0
  102. package/dist/teaching.js +14 -0
  103. package/package.json +43 -0
@@ -0,0 +1 @@
1
+ export * from './siduri-db';
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./siduri-db"), exports);
@@ -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,58 @@
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
+ speech: 'Hello dispatch',
37
+ language: 'en',
38
+ });
39
+ const summary = await dispatcher.dispatchEvents(events);
40
+ expect(summary.dispatched).toBe(true);
41
+ expect(summary.eventResults.length).toBe(2);
42
+ expect(mockVoiceAdapter.handleEvent).toHaveBeenCalledTimes(1);
43
+ expect(mockAvatarAdapter.handleEvent).toHaveBeenCalledTimes(1);
44
+ });
45
+ test('does not dispatch when no matching adapters registered', async () => {
46
+ const emptyDispatcher = new dispatcher_1.ExperienceDispatcher();
47
+ const events = (0, experience_1.createExperienceEvents)({
48
+ responseId: 'resp-1',
49
+ companionId: 'companion-a',
50
+ correlationId: 'corr-1',
51
+ channel: 'public',
52
+ speech: 'Hello empty',
53
+ });
54
+ const summary = await emptyDispatcher.dispatchEvents(events);
55
+ expect(summary.dispatched).toBe(false);
56
+ expect(summary.eventResults.length).toBe(0);
57
+ });
58
+ });
@@ -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' | 'memory' | 'life' | '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
+ companionId: string;
20
+ correlationId: string;
21
+ [key: string]: unknown;
22
+ }
23
+ export type ResponseApprovalStatus = 'STAGED' | 'APPROVED' | 'REJECTED' | 'EXPIRED' | 'EMITTED';
24
+ export type ResponseGateReasonCode = 'APPROVED_DIRECT' | 'APPROVAL_REQUIRED' | 'UNKNOWN_APPROVAL_ID' | 'APPROVAL_ID_MISMATCH' | 'COMPANION_MISMATCH' | 'EVIDENCE_EXPIRED' | 'EVIDENCE_SENSITIVITY_EXCLUDED' | 'UNRESOLVED_LOW_CONFIDENCE' | 'EMPTY_SPEECH' | 'PROPOSAL_VALIDATION_FAILED' | 'EXPLICITLY_REJECTED';
25
+ export interface ResponseCitation {
26
+ sourceId: string;
27
+ documentId?: string;
28
+ chunkId?: string;
29
+ locator?: string;
30
+ revision?: string;
31
+ evidenceId?: string;
32
+ provenance?: string;
33
+ preview?: string;
34
+ }
35
+ export interface StagedResponsePlan {
36
+ responseId: string;
37
+ companionId: string;
38
+ correlationId: string;
39
+ channel?: string;
40
+ speech: string;
41
+ language: string;
42
+ evidenceIds: string[];
43
+ citations: ResponseCitation[];
44
+ confidenceSummary: number;
45
+ uncertaintySummary?: string;
46
+ requiresApproval: boolean;
47
+ status: ResponseApprovalStatus;
48
+ createdAt: string;
49
+ expiresAt?: string;
50
+ memoryProposals?: MemoryProposal[];
51
+ behaviorProposals?: BehaviorProposal[];
52
+ internalMonologue?: string;
53
+ [key: string]: unknown;
54
+ }
55
+ export interface ResponseGateEvaluation {
56
+ admissible: boolean;
57
+ disposition: ResponseApprovalStatus;
58
+ reasonCode: ResponseGateReasonCode;
59
+ stagedPlan: StagedResponsePlan;
60
+ filteredEvidenceIds: string[];
61
+ filteredCitations: ResponseCitation[];
62
+ diagnostics?: Record<string, string>;
63
+ }
64
+ export interface EvidenceFilterOptions {
65
+ companionId: string;
66
+ channel?: 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,27 @@
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
+ // In single-owner architecture, memory and evidence are partitioned strictly
23
+ // by companion boundary (companionId) and temporal expiration.
24
+ admitted.push(record);
25
+ }
26
+ return { admitted, excluded };
27
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,75 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const evidence_1 = require("./evidence");
4
+ describe('T4 Evidence & Disclosure Core Contract', () => {
5
+ const baseRecord = {
6
+ evidenceId: 'ev-1',
7
+ sourceId: 'src-1',
8
+ origin: 'knowledge',
9
+ trust: 'configured',
10
+ sensitivity: 'public',
11
+ companionId: 'companion-a',
12
+ correlationId: 'corr-1',
13
+ createdAt: new Date(Date.now() - 5000).toISOString(),
14
+ };
15
+ test('companion isolation excludes foreign companion evidence', () => {
16
+ const records = [
17
+ { ...baseRecord, evidenceId: 'ev-mine', companionId: 'companion-a' },
18
+ { ...baseRecord, evidenceId: 'ev-foreign', companionId: 'companion-b' },
19
+ ];
20
+ const options = {
21
+ companionId: 'companion-a',
22
+ channel: 'public',
23
+ };
24
+ const { admitted, excluded } = (0, evidence_1.filterEvidenceRecords)(records, options);
25
+ expect(admitted.map((e) => e.evidenceId)).toEqual(['ev-mine']);
26
+ expect(excluded).toEqual([
27
+ expect.objectContaining({
28
+ record: expect.objectContaining({ evidenceId: 'ev-foreign' }),
29
+ reason: 'companion_isolation_mismatch',
30
+ }),
31
+ ]);
32
+ });
33
+ test('expired evidence is excluded', () => {
34
+ const records = [
35
+ { ...baseRecord, evidenceId: 'ev-valid', expiresAt: new Date(Date.now() + 60000).toISOString() },
36
+ { ...baseRecord, evidenceId: 'ev-expired', expiresAt: new Date(Date.now() - 1000).toISOString() },
37
+ ];
38
+ const options = {
39
+ companionId: 'companion-a',
40
+ channel: 'public',
41
+ };
42
+ const { admitted, excluded } = (0, evidence_1.filterEvidenceRecords)(records, options);
43
+ expect(admitted.map((e) => e.evidenceId)).toEqual(['ev-valid']);
44
+ expect(excluded).toEqual([
45
+ expect.objectContaining({
46
+ record: expect.objectContaining({ evidenceId: 'ev-expired' }),
47
+ reason: 'evidence_expired',
48
+ }),
49
+ ]);
50
+ });
51
+ test('single-owner companion admits valid evidence without multi-audience filtering', () => {
52
+ const records = [
53
+ { ...baseRecord, evidenceId: 'ev-public' },
54
+ { ...baseRecord, evidenceId: 'ev-direct-only' },
55
+ ];
56
+ const options = {
57
+ companionId: 'companion-a',
58
+ };
59
+ const { admitted, excluded } = (0, evidence_1.filterEvidenceRecords)(records, options);
60
+ expect(admitted.map((e) => e.evidenceId)).toEqual(['ev-public', 'ev-direct-only']);
61
+ expect(excluded).toEqual([]);
62
+ });
63
+ test('single-owner companion admits private and restricted sensitivity for companion owner', () => {
64
+ const records = [
65
+ { ...baseRecord, evidenceId: 'ev-pub', sensitivity: 'public' },
66
+ { ...baseRecord, evidenceId: 'ev-priv', sensitivity: 'private' },
67
+ { ...baseRecord, evidenceId: 'ev-rest', sensitivity: 'restricted' },
68
+ ];
69
+ const { admitted, excluded } = (0, evidence_1.filterEvidenceRecords)(records, {
70
+ companionId: 'companion-a',
71
+ });
72
+ expect(admitted.map((e) => e.evidenceId)).toEqual(['ev-pub', 'ev-priv', 'ev-rest']);
73
+ expect(excluded).toEqual([]);
74
+ });
75
+ });
@@ -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,47 @@
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
+ speech,
17
+ language: language || 'ja',
18
+ evidenceIds: gateEval.filteredEvidenceIds,
19
+ citations: gateEval.filteredCitations,
20
+ expression: 'neutral',
21
+ action: 'talk',
22
+ expiresAt: stagedPlan.expiresAt,
23
+ });
24
+ const dispatchResult = await dispatcher.dispatchEvents(experienceEvents);
25
+ let speechId;
26
+ const voiceResult = dispatchResult.eventResults.find((r) => r.event.kind === 'voice');
27
+ if (voiceResult?.result?.metadata?.speechId) {
28
+ speechId = voiceResult.result.metadata.speechId;
29
+ }
30
+ else if (voice &&
31
+ typeof voice.handleEvent !== 'function' &&
32
+ typeof voice.enqueueSpeech === 'function') {
33
+ speechId = voice.enqueueSpeech(speech, language || 'ja', 1);
34
+ }
35
+ if (body && typeof body.handleEvent !== 'function') {
36
+ if (typeof body.setExpression === 'function') {
37
+ body.setExpression('neutral');
38
+ }
39
+ if (typeof body.act === 'function') {
40
+ body.act('talk');
41
+ }
42
+ }
43
+ return {
44
+ experienceEvents,
45
+ speechId,
46
+ };
47
+ }
@@ -0,0 +1,55 @@
1
+ import { ResponseCitation } from './evidence';
2
+ export type ExperienceEventKind = 'voice' | 'caption' | 'avatar' | 'platform_action';
3
+ export type ExperienceEventLifecycle = 'STARTED' | 'PROGRESS' | 'COMPLETED' | 'FAILED';
4
+ export interface ExperienceEvent {
5
+ eventId: string;
6
+ companionId: string;
7
+ responseId: string;
8
+ correlationId: string;
9
+ channel?: string;
10
+ approval: 'APPROVED';
11
+ kind: ExperienceEventKind;
12
+ lifecycle: ExperienceEventLifecycle;
13
+ evidenceIds: string[];
14
+ citations?: ResponseCitation[];
15
+ text?: string;
16
+ language?: string;
17
+ action?: string;
18
+ expression?: string;
19
+ createdAt: string;
20
+ expiresAt?: string;
21
+ [key: string]: unknown;
22
+ }
23
+ export interface ExperienceAdapterResult {
24
+ accepted: boolean;
25
+ eventId: string;
26
+ lifecycle: ExperienceEventLifecycle;
27
+ error?: string;
28
+ reason?: string;
29
+ audioBuffer?: Uint8Array;
30
+ metadata?: Record<string, unknown>;
31
+ }
32
+ export interface ExperienceAdapter {
33
+ readonly kind: ExperienceEventKind;
34
+ handleEvent(event: ExperienceEvent): Promise<ExperienceAdapterResult>;
35
+ }
36
+ export interface CreateExperienceEventsOptions {
37
+ responseId: string;
38
+ companionId: string;
39
+ correlationId: string;
40
+ channel?: string;
41
+ speech: string;
42
+ language?: string;
43
+ evidenceIds?: string[];
44
+ citations?: ResponseCitation[];
45
+ expression?: string;
46
+ action?: string;
47
+ expiresAt?: string;
48
+ now?: string | Date;
49
+ [key: string]: unknown;
50
+ }
51
+ export declare function createExperienceEvents(options: CreateExperienceEventsOptions): ExperienceEvent[];
52
+ export declare function validateExperienceEvent(event: unknown): {
53
+ valid: boolean;
54
+ error?: string;
55
+ };
@@ -0,0 +1,74 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createExperienceEvents = createExperienceEvents;
4
+ exports.validateExperienceEvent = validateExperienceEvent;
5
+ function generateEventId(kind) {
6
+ return `evt-${kind}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
7
+ }
8
+ function createExperienceEvents(options) {
9
+ const nowStr = options.now
10
+ ? new Date(options.now).toISOString()
11
+ : new Date().toISOString();
12
+ const events = [];
13
+ // 1. Voice event
14
+ events.push({
15
+ eventId: generateEventId('voice'),
16
+ companionId: options.companionId,
17
+ responseId: options.responseId,
18
+ correlationId: options.correlationId,
19
+ channel: options.channel,
20
+ approval: 'APPROVED',
21
+ kind: 'voice',
22
+ lifecycle: 'STARTED',
23
+ evidenceIds: options.evidenceIds ?? [],
24
+ citations: options.citations,
25
+ text: options.speech,
26
+ language: options.language || 'ja',
27
+ createdAt: nowStr,
28
+ expiresAt: options.expiresAt,
29
+ });
30
+ // 2. Avatar/Body event
31
+ events.push({
32
+ eventId: generateEventId('avatar'),
33
+ companionId: options.companionId,
34
+ responseId: options.responseId,
35
+ correlationId: options.correlationId,
36
+ channel: options.channel,
37
+ approval: 'APPROVED',
38
+ kind: 'avatar',
39
+ lifecycle: 'STARTED',
40
+ evidenceIds: options.evidenceIds ?? [],
41
+ text: options.speech,
42
+ language: options.language || 'ja',
43
+ expression: options.expression || 'neutral',
44
+ action: options.action || 'talk',
45
+ createdAt: nowStr,
46
+ expiresAt: options.expiresAt,
47
+ });
48
+ return events;
49
+ }
50
+ function validateExperienceEvent(event) {
51
+ if (!event || typeof event !== 'object') {
52
+ return { valid: false, error: 'Event must be an object' };
53
+ }
54
+ const e = event;
55
+ if (!e.eventId || typeof e.eventId !== 'string')
56
+ return { valid: false, error: 'Missing or invalid eventId' };
57
+ if (!e.companionId || typeof e.companionId !== 'string')
58
+ return { valid: false, error: 'Missing or invalid companionId' };
59
+ if (!e.responseId || typeof e.responseId !== 'string')
60
+ return { valid: false, error: 'Missing or invalid responseId' };
61
+ if (!e.correlationId || typeof e.correlationId !== 'string')
62
+ return { valid: false, error: 'Missing or invalid correlationId' };
63
+ if (e.approval !== 'APPROVED')
64
+ return { valid: false, error: 'Event approval must be APPROVED' };
65
+ if (!['voice', 'caption', 'avatar', 'platform_action'].includes(e.kind)) {
66
+ return { valid: false, error: `Invalid kind: ${e.kind}` };
67
+ }
68
+ if (!['STARTED', 'PROGRESS', 'COMPLETED', 'FAILED'].includes(e.lifecycle)) {
69
+ return { valid: false, error: `Invalid lifecycle: ${e.lifecycle}` };
70
+ }
71
+ if (!Array.isArray(e.evidenceIds))
72
+ return { valid: false, error: 'Missing or invalid evidenceIds array' };
73
+ return { valid: true };
74
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const experience_1 = require("./experience");
4
+ describe('T5 Experience Event Contract Suite', () => {
5
+ const baseEventOptions = {
6
+ responseId: 'resp-123',
7
+ companionId: 'companion-a',
8
+ correlationId: 'corr-123',
9
+ channel: 'public',
10
+ speech: 'Hello world',
11
+ language: 'en',
12
+ evidenceIds: ['ev-1'],
13
+ expression: 'happy',
14
+ action: 'wave',
15
+ };
16
+ test('creates structured voice and avatar experience events from approved response options', () => {
17
+ const events = (0, experience_1.createExperienceEvents)(baseEventOptions);
18
+ expect(events.length).toBe(2);
19
+ const voiceEvent = events.find((e) => e.kind === 'voice');
20
+ expect(voiceEvent).toBeDefined();
21
+ expect(voiceEvent?.approval).toBe('APPROVED');
22
+ expect(voiceEvent?.companionId).toBe('companion-a');
23
+ expect(voiceEvent?.correlationId).toBe('corr-123');
24
+ expect(voiceEvent?.text).toBe('Hello world');
25
+ expect(voiceEvent?.language).toBe('en');
26
+ expect(voiceEvent?.evidenceIds).toEqual(['ev-1']);
27
+ const avatarEvent = events.find((e) => e.kind === 'avatar');
28
+ expect(avatarEvent).toBeDefined();
29
+ expect(avatarEvent?.approval).toBe('APPROVED');
30
+ expect(avatarEvent?.expression).toBe('happy');
31
+ expect(avatarEvent?.action).toBe('wave');
32
+ });
33
+ test('validates experience event envelope correctly', () => {
34
+ const events = (0, experience_1.createExperienceEvents)(baseEventOptions);
35
+ for (const e of events) {
36
+ const val = (0, experience_1.validateExperienceEvent)(e);
37
+ expect(val.valid).toBe(true);
38
+ expect(val.error).toBeUndefined();
39
+ }
40
+ });
41
+ test('rejects unapproved experience event', () => {
42
+ const events = (0, experience_1.createExperienceEvents)(baseEventOptions);
43
+ const unapproved = { ...events[0], approval: 'STAGED' };
44
+ const val = (0, experience_1.validateExperienceEvent)(unapproved);
45
+ expect(val.valid).toBe(false);
46
+ expect(val.error).toContain('Event approval must be APPROVED');
47
+ });
48
+ test('rejects missing metadata (companionId, responseId, etc.)', () => {
49
+ const events = (0, experience_1.createExperienceEvents)(baseEventOptions);
50
+ const missingCompanion = { ...events[0], companionId: '' };
51
+ expect((0, experience_1.validateExperienceEvent)(missingCompanion).valid).toBe(false);
52
+ const missingResponseId = { ...events[0], responseId: '' };
53
+ expect((0, experience_1.validateExperienceEvent)(missingResponseId).valid).toBe(false);
54
+ });
55
+ });
@@ -0,0 +1,45 @@
1
+ import { RequestContext } from './context';
2
+ import { MemoryProposal, BehaviorProposal } from './proposals';
3
+ import { EvidenceRecord, StagedResponsePlan, ResponseGateEvaluation, ResponseCitation } from './evidence';
4
+ export interface StageResponseOptions {
5
+ requestContext: RequestContext;
6
+ candidateSpeech: string;
7
+ candidateLanguage: string;
8
+ internalMonologue?: string;
9
+ memoryProposals?: MemoryProposal[];
10
+ behaviorProposals?: BehaviorProposal[];
11
+ evidenceRecords?: EvidenceRecord[];
12
+ citations?: ResponseCitation[];
13
+ requiresApproval?: boolean;
14
+ ttlMs?: number;
15
+ now?: string | Date;
16
+ }
17
+ export interface ApproveResponseOptions {
18
+ responseId: string;
19
+ companionId: string;
20
+ correlationId: string;
21
+ }
22
+ export interface RejectResponseOptions {
23
+ responseId: string;
24
+ companionId: string;
25
+ correlationId: string;
26
+ reason?: string;
27
+ }
28
+ export declare class ResponseGatingEngine {
29
+ private readonly stagedPlans;
30
+ private readonly consumedApprovals;
31
+ stageResponse(options: StageResponseOptions): StagedResponsePlan;
32
+ evaluateGate(staged: StagedResponsePlan, allEvidence?: EvidenceRecord[], now?: string | Date): ResponseGateEvaluation;
33
+ approveResponse(options: ApproveResponseOptions): {
34
+ success: boolean;
35
+ reason?: string;
36
+ plan?: StagedResponsePlan;
37
+ };
38
+ rejectResponse(options: RejectResponseOptions): {
39
+ success: boolean;
40
+ reason?: string;
41
+ plan?: StagedResponsePlan;
42
+ };
43
+ getStagedPlan(responseId: string): StagedResponsePlan | undefined;
44
+ findStagedPlanByCorrelation(companionId: string, correlationId: string): StagedResponsePlan | undefined;
45
+ }