@canonmsg/backend-contracts 5.2.0 → 5.4.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.
- package/dist/canon-verbs.schema.json +46 -7
- package/dist/cjs/contactRequest.js +20 -0
- package/dist/cjs/verbContract.js +3 -4
- package/dist/cjs/verbSchemas.js +45 -22
- package/dist/cjs/verbWire.js +3 -3
- package/dist/contactRequest.d.ts +11 -0
- package/dist/contactRequest.js +19 -0
- package/dist/verbContract.d.ts +18 -14
- package/dist/verbContract.js +3 -4
- package/dist/verbSchemas.d.ts +34 -3
- package/dist/verbSchemas.js +45 -22
- package/dist/verbWire.js +3 -3
- package/package.json +1 -1
|
@@ -13,6 +13,23 @@
|
|
|
13
13
|
},
|
|
14
14
|
"description": "Client idempotency key (<= 160 chars / 256 UTF-8 bytes; no slashes, control chars, '.', '..', __x__). Same id + identical payload replays idempotently; different payload -> 409 MESSAGE_ID_EXISTS."
|
|
15
15
|
},
|
|
16
|
+
"groupInviteRequirements": {
|
|
17
|
+
"type": "object",
|
|
18
|
+
"description": "Independent gates on a pending group invite. policyApproval is social consent; ownerSessionSetup means the agent owner must choose the runtime session configuration before membership is activated.",
|
|
19
|
+
"required": [
|
|
20
|
+
"policyApproval",
|
|
21
|
+
"ownerSessionSetup"
|
|
22
|
+
],
|
|
23
|
+
"additionalProperties": false,
|
|
24
|
+
"properties": {
|
|
25
|
+
"policyApproval": {
|
|
26
|
+
"type": "boolean"
|
|
27
|
+
},
|
|
28
|
+
"ownerSessionSetup": {
|
|
29
|
+
"type": "boolean"
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
},
|
|
16
33
|
"selfContext": {
|
|
17
34
|
"type": "object",
|
|
18
35
|
"description": "Private note-to-self attached to a cross-conversation send. Visible only to the sending agent (surfaced back as provenance.activeSelfContext); never shown to recipients. Enforced by functions/src/utils/selfContexts.ts.",
|
|
@@ -1471,7 +1488,7 @@
|
|
|
1471
1488
|
},
|
|
1472
1489
|
"create_group_input": {
|
|
1473
1490
|
"type": "object",
|
|
1474
|
-
"description": "Create a group conversation. Each target's groupJoinPolicy is enforced server-side with staged admission: directly-addable members join at creation, approval-required members become pending group_invite requests
|
|
1491
|
+
"description": "Create a group conversation. Each target's groupJoinPolicy is enforced server-side with staged admission: directly-addable members join at creation, approval-required members become pending group_invite requests; agents needing explicit group-session setup become pending owner requests; hard-denied members are skipped (see the result). A creator-only group is valid when at least one requested member has a pending invite. A creator that itself requires setup cannot create groups (403 CREATE_GROUP_CREATOR_SETUP_REQUIRED). Under MLS, membership changes are Add/Remove proposals + Commit — a group operation is a cryptographic state change, not a codec swap.",
|
|
1475
1492
|
"required": [
|
|
1476
1493
|
"name",
|
|
1477
1494
|
"memberIds"
|
|
@@ -1497,10 +1514,13 @@
|
|
|
1497
1514
|
},
|
|
1498
1515
|
"create_group_result": {
|
|
1499
1516
|
"type": "object",
|
|
1500
|
-
"description": "Staged admission: the group exists with the directly-
|
|
1517
|
+
"description": "Staged admission: the group exists with the directly-added members; `pending` lists group_invite requests awaiting policy approval, owner session setup, or both; `skipped` lists hard-denied targets.",
|
|
1501
1518
|
"required": [
|
|
1502
1519
|
"status",
|
|
1503
|
-
"conversationId"
|
|
1520
|
+
"conversationId",
|
|
1521
|
+
"added",
|
|
1522
|
+
"pending",
|
|
1523
|
+
"skipped"
|
|
1504
1524
|
],
|
|
1505
1525
|
"additionalProperties": true,
|
|
1506
1526
|
"properties": {
|
|
@@ -1510,13 +1530,20 @@
|
|
|
1510
1530
|
"conversationId": {
|
|
1511
1531
|
"type": "string"
|
|
1512
1532
|
},
|
|
1533
|
+
"added": {
|
|
1534
|
+
"type": "array",
|
|
1535
|
+
"items": {
|
|
1536
|
+
"type": "string"
|
|
1537
|
+
}
|
|
1538
|
+
},
|
|
1513
1539
|
"pending": {
|
|
1514
1540
|
"type": "array",
|
|
1515
1541
|
"items": {
|
|
1516
1542
|
"type": "object",
|
|
1517
1543
|
"required": [
|
|
1518
1544
|
"userId",
|
|
1519
|
-
"requestId"
|
|
1545
|
+
"requestId",
|
|
1546
|
+
"requirements"
|
|
1520
1547
|
],
|
|
1521
1548
|
"additionalProperties": true,
|
|
1522
1549
|
"properties": {
|
|
@@ -1525,6 +1552,9 @@
|
|
|
1525
1552
|
},
|
|
1526
1553
|
"requestId": {
|
|
1527
1554
|
"type": "string"
|
|
1555
|
+
},
|
|
1556
|
+
"requirements": {
|
|
1557
|
+
"$ref": "#/$defs/groupInviteRequirements"
|
|
1528
1558
|
}
|
|
1529
1559
|
}
|
|
1530
1560
|
}
|
|
@@ -1552,7 +1582,7 @@
|
|
|
1552
1582
|
},
|
|
1553
1583
|
"add_member_input": {
|
|
1554
1584
|
"type": "object",
|
|
1555
|
-
"description": "
|
|
1585
|
+
"description": "A target needing policy approval and/or explicit owner session setup yields one pending group_invite. Under MLS an add is an Add proposal + Commit (new epoch), not a server-side membership write — the verb semantics are stable, the mechanism is not.",
|
|
1556
1586
|
"required": [
|
|
1557
1587
|
"conversationId",
|
|
1558
1588
|
"userId"
|
|
@@ -1570,7 +1600,7 @@
|
|
|
1570
1600
|
}
|
|
1571
1601
|
},
|
|
1572
1602
|
"add_member_result": {
|
|
1573
|
-
"description": "approval
|
|
1603
|
+
"description": "Targets needing policy approval and/or owner session setup yield one pending group_invite routed to the target or agent owner.",
|
|
1574
1604
|
"oneOf": [
|
|
1575
1605
|
{
|
|
1576
1606
|
"type": "object",
|
|
@@ -1588,7 +1618,8 @@
|
|
|
1588
1618
|
"type": "object",
|
|
1589
1619
|
"required": [
|
|
1590
1620
|
"status",
|
|
1591
|
-
"requestId"
|
|
1621
|
+
"requestId",
|
|
1622
|
+
"requirements"
|
|
1592
1623
|
],
|
|
1593
1624
|
"additionalProperties": true,
|
|
1594
1625
|
"properties": {
|
|
@@ -1597,6 +1628,9 @@
|
|
|
1597
1628
|
},
|
|
1598
1629
|
"requestId": {
|
|
1599
1630
|
"type": "string"
|
|
1631
|
+
},
|
|
1632
|
+
"requirements": {
|
|
1633
|
+
"$ref": "#/$defs/groupInviteRequirements"
|
|
1600
1634
|
}
|
|
1601
1635
|
}
|
|
1602
1636
|
}
|
|
@@ -1901,6 +1935,11 @@
|
|
|
1901
1935
|
"type": "string",
|
|
1902
1936
|
"pattern": "^[A-Za-z0-9_.:-]{1,160}$"
|
|
1903
1937
|
},
|
|
1938
|
+
"messageId": {
|
|
1939
|
+
"type": "string",
|
|
1940
|
+
"pattern": "^[A-Za-z0-9_.:-]{1,160}$",
|
|
1941
|
+
"description": "The triggering message this silence answers, so the record cannot be misread against a later message."
|
|
1942
|
+
},
|
|
1904
1943
|
"reason": {
|
|
1905
1944
|
"type": "string",
|
|
1906
1945
|
"maxLength": 500,
|
|
@@ -1,6 +1,18 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.readGroupInviteRequirements = readGroupInviteRequirements;
|
|
3
4
|
exports.serializeContactRequest = serializeContactRequest;
|
|
5
|
+
function readGroupInviteRequirements(value) {
|
|
6
|
+
const record = isRecord(value) ? value : null;
|
|
7
|
+
if (typeof record?.policyApproval !== 'boolean'
|
|
8
|
+
|| typeof record.ownerSessionSetup !== 'boolean') {
|
|
9
|
+
throw new TypeError('Invalid group invite requirements');
|
|
10
|
+
}
|
|
11
|
+
return {
|
|
12
|
+
policyApproval: record.policyApproval,
|
|
13
|
+
ownerSessionSetup: record.ownerSessionSetup,
|
|
14
|
+
};
|
|
15
|
+
}
|
|
4
16
|
function isRecord(value) {
|
|
5
17
|
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
6
18
|
}
|
|
@@ -85,7 +97,15 @@ function serializeContactRequest(requestId, data) {
|
|
|
85
97
|
expiresAt: normalizeTimestamp(data.expiresAt),
|
|
86
98
|
};
|
|
87
99
|
if (groupContext) {
|
|
100
|
+
let requirements;
|
|
101
|
+
try {
|
|
102
|
+
requirements = readGroupInviteRequirements(data.requirements);
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
88
107
|
payload.groupContext = groupContext;
|
|
108
|
+
payload.requirements = requirements;
|
|
89
109
|
}
|
|
90
110
|
if (data.targetUserType === 'ai_agent') {
|
|
91
111
|
payload.targetOwnerId = data.targetOwnerId;
|
package/dist/cjs/verbContract.js
CHANGED
|
@@ -266,10 +266,9 @@ function normalizeVerbNativeMetadata(value) {
|
|
|
266
266
|
return Object.keys(native).length > 0 ? native : undefined;
|
|
267
267
|
}
|
|
268
268
|
/**
|
|
269
|
-
* Error code on the 400 a create_group receives when
|
|
270
|
-
*
|
|
271
|
-
*
|
|
272
|
-
* partition: pendingRequired[] and skipped[].
|
|
269
|
+
* Error code on the 400 a create_group receives when no requested member can
|
|
270
|
+
* be added or invited. A creator-only group with at least one valid pending
|
|
271
|
+
* invite is allowed; the error detail carries the attempted partition.
|
|
273
272
|
*/
|
|
274
273
|
exports.CREATE_GROUP_NO_ADDABLE_MEMBERS_CODE = 'CREATE_GROUP_NO_ADDABLE_MEMBERS';
|
|
275
274
|
/**
|
package/dist/cjs/verbSchemas.js
CHANGED
|
@@ -34,6 +34,18 @@ exports.getVerbInputSchema = getVerbInputSchema;
|
|
|
34
34
|
exports.getVerbResultSchema = getVerbResultSchema;
|
|
35
35
|
const verbContract_js_1 = require("./verbContract.js");
|
|
36
36
|
const REF = (def) => ({ $ref: `#/$defs/${def}` });
|
|
37
|
+
const groupInviteRequirementsDef = {
|
|
38
|
+
type: 'object',
|
|
39
|
+
description: 'Independent gates on a pending group invite. policyApproval is social '
|
|
40
|
+
+ 'consent; ownerSessionSetup means the agent owner must choose the '
|
|
41
|
+
+ 'runtime session configuration before membership is activated.',
|
|
42
|
+
required: ['policyApproval', 'ownerSessionSetup'],
|
|
43
|
+
additionalProperties: false,
|
|
44
|
+
properties: {
|
|
45
|
+
policyApproval: { type: 'boolean' },
|
|
46
|
+
ownerSessionSetup: { type: 'boolean' },
|
|
47
|
+
},
|
|
48
|
+
};
|
|
37
49
|
const selfContextDef = {
|
|
38
50
|
type: 'object',
|
|
39
51
|
description: 'Private note-to-self attached to a cross-conversation send. Visible only to the '
|
|
@@ -820,13 +832,11 @@ const create_group_input = {
|
|
|
820
832
|
description: "Create a group conversation. Each target's groupJoinPolicy is enforced "
|
|
821
833
|
+ 'server-side with staged admission: directly-addable members join at '
|
|
822
834
|
+ 'creation, approval-required members become pending group_invite '
|
|
823
|
-
+ 'requests
|
|
824
|
-
+ '
|
|
825
|
-
+ '
|
|
826
|
-
+ '
|
|
827
|
-
+ '(
|
|
828
|
-
+ 'cannot create groups (403 CREATE_GROUP_CREATOR_SETUP_REQUIRED); only '
|
|
829
|
-
+ 'the owner can set such agents up, from the app. Under MLS, membership '
|
|
835
|
+
+ 'requests; agents needing explicit group-session setup become pending '
|
|
836
|
+
+ 'owner requests; hard-denied members are skipped (see the result). A '
|
|
837
|
+
+ 'creator-only group is valid when at least one requested member has a '
|
|
838
|
+
+ 'pending invite. A creator that itself requires setup cannot create '
|
|
839
|
+
+ 'groups (403 CREATE_GROUP_CREATOR_SETUP_REQUIRED). Under MLS, membership '
|
|
830
840
|
+ 'changes are Add/Remove proposals + Commit — a group operation is a '
|
|
831
841
|
+ 'cryptographic state change, not a codec swap.',
|
|
832
842
|
required: ['name', 'memberIds'],
|
|
@@ -844,24 +854,28 @@ const create_group_input = {
|
|
|
844
854
|
};
|
|
845
855
|
const create_group_result = {
|
|
846
856
|
type: 'object',
|
|
847
|
-
description: 'Staged admission: the group exists with the directly-
|
|
848
|
-
+ '`pending` lists
|
|
849
|
-
+ '
|
|
850
|
-
|
|
851
|
-
required: ['status', 'conversationId'],
|
|
857
|
+
description: 'Staged admission: the group exists with the directly-added members; '
|
|
858
|
+
+ '`pending` lists group_invite requests awaiting policy approval, owner '
|
|
859
|
+
+ 'session setup, or both; `skipped` lists hard-denied targets.',
|
|
860
|
+
required: ['status', 'conversationId', 'added', 'pending', 'skipped'],
|
|
852
861
|
additionalProperties: true,
|
|
853
862
|
properties: {
|
|
854
863
|
status: { const: 'created' },
|
|
855
864
|
conversationId: { type: 'string' },
|
|
865
|
+
added: {
|
|
866
|
+
type: 'array',
|
|
867
|
+
items: { type: 'string' },
|
|
868
|
+
},
|
|
856
869
|
pending: {
|
|
857
870
|
type: 'array',
|
|
858
871
|
items: {
|
|
859
872
|
type: 'object',
|
|
860
|
-
required: ['userId', 'requestId'],
|
|
873
|
+
required: ['userId', 'requestId', 'requirements'],
|
|
861
874
|
additionalProperties: true,
|
|
862
875
|
properties: {
|
|
863
876
|
userId: { type: 'string' },
|
|
864
877
|
requestId: { type: 'string' },
|
|
878
|
+
requirements: REF('groupInviteRequirements'),
|
|
865
879
|
},
|
|
866
880
|
},
|
|
867
881
|
},
|
|
@@ -881,11 +895,10 @@ const create_group_result = {
|
|
|
881
895
|
};
|
|
882
896
|
const add_member_input = {
|
|
883
897
|
type: 'object',
|
|
884
|
-
description: '
|
|
885
|
-
+ '
|
|
886
|
-
+ '
|
|
887
|
-
+ '
|
|
888
|
-
+ 'not.',
|
|
898
|
+
description: 'A target needing policy approval and/or explicit owner session setup '
|
|
899
|
+
+ 'yields one pending group_invite. Under MLS an add is an Add proposal '
|
|
900
|
+
+ '+ Commit (new epoch), not a server-side membership write — the verb '
|
|
901
|
+
+ 'semantics are stable, the mechanism is not.',
|
|
889
902
|
required: ['conversationId', 'userId'],
|
|
890
903
|
additionalProperties: false,
|
|
891
904
|
properties: {
|
|
@@ -894,8 +907,8 @@ const add_member_input = {
|
|
|
894
907
|
},
|
|
895
908
|
};
|
|
896
909
|
const add_member_result = {
|
|
897
|
-
description: 'approval
|
|
898
|
-
+ 'routed to
|
|
910
|
+
description: 'Targets needing policy approval and/or owner session setup yield one '
|
|
911
|
+
+ 'pending group_invite routed to the target or agent owner.',
|
|
899
912
|
oneOf: [
|
|
900
913
|
{
|
|
901
914
|
type: 'object',
|
|
@@ -905,9 +918,13 @@ const add_member_result = {
|
|
|
905
918
|
},
|
|
906
919
|
{
|
|
907
920
|
type: 'object',
|
|
908
|
-
required: ['status', 'requestId'],
|
|
921
|
+
required: ['status', 'requestId', 'requirements'],
|
|
909
922
|
additionalProperties: true,
|
|
910
|
-
properties: {
|
|
923
|
+
properties: {
|
|
924
|
+
status: { const: 'pending' },
|
|
925
|
+
requestId: { type: 'string' },
|
|
926
|
+
requirements: REF('groupInviteRequirements'),
|
|
927
|
+
},
|
|
911
928
|
},
|
|
912
929
|
],
|
|
913
930
|
};
|
|
@@ -1065,6 +1082,11 @@ const no_reply_input = {
|
|
|
1065
1082
|
additionalProperties: false,
|
|
1066
1083
|
properties: {
|
|
1067
1084
|
conversationId: { type: 'string', pattern: verbContract_js_1.VERB_ID_PATTERNS.runtimeId },
|
|
1085
|
+
messageId: {
|
|
1086
|
+
type: 'string',
|
|
1087
|
+
pattern: verbContract_js_1.VERB_ID_PATTERNS.runtimeId,
|
|
1088
|
+
description: 'The triggering message this silence answers, so the record cannot be misread against a later message.',
|
|
1089
|
+
},
|
|
1068
1090
|
reason: {
|
|
1069
1091
|
type: 'string',
|
|
1070
1092
|
maxLength: verbContract_js_1.VERB_LIMITS.noReplyReasonChars,
|
|
@@ -1095,6 +1117,7 @@ exports.CANON_VERBS_JSON_SCHEMA = {
|
|
|
1095
1117
|
+ 'deliberately NOT verbs — they stay host-mediated.',
|
|
1096
1118
|
$defs: {
|
|
1097
1119
|
messageId: messageIdDef,
|
|
1120
|
+
groupInviteRequirements: groupInviteRequirementsDef,
|
|
1098
1121
|
selfContext: selfContextDef,
|
|
1099
1122
|
sessionSelection: sessionSelectionDef,
|
|
1100
1123
|
mediaAttachment: mediaAttachmentDef,
|
package/dist/cjs/verbWire.js
CHANGED
|
@@ -118,7 +118,7 @@ exports.VERB_WIRE_ENVELOPE_FIELDS = {
|
|
|
118
118
|
list_contacts: { required: [], optional: [] },
|
|
119
119
|
list_contact_requests: { required: [], optional: [] },
|
|
120
120
|
list_conversations: { required: [], optional: ['limit'] },
|
|
121
|
-
no_reply: { required: [], optional: ['conversationId'] },
|
|
121
|
+
no_reply: { required: [], optional: ['conversationId', 'messageId'] },
|
|
122
122
|
};
|
|
123
123
|
// ---------------------------------------------------------------------------
|
|
124
124
|
// JSON Schema
|
|
@@ -478,7 +478,7 @@ function projectVerbIntentToWire(verb, intent, options) {
|
|
|
478
478
|
break;
|
|
479
479
|
}
|
|
480
480
|
case 'no_reply': {
|
|
481
|
-
envelope = compact({ conversationId: input.conversationId });
|
|
481
|
+
envelope = compact({ conversationId: input.conversationId, messageId: input.messageId });
|
|
482
482
|
value = compact({ reason: input.reason });
|
|
483
483
|
break;
|
|
484
484
|
}
|
|
@@ -620,7 +620,7 @@ function mergeVerbWireToIntent(request) {
|
|
|
620
620
|
case 'list_conversations':
|
|
621
621
|
return compact({ limit: envelope.limit });
|
|
622
622
|
case 'no_reply':
|
|
623
|
-
return compact({ conversationId: envelope.conversationId, reason: value.reason });
|
|
623
|
+
return compact({ conversationId: envelope.conversationId, messageId: envelope.messageId, reason: value.reason });
|
|
624
624
|
}
|
|
625
625
|
}
|
|
626
626
|
/**
|
package/dist/contactRequest.d.ts
CHANGED
|
@@ -1,4 +1,13 @@
|
|
|
1
1
|
export type SerializedContactRequestStatus = 'pending' | 'approved' | 'rejected' | 'expired';
|
|
2
|
+
/**
|
|
3
|
+
* Independent gates that must be satisfied before a pending group invite can
|
|
4
|
+
* add its target.
|
|
5
|
+
*/
|
|
6
|
+
export interface GroupInviteRequirements {
|
|
7
|
+
policyApproval: boolean;
|
|
8
|
+
ownerSessionSetup: boolean;
|
|
9
|
+
}
|
|
10
|
+
export declare function readGroupInviteRequirements(value: unknown): GroupInviteRequirements;
|
|
2
11
|
export interface SerializedContactRequest {
|
|
3
12
|
id: string;
|
|
4
13
|
requesterId: string;
|
|
@@ -18,6 +27,8 @@ export interface SerializedContactRequest {
|
|
|
18
27
|
conversationId: string;
|
|
19
28
|
groupName: string | null;
|
|
20
29
|
};
|
|
30
|
+
/** Present for group_invite payloads; omitted for DM requests. */
|
|
31
|
+
requirements?: GroupInviteRequirements;
|
|
21
32
|
createdAt: string | null;
|
|
22
33
|
resolvedAt?: string | null;
|
|
23
34
|
expiresAt?: string | null;
|
package/dist/contactRequest.js
CHANGED
|
@@ -1,3 +1,14 @@
|
|
|
1
|
+
export function readGroupInviteRequirements(value) {
|
|
2
|
+
const record = isRecord(value) ? value : null;
|
|
3
|
+
if (typeof record?.policyApproval !== 'boolean'
|
|
4
|
+
|| typeof record.ownerSessionSetup !== 'boolean') {
|
|
5
|
+
throw new TypeError('Invalid group invite requirements');
|
|
6
|
+
}
|
|
7
|
+
return {
|
|
8
|
+
policyApproval: record.policyApproval,
|
|
9
|
+
ownerSessionSetup: record.ownerSessionSetup,
|
|
10
|
+
};
|
|
11
|
+
}
|
|
1
12
|
function isRecord(value) {
|
|
2
13
|
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
3
14
|
}
|
|
@@ -82,7 +93,15 @@ export function serializeContactRequest(requestId, data) {
|
|
|
82
93
|
expiresAt: normalizeTimestamp(data.expiresAt),
|
|
83
94
|
};
|
|
84
95
|
if (groupContext) {
|
|
96
|
+
let requirements;
|
|
97
|
+
try {
|
|
98
|
+
requirements = readGroupInviteRequirements(data.requirements);
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
85
103
|
payload.groupContext = groupContext;
|
|
104
|
+
payload.requirements = requirements;
|
|
86
105
|
}
|
|
87
106
|
if (data.targetUserType === 'ai_agent') {
|
|
88
107
|
payload.targetOwnerId = data.targetOwnerId;
|
package/dist/verbContract.d.ts
CHANGED
|
@@ -42,6 +42,7 @@
|
|
|
42
42
|
* `findVerbByteLimitViolations()` (or equivalent checks from the emitted
|
|
43
43
|
* canon-verbs.limits.json) after schema validation.
|
|
44
44
|
*/
|
|
45
|
+
import type { GroupInviteRequirements } from './contactRequest.js';
|
|
45
46
|
/** Identifier for this contract document. */
|
|
46
47
|
export declare const CANON_VERBS_SCHEMA_VERSION = "canon.verbs.v1";
|
|
47
48
|
/** $id of the emitted JSON Schema bundle. */
|
|
@@ -602,27 +603,29 @@ export interface CreateGroupInput {
|
|
|
602
603
|
* groupJoinPolicy is enforced server-side. */
|
|
603
604
|
memberIds: string[];
|
|
604
605
|
}
|
|
606
|
+
export interface PendingGroupInviteResult {
|
|
607
|
+
userId: string;
|
|
608
|
+
requestId: string;
|
|
609
|
+
requirements: GroupInviteRequirements;
|
|
610
|
+
}
|
|
605
611
|
export interface CreateGroupResult {
|
|
606
612
|
status: 'created';
|
|
607
613
|
conversationId: string;
|
|
608
|
-
/**
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
* only their owner can provide session setup, from the app). */
|
|
616
|
-
skipped?: Array<{
|
|
614
|
+
/** Members admitted synchronously during creation. */
|
|
615
|
+
added: string[];
|
|
616
|
+
/** group_invite requests awaiting policy approval, owner setup, or both. */
|
|
617
|
+
pending: PendingGroupInviteResult[];
|
|
618
|
+
/** Members the staged create could not admit: hard policy denials
|
|
619
|
+
* (owner-only, blocked, inactive, not-found, …). */
|
|
620
|
+
skipped: Array<{
|
|
617
621
|
userId: string;
|
|
618
622
|
reason: string;
|
|
619
623
|
}>;
|
|
620
624
|
}
|
|
621
625
|
/**
|
|
622
|
-
* Error code on the 400 a create_group receives when
|
|
623
|
-
*
|
|
624
|
-
*
|
|
625
|
-
* partition: pendingRequired[] and skipped[].
|
|
626
|
+
* Error code on the 400 a create_group receives when no requested member can
|
|
627
|
+
* be added or invited. A creator-only group with at least one valid pending
|
|
628
|
+
* invite is allowed; the error detail carries the attempted partition.
|
|
626
629
|
*/
|
|
627
630
|
export declare const CREATE_GROUP_NO_ADDABLE_MEMBERS_CODE = "CREATE_GROUP_NO_ADDABLE_MEMBERS";
|
|
628
631
|
/**
|
|
@@ -637,12 +640,13 @@ export interface AddMemberInput {
|
|
|
637
640
|
conversationId: string;
|
|
638
641
|
userId: string;
|
|
639
642
|
}
|
|
640
|
-
/** approval
|
|
643
|
+
/** Policy approval and/or required owner setup yield one pending group invite. */
|
|
641
644
|
export type AddMemberResult = {
|
|
642
645
|
status: 'added';
|
|
643
646
|
} | {
|
|
644
647
|
status: 'pending';
|
|
645
648
|
requestId: string;
|
|
649
|
+
requirements: GroupInviteRequirements;
|
|
646
650
|
};
|
|
647
651
|
export interface RemoveMemberInput {
|
|
648
652
|
conversationId: string;
|
package/dist/verbContract.js
CHANGED
|
@@ -260,10 +260,9 @@ export function normalizeVerbNativeMetadata(value) {
|
|
|
260
260
|
return Object.keys(native).length > 0 ? native : undefined;
|
|
261
261
|
}
|
|
262
262
|
/**
|
|
263
|
-
* Error code on the 400 a create_group receives when
|
|
264
|
-
*
|
|
265
|
-
*
|
|
266
|
-
* partition: pendingRequired[] and skipped[].
|
|
263
|
+
* Error code on the 400 a create_group receives when no requested member can
|
|
264
|
+
* be added or invited. A creator-only group with at least one valid pending
|
|
265
|
+
* invite is allowed; the error detail carries the attempted partition.
|
|
267
266
|
*/
|
|
268
267
|
export const CREATE_GROUP_NO_ADDABLE_MEMBERS_CODE = 'CREATE_GROUP_NO_ADDABLE_MEMBERS';
|
|
269
268
|
/**
|
package/dist/verbSchemas.d.ts
CHANGED
|
@@ -183,6 +183,20 @@ export declare const CANON_VERBS_JSON_SCHEMA: {
|
|
|
183
183
|
};
|
|
184
184
|
readonly description: string;
|
|
185
185
|
};
|
|
186
|
+
readonly groupInviteRequirements: {
|
|
187
|
+
readonly type: "object";
|
|
188
|
+
readonly description: string;
|
|
189
|
+
readonly required: readonly ["policyApproval", "ownerSessionSetup"];
|
|
190
|
+
readonly additionalProperties: false;
|
|
191
|
+
readonly properties: {
|
|
192
|
+
readonly policyApproval: {
|
|
193
|
+
readonly type: "boolean";
|
|
194
|
+
};
|
|
195
|
+
readonly ownerSessionSetup: {
|
|
196
|
+
readonly type: "boolean";
|
|
197
|
+
};
|
|
198
|
+
};
|
|
199
|
+
};
|
|
186
200
|
readonly selfContext: {
|
|
187
201
|
readonly type: "object";
|
|
188
202
|
readonly description: string;
|
|
@@ -1404,7 +1418,7 @@ export declare const CANON_VERBS_JSON_SCHEMA: {
|
|
|
1404
1418
|
readonly create_group_result: {
|
|
1405
1419
|
readonly type: "object";
|
|
1406
1420
|
readonly description: string;
|
|
1407
|
-
readonly required: readonly ["status", "conversationId"];
|
|
1421
|
+
readonly required: readonly ["status", "conversationId", "added", "pending", "skipped"];
|
|
1408
1422
|
readonly additionalProperties: true;
|
|
1409
1423
|
readonly properties: {
|
|
1410
1424
|
readonly status: {
|
|
@@ -1413,11 +1427,17 @@ export declare const CANON_VERBS_JSON_SCHEMA: {
|
|
|
1413
1427
|
readonly conversationId: {
|
|
1414
1428
|
readonly type: "string";
|
|
1415
1429
|
};
|
|
1430
|
+
readonly added: {
|
|
1431
|
+
readonly type: "array";
|
|
1432
|
+
readonly items: {
|
|
1433
|
+
readonly type: "string";
|
|
1434
|
+
};
|
|
1435
|
+
};
|
|
1416
1436
|
readonly pending: {
|
|
1417
1437
|
readonly type: "array";
|
|
1418
1438
|
readonly items: {
|
|
1419
1439
|
readonly type: "object";
|
|
1420
|
-
readonly required: readonly ["userId", "requestId"];
|
|
1440
|
+
readonly required: readonly ["userId", "requestId", "requirements"];
|
|
1421
1441
|
readonly additionalProperties: true;
|
|
1422
1442
|
readonly properties: {
|
|
1423
1443
|
readonly userId: {
|
|
@@ -1426,6 +1446,9 @@ export declare const CANON_VERBS_JSON_SCHEMA: {
|
|
|
1426
1446
|
readonly requestId: {
|
|
1427
1447
|
readonly type: "string";
|
|
1428
1448
|
};
|
|
1449
|
+
readonly requirements: {
|
|
1450
|
+
$ref: string;
|
|
1451
|
+
};
|
|
1429
1452
|
};
|
|
1430
1453
|
};
|
|
1431
1454
|
};
|
|
@@ -1476,7 +1499,7 @@ export declare const CANON_VERBS_JSON_SCHEMA: {
|
|
|
1476
1499
|
};
|
|
1477
1500
|
}, {
|
|
1478
1501
|
readonly type: "object";
|
|
1479
|
-
readonly required: readonly ["status", "requestId"];
|
|
1502
|
+
readonly required: readonly ["status", "requestId", "requirements"];
|
|
1480
1503
|
readonly additionalProperties: true;
|
|
1481
1504
|
readonly properties: {
|
|
1482
1505
|
readonly status: {
|
|
@@ -1485,6 +1508,9 @@ export declare const CANON_VERBS_JSON_SCHEMA: {
|
|
|
1485
1508
|
readonly requestId: {
|
|
1486
1509
|
readonly type: "string";
|
|
1487
1510
|
};
|
|
1511
|
+
readonly requirements: {
|
|
1512
|
+
$ref: string;
|
|
1513
|
+
};
|
|
1488
1514
|
};
|
|
1489
1515
|
}];
|
|
1490
1516
|
};
|
|
@@ -1709,6 +1735,11 @@ export declare const CANON_VERBS_JSON_SCHEMA: {
|
|
|
1709
1735
|
readonly type: "string";
|
|
1710
1736
|
readonly pattern: "^[A-Za-z0-9_.:-]{1,160}$";
|
|
1711
1737
|
};
|
|
1738
|
+
readonly messageId: {
|
|
1739
|
+
readonly type: "string";
|
|
1740
|
+
readonly pattern: "^[A-Za-z0-9_.:-]{1,160}$";
|
|
1741
|
+
readonly description: "The triggering message this silence answers, so the record cannot be misread against a later message.";
|
|
1742
|
+
};
|
|
1712
1743
|
readonly reason: {
|
|
1713
1744
|
readonly type: "string";
|
|
1714
1745
|
readonly maxLength: 500;
|
package/dist/verbSchemas.js
CHANGED
|
@@ -29,6 +29,18 @@
|
|
|
29
29
|
*/
|
|
30
30
|
import { CANON_VERBS_SCHEMA_ID, CANON_VERB_NAMES, SELF_CONTEXT_TYPE, VERB_ID_PATTERNS, VERB_LIMITS, } from './verbContract.js';
|
|
31
31
|
const REF = (def) => ({ $ref: `#/$defs/${def}` });
|
|
32
|
+
const groupInviteRequirementsDef = {
|
|
33
|
+
type: 'object',
|
|
34
|
+
description: 'Independent gates on a pending group invite. policyApproval is social '
|
|
35
|
+
+ 'consent; ownerSessionSetup means the agent owner must choose the '
|
|
36
|
+
+ 'runtime session configuration before membership is activated.',
|
|
37
|
+
required: ['policyApproval', 'ownerSessionSetup'],
|
|
38
|
+
additionalProperties: false,
|
|
39
|
+
properties: {
|
|
40
|
+
policyApproval: { type: 'boolean' },
|
|
41
|
+
ownerSessionSetup: { type: 'boolean' },
|
|
42
|
+
},
|
|
43
|
+
};
|
|
32
44
|
const selfContextDef = {
|
|
33
45
|
type: 'object',
|
|
34
46
|
description: 'Private note-to-self attached to a cross-conversation send. Visible only to the '
|
|
@@ -815,13 +827,11 @@ const create_group_input = {
|
|
|
815
827
|
description: "Create a group conversation. Each target's groupJoinPolicy is enforced "
|
|
816
828
|
+ 'server-side with staged admission: directly-addable members join at '
|
|
817
829
|
+ 'creation, approval-required members become pending group_invite '
|
|
818
|
-
+ 'requests
|
|
819
|
-
+ '
|
|
820
|
-
+ '
|
|
821
|
-
+ '
|
|
822
|
-
+ '(
|
|
823
|
-
+ 'cannot create groups (403 CREATE_GROUP_CREATOR_SETUP_REQUIRED); only '
|
|
824
|
-
+ 'the owner can set such agents up, from the app. Under MLS, membership '
|
|
830
|
+
+ 'requests; agents needing explicit group-session setup become pending '
|
|
831
|
+
+ 'owner requests; hard-denied members are skipped (see the result). A '
|
|
832
|
+
+ 'creator-only group is valid when at least one requested member has a '
|
|
833
|
+
+ 'pending invite. A creator that itself requires setup cannot create '
|
|
834
|
+
+ 'groups (403 CREATE_GROUP_CREATOR_SETUP_REQUIRED). Under MLS, membership '
|
|
825
835
|
+ 'changes are Add/Remove proposals + Commit — a group operation is a '
|
|
826
836
|
+ 'cryptographic state change, not a codec swap.',
|
|
827
837
|
required: ['name', 'memberIds'],
|
|
@@ -839,24 +849,28 @@ const create_group_input = {
|
|
|
839
849
|
};
|
|
840
850
|
const create_group_result = {
|
|
841
851
|
type: 'object',
|
|
842
|
-
description: 'Staged admission: the group exists with the directly-
|
|
843
|
-
+ '`pending` lists
|
|
844
|
-
+ '
|
|
845
|
-
|
|
846
|
-
required: ['status', 'conversationId'],
|
|
852
|
+
description: 'Staged admission: the group exists with the directly-added members; '
|
|
853
|
+
+ '`pending` lists group_invite requests awaiting policy approval, owner '
|
|
854
|
+
+ 'session setup, or both; `skipped` lists hard-denied targets.',
|
|
855
|
+
required: ['status', 'conversationId', 'added', 'pending', 'skipped'],
|
|
847
856
|
additionalProperties: true,
|
|
848
857
|
properties: {
|
|
849
858
|
status: { const: 'created' },
|
|
850
859
|
conversationId: { type: 'string' },
|
|
860
|
+
added: {
|
|
861
|
+
type: 'array',
|
|
862
|
+
items: { type: 'string' },
|
|
863
|
+
},
|
|
851
864
|
pending: {
|
|
852
865
|
type: 'array',
|
|
853
866
|
items: {
|
|
854
867
|
type: 'object',
|
|
855
|
-
required: ['userId', 'requestId'],
|
|
868
|
+
required: ['userId', 'requestId', 'requirements'],
|
|
856
869
|
additionalProperties: true,
|
|
857
870
|
properties: {
|
|
858
871
|
userId: { type: 'string' },
|
|
859
872
|
requestId: { type: 'string' },
|
|
873
|
+
requirements: REF('groupInviteRequirements'),
|
|
860
874
|
},
|
|
861
875
|
},
|
|
862
876
|
},
|
|
@@ -876,11 +890,10 @@ const create_group_result = {
|
|
|
876
890
|
};
|
|
877
891
|
const add_member_input = {
|
|
878
892
|
type: 'object',
|
|
879
|
-
description: '
|
|
880
|
-
+ '
|
|
881
|
-
+ '
|
|
882
|
-
+ '
|
|
883
|
-
+ 'not.',
|
|
893
|
+
description: 'A target needing policy approval and/or explicit owner session setup '
|
|
894
|
+
+ 'yields one pending group_invite. Under MLS an add is an Add proposal '
|
|
895
|
+
+ '+ Commit (new epoch), not a server-side membership write — the verb '
|
|
896
|
+
+ 'semantics are stable, the mechanism is not.',
|
|
884
897
|
required: ['conversationId', 'userId'],
|
|
885
898
|
additionalProperties: false,
|
|
886
899
|
properties: {
|
|
@@ -889,8 +902,8 @@ const add_member_input = {
|
|
|
889
902
|
},
|
|
890
903
|
};
|
|
891
904
|
const add_member_result = {
|
|
892
|
-
description: 'approval
|
|
893
|
-
+ 'routed to
|
|
905
|
+
description: 'Targets needing policy approval and/or owner session setup yield one '
|
|
906
|
+
+ 'pending group_invite routed to the target or agent owner.',
|
|
894
907
|
oneOf: [
|
|
895
908
|
{
|
|
896
909
|
type: 'object',
|
|
@@ -900,9 +913,13 @@ const add_member_result = {
|
|
|
900
913
|
},
|
|
901
914
|
{
|
|
902
915
|
type: 'object',
|
|
903
|
-
required: ['status', 'requestId'],
|
|
916
|
+
required: ['status', 'requestId', 'requirements'],
|
|
904
917
|
additionalProperties: true,
|
|
905
|
-
properties: {
|
|
918
|
+
properties: {
|
|
919
|
+
status: { const: 'pending' },
|
|
920
|
+
requestId: { type: 'string' },
|
|
921
|
+
requirements: REF('groupInviteRequirements'),
|
|
922
|
+
},
|
|
906
923
|
},
|
|
907
924
|
],
|
|
908
925
|
};
|
|
@@ -1060,6 +1077,11 @@ const no_reply_input = {
|
|
|
1060
1077
|
additionalProperties: false,
|
|
1061
1078
|
properties: {
|
|
1062
1079
|
conversationId: { type: 'string', pattern: VERB_ID_PATTERNS.runtimeId },
|
|
1080
|
+
messageId: {
|
|
1081
|
+
type: 'string',
|
|
1082
|
+
pattern: VERB_ID_PATTERNS.runtimeId,
|
|
1083
|
+
description: 'The triggering message this silence answers, so the record cannot be misread against a later message.',
|
|
1084
|
+
},
|
|
1063
1085
|
reason: {
|
|
1064
1086
|
type: 'string',
|
|
1065
1087
|
maxLength: VERB_LIMITS.noReplyReasonChars,
|
|
@@ -1090,6 +1112,7 @@ export const CANON_VERBS_JSON_SCHEMA = {
|
|
|
1090
1112
|
+ 'deliberately NOT verbs — they stay host-mediated.',
|
|
1091
1113
|
$defs: {
|
|
1092
1114
|
messageId: messageIdDef,
|
|
1115
|
+
groupInviteRequirements: groupInviteRequirementsDef,
|
|
1093
1116
|
selfContext: selfContextDef,
|
|
1094
1117
|
sessionSelection: sessionSelectionDef,
|
|
1095
1118
|
mediaAttachment: mediaAttachmentDef,
|
package/dist/verbWire.js
CHANGED
|
@@ -112,7 +112,7 @@ export const VERB_WIRE_ENVELOPE_FIELDS = {
|
|
|
112
112
|
list_contacts: { required: [], optional: [] },
|
|
113
113
|
list_contact_requests: { required: [], optional: [] },
|
|
114
114
|
list_conversations: { required: [], optional: ['limit'] },
|
|
115
|
-
no_reply: { required: [], optional: ['conversationId'] },
|
|
115
|
+
no_reply: { required: [], optional: ['conversationId', 'messageId'] },
|
|
116
116
|
};
|
|
117
117
|
// ---------------------------------------------------------------------------
|
|
118
118
|
// JSON Schema
|
|
@@ -472,7 +472,7 @@ export function projectVerbIntentToWire(verb, intent, options) {
|
|
|
472
472
|
break;
|
|
473
473
|
}
|
|
474
474
|
case 'no_reply': {
|
|
475
|
-
envelope = compact({ conversationId: input.conversationId });
|
|
475
|
+
envelope = compact({ conversationId: input.conversationId, messageId: input.messageId });
|
|
476
476
|
value = compact({ reason: input.reason });
|
|
477
477
|
break;
|
|
478
478
|
}
|
|
@@ -614,7 +614,7 @@ export function mergeVerbWireToIntent(request) {
|
|
|
614
614
|
case 'list_conversations':
|
|
615
615
|
return compact({ limit: envelope.limit });
|
|
616
616
|
case 'no_reply':
|
|
617
|
-
return compact({ conversationId: envelope.conversationId, reason: value.reason });
|
|
617
|
+
return compact({ conversationId: envelope.conversationId, messageId: envelope.messageId, reason: value.reason });
|
|
618
618
|
}
|
|
619
619
|
}
|
|
620
620
|
/**
|