@segmentstream/ontology 0.92.0 → 0.93.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 (35) hide show
  1. package/README.md +38 -1
  2. package/dist/analyze.d.ts +16 -1
  3. package/dist/analyze.js +52 -33
  4. package/dist/manifest.d.ts +87 -0
  5. package/dist/manifest.js +9 -0
  6. package/dist/migrate/model.d.ts +48 -0
  7. package/dist/runtime-source.d.ts +10 -2
  8. package/dist/runtime-source.js +18 -2
  9. package/dist/service/agent/attachments.js +12 -8
  10. package/dist/service/chat/delivery.d.ts +7 -0
  11. package/dist/service/chat/delivery.js +47 -25
  12. package/dist/service/chat/envelope.d.ts +26 -2
  13. package/dist/service/chat/providers/index.d.ts +22 -0
  14. package/dist/service/chat/providers/index.js +31 -0
  15. package/dist/service/chat/providers/provider.d.ts +61 -0
  16. package/dist/service/chat/providers/provider.js +28 -0
  17. package/dist/service/chat/providers/slack.d.ts +10 -0
  18. package/dist/service/chat/providers/slack.js +93 -0
  19. package/dist/service/chat/providers/telegram.d.ts +4 -12
  20. package/dist/service/chat/providers/telegram.js +29 -21
  21. package/dist/service/chat/providers/whatsapp.d.ts +14 -0
  22. package/dist/service/chat/providers/whatsapp.js +91 -0
  23. package/dist/service/chat/reads.d.ts +6 -1
  24. package/dist/service/chat/reads.js +9 -3
  25. package/dist/service/chat/resolve.d.ts +5 -1
  26. package/dist/service/chat/resolve.js +26 -19
  27. package/dist/service/chat/routes.js +6 -1
  28. package/dist/service/mcp/server.js +6 -1
  29. package/dist/service/mcp/tools.js +6 -1
  30. package/dist/service/schemas.d.ts +12 -0
  31. package/dist/slack.d.ts +15 -0
  32. package/dist/slack.js +21 -0
  33. package/dist/whatsapp.d.ts +14 -0
  34. package/dist/whatsapp.js +29 -0
  35. package/package.json +20 -12
package/README.md CHANGED
@@ -1399,10 +1399,47 @@ Grant `agent_chat:post_message` explicitly (`agent_chat:*` also covers it). The
1399
1399
  `AgentChannel`/`AgentMessage` exports and agent-origin configuration have been removed.
1400
1400
  Older ontology definitions still expose reads; posting requires the provider mapping.
1401
1401
 
