@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.
- package/README.md +615 -2
- package/dist/arbitration-engine.d.ts +20 -0
- package/dist/arbitration-engine.d.ts.map +1 -0
- package/dist/arbitration-engine.js +67 -0
- package/dist/billing-controller.d.ts +38 -0
- package/dist/billing-controller.d.ts.map +1 -0
- package/dist/billing-controller.js +239 -0
- package/dist/db/repository.d.ts +41 -0
- package/dist/db/repository.d.ts.map +1 -0
- package/dist/db/repository.js +385 -0
- package/dist/event-publisher.d.ts +30 -0
- package/dist/event-publisher.d.ts.map +1 -0
- package/dist/event-publisher.js +122 -0
- package/dist/execution-bridge.d.ts +27 -0
- package/dist/execution-bridge.d.ts.map +1 -0
- package/dist/execution-bridge.js +48 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -0
- package/dist/moderation-gateway.d.ts +36 -0
- package/dist/moderation-gateway.d.ts.map +1 -0
- package/dist/moderation-gateway.js +167 -0
- package/dist/orchestrator.d.ts +36 -0
- package/dist/orchestrator.d.ts.map +1 -0
- package/dist/orchestrator.js +258 -0
- package/dist/policies/index.d.ts +33 -0
- package/dist/policies/index.d.ts.map +1 -0
- package/dist/policies/index.js +177 -0
- package/dist/submission-gateway.d.ts +26 -0
- package/dist/submission-gateway.d.ts.map +1 -0
- package/dist/submission-gateway.js +102 -0
- package/dist/types.d.ts +322 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +8 -0
- package/dist/validation.d.ts +20 -0
- package/dist/validation.d.ts.map +1 -0
- package/dist/validation.js +221 -0
- package/dist/window-manager.d.ts +27 -0
- package/dist/window-manager.d.ts.map +1 -0
- package/dist/window-manager.js +57 -0
- package/package.json +54 -4
- package/sql/001_interaction_orchestrator_core.sql +139 -0
- package/sql/002_durable_lifecycle.sql +44 -0
package/README.md
CHANGED
|
@@ -1,3 +1,616 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @portalshq/capability-interaction-orchestrator
|
|
2
2
|
|
|
3
|
-
|
|
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"}
|