oomi-ai 0.3.3 → 0.3.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -150,6 +150,7 @@ Optional fields:
150
150
  Component-composed persona apps are the default Oomi mini-app path. Use these commands when the user asks about or updates an existing Oomi persona app such as Fitness Today:
151
151
 
152
152
  ```bash
153
+ oomi fitness show --json
153
154
  oomi persona-apps list --json
154
155
  oomi persona-apps show fitness-today --json
155
156
  oomi persona-apps apply-action fitness-today --action fitness.complete_workout --payload-json '{"goalId":"easy-run","minutes":20}' --json
@@ -158,17 +159,21 @@ oomi persona-apps apply-action fitness-today --action fitness.complete_workout -
158
159
  Rules for agents:
159
160
 
160
161
  - Answer from returned `appState.bindings`, not from memory.
162
+ - For Fitness questions, prefer `oomi fitness show --json` because it returns the Fitness mini-app state and approved health context together.
161
163
  - Apply updates only through approved `persona-apps apply-action` actions.
162
164
  - If the persona app does not exist, tell the user to add it from the Oomi client first.
163
165
  - Do not create local persona UI projects.
164
166
 
165
- For the Fitness v0 slice:
167
+ For the Fitness slice:
166
168
 
167
169
  ```bash
170
+ oomi persona-apps apply-action fitness-today --action fitness.set_goal --payload-json '{"goalId":"move_more"}' --json
168
171
  oomi persona-apps apply-action fitness-today --action fitness.complete_goal --payload-json '{"goalId":"easy-run"}' --json
169
172
  oomi persona-apps apply-action fitness-today --action fitness.complete_workout --payload-json '{"goalId":"easy-run","minutes":20}' --json
