ocuclaw 1.2.4 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -5
- package/dist/config/runtime-config.js +81 -3
- package/dist/domain/activity-status-adapter.js +138 -605
- package/dist/domain/activity-status-arbiter.js +109 -0
- package/dist/domain/activity-status-labels.js +906 -0
- package/dist/domain/code-span-regions.js +103 -0
- package/dist/domain/conversation-state.js +14 -1
- package/dist/domain/debug-store.js +38 -182
- package/dist/domain/glasses-ui-content-summary.js +62 -0
- package/dist/domain/glasses-ui-system-prompt.js +28 -0
- package/dist/domain/message-emoji-allowlist.js +16 -0
- package/dist/domain/message-emoji-filter.js +33 -55
- package/dist/domain/neural-emoji-reactor-system-prompt.js +43 -0
- package/dist/domain/neural-emoji-reactor-tag-config.js +56 -0
- package/dist/domain/neural-pace-modulator-system-prompt.js +32 -0
- package/dist/domain/neural-pace-modulator-tag-config.js +51 -0
- package/dist/domain/tagged-span-parser.js +121 -0
- package/dist/domain/tagged-span-strip.js +38 -0
- package/dist/even-ai/even-ai-endpoint.js +91 -0
- package/dist/even-ai/even-ai-run-waiter.js +14 -0
- package/dist/even-ai/even-ai-settings-store.js +14 -0
- package/dist/gateway/gateway-bridge.js +14 -2
- package/dist/gateway/gateway-timing-ledger.js +457 -0
- package/dist/gateway/openclaw-client.js +462 -38
- package/dist/index.js +28 -1
- package/dist/runtime/downstream-handler.js +754 -83
- package/dist/runtime/downstream-server.js +700 -534
- package/dist/runtime/ocuclaw-settings-store.js +74 -31
- package/dist/runtime/plugin-update-service.js +216 -0
- package/dist/runtime/protocol-adapter.js +9 -0
- package/dist/runtime/provider-usage-select.js +168 -0
- package/dist/runtime/relay-client-nudge-controller.js +553 -0
- package/dist/runtime/relay-core.js +1209 -204
- package/dist/runtime/relay-health-monitor.js +172 -0
- package/dist/runtime/relay-operation-registry.js +263 -0
- package/dist/runtime/relay-service.js +201 -1
- package/dist/runtime/relay-worker-approval-replay-cache.js +68 -0
- package/dist/runtime/relay-worker-entry.js +32 -0
- package/dist/runtime/relay-worker-health.js +272 -0
- package/dist/runtime/relay-worker-protocol.js +285 -0
- package/dist/runtime/relay-worker-queue.js +202 -0
- package/dist/runtime/relay-worker-supervisor.js +1081 -0
- package/dist/runtime/relay-worker-transport.js +1051 -0
- package/dist/runtime/session-context-service.js +189 -0
- package/dist/runtime/session-service.js +615 -24
- package/dist/runtime/upstream-runtime.js +1167 -60
- package/dist/tools/device-info-tool.js +242 -0
- package/dist/tools/glasses-ui-cron.js +427 -0
- package/dist/tools/glasses-ui-descriptors.js +261 -0
- package/dist/tools/glasses-ui-limits.js +21 -0
- package/dist/tools/glasses-ui-paint-floor.js +99 -0
- package/dist/tools/glasses-ui-recipes.js +746 -0
- package/dist/tools/glasses-ui-surfaces.js +278 -0
- package/dist/tools/glasses-ui-template.js +182 -0
- package/dist/tools/glasses-ui-tool.js +1147 -0
- package/dist/tools/session-title-tool.js +209 -0
- package/dist/version.js +2 -0
- package/openclaw.plugin.json +163 -15
- package/package.json +12 -4
|
@@ -4,6 +4,10 @@ import {
|
|
|
4
4
|
normalizeOcuClawDefaultThinking,
|
|
5
5
|
normalizeOcuClawSystemPrompt,
|
|
6
6
|
} from "./ocuclaw-settings-store.js";
|
|
7
|
+
import {
|
|
8
|
+
formatMainOperationReceived,
|
|
9
|
+
formatSendAck,
|
|
10
|
+
} from "./relay-worker-protocol.js";
|
|
7
11
|
|
|
8
12
|
// --- Factory ---
|
|
9
13
|
|
|
@@ -27,7 +31,7 @@ function normalizeLogger(logger) {
|
|
|
27
31
|
* downstream clients (Even App, commander.html). Consumed by relay.js.
|
|
28
32
|
*
|
|
29
33
|
* @param {object} opts
|
|
30
|
-
* @param {(id: string, text: string, sessionKey: string|null, attachment: object|null) => Promise} opts.onSend
|
|
34
|
+
* @param {(id: string, text: string, sessionKey: string|null, attachment: object|null, clientDisplaySignals: object|null) => Promise} opts.onSend
|
|
31
35
|
* Forward a user message to the upstream OpenClaw agent.
|
|
32
36
|
* @param {(sender: string, text: string) => Array} opts.onSimulate
|
|
33
37
|
* Inject a fake message into conversation state; returns pages array.
|
|
@@ -41,10 +45,14 @@ function normalizeLogger(logger) {
|
|
|
41
45
|
* Return cached skills catalog snapshot.
|
|
42
46
|
* @param {() => Promise<{models: Array, fetchedAtMs: number, stale: boolean}>} [opts.onGetSonioxModels]
|
|
43
47
|
* Return cached Soniox model snapshot.
|
|
48
|
+
* @param {() => Promise<object>} [opts.onGetProviderUsageSnapshot]
|
|
49
|
+
* Return the current provider usage snapshot for the active provider.
|
|
44
50
|
* @param {() => Promise<object>} [opts.onGetSessionModelConfig]
|
|
45
51
|
* Return current session model controls.
|
|
46
52
|
* @param {(patch: object) => Promise<{status: string, error?: string}>} [opts.onSetSessionModelConfig]
|
|
47
53
|
* Patch current session model controls.
|
|
54
|
+
* @param {({sessionKey: string}) => Promise<{status: string, error?: string}>} [opts.onCompactSession]
|
|
55
|
+
* Trigger gateway-side compaction for a session key.
|
|
48
56
|
* @param {() => Promise<object>} [opts.onGetEvenAiSettings]
|
|
49
57
|
* Return current relay-owned Even AI settings.
|
|
50
58
|
* @param {() => Promise<{sessions: Array, dedicatedKey: string}>} [opts.onGetEvenAiSessions]
|
|
@@ -81,8 +89,10 @@ function createDownstreamHandler(opts) {
|
|
|
81
89
|
const onGetModelsCatalog = opts.onGetModelsCatalog;
|
|
82
90
|
const onGetSkillsCatalog = opts.onGetSkillsCatalog;
|
|
83
91
|
const onGetSonioxModels = opts.onGetSonioxModels || null;
|
|
92
|
+
const onGetProviderUsageSnapshot = opts.onGetProviderUsageSnapshot || null;
|
|
84
93
|
const onGetSessionModelConfig = opts.onGetSessionModelConfig;
|
|
85
94
|
const onSetSessionModelConfig = opts.onSetSessionModelConfig;
|
|
95
|
+
const onCompactSession = opts.onCompactSession || null;
|
|
86
96
|
const onGetEvenAiSettings = opts.onGetEvenAiSettings;
|
|
87
97
|
const onGetEvenAiSessions = opts.onGetEvenAiSessions;
|
|
88
98
|
const onSetEvenAiSettings = opts.onSetEvenAiSettings;
|
|
@@ -96,9 +106,21 @@ function createDownstreamHandler(opts) {
|
|
|
96
106
|
const onDebugSet = opts.onDebugSet || null;
|
|
97
107
|
const onDebugDump = opts.onDebugDump || null;
|
|
98
108
|
const onEventDebug = opts.onEventDebug || null;
|
|
109
|
+
const onTraceLogSet = opts.onTraceLogSet || null;
|
|
110
|
+
const onTraceLogGet = opts.onTraceLogGet || null;
|
|
99
111
|
const onRemoteControl = opts.onRemoteControl || null;
|
|
112
|
+
const onAutomationState = opts.onAutomationState || null;
|
|
100
113
|
const onReadinessProbe = opts.onReadinessProbe || null;
|
|
114
|
+
const onGlassesUiResult = opts.onGlassesUiResult || null;
|
|
115
|
+
const onGlassesUiRenderInject = opts.onGlassesUiRenderInject || null;
|
|
116
|
+
const onGlassesUiNavEvent = opts.onGlassesUiNavEvent || null;
|
|
117
|
+
const onDeviceInfoResponse = opts.onDeviceInfoResponse || null;
|
|
118
|
+
const onSetUserSessionTitle = opts.onSetUserSessionTitle || null;
|
|
119
|
+
const onSetSessionPinned = opts.onSetSessionPinned || null;
|
|
120
|
+
const onDeleteSessions = opts.onDeleteSessions || null;
|
|
121
|
+
const onSearchTranscripts = opts.onSearchTranscripts || null;
|
|
101
122
|
const getSnapshotRevision = opts.getSnapshotRevision || null;
|
|
123
|
+
const operationRegistry = opts.operationRegistry || null;
|
|
102
124
|
|
|
103
125
|
/** Client IDs subscribed to raw protocol frame forwarding. */
|
|
104
126
|
const protocolSubscribers = new Set();
|
|
@@ -115,6 +137,8 @@ function createDownstreamHandler(opts) {
|
|
|
115
137
|
const approvalResolveCache = new Map();
|
|
116
138
|
const APP_PROTOCOL = {
|
|
117
139
|
activity: "ocuclaw.activity.update",
|
|
140
|
+
automationStateGet: "ocuclaw.automation.state.get",
|
|
141
|
+
automationStateSnapshot: "ocuclaw.automation.state.snapshot",
|
|
118
142
|
approvalRequest: "ocuclaw.approval.request",
|
|
119
143
|
approvalResolve: "ocuclaw.approval.resolve",
|
|
120
144
|
approvalResolveAck: "ocuclaw.approval.resolve.ack",
|
|
@@ -137,6 +161,8 @@ function createDownstreamHandler(opts) {
|
|
|
137
161
|
messageStreamDelta: "ocuclaw.message.stream.delta",
|
|
138
162
|
modelCatalogGet: "ocuclaw.model.catalog.get",
|
|
139
163
|
modelCatalogSnapshot: "ocuclaw.model.catalog.snapshot",
|
|
164
|
+
providerUsageGet: "ocuclaw.provider.usage.get",
|
|
165
|
+
providerUsageSnapshot: "ocuclaw.provider.usage.snapshot",
|
|
140
166
|
skillsCatalogGet: "ocuclaw.skills.catalog.get",
|
|
141
167
|
skillsCatalogSnapshot: "ocuclaw.skills.catalog.snapshot",
|
|
142
168
|
pages: "ocuclaw.view.pages.snapshot",
|
|
@@ -152,16 +178,20 @@ function createDownstreamHandler(opts) {
|
|
|
152
178
|
sessionConfigSet: "ocuclaw.session.config.set",
|
|
153
179
|
sessionConfigSetAck: "ocuclaw.session.config.set.ack",
|
|
154
180
|
sessionConfigSnapshot: "ocuclaw.session.config.snapshot",
|
|
181
|
+
sessionCompact: "ocuclaw.session.compact",
|
|
182
|
+
sessionCompactAck: "ocuclaw.session.compact.ack",
|
|
155
183
|
sessionCreate: "ocuclaw.session.create",
|
|
156
184
|
sessionList: "ocuclaw.session.list",
|
|
157
185
|
sessionListResult: "ocuclaw.session.list.result",
|
|
158
186
|
sessionReset: "ocuclaw.session.reset",
|
|
159
187
|
sessionSwitch: "ocuclaw.session.switch",
|
|
160
188
|
sessionSwitchApplied: "ocuclaw.session.switch.applied",
|
|
189
|
+
sessionTitleSet: "ocuclaw.session.title.set",
|
|
161
190
|
sonioxTemporaryKey: "sonioxTemporaryKey",
|
|
162
191
|
sonioxTemporaryKeyError: "sonioxTemporaryKeyError",
|
|
163
192
|
status: "ocuclaw.runtime.status",
|
|
164
193
|
statusGet: "ocuclaw.runtime.status.get",
|
|
194
|
+
typingUpdate: "ocuclaw.typing.update",
|
|
165
195
|
};
|
|
166
196
|
|
|
167
197
|
// --- Format helpers ---
|
|
@@ -222,25 +252,52 @@ function createDownstreamHandler(opts) {
|
|
|
222
252
|
return JSON.stringify({ ...activity, type: APP_PROTOCOL.activity });
|
|
223
253
|
}
|
|
224
254
|
|
|
255
|
+
/**
|
|
256
|
+
* Format a typing update message for broadcast.
|
|
257
|
+
*
|
|
258
|
+
* @param {object} update - Typing fields (state, runId, sessionKey, etc.)
|
|
259
|
+
* @returns {string} JSON string
|
|
260
|
+
*/
|
|
261
|
+
function formatTyping(update) {
|
|
262
|
+
return JSON.stringify({ ...update, type: APP_PROTOCOL.typingUpdate });
|
|
263
|
+
}
|
|
264
|
+
|
|
225
265
|
/**
|
|
226
266
|
* Format an error message for unicast.
|
|
227
267
|
*
|
|
228
268
|
* @param {string} error
|
|
269
|
+
* @param {{code?: string, requestId?: string, op?: string}} [meta]
|
|
229
270
|
* @returns {string} JSON string
|
|
230
271
|
*/
|
|
231
|
-
function formatError(error) {
|
|
232
|
-
|
|
272
|
+
function formatError(error, meta) {
|
|
273
|
+
const msg = {
|
|
233
274
|
type: "error",
|
|
234
275
|
error: error || "Unknown error",
|
|
235
|
-
}
|
|
276
|
+
};
|
|
277
|
+
if (meta && typeof meta === "object" && !Array.isArray(meta)) {
|
|
278
|
+
if (typeof meta.code === "string" && meta.code.trim()) {
|
|
279
|
+
msg.code = meta.code.trim();
|
|
280
|
+
}
|
|
281
|
+
if (typeof meta.requestId === "string" && meta.requestId.trim()) {
|
|
282
|
+
msg.requestId = meta.requestId.trim();
|
|
283
|
+
}
|
|
284
|
+
if (typeof meta.op === "string" && meta.op.trim()) {
|
|
285
|
+
msg.op = meta.op.trim();
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
return JSON.stringify(msg);
|
|
236
289
|
}
|
|
237
290
|
|
|
238
291
|
function isExternalDebugToolMessageType(messageType) {
|
|
239
292
|
return (
|
|
240
293
|
messageType === "debug-set" ||
|
|
241
294
|
messageType === "debug-dump" ||
|
|
295
|
+
messageType === "trace-log-set" ||
|
|
296
|
+
messageType === "trace-log-get" ||
|
|
242
297
|
messageType === "remote-control" ||
|
|
243
|
-
messageType === APP_PROTOCOL.
|
|
298
|
+
messageType === APP_PROTOCOL.automationStateGet ||
|
|
299
|
+
messageType === APP_PROTOCOL.readinessProbeRequest ||
|
|
300
|
+
messageType === "glasses_ui_render"
|
|
244
301
|
);
|
|
245
302
|
}
|
|
246
303
|
|
|
@@ -253,15 +310,12 @@ function createDownstreamHandler(opts) {
|
|
|
253
310
|
* @param {string} [errorCode] - Structured error code (for deterministic client handling)
|
|
254
311
|
* @returns {string} JSON string
|
|
255
312
|
*/
|
|
256
|
-
function
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
msg.errorCode = errorCode;
|
|
263
|
-
}
|
|
264
|
-
return JSON.stringify(msg);
|
|
313
|
+
function formatSendAckCompat(id, status, error, errorCode) {
|
|
314
|
+
return formatSendAck(id, status, error, errorCode);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
function formatOperationReceived(data) {
|
|
318
|
+
return formatMainOperationReceived(data);
|
|
265
319
|
}
|
|
266
320
|
|
|
267
321
|
/**
|
|
@@ -283,10 +337,18 @@ function createDownstreamHandler(opts) {
|
|
|
283
337
|
* Format a streaming text message for broadcast during agent runs.
|
|
284
338
|
*
|
|
285
339
|
* @param {string} text - Streaming assistant text
|
|
340
|
+
* @param {Array<{start: number, end: number, emoji: string}>} [spans] - Optional emoji spans
|
|
286
341
|
* @returns {string} JSON string
|
|
287
342
|
*/
|
|
288
|
-
function formatStreaming(text) {
|
|
289
|
-
|
|
343
|
+
function formatStreaming(text, emojiSpans, paceSpans) {
|
|
344
|
+
const payload = { type: APP_PROTOCOL.messageStreamDelta, text };
|
|
345
|
+
if (Array.isArray(emojiSpans) && emojiSpans.length > 0) {
|
|
346
|
+
payload.emojiSpans = emojiSpans;
|
|
347
|
+
}
|
|
348
|
+
if (Array.isArray(paceSpans) && paceSpans.length > 0) {
|
|
349
|
+
payload.paceSpans = paceSpans;
|
|
350
|
+
}
|
|
351
|
+
return JSON.stringify(payload);
|
|
290
352
|
}
|
|
291
353
|
|
|
292
354
|
/**
|
|
@@ -366,6 +428,75 @@ function createDownstreamHandler(opts) {
|
|
|
366
428
|
});
|
|
367
429
|
}
|
|
368
430
|
|
|
431
|
+
/**
|
|
432
|
+
* Format a provider usage snapshot for unicast.
|
|
433
|
+
*
|
|
434
|
+
* @param {object} payload
|
|
435
|
+
* @returns {string}
|
|
436
|
+
*/
|
|
437
|
+
function formatProviderUsageSnapshot(payload) {
|
|
438
|
+
const provider =
|
|
439
|
+
payload && typeof payload.provider === "string" && payload.provider.trim()
|
|
440
|
+
? payload.provider.trim()
|
|
441
|
+
: null;
|
|
442
|
+
const windows = Array.isArray(payload && payload.windows)
|
|
443
|
+
? payload.windows.map((window) => ({
|
|
444
|
+
key:
|
|
445
|
+
window && typeof window.key === "string" && window.key.trim()
|
|
446
|
+
? window.key.trim()
|
|
447
|
+
: null,
|
|
448
|
+
label:
|
|
449
|
+
window && typeof window.label === "string" && window.label.trim()
|
|
450
|
+
? window.label.trim()
|
|
451
|
+
: null,
|
|
452
|
+
usedPercent:
|
|
453
|
+
Number.isFinite(window && window.usedPercent)
|
|
454
|
+
? window.usedPercent
|
|
455
|
+
: 0,
|
|
456
|
+
resetAtMs:
|
|
457
|
+
Number.isFinite(window && window.resetAtMs)
|
|
458
|
+
? Math.floor(window.resetAtMs)
|
|
459
|
+
: null,
|
|
460
|
+
sortOrder:
|
|
461
|
+
Number.isFinite(window && window.sortOrder)
|
|
462
|
+
? Math.floor(window.sortOrder)
|
|
463
|
+
: null,
|
|
464
|
+
}))
|
|
465
|
+
: [];
|
|
466
|
+
return JSON.stringify({
|
|
467
|
+
type: APP_PROTOCOL.providerUsageSnapshot,
|
|
468
|
+
sessionKey:
|
|
469
|
+
payload && typeof payload.sessionKey === "string" && payload.sessionKey.trim()
|
|
470
|
+
? payload.sessionKey.trim()
|
|
471
|
+
: null,
|
|
472
|
+
provider,
|
|
473
|
+
displayName:
|
|
474
|
+
payload && typeof payload.displayName === "string" && payload.displayName.trim()
|
|
475
|
+
? payload.displayName.trim()
|
|
476
|
+
: provider,
|
|
477
|
+
limitingWindowKey:
|
|
478
|
+
payload &&
|
|
479
|
+
typeof payload.limitingWindowKey === "string" &&
|
|
480
|
+
payload.limitingWindowKey.trim()
|
|
481
|
+
? payload.limitingWindowKey.trim()
|
|
482
|
+
: null,
|
|
483
|
+
windows,
|
|
484
|
+
fetchedAtMs:
|
|
485
|
+
Number.isFinite(payload && payload.fetchedAtMs)
|
|
486
|
+
? Math.floor(payload.fetchedAtMs)
|
|
487
|
+
: Date.now(),
|
|
488
|
+
stale: !!(payload && payload.stale),
|
|
489
|
+
poolStatus:
|
|
490
|
+
payload && (payload.poolStatus === "ready" || payload.poolStatus === "exhausted")
|
|
491
|
+
? payload.poolStatus
|
|
492
|
+
: "unknown",
|
|
493
|
+
totalProfileCount:
|
|
494
|
+
Number.isFinite(payload && payload.totalProfileCount) && payload.totalProfileCount >= 0
|
|
495
|
+
? Math.floor(payload.totalProfileCount)
|
|
496
|
+
: null,
|
|
497
|
+
});
|
|
498
|
+
}
|
|
499
|
+
|
|
369
500
|
/**
|
|
370
501
|
* Format current session model controls.
|
|
371
502
|
*
|
|
@@ -394,6 +525,11 @@ function createDownstreamHandler(opts) {
|
|
|
394
525
|
payload && typeof payload.verboseLevel === "string"
|
|
395
526
|
? payload.verboseLevel
|
|
396
527
|
: "off",
|
|
528
|
+
fastMode: !!(payload && payload.fastMode === true),
|
|
529
|
+
elevatedLevel:
|
|
530
|
+
payload && typeof payload.elevatedLevel === "string"
|
|
531
|
+
? payload.elevatedLevel
|
|
532
|
+
: "off",
|
|
397
533
|
});
|
|
398
534
|
}
|
|
399
535
|
|
|
@@ -417,6 +553,30 @@ function createDownstreamHandler(opts) {
|
|
|
417
553
|
return JSON.stringify(out);
|
|
418
554
|
}
|
|
419
555
|
|
|
556
|
+
/**
|
|
557
|
+
* Format a compactSession ack message for unicast.
|
|
558
|
+
*
|
|
559
|
+
* @param {{status: string, error?: string, requestId?: string}} payload
|
|
560
|
+
* @returns {string} JSON string
|
|
561
|
+
*/
|
|
562
|
+
function formatCompactSessionAck(payload) {
|
|
563
|
+
const msg = {
|
|
564
|
+
type: APP_PROTOCOL.sessionCompactAck,
|
|
565
|
+
status:
|
|
566
|
+
payload && payload.status === "accepted" ? "accepted" : "rejected",
|
|
567
|
+
};
|
|
568
|
+
if (payload && payload.requestId) {
|
|
569
|
+
msg.requestId = String(payload.requestId);
|
|
570
|
+
}
|
|
571
|
+
if (msg.status === "rejected") {
|
|
572
|
+
msg.error =
|
|
573
|
+
payload && payload.error
|
|
574
|
+
? String(payload.error)
|
|
575
|
+
: "compact failed";
|
|
576
|
+
}
|
|
577
|
+
return JSON.stringify(msg);
|
|
578
|
+
}
|
|
579
|
+
|
|
420
580
|
/**
|
|
421
581
|
* Format the current relay-owned Even AI settings snapshot.
|
|
422
582
|
*
|
|
@@ -444,6 +604,7 @@ function createDownstreamHandler(opts) {
|
|
|
444
604
|
? payload.defaultThinking
|
|
445
605
|
: "",
|
|
446
606
|
listenEnabled: payload && payload.listenEnabled === true,
|
|
607
|
+
defaultFastMode: !!(payload && payload.defaultFastMode === true),
|
|
447
608
|
});
|
|
448
609
|
}
|
|
449
610
|
|
|
@@ -491,6 +652,7 @@ function createDownstreamHandler(opts) {
|
|
|
491
652
|
? payload.defaultThinking
|
|
492
653
|
: undefined,
|
|
493
654
|
),
|
|
655
|
+
defaultFastMode: !!(payload && payload.defaultFastMode === true),
|
|
494
656
|
});
|
|
495
657
|
}
|
|
496
658
|
|
|
@@ -539,23 +701,52 @@ function createDownstreamHandler(opts) {
|
|
|
539
701
|
*/
|
|
540
702
|
function formatApproval(data) {
|
|
541
703
|
const request = data && data.request ? data.request : {};
|
|
704
|
+
const approvalKind =
|
|
705
|
+
(data && data.approvalKind === "plugin") ||
|
|
706
|
+
(data && typeof data.id === "string" && data.id.startsWith("plugin:")) ||
|
|
707
|
+
(typeof request.title === "string" && request.title.length > 0)
|
|
708
|
+
? "plugin"
|
|
709
|
+
: "exec";
|
|
710
|
+
const isPluginApproval = approvalKind === "plugin";
|
|
711
|
+
const commandText =
|
|
712
|
+
request.command ||
|
|
713
|
+
(request.host === "node" && request.systemRunPlan && typeof request.systemRunPlan.commandText === "string"
|
|
714
|
+
? request.systemRunPlan.commandText
|
|
715
|
+
: "") ||
|
|
716
|
+
(isPluginApproval && typeof request.title === "string" ? request.title : "") ||
|
|
717
|
+
"";
|
|
718
|
+
const pluginDescription =
|
|
719
|
+
isPluginApproval && typeof request.description === "string" && request.description.length > 0
|
|
720
|
+
? request.description
|
|
721
|
+
: null;
|
|
542
722
|
return JSON.stringify({
|
|
543
723
|
type: APP_PROTOCOL.approvalRequest,
|
|
544
724
|
id: data.id,
|
|
725
|
+
...(isPluginApproval ? { approvalKind: "plugin" } : {}),
|
|
545
726
|
requestId:
|
|
546
727
|
(data && typeof data.requestId === "string" && data.requestId) ||
|
|
547
728
|
(request && typeof request.requestId === "string" && request.requestId) ||
|
|
548
729
|
null,
|
|
549
|
-
command:
|
|
550
|
-
cwd: request.cwd || null,
|
|
730
|
+
command: commandText,
|
|
731
|
+
cwd: request.cwd || pluginDescription || null,
|
|
551
732
|
agentId: request.agentId || null,
|
|
552
|
-
host: request.host || null,
|
|
553
|
-
security: request.security || null,
|
|
733
|
+
host: request.host || (isPluginApproval ? "plugin" : null),
|
|
734
|
+
security: request.security || (isPluginApproval && typeof request.severity === "string" ? request.severity : null),
|
|
554
735
|
ask: request.ask || null,
|
|
555
736
|
resolvedPath: request.resolvedPath || null,
|
|
556
737
|
sessionKey: request.sessionKey || null,
|
|
738
|
+
...(isPluginApproval
|
|
739
|
+
? {
|
|
740
|
+
pluginId: typeof request.pluginId === "string" ? request.pluginId : null,
|
|
741
|
+
toolName: typeof request.toolName === "string" ? request.toolName : null,
|
|
742
|
+
description: pluginDescription,
|
|
743
|
+
}
|
|
744
|
+
: {}),
|
|
557
745
|
createdAtMs: data.createdAtMs || 0,
|
|
558
746
|
expiresAtMs: data.expiresAtMs || 0,
|
|
747
|
+
allowedDecisions: Array.isArray(request.allowedDecisions)
|
|
748
|
+
? request.allowedDecisions.filter((d) => typeof d === "string")
|
|
749
|
+
: null,
|
|
559
750
|
});
|
|
560
751
|
}
|
|
561
752
|
|
|
@@ -611,17 +802,6 @@ function createDownstreamHandler(opts) {
|
|
|
611
802
|
});
|
|
612
803
|
}
|
|
613
804
|
|
|
614
|
-
/**
|
|
615
|
-
* Format a transcription update for broadcast during voice mode.
|
|
616
|
-
*
|
|
617
|
-
* @param {string} text - Current transcription text
|
|
618
|
-
* @param {boolean} isFinal - Whether this is a final transcription
|
|
619
|
-
* @returns {string} JSON string
|
|
620
|
-
*/
|
|
621
|
-
function formatTranscription(text, isFinal) {
|
|
622
|
-
return JSON.stringify({ type: "transcription", text, final: isFinal });
|
|
623
|
-
}
|
|
624
|
-
|
|
625
805
|
/**
|
|
626
806
|
* Format a committed listen handoff update for broadcast during voice mode.
|
|
627
807
|
*
|
|
@@ -639,6 +819,13 @@ function createDownstreamHandler(opts) {
|
|
|
639
819
|
});
|
|
640
820
|
}
|
|
641
821
|
|
|
822
|
+
function formatEvenAiListenIntercepted(sessionKey) {
|
|
823
|
+
return JSON.stringify({
|
|
824
|
+
type: "even-ai-listen-intercepted",
|
|
825
|
+
sessionKey: sessionKey ?? null,
|
|
826
|
+
});
|
|
827
|
+
}
|
|
828
|
+
|
|
642
829
|
/**
|
|
643
830
|
* Format a listen-ended message for broadcast.
|
|
644
831
|
* @returns {string} JSON string
|
|
@@ -728,6 +915,10 @@ function createDownstreamHandler(opts) {
|
|
|
728
915
|
: "";
|
|
729
916
|
const lowered = message.toLowerCase();
|
|
730
917
|
if (!message) return "soniox_temp_key_request_failed";
|
|
918
|
+
// AbortError from the per-fetch timeout AbortController in mintSonioxTemporaryKey.
|
|
919
|
+
if (err && err.name === "AbortError") {
|
|
920
|
+
return "soniox_temp_key_mint_timeout";
|
|
921
|
+
}
|
|
731
922
|
if (lowered.includes("is not available")) {
|
|
732
923
|
return "soniox_temp_key_unavailable";
|
|
733
924
|
}
|
|
@@ -776,6 +967,27 @@ function createDownstreamHandler(opts) {
|
|
|
776
967
|
});
|
|
777
968
|
}
|
|
778
969
|
|
|
970
|
+
/**
|
|
971
|
+
* Parse a "trace-log-set" message.
|
|
972
|
+
* @returns {{enabled: boolean}}
|
|
973
|
+
*/
|
|
974
|
+
function parseTraceLogSet(msg) {
|
|
975
|
+
if (!msg || typeof msg !== "object") {
|
|
976
|
+
throw new Error("trace-log-set requires an object");
|
|
977
|
+
}
|
|
978
|
+
if (typeof msg.enabled !== "boolean") {
|
|
979
|
+
throw new Error("trace-log-set requires boolean 'enabled'");
|
|
980
|
+
}
|
|
981
|
+
return { enabled: msg.enabled };
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
/**
|
|
985
|
+
* Format a trace-log response for unicast (set + get share one type).
|
|
986
|
+
*/
|
|
987
|
+
function formatTraceLog(data) {
|
|
988
|
+
return JSON.stringify({ type: "trace-log", ...data });
|
|
989
|
+
}
|
|
990
|
+
|
|
779
991
|
/**
|
|
780
992
|
* Format the current relay debug-config snapshot for app/WebUI clients.
|
|
781
993
|
*
|
|
@@ -833,6 +1045,41 @@ function createDownstreamHandler(opts) {
|
|
|
833
1045
|
});
|
|
834
1046
|
}
|
|
835
1047
|
|
|
1048
|
+
function formatAutomationStateRequest(data) {
|
|
1049
|
+
return JSON.stringify({
|
|
1050
|
+
type: APP_PROTOCOL.automationStateGet,
|
|
1051
|
+
requestId:
|
|
1052
|
+
data && typeof data.requestId === "string" && data.requestId
|
|
1053
|
+
? data.requestId
|
|
1054
|
+
: null,
|
|
1055
|
+
sessionKey:
|
|
1056
|
+
data && typeof data.sessionKey === "string" && data.sessionKey
|
|
1057
|
+
? data.sessionKey
|
|
1058
|
+
: null,
|
|
1059
|
+
});
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
function formatAutomationStateSnapshot(data) {
|
|
1063
|
+
return JSON.stringify({
|
|
1064
|
+
type: APP_PROTOCOL.automationStateSnapshot,
|
|
1065
|
+
ok: data && data.ok !== false,
|
|
1066
|
+
requestId:
|
|
1067
|
+
data && typeof data.requestId === "string" && data.requestId
|
|
1068
|
+
? data.requestId
|
|
1069
|
+
: null,
|
|
1070
|
+
state:
|
|
1071
|
+
data && data.state && typeof data.state === "object" ? data.state : null,
|
|
1072
|
+
reasonCode:
|
|
1073
|
+
data && typeof data.reasonCode === "string" && data.reasonCode
|
|
1074
|
+
? data.reasonCode
|
|
1075
|
+
: null,
|
|
1076
|
+
message:
|
|
1077
|
+
data && typeof data.message === "string" && data.message
|
|
1078
|
+
? data.message
|
|
1079
|
+
: null,
|
|
1080
|
+
});
|
|
1081
|
+
}
|
|
1082
|
+
|
|
836
1083
|
function formatReadinessProbeRequest(data) {
|
|
837
1084
|
return JSON.stringify({
|
|
838
1085
|
type: APP_PROTOCOL.readinessProbeRequest,
|
|
@@ -963,25 +1210,12 @@ function createDownstreamHandler(opts) {
|
|
|
963
1210
|
) {
|
|
964
1211
|
throw new Error("debug-dump untilMs must be a non-negative number");
|
|
965
1212
|
}
|
|
966
|
-
if (msg.redaction !== undefined) {
|
|
967
|
-
if (typeof msg.redaction !== "string") {
|
|
968
|
-
throw new Error("debug-dump redaction must be one of: safe, full");
|
|
969
|
-
}
|
|
970
|
-
const normalized = msg.redaction.trim().toLowerCase();
|
|
971
|
-
if (normalized !== "safe" && normalized !== "full") {
|
|
972
|
-
throw new Error("debug-dump redaction must be one of: safe, full");
|
|
973
|
-
}
|
|
974
|
-
}
|
|
975
1213
|
return {
|
|
976
1214
|
categories,
|
|
977
1215
|
limit: msg.limit === undefined ? undefined : Number(msg.limit),
|
|
978
1216
|
sinceMs: msg.sinceMs === undefined ? undefined : Number(msg.sinceMs),
|
|
979
1217
|
sinceAgeMs: msg.sinceAgeMs === undefined ? undefined : Number(msg.sinceAgeMs),
|
|
980
1218
|
untilMs: msg.untilMs === undefined ? undefined : Number(msg.untilMs),
|
|
981
|
-
redaction:
|
|
982
|
-
msg.redaction === undefined
|
|
983
|
-
? undefined
|
|
984
|
-
: msg.redaction.trim().toLowerCase(),
|
|
985
1219
|
};
|
|
986
1220
|
}
|
|
987
1221
|
|
|
@@ -1187,26 +1421,6 @@ function createDownstreamHandler(opts) {
|
|
|
1187
1421
|
) {
|
|
1188
1422
|
return "debug-close-app-client";
|
|
1189
1423
|
}
|
|
1190
|
-
if (
|
|
1191
|
-
normalized === "debug-screen-off-worker-on" ||
|
|
1192
|
-
normalized === "debug_screen_off_worker_on" ||
|
|
1193
|
-
normalized === "debugscreenoffworkeron" ||
|
|
1194
|
-
normalized === "screen-off-worker-on" ||
|
|
1195
|
-
normalized === "screen_off_worker_on" ||
|
|
1196
|
-
normalized === "screenoffworkeron"
|
|
1197
|
-
) {
|
|
1198
|
-
return "debug-screen-off-worker-on";
|
|
1199
|
-
}
|
|
1200
|
-
if (
|
|
1201
|
-
normalized === "debug-screen-off-worker-off" ||
|
|
1202
|
-
normalized === "debug_screen_off_worker_off" ||
|
|
1203
|
-
normalized === "debugscreenoffworkeroff" ||
|
|
1204
|
-
normalized === "screen-off-worker-off" ||
|
|
1205
|
-
normalized === "screen_off_worker_off" ||
|
|
1206
|
-
normalized === "screenoffworkeroff"
|
|
1207
|
-
) {
|
|
1208
|
-
return "debug-screen-off-worker-off";
|
|
1209
|
-
}
|
|
1210
1424
|
throw new Error(`unsupported remote relayAction: ${raw}`);
|
|
1211
1425
|
}
|
|
1212
1426
|
|
|
@@ -1320,6 +1534,20 @@ function createDownstreamHandler(opts) {
|
|
|
1320
1534
|
payload.verboseLevel = normalized;
|
|
1321
1535
|
}
|
|
1322
1536
|
|
|
1537
|
+
const fastMode = parseOptionalBoolean(msg.fastMode, "fastMode");
|
|
1538
|
+
if (fastMode !== undefined) {
|
|
1539
|
+
payload.fastMode = fastMode;
|
|
1540
|
+
}
|
|
1541
|
+
|
|
1542
|
+
const elevatedLevel = parseOptionalTrimmedString(msg.elevatedLevel);
|
|
1543
|
+
if (elevatedLevel) {
|
|
1544
|
+
const normalized = elevatedLevel.toLowerCase();
|
|
1545
|
+
if (!["off", "on", "ask", "full"].includes(normalized)) {
|
|
1546
|
+
throw new Error("elevatedLevel must be off|on|ask|full");
|
|
1547
|
+
}
|
|
1548
|
+
payload.elevatedLevel = normalized;
|
|
1549
|
+
}
|
|
1550
|
+
|
|
1323
1551
|
if (Object.keys(payload).length === 0) {
|
|
1324
1552
|
throw new Error("setSessionModelConfig requires at least one field");
|
|
1325
1553
|
}
|
|
@@ -1391,6 +1619,13 @@ function createDownstreamHandler(opts) {
|
|
|
1391
1619
|
payload.listenEnabled = msg.listenEnabled;
|
|
1392
1620
|
}
|
|
1393
1621
|
|
|
1622
|
+
if (Object.prototype.hasOwnProperty.call(msg, "defaultFastMode")) {
|
|
1623
|
+
if (typeof msg.defaultFastMode !== "boolean") {
|
|
1624
|
+
throw new Error("defaultFastMode must be a boolean");
|
|
1625
|
+
}
|
|
1626
|
+
payload.defaultFastMode = msg.defaultFastMode;
|
|
1627
|
+
}
|
|
1628
|
+
|
|
1394
1629
|
if (Object.keys(payload).length === 0) {
|
|
1395
1630
|
throw new Error("setEvenAiSettings requires at least one field");
|
|
1396
1631
|
}
|
|
@@ -1426,6 +1661,13 @@ function createDownstreamHandler(opts) {
|
|
|
1426
1661
|
payload.defaultThinking = normalizeOcuClawDefaultThinking(msg.defaultThinking);
|
|
1427
1662
|
}
|
|
1428
1663
|
|
|
1664
|
+
if (Object.prototype.hasOwnProperty.call(msg, "defaultFastMode")) {
|
|
1665
|
+
if (typeof msg.defaultFastMode !== "boolean") {
|
|
1666
|
+
throw new Error("defaultFastMode must be a boolean");
|
|
1667
|
+
}
|
|
1668
|
+
payload.defaultFastMode = msg.defaultFastMode;
|
|
1669
|
+
}
|
|
1670
|
+
|
|
1429
1671
|
if (Object.keys(payload).length === 0) {
|
|
1430
1672
|
throw new Error("setOcuClawSettings requires at least one field");
|
|
1431
1673
|
}
|
|
@@ -1518,6 +1760,20 @@ function createDownstreamHandler(opts) {
|
|
|
1518
1760
|
return payload;
|
|
1519
1761
|
}
|
|
1520
1762
|
|
|
1763
|
+
if (action === "setting-set") {
|
|
1764
|
+
const settingKey = parseOptionalTrimmedString(msg.settingKey);
|
|
1765
|
+
const value = parseOptionalTrimmedString(msg.value);
|
|
1766
|
+
if (!settingKey) {
|
|
1767
|
+
throw new Error("remote-control setting-set requires settingKey");
|
|
1768
|
+
}
|
|
1769
|
+
if (!value) {
|
|
1770
|
+
throw new Error("remote-control setting-set requires value");
|
|
1771
|
+
}
|
|
1772
|
+
payload.settingKey = settingKey;
|
|
1773
|
+
payload.value = value;
|
|
1774
|
+
return payload;
|
|
1775
|
+
}
|
|
1776
|
+
|
|
1521
1777
|
if (action === "relay-action") {
|
|
1522
1778
|
payload.relayAction = normalizeRemoteRelayAction(msg.relayAction);
|
|
1523
1779
|
if (
|
|
@@ -1598,6 +1854,20 @@ function createDownstreamHandler(opts) {
|
|
|
1598
1854
|
};
|
|
1599
1855
|
}
|
|
1600
1856
|
|
|
1857
|
+
function parseAutomationStateGet(msg) {
|
|
1858
|
+
if (!msg || typeof msg !== "object") {
|
|
1859
|
+
throw new Error("automation state payload must be an object");
|
|
1860
|
+
}
|
|
1861
|
+
const requestId = parseOptionalTrimmedString(msg.requestId);
|
|
1862
|
+
if (!requestId) {
|
|
1863
|
+
throw new Error("automation state request requires requestId");
|
|
1864
|
+
}
|
|
1865
|
+
return {
|
|
1866
|
+
requestId,
|
|
1867
|
+
sessionKey: parseOptionalTrimmedString(msg.sessionKey) || null,
|
|
1868
|
+
};
|
|
1869
|
+
}
|
|
1870
|
+
|
|
1601
1871
|
const ATTACHMENT_MAX_DECODED_BYTES = 5_000_000;
|
|
1602
1872
|
const ATTACHMENT_MAX_ENCODED_CHARS =
|
|
1603
1873
|
Math.ceil((ATTACHMENT_MAX_DECODED_BYTES * 4) / 3) + 16;
|
|
@@ -1736,6 +2006,32 @@ function createDownstreamHandler(opts) {
|
|
|
1736
2006
|
};
|
|
1737
2007
|
}
|
|
1738
2008
|
|
|
2009
|
+
/**
|
|
2010
|
+
* Validate and normalize an inbound clientDisplaySignals envelope.
|
|
2011
|
+
* Returns null if absent or unparseable. Falls back to safe defaults
|
|
2012
|
+
* for individual fields rather than rejecting the whole envelope.
|
|
2013
|
+
*/
|
|
2014
|
+
function parseClientDisplaySignals(raw) {
|
|
2015
|
+
if (raw == null || typeof raw !== "object") return null;
|
|
2016
|
+
const coerceState = (val) => {
|
|
2017
|
+
const s = typeof val === "string" ? val : null;
|
|
2018
|
+
return s === "active" || s === "recently-disabled" || s === "inactive"
|
|
2019
|
+
? s
|
|
2020
|
+
: "inactive";
|
|
2021
|
+
};
|
|
2022
|
+
const state = coerceState(raw.neuralEmojiReactorState);
|
|
2023
|
+
const paceState = coerceState(raw.neuralPaceModulatorState);
|
|
2024
|
+
const enabledRaw = raw.neuralSessionNamesEnabled;
|
|
2025
|
+
const neuralSessionNamesEnabled =
|
|
2026
|
+
typeof enabledRaw === "boolean" ? enabledRaw : true;
|
|
2027
|
+
// readSpeedWpm tolerated but ignored on inbound for old clients.
|
|
2028
|
+
return {
|
|
2029
|
+
neuralEmojiReactorState: state,
|
|
2030
|
+
neuralPaceModulatorState: paceState,
|
|
2031
|
+
neuralSessionNamesEnabled,
|
|
2032
|
+
};
|
|
2033
|
+
}
|
|
2034
|
+
|
|
1739
2035
|
// --- Message handlers ---
|
|
1740
2036
|
|
|
1741
2037
|
/**
|
|
@@ -1749,7 +2045,7 @@ function createDownstreamHandler(opts) {
|
|
|
1749
2045
|
const requestId = parseOptionalTrimmedString(msg.requestId);
|
|
1750
2046
|
if (!requestId) {
|
|
1751
2047
|
return {
|
|
1752
|
-
unicast:
|
|
2048
|
+
unicast: formatSendAckCompat(
|
|
1753
2049
|
requestId,
|
|
1754
2050
|
"rejected",
|
|
1755
2051
|
"Missing required field: requestId",
|
|
@@ -1760,7 +2056,7 @@ function createDownstreamHandler(opts) {
|
|
|
1760
2056
|
const parsedAttachment = parseAttachment(msg.attachment);
|
|
1761
2057
|
if (!parsedAttachment.ok) {
|
|
1762
2058
|
return {
|
|
1763
|
-
unicast:
|
|
2059
|
+
unicast: formatSendAckCompat(
|
|
1764
2060
|
requestId,
|
|
1765
2061
|
"rejected",
|
|
1766
2062
|
parsedAttachment.error,
|
|
@@ -1772,7 +2068,7 @@ function createDownstreamHandler(opts) {
|
|
|
1772
2068
|
const text = typeof msg.text === "string" ? msg.text : "";
|
|
1773
2069
|
if (!text.trim() && !parsedAttachment.attachment) {
|
|
1774
2070
|
return {
|
|
1775
|
-
unicast:
|
|
2071
|
+
unicast: formatSendAckCompat(
|
|
1776
2072
|
requestId,
|
|
1777
2073
|
"rejected",
|
|
1778
2074
|
"Missing required field: text",
|
|
@@ -1783,7 +2079,7 @@ function createDownstreamHandler(opts) {
|
|
|
1783
2079
|
// Check upstream connectivity
|
|
1784
2080
|
if (!isUpstreamConnected()) {
|
|
1785
2081
|
return {
|
|
1786
|
-
unicast:
|
|
2082
|
+
unicast: formatSendAckCompat(
|
|
1787
2083
|
requestId,
|
|
1788
2084
|
"rejected",
|
|
1789
2085
|
"OpenClaw disconnected",
|
|
@@ -1791,26 +2087,66 @@ function createDownstreamHandler(opts) {
|
|
|
1791
2087
|
};
|
|
1792
2088
|
}
|
|
1793
2089
|
|
|
2090
|
+
const operation =
|
|
2091
|
+
operationRegistry && typeof operationRegistry.beginMessageSend === "function"
|
|
2092
|
+
? operationRegistry.beginMessageSend({
|
|
2093
|
+
requestId,
|
|
2094
|
+
clientId,
|
|
2095
|
+
sessionKey: msg.sessionKey || null,
|
|
2096
|
+
})
|
|
2097
|
+
: null;
|
|
2098
|
+
|
|
2099
|
+
if (operation && operation.duplicate) {
|
|
2100
|
+
const frames = operation.finalFrame
|
|
2101
|
+
? [operation.receipt, operation.finalFrame]
|
|
2102
|
+
: [operation.receipt];
|
|
2103
|
+
return { unicast: frames };
|
|
2104
|
+
}
|
|
2105
|
+
|
|
2106
|
+
const clientDisplaySignals = parseClientDisplaySignals(msg.clientDisplaySignals);
|
|
2107
|
+
|
|
1794
2108
|
// Forward to upstream — resolves on initial ack (accepted/queued)
|
|
1795
|
-
|
|
2109
|
+
const followup = onSend(
|
|
1796
2110
|
requestId,
|
|
1797
2111
|
text,
|
|
1798
2112
|
msg.sessionKey || null,
|
|
1799
2113
|
parsedAttachment.attachment,
|
|
2114
|
+
clientDisplaySignals,
|
|
1800
2115
|
).then(
|
|
1801
2116
|
(result) => {
|
|
1802
2117
|
const status = (result && result.status) || "accepted";
|
|
1803
|
-
|
|
2118
|
+
const frame = formatSendAckCompat(requestId, status);
|
|
2119
|
+
if (operation && typeof operation.complete === "function") {
|
|
2120
|
+
operation.complete(frame, { status });
|
|
2121
|
+
}
|
|
2122
|
+
return { unicast: frame };
|
|
1804
2123
|
},
|
|
1805
|
-
(err) =>
|
|
1806
|
-
|
|
2124
|
+
(err) => {
|
|
2125
|
+
const frame = formatSendAckCompat(
|
|
1807
2126
|
requestId,
|
|
1808
2127
|
"rejected",
|
|
1809
2128
|
err.message || "Send failed",
|
|
1810
|
-
err.errorCode || undefined,
|
|
1811
|
-
)
|
|
1812
|
-
|
|
2129
|
+
err.errorCode || err.code || undefined,
|
|
2130
|
+
);
|
|
2131
|
+
if (operation && typeof operation.fail === "function") {
|
|
2132
|
+
operation.fail(frame, {
|
|
2133
|
+
errorCode: err.errorCode || err.code || null,
|
|
2134
|
+
message: err.message || "Send failed",
|
|
2135
|
+
});
|
|
2136
|
+
}
|
|
2137
|
+
return { unicast: frame };
|
|
2138
|
+
},
|
|
1813
2139
|
);
|
|
2140
|
+
|
|
2141
|
+
return operation
|
|
2142
|
+
? {
|
|
2143
|
+
unicast: operation.receipt || formatOperationReceived({
|
|
2144
|
+
requestId,
|
|
2145
|
+
operation: "message.send",
|
|
2146
|
+
}),
|
|
2147
|
+
followup,
|
|
2148
|
+
}
|
|
2149
|
+
: followup;
|
|
1814
2150
|
}
|
|
1815
2151
|
|
|
1816
2152
|
/**
|
|
@@ -1839,7 +2175,7 @@ function createDownstreamHandler(opts) {
|
|
|
1839
2175
|
const id = parseOptionalTrimmedString(msg.id);
|
|
1840
2176
|
if (!id) {
|
|
1841
2177
|
return {
|
|
1842
|
-
unicast:
|
|
2178
|
+
unicast: formatSendAckCompat(
|
|
1843
2179
|
msg.id || null,
|
|
1844
2180
|
"rejected",
|
|
1845
2181
|
"Missing required field: id",
|
|
@@ -1849,7 +2185,7 @@ function createDownstreamHandler(opts) {
|
|
|
1849
2185
|
|
|
1850
2186
|
if (!onSimulateStream) {
|
|
1851
2187
|
return {
|
|
1852
|
-
unicast:
|
|
2188
|
+
unicast: formatSendAckCompat(
|
|
1853
2189
|
id,
|
|
1854
2190
|
"rejected",
|
|
1855
2191
|
"simulateStream not supported by relay",
|
|
@@ -1860,7 +2196,7 @@ function createDownstreamHandler(opts) {
|
|
|
1860
2196
|
const text = typeof msg.text === "string" ? msg.text : "";
|
|
1861
2197
|
if (!text.trim()) {
|
|
1862
2198
|
return {
|
|
1863
|
-
unicast:
|
|
2199
|
+
unicast: formatSendAckCompat(
|
|
1864
2200
|
id,
|
|
1865
2201
|
"rejected",
|
|
1866
2202
|
"simulateStream requires non-empty text",
|
|
@@ -1879,7 +2215,7 @@ function createDownstreamHandler(opts) {
|
|
|
1879
2215
|
thinkingTailMs = parseOptionalNonNegativeNumber(msg.thinkingTailMs, "thinkingTailMs");
|
|
1880
2216
|
} catch (err) {
|
|
1881
2217
|
return {
|
|
1882
|
-
unicast:
|
|
2218
|
+
unicast: formatSendAckCompat(
|
|
1883
2219
|
id,
|
|
1884
2220
|
"rejected",
|
|
1885
2221
|
err && err.message ? err.message : "Invalid simulateStream parameters",
|
|
@@ -1902,10 +2238,10 @@ function createDownstreamHandler(opts) {
|
|
|
1902
2238
|
(result) => {
|
|
1903
2239
|
const status = result && result.status ? result.status : "accepted";
|
|
1904
2240
|
const error = result && result.error ? result.error : undefined;
|
|
1905
|
-
return { unicast:
|
|
2241
|
+
return { unicast: formatSendAckCompat(id, status, error) };
|
|
1906
2242
|
},
|
|
1907
2243
|
(err) => ({
|
|
1908
|
-
unicast:
|
|
2244
|
+
unicast: formatSendAckCompat(
|
|
1909
2245
|
id,
|
|
1910
2246
|
"rejected",
|
|
1911
2247
|
err && err.message ? err.message : "simulateStream failed",
|
|
@@ -2168,6 +2504,41 @@ function createDownstreamHandler(opts) {
|
|
|
2168
2504
|
);
|
|
2169
2505
|
}
|
|
2170
2506
|
|
|
2507
|
+
/**
|
|
2508
|
+
* Handle "getProviderUsageSnapshot": return the current provider usage snapshot.
|
|
2509
|
+
*
|
|
2510
|
+
* @param {string} clientId
|
|
2511
|
+
* @returns {Promise<{ unicast: string }>|{ unicast: string }}
|
|
2512
|
+
*/
|
|
2513
|
+
function handleGetProviderUsageSnapshot(clientId) {
|
|
2514
|
+
const emptySnapshot = () => ({
|
|
2515
|
+
sessionKey: null,
|
|
2516
|
+
provider: null,
|
|
2517
|
+
displayName: null,
|
|
2518
|
+
limitingWindowKey: null,
|
|
2519
|
+
windows: [],
|
|
2520
|
+
fetchedAtMs: Date.now(),
|
|
2521
|
+
stale: true,
|
|
2522
|
+
});
|
|
2523
|
+
|
|
2524
|
+
if (!onGetProviderUsageSnapshot) {
|
|
2525
|
+
return {
|
|
2526
|
+
unicast: formatProviderUsageSnapshot(emptySnapshot()),
|
|
2527
|
+
};
|
|
2528
|
+
}
|
|
2529
|
+
return Promise.resolve(onGetProviderUsageSnapshot()).then(
|
|
2530
|
+
(payload) => ({
|
|
2531
|
+
unicast: formatProviderUsageSnapshot(payload || {}),
|
|
2532
|
+
}),
|
|
2533
|
+
(err) => {
|
|
2534
|
+
logger.error(`[downstream] getProviderUsageSnapshot failed: ${err.message}`);
|
|
2535
|
+
return {
|
|
2536
|
+
unicast: formatProviderUsageSnapshot(emptySnapshot()),
|
|
2537
|
+
};
|
|
2538
|
+
},
|
|
2539
|
+
);
|
|
2540
|
+
}
|
|
2541
|
+
|
|
2171
2542
|
/**
|
|
2172
2543
|
* Handle "getStatus": return the current status snapshot.
|
|
2173
2544
|
*
|
|
@@ -2252,6 +2623,53 @@ function createDownstreamHandler(opts) {
|
|
|
2252
2623
|
);
|
|
2253
2624
|
}
|
|
2254
2625
|
|
|
2626
|
+
/**
|
|
2627
|
+
* Handle "compactSession": trigger gateway-side compaction for a session key.
|
|
2628
|
+
*
|
|
2629
|
+
* @param {string} clientId
|
|
2630
|
+
* @param {object} msg - { sessionKey, requestId }
|
|
2631
|
+
* @returns {Promise<{ unicast: string }>}
|
|
2632
|
+
*/
|
|
2633
|
+
function handleCompactSession(clientId, msg) {
|
|
2634
|
+
if (!onCompactSession) {
|
|
2635
|
+
return Promise.resolve({
|
|
2636
|
+
unicast: formatCompactSessionAck({
|
|
2637
|
+
status: "rejected",
|
|
2638
|
+
requestId: msg && msg.requestId,
|
|
2639
|
+
error: "compactSession is not available",
|
|
2640
|
+
}),
|
|
2641
|
+
});
|
|
2642
|
+
}
|
|
2643
|
+
const sessionKey =
|
|
2644
|
+
msg && typeof msg.sessionKey === "string" && msg.sessionKey
|
|
2645
|
+
? msg.sessionKey
|
|
2646
|
+
: null;
|
|
2647
|
+
if (!sessionKey) {
|
|
2648
|
+
return Promise.resolve({
|
|
2649
|
+
unicast: formatCompactSessionAck({
|
|
2650
|
+
status: "rejected",
|
|
2651
|
+
requestId: msg && msg.requestId,
|
|
2652
|
+
error: "sessionKey is required",
|
|
2653
|
+
}),
|
|
2654
|
+
});
|
|
2655
|
+
}
|
|
2656
|
+
return Promise.resolve(onCompactSession({ sessionKey })).then(
|
|
2657
|
+
(result) => ({
|
|
2658
|
+
unicast: formatCompactSessionAck({
|
|
2659
|
+
...(result || { status: "accepted" }),
|
|
2660
|
+
requestId: msg.requestId,
|
|
2661
|
+
}),
|
|
2662
|
+
}),
|
|
2663
|
+
(err) => ({
|
|
2664
|
+
unicast: formatCompactSessionAck({
|
|
2665
|
+
status: "rejected",
|
|
2666
|
+
requestId: msg.requestId,
|
|
2667
|
+
error: err && err.message ? err.message : "compactSession failed",
|
|
2668
|
+
}),
|
|
2669
|
+
}),
|
|
2670
|
+
);
|
|
2671
|
+
}
|
|
2672
|
+
|
|
2255
2673
|
/**
|
|
2256
2674
|
* Handle "getEvenAiSettings": read relay-owned Even AI settings.
|
|
2257
2675
|
*
|
|
@@ -2447,6 +2865,65 @@ function createDownstreamHandler(opts) {
|
|
|
2447
2865
|
);
|
|
2448
2866
|
}
|
|
2449
2867
|
|
|
2868
|
+
/**
|
|
2869
|
+
* Handle a "session.title.set" message: user-edited session title from
|
|
2870
|
+
* phone UI. Sticky-locks the agent against retitling.
|
|
2871
|
+
*
|
|
2872
|
+
* @param {string} clientId
|
|
2873
|
+
* @param {object} msg - Parsed message with sessionKey and title
|
|
2874
|
+
* @returns {null}
|
|
2875
|
+
*/
|
|
2876
|
+
function handleSetUserSessionTitle(clientId, msg) {
|
|
2877
|
+
if (typeof onSetUserSessionTitle !== "function") return null;
|
|
2878
|
+
const sessionKey =
|
|
2879
|
+
typeof msg.sessionKey === "string" ? msg.sessionKey.trim() : "";
|
|
2880
|
+
const title = typeof msg.title === "string" ? msg.title.trim() : "";
|
|
2881
|
+
if (!sessionKey || !title) return null;
|
|
2882
|
+
if (title.length > 55) return null;
|
|
2883
|
+
onSetUserSessionTitle(sessionKey, title);
|
|
2884
|
+
return null;
|
|
2885
|
+
}
|
|
2886
|
+
|
|
2887
|
+
function handleSetSessionPinned(clientId, msg) {
|
|
2888
|
+
if (typeof onSetSessionPinned !== "function") return null;
|
|
2889
|
+
const sessionKey =
|
|
2890
|
+
typeof msg.sessionKey === "string" ? msg.sessionKey.trim() : "";
|
|
2891
|
+
const pinned = msg.pinned === true;
|
|
2892
|
+
const kind = msg.kind;
|
|
2893
|
+
if (!sessionKey || (kind !== "ocuclaw" && kind !== "evenai")) {
|
|
2894
|
+
return { unicast: formatError("invalid_session_pin_request") };
|
|
2895
|
+
}
|
|
2896
|
+
const result = onSetSessionPinned(sessionKey, pinned, kind);
|
|
2897
|
+
if (result && result.ok === false) {
|
|
2898
|
+
const code = result.reason === "cap" ? "pin_cap_reached" : "invalid_session_pin_request";
|
|
2899
|
+
return { unicast: formatError(code) };
|
|
2900
|
+
}
|
|
2901
|
+
return null;
|
|
2902
|
+
}
|
|
2903
|
+
|
|
2904
|
+
function handleDeleteSessions(clientId, msg) {
|
|
2905
|
+
if (typeof onDeleteSessions !== "function") return null;
|
|
2906
|
+
const sessionKeys = Array.isArray(msg.sessionKeys) ? msg.sessionKeys.filter((k) => typeof k === "string" && k) : [];
|
|
2907
|
+
const kind = msg.kind;
|
|
2908
|
+
const switchBeforeDelete = msg.switchBeforeDelete === true;
|
|
2909
|
+
if (sessionKeys.length === 0 || (kind !== "ocuclaw" && kind !== "evenai")) {
|
|
2910
|
+
return { unicast: formatError("invalid_session_delete_request") };
|
|
2911
|
+
}
|
|
2912
|
+
onDeleteSessions(sessionKeys, kind, switchBeforeDelete);
|
|
2913
|
+
return null;
|
|
2914
|
+
}
|
|
2915
|
+
|
|
2916
|
+
function handleSearchTranscripts(clientId, msg) {
|
|
2917
|
+
if (typeof onSearchTranscripts !== "function") return null;
|
|
2918
|
+
const query = typeof msg.query === "string" ? msg.query : "";
|
|
2919
|
+
const kind = msg.kind;
|
|
2920
|
+
if (!query.trim() || (kind !== "ocuclaw" && kind !== "evenai")) {
|
|
2921
|
+
return { unicast: formatError("invalid_transcript_search_request") };
|
|
2922
|
+
}
|
|
2923
|
+
onSearchTranscripts(clientId, query, kind);
|
|
2924
|
+
return null;
|
|
2925
|
+
}
|
|
2926
|
+
|
|
2450
2927
|
/**
|
|
2451
2928
|
* Handle a "slashCommand" message: forward a slash command to OpenClaw.
|
|
2452
2929
|
*
|
|
@@ -2650,6 +3127,36 @@ function createDownstreamHandler(opts) {
|
|
|
2650
3127
|
}
|
|
2651
3128
|
}
|
|
2652
3129
|
|
|
3130
|
+
function handleTraceLogSet(clientId, msg) {
|
|
3131
|
+
if (!onTraceLogSet) {
|
|
3132
|
+
return { unicast: formatError("trace-log-set is not available") };
|
|
3133
|
+
}
|
|
3134
|
+
let payload;
|
|
3135
|
+
try {
|
|
3136
|
+
payload = parseTraceLogSet(msg);
|
|
3137
|
+
} catch (err) {
|
|
3138
|
+
return { unicast: formatError(err.message) };
|
|
3139
|
+
}
|
|
3140
|
+
try {
|
|
3141
|
+
const result = onTraceLogSet(clientId, payload) || { ok: true };
|
|
3142
|
+
return { unicast: formatTraceLog(result) };
|
|
3143
|
+
} catch (err) {
|
|
3144
|
+
return { unicast: formatError(err.message || "trace-log-set failed") };
|
|
3145
|
+
}
|
|
3146
|
+
}
|
|
3147
|
+
|
|
3148
|
+
function handleTraceLogGet(clientId) {
|
|
3149
|
+
if (!onTraceLogGet) {
|
|
3150
|
+
return { unicast: formatError("trace-log-get is not available") };
|
|
3151
|
+
}
|
|
3152
|
+
try {
|
|
3153
|
+
const result = onTraceLogGet(clientId) || { ok: true };
|
|
3154
|
+
return { unicast: formatTraceLog(result) };
|
|
3155
|
+
} catch (err) {
|
|
3156
|
+
return { unicast: formatError(err.message || "trace-log-get failed") };
|
|
3157
|
+
}
|
|
3158
|
+
}
|
|
3159
|
+
|
|
2653
3160
|
/**
|
|
2654
3161
|
* Handle a "remote-control" message: dispatch remote actions to app clients.
|
|
2655
3162
|
*/
|
|
@@ -2757,6 +3264,82 @@ function createDownstreamHandler(opts) {
|
|
|
2757
3264
|
}
|
|
2758
3265
|
}
|
|
2759
3266
|
|
|
3267
|
+
function handleAutomationState(clientId, msg) {
|
|
3268
|
+
let payload;
|
|
3269
|
+
try {
|
|
3270
|
+
payload = parseAutomationStateGet(msg);
|
|
3271
|
+
} catch (err) {
|
|
3272
|
+
return {
|
|
3273
|
+
unicast: formatAutomationStateSnapshot({
|
|
3274
|
+
ok: false,
|
|
3275
|
+
requestId:
|
|
3276
|
+
msg && typeof msg.requestId === "string" ? msg.requestId : null,
|
|
3277
|
+
reasonCode: "snapshot_unavailable",
|
|
3278
|
+
message: err.message,
|
|
3279
|
+
}),
|
|
3280
|
+
};
|
|
3281
|
+
}
|
|
3282
|
+
|
|
3283
|
+
if (!onAutomationState) {
|
|
3284
|
+
return null;
|
|
3285
|
+
}
|
|
3286
|
+
|
|
3287
|
+
const finalize = (result) => {
|
|
3288
|
+
const resolved = result || {};
|
|
3289
|
+
const requestId = resolved.requestId || payload.requestId;
|
|
3290
|
+
if (
|
|
3291
|
+
resolved.ok === false ||
|
|
3292
|
+
!resolved.targetClientId ||
|
|
3293
|
+
!resolved.request
|
|
3294
|
+
) {
|
|
3295
|
+
return {
|
|
3296
|
+
unicast: formatAutomationStateSnapshot({
|
|
3297
|
+
ok: false,
|
|
3298
|
+
requestId,
|
|
3299
|
+
reasonCode: resolved.reasonCode || "snapshot_unavailable",
|
|
3300
|
+
message:
|
|
3301
|
+
resolved.message || "automation state request was not dispatched",
|
|
3302
|
+
}),
|
|
3303
|
+
};
|
|
3304
|
+
}
|
|
3305
|
+
|
|
3306
|
+
return {
|
|
3307
|
+
automationStateRequest: {
|
|
3308
|
+
requestId,
|
|
3309
|
+
targetClientId: resolved.targetClientId,
|
|
3310
|
+
message: formatAutomationStateRequest(resolved.request),
|
|
3311
|
+
},
|
|
3312
|
+
};
|
|
3313
|
+
};
|
|
3314
|
+
|
|
3315
|
+
try {
|
|
3316
|
+
const result = onAutomationState(clientId, payload);
|
|
3317
|
+
if (result && typeof result.then === "function") {
|
|
3318
|
+
return result.then(
|
|
3319
|
+
(resolved) => finalize(resolved),
|
|
3320
|
+
(err) => ({
|
|
3321
|
+
unicast: formatAutomationStateSnapshot({
|
|
3322
|
+
ok: false,
|
|
3323
|
+
requestId: payload.requestId,
|
|
3324
|
+
reasonCode: "snapshot_unavailable",
|
|
3325
|
+
message: err.message || "automation state request failed",
|
|
3326
|
+
}),
|
|
3327
|
+
}),
|
|
3328
|
+
);
|
|
3329
|
+
}
|
|
3330
|
+
return finalize(result);
|
|
3331
|
+
} catch (err) {
|
|
3332
|
+
return {
|
|
3333
|
+
unicast: formatAutomationStateSnapshot({
|
|
3334
|
+
ok: false,
|
|
3335
|
+
requestId: payload.requestId,
|
|
3336
|
+
reasonCode: "snapshot_unavailable",
|
|
3337
|
+
message: err.message || "automation state request failed",
|
|
3338
|
+
}),
|
|
3339
|
+
};
|
|
3340
|
+
}
|
|
3341
|
+
}
|
|
3342
|
+
|
|
2760
3343
|
// --- Public API ---
|
|
2761
3344
|
|
|
2762
3345
|
return {
|
|
@@ -2805,6 +3388,14 @@ function createDownstreamHandler(opts) {
|
|
|
2805
3388
|
return handleSwitchSession(clientId, msg);
|
|
2806
3389
|
case APP_PROTOCOL.sessionCreate:
|
|
2807
3390
|
return handleNewSession(clientId);
|
|
3391
|
+
case APP_PROTOCOL.sessionTitleSet:
|
|
3392
|
+
return handleSetUserSessionTitle(clientId, msg);
|
|
3393
|
+
case "ocuclaw.session.pinned.set":
|
|
3394
|
+
return handleSetSessionPinned(clientId, msg);
|
|
3395
|
+
case "ocuclaw.session.delete":
|
|
3396
|
+
return handleDeleteSessions(clientId, msg);
|
|
3397
|
+
case "ocuclaw.session.transcripts.search":
|
|
3398
|
+
return handleSearchTranscripts(clientId, msg);
|
|
2808
3399
|
case APP_PROTOCOL.modelCatalogGet:
|
|
2809
3400
|
return handleGetModelsCatalog(clientId);
|
|
2810
3401
|
case APP_PROTOCOL.skillsCatalogGet:
|
|
@@ -2813,6 +3404,9 @@ function createDownstreamHandler(opts) {
|
|
|
2813
3404
|
case APP_PROTOCOL.sonioxModelsGet:
|
|
2814
3405
|
case "getSonioxModels":
|
|
2815
3406
|
return handleGetSonioxModels(clientId);
|
|
3407
|
+
case APP_PROTOCOL.providerUsageGet:
|
|
3408
|
+
case "getProviderUsageSnapshot":
|
|
3409
|
+
return handleGetProviderUsageSnapshot(clientId);
|
|
2816
3410
|
case APP_PROTOCOL.statusGet:
|
|
2817
3411
|
case "getStatus":
|
|
2818
3412
|
return handleGetStatus(clientId);
|
|
@@ -2820,6 +3414,8 @@ function createDownstreamHandler(opts) {
|
|
|
2820
3414
|
return handleGetSessionModelConfig(clientId);
|
|
2821
3415
|
case APP_PROTOCOL.sessionConfigSet:
|
|
2822
3416
|
return handleSetSessionModelConfig(clientId, msg);
|
|
3417
|
+
case APP_PROTOCOL.sessionCompact:
|
|
3418
|
+
return handleCompactSession(clientId, msg);
|
|
2823
3419
|
case APP_PROTOCOL.evenAiSettingsGet:
|
|
2824
3420
|
return handleGetEvenAiSettings(clientId);
|
|
2825
3421
|
case APP_PROTOCOL.evenAiSessionList:
|
|
@@ -2845,12 +3441,83 @@ function createDownstreamHandler(opts) {
|
|
|
2845
3441
|
return handleDebugSet(clientId, msg);
|
|
2846
3442
|
case "debug-dump":
|
|
2847
3443
|
return handleDebugDump(clientId, msg);
|
|
3444
|
+
case "trace-log-set":
|
|
3445
|
+
return handleTraceLogSet(clientId, msg);
|
|
3446
|
+
case "trace-log-get":
|
|
3447
|
+
return handleTraceLogGet(clientId);
|
|
2848
3448
|
case "remote-control":
|
|
2849
3449
|
return handleRemoteControl(clientId, msg);
|
|
3450
|
+
case APP_PROTOCOL.automationStateGet:
|
|
3451
|
+
return handleAutomationState(clientId, msg);
|
|
2850
3452
|
case APP_PROTOCOL.readinessProbeRequest:
|
|
2851
3453
|
return handleReadinessProbe(clientId, msg);
|
|
2852
3454
|
case APP_PROTOCOL.debugEvent:
|
|
2853
3455
|
return handleEventDebug(clientId, msg);
|
|
3456
|
+
case "glasses_ui_result":
|
|
3457
|
+
if (typeof onGlassesUiResult === "function") {
|
|
3458
|
+
try {
|
|
3459
|
+
onGlassesUiResult({
|
|
3460
|
+
surfaceId: typeof msg.surfaceId === "string" ? msg.surfaceId : "",
|
|
3461
|
+
outcome: msg.outcome,
|
|
3462
|
+
});
|
|
3463
|
+
} catch (err) {
|
|
3464
|
+
logger.warn(
|
|
3465
|
+
`[downstream] glasses_ui_result handler threw: ${err && err.message ? err.message : err}`,
|
|
3466
|
+
);
|
|
3467
|
+
}
|
|
3468
|
+
}
|
|
3469
|
+
return null;
|
|
3470
|
+
case "glasses_ui_nav_event":
|
|
3471
|
+
// Client-reported nav-stack transition (push reports the PARENT
|
|
3472
|
+
// surfaceId; pop reports the post-pop depth) so the plugin can
|
|
3473
|
+
// pause/resume the right surface's cron. Not a debug tool — stays out
|
|
3474
|
+
// of isExternalDebugToolMessageType.
|
|
3475
|
+
if (typeof onGlassesUiNavEvent === "function") {
|
|
3476
|
+
try {
|
|
3477
|
+
onGlassesUiNavEvent({
|
|
3478
|
+
surfaceId: typeof msg.surfaceId === "string" ? msg.surfaceId : "",
|
|
3479
|
+
depth: Number.isFinite(msg.depth) ? Math.max(1, Math.floor(msg.depth)) : 1,
|
|
3480
|
+
});
|
|
3481
|
+
} catch (err) {
|
|
3482
|
+
logger.warn(
|
|
3483
|
+
`[downstream] glasses_ui_nav_event handler threw: ${err && err.message ? err.message : err}`,
|
|
3484
|
+
);
|
|
3485
|
+
}
|
|
3486
|
+
}
|
|
3487
|
+
return null;
|
|
3488
|
+
case "glasses_ui_render":
|
|
3489
|
+
// Debug inject path used by tools/debugctl.js glasses-ui render to
|
|
3490
|
+
// exercise the simulator paint loop without spinning up an agent.
|
|
3491
|
+
if (typeof onGlassesUiRenderInject === "function") {
|
|
3492
|
+
try {
|
|
3493
|
+
onGlassesUiRenderInject({
|
|
3494
|
+
surfaceId: typeof msg.surfaceId === "string" ? msg.surfaceId : "",
|
|
3495
|
+
depth: Number.isFinite(msg.depth) ? Math.max(1, Math.floor(msg.depth)) : 1,
|
|
3496
|
+
spec: msg.spec,
|
|
3497
|
+
});
|
|
3498
|
+
} catch (err) {
|
|
3499
|
+
logger.warn(
|
|
3500
|
+
`[downstream] glasses_ui_render inject handler threw: ${err && err.message ? err.message : err}`,
|
|
3501
|
+
);
|
|
3502
|
+
}
|
|
3503
|
+
}
|
|
3504
|
+
return null;
|
|
3505
|
+
case "device_info_response":
|
|
3506
|
+
if (typeof onDeviceInfoResponse === "function") {
|
|
3507
|
+
try {
|
|
3508
|
+
onDeviceInfoResponse({
|
|
3509
|
+
requestId: typeof msg.requestId === "string" ? msg.requestId : "",
|
|
3510
|
+
ok: msg.ok === true,
|
|
3511
|
+
code: typeof msg.code === "string" ? msg.code : undefined,
|
|
3512
|
+
data: msg.data && typeof msg.data === "object" ? msg.data : undefined,
|
|
3513
|
+
});
|
|
3514
|
+
} catch (err) {
|
|
3515
|
+
logger.warn(
|
|
3516
|
+
`[downstream] device_info_response handler threw: ${err && err.message ? err.message : err}`,
|
|
3517
|
+
);
|
|
3518
|
+
}
|
|
3519
|
+
}
|
|
3520
|
+
return null;
|
|
2854
3521
|
default:
|
|
2855
3522
|
return null;
|
|
2856
3523
|
}
|
|
@@ -2859,7 +3526,8 @@ function createDownstreamHandler(opts) {
|
|
|
2859
3526
|
formatPages,
|
|
2860
3527
|
formatStatus,
|
|
2861
3528
|
formatActivity,
|
|
2862
|
-
|
|
3529
|
+
formatTyping,
|
|
3530
|
+
formatSendAck: formatSendAckCompat,
|
|
2863
3531
|
formatProtocol,
|
|
2864
3532
|
formatStreaming,
|
|
2865
3533
|
formatSessions,
|
|
@@ -2867,6 +3535,7 @@ function createDownstreamHandler(opts) {
|
|
|
2867
3535
|
formatModelsCatalog,
|
|
2868
3536
|
formatSkillsCatalog,
|
|
2869
3537
|
formatSonioxModels,
|
|
3538
|
+
formatProviderUsageSnapshot,
|
|
2870
3539
|
formatSessionModelConfig,
|
|
2871
3540
|
formatSessionModelConfigAck,
|
|
2872
3541
|
formatEvenAiSettings,
|
|
@@ -2877,8 +3546,8 @@ function createDownstreamHandler(opts) {
|
|
|
2877
3546
|
formatApproval,
|
|
2878
3547
|
formatApprovalResolved,
|
|
2879
3548
|
formatApprovalResponseAck,
|
|
2880
|
-
formatTranscription,
|
|
2881
3549
|
formatListenCommitted,
|
|
3550
|
+
formatEvenAiListenIntercepted,
|
|
2882
3551
|
formatListenEnded,
|
|
2883
3552
|
formatListenError,
|
|
2884
3553
|
formatListenReady,
|
|
@@ -2889,6 +3558,8 @@ function createDownstreamHandler(opts) {
|
|
|
2889
3558
|
formatDebugConfigSnapshot,
|
|
2890
3559
|
formatRemoteControl,
|
|
2891
3560
|
formatRemoteControlAck,
|
|
3561
|
+
formatAutomationStateRequest,
|
|
3562
|
+
formatAutomationStateSnapshot,
|
|
2892
3563
|
formatReadinessProbeRequest,
|
|
2893
3564
|
formatReadinessProbeAck,
|
|
2894
3565
|
formatError,
|