@mapier/imsg-sdk 0.1.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 (57) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +68 -0
  3. package/dist/gateway/external-ids.d.ts +3 -0
  4. package/dist/gateway/external-ids.js +37 -0
  5. package/dist/gateway/external-ids.js.map +1 -0
  6. package/dist/gateway/fake.d.ts +161 -0
  7. package/dist/gateway/fake.js +870 -0
  8. package/dist/gateway/fake.js.map +1 -0
  9. package/dist/gateway/imsg.d.ts +77 -0
  10. package/dist/gateway/imsg.js +676 -0
  11. package/dist/gateway/imsg.js.map +1 -0
  12. package/dist/gateway/portable-chat.d.ts +65 -0
  13. package/dist/gateway/portable-chat.js +118 -0
  14. package/dist/gateway/portable-chat.js.map +1 -0
  15. package/dist/gateway/types.d.ts +169 -0
  16. package/dist/gateway/types.js +16 -0
  17. package/dist/gateway/types.js.map +1 -0
  18. package/dist/imsg/binary.d.ts +1 -0
  19. package/dist/imsg/binary.js +4 -0
  20. package/dist/imsg/binary.js.map +1 -0
  21. package/dist/imsg/react.d.ts +2 -0
  22. package/dist/imsg/react.js +24 -0
  23. package/dist/imsg/react.js.map +1 -0
  24. package/dist/imsg/rpc.d.ts +145 -0
  25. package/dist/imsg/rpc.js +227 -0
  26. package/dist/imsg/rpc.js.map +1 -0
  27. package/dist/imsg/status.d.ts +9 -0
  28. package/dist/imsg/status.js +37 -0
  29. package/dist/imsg/status.js.map +1 -0
  30. package/dist/index.d.ts +9 -0
  31. package/dist/index.js +9 -0
  32. package/dist/index.js.map +1 -0
  33. package/dist/interactions/catalog.d.ts +2 -0
  34. package/dist/interactions/catalog.js +98 -0
  35. package/dist/interactions/catalog.js.map +1 -0
  36. package/dist/interactions/harness.d.ts +25 -0
  37. package/dist/interactions/harness.js +69 -0
  38. package/dist/interactions/harness.js.map +1 -0
  39. package/dist/interactions/index.d.ts +5 -0
  40. package/dist/interactions/index.js +5 -0
  41. package/dist/interactions/index.js.map +1 -0
  42. package/dist/interactions/registry.d.ts +9 -0
  43. package/dist/interactions/registry.js +33 -0
  44. package/dist/interactions/registry.js.map +1 -0
  45. package/dist/interactions/types.d.ts +28 -0
  46. package/dist/interactions/types.js +5 -0
  47. package/dist/interactions/types.js.map +1 -0
  48. package/dist/polls.d.ts +12 -0
  49. package/dist/polls.js +102 -0
  50. package/dist/polls.js.map +1 -0
  51. package/dist/runtime-report.d.ts +3 -0
  52. package/dist/runtime-report.js +10 -0
  53. package/dist/runtime-report.js.map +1 -0
  54. package/dist/types.d.ts +73 -0
  55. package/dist/types.js +2 -0
  56. package/dist/types.js.map +1 -0
  57. package/package.json +59 -0
