@mhome/app-facade-protocol 1.49.2 → 1.59.0

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
@@ -17,6 +17,61 @@ owns public targets, request/response/event schemas, and stable operation
17
17
  identity. Core-to-Node routes and provider runtime models belong to
18
18
  `mhome-core-api` instead.
19
19
 
20
+ ## App result contract
21
+
22
+ `control.responseDetail` selects information depth: `full` (default) returns
23
+ `data`, while `essential` returns YAML in `text`. It is independent of action
24
+ `mode` and is never copied into business input, prepared snapshots or execution
25
+ fingerprints. `responseFormat` and business `outputFormat` are rejected.
26
+
27
+ Final responses use exactly one of these shapes:
28
+
29
+ ```json
30
+ {"status":"success","data":{"items":[]}}
31
+ {"status":"success","text":"items: []\n"}
32
+ {"status":"partial","data":{"moved":["a"],"notMoved":[{"deviceId":"b","reason":"offline"}]}}
33
+ {"status":"error","error":{"code":"PRECONDITION_FAIL","message":"The object changed"}}
34
+ {"status":"cancelled"}
35
+ ```
36
+
37
+ Success and partial carry exactly one of `data` or `text`. Error carries a
38
+ `code`, `message`, and optional `details`; success, partial and cancellation may
39
+ carry structured `warnings`. Resource state belongs inside data: successfully
40
+ reading a failed job is still a successful invocation. Partial is not complete
41
+ success and must remain visible to the caller. Transport admission failures keep
42
+ their transport status and retry metadata.
43
+
44
+ Prepare returns its fixed structured control DTO (`preparedActionId`,
45
+ `approvalToken`, `target`, `status`, `preview`, `expiresAt`). Commit executes the
46
+ stored snapshot and caches the full business result; presentation happens after
47
+ execution or replay. Reject returns cancellation. Response detail never changes
48
+ approval meaning or permission checks. Operation and HITL continuation protocols
49
+ are separate from this result envelope.
50
+
51
+ App UI requests full and unwraps the envelope at its transport boundary. Agent
52
+ and MCP public results use essential; internal workflow reads use full. The CLI
53
+ uses `--response-detail full|essential`, and the bundled external Skill requests
54
+ essential. Dashboard essential projection keeps card identity, title, type,
55
+ layouts and view choices, excluding renderer configuration and resource payloads.
56
+ Device brief-list essential retains identity, room, capability IDs, Integration and
57
+ availability, omitting `connectId`, `manufacturerId` and `manufacturerName`.
58
+ Selected Device detail, history, recent activity and mutation outcomes keep their
59
+ public business fields. Device bulk list remains a CLI-only surface.
60
+ Targets without a dedicated projection retain their business fields in YAML;
61
+ essential does not promise a smaller field set for every domain. Targets own that selection.
62
+
63
+ The [response schema](schema/facade-response.v1.schema.json) and shared
64
+ [presentation fixtures](fixtures/presentation/cases.json) define the wire
65
+ invariants and cross-runtime projection checks. Domain schemas describe the
66
+ inner `data` DTO. `/pod/deck/*`, `/pod/talk/*` and legacy Controllers retain their
67
+ separate wire contracts.
68
+
69
+ The npm protocol descriptor exports this response schema as `schemas.response`.
70
+ Protocol verification compiles it and the Conversation execution envelope with
71
+ strict AJV settings, checks their shared response definition, and validates the
72
+ same positive/negative response fixtures used by Rust. Package staging tests
73
+ verify that the schema is available through the published descriptor.
74
+
20
75
  `interaction_flow` exposes only the materialized current-step view and session
21
76
  commands. Handler operations, the complete step graph, and Node routing
22
77
  identity remain internal to MeowCore.
@@ -49,9 +104,11 @@ timings and request reasons are in `manifest/pod-targets.v1.json`; snapshots use
49
104
  `schema/pod-discovery.v1.schema.json` and `schema/pod-commission.v1.schema.json`;
50
105
  User Pod enrollment uses a one-time login approval; activation is confirmed on the paired device.
51
106
 
