ocuclaw 1.3.2 → 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.
Files changed (84) hide show
  1. package/README.md +29 -1
  2. package/dist/config/runtime-config-session-title-model.test.js +0 -3
  3. package/dist/config/runtime-config.js +22 -33
  4. package/dist/domain/activity-status-adapter.js +0 -7
  5. package/dist/domain/activity-status-arbiter.js +3 -27
  6. package/dist/domain/activity-status-labels.js +8 -38
  7. package/dist/domain/code-span-regions.js +4 -24
  8. package/dist/domain/constant-time-equal.js +9 -0
  9. package/dist/domain/constant-time-equal.test.js +28 -0
  10. package/dist/domain/conversation-state.js +27 -138
  11. package/dist/domain/debug-bundle-cache.js +52 -0
  12. package/dist/domain/debug-bundle-format.js +60 -0
  13. package/dist/domain/debug-bundle-preview.js +123 -0
  14. package/dist/domain/debug-bundle-redaction.js +182 -0
  15. package/dist/domain/debug-bundle-save.js +11 -0
  16. package/dist/domain/debug-bundle-zip.js +15 -0
  17. package/dist/domain/debug-bundle.js +97 -0
  18. package/dist/domain/debug-store.js +6 -17
  19. package/dist/domain/debug-upload-preset.js +27 -0
  20. package/dist/domain/glasses-display-system-prompt.js +0 -5
  21. package/dist/domain/glasses-display-system-prompt.test.js +1 -1
  22. package/dist/domain/glasses-ui-content-summary.js +0 -6
  23. package/dist/domain/glasses-ui-system-prompt.test.js +1 -2
  24. package/dist/domain/message-emoji-allowlist.js +0 -7
  25. package/dist/domain/message-emoji-filter.js +3 -9
  26. package/dist/domain/neural-emoji-reactor-tag-config.js +3 -3
  27. package/dist/domain/prompt-channel-fragments.js +1 -10
  28. package/dist/domain/tagged-span-parser.js +3 -26
  29. package/dist/domain/tagged-span-strip.js +0 -7
  30. package/dist/even-ai/even-ai-endpoint.js +77 -24
  31. package/dist/even-ai/even-ai-run-waiter.js +0 -1
  32. package/dist/even-ai/even-ai-settings-store.js +11 -0
  33. package/dist/gateway/gateway-bridge.js +8 -9
  34. package/dist/gateway/gateway-timing-ledger.js +8 -6
  35. package/dist/gateway/openclaw-client.js +97 -297
  36. package/dist/gateway/sanitize-connect-reason.js +10 -0
  37. package/dist/gateway/sanitize-connect-reason.test.js +34 -0
  38. package/dist/index.js +3 -3
  39. package/dist/runtime/channel-two-hook.js +1 -6
  40. package/dist/runtime/container-env.js +1 -5
  41. package/dist/runtime/debug-bundle-handler.js +159 -0
  42. package/dist/runtime/display-toggle-states.js +6 -17
  43. package/dist/runtime/downstream-handler.js +682 -508
  44. package/dist/runtime/glasses-backpressure-latch.js +93 -0
  45. package/dist/runtime/ocuclaw-settings-store.js +10 -1
  46. package/dist/runtime/openclaw-host-version.js +5 -0
  47. package/dist/runtime/plugin-version-service.js +13 -6
  48. package/dist/runtime/provider-usage-select.js +0 -6
  49. package/dist/runtime/register-session-title-distiller.js +14 -16
  50. package/dist/runtime/relay-core.js +657 -271
  51. package/dist/runtime/relay-service.js +40 -36
  52. package/dist/runtime/relay-worker-approval-replay-cache.js +1 -1
  53. package/dist/runtime/relay-worker-entry.js +1 -2
  54. package/dist/runtime/relay-worker-health.js +2 -10
  55. package/dist/runtime/relay-worker-protocol.js +6 -1
  56. package/dist/runtime/relay-worker-supervisor.js +109 -39
  57. package/dist/runtime/relay-worker-transport.js +157 -15
  58. package/dist/runtime/session-context-service.js +5 -45
  59. package/dist/runtime/session-service.js +157 -175
  60. package/dist/runtime/session-title-distiller-budget.js +1 -5
  61. package/dist/runtime/session-title-distiller-helpers.js +14 -24
  62. package/dist/runtime/session-title-distiller.js +109 -122
  63. package/dist/runtime/session-title-record.js +0 -6
  64. package/dist/runtime/stable-prompt-snapshot.js +3 -14
  65. package/dist/runtime/upstream-runtime.js +600 -103
  66. package/dist/tools/device-info-tool.js +4 -21
  67. package/dist/tools/glasses-ui-cron.js +58 -63
  68. package/dist/tools/glasses-ui-descriptors.js +4 -33
  69. package/dist/tools/glasses-ui-limits.js +0 -13
  70. package/dist/tools/glasses-ui-paint-floor.js +22 -34
  71. package/dist/tools/glasses-ui-recipes.js +92 -101
  72. package/dist/tools/glasses-ui-surfaces.js +295 -100
  73. package/dist/tools/glasses-ui-template.js +7 -22
  74. package/dist/tools/glasses-ui-tool-description.test.js +2 -2
  75. package/dist/tools/glasses-ui-tool.js +475 -331
  76. package/dist/tools/glasses-ui-voicemail.js +242 -0
  77. package/dist/tools/glasses-ui-wake.js +195 -0
  78. package/dist/tools/session-title-tool.js +2 -7
  79. package/dist/tools/session-title-tool.test.js +1 -1
  80. package/dist/version.js +3 -2
  81. package/openclaw.plugin.json +60 -13
  82. package/package.json +3 -2
  83. package/skills/glasses-ui/SKILL.md +19 -3
  84. package/dist/runtime/protocol-adapter.js +0 -387
