human-in-the-loop 0.1.0 → 2.0.2

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 CHANGED
@@ -1,222 +1,656 @@
1
- # Human-in-the-Loop
1
+ # human-in-the-loop
2
2
 
3
- A strongly-typed interface for human functions across multiple platforms.
3
+ Primitives for integrating human oversight and intervention in AI workflows. Implements the digital-workers interface for humans operating within a company boundary.
4
+
5
+ ## Overview
6
+
7
+ This package provides a comprehensive toolkit for human-in-the-loop (HITL) workflows, enabling seamless integration of human judgment, approval, and intervention points in automated AI systems.
8
+
9
+ **Key Features:**
10
+
11
+ - 🎯 **Approval Workflows** - Multi-step approval gates with escalation
12
+ - ❓ **Question & Answer** - Ask humans for information or guidance
13
+ - 📋 **Task Assignment** - Delegate tasks that require human judgment
14
+ - 🔀 **Decision Points** - Request human decisions between options
15
+ - 👀 **Review Processes** - Code, content, design, and data reviews
16
+ - 📬 **Notifications** - Multi-channel notifications (Slack, email, SMS, web)
17
+ - 👥 **Role & Team Management** - Define roles, teams, and capabilities
18
+ - 📊 **Goals & OKRs** - Track objectives, KPIs, and key results
19
+ - ⏰ **Timeouts & Escalation** - Automatic escalation on timeout
20
+ - 🔄 **Review Queues** - Organize and prioritize pending requests
4
21
 
5
22
  ## Installation
6
23
 
7
24
  ```bash
8
- npm install human-in-the-loop
9
- # or
10
- yarn add human-in-the-loop
11
- # or
12
25
  pnpm add human-in-the-loop
13
26
  ```
14
27
 
15
- ## Features
28
+ ## Quick Start
29
+
30
+ ```typescript
31
+ import { Human, approve, ask, notify } from 'human-in-the-loop'
32
+
33
+ // Create a Human-in-the-loop manager
34
+ const human = Human({
35
+ defaultTimeout: 3600000, // 1 hour
36
+ autoEscalate: true,
37
+ })
38
+
39
+ // Request approval
40
+ const result = await approve({
41
+ title: 'Deploy to production',
42
+ description: 'Approve deployment of v2.0.0',
43
+ subject: 'Production Deployment',
44
+ input: { version: '2.0.0' },
45
+ assignee: 'tech-lead@example.com',
46
+ priority: 'high',
47
+ })
48
+
49
+ if (result.approved) {
50
+ await deploy()
51
+ await notify({
52
+ type: 'success',
53
+ title: 'Deployment complete',
54
+ message: 'v2.0.0 deployed to production',
55
+ recipient: 'team@example.com',
56
+ })
57
+ }
58
+ ```
59
+
60
+ ## API Reference
16
61
 
17
- - Strongly-typed human functions with TypeScript generics
18
- - Support for multiple platforms:
19
- - Slack (using Block Kit)
20
- - Microsoft Teams
21
- - React/ShadCN UI components
22
- - Email (via React Email)
23
- - Simple, consistent API across all platforms
24
- - Type-safe inputs and outputs
62
+ ### Core Functions
25
63
 
26
- ## Usage
64
+ #### `Human(options?)`
27
65
 
28
- ### Basic Example
66
+ Create a Human-in-the-loop manager instance.
29
67
 
30
68
  ```typescript
31
- import { createHumanFunction } from 'human-in-the-loop'
69
+ const human = Human({
70
+ defaultTimeout: 3600000, // Default timeout in ms
71
+ defaultPriority: 'normal', // Default priority level
72
+ autoEscalate: true, // Auto-escalate on timeout
73
+ escalationPolicies: [...], // Escalation policies
74
+ store: customStore, // Custom storage backend
75
+ })
76
+ ```
32
77
 
