@canonmsg/agent-sdk 0.4.0 → 0.6.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/dist/canon-agent.d.ts +5 -0
- package/dist/canon-agent.js +70 -4
- package/dist/index.d.ts +1 -1
- package/dist/polling.d.ts +3 -1
- package/dist/polling.js +8 -1
- package/dist/realtime.d.ts +6 -0
- package/dist/realtime.js +11 -0
- package/dist/types.d.ts +14 -2
- package/package.json +2 -2
package/dist/canon-agent.d.ts
CHANGED
|
@@ -12,6 +12,7 @@ export declare class CanonAgent {
|
|
|
12
12
|
private agentContext;
|
|
13
13
|
private cachedConversationIds;
|
|
14
14
|
private running;
|
|
15
|
+
private runtimeHeartbeatTimer;
|
|
15
16
|
constructor(options: CanonAgentOptions);
|
|
16
17
|
on(event: 'message', handler: MessageHandler): void;
|
|
17
18
|
start(): Promise<void>;
|
|
@@ -28,6 +29,10 @@ export declare class CanonAgent {
|
|
|
28
29
|
attachment: import('@canonmsg/core').MediaAttachment;
|
|
29
30
|
}>;
|
|
30
31
|
stop(): Promise<void>;
|
|
32
|
+
private publishAgentRuntime;
|
|
33
|
+
private startRuntimeHeartbeat;
|
|
34
|
+
private stopRuntimeHeartbeat;
|
|
35
|
+
private clearAgentRuntime;
|
|
31
36
|
private handleMessages;
|
|
32
37
|
private executeHandler;
|
|
33
38
|
static register(options: {
|
package/dist/canon-agent.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { CanonClient, FINAL_MESSAGE_HANDOFF_MS, initRTDBAuth, writeSessionState, clearSessionState, writeTurnState, clearTurnState, } from '@canonmsg/core';
|
|
1
|
+
import { CanonClient, FINAL_MESSAGE_HANDOFF_MS, initRTDBAuth, mergeWorkSessionContexts, rtdbWrite, writeSessionState, clearSessionState, writeTurnState, clearTurnState, } from '@canonmsg/core';
|
|
2
2
|
import { randomUUID } from 'node:crypto';
|
|
3
3
|
import { AuthManager } from './auth.js';
|
|
4
4
|
import { Debouncer } from './debouncer.js';
|
|
5
5
|
import { PollingManager } from './polling.js';
|
|
6
6
|
import { SessionManager } from './session-manager.js';
|
|
7
7
|
const AUTO_MODE_THRESHOLD = 500;
|
|
8
|
+
const AGENT_RUNTIME_HEARTBEAT_MS = 30_000;
|
|
8
9
|
const SDK_RUNTIME_CAPABILITIES = {
|
|
9
10
|
supportsInterrupt: false,
|
|
10
11
|
supportsQueue: true,
|
|
@@ -28,6 +29,7 @@ export class CanonAgent {
|
|
|
28
29
|
agentContext = null;
|
|
29
30
|
cachedConversationIds = [];
|
|
30
31
|
running = false;
|
|
32
|
+
runtimeHeartbeatTimer = null;
|
|
31
33
|
constructor(options) {
|
|
32
34
|
this.options = {
|
|
33
35
|
baseUrl: 'https://api-6m6mlelskq-uc.a.run.app',
|
|
@@ -109,12 +111,16 @@ export class CanonAgent {
|
|
|
109
111
|
rtm.setOnAgentContext((ctx) => {
|
|
110
112
|
this.agentContext = ctx;
|
|
111
113
|
});
|
|
114
|
+
rtm.setConnectionHandlers({
|
|
115
|
+
onConnected: () => this.startRuntimeHeartbeat(),
|
|
116
|
+
onDisconnected: () => this.stopRuntimeHeartbeat(),
|
|
117
|
+
});
|
|
112
118
|
this.realtimeManager = rtm;
|
|
113
119
|
await rtm.start();
|
|
114
120
|
console.log('[canon-sdk] SSE stream started');
|
|
115
121
|
}
|
|
116
122
|
else {
|
|
117
|
-
this.pollingManager = new PollingManager(this.apiClient, this.debouncer, agentId, this.options.pollingIntervalMs);
|
|
123
|
+
this.pollingManager = new PollingManager(this.apiClient, this.debouncer, agentId, this.options.pollingIntervalMs, () => this.startRuntimeHeartbeat(), () => this.stopRuntimeHeartbeat());
|
|
118
124
|
await this.pollingManager.start();
|
|
119
125
|
console.log(`[canon-sdk] Polling started (interval: ${this.options.pollingIntervalMs}ms)`);
|
|
120
126
|
}
|
|
@@ -155,6 +161,7 @@ export class CanonAgent {
|
|
|
155
161
|
clearTurnState(id, this.agentId).catch(() => { });
|
|
156
162
|
}
|
|
157
163
|
}
|
|
164
|
+
await this.clearAgentRuntime();
|
|
158
165
|
this.pollingManager?.stop();
|
|
159
166
|
this.realtimeManager?.stop();
|
|
160
167
|
this.sessionManager?.destroy();
|
|
@@ -162,6 +169,36 @@ export class CanonAgent {
|
|
|
162
169
|
this.debouncer.destroy();
|
|
163
170
|
console.log('[canon-sdk] Stopped');
|
|
164
171
|
}
|
|
172
|
+
async publishAgentRuntime() {
|
|
173
|
+
if (!this.agentId)
|
|
174
|
+
return;
|
|
175
|
+
await rtdbWrite(`/agent-runtime/${this.agentId}`, {
|
|
176
|
+
clientType: this.options.clientType ?? 'generic',
|
|
177
|
+
hostMode: false,
|
|
178
|
+
updatedAt: { '.sv': 'timestamp' },
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
startRuntimeHeartbeat() {
|
|
182
|
+
void this.publishAgentRuntime();
|
|
183
|
+
if (this.runtimeHeartbeatTimer)
|
|
184
|
+
return;
|
|
185
|
+
this.runtimeHeartbeatTimer = setInterval(() => {
|
|
186
|
+
void this.publishAgentRuntime();
|
|
187
|
+
}, AGENT_RUNTIME_HEARTBEAT_MS);
|
|
188
|
+
this.runtimeHeartbeatTimer.unref?.();
|
|
189
|
+
}
|
|
190
|
+
stopRuntimeHeartbeat() {
|
|
191
|
+
if (this.runtimeHeartbeatTimer) {
|
|
192
|
+
clearInterval(this.runtimeHeartbeatTimer);
|
|
193
|
+
this.runtimeHeartbeatTimer = null;
|
|
194
|
+
}
|
|
195
|
+
void this.clearAgentRuntime();
|
|
196
|
+
}
|
|
197
|
+
async clearAgentRuntime() {
|
|
198
|
+
if (!this.agentId)
|
|
199
|
+
return;
|
|
200
|
+
await rtdbWrite(`/agent-runtime/${this.agentId}`, null).catch(() => { });
|
|
201
|
+
}
|
|
165
202
|
async handleMessages(conversationId, messages) {
|
|
166
203
|
if (!this.handler) {
|
|
167
204
|
console.warn(`[canon-sdk] No message handler registered — messages for ${conversationId} dropped. Call agent.on('message', handler) before starting.`);
|
|
@@ -237,8 +274,9 @@ export class CanonAgent {
|
|
|
237
274
|
}
|
|
238
275
|
}, 3500);
|
|
239
276
|
try {
|
|
240
|
-
// Fetch history from API
|
|
241
|
-
const
|
|
277
|
+
// Fetch hydrated history/context from API
|
|
278
|
+
const page = await this.apiClient.getMessagesPage(conversationId, this.options.historyLimit);
|
|
279
|
+
const history = page.messages;
|
|
242
280
|
// If sessions enabled, seed the session with fetched history
|
|
243
281
|
if (this.sessionManager && session) {
|
|
244
282
|
this.sessionManager.seedHistory(conversationId, history);
|
|
@@ -294,6 +332,11 @@ export class CanonAgent {
|
|
|
294
332
|
m.isOwner = m.senderId === ownerId;
|
|
295
333
|
}
|
|
296
334
|
}
|
|
335
|
+
const explicitWorkSession = messages.find((message) => message.workSession)?.workSession
|
|
336
|
+
?? history.find((message) => message.workSession)?.workSession
|
|
337
|
+
?? null;
|
|
338
|
+
const activeWorkSessions = mergeWorkSessionContexts(explicitWorkSession, page.workSessions ?? []);
|
|
339
|
+
const workSession = explicitWorkSession;
|
|
297
340
|
// Build agent context (fallback to minimal if not yet received)
|
|
298
341
|
const agent = this.agentContext ?? {
|
|
299
342
|
agentId: this.agentId,
|
|
@@ -308,6 +351,23 @@ export class CanonAgent {
|
|
|
308
351
|
const react = (messageId, emoji) => this.apiClient.react(conversationId, messageId, emoji);
|
|
309
352
|
const addMember = (userId) => this.apiClient.addMember(conversationId, userId);
|
|
310
353
|
const removeMember = (userId) => this.apiClient.removeMember(conversationId, userId);
|
|
354
|
+
const createWorkSession = (options) => this.apiClient.createWorkSession({
|
|
355
|
+
conversationId,
|
|
356
|
+
...(options ?? {}),
|
|
357
|
+
});
|
|
358
|
+
const getWorkSession = (workSessionId, targetConversationId = conversationId) => this.apiClient.getWorkSession(workSessionId, targetConversationId);
|
|
359
|
+
const updateWorkSessionContext = (workSessionId, options) => this.apiClient.upsertWorkSessionConversation(workSessionId, conversationId, options);
|
|
360
|
+
const sendLinkedMessage = (targetConversationId, text, options) => {
|
|
361
|
+
if (!options?.workSessionId && !options?.createWorkSession) {
|
|
362
|
+
throw new Error('sendLinkedMessage requires workSessionId or createWorkSession');
|
|
363
|
+
}
|
|
364
|
+
return this.apiClient.sendLinkedMessage({
|
|
365
|
+
sourceConversationId: conversationId,
|
|
366
|
+
targetConversationId,
|
|
367
|
+
text,
|
|
368
|
+
...options,
|
|
369
|
+
});
|
|
370
|
+
};
|
|
311
371
|
// Invoke handler
|
|
312
372
|
await this.handler({
|
|
313
373
|
messages,
|
|
@@ -323,7 +383,13 @@ export class CanonAgent {
|
|
|
323
383
|
react,
|
|
324
384
|
addMember,
|
|
325
385
|
removeMember,
|
|
386
|
+
createWorkSession,
|
|
387
|
+
getWorkSession,
|
|
388
|
+
updateWorkSessionContext,
|
|
389
|
+
sendLinkedMessage,
|
|
326
390
|
agent,
|
|
391
|
+
workSession,
|
|
392
|
+
activeWorkSessions,
|
|
327
393
|
session: session
|
|
328
394
|
? {
|
|
329
395
|
id: session.id,
|
package/dist/index.d.ts
CHANGED
|
@@ -2,5 +2,5 @@ export { CanonAgent } from './canon-agent.js';
|
|
|
2
2
|
export { CanonApiError } from '@canonmsg/core';
|
|
3
3
|
export { SessionManager } from './session-manager.js';
|
|
4
4
|
export type { SessionConfig, Session } from './session-manager.js';
|
|
5
|
-
export type { AgentContext, CanonMessage, CanonConversation, SendMessageOptions, CreateConversationOptions, } from '@canonmsg/core';
|
|
5
|
+
export type { AgentContext, CanonMessage, CanonConversation, CanonResolvedWorkSession, CanonWorkSession, CanonWorkSessionContext, CanonWorkSessionConversationRole, CanonWorkSessionDisclosureMode, CanonWorkSessionParticipant, CanonWorkSessionStatus, CreateWorkSessionOptions, SendLinkedMessageOptions, SendLinkedMessageResult, SendMessageOptions, CreateConversationOptions, UpdateWorkSessionConversationOptions, } from '@canonmsg/core';
|
|
6
6
|
export type { SDKMessage, SDKConversation, CanonAgentOptions, MessageHandler, MessageHandlerContext, ProgressMessageOptions, ProgressMessageResult, SessionInfo, SessionOptions, DeliveryMode, } from './types.js';
|
package/dist/polling.d.ts
CHANGED
|
@@ -5,10 +5,12 @@ export declare class PollingManager {
|
|
|
5
5
|
private debouncer;
|
|
6
6
|
private agentId;
|
|
7
7
|
private pollingIntervalMs;
|
|
8
|
+
private onHealthy;
|
|
9
|
+
private onUnhealthy;
|
|
8
10
|
private lastSeenTimestamps;
|
|
9
11
|
private pollTimer;
|
|
10
12
|
private running;
|
|
11
|
-
constructor(apiClient: CanonClient, debouncer: Debouncer, agentId: string, pollingIntervalMs: number);
|
|
13
|
+
constructor(apiClient: CanonClient, debouncer: Debouncer, agentId: string, pollingIntervalMs: number, onHealthy?: () => void, onUnhealthy?: () => void);
|
|
12
14
|
start(): Promise<void>;
|
|
13
15
|
private poll;
|
|
14
16
|
private findActiveConversations;
|
package/dist/polling.js
CHANGED
|
@@ -5,14 +5,18 @@ export class PollingManager {
|
|
|
5
5
|
debouncer;
|
|
6
6
|
agentId;
|
|
7
7
|
pollingIntervalMs;
|
|
8
|
+
onHealthy;
|
|
9
|
+
onUnhealthy;
|
|
8
10
|
lastSeenTimestamps = new Map();
|
|
9
11
|
pollTimer = null;
|
|
10
12
|
running = false;
|
|
11
|
-
constructor(apiClient, debouncer, agentId, pollingIntervalMs) {
|
|
13
|
+
constructor(apiClient, debouncer, agentId, pollingIntervalMs, onHealthy, onUnhealthy) {
|
|
12
14
|
this.apiClient = apiClient;
|
|
13
15
|
this.debouncer = debouncer;
|
|
14
16
|
this.agentId = agentId;
|
|
15
17
|
this.pollingIntervalMs = pollingIntervalMs;
|
|
18
|
+
this.onHealthy = onHealthy ?? null;
|
|
19
|
+
this.onUnhealthy = onUnhealthy ?? null;
|
|
16
20
|
}
|
|
17
21
|
async start() {
|
|
18
22
|
this.running = true;
|
|
@@ -22,6 +26,7 @@ export class PollingManager {
|
|
|
22
26
|
for (const convo of conversations) {
|
|
23
27
|
this.lastSeenTimestamps.set(convo.id, now);
|
|
24
28
|
}
|
|
29
|
+
this.onHealthy?.();
|
|
25
30
|
// Start polling
|
|
26
31
|
this.pollTimer = setInterval(() => this.poll(), this.pollingIntervalMs);
|
|
27
32
|
}
|
|
@@ -30,6 +35,7 @@ export class PollingManager {
|
|
|
30
35
|
return;
|
|
31
36
|
try {
|
|
32
37
|
const conversations = await this.apiClient.getConversations();
|
|
38
|
+
this.onHealthy?.();
|
|
33
39
|
const activeConvos = this.findActiveConversations(conversations);
|
|
34
40
|
await Promise.all(activeConvos.map(async (convo) => {
|
|
35
41
|
try {
|
|
@@ -67,6 +73,7 @@ export class PollingManager {
|
|
|
67
73
|
}));
|
|
68
74
|
}
|
|
69
75
|
catch (err) {
|
|
76
|
+
this.onUnhealthy?.();
|
|
70
77
|
console.error('[canon-sdk] Polling error:', err);
|
|
71
78
|
}
|
|
72
79
|
}
|
package/dist/realtime.d.ts
CHANGED
|
@@ -15,8 +15,14 @@ export declare class RealtimeManager {
|
|
|
15
15
|
private knownConversationIds;
|
|
16
16
|
private discoveryTimer;
|
|
17
17
|
private onAgentContext;
|
|
18
|
+
private onConnected;
|
|
19
|
+
private onDisconnected;
|
|
18
20
|
constructor(apiKey: string, debouncer: Debouncer, agentId: string, streamUrl?: string, apiClient?: CanonClient);
|
|
19
21
|
setOnAgentContext(cb: (ctx: AgentContext) => void): void;
|
|
22
|
+
setConnectionHandlers(handlers: {
|
|
23
|
+
onConnected?: () => void;
|
|
24
|
+
onDisconnected?: () => void;
|
|
25
|
+
}): void;
|
|
20
26
|
start(): Promise<void>;
|
|
21
27
|
stop(): void;
|
|
22
28
|
private discoverNewConversations;
|
package/dist/realtime.js
CHANGED
|
@@ -17,6 +17,8 @@ export class RealtimeManager {
|
|
|
17
17
|
knownConversationIds = new Set();
|
|
18
18
|
discoveryTimer = null;
|
|
19
19
|
onAgentContext = null;
|
|
20
|
+
onConnected = null;
|
|
21
|
+
onDisconnected = null;
|
|
20
22
|
constructor(apiKey, debouncer, agentId, streamUrl, apiClient) {
|
|
21
23
|
this.debouncer = debouncer;
|
|
22
24
|
this.agentId = agentId;
|
|
@@ -54,6 +56,10 @@ export class RealtimeManager {
|
|
|
54
56
|
},
|
|
55
57
|
onConnected: () => {
|
|
56
58
|
// Reset backoff is handled internally by CanonStream
|
|
59
|
+
this.onConnected?.();
|
|
60
|
+
},
|
|
61
|
+
onDisconnected: () => {
|
|
62
|
+
this.onDisconnected?.();
|
|
57
63
|
},
|
|
58
64
|
onError: (err) => {
|
|
59
65
|
console.error('[canon-sdk] SSE error:', err.message);
|
|
@@ -64,6 +70,10 @@ export class RealtimeManager {
|
|
|
64
70
|
setOnAgentContext(cb) {
|
|
65
71
|
this.onAgentContext = cb;
|
|
66
72
|
}
|
|
73
|
+
setConnectionHandlers(handlers) {
|
|
74
|
+
this.onConnected = handlers.onConnected ?? null;
|
|
75
|
+
this.onDisconnected = handlers.onDisconnected ?? null;
|
|
76
|
+
}
|
|
67
77
|
async start() {
|
|
68
78
|
this.running = true;
|
|
69
79
|
// Snapshot current conversations
|
|
@@ -89,6 +99,7 @@ export class RealtimeManager {
|
|
|
89
99
|
this.discoveryTimer = null;
|
|
90
100
|
}
|
|
91
101
|
this.stream.stop();
|
|
102
|
+
this.onDisconnected?.();
|
|
92
103
|
}
|
|
93
104
|
// ── Conversation discovery ─────────────────────────────────────────
|
|
94
105
|
async discoverNewConversations() {
|
package/dist/types.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export type { AgentClientType, CanonMessage, CanonConversation, AgentContext, SendMessageOptions, CreateConversationOptions, TurnLifecycleState, } from '@canonmsg/core';
|
|
2
|
-
import type { CanonMessage, CanonConversation, SendMessageOptions } from '@canonmsg/core';
|
|
1
|
+
export type { AgentClientType, CanonMessage, CanonConversation, 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';
|
|
3
3
|
export type SDKMessage = CanonMessage;
|
|
4
4
|
export type SDKConversation = CanonConversation;
|
|
5
5
|
export interface ProgressMessageOptions extends SendMessageOptions {
|
|
@@ -59,8 +59,20 @@ export interface MessageHandlerContext {
|
|
|
59
59
|
addMember: (userId: string) => Promise<void>;
|
|
60
60
|
/** Remove a member from this conversation (requires owner/admin role) */
|
|
61
61
|
removeMember: (userId: string) => Promise<void>;
|
|
62
|
+
/** Create a Canon work session rooted in this conversation. */
|
|
63
|
+
createWorkSession: (options?: Omit<CreateWorkSessionOptions, 'conversationId'>) => Promise<import('@canonmsg/core').CanonResolvedWorkSession>;
|
|
64
|
+
/** Load this conversation's scoped view of a Canon work session. */
|
|
65
|
+
getWorkSession: (workSessionId: string, conversationId?: string) => Promise<import('@canonmsg/core').CanonResolvedWorkSession>;
|
|
66
|
+
/** Update or attach this conversation's scoped work-session context. */
|
|
67
|
+
updateWorkSessionContext: (workSessionId: string, options?: UpdateWorkSessionConversationOptions) => Promise<import('@canonmsg/core').CanonResolvedWorkSession>;
|
|
68
|
+
/** Send into another conversation under an existing or lazily created Canon work session. */
|
|
69
|
+
sendLinkedMessage: (targetConversationId: string, text: string, options?: Omit<import('@canonmsg/core').SendLinkedMessageOptions, 'sourceConversationId' | 'targetConversationId' | 'text'>) => Promise<import('@canonmsg/core').SendLinkedMessageResult>;
|
|
62
70
|
/** Trusted agent identity & access context */
|
|
63
71
|
agent: import('@canonmsg/core').AgentContext;
|
|
72
|
+
/** Canon-provided shared task context for this turn, when attached to inbound messages. */
|
|
73
|
+
workSession?: import('@canonmsg/core').CanonWorkSessionContext | null;
|
|
74
|
+
/** All active Canon work sessions currently linked to this conversation. */
|
|
75
|
+
activeWorkSessions?: import('@canonmsg/core').CanonWorkSessionContext[];
|
|
64
76
|
/** Per-conversation session state. Present when sessions are enabled. */
|
|
65
77
|
session?: SessionInfo;
|
|
66
78
|
/** Turn lifecycle helpers for live-work rendering and progress reporting. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@canonmsg/agent-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.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",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"node": ">=18.0.0"
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"@canonmsg/core": "^0.
|
|
26
|
+
"@canonmsg/core": "^0.6.0"
|
|
27
27
|
},
|
|
28
28
|
"publishConfig": {
|
|
29
29
|
"access": "public"
|