@pellux/goodvibes-sdk 0.25.21 → 0.26.1

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 (142) hide show
  1. package/dist/_internal/contracts/artifacts/operator-contract.json +5860 -982
  2. package/dist/_internal/contracts/generated/foundation-client-types.d.ts +90 -16
  3. package/dist/_internal/contracts/generated/foundation-client-types.d.ts.map +1 -1
  4. package/dist/_internal/contracts/generated/foundation-metadata.d.ts +2 -2
  5. package/dist/_internal/contracts/generated/foundation-metadata.js +2 -2
  6. package/dist/_internal/contracts/generated/operator-contract.d.ts.map +1 -1
  7. package/dist/_internal/contracts/generated/operator-contract.js +5860 -982
  8. package/dist/_internal/contracts/generated/operator-method-ids.d.ts +1 -1
  9. package/dist/_internal/contracts/generated/operator-method-ids.d.ts.map +1 -1
  10. package/dist/_internal/contracts/generated/operator-method-ids.js +18 -0
  11. package/dist/_internal/daemon/context.d.ts +1 -0
  12. package/dist/_internal/daemon/context.d.ts.map +1 -1
  13. package/dist/_internal/daemon/knowledge-route-types.d.ts +1 -0
  14. package/dist/_internal/daemon/knowledge-route-types.d.ts.map +1 -1
  15. package/dist/_internal/daemon/knowledge-routes.d.ts +1 -1
  16. package/dist/_internal/daemon/knowledge-routes.d.ts.map +1 -1
  17. package/dist/_internal/daemon/knowledge-routes.js +30 -0
  18. package/dist/_internal/daemon/operator.d.ts +1 -1
  19. package/dist/_internal/daemon/operator.d.ts.map +1 -1
  20. package/dist/_internal/daemon/operator.js +2 -0
  21. package/dist/_internal/daemon/runtime-session-routes.d.ts.map +1 -1
  22. package/dist/_internal/daemon/runtime-session-routes.js +3 -2
  23. package/dist/_internal/platform/agents/orchestrator-runner.d.ts.map +1 -1
  24. package/dist/_internal/platform/agents/orchestrator-runner.js +2 -1
  25. package/dist/_internal/platform/agents/orchestrator.d.ts +1 -0
  26. package/dist/_internal/platform/agents/orchestrator.d.ts.map +1 -1
  27. package/dist/_internal/platform/channels/builtin/homeassistant.d.ts.map +1 -1
  28. package/dist/_internal/platform/channels/builtin/homeassistant.js +11 -0
  29. package/dist/_internal/platform/companion/companion-chat-manager.d.ts.map +1 -1
  30. package/dist/_internal/platform/companion/companion-chat-manager.js +2 -1
  31. package/dist/_internal/platform/control-plane/method-catalog-control-automation.js +7 -7
  32. package/dist/_internal/platform/control-plane/method-catalog-control-core.d.ts.map +1 -1
  33. package/dist/_internal/platform/control-plane/method-catalog-control-core.js +8 -12
  34. package/dist/_internal/platform/control-plane/method-catalog-homegraph.d.ts +3 -0
  35. package/dist/_internal/platform/control-plane/method-catalog-homegraph.d.ts.map +1 -0
  36. package/dist/_internal/platform/control-plane/method-catalog-homegraph.js +223 -0
  37. package/dist/_internal/platform/control-plane/method-catalog-knowledge.d.ts.map +1 -1
  38. package/dist/_internal/platform/control-plane/method-catalog-knowledge.js +23 -1
  39. package/dist/_internal/platform/control-plane/operator-contract-schemas-knowledge.d.ts +15 -1
  40. package/dist/_internal/platform/control-plane/operator-contract-schemas-knowledge.d.ts.map +1 -1
  41. package/dist/_internal/platform/control-plane/operator-contract-schemas-knowledge.js +118 -0
  42. package/dist/_internal/platform/control-plane/operator-contract-schemas-runtime.d.ts +4 -0
  43. package/dist/_internal/platform/control-plane/operator-contract-schemas-runtime.d.ts.map +1 -1
  44. package/dist/_internal/platform/control-plane/operator-contract-schemas-runtime.js +11 -0
  45. package/dist/_internal/platform/core/orchestrator-turn-loop.d.ts.map +1 -1
  46. package/dist/_internal/platform/core/orchestrator-turn-loop.js +2 -1
  47. package/dist/_internal/platform/daemon/facade-composition.d.ts.map +1 -1
  48. package/dist/_internal/platform/daemon/facade-composition.js +2 -0
  49. package/dist/_internal/platform/daemon/facade-types.d.ts +2 -1
  50. package/dist/_internal/platform/daemon/facade-types.d.ts.map +1 -1
  51. package/dist/_internal/platform/daemon/http/home-graph-routes.d.ts +18 -0
  52. package/dist/_internal/platform/daemon/http/home-graph-routes.d.ts.map +1 -0
  53. package/dist/_internal/platform/daemon/http/home-graph-routes.js +108 -0
  54. package/dist/_internal/platform/daemon/http/router-route-contexts.d.ts.map +1 -1
  55. package/dist/_internal/platform/daemon/http/router-route-contexts.js +1 -0
  56. package/dist/_internal/platform/daemon/http/router.d.ts +4 -1
  57. package/dist/_internal/platform/daemon/http/router.d.ts.map +1 -1
  58. package/dist/_internal/platform/daemon/http/router.js +16 -0
  59. package/dist/_internal/platform/daemon/types.d.ts +2 -1
  60. package/dist/_internal/platform/daemon/types.d.ts.map +1 -1
  61. package/dist/_internal/platform/knowledge/browser-history/discover.d.ts +3 -0
  62. package/dist/_internal/platform/knowledge/browser-history/discover.d.ts.map +1 -0
  63. package/dist/_internal/platform/knowledge/browser-history/discover.js +88 -0
  64. package/dist/_internal/platform/knowledge/browser-history/index.d.ts +7 -0
  65. package/dist/_internal/platform/knowledge/browser-history/index.d.ts.map +1 -0
  66. package/dist/_internal/platform/knowledge/browser-history/index.js +4 -0
  67. package/dist/_internal/platform/knowledge/browser-history/ingest.d.ts +12 -0
  68. package/dist/_internal/platform/knowledge/browser-history/ingest.d.ts.map +1 -0
  69. package/dist/_internal/platform/knowledge/browser-history/ingest.js +275 -0
  70. package/dist/_internal/platform/knowledge/browser-history/locked-db.d.ts +7 -0
  71. package/dist/_internal/platform/knowledge/browser-history/locked-db.d.ts.map +1 -0
  72. package/dist/_internal/platform/knowledge/browser-history/locked-db.js +38 -0
  73. package/dist/_internal/platform/knowledge/browser-history/paths.d.ts +15 -0
  74. package/dist/_internal/platform/knowledge/browser-history/paths.d.ts.map +1 -0
  75. package/dist/_internal/platform/knowledge/browser-history/paths.js +125 -0
  76. package/dist/_internal/platform/knowledge/browser-history/readers.d.ts +8 -0
  77. package/dist/_internal/platform/knowledge/browser-history/readers.d.ts.map +1 -0
  78. package/dist/_internal/platform/knowledge/browser-history/readers.js +374 -0
  79. package/dist/_internal/platform/knowledge/browser-history/types.d.ts +50 -0
  80. package/dist/_internal/platform/knowledge/browser-history/types.d.ts.map +1 -0
  81. package/dist/_internal/platform/knowledge/browser-history/types.js +1 -0
  82. package/dist/_internal/platform/knowledge/consolidation.js +2 -0
  83. package/dist/_internal/platform/knowledge/extractors.d.ts.map +1 -1
  84. package/dist/_internal/platform/knowledge/extractors.js +31 -0
  85. package/dist/_internal/platform/knowledge/graphql-schema.d.ts +1 -1
  86. package/dist/_internal/platform/knowledge/graphql-schema.d.ts.map +1 -1
  87. package/dist/_internal/platform/knowledge/graphql-schema.js +5 -5
  88. package/dist/_internal/platform/knowledge/graphql.d.ts.map +1 -1
  89. package/dist/_internal/platform/knowledge/graphql.js +16 -5
  90. package/dist/_internal/platform/knowledge/home-graph/helpers.d.ts +46 -0
  91. package/dist/_internal/platform/knowledge/home-graph/helpers.d.ts.map +1 -0
  92. package/dist/_internal/platform/knowledge/home-graph/helpers.js +230 -0
  93. package/dist/_internal/platform/knowledge/home-graph/index.d.ts +4 -0
  94. package/dist/_internal/platform/knowledge/home-graph/index.d.ts.map +1 -0
  95. package/dist/_internal/platform/knowledge/home-graph/index.js +2 -0
  96. package/dist/_internal/platform/knowledge/home-graph/rendering.d.ts +25 -0
  97. package/dist/_internal/platform/knowledge/home-graph/rendering.d.ts.map +1 -0
  98. package/dist/_internal/platform/knowledge/home-graph/rendering.js +139 -0
  99. package/dist/_internal/platform/knowledge/home-graph/service.d.ts +82 -0
  100. package/dist/_internal/platform/knowledge/home-graph/service.d.ts.map +1 -0
  101. package/dist/_internal/platform/knowledge/home-graph/service.js +654 -0
  102. package/dist/_internal/platform/knowledge/home-graph/state.d.ts +24 -0
  103. package/dist/_internal/platform/knowledge/home-graph/state.d.ts.map +1 -0
  104. package/dist/_internal/platform/knowledge/home-graph/state.js +96 -0
  105. package/dist/_internal/platform/knowledge/home-graph/types.d.ts +206 -0
  106. package/dist/_internal/platform/knowledge/home-graph/types.d.ts.map +1 -0
  107. package/dist/_internal/platform/knowledge/home-graph/types.js +33 -0
  108. package/dist/_internal/platform/knowledge/html-readability.d.ts +13 -0
  109. package/dist/_internal/platform/knowledge/html-readability.d.ts.map +1 -0
  110. package/dist/_internal/platform/knowledge/html-readability.js +66 -0
  111. package/dist/_internal/platform/knowledge/index.d.ts +6 -0
  112. package/dist/_internal/platform/knowledge/index.d.ts.map +1 -1
  113. package/dist/_internal/platform/knowledge/index.js +3 -0
  114. package/dist/_internal/platform/knowledge/internal.d.ts.map +1 -1
  115. package/dist/_internal/platform/knowledge/internal.js +1 -0
  116. package/dist/_internal/platform/knowledge/knowledge-api.d.ts +2 -0
  117. package/dist/_internal/platform/knowledge/knowledge-api.d.ts.map +1 -1
  118. package/dist/_internal/platform/knowledge/knowledge-api.js +1 -0
  119. package/dist/_internal/platform/knowledge/lint.js +1 -0
  120. package/dist/_internal/platform/knowledge/scheduling.d.ts.map +1 -1
  121. package/dist/_internal/platform/knowledge/scheduling.js +1 -0
  122. package/dist/_internal/platform/knowledge/service.d.ts +4 -0
  123. package/dist/_internal/platform/knowledge/service.d.ts.map +1 -1
  124. package/dist/_internal/platform/knowledge/service.js +15 -0
  125. package/dist/_internal/platform/knowledge/spaces.d.ts +19 -0
  126. package/dist/_internal/platform/knowledge/spaces.d.ts.map +1 -0
  127. package/dist/_internal/platform/knowledge/spaces.js +54 -0
  128. package/dist/_internal/platform/knowledge/types.d.ts +3 -3
  129. package/dist/_internal/platform/knowledge/types.d.ts.map +1 -1
  130. package/dist/_internal/platform/permissions/manager.d.ts.map +1 -1
  131. package/dist/_internal/platform/permissions/manager.js +4 -0
  132. package/dist/_internal/platform/runtime/services.d.ts +2 -1
  133. package/dist/_internal/platform/runtime/services.d.ts.map +1 -1
  134. package/dist/_internal/platform/runtime/services.js +4 -1
  135. package/dist/_internal/platform/tools/goodvibes-runtime/index.d.ts +23 -0
  136. package/dist/_internal/platform/tools/goodvibes-runtime/index.d.ts.map +1 -0
  137. package/dist/_internal/platform/tools/goodvibes-runtime/index.js +400 -0
  138. package/dist/_internal/platform/tools/index.d.ts +3 -1
  139. package/dist/_internal/platform/tools/index.d.ts.map +1 -1
  140. package/dist/_internal/platform/tools/index.js +15 -0
  141. package/dist/_internal/platform/version.js +1 -1
  142. package/package.json +4 -1
