@animalabs/agent-framework 0.5.7 → 0.6.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/dist/src/agent.d.ts +4 -0
- package/dist/src/agent.d.ts.map +1 -1
- package/dist/src/agent.js +15 -0
- package/dist/src/agent.js.map +1 -1
- package/dist/src/framework.d.ts +243 -0
- package/dist/src/framework.d.ts.map +1 -1
- package/dist/src/framework.js +1378 -114
- package/dist/src/framework.js.map +1 -1
- package/dist/src/gate/event-gate.d.ts +64 -2
- package/dist/src/gate/event-gate.d.ts.map +1 -1
- package/dist/src/gate/event-gate.js +212 -12
- package/dist/src/gate/event-gate.js.map +1 -1
- package/dist/src/gate/gate-script-worker.d.ts +2 -0
- package/dist/src/gate/gate-script-worker.d.ts.map +1 -0
- package/dist/src/gate/gate-script-worker.js +69 -0
- package/dist/src/gate/gate-script-worker.js.map +1 -0
- package/dist/src/gate/gate-script.d.ts +44 -0
- package/dist/src/gate/gate-script.d.ts.map +1 -0
- package/dist/src/gate/gate-script.js +164 -0
- package/dist/src/gate/gate-script.js.map +1 -0
- package/dist/src/gate/types.d.ts +18 -3
- package/dist/src/gate/types.d.ts.map +1 -1
- package/dist/src/index.d.ts +3 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +4 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/mcpl/channel-registry.d.ts +60 -2
- package/dist/src/mcpl/channel-registry.d.ts.map +1 -1
- package/dist/src/mcpl/channel-registry.js +114 -9
- package/dist/src/mcpl/channel-registry.js.map +1 -1
- package/dist/src/mcpl/checkpoint-manager.d.ts +7 -0
- package/dist/src/mcpl/checkpoint-manager.d.ts.map +1 -1
- package/dist/src/mcpl/checkpoint-manager.js +15 -0
- package/dist/src/mcpl/checkpoint-manager.js.map +1 -1
- package/dist/src/mcpl/push-handler.d.ts.map +1 -1
- package/dist/src/mcpl/push-handler.js +5 -0
- package/dist/src/mcpl/push-handler.js.map +1 -1
- package/dist/src/mcpl/server-connection.d.ts +64 -30
- package/dist/src/mcpl/server-connection.d.ts.map +1 -1
- package/dist/src/mcpl/server-connection.js +247 -210
- package/dist/src/mcpl/server-connection.js.map +1 -1
- package/dist/src/mcpl/transport.d.ts +98 -0
- package/dist/src/mcpl/transport.d.ts.map +1 -0
- package/dist/src/mcpl/transport.js +281 -0
- package/dist/src/mcpl/transport.js.map +1 -0
- package/dist/src/mcpl/types.d.ts +59 -3
- package/dist/src/mcpl/types.d.ts.map +1 -1
- package/dist/src/mcpl/types.js.map +1 -1
- package/dist/src/module-registry.d.ts +17 -1
- package/dist/src/module-registry.d.ts.map +1 -1
- package/dist/src/module-registry.js +101 -6
- package/dist/src/module-registry.js.map +1 -1
- package/dist/src/prose-segments.d.ts +18 -0
- package/dist/src/prose-segments.d.ts.map +1 -0
- package/dist/src/prose-segments.js +39 -0
- package/dist/src/prose-segments.js.map +1 -0
- package/dist/src/runtime/liveness-watchdog-worker.d.ts +2 -0
- package/dist/src/runtime/liveness-watchdog-worker.d.ts.map +1 -0
- package/dist/src/runtime/liveness-watchdog-worker.js +57 -0
- package/dist/src/runtime/liveness-watchdog-worker.js.map +1 -0
- package/dist/src/runtime/liveness-watchdog.d.ts +36 -0
- package/dist/src/runtime/liveness-watchdog.d.ts.map +1 -0
- package/dist/src/runtime/liveness-watchdog.js +106 -0
- package/dist/src/runtime/liveness-watchdog.js.map +1 -0
- package/dist/src/types/agent.d.ts +47 -0
- package/dist/src/types/agent.d.ts.map +1 -1
- package/dist/src/types/framework.d.ts +14 -0
- package/dist/src/types/framework.d.ts.map +1 -1
- package/dist/src/types/module.d.ts +58 -0
- package/dist/src/types/module.d.ts.map +1 -1
- package/dist/src/types/trace.d.ts +42 -0
- package/dist/src/types/trace.d.ts.map +1 -1
- package/dist/test/agent-cache-ttl.test.d.ts +2 -0
- package/dist/test/agent-cache-ttl.test.d.ts.map +1 -0
- package/dist/test/agent-cache-ttl.test.js +25 -0
- package/dist/test/agent-cache-ttl.test.js.map +1 -0
- package/dist/test/channel-registry-routing.test.js +108 -1
- package/dist/test/channel-registry-routing.test.js.map +1 -1
- package/dist/test/checkpoint-attribution.test.d.ts +2 -0
- package/dist/test/checkpoint-attribution.test.d.ts.map +1 -0
- package/dist/test/checkpoint-attribution.test.js +40 -0
- package/dist/test/checkpoint-attribution.test.js.map +1 -0
- package/dist/test/conversation-fork-injection-scoping.test.d.ts +2 -0
- package/dist/test/conversation-fork-injection-scoping.test.d.ts.map +1 -0
- package/dist/test/conversation-fork-injection-scoping.test.js +184 -0
- package/dist/test/conversation-fork-injection-scoping.test.js.map +1 -0
- package/dist/test/event-gate-policy-mutation.test.d.ts +2 -0
- package/dist/test/event-gate-policy-mutation.test.d.ts.map +1 -0
- package/dist/test/event-gate-policy-mutation.test.js +137 -0
- package/dist/test/event-gate-policy-mutation.test.js.map +1 -0
- package/dist/test/event-gate-script.test.d.ts +2 -0
- package/dist/test/event-gate-script.test.d.ts.map +1 -0
- package/dist/test/event-gate-script.test.js +95 -0
- package/dist/test/event-gate-script.test.js.map +1 -0
- package/dist/test/event-gate-tags.test.d.ts +2 -0
- package/dist/test/event-gate-tags.test.d.ts.map +1 -0
- package/dist/test/event-gate-tags.test.js +76 -0
- package/dist/test/event-gate-tags.test.js.map +1 -0
- package/dist/test/gather-context-timeout.test.d.ts +2 -0
- package/dist/test/gather-context-timeout.test.d.ts.map +1 -0
- package/dist/test/gather-context-timeout.test.js +120 -0
- package/dist/test/gather-context-timeout.test.js.map +1 -0
- package/dist/test/inference-exhaustion-rewind.test.d.ts +2 -0
- package/dist/test/inference-exhaustion-rewind.test.d.ts.map +1 -0
- package/dist/test/inference-exhaustion-rewind.test.js +181 -0
- package/dist/test/inference-exhaustion-rewind.test.js.map +1 -0
- package/dist/test/inference-failure-observability.test.js +4 -0
- package/dist/test/inference-failure-observability.test.js.map +1 -1
- package/dist/test/liveness-watchdog.test.d.ts +2 -0
- package/dist/test/liveness-watchdog.test.d.ts.map +1 -0
- package/dist/test/liveness-watchdog.test.js +78 -0
- package/dist/test/liveness-watchdog.test.js.map +1 -0
- package/dist/test/mcpl-reconnect-events.test.d.ts +2 -0
- package/dist/test/mcpl-reconnect-events.test.d.ts.map +1 -0
- package/dist/test/mcpl-reconnect-events.test.js +119 -0
- package/dist/test/mcpl-reconnect-events.test.js.map +1 -0
- package/dist/test/mcpl-reconnect-reregistration.test.d.ts +2 -0
- package/dist/test/mcpl-reconnect-reregistration.test.d.ts.map +1 -0
- package/dist/test/mcpl-reconnect-reregistration.test.js +181 -0
- package/dist/test/mcpl-reconnect-reregistration.test.js.map +1 -0
- package/dist/test/mcpl-request-timeout.test.d.ts +2 -0
- package/dist/test/mcpl-request-timeout.test.d.ts.map +1 -0
- package/dist/test/mcpl-request-timeout.test.js +123 -0
- package/dist/test/mcpl-request-timeout.test.js.map +1 -0
- package/dist/test/mcpl-runtime-lifecycle.test.d.ts +7 -0
- package/dist/test/mcpl-runtime-lifecycle.test.d.ts.map +1 -0
- package/dist/test/mcpl-runtime-lifecycle.test.js +142 -0
- package/dist/test/mcpl-runtime-lifecycle.test.js.map +1 -0
- package/dist/test/mcpl-ws-transport.test.d.ts +2 -0
- package/dist/test/mcpl-ws-transport.test.d.ts.map +1 -0
- package/dist/test/mcpl-ws-transport.test.js +155 -0
- package/dist/test/mcpl-ws-transport.test.js.map +1 -0
- package/dist/test/prose-segments.test.d.ts +2 -0
- package/dist/test/prose-segments.test.d.ts.map +1 -0
- package/dist/test/prose-segments.test.js +52 -0
- package/dist/test/prose-segments.test.js.map +1 -0
- package/dist/test/refusal-rewind.test.d.ts +2 -0
- package/dist/test/refusal-rewind.test.d.ts.map +1 -0
- package/dist/test/refusal-rewind.test.js +108 -0
- package/dist/test/refusal-rewind.test.js.map +1 -0
- package/dist/test/state-scaling-e2e.test.d.ts +22 -0
- package/dist/test/state-scaling-e2e.test.d.ts.map +1 -0
- package/dist/test/state-scaling-e2e.test.js +200 -0
- package/dist/test/state-scaling-e2e.test.js.map +1 -0
- package/dist/test/state-scaling.test.d.ts +12 -0
- package/dist/test/state-scaling.test.d.ts.map +1 -0
- package/dist/test/state-scaling.test.js +243 -0
- package/dist/test/state-scaling.test.js.map +1 -0
- package/dist/test/tool-result-content.test.d.ts +2 -0
- package/dist/test/tool-result-content.test.d.ts.map +1 -0
- package/dist/test/tool-result-content.test.js +37 -0
- package/dist/test/tool-result-content.test.js.map +1 -0
- package/dist/test/trunk-channel-routing.test.d.ts +19 -0
- package/dist/test/trunk-channel-routing.test.d.ts.map +1 -0
- package/dist/test/trunk-channel-routing.test.js +175 -0
- package/dist/test/trunk-channel-routing.test.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +4 -3
- package/src/agent.ts +16 -0
- package/src/framework.ts +1576 -121
- package/src/gate/event-gate.ts +209 -12
- package/src/gate/gate-script-worker.ts +73 -0
- package/src/gate/gate-script.ts +180 -0
- package/src/gate/types.ts +19 -3
- package/src/index.ts +10 -0
- package/src/mcpl/channel-registry.ts +143 -8
- package/src/mcpl/checkpoint-manager.ts +16 -0
- package/src/mcpl/push-handler.ts +5 -0
- package/src/mcpl/server-connection.ts +262 -227
- package/src/mcpl/transport.ts +307 -0
- package/src/mcpl/types.ts +68 -3
- package/src/module-registry.ts +126 -6
- package/src/prose-segments.ts +42 -0
- package/src/runtime/liveness-watchdog-worker.ts +66 -0
- package/src/runtime/liveness-watchdog.ts +122 -0
- package/src/types/agent.ts +48 -0
- package/src/types/framework.ts +15 -0
- package/src/types/module.ts +65 -0
- package/src/types/trace.ts +51 -0
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* inference tokens, not whether the agent should know about the event.
|
|
12
12
|
*/
|
|
13
13
|
import type { ToolDefinition } from '../types/events.js';
|
|
14
|
-
import type { GateConfig, GateDecision, GateEventInfo, GateStatus } from './types.js';
|
|
14
|
+
import type { GateConfig, GatePolicy, GateDecision, GateEventInfo, GateStatus } from './types.js';
|
|
15
15
|
export type { GateConfig, GateOptions, GateDecision, GateEventInfo } from './types.js';
|
|
16
16
|
interface TraceEventLike {
|
|
17
17
|
type: string;
|
|
@@ -20,6 +20,8 @@ interface TraceEventLike {
|
|
|
20
20
|
export declare class EventGate {
|
|
21
21
|
private configPath;
|
|
22
22
|
private config;
|
|
23
|
+
/** Optional agent-authored programmable gate (gate.js). */
|
|
24
|
+
private gateScript;
|
|
23
25
|
private compiledPolicies;
|
|
24
26
|
private configMtime;
|
|
25
27
|
private lastReloadCheck;
|
|
@@ -41,6 +43,8 @@ export declare class EventGate {
|
|
|
41
43
|
private sleepUntil;
|
|
42
44
|
private sleepNote;
|
|
43
45
|
private sleepSuppressed;
|
|
46
|
+
private sleepTimer;
|
|
47
|
+
private sleepAgent;
|
|
44
48
|
private privilegedUsersPath;
|
|
45
49
|
private privilegedUserIds;
|
|
46
50
|
private privilegedMtime;
|
|
@@ -65,10 +69,51 @@ export declare class EventGate {
|
|
|
65
69
|
getAgentNames: () => string[];
|
|
66
70
|
/** Optional clock — defaults to Date.now. Tests inject for deterministic time. */
|
|
67
71
|
now?: () => number;
|
|
72
|
+
/** Per-event timeout (ms) for the optional gate.js script. Default 50. */
|
|
73
|
+
scriptTimeoutMs?: number;
|
|
68
74
|
});
|
|
69
75
|
private loadConfig;
|
|
70
76
|
private compilePolicies;
|
|
71
77
|
private reloadIfChanged;
|
|
78
|
+
/**
|
|
79
|
+
* Add or replace (upsert) a single policy in gate.json and apply it live.
|
|
80
|
+
*
|
|
81
|
+
* The raw policy is validated with the SAME `validatePolicy` used when loading
|
|
82
|
+
* gate.json — an invalid shape throws (nothing is written) and the caller
|
|
83
|
+
* surfaces it as a tool error. On success the policy is persisted to gate.json
|
|
84
|
+
* (so it survives restart and is visible to `workspace--edit _config/gate.json`)
|
|
85
|
+
* and the in-memory config is swapped in immediately, so the rule takes effect
|
|
86
|
+
* without waiting for the throttled mtime reload.
|
|
87
|
+
*
|
|
88
|
+
* The freshest on-disk config is re-read first, so a concurrent operator edit
|
|
89
|
+
* isn't clobbered. A policy with the same `name` is replaced IN PLACE (order
|
|
90
|
+
* preserved); otherwise it is appended, or prepended when `position:'prepend'`
|
|
91
|
+
* — first match wins, so a wake rule that must beat a broad defer/debounce
|
|
92
|
+
* belongs at the front.
|
|
93
|
+
*/
|
|
94
|
+
addPolicy(rawPolicy: unknown, options?: {
|
|
95
|
+
position?: 'append' | 'prepend';
|
|
96
|
+
}): GatePolicy;
|
|
97
|
+
/**
|
|
98
|
+
* Remove a policy by name from gate.json and clear its live state. Any pending
|
|
99
|
+
* debounce batch is delivered first (matching reloadIfChanged's removal path,
|
|
100
|
+
* so buffered events aren't silently dropped). Returns false if no policy of
|
|
101
|
+
* that name exists (nothing written). Freshest on-disk config is re-read first
|
|
102
|
+
* (see addPolicy).
|
|
103
|
+
*/
|
|
104
|
+
removePolicy(name: string): boolean;
|
|
105
|
+
/**
|
|
106
|
+
* List the current policy names (freshest on-disk view). Cheap helper for
|
|
107
|
+
* tools/UX that want to show what's active without the full status payload.
|
|
108
|
+
*/
|
|
109
|
+
listPolicyNames(): string[];
|
|
110
|
+
/**
|
|
111
|
+
* Persist a config to gate.json and apply it in memory together: write the
|
|
112
|
+
* file, swap in the compiled policies, and sync `configMtime` to the new file
|
|
113
|
+
* so `reloadIfChanged` doesn't redundantly re-parse identical content. Throws
|
|
114
|
+
* if the write fails (disk/permission) — callers surface it as a tool error.
|
|
115
|
+
*/
|
|
116
|
+
private writeConfig;
|
|
72
117
|
private matchPolicy;
|
|
73
118
|
private compiledMatches;
|
|
74
119
|
evaluate(info: GateEventInfo): GateDecision;
|
|
@@ -101,14 +146,31 @@ export declare class EventGate {
|
|
|
101
146
|
private deliverEvents;
|
|
102
147
|
onInferenceStarted(agentName: string): void;
|
|
103
148
|
onInferenceEnded(agentName: string): void;
|
|
149
|
+
/** Live inference-gating snapshot for diagnostics (e.g. a SIGUSR2 dump).
|
|
150
|
+
* A non-empty `inferring` with a stale timestamp + growing `bufferedEvents`
|
|
151
|
+
* is the signature of the wake-wedge: an agent stuck mid-"inference" so all
|
|
152
|
+
* incoming events buffer and never trigger. */
|
|
153
|
+
inferenceDiagnostics(): {
|
|
154
|
+
inferring: string[];
|
|
155
|
+
bufferedEvents: number;
|
|
156
|
+
sleepUntil: number;
|
|
157
|
+
};
|
|
104
158
|
/** Called internally when debounce fires during inference. */
|
|
105
159
|
private bufferForInference;
|
|
106
160
|
/** Begin (or extend) a sleep window of `seconds`. Returns the wake time. */
|
|
107
|
-
setSleep(seconds: number, note?: string): {
|
|
161
|
+
setSleep(seconds: number, note?: string, agentName?: string): {
|
|
108
162
|
until: number;
|
|
109
163
|
};
|
|
110
164
|
/** End sleep immediately. Returns true if the agent was asleep. */
|
|
111
165
|
clearSleep(): boolean;
|
|
166
|
+
/**
|
|
167
|
+
* Self-wake handler: fired by the sleepTimer when a sleep window elapses.
|
|
168
|
+
* Clears the sleep state and requests an inference so the agent wakes on its
|
|
169
|
+
* own — this is what makes `sleep(N)` a real "wake me in N seconds" rather
|
|
170
|
+
* than just a suppression window. No-op if sleep was already cleared (via
|
|
171
|
+
* `wake`) or re-armed to a later time by a fresh `sleep` call.
|
|
172
|
+
*/
|
|
173
|
+
private wakeFromSleep;
|
|
112
174
|
/** Current sleep state, or null if awake. */
|
|
113
175
|
getSleepState(): {
|
|
114
176
|
until: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"event-gate.d.ts","sourceRoot":"","sources":["../../../src/gate/event-gate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;
|
|
1
|
+
{"version":3,"file":"event-gate.d.ts","sourceRoot":"","sources":["../../../src/gate/event-gate.ts"],"names":[],"mappings":"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"}
|
|
@@ -11,7 +11,8 @@
|
|
|
11
11
|
* inference tokens, not whether the agent should know about the event.
|
|
12
12
|
*/
|
|
13
13
|
import { existsSync, readFileSync, statSync, writeFileSync, mkdirSync } from 'node:fs';
|
|
14
|
-
import { dirname } from 'node:path';
|
|
14
|
+
import { dirname, join } from 'node:path';
|
|
15
|
+
import { GateScript } from './gate-script.js';
|
|
15
16
|
// ---------------------------------------------------------------------------
|
|
16
17
|
// Constants
|
|
17
18
|
// ---------------------------------------------------------------------------
|
|
@@ -99,8 +100,8 @@ function validateConfig(raw) {
|
|
|
99
100
|
}
|
|
100
101
|
const obj = raw;
|
|
101
102
|
const defaultBehavior = obj.default ?? 'always';
|
|
102
|
-
if (defaultBehavior !== 'always' && defaultBehavior !== 'skip') {
|
|
103
|
-
throw new Error(`gate.json "default" must be "always" or "skip", got: ${defaultBehavior}`);
|
|
103
|
+
if (defaultBehavior !== 'always' && defaultBehavior !== 'defer' && defaultBehavior !== 'skip') {
|
|
104
|
+
throw new Error(`gate.json "default" must be "always", "defer", or "skip", got: ${defaultBehavior}`);
|
|
104
105
|
}
|
|
105
106
|
const policies = [];
|
|
106
107
|
if (Array.isArray(obj.policies)) {
|
|
@@ -120,7 +121,7 @@ function validatePolicy(raw) {
|
|
|
120
121
|
}
|
|
121
122
|
// Validate behavior
|
|
122
123
|
let behavior;
|
|
123
|
-
if (obj.behavior === 'always' || obj.behavior === 'skip') {
|
|
124
|
+
if (obj.behavior === 'always' || obj.behavior === 'defer' || obj.behavior === 'skip') {
|
|
124
125
|
behavior = obj.behavior;
|
|
125
126
|
}
|
|
126
127
|
else if (obj.behavior && typeof obj.behavior === 'object') {
|
|
@@ -174,7 +175,7 @@ function validatePolicy(raw) {
|
|
|
174
175
|
behavior = { passive_sample: out };
|
|
175
176
|
}
|
|
176
177
|
else {
|
|
177
|
-
throw new Error(`Policy "${obj.name}": unknown behavior — expected always | skip | { debounce } | { rate_limit } | { passive_sample }`);
|
|
178
|
+
throw new Error(`Policy "${obj.name}": unknown behavior — expected always | defer | skip | { debounce } | { rate_limit } | { passive_sample }`);
|
|
178
179
|
}
|
|
179
180
|
}
|
|
180
181
|
else {
|
|
@@ -199,6 +200,21 @@ function validatePolicy(raw) {
|
|
|
199
200
|
if (fields.length > 0)
|
|
200
201
|
match.metadataTrue = fields;
|
|
201
202
|
}
|
|
203
|
+
const tagList = (v) => {
|
|
204
|
+
if (!Array.isArray(v))
|
|
205
|
+
return undefined;
|
|
206
|
+
const out = v.filter((s) => typeof s === 'string' && s.length > 0);
|
|
207
|
+
return out.length > 0 ? out : undefined;
|
|
208
|
+
};
|
|
209
|
+
const tagsAny = tagList(m.tagsAny);
|
|
210
|
+
if (tagsAny)
|
|
211
|
+
match.tagsAny = tagsAny;
|
|
212
|
+
const tagsAll = tagList(m.tagsAll);
|
|
213
|
+
if (tagsAll)
|
|
214
|
+
match.tagsAll = tagsAll;
|
|
215
|
+
const tagsNone = tagList(m.tagsNone);
|
|
216
|
+
if (tagsNone)
|
|
217
|
+
match.tagsNone = tagsNone;
|
|
202
218
|
if (m.filter && typeof m.filter === 'object') {
|
|
203
219
|
const f = m.filter;
|
|
204
220
|
if ((f.type === 'text' || f.type === 'regex') && typeof f.pattern === 'string') {
|
|
@@ -233,6 +249,8 @@ function validatePolicy(raw) {
|
|
|
233
249
|
export class EventGate {
|
|
234
250
|
configPath;
|
|
235
251
|
config;
|
|
252
|
+
/** Optional agent-authored programmable gate (gate.js). */
|
|
253
|
+
gateScript;
|
|
236
254
|
compiledPolicies = [];
|
|
237
255
|
configMtime = 0;
|
|
238
256
|
lastReloadCheck = 0;
|
|
@@ -258,12 +276,16 @@ export class EventGate {
|
|
|
258
276
|
defaultTriggered = 0;
|
|
259
277
|
defaultSkipped = 0;
|
|
260
278
|
defaultByEventType = new Map();
|
|
261
|
-
// Sleep state (set via the `sleep` tool).
|
|
262
|
-
//
|
|
263
|
-
//
|
|
279
|
+
// Sleep state (set via the `sleep` tool). evaluate() suppresses non-privileged
|
|
280
|
+
// wakes while now() < sleepUntil. A self-wake timer (sleepTimer) fires an
|
|
281
|
+
// inference when the window expires, so `sleep(N)` means "wake me in N
|
|
282
|
+
// seconds" rather than merely lifting suppression and waiting for an external
|
|
283
|
+
// event.
|
|
264
284
|
sleepUntil = 0;
|
|
265
285
|
sleepNote;
|
|
266
286
|
sleepSuppressed = 0;
|
|
287
|
+
sleepTimer = null;
|
|
288
|
+
sleepAgent;
|
|
267
289
|
// Privileged users who may wake the agent through sleep. Hot-reloaded from
|
|
268
290
|
// privilegedUsersPath on change.
|
|
269
291
|
privilegedUsersPath;
|
|
@@ -332,6 +354,9 @@ export class EventGate {
|
|
|
332
354
|
// Load config
|
|
333
355
|
this.config = this.loadConfig() ?? opts.initialConfig ?? { ...DEFAULT_CONFIG };
|
|
334
356
|
this.compiledPolicies = this.compilePolicies(this.config);
|
|
357
|
+
// Programmable gate: an optional agent-authored `gate.js` next to gate.json.
|
|
358
|
+
// Absent ⇒ inactive (zero overhead beyond a throttled stat check).
|
|
359
|
+
this.gateScript = new GateScript(join(dirname(this.configPath), 'gate.js'), opts.scriptTimeoutMs ?? 50, this.now);
|
|
335
360
|
}
|
|
336
361
|
// =========================================================================
|
|
337
362
|
// Config loading
|
|
@@ -382,6 +407,12 @@ export class EventGate {
|
|
|
382
407
|
if (policy.match.pathGlob) {
|
|
383
408
|
compiled.pathRegex = compileGlob(policy.match.pathGlob);
|
|
384
409
|
}
|
|
410
|
+
if (policy.match.tagsAny)
|
|
411
|
+
compiled.tagsAnyRe = policy.match.tagsAny.map(compileGlob);
|
|
412
|
+
if (policy.match.tagsAll)
|
|
413
|
+
compiled.tagsAllRe = policy.match.tagsAll.map(compileGlob);
|
|
414
|
+
if (policy.match.tagsNone)
|
|
415
|
+
compiled.tagsNoneRe = policy.match.tagsNone.map(compileGlob);
|
|
385
416
|
return compiled;
|
|
386
417
|
});
|
|
387
418
|
}
|
|
@@ -447,6 +478,103 @@ export class EventGate {
|
|
|
447
478
|
}
|
|
448
479
|
}
|
|
449
480
|
// =========================================================================
|
|
481
|
+
// Runtime policy mutation (backs wake_add_rule / wake_remove_rule)
|
|
482
|
+
// =========================================================================
|
|
483
|
+
/**
|
|
484
|
+
* Add or replace (upsert) a single policy in gate.json and apply it live.
|
|
485
|
+
*
|
|
486
|
+
* The raw policy is validated with the SAME `validatePolicy` used when loading
|
|
487
|
+
* gate.json — an invalid shape throws (nothing is written) and the caller
|
|
488
|
+
* surfaces it as a tool error. On success the policy is persisted to gate.json
|
|
489
|
+
* (so it survives restart and is visible to `workspace--edit _config/gate.json`)
|
|
490
|
+
* and the in-memory config is swapped in immediately, so the rule takes effect
|
|
491
|
+
* without waiting for the throttled mtime reload.
|
|
492
|
+
*
|
|
493
|
+
* The freshest on-disk config is re-read first, so a concurrent operator edit
|
|
494
|
+
* isn't clobbered. A policy with the same `name` is replaced IN PLACE (order
|
|
495
|
+
* preserved); otherwise it is appended, or prepended when `position:'prepend'`
|
|
496
|
+
* — first match wins, so a wake rule that must beat a broad defer/debounce
|
|
497
|
+
* belongs at the front.
|
|
498
|
+
*/
|
|
499
|
+
addPolicy(rawPolicy, options) {
|
|
500
|
+
const policy = validatePolicy(rawPolicy);
|
|
501
|
+
const current = this.loadConfig() ?? this.config;
|
|
502
|
+
const policies = [...current.policies];
|
|
503
|
+
const idx = policies.findIndex((p) => p.name === policy.name);
|
|
504
|
+
if (idx >= 0) {
|
|
505
|
+
policies[idx] = policy;
|
|
506
|
+
}
|
|
507
|
+
else if (options?.position === 'prepend') {
|
|
508
|
+
policies.unshift(policy);
|
|
509
|
+
}
|
|
510
|
+
else {
|
|
511
|
+
policies.push(policy);
|
|
512
|
+
}
|
|
513
|
+
this.writeConfig({ ...current, policies });
|
|
514
|
+
this.emitTrace({
|
|
515
|
+
type: 'gate:policy-added',
|
|
516
|
+
configPath: this.configPath,
|
|
517
|
+
policyName: policy.name,
|
|
518
|
+
replaced: idx >= 0,
|
|
519
|
+
timestamp: Date.now(),
|
|
520
|
+
});
|
|
521
|
+
return policy;
|
|
522
|
+
}
|
|
523
|
+
/**
|
|
524
|
+
* Remove a policy by name from gate.json and clear its live state. Any pending
|
|
525
|
+
* debounce batch is delivered first (matching reloadIfChanged's removal path,
|
|
526
|
+
* so buffered events aren't silently dropped). Returns false if no policy of
|
|
527
|
+
* that name exists (nothing written). Freshest on-disk config is re-read first
|
|
528
|
+
* (see addPolicy).
|
|
529
|
+
*/
|
|
530
|
+
removePolicy(name) {
|
|
531
|
+
const current = this.loadConfig() ?? this.config;
|
|
532
|
+
if (!current.policies.some((p) => p.name === name)) {
|
|
533
|
+
return false;
|
|
534
|
+
}
|
|
535
|
+
// Deliver any pending debounce batch, then drop timers/buckets/counters.
|
|
536
|
+
this.clearPolicyState(name, { deliverPendingDebounce: true });
|
|
537
|
+
this.stats.delete(name);
|
|
538
|
+
const policies = current.policies.filter((p) => p.name !== name);
|
|
539
|
+
this.writeConfig({ ...current, policies });
|
|
540
|
+
this.emitTrace({
|
|
541
|
+
type: 'gate:policy-removed',
|
|
542
|
+
configPath: this.configPath,
|
|
543
|
+
policyName: name,
|
|
544
|
+
timestamp: Date.now(),
|
|
545
|
+
});
|
|
546
|
+
return true;
|
|
547
|
+
}
|
|
548
|
+
/**
|
|
549
|
+
* List the current policy names (freshest on-disk view). Cheap helper for
|
|
550
|
+
* tools/UX that want to show what's active without the full status payload.
|
|
551
|
+
*/
|
|
552
|
+
listPolicyNames() {
|
|
553
|
+
const current = this.loadConfig() ?? this.config;
|
|
554
|
+
return current.policies.map((p) => p.name);
|
|
555
|
+
}
|
|
556
|
+
/**
|
|
557
|
+
* Persist a config to gate.json and apply it in memory together: write the
|
|
558
|
+
* file, swap in the compiled policies, and sync `configMtime` to the new file
|
|
559
|
+
* so `reloadIfChanged` doesn't redundantly re-parse identical content. Throws
|
|
560
|
+
* if the write fails (disk/permission) — callers surface it as a tool error.
|
|
561
|
+
*/
|
|
562
|
+
writeConfig(config) {
|
|
563
|
+
mkdirSync(dirname(this.configPath), { recursive: true });
|
|
564
|
+
writeFileSync(this.configPath, JSON.stringify(config, null, 2) + '\n');
|
|
565
|
+
this.config = config;
|
|
566
|
+
this.compiledPolicies = this.compilePolicies(config);
|
|
567
|
+
this.configSource = 'file';
|
|
568
|
+
this.configErrors = [];
|
|
569
|
+
this.lastReloadTimestamp = Date.now();
|
|
570
|
+
try {
|
|
571
|
+
this.configMtime = statSync(this.configPath).mtimeMs;
|
|
572
|
+
}
|
|
573
|
+
catch {
|
|
574
|
+
// A failed stat only risks one redundant reload; keep the old mtime.
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
// =========================================================================
|
|
450
578
|
// Policy matching — first match wins
|
|
451
579
|
// =========================================================================
|
|
452
580
|
matchPolicy(info) {
|
|
@@ -511,6 +639,18 @@ export class EventGate {
|
|
|
511
639
|
if (!anyTrue)
|
|
512
640
|
return false;
|
|
513
641
|
}
|
|
642
|
+
// Tag matching (MCPL RFC-001). Patterns are globs; `tags` is the event's
|
|
643
|
+
// (implication-expanded) tag set. A pattern "matches" if it matches ANY tag.
|
|
644
|
+
if (compiled.tagsAnyRe || compiled.tagsAllRe || compiled.tagsNoneRe) {
|
|
645
|
+
const tags = info.tags ?? [];
|
|
646
|
+
const patternHits = (re) => tags.some(t => re.test(t));
|
|
647
|
+
if (compiled.tagsAnyRe && !compiled.tagsAnyRe.some(patternHits))
|
|
648
|
+
return false;
|
|
649
|
+
if (compiled.tagsAllRe && !compiled.tagsAllRe.every(patternHits))
|
|
650
|
+
return false;
|
|
651
|
+
if (compiled.tagsNoneRe && compiled.tagsNoneRe.some(patternHits))
|
|
652
|
+
return false;
|
|
653
|
+
}
|
|
514
654
|
return true;
|
|
515
655
|
}
|
|
516
656
|
// =========================================================================
|
|
@@ -545,7 +685,11 @@ export class EventGate {
|
|
|
545
685
|
}
|
|
546
686
|
// Privileged author — fall through to normal policy evaluation.
|
|
547
687
|
}
|
|
548
|
-
|
|
688
|
+
// Programmable gate (gate.js) takes precedence when present and returns a
|
|
689
|
+
// behavior; null/undefined (inactive, error, or a deliberate pass) falls
|
|
690
|
+
// through to the declarative gate.json policies below.
|
|
691
|
+
const scripted = this.gateScript.evaluate(info);
|
|
692
|
+
const policy = scripted != null ? { name: 'gate.js', match: {}, behavior: scripted } : this.matchPolicy(info);
|
|
549
693
|
const decision = this.computeDecision(policy, info);
|
|
550
694
|
this.totalEvaluations++;
|
|
551
695
|
if (!policy) {
|
|
@@ -592,8 +736,9 @@ export class EventGate {
|
|
|
592
736
|
source: info.serverId || undefined,
|
|
593
737
|
timestamp: this.now(),
|
|
594
738
|
});
|
|
595
|
-
if (policy.behavior === 'skip') {
|
|
596
|
-
|
|
739
|
+
if (policy.behavior === 'defer' || policy.behavior === 'skip') {
|
|
740
|
+
// Report the configured name faithfully ('defer' or legacy 'skip').
|
|
741
|
+
return { trigger: false, policyName: policy.name, behavior: policy.behavior };
|
|
597
742
|
}
|
|
598
743
|
if (policy.behavior === 'always') {
|
|
599
744
|
this.applyResets(policy);
|
|
@@ -729,6 +874,7 @@ export class EventGate {
|
|
|
729
874
|
serverId: metadata.serverId ?? '',
|
|
730
875
|
channelId: metadata.channelId ?? '',
|
|
731
876
|
metadata,
|
|
877
|
+
tags: Array.isArray(metadata.tags) ? metadata.tags : undefined,
|
|
732
878
|
});
|
|
733
879
|
return decision.trigger;
|
|
734
880
|
};
|
|
@@ -812,6 +958,17 @@ export class EventGate {
|
|
|
812
958
|
queueMicrotask(() => this.deliverEvents(events));
|
|
813
959
|
}
|
|
814
960
|
}
|
|
961
|
+
/** Live inference-gating snapshot for diagnostics (e.g. a SIGUSR2 dump).
|
|
962
|
+
* A non-empty `inferring` with a stale timestamp + growing `bufferedEvents`
|
|
963
|
+
* is the signature of the wake-wedge: an agent stuck mid-"inference" so all
|
|
964
|
+
* incoming events buffer and never trigger. */
|
|
965
|
+
inferenceDiagnostics() {
|
|
966
|
+
return {
|
|
967
|
+
inferring: [...this.inferring],
|
|
968
|
+
bufferedEvents: this.inferenceBuffer.length,
|
|
969
|
+
sleepUntil: this.sleepUntil,
|
|
970
|
+
};
|
|
971
|
+
}
|
|
815
972
|
// =========================================================================
|
|
816
973
|
// Inference buffer cap
|
|
817
974
|
// =========================================================================
|
|
@@ -831,11 +988,18 @@ export class EventGate {
|
|
|
831
988
|
// Sleep
|
|
832
989
|
// =========================================================================
|
|
833
990
|
/** Begin (or extend) a sleep window of `seconds`. Returns the wake time. */
|
|
834
|
-
setSleep(seconds, note) {
|
|
991
|
+
setSleep(seconds, note, agentName) {
|
|
835
992
|
const ms = Math.max(0, Math.floor(seconds * 1000));
|
|
836
993
|
this.sleepUntil = this.now() + ms;
|
|
837
994
|
this.sleepNote = note;
|
|
995
|
+
this.sleepAgent = agentName;
|
|
838
996
|
this.sleepSuppressed = 0;
|
|
997
|
+
// Arm the self-wake: when the window elapses, clear sleep and request an
|
|
998
|
+
// inference so the agent resumes on its own (not dependent on a heartbeat
|
|
999
|
+
// tick or an incoming message). Cancelled by clearSleep()/`wake`.
|
|
1000
|
+
if (this.sleepTimer)
|
|
1001
|
+
clearTimeout(this.sleepTimer);
|
|
1002
|
+
this.sleepTimer = setTimeout(() => this.wakeFromSleep('sleep-expired'), ms);
|
|
839
1003
|
this.reloadPrivilegedIfChanged();
|
|
840
1004
|
this.emitTrace({
|
|
841
1005
|
type: 'gate:decision',
|
|
@@ -852,8 +1016,42 @@ export class EventGate {
|
|
|
852
1016
|
const wasAsleep = this.sleepUntil > this.now();
|
|
853
1017
|
this.sleepUntil = 0;
|
|
854
1018
|
this.sleepNote = undefined;
|
|
1019
|
+
this.sleepAgent = undefined;
|
|
1020
|
+
if (this.sleepTimer) {
|
|
1021
|
+
clearTimeout(this.sleepTimer);
|
|
1022
|
+
this.sleepTimer = null;
|
|
1023
|
+
}
|
|
855
1024
|
return wasAsleep;
|
|
856
1025
|
}
|
|
1026
|
+
/**
|
|
1027
|
+
* Self-wake handler: fired by the sleepTimer when a sleep window elapses.
|
|
1028
|
+
* Clears the sleep state and requests an inference so the agent wakes on its
|
|
1029
|
+
* own — this is what makes `sleep(N)` a real "wake me in N seconds" rather
|
|
1030
|
+
* than just a suppression window. No-op if sleep was already cleared (via
|
|
1031
|
+
* `wake`) or re-armed to a later time by a fresh `sleep` call.
|
|
1032
|
+
*/
|
|
1033
|
+
wakeFromSleep(reason) {
|
|
1034
|
+
this.sleepTimer = null;
|
|
1035
|
+
if (this.sleepUntil === 0 || this.sleepUntil > this.now())
|
|
1036
|
+
return;
|
|
1037
|
+
const note = this.sleepNote;
|
|
1038
|
+
const agent = this.sleepAgent;
|
|
1039
|
+
this.sleepUntil = 0;
|
|
1040
|
+
this.sleepNote = undefined;
|
|
1041
|
+
this.sleepAgent = undefined;
|
|
1042
|
+
this.emitTrace({
|
|
1043
|
+
type: 'gate:decision',
|
|
1044
|
+
eventType: 'sleep:expired',
|
|
1045
|
+
matchedPolicy: 'asleep',
|
|
1046
|
+
trigger: true,
|
|
1047
|
+
behavior: 'always',
|
|
1048
|
+
timestamp: this.now(),
|
|
1049
|
+
});
|
|
1050
|
+
const detail = note ? `${reason}: ${note}` : reason;
|
|
1051
|
+
const targets = agent ? [agent] : this.getAgentNamesFn();
|
|
1052
|
+
for (const a of targets)
|
|
1053
|
+
this.requestInferenceFn(a, detail, 'sleep');
|
|
1054
|
+
}
|
|
857
1055
|
/** Current sleep state, or null if awake. */
|
|
858
1056
|
getSleepState() {
|
|
859
1057
|
const remainingMs = this.sleepUntil - this.now();
|
|
@@ -976,6 +1174,7 @@ export class EventGate {
|
|
|
976
1174
|
configSource: this.configSource,
|
|
977
1175
|
lastReloadTimestamp: this.lastReloadTimestamp,
|
|
978
1176
|
default: this.config.default ?? 'always',
|
|
1177
|
+
script: this.gateScript.status(),
|
|
979
1178
|
policies,
|
|
980
1179
|
errors: [...this.configErrors],
|
|
981
1180
|
totalEvaluations: this.totalEvaluations,
|
|
@@ -990,6 +1189,7 @@ export class EventGate {
|
|
|
990
1189
|
// Cleanup
|
|
991
1190
|
// =========================================================================
|
|
992
1191
|
dispose() {
|
|
1192
|
+
this.gateScript.dispose();
|
|
993
1193
|
for (const state of this.debounceTimers.values()) {
|
|
994
1194
|
clearTimeout(state.timer);
|
|
995
1195
|
}
|