@canonmsg/agent-sdk 1.2.1 → 1.3.1
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 +17 -3
- package/dist/canon-agent.js +340 -21
- package/dist/index.d.ts +2 -2
- package/dist/realtime.d.ts +3 -1
- package/dist/realtime.js +8 -0
- package/dist/types.d.ts +26 -2
- package/package.json +2 -2
package/dist/canon-agent.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { type AddMemberResult, type CanonContact, type ContactCardPayload, type CreateContactRequestResult } from '@canonmsg/core';
|
|
2
|
-
import type { CanonAgentOptions, ContactAddedHandler, ContactRemovedHandler, CreateConversationOptions, MessageHandler, ReachOutOptions, ReachOutResult, ContactRequestHandler, RuntimeSignalHandler } from './types.js';
|
|
1
|
+
import { type AddMemberResult, type CanonContact, type CanonRuntimePrimitiveId, type ContactCardPayload, type CreateContactRequestResult } from '@canonmsg/core';
|
|
2
|
+
import type { CanonAgentOptions, ContactAddedHandler, ContactRemovedHandler, CreateConversationOptions, MessageHandler, ReachOutOptions, ReachOutResult, ContactRequestHandler, RuntimeSignalHandler, RuntimePrimitiveHandler } from './types.js';
|
|
3
3
|
/**
|
|
4
4
|
* Contact-graph operations exposed under `agent.contacts`. Wraps the REST
|
|
5
5
|
* endpoints in CanonClient — the same surface a human user would hit through
|
|
@@ -33,6 +33,8 @@ export declare class CanonAgent {
|
|
|
33
33
|
private interruptHandler;
|
|
34
34
|
private stopAndDropHandler;
|
|
35
35
|
private newSessionHandler;
|
|
36
|
+
private readonly primitiveHandlers;
|
|
37
|
+
private primitiveFallbackHandler;
|
|
36
38
|
/** Contact-graph operations (`agent.contacts.*`). Initialized in the constructor. */
|
|
37
39
|
readonly contacts: AgentContactsAPI;
|
|
38
40
|
/** Block/unblock operations (`agent.users.*`). Initialized in the constructor. */
|
|
@@ -45,7 +47,10 @@ export declare class CanonAgent {
|
|
|
45
47
|
private runtimeHeartbeatTimer;
|
|
46
48
|
private runtimeControlPollTimer;
|
|
47
49
|
private readonly lastSeenSignal;
|
|
50
|
+
private readonly primitiveRequestDedupe;
|
|
48
51
|
private readonly activeAbortControllers;
|
|
52
|
+
private readonly conversationMemberIds;
|
|
53
|
+
private readonly pendingMembershipChanges;
|
|
49
54
|
constructor(options: CanonAgentOptions);
|
|
50
55
|
on(event: 'message', handler: MessageHandler): void;
|
|
51
56
|
on(event: 'contactRequest', handler: ContactRequestHandler): void;
|
|
@@ -55,6 +60,7 @@ export declare class CanonAgent {
|
|
|
55
60
|
on(event: 'interrupt', handler: RuntimeSignalHandler): void;
|
|
56
61
|
on(event: 'stopAndDrop', handler: RuntimeSignalHandler): void;
|
|
57
62
|
on(event: 'newSession', handler: RuntimeSignalHandler): void;
|
|
63
|
+
onPrimitive(primitive: CanonRuntimePrimitiveId | '*', handler: RuntimePrimitiveHandler): void;
|
|
58
64
|
/**
|
|
59
65
|
* Resolve admission live for a target user (typically read off a shared
|
|
60
66
|
* contact card) and route into either an immediate message or a contact
|
|
@@ -98,6 +104,8 @@ export declare class CanonAgent {
|
|
|
98
104
|
private hasStopAndDropSupport;
|
|
99
105
|
private hasNewSessionSupport;
|
|
100
106
|
private hasRuntimeSignalSupport;
|
|
107
|
+
private hasRuntimePrimitiveSupport;
|
|
108
|
+
private hasRuntimeControlSupport;
|
|
101
109
|
private buildRuntimeDescriptor;
|
|
102
110
|
private buildRuntimeCapabilities;
|
|
103
111
|
private publishAgentRuntime;
|
|
@@ -105,10 +113,16 @@ export declare class CanonAgent {
|
|
|
105
113
|
private stopRuntimeHeartbeat;
|
|
106
114
|
private clearAgentRuntime;
|
|
107
115
|
private rememberConversationId;
|
|
116
|
+
private rememberConversationMembers;
|
|
117
|
+
private handleConversationUpdated;
|
|
118
|
+
private buildGroupContext;
|
|
108
119
|
private baselineRuntimeControlSignals;
|
|
109
120
|
private startRuntimeControlPolling;
|
|
110
121
|
private stopRuntimeControlPolling;
|
|
111
|
-
private
|
|
122
|
+
private pollRuntimeControls;
|
|
123
|
+
private handleRuntimePrimitiveRequests;
|
|
124
|
+
private clearRuntimePrimitiveRequest;
|
|
125
|
+
private prunePrimitiveRequestDedupe;
|
|
112
126
|
private handleRuntimeSignal;
|
|
113
127
|
private abortActiveTurns;
|
|
114
128
|
private resolveBatchDeliveryIntent;
|
package/dist/canon-agent.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
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';
|
|
1
|
+
import { CanonClient, buildCanonGroupContext, createRuntimeStatePublisher, diffCanonMemberIds, FINAL_MESSAGE_HANDOFF_MS, RUNTIME_NEW_SESSION_ACTION, RUNTIME_STOP_ACTION, RUNTIME_STOP_AND_DROP_ACTION, initRTDBAuth, rtdbRead, rtdbWrite, normalizeTurnMetadata, reachOutToCanonContact, resolveMessageActiveSelfContextId, selectActiveSelfContexts, } 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 { materializeMessageMedia, sendMediaFileMessage, uploadMediaFile, } from './media.js';
|
|
6
6
|
import { SessionManager } from './session-manager.js';
|
|
7
7
|
const AGENT_RUNTIME_HEARTBEAT_MS = 30_000;
|
|
8
|
+
const RUNTIME_PRIMITIVE_DEDUPE_TTL_MS = 5 * 60 * 1000;
|
|
9
|
+
const RUNTIME_PRIMITIVE_DEDUPE_MAX = 1_000;
|
|
8
10
|
const SDK_RUNTIME_CAPABILITIES = {
|
|
9
11
|
supportsInterrupt: false,
|
|
10
12
|
supportsQueue: true,
|
|
@@ -18,9 +20,133 @@ const DEFAULT_SDK_RUNTIME_DESCRIPTOR = {
|
|
|
18
20
|
supportsInterrupt: false,
|
|
19
21
|
streamingTextMode: 'snapshot',
|
|
20
22
|
};
|
|
23
|
+
const STANDARD_PRIMITIVE_COMMANDS = {
|
|
24
|
+
'runtime.status': {
|
|
25
|
+
id: 'runtime-status',
|
|
26
|
+
label: 'Runtime status',
|
|
27
|
+
description: 'Ask the runtime for its current status.',
|
|
28
|
+
primitive: 'runtime.status',
|
|
29
|
+
aliases: ['status'],
|
|
30
|
+
category: 'runtime',
|
|
31
|
+
placements: ['composer_slash', 'command_palette'],
|
|
32
|
+
availability: ['always'],
|
|
33
|
+
dispatch: { kind: 'primitive', primitive: 'runtime.status' },
|
|
34
|
+
},
|
|
35
|
+
'runtime.reasoning.set': {
|
|
36
|
+
id: 'thinking-level',
|
|
37
|
+
label: 'Thinking level',
|
|
38
|
+
description: 'Set the runtime reasoning or effort level.',
|
|
39
|
+
primitive: 'runtime.reasoning.set',
|
|
40
|
+
aliases: ['think', 'effort'],
|
|
41
|
+
category: 'runtime',
|
|
42
|
+
placements: ['composer_slash', 'command_palette'],
|
|
43
|
+
availability: ['always'],
|
|
44
|
+
args: [{
|
|
45
|
+
id: 'level',
|
|
46
|
+
label: 'Level',
|
|
47
|
+
kind: 'enum',
|
|
48
|
+
required: true,
|
|
49
|
+
choices: [
|
|
50
|
+
{ value: 'low', label: 'Low' },
|
|
51
|
+
{ value: 'medium', label: 'Medium' },
|
|
52
|
+
{ value: 'high', label: 'High' },
|
|
53
|
+
],
|
|
54
|
+
}],
|
|
55
|
+
dispatch: { kind: 'primitive', primitive: 'runtime.reasoning.set' },
|
|
56
|
+
},
|
|
57
|
+
'runtime.verbosity.set': {
|
|
58
|
+
id: 'verbosity',
|
|
59
|
+
label: 'Verbosity',
|
|
60
|
+
description: 'Set runtime verbosity.',
|
|
61
|
+
primitive: 'runtime.verbosity.set',
|
|
62
|
+
aliases: ['verbose'],
|
|
63
|
+
category: 'runtime',
|
|
64
|
+
placements: ['composer_slash', 'command_palette'],
|
|
65
|
+
availability: ['always'],
|
|
66
|
+
args: [{
|
|
67
|
+
id: 'level',
|
|
68
|
+
label: 'Level',
|
|
69
|
+
kind: 'enum',
|
|
70
|
+
required: true,
|
|
71
|
+
choices: [
|
|
72
|
+
{ value: 'off', label: 'Off' },
|
|
73
|
+
{ value: 'on', label: 'On' },
|
|
74
|
+
{ value: 'full', label: 'Full' },
|
|
75
|
+
],
|
|
76
|
+
}],
|
|
77
|
+
dispatch: { kind: 'primitive', primitive: 'runtime.verbosity.set' },
|
|
78
|
+
},
|
|
79
|
+
'runtime.usage': {
|
|
80
|
+
id: 'usage',
|
|
81
|
+
label: 'Usage',
|
|
82
|
+
description: 'Show or change runtime usage reporting.',
|
|
83
|
+
primitive: 'runtime.usage',
|
|
84
|
+
aliases: ['usage'],
|
|
85
|
+
category: 'runtime',
|
|
86
|
+
placements: ['composer_slash', 'command_palette'],
|
|
87
|
+
availability: ['always'],
|
|
88
|
+
dispatch: { kind: 'primitive', primitive: 'runtime.usage' },
|
|
89
|
+
},
|
|
90
|
+
'context.compact': {
|
|
91
|
+
id: 'compact-context',
|
|
92
|
+
label: 'Compact context',
|
|
93
|
+
description: 'Ask the runtime to compact its conversation context.',
|
|
94
|
+
primitive: 'context.compact',
|
|
95
|
+
aliases: ['compact'],
|
|
96
|
+
category: 'session',
|
|
97
|
+
placements: ['composer_slash', 'command_palette'],
|
|
98
|
+
availability: ['always'],
|
|
99
|
+
dispatch: { kind: 'primitive', primitive: 'context.compact' },
|
|
100
|
+
},
|
|
101
|
+
'session.new': {
|
|
102
|
+
id: 'new-session-primitive',
|
|
103
|
+
label: 'New session',
|
|
104
|
+
description: 'Ask the runtime to start a fresh session.',
|
|
105
|
+
primitive: 'session.new',
|
|
106
|
+
aliases: ['new'],
|
|
107
|
+
category: 'session',
|
|
108
|
+
placements: ['composer_slash', 'command_palette', 'session_strip'],
|
|
109
|
+
availability: ['always'],
|
|
110
|
+
dispatch: { kind: 'primitive', primitive: 'session.new' },
|
|
111
|
+
},
|
|
112
|
+
'session.reset': {
|
|
113
|
+
id: 'reset-session',
|
|
114
|
+
label: 'Reset session',
|
|
115
|
+
description: 'Ask the runtime to reset the current session.',
|
|
116
|
+
primitive: 'session.reset',
|
|
117
|
+
aliases: ['reset'],
|
|
118
|
+
category: 'session',
|
|
119
|
+
placements: ['composer_slash', 'command_palette'],
|
|
120
|
+
availability: ['always'],
|
|
121
|
+
dispatch: { kind: 'primitive', primitive: 'session.reset' },
|
|
122
|
+
},
|
|
123
|
+
};
|
|
21
124
|
function sleep(ms) {
|
|
22
125
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
23
126
|
}
|
|
127
|
+
function isRuntimePrimitiveId(value) {
|
|
128
|
+
return value === 'runtime.status'
|
|
129
|
+
|| value === 'runtime.reasoning.set'
|
|
130
|
+
|| value === 'runtime.verbosity.set'
|
|
131
|
+
|| value === 'runtime.usage'
|
|
132
|
+
|| value === 'context.compact'
|
|
133
|
+
|| value === 'session.new'
|
|
134
|
+
|| value === 'session.reset';
|
|
135
|
+
}
|
|
136
|
+
function normalizePrimitiveArgs(value) {
|
|
137
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
138
|
+
return {};
|
|
139
|
+
}
|
|
140
|
+
const args = {};
|
|
141
|
+
for (const [key, rawValue] of Object.entries(value)) {
|
|
142
|
+
if (!/^[A-Za-z0-9_-]+$/.test(key))
|
|
143
|
+
continue;
|
|
144
|
+
if (typeof rawValue === 'boolean' || typeof rawValue === 'string') {
|
|
145
|
+
args[key] = rawValue;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return args;
|
|
149
|
+
}
|
|
24
150
|
export class CanonAgent {
|
|
25
151
|
options;
|
|
26
152
|
apiClient;
|
|
@@ -36,6 +162,8 @@ export class CanonAgent {
|
|
|
36
162
|
interruptHandler = null;
|
|
37
163
|
stopAndDropHandler = null;
|
|
38
164
|
newSessionHandler = null;
|
|
165
|
+
primitiveHandlers = new Map();
|
|
166
|
+
primitiveFallbackHandler = null;
|
|
39
167
|
/** Contact-graph operations (`agent.contacts.*`). Initialized in the constructor. */
|
|
40
168
|
contacts;
|
|
41
169
|
/** Block/unblock operations (`agent.users.*`). Initialized in the constructor. */
|
|
@@ -48,7 +176,10 @@ export class CanonAgent {
|
|
|
48
176
|
runtimeHeartbeatTimer = null;
|
|
49
177
|
runtimeControlPollTimer = null;
|
|
50
178
|
lastSeenSignal = new Map();
|
|
179
|
+
primitiveRequestDedupe = new Map();
|
|
51
180
|
activeAbortControllers = new Map();
|
|
181
|
+
conversationMemberIds = new Map();
|
|
182
|
+
pendingMembershipChanges = new Map();
|
|
52
183
|
constructor(options) {
|
|
53
184
|
this.options = {
|
|
54
185
|
baseUrl: 'https://api-6m6mlelskq-uc.a.run.app',
|
|
@@ -82,6 +213,14 @@ export class CanonAgent {
|
|
|
82
213
|
this.interruptHandler = options.runtimeControls?.onInterrupt ?? null;
|
|
83
214
|
this.stopAndDropHandler = options.runtimeControls?.onStopAndDrop ?? null;
|
|
84
215
|
this.newSessionHandler = options.runtimeControls?.onNewSession ?? null;
|
|
216
|
+
for (const [primitive, handler] of Object.entries(options.runtimePrimitives ?? {})) {
|
|
217
|
+
if (primitive === '*') {
|
|
218
|
+
this.primitiveFallbackHandler = handler;
|
|
219
|
+
}
|
|
220
|
+
else if (isRuntimePrimitiveId(primitive)) {
|
|
221
|
+
this.primitiveHandlers.set(primitive, handler);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
85
224
|
}
|
|
86
225
|
on(event, handler) {
|
|
87
226
|
if (event === 'message') {
|
|
@@ -132,6 +271,18 @@ export class CanonAgent {
|
|
|
132
271
|
}
|
|
133
272
|
this.contactRemovedHandler = handler;
|
|
134
273
|
}
|
|
274
|
+
onPrimitive(primitive, handler) {
|
|
275
|
+
if (primitive === '*') {
|
|
276
|
+
this.primitiveFallbackHandler = handler;
|
|
277
|
+
}
|
|
278
|
+
else {
|
|
279
|
+
this.primitiveHandlers.set(primitive, handler);
|
|
280
|
+
}
|
|
281
|
+
if (this.running) {
|
|
282
|
+
this.startRuntimeControlPolling();
|
|
283
|
+
}
|
|
284
|
+
void this.publishAgentRuntime().catch(() => { });
|
|
285
|
+
}
|
|
135
286
|
/**
|
|
136
287
|
* Resolve admission live for a target user (typically read off a shared
|
|
137
288
|
* contact card) and route into either an immediate message or a contact
|
|
@@ -178,6 +329,7 @@ export class CanonAgent {
|
|
|
178
329
|
status: 'messaged',
|
|
179
330
|
conversationId: result.conversationId,
|
|
180
331
|
messageId: result.messageId,
|
|
332
|
+
selfContextId: result.selfContextId,
|
|
181
333
|
}
|
|
182
334
|
: result;
|
|
183
335
|
}
|
|
@@ -207,6 +359,7 @@ export class CanonAgent {
|
|
|
207
359
|
try {
|
|
208
360
|
conversations = await this.apiClient.getConversations();
|
|
209
361
|
this.cachedConversationIds = conversations.map((c) => c.id);
|
|
362
|
+
this.rememberConversationMembers(conversations);
|
|
210
363
|
}
|
|
211
364
|
catch {
|
|
212
365
|
// Non-fatal — delivery mode will fall back to default
|
|
@@ -265,6 +418,9 @@ export class CanonAgent {
|
|
|
265
418
|
void this.handleContactGraphEvent(this.contactRemovedHandler, payload);
|
|
266
419
|
},
|
|
267
420
|
});
|
|
421
|
+
rtm.setConversationUpdatedHandler((payload) => {
|
|
422
|
+
this.handleConversationUpdated(payload);
|
|
423
|
+
});
|
|
268
424
|
rtm.setConnectionHandlers({
|
|
269
425
|
onConnected: () => this.startRuntimeHeartbeat(),
|
|
270
426
|
onDisconnected: () => this.stopRuntimeHeartbeat(),
|
|
@@ -364,6 +520,12 @@ export class CanonAgent {
|
|
|
364
520
|
hasRuntimeSignalSupport() {
|
|
365
521
|
return this.hasInterruptSupport() || this.hasStopAndDropSupport() || this.hasNewSessionSupport();
|
|
366
522
|
}
|
|
523
|
+
hasRuntimePrimitiveSupport() {
|
|
524
|
+
return this.primitiveHandlers.size > 0 || Boolean(this.primitiveFallbackHandler);
|
|
525
|
+
}
|
|
526
|
+
hasRuntimeControlSupport() {
|
|
527
|
+
return this.hasRuntimeSignalSupport() || this.hasRuntimePrimitiveSupport();
|
|
528
|
+
}
|
|
367
529
|
buildRuntimeDescriptor() {
|
|
368
530
|
const source = this.options.runtimeDescriptor ?? DEFAULT_SDK_RUNTIME_DESCRIPTOR;
|
|
369
531
|
const hasInterrupt = this.hasInterruptSupport();
|
|
@@ -392,9 +554,23 @@ export class CanonAgent {
|
|
|
392
554
|
if (hasNewSession && !hasNewSessionAction) {
|
|
393
555
|
actions.push(RUNTIME_NEW_SESSION_ACTION);
|
|
394
556
|
}
|
|
557
|
+
const commands = [...(source.commands ?? [])].filter((command) => {
|
|
558
|
+
if (command.dispatch.kind !== 'primitive')
|
|
559
|
+
return true;
|
|
560
|
+
return this.primitiveHandlers.has(command.dispatch.primitive)
|
|
561
|
+
|| Boolean(this.primitiveFallbackHandler);
|
|
562
|
+
});
|
|
563
|
+
const hasCommandForPrimitive = (primitive) => commands.some((command) => (command.primitive === primitive
|
|
564
|
+
|| (command.dispatch.kind === 'primitive' && command.dispatch.primitive === primitive)));
|
|
565
|
+
for (const primitive of this.primitiveHandlers.keys()) {
|
|
566
|
+
if (!hasCommandForPrimitive(primitive)) {
|
|
567
|
+
commands.push(STANDARD_PRIMITIVE_COMMANDS[primitive]);
|
|
568
|
+
}
|
|
569
|
+
}
|
|
395
570
|
return {
|
|
396
571
|
...source,
|
|
397
572
|
supportsInterrupt: hasInterrupt,
|
|
573
|
+
commands,
|
|
398
574
|
actions,
|
|
399
575
|
};
|
|
400
576
|
}
|
|
@@ -437,6 +613,41 @@ export class CanonAgent {
|
|
|
437
613
|
return;
|
|
438
614
|
this.cachedConversationIds.push(conversationId);
|
|
439
615
|
}
|
|
616
|
+
rememberConversationMembers(conversations) {
|
|
617
|
+
for (const conversation of conversations) {
|
|
618
|
+
this.conversationMemberIds.set(conversation.id, [...(conversation.memberIds ?? [])]);
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
handleConversationUpdated(payload) {
|
|
622
|
+
const rawMemberIds = payload.changes.memberIds;
|
|
623
|
+
if (!Array.isArray(rawMemberIds))
|
|
624
|
+
return;
|
|
625
|
+
const memberIds = rawMemberIds.filter((id) => typeof id === 'string');
|
|
626
|
+
const hadPreviousMemberIds = this.conversationMemberIds.has(payload.conversationId);
|
|
627
|
+
const previousMemberIds = this.conversationMemberIds.get(payload.conversationId) ?? [];
|
|
628
|
+
const membershipChange = payload.membershipChange
|
|
629
|
+
?? (hadPreviousMemberIds ? diffCanonMemberIds(previousMemberIds, memberIds) : null);
|
|
630
|
+
this.conversationMemberIds.set(payload.conversationId, memberIds);
|
|
631
|
+
if (membershipChange) {
|
|
632
|
+
this.pendingMembershipChanges.set(payload.conversationId, membershipChange);
|
|
633
|
+
}
|
|
634
|
+
if (this.agentId && !memberIds.includes(this.agentId)) {
|
|
635
|
+
this.cachedConversationIds = this.cachedConversationIds.filter((id) => id !== payload.conversationId);
|
|
636
|
+
}
|
|
637
|
+
else {
|
|
638
|
+
this.rememberConversationId(payload.conversationId);
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
buildGroupContext(input) {
|
|
642
|
+
return buildCanonGroupContext({
|
|
643
|
+
conversation: input.conversation,
|
|
644
|
+
messages: [...input.history, ...input.messages],
|
|
645
|
+
agentId: input.agent.agentId,
|
|
646
|
+
ownerId: input.agent.ownerId,
|
|
647
|
+
ownerName: input.agent.ownerName,
|
|
648
|
+
membershipChange: input.membershipChange,
|
|
649
|
+
});
|
|
650
|
+
}
|
|
440
651
|
async baselineRuntimeControlSignals(conversationIds) {
|
|
441
652
|
if (!this.agentId || !this.hasRuntimeSignalSupport())
|
|
442
653
|
return;
|
|
@@ -451,10 +662,10 @@ export class CanonAgent {
|
|
|
451
662
|
}));
|
|
452
663
|
}
|
|
453
664
|
startRuntimeControlPolling() {
|
|
454
|
-
if (!this.agentId || this.runtimeControlPollTimer || !this.
|
|
665
|
+
if (!this.agentId || this.runtimeControlPollTimer || !this.hasRuntimeControlSupport())
|
|
455
666
|
return;
|
|
456
667
|
this.runtimeControlPollTimer = setInterval(() => {
|
|
457
|
-
void this.
|
|
668
|
+
void this.pollRuntimeControls();
|
|
458
669
|
}, 2_000);
|
|
459
670
|
this.runtimeControlPollTimer.unref?.();
|
|
460
671
|
}
|
|
@@ -464,16 +675,97 @@ export class CanonAgent {
|
|
|
464
675
|
clearInterval(this.runtimeControlPollTimer);
|
|
465
676
|
this.runtimeControlPollTimer = null;
|
|
466
677
|
}
|
|
467
|
-
async
|
|
468
|
-
if (!this.agentId || !this.
|
|
678
|
+
async pollRuntimeControls() {
|
|
679
|
+
if (!this.agentId || !this.hasRuntimeControlSupport())
|
|
469
680
|
return;
|
|
470
681
|
await Promise.all(this.cachedConversationIds.map(async (conversationId) => {
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
682
|
+
if (this.hasRuntimeSignalSupport()) {
|
|
683
|
+
const raw = await Promise.resolve(rtdbRead(`/control/${conversationId}/${this.agentId}/signal`)).catch(() => null);
|
|
684
|
+
if (raw && typeof raw === 'object') {
|
|
685
|
+
await this.handleRuntimeSignal(conversationId, raw);
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
if (this.hasRuntimePrimitiveSupport()) {
|
|
689
|
+
const raw = await Promise.resolve(rtdbRead(`/control/${conversationId}/${this.agentId}/primitive`)).catch(() => null);
|
|
690
|
+
if (raw && typeof raw === 'object') {
|
|
691
|
+
await this.handleRuntimePrimitiveRequests(conversationId, raw);
|
|
692
|
+
}
|
|
693
|
+
}
|
|
475
694
|
}));
|
|
476
695
|
}
|
|
696
|
+
async handleRuntimePrimitiveRequests(conversationId, raw) {
|
|
697
|
+
if (!this.agentId)
|
|
698
|
+
return;
|
|
699
|
+
this.prunePrimitiveRequestDedupe();
|
|
700
|
+
const requests = Object.entries(raw)
|
|
701
|
+
.map(([requestId, value]) => ({ requestId, value }))
|
|
702
|
+
.filter((entry) => (Boolean(entry.requestId)
|
|
703
|
+
&& Boolean(entry.value)
|
|
704
|
+
&& typeof entry.value === 'object'
|
|
705
|
+
&& !Array.isArray(entry.value)))
|
|
706
|
+
.sort((a, b) => Number(a.value.updatedAt ?? 0) - Number(b.value.updatedAt ?? 0));
|
|
707
|
+
for (const { requestId, value } of requests) {
|
|
708
|
+
const requestKey = `${conversationId}:${requestId}`;
|
|
709
|
+
if (this.primitiveRequestDedupe.has(requestKey))
|
|
710
|
+
continue;
|
|
711
|
+
this.primitiveRequestDedupe.set(requestKey, Date.now());
|
|
712
|
+
let cleared = false;
|
|
713
|
+
try {
|
|
714
|
+
const primitive = value.id;
|
|
715
|
+
if (!isRuntimePrimitiveId(primitive)) {
|
|
716
|
+
cleared = await this.clearRuntimePrimitiveRequest(conversationId, requestId);
|
|
717
|
+
continue;
|
|
718
|
+
}
|
|
719
|
+
const handler = this.primitiveHandlers.get(primitive) ?? this.primitiveFallbackHandler;
|
|
720
|
+
if (!handler) {
|
|
721
|
+
cleared = await this.clearRuntimePrimitiveRequest(conversationId, requestId);
|
|
722
|
+
continue;
|
|
723
|
+
}
|
|
724
|
+
const args = normalizePrimitiveArgs(value.args);
|
|
725
|
+
await Promise.resolve(handler({
|
|
726
|
+
conversationId,
|
|
727
|
+
primitive,
|
|
728
|
+
args,
|
|
729
|
+
requestId,
|
|
730
|
+
updatedAt: typeof value.updatedAt === 'number' ? value.updatedAt : undefined,
|
|
731
|
+
rawText: typeof value.rawText === 'string' ? value.rawText : undefined,
|
|
732
|
+
alias: typeof value.alias === 'string' ? value.alias : undefined,
|
|
733
|
+
})).catch((error) => {
|
|
734
|
+
console.error(`[canon-sdk] Runtime primitive ${primitive} handler failed for ${conversationId}:`, error);
|
|
735
|
+
});
|
|
736
|
+
cleared = await this.clearRuntimePrimitiveRequest(conversationId, requestId);
|
|
737
|
+
}
|
|
738
|
+
finally {
|
|
739
|
+
if (cleared) {
|
|
740
|
+
this.primitiveRequestDedupe.delete(requestKey);
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
async clearRuntimePrimitiveRequest(conversationId, requestId) {
|
|
746
|
+
if (!this.agentId)
|
|
747
|
+
return false;
|
|
748
|
+
try {
|
|
749
|
+
await Promise.resolve(rtdbWrite(`/control/${conversationId}/${this.agentId}/primitive/${requestId}`, null));
|
|
750
|
+
return true;
|
|
751
|
+
}
|
|
752
|
+
catch {
|
|
753
|
+
return false;
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
prunePrimitiveRequestDedupe(now = Date.now()) {
|
|
757
|
+
for (const [key, timestamp] of this.primitiveRequestDedupe) {
|
|
758
|
+
if (now - timestamp >= RUNTIME_PRIMITIVE_DEDUPE_TTL_MS) {
|
|
759
|
+
this.primitiveRequestDedupe.delete(key);
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
while (this.primitiveRequestDedupe.size > RUNTIME_PRIMITIVE_DEDUPE_MAX) {
|
|
763
|
+
const oldestKey = this.primitiveRequestDedupe.keys().next().value;
|
|
764
|
+
if (!oldestKey)
|
|
765
|
+
break;
|
|
766
|
+
this.primitiveRequestDedupe.delete(oldestKey);
|
|
767
|
+
}
|
|
768
|
+
}
|
|
477
769
|
async handleRuntimeSignal(conversationId, raw) {
|
|
478
770
|
if (!this.agentId)
|
|
479
771
|
return;
|
|
@@ -655,6 +947,7 @@ export class CanonAgent {
|
|
|
655
947
|
}
|
|
656
948
|
// Get conversation info
|
|
657
949
|
const conversations = await this.apiClient.getConversations();
|
|
950
|
+
this.rememberConversationMembers(conversations);
|
|
658
951
|
const conversation = conversations.find((c) => c.id === conversationId);
|
|
659
952
|
if (!conversation)
|
|
660
953
|
return;
|
|
@@ -664,13 +957,11 @@ export class CanonAgent {
|
|
|
664
957
|
await this.apiClient.setTyping(conversationId, true, 'typing');
|
|
665
958
|
}
|
|
666
959
|
catch { }
|
|
960
|
+
const sendOptions = withActiveSelfContext(options);
|
|
667
961
|
const result = await this.apiClient.sendMessage(conversationId, text, {
|
|
668
|
-
...
|
|
669
|
-
...(options?.selfContextId === undefined && activeSelfContextId
|
|
670
|
-
? { selfContextId: activeSelfContextId }
|
|
671
|
-
: {}),
|
|
962
|
+
...sendOptions,
|
|
672
963
|
metadata: {
|
|
673
|
-
...(
|
|
964
|
+
...(sendOptions.metadata ?? {}),
|
|
674
965
|
turnId,
|
|
675
966
|
turnSemantics: 'turn_complete',
|
|
676
967
|
turnComplete: true,
|
|
@@ -689,10 +980,11 @@ export class CanonAgent {
|
|
|
689
980
|
return { turnId, durable: false, messageId: null };
|
|
690
981
|
}
|
|
691
982
|
const { durable: _durable, ...sendOptions } = options;
|
|
983
|
+
const sendOptionsWithContext = withActiveSelfContext(sendOptions);
|
|
692
984
|
const result = await this.apiClient.sendMessage(conversationId, text, {
|
|
693
|
-
...
|
|
985
|
+
...sendOptionsWithContext,
|
|
694
986
|
metadata: {
|
|
695
|
-
...(
|
|
987
|
+
...(sendOptionsWithContext.metadata ?? {}),
|
|
696
988
|
turnId,
|
|
697
989
|
turnSemantics: 'progress',
|
|
698
990
|
turnComplete: false,
|
|
@@ -707,8 +999,19 @@ export class CanonAgent {
|
|
|
707
999
|
m.isOwner = m.senderId === ownerId;
|
|
708
1000
|
}
|
|
709
1001
|
}
|
|
710
|
-
const
|
|
711
|
-
const
|
|
1002
|
+
const latestMessage = hydratedMessages[hydratedMessages.length - 1] ?? null;
|
|
1003
|
+
const resolvedActiveSelfContextId = resolveMessageActiveSelfContextId({
|
|
1004
|
+
messageId: latestMessage?.id,
|
|
1005
|
+
activeSelfContextIdByMessageId: page.activeSelfContextIdByMessageId,
|
|
1006
|
+
});
|
|
1007
|
+
const selfContexts = selectActiveSelfContexts(page.selfContexts, resolvedActiveSelfContextId);
|
|
1008
|
+
const activeSelfContextId = selfContexts.length > 0 ? resolvedActiveSelfContextId : null;
|
|
1009
|
+
const withActiveSelfContext = (options) => {
|
|
1010
|
+
const base = { ...(options ?? {}) };
|
|
1011
|
+
if (base.selfContextId !== undefined)
|
|
1012
|
+
return base;
|
|
1013
|
+
return activeSelfContextId ? { ...base, selfContextId: activeSelfContextId } : base;
|
|
1014
|
+
};
|
|
712
1015
|
// Build agent context (fallback to minimal if not yet received)
|
|
713
1016
|
const agent = this.agentContext ?? {
|
|
714
1017
|
agentId: this.agentId,
|
|
@@ -718,6 +1021,15 @@ export class CanonAgent {
|
|
|
718
1021
|
inboundPolicy: 'approval-required',
|
|
719
1022
|
groupJoinPolicy: 'approval-required',
|
|
720
1023
|
};
|
|
1024
|
+
const membershipChange = this.pendingMembershipChanges.get(conversationId) ?? null;
|
|
1025
|
+
this.pendingMembershipChanges.delete(conversationId);
|
|
1026
|
+
const groupContext = this.buildGroupContext({
|
|
1027
|
+
conversation,
|
|
1028
|
+
history,
|
|
1029
|
+
messages: hydratedMessages,
|
|
1030
|
+
agent,
|
|
1031
|
+
membershipChange,
|
|
1032
|
+
});
|
|
721
1033
|
// Build context methods bound to this conversation
|
|
722
1034
|
const deleteMessage = (messageId) => this.apiClient.deleteMessage(conversationId, messageId);
|
|
723
1035
|
const markAsRead = () => this.apiClient.markAsRead(conversationId);
|
|
@@ -740,6 +1052,10 @@ export class CanonAgent {
|
|
|
740
1052
|
},
|
|
741
1053
|
},
|
|
742
1054
|
});
|
|
1055
|
+
const reachOut = (card, options) => this.reachOut(card, {
|
|
1056
|
+
...(options ?? {}),
|
|
1057
|
+
sourceConversationId: conversationId,
|
|
1058
|
+
});
|
|
743
1059
|
const uploadFile = (filePath, options) => uploadMediaFile(this.apiClient, conversationId, filePath, options);
|
|
744
1060
|
const replyWithFile = async (filePath, text = '', options) => {
|
|
745
1061
|
try {
|
|
@@ -753,9 +1069,9 @@ export class CanonAgent {
|
|
|
753
1069
|
? { replyToPosition: options.replyToPosition }
|
|
754
1070
|
: {}),
|
|
755
1071
|
...(options?.mentions ? { mentions: options.mentions } : {}),
|
|
756
|
-
...(options?.selfContextId
|
|
757
|
-
? { selfContextId:
|
|
758
|
-
:
|
|
1072
|
+
...withActiveSelfContext(options?.selfContextId !== undefined
|
|
1073
|
+
? { selfContextId: options.selfContextId }
|
|
1074
|
+
: undefined),
|
|
759
1075
|
metadata: {
|
|
760
1076
|
...(options?.metadata ?? {}),
|
|
761
1077
|
turnId,
|
|
@@ -782,6 +1098,7 @@ export class CanonAgent {
|
|
|
782
1098
|
history,
|
|
783
1099
|
conversationId,
|
|
784
1100
|
conversation,
|
|
1101
|
+
...(groupContext ? { groupContext } : {}),
|
|
785
1102
|
replyFinal,
|
|
786
1103
|
replyProgress,
|
|
787
1104
|
deleteMessage,
|
|
@@ -791,7 +1108,9 @@ export class CanonAgent {
|
|
|
791
1108
|
addMember,
|
|
792
1109
|
removeMember,
|
|
793
1110
|
sendContextualMessage,
|
|
1111
|
+
reachOut,
|
|
794
1112
|
agent,
|
|
1113
|
+
activeSelfContextId,
|
|
795
1114
|
selfContexts,
|
|
796
1115
|
abortSignal: abortController.signal,
|
|
797
1116
|
media: {
|
package/dist/index.d.ts
CHANGED
|
@@ -6,5 +6,5 @@ export { SessionManager } from './session-manager.js';
|
|
|
6
6
|
export { DEFAULT_MEDIA_CACHE_DIR, getCodexImagePath, getMessageAttachments, inferUploadMimeType, isAnthropicImageAttachment, materializeAttachment, materializeMessageMedia, resolveAttachmentMimeType, sendMediaFileMessage, toAnthropicImageBlock, uploadMediaFile, } from './media.js';
|
|
7
7
|
export type { AnthropicImageBlock, AnthropicImageMimeType, MaterializeMediaOptions, MaterializedCanonAttachment, ReplyWithFileOptions, UploadMediaFileOptions, } from './media.js';
|
|
8
8
|
export type { SessionConfig, Session } from './session-manager.js';
|
|
9
|
-
export type { AgentContext, CanonContactRequest, CanonMessage, CanonConversation, CanonSelfContext, SendContextualMessageOptions, SendContextualMessageResult, SendContextualSelfContextInput, SendMessageOptions, CreateConversationOptions, } from '@canonmsg/core';
|
|
10
|
-
export type { CanonAgentOptions, ContactAddedHandler, ContactRemovedHandler, ContactRequestHandler, MessageHandler, MessageHandlerContext, ProgressMessageOptions, ProgressMessageResult, ReachOutOptions, ReachOutResult, SessionInfo, SessionOptions, DeliveryMode, } from './types.js';
|
|
9
|
+
export type { AgentContext, CanonGroupContext, CanonKnownRecentParticipant, CanonMembershipChange, CanonContactRequest, CanonMessage, CanonConversation, CanonSelfContext, SendContextualMessageOptions, SendContextualMessageResult, SendContextualSelfContextInput, SendMessageOptions, CreateConversationOptions, } from '@canonmsg/core';
|
|
10
|
+
export type { CanonAgentOptions, ContactAddedHandler, ContactRemovedHandler, ContactRequestHandler, MessageHandler, MessageHandlerContext, ProgressMessageOptions, ProgressMessageResult, ReachOutOptions, ReachOutResult, RuntimePrimitiveContext, RuntimePrimitiveHandler, RuntimePrimitiveHandlers, SessionInfo, SessionOptions, DeliveryMode, } from './types.js';
|
package/dist/realtime.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type AgentContext, type CanonClient, type ContactAddedPayload, type ContactApprovedPayload, type ContactRemovedPayload, type ContactRequestPayload } from '@canonmsg/core';
|
|
1
|
+
import { type AgentContext, type CanonClient, type ContactAddedPayload, type ContactApprovedPayload, type ContactRemovedPayload, type ContactRequestPayload, type ConversationUpdatedPayload } from '@canonmsg/core';
|
|
2
2
|
import { Debouncer } from './debouncer.js';
|
|
3
3
|
/**
|
|
4
4
|
* Wraps @canonmsg/core's CanonStream with SDK-specific features:
|
|
@@ -15,6 +15,7 @@ export declare class RealtimeManager {
|
|
|
15
15
|
private onContactApproved;
|
|
16
16
|
private onContactAdded;
|
|
17
17
|
private onContactRemoved;
|
|
18
|
+
private onConversationUpdated;
|
|
18
19
|
private onConnected;
|
|
19
20
|
private onDisconnected;
|
|
20
21
|
constructor(apiKey: string, debouncer: Debouncer, agentId: string, streamUrl?: string, apiClient?: CanonClient);
|
|
@@ -27,6 +28,7 @@ export declare class RealtimeManager {
|
|
|
27
28
|
onContactAdded?: (payload: ContactAddedPayload) => void;
|
|
28
29
|
onContactRemoved?: (payload: ContactRemovedPayload) => void;
|
|
29
30
|
}): void;
|
|
31
|
+
setConversationUpdatedHandler(cb: (payload: ConversationUpdatedPayload) => void): void;
|
|
30
32
|
setConnectionHandlers(handlers: {
|
|
31
33
|
onConnected?: () => void;
|
|
32
34
|
onDisconnected?: () => void;
|
package/dist/realtime.js
CHANGED
|
@@ -14,6 +14,7 @@ export class RealtimeManager {
|
|
|
14
14
|
onContactApproved = null;
|
|
15
15
|
onContactAdded = null;
|
|
16
16
|
onContactRemoved = null;
|
|
17
|
+
onConversationUpdated = null;
|
|
17
18
|
onConnected = null;
|
|
18
19
|
onDisconnected = null;
|
|
19
20
|
constructor(apiKey, debouncer, agentId, streamUrl, apiClient) {
|
|
@@ -30,6 +31,7 @@ export class RealtimeManager {
|
|
|
30
31
|
const message = {
|
|
31
32
|
id: m.id,
|
|
32
33
|
senderId: m.senderId,
|
|
34
|
+
...(m.senderName ? { senderName: m.senderName } : {}),
|
|
33
35
|
senderType: m.senderType ?? 'human',
|
|
34
36
|
isOwner: m.isOwner ?? false,
|
|
35
37
|
contentType: m.contentType ?? 'text',
|
|
@@ -67,6 +69,9 @@ export class RealtimeManager {
|
|
|
67
69
|
onContactRemoved: (payload) => {
|
|
68
70
|
this.onContactRemoved?.(payload);
|
|
69
71
|
},
|
|
72
|
+
onConversationUpdated: (payload) => {
|
|
73
|
+
this.onConversationUpdated?.(payload);
|
|
74
|
+
},
|
|
70
75
|
onConnected: () => {
|
|
71
76
|
// Reset backoff is handled internally by CanonStream
|
|
72
77
|
this.onConnected?.();
|
|
@@ -91,6 +96,9 @@ export class RealtimeManager {
|
|
|
91
96
|
this.onContactAdded = handlers.onContactAdded ?? null;
|
|
92
97
|
this.onContactRemoved = handlers.onContactRemoved ?? null;
|
|
93
98
|
}
|
|
99
|
+
setConversationUpdatedHandler(cb) {
|
|
100
|
+
this.onConversationUpdated = cb;
|
|
101
|
+
}
|
|
94
102
|
setConnectionHandlers(handlers) {
|
|
95
103
|
this.onConnected = handlers.onConnected ?? null;
|
|
96
104
|
this.onDisconnected = handlers.onDisconnected ?? null;
|
package/dist/types.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export type { AddMemberResult, AgentClientType, CanonRuntimeDescriptor, CanonMessage, CanonConversation, CanonContact, CanonContactRequest, CanonResolveAdmissionResult, ContactAddedPayload, ContactRemovedPayload, ContactSource, AgentContext, ResolvedAdmissionState, ResolvedAdmissionTargetSummary, ResolvedTargetAdmissionPayload, CanonSelfContext, SendContextualMessageOptions, SendContextualMessageResult, SendContextualSelfContextInput, SendMessageOptions, SessionConfig, CreateConversationOptions, TurnLifecycleState, } from '@canonmsg/core';
|
|
2
|
-
import type { AddMemberResult, CanonMessage, CanonConversation, CanonRuntimeActionDispatch, SendMessageOptions, SendContextualSelfContextInput, SessionConfig } from '@canonmsg/core';
|
|
1
|
+
export type { AddMemberResult, AgentClientType, CanonGroupContext, CanonRuntimeDescriptor, CanonRuntimePrimitiveId, CanonMessage, CanonConversation, CanonContact, CanonContactRequest, CanonResolveAdmissionResult, ContactAddedPayload, ContactRemovedPayload, ContactSource, AgentContext, ResolvedAdmissionState, ResolvedAdmissionTargetSummary, ResolvedTargetAdmissionPayload, CanonSelfContext, SendContextualMessageOptions, SendContextualMessageResult, SendContextualSelfContextInput, SendMessageOptions, SessionConfig, CreateConversationOptions, TurnLifecycleState, } from '@canonmsg/core';
|
|
2
|
+
import type { AddMemberResult, CanonGroupContext, CanonMessage, CanonConversation, ContactCardPayload, CanonRuntimeActionDispatch, CanonRuntimePrimitiveId, SendMessageOptions, SendContextualSelfContextInput, SessionConfig } from '@canonmsg/core';
|
|
3
3
|
import type { MaterializeMediaOptions, MaterializedCanonAttachment, ReplyWithFileOptions, UploadMediaFileOptions } from './media.js';
|
|
4
4
|
export interface ProgressMessageOptions extends SendMessageOptions {
|
|
5
5
|
/**
|
|
@@ -39,6 +39,8 @@ export interface MessageHandlerContext {
|
|
|
39
39
|
history: CanonMessage[];
|
|
40
40
|
conversationId: string;
|
|
41
41
|
conversation: CanonConversation;
|
|
42
|
+
/** Lightweight group awareness, present for group conversations. */
|
|
43
|
+
groupContext?: CanonGroupContext;
|
|
42
44
|
replyFinal: (text: string, options?: SendMessageOptions) => Promise<{
|
|
43
45
|
messageId: string;
|
|
44
46
|
}>;
|
|
@@ -67,8 +69,12 @@ export interface MessageHandlerContext {
|
|
|
67
69
|
} | {
|
|
68
70
|
targetUserId: string;
|
|
69
71
|
}, text: string, options: Omit<import('@canonmsg/core').SendContextualMessageOptions, 'sourceConversationId' | 'targetConversationId' | 'targetUserId' | 'text'>) => Promise<import('@canonmsg/core').SendContextualMessageResult>;
|
|
72
|
+
/** Reach a contact card from this conversation; contextual reach-outs use this conversation as source. */
|
|
73
|
+
reachOut: (card: ContactCardPayload, options?: Omit<ReachOutOptions, 'sourceConversationId'>) => Promise<ReachOutResult>;
|
|
70
74
|
/** Trusted agent identity & access context */
|
|
71
75
|
agent: import('@canonmsg/core').AgentContext;
|
|
76
|
+
/** Active private self-context to continue for this turn, if Canon supplied one. */
|
|
77
|
+
activeSelfContextId: string | null;
|
|
72
78
|
/** Canon-provided private context explaining this agent's cross-session actions. */
|
|
73
79
|
selfContexts?: import('@canonmsg/core').CanonSelfContext[];
|
|
74
80
|
/** Canon-managed local media access for the current conversation. */
|
|
@@ -117,6 +123,19 @@ export interface RuntimeControlHandlers {
|
|
|
117
123
|
onStopAndDrop?: RuntimeSignalHandler;
|
|
118
124
|
onNewSession?: RuntimeSignalHandler;
|
|
119
125
|
}
|
|
126
|
+
export interface RuntimePrimitiveContext {
|
|
127
|
+
conversationId: string;
|
|
128
|
+
primitive: CanonRuntimePrimitiveId;
|
|
129
|
+
args: Record<string, string | boolean>;
|
|
130
|
+
rawText?: string;
|
|
131
|
+
alias?: string;
|
|
132
|
+
requestId?: string;
|
|
133
|
+
updatedAt?: number;
|
|
134
|
+
}
|
|
135
|
+
export type RuntimePrimitiveHandler = (context: RuntimePrimitiveContext) => void | Promise<void>;
|
|
136
|
+
export type RuntimePrimitiveHandlers = Partial<Record<CanonRuntimePrimitiveId, RuntimePrimitiveHandler>> & {
|
|
137
|
+
'*'?: RuntimePrimitiveHandler;
|
|
138
|
+
};
|
|
120
139
|
export interface CanonAgentOptions {
|
|
121
140
|
apiKey: string;
|
|
122
141
|
baseUrl?: string;
|
|
@@ -135,6 +154,8 @@ export interface CanonAgentOptions {
|
|
|
135
154
|
runtimeDescriptor?: import('@canonmsg/core').CanonRuntimeDescriptor;
|
|
136
155
|
/** Optional Canon runtime signal handlers. Enables interrupt controls when provided. */
|
|
137
156
|
runtimeControls?: RuntimeControlHandlers;
|
|
157
|
+
/** Optional typed runtime primitive handlers. Enables descriptor-backed command controls when provided. */
|
|
158
|
+
runtimePrimitives?: RuntimePrimitiveHandlers;
|
|
138
159
|
/**
|
|
139
160
|
* Enable RTDB session-state reporting. Off by default.
|
|
140
161
|
* Turn-state reporting is automatic while handlers run.
|
|
@@ -156,12 +177,15 @@ export type ReachOutResult = {
|
|
|
156
177
|
status: 'messaged';
|
|
157
178
|
conversationId: string;
|
|
158
179
|
messageId?: string;
|
|
180
|
+
selfContextId?: string;
|
|
159
181
|
} | {
|
|
160
182
|
status: 'requested';
|
|
161
183
|
requestId: string | null;
|
|
184
|
+
deferredIntentId?: string | null;
|
|
162
185
|
} | {
|
|
163
186
|
status: 'pending';
|
|
164
187
|
requestId: string | null;
|
|
188
|
+
deferredIntentId?: string | null;
|
|
165
189
|
} | {
|
|
166
190
|
status: 'setup_required';
|
|
167
191
|
reason: string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@canonmsg/agent-sdk",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.1",
|
|
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.17.2"
|
|
32
32
|
},
|
|
33
33
|
"publishConfig": {
|
|
34
34
|
"access": "public"
|