@canonmsg/agent-sdk 8.9.0 → 9.0.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 +4 -33
- package/dist/canon-agent.d.ts +7 -22
- package/dist/canon-agent.js +11 -223
- package/dist/index.d.ts +2 -2
- package/dist/realtime.d.ts +1 -5
- package/dist/realtime.js +0 -8
- package/dist/types.d.ts +4 -83
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -53,29 +53,12 @@ The only runtime dependency is `@canonmsg/core`, which npm installs for you. Eve
|
|
|
53
53
|
| `sessions` | `SessionOptions` | `undefined` | Enable per-conversation session queues and persistent metadata |
|
|
54
54
|
| `clientType` | `AgentClientType` | `'generic'` | Agent runtime label used for Canon capability detection |
|
|
55
55
|
| `runtimeDescriptor` | `CanonRuntimeDescriptor` | minimal generic descriptor | Optional setup/live controls and runtime capability metadata for Canon UI |
|
|
56
|
-
| `ownerBoundCommunication` | `{ enabled: true, lifecycleStore? }` | `undefined` | Opt into owner-foreground `ctx.reachOut`, truthful reach-out capabilities, and terminal contact lifecycle context. Supply a durable store when the SDK host must retain events across process restarts. |
|
|
57
56
|
| `runtimeControls` | `RuntimeControlHandlers` | `undefined` | Optional `onInterrupt` / `onStopAndDrop` / `onNewSession` handlers for Canon working-state controls |
|
|
58
57
|
| `runtimeControlSurface` | `'agent' \| 'host'` | `'agent'` | Runtime publishing surface. Use `host` when this SDK agent owns live runtime controls. |
|
|
59
58
|
| `runtimePrimitives` | `RuntimePrimitiveHandlers` | `undefined` | Optional typed primitive command handlers for descriptor-backed runtime commands |
|
|
60
59
|
| `sessionState` | `boolean` | `false` | Publish runtime-applied state to the canonical agent-session snapshot |
|
|
61
60
|
| `turnVerbosity` | `'verbose' \| 'quiet' \| 'auto'` or `{ direct?, group? }` | `'auto'` | How much of a turn's middle readers see. See [Turn verbosity](#turn-verbosity). |
|
|
62
61
|
|
|
63
|
-
### Owner-bound agent introductions
|
|
64
|
-
|
|
65
|
-
Enable `ownerBoundCommunication` only when your handler is the model-facing
|
|
66
|
-
foreground surface. In that mode, `ctx.reachOut` requires an owner-authored
|
|
67
|
-
source message and visible text, binds replies to the replied contact card, and
|
|
68
|
-
rejects model-selected session setup or hidden self-context. Programmatic
|
|
69
|
-
`agent.reachOut(...)` remains available for application-controlled workflows.
|
|
70
|
-
|
|
71
|
-
Terminal outbound contact-request phases are deduplicated and exposed through
|
|
72
|
-
`ctx.contactLifecycleEvents` on the next natural owner turn in the source
|
|
73
|
-
conversation. The store records an initial baseline without replaying historical
|
|
74
|
-
outcomes; persist its baseline, dedupe keys, and pending rows when restart
|
|
75
|
-
recovery matters. The default store is process-local. Lifecycle events never
|
|
76
|
-
invoke the message handler on their own, and `connected` means Canon has already
|
|
77
|
-
delivered the opener.
|
|
78
|
-
|
|
79
62
|
### Optional runtime controls
|
|
80
63
|
|
|
81
64
|
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:
|
|
@@ -207,8 +190,7 @@ The `message` event handler receives a context object with:
|
|
|
207
190
|
| `leave` | `() => Promise<void>` | Leave the current group conversation |
|
|
208
191
|
| `react` | `(messageId, emoji) => Promise<void>` | Toggle an emoji reaction |
|
|
209
192
|
| `addMember` / `removeMember` | functions | Manage group members when the agent has permission |
|
|
210
|
-
| `
|
|
211
|
-
| `reachOut` | function | Act on a Canon contact card using live admission resolution |
|
|
193
|
+
| `communicate` | function | Message an existing conversation, start a direct conversation, create a group, or forward an exact message |
|
|
212
194
|
| `agent` | `AgentContext` | Trusted Canon agent identity and access context |
|
|
213
195
|
| `activeSelfContextId` | `string \| null` | Active private self-context id for this turn |
|
|
214
196
|
| `selfContexts` | `CanonSelfContext[] \| undefined` | Private context explaining this agent's cross-session actions |
|
|
@@ -307,18 +289,9 @@ agent.on('contactRequest', (request) => {
|
|
|
307
289
|
agent.on('contactApproved', (request) => {
|
|
308
290
|
console.log('Request approved:', request.id);
|
|
309
291
|
});
|
|
310
|
-
|
|
311
|
-
agent.on('contactRequestUpdated', (request) => {
|
|
312
|
-
console.log('Outbound request phase:', request.phase);
|
|
313
|
-
});
|
|
314
292
|
```
|
|
315
293
|
|
|
316
|
-
These are awareness callbacks only. Canon still routes approval
|
|
317
|
-
and any coding-session setup for agent-targeted requests through the human
|
|
318
|
-
owner's UI/callable flow. Outbound phases intentionally hide delivery internals:
|
|
319
|
-
`awaiting_owner`, `starting`, `connected`, `rejected`, `expired`, `cancelled`,
|
|
320
|
-
or `failed`. A connected update includes the conversation id and means the
|
|
321
|
-
parked opener was already delivered; do not send it again.
|
|
294
|
+
These are awareness callbacks only. Canon still routes approval and rejection for agent-targeted requests through the human owner's UI/callable flow.
|
|
322
295
|
|
|
323
296
|
### Turn-aware example
|
|
324
297
|
|
|
@@ -361,8 +334,6 @@ await agent.contacts.list(); // CanonContact[]
|
|
|
361
334
|
await agent.contacts.get(contactId); // CanonContact | null
|
|
362
335
|
await agent.contacts.remove(contactId);
|
|
363
336
|
await agent.contacts.request(targetUserId, 'why I am reaching out');
|
|
364
|
-
await agent.contacts.listRequests({ direction: 'outbound', includeResolved: true });
|
|
365
|
-
await agent.contacts.cancelRequest(requestId);
|
|
366
337
|
|
|
367
338
|
await agent.users.block(userId);
|
|
368
339
|
await agent.users.unblock(userId);
|
|
@@ -536,7 +507,7 @@ A conversation whose type Canon could not determine falls back to verbose, never
|
|
|
536
507
|
|
|
537
508
|
**Quiet suppresses**: every `/streaming` publication — the `'Thinking...'` seed and its keepalive, `turn.setThinking/setStreaming/setTool`, `turn.appendDelta`/`appendBlock`/segment updates, `turn.addBlock` and friends, and the live half of `replyProgress()` — plus the `turnTrail` on `replyFinal()` and `media.replyWithFile()`. Every one of those calls still works and still returns normally; only the publication is dropped.
|
|
538
509
|
|
|
539
|
-
**Quiet does not suppress**: the typing/thinking indicator (which stays up for the turn's whole working phase; while the turn is parked on an approval the clients suppress an agent's dots and the header line carries the state), turn state, `replyFinal()` including every part of a chunked reply, the partial-final notice, `media.replyWithFile()` itself, `turn.setWaitingInput()`'s note, `
|
|
510
|
+
**Quiet does not suppress**: the typing/thinking indicator (which stays up for the turn's whole working phase; while the turn is parked on an approval the clients suppress an agent's dots and the header line carries the state), turn state, `replyFinal()` including every part of a chunked reply, the partial-final notice, `media.replyWithFile()` itself, `turn.setWaitingInput()`'s note, `communicate()`, `publishRuntimeActivity()`, approval/input/card requests, and their outcome receipts.
|
|
540
511
|
|
|
541
512
|
**`replyProgress(text, { durable: true })` still posts.** Quiet removes narration the runtime generates on its own; a `durable: true` call is your explicit decision to put a message in the conversation, the same kind of act as `replyFinal()`. Its implicit live-preview half is dropped, the durable send is not, and the returned `durable` flag always describes what actually happened.
|
|
542
513
|
|
|
@@ -561,4 +532,4 @@ Canon caps a single message at 4 KB of UTF-8 text, and rejects anything longer o
|
|
|
561
532
|
Every other send path passes your text through as-is, so text over the cap still fails there. Notably:
|
|
562
533
|
|
|
563
534
|
- `media.replyWithFile(path, caption)` — the caption rides along with an attachment that cannot be duplicated across parts. Keep captions short and send long prose as a separate `replyFinal()`.
|
|
564
|
-
- `
|
|
535
|
+
- `communicate()` — a distinct compact cross-conversation operation; keep each message under the cap.
|
package/dist/canon-agent.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { type AddMemberResult, type CanonContact, type
|
|
2
|
-
import type { CanonAgentConnectionOptions, CanonAgentOptions, ContactAddedHandler, ContactRemovedHandler,
|
|
1
|
+
import { type AddMemberResult, type CanonContact, type CommunicateInput, type CommunicateResult, type CanonConversation, type CanonConversationsPage, type CanonConversationsPageOptions, type CreateGroupOptions, type CreateGroupResult, type CanonRuntimeActivityItem, type CanonRuntimeCommandDescriptor, type CanonRuntimeFact, type CanonRuntimePrimitiveId, type ClearRuntimeActivityOptions, type CanonVoiceSession, type CanonVoiceSessionToken, type CreateVoiceSessionOptions, type VoiceSessionEventPayload } from '@canonmsg/core';
|
|
2
|
+
import type { CanonAgentConnectionOptions, CanonAgentOptions, ContactAddedHandler, ContactRemovedHandler, MessageHandler, MessageUpdatedHandler, ParticipationSuppressedHandler, 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
|
|
@@ -9,12 +9,6 @@ export interface AgentContactsAPI {
|
|
|
9
9
|
list(): Promise<CanonContact[]>;
|
|
10
10
|
get(contactId: string): Promise<CanonContact | null>;
|
|
11
11
|
remove(contactId: string): Promise<void>;
|
|
12
|
-
request(targetUserId: string, message?: string | null): Promise<CreateContactRequestResult>;
|
|
13
|
-
listRequests(options?: ContactRequestListOptions): Promise<CanonContactRequest[]>;
|
|
14
|
-
cancelRequest(requestId: string): Promise<{
|
|
15
|
-
status: 'cancelled';
|
|
16
|
-
requestId: string;
|
|
17
|
-
}>;
|
|
18
12
|
}
|
|
19
13
|
/**
|
|
20
14
|
* User-level moderation actions exposed under `agent.users`.
|
|
@@ -47,7 +41,6 @@ export declare class CanonAgent {
|
|
|
47
41
|
private sessionManager;
|
|
48
42
|
private handler;
|
|
49
43
|
private contactRequestHandler;
|
|
50
|
-
private contactRequestUpdatedHandler;
|
|
51
44
|
private contactApprovedHandler;
|
|
52
45
|
private contactAddedHandler;
|
|
53
46
|
private contactRemovedHandler;
|
|
@@ -68,8 +61,6 @@ export declare class CanonAgent {
|
|
|
68
61
|
readonly users: AgentUsersAPI;
|
|
69
62
|
/** Conversation discovery for choosing existing sessions intentionally. */
|
|
70
63
|
readonly conversations: AgentConversationsAPI;
|
|
71
|
-
private readonly reachOutInFlight;
|
|
72
|
-
private readonly contactLifecycleStore;
|
|
73
64
|
private agentId;
|
|
74
65
|
private agentContext;
|
|
75
66
|
private approvalManager;
|
|
@@ -111,7 +102,6 @@ export declare class CanonAgent {
|
|
|
111
102
|
on(event: 'message', handler: MessageHandler): void;
|
|
112
103
|
on(event: 'messageUpdated', handler: MessageUpdatedHandler): void;
|
|
113
104
|
on(event: 'contactRequest', handler: ContactRequestHandler): void;
|
|
114
|
-
on(event: 'contactRequestUpdated', handler: ContactRequestUpdatedHandler): void;
|
|
115
105
|
on(event: 'contactApproved', handler: ContactRequestHandler): void;
|
|
116
106
|
on(event: 'contactAdded', handler: ContactAddedHandler): void;
|
|
117
107
|
on(event: 'contactRemoved', handler: ContactRemovedHandler): void;
|
|
@@ -135,16 +125,13 @@ export declare class CanonAgent {
|
|
|
135
125
|
publishRuntimeActivity(conversationId: string, item: CanonRuntimeActivityItem): Promise<void>;
|
|
136
126
|
clearRuntimeActivity(conversationId: string, options?: ClearRuntimeActivityOptions): Promise<void>;
|
|
137
127
|
/**
|
|
138
|
-
*
|
|
139
|
-
*
|
|
140
|
-
*
|
|
141
|
-
* time an LLM acts on it. Instead defers to `resolveAdmission` so the
|
|
142
|
-
* answer reflects the target's *current* inbound policy.
|
|
128
|
+
* Message an existing Canon conversation or start/continue a direct one.
|
|
129
|
+
* Policy and admission are enforced by Canon; this method deliberately
|
|
130
|
+
* exposes no runtime configuration or trusted source-context fields.
|
|
143
131
|
*/
|
|
144
|
-
|
|
145
|
-
private executeReachOut;
|
|
132
|
+
communicate(input: CommunicateInput): Promise<CommunicateResult>;
|
|
146
133
|
start(): Promise<void>;
|
|
147
|
-
|
|
134
|
+
createGroup(options: CreateGroupOptions): Promise<CreateGroupResult>;
|
|
148
135
|
/** Start (or rejoin) a call in a conversation and get the room token. */
|
|
149
136
|
startCall(options: CreateVoiceSessionOptions): Promise<CanonVoiceSessionToken>;
|
|
150
137
|
/** Join an active call session and get the room token. */
|
|
@@ -179,8 +166,6 @@ export declare class CanonAgent {
|
|
|
179
166
|
attachment: import('@canonmsg/core').MediaAttachment;
|
|
180
167
|
}>;
|
|
181
168
|
private handleContactRequestEvent;
|
|
182
|
-
private recordAndHandleContactLifecycleEvent;
|
|
183
|
-
private reconcileContactLifecycleInbox;
|
|
184
169
|
private handleContactGraphEvent;
|
|
185
170
|
private handleParticipationSuppressedEvent;
|
|
186
171
|
private handleMessageUpdatedEvent;
|
package/dist/canon-agent.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ApprovalManager, RuntimeRequestManager, runtimeInputDescriptor, runtimeCardDescriptor, CanonClient, ControlChannelPoller, buildCanonTurnContextV2, buildCanonGroupContext, buildParticipationHistorySnapshot, createTurnOutputController, createRuntimeStatePublisher, createTypingStatusPublisher, diffCanonMemberIds,
|
|
1
|
+
import { ApprovalManager, RuntimeRequestManager, runtimeInputDescriptor, runtimeCardDescriptor, CanonClient, ControlChannelPoller, buildCanonTurnContextV2, buildCanonGroupContext, buildParticipationHistorySnapshot, createTurnOutputController, createRuntimeStatePublisher, createTypingStatusPublisher, diffCanonMemberIds, FINAL_MESSAGE_HANDOFF_MS, RUNTIME_NEW_SESSION_ACTION, RUNTIME_STOP_ACTION, RUNTIME_STOP_AND_DROP_ACTION, buildRuntimeCardOutcome, buildRuntimeInputOutcome, initRTDBAuth, isChunkedSendMessageError, normalizeRuntimeCommandDescriptors, normalizeTurnMetadata, normalizeTurnVerbosityConversationType, reportNoReplyOutcome, resolveCanonReplyContext, resolveMessageActiveSelfContextId, resolveRuntimeProvenance, resolveTurnVerbosity, selectActiveSelfContexts, renderCanonHostInboundContent, resolveCanonRuntimeConnection, sendMessageWithRetryChunked, shouldPublishTurnTrail, splitTextByUtf8Bytes, verifyCanonRuntimeConnection, } from '@canonmsg/core';
|
|
2
2
|
import { createHash, randomUUID } from 'node:crypto';
|
|
3
3
|
import { AuthManager } from './auth.js';
|
|
4
4
|
import { Debouncer } from './debouncer.js';
|
|
@@ -15,34 +15,6 @@ const SDK_MESSAGE_ID_READABLE_MAX = 120;
|
|
|
15
15
|
/** Canon's message id ceiling, matching core's chunked sender. */
|
|
16
16
|
const CANON_MESSAGE_ID_MAX = 160;
|
|
17
17
|
const SDK_PARTIAL_FINAL_NOTICE = 'This reply stops short because Canon could not deliver the remaining text.';
|
|
18
|
-
class InMemoryContactLifecycleStore {
|
|
19
|
-
keys = new Set();
|
|
20
|
-
pending = [];
|
|
21
|
-
baselineComplete = false;
|
|
22
|
-
record(request, options = {}) {
|
|
23
|
-
const key = contactRequestLifecycleEventKey(request);
|
|
24
|
-
if (!key || this.keys.has(key))
|
|
25
|
-
return false;
|
|
26
|
-
this.keys.add(key);
|
|
27
|
-
if (options.pending !== false)
|
|
28
|
-
this.pending.push(request);
|
|
29
|
-
while (this.keys.size > 512)
|
|
30
|
-
this.keys.delete(this.keys.values().next().value);
|
|
31
|
-
this.pending = this.pending.slice(-100);
|
|
32
|
-
return true;
|
|
33
|
-
}
|
|
34
|
-
take(sourceConversationId) {
|
|
35
|
-
const taken = this.pending.filter((request) => request.sourceConversationId === sourceConversationId);
|
|
36
|
-
this.pending = this.pending.filter((request) => request.sourceConversationId !== sourceConversationId);
|
|
37
|
-
return taken;
|
|
38
|
-
}
|
|
39
|
-
isBaselineComplete() {
|
|
40
|
-
return this.baselineComplete;
|
|
41
|
-
}
|
|
42
|
-
completeBaseline() {
|
|
43
|
-
this.baselineComplete = true;
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
18
|
const SDK_RUNTIME_CAPABILITIES = {
|
|
47
19
|
supportsInterrupt: false,
|
|
48
20
|
supportsInputInterrupt: false,
|
|
@@ -283,7 +255,6 @@ export class CanonAgent {
|
|
|
283
255
|
sessionManager = null;
|
|
284
256
|
handler = null;
|
|
285
257
|
contactRequestHandler = null;
|
|
286
|
-
contactRequestUpdatedHandler = null;
|
|
287
258
|
contactApprovedHandler = null;
|
|
288
259
|
contactAddedHandler = null;
|
|
289
260
|
contactRemovedHandler = null;
|
|
@@ -304,8 +275,6 @@ export class CanonAgent {
|
|
|
304
275
|
users;
|
|
305
276
|
/** Conversation discovery for choosing existing sessions intentionally. */
|
|
306
277
|
conversations;
|
|
307
|
-
reachOutInFlight = new Map();
|
|
308
|
-
contactLifecycleStore;
|
|
309
278
|
agentId = null;
|
|
310
279
|
agentContext = null;
|
|
311
280
|
approvalManager = null;
|
|
@@ -352,9 +321,6 @@ export class CanonAgent {
|
|
|
352
321
|
rtdbUrl: this.runtimeConnection.rtdbUrl,
|
|
353
322
|
firebaseApiKey: this.runtimeConnection.firebaseWebApiKey,
|
|
354
323
|
};
|
|
355
|
-
this.contactLifecycleStore = options.ownerBoundCommunication?.enabled
|
|
356
|
-
? options.ownerBoundCommunication.lifecycleStore ?? new InMemoryContactLifecycleStore()
|
|
357
|
-
: null;
|
|
358
324
|
this.apiClient = new CanonClient(this.options.apiKey, this.options.baseUrl);
|
|
359
325
|
this.typingSignals = createTypingStatusPublisher({
|
|
360
326
|
setTyping: (conversationId, typing, status) => status
|
|
@@ -368,9 +334,6 @@ export class CanonAgent {
|
|
|
368
334
|
list: () => apiClient.listContacts(),
|
|
369
335
|
get: (contactId) => apiClient.getContact(contactId),
|
|
370
336
|
remove: (contactId) => apiClient.deleteContact(contactId),
|
|
371
|
-
request: (targetUserId, message) => apiClient.createContactRequest(targetUserId, message ?? null),
|
|
372
|
-
listRequests: (options) => apiClient.listContactRequests(options),
|
|
373
|
-
cancelRequest: (requestId) => apiClient.cancelContactRequest(requestId),
|
|
374
337
|
};
|
|
375
338
|
this.users = {
|
|
376
339
|
block: (userId) => apiClient.blockUser(userId),
|
|
@@ -498,10 +461,6 @@ export class CanonAgent {
|
|
|
498
461
|
this.contactRequestHandler = handler;
|
|
499
462
|
return;
|
|
500
463
|
}
|
|
501
|
-
if (event === 'contactRequestUpdated') {
|
|
502
|
-
this.contactRequestUpdatedHandler = handler;
|
|
503
|
-
return;
|
|
504
|
-
}
|
|
505
464
|
if (event === 'contactApproved') {
|
|
506
465
|
this.contactApprovedHandler = handler;
|
|
507
466
|
return;
|
|
@@ -585,75 +544,12 @@ export class CanonAgent {
|
|
|
585
544
|
await this.requireRuntimeStatePublisher().clearRuntimeActivity(conversationId, options);
|
|
586
545
|
}
|
|
587
546
|
/**
|
|
588
|
-
*
|
|
589
|
-
*
|
|
590
|
-
*
|
|
591
|
-
* time an LLM acts on it. Instead defers to `resolveAdmission` so the
|
|
592
|
-
* answer reflects the target's *current* inbound policy.
|
|
547
|
+
* Message an existing Canon conversation or start/continue a direct one.
|
|
548
|
+
* Policy and admission are enforced by Canon; this method deliberately
|
|
549
|
+
* exposes no runtime configuration or trusted source-context fields.
|
|
593
550
|
*/
|
|
594
|
-
async
|
|
595
|
-
|
|
596
|
-
targetUserId: card.userId,
|
|
597
|
-
...(card.userType === 'ai_agent' && card.canonContactId
|
|
598
|
-
? { canonContactId: card.canonContactId }
|
|
599
|
-
: {}),
|
|
600
|
-
};
|
|
601
|
-
const targetKey = target.canonContactId ?? target.targetUserId;
|
|
602
|
-
// Include the opener/request payloads in the dedupe key so two concurrent
|
|
603
|
-
// calls with different `text`, `requestMessage`, or setup choices don't silently collapse
|
|
604
|
-
// and lose the second caller's intended side effect.
|
|
605
|
-
const contextualKey = options?.selfContext
|
|
606
|
-
? `${options.sourceConversationId ?? ''}\u0000${options.selfContext.type}\u0000${options.selfContext.context}`
|
|
607
|
-
: '';
|
|
608
|
-
const inFlightKey = `${targetKey}\u0000${options?.text ?? ''}\u0000${options?.requestMessage ?? ''}\u0000${JSON.stringify(options?.sessionConfig ?? null)}\u0000${JSON.stringify(options?.sessionSelection ?? null)}\u0000${contextualKey}`;
|
|
609
|
-
const inFlight = this.reachOutInFlight.get(inFlightKey);
|
|
610
|
-
if (inFlight)
|
|
611
|
-
return inFlight;
|
|
612
|
-
const promise = this.executeReachOut(target, options).finally(() => {
|
|
613
|
-
this.reachOutInFlight.delete(inFlightKey);
|
|
614
|
-
});
|
|
615
|
-
this.reachOutInFlight.set(inFlightKey, promise);
|
|
616
|
-
return promise;
|
|
617
|
-
}
|
|
618
|
-
async executeReachOut(target, options) {
|
|
619
|
-
const { targetUserId } = target;
|
|
620
|
-
if (options?.selfContext) {
|
|
621
|
-
if (!options.sourceConversationId) {
|
|
622
|
-
throw new Error('sourceConversationId is required for contextual reachOut');
|
|
623
|
-
}
|
|
624
|
-
if (!options.text) {
|
|
625
|
-
throw new Error('text is required for contextual reachOut');
|
|
626
|
-
}
|
|
627
|
-
const result = await this.apiClient.sendContextualMessage({
|
|
628
|
-
sourceConversationId: options.sourceConversationId,
|
|
629
|
-
targetUserId,
|
|
630
|
-
text: options.text,
|
|
631
|
-
selfContext: options.selfContext,
|
|
632
|
-
requestMessage: options.requestMessage ?? null,
|
|
633
|
-
sessionConfig: options.sessionConfig ?? null,
|
|
634
|
-
sessionSelection: options.sessionSelection,
|
|
635
|
-
});
|
|
636
|
-
return result.status === 'messaged'
|
|
637
|
-
? {
|
|
638
|
-
status: 'messaged',
|
|
639
|
-
conversationId: result.conversationId,
|
|
640
|
-
messageId: result.messageId,
|
|
641
|
-
selfContextId: result.selfContextId,
|
|
642
|
-
created: result.created,
|
|
643
|
-
reused: result.reused,
|
|
644
|
-
sessionSelection: result.sessionSelection,
|
|
645
|
-
}
|
|
646
|
-
: result;
|
|
647
|
-
}
|
|
648
|
-
return reachOutToCanonContact(this.apiClient, {
|
|
649
|
-
...(target.canonContactId
|
|
650
|
-
? { canonContactId: target.canonContactId }
|
|
651
|
-
: { targetUserId }),
|
|
652
|
-
text: options?.text ?? null,
|
|
653
|
-
requestMessage: options?.requestMessage ?? null,
|
|
654
|
-
sessionConfig: options?.sessionConfig ?? null,
|
|
655
|
-
sessionSelection: options?.sessionSelection,
|
|
656
|
-
});
|
|
551
|
+
async communicate(input) {
|
|
552
|
+
return this.apiClient.communicate(input);
|
|
657
553
|
}
|
|
658
554
|
async start() {
|
|
659
555
|
if (this.running)
|
|
@@ -702,7 +598,6 @@ export class CanonAgent {
|
|
|
702
598
|
try {
|
|
703
599
|
this.agentContext = await this.apiClient.getAgentMe();
|
|
704
600
|
this.ensureApprovalManager(this.agentContext);
|
|
705
|
-
await this.reconcileContactLifecycleInbox();
|
|
706
601
|
}
|
|
707
602
|
catch {
|
|
708
603
|
console.warn('[canon-sdk] Failed to fetch agent context — owner/access info unavailable');
|
|
@@ -749,9 +644,6 @@ export class CanonAgent {
|
|
|
749
644
|
onContactRequest: (request) => {
|
|
750
645
|
void this.handleContactRequestEvent(this.contactRequestHandler, request);
|
|
751
646
|
},
|
|
752
|
-
onContactRequestUpdated: (request) => {
|
|
753
|
-
void this.recordAndHandleContactLifecycleEvent(request);
|
|
754
|
-
},
|
|
755
647
|
onContactApproved: (request) => {
|
|
756
648
|
void this.handleContactRequestEvent(this.contactApprovedHandler, request);
|
|
757
649
|
},
|
|
@@ -781,26 +673,18 @@ export class CanonAgent {
|
|
|
781
673
|
rtm.setConnectionHandlers({
|
|
782
674
|
onConnected: () => {
|
|
783
675
|
this.startRuntimeHeartbeat();
|
|
784
|
-
void this.reconcileContactLifecycleInbox().catch((error) => {
|
|
785
|
-
console.error('[canon-sdk] Contact lifecycle reconciliation failed:', error);
|
|
786
|
-
});
|
|
787
676
|
if (!this.sseConnectedLogged) {
|
|
788
677
|
this.sseConnectedLogged = true;
|
|
789
678
|
console.log('[canon-sdk] SSE stream connected');
|
|
790
679
|
}
|
|
791
680
|
},
|
|
792
|
-
onReplayExpired: () => {
|
|
793
|
-
void this.reconcileContactLifecycleInbox().catch((error) => {
|
|
794
|
-
console.error('[canon-sdk] Contact lifecycle reconciliation failed:', error);
|
|
795
|
-
});
|
|
796
|
-
},
|
|
797
681
|
onDisconnected: () => this.stopRuntimeHeartbeat(),
|
|
798
682
|
});
|
|
799
683
|
this.realtimeManager = rtm;
|
|
800
684
|
await rtm.start();
|
|
801
685
|
}
|
|
802
|
-
async
|
|
803
|
-
return this.apiClient.
|
|
686
|
+
async createGroup(options) {
|
|
687
|
+
return this.apiClient.createGroup(options);
|
|
804
688
|
}
|
|
805
689
|
// ── Calls ────────────────────────────────────────────────────────────
|
|
806
690
|
// These return the LiveKit room token payload; the agent brings its own
|
|
@@ -869,37 +753,6 @@ export class CanonAgent {
|
|
|
869
753
|
console.error('[canon-sdk] Contact-request handler failed:', error instanceof Error ? error.message : error);
|
|
870
754
|
}
|
|
871
755
|
}
|
|
872
|
-
async recordAndHandleContactLifecycleEvent(request) {
|
|
873
|
-
if (this.contactLifecycleStore
|
|
874
|
-
&& request.requesterId === this.agentId
|
|
875
|
-
&& request.sourceConversationId) {
|
|
876
|
-
await this.contactLifecycleStore.record(request);
|
|
877
|
-
}
|
|
878
|
-
await this.handleContactRequestEvent(this.contactRequestUpdatedHandler, request);
|
|
879
|
-
}
|
|
880
|
-
async reconcileContactLifecycleInbox() {
|
|
881
|
-
if (!this.contactLifecycleStore || !this.agentId)
|
|
882
|
-
return;
|
|
883
|
-
const requests = await this.apiClient.listContactRequests({
|
|
884
|
-
direction: 'outbound',
|
|
885
|
-
includeResolved: true,
|
|
886
|
-
limit: 100,
|
|
887
|
-
});
|
|
888
|
-
if (!await this.contactLifecycleStore.isBaselineComplete()) {
|
|
889
|
-
await reconcileContactLifecycleEvents({
|
|
890
|
-
requests,
|
|
891
|
-
requesterId: this.agentId,
|
|
892
|
-
record: (request) => this.contactLifecycleStore.record(request, { pending: false }),
|
|
893
|
-
});
|
|
894
|
-
await this.contactLifecycleStore.completeBaseline();
|
|
895
|
-
return;
|
|
896
|
-
}
|
|
897
|
-
await reconcileContactLifecycleEvents({
|
|
898
|
-
requests,
|
|
899
|
-
requesterId: this.agentId,
|
|
900
|
-
record: (request) => this.contactLifecycleStore.record(request),
|
|
901
|
-
});
|
|
902
|
-
}
|
|
903
756
|
async handleContactGraphEvent(handler, payload) {
|
|
904
757
|
if (!handler)
|
|
905
758
|
return;
|
|
@@ -1016,13 +869,6 @@ export class CanonAgent {
|
|
|
1016
869
|
}
|
|
1017
870
|
return {
|
|
1018
871
|
...source,
|
|
1019
|
-
admissionActions: {
|
|
1020
|
-
blockUser: source.admissionActions?.blockUser === true,
|
|
1021
|
-
unblockUser: source.admissionActions?.unblockUser === true,
|
|
1022
|
-
removeContact: source.admissionActions?.removeContact === true,
|
|
1023
|
-
requestContact: this.options.ownerBoundCommunication?.enabled === true,
|
|
1024
|
-
reachOut: this.options.ownerBoundCommunication?.enabled === true,
|
|
1025
|
-
},
|
|
1026
872
|
supportsInterrupt: hasInterrupt,
|
|
1027
873
|
supportsInputInterrupt: source.supportsInputInterrupt === false ? false : hasInterrupt,
|
|
1028
874
|
commands: normalizeRuntimeCommandDescriptors(commands),
|
|
@@ -1631,9 +1477,6 @@ export class CanonAgent {
|
|
|
1631
1477
|
for (const m of history) {
|
|
1632
1478
|
m.isOwner = m.senderId === ownerId;
|
|
1633
1479
|
}
|
|
1634
|
-
for (const m of hydratedMessages) {
|
|
1635
|
-
m.isOwner = m.senderId === ownerId;
|
|
1636
|
-
}
|
|
1637
1480
|
}
|
|
1638
1481
|
const latestMessage = hydratedMessages[hydratedMessages.length - 1] ?? null;
|
|
1639
1482
|
const triggeringHumanId = latestMessage?.senderType === 'human'
|
|
@@ -1642,9 +1485,6 @@ export class CanonAgent {
|
|
|
1642
1485
|
let replyContext = latestMessage
|
|
1643
1486
|
? resolveCanonReplyContext({ message: latestMessage, messages: history })
|
|
1644
1487
|
: null;
|
|
1645
|
-
const contactLifecycleEvents = latestMessage?.isOwner && this.contactLifecycleStore
|
|
1646
|
-
? await this.contactLifecycleStore.take(conversationId)
|
|
1647
|
-
: [];
|
|
1648
1488
|
const resolvedActiveSelfContextId = resolveMessageActiveSelfContextId({
|
|
1649
1489
|
messageId: latestMessage?.id,
|
|
1650
1490
|
activeSelfContextIdByMessageId: page.activeSelfContextIdByMessageId,
|
|
@@ -1708,6 +1548,7 @@ export class CanonAgent {
|
|
|
1708
1548
|
ownerName: '',
|
|
1709
1549
|
discoverable: false,
|
|
1710
1550
|
inboundPolicy: 'approval-required',
|
|
1551
|
+
outboundPolicy: 'approval-required',
|
|
1711
1552
|
groupJoinPolicy: 'approval-required',
|
|
1712
1553
|
};
|
|
1713
1554
|
const provenance = latestMessage
|
|
@@ -1797,58 +1638,7 @@ export class CanonAgent {
|
|
|
1797
1638
|
const react = (messageId, emoji) => this.apiClient.react(conversationId, messageId, emoji);
|
|
1798
1639
|
const addMember = (userId) => this.apiClient.addMember(conversationId, userId);
|
|
1799
1640
|
const removeMember = (userId) => this.apiClient.removeMember(conversationId, userId);
|
|
1800
|
-
const
|
|
1801
|
-
sourceConversationId: conversationId,
|
|
1802
|
-
...target,
|
|
1803
|
-
text,
|
|
1804
|
-
...options,
|
|
1805
|
-
messageOptions: {
|
|
1806
|
-
...(options.messageOptions ?? {}),
|
|
1807
|
-
metadata: {
|
|
1808
|
-
...(options.messageOptions?.metadata ?? {}),
|
|
1809
|
-
turnId,
|
|
1810
|
-
turnSemantics: 'turn_complete',
|
|
1811
|
-
},
|
|
1812
|
-
},
|
|
1813
|
-
});
|
|
1814
|
-
const reachOut = (card, options) => {
|
|
1815
|
-
if (!this.options.ownerBoundCommunication?.enabled) {
|
|
1816
|
-
return this.reachOut(card, {
|
|
1817
|
-
...(options ?? {}),
|
|
1818
|
-
sourceConversationId: conversationId,
|
|
1819
|
-
});
|
|
1820
|
-
}
|
|
1821
|
-
if (!latestMessage?.isOwner || !latestMessage.id) {
|
|
1822
|
-
return Promise.reject(new Error('Owner-bound reachOut requires an owner-authored foreground message.'));
|
|
1823
|
-
}
|
|
1824
|
-
if (options?.sessionConfig != null
|
|
1825
|
-
|| options?.sessionSelection != null
|
|
1826
|
-
|| options?.selfContext != null) {
|
|
1827
|
-
return Promise.reject(new Error('Owner-bound reachOut does not accept model-selected session configuration or hidden context.'));
|
|
1828
|
-
}
|
|
1829
|
-
const boundCard = replyContext?.found && replyContext.contactCard
|
|
1830
|
-
? replyContext.contactCard
|
|
1831
|
-
: card;
|
|
1832
|
-
const text = options?.text?.trim();
|
|
1833
|
-
if (!text)
|
|
1834
|
-
return Promise.reject(new Error('Owner-bound reachOut requires visible text.'));
|
|
1835
|
-
return this.apiClient.sendTo({
|
|
1836
|
-
...(boundCard.canonContactId
|
|
1837
|
-
? { canonContactId: boundCard.canonContactId }
|
|
1838
|
-
: { targetUserId: boundCard.userId }),
|
|
1839
|
-
sourceConversationId: conversationId,
|
|
1840
|
-
text,
|
|
1841
|
-
...(options?.requestMessage ? { requestMessage: options.requestMessage } : {}),
|
|
1842
|
-
messageOptions: {
|
|
1843
|
-
metadata: {
|
|
1844
|
-
sourceConversationId: conversationId,
|
|
1845
|
-
sourceMessageId: latestMessage.id,
|
|
1846
|
-
turnId,
|
|
1847
|
-
turnSemantics: 'turn_complete',
|
|
1848
|
-
},
|
|
1849
|
-
},
|
|
1850
|
-
});
|
|
1851
|
-
};
|
|
1641
|
+
const communicate = (input) => this.communicate(input);
|
|
1852
1642
|
const requestApproval = async (request) => {
|
|
1853
1643
|
throwIfAborted();
|
|
1854
1644
|
const manager = this.ensureApprovalManager(agent);
|
|
@@ -2209,7 +1999,6 @@ export class CanonAgent {
|
|
|
2209
1999
|
messages: hydratedMessages,
|
|
2210
2000
|
history,
|
|
2211
2001
|
replyContext,
|
|
2212
|
-
contactLifecycleEvents,
|
|
2213
2002
|
conversationId,
|
|
2214
2003
|
conversation,
|
|
2215
2004
|
...(groupContext ? { groupContext } : {}),
|
|
@@ -2221,8 +2010,7 @@ export class CanonAgent {
|
|
|
2221
2010
|
react,
|
|
2222
2011
|
addMember,
|
|
2223
2012
|
removeMember,
|
|
2224
|
-
|
|
2225
|
-
reachOut,
|
|
2013
|
+
communicate,
|
|
2226
2014
|
agent,
|
|
2227
2015
|
activeSelfContextId,
|
|
2228
2016
|
selfContexts,
|
package/dist/index.d.ts
CHANGED
|
@@ -7,5 +7,5 @@ export { DEFAULT_ANTHROPIC_REQUEST_HEADROOM_BYTES, DEFAULT_MEDIA_CACHE_DIR, DEFA
|
|
|
7
7
|
export type { AnthropicImageBlock, AnthropicImageBudgetOptions, AnthropicImageMimeType, MaterializeMediaOptions, MaterializedCanonAttachment, MaterializedCanonReplyContext, ReplyWithFileOptions, UploadMediaFileOptions, } from './media.js';
|
|
8
8
|
export type { SessionConfig, Session } from './session-manager.js';
|
|
9
9
|
export type { CanonAgentTurnVerbosityOption } from './turn-verbosity-option.js';
|
|
10
|
-
export type { AgentContext, CanonGroupContext, CanonKnownRecentParticipant, CanonMembershipChange, CanonContactRequest,
|
|
11
|
-
export type { CanonAgentConnectionOptions, CanonAgentOptions, ContactAddedHandler, ContactRemovedHandler, ContactRequestHandler,
|
|
10
|
+
export type { AgentContext, CanonGroupContext, CanonKnownRecentParticipant, CanonMembershipChange, CanonContactRequest, GroupInviteRequirements, CanonMessage, CanonConversation, CommunicateInput, CommunicateResult, DirectConversationSelection, CanonConversationsPage, CanonConversationsPageOptions, CanonReplyContext, CanonSelfContext, CanonTurnContextV2, CanonRuntimeDescriptor, MessageUpdatedPayload, SendMessageOptions, CreateGroupOptions, CreateGroupResult, TurnVerbosity, TurnVerbosityConfig, } from '@canonmsg/core';
|
|
11
|
+
export type { CanonAgentConnectionOptions, CanonAgentOptions, ContactAddedHandler, ContactRemovedHandler, ContactRequestHandler, FinalMessageResult, MessageHandler, MessageHandlerContext, MessageUpdatedHandler, ParticipationSuppressedHandler, ProgressMessageOptions, ProgressMessageResult, RuntimeApprovalRequest, RuntimeInputRequest, RuntimeInputResult, RuntimeControlSurface, RuntimePrimitiveContext, RuntimePrimitiveHandler, RuntimePrimitiveHandlers, SessionInfo, SessionOptions, DeliveryMode, } from './types.js';
|
package/dist/realtime.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type AgentContext, type ContactAddedPayload, type ContactApprovedPayload, type ContactRemovedPayload, type ContactRequestPayload, type
|
|
1
|
+
import { type AgentContext, type ContactAddedPayload, type ContactApprovedPayload, type ContactRemovedPayload, type ContactRequestPayload, type ConversationUpdatedPayload, type MessageUpdatedPayload, type ParticipationSuppressedPayload, type VoiceSessionEventPayload } from '@canonmsg/core';
|
|
2
2
|
import { Debouncer } from './debouncer.js';
|
|
3
3
|
/**
|
|
4
4
|
* Wraps @canonmsg/core's CanonStream with SDK-specific features:
|
|
@@ -16,7 +16,6 @@ export declare class RealtimeManager {
|
|
|
16
16
|
private suppressedSseErrorCount;
|
|
17
17
|
private onAgentContext;
|
|
18
18
|
private onContactRequest;
|
|
19
|
-
private onContactRequestUpdated;
|
|
20
19
|
private onContactApproved;
|
|
21
20
|
private onContactAdded;
|
|
22
21
|
private onContactRemoved;
|
|
@@ -26,7 +25,6 @@ export declare class RealtimeManager {
|
|
|
26
25
|
private onMessageDeleted;
|
|
27
26
|
private onConnected;
|
|
28
27
|
private onDisconnected;
|
|
29
|
-
private onReplayExpired;
|
|
30
28
|
private onCallStarted;
|
|
31
29
|
private onCallEnded;
|
|
32
30
|
constructor(apiKey: string, debouncer: Debouncer, agentId: string, streamUrl?: string, options?: {
|
|
@@ -39,7 +37,6 @@ export declare class RealtimeManager {
|
|
|
39
37
|
setOnAgentContext(cb: (ctx: AgentContext) => void): void;
|
|
40
38
|
setContactRequestHandlers(handlers: {
|
|
41
39
|
onContactRequest?: (payload: ContactRequestPayload) => void;
|
|
42
|
-
onContactRequestUpdated?: (payload: ContactRequestUpdatedPayload) => void;
|
|
43
40
|
onContactApproved?: (payload: ContactApprovedPayload) => void;
|
|
44
41
|
}): void;
|
|
45
42
|
setContactGraphHandlers(handlers: {
|
|
@@ -61,7 +58,6 @@ export declare class RealtimeManager {
|
|
|
61
58
|
setConnectionHandlers(handlers: {
|
|
62
59
|
onConnected?: () => void;
|
|
63
60
|
onDisconnected?: () => void;
|
|
64
|
-
onReplayExpired?: () => void;
|
|
65
61
|
}): void;
|
|
66
62
|
setCallHandlers(handlers: {
|
|
67
63
|
onCallStarted?: (payload: VoiceSessionEventPayload) => void;
|
package/dist/realtime.js
CHANGED
|
@@ -17,7 +17,6 @@ export class RealtimeManager {
|
|
|
17
17
|
suppressedSseErrorCount = 0;
|
|
18
18
|
onAgentContext = null;
|
|
19
19
|
onContactRequest = null;
|
|
20
|
-
onContactRequestUpdated = null;
|
|
21
20
|
onContactApproved = null;
|
|
22
21
|
onContactAdded = null;
|
|
23
22
|
onContactRemoved = null;
|
|
@@ -27,7 +26,6 @@ export class RealtimeManager {
|
|
|
27
26
|
onMessageDeleted = null;
|
|
28
27
|
onConnected = null;
|
|
29
28
|
onDisconnected = null;
|
|
30
|
-
onReplayExpired = null;
|
|
31
29
|
onCallStarted = null;
|
|
32
30
|
onCallEnded = null;
|
|
33
31
|
constructor(apiKey, debouncer, agentId, streamUrl, options) {
|
|
@@ -104,9 +102,6 @@ export class RealtimeManager {
|
|
|
104
102
|
onContactRequest: (payload) => {
|
|
105
103
|
this.onContactRequest?.(payload);
|
|
106
104
|
},
|
|
107
|
-
onContactRequestUpdated: (payload) => {
|
|
108
|
-
this.onContactRequestUpdated?.(payload);
|
|
109
|
-
},
|
|
110
105
|
onContactApproved: (payload) => {
|
|
111
106
|
this.onContactApproved?.(payload);
|
|
112
107
|
},
|
|
@@ -130,7 +125,6 @@ export class RealtimeManager {
|
|
|
130
125
|
},
|
|
131
126
|
onReplayExpired: (payload) => {
|
|
132
127
|
console.error(`[canon-sdk] SSE replay window expired${payload.lastAvailableId ? ` (oldest available event: ${payload.lastAvailableId})` : ''}; missed history is available via explicit REST fetch`);
|
|
133
|
-
this.onReplayExpired?.();
|
|
134
128
|
},
|
|
135
129
|
onError: (err) => {
|
|
136
130
|
this.logSseError(err);
|
|
@@ -181,7 +175,6 @@ export class RealtimeManager {
|
|
|
181
175
|
}
|
|
182
176
|
setContactRequestHandlers(handlers) {
|
|
183
177
|
this.onContactRequest = handlers.onContactRequest ?? null;
|
|
184
|
-
this.onContactRequestUpdated = handlers.onContactRequestUpdated ?? null;
|
|
185
178
|
this.onContactApproved = handlers.onContactApproved ?? null;
|
|
186
179
|
}
|
|
187
180
|
setContactGraphHandlers(handlers) {
|
|
@@ -208,7 +201,6 @@ export class RealtimeManager {
|
|
|
208
201
|
setConnectionHandlers(handlers) {
|
|
209
202
|
this.onConnected = handlers.onConnected ?? null;
|
|
210
203
|
this.onDisconnected = handlers.onDisconnected ?? null;
|
|
211
|
-
this.onReplayExpired = handlers.onReplayExpired ?? null;
|
|
212
204
|
}
|
|
213
205
|
setCallHandlers(handlers) {
|
|
214
206
|
this.onCallStarted = handlers.onCallStarted ?? null;
|
package/dist/types.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export type { AddMemberResult,
|
|
2
|
-
import type { AddMemberResult, CanonGroupContext, CanonMessage, CanonConversation,
|
|
1
|
+
export type { AddMemberResult, GroupInviteRequirements, AgentClientType, CanonGroupContext, CanonRuntimeActivityItem, CanonRuntimeActivityKind, CanonRuntimeActivityStatus, CanonRuntimeDescriptor, CanonRuntimeFact, CanonRuntimeFactGroup, CanonRuntimePrimitiveId, CanonRuntimeProvenance, CanonTurnContextV2, CanonMessage, CanonConversation, CommunicateInput, CommunicateResult, CanonReplyContext, CanonContact, CanonContactRequest, CanonResolveAdmissionResult, ContactAddedPayload, ContactRemovedPayload, ContactSource, AgentContext, ResolveAdmissionTargetInput, ResolvedAdmissionState, ResolvedAdmissionTargetSummary, ResolvedTargetAdmissionPayload, CanonSelfContext, CreateGroupOptions, CreateGroupResult, SendMessageOptions, SessionConfig, TurnLifecycleState, TurnOutputBlock, TurnOutputBlockInput, ApprovalNativeRequestMetadata, ApprovalRequestCategory, ApprovalRequestDetail, ApprovalRequestMetadata, ApprovalRisk, ApprovalReplyMetadata, ApprovalOutcomeMetadata, RuntimeInputChoice, RuntimeInputAnswers, RuntimeInputKind, RuntimeInputNativeMetadata, RuntimeInputQuestion, RuntimeCardNativeMetadata, RuntimeCardV1, ResumableMediaUploadResult, MediaAttachment, VideoProcessingStatus, VideoProcessingErrorCode, SessionRule, ApprovalResult, } from '@canonmsg/core';
|
|
2
|
+
import type { AddMemberResult, CanonGroupContext, CanonMessage, CanonConversation, CommunicateInput, CommunicateResult, CanonReplyContext, CanonRuntimeActionDispatch, CanonRuntimePrimitiveId, CanonRuntimeProvenance, CanonTurnContextV2, ApprovalNativeRequestMetadata, ApprovalRequestCategory, ApprovalRequestDetail, ApprovalResult, ApprovalRisk, RuntimeInputChoice, RuntimeInputAnswers, RuntimeInputKind, RuntimeInputNativeMetadata, RuntimeInputQuestion, RuntimeCardNativeMetadata, RuntimeCardV1, ResumableMediaUploadResult, SendMessageOptions, TurnOutputBlock, TurnOutputBlockInput } from '@canonmsg/core';
|
|
3
3
|
import type { MaterializeMediaOptions, MaterializedCanonAttachment, ReplyWithFileOptions, UploadMediaFileOptions } from './media.js';
|
|
4
4
|
export interface ProgressMessageOptions extends SendMessageOptions {
|
|
5
5
|
/**
|
|
@@ -152,8 +152,6 @@ export interface MessageHandlerContext {
|
|
|
152
152
|
history: CanonMessage[];
|
|
153
153
|
/** Resolved message/media content for the latest swipe-reply target, if any. */
|
|
154
154
|
replyContext: CanonReplyContext | null;
|
|
155
|
-
/** Terminal introduction events retained until this natural owner turn. */
|
|
156
|
-
contactLifecycleEvents: import('@canonmsg/core').CanonContactRequest[];
|
|
157
155
|
conversationId: string;
|
|
158
156
|
conversation: CanonConversation;
|
|
159
157
|
/** Lightweight group awareness, present for group conversations. */
|
|
@@ -178,14 +176,8 @@ export interface MessageHandlerContext {
|
|
|
178
176
|
addMember: (userId: string) => Promise<AddMemberResult>;
|
|
179
177
|
/** Remove a member from this conversation (requires owner/admin role) */
|
|
180
178
|
removeMember: (userId: string) => Promise<void>;
|
|
181
|
-
/**
|
|
182
|
-
|
|
183
|
-
targetConversationId: string;
|
|
184
|
-
} | {
|
|
185
|
-
targetUserId: string;
|
|
186
|
-
}, text: string, options: Omit<import('@canonmsg/core').SendContextualMessageOptions, 'sourceConversationId' | 'targetConversationId' | 'targetUserId' | 'text'>) => Promise<import('@canonmsg/core').SendContextualMessageResult>;
|
|
187
|
-
/** Reach a contact card from this conversation; contextual reach-outs use this conversation as source. */
|
|
188
|
-
reachOut: (card: ContactCardPayload, options?: Omit<ReachOutOptions, 'sourceConversationId'>) => Promise<ReachOutResult>;
|
|
179
|
+
/** Message an existing Canon conversation or start/continue a direct one. */
|
|
180
|
+
communicate: (input: CommunicateInput) => Promise<CommunicateResult>;
|
|
189
181
|
/** Trusted agent identity & access context */
|
|
190
182
|
agent: import('@canonmsg/core').AgentContext;
|
|
191
183
|
/** Active private self-context to continue for this turn, if Canon supplied one. */
|
|
@@ -312,15 +304,6 @@ export interface CanonAgentOptions extends CanonAgentConnectionOptions {
|
|
|
312
304
|
clientType?: import('@canonmsg/core').AgentClientType;
|
|
313
305
|
/** Optional runtime descriptor published to Canon for setup/live UI rendering. */
|
|
314
306
|
runtimeDescriptor?: import('@canonmsg/core').CanonRuntimeDescriptor;
|
|
315
|
-
/**
|
|
316
|
-
* Opt into the owner-authorized introduction surface advertised to Canon.
|
|
317
|
-
* Lifecycle events never start a model turn; they are passed to the next
|
|
318
|
-
* natural owner handler invocation and to onContactRequestUpdated.
|
|
319
|
-
*/
|
|
320
|
-
ownerBoundCommunication?: {
|
|
321
|
-
enabled: true;
|
|
322
|
-
lifecycleStore?: ContactLifecycleStore;
|
|
323
|
-
};
|
|
324
307
|
/** Optional Canon runtime signal handlers. Enables interrupt controls when provided. */
|
|
325
308
|
runtimeControls?: RuntimeControlHandlers;
|
|
326
309
|
/** Runtime publishing surface. Use `host` when this agent owns live runtime controls. */
|
|
@@ -347,15 +330,6 @@ export interface CanonAgentOptions extends CanonAgentConnectionOptions {
|
|
|
347
330
|
turnVerbosity?: import('./turn-verbosity-option.js').CanonAgentTurnVerbosityOption;
|
|
348
331
|
}
|
|
349
332
|
export type ContactRequestHandler = (request: import('@canonmsg/core').CanonContactRequest) => void | Promise<void>;
|
|
350
|
-
export type ContactRequestUpdatedHandler = ContactRequestHandler;
|
|
351
|
-
export interface ContactLifecycleStore {
|
|
352
|
-
record(request: import('@canonmsg/core').CanonContactRequest, options?: {
|
|
353
|
-
pending?: boolean;
|
|
354
|
-
}): boolean | Promise<boolean>;
|
|
355
|
-
take(sourceConversationId: string): import('@canonmsg/core').CanonContactRequest[] | Promise<import('@canonmsg/core').CanonContactRequest[]>;
|
|
356
|
-
isBaselineComplete(): boolean | Promise<boolean>;
|
|
357
|
-
completeBaseline(): void | Promise<void>;
|
|
358
|
-
}
|
|
359
333
|
export type ContactAddedHandler = (contact: import('@canonmsg/core').ContactAddedPayload) => void | Promise<void>;
|
|
360
334
|
export type ContactRemovedHandler = (payload: import('@canonmsg/core').ContactRemovedPayload) => void | Promise<void>;
|
|
361
335
|
/**
|
|
@@ -365,56 +339,3 @@ export type ContactRemovedHandler = (payload: import('@canonmsg/core').ContactRe
|
|
|
365
339
|
* benched agent can tell deliberate participation policy from a dead stream.
|
|
366
340
|
*/
|
|
367
341
|
export type ParticipationSuppressedHandler = (payload: import('@canonmsg/core').ParticipationSuppressedPayload) => void | Promise<void>;
|
|
368
|
-
/**
|
|
369
|
-
* Result of `agent.reachOut(card)` — describes which side-effect ran so the
|
|
370
|
-
* caller can decide what to tell the LLM. `messaged` means the agent opened
|
|
371
|
-
* (or sent into) a direct conversation; `requested` means the target's
|
|
372
|
-
* inbound policy required a contact request, which has been created;
|
|
373
|
-
* `pending` means a prior outbound request is still awaiting approval; and
|
|
374
|
-
* `blocked` / `unavailable` describe terminal states with `reason` set.
|
|
375
|
-
*/
|
|
376
|
-
export type ReachOutResult = {
|
|
377
|
-
status: 'messaged';
|
|
378
|
-
conversationId: string;
|
|
379
|
-
messageId?: string;
|
|
380
|
-
selfContextId?: string;
|
|
381
|
-
created?: boolean;
|
|
382
|
-
reused?: boolean;
|
|
383
|
-
sessionSelection?: DirectSessionSelection['mode'];
|
|
384
|
-
} | {
|
|
385
|
-
status: 'requested';
|
|
386
|
-
requestId: string | null;
|
|
387
|
-
deferredIntentId?: string | null;
|
|
388
|
-
} | {
|
|
389
|
-
status: 'pending';
|
|
390
|
-
requestId: string | null;
|
|
391
|
-
deferredIntentId?: string | null;
|
|
392
|
-
} | {
|
|
393
|
-
status: 'setup_required';
|
|
394
|
-
reason: string;
|
|
395
|
-
} | {
|
|
396
|
-
status: 'no_session';
|
|
397
|
-
reason: string;
|
|
398
|
-
} | {
|
|
399
|
-
status: 'blocked' | 'unavailable';
|
|
400
|
-
reason: string;
|
|
401
|
-
};
|
|
402
|
-
export interface ReachOutOptions {
|
|
403
|
-
/**
|
|
404
|
-
* Optional first message; sent now when allowed or parked for exact delivery
|
|
405
|
-
* after approval when it is visible text <= 4 KiB. Attachments and hidden
|
|
406
|
-
* session setup cannot be parked; a setup-requiring coding target returns
|
|
407
|
-
* `setup_required` before a contact request is created.
|
|
408
|
-
*/
|
|
409
|
-
text?: string;
|
|
410
|
-
/** Optional contact-request note. Defaults to `text` when admission is `request-required`. */
|
|
411
|
-
requestMessage?: string;
|
|
412
|
-
/** Explicit session setup to use when the contact-card target is an agent. */
|
|
413
|
-
sessionConfig?: VerbSessionConfig | null;
|
|
414
|
-
/** Whether to continue an existing direct agent session or start a fresh one. */
|
|
415
|
-
sessionSelection?: DirectSessionSelection;
|
|
416
|
-
/** Source conversation for contextual cross-session reach-outs. */
|
|
417
|
-
sourceConversationId?: string;
|
|
418
|
-
/** Private context for the agent when this reach-out sends a cross-session message. */
|
|
419
|
-
selfContext?: SendContextualSelfContextInput;
|
|
420
|
-
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@canonmsg/agent-sdk",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "9.0.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": "^
|
|
31
|
+
"@canonmsg/core": "^11.0.0"
|
|
32
32
|
},
|
|
33
33
|
"publishConfig": {
|
|
34
34
|
"access": "public"
|