170
173
  ```
171
174
 
175
+ Supported `fitness.set_goal` values are `move_more`, `sleep_better`, and `build_workout_habit`.
176
+
172
177
  ## Context Tools
173
178
 
174
179
  Use context tools when the user asks a data-backed question that should come from Oomi-approved mobile permissions, not model memory. For health and fitness questions, read the latest backend HealthKit context first:
@@ -181,9 +186,10 @@ Rules for agents:
181
186
 
182
187
  - Answer from `healthContext.summary` and `healthContext.derived`.
183
188
  - Respect `healthContext.agentGuidance.canAnswerFromContext`.
184
- - If context is `missing`, `needs_sync`, `permission_denied`, `disabled_by_user`, or `unavailable`, tell the user what repair action is needed instead of guessing.
185
- - If context is `stale`, mention freshness when timing matters.
186
- - To request a fresh phone-side HealthKit sync, run `oomi context health sync --json`. The iPhone app must open or refresh before it can read Apple Health and fulfill the pending request.
189
+ - If `healthContext.agentGuidance.recommendedCommand` is present, run it before answering current/latest health questions.
190
+ - If context is `stale` or `needs_sync`, run `oomi context health sync --wait --json`; answer only after fresh context is uploaded, or tell the user the phone sync is still pending if it times out.
191
+ - If context is `missing`, `permission_denied`, `disabled_by_user`, or `unavailable`, tell the user what repair action is needed instead of guessing.
192
+ - Never present stale HealthKit values as current values.
187
193
  - Do not ask the phone for HealthKit directly; the Oomi mobile app syncs HealthKit to the backend.
188
194
 
189
195
  ## Voice Contract
@@ -12,6 +12,7 @@ Get OpenClaw into a state where:
12
12
  - Oomi device-backed voice can start cleanly
13
13
  - the bridge is supervised or restartable
14
14
  - component-composed persona apps can be inspected and updated through Oomi API tools
15
+ - Fitness state can be inspected with one command that combines mini-app state and approved health context
15
16
  - approved user context, such as HealthKit data synced by the Oomi mobile app, can be read through Oomi backend context tools
16
17
  - the user never has to paste raw pair identifiers or gateway secrets manually when managed connect is available
17
18
 
@@ -248,8 +249,17 @@ When a user asks about an existing persona app:
248
249
  4. To update state, use a validated action: `oomi persona-apps apply-action <slug> --action <action-id> --payload-json '<json>' --json`.
249
250
  5. If `persona-apps list` returns no app for the user's request, tell the user they can add that persona from the Oomi client so the client can collect the right permissions and data sources.
250
251
 
251
- For the Fitness v0 slice:
252
+ For Fitness questions, prefer the combined read command first:
252
253
 
254
+ ```bash
255
+ oomi fitness show --json
256
+ ```
257
+
258
+ It returns the linked Fitness persona app plus the latest approved health context in one payload.
259
+
260
+ For the Fitness slice:
261
+
262
+ - Use `fitness.set_goal` with payload `{"goalId":"move_more"}`, `{"goalId":"sleep_better"}`, or `{"goalId":"build_workout_habit"}` to set the active focus.
253
263
  - Use `fitness.complete_goal` with payload `{"goalId":"easy-run"}` to mark a known goal complete.
254
264
  - Use `fitness.complete_workout` with payload like `{"goalId":"easy-run","minutes":20}` when the user says they finished a workout.
255
265
 
@@ -276,8 +286,9 @@ Rules:
276
286
 
277
287
  - Answer only from `healthContext.summary` and `healthContext.derived`.
278
288
  - Respect `healthContext.agentGuidance.canAnswerFromContext`.
279
- - If `canAnswerFromContext` is false, tell the user the relevant `healthContext.repair.label` or `healthContext.repair.reason`.
280
- - If `healthContext.status` is `stale`, mention that the latest HealthKit sync may be out of date when timing matters.
281
- - If the user asks you to refresh stale HealthKit context, run `oomi context health sync --json`; tell the user the iPhone app must be opened for Oomi to read Apple Health and fulfill the pending sync.
289
+ - If `healthContext.agentGuidance.recommendedCommand` is present, run it before answering current/latest health questions.
290
+ - If `healthContext.status` is `stale` or `needs_sync`, run `oomi context health sync --wait --json`; answer only after fresh context is uploaded, or tell the user the phone sync is still pending if it times out.
291
+ - If `canAnswerFromContext` is false and no sync command is recommended, tell the user the relevant `healthContext.repair.label` or `healthContext.repair.reason`.
292
+ - Never present stale HealthKit values as current values.
282
293
  - Do not infer unavailable health fields.
283
294
  - Do not request HealthKit directly from the phone; the Oomi mobile app owns permission prompts and syncs approved data to the backend.
package/bin/oomi-ai.js CHANGED
@@ -79,6 +79,12 @@ function parsePositiveInteger(value, fallback) {
79
79
  return Math.floor(num);
80
80
  }
81
81
 
82
+ function delay(ms) {
83
+ return new Promise((resolve) => {
84
+ setTimeout(resolve, ms);
85
+ });
86
+ }
87
+
82
88
  function readJsonSafe(filePath) {
83
89
  if (!fs.existsSync(filePath)) return null;
84
90
  try {
@@ -227,10 +233,14 @@ Commands:
227
233
  persona-apps apply-action <slug>
228
234
  Apply an approved persona app action through the Oomi backend.
229
235
 
236
+ fitness show
237
+ Show the linked Fitness persona app plus approved health context.
238
+
230
239
  context health
231
240
  Show the latest account-linked health context available to this paired OpenClaw device.
232
- context health sync
241
+ context health sync [--wait]
233
242
  Request the linked Oomi mobile app to sync fresh HealthKit context.
243
+ Use --wait to poll until fresh context is uploaded or the timeout expires.
234
244
 
235
245
  Common flags:
236
246
  --agents-file PATH Override AGENTS.md path
@@ -577,11 +587,73 @@ async function handleContextHealthCommand(flags = {}) {
577
587
  printStructuredResult(payload, isTruthyFlag(flags.json));
578
588
  }
579
589
 
590
+ async function handleFitnessShowCommand(flags = {}) {
591
+ const client = createCliPersonaApiClient(flags);
592
+ const listPayload = await client.listPersonaApps();
593
+ const personaApps = Array.isArray(listPayload?.personaApps) ? listPayload.personaApps : [];
594
+ const fitnessApp = personaApps.find((app) => app?.personaType === 'fitness') ||
595
+ personaApps.find((app) => app?.slug === 'fitness-today');
596
+
597
+ if (!fitnessApp?.slug) {
598
+ throw new Error('Fitness persona app is not linked. Ask the user to add Fitness from the Oomi client first.');
599
+ }
600
+
601
+ const appPayload = await client.getPersonaApp({ slug: fitnessApp.slug });
602
+ let healthPayload = null;
603
+ let healthContextError = null;
604
+ try {
605
+ healthPayload = await client.getHealthContext();
606
+ } catch (err) {
607
+ healthContextError = err?.message || String(err);
608
+ }
609
+
610
+ printStructuredResult({
611
+ ok: true,
612
+ personaApp: appPayload?.personaApp || null,
613
+ healthContext: healthPayload?.healthContext || null,
614
+ healthContextError,
615
+ }, isTruthyFlag(flags.json));
616
+ }
617
+
580
618
  async function handleContextHealthSyncCommand(flags = {}) {
581
619
  const client = createCliPersonaApiClient(flags);
582
620
  const payload = await client.requestHealthContextSync({
583
621
  reason: flags.reason || 'agent_requested',
584
622
  });
623
+
624
+ if (isTruthyFlag(flags.wait)) {
625
+ const previousCapturedAt = payload?.healthContext?.source?.capturedAt || null;
626
+ const timeoutMs = parsePositiveInteger(flags['timeout-ms'] || flags.timeoutMs, 30_000);
627
+ const pollMs = parsePositiveInteger(flags['poll-ms'] || flags.pollMs, 3_000);
628
+ const startedAt = Date.now();
629
+ let latestPayload = payload;
630
+
631
+ while (Date.now() - startedAt < timeoutMs) {
632
+ await delay(pollMs);
633
+ latestPayload = await client.getHealthContext();
634
+ const healthContext = latestPayload?.healthContext;
635
+ const capturedAt = healthContext?.source?.capturedAt || null;
636
+ if (healthContext?.status === 'ready' && (!previousCapturedAt || capturedAt !== previousCapturedAt)) {
637
+ printStructuredResult({
638
+ ...payload,
639
+ refreshed: true,
640
+ waitedMs: Date.now() - startedAt,
641
+ healthContext,
642
+ }, isTruthyFlag(flags.json));
643
+ return;
644
+ }
645
+ }
646
+
647
+ printStructuredResult({
648
+ ...payload,
649
+ refreshed: false,
650
+ waitedMs: Date.now() - startedAt,
651
+ waitTimedOut: true,
652
+ healthContext: latestPayload?.healthContext || payload.healthContext,
653
+ }, isTruthyFlag(flags.json));
654
+ return;
655
+ }
656
+
585
657
  printStructuredResult(payload, isTruthyFlag(flags.json));
586
658
  }
587
659
 
@@ -4331,6 +4403,11 @@ async function main() {
4331
4403
  return;
4332
4404
  }
4333
4405
 
4406
+ if (command === 'fitness' && subcommand === 'show') {
4407
+ await handleFitnessShowCommand(args.flags);
4408
+ return;
4409
+ }
4410
+
4334
4411
  if (command === 'context' && subcommand === 'health') {
4335
4412
  if (args.positionals[0] === 'sync') {
4336
4413
  await handleContextHealthSyncCommand(args.flags);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oomi-ai",
3
- "version": "0.3.3",
3
+ "version": "0.3.5",
4
4
  "description": "Managed Oomi channel, bridge, voice, and persona app API tooling for OpenClaw",
5
5
  "bin": {
6
6
  "oomi": "bin/oomi-ai.js"
@@ -19,23 +19,26 @@ The current files are the first shared contract for the vertical slice:
19
19
  OpenClaw agents should use the packaged CLI to inspect and update account-linked persona apps:
20
20
 
21
21
  ```bash