33
- // Define a strongly-typed human function for approval
34
- const getApproval = createHumanFunction<
35
- { documentTitle: string, documentUrl: string },
36
- { approved: boolean, comments?: string }
37
- >({
38
- platform: 'slack',
39
- title: 'Document Approval Request',
40
- description: 'Please review and approve the following document:',
41
- options: [
42
- { value: 'approve', label: 'Approve' },
43
- { value: 'reject', label: 'Reject' }
44
- ],
45
- freeText: true,
46
- // Slack-specific options
47
- channel: 'approvals'
78
+ #### `approve(params)`
79
+
80
+ Request approval from a human.
81
+
82
+ ```typescript
83
+ const result = await approve({
84
+ title: 'Expense Approval',
85
+ description: 'Approve employee expense claim',
86
+ subject: 'Expense Claim #1234',
87
+ input: { amount: 150, category: 'Travel' },
88
+ assignee: 'manager@example.com',
89
+ priority: 'normal',
90
+ timeout: 86400000, // 24 hours
91
+ escalatesTo: 'director@example.com',
48
92
  })
49
93
 
50
- // Request human input
51
- const task = await getApproval.request({
52
- documentTitle: 'Q2 Financial Report',
53
- documentUrl: 'https://example.com/docs/q2-finance'
94
+ // result: { approved: boolean, comments?: string, conditions?: string[] }
95
+ ```
96
+
97
+ #### `ask(params)`
98
+
99
+ Ask a question to a human.
100
+
101
+ ```typescript
102
+ const answer = await ask({
103
+ title: 'Product naming',
104
+ question: 'What should we name the new feature?',
105
+ context: { feature: 'AI Assistant' },
106
+ assignee: 'product-manager@example.com',
107
+ suggestions: ['CodeMate', 'DevAssist', 'AIHelper'],
54
108
  })
55
109
 
56
- console.log(`Task created with ID: ${task.taskId}`)
110
+ // answer: string
111
+ ```
57
112
 
58
- // Later, get the response
59
- const response = await getApproval.getResponse(task.taskId)
60
- if (response?.approved) {
61
- console.log('Document was approved!')
62
- if (response.comments) {
63
- console.log(`Comments: ${response.comments}`)
64
- }
65
- } else {
66
- console.log('Document was rejected or pending')
67
- }
113
+ #### `do(params)`
114
+
115
+ Request a human to perform a task (implements digital-workers interface).
116
+
117
+ ```typescript
118
+ const result = await do({
119
+ title: 'Review code',
120
+ instructions: 'Review the PR and provide feedback',
121
+ input: { prUrl: 'https://github.com/...' },
122
+ assignee: 'senior-dev@example.com',
123
+ estimatedEffort: '30 minutes',
124
+ })
125
+
126
+ // result: TOutput (task-specific)
68
127
  ```
69
128
 
70
- ### Platform-Specific Examples
129
+ #### `decide(params)`
71
130
 
72
- #### Slack
131
+ Request a human to make a decision.
73
132
 
74
133
  ```typescript
75
- import { createHumanFunction } from 'human-in-the-loop'
134
+ const strategy = await decide({
135
+ title: 'Deployment strategy',
136
+ options: ['blue-green', 'canary', 'rolling'],
137
+ context: { risk: 'high', users: 100000 },
138
+ criteria: ['Minimize risk', 'Fast rollback'],
139
+ assignee: 'devops-lead@example.com',
140
+ })
76
141
 
77
- const slackFeedback = createHumanFunction<
78
- { featureDescription: string },
79
- { rating: number, feedback?: string }
80
- >({
81
- platform: 'slack',
82
- title: 'Feature Feedback Request',
83
- description: 'Please provide feedback on our new feature:',
84
- options: [
85
- { value: '1', label: '⭐ Poor' },
86
- { value: '2', label: '⭐⭐ Fair' },
87
- { value: '3', label: '⭐⭐⭐ Good' },
88
- { value: '4', label: '⭐⭐⭐⭐ Very Good' },
89
- { value: '5', label: '⭐⭐⭐⭐⭐ Excellent' }
90
- ],
91
- freeText: true,
92
- channel: 'product-feedback'
142
+ // strategy: 'blue-green' | 'canary' | 'rolling'
143
+ ```
144
+
145
+ #### `generate(params)`
146
+
147
+ Request content generation from a human (specialized form of `do()`).
148
+
149
+ ```typescript
150
+ const content = await generate({
151
+ title: 'Write blog post',
152
+ instructions: 'Write about our new AI features',
153
+ input: { topic: 'AI Assistant', targetAudience: 'developers' },
154
+ assignee: 'content-writer@example.com',
155
+ })
156
+
157
+ // content: string
158
+ ```
159
+
160
+ #### `is(params)`
161
+
162
+ Request validation/type checking from a human.
163
+
164
+ ```typescript
165
+ const valid = await is({
166
+ title: 'Validate data',
167
+ question: 'Is this user data valid and complete?',
168
+ input: userData,
169
+ assignee: 'data-specialist@example.com',
93
170
  })
171
+
172
+ // valid: boolean
94
173
  ```
95
174
 
96
- #### Microsoft Teams
175
+ #### `notify(params)`
176
+
177
+ Send a notification to a human.
97
178
 
98
179
  ```typescript
99
- import { createHumanFunction } from 'human-in-the-loop'
180
+ await notify({
181
+ type: 'info', // 'info' | 'warning' | 'error' | 'success'
182
+ title: 'Deployment complete',
183
+ message: 'Version 2.0.0 deployed successfully',
184
+ recipient: 'team@example.com',
185
+ channels: ['slack', 'email'], // Optional
186
+ priority: 'normal',
187
+ })
188
+ ```
100
189
 
101
- const teamsApproval = createHumanFunction<
102
- { requestDetails: string },
103
- { approved: boolean }
104
- >({
105
- platform: 'teams',
106
- title: 'Approval Request',
107
- description: 'Please review and approve this request:',
108
- options: [
109
- { value: 'approve', label: 'Approve' },
110
- { value: 'reject', label: 'Reject' }
111
- ],
112
- webhookUrl: process.env.TEAMS_WEBHOOK_URL,
113
- useAdaptiveCards: true
114
- })
115
- ```
116
-
117
- #### React
118
-
119
- ```tsx
120
- import { createHumanFunction, ReactHumanFunction } from 'human-in-the-loop'
121
- import React, { useState } from 'react'
122
-
123
- // Create the human function
124
- const reactFeedback = createHumanFunction<
125
- { productName: string },
126
- { rating: number, comments?: string }
127
- >({
128
- platform: 'react',
129
- title: 'Product Feedback',
130
- description: 'Please rate this product and provide any comments:',
131
- options: [
132
- { value: '1', label: '1 - Poor' },
133
- { value: '2', label: '2 - Fair' },
134
- { value: '3', label: '3 - Good' },
135
- { value: '4', label: '4 - Very Good' },
136
- { value: '5', label: '5 - Excellent' }
190
+ ### Role & Team Management
191
+
192
+ #### `Role(definition)`
193
+
194
+ Define a human role.
195
+
196
+ ```typescript
197
+ const techLead = Role({
198
+ id: 'tech-lead',
199
+ name: 'Tech Lead',
200
+ description: 'Technical leadership',
201
+ capabilities: ['approve-prs', 'deploy-prod'],
202
+ escalatesTo: 'engineering-manager',
203
+ })
204
+ ```
205
+
206
+ #### `Team(definition)`
207
+
208
+ Define a team.
209
+
210
+ ```typescript
211
+ const engineering = Team({
212
+ id: 'engineering',
213
+ name: 'Engineering Team',
214
+ members: ['alice', 'bob', 'charlie'],
215
+ lead: 'alice',
216
+ })
217
+ ```
218
+
219
+ #### `registerHuman(human)`
220
+
221
+ Register a human worker.
222
+
223
+ ```typescript
224
+ const alice = registerHuman({
225
+ id: 'alice',
226
+ name: 'Alice Smith',
227
+ email: 'alice@example.com',
228
+ roles: ['tech-lead'],
229
+ teams: ['engineering'],
230
+ channels: {
231
+ slack: '@alice',
232
+ email: 'alice@example.com',
233
+ },
234
+ })
235
+ ```
236
+
237
+ ### Goals & Performance Tracking
238
+
239
+ #### `Goals(definition)`
240
+
241
+ Define goals for a team or individual.
242
+
243
+ ```typescript
244
+ const q1Goals = Goals({
245
+ id: 'q1-2024',
246
+ objectives: ['Launch v2.0', 'Improve performance by 50%'],
247
+ successCriteria: ['Release by March 31', 'Pass benchmarks'],
248
+ targetDate: new Date('2024-03-31'),
249
+ })
250
+ ```
251
+
252
+ #### `kpis(kpi)`
253
+
254
+ Track Key Performance Indicators.
255
+
256
+ ```typescript
257
+ kpis({
258
+ id: 'response-time',
259
+ name: 'API Response Time',
260
+ value: 120,
261
+ target: 100,
262
+ unit: 'ms',
263
+ trend: 'down',
264
+ })
265
+ ```
266
+
267
+ #### `okrs(okr)`
268
+
269
+ Define Objectives and Key Results.
270
+
271
+ ```typescript
272
+ okrs({
273
+ id: 'q1-2024-growth',
274
+ objective: 'Accelerate user growth',
275
+ keyResults: [
276
+ {
277
+ description: 'Increase active users by 50%',
278
+ progress: 0.3,
279
+ current: 13000,
280
+ target: 15000,
281
+ },
137
282
  ],
138
- freeText: true,
139
- theme: 'light'
140
- }) as ReactHumanFunction<{ productName: string }, { rating: number, comments?: string }>
141
-
142
- // In your React component
143
- function FeedbackComponent() {
144
- const [taskId, setTaskId] = useState<string | null>(null)
145
-
146
- const handleRequestFeedback = async () => {
147
- const task = await reactFeedback.request({ productName: 'Super Product' })
148
- setTaskId(task.taskId)
283
+ period: 'Q1 2024',
284
+ owner: 'ceo@example.com',
285
+ })
286
+ ```
287
+
288
+ ## Advanced Usage
289
+
290
+ ### Custom Store Implementation
291
+
292
+ Implement a custom storage backend:
293
+
294
+ ```typescript
295
+ import { HumanStore } from 'human-in-the-loop'
296
+
297
+ class DatabaseHumanStore implements HumanStore {
298
+ async create(request) {
299
+ // Save to database
300
+ }
301
+
302
+ async get(id) {
303
+ // Fetch from database
304
+ }
305
+
306
+ async update(id, updates) {
307
+ // Update in database
308
+ }
309
+
310
+ async complete(id, response) {
311
+ // Complete request
312
+ }
313
+
314
+ async reject(id, reason) {
315
+ // Reject request
316
+ }
317
+
318
+ async escalate(id, to) {
319
+ // Escalate request
320
+ }
321
+
322
+ async cancel(id) {
323
+ // Cancel request
324
+ }
325
+
326
+ async list(filters, limit) {
327
+ // List requests with filters
149
328
  }
150
-
151
- return (
152
- <div>
153
- <button onClick={handleRequestFeedback}>Request Feedback</button>
154
-
155
- {taskId && reactFeedback.getComponent(taskId, { productName: 'Super Product' })}
156
- </div>
157
- )
158
329
  }
330
+
331
+ const human = Human({
332
+ store: new DatabaseHumanStore(),
333
+ })
159
334
  ```
160
335
 
161
- #### Email
336
+ ### Escalation Policies
337
+
338
+ Define automatic escalation policies:
162
339
 
163
340
  ```typescript
164
- import { createHumanFunction, EmailHumanFunction } from 'human-in-the-loop'
165
- import React from 'react'
166
- import { render } from 'react-email'
341
+ const human = Human({
342
+ autoEscalate: true,
343
+ escalationPolicies: [
344
+ {
345
+ id: 'critical-approval',
346
+ name: 'Critical Approval Policy',
347
+ conditions: {
348
+ timeout: 1800000, // 30 minutes
349
+ minPriority: 'critical',
350
+ },
351
+ escalationPath: [
352
+ {
353
+ assignee: 'tech-lead',
354
+ afterMs: 1800000, // 30 minutes
355
+ notifyVia: ['slack', 'sms'],
356
+ },
357
+ {
358
+ assignee: 'engineering-manager',
359
+ afterMs: 3600000, // 1 hour
360
+ notifyVia: ['slack', 'email', 'sms'],
361
+ },
362
+ ],
363
+ },
364
+ ],
365
+ })
366
+ ```
367
+
368
+ ### Approval Workflows
167
369
 
168
- // Create the human function
169
- const emailSurvey = createHumanFunction<
170
- { surveyTopic: string },
171
- { response: string }
172
- >({
173
- platform: 'email',
174
- title: 'Customer Survey',
175
- description: 'Please complete our customer satisfaction survey:',
176
- options: [
177
- { value: 'very-satisfied', label: 'Very Satisfied' },
178
- { value: 'satisfied', label: 'Satisfied' },
179
- { value: 'neutral', label: 'Neutral' },
180
- { value: 'dissatisfied', label: 'Dissatisfied' },
181
- { value: 'very-dissatisfied', label: 'Very Dissatisfied' }
370
+ Create multi-step approval workflows:
371
+
372
+ ```typescript
373
+ const workflow = human.createWorkflow({
374
+ id: 'prod-deployment-workflow',
375
+ name: 'Production Deployment Workflow',
376
+ steps: [
377
+ {
378
+ name: 'Code Review',
379
+ role: 'engineer',
380
+ approvers: ['bob'],
381
+ requireAll: true,
382
+ },
383
+ {
384
+ name: 'Security Review',
385
+ role: 'security-engineer',
386
+ approvers: ['security-team'],
387
+ requireAll: false, // Any one approver
388
+ },
389
+ {
390
+ name: 'Tech Lead Approval',
391
+ role: 'tech-lead',
392
+ approvers: ['alice'],
393
+ requireAll: true,
394
+ },
182
395
  ],
183
- to: 'customer@example.com',
184
- from: 'surveys@company.com',
185
- callbackUrl: 'https://example.com/api/survey-response'
186
- }) as EmailHumanFunction<{ surveyTopic: string }, { response: string }>
396
+ })
397
+ ```
398
+
399
+ ### Review Queues
400
+
401
+ Organize and prioritize pending requests:
402
+
403
+ ```typescript
404
+ const queue = await human.getQueue({
405
+ name: 'High Priority Queue',
406
+ description: 'All high priority pending requests',
407
+ filters: {
408
+ status: ['pending', 'in_progress'],
409
+ priority: ['high', 'critical'],
410
+ },
411
+ sortBy: 'priority',
412
+ sortDirection: 'desc',
413
+ limit: 10,
414
+ })
415
+
416
+ console.log(`Queue: ${queue.name}`)
417
+ console.log(`Items: ${queue.items.length}`)
187
418
 
188
- // Get the email template as HTML
189
- const taskId = 'task-123'
190
- const emailComponent = emailSurvey.getEmailComponent(taskId)
191
- const emailHtml = render(emailComponent)
419
+ for (const item of queue.items) {
420
+ console.log(`- ${item.title} (${item.priority})`)
421
+ }
192
422
  ```
193
423
 
194
- ## API Reference
424
+ ### Request Management
195
425
 
196
- ### `createHumanFunction<TInput, TOutput>(options)`
426
+ Manually manage requests:
197
427
 
198
- Creates a strongly-typed human function with the specified input and output types.
428
+ ```typescript
429
+ // Get a request
430
+ const request = await human.getRequest('req_123')
431
+
432
+ // Complete with response
433
+ await human.completeRequest('req_123', {
434
+ approved: true,
435
+ comments: 'Looks good!',
436
+ })
437
+
438
+ // Reject
439
+ await human.rejectRequest('req_123', 'Not ready yet')
440
+
441
+ // Escalate
442
+ await human.escalateRequest('req_123', 'manager@example.com')
443
+
444
+ // Cancel
445
+ await human.cancelRequest('req_123')
446
+ ```
447
+
448
+ ## Integration with digital-workers
449
+
450
+ Human-in-the-loop implements the `Worker` interface from `digital-workers`, enabling humans to:
451
+ - Receive notifications, questions, and approval requests via Worker Actions
452
+ - Be targeted by workflow actions
453
+ - Communicate through configured contact channels
454
+
455
+ ### Using Worker Actions in Workflows
456
+
457
+ ```typescript
458
+ import { Workflow } from 'ai-workflows'
459
+ import { registerWorkerActions, withWorkers } from 'digital-workers'
460
+ import type { Worker } from 'digital-workers'
461
+
462
+ // Define a human worker with contacts
463
+ const alice: Worker = {
464
+ id: 'alice',
465
+ name: 'Alice',
466
+ type: 'human',
467
+ status: 'available',
468
+ contacts: {
469
+ email: 'alice@company.com',
470
+ slack: { workspace: 'company', user: 'U123' },
471
+ phone: '+1-555-1234',
472
+ },
473
+ }
474
+
475
+ const workflow = Workflow($ => {
476
+ registerWorkerActions($)
477
+ const worker$ = withWorkers($)
478
+
479
+ $.on.Expense.submitted(async (expense) => {
480
+ // Request approval from manager using Worker Actions
481
+ const approval = await worker$.approve(
482
+ `Expense: $${expense.amount} for ${expense.description}`,
483
+ alice,
484
+ { via: 'slack', timeout: 86400000 }
485
+ )
486
+
487
+ if (approval.approved) {
488
+ await worker$.notify(
489
+ expense.submitter,
490
+ 'Your expense has been approved!',
491
+ { via: 'email' }
492
+ )
493
+ }
494
+ })
495
+
496
+ $.on.Deployment.requested(async (deploy) => {
497
+ // Ask human for confirmation
498
+ const confirmed = await worker$.ask(
499
+ alice,
500
+ `Proceed with deployment of ${deploy.version} to ${deploy.env}?`,
501
+ { schema: { proceed: 'boolean', notes: 'string' } }
502
+ )
503
+
504
+ if (confirmed.answer.proceed) {
505
+ $.send('Deployment.approved', deploy)
506
+ }
507
+ })
508
+ })
509
+ ```
510
+
511
+ ### Human as Worker Target
512
+
513
+ ```typescript
514
+ import { registerHuman } from 'human-in-the-loop'
515
+ import { notify, ask, approve } from 'digital-workers'
516
+
517
+ // Register a human worker
518
+ const manager = registerHuman({
519
+ id: 'manager',
520
+ name: 'Manager',
521
+ email: 'manager@company.com',
522
+ roles: ['approver'],
523
+ teams: ['finance'],
524
+ channels: {
525
+ slack: '@manager',
526
+ email: 'manager@company.com',
527
+ },
528
+ })
529
+
530
+ // Use digital-workers actions directly
531
+ await notify(manager, 'Weekly report ready', { via: 'email' })
199
532
 
200
- #### Options
533
+ const answer = await ask(manager, 'What is the Q2 budget?', {
534
+ via: 'slack',
535
+ schema: { amount: 'number', notes: 'string' },
536
+ })
201
537
 
202
- - `platform`: The platform to use ('slack', 'teams', 'react', or 'email')
203
- - `title`: Title of the request shown to humans
204
- - `description`: Description of the task for humans
205
- - `options`: Array of options for the human to choose from
206
- - `freeText`: Whether to allow free text input
207
- - `timeout`: Timeout in milliseconds after which the task is marked as timed out
538
+ const approval = await approve('Hire contractor', manager, {
539
+ via: 'slack',
540
+ context: { role: 'Senior Developer', rate: '$150/hr' },
541
+ })
542
+ ```
208
543
 
209
- Platform-specific options are also available based on the selected platform.
544
+ ## Integration Examples
210
545
 
211
- ### `HumanFunction<TInput, TOutput>`
546
+ ### With AI Workflows
212
547
 
213
- The interface for human functions.
548
+ ```typescript
549
+ import { Human } from 'human-in-the-loop'
550
+ import { AI } from 'ai-functions'
551
+
552
+ const human = Human()
553
+ const ai = AI()
554
+
555
+ async function processWithOversight(data: unknown) {
556
+ // AI generates initial result
557
+ const result = await ai.analyze(data)
558
+
559
+ // Human reviews before proceeding
560
+ const review = await human.review({
561
+ title: 'Review AI analysis',
562
+ content: result,
563
+ reviewType: 'data',
564
+ criteria: ['Accuracy', 'Completeness', 'Bias'],
565
+ assignee: 'data-analyst@example.com',
566
+ })
567
+
568
+ if (review.approved) {
569
+ return result
570
+ } else {
571
+ // Request human to fix
572
+ return await human.do({
573
+ title: 'Fix analysis',
574
+ instructions: review.comments,
575
+ input: { original: result, feedback: review.changes },
576
+ assignee: 'data-analyst@example.com',
577
+ })
578
+ }
579
+ }
580
+ ```
581
+
582
+ ### With MCP Tools
214
583
 
215
- #### Methods
584
+ ```typescript
585
+ import { createMCPServer } from '@mdxe/mcp'
586
+ import { Human } from 'human-in-the-loop'
587
+
588
+ const human = Human()
589
+
590
+ const mcpServer = createMCPServer({
591
+ tools: {
592
+ request_approval: {
593
+ description: 'Request human approval',
594
+ parameters: {
595
+ type: 'object',
596
+ properties: {
597
+ title: { type: 'string' },
598
+ description: { type: 'string' },
599
+ assignee: { type: 'string' },
600
+ },
601
+ required: ['title', 'description'],
602
+ },
603
+ handler: async (params) => {
604
+ const result = await human.approve({
605
+ title: params.title,
606
+ description: params.description,
607
+ subject: params.title,
608
+ input: params,
609
+ assignee: params.assignee,
610
+ })
611
+ return result
612
+ },
613
+ },
614
+ },
615
+ })
616
+ ```
216
617
 
217
- - `request(input: TInput): Promise<HumanTaskRequest>`: Request human input with the given input data
218
- - `getResponse(taskId: string): Promise<TOutput | null>`: Get the human response for a given task
618
+ ## Architecture
619
+
620
+ The package follows a clean architecture with clear separation of concerns:
621
+
622
+ ```
623
+ human-in-the-loop/
624
+ ├── src/
625
+ │ ├── types.ts # Type definitions
626
+ │ ├── store.ts # Storage implementation
627
+ │ ├── human.ts # Core HumanManager class
628
+ │ ├── helpers.ts # Convenience functions
629
+ │ └── index.ts # Public exports
630
+ ├── examples/
631
+ │ └── basic-usage.ts # Usage examples
632
+ └── README.md
633
+ ```
634
+
635
+ **Design Principles:**
636
+
637
+ - **Store-agnostic**: Works with any storage backend (in-memory, database, queue)
638
+ - **Channel-flexible**: Supports multiple notification channels
639
+ - **Type-safe**: Full TypeScript support with strict types
640
+ - **Extensible**: Easy to add custom workflows and policies
641
+ - **Digital Workers Interface**: Compatible with the digital-workers abstraction
642
+
643
+ ## Related Packages
644
+
645
+ - **ai-functions**: Core AI primitives
646
+ - **digital-workers**: Abstract interface over AI agents and humans
647
+ - **ai-workflows**: Event-driven AI workflows
648
+ - **@mdxe/mcp**: Model Context Protocol integration
219
649
 
220
650
  ## License
221
651
 
222
652
  MIT
653
+
654
+ ## Contributing
655
+
656
+ Contributions welcome! Please see the main repository for guidelines.