@canonmsg/backend-contracts 6.4.0 → 7.0.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/README.md +1 -1
- package/dist/canon-verb-wire.schema.json +0 -6
- package/dist/canon-verbs.schema.json +4 -61
- package/dist/cjs/communication.js +35 -0
- package/dist/cjs/contactRequest.js +7 -56
- package/dist/cjs/index.js +1 -0
- package/dist/cjs/verbContract.js +0 -1
- package/dist/cjs/verbSchemas.js +6 -40
- package/dist/cjs/verbWire.js +3 -29
- package/dist/communication.d.ts +91 -0
- package/dist/communication.js +31 -0
- package/dist/contactRequest.d.ts +7 -21
- package/dist/contactRequest.js +7 -54
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/verbContract.d.ts +7 -23
- package/dist/verbContract.js +0 -1
- package/dist/verbSchemas.d.ts +3 -42
- package/dist/verbSchemas.js +6 -40
- package/dist/verbWire.d.ts +2 -8
- package/dist/verbWire.js +3 -29
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -16,7 +16,7 @@ Node.js 18+. Ships both ESM and CommonJS builds — Cloud Functions consume the
|
|
|
16
16
|
|
|
17
17
|
## What is in it
|
|
18
18
|
|
|
19
|
-
**The verb contract.** `canon.verbs.v1` (`verbContract.ts`) is the plaintext *intent* schema —
|
|
19
|
+
**The verb contract.** `canon.verbs.v1` (`verbContract.ts`) is the plaintext *intent* schema — seventeen verbs, their input and result schemas, byte limits, and rate limits. `canon.verb-wire.v1` (`verbWire.ts`) is what actually crosses the network: a server-readable envelope plus a body that is either `{ encoding: 'json', value }` or `{ encoding: 'mls', … }`. `projectVerbIntentToWire` and `mergeVerbWireToIntent` are the two halves of that split, so bindings and the server never disagree about which fields are envelope and which are content.
|
|
20
20
|
|
|
21
21
|
```ts
|
|
22
22
|
import {
|
|
@@ -33,7 +33,6 @@
|
|
|
33
33
|
"list_contacts",
|
|
34
34
|
"list_contact_requests",
|
|
35
35
|
"list_conversations",
|
|
36
|
-
"cancel_contact_request",
|
|
37
36
|
"no_reply"
|
|
38
37
|
]
|
|
39
38
|
},
|
|
@@ -428,10 +427,6 @@
|
|
|
428
427
|
"null"
|
|
429
428
|
]
|
|
430
429
|
},
|
|
431
|
-
"sourceMessageId": {
|
|
432
|
-
"type": "string",
|
|
433
|
-
"minLength": 1
|
|
434
|
-
},
|
|
435
430
|
"turnSemantics": {
|
|
436
431
|
"enum": [
|
|
437
432
|
"progress",
|
|
@@ -513,7 +508,6 @@
|
|
|
513
508
|
"list_contacts",
|
|
514
509
|
"list_contact_requests",
|
|
515
510
|
"list_conversations",
|
|
516
|
-
"cancel_contact_request",
|
|
517
511
|
"no_reply"
|
|
518
512
|
]
|
|
519
513
|
},
|
|
@@ -1767,26 +1767,11 @@
|
|
|
1767
1767
|
"list_contact_requests_input": {
|
|
1768
1768
|
"type": "object",
|
|
1769
1769
|
"additionalProperties": false,
|
|
1770
|
-
"properties": {
|
|
1771
|
-
"direction": {
|
|
1772
|
-
"enum": [
|
|
1773
|
-
"inbound",
|
|
1774
|
-
"outbound"
|
|
1775
|
-
]
|
|
1776
|
-
},
|
|
1777
|
-
"includeResolved": {
|
|
1778
|
-
"type": "boolean"
|
|
1779
|
-
},
|
|
1780
|
-
"limit": {
|
|
1781
|
-
"type": "integer",
|
|
1782
|
-
"minimum": 1,
|
|
1783
|
-
"maximum": 100
|
|
1784
|
-
}
|
|
1785
|
-
}
|
|
1770
|
+
"properties": {}
|
|
1786
1771
|
},
|
|
1787
1772
|
"list_contact_requests_result": {
|
|
1788
1773
|
"type": "object",
|
|
1789
|
-
"description": "
|
|
1774
|
+
"description": "Pending inbound requests, newest first, capped at 100. Items are SerializedContactRequest (backend-contracts contactRequest.ts). Read-only awareness: approval routes to the owner; agents cannot approve/reject.",
|
|
1790
1775
|
"required": [
|
|
1791
1776
|
"requests"
|
|
1792
1777
|
],
|
|
@@ -1801,8 +1786,7 @@
|
|
|
1801
1786
|
"requesterId",
|
|
1802
1787
|
"targetId",
|
|
1803
1788
|
"status",
|
|
1804
|
-
"kind"
|
|
1805
|
-
"phase"
|
|
1789
|
+
"kind"
|
|
1806
1790
|
],
|
|
1807
1791
|
"additionalProperties": true,
|
|
1808
1792
|
"properties": {
|
|
@@ -1823,8 +1807,7 @@
|
|
|
1823
1807
|
"pending",
|
|
1824
1808
|
"approved",
|
|
1825
1809
|
"rejected",
|
|
1826
|
-
"expired"
|
|
1827
|
-
"cancelled"
|
|
1810
|
+
"expired"
|
|
1828
1811
|
]
|
|
1829
1812
|
},
|
|
1830
1813
|
"kind": {
|
|
@@ -1833,17 +1816,6 @@
|
|
|
1833
1816
|
"group_invite"
|
|
1834
1817
|
]
|
|
1835
1818
|
},
|
|
1836
|
-
"phase": {
|
|
1837
|
-
"enum": [
|
|
1838
|
-
"awaiting_owner",
|
|
1839
|
-
"starting",
|
|
1840
|
-
"connected",
|
|
1841
|
-
"rejected",
|
|
1842
|
-
"expired",
|
|
1843
|
-
"cancelled",
|
|
1844
|
-
"failed"
|
|
1845
|
-
]
|
|
1846
|
-
},
|
|
1847
1819
|
"message": {
|
|
1848
1820
|
"type": [
|
|
1849
1821
|
"string",
|
|
@@ -1977,35 +1949,6 @@
|
|
|
1977
1949
|
}
|
|
1978
1950
|
}
|
|
1979
1951
|
},
|
|
1980
|
-
"cancel_contact_request_input": {
|
|
1981
|
-
"type": "object",
|
|
1982
|
-
"required": [
|
|
1983
|
-
"requestId"
|
|
1984
|
-
],
|
|
1985
|
-
"additionalProperties": false,
|
|
1986
|
-
"properties": {
|
|
1987
|
-
"requestId": {
|
|
1988
|
-
"type": "string",
|
|
1989
|
-
"pattern": "^[A-Za-z0-9_.:-]{1,160}$"
|
|
1990
|
-
}
|
|
1991
|
-
}
|
|
1992
|
-
},
|
|
1993
|
-
"cancel_contact_request_result": {
|
|
1994
|
-
"type": "object",
|
|
1995
|
-
"required": [
|
|
1996
|
-
"status",
|
|
1997
|
-
"requestId"
|
|
1998
|
-
],
|
|
1999
|
-
"additionalProperties": true,
|
|
2000
|
-
"properties": {
|
|
2001
|
-
"status": {
|
|
2002
|
-
"const": "cancelled"
|
|
2003
|
-
},
|
|
2004
|
-
"requestId": {
|
|
2005
|
-
"type": "string"
|
|
2006
|
-
}
|
|
2007
|
-
}
|
|
2008
|
-
},
|
|
2009
1952
|
"no_reply_input": {
|
|
2010
1953
|
"type": "object",
|
|
2011
1954
|
"description": "End this turn without posting anything to the conversation. Nothing is rendered and no other member or agent is triggered.",
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.readCommunicationRule = readCommunicationRule;
|
|
4
|
+
exports.parseDirectConversationSelection = parseDirectConversationSelection;
|
|
5
|
+
/** Missing or invalid policy never widens access. */
|
|
6
|
+
function readCommunicationRule(value) {
|
|
7
|
+
if (value === 'open' || value === 'approval-required' || value === 'closed') {
|
|
8
|
+
return value;
|
|
9
|
+
}
|
|
10
|
+
// Existing owner-only records have the same outsider-facing meaning as
|
|
11
|
+
// closed; the agent owner bypass remains a separate server-side rule.
|
|
12
|
+
if (value === 'owner-only')
|
|
13
|
+
return 'closed';
|
|
14
|
+
return 'approval-required';
|
|
15
|
+
}
|
|
16
|
+
function parseDirectConversationSelection(value) {
|
|
17
|
+
if (value == null)
|
|
18
|
+
return { mode: 'latest_or_new' };
|
|
19
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
20
|
+
throw new Error('selection must be an object');
|
|
21
|
+
}
|
|
22
|
+
const record = value;
|
|
23
|
+
if (record.mode === 'latest_or_new' || record.mode === 'new') {
|
|
24
|
+
return { mode: record.mode };
|
|
25
|
+
}
|
|
26
|
+
if (record.mode === 'specific') {
|
|
27
|
+
const conversationId = typeof record.conversationId === 'string'
|
|
28
|
+
? record.conversationId.trim()
|
|
29
|
+
: '';
|
|
30
|
+
if (!conversationId)
|
|
31
|
+
throw new Error('selection.conversationId is required');
|
|
32
|
+
return { mode: 'specific', conversationId };
|
|
33
|
+
}
|
|
34
|
+
throw new Error('selection.mode is invalid');
|
|
35
|
+
}
|
|
@@ -1,29 +1,18 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.readContactRequestRequirements = readContactRequestRequirements;
|
|
4
3
|
exports.readGroupInviteRequirements = readGroupInviteRequirements;
|
|
5
|
-
exports.deriveContactRequestPhase = deriveContactRequestPhase;
|
|
6
4
|
exports.serializeContactRequest = serializeContactRequest;
|
|
7
|
-
function
|
|
5
|
+
function readGroupInviteRequirements(value) {
|
|
8
6
|
const record = isRecord(value) ? value : null;
|
|
9
7
|
if (typeof record?.policyApproval !== 'boolean'
|
|
10
8
|
|| typeof record.ownerSessionSetup !== 'boolean') {
|
|
11
|
-
throw new TypeError('Invalid
|
|
9
|
+
throw new TypeError('Invalid group invite requirements');
|
|
12
10
|
}
|
|
13
11
|
return {
|
|
14
12
|
policyApproval: record.policyApproval,
|
|
15
13
|
ownerSessionSetup: record.ownerSessionSetup,
|
|
16
14
|
};
|
|
17
15
|
}
|
|
18
|
-
/** Backward-compatible parser retained for existing group invite clients. */
|
|
19
|
-
function readGroupInviteRequirements(value) {
|
|
20
|
-
try {
|
|
21
|
-
return readContactRequestRequirements(value);
|
|
22
|
-
}
|
|
23
|
-
catch {
|
|
24
|
-
throw new TypeError('Invalid group invite requirements');
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
16
|
function isRecord(value) {
|
|
28
17
|
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
29
18
|
}
|
|
@@ -41,36 +30,12 @@ function normalizeStatus(value) {
|
|
|
41
30
|
if (value === 'pending'
|
|
42
31
|
|| value === 'approved'
|
|
43
32
|
|| value === 'rejected'
|
|
44
|
-
|| value === 'expired'
|
|
45
|
-
|| value === 'cancelled') {
|
|
33
|
+
|| value === 'expired') {
|
|
46
34
|
return value;
|
|
47
35
|
}
|
|
48
36
|
return 'pending';
|
|
49
37
|
}
|
|
50
|
-
function
|
|
51
|
-
const status = data.status;
|
|
52
|
-
const openingStatus = data.openingMessageStatus;
|
|
53
|
-
let phase;
|
|
54
|
-
if (status === 'cancelled')
|
|
55
|
-
phase = 'cancelled';
|
|
56
|
-
else if (status === 'rejected')
|
|
57
|
-
phase = 'rejected';
|
|
58
|
-
else if (status === 'expired')
|
|
59
|
-
phase = 'expired';
|
|
60
|
-
else if (status === 'delivery_failed' || openingStatus === 'failed')
|
|
61
|
-
phase = 'failed';
|
|
62
|
-
else if (status === 'approved')
|
|
63
|
-
phase = 'connected';
|
|
64
|
-
else if (status === 'delivery_pending'
|
|
65
|
-
|| openingStatus === 'ready'
|
|
66
|
-
|| openingStatus === 'delivering'
|
|
67
|
-
|| openingStatus === 'retryable')
|
|
68
|
-
phase = 'starting';
|
|
69
|
-
else
|
|
70
|
-
phase = 'awaiting_owner';
|
|
71
|
-
return phase;
|
|
72
|
-
}
|
|
73
|
-
function serializeContactRequest(requestId, data, options = {}) {
|
|
38
|
+
function serializeContactRequest(requestId, data) {
|
|
74
39
|
if (typeof data.approverId !== 'string' || data.approverId.length === 0) {
|
|
75
40
|
return null;
|
|
76
41
|
}
|
|
@@ -127,34 +92,20 @@ function serializeContactRequest(requestId, data, options = {}) {
|
|
|
127
92
|
message: null,
|
|
128
93
|
status: normalizeStatus(data.status),
|
|
129
94
|
kind: kindValue,
|
|
130
|
-
phase: deriveContactRequestPhase(data),
|
|
131
95
|
createdAt: normalizeTimestamp(data.createdAt),
|
|
132
96
|
resolvedAt: normalizeTimestamp(data.resolvedAt),
|
|
133
97
|
expiresAt: normalizeTimestamp(data.expiresAt),
|
|
134
98
|
};
|
|
135
|
-
if (groupContext
|
|
99
|
+
if (groupContext) {
|
|
136
100
|
let requirements;
|
|
137
101
|
try {
|
|
138
|
-
requirements = data.requirements
|
|
139
|
-
? { policyApproval: true, ownerSessionSetup: false }
|
|
140
|
-
: readContactRequestRequirements(data.requirements);
|
|
102
|
+
requirements = readGroupInviteRequirements(data.requirements);
|
|
141
103
|
}
|
|
142
104
|
catch {
|
|
143
105
|
return null;
|
|
144
106
|
}
|
|
145
|
-
payload.requirements = requirements;
|
|
146
|
-
}
|
|
147
|
-
if (groupContext)
|
|
148
107
|
payload.groupContext = groupContext;
|
|
149
|
-
|
|
150
|
-
if (typeof data.sourceConversationId === 'string' && data.sourceConversationId.length > 0) {
|
|
151
|
-
payload.sourceConversationId = data.sourceConversationId;
|
|
152
|
-
}
|
|
153
|
-
if (payload.phase === 'connected'
|
|
154
|
-
&& typeof data.openingMessageConversationId === 'string'
|
|
155
|
-
&& data.openingMessageConversationId.length > 0) {
|
|
156
|
-
payload.conversationId = data.openingMessageConversationId;
|
|
157
|
-
}
|
|
108
|
+
payload.requirements = requirements;
|
|
158
109
|
}
|
|
159
110
|
if (data.targetUserType === 'ai_agent') {
|
|
160
111
|
payload.targetOwnerId = data.targetOwnerId;
|
package/dist/cjs/index.js
CHANGED
|
@@ -29,6 +29,7 @@ __exportStar(require("./verbContract.js"), exports);
|
|
|
29
29
|
__exportStar(require("./verbSchemas.js"), exports);
|
|
30
30
|
__exportStar(require("./verbWire.js"), exports);
|
|
31
31
|
__exportStar(require("./accessPolicy.js"), exports);
|
|
32
|
+
__exportStar(require("./communication.js"), exports);
|
|
32
33
|
__exportStar(require("./apiErrors.js"), exports);
|
|
33
34
|
__exportStar(require("./firestoreValues.js"), exports);
|
|
34
35
|
__exportStar(require("./moderation.js"), exports);
|
package/dist/cjs/verbContract.js
CHANGED
package/dist/cjs/verbSchemas.js
CHANGED
|
@@ -1004,17 +1004,13 @@ const list_contacts_result = {
|
|
|
1004
1004
|
const list_contact_requests_input = {
|
|
1005
1005
|
type: 'object',
|
|
1006
1006
|
additionalProperties: false,
|
|
1007
|
-
properties: {
|
|
1008
|
-
direction: { enum: ['inbound', 'outbound'] },
|
|
1009
|
-
includeResolved: { type: 'boolean' },
|
|
1010
|
-
limit: { type: 'integer', minimum: 1, maximum: 100 },
|
|
1011
|
-
},
|
|
1007
|
+
properties: {},
|
|
1012
1008
|
};
|
|
1013
1009
|
const list_contact_requests_result = {
|
|
1014
1010
|
type: 'object',
|
|
1015
|
-
description: '
|
|
1016
|
-
+ '
|
|
1017
|
-
+
|
|
1011
|
+
description: 'Pending inbound requests, newest first, capped at 100. Items are '
|
|
1012
|
+
+ 'SerializedContactRequest (backend-contracts contactRequest.ts). Read-only awareness: '
|
|
1013
|
+
+ "approval routes to the owner; agents cannot approve/reject.",
|
|
1018
1014
|
required: ['requests'],
|
|
1019
1015
|
additionalProperties: true,
|
|
1020
1016
|
properties: {
|
|
@@ -1022,26 +1018,15 @@ const list_contact_requests_result = {
|
|
|
1022
1018
|
type: 'array',
|
|
1023
1019
|
items: {
|
|
1024
1020
|
type: 'object',
|
|
1025
|
-
required: ['id', 'requesterId', 'targetId', 'status', 'kind'
|
|
1021
|
+
required: ['id', 'requesterId', 'targetId', 'status', 'kind'],
|
|
1026
1022
|
additionalProperties: true,
|
|
1027
1023
|
properties: {
|
|
1028
1024
|
id: { type: 'string' },
|
|
1029
1025
|
requesterId: { type: 'string' },
|
|
1030
1026
|
requesterName: { type: 'string' },
|
|
1031
1027
|
targetId: { type: 'string' },
|
|
1032
|
-
status: { enum: ['pending', 'approved', 'rejected', 'expired'
|
|
1028
|
+
status: { enum: ['pending', 'approved', 'rejected', 'expired'] },
|
|
1033
1029
|
kind: { enum: ['dm', 'group_invite'] },
|
|
1034
|
-
phase: {
|
|
1035
|
-
enum: [
|
|
1036
|
-
'awaiting_owner',
|
|
1037
|
-
'starting',
|
|
1038
|
-
'connected',
|
|
1039
|
-
'rejected',
|
|
1040
|
-
'expired',
|
|
1041
|
-
'cancelled',
|
|
1042
|
-
'failed',
|
|
1043
|
-
],
|
|
1044
|
-
},
|
|
1045
1030
|
message: { type: ['string', 'null'] },
|
|
1046
1031
|
createdAt: { type: ['string', 'null'] },
|
|
1047
1032
|
expiresAt: { type: ['string', 'null'] },
|
|
@@ -1050,23 +1035,6 @@ const list_contact_requests_result = {
|
|
|
1050
1035
|
},
|
|
1051
1036
|
},
|
|
1052
1037
|
};
|
|
1053
|
-
const cancel_contact_request_input = {
|
|
1054
|
-
type: 'object',
|
|
1055
|
-
required: ['requestId'],
|
|
1056
|
-
additionalProperties: false,
|
|
1057
|
-
properties: {
|
|
1058
|
-
requestId: { type: 'string', pattern: verbContract_js_1.VERB_ID_PATTERNS.runtimeId },
|
|
1059
|
-
},
|
|
1060
|
-
};
|
|
1061
|
-
const cancel_contact_request_result = {
|
|
1062
|
-
type: 'object',
|
|
1063
|
-
required: ['status', 'requestId'],
|
|
1064
|
-
additionalProperties: true,
|
|
1065
|
-
properties: {
|
|
1066
|
-
status: { const: 'cancelled' },
|
|
1067
|
-
requestId: { type: 'string' },
|
|
1068
|
-
},
|
|
1069
|
-
};
|
|
1070
1038
|
const list_conversations_input = {
|
|
1071
1039
|
type: 'object',
|
|
1072
1040
|
additionalProperties: false,
|
|
@@ -1206,8 +1174,6 @@ exports.CANON_VERBS_JSON_SCHEMA = {
|
|
|
1206
1174
|
list_contact_requests_result,
|
|
1207
1175
|
list_conversations_input,
|
|
1208
1176
|
list_conversations_result,
|
|
1209
|
-
cancel_contact_request_input,
|
|
1210
|
-
cancel_contact_request_result,
|
|
1211
1177
|
no_reply_input,
|
|
1212
1178
|
no_reply_result,
|
|
1213
1179
|
},
|
package/dist/cjs/verbWire.js
CHANGED
|
@@ -118,7 +118,6 @@ 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
|
-
cancel_contact_request: { required: ['requestId'], optional: [] },
|
|
122
121
|
no_reply: { required: [], optional: ['conversationId', 'messageId'] },
|
|
123
122
|
};
|
|
124
123
|
// ---------------------------------------------------------------------------
|
|
@@ -160,7 +159,6 @@ const wireTurnDef = {
|
|
|
160
159
|
additionalProperties: false,
|
|
161
160
|
properties: {
|
|
162
161
|
turnId: { type: ['string', 'null'] },
|
|
163
|
-
sourceMessageId: { type: 'string', minLength: 1 },
|
|
164
162
|
turnSemantics: { enum: ['progress', 'turn_complete', 'control'] },
|
|
165
163
|
deliveryIntent: { enum: ['queue', 'interrupt', 'interleave', 'stop'] },
|
|
166
164
|
replyBehavior: { enum: ['allow_auto_reply', 'suppress_auto_reply'] },
|
|
@@ -286,13 +284,7 @@ exports.CANON_VERB_WIRE_JSON_SCHEMA = {
|
|
|
286
284
|
// ---------------------------------------------------------------------------
|
|
287
285
|
// Intent <-> wire projection (json codec)
|
|
288
286
|
// ---------------------------------------------------------------------------
|
|
289
|
-
const TURN_KEYS = [
|
|
290
|
-
'turnId',
|
|
291
|
-
'sourceMessageId',
|
|
292
|
-
'turnSemantics',
|
|
293
|
-
'deliveryIntent',
|
|
294
|
-
'replyBehavior',
|
|
295
|
-
];
|
|
287
|
+
const TURN_KEYS = ['turnId', 'turnSemantics', 'deliveryIntent', 'replyBehavior'];
|
|
296
288
|
function splitTurnMetadata(metadata) {
|
|
297
289
|
if (!metadata || typeof metadata !== 'object')
|
|
298
290
|
return { turn: undefined, rest: undefined };
|
|
@@ -477,25 +469,14 @@ function projectVerbIntentToWire(verb, intent, options) {
|
|
|
477
469
|
envelope = compact({ conversationId: input.conversationId });
|
|
478
470
|
break;
|
|
479
471
|
}
|
|
480
|
-
case 'list_contacts':
|
|
481
|
-
break;
|
|
482
|
-
}
|
|
472
|
+
case 'list_contacts':
|
|
483
473
|
case 'list_contact_requests': {
|
|
484
|
-
value = compact({
|
|
485
|
-
direction: input.direction,
|
|
486
|
-
includeResolved: input.includeResolved,
|
|
487
|
-
limit: input.limit,
|
|
488
|
-
});
|
|
489
474
|
break;
|
|
490
475
|
}
|
|
491
476
|
case 'list_conversations': {
|
|
492
477
|
envelope = compact({ limit: input.limit });
|
|
493
478
|
break;
|
|
494
479
|
}
|
|
495
|
-
case 'cancel_contact_request': {
|
|
496
|
-
envelope = compact({ requestId: input.requestId });
|
|
497
|
-
break;
|
|
498
|
-
}
|
|
499
480
|
case 'no_reply': {
|
|
500
481
|
envelope = compact({ conversationId: input.conversationId, messageId: input.messageId });
|
|
501
482
|
value = compact({ reason: input.reason });
|
|
@@ -634,17 +615,10 @@ function mergeVerbWireToIntent(request) {
|
|
|
634
615
|
case 'leave_conversation':
|
|
635
616
|
return compact({ conversationId: envelope.conversationId });
|
|
636
617
|
case 'list_contacts':
|
|
637
|
-
return {};
|
|
638
618
|
case 'list_contact_requests':
|
|
639
|
-
return
|
|
640
|
-
direction: value.direction,
|
|
641
|
-
includeResolved: value.includeResolved,
|
|
642
|
-
limit: value.limit,
|
|
643
|
-
});
|
|
619
|
+
return {};
|
|
644
620
|
case 'list_conversations':
|
|
645
621
|
return compact({ limit: envelope.limit });
|
|
646
|
-
case 'cancel_contact_request':
|
|
647
|
-
return compact({ requestId: envelope.requestId });
|
|
648
622
|
case 'no_reply':
|
|
649
623
|
return compact({ conversationId: envelope.conversationId, messageId: envelope.messageId, reason: value.reason });
|
|
650
624
|
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/** The deliberately small policy vocabulary for principal communication. */
|
|
2
|
+
export type CommunicationRule = 'open' | 'approval-required' | 'closed';
|
|
3
|
+
/** Missing or invalid policy never widens access. */
|
|
4
|
+
export declare function readCommunicationRule(value: unknown): CommunicationRule;
|
|
5
|
+
/** Canon conversation selection is principal-neutral; a conversation is a runtime session boundary. */
|
|
6
|
+
export type DirectConversationSelection = {
|
|
7
|
+
mode: 'latest_or_new';
|
|
8
|
+
} | {
|
|
9
|
+
mode: 'new';
|
|
10
|
+
} | {
|
|
11
|
+
mode: 'specific';
|
|
12
|
+
conversationId: string;
|
|
13
|
+
};
|
|
14
|
+
export type CommunicateInput = {
|
|
15
|
+
action: 'message_existing';
|
|
16
|
+
conversationId: string;
|
|
17
|
+
text: string;
|
|
18
|
+
messageId?: string;
|
|
19
|
+
} | {
|
|
20
|
+
action: 'start_direct';
|
|
21
|
+
principalId: string;
|
|
22
|
+
text: string;
|
|
23
|
+
selection?: DirectConversationSelection;
|
|
24
|
+
messageId?: string;
|
|
25
|
+
} | {
|
|
26
|
+
action: 'create_group';
|
|
27
|
+
name: string;
|
|
28
|
+
memberIds: string[];
|
|
29
|
+
} | {
|
|
30
|
+
action: 'forward_message';
|
|
31
|
+
sourceConversationId: string;
|
|
32
|
+
targetConversationId: string;
|
|
33
|
+
messageId: string;
|
|
34
|
+
text?: string;
|
|
35
|
+
};
|
|
36
|
+
export type CommunicateResult = {
|
|
37
|
+
status: 'messaged';
|
|
38
|
+
conversationId: string;
|
|
39
|
+
messageId: string;
|
|
40
|
+
created?: boolean;
|
|
41
|
+
reused?: boolean;
|
|
42
|
+
} | {
|
|
43
|
+
status: 'requested' | 'pending';
|
|
44
|
+
requestId: string;
|
|
45
|
+
} | {
|
|
46
|
+
status: 'created';
|
|
47
|
+
conversationId: string;
|
|
48
|
+
added: string[];
|
|
49
|
+
pending: Array<{
|
|
50
|
+
userId: string;
|
|
51
|
+
requestId: string;
|
|
52
|
+
requirements: {
|
|
53
|
+
policyApproval: boolean;
|
|
54
|
+
ownerSessionSetup: boolean;
|
|
55
|
+
};
|
|
56
|
+
}>;
|
|
57
|
+
skipped: Array<{
|
|
58
|
+
userId: string;
|
|
59
|
+
reason: string;
|
|
60
|
+
}>;
|
|
61
|
+
} | {
|
|
62
|
+
status: 'forwarded';
|
|
63
|
+
messageId: string;
|
|
64
|
+
targetConversationId: string;
|
|
65
|
+
} | {
|
|
66
|
+
status: 'blocked' | 'unavailable';
|
|
67
|
+
reason: string;
|
|
68
|
+
};
|
|
69
|
+
/** Principal-neutral app/SDK command. Humans may open a composer before sending. */
|
|
70
|
+
export interface StartDirectInput {
|
|
71
|
+
targetUserId: string;
|
|
72
|
+
opener?: {
|
|
73
|
+
text: string;
|
|
74
|
+
messageId?: string;
|
|
75
|
+
} | null;
|
|
76
|
+
selection?: DirectConversationSelection;
|
|
77
|
+
}
|
|
78
|
+
export type StartDirectResult = {
|
|
79
|
+
status: 'opened' | 'messaged';
|
|
80
|
+
conversationId: string;
|
|
81
|
+
messageId?: string;
|
|
82
|
+
created?: boolean;
|
|
83
|
+
reused?: boolean;
|
|
84
|
+
} | {
|
|
85
|
+
status: 'requested' | 'pending';
|
|
86
|
+
requestId: string;
|
|
87
|
+
} | {
|
|
88
|
+
status: 'blocked' | 'unavailable';
|
|
89
|
+
reason: string;
|
|
90
|
+
};
|
|
91
|
+
export declare function parseDirectConversationSelection(value: unknown): DirectConversationSelection;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/** Missing or invalid policy never widens access. */
|
|
2
|
+
export function readCommunicationRule(value) {
|
|
3
|
+
if (value === 'open' || value === 'approval-required' || value === 'closed') {
|
|
4
|
+
return value;
|
|
5
|
+
}
|
|
6
|
+
// Existing owner-only records have the same outsider-facing meaning as
|
|
7
|
+
// closed; the agent owner bypass remains a separate server-side rule.
|
|
8
|
+
if (value === 'owner-only')
|
|
9
|
+
return 'closed';
|
|
10
|
+
return 'approval-required';
|
|
11
|
+
}
|
|
12
|
+
export function parseDirectConversationSelection(value) {
|
|
13
|
+
if (value == null)
|
|
14
|
+
return { mode: 'latest_or_new' };
|
|
15
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
16
|
+
throw new Error('selection must be an object');
|
|
17
|
+
}
|
|
18
|
+
const record = value;
|
|
19
|
+
if (record.mode === 'latest_or_new' || record.mode === 'new') {
|
|
20
|
+
return { mode: record.mode };
|
|
21
|
+
}
|
|
22
|
+
if (record.mode === 'specific') {
|
|
23
|
+
const conversationId = typeof record.conversationId === 'string'
|
|
24
|
+
? record.conversationId.trim()
|
|
25
|
+
: '';
|
|
26
|
+
if (!conversationId)
|
|
27
|
+
throw new Error('selection.conversationId is required');
|
|
28
|
+
return { mode: 'specific', conversationId };
|
|
29
|
+
}
|
|
30
|
+
throw new Error('selection.mode is invalid');
|
|
31
|
+
}
|
package/dist/contactRequest.d.ts
CHANGED
|
@@ -1,18 +1,12 @@
|
|
|
1
|
-
export type SerializedContactRequestStatus = 'pending' | 'approved' | 'rejected' | 'expired'
|
|
2
|
-
export type ContactRequestLifecyclePhase = 'awaiting_owner' | 'starting' | 'connected' | 'rejected' | 'expired' | 'cancelled' | 'failed';
|
|
1
|
+
export type SerializedContactRequestStatus = 'pending' | 'approved' | 'rejected' | 'expired';
|
|
3
2
|
/**
|
|
4
|
-
* Independent gates that must be satisfied before a pending
|
|
5
|
-
*
|
|
6
|
-
* vocabulary even though their activation pipelines remain separate.
|
|
3
|
+
* Independent gates that must be satisfied before a pending group invite can
|
|
4
|
+
* add its target.
|
|
7
5
|
*/
|
|
8
|
-
export interface
|
|
6
|
+
export interface GroupInviteRequirements {
|
|
9
7
|
policyApproval: boolean;
|
|
10
8
|
ownerSessionSetup: boolean;
|
|
11
9
|
}
|
|
12
|
-
/** Backward-compatible name retained for existing group invite clients. */
|
|
13
|
-
export type GroupInviteRequirements = ContactRequestRequirements;
|
|
14
|
-
export declare function readContactRequestRequirements(value: unknown): ContactRequestRequirements;
|
|
15
|
-
/** Backward-compatible parser retained for existing group invite clients. */
|
|
16
10
|
export declare function readGroupInviteRequirements(value: unknown): GroupInviteRequirements;
|
|
17
11
|
export interface SerializedContactRequest {
|
|
18
12
|
id: string;
|
|
@@ -33,18 +27,10 @@ export interface SerializedContactRequest {
|
|
|
33
27
|
conversationId: string;
|
|
34
28
|
groupName: string | null;
|
|
35
29
|
};
|
|
36
|
-
/** Present for
|
|
37
|
-
requirements?:
|
|
38
|
-
phase: ContactRequestLifecyclePhase;
|
|
39
|
-
/** Requester-only source lane for an opener-backed introduction. */
|
|
40
|
-
sourceConversationId?: string;
|
|
41
|
-
/** Requester-only destination, present after an opener-backed DM connects. */
|
|
42
|
-
conversationId?: string;
|
|
30
|
+
/** Present for group_invite payloads; omitted for DM requests. */
|
|
31
|
+
requirements?: GroupInviteRequirements;
|
|
43
32
|
createdAt: string | null;
|
|
44
33
|
resolvedAt?: string | null;
|
|
45
34
|
expiresAt?: string | null;
|
|
46
35
|
}
|
|
47
|
-
export declare function
|
|
48
|
-
export declare function serializeContactRequest(requestId: string, data: Record<string, unknown>, options?: {
|
|
49
|
-
audience?: 'inbound' | 'requester';
|
|
50
|
-
}): SerializedContactRequest | null;
|
|
36
|
+
export declare function serializeContactRequest(requestId: string, data: Record<string, unknown>): SerializedContactRequest | null;
|
package/dist/contactRequest.js
CHANGED
|
@@ -1,23 +1,14 @@
|
|
|
1
|
-
export function
|
|
1
|
+
export function readGroupInviteRequirements(value) {
|
|
2
2
|
const record = isRecord(value) ? value : null;
|
|
3
3
|
if (typeof record?.policyApproval !== 'boolean'
|
|
4
4
|
|| typeof record.ownerSessionSetup !== 'boolean') {
|
|
5
|
-
throw new TypeError('Invalid
|
|
5
|
+
throw new TypeError('Invalid group invite requirements');
|
|
6
6
|
}
|
|
7
7
|
return {
|
|
8
8
|
policyApproval: record.policyApproval,
|
|
9
9
|
ownerSessionSetup: record.ownerSessionSetup,
|
|
10
10
|
};
|
|
11
11
|
}
|
|
12
|
-
/** Backward-compatible parser retained for existing group invite clients. */
|
|
13
|
-
export function readGroupInviteRequirements(value) {
|
|
14
|
-
try {
|
|
15
|
-
return readContactRequestRequirements(value);
|
|
16
|
-
}
|
|
17
|
-
catch {
|
|
18
|
-
throw new TypeError('Invalid group invite requirements');
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
12
|
function isRecord(value) {
|
|
22
13
|
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
23
14
|
}
|
|
@@ -35,36 +26,12 @@ function normalizeStatus(value) {
|
|
|
35
26
|
if (value === 'pending'
|
|
36
27
|
|| value === 'approved'
|
|
37
28
|
|| value === 'rejected'
|
|
38
|
-
|| value === 'expired'
|
|
39
|
-
|| value === 'cancelled') {
|
|
29
|
+
|| value === 'expired') {
|
|
40
30
|
return value;
|
|
41
31
|
}
|
|
42
32
|
return 'pending';
|
|
43
33
|
}
|
|
44
|
-
export function
|
|
45
|
-
const status = data.status;
|
|
46
|
-
const openingStatus = data.openingMessageStatus;
|
|
47
|
-
let phase;
|
|
48
|
-
if (status === 'cancelled')
|
|
49
|
-
phase = 'cancelled';
|
|
50
|
-
else if (status === 'rejected')
|
|
51
|
-
phase = 'rejected';
|
|
52
|
-
else if (status === 'expired')
|
|
53
|
-
phase = 'expired';
|
|
54
|
-
else if (status === 'delivery_failed' || openingStatus === 'failed')
|
|
55
|
-
phase = 'failed';
|
|
56
|
-
else if (status === 'approved')
|
|
57
|
-
phase = 'connected';
|
|
58
|
-
else if (status === 'delivery_pending'
|
|
59
|
-
|| openingStatus === 'ready'
|
|
60
|
-
|| openingStatus === 'delivering'
|
|
61
|
-
|| openingStatus === 'retryable')
|
|
62
|
-
phase = 'starting';
|
|
63
|
-
else
|
|
64
|
-
phase = 'awaiting_owner';
|
|
65
|
-
return phase;
|
|
66
|
-
}
|
|
67
|
-
export function serializeContactRequest(requestId, data, options = {}) {
|
|
34
|
+
export function serializeContactRequest(requestId, data) {
|
|
68
35
|
if (typeof data.approverId !== 'string' || data.approverId.length === 0) {
|
|
69
36
|
return null;
|
|
70
37
|
}
|
|
@@ -121,34 +88,20 @@ export function serializeContactRequest(requestId, data, options = {}) {
|
|
|
121
88
|
message: null,
|
|
122
89
|
status: normalizeStatus(data.status),
|
|
123
90
|
kind: kindValue,
|
|
124
|
-
phase: deriveContactRequestPhase(data),
|
|
125
91
|
createdAt: normalizeTimestamp(data.createdAt),
|
|
126
92
|
resolvedAt: normalizeTimestamp(data.resolvedAt),
|
|
127
93
|
expiresAt: normalizeTimestamp(data.expiresAt),
|
|
128
94
|
};
|
|
129
|
-
if (groupContext
|
|
95
|
+
if (groupContext) {
|
|
130
96
|
let requirements;
|
|
131
97
|
try {
|
|
132
|
-
requirements = data.requirements
|
|
133
|
-
? { policyApproval: true, ownerSessionSetup: false }
|
|
134
|
-
: readContactRequestRequirements(data.requirements);
|
|
98
|
+
requirements = readGroupInviteRequirements(data.requirements);
|
|
135
99
|
}
|
|
136
100
|
catch {
|
|
137
101
|
return null;
|
|
138
102
|
}
|
|
139
|
-
payload.requirements = requirements;
|
|
140
|
-
}
|
|
141
|
-
if (groupContext)
|
|
142
103
|
payload.groupContext = groupContext;
|
|
143
|
-
|
|
144
|
-
if (typeof data.sourceConversationId === 'string' && data.sourceConversationId.length > 0) {
|
|
145
|
-
payload.sourceConversationId = data.sourceConversationId;
|
|
146
|
-
}
|
|
147
|
-
if (payload.phase === 'connected'
|
|
148
|
-
&& typeof data.openingMessageConversationId === 'string'
|
|
149
|
-
&& data.openingMessageConversationId.length > 0) {
|
|
150
|
-
payload.conversationId = data.openingMessageConversationId;
|
|
151
|
-
}
|
|
104
|
+
payload.requirements = requirements;
|
|
152
105
|
}
|
|
153
106
|
if (data.targetUserType === 'ai_agent') {
|
|
154
107
|
payload.targetOwnerId = data.targetOwnerId;
|
package/dist/index.d.ts
CHANGED
|
@@ -13,6 +13,7 @@ export * from './verbContract.js';
|
|
|
13
13
|
export * from './verbSchemas.js';
|
|
14
14
|
export * from './verbWire.js';
|
|
15
15
|
export * from './accessPolicy.js';
|
|
16
|
+
export * from './communication.js';
|
|
16
17
|
export * from './apiErrors.js';
|
|
17
18
|
export * from './firestoreValues.js';
|
|
18
19
|
export * from './moderation.js';
|
package/dist/index.js
CHANGED
|
@@ -13,6 +13,7 @@ export * from './verbContract.js';
|
|
|
13
13
|
export * from './verbSchemas.js';
|
|
14
14
|
export * from './verbWire.js';
|
|
15
15
|
export * from './accessPolicy.js';
|
|
16
|
+
export * from './communication.js';
|
|
16
17
|
export * from './apiErrors.js';
|
|
17
18
|
export * from './firestoreValues.js';
|
|
18
19
|
export * from './moderation.js';
|
package/dist/verbContract.d.ts
CHANGED
|
@@ -42,7 +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 {
|
|
45
|
+
import type { GroupInviteRequirements } from './contactRequest.js';
|
|
46
46
|
/** Identifier for this contract document. */
|
|
47
47
|
export declare const CANON_VERBS_SCHEMA_VERSION = "canon.verbs.v1";
|
|
48
48
|
/** $id of the emitted JSON Schema bundle. */
|
|
@@ -184,7 +184,7 @@ export declare const VERB_RATE_LIMITS: {
|
|
|
184
184
|
/** The one self-context type the platform accepts today. */
|
|
185
185
|
export declare const SELF_CONTEXT_TYPE = "cross_session";
|
|
186
186
|
/** Canonical verb names. */
|
|
187
|
-
export declare const CANON_VERB_NAMES: readonly ["send_to", "request_input", "request_approval", "check_approval", "send_card", "request_card", "share_contact", "react", "forward", "create_group", "add_member", "remove_member", "leave_conversation", "list_contacts", "list_contact_requests", "list_conversations", "
|
|
187
|
+
export declare const CANON_VERB_NAMES: readonly ["send_to", "request_input", "request_approval", "check_approval", "send_card", "request_card", "share_contact", "react", "forward", "create_group", "add_member", "remove_member", "leave_conversation", "list_contacts", "list_contact_requests", "list_conversations", "no_reply"];
|
|
188
188
|
export type CanonVerbName = (typeof CANON_VERB_NAMES)[number];
|
|
189
189
|
/** Private note-to-self attached to a cross-conversation send. */
|
|
190
190
|
export interface VerbSelfContext {
|
|
@@ -268,7 +268,6 @@ export interface VerbMediaAttachment {
|
|
|
268
268
|
*/
|
|
269
269
|
export interface VerbTurnMetadata {
|
|
270
270
|
turnId?: string | null;
|
|
271
|
-
sourceMessageId?: string;
|
|
272
271
|
turnSemantics?: 'progress' | 'turn_complete' | 'control';
|
|
273
272
|
deliveryIntent?: 'queue' | 'interrupt' | 'interleave' | 'stop';
|
|
274
273
|
replyBehavior?: 'allow_auto_reply' | 'suppress_auto_reply';
|
|
@@ -323,7 +322,7 @@ export type SendToResult = {
|
|
|
323
322
|
selfContextId?: string;
|
|
324
323
|
created?: boolean;
|
|
325
324
|
reused?: boolean;
|
|
326
|
-
sessionSelection?:
|
|
325
|
+
sessionSelection?: string;
|
|
327
326
|
} | {
|
|
328
327
|
status: 'requested';
|
|
329
328
|
requestId: string | null;
|
|
@@ -614,7 +613,7 @@ export interface CreateGroupInput {
|
|
|
614
613
|
export interface PendingGroupInviteResult {
|
|
615
614
|
userId: string;
|
|
616
615
|
requestId: string;
|
|
617
|
-
requirements:
|
|
616
|
+
requirements: GroupInviteRequirements;
|
|
618
617
|
}
|
|
619
618
|
export interface CreateGroupResult {
|
|
620
619
|
status: 'created';
|
|
@@ -654,7 +653,7 @@ export type AddMemberResult = {
|
|
|
654
653
|
} | {
|
|
655
654
|
status: 'pending';
|
|
656
655
|
requestId: string;
|
|
657
|
-
requirements:
|
|
656
|
+
requirements: GroupInviteRequirements;
|
|
658
657
|
};
|
|
659
658
|
export interface RemoveMemberInput {
|
|
660
659
|
conversationId: string;
|
|
@@ -681,26 +680,11 @@ export interface VerbContact {
|
|
|
681
680
|
export interface ListContactsResult {
|
|
682
681
|
contacts: VerbContact[];
|
|
683
682
|
}
|
|
684
|
-
export type
|
|
685
|
-
|
|
686
|
-
/** Defaults to inbound. Outbound is the reconnect/recovery surface. */
|
|
687
|
-
direction?: ContactRequestListDirection;
|
|
688
|
-
/** Include terminal and starting lifecycle states; default false. */
|
|
689
|
-
includeResolved?: boolean;
|
|
690
|
-
/** Newest-first result cap; default and maximum 100. */
|
|
691
|
-
limit?: number;
|
|
692
|
-
}
|
|
693
|
-
/** Items are SerializedContactRequest (contactRequest.ts), newest first, capped at 100. */
|
|
683
|
+
export type ListContactRequestsInput = Record<string, never>;
|
|
684
|
+
/** Items are SerializedContactRequest (contactRequest.ts) — pending inbound only, newest first, capped at 100. */
|
|
694
685
|
export interface ListContactRequestsResult {
|
|
695
686
|
requests: unknown[];
|
|
696
687
|
}
|
|
697
|
-
export interface CancelContactRequestInput {
|
|
698
|
-
requestId: string;
|
|
699
|
-
}
|
|
700
|
-
export interface CancelContactRequestResult {
|
|
701
|
-
status: 'cancelled';
|
|
702
|
-
requestId: string;
|
|
703
|
-
}
|
|
704
688
|
export interface ListConversationsInput {
|
|
705
689
|
/** Optional client-side cap; the REST endpoint returns all memberships. */
|
|
706
690
|
limit?: number;
|
package/dist/verbContract.js
CHANGED
package/dist/verbSchemas.d.ts
CHANGED
|
@@ -1615,19 +1615,7 @@ export declare const CANON_VERBS_JSON_SCHEMA: {
|
|
|
1615
1615
|
readonly list_contact_requests_input: {
|
|
1616
1616
|
readonly type: "object";
|
|
1617
1617
|
readonly additionalProperties: false;
|
|
1618
|
-
readonly properties: {
|
|
1619
|
-
readonly direction: {
|
|
1620
|
-
readonly enum: readonly ["inbound", "outbound"];
|
|
1621
|
-
};
|
|
1622
|
-
readonly includeResolved: {
|
|
1623
|
-
readonly type: "boolean";
|
|
1624
|
-
};
|
|
1625
|
-
readonly limit: {
|
|
1626
|
-
readonly type: "integer";
|
|
1627
|
-
readonly minimum: 1;
|
|
1628
|
-
readonly maximum: 100;
|
|
1629
|
-
};
|
|
1630
|
-
};
|
|
1618
|
+
readonly properties: {};
|
|
1631
1619
|
};
|
|
1632
1620
|
readonly list_contact_requests_result: {
|
|
1633
1621
|
readonly type: "object";
|
|
@@ -1639,7 +1627,7 @@ export declare const CANON_VERBS_JSON_SCHEMA: {
|
|
|
1639
1627
|
readonly type: "array";
|
|
1640
1628
|
readonly items: {
|
|
1641
1629
|
readonly type: "object";
|
|
1642
|
-
readonly required: readonly ["id", "requesterId", "targetId", "status", "kind"
|
|
1630
|
+
readonly required: readonly ["id", "requesterId", "targetId", "status", "kind"];
|
|
1643
1631
|
readonly additionalProperties: true;
|
|
1644
1632
|
readonly properties: {
|
|
1645
1633
|
readonly id: {
|
|
@@ -1655,14 +1643,11 @@ export declare const CANON_VERBS_JSON_SCHEMA: {
|
|
|
1655
1643
|
readonly type: "string";
|
|
1656
1644
|
};
|
|
1657
1645
|
readonly status: {
|
|
1658
|
-
readonly enum: readonly ["pending", "approved", "rejected", "expired"
|
|
1646
|
+
readonly enum: readonly ["pending", "approved", "rejected", "expired"];
|
|
1659
1647
|
};
|
|
1660
1648
|
readonly kind: {
|
|
1661
1649
|
readonly enum: readonly ["dm", "group_invite"];
|
|
1662
1650
|
};
|
|
1663
|
-
readonly phase: {
|
|
1664
|
-
readonly enum: readonly ["awaiting_owner", "starting", "connected", "rejected", "expired", "cancelled", "failed"];
|
|
1665
|
-
};
|
|
1666
1651
|
readonly message: {
|
|
1667
1652
|
readonly type: readonly ["string", "null"];
|
|
1668
1653
|
};
|
|
@@ -1756,30 +1741,6 @@ export declare const CANON_VERBS_JSON_SCHEMA: {
|
|
|
1756
1741
|
};
|
|
1757
1742
|
};
|
|
1758
1743
|
};
|
|
1759
|
-
readonly cancel_contact_request_input: {
|
|
1760
|
-
readonly type: "object";
|
|
1761
|
-
readonly required: readonly ["requestId"];
|
|
1762
|
-
readonly additionalProperties: false;
|
|
1763
|
-
readonly properties: {
|
|
1764
|
-
readonly requestId: {
|
|
1765
|
-
readonly type: "string";
|
|
1766
|
-
readonly pattern: "^[A-Za-z0-9_.:-]{1,160}$";
|
|
1767
|
-
};
|
|
1768
|
-
};
|
|
1769
|
-
};
|
|
1770
|
-
readonly cancel_contact_request_result: {
|
|
1771
|
-
readonly type: "object";
|
|
1772
|
-
readonly required: readonly ["status", "requestId"];
|
|
1773
|
-
readonly additionalProperties: true;
|
|
1774
|
-
readonly properties: {
|
|
1775
|
-
readonly status: {
|
|
1776
|
-
readonly const: "cancelled";
|
|
1777
|
-
};
|
|
1778
|
-
readonly requestId: {
|
|
1779
|
-
readonly type: "string";
|
|
1780
|
-
};
|
|
1781
|
-
};
|
|
1782
|
-
};
|
|
1783
1744
|
readonly no_reply_input: {
|
|
1784
1745
|
readonly type: "object";
|
|
1785
1746
|
readonly description: string;
|
package/dist/verbSchemas.js
CHANGED
|
@@ -999,17 +999,13 @@ const list_contacts_result = {
|
|
|
999
999
|
const list_contact_requests_input = {
|
|
1000
1000
|
type: 'object',
|
|
1001
1001
|
additionalProperties: false,
|
|
1002
|
-
properties: {
|
|
1003
|
-
direction: { enum: ['inbound', 'outbound'] },
|
|
1004
|
-
includeResolved: { type: 'boolean' },
|
|
1005
|
-
limit: { type: 'integer', minimum: 1, maximum: 100 },
|
|
1006
|
-
},
|
|
1002
|
+
properties: {},
|
|
1007
1003
|
};
|
|
1008
1004
|
const list_contact_requests_result = {
|
|
1009
1005
|
type: 'object',
|
|
1010
|
-
description: '
|
|
1011
|
-
+ '
|
|
1012
|
-
+
|
|
1006
|
+
description: 'Pending inbound requests, newest first, capped at 100. Items are '
|
|
1007
|
+
+ 'SerializedContactRequest (backend-contracts contactRequest.ts). Read-only awareness: '
|
|
1008
|
+
+ "approval routes to the owner; agents cannot approve/reject.",
|
|
1013
1009
|
required: ['requests'],
|
|
1014
1010
|
additionalProperties: true,
|
|
1015
1011
|
properties: {
|
|
@@ -1017,26 +1013,15 @@ const list_contact_requests_result = {
|
|
|
1017
1013
|
type: 'array',
|
|
1018
1014
|
items: {
|
|
1019
1015
|
type: 'object',
|
|
1020
|
-
required: ['id', 'requesterId', 'targetId', 'status', 'kind'
|
|
1016
|
+
required: ['id', 'requesterId', 'targetId', 'status', 'kind'],
|
|
1021
1017
|
additionalProperties: true,
|
|
1022
1018
|
properties: {
|
|
1023
1019
|
id: { type: 'string' },
|
|
1024
1020
|
requesterId: { type: 'string' },
|
|
1025
1021
|
requesterName: { type: 'string' },
|
|
1026
1022
|
targetId: { type: 'string' },
|
|
1027
|
-
status: { enum: ['pending', 'approved', 'rejected', 'expired'
|
|
1023
|
+
status: { enum: ['pending', 'approved', 'rejected', 'expired'] },
|
|
1028
1024
|
kind: { enum: ['dm', 'group_invite'] },
|
|
1029
|
-
phase: {
|
|
1030
|
-
enum: [
|
|
1031
|
-
'awaiting_owner',
|
|
1032
|
-
'starting',
|
|
1033
|
-
'connected',
|
|
1034
|
-
'rejected',
|
|
1035
|
-
'expired',
|
|
1036
|
-
'cancelled',
|
|
1037
|
-
'failed',
|
|
1038
|
-
],
|
|
1039
|
-
},
|
|
1040
1025
|
message: { type: ['string', 'null'] },
|
|
1041
1026
|
createdAt: { type: ['string', 'null'] },
|
|
1042
1027
|
expiresAt: { type: ['string', 'null'] },
|
|
@@ -1045,23 +1030,6 @@ const list_contact_requests_result = {
|
|
|
1045
1030
|
},
|
|
1046
1031
|
},
|
|
1047
1032
|
};
|
|
1048
|
-
const cancel_contact_request_input = {
|
|
1049
|
-
type: 'object',
|
|
1050
|
-
required: ['requestId'],
|
|
1051
|
-
additionalProperties: false,
|
|
1052
|
-
properties: {
|
|
1053
|
-
requestId: { type: 'string', pattern: VERB_ID_PATTERNS.runtimeId },
|
|
1054
|
-
},
|
|
1055
|
-
};
|
|
1056
|
-
const cancel_contact_request_result = {
|
|
1057
|
-
type: 'object',
|
|
1058
|
-
required: ['status', 'requestId'],
|
|
1059
|
-
additionalProperties: true,
|
|
1060
|
-
properties: {
|
|
1061
|
-
status: { const: 'cancelled' },
|
|
1062
|
-
requestId: { type: 'string' },
|
|
1063
|
-
},
|
|
1064
|
-
};
|
|
1065
1033
|
const list_conversations_input = {
|
|
1066
1034
|
type: 'object',
|
|
1067
1035
|
additionalProperties: false,
|
|
@@ -1201,8 +1169,6 @@ export const CANON_VERBS_JSON_SCHEMA = {
|
|
|
1201
1169
|
list_contact_requests_result,
|
|
1202
1170
|
list_conversations_input,
|
|
1203
1171
|
list_conversations_result,
|
|
1204
|
-
cancel_contact_request_input,
|
|
1205
|
-
cancel_contact_request_result,
|
|
1206
1172
|
no_reply_input,
|
|
1207
1173
|
no_reply_result,
|
|
1208
1174
|
},
|
package/dist/verbWire.d.ts
CHANGED
|
@@ -40,8 +40,6 @@ export type VerbWireBody = {
|
|
|
40
40
|
/** Turn-protocol keys the server enforces on (subset of TurnMetadata). */
|
|
41
41
|
export interface VerbWireTurn {
|
|
42
42
|
turnId?: string | null;
|
|
43
|
-
/** Trusted message that initiated a host-bound owner turn. */
|
|
44
|
-
sourceMessageId?: string;
|
|
45
43
|
turnSemantics?: 'progress' | 'turn_complete' | 'control';
|
|
46
44
|
deliveryIntent?: 'queue' | 'interrupt' | 'interleave' | 'stop';
|
|
47
45
|
replyBehavior?: 'allow_auto_reply' | 'suppress_auto_reply';
|
|
@@ -139,7 +137,7 @@ export declare const CANON_VERB_WIRE_JSON_SCHEMA: {
|
|
|
139
137
|
readonly const: "canon.verb-wire.v1";
|
|
140
138
|
};
|
|
141
139
|
readonly verb: {
|
|
142
|
-
readonly enum: readonly ["send_to", "request_input", "request_approval", "check_approval", "send_card", "request_card", "share_contact", "react", "forward", "create_group", "add_member", "remove_member", "leave_conversation", "list_contacts", "list_contact_requests", "list_conversations", "
|
|
140
|
+
readonly enum: readonly ["send_to", "request_input", "request_approval", "check_approval", "send_card", "request_card", "share_contact", "react", "forward", "create_group", "add_member", "remove_member", "leave_conversation", "list_contacts", "list_contact_requests", "list_conversations", "no_reply"];
|
|
143
141
|
};
|
|
144
142
|
readonly envelope: {
|
|
145
143
|
readonly $ref: "#/$defs/envelope";
|
|
@@ -466,10 +464,6 @@ export declare const CANON_VERB_WIRE_JSON_SCHEMA: {
|
|
|
466
464
|
readonly turnId: {
|
|
467
465
|
readonly type: readonly ["string", "null"];
|
|
468
466
|
};
|
|
469
|
-
readonly sourceMessageId: {
|
|
470
|
-
readonly type: "string";
|
|
471
|
-
readonly minLength: 1;
|
|
472
|
-
};
|
|
473
467
|
readonly turnSemantics: {
|
|
474
468
|
readonly enum: readonly ["progress", "turn_complete", "control"];
|
|
475
469
|
};
|
|
@@ -515,7 +509,7 @@ export declare const CANON_VERB_WIRE_JSON_SCHEMA: {
|
|
|
515
509
|
readonly const: "canon.verb-wire.v1";
|
|
516
510
|
};
|
|
517
511
|
readonly verb: {
|
|
518
|
-
readonly enum: readonly ["send_to", "request_input", "request_approval", "check_approval", "send_card", "request_card", "share_contact", "react", "forward", "create_group", "add_member", "remove_member", "leave_conversation", "list_contacts", "list_contact_requests", "list_conversations", "
|
|
512
|
+
readonly enum: readonly ["send_to", "request_input", "request_approval", "check_approval", "send_card", "request_card", "share_contact", "react", "forward", "create_group", "add_member", "remove_member", "leave_conversation", "list_contacts", "list_contact_requests", "list_conversations", "no_reply"];
|
|
519
513
|
};
|
|
520
514
|
readonly result: {
|
|
521
515
|
readonly $ref: "#/$defs/body";
|
package/dist/verbWire.js
CHANGED
|
@@ -112,7 +112,6 @@ 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
|
-
cancel_contact_request: { required: ['requestId'], optional: [] },
|
|
116
115
|
no_reply: { required: [], optional: ['conversationId', 'messageId'] },
|
|
117
116
|
};
|
|
118
117
|
// ---------------------------------------------------------------------------
|
|
@@ -154,7 +153,6 @@ const wireTurnDef = {
|
|
|
154
153
|
additionalProperties: false,
|
|
155
154
|
properties: {
|
|
156
155
|
turnId: { type: ['string', 'null'] },
|
|
157
|
-
sourceMessageId: { type: 'string', minLength: 1 },
|
|
158
156
|
turnSemantics: { enum: ['progress', 'turn_complete', 'control'] },
|
|
159
157
|
deliveryIntent: { enum: ['queue', 'interrupt', 'interleave', 'stop'] },
|
|
160
158
|
replyBehavior: { enum: ['allow_auto_reply', 'suppress_auto_reply'] },
|
|
@@ -280,13 +278,7 @@ export const CANON_VERB_WIRE_JSON_SCHEMA = {
|
|
|
280
278
|
// ---------------------------------------------------------------------------
|
|
281
279
|
// Intent <-> wire projection (json codec)
|
|
282
280
|
// ---------------------------------------------------------------------------
|
|
283
|
-
const TURN_KEYS = [
|
|
284
|
-
'turnId',
|
|
285
|
-
'sourceMessageId',
|
|
286
|
-
'turnSemantics',
|
|
287
|
-
'deliveryIntent',
|
|
288
|
-
'replyBehavior',
|
|
289
|
-
];
|
|
281
|
+
const TURN_KEYS = ['turnId', 'turnSemantics', 'deliveryIntent', 'replyBehavior'];
|
|
290
282
|
function splitTurnMetadata(metadata) {
|
|
291
283
|
if (!metadata || typeof metadata !== 'object')
|
|
292
284
|
return { turn: undefined, rest: undefined };
|
|
@@ -471,25 +463,14 @@ export function projectVerbIntentToWire(verb, intent, options) {
|
|
|
471
463
|
envelope = compact({ conversationId: input.conversationId });
|
|
472
464
|
break;
|
|
473
465
|
}
|
|
474
|
-
case 'list_contacts':
|
|
475
|
-
break;
|
|
476
|
-
}
|
|
466
|
+
case 'list_contacts':
|
|
477
467
|
case 'list_contact_requests': {
|
|
478
|
-
value = compact({
|
|
479
|
-
direction: input.direction,
|
|
480
|
-
includeResolved: input.includeResolved,
|
|
481
|
-
limit: input.limit,
|
|
482
|
-
});
|
|
483
468
|
break;
|
|
484
469
|
}
|
|
485
470
|
case 'list_conversations': {
|
|
486
471
|
envelope = compact({ limit: input.limit });
|
|
487
472
|
break;
|
|
488
473
|
}
|
|
489
|
-
case 'cancel_contact_request': {
|
|
490
|
-
envelope = compact({ requestId: input.requestId });
|
|
491
|
-
break;
|
|
492
|
-
}
|
|
493
474
|
case 'no_reply': {
|
|
494
475
|
envelope = compact({ conversationId: input.conversationId, messageId: input.messageId });
|
|
495
476
|
value = compact({ reason: input.reason });
|
|
@@ -628,17 +609,10 @@ export function mergeVerbWireToIntent(request) {
|
|
|
628
609
|
case 'leave_conversation':
|
|
629
610
|
return compact({ conversationId: envelope.conversationId });
|
|
630
611
|
case 'list_contacts':
|
|
631
|
-
return {};
|
|
632
612
|
case 'list_contact_requests':
|
|
633
|
-
return
|
|
634
|
-
direction: value.direction,
|
|
635
|
-
includeResolved: value.includeResolved,
|
|
636
|
-
limit: value.limit,
|
|
637
|
-
});
|
|
613
|
+
return {};
|
|
638
614
|
case 'list_conversations':
|
|
639
615
|
return compact({ limit: envelope.limit });
|
|
640
|
-
case 'cancel_contact_request':
|
|
641
|
-
return compact({ requestId: envelope.requestId });
|
|
642
616
|
case 'no_reply':
|
|
643
617
|
return compact({ conversationId: envelope.conversationId, messageId: envelope.messageId, reason: value.reason });
|
|
644
618
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@canonmsg/backend-contracts",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "7.0.0",
|
|
4
4
|
"description": "Canon backend contract helpers shared by Functions and stream-service",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/cjs/index.js",
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
"access": "public"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
|
-
"@canonmsg/rich-cards": "^0.10.
|
|
46
|
+
"@canonmsg/rich-cards": "^0.10.3",
|
|
47
47
|
"@types/node": "^22.0.0",
|
|
48
48
|
"ajv": "^8.20.0",
|
|
49
49
|
"typescript": "~5.7.0",
|