22
+ oomi fitness show --json
22
23
  oomi persona-apps list --json
23
24
  oomi persona-apps show fitness-today --json
24
25
  oomi persona-apps apply-action fitness-today --action fitness.complete_workout --payload-json '{"goalId":"easy-run","minutes":20}' --json
25
26
  ```
26
27
 
28
+ For Fitness, prefer `oomi fitness show --json` before answering because it returns the mini-app state and approved health context together.
29
+
27
30
  When answering data-backed health or fitness questions, agents should read approved backend context first:
28
31
 
29
32
  ```bash
30
33
  oomi context health --json
31
34
  ```
32
35
 
33
- If the user asks you to refresh stale HealthKit context, run:
36
+ If the user asks for current/latest health context and the context is stale or needs sync, run:
34
37
 
35
38
  ```bash
36
- oomi context health sync --json
39
+ oomi context health sync --wait --json
37
40
  ```
38
41
 
39
- The iPhone app must open or refresh before it can read Apple Health and fulfill the pending request.
42
+ Answer only after fresh context is uploaded. If the command times out, tell the user the phone sync is still pending.
40
43
 
41
44
  If a persona app is missing, ask the user to add it from the Oomi client first so platform-specific permissions and hydration can run.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "schemaVersion": "persona-app.v1",
3
- "registryVersion": "2026-04-24.1",
3
+ "registryVersion": "2026-04-25.1",
4
4
  "contextTools": [
5
5
  {
6
6
  "id": "health.context.read",
@@ -34,6 +34,21 @@
34
34
  "description": "Daily movement summary with progress, steps, and sleep context.",
35
35
  "targets": ["ios", "android", "web"]
36
36
  },
37
+ {
38
+ "type": "fitness.dataFreshnessCard",
39
+ "description": "Shows whether mobile health context is fresh enough for decisions and nudges.",
40
+ "targets": ["ios", "android", "web"]
41
+ },
42
+ {
43
+ "type": "fitness.dailyPlanCard",
44
+ "description": "Goal-aware daily plan generated from approved health context.",
45
+ "targets": ["ios", "android", "web"]
46
+ },
47
+ {
48
+ "type": "fitness.goalEditorCard",
49
+ "description": "Lets the user select one active fitness focus from approved Oomi goals.",
50
+ "targets": ["ios", "android", "web"]
51
+ },
37
52
  {
38
53
  "type": "fitness.goalChecklist",
39
54
  "description": "Checklist of suggested goals for today.",
@@ -72,6 +87,20 @@
72
87
  "minutes": { "type": "integer", "minimum": 1 }
73
88
  }
74
89
  }
90
+ },
91
+ {
92
+ "id": "fitness.set_goal",
93
+ "description": "Sets the user's active Fitness focus so Oomi can tune the mini-app and future nudges.",
94
+ "payloadSchema": {
95
+ "type": "object",
96
+ "required": ["goalId"],
97
+ "properties": {
98
+ "goalId": {
99
+ "type": "string",
100
+ "enum": ["move_more", "sleep_better", "build_workout_habit"]
101
+ }
102
+ }
103
+ }
75
104
  }
76
105
  ],
77
106
  "permissions": [
@@ -101,18 +101,25 @@ Interpret bridge states like this:
101
101
  Component-composed persona apps are the default Oomi mini-app path. Use these commands when the user asks about or updates an existing native/web Oomi persona app such as Fitness Today:
102
102
 
103
103
  ```bash