@@ -0,0 +1,227 @@
1
+ import { spawn } from 'node:child_process';
2
+ import { createInterface } from 'node:readline';
3
+ import { imsgBinary } from './binary.js';
4
+ // One long-lived `imsg rpc` child. JSON-RPC 2.0, one object per line on
5
+ // stdin/stdout; notifications (method: "message") carry watch events.
6
+ export class ImsgRpc {
7
+ onMessage;
8
+ child;
9
+ nextId = 1;
10
+ pending = new Map();
11
+ stopping = false;
12
+ constructor(onMessage, onExit) {
13
+ this.onMessage = onMessage;
14
+ this.child = spawn(imsgBinary(), ['rpc'], { stdio: ['pipe', 'pipe', 'inherit'] });
15
+ const rl = createInterface({ input: this.child.stdout });
16
+ rl.on('line', (line) => this.handleLine(line));
17
+ this.child.on('exit', (code) => {
18
+ for (const p of this.pending.values())
19
+ p.reject(new Error('imsg rpc exited'));
20
+ this.pending.clear();
21
+ if (!this.stopping)
22
+ onExit(code);
23
+ });
24
+ }
25
+ handleLine(line) {
26
+ let msg;
27
+ try {
28
+ msg = JSON.parse(line);
29
+ }
30
+ catch {
31
+ console.error('[rpc] unparseable line:', line.slice(0, 200));
32
+ return;
33
+ }
34
+ if (msg.id !== undefined) {
35
+ const p = this.pending.get(Number(msg.id));
36
+ if (!p)
37
+ return;
38
+ this.pending.delete(Number(msg.id));
39
+ if (msg.error)
40
+ p.reject(new Error(`imsg rpc ${msg.error.code}: ${msg.error.message}`));
41
+ else
42
+ p.resolve(msg.result);
43
+ }
44
+ else if (msg.method === 'message' && msg.params) {
45
+ this.onMessage(msg.params.message);
46
+ }
47
+ }
48
+ request(method, params) {
49
+ const id = this.nextId++;
50
+ const payload = JSON.stringify({ jsonrpc: '2.0', id, method, params: params ?? {} });
51
+ return new Promise((resolve, reject) => {
52
+ this.pending.set(id, { resolve: resolve, reject });
53
+ this.child.stdin.write(`${payload}\n`);
54
+ });
55
+ }
56
+ // since_rowid is an exclusive cursor — pass the last rowid already handled.
57
+ async subscribe(sinceRowid) {
58
+ const res = await this.request('watch.subscribe', {
59
+ ...(sinceRowid ? { since_rowid: sinceRowid } : {}),
60
+ include_reactions: true,
61
+ attachments: true,
62
+ });
63
+ return res.subscription;
64
+ }
65
+ send(target, text) {
66
+ const params = 'chatId' in target ? { chat_id: target.chatId, text } : { to: target.to, text };
67
+ return this.request('send', params);
68
+ }
69
+ async history(chatId, limit = 30) {
70
+ const res = await this.request('messages.history', {
71
+ chat_id: chatId,
72
+ limit,
73
+ attachments: true,
74
+ });
75
+ // Order is not documented — normalize to oldest-first by rowid.
76
+ return [...res.messages].sort((a, b) => a.id - b.id);
77
+ }
78
+ async historyRange(chatId, options) {
79
+ const res = await this.request('messages.history', {
80
+ chat_id: chatId,
81
+ limit: options.limit ?? 500,
82
+ ...(options.startAt ? { start: options.startAt } : {}),
83
+ ...(options.endBefore ? { end: options.endBefore } : {}),
84
+ ...(options.includeAttachments ? { attachments: true } : {}),
85
+ });
86
+ const startMs = options.startAt ? new Date(options.startAt).getTime() : null;
87
+ const endMs = options.endBefore ? new Date(options.endBefore).getTime() : null;
88
+ return [...res.messages]
89
+ .filter((message) => {
90
+ if (!message.created_at)
91
+ return startMs === null && endMs === null;
92
+ const occurredAt = new Date(message.created_at).getTime();
93
+ return (startMs === null || occurredAt >= startMs) && (endMs === null || occurredAt < endMs);
94
+ })
95
+ .sort((a, b) => a.id - b.id);
96
+ }
97
+ // Verified live against imsg 0.12.0 (2026-07-02): `chats.list` accepts
98
+ // `{ limit }`, returns `{ chats }` ordered newest-first by last_message_at.
99
+ async chats(limit = 20) {
100
+ const res = await this.request('chats.list', { limit });
101
+ return res.chats;
102
+ }
103
+ // ---- Tier 2 (bridge-backed RPC methods — openclaw/imsg docs/rpc.md
104
+ // "Bridge Message Actions" + the `kSupportedRPCMethods` dispatch table in
105
+ // openclaw's RPCServer.swift). All target an existing chat_id; none has a
106
+ // find-or-create `to:` form the way send() does. ----
107
+ // `tapback`: message_guid/kind/remove params (RPCServer+BridgeMessageHandlers.swift
108
+ // handleTapback → normalizeBridgeReactionType). `kind` accepts our Reaction
109
+ // values directly — the normalizer takes "emphasis" as an alias for its
110
+ // canonical "emphasize" (and the other 5 names match verbatim) — and
111
+ // rejects anything outside the closed 6-type set. The patched fork's
112
+ // arbitrary-emoji parameter is exposed separately below.
113
+ tapback(chatId, targetGuid, reaction, remove = false) {
114
+ return this.request('tapback', { chat_id: chatId, message_guid: targetGuid, kind: reaction, remove });
115
+ }
116
+ // Mapier fork extension. The `emoji` parameter deliberately replaces
117
+ // `kind`; sending both would route through stock imsg's classic-6
118
+ // normalizer and reject the custom emoji.
119
+ emojiTapback(chatId, targetGuid, emoji, remove = false) {
120
+ return this.request('tapback', { chat_id: chatId, message_guid: targetGuid, emoji, remove });
121
+ }
122
+ // Verified against openclaw/imsg 0.13.x source: `send.rich` text mode takes
123
+ // text + optional subject/effect/reply_to (handleSendRich).
124
+ sendRich(chatId, text, opts = {}) {
125
+ return this.request('send.rich', {
126
+ chat_id: chatId,
127
+ text,
128
+ ...(opts.subject !== undefined ? { subject: opts.subject } : {}),
129
+ ...(opts.effect ? { effect: opts.effect } : {}),
130
+ ...(opts.replyToGuid ? { reply_to: opts.replyToGuid } : {}),
131
+ });
132
+ }
133
+ // Verified against openclaw/imsg 0.13.x source: `send.attachment`
134
+ // (handleSendAttachment) takes file, optional audio:true, and reply_to.
135
+ sendAttachment(chatId, filePath, opts = {}) {
136
+ return this.request('send.attachment', {
137
+ chat_id: chatId,
138
+ file: filePath,
139
+ ...(opts.audio ? { audio: true } : {}),
140
+ ...(opts.replyToGuid ? { reply_to: opts.replyToGuid } : {}),
141
+ });
142
+ }
143
+ // Wire shape transcribed from the Mapier fork's docs/rpc.md
144
+ // `message.send_status` response (mapier/macos26-tier2).
145
+ messageSendStatus(guid) {
146
+ return this.request('message.send_status', { guid });
147
+ }
148
+ // Wire shape transcribed from the Mapier fork's docs/rpc.md
149
+ // `handles.check` response (mapier/macos26-tier2).
150
+ checkHandle(address, opts = {}) {
151
+ return this.request('handles.check', {
152
+ address,
153
+ ...(opts.aliasType ? { alias_type: opts.aliasType } : {}),
154
+ });
155
+ }
156
+ // Wire shape transcribed from the Mapier fork's docs/rpc.md
157
+ // `send.sticker` response (mapier/macos26-tier2). part_index is invalid
158
+ // without attach_to, so it is omitted unless both are supplied.
159
+ sendSticker(chatId, filePath, opts = {}) {
160
+ return this.request('send.sticker', {
161
+ chat_id: chatId,
162
+ file: filePath,
163
+ ...(opts.attachToGuid ? { attach_to: opts.attachToGuid } : {}),
164
+ ...(opts.attachToGuid && opts.partIndex !== undefined ? { part_index: opts.partIndex } : {}),
165
+ });
166
+ }
167
+ // Verified against openclaw/imsg 0.13.x source: `poll.send`
168
+ // (handlePollSend) takes a question and at least two options.
169
+ sendPoll(chatId, question, options) {
170
+ return this.request('poll.send', { chat_id: chatId, question, options });
171
+ }
172
+ // Verified against openclaw/imsg 0.13.x source: `send.rich` URL mode
173
+ // (handleSendRichLink) accepts only the URL alongside the chat id.
174
+ sendRichLink(chatId, url) {
175
+ return this.request('send.rich', { chat_id: chatId, url });
176
+ }
177
+ // `message.edit`: text is required; backwards-compatibility text defaults
178
+ // to the same value when omitted (handleMessageEdit).
179
+ editMessage(chatId, targetGuid, text) {
180
+ return this.request('message.edit', { chat_id: chatId, message_guid: targetGuid, text });
181
+ }
182
+ unsendMessage(chatId, targetGuid) {
183
+ return this.request('message.unsend', { chat_id: chatId, message_guid: targetGuid });
184
+ }
185
+ deleteMessage(chatId, targetGuid) {
186
+ return this.request('message.delete', { chat_id: chatId, message_guid: targetGuid });
187
+ }
188
+ // `typing`/`read` (RPCServer+Handlers.swift handleTyping/handleRead) — no
189
+ // observable verification surface through the Gateway (contract §2).
190
+ setTyping(chatId, on) {
191
+ return this.request('typing', { chat_id: chatId, typing: on });
192
+ }
193
+ markRead(chatId) {
194
+ return this.request('read', { chat_id: chatId });
195
+ }
196
+ // `group.*` (RPCServer+ChatHandlers.swift) — all resolve chat_id/chat_guid/
197
+ // chat_identifier the same way as the other bridge methods.
198
+ renameGroup(chatId, name) {
199
+ return this.request('group.rename', { chat_id: chatId, name });
200
+ }
201
+ // Omitting `file` clears the photo (mirrors CLI `imsg chat-photo` with no
202
+ // `--file`).
203
+ setGroupPhoto(chatId, filePath) {
204
+ return this.request('group.setIcon', { chat_id: chatId, ...(filePath ? { file: filePath } : {}) });
205
+ }
206
+ addParticipant(chatId, handle) {
207
+ return this.request('group.addParticipant', { chat_id: chatId, address: handle });
208
+ }
209
+ removeParticipant(chatId, handle) {
210
+ return this.request('group.removeParticipant', { chat_id: chatId, address: handle });
211
+ }
212
+ leaveGroup(chatId) {
213
+ return this.request('group.leave', { chat_id: chatId });
214
+ }
215
+ namePhotoStatus(chatId) {
216
+ return this.request('contacts.shouldShareContact', { chat_id: chatId });
217
+ }
218
+ shareNamePhoto(chatId) {
219
+ return this.request('contacts.shareContactCard', { chat_id: chatId });
220
+ }
221
+ stop() {
222
+ this.stopping = true;
223
+ this.child.stdin.end();
224
+ this.child.kill();
225
+ }
226
+ }
227
+ //# sourceMappingURL=rpc.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rpc.js","sourceRoot":"","sources":["../../src/imsg/rpc.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import type { GatewayCapabilities } from '../gateway/types.js';
2
+ interface ImsgStatusPayload {
3
+ selectors?: Record<string, boolean>;
4
+ rpc_features?: string[];
5
+ }
6
+ export declare const NO_PATCHED_GATEWAY_CAPABILITIES: GatewayCapabilities;
7
+ export declare function patchedCapabilitiesFromStatus(status: ImsgStatusPayload): GatewayCapabilities;
8
+ export declare function readPatchedGatewayCapabilities(): GatewayCapabilities;
9
+ export {};
@@ -0,0 +1,37 @@
1
+ import { execFileSync } from 'node:child_process';
2
+ import { reportRuntimeIssue } from '../runtime-report.js';
3
+ import { imsgBinary } from './binary.js';
4
+ export const NO_PATCHED_GATEWAY_CAPABILITIES = {
5
+ emojiTapback: false,
6
+ stickerSend: false,
7
+ groupPhoto: false,
8
+ groupParticipants: false,
9
+ namePhotoSharing: false,
10
+ };
11
+ export function patchedCapabilitiesFromStatus(status) {
12
+ const selectors = status.selectors ?? {};
13
+ return {
14
+ emojiTapback: selectors.emojiTapbackSend === true && status.rpc_features?.includes('tapback.emoji') === true,
15
+ stickerSend: selectors.stickerSend === true,
16
+ groupPhoto: selectors.groupPhotoUpdate === true,
17
+ groupParticipants: selectors.groupAddParticipant === true && selectors.groupRemoveParticipant === true,
18
+ namePhotoSharing: selectors.namePhotoShouldOffer === true && selectors.namePhotoShare === true,
19
+ };
20
+ }
21
+ export function readPatchedGatewayCapabilities() {
22
+ try {
23
+ const stdout = execFileSync(imsgBinary(), ['status', '--json'], {
24
+ encoding: 'utf8',
25
+ timeout: 10_000,
26
+ });
27
+ return patchedCapabilitiesFromStatus(JSON.parse(stdout));
28
+ }
29
+ catch {
30
+ // Fail closed, but never silently: a probe failure downgrades the pod to
31
+ // stock-imsg capability, and an operator has to be able to see why every
32
+ // patched verb vanished. Closed code only — no ambient console here.
33
+ reportRuntimeIssue('imsg_capability_probe_failed');
34
+ return { ...NO_PATCHED_GATEWAY_CAPABILITIES };
35
+ }
36
+ }
37
+ //# sourceMappingURL=status.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status.js","sourceRoot":"","sources":["../../src/imsg/status.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAOzC,MAAM,CAAC,MAAM,+BAA+B,GAAwB;IAClE,YAAY,EAAE,KAAK;IACnB,WAAW,EAAE,KAAK;IAClB,UAAU,EAAE,KAAK;IACjB,iBAAiB,EAAE,KAAK;IACxB,gBAAgB,EAAE,KAAK;CACxB,CAAC;AAEF,MAAM,UAAU,6BAA6B,CAAC,MAAyB;IACrE,MAAM,SAAS,GAAG,MAAM,CAAC,SAAS,IAAI,EAAE,CAAC;IACzC,OAAO;QACL,YAAY,EAAE,SAAS,CAAC,gBAAgB,KAAK,IAAI,IAAI,MAAM,CAAC,YAAY,EAAE,QAAQ,CAAC,eAAe,CAAC,KAAK,IAAI;QAC5G,WAAW,EAAE,SAAS,CAAC,WAAW,KAAK,IAAI;QAC3C,UAAU,EAAE,SAAS,CAAC,gBAAgB,KAAK,IAAI;QAC/C,iBAAiB,EAAE,SAAS,CAAC,mBAAmB,KAAK,IAAI,IAAI,SAAS,CAAC,sBAAsB,KAAK,IAAI;QACtG,gBAAgB,EAAE,SAAS,CAAC,oBAAoB,KAAK,IAAI,IAAI,SAAS,CAAC,cAAc,KAAK,IAAI;KAC/F,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,8BAA8B;IAC5C,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,YAAY,CAAC,UAAU,EAAE,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC,EAAE;YAC9D,QAAQ,EAAE,MAAM;YAChB,OAAO,EAAE,MAAM;SAChB,CAAC,CAAC;QACH,OAAO,6BAA6B,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAsB,CAAC,CAAC;IAChF,CAAC;IAAC,MAAM,CAAC;QACP,yEAAyE;QACzE,yEAAyE;QACzE,qEAAqE;QACrE,kBAAkB,CAAC,8BAA8B,CAAC,CAAC;QACnD,OAAO,EAAE,GAAG,+BAA+B,EAAE,CAAC;IAChD,CAAC;AACH,CAAC"}
@@ -0,0 +1,9 @@
1
+ export { FakeGateway } from './gateway/fake.js';
2
+ export { ImsgGateway } from './gateway/imsg.js';
3
+ export * from './gateway/types.js';
4
+ export * from './gateway/portable-chat.js';
5
+ export * from './gateway/external-ids.js';
6
+ export * from './polls.js';
7
+ export { reportRuntimeIssue, setRuntimeIssueReporter, type RuntimeIssueReporter } from './runtime-report.js';
8
+ export type { MessageAttachment, MessagePoll, MessageReaction, PollOption, PollVote } from './types.js';
9
+ export * from './interactions/index.js';
package/dist/index.js ADDED
@@ -0,0 +1,9 @@
1
+ export { FakeGateway } from './gateway/fake.js';
2
+ export { ImsgGateway } from './gateway/imsg.js';
3
+ export * from './gateway/types.js';
4
+ export * from './gateway/portable-chat.js';
5
+ export * from './gateway/external-ids.js';
6
+ export * from './polls.js';
7
+ export { reportRuntimeIssue, setRuntimeIssueReporter } from './runtime-report.js';
8
+ export * from './interactions/index.js';
9
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,cAAc,oBAAoB,CAAC;AACnC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,YAAY,CAAC;AAC3B,OAAO,EAAE,kBAAkB,EAAE,uBAAuB,EAA6B,MAAM,qBAAqB,CAAC;AAE7G,cAAc,yBAAyB,CAAC"}
@@ -0,0 +1,2 @@
1
+ import { InteractionRegistry } from './registry.js';
2
+ export declare function defaultCatalog(): InteractionRegistry;
@@ -0,0 +1,98 @@
1
+ // Today's honest inventory of interaction kinds. Every claim about OUR
2
+ // Gateway is checked against docs/gateway-contract.md and docs/imsg-polls.md;
3
+ // claims about external products (Photon, Linq) are cited but not verifiable
4
+ // from this repo. See docs/interactions.md for the full citations and the
5
+ // corrections made against earlier catalog drafts.
6
+ import { InteractionRegistry } from './registry.js';
7
+ const TAPBACK = {
8
+ id: 'tapback',
9
+ tier: 'native-balloon',
10
+ requiresRecipientInstall: false,
11
+ correlation: 'message-guid',
12
+ completionTransport: 'gateway-events',
13
+ outbound: 'agent',
14
+ inboundReadback: true,
15
+ status: 'host-smoked',
16
+ fallback: 'plain-text',
17
+ notes: 'Gateway.tapback (guid-targeted, bridge); react() has no guid param. Host-verified: tier2-smoke.ts.',
18
+ };
19
+ const WEB_LINK = {
20
+ id: 'web-link',
21
+ tier: 'web-surface',
22
+ requiresRecipientInstall: false,
23
+ correlation: 'link-code',
24
+ completionTransport: 'out-of-band',
25
+ outbound: 'agent',
26
+ inboundReadback: false,
27
+ status: 'host-smoked',
28
+ fallback: 'none', // this IS the floor — nothing to fall back to
29
+ notes: 'Completion via product store/webhook, not this SDK; verified in imsg-agent link-surface, not here.',
30
+ };
31
+ const POLL = {
32
+ id: 'poll',
33
+ tier: 'native-balloon',
34
+ requiresRecipientInstall: false,
35
+ correlation: 'message-guid',
36
+ completionTransport: 'gateway-events',
37
+ outbound: 'agent',
38
+ inboundReadback: true,
39
+ status: 'host-smoked',
40
+ fallback: 'plain-text',
41
+ notes: 'sendPoll creates only, host-verified delivering; voting has no Gateway path (forbidden surface).',
42
+ };
43
+ const EFFECT = {
44
+ id: 'effect',
45
+ tier: 'native-balloon',
46
+ requiresRecipientInstall: false,
47
+ correlation: 'none',
48
+ completionTransport: 'none',
49
+ outbound: 'agent',
50
+ inboundReadback: false,
51
+ status: 'host-smoked',
52
+ fallback: 'plain-text',
53
+ notes: 'Gateway.sendRich opts.effect already provides all 12 (4 bubble+8 screen); host-verified live.',
54
+ };
55
+ const RICH_LINK_CARD = {
56
+ id: 'rich-link-card',
57
+ tier: 'native-balloon',
58
+ requiresRecipientInstall: false,
59
+ correlation: 'none',
60
+ completionTransport: 'none',
61
+ outbound: 'none',
62
+ inboundReadback: false,
63
+ status: 'host-smoked',
64
+ fallback: 'plain-text',
65
+ notes: 'sendRichLink delivers on builds carrying the fork richlink-sync-fix (host-verified 2026-07-14); outbound stays none until the pinned release ships that patch — no capability marker gates it. See docs/interactions.md.',
66
+ };
67
+ const APP_CLIP = {
68
+ id: 'app-clip',
69
+ tier: 'app-clip',
70
+ requiresRecipientInstall: false, // App Clips are ephemeral — no App Store install
71
+ correlation: 'link-code',
72
+ completionTransport: 'out-of-band',
73
+ outbound: 'none',
74
+ inboundReadback: false,
75
+ status: 'unbuilt',
76
+ fallback: 'web-url',
77
+ notes: 'Linq Agent Pay model: link opens a native checkout sheet, no install. Unbuilt seam.',
78
+ };
79
+ const HOST_EXTENSION_CARD = {
80
+ id: 'host-extension-card',
81
+ tier: 'extension',
82
+ requiresRecipientInstall: true,
83
+ correlation: 'message-guid',
84
+ completionTransport: 'gateway-events',
85
+ outbound: 'none',
86
+ inboundReadback: false,
87
+ status: 'unbuilt',
88
+ fallback: 'web-url',
89
+ notes: 'Photon Spectrum model: install the host once, mini-app URLs render live in-bubble. Unbuilt seam.',
90
+ };
91
+ export function defaultCatalog() {
92
+ const registry = new InteractionRegistry();
93
+ for (const descriptor of [TAPBACK, WEB_LINK, POLL, EFFECT, RICH_LINK_CARD, APP_CLIP, HOST_EXTENSION_CARD]) {
94
+ registry.register(descriptor);
95
+ }
96
+ return registry;
97
+ }
98
+ //# sourceMappingURL=catalog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"catalog.js","sourceRoot":"","sources":["../../src/interactions/catalog.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ import type { FakeGateway } from '../gateway/fake.js';
2
+ import type { InteractionRegistry } from './registry.js';
3
+ import type { InteractionCompletion } from './types.js';
4
+ export interface PollCycleParams {
5
+ chatId: number;
6
+ question: string;
7
+ options: string[];
8
+ voter: {
9
+ sender: string;
10
+ senderName?: string;
11
+ optionIndexes: number[];
12
+ };
13
+ }
14
+ export interface PollVoteResponse {
15
+ optionIds: string[];
16
+ optionTexts: string[];
17
+ }
18
+ export declare class InteractionHarness {
19
+ private readonly fake;
20
+ private readonly registry;
21
+ constructor(fake: FakeGateway, registry: InteractionRegistry);
22
+ private requireBuilt;
23
+ drivePollCycle(params: PollCycleParams): InteractionCompletion<PollVoteResponse>;
24
+ driveLinkCompletion<R>(kind: string, code: string, response: R): InteractionCompletion<R>;
25
+ }
@@ -0,0 +1,69 @@
1
+ import { pollEventKind } from '../polls.js';
2
+ export class InteractionHarness {
3
+ fake;
4
+ registry;
5
+ constructor(fake, registry) {
6
+ this.fake = fake;
7
+ this.registry = registry;
8
+ }
9
+ requireBuilt(kind) {
10
+ const descriptor = this.registry.get(kind);
11
+ if (!descriptor)
12
+ throw new Error(`InteractionHarness: unknown interaction kind "${kind}"`);
13
+ if (descriptor.status === 'unbuilt') {
14
+ throw new Error(`InteractionHarness: "${kind}" is unbuilt — no capability exists to simulate`);
15
+ }
16
+ return descriptor;
17
+ }
18
+ // Injects a poll create (us) + a vote (the given participant) through
19
+ // FakeGateway's own poll fixture drivers, and folds the vote row into an
20
+ // InteractionCompletion keyed by the balloon guid (src/polls.ts classifies
21
+ // the vote row so this doesn't re-derive the wire hazards).
22
+ drivePollCycle(params) {
23
+ this.requireBuilt('poll');
24
+ const { balloon } = this.fake.injectPollCreate({
25
+ chatId: params.chatId,
26
+ fromMe: true,
27
+ question: params.question,
28
+ options: params.options,
29
+ });
30
+ const mintedOptions = balloon.poll?.options ?? [];
31
+ const optionIds = params.voter.optionIndexes.map((index) => {
32
+ const option = mintedOptions[index];
33
+ if (!option)
34
+ throw new Error(`InteractionHarness: no poll option at index ${index}`);
35
+ return option.id;
36
+ });
37
+ const voteMessage = this.fake.injectPollVote({
38
+ chatId: params.chatId,
39
+ sender: params.voter.sender,
40
+ senderName: params.voter.senderName,
41
+ pollGuid: balloon.guid,
42
+ optionIds,
43
+ });
44
+ if (!voteMessage.poll || pollEventKind(voteMessage.poll) !== 'vote') {
45
+ throw new Error('InteractionHarness: injectPollVote did not produce a vote row');
46
+ }
47
+ const votes = voteMessage.poll.votes ?? (voteMessage.poll.vote ? [voteMessage.poll.vote] : []);
48
+ return {
49
+ kind: 'poll',
50
+ correlationKey: balloon.guid,
51
+ response: {
52
+ optionIds: votes.map((v) => v.option_id).filter((id) => id !== undefined),
53
+ optionTexts: votes.map((v) => v.option_text).filter((t) => t !== undefined),
54
+ },
55
+ };
56
+ }
57
+ // Synthesizes a completion for a link-code kind. No gateway involvement —
58
+ // the completion transport is out-of-band (product store/webhook), so
59
+ // there is nothing to inject against FakeGateway; this just validates the
60
+ // kind and shapes the record.
61
+ driveLinkCompletion(kind, code, response) {
62
+ const descriptor = this.requireBuilt(kind);
63
+ if (descriptor.correlation !== 'link-code') {
64
+ throw new Error(`InteractionHarness: "${kind}" does not use link-code correlation`);
65
+ }
66
+ return { kind, correlationKey: code, response };
67
+ }
68
+ }
69
+ //# sourceMappingURL=harness.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"harness.js","sourceRoot":"","sources":["../../src/interactions/harness.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ export * from './types.js';
2
+ export { InteractionRegistry } from './registry.js';
3
+ export { defaultCatalog } from './catalog.js';
4
+ export { InteractionHarness } from './harness.js';
5
+ export type { PollCycleParams, PollVoteResponse } from './harness.js';
@@ -0,0 +1,5 @@
1
+ export * from './types.js';
2
+ export { InteractionRegistry } from './registry.js';
3
+ export { defaultCatalog } from './catalog.js';
4
+ export { InteractionHarness } from './harness.js';
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/interactions/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC"}
@@ -0,0 +1,9 @@
1
+ import type { InteractionDescriptor, InteractionTier } from './types.js';
2
+ export declare class InteractionRegistry {
3
+ private readonly descriptors;
4
+ register(descriptor: InteractionDescriptor): void;
5
+ get(id: string): InteractionDescriptor | undefined;
6
+ all(): InteractionDescriptor[];
7
+ byTier(tier: InteractionTier): InteractionDescriptor[];
8
+ agentUsable(): InteractionDescriptor[];
9
+ }
@@ -0,0 +1,33 @@
1
+ export class InteractionRegistry {
2
+ descriptors = new Map();
3
+ register(descriptor) {
4
+ if (this.descriptors.has(descriptor.id)) {
5
+ throw new Error(`InteractionRegistry: duplicate id "${descriptor.id}"`);
6
+ }
7
+ if (descriptor.tier === 'extension' && !descriptor.requiresRecipientInstall) {
8
+ throw new Error(`InteractionRegistry: "${descriptor.id}" is tier "extension" but requiresRecipientInstall is false — ` +
9
+ 'a real MSMessages card only renders on devices with the extension installed.');
10
+ }
11
+ if (descriptor.correlation === 'none' && descriptor.inboundReadback) {
12
+ throw new Error(`InteractionRegistry: "${descriptor.id}" has correlation "none" but inboundReadback is true — ` +
13
+ 'a readback needs a key to correlate against.');
14
+ }
15
+ this.descriptors.set(descriptor.id, descriptor);
16
+ }
17
+ get(id) {
18
+ return this.descriptors.get(id);
19
+ }
20
+ all() {
21
+ return [...this.descriptors.values()];
22
+ }
23
+ byTier(tier) {
24
+ return this.all().filter((d) => d.tier === tier);
25
+ }
26
+ // Kinds the running agent process can trigger itself, with a real,
27
+ // ground-truthed capability behind them — not merely "the method is
28
+ // wired" (docs/interactions.md "Status is not a success flag").
29
+ agentUsable() {
30
+ return this.all().filter((d) => d.outbound === 'agent' && d.status === 'host-smoked');
31
+ }
32
+ }
33
+ //# sourceMappingURL=registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/interactions/registry.ts"],"names":[],"mappings":"AAKA,MAAM,OAAO,mBAAmB;IACb,WAAW,GAAG,IAAI,GAAG,EAAiC,CAAC;IAExE,QAAQ,CAAC,UAAiC;QACxC,IAAI,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,CAAC,EAAE,CAAC;YACxC,MAAM,IAAI,KAAK,CAAC,sCAAsC,UAAU,CAAC,EAAE,GAAG,CAAC,CAAC;QAC1E,CAAC;QACD,IAAI,UAAU,CAAC,IAAI,KAAK,WAAW,IAAI,CAAC,UAAU,CAAC,wBAAwB,EAAE,CAAC;YAC5E,MAAM,IAAI,KAAK,CACb,yBAAyB,UAAU,CAAC,EAAE,gEAAgE;gBACpG,8EAA8E,CACjF,CAAC;QACJ,CAAC;QACD,IAAI,UAAU,CAAC,WAAW,KAAK,MAAM,IAAI,UAAU,CAAC,eAAe,EAAE,CAAC;YACpE,MAAM,IAAI,KAAK,CACb,yBAAyB,UAAU,CAAC,EAAE,yDAAyD;gBAC7F,8CAA8C,CACjD,CAAC;QACJ,CAAC;QACD,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,EAAE,UAAU,CAAC,CAAC;IAClD,CAAC;IAED,GAAG,CAAC,EAAU;QACZ,OAAO,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAClC,CAAC;IAED,GAAG;QACD,OAAO,CAAC,GAAG,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,CAAC,CAAC;IACxC,CAAC;IAED,MAAM,CAAC,IAAqB;QAC1B,OAAO,IAAI,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;IACnD,CAAC;IAED,mEAAmE;IACnE,oEAAoE;IACpE,gEAAgE;IAChE,WAAW;QACT,OAAO,IAAI,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,OAAO,IAAI,CAAC,CAAC,MAAM,KAAK,aAAa,CAAC,CAAC;IACxF,CAAC;CACF"}
@@ -0,0 +1,28 @@
1
+ export type InteractionTier = 'web-surface' | 'app-clip' | 'native-balloon' | 'extension';
2
+ export type CorrelationKind = 'link-code' | 'message-guid' | 'none';
3
+ export type InteractionStatus = 'unbuilt' | 'fake-only' | 'host-smoked';
4
+ export type OutboundCapability = 'agent' | 'manual-host-op' | 'none';
5
+ export type CompletionTransport = 'gateway-events' | 'out-of-band' | 'none';
6
+ export type InteractionFallback = 'web-url' | 'plain-text' | 'none';
7
+ export interface InteractionDescriptor {
8
+ id: string;
9
+ tier: InteractionTier;
10
+ requiresRecipientInstall: boolean;
11
+ correlation: CorrelationKind;
12
+ completionTransport: CompletionTransport;
13
+ outbound: OutboundCapability;
14
+ inboundReadback: boolean;
15
+ status: InteractionStatus;
16
+ fallback: InteractionFallback;
17
+ notes: string;
18
+ }
19
+ export interface InteractionRequest<P = unknown> {
20
+ kind: string;
21
+ correlationKey: string;
22
+ payload: P;
23
+ }
24
+ export interface InteractionCompletion<R = unknown> {
25
+ kind: string;
26
+ correlationKey: string;
27
+ response: R;
28
+ }
@@ -0,0 +1,5 @@
1
+ // The interaction catalog's wire types. See docs/interactions.md for the
2
+ // four-mechanism model and the contract each descriptor row makes a claim
3
+ // against (docs/gateway-contract.md, docs/imsg-polls.md).
4
+ export {};
5
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/interactions/types.ts"],"names":[],"mappings":"AAAA,yEAAyE;AACzE,0EAA0E;AAC1E,0DAA0D"}