@mhome/app-facade-protocol 1.62.0 → 1.66.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.
Files changed (33) hide show
  1. package/README.md +19 -5
  2. package/contract/entity-presentation-v1.md +22 -0
  3. package/contract/integration-presentation-v1.md +27 -0
  4. package/contract/pod-directory-v1.md +3 -3
  5. package/fixtures/binding-list.response.json +1 -1
  6. package/fixtures/entity-presentation.conformance.json +54 -0
  7. package/fixtures/hub-state.configured.json +1 -1
  8. package/fixtures/hub-state.revoked.json +1 -1
  9. package/fixtures/integration-presentation.conformance.json +206 -0
  10. package/fixtures/presentation/cases.json +578 -152
  11. package/fixtures/route-list.response.json +2 -2
  12. package/fixtures/route-update.request.json +1 -1
  13. package/fixtures/route-update.response.json +1 -1
  14. package/fixtures/shared-account-grant-list.response.json +1 -1
  15. package/manifest/app-facade.v1.json +15 -1
  16. package/manifest/authorization.v1.json +6 -8
  17. package/manifest/host-provision-targets.v1.json +1 -1
  18. package/manifest/hub-targets.v1.json +1 -1
  19. package/manifest/pod-targets.v1.json +1 -1
  20. package/manifest/routing.v1.json +1 -1
  21. package/manifest/targets.v1.json +1 -1
  22. package/package.json +1 -1
  23. package/protocol.json +5 -1
  24. package/schema/entity-presentation.v1.schema.json +83 -0
  25. package/schema/facade-response.v1.schema.json +7 -45
  26. package/schema/hub-state.v1.schema.json +2 -2
  27. package/schema/integration-presentation.v1.schema.json +81 -0
  28. package/schema/messaging-frame.v3.schema.json +9 -9
  29. package/schema/system-clients.v1.schema.json +2 -2
  30. package/schema/system-hosts-runtime.v1.schema.json +2 -2
  31. package/schema/system-hosts.v1.schema.json +2 -2
  32. package/schema/system-instances.v1.schema.json +2 -2
  33. package/schema/topology.v1.schema.json +2 -2
package/README.md CHANGED
@@ -41,7 +41,7 @@ Composition does not imply a distributed transaction or automatic rollback.
41
41
  ## App result contract
42
42
 
43
43
  `control.responseDetail` selects information depth: `full` (default) returns
44
- `data`, while `essential` returns YAML in `text`. It is independent of action
44
+ `data`, while `essential` returns YAML in `data`. It is independent of action
45
45
  `mode` and is never copied into business input, prepared snapshots or execution
46
46
  fingerprints. `responseFormat` and business `outputFormat` are rejected.
47
47
 
@@ -49,14 +49,18 @@ Final responses use exactly one of these shapes:
49
49
 
50
50
  ```json
51
51
  {"status":"success","data":{"items":[]}}
52
- {"status":"success","text":"items: []\n"}
52
+ {"status":"success","data":"items: []\n"}
53
53
  {"status":"partial","data":{"moved":["a"],"notMoved":[{"deviceId":"b","reason":"offline"}]}}
54
54
  {"status":"error","error":{"code":"PRECONDITION_FAIL","message":"The object changed"}}
55
55
  {"status":"cancelled"}
56
56
  ```
57
57
 
58
- Success and partial carry exactly one of `data` or `text`. Error carries a
59
- `code`, `message`, and optional `details`; success, partial and cancellation may
58
+ Success may omit `data` for an acknowledgement; partial always carries `data`.
59
+ The request selects the representation: Full is the business value (which may itself
60
+ be a string); Essential is a YAML string. Do not infer response detail from its type.
61
+ Pure acknowledgements are identical in both modes: `{"status":"success"}`. Error carries a
62
+ `code`, `message`, and optional Full `details`. Essential errors keep code/message
63
+ and place their projected diagnostics in YAML `data`; success, partial and cancellation may
60
64
  carry structured `warnings`. Resource state belongs inside data: successfully
61
65
  reading a failed job is still a successful invocation. Partial is not complete
62
66
  success and must remain visible to the caller. Transport admission failures keep
@@ -73,7 +77,10 @@ App UI requests full and unwraps the envelope at its transport boundary. Agent
73
77
  and MCP public results use essential; internal workflow reads use full. The CLI
74
78
  uses `--response-detail full|essential`, and the bundled external Skill requests
75
79
  essential. Dashboard essential projection keeps card identity, title, type,
76
- layouts and view choices, excluding renderer configuration and resource payloads.
80
+ view choices, excluding renderer configuration and resource payloads. Card list
81
+ returns only ID/type/title; card detail essential omits layouts. Canvas layout get
82
+ requires one sm/md/lg size and retains every card ID/title/layout in full and essential;
83
+ uninitialized layouts or coordinates are null.
77
84
  Device brief-list essential retains identity, room, capability IDs, Integration and
