@canonmsg/agent-sdk 10.2.1 → 10.3.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 CHANGED
@@ -54,7 +54,7 @@ Runtime heartbeats and Firebase token refresh come from Core, using the same mac
54
54
  | `autoMarkRead` | `boolean` | `true` | Advance Canon's read cursor after successful handler completion. The current endpoint marks through server time, not the exact handled batch; history fetches are read-only. |
55
55
  | `sessions` | `SessionOptions` | `undefined` | Enable per-conversation session queues and persistent metadata |
56
56
  | `clientType` | `AgentClientType` | `'generic'` | Agent runtime label used for Canon capability detection |
57
- | `runtimeDescriptor` | `CanonRuntimeDescriptor` | minimal generic descriptor | Optional setup/live controls and runtime capability metadata for Canon UI |
57
+ | `runtimeDescriptor` | `CanonRuntimeDescriptor` | minimal generic descriptor | Supported live actions and runtime capability metadata for Canon UI |
58
58
  | `runtimeControls` | `RuntimeControlHandlers` | `undefined` | Optional `onInterrupt` / `onStopAndDrop` / `onNewSession` handlers for Canon working-state controls |
59
59
  | `runtimeControlSurface` | `'agent' \| 'host'` | `'agent'` | Runtime publishing surface. Use `host` when this SDK agent owns live runtime controls. |
60
60
  | `runtimePrimitives` | `RuntimePrimitiveHandlers` | `undefined` | Optional typed primitive command handlers for descriptor-backed runtime commands |
@@ -63,49 +63,7 @@ Runtime heartbeats and Firebase token refresh come from Core, using the same mac
63
63
 
64
64
  ### Optional runtime controls
65
65
 
66
- 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:
67
-
68
- ```typescript
69
- const agent = new CanonAgent({
70
- apiKey: process.env.CANON_API_KEY!,
71
- environmentId: process.env.CANON_ENVIRONMENT_ID!,
72
- runtimeDescriptor: {
73
- coreControls: [
74
- {
75
- id: 'workspace',
76
- label: 'Project',
77
- options: [
78
- {
79
- value: 'workspace-canon',
80
- label: 'canon',
81
- description: 'dev/canon',
82
- workspaceRootId: 'dev',
83
- workspaceRelativePath: 'canon',
84
- source: 'discovered',
85
- },
86
- {
87
- value: 'workspace-yumyumv2',
88
- label: 'yumyumv2',
89
- description: 'dev/yumyumv2',
90
- workspaceRootId: 'dev',
91
- workspaceRelativePath: 'yumyumv2',
92
- source: 'discovered',
93
- },
94
- ],
95
- defaultValue: 'workspace-canon',
96
- availability: 'setup',
97
- liveBehavior: 'none',
98
- selectionPolicy: 'inherit',
99
- description: 'Choose one of the local projects this SDK host is configured to use.',
100
- },
101
- ],
102
- runtimeControls: [],
103
- workspaceRoots: [
104
- { id: 'dev', label: '~/dev' },
105
- ],
106
- },
107
- });
108
- ```
66
+ Runtime descriptors describe supported live actions and status. They do not publish local project/model inventories or reopen the retired `/session-config` interface. Native work-session setup uses the owner-private workflow below.
109
67
 
110
68
  SDK agents only advertise Stop or Send Now when they register runtime-control handlers. Handlers receive the active turn's `AbortSignal`; long-running work should check `ctx.abortSignal.aborted` or pass the signal into cancellable APIs.
111
69
 
@@ -131,25 +89,21 @@ agent.on('message', async ({ messages, replyFinal, abortSignal }) => {
131
89
  });
