@dot-ai/core 0.5.2 → 0.7.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 (132) hide show
  1. package/dist/boot-cache.d.ts +40 -0
  2. package/dist/boot-cache.d.ts.map +1 -0
  3. package/dist/boot-cache.js +72 -0
  4. package/dist/boot-cache.js.map +1 -0
  5. package/dist/capabilities.d.ts +35 -0
  6. package/dist/capabilities.d.ts.map +1 -0
  7. package/dist/capabilities.js +17 -0
  8. package/dist/capabilities.js.map +1 -0
  9. package/dist/config.d.ts +7 -23
  10. package/dist/config.d.ts.map +1 -1
  11. package/dist/config.js +131 -108
  12. package/dist/config.js.map +1 -1
  13. package/dist/extension-api.d.ts +65 -0
  14. package/dist/extension-api.d.ts.map +1 -0
  15. package/dist/extension-api.js +2 -0
  16. package/dist/extension-api.js.map +1 -0
  17. package/dist/extension-loader.d.ts +19 -0
  18. package/dist/extension-loader.d.ts.map +1 -0
  19. package/dist/extension-loader.js +113 -0
  20. package/dist/extension-loader.js.map +1 -0
  21. package/dist/extension-runner.d.ts +62 -0
  22. package/dist/extension-runner.d.ts.map +1 -0
  23. package/dist/extension-runner.js +260 -0
  24. package/dist/extension-runner.js.map +1 -0
  25. package/dist/extension-types.d.ts +312 -0
  26. package/dist/extension-types.d.ts.map +1 -0
  27. package/dist/extension-types.js +89 -0
  28. package/dist/extension-types.js.map +1 -0
  29. package/dist/format.d.ts +13 -1
  30. package/dist/format.d.ts.map +1 -1
  31. package/dist/format.js +131 -15
  32. package/dist/format.js.map +1 -1
  33. package/dist/format.spec.d.ts +2 -0
  34. package/dist/format.spec.d.ts.map +1 -0
  35. package/dist/format.spec.js +140 -0
  36. package/dist/format.spec.js.map +1 -0
  37. package/dist/index.d.ts +21 -14
  38. package/dist/index.d.ts.map +1 -1
  39. package/dist/index.js +21 -14
  40. package/dist/index.js.map +1 -1
  41. package/dist/logger.d.ts +1 -1
  42. package/dist/logger.d.ts.map +1 -1
  43. package/dist/package-manager.d.ts +30 -0
  44. package/dist/package-manager.d.ts.map +1 -0
  45. package/dist/package-manager.js +91 -0
  46. package/dist/package-manager.js.map +1 -0
  47. package/dist/runtime.d.ts +119 -0
  48. package/dist/runtime.d.ts.map +1 -0
  49. package/dist/runtime.js +441 -0
  50. package/dist/runtime.js.map +1 -0
  51. package/dist/types.d.ts +29 -10
  52. package/dist/types.d.ts.map +1 -1
  53. package/package.json +4 -1
  54. package/src/__tests__/capabilities.test.ts +72 -0
  55. package/src/__tests__/config.test.ts +22 -120
  56. package/src/__tests__/extension-loader.test.ts +84 -0
  57. package/src/__tests__/extension-runner.test.ts +228 -0
  58. package/src/__tests__/fixtures/extensions/ctx-aware.js +26 -0
  59. package/src/__tests__/fixtures/extensions/security-gate.js +20 -0
  60. package/src/__tests__/fixtures/extensions/session-analytics.js +28 -0
  61. package/src/__tests__/fixtures/extensions/smart-context.js +10 -0
  62. package/src/__tests__/format.test.ts +207 -2
  63. package/src/__tests__/runtime.test.ts +141 -0
  64. package/src/boot-cache.ts +104 -0
  65. package/src/capabilities.ts +49 -0
  66. package/src/config.ts +131 -133
  67. package/src/extension-api.ts +99 -0
  68. package/src/extension-loader.ts +127 -0
  69. package/src/extension-runner.ts +297 -0
  70. package/src/extension-types.ts +416 -0
  71. package/src/format.spec.ts +175 -0
  72. package/src/format.test.ts +218 -0
  73. package/src/format.ts +140 -16
  74. package/src/index.ts +68 -30
  75. package/src/logger.ts +1 -1
  76. package/src/package-manager.ts +119 -0
  77. package/src/runtime.ts +562 -0
  78. package/src/types.ts +36 -14
  79. package/tsconfig.json +1 -1
  80. package/tsconfig.tsbuildinfo +1 -1
  81. package/.ai/memory/2026-03-04.md +0 -2
  82. package/.ai/tasks.json +0 -7
  83. package/dist/__tests__/config.test.d.ts +0 -2
  84. package/dist/__tests__/config.test.d.ts.map +0 -1
  85. package/dist/__tests__/config.test.js +0 -128
  86. package/dist/__tests__/config.test.js.map +0 -1
  87. package/dist/__tests__/e2e.test.d.ts +0 -2
  88. package/dist/__tests__/e2e.test.d.ts.map +0 -1
  89. package/dist/__tests__/e2e.test.js +0 -211
  90. package/dist/__tests__/e2e.test.js.map +0 -1
  91. package/dist/__tests__/engine.test.d.ts +0 -2
  92. package/dist/__tests__/engine.test.d.ts.map +0 -1
  93. package/dist/__tests__/engine.test.js +0 -271
  94. package/dist/__tests__/engine.test.js.map +0 -1
  95. package/dist/__tests__/format.test.d.ts +0 -2
  96. package/dist/__tests__/format.test.d.ts.map +0 -1
  97. package/dist/__tests__/format.test.js +0 -200
  98. package/dist/__tests__/format.test.js.map +0 -1
  99. package/dist/__tests__/labels.test.d.ts +0 -2
  100. package/dist/__tests__/labels.test.d.ts.map +0 -1
  101. package/dist/__tests__/labels.test.js +0 -82
  102. package/dist/__tests__/labels.test.js.map +0 -1
  103. package/dist/__tests__/loader.test.d.ts +0 -2
  104. package/dist/__tests__/loader.test.d.ts.map +0 -1
  105. package/dist/__tests__/loader.test.js +0 -161
  106. package/dist/__tests__/loader.test.js.map +0 -1
  107. package/dist/__tests__/logger.test.d.ts +0 -2
  108. package/dist/__tests__/logger.test.d.ts.map +0 -1
  109. package/dist/__tests__/logger.test.js +0 -95
  110. package/dist/__tests__/logger.test.js.map +0 -1
  111. package/dist/__tests__/nodes.test.d.ts +0 -2
  112. package/dist/__tests__/nodes.test.d.ts.map +0 -1
  113. package/dist/__tests__/nodes.test.js +0 -83
  114. package/dist/__tests__/nodes.test.js.map +0 -1
  115. package/dist/contracts.d.ts +0 -56
  116. package/dist/contracts.d.ts.map +0 -1
  117. package/dist/contracts.js +0 -2
  118. package/dist/contracts.js.map +0 -1
  119. package/dist/engine.d.ts +0 -38
  120. package/dist/engine.d.ts.map +0 -1
  121. package/dist/engine.js +0 -88
  122. package/dist/engine.js.map +0 -1
  123. package/dist/loader.d.ts +0 -26
  124. package/dist/loader.d.ts.map +0 -1
  125. package/dist/loader.js +0 -120
  126. package/dist/loader.js.map +0 -1
  127. package/src/__tests__/e2e.test.ts +0 -257
  128. package/src/__tests__/engine.test.ts +0 -305
  129. package/src/__tests__/loader.test.ts +0 -191
  130. package/src/contracts.ts +0 -71
  131. package/src/engine.ts +0 -145
  132. package/src/loader.ts +0 -152