78
85
  availability, omitting `connectId`, `manufacturerId` and `manufacturerName`.
79
86
  Selected Device detail, history, recent activity and mutation outcomes keep their
@@ -96,6 +103,10 @@ asset manifests and renderer configuration schemas. Selected release detail keep
96
103
  Revisions, installation IDs, existing override config and supported patterns remain.
97
104
  Empty mutation acknowledgements remain empty; they do not invent a new revision.
98
105
 
106
+ Room uses one `/app/room/list` read. Every Room has `roomId`, `roomName`,
107
+ `index`, `deviceCount`, and `deviceIds`; full and essential keep all five fields.
108
+ Membership IDs include Device groups without expanding their members or state.
109
+
99
110
  Targets without a dedicated projection retain their business fields in YAML;
100
111
  essential does not promise a smaller field set for every domain. Targets own that selection.
101
112
 
@@ -211,3 +222,6 @@ Business operation permissions are declared once in [the authorization manifest]
211
222
  `/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.
212
223
 
213
224
  `/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.
225
+
226
+ UI Integration metadata uses `/app/integration/presentation/get`; see
227
+ [the presentation contract](contract/integration-presentation-v1.md).
@@ -0,0 +1,22 @@
1
+ # Entity UI presentation
2
+
3
+ `/app/entity/presentation/get` is a read-only App Facade for the UI. Its direct
4
+ input is `{entityType, entityId}`; tenant, Space and caller come from authenticated
5
+ context. It requires Space read access. It is not an Agent or MCP catalog entry.
6
+
7
+ The successful data object contains `entityEntry`, nullable `entityCard`, and
8
+ `twinAutomations`. The entry and card reuse the existing UI renderer models;
9
+ the schema owns this boundary without duplicating their nested widget/runtime
10
+ contracts. A missing, deleted or differently typed Twin is not found. Device
11
+ presentation reads reuse the device manager; other Twin entities reuse the general
12
+ manager. Ordinary Entity business detail and Group detail retain their own APIs.
13
+
14
+ Device card assembly is best effort. An absent card is `null`, with an empty
15
+ association list. Association-read failures retain the existing execution policy:
16
+ local Core returns an empty list; cloud propagates the error. No writes or repairs
17
+ are performed. Current Twin states still have their separate read/subscription.
18
+
19
+ UI uses `control: {mode: "direct", responseDetail: "full"}` and receives the shared
20
+ `{status: "success", data: ...}` envelope. The generic essential projection returns
21
+ `{status: "success", text: ...}` with the same business fields rendered as YAML;
22
+ there is no domain field reduction. This is not the compact business detail API.
@@ -0,0 +1,27 @@
1
+ # Integration UI presentation
2
+
3
+ `/app/integration/presentation/get` is a read-only App Facade for UI consumers.
4
+ Input is `{integrationId}` for one root Integration. Tenant, Space and caller
5
+ come from authenticated context; Space read membership is required. Empty IDs,
6
+ unknown fields and Provider IDs are rejected; missing Integrations are not found.
7
+ This operation is not registered in Agent, MCP or CLI catalogs.
8
+
9
+ The Space execution authority performs the complete read. Integration names and
10
+ icons include the root and every Provider in its visible catalog, regardless of
11
+ installation. `typeNames` labels entity types; `entityStandalone` indicates
12
+ independent entity presentation; `groupSupported` indicates type-level group
13
+ support. Neither boolean map describes an instance's active state. `twinNames`
14
+ labels the root's applicable production Twin templates, not entity instances.
15
+ Developer draft overrides belong to developer tooling, not ordinary App display.
16
+ `capabilities` uses the shared CapabilityDescriptor, including full IDs and
17
+ visibility. No scripts, entity instances, live states or connection data are read.
18
+
19
+ Collections are always present and empty when the model has no corresponding
20
+ metadata. Optional empty names/icons are omitted. Upstream failures propagate;
21
+ existing metadata cache refresh policies remain in effect. No installation,
22
+ entity synchronization or business writes are performed.
23
+
24
+ UI sends `control: {mode: "direct", responseDetail: "full"}` and receives
25
+ `{status: "success", data: ...}`. Essential retains the same business fields in
26
+ YAML `data`; this interface has no separate field reduction. Mutating control
27
+ modes are not supported. SDK indexes the capability array by full ID for UI use.
@@ -13,11 +13,11 @@ bridge. A cloud Space composes it in cloud. LLM placement does not change this
13
13
  owner. Cloud entrypoints forward local-Space business to Core; only an unavailable
14
14
  Hub transport permits the explicit read-only registration fallback below.
15
15
 
