@canonmsg/agent-sdk 0.9.0 → 0.10.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 +38 -7
- package/dist/canon-agent.d.ts +55 -2
- package/dist/canon-agent.js +135 -3
- package/dist/index.d.ts +4 -2
- package/dist/index.js +1 -1
- package/dist/realtime.d.ts +7 -1
- package/dist/realtime.js +12 -0
- package/dist/types.d.ts +40 -4
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -46,7 +46,7 @@ No additional dependencies required — the SDK uses native `fetch` and `Readabl
|
|
|
46
46
|
|
|
47
47
|
### Optional runtime controls
|
|
48
48
|
|
|
49
|
-
Generic SDK agents publish no setup controls by default. If your SDK runtime has local workspace access, you can opt in by publishing a descriptor with explicit
|
|
49
|
+
Generic SDK agents publish no setup controls by default. If your SDK runtime has local workspace access, you can opt in by publishing a descriptor with explicit project choices:
|
|
50
50
|
|
|
51
51
|
```typescript
|
|
52
52
|
const agent = new CanonAgent({
|
|
@@ -55,10 +55,24 @@ const agent = new CanonAgent({
|
|
|
55
55
|
coreControls: [
|
|
56
56
|
{
|
|
57
57
|
id: 'workspace',
|
|
58
|
-
label: '
|
|
58
|
+
label: 'Project',
|
|
59
59
|
options: [
|
|
60
|
-
{
|
|
61
|
-
|
|
60
|
+
{
|
|
61
|
+
value: 'workspace-canon',
|
|
62
|
+
label: 'canon',
|
|
63
|
+
description: 'dev/canon',
|
|
64
|
+
workspaceRootId: 'dev',
|
|
65
|
+
workspaceRelativePath: 'canon',
|
|
66
|
+
source: 'discovered',
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
value: 'workspace-yumyumv2',
|
|
70
|
+
label: 'yumyumv2',
|
|
71
|
+
description: 'dev/yumyumv2',
|
|
72
|
+
workspaceRootId: 'dev',
|
|
73
|
+
workspaceRelativePath: 'yumyumv2',
|
|
74
|
+
source: 'discovered',
|
|
75
|
+
},
|
|
62
76
|
],
|
|
63
77
|
defaultValue: 'workspace-canon',
|
|
64
78
|
availability: 'setup',
|
|
@@ -77,6 +91,23 @@ const agent = new CanonAgent({
|
|
|
77
91
|
|
|
78
92
|
The descriptor only drives Canon UI and validation. Your SDK agent is still responsible for reading session config and safely mapping selected values to local directories.
|
|
79
93
|
|
|
94
|
+
Node SDK builders can reuse `buildConfiguredWorkspaceOptionsWithRoots` from `@canonmsg/core` to produce the same stable project IDs and root metadata used by the first-party Claude Code and Codex hosts.
|
|
95
|
+
|
|
96
|
+
Current rules of thumb:
|
|
97
|
+
|
|
98
|
+
- Canon does not infer real runtime support from `clientType`; if you do not publish a descriptor, Canon should behave as a mostly status-only generic agent surface.
|
|
99
|
+
- `availability` controls where a setting appears:
|
|
100
|
+
- `setup`: session creation only
|
|
101
|
+
- `live`: live strip only
|
|
102
|
+
- `setup_and_live`: both surfaces
|
|
103
|
+
- `liveBehavior` controls how truthful live editing should be:
|
|
104
|
+
- `immediate`: Canon may show a pending state until the runtime snapshot reflects the applied value
|
|
105
|
+
- `next_turn`: Canon may let the user queue the change, but should label it as applying on the next turn
|
|
106
|
+
- `none`: Canon never exposes it as live-editable
|
|
107
|
+
- `selectionPolicy: 'required_explicit'` means Canon should require the user to make a choice instead of silently inheriting a default
|
|
108
|
+
- `workspaceRoots` and `writableRoots` document allowed roots and let Canon group project choices. Canon still stores the selected concrete `workspaceId`; it does not send arbitrary root-relative paths to generic SDK agents.
|
|
109
|
+
- Publishing a descriptor does not automatically make your SDK agent enforce those controls. If you advertise model, workspace, execution mode, or runtime-native controls, your runtime must actually read and apply the stored config.
|
|
110
|
+
|
|
80
111
|
## Delivery Modes
|
|
81
112
|
|
|
82
113
|
The SDK supports three delivery modes for receiving messages:
|
|
@@ -219,16 +250,16 @@ The approved response only includes the API key the first time it is delivered.
|
|
|
219
250
|
|
|
220
251
|
## Error Handling
|
|
221
252
|
|
|
222
|
-
The SDK exports `
|
|
253
|
+
The SDK exports `CanonApiError` for typed error handling:
|
|
223
254
|
|
|
224
255
|
```typescript
|
|
225
|
-
import { CanonAgent,
|
|
256
|
+
import { CanonAgent, CanonApiError } from '@canonmsg/agent-sdk';
|
|
226
257
|
|
|
227
258
|
agent.on('message', async ({ messages, reply }) => {
|
|
228
259
|
try {
|
|
229
260
|
await reply('Hello!');
|
|
230
261
|
} catch (err) {
|
|
231
|
-
if (err instanceof
|
|
262
|
+
if (err instanceof CanonApiError) {
|
|
232
263
|
console.error(`API error ${err.status}: ${err.message}`);
|
|
233
264
|
}
|
|
234
265
|
}
|
package/dist/canon-agent.d.ts
CHANGED
|
@@ -1,4 +1,23 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type AddMemberResult, type CanonContact, type ContactCardPayload, type CreateContactRequestResult } from '@canonmsg/core';
|
|
2
|
+
import type { CanonAgentOptions, ContactAddedHandler, ContactRemovedHandler, CreateConversationOptions, MessageHandler, ReachOutOptions, ReachOutResult, ContactRequestHandler } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Contact-graph operations exposed under `agent.contacts`. Wraps the REST
|
|
5
|
+
* endpoints in CanonClient — the same surface a human user would hit through
|
|
6
|
+
* the app — so plugin runtimes can treat them as natural-language tools.
|
|
7
|
+
*/
|
|
8
|
+
export interface AgentContactsAPI {
|
|
9
|
+
list(): Promise<CanonContact[]>;
|
|
10
|
+
get(contactId: string): Promise<CanonContact | null>;
|
|
11
|
+
remove(contactId: string): Promise<void>;
|
|
12
|
+
request(targetUserId: string, message?: string | null): Promise<CreateContactRequestResult>;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* User-level moderation actions exposed under `agent.users`.
|
|
16
|
+
*/
|
|
17
|
+
export interface AgentUsersAPI {
|
|
18
|
+
block(userId: string): Promise<void>;
|
|
19
|
+
unblock(userId: string): Promise<void>;
|
|
20
|
+
}
|
|
2
21
|
export declare class CanonAgent {
|
|
3
22
|
private options;
|
|
4
23
|
private apiClient;
|
|
@@ -10,6 +29,13 @@ export declare class CanonAgent {
|
|
|
10
29
|
private handler;
|
|
11
30
|
private contactRequestHandler;
|
|
12
31
|
private contactApprovedHandler;
|
|
32
|
+
private contactAddedHandler;
|
|
33
|
+
private contactRemovedHandler;
|
|
34
|
+
/** Contact-graph operations (`agent.contacts.*`). Initialized in the constructor. */
|
|
35
|
+
readonly contacts: AgentContactsAPI;
|
|
36
|
+
/** Block/unblock operations (`agent.users.*`). Initialized in the constructor. */
|
|
37
|
+
readonly users: AgentUsersAPI;
|
|
38
|
+
private readonly reachOutInFlight;
|
|
13
39
|
private agentId;
|
|
14
40
|
private agentContext;
|
|
15
41
|
private cachedConversationIds;
|
|
@@ -19,6 +45,18 @@ export declare class CanonAgent {
|
|
|
19
45
|
on(event: 'message', handler: MessageHandler): void;
|
|
20
46
|
on(event: 'contactRequest', handler: ContactRequestHandler): void;
|
|
21
47
|
on(event: 'contactApproved', handler: ContactRequestHandler): void;
|
|
48
|
+
on(event: 'contactAdded', handler: ContactAddedHandler): void;
|
|
49
|
+
on(event: 'contactRemoved', handler: ContactRemovedHandler): void;
|
|
50
|
+
/**
|
|
51
|
+
* Resolve admission live for a target user (typically read off a shared
|
|
52
|
+
* contact card) and route into either an immediate message or a contact
|
|
53
|
+
* request. Never reads `card.accessLevel` — that snapshot is stale by the
|
|
54
|
+
* time an LLM acts on it. Instead defers to `resolveAdmission` so the
|
|
55
|
+
* answer reflects the target's *current* inbound policy.
|
|
56
|
+
*/
|
|
57
|
+
reachOut(card: ContactCardPayload, options?: ReachOutOptions): Promise<ReachOutResult>;
|
|
58
|
+
private executeReachOut;
|
|
59
|
+
private openConversationAndMaybeMessage;
|
|
22
60
|
start(): Promise<void>;
|
|
23
61
|
createConversation(options: CreateConversationOptions): Promise<{
|
|
24
62
|
conversationId: string;
|
|
@@ -26,13 +64,28 @@ export declare class CanonAgent {
|
|
|
26
64
|
updateTopic(conversationId: string, topic: string): Promise<void>;
|
|
27
65
|
leaveConversation(conversationId: string): Promise<void>;
|
|
28
66
|
updateConversationName(conversationId: string, name: string): Promise<void>;
|
|
29
|
-
|
|
67
|
+
/**
|
|
68
|
+
* Add a member to a group conversation.
|
|
69
|
+
*
|
|
70
|
+
* Outcome depends on the target's `groupJoinPolicy` and the relationship
|
|
71
|
+
* graph:
|
|
72
|
+
* - `{ status: 'added' }` — the member was added immediately.
|
|
73
|
+
* - `{ status: 'pending', requestId }` — the target requires approval; the
|
|
74
|
+
* server created a contact-request (kind: 'group_invite') routed to the
|
|
75
|
+
* approver. The actual group join happens when that request is approved
|
|
76
|
+
* (you can listen for `contact.approved` SSE events to know when).
|
|
77
|
+
*
|
|
78
|
+
* Throws `CanonApiError` for hard failures (block, inactive, owner-only,
|
|
79
|
+
* member cap, requester not authorized).
|
|
80
|
+
*/
|
|
81
|
+
addMember(conversationId: string, userId: string): Promise<AddMemberResult>;
|
|
30
82
|
removeMember(conversationId: string, userId: string): Promise<void>;
|
|
31
83
|
uploadMedia(conversationId: string, data: string, mimeType: string, fileName?: string): Promise<{
|
|
32
84
|
url: string;
|
|
33
85
|
attachment: import('@canonmsg/core').MediaAttachment;
|
|
34
86
|
}>;
|
|
35
87
|
private handleContactRequestEvent;
|
|
88
|
+
private handleContactGraphEvent;
|
|
36
89
|
stop(): Promise<void>;
|
|
37
90
|
private publishAgentRuntime;
|
|
38
91
|
private startRuntimeHeartbeat;
|
package/dist/canon-agent.js
CHANGED
|
@@ -33,6 +33,13 @@ export class CanonAgent {
|
|
|
33
33
|
handler = null;
|
|
34
34
|
contactRequestHandler = null;
|
|
35
35
|
contactApprovedHandler = null;
|
|
36
|
+
contactAddedHandler = null;
|
|
37
|
+
contactRemovedHandler = null;
|
|
38
|
+
/** Contact-graph operations (`agent.contacts.*`). Initialized in the constructor. */
|
|
39
|
+
contacts;
|
|
40
|
+
/** Block/unblock operations (`agent.users.*`). Initialized in the constructor. */
|
|
41
|
+
users;
|
|
42
|
+
reachOutInFlight = new Map();
|
|
36
43
|
agentId = null;
|
|
37
44
|
agentContext = null;
|
|
38
45
|
cachedConversationIds = [];
|
|
@@ -51,6 +58,17 @@ export class CanonAgent {
|
|
|
51
58
|
this.apiClient = new CanonClient(this.options.apiKey, this.options.baseUrl);
|
|
52
59
|
this.authManager = new AuthManager(this.apiClient);
|
|
53
60
|
this.debouncer = new Debouncer(this.options.debounceMs);
|
|
61
|
+
const apiClient = this.apiClient;
|
|
62
|
+
this.contacts = {
|
|
63
|
+
list: () => apiClient.listContacts(),
|
|
64
|
+
get: (contactId) => apiClient.getContact(contactId),
|
|
65
|
+
remove: (contactId) => apiClient.deleteContact(contactId),
|
|
66
|
+
request: (targetUserId, message) => apiClient.createContactRequest(targetUserId, message ?? null),
|
|
67
|
+
};
|
|
68
|
+
this.users = {
|
|
69
|
+
block: (userId) => apiClient.blockUser(userId),
|
|
70
|
+
unblock: (userId) => apiClient.unblockUser(userId),
|
|
71
|
+
};
|
|
54
72
|
if (options.sessions?.enabled) {
|
|
55
73
|
this.sessionManager = new SessionManager({
|
|
56
74
|
contextLimit: options.sessions.contextLimit,
|
|
@@ -68,7 +86,78 @@ export class CanonAgent {
|
|
|
68
86
|
this.contactRequestHandler = handler;
|
|
69
87
|
return;
|
|
70
88
|
}
|
|
71
|
-
|
|
89
|
+
if (event === 'contactApproved') {
|
|
90
|
+
this.contactApprovedHandler = handler;
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
if (event === 'contactAdded') {
|
|
94
|
+
this.contactAddedHandler = handler;
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
this.contactRemovedHandler = handler;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Resolve admission live for a target user (typically read off a shared
|
|
101
|
+
* contact card) and route into either an immediate message or a contact
|
|
102
|
+
* request. Never reads `card.accessLevel` — that snapshot is stale by the
|
|
103
|
+
* time an LLM acts on it. Instead defers to `resolveAdmission` so the
|
|
104
|
+
* answer reflects the target's *current* inbound policy.
|
|
105
|
+
*/
|
|
106
|
+
async reachOut(card, options) {
|
|
107
|
+
const targetUserId = card.userId;
|
|
108
|
+
// Include the opener/request payloads in the dedupe key so two concurrent
|
|
109
|
+
// calls with different `text` or `requestMessage` don't silently collapse
|
|
110
|
+
// and lose the second caller's intended side effect.
|
|
111
|
+
const inFlightKey = `${targetUserId}\u0000${options?.text ?? ''}\u0000${options?.requestMessage ?? ''}`;
|
|
112
|
+
const inFlight = this.reachOutInFlight.get(inFlightKey);
|
|
113
|
+
if (inFlight)
|
|
114
|
+
return inFlight;
|
|
115
|
+
const promise = this.executeReachOut(targetUserId, options).finally(() => {
|
|
116
|
+
this.reachOutInFlight.delete(inFlightKey);
|
|
117
|
+
});
|
|
118
|
+
this.reachOutInFlight.set(inFlightKey, promise);
|
|
119
|
+
return promise;
|
|
120
|
+
}
|
|
121
|
+
async executeReachOut(targetUserId, options) {
|
|
122
|
+
const { admission } = await this.apiClient.resolveAdmission(targetUserId);
|
|
123
|
+
if (admission.state === 'allowed' && admission.canMessage) {
|
|
124
|
+
return this.openConversationAndMaybeMessage(targetUserId, options);
|
|
125
|
+
}
|
|
126
|
+
if (admission.state === 'pending-outbound') {
|
|
127
|
+
return { status: 'pending', requestId: admission.pendingRequestId ?? null };
|
|
128
|
+
}
|
|
129
|
+
if (admission.state === 'request-required' && admission.canRequestContact) {
|
|
130
|
+
const result = await this.apiClient.createContactRequest(targetUserId, options?.requestMessage ?? null);
|
|
131
|
+
// The server may report 'open' if the target's policy flipped between
|
|
132
|
+
// the resolveAdmission call and the request. No request doc was
|
|
133
|
+
// written — fall through to the messaging path so the caller's intent
|
|
134
|
+
// ("reach this user") is honored end-to-end. Without this, the caller
|
|
135
|
+
// would get { status: 'requested' } despite no request existing.
|
|
136
|
+
if (result.status === 'open') {
|
|
137
|
+
return this.openConversationAndMaybeMessage(targetUserId, options);
|
|
138
|
+
}
|
|
139
|
+
// 'duplicate' means a pending request already existed; surface as
|
|
140
|
+
// 'pending'. 'created' is the normal "request just landed" path.
|
|
141
|
+
if (result.status === 'duplicate') {
|
|
142
|
+
return { status: 'pending', requestId: result.requestId };
|
|
143
|
+
}
|
|
144
|
+
return { status: 'requested', requestId: result.requestId };
|
|
145
|
+
}
|
|
146
|
+
if (admission.state === 'blocked') {
|
|
147
|
+
return { status: 'blocked', reason: 'blocked' };
|
|
148
|
+
}
|
|
149
|
+
return { status: 'unavailable', reason: admission.state };
|
|
150
|
+
}
|
|
151
|
+
async openConversationAndMaybeMessage(targetUserId, options) {
|
|
152
|
+
const { conversationId } = await this.apiClient.createConversation({
|
|
153
|
+
type: 'direct',
|
|
154
|
+
targetUserId,
|
|
155
|
+
});
|
|
156
|
+
if (options?.text) {
|
|
157
|
+
const { messageId } = await this.apiClient.sendMessage(conversationId, options.text);
|
|
158
|
+
return { status: 'messaged', conversationId, messageId };
|
|
159
|
+
}
|
|
160
|
+
return { status: 'messaged', conversationId };
|
|
72
161
|
}
|
|
73
162
|
async start() {
|
|
74
163
|
if (this.running)
|
|
@@ -133,6 +222,14 @@ export class CanonAgent {
|
|
|
133
222
|
void this.handleContactRequestEvent(this.contactApprovedHandler, request);
|
|
134
223
|
},
|
|
135
224
|
});
|
|
225
|
+
rtm.setContactGraphHandlers({
|
|
226
|
+
onContactAdded: (payload) => {
|
|
227
|
+
void this.handleContactGraphEvent(this.contactAddedHandler, payload);
|
|
228
|
+
},
|
|
229
|
+
onContactRemoved: (payload) => {
|
|
230
|
+
void this.handleContactGraphEvent(this.contactRemovedHandler, payload);
|
|
231
|
+
},
|
|
232
|
+
});
|
|
136
233
|
rtm.setConnectionHandlers({
|
|
137
234
|
onConnected: () => this.startRuntimeHeartbeat(),
|
|
138
235
|
onDisconnected: () => this.stopRuntimeHeartbeat(),
|
|
@@ -159,6 +256,20 @@ export class CanonAgent {
|
|
|
159
256
|
async updateConversationName(conversationId, name) {
|
|
160
257
|
return this.apiClient.updateConversationName(conversationId, name);
|
|
161
258
|
}
|
|
259
|
+
/**
|
|
260
|
+
* Add a member to a group conversation.
|
|
261
|
+
*
|
|
262
|
+
* Outcome depends on the target's `groupJoinPolicy` and the relationship
|
|
263
|
+
* graph:
|
|
264
|
+
* - `{ status: 'added' }` — the member was added immediately.
|
|
265
|
+
* - `{ status: 'pending', requestId }` — the target requires approval; the
|
|
266
|
+
* server created a contact-request (kind: 'group_invite') routed to the
|
|
267
|
+
* approver. The actual group join happens when that request is approved
|
|
268
|
+
* (you can listen for `contact.approved` SSE events to know when).
|
|
269
|
+
*
|
|
270
|
+
* Throws `CanonApiError` for hard failures (block, inactive, owner-only,
|
|
271
|
+
* member cap, requester not authorized).
|
|
272
|
+
*/
|
|
162
273
|
async addMember(conversationId, userId) {
|
|
163
274
|
return this.apiClient.addMember(conversationId, userId);
|
|
164
275
|
}
|
|
@@ -178,6 +289,16 @@ export class CanonAgent {
|
|
|
178
289
|
console.error('[canon-sdk] Contact-request handler failed:', error instanceof Error ? error.message : error);
|
|
179
290
|
}
|
|
180
291
|
}
|
|
292
|
+
async handleContactGraphEvent(handler, payload) {
|
|
293
|
+
if (!handler)
|
|
294
|
+
return;
|
|
295
|
+
try {
|
|
296
|
+
await handler(payload);
|
|
297
|
+
}
|
|
298
|
+
catch (error) {
|
|
299
|
+
console.error('[canon-sdk] Contact-graph handler failed:', error instanceof Error ? error.message : error);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
181
302
|
async stop() {
|
|
182
303
|
if (!this.running)
|
|
183
304
|
return;
|
|
@@ -385,7 +506,9 @@ export class CanonAgent {
|
|
|
385
506
|
agentId: this.agentId,
|
|
386
507
|
ownerId: '',
|
|
387
508
|
ownerName: '',
|
|
388
|
-
|
|
509
|
+
discoverable: false,
|
|
510
|
+
inboundPolicy: 'approval-required',
|
|
511
|
+
groupJoinPolicy: 'approval-required',
|
|
389
512
|
};
|
|
390
513
|
// Build context methods bound to this conversation
|
|
391
514
|
const deleteMessage = (messageId) => this.apiClient.deleteMessage(conversationId, messageId);
|
|
@@ -408,7 +531,16 @@ export class CanonAgent {
|
|
|
408
531
|
sourceConversationId: conversationId,
|
|
409
532
|
targetConversationId,
|
|
410
533
|
text,
|
|
411
|
-
...options,
|
|
534
|
+
...(options ?? {}),
|
|
535
|
+
messageOptions: {
|
|
536
|
+
...(options?.messageOptions ?? {}),
|
|
537
|
+
metadata: {
|
|
538
|
+
...(options?.messageOptions?.metadata ?? {}),
|
|
539
|
+
turnId,
|
|
540
|
+
turnSemantics: 'turn_complete',
|
|
541
|
+
turnComplete: true,
|
|
542
|
+
},
|
|
543
|
+
},
|
|
412
544
|
});
|
|
413
545
|
};
|
|
414
546
|
const uploadFile = (filePath, options) => uploadMediaFile(this.apiClient, conversationId, filePath, options);
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
export { CanonAgent } from './canon-agent.js';
|
|
2
|
-
export {
|
|
2
|
+
export type { AgentContactsAPI, AgentUsersAPI } from './canon-agent.js';
|
|
3
|
+
export { CanonApiError, HOST_ADMISSION_ACTION_CAPABILITIES, HOST_ADMISSION_ACTIONS_DISABLED, } from '@canonmsg/core';
|
|
4
|
+
export type { CanonContact, CanonResolveAdmissionResult, ContactAddedPayload, ContactCardPayload, ContactRemovedPayload, ContactSource, HostAdmissionActionCapabilities, ResolvedAdmissionState, ResolvedAdmissionTargetSummary, ResolvedTargetAdmissionPayload, } from '@canonmsg/core';
|
|
3
5
|
export { SessionManager } from './session-manager.js';
|
|
4
6
|
export { getCodexImagePath, getMessageAttachments, inferUploadMimeType, isAnthropicImageAttachment, materializeAttachment, materializeMessageMedia, resolveAttachmentMimeType, toAnthropicImageBlock, uploadMediaFile, } from './media.js';
|
|
5
7
|
export type { AnthropicImageBlock, AnthropicImageMimeType, MaterializeMediaOptions, MaterializedCanonAttachment, ReplyWithFileOptions, UploadMediaFileOptions, } from './media.js';
|
|
6
8
|
export type { SessionConfig, Session } from './session-manager.js';
|
|
7
9
|
export type { AgentContext, CanonContactRequest, CanonMessage, CanonConversation, CanonResolvedWorkSession, CanonWorkSession, CanonWorkSessionContext, CanonWorkSessionConversationRole, CanonWorkSessionDisclosureMode, CanonWorkSessionParticipant, CanonWorkSessionStatus, CreateWorkSessionOptions, SendLinkedMessageOptions, SendLinkedMessageResult, SendMessageOptions, CreateConversationOptions, UpdateWorkSessionConversationOptions, } from '@canonmsg/core';
|
|
8
|
-
export type { SDKMessage, SDKConversation, CanonAgentOptions, ContactRequestHandler, MessageHandler, MessageHandlerContext, ProgressMessageOptions, ProgressMessageResult, SessionInfo, SessionOptions, DeliveryMode, } from './types.js';
|
|
10
|
+
export type { SDKMessage, SDKConversation, CanonAgentOptions, ContactAddedHandler, ContactRemovedHandler, ContactRequestHandler, MessageHandler, MessageHandlerContext, ProgressMessageOptions, ProgressMessageResult, ReachOutOptions, ReachOutResult, SessionInfo, SessionOptions, DeliveryMode, } from './types.js';
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { CanonAgent } from './canon-agent.js';
|
|
2
|
-
export { CanonApiError } from '@canonmsg/core';
|
|
2
|
+
export { CanonApiError, HOST_ADMISSION_ACTION_CAPABILITIES, HOST_ADMISSION_ACTIONS_DISABLED, } from '@canonmsg/core';
|
|
3
3
|
export { SessionManager } from './session-manager.js';
|
|
4
4
|
export { getCodexImagePath, getMessageAttachments, inferUploadMimeType, isAnthropicImageAttachment, materializeAttachment, materializeMessageMedia, resolveAttachmentMimeType, toAnthropicImageBlock, uploadMediaFile, } from './media.js';
|
package/dist/realtime.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type AgentContext, type CanonClient, type ContactApprovedPayload, type ContactRequestPayload } from '@canonmsg/core';
|
|
1
|
+
import { type AgentContext, type CanonClient, type ContactAddedPayload, type ContactApprovedPayload, type ContactRemovedPayload, type ContactRequestPayload } from '@canonmsg/core';
|
|
2
2
|
import { Debouncer } from './debouncer.js';
|
|
3
3
|
/**
|
|
4
4
|
* Wraps @canonmsg/core's CanonStream with SDK-specific features:
|
|
@@ -13,6 +13,8 @@ export declare class RealtimeManager {
|
|
|
13
13
|
private onAgentContext;
|
|
14
14
|
private onContactRequest;
|
|
15
15
|
private onContactApproved;
|
|
16
|
+
private onContactAdded;
|
|
17
|
+
private onContactRemoved;
|
|
16
18
|
private onConnected;
|
|
17
19
|
private onDisconnected;
|
|
18
20
|
constructor(apiKey: string, debouncer: Debouncer, agentId: string, streamUrl?: string, apiClient?: CanonClient);
|
|
@@ -21,6 +23,10 @@ export declare class RealtimeManager {
|
|
|
21
23
|
onContactRequest?: (payload: ContactRequestPayload) => void;
|
|
22
24
|
onContactApproved?: (payload: ContactApprovedPayload) => void;
|
|
23
25
|
}): void;
|
|
26
|
+
setContactGraphHandlers(handlers: {
|
|
27
|
+
onContactAdded?: (payload: ContactAddedPayload) => void;
|
|
28
|
+
onContactRemoved?: (payload: ContactRemovedPayload) => void;
|
|
29
|
+
}): void;
|
|
24
30
|
setConnectionHandlers(handlers: {
|
|
25
31
|
onConnected?: () => void;
|
|
26
32
|
onDisconnected?: () => void;
|
package/dist/realtime.js
CHANGED
|
@@ -12,6 +12,8 @@ export class RealtimeManager {
|
|
|
12
12
|
onAgentContext = null;
|
|
13
13
|
onContactRequest = null;
|
|
14
14
|
onContactApproved = null;
|
|
15
|
+
onContactAdded = null;
|
|
16
|
+
onContactRemoved = null;
|
|
15
17
|
onConnected = null;
|
|
16
18
|
onDisconnected = null;
|
|
17
19
|
constructor(apiKey, debouncer, agentId, streamUrl, apiClient) {
|
|
@@ -59,6 +61,12 @@ export class RealtimeManager {
|
|
|
59
61
|
onContactApproved: (payload) => {
|
|
60
62
|
this.onContactApproved?.(payload);
|
|
61
63
|
},
|
|
64
|
+
onContactAdded: (payload) => {
|
|
65
|
+
this.onContactAdded?.(payload);
|
|
66
|
+
},
|
|
67
|
+
onContactRemoved: (payload) => {
|
|
68
|
+
this.onContactRemoved?.(payload);
|
|
69
|
+
},
|
|
62
70
|
onConnected: () => {
|
|
63
71
|
// Reset backoff is handled internally by CanonStream
|
|
64
72
|
this.onConnected?.();
|
|
@@ -79,6 +87,10 @@ export class RealtimeManager {
|
|
|
79
87
|
this.onContactRequest = handlers.onContactRequest ?? null;
|
|
80
88
|
this.onContactApproved = handlers.onContactApproved ?? null;
|
|
81
89
|
}
|
|
90
|
+
setContactGraphHandlers(handlers) {
|
|
91
|
+
this.onContactAdded = handlers.onContactAdded ?? null;
|
|
92
|
+
this.onContactRemoved = handlers.onContactRemoved ?? null;
|
|
93
|
+
}
|
|
82
94
|
setConnectionHandlers(handlers) {
|
|
83
95
|
this.onConnected = handlers.onConnected ?? null;
|
|
84
96
|
this.onDisconnected = handlers.onDisconnected ?? null;
|
package/dist/types.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export type { AgentClientType, CanonRuntimeDescriptor, CanonMessage, CanonConversation, CanonContactRequest, AgentContext, CanonResolvedWorkSession, CanonWorkSession, CanonWorkSessionContext, CanonWorkSessionConversationRole, CanonWorkSessionDisclosureMode, CanonWorkSessionParticipant, CanonWorkSessionStatus, CreateWorkSessionOptions, SendLinkedMessageOptions, SendLinkedMessageResult, SendMessageOptions, CreateConversationOptions, TurnLifecycleState, UpdateWorkSessionConversationOptions, } from '@canonmsg/core';
|
|
2
|
-
import type { CanonMessage, CanonConversation, CreateWorkSessionOptions, SendMessageOptions, UpdateWorkSessionConversationOptions } from '@canonmsg/core';
|
|
1
|
+
export type { AddMemberResult, AgentClientType, CanonRuntimeDescriptor, CanonMessage, CanonConversation, CanonContact, CanonContactRequest, CanonResolveAdmissionResult, ContactAddedPayload, ContactRemovedPayload, ContactSource, AgentContext, ResolvedAdmissionState, ResolvedAdmissionTargetSummary, ResolvedTargetAdmissionPayload, CanonResolvedWorkSession, CanonWorkSession, CanonWorkSessionContext, CanonWorkSessionConversationRole, CanonWorkSessionDisclosureMode, CanonWorkSessionParticipant, CanonWorkSessionStatus, CreateWorkSessionOptions, SendLinkedMessageOptions, SendLinkedMessageResult, SendMessageOptions, CreateConversationOptions, TurnLifecycleState, UpdateWorkSessionConversationOptions, } from '@canonmsg/core';
|
|
2
|
+
import type { AddMemberResult, CanonMessage, CanonConversation, CreateWorkSessionOptions, SendMessageOptions, UpdateWorkSessionConversationOptions } from '@canonmsg/core';
|
|
3
3
|
import type { MaterializeMediaOptions, MaterializedCanonAttachment, ReplyWithFileOptions, UploadMediaFileOptions } from './media.js';
|
|
4
4
|
export type SDKMessage = CanonMessage;
|
|
5
5
|
export type SDKConversation = CanonConversation;
|
|
@@ -56,8 +56,14 @@ export interface MessageHandlerContext {
|
|
|
56
56
|
leave: () => Promise<void>;
|
|
57
57
|
/** Toggle emoji reaction on a message */
|
|
58
58
|
react: (messageId: string, emoji: string) => Promise<void>;
|
|
59
|
-
/**
|
|
60
|
-
|
|
59
|
+
/**
|
|
60
|
+
* Add a member to this conversation (requires owner/admin role).
|
|
61
|
+
* Returns `{ status: 'added' }` on immediate add, or
|
|
62
|
+
* `{ status: 'pending', requestId }` if the target requires approval —
|
|
63
|
+
* a contact-request (kind: 'group_invite') is created and the join
|
|
64
|
+
* lands when the approver accepts.
|
|
65
|
+
*/
|
|
66
|
+
addMember: (userId: string) => Promise<AddMemberResult>;
|
|
61
67
|
/** Remove a member from this conversation (requires owner/admin role) */
|
|
62
68
|
removeMember: (userId: string) => Promise<void>;
|
|
63
69
|
/** Create a Canon work session rooted in this conversation. */
|
|
@@ -126,3 +132,33 @@ export interface CanonAgentOptions {
|
|
|
126
132
|
sessionState?: boolean;
|
|
127
133
|
}
|
|
128
134
|
export type ContactRequestHandler = (request: import('@canonmsg/core').CanonContactRequest) => void | Promise<void>;
|
|
135
|
+
export type ContactAddedHandler = (contact: import('@canonmsg/core').ContactAddedPayload) => void | Promise<void>;
|
|
136
|
+
export type ContactRemovedHandler = (payload: import('@canonmsg/core').ContactRemovedPayload) => void | Promise<void>;
|
|
137
|
+
/**
|
|
138
|
+
* Result of `agent.reachOut(card)` — describes which side-effect ran so the
|
|
139
|
+
* caller can decide what to tell the LLM. `messaged` means the agent opened
|
|
140
|
+
* (or sent into) a direct conversation; `requested` means the target's
|
|
141
|
+
* inbound policy required a contact request, which has been created;
|
|
142
|
+
* `pending` means a prior outbound request is still awaiting approval; and
|
|
143
|
+
* `blocked` / `unavailable` describe terminal states with `reason` set.
|
|
144
|
+
*/
|
|
145
|
+
export type ReachOutResult = {
|
|
146
|
+
status: 'messaged';
|
|
147
|
+
conversationId: string;
|
|
148
|
+
messageId?: string;
|
|
149
|
+
} | {
|
|
150
|
+
status: 'requested';
|
|
151
|
+
requestId: string | null;
|
|
152
|
+
} | {
|
|
153
|
+
status: 'pending';
|
|
154
|
+
requestId: string | null;
|
|
155
|
+
} | {
|
|
156
|
+
status: 'blocked' | 'unavailable';
|
|
157
|
+
reason: string;
|
|
158
|
+
};
|
|
159
|
+
export interface ReachOutOptions {
|
|
160
|
+
/** Optional first message to send when admission is `allowed`. */
|
|
161
|
+
text?: string;
|
|
162
|
+
/** Optional message to attach to the contact request when admission is `request-required`. */
|
|
163
|
+
requestMessage?: string;
|
|
164
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@canonmsg/agent-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"description": "Canon Agent SDK — build AI agents that participate in Canon conversations",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"node": ">=18.0.0"
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@canonmsg/core": "^0.
|
|
31
|
+
"@canonmsg/core": "^0.12.0"
|
|
32
32
|
},
|
|
33
33
|
"publishConfig": {
|
|
34
34
|
"access": "public"
|