1402
+ Slack chats are destinations only when `chat.slack` declares their connection, which must be
1403
+ listed in `connections` and must not be Telegram's:
1404
+
1405
+ ```ts
1406
+ const slack = connection("slack", { description: "Workspace Slack app." });
1407
+ defineOntology({
1408
+ entities,
1409
+ roles,
1410
+ connections: [telegram, slack],
1411
+ chat: { telegram: { connection: telegram }, slack: { connection: slack } },
1412
+ });
1413
+ ```
1414
+
1415
+ Its destinations are the `agent_chat` registry's Slack channels, which the agent's Slack channel
1416
+ reports, and `slack:private:<member UUID>` for each member paired on Slack, posted to their user
1417
+ id. A Slack post is one `chat.postMessage` with `markdown_text`: the member's exact Markdown, then
1418
+ the separator and attribution with Markdown escaped and the attribution in italics, as
1419
+ `renderSlack` from `@segmentstream/ontology/slack` renders it. `reply_to` is a thread message's
1420
+ `ts`. Slack's `ok: false` refusal consumes the key; a timeout, a 5xx or an unverifiable answer is
1421
+ `uncertain`. The receipt's `message_id` is the message's `ts`; it has no URL.
1422
+
1423
+ Members' WhatsApp chats with the workspace's number are destinations only when `chat.whatsapp`
1424
+ declares the `WhatsAppConnection`, under the same rules. Each member with a live WhatsApp pairing
1425
+ is `whatsapp:private:<member UUID>`, posted to their WhatsApp id; WhatsApp has no group
1426
+ destinations. A WhatsApp post is one text message, `POST /phone/messages` on the connection's
1427
+ `graph` API with link previews off: the member's text as written, then the separator and the
1428
+ attribution in italics, whose WhatsApp formatting markers (`*`, `_`, `~`, `` ` ``) become
1429
+ look-alikes WhatsApp never formats, as `renderWhatsApp` from `@segmentstream/ontology/whatsapp`
1430
+ renders it. The body is at most 4096 characters, and `reply_to` is refused. Meta delivers
1431
+ free-form text only inside the 24-hour customer-service window: a post outside it fails with
1432
+ Meta's code 131047 and consumes the key. Any Meta refusal with an error code does the same; a
1433
+ timeout, a 5xx or an answer naming no message is `uncertain`. The receipt's `message_id` is the
1434
+ `wamid…` Meta accepted; it has no URL, and a failure Meta reports later in delivery statuses does
1435
+ not change it.
1436
+
1402
1437
  `chat_list({ query?, chat?, after?, limit? })` returns eligible routing metadata and server-derived
1403
1438
  attribution. `query` filters titles by a case-insensitive literal substring before pagination.
1404
1439
  `chat` selects an exact ID/title. Choose an ID or exact title; ambiguous titles return candidates and never guess.
1405
- Paired private chats have stable handles `telegram:private:<member UUID>`. Posting permission
1440
+ Paired private chats have stable handles `telegram:private:<member UUID>`, and
1441
+ `slack:private:<member UUID>` or `whatsapp:private:<member UUID>` when that provider is declared.
1442
+ Posting permission
1406
1443
  allows target discovery independently of history. `user_find` still requires `user:read`.
1407
1444
 
1408
1445
  External OAuth/service clients call `chat_post_message({ chat, text, reply_to?, idempotency_key })`.
package/dist/analyze.d.ts CHANGED
@@ -65,11 +65,20 @@ export interface OntologyConfig<Entities extends ReadonlyArray<AnyEntity> = Read
65
65
  * handle an action lists must be here, and every handle here must be used by an action.
66
66
  */
67
67
  readonly connections?: ReadonlyArray<AnyConnectionHandle>;
68
- /** Provider connections used by the built-in exact chat delivery action. */
68
+ /**
69
+ * The providers the built-in exact chat post delivers to, each through its connection, which
70
+ * must be listed in `connections`. Only declared providers are destinations.
71
+ */
69
72
  readonly chat?: {
70
73
  readonly telegram?: {
71
74
  readonly connection: AnyConnectionHandle;
72
75
  };
76
+ readonly slack?: {
77
+ readonly connection: AnyConnectionHandle;
78
+ };
79
+ readonly whatsapp?: {
80
+ readonly connection: AnyConnectionHandle;
81
+ };
73
82
  readonly attribution?: ChatAttributionSettings;
74
83
  };
75
84
  /**
@@ -184,6 +193,12 @@ export interface Analysis {
184
193
  readonly telegram?: {
185
194
  readonly connection: string;
186
195
  };
196
+ readonly slack?: {
197
+ readonly connection: string;
198
+ };
199
+ readonly whatsapp?: {
200
+ readonly connection: string;
201
+ };
187
202
  readonly attribution?: ChatAttributionSettings;
188
203
  };
189
204
  readonly entities: ReadonlyArray<ResolvedEntity>;
package/dist/analyze.js CHANGED
@@ -30,6 +30,8 @@ import { MAX_WEBHOOK_HEADERS, MAX_WEBHOOKS, WEBHOOK_SIGNATURE_SCHEMES, webhookDe
30
30
  import { actionInputToWire } from "./authoring-input.js";
31
31
  import { validate } from "./runtime/validation.js";
32
32
  const GENERATED_UPSERT = new RegExp(`^${GENERATED_UPSERT_PREFIX}`);
33
+ /** The providers `chat` may declare a posting connection for, in the order posting lists them. */
34
+ const CHAT_PROVIDER_KEYS = ["telegram", "slack", "whatsapp"];
33
35
  function errorMessage(error) {
34
36
  return error instanceof Error ? error.message : String(error);
35
37
  }
@@ -126,39 +128,56 @@ export function analyze(config, options = {}) {
126
128
  ];
127
129
  let chat;
128
130
  if (config.chat !== undefined) {
129
- if (!isPlainObject(config.chat) ||
130
- Object.keys(config.chat).some((key) => key !== "telegram" && key !== "attribution") ||
131
- (config.chat.telegram !== undefined &&
132
- (!isPlainObject(config.chat.telegram) ||
133
- Object.keys(config.chat.telegram).some((key) => key !== "connection") ||
134
- !isConnection(config.chat.telegram.connection)))) {
135
- report("invalid-connection", "chat", "chat.telegram.connection must reference a declared connection handle.");
136
- }
137
- else if (config.chat.telegram !== undefined) {
138
- const handle = config.chat.telegram.connection;
139
- if (!(config.connections ?? []).includes(handle)) {
140
- report("invalid-connection", "chat.telegram.connection", "The chat provider connection must be listed in connections.");
141
- }
142
- else {
143
- chat = {
144
- telegram: { connection: handle.key },
145
- ...(config.chat.attribution === undefined
146
- ? {}
147
- : { attribution: config.chat.attribution }),
148
- };
149
- resolved = resolved.map((entity) => entity.name !== "agent_chat"
150
- ? entity
151
- : {
152
- ...entity,
153
- actions: entity.actions.map((candidate) => candidate.name !== "post_message"
154
- ? candidate
155
- : {
156
- ...candidate,
157
- connections: [handle.key],
158
- action: { ...candidate.action, connections: [handle] },
159
- }),
160
- });
161
- }
131
+ const declared = isPlainObject(config.chat)
132
+ ? CHAT_PROVIDER_KEYS.filter((name) => config.chat?.[name] !== undefined)
133
+ : [];
134
+ const unknown = isPlainObject(config.chat)
135
+ ? Object.keys(config.chat).find((key) => key !== "attribution" && !CHAT_PROVIDER_KEYS.includes(key))
136
+ : "chat";
137
+ const malformed = declared.find((name) => {
138
+ const provider = config.chat?.[name];
139
+ return (!isPlainObject(provider) ||
140
+ Object.keys(provider).some((key) => key !== "connection") ||
141
+ !isConnection(provider.connection));
142
+ });
143
+ // Read only once every declared provider is well formed.
144
+ const handles = malformed === undefined
145
+ ? declared.map((name) => [name, config.chat[name].connection])
146
+ : [];
147
+ const unlisted = handles.find(([, handle]) => !(config.connections ?? []).includes(handle));
148
+ const shared = handles.find(([, handle], index) => handles.findIndex(([, other]) => other === handle) !== index);
149
+ if (unknown !== undefined) {
150
+ report("invalid-connection", "chat", `chat accepts only ${CHAT_PROVIDER_KEYS.join(", ")} and attribution.`);
151
+ }
152
+ else if (malformed !== undefined) {
153
+ report("invalid-connection", "chat", `chat.${malformed}.connection must reference a declared connection handle.`);
154
+ }
155
+ else if (unlisted !== undefined) {
156
+ report("invalid-connection", `chat.${unlisted[0]}.connection`, "The chat provider connection must be listed in connections.");
157
+ }
158
+ else if (shared !== undefined) {
159
+ report("invalid-connection", `chat.${shared[0]}.connection`, "Each chat provider needs its own connection.");
160
+ }
161
+ else if (handles.length > 0) {
162
+ chat = {
163
+ ...Object.fromEntries(handles.map(([name, handle]) => [name, { connection: handle.key }])),
164
+ ...(config.chat.attribution === undefined ? {} : { attribution: config.chat.attribution }),
165
+ };
166
+ resolved = resolved.map((entity) => entity.name !== "agent_chat"
167
+ ? entity
168
+ : {
169
+ ...entity,
170
+ actions: entity.actions.map((candidate) => candidate.name !== "post_message"
171
+ ? candidate
172
+ : {
173
+ ...candidate,
174
+ connections: handles.map(([, handle]) => handle.key),
175
+ action: {
176
+ ...candidate.action,
177
+ connections: handles.map(([, handle]) => handle),
178
+ },
179
+ }),
180
+ });
162
181
  }
163
182
  }
164
183
  const attribution = config.chat?.attribution;
@@ -53,6 +53,9 @@ import { type SignInOptions, type SignInPolicy } from "./sign-in.js";
53
53
  * Version 15 changes no field this SDK writes. Ontology SDKs 0.86.0 to 0.88.0 introduced it with
54
54
  * `executionCompatibility` and three workflow capabilities for a deploy guard that was withdrawn;
55
55
  * readers ignore them.
56
+ * Within version 15, `chat` may carry `slack` and `whatsapp` beside `telegram`: the connections
57
+ * exact chat posts to Slack chats and to members' WhatsApp chats go through. Older readers ignore
58
+ * them, and their services post to Telegram alone.
56
59
  */
57
60
  export declare const MANIFEST_VERSION = 15;
58
61
  /** The phrase used for a direction the author did not phrase. */
@@ -862,10 +865,20 @@ export declare const OntologyManifestWire: Schema.encodeKeys<Schema.Struct<{
862
865
  /** Written only when the ontology sends data out through the connection. */
863
866
  readonly delivers: Schema.optionalKey<Schema.Literal<true>>;
864
867
  }>>>;
868
+ /**
869
+ * The providers whose chats the built-in exact chat post delivers to, each through its declared
870
+ * connection, and the attribution it adds. Absent, or without a provider, delivers nowhere.
871
+ */
865
872
  readonly chat: Schema.optionalKey<Schema.Struct<{
866
873
  readonly telegram: Schema.optionalKey<Schema.Struct<{
867
874
  readonly connection: Schema.String;
868
875
  }>>;
876
+ readonly slack: Schema.optionalKey<Schema.Struct<{
877
+ readonly connection: Schema.String;
878
+ }>>;
879
+ readonly whatsapp: Schema.optionalKey<Schema.Struct<{
880
+ readonly connection: Schema.String;
881
+ }>>;
869
882
  readonly attribution: Schema.optionalKey<Schema.Struct<{
870
883
  readonly template: Schema.optionalKey<Schema.String>;
871
884
  readonly separator: Schema.optionalKey<Schema.String>;
@@ -1054,6 +1067,12 @@ export declare const OntologyManifest: Schema.declareConstructor<{
1054
1067
  readonly telegram?: {
1055
1068
  readonly connection: string;
1056
1069
  } | undefined;
1070
+ readonly slack?: {
1071
+ readonly connection: string;
1072
+ } | undefined;
1073
+ readonly whatsapp?: {
1074
+ readonly connection: string;
1075
+ } | undefined;
1057
1076
  readonly attribution?: {
1058
1077
  readonly template?: string | undefined;
1059
1078
  readonly separator?: string | undefined;
@@ -1275,6 +1294,12 @@ export declare const OntologyManifest: Schema.declareConstructor<{
1275
1294
  readonly telegram?: {
1276
1295
  readonly connection: string;
1277
1296
  } | undefined;
1297
+ readonly slack?: {
1298
+ readonly connection: string;
1299
+ } | undefined;
1300
+ readonly whatsapp?: {
1301
+ readonly connection: string;
1302
+ } | undefined;
1278
1303
  readonly attribution?: {
1279
1304
  readonly template?: string | undefined;
1280
1305
  readonly separator?: string | undefined;
@@ -1496,6 +1521,12 @@ export declare const OntologyManifest: Schema.declareConstructor<{
1496
1521
  readonly telegram?: {
1497
1522
  readonly connection: string;
1498
1523
  } | undefined;
1524
+ readonly slack?: {
1525
+ readonly connection: string;
1526
+ } | undefined;
1527
+ readonly whatsapp?: {
1528
+ readonly connection: string;
1529
+ } | undefined;
1499
1530
  readonly attribution?: {
1500
1531
  readonly template?: string | undefined;
1501
1532
  readonly separator?: string | undefined;
@@ -1717,6 +1748,12 @@ export declare const OntologyManifest: Schema.declareConstructor<{
1717
1748
  readonly telegram?: {
1718
1749
  readonly connection: string;
1719
1750
  } | undefined;
1751
+ readonly slack?: {
1752
+ readonly connection: string;
1753
+ } | undefined;
1754
+ readonly whatsapp?: {
1755
+ readonly connection: string;
1756
+ } | undefined;
1720
1757
  readonly attribution?: {
1721
1758
  readonly template?: string | undefined;
1722
1759
  readonly separator?: string | undefined;
@@ -2043,10 +2080,20 @@ export declare const OntologyManifest: Schema.declareConstructor<{
2043
2080
  /** Written only when the ontology sends data out through the connection. */
2044
2081
  readonly delivers: Schema.optionalKey<Schema.Literal<true>>;
2045
2082
  }>>>;
2083
+ /**
2084
+ * The providers whose chats the built-in exact chat post delivers to, each through its declared
2085
+ * connection, and the attribution it adds. Absent, or without a provider, delivers nowhere.
2086
+ */
2046
2087
  readonly chat: Schema.optionalKey<Schema.Struct<{
2047
2088
  readonly telegram: Schema.optionalKey<Schema.Struct<{
2048
2089
  readonly connection: Schema.String;
2049
2090
  }>>;
2091
+ readonly slack: Schema.optionalKey<Schema.Struct<{
2092
+ readonly connection: Schema.String;
2093
+ }>>;
2094
+ readonly whatsapp: Schema.optionalKey<Schema.Struct<{
2095
+ readonly connection: Schema.String;
2096
+ }>>;
2050
2097
  readonly attribution: Schema.optionalKey<Schema.Struct<{
2051
2098
  readonly template: Schema.optionalKey<Schema.String>;
2052
2099
  readonly separator: Schema.optionalKey<Schema.String>;
@@ -2327,10 +2374,20 @@ export declare const OntologyManifest: Schema.declareConstructor<{
2327
2374
  /** Written only when the ontology sends data out through the connection. */
2328
2375
  readonly delivers: Schema.optionalKey<Schema.Literal<true>>;
2329
2376
  }>>>;
2377
+ /**
2378
+ * The providers whose chats the built-in exact chat post delivers to, each through its declared
2379
+ * connection, and the attribution it adds. Absent, or without a provider, delivers nowhere.
2380
+ */
2330
2381
  readonly chat: Schema.optionalKey<Schema.Struct<{
2331
2382
  readonly telegram: Schema.optionalKey<Schema.Struct<{
2332
2383
  readonly connection: Schema.String;
2333
2384
  }>>;
2385
+ readonly slack: Schema.optionalKey<Schema.Struct<{
2386
+ readonly connection: Schema.String;
2387
+ }>>;
2388
+ readonly whatsapp: Schema.optionalKey<Schema.Struct<{
2389
+ readonly connection: Schema.String;
2390
+ }>>;
2334
2391
  readonly attribution: Schema.optionalKey<Schema.Struct<{
2335
2392
  readonly template: Schema.optionalKey<Schema.String>;
2336
2393
  readonly separator: Schema.optionalKey<Schema.String>;
@@ -2511,6 +2568,12 @@ export declare const OntologyManifest: Schema.declareConstructor<{
2511
2568
  readonly telegram?: {
2512
2569
  readonly connection: string;
2513
2570
  } | undefined;
2571
+ readonly slack?: {
2572
+ readonly connection: string;
2573
+ } | undefined;
2574
+ readonly whatsapp?: {
2575
+ readonly connection: string;
2576
+ } | undefined;
2514
2577
  readonly attribution?: {
2515
2578
  readonly template?: string | undefined;
2516
2579
  readonly separator?: string | undefined;
@@ -2732,6 +2795,12 @@ export declare const OntologyManifest: Schema.declareConstructor<{
2732
2795
  readonly telegram?: {
2733
2796
  readonly connection: string;
2734
2797
  } | undefined;
2798
+ readonly slack?: {
2799
+ readonly connection: string;
2800
+ } | undefined;
2801
+ readonly whatsapp?: {
2802
+ readonly connection: string;
2803
+ } | undefined;
2735
2804
  readonly attribution?: {
2736
2805
  readonly template?: string | undefined;
2737
2806
  readonly separator?: string | undefined;
@@ -2959,6 +3028,12 @@ export declare const decodeManifestDocument: (value: unknown) => Effect.Effect<{
2959
3028
  readonly telegram?: {
2960
3029
  readonly connection: string;
2961
3030
  } | undefined;
3031
+ readonly slack?: {
3032
+ readonly connection: string;
3033
+ } | undefined;
3034
+ readonly whatsapp?: {
3035
+ readonly connection: string;
3036
+ } | undefined;
2962
3037
  readonly attribution?: {
2963
3038
  readonly template?: string | undefined;
2964
3039
  readonly separator?: string | undefined;
@@ -3180,6 +3255,12 @@ export declare const decodeManifestDocument: (value: unknown) => Effect.Effect<{
3180
3255
  readonly telegram?: {
3181
3256
  readonly connection: string;
3182
3257
  } | undefined;
3258
+ readonly slack?: {
3259
+ readonly connection: string;
3260
+ } | undefined;
3261
+ readonly whatsapp?: {
3262
+ readonly connection: string;
3263
+ } | undefined;
3183
3264
  readonly attribution?: {
3184
3265
  readonly template?: string | undefined;
3185
3266
  readonly separator?: string | undefined;
@@ -3312,6 +3393,12 @@ export declare function buildManifest(input: {
3312
3393
  readonly telegram?: {
3313
3394
  readonly connection: string;
3314
3395
  };
3396
+ readonly slack?: {
3397
+ readonly connection: string;
3398
+ };
3399
+ readonly whatsapp?: {
3400
+ readonly connection: string;
3401
+ };
3315
3402
  readonly attribution?: ChatAttributionSettings;
3316
3403
  };
3317
3404
  readonly entities: ReadonlyArray<ResolvedEntity>;
package/dist/manifest.js CHANGED
@@ -54,6 +54,9 @@ import { CHANNEL_PROVIDERS, DEFAULT_SIGN_IN_POLICY, isSignInPolicy, } from "./si
54
54
  * Version 15 changes no field this SDK writes. Ontology SDKs 0.86.0 to 0.88.0 introduced it with
55
55
  * `executionCompatibility` and three workflow capabilities for a deploy guard that was withdrawn;
56
56
  * readers ignore them.
57
+ * Within version 15, `chat` may carry `slack` and `whatsapp` beside `telegram`: the connections
58
+ * exact chat posts to Slack chats and to members' WhatsApp chats go through. Older readers ignore
59
+ * them, and their services post to Telegram alone.
57
60
  */
58
61
  export const MANIFEST_VERSION = 15;
59
62
  /** The first version whose documents use snake_case names on the wire. */
@@ -411,8 +414,14 @@ const OntologyManifestModel = Schema.Struct({
411
414
  emails: Schema.optionalKey(Schema.Record(Schema.String, EmailTemplateManifest)),
412
415
  /** The connections the actions call. Written only when there are some; absent reads as none. */
413
416
  connections: Schema.optionalKey(Schema.Array(ConnectionManifest)),
417
+ /**
418
+ * The providers whose chats the built-in exact chat post delivers to, each through its declared
419
+ * connection, and the attribution it adds. Absent, or without a provider, delivers nowhere.
420
+ */
414
421
  chat: Schema.optionalKey(Schema.Struct({
415
422
  telegram: Schema.optionalKey(Schema.Struct({ connection: Schema.String })),
423
+ slack: Schema.optionalKey(Schema.Struct({ connection: Schema.String })),
424
+ whatsapp: Schema.optionalKey(Schema.Struct({ connection: Schema.String })),
416
425
  attribution: Schema.optionalKey(Schema.Struct({
417
426
  template: Schema.optionalKey(Schema.String),
418
427
  separator: Schema.optionalKey(Schema.String),
@@ -400,6 +400,12 @@ export declare const MigrationPlan: Schema.Struct<{
400
400
  readonly telegram?: {
401
401
  readonly connection: string;
402
402
  } | undefined;
403
+ readonly slack?: {
404
+ readonly connection: string;
405
+ } | undefined;
406
+ readonly whatsapp?: {
407
+ readonly connection: string;
408
+ } | undefined;
403
409
  readonly attribution?: {
404
410
  readonly template?: string | undefined;
405
411
  readonly separator?: string | undefined;
@@ -621,6 +627,12 @@ export declare const MigrationPlan: Schema.Struct<{
621
627
  readonly telegram?: {
622
628
  readonly connection: string;
623
629
  } | undefined;
630
+ readonly slack?: {
631
+ readonly connection: string;
632
+ } | undefined;
633
+ readonly whatsapp?: {
634
+ readonly connection: string;
635
+ } | undefined;
624
636
  readonly attribution?: {
625
637
  readonly template?: string | undefined;
626
638
  readonly separator?: string | undefined;
@@ -842,6 +854,12 @@ export declare const MigrationPlan: Schema.Struct<{
842
854
  readonly telegram?: {
843
855
  readonly connection: string;
844
856
  } | undefined;
857
+ readonly slack?: {
858
+ readonly connection: string;
859
+ } | undefined;
860
+ readonly whatsapp?: {
861
+ readonly connection: string;
862
+ } | undefined;
845
863
  readonly attribution?: {
846
864
  readonly template?: string | undefined;
847
865
  readonly separator?: string | undefined;
@@ -1063,6 +1081,12 @@ export declare const MigrationPlan: Schema.Struct<{
1063
1081
  readonly telegram?: {
1064
1082
  readonly connection: string;
1065
1083
  } | undefined;
1084
+ readonly slack?: {
1085
+ readonly connection: string;
1086
+ } | undefined;
1087
+ readonly whatsapp?: {
1088
+ readonly connection: string;
1089
+ } | undefined;
1066
1090
  readonly attribution?: {
1067
1091
  readonly template?: string | undefined;
1068
1092
  readonly separator?: string | undefined;
@@ -1346,6 +1370,12 @@ export declare const MigrationPlan: Schema.Struct<{
1346
1370
  readonly telegram: Schema.optionalKey<Schema.Struct<{
1347
1371
  readonly connection: Schema.String;
1348
1372
  }>>;
1373
+ readonly slack: Schema.optionalKey<Schema.Struct<{
1374
+ readonly connection: Schema.String;
1375
+ }>>;
1376
+ readonly whatsapp: Schema.optionalKey<Schema.Struct<{
1377
+ readonly connection: Schema.String;
1378
+ }>>;
1349
1379
  readonly attribution: Schema.optionalKey<Schema.Struct<{
1350
1380
  readonly template: Schema.optionalKey<Schema.String>;
1351
1381
  readonly separator: Schema.optionalKey<Schema.String>;
@@ -1562,6 +1592,12 @@ export declare const MigrationPlan: Schema.Struct<{
1562
1592
  readonly telegram: Schema.optionalKey<Schema.Struct<{
1563
1593
  readonly connection: Schema.String;
1564
1594
  }>>;
1595
+ readonly slack: Schema.optionalKey<Schema.Struct<{
1596
+ readonly connection: Schema.String;
1597
+ }>>;
1598
+ readonly whatsapp: Schema.optionalKey<Schema.Struct<{
1599
+ readonly connection: Schema.String;
1600
+ }>>;
1565
1601
  readonly attribution: Schema.optionalKey<Schema.Struct<{
1566
1602
  readonly template: Schema.optionalKey<Schema.String>;
1567
1603
  readonly separator: Schema.optionalKey<Schema.String>;
@@ -1729,6 +1765,12 @@ export declare const MigrationPlan: Schema.Struct<{
1729
1765
  readonly telegram?: {
1730
1766
  readonly connection: string;
1731
1767
  } | undefined;
1768
+ readonly slack?: {
1769
+ readonly connection: string;
1770
+ } | undefined;
1771
+ readonly whatsapp?: {
1772
+ readonly connection: string;
1773
+ } | undefined;
1732
1774
  readonly attribution?: {
1733
1775
  readonly template?: string | undefined;
1734
1776
  readonly separator?: string | undefined;
@@ -1950,6 +1992,12 @@ export declare const MigrationPlan: Schema.Struct<{
1950
1992
  readonly telegram?: {
1951
1993
  readonly connection: string;
1952
1994
  } | undefined;
1995
+ readonly slack?: {
1996
+ readonly connection: string;
1997
+ } | undefined;
1998
+ readonly whatsapp?: {
1999
+ readonly connection: string;
2000
+ } | undefined;
1953
2001
  readonly attribution?: {
1954
2002
  readonly template?: string | undefined;
1955
2003
  readonly separator?: string | undefined;
@@ -1,6 +1,6 @@
1
1
  import { Schema } from "effect";
2
2
  /** Decoded only at a verified runtime boundary; ordinary action arguments never establish provenance. */
3
- export declare const RuntimeSource: Schema.Struct<{
3
+ export declare const RuntimeSource: Schema.Union<readonly [Schema.Struct<{
4
4
  readonly provider: Schema.Literal<"telegram">;
5
5
  readonly chat_id: Schema.String;
6
6
  readonly thread_id: Schema.String;
@@ -15,7 +15,15 @@ export declare const RuntimeSource: Schema.Struct<{
15
15
  readonly media_type: Schema.optionalKey<Schema.String>;
16
16
  readonly staged_id: Schema.String;
17
17
  }>>>;
18
- }>;
18
+ }>, Schema.Struct<{
19
+ readonly provider: Schema.Literals<readonly ["slack", "whatsapp"]>;
20
+ readonly chat_id: Schema.String;
21
+ readonly thread_id: Schema.String;
22
+ readonly kind: Schema.Literals<readonly ["private", "group"]>;
23
+ readonly peer: Schema.optionalKey<Schema.String>;
24
+ readonly message_id: Schema.optionalKey<Schema.String>;
25
+ readonly sender: Schema.optionalKey<Schema.String>;
26
+ }>]>;
19
27
  export type RuntimeSource = typeof RuntimeSource.Type;
20
28
  export interface ActionRuntime {
21
29
  readonly source: RuntimeSource;
@@ -1,7 +1,7 @@
1
1
  import { Schema } from "effect";
2
2
  const Id = Schema.String.check(Schema.isMinLength(1), Schema.isMaxLength(512));
3
- /** Decoded only at a verified runtime boundary; ordinary action arguments never establish provenance. */
4
- export const RuntimeSource = Schema.Struct({
3
+ /** A Telegram chat, with the references Telegram's own actions and attachments use. */
4
+ const TelegramRuntimeSource = Schema.Struct({
5
5
  provider: Schema.Literal("telegram"),
6
6
  chat_id: Id,
7
7
  thread_id: Schema.String.check(Schema.isMaxLength(512)),
@@ -26,3 +26,19 @@ export const RuntimeSource = Schema.Struct({
26
26
  staged_id: Id,
27
27
  })).check(Schema.isMaxLength(10))),
28
28
  });
29
+ /**
30
+ * A conversation in another channel the agent serves. `kind` says whether it is one person's
31
+ * direct conversation; `peer` is that person's id on the provider, which a private destination on
32
+ * the same provider names, while `chat_id` names the conversation itself.
33
+ */
34
+ const ChannelRuntimeSource = Schema.Struct({
35
+ provider: Schema.Literals(["slack", "whatsapp"]),
36
+ chat_id: Id,
37
+ thread_id: Schema.String.check(Schema.isMaxLength(512)),
38
+ kind: Schema.Literals(["private", "group"]),
39
+ peer: Schema.optionalKey(Id),
40
+ message_id: Schema.optionalKey(Id),
41
+ sender: Schema.optionalKey(Id),
42
+ });
43
+ /** Decoded only at a verified runtime boundary; ordinary action arguments never establish provenance. */
44
+ export const RuntimeSource = Schema.Union([TelegramRuntimeSource, ChannelRuntimeSource]);
@@ -173,15 +173,19 @@ export function attachmentsLayer(options) {
173
173
  subjectId: caller.principal.id,
174
174
  });
175
175
  const attachmentId = yield* idOf;
176
- const { attachments: sourceAttachments, ...sourceFields } = metadata.envelope?.source ?? {};
177
- const matchingAttachments = sourceAttachments?.filter((attachment) => attachment.staged_id === attachmentId);
178
- const source = metadata.envelope?.source === undefined
176
+ const envelopeSource = metadata.envelope?.source;
177
+ // Only a Telegram source names the provider files a message carried.
178
+ const matchingAttachments = envelopeSource?.provider === "telegram"
179
+ ? envelopeSource.attachments?.filter((attachment) => attachment.staged_id === attachmentId)
180
+ : undefined;
181
+ const source = envelopeSource === undefined
179
182
  ? undefined
180
- : {
181
- ...metadata.envelope.source,
182
- ...sourceFields,
183
- attachments: matchingAttachments?.length ? matchingAttachments : undefined,
184
- };
183
+ : envelopeSource.provider === "telegram"
184
+ ? {
185
+ ...envelopeSource,
186
+ attachments: matchingAttachments?.length ? matchingAttachments : undefined,
187
+ }
188
+ : envelopeSource;
185
189
  const principal = source === undefined
186
190
  ? caller.principal
187
191
  : yield* agents.actingMember(caller.principal.id, source.provider);
@@ -7,6 +7,7 @@ import type { ConnectionTransportFactory } from "../connections.js";
7
7
  import type { OntologyBackend } from "../runtime-contract.js";
8
8
  import type { DeliveryArchive } from "./archive.js";
9
9
  import type { RuntimeChatEnvelope } from "./envelope.js";
10
+ import { type Destination } from "./resolve.js";
10
11
  export type ChatDeliveryFactory = (input: {
11
12
  readonly backend: OntologyBackend["Service"];
12
13
  readonly principal: Principal;
@@ -15,4 +16,10 @@ export type ChatDeliveryFactory = (input: {
15
16
  readonly requestId: string;
16
17
  readonly envelope?: RuntimeChatEnvelope;
17
18
  }) => ChatDelivery["Service"];
19
+ /**
20
+ * Whether a post would land in the conversation it was asked from. A private destination names the
21
+ * person, not the conversation: a Slack direct message is `D…` while the person is `U…`, so a
22
+ * private source matches a private destination on the same provider by its `peer`.
23
+ */
24
+ export declare function sameChat(source: NonNullable<RuntimeChatEnvelope["source"]>, target: Pick<Destination, "provider" | "external_id" | "kind">): boolean;
18
25
  export declare function makeChatDelivery(ontology: Ontology, connections: ConnectionTransportFactory, archive: DeliveryArchive, previews?: TelegramPreviewContext): ChatDeliveryFactory;