16
- The result contains `pods`, `scopeId`, `observedAtMs`, and `sourceErrors`.
16
+ The result contains `pods`, `spaceId`, `observedAtMs`, and `sourceErrors`.
17
17
  Every row contains `registrationId`, `clientMode` (`user` or `space`), `registered`
18
- (true), and `connection` with `scopeId`, `state` (`connected`, `disconnected`, or
18
+ (true), and `connection` with `spaceId`, `state` (`connected`, `disconnected`, or
19
19
  `unknown`), and `source` (`hub`, `cloud`, or `unavailable`). Space rows also contain
20
- `scopeId` and `appClientId`. Personal rows contain the saved `name`, `deviceId`,
20
+ `spaceId` and `appClientId`. Personal rows contain the saved `name`, `deviceId`,
21
21
  `model`, `platform`, `firmwareVersion`, `issuedAt`, and `activatedAt`.
22
22
  No tokens, enrollment grants, credential IDs or private keys are returned.
23
23
 
@@ -9,7 +9,7 @@
9
9
  "placement": "local",
10
10
  "accountId": "bot",
11
11
  "conversationId": "group",
12
- "scopeId": "home",
12
+ "spaceId": "home",
13
13
  "displayName": "Family",
14
14
  "createdAtMs": 1
15
15
  }
