@canonmsg/agent-sdk 1.1.4 → 1.2.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 +3 -0
- package/dist/canon-agent.js +37 -31
- package/dist/session-manager.d.ts +2 -0
- package/dist/session-manager.js +8 -0
- package/dist/types.d.ts +1 -0
- package/package.json +2 -2
package/dist/canon-agent.d.ts
CHANGED
|
@@ -32,6 +32,7 @@ export declare class CanonAgent {
|
|
|
32
32
|
private contactRemovedHandler;
|
|
33
33
|
private interruptHandler;
|
|
34
34
|
private stopAndDropHandler;
|
|
35
|
+
private newSessionHandler;
|
|
35
36
|
/** Contact-graph operations (`agent.contacts.*`). Initialized in the constructor. */
|
|
36
37
|
readonly contacts: AgentContactsAPI;
|
|
37
38
|
/** Block/unblock operations (`agent.users.*`). Initialized in the constructor. */
|
|
@@ -53,6 +54,7 @@ export declare class CanonAgent {
|
|
|
53
54
|
on(event: 'contactRemoved', handler: ContactRemovedHandler): void;
|
|
54
55
|
on(event: 'interrupt', handler: RuntimeSignalHandler): void;
|
|
55
56
|
on(event: 'stopAndDrop', handler: RuntimeSignalHandler): void;
|
|
57
|
+
on(event: 'newSession', handler: RuntimeSignalHandler): void;
|
|
56
58
|
/**
|
|
57
59
|
* Resolve admission live for a target user (typically read off a shared
|
|
58
60
|
* contact card) and route into either an immediate message or a contact
|
|
@@ -94,6 +96,7 @@ export declare class CanonAgent {
|
|
|
94
96
|
stop(): Promise<void>;
|
|
95
97
|
private hasInterruptSupport;
|
|
96
98
|
private hasStopAndDropSupport;
|
|
99
|
+
private hasNewSessionSupport;
|
|
97
100
|
private hasRuntimeSignalSupport;
|
|
98
101
|
private buildRuntimeDescriptor;
|
|
99
102
|
private buildRuntimeCapabilities;
|
package/dist/canon-agent.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CanonClient, createRuntimeStatePublisher, FINAL_MESSAGE_HANDOFF_MS, initRTDBAuth, rtdbRead, rtdbWrite, normalizeTurnMetadata, reachOutToCanonContact, } from '@canonmsg/core';
|
|
1
|
+
import { CanonClient, createRuntimeStatePublisher, FINAL_MESSAGE_HANDOFF_MS, RUNTIME_NEW_SESSION_ACTION, RUNTIME_STOP_ACTION, RUNTIME_STOP_AND_DROP_ACTION, initRTDBAuth, rtdbRead, rtdbWrite, normalizeTurnMetadata, reachOutToCanonContact, } from '@canonmsg/core';
|
|
2
2
|
import { randomUUID } from 'node:crypto';
|
|
3
3
|
import { AuthManager } from './auth.js';
|
|
4
4
|
import { Debouncer } from './debouncer.js';
|
|
@@ -18,26 +18,6 @@ const DEFAULT_SDK_RUNTIME_DESCRIPTOR = {
|
|
|
18
18
|
supportsInterrupt: false,
|
|
19
19
|
streamingTextMode: 'snapshot',
|
|
20
20
|
};
|
|
21
|
-
const SDK_STOP_ACTION = {
|
|
22
|
-
id: 'stop',
|
|
23
|
-
label: 'Stop',
|
|
24
|
-
description: 'Interrupt the current SDK agent turn.',
|
|
25
|
-
aliases: ['stop'],
|
|
26
|
-
category: 'turn',
|
|
27
|
-
placements: ['composer_slash', 'command_palette'],
|
|
28
|
-
availability: ['busy'],
|
|
29
|
-
dispatch: { kind: 'signal', signal: 'interrupt' },
|
|
30
|
-
};
|
|
31
|
-
const SDK_STOP_AND_DROP_ACTION = {
|
|
32
|
-
id: 'stop-and-clear-queue',
|
|
33
|
-
label: 'Stop & clear queue',
|
|
34
|
-
description: 'Interrupt the current SDK agent turn and drop queued Canon messages.',
|
|
35
|
-
aliases: ['stop-clear', 'clear-queue'],
|
|
36
|
-
category: 'turn',
|
|
37
|
-
placements: ['composer_slash', 'command_palette', 'session_strip'],
|
|
38
|
-
availability: ['busy_with_queue'],
|
|
39
|
-
dispatch: { kind: 'signal', signal: 'stop_and_drop' },
|
|
40
|
-
};
|
|
41
21
|
function sleep(ms) {
|
|
42
22
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
43
23
|
}
|
|
@@ -55,6 +35,7 @@ export class CanonAgent {
|
|
|
55
35
|
contactRemovedHandler = null;
|
|
56
36
|
interruptHandler = null;
|
|
57
37
|
stopAndDropHandler = null;
|
|
38
|
+
newSessionHandler = null;
|
|
58
39
|
/** Contact-graph operations (`agent.contacts.*`). Initialized in the constructor. */
|
|
59
40
|
contacts;
|
|
60
41
|
/** Block/unblock operations (`agent.users.*`). Initialized in the constructor. */
|
|
@@ -100,6 +81,7 @@ export class CanonAgent {
|
|
|
100
81
|
}
|
|
101
82
|
this.interruptHandler = options.runtimeControls?.onInterrupt ?? null;
|
|
102
83
|
this.stopAndDropHandler = options.runtimeControls?.onStopAndDrop ?? null;
|
|
84
|
+
this.newSessionHandler = options.runtimeControls?.onNewSession ?? null;
|
|
103
85
|
}
|
|
104
86
|
on(event, handler) {
|
|
105
87
|
if (event === 'message') {
|
|
@@ -138,6 +120,16 @@ export class CanonAgent {
|
|
|
138
120
|
void this.publishAgentRuntime().catch(() => { });
|
|
139
121
|
return;
|
|
140
122
|
}
|
|
123
|
+
if (event === 'newSession') {
|
|
124
|
+
this.newSessionHandler = handler;
|
|
125
|
+
if (this.running) {
|
|
126
|
+
void this.baselineRuntimeControlSignals(this.cachedConversationIds)
|
|
127
|
+
.then(() => this.startRuntimeControlPolling())
|
|
128
|
+
.catch(() => { });
|
|
129
|
+
}
|
|
130
|
+
void this.publishAgentRuntime().catch(() => { });
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
141
133
|
this.contactRemovedHandler = handler;
|
|
142
134
|
}
|
|
143
135
|
/**
|
|
@@ -366,13 +358,17 @@ export class CanonAgent {
|
|
|
366
358
|
hasStopAndDropSupport() {
|
|
367
359
|
return Boolean(this.stopAndDropHandler);
|
|
368
360
|
}
|
|
361
|
+
hasNewSessionSupport() {
|
|
362
|
+
return Boolean(this.newSessionHandler);
|
|
363
|
+
}
|
|
369
364
|
hasRuntimeSignalSupport() {
|
|
370
|
-
return this.hasInterruptSupport() || this.hasStopAndDropSupport();
|
|
365
|
+
return this.hasInterruptSupport() || this.hasStopAndDropSupport() || this.hasNewSessionSupport();
|
|
371
366
|
}
|
|
372
367
|
buildRuntimeDescriptor() {
|
|
373
368
|
const source = this.options.runtimeDescriptor ?? DEFAULT_SDK_RUNTIME_DESCRIPTOR;
|
|
374
369
|
const hasInterrupt = this.hasInterruptSupport();
|
|
375
370
|
const hasStopAndDrop = this.hasStopAndDropSupport();
|
|
371
|
+
const hasNewSession = this.hasNewSessionSupport();
|
|
376
372
|
const actions = [...(source.actions ?? [])].filter((action) => {
|
|
377
373
|
if (action.dispatch.kind !== 'signal')
|
|
378
374
|
return true;
|
|
@@ -380,15 +376,21 @@ export class CanonAgent {
|
|
|
380
376
|
return hasInterrupt;
|
|
381
377
|
if (action.dispatch.signal === 'stop_and_drop')
|
|
382
378
|
return hasStopAndDrop;
|
|
379
|
+
if (action.dispatch.signal === 'new_session')
|
|
380
|
+
return hasNewSession;
|
|
383
381
|
return false;
|
|
384
382
|
});
|
|
385
383
|
const hasInterruptAction = actions.some((action) => action.dispatch.kind === 'signal' && action.dispatch.signal === 'interrupt');
|
|
386
384
|
const hasStopAndDropAction = actions.some((action) => action.dispatch.kind === 'signal' && action.dispatch.signal === 'stop_and_drop');
|
|
385
|
+
const hasNewSessionAction = actions.some((action) => action.dispatch.kind === 'signal' && action.dispatch.signal === 'new_session');
|
|
387
386
|
if (hasInterrupt && !hasInterruptAction) {
|
|
388
|
-
actions.push(
|
|
387
|
+
actions.push(RUNTIME_STOP_ACTION);
|
|
389
388
|
}
|
|
390
389
|
if (hasStopAndDrop && this.sessionManager && !hasStopAndDropAction) {
|
|
391
|
-
actions.push(
|
|
390
|
+
actions.push(RUNTIME_STOP_AND_DROP_ACTION);
|
|
391
|
+
}
|
|
392
|
+
if (hasNewSession && !hasNewSessionAction) {
|
|
393
|
+
actions.push(RUNTIME_NEW_SESSION_ACTION);
|
|
392
394
|
}
|
|
393
395
|
return {
|
|
394
396
|
...source,
|
|
@@ -476,23 +478,27 @@ export class CanonAgent {
|
|
|
476
478
|
if (!this.agentId)
|
|
477
479
|
return;
|
|
478
480
|
const signal = raw.type;
|
|
479
|
-
if (signal !== 'interrupt' && signal !== 'stop_and_drop')
|
|
481
|
+
if (signal !== 'interrupt' && signal !== 'stop_and_drop' && signal !== 'new_session')
|
|
480
482
|
return;
|
|
481
483
|
const timestamp = Number(raw.updatedAt ?? 0);
|
|
482
484
|
if (timestamp <= (this.lastSeenSignal.get(conversationId) ?? 0))
|
|
483
485
|
return;
|
|
484
486
|
this.lastSeenSignal.set(conversationId, timestamp);
|
|
485
|
-
const handler = signal === '
|
|
486
|
-
? this.
|
|
487
|
-
:
|
|
487
|
+
const handler = signal === 'new_session'
|
|
488
|
+
? this.newSessionHandler
|
|
489
|
+
: signal === 'stop_and_drop'
|
|
490
|
+
? this.stopAndDropHandler
|
|
491
|
+
: this.interruptHandler;
|
|
488
492
|
if (!handler) {
|
|
489
493
|
await Promise.resolve(rtdbWrite(`/control/${conversationId}/${this.agentId}/signal`, null)).catch(() => { });
|
|
490
494
|
return;
|
|
491
495
|
}
|
|
492
496
|
const abortSignal = this.abortActiveTurns(conversationId);
|
|
493
|
-
const droppedMessages = signal === '
|
|
494
|
-
? this.sessionManager?.
|
|
495
|
-
:
|
|
497
|
+
const droppedMessages = signal === 'new_session'
|
|
498
|
+
? this.sessionManager?.resetSession(conversationId) ?? []
|
|
499
|
+
: signal === 'stop_and_drop'
|
|
500
|
+
? this.sessionManager?.dropQueued(conversationId) ?? []
|
|
501
|
+
: [];
|
|
496
502
|
const droppedMessageIds = droppedMessages.map((message) => message.id);
|
|
497
503
|
await Promise.all(droppedMessages.map((message) => {
|
|
498
504
|
if (message.metadata?.inboundDisposition !== 'queued')
|
|
@@ -65,6 +65,8 @@ export declare class SessionManager {
|
|
|
65
65
|
getQueueDepth(conversationId: string): number;
|
|
66
66
|
/** Drop queued, not-yet-running batches for a conversation. */
|
|
67
67
|
dropQueued(conversationId: string): CanonMessage[];
|
|
68
|
+
/** Drop queued work and clear retained context for a conversation. */
|
|
69
|
+
resetSession(conversationId: string): CanonMessage[];
|
|
68
70
|
/** Clean up all state */
|
|
69
71
|
destroy(): void;
|
|
70
72
|
}
|
package/dist/session-manager.js
CHANGED
|
@@ -213,6 +213,14 @@ export class SessionManager {
|
|
|
213
213
|
}
|
|
214
214
|
return droppedMessages;
|
|
215
215
|
}
|
|
216
|
+
/** Drop queued work and clear retained context for a conversation. */
|
|
217
|
+
resetSession(conversationId) {
|
|
218
|
+
const droppedMessages = this.dropQueued(conversationId);
|
|
219
|
+
this.sessions.delete(conversationId);
|
|
220
|
+
this.seenMessages.delete(conversationId);
|
|
221
|
+
this.seededSessions.delete(conversationId);
|
|
222
|
+
return droppedMessages;
|
|
223
|
+
}
|
|
216
224
|
/** Clean up all state */
|
|
217
225
|
destroy() {
|
|
218
226
|
if (this.sweepTimer) {
|
package/dist/types.d.ts
CHANGED
|
@@ -115,6 +115,7 @@ export type RuntimeSignalHandler = (context: RuntimeSignalContext) => void | Pro
|
|
|
115
115
|
export interface RuntimeControlHandlers {
|
|
116
116
|
onInterrupt?: RuntimeSignalHandler;
|
|
117
117
|
onStopAndDrop?: RuntimeSignalHandler;
|
|
118
|
+
onNewSession?: RuntimeSignalHandler;
|
|
118
119
|
}
|
|
119
120
|
export interface CanonAgentOptions {
|
|
120
121
|
apiKey: string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@canonmsg/agent-sdk",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.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.16.0"
|
|
32
32
|
},
|
|
33
33
|
"publishConfig": {
|
|
34
34
|
"access": "public"
|