@@ -0,0 +1,297 @@
1
+ import type { Logger } from './logger.js';
2
+ import type {
3
+ LoadedExtension,
4
+ ToolDefinition,
5
+ ExtensionTier,
6
+ ToolCallEvent,
7
+ ToolCallResult,
8
+ ExtensionDiagnostic,
9
+ ExtensionContext,
10
+ Section,
11
+ ContextEnrichResult,
12
+ CollectedSections,
13
+ CommandDefinition,
14
+ InputResult,
15
+ } from './extension-types.js';
16
+
17
+ /**
18
+ * Simple event bus for inter-extension communication.
19
+ * In-memory only, no persistence.
20
+ */
21
+ export class EventBus {
22
+ private listeners = new Map<string, Array<(...args: unknown[]) => void>>();
23
+
24
+ on(event: string, handler: (...args: unknown[]) => void): void {
25
+ if (!this.listeners.has(event)) {
26
+ this.listeners.set(event, []);
27
+ }
28
+ this.listeners.get(event)!.push(handler);
29
+ }
30
+
31
+ off(event: string, handler: (...args: unknown[]) => void): void {
32
+ const handlers = this.listeners.get(event);
33
+ if (!handlers) return;
34
+ const idx = handlers.indexOf(handler);
35
+ if (idx >= 0) handlers.splice(idx, 1);
36
+ }
37
+
38
+ emit(event: string, ...args: unknown[]): void {
39
+ const handlers = this.listeners.get(event);
40
+ if (!handlers) return;
41
+ for (const handler of handlers) {
42
+ try {
43
+ handler(...args);
44
+ } catch {
45
+ // Silently ignore errors in event bus handlers
46
+ }
47
+ }
48
+ }
49
+ }
50
+
51
+ /**
52
+ * ExtensionRunner — fires events and collects results from loaded extensions.
53
+ */
54
+ export class ExtensionRunner {
55
+ private extensions: LoadedExtension[];
56
+ private logger?: Logger;
57
+
58
+ constructor(extensions: LoadedExtension[], logger?: Logger) {
59
+ this.extensions = extensions;
60
+ this.logger = logger;
61
+ }
62
+
63
+ /**
64
+ * Fire an event and collect results from all extensions.
65
+ * ctx is passed as second argument to handlers (like Pi's ctx pattern).
66
+ * Errors in individual handlers are caught and logged — never thrown.
67
+ */
68
+ async fire<T>(event: string, data?: unknown, ctx?: ExtensionContext): Promise<T[]> {
69
+ const results: T[] = [];
70
+
71
+ for (const ext of this.extensions) {
72
+ const handlers = ext.handlers.get(event);
73
+ if (!handlers) continue;
74
+
75
+ for (const handler of handlers) {
76
+ try {
77
+ const result = await (handler as (data?: unknown, ctx?: ExtensionContext) => Promise<T | void>)(data, ctx);
78
+ if (result !== undefined && result !== null) {
79
+ results.push(result);
80
+ }
81
+ } catch (err) {
82
+ this.logHandlerError(ext.path, event, err);
83
+ }
84
+ }
85
+ }
86
+
87
+ return results;
88
+ }
89
+
90
+ /**
91
+ * Fire a tool_call event, stop at first blocking result.
92
+ * Returns the blocking result, or null if all allow.
93
+ */
94
+ async fireUntilBlocked(event: 'tool_call', data: ToolCallEvent, ctx?: ExtensionContext): Promise<ToolCallResult | null> {
95
+ for (const ext of this.extensions) {
96
+ const handlers = ext.handlers.get(event);
97
+ if (!handlers) continue;
98
+
99
+ for (const handler of handlers) {
100
+ try {
101
+ const result = await (handler as (data: ToolCallEvent, ctx?: ExtensionContext) => Promise<ToolCallResult | void>)(data, ctx);
102
+ if (result?.decision === 'block') {
103
+ return result;
104
+ }
105
+ } catch (err) {
106
+ this.logHandlerError(ext.path, event, err);
107
+ }
108
+ }
109
+ }
110
+
111
+ return null;
112
+ }
113
+
114
+ // ── v6 Emission Patterns ──
115
+
116
+ /**
117
+ * Fire an event and collect sections from all handlers.
118
+ * Used for `context_enrich` — each handler returns sections and/or systemPrompt fragments.
119
+ * All sections are flattened into a single array; all systemPrompt strings are concatenated.
120
+ */
121
+ async fireCollectSections(event: string, data?: unknown, ctx?: ExtensionContext): Promise<CollectedSections> {
122
+ const sectionMap = new Map<string, Section>();
123
+ const anonymousSections: Section[] = [];
124
+ const systemPromptParts: string[] = [];
125
+
126
+ for (const ext of this.extensions) {
127
+ const handlers = ext.handlers.get(event);
128
+ if (!handlers) continue;
129
+
130
+ for (const handler of handlers) {
131
+ try {
132
+ const result = await (handler as (data?: unknown, ctx?: ExtensionContext) => Promise<ContextEnrichResult | void>)(data, ctx);
133
+ if (result === undefined || result === null) continue;
134
+
135
+ if (result.sections && Array.isArray(result.sections)) {
136
+ for (const section of result.sections) {
137
+ // Sections with the same id are overridden (last-wins)
138
+ if (section.id) {
139
+ sectionMap.set(section.id, section);
140
+ } else {
141
+ anonymousSections.push(section);
142
+ }
143
+ }
144
+ }
145
+ if (result.systemPrompt && typeof result.systemPrompt === 'string') {
146
+ systemPromptParts.push(result.systemPrompt);
147
+ }
148
+ } catch (err) {
149
+ this.logHandlerError(ext.path, event, err);
150
+ }
151
+ }
152
+ }
153
+
154
+ return {
155
+ sections: [...sectionMap.values(), ...anonymousSections],
156
+ systemPrompt: systemPromptParts.join('\n'),
157
+ };
158
+ }
159
+
160
+ /**
161
+ * Fire an event and return the first non-null/undefined result.
162
+ * Used for `route` — stops at the first handler that returns a value.
163
+ * Returns null if no handler returns a result.
164
+ */
165
+ async fireFirstResult<T>(event: string, data?: unknown, ctx?: ExtensionContext): Promise<T | null> {
166
+ for (const ext of this.extensions) {
167
+ const handlers = ext.handlers.get(event);
168
+ if (!handlers) continue;
169
+
170
+ for (const handler of handlers) {
171
+ try {
172
+ const result = await (handler as (data?: unknown, ctx?: ExtensionContext) => Promise<T | void>)(data, ctx);
173
+ if (result !== undefined && result !== null) {
174
+ return result;
175
+ }
176
+ } catch (err) {
177
+ this.logHandlerError(ext.path, event, err);
178
+ }
179
+ }
180
+ }
181
+
182
+ return null;
183
+ }
184
+
185
+ /**
186
+ * Fire an event as a transform chain — each handler receives the previous handler's output.
187
+ * Used for `label_extract`, `input`, `context_modify`.
188
+ *
189
+ * - Initial value is `data`.
190
+ * - If a handler returns undefined/null, the previous value is kept (no-op).
191
+ * - For `input` events: if the result has `consumed: true`, short-circuits and returns immediately.
192
+ */
193
+ async fireChainTransform<T>(event: string, data: T, ctx?: ExtensionContext): Promise<T> {
194
+ let current: T = data;
195
+
196
+ for (const ext of this.extensions) {
197
+ const handlers = ext.handlers.get(event);
198
+ if (!handlers) continue;
199
+
200
+ for (const handler of handlers) {
201
+ try {
202
+ const result = await (handler as (data: T, ctx?: ExtensionContext) => Promise<T | void>)(current, ctx);
203
+ if (result !== undefined && result !== null) {
204
+ current = result;
205
+
206
+ // Short-circuit for input events when consumed
207
+ if (event === 'input' && (result as unknown as InputResult).consumed === true) {
208
+ return current;
209
+ }
210
+ }
211
+ } catch (err) {
212
+ this.logHandlerError(ext.path, event, err);
213
+ }
214
+ }
215
+ }
216
+
217
+ return current;
218
+ }
219
+
220
+ // ── Accessors ──
221
+
222
+ /** Get all registered tools across extensions (last-wins for overrides) */
223
+ get tools(): ToolDefinition[] {
224
+ const toolMap = new Map<string, ToolDefinition>();
225
+
226
+ for (const ext of this.extensions) {
227
+ for (const [name, tool] of ext.tools) {
228
+ if (toolMap.has(name)) {
229
+ this.logger?.log({
230
+ timestamp: new Date().toISOString(),
231
+ level: 'info',
232
+ phase: 'runtime',
233
+ event: 'tool_override',
234
+ data: { tool: name, extension: ext.path },
235
+ });
236
+ }
237
+ toolMap.set(name, tool);
238
+ }
239
+ }
240
+
241
+ return Array.from(toolMap.values());
242
+ }
243
+
244
+ /** Get all registered commands across extensions (last-wins for overrides) */
245
+ get commands(): CommandDefinition[] {
246
+ const cmdMap = new Map<string, CommandDefinition>();
247
+
248
+ for (const ext of this.extensions) {
249
+ if (!ext.commands) continue;
250
+ for (const [name, cmd] of ext.commands) {
251
+ cmdMap.set(name, cmd);
252
+ }
253
+ }
254
+
255
+ return Array.from(cmdMap.values());
256
+ }
257
+
258
+ /** Get diagnostic info */
259
+ get diagnostics(): ExtensionDiagnostic[] {
260
+ return this.extensions.map(ext => ({
261
+ path: ext.path,
262
+ handlerCounts: Object.fromEntries(
263
+ Array.from(ext.handlers.entries()).map(([k, v]) => [k, v.length]),
264
+ ),
265
+ toolNames: Array.from(ext.tools.keys()),
266
+ commandNames: ext.commands ? Array.from(ext.commands.keys()) : [],
267
+ tiers: Array.from(ext.tiers),
268
+ }));
269
+ }
270
+
271
+ /** Which tiers are used by loaded extensions */
272
+ get usedTiers(): Set<ExtensionTier> {
273
+ const tiers = new Set<ExtensionTier>();
274
+ for (const ext of this.extensions) {
275
+ for (const tier of ext.tiers) {
276
+ tiers.add(tier);
277
+ }
278
+ }
279
+ return tiers;
280
+ }
281
+
282
+ // ── Private Helpers ──
283
+
284
+ private logHandlerError(extPath: string, event: string, err: unknown): void {
285
+ this.logger?.log({
286
+ timestamp: new Date().toISOString(),
287
+ level: 'warn',
288
+ phase: 'runtime',
289
+ event: 'extension_handler_error',
290
+ data: {
291
+ extension: extPath,
292
+ eventName: event,
293
+ error: err instanceof Error ? err.message : String(err),
294
+ },
295
+ });
296
+ }
297
+ }