@@ -0,0 +1,54 @@
1
+ {
2
+ "input": {
3
+ "valid": [
4
+ {
5
+ "entityType": "basic.device.Device",
6
+ "entityId": "device-1"
7
+ }
8
+ ],
9
+ "invalid": [
10
+ {},
11
+ {
12
+ "entityType": " ",
13
+ "entityId": "device-1"
14
+ },
15
+ {
16
+ "entityType": "basic.device.Device",
17
+ "entityId": ""
18
+ },
19
+ {
20
+ "entityType": "basic.device.Device",
21
+ "entityId": "device-1",
22
+ "scopeId": "other"
23
+ }
24
+ ]
25
+ },
26
+ "output": {
27
+ "valid": [
28
+ {
29
+ "entityEntry": {
30
+ "uid": "device-1",
31
+ "type": "basic.device.Device",
32
+ "integration": "example.device",
33
+ "name": "Desk lamp",
34
+ "active": false,
35
+ "data": {},
36
+ "capabilities": [],
37
+ "twinTags": [],
38
+ "cardCount": 0,
39
+ "issue": false
40
+ },
41
+ "entityCard": null,
42
+ "twinAutomations": []
43
+ }
44
+ ],
45
+ "invalid": [
46
+ {},
47
+ {
48
+ "entityEntry": null,
49
+ "entityCard": null,
50
+ "twinAutomations": []
51
+ }
52
+ ]
53
+ }
54
+ }
@@ -1,5 +1,5 @@
1
1
  {
2
- "scopeId": "scope-1",
2
+ "spaceId": "scope-1",
3
3
  "configured": true,
4
4
  "hub": {
5
5
  "id": "hub-1",
@@ -1,5 +1,5 @@
1
1
  {
2
- "scopeId": "scope-1",
2
+ "spaceId": "scope-1",
3
3
  "configured": false,
4
4
  "operation": {
5
5
  "kind": "REMOVE",
@@ -0,0 +1,206 @@
1
+ {
2
+ "input": {
3
+ "valid": [
4
+ {
5
+ "integrationId": "basic.device"
6
+ }
7
+ ],
8
+ "invalid": [
9
+ {},
10
+ {
11
+ "integration": "basic.device"
12
+ },
13
+ {
14
+ "integrationId": ""
15
+ },
16
+ {
17
+ "integrationId": " "
18
+ },
19
+ {
20
+ "integrationId": "basic.device",
21
+ "scopeId": "other"
22
+ },
23
+ {
24
+ "integrationId": 7
25
+ }
26
+ ]
27
+ },
28
+ "model": {
29
+ "integrationId": "basic.device",
30
+ "displayName": "Devices",
31
+ "iconUrl": "https://example.invalid/device.png",
32
+ "entityNames": {
33
+ "basic.device.Device": "Device",
34
+ "basic.device.Component": "Component"
35
+ },
36
+ "standaloneEntityTypes": [
37
+ "basic.device.Device"
38
+ ],
39
+ "entityGroupEnabled": {
40
+ "basic.device.Device": true
41
+ },
42
+ "implIntegrations": [
43
+ {
44
+ "integration": "example.device",
45
+ "name": "Example Provider",
46
+ "iconUrl": "https://example.invalid/provider.png"
47
+ },
48
+ {
49
+ "integration": "other.device",
50
+ "name": "Other Provider",
51
+ "iconUrl": ""
52
+ }
53
+ ],
54
+ "capabilities": {
55
+ "command.onOff": {
56
+ "displayName": "Power",
57
+ "description": "Switch power.",
58
+ "visibility": "full",
59
+ "tag": "on/off"
60
+ }
61
+ }
62
+ },
63
+ "output": {
64
+ "valid": [
65
+ {
66
+ "integrationId": "basic.device",
67
+ "integrationNames": {
68
+ "basic.device": "Devices",
69
+ "example.device": "Example Provider",
70
+ "other.device": "Other Provider"
71
+ },
72
+ "integrationIconUrls": {
73
+ "basic.device": "https://example.invalid/device.png",
74
+ "example.device": "https://example.invalid/provider.png"
75
+ },
76
+ "typeNames": {
77
+ "basic.device.Device": "Device",
78
+ "basic.device.Component": "Component"
79
+ },
80
+ "twinNames": {
81
+ "basic.device.Light": "Light"
82
+ },
83
+ "entityStandalone": {
84
+ "basic.device.Device": true,
85
+ "basic.device.Component": false
86
+ },
87
+ "groupSupported": {
88
+ "basic.device.Device": true
89
+ },
90
+ "capabilities": [
91
+ {
92
+ "capabilityId": "basic.device.command.onOff",
93
+ "type": "COMMAND",
94
+ "displayName": "Power",
95
+ "description": "Switch power.",
96
+ "visibility": "full",
97
+ "tag": "on/off"
98
+ }
99
+ ]
100
+ }
101
+ ],
102
+ "invalid": [
103
+ {
104
+ "integrationId": "basic.device",
105
+ "integrationNames": {
106
+ "basic.device": "Devices",
107
+ "example.device": "Example Provider",
108
+ "other.device": "Other Provider"
109
+ },
110
+ "integrationIconUrls": {
111
+ "basic.device": "https://example.invalid/device.png",
112
+ "example.device": "https://example.invalid/provider.png"
113
+ },
114
+ "typeNames": {
115
+ "basic.device.Device": "Device",
116
+ "basic.device.Component": "Component"
117
+ },
118
+ "twinNames": {
119
+ "basic.device.Light": "Light"
120
+ },
121
+ "entityStandalone": {
122
+ "basic.device.Device": true,
123
+ "basic.device.Component": false
124
+ },
125
+ "groupSupported": {
126
+ "basic.device.Device": true
127
+ }
128
+ },
129
+ {
130
+ "integrationId": "basic.device",
131
+ "integrationNames": {
132
+ "basic.device": "Devices",
133
+ "example.device": "Example Provider",
134
+ "other.device": "Other Provider"
135
+ },
136
+ "integrationIconUrls": {
137
+ "basic.device": "https://example.invalid/device.png",
138
+ "example.device": "https://example.invalid/provider.png"
139
+ },
140
+ "typeNames": {
141
+ "basic.device.Device": "Device",
142
+ "basic.device.Component": "Component"
143
+ },
144
+ "twinNames": {
145
+ "basic.device.Light": "Light"
146
+ },
147
+ "entityStandalone": {
148
+ "basic.device.Device": true,
149
+ "basic.device.Component": false
150
+ },
151
+ "groupSupported": {
152
+ "basic.device.Device": true
153
+ },
154
+ "capabilities": [
155
+ {
156
+ "capabilityId": "basic.device.command.onOff",
157
+ "type": "COMMAND",
158
+ "displayName": "Power",
159
+ "description": "Switch power.",
160
+ "visibility": "full",
161
+ "tag": "on/off"
162
+ }
163
+ ],
164
+ "entityEnabled": {
165
+ "basic.device.Device": true
166
+ }
167
+ },
168
+ {
169
+ "integrationId": "basic.device",
170
+ "integrationNames": {
171
+ "basic.device": "Devices",
172
+ "example.device": "Example Provider",
173
+ "other.device": "Other Provider"
174
+ },
175
+ "integrationIconUrls": {
176
+ "basic.device": "https://example.invalid/device.png",
177
+ "example.device": "https://example.invalid/provider.png"
178
+ },
179
+ "typeNames": {
180
+ "basic.device.Device": "Device",
181
+ "basic.device.Component": "Component"
182
+ },
183
+ "twinNames": {
184
+ "basic.device.Light": "Light"
185
+ },
186
+ "entityStandalone": {
187
+ "basic.device.Device": true,
188
+ "basic.device.Component": false
189
+ },
190
+ "groupSupported": {
191
+ "basic.device.Device": "yes"
192
+ },
193
+ "capabilities": [
194
+ {
195
+ "capabilityId": "basic.device.command.onOff",
196
+ "type": "COMMAND",
197
+ "displayName": "Power",
198
+ "description": "Switch power.",
199
+ "visibility": "full",
200
+ "tag": "on/off"
201
+ }
202
+ ]
203
+ }
204
+ ]
205
+ }
206
+ }