@canonmsg/core 0.15.3 → 0.15.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/reach-out.d.ts +5 -1
- package/dist/reach-out.js +49 -5
- package/dist/types.d.ts +3 -0
- package/package.json +1 -1
package/dist/reach-out.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { CanonResolveAdmissionResult, CreateContactRequestResult, CreateConversationOptions, SendMessageOptions } from './types.js';
|
|
1
|
+
import type { CanonResolveAdmissionResult, CreateContactRequestResult, CreateConversationOptions, SendMessageOptions, SessionConfig } from './types.js';
|
|
2
2
|
export interface CanonReachOutClient {
|
|
3
3
|
resolveAdmission(targetUserId: string): Promise<CanonResolveAdmissionResult>;
|
|
4
4
|
createConversation(options: CreateConversationOptions): Promise<{
|
|
@@ -14,6 +14,7 @@ export interface CanonReachOutOptions {
|
|
|
14
14
|
text?: string | null;
|
|
15
15
|
requestMessage?: string | null;
|
|
16
16
|
sendMessageOptions?: SendMessageOptions;
|
|
17
|
+
sessionConfig?: SessionConfig | null;
|
|
17
18
|
}
|
|
18
19
|
export type CanonReachOutResult = {
|
|
19
20
|
status: 'messaged';
|
|
@@ -25,6 +26,9 @@ export type CanonReachOutResult = {
|
|
|
25
26
|
} | {
|
|
26
27
|
status: 'pending';
|
|
27
28
|
requestId: string | null;
|
|
29
|
+
} | {
|
|
30
|
+
status: 'setup_required';
|
|
31
|
+
reason: string;
|
|
28
32
|
} | {
|
|
29
33
|
status: 'blocked' | 'unavailable';
|
|
30
34
|
reason: string;
|
package/dist/reach-out.js
CHANGED
|
@@ -11,6 +11,12 @@ function normalizeContactRequestMessage(requestMessage, fallbackText) {
|
|
|
11
11
|
return text;
|
|
12
12
|
return `${text.slice(0, CONTACT_REQUEST_MESSAGE_LIMIT - 3)}...`;
|
|
13
13
|
}
|
|
14
|
+
function optionsForResolvedTarget(options, targetUserType) {
|
|
15
|
+
if (options.sessionConfig && targetUserType === 'human') {
|
|
16
|
+
return { ...options, sessionConfig: null };
|
|
17
|
+
}
|
|
18
|
+
return options;
|
|
19
|
+
}
|
|
14
20
|
function isConnectionRequiredError(error) {
|
|
15
21
|
const status = error && typeof error === 'object' && 'status' in error
|
|
16
22
|
? Number(error.status)
|
|
@@ -18,10 +24,34 @@ function isConnectionRequiredError(error) {
|
|
|
18
24
|
const message = error instanceof Error ? error.message : String(error ?? '');
|
|
19
25
|
return status === 403 && /CONNECTION_REQUIRED|connection required/i.test(message);
|
|
20
26
|
}
|
|
27
|
+
function isSessionSetupRequiredError(error) {
|
|
28
|
+
const status = error && typeof error === 'object' && 'status' in error
|
|
29
|
+
? Number(error.status)
|
|
30
|
+
: null;
|
|
31
|
+
if (status !== 400 && status !== 403)
|
|
32
|
+
return null;
|
|
33
|
+
const rawMessage = error instanceof Error ? error.message : String(error ?? '');
|
|
34
|
+
const message = rawMessage.replace(/^Canon API error \d+:\s*/i, '').trim();
|
|
35
|
+
const parsed = (() => {
|
|
36
|
+
try {
|
|
37
|
+
const value = JSON.parse(message);
|
|
38
|
+
return typeof value.error === 'string' ? value.error : message;
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return message;
|
|
42
|
+
}
|
|
43
|
+
})();
|
|
44
|
+
if (/session config|execution mode|coding session|worktree|workspace|permission mode|agent owner|runtime unavailable|unsupported control|invalid .*mode|invalid .*model/i
|
|
45
|
+
.test(parsed)) {
|
|
46
|
+
return parsed || 'Agent session setup is required.';
|
|
47
|
+
}
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
21
50
|
async function openConversationAndMaybeMessage(client, options) {
|
|
22
51
|
const { conversationId } = await client.createConversation({
|
|
23
52
|
type: 'direct',
|
|
24
53
|
targetUserId: options.targetUserId,
|
|
54
|
+
...(options.sessionConfig ? { sessionConfig: options.sessionConfig } : {}),
|
|
25
55
|
});
|
|
26
56
|
const text = normalizeOptionalText(options.text);
|
|
27
57
|
if (text) {
|
|
@@ -35,7 +65,16 @@ async function openConversationAndMaybeMessage(client, options) {
|
|
|
35
65
|
async function requestContact(client, options) {
|
|
36
66
|
const result = await client.createContactRequest(options.targetUserId, normalizeContactRequestMessage(options.requestMessage, options.text));
|
|
37
67
|
if (result.status === 'open') {
|
|
38
|
-
|
|
68
|
+
try {
|
|
69
|
+
return await openConversationAndMaybeMessage(client, { ...options, requestMessage: null });
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
const setupReason = isSessionSetupRequiredError(error);
|
|
73
|
+
if (setupReason) {
|
|
74
|
+
return { status: 'setup_required', reason: setupReason };
|
|
75
|
+
}
|
|
76
|
+
throw error;
|
|
77
|
+
}
|
|
39
78
|
}
|
|
40
79
|
if (result.status === 'duplicate') {
|
|
41
80
|
return { status: 'pending', requestId: result.requestId };
|
|
@@ -48,14 +87,19 @@ async function requestContact(client, options) {
|
|
|
48
87
|
* their product behavior is "message if reachable, otherwise request access."
|
|
49
88
|
*/
|
|
50
89
|
export async function reachOutToCanonContact(client, options) {
|
|
51
|
-
const { admission } = await client.resolveAdmission(options.targetUserId);
|
|
90
|
+
const { target, admission } = await client.resolveAdmission(options.targetUserId);
|
|
91
|
+
const resolvedOptions = optionsForResolvedTarget(options, target?.userType);
|
|
52
92
|
if (admission.state === 'allowed' && admission.canMessage) {
|
|
53
93
|
try {
|
|
54
|
-
return await openConversationAndMaybeMessage(client,
|
|
94
|
+
return await openConversationAndMaybeMessage(client, resolvedOptions);
|
|
55
95
|
}
|
|
56
96
|
catch (error) {
|
|
97
|
+
const setupReason = isSessionSetupRequiredError(error);
|
|
98
|
+
if (setupReason) {
|
|
99
|
+
return { status: 'setup_required', reason: setupReason };
|
|
100
|
+
}
|
|
57
101
|
if (isConnectionRequiredError(error)) {
|
|
58
|
-
return requestContact(client,
|
|
102
|
+
return requestContact(client, resolvedOptions);
|
|
59
103
|
}
|
|
60
104
|
throw error;
|
|
61
105
|
}
|
|
@@ -64,7 +108,7 @@ export async function reachOutToCanonContact(client, options) {
|
|
|
64
108
|
return { status: 'pending', requestId: admission.pendingRequestId ?? null };
|
|
65
109
|
}
|
|
66
110
|
if (admission.state === 'request-required' && admission.canRequestContact) {
|
|
67
|
-
return requestContact(client,
|
|
111
|
+
return requestContact(client, resolvedOptions);
|
|
68
112
|
}
|
|
69
113
|
if (admission.state === 'blocked') {
|
|
70
114
|
return { status: 'blocked', reason: 'blocked' };
|
package/dist/types.d.ts
CHANGED
|
@@ -30,6 +30,7 @@ export interface ContactCardPayload {
|
|
|
30
30
|
displayName: string;
|
|
31
31
|
avatarUrl: string | null;
|
|
32
32
|
userType: 'human' | 'ai_agent';
|
|
33
|
+
clientType?: AgentClientType;
|
|
33
34
|
about?: string;
|
|
34
35
|
isActive?: boolean;
|
|
35
36
|
ownerId?: string;
|
|
@@ -461,6 +462,8 @@ export interface CreateConversationOptions {
|
|
|
461
462
|
targetUserId?: string;
|
|
462
463
|
memberIds?: string[];
|
|
463
464
|
name?: string;
|
|
465
|
+
/** Required when creating a direct conversation with a first-party coding agent. */
|
|
466
|
+
sessionConfig?: SessionConfig | null;
|
|
464
467
|
}
|
|
465
468
|
export type StreamingStatus = 'thinking' | 'streaming' | 'tool';
|
|
466
469
|
export interface SetStreamingOptions {
|