ocuclaw 1.3.3 → 1.3.4
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 +29 -1
- package/dist/config/runtime-config-session-title-model.test.js +0 -3
- package/dist/config/runtime-config.js +22 -33
- package/dist/domain/activity-status-adapter.js +0 -7
- package/dist/domain/activity-status-arbiter.js +3 -27
- package/dist/domain/activity-status-labels.js +8 -38
- package/dist/domain/code-span-regions.js +4 -24
- package/dist/domain/constant-time-equal.js +9 -0
- package/dist/domain/constant-time-equal.test.js +28 -0
- package/dist/domain/conversation-state.js +27 -138
- package/dist/domain/debug-bundle-cache.js +52 -0
- package/dist/domain/debug-bundle-format.js +60 -0
- package/dist/domain/debug-bundle-preview.js +123 -0
- package/dist/domain/debug-bundle-redaction.js +182 -0
- package/dist/domain/debug-bundle-save.js +11 -0
- package/dist/domain/debug-bundle-zip.js +15 -0
- package/dist/domain/debug-bundle.js +97 -0
- package/dist/domain/debug-store.js +6 -17
- package/dist/domain/debug-upload-preset.js +27 -0
- package/dist/domain/glasses-display-system-prompt.js +0 -5
- package/dist/domain/glasses-display-system-prompt.test.js +1 -1
- package/dist/domain/glasses-ui-content-summary.js +0 -6
- package/dist/domain/glasses-ui-system-prompt.test.js +1 -2
- package/dist/domain/message-emoji-allowlist.js +0 -7
- package/dist/domain/message-emoji-filter.js +3 -9
- package/dist/domain/neural-emoji-reactor-tag-config.js +3 -3
- package/dist/domain/prompt-channel-fragments.js +1 -10
- package/dist/domain/tagged-span-parser.js +3 -26
- package/dist/domain/tagged-span-strip.js +0 -7
- package/dist/even-ai/even-ai-endpoint.js +77 -24
- package/dist/even-ai/even-ai-run-waiter.js +0 -1
- package/dist/even-ai/even-ai-settings-store.js +11 -0
- package/dist/gateway/gateway-bridge.js +8 -9
- package/dist/gateway/gateway-timing-ledger.js +8 -6
- package/dist/gateway/openclaw-client.js +97 -297
- package/dist/gateway/sanitize-connect-reason.js +10 -0
- package/dist/gateway/sanitize-connect-reason.test.js +34 -0
- package/dist/index.js +3 -3
- package/dist/runtime/channel-two-hook.js +1 -6
- package/dist/runtime/container-env.js +1 -5
- package/dist/runtime/debug-bundle-handler.js +159 -0
- package/dist/runtime/display-toggle-states.js +6 -17
- package/dist/runtime/downstream-handler.js +682 -508
- package/dist/runtime/glasses-backpressure-latch.js +2 -24
- package/dist/runtime/ocuclaw-settings-store.js +10 -1
- package/dist/runtime/openclaw-host-version.js +5 -0
- package/dist/runtime/plugin-version-service.js +13 -6
- package/dist/runtime/provider-usage-select.js +0 -6
- package/dist/runtime/register-session-title-distiller.js +14 -16
- package/dist/runtime/relay-core.js +601 -290
- package/dist/runtime/relay-service.js +19 -47
- package/dist/runtime/relay-worker-approval-replay-cache.js +1 -1
- package/dist/runtime/relay-worker-entry.js +1 -2
- package/dist/runtime/relay-worker-health.js +2 -10
- package/dist/runtime/relay-worker-protocol.js +6 -1
- package/dist/runtime/relay-worker-supervisor.js +103 -41
- package/dist/runtime/relay-worker-transport.js +150 -17
- package/dist/runtime/session-context-service.js +5 -45
- package/dist/runtime/session-service.js +157 -175
- package/dist/runtime/session-title-distiller-budget.js +1 -5
- package/dist/runtime/session-title-distiller-helpers.js +14 -24
- package/dist/runtime/session-title-distiller.js +109 -122
- package/dist/runtime/session-title-record.js +0 -6
- package/dist/runtime/stable-prompt-snapshot.js +3 -14
- package/dist/runtime/upstream-runtime.js +600 -103
- package/dist/tools/device-info-tool.js +4 -21
- package/dist/tools/glasses-ui-cron.js +22 -77
- package/dist/tools/glasses-ui-descriptors.js +4 -33
- package/dist/tools/glasses-ui-limits.js +0 -13
- package/dist/tools/glasses-ui-paint-floor.js +5 -39
- package/dist/tools/glasses-ui-recipes.js +92 -101
- package/dist/tools/glasses-ui-surfaces.js +31 -163
- package/dist/tools/glasses-ui-template.js +7 -22
- package/dist/tools/glasses-ui-tool-description.test.js +2 -2
- package/dist/tools/glasses-ui-tool.js +87 -451
- package/dist/tools/glasses-ui-voicemail.js +6 -63
- package/dist/tools/glasses-ui-wake.js +9 -76
- package/dist/tools/session-title-tool.js +2 -7
- package/dist/tools/session-title-tool.test.js +1 -1
- package/dist/version.js +3 -2
- package/openclaw.plugin.json +60 -13
- package/package.json +3 -2
- package/dist/runtime/protocol-adapter.js +0 -387
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createHash, randomUUID } from "node:crypto";
|
|
2
|
+
import { constantTimeEqual } from "../domain/constant-time-equal.js";
|
|
2
3
|
import { filterRawEmojiText } from "../domain/message-emoji-filter.js";
|
|
3
4
|
import { composeReadabilitySystemPrompt } from "../domain/readability-system-prompt.js";
|
|
4
5
|
import { normalizeEvenAiSystemPrompt } from "./even-ai-settings-store.js";
|
|
@@ -7,6 +8,8 @@ const DEFAULT_RESPONSE_MODEL = "ocuclaw-active-session";
|
|
|
7
8
|
const DEFAULT_TIMEOUT_MS = 60000;
|
|
8
9
|
const DEFAULT_MAX_BODY_BYTES = 65536;
|
|
9
10
|
const DEFAULT_DEDUP_WINDOW_MS = 500;
|
|
11
|
+
|
|
12
|
+
const DEFAULT_MAX_INTERCEPT_INFLIGHT = 4;
|
|
10
13
|
export const EVEN_AI_CHAT_COMPLETIONS_PATH = "/v1/chat/completions";
|
|
11
14
|
const REQUEST_HANDLED_MARKER = Symbol.for("ocuclaw.evenai.handled");
|
|
12
15
|
function normalizeLogger(logger) {
|
|
@@ -277,6 +280,10 @@ export function createEvenAiEndpoint(opts = {}) {
|
|
|
277
280
|
typeof opts.seedFastModeForRoute === "function"
|
|
278
281
|
? opts.seedFastModeForRoute
|
|
279
282
|
: null;
|
|
283
|
+
const resolveAgentForRoute =
|
|
284
|
+
typeof opts.resolveAgentForRoute === "function"
|
|
285
|
+
? opts.resolveAgentForRoute
|
|
286
|
+
: null;
|
|
280
287
|
const now =
|
|
281
288
|
typeof opts.now === "function" ? opts.now : () => Date.now();
|
|
282
289
|
const requestTimeoutMs = normalizePositiveInt(
|
|
@@ -291,6 +298,10 @@ export function createEvenAiEndpoint(opts = {}) {
|
|
|
291
298
|
0,
|
|
292
299
|
normalizePositiveInt(opts.dedupWindowMs, DEFAULT_DEDUP_WINDOW_MS),
|
|
293
300
|
);
|
|
301
|
+
const maxInterceptInflight = normalizePositiveInt(
|
|
302
|
+
opts.maxInterceptInflight,
|
|
303
|
+
DEFAULT_MAX_INTERCEPT_INFLIGHT,
|
|
304
|
+
);
|
|
294
305
|
|
|
295
306
|
if (!gatewayBridge || typeof gatewayBridge.sendMessage !== "function") {
|
|
296
307
|
throw new Error("Even AI endpoint requires gatewayBridge.sendMessage()");
|
|
@@ -310,9 +321,10 @@ export function createEvenAiEndpoint(opts = {}) {
|
|
|
310
321
|
throw new Error("Even AI endpoint requires runWaiter.waitForRun()");
|
|
311
322
|
}
|
|
312
323
|
|
|
313
|
-
/** @type {{requestId: string, fingerprint: string, sessionKey: string|null, startedAtMs: number}|null} */
|
|
314
324
|
let inFlight = null;
|
|
315
|
-
|
|
325
|
+
|
|
326
|
+
let interceptInflight = 0;
|
|
327
|
+
|
|
316
328
|
let lastAccepted = null;
|
|
317
329
|
|
|
318
330
|
async function handleRequest(req, res) {
|
|
@@ -346,7 +358,7 @@ export function createEvenAiEndpoint(opts = {}) {
|
|
|
346
358
|
}),
|
|
347
359
|
);
|
|
348
360
|
|
|
349
|
-
if (!token || authToken
|
|
361
|
+
if (!token || !constantTimeEqual(authToken, token)) {
|
|
350
362
|
emitDebug(
|
|
351
363
|
"evenai",
|
|
352
364
|
"request_auth_failed",
|
|
@@ -581,6 +593,29 @@ export function createEvenAiEndpoint(opts = {}) {
|
|
|
581
593
|
userText,
|
|
582
594
|
}),
|
|
583
595
|
) || "main";
|
|
596
|
+
|
|
597
|
+
if (interceptInflight >= maxInterceptInflight) {
|
|
598
|
+
emitDebug(
|
|
599
|
+
"evenai",
|
|
600
|
+
"listen_intercept_capacity_exceeded",
|
|
601
|
+
"warn",
|
|
602
|
+
{ sessionKey },
|
|
603
|
+
() => ({
|
|
604
|
+
requestId,
|
|
605
|
+
interceptInflight,
|
|
606
|
+
maxInterceptInflight,
|
|
607
|
+
}),
|
|
608
|
+
);
|
|
609
|
+
writeJson(
|
|
610
|
+
res,
|
|
611
|
+
buildListenInterceptCloseoutPayload({
|
|
612
|
+
id: requestId,
|
|
613
|
+
createdMs: startedAtMs,
|
|
614
|
+
model: responseModel,
|
|
615
|
+
}),
|
|
616
|
+
);
|
|
617
|
+
return true;
|
|
618
|
+
}
|
|
584
619
|
lastAccepted = {
|
|
585
620
|
fingerprint,
|
|
586
621
|
startedAtMs,
|
|
@@ -600,6 +635,7 @@ export function createEvenAiEndpoint(opts = {}) {
|
|
|
600
635
|
}),
|
|
601
636
|
);
|
|
602
637
|
|
|
638
|
+
interceptInflight += 1;
|
|
603
639
|
void (async () => {
|
|
604
640
|
try {
|
|
605
641
|
const dispatchResult = await Promise.resolve(
|
|
@@ -610,6 +646,12 @@ export function createEvenAiEndpoint(opts = {}) {
|
|
|
610
646
|
source: "hybrid_voice_endpoint",
|
|
611
647
|
}),
|
|
612
648
|
);
|
|
649
|
+
const dispatchRunId =
|
|
650
|
+
dispatchResult &&
|
|
651
|
+
typeof dispatchResult.runId === "string" &&
|
|
652
|
+
dispatchResult.runId.trim()
|
|
653
|
+
? dispatchResult.runId.trim()
|
|
654
|
+
: null;
|
|
613
655
|
if (emitListenInterceptBroadcast) {
|
|
614
656
|
try {
|
|
615
657
|
emitListenInterceptBroadcast({ sessionKey });
|
|
@@ -625,12 +667,7 @@ export function createEvenAiEndpoint(opts = {}) {
|
|
|
625
667
|
"info",
|
|
626
668
|
{
|
|
627
669
|
sessionKey,
|
|
628
|
-
runId:
|
|
629
|
-
dispatchResult &&
|
|
630
|
-
typeof dispatchResult.runId === "string" &&
|
|
631
|
-
dispatchResult.runId.trim()
|
|
632
|
-
? dispatchResult.runId.trim()
|
|
633
|
-
: undefined,
|
|
670
|
+
runId: dispatchRunId || undefined,
|
|
634
671
|
},
|
|
635
672
|
() => ({
|
|
636
673
|
requestId,
|
|
@@ -643,6 +680,16 @@ export function createEvenAiEndpoint(opts = {}) {
|
|
|
643
680
|
: null,
|
|
644
681
|
}),
|
|
645
682
|
);
|
|
683
|
+
|
|
684
|
+
if (dispatchRunId) {
|
|
685
|
+
try {
|
|
686
|
+
await runWaiter.waitForRun({
|
|
687
|
+
runId: dispatchRunId,
|
|
688
|
+
sessionKey,
|
|
689
|
+
timeoutMs: requestTimeoutMs,
|
|
690
|
+
});
|
|
691
|
+
} catch (_) {}
|
|
692
|
+
}
|
|
646
693
|
} catch (err) {
|
|
647
694
|
let cleanupEmitted = false;
|
|
648
695
|
let cleanupConnectedAppClients = null;
|
|
@@ -687,6 +734,8 @@ export function createEvenAiEndpoint(opts = {}) {
|
|
|
687
734
|
cleanupError && cleanupError.message ? cleanupError.message : null,
|
|
688
735
|
}),
|
|
689
736
|
);
|
|
737
|
+
} finally {
|
|
738
|
+
interceptInflight -= 1;
|
|
690
739
|
}
|
|
691
740
|
})();
|
|
692
741
|
|
|
@@ -789,12 +838,6 @@ export function createEvenAiEndpoint(opts = {}) {
|
|
|
789
838
|
startedAtMs,
|
|
790
839
|
};
|
|
791
840
|
|
|
792
|
-
// Track the upstream runId so the disconnect handler can cancel the
|
|
793
|
-
// pending wait without having to wait for the full request timeout.
|
|
794
|
-
// `clientDisconnected` covers the pre-ack window: if the socket closes
|
|
795
|
-
// before sendMessage() resolves, activeRunId is still null so cancelRun
|
|
796
|
-
// is unreachable; we record the flag and short-circuit waitForRun once
|
|
797
|
-
// the ack arrives.
|
|
798
841
|
let activeRunId = null;
|
|
799
842
|
let clientDisconnected = false;
|
|
800
843
|
const onClientDisconnect = () => {
|
|
@@ -818,16 +861,11 @@ export function createEvenAiEndpoint(opts = {}) {
|
|
|
818
861
|
try {
|
|
819
862
|
runWaiter.cancelRun(activeRunId, "client_disconnect");
|
|
820
863
|
} catch (_err) {
|
|
821
|
-
|
|
822
|
-
// server-side because no gateway abort RPC exists.
|
|
864
|
+
|
|
823
865
|
}
|
|
824
866
|
}
|
|
825
867
|
};
|
|
826
|
-
|
|
827
|
-
// the client disconnects mid-flight. We discriminate via
|
|
828
|
-
// res.writableEnded inside the handler. (req's own 'close' is not
|
|
829
|
-
// reliable for premature client aborts in Node 22 — the socket-level
|
|
830
|
-
// close is what fires.)
|
|
868
|
+
|
|
831
869
|
res.once("close", onClientDisconnect);
|
|
832
870
|
|
|
833
871
|
emitDebug(
|
|
@@ -867,14 +905,29 @@ export function createEvenAiEndpoint(opts = {}) {
|
|
|
867
905
|
seedFastModeForRoute({ route, sessionKey, routingMode }),
|
|
868
906
|
);
|
|
869
907
|
} catch (err) {
|
|
870
|
-
|
|
871
|
-
// without fast mode.
|
|
908
|
+
|
|
872
909
|
emitDebug("evenai", "fast_mode_seed_failed", "warn", { sessionKey }, () => ({
|
|
873
910
|
requestId,
|
|
874
911
|
message: err && err.message ? err.message : String(err),
|
|
875
912
|
}));
|
|
876
913
|
}
|
|
877
914
|
}
|
|
915
|
+
if (resolveAgentForRoute) {
|
|
916
|
+
try {
|
|
917
|
+
const agentId = await Promise.resolve(
|
|
918
|
+
resolveAgentForRoute({ route, sessionKey, routingMode }),
|
|
919
|
+
);
|
|
920
|
+
if (typeof agentId === "string" && agentId.trim()) {
|
|
921
|
+
sendOptions.agentId = agentId.trim();
|
|
922
|
+
}
|
|
923
|
+
} catch (err) {
|
|
924
|
+
|
|
925
|
+
emitDebug("evenai", "agent_resolve_failed", "warn", { sessionKey }, () => ({
|
|
926
|
+
requestId,
|
|
927
|
+
message: err && err.message ? err.message : String(err),
|
|
928
|
+
}));
|
|
929
|
+
}
|
|
930
|
+
}
|
|
878
931
|
const ack = await promiseWithTimeout(
|
|
879
932
|
gatewayBridge.sendMessage(
|
|
880
933
|
userText,
|
|
@@ -81,7 +81,6 @@ export function createEvenAiRunWaiter(opts = {}) {
|
|
|
81
81
|
const clearTimeoutFn =
|
|
82
82
|
typeof opts.clearTimeout === "function" ? opts.clearTimeout : clearTimeout;
|
|
83
83
|
|
|
84
|
-
/** @type {Map<string, {resolve: Function, reject: Function, sessionKey: string|null, timer: any, timeoutMs: number|null}>} */
|
|
85
84
|
const pendingRuns = new Map();
|
|
86
85
|
|
|
87
86
|
function cleanupPending(runId) {
|
|
@@ -83,6 +83,10 @@ export function normalizeEvenAiListenEnabled(value) {
|
|
|
83
83
|
return value === true;
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
+
export function normalizeEvenAiDefaultAgent(value) {
|
|
87
|
+
return normalizeTrimmedString(value);
|
|
88
|
+
}
|
|
89
|
+
|
|
86
90
|
function normalizeTrackedThrowawayKeys(value) {
|
|
87
91
|
if (!Array.isArray(value)) {
|
|
88
92
|
return [];
|
|
@@ -137,6 +141,9 @@ function isStoredSnapshotCanonical(value, snapshot) {
|
|
|
137
141
|
if (normalizeEvenAiDefaultFastMode(value.defaultFastMode) !== snapshot.defaultFastMode) {
|
|
138
142
|
return false;
|
|
139
143
|
}
|
|
144
|
+
if (normalizeEvenAiDefaultAgent(value.defaultAgent) !== snapshot.defaultAgent) {
|
|
145
|
+
return false;
|
|
146
|
+
}
|
|
140
147
|
if (!Array.isArray(value.trackedThrowawayKeys)) {
|
|
141
148
|
return snapshot.trackedThrowawayKeys.length === 0;
|
|
142
149
|
}
|
|
@@ -155,6 +162,7 @@ export function normalizeEvenAiSettingsSnapshot(value = {}) {
|
|
|
155
162
|
defaultThinking: normalizeEvenAiDefaultThinking(value.defaultThinking),
|
|
156
163
|
listenEnabled: normalizeEvenAiListenEnabled(value.listenEnabled),
|
|
157
164
|
defaultFastMode: normalizeEvenAiDefaultFastMode(value.defaultFastMode),
|
|
165
|
+
defaultAgent: normalizeEvenAiDefaultAgent(value.defaultAgent),
|
|
158
166
|
trackedThrowawayKeys: normalizeTrackedThrowawayKeys(value.trackedThrowawayKeys),
|
|
159
167
|
};
|
|
160
168
|
}
|
|
@@ -364,6 +372,9 @@ export function createEvenAiSettingsStore(opts = {}) {
|
|
|
364
372
|
defaultFastMode: hasOwn(patch, "defaultFastMode")
|
|
365
373
|
? normalizeEvenAiDefaultFastMode(patch.defaultFastMode)
|
|
366
374
|
: snapshot.defaultFastMode,
|
|
375
|
+
defaultAgent: hasOwn(patch, "defaultAgent")
|
|
376
|
+
? normalizeEvenAiDefaultAgent(patch.defaultAgent)
|
|
377
|
+
: snapshot.defaultAgent,
|
|
367
378
|
trackedThrowawayKeys: [...snapshot.trackedThrowawayKeys],
|
|
368
379
|
};
|
|
369
380
|
snapshot = next;
|
|
@@ -61,6 +61,14 @@ function buildAgentRequestParams(
|
|
|
61
61
|
params.thinking = thinking;
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
+
const agentId =
|
|
65
|
+
requestOptions && typeof requestOptions.agentId === "string"
|
|
66
|
+
? requestOptions.agentId.trim()
|
|
67
|
+
: "";
|
|
68
|
+
if (agentId) {
|
|
69
|
+
params.agentId = agentId;
|
|
70
|
+
}
|
|
71
|
+
|
|
64
72
|
if (
|
|
65
73
|
attachment &&
|
|
66
74
|
typeof attachment === "object" &&
|
|
@@ -93,15 +101,6 @@ function buildAgentRequestParams(
|
|
|
93
101
|
return params;
|
|
94
102
|
}
|
|
95
103
|
|
|
96
|
-
/**
|
|
97
|
-
* Bridge the relay runtime to OpenClaw structured RPC method calls.
|
|
98
|
-
*
|
|
99
|
-
* This keeps relay-facing bridge semantics unchanged while switching
|
|
100
|
-
* send/approval operations to request-based structured RPC calls.
|
|
101
|
-
*
|
|
102
|
-
* @param {{openclawClient: object, idempotencyKeyFactory?: () => string}} opts
|
|
103
|
-
* @returns {object}
|
|
104
|
-
*/
|
|
105
104
|
function createPluginRpcGatewayBridge(opts) {
|
|
106
105
|
const openclawClient = opts && opts.openclawClient;
|
|
107
106
|
const idempotencyKeyFactory =
|
|
@@ -75,6 +75,7 @@ function sanitizeDiagnostic(args) {
|
|
|
75
75
|
pickBoolean(diagnostic.hasAttachment) ?? attachmentSummary.hasAttachment;
|
|
76
76
|
return {
|
|
77
77
|
messageId: pickId(diagnostic.messageId ?? args.messageId),
|
|
78
|
+
sessionKey: pickId(diagnostic.sessionKey ?? args.sessionKey),
|
|
78
79
|
source: pickId(diagnostic.source ?? args.source),
|
|
79
80
|
textChars,
|
|
80
81
|
hasAttachment,
|
|
@@ -160,10 +161,6 @@ export function createGatewayTimingLedger(opts = {}) {
|
|
|
160
161
|
clearTimer(ref);
|
|
161
162
|
}
|
|
162
163
|
|
|
163
|
-
// Diagnostic timers must never keep the host process alive: a bare
|
|
164
|
-
// 120s ttlTimer kept `node tests/relay.test.js` idling ~120s after the
|
|
165
|
-
// suite finished. Injected fake timers (numeric ids) have no unref —
|
|
166
|
-
// guard for it.
|
|
167
164
|
function armDiagnosticTimer(fn, delayMs) {
|
|
168
165
|
const timer = setTimer(fn, delayMs);
|
|
169
166
|
if (timer && typeof timer.unref === "function") {
|
|
@@ -347,7 +344,9 @@ export function createGatewayTimingLedger(opts = {}) {
|
|
|
347
344
|
method: pickId(args.method) || "unknown",
|
|
348
345
|
expectFinal: args.expectFinal === true,
|
|
349
346
|
sessionKey: pickId(
|
|
350
|
-
|
|
347
|
+
diagnostic.sessionKey ??
|
|
348
|
+
args.sessionKey ??
|
|
349
|
+
(isObject(args.params) ? args.params.sessionKey : null),
|
|
351
350
|
),
|
|
352
351
|
messageId: diagnostic.messageId,
|
|
353
352
|
source: diagnostic.source,
|
|
@@ -419,7 +418,10 @@ export function createGatewayTimingLedger(opts = {}) {
|
|
|
419
418
|
data,
|
|
420
419
|
);
|
|
421
420
|
|
|
422
|
-
|
|
421
|
+
const acceptedAgentRun =
|
|
422
|
+
(method === "agent" && status === "accepted") ||
|
|
423
|
+
(method === "sessions.steer" && (status === "started" || status === "accepted"));
|
|
424
|
+
if (ok && acceptedAgentRun && runId) {
|
|
423
425
|
const run = {
|
|
424
426
|
runId,
|
|
425
427
|
requestId,
|