@canonmsg/core 0.23.0 → 0.24.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/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/runtime-state-publisher.d.ts +1 -0
- package/dist/streaming.d.ts +2 -1
- package/dist/streaming.js +2 -1
- package/dist/turn-output-controller.d.ts +35 -0
- package/dist/turn-output-controller.js +125 -0
- package/dist/types.d.ts +1 -0
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -24,6 +24,8 @@ export { buildRuntimeInputOutcome, buildRuntimeInputReply, buildRuntimeInputRequ
|
|
|
24
24
|
export type { ClaudeQuestionMetadata, ClaudeQuestionReplyMetadata, PlanApprovalMetadata, PlanApprovalReplyMetadata, RuntimeInputChoice, RuntimeInputKind, RuntimeInputNativeMetadata, RuntimeInputOutcomeMetadata, RuntimeInputReplyMetadata, RuntimeInputReplyStatus, RuntimeInputRequestMetadata, RuntimeInputResolutionStatus, RuntimeQuestionDefinition, RuntimeQuestionOption, } from './runtime-cards.js';
|
|
25
25
|
export { createStreamingHelper } from './streaming.js';
|
|
26
26
|
export type { RTDBHandle, RTDBRef, ServerTimestamp, StreamingHelperOptions, StreamingNode } from './streaming.js';
|
|
27
|
+
export { createTurnOutputController } from './turn-output-controller.js';
|
|
28
|
+
export type { TurnOutputController, TurnOutputControllerOptions, TurnOutputMode, TurnOutputSnapshot, } from './turn-output-controller.js';
|
|
27
29
|
export { clearPendingRegistration, getOrCreatePendingRegistration, loadPendingRegistrations, loadProfiles, savePendingRegistrations, saveProfiles, updatePendingRegistration, upsertAgentProfile, isProfileLocked, acquireLock, releaseLock, isProcessAlive, CANON_DIR, AGENTS_PATH, LOCKS_DIR, } from './agent-profiles.js';
|
|
28
30
|
export type { AgentProfile, PendingRegistration, ProfileLockHandle } from './agent-profiles.js';
|
|
29
31
|
export { resolveCanonAgent, resolveCanonProfile, getActiveProfile, getActiveProfileLock } from './agent-resolver.js';
|
package/dist/index.js
CHANGED
|
@@ -22,6 +22,7 @@ export { DEFAULT_APPROVAL_CONFIG, parseApprovalRequestMetadata, parseApprovalRep
|
|
|
22
22
|
export { buildRuntimeInputOutcome, buildRuntimeInputReply, buildRuntimeInputRequest, buildPlanApprovalReply, buildPlanApprovalRequest, buildQuestionReply, buildQuestionRequest, parseRuntimeInputOutcomeMetadata, parseRuntimeInputReplyMetadata, parseRuntimeInputRequestMetadata, } from './runtime-cards.js';
|
|
23
23
|
// Streaming (RTDB helpers)
|
|
24
24
|
export { createStreamingHelper } from './streaming.js';
|
|
25
|
+
export { createTurnOutputController } from './turn-output-controller.js';
|
|
25
26
|
// Agent profiles (loading, locking, resolution)
|
|
26
27
|
export { clearPendingRegistration, getOrCreatePendingRegistration, loadPendingRegistrations, loadProfiles, savePendingRegistrations, saveProfiles, updatePendingRegistration, upsertAgentProfile, isProfileLocked, acquireLock, releaseLock, isProcessAlive, CANON_DIR, AGENTS_PATH, LOCKS_DIR, } from './agent-profiles.js';
|
|
27
28
|
// Agent resolver
|
package/dist/streaming.d.ts
CHANGED
|
@@ -23,6 +23,7 @@ export interface StreamingNode {
|
|
|
23
23
|
startedAt: unknown;
|
|
24
24
|
updatedAt: unknown;
|
|
25
25
|
messageId: string;
|
|
26
|
+
turnId?: string | null;
|
|
26
27
|
}
|
|
27
28
|
/**
|
|
28
29
|
* Creates helpers that read/write the RTDB streaming node for a single
|
|
@@ -41,7 +42,7 @@ export interface StreamingNode {
|
|
|
41
42
|
export declare function createStreamingHelper(opts: StreamingHelperOptions): {
|
|
42
43
|
forConversation: (conversationId: string, agentId: string) => {
|
|
43
44
|
/** Write the initial streaming node (status: "thinking", empty text). */
|
|
44
|
-
start(messageId: string): Promise<void>;
|
|
45
|
+
start(messageId: string, turnId?: string | null): Promise<void>;
|
|
45
46
|
/** Update the accumulated text and optionally change status. */
|
|
46
47
|
update(text: string, status?: StreamingStatus): Promise<void>;
|
|
47
48
|
/** Set status without changing text (e.g. switching to "tool"). */
|
package/dist/streaming.js
CHANGED
|
@@ -20,13 +20,14 @@ export function createStreamingHelper(opts) {
|
|
|
20
20
|
const nodeRef = db.ref(nodePath);
|
|
21
21
|
return {
|
|
22
22
|
/** Write the initial streaming node (status: "thinking", empty text). */
|
|
23
|
-
async start(messageId) {
|
|
23
|
+
async start(messageId, turnId) {
|
|
24
24
|
const node = {
|
|
25
25
|
text: '',
|
|
26
26
|
status: 'thinking',
|
|
27
27
|
startedAt: serverTimestamp,
|
|
28
28
|
updatedAt: serverTimestamp,
|
|
29
29
|
messageId,
|
|
30
|
+
...(turnId ? { turnId } : {}),
|
|
30
31
|
};
|
|
31
32
|
await nodeRef.set(node);
|
|
32
33
|
},
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { StreamingStatus } from './types.js';
|
|
2
|
+
export type TurnOutputMode = 'delta' | 'block' | 'snapshot' | 'status';
|
|
3
|
+
export interface TurnOutputSnapshot {
|
|
4
|
+
turnId: string;
|
|
5
|
+
messageId: string;
|
|
6
|
+
text: string;
|
|
7
|
+
status: StreamingStatus;
|
|
8
|
+
}
|
|
9
|
+
export interface TurnOutputControllerOptions {
|
|
10
|
+
turnId: string;
|
|
11
|
+
mode?: TurnOutputMode;
|
|
12
|
+
throttleMs?: number;
|
|
13
|
+
blockSeparator?: string;
|
|
14
|
+
writeSnapshot: (snapshot: TurnOutputSnapshot) => void | Promise<void>;
|
|
15
|
+
clearSnapshot: () => void | Promise<void>;
|
|
16
|
+
schedule?: typeof setTimeout;
|
|
17
|
+
cancel?: typeof clearTimeout;
|
|
18
|
+
}
|
|
19
|
+
export interface TurnOutputController {
|
|
20
|
+
readonly turnId: string;
|
|
21
|
+
readonly mode: TurnOutputMode;
|
|
22
|
+
getText(): string;
|
|
23
|
+
getStatus(): StreamingStatus;
|
|
24
|
+
startThinking(text?: string): Promise<void>;
|
|
25
|
+
startStreaming(text?: string): Promise<void>;
|
|
26
|
+
appendDelta(delta: string): void;
|
|
27
|
+
appendBlock(block: string): void;
|
|
28
|
+
replaceSnapshot(text: string, status?: StreamingStatus): Promise<void>;
|
|
29
|
+
setStatus(status: StreamingStatus, text?: string): Promise<void>;
|
|
30
|
+
flush(status?: StreamingStatus): Promise<void>;
|
|
31
|
+
waitingInput(): Promise<void>;
|
|
32
|
+
interrupt(): Promise<void>;
|
|
33
|
+
clear(): Promise<void>;
|
|
34
|
+
}
|
|
35
|
+
export declare function createTurnOutputController(options: TurnOutputControllerOptions): TurnOutputController;
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
const DEFAULT_THROTTLE_MS = 120;
|
|
2
|
+
const DEFAULT_BLOCK_SEPARATOR = '\n\n';
|
|
3
|
+
function normalizeStatus(status) {
|
|
4
|
+
return status === 'thinking' || status === 'tool' || status === 'streaming'
|
|
5
|
+
? status
|
|
6
|
+
: 'streaming';
|
|
7
|
+
}
|
|
8
|
+
function appendBlockText(currentText, block, separator) {
|
|
9
|
+
if (!block)
|
|
10
|
+
return currentText;
|
|
11
|
+
const prefix = currentText ? separator : '';
|
|
12
|
+
const combined = `${currentText}${prefix}${block}`;
|
|
13
|
+
return separator === '\n\n' ? combined.replace(/\n{3,}/g, '\n\n') : combined;
|
|
14
|
+
}
|
|
15
|
+
export function createTurnOutputController(options) {
|
|
16
|
+
const schedule = options.schedule ?? setTimeout;
|
|
17
|
+
const cancel = options.cancel ?? clearTimeout;
|
|
18
|
+
const throttleMs = Math.max(0, options.throttleMs ?? DEFAULT_THROTTLE_MS);
|
|
19
|
+
const mode = options.mode ?? 'snapshot';
|
|
20
|
+
const blockSeparator = options.blockSeparator ?? DEFAULT_BLOCK_SEPARATOR;
|
|
21
|
+
let text = '';
|
|
22
|
+
let status = 'thinking';
|
|
23
|
+
let timer = null;
|
|
24
|
+
let pendingPromise = Promise.resolve();
|
|
25
|
+
const write = (nextStatus = status) => {
|
|
26
|
+
if (mode === 'status')
|
|
27
|
+
return;
|
|
28
|
+
const snapshot = {
|
|
29
|
+
turnId: options.turnId,
|
|
30
|
+
messageId: options.turnId,
|
|
31
|
+
text,
|
|
32
|
+
status: normalizeStatus(nextStatus),
|
|
33
|
+
};
|
|
34
|
+
pendingPromise = pendingPromise
|
|
35
|
+
.catch(() => { })
|
|
36
|
+
.then(() => Promise.resolve(options.writeSnapshot(snapshot)).catch(() => { }));
|
|
37
|
+
};
|
|
38
|
+
const cancelTimer = () => {
|
|
39
|
+
if (!timer)
|
|
40
|
+
return;
|
|
41
|
+
cancel(timer);
|
|
42
|
+
timer = null;
|
|
43
|
+
};
|
|
44
|
+
const scheduleWrite = () => {
|
|
45
|
+
if (mode === 'status')
|
|
46
|
+
return;
|
|
47
|
+
if (throttleMs <= 0) {
|
|
48
|
+
write();
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
if (timer)
|
|
52
|
+
return;
|
|
53
|
+
timer = schedule(() => {
|
|
54
|
+
timer = null;
|
|
55
|
+
write();
|
|
56
|
+
}, throttleMs);
|
|
57
|
+
};
|
|
58
|
+
const flush = async (nextStatus = status) => {
|
|
59
|
+
cancelTimer();
|
|
60
|
+
write(nextStatus);
|
|
61
|
+
await pendingPromise;
|
|
62
|
+
};
|
|
63
|
+
const clear = async () => {
|
|
64
|
+
cancelTimer();
|
|
65
|
+
text = '';
|
|
66
|
+
status = 'thinking';
|
|
67
|
+
pendingPromise = pendingPromise
|
|
68
|
+
.catch(() => { })
|
|
69
|
+
.then(() => Promise.resolve(options.clearSnapshot()).catch(() => { }));
|
|
70
|
+
await pendingPromise;
|
|
71
|
+
};
|
|
72
|
+
return {
|
|
73
|
+
turnId: options.turnId,
|
|
74
|
+
mode,
|
|
75
|
+
getText() {
|
|
76
|
+
return text;
|
|
77
|
+
},
|
|
78
|
+
getStatus() {
|
|
79
|
+
return status;
|
|
80
|
+
},
|
|
81
|
+
async startThinking(nextText = '') {
|
|
82
|
+
text = nextText;
|
|
83
|
+
status = 'thinking';
|
|
84
|
+
await flush('thinking');
|
|
85
|
+
},
|
|
86
|
+
async startStreaming(nextText = text) {
|
|
87
|
+
text = nextText;
|
|
88
|
+
status = 'streaming';
|
|
89
|
+
await flush('streaming');
|
|
90
|
+
},
|
|
91
|
+
appendDelta(delta) {
|
|
92
|
+
if (!delta)
|
|
93
|
+
return;
|
|
94
|
+
if (status !== 'streaming')
|
|
95
|
+
text = '';
|
|
96
|
+
text += delta;
|
|
97
|
+
status = 'streaming';
|
|
98
|
+
scheduleWrite();
|
|
99
|
+
},
|
|
100
|
+
appendBlock(block) {
|
|
101
|
+
if (!block)
|
|
102
|
+
return;
|
|
103
|
+
if (status !== 'streaming')
|
|
104
|
+
text = '';
|
|
105
|
+
text = appendBlockText(text, block, blockSeparator);
|
|
106
|
+
status = 'streaming';
|
|
107
|
+
scheduleWrite();
|
|
108
|
+
},
|
|
109
|
+
async replaceSnapshot(nextText, nextStatus = 'streaming') {
|
|
110
|
+
text = nextText;
|
|
111
|
+
status = normalizeStatus(nextStatus);
|
|
112
|
+
await flush(status);
|
|
113
|
+
},
|
|
114
|
+
async setStatus(nextStatus, nextText) {
|
|
115
|
+
if (nextText !== undefined)
|
|
116
|
+
text = nextText;
|
|
117
|
+
status = normalizeStatus(nextStatus);
|
|
118
|
+
await flush(status);
|
|
119
|
+
},
|
|
120
|
+
flush,
|
|
121
|
+
waitingInput: clear,
|
|
122
|
+
interrupt: clear,
|
|
123
|
+
clear,
|
|
124
|
+
};
|
|
125
|
+
}
|
package/dist/types.d.ts
CHANGED