@matatbread/matbot-triggers 0.4.14 → 0.4.17

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/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "node",
5
5
  "browser"
6
6
  ],
7
- "version": "0.4.14",
7
+ "version": "0.4.17",
8
8
  "engines": {
9
9
  "node": ">=24.0.0"
10
10
  },
@@ -33,11 +33,11 @@
33
33
  "access": "public"
34
34
  },
35
35
  "peerDependencies": {
36
- "@matatbread/matbot-plugin-api": "^0.4.14"
36
+ "@matatbread/matbot-plugin-api": "^0.4.17"
37
37
  },
38
38
  "devDependencies": {
39
39
  "typescript": "^6.0.3",
40
- "@matatbread/matbot-plugin-api": "^0.4.14"
40
+ "@matatbread/matbot-plugin-api": "^0.4.17"
41
41
  },
42
42
  "scripts": {
43
43
  "typecheck": "tsc --noEmit"
package/src/manager.ts CHANGED
@@ -81,7 +81,7 @@ export class TriggerManager implements Triggers {
81
81
  const now = new Date().toISOString();
82
82
  const doc: Trigger = {
83
83
  id: crypto.randomUUID(),
84
- version: Date.now().toString(),
84
+ version: crypto.randomUUID(),
85
85
  conditions: spec.conditions,
86
86
  invoke: spec.invoke,
87
87
  ...(spec.enabled !== undefined ? { enabled: spec.enabled } : {}),
@@ -102,7 +102,7 @@ export class TriggerManager implements Triggers {
102
102
  ...(patch.invoke !== undefined ? { invoke: patch.invoke } : {}),
103
103
  ...(patch.enabled !== undefined ? { enabled: patch.enabled } : {}),
104
104
  ...(patch.cooldown !== undefined ? { cooldown: patch.cooldown } : {}),
105
- version: Date.now().toString(),
105
+ version: crypto.randomUUID(),
106
106
  updatedAt: new Date().toISOString(),
107
107
  }));
108
108
  }
package/src/tools.ts CHANGED
@@ -1,27 +1,27 @@
1
1
  import type { Tool, ToolExecutor, ToolContract, ToolResultOf, ToolContext, MatbotMachine } from '@matatbread/matbot-plugin-api';
2
2
  import type { TriggerManager } from './manager.js';
3
3
  import { checkInvocation } from './dispatch.js';
4
- import type { TriggerCondition, TriggerCooldown, TriggerKind, Trigger } from './types.js';
4
+ import type { TriggerCondition, TriggerCooldown, TriggerKind, Trigger, ReportedTrigger } from './types.js';
5
5
 