@@ -0,0 +1,654 @@
1
+ import { extractKnowledgeArtifact } from '../extractors.js';
2
+ import { HOME_GRAPH_CONNECTOR_ID, belongsToSpace, buildHomeGraphMetadata, buildHomeGraphNodeInput, buildIssue, edgeIsActive, homeGraphNodeId, homeGraphSourceId, namespacedCanonicalUri, nodeKindForHomeGraphObject, resolveHomeGraphSpace, scoreHomeGraphResults, targetToReference, uniqueStrings, } from './helpers.js';
3
+ import { collectLinkedObjects, findHomeAssistantNode, inferHomeGraphSourceType, missingDevicePassportFields, readHomeGraphState, renderAskAnswer, renderHomeGraphState, safeHomeGraphFilename, sourcesLinkedToNode, } from './state.js';
4
+ import { renderDevicePassportPage, renderPacketPage, renderRoomPage, } from './rendering.js';
5
+ import { HOME_GRAPH_CAPABILITIES } from './types.js';
6
+ export class HomeGraphService {
7
+ store;
8
+ artifactStore;
9
+ constructor(store, artifactStore) {
10
+ this.store = store;
11
+ this.artifactStore = artifactStore;
12
+ }
13
+ async status(input = {}) {
14
+ await this.store.init();
15
+ const { spaceId, installationId } = resolveHomeGraphSpace(input);
16
+ const state = readHomeGraphState(this.store, spaceId);
17
+ const snapshotSources = state.sources
18
+ .filter((source) => source.metadata.homeGraphSourceKind === 'snapshot')
19
+ .sort((a, b) => b.updatedAt - a.updatedAt);
20
+ return {
21
+ ok: true,
22
+ spaceId,
23
+ installationId,
24
+ sourceCount: state.sources.length,
25
+ nodeCount: state.nodes.length,
26
+ edgeCount: state.edges.length,
27
+ issueCount: state.issues.length,
28
+ extractionCount: state.extractions.length,
29
+ ...(snapshotSources[0]?.updatedAt ? { lastSnapshotAt: snapshotSources[0].updatedAt } : {}),
30
+ capabilities: HOME_GRAPH_CAPABILITIES,
31
+ };
32
+ }
33
+ async syncSnapshot(input) {
34
+ await this.store.init();
35
+ const { spaceId, installationId } = resolveHomeGraphSpace(input);
36
+ const capturedAt = input.capturedAt ?? Date.now();
37
+ const source = await this.store.upsertSource({
38
+ id: homeGraphSourceId(spaceId, 'snapshot', String(capturedAt)),
39
+ connectorId: HOME_GRAPH_CONNECTOR_ID,
40
+ sourceType: 'dataset',
41
+ title: input.title ?? 'Home Assistant snapshot',
42
+ canonicalUri: namespacedCanonicalUri(spaceId, 'snapshot', String(capturedAt)),
43
+ summary: 'Home Assistant entity, device, area, automation, script, scene, label, and integration snapshot.',
44
+ tags: ['homeassistant', 'home-graph', 'snapshot'],
45
+ status: 'indexed',
46
+ lastCrawledAt: capturedAt,
47
+ metadata: buildHomeGraphMetadata(spaceId, installationId, {
48
+ ...(input.metadata ?? {}),
49
+ homeGraphSourceKind: 'snapshot',
50
+ capturedAt,
51
+ }),
52
+ });
53
+ const home = await this.upsertHomeNode(spaceId, installationId, input);
54
+ const beforeNodeIds = new Set(readHomeGraphState(this.store, spaceId).nodes.map((node) => node.id));
55
+ const beforeEdgeIds = new Set(readHomeGraphState(this.store, spaceId).edges.map((edge) => edge.id));
56
+ const groups = await this.upsertSnapshotObjects(spaceId, installationId, input, home.id, source.id);
57
+ const issues = await this.refreshQualityIssues(spaceId, installationId);
58
+ const after = readHomeGraphState(this.store, spaceId);
59
+ return {
60
+ ok: true,
61
+ spaceId,
62
+ installationId,
63
+ source,
64
+ home,
65
+ created: {
66
+ nodes: after.nodes.filter((node) => !beforeNodeIds.has(node.id)).length,
67
+ edges: after.edges.filter((edge) => !beforeEdgeIds.has(edge.id)).length,
68
+ issues: issues.length,
69
+ },
70
+ counts: groups,
71
+ };
72
+ }
73
+ async ingestUrl(input) {
74
+ const { spaceId, installationId } = resolveHomeGraphSpace(input);
75
+ const artifact = await this.artifactStore.create({
76
+ uri: input.url,
77
+ allowPrivateHosts: input.allowPrivateHosts,
78
+ metadata: buildHomeGraphMetadata(spaceId, installationId, {
79
+ homeGraphSourceKind: 'url',
80
+ requestedAt: Date.now(),
81
+ }),
82
+ });
83
+ return this.ingestCreatedArtifact({
84
+ spaceId,
85
+ installationId,
86
+ artifact,
87
+ title: input.title,
88
+ sourceUri: input.url,
89
+ sourceType: inferHomeGraphSourceType(input.tags, 'url'),
90
+ tags: ['homeassistant', 'home-graph', ...(input.tags ?? [])],
91
+ target: input.target,
92
+ metadata: {
93
+ ...(input.metadata ?? {}),
94
+ homeGraphSourceKind: 'url',
95
+ },
96
+ });
97
+ }
98
+ async ingestNote(input) {
99
+ const { spaceId, installationId } = resolveHomeGraphSpace(input);
100
+ const title = input.title ?? `Home Assistant note ${new Date().toISOString()}`;
101
+ const artifact = await this.artifactStore.create({
102
+ kind: 'document',
103
+ mimeType: 'text/markdown',
104
+ filename: `${safeHomeGraphFilename(title)}.md`,
105
+ text: input.body,
106
+ metadata: buildHomeGraphMetadata(spaceId, installationId, {
107
+ homeGraphSourceKind: 'note',
108
+ category: input.category ?? 'note',
109
+ }),
110
+ });
111
+ return this.ingestCreatedArtifact({
112
+ spaceId,
113
+ installationId,
114
+ artifact,
115
+ title,
116
+ sourceType: 'document',
117
+ tags: uniqueStrings(['homeassistant', 'home-graph', 'note', input.category, ...(input.tags ?? [])]),
118
+ target: input.target,
119
+ metadata: {
120
+ ...(input.metadata ?? {}),
121
+ homeGraphSourceKind: 'note',
122
+ category: input.category ?? 'note',
123
+ },
124
+ });
125
+ }
126
+ async ingestArtifact(input) {
127
+ const { spaceId, installationId } = resolveHomeGraphSpace(input);
128
+ const artifact = input.artifactId
129
+ ? this.artifactStore.get(input.artifactId)
130
+ : await this.artifactStore.create({
131
+ path: input.path,
132
+ uri: input.uri,
133
+ allowPrivateHosts: input.allowPrivateHosts,
134
+ metadata: buildHomeGraphMetadata(spaceId, installationId, {
135
+ homeGraphSourceKind: 'artifact',
136
+ requestedAt: Date.now(),
137
+ }),
138
+ });
139
+ if (!artifact)
140
+ throw new Error('Unknown Home Graph artifact.');
141
+ return this.ingestCreatedArtifact({
142
+ spaceId,
143
+ installationId,
144
+ artifact,
145
+ title: input.title,
146
+ sourceUri: input.uri ?? input.path ?? artifact.sourceUri,
147
+ sourceType: inferHomeGraphSourceType(input.tags, 'document'),
148
+ tags: ['homeassistant', 'home-graph', 'artifact', ...(input.tags ?? [])],
149
+ target: input.target,
150
+ metadata: {
151
+ ...(input.metadata ?? {}),
152
+ homeGraphSourceKind: 'artifact',
153
+ },
154
+ });
155
+ }
156
+ async linkKnowledge(input) {
157
+ await this.store.init();
158
+ const { spaceId, installationId } = resolveHomeGraphSpace(input);
159
+ const from = this.resolveLinkSource(spaceId, input);
160
+ const target = await this.ensureTarget(spaceId, installationId, input.target);
161
+ const relation = input.relation ?? input.target.relation ?? 'source_for';
162
+ const edge = await this.store.upsertEdge({
163
+ fromKind: from.kind,
164
+ fromId: from.id,
165
+ toKind: target.kind,
166
+ toId: target.id,
167
+ relation,
168
+ metadata: buildHomeGraphMetadata(spaceId, installationId, {
169
+ ...(input.metadata ?? {}),
170
+ linkStatus: typeof input.metadata?.linkStatus === 'string' ? input.metadata.linkStatus : 'active',
171
+ }),
172
+ });
173
+ return { ok: true, spaceId, edge, target: target.record };
174
+ }
175
+ async unlinkKnowledge(input) {
176
+ const linked = await this.linkKnowledge({
177
+ ...input,
178
+ metadata: {
179
+ ...(input.metadata ?? {}),
180
+ linkStatus: 'unlinked',
181
+ unlinkedAt: Date.now(),
182
+ },
183
+ });
184
+ return { ...linked, edge: linked.edge };
185
+ }
186
+ async ask(input) {
187
+ await this.store.init();
188
+ const { spaceId } = resolveHomeGraphSpace(input);
189
+ const state = readHomeGraphState(this.store, spaceId);
190
+ const results = scoreHomeGraphResults(input.query, state.sources, state.nodes, (sourceId) => this.store.getExtractionBySourceId(sourceId), input.limit ?? 8);
191
+ const sources = results.flatMap((result) => result.source ? [result.source] : []);
192
+ const linkedObjects = collectLinkedObjects(results, state);
193
+ const confidence = Math.min(100, Math.max(10, results[0]?.score ?? 10));
194
+ return {
195
+ ok: true,
196
+ spaceId,
197
+ query: input.query,
198
+ answer: {
199
+ text: renderAskAnswer(input.query, results, input.mode ?? 'standard'),
200
+ mode: input.mode ?? 'standard',
201
+ confidence,
202
+ sources: input.includeSources === false ? [] : sources,
203
+ linkedObjects: input.includeLinkedObjects === false ? [] : linkedObjects,
204
+ },
205
+ results,
206
+ };
207
+ }
208
+ async refreshDevicePassport(input) {
209
+ await this.store.init();
210
+ const { spaceId, installationId } = resolveHomeGraphSpace(input);
211
+ if (!input.deviceId)
212
+ throw new Error('refreshDevicePassport requires deviceId.');
213
+ const state = readHomeGraphState(this.store, spaceId);
214
+ const device = findHomeAssistantNode(state.nodes, 'ha_device', input.deviceId);
215
+ if (!device)
216
+ throw new Error(`Unknown Home Assistant device: ${input.deviceId}`);
217
+ const entities = state.nodes.filter((node) => (node.kind === 'ha_entity' && state.edges.some((edge) => (edgeIsActive(edge)
218
+ && edge.fromKind === 'node'
219
+ && edge.fromId === node.id
220
+ && edge.toKind === 'node'
221
+ && edge.toId === device.id
222
+ && edge.relation === 'belongs_to_device'))));
223
+ const sources = sourcesLinkedToNode(device.id, state);
224
+ const issues = state.issues.filter((issue) => issue.nodeId === device.id);
225
+ const missingFields = missingDevicePassportFields(device, sources);
226
+ const passport = await this.store.upsertNode({
227
+ id: homeGraphNodeId(spaceId, 'ha_device_passport', input.deviceId),
228
+ kind: 'ha_device_passport',
229
+ slug: `${device.slug}-passport`,
230
+ title: `${device.title} passport`,
231
+ summary: `Living device profile for ${device.title}.`,
232
+ aliases: [`${device.title} passport`],
233
+ confidence: 80,
234
+ metadata: buildHomeGraphMetadata(spaceId, installationId, {
235
+ homeAssistant: { installationId, objectKind: 'device_passport', objectId: input.deviceId },
236
+ deviceId: input.deviceId,
237
+ missingFields,
238
+ refreshedAt: Date.now(),
239
+ }),
240
+ });
241
+ await this.store.upsertEdge({
242
+ fromKind: 'node',
243
+ fromId: passport.id,
244
+ toKind: 'node',
245
+ toId: device.id,
246
+ relation: 'source_for',
247
+ metadata: buildHomeGraphMetadata(spaceId, installationId),
248
+ });
249
+ const markdown = renderDevicePassportPage({ spaceId, device, entities, sources, issues, missingFields });
250
+ const artifact = await this.materializeMarkdown(spaceId, installationId, `${safeHomeGraphFilename(device.title)}-passport.md`, markdown, {
251
+ projectionKind: 'device-passport',
252
+ deviceId: input.deviceId,
253
+ });
254
+ return {
255
+ ok: true,
256
+ spaceId,
257
+ title: `${device.title} passport`,
258
+ markdown,
259
+ artifact,
260
+ device,
261
+ passport,
262
+ missingFields,
263
+ };
264
+ }
265
+ async generateRoomPage(input) {
266
+ await this.store.init();
267
+ const { spaceId, installationId } = resolveHomeGraphSpace(input);
268
+ const state = renderHomeGraphState(this.store, spaceId, input.title ?? 'Home Graph Room');
269
+ const markdown = renderRoomPage(state, input.areaId ?? input.roomId);
270
+ const filename = `${safeHomeGraphFilename(input.title ?? input.areaId ?? input.roomId ?? 'room')}.md`;
271
+ const artifact = await this.materializeMarkdown(spaceId, installationId, filename, markdown, {
272
+ projectionKind: 'room-page',
273
+ areaId: input.areaId ?? input.roomId,
274
+ });
275
+ return { ok: true, spaceId, title: input.title ?? 'Home Graph Room', markdown, artifact };
276
+ }
277
+ async generatePacket(input) {
278
+ await this.store.init();
279
+ const { spaceId, installationId } = resolveHomeGraphSpace(input);
280
+ const title = input.title ?? `${input.packetKind ?? 'home'} packet`;
281
+ const markdown = renderPacketPage(renderHomeGraphState(this.store, spaceId, title), input);
282
+ const artifact = await this.materializeMarkdown(spaceId, installationId, `${safeHomeGraphFilename(title)}.md`, markdown, {
283
+ projectionKind: 'packet',
284
+ packetKind: input.packetKind ?? 'home',
285
+ sharingProfile: input.sharingProfile ?? 'default',
286
+ includeFields: input.includeFields ? [...input.includeFields] : [],
287
+ excludeFields: input.excludeFields ? [...input.excludeFields] : [],
288
+ });
289
+ return { ok: true, spaceId, title, markdown, artifact };
290
+ }
291
+ async listIssues(input) {
292
+ const { spaceId } = resolveHomeGraphSpace(input);
293
+ const limit = Math.max(1, input.limit ?? 100);
294
+ const issues = readHomeGraphState(this.store, spaceId).issues
295
+ .filter((issue) => !input.status || issue.status === input.status)
296
+ .filter((issue) => !input.severity || issue.severity === input.severity)
297
+ .filter((issue) => !input.code || issue.code === input.code)
298
+ .slice(0, limit);
299
+ return { ok: true, spaceId, issues };
300
+ }
301
+ async reviewFact(input) {
302
+ const { spaceId, installationId } = resolveHomeGraphSpace(input);
303
+ const reviewedAt = Date.now();
304
+ const metadata = buildHomeGraphMetadata(spaceId, installationId, {
305
+ review: {
306
+ action: input.action,
307
+ reviewer: input.reviewer ?? 'homeassistant',
308
+ reviewedAt,
309
+ ...(input.value ? { value: input.value } : {}),
310
+ },
311
+ });
312
+ if (input.issueId) {
313
+ const issue = this.store.getIssue(input.issueId);
314
+ if (!issue || !belongsToSpace(issue, spaceId))
315
+ throw new Error(`Unknown Home Graph issue: ${input.issueId}`);
316
+ const updated = await this.store.upsertIssue({
317
+ id: issue.id,
318
+ severity: issue.severity,
319
+ code: issue.code,
320
+ message: issue.message,
321
+ status: input.action === 'reject' || input.action === 'resolve' || input.action === 'accept' ? 'resolved' : issue.status,
322
+ sourceId: issue.sourceId,
323
+ nodeId: issue.nodeId,
324
+ metadata,
325
+ });
326
+ return { ok: true, spaceId, issue: updated };
327
+ }
328
+ if (input.nodeId) {
329
+ const node = this.store.getNode(input.nodeId);
330
+ if (!node || !belongsToSpace(node, spaceId))
331
+ throw new Error(`Unknown Home Graph node: ${input.nodeId}`);
332
+ const updated = await this.store.upsertNode({
333
+ id: node.id,
334
+ kind: node.kind,
335
+ slug: node.slug,
336
+ title: node.title,
337
+ summary: node.summary,
338
+ aliases: node.aliases,
339
+ status: input.action === 'forget' ? 'stale' : node.status,
340
+ confidence: input.action === 'accept' ? 100 : node.confidence,
341
+ sourceId: node.sourceId,
342
+ metadata,
343
+ });
344
+ return { ok: true, spaceId, node: updated };
345
+ }
346
+ if (input.sourceId) {
347
+ const source = this.store.getSource(input.sourceId);
348
+ if (!source || !belongsToSpace(source, spaceId))
349
+ throw new Error(`Unknown Home Graph source: ${input.sourceId}`);
350
+ const updated = await this.store.upsertSource({
351
+ id: source.id,
352
+ connectorId: source.connectorId,
353
+ sourceType: source.sourceType,
354
+ title: source.title,
355
+ sourceUri: source.sourceUri,
356
+ canonicalUri: source.canonicalUri,
357
+ summary: source.summary,
358
+ description: source.description,
359
+ tags: source.tags,
360
+ folderPath: source.folderPath,
361
+ status: input.action === 'forget' ? 'stale' : source.status,
362
+ artifactId: source.artifactId,
363
+ contentHash: source.contentHash,
364
+ lastCrawledAt: source.lastCrawledAt,
365
+ crawlError: source.crawlError,
366
+ sessionId: source.sessionId,
367
+ metadata,
368
+ });
369
+ return { ok: true, spaceId, source: updated };
370
+ }
371
+ throw new Error('reviewFact requires issueId, nodeId, or sourceId.');
372
+ }
373
+ async listSources(input = {}) {
374
+ await this.store.init();
375
+ const { spaceId } = resolveHomeGraphSpace(input);
376
+ return { ok: true, spaceId, sources: readHomeGraphState(this.store, spaceId).sources.slice(0, Math.max(1, input.limit ?? 100)) };
377
+ }
378
+ async browse(input = {}) {
379
+ await this.store.init();
380
+ const { spaceId } = resolveHomeGraphSpace(input);
381
+ const limit = Math.max(1, input.limit ?? 250);
382
+ const state = readHomeGraphState(this.store, spaceId);
383
+ return {
384
+ ok: true,
385
+ spaceId,
386
+ nodes: state.nodes.slice(0, limit),
387
+ edges: state.edges.slice(0, limit),
388
+ sources: state.sources.slice(0, limit),
389
+ issues: state.issues.slice(0, limit),
390
+ };
391
+ }
392
+ async exportSpace(input = {}) {
393
+ await this.store.init();
394
+ const { spaceId, installationId } = resolveHomeGraphSpace(input);
395
+ const state = readHomeGraphState(this.store, spaceId);
396
+ return {
397
+ version: 1,
398
+ exportedAt: Date.now(),
399
+ spaceId,
400
+ installationId,
401
+ sources: state.sources,
402
+ nodes: state.nodes,
403
+ edges: state.edges,
404
+ issues: state.issues,
405
+ extractions: state.extractions,
406
+ };
407
+ }
408
+ async importSpace(input) {
409
+ const { spaceId, installationId } = resolveHomeGraphSpace(input);
410
+ const data = input.data;
411
+ let sources = 0;
412
+ let nodes = 0;
413
+ let edges = 0;
414
+ let issues = 0;
415
+ let extractions = 0;
416
+ for (const source of data.sources ?? []) {
417
+ await this.store.upsertSource({ ...source, metadata: buildHomeGraphMetadata(spaceId, installationId, source.metadata) });
418
+ sources += 1;
419
+ }
420
+ for (const node of data.nodes ?? []) {
421
+ await this.store.upsertNode({ ...node, metadata: buildHomeGraphMetadata(spaceId, installationId, node.metadata) });
422
+ nodes += 1;
423
+ }
424
+ for (const edge of data.edges ?? []) {
425
+ await this.store.upsertEdge({ ...edge, metadata: buildHomeGraphMetadata(spaceId, installationId, edge.metadata) });
426
+ edges += 1;
427
+ }
428
+ for (const issue of data.issues ?? []) {
429
+ await this.store.upsertIssue({ ...issue, metadata: buildHomeGraphMetadata(spaceId, installationId, issue.metadata) });
430
+ issues += 1;
431
+ }
432
+ for (const extraction of data.extractions ?? []) {
433
+ await this.store.upsertExtraction({ ...extraction, metadata: buildHomeGraphMetadata(spaceId, installationId, extraction.metadata) });
434
+ extractions += 1;
435
+ }
436
+ return { ok: true, spaceId, imported: { sources, nodes, edges, issues, extractions } };
437
+ }
438
+ async ingestCreatedArtifact(input) {
439
+ const sourceId = homeGraphSourceId(input.spaceId, input.metadata.homeGraphSourceKind, input.sourceUri ?? input.artifact.id);
440
+ const source = await this.store.upsertSource({
441
+ id: sourceId,
442
+ connectorId: HOME_GRAPH_CONNECTOR_ID,
443
+ sourceType: input.sourceType,
444
+ title: input.title ?? input.artifact.filename,
445
+ sourceUri: input.sourceUri ?? input.artifact.sourceUri,
446
+ canonicalUri: namespacedCanonicalUri(input.spaceId, 'source', input.sourceUri ?? input.artifact.id),
447
+ tags: uniqueStrings(input.tags),
448
+ status: 'indexed',
449
+ artifactId: input.artifact.id,
450
+ lastCrawledAt: Date.now(),
451
+ metadata: buildHomeGraphMetadata(input.spaceId, input.installationId, {
452
+ ...input.metadata,
453
+ artifactMimeType: input.artifact.mimeType,
454
+ }),
455
+ });
456
+ const extraction = await this.extractArtifact(source, input.artifact, input.spaceId, input.installationId);
457
+ const linked = input.target
458
+ ? (await this.linkKnowledge({ knowledgeSpaceId: input.spaceId, sourceId: source.id, target: input.target })).edge
459
+ : undefined;
460
+ return {
461
+ ok: true,
462
+ spaceId: input.spaceId,
463
+ source,
464
+ artifactId: input.artifact.id,
465
+ extraction,
466
+ ...(linked ? { linked } : {}),
467
+ };
468
+ }
469
+ async extractArtifact(source, artifact, spaceId, installationId) {
470
+ try {
471
+ const record = this.artifactStore.getRecord(artifact.id);
472
+ if (!record)
473
+ return undefined;
474
+ const { buffer } = await this.artifactStore.readContent(artifact.id);
475
+ const extracted = await extractKnowledgeArtifact(record, buffer);
476
+ return this.store.upsertExtraction({
477
+ id: `hg-extract-${source.id.replace(/^hg-src-/, '')}`,
478
+ sourceId: source.id,
479
+ artifactId: artifact.id,
480
+ extractorId: extracted.extractorId,
481
+ format: extracted.format,
482
+ title: extracted.title,
483
+ summary: extracted.summary,
484
+ excerpt: extracted.excerpt,
485
+ sections: extracted.sections,
486
+ links: extracted.links,
487
+ estimatedTokens: extracted.estimatedTokens,
488
+ structure: extracted.structure,
489
+ metadata: buildHomeGraphMetadata(spaceId, installationId, extracted.metadata),
490
+ });
491
+ }
492
+ catch {
493
+ return undefined;
494
+ }
495
+ }
496
+ async upsertHomeNode(spaceId, installationId, input) {
497
+ return this.store.upsertNode({
498
+ id: homeGraphNodeId(spaceId, 'ha_home', input.homeId ?? installationId),
499
+ kind: 'ha_home',
500
+ slug: `${spaceId.replace(/[^a-z0-9]+/gi, '-')}-home`,
501
+ title: input.title ?? 'Home Assistant',
502
+ summary: 'Home Assistant installation captured in the GoodVibes Home Graph.',
503
+ aliases: [installationId],
504
+ confidence: 100,
505
+ metadata: buildHomeGraphMetadata(spaceId, installationId, {
506
+ homeAssistant: { installationId, objectKind: 'home', objectId: input.homeId ?? installationId },
507
+ }),
508
+ });
509
+ }
510
+ async upsertSnapshotObjects(spaceId, installationId, input, homeNodeId, sourceId) {
511
+ const counts = {
512
+ entities: 0,
513
+ devices: 0,
514
+ areas: 0,
515
+ automations: 0,
516
+ scripts: 0,
517
+ scenes: 0,
518
+ labels: 0,
519
+ integrations: 0,
520
+ };
521
+ const upsertGroup = async (kind, objects) => {
522
+ for (const object of objects ?? []) {
523
+ const nodeInput = buildHomeGraphNodeInput(spaceId, installationId, kind, object);
524
+ const node = await this.store.upsertNode({ ...nodeInput, sourceId, confidence: 90 });
525
+ await this.store.upsertEdge({
526
+ fromKind: 'node',
527
+ fromId: node.id,
528
+ toKind: 'node',
529
+ toId: homeNodeId,
530
+ relation: 'source_for',
531
+ metadata: buildHomeGraphMetadata(spaceId, installationId),
532
+ });
533
+ await this.linkSnapshotObjectRelations(spaceId, installationId, node, object);
534
+ }
535
+ };
536
+ await upsertGroup('area', input.areas);
537
+ counts.areas = input.areas?.length ?? 0;
538
+ await upsertGroup('integration', input.integrations);
539
+ counts.integrations = input.integrations?.length ?? 0;
540
+ await upsertGroup('device', input.devices);
541
+ counts.devices = input.devices?.length ?? 0;
542
+ await upsertGroup('entity', input.entities);
543
+ counts.entities = input.entities?.length ?? 0;
544
+ await upsertGroup('automation', input.automations);
545
+ counts.automations = input.automations?.length ?? 0;
546
+ await upsertGroup('script', input.scripts);
547
+ counts.scripts = input.scripts?.length ?? 0;
548
+ await upsertGroup('scene', input.scenes);
549
+ counts.scenes = input.scenes?.length ?? 0;
550
+ await upsertGroup('label', input.labels);
551
+ counts.labels = input.labels?.length ?? 0;
552
+ return counts;
553
+ }
554
+ async linkSnapshotObjectRelations(spaceId, installationId, node, object) {
555
+ if (object.deviceId && node.kind !== 'ha_device') {
556
+ await this.tryLinkNode(spaceId, installationId, node.id, 'belongs_to_device', 'ha_device', object.deviceId);
557
+ }
558
+ if (object.areaId) {
559
+ await this.tryLinkNode(spaceId, installationId, node.id, 'located_in', 'ha_area', object.areaId);
560
+ }
561
+ if (object.integrationId) {
562
+ await this.tryLinkNode(spaceId, installationId, node.id, 'connected_via', 'ha_integration', object.integrationId);
563
+ }
564
+ }
565
+ async tryLinkNode(spaceId, installationId, fromId, relation, toKind, toObjectId) {
566
+ const toId = homeGraphNodeId(spaceId, toKind, toObjectId);
567
+ if (!this.store.getNode(toId))
568
+ return;
569
+ await this.store.upsertEdge({
570
+ fromKind: 'node',
571
+ fromId,
572
+ toKind: 'node',
573
+ toId,
574
+ relation,
575
+ metadata: buildHomeGraphMetadata(spaceId, installationId),
576
+ });
577
+ }
578
+ async refreshQualityIssues(spaceId, installationId) {
579
+ const state = readHomeGraphState(this.store, spaceId);
580
+ const inputs = [
581
+ ...state.nodes
582
+ .filter((node) => node.kind === 'ha_device')
583
+ .filter((node) => sourcesLinkedToNode(node.id, state).length === 0)
584
+ .map((node) => buildIssue(spaceId, installationId, 'homegraph.device.missing_manual', `${node.title} has no linked manual or source.`, { nodeId: node.id })),
585
+ ...state.nodes
586
+ .filter((node) => node.kind === 'ha_device')
587
+ .filter((node) => typeof node.metadata.batteryType !== 'string')
588
+ .map((node) => buildIssue(spaceId, installationId, 'homegraph.device.unknown_battery', `${node.title} has no known battery type.`, { nodeId: node.id })),
589
+ ];
590
+ return this.store.replaceIssues(inputs, `homegraph:${spaceId}:quality`);
591
+ }
592
+ resolveLinkSource(spaceId, input) {
593
+ if (input.sourceId) {
594
+ const source = this.store.getSource(input.sourceId);
595
+ if (!source || !belongsToSpace(source, spaceId))
596
+ throw new Error(`Unknown Home Graph source: ${input.sourceId}`);
597
+ return { kind: 'source', id: source.id };
598
+ }
599
+ if (input.nodeId) {
600
+ const node = this.store.getNode(input.nodeId);
601
+ if (!node || !belongsToSpace(node, spaceId))
602
+ throw new Error(`Unknown Home Graph node: ${input.nodeId}`);
603
+ return { kind: 'node', id: node.id };
604
+ }
605
+ throw new Error('linkKnowledge requires sourceId or nodeId.');
606
+ }
607
+ async ensureTarget(spaceId, installationId, target) {
608
+ const ref = targetToReference(target);
609
+ if (ref.kind === 'source') {
610
+ const source = this.store.getSource(ref.id);
611
+ if (!source || !belongsToSpace(source, spaceId))
612
+ throw new Error(`Unknown Home Graph source target: ${ref.id}`);
613
+ return { kind: 'source', id: source.id, record: source };
614
+ }
615
+ const existing = this.store.getNode(ref.id);
616
+ if (existing && belongsToSpace(existing, spaceId))
617
+ return { kind: 'node', id: existing.id, record: existing };
618
+ const kind = ref.nodeKind ?? nodeKindForHomeGraphObject(target.kind);
619
+ const deterministicId = homeGraphNodeId(spaceId, kind, ref.id);
620
+ const deterministic = this.store.getNode(deterministicId);
621
+ if (deterministic && belongsToSpace(deterministic, spaceId)) {
622
+ return { kind: 'node', id: deterministic.id, record: deterministic };
623
+ }
624
+ const nodeInput = {
625
+ id: ref.id.startsWith('hg-node-') ? ref.id : deterministicId,
626
+ kind,
627
+ slug: `${spaceId.replace(/[^a-z0-9]+/gi, '-')}-${kind}-${target.id.replace(/[^a-z0-9]+/gi, '-')}`,
628
+ title: target.title ?? target.id,
629
+ aliases: [target.id],
630
+ confidence: 60,
631
+ metadata: buildHomeGraphMetadata(spaceId, installationId, {
632
+ homeAssistant: { installationId, objectKind: kind, objectId: target.id },
633
+ }),
634
+ };
635
+ const node = await this.store.upsertNode(nodeInput);
636
+ return { kind: 'node', id: node.id, record: node };
637
+ }
638
+ async materializeMarkdown(spaceId, installationId, filename, markdown, metadata) {
639
+ const artifact = await this.artifactStore.create({
640
+ kind: 'document',
641
+ mimeType: 'text/markdown',
642
+ filename,
643
+ text: markdown,
644
+ metadata: buildHomeGraphMetadata(spaceId, installationId, metadata),
645
+ });
646
+ return {
647
+ id: artifact.id,
648
+ mimeType: artifact.mimeType,
649
+ filename: artifact.filename,
650
+ createdAt: artifact.createdAt,
651
+ metadata: artifact.metadata,
652
+ };
653
+ }
654
+ }
@@ -0,0 +1,24 @@
1
+ import type { KnowledgeStore } from '../store.js';
2
+ import type { KnowledgeEdgeRecord, KnowledgeExtractionRecord, KnowledgeNodeRecord, KnowledgeSourceRecord, KnowledgeSourceType } from '../types.js';
3
+ import type { HomeGraphRenderState } from './rendering.js';
4
+ export interface HomeGraphState extends Omit<HomeGraphRenderState, 'title'> {
5
+ readonly extractions: readonly KnowledgeExtractionRecord[];
6
+ }
7
+ export declare function readHomeGraphState(store: KnowledgeStore, spaceId: string): HomeGraphState;
8
+ export declare function renderHomeGraphState(store: KnowledgeStore, spaceId: string, title: string): HomeGraphRenderState;
9
+ export declare function sourcesLinkedToNode(nodeId: string, state: HomeGraphState): KnowledgeSourceRecord[];
10
+ export declare function collectLinkedObjects(results: readonly {
11
+ readonly source?: KnowledgeSourceRecord;
12
+ readonly node?: KnowledgeNodeRecord;
13
+ }[], state: HomeGraphState): KnowledgeNodeRecord[];
14
+ export declare function missingDevicePassportFields(device: KnowledgeNodeRecord, sources: readonly KnowledgeSourceRecord[]): string[];
15
+ export declare function findHomeAssistantNode(nodes: readonly KnowledgeNodeRecord[], kind: string, id: string): KnowledgeNodeRecord | undefined;
16
+ export declare function inferHomeGraphSourceType(tags: readonly string[] | undefined, fallback: KnowledgeSourceType): KnowledgeSourceType;
17
+ export declare function safeHomeGraphFilename(value: string): string;
18
+ export declare function renderAskAnswer(query: string, results: readonly {
19
+ readonly title: string;
20
+ readonly summary?: string;
21
+ readonly source?: KnowledgeSourceRecord;
22
+ }[], mode: 'concise' | 'standard' | 'detailed'): string;
23
+ export declare function edgeConnectsNode(edge: KnowledgeEdgeRecord, nodeId: string, relation: string, toId: string): boolean;
24
+ //# sourceMappingURL=state.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../../../../src/_internal/platform/knowledge/home-graph/state.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,KAAK,EACV,mBAAmB,EACnB,yBAAyB,EAEzB,mBAAmB,EACnB,qBAAqB,EACrB,mBAAmB,EACpB,MAAM,aAAa,CAAC;AAErB,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAE3D,MAAM,WAAW,cAAe,SAAQ,IAAI,CAAC,oBAAoB,EAAE,OAAO,CAAC;IACzE,QAAQ,CAAC,WAAW,EAAE,SAAS,yBAAyB,EAAE,CAAC;CAC5D;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,GAAG,cAAc,CAkBzF;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,oBAAoB,CAUhH;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,cAAc,GAAG,qBAAqB,EAAE,CAKlG;AAED,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,SAAS;IAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,qBAAqB,CAAC;IAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,mBAAmB,CAAA;CAAE,EAAE,EACpG,KAAK,EAAE,cAAc,GACpB,mBAAmB,EAAE,CAavB;AAED,wBAAgB,2BAA2B,CACzC,MAAM,EAAE,mBAAmB,EAC3B,OAAO,EAAE,SAAS,qBAAqB,EAAE,GACxC,MAAM,EAAE,CAOV;AAED,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,SAAS,mBAAmB,EAAE,EACrC,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,MAAM,GACT,mBAAmB,GAAG,SAAS,CAOjC;AAED,wBAAgB,wBAAwB,CACtC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,EACnC,QAAQ,EAAE,mBAAmB,GAC5B,mBAAmB,CAMrB;AAED,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE3D;AAED,wBAAgB,eAAe,CAC7B,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,SAAS;IAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,qBAAqB,CAAA;CAAE,EAAE,EAClH,IAAI,EAAE,SAAS,GAAG,UAAU,GAAG,UAAU,GACxC,MAAM,CASR;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,mBAAmB,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAOnH"}