@canonmsg/agent-sdk 11.2.0 → 12.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 +171 -536
- package/dist/chunks/chunk-6VK4UZHJ.js +2806 -0
- package/dist/chunks/chunk-FPQFTTND.js +4899 -0
- package/dist/chunks/chunk-OLJ7H4E2.js +14705 -0
- package/dist/chunks/mls-crypto-NZDNSPAO.js +10 -0
- package/dist/cli.js +198 -0
- package/dist/drivers/claude.d.ts +36 -0
- package/dist/drivers/code.d.ts +18 -0
- package/dist/drivers/codex.d.ts +37 -0
- package/dist/index.d.ts +12 -25
- package/dist/index.js +69 -13
- package/dist/internal/backend-contracts/agentBehaviorPolicy.d.ts +122 -0
- package/dist/internal/backend-contracts/agentDirectory.d.ts +32 -0
- package/dist/internal/backend-contracts/agentOrigin.d.ts +23 -0
- package/dist/internal/backend-contracts/agentSearch.d.ts +17 -0
- package/dist/internal/backend-contracts/api-origins.generated.d.ts +4 -0
- package/dist/internal/backend-contracts/apiErrors.d.ts +1 -0
- package/dist/internal/backend-contracts/artifactActions.d.ts +159 -0
- package/dist/internal/backend-contracts/artifacts.d.ts +386 -0
- package/dist/internal/backend-contracts/communication.d.ts +131 -0
- package/dist/internal/backend-contracts/conversationMembership.d.ts +28 -0
- package/dist/internal/backend-contracts/deliveryObservation.d.ts +23 -0
- package/dist/internal/backend-contracts/diffRedaction.d.ts +6 -0
- package/dist/internal/backend-contracts/endpoint-unread.d.ts +4 -0
- package/dist/internal/backend-contracts/endpoint.d.ts +103 -0
- package/dist/internal/backend-contracts/endpointContentPolicy.d.ts +37 -0
- package/dist/internal/backend-contracts/environment.d.ts +20 -0
- package/dist/internal/backend-contracts/externalInbound.d.ts +24 -0
- package/dist/internal/backend-contracts/firestoreValues.d.ts +9 -0
- package/dist/internal/backend-contracts/index.d.ts +48 -0
- package/dist/internal/backend-contracts/media-url.d.ts +6 -0
- package/dist/internal/backend-contracts/media.d.ts +51 -0
- package/dist/internal/backend-contracts/mediaDelivery.d.ts +46 -0
- package/dist/internal/backend-contracts/mediaTransferProtocol.d.ts +17 -0
- package/dist/internal/backend-contracts/membershipChange.d.ts +21 -0
- package/dist/internal/backend-contracts/message.d.ts +86 -0
- package/dist/internal/backend-contracts/messageText.d.ts +6 -0
- package/dist/internal/backend-contracts/mlsDevApplication.d.ts +73 -0
- package/dist/internal/backend-contracts/mlsDevCredential.d.ts +75 -0
- package/dist/internal/backend-contracts/mlsDevCredentialReconciliation.d.ts +45 -0
- package/dist/internal/backend-contracts/mlsDevDeviceCustody.d.ts +51 -0
- package/dist/internal/backend-contracts/mlsDevEnrollment.d.ts +74 -0
- package/dist/internal/backend-contracts/mlsDevGroup.d.ts +197 -0
- package/dist/internal/backend-contracts/mlsDevGroupFeed.d.ts +47 -0
- package/dist/internal/backend-contracts/mlsDevKeyPackage.d.ts +42 -0
- package/dist/internal/backend-contracts/mlsDevKeyPackages.d.ts +55 -0
- package/dist/internal/backend-contracts/moderation.d.ts +7 -0
- package/dist/internal/backend-contracts/plaintextProcessors.d.ts +63 -0
- package/dist/internal/backend-contracts/preparedContent.d.ts +54 -0
- package/dist/internal/backend-contracts/replyAuthority.d.ts +14 -0
- package/dist/internal/backend-contracts/runtimeCardFields.d.ts +66 -0
- package/dist/internal/backend-contracts/runtimeCardStorage.d.ts +3 -0
- package/dist/internal/backend-contracts/runtimeDescriptor.d.ts +88 -0
- package/dist/internal/backend-contracts/selfContext.d.ts +9 -0
- package/dist/internal/backend-contracts/turnProtocol.d.ts +43 -0
- package/dist/internal/backend-contracts/verbContract.d.ts +848 -0
- package/dist/internal/backend-contracts/verbSchemas.d.ts +1707 -0
- package/dist/internal/backend-contracts/verbWire.d.ts +493 -0
- package/dist/internal/backend-contracts/webAuth.d.ts +8 -0
- package/dist/internal/backend-contracts/workSessions.d.ts +205 -0
- package/dist/internal/core/agent-profiles.d.ts +69 -0
- package/dist/internal/core/agent-resolver.d.ts +49 -0
- package/dist/internal/core/agent-session.d.ts +12 -0
- package/dist/internal/core/approval-format.d.ts +26 -0
- package/dist/internal/core/approval-manager.d.ts +43 -0
- package/dist/internal/core/approval-types.d.ts +74 -0
- package/dist/internal/core/artifacts.d.ts +262 -0
- package/dist/internal/core/attached-native-session.d.ts +174 -0
- package/dist/internal/core/attached-session-file-store.d.ts +3 -0
- package/dist/internal/core/base-url.d.ts +1 -0
- package/dist/internal/core/cli-metadata.d.ts +13 -0
- package/dist/internal/core/client.d.ts +486 -0
- package/dist/internal/core/completed-output-journal.d.ts +39 -0
- package/dist/internal/core/constants.d.ts +4 -0
- package/dist/internal/core/control-poller.d.ts +221 -0
- package/dist/internal/core/endpoint-client.d.ts +43 -0
- package/dist/internal/core/exclusive-json-file-store.d.ts +7 -0
- package/dist/internal/core/execution-environment-mode.d.ts +8 -0
- package/dist/internal/core/execution-environment.d.ts +55 -0
- package/dist/internal/core/gifs.d.ts +35 -0
- package/dist/internal/core/group-context.d.ts +25 -0
- package/dist/internal/core/host-runtime.d.ts +257 -0
- package/dist/internal/core/inbound-frame.d.ts +64 -0
- package/dist/internal/core/index.d.ts +107 -0
- package/dist/internal/core/local-runtime-catalog.d.ts +131 -0
- package/dist/internal/core/message-format.d.ts +13 -0
- package/dist/internal/core/native-session-interaction.d.ts +44 -0
- package/dist/internal/core/node-media.d.ts +6 -0
- package/dist/internal/core/policy.d.ts +2 -0
- package/dist/internal/core/provenance.d.ts +31 -0
- package/dist/internal/core/registration-cli.d.ts +40 -0
- package/dist/internal/core/registration-profile.d.ts +10 -0
- package/dist/internal/core/registration-session.d.ts +55 -0
- package/dist/internal/core/registration.d.ts +21 -0
- package/dist/internal/core/retry.d.ts +122 -0
- package/dist/internal/core/rtdb-rest.d.ts +145 -0
- package/dist/internal/core/runtime-cards.d.ts +331 -0
- package/dist/internal/core/runtime-descriptor.d.ts +75 -0
- package/dist/internal/core/runtime-endpoints.d.ts +26 -0
- package/dist/internal/core/runtime-heartbeat.d.ts +29 -0
- package/dist/internal/core/runtime-presentation.d.ts +35 -0
- package/dist/internal/core/runtime-request-manager.d.ts +284 -0
- package/dist/internal/core/runtime-state-publisher.d.ts +42 -0
- package/dist/internal/core/self-context.d.ts +61 -0
- package/dist/internal/core/silent-turn.d.ts +86 -0
- package/dist/internal/core/stream.d.ts +77 -0
- package/dist/internal/core/turn-output-controller.d.ts +69 -0
- package/dist/internal/core/turn-protocol.d.ts +33 -0
- package/dist/internal/core/turn-verbosity.d.ts +97 -0
- package/dist/internal/core/types.d.ts +1075 -0
- package/dist/internal/core/typing-status-publisher.d.ts +14 -0
- package/dist/internal/core/unified-diff.d.ts +35 -0
- package/dist/internal/core/work-session-provider.d.ts +29 -0
- package/dist/internal/core/workspace-discovery.d.ts +17 -0
- package/dist/internal/endpoint/artifact-images.d.ts +60 -0
- package/dist/internal/endpoint/artifact-reducer.d.ts +35 -0
- package/dist/internal/endpoint/artifact-views.d.ts +113 -0
- package/dist/internal/endpoint/artifacts.d.ts +75 -0
- package/dist/internal/endpoint/delivery.d.ts +47 -0
- package/dist/internal/endpoint/engine.d.ts +219 -0
- package/dist/internal/endpoint/ephemeral.d.ts +8 -0
- package/dist/internal/endpoint/events.d.ts +32 -0
- package/dist/internal/endpoint/external-inbound.d.ts +36 -0
- package/dist/internal/endpoint/handoff.d.ts +50 -0
- package/dist/internal/endpoint/history.d.ts +22 -0
- package/dist/internal/endpoint/image-generation-errors.d.ts +2 -0
- package/dist/internal/endpoint/inbound.d.ts +29 -0
- package/dist/internal/endpoint/index.d.ts +32 -0
- package/dist/internal/endpoint/media.d.ts +172 -0
- package/dist/internal/endpoint/migration.d.ts +50 -0
- package/dist/internal/endpoint/mls-crypto.d.ts +25 -0
- package/dist/internal/endpoint/mls-dev-client.d.ts +44 -0
- package/dist/internal/endpoint/mls-dev-device.d.ts +67 -0
- package/dist/internal/endpoint/mls-dev-groups.d.ts +114 -0
- package/dist/internal/endpoint/mls-dev-key-package-service.d.ts +33 -0
- package/dist/internal/endpoint/mls-dev-key-packages.d.ts +42 -0
- package/dist/internal/endpoint/mls-dev-transport.d.ts +37 -0
- package/dist/internal/endpoint/node-mls-custody-protocol.d.ts +24 -0
- package/dist/internal/endpoint/node-mls-custody.d.ts +15 -0
- package/dist/internal/endpoint/node.d.ts +7 -0
- package/dist/internal/endpoint/observe-query.d.ts +17 -0
- package/dist/internal/endpoint/portable-node-mls-custody.d.ts +23 -0
- package/dist/internal/endpoint/queue.d.ts +22 -0
- package/dist/internal/endpoint/recovery.d.ts +27 -0
- package/dist/internal/endpoint/retention.d.ts +7 -0
- package/dist/internal/endpoint/store.d.ts +22 -0
- package/dist/internal/endpoint/sync.d.ts +33 -0
- package/dist/internal/endpoint/transient-publisher.d.ts +8 -0
- package/dist/internal/endpoint/transport.d.ts +58 -0
- package/dist/media.d.ts +1 -1
- package/dist/media.js +42 -541
- package/dist/runner/runner.d.ts +37 -0
- package/dist/runner/types.d.ts +168 -0
- package/package.json +18 -5
- package/dist/attached-session.d.ts +0 -58
- package/dist/attached-session.js +0 -638
- package/dist/bounded-file-read.d.ts +0 -14
- package/dist/bounded-file-read.js +0 -34
- package/dist/canon-agent.d.ts +0 -238
- package/dist/canon-agent.js +0 -2534
- package/dist/debouncer.d.ts +0 -18
- package/dist/debouncer.js +0 -133
- package/dist/inbound-handoff.d.ts +0 -6
- package/dist/inbound-handoff.js +0 -17
- package/dist/managed-session-host.d.ts +0 -27
- package/dist/managed-session-host.js +0 -195
- package/dist/managed-session-setup.d.ts +0 -99
- package/dist/managed-session-setup.js +0 -234
- package/dist/realtime.d.ts +0 -62
- package/dist/realtime.js +0 -193
- package/dist/runtime-card.d.ts +0 -48
- package/dist/runtime-card.js +0 -64
- package/dist/session-manager.d.ts +0 -77
- package/dist/session-manager.js +0 -285
- package/dist/turn-streaming-request.d.ts +0 -17
- package/dist/turn-streaming-request.js +0 -22
- package/dist/turn-verbosity-option.d.ts +0 -33
- package/dist/turn-verbosity-option.js +0 -23
- package/dist/types.d.ts +0 -387
- package/dist/types.js +0 -1
- package/dist/work-session-coordination.d.ts +0 -21
- package/dist/work-session-coordination.js +0 -53
- package/dist/work-session-host.d.ts +0 -31
- package/dist/work-session-host.js +0 -617
- package/dist/work-session-interactions.d.ts +0 -12
- package/dist/work-session-interactions.js +0 -173
- package/dist/work-session-state.d.ts +0 -36
- package/dist/work-session-state.js +0 -62
package/dist/debouncer.d.ts
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import type { CanonMessage, CanonRuntimeProvenance, EndpointInboundHandoff } from '@canonmsg/core';
|
|
2
|
-
import { type InboundHandoffs } from './inbound-handoff.js';
|
|
3
|
-
export declare class Debouncer {
|
|
4
|
-
private debounceMs;
|
|
5
|
-
private pending;
|
|
6
|
-
private provenanceByConversation;
|
|
7
|
-
private handoffsByConversation;
|
|
8
|
-
private timers;
|
|
9
|
-
private orderedFlags;
|
|
10
|
-
private callback;
|
|
11
|
-
constructor(debounceMs: number);
|
|
12
|
-
setCallback(cb: (conversationId: string, messages: CanonMessage[], provenanceByMessageId: ReadonlyMap<string, CanonRuntimeProvenance>, handoffs: InboundHandoffs) => void | Promise<void>): void;
|
|
13
|
-
add(conversationId: string, message: CanonMessage, provenance?: CanonRuntimeProvenance | null, handoff?: EndpointInboundHandoff): void;
|
|
14
|
-
removeMessage(conversationId: string, messageId: string): boolean;
|
|
15
|
-
private flush;
|
|
16
|
-
dropConversation(conversationId: string): CanonMessage[];
|
|
17
|
-
destroy(): void;
|
|
18
|
-
}
|
package/dist/debouncer.js
DELETED
|
@@ -1,133 +0,0 @@
|
|
|
1
|
-
import { disposeInboundHandoffs } from './inbound-handoff.js';
|
|
2
|
-
export class Debouncer {
|
|
3
|
-
debounceMs;
|
|
4
|
-
pending = new Map();
|
|
5
|
-
provenanceByConversation = new Map();
|
|
6
|
-
handoffsByConversation = new Map();
|
|
7
|
-
timers = new Map();
|
|
8
|
-
// Track whether each conversation's pending messages are already in sorted
|
|
9
|
-
// order so we can skip the sort on flush when possible (#10)
|
|
10
|
-
orderedFlags = new Map();
|
|
11
|
-
callback = null;
|
|
12
|
-
constructor(debounceMs) {
|
|
13
|
-
this.debounceMs = debounceMs;
|
|
14
|
-
}
|
|
15
|
-
setCallback(cb) {
|
|
16
|
-
this.callback = cb;
|
|
17
|
-
}
|
|
18
|
-
add(conversationId, message, provenance, handoff) {
|
|
19
|
-
const existing = this.pending.get(conversationId) || [];
|
|
20
|
-
if (provenance) {
|
|
21
|
-
const provenanceByMessageId = this.provenanceByConversation.get(conversationId) ?? new Map();
|
|
22
|
-
provenanceByMessageId.set(message.id, provenance);
|
|
23
|
-
this.provenanceByConversation.set(conversationId, provenanceByMessageId);
|
|
24
|
-
}
|
|
25
|
-
if (handoff) {
|
|
26
|
-
const handoffs = this.handoffsByConversation.get(conversationId) ?? new Map();
|
|
27
|
-
handoffs.set(message.id, handoff);
|
|
28
|
-
this.handoffsByConversation.set(conversationId, handoffs);
|
|
29
|
-
}
|
|
30
|
-
// Deduplicate by message ID
|
|
31
|
-
if (!existing.some((m) => m.id === message.id)) {
|
|
32
|
-
if (existing.length === 0) {
|
|
33
|
-
// First message for this conversation — trivially ordered
|
|
34
|
-
this.orderedFlags.set(conversationId, true);
|
|
35
|
-
}
|
|
36
|
-
else if (this.orderedFlags.get(conversationId) !== false) {
|
|
37
|
-
// Still potentially ordered — check if new message maintains sort order
|
|
38
|
-
const lastTime = new Date(existing[existing.length - 1].createdAt).getTime();
|
|
39
|
-
const newTime = new Date(message.createdAt).getTime();
|
|
40
|
-
if (newTime < lastTime) {
|
|
41
|
-
this.orderedFlags.set(conversationId, false);
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
existing.push(message);
|
|
45
|
-
this.pending.set(conversationId, existing);
|
|
46
|
-
}
|
|
47
|
-
// Reset timer for this conversation
|
|
48
|
-
const existingTimer = this.timers.get(conversationId);
|
|
49
|
-
if (existingTimer)
|
|
50
|
-
clearTimeout(existingTimer);
|
|
51
|
-
this.timers.set(conversationId, setTimeout(() => {
|
|
52
|
-
this.flush(conversationId);
|
|
53
|
-
}, this.debounceMs));
|
|
54
|
-
}
|
|
55
|
-
removeMessage(conversationId, messageId) {
|
|
56
|
-
const existing = this.pending.get(conversationId);
|
|
57
|
-
if (!existing || existing.length === 0)
|
|
58
|
-
return false;
|
|
59
|
-
const next = existing.filter((message) => message.id !== messageId);
|
|
60
|
-
if (next.length === existing.length)
|
|
61
|
-
return false;
|
|
62
|
-
this.provenanceByConversation.get(conversationId)?.delete(messageId);
|
|
63
|
-
const handoff = this.handoffsByConversation.get(conversationId)?.get(messageId);
|
|
64
|
-
if (handoff)
|
|
65
|
-
void disposeInboundHandoffs(new Map([[messageId, handoff]]), 'cancel', 'message-deleted');
|
|
66
|
-
this.handoffsByConversation.get(conversationId)?.delete(messageId);
|
|
67
|
-
if (next.length === 0) {
|
|
68
|
-
this.pending.delete(conversationId);
|
|
69
|
-
this.provenanceByConversation.delete(conversationId);
|
|
70
|
-
this.handoffsByConversation.delete(conversationId);
|
|
71
|
-
this.orderedFlags.delete(conversationId);
|
|
72
|
-
const timer = this.timers.get(conversationId);
|
|
73
|
-
if (timer)
|
|
74
|
-
clearTimeout(timer);
|
|
75
|
-
this.timers.delete(conversationId);
|
|
76
|
-
}
|
|
77
|
-
else {
|
|
78
|
-
this.pending.set(conversationId, next);
|
|
79
|
-
}
|
|
80
|
-
return true;
|
|
81
|
-
}
|
|
82
|
-
flush(conversationId) {
|
|
83
|
-
const messages = this.pending.get(conversationId);
|
|
84
|
-
const isOrdered = this.orderedFlags.get(conversationId) ?? false;
|
|
85
|
-
this.pending.delete(conversationId);
|
|
86
|
-
this.timers.delete(conversationId);
|
|
87
|
-
this.orderedFlags.delete(conversationId);
|
|
88
|
-
if (messages && messages.length > 0 && this.callback) {
|
|
89
|
-
// Only sort if insertions arrived out of order (#10)
|
|
90
|
-
if (!isOrdered) {
|
|
91
|
-
messages.sort((a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime());
|
|
92
|
-
}
|
|
93
|
-
const provenanceByMessageId = this.provenanceByConversation.get(conversationId) ?? new Map();
|
|
94
|
-
const handoffs = this.handoffsByConversation.get(conversationId) ?? new Map();
|
|
95
|
-
this.provenanceByConversation.delete(conversationId);
|
|
96
|
-
this.handoffsByConversation.delete(conversationId);
|
|
97
|
-
const failed = async (error) => {
|
|
98
|
-
await disposeInboundHandoffs(handoffs, 'release', 'dispatch-failed');
|
|
99
|
-
console.error('[canon-sdk] Inbound dispatch failed:', error);
|
|
100
|
-
};
|
|
101
|
-
try {
|
|
102
|
-
void Promise.resolve(this.callback(conversationId, messages, provenanceByMessageId, handoffs)).catch(failed);
|
|
103
|
-
}
|
|
104
|
-
catch (error) {
|
|
105
|
-
void failed(error);
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
else {
|
|
109
|
-
void disposeInboundHandoffs(this.handoffsByConversation.get(conversationId) ?? new Map(), 'release', 'no-dispatcher');
|
|
110
|
-
this.provenanceByConversation.delete(conversationId);
|
|
111
|
-
this.handoffsByConversation.delete(conversationId);
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
dropConversation(conversationId) {
|
|
115
|
-
const messages = this.pending.get(conversationId) ?? [];
|
|
116
|
-
for (const message of messages)
|
|
117
|
-
this.removeMessage(conversationId, message.id);
|
|
118
|
-
return messages;
|
|
119
|
-
}
|
|
120
|
-
destroy() {
|
|
121
|
-
for (const handoffs of this.handoffsByConversation.values()) {
|
|
122
|
-
void disposeInboundHandoffs(handoffs, 'release', 'debouncer-closed');
|
|
123
|
-
}
|
|
124
|
-
this.handoffsByConversation.clear();
|
|
125
|
-
for (const timer of this.timers.values()) {
|
|
126
|
-
clearTimeout(timer);
|
|
127
|
-
}
|
|
128
|
-
this.timers.clear();
|
|
129
|
-
this.pending.clear();
|
|
130
|
-
this.provenanceByConversation.clear();
|
|
131
|
-
this.orderedFlags.clear();
|
|
132
|
-
}
|
|
133
|
-
}
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import type { EndpointInboundHandoff } from '@canonmsg/core';
|
|
2
|
-
/** Message-keyed handles within one conversation, carried with its queue entry. */
|
|
3
|
-
export type InboundHandoffs = ReadonlyMap<string, EndpointInboundHandoff>;
|
|
4
|
-
export declare function selectInboundHandoffs(handoffs: InboundHandoffs, messageIds: readonly string[]): InboundHandoffs;
|
|
5
|
-
/** Calls record intent synchronously, even when a durable claim is awaiting IO. */
|
|
6
|
-
export declare function disposeInboundHandoffs(handoffs: InboundHandoffs, disposition: 'cancel' | 'release' | 'settle', reason: string): Promise<void>;
|
package/dist/inbound-handoff.js
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
export function selectInboundHandoffs(handoffs, messageIds) {
|
|
2
|
-
const selected = new Map();
|
|
3
|
-
for (const id of messageIds) {
|
|
4
|
-
const handoff = handoffs.get(id);
|
|
5
|
-
if (handoff)
|
|
6
|
-
selected.set(id, handoff);
|
|
7
|
-
}
|
|
8
|
-
return selected;
|
|
9
|
-
}
|
|
10
|
-
/** Calls record intent synchronously, even when a durable claim is awaiting IO. */
|
|
11
|
-
export async function disposeInboundHandoffs(handoffs, disposition, reason) {
|
|
12
|
-
const results = await Promise.allSettled([...handoffs.values()].map((handoff) => handoff[disposition](reason)));
|
|
13
|
-
for (const result of results) {
|
|
14
|
-
if (result.status === 'rejected')
|
|
15
|
-
console.error(`[canon-sdk] Inbound ${disposition} failed:`, result.reason);
|
|
16
|
-
}
|
|
17
|
-
}
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { CanonClient, type WorkSessionBinding, type WorkSessionCatalog, type WorkSessionDetailFields, type WorkSessionRequest, type WorkSessionSettings } from '@canonmsg/core';
|
|
2
|
-
import { type WorkSessionHostStore } from './work-session-state.js';
|
|
3
|
-
export interface CanonManagedSessionHostOptions {
|
|
4
|
-
client: CanonClient;
|
|
5
|
-
agentId: string;
|
|
6
|
-
environmentId: string;
|
|
7
|
-
hostId: string;
|
|
8
|
-
displayName: string;
|
|
9
|
-
store: WorkSessionHostStore;
|
|
10
|
-
catalog(): Promise<WorkSessionCatalog>;
|
|
11
|
-
/** Must durably save the resolved selection before returning. May be called again after a crash. */
|
|
12
|
-
configure(request: WorkSessionRequest): Promise<{
|
|
13
|
-
settings: WorkSessionSettings;
|
|
14
|
-
fields: WorkSessionDetailFields;
|
|
15
|
-
}>;
|
|
16
|
-
onError?: (error: unknown, operation: string) => void;
|
|
17
|
-
pollIntervalMs?: number;
|
|
18
|
-
heartbeatIntervalMs?: number;
|
|
19
|
-
}
|
|
20
|
-
/** Broker coordination for a managed host whose ordinary message loop owns turns. */
|
|
21
|
-
export declare function createCanonManagedSessionHost(options: CanonManagedSessionHostOptions): {
|
|
22
|
-
start: () => Promise<void>;
|
|
23
|
-
stop: () => Promise<void>;
|
|
24
|
-
reconcile: () => Promise<void>;
|
|
25
|
-
report: (conversationId: string, fields: WorkSessionDetailFields, setupState?: "configured" | "attached") => Promise<void>;
|
|
26
|
-
binding: (conversationId: string) => WorkSessionBinding | undefined;
|
|
27
|
-
};
|
|
@@ -1,195 +0,0 @@
|
|
|
1
|
-
import { randomUUID } from 'node:crypto';
|
|
2
|
-
import { CanonApiError, } from '@canonmsg/core';
|
|
3
|
-
import { createWorkSessionJournal } from './work-session-state.js';
|
|
4
|
-
import { createWorkSessionAuthority, fingerprintWorkSessionRequest } from './work-session-coordination.js';
|
|
5
|
-
/** Broker coordination for a managed host whose ordinary message loop owns turns. */
|
|
6
|
-
export function createCanonManagedSessionHost(options) {
|
|
7
|
-
const journal = createWorkSessionJournal(options.store, {
|
|
8
|
-
environmentId: options.environmentId, agentId: options.agentId, hostId: options.hostId,
|
|
9
|
-
});
|
|
10
|
-
const runtimeEpoch = randomUUID();
|
|
11
|
-
const pollMs = options.pollIntervalMs ?? 2_000;
|
|
12
|
-
const heartbeatMs = options.heartbeatIntervalMs ?? 25_000;
|
|
13
|
-
if (!options.hostId || !Number.isFinite(pollMs) || pollMs <= 0 || !Number.isFinite(heartbeatMs)
|
|
14
|
-
|| heartbeatMs <= 0 || heartbeatMs > 30_000)
|
|
15
|
-
throw new Error('Invalid managed-session host configuration.');
|
|
16
|
-
let broker;
|
|
17
|
-
let stopped = false;
|
|
18
|
-
const authority = createWorkSessionAuthority({ client: options.client, hostId: options.hostId,
|
|
19
|
-
runtimeEpoch, displayName: options.displayName, isStopped: () => stopped });
|
|
20
|
-
let started = false;
|
|
21
|
-
let pollTimer;
|
|
22
|
-
let heartbeatTimer;
|
|
23
|
-
let processing;
|
|
24
|
-
let renewing;
|
|
25
|
-
let starting;
|
|
26
|
-
const reportError = (error, operation) => { try {
|
|
27
|
-
options.onError?.(error, operation);
|
|
28
|
-
}
|
|
29
|
-
catch { /* observer */ } };
|
|
30
|
-
const auth = authority.auth;
|
|
31
|
-
const fence = authority.fence;
|
|
32
|
-
function retain(state) {
|
|
33
|
-
broker = authority.retain(state);
|
|
34
|
-
}
|
|
35
|
-
async function processRequest(request, replayed) {
|
|
36
|
-
if (request.status !== 'claimed' && request.status !== 'uncertain')
|
|
37
|
-
return;
|
|
38
|
-
fence();
|
|
39
|
-
const hash = fingerprintWorkSessionRequest(request);
|
|
40
|
-
let configuredFields;
|
|
41
|
-
let entry = journal.read().operations.find((operation) => operation.request.requestId === request.requestId);
|
|
42
|
-
if (entry && entry.fingerprint !== hash)
|
|
43
|
-
throw new Error('Managed-session request changed during recovery.');
|
|
44
|
-
if (!entry) {
|
|
45
|
-
entry = { request, fingerprint: hash, phase: 'configuring',
|
|
46
|
-
...(replayed ? { completion: { status: 'uncertain', error: { code: 'local_state_missing',
|
|
47
|
-
message: 'The host lost the saved setup result. Inspect this local runtime before trying again.' } }, phase: 'complete' } : {}),
|
|
48
|
-
};
|
|
49
|
-
await journal.mutate((state) => { state.operations.push(entry); });
|
|
50
|
-
}
|
|
51
|
-
if (!entry.completion) {
|
|
52
|
-
if (request.selection.mode !== 'configure') {
|
|
53
|
-
entry.completion = { status: 'failed', error: { code: 'unsupported_setup', message: 'This host only supports managed setup.' } };
|
|
54
|
-
}
|
|
55
|
-
else {
|
|
56
|
-
try {
|
|
57
|
-
const result = await options.configure(request);
|
|
58
|
-
fence();
|
|
59
|
-
entry.completion = { status: 'configured', settings: result.settings };
|
|
60
|
-
configuredFields = result.fields;
|
|
61
|
-
await journal.mutate((state) => {
|
|
62
|
-
const saved = state.operations.find((operation) => operation.request.requestId === request.requestId);
|
|
63
|
-
saved.phase = 'complete';
|
|
64
|
-
saved.completion = entry.completion;
|
|
65
|
-
});
|
|
66
|
-
}
|
|
67
|
-
catch (error) {
|
|
68
|
-
reportError(error, 'configure');
|
|
69
|
-
entry.completion = { status: 'failed', error: { code: 'managed_configuration_failed',
|
|
70
|
-
message: (error instanceof Error ? error.message : String(error)).slice(0, 512) } };
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
if (entry.completion.status !== 'configured') {
|
|
74
|
-
await journal.mutate((state) => {
|
|
75
|
-
const saved = state.operations.find((operation) => operation.request.requestId === request.requestId);
|
|
76
|
-
saved.phase = 'complete';
|
|
77
|
-
saved.completion = entry.completion;
|
|
78
|
-
});
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
fence();
|
|
82
|
-
const completed = await authority.acknowledge(journal, request.requestId, entry.completion);
|
|
83
|
-
if (completed.binding) {
|
|
84
|
-
broker.bindings = [...broker.bindings.filter((binding) => binding.conversationId !== completed.conversationId), completed.binding];
|
|
85
|
-
}
|
|
86
|
-
if (configuredFields && completed.status === 'configured') {
|
|
87
|
-
await report(request.conversationId, configuredFields).catch((error) => reportError(error, 'report-configured'));
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
async function doReconcile() {
|
|
91
|
-
fence();
|
|
92
|
-
const outstanding = journal.read().operations.find((entry) => !entry.acknowledged && !entry.quarantined);
|
|
93
|
-
let claimed;
|
|
94
|
-
try {
|
|
95
|
-
claimed = await authority.claim(outstanding?.request.requestId);
|
|
96
|
-
}
|
|
97
|
-
catch (error) {
|
|
98
|
-
if (outstanding && error instanceof CanonApiError && error.code === 'WORK_SESSION_MEMBERSHIP_REQUIRED') {
|
|
99
|
-
await journal.mutate((state) => {
|
|
100
|
-
state.operations.find((entry) => entry.request.requestId === outstanding.request.requestId).quarantined = 'Conversation membership was removed.';
|
|
101
|
-
});
|
|
102
|
-
return;
|
|
103
|
-
}
|
|
104
|
-
throw error;
|
|
105
|
-
}
|
|
106
|
-
fence();
|
|
107
|
-
if (!claimed.request) {
|
|
108
|
-
if (outstanding)
|
|
109
|
-
await journal.mutate((state) => {
|
|
110
|
-
state.operations.find((entry) => entry.request.requestId === outstanding.request.requestId).quarantined = 'Request no longer belongs to this runtime.';
|
|
111
|
-
});
|
|
112
|
-
return;
|
|
113
|
-
}
|
|
114
|
-
if (['configured', 'failed', 'expired'].includes(claimed.request.status)) {
|
|
115
|
-
if (outstanding)
|
|
116
|
-
await journal.mutate((state) => {
|
|
117
|
-
state.operations.find((entry) => entry.request.requestId === outstanding.request.requestId).acknowledged = true;
|
|
118
|
-
});
|
|
119
|
-
if (claimed.request.binding) {
|
|
120
|
-
broker.bindings = [...broker.bindings.filter((binding) => binding.conversationId !== claimed.request.conversationId), claimed.request.binding];
|
|
121
|
-
}
|
|
122
|
-
return;
|
|
123
|
-
}
|
|
124
|
-
await processRequest(claimed.request, claimed.replayed);
|
|
125
|
-
}
|
|
126
|
-
function reconcile() {
|
|
127
|
-
if (stopped || !started)
|
|
128
|
-
return Promise.resolve();
|
|
129
|
-
processing ??= doReconcile().finally(() => { processing = undefined; });
|
|
130
|
-
return processing;
|
|
131
|
-
}
|
|
132
|
-
async function renew() {
|
|
133
|
-
fence();
|
|
134
|
-
const state = await authority.heartbeat(await options.catalog());
|
|
135
|
-
if (!stopped)
|
|
136
|
-
retain(state);
|
|
137
|
-
}
|
|
138
|
-
function start() {
|
|
139
|
-
if (stopped)
|
|
140
|
-
throw new Error('Managed-session host already stopped.');
|
|
141
|
-
if (started && broker && broker.lease.expiresAt - Date.now() >= 5_000)
|
|
142
|
-
return Promise.resolve();
|
|
143
|
-
starting ??= (async () => {
|
|
144
|
-
if (!started)
|
|
145
|
-
await journal.load();
|
|
146
|
-
retain(await authority.register(await options.catalog()));
|
|
147
|
-
if (stopped)
|
|
148
|
-
return;
|
|
149
|
-
if (!started) {
|
|
150
|
-
started = true;
|
|
151
|
-
heartbeatTimer = setInterval(() => {
|
|
152
|
-
if (!renewing)
|
|
153
|
-
renewing = renew().catch((error) => reportError(error, 'lease-renewal')).finally(() => { renewing = undefined; });
|
|
154
|
-
}, heartbeatMs);
|
|
155
|
-
heartbeatTimer.unref?.();
|
|
156
|
-
pollTimer = setInterval(() => { void reconcile().catch((error) => reportError(error, 'request')); }, pollMs);
|
|
157
|
-
pollTimer.unref?.();
|
|
158
|
-
}
|
|
159
|
-
await reconcile();
|
|
160
|
-
})().finally(() => { starting = undefined; });
|
|
161
|
-
return starting;
|
|
162
|
-
}
|
|
163
|
-
async function report(conversationId, fields, setupState) {
|
|
164
|
-
fence();
|
|
165
|
-
const binding = broker.bindings.find((entry) => entry.conversationId === conversationId);
|
|
166
|
-
await options.client.reportWorkSessionDetails({ ...auth(), conversationId,
|
|
167
|
-
...(binding ? { workSessionId: binding.workSessionId } : {}),
|
|
168
|
-
...(setupState ? { setupState } : binding ? { setupState: binding.kind === 'managed' ? 'configured' : 'attached' } : {}),
|
|
169
|
-
fields });
|
|
170
|
-
}
|
|
171
|
-
async function stop() {
|
|
172
|
-
if (stopped)
|
|
173
|
-
return;
|
|
174
|
-
stopped = true;
|
|
175
|
-
if (pollTimer)
|
|
176
|
-
clearInterval(pollTimer);
|
|
177
|
-
if (heartbeatTimer)
|
|
178
|
-
clearInterval(heartbeatTimer);
|
|
179
|
-
await Promise.allSettled([starting, processing, renewing].filter(Boolean));
|
|
180
|
-
try {
|
|
181
|
-
if (broker)
|
|
182
|
-
await options.client.releaseWorkSessionRuntime(auth());
|
|
183
|
-
}
|
|
184
|
-
catch (error) {
|
|
185
|
-
reportError(error, 'release-runtime');
|
|
186
|
-
}
|
|
187
|
-
await journal.close();
|
|
188
|
-
}
|
|
189
|
-
function binding(conversationId) {
|
|
190
|
-
if (stopped || !broker || broker.lease.expiresAt - Date.now() < 5_000)
|
|
191
|
-
return undefined;
|
|
192
|
-
return broker.bindings.find((entry) => entry.conversationId === conversationId && entry.kind === 'managed');
|
|
193
|
-
}
|
|
194
|
-
return { start, stop, reconcile, report, binding };
|
|
195
|
-
}
|
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
import { type ExecutionEnvironmentMode, type LocalRuntimeResumeField, type LocalRuntimeSessionState, type WorkSessionCatalog, type WorkSessionChoice, type WorkSessionDetailFields, type WorkSessionModelChoice, type WorkSessionRequest, type WorkSessionSettings } from '@canonmsg/core';
|
|
2
|
-
/**
|
|
3
|
-
* Owner-confirmed ("managed") setup for a coding host whose own message loop
|
|
4
|
-
* runs the turns. The broker side lives in `createCanonManagedSessionHost`;
|
|
5
|
-
* these helpers are the runtime-neutral host side: the catalog it offers, the
|
|
6
|
-
* idempotent `configure`, the saved-state fences and the private details it
|
|
7
|
-
* reports. A runtime supplies its labels, models and permission mapping.
|
|
8
|
-
*/
|
|
9
|
-
export interface ManagedSessionProject {
|
|
10
|
-
id: string;
|
|
11
|
-
label: string;
|
|
12
|
-
cwd: string;
|
|
13
|
-
}
|
|
14
|
-
/** How a runtime names itself and its native session in owner-facing errors. */
|
|
15
|
-
export interface ManagedSessionRuntime {
|
|
16
|
-
/** e.g. `Codex`, `Claude Code`. */
|
|
17
|
-
label: string;
|
|
18
|
-
/** e.g. `thread`, `session`. */
|
|
19
|
-
nativeSession: string;
|
|
20
|
-
/** The local runtime catalog field holding the native session id. */
|
|
21
|
-
resumeField: LocalRuntimeResumeField;
|
|
22
|
-
}
|
|
23
|
-
/** Runtime-specific configured facts shown beside the generic settings. */
|
|
24
|
-
export interface ManagedSessionConfiguredFacts {
|
|
25
|
-
sandbox?: string | null;
|
|
26
|
-
approvalPolicy?: string | null;
|
|
27
|
-
nativeThreadBehavior?: string;
|
|
28
|
-
}
|
|
29
|
-
export declare const MANAGED_EXECUTION_MODE_LABELS: Readonly<Record<ExecutionEnvironmentMode, string>>;
|
|
30
|
-
export declare function buildManagedSessionCatalog(input: {
|
|
31
|
-
provider: string;
|
|
32
|
-
projects: readonly ManagedSessionProject[];
|
|
33
|
-
models: readonly WorkSessionModelChoice[];
|
|
34
|
-
defaultModelId?: string;
|
|
35
|
-
defaultReasoningEffort?: string | null;
|
|
36
|
-
/** Catalog-wide thinking levels, for models that list none of their own. */
|
|
37
|
-
reasoningEfforts?: readonly WorkSessionChoice[];
|
|
38
|
-
permissionModes: readonly WorkSessionChoice[];
|
|
39
|
-
defaultPermissionMode: string;
|
|
40
|
-
executionModes: readonly ExecutionEnvironmentMode[];
|
|
41
|
-
defaultExecutionMode: ExecutionEnvironmentMode;
|
|
42
|
-
}): WorkSessionCatalog;
|
|
43
|
-
/** The one saved setup for a conversation, or null; conflicting copies fail closed. */
|
|
44
|
-
export declare function findManagedSessionState(runtimeId: string, conversationId: string, runtime: Pick<ManagedSessionRuntime, 'label'>): LocalRuntimeSessionState | null;
|
|
45
|
-
export declare function hasSavedNativeSession(runtimeId: string, conversationId: string, resumeField: LocalRuntimeResumeField): boolean;
|
|
46
|
-
/** Older records without ownership fields stay usable; a different owner generation does not. */
|
|
47
|
-
export declare function savedSessionMatchesOwner(state: LocalRuntimeSessionState, ownerId: string | null, ownershipVersion: number | undefined): boolean;
|
|
48
|
-
export declare function inspectGitEnvironment(cwd: string): Pick<WorkSessionDetailFields, 'branch' | 'worktreePath'>;
|
|
49
|
-
/** Private owner details for a running session: confirmed settings, then what the runtime and Git observe. */
|
|
50
|
-
export declare function managedSessionFields(input: {
|
|
51
|
-
settings: WorkSessionSettings;
|
|
52
|
-
cwd: string;
|
|
53
|
-
projectLabel?: string;
|
|
54
|
-
configured?: ManagedSessionConfiguredFacts;
|
|
55
|
-
observedModel?: string;
|
|
56
|
-
observedEffort?: string;
|
|
57
|
-
}): WorkSessionDetailFields;
|
|
58
|
-
/** Private details for a running session nobody set up: what the host and Git observe. */
|
|
59
|
-
export declare function observedSessionFields(input: {
|
|
60
|
-
cwd: string;
|
|
61
|
-
executionMode?: string;
|
|
62
|
-
project?: {
|
|
63
|
-
id: string;
|
|
64
|
-
label: string;
|
|
65
|
-
};
|
|
66
|
-
observedModel?: string;
|
|
67
|
-
observedEffort?: string;
|
|
68
|
-
}): WorkSessionDetailFields;
|
|
69
|
-
/** Read only: details for a saved session with no live native process, if its directory still exists. */
|
|
70
|
-
export declare function savedSessionFields(input: {
|
|
71
|
-
state: LocalRuntimeSessionState;
|
|
72
|
-
agentId: string;
|
|
73
|
-
projects: readonly ManagedSessionProject[];
|
|
74
|
-
describeConfigured?: (settings: WorkSessionSettings) => ManagedSessionConfiguredFacts;
|
|
75
|
-
}): WorkSessionDetailFields;
|
|
76
|
-
/**
|
|
77
|
-
* Idempotent configuration for `createCanonManagedSessionHost`. Validates the
|
|
78
|
-
* owner's selection against the catalog, prepares the requested environment
|
|
79
|
-
* (a requested worktree that cannot be created fails instead of falling back)
|
|
80
|
-
* and saves the setup before the broker is acknowledged. A replayed request
|
|
81
|
-
* returns the saved setup; a conversation that already has a native session or
|
|
82
|
-
* a different confirmed setup keeps it.
|
|
83
|
-
*/
|
|
84
|
-
export declare function configureManagedSession(input: {
|
|
85
|
-
request: WorkSessionRequest;
|
|
86
|
-
runtimeId: string;
|
|
87
|
-
agentId: string;
|
|
88
|
-
catalog: WorkSessionCatalog;
|
|
89
|
-
projects: readonly ManagedSessionProject[];
|
|
90
|
-
runtime: ManagedSessionRuntime;
|
|
91
|
-
ownerId?: string;
|
|
92
|
-
ownershipVersion?: number;
|
|
93
|
-
/** False when the runtime cannot apply a catalog permission mode with the selected model. */
|
|
94
|
-
isPermissionModeApplicable?: (permissionMode: string, modelId: string | undefined) => boolean;
|
|
95
|
-
describeConfigured?: (settings: WorkSessionSettings) => ManagedSessionConfiguredFacts;
|
|
96
|
-
}): {
|
|
97
|
-
settings: WorkSessionSettings;
|
|
98
|
-
fields: WorkSessionDetailFields;
|
|
99
|
-
};
|