@pellux/goodvibes-sdk 1.2.0 → 1.3.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 (120) hide show
  1. package/dist/contracts/artifacts/operator-contract.json +26 -2
  2. package/dist/platform/companion/companion-chat-manager.d.ts +6 -12
  3. package/dist/platform/companion/companion-chat-manager.d.ts.map +1 -1
  4. package/dist/platform/companion/companion-chat-manager.js +8 -2
  5. package/dist/platform/config/index.d.ts +2 -1
  6. package/dist/platform/config/index.d.ts.map +1 -1
  7. package/dist/platform/config/index.js +1 -0
  8. package/dist/platform/config/manager.d.ts +35 -0
  9. package/dist/platform/config/manager.d.ts.map +1 -1
  10. package/dist/platform/config/manager.js +98 -2
  11. package/dist/platform/config/shared-config-tier.d.ts +51 -0
  12. package/dist/platform/config/shared-config-tier.d.ts.map +1 -0
  13. package/dist/platform/config/shared-config-tier.js +127 -0
  14. package/dist/platform/control-plane/operator-contract-schemas-knowledge.d.ts.map +1 -1
  15. package/dist/platform/control-plane/operator-contract-schemas-knowledge.js +6 -1
  16. package/dist/platform/control-plane/operator-contract-schemas-runtime.d.ts.map +1 -1
  17. package/dist/platform/control-plane/operator-contract-schemas-runtime.js +2 -1
  18. package/dist/platform/daemon/facade-composition.d.ts.map +1 -1
  19. package/dist/platform/daemon/facade-composition.js +1 -0
  20. package/dist/platform/daemon/homeassistant-chat.d.ts +4 -1
  21. package/dist/platform/daemon/homeassistant-chat.d.ts.map +1 -1
  22. package/dist/platform/daemon/homeassistant-chat.js +1 -1
  23. package/dist/platform/daemon/http/homeassistant-routes.d.ts.map +1 -1
  24. package/dist/platform/daemon/http/homeassistant-routes.js +28 -2
  25. package/dist/platform/integrations/webhooks.d.ts +15 -0
  26. package/dist/platform/integrations/webhooks.d.ts.map +1 -1
  27. package/dist/platform/integrations/webhooks.js +18 -0
  28. package/dist/platform/knowledge/extraction-policy.d.ts +12 -1
  29. package/dist/platform/knowledge/extraction-policy.d.ts.map +1 -1
  30. package/dist/platform/knowledge/extraction-policy.js +19 -1
  31. package/dist/platform/knowledge/graphql-schema.d.ts +1 -1
  32. package/dist/platform/knowledge/graphql-schema.d.ts.map +1 -1
  33. package/dist/platform/knowledge/graphql-schema.js +5 -0
  34. package/dist/platform/knowledge/home-graph/index.d.ts.map +1 -1
  35. package/dist/platform/knowledge/home-graph/index.js +4 -0
  36. package/dist/platform/knowledge/home-graph/link.d.ts +2 -2
  37. package/dist/platform/knowledge/home-graph/link.d.ts.map +1 -1
  38. package/dist/platform/knowledge/home-graph/link.js +60 -9
  39. package/dist/platform/knowledge/home-graph/refinement.d.ts +14 -9
  40. package/dist/platform/knowledge/home-graph/refinement.d.ts.map +1 -1
  41. package/dist/platform/knowledge/home-graph/refinement.js +18 -3
  42. package/dist/platform/knowledge/home-graph/reset.js +21 -4
  43. package/dist/platform/knowledge/home-graph/service.d.ts +10 -11
  44. package/dist/platform/knowledge/home-graph/service.d.ts.map +1 -1
  45. package/dist/platform/knowledge/home-graph/triage.d.ts +103 -0
  46. package/dist/platform/knowledge/home-graph/triage.d.ts.map +1 -0
  47. package/dist/platform/knowledge/home-graph/triage.js +362 -0
  48. package/dist/platform/knowledge/home-graph/types.d.ts +15 -0
  49. package/dist/platform/knowledge/home-graph/types.d.ts.map +1 -1
  50. package/dist/platform/knowledge/knowledge-api.d.ts +6 -0
  51. package/dist/platform/knowledge/knowledge-api.d.ts.map +1 -1
  52. package/dist/platform/knowledge/knowledge-api.js +5 -0
  53. package/dist/platform/knowledge/knowledge-history-types.d.ts +56 -0
  54. package/dist/platform/knowledge/knowledge-history-types.d.ts.map +1 -0
  55. package/dist/platform/knowledge/knowledge-history-types.js +1 -0
  56. package/dist/platform/knowledge/packet.d.ts.map +1 -1
  57. package/dist/platform/knowledge/packet.js +27 -3
  58. package/dist/platform/knowledge/scope-records.js +4 -2
  59. package/dist/platform/knowledge/semantic/answer-gaps.d.ts +8 -0
  60. package/dist/platform/knowledge/semantic/answer-gaps.d.ts.map +1 -1
  61. package/dist/platform/knowledge/semantic/answer-gaps.js +26 -6
  62. package/dist/platform/knowledge/semantic/enrichment.d.ts.map +1 -1
  63. package/dist/platform/knowledge/semantic/enrichment.js +15 -13
  64. package/dist/platform/knowledge/semantic/graph-index.d.ts.map +1 -1
  65. package/dist/platform/knowledge/semantic/graph-index.js +3 -1
  66. package/dist/platform/knowledge/semantic/service.d.ts +7 -0
  67. package/dist/platform/knowledge/semantic/service.d.ts.map +1 -1
  68. package/dist/platform/knowledge/semantic/service.js +10 -1
  69. package/dist/platform/knowledge/service-node-admin.d.ts +46 -0
  70. package/dist/platform/knowledge/service-node-admin.d.ts.map +1 -0
  71. package/dist/platform/knowledge/service-node-admin.js +72 -0
  72. package/dist/platform/knowledge/service.d.ts +24 -20
  73. package/dist/platform/knowledge/service.d.ts.map +1 -1
  74. package/dist/platform/knowledge/service.js +23 -47
  75. package/dist/platform/knowledge/store-config.d.ts +34 -0
  76. package/dist/platform/knowledge/store-config.d.ts.map +1 -1
  77. package/dist/platform/knowledge/store-config.js +23 -0
  78. package/dist/platform/knowledge/store-load.d.ts +3 -1
  79. package/dist/platform/knowledge/store-load.d.ts.map +1 -1
  80. package/dist/platform/knowledge/store-load.js +3 -1
  81. package/dist/platform/knowledge/store-node-history.d.ts +49 -0
  82. package/dist/platform/knowledge/store-node-history.d.ts.map +1 -0
  83. package/dist/platform/knowledge/store-node-history.js +331 -0
  84. package/dist/platform/knowledge/store-record-delete.d.ts +25 -0
  85. package/dist/platform/knowledge/store-record-delete.d.ts.map +1 -0
  86. package/dist/platform/knowledge/store-record-delete.js +84 -0
  87. package/dist/platform/knowledge/store-schema.d.ts +3 -1
  88. package/dist/platform/knowledge/store-schema.d.ts.map +1 -1
  89. package/dist/platform/knowledge/store-schema.js +66 -0
  90. package/dist/platform/knowledge/store.d.ts +19 -1
  91. package/dist/platform/knowledge/store.d.ts.map +1 -1
  92. package/dist/platform/knowledge/store.js +69 -108
  93. package/dist/platform/knowledge/types.d.ts +18 -0
  94. package/dist/platform/knowledge/types.d.ts.map +1 -1
  95. package/dist/platform/providers/registry.d.ts +18 -0
  96. package/dist/platform/providers/registry.d.ts.map +1 -1
  97. package/dist/platform/providers/registry.js +49 -3
  98. package/dist/platform/providers/runtime-snapshot.d.ts.map +1 -1
  99. package/dist/platform/providers/runtime-snapshot.js +14 -2
  100. package/dist/platform/runtime/memory-spine/client.d.ts +12 -0
  101. package/dist/platform/runtime/memory-spine/client.d.ts.map +1 -1
  102. package/dist/platform/runtime/memory-spine/client.js +14 -4
  103. package/dist/platform/runtime/memory-spine/index.d.ts +1 -0
  104. package/dist/platform/runtime/memory-spine/index.d.ts.map +1 -1
  105. package/dist/platform/runtime/memory-spine/index.js +1 -0
  106. package/dist/platform/runtime/memory-spine/recall-snapshot.d.ts.map +1 -1
  107. package/dist/platform/runtime/memory-spine/recall-snapshot.js +16 -3
  108. package/dist/platform/runtime/memory-spine/wire-verb-availability.d.ts +59 -0
  109. package/dist/platform/runtime/memory-spine/wire-verb-availability.d.ts.map +1 -0
  110. package/dist/platform/runtime/memory-spine/wire-verb-availability.js +114 -0
  111. package/dist/platform/runtime/services.d.ts.map +1 -1
  112. package/dist/platform/runtime/services.js +3 -12
  113. package/dist/platform/state/memory-recall-contract.d.ts +7 -0
  114. package/dist/platform/state/memory-recall-contract.d.ts.map +1 -1
  115. package/dist/platform/state/memory-recall-contract.js +2 -0
  116. package/dist/platform/utils/notify.d.ts +30 -1
  117. package/dist/platform/utils/notify.d.ts.map +1 -1
  118. package/dist/platform/utils/notify.js +44 -2
  119. package/dist/platform/version.js +1 -1
  120. package/package.json +9 -9
