@chatpanel/events 0.102.0 → 0.103.1
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/adapters.js +1 -83
- package/adaptive-tool-policy.js +1 -45
- package/agent.js +2 -255
- package/attribution.js +1 -132
- package/backup-envelope.js +1 -221
- package/board-tool.js +2 -165
- package/budget.js +1 -116
- package/capability.js +1 -126
- package/citations.js +5 -127
- package/client-prefs.js +1 -148
- package/context-attachments.js +8 -144
- package/cowriter-router.js +1 -83
- package/cowriter-writer.js +19 -193
- package/cowriter.js +1 -190
- package/curate.js +3 -509
- package/distance.js +1 -124
- package/engine.js +1 -131
- package/entitlement.js +1 -332
- package/entity.js +1 -354
- package/event.js +1 -175
- package/extraction.js +4 -293
- package/failover.js +1 -93
- package/find-tool.js +1 -66
- package/flowchart.js +2 -644
- package/gate.js +1 -74
- package/harness.js +4 -101
- package/index.js +1 -345
- package/invariants.js +1 -174
- package/job.js +3 -147
- package/kernel.js +1 -255
- package/knowledge-derive.js +1 -267
- package/knowledge.js +3 -230
- package/library.js +11 -280
- package/loop.js +1 -132
- package/manifest.js +1 -107
- package/markdown-authoring.js +11 -288
- package/markdown-render.js +0 -0
- package/mcp-client.js +8 -358
- package/mcp-dispatch.js +1 -52
- package/mcp-errors.js +1 -106
- package/mcp-manager.js +1 -127
- package/media-transcript.js +8 -531
- package/meeting-analyzers.js +1 -83
- package/meeting-insights.js +7 -164
- package/meeting-shape.js +1 -282
- package/meeting-text.js +5 -94
- package/memory.js +2 -638
- package/model-candidates.js +1 -358
- package/model-health.js +1 -182
- package/model-ledger.js +1 -228
- package/model-picker.js +1 -188
- package/note-actions.js +15 -246
- package/note-graph.js +1 -158
- package/note-links.js +1 -68
- package/note-mentions.js +8 -114
- package/note-plan.js +9 -107
- package/note-research.js +1 -135
- package/observability.js +1 -119
- package/omni.js +1 -125
- package/order.js +1 -78
- package/package.json +1 -4
- package/pdf-layout.js +7 -253
- package/project-run.js +20 -444
- package/project.js +1 -170
- package/promotion.js +1 -171
- package/queue.js +1 -98
- package/reach.js +1 -23
- package/recipe-tool.js +2 -161
- package/recipe.js +1 -264
- package/record-list.js +2 -110
- package/recruit.js +3 -456
- package/redaction-tokens.js +1 -61
- package/ref.js +1 -55
- package/registry.js +1 -240
- package/route-graph.js +1 -115
- package/route-strategies.js +2 -232
- package/router.js +2 -880
- package/rrf.js +1 -89
- package/rules.js +1 -142
- package/schedule.js +1 -663
- package/scm-connection.js +1 -180
- package/scopes.js +1 -12
- package/scorecard.js +1 -335
- package/search-engines.js +1 -81
- package/skill-manifest.js +1 -330
- package/skill-scan.js +3 -209
- package/skill-sources.js +1 -162
- package/skill-vars.js +2 -252
- package/slash-commands.js +1 -158
- package/source-gate.js +1 -92
- package/sources-retrieval.js +5 -186
- package/sources.js +1 -273
- package/store.js +1 -171
- package/structured.js +4 -901
- package/subject-kinds.js +1 -5
- package/subject-name.js +1 -96
- package/sync-plan.js +1 -213
- package/synthesis.js +3 -123
- package/tags.js +1 -203
- package/team-board.js +6 -337
- package/team-cache.js +2 -63
- package/team-observe.js +1 -118
- package/team-org.js +1 -442
- package/team-plan.js +4 -131
- package/team-record.js +1 -225
- package/team-run.js +9 -652
- package/team-subtask.js +2 -191
- package/team-task.js +2 -124
- package/team-tool.js +2 -196
- package/team-trail.js +2 -73
- package/team-worklog.js +2 -149
- package/team.js +1 -302
- package/text-search.js +1 -155
- package/theme.js +17 -154
- package/titles.js +4 -285
- package/tool-discovery.js +1 -87
- package/tool-dispatch.js +5 -230
- package/tool-groups.js +1 -81
- package/tool-hints.js +10 -185
- package/tool-loop-guard.js +4 -182
- package/tool-need.js +2 -124
- package/tool-result.js +2 -438
- package/tool-round.js +0 -0
- package/tool-schema.js +1 -155
- package/tool-traits.js +1 -182
- package/toolset.js +2 -88
- package/trajectory.js +1 -530
- package/turn-loop.js +4 -440
- package/upcast.js +1 -37
- package/vault.js +2 -250
- package/view.js +1 -88
- package/voice-intents.js +2 -1488
- package/voice-speaker.js +1 -98
- package/weather-tool.js +1 -65
- package/weather.js +2 -211
- package/web-search-tool.js +14 -102
- package/web-search.js +1 -160
- package/widget.js +1 -153
package/rrf.js
CHANGED
|
@@ -1,89 +1 @@
|
|
|
1
|
-
|
|
2
|
-
//
|
|
3
|
-
// One question rarely makes one good keyword query. "What was the outcome of the Ben tooling
|
|
4
|
-
// demo?" and "Ben demo decisions action items" retrieve different things, and the answer is
|
|
5
|
-
// usually in the union. RRF merges several ranked lists without needing their scores to be
|
|
6
|
-
// comparable — each list contributes 1/(k+rank) — which is exactly the situation here: BM25
|
|
7
|
-
// scores, vector distances and a hot/warm split are all on different scales.
|
|
8
|
-
//
|
|
9
|
-
// Pure and dependency-free, so the identical fusion runs in the extension (hot+warm), the
|
|
10
|
-
// gateway (multi-query search) and any future client. The extension had this privately; it
|
|
11
|
-
// lives here now so the gateway doesn't grow a second, subtly different copy.
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Fuse ranked id lists. `lists` is an array of arrays of ids, each already in rank order.
|
|
15
|
-
* k dampens the head of each list (60 is the standard default). limit 0 = everything.
|
|
16
|
-
*/
|
|
17
|
-
export function fuseRRF(lists, { k = 60, limit = 0 } = {}) {
|
|
18
|
-
const score = new Map();
|
|
19
|
-
for (const list of lists || []) {
|
|
20
|
-
if (!Array.isArray(list)) continue;
|
|
21
|
-
list.forEach((id, rank) => {
|
|
22
|
-
if (id == null) return;
|
|
23
|
-
score.set(id, (score.get(id) || 0) + 1 / (k + rank));
|
|
24
|
-
});
|
|
25
|
-
}
|
|
26
|
-
const out = [...score.entries()].map(([id, s]) => ({ id, score: s })).sort((a, b) => b.score - a.score);
|
|
27
|
-
return limit > 0 ? out.slice(0, limit) : out;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
// Words that carry no retrieval signal but do dilute BM25 — dropped to build a keyword-only
|
|
31
|
-
// variant of a natural-language question.
|
|
32
|
-
const STOP = new Set(('a an and are as at be been but by can could did do does for from had has have how i if in into is it its me my of on or our ought shall should '
|
|
33
|
-
+ 'so than that the their them then there these they this those to um was we were what when where which who whom why will with would you your about tell show give find get '
|
|
34
|
-
+ 'please could-you was-there did-we').split(/\s+/));
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Turn one natural-language question into a small set of complementary queries — the cheap,
|
|
38
|
-
* deterministic half of query expansion. No model call, so it costs nothing and cannot fail.
|
|
39
|
-
*
|
|
40
|
-
* A CALLING AGENT can do better (it understands the domain), which is why the tools accept an
|
|
41
|
-
* explicit `queries` list; these variants are the floor, not the ceiling.
|
|
42
|
-
*/
|
|
43
|
-
export function planQueries(question, { extra = [], max = 4 } = {}) {
|
|
44
|
-
const q = String(question || '').trim();
|
|
45
|
-
const out = [];
|
|
46
|
-
const seen = new Set();
|
|
47
|
-
const add = (s) => {
|
|
48
|
-
const t = String(s || '').trim().replace(/\s+/g, ' ');
|
|
49
|
-
const key = t.toLowerCase();
|
|
50
|
-
if (t && !seen.has(key)) { seen.add(key); out.push(t); }
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
add(q); // the question as asked
|
|
54
|
-
for (const e of extra) add(e); // whatever the agent proposed — it knows more
|
|
55
|
-
|
|
56
|
-
const words = q.toLowerCase().match(/[a-z0-9][a-z0-9'’_+-]*/g) || [];
|
|
57
|
-
const keywords = words.filter((w) => !STOP.has(w) && w.length > 2);
|
|
58
|
-
if (keywords.length >= 2) add(keywords.join(' ')); // keyword-only: BM25's best shape
|
|
59
|
-
// The rarest-looking terms (longest words are a decent proxy for specificity) — helps when
|
|
60
|
-
// the full question is too broad to rank anything well.
|
|
61
|
-
if (keywords.length > 3) add([...keywords].sort((a, b) => b.length - a.length).slice(0, 3).join(' '));
|
|
62
|
-
|
|
63
|
-
return out.slice(0, Math.max(1, max));
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Run several queries through one `search(query, opts)` function and fuse the results by id.
|
|
68
|
-
* `search` returns arrays of { id, ... }; the fused output keeps the richest record seen for
|
|
69
|
-
* each id (so snippets survive) and reports which queries found it — the "why is this here"
|
|
70
|
-
* a reader needs when a multi-query search surfaces something unexpected.
|
|
71
|
-
*/
|
|
72
|
-
export async function multiSearch(queries, search, { limit = 10, k = 60 } = {}) {
|
|
73
|
-
const lists = [];
|
|
74
|
-
const byId = new Map();
|
|
75
|
-
const foundBy = new Map();
|
|
76
|
-
for (const q of queries || []) {
|
|
77
|
-
let rows = [];
|
|
78
|
-
try { rows = (await search(q)) || []; } catch { rows = []; } // one bad query must not sink the rest
|
|
79
|
-
lists.push(rows.map((r) => r.id));
|
|
80
|
-
for (const r of rows) {
|
|
81
|
-
if (!byId.has(r.id) || (!byId.get(r.id).snippet && r.snippet)) byId.set(r.id, r);
|
|
82
|
-
if (!foundBy.has(r.id)) foundBy.set(r.id, []);
|
|
83
|
-
foundBy.get(r.id).push(q);
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
return fuseRRF(lists, { k, limit }).map(({ id, score }) => ({
|
|
87
|
-
...byId.get(id), id, score, foundBy: foundBy.get(id) || [],
|
|
88
|
-
}));
|
|
89
|
-
}
|
|
1
|
+
function l(h,{k:c=60,limit:a=0}={}){const r=new Map;for(const o of h||[])Array.isArray(o)&&o.forEach((e,n)=>{e!=null&&r.set(e,(r.get(e)||0)+1/(c+n))});const i=[...r.entries()].map(([o,e])=>({id:o,score:e})).sort((o,e)=>e.score-o.score);return a>0?i.slice(0,a):i}const w=new Set("a an and are as at be been but by can could did do does for from had has have how i if in into is it its me my of on or our ought shall should so than that the their them then there these they this those to um was we were what when where which who whom why will with would you your about tell show give find get please could-you was-there did-we".split(/\s+/));function d(h,{extra:c=[],max:a=4}={}){const r=String(h||"").trim(),i=[],o=new Set,e=t=>{const u=String(t||"").trim().replace(/\s+/g," "),f=u.toLowerCase();u&&!o.has(f)&&(o.add(f),i.push(u))};e(r);for(const t of c)e(t);const s=(r.toLowerCase().match(/[a-z0-9][a-z0-9'’_+-]*/g)||[]).filter(t=>!w.has(t)&&t.length>2);return s.length>=2&&e(s.join(" ")),s.length>3&&e([...s].sort((t,u)=>u.length-t.length).slice(0,3).join(" ")),i.slice(0,Math.max(1,a))}async function p(h,c,{limit:a=10,k:r=60}={}){const i=[],o=new Map,e=new Map;for(const n of h||[]){let s=[];try{s=await c(n)||[]}catch{s=[]}i.push(s.map(t=>t.id));for(const t of s)(!o.has(t.id)||!o.get(t.id).snippet&&t.snippet)&&o.set(t.id,t),e.has(t.id)||e.set(t.id,[]),e.get(t.id).push(n)}return l(i,{k:r,limit:a}).map(({id:n,score:s})=>({...o.get(n),id:n,score:s,foundBy:e.get(n)||[]}))}export{l as fuseRRF,p as multiSearch,d as planQueries};
|
package/rules.js
CHANGED
|
@@ -1,142 +1 @@
|
|
|
1
|
-
|
|
2
|
-
//
|
|
3
|
-
// Everything ChatPanel does today needs a model, which means everything costs tokens,
|
|
4
|
-
// latency and a network. But a great deal of what users actually want is a RULE: when a
|
|
5
|
-
// meeting ends, save the notes; when a page matches, offer to act on it; when a note gains
|
|
6
|
-
// a heading, index it. Those are deterministic, instant, free, and provable — and until
|
|
7
|
-
// there is somewhere to declare them, each is hand-written into whichever file noticed the
|
|
8
|
-
// need first.
|
|
9
|
-
//
|
|
10
|
-
// A rule fires FROM THE EVENT LOG, which is what finally makes the log a bus rather than a
|
|
11
|
-
// record. That is the whole reason the log came first: a rule that had to be called
|
|
12
|
-
// explicitly by the code that might interest it is not automation, it is a function call
|
|
13
|
-
// with extra steps.
|
|
14
|
-
//
|
|
15
|
-
// THREE PROPERTIES THAT ARE NOT NEGOTIABLE, because automation is where a mistake is
|
|
16
|
-
// unattended:
|
|
17
|
-
// • A rule may NARROW authority, never widen it. It runs with the scope it was granted
|
|
18
|
-
// and cannot request more at fire time.
|
|
19
|
-
// • A non-pure action carries an idempotency key, so a rule that fires twice on a redelivered
|
|
20
|
-
// event does its thing once (I3).
|
|
21
|
-
// • Every decision is recorded — fired AND suppressed — because an automation you cannot
|
|
22
|
-
// see is one you cannot trust, and 'it did nothing' has many causes worth telling apart.
|
|
23
|
-
|
|
24
|
-
export class RuleError extends Error {
|
|
25
|
-
constructor(code, message) { super(message); this.name = 'RuleError'; this.code = code; }
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/** Why a rule did not fire. Each is a different problem, so each has a name. */
|
|
29
|
-
export const SUPPRESSED = Object.freeze({
|
|
30
|
-
DISABLED: 'disabled', // switched off in Plugins
|
|
31
|
-
CONDITION: 'condition-false', // the trigger matched, the condition did not
|
|
32
|
-
DUPLICATE: 'already-fired', // same event, same rule — redelivery, not a new cause
|
|
33
|
-
RATE_LIMITED: 'rate-limited', // fired too recently
|
|
34
|
-
NO_PERMISSION: 'no-permission', // the guard refused
|
|
35
|
-
ERROR: 'error', // the rule itself threw
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* @param on event type, or array of them. Matching on TYPE first is what keeps a busy
|
|
40
|
-
* log cheap: a predicate is only run for events that could possibly match.
|
|
41
|
-
* @param when (event, ctx) => boolean. Pure and synchronous, deliberately — a condition
|
|
42
|
-
* that could do I/O would make "did this rule match" unanswerable without
|
|
43
|
-
* side effects, and untestable without mocks.
|
|
44
|
-
* @param then async (event, ctx) => result. The action. Receives `ctx.invoke`, so a rule
|
|
45
|
-
* cannot reach a capability except through the guarded path.
|
|
46
|
-
* @param classUsed the execution class this rule actually uses — 'R' for a pure rule, 'M'
|
|
47
|
-
* for a small model, 'C' for a cloud one. Declared rather than inferred,
|
|
48
|
-
* because the honest answer to "did this cost anything" cannot be guessed.
|
|
49
|
-
* @param everyMs minimum gap between fires. 0 means every matching event.
|
|
50
|
-
*/
|
|
51
|
-
export function defineRule({
|
|
52
|
-
id, label, on, when = null, then, classUsed = 'R',
|
|
53
|
-
everyMs = 0, requiresApproval = false, description = '', effects = 'idempotent',
|
|
54
|
-
}) {
|
|
55
|
-
if (!id) throw new RuleError('BAD_RULE', 'rule.id required');
|
|
56
|
-
if (typeof then !== 'function') throw new RuleError('BAD_RULE', `rule '${id}': then required`);
|
|
57
|
-
const types = Array.isArray(on) ? on : [on];
|
|
58
|
-
if (!types.length || types.some((t) => typeof t !== 'string' || !t)) {
|
|
59
|
-
throw new RuleError('BAD_RULE', `rule '${id}': on must name at least one event type`);
|
|
60
|
-
}
|
|
61
|
-
if (when && typeof when !== 'function') throw new RuleError('BAD_RULE', `rule '${id}': when must be a function`);
|
|
62
|
-
// A rule that changes the world without an idempotency story will eventually do it twice.
|
|
63
|
-
if (effects === 'non-replayable' && !requiresApproval && classUsed === 'R') {
|
|
64
|
-
// Not an error — some rules genuinely must act — but it has to be stated, so the
|
|
65
|
-
// decision is visible in the declaration rather than discovered from behaviour.
|
|
66
|
-
}
|
|
67
|
-
return Object.freeze({
|
|
68
|
-
id, label: label || id, on: types, when, then, classUsed,
|
|
69
|
-
everyMs, requiresApproval, description, effects,
|
|
70
|
-
});
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
export function createRuleEngine({ emit = () => {}, now = () => 0, admit = null, approve = null } = {}) {
|
|
74
|
-
const rules = [];
|
|
75
|
-
const lastFired = new Map();
|
|
76
|
-
const seen = new Set(); // `${ruleId}:${eventId}` — redelivery is not a new cause
|
|
77
|
-
|
|
78
|
-
return {
|
|
79
|
-
add(rule) {
|
|
80
|
-
rules.push(rule);
|
|
81
|
-
return () => { const i = rules.indexOf(rule); if (i >= 0) rules.splice(i, 1); };
|
|
82
|
-
},
|
|
83
|
-
list: () => [...rules],
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* Offer one event to every rule. Returns what happened, so a caller can assert on it —
|
|
87
|
-
* an engine whose only output is side effects cannot be tested without mocks.
|
|
88
|
-
*
|
|
89
|
-
* Never throws. A rule that fails must not take down the thing that emitted the event:
|
|
90
|
-
* automation is a passenger, not a driver.
|
|
91
|
-
*/
|
|
92
|
-
async dispatch(event, ctx = {}) {
|
|
93
|
-
const out = [];
|
|
94
|
-
for (const rule of rules) {
|
|
95
|
-
if (!rule.on.includes(event?.type)) continue;
|
|
96
|
-
|
|
97
|
-
const suppress = (reason, detail) => {
|
|
98
|
-
emit('automation.suppressed', { ruleId: rule.id, reason, eventId: event.id, ...detail });
|
|
99
|
-
out.push({ ruleId: rule.id, fired: false, reason });
|
|
100
|
-
};
|
|
101
|
-
|
|
102
|
-
if (admit && !admit(rule)) { suppress(SUPPRESSED.DISABLED); continue; }
|
|
103
|
-
|
|
104
|
-
const key = `${rule.id}:${event.id}`;
|
|
105
|
-
if (seen.has(key)) { suppress(SUPPRESSED.DUPLICATE); continue; }
|
|
106
|
-
|
|
107
|
-
if (rule.everyMs > 0 && lastFired.has(rule.id)) {
|
|
108
|
-
// `has`, not truthiness: a rule that fired at timestamp 0 HAS fired, and treating
|
|
109
|
-
// that as "never" gives it one free pass through its own rate limit. Only a test
|
|
110
|
-
// clock starts at 0, but a guard that is wrong for one value is wrong.
|
|
111
|
-
if (now() - lastFired.get(rule.id) < rule.everyMs) { suppress(SUPPRESSED.RATE_LIMITED); continue; }
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
let matched = true;
|
|
115
|
-
try { matched = rule.when ? !!rule.when(event, ctx) : true; } catch (e) {
|
|
116
|
-
// A condition that throws is a condition that did not match. Firing on an
|
|
117
|
-
// unanswered question is how automation does something nobody asked for.
|
|
118
|
-
suppress(SUPPRESSED.ERROR, { message: e.message });
|
|
119
|
-
continue;
|
|
120
|
-
}
|
|
121
|
-
if (!matched) { suppress(SUPPRESSED.CONDITION); continue; }
|
|
122
|
-
|
|
123
|
-
if (rule.requiresApproval) {
|
|
124
|
-
const ok = approve ? await approve(rule, event) : false;
|
|
125
|
-
if (!ok) { suppress(SUPPRESSED.NO_PERMISSION); continue; }
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
seen.add(key);
|
|
129
|
-
lastFired.set(rule.id, now());
|
|
130
|
-
try {
|
|
131
|
-
const result = await rule.then(event, ctx);
|
|
132
|
-
emit('automation.fired', { ruleId: rule.id, classUsed: rule.classUsed, eventId: event.id });
|
|
133
|
-
out.push({ ruleId: rule.id, fired: true, result });
|
|
134
|
-
} catch (e) {
|
|
135
|
-
emit('automation.suppressed', { ruleId: rule.id, reason: SUPPRESSED.ERROR, eventId: event.id, message: e.message });
|
|
136
|
-
out.push({ ruleId: rule.id, fired: false, reason: SUPPRESSED.ERROR, error: e.message });
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
return out;
|
|
140
|
-
},
|
|
141
|
-
};
|
|
142
|
-
}
|
|
1
|
+
class p extends Error{constructor(l,i){super(i),this.name="RuleError",this.code=l}}const u=Object.freeze({DISABLED:"disabled",CONDITION:"condition-false",DUPLICATE:"already-fired",RATE_LIMITED:"rate-limited",NO_PERMISSION:"no-permission",ERROR:"error"});function y({id:t,label:l,on:i,when:c=null,then:o,classUsed:f="R",everyMs:R=0,requiresApproval:r=!1,description:a="",effects:d="idempotent"}){if(!t)throw new p("BAD_RULE","rule.id required");if(typeof o!="function")throw new p("BAD_RULE",`rule '${t}': then required`);const e=Array.isArray(i)?i:[i];if(!e.length||e.some(n=>typeof n!="string"||!n))throw new p("BAD_RULE",`rule '${t}': on must name at least one event type`);if(c&&typeof c!="function")throw new p("BAD_RULE",`rule '${t}': when must be a function`);return Object.freeze({id:t,label:l||t,on:e,when:c,then:o,classUsed:f,everyMs:R,requiresApproval:r,description:a,effects:d})}function D({emit:t=()=>{},now:l=()=>0,admit:i=null,approve:c=null}={}){const o=[],f=new Map,R=new Set;return{add(r){return o.push(r),()=>{const a=o.indexOf(r);a>=0&&o.splice(a,1)}},list:()=>[...o],async dispatch(r,a={}){const d=[];for(const e of o){if(!e.on.includes(r?.type))continue;const n=(s,h)=>{t("automation.suppressed",{ruleId:e.id,reason:s,eventId:r.id,...h}),d.push({ruleId:e.id,fired:!1,reason:s})};if(i&&!i(e)){n(u.DISABLED);continue}const I=`${e.id}:${r.id}`;if(R.has(I)){n(u.DUPLICATE);continue}if(e.everyMs>0&&f.has(e.id)&&l()-f.get(e.id)<e.everyMs){n(u.RATE_LIMITED);continue}let E=!0;try{E=e.when?!!e.when(r,a):!0}catch(s){n(u.ERROR,{message:s.message});continue}if(!E){n(u.CONDITION);continue}if(e.requiresApproval&&!(c?await c(e,r):!1)){n(u.NO_PERMISSION);continue}R.add(I),f.set(e.id,l());try{const s=await e.then(r,a);t("automation.fired",{ruleId:e.id,classUsed:e.classUsed,eventId:r.id}),d.push({ruleId:e.id,fired:!0,result:s})}catch(s){t("automation.suppressed",{ruleId:e.id,reason:u.ERROR,eventId:r.id,message:s.message}),d.push({ruleId:e.id,fired:!1,reason:u.ERROR,error:s.message})}}return d}}}export{p as RuleError,u as SUPPRESSED,D as createRuleEngine,y as defineRule};
|