@pellux/goodvibes-sdk 0.28.12 → 0.28.14

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 (42) hide show
  1. package/dist/_internal/contracts/artifacts/operator-contract.json +7 -1
  2. package/dist/_internal/contracts/generated/foundation-metadata.d.ts +1 -1
  3. package/dist/_internal/contracts/generated/foundation-metadata.js +1 -1
  4. package/dist/_internal/contracts/generated/operator-contract.d.ts.map +1 -1
  5. package/dist/_internal/contracts/generated/operator-contract.js +7 -1
  6. package/dist/_internal/platform/control-plane/method-catalog-homegraph.d.ts.map +1 -1
  7. package/dist/_internal/platform/control-plane/method-catalog-homegraph.js +1 -0
  8. package/dist/_internal/platform/control-plane/method-catalog-knowledge.d.ts.map +1 -1
  9. package/dist/_internal/platform/control-plane/method-catalog-knowledge.js +2 -1
  10. package/dist/_internal/platform/knowledge/home-graph/ask.d.ts +1 -1
  11. package/dist/_internal/platform/knowledge/home-graph/ask.d.ts.map +1 -1
  12. package/dist/_internal/platform/knowledge/home-graph/ask.js +3 -84
  13. package/dist/_internal/platform/knowledge/home-graph/generated-pages.d.ts.map +1 -1
  14. package/dist/_internal/platform/knowledge/home-graph/generated-pages.js +16 -0
  15. package/dist/_internal/platform/knowledge/home-graph/service.d.ts.map +1 -1
  16. package/dist/_internal/platform/knowledge/home-graph/service.js +3 -5
  17. package/dist/_internal/platform/knowledge/home-graph/types.d.ts +2 -0
  18. package/dist/_internal/platform/knowledge/home-graph/types.d.ts.map +1 -1
  19. package/dist/_internal/platform/knowledge/semantic/answer-fallback.d.ts +11 -0
  20. package/dist/_internal/platform/knowledge/semantic/answer-fallback.d.ts.map +1 -0
  21. package/dist/_internal/platform/knowledge/semantic/answer-fallback.js +31 -0
  22. package/dist/_internal/platform/knowledge/semantic/answer.d.ts.map +1 -1
  23. package/dist/_internal/platform/knowledge/semantic/answer.js +18 -23
  24. package/dist/_internal/platform/knowledge/semantic/enrichment.js +6 -1
  25. package/dist/_internal/platform/knowledge/semantic/gap-repair.d.ts +2 -0
  26. package/dist/_internal/platform/knowledge/semantic/gap-repair.d.ts.map +1 -1
  27. package/dist/_internal/platform/knowledge/semantic/gap-repair.js +262 -71
  28. package/dist/_internal/platform/knowledge/semantic/self-improvement-tasks.d.ts +12 -0
  29. package/dist/_internal/platform/knowledge/semantic/self-improvement-tasks.d.ts.map +1 -0
  30. package/dist/_internal/platform/knowledge/semantic/self-improvement-tasks.js +79 -0
  31. package/dist/_internal/platform/knowledge/semantic/self-improvement.d.ts +4 -0
  32. package/dist/_internal/platform/knowledge/semantic/self-improvement.d.ts.map +1 -1
  33. package/dist/_internal/platform/knowledge/semantic/self-improvement.js +107 -89
  34. package/dist/_internal/platform/knowledge/semantic/service.d.ts.map +1 -1
  35. package/dist/_internal/platform/knowledge/semantic/service.js +8 -0
  36. package/dist/_internal/platform/knowledge/semantic/timeouts.d.ts +4 -0
  37. package/dist/_internal/platform/knowledge/semantic/timeouts.d.ts.map +1 -0
  38. package/dist/_internal/platform/knowledge/semantic/timeouts.js +26 -0
  39. package/dist/_internal/platform/knowledge/semantic/types.d.ts +6 -0
  40. package/dist/_internal/platform/knowledge/semantic/types.d.ts.map +1 -1
  41. package/dist/_internal/platform/version.js +1 -1
  42. package/package.json +1 -1
