@canonmsg/core 0.4.0 → 0.6.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/dist/browser.d.ts +2 -0
- package/dist/browser.js +1 -0
- package/dist/client.d.ts +6 -0
- package/dist/client.js +48 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/registration.js +1 -0
- package/dist/types.d.ts +7 -0
- package/dist/work-session.d.ts +88 -0
- package/dist/work-session.js +238 -0
- package/package.json +1 -1
package/dist/browser.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export { AGENT_CAPABILITIES, } from './types.js';
|
|
2
2
|
export type { AgentCapabilities, AgentClientType, AgentRuntime, MediaAttachment, MediaAttachmentKind, ModelOption, SessionConfig, WorkspaceOption, } from './types.js';
|
|
3
|
+
export type { CanonResolvedWorkSession, CanonWorkSession, CanonWorkSessionContext, CanonWorkSessionConversationRole, CanonWorkSessionDisclosureMode, CanonWorkSessionParticipant, CanonWorkSessionStatus, CreateWorkSessionOptions, SendLinkedMessageOptions, SendLinkedMessageResult, UpdateWorkSessionConversationOptions, WorkSessionPromptRenderOptions, } from './work-session.js';
|
|
4
|
+
export { buildWorkSessionPromptLines, buildWorkSessionsPromptLines, mergeWorkSessionContexts, } from './work-session.js';
|
|
3
5
|
export type { AgentBehaviorSettings, ParticipationHistoryMessage, ParticipationHistorySnapshot, ParticipationStyle, ResolvedAgentBehaviorPolicy, } from './policy.js';
|
|
4
6
|
export { buildParticipationHistorySnapshot, buildParticipationHistorySnapshots, buildBehaviorPolicyLines, DEFAULT_PARTICIPATION_HISTORY_FETCH_LIMIT, evaluateParticipationPolicy, getDefaultParticipationPolicy, resolveAgentBehaviorPolicy, } from './policy.js';
|
|
5
7
|
export { DEFAULT_RUNTIME_CAPABILITIES, FINAL_MESSAGE_HANDOFF_MS, isTurnOpen, normalizeTurnMetadata, normalizeTurnState, resolveTurnMessageSemantics, shouldPromoteConversationMessage, shouldTriggerAgentTurn, } from './turn-protocol.js';
|
package/dist/browser.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
export { AGENT_CAPABILITIES, } from './types.js';
|
|
2
|
+
export { buildWorkSessionPromptLines, buildWorkSessionsPromptLines, mergeWorkSessionContexts, } from './work-session.js';
|
|
2
3
|
export { buildParticipationHistorySnapshot, buildParticipationHistorySnapshots, buildBehaviorPolicyLines, DEFAULT_PARTICIPATION_HISTORY_FETCH_LIMIT, evaluateParticipationPolicy, getDefaultParticipationPolicy, resolveAgentBehaviorPolicy, } from './policy.js';
|
|
3
4
|
export { DEFAULT_RUNTIME_CAPABILITIES, FINAL_MESSAGE_HANDOFF_MS, isTurnOpen, normalizeTurnMetadata, normalizeTurnState, resolveTurnMessageSemantics, shouldPromoteConversationMessage, shouldTriggerAgentTurn, } from './turn-protocol.js';
|
package/dist/client.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type CanonMessage, type CanonConversation, type CanonMessagesPage, type AgentContext, type MediaAttachment, type SendMessageOptions, type CreateConversationOptions, type RegistrationStatus, type SetStreamingOptions } from './types.js';
|
|
2
|
+
import type { CanonResolvedWorkSession, CreateWorkSessionOptions, SendLinkedMessageOptions, SendLinkedMessageResult, UpdateWorkSessionConversationOptions } from './work-session.js';
|
|
2
3
|
import type { InboundDisposition } from './turn-protocol.js';
|
|
3
4
|
/**
|
|
4
5
|
* Thin REST client for Canon's agent API.
|
|
@@ -21,6 +22,10 @@ export declare class CanonClient {
|
|
|
21
22
|
sendMessage(conversationId: string, text: string, options?: SendMessageOptions): Promise<{
|
|
22
23
|
messageId: string;
|
|
23
24
|
}>;
|
|
25
|
+
createWorkSession(options: CreateWorkSessionOptions): Promise<CanonResolvedWorkSession>;
|
|
26
|
+
getWorkSession(workSessionId: string, conversationId: string): Promise<CanonResolvedWorkSession>;
|
|
27
|
+
upsertWorkSessionConversation(workSessionId: string, conversationId: string, options?: UpdateWorkSessionConversationOptions): Promise<CanonResolvedWorkSession>;
|
|
28
|
+
sendLinkedMessage(options: SendLinkedMessageOptions): Promise<SendLinkedMessageResult>;
|
|
24
29
|
createConversation(options: CreateConversationOptions): Promise<{
|
|
25
30
|
conversationId: string;
|
|
26
31
|
}>;
|
|
@@ -47,6 +52,7 @@ export declare class CanonClient {
|
|
|
47
52
|
developerInfo: string;
|
|
48
53
|
avatarUrl?: string;
|
|
49
54
|
clientType?: string;
|
|
55
|
+
requestedAgentId?: string;
|
|
50
56
|
}): Promise<{
|
|
51
57
|
requestId: string;
|
|
52
58
|
}>;
|
package/dist/client.js
CHANGED
|
@@ -73,6 +73,54 @@ export class CanonClient {
|
|
|
73
73
|
throw new CanonApiError(res.status, await res.text());
|
|
74
74
|
return res.json();
|
|
75
75
|
}
|
|
76
|
+
async createWorkSession(options) {
|
|
77
|
+
const res = await fetch(`${this.baseUrl}/work-sessions`, {
|
|
78
|
+
method: 'POST',
|
|
79
|
+
headers: this.authHeaders(),
|
|
80
|
+
body: JSON.stringify(options),
|
|
81
|
+
});
|
|
82
|
+
if (!res.ok)
|
|
83
|
+
throw new CanonApiError(res.status, await res.text());
|
|
84
|
+
return res.json();
|
|
85
|
+
}
|
|
86
|
+
async getWorkSession(workSessionId, conversationId) {
|
|
87
|
+
const params = new URLSearchParams({ conversationId });
|
|
88
|
+
const res = await fetch(`${this.baseUrl}/work-sessions/${workSessionId}?${params}`, { headers: this.authHeaders() });
|
|
89
|
+
if (!res.ok)
|
|
90
|
+
throw new CanonApiError(res.status, await res.text());
|
|
91
|
+
return res.json();
|
|
92
|
+
}
|
|
93
|
+
async upsertWorkSessionConversation(workSessionId, conversationId, options) {
|
|
94
|
+
const res = await fetch(`${this.baseUrl}/work-sessions/${workSessionId}/conversations/${conversationId}`, {
|
|
95
|
+
method: 'PUT',
|
|
96
|
+
headers: this.authHeaders(),
|
|
97
|
+
body: JSON.stringify(options ?? {}),
|
|
98
|
+
});
|
|
99
|
+
if (!res.ok)
|
|
100
|
+
throw new CanonApiError(res.status, await res.text());
|
|
101
|
+
return res.json();
|
|
102
|
+
}
|
|
103
|
+
async sendLinkedMessage(options) {
|
|
104
|
+
const res = await fetch(`${this.baseUrl}/messages/send-linked`, {
|
|
105
|
+
method: 'POST',
|
|
106
|
+
headers: this.authHeaders(),
|
|
107
|
+
body: JSON.stringify({
|
|
108
|
+
sourceConversationId: options.sourceConversationId,
|
|
109
|
+
targetConversationId: options.targetConversationId,
|
|
110
|
+
text: options.text,
|
|
111
|
+
...(options.workSessionId ? { workSessionId: options.workSessionId } : {}),
|
|
112
|
+
...(options.createWorkSession
|
|
113
|
+
? { createWorkSession: options.createWorkSession }
|
|
114
|
+
: {}),
|
|
115
|
+
...(options.sourceContext ? { sourceContext: options.sourceContext } : {}),
|
|
116
|
+
...(options.targetContext ? { targetContext: options.targetContext } : {}),
|
|
117
|
+
...(options.messageOptions ? { messageOptions: options.messageOptions } : {}),
|
|
118
|
+
}),
|
|
119
|
+
});
|
|
120
|
+
if (!res.ok)
|
|
121
|
+
throw new CanonApiError(res.status, await res.text());
|
|
122
|
+
return res.json();
|
|
123
|
+
}
|
|
76
124
|
async createConversation(options) {
|
|
77
125
|
const res = await fetch(`${this.baseUrl}/conversations/create`, {
|
|
78
126
|
method: 'POST',
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export { AGENT_CAPABILITIES, } from './types.js';
|
|
2
2
|
export type { AgentCapabilities, AgentClientType, CanonMessage, CanonConversation, CanonMessagesPage, AgentContext, MediaAttachment, MediaAttachmentKind, MessageCreatedPayload, TypingPayload, PresencePayload, SendMessageOptions, CreateConversationOptions, RegistrationInput, RegistrationResult, RegistrationStatus, StreamingStatus, SetStreamingOptions, SessionControl, SessionState, SessionConfig, AgentRuntime, ModelOption, WorkspaceOption, } from './types.js';
|
|
3
|
+
export type { CanonResolvedWorkSession, CanonWorkSession, CanonWorkSessionContext, CanonWorkSessionConversationRole, CreateWorkSessionOptions, CanonWorkSessionDisclosureMode, CanonWorkSessionParticipant, CanonWorkSessionStatus, SendLinkedMessageOptions, SendLinkedMessageResult, UpdateWorkSessionConversationOptions, WorkSessionPromptRenderOptions, } from './work-session.js';
|
|
4
|
+
export { buildWorkSessionPromptLines, buildWorkSessionsPromptLines, mergeWorkSessionContexts, } from './work-session.js';
|
|
3
5
|
export { CanonClient, CanonApiError } from './client.js';
|
|
4
6
|
export { CanonStream } from './stream.js';
|
|
5
7
|
export type { StreamHandler } from './stream.js';
|
package/dist/index.js
CHANGED
package/dist/registration.js
CHANGED
|
@@ -17,6 +17,7 @@ export async function registerAndWaitForApproval(input, callbacks) {
|
|
|
17
17
|
developerInfo: input.developerInfo || 'Canon agent',
|
|
18
18
|
avatarUrl: input.avatarUrl,
|
|
19
19
|
clientType: input.clientType,
|
|
20
|
+
requestedAgentId: input.requestedAgentId,
|
|
20
21
|
});
|
|
21
22
|
callbacks?.onSubmitted?.(requestId);
|
|
22
23
|
const deadline = Date.now() + POLL_TIMEOUT_MS;
|
package/dist/types.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ResolvedAgentBehaviorPolicy } from './policy.js';
|
|
2
|
+
import type { CanonWorkSessionContext } from './work-session.js';
|
|
2
3
|
export type MediaAttachmentKind = 'image' | 'audio' | 'file';
|
|
3
4
|
export interface MediaAttachment {
|
|
4
5
|
kind: MediaAttachmentKind;
|
|
@@ -25,6 +26,7 @@ export interface CanonMessage {
|
|
|
25
26
|
mentions: string[];
|
|
26
27
|
replyTo: string | null;
|
|
27
28
|
replyToPosition: number | null;
|
|
29
|
+
workSession?: CanonWorkSessionContext | null;
|
|
28
30
|
metadata?: Record<string, unknown>;
|
|
29
31
|
status: 'sent' | 'read';
|
|
30
32
|
deleted: boolean;
|
|
@@ -50,6 +52,7 @@ export interface CanonConversation {
|
|
|
50
52
|
export interface CanonMessagesPage {
|
|
51
53
|
messages: CanonMessage[];
|
|
52
54
|
behavior?: ResolvedAgentBehaviorPolicy;
|
|
55
|
+
workSessions?: CanonWorkSessionContext[];
|
|
53
56
|
}
|
|
54
57
|
export type AgentClientType = 'claude-code' | 'openclaw' | 'codex' | 'generic';
|
|
55
58
|
/** Declares what session controls an agent type supports. */
|
|
@@ -88,6 +91,7 @@ export interface AgentContext {
|
|
|
88
91
|
export interface MessageCreatedPayload {
|
|
89
92
|
conversationId: string;
|
|
90
93
|
behavior?: ResolvedAgentBehaviorPolicy;
|
|
94
|
+
workSessions?: CanonWorkSessionContext[];
|
|
91
95
|
message: {
|
|
92
96
|
id: string;
|
|
93
97
|
senderId: string;
|
|
@@ -105,6 +109,7 @@ export interface MessageCreatedPayload {
|
|
|
105
109
|
replyToPosition?: number;
|
|
106
110
|
mentions?: string[];
|
|
107
111
|
createdAt?: string;
|
|
112
|
+
workSession?: CanonWorkSessionContext | null;
|
|
108
113
|
/** Structured metadata for rich UI (approval cards, etc.) */
|
|
109
114
|
metadata?: Record<string, unknown>;
|
|
110
115
|
};
|
|
@@ -129,6 +134,7 @@ export interface SendMessageOptions {
|
|
|
129
134
|
attachments?: MediaAttachment[];
|
|
130
135
|
contactCardUserId?: string;
|
|
131
136
|
mentions?: string[];
|
|
137
|
+
workSessionId?: string;
|
|
132
138
|
/** Structured metadata for rich UI (approval cards, etc.) */
|
|
133
139
|
metadata?: Record<string, unknown>;
|
|
134
140
|
}
|
|
@@ -202,6 +208,7 @@ export interface RegistrationInput {
|
|
|
202
208
|
avatarUrl?: string;
|
|
203
209
|
baseUrl?: string;
|
|
204
210
|
clientType?: AgentClientType;
|
|
211
|
+
requestedAgentId?: string;
|
|
205
212
|
}
|
|
206
213
|
export interface RegistrationResult {
|
|
207
214
|
status: 'approved' | 'rejected' | 'timeout';
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import type { RepresentationMode } from './policy.js';
|
|
2
|
+
import type { SendMessageOptions } from './types.js';
|
|
3
|
+
export type CanonWorkSessionStatus = 'active' | 'paused' | 'completed';
|
|
4
|
+
export type CanonWorkSessionConversationRole = 'requester' | 'coordinator' | 'participant' | 'delegate' | 'observer';
|
|
5
|
+
export type CanonWorkSessionDisclosureMode = 'none' | 'summary' | 'full';
|
|
6
|
+
export interface CanonWorkSessionParticipant {
|
|
7
|
+
conversationId?: string | null;
|
|
8
|
+
participantId?: string | null;
|
|
9
|
+
label?: string | null;
|
|
10
|
+
role?: CanonWorkSessionConversationRole | null;
|
|
11
|
+
}
|
|
12
|
+
export interface CanonWorkSessionContext {
|
|
13
|
+
id: string;
|
|
14
|
+
title?: string | null;
|
|
15
|
+
objective?: string | null;
|
|
16
|
+
status?: CanonWorkSessionStatus | null;
|
|
17
|
+
summary?: string | null;
|
|
18
|
+
representation?: RepresentationMode | null;
|
|
19
|
+
currentConversationRole?: CanonWorkSessionConversationRole | null;
|
|
20
|
+
sourceConversationId?: string | null;
|
|
21
|
+
sourceConversationLabel?: string | null;
|
|
22
|
+
sourceParticipantId?: string | null;
|
|
23
|
+
sourceParticipantLabel?: string | null;
|
|
24
|
+
disclosure?: CanonWorkSessionDisclosureMode | null;
|
|
25
|
+
disclosureNotes?: string[];
|
|
26
|
+
visibleFacts?: string[];
|
|
27
|
+
pendingQuestions?: string[];
|
|
28
|
+
participants?: CanonWorkSessionParticipant[];
|
|
29
|
+
updatedAt?: string | null;
|
|
30
|
+
}
|
|
31
|
+
export interface CanonWorkSession {
|
|
32
|
+
id: string;
|
|
33
|
+
title?: string | null;
|
|
34
|
+
objective?: string | null;
|
|
35
|
+
status: CanonWorkSessionStatus;
|
|
36
|
+
createdAt?: string | null;
|
|
37
|
+
updatedAt?: string | null;
|
|
38
|
+
}
|
|
39
|
+
export interface UpdateWorkSessionConversationOptions {
|
|
40
|
+
summary?: string | null;
|
|
41
|
+
representation?: RepresentationMode | null;
|
|
42
|
+
currentConversationRole?: CanonWorkSessionConversationRole | null;
|
|
43
|
+
sourceConversationId?: string | null;
|
|
44
|
+
sourceConversationLabel?: string | null;
|
|
45
|
+
sourceParticipantId?: string | null;
|
|
46
|
+
sourceParticipantLabel?: string | null;
|
|
47
|
+
disclosure?: CanonWorkSessionDisclosureMode | null;
|
|
48
|
+
disclosureNotes?: string[];
|
|
49
|
+
visibleFacts?: string[];
|
|
50
|
+
pendingQuestions?: string[];
|
|
51
|
+
participants?: CanonWorkSessionParticipant[];
|
|
52
|
+
}
|
|
53
|
+
export interface CreateWorkSessionOptions extends UpdateWorkSessionConversationOptions {
|
|
54
|
+
conversationId: string;
|
|
55
|
+
title?: string | null;
|
|
56
|
+
objective?: string | null;
|
|
57
|
+
status?: CanonWorkSessionStatus | null;
|
|
58
|
+
}
|
|
59
|
+
export interface CanonResolvedWorkSession {
|
|
60
|
+
workSession: CanonWorkSession;
|
|
61
|
+
context: CanonWorkSessionContext;
|
|
62
|
+
}
|
|
63
|
+
export interface SendLinkedMessageOptions {
|
|
64
|
+
sourceConversationId: string;
|
|
65
|
+
targetConversationId: string;
|
|
66
|
+
text: string;
|
|
67
|
+
workSessionId?: string;
|
|
68
|
+
createWorkSession?: Omit<CreateWorkSessionOptions, 'conversationId'>;
|
|
69
|
+
sourceContext?: UpdateWorkSessionConversationOptions;
|
|
70
|
+
targetContext?: UpdateWorkSessionConversationOptions;
|
|
71
|
+
messageOptions?: Omit<SendMessageOptions, 'workSessionId'>;
|
|
72
|
+
}
|
|
73
|
+
export interface SendLinkedMessageResult {
|
|
74
|
+
messageId: string;
|
|
75
|
+
workSessionId: string;
|
|
76
|
+
workSessionCreated: boolean;
|
|
77
|
+
}
|
|
78
|
+
export interface WorkSessionPromptRenderOptions {
|
|
79
|
+
maxVisibleFacts?: number;
|
|
80
|
+
maxPendingQuestions?: number;
|
|
81
|
+
maxDisclosureNotes?: number;
|
|
82
|
+
maxParticipants?: number;
|
|
83
|
+
maxDetailedWorkSessions?: number;
|
|
84
|
+
maxOtherWorkSessions?: number;
|
|
85
|
+
}
|
|
86
|
+
export declare function mergeWorkSessionContexts(explicitWorkSession?: CanonWorkSessionContext | null, activeWorkSessions?: CanonWorkSessionContext[] | null): CanonWorkSessionContext[];
|
|
87
|
+
export declare function buildWorkSessionPromptLines(workSession?: CanonWorkSessionContext | null, options?: WorkSessionPromptRenderOptions): string[];
|
|
88
|
+
export declare function buildWorkSessionsPromptLines(workSessions?: CanonWorkSessionContext[] | null, options?: WorkSessionPromptRenderOptions): string[];
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
const DEFAULT_WORK_SESSION_PROMPT_OPTIONS = {
|
|
2
|
+
maxVisibleFacts: 3,
|
|
3
|
+
maxPendingQuestions: 2,
|
|
4
|
+
maxDisclosureNotes: 1,
|
|
5
|
+
maxParticipants: 3,
|
|
6
|
+
maxDetailedWorkSessions: 1,
|
|
7
|
+
maxOtherWorkSessions: 3,
|
|
8
|
+
};
|
|
9
|
+
function normalizeString(value) {
|
|
10
|
+
if (typeof value !== 'string')
|
|
11
|
+
return null;
|
|
12
|
+
const trimmed = value.trim();
|
|
13
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
14
|
+
}
|
|
15
|
+
function normalizeStringList(value) {
|
|
16
|
+
if (!Array.isArray(value))
|
|
17
|
+
return [];
|
|
18
|
+
return value
|
|
19
|
+
.map((entry) => normalizeString(entry))
|
|
20
|
+
.filter((entry) => entry != null);
|
|
21
|
+
}
|
|
22
|
+
function mergeStringLists(primary, secondary) {
|
|
23
|
+
const merged = [...normalizeStringList(primary), ...normalizeStringList(secondary)];
|
|
24
|
+
const unique = Array.from(new Set(merged));
|
|
25
|
+
return unique.length > 0 ? unique : undefined;
|
|
26
|
+
}
|
|
27
|
+
function mergeParticipants(primary, secondary) {
|
|
28
|
+
const result = [];
|
|
29
|
+
const seen = new Set();
|
|
30
|
+
const pushParticipant = (participant) => {
|
|
31
|
+
if (!participant)
|
|
32
|
+
return;
|
|
33
|
+
const key = [
|
|
34
|
+
normalizeString(participant.conversationId),
|
|
35
|
+
normalizeString(participant.participantId),
|
|
36
|
+
normalizeString(participant.label),
|
|
37
|
+
normalizeString(participant.role),
|
|
38
|
+
].join('::');
|
|
39
|
+
if (seen.has(key))
|
|
40
|
+
return;
|
|
41
|
+
seen.add(key);
|
|
42
|
+
result.push(participant);
|
|
43
|
+
};
|
|
44
|
+
for (const participant of primary ?? [])
|
|
45
|
+
pushParticipant(participant);
|
|
46
|
+
for (const participant of secondary ?? [])
|
|
47
|
+
pushParticipant(participant);
|
|
48
|
+
return result.length > 0 ? result : undefined;
|
|
49
|
+
}
|
|
50
|
+
function mergeTwoWorkSessions(primary, secondary) {
|
|
51
|
+
const disclosureNotes = mergeStringLists(primary.disclosureNotes, secondary.disclosureNotes);
|
|
52
|
+
const visibleFacts = mergeStringLists(primary.visibleFacts, secondary.visibleFacts);
|
|
53
|
+
const pendingQuestions = mergeStringLists(primary.pendingQuestions, secondary.pendingQuestions);
|
|
54
|
+
const participants = mergeParticipants(primary.participants, secondary.participants);
|
|
55
|
+
return {
|
|
56
|
+
id: primary.id,
|
|
57
|
+
title: primary.title ?? secondary.title ?? null,
|
|
58
|
+
objective: primary.objective ?? secondary.objective ?? null,
|
|
59
|
+
status: primary.status ?? secondary.status ?? null,
|
|
60
|
+
summary: primary.summary ?? secondary.summary ?? null,
|
|
61
|
+
representation: primary.representation ?? secondary.representation ?? null,
|
|
62
|
+
currentConversationRole: primary.currentConversationRole ?? secondary.currentConversationRole ?? null,
|
|
63
|
+
sourceConversationId: primary.sourceConversationId ?? secondary.sourceConversationId ?? null,
|
|
64
|
+
sourceConversationLabel: primary.sourceConversationLabel ?? secondary.sourceConversationLabel ?? null,
|
|
65
|
+
sourceParticipantId: primary.sourceParticipantId ?? secondary.sourceParticipantId ?? null,
|
|
66
|
+
sourceParticipantLabel: primary.sourceParticipantLabel ?? secondary.sourceParticipantLabel ?? null,
|
|
67
|
+
disclosure: primary.disclosure ?? secondary.disclosure ?? null,
|
|
68
|
+
...(disclosureNotes ? { disclosureNotes } : {}),
|
|
69
|
+
...(visibleFacts ? { visibleFacts } : {}),
|
|
70
|
+
...(pendingQuestions ? { pendingQuestions } : {}),
|
|
71
|
+
...(participants ? { participants } : {}),
|
|
72
|
+
updatedAt: primary.updatedAt ?? secondary.updatedAt ?? null,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
export function mergeWorkSessionContexts(explicitWorkSession, activeWorkSessions) {
|
|
76
|
+
const merged = new Map();
|
|
77
|
+
const add = (workSession) => {
|
|
78
|
+
if (!workSession?.id)
|
|
79
|
+
return;
|
|
80
|
+
const existing = merged.get(workSession.id);
|
|
81
|
+
merged.set(workSession.id, existing ? mergeTwoWorkSessions(existing, workSession) : workSession);
|
|
82
|
+
};
|
|
83
|
+
add(explicitWorkSession);
|
|
84
|
+
for (const workSession of activeWorkSessions ?? []) {
|
|
85
|
+
add(workSession);
|
|
86
|
+
}
|
|
87
|
+
return Array.from(merged.values());
|
|
88
|
+
}
|
|
89
|
+
function formatParticipantLabel(participant) {
|
|
90
|
+
const label = normalizeString(participant.label);
|
|
91
|
+
if (label)
|
|
92
|
+
return label;
|
|
93
|
+
const participantId = normalizeString(participant.participantId);
|
|
94
|
+
if (participantId)
|
|
95
|
+
return participantId;
|
|
96
|
+
return normalizeString(participant.conversationId);
|
|
97
|
+
}
|
|
98
|
+
function resolvePromptOptions(options) {
|
|
99
|
+
return {
|
|
100
|
+
maxVisibleFacts: Math.max(0, options?.maxVisibleFacts
|
|
101
|
+
?? DEFAULT_WORK_SESSION_PROMPT_OPTIONS.maxVisibleFacts),
|
|
102
|
+
maxPendingQuestions: Math.max(0, options?.maxPendingQuestions
|
|
103
|
+
?? DEFAULT_WORK_SESSION_PROMPT_OPTIONS.maxPendingQuestions),
|
|
104
|
+
maxDisclosureNotes: Math.max(0, options?.maxDisclosureNotes
|
|
105
|
+
?? DEFAULT_WORK_SESSION_PROMPT_OPTIONS.maxDisclosureNotes),
|
|
106
|
+
maxParticipants: Math.max(0, options?.maxParticipants ?? DEFAULT_WORK_SESSION_PROMPT_OPTIONS.maxParticipants),
|
|
107
|
+
maxDetailedWorkSessions: Math.max(1, options?.maxDetailedWorkSessions
|
|
108
|
+
?? DEFAULT_WORK_SESSION_PROMPT_OPTIONS.maxDetailedWorkSessions),
|
|
109
|
+
maxOtherWorkSessions: Math.max(0, options?.maxOtherWorkSessions
|
|
110
|
+
?? DEFAULT_WORK_SESSION_PROMPT_OPTIONS.maxOtherWorkSessions),
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
function takeWithHiddenCount(values, maxCount) {
|
|
114
|
+
if (maxCount <= 0) {
|
|
115
|
+
return { visible: [], hiddenCount: values.length };
|
|
116
|
+
}
|
|
117
|
+
return {
|
|
118
|
+
visible: values.slice(0, maxCount),
|
|
119
|
+
hiddenCount: Math.max(0, values.length - maxCount),
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
function buildOriginLabel(workSession) {
|
|
123
|
+
const sourceConversation = normalizeString(workSession.sourceConversationLabel)
|
|
124
|
+
?? normalizeString(workSession.sourceConversationId);
|
|
125
|
+
const sourceParticipant = normalizeString(workSession.sourceParticipantLabel)
|
|
126
|
+
?? normalizeString(workSession.sourceParticipantId);
|
|
127
|
+
if (sourceConversation && sourceParticipant) {
|
|
128
|
+
return `${sourceParticipant} in ${sourceConversation}`;
|
|
129
|
+
}
|
|
130
|
+
return sourceParticipant ?? sourceConversation;
|
|
131
|
+
}
|
|
132
|
+
function formatWorkSessionReference(workSession) {
|
|
133
|
+
const title = normalizeString(workSession.title);
|
|
134
|
+
const objective = normalizeString(workSession.objective);
|
|
135
|
+
if (title)
|
|
136
|
+
return `${title} (${workSession.id})`;
|
|
137
|
+
if (objective)
|
|
138
|
+
return `${objective} (${workSession.id})`;
|
|
139
|
+
return workSession.id;
|
|
140
|
+
}
|
|
141
|
+
export function buildWorkSessionPromptLines(workSession, options) {
|
|
142
|
+
if (!workSession)
|
|
143
|
+
return [];
|
|
144
|
+
const promptOptions = resolvePromptOptions(options);
|
|
145
|
+
const title = normalizeString(workSession.title);
|
|
146
|
+
const objective = normalizeString(workSession.objective);
|
|
147
|
+
const summary = normalizeString(workSession.summary);
|
|
148
|
+
const origin = buildOriginLabel(workSession);
|
|
149
|
+
const visibleFacts = takeWithHiddenCount(normalizeStringList(workSession.visibleFacts), promptOptions.maxVisibleFacts);
|
|
150
|
+
const pendingQuestions = takeWithHiddenCount(normalizeStringList(workSession.pendingQuestions), promptOptions.maxPendingQuestions);
|
|
151
|
+
const disclosureNotes = takeWithHiddenCount(normalizeStringList(workSession.disclosureNotes), promptOptions.maxDisclosureNotes);
|
|
152
|
+
const participants = takeWithHiddenCount(Array.isArray(workSession.participants) ? workSession.participants : [], promptOptions.maxParticipants);
|
|
153
|
+
return [
|
|
154
|
+
`Canon work session ID: ${workSession.id}`,
|
|
155
|
+
...(title ? [`Canon work session title: ${title}`] : []),
|
|
156
|
+
...(objective ? [`Canon work session objective: ${objective}`] : []),
|
|
157
|
+
...(workSession.status
|
|
158
|
+
? [`Canon work session status: ${workSession.status}`]
|
|
159
|
+
: []),
|
|
160
|
+
...(workSession.currentConversationRole
|
|
161
|
+
? [
|
|
162
|
+
`This conversation's role in the work session: ${workSession.currentConversationRole}`,
|
|
163
|
+
]
|
|
164
|
+
: []),
|
|
165
|
+
...(workSession.representation
|
|
166
|
+
? [`Participate in this conversation as: ${workSession.representation}`]
|
|
167
|
+
: []),
|
|
168
|
+
...(origin ? [`Work session origin: ${origin}`] : []),
|
|
169
|
+
...(workSession.disclosure
|
|
170
|
+
? [`Allowed disclosure level in this conversation: ${workSession.disclosure}`]
|
|
171
|
+
: []),
|
|
172
|
+
...(summary ? [`Shared work session summary: ${summary}`] : []),
|
|
173
|
+
...visibleFacts.visible.map((fact, index) => `Shared fact ${index + 1}: ${fact}`),
|
|
174
|
+
...(visibleFacts.hiddenCount > 0
|
|
175
|
+
? [`Additional shared facts omitted for brevity: ${visibleFacts.hiddenCount}`]
|
|
176
|
+
: []),
|
|
177
|
+
...pendingQuestions.visible.map((question, index) => `Open question ${index + 1}: ${question}`),
|
|
178
|
+
...(pendingQuestions.hiddenCount > 0
|
|
179
|
+
? [
|
|
180
|
+
`Additional open questions omitted for brevity: ${pendingQuestions.hiddenCount}`,
|
|
181
|
+
]
|
|
182
|
+
: []),
|
|
183
|
+
...disclosureNotes.visible.map((note, index) => `Disclosure note ${index + 1}: ${note}`),
|
|
184
|
+
...(disclosureNotes.hiddenCount > 0
|
|
185
|
+
? [
|
|
186
|
+
`Additional disclosure notes omitted for brevity: ${disclosureNotes.hiddenCount}`,
|
|
187
|
+
]
|
|
188
|
+
: []),
|
|
189
|
+
...participants.visible.flatMap((participant, index) => {
|
|
190
|
+
const label = formatParticipantLabel(participant);
|
|
191
|
+
if (!label)
|
|
192
|
+
return [];
|
|
193
|
+
const role = normalizeString(participant.role);
|
|
194
|
+
return [
|
|
195
|
+
`Related participant ${index + 1}: ${label}${role ? ` (${role})` : ''}`,
|
|
196
|
+
];
|
|
197
|
+
}),
|
|
198
|
+
...(participants.hiddenCount > 0
|
|
199
|
+
? [
|
|
200
|
+
`Additional related participants omitted for brevity: ${participants.hiddenCount}`,
|
|
201
|
+
]
|
|
202
|
+
: []),
|
|
203
|
+
'Canon rule: this shared work-session context is scoped to this conversation only and does not grant access to other conversation transcripts.',
|
|
204
|
+
];
|
|
205
|
+
}
|
|
206
|
+
export function buildWorkSessionsPromptLines(workSessions, options) {
|
|
207
|
+
const promptOptions = resolvePromptOptions(options);
|
|
208
|
+
if (!Array.isArray(workSessions) || workSessions.length === 0)
|
|
209
|
+
return [];
|
|
210
|
+
if (workSessions.length === 1) {
|
|
211
|
+
return [
|
|
212
|
+
'Canon active work session for this conversation:',
|
|
213
|
+
...buildWorkSessionPromptLines(workSessions[0], promptOptions),
|
|
214
|
+
];
|
|
215
|
+
}
|
|
216
|
+
const detailedWorkSessions = workSessions.slice(0, promptOptions.maxDetailedWorkSessions);
|
|
217
|
+
const hiddenWorkSessions = workSessions.slice(promptOptions.maxDetailedWorkSessions);
|
|
218
|
+
const lines = ['Canon active work sessions for this conversation:'];
|
|
219
|
+
detailedWorkSessions.forEach((workSession, index) => {
|
|
220
|
+
lines.push(detailedWorkSessions.length === 1
|
|
221
|
+
? 'Primary active work session:'
|
|
222
|
+
: `Detailed work session ${index + 1}:`);
|
|
223
|
+
lines.push(...buildWorkSessionPromptLines(workSession, promptOptions));
|
|
224
|
+
});
|
|
225
|
+
if (hiddenWorkSessions.length > 0) {
|
|
226
|
+
lines.push('Other active work sessions linked here:');
|
|
227
|
+
hiddenWorkSessions
|
|
228
|
+
.slice(0, promptOptions.maxOtherWorkSessions)
|
|
229
|
+
.forEach((workSession, index) => {
|
|
230
|
+
lines.push(`Other work session ${index + 1}: ${formatWorkSessionReference(workSession)}`);
|
|
231
|
+
});
|
|
232
|
+
if (hiddenWorkSessions.length > promptOptions.maxOtherWorkSessions) {
|
|
233
|
+
lines.push(`Additional active work sessions omitted for brevity: ${hiddenWorkSessions.length - promptOptions.maxOtherWorkSessions}`);
|
|
234
|
+
}
|
|
235
|
+
lines.push('Canon rule: do not assume the current message refers to another linked work session unless the conversation clearly points to it.');
|
|
236
|
+
}
|
|
237
|
+
return lines;
|
|
238
|
+
}
|