@@ -0,0 +1,93 @@
1
+ const DEFAULT_RECOVERED_HOLD_MS = 3_000;
2
+ const DEFAULT_STALE_MS = 5_000;
3
+
4
+ export function createGlassesBackpressureLatch(options = {}) {
5
+ const now = typeof options.now === "function" ? options.now : Date.now;
6
+ const recoveredHoldMs = Number.isFinite(options.recoveredHoldMs)
7
+ ? options.recoveredHoldMs
8
+ : DEFAULT_RECOVERED_HOLD_MS;
9
+ const staleMs = Number.isFinite(options.staleMs) ? options.staleMs : DEFAULT_STALE_MS;
10
+ const emitDebug = typeof options.emitDebug === "function" ? options.emitDebug : () => {};
11
+
12
+ let latched = false;
13
+ let latchedAtMs = null;
14
+ let lastOverAtMs = null;
15
+ let lastReportAtMs = null;
16
+ let workerEpoch = null;
17
+
18
+ function clearState() {
19
+ latched = false;
20
+ latchedAtMs = null;
21
+ lastOverAtMs = null;
22
+ lastReportAtMs = null;
23
+ }
24
+
25
+ function emitTransition(nextLatched, reason, atMs) {
26
+ if (nextLatched === latched) return;
27
+ if (nextLatched) {
28
+ latchedAtMs = atMs;
29
+ emitDebug("glasses_backpressure_latched", "warn", { reason });
30
+ } else {
31
+ emitDebug("glasses_backpressure_cleared", "info", {
32
+ reason,
33
+ latchedForMs: latchedAtMs === null ? null : Math.max(0, atMs - latchedAtMs),
34
+ });
35
+ latchedAtMs = null;
36
+ }
37
+ latched = nextLatched;
38
+ }
39
+
40
+ function evaluate(atMs) {
41
+ if (!latched) return;
42
+ if (lastReportAtMs !== null && atMs - lastReportAtMs > staleMs) {
43
+ emitTransition(false, "stale_reports", atMs);
44
+ return;
45
+ }
46
+ if (
47
+ lastOverAtMs !== null &&
48
+ atMs - lastOverAtMs >= recoveredHoldMs &&
49
+ lastReportAtMs !== null &&
50
+ lastReportAtMs > lastOverAtMs
51
+ ) {
52
+
53
+ emitTransition(false, "recovered", atMs);
54
+ }
55
+ }
56
+
57
+ function report(params) {
58
+ const atMs = now();
59
+ const count =
60
+ params && Number.isFinite(params.sendBufferHighWaterClients)
61
+ ? params.sendBufferHighWaterClients
62
+ : null;
63
+ if (count === null) return;
64
+ const epoch = params && Number.isFinite(params.workerEpoch) ? params.workerEpoch : null;
65
+ if (epoch !== null && workerEpoch !== null && epoch !== workerEpoch) {
66
+
67
+ clearState();
68
+ }
69
+ if (epoch !== null) workerEpoch = epoch;
70
+ lastReportAtMs = atMs;
71
+ if (count >= 1) {
72
+ lastOverAtMs = atMs;
73
+ emitTransition(true, "over_high_water", atMs);
74
+ return;
75
+ }
76
+ evaluate(atMs);
77
+ }
78
+
79
+ function isOverHighWater() {
80
+ evaluate(now());
81
+ return latched;
82
+ }
83
+
84
+ function reset(reason) {
85
+ const atMs = now();
86
+ emitTransition(false, typeof reason === "string" ? reason : "reset", atMs);
87
+ clearState();
88
+ }
89
+
90
+ return { report, isOverHighWater, reset };
91
+ }
92
+
93
+ export default { createGlassesBackpressureLatch };
@@ -53,6 +53,10 @@ export function normalizeOcuClawDefaultFastMode(value) {
53
53
  return value === true;
54
54
  }
