@portalshq/capability-interaction-orchestrator 0.0.0-stage → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/README.md +615 -2
  2. package/dist/arbitration-engine.d.ts +20 -0
  3. package/dist/arbitration-engine.d.ts.map +1 -0
  4. package/dist/arbitration-engine.js +67 -0
  5. package/dist/billing-controller.d.ts +38 -0
  6. package/dist/billing-controller.d.ts.map +1 -0
  7. package/dist/billing-controller.js +239 -0
  8. package/dist/db/repository.d.ts +41 -0
  9. package/dist/db/repository.d.ts.map +1 -0
  10. package/dist/db/repository.js +385 -0
  11. package/dist/event-publisher.d.ts +30 -0
  12. package/dist/event-publisher.d.ts.map +1 -0
  13. package/dist/event-publisher.js +122 -0
  14. package/dist/execution-bridge.d.ts +27 -0
  15. package/dist/execution-bridge.d.ts.map +1 -0
  16. package/dist/execution-bridge.js +48 -0
  17. package/dist/index.d.ts +14 -0
  18. package/dist/index.d.ts.map +1 -0
  19. package/dist/index.js +13 -0
  20. package/dist/moderation-gateway.d.ts +36 -0
  21. package/dist/moderation-gateway.d.ts.map +1 -0
  22. package/dist/moderation-gateway.js +167 -0
  23. package/dist/orchestrator.d.ts +36 -0
  24. package/dist/orchestrator.d.ts.map +1 -0
  25. package/dist/orchestrator.js +258 -0
  26. package/dist/policies/index.d.ts +33 -0
  27. package/dist/policies/index.d.ts.map +1 -0
  28. package/dist/policies/index.js +177 -0
  29. package/dist/submission-gateway.d.ts +26 -0
  30. package/dist/submission-gateway.d.ts.map +1 -0
  31. package/dist/submission-gateway.js +102 -0
  32. package/dist/types.d.ts +322 -0
  33. package/dist/types.d.ts.map +1 -0
  34. package/dist/types.js +8 -0
  35. package/dist/validation.d.ts +20 -0
  36. package/dist/validation.d.ts.map +1 -0
  37. package/dist/validation.js +221 -0
  38. package/dist/window-manager.d.ts +27 -0
  39. package/dist/window-manager.d.ts.map +1 -0
  40. package/dist/window-manager.js +57 -0
  41. package/package.json +54 -4
  42. package/sql/001_interaction_orchestrator_core.sql +139 -0
  43. package/sql/002_durable_lifecycle.sql +44 -0