52
- `/app/pod/list` reads registered Pod Clients in the active Space, including
53
- connections that are currently absent. `/app/host/list` reads historical Host
54
- registrations without granting management access; see the
107
+ `/app/pod/list` combines the current User's personal registrations with the current
108
+ Space's Space-owned Pods, including offline entries. `/app/pod/rename` updates a
109
+ personal registration name. See [Pod directory](contract/pod-directory-v1.md). `/app/host/list` combines Host registrations with current observations and details.
110
+ `/app/host/directory/list` is the historical registration input used by native
111
+ list composition; it does not grant management access. See the
55
112
  [Host directory contract](contract/host-directory-v1.md). Native commissioning
56
113
  uses the existing device protocols; the Agent resumes only after verifying the
57
114
  resulting registration. Host completion identifies both `hostId` and `env`.
@@ -109,3 +166,9 @@ Client sessions. Host inventory and operations use `/app/system/hosts/get` and
109
166
  current Core. First claim is direct LAN only. See [SYSTEM.md](SYSTEM.md).
110
167
 
111
168
  Business operation permissions are declared once in [the authorization manifest](manifest/authorization.v1.json), consumed by Core and Lion. See [business authorization](contract/authorization-v1.md).
169
+
170
+ ## Matter management reads
171
+
172
+ `/app/matter/device/list` is the management inventory, grouped by Fabric node. It is distinct from the Plugin's Integration enumeration `/matter/device/list`. Its `sourceDeviceId` values identify Plugin projections, not Twin entities. Nodes without projected devices remain visible; `membershipComplete`, `inventoryComplete`, per-node completeness and `errors` describe partial observations. Lists omit endpoints and attribute payloads.
173
+
174
+ `/app/matter/fabric/node/detail` accepts 1–20 unique `matterNodeIds`, returning an ordered item result or error for each ID. The request uses string IDs without numeric coercion. See `schema/matter-management.v1.schema.json`. All `/app/matter/` operations execute through the Space's Hub irrespective of scope execution mode. Node removal is a manual UI/CLI capability, not a delegated Agent/MCP tool.
@@ -120,8 +120,13 @@ and responses are bounded to 64 KiB including encryption overhead.
120
120
  ```
121
121
 
122
122
  `env` must match the native Client runtime (`prod`, `dev`, `e2e`).
123
- `podId` is included when activation is in flight or durably completed, so a
124
- new session can observe completion without issuing another credential.
123
+ `podId` and `clientMode` (`user` or `space`) are included together when activation
124
+ is in flight or durably completed. A new session reads both from the secure
125
+ device response before resuming; a missing or unknown mode fails the attempt.
126
+ This observes completion without issuing another credential. Native completed
127
+ enrollment sessions return both fields. HITL passes this receipt to its handler,
128
+ which verifies exactly the corresponding personal or Space enrollment source;
129
+ it never probes the other source or treats the mode itself as proof of success.
125
130
  `platform` is diagnostic only. `wifiConfigured` is true when the pod kept a
126
131
  working network across logout; the commissioner then skips the Wi-Fi steps after
127
132
  confirming connectivity with `prov-config` status. Inside a Wi-Fi change window
@@ -249,8 +254,8 @@ accepted.
249
254
 
250
255
  `podId` identifies a single approval attempt, not the durable device or authenticated
251
256
  User. The durable `registrationId` derives from the business User and the validated
252
- persistent device-key fingerprint. `POST /api/v1/pod/list` returns that ID;
253
- `/api/v1/pod/rename` accepts `{registrationId, name}` with ordinary User login.
257
+ persistent device-key fingerprint. `/app/pod/list` returns that ID;
258
+ `/app/pod/rename` accepts `{registrationId, name}` with authenticated User authority.
254
259
  Successful authentication atomically creates the registration if absent and records
255
260
  completion on the consumed approval. Retries preserve the initial registration and
256
261
  current display name. Unauthenticated attempts create no device-list entry.
@@ -472,19 +477,13 @@ independent and are never issued by this flow.
472
477
 
473
478
  ## Registered Pod list
474
479
 
475
- `/app/pod/list` takes empty input and lists persisted Pod Clients in the current
476
- Space, including disconnected registrations. A personal caller sees their own
477
- User Pods and Space-owned Pods. Rows contain `appClientId`, `clientMode`,
478
- `scopeId`, `registered`, `connected` and `observationSource` (`hub` or `cloud`);
479
- the envelope includes `observedAtMs`. Connection status is observed from that
480
- execution endpoint, not a statement about physical power or LAN reachability.
481
- Native clients combine this result with their own discovery observations.
482
- Never merge a Bluetooth short ID with a registered identity without proof.
483
- This read does not issue credentials or change Pod identity.
480
+ The public `/app/pod/list` Facade combines all current-User personal registrations
481
+ and current-Space Space-owned Pod registrations. UI and tools share this result.
482
+ `registrationId` identifies the durable registration; it is not the enrollment
483
+ `podId` used by native commissioning. Connection observations are separately
484
+ scoped and may be unknown. See [Pod directory](pod-directory-v1.md) for fields,
485
+ aggregation, failure handling and personal display-name updates.
484
486
 
485
-
486
- `appClientId` identifies a registered application client; it is not the enrollment
487
- `podId` used by native commissioning. The list does not invent that association.
488
487
  `/app/pod/enrollment/get` takes `{podId}` and returns `{podId, clientMode: "user",
489
488
  registered}` from the authenticated user's successful receipt for that exact attempt.
490
489
  An older registration for the same device cannot complete a different attempt.
@@ -0,0 +1,51 @@
1
+ # Pod directory
2
+
3
+ `/app/pod/list` is a Space-mode-routed, direct, read-only Facade call with empty
4
+ input. It requires a personal User and read access to the current Space. It combines
5
+ all completed personal Pod registrations owned by that User with registered
6
+ Space-owned Pods in that Space. Another User's personal Pods and other Spaces'
7
+ Pods are excluded. Offline registrations remain; incomplete commissioning attempts
8
+ and nearby discovery candidates are not registrations.
9
+
10
+ A local Space composes this result in Core, reading its own Space inventory and
11
+ obtaining personal registrations through the authenticated private cloud control
12
+ bridge. A cloud Space composes it in cloud. LLM placement does not change this
13
+ owner. Cloud entrypoints forward local-Space business to Core; only an unavailable
14
+ Hub transport permits the explicit read-only registration fallback below.
15
+
16
+ The result contains `pods`, `scopeId`, `observedAtMs`, and `sourceErrors`.
17
+ Every row contains `registrationId`, `clientMode` (`user` or `space`), `registered`
18
+ (true), and `connection` with `scopeId`, `state` (`connected`, `disconnected`, or
19
+ `unknown`), and `source` (`hub`, `cloud`, or `unavailable`). Space rows also contain
20
+ `scopeId` and `appClientId`. Personal rows contain the saved `name`, `deviceId`,
21
+ `model`, `platform`, `firmwareVersion`, `issuedAt`, and `activatedAt`.
22
+ No tokens, enrollment grants, credential IDs or private keys are returned.
23
+
24
+ Connection is an observation in the indicated Space, not global power state.
25
+ Personal registration IDs are keyed by User and device public-key fingerprint;
26
+ personal connection IDs do not carry that fingerprint. The directory does not
27
+ join them by name or hardware ID. Personal connection state is therefore unknown.
28
+ Space registration IDs are their authenticated appClientId and can be correlated.
29
+ When a local Hub cannot be queried, the cloud's persisted Space registrations
30
+ remain visible with unknown connection state and `sourceErrors.space`. The cache
31
+ may omit registrations not yet synchronized; it must not imply a complete live
32
+ snapshot. Cloud presence lookup failures likewise retain saved Space records
33
+ as unknown, with an explicit source error. A successful empty presence lookup
34
+ can establish disconnection. Registration read failures fail the call instead
35
+ of fabricating an empty inventory; authorization and request failures remain errors.
36
+
37
+ `/app/pod/space/list` is an internal composition target, not a separate Agent,
38
+ MCP or CLI operation. Scope-mode routing reads the Hub for a local Space or cloud
39
+ inventory for a cloud Space. It returns only Space-owned Pod registrations.
40
+ UI, Agent, MCP and CLI all use `/app/pod/list`; UI groups by clientMode.
41
+
42
+ `/app/pod/rename` is a direct, idempotent write with exactly `registrationId` and
43
+ `name` (trimmed, 1–64 characters). The authenticated User is the owner; request
44
+ input cannot select a User. It updates only that personal registration's display
45
+ name and returns `{registrationId,name}`. It works while the Pod is offline and
46
+ does not change credentials, ownership, networking or the Pod's local settings.
47
+ It has no prepare/commit or HITL requirement. Space Pod rename is not provided.
48
+ The CLI is `meow pod rename <registrationId> <name>`; Agent/MCP use `pod_rename`.
49
+
50
+ Personal login approve/redeem and Space commissioning retain their existing
51
+ protocols. Factory reset is available only on the Pod itself, not remotely.