@memberjunction/realtime-runtime 6.2.0-edge.3 → 6.2.0-edge.5
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 +75 -0
- package/dist/channels/base-realtime-channel-client.d.ts +535 -28
- package/dist/channels/base-realtime-channel-client.d.ts.map +1 -1
- package/dist/channels/base-realtime-channel-client.js +864 -10
- package/dist/channels/base-realtime-channel-client.js.map +1 -1
- package/dist/channels/channel-action-dispatcher.d.ts +70 -0
- package/dist/channels/channel-action-dispatcher.d.ts.map +1 -0
- package/dist/channels/channel-action-dispatcher.js +136 -0
- package/dist/channels/channel-action-dispatcher.js.map +1 -0
- package/dist/channels/channel-catalog-note.d.ts +48 -0
- package/dist/channels/channel-catalog-note.d.ts.map +1 -0
- package/dist/channels/channel-catalog-note.js +119 -0
- package/dist/channels/channel-catalog-note.js.map +1 -0
- package/dist/channels/channel-contract-types.d.ts +107 -0
- package/dist/channels/channel-contract-types.d.ts.map +1 -0
- package/dist/channels/channel-contract-types.js +12 -0
- package/dist/channels/channel-contract-types.js.map +1 -0
- package/dist/channels/channel-descriptor-synthesis.d.ts +56 -0
- package/dist/channels/channel-descriptor-synthesis.d.ts.map +1 -0
- package/dist/channels/channel-descriptor-synthesis.js +90 -0
- package/dist/channels/channel-descriptor-synthesis.js.map +1 -0
- package/dist/channels/channel-perception.d.ts +82 -0
- package/dist/channels/channel-perception.d.ts.map +1 -0
- package/dist/channels/channel-perception.js +120 -0
- package/dist/channels/channel-perception.js.map +1 -0
- package/dist/channels/channel-schema-format.d.ts +17 -0
- package/dist/channels/channel-schema-format.d.ts.map +1 -0
- package/dist/channels/channel-schema-format.js +45 -0
- package/dist/channels/channel-schema-format.js.map +1 -0
- package/dist/channels/channel-state-delta.d.ts +59 -0
- package/dist/channels/channel-state-delta.d.ts.map +1 -0
- package/dist/channels/channel-state-delta.js +116 -0
- package/dist/channels/channel-state-delta.js.map +1 -0
- package/dist/channels/channel-surface-placement.d.ts +25 -0
- package/dist/channels/channel-surface-placement.d.ts.map +1 -0
- package/dist/channels/channel-surface-placement.js +29 -0
- package/dist/channels/channel-surface-placement.js.map +1 -0
- package/dist/channels/channel-visual-pump.d.ts +126 -0
- package/dist/channels/channel-visual-pump.d.ts.map +1 -0
- package/dist/channels/channel-visual-pump.js +174 -0
- package/dist/channels/channel-visual-pump.js.map +1 -0
- package/dist/hosts/IRealtimeMediaHost.d.ts +86 -34
- package/dist/hosts/IRealtimeMediaHost.d.ts.map +1 -1
- package/dist/hosts/IRealtimeMediaHost.js +2 -1
- package/dist/hosts/IRealtimeMediaHost.js.map +1 -1
- package/dist/index.d.ts +26 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +28 -1
- package/dist/index.js.map +1 -1
- package/dist/session/RealtimeSessionRuntime.d.ts +984 -111
- package/dist/session/RealtimeSessionRuntime.d.ts.map +1 -1
- package/dist/session/RealtimeSessionRuntime.js +2396 -381
- package/dist/session/RealtimeSessionRuntime.js.map +1 -1
- package/dist/session/RecordingShardDrain.d.ts +59 -0
- package/dist/session/RecordingShardDrain.d.ts.map +1 -0
- package/dist/session/RecordingShardDrain.js +135 -0
- package/dist/session/RecordingShardDrain.js.map +1 -0
- package/dist/session/RecordingShardQueue.d.ts +94 -0
- package/dist/session/RecordingShardQueue.d.ts.map +1 -0
- package/dist/session/RecordingShardQueue.js +176 -0
- package/dist/session/RecordingShardQueue.js.map +1 -0
- package/dist/session/RecordingShardStore.d.ts +57 -0
- package/dist/session/RecordingShardStore.d.ts.map +1 -0
- package/dist/session/RecordingShardStore.js +184 -0
- package/dist/session/RecordingShardStore.js.map +1 -0
- package/dist/session/RecordingShardWindow.d.ts +42 -0
- package/dist/session/RecordingShardWindow.d.ts.map +1 -0
- package/dist/session/RecordingShardWindow.js +88 -0
- package/dist/session/RecordingShardWindow.js.map +1 -0
- package/dist/session/RecordingTransport.d.ts +66 -0
- package/dist/session/RecordingTransport.d.ts.map +1 -0
- package/dist/session/RecordingTransport.js +61 -0
- package/dist/session/RecordingTransport.js.map +1 -0
- package/dist/session/app-client-tool-registry.d.ts +76 -0
- package/dist/session/app-client-tool-registry.d.ts.map +1 -0
- package/dist/session/app-client-tool-registry.js +100 -0
- package/dist/session/app-client-tool-registry.js.map +1 -0
- package/dist/session/avatar-notice.d.ts +43 -0
- package/dist/session/avatar-notice.d.ts.map +1 -0
- package/dist/session/avatar-notice.js +60 -0
- package/dist/session/avatar-notice.js.map +1 -0
- package/dist/session/call-end.d.ts +21 -0
- package/dist/session/call-end.d.ts.map +1 -0
- package/dist/session/call-end.js +19 -0
- package/dist/session/call-end.js.map +1 -0
- package/dist/session/channel-exposure-preferences.d.ts +70 -0
- package/dist/session/channel-exposure-preferences.d.ts.map +1 -0
- package/dist/session/channel-exposure-preferences.js +138 -0
- package/dist/session/channel-exposure-preferences.js.map +1 -0
- package/dist/session/channel-session-scope.d.ts +142 -0
- package/dist/session/channel-session-scope.d.ts.map +1 -0
- package/dist/session/channel-session-scope.js +128 -0
- package/dist/session/channel-session-scope.js.map +1 -0
- package/dist/session/client-session-deadline.d.ts +28 -0
- package/dist/session/client-session-deadline.d.ts.map +1 -0
- package/dist/session/client-session-deadline.js +50 -0
- package/dist/session/client-session-deadline.js.map +1 -0
- package/dist/session/realtime-captures.d.ts +236 -0
- package/dist/session/realtime-captures.d.ts.map +1 -0
- package/dist/session/realtime-captures.js +383 -0
- package/dist/session/realtime-captures.js.map +1 -0
- package/dist/session/realtime-microphone.d.ts +50 -0
- package/dist/session/realtime-microphone.d.ts.map +1 -0
- package/dist/session/realtime-microphone.js +51 -0
- package/dist/session/realtime-microphone.js.map +1 -0
- package/dist/session/session-event-hub.d.ts +72 -0
- package/dist/session/session-event-hub.d.ts.map +1 -0
- package/dist/session/session-event-hub.js +151 -0
- package/dist/session/session-event-hub.js.map +1 -0
- package/dist/session/session-launcher.d.ts +128 -0
- package/dist/session/session-launcher.d.ts.map +1 -0
- package/dist/session/session-launcher.js +160 -0
- package/dist/session/session-launcher.js.map +1 -0
- package/package.json +10 -9
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview **Legacy descriptor synthesis** — how a channel written against the original (v1)
|
|
3
|
+
* members gets a v2 descriptor without being edited.
|
|
4
|
+
*
|
|
5
|
+
* Back-compat is absolute: a plugin that implements only `GetToolDefinitions()`, `ApplyAgentTool()`
|
|
6
|
+
* and tab chrome keeps working unchanged, and — because the runtime reads descriptors, never the
|
|
7
|
+
* v1 members, when it scopes channels and advertises them — it must be *describable*. Synthesis
|
|
8
|
+
* derives a descriptor from what the plugin already declares:
|
|
9
|
+
*
|
|
10
|
+
* - **verbs** from its tool definitions (the tool name minus the shared prefix; the full tool name
|
|
11
|
+
* is kept as `NativeToolName`, so the proxy and the native path reach the same executor);
|
|
12
|
+
* - **instructions** from the registry row's `Description` when the runtime has one, else the
|
|
13
|
+
* channel's first-run intro, else a plain statement of the tool prefix;
|
|
14
|
+
* - **availability / display / exposure** from conservative defaults that preserve today's
|
|
15
|
+
* behavior: `'all-sessions'` (every pre-existing channel was), `'open-on-start'` when it has a
|
|
16
|
+
* surface and `'headless'` when it does not, and `'pixels'` only when it sources video.
|
|
17
|
+
*
|
|
18
|
+
* The result is stamped with {@link REALTIME_CHANNEL_LEGACY_CONTRACT_VERSION} so a synthesized
|
|
19
|
+
* descriptor is always distinguishable from an authored one.
|
|
20
|
+
*
|
|
21
|
+
* @module @memberjunction/realtime-runtime
|
|
22
|
+
*/
|
|
23
|
+
import type { RealtimeToolDefinition } from '@memberjunction/ai';
|
|
24
|
+
import { type RealtimeChannelDescriptor, type RealtimeChannelInvoker, type RealtimeChannelVerb } from '@memberjunction/ai-core-plus';
|
|
25
|
+
import type { BaseRealtimeChannelClient } from './base-realtime-channel-client.js';
|
|
26
|
+
/**
|
|
27
|
+
* The verb name for a native tool: the tool name with the channel's shared prefix removed
|
|
28
|
+
* (`'Whiteboard_AddNote'` → `'AddNote'`). A tool that does not carry the prefix — or that *is* the
|
|
29
|
+
* prefix, like `ContextTool` — keeps its full name.
|
|
30
|
+
*/
|
|
31
|
+
export declare function VerbNameForTool(toolName: string, toolNamePrefix: string): string;
|
|
32
|
+
/**
|
|
33
|
+
* Builds the verb list for a channel whose actions are its native tools. Both the synthesizer and
|
|
34
|
+
* the reference channels' explicit descriptors use it, so a subclass that adds a tool (an app
|
|
35
|
+
* extending a MJ channel) gets a verb for it automatically instead of a descriptor that lies.
|
|
36
|
+
*
|
|
37
|
+
* @param tools The channel's current tool declarations (`GetToolDefinitions()`).
|
|
38
|
+
* @param toolNamePrefix The channel's shared tool-name prefix.
|
|
39
|
+
* @param invokableBy Who may invoke these verbs (default `'agent'` — native tools are agent tools).
|
|
40
|
+
* @param returnsChannelData Native tool name to the exposure its RESULT requires, for the tools whose result carries what
|
|
41
|
+
* the channel holds (see `RealtimeChannelVerb.ReturnsChannelData`); tools not listed only act.
|
|
42
|
+
*/
|
|
43
|
+
export declare function BuildToolBackedVerbs(tools: ReadonlyArray<RealtimeToolDefinition>, toolNamePrefix: string, invokableBy?: RealtimeChannelInvoker, returnsChannelData?: Readonly<Record<string, 'state' | 'pixels'>>): RealtimeChannelVerb[];
|
|
44
|
+
/** Extra context the runtime can supply that the channel instance cannot know. */
|
|
45
|
+
export interface DescriptorSynthesisContext {
|
|
46
|
+
/** The registry row's `Description`, when the channel was resolved from a row. */
|
|
47
|
+
RegistryDescription?: string | null;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Synthesizes a v2 descriptor for a channel that implements only the v1 members.
|
|
51
|
+
*
|
|
52
|
+
* @param channel The channel (need not be initialized).
|
|
53
|
+
* @param context Optional runtime-supplied context (the registry row's description).
|
|
54
|
+
*/
|
|
55
|
+
export declare function SynthesizeChannelDescriptor(channel: BaseRealtimeChannelClient, context?: DescriptorSynthesisContext): RealtimeChannelDescriptor;
|
|
56
|
+
//# sourceMappingURL=channel-descriptor-synthesis.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"channel-descriptor-synthesis.d.ts","sourceRoot":"","sources":["../../src/channels/channel-descriptor-synthesis.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AACjE,OAAO,EAEH,KAAK,yBAAyB,EAC9B,KAAK,sBAAsB,EAC3B,KAAK,mBAAmB,EAC3B,MAAM,8BAA8B,CAAC;AACtC,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,gCAAgC,CAAC;AAEhF;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,MAAM,CAIhF;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,oBAAoB,CAChC,KAAK,EAAE,aAAa,CAAC,sBAAsB,CAAC,EAC5C,cAAc,EAAE,MAAM,EACtB,WAAW,GAAE,sBAAgC,EAC7C,kBAAkB,GAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,GAAG,QAAQ,CAAC,CAAM,GACtE,mBAAmB,EAAE,CASvB;AAED,kFAAkF;AAClF,MAAM,WAAW,0BAA0B;IACvC,kFAAkF;IAClF,mBAAmB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACvC;AAED;;;;;GAKG;AACH,wBAAgB,2BAA2B,CACvC,OAAO,EAAE,yBAAyB,EAClC,OAAO,GAAE,0BAA+B,GACzC,yBAAyB,CAa3B"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview **Legacy descriptor synthesis** — how a channel written against the original (v1)
|
|
3
|
+
* members gets a v2 descriptor without being edited.
|
|
4
|
+
*
|
|
5
|
+
* Back-compat is absolute: a plugin that implements only `GetToolDefinitions()`, `ApplyAgentTool()`
|
|
6
|
+
* and tab chrome keeps working unchanged, and — because the runtime reads descriptors, never the
|
|
7
|
+
* v1 members, when it scopes channels and advertises them — it must be *describable*. Synthesis
|
|
8
|
+
* derives a descriptor from what the plugin already declares:
|
|
9
|
+
*
|
|
10
|
+
* - **verbs** from its tool definitions (the tool name minus the shared prefix; the full tool name
|
|
11
|
+
* is kept as `NativeToolName`, so the proxy and the native path reach the same executor);
|
|
12
|
+
* - **instructions** from the registry row's `Description` when the runtime has one, else the
|
|
13
|
+
* channel's first-run intro, else a plain statement of the tool prefix;
|
|
14
|
+
* - **availability / display / exposure** from conservative defaults that preserve today's
|
|
15
|
+
* behavior: `'all-sessions'` (every pre-existing channel was), `'open-on-start'` when it has a
|
|
16
|
+
* surface and `'headless'` when it does not, and `'pixels'` only when it sources video.
|
|
17
|
+
*
|
|
18
|
+
* The result is stamped with {@link REALTIME_CHANNEL_LEGACY_CONTRACT_VERSION} so a synthesized
|
|
19
|
+
* descriptor is always distinguishable from an authored one.
|
|
20
|
+
*
|
|
21
|
+
* @module @memberjunction/realtime-runtime
|
|
22
|
+
*/
|
|
23
|
+
import { REALTIME_CHANNEL_LEGACY_CONTRACT_VERSION, } from '@memberjunction/ai-core-plus';
|
|
24
|
+
/**
|
|
25
|
+
* The verb name for a native tool: the tool name with the channel's shared prefix removed
|
|
26
|
+
* (`'Whiteboard_AddNote'` → `'AddNote'`). A tool that does not carry the prefix — or that *is* the
|
|
27
|
+
* prefix, like `ContextTool` — keeps its full name.
|
|
28
|
+
*/
|
|
29
|
+
export function VerbNameForTool(toolName, toolNamePrefix) {
|
|
30
|
+
return toolNamePrefix.length > 0 && toolName.length > toolNamePrefix.length && toolName.startsWith(toolNamePrefix)
|
|
31
|
+
? toolName.slice(toolNamePrefix.length)
|
|
32
|
+
: toolName;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Builds the verb list for a channel whose actions are its native tools. Both the synthesizer and
|
|
36
|
+
* the reference channels' explicit descriptors use it, so a subclass that adds a tool (an app
|
|
37
|
+
* extending a MJ channel) gets a verb for it automatically instead of a descriptor that lies.
|
|
38
|
+
*
|
|
39
|
+
* @param tools The channel's current tool declarations (`GetToolDefinitions()`).
|
|
40
|
+
* @param toolNamePrefix The channel's shared tool-name prefix.
|
|
41
|
+
* @param invokableBy Who may invoke these verbs (default `'agent'` — native tools are agent tools).
|
|
42
|
+
* @param returnsChannelData Native tool name to the exposure its RESULT requires, for the tools whose result carries what
|
|
43
|
+
* the channel holds (see `RealtimeChannelVerb.ReturnsChannelData`); tools not listed only act.
|
|
44
|
+
*/
|
|
45
|
+
export function BuildToolBackedVerbs(tools, toolNamePrefix, invokableBy = 'agent', returnsChannelData = {}) {
|
|
46
|
+
return tools.map((tool) => ({
|
|
47
|
+
Name: VerbNameForTool(tool.Name, toolNamePrefix),
|
|
48
|
+
Description: tool.Description,
|
|
49
|
+
ParametersSchema: tool.ParametersSchema,
|
|
50
|
+
InvokableBy: invokableBy,
|
|
51
|
+
NativeToolName: tool.Name,
|
|
52
|
+
...(returnsChannelData[tool.Name] ? { ReturnsChannelData: returnsChannelData[tool.Name] } : {}),
|
|
53
|
+
}));
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Synthesizes a v2 descriptor for a channel that implements only the v1 members.
|
|
57
|
+
*
|
|
58
|
+
* @param channel The channel (need not be initialized).
|
|
59
|
+
* @param context Optional runtime-supplied context (the registry row's description).
|
|
60
|
+
*/
|
|
61
|
+
export function SynthesizeChannelDescriptor(channel, context = {}) {
|
|
62
|
+
const sourcesVideo = channel.GetSourcedTracks().some((t) => t.Modality === 'video' && t.Direction === 'inbound');
|
|
63
|
+
return {
|
|
64
|
+
Key: channel.ChannelName,
|
|
65
|
+
Version: REALTIME_CHANNEL_LEGACY_CONTRACT_VERSION,
|
|
66
|
+
DisplayName: channel.TabTitle,
|
|
67
|
+
Instructions: legacyInstructions(channel, context),
|
|
68
|
+
Nouns: [],
|
|
69
|
+
Verbs: BuildToolBackedVerbs(channel.GetToolDefinitions(), channel.ToolNamePrefix),
|
|
70
|
+
DisplayPolicy: channel.HasSurface() ? 'open-on-start' : 'headless',
|
|
71
|
+
DefaultAvailability: 'all-sessions',
|
|
72
|
+
MaxExposure: sourcesVideo ? 'pixels' : 'state',
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
/** The best available natural-language account of a legacy channel. */
|
|
76
|
+
function legacyInstructions(channel, context) {
|
|
77
|
+
const fromRegistry = context.RegistryDescription?.trim();
|
|
78
|
+
if (fromRegistry) {
|
|
79
|
+
return fromRegistry;
|
|
80
|
+
}
|
|
81
|
+
const fromIntro = channel.GetOnboardingDetails()?.Description?.trim();
|
|
82
|
+
if (fromIntro) {
|
|
83
|
+
return fromIntro;
|
|
84
|
+
}
|
|
85
|
+
const prefix = channel.ToolNamePrefix;
|
|
86
|
+
return prefix.length > 0
|
|
87
|
+
? `The ${channel.TabTitle} channel. Use its tools (names beginning "${prefix}") to act on it.`
|
|
88
|
+
: `The ${channel.TabTitle} channel.`;
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=channel-descriptor-synthesis.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"channel-descriptor-synthesis.js","sourceRoot":"","sources":["../../src/channels/channel-descriptor-synthesis.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAGH,OAAO,EACH,wCAAwC,GAI3C,MAAM,8BAA8B,CAAC;AAGtC;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,QAAgB,EAAE,cAAsB;IACpE,OAAO,cAAc,CAAC,MAAM,GAAG,CAAC,IAAI,QAAQ,CAAC,MAAM,GAAG,cAAc,CAAC,MAAM,IAAI,QAAQ,CAAC,UAAU,CAAC,cAAc,CAAC;QAC9G,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,MAAM,CAAC;QACvC,CAAC,CAAC,QAAQ,CAAC;AACnB,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,oBAAoB,CAChC,KAA4C,EAC5C,cAAsB,EACtB,cAAsC,OAAO,EAC7C,qBAAmE,EAAE;IAErE,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QACxB,IAAI,EAAE,eAAe,CAAC,IAAI,CAAC,IAAI,EAAE,cAAc,CAAC;QAChD,WAAW,EAAE,IAAI,CAAC,WAAW;QAC7B,gBAAgB,EAAE,IAAI,CAAC,gBAAgB;QACvC,WAAW,EAAE,WAAW;QACxB,cAAc,EAAE,IAAI,CAAC,IAAI;QACzB,GAAG,CAAC,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,kBAAkB,EAAE,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAClG,CAAC,CAAC,CAAC;AACR,CAAC;AAQD;;;;;GAKG;AACH,MAAM,UAAU,2BAA2B,CACvC,OAAkC,EAClC,UAAsC,EAAE;IAExC,MAAM,YAAY,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,OAAO,IAAI,CAAC,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC;IACjH,OAAO;QACH,GAAG,EAAE,OAAO,CAAC,WAAW;QACxB,OAAO,EAAE,wCAAwC;QACjD,WAAW,EAAE,OAAO,CAAC,QAAQ;QAC7B,YAAY,EAAE,kBAAkB,CAAC,OAAO,EAAE,OAAO,CAAC;QAClD,KAAK,EAAE,EAAE;QACT,KAAK,EAAE,oBAAoB,CAAC,OAAO,CAAC,kBAAkB,EAAE,EAAE,OAAO,CAAC,cAAc,CAAC;QACjF,aAAa,EAAE,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,UAAU;QAClE,mBAAmB,EAAE,cAAc;QACnC,WAAW,EAAE,YAAY,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO;KACjD,CAAC;AACN,CAAC;AAED,uEAAuE;AACvE,SAAS,kBAAkB,CAAC,OAAkC,EAAE,OAAmC;IAC/F,MAAM,YAAY,GAAG,OAAO,CAAC,mBAAmB,EAAE,IAAI,EAAE,CAAC;IACzD,IAAI,YAAY,EAAE,CAAC;QACf,OAAO,YAAY,CAAC;IACxB,CAAC;IACD,MAAM,SAAS,GAAG,OAAO,CAAC,oBAAoB,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC;IACtE,IAAI,SAAS,EAAE,CAAC;QACZ,OAAO,SAAS,CAAC;IACrB,CAAC;IACD,MAAM,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IACtC,OAAO,MAAM,CAAC,MAAM,GAAG,CAAC;QACpB,CAAC,CAAC,OAAO,OAAO,CAAC,QAAQ,6CAA6C,MAAM,kBAAkB;QAC9F,CAAC,CAAC,OAAO,OAAO,CAAC,QAAQ,WAAW,CAAC;AAC7C,CAAC"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview The **perception coalescer** — turns a burst of channel state changes into ONE
|
|
3
|
+
* structured note carrying a delta.
|
|
4
|
+
*
|
|
5
|
+
* A user dragging a shape on a whiteboard, or typing in a form, changes state dozens of times a
|
|
6
|
+
* second. The model needs to know *where things ended up*, not to be narrated every intermediate
|
|
7
|
+
* step: each note is context it must read, tokens it is billed for, and — worst — a prompt to
|
|
8
|
+
* respond. So changes are debounced: the first change arms a timer, further changes inside the
|
|
9
|
+
* window only extend the count, and when the window closes exactly one note goes out describing
|
|
10
|
+
* the difference between what the model was last told and the state now.
|
|
11
|
+
*
|
|
12
|
+
* Deliberately a small standalone class with injected clock/timers/state access, so the debounce
|
|
13
|
+
* and delta behavior are unit-testable with fake timers and no channel, session or DOM.
|
|
14
|
+
*
|
|
15
|
+
* @module @memberjunction/realtime-runtime
|
|
16
|
+
*/
|
|
17
|
+
import type { JSONObject } from '@memberjunction/ai';
|
|
18
|
+
/** How the coalescer reaches the channel it speaks for. */
|
|
19
|
+
export interface ChannelPerceptionHost {
|
|
20
|
+
/** The channel's key, for the note prefix. */
|
|
21
|
+
readonly ChannelKey: string;
|
|
22
|
+
/** The instance id, for the note prefix. */
|
|
23
|
+
readonly InstanceId: string;
|
|
24
|
+
/** Reads the channel's current state snapshot (the nouns). */
|
|
25
|
+
GetState(): JSONObject;
|
|
26
|
+
/** Delivers a finished note to the model. */
|
|
27
|
+
SendNote(text: string): void;
|
|
28
|
+
/** Reports a problem reading state (logged by the caller); the coalescer never throws into a timer. */
|
|
29
|
+
OnError(error: unknown): void;
|
|
30
|
+
}
|
|
31
|
+
/** Tuning for a {@link ChannelPerceptionCoalescer}. */
|
|
32
|
+
export interface ChannelPerceptionOptions {
|
|
33
|
+
/** How long a burst may keep extending before its note is sent (default 750ms — matches the whiteboard's surface coalescing). */
|
|
34
|
+
DebounceMs: number;
|
|
35
|
+
/** The largest note body (JSON chars) sent in full; larger ones degrade to a list of changed paths (default 4000). */
|
|
36
|
+
MaxNoteChars: number;
|
|
37
|
+
}
|
|
38
|
+
/** The defaults. */
|
|
39
|
+
export declare const DEFAULT_CHANNEL_PERCEPTION_OPTIONS: ChannelPerceptionOptions;
|
|
40
|
+
/**
|
|
41
|
+
* Coalesces state changes into debounced delta notes. One instance per channel instance.
|
|
42
|
+
*/
|
|
43
|
+
export declare class ChannelPerceptionCoalescer {
|
|
44
|
+
private readonly host;
|
|
45
|
+
private readonly options;
|
|
46
|
+
private timer;
|
|
47
|
+
/** What the model was last told, or `null` before the first note (the first note is a snapshot). */
|
|
48
|
+
private baseline;
|
|
49
|
+
private pendingChanges;
|
|
50
|
+
private pendingChangeId;
|
|
51
|
+
constructor(host: ChannelPerceptionHost, options?: ChannelPerceptionOptions);
|
|
52
|
+
/**
|
|
53
|
+
* Records that the state changed. Arms the debounce timer if it is not already running; the
|
|
54
|
+
* timer is NOT re-armed by further changes, so a continuous stream of edits still produces a
|
|
55
|
+
* note every {@link ChannelPerceptionOptions.DebounceMs} rather than starving until it stops.
|
|
56
|
+
*
|
|
57
|
+
* @param changeId The id the change was assigned; the note carries the latest one.
|
|
58
|
+
*/
|
|
59
|
+
Record(changeId: number): void;
|
|
60
|
+
/**
|
|
61
|
+
* Sets what the model is known to have been told, without sending anything — used right after a
|
|
62
|
+
* snapshot note went out by another route (e.g. when a channel opens), so the next change is
|
|
63
|
+
* described as a delta against it.
|
|
64
|
+
*/
|
|
65
|
+
SetBaseline(state: JSONObject): void;
|
|
66
|
+
/**
|
|
67
|
+
* Forgets what the model was told, so the NEXT note is a full snapshot. Used when the model starts
|
|
68
|
+
* seeing the channel again after exposure was withdrawn: whatever it was last told is stale.
|
|
69
|
+
*/
|
|
70
|
+
ResetBaseline(): void;
|
|
71
|
+
/** Drops any pending note without sending it (exposure was withdrawn: the model is no longer to be told). */
|
|
72
|
+
CancelPending(): void;
|
|
73
|
+
/** Sends the pending note now, if anything is pending. Safe to call at any time. */
|
|
74
|
+
Flush(): void;
|
|
75
|
+
/** Cancels any pending note. Call at teardown. */
|
|
76
|
+
Dispose(): void;
|
|
77
|
+
/** Builds and sends the note for the current burst: a snapshot the first time, a delta after. */
|
|
78
|
+
private sendNote;
|
|
79
|
+
/** Sends `full` unless it is too large, in which case the compact fallback goes instead. */
|
|
80
|
+
private deliver;
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=channel-perception.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"channel-perception.d.ts","sourceRoot":"","sources":["../../src/channels/channel-perception.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAGrD,2DAA2D;AAC3D,MAAM,WAAW,qBAAqB;IAClC,8CAA8C;IAC9C,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,4CAA4C;IAC5C,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,8DAA8D;IAC9D,QAAQ,IAAI,UAAU,CAAC;IACvB,6CAA6C;IAC7C,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,uGAAuG;IACvG,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAAC;CACjC;AAED,uDAAuD;AACvD,MAAM,WAAW,wBAAwB;IACrC,iIAAiI;IACjI,UAAU,EAAE,MAAM,CAAC;IACnB,sHAAsH;IACtH,YAAY,EAAE,MAAM,CAAC;CACxB;AAED,oBAAoB;AACpB,eAAO,MAAM,kCAAkC,EAAE,wBAGhD,CAAC;AAEF;;GAEG;AACH,qBAAa,0BAA0B;IAQ/B,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAR5B,OAAO,CAAC,KAAK,CAA8C;IAC3D,oGAAoG;IACpG,OAAO,CAAC,QAAQ,CAA2B;IAC3C,OAAO,CAAC,cAAc,CAAK;IAC3B,OAAO,CAAC,eAAe,CAAK;gBAGP,IAAI,EAAE,qBAAqB,EAC3B,OAAO,GAAE,wBAA6D;IAG3F;;;;;;OAMG;IACI,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAQrC;;;;OAIG;IACI,WAAW,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI;IAI3C;;;OAGG;IACI,aAAa,IAAI,IAAI;IAI5B,6GAA6G;IACtG,aAAa,IAAI,IAAI;IAQ5B,oFAAoF;IAC7E,KAAK,IAAI,IAAI;IAkBpB,kDAAkD;IAC3C,OAAO,IAAI,IAAI;IAQtB,iGAAiG;IACjG,OAAO,CAAC,QAAQ;IAiBhB,4FAA4F;IAC5F,OAAO,CAAC,OAAO;CAIlB"}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview The **perception coalescer** — turns a burst of channel state changes into ONE
|
|
3
|
+
* structured note carrying a delta.
|
|
4
|
+
*
|
|
5
|
+
* A user dragging a shape on a whiteboard, or typing in a form, changes state dozens of times a
|
|
6
|
+
* second. The model needs to know *where things ended up*, not to be narrated every intermediate
|
|
7
|
+
* step: each note is context it must read, tokens it is billed for, and — worst — a prompt to
|
|
8
|
+
* respond. So changes are debounced: the first change arms a timer, further changes inside the
|
|
9
|
+
* window only extend the count, and when the window closes exactly one note goes out describing
|
|
10
|
+
* the difference between what the model was last told and the state now.
|
|
11
|
+
*
|
|
12
|
+
* Deliberately a small standalone class with injected clock/timers/state access, so the debounce
|
|
13
|
+
* and delta behavior are unit-testable with fake timers and no channel, session or DOM.
|
|
14
|
+
*
|
|
15
|
+
* @module @memberjunction/realtime-runtime
|
|
16
|
+
*/
|
|
17
|
+
import { ComputeStateDelta, FormatChannelNote, ListChangedPaths } from './channel-state-delta.js';
|
|
18
|
+
/** The defaults. */
|
|
19
|
+
export const DEFAULT_CHANNEL_PERCEPTION_OPTIONS = {
|
|
20
|
+
DebounceMs: 750,
|
|
21
|
+
MaxNoteChars: 4000,
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Coalesces state changes into debounced delta notes. One instance per channel instance.
|
|
25
|
+
*/
|
|
26
|
+
export class ChannelPerceptionCoalescer {
|
|
27
|
+
constructor(host, options = DEFAULT_CHANNEL_PERCEPTION_OPTIONS) {
|
|
28
|
+
this.host = host;
|
|
29
|
+
this.options = options;
|
|
30
|
+
this.timer = null;
|
|
31
|
+
/** What the model was last told, or `null` before the first note (the first note is a snapshot). */
|
|
32
|
+
this.baseline = null;
|
|
33
|
+
this.pendingChanges = 0;
|
|
34
|
+
this.pendingChangeId = 0;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Records that the state changed. Arms the debounce timer if it is not already running; the
|
|
38
|
+
* timer is NOT re-armed by further changes, so a continuous stream of edits still produces a
|
|
39
|
+
* note every {@link ChannelPerceptionOptions.DebounceMs} rather than starving until it stops.
|
|
40
|
+
*
|
|
41
|
+
* @param changeId The id the change was assigned; the note carries the latest one.
|
|
42
|
+
*/
|
|
43
|
+
Record(changeId) {
|
|
44
|
+
this.pendingChanges++;
|
|
45
|
+
this.pendingChangeId = Math.max(this.pendingChangeId, changeId);
|
|
46
|
+
if (this.timer === null) {
|
|
47
|
+
this.timer = setTimeout(() => this.Flush(), this.options.DebounceMs);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Sets what the model is known to have been told, without sending anything — used right after a
|
|
52
|
+
* snapshot note went out by another route (e.g. when a channel opens), so the next change is
|
|
53
|
+
* described as a delta against it.
|
|
54
|
+
*/
|
|
55
|
+
SetBaseline(state) {
|
|
56
|
+
this.baseline = state;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Forgets what the model was told, so the NEXT note is a full snapshot. Used when the model starts
|
|
60
|
+
* seeing the channel again after exposure was withdrawn: whatever it was last told is stale.
|
|
61
|
+
*/
|
|
62
|
+
ResetBaseline() {
|
|
63
|
+
this.baseline = null;
|
|
64
|
+
}
|
|
65
|
+
/** Drops any pending note without sending it (exposure was withdrawn: the model is no longer to be told). */
|
|
66
|
+
CancelPending() {
|
|
67
|
+
if (this.timer !== null) {
|
|
68
|
+
clearTimeout(this.timer);
|
|
69
|
+
this.timer = null;
|
|
70
|
+
}
|
|
71
|
+
this.pendingChanges = 0;
|
|
72
|
+
}
|
|
73
|
+
/** Sends the pending note now, if anything is pending. Safe to call at any time. */
|
|
74
|
+
Flush() {
|
|
75
|
+
if (this.timer !== null) {
|
|
76
|
+
clearTimeout(this.timer);
|
|
77
|
+
this.timer = null;
|
|
78
|
+
}
|
|
79
|
+
if (this.pendingChanges === 0) {
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
const changes = this.pendingChanges;
|
|
83
|
+
const changeId = this.pendingChangeId;
|
|
84
|
+
this.pendingChanges = 0;
|
|
85
|
+
try {
|
|
86
|
+
this.sendNote(this.host.GetState(), changes, changeId);
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
this.host.OnError(error);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/** Cancels any pending note. Call at teardown. */
|
|
93
|
+
Dispose() {
|
|
94
|
+
if (this.timer !== null) {
|
|
95
|
+
clearTimeout(this.timer);
|
|
96
|
+
this.timer = null;
|
|
97
|
+
}
|
|
98
|
+
this.pendingChanges = 0;
|
|
99
|
+
}
|
|
100
|
+
/** Builds and sends the note for the current burst: a snapshot the first time, a delta after. */
|
|
101
|
+
sendNote(state, changes, changeId) {
|
|
102
|
+
const previous = this.baseline;
|
|
103
|
+
this.baseline = state;
|
|
104
|
+
if (previous === null) {
|
|
105
|
+
this.deliver({ changeId, changes, snapshot: state }, () => ({ changeId, changes, truncated: true, keys: Object.keys(state) }));
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
const delta = ComputeStateDelta(previous, state);
|
|
109
|
+
if (delta === null) {
|
|
110
|
+
return; // the burst netted out to no change — nothing to tell the model
|
|
111
|
+
}
|
|
112
|
+
this.deliver({ changeId, changes, delta: { changed: delta.Changed, removed: delta.Removed } }, () => ({ changeId, changes, truncated: true, changedPaths: ListChangedPaths(delta) }));
|
|
113
|
+
}
|
|
114
|
+
/** Sends `full` unless it is too large, in which case the compact fallback goes instead. */
|
|
115
|
+
deliver(full, compact) {
|
|
116
|
+
const body = JSON.stringify(full).length <= this.options.MaxNoteChars ? full : compact();
|
|
117
|
+
this.host.SendNote(FormatChannelNote(this.host.ChannelKey, this.host.InstanceId, 'state_changed', body));
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
//# sourceMappingURL=channel-perception.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"channel-perception.js","sourceRoot":"","sources":["../../src/channels/channel-perception.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Renders a JSON Schema's top-level parameters as a compact, model-readable list —
|
|
3
|
+
* `row:integer, col:integer, note?:string` — for error messages and the channel catalog note.
|
|
4
|
+
*
|
|
5
|
+
* Intentionally shallow: a speaking model needs the names, rough types and which are optional to
|
|
6
|
+
* form a call, not a schema dump. Nested structure collapses to its type (`object`, `array`).
|
|
7
|
+
*
|
|
8
|
+
* @module @memberjunction/realtime-runtime
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Lists a schema's top-level properties as `name:type`, with `?` after the name of optional ones.
|
|
12
|
+
* An empty or property-less schema renders as an empty string.
|
|
13
|
+
*
|
|
14
|
+
* @param schema A JSON Schema object.
|
|
15
|
+
*/
|
|
16
|
+
export declare function FormatParameterList(schema: unknown): string;
|
|
17
|
+
//# sourceMappingURL=channel-schema-format.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"channel-schema-format.d.ts","sourceRoot":"","sources":["../../src/channels/channel-schema-format.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAwBH;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,CAQ3D"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Renders a JSON Schema's top-level parameters as a compact, model-readable list —
|
|
3
|
+
* `row:integer, col:integer, note?:string` — for error messages and the channel catalog note.
|
|
4
|
+
*
|
|
5
|
+
* Intentionally shallow: a speaking model needs the names, rough types and which are optional to
|
|
6
|
+
* form a call, not a schema dump. Nested structure collapses to its type (`object`, `array`).
|
|
7
|
+
*
|
|
8
|
+
* @module @memberjunction/realtime-runtime
|
|
9
|
+
*/
|
|
10
|
+
import { IsPlainObject } from '@memberjunction/global';
|
|
11
|
+
/** Renders one property's type: its `enum` as `a|b|c`, else its `type` (a union joined with `|`), else `any`. */
|
|
12
|
+
function describePropertyType(schema) {
|
|
13
|
+
if (!IsPlainObject(schema)) {
|
|
14
|
+
return 'any';
|
|
15
|
+
}
|
|
16
|
+
const options = schema['enum'];
|
|
17
|
+
if (Array.isArray(options) && options.length > 0) {
|
|
18
|
+
return options.map((o) => String(o)).join('|');
|
|
19
|
+
}
|
|
20
|
+
const type = schema['type'];
|
|
21
|
+
if (typeof type === 'string') {
|
|
22
|
+
return type;
|
|
23
|
+
}
|
|
24
|
+
if (Array.isArray(type)) {
|
|
25
|
+
const names = type.filter((t) => typeof t === 'string');
|
|
26
|
+
return names.length > 0 ? names.join('|') : 'any';
|
|
27
|
+
}
|
|
28
|
+
return 'any';
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Lists a schema's top-level properties as `name:type`, with `?` after the name of optional ones.
|
|
32
|
+
* An empty or property-less schema renders as an empty string.
|
|
33
|
+
*
|
|
34
|
+
* @param schema A JSON Schema object.
|
|
35
|
+
*/
|
|
36
|
+
export function FormatParameterList(schema) {
|
|
37
|
+
if (!IsPlainObject(schema) || !IsPlainObject(schema['properties'])) {
|
|
38
|
+
return '';
|
|
39
|
+
}
|
|
40
|
+
const required = new Set(Array.isArray(schema['required']) ? schema['required'].filter((r) => typeof r === 'string') : []);
|
|
41
|
+
return Object.entries(schema['properties'])
|
|
42
|
+
.map(([name, property]) => `${name}${required.has(name) ? '' : '?'}:${describePropertyType(property)}`)
|
|
43
|
+
.join(', ');
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=channel-schema-format.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"channel-schema-format.js","sourceRoot":"","sources":["../../src/channels/channel-schema-format.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAEvD,iHAAiH;AACjH,SAAS,oBAAoB,CAAC,MAAe;IAC3C,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,EAAE,CAAC;QAC3B,OAAO,KAAK,CAAC;IACf,CAAC;IACD,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;IAC/B,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACjD,OAAO,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACjD,CAAC;IACD,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;IAC5B,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC7B,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;QACxB,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAe,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC;QACrE,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;IACpD,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,mBAAmB,CAAC,MAAe;IACjD,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,EAAE,CAAC;QACnE,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAe,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IACxI,OAAO,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;SACxC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,QAAQ,CAAC,EAAE,EAAE,CAAC,GAAG,IAAI,GAAG,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,IAAI,oBAAoB,CAAC,QAAQ,CAAC,EAAE,CAAC;SACtG,IAAI,CAAC,IAAI,CAAC,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Pure helpers for **structured perception notes** — the compact, typed context notes
|
|
3
|
+
* a channel sends the model when its state changes.
|
|
4
|
+
*
|
|
5
|
+
* Perception used to be free-form text: each channel invented its own wording and its own idea of
|
|
6
|
+
* when to speak, so a burst of edits became a burst of sentences the model had to read and ignore.
|
|
7
|
+
* The contract now is:
|
|
8
|
+
*
|
|
9
|
+
* ```
|
|
10
|
+
* [channel:<Key>#<instance>] <event> <json>
|
|
11
|
+
* ```
|
|
12
|
+
*
|
|
13
|
+
* with the JSON carrying **what changed** (a delta against what the model was last told), not the
|
|
14
|
+
* whole state. These helpers are the arithmetic behind that: a structural delta between two state
|
|
15
|
+
* snapshots, and the one place the note is formatted.
|
|
16
|
+
*
|
|
17
|
+
* @module @memberjunction/realtime-runtime
|
|
18
|
+
*/
|
|
19
|
+
import type { JSONObject } from '@memberjunction/ai';
|
|
20
|
+
/**
|
|
21
|
+
* The structural difference between two state snapshots.
|
|
22
|
+
*
|
|
23
|
+
* `Changed` is a nested *partial* of the new state — overlay it on the old state and you have the
|
|
24
|
+
* new one (arrays and scalars are replaced whole, never patched element-wise: a patch language the
|
|
25
|
+
* model would have to execute in its head is worse than re-reading a short array). `Removed` lists
|
|
26
|
+
* the dotted paths that existed before and do not now.
|
|
27
|
+
*/
|
|
28
|
+
export interface StateDelta {
|
|
29
|
+
/** The changed and added values, as a nested partial of the new state. */
|
|
30
|
+
Changed: JSONObject;
|
|
31
|
+
/** Dotted paths that were present before and are gone. */
|
|
32
|
+
Removed: string[];
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Computes what changed between two state snapshots.
|
|
36
|
+
*
|
|
37
|
+
* @param previous The state the model was last told about.
|
|
38
|
+
* @param next The state now.
|
|
39
|
+
* @returns The delta, or `null` when nothing changed (so a burst that nets out to no change sends nothing).
|
|
40
|
+
*/
|
|
41
|
+
export declare function ComputeStateDelta(previous: JSONObject, next: JSONObject): StateDelta | null;
|
|
42
|
+
/**
|
|
43
|
+
* Lists the dotted paths a delta touched, to a bounded depth — what a note falls back to when the
|
|
44
|
+
* full delta would be too large to send. Paths to a changed *subtree* stop at `maxDepth`.
|
|
45
|
+
*
|
|
46
|
+
* @param delta The delta to summarize.
|
|
47
|
+
* @param maxDepth How many path segments to keep (default 2).
|
|
48
|
+
*/
|
|
49
|
+
export declare function ListChangedPaths(delta: StateDelta, maxDepth?: number): string[];
|
|
50
|
+
/**
|
|
51
|
+
* Formats the one-line structured note: `[channel:<Key>#<instance>] <event> <json>`.
|
|
52
|
+
*
|
|
53
|
+
* @param channelKey The channel's key.
|
|
54
|
+
* @param instanceId The instance's id.
|
|
55
|
+
* @param event The event name (`state_changed`, `opened`, `completed`, …).
|
|
56
|
+
* @param payload The JSON payload (omit for an event with none).
|
|
57
|
+
*/
|
|
58
|
+
export declare function FormatChannelNote(channelKey: string, instanceId: string, event: string, payload?: JSONObject): string;
|
|
59
|
+
//# sourceMappingURL=channel-state-delta.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"channel-state-delta.d.ts","sourceRoot":"","sources":["../../src/channels/channel-state-delta.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAa,MAAM,oBAAoB,CAAC;AAGhE;;;;;;;GAOG;AACH,MAAM,WAAW,UAAU;IACvB,0EAA0E;IAC1E,OAAO,EAAE,UAAU,CAAC;IACpB,0DAA0D;IAC1D,OAAO,EAAE,MAAM,EAAE,CAAC;CACrB;AAiBD;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,GAAG,UAAU,GAAG,IAAI,CAG3F;AAkCD;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,UAAU,EAAE,QAAQ,GAAE,MAAU,GAAG,MAAM,EAAE,CAiBlF;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,UAAU,GAAG,MAAM,CAGrH"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Pure helpers for **structured perception notes** — the compact, typed context notes
|
|
3
|
+
* a channel sends the model when its state changes.
|
|
4
|
+
*
|
|
5
|
+
* Perception used to be free-form text: each channel invented its own wording and its own idea of
|
|
6
|
+
* when to speak, so a burst of edits became a burst of sentences the model had to read and ignore.
|
|
7
|
+
* The contract now is:
|
|
8
|
+
*
|
|
9
|
+
* ```
|
|
10
|
+
* [channel:<Key>#<instance>] <event> <json>
|
|
11
|
+
* ```
|
|
12
|
+
*
|
|
13
|
+
* with the JSON carrying **what changed** (a delta against what the model was last told), not the
|
|
14
|
+
* whole state. These helpers are the arithmetic behind that: a structural delta between two state
|
|
15
|
+
* snapshots, and the one place the note is formatted.
|
|
16
|
+
*
|
|
17
|
+
* @module @memberjunction/realtime-runtime
|
|
18
|
+
*/
|
|
19
|
+
import { IsPlainObject } from '@memberjunction/global';
|
|
20
|
+
/** Deep equality for JSON values. */
|
|
21
|
+
function jsonEqual(a, b) {
|
|
22
|
+
if (a === b) {
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
if (Array.isArray(a) && Array.isArray(b)) {
|
|
26
|
+
return a.length === b.length && a.every((item, index) => jsonEqual(item, b[index]));
|
|
27
|
+
}
|
|
28
|
+
if (IsPlainObject(a) && IsPlainObject(b)) {
|
|
29
|
+
const aKeys = Object.keys(a);
|
|
30
|
+
return aKeys.length === Object.keys(b).length && aKeys.every((k) => k in b && jsonEqual(a[k], b[k]));
|
|
31
|
+
}
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Computes what changed between two state snapshots.
|
|
36
|
+
*
|
|
37
|
+
* @param previous The state the model was last told about.
|
|
38
|
+
* @param next The state now.
|
|
39
|
+
* @returns The delta, or `null` when nothing changed (so a burst that nets out to no change sends nothing).
|
|
40
|
+
*/
|
|
41
|
+
export function ComputeStateDelta(previous, next) {
|
|
42
|
+
const delta = diffObjects(previous, next, '');
|
|
43
|
+
return Object.keys(delta.Changed).length === 0 && delta.Removed.length === 0 ? null : delta;
|
|
44
|
+
}
|
|
45
|
+
/** Recursive worker for {@link ComputeStateDelta}. */
|
|
46
|
+
function diffObjects(previous, next, prefix) {
|
|
47
|
+
const changed = {};
|
|
48
|
+
const removed = [];
|
|
49
|
+
for (const key of Object.keys(next)) {
|
|
50
|
+
// Channel state is caller-supplied JSON; a key like `__proto__` must never be copied through.
|
|
51
|
+
if (key === '__proto__' || key === 'constructor' || key === 'prototype') {
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
const path = prefix ? `${prefix}.${key}` : key;
|
|
55
|
+
const before = previous[key];
|
|
56
|
+
const after = next[key];
|
|
57
|
+
if (!(key in previous)) {
|
|
58
|
+
changed[key] = after;
|
|
59
|
+
}
|
|
60
|
+
else if (IsPlainObject(before) && IsPlainObject(after)) {
|
|
61
|
+
const child = diffObjects(before, after, path);
|
|
62
|
+
if (Object.keys(child.Changed).length > 0) {
|
|
63
|
+
changed[key] = child.Changed;
|
|
64
|
+
}
|
|
65
|
+
removed.push(...child.Removed);
|
|
66
|
+
}
|
|
67
|
+
else if (!jsonEqual(before, after)) {
|
|
68
|
+
changed[key] = after;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
for (const key of Object.keys(previous)) {
|
|
72
|
+
if (!(key in next)) {
|
|
73
|
+
removed.push(prefix ? `${prefix}.${key}` : key);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return { Changed: changed, Removed: removed };
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Lists the dotted paths a delta touched, to a bounded depth — what a note falls back to when the
|
|
80
|
+
* full delta would be too large to send. Paths to a changed *subtree* stop at `maxDepth`.
|
|
81
|
+
*
|
|
82
|
+
* @param delta The delta to summarize.
|
|
83
|
+
* @param maxDepth How many path segments to keep (default 2).
|
|
84
|
+
*/
|
|
85
|
+
export function ListChangedPaths(delta, maxDepth = 2) {
|
|
86
|
+
const paths = new Set();
|
|
87
|
+
const walk = (node, prefix) => {
|
|
88
|
+
for (const [key, value] of Object.entries(node)) {
|
|
89
|
+
const here = [...prefix, key];
|
|
90
|
+
if (IsPlainObject(value) && here.length < maxDepth) {
|
|
91
|
+
walk(value, here);
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
paths.add(here.join('.'));
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
walk(delta.Changed, []);
|
|
99
|
+
for (const removed of delta.Removed) {
|
|
100
|
+
paths.add(removed.split('.').slice(0, maxDepth).join('.'));
|
|
101
|
+
}
|
|
102
|
+
return [...paths];
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Formats the one-line structured note: `[channel:<Key>#<instance>] <event> <json>`.
|
|
106
|
+
*
|
|
107
|
+
* @param channelKey The channel's key.
|
|
108
|
+
* @param instanceId The instance's id.
|
|
109
|
+
* @param event The event name (`state_changed`, `opened`, `completed`, …).
|
|
110
|
+
* @param payload The JSON payload (omit for an event with none).
|
|
111
|
+
*/
|
|
112
|
+
export function FormatChannelNote(channelKey, instanceId, event, payload) {
|
|
113
|
+
const head = `[channel:${channelKey}#${instanceId}] ${event}`;
|
|
114
|
+
return payload === undefined ? head : `${head} ${JSON.stringify(payload)}`;
|
|
115
|
+
}
|
|
116
|
+
//# sourceMappingURL=channel-state-delta.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"channel-state-delta.js","sourceRoot":"","sources":["../../src/channels/channel-state-delta.ts"],"names":[],"mappings":"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"}
|