@animalabs/agent-framework 0.5.6 → 0.6.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 (177) hide show
  1. package/dist/src/agent.d.ts +20 -4
  2. package/dist/src/agent.d.ts.map +1 -1
  3. package/dist/src/agent.js +45 -14
  4. package/dist/src/agent.js.map +1 -1
  5. package/dist/src/framework.d.ts +220 -5
  6. package/dist/src/framework.d.ts.map +1 -1
  7. package/dist/src/framework.js +1489 -69
  8. package/dist/src/framework.js.map +1 -1
  9. package/dist/src/gate/event-gate.d.ts +71 -1
  10. package/dist/src/gate/event-gate.d.ts.map +1 -1
  11. package/dist/src/gate/event-gate.js +292 -9
  12. package/dist/src/gate/event-gate.js.map +1 -1
  13. package/dist/src/gate/gate-script-worker.d.ts +2 -0
  14. package/dist/src/gate/gate-script-worker.d.ts.map +1 -0
  15. package/dist/src/gate/gate-script-worker.js +69 -0
  16. package/dist/src/gate/gate-script-worker.js.map +1 -0
  17. package/dist/src/gate/gate-script.d.ts +44 -0
  18. package/dist/src/gate/gate-script.d.ts.map +1 -0
  19. package/dist/src/gate/gate-script.js +164 -0
  20. package/dist/src/gate/gate-script.js.map +1 -0
  21. package/dist/src/gate/types.d.ts +35 -3
  22. package/dist/src/gate/types.d.ts.map +1 -1
  23. package/dist/src/index.d.ts +2 -0
  24. package/dist/src/index.d.ts.map +1 -1
  25. package/dist/src/index.js +2 -0
  26. package/dist/src/index.js.map +1 -1
  27. package/dist/src/mcpl/channel-registry.d.ts +93 -1
  28. package/dist/src/mcpl/channel-registry.d.ts.map +1 -1
  29. package/dist/src/mcpl/channel-registry.js +260 -6
  30. package/dist/src/mcpl/channel-registry.js.map +1 -1
  31. package/dist/src/mcpl/checkpoint-manager.d.ts +7 -0
  32. package/dist/src/mcpl/checkpoint-manager.d.ts.map +1 -1
  33. package/dist/src/mcpl/checkpoint-manager.js +15 -0
  34. package/dist/src/mcpl/checkpoint-manager.js.map +1 -1
  35. package/dist/src/mcpl/conversation-router.d.ts +161 -0
  36. package/dist/src/mcpl/conversation-router.d.ts.map +1 -0
  37. package/dist/src/mcpl/conversation-router.js +179 -0
  38. package/dist/src/mcpl/conversation-router.js.map +1 -0
  39. package/dist/src/mcpl/feature-set-manager.d.ts.map +1 -1
  40. package/dist/src/mcpl/feature-set-manager.js +10 -1
  41. package/dist/src/mcpl/feature-set-manager.js.map +1 -1
  42. package/dist/src/mcpl/index.d.ts +2 -0
  43. package/dist/src/mcpl/index.d.ts.map +1 -1
  44. package/dist/src/mcpl/index.js +2 -0
  45. package/dist/src/mcpl/index.js.map +1 -1
  46. package/dist/src/mcpl/push-handler.d.ts.map +1 -1
  47. package/dist/src/mcpl/push-handler.js +1 -0
  48. package/dist/src/mcpl/push-handler.js.map +1 -1
  49. package/dist/src/mcpl/server-connection.d.ts +35 -30
  50. package/dist/src/mcpl/server-connection.d.ts.map +1 -1
  51. package/dist/src/mcpl/server-connection.js +149 -206
  52. package/dist/src/mcpl/server-connection.js.map +1 -1
  53. package/dist/src/mcpl/transport.d.ts +98 -0
  54. package/dist/src/mcpl/transport.d.ts.map +1 -0
  55. package/dist/src/mcpl/transport.js +281 -0
  56. package/dist/src/mcpl/transport.js.map +1 -0
  57. package/dist/src/mcpl/types.d.ts +42 -2
  58. package/dist/src/mcpl/types.d.ts.map +1 -1
  59. package/dist/src/mcpl/types.js.map +1 -1
  60. package/dist/src/module-registry.d.ts +5 -1
  61. package/dist/src/module-registry.d.ts.map +1 -1
  62. package/dist/src/module-registry.js +16 -2
  63. package/dist/src/module-registry.js.map +1 -1
  64. package/dist/src/modules/discord/index.d.ts +10 -0
  65. package/dist/src/modules/discord/index.d.ts.map +1 -1
  66. package/dist/src/modules/discord/index.js +83 -0
  67. package/dist/src/modules/discord/index.js.map +1 -1
  68. package/dist/src/prose-segments.d.ts +18 -0
  69. package/dist/src/prose-segments.d.ts.map +1 -0
  70. package/dist/src/prose-segments.js +39 -0
  71. package/dist/src/prose-segments.js.map +1 -0
  72. package/dist/src/runtime/liveness-watchdog-worker.d.ts +2 -0
  73. package/dist/src/runtime/liveness-watchdog-worker.d.ts.map +1 -0
  74. package/dist/src/runtime/liveness-watchdog-worker.js +57 -0
  75. package/dist/src/runtime/liveness-watchdog-worker.js.map +1 -0
  76. package/dist/src/runtime/liveness-watchdog.d.ts +36 -0
  77. package/dist/src/runtime/liveness-watchdog.d.ts.map +1 -0
  78. package/dist/src/runtime/liveness-watchdog.js +106 -0
  79. package/dist/src/runtime/liveness-watchdog.js.map +1 -0
  80. package/dist/src/tool-result-history.d.ts +17 -0
  81. package/dist/src/tool-result-history.d.ts.map +1 -0
  82. package/dist/src/tool-result-history.js +60 -0
  83. package/dist/src/tool-result-history.js.map +1 -0
  84. package/dist/src/types/agent.d.ts +21 -0
  85. package/dist/src/types/agent.d.ts.map +1 -1
  86. package/dist/src/types/framework.d.ts +22 -0
  87. package/dist/src/types/framework.d.ts.map +1 -1
  88. package/dist/src/types/module.d.ts +27 -0
  89. package/dist/src/types/module.d.ts.map +1 -1
  90. package/dist/src/types/trace.d.ts +28 -0
  91. package/dist/src/types/trace.d.ts.map +1 -1
  92. package/dist/test/channel-registry-routing.test.d.ts +2 -0
  93. package/dist/test/channel-registry-routing.test.d.ts.map +1 -0
  94. package/dist/test/channel-registry-routing.test.js +182 -0
  95. package/dist/test/channel-registry-routing.test.js.map +1 -0
  96. package/dist/test/checkpoint-attribution.test.d.ts +2 -0
  97. package/dist/test/checkpoint-attribution.test.d.ts.map +1 -0
  98. package/dist/test/checkpoint-attribution.test.js +40 -0
  99. package/dist/test/checkpoint-attribution.test.js.map +1 -0
  100. package/dist/test/conversation-router.test.d.ts +2 -0
  101. package/dist/test/conversation-router.test.d.ts.map +1 -0
  102. package/dist/test/conversation-router.test.js +160 -0
  103. package/dist/test/conversation-router.test.js.map +1 -0
  104. package/dist/test/conversation-routing.test.d.ts +7 -0
  105. package/dist/test/conversation-routing.test.d.ts.map +1 -0
  106. package/dist/test/conversation-routing.test.js +203 -0
  107. package/dist/test/conversation-routing.test.js.map +1 -0
  108. package/dist/test/event-gate-policy-mutation.test.d.ts +2 -0
  109. package/dist/test/event-gate-policy-mutation.test.d.ts.map +1 -0
  110. package/dist/test/event-gate-policy-mutation.test.js +137 -0
  111. package/dist/test/event-gate-policy-mutation.test.js.map +1 -0
  112. package/dist/test/event-gate-script.test.d.ts +2 -0
  113. package/dist/test/event-gate-script.test.d.ts.map +1 -0
  114. package/dist/test/event-gate-script.test.js +95 -0
  115. package/dist/test/event-gate-script.test.js.map +1 -0
  116. package/dist/test/event-gate-tags.test.d.ts +2 -0
  117. package/dist/test/event-gate-tags.test.d.ts.map +1 -0
  118. package/dist/test/event-gate-tags.test.js +76 -0
  119. package/dist/test/event-gate-tags.test.js.map +1 -0
  120. package/dist/test/framework.test.js +73 -1
  121. package/dist/test/framework.test.js.map +1 -1
  122. package/dist/test/helpers/mock-membrane.d.ts +41 -0
  123. package/dist/test/helpers/mock-membrane.d.ts.map +1 -0
  124. package/dist/test/helpers/mock-membrane.js +161 -0
  125. package/dist/test/helpers/mock-membrane.js.map +1 -0
  126. package/dist/test/inference-failure-observability.test.d.ts +2 -0
  127. package/dist/test/inference-failure-observability.test.d.ts.map +1 -0
  128. package/dist/test/inference-failure-observability.test.js +75 -0
  129. package/dist/test/inference-failure-observability.test.js.map +1 -0
  130. package/dist/test/liveness-watchdog.test.d.ts +2 -0
  131. package/dist/test/liveness-watchdog.test.d.ts.map +1 -0
  132. package/dist/test/liveness-watchdog.test.js +78 -0
  133. package/dist/test/liveness-watchdog.test.js.map +1 -0
  134. package/dist/test/mcpl-ws-transport.test.d.ts +2 -0
  135. package/dist/test/mcpl-ws-transport.test.d.ts.map +1 -0
  136. package/dist/test/mcpl-ws-transport.test.js +155 -0
  137. package/dist/test/mcpl-ws-transport.test.js.map +1 -0
  138. package/dist/test/prose-segments.test.d.ts +2 -0
  139. package/dist/test/prose-segments.test.d.ts.map +1 -0
  140. package/dist/test/prose-segments.test.js +52 -0
  141. package/dist/test/prose-segments.test.js.map +1 -0
  142. package/dist/test/tool-result-history.test.d.ts +7 -0
  143. package/dist/test/tool-result-history.test.d.ts.map +1 -0
  144. package/dist/test/tool-result-history.test.js +77 -0
  145. package/dist/test/tool-result-history.test.js.map +1 -0
  146. package/dist/test/trunk-channel-routing.test.d.ts +19 -0
  147. package/dist/test/trunk-channel-routing.test.d.ts.map +1 -0
  148. package/dist/test/trunk-channel-routing.test.js +175 -0
  149. package/dist/test/trunk-channel-routing.test.js.map +1 -0
  150. package/dist/tsconfig.tsbuildinfo +1 -1
  151. package/package.json +3 -3
  152. package/src/agent.ts +53 -18
  153. package/src/framework.ts +1685 -71
  154. package/src/gate/event-gate.ts +297 -9
  155. package/src/gate/gate-script-worker.ts +73 -0
  156. package/src/gate/gate-script.ts +180 -0
  157. package/src/gate/types.ts +36 -3
  158. package/src/index.ts +12 -0
  159. package/src/mcpl/channel-registry.ts +331 -6
  160. package/src/mcpl/checkpoint-manager.ts +16 -0
  161. package/src/mcpl/conversation-router.ts +284 -0
  162. package/src/mcpl/feature-set-manager.ts +14 -1
  163. package/src/mcpl/index.ts +12 -0
  164. package/src/mcpl/push-handler.ts +1 -0
  165. package/src/mcpl/server-connection.ts +157 -224
  166. package/src/mcpl/transport.ts +307 -0
  167. package/src/mcpl/types.ts +49 -2
  168. package/src/module-registry.ts +25 -2
  169. package/src/modules/discord/index.ts +93 -0
  170. package/src/prose-segments.ts +42 -0
  171. package/src/runtime/liveness-watchdog-worker.ts +66 -0
  172. package/src/runtime/liveness-watchdog.ts +122 -0
  173. package/src/tool-result-history.ts +57 -0
  174. package/src/types/agent.ts +21 -0
  175. package/src/types/framework.ts +24 -0
  176. package/src/types/module.ts +29 -0
  177. package/src/types/trace.ts +36 -0