@@ -24,6 +24,8 @@ export interface WebGapRepairOptions {
24
24
  readonly searchService: GapRepairSearch;
25
25
  readonly ingestService: GapRepairIngest;
26
26
  readonly maxResults?: number;
27
+ readonly maxSearches?: number;
28
+ readonly maxSources?: number;
27
29
  readonly minDistinctDomains?: number;
28
30
  readonly minConfidence?: number;
29
31
  readonly maxIngest?: number;
@@ -1 +1 @@
1
- {"version":3,"file":"gap-repair.d.ts","sourceRoot":"","sources":["../../../../../src/_internal/platform/knowledge/semantic/gap-repair.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,gBAAgB,EAChB,iBAAiB,EAElB,MAAM,2BAA2B,CAAC;AAEnC,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AACvD,OAAO,KAAK,EACV,4BAA4B,EAG7B,MAAM,YAAY,CAAC;AAGpB,UAAU,eAAe;IACvB,MAAM,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC;CAC/D;AAED,UAAU,eAAe;IACvB,SAAS,CAAC,KAAK,EAAE;QACf,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;QACrB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QACxB,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;QAClC,QAAQ,CAAC,UAAU,CAAC,EAAE,mBAAmB,CAAC;QAC1C,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;QAC9B,QAAQ,CAAC,iBAAiB,CAAC,EAAE,OAAO,CAAC;QACrC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KAC7C,GAAG,OAAO,CAAC;QAAE,QAAQ,CAAC,MAAM,EAAE;YAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;YAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,CAAC,CAAC;CACpF;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,aAAa,EAAE,eAAe,CAAC;IACxC,QAAQ,CAAC,aAAa,EAAE,eAAe,CAAC;IACxC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IACrC,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;CACnC;AAmBD,wBAAgB,6BAA6B,CAAC,OAAO,EAAE,mBAAmB,GAAG,4BAA4B,CAExG"}
1
+ {"version":3,"file":"gap-repair.d.ts","sourceRoot":"","sources":["../../../../../src/_internal/platform/knowledge/semantic/gap-repair.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,gBAAgB,EAChB,iBAAiB,EAElB,MAAM,2BAA2B,CAAC;AAEnC,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AACvD,OAAO,KAAK,EACV,4BAA4B,EAG7B,MAAM,YAAY,CAAC;AAIpB,UAAU,eAAe;IACvB,MAAM,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC;CAC/D;AAED,UAAU,eAAe;IACvB,SAAS,CAAC,KAAK,EAAE;QACf,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;QACrB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QACxB,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;QAClC,QAAQ,CAAC,UAAU,CAAC,EAAE,mBAAmB,CAAC;QAC1C,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;QAC9B,QAAQ,CAAC,iBAAiB,CAAC,EAAE,OAAO,CAAC;QACrC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KAC7C,GAAG,OAAO,CAAC;QAAE,QAAQ,CAAC,MAAM,EAAE;YAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;YAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,CAAC,CAAC;CACpF;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,aAAa,EAAE,eAAe,CAAC;IACxC,QAAQ,CAAC,aAAa,EAAE,eAAe,CAAC;IACxC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IACrC,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;CACnC;AA0BD,wBAAgB,6BAA6B,CAAC,OAAO,EAAE,mBAAmB,GAAG,4BAA4B,CAExG"}
@@ -1,11 +1,12 @@
1
1
  import { canonicalizeUri } from '../internal.js';
2
+ import { withTimeout } from './timeouts.js';
2
3
  import { readString, scoreSemanticText, tokenizeSemanticQuery, uniqueStrings } from './utils.js';
3
4
  export function createWebKnowledgeGapRepairer(options) {
4
5
  return async (request) => repairKnowledgeGapsWithWeb(request, options);
5
6
  }
6
7
  async function repairKnowledgeGapsWithWeb(request, options) {
7
- const query = buildGapRepairQuery(request);
8
- if (!query) {
8
+ const queries = buildGapRepairQueries(request);
9
+ if (queries.length === 0) {
9
10
  return {
10
11
  searched: false,
11
12
  ingestedSourceIds: [],
@@ -13,47 +14,77 @@ async function repairKnowledgeGapsWithWeb(request, options) {
13
14
  reason: 'No concrete subject was available for gap repair.',
14
15
  };
15
16
  }
16
- let response;
17
- try {
18
- response = await withTimeout(options.searchService.search({
19
- query,
20
- maxResults: Math.max(2, Math.min(12, options.maxResults ?? 6)),
21
- verbosity: 'snippets',
22
- safeSearch: 'moderate',
23
- metadata: {
24
- purpose: 'knowledge-gap-repair',
25
- knowledgeSpaceId: request.spaceId,
26
- },
27
- }), Math.max(1_000, options.searchTimeoutMs ?? 8_000), 'Semantic gap repair search timed out.');
17
+ const existing = existingSources(request);
18
+ const sourceLimit = Math.max(2, Math.min(5, request.maxSources ?? options.maxSources ?? options.maxIngest ?? 5));
19
+ const searchLimit = Math.max(1, Math.min(5, options.maxSearches ?? queries.length));
20
+ const existingCandidates = selectExistingRepairSources(request, options, sourceLimit);
21
+ const searchResults = new Map();
22
+ const providerIds = new Set();
23
+ let lastError;
24
+ for (const query of queries.slice(0, searchLimit)) {
25
+ if (request.signal?.aborted || deadlineExceeded(request.deadlineAt))
26
+ break;
27
+ try {
28
+ const timeoutMs = operationTimeout(request.deadlineAt, options.searchTimeoutMs ?? 8_000);
29
+ if (timeoutMs <= 0)
30
+ break;
31
+ const response = await withTimeout(options.searchService.search({
32
+ query,
33
+ maxResults: Math.max(sourceLimit, Math.min(8, options.maxResults ?? sourceLimit)),
34
+ verbosity: 'snippets',
35
+ safeSearch: 'moderate',
36
+ trustedHosts: trustedHostsForRepair(request),
37
+ metadata: {
38
+ purpose: 'knowledge-gap-repair',
39
+ knowledgeSpaceId: request.spaceId,
40
+ },
41
+ }), Math.max(1_000, timeoutMs), 'Semantic gap repair search timed out.');
42
+ if (response.providerId)
43
+ providerIds.add(response.providerId);
44
+ for (const result of response.results) {
45
+ const canonical = canonicalizeUri(result.url);
46
+ if (!canonical || searchResults.has(canonical))
47
+ continue;
48
+ searchResults.set(canonical, { ...result, searchQuery: query, searchProviderId: response.providerId });
49
+ }
50
+ const partial = mergeRepairCandidates(existingCandidates, selectGapRepairCandidates([...searchResults.values()], existing, options, request, sourceLimit), sourceLimit);
51
+ if (hasEnoughRepairEvidence(partial, options))
52
+ break;
53
+ }
54
+ catch (error) {
55
+ lastError = error instanceof Error ? error.message : String(error);
56
+ if (deadlineExceeded(request.deadlineAt))
57
+ break;
58
+ }
28
59
  }
29
- catch (error) {
60
+ const allResults = [...searchResults.values()];
61
+ const candidates = mergeRepairCandidates(existingCandidates, selectGapRepairCandidates(allResults, existing, options, request, sourceLimit), sourceLimit);
62
+ if (!hasEnoughRepairEvidence(candidates, options)) {
30
63
  return {
31
64
  searched: true,
32
- query,
65
+ query: queries[0],
66
+ evidenceSufficient: false,
67
+ acceptedSourceIds: existingCandidates.map((candidate) => candidate.existingSourceId).filter((id) => Boolean(id)),
33
68
  ingestedSourceIds: [],
34
- skippedUrls: [],
35
- reason: error instanceof Error ? error.message : String(error),
36
- };
37
- }
38
- const existing = new Set(request.sources.flatMap((source) => [
39
- canonicalizeUri(source.canonicalUri ?? ''),
40
- canonicalizeUri(source.sourceUri ?? ''),
41
- ].filter((value) => Boolean(value))));
42
- const candidates = selectGapRepairCandidates(response.results, existing, options, query, request);
43
- if (candidates.length < Math.max(2, options.minDistinctDomains ?? 2)) {
44
- return {
45
- searched: true,
46
- query,
47
- ingestedSourceIds: [],
48
- skippedUrls: response.results.map((result) => result.url),
49
- sourceAssessments: buildSourceAssessments(response.results, candidates, existing, options, query, request),
50
- reason: 'Fewer than two distinct external sources were found for source-backed gap repair.',
69
+ skippedUrls: allResults.map((result) => result.url),
70
+ sourceAssessments: [
71
+ ...existingCandidates.map((candidate) => candidateToAssessment(candidate, true)),
72
+ ...buildSourceAssessments(allResults, candidates, existing, options, request),
73
+ ],
74
+ reason: lastError ?? 'Insufficient distinct source-backed evidence was found for gap repair.',
51
75
  };
52
76
  }
53
77
  const ingestedSourceIds = [];
54
78
  const skippedUrls = [];
55
- for (const result of candidates.slice(0, Math.max(2, Math.min(4, options.maxIngest ?? 2)))) {
79
+ const existingSourceIds = candidates.map((candidate) => candidate.existingSourceId).filter((id) => Boolean(id));
80
+ const newCandidates = candidates.filter((candidate) => !candidate.existingSourceId);
81
+ for (const result of newCandidates.slice(0, Math.max(0, Math.min(sourceLimit - existingSourceIds.length, options.maxIngest ?? sourceLimit)))) {
82
+ if (request.signal?.aborted || deadlineExceeded(request.deadlineAt))
83
+ break;
56
84
  try {
85
+ const timeoutMs = operationTimeout(request.deadlineAt, options.ingestTimeoutMs ?? 10_000);
86
+ if (timeoutMs <= 0)
87
+ break;
57
88
  const ingested = await withTimeout(options.ingestService.ingestUrl({
58
89
  url: result.url,
59
90
  ...(result.title ? { title: result.title } : {}),
@@ -64,8 +95,9 @@ async function repairKnowledgeGapsWithWeb(request, options) {
64
95
  knowledgeSpaceId: request.spaceId,
65
96
  sourceDiscovery: {
66
97
  purpose: 'semantic-gap-repair',
67
- query,
68
- providerId: response.providerId,
98
+ query: result.searchQuery,
99
+ searchQueries: queries.slice(0, searchLimit),
100
+ providerId: result.searchProviderId ?? [...providerIds][0],
69
101
  gapIds: request.gaps.map((gap) => gap.id),
70
102
  gapQuestions: request.gaps.map((gap) => gap.title),
71
103
  originalSourceIds: request.sources.map((source) => source.id),
@@ -76,11 +108,12 @@ async function repairKnowledgeGapsWithWeb(request, options) {
76
108
  sourceDomain: result.domain ?? safeDomain(result.url),
77
109
  trustReason: result.reasons.join(', '),
78
110
  agreementSourceCount: candidates.length,
111
+ checkedSourceLimit: sourceLimit,
79
112
  selectedUrl: result.url,
80
113
  searchedAt: Date.now(),
81
114
  },
82
115
  },
83
- }), Math.max(1_000, options.ingestTimeoutMs ?? 10_000), 'Semantic gap repair source ingest timed out.');
116
+ }), Math.max(1_000, timeoutMs), 'Semantic gap repair source ingest timed out.');
84
117
  if (ingested.source.status === 'indexed' || ingested.source.status === 'pending') {
85
118
  ingestedSourceIds.push(ingested.source.id);
86
119
  }
@@ -92,27 +125,35 @@ async function repairKnowledgeGapsWithWeb(request, options) {
92
125
  }
93
126
  return {
94
127
  searched: true,
95
- query,
128
+ query: queries[0],
129
+ evidenceSufficient: true,
130
+ acceptedSourceIds: uniqueStrings([...existingSourceIds, ...ingestedSourceIds]),
96
131
  ingestedSourceIds,
97
132
  skippedUrls,
98
- sourceAssessments: buildSourceAssessments(response.results, candidates, existing, options, query, request),
99
- ...(ingestedSourceIds.length < 2 ? { reason: 'Gap repair searched but fewer than two sources were ingested.' } : {}),
133
+ sourceAssessments: [
134
+ ...existingCandidates.map((candidate) => candidateToAssessment(candidate, candidates.some((entry) => entry.url === candidate.url))),
135
+ ...buildSourceAssessments(allResults, candidates, existing, options, request),
136
+ ],
137
+ ...(existingSourceIds.length + ingestedSourceIds.length < 1 ? { reason: 'Gap repair searched but did not accept usable sources.' } : {}),
100
138
  };
101
139
  }
102
- function buildGapRepairQuery(request) {
140
+ function buildGapRepairQueries(request) {
103
141
  const subject = bestSubject(request);
104
142
  if (!subject)
105
- return null;
106
- const gapTerms = uniqueStrings(request.gaps.flatMap((gap) => [
143
+ return [];
144
+ const gapTerms = clampSearchTerms(uniqueStrings(request.gaps.flatMap((gap) => [
107
145
  gap.title,
108
146
  gap.summary,
109
147
  readString(gap.metadata.reason),
110
- ])).join(' ');
148
+ ])).join(' '));
149
+ const profileTerms = inferGapProfileTerms(request);
111
150
  return uniqueStrings([
112
- subject,
113
- gapTerms,
114
- 'official specifications features',
115
- ]).join(' ');
151
+ [subject, gapTerms, 'official specifications'].filter(Boolean).join(' '),
152
+ [subject, profileTerms, 'product specifications'].filter(Boolean).join(' '),
153
+ [subject, profileTerms, 'features ports connectivity audio display'].filter(Boolean).join(' '),
154
+ [subject, 'manufacturer product page specifications'].join(' '),
155
+ [subject, 'datasheet manual specifications'].join(' '),
156
+ ].map((query) => query.replace(/\s+/g, ' ').trim()).filter(Boolean));
116
157
  }
117
158
  function bestSubject(request) {
118
159
  const linked = request.linkedObjects[0];
@@ -129,8 +170,39 @@ function bestSubject(request) {
129
170
  source?.title,
130
171
  ]).join(' ') || null;
131
172
  }
132
- function selectGapRepairCandidates(results, existingCanonicalUris, options, query, request) {
133
- const tokens = tokenizeSemanticQuery(query);
173
+ function inferGapProfileTerms(request) {
174
+ const text = request.gaps.map((gap) => `${gap.title} ${gap.summary ?? ''}`).join(' ').toLowerCase();
175
+ const terms = [];
176
+ if (/\b(port|ports|hdmi|usb|optical|rf|antenna|ethernet|rs-?232|composite|component|input|output|i\/o)\b/.test(text)) {
177
+ terms.push('ports inputs outputs connectivity');
178
+ }
179
+ if (/\b(bluetooth|wifi|wi-fi|wireless|network)\b/.test(text))
180
+ terms.push('wireless bluetooth wi-fi network');
181
+ if (/\b(refresh|hz|hdr|dolby|vision|gaming|vrr|allm|freesync)\b/.test(text))
182
+ terms.push('refresh rate hdr gaming vrr allm');
183
+ if (/\b(audio|speaker|sound|earc|arc)\b/.test(text))
184
+ terms.push('audio speakers earc arc');
185
+ if (/\b(display|screen|resolution|panel|lcd|oled|qled|nanocell)\b/.test(text))
186
+ terms.push('display resolution panel');
187
+ return uniqueStrings([
188
+ ...terms,
189
+ ...tokenizeSemanticQuery(text)
190
+ .filter((token) => token.length >= 3)
191
+ .filter((token) => !['what', 'does', 'have', 'which', 'with', 'from', 'that', 'this', 'current', 'source', 'text'].includes(token))
192
+ .slice(0, 12),
193
+ ]).join(' ');
194
+ }
195
+ function clampSearchTerms(value) {
196
+ return tokenizeSemanticQuery(value).slice(0, 24).join(' ');
197
+ }
198
+ function existingSources(request) {
199
+ return new Set(request.sources.flatMap((source) => [
200
+ canonicalizeUri(source.canonicalUri ?? ''),
201
+ canonicalizeUri(source.sourceUri ?? ''),
202
+ ].filter((value) => Boolean(value))));
203
+ }
204
+ function selectGapRepairCandidates(results, existingCanonicalUris, options, request, sourceLimit) {
205
+ const tokens = tokenizeSemanticQuery([bestSubject(request), inferGapProfileTerms(request)].filter(Boolean).join(' '));
134
206
  const minimumConfidence = Math.max(1, Math.min(100, options.minConfidence ?? 70));
135
207
  const byDomain = new Map();
136
208
  for (const result of results) {
@@ -143,14 +215,52 @@ function selectGapRepairCandidates(results, existingCanonicalUris, options, quer
143
215
  const domain = result.domain ?? safeDomain(result.url);
144
216
  if (!domain || byDomain.has(domain))
145
217
  continue;
146
- const assessment = assessGapRepairSource(result, query, request);
218
+ const assessment = assessGapRepairSource(result, result.searchQuery, request);
147
219
  if (assessment.confidence < minimumConfidence)
148
220
  continue;
149
221
  byDomain.set(domain, { ...result, confidence: assessment.confidence, reasons: assessment.reasons });
150
222
  }
151
223
  return [...byDomain.values()]
152
- .sort((left, right) => right.confidence - left.confidence || left.rank - right.rank)
153
- .slice(0, Math.max(2, Math.min(8, options.maxResults ?? 6)));
224
+ .sort(compareRepairCandidates)
225
+ .slice(0, sourceLimit);
226
+ }
227
+ function selectExistingRepairSources(request, options, sourceLimit) {
228
+ const minimumConfidence = Math.max(1, Math.min(100, options.minConfidence ?? 70));
229
+ const byDomain = new Map();
230
+ for (const source of request.sources) {
231
+ if (source.status !== 'indexed' || isGeneratedOrSyntheticSource(source))
232
+ continue;
233
+ const url = source.sourceUri ?? source.canonicalUri;
234
+ if (!url)
235
+ continue;
236
+ const domain = safeDomain(url);
237
+ if (!domain || byDomain.has(domain))
238
+ continue;
239
+ const snippet = [source.summary, source.description, source.tags.join(' ')].filter(Boolean).join(' ');
240
+ const result = {
241
+ rank: 0,
242
+ url,
243
+ domain,
244
+ displayUrl: url,
245
+ type: 'organic',
246
+ providerId: 'indexed',
247
+ metadata: source.metadata,
248
+ searchQuery: request.query,
249
+ searchProviderId: 'indexed',
250
+ ...(source.title ? { title: source.title } : {}),
251
+ ...(snippet ? { snippet } : {}),
252
+ };
253
+ const assessment = assessGapRepairSource(result, request.query, request);
254
+ if (assessment.confidence < minimumConfidence && !isOfficialOrVendorSource(assessment.reasons))
255
+ continue;
256
+ byDomain.set(domain, {
257
+ ...result,
258
+ existingSourceId: source.id,
259
+ confidence: Math.max(assessment.confidence, isOfficialOrVendorSource(assessment.reasons) ? 82 : assessment.confidence),
260
+ reasons: uniqueStrings([...assessment.reasons, 'already-indexed']),
261
+ });
262
+ }
263
+ return [...byDomain.values()].sort(compareRepairCandidates).slice(0, sourceLimit);
154
264
  }
155
265
  function assessGapRepairSource(result, query, request) {
156
266
  const searchable = [result.title, result.snippet, result.url, result.domain].filter(Boolean).join(' ').toLowerCase();
@@ -194,26 +304,61 @@ function assessGapRepairSource(result, query, request) {
194
304
  reasons.push('source-purpose');
195
305
  }
196
306
  if (domain && identities.manufacturers.some((manufacturer) => manufacturer.length >= 2 && domain.includes(manufacturer.toLowerCase()))) {
197
- score += 10;
307
+ score += 28;
198
308
  reasons.push('manufacturer-domain');
199
309
  }
310
+ if (domain && isOfficialVendorDomain(domain, identities.manufacturers)) {
311
+ score += 28;
312
+ reasons.push('official-vendor-domain');
313
+ }
200
314
  return {
201
315
  url: result.url,
202
316
  ...(result.title ? { title: result.title } : {}),
203
317
  ...(domain ? { domain } : {}),
204
318
  rank: result.rank,
319
+ query: result.searchQuery,
205
320
  confidence: Math.max(0, Math.min(100, score)),
206
321
  reasons,
207
322
  ...(reasons.length > 0 ? { trustReason: reasons.join(', ') } : {}),
208
323
  };
209
324
  }
210
- function buildSourceAssessments(results, candidates, existingCanonicalUris, options, query, request) {
325
+ function mergeRepairCandidates(existingCandidates, searchCandidates, sourceLimit) {
326
+ const byCanonical = new Map();
327
+ for (const candidate of [...existingCandidates, ...searchCandidates]) {
328
+ const key = canonicalizeUri(candidate.url) ?? candidate.url;
329
+ const current = byCanonical.get(key);
330
+ if (!current || compareRepairCandidates(candidate, current) < 0)
331
+ byCanonical.set(key, candidate);
332
+ }
333
+ return [...byCanonical.values()].sort(compareRepairCandidates).slice(0, sourceLimit);
334
+ }
335
+ function hasEnoughRepairEvidence(candidates, options) {
336
+ if (candidates.some((candidate) => candidate.reasons.includes('official-vendor-domain')))
337
+ return true;
338
+ const required = Math.max(2, options.minDistinctDomains ?? 2);
339
+ return new Set(candidates.map((candidate) => candidate.domain ?? safeDomain(candidate.url)).filter(Boolean)).size >= required;
340
+ }
341
+ function compareRepairCandidates(left, right) {
342
+ return sourceAuthorityScore(right) - sourceAuthorityScore(left)
343
+ || right.confidence - left.confidence
344
+ || left.rank - right.rank;
345
+ }
346
+ function sourceAuthorityScore(candidate) {
347
+ if (candidate.reasons.includes('official-vendor-domain'))
348
+ return 3;
349
+ if (candidate.reasons.includes('manufacturer-domain'))
350
+ return 2;
351
+ if (candidate.existingSourceId)
352
+ return 1;
353
+ return 0;
354
+ }
355
+ function buildSourceAssessments(results, candidates, existingCanonicalUris, options, request) {
211
356
  const accepted = new Set(candidates.map((candidate) => canonicalizeUri(candidate.url)));
212
357
  const acceptedDomains = new Set(candidates.map((candidate) => candidate.domain ?? safeDomain(candidate.url)).filter(Boolean));
213
358
  const minimumConfidence = Math.max(1, Math.min(100, options.minConfidence ?? 70));
214
- const tokens = tokenizeSemanticQuery(query);
359
+ const tokens = tokenizeSemanticQuery([bestSubject(request), inferGapProfileTerms(request)].filter(Boolean).join(' '));
215
360
  return results.map((result) => {
216
- const assessment = assessGapRepairSource(result, query, request);
361
+ const assessment = assessGapRepairSource(result, result.searchQuery, request);
217
362
  const canonical = canonicalizeUri(result.url);
218
363
  const domain = result.domain ?? safeDomain(result.url);
219
364
  const isAccepted = Boolean(canonical && accepted.has(canonical));
@@ -237,6 +382,20 @@ function buildSourceAssessments(results, candidates, existingCanonicalUris, opti
237
382
  };
238
383
  });
239
384
  }
385
+ function candidateToAssessment(candidate, accepted) {
386
+ return {
387
+ url: candidate.url,
388
+ ...(candidate.title ? { title: candidate.title } : {}),
389
+ ...(candidate.domain ? { domain: candidate.domain } : {}),
390
+ rank: candidate.rank,
391
+ query: candidate.searchQuery,
392
+ accepted,
393
+ confidence: candidate.confidence,
394
+ reasons: candidate.reasons,
395
+ trustReason: candidate.reasons.join(', '),
396
+ ...(accepted ? {} : { rejectionReason: 'not-selected' }),
397
+ };
398
+ }
240
399
  function sourceIdentityHints(request) {
241
400
  const subjects = uniqueStrings([
242
401
  ...request.linkedObjects.flatMap((node) => [node.title, ...node.aliases]),
@@ -251,9 +410,26 @@ function sourceIdentityHints(request) {
251
410
  const manufacturers = uniqueStrings(request.linkedObjects.flatMap((node) => [
252
411
  readString(node.metadata.manufacturer),
253
412
  readString(node.metadata.vendor),
254
- ]).concat(request.sources.flatMap((source) => manufacturerHints(`${source.title ?? ''} ${source.sourceUri ?? ''} ${source.canonicalUri ?? ''}`))));
413
+ ]).concat(request.sources.flatMap((source) => manufacturerHints(`${source.title ?? ''} ${source.sourceUri ?? ''} ${source.canonicalUri ?? ''}`)))
414
+ .map((manufacturer) => manufacturer?.trim().toLowerCase())
415
+ .filter((manufacturer) => Boolean(manufacturer)));
255
416
  return { models, manufacturers, subjects };
256
417
  }
418
+ function trustedHostsForRepair(request) {
419
+ const manufacturers = sourceIdentityHints(request).manufacturers;
420
+ const hosts = [];
421
+ if (manufacturers.includes('lg'))
422
+ hosts.push('lg.com', 'www.lg.com');
423
+ if (manufacturers.includes('sony'))
424
+ hosts.push('sony.com', 'www.sony.com');
425
+ if (manufacturers.includes('samsung'))
426
+ hosts.push('samsung.com', 'www.samsung.com');
427
+ if (manufacturers.includes('apple'))
428
+ hosts.push('apple.com', 'support.apple.com');
429
+ if (manufacturers.includes('espressif') || manufacturers.includes('esp32'))
430
+ hosts.push('espressif.com', 'docs.espressif.com');
431
+ return uniqueStrings(hosts);
432
+ }
257
433
  function isGenericSubject(value) {
258
434
  return /^(tv|television|device|manual|user guide|owner manual|home assistant|service|provider|integration)$/i.test(value.trim());
259
435
  }
@@ -264,6 +440,34 @@ function manufacturerHints(value) {
264
440
  const hints = value.match(/\b(lg|samsung|sony|vizio|tcl|hisense|philips|panasonic|kasa|tp-link|ecobee|honeywell|ring|arlo|nest|eufy|aqara|sonoff|shelly|lutron|leviton|ikea|bosch|ge|whirlpool|frigidaire|apple|espressif|esp32|nabu casa|home assistant)\b/gi) ?? [];
265
441
  return uniqueStrings(hints.map((hint) => hint.toLowerCase()));
266
442
  }
443
+ function isGeneratedOrSyntheticSource(source) {
444
+ return source.tags.includes('generated-page')
445
+ || source.metadata.projectionKind === 'device-passport'
446
+ || source.metadata.projectionKind === 'room-page';
447
+ }
448
+ function isOfficialVendorDomain(domain, manufacturers) {
449
+ const lower = domain.toLowerCase();
450
+ if (manufacturers.some((manufacturer) => manufacturer === 'lg' && /(^|\.)lg\.com$/.test(lower)))
451
+ return true;
452
+ if (manufacturers.some((manufacturer) => manufacturer === 'sony' && /(^|\.)sony\.(com|net)$/.test(lower)))
453
+ return true;
454
+ if (manufacturers.some((manufacturer) => manufacturer === 'samsung' && /(^|\.)samsung\.com$/.test(lower)))
455
+ return true;
456
+ if (manufacturers.some((manufacturer) => manufacturer === 'apple' && /(^|\.)apple\.com$/.test(lower)))
457
+ return true;
458
+ return manufacturers.some((manufacturer) => manufacturer.length >= 4 && lower.includes(manufacturer.toLowerCase()) && /\b(support|docs?|developer|product)\b/.test(lower));
459
+ }
460
+ function isOfficialOrVendorSource(reasons) {
461
+ return reasons.includes('official-vendor-domain') || reasons.includes('manufacturer-domain');
462
+ }
463
+ function deadlineExceeded(deadlineAt) {
464
+ return typeof deadlineAt === 'number' && Date.now() >= deadlineAt;
465
+ }
466
+ function operationTimeout(deadlineAt, fallbackMs) {
467
+ if (typeof deadlineAt !== 'number')
468
+ return fallbackMs;
469
+ return Math.min(fallbackMs, Math.max(0, deadlineAt - Date.now()));
470
+ }
267
471
  function hasIdentity(searchable, value) {
268
472
  const normalized = value.trim().toLowerCase();
269
473
  if (!normalized)
@@ -282,19 +486,6 @@ function safeDomain(url) {
282
486
  return undefined;
283
487
  }
284
488
  }
285
- async function withTimeout(promise, timeoutMs, message) {
286
- let timer;
287
- const timeout = new Promise((_, reject) => {
288
- timer = setTimeout(() => reject(new Error(message)), timeoutMs);
289
- });
290
- try {
291
- return await Promise.race([promise, timeout]);
292
- }
293
- finally {
294
- if (timer)
295
- clearTimeout(timer);
296
- }
297
- }
298
489
  async function yieldToEventLoop() {
299
490
  await new Promise((resolve) => setTimeout(resolve, 0));
300
491
  }
@@ -0,0 +1,12 @@
1
+ import type { KnowledgeNodeRecord, KnowledgeRefinementTaskRecord, KnowledgeRefinementTaskState, KnowledgeRefinementTaskTrigger } from '../types.js';
2
+ import type { KnowledgeStore } from '../store.js';
3
+ export interface RefinementTaskGapContext {
4
+ readonly gap: KnowledgeNodeRecord;
5
+ readonly sources: readonly {
6
+ readonly id: string;
7
+ }[];
8
+ readonly linkedObjects: readonly KnowledgeNodeRecord[];
9
+ }
10
+ export declare function upsertRefinementTaskForGap(store: KnowledgeStore, spaceId: string, context: RefinementTaskGapContext, trigger: KnowledgeRefinementTaskTrigger, state: KnowledgeRefinementTaskState, message: string, data?: Record<string, unknown>): Promise<KnowledgeRefinementTaskRecord>;
11
+ export declare function updateRefinementTask(store: KnowledgeStore, task: KnowledgeRefinementTaskRecord, state: KnowledgeRefinementTaskState, message: string, data?: Record<string, unknown>): Promise<KnowledgeRefinementTaskRecord>;
12
+ //# sourceMappingURL=self-improvement-tasks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"self-improvement-tasks.d.ts","sourceRoot":"","sources":["../../../../../src/_internal/platform/knowledge/semantic/self-improvement-tasks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,mBAAmB,EACnB,6BAA6B,EAC7B,4BAA4B,EAC5B,8BAA8B,EAC/B,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAGlD,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,GAAG,EAAE,mBAAmB,CAAC;IAClC,QAAQ,CAAC,OAAO,EAAE,SAAS;QAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACrD,QAAQ,CAAC,aAAa,EAAE,SAAS,mBAAmB,EAAE,CAAC;CACxD;AAED,wBAAsB,0BAA0B,CAC9C,KAAK,EAAE,cAAc,EACrB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,wBAAwB,EACjC,OAAO,EAAE,8BAA8B,EACvC,KAAK,EAAE,4BAA4B,EACnC,OAAO,EAAE,MAAM,EACf,IAAI,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GACjC,OAAO,CAAC,6BAA6B,CAAC,CAiCxC;AAED,wBAAsB,oBAAoB,CACxC,KAAK,EAAE,cAAc,EACrB,IAAI,EAAE,6BAA6B,EACnC,KAAK,EAAE,4BAA4B,EACnC,OAAO,EAAE,MAAM,EACf,IAAI,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GACjC,OAAO,CAAC,6BAA6B,CAAC,CAmBxC"}
@@ -0,0 +1,79 @@
1
+ import { readString, semanticHash, semanticMetadata } from './utils.js';
2
+ export async function upsertRefinementTaskForGap(store, spaceId, context, trigger, state, message, data = {}) {
3
+ const subject = context.linkedObjects[0];
4
+ const id = `kref-${semanticHash(spaceId, context.gap.id, subject?.id ?? 'unscoped')}`;
5
+ const existing = store.getRefinementTask(id);
6
+ const attemptCount = existing?.attemptCount ?? 0;
7
+ return store.upsertRefinementTask({
8
+ id,
9
+ spaceId,
10
+ ...(subject ? {
11
+ subjectKind: 'node',
12
+ subjectId: subject.id,
13
+ subjectTitle: subjectTitle(subject),
14
+ subjectType: subject.kind,
15
+ } : {}),
16
+ gapId: context.gap.id,
17
+ state,
18
+ trigger,
19
+ priority: refinementPriority(context.gap),
20
+ budget: {
21
+ maxSearches: 5,
22
+ maxSources: 5,
23
+ maxLlmCalls: 1,
24
+ },
25
+ attemptCount,
26
+ appendTrace: [trace(state, message, data)],
27
+ metadata: semanticMetadata(spaceId, {
28
+ gapTitle: context.gap.title,
29
+ gapKind: readString(context.gap.metadata.gapKind) ?? 'unknown',
30
+ sourceIds: context.sources.map((source) => source.id),
31
+ linkedObjectIds: context.linkedObjects.map((node) => node.id),
32
+ policyVersion: 'knowledge-refinement-v1',
33
+ }),
34
+ });
35
+ }
36
+ export async function updateRefinementTask(store, task, state, message, data = {}) {
37
+ return store.upsertRefinementTask({
38
+ id: task.id,
39
+ spaceId: task.spaceId,
40
+ subjectKind: task.subjectKind,
41
+ subjectId: task.subjectId,
42
+ subjectTitle: task.subjectTitle,
43
+ subjectType: task.subjectType,
44
+ gapId: task.gapId,
45
+ issueId: task.issueId,
46
+ state,
47
+ priority: task.priority,
48
+ trigger: task.trigger,
49
+ budget: task.budget,
50
+ attemptCount: state === 'searching' ? task.attemptCount + 1 : task.attemptCount,
51
+ ...(state === 'blocked' || state === 'failed' ? { blockedReason: message } : {}),
52
+ appendTrace: [trace(state, message, data)],
53
+ metadata: task.metadata,
54
+ });
55
+ }
56
+ function trace(state, message, data = {}) {
57
+ return {
58
+ at: Date.now(),
59
+ state,
60
+ message,
61
+ ...(Object.keys(data).length > 0 ? { data } : {}),
62
+ };
63
+ }
64
+ function refinementPriority(gap) {
65
+ const severity = readString(gap.metadata.severity);
66
+ const kind = readString(gap.metadata.gapKind);
67
+ if (severity === 'error')
68
+ return 'urgent';
69
+ if (severity === 'warning' || kind === 'intrinsic_features')
70
+ return 'high';
71
+ return 'normal';
72
+ }
73
+ function subjectTitle(subject) {
74
+ return [
75
+ readString(subject.metadata.manufacturer),
76
+ readString(subject.metadata.model),
77
+ subject.title,
78
+ ].filter(Boolean).join(' ');
79
+ }
@@ -4,6 +4,10 @@ interface SelfImproveContext {
4
4
  readonly store: KnowledgeStore;
5
5
  readonly gapRepairer?: KnowledgeSemanticGapRepairer | null;
6
6
  readonly activeGapRepairs: Set<string>;
7
+ readonly enrichSource?: (sourceId: string, options: {
8
+ readonly force?: boolean;
9
+ readonly knowledgeSpaceId?: string;
10
+ }) => Promise<unknown>;
7
11
  }
8
12
  export declare function runKnowledgeSemanticSelfImprovement(context: SelfImproveContext, input?: KnowledgeSemanticSelfImproveInput): Promise<KnowledgeSemanticSelfImproveResult>;
9
13
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"self-improvement.d.ts","sourceRoot":"","sources":["../../../../../src/_internal/platform/knowledge/semantic/self-improvement.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAUlD,OAAO,KAAK,EACV,4BAA4B,EAC5B,iCAAiC,EACjC,kCAAkC,EACnC,MAAM,YAAY,CAAC;AAkBpB,UAAU,kBAAkB;IAC1B,QAAQ,CAAC,KAAK,EAAE,cAAc,CAAC;IAC/B,QAAQ,CAAC,WAAW,CAAC,EAAE,4BAA4B,GAAG,IAAI,CAAC;IAC3D,QAAQ,CAAC,gBAAgB,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CACxC;AAUD,wBAAsB,mCAAmC,CACvD,OAAO,EAAE,kBAAkB,EAC3B,KAAK,GAAE,iCAAsC,GAC5C,OAAO,CAAC,kCAAkC,CAAC,CAmL7C"}
1
+ {"version":3,"file":"self-improvement.d.ts","sourceRoot":"","sources":["../../../../../src/_internal/platform/knowledge/semantic/self-improvement.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAQlD,OAAO,KAAK,EACV,4BAA4B,EAC5B,iCAAiC,EACjC,kCAAkC,EACnC,MAAM,YAAY,CAAC;AAYpB,UAAU,kBAAkB;IAC1B,QAAQ,CAAC,KAAK,EAAE,cAAc,CAAC;IAC/B,QAAQ,CAAC,WAAW,CAAC,EAAE,4BAA4B,GAAG,IAAI,CAAC;IAC3D,QAAQ,CAAC,gBAAgB,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACvC,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE;QAAE,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;QAAC,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;CAC3I;AAUD,wBAAsB,mCAAmC,CACvD,OAAO,EAAE,kBAAkB,EAC3B,KAAK,GAAE,iCAAsC,GAC5C,OAAO,CAAC,kCAAkC,CAAC,CA0N7C"}