package/README.md CHANGED
@@ -1,3 +1,616 @@
1
- # Temporary Holding Version
1
+ # @portalshq/capability-interaction-orchestrator
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ **Reusable interaction orchestration for Portals:** input windows, submission gateway, arbitration engine, moderation gateway, billing controller, and event publisher.
4
+
5
+ ## Status: Phase 1 (Stub Package) - NOT Production-Ready
6
+
7
+ This is currently a **stub package** providing TypeScript interfaces and type definitions. Implementations throw "not implemented" errors. Full implementation coming in Phase 2.
8
+
9
+ **Production Readiness:**
10
+ - **Phase 1 (Current):** NOT production-ready - stubs only
11
+ - **Phase 2:** Production-ready for basic use cases (FIFO, E-rated, prepaid credits)
12
+ - **Phase 3:** Production-ready for all use cases (all policies, all ratings, pluggable moderation)
13
+
14
+ ## Overview
15
+
16
+ The Interaction Orchestrator provides reusable infrastructure for managing user interactions in AI-driven entertainment experiences. It handles:
17
+
18
+ - **Input Windows** - Time-bounded opportunities for user interaction
19
+ - **Submission Gateway** - Validation, idempotency, and admission
20
+ - **Arbitration Engine** - Configurable contention resolution policies
21
+ - **Moderation Gateway** - Rating-based and pluggable content moderation
22
+ - **Billing Controller** - Integration with `@portalshq/monetization`
23
+ - **Event Publisher** - SSE/outbox for frontend signaling
24
+
25
+ ## Installation
26
+
27
+ ```bash
28
+ npm install @portalshq/monetization # Peer dependency
29
+ npm install @portalshq/capability-interaction-orchestrator
30
+ ```
31
+
32
+ ## Core Concepts
33
+
34
+ ### Input Windows
35
+
36
+ An Input Window represents a time-bounded opportunity for user interaction. Examples:
37
+ - A 30-second audience vote on a plot twist
38
+ - A 1-minute challenge round in a game show
39
+ - An always-open paid prompt queue (set `closesAt` to null)
40
+
41
+ **Window Lifecycle:**
42
+ ```
43
+ scheduled → open → qualifying → resolving → executing → committing → published
44
+ ```
45
+
46
+ **Key Properties:**
47
+ - `worldId` - Channel/world identifier
48
+ - `conflictKey` - Contention scoping (entity, branch, or session)
49
+ - `arbitrationPolicy` - How to resolve contention (FIFO, voting, etc.)
50
+ - `billingPolicy` - How to charge (free, pay-to-submit, etc.)
51
+ - `contentRating` - Moderation threshold (E, T, T+, M)
52
+ - `closesAt` - When window closes (null = always-open)
53
+
54
+ ### Submissions
55
+
56
+ A Submission is a user's proposed action against a specific target. Multiple submissions may compete for selection via arbitration.
57
+
58
+ **Key Properties:**
59
+ - `windowId` - Window this submission belongs to
60
+ - `actorId` - User/app identifier
61
+ - `idempotencyKey` - Retry safety
62
+ - `kind` - Type of interaction (vote, prompt, mutation, etc.)
63
+ - `payload` - Submission data
64
+ - `status` - Lifecycle state
65
+
66
+ ### Resolutions
67
+
68
+ A Resolution represents the outcome of arbitration: which submissions were selected and how they were combined into execution.
69
+
70
+ **Key Properties:**
71
+ - `selectedSubmissionIds` - IDs of selected submissions
72
+ - `rejectedSubmissionIds` - IDs of rejected submissions
73
+ - `arbitrationPolicy` - Policy used
74
+ - `resultingCommitHash` - Resulting world state
75
+
76
+ ### Arbitration Policies
77
+
78
+ The package supports 7 arbitration policies, configurable per window:
79
+
80
+ 1. **first_accepted** - Earliest qualifying submission wins (exclusive actions)
81
+ 2. **fifo** - Every qualifying input executes in order (paid story creation)
82
+ 3. **plurality** - Most votes wins (game shows)
83
+ 4. **ranked** - Rank by priority, then tie-break (featured submissions)
84
+ 5. **batch** - Combine compatible inputs (live narrative twists)
85
+ 6. **parallel_branches** - Every qualified input gets independent result (remix galleries)
86
+ 7. **host_selection** - Host manually selects (curated events)
87
+
88
+ ### Content Rating System
89
+
90
+ Based on DC Comics rating system:
91
+
92
+ - **E (Everyone)** - All ages, cartoonish action, no profanity/violence/sexual content
93
+ - **T (Teen)** - Ages 12+, mild violence/language/suggestive dialogue
94
+ - **T+ (Teen Plus)** - Ages 15+, moderate profanity, graphic action, mature storylines
95
+ - **M (Mature)** - Ages 17+, heavy sexual themes, nudity, extensive profanity, graphic violence
96
+
97
+ ### Billing Policies
98
+
99
+ - **free** - No payment required
100
+ - **pay_to_generate** - Payment before generation
101
+ - **pay_to_publish** - Payment after valid preview, before publication
102
+ - **pay_for_guaranteed_slot** - Payment for guaranteed submission/placement
103
+ - **capture_if_selected** - Authorize payment, capture only if selected
104
+ - **prepaid_credits** - Consume prepaid credits
105
+
106
+ ## Phase 1: Stub Package (Current)
107
+
108
+ **What exists:**
109
+ - TypeScript interfaces and types
110
+ - Stub implementations (throw "not implemented - Phase 2")
111
+ - Core database schema
112
+ - Basic documentation
113
+
114
+ **What's missing:**
115
+ - Actual implementations
116
+ - Database persistence layer
117
+ - Integration with `@portalshq/monetization`
118
+ - Integration with `@portalshq/capability-realtime-fanout-postgres`
119
+ - Tests
120
+
121
+ ## Phase 2: Core Implementation (Next)
122
+
123
+ **Will include:**
124
+ - Window Manager (in-process window closer via setInterval)
125
+ - Submission Gateway (validation, idempotency, admission)
126
+ - Arbitration Engine (FIFO policy)
127
+ - Moderation Gateway (E-rated, rule-based validators)
128
+ - Billing Controller (monetization integration)
129
+ - Event Publisher (fanout-postgres integration)
130
+ - PostgreSQL persistence layer
131
+ - Tests for implemented components
132
+ - Migration guide for replacing existing guides.ts
133
+
134
+ **Production-ready for:**
135
+ - Always-open queues (like current guides.ts)
136
+ - FIFO arbitration
137
+ - E-rated content
138
+ - Prepaid credit billing
139
+ - Basic SSE event publishing
140
+
141
+ **Estimated effort:** 60 hours
142
+
143
+ ## Phase 3: Full Feature Set
144
+
145
+ **Will add:**
146
+ - Remaining 6 arbitration policies (plurality, ranked, batch, etc.)
147
+ - T, T+, M rating validators
148
+ - Pluggable moderation interface for custom validators
149
+ - Advanced billing policies
150
+ - Complete documentation and examples
151
+ - 95% test coverage
152
+
153
+ **Estimated effort:** 44 hours
154
+
155
+ ## Database Schema
156
+
157
+ ### Core Schema (Package-Provided)
158
+
159
+ The package provides core tables in `sql/001_interaction_orchestrator_core.sql`:
160
+
161
+ - `input_windows` - Window definitions
162
+ - `submissions` - User submissions
163
+ - `resolutions` - Arbitration outcomes
164
+ - `moderation_decisions` - Moderation results
165
+ - `interaction_outbox` - Event queue
166
+
167
+ ### App Extensions (App-Provided)
168
+
169
+ Applications can extend core tables via:
170
+ - `app_metadata` JSONB column on all core tables
171
+ - Additional extension tables with foreign keys to core tables
172
+
173
+ **Example extension:**
174
+ ```sql
175
+ -- App-specific extension table
176
+ CREATE TABLE game_show_windows (
177
+ window_id text PRIMARY KEY REFERENCES input_windows(id),
178
+ round_number integer NOT NULL,
179
+ contestant_count integer NOT NULL
180
+ );
181
+ ```
182
+
183
+ ## Architecture
184
+
185
+ ```
186
+ ┌─────────────────────────────────────────────────────────────┐
187
+ │ Interaction Orchestrator Package │
188
+ │ (In-Process Only) │
189
+ ├─────────────────────────────────────────────────────────────┤
190
+ │ │
191
+ │ ┌──────────────────┐ ┌──────────────────┐ │
192
+ │ │ Window Manager │ │Submission Gateway│ │
193
+ │ │ - Lifecycle │ │ - Validation │ │
194
+ │ │ - Triggers │ │ - Idempotency │ │
195
+ │ │ - State Machine │ │ - Admission │ │
196
+ │ │ - setInterval │ │ │ │
197
+ │ └────────┬─────────┘ └────────┬─────────┘ │
198
+ │ │ │ │
199
+ │ └────────────┬───────────────┘ │
200
+ │ │ │
201
+ │ ┌────────────────────▼────────────────────┐ │
202
+ │ │ Moderation Gateway │ │
203
+ │ │ - Admission validation (built-in) │ │
204
+ │ │ - Content safety (rating-based) │ │
205
+ │ │ - Custom validators (pluggable) │ │
206
+ │ │ - E/T/T+/M rating system │ │
207
+ │ └────────────────────┬────────────────────┘ │
208
+ │ │ │
209
+ │ ┌────────────────────▼────────────────────┐ │
210
+ │ │ Arbitration Engine │ │
211
+ │ │ - FIFO, voting, branches, etc. │ │
212
+ │ │ - Configurable per window │ │
213
+ │ └────────────────────┬────────────────────┘ │
214
+ │ │ │
215
+ │ ┌────────────────────▼────────────────────┐ │
216
+ │ │ Billing Controller │ │
217
+ │ │ - Quote, authorize, capture │ │
218
+ │ │ - Integration with @portalshq/monetization│
219
+ │ └────────────────────┬────────────────────┘ │
220
+ │ │ │
221
+ │ ┌────────────────────▼────────────────────┐ │
222
+ │ │ Event Publisher │ │
223
+ │ │ - SSE events, outbox │ │
224
+ │ │ - Integration with fanout-postgres │ │
225
+ │ └─────────────────────────────────────────┘ │
226
+ │ │
227
+ └─────────────────────────────────────────────────────────────┘
228
+ ```
229
+
230
+ ## Ownership Boundaries
231
+
232
+ **Package owns:**
233
+ - Window lifecycle
234
+ - Admission gates
235
+ - Arbitration policies
236
+ - Event signaling
237
+ - Core database schema
238
+
239
+ **Application owns:**
240
+ - Moderation rules (custom validators)
241
+ - World constraints (custom validators)
242
+ - Model execution (production layer)
243
+ - Pricing (monetization catalog)
244
+ - Schema extensions (app_metadata, extension tables)
245
+ - Production layer workers (unchanged)
246
+
247
+ ## Peer Dependencies
248
+
249
+ - `@portalshq/monetization` - Billing, entitlements, ledger
250
+
251
+ ## Integration with Portals Packages
252
+
253
+ - `@portalshq/monetization` - Billing Controller integration
254
+ - `@portalshq/capability-realtime-fanout-postgres` - Event Publisher integration
255
+ - `@portalshq/capability-identity` - Actor ID verification
256
+ - `@portalshq/capability-live-production` - Window triggers integration
257
+ - `@portalshq/queue-broadcast` - Media staging integration
258
+
259
+ ## Migration Path for Bears Live App
260
+
261
+ **Phase 1:** Apply core schema, keep existing `guides.ts` unchanged
262
+
263
+ **Phase 2:**
264
+ - Rename `guides.ts` to `interaction-orchestrator-client.ts`
265
+ - Rewrite to use new package
266
+ - Configure as always-open window (no closesAt)
267
+ - Set content rating to "E"
268
+ - Test with existing payment flow
269
+
270
+ **Phase 3:** Add advanced features (voting, branches, etc.)
271
+
272
+ ## Development
273
+
274
+ ```bash
275
+ # Build
276
+ npm run build
277
+
278
+ # Type check
279
+ npm run typecheck
280
+
281
+ # Test (Phase 2+)
282
+ npm test
283
+
284
+ # Test with coverage (Phase 2+)
285
+ npm run test:coverage
286
+ ```
287
+
288
+ ## Database Setup
289
+
290
+ Apply the core schema to your PostgreSQL database:
291
+
292
+ ```bash
293
+ psql $DATABASE_URL -f node_modules/@portalshq/capability-interaction-orchestrator/sql/001_interaction_orchestrator_core.sql
294
+ ```
295
+
296
+ For Bears Live App migration, see the migration section below.
297
+
298
+ ## Migration from guides.ts to Interaction Orchestrator
299
+
300
+ ### Current State (guides.ts)
301
+
302
+ The existing `guides.ts` in Bears Live App provides:
303
+ - Simple FIFO queue for paid prompts
304
+ - 1 credit per prompt via `@portalshq/monetization`
305
+ - Basic validation and text model moderation
306
+ - Implicit always-open window (no explicit window management)
307
+ - Single arbitration policy (all eligible prompts used)
308
+
309
+ ### Migration Path
310
+
311
+ **Phase 1 (Current):**
312
+ - Apply core schema to Bears database
313
+ - Keep existing `guides.ts` unchanged
314
+ - No functional changes
315
+
316
+ **Phase 2:**
317
+ 1. Rename `guides.ts` to `interaction-orchestrator-client.ts`
318
+ 2. Import `WindowManager`, `SubmissionGateway`, etc. from new package
319
+ 3. Create an always-open window:
320
+ ```typescript
321
+ const window = await windowManager.createWindow({
322
+ worldId: channelId,
323
+ branchId: "canonical",
324
+ baseCommitHash: currentCommit,
325
+ conflictKey: channelId,
326
+ allowedInputKinds: ["custom_prompt"],
327
+ arbitrationPolicy: "fifo",
328
+ billingPolicy: "prepaid_credits",
329
+ maxSubmissionsPerUser: 1,
330
+ maxSelectedInputs: 5,
331
+ opensAt: new Date().toISOString(),
332
+ closesAt: null, // Always-open
333
+ contentRating: "E",
334
+ });
335
+ ```
336
+ 4. Replace `submitGuide()` with `submissionGateway.submitIntent()`
337
+ 5. Replace `takePendingGuides()` with window closure and arbitration
338
+ 6. Integrate moderation gateway with existing text model moderation
339
+ 7. Test with existing payment flow
340
+
341
+ **Phase 3:**
342
+ - Add voting windows for game-show modes
343
+ - Add host selection for curated events
344
+ - Add parallel branches for remixes
345
+ - Add billing variants (pay-if-selected, pay-to-publish)
346
+
347
+ ## Example: Always-Open Paid Prompt Queue
348
+
349
+ This example shows how to replace the current `guides.ts` with the Interaction Orchestrator:
350
+
351
+ ```typescript
352
+ import {
353
+ WindowManager,
354
+ SubmissionGateway,
355
+ ArbitrationEngine,
356
+ ModerationGateway,
357
+ BillingController,
358
+ EventPublisher,
359
+ type CreateWindowInput,
360
+ type SubmitIntentInput,
361
+ } from "@portalshq/capability-interaction-orchestrator";
362
+ import { createHash, randomUUID } from "node:crypto";
363
+ import type pg from "pg";
364
+
365
+ // Initialize components (in Phase 2, these will be real implementations)
366
+ const windowManager = new WindowManager(pool);
367
+ const submissionGateway = new SubmissionGateway(pool);
368
+ const arbitrationEngine = new ArbitrationEngine();
369
+ const moderationGateway = new ModerationGateway();
370
+ const billingController = new BillingController(monetizationStore);
371
+ const eventPublisher = new EventPublisher(fanoutBus);
372
+
373
+ // Create always-open window for paid prompts
374
+ async function createPromptWindow(channelId: string): Promise<string> {
375
+ const windowId = randomUUID();
376
+ const window = await windowManager.createWindow({
377
+ worldId: channelId,
378
+ branchId: "canonical",
379
+ baseCommitHash: currentCommit,
380
+ conflictKey: channelId,
381
+ allowedInputKinds: ["custom_prompt"],
382
+ arbitrationPolicy: "fifo",
383
+ billingPolicy: "prepaid_credits",
384
+ maxSubmissionsPerUser: 1,
385
+ maxSelectedInputs: 5,
386
+ opensAt: new Date().toISOString(),
387
+ closesAt: null, // Always-open
388
+ contentRating: "E",
389
+ moderationPolicyVersion: "1.0",
390
+ rulesetVersion: "1.0",
391
+ });
392
+ return window.id;
393
+ }
394
+
395
+ // Submit a paid prompt
396
+ async function submitPaidPrompt(
397
+ channelId: string,
398
+ userId: string,
399
+ prompt: string,
400
+ ): Promise<Submission> {
401
+ const windowId = getOrCreateWindowId(channelId);
402
+
403
+ const submission = await submissionGateway.submitIntent({
404
+ windowId,
405
+ actorId: userId,
406
+ idempotencyKey: createHash(userId + prompt).digest('hex'),
407
+ kind: "custom_prompt",
408
+ payload: { prompt },
409
+ });
410
+
411
+ return submission;
412
+ }
413
+
414
+ // Take prompts for next generation (replaces takePendingGuides)
415
+ async function takePromptsForGeneration(
416
+ channelId: string,
417
+ limit: number = 5,
418
+ ): Promise<Submission[]> {
419
+ const windowId = getWindowId(channelId);
420
+ const submissions = await submissionGateway.listSubmissions(windowId, "eligible");
421
+
422
+ // Resolve via FIFO arbitration
423
+ const resolution = await arbitrationEngine.resolve(
424
+ await windowManager.getWindow(windowId),
425
+ submissions.slice(0, limit),
426
+ );
427
+
428
+ // Get selected submissions
429
+ const selected = await Promise.all(
430
+ resolution.selectedSubmissionIds.map(id => submissionGateway.getSubmission(id))
431
+ );
432
+
433
+ return selected.filter((s): s is Submission => s !== null);
434
+ }
435
+ ```
436
+
437
+ ## Architecture Diagram
438
+
439
+ ```
440
+ ┌─────────────────────────────────────────────────────────────┐
441
+ │ Interaction Orchestrator Package │
442
+ │ (In-Process Only) │
443
+ ├─────────────────────────────────────────────────────────────┤
444
+ │ │
445
+ │ ┌──────────────────┐ ┌──────────────────┐ │
446
+ │ │ Window Manager │ │Submission Gateway│ │
447
+ │ │ - Lifecycle │ │ - Validation │ │
448
+ │ │ - Triggers │ │ - Idempotency │ │
449
+ │ │ - State Machine │ │ - Admission │ │
450
+ │ │ - setInterval │ │ │ │
451
+ │ └────────┬─────────┘ └────────┬─────────┘ │
452
+ │ │ │ │
453
+ │ └────────────┬───────────────┘ │
454
+ │ │ │
455
+ │ ┌────────────────────▼────────────────────┐ │
456
+ │ │ Moderation Gateway │ │
457
+ │ │ - Admission validation (built-in) │ │
458
+ │ │ - Content safety (rating-based) │ │
459
+ │ │ - Custom validators (pluggable) │ │
460
+ │ │ - E/T/T+/M rating system │ │
461
+ │ └────────────────────┬────────────────────┘ │
462
+ │ │ │
463
+ │ ┌────────────────────▼────────────────────┐ │
464
+ │ │ Arbitration Engine │ │
465
+ │ │ - FIFO │ │
466
+ │ │ - Plurality voting │ │
467
+ │ │ - First accepted │ │
468
+ │ │ - Ranked selection │ │
469
+ │ │ - Batch composition │ │
470
+ │ │ - Parallel branches │ │
471
+ │ │ - Host selection │ │
472
+ │ │ - Configurable per window │ │
473
+ │ └────────────────────┬────────────────────┘ │
474
+ │ │ │
475
+ │ ┌────────────────────▼────────────────────┐ │
476
+ │ │ Billing Controller │ │
477
+ │ │ - Quote generation │ │
478
+ │ │ - Credit reservation │ │
479
+ │ │ - Authorization/capture │ │
480
+ │ │ - Settlement │ │
481
+ │ │ - Refund handling │ │
482
+ │ │ - Integration with @portalshq/monetization│
483
+ │ └────────────────────┬────────────────────┘ │
484
+ │ │ │
485
+ │ ┌────────────────────▼────────────────────┐ │
486
+ │ │ Event Publisher │ │
487
+ │ │ - SSE events │ │
488
+ │ │ - Outbox pattern │ │
489
+ │ │ - Replay support │ │
490
+ │ │ - Integration with fanout-postgres │ │
491
+ │ └─────────────────────────────────────────┘ │
492
+ │ │
493
+ └─────────────────────────────────────────────────────────────┘
494
+ ```
495
+
496
+ ## Data Flow Diagram
497
+
498
+ ```
499
+ User Input
500
+ │
501
+ ▼
502
+ ┌─────────────┐
503
+ │ HTTP POST │
504
+ └──────┬──────┘
505
+ │
506
+ ▼
507
+ ┌──────────────────┐
508
+ │ Submission │
509
+ │ Gateway │
510
+ │ - Validate │
511
+ │ - Idempotency │
512
+ │ - Check window │
513
+ └──────┬───────────┘
514
+ │
515
+ ▼
516
+ ┌──────────────────┐
517
+ │ Moderation │
518
+ │ Gateway │
519
+ │ - Admission │
520
+ │ - Content safety │
521
+ │ - World rules │
522
+ └──────┬───────────┘
523
+ │
524
+ ▼
525
+ ┌──────────────────┐
526
+ │ Billing │
527
+ │ Controller │
528
+ │ - Quote │
529
+ │ - Authorize │
530
+ └──────┬───────────┘
531
+ │
532
+ ▼
533
+ ┌──────────────────┐
534
+ │ Submission │
535
+ │ Stored (DB) │
536
+ └──────────────────┘
537
+ │
538
+ ▼
539
+ ┌──────────────────┐
540
+ │ Window Close │
541
+ │ (setInterval) │
542
+ └──────┬───────────┘
543
+ │
544
+ ▼
545
+ ┌──────────────────┐
546
+ │ Arbitration │
547
+ │ Engine │
548
+ │ - Select inputs │
549
+ │ - Apply policy │
550
+ └──────┬───────────┘
551
+ │
552
+ ▼
553
+ ┌──────────────────┐
554
+ │ Resolution │
555
+ │ Created (DB) │
556
+ └──────┬───────────┘
557
+ │
558
+ ▼
559
+ ┌──────────────────┐
560
+ │ Billing Capture │
561
+ │ (if selected) │
562
+ └──────┬───────────┘
563
+ │
564
+ ▼
565
+ ┌──────────────────┐
566
+ │ Model Execution │
567
+ │ (App-owned) │
568
+ └──────┬───────────┘
569
+ │
570
+ ▼
571
+ ┌──────────────────┐
572
+ │ Event Publisher │
573
+ │ - Outbox │
574
+ │ - SSE │
575
+ └──────────────────┘
576
+ ```
577
+
578
+ ## Error Handling
579
+
580
+ All components should implement comprehensive error handling:
581
+
582
+ - **Window Manager:** Handle invalid state transitions, missing windows
583
+ - **Submission Gateway:** Handle idempotency conflicts, closed windows, validation failures
584
+ - **Arbitration Engine:** Handle empty submissions, policy errors
585
+ - **Moderation Gateway:** Handle moderation service failures, rating violations
586
+ - **Billing Controller:** Handle authorization failures, payment errors
587
+ - **Event Publisher:** Handle fanout failures, retry with backoff
588
+
589
+ ## Logging
590
+
591
+ Components should use structured logging with context:
592
+
593
+ ```typescript
594
+ console.log(`[interaction-orchestrator] window=${windowId} state=${state} action=${action}`);
595
+ console.error(`[interaction-orchestrator] window=${windowId} error=${error.message}`);
596
+ ```
597
+
598
+ ## Testing
599
+
600
+ Phase 2 will include comprehensive test coverage:
601
+
602
+ - Unit tests for each component
603
+ - Integration tests with monetization package
604
+ - Integration tests with fanout-postgres package
605
+ - Database migration tests
606
+ - End-to-end workflow tests
607
+
608
+ Target: 95% code coverage
609
+
610
+ ## License
611
+
612
+ MIT
613
+
614
+ ## License
615
+
616
+ MIT
@@ -0,0 +1,20 @@
1
+ import type { ArbitrationPolicyName, InputWindow, Resolution, Submission } from "./types.js";
2
+ export interface ArbitrationPolicy {
3
+ readonly name: ArbitrationPolicyName;
4
+ resolve(window: InputWindow, submissions: Submission[]): Resolution;
5
+ }
6
+ export interface IArbitrationEngine {
7
+ resolve(window: InputWindow, submissions: Submission[]): Promise<Resolution>;
8
+ registerPolicy(name: string, policy: ArbitrationPolicy): void;
9
+ setPolicyConfig(policyName: string, config: Record<string, unknown>): void;
10
+ }
11
+ /** Pure selection. Persistence and monotonic resolution sequences belong to the orchestrator. */
12
+ export declare class ArbitrationEngine implements IArbitrationEngine {
13
+ private readonly policies;
14
+ private readonly configs;
15
+ constructor();
16
+ registerPolicy(name: string, policy: ArbitrationPolicy): void;
17
+ setPolicyConfig(name: string, config: Record<string, unknown>): void;
18
+ resolve(window: InputWindow, submissions: Submission[]): Promise<Resolution>;
19
+ }
20
+ //# sourceMappingURL=arbitration-engine.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"arbitration-engine.d.ts","sourceRoot":"","sources":["../src/arbitration-engine.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,qBAAqB,EACrB,WAAW,EACX,UAAU,EACV,UAAU,EACX,MAAM,YAAY,CAAC;AAkBpB,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,IAAI,EAAE,qBAAqB,CAAC;IACrC,OAAO,CAAC,MAAM,EAAE,WAAW,EAAE,WAAW,EAAE,UAAU,EAAE,GAAG,UAAU,CAAC;CACrE;AACD,MAAM,WAAW,kBAAkB;IACjC,OAAO,CAAC,MAAM,EAAE,WAAW,EAAE,WAAW,EAAE,UAAU,EAAE,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IAC7E,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,iBAAiB,GAAG,IAAI,CAAC;IAC9D,eAAe,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;CAC5E;AACD,iGAAiG;AACjG,qBAAa,iBAAkB,YAAW,kBAAkB;IAC1D,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAwC;IACjE,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA8C;;IAatE,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,iBAAiB,GAAG,IAAI;IAS7D,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAS9D,OAAO,CACX,MAAM,EAAE,WAAW,EACnB,WAAW,EAAE,UAAU,EAAE,GACxB,OAAO,CAAC,UAAU,CAAC;CAuDvB"}