@@ -0,0 +1,180 @@
1
+ /**
2
+ * GateScript — runs an agent-authored `gate.js` decision function on a worker
3
+ * thread, synchronously from the gate's perspective, with a hard timeout.
4
+ *
5
+ * The agent is trusted (shell + fs already), so this is a robustness seatbelt,
6
+ * not a security sandbox: a script that throws falls through to the declarative
7
+ * policies (and the error is surfaced via status()); a script that hangs is
8
+ * timed out (Atomics.wait), the wedged worker is terminated and respawned, and
9
+ * the event falls through — so a bad rule can't brick the agent's own attention.
10
+ *
11
+ * Sync model: the main thread posts the event then blocks in Atomics.wait until
12
+ * the worker writes the result into shared memory (or the timeout elapses).
13
+ * Node permits Atomics.wait on the main thread, so evaluate() stays synchronous
14
+ * and the rest of the EventGate is unchanged.
15
+ */
16
+ import { Worker } from 'node:worker_threads';
17
+ import { existsSync, statSync } from 'node:fs';
18
+ import { fileURLToPath } from 'node:url';
19
+ import { dirname, join } from 'node:path';
20
+ import type { GateBehavior, GateEventInfo } from './types.js';
21
+
22
+ const WORKER_PATH = join(dirname(fileURLToPath(import.meta.url)), 'gate-script-worker.js');
23
+ const SAB_SIZE = 16_384;
24
+ const RELOAD_THROTTLE_MS = 1000;
25
+
26
+ export interface GateScriptStatus {
27
+ active: boolean;
28
+ ready: boolean;
29
+ path: string;
30
+ runs: number;
31
+ errors: number;
32
+ timeouts: number;
33
+ lastError: string | null;
34
+ }
35
+
36
+ interface ScriptResult {
37
+ behavior?: GateBehavior | null;
38
+ error?: string;
39
+ }
40
+
41
+ export class GateScript {
42
+ private worker?: Worker;
43
+ private readonly sab = new SharedArrayBuffer(SAB_SIZE);
44
+ private readonly ctrl = new Int32Array(this.sab, 0, 2);
45
+ private readonly data = new Uint8Array(this.sab, 8);
46
+ private readonly dec = new TextDecoder();
47
+ private mtime = 0;
48
+ private lastCheck = 0;
49
+ private active = false;
50
+ private ready = false;
51
+ private lastError: string | null = null;
52
+ private runs = 0;
53
+ private errors = 0;
54
+ private timeouts = 0;
55
+
56
+ constructor(
57
+ private readonly scriptPath: string,
58
+ private readonly timeoutMs = 50,
59
+ private readonly now: () => number = Date.now,
60
+ ) {
61
+ this.reloadIfChanged();
62
+ }
63
+
64
+ /** Hot-reload: (re)spawn the worker when gate.js appears or changes; stop it
65
+ * when the file is removed. Throttled like the gate's config reload. */
66
+ private reloadIfChanged(): void {
67
+ const t = this.now();
68
+ if (this.lastCheck !== 0 && t - this.lastCheck < RELOAD_THROTTLE_MS) return;
69
+ this.lastCheck = t;
70
+ if (!existsSync(this.scriptPath)) {
71
+ if (this.active) this.stop();
72
+ return;
73
+ }
74
+ let m = 0;
75
+ try {
76
+ m = statSync(this.scriptPath).mtimeMs;
77
+ } catch {
78
+ return;
79
+ }
80
+ if (this.active && m === this.mtime) return;
81
+ this.mtime = m;
82
+ this.spawn();
83
+ }
84
+
85
+ private spawn(): void {
86
+ this.stop();
87
+ try {
88
+ const w = new Worker(WORKER_PATH, {
89
+ workerData: { scriptPath: this.scriptPath, sab: this.sab },
90
+ });
91
+ w.on('message', (m: { type?: string; error?: string | null }) => {
92
+ if (m?.type === 'ready') {
93
+ this.ready = true; // load finished (with or without a load error)
94
+ if (m.error) this.lastError = m.error;
95
+ }
96
+ });
97
+ w.on('error', (e) => {
98
+ this.lastError = e.message;
99
+ });
100
+ w.unref(); // never keep the process alive on the gate script's account
101
+ this.worker = w;
102
+ this.active = true;
103
+ this.lastError = null;
104
+ } catch (e) {
105
+ this.lastError = (e as Error).message;
106
+ this.active = false;
107
+ }
108
+ }
109
+
110
+ private stop(): void {
111
+ if (this.worker) {
112
+ void this.worker.terminate().catch(() => {});
113
+ this.worker = undefined;
114
+ }
115
+ this.active = false;
116
+ this.ready = false;
117
+ }
118
+
119
+ /**
120
+ * Evaluate the script for one event.
121
+ * - returns a GateBehavior → the script decided
122
+ * - returns null → fall through to the declarative policies
123
+ * - returns undefined → script inactive / errored / timed out (fall through)
124
+ */
125
+ evaluate(event: GateEventInfo): GateBehavior | null | undefined {
126
+ this.reloadIfChanged();
127
+ // Inactive, or the worker hasn't finished importing gate.js yet → fall
128
+ // through (don't time-out/respawn during a cold start).
129
+ if (!this.active || !this.worker || !this.ready) return undefined;
130
+
131
+ Atomics.store(this.ctrl, 0, 0);
132
+ try {
133
+ this.worker.postMessage({ type: 'eval', event });
134
+ } catch (e) {
135
+ this.lastError = (e as Error).message;
136
+ return undefined;
137
+ }
138
+
139
+ const r = Atomics.wait(this.ctrl, 0, 0, this.timeoutMs);
140
+ this.runs++;
141
+ if (r === 'timed-out') {
142
+ this.timeouts++;
143
+ this.lastError = `gate.js timed out (>${this.timeoutMs}ms) — terminated and restarted`;
144
+ this.spawn(); // kill the wedged worker, fresh one for next time
145
+ return undefined;
146
+ }
147
+
148
+ const len = Atomics.load(this.ctrl, 1);
149
+ let parsed: ScriptResult;
150
+ try {
151
+ parsed = JSON.parse(this.dec.decode(this.data.subarray(0, len))) as ScriptResult;
152
+ } catch {
153
+ this.errors++;
154
+ this.lastError = 'gate.js produced an unreadable result';
155
+ return undefined;
156
+ }
157
+ if (parsed.error) {
158
+ this.errors++;
159
+ this.lastError = parsed.error;
160
+ return undefined;
161
+ }
162
+ return parsed.behavior ?? null;
163
+ }
164
+
165
+ status(): GateScriptStatus {
166
+ return {
167
+ active: this.active,
168
+ ready: this.ready,
169
+ path: this.scriptPath,
170
+ runs: this.runs,
171
+ errors: this.errors,
172
+ timeouts: this.timeouts,
173
+ lastError: this.lastError,
174
+ };
175
+ }
176
+
177
+ dispose(): void {
178
+ this.stop();
179
+ }
180
+ }
package/src/gate/types.ts CHANGED
@@ -52,7 +52,8 @@ export interface PassiveSampleConfig {
52
52
  /** How a matching policy affects inference triggering. */
53
53
  export type GateBehavior =
54
54
  | 'always' // Trigger inference immediately
55
- | 'skip' // Don't trigger inference (event still enters context)
55
+ | 'defer' // Don't trigger inference (event still enters context). Preferred name.
56
+ | 'skip' // Legacy alias for 'defer' (still accepted).
56
57
  | { debounce: number } // Batch events per-policy, deliver after delay (ms)
57
58
  | { rate_limit: RateLimitConfig } // Token bucket per `keyBy`; deny when empty
58
59
  | { passive_sample: PassiveSampleConfig }; // Fire every Nth match
@@ -88,6 +89,17 @@ export interface GatePolicyMatch {
88
89
  * matches if any of those is set. Empty/omitted = no metadata constraint.
89
90
  */
90
91
  metadataTrue?: string[];
92
+ /**
93
+ * Tag matching (MCPL RFC-001 event tags). Globs allowed (e.g. `robotics:*`).
94
+ * Evaluated against the event's (implication-expanded) tag set:
95
+ * tagsAny — match if ANY listed pattern matches an event tag
96
+ * tagsAll — match only if EVERY listed pattern matches some event tag
97
+ * tagsNone — match only if NO listed pattern matches any event tag
98
+ * Combined with the other fields by AND, like the rest of the match.
99
+ */
100
+ tagsAny?: string[];
101
+ tagsAll?: string[];
102
+ tagsNone?: string[];
91
103
  }
92
104
 
93
105
  // ---------------------------------------------------------------------------
@@ -116,7 +128,7 @@ export interface GatePolicy {
116
128
  export interface GateConfig {
117
129
  policies: GatePolicy[];
118
130
  /** Behavior when no policy matches. Default: 'always'. */
119
- default?: 'always' | 'skip';
131
+ default?: 'always' | 'defer' | 'skip';
120
132
  }
121
133
 
122
134
  // ---------------------------------------------------------------------------
@@ -129,6 +141,13 @@ export interface GateOptions {
129
141
  configPath?: string;
130
142
  /** Initial config, seeded to gate.json if the file doesn't exist. */
131
143
  config?: GateConfig;
144
+ /**
145
+ * Path to a JSON file listing user ids that may wake the agent even while
146
+ * it is asleep (`sleep` tool). The file is either a bare array of id
147
+ * strings or `{ "userIds": [...] }`. Hot-reloaded on change. When unset or
148
+ * missing, no one bypasses sleep.
149
+ */
150
+ privilegedUsersPath?: string;
132
151
  }
133
152
 
134
153
  // ---------------------------------------------------------------------------
@@ -156,6 +175,8 @@ export interface GateEventInfo {
156
175
  mount?: string;
157
176
  /** Mount-prefixed paths touched (for workspace:* events). */
158
177
  paths?: string[];
178
+ /** Event tags (MCPL RFC-001), already implication-expanded by the host. */
179
+ tags?: string[];
159
180
  }
160
181
 
161
182
  /** Per-policy runtime statistics (for gate:status). */
@@ -185,7 +206,9 @@ export interface GateStatus {
185
206
  configPath: string;
186
207
  configSource: 'file' | 'initial' | 'default';
187
208
  lastReloadTimestamp: number | null;
188
- default: 'always' | 'skip';
209
+ default: 'always' | 'defer' | 'skip';
210
+ /** Status of the optional programmable gate (gate.js). */
211
+ script: import('./gate-script.js').GateScriptStatus;
189
212
  policies: GatePolicyStats[];
190
213
  errors: string[];
191
214
  /** Total events the gate has evaluated since startup. */
@@ -202,4 +225,14 @@ export interface GateStatus {
202
225
  skipped: number;
203
226
  byEventType: Record<string, { triggered: number; skipped: number }>;
204
227
  };
228
+ /** Present when the agent is currently asleep (sleep tool). */
229
+ sleep?: {
230
+ until: number;
231
+ remainingMs: number;
232
+ note?: string;
233
+ /** How many wake events have been suppressed during the current sleep. */
234
+ suppressed: number;
235
+ /** Count of privileged user ids that may wake through sleep. */
236
+ privilegedCount: number;
237
+ };
205
238
  }
package/src/index.ts CHANGED
@@ -63,6 +63,18 @@ export type { GateConfig, GateOptions, GatePolicy, GatePolicyMatch, GateBehavior
63
63
  // MCPL channel registry (exposed for modules that need channel-level operations)
64
64
  export { ChannelRegistry } from './mcpl/index.js';
65
65
 
66
+ // Per-channel conversation routing
67
+ export { ConversationRouter, DEFAULT_CLOSURE_PROMPT } from './mcpl/index.js';
68
+ export type {
69
+ ConversationRouterConfig,
70
+ ConversationBinding,
71
+ RoutingDecision,
72
+ IncomingMessageFacts,
73
+ BindRule,
74
+ TriggerRule,
75
+ ChannelKind,
76
+ } from './mcpl/index.js';
77
+
66
78
  // Re-export commonly used types from dependencies
67
79
  export type { ContextManager, ContextStrategy, TokenBudget } from '@animalabs/context-manager';
68
80
  export { PassthroughStrategy, AutobiographicalStrategy, KnowledgeStrategy } from '@animalabs/context-manager';