dsh-search-enhance 0.2.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -28
- package/README.zh.md +17 -30
- package/client/client.js +37 -47
- package/client/types/client/locales.d.ts +7 -6
- package/client/types/web-config/contracts.d.ts +5 -4
- package/cordis.patch.yml +4 -0
- package/lib/config.d.ts +2 -10
- package/lib/config.js +7 -62
- package/lib/contracts/sources.d.ts +0 -5
- package/lib/diagnostics/status.js +0 -2
- package/lib/diagnostics/types.d.ts +1 -3
- package/lib/documentation/service.d.ts +1 -1
- package/lib/documentation/service.js +16 -8
- package/lib/index.js +18 -104
- package/lib/orchestration/orchestrator.js +4 -9
- package/lib/orchestration/types.d.ts +2 -6
- package/lib/orchestration/types.js +2 -0
- package/lib/presentation/render.d.ts +3 -0
- package/lib/presentation/render.js +61 -35
- package/lib/presentation/web-card.js +16 -6
- package/lib/prompt/tool-discovery.d.ts +1 -1
- package/lib/prompt/tool-discovery.js +10 -12
- package/lib/providers/context7.js +11 -1
- package/lib/providers/search-api-request.d.ts +1 -1
- package/lib/providers/search-api-request.js +2 -2
- package/lib/providers/search-api.d.ts +1 -4
- package/lib/providers/search-api.js +1 -4
- package/lib/research/bridge.d.ts +29 -0
- package/lib/research/bridge.js +106 -0
- package/lib/research/child.d.ts +13 -0
- package/lib/research/child.js +133 -0
- package/lib/research/executor.d.ts +28 -0
- package/lib/research/executor.js +94 -0
- package/lib/research/model.d.ts +19 -0
- package/lib/research/model.js +43 -0
- package/lib/research/persona.d.ts +2 -0
- package/lib/research/persona.js +14 -0
- package/lib/research/profile.d.ts +8 -0
- package/lib/research/profile.js +48 -0
- package/lib/research/protocol.d.ts +29 -0
- package/lib/research/protocol.js +3 -0
- package/lib/research/report.d.ts +89 -0
- package/lib/research/report.js +51 -0
- package/lib/research/sdk.d.ts +45 -0
- package/lib/research/sdk.js +66 -0
- package/lib/research/session-mirror.d.ts +10 -0
- package/lib/research/session-mirror.js +135 -0
- package/lib/research/session-observer.d.ts +7 -0
- package/lib/research/session-observer.js +57 -0
- package/lib/search/index.d.ts +1 -1
- package/lib/search/index.js +1 -1
- package/lib/search/policy.d.ts +12 -0
- package/lib/search/policy.js +35 -0
- package/lib/source-storage/domain.js +4 -4
- package/lib/source-storage/retention.js +0 -2
- package/lib/source-storage/service.d.ts +1 -0
- package/lib/source-storage/service.js +3 -0
- package/lib/source-storage/store.d.ts +2 -0
- package/lib/source-storage/store.js +17 -0
- package/lib/tool-discovery/capabilities.d.ts +4 -5
- package/lib/tool-discovery/capabilities.js +11 -25
- package/lib/tool-discovery/fold.d.ts +14 -6
- package/lib/tool-discovery/fold.js +49 -34
- package/lib/tool-discovery/manager.d.ts +11 -2
- package/lib/tool-discovery/manager.js +81 -16
- package/lib/tools/docs-search.js +5 -4
- package/lib/tools/index.d.ts +1 -1
- package/lib/tools/index.js +1 -1
- package/lib/tools/schemas.d.ts +6 -14
- package/lib/tools/schemas.js +8 -14
- package/lib/tools/services.d.ts +15 -0
- package/lib/tools/services.js +96 -0
- package/lib/tools/web-research.d.ts +9 -0
- package/lib/tools/web-research.js +28 -0
- package/lib/tools/web-search.d.ts +1 -1
- package/lib/tools/web-search.js +8 -9
- package/lib/web-config/contracts.d.ts +5 -4
- package/lib/web-config/contracts.js +1 -2
- package/lib/web-config/host.js +4 -5
- package/package.json +54 -17
- package/lib/search/profiles.d.ts +0 -45
- package/lib/search/profiles.js +0 -213
- package/lib/tools/search-tools.d.ts +0 -62
- package/lib/tools/search-tools.js +0 -65
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { CredentialProvider } from '@deepseek-ai/dsh-credentials';
|
|
2
|
-
import type { Config, FallbackMode, MinimumCapabilityProfile, SearchApiProtocol,
|
|
2
|
+
import type { Config, FallbackMode, MinimumCapabilityProfile, SearchApiProtocol, ThinkingLevel } from '../config.js';
|
|
3
3
|
import type { ProviderErrorKind } from '../provider-runtime/index.js';
|
|
4
4
|
export declare const DIAGNOSTIC_ACTIONS: readonly ["show", "test"];
|
|
5
5
|
export type DiagnosticAction = (typeof DIAGNOSTIC_ACTIONS)[number];
|
|
@@ -38,8 +38,6 @@ export interface DiagnosticMinimumProfile {
|
|
|
38
38
|
}
|
|
39
39
|
/** Safe deployment facts. Values, refs, endpoints, proxies, and paths do not fit this type. */
|
|
40
40
|
export interface DiagnosticConfigurationStatus {
|
|
41
|
-
readonly defaultProfile: SearchProfile;
|
|
42
|
-
readonly defaultDepth: SearchDepth;
|
|
43
41
|
readonly searchApiProtocol: SearchApiProtocol;
|
|
44
42
|
readonly searchModelConfigured: boolean;
|
|
45
43
|
readonly thinkingLevel: ThinkingLevel;
|
|
@@ -18,7 +18,7 @@ export interface DocumentationProviderStatus {
|
|
|
18
18
|
readonly provider: DocumentationResultProvider;
|
|
19
19
|
readonly state: DocumentationProviderState;
|
|
20
20
|
}
|
|
21
|
-
export declare const DOCUMENTATION_WARNING_CODES: readonly ["provider_failed", "provider_not_configured", "cache_stale", "cache_evicted", "provider_result_truncated", "no_results"];
|
|
21
|
+
export declare const DOCUMENTATION_WARNING_CODES: readonly ["provider_failed", "provider_not_configured", "cache_stale", "cache_evicted", "provider_result_truncated", "library_not_found", "collection_limit_reached", "visible_sources_truncated", "no_results"];
|
|
22
22
|
export type DocumentationWarningCode = (typeof DOCUMENTATION_WARNING_CODES)[number];
|
|
23
23
|
export declare const DOCUMENTATION_CACHE_PATH_STATES: readonly ["hit", "miss", "refresh", "stale", "skipped", "failed"];
|
|
24
24
|
export type DocumentationCachePathState = (typeof DOCUMENTATION_CACHE_PATH_STATES)[number];
|
|
@@ -15,6 +15,9 @@ export const DOCUMENTATION_WARNING_CODES = [
|
|
|
15
15
|
'cache_stale',
|
|
16
16
|
'cache_evicted',
|
|
17
17
|
'provider_result_truncated',
|
|
18
|
+
'library_not_found',
|
|
19
|
+
'collection_limit_reached',
|
|
20
|
+
'visible_sources_truncated',
|
|
18
21
|
'no_results',
|
|
19
22
|
];
|
|
20
23
|
export const DOCUMENTATION_CACHE_PATH_STATES = [
|
|
@@ -592,12 +595,18 @@ export class DocumentationSearchService extends Service {
|
|
|
592
595
|
const qualitySources = applySourceQuality(validated.query, compatibleSources);
|
|
593
596
|
const sources = Object.freeze(qualitySources.slice(0, input.maxResults));
|
|
594
597
|
const snippets = Object.freeze(contextOutcome.snippets.slice(0, input.maxResults));
|
|
595
|
-
const
|
|
596
|
-
|
|
597
|
-
|
|
598
|
+
const providerTruncated = contextOutcome.truncated || exaOutcome.truncated;
|
|
599
|
+
const visibleSourcesTruncated = sources.length < qualitySources.length;
|
|
600
|
+
const truncated = providerTruncated
|
|
601
|
+
|| visibleSourcesTruncated
|
|
598
602
|
|| snippets.length < contextOutcome.snippets.length;
|
|
599
|
-
if (
|
|
603
|
+
if (providerTruncated)
|
|
600
604
|
warnings.push(fixedWarning('provider_result_truncated'));
|
|
605
|
+
if (visibleSourcesTruncated)
|
|
606
|
+
warnings.push(fixedWarning('visible_sources_truncated'));
|
|
607
|
+
if (exaOutcome.state === 'complete' && exaOutcome.sources.length >= input.maxResults) {
|
|
608
|
+
warnings.push(fixedWarning('collection_limit_reached', { provider: 'exa', count: input.maxResults }));
|
|
609
|
+
}
|
|
601
610
|
const completedValidPath = contextOutcome.completedValidPath || exaOutcome.completedValidPath;
|
|
602
611
|
if (sources.length === 0 && snippets.length === 0 && !completedValidPath) {
|
|
603
612
|
const cause = contextOutcome.error ?? exaOutcome.error ?? new ProviderError({
|
|
@@ -613,11 +622,9 @@ export class DocumentationSearchService extends Service {
|
|
|
613
622
|
}
|
|
614
623
|
const cache = contextOutcome.cache ?? cacheReport(cachePath('skipped', 0, 'provider_not_selected'), cachePath('skipped', 0, 'provider_not_selected'));
|
|
615
624
|
const persistence = Object.freeze({
|
|
616
|
-
collectionTruncated: truncated,
|
|
617
|
-
depth: 'compact',
|
|
618
|
-
profile: 'coding_docs',
|
|
625
|
+
collectionTruncated: exaOutcome.truncated,
|
|
619
626
|
query: validated.query,
|
|
620
|
-
sources,
|
|
627
|
+
sources: Object.freeze(qualitySources),
|
|
621
628
|
});
|
|
622
629
|
return Object.freeze({
|
|
623
630
|
attempts,
|
|
@@ -712,6 +719,7 @@ export class DocumentationSearchService extends Service {
|
|
|
712
719
|
}
|
|
713
720
|
if (selectedLibrary?.id === undefined || !isContext7LibraryId(selectedLibrary.id)) {
|
|
714
721
|
docsReport = cachePath('skipped', 0, 'library_not_found');
|
|
722
|
+
warnings.push(fixedWarning('library_not_found', { provider: 'context7', path: 'resolve' }));
|
|
715
723
|
attempts.push(skippedAttempt('context7-docs'));
|
|
716
724
|
return frozenPathOutcome({
|
|
717
725
|
attempts,
|
package/lib/index.js
CHANGED
|
@@ -1,116 +1,30 @@
|
|
|
1
1
|
import { Config as SearchEnhanceConfig } from './config.js';
|
|
2
|
-
import {
|
|
3
|
-
import { SearchOrchestrator } from './orchestration/index.js';
|
|
2
|
+
import { ResearchExecutor } from './research/executor.js';
|
|
4
3
|
import { registerToolDiscoveryGuidance } from './prompt/tool-discovery.js';
|
|
5
|
-
import { Context7RemoteClient } from './providers/context7.js';
|
|
6
|
-
import { ExaProvider } from './providers/exa.js';
|
|
7
|
-
import { FirecrawlSearchProvider } from './providers/firecrawl.js';
|
|
8
|
-
import { FirecrawlScrapeProvider } from './providers/firecrawl-scrape.js';
|
|
9
|
-
import { DirectFetchProvider } from './providers/direct-fetch.js';
|
|
10
|
-
import { ParallelSearchProvider } from './providers/parallel.js';
|
|
11
|
-
import { ParallelExtractProvider } from './providers/parallel-extract.js';
|
|
12
|
-
import { SearchApiProvider } from './providers/search-api.js';
|
|
13
|
-
import { SmartDirectProvider } from './providers/smart-direct.js';
|
|
14
|
-
import { TavilySearchProvider } from './providers/tavily.js';
|
|
15
|
-
import { TavilyExtractProvider } from './providers/tavily-extract.js';
|
|
16
|
-
import { TavilyMapProvider } from './providers/tavily-map.js';
|
|
17
|
-
import { SOURCE_RECORD_DOMAIN_SPEC, SOURCE_RECORD_TABLE_NAME, SearchEnhanceSourceService, SourceRecordStore, } from './source-storage/index.js';
|
|
18
4
|
import { installAgentToolDisclosure } from './tool-discovery/index.js';
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
5
|
+
import { createWebResearchTool } from './tools/index.js';
|
|
6
|
+
import { createSearchServices } from './tools/services.js';
|
|
21
7
|
import { installWebConfigBridge } from './web-config/host.js';
|
|
22
8
|
export const name = 'search-enhance';
|
|
23
|
-
|
|
9
|
+
// Display services are discovered per call through reflect.get, never required here.
|
|
10
|
+
export const inject = ['agents', 'configEditor', 'credentials', 'llm', 'storageDomain', 'subagents', 'systemPrompt', 'tools'];
|
|
24
11
|
export const Config = SearchEnhanceConfig;
|
|
25
12
|
export async function apply(ctx, config) {
|
|
26
|
-
const
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
});
|
|
36
|
-
const operations = new ForegroundOperationScope();
|
|
37
|
-
// ConfigEditor reconciles edits by rebuilding this plugin in-process; a saved
|
|
38
|
-
// configuration is active as soon as the edit request completes.
|
|
39
|
-
const getConfig = () => effective;
|
|
40
|
-
const providerDependencies = { credentials: ctx.credentials };
|
|
41
|
-
const context7Cache = new PersistentContext7Cache(ctx.storageDomain, {
|
|
42
|
-
maxEntries: effective.cache.maxEntries,
|
|
43
|
-
maxEntryBytes: effective.cache.context7EntryMaxBytes,
|
|
44
|
-
});
|
|
45
|
-
const cachedContext7 = new Context7CachedOperations(context7Cache);
|
|
46
|
-
const context7Remote = new Context7RemoteClient(providerDependencies);
|
|
47
|
-
const exa = new ExaProvider(providerDependencies);
|
|
48
|
-
let documentation;
|
|
49
|
-
// Keep dependent shutdown in one ordered disposer: stop both public-tool and
|
|
50
|
-
// service calls, close the lazy docs cache, then drain source writes/domain.
|
|
51
|
-
ctx.effect(() => async () => {
|
|
52
|
-
await Promise.all([
|
|
53
|
-
operations.stop(),
|
|
54
|
-
documentation?.stop() ?? Promise.resolve(),
|
|
55
|
-
]);
|
|
56
|
-
await context7Cache.close();
|
|
57
|
-
store.stop();
|
|
58
|
-
await store.drain();
|
|
59
|
-
await domain.close();
|
|
60
|
-
});
|
|
61
|
-
new SearchEnhanceSourceService(ctx, store);
|
|
62
|
-
documentation = new DocumentationSearchService(ctx, {
|
|
63
|
-
context7: context7Remote,
|
|
64
|
-
context7Cache: cachedContext7,
|
|
65
|
-
exa,
|
|
66
|
-
getConfig,
|
|
67
|
-
});
|
|
68
|
-
const searchApi = new SearchApiProvider({
|
|
69
|
-
credentials: ctx.credentials,
|
|
70
|
-
getConfig,
|
|
71
|
-
});
|
|
72
|
-
const firecrawl = new FirecrawlSearchProvider(providerDependencies);
|
|
73
|
-
const tavily = new TavilySearchProvider(providerDependencies);
|
|
74
|
-
const parallel = new ParallelSearchProvider(providerDependencies);
|
|
75
|
-
const orchestrator = new SearchOrchestrator({
|
|
76
|
-
exa,
|
|
77
|
-
firecrawl,
|
|
78
|
-
getConfig,
|
|
79
|
-
mainSearch: searchApi,
|
|
80
|
-
parallel,
|
|
81
|
-
tavily,
|
|
82
|
-
});
|
|
83
|
-
const webExtract = new WebExtractOrchestrator({
|
|
84
|
-
tavilyExtract: new TavilyExtractProvider(providerDependencies),
|
|
85
|
-
firecrawlScrape: new FirecrawlScrapeProvider(providerDependencies),
|
|
86
|
-
parallelExtract: new ParallelExtractProvider(providerDependencies),
|
|
87
|
-
smartDirect: new SmartDirectProvider(),
|
|
88
|
-
direct: new DirectFetchProvider(),
|
|
89
|
-
getConfig,
|
|
90
|
-
});
|
|
91
|
-
const disclosure = installAgentToolDisclosure(ctx, {
|
|
92
|
-
mode: effective.toolDiscovery.mode,
|
|
93
|
-
webSearchDefinition: createWebSearchTool({
|
|
94
|
-
getConfig, operations, orchestrator, sources: ctx.searchEnhanceSources,
|
|
95
|
-
}),
|
|
96
|
-
deferredTools: {
|
|
97
|
-
sources: createSearchSourcesTool({
|
|
98
|
-
getConfig, operations, sources: ctx.searchEnhanceSources,
|
|
99
|
-
}),
|
|
100
|
-
site_map: createWebMapTool({
|
|
101
|
-
getConfig, operations, provider: new TavilyMapProvider(providerDependencies),
|
|
102
|
-
}),
|
|
13
|
+
const { tools, operations } = await createSearchServices(ctx, config);
|
|
14
|
+
const research = new ResearchExecutor(ctx, () => config);
|
|
15
|
+
ctx.effect(() => async () => { await operations.stop(); research.dispose(); });
|
|
16
|
+
installAgentToolDisclosure(ctx, {
|
|
17
|
+
mode: config.toolDiscovery.mode,
|
|
18
|
+
residentTools: {
|
|
19
|
+
docs_search: tools.docs_search,
|
|
20
|
+
web_extract: tools.web_extract,
|
|
21
|
+
web_research: createWebResearchTool({ research, operations }),
|
|
103
22
|
},
|
|
23
|
+
canReadSourceRef: (agent, ref) => ctx.searchEnhanceSources.lookup(agent.session, ref).state === 'found',
|
|
24
|
+
sourceRefForCall: (agent, call) => ctx.searchEnhanceSources.sourceRefForCall(agent.session, call),
|
|
25
|
+
webSearchDefinition: tools.web_search,
|
|
26
|
+
deferredTools: { sources: tools.search_sources, site_map: tools.web_map },
|
|
104
27
|
});
|
|
105
|
-
const residentToolDefinitions = [
|
|
106
|
-
createDocsSearchTool({
|
|
107
|
-
documentation, getConfig, operations, sources: ctx.searchEnhanceSources,
|
|
108
|
-
}),
|
|
109
|
-
createWebExtractTool({ getConfig, operations, orchestrator: webExtract }),
|
|
110
|
-
createSearchToolsTool(disclosure),
|
|
111
|
-
];
|
|
112
|
-
for (const definition of residentToolDefinitions)
|
|
113
|
-
ctx.tools.register(definition);
|
|
114
28
|
registerToolDiscoveryGuidance(ctx);
|
|
115
29
|
installWebConfigBridge(ctx);
|
|
116
30
|
}
|
|
@@ -320,10 +320,7 @@ export class SearchOrchestrator {
|
|
|
320
320
|
}
|
|
321
321
|
let strategy;
|
|
322
322
|
try {
|
|
323
|
-
strategy = resolveSearchStrategy(config
|
|
324
|
-
...(input.depth === undefined ? {} : { depth: input.depth }),
|
|
325
|
-
...(input.profile === undefined ? {} : { profile: input.profile }),
|
|
326
|
-
});
|
|
323
|
+
strategy = resolveSearchStrategy(config);
|
|
327
324
|
}
|
|
328
325
|
catch (error) {
|
|
329
326
|
throw new ProviderError({
|
|
@@ -562,8 +559,10 @@ export class SearchOrchestrator {
|
|
|
562
559
|
const collectionTruncated = providerTruncated || mainSourcesTruncated;
|
|
563
560
|
const visibleSourcesTruncated = visibleSources.length < prioritizedSources.length;
|
|
564
561
|
const sourcesTruncated = collectionTruncated || visibleSourcesTruncated;
|
|
565
|
-
if (
|
|
562
|
+
if (collectionTruncated)
|
|
566
563
|
warnings.push(warning('sources_truncated'));
|
|
564
|
+
if (visibleSourcesTruncated)
|
|
565
|
+
warnings.push(warning('visible_sources_truncated'));
|
|
567
566
|
if (mainSucceeded && rawAnswer.trim().length === 0 && prioritizedSources.length === 0) {
|
|
568
567
|
warnings.push(warning('no_results'));
|
|
569
568
|
}
|
|
@@ -583,10 +582,8 @@ export class SearchOrchestrator {
|
|
|
583
582
|
...slots.map(slot => sourceAttempt(slot)),
|
|
584
583
|
]);
|
|
585
584
|
const routing = Object.freeze({
|
|
586
|
-
depth: strategy.depth,
|
|
587
585
|
supplementalProviders: slots.filter(slot => config.supplementalSearch[slot.key]).map(slot => slot.key),
|
|
588
586
|
sourcesPerProvider: config.supplementalSearch.maxSourcesPerProvider,
|
|
589
|
-
profile: strategy.profile,
|
|
590
587
|
});
|
|
591
588
|
const answer = limitedAnswer.text.length === 0 ? undefined : limitedAnswer.text;
|
|
592
589
|
const candidate = {
|
|
@@ -607,8 +604,6 @@ export class SearchOrchestrator {
|
|
|
607
604
|
diagnostics: { attempts, routing },
|
|
608
605
|
persistence: {
|
|
609
606
|
collectionTruncated,
|
|
610
|
-
depth: strategy.depth,
|
|
611
|
-
profile: strategy.profile,
|
|
612
607
|
query,
|
|
613
608
|
sources: prioritizedSources,
|
|
614
609
|
},
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import type { Config,
|
|
1
|
+
import type { Config, SupplementalSearchProvider } from '../config.js';
|
|
2
2
|
import type { CanonicalSource, SourceRecordCandidate } from '../contracts/index.js';
|
|
3
3
|
import type { ProviderAttemptRecord, ProviderCapability, ProviderErrorKind } from '../provider-runtime/index.js';
|
|
4
4
|
import type { SearchApiResolvedSearchInput, SearchApiSearchResult } from '../providers/search-api.js';
|
|
5
5
|
import type { BoundedSourceProvider } from '../providers/types.js';
|
|
6
|
-
export declare const SEARCH_WARNING_CODES: readonly ["main_search_failed", "native_search_unconfirmed", "provider_failed", "provider_not_configured", "provider_result_truncated", "cache_stale", "cache_evicted", "answer_truncated", "sources_truncated", "canonical_output_truncated", "no_results"];
|
|
6
|
+
export declare const SEARCH_WARNING_CODES: readonly ["main_search_failed", "native_search_unconfirmed", "provider_failed", "provider_not_configured", "provider_result_truncated", "cache_stale", "cache_evicted", "answer_truncated", "sources_truncated", "visible_sources_truncated", "source_retention_truncated", "canonical_output_truncated", "no_results"];
|
|
7
7
|
export type SearchWarningCode = (typeof SEARCH_WARNING_CODES)[number];
|
|
8
8
|
/** Stable, secret-free warning; it deliberately has no arbitrary message field. */
|
|
9
9
|
export interface SearchWarning {
|
|
@@ -14,8 +14,6 @@ export interface SearchWarning {
|
|
|
14
14
|
}
|
|
15
15
|
/** One operation's fixed policy decision, excluding credentials and endpoints. */
|
|
16
16
|
export interface SearchRoutingDecision {
|
|
17
|
-
readonly profile: SearchProfile;
|
|
18
|
-
readonly depth: SearchDepth;
|
|
19
17
|
readonly supplementalProviders: readonly SupplementalSearchProvider[];
|
|
20
18
|
readonly sourcesPerProvider: number;
|
|
21
19
|
}
|
|
@@ -47,8 +45,6 @@ export interface SearchOrchestrationResult {
|
|
|
47
45
|
}
|
|
48
46
|
export interface SearchOrchestrationInput {
|
|
49
47
|
readonly query: string;
|
|
50
|
-
readonly profile?: unknown;
|
|
51
|
-
readonly depth?: unknown;
|
|
52
48
|
/** Optional caller-owned operation snapshot; otherwise `getConfig()` is read once. */
|
|
53
49
|
readonly config?: Config;
|
|
54
50
|
readonly signal: AbortSignal;
|
|
@@ -11,8 +11,11 @@ export declare function renderWebSearchLimitations(value: WebSearchOutput): stri
|
|
|
11
11
|
* Settings, caches, the clock, or network state.
|
|
12
12
|
*/
|
|
13
13
|
export declare function renderWebSearchText(value: WebSearchOutput): string;
|
|
14
|
+
export declare function isWebSearchModelTextTruncated(value: WebSearchOutput): boolean;
|
|
15
|
+
export declare function renderDocsSearchLimitations(value: DocsSearchOutput): string | undefined;
|
|
14
16
|
/** Pure Native projection for docs_search; it never reads Settings, cache, storage, or the network. */
|
|
15
17
|
export declare function renderDocsSearchText(value: DocsSearchOutput): string;
|
|
18
|
+
export declare function isDocsSearchModelTextTruncated(value: DocsSearchOutput): boolean;
|
|
16
19
|
/** Pure model-text projection for one private-storage source page. */
|
|
17
20
|
export declare function renderSearchSourcesText(value: SearchSourcesOutput): string;
|
|
18
21
|
/**
|
|
@@ -9,19 +9,24 @@ export function sourceDisplayLabel(source) {
|
|
|
9
9
|
return new URL(source.url).hostname;
|
|
10
10
|
}
|
|
11
11
|
function renderWithBoundedNotice(text, notice, maximumBytes) {
|
|
12
|
-
if (notice === undefined)
|
|
13
|
-
return truncateUtf8(text, maximumBytes).text;
|
|
14
12
|
const separator = '\n\n';
|
|
15
|
-
const
|
|
13
|
+
const complete = notice === undefined ? text : `${text}${separator}${notice}`;
|
|
14
|
+
if (utf8ByteLength(complete) <= maximumBytes)
|
|
15
|
+
return complete;
|
|
16
|
+
const marker = '[Model text truncated by model_text_max_bytes.]';
|
|
17
|
+
const suffix = notice === undefined
|
|
18
|
+
? marker
|
|
19
|
+
: `${marker}\nCounts below describe the canonical result, not fully displayed text.${separator}${notice}`;
|
|
20
|
+
const prefixBudget = maximumBytes - utf8ByteLength(suffix) - utf8ByteLength(separator);
|
|
16
21
|
if (prefixBudget <= 0)
|
|
17
|
-
return truncateUtf8(
|
|
22
|
+
return truncateUtf8(suffix, maximumBytes).text;
|
|
18
23
|
const prefix = truncateUtf8(text, prefixBudget).text;
|
|
19
|
-
return prefix.length === 0 ?
|
|
24
|
+
return prefix.length === 0 ? suffix : `${prefix}${separator}${suffix}`;
|
|
20
25
|
}
|
|
21
26
|
function sourceLoadingNotice(sourceRef) {
|
|
22
27
|
if (sourceRef === undefined)
|
|
23
28
|
return undefined;
|
|
24
|
-
return `Source reference: ${sourceRef}\n\nFor more sources, call
|
|
29
|
+
return `Source reference: ${sourceRef}\n\nFor more sources, call search_sources directly on the next step; it becomes available automatically.`;
|
|
25
30
|
}
|
|
26
31
|
function warningText(warning) {
|
|
27
32
|
const subject = [warning.provider, warning.capability].filter(Boolean).join('/');
|
|
@@ -45,7 +50,11 @@ function warningText(warning) {
|
|
|
45
50
|
case 'answer_truncated':
|
|
46
51
|
return 'The generated answer was truncated.';
|
|
47
52
|
case 'sources_truncated':
|
|
48
|
-
return '
|
|
53
|
+
return 'Source collection reached a Provider limit; uncollected sources cannot be recovered by pagination. Rerun with a larger collection limit where supported.';
|
|
54
|
+
case 'visible_sources_truncated':
|
|
55
|
+
return 'Only an inline source prefix is shown; use source_ref to page retained sources.';
|
|
56
|
+
case 'source_retention_truncated':
|
|
57
|
+
return 'Source storage dropped some collected sources at its count/byte limit; pagination cannot recover those entries.';
|
|
49
58
|
case 'canonical_output_truncated':
|
|
50
59
|
return 'The canonical search value was truncated to its configured limit.';
|
|
51
60
|
case 'no_results':
|
|
@@ -87,9 +96,8 @@ function sourceSummary(value) {
|
|
|
87
96
|
/** Shared model/card notice; Provider failures precede generic truncation information. */
|
|
88
97
|
export function renderWebSearchLimitations(value) {
|
|
89
98
|
const lines = value.warnings.map(warningText);
|
|
90
|
-
if (value.truncated)
|
|
91
|
-
lines.push('The
|
|
92
|
-
}
|
|
99
|
+
if (value.truncated && lines.length === 0)
|
|
100
|
+
lines.push('The canonical search result was bounded.');
|
|
93
101
|
if (lines.length === 0)
|
|
94
102
|
return undefined;
|
|
95
103
|
return `Limitations\n${lines.map(line => `- ${line}`).join('\n')}`;
|
|
@@ -104,15 +112,20 @@ export function renderWebSearchText(value) {
|
|
|
104
112
|
renderWebSearchLimitations(value),
|
|
105
113
|
answerSection(value),
|
|
106
114
|
sourceSection(value),
|
|
107
|
-
sourceSummary(value),
|
|
108
|
-
DISCOVERY_NOTICE,
|
|
109
115
|
].filter((section) => section !== undefined);
|
|
110
116
|
const complete = sections.join('\n\n');
|
|
111
117
|
const maximumBytes = Number.isSafeInteger(value.model_text_max_bytes)
|
|
112
118
|
&& value.model_text_max_bytes > 0
|
|
113
119
|
? value.model_text_max_bytes
|
|
114
120
|
: 0;
|
|
115
|
-
return renderWithBoundedNotice(complete, sourceLoadingNotice(value.source_ref)
|
|
121
|
+
return renderWithBoundedNotice(complete, [sourceSummary(value), DISCOVERY_NOTICE, sourceLoadingNotice(value.source_ref)]
|
|
122
|
+
.filter((section) => section !== undefined).join('\n\n'), maximumBytes);
|
|
123
|
+
}
|
|
124
|
+
export function isWebSearchModelTextTruncated(value) {
|
|
125
|
+
const maximumBytes = Number.isSafeInteger(value.model_text_max_bytes) && value.model_text_max_bytes > 0
|
|
126
|
+
? value.model_text_max_bytes : 0;
|
|
127
|
+
return utf8ByteLength(renderWebSearchText({ ...value, model_text_max_bytes: Number.MAX_SAFE_INTEGER }))
|
|
128
|
+
> maximumBytes;
|
|
116
129
|
}
|
|
117
130
|
function docsWarningText(warning) {
|
|
118
131
|
const subject = [warning.provider, warning.path].filter(Boolean).join('/');
|
|
@@ -131,8 +144,14 @@ function docsWarningText(warning) {
|
|
|
131
144
|
return `A documentation Provider bounded its discovery result${detail}.`;
|
|
132
145
|
case 'no_results':
|
|
133
146
|
return 'Documentation search completed without matching snippets or sources.';
|
|
134
|
-
case '
|
|
135
|
-
return '
|
|
147
|
+
case 'library_not_found':
|
|
148
|
+
return 'No Context7 library matched library_name. No unrelated library was selected; check the name or supply an exact library_id.';
|
|
149
|
+
case 'collection_limit_reached':
|
|
150
|
+
return `The Provider returned its requested limit (${warning.count ?? 'max_results'}). Counts cover this request, not all available matches. Increase max_results and rerun to collect more; pagination only reads retained records.`;
|
|
151
|
+
case 'visible_sources_truncated':
|
|
152
|
+
return 'Only an inline source prefix is shown; use source_ref to page retained sources.';
|
|
153
|
+
case 'source_retention_truncated':
|
|
154
|
+
return 'Source storage dropped some collected sources at its count/byte limit; pagination cannot recover those entries.';
|
|
136
155
|
case 'canonical_output_truncated':
|
|
137
156
|
return 'The documentation result was shortened to its configured canonical JSON limit.';
|
|
138
157
|
}
|
|
@@ -191,6 +210,12 @@ function docsSourceSection(value) {
|
|
|
191
210
|
}
|
|
192
211
|
return lines.join('\n');
|
|
193
212
|
}
|
|
213
|
+
export function renderDocsSearchLimitations(value) {
|
|
214
|
+
const lines = value.warnings.map(docsWarningText);
|
|
215
|
+
if (value.truncated && lines.length === 0)
|
|
216
|
+
lines.push('The canonical documentation result was bounded.');
|
|
217
|
+
return lines.length === 0 ? undefined : `Limitations\n${lines.map(line => `- ${line}`).join('\n')}`;
|
|
218
|
+
}
|
|
194
219
|
/** Pure Native projection for docs_search; it never reads Settings, cache, storage, or the network. */
|
|
195
220
|
export function renderDocsSearchText(value) {
|
|
196
221
|
const status = value.state === 'partial' ? 'partial' : 'complete';
|
|
@@ -201,35 +226,34 @@ export function renderDocsSearchText(value) {
|
|
|
201
226
|
: 'Documentation discovery completed; bounded discovery results follow.';
|
|
202
227
|
const sections = [
|
|
203
228
|
`Documentation search (${status})\n${explanation}`,
|
|
229
|
+
renderDocsSearchLimitations(value),
|
|
204
230
|
docsLibrarySection(value),
|
|
205
231
|
docsSnippetSection(value),
|
|
206
232
|
docsSourceSection(value),
|
|
207
|
-
[
|
|
208
|
-
`Sources shown: ${value.returned_sources}/${value.total_sources}`,
|
|
209
|
-
`Snippets shown: ${value.returned_snippets}/${value.total_snippets}`,
|
|
210
|
-
].join('\n'),
|
|
211
233
|
[
|
|
212
234
|
docsCachePathText('Context7 resolve cache', value.cache.resolve),
|
|
213
235
|
docsCachePathText('Context7 docs cache', value.cache.docs),
|
|
214
236
|
docsProviderSection(value),
|
|
215
237
|
].join('\n'),
|
|
216
|
-
value.truncated || value.warnings.length > 0
|
|
217
|
-
? [
|
|
218
|
-
'Limitations',
|
|
219
|
-
...(value.truncated
|
|
220
|
-
? ['- Documentation snippets, visible sources, Provider collection, or retained sources were bounded.']
|
|
221
|
-
: []),
|
|
222
|
-
...value.warnings.map(warning => `- ${docsWarningText(warning)}`),
|
|
223
|
-
].join('\n')
|
|
224
|
-
: undefined,
|
|
225
|
-
'Evidence level: discovery. Documentation snippets and source summaries are discovery metadata, not verified fetched page-body evidence; inspect an exact source page before relying on a claim.',
|
|
226
238
|
].filter((section) => section !== undefined);
|
|
227
|
-
const
|
|
239
|
+
const notice = [
|
|
240
|
+
`Sources shown: ${value.returned_sources}/${value.total_sources}`,
|
|
241
|
+
`Snippets shown: ${value.returned_snippets}/${value.total_snippets}`,
|
|
242
|
+
'Counts cover collected results for this request, not all available matches.',
|
|
243
|
+
'Evidence level: discovery. Documentation snippets and source summaries are not verified fetched page-body evidence.',
|
|
244
|
+
sourceLoadingNotice(value.source_ref),
|
|
245
|
+
].filter((section) => section !== undefined).join('\n\n');
|
|
228
246
|
const maximumBytes = Number.isSafeInteger(value.model_text_max_bytes)
|
|
229
247
|
&& value.model_text_max_bytes > 0
|
|
230
248
|
? value.model_text_max_bytes
|
|
231
249
|
: 0;
|
|
232
|
-
return renderWithBoundedNotice(
|
|
250
|
+
return renderWithBoundedNotice(sections.join('\n\n'), notice, maximumBytes);
|
|
251
|
+
}
|
|
252
|
+
export function isDocsSearchModelTextTruncated(value) {
|
|
253
|
+
const maximumBytes = Number.isSafeInteger(value.model_text_max_bytes) && value.model_text_max_bytes > 0
|
|
254
|
+
? value.model_text_max_bytes : 0;
|
|
255
|
+
return utf8ByteLength(renderDocsSearchText({ ...value, model_text_max_bytes: Number.MAX_SAFE_INTEGER }))
|
|
256
|
+
> maximumBytes;
|
|
233
257
|
}
|
|
234
258
|
function renderPageSources(page) {
|
|
235
259
|
if (page.sources.length === 0)
|
|
@@ -261,13 +285,15 @@ export function renderSearchSourcesText(value) {
|
|
|
261
285
|
const sections = [
|
|
262
286
|
renderPageSources(value),
|
|
263
287
|
[
|
|
264
|
-
`Page returned: ${value.returned}; retained total: ${value.total};
|
|
288
|
+
`Page returned: ${value.returned}; retained total: ${value.total}; collected total before storage: ${value.total_before_retention}.`,
|
|
265
289
|
value.has_more && value.next_offset !== undefined
|
|
266
290
|
? `More sources are available at offset ${value.next_offset}.`
|
|
267
291
|
: 'No later retained source page is available.',
|
|
268
|
-
value.
|
|
269
|
-
? '
|
|
270
|
-
:
|
|
292
|
+
value.total < value.total_before_retention
|
|
293
|
+
? 'Source storage dropped collected entries; pagination cannot recover them.'
|
|
294
|
+
: value.truncated
|
|
295
|
+
? 'Upstream source collection was bounded; pagination cannot recover uncollected entries.'
|
|
296
|
+
: undefined,
|
|
271
297
|
value.page_byte_limited
|
|
272
298
|
? 'This page was shortened by its canonical JSON byte limit.'
|
|
273
299
|
: undefined,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { DOCUMENTATION_CACHE_PATH_STATES, DOCUMENTATION_CACHE_SKIP_REASONS, } from '../documentation/index.js';
|
|
2
|
-
import { isWebExtractModelTextTruncated, isWebMapModelTextTruncated, renderWebSearchLimitations, } from './render.js';
|
|
2
|
+
import { isDocsSearchModelTextTruncated, isWebSearchModelTextTruncated, renderDocsSearchLimitations, isWebExtractModelTextTruncated, isWebMapModelTextTruncated, renderWebSearchLimitations, } from './render.js';
|
|
3
3
|
import { WEB_EXTRACT_EVIDENCE_LEVELS, WEB_EXTRACT_ROUTES, } from '../web-extract/types.js';
|
|
4
4
|
function isRecord(value) {
|
|
5
5
|
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
@@ -49,7 +49,7 @@ export function webSearchPresentationMeta(_args, value) {
|
|
|
49
49
|
source_produced: value.source_ref !== undefined,
|
|
50
50
|
...(answer === undefined ? {} : { answer }),
|
|
51
51
|
sources: value.sources.map(source => ({ ...source })),
|
|
52
|
-
truncated: value.truncated,
|
|
52
|
+
truncated: value.truncated || isWebSearchModelTextTruncated(value),
|
|
53
53
|
};
|
|
54
54
|
}
|
|
55
55
|
/** Generic pending web-search intent whose title is exactly the query. */
|
|
@@ -99,17 +99,19 @@ export function presentWebSearchResult(args, result) {
|
|
|
99
99
|
* durable disclosure recovery; cache paths, credentials, and source_ref stay out.
|
|
100
100
|
*/
|
|
101
101
|
export function docsSearchPresentationMeta(_args, value) {
|
|
102
|
+
const limitations = renderDocsSearchLimitations(value);
|
|
102
103
|
return {
|
|
103
104
|
version: 1,
|
|
104
105
|
type: 'docs_search',
|
|
105
106
|
source_produced: value.source_ref !== undefined,
|
|
107
|
+
...(limitations === undefined ? {} : { limitations }),
|
|
106
108
|
sources: value.sources.map(source => ({ ...source })),
|
|
107
109
|
snippets: value.snippets.map(snippet => ({ ...snippet })),
|
|
108
110
|
cache: {
|
|
109
111
|
resolve: { ...value.cache.resolve },
|
|
110
112
|
docs: { ...value.cache.docs },
|
|
111
113
|
},
|
|
112
|
-
truncated: value.truncated,
|
|
114
|
+
truncated: value.truncated || isDocsSearchModelTextTruncated(value),
|
|
113
115
|
};
|
|
114
116
|
}
|
|
115
117
|
/** Pending documentation discovery intent; snippets are not presented as fetched pages. */
|
|
@@ -155,6 +157,7 @@ function parseDocsSearchMeta(value) {
|
|
|
155
157
|
|| value.version !== 1
|
|
156
158
|
|| value.type !== 'docs_search'
|
|
157
159
|
|| typeof value.truncated !== 'boolean'
|
|
160
|
+
|| !isOptionalString(value.limitations)
|
|
158
161
|
|| !Array.isArray(value.snippets)
|
|
159
162
|
|| !isRecord(value.cache))
|
|
160
163
|
return undefined;
|
|
@@ -173,6 +176,7 @@ function parseDocsSearchMeta(value) {
|
|
|
173
176
|
return {
|
|
174
177
|
version: 1,
|
|
175
178
|
type: 'docs_search',
|
|
179
|
+
...(value.limitations === undefined ? {} : { limitations: value.limitations }),
|
|
176
180
|
sources,
|
|
177
181
|
snippets,
|
|
178
182
|
cache: { resolve, docs },
|
|
@@ -193,8 +197,11 @@ function docsCacheLabel(meta) {
|
|
|
193
197
|
}
|
|
194
198
|
function docsCardAnswer(meta) {
|
|
195
199
|
if (meta.snippets.length === 0)
|
|
196
|
-
return
|
|
197
|
-
const lines = [
|
|
200
|
+
return meta.limitations;
|
|
201
|
+
const lines = [
|
|
202
|
+
...(meta.limitations === undefined ? [] : [meta.limitations, '']),
|
|
203
|
+
'Documentation snippets (discovery metadata; not fetched page bodies):',
|
|
204
|
+
];
|
|
198
205
|
for (let index = 0; index < meta.snippets.length; index += 1) {
|
|
199
206
|
const snippet = meta.snippets[index];
|
|
200
207
|
if (snippet === undefined)
|
|
@@ -216,7 +223,10 @@ export function presentDocsSearchResult(args, result) {
|
|
|
216
223
|
const label = docsCacheLabel(meta);
|
|
217
224
|
const title = `Docs${label === undefined ? '' : ` (${label})`}: ${args.query}`;
|
|
218
225
|
if (meta.sources.length === 0 && meta.snippets.length === 0) {
|
|
219
|
-
|
|
226
|
+
const reason = meta.cache.docs.reason === 'library_not_found'
|
|
227
|
+
? 'No matching Context7 library; check library_name or supply library_id'
|
|
228
|
+
: 'No documentation results';
|
|
229
|
+
return { card: 'generic', title: `${reason}: ${args.query}` };
|
|
220
230
|
}
|
|
221
231
|
const answer = docsCardAnswer(meta);
|
|
222
232
|
return {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Context } from '@deepseek-ai/cordis';
|
|
2
2
|
export declare const TOOL_DISCOVERY_GUIDANCE: string;
|
|
3
3
|
export declare const EVIDENCE_DISCIPLINE_GUIDANCE: string;
|
|
4
|
-
/** Register
|
|
4
|
+
/** Register main/ordinary-agent guidance; the dedicated research persona owns its own routing. */
|
|
5
5
|
export declare function registerToolDiscoveryGuidance(ctx: Context): void;
|
|
6
6
|
//# sourceMappingURL=tool-discovery.d.ts.map
|
|
@@ -1,19 +1,17 @@
|
|
|
1
1
|
export const TOOL_DISCOVERY_GUIDANCE = [
|
|
2
|
-
'
|
|
3
|
-
'
|
|
4
|
-
'
|
|
5
|
-
'For
|
|
6
|
-
'
|
|
7
|
-
'
|
|
8
|
-
'Newly loaded tools become available on the next model step. Call search_sources or web_map directly using the tool definitions supplied by the host; already loaded tools stay available.',
|
|
9
|
-
'Search source metadata and mapped URLs are discovery, not verified webpage-body evidence.',
|
|
2
|
+
'For docs_search, use known library identity for library-specific queries; omit it for broad/cross-project or unknown-library discovery (auto uses Exa). Never guess a Context7 library.',
|
|
3
|
+
'Read webpage bodies with web_extract, choosing exactly one enabled Provider; use host web_fetch (if available) for structured responses such as raw JSON APIs or XML/Atom.',
|
|
4
|
+
'Prefer HTTPS. A title/app shell alone is not usable page-body evidence; choose another suitable enabled extraction Provider instead of refetching the same shell. Never bypass URL safety checks.',
|
|
5
|
+
'For multi-step investigations, comparisons or corroboration across sources, delegate to web_research with a self-contained question and all constraints; wait for its URL-and-excerpt report. Use web_search/docs_search/web_extract directly for quick lookups, documentation or a known URL.',
|
|
6
|
+
'web_research accepts optional timeout_ms for this call\'s total budget, including creation, reasoning, retrieval and reporting: default 600000 ms; integer range 60000–3600000 ms. Choose by task complexity, for example 120000 for a focused comparison or 1200000 for broad corroboration; omission uses the default, and more time does not guarantee better evidence.',
|
|
7
|
+
'After a successful search returns a readable source_ref, search_sources becomes available automatically on the next step. Call it directly to paginate retained sources; no loader is needed.',
|
|
10
8
|
].join('\n');
|
|
11
9
|
export const EVIDENCE_DISCIPLINE_GUIDANCE = [
|
|
12
|
-
'For current
|
|
13
|
-
'
|
|
14
|
-
'Before
|
|
10
|
+
'For current/external facts, use web_research for multi-step investigations; otherwise start with one focused web_search (docs_search for SDK/API docs); do not inspect local files/settings/sessions/credentials unless the user explicitly asks about local state.',
|
|
11
|
+
'Search answers/snippets, source metadata and mapped URLs are discovery, not claim-level evidence.',
|
|
12
|
+
'Before decisive factual/causal conclusions, inspect authoritative URLs with web_extract; label unestablished mechanisms as inference or unconfirmed, never source-stated fact.',
|
|
15
13
|
].join('\n');
|
|
16
|
-
/** Register
|
|
14
|
+
/** Register main/ordinary-agent guidance; the dedicated research persona owns its own routing. */
|
|
17
15
|
export function registerToolDiscoveryGuidance(ctx) {
|
|
18
16
|
ctx.systemPrompt.section({
|
|
19
17
|
name: 'search-enhance:tool-discovery',
|