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 +10 -4
- package/agent_instructions.md +15 -4
- package/bin/oomi-ai.js +78 -1
- package/package.json +1 -1
- package/persona-app/README.md +6 -3
- package/persona-app/registry/v1.json +30 -1
- package/skills/oomi/SKILL.md +11 -2
- package/skills/oomi/agent_instructions.md +11 -3
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
|
|
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
|
|
185
|
-
- If context is `stale`,
|
|
186
|
-
-
|
|
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
|
package/agent_instructions.md
CHANGED
|
@@ -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
|
|
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 `
|
|
280
|
-
- If `healthContext.status` is `stale`,
|
|
281
|
-
- If
|
|
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
package/persona-app/README.md
CHANGED
|
@@ -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
|
|
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
|
-
|
|
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-
|
|
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": [
|
package/skills/oomi/SKILL.md
CHANGED
|
@@ -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
|
|
130
|
-
- if
|
|
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 `
|
|
115
|
-
- if `healthContext.status` is `stale`,
|
|
116
|
-
- if
|
|
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
|