@mast-ai/core 0.2.0 → 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/dist/tool.d.ts CHANGED
@@ -45,9 +45,27 @@ export interface ToolProvider {
45
45
  /** Returns the tool registered under `name`, or `undefined` if not found or out of scope. */
46
46
  getTool(name: string): Tool | undefined;
47
47
  }
48
+ /**
49
+ * Events emitted by {@link ToolRegistry} (and {@link ToolRegistryView}) when its
50
+ * contents change at runtime. UI components and adapters can subscribe via
51
+ * `addEventListener` to refresh toolbars, re-advertise capabilities, or trigger
52
+ * a new agent turn.
53
+ */
54
+ export type ToolRegistryEventMap = {
55
+ /** Fired after a tool is successfully added. */
56
+ 'tool-registered': {
57
+ tool: Tool;
58
+ };
59
+ /** Fired after a tool is removed. Not fired for no-op `unregister` calls. */
60
+ 'tool-unregistered': {
61
+ name: string;
62
+ };
63
+ };
64
+ type ToolRegistryListener<K extends keyof ToolRegistryEventMap> = (event: ToolRegistryEventMap[K]) => void;
48
65
  /** Holds all tools available to an {@link AgentRunner} and resolves them by name during execution. */
49
66
  export declare class ToolRegistry implements ToolProvider {
50
67
  private _tools;
68
+ private _emitter;
51
69
  /**
52
70
  * Registers a tool. Throws if a tool with the same name is already registered.
53
71
  * Returns `this` for chaining.
@@ -61,6 +79,10 @@ export declare class ToolRegistry implements ToolProvider {
61
79
  getTools(): ToolDefinition[];
62
80
  /** Returns a live read-only view filtered to tools with `scope: 'read'`. */
63
81
  readOnly(): ToolRegistryView;
82
+ /** Subscribe to registry mutation events. */
83
+ addEventListener<K extends keyof ToolRegistryEventMap>(type: K, listener: ToolRegistryListener<K>): void;
84
+ /** Unsubscribe a previously registered listener. */
85
+ removeEventListener<K extends keyof ToolRegistryEventMap>(type: K, listener: ToolRegistryListener<K>): void;
64
86
  }
65
87
  /**
66
88
  * A live filtered projection of a {@link ToolRegistry}.
@@ -71,7 +93,18 @@ export declare class ToolRegistry implements ToolProvider {
71
93
  export declare class ToolRegistryView implements ToolProvider {
72
94
  private readonly parent;
73
95
  private readonly scope;
96
+ private _emitter;
97
+ private _scopedNames?;
74
98
  constructor(parent: ToolRegistry, scope: 'read' | 'write');
75
99
  getTools(): ToolDefinition[];
76
100
  getTool(name: string): Tool | undefined;
101
+ /**
102
+ * Subscribe to mutation events forwarded from the parent registry. Only
103
+ * events for tools whose `scope` matches this view's scope are delivered.
104
+ */
105
+ addEventListener<K extends keyof ToolRegistryEventMap>(type: K, listener: ToolRegistryListener<K>): void;
106
+ /** Unsubscribe a previously registered listener. */
107
+ removeEventListener<K extends keyof ToolRegistryEventMap>(type: K, listener: ToolRegistryListener<K>): void;
108
+ private ensureParentSubscription;
77
109
  }
110
+ export {};
package/dist/tool.js CHANGED
@@ -1,8 +1,28 @@
1
1
  // Copyright 2026 Andre Cipriani Bandarra
2
2
  // SPDX-License-Identifier: Apache-2.0