6
6
  declare module '@matatbread/matbot-plugin-api' {
7
7
  interface ToolContracts {
8
8
  // One arm per action: a caller of `invokeTool(machine, 'trigger_action', { action: '…' })` gets the
9
9
  // matching result narrowed by the `action` it passed (see ToolContract / the multi-action note on ToolContracts).
10
10
  trigger_action:
11
- | ToolContract<{ triggers: Trigger[] }, { action: 'list' }>
12
- | ToolContract<{ triggers: Trigger[] }, { action: 'query'; tool?: string; params?: object }>
13
- | ToolContract<Trigger, { action: 'get'; id: string }>
11
+ | ToolContract<{ triggers: ReportedTrigger[] }, { action: 'list' }>
12
+ | ToolContract<{ triggers: ReportedTrigger[] }, { action: 'query'; tool?: string; params?: object }>
13
+ | ToolContract<ReportedTrigger, { action: 'get'; id: string }>
14
14
  | ToolContract<{ id: string }, { action: 'add'; conditions: TriggerCondition[]; tool: string; params?: object; enabled?: boolean; cooldown?: TriggerCooldown }>
15
15
  // `cooldown: null` CLEARS every limit, which an omitted `cooldown` deliberately does not (it
16
16
  // leaves the stored one untouched). The executor has always accepted it and the web UI has always
17
17
  // sent it; only this arm omitted it, so a documented call was untypeable — invisible until the
18
18
  // params type was actually enforced. `add` takes no `null`: there is no prior limit to clear.
19
- | ToolContract<Trigger, { action: 'update'; id: string; conditions?: TriggerCondition[]; tool?: string; params?: object; enabled?: boolean; cooldown?: TriggerCooldown | null }>
20
- | ToolContract<Trigger, { action: 'disable'; id: string }>
21
- | ToolContract<Trigger, { action: 'enable'; id: string }>
22
- | ToolContract<{ id: string }, { action: 'remove'; id: string }>
23
- | ToolContract<{ triggers: Trigger[] }, { action: 'move'; tool?: string; params?: object; toTool: string; toParams?: object }>
24
- | ToolContract<{ triggers: Trigger[] }, { action: 'copy'; tool?: string; params?: object; toTool: string; toParams?: object }>;
19
+ | ToolContract<ReportedTrigger, { action: 'update'; id: string; conditions?: TriggerCondition[]; tool?: string; params?: object; enabled?: boolean; cooldown?: TriggerCooldown | null }>
20
+ | ToolContract<ReportedTrigger, { action: 'disable'; id: string }>
21
+ | ToolContract<ReportedTrigger, { action: 'enable'; id: string }>
22
+ | ToolContract<{ id: string }, { action: 'remove'; id: string }>
23
+ | ToolContract<{ triggers: ReportedTrigger[] }, { action: 'move'; tool?: string; params?: object; toTool: string; toParams?: object }>
24
+ | ToolContract<{ triggers: ReportedTrigger[] }, { action: 'copy'; tool?: string; params?: object; toTool: string; toParams?: object }>;
25
25
  triggers_config:
26
26
  | ToolContract<{ classifierProvider: string | null; available: string[] }, { action: 'get' }>
27
27
  | ToolContract<{ classifierProvider: string }, { action: 'set'; provider: string }>
@@ -84,6 +84,11 @@ type TriggerActionInput =
84
84
  | { action: 'move'; tool?: string; params?: unknown; toTool: string; toParams?: unknown }
85
85
  | { action: 'copy'; tool?: string; params?: unknown; toTool: string; toParams?: unknown };
86
86
 
87
+ // `enabled` resolved for reporting (see ReportedTrigger): it is stored only when explicitly written, so
88
+ // a listing otherwise carried it for some triggers and not others, leaving a reader to know "absent ⇒
89
+ // enabled" rather than see it. Applied at the yield sites only — the stored document is untouched.
90
+ const reported = (t: Trigger): ReportedTrigger => ({ ...t, enabled: t.enabled !== false });
91
+
87
92
  // The query selector shared by query/move/copy: `tool`/`params` narrow the set by what a trigger invokes.
88
93
  function queryFilter(a: { tool?: unknown; params?: unknown }): { tool?: string; params?: unknown } {
89
94
  return {
@@ -124,13 +129,13 @@ export function createTriggerActionTool(manager: TriggerManager, services: Matbo
124
129
 
125
130
  switch (args.action) {
126
131
  case 'list': {
127
- yield { type: 'result', value: { triggers: await manager.all() } };
132
+ yield { type: 'result', value: { triggers: (await manager.all()).map(reported) } };
128
133
  return;
129
134
  }
130
135
 
131
136
  case 'query': {
132
137
  const a = args as Extract<TriggerActionInput, { action: 'query' }>;
133
- yield { type: 'result', value: { triggers: await manager.query(queryFilter(a)) } };
138
+ yield { type: 'result', value: { triggers: (await manager.query(queryFilter(a))).map(reported) } };
134
139
  return;
135
140
  }
136
141
 
@@ -139,7 +144,7 @@ export function createTriggerActionTool(manager: TriggerManager, services: Matbo
139
144
  if (!id) { yield { type: 'error', message: 'action "get" requires "id".' }; return; }
140
145
  const t = await manager.get(id);
141
146
  if (!t) { yield { type: 'error', message: `Trigger not found: "${id}"` }; return; }
142
- yield { type: 'result', value: t };
147
+ yield { type: 'result', value: reported(t) };
143
148
  return;
144
149
  }
145
150
 
@@ -183,7 +188,7 @@ export function createTriggerActionTool(manager: TriggerManager, services: Matbo
183
188
  ...(a.cooldown !== undefined ? { cooldown: (a.cooldown === null ? {} : a.cooldown) as TriggerCooldown } : {}),
184
189
  });
185
190
  if (t === undefined) { yield { type: 'error', message: `Trigger not found: "${a.id}"` }; return; }
186
- yield { type: 'result', value: t };
191
+ yield { type: 'result', value: reported(t) };
187
192
  return;
188
193
  }
189
194
 
@@ -196,7 +201,7 @@ export function createTriggerActionTool(manager: TriggerManager, services: Matbo
196
201
  if (!id) { yield { type: 'error', message: `action "${args.action}" requires "id".` }; return; }
197
202
  const t = await manager.update(id, { enabled: args.action === 'enable' });
198
203
  if (t === undefined) { yield { type: 'error', message: `Trigger not found: "${id}"` }; return; }
199
- yield { type: 'result', value: t };
204
+ yield { type: 'result', value: reported(t) };
200
205
  return;
201
206
  }
202
207
 
@@ -216,10 +221,10 @@ export function createTriggerActionTool(manager: TriggerManager, services: Matbo
216
221
  const invoke = { tool: a.toTool, ...(a.toParams !== undefined ? { params: a.toParams } : {}) };
217
222
  const refused = await checkInvocation(services, invoke);
218
223
  if (refused !== undefined) { yield { type: 'error', message: `No triggers moved — ${refused}.` }; return; }
219
- const triggers: Trigger[] = [];
224
+ const triggers: ReportedTrigger[] = [];
220
225
  for (const t of await manager.query(queryFilter(a))) {
221
226
  const updated = await manager.update(t.id, { invoke });
222
- if (updated !== undefined) triggers.push(updated);
227
+ if (updated !== undefined) triggers.push(reported(updated));
223
228
  }
224
229
  yield { type: 'result', value: { triggers } };
225
230
  return;
@@ -232,14 +237,14 @@ export function createTriggerActionTool(manager: TriggerManager, services: Matbo
232
237
  const invoke = { tool: a.toTool, ...(a.toParams !== undefined ? { params: a.toParams } : {}) };
233
238
  const refused = await checkInvocation(services, invoke);
234
239
  if (refused !== undefined) { yield { type: 'error', message: `No triggers copied — ${refused}.` }; return; }
235
- const triggers: Trigger[] = [];
240
+ const triggers: ReportedTrigger[] = [];
236
241
  for (const t of await manager.query(queryFilter(a))) {
237
- triggers.push(await manager.add({
242
+ triggers.push(reported(await manager.add({
238
243
  conditions: t.conditions,
239
244
  invoke,
240
245
  ...(t.enabled !== undefined ? { enabled: t.enabled } : {}),
241
246
  ...(t.cooldown !== undefined ? { cooldown: t.cooldown } : {}),
242
- }));
247
+ })));
243
248
  }
244
249
  yield { type: 'result', value: { triggers } };
245
250
  return;
package/src/types.ts CHANGED
@@ -107,6 +107,16 @@ export interface Trigger {
107
107
  updatedAt: string;
108
108
  }
109
109
 
110
+ /**
111
+ * A trigger as `trigger_action` REPORTS it: `enabled` resolved rather than omitted.
112
+ *
113
+ * Stored documents carry `enabled` only when it was explicitly written, so a listing showed it for some
114
+ * triggers and not others — and "absent ⇒ enabled" is a rule a reader has to know rather than see. The
115
+ * resolution happens at the tool boundary only: stored shape and {@link Triggers} consumers are
116
+ * untouched, so no write can persist a value nobody asked for.
117
+ */
118
+ export type ReportedTrigger = Omit<Trigger, 'enabled'> & { enabled: boolean };
119
+
110
120
  /** Fields a caller supplies when creating or replacing a trigger; identity/versioning is the store's. */
111
121
  export interface TriggerSpec {
112
122
  conditions: TriggerCondition[];