104
+ oomi fitness show --json
104
105
  oomi persona-apps list --json
105
106
  oomi persona-apps show fitness-today --json
106
107
  oomi persona-apps apply-action fitness-today --action fitness.complete_workout --payload-json '{"goalId":"easy-run","minutes":20}' --json
107
108
  ```
108
109
 
109
110
  Rules:
111
+ - prefer `oomi fitness show --json` for Fitness questions because it combines mini-app state and approved health context
110
112
  - answer from returned `appState.bindings`, not from memory
111
113
  - apply updates through approved `persona-apps apply-action` actions
112
114
  - if the persona app does not exist, ask the user to add it from the Oomi client first so permissions and hydration can run
113
115
  - do not create local persona UI projects from the OpenClaw machine
114
116
  - do not execute persona UI jobs or start local persona app runtimes
115
117
 
118
+ Supported Fitness actions include:
119
+ - `fitness.set_goal` with `{"goalId":"move_more"}`, `{"goalId":"sleep_better"}`, or `{"goalId":"build_workout_habit"}`
120
+ - `fitness.complete_goal`
121
+ - `fitness.complete_workout`
122
+
116
123
  ## Permissioned Context Tools
117
124
 
118
125
  Use context commands before answering personal data questions that should come from Oomi-approved permissions.
@@ -126,8 +133,10 @@ oomi context health --json
126
133
  Rules:
127
134
  - answer only from `healthContext.summary` and `healthContext.derived`
128
135
  - respect `healthContext.agentGuidance.canAnswerFromContext`
129
- - if context is missing, stale, denied, disabled, or unavailable, follow `healthContext.repair`
130
- - if the user asks you to refresh stale HealthKit context, run `oomi context health sync --json`; tell the user the iPhone app must be opened for Oomi to read Apple Health and fulfill the pending sync
136
+ - if `healthContext.agentGuidance.recommendedCommand` is present, run it before answering current/latest health questions
137
+ - if context is stale or needs sync, run `oomi context health sync --wait --json`; answer only after fresh context is uploaded, or tell the user the phone sync is still pending if it times out
138
+ - if context is missing, denied, disabled, or unavailable, follow `healthContext.repair`
139
+ - never present stale HealthKit values as current values
131
140
  - do not ask HealthKit directly; the Oomi mobile app syncs approved HealthKit data to the backend
132
141
 
133
142
  ## Hidden Speech Payload
@@ -87,17 +87,24 @@ Oomi persona apps are component-composed client surfaces backed by approved temp
87
87
  Persona UI creation and rendering happen inside Oomi-managed systems. Use the Oomi API tools instead:
88
88
 
89
89
  ```bash
