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/src/index.ts CHANGED
@@ -1,7 +1,103 @@
1
- export * from './core/types'
2
- export { createHumanFunction } from './core/factory'
1
+ /**
2
+ * human-in-the-loop - Primitives for integrating human oversight and intervention in AI workflows
3
+ *
4
+ * This package provides primitives for human oversight and intervention in AI workflows:
5
+ * - Approval gates and workflows
6
+ * - Review processes and queues
7
+ * - Escalation paths
8
+ * - Human intervention points
9
+ * - Role and team management
10
+ * - Goals, KPIs, and OKRs tracking
11
+ *
12
+ * Implements the digital-workers interface for humans operating within a company boundary.
13
+ *
14
+ * @packageDocumentation
15
+ * @example
16
+ * ```ts
17
+ * import { Human, approve, ask, notify } from 'human-in-the-loop'
18
+ *
19
+ * // Create a Human-in-the-loop manager
20
+ * const human = Human({
21
+ * defaultTimeout: 3600000, // 1 hour
22
+ * autoEscalate: true,
23
+ * })
24
+ *
25
+ * // Request approval
26
+ * const result = await approve({
27
+ * title: 'Deploy to production',
28
+ * description: 'Approve deployment of v2.0.0',
29
+ * subject: 'Production Deployment',
30
+ * assignee: 'tech-lead@example.com',
31
+ * priority: 'high',
32
+ * })
33
+ *
34
+ * if (result.approved) {
35
+ * await deploy()
36
+ * await notify({
37
+ * type: 'success',
38
+ * title: 'Deployment complete',
39
+ * message: 'v2.0.0 deployed to production',
40
+ * recipient: 'team@example.com',
41
+ * })
42
+ * }
43
+ * ```
44
+ */
3
45
 
