@better_openclaw/betterclaw 3.0.3 → 3.0.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@better_openclaw/betterclaw",
3
- "version": "3.0.3",
3
+ "version": "3.0.4",
4
4
  "description": "Intelligent event filtering, context tracking, and proactive triggers for BetterClaw",
5
5
  "license": "AGPL-3.0-only",
6
6
  "repository": {
@@ -1,12 +1,39 @@
1
1
  import type { ContextManager } from "../context.js";
2
2
  import { loadTriageProfile } from "../learner.js";
3
3
 
4
+ const STALE_THRESHOLD_S = 600; // 10 minutes
5
+
6
+ /** Format seconds into human-readable age string */
7
+ function formatAge(seconds: number): string {
8
+ if (seconds < 60) return `${Math.round(seconds)}s ago`;
9
+ if (seconds < 3600) return `${Math.round(seconds / 60)}m ago`;
10
+ if (seconds < 86400) return `${Math.round(seconds / 3600)}h ago`;
11
+ return `${Math.round(seconds / 86400)}d ago`;
12
+ }
13
+
14
+ /**
15
+ * On premium, stale device data (>10 min) is replaced with a pointer
16
+ * to the fresh node command. The agent can't shortcut to stale values.
17
+ */
18
+ function deviceFieldOrPointer(
19
+ data: Record<string, unknown> | null,
20
+ ageSeconds: number | null,
21
+ freshCommand: string,
22
+ isPremium: boolean,
23
+ ): Record<string, unknown> | null {
24
+ if (!data) return null;
25
+ if (isPremium && ageSeconds != null && ageSeconds > STALE_THRESHOLD_S) {
26
+ return { stale: true, ageHuman: formatAge(ageSeconds), freshCommand };
27
+ }
28
+ return { ...data, dataAgeSeconds: ageSeconds };
29
+ }
30
+
4
31
  export function createGetContextTool(ctx: ContextManager, stateDir?: string) {
5
32
  return {
6
33
  name: "get_context",
7
34
  label: "Get Device Context",
8
35
  description:
9
- "Get BetterClaw context — patterns, trends, activity zone, event history, and cached device snapshots with staleness indicators. On premium, node commands return fresher data for current readings. On free, this includes the latest device snapshot.",
36
+ "Get BetterClaw context — patterns, trends, activity zone, and event history. On premium, stale device readings (>10 min) are hidden use node commands (location.get, device.battery, health.*) for current data. On free, this includes the full device snapshot.",
10
37
  parameters: {},
11
38
  async execute(_id: string, _params: Record<string, unknown>) {
12
39
  const state = ctx.get();
@@ -14,28 +41,37 @@ export function createGetContextTool(ctx: ContextManager, stateDir?: string) {
14
41
  const patterns = await ctx.readPatterns();
15
42
  const dataAge = ctx.getDataAge();
16
43
 
17
- const isPremium = runtime.tier === "premium";
44
+ const isPremium = runtime.tier === "premium" || runtime.tier === "premium+";
18
45
 
19
46
  const result: Record<string, unknown> = {
20
47
  tierHint: {
21
48
  tier: runtime.tier,
22
49
  note: isPremium
23
- ? "Node commands available for fresh readings (location.get, device.battery, health.*)"
50
+ ? "Node commands available for fresh readings (location.get, device.battery, health.*). Stale device data is hidden — call the node command instead."
24
51
  : "This is the only data source on free tier — check dataAgeSeconds for freshness",
25
52
  },
26
53
  smartMode: runtime.smartMode,
27
54
  };
28
55
 
29
56
  result.device = {
30
- battery: state.device.battery
31
- ? { ...state.device.battery, updatedAt: ctx.getTimestamp("battery"), dataAgeSeconds: dataAge.battery }
32
- : null,
33
- location: state.device.location
34
- ? { ...state.device.location, updatedAt: ctx.getTimestamp("location"), dataAgeSeconds: dataAge.location }
35
- : null,
36
- health: state.device.health
37
- ? { ...state.device.health, updatedAt: ctx.getTimestamp("health"), dataAgeSeconds: dataAge.health }
38
- : null,
57
+ battery: deviceFieldOrPointer(
58
+ state.device.battery as unknown as Record<string, unknown>,
59
+ dataAge.battery,
60
+ "device.battery",
61
+ isPremium,
62
+ ),
63
+ location: deviceFieldOrPointer(
64
+ state.device.location as unknown as Record<string, unknown>,
65
+ dataAge.location,
66
+ "location.get",
67
+ isPremium,
68
+ ),
69
+ health: deviceFieldOrPointer(
70
+ state.device.health as unknown as Record<string, unknown>,
71
+ dataAge.health,
72
+ "health.summary",
73
+ isPremium,
74
+ ),
39
75
  };
40
76
 
41
77
  result.activity = { ...state.activity, updatedAt: ctx.getTimestamp("activity") };