3
+ class ToolRegistryEventEmitter {
4
+ registered = new Set();
5
+ unregistered = new Set();
6
+ on(type, listener) {
7
+ this.bucket(type).add(listener);
8
+ }
9
+ off(type, listener) {
10
+ this.bucket(type).delete(listener);
11
+ }
12
+ emit(type, event) {
13
+ for (const listener of [...this.bucket(type)])
14
+ listener(event);
15
+ }
16
+ // The cast is contained here: each generic call resolves `K` to one of the
17
+ // literal keys, and the matching bucket has a compatible listener type.
18
+ bucket(type) {
19
+ return (type === 'tool-registered' ? this.registered : this.unregistered);
20
+ }
21
+ }
3
22
  /** Holds all tools available to an {@link AgentRunner} and resolves them by name during execution. */
4
23
  export class ToolRegistry {
5
24
  _tools = new Map();
25
+ _emitter = new ToolRegistryEventEmitter();
6
26
  /**
7
27
  * Registers a tool. Throws if a tool with the same name is already registered.
8
28
  * Returns `this` for chaining.
@@ -13,11 +33,14 @@ export class ToolRegistry {
13
33
  throw new Error(`Tool '${name}' is already registered.`);
14
34
  }
15
35
  this._tools.set(name, tool);
36
+ this._emitter.emit('tool-registered', { tool });
16
37
  return this;
17
38
  }
18
39
  /** Removes the tool registered under `name`. No-op if not found. */
19
40
  unregister(name) {
20
- this._tools.delete(name);
41
+ if (!this._tools.delete(name))
42
+ return;
43
+ this._emitter.emit('tool-unregistered', { name });
21
44
  }
22
45
  /** Returns the tool registered under `name`, or `undefined` if not found. */
23
46
  getTool(name) {
@@ -31,6 +54,14 @@ export class ToolRegistry {
31
54
  readOnly() {
32
55
  return new ToolRegistryView(this, 'read');
33
56
  }
57
+ /** Subscribe to registry mutation events. */
58
+ addEventListener(type, listener) {
59
+ this._emitter.on(type, listener);
60
+ }
61
+ /** Unsubscribe a previously registered listener. */
62
+ removeEventListener(type, listener) {
63
+ this._emitter.off(type, listener);
64
+ }
34
65
  }
35
66
  /**
36
67
  * A live filtered projection of a {@link ToolRegistry}.
@@ -41,6 +72,8 @@ export class ToolRegistry {
41
72
  export class ToolRegistryView {
42
73
  parent;
43
74
  scope;
75
+ _emitter = new ToolRegistryEventEmitter();
76
+ _scopedNames;
44
77
  constructor(parent, scope) {
45
78
  this.parent = parent;
46
79
  this.scope = scope;
@@ -54,4 +87,38 @@ export class ToolRegistryView {
54
87
  return undefined;
55
88
  return tool.definition().scope === this.scope ? tool : undefined;
56
89
  }
90
+ /**
91
+ * Subscribe to mutation events forwarded from the parent registry. Only
92
+ * events for tools whose `scope` matches this view's scope are delivered.
93
+ */
94
+ addEventListener(type, listener) {
95
+ this.ensureParentSubscription();
96
+ this._emitter.on(type, listener);
97
+ }
98
+ /** Unsubscribe a previously registered listener. */
99
+ removeEventListener(type, listener) {
100
+ this._emitter.off(type, listener);
101
+ }
102
+ ensureParentSubscription() {
103
+ if (this._scopedNames)
104
+ return;
105
+ const scoped = new Set();
106
+ for (const def of this.parent.getTools()) {
107
+ if (def.scope === this.scope)
108
+ scoped.add(def.name);
109
+ }
110
+ this._scopedNames = scoped;
111
+ this.parent.addEventListener('tool-registered', (event) => {
112
+ const def = event.tool.definition();
113
+ if (def.scope !== this.scope)
114
+ return;
115
+ scoped.add(def.name);
116
+ this._emitter.emit('tool-registered', event);
117
+ });
118
+ this.parent.addEventListener('tool-unregistered', (event) => {
119
+ if (!scoped.delete(event.name))
120
+ return;
121
+ this._emitter.emit('tool-unregistered', event);
122
+ });
123
+ }
57
124
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mast-ai/core",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",