55
55
 
56
+ export function normalizeOcuClawDefaultAgent(value) {
57
+ return normalizeTrimmedString(value);
58
+ }
59
+
56
60
  function isStoredSnapshotCanonical(value, snapshot) {
57
61
  if (!value || typeof value !== "object") {
58
62
  return false;
@@ -61,7 +65,8 @@ function isStoredSnapshotCanonical(value, snapshot) {
61
65
  normalizeTrimmedString(value.systemPrompt) === snapshot.systemPrompt &&
62
66
  normalizeTrimmedString(value.defaultModel) === snapshot.defaultModel &&
63
67
  normalizeOcuClawDefaultThinking(value.defaultThinking) === snapshot.defaultThinking &&
64
- normalizeOcuClawDefaultFastMode(value.defaultFastMode) === snapshot.defaultFastMode
68
+ normalizeOcuClawDefaultFastMode(value.defaultFastMode) === snapshot.defaultFastMode &&
69
+ normalizeOcuClawDefaultAgent(value.defaultAgent) === snapshot.defaultAgent
65
70
  );
66
71
  }
67
72
 
@@ -71,6 +76,7 @@ export function normalizeOcuClawSettingsSnapshot(value = {}) {
71
76
  defaultModel: normalizeOcuClawDefaultModel(value.defaultModel),
72
77
  defaultThinking: normalizeOcuClawDefaultThinking(value.defaultThinking),
73
78
  defaultFastMode: normalizeOcuClawDefaultFastMode(value.defaultFastMode),
79
+ defaultAgent: normalizeOcuClawDefaultAgent(value.defaultAgent),
74
80
  };
75
81
  }
76
82
 
@@ -266,6 +272,9 @@ export function createOcuClawSettingsStore(opts = {}) {
266
272
  defaultFastMode: hasOwn(patch, "defaultFastMode")
267
273
  ? normalizeOcuClawDefaultFastMode(patch.defaultFastMode)
268
274
  : snapshot.defaultFastMode,
275
+ defaultAgent: hasOwn(patch, "defaultAgent")
276
+ ? normalizeOcuClawDefaultAgent(patch.defaultAgent)
277
+ : snapshot.defaultAgent,
269
278
  };
270
279
  snapshot = next;
271
280
  persistSnapshot(snapshot, "set_settings");
@@ -0,0 +1,5 @@
1
+ export function readOpenClawHostVersion(env = process.env) {
2
+ const raw = env ? env.OPENCLAW_SERVICE_VERSION || env.OPENCLAW_VERSION : undefined;
3
+ const trimmed = typeof raw === "string" ? raw.trim() : "";
4
+ return trimmed.length > 0 ? trimmed : null;
5
+ }
@@ -1,9 +1,6 @@
1
- import { PLUGIN_VERSION, REQUIRES_CLIENT_VERSION } from "../version.js";
1
+ import { PLUGIN_VERSION, REQUIRES_CLIENT_VERSION, BUILD_INPUT_HASH } from "../version.js";
2
+ import { readOpenClawHostVersion } from "./openclaw-host-version.js";
2
3
 