4
- export * from './platforms/slack'
5
- export * from './platforms/teams'
6
- export * from './platforms/react'
7
- export * from './platforms/email'
46
+ // Export main Human constructor and manager
47
+ export { Human, HumanManager } from './human.js'
48
+
49
+ // Export helper functions (convenience API)
50
+ export {
51
+ Role,
52
+ Team,
53
+ Goals,
54
+ approve,
55
+ ask,
56
+ do,
57
+ decide,
58
+ generate,
59
+ is,
60
+ notify,
61
+ kpis,
62
+ okrs,
63
+ registerHuman,
64
+ getDefaultHuman,
65
+ } from './helpers.js'
66
+
67
+ // Export store implementations
68
+ export { InMemoryHumanStore } from './store.js'
69
+
70
+ // Export all types
71
+ export type {
72
+ // Status and enums
73
+ HumanRequestStatus,
74
+ Priority,
75
+
76
+ // Core entities
77
+ Role as RoleType,
78
+ Team as TeamType,
79
+ Human as HumanType,
80
+ Goals as GoalsType,
81
+ KPIs,
82
+ OKRs,
83
+
84
+ // Request types
85
+ HumanRequest,
86
+ ApprovalRequest,
87
+ ApprovalResponse,
88
+ QuestionRequest,
89
+ TaskRequest,
90
+ DecisionRequest,
91
+ ReviewRequest,
92
+ ReviewResponse,
93
+ Notification,
94
+
95
+ // Management
96
+ ReviewQueue,
97
+ EscalationPolicy,
98
+ ApprovalWorkflow,
99
+
100
+ // Store interface
101
+ HumanStore,
102
+ HumanOptions,
103
+ } from './types.js'
package/src/store.ts ADDED
@@ -0,0 +1,201 @@
1
+ /**
2
+ * In-memory store implementation for human requests
3
+ */
4
+
5
+ import type {
6
+ HumanStore,
7
+ HumanRequest,
8
+ HumanRequestStatus,
9
+ ReviewQueue,
10
+ } from './types.js'
11
+
12
+ /**
13
+ * Simple in-memory implementation of HumanStore
14
+ *
15
+ * For production use, implement a persistent store using:
16
+ * - Database (PostgreSQL, MongoDB, etc.)
17
+ * - Key-value store (Redis)
18
+ * - Message queue (RabbitMQ, AWS SQS)
19
+ */
20
+ export class InMemoryHumanStore implements HumanStore {
21
+ private requests = new Map<string, HumanRequest>()
22
+ private requestIdCounter = 0
23
+
24
+ /**
25
+ * Generate a unique request ID
26
+ */
27
+ private generateId(): string {
28
+ return `req_${Date.now()}_${++this.requestIdCounter}`
29
+ }
30
+
31
+ /**
32
+ * Create a new request
33
+ */
34
+ async create<T extends HumanRequest>(
35
+ request: Omit<T, 'id' | 'createdAt' | 'updatedAt'>
36
+ ): Promise<T> {
37
+ const now = new Date()
38
+ const fullRequest = {
39
+ ...request,
40
+ id: this.generateId(),
41
+ createdAt: now,
42
+ updatedAt: now,
43
+ } as T
44
+
45
+ this.requests.set(fullRequest.id, fullRequest)
46
+ return fullRequest
47
+ }
48
+
49
+ /**
50
+ * Get a request by ID
51
+ */
52
+ async get<T extends HumanRequest>(id: string): Promise<T | null> {
53
+ const request = this.requests.get(id)
54
+ return request ? (request as T) : null
55
+ }
56
+
57
+ /**
58
+ * Update a request
59
+ */
60
+ async update<T extends HumanRequest>(
61
+ id: string,
62
+ updates: Partial<T>
63
+ ): Promise<T> {
64
+ const request = this.requests.get(id)
65
+ if (!request) {
66
+ throw new Error(`Request not found: ${id}`)
67
+ }
68
+
69
+ const updated = {
70
+ ...request,
71
+ ...updates,
72
+ updatedAt: new Date(),
73
+ } as T
74
+
75
+ this.requests.set(id, updated)
76
+ return updated
77
+ }
78
+
79
+ /**
80
+ * List requests with filters
81
+ */
82
+ async list<T extends HumanRequest>(
83
+ filters?: ReviewQueue['filters'],
84
+ limit?: number
85
+ ): Promise<T[]> {
86
+ let requests = Array.from(this.requests.values()) as T[]
87
+
88
+ // Apply filters
89
+ if (filters) {
90
+ if (filters.status) {
91
+ requests = requests.filter((r) => filters.status!.includes(r.status))
92
+ }
93
+ if (filters.priority) {
94
+ requests = requests.filter((r) => filters.priority!.includes(r.priority))
95
+ }
96
+ if (filters.assignee) {
97
+ requests = requests.filter((r) => {
98
+ if (!r.assignee) return false
99
+ const assignees = Array.isArray(r.assignee) ? r.assignee : [r.assignee]
100
+ return assignees.some((a) => filters.assignee!.includes(a))
101
+ })
102
+ }
103
+ if (filters.role) {
104
+ requests = requests.filter((r) => r.role && filters.role!.includes(r.role))
105
+ }
106
+ if (filters.team) {
107
+ requests = requests.filter((r) => r.team && filters.team!.includes(r.team))
108
+ }
109
+ }
110
+
111
+ // Sort by creation date (newest first)
112
+ requests.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime())
113
+
114
+ // Apply limit
115
+ if (limit && limit > 0) {
116
+ requests = requests.slice(0, limit)
117
+ }
118
+
119
+ return requests
120
+ }
121
+
122
+ /**
123
+ * Complete a request
124
+ */
125
+ async complete<T extends HumanRequest>(
126
+ id: string,
127
+ response: T['response']
128
+ ): Promise<T> {
129
+ const request = await this.get<T>(id)
130
+ if (!request) {
131
+ throw new Error(`Request not found: ${id}`)
132
+ }
133
+
134
+ return this.update<T>(id, {
135
+ status: 'completed' as HumanRequestStatus,
136
+ response,
137
+ completedAt: new Date(),
138
+ } as Partial<T>)
139
+ }
140
+
141
+ /**
142
+ * Reject a request
143
+ */
144
+ async reject(id: string, reason: string): Promise<HumanRequest> {
145
+ const request = await this.get(id)
146
+ if (!request) {
147
+ throw new Error(`Request not found: ${id}`)
148
+ }
149
+
150
+ return this.update(id, {
151
+ status: 'rejected' as HumanRequestStatus,
152
+ rejectionReason: reason,
153
+ completedAt: new Date(),
154
+ })
155
+ }
156
+
157
+ /**
158
+ * Escalate a request
159
+ */
160
+ async escalate(id: string, to: string): Promise<HumanRequest> {
161
+ const request = await this.get(id)
162
+ if (!request) {
163
+ throw new Error(`Request not found: ${id}`)
164
+ }
165
+
166
+ return this.update(id, {
167
+ status: 'escalated' as HumanRequestStatus,
168
+ assignee: to,
169
+ })
170
+ }
171
+
172
+ /**
173
+ * Cancel a request
174
+ */
175
+ async cancel(id: string): Promise<HumanRequest> {
176
+ const request = await this.get(id)
177
+ if (!request) {
178
+ throw new Error(`Request not found: ${id}`)
179
+ }
180
+
181
+ return this.update(id, {
182
+ status: 'cancelled' as HumanRequestStatus,
183
+ completedAt: new Date(),
184
+ })
185
+ }
186
+
187
+ /**
188
+ * Clear all requests (for testing)
189
+ */
190
+ clear(): void {
191
+ this.requests.clear()
192
+ this.requestIdCounter = 0
193
+ }
194
+
195
+ /**
196
+ * Get total count of requests
197
+ */
198
+ count(): number {
199
+ return this.requests.size
200
+ }
201
+ }