@butlerbot/sdk 0.0.21 → 0.0.22
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/link/hook.d.ts +14 -1
- package/dist/link/hook.js +24 -0
- package/dist/link/link.d.ts +1 -1
- package/dist/link/link.js +11 -2
- package/dist/link/protocol.d.ts +1 -0
- package/dist/link/tool.d.ts +11 -0
- package/package.json +1 -1
- package/readme.md +16 -2
package/dist/link/hook.d.ts
CHANGED
|
@@ -42,7 +42,7 @@ export type HookEmitter = {
|
|
|
42
42
|
* Reports an event against the subscriptions it matched, sending nothing when it
|
|
43
43
|
* matched none. Returns the ids that were reported.
|
|
44
44
|
*/
|
|
45
|
-
reportHookEvent(hookId: string, event: string, payload?: Record<string, unknown
|
|
45
|
+
reportHookEvent(hookId: string, event: string, payload?: Record<string, unknown>, subscriptionIds?: string[]): Promise<string[]>;
|
|
46
46
|
/** The subscriptions the server has pushed for this hook's source. */
|
|
47
47
|
hookSubscriptions(hookId: string): LinkSubscription[];
|
|
48
48
|
};
|
|
@@ -99,4 +99,17 @@ export declare class Hook<S extends ToolSchema | undefined = undefined> {
|
|
|
99
99
|
* disconnect — nothing is persisted, because the server re-sends it on every connect.
|
|
100
100
|
*/
|
|
101
101
|
get subscriptions(): LinkSubscription[];
|
|
102
|
+
/**
|
|
103
|
+
* Reports an event to subscriptions you picked yourself.
|
|
104
|
+
*
|
|
105
|
+
* The escape hatch from the prefilter, for conditions that are not field equality: "mentions my
|
|
106
|
+
* user", "within 50 metres", "the third time today". Read `subscriptions`, decide with real code —
|
|
107
|
+
* `identities` is there for the "is this about my user" half — and pass the ids you chose.
|
|
108
|
+
*
|
|
109
|
+
* The prefilter is deliberately not applied to these: you already decided. What is checked is that
|
|
110
|
+
* each id is one this link currently holds for this hook, so a stale id is dropped rather than sent
|
|
111
|
+
* and rejected. Unknown ids are dropped quietly, because a subscription disappearing between your
|
|
112
|
+
* decision and this call is a race, not a mistake.
|
|
113
|
+
*/
|
|
114
|
+
reportTo(subscriptionIds: string[], event: string, payload?: Record<string, unknown>): Promise<string[]>;
|
|
102
115
|
}
|
package/dist/link/hook.js
CHANGED
|
@@ -92,5 +92,29 @@ class Hook {
|
|
|
92
92
|
get subscriptions() {
|
|
93
93
|
return this.link?.hookSubscriptions(this.id) ?? [];
|
|
94
94
|
}
|
|
95
|
+
/**
|
|
96
|
+
* Reports an event to subscriptions you picked yourself.
|
|
97
|
+
*
|
|
98
|
+
* The escape hatch from the prefilter, for conditions that are not field equality: "mentions my
|
|
99
|
+
* user", "within 50 metres", "the third time today". Read `subscriptions`, decide with real code —
|
|
100
|
+
* `identities` is there for the "is this about my user" half — and pass the ids you chose.
|
|
101
|
+
*
|
|
102
|
+
* The prefilter is deliberately not applied to these: you already decided. What is checked is that
|
|
103
|
+
* each id is one this link currently holds for this hook, so a stale id is dropped rather than sent
|
|
104
|
+
* and rejected. Unknown ids are dropped quietly, because a subscription disappearing between your
|
|
105
|
+
* decision and this call is a race, not a mistake.
|
|
106
|
+
*/
|
|
107
|
+
async reportTo(subscriptionIds, event, payload) {
|
|
108
|
+
if (!this.link) {
|
|
109
|
+
throw new Error(`Hook "${this.id}" is not on a link yet — call link.addHook(hook) first.`);
|
|
110
|
+
}
|
|
111
|
+
if (!this.config.events.some(declared => declared.name === event)) {
|
|
112
|
+
const declared = this.config.events.map(entry => entry.name).join(", ") || "none";
|
|
113
|
+
throw new Error(`Hook "${this.id}" does not declare an event named "${event}". Declared: ${declared}.`);
|
|
114
|
+
}
|
|
115
|
+
if (subscriptionIds.length === 0)
|
|
116
|
+
return [];
|
|
117
|
+
return this.link.reportHookEvent(this.id, event, payload, subscriptionIds);
|
|
118
|
+
}
|
|
95
119
|
}
|
|
96
120
|
exports.Hook = Hook;
|
package/dist/link/link.d.ts
CHANGED
|
@@ -142,7 +142,7 @@ export declare class Link {
|
|
|
142
142
|
* The epoch travels with the frame so the server can tell a stale view from a bad one — an id
|
|
143
143
|
* that was valid a moment ago is a race, not a bug worth complaining about.
|
|
144
144
|
*/
|
|
145
|
-
reportHookEvent(hookId: string, event: string, payload?: Record<string, unknown
|
|
145
|
+
reportHookEvent(hookId: string, event: string, payload?: Record<string, unknown>, chosenIds?: string[]): Promise<string[]>;
|
|
146
146
|
/** Called by `Hook.subscriptions`. */
|
|
147
147
|
hookSubscriptions(hookId: string): LinkSubscription[];
|
|
148
148
|
/** Everything this link has been asked to watch, across all of its hooks. */
|
package/dist/link/link.js
CHANGED
|
@@ -268,10 +268,19 @@ class Link {
|
|
|
268
268
|
* The epoch travels with the frame so the server can tell a stale view from a bad one — an id
|
|
269
269
|
* that was valid a moment ago is a race, not a bug worth complaining about.
|
|
270
270
|
*/
|
|
271
|
-
async reportHookEvent(hookId, event, payload) {
|
|
271
|
+
async reportHookEvent(hookId, event, payload, chosenIds) {
|
|
272
272
|
await this.ready();
|
|
273
273
|
const sourceId = this.hooks.get(hookId)?.sourceId ?? hookId;
|
|
274
|
-
|
|
274
|
+
// Explicit ids are still checked against what this link actually holds. Not out of distrust of
|
|
275
|
+
// the caller — the server checks again anyway — but because an id it was never given can only
|
|
276
|
+
// be a bug or a race with a removal, and both are better as a dropped report than as a frame
|
|
277
|
+
// the server rejects. Dropped rather than thrown: a subscription vanishing mid-event is normal.
|
|
278
|
+
const subscriptionIds = chosenIds
|
|
279
|
+
? chosenIds.filter(id => this.subscriptionStore.get(id)?.sourceId === sourceId)
|
|
280
|
+
: this.subscriptionStore.match({ sourceId, event, payload });
|
|
281
|
+
if (chosenIds && subscriptionIds.length !== chosenIds.length) {
|
|
282
|
+
this.debug(`dropped ${chosenIds.length - subscriptionIds.length} unknown subscription id(s) on ${event}`);
|
|
283
|
+
}
|
|
275
284
|
if (subscriptionIds.length === 0)
|
|
276
285
|
return [];
|
|
277
286
|
this.send("hook.event", {
|
package/dist/link/protocol.d.ts
CHANGED
package/dist/link/tool.d.ts
CHANGED
|
@@ -24,6 +24,17 @@ export type ToolCallMeta = {
|
|
|
24
24
|
chatId?: string;
|
|
25
25
|
/** Unique per call, useful for logs. */
|
|
26
26
|
runId: string;
|
|
27
|
+
/**
|
|
28
|
+
* The user's linked accounts, in namespaces you understand: `{ discord: "1897..." }`.
|
|
29
|
+
*
|
|
30
|
+
* Present only for global-scope links — a platform client acting *as* the user, which is the case
|
|
31
|
+
* that needs it: to kick somebody as them you need their Discord id, and resolving that from an
|
|
32
|
+
* Alfred user id is the server's job. A user-scoped link is already the user's own process and is
|
|
33
|
+
* told nothing extra.
|
|
34
|
+
*
|
|
35
|
+
* Absent when the user has linked nothing. Refuse the call rather than guessing.
|
|
36
|
+
*/
|
|
37
|
+
identities?: Record<string, string>;
|
|
27
38
|
};
|
|
28
39
|
export type ToolRunContext<S extends ToolSchema | undefined> = {
|
|
29
40
|
/** Typed from `schema` when one was given. */
|
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -130,10 +130,24 @@ Why this is the better path:
|
|
|
130
130
|
|
|
131
131
|
`subscription.prefilter` is applied for you by `report` — a dot-path map of conditions, all ANDed,
|
|
132
132
|
scalars or arrays (`{ "author.bot": false, "channel.id": ["1", "2"] }`). It is a volume gate, not a
|
|
133
|
-
query language
|
|
134
|
-
ids you chose. Ignoring prefilters entirely is still *correct*, just louder — the server evaluates
|
|
133
|
+
query language. Ignoring prefilters entirely is still *correct*, just louder — the server evaluates
|
|
135
134
|
them again before spending anything.
|
|
136
135
|
|
|
136
|
+
For a condition that is not field equality — "mentions my user", "within 50 metres", "the third time
|
|
137
|
+
today" — decide with real code and use `reportTo`:
|
|
138
|
+
|
|
139
|
+
```ts
|
|
140
|
+
const mine = doorbell.subscriptions.filter(
|
|
141
|
+
(s) => s.identities?.discord && message.mentions.users.has(s.identities.discord),
|
|
142
|
+
);
|
|
143
|
+
|
|
144
|
+
await doorbell.reportTo(mine.map((s) => s.subscriptionId), "rang", payload);
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
`reportTo` does not apply the prefilter — you already decided. It does drop any id this link isn't
|
|
148
|
+
currently holding, so a subscription that disappeared between your decision and the call is a
|
|
149
|
+
dropped report rather than a rejected frame.
|
|
150
|
+
|
|
137
151
|
`subscription.identities` is how you answer "is this event about *my* user": a plain string map in
|
|
138
152
|
namespaces you understand, e.g. `{ discord: "1897..." }`, present only for owners who have linked
|
|
139
153
|
that account. Nothing else about the user is exposed.
|