132
90
  ```
133
91
 
134
- The descriptor only drives Canon UI and validation. Your SDK agent is still responsible for reading session config and safely mapping selected values to local directories.
135
-
136
- Node SDK builders can reuse `buildConfiguredWorkspaceOptionsWithRoots` from `@canonmsg/core` to produce the same stable project IDs and root metadata used by the first-party Claude Code and Codex hosts.
137
-
138
- Current rules of thumb:
139
-
140
- - Canon does not infer real runtime support from `clientType`; if you do not publish a descriptor, Canon should behave as a mostly status-only generic agent surface.
141
- - `availability` controls where a setting appears:
142
- - `setup`: session creation only
143
- - `live`: live strip only
144
- - `setup_and_live`: both surfaces
145
- - `liveBehavior` controls how truthful live editing should be:
146
- - `immediate`: Canon may show a pending state until the runtime snapshot reflects the applied value
147
- - `next_turn`: Canon may let the user queue the change, but should label it as applying on the next turn
148
- - `none`: Canon never exposes it as live-editable
149
- - `selectionPolicy: 'required_explicit'` means Canon should require the user to make a choice instead of silently inheriting a default
150
- - `workspaceRoots` and `writableRoots` document allowed roots and let Canon group project choices. Canon still stores the selected concrete `workspaceId`; it does not send arbitrary root-relative paths to generic SDK agents.
151
- - Publishing a descriptor does not automatically make your SDK agent enforce those controls. If you advertise model, workspace, execution mode, or runtime-native controls, your runtime must actually read and apply the stored config.
152
- - Message handlers receive `ctx.provenance`, a Canon-computed sender/conversation context for the latest inbound message in the batch. Use it when your runtime wants owner-only tools, group mention policy, or self-context-aware behavior; Canon does not impose a sandbox on SDK agents.
92
+ Canon does not infer support from `clientType`. Advertise only actions your runtime implements. Message handlers receive Canon-computed `ctx.provenance`; use it to enforce your runtime's owner and group policies.
93
+
94
+ ### Native work sessions
95
+
96
+ Use `createCanonWorkSessionHost` when Canon should share actual sessions from another runtime. Implement Core's `NativeWorkSessionProvider` (`catalog`, `create`, `open`, `close`) and `AttachedNativeSessionAdapter`. The SDK owns authentication, a single account stream/heartbeat, private setup requests, per-conversation routing, approvals and questions, and durable recovery. Codex is one provider of this API.
97
+
98
+ The host requires a stable `hostId`, a durable `createFileWorkSessionHostStore`, a per-room attachment store, and an exclusive native-session lock. Its `connection` identifies the Canon account and environment. `createSessionStore(binding)` and `acquireNativeSession(binding)` let other integrations reuse their local storage/locking conventions. Start the host once per account; it can serve many conversations.
99
+
100
+ The owner sees only choices advertised by the local provider. Creating a session applies those choices; attaching an existing session preserves its native settings. Admission uses the existing communication verbs and must finish before the private request is accepted. Each account has one session per Canon conversation. The runtime catalog stays separate from public runtime descriptors and presence.
101
+
102
+ The host persists creation intent before native work and the exact returned native ID before attachment. A replayed claim never creates another session. Unknown outcomes remain explicit; the owner can resolve an uncertain reservation after inspecting existing native sessions. Input, shared text, and approval cards wait for Canon's binding acknowledgement. Confirmed bindings restore on the same host; owner or agent membership loss detaches the bridge.
103
+
104
+ Native approval/input interactions use the existing Core request manager, exact owner identity, stable IDs and deadlines, and durable results. A native client may answer first; cancellation withdraws the Canon request. Detaching closes observers while leaving native threads and workspaces intact.
105
+
106
+ See the [Codex workflow](../codex-plugin/README.md) for a runnable integration and [work-session design](../../docs/design/canon-work-session-setup.md) for boundaries and recovery behavior.
153
107
 
154
108
  ### Runtime primitives
155
109
 
@@ -0,0 +1,58 @@
1
+ import { CanonClient, CanonStream, type AttachedNativeSessionAdapter, type AttachedSessionBinding, type AttachedSessionPublisher, type AttachedSessionState, type AttachedSessionStore, type ResolvedAgent, type RuntimeStatePublisher } from '@canonmsg/core';
2
+ export type CanonAttachedSessionConnection = Pick<ResolvedAgent, 'apiKey' | 'environmentId' | 'baseUrl' | 'streamUrl' | 'rtdbUrl' | 'firebaseApiKey' | 'clientType'> & {
3
+ agentId: string;
4
+ };
5
+ export interface CanonAttachedSessionStatus {
6
+ status: 'connecting' | 'connected' | 'disconnected' | 'native' | 'accepted' | 'blocked' | 'stopped';
7
+ reason?: string;
8
+ messageId?: string;
9
+ nativeState?: AttachedSessionState;
10
+ /** REST history is an observation, not fresh permission to execute. */
11
+ pendingMessageIds?: string[];
12
+ }
13
+ export interface CanonAttachedSessionOptions {
14
+ connection: CanonAttachedSessionConnection;
15
+ binding: AttachedSessionBinding;
16
+ native: AttachedNativeSessionAdapter;
17
+ /** Exclusive durable store; the Core attachment closes it on stop/failure. */
18
+ store: AttachedSessionStore;
19
+ reconcileIntervalMs?: number;
20
+ onStatus?: (status: CanonAttachedSessionStatus) => void;
21
+ onError?: (error: unknown, operation: string) => void;
22
+ /** An account coordinator may supply its authenticated transport for several rooms. */
23
+ transport?: CanonAttachedSessionTransport;
24
+ }
25
+ export type CanonAttachedSessionStreamHandler = ConstructorParameters<typeof CanonStream>[0]['handler'];
26
+ /** Account resources stay with the coordinator; an individual room never closes them. */
27
+ export interface CanonAttachedSessionTransport {
28
+ agentId: string;
29
+ environmentId: string;
30
+ client: CanonClient;
31
+ publisher: RuntimeStatePublisher;
32
+ subscribe(handler: CanonAttachedSessionStreamHandler): () => void;
33
+ hasPendingInteraction?(conversationId: string): boolean;
34
+ /** A prepared observer can wait for its owner's durable setup acknowledgement. */
35
+ isRouteActive?(conversationId: string): boolean;
36
+ }
37
+ export interface CanonAttachedSession {
38
+ /** Resolves after native attachment and Canon transport startup, not stream termination. */
39
+ start(): Promise<void>;
40
+ /** Terminal: closes this attachment; the native session keeps running. */
41
+ stop(): Promise<void>;
42
+ getState(): AttachedSessionState;
43
+ /** Republish an external native interaction's waiting state. */
44
+ refreshRuntimeState(): void;
45
+ }
46
+ /**
47
+ * Connect one existing native session to one existing Canon conversation.
48
+ * This is account-level participation, not a second group member. The caller
49
+ * must hold exclusive account and native-session ownership so no competing
50
+ * runtime writes this account's live state or republishes the native thread.
51
+ */
52
+ export declare function createCanonAttachedSession(options: CanonAttachedSessionOptions): CanonAttachedSession;
53
+ /**
54
+ * Observed native turns may combine local and Canon input. They are proactive
55
+ * agent transcript publications, never replies authorized by a particular
56
+ * Canon input. Core persists these immutable parts before calling send.
57
+ */
58
+ export declare function createCanonAttachedSessionPublisher(client: Pick<CanonClient, 'sendProactiveMessage'>, binding: AttachedSessionBinding): AttachedSessionPublisher;