@@ -1,4 +1,5 @@
1
1
  import { resolveReadableHomeGraphSpace } from './space-selection.js';
2
+ import { runHomeGraphIssueTriage, } from './triage.js';
2
3
  export async function listHomeGraphRefinementTasks(input) {
3
4
  await input.store.init();
4
5
  const { spaceId } = resolveReadableHomeGraphSpace(input.store, input);
@@ -23,8 +24,22 @@ export async function getHomeGraphRefinementTask(input) {
23
24
  export async function runHomeGraphRefinement(input) {
24
25
  await input.store.init();
25
26
  const { spaceId } = resolveReadableHomeGraphSpace(input.store, input);
26
- if (!input.semanticService)
27
- return { ok: false, spaceId, error: 'Semantic refinement is not configured.' };
27
+ const triageRequested = input.triage !== undefined && input.triage !== false;
28
+ const triage = triageRequested
29
+ ? await runHomeGraphIssueTriage({
30
+ store: input.store,
31
+ ...(input.semanticService ? { semanticService: input.semanticService } : {}),
32
+ ...(input.installationId ? { installationId: input.installationId } : {}),
33
+ ...(input.knowledgeSpaceId ? { knowledgeSpaceId: input.knowledgeSpaceId } : {}),
34
+ options: typeof input.triage === 'object' ? input.triage : {},
35
+ })
36
+ : undefined;
37
+ if (input.skipGapRefinement === true) {
38
+ return { ok: true, spaceId, ...(triage ? { triage } : {}) };
39
+ }
40
+ if (!input.semanticService) {
41
+ return { ok: false, spaceId, error: 'Semantic refinement is not configured.', ...(triage ? { triage } : {}) };
42
+ }
28
43
  const result = await input.semanticService.selfImprove({
29
44
  knowledgeSpaceId: spaceId,
30
45
  gapIds: input.gapIds,
@@ -34,7 +49,7 @@ export async function runHomeGraphRefinement(input) {
34
49
  force: input.force,
35
50
  reason: 'manual',
36
51
  });
37
- return { ok: true, spaceId, result };
52
+ return { ok: true, spaceId, result, ...(triage ? { triage } : {}) };
38
53
  }
39
54
  export async function cancelHomeGraphRefinementTask(input) {
40
55
  await input.store.init();
@@ -1,4 +1,4 @@
1
- import { getKnowledgeSpaceId, normalizeKnowledgeSpaceId } from '../spaces.js';
1
+ import { getExplicitKnowledgeSpaceId, getKnowledgeSpaceId, normalizeKnowledgeSpaceId } from '../spaces.js';
2
2
  import { resolveReadableHomeGraphSpace } from './space-selection.js';
3
3
  export async function resetHomeGraphSpace(store, artifactStore, input) {
4
4
  await store.init();
@@ -26,12 +26,29 @@ export async function resetHomeGraphSpace(store, artifactStore, input) {
26
26
  function collectKnowledgeSpaceArtifactIds(store, artifactStore, spaceId) {
27
27
  const normalized = normalizeKnowledgeSpaceId(spaceId);
28
28
  const ids = new Set();
29
+ // Scope-checked deletion (HAZARD H1): the ArtifactStore is shared across the
30
+ // wiki / home-graph / agent families. A blob that a *different* family owns —
31
+ // identifiable by its own explicit knowledge-space stamp — must never be deleted
32
+ // by a home-graph reset, even if a home-graph source references it, because that
33
+ // would orphan the other family's reference. We err toward preserving: an
34
+ // artifact is a delete candidate only when it is not explicitly owned by another
35
+ // space.
36
+ const ownedByOtherSpace = (artifactId) => {
37
+ const descriptor = artifactStore.get(artifactId);
38
+ if (!descriptor)
39
+ return false;
40
+ const artifactSpace = getExplicitKnowledgeSpaceId({ metadata: descriptor.metadata });
41
+ return artifactSpace !== undefined && artifactSpace !== normalized;
42
+ };
29
43
  for (const source of store.listSources(100_000)) {
30
44
  if (getKnowledgeSpaceId(source) !== normalized)
31
45
  continue;
32
- if (typeof source.artifactId === 'string' && source.artifactId.trim().length > 0) {
33
- ids.add(source.artifactId.trim());
34
- }
46
+ const artifactId = typeof source.artifactId === 'string' ? source.artifactId.trim() : '';
47
+ if (artifactId.length === 0)
48
+ continue;
49
+ if (ownedByOtherSpace(artifactId))
50
+ continue;
51
+ ids.add(artifactId);
35
52
  }
36
53
  for (const artifact of artifactStore.list(100_000)) {
37
54
  if (getKnowledgeSpaceId({ metadata: artifact.metadata }) === normalized)
@@ -3,7 +3,8 @@ import type { KnowledgeStore } from '../store.js';
3
3
  import type { KnowledgeEdgeRecord, KnowledgeIssueRecord, KnowledgeNodeRecord, KnowledgeSourceRecord } from '../types.js';
4
4
  import { type HomeGraphReviewResult } from './review.js';
5
5
  import type { KnowledgeSemanticService } from '../semantic/index.js';
6
- import type { HomeGraphAskInput, HomeGraphAskResult, HomeGraphDevicePassportResult, HomeGraphExport, HomeGraphIngestArtifactInput, HomeGraphIngestNoteInput, HomeGraphIngestResult, HomeGraphIngestUrlInput, HomeGraphLinkInput, HomeGraphLinkResult, HomeGraphMapInput, HomeGraphMapResult, HomeGraphProjectionInput, HomeGraphProjectionResult, HomeGraphPageListResult, HomeGraphReindexInput, HomeGraphReindexResult, HomeGraphResetInput, HomeGraphResetResult, HomeGraphReviewInput, HomeGraphSpaceInput, HomeGraphSnapshotInput, HomeGraphStatus, HomeGraphSyncResult } from './types.js';
6
+ import type { HomeGraphTriageOptions } from './triage.js';
7
+ import type { HomeGraphAskInput, HomeGraphAskResult, HomeGraphDevicePassportResult, HomeGraphExport, HomeGraphIngestArtifactInput, HomeGraphIngestNoteInput, HomeGraphIngestResult, HomeGraphIngestUrlInput, HomeGraphLinkInput, HomeGraphLinkResult, HomeGraphUnlinkResult, HomeGraphMapInput, HomeGraphMapResult, HomeGraphProjectionInput, HomeGraphProjectionResult, HomeGraphPageListResult, HomeGraphReindexInput, HomeGraphReindexResult, HomeGraphResetInput, HomeGraphResetResult, HomeGraphReviewInput, HomeGraphSpaceInput, HomeGraphSnapshotInput, HomeGraphStatus, HomeGraphSyncResult } from './types.js';
7
8
  export declare class HomeGraphService {
8
9
  private readonly store;
9
10
  private readonly artifactStore;
@@ -24,7 +25,7 @@ export declare class HomeGraphService {
24
25
  ingestNote(input: HomeGraphIngestNoteInput): Promise<HomeGraphIngestResult>;
25
26
  ingestArtifact(input: HomeGraphIngestArtifactInput): Promise<HomeGraphIngestResult>;
26
27
  linkKnowledge(input: HomeGraphLinkInput): Promise<HomeGraphLinkResult>;
27
- unlinkKnowledge(input: HomeGraphLinkInput): Promise<HomeGraphLinkResult>;
28
+ unlinkKnowledge(input: HomeGraphLinkInput): Promise<HomeGraphUnlinkResult>;
28
29
  ask(input: HomeGraphAskInput): Promise<HomeGraphAskResult>;
29
30
  reindex(input?: HomeGraphReindexInput): Promise<HomeGraphReindexResult>;
30
31
  private repairStaleExtractionsForAsk;
@@ -76,16 +77,14 @@ export declare class HomeGraphService {
76
77
  readonly limit?: number | undefined;
77
78
  readonly maxRunMs?: number | undefined;
78
79
  readonly force?: boolean | undefined;
80
+ readonly triage?: HomeGraphTriageOptions | boolean | undefined;
81
+ readonly skipGapRefinement?: boolean | undefined;
79
82
  }): Promise<{
80
- ok: boolean;
81
- spaceId: string;
82
- error: string;
83
- result?: never;
84
- } | {
85
- ok: boolean;
86
- spaceId: string;
87
- result: import("../semantic/types.js").KnowledgeSemanticSelfImproveResult;
88
- error?: never;
83
+ readonly ok: boolean;
84
+ readonly spaceId: string;
85
+ readonly error?: string | undefined;
86
+ readonly result?: Awaited<ReturnType<KnowledgeSemanticService["selfImprove"]>> | undefined;
87
+ readonly triage?: import("./triage.js").HomeGraphTriageResult | undefined;
89
88
  }>;
90
89
  cancelRefinementTask(input: HomeGraphSpaceInput & {
91
90
  readonly taskId: string;
@@ -1 +1 @@
1
- {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../../../src/platform/knowledge/home-graph/service.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../../../src/platform/knowledge/home-graph/service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,103 @@
1
+ import type { KnowledgeStore } from '../store.js';
2
+ import type { KnowledgeSemanticService } from '../semantic/index.js';
3
+ import type { HomeGraphSpaceInput } from './types.js';
4
+ /**
5
+ * Default auto-apply threshold for LLM triage decisions, expressed on the SDK's
6
+ * 0-100 confidence scale. This mirrors the Home Assistant Python triage engine's
7
+ * `0.85` gate and reuses the same "trust above a threshold" idiom as the semantic
8
+ * gap-repair `minConfidence` precedent (`knowledge/semantic/gap-repair.ts`).
9
+ */
10
+ export declare const HOME_GRAPH_TRIAGE_DEFAULT_MIN_CONFIDENCE = 85;
11
+ /** Issues sent to the model per completion call. */
12
+ export declare const HOME_GRAPH_TRIAGE_DEFAULT_CHUNK_SIZE = 25;
13
+ /** Untriaged open issues examined in a single run by default. */
14
+ export declare const HOME_GRAPH_TRIAGE_DEFAULT_LIMIT = 25;
15
+ /**
16
+ * One entry in the extensible issue-code → applicability-rule framework. Adding a
17
+ * rule for a new `homegraph.*` issue code makes that code eligible for LLM triage
18
+ * without touching the loop itself. This replaces the two hardcoded codes the
19
+ * heuristic layer knows about.
20
+ */
21
+ export interface HomeGraphTriageRule {
22
+ /** The issue code this rule governs, e.g. `homegraph.device.unknown_battery`. */
23
+ readonly code: string;
24
+ /** Extra model guidance appended to the triage instruction for this code. */
25
+ readonly promptGuidance?: string | undefined;
26
+ /** Review category recorded when the model omits one for a reject. */
27
+ readonly defaultCategory?: string | undefined;
28
+ }
29
+ /**
30
+ * The built-in rules. These reproduce the applicability judgment the Python triage
31
+ * prompt asked for, for exactly the two codes `quality.ts` raises today. The
32
+ * `deriveIssueFacts` mapping in `review.ts` supplies the code-specific facts when a
33
+ * reject is applied, so rules only carry the prompt guidance and default category.
34
+ */
35
+ export declare const DEFAULT_HOME_GRAPH_TRIAGE_RULES: readonly HomeGraphTriageRule[];
36
+ export interface HomeGraphTriageOptions {
37
+ /** Auto-apply threshold on the 0-100 scale. Default 85. */
38
+ readonly minConfidence?: number | undefined;
39
+ /** Max untriaged open issues to examine this run. Default 25. */
40
+ readonly limit?: number | undefined;
41
+ /** Issues per model completion. Default 25. */
42
+ readonly chunkSize?: number | undefined;
43
+ /** Re-triage even issues whose cached fingerprint is unchanged. */
44
+ readonly force?: boolean | undefined;
45
+ /** Issue ids to leave untouched this run. */
46
+ readonly skipIssueIds?: readonly string[] | undefined;
47
+ /** Restrict triage to this subset of issue codes (must still have a rule). */
48
+ readonly issueCodes?: readonly string[] | undefined;
49
+ /** Extra rules merged over the built-ins (by code) — the extensibility hook. */
50
+ readonly additionalRules?: readonly HomeGraphTriageRule[] | undefined;
51
+ /** Per-completion model timeout in ms. */
52
+ readonly timeoutMs?: number | undefined;
53
+ /** Reviewer label recorded on applied decisions. */
54
+ readonly reviewer?: string | undefined;
55
+ }
56
+ export interface HomeGraphTriageDecision {
57
+ readonly issueId: string;
58
+ readonly code: string;
59
+ readonly action: 'reject' | 'review';
60
+ readonly category?: string | undefined;
61
+ readonly confidence: number;
62
+ readonly reason?: string | undefined;
63
+ /** True when the decision cleared the threshold and was auto-applied. */
64
+ readonly applied: boolean;
65
+ /** Facts written to the device node when applied. */
66
+ readonly appliedFacts?: Record<string, unknown> | undefined;
67
+ /** Provenance tag — always `homegraph-triage`. */
68
+ readonly source: string;
69
+ }
70
+ export interface HomeGraphTriageResult {
71
+ readonly ok: true;
72
+ readonly spaceId: string;
73
+ /** False when no semantic LLM is configured — the loop is a no-op. */
74
+ readonly configured: boolean;
75
+ readonly processed: number;
76
+ /** Open triageable issues skipped because their cached fingerprint was unchanged. */
77
+ readonly skipped: number;
78
+ readonly applied: number;
79
+ readonly reviewed: number;
80
+ readonly decisions: readonly HomeGraphTriageDecision[];
81
+ /** Open triageable issues still unresolved after this run. */
82
+ readonly remaining: number;
83
+ readonly minConfidence: number;
84
+ readonly reason?: string | undefined;
85
+ }
86
+ /**
87
+ * LLM-driven triage over open Home Graph device-quality issues. Operates strictly
88
+ * on the resolved Home Assistant knowledge space: it reads that space's issues and
89
+ * nodes, prompts the configured semantic LLM to classify each open issue as
90
+ * `reject` (safe to auto-dismiss) or `review` (needs a human), auto-applies rejects
91
+ * at or above the confidence threshold via {@link reviewHomeGraphFact}, and caches
92
+ * every decision on the issue so an unchanged issue is never re-sent to the model.
93
+ *
94
+ * This never reads or writes any other knowledge space — Home Graph shares this
95
+ * code with the wiki/agent knowledge functions but never their data.
96
+ */
97
+ export declare function runHomeGraphIssueTriage(input: HomeGraphSpaceInput & {
98
+ readonly store: KnowledgeStore;
99
+ readonly semanticService?: KnowledgeSemanticService | undefined;
100
+ readonly options?: HomeGraphTriageOptions | undefined;
101
+ readonly signal?: AbortSignal | undefined;
102
+ }): Promise<HomeGraphTriageResult>;
103
+ //# sourceMappingURL=triage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"triage.d.ts","sourceRoot":"","sources":["../../../../src/platform/knowledge/home-graph/triage.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,362 @@
1
+ import { yieldEvery } from '../cooperative.js';
2
+ import { readRecord, readString, stableHash } from './helpers.js';
3
+ import { readHomeGraphState } from './state.js';
4
+ import { reviewHomeGraphFact } from './review.js';
5
+ import { resolveReadableHomeGraphSpace } from './space-selection.js';
6
+ /**
7
+ * Default auto-apply threshold for LLM triage decisions, expressed on the SDK's
8
+ * 0-100 confidence scale. This mirrors the Home Assistant Python triage engine's
9
+ * `0.85` gate and reuses the same "trust above a threshold" idiom as the semantic
10
+ * gap-repair `minConfidence` precedent (`knowledge/semantic/gap-repair.ts`).
11
+ */
12
+ export const HOME_GRAPH_TRIAGE_DEFAULT_MIN_CONFIDENCE = 85;
13
+ /** Issues sent to the model per completion call. */
14
+ export const HOME_GRAPH_TRIAGE_DEFAULT_CHUNK_SIZE = 25;
15
+ /** Untriaged open issues examined in a single run by default. */
16
+ export const HOME_GRAPH_TRIAGE_DEFAULT_LIMIT = 25;
17
+ /** Ceiling on how many open issues are scanned before limit/skip filtering. */
18
+ const TRIAGE_ISSUE_SCAN_LIMIT = 1_000;
19
+ /** Issue-metadata key under which a triage decision + fingerprint is cached. */
20
+ const TRIAGE_METADATA_KEY = 'triage';
21
+ /** Provenance tag stamped on every triage decision and review value. */
22
+ const TRIAGE_SOURCE = 'homegraph-triage';
23
+ /**
24
+ * The built-in rules. These reproduce the applicability judgment the Python triage
25
+ * prompt asked for, for exactly the two codes `quality.ts` raises today. The
26
+ * `deriveIssueFacts` mapping in `review.ts` supplies the code-specific facts when a
27
+ * reject is applied, so rules only carry the prompt guidance and default category.
28
+ */
29
+ export const DEFAULT_HOME_GRAPH_TRIAGE_RULES = [
30
+ {
31
+ code: 'homegraph.device.unknown_battery',
32
+ defaultCategory: 'not_applicable',
33
+ promptGuidance: [
34
+ 'For unknown battery type issues: reject software objects, integrations, automations,',
35
+ 'scripts, scenes, areas, helpers, the sun, weather, Home Assistant host/core/supervisor',
36
+ 'objects, servers, adapters, hubs, coordinators, bridges, and mains-powered media devices',
37
+ 'or appliances. Include fact {"batteryPowered":false,"batteryType":"none"} for those',
38
+ 'not-applicable rejects. Review sensors, locks, remotes, buttons, keypads, contact sensors,',
39
+ 'motion sensors, leak sensors, smoke detectors, thermostats, shades, blinds, and any',
40
+ 'ambiguous physical device that could plausibly be battery powered.',
41
+ ].join(' '),
42
+ },
43
+ {
44
+ code: 'homegraph.device.missing_manual',
45
+ defaultCategory: 'not_applicable',
46
+ promptGuidance: [
47
+ 'For missing manual issues that are not applicable to software, helpers, or generated Home',
48
+ 'Assistant objects, include fact {"manualRequired":false}.',
49
+ ].join(' '),
50
+ },
51
+ ];
52
+ /**
53
+ * LLM-driven triage over open Home Graph device-quality issues. Operates strictly
54
+ * on the resolved Home Assistant knowledge space: it reads that space's issues and
55
+ * nodes, prompts the configured semantic LLM to classify each open issue as
56
+ * `reject` (safe to auto-dismiss) or `review` (needs a human), auto-applies rejects
57
+ * at or above the confidence threshold via {@link reviewHomeGraphFact}, and caches
58
+ * every decision on the issue so an unchanged issue is never re-sent to the model.
59
+ *
60
+ * This never reads or writes any other knowledge space — Home Graph shares this
61
+ * code with the wiki/agent knowledge functions but never their data.
62
+ */
63
+ export async function runHomeGraphIssueTriage(input) {
64
+ await input.store.init();
65
+ const { spaceId, installationId } = resolveReadableHomeGraphSpace(input.store, input);
66
+ const options = input.options ?? {};
67
+ const minConfidence = clampConfidence(options.minConfidence ?? HOME_GRAPH_TRIAGE_DEFAULT_MIN_CONFIDENCE);
68
+ const llm = input.semanticService?.llm ?? null;
69
+ const rulesByCode = mergeTriageRules(options.additionalRules);
70
+ const codeFilter = options.issueCodes ? new Set(options.issueCodes) : null;
71
+ const state = readHomeGraphState(input.store, spaceId);
72
+ const nodeById = new Map(state.nodes.map((node) => [node.id, node]));
73
+ const openTriageable = state.issues.filter((issue) => (issue.status === 'open'
74
+ && rulesByCode.has(issue.code)
75
+ && (!codeFilter || codeFilter.has(issue.code))));
76
+ if (!llm) {
77
+ return emptyResult(spaceId, minConfidence, openTriageable.length, false, 'triage-llm-not-configured');
78
+ }
79
+ const skipIssueIds = new Set(options.skipIssueIds ?? []);
80
+ const scannable = openTriageable.slice(0, TRIAGE_ISSUE_SCAN_LIMIT);
81
+ let cacheSkipped = 0;
82
+ const candidates = [];
83
+ for (const issue of scannable) {
84
+ if (skipIssueIds.has(issue.id))
85
+ continue;
86
+ const node = issue.nodeId ? nodeById.get(issue.nodeId) : undefined;
87
+ if (!options.force && isCachedTriage(issue, node)) {
88
+ cacheSkipped += 1;
89
+ continue;
90
+ }
91
+ candidates.push(issue);
92
+ }
93
+ const limit = Math.max(1, options.limit ?? HOME_GRAPH_TRIAGE_DEFAULT_LIMIT);
94
+ const selected = candidates.slice(0, limit);
95
+ if (selected.length === 0) {
96
+ const reason = cacheSkipped > 0 ? 'no-untriaged-open-issues' : 'no-open-issues';
97
+ return emptyResult(spaceId, minConfidence, openTriageable.length, true, reason, cacheSkipped);
98
+ }
99
+ const records = selected.map((issue) => buildTriageRecord(issue, issue.nodeId ? nodeById.get(issue.nodeId) : undefined));
100
+ const chunkSize = Math.max(1, options.chunkSize ?? HOME_GRAPH_TRIAGE_DEFAULT_CHUNK_SIZE);
101
+ const decisionByIssueId = new Map();
102
+ for (let index = 0; index < records.length; index += chunkSize) {
103
+ if (input.signal?.aborted)
104
+ break;
105
+ const chunk = records.slice(index, index + chunkSize);
106
+ const response = await llm.completeJson({
107
+ purpose: 'homegraph-issue-triage',
108
+ maxTokens: 2_000,
109
+ systemPrompt: buildTriageSystemPrompt(rulesByCode, chunk),
110
+ prompt: JSON.stringify({ issues: chunk }),
111
+ ...(options.timeoutMs !== undefined ? { timeoutMs: options.timeoutMs } : {}),
112
+ ...(input.signal ? { signal: input.signal } : {}),
113
+ });
114
+ for (const decision of parseTriageDecisions(response)) {
115
+ decisionByIssueId.set(decision.issueId, decision);
116
+ }
117
+ await yieldEvery(index, chunkSize);
118
+ }
119
+ const issueById = new Map(selected.map((issue) => [issue.id, issue]));
120
+ const decisions = [];
121
+ let appliedCount = 0;
122
+ for (const issue of selected) {
123
+ const parsed = decisionByIssueId.get(issue.id);
124
+ if (!parsed)
125
+ continue;
126
+ const rule = rulesByCode.get(issue.code);
127
+ const category = parsed.category ?? rule?.defaultCategory;
128
+ const shouldApply = parsed.action === 'reject' && parsed.confidence >= minConfidence;
129
+ let appliedFacts;
130
+ if (shouldApply) {
131
+ const review = await reviewHomeGraphFact(input.store, spaceId, installationId, {
132
+ knowledgeSpaceId: spaceId,
133
+ issueId: issue.id,
134
+ action: 'reject',
135
+ reviewer: options.reviewer ?? `${TRIAGE_SOURCE}:auto`,
136
+ value: buildReviewValue(category, parsed, rule),
137
+ });
138
+ appliedFacts = review.appliedFacts;
139
+ appliedCount += 1;
140
+ }
141
+ const decision = {
142
+ issueId: issue.id,
143
+ code: issue.code,
144
+ action: parsed.action,
145
+ ...(category ? { category } : {}),
146
+ confidence: parsed.confidence,
147
+ ...(parsed.reason ? { reason: parsed.reason } : {}),
148
+ applied: shouldApply,
149
+ ...(appliedFacts && Object.keys(appliedFacts).length > 0 ? { appliedFacts } : {}),
150
+ source: TRIAGE_SOURCE,
151
+ };
152
+ decisions.push(decision);
153
+ await recordTriageDecision(input.store, issue.id, issueById.get(issue.id) ?? issue, decision, nodeById);
154
+ }
155
+ const remaining = countOpenTriageable(input.store, spaceId, rulesByCode, codeFilter);
156
+ return {
157
+ ok: true,
158
+ spaceId,
159
+ configured: true,
160
+ processed: selected.length,
161
+ skipped: cacheSkipped,
162
+ applied: appliedCount,
163
+ reviewed: decisions.length - appliedCount,
164
+ decisions,
165
+ remaining,
166
+ minConfidence,
167
+ };
168
+ }
169
+ function emptyResult(spaceId, minConfidence, remaining, configured, reason, skipped = 0) {
170
+ return {
171
+ ok: true,
172
+ spaceId,
173
+ configured,
174
+ processed: 0,
175
+ skipped,
176
+ applied: 0,
177
+ reviewed: 0,
178
+ decisions: [],
179
+ remaining,
180
+ minConfidence,
181
+ reason,
182
+ };
183
+ }
184
+ function mergeTriageRules(additional) {
185
+ const byCode = new Map();
186
+ for (const rule of DEFAULT_HOME_GRAPH_TRIAGE_RULES)
187
+ byCode.set(rule.code, rule);
188
+ for (const rule of additional ?? []) {
189
+ if (typeof rule?.code === 'string' && rule.code.trim())
190
+ byCode.set(rule.code, rule);
191
+ }
192
+ return byCode;
193
+ }
194
+ function buildTriageRecord(issue, node) {
195
+ const record = {
196
+ issueId: issue.id,
197
+ code: issue.code,
198
+ severity: issue.severity,
199
+ status: issue.status,
200
+ message: issue.message,
201
+ ...(issue.nodeId ? { nodeId: issue.nodeId } : {}),
202
+ ...(issue.sourceId ? { sourceId: issue.sourceId } : {}),
203
+ };
204
+ if (!node)
205
+ return record;
206
+ const homeAssistant = readRecord(node.metadata.homeAssistant);
207
+ const nodeSummary = pruneEmpty({
208
+ id: node.id,
209
+ kind: node.kind,
210
+ title: node.title,
211
+ summary: node.summary,
212
+ aliases: node.aliases.slice(0, 8),
213
+ confidence: node.confidence,
214
+ manufacturer: readString(node.metadata.manufacturer),
215
+ model: readString(node.metadata.model),
216
+ homeAssistant: pruneEmpty({
217
+ objectKind: readString(homeAssistant.objectKind),
218
+ objectId: readString(homeAssistant.objectId),
219
+ entityId: readString(homeAssistant.entityId),
220
+ deviceId: readString(homeAssistant.deviceId),
221
+ areaId: readString(homeAssistant.areaId),
222
+ integrationId: readString(homeAssistant.integrationId),
223
+ }),
224
+ });
225
+ return { ...record, node: nodeSummary };
226
+ }
227
+ function buildTriageSystemPrompt(rulesByCode, records) {
228
+ const activeCodes = new Set(records.map((record) => record.code));
229
+ const guidance = [...rulesByCode.values()]
230
+ .filter((rule) => activeCodes.has(rule.code))
231
+ .map((rule) => rule.promptGuidance)
232
+ .filter((line) => Boolean(line));
233
+ return [
234
+ 'You are GoodVibes Home Graph review triage for Home Assistant.',
235
+ 'Classify each issue so people only review uncertain cases.',
236
+ 'Return only strict JSON with this shape:',
237
+ '{"decisions":[{"issueId":"...","action":"reject|review","category":"...","confidence":0,"reason":"...","fact":{}}]}.',
238
+ 'Echo the issueId exactly as supplied. confidence is a number from 0 to 100.',
239
+ 'Use action reject only when the issue is clearly not applicable or incorrect and can be safely',
240
+ 'dismissed. Use action review for anything uncertain, anything that may require household',
241
+ 'knowledge, or any physical device that could plausibly be affected.',
242
+ ...guidance,
243
+ 'Do not invent facts. Do not choose accept, resolve, edit, or forget.',
244
+ ].join('\n');
245
+ }
246
+ function parseTriageDecisions(value) {
247
+ const record = readRecord(value);
248
+ const raw = Array.isArray(record.decisions) ? record.decisions : Array.isArray(value) ? value : [];
249
+ const parsed = [];
250
+ for (const entry of raw) {
251
+ const decision = readRecord(entry);
252
+ const issueId = readString(decision.issueId) ?? readString(decision.id);
253
+ if (!issueId)
254
+ continue;
255
+ const action = readString(decision.action)?.toLowerCase() === 'reject' ? 'reject' : 'review';
256
+ const fact = readRecord(decision.fact);
257
+ parsed.push({
258
+ issueId,
259
+ action,
260
+ ...(readString(decision.category) ? { category: normalizeCategory(readString(decision.category)) } : {}),
261
+ confidence: normalizeConfidence(decision.confidence),
262
+ ...(readString(decision.reason) ? { reason: readString(decision.reason) } : {}),
263
+ ...(Object.keys(fact).length > 0 ? { fact } : {}),
264
+ });
265
+ }
266
+ return parsed;
267
+ }
268
+ function buildReviewValue(category, parsed, rule) {
269
+ const value = {
270
+ category: category ?? rule?.defaultCategory ?? 'not_applicable',
271
+ confidence: parsed.confidence,
272
+ reason: parsed.reason ?? 'LLM triage classified this issue as not applicable.',
273
+ source: TRIAGE_SOURCE,
274
+ };
275
+ if (parsed.fact && Object.keys(parsed.fact).length > 0)
276
+ value.fact = parsed.fact;
277
+ return value;
278
+ }
279
+ async function recordTriageDecision(store, issueId, fallback, decision, nodeById) {
280
+ // Re-read so an applied reject (which resolved the issue via reviewFact) keeps
281
+ // its fresh status/metadata; upsertIssue shallow-merges metadata, preserving it.
282
+ const current = store.getIssue(issueId) ?? fallback;
283
+ const node = current.nodeId ? nodeById.get(current.nodeId) : undefined;
284
+ await store.upsertIssue({
285
+ id: current.id,
286
+ severity: current.severity,
287
+ code: current.code,
288
+ message: current.message,
289
+ status: current.status,
290
+ ...(current.sourceId ? { sourceId: current.sourceId } : {}),
291
+ ...(current.nodeId ? { nodeId: current.nodeId } : {}),
292
+ metadata: {
293
+ ...current.metadata,
294
+ [TRIAGE_METADATA_KEY]: {
295
+ fingerprint: triageFingerprint(current, node),
296
+ action: decision.action,
297
+ ...(decision.category ? { category: decision.category } : {}),
298
+ confidence: decision.confidence,
299
+ ...(decision.reason ? { reason: decision.reason } : {}),
300
+ applied: decision.applied,
301
+ source: TRIAGE_SOURCE,
302
+ decidedAt: Date.now(),
303
+ },
304
+ },
305
+ });
306
+ }
307
+ function isCachedTriage(issue, node) {
308
+ const cached = readRecord(issue.metadata[TRIAGE_METADATA_KEY]);
309
+ const fingerprint = readString(cached.fingerprint);
310
+ if (!fingerprint)
311
+ return false;
312
+ return fingerprint === triageFingerprint(issue, node);
313
+ }
314
+ function triageFingerprint(issue, node) {
315
+ const homeAssistant = node ? readRecord(node.metadata.homeAssistant) : {};
316
+ return stableHash(JSON.stringify({
317
+ code: issue.code,
318
+ severity: issue.severity,
319
+ message: issue.message,
320
+ nodeId: issue.nodeId ?? null,
321
+ sourceId: issue.sourceId ?? null,
322
+ title: node?.title ?? null,
323
+ confidence: node?.confidence ?? null,
324
+ manufacturer: node ? readString(node.metadata.manufacturer) ?? null : null,
325
+ model: node ? readString(node.metadata.model) ?? null : null,
326
+ objectKind: readString(homeAssistant.objectKind) ?? null,
327
+ entityId: readString(homeAssistant.entityId) ?? null,
328
+ deviceId: readString(homeAssistant.deviceId) ?? null,
329
+ }));
330
+ }
331
+ function countOpenTriageable(store, spaceId, rulesByCode, codeFilter) {
332
+ return readHomeGraphState(store, spaceId).issues.filter((issue) => (issue.status === 'open'
333
+ && rulesByCode.has(issue.code)
334
+ && (!codeFilter || codeFilter.has(issue.code)))).length;
335
+ }
336
+ function normalizeConfidence(value) {
337
+ const parsed = typeof value === 'number' ? value : typeof value === 'string' ? Number(value) : NaN;
338
+ if (!Number.isFinite(parsed))
339
+ return 0;
340
+ // Accept both the 0-1 fraction the Python engine used and the 0-100 SDK scale.
341
+ const scaled = parsed > 0 && parsed <= 1 ? parsed * 100 : parsed;
342
+ return clampConfidence(scaled);
343
+ }
344
+ function clampConfidence(value) {
345
+ return Math.max(0, Math.min(100, Math.round(value)));
346
+ }
347
+ function normalizeCategory(value) {
348
+ return value.trim().toLowerCase().replace(/[-\s]+/g, '_');
349
+ }
350
+ function pruneEmpty(record) {
351
+ const result = {};
352
+ for (const [key, value] of Object.entries(record)) {
353
+ if (value === undefined || value === null || value === '')
354
+ continue;
355
+ if (Array.isArray(value) && value.length === 0)
356
+ continue;
357
+ if (typeof value === 'object' && !Array.isArray(value) && Object.keys(value).length === 0)
358
+ continue;
359
+ result[key] = value;
360
+ }
361
+ return result;
362
+ }
@@ -161,6 +161,21 @@ export interface HomeGraphLinkResult {
161
161
  readonly edge: KnowledgeEdgeRecord;
162
162
  readonly target: KnowledgeNodeRecord | KnowledgeSourceRecord | null;
163
163
  }
164
+ /**
165
+ * Result of an unlink. Unlink is a real reversal: it removes the link edge (and,
166
+ * if the prior link materialized the target node and nothing else references it,
167
+ * that node too). Unlinking a never-linked target is an honest no-op — it creates
168
+ * no phantom records. `reversed` is true only when an active link was actually
169
+ * removed.
170
+ */
171
+ export interface HomeGraphUnlinkResult {
172
+ readonly ok: true;
173
+ readonly spaceId: string;
174
+ readonly reversed: boolean;
175
+ readonly removedEdgeId?: string | undefined;
176
+ readonly removedNodeId?: string | undefined;
177
+ readonly target: KnowledgeNodeRecord | KnowledgeSourceRecord | null;
178
+ }
164
179
  export interface HomeGraphAskInput extends HomeGraphSpaceInput {
165
180
  readonly query: string;
166
181
  readonly limit?: number | undefined;