3
- /**
4
- * Plugin version provider. Exposes the build-time version constants used by the
5
- * relay handshake. No process execution.
6
- */
7
4
  function createPluginVersionService() {
8
5
  function getPluginVersion() {
9
6
  return typeof PLUGIN_VERSION === "string" && PLUGIN_VERSION.length > 0
@@ -17,7 +14,17 @@ function createPluginVersionService() {
17
14
  : null;
18
15
  }
19
16
 
20
- return { getPluginVersion, getRequiresClientVersion };
17
+ function getOpenClawHostVersion() {
18
+ return readOpenClawHostVersion();
19
+ }
20
+
21
+ function getDistHash() {
22
+ return typeof BUILD_INPUT_HASH === "string" && BUILD_INPUT_HASH.length > 0
23
+ ? BUILD_INPUT_HASH
24
+ : null;
25
+ }
26
+
27
+ return { getPluginVersion, getRequiresClientVersion, getOpenClawHostVersion, getDistHash };
21
28
  }
22
29
 
23
30
  export { createPluginVersionService };
@@ -85,12 +85,6 @@ export function selectProviderUsageSnapshot(summary, opts = {}) {
85
85
  (entry) => entry.provider.trim().toLowerCase() === activeProvider,
86
86
  );
87
87
 
88
- // Family fallback: the session model config reports the base provider id
89
- // (e.g. "openai") while the usage summary keys the same usage by its
90
- // sub-provider source (e.g. "openai-codex"). When there is no exact match
91
- // but exactly ONE summary entry belongs to the active provider's family
92
- // ("${activeProvider}-…"), resolve it. Restricted to a single family member
93
- // to avoid ambiguous attribution when multiple sub-providers exist.
94
88
  if (!match) {
95
89
  const familyMatches = namedEntries.filter((entry) =>
96
90
  entry.provider.trim().toLowerCase().startsWith(`${activeProvider}-`),
@@ -10,10 +10,7 @@ function genRunId() {
10
10
 
11
11
  export function registerSessionTitleDistiller(api, service) {
12
12
  if (!api || typeof api.on !== "function") return () => {};
13
- // The budget lives in sessionService (so a logical reset clears it with the
14
- // title record + toggle state). Resolve it lazily per call: at registration
15
- // the live relay may not exist yet, and a sibling context resolves the shared
16
- // relay's budget.
13
+
17
14
  const budget = {
18
15
  canRun: (k) => {
19
16
  const b = service.getDistillerBudget();
@@ -29,8 +26,7 @@ export function registerSessionTitleDistiller(api, service) {
29
26
  },
30
27
  };
31
28
  const distiller = createSessionTitleDistiller({
32
- // Lazy getter: the real state dir only arrives at service.start(), after
33
- // this distiller is constructed during registration.
29
+
34
30
  getStateDir: () => (service.getStateDir ? service.getStateDir() : undefined),
35
31
  nowMs: () => Date.now(),
36
32
  genId: genRunId,
@@ -49,6 +45,12 @@ export function registerSessionTitleDistiller(api, service) {
49
45
  typeof service.deleteDistillerSession === "function"
50
46
  ? service.deleteDistillerSession(k)
51
47
  : Promise.resolve(null),
48
+
49
+ llmComplete: (() => {
50
+ const llm = api && api.runtime && api.runtime.llm;
51
+ if (!llm || typeof llm.complete !== "function") return undefined;
52
+ return (params) => llm.complete(params);
53
+ })(),
52
54
  subagentRuntime: (() => {
53
55
  const sa = api && api.runtime && api.runtime.subagent;
54
56
  if (
@@ -76,24 +78,20 @@ export function registerSessionTitleDistiller(api, service) {
76
78
  });
77
79
 
78
80
  return api.on("agent_end", (event, ctx) => {
79
- // 2026.6.x delivers the canonical `agent:<id>:<key>` form; relay state is
80
- // keyed by the bare relay key — normalize at the boundary.
81
+
81
82
  const rawSessionKey = ctx && typeof ctx.sessionKey === "string" ? ctx.sessionKey : null;
82
83
  const sessionKey = stripAgentSessionPrefix(rawSessionKey);
83
84
  if (!sessionKey) return;
84
- // Capture the completed run's messages NOW, before the fire-and-forget run
85
- // defers: agent_end carries the run's final messages (the authoritative
86
- // source for THIS session). Fall back to a synchronous snapshot of the live
87
- // conversation state. Either way the messages are pinned here so a later
88
- // session switch / clear can't make the distiller title from another
89
- // session's transcript.
85
+
86
+ const agentId = ctx && typeof ctx.agentId === "string" && ctx.agentId.trim() ? ctx.agentId.trim() : undefined;
87
+
90
88
  const eventMessages = event && Array.isArray(event.messages) ? event.messages : null;
91
89
  const messages =
92
90
  eventMessages && eventMessages.length
93
91
  ? eventMessages
94
92
  : service.getRawMessages();
95
- // Fire-and-forget; never block run teardown.
96
- Promise.resolve(distiller.maybeRun(sessionKey, { messages })).catch(() => {});
93
+
94
+ Promise.resolve(distiller.maybeRun(sessionKey, { messages, agentId })).catch(() => {});
97
95
  });
98
96
  }
99
97