@canonmsg/core 0.8.0 → 0.11.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/approval-manager.js +14 -4
- package/dist/approval-types.d.ts +6 -0
- package/dist/browser.d.ts +2 -2
- package/dist/execution-environment.d.ts +6 -2
- package/dist/execution-environment.js +10 -2
- package/dist/index.d.ts +5 -2
- package/dist/index.js +3 -0
- package/dist/message-format.d.ts +13 -0
- package/dist/message-format.js +47 -0
- package/dist/runtime-cards.d.ts +8 -1
- package/dist/runtime-cards.js +2 -1
- package/dist/types.d.ts +42 -0
- package/dist/types.js +1 -0
- package/dist/workspace-discovery.d.ts +18 -0
- package/dist/workspace-discovery.js +193 -0
- package/package.json +2 -1
package/dist/approval-manager.js
CHANGED
|
@@ -41,7 +41,7 @@ export class ApprovalManager {
|
|
|
41
41
|
const summary = this.summarizeTool(toolName, toolInput);
|
|
42
42
|
const logMsg = buildApprovalOutcome('', toolName, summary, decision, 'session-rule');
|
|
43
43
|
this.client.sendMessage(conversationId, logMsg, {
|
|
44
|
-
metadata: { type: 'approval_outcome' },
|
|
44
|
+
metadata: { type: 'approval_outcome', decision, reason: 'session-rule' },
|
|
45
45
|
}).catch(() => { });
|
|
46
46
|
return { decision };
|
|
47
47
|
}
|
|
@@ -64,7 +64,12 @@ export class ApprovalManager {
|
|
|
64
64
|
const summary = this.summarizeTool(toolName, toolInput);
|
|
65
65
|
const msg = buildApprovalOutcome(approvalId, toolName, summary, 'deny', 'timeout');
|
|
66
66
|
this.client.sendMessage(conversationId, msg, {
|
|
67
|
-
metadata: {
|
|
67
|
+
metadata: {
|
|
68
|
+
type: 'approval_outcome',
|
|
69
|
+
approvalId,
|
|
70
|
+
decision: 'deny',
|
|
71
|
+
reason: 'timeout',
|
|
72
|
+
},
|
|
68
73
|
}).catch(() => { });
|
|
69
74
|
resolve({ decision: 'deny' });
|
|
70
75
|
}, this.config.timeoutSeconds * 1000);
|
|
@@ -164,14 +169,19 @@ export class ApprovalManager {
|
|
|
164
169
|
// Send confirmation (fire-and-forget)
|
|
165
170
|
const msg = buildApprovalOutcome(approvalId, entry.toolName, entry.toolSummary, decision, 'replied');
|
|
166
171
|
this.client.sendMessage(conversationId, msg, {
|
|
167
|
-
metadata: {
|
|
172
|
+
metadata: {
|
|
173
|
+
type: 'approval_outcome',
|
|
174
|
+
approvalId,
|
|
175
|
+
decision,
|
|
176
|
+
reason: 'replied',
|
|
177
|
+
},
|
|
168
178
|
}).catch(() => { });
|
|
169
179
|
// If session rule was set, log that too
|
|
170
180
|
if (sessionRule) {
|
|
171
181
|
const ruleDesc = this.describeRule(sessionRule);
|
|
172
182
|
this.client
|
|
173
183
|
.sendMessage(conversationId, `Session rule set: ${ruleDesc}`, {
|
|
174
|
-
metadata: { type: 'approval_outcome' },
|
|
184
|
+
metadata: { type: 'approval_outcome', decision, reason: 'session-rule' },
|
|
175
185
|
})
|
|
176
186
|
.catch(() => { });
|
|
177
187
|
}
|
package/dist/approval-types.d.ts
CHANGED
|
@@ -7,6 +7,12 @@ export interface ApprovalRequestMetadata {
|
|
|
7
7
|
riskLevel?: 'normal' | 'destructive';
|
|
8
8
|
expiresAt: string;
|
|
9
9
|
}
|
|
10
|
+
export interface ApprovalOutcomeMetadata {
|
|
11
|
+
type: 'approval_outcome';
|
|
12
|
+
approvalId?: string;
|
|
13
|
+
decision?: 'allow' | 'deny';
|
|
14
|
+
reason?: 'replied' | 'timeout' | 'session-rule';
|
|
15
|
+
}
|
|
10
16
|
export interface ApprovalReplyMetadata {
|
|
11
17
|
type: 'approval_reply';
|
|
12
18
|
approvalId: string;
|
package/dist/browser.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { AGENT_CAPABILITIES, CLAUDE_PERMISSION_MODE_OPTIONS, } from './types.js';
|
|
2
2
|
export { resolveCanonBaseUrl } from './base-url.js';
|
|
3
3
|
export { DEFAULT_BASE_URL, DEFAULT_STREAM_URL, DEFAULT_RTDB_URL, FIREBASE_WEB_API_KEY } from './constants.js';
|
|
4
|
-
export type { AgentCapabilities, AgentClientType, AgentSessionSnapshot, AgentSessionWorkSessionSummary, AgentRuntime, CanonControlAvailability, CanonControlDescriptor, CanonControlLiveBehavior, CanonControlSelectionPolicy, CanonControlValue, CanonContactRequest, CanonContactRequestStatus, ContactApprovedPayload, ContactRequestPayload, CanonStreamEvent, CreateContactRequestResult, MediaAttachment, MediaAttachmentKind, ModelOption, PermissionModeOption, CanonRuntimeDescriptor, CanonRuntimeExecutionMetadata, CanonRuntimeInventory, CanonRuntimeInventoryEntry, CanonRuntimeStreamingMode, CanonRuntimeStatusItem, CanonRuntimeSurfaceMode, CanonWorkspaceRootMetadata, RuntimeUpdatedPayload, RuntimeInfoPayload, ResolvedAdmission, SessionConfig, TurnUpdatedPayload, WorkspaceOption, } from './types.js';
|
|
4
|
+
export type { AgentCapabilities, AgentClientType, AgentSessionSnapshot, AgentSessionWorkSessionSummary, AgentRuntime, CanonControlAvailability, CanonControlDescriptor, CanonControlLiveBehavior, CanonControlSelectionPolicy, CanonControlValue, CanonContactRequest, CanonContactRequestStatus, ContactApprovedPayload, ContactRequestPayload, CanonStreamEvent, CreateContactRequestResult, MediaAttachment, MediaAttachmentKind, ModelOption, PermissionModeOption, CanonRuntimeDescriptor, CanonRuntimeActionAvailability, CanonRuntimeActionCategory, CanonRuntimeActionDescriptor, CanonRuntimeActionDispatch, CanonRuntimeActionPlacement, CanonRuntimeExecutionMetadata, CanonRuntimeInventory, CanonRuntimeInventoryEntry, CanonRuntimeStreamingMode, CanonRuntimeStatusItem, CanonRuntimeSurfaceMode, CanonWorkspaceRootMetadata, RuntimeUpdatedPayload, RuntimeInfoPayload, ResolvedAdmission, SessionConfig, TurnUpdatedPayload, WorkspaceOption, WorkspaceOptionSource, } from './types.js';
|
|
5
5
|
export { EXECUTION_ENVIRONMENT_MODES, isExecutionEnvironmentMode, } from './execution-environment-mode.js';
|
|
6
6
|
export type { ExecutionEnvironmentMode } from './execution-environment-mode.js';
|
|
7
7
|
export type { CanonResolvedWorkSession, CanonWorkSession, CanonWorkSessionContext, CanonWorkSessionConversationRole, CanonWorkSessionDisclosureMode, CanonWorkSessionParticipant, CanonWorkSessionStatus, CreateWorkSessionOptions, SendLinkedMessageOptions, SendLinkedMessageResult, UpdateWorkSessionConversationOptions, WorkSessionPromptRenderOptions, } from './work-session.js';
|
|
@@ -12,6 +12,6 @@ export { buildParticipationHistorySnapshot, buildParticipationHistorySnapshots,
|
|
|
12
12
|
export { DEFAULT_RUNTIME_CAPABILITIES, FINAL_MESSAGE_HANDOFF_MS, isTurnOpen, normalizeTurnMetadata, normalizeTurnState, resolveTurnMessageSemantics, shouldPromoteConversationMessage, shouldTriggerAgentTurn, } from './turn-protocol.js';
|
|
13
13
|
export type { DeliveryIntent, InboundDisposition, RuntimeCapabilities, TriggerDecision, TurnLifecycleState, TurnMessageSemantics, TurnMetadata, TurnState, } from './turn-protocol.js';
|
|
14
14
|
export { buildApprovalReply, buildApprovalRequest, buildApprovalOutcome, generateApprovalId, parseTextApprovalReply, redactSecrets, } from './approval-format.js';
|
|
15
|
-
export type { ApprovalRequestMetadata, ApprovalReplyMetadata, SessionRule, ApprovalResult, ApprovalConfig, } from './approval-types.js';
|
|
15
|
+
export type { ApprovalRequestMetadata, ApprovalReplyMetadata, ApprovalOutcomeMetadata, SessionRule, ApprovalResult, ApprovalConfig, } from './approval-types.js';
|
|
16
16
|
export { buildPlanApprovalReply, buildPlanApprovalRequest, buildQuestionReply, buildQuestionRequest, } from './runtime-cards.js';
|
|
17
17
|
export type { ClaudeQuestionMetadata, ClaudeQuestionReplyMetadata, PlanApprovalMetadata, PlanApprovalReplyMetadata, RuntimeQuestionDefinition, RuntimeQuestionOption, } from './runtime-cards.js';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { WorkspaceOption } from './types.js';
|
|
1
|
+
import type { WorkspaceOption, WorkspaceOptionSource } from './types.js';
|
|
2
2
|
import { EXECUTION_ENVIRONMENT_MODES, isExecutionEnvironmentMode, type ExecutionEnvironmentMode } from './execution-environment-mode.js';
|
|
3
3
|
export { EXECUTION_ENVIRONMENT_MODES, isExecutionEnvironmentMode };
|
|
4
4
|
export type { ExecutionEnvironmentMode };
|
|
@@ -22,6 +22,10 @@ interface WorkspaceResolverOption {
|
|
|
22
22
|
}
|
|
23
23
|
export interface ConfiguredWorkspaceOption extends WorkspaceResolverOption {
|
|
24
24
|
label: string;
|
|
25
|
+
description?: string;
|
|
26
|
+
workspaceRootId?: string;
|
|
27
|
+
workspaceRelativePath?: string;
|
|
28
|
+
source?: WorkspaceOptionSource;
|
|
25
29
|
}
|
|
26
30
|
export interface SessionWorkspaceConfig {
|
|
27
31
|
workspaceId?: string;
|
|
@@ -34,7 +38,7 @@ export declare function isEnabledFlag(value: unknown): boolean;
|
|
|
34
38
|
export declare function buildConversationEnvironmentKey(conversationId: string, workspaceCwd: string): string;
|
|
35
39
|
export declare function buildWorkspaceOptionId(workspaceCwd: string): string;
|
|
36
40
|
export declare function buildConfiguredWorkspaceOptions(primaryCwd: string, configured: string[]): ConfiguredWorkspaceOption[];
|
|
37
|
-
export declare function buildPublicWorkspaceOptions(workspaceOptions: Array<Pick<ConfiguredWorkspaceOption, 'id' | 'label'
|
|
41
|
+
export declare function buildPublicWorkspaceOptions(workspaceOptions: Array<Pick<ConfiguredWorkspaceOption, 'id' | 'label'> & Partial<Pick<ConfiguredWorkspaceOption, 'description' | 'workspaceRootId' | 'workspaceRelativePath' | 'source'>>>): WorkspaceOption[];
|
|
38
42
|
export declare function readSessionWorkspaceConfig(raw: unknown): SessionWorkspaceConfig | null;
|
|
39
43
|
export declare function resolveConfiguredWorkspaceCwd(input: {
|
|
40
44
|
workspaceOptions: WorkspaceResolverOption[];
|
|
@@ -93,7 +93,7 @@ export function buildWorkspaceOptionId(workspaceCwd) {
|
|
|
93
93
|
export function buildConfiguredWorkspaceOptions(primaryCwd, configured) {
|
|
94
94
|
const uniqueDirs = Array.from(new Set([primaryCwd, ...configured].map((dir) => resolve(dir))));
|
|
95
95
|
const seenLabels = new Map();
|
|
96
|
-
return uniqueDirs.map((cwd) => {
|
|
96
|
+
return uniqueDirs.map((cwd, index) => {
|
|
97
97
|
const baseLabel = basename(cwd) || cwd;
|
|
98
98
|
const seenCount = (seenLabels.get(baseLabel) ?? 0) + 1;
|
|
99
99
|
seenLabels.set(baseLabel, seenCount);
|
|
@@ -101,11 +101,19 @@ export function buildConfiguredWorkspaceOptions(primaryCwd, configured) {
|
|
|
101
101
|
id: buildWorkspaceOptionId(cwd),
|
|
102
102
|
label: seenCount === 1 ? baseLabel : `${baseLabel} (${seenCount})`,
|
|
103
103
|
cwd,
|
|
104
|
+
source: index === 0 ? 'default' : 'explicit',
|
|
104
105
|
};
|
|
105
106
|
});
|
|
106
107
|
}
|
|
107
108
|
export function buildPublicWorkspaceOptions(workspaceOptions) {
|
|
108
|
-
return workspaceOptions.map((
|
|
109
|
+
return workspaceOptions.map((workspace) => ({
|
|
110
|
+
id: workspace.id,
|
|
111
|
+
label: workspace.label,
|
|
112
|
+
...(workspace.description ? { description: workspace.description } : {}),
|
|
113
|
+
...(workspace.workspaceRootId ? { workspaceRootId: workspace.workspaceRootId } : {}),
|
|
114
|
+
...(workspace.workspaceRelativePath ? { workspaceRelativePath: workspace.workspaceRelativePath } : {}),
|
|
115
|
+
...(workspace.source ? { source: workspace.source } : {}),
|
|
116
|
+
}));
|
|
109
117
|
}
|
|
110
118
|
function isRetiredWorkspaceId(value) {
|
|
111
119
|
if (!value)
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
export { AGENT_CAPABILITIES, CLAUDE_PERMISSION_MODE_OPTIONS, } from './types.js';
|
|
2
|
-
export type { AgentCapabilities, AgentClientType, CanonControlAvailability, CanonControlDescriptor, CanonControlLiveBehavior, CanonControlSelectionPolicy, CanonControlValue, CanonContactRequest, CanonContactRequestStatus, ContactApprovedPayload, ContactRequestPayload, CanonMessage, CanonConversation, CanonMessagesPage, CreateContactRequestResult, AgentContext, CanonStreamEvent, AgentSessionSnapshot, AgentSessionWorkSessionSummary, ResolvedAdmission, MediaAttachment, MediaAttachmentKind, MessageCreatedPayload, TypingPayload, PresencePayload, RuntimeUpdatedPayload, TurnUpdatedPayload, SendMessageOptions, CreateConversationOptions, RegistrationInput, RegistrationResult, RegistrationStatus, StreamingStatus, SetStreamingOptions, SessionControl, SessionState, SessionConfig, AgentRuntime, CanonRuntimeDescriptor, CanonRuntimeExecutionMetadata, CanonRuntimeInventory, CanonRuntimeInventoryEntry, CanonRuntimeStreamingMode, CanonRuntimeStatusItem, CanonRuntimeSurfaceMode, CanonWorkspaceRootMetadata, ModelOption, PermissionModeOption, RuntimeInfoPayload, WorkspaceOption, } from './types.js';
|
|
2
|
+
export type { AgentCapabilities, AgentClientType, CanonControlAvailability, CanonControlDescriptor, CanonControlLiveBehavior, CanonControlSelectionPolicy, CanonControlValue, CanonContactRequest, CanonContactRequestStatus, ContactApprovedPayload, ContactRequestPayload, CanonMessage, CanonConversation, CanonMessagesPage, CreateContactRequestResult, AgentContext, CanonStreamEvent, AgentSessionSnapshot, AgentSessionWorkSessionSummary, ResolvedAdmission, MediaAttachment, MediaAttachmentKind, MessageCreatedPayload, TypingPayload, PresencePayload, RuntimeUpdatedPayload, TurnUpdatedPayload, SendMessageOptions, CreateConversationOptions, RegistrationInput, RegistrationResult, RegistrationStatus, StreamingStatus, SetStreamingOptions, SessionControl, SessionState, SessionConfig, AgentRuntime, CanonRuntimeDescriptor, CanonRuntimeActionAvailability, CanonRuntimeActionCategory, CanonRuntimeActionDescriptor, CanonRuntimeActionDispatch, CanonRuntimeActionPlacement, CanonRuntimeExecutionMetadata, CanonRuntimeInventory, CanonRuntimeInventoryEntry, CanonRuntimeStreamingMode, CanonRuntimeStatusItem, CanonRuntimeSurfaceMode, CanonWorkspaceRootMetadata, ModelOption, PermissionModeOption, RuntimeInfoPayload, WorkspaceOption, WorkspaceOptionSource, } from './types.js';
|
|
3
3
|
export type { CanonResolvedWorkSession, CanonWorkSession, CanonWorkSessionContext, CanonWorkSessionConversationRole, CreateWorkSessionOptions, CanonWorkSessionDisclosureMode, CanonWorkSessionParticipant, CanonWorkSessionStatus, SendLinkedMessageOptions, SendLinkedMessageResult, UpdateWorkSessionConversationOptions, WorkSessionPromptRenderOptions, } from './work-session.js';
|
|
4
4
|
export { buildWorkSessionPromptLines, buildWorkSessionsPromptLines, mergeWorkSessionContexts, } from './work-session.js';
|
|
5
|
+
export { buildConfiguredWorkspaceOptionsWithRoots, buildPublicWorkspaceRoots, buildWorkspaceRootId, discoverWorkspaceProjects, } from './workspace-discovery.js';
|
|
6
|
+
export type { ConfiguredWorkspaceRoot, WorkspaceDiscoveryResult, } from './workspace-discovery.js';
|
|
5
7
|
export { CanonClient, CanonApiError } from './client.js';
|
|
6
8
|
export { buildAgentSessionSnapshot } from './agent-session.js';
|
|
7
9
|
export { CanonStream } from './stream.js';
|
|
@@ -14,7 +16,7 @@ export { registerAndWaitForApproval } from './registration.js';
|
|
|
14
16
|
export { ApprovalManager } from './approval-manager.js';
|
|
15
17
|
export { generateApprovalId, buildApprovalRequest, buildApprovalReply, buildApprovalOutcome, parseTextApprovalReply, redactSecrets, } from './approval-format.js';
|
|
16
18
|
export { DEFAULT_APPROVAL_CONFIG, } from './approval-types.js';
|
|
17
|
-
export type { ApprovalRequestMetadata, ApprovalReplyMetadata, SessionRule, ApprovalResult, ApprovalConfig, } from './approval-types.js';
|
|
19
|
+
export type { ApprovalRequestMetadata, ApprovalReplyMetadata, ApprovalOutcomeMetadata, SessionRule, ApprovalResult, ApprovalConfig, } from './approval-types.js';
|
|
18
20
|
export { buildPlanApprovalReply, buildPlanApprovalRequest, buildQuestionReply, buildQuestionRequest, } from './runtime-cards.js';
|
|
19
21
|
export type { ClaudeQuestionMetadata, ClaudeQuestionReplyMetadata, PlanApprovalMetadata, PlanApprovalReplyMetadata, RuntimeQuestionDefinition, RuntimeQuestionOption, } from './runtime-cards.js';
|
|
20
22
|
export { createStreamingHelper } from './streaming.js';
|
|
@@ -27,5 +29,6 @@ export { buildConfiguredWorkspaceOptions, buildConversationEnvironmentKey, build
|
|
|
27
29
|
export type { ConfiguredWorkspaceOption, ExecutionEnvironmentMode, PreparedExecutionEnvironment, SessionWorkspaceConfig, } from './execution-environment.js';
|
|
28
30
|
export { initRTDBAuth, rtdbWrite, rtdbRead, patchAgentSessionSnapshot, patchRuntimeInfo, writeRuntimeInfo, clearRuntimeInfo, writeSessionState, clearSessionState, writeTurnState, clearTurnState, } from './rtdb-rest.js';
|
|
29
31
|
export type { AgentSessionSnapshotPatch, RuntimeInfoPayloadData, SessionStatePayload, TurnStatePayload, } from './rtdb-rest.js';
|
|
32
|
+
export { formatCanonMessageAsText } from './message-format.js';
|
|
30
33
|
export { DEFAULT_BASE_URL, DEFAULT_STREAM_URL, DEFAULT_RTDB_URL, FIREBASE_WEB_API_KEY } from './constants.js';
|
|
31
34
|
export { resolveCanonBaseUrl } from './base-url.js';
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// Types
|
|
2
2
|
export { AGENT_CAPABILITIES, CLAUDE_PERMISSION_MODE_OPTIONS, } from './types.js';
|
|
3
3
|
export { buildWorkSessionPromptLines, buildWorkSessionsPromptLines, mergeWorkSessionContexts, } from './work-session.js';
|
|
4
|
+
export { buildConfiguredWorkspaceOptionsWithRoots, buildPublicWorkspaceRoots, buildWorkspaceRootId, discoverWorkspaceProjects, } from './workspace-discovery.js';
|
|
4
5
|
// Client
|
|
5
6
|
export { CanonClient, CanonApiError } from './client.js';
|
|
6
7
|
export { buildAgentSessionSnapshot } from './agent-session.js';
|
|
@@ -26,6 +27,8 @@ export { resolveCanonAgent, resolveCanonProfile, getActiveProfile } from './agen
|
|
|
26
27
|
export { buildConfiguredWorkspaceOptions, buildConversationEnvironmentKey, buildConversationWorktreeSpec, buildPublicWorkspaceOptions, buildWorkspaceOptionId, EXECUTION_ENVIRONMENT_MODES, isEnabledFlag, isExecutionEnvironmentMode, normalizeOptionalString, readSessionWorkspaceConfig, resolveConfiguredWorkspaceCwd, ExecutionEnvironmentError, prepareConversationEnvironment, releaseConversationEnvironment, } from './execution-environment.js';
|
|
27
28
|
// RTDB REST helpers (token exchange, session state, generic read/write)
|
|
28
29
|
export { initRTDBAuth, rtdbWrite, rtdbRead, patchAgentSessionSnapshot, patchRuntimeInfo, writeRuntimeInfo, clearRuntimeInfo, writeSessionState, clearSessionState, writeTurnState, clearTurnState, } from './rtdb-rest.js';
|
|
30
|
+
// Message formatting (LLM-facing text projection)
|
|
31
|
+
export { formatCanonMessageAsText } from './message-format.js';
|
|
29
32
|
// Constants
|
|
30
33
|
export { DEFAULT_BASE_URL, DEFAULT_STREAM_URL, DEFAULT_RTDB_URL, FIREBASE_WEB_API_KEY } from './constants.js';
|
|
31
34
|
// Base URL resolver
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { CanonMessage } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Project a CanonMessage to a single text representation suitable as a
|
|
4
|
+
* baseline for plugins that feed messages to an LLM as plain text.
|
|
5
|
+
*
|
|
6
|
+
* Plugins that compose richer prompts (placeholders, capability hints,
|
|
7
|
+
* native vision input) layer on top of this — the goal here is only to
|
|
8
|
+
* guarantee that no contentType is ever silently dropped to an empty
|
|
9
|
+
* string. Adding a new contentType to CanonMessage and forgetting to
|
|
10
|
+
* handle it here surfaces in this file's tests, not as a silent
|
|
11
|
+
* regression in every consumer plugin.
|
|
12
|
+
*/
|
|
13
|
+
export declare function formatCanonMessageAsText(message: Pick<CanonMessage, 'contentType' | 'text' | 'attachments' | 'contactCard'>): string;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Project a CanonMessage to a single text representation suitable as a
|
|
3
|
+
* baseline for plugins that feed messages to an LLM as plain text.
|
|
4
|
+
*
|
|
5
|
+
* Plugins that compose richer prompts (placeholders, capability hints,
|
|
6
|
+
* native vision input) layer on top of this — the goal here is only to
|
|
7
|
+
* guarantee that no contentType is ever silently dropped to an empty
|
|
8
|
+
* string. Adding a new contentType to CanonMessage and forgetting to
|
|
9
|
+
* handle it here surfaces in this file's tests, not as a silent
|
|
10
|
+
* regression in every consumer plugin.
|
|
11
|
+
*/
|
|
12
|
+
export function formatCanonMessageAsText(message) {
|
|
13
|
+
if (message.contentType === 'contact_card' && message.contactCard) {
|
|
14
|
+
return formatContactCard(message.contactCard);
|
|
15
|
+
}
|
|
16
|
+
const attachment = pickPrimaryAttachment(message.attachments);
|
|
17
|
+
const trimmedText = typeof message.text === 'string' ? message.text.trim() : '';
|
|
18
|
+
if (attachment?.kind === 'image') {
|
|
19
|
+
return trimmedText ? `[image] ${trimmedText}` : '[image]';
|
|
20
|
+
}
|
|
21
|
+
if (attachment?.kind === 'audio') {
|
|
22
|
+
const seconds = typeof attachment.durationMs === 'number'
|
|
23
|
+
? ` ${Math.round(attachment.durationMs / 1000)}s`
|
|
24
|
+
: '';
|
|
25
|
+
return trimmedText ? `[audio${seconds}] ${trimmedText}` : `[audio${seconds}]`;
|
|
26
|
+
}
|
|
27
|
+
if (attachment?.kind === 'file') {
|
|
28
|
+
const label = attachment.fileName?.trim() || 'file';
|
|
29
|
+
return trimmedText ? `[${label}] ${trimmedText}` : `[${label}]`;
|
|
30
|
+
}
|
|
31
|
+
return trimmedText || '[message]';
|
|
32
|
+
}
|
|
33
|
+
function pickPrimaryAttachment(attachments) {
|
|
34
|
+
const first = attachments?.[0];
|
|
35
|
+
return first?.url ? first : null;
|
|
36
|
+
}
|
|
37
|
+
function formatContactCard(card) {
|
|
38
|
+
const displayName = card.displayName?.trim() || 'Unknown';
|
|
39
|
+
const parts = [card.userType, `userId: ${card.userId}`];
|
|
40
|
+
if (card.ownerName)
|
|
41
|
+
parts.push(`owner: ${card.ownerName}`);
|
|
42
|
+
if (card.accessLevel)
|
|
43
|
+
parts.push(`access: ${card.accessLevel}`);
|
|
44
|
+
if (card.about)
|
|
45
|
+
parts.push(`about: ${card.about}`);
|
|
46
|
+
return `[Contact card] "${displayName}" — ${parts.join(' · ')}`;
|
|
47
|
+
}
|
package/dist/runtime-cards.d.ts
CHANGED
|
@@ -21,6 +21,13 @@ export interface ClaudeQuestionReplyMetadata {
|
|
|
21
21
|
export interface PlanApprovalMetadata {
|
|
22
22
|
type: 'plan_approval';
|
|
23
23
|
planId: string;
|
|
24
|
+
title?: string;
|
|
25
|
+
summary?: string;
|
|
26
|
+
body?: string;
|
|
27
|
+
allowedPrompts?: ReadonlyArray<{
|
|
28
|
+
tool: string;
|
|
29
|
+
prompt: string;
|
|
30
|
+
}>;
|
|
24
31
|
}
|
|
25
32
|
export interface PlanApprovalReplyMetadata {
|
|
26
33
|
type: 'plan_approval_reply';
|
|
@@ -36,7 +43,7 @@ export declare function buildQuestionReply(questionId: string, answers: Record<s
|
|
|
36
43
|
text: string;
|
|
37
44
|
metadata: ClaudeQuestionReplyMetadata;
|
|
38
45
|
};
|
|
39
|
-
export declare function buildPlanApprovalRequest(planId: string, text?: string): {
|
|
46
|
+
export declare function buildPlanApprovalRequest(planId: string, text?: string, details?: Omit<PlanApprovalMetadata, 'type' | 'planId'>): {
|
|
40
47
|
text: string;
|
|
41
48
|
metadata: PlanApprovalMetadata;
|
|
42
49
|
};
|
package/dist/runtime-cards.js
CHANGED
|
@@ -22,12 +22,13 @@ export function buildQuestionReply(questionId, answers) {
|
|
|
22
22
|
},
|
|
23
23
|
};
|
|
24
24
|
}
|
|
25
|
-
export function buildPlanApprovalRequest(planId, text = 'Plan ready for review.') {
|
|
25
|
+
export function buildPlanApprovalRequest(planId, text = 'Plan ready for review.', details) {
|
|
26
26
|
return {
|
|
27
27
|
text,
|
|
28
28
|
metadata: {
|
|
29
29
|
type: 'plan_approval',
|
|
30
30
|
planId,
|
|
31
|
+
...(details ?? {}),
|
|
31
32
|
},
|
|
32
33
|
};
|
|
33
34
|
}
|
package/dist/types.d.ts
CHANGED
|
@@ -133,10 +133,18 @@ export interface ModelOption {
|
|
|
133
133
|
value: string;
|
|
134
134
|
label: string;
|
|
135
135
|
description?: string;
|
|
136
|
+
workspaceRootId?: string;
|
|
137
|
+
workspaceRelativePath?: string;
|
|
138
|
+
source?: WorkspaceOptionSource;
|
|
136
139
|
}
|
|
140
|
+
export type WorkspaceOptionSource = 'default' | 'explicit' | 'discovered';
|
|
137
141
|
export interface WorkspaceOption {
|
|
138
142
|
id: string;
|
|
139
143
|
label: string;
|
|
144
|
+
description?: string;
|
|
145
|
+
workspaceRootId?: string;
|
|
146
|
+
workspaceRelativePath?: string;
|
|
147
|
+
source?: WorkspaceOptionSource;
|
|
140
148
|
}
|
|
141
149
|
export interface CanonWorkspaceRootMetadata {
|
|
142
150
|
id: string;
|
|
@@ -152,6 +160,36 @@ export type CanonRuntimeStreamingMode = 'none' | 'status' | 'snapshot' | 'block'
|
|
|
152
160
|
export type CanonRuntimeSurfaceMode = 'host' | 'channel' | 'limited_channel' | 'operator';
|
|
153
161
|
export type CanonRuntimeInventoryStatus = 'ready' | 'auth_needed' | 'unknown' | 'configured' | 'running' | 'error';
|
|
154
162
|
export type CanonRuntimeStatusTone = 'default' | 'success' | 'warning' | 'danger';
|
|
163
|
+
export type CanonRuntimeActionAvailability = 'idle' | 'busy' | 'busy_with_queue' | 'waiting_input' | 'always';
|
|
164
|
+
export type CanonRuntimeActionPlacement = 'composer_slash' | 'command_palette' | 'session_strip';
|
|
165
|
+
export type CanonRuntimeActionCategory = 'plan' | 'turn' | 'session' | 'details' | 'custom';
|
|
166
|
+
export type CanonRuntimeActionDispatch = {
|
|
167
|
+
kind: 'control';
|
|
168
|
+
controlId: string;
|
|
169
|
+
value: CanonControlValue;
|
|
170
|
+
} | {
|
|
171
|
+
kind: 'signal';
|
|
172
|
+
signal: 'interrupt' | 'stop_and_drop';
|
|
173
|
+
} | {
|
|
174
|
+
kind: 'compose';
|
|
175
|
+
text: string;
|
|
176
|
+
} | {
|
|
177
|
+
kind: 'open_details';
|
|
178
|
+
target?: string;
|
|
179
|
+
};
|
|
180
|
+
export interface CanonRuntimeActionDescriptor {
|
|
181
|
+
id: string;
|
|
182
|
+
label: string;
|
|
183
|
+
description?: string;
|
|
184
|
+
aliases?: ReadonlyArray<string>;
|
|
185
|
+
category?: CanonRuntimeActionCategory;
|
|
186
|
+
placements?: ReadonlyArray<CanonRuntimeActionPlacement>;
|
|
187
|
+
availability?: ReadonlyArray<CanonRuntimeActionAvailability>;
|
|
188
|
+
ownerOnly?: boolean;
|
|
189
|
+
disabledReason?: string | null;
|
|
190
|
+
trailingTextBehavior?: 'ignore' | 'send_as_prompt';
|
|
191
|
+
dispatch: CanonRuntimeActionDispatch;
|
|
192
|
+
}
|
|
155
193
|
export interface CanonControlDescriptor {
|
|
156
194
|
id: string;
|
|
157
195
|
label: string;
|
|
@@ -165,6 +203,7 @@ export interface CanonControlDescriptor {
|
|
|
165
203
|
export interface CanonRuntimeDescriptor {
|
|
166
204
|
coreControls: ReadonlyArray<CanonControlDescriptor>;
|
|
167
205
|
runtimeControls?: ReadonlyArray<CanonControlDescriptor>;
|
|
206
|
+
actions?: ReadonlyArray<CanonRuntimeActionDescriptor>;
|
|
168
207
|
/**
|
|
169
208
|
* Optional setup-time local roots advertised by a runtime. These are
|
|
170
209
|
* metadata only for now; existing session config still selects concrete
|
|
@@ -410,6 +449,9 @@ export declare const CLAUDE_PERMISSION_MODE_OPTIONS: readonly [{
|
|
|
410
449
|
}, {
|
|
411
450
|
readonly value: "plan";
|
|
412
451
|
readonly label: "Plan";
|
|
452
|
+
}, {
|
|
453
|
+
readonly value: "dontAsk";
|
|
454
|
+
readonly label: "Don't ask";
|
|
413
455
|
}, {
|
|
414
456
|
readonly value: "bypassPermissions";
|
|
415
457
|
readonly label: "Bypass";
|
package/dist/types.js
CHANGED
|
@@ -36,6 +36,7 @@ export const CLAUDE_PERMISSION_MODE_OPTIONS = [
|
|
|
36
36
|
{ value: 'default', label: 'Default' },
|
|
37
37
|
{ value: 'acceptEdits', label: 'Auto-edit' },
|
|
38
38
|
{ value: 'plan', label: 'Plan' },
|
|
39
|
+
{ value: 'dontAsk', label: "Don't ask" },
|
|
39
40
|
{ value: 'bypassPermissions', label: 'Bypass' },
|
|
40
41
|
{ value: 'auto', label: 'Auto' },
|
|
41
42
|
];
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { type ConfiguredWorkspaceOption } from './execution-environment.js';
|
|
2
|
+
import type { CanonWorkspaceRootMetadata } from './types.js';
|
|
3
|
+
export interface ConfiguredWorkspaceRoot extends CanonWorkspaceRootMetadata {
|
|
4
|
+
cwd: string;
|
|
5
|
+
}
|
|
6
|
+
export interface WorkspaceDiscoveryResult {
|
|
7
|
+
workspaceOptions: ConfiguredWorkspaceOption[];
|
|
8
|
+
workspaceRoots: ConfiguredWorkspaceRoot[];
|
|
9
|
+
warnings: string[];
|
|
10
|
+
}
|
|
11
|
+
export declare function buildWorkspaceRootId(workspaceRoot: string): string;
|
|
12
|
+
export declare function discoverWorkspaceProjects(root: ConfiguredWorkspaceRoot, warnings?: string[]): ConfiguredWorkspaceOption[];
|
|
13
|
+
export declare function buildPublicWorkspaceRoots(roots: ReadonlyArray<ConfiguredWorkspaceRoot>): CanonWorkspaceRootMetadata[];
|
|
14
|
+
export declare function buildConfiguredWorkspaceOptionsWithRoots(input: {
|
|
15
|
+
primaryCwd: string;
|
|
16
|
+
configuredWorkspaces?: string[];
|
|
17
|
+
workspaceRoots?: string[];
|
|
18
|
+
}): WorkspaceDiscoveryResult;
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { existsSync, readdirSync, realpathSync, statSync } from 'node:fs';
|
|
3
|
+
import { basename, isAbsolute, join, relative, resolve, sep } from 'node:path';
|
|
4
|
+
import { buildConfiguredWorkspaceOptions, buildWorkspaceOptionId, } from './execution-environment.js';
|
|
5
|
+
const PROJECT_MARKERS = [
|
|
6
|
+
'.git',
|
|
7
|
+
'package.json',
|
|
8
|
+
'pyproject.toml',
|
|
9
|
+
'Cargo.toml',
|
|
10
|
+
'go.mod',
|
|
11
|
+
];
|
|
12
|
+
const IGNORED_PROJECT_DIRS = new Set([
|
|
13
|
+
'.cache',
|
|
14
|
+
'.git',
|
|
15
|
+
'.hg',
|
|
16
|
+
'.svn',
|
|
17
|
+
'.turbo',
|
|
18
|
+
'.yarn',
|
|
19
|
+
'build',
|
|
20
|
+
'coverage',
|
|
21
|
+
'dist',
|
|
22
|
+
'node_modules',
|
|
23
|
+
'target',
|
|
24
|
+
'vendor',
|
|
25
|
+
]);
|
|
26
|
+
function shortHash(value) {
|
|
27
|
+
return createHash('sha256').update(value).digest('hex').slice(0, 12);
|
|
28
|
+
}
|
|
29
|
+
export function buildWorkspaceRootId(workspaceRoot) {
|
|
30
|
+
let stablePath = resolve(workspaceRoot);
|
|
31
|
+
try {
|
|
32
|
+
stablePath = realpathSync(stablePath);
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
// Nonexistent roots are validated elsewhere; keep deterministic IDs.
|
|
36
|
+
}
|
|
37
|
+
return `workspace-root-${shortHash(stablePath)}`;
|
|
38
|
+
}
|
|
39
|
+
function realDirectory(path) {
|
|
40
|
+
try {
|
|
41
|
+
const resolved = resolve(path);
|
|
42
|
+
if (!existsSync(resolved) || !statSync(resolved).isDirectory()) {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
return realpathSync(resolved);
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
function isWithin(parent, child) {
|
|
52
|
+
const rel = relative(parent, child);
|
|
53
|
+
return rel === '' || (rel.length > 0 && !rel.startsWith('..') && !isAbsolute(rel));
|
|
54
|
+
}
|
|
55
|
+
function toRelativeProjectPath(rootCwd, projectCwd) {
|
|
56
|
+
const rel = relative(rootCwd, projectCwd).split(sep).join('/');
|
|
57
|
+
return rel || '.';
|
|
58
|
+
}
|
|
59
|
+
function hasProjectMarker(cwd) {
|
|
60
|
+
return PROJECT_MARKERS.some((marker) => existsSync(join(cwd, marker)));
|
|
61
|
+
}
|
|
62
|
+
function normalizeWorkspaceRoots(workspaceRoots) {
|
|
63
|
+
const roots = [];
|
|
64
|
+
const warnings = [];
|
|
65
|
+
const seen = new Set();
|
|
66
|
+
const seenLabels = new Map();
|
|
67
|
+
for (const rawRoot of workspaceRoots) {
|
|
68
|
+
const cwd = realDirectory(rawRoot);
|
|
69
|
+
if (!cwd) {
|
|
70
|
+
warnings.push(`Workspace root is not a readable directory: ${rawRoot}`);
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
if (seen.has(cwd)) {
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
seen.add(cwd);
|
|
77
|
+
const baseLabel = basename(cwd) || cwd;
|
|
78
|
+
const seenLabelCount = (seenLabels.get(baseLabel) ?? 0) + 1;
|
|
79
|
+
seenLabels.set(baseLabel, seenLabelCount);
|
|
80
|
+
roots.push({
|
|
81
|
+
id: buildWorkspaceRootId(cwd),
|
|
82
|
+
label: seenLabelCount === 1 ? baseLabel : `${baseLabel} (${seenLabelCount})`,
|
|
83
|
+
cwd,
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
return { roots, warnings };
|
|
87
|
+
}
|
|
88
|
+
function findWorkspaceRoot(roots, cwd) {
|
|
89
|
+
const resolved = realDirectory(cwd) ?? resolve(cwd);
|
|
90
|
+
return [...roots]
|
|
91
|
+
.sort((a, b) => b.cwd.length - a.cwd.length)
|
|
92
|
+
.find((root) => isWithin(root.cwd, resolved)) ?? null;
|
|
93
|
+
}
|
|
94
|
+
function annotateWorkspaceOption(option, roots) {
|
|
95
|
+
const root = findWorkspaceRoot(roots, option.cwd);
|
|
96
|
+
if (!root) {
|
|
97
|
+
return option;
|
|
98
|
+
}
|
|
99
|
+
const stableCwd = realDirectory(option.cwd) ?? resolve(option.cwd);
|
|
100
|
+
const relativePath = toRelativeProjectPath(root.cwd, stableCwd);
|
|
101
|
+
return {
|
|
102
|
+
...option,
|
|
103
|
+
workspaceRootId: root.id,
|
|
104
|
+
workspaceRelativePath: relativePath,
|
|
105
|
+
description: relativePath === '.'
|
|
106
|
+
? `${root.label} root`
|
|
107
|
+
: `${root.label}/${relativePath}`,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
export function discoverWorkspaceProjects(root, warnings) {
|
|
111
|
+
const projects = [];
|
|
112
|
+
if (hasProjectMarker(root.cwd)) {
|
|
113
|
+
projects.push({
|
|
114
|
+
id: buildWorkspaceOptionId(root.cwd),
|
|
115
|
+
label: root.label,
|
|
116
|
+
cwd: root.cwd,
|
|
117
|
+
workspaceRootId: root.id,
|
|
118
|
+
workspaceRelativePath: '.',
|
|
119
|
+
description: `${root.label} root`,
|
|
120
|
+
source: 'discovered',
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
let entries;
|
|
124
|
+
try {
|
|
125
|
+
entries = readdirSync(root.cwd, { withFileTypes: true });
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
warnings?.push(`Workspace root could not be scanned: ${root.cwd}`);
|
|
129
|
+
return projects;
|
|
130
|
+
}
|
|
131
|
+
for (const entry of entries) {
|
|
132
|
+
if (!entry.isDirectory() || IGNORED_PROJECT_DIRS.has(entry.name)) {
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
const cwd = realDirectory(join(root.cwd, entry.name));
|
|
136
|
+
if (!cwd || !isWithin(root.cwd, cwd) || !hasProjectMarker(cwd)) {
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
const relativePath = toRelativeProjectPath(root.cwd, cwd);
|
|
140
|
+
projects.push({
|
|
141
|
+
id: buildWorkspaceOptionId(cwd),
|
|
142
|
+
label: basename(cwd) || relativePath,
|
|
143
|
+
cwd,
|
|
144
|
+
workspaceRootId: root.id,
|
|
145
|
+
workspaceRelativePath: relativePath,
|
|
146
|
+
description: `${root.label}/${relativePath}`,
|
|
147
|
+
source: 'discovered',
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
return projects;
|
|
151
|
+
}
|
|
152
|
+
export function buildPublicWorkspaceRoots(roots) {
|
|
153
|
+
return roots.map((root) => ({
|
|
154
|
+
id: root.id,
|
|
155
|
+
label: root.label,
|
|
156
|
+
...(root.description ? { description: root.description } : {}),
|
|
157
|
+
...(root.defaultRelativePath !== undefined
|
|
158
|
+
? { defaultRelativePath: root.defaultRelativePath }
|
|
159
|
+
: {}),
|
|
160
|
+
}));
|
|
161
|
+
}
|
|
162
|
+
function relabelWorkspaceOptions(options) {
|
|
163
|
+
const seenLabels = new Map();
|
|
164
|
+
return options.map((option) => {
|
|
165
|
+
const seenCount = (seenLabels.get(option.label) ?? 0) + 1;
|
|
166
|
+
seenLabels.set(option.label, seenCount);
|
|
167
|
+
return seenCount === 1
|
|
168
|
+
? option
|
|
169
|
+
: { ...option, label: `${option.label} (${seenCount})` };
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
export function buildConfiguredWorkspaceOptionsWithRoots(input) {
|
|
173
|
+
const normalizedRoots = normalizeWorkspaceRoots(input.workspaceRoots ?? []);
|
|
174
|
+
const explicitOptions = buildConfiguredWorkspaceOptions(input.primaryCwd, input.configuredWorkspaces ?? []).map((option) => annotateWorkspaceOption(option, normalizedRoots.roots));
|
|
175
|
+
const discoveryWarnings = [];
|
|
176
|
+
const discoveredOptions = normalizedRoots.roots.flatMap((root) => (discoverWorkspaceProjects(root, discoveryWarnings)));
|
|
177
|
+
const byCwd = new Map();
|
|
178
|
+
for (const option of discoveredOptions) {
|
|
179
|
+
byCwd.set(resolve(option.cwd), option);
|
|
180
|
+
}
|
|
181
|
+
for (const option of explicitOptions) {
|
|
182
|
+
byCwd.set(resolve(option.cwd), option);
|
|
183
|
+
}
|
|
184
|
+
const ordered = [
|
|
185
|
+
...explicitOptions,
|
|
186
|
+
...discoveredOptions.filter((option) => !explicitOptions.some((explicit) => resolve(explicit.cwd) === resolve(option.cwd))),
|
|
187
|
+
].filter((option, index, options) => (options.findIndex((candidate) => resolve(candidate.cwd) === resolve(option.cwd)) === index)).map((option) => byCwd.get(resolve(option.cwd)) ?? option);
|
|
188
|
+
return {
|
|
189
|
+
workspaceOptions: relabelWorkspaceOptions(ordered),
|
|
190
|
+
workspaceRoots: normalizedRoots.roots,
|
|
191
|
+
warnings: [...normalizedRoots.warnings, ...discoveryWarnings],
|
|
192
|
+
};
|
|
193
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@canonmsg/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"description": "Canon core — shared types, REST client, SSE stream, and registration for Canon messaging",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
"scripts": {
|
|
24
24
|
"build": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\" && tsc",
|
|
25
25
|
"dev": "tsc --watch",
|
|
26
|
+
"test": "vitest run",
|
|
26
27
|
"prepack": "npm run build"
|
|
27
28
|
},
|
|
28
29
|
"engines": {
|