90
+ oomi fitness show --json
90
91
  oomi persona-apps list --json
91
92
  oomi persona-apps show fitness-today --json
92
93
  oomi persona-apps apply-action fitness-today --action fitness.complete_workout --payload-json '{"goalId":"easy-run","minutes":20}' --json
93
94
  ```
94
95
 
95
96
  Rules:
97
+ - for Fitness questions, prefer `oomi fitness show --json` first because it combines mini-app state and approved health context
96
98
  - answer from returned `appState.bindings`, not memory
97
99
  - apply updates only through approved `persona-apps apply-action` actions
98
100
  - if the persona app is missing, ask the user to add it from the Oomi client first so permissions and hydration can run
99
101
  - do not execute persona UI jobs or start local persona app runtimes
100
102
 
103
+ Supported Fitness actions include:
104
+ - `fitness.set_goal` with `{"goalId":"move_more"}`, `{"goalId":"sleep_better"}`, or `{"goalId":"build_workout_habit"}`
105
+ - `fitness.complete_goal`
106
+ - `fitness.complete_workout`
107
+
101
108
  ## Permissioned Context Tools
102
109
 
103
110
  Use Oomi context tools when the user asks about personal data that should come from mobile permissions instead of memory.
@@ -111,8 +118,9 @@ oomi context health --json
111
118
  Rules:
112
119
  - answer only from `healthContext.summary` and `healthContext.derived`
113
120
  - respect `healthContext.agentGuidance.canAnswerFromContext`
114
- - if `canAnswerFromContext` is false, tell the user the relevant `healthContext.repair.label` or `healthContext.repair.reason`
115
- - if `healthContext.status` is `stale`, mention that the latest HealthKit sync may be out of date when timing matters
116
- - if the user asks you to refresh stale HealthKit context, run `oomi context health sync --json`; tell the user the iPhone app must be opened for Oomi to read Apple Health and fulfill the pending sync
121
+ - if `healthContext.agentGuidance.recommendedCommand` is present, run it before answering current/latest health questions
122
+ - if `healthContext.status` is `stale` or `needs_sync`, run `oomi context health sync --wait --json`; answer only after fresh context is uploaded, or tell the user the phone sync is still pending if it times out
123
+ - if `canAnswerFromContext` is false and no sync command is recommended, tell the user the relevant `healthContext.repair.label` or `healthContext.repair.reason`
124
+ - never present stale HealthKit values as current values
117
125
  - do not infer unavailable health fields
118
126
  - do not request HealthKit directly from the phone; the Oomi mobile app owns permission prompts and syncs approved data to the backend