@canonmsg/agent-sdk 8.9.0 → 9.1.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 +6 -34
- package/dist/canon-agent.d.ts +7 -22
- package/dist/canon-agent.js +117 -225
- package/dist/index.d.ts +3 -3
- package/dist/realtime.d.ts +1 -5
- package/dist/realtime.js +0 -8
- package/dist/types.d.ts +20 -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 |
|
|
@@ -218,6 +200,7 @@ The `message` event handler receives a context object with:
|
|
|
218
200
|
| `turnVerbosity` | `'verbose' \| 'quiet'` | Resolved emission mode for this turn — see [Turn verbosity](#turn-verbosity). Fixed for the whole turn |
|
|
219
201
|
| `requestApproval` | `(request) => Promise<ApprovalResult>` | Render a Canon approval card and wait for the decision. Fail-closed: returns `{ decision: 'deny' }` on any non-abort failure instead of throwing |
|
|
220
202
|
| `requestRuntimeInput` | `(request) => Promise<RuntimeInputResult>` | Render a Canon input card for clarification, sudo, or secret values |
|
|
203
|
+
| `requestPlanReview` | `(request) => Promise<RuntimePlanReviewResult>` | Render Canon's native plan-review card and wait for approve, revise, reject, cancellation, or timeout |
|
|
221
204
|
| `requestCard` / `sendCard` | functions | Render a generic `canon.card.v1` rich card. `requestCard` blocks only on cards that carry an `actions` block; `sendCard` posts a display card |
|
|
222
205
|
| `media` | `{ materialize, uploadFile, replyWithFile }` | Canon-managed access to real media bytes via `~/.canon/media-cache` plus local-file uploads back into Canon |
|
|
223
206
|
| `session` | `SessionInfo \| undefined` | Per-conversation queue/session state when sessions are enabled |
|
|
@@ -264,7 +247,7 @@ Reaction update events are interaction state, not new chat turns. They do not ca
|
|
|
264
247
|
|
|
265
248
|
### Human-in-the-loop cards
|
|
266
249
|
|
|
267
|
-
Use `ctx.requestRuntimeInput(...)` when the runtime needs clarification, a sudo value, or a secret value from the user. Use `ctx.requestApproval(...)` when the runtime needs an allow/deny decision before taking an action. Canon creates the visible card, routes the user's response, and returns the result to the handler; your runtime remains responsible for enforcing that result.
|
|
250
|
+
Use `ctx.requestRuntimeInput(...)` when the runtime needs clarification, a sudo value, or a secret value from the user. Use `ctx.requestPlanReview(...)` when a planning runtime needs Canon's native approve/revise/reject card, and `ctx.requestApproval(...)` when the runtime needs an allow/deny decision before taking an action. Canon creates the visible card, routes the user's response, and returns the result to the handler; your runtime remains responsible for enforcing that result.
|
|
268
251
|
|
|
269
252
|
`requestApproval` is fail-closed and never throws: it returns `{ decision: 'deny' }` when no approval manager can be built (no resolved agent identity or owner) and on any non-abort error. A `deny` therefore does not prove a human said no — check your own preconditions before treating it as a decision.
|
|
270
253
|
|
|
@@ -307,18 +290,9 @@ agent.on('contactRequest', (request) => {
|
|
|
307
290
|
agent.on('contactApproved', (request) => {
|
|
308
291
|
console.log('Request approved:', request.id);
|
|
309
292
|
});
|
|
310
|
-
|
|
311
|
-
agent.on('contactRequestUpdated', (request) => {
|
|
312
|
-
console.log('Outbound request phase:', request.phase);
|
|
313
|
-
});
|
|
314
293
|
```
|
|
315
294
|
|
|
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.
|
|
295
|
+
These are awareness callbacks only. Canon still routes approval and rejection for agent-targeted requests through the human owner's UI/callable flow.
|
|
322
296
|
|
|
323
297
|
### Turn-aware example
|
|
324
298
|
|
|
@@ -361,8 +335,6 @@ await agent.contacts.list(); // CanonContact[]
|
|
|
361
335
|
await agent.contacts.get(contactId); // CanonContact | null
|
|
362
336
|
await agent.contacts.remove(contactId);
|
|
363
337
|
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
338
|
|
|
367
339
|
await agent.users.block(userId);
|
|
368
340
|
await agent.users.unblock(userId);
|
|
@@ -536,7 +508,7 @@ A conversation whose type Canon could not determine falls back to verbose, never
|
|
|
536
508
|
|
|
537
509
|
**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
510
|
|
|
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, `
|
|
511
|
+
**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
512
|
|
|
541
513
|
**`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
514
|
|
|
@@ -561,4 +533,4 @@ Canon caps a single message at 4 KB of UTF-8 text, and rejects anything longer o
|
|
|
561
533
|
Every other send path passes your text through as-is, so text over the cap still fails there. Notably:
|
|
562
534
|
|
|
563
535
|
- `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
|
-
- `
|
|
536
|
+
- `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,
|
|
@@ -273,6 +245,33 @@ function isAbortLikeError(error) {
|
|
|
273
245
|
return true;
|
|
274
246
|
return typeof record.message === 'string' && /\babort(?:ed)?\b/i.test(record.message);
|
|
275
247
|
}
|
|
248
|
+
function linkAbortSignals(turnSignal, requestSignal) {
|
|
249
|
+
if (!requestSignal || requestSignal === turnSignal) {
|
|
250
|
+
return { signal: turnSignal, dispose: () => { } };
|
|
251
|
+
}
|
|
252
|
+
const controller = new AbortController();
|
|
253
|
+
const abortFrom = (source) => {
|
|
254
|
+
if (!controller.signal.aborted)
|
|
255
|
+
controller.abort(source.reason);
|
|
256
|
+
};
|
|
257
|
+
const onTurnAbort = () => abortFrom(turnSignal);
|
|
258
|
+
const onRequestAbort = () => abortFrom(requestSignal);
|
|
259
|
+
if (turnSignal.aborted)
|
|
260
|
+
abortFrom(turnSignal);
|
|
261
|
+
else
|
|
262
|
+
turnSignal.addEventListener('abort', onTurnAbort, { once: true });
|
|
263
|
+
if (requestSignal.aborted)
|
|
264
|
+
abortFrom(requestSignal);
|
|
265
|
+
else
|
|
266
|
+
requestSignal.addEventListener('abort', onRequestAbort, { once: true });
|
|
267
|
+
return {
|
|
268
|
+
signal: controller.signal,
|
|
269
|
+
dispose: () => {
|
|
270
|
+
turnSignal.removeEventListener('abort', onTurnAbort);
|
|
271
|
+
requestSignal.removeEventListener('abort', onRequestAbort);
|
|
272
|
+
},
|
|
273
|
+
};
|
|
274
|
+
}
|
|
276
275
|
export class CanonAgent {
|
|
277
276
|
options;
|
|
278
277
|
runtimeConnection;
|
|
@@ -283,7 +282,6 @@ export class CanonAgent {
|
|
|
283
282
|
sessionManager = null;
|
|
284
283
|
handler = null;
|
|
285
284
|
contactRequestHandler = null;
|
|
286
|
-
contactRequestUpdatedHandler = null;
|
|
287
285
|
contactApprovedHandler = null;
|
|
288
286
|
contactAddedHandler = null;
|
|
289
287
|
contactRemovedHandler = null;
|
|
@@ -304,8 +302,6 @@ export class CanonAgent {
|
|
|
304
302
|
users;
|
|
305
303
|
/** Conversation discovery for choosing existing sessions intentionally. */
|
|
306
304
|
conversations;
|
|
307
|
-
reachOutInFlight = new Map();
|
|
308
|
-
contactLifecycleStore;
|
|
309
305
|
agentId = null;
|
|
310
306
|
agentContext = null;
|
|
311
307
|
approvalManager = null;
|
|
@@ -352,9 +348,6 @@ export class CanonAgent {
|
|
|
352
348
|
rtdbUrl: this.runtimeConnection.rtdbUrl,
|
|
353
349
|
firebaseApiKey: this.runtimeConnection.firebaseWebApiKey,
|
|
354
350
|
};
|
|
355
|
-
this.contactLifecycleStore = options.ownerBoundCommunication?.enabled
|
|
356
|
-
? options.ownerBoundCommunication.lifecycleStore ?? new InMemoryContactLifecycleStore()
|
|
357
|
-
: null;
|
|
358
351
|
this.apiClient = new CanonClient(this.options.apiKey, this.options.baseUrl);
|
|
359
352
|
this.typingSignals = createTypingStatusPublisher({
|
|
360
353
|
setTyping: (conversationId, typing, status) => status
|
|
@@ -368,9 +361,6 @@ export class CanonAgent {
|
|
|
368
361
|
list: () => apiClient.listContacts(),
|
|
369
362
|
get: (contactId) => apiClient.getContact(contactId),
|
|
370
363
|
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
364
|
};
|
|
375
365
|
this.users = {
|
|
376
366
|
block: (userId) => apiClient.blockUser(userId),
|
|
@@ -498,10 +488,6 @@ export class CanonAgent {
|
|
|
498
488
|
this.contactRequestHandler = handler;
|
|
499
489
|
return;
|
|
500
490
|
}
|
|
501
|
-
if (event === 'contactRequestUpdated') {
|
|
502
|
-
this.contactRequestUpdatedHandler = handler;
|
|
503
|
-
return;
|
|
504
|
-
}
|
|
505
491
|
if (event === 'contactApproved') {
|
|
506
492
|
this.contactApprovedHandler = handler;
|
|
507
493
|
return;
|
|
@@ -585,75 +571,12 @@ export class CanonAgent {
|
|
|
585
571
|
await this.requireRuntimeStatePublisher().clearRuntimeActivity(conversationId, options);
|
|
586
572
|
}
|
|
587
573
|
/**
|
|
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.
|
|
574
|
+
* Message an existing Canon conversation or start/continue a direct one.
|
|
575
|
+
* Policy and admission are enforced by Canon; this method deliberately
|
|
576
|
+
* exposes no runtime configuration or trusted source-context fields.
|
|
593
577
|
*/
|
|
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
|
-
});
|
|
578
|
+
async communicate(input) {
|
|
579
|
+
return this.apiClient.communicate(input);
|
|
657
580
|
}
|
|
658
581
|
async start() {
|
|
659
582
|
if (this.running)
|
|
@@ -702,7 +625,6 @@ export class CanonAgent {
|
|
|
702
625
|
try {
|
|
703
626
|
this.agentContext = await this.apiClient.getAgentMe();
|
|
704
627
|
this.ensureApprovalManager(this.agentContext);
|
|
705
|
-
await this.reconcileContactLifecycleInbox();
|
|
706
628
|
}
|
|
707
629
|
catch {
|
|
708
630
|
console.warn('[canon-sdk] Failed to fetch agent context — owner/access info unavailable');
|
|
@@ -749,9 +671,6 @@ export class CanonAgent {
|
|
|
749
671
|
onContactRequest: (request) => {
|
|
750
672
|
void this.handleContactRequestEvent(this.contactRequestHandler, request);
|
|
751
673
|
},
|
|
752
|
-
onContactRequestUpdated: (request) => {
|
|
753
|
-
void this.recordAndHandleContactLifecycleEvent(request);
|
|
754
|
-
},
|
|
755
674
|
onContactApproved: (request) => {
|
|
756
675
|
void this.handleContactRequestEvent(this.contactApprovedHandler, request);
|
|
757
676
|
},
|
|
@@ -781,26 +700,18 @@ export class CanonAgent {
|
|
|
781
700
|
rtm.setConnectionHandlers({
|
|
782
701
|
onConnected: () => {
|
|
783
702
|
this.startRuntimeHeartbeat();
|
|
784
|
-
void this.reconcileContactLifecycleInbox().catch((error) => {
|
|
785
|
-
console.error('[canon-sdk] Contact lifecycle reconciliation failed:', error);
|
|
786
|
-
});
|
|
787
703
|
if (!this.sseConnectedLogged) {
|
|
788
704
|
this.sseConnectedLogged = true;
|
|
789
705
|
console.log('[canon-sdk] SSE stream connected');
|
|
790
706
|
}
|
|
791
707
|
},
|
|
792
|
-
onReplayExpired: () => {
|
|
793
|
-
void this.reconcileContactLifecycleInbox().catch((error) => {
|
|
794
|
-
console.error('[canon-sdk] Contact lifecycle reconciliation failed:', error);
|
|
795
|
-
});
|
|
796
|
-
},
|
|
797
708
|
onDisconnected: () => this.stopRuntimeHeartbeat(),
|
|
798
709
|
});
|
|
799
710
|
this.realtimeManager = rtm;
|
|
800
711
|
await rtm.start();
|
|
801
712
|
}
|
|
802
|
-
async
|
|
803
|
-
return this.apiClient.
|
|
713
|
+
async createGroup(options) {
|
|
714
|
+
return this.apiClient.createGroup(options);
|
|
804
715
|
}
|
|
805
716
|
// ── Calls ────────────────────────────────────────────────────────────
|
|
806
717
|
// These return the LiveKit room token payload; the agent brings its own
|
|
@@ -869,37 +780,6 @@ export class CanonAgent {
|
|
|
869
780
|
console.error('[canon-sdk] Contact-request handler failed:', error instanceof Error ? error.message : error);
|
|
870
781
|
}
|
|
871
782
|
}
|
|
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
783
|
async handleContactGraphEvent(handler, payload) {
|
|
904
784
|
if (!handler)
|
|
905
785
|
return;
|
|
@@ -1016,13 +896,6 @@ export class CanonAgent {
|
|
|
1016
896
|
}
|
|
1017
897
|
return {
|
|
1018
898
|
...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
899
|
supportsInterrupt: hasInterrupt,
|
|
1027
900
|
supportsInputInterrupt: source.supportsInputInterrupt === false ? false : hasInterrupt,
|
|
1028
901
|
commands: normalizeRuntimeCommandDescriptors(commands),
|
|
@@ -1631,9 +1504,6 @@ export class CanonAgent {
|
|
|
1631
1504
|
for (const m of history) {
|
|
1632
1505
|
m.isOwner = m.senderId === ownerId;
|
|
1633
1506
|
}
|
|
1634
|
-
for (const m of hydratedMessages) {
|
|
1635
|
-
m.isOwner = m.senderId === ownerId;
|
|
1636
|
-
}
|
|
1637
1507
|
}
|
|
1638
1508
|
const latestMessage = hydratedMessages[hydratedMessages.length - 1] ?? null;
|
|
1639
1509
|
const triggeringHumanId = latestMessage?.senderType === 'human'
|
|
@@ -1642,9 +1512,6 @@ export class CanonAgent {
|
|
|
1642
1512
|
let replyContext = latestMessage
|
|
1643
1513
|
? resolveCanonReplyContext({ message: latestMessage, messages: history })
|
|
1644
1514
|
: null;
|
|
1645
|
-
const contactLifecycleEvents = latestMessage?.isOwner && this.contactLifecycleStore
|
|
1646
|
-
? await this.contactLifecycleStore.take(conversationId)
|
|
1647
|
-
: [];
|
|
1648
1515
|
const resolvedActiveSelfContextId = resolveMessageActiveSelfContextId({
|
|
1649
1516
|
messageId: latestMessage?.id,
|
|
1650
1517
|
activeSelfContextIdByMessageId: page.activeSelfContextIdByMessageId,
|
|
@@ -1708,6 +1575,7 @@ export class CanonAgent {
|
|
|
1708
1575
|
ownerName: '',
|
|
1709
1576
|
discoverable: false,
|
|
1710
1577
|
inboundPolicy: 'approval-required',
|
|
1578
|
+
outboundPolicy: 'approval-required',
|
|
1711
1579
|
groupJoinPolicy: 'approval-required',
|
|
1712
1580
|
};
|
|
1713
1581
|
const provenance = latestMessage
|
|
@@ -1797,58 +1665,7 @@ export class CanonAgent {
|
|
|
1797
1665
|
const react = (messageId, emoji) => this.apiClient.react(conversationId, messageId, emoji);
|
|
1798
1666
|
const addMember = (userId) => this.apiClient.addMember(conversationId, userId);
|
|
1799
1667
|
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
|
-
};
|
|
1668
|
+
const communicate = (input) => this.communicate(input);
|
|
1852
1669
|
const requestApproval = async (request) => {
|
|
1853
1670
|
throwIfAborted();
|
|
1854
1671
|
const manager = this.ensureApprovalManager(agent);
|
|
@@ -1916,6 +1733,7 @@ export class CanonAgent {
|
|
|
1916
1733
|
};
|
|
1917
1734
|
const requestRuntimeInput = async (request) => {
|
|
1918
1735
|
throwIfAborted();
|
|
1736
|
+
const linkedSignal = linkAbortSignals(abortController.signal, request.signal);
|
|
1919
1737
|
const inputId = safeRuntimeInputId(request.inputId, request.kind);
|
|
1920
1738
|
const timeoutMs = Math.max(1_000, request.timeoutMs ?? DEFAULT_RUNTIME_INPUT_TIMEOUT_MS);
|
|
1921
1739
|
const expiresAtMs = Date.now() + timeoutMs;
|
|
@@ -1971,7 +1789,7 @@ export class CanonAgent {
|
|
|
1971
1789
|
result = await this.ensureRuntimeRequestManager().request('input', conversationId, { kind: request.kind }, {
|
|
1972
1790
|
requestId: inputId,
|
|
1973
1791
|
expiresAt: expiresAtMs,
|
|
1974
|
-
signal:
|
|
1792
|
+
signal: linkedSignal.signal,
|
|
1975
1793
|
});
|
|
1976
1794
|
const outcome = buildRuntimeInputOutcome(inputId, result.status, {
|
|
1977
1795
|
kind: request.kind,
|
|
@@ -1997,7 +1815,7 @@ export class CanonAgent {
|
|
|
1997
1815
|
return result;
|
|
1998
1816
|
}
|
|
1999
1817
|
catch (error) {
|
|
2000
|
-
if (abortController.signal.aborted || isAbortLikeError(error)) {
|
|
1818
|
+
if (abortController.signal.aborted || request.signal?.aborted || isAbortLikeError(error)) {
|
|
2001
1819
|
if (requestCreated) {
|
|
2002
1820
|
// Abort landing before the manager wired its cancel (e.g. during
|
|
2003
1821
|
// the pre-request writeTurn/typing round-trips) leaves the
|
|
@@ -2020,6 +1838,81 @@ export class CanonAgent {
|
|
|
2020
1838
|
await resumeTurnFromWaiting();
|
|
2021
1839
|
return result;
|
|
2022
1840
|
}
|
|
1841
|
+
finally {
|
|
1842
|
+
linkedSignal.dispose();
|
|
1843
|
+
}
|
|
1844
|
+
};
|
|
1845
|
+
const requestPlanReview = async (request) => {
|
|
1846
|
+
throwIfAborted();
|
|
1847
|
+
const linkedSignal = linkAbortSignals(abortController.signal, request.signal);
|
|
1848
|
+
const planId = request.planId && RUNTIME_INPUT_ID_PATTERN.test(request.planId)
|
|
1849
|
+
? request.planId
|
|
1850
|
+
: randomUUID();
|
|
1851
|
+
const timeoutMs = Math.max(1_000, request.timeoutMs ?? DEFAULT_RUNTIME_INPUT_TIMEOUT_MS);
|
|
1852
|
+
const expiresAtMs = Date.now() + timeoutMs;
|
|
1853
|
+
const responseUserId = normalizeResponseUserId(request.responseUserId)
|
|
1854
|
+
?? triggeringHumanId
|
|
1855
|
+
?? normalizeResponseUserId(agent.ownerId);
|
|
1856
|
+
let created = false;
|
|
1857
|
+
let result = { status: 'timeout', planId };
|
|
1858
|
+
shouldPersistTurnState = true;
|
|
1859
|
+
try {
|
|
1860
|
+
result = await this.ensureRuntimeRequestManager().request('plan', conversationId, {
|
|
1861
|
+
...(request.title ? { title: request.title } : {}),
|
|
1862
|
+
...(request.summary ? { summary: request.summary } : {}),
|
|
1863
|
+
...(request.body ? { body: request.body } : {}),
|
|
1864
|
+
...(request.allowedPrompts ? { allowedPrompts: request.allowedPrompts } : {}),
|
|
1865
|
+
...(responseUserId ? { responseUserId } : {}),
|
|
1866
|
+
turnId: request.turnId ?? turnId,
|
|
1867
|
+
}, {
|
|
1868
|
+
requestId: planId,
|
|
1869
|
+
expiresAt: expiresAtMs,
|
|
1870
|
+
responderPolicy: 'infer',
|
|
1871
|
+
signal: linkedSignal.signal,
|
|
1872
|
+
onCreated: async () => {
|
|
1873
|
+
created = true;
|
|
1874
|
+
try {
|
|
1875
|
+
await turnOutput.addBlock({
|
|
1876
|
+
id: `plan:${planId}`,
|
|
1877
|
+
kind: 'input',
|
|
1878
|
+
status: 'pending',
|
|
1879
|
+
title: request.title ?? 'Plan review',
|
|
1880
|
+
summary: request.summary ?? 'Review requested',
|
|
1881
|
+
});
|
|
1882
|
+
await turnOutput.waitingInput();
|
|
1883
|
+
}
|
|
1884
|
+
catch { }
|
|
1885
|
+
await writeTurn('waiting_input');
|
|
1886
|
+
try {
|
|
1887
|
+
await this.typingSignals.clear(conversationId);
|
|
1888
|
+
}
|
|
1889
|
+
catch { }
|
|
1890
|
+
},
|
|
1891
|
+
});
|
|
1892
|
+
throwIfAborted();
|
|
1893
|
+
shouldPersistTurnState = false;
|
|
1894
|
+
try {
|
|
1895
|
+
await turnOutput.completeBlock(`plan:${planId}`, {
|
|
1896
|
+
summary: `Plan ${result.status}`,
|
|
1897
|
+
});
|
|
1898
|
+
}
|
|
1899
|
+
catch { }
|
|
1900
|
+
await resumeTurnFromWaiting();
|
|
1901
|
+
return result;
|
|
1902
|
+
}
|
|
1903
|
+
catch (error) {
|
|
1904
|
+
if (abortController.signal.aborted || request.signal?.aborted || isAbortLikeError(error)) {
|
|
1905
|
+
throw error;
|
|
1906
|
+
}
|
|
1907
|
+
shouldPersistTurnState = false;
|
|
1908
|
+
if (!created)
|
|
1909
|
+
throw error;
|
|
1910
|
+
await resumeTurnFromWaiting();
|
|
1911
|
+
return result;
|
|
1912
|
+
}
|
|
1913
|
+
finally {
|
|
1914
|
+
linkedSignal.dispose();
|
|
1915
|
+
}
|
|
2023
1916
|
};
|
|
2024
1917
|
const sendCard = async (request) => {
|
|
2025
1918
|
throwIfAborted();
|
|
@@ -2209,7 +2102,6 @@ export class CanonAgent {
|
|
|
2209
2102
|
messages: hydratedMessages,
|
|
2210
2103
|
history,
|
|
2211
2104
|
replyContext,
|
|
2212
|
-
contactLifecycleEvents,
|
|
2213
2105
|
conversationId,
|
|
2214
2106
|
conversation,
|
|
2215
2107
|
...(groupContext ? { groupContext } : {}),
|
|
@@ -2221,8 +2113,7 @@ export class CanonAgent {
|
|
|
2221
2113
|
react,
|
|
2222
2114
|
addMember,
|
|
2223
2115
|
removeMember,
|
|
2224
|
-
|
|
2225
|
-
reachOut,
|
|
2116
|
+
communicate,
|
|
2226
2117
|
agent,
|
|
2227
2118
|
activeSelfContextId,
|
|
2228
2119
|
selfContexts,
|
|
@@ -2232,6 +2123,7 @@ export class CanonAgent {
|
|
|
2232
2123
|
turnVerbosity,
|
|
2233
2124
|
requestApproval,
|
|
2234
2125
|
requestRuntimeInput,
|
|
2126
|
+
requestPlanReview,
|
|
2235
2127
|
requestCard,
|
|
2236
2128
|
sendCard,
|
|
2237
2129
|
abortSignal: abortController.signal,
|
package/dist/index.d.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
export { CanonAgent } from './canon-agent.js';
|
|
2
2
|
export type { AgentContactsAPI, AgentConversationsAPI, AgentUsersAPI } from './canon-agent.js';
|
|
3
3
|
export { ApprovalManager, buildApprovalOutcome, buildApprovalReply, buildApprovalRequest, CanonApiError, DEFAULT_APPROVAL_CONFIG, generateApprovalId, HOST_ADMISSION_ACTION_CAPABILITIES, HOST_ADMISSION_ACTIONS_DISABLED, parseApprovalReplyMetadata, parseApprovalRequestMetadata, parseSessionRule, redactSecrets, } from '@canonmsg/core';
|
|
4
|
-
export type { ApprovalConfig, ApprovalNativeRequestMetadata, ApprovalOutcomeMetadata, ApprovalReplyMetadata, ApprovalRequestCategory, ApprovalRequestDetail, ApprovalRequestMetadata, ApprovalResult, ApprovalRisk, CanonContact, CanonRuntimeActivityItem, CanonRuntimeActivityKind, CanonRuntimeActivityStatus, CanonRuntimeTurnModeActivation, CanonRuntimeTurnModeDescriptor, CanonRuntimeTurnModeScope, CanonRuntimeFact, CanonRuntimeFactGroup, CanonResolveAdmissionResult, ContactAddedPayload, ContactCardPayload, ContactRemovedPayload, ContactSource, HostAdmissionActionCapabilities, ParticipationSuppressedPayload, ResolveAdmissionTargetInput, ResolvedAdmissionState, ResolvedAdmissionTargetSummary, ResolvedTargetAdmissionPayload, ResumableMediaUploadResult, MediaAttachment, VideoProcessingStatus, VideoProcessingErrorCode, RuntimeInputAnswers, RuntimeInputChoice, RuntimeInputKind, RuntimeInputNativeMetadata, RuntimeInputQuestion, SessionRule, } from '@canonmsg/core';
|
|
4
|
+
export type { ApprovalConfig, ApprovalNativeRequestMetadata, ApprovalOutcomeMetadata, ApprovalReplyMetadata, ApprovalRequestCategory, ApprovalRequestDetail, ApprovalRequestMetadata, ApprovalResult, ApprovalRisk, CanonContact, CanonRuntimeActivityItem, CanonRuntimeActivityKind, CanonRuntimeActivityStatus, CanonRuntimeTurnModeActivation, CanonRuntimeTurnModeDescriptor, CanonRuntimeTurnModeScope, CanonRuntimeFact, CanonRuntimeFactGroup, CanonResolveAdmissionResult, ContactAddedPayload, ContactCardPayload, ContactRemovedPayload, ContactSource, HostAdmissionActionCapabilities, ParticipationSuppressedPayload, ResolveAdmissionTargetInput, ResolvedAdmissionState, ResolvedAdmissionTargetSummary, ResolvedTargetAdmissionPayload, ResumableMediaUploadResult, MediaAttachment, VideoProcessingStatus, VideoProcessingErrorCode, RuntimeInputAnswers, RuntimeInputChoice, RuntimeInputKind, RuntimeInputNativeMetadata, RuntimeInputQuestion, RuntimePlanRequestPayload, RuntimePlanRequestResult, SessionRule, } from '@canonmsg/core';
|
|
5
5
|
export { SessionManager } from './session-manager.js';
|
|
6
6
|
export { DEFAULT_ANTHROPIC_REQUEST_HEADROOM_BYTES, DEFAULT_MEDIA_CACHE_DIR, DEFAULT_MEDIA_MATERIALIZATION_BYTES, MAX_ANTHROPIC_IMAGE_RAW_BYTES, MAX_ANTHROPIC_REQUEST_BYTES, MAX_CANON_MEDIA_BYTES, getCodexImagePath, getMessageAttachments, inferUploadMimeType, isAnthropicImageAttachment, materializeAttachment, materializeMessageMedia, materializeReplyContextMedia, resolveAttachmentMimeType, sendMediaFileMessage, toAnthropicImageBlock, toAnthropicImageBlocksWithinBudget, uploadMediaFile, } from './media.js';
|
|
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, RuntimePlanReviewRequest, RuntimePlanReviewResult, 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, RuntimePlanRequestPayload, RuntimePlanRequestResult, 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, RuntimePlanRequestPayload, RuntimePlanRequestResult, 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
|
/**
|
|
@@ -119,6 +119,8 @@ export interface RuntimeInputRequest {
|
|
|
119
119
|
native?: RuntimeInputNativeMetadata;
|
|
120
120
|
inputId?: string;
|
|
121
121
|
timeoutMs?: number;
|
|
122
|
+
/** Aborts the pending input request without waiting for the enclosing turn to stop. */
|
|
123
|
+
signal?: AbortSignal;
|
|
122
124
|
/** Human conversation member who should answer. Secret and sudo prompts remain owner-only. */
|
|
123
125
|
responseUserId?: string;
|
|
124
126
|
}
|
|
@@ -128,6 +130,14 @@ export interface RuntimeInputResult {
|
|
|
128
130
|
answers?: RuntimeInputAnswers;
|
|
129
131
|
inputId: string;
|
|
130
132
|
}
|
|
133
|
+
/** Native Canon plan-review card backed by Core's existing runtime-plan interaction. */
|
|
134
|
+
export interface RuntimePlanReviewRequest extends RuntimePlanRequestPayload {
|
|
135
|
+
planId?: string;
|
|
136
|
+
timeoutMs?: number;
|
|
137
|
+
/** Aborts the pending plan review without waiting for the enclosing turn to stop. */
|
|
138
|
+
signal?: AbortSignal;
|
|
139
|
+
}
|
|
140
|
+
export type RuntimePlanReviewResult = RuntimePlanRequestResult;
|
|
131
141
|
export interface RuntimeCardRequest {
|
|
132
142
|
card: RuntimeCardV1;
|
|
133
143
|
cardId?: string;
|
|
@@ -152,8 +162,6 @@ export interface MessageHandlerContext {
|
|
|
152
162
|
history: CanonMessage[];
|
|
153
163
|
/** Resolved message/media content for the latest swipe-reply target, if any. */
|
|
154
164
|
replyContext: CanonReplyContext | null;
|
|
155
|
-
/** Terminal introduction events retained until this natural owner turn. */
|
|
156
|
-
contactLifecycleEvents: import('@canonmsg/core').CanonContactRequest[];
|
|
157
165
|
conversationId: string;
|
|
158
166
|
conversation: CanonConversation;
|
|
159
167
|
/** Lightweight group awareness, present for group conversations. */
|
|
@@ -178,14 +186,8 @@ export interface MessageHandlerContext {
|
|
|
178
186
|
addMember: (userId: string) => Promise<AddMemberResult>;
|
|
179
187
|
/** Remove a member from this conversation (requires owner/admin role) */
|
|
180
188
|
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>;
|
|
189
|
+
/** Message an existing Canon conversation or start/continue a direct one. */
|
|
190
|
+
communicate: (input: CommunicateInput) => Promise<CommunicateResult>;
|
|
189
191
|
/** Trusted agent identity & access context */
|
|
190
192
|
agent: import('@canonmsg/core').AgentContext;
|
|
191
193
|
/** Active private self-context to continue for this turn, if Canon supplied one. */
|
|
@@ -218,6 +220,12 @@ export interface MessageHandlerContext {
|
|
|
218
220
|
* are never persisted in Canon message metadata.
|
|
219
221
|
*/
|
|
220
222
|
requestRuntimeInput: (request: RuntimeInputRequest) => Promise<RuntimeInputResult>;
|
|
223
|
+
/**
|
|
224
|
+
* Present a native Canon plan-review card and wait for approve, revise,
|
|
225
|
+
* reject, cancellation, or timeout. Canon owns presentation and response
|
|
226
|
+
* routing; the runtime remains responsible for enforcing the decision.
|
|
227
|
+
*/
|
|
228
|
+
requestPlanReview: (request: RuntimePlanReviewRequest) => Promise<RuntimePlanReviewResult>;
|
|
221
229
|
/**
|
|
222
230
|
* Ask the triggering human to review/respond to a generic rich card. The
|
|
223
231
|
* visible card document is redacted for presentation; raw response values
|
|
@@ -312,15 +320,6 @@ export interface CanonAgentOptions extends CanonAgentConnectionOptions {
|
|
|
312
320
|
clientType?: import('@canonmsg/core').AgentClientType;
|
|
313
321
|
/** Optional runtime descriptor published to Canon for setup/live UI rendering. */
|
|
314
322
|
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
323
|
/** Optional Canon runtime signal handlers. Enables interrupt controls when provided. */
|
|
325
324
|
runtimeControls?: RuntimeControlHandlers;
|
|
326
325
|
/** Runtime publishing surface. Use `host` when this agent owns live runtime controls. */
|
|
@@ -347,15 +346,6 @@ export interface CanonAgentOptions extends CanonAgentConnectionOptions {
|
|
|
347
346
|
turnVerbosity?: import('./turn-verbosity-option.js').CanonAgentTurnVerbosityOption;
|
|
348
347
|
}
|
|
349
348
|
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
349
|
export type ContactAddedHandler = (contact: import('@canonmsg/core').ContactAddedPayload) => void | Promise<void>;
|
|
360
350
|
export type ContactRemovedHandler = (payload: import('@canonmsg/core').ContactRemovedPayload) => void | Promise<void>;
|
|
361
351
|
/**
|
|
@@ -365,56 +355,3 @@ export type ContactRemovedHandler = (payload: import('@canonmsg/core').ContactRe
|
|
|
365
355
|
* benched agent can tell deliberate participation policy from a dead stream.
|
|
366
356
|
*/
|
|
367
357
|
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.1.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"
|