@canonmsg/backend-contracts 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,59 @@
1
+ export type ParticipationStyle = 'natural' | 'collaborative' | 'mention-first' | 'approval-gated' | 'handoff-only' | 'observer';
2
+ export interface AgentBehaviorSettingsRecord {
3
+ participationStyle?: ParticipationStyle;
4
+ allowAgentToAgent?: boolean;
5
+ allowLongRunningCollaboration?: boolean;
6
+ requireMentionForGroupReplies?: boolean;
7
+ maxConsecutiveAgentTurns?: number | null;
8
+ instructions?: string | null;
9
+ }
10
+ export interface ResolvedAgentBehaviorPolicyRecord {
11
+ participation: {
12
+ style: ParticipationStyle;
13
+ allowAgentToAgent: boolean;
14
+ allowHumanToAgent: boolean;
15
+ allowLongRunningCollaboration: boolean;
16
+ requireMentionForGroupReplies: boolean;
17
+ maxConsecutiveAgentTurns?: number | null;
18
+ };
19
+ instructions: string[];
20
+ source: {
21
+ hasAgentDefault: boolean;
22
+ hasConversationOverride: boolean;
23
+ };
24
+ }
25
+ export interface ParticipationDecisionInput {
26
+ conversationType: 'direct' | 'group' | 'unknown';
27
+ senderType: 'human' | 'ai_agent';
28
+ isOwner: boolean;
29
+ mentionedAgent: boolean;
30
+ recentHumanCount?: number;
31
+ consecutiveAgentTurns?: number;
32
+ currentAgentStreakStartedByHuman?: boolean;
33
+ }
34
+ export interface ParticipationHistoryMessage {
35
+ senderId: string;
36
+ senderType: 'human' | 'ai_agent';
37
+ metadata?: unknown;
38
+ }
39
+ export interface ParticipationHistorySnapshot {
40
+ recentSenderTypes: Array<'human' | 'ai_agent'>;
41
+ recentHumanCount: number;
42
+ recentAgentCount: number;
43
+ consecutiveAgentTurns: number;
44
+ currentAgentStreakStartedByHuman: boolean;
45
+ }
46
+ export declare const PARTICIPATION_HISTORY_FETCH_LIMIT = 50;
47
+ export declare function parseAgentBehaviorSettings(raw: unknown): AgentBehaviorSettingsRecord;
48
+ export declare function normalizeStoredAgentBehaviorPolicy(raw: Record<string, unknown> | undefined): AgentBehaviorSettingsRecord | null;
49
+ export declare function normalizeAgentBehaviorInstructions(value: string | null | undefined): string | null;
50
+ export declare function resolveAgentBehaviorPolicy(params?: {
51
+ agentDefault?: AgentBehaviorSettingsRecord | null;
52
+ conversationOverride?: AgentBehaviorSettingsRecord | null;
53
+ }): ResolvedAgentBehaviorPolicyRecord;
54
+ export declare function buildParticipationHistorySnapshot(messages: ParticipationHistoryMessage[], agentId?: string): ParticipationHistorySnapshot;
55
+ export declare function appendParticipationHistoryMessage(snapshot: ParticipationHistorySnapshot, message: ParticipationHistoryMessage, limit?: number): ParticipationHistorySnapshot;
56
+ export declare function evaluateParticipationPolicy(policy: ResolvedAgentBehaviorPolicyRecord, input: ParticipationDecisionInput): {
57
+ allow: boolean;
58
+ reason: string;
59
+ };
@@ -0,0 +1,240 @@
1
+ import { resolveTurnMessageSemantics } from './turnProtocol.js';
2
+ export const PARTICIPATION_HISTORY_FETCH_LIMIT = 50;
3
+ const VALID_PARTICIPATION_STYLES = new Set([
4
+ 'natural',
5
+ 'collaborative',
6
+ 'mention-first',
7
+ 'approval-gated',
8
+ 'handoff-only',
9
+ 'observer',
10
+ ]);
11
+ const DEFAULT_POLICY = {
12
+ participation: {
13
+ style: 'natural',
14
+ allowAgentToAgent: true,
15
+ allowHumanToAgent: true,
16
+ allowLongRunningCollaboration: true,
17
+ requireMentionForGroupReplies: false,
18
+ maxConsecutiveAgentTurns: null,
19
+ },
20
+ instructions: [],
21
+ source: {
22
+ hasAgentDefault: false,
23
+ hasConversationOverride: false,
24
+ },
25
+ };
26
+ function normalizeString(value) {
27
+ if (typeof value !== 'string')
28
+ return undefined;
29
+ const trimmed = value.trim();
30
+ return trimmed.length > 0 ? trimmed : undefined;
31
+ }
32
+ function normalizeOptionalBoolean(value, fieldName) {
33
+ if (value === undefined)
34
+ return undefined;
35
+ if (typeof value !== 'boolean') {
36
+ throw new Error(`${fieldName} must be a boolean`);
37
+ }
38
+ return value;
39
+ }
40
+ function normalizeMaxConsecutiveAgentTurns(value) {
41
+ if (value === undefined)
42
+ return undefined;
43
+ if (value === null || value === '')
44
+ return null;
45
+ if (!Number.isInteger(value) || Number(value) < 1 || Number(value) > 20) {
46
+ throw new Error('maxConsecutiveAgentTurns must be an integer between 1 and 20, or null');
47
+ }
48
+ return Number(value);
49
+ }
50
+ function resolveGroupMentionRequirement(input) {
51
+ if (input.requireMentionForGroupReplies !== undefined
52
+ && typeof input.requireMentionForGroupReplies !== 'boolean') {
53
+ throw new Error('requireMentionForGroupReplies must be a boolean');
54
+ }
55
+ if (input.requireMentionForGroupAgentReplies !== undefined
56
+ && typeof input.requireMentionForGroupAgentReplies !== 'boolean') {
57
+ throw new Error('requireMentionForGroupAgentReplies must be a boolean');
58
+ }
59
+ if (typeof input.requireMentionForGroupReplies === 'boolean') {
60
+ return input.requireMentionForGroupReplies;
61
+ }
62
+ if (typeof input.requireMentionForGroupAgentReplies === 'boolean') {
63
+ return input.requireMentionForGroupAgentReplies;
64
+ }
65
+ return undefined;
66
+ }
67
+ export function parseAgentBehaviorSettings(raw) {
68
+ const input = raw && typeof raw === 'object' ? raw : {};
69
+ const participationStyle = normalizeString(input.participationStyle);
70
+ if (participationStyle && !VALID_PARTICIPATION_STYLES.has(participationStyle)) {
71
+ throw new Error('Invalid participationStyle');
72
+ }
73
+ const instructions = input.instructions === undefined
74
+ ? undefined
75
+ : input.instructions === null
76
+ ? null
77
+ : normalizeString(input.instructions) ?? null;
78
+ if (typeof instructions === 'string' && instructions.length > 2_000) {
79
+ throw new Error('instructions must be at most 2000 characters');
80
+ }
81
+ const allowAgentToAgent = normalizeOptionalBoolean(input.allowAgentToAgent, 'allowAgentToAgent');
82
+ const allowLongRunningCollaboration = normalizeOptionalBoolean(input.allowLongRunningCollaboration, 'allowLongRunningCollaboration');
83
+ const requireMentionForGroupReplies = resolveGroupMentionRequirement(input);
84
+ const maxConsecutiveAgentTurns = normalizeMaxConsecutiveAgentTurns(input.maxConsecutiveAgentTurns);
85
+ return {
86
+ ...(participationStyle ? { participationStyle: participationStyle } : {}),
87
+ ...(allowAgentToAgent !== undefined ? { allowAgentToAgent } : {}),
88
+ ...(allowLongRunningCollaboration !== undefined ? { allowLongRunningCollaboration } : {}),
89
+ ...(requireMentionForGroupReplies !== undefined ? { requireMentionForGroupReplies } : {}),
90
+ ...(maxConsecutiveAgentTurns !== undefined ? { maxConsecutiveAgentTurns } : {}),
91
+ ...(instructions !== undefined ? { instructions } : {}),
92
+ };
93
+ }
94
+ export function normalizeStoredAgentBehaviorPolicy(raw) {
95
+ if (!raw)
96
+ return null;
97
+ return {
98
+ ...(typeof raw.participationStyle === 'string' && VALID_PARTICIPATION_STYLES.has(raw.participationStyle)
99
+ ? { participationStyle: raw.participationStyle }
100
+ : {}),
101
+ ...(typeof raw.allowAgentToAgent === 'boolean' ? { allowAgentToAgent: raw.allowAgentToAgent } : {}),
102
+ ...(typeof raw.allowLongRunningCollaboration === 'boolean'
103
+ ? { allowLongRunningCollaboration: raw.allowLongRunningCollaboration }
104
+ : {}),
105
+ ...(typeof raw.requireMentionForGroupReplies === 'boolean'
106
+ ? { requireMentionForGroupReplies: raw.requireMentionForGroupReplies }
107
+ : typeof raw.requireMentionForGroupAgentReplies === 'boolean'
108
+ ? { requireMentionForGroupReplies: raw.requireMentionForGroupAgentReplies }
109
+ : {}),
110
+ ...(typeof raw.maxConsecutiveAgentTurns === 'number' || raw.maxConsecutiveAgentTurns === null
111
+ ? { maxConsecutiveAgentTurns: raw.maxConsecutiveAgentTurns }
112
+ : {}),
113
+ ...(typeof raw.instructions === 'string' || raw.instructions === null
114
+ ? { instructions: raw.instructions }
115
+ : {}),
116
+ };
117
+ }
118
+ function coalesceBoolean(overrideValue, fallbackValue, defaultValue) {
119
+ if (typeof overrideValue === 'boolean')
120
+ return overrideValue;
121
+ if (typeof fallbackValue === 'boolean')
122
+ return fallbackValue;
123
+ return defaultValue;
124
+ }
125
+ export function normalizeAgentBehaviorInstructions(value) {
126
+ if (typeof value !== 'string')
127
+ return null;
128
+ const trimmed = value.trim();
129
+ return trimmed.length > 0 ? trimmed : null;
130
+ }
131
+ export function resolveAgentBehaviorPolicy(params) {
132
+ const agentDefault = params?.agentDefault ?? null;
133
+ const conversationOverride = params?.conversationOverride ?? null;
134
+ const defaultInstructions = normalizeAgentBehaviorInstructions(agentDefault?.instructions);
135
+ const overrideInstructions = normalizeAgentBehaviorInstructions(conversationOverride?.instructions);
136
+ return {
137
+ participation: {
138
+ style: conversationOverride?.participationStyle
139
+ ?? agentDefault?.participationStyle
140
+ ?? DEFAULT_POLICY.participation.style,
141
+ allowAgentToAgent: coalesceBoolean(conversationOverride?.allowAgentToAgent, agentDefault?.allowAgentToAgent, DEFAULT_POLICY.participation.allowAgentToAgent),
142
+ allowHumanToAgent: DEFAULT_POLICY.participation.allowHumanToAgent,
143
+ allowLongRunningCollaboration: coalesceBoolean(conversationOverride?.allowLongRunningCollaboration, agentDefault?.allowLongRunningCollaboration, DEFAULT_POLICY.participation.allowLongRunningCollaboration),
144
+ requireMentionForGroupReplies: coalesceBoolean(conversationOverride?.requireMentionForGroupReplies, agentDefault?.requireMentionForGroupReplies, DEFAULT_POLICY.participation.requireMentionForGroupReplies),
145
+ maxConsecutiveAgentTurns: conversationOverride?.maxConsecutiveAgentTurns !== undefined
146
+ ? conversationOverride.maxConsecutiveAgentTurns ?? null
147
+ : agentDefault?.maxConsecutiveAgentTurns !== undefined
148
+ ? agentDefault.maxConsecutiveAgentTurns ?? null
149
+ : DEFAULT_POLICY.participation.maxConsecutiveAgentTurns,
150
+ },
151
+ instructions: [
152
+ ...(defaultInstructions ? [defaultInstructions] : []),
153
+ ...(overrideInstructions ? [overrideInstructions] : []),
154
+ ],
155
+ source: {
156
+ hasAgentDefault: agentDefault != null,
157
+ hasConversationOverride: conversationOverride != null,
158
+ },
159
+ };
160
+ }
161
+ function toHistorySenderType(message) {
162
+ if (message.senderType !== 'ai_agent') {
163
+ return 'human';
164
+ }
165
+ return resolveTurnMessageSemantics({
166
+ senderType: message.senderType,
167
+ metadata: message.metadata,
168
+ }) === 'turn_complete'
169
+ ? 'ai_agent'
170
+ : null;
171
+ }
172
+ function buildParticipationHistorySnapshotFromSenderTypes(recentSenderTypes) {
173
+ let consecutiveAgentTurns = 0;
174
+ for (const senderType of recentSenderTypes) {
175
+ if (senderType !== 'ai_agent')
176
+ break;
177
+ consecutiveAgentTurns += 1;
178
+ }
179
+ return {
180
+ recentSenderTypes,
181
+ recentHumanCount: recentSenderTypes.filter((senderType) => senderType === 'human').length,
182
+ recentAgentCount: recentSenderTypes.filter((senderType) => senderType === 'ai_agent').length,
183
+ consecutiveAgentTurns,
184
+ currentAgentStreakStartedByHuman: consecutiveAgentTurns > 0 && recentSenderTypes[consecutiveAgentTurns] === 'human',
185
+ };
186
+ }
187
+ export function buildParticipationHistorySnapshot(messages, agentId) {
188
+ void agentId;
189
+ const recentSenderTypes = messages
190
+ .flatMap((message) => {
191
+ const senderType = toHistorySenderType(message);
192
+ return senderType ? [senderType] : [];
193
+ });
194
+ return buildParticipationHistorySnapshotFromSenderTypes(recentSenderTypes);
195
+ }
196
+ export function appendParticipationHistoryMessage(snapshot, message, limit = PARTICIPATION_HISTORY_FETCH_LIMIT) {
197
+ const senderType = toHistorySenderType(message);
198
+ if (!senderType) {
199
+ return snapshot;
200
+ }
201
+ return buildParticipationHistorySnapshotFromSenderTypes([
202
+ senderType,
203
+ ...snapshot.recentSenderTypes,
204
+ ].slice(0, limit));
205
+ }
206
+ export function evaluateParticipationPolicy(policy, input) {
207
+ const consecutiveAgentTurns = Math.max(input.consecutiveAgentTurns ?? (input.senderType === 'ai_agent' ? 1 : 0), input.senderType === 'ai_agent' ? 1 : 0);
208
+ const currentAgentStreakStartedByHuman = input.currentAgentStreakStartedByHuman === true;
209
+ if (input.isOwner) {
210
+ return { allow: true, reason: 'owner messages always pass through' };
211
+ }
212
+ if (input.conversationType === 'group'
213
+ && policy.participation.requireMentionForGroupReplies
214
+ && !input.mentionedAgent) {
215
+ return {
216
+ allow: false,
217
+ reason: 'group replies require a direct mention',
218
+ };
219
+ }
220
+ if (input.senderType !== 'ai_agent') {
221
+ return { allow: true, reason: 'latest sender is human' };
222
+ }
223
+ if (!policy.participation.allowAgentToAgent) {
224
+ return { allow: false, reason: 'agent-to-agent participation disabled by policy' };
225
+ }
226
+ if (!policy.participation.allowLongRunningCollaboration
227
+ && (consecutiveAgentTurns > 1
228
+ || !currentAgentStreakStartedByHuman)) {
229
+ return {
230
+ allow: false,
231
+ reason: 'a fresh human steer is required before continuing agent collaboration',
232
+ };
233
+ }
234
+ if (typeof policy.participation.maxConsecutiveAgentTurns === 'number'
235
+ && policy.participation.maxConsecutiveAgentTurns >= 0
236
+ && consecutiveAgentTurns > policy.participation.maxConsecutiveAgentTurns) {
237
+ return { allow: false, reason: 'maximum consecutive agent turns reached' };
238
+ }
239
+ return { allow: true, reason: 'agent-to-agent participation allowed by policy' };
240
+ }
@@ -0,0 +1,250 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PARTICIPATION_HISTORY_FETCH_LIMIT = void 0;
4
+ exports.parseAgentBehaviorSettings = parseAgentBehaviorSettings;
5
+ exports.normalizeStoredAgentBehaviorPolicy = normalizeStoredAgentBehaviorPolicy;
6
+ exports.normalizeAgentBehaviorInstructions = normalizeAgentBehaviorInstructions;
7
+ exports.resolveAgentBehaviorPolicy = resolveAgentBehaviorPolicy;
8
+ exports.buildParticipationHistorySnapshot = buildParticipationHistorySnapshot;
9
+ exports.appendParticipationHistoryMessage = appendParticipationHistoryMessage;
10
+ exports.evaluateParticipationPolicy = evaluateParticipationPolicy;
11
+ const turnProtocol_js_1 = require("./turnProtocol.js");
12
+ exports.PARTICIPATION_HISTORY_FETCH_LIMIT = 50;
13
+ const VALID_PARTICIPATION_STYLES = new Set([
14
+ 'natural',
15
+ 'collaborative',
16
+ 'mention-first',
17
+ 'approval-gated',
18
+ 'handoff-only',
19
+ 'observer',
20
+ ]);
21
+ const DEFAULT_POLICY = {
22
+ participation: {
23
+ style: 'natural',
24
+ allowAgentToAgent: true,
25
+ allowHumanToAgent: true,
26
+ allowLongRunningCollaboration: true,
27
+ requireMentionForGroupReplies: false,
28
+ maxConsecutiveAgentTurns: null,
29
+ },
30
+ instructions: [],
31
+ source: {
32
+ hasAgentDefault: false,
33
+ hasConversationOverride: false,
34
+ },
35
+ };
36
+ function normalizeString(value) {
37
+ if (typeof value !== 'string')
38
+ return undefined;
39
+ const trimmed = value.trim();
40
+ return trimmed.length > 0 ? trimmed : undefined;
41
+ }
42
+ function normalizeOptionalBoolean(value, fieldName) {
43
+ if (value === undefined)
44
+ return undefined;
45
+ if (typeof value !== 'boolean') {
46
+ throw new Error(`${fieldName} must be a boolean`);
47
+ }
48
+ return value;
49
+ }
50
+ function normalizeMaxConsecutiveAgentTurns(value) {
51
+ if (value === undefined)
52
+ return undefined;
53
+ if (value === null || value === '')
54
+ return null;
55
+ if (!Number.isInteger(value) || Number(value) < 1 || Number(value) > 20) {
56
+ throw new Error('maxConsecutiveAgentTurns must be an integer between 1 and 20, or null');
57
+ }
58
+ return Number(value);
59
+ }
60
+ function resolveGroupMentionRequirement(input) {
61
+ if (input.requireMentionForGroupReplies !== undefined
62
+ && typeof input.requireMentionForGroupReplies !== 'boolean') {
63
+ throw new Error('requireMentionForGroupReplies must be a boolean');
64
+ }
65
+ if (input.requireMentionForGroupAgentReplies !== undefined
66
+ && typeof input.requireMentionForGroupAgentReplies !== 'boolean') {
67
+ throw new Error('requireMentionForGroupAgentReplies must be a boolean');
68
+ }
69
+ if (typeof input.requireMentionForGroupReplies === 'boolean') {
70
+ return input.requireMentionForGroupReplies;
71
+ }
72
+ if (typeof input.requireMentionForGroupAgentReplies === 'boolean') {
73
+ return input.requireMentionForGroupAgentReplies;
74
+ }
75
+ return undefined;
76
+ }
77
+ function parseAgentBehaviorSettings(raw) {
78
+ const input = raw && typeof raw === 'object' ? raw : {};
79
+ const participationStyle = normalizeString(input.participationStyle);
80
+ if (participationStyle && !VALID_PARTICIPATION_STYLES.has(participationStyle)) {
81
+ throw new Error('Invalid participationStyle');
82
+ }
83
+ const instructions = input.instructions === undefined
84
+ ? undefined
85
+ : input.instructions === null
86
+ ? null
87
+ : normalizeString(input.instructions) ?? null;
88
+ if (typeof instructions === 'string' && instructions.length > 2_000) {
89
+ throw new Error('instructions must be at most 2000 characters');
90
+ }
91
+ const allowAgentToAgent = normalizeOptionalBoolean(input.allowAgentToAgent, 'allowAgentToAgent');
92
+ const allowLongRunningCollaboration = normalizeOptionalBoolean(input.allowLongRunningCollaboration, 'allowLongRunningCollaboration');
93
+ const requireMentionForGroupReplies = resolveGroupMentionRequirement(input);
94
+ const maxConsecutiveAgentTurns = normalizeMaxConsecutiveAgentTurns(input.maxConsecutiveAgentTurns);
95
+ return {
96
+ ...(participationStyle ? { participationStyle: participationStyle } : {}),
97
+ ...(allowAgentToAgent !== undefined ? { allowAgentToAgent } : {}),
98
+ ...(allowLongRunningCollaboration !== undefined ? { allowLongRunningCollaboration } : {}),
99
+ ...(requireMentionForGroupReplies !== undefined ? { requireMentionForGroupReplies } : {}),
100
+ ...(maxConsecutiveAgentTurns !== undefined ? { maxConsecutiveAgentTurns } : {}),
101
+ ...(instructions !== undefined ? { instructions } : {}),
102
+ };
103
+ }
104
+ function normalizeStoredAgentBehaviorPolicy(raw) {
105
+ if (!raw)
106
+ return null;
107
+ return {
108
+ ...(typeof raw.participationStyle === 'string' && VALID_PARTICIPATION_STYLES.has(raw.participationStyle)
109
+ ? { participationStyle: raw.participationStyle }
110
+ : {}),
111
+ ...(typeof raw.allowAgentToAgent === 'boolean' ? { allowAgentToAgent: raw.allowAgentToAgent } : {}),
112
+ ...(typeof raw.allowLongRunningCollaboration === 'boolean'
113
+ ? { allowLongRunningCollaboration: raw.allowLongRunningCollaboration }
114
+ : {}),
115
+ ...(typeof raw.requireMentionForGroupReplies === 'boolean'
116
+ ? { requireMentionForGroupReplies: raw.requireMentionForGroupReplies }
117
+ : typeof raw.requireMentionForGroupAgentReplies === 'boolean'
118
+ ? { requireMentionForGroupReplies: raw.requireMentionForGroupAgentReplies }
119
+ : {}),
120
+ ...(typeof raw.maxConsecutiveAgentTurns === 'number' || raw.maxConsecutiveAgentTurns === null
121
+ ? { maxConsecutiveAgentTurns: raw.maxConsecutiveAgentTurns }
122
+ : {}),
123
+ ...(typeof raw.instructions === 'string' || raw.instructions === null
124
+ ? { instructions: raw.instructions }
125
+ : {}),
126
+ };
127
+ }
128
+ function coalesceBoolean(overrideValue, fallbackValue, defaultValue) {
129
+ if (typeof overrideValue === 'boolean')
130
+ return overrideValue;
131
+ if (typeof fallbackValue === 'boolean')
132
+ return fallbackValue;
133
+ return defaultValue;
134
+ }
135
+ function normalizeAgentBehaviorInstructions(value) {
136
+ if (typeof value !== 'string')
137
+ return null;
138
+ const trimmed = value.trim();
139
+ return trimmed.length > 0 ? trimmed : null;
140
+ }
141
+ function resolveAgentBehaviorPolicy(params) {
142
+ const agentDefault = params?.agentDefault ?? null;
143
+ const conversationOverride = params?.conversationOverride ?? null;
144
+ const defaultInstructions = normalizeAgentBehaviorInstructions(agentDefault?.instructions);
145
+ const overrideInstructions = normalizeAgentBehaviorInstructions(conversationOverride?.instructions);
146
+ return {
147
+ participation: {
148
+ style: conversationOverride?.participationStyle
149
+ ?? agentDefault?.participationStyle
150
+ ?? DEFAULT_POLICY.participation.style,
151
+ allowAgentToAgent: coalesceBoolean(conversationOverride?.allowAgentToAgent, agentDefault?.allowAgentToAgent, DEFAULT_POLICY.participation.allowAgentToAgent),
152
+ allowHumanToAgent: DEFAULT_POLICY.participation.allowHumanToAgent,
153
+ allowLongRunningCollaboration: coalesceBoolean(conversationOverride?.allowLongRunningCollaboration, agentDefault?.allowLongRunningCollaboration, DEFAULT_POLICY.participation.allowLongRunningCollaboration),
154
+ requireMentionForGroupReplies: coalesceBoolean(conversationOverride?.requireMentionForGroupReplies, agentDefault?.requireMentionForGroupReplies, DEFAULT_POLICY.participation.requireMentionForGroupReplies),
155
+ maxConsecutiveAgentTurns: conversationOverride?.maxConsecutiveAgentTurns !== undefined
156
+ ? conversationOverride.maxConsecutiveAgentTurns ?? null
157
+ : agentDefault?.maxConsecutiveAgentTurns !== undefined
158
+ ? agentDefault.maxConsecutiveAgentTurns ?? null
159
+ : DEFAULT_POLICY.participation.maxConsecutiveAgentTurns,
160
+ },
161
+ instructions: [
162
+ ...(defaultInstructions ? [defaultInstructions] : []),
163
+ ...(overrideInstructions ? [overrideInstructions] : []),
164
+ ],
165
+ source: {
166
+ hasAgentDefault: agentDefault != null,
167
+ hasConversationOverride: conversationOverride != null,
168
+ },
169
+ };
170
+ }
171
+ function toHistorySenderType(message) {
172
+ if (message.senderType !== 'ai_agent') {
173
+ return 'human';
174
+ }
175
+ return (0, turnProtocol_js_1.resolveTurnMessageSemantics)({
176
+ senderType: message.senderType,
177
+ metadata: message.metadata,
178
+ }) === 'turn_complete'
179
+ ? 'ai_agent'
180
+ : null;
181
+ }
182
+ function buildParticipationHistorySnapshotFromSenderTypes(recentSenderTypes) {
183
+ let consecutiveAgentTurns = 0;
184
+ for (const senderType of recentSenderTypes) {
185
+ if (senderType !== 'ai_agent')
186
+ break;
187
+ consecutiveAgentTurns += 1;
188
+ }
189
+ return {
190
+ recentSenderTypes,
191
+ recentHumanCount: recentSenderTypes.filter((senderType) => senderType === 'human').length,
192
+ recentAgentCount: recentSenderTypes.filter((senderType) => senderType === 'ai_agent').length,
193
+ consecutiveAgentTurns,
194
+ currentAgentStreakStartedByHuman: consecutiveAgentTurns > 0 && recentSenderTypes[consecutiveAgentTurns] === 'human',
195
+ };
196
+ }
197
+ function buildParticipationHistorySnapshot(messages, agentId) {
198
+ void agentId;
199
+ const recentSenderTypes = messages
200
+ .flatMap((message) => {
201
+ const senderType = toHistorySenderType(message);
202
+ return senderType ? [senderType] : [];
203
+ });
204
+ return buildParticipationHistorySnapshotFromSenderTypes(recentSenderTypes);
205
+ }
206
+ function appendParticipationHistoryMessage(snapshot, message, limit = exports.PARTICIPATION_HISTORY_FETCH_LIMIT) {
207
+ const senderType = toHistorySenderType(message);
208
+ if (!senderType) {
209
+ return snapshot;
210
+ }
211
+ return buildParticipationHistorySnapshotFromSenderTypes([
212
+ senderType,
213
+ ...snapshot.recentSenderTypes,
214
+ ].slice(0, limit));
215
+ }
216
+ function evaluateParticipationPolicy(policy, input) {
217
+ const consecutiveAgentTurns = Math.max(input.consecutiveAgentTurns ?? (input.senderType === 'ai_agent' ? 1 : 0), input.senderType === 'ai_agent' ? 1 : 0);
218
+ const currentAgentStreakStartedByHuman = input.currentAgentStreakStartedByHuman === true;
219
+ if (input.isOwner) {
220
+ return { allow: true, reason: 'owner messages always pass through' };
221
+ }
222
+ if (input.conversationType === 'group'
223
+ && policy.participation.requireMentionForGroupReplies
224
+ && !input.mentionedAgent) {
225
+ return {
226
+ allow: false,
227
+ reason: 'group replies require a direct mention',
228
+ };
229
+ }
230
+ if (input.senderType !== 'ai_agent') {
231
+ return { allow: true, reason: 'latest sender is human' };
232
+ }
233
+ if (!policy.participation.allowAgentToAgent) {
234
+ return { allow: false, reason: 'agent-to-agent participation disabled by policy' };
235
+ }
236
+ if (!policy.participation.allowLongRunningCollaboration
237
+ && (consecutiveAgentTurns > 1
238
+ || !currentAgentStreakStartedByHuman)) {
239
+ return {
240
+ allow: false,
241
+ reason: 'a fresh human steer is required before continuing agent collaboration',
242
+ };
243
+ }
244
+ if (typeof policy.participation.maxConsecutiveAgentTurns === 'number'
245
+ && policy.participation.maxConsecutiveAgentTurns >= 0
246
+ && consecutiveAgentTurns > policy.participation.maxConsecutiveAgentTurns) {
247
+ return { allow: false, reason: 'maximum consecutive agent turns reached' };
248
+ }
249
+ return { allow: true, reason: 'agent-to-agent participation allowed by policy' };
250
+ }
@@ -0,0 +1,72 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.serializeContactRequest = serializeContactRequest;
4
+ function isRecord(value) {
5
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
6
+ }
7
+ function normalizeTimestamp(value) {
8
+ if (value instanceof Date)
9
+ return value.toISOString();
10
+ if (isRecord(value) && typeof value.toDate === 'function') {
11
+ const result = value.toDate();
12
+ if (result instanceof Date)
13
+ return result.toISOString();
14
+ }
15
+ return null;
16
+ }
17
+ function normalizeStatus(value) {
18
+ if (value === 'pending'
19
+ || value === 'approved'
20
+ || value === 'rejected'
21
+ || value === 'expired') {
22
+ return value;
23
+ }
24
+ return 'pending';
25
+ }
26
+ function serializeContactRequest(requestId, data) {
27
+ if (typeof data.approverId !== 'string' || data.approverId.length === 0) {
28
+ return null;
29
+ }
30
+ if (typeof data.requesterId !== 'string' || data.requesterId.length === 0) {
31
+ return null;
32
+ }
33
+ if (typeof data.targetId !== 'string' || data.targetId.length === 0) {
34
+ return null;
35
+ }
36
+ const kindValue = typeof data.kind === 'string' ? data.kind : 'dm';
37
+ if (kindValue !== 'dm' && kindValue !== 'group_invite') {
38
+ return null;
39
+ }
40
+ let groupContext = null;
41
+ if (kindValue === 'group_invite') {
42
+ const ctx = isRecord(data.groupContext) ? data.groupContext : null;
43
+ const conversationId = typeof ctx?.conversationId === 'string'
44
+ ? ctx.conversationId.trim()
45
+ : '';
46
+ if (!conversationId) {
47
+ return null;
48
+ }
49
+ groupContext = {
50
+ conversationId,
51
+ groupName: typeof ctx?.groupName === 'string' ? ctx.groupName : null,
52
+ };
53
+ }
54
+ const payload = {
55
+ id: requestId,
56
+ requesterId: data.requesterId,
57
+ requesterName: typeof data.requesterName === 'string' ? data.requesterName : 'Unknown',
58
+ requesterAvatarUrl: typeof data.requesterAvatarUrl === 'string' ? data.requesterAvatarUrl : null,
59
+ targetId: data.targetId,
60
+ approverId: data.approverId,
61
+ message: typeof data.message === 'string' ? data.message : null,
62
+ status: normalizeStatus(data.status),
63
+ kind: kindValue,
64
+ createdAt: normalizeTimestamp(data.createdAt),
65
+ resolvedAt: normalizeTimestamp(data.resolvedAt),
66
+ expiresAt: normalizeTimestamp(data.expiresAt),
67
+ };
68
+ if (groupContext) {
69
+ payload.groupContext = groupContext;
70
+ }
71
+ return payload;
72
+ }
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./media.js"), exports);
18
+ __exportStar(require("./message.js"), exports);
19
+ __exportStar(require("./turnProtocol.js"), exports);
20
+ __exportStar(require("./agentBehaviorPolicy.js"), exports);
21
+ __exportStar(require("./contactRequest.js"), exports);