@canonmsg/backend-contracts 6.4.0 → 8.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 +3 -3
- package/dist/canon-verb-wire.schema.json +0 -73
- package/dist/canon-verbs.limits.json +0 -5
- package/dist/canon-verbs.schema.json +7 -233
- package/dist/cjs/communication.js +31 -0
- package/dist/cjs/firestoreValues.js +1 -1
- package/dist/cjs/index.js +2 -2
- package/dist/cjs/replyAuthority.js +2 -0
- package/dist/cjs/verbContract.js +1 -17
- package/dist/cjs/verbSchemas.js +11 -149
- package/dist/cjs/verbWire.js +1 -36
- package/dist/communication.d.ts +109 -0
- package/dist/communication.js +27 -0
- package/dist/firestoreValues.d.ts +1 -1
- package/dist/firestoreValues.js +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/replyAuthority.d.ts +12 -0
- package/dist/replyAuthority.js +1 -0
- package/dist/verbContract.d.ts +5 -65
- package/dist/verbContract.js +0 -16
- package/dist/verbSchemas.d.ts +3 -210
- package/dist/verbSchemas.js +10 -148
- package/dist/verbWire.d.ts +4 -61
- package/dist/verbWire.js +2 -37
- package/package.json +2 -2
- package/dist/accessPolicy.d.ts +0 -9
- package/dist/accessPolicy.js +0 -12
- package/dist/cjs/accessPolicy.js +0 -15
- package/dist/cjs/contactRequest.js +0 -163
- package/dist/contactRequest.d.ts +0 -50
- package/dist/contactRequest.js +0 -157
|
@@ -1,163 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.readContactRequestRequirements = readContactRequestRequirements;
|
|
4
|
-
exports.readGroupInviteRequirements = readGroupInviteRequirements;
|
|
5
|
-
exports.deriveContactRequestPhase = deriveContactRequestPhase;
|
|
6
|
-
exports.serializeContactRequest = serializeContactRequest;
|
|
7
|
-
function readContactRequestRequirements(value) {
|
|
8
|
-
const record = isRecord(value) ? value : null;
|
|
9
|
-
if (typeof record?.policyApproval !== 'boolean'
|
|
10
|
-
|| typeof record.ownerSessionSetup !== 'boolean') {
|
|
11
|
-
throw new TypeError('Invalid contact request requirements');
|
|
12
|
-
}
|
|
13
|
-
return {
|
|
14
|
-
policyApproval: record.policyApproval,
|
|
15
|
-
ownerSessionSetup: record.ownerSessionSetup,
|
|
16
|
-
};
|
|
17
|
-
}
|
|
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
|
-
function isRecord(value) {
|
|
28
|
-
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
29
|
-
}
|
|
30
|
-
function normalizeTimestamp(value) {
|
|
31
|
-
if (value instanceof Date)
|
|
32
|
-
return value.toISOString();
|
|
33
|
-
if (isRecord(value) && typeof value.toDate === 'function') {
|
|
34
|
-
const result = value.toDate();
|
|
35
|
-
if (result instanceof Date)
|
|
36
|
-
return result.toISOString();
|
|
37
|
-
}
|
|
38
|
-
return null;
|
|
39
|
-
}
|
|
40
|
-
function normalizeStatus(value) {
|
|
41
|
-
if (value === 'pending'
|
|
42
|
-
|| value === 'approved'
|
|
43
|
-
|| value === 'rejected'
|
|
44
|
-
|| value === 'expired'
|
|
45
|
-
|| value === 'cancelled') {
|
|
46
|
-
return value;
|
|
47
|
-
}
|
|
48
|
-
return 'pending';
|
|
49
|
-
}
|
|
50
|
-
function deriveContactRequestPhase(data) {
|
|
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 = {}) {
|
|
74
|
-
if (typeof data.approverId !== 'string' || data.approverId.length === 0) {
|
|
75
|
-
return null;
|
|
76
|
-
}
|
|
77
|
-
if (typeof data.requesterId !== 'string' || data.requesterId.length === 0) {
|
|
78
|
-
return null;
|
|
79
|
-
}
|
|
80
|
-
if (typeof data.targetId !== 'string' || data.targetId.length === 0) {
|
|
81
|
-
return null;
|
|
82
|
-
}
|
|
83
|
-
if (typeof data.targetName !== 'string' || data.targetName.trim().length === 0) {
|
|
84
|
-
return null;
|
|
85
|
-
}
|
|
86
|
-
if (data.targetAvatarUrl !== null && data.targetAvatarUrl !== undefined && typeof data.targetAvatarUrl !== 'string') {
|
|
87
|
-
return null;
|
|
88
|
-
}
|
|
89
|
-
if (data.targetUserType !== 'human' && data.targetUserType !== 'ai_agent') {
|
|
90
|
-
return null;
|
|
91
|
-
}
|
|
92
|
-
if (data.targetUserType === 'ai_agent'
|
|
93
|
-
&& (typeof data.targetOwnerId !== 'string' || data.targetOwnerId.length === 0)) {
|
|
94
|
-
return null;
|
|
95
|
-
}
|
|
96
|
-
const kindValue = typeof data.kind === 'string' ? data.kind : null;
|
|
97
|
-
if (kindValue !== 'dm' && kindValue !== 'group_invite') {
|
|
98
|
-
return null;
|
|
99
|
-
}
|
|
100
|
-
let groupContext = null;
|
|
101
|
-
if (kindValue === 'group_invite') {
|
|
102
|
-
const ctx = isRecord(data.groupContext) ? data.groupContext : null;
|
|
103
|
-
const conversationId = typeof ctx?.conversationId === 'string'
|
|
104
|
-
? ctx.conversationId.trim()
|
|
105
|
-
: '';
|
|
106
|
-
if (!conversationId) {
|
|
107
|
-
return null;
|
|
108
|
-
}
|
|
109
|
-
groupContext = {
|
|
110
|
-
conversationId,
|
|
111
|
-
groupName: typeof ctx?.groupName === 'string' ? ctx.groupName : null,
|
|
112
|
-
};
|
|
113
|
-
}
|
|
114
|
-
const payload = {
|
|
115
|
-
id: requestId,
|
|
116
|
-
requesterId: data.requesterId,
|
|
117
|
-
requesterName: typeof data.requesterName === 'string' ? data.requesterName : 'Unknown',
|
|
118
|
-
requesterAvatarUrl: typeof data.requesterAvatarUrl === 'string' ? data.requesterAvatarUrl : null,
|
|
119
|
-
targetId: data.targetId,
|
|
120
|
-
targetName: data.targetName.trim(),
|
|
121
|
-
targetAvatarUrl: typeof data.targetAvatarUrl === 'string' ? data.targetAvatarUrl : null,
|
|
122
|
-
targetUserType: data.targetUserType,
|
|
123
|
-
approverId: data.approverId,
|
|
124
|
-
// Free-form request content is owner-review material. This shared DTO is
|
|
125
|
-
// delivered to the target agent over REST/SSE, so it must never cross the
|
|
126
|
-
// admission boundary before the human approver accepts the reach-out.
|
|
127
|
-
message: null,
|
|
128
|
-
status: normalizeStatus(data.status),
|
|
129
|
-
kind: kindValue,
|
|
130
|
-
phase: deriveContactRequestPhase(data),
|
|
131
|
-
createdAt: normalizeTimestamp(data.createdAt),
|
|
132
|
-
resolvedAt: normalizeTimestamp(data.resolvedAt),
|
|
133
|
-
expiresAt: normalizeTimestamp(data.expiresAt),
|
|
134
|
-
};
|
|
135
|
-
if (groupContext || kindValue === 'dm') {
|
|
136
|
-
let requirements;
|
|
137
|
-
try {
|
|
138
|
-
requirements = data.requirements === undefined && kindValue === 'dm'
|
|
139
|
-
? { policyApproval: true, ownerSessionSetup: false }
|
|
140
|
-
: readContactRequestRequirements(data.requirements);
|
|
141
|
-
}
|
|
142
|
-
catch {
|
|
143
|
-
return null;
|
|
144
|
-
}
|
|
145
|
-
payload.requirements = requirements;
|
|
146
|
-
}
|
|
147
|
-
if (groupContext)
|
|
148
|
-
payload.groupContext = groupContext;
|
|
149
|
-
if (options.audience === 'requester') {
|
|
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
|
-
}
|
|
158
|
-
}
|
|
159
|
-
if (data.targetUserType === 'ai_agent') {
|
|
160
|
-
payload.targetOwnerId = data.targetOwnerId;
|
|
161
|
-
}
|
|
162
|
-
return payload;
|
|
163
|
-
}
|
package/dist/contactRequest.d.ts
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
export type SerializedContactRequestStatus = 'pending' | 'approved' | 'rejected' | 'expired' | 'cancelled';
|
|
2
|
-
export type ContactRequestLifecyclePhase = 'awaiting_owner' | 'starting' | 'connected' | 'rejected' | 'expired' | 'cancelled' | 'failed';
|
|
3
|
-
/**
|
|
4
|
-
* Independent gates that must be satisfied before a pending contact request
|
|
5
|
-
* can activate. DM introductions and group invites share this owner-review
|
|
6
|
-
* vocabulary even though their activation pipelines remain separate.
|
|
7
|
-
*/
|
|
8
|
-
export interface ContactRequestRequirements {
|
|
9
|
-
policyApproval: boolean;
|
|
10
|
-
ownerSessionSetup: boolean;
|
|
11
|
-
}
|
|
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
|
-
export declare function readGroupInviteRequirements(value: unknown): GroupInviteRequirements;
|
|
17
|
-
export interface SerializedContactRequest {
|
|
18
|
-
id: string;
|
|
19
|
-
requesterId: string;
|
|
20
|
-
requesterName: string;
|
|
21
|
-
requesterAvatarUrl: string | null;
|
|
22
|
-
targetId: string;
|
|
23
|
-
targetName: string;
|
|
24
|
-
targetAvatarUrl: string | null;
|
|
25
|
-
targetUserType: 'human' | 'ai_agent';
|
|
26
|
-
targetOwnerId?: string | null;
|
|
27
|
-
approverId: string;
|
|
28
|
-
/** Always null on agent-facing DTOs; free-form content stays in authorized review storage. */
|
|
29
|
-
message: string | null;
|
|
30
|
-
status: SerializedContactRequestStatus;
|
|
31
|
-
kind: 'dm' | 'group_invite';
|
|
32
|
-
groupContext?: {
|
|
33
|
-
conversationId: string;
|
|
34
|
-
groupName: string | null;
|
|
35
|
-
};
|
|
36
|
-
/** Present for group invites and setup/policy-aware DM introductions. */
|
|
37
|
-
requirements?: ContactRequestRequirements;
|
|
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;
|
|
43
|
-
createdAt: string | null;
|
|
44
|
-
resolvedAt?: string | null;
|
|
45
|
-
expiresAt?: string | null;
|
|
46
|
-
}
|
|
47
|
-
export declare function deriveContactRequestPhase(data: Record<string, unknown>): ContactRequestLifecyclePhase;
|
|
48
|
-
export declare function serializeContactRequest(requestId: string, data: Record<string, unknown>, options?: {
|
|
49
|
-
audience?: 'inbound' | 'requester';
|
|
50
|
-
}): SerializedContactRequest | null;
|
package/dist/contactRequest.js
DELETED
|
@@ -1,157 +0,0 @@
|
|
|
1
|
-
export function readContactRequestRequirements(value) {
|
|
2
|
-
const record = isRecord(value) ? value : null;
|
|
3
|
-
if (typeof record?.policyApproval !== 'boolean'
|
|
4
|
-
|| typeof record.ownerSessionSetup !== 'boolean') {
|
|
5
|
-
throw new TypeError('Invalid contact request requirements');
|
|
6
|
-
}
|
|
7
|
-
return {
|
|
8
|
-
policyApproval: record.policyApproval,
|
|
9
|
-
ownerSessionSetup: record.ownerSessionSetup,
|
|
10
|
-
};
|
|
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
|
-
function isRecord(value) {
|
|
22
|
-
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
23
|
-
}
|
|
24
|
-
function normalizeTimestamp(value) {
|
|
25
|
-
if (value instanceof Date)
|
|
26
|
-
return value.toISOString();
|
|
27
|
-
if (isRecord(value) && typeof value.toDate === 'function') {
|
|
28
|
-
const result = value.toDate();
|
|
29
|
-
if (result instanceof Date)
|
|
30
|
-
return result.toISOString();
|
|
31
|
-
}
|
|
32
|
-
return null;
|
|
33
|
-
}
|
|
34
|
-
function normalizeStatus(value) {
|
|
35
|
-
if (value === 'pending'
|
|
36
|
-
|| value === 'approved'
|
|
37
|
-
|| value === 'rejected'
|
|
38
|
-
|| value === 'expired'
|
|
39
|
-
|| value === 'cancelled') {
|
|
40
|
-
return value;
|
|
41
|
-
}
|
|
42
|
-
return 'pending';
|
|
43
|
-
}
|
|
44
|
-
export function deriveContactRequestPhase(data) {
|
|
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 = {}) {
|
|
68
|
-
if (typeof data.approverId !== 'string' || data.approverId.length === 0) {
|
|
69
|
-
return null;
|
|
70
|
-
}
|
|
71
|
-
if (typeof data.requesterId !== 'string' || data.requesterId.length === 0) {
|
|
72
|
-
return null;
|
|
73
|
-
}
|
|
74
|
-
if (typeof data.targetId !== 'string' || data.targetId.length === 0) {
|
|
75
|
-
return null;
|
|
76
|
-
}
|
|
77
|
-
if (typeof data.targetName !== 'string' || data.targetName.trim().length === 0) {
|
|
78
|
-
return null;
|
|
79
|
-
}
|
|
80
|
-
if (data.targetAvatarUrl !== null && data.targetAvatarUrl !== undefined && typeof data.targetAvatarUrl !== 'string') {
|
|
81
|
-
return null;
|
|
82
|
-
}
|
|
83
|
-
if (data.targetUserType !== 'human' && data.targetUserType !== 'ai_agent') {
|
|
84
|
-
return null;
|
|
85
|
-
}
|
|
86
|
-
if (data.targetUserType === 'ai_agent'
|
|
87
|
-
&& (typeof data.targetOwnerId !== 'string' || data.targetOwnerId.length === 0)) {
|
|
88
|
-
return null;
|
|
89
|
-
}
|
|
90
|
-
const kindValue = typeof data.kind === 'string' ? data.kind : null;
|
|
91
|
-
if (kindValue !== 'dm' && kindValue !== 'group_invite') {
|
|
92
|
-
return null;
|
|
93
|
-
}
|
|
94
|
-
let groupContext = null;
|
|
95
|
-
if (kindValue === 'group_invite') {
|
|
96
|
-
const ctx = isRecord(data.groupContext) ? data.groupContext : null;
|
|
97
|
-
const conversationId = typeof ctx?.conversationId === 'string'
|
|
98
|
-
? ctx.conversationId.trim()
|
|
99
|
-
: '';
|
|
100
|
-
if (!conversationId) {
|
|
101
|
-
return null;
|
|
102
|
-
}
|
|
103
|
-
groupContext = {
|
|
104
|
-
conversationId,
|
|
105
|
-
groupName: typeof ctx?.groupName === 'string' ? ctx.groupName : null,
|
|
106
|
-
};
|
|
107
|
-
}
|
|
108
|
-
const payload = {
|
|
109
|
-
id: requestId,
|
|
110
|
-
requesterId: data.requesterId,
|
|
111
|
-
requesterName: typeof data.requesterName === 'string' ? data.requesterName : 'Unknown',
|
|
112
|
-
requesterAvatarUrl: typeof data.requesterAvatarUrl === 'string' ? data.requesterAvatarUrl : null,
|
|
113
|
-
targetId: data.targetId,
|
|
114
|
-
targetName: data.targetName.trim(),
|
|
115
|
-
targetAvatarUrl: typeof data.targetAvatarUrl === 'string' ? data.targetAvatarUrl : null,
|
|
116
|
-
targetUserType: data.targetUserType,
|
|
117
|
-
approverId: data.approverId,
|
|
118
|
-
// Free-form request content is owner-review material. This shared DTO is
|
|
119
|
-
// delivered to the target agent over REST/SSE, so it must never cross the
|
|
120
|
-
// admission boundary before the human approver accepts the reach-out.
|
|
121
|
-
message: null,
|
|
122
|
-
status: normalizeStatus(data.status),
|
|
123
|
-
kind: kindValue,
|
|
124
|
-
phase: deriveContactRequestPhase(data),
|
|
125
|
-
createdAt: normalizeTimestamp(data.createdAt),
|
|
126
|
-
resolvedAt: normalizeTimestamp(data.resolvedAt),
|
|
127
|
-
expiresAt: normalizeTimestamp(data.expiresAt),
|
|
128
|
-
};
|
|
129
|
-
if (groupContext || kindValue === 'dm') {
|
|
130
|
-
let requirements;
|
|
131
|
-
try {
|
|
132
|
-
requirements = data.requirements === undefined && kindValue === 'dm'
|
|
133
|
-
? { policyApproval: true, ownerSessionSetup: false }
|
|
134
|
-
: readContactRequestRequirements(data.requirements);
|
|
135
|
-
}
|
|
136
|
-
catch {
|
|
137
|
-
return null;
|
|
138
|
-
}
|
|
139
|
-
payload.requirements = requirements;
|
|
140
|
-
}
|
|
141
|
-
if (groupContext)
|
|
142
|
-
payload.groupContext = groupContext;
|
|
143
|
-
if (options.audience === 'requester') {
|
|
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
|
-
}
|
|
152
|
-
}
|
|
153
|
-
if (data.targetUserType === 'ai_agent') {
|
|
154
|
-
payload.targetOwnerId = data.targetOwnerId;
|
|
155
|
-
}
|
|
156
|
-
return payload;
|
|
157
|
-
}
|