@animalabs/agent-framework 0.1.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 (212) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +233 -0
  3. package/dist/src/agent.d.ts +130 -0
  4. package/dist/src/agent.d.ts.map +1 -0
  5. package/dist/src/agent.js +406 -0
  6. package/dist/src/agent.js.map +1 -0
  7. package/dist/src/api/mcp-server.d.ts +37 -0
  8. package/dist/src/api/mcp-server.d.ts.map +1 -0
  9. package/dist/src/api/mcp-server.js +598 -0
  10. package/dist/src/api/mcp-server.js.map +1 -0
  11. package/dist/src/api/server.d.ts +83 -0
  12. package/dist/src/api/server.d.ts.map +1 -0
  13. package/dist/src/api/server.js +785 -0
  14. package/dist/src/api/server.js.map +1 -0
  15. package/dist/src/api/types.d.ts +395 -0
  16. package/dist/src/api/types.d.ts.map +1 -0
  17. package/dist/src/api/types.js +5 -0
  18. package/dist/src/api/types.js.map +1 -0
  19. package/dist/src/framework.d.ts +309 -0
  20. package/dist/src/framework.d.ts.map +1 -0
  21. package/dist/src/framework.js +2309 -0
  22. package/dist/src/framework.js.map +1 -0
  23. package/dist/src/gate/event-gate.d.ts +75 -0
  24. package/dist/src/gate/event-gate.d.ts.map +1 -0
  25. package/dist/src/gate/event-gate.js +503 -0
  26. package/dist/src/gate/event-gate.js.map +1 -0
  27. package/dist/src/gate/index.d.ts +3 -0
  28. package/dist/src/gate/index.d.ts.map +1 -0
  29. package/dist/src/gate/index.js +2 -0
  30. package/dist/src/gate/index.js.map +1 -0
  31. package/dist/src/gate/types.d.ts +82 -0
  32. package/dist/src/gate/types.d.ts.map +1 -0
  33. package/dist/src/gate/types.js +9 -0
  34. package/dist/src/gate/types.js.map +1 -0
  35. package/dist/src/index.d.ts +17 -0
  36. package/dist/src/index.d.ts.map +1 -0
  37. package/dist/src/index.js +18 -0
  38. package/dist/src/index.js.map +1 -0
  39. package/dist/src/mcpl/channel-registry.d.ts +141 -0
  40. package/dist/src/mcpl/channel-registry.d.ts.map +1 -0
  41. package/dist/src/mcpl/channel-registry.js +647 -0
  42. package/dist/src/mcpl/channel-registry.js.map +1 -0
  43. package/dist/src/mcpl/checkpoint-manager.d.ts +79 -0
  44. package/dist/src/mcpl/checkpoint-manager.d.ts.map +1 -0
  45. package/dist/src/mcpl/checkpoint-manager.js +430 -0
  46. package/dist/src/mcpl/checkpoint-manager.js.map +1 -0
  47. package/dist/src/mcpl/errors.d.ts +52 -0
  48. package/dist/src/mcpl/errors.d.ts.map +1 -0
  49. package/dist/src/mcpl/errors.js +93 -0
  50. package/dist/src/mcpl/errors.js.map +1 -0
  51. package/dist/src/mcpl/feature-set-manager.d.ts +91 -0
  52. package/dist/src/mcpl/feature-set-manager.d.ts.map +1 -0
  53. package/dist/src/mcpl/feature-set-manager.js +250 -0
  54. package/dist/src/mcpl/feature-set-manager.js.map +1 -0
  55. package/dist/src/mcpl/hook-orchestrator.d.ts +51 -0
  56. package/dist/src/mcpl/hook-orchestrator.d.ts.map +1 -0
  57. package/dist/src/mcpl/hook-orchestrator.js +217 -0
  58. package/dist/src/mcpl/hook-orchestrator.js.map +1 -0
  59. package/dist/src/mcpl/index.d.ts +14 -0
  60. package/dist/src/mcpl/index.d.ts.map +1 -0
  61. package/dist/src/mcpl/index.js +21 -0
  62. package/dist/src/mcpl/index.js.map +1 -0
  63. package/dist/src/mcpl/inference-router.d.ts +67 -0
  64. package/dist/src/mcpl/inference-router.d.ts.map +1 -0
  65. package/dist/src/mcpl/inference-router.js +249 -0
  66. package/dist/src/mcpl/inference-router.js.map +1 -0
  67. package/dist/src/mcpl/push-handler.d.ts +60 -0
  68. package/dist/src/mcpl/push-handler.d.ts.map +1 -0
  69. package/dist/src/mcpl/push-handler.js +162 -0
  70. package/dist/src/mcpl/push-handler.js.map +1 -0
  71. package/dist/src/mcpl/scope-manager.d.ts +76 -0
  72. package/dist/src/mcpl/scope-manager.d.ts.map +1 -0
  73. package/dist/src/mcpl/scope-manager.js +213 -0
  74. package/dist/src/mcpl/scope-manager.js.map +1 -0
  75. package/dist/src/mcpl/server-connection.d.ts +174 -0
  76. package/dist/src/mcpl/server-connection.d.ts.map +1 -0
  77. package/dist/src/mcpl/server-connection.js +559 -0
  78. package/dist/src/mcpl/server-connection.js.map +1 -0
  79. package/dist/src/mcpl/server-registry.d.ts +63 -0
  80. package/dist/src/mcpl/server-registry.d.ts.map +1 -0
  81. package/dist/src/mcpl/server-registry.js +119 -0
  82. package/dist/src/mcpl/server-registry.js.map +1 -0
  83. package/dist/src/mcpl/types.d.ts +755 -0
  84. package/dist/src/mcpl/types.d.ts.map +1 -0
  85. package/dist/src/mcpl/types.js +48 -0
  86. package/dist/src/mcpl/types.js.map +1 -0
  87. package/dist/src/module-registry.d.ts +93 -0
  88. package/dist/src/module-registry.d.ts.map +1 -0
  89. package/dist/src/module-registry.js +386 -0
  90. package/dist/src/module-registry.js.map +1 -0
  91. package/dist/src/modules/api/index.d.ts +22 -0
  92. package/dist/src/modules/api/index.d.ts.map +1 -0
  93. package/dist/src/modules/api/index.js +63 -0
  94. package/dist/src/modules/api/index.js.map +1 -0
  95. package/dist/src/modules/discord/discord-js-client.d.ts +58 -0
  96. package/dist/src/modules/discord/discord-js-client.d.ts.map +1 -0
  97. package/dist/src/modules/discord/discord-js-client.js +273 -0
  98. package/dist/src/modules/discord/discord-js-client.js.map +1 -0
  99. package/dist/src/modules/discord/index.d.ts +82 -0
  100. package/dist/src/modules/discord/index.d.ts.map +1 -0
  101. package/dist/src/modules/discord/index.js +959 -0
  102. package/dist/src/modules/discord/index.js.map +1 -0
  103. package/dist/src/modules/discord/types.d.ts +327 -0
  104. package/dist/src/modules/discord/types.d.ts.map +1 -0
  105. package/dist/src/modules/discord/types.js +5 -0
  106. package/dist/src/modules/discord/types.js.map +1 -0
  107. package/dist/src/modules/index.d.ts +11 -0
  108. package/dist/src/modules/index.d.ts.map +1 -0
  109. package/dist/src/modules/index.js +7 -0
  110. package/dist/src/modules/index.js.map +1 -0
  111. package/dist/src/modules/workspace/index.d.ts +59 -0
  112. package/dist/src/modules/workspace/index.d.ts.map +1 -0
  113. package/dist/src/modules/workspace/index.js +831 -0
  114. package/dist/src/modules/workspace/index.js.map +1 -0
  115. package/dist/src/modules/workspace/sync.d.ts +48 -0
  116. package/dist/src/modules/workspace/sync.d.ts.map +1 -0
  117. package/dist/src/modules/workspace/sync.js +253 -0
  118. package/dist/src/modules/workspace/sync.js.map +1 -0
  119. package/dist/src/modules/workspace/types.d.ts +166 -0
  120. package/dist/src/modules/workspace/types.d.ts.map +1 -0
  121. package/dist/src/modules/workspace/types.js +5 -0
  122. package/dist/src/modules/workspace/types.js.map +1 -0
  123. package/dist/src/modules/workspace/watcher.d.ts +43 -0
  124. package/dist/src/modules/workspace/watcher.d.ts.map +1 -0
  125. package/dist/src/modules/workspace/watcher.js +120 -0
  126. package/dist/src/modules/workspace/watcher.js.map +1 -0
  127. package/dist/src/queue.d.ts +50 -0
  128. package/dist/src/queue.d.ts.map +1 -0
  129. package/dist/src/queue.js +95 -0
  130. package/dist/src/queue.js.map +1 -0
  131. package/dist/src/safe-slice.d.ts +8 -0
  132. package/dist/src/safe-slice.d.ts.map +1 -0
  133. package/dist/src/safe-slice.js +18 -0
  134. package/dist/src/safe-slice.js.map +1 -0
  135. package/dist/src/types/agent.d.ts +118 -0
  136. package/dist/src/types/agent.d.ts.map +1 -0
  137. package/dist/src/types/agent.js +2 -0
  138. package/dist/src/types/agent.js.map +1 -0
  139. package/dist/src/types/events.d.ts +220 -0
  140. package/dist/src/types/events.d.ts.map +1 -0
  141. package/dist/src/types/events.js +2 -0
  142. package/dist/src/types/events.js.map +1 -0
  143. package/dist/src/types/framework.d.ts +184 -0
  144. package/dist/src/types/framework.d.ts.map +1 -0
  145. package/dist/src/types/framework.js +2 -0
  146. package/dist/src/types/framework.js.map +1 -0
  147. package/dist/src/types/index.d.ts +7 -0
  148. package/dist/src/types/index.d.ts.map +1 -0
  149. package/dist/src/types/index.js +7 -0
  150. package/dist/src/types/index.js.map +1 -0
  151. package/dist/src/types/inference-log.d.ts +64 -0
  152. package/dist/src/types/inference-log.d.ts.map +1 -0
  153. package/dist/src/types/inference-log.js +5 -0
  154. package/dist/src/types/inference-log.js.map +1 -0
  155. package/dist/src/types/module.d.ts +302 -0
  156. package/dist/src/types/module.d.ts.map +1 -0
  157. package/dist/src/types/module.js +2 -0
  158. package/dist/src/types/module.js.map +1 -0
  159. package/dist/src/types/trace.d.ts +173 -0
  160. package/dist/src/types/trace.d.ts.map +1 -0
  161. package/dist/src/types/trace.js +2 -0
  162. package/dist/src/types/trace.js.map +1 -0
  163. package/dist/test/event-gate.test.d.ts +2 -0
  164. package/dist/test/event-gate.test.d.ts.map +1 -0
  165. package/dist/test/event-gate.test.js +535 -0
  166. package/dist/test/event-gate.test.js.map +1 -0
  167. package/dist/test/framework.test.d.ts +5 -0
  168. package/dist/test/framework.test.d.ts.map +1 -0
  169. package/dist/test/framework.test.js +738 -0
  170. package/dist/test/framework.test.js.map +1 -0
  171. package/dist/tsconfig.tsbuildinfo +1 -0
  172. package/package.json +58 -0
  173. package/src/agent.ts +512 -0
  174. package/src/api/mcp-server.ts +687 -0
  175. package/src/api/server.ts +1023 -0
  176. package/src/api/types.ts +542 -0
  177. package/src/framework.ts +2784 -0
  178. package/src/gate/event-gate.ts +623 -0
  179. package/src/gate/index.ts +12 -0
  180. package/src/gate/types.ts +112 -0
  181. package/src/index.ts +63 -0
  182. package/src/mcpl/channel-registry.ts +824 -0
  183. package/src/mcpl/checkpoint-manager.ts +545 -0
  184. package/src/mcpl/errors.ts +114 -0
  185. package/src/mcpl/feature-set-manager.ts +333 -0
  186. package/src/mcpl/hook-orchestrator.ts +281 -0
  187. package/src/mcpl/index.ts +134 -0
  188. package/src/mcpl/inference-router.ts +335 -0
  189. package/src/mcpl/push-handler.ts +232 -0
  190. package/src/mcpl/scope-manager.ts +262 -0
  191. package/src/mcpl/server-connection.ts +694 -0
  192. package/src/mcpl/server-registry.ts +144 -0
  193. package/src/mcpl/types.ts +991 -0
  194. package/src/module-registry.ts +547 -0
  195. package/src/modules/api/index.ts +94 -0
  196. package/src/modules/discord/discord-js-client.ts +353 -0
  197. package/src/modules/discord/index.ts +1146 -0
  198. package/src/modules/discord/types.ts +465 -0
  199. package/src/modules/index.ts +43 -0
  200. package/src/modules/workspace/index.ts +969 -0
  201. package/src/modules/workspace/sync.ts +314 -0
  202. package/src/modules/workspace/types.ts +202 -0
  203. package/src/modules/workspace/watcher.ts +142 -0
  204. package/src/queue.ts +106 -0
  205. package/src/safe-slice.ts +18 -0
  206. package/src/types/agent.ts +119 -0
  207. package/src/types/events.ts +252 -0
  208. package/src/types/framework.ts +211 -0
  209. package/src/types/index.ts +6 -0
  210. package/src/types/inference-log.ts +64 -0
  211. package/src/types/module.ts +367 -0
  212. package/src/types/trace.ts +196 -0
@@ -0,0 +1,262 @@
1
+ /**
2
+ * ScopeManager — Whitelist/blacklist enforcement and scope elevation for MCPL.
3
+ *
4
+ * Manages per-feature-set scope configurations and evaluates whether actions
5
+ * are approved, denied, or require user prompting. Handles `scope/elevate`
6
+ * requests from servers.
7
+ *
8
+ * Spec references: Section 7 (Scoped Access).
9
+ */
10
+
11
+ import type {
12
+ ScopeConfig,
13
+ ScopeLabel,
14
+ ScopeElevateParams,
15
+ ScopeElevateResult,
16
+ } from './types.js';
17
+
18
+ // ============================================================================
19
+ // Types
20
+ // ============================================================================
21
+
22
+ /**
23
+ * Callback invoked when a scope elevation requires user approval.
24
+ * Returns true if the user approves, false otherwise.
25
+ */
26
+ export type ElevationHandler = (
27
+ featureSet: string,
28
+ scope: ScopeLabel
29
+ ) => Promise<boolean>;
30
+
31
+ // ============================================================================
32
+ // Glob Matching
33
+ // ============================================================================
34
+
35
+ /**
36
+ * Simple glob-style pattern matching.
37
+ *
38
+ * Supported syntax:
39
+ * - `*` matches any sequence of characters except `/`
40
+ * - `**` matches any sequence of characters including `/`
41
+ * - All other characters are matched literally
42
+ *
43
+ * The pattern must match the entire text (anchored at both ends).
44
+ */
45
+ function globMatch(pattern: string, text: string): boolean {
46
+ // Convert glob pattern to a RegExp
47
+ let regexStr = '^';
48
+ let i = 0;
49
+
50
+ while (i < pattern.length) {
51
+ if (pattern[i] === '*' && pattern[i + 1] === '*') {
52
+ // `**` matches any characters including `/`
53
+ regexStr += '.*';
54
+ i += 2;
55
+ // Skip a trailing `/` after `**` if present (e.g., `**/foo`)
56
+ if (i < pattern.length && pattern[i] === '/') {
57
+ regexStr += '(?:/|$)';
58
+ i++;
59
+ }
60
+ } else if (pattern[i] === '*') {
61
+ // `*` matches any characters except `/`
62
+ regexStr += '[^/]*';
63
+ i++;
64
+ } else {
65
+ // Escape regex special characters
66
+ regexStr += escapeRegex(pattern[i]);
67
+ i++;
68
+ }
69
+ }
70
+
71
+ regexStr += '$';
72
+
73
+ try {
74
+ const regex = new RegExp(regexStr);
75
+ return regex.test(text);
76
+ } catch {
77
+ // If the pattern produces an invalid regex, fall back to exact match
78
+ return pattern === text;
79
+ }
80
+ }
81
+
82
+ /** Escape a character for use in a RegExp. */
83
+ function escapeRegex(char: string): string {
84
+ const special = /[.+?^${}()|[\]\\]/;
85
+ if (special.test(char)) {
86
+ return '\\' + char;
87
+ }
88
+ return char;
89
+ }
90
+
91
+ // ============================================================================
92
+ // ScopeManager
93
+ // ============================================================================
94
+
95
+ /**
96
+ * Manages whitelist/blacklist enforcement and scope elevation for feature sets.
97
+ *
98
+ * Evaluation order (per spec Section 7.6):
99
+ * 1. Check approval cache (session-level)
100
+ * 2. Check blacklist (if any pattern matches -> denied)
101
+ * 3. Check whitelist (if any pattern matches -> approved)
102
+ * 4. Otherwise -> prompt
103
+ */
104
+ export class ScopeManager {
105
+ /** Scope configs keyed by feature set name. */
106
+ private configs = new Map<string, ScopeConfig>();
107
+
108
+ /** Cached approvals: Set of `${featureSet}\0${label}` keys. */
109
+ private approvalCache = new Set<string>();
110
+
111
+ /** Optional handler for user-approval prompts. */
112
+ private elevationHandler: ElevationHandler | null = null;
113
+
114
+ /**
115
+ * Set scope configuration for a single feature set.
116
+ */
117
+ configure(featureSet: string, config: ScopeConfig): void {
118
+ this.configs.set(featureSet, config);
119
+ }
120
+
121
+ /**
122
+ * Set scope configurations for multiple feature sets at once.
123
+ */
124
+ configureAll(scopes: Record<string, ScopeConfig>): void {
125
+ for (const [featureSet, config] of Object.entries(scopes)) {
126
+ this.configs.set(featureSet, config);
127
+ }
128
+ }
129
+
130
+ /**
131
+ * Check whether a label is approved, denied, or needs prompting
132
+ * for the given feature set.
133
+ *
134
+ * Evaluation order:
135
+ * 1. Approval cache hit -> 'approved'
136
+ * 2. Blacklist match -> 'denied'
137
+ * 3. Whitelist match -> 'approved'
138
+ * 4. No config or no match -> 'prompt'
139
+ */
140
+ checkScope(
141
+ featureSet: string,
142
+ label: string
143
+ ): 'approved' | 'denied' | 'prompt' {
144
+ // 1. Check approval cache
145
+ if (this.approvalCache.has(cacheKey(featureSet, label))) {
146
+ return 'approved';
147
+ }
148
+
149
+ const config = this.configs.get(featureSet);
150
+
151
+ // No config for this feature set -> prompt
152
+ if (!config) {
153
+ return 'prompt';
154
+ }
155
+
156
+ // 2. Check blacklist first (deny wins)
157
+ if (config.blacklist) {
158
+ for (const pattern of config.blacklist) {
159
+ if (globMatch(pattern, label)) {
160
+ return 'denied';
161
+ }
162
+ }
163
+ }
164
+
165
+ // 3. Check whitelist
166
+ if (config.whitelist) {
167
+ for (const pattern of config.whitelist) {
168
+ if (globMatch(pattern, label)) {
169
+ return 'approved';
170
+ }
171
+ }
172
+ }
173
+
174
+ // 4. Neither matched
175
+ return 'prompt';
176
+ }
177
+
178
+ /**
179
+ * Handle a `scope/elevate` request from a server.
180
+ *
181
+ * Checks scope against whitelist/blacklist, then:
182
+ * - 'approved' -> return approved with payload
183
+ * - 'denied' -> return denied with reason
184
+ * - 'prompt' -> invoke elevation handler if registered, otherwise deny
185
+ */
186
+ async handleElevation(
187
+ params: ScopeElevateParams
188
+ ): Promise<ScopeElevateResult> {
189
+ const { featureSet, scope } = params;
190
+ const result = this.checkScope(featureSet, scope.label);
191
+
192
+ if (result === 'approved') {
193
+ return {
194
+ approved: true,
195
+ payload: scope.payload,
196
+ };
197
+ }
198
+
199
+ if (result === 'denied') {
200
+ return {
201
+ approved: false,
202
+ reason: 'Scope denied by blacklist',
203
+ };
204
+ }
205
+
206
+ // result === 'prompt'
207
+ if (this.elevationHandler) {
208
+ const approved = await this.elevationHandler(featureSet, scope);
209
+ if (approved) {
210
+ // Cache the approval for the session
211
+ this.cacheApproval(featureSet, scope.label);
212
+ return {
213
+ approved: true,
214
+ payload: scope.payload,
215
+ };
216
+ }
217
+ return {
218
+ approved: false,
219
+ reason: 'User denied the elevation request',
220
+ };
221
+ }
222
+
223
+ // No handler registered — default to denied
224
+ return {
225
+ approved: false,
226
+ reason: 'User approval required',
227
+ };
228
+ }
229
+
230
+ /**
231
+ * Register a custom elevation handler for user-approval prompts.
232
+ * The handler is called when a scope check returns 'prompt'.
233
+ */
234
+ setElevationHandler(handler: ElevationHandler): void {
235
+ this.elevationHandler = handler;
236
+ }
237
+
238
+ /**
239
+ * Cache an approval for the session.
240
+ * Subsequent `checkScope()` calls with the same feature set and label
241
+ * will return 'approved' without re-evaluating patterns.
242
+ */
243
+ cacheApproval(featureSet: string, label: string): void {
244
+ this.approvalCache.add(cacheKey(featureSet, label));
245
+ }
246
+
247
+ /**
248
+ * Clear all cached approvals.
249
+ */
250
+ clearCache(): void {
251
+ this.approvalCache.clear();
252
+ }
253
+ }
254
+
255
+ // ============================================================================
256
+ // Helpers
257
+ // ============================================================================
258
+
259
+ /** Build a cache key from feature set and label. */
260
+ function cacheKey(featureSet: string, label: string): string {
261
+ return `${featureSet}\0${label}`;
262
+ }