@dsh-edge/dsh-edge-agent 0.0.0-stage → 0.1.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.
- package/CHANGELOG.md +142 -0
- package/LICENSE +21 -0
- package/README.md +372 -2
- package/config/edge.example.json +179 -0
- package/config/privacy-policy.json +202 -0
- package/cordis.desktop-safe.patch.yml +129 -0
- package/cordis.patch.yml +382 -0
- package/cordis.yml +374 -0
- package/lib/attestation.d.ts +2 -0
- package/lib/attestation.d.ts.map +1 -0
- package/lib/attestation.js +2 -0
- package/lib/audit-log.d.ts +2 -0
- package/lib/audit-log.d.ts.map +1 -0
- package/lib/audit-log.js +2 -0
- package/lib/client.js +87 -0
- package/lib/cloud-llm-adapter.d.ts +2 -0
- package/lib/cloud-llm-adapter.d.ts.map +1 -0
- package/lib/cloud-llm-adapter.js +2 -0
- package/lib/dsh-adapter/attestation.d.ts +42 -0
- package/lib/dsh-adapter/attestation.d.ts.map +1 -0
- package/lib/dsh-adapter/attestation.js +40 -0
- package/lib/dsh-adapter/audit-log.d.ts +139 -0
- package/lib/dsh-adapter/audit-log.d.ts.map +1 -0
- package/lib/dsh-adapter/audit-log.js +376 -0
- package/lib/dsh-adapter/cloud-egress.d.ts +181 -0
- package/lib/dsh-adapter/cloud-egress.d.ts.map +1 -0
- package/lib/dsh-adapter/cloud-egress.js +257 -0
- package/lib/dsh-adapter/cloud-llm-adapter.d.ts +416 -0
- package/lib/dsh-adapter/cloud-llm-adapter.d.ts.map +1 -0
- package/lib/dsh-adapter/cloud-llm-adapter.js +1472 -0
- package/lib/dsh-adapter/contract.d.ts +80 -0
- package/lib/dsh-adapter/contract.d.ts.map +1 -0
- package/lib/dsh-adapter/contract.js +194 -0
- package/lib/dsh-adapter/desktop-cookie-janitor.d.ts +214 -0
- package/lib/dsh-adapter/desktop-cookie-janitor.d.ts.map +1 -0
- package/lib/dsh-adapter/desktop-cookie-janitor.js +409 -0
- package/lib/dsh-adapter/dsh-edge-agent.d.ts +48 -0
- package/lib/dsh-adapter/dsh-edge-agent.d.ts.map +1 -0
- package/lib/dsh-adapter/dsh-edge-agent.js +64 -0
- package/lib/dsh-adapter/edge-config.d.ts +484 -0
- package/lib/dsh-adapter/edge-config.d.ts.map +1 -0
- package/lib/dsh-adapter/edge-config.js +898 -0
- package/lib/dsh-adapter/edge-context-view.d.ts +184 -0
- package/lib/dsh-adapter/edge-context-view.d.ts.map +1 -0
- package/lib/dsh-adapter/edge-context-view.js +483 -0
- package/lib/dsh-adapter/effective-model.d.ts +85 -0
- package/lib/dsh-adapter/effective-model.d.ts.map +1 -0
- package/lib/dsh-adapter/effective-model.js +171 -0
- package/lib/dsh-adapter/etscli-shell-guard.d.ts +244 -0
- package/lib/dsh-adapter/etscli-shell-guard.d.ts.map +1 -0
- package/lib/dsh-adapter/etscli-shell-guard.js +420 -0
- package/lib/dsh-adapter/etscli-tools.d.ts +112 -0
- package/lib/dsh-adapter/etscli-tools.d.ts.map +1 -0
- package/lib/dsh-adapter/etscli-tools.js +393 -0
- package/lib/dsh-adapter/file-logger.d.ts +294 -0
- package/lib/dsh-adapter/file-logger.d.ts.map +1 -0
- package/lib/dsh-adapter/file-logger.js +564 -0
- package/lib/dsh-adapter/function-call-external.d.ts +357 -0
- package/lib/dsh-adapter/function-call-external.d.ts.map +1 -0
- package/lib/dsh-adapter/function-call-external.js +1005 -0
- package/lib/dsh-adapter/host-egress-guard.d.ts +161 -0
- package/lib/dsh-adapter/host-egress-guard.d.ts.map +1 -0
- package/lib/dsh-adapter/host-egress-guard.js +213 -0
- package/lib/dsh-adapter/host-llm-trace.d.ts +198 -0
- package/lib/dsh-adapter/host-llm-trace.d.ts.map +1 -0
- package/lib/dsh-adapter/host-llm-trace.js +344 -0
- package/lib/dsh-adapter/http-api-routes.d.ts +61 -0
- package/lib/dsh-adapter/http-api-routes.d.ts.map +1 -0
- package/lib/dsh-adapter/http-api-routes.js +79 -0
- package/lib/dsh-adapter/http-api-sessions.d.ts +105 -0
- package/lib/dsh-adapter/http-api-sessions.d.ts.map +1 -0
- package/lib/dsh-adapter/http-api-sessions.js +173 -0
- package/lib/dsh-adapter/http-api.d.ts +221 -0
- package/lib/dsh-adapter/http-api.d.ts.map +1 -0
- package/lib/dsh-adapter/http-api.js +1811 -0
- package/lib/dsh-adapter/index.d.ts +62 -0
- package/lib/dsh-adapter/index.d.ts.map +1 -0
- package/lib/dsh-adapter/index.js +97 -0
- package/lib/dsh-adapter/input-guard-messages.d.ts +56 -0
- package/lib/dsh-adapter/input-guard-messages.d.ts.map +1 -0
- package/lib/dsh-adapter/input-guard-messages.js +136 -0
- package/lib/dsh-adapter/input-guard.d.ts +508 -0
- package/lib/dsh-adapter/input-guard.d.ts.map +1 -0
- package/lib/dsh-adapter/input-guard.js +2174 -0
- package/lib/dsh-adapter/intent-prompts.d.ts +77 -0
- package/lib/dsh-adapter/intent-prompts.d.ts.map +1 -0
- package/lib/dsh-adapter/intent-prompts.js +97 -0
- package/lib/dsh-adapter/intent-router.d.ts +710 -0
- package/lib/dsh-adapter/intent-router.d.ts.map +1 -0
- package/lib/dsh-adapter/intent-router.js +2802 -0
- package/lib/dsh-adapter/intent-signals.d.ts +208 -0
- package/lib/dsh-adapter/intent-signals.d.ts.map +1 -0
- package/lib/dsh-adapter/intent-signals.js +391 -0
- package/lib/dsh-adapter/invariant-install.d.ts +16 -0
- package/lib/dsh-adapter/invariant-install.d.ts.map +1 -0
- package/lib/dsh-adapter/invariant-install.js +15 -0
- package/lib/dsh-adapter/invariant.d.ts +16 -0
- package/lib/dsh-adapter/invariant.d.ts.map +1 -0
- package/lib/dsh-adapter/invariant.js +22 -0
- package/lib/dsh-adapter/llm-adapters.d.ts +19 -0
- package/lib/dsh-adapter/llm-adapters.d.ts.map +1 -0
- package/lib/dsh-adapter/llm-adapters.js +16 -0
- package/lib/dsh-adapter/llm-internal-caller.d.ts +92 -0
- package/lib/dsh-adapter/llm-internal-caller.d.ts.map +1 -0
- package/lib/dsh-adapter/llm-internal-caller.js +100 -0
- package/lib/dsh-adapter/llm-max-turns.d.ts +55 -0
- package/lib/dsh-adapter/llm-max-turns.d.ts.map +1 -0
- package/lib/dsh-adapter/llm-max-turns.js +127 -0
- package/lib/dsh-adapter/llm-trace-store.d.ts +131 -0
- package/lib/dsh-adapter/llm-trace-store.d.ts.map +1 -0
- package/lib/dsh-adapter/llm-trace-store.js +205 -0
- package/lib/dsh-adapter/llm-trace.d.ts +450 -0
- package/lib/dsh-adapter/llm-trace.d.ts.map +1 -0
- package/lib/dsh-adapter/llm-trace.js +463 -0
- package/lib/dsh-adapter/local-llm-adapter.d.ts +141 -0
- package/lib/dsh-adapter/local-llm-adapter.d.ts.map +1 -0
- package/lib/dsh-adapter/local-llm-adapter.js +787 -0
- package/lib/dsh-adapter/local-storage.d.ts +96 -0
- package/lib/dsh-adapter/local-storage.d.ts.map +1 -0
- package/lib/dsh-adapter/local-storage.js +198 -0
- package/lib/dsh-adapter/model-router.d.ts +79 -0
- package/lib/dsh-adapter/model-router.d.ts.map +1 -0
- package/lib/dsh-adapter/model-router.js +107 -0
- package/lib/dsh-adapter/ohttp-gateway.d.ts +49 -0
- package/lib/dsh-adapter/ohttp-gateway.d.ts.map +1 -0
- package/lib/dsh-adapter/ohttp-gateway.js +32 -0
- package/lib/dsh-adapter/openai-sse.d.ts +85 -0
- package/lib/dsh-adapter/openai-sse.d.ts.map +1 -0
- package/lib/dsh-adapter/openai-sse.js +462 -0
- package/lib/dsh-adapter/pii-utils.d.ts +96 -0
- package/lib/dsh-adapter/pii-utils.d.ts.map +1 -0
- package/lib/dsh-adapter/pii-utils.js +324 -0
- package/lib/dsh-adapter/plugin-host.d.ts +38 -0
- package/lib/dsh-adapter/plugin-host.d.ts.map +1 -0
- package/lib/dsh-adapter/plugin-host.js +38 -0
- package/lib/dsh-adapter/plugin.d.ts +46 -0
- package/lib/dsh-adapter/plugin.d.ts.map +1 -0
- package/lib/dsh-adapter/plugin.js +100 -0
- package/lib/dsh-adapter/privacy-policy.d.ts +237 -0
- package/lib/dsh-adapter/privacy-policy.d.ts.map +1 -0
- package/lib/dsh-adapter/privacy-policy.js +391 -0
- package/lib/dsh-adapter/prompt-guard.d.ts +152 -0
- package/lib/dsh-adapter/prompt-guard.d.ts.map +1 -0
- package/lib/dsh-adapter/prompt-guard.js +872 -0
- package/lib/dsh-adapter/realm-skill-bridge.d.ts +365 -0
- package/lib/dsh-adapter/realm-skill-bridge.d.ts.map +1 -0
- package/lib/dsh-adapter/realm-skill-bridge.js +1180 -0
- package/lib/dsh-adapter/sanitizer-pipeline.d.ts +140 -0
- package/lib/dsh-adapter/sanitizer-pipeline.d.ts.map +1 -0
- package/lib/dsh-adapter/sanitizer-pipeline.js +431 -0
- package/lib/dsh-adapter/secure-bridge.d.ts +47 -0
- package/lib/dsh-adapter/secure-bridge.d.ts.map +1 -0
- package/lib/dsh-adapter/secure-bridge.js +35 -0
- package/lib/dsh-adapter/session-attribution.d.ts +70 -0
- package/lib/dsh-adapter/session-attribution.d.ts.map +1 -0
- package/lib/dsh-adapter/session-attribution.js +76 -0
- package/lib/dsh-adapter/sessions.d.ts +86 -0
- package/lib/dsh-adapter/sessions.d.ts.map +1 -0
- package/lib/dsh-adapter/sessions.js +179 -0
- package/lib/dsh-adapter/tcm.d.ts +55 -0
- package/lib/dsh-adapter/tcm.d.ts.map +1 -0
- package/lib/dsh-adapter/tcm.js +94 -0
- package/lib/dsh-adapter/td-bus.d.ts +87 -0
- package/lib/dsh-adapter/td-bus.d.ts.map +1 -0
- package/lib/dsh-adapter/td-bus.js +196 -0
- package/lib/dsh-adapter/tool-runner.d.ts +185 -0
- package/lib/dsh-adapter/tool-runner.d.ts.map +1 -0
- package/lib/dsh-adapter/tool-runner.js +271 -0
- package/lib/dsh-adapter/tools.d.ts +118 -0
- package/lib/dsh-adapter/tools.d.ts.map +1 -0
- package/lib/dsh-adapter/tools.js +292 -0
- package/lib/dsh-adapter/types.d.ts +23 -0
- package/lib/dsh-adapter/types.d.ts.map +1 -0
- package/lib/dsh-adapter/types.js +16 -0
- package/lib/dsh-adapter/voice-input.d.ts +30 -0
- package/lib/dsh-adapter/voice-input.d.ts.map +1 -0
- package/lib/dsh-adapter/voice-input.js +34 -0
- package/lib/dsh-adapter/web-search-tavily-bocha.d.ts +152 -0
- package/lib/dsh-adapter/web-search-tavily-bocha.d.ts.map +1 -0
- package/lib/dsh-adapter/web-search-tavily-bocha.js +289 -0
- package/lib/http-api.d.ts +2 -0
- package/lib/http-api.d.ts.map +1 -0
- package/lib/http-api.js +2 -0
- package/lib/index.d.ts +48 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +49 -0
- package/lib/intent-router.d.ts +2 -0
- package/lib/intent-router.d.ts.map +1 -0
- package/lib/intent-router.js +2 -0
- package/lib/llm-adapters.d.ts +2 -0
- package/lib/llm-adapters.d.ts.map +1 -0
- package/lib/llm-adapters.js +2 -0
- package/lib/local-llm-adapter.d.ts +2 -0
- package/lib/local-llm-adapter.d.ts.map +1 -0
- package/lib/local-llm-adapter.js +2 -0
- package/lib/local-storage.d.ts +2 -0
- package/lib/local-storage.d.ts.map +1 -0
- package/lib/local-storage.js +2 -0
- package/lib/model-router.d.ts +2 -0
- package/lib/model-router.d.ts.map +1 -0
- package/lib/model-router.js +2 -0
- package/lib/ohttp-gateway.d.ts +2 -0
- package/lib/ohttp-gateway.d.ts.map +1 -0
- package/lib/ohttp-gateway.js +2 -0
- package/lib/plugin-host.d.ts +2 -0
- package/lib/plugin-host.d.ts.map +1 -0
- package/lib/plugin-host.js +2 -0
- package/lib/privacy-policy.d.ts +2 -0
- package/lib/privacy-policy.d.ts.map +1 -0
- package/lib/privacy-policy.js +2 -0
- package/lib/prompt-guard.d.ts +2 -0
- package/lib/prompt-guard.d.ts.map +1 -0
- package/lib/prompt-guard.js +2 -0
- package/lib/sanitizer-pipeline.d.ts +2 -0
- package/lib/sanitizer-pipeline.d.ts.map +1 -0
- package/lib/sanitizer-pipeline.js +2 -0
- package/lib/secure-bridge.d.ts +2 -0
- package/lib/secure-bridge.d.ts.map +1 -0
- package/lib/secure-bridge.js +2 -0
- package/lib/sessions.d.ts +2 -0
- package/lib/sessions.d.ts.map +1 -0
- package/lib/sessions.js +2 -0
- package/lib/tcm.d.ts +2 -0
- package/lib/tcm.d.ts.map +1 -0
- package/lib/tcm.js +2 -0
- package/lib/td-bus.d.ts +2 -0
- package/lib/td-bus.d.ts.map +1 -0
- package/lib/td-bus.js +2 -0
- package/lib/tools.d.ts +2 -0
- package/lib/tools.d.ts.map +1 -0
- package/lib/tools.js +2 -0
- package/lib/voice-input.d.ts +2 -0
- package/lib/voice-input.d.ts.map +1 -0
- package/lib/voice-input.js +2 -0
- package/package.json +341 -3
- package/presets/agent-preset-edge.json +50 -0
- package/presets/edge.desktop-safe.patch.yml +503 -0
- package/presets/edge.patch.yml +557 -0
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* web-search-tavily-bocha — Phase 5 (§7.82) 3-tier fallback search provider.
|
|
3
|
+
*
|
|
4
|
+
* Provider chain (resolved per call, on `ctx.web.search`):
|
|
5
|
+
* 1. Tavily — primary (English/global, AI-tuned results, `tavilyApiKey`)
|
|
6
|
+
* 2. 博查 AI — secondary (Chinese search optimization, `bochaApiKey`)
|
|
7
|
+
* 3. DeepSeek — tertiary (re-imports `@deepseek-ai/dsh-web-search-deepseek`,
|
|
8
|
+
* uses native `web_search_20250305` server tool on Anthropic
|
|
9
|
+
* Messages; final backstop when both Tavily and Bocha fail
|
|
10
|
+
* or when neither key is set and only DEEPSEEK_API_KEY is present)
|
|
11
|
+
*
|
|
12
|
+
* Configuration (.env-managed via §7.75 .env.sh source chain — bare names,
|
|
13
|
+
* no DSH_/XDG_/DYLD_/BASH_FUNC_ prefix to bypass dsh-app-boot blacklist):
|
|
14
|
+
* - TAVILY_API_KEY (Tier 1) — bare name, lives in repo .env
|
|
15
|
+
* - BOCHA_API_KEY (Tier 2) — bare name, lives in repo .env
|
|
16
|
+
* - DEEPSEEK_API_KEY (Tier 3 fallback, already required for cloud-llm)
|
|
17
|
+
* - DEEPSEEK_BASE_URL (optional, default https://api.deepseek.com/anthropic/v1)
|
|
18
|
+
*
|
|
19
|
+
* Where the keys come from (§7.102, 2026-10-21): config/edge.json
|
|
20
|
+
* `secrets.tavilyApiKey` / `secrets.bochaApiKey` / `secrets.deepseekApiKey`.
|
|
21
|
+
* The old flow was: repo .env → install.sh `source .env.sh` → process.env
|
|
22
|
+
* → this read. That chain is gone — the desktop GUI never sourced it, so
|
|
23
|
+
* the desktop build had no search keys at all. The env names below stay
|
|
24
|
+
* honoured as a per-process override.
|
|
25
|
+
*
|
|
26
|
+
* Why bare names (NOT DSH_EDGE_* prefix)? DSH dsh-app-boot 0.1.2-rc.1's
|
|
27
|
+
* BOOTSTRAP_PREFIXES = ["DSH_", "XDG_", "DYLD_", "BASH_FUNC_"] rejects any
|
|
28
|
+
* DSH_-prefixed KEY in .env files when dsh itself parses them
|
|
29
|
+
* (dsh-app-boot/lib/index.js:933-1015). Bare names stay compatible with
|
|
30
|
+
* that launcher behaviour for the env override path.
|
|
31
|
+
*
|
|
32
|
+
* Cordis integration: registers as a single search provider with id
|
|
33
|
+
* `tavily-bocha-fallback` in `ctx.web`. `available()` returns true iff at
|
|
34
|
+
* least one tier has its key configured — when both Tavily and Bocha keys
|
|
35
|
+
* are missing but DEEPSEEK_API_KEY is set, the aggregator still registers
|
|
36
|
+
* (Tier-3 DeepSeek only), avoiding the seam's `WEB_PROVIDER_UNAVAILABLE`.
|
|
37
|
+
*
|
|
38
|
+
* Fallback semantics mirror lingguard's `internal/tools/web.go` (Tavily →
|
|
39
|
+
* 博查) but extend to a 3rd tier for resilience: any exception from a tier
|
|
40
|
+
* (HTTP non-2xx, malformed JSON, AbortError) is logged and the next tier
|
|
41
|
+
* takes over. Final WebError `WEB_PROVIDER_UNAVAILABLE` only fires when ALL
|
|
42
|
+
* three tiers fail or are unavailable.
|
|
43
|
+
*/
|
|
44
|
+
import { type WebSearchProvider, type WebSearchRequest, type WebSearchResult } from '@deepseek-ai/dsh-web';
|
|
45
|
+
import { DeepSeekSearchProvider } from '@deepseek-ai/dsh-web-search-deepseek';
|
|
46
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
47
|
+
import Schema from '@deepseek-ai/schemastery';
|
|
48
|
+
/** Stable id for `ctx.web.searchProviderId` selection and audit messages. */
|
|
49
|
+
export declare const TAVILY_BOCHA_PROVIDER_ID = "tavily-bocha-fallback";
|
|
50
|
+
/**
|
|
51
|
+
* Cordis plugin name — must match the `id:` row in `cordis.patch.yml`.
|
|
52
|
+
*/
|
|
53
|
+
export declare const name = "web-search-tavily-bocha";
|
|
54
|
+
/** Inject `web` (seam) so `ctx.web.registerSearchProvider` is available. */
|
|
55
|
+
export declare const inject: string[];
|
|
56
|
+
/**
|
|
57
|
+
* Config schema. The aggregator is fully env-driven (§7.82: TAVILY_API_KEY
|
|
58
|
+
* / BOCHA_API_KEY / DEEPSEEK_API_KEY — bare names per §7.75), so the schema is
|
|
59
|
+
* intentionally minimal — provided only to satisfy the `verify:plugin-spec`
|
|
60
|
+
* battery. Future Phase 5 work (per-provider rate-limit split, domain
|
|
61
|
+
* whitelist, SafeSearch) can add fields here without touching `apply()`.
|
|
62
|
+
*/
|
|
63
|
+
export interface Config {
|
|
64
|
+
/** Reserved for future tuning knobs. No-op in v0.1.x. */
|
|
65
|
+
reserved: Record<string, unknown>;
|
|
66
|
+
}
|
|
67
|
+
export declare const Config: Schema<Config>;
|
|
68
|
+
/**
|
|
69
|
+
* Aggregator provider: Tier-1 Tavily → Tier-2 Bocha → Tier-3 DeepSeek.
|
|
70
|
+
* Implements the `WebSearchProvider` interface from `@deepseek-ai/dsh-web`.
|
|
71
|
+
*/
|
|
72
|
+
export declare class TavilyBochaFallbackProvider implements WebSearchProvider {
|
|
73
|
+
private readonly tavilyKey;
|
|
74
|
+
private readonly bochaKey;
|
|
75
|
+
private readonly dsKey;
|
|
76
|
+
private readonly deepseek;
|
|
77
|
+
/**
|
|
78
|
+
* §7.107 (2026-10-24): per-tier switches. `config/edge.json` has always
|
|
79
|
+
* declared `webSearch.tavilyEnabled` / `bochaEnabled` — and NOTHING read
|
|
80
|
+
* them: the tiers were gated purely on "is the key present". An operator
|
|
81
|
+
* who wanted DeepSeek Search as the backend had no way to say so; every
|
|
82
|
+
* search burned two 401 round-trips on the way down the chain to it.
|
|
83
|
+
* §7.182 (2026-11-11, owner ruling): the shipped default is **OFF** for
|
|
84
|
+
* both tiers ⇒ out of the box only the DeepSeek tier is on the chain.
|
|
85
|
+
* An unset switch therefore means DISABLED (opt-in, not opt-out).
|
|
86
|
+
*/
|
|
87
|
+
private readonly tiers;
|
|
88
|
+
readonly id = "tavily-bocha-fallback";
|
|
89
|
+
constructor(tavilyKey: string | undefined, bochaKey: string | undefined, dsKey: string | undefined, deepseek: DeepSeekSearchProvider,
|
|
90
|
+
/**
|
|
91
|
+
* §7.107 (2026-10-24): per-tier switches. `config/edge.json` has always
|
|
92
|
+
* declared `webSearch.tavilyEnabled` / `bochaEnabled` — and NOTHING read
|
|
93
|
+
* them: the tiers were gated purely on "is the key present". An operator
|
|
94
|
+
* who wanted DeepSeek Search as the backend had no way to say so; every
|
|
95
|
+
* search burned two 401 round-trips on the way down the chain to it.
|
|
96
|
+
* §7.182 (2026-11-11, owner ruling): the shipped default is **OFF** for
|
|
97
|
+
* both tiers ⇒ out of the box only the DeepSeek tier is on the chain.
|
|
98
|
+
* An unset switch therefore means DISABLED (opt-in, not opt-out).
|
|
99
|
+
*/
|
|
100
|
+
tiers?: {
|
|
101
|
+
tavilyEnabled?: boolean;
|
|
102
|
+
bochaEnabled?: boolean;
|
|
103
|
+
});
|
|
104
|
+
private get tavilyEnabled();
|
|
105
|
+
private get bochaEnabled();
|
|
106
|
+
/**
|
|
107
|
+
* Cheap local availability check; no network calls. Returns true iff any
|
|
108
|
+
* tier can serve — at least one of Tavily/Bocha/DeepSeek key set.
|
|
109
|
+
*
|
|
110
|
+
* We deliberately do NOT delegate to `this.deepseek.available()` because
|
|
111
|
+
* upstream's check returns true whenever a `resolveApiKey` thunk is
|
|
112
|
+
* defined, regardless of whether the resolved key string is empty.
|
|
113
|
+
* Our caller may legitimately invoke us with `dsKey=undefined` (Tiers 1/2
|
|
114
|
+
* only config), so we must check the key string ourselves.
|
|
115
|
+
*
|
|
116
|
+
* When ALL three are unavailable, `ctx.web` will filter us out and the
|
|
117
|
+
* seam will throw `WEB_PROVIDER_UNAVAILABLE` only if no other provider
|
|
118
|
+
* is registered as a fallback. (We always register this row, so an
|
|
119
|
+
* absent-key env is a no-op rather than an error.)
|
|
120
|
+
*/
|
|
121
|
+
available(): boolean;
|
|
122
|
+
search(request: WebSearchRequest, signal?: AbortSignal): Promise<WebSearchResult>;
|
|
123
|
+
private searchTavily;
|
|
124
|
+
private searchBocha;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* The dsh loader passes the parent Cordis `ctx` here; `inject: ['web']`
|
|
128
|
+
* guarantees `ctx.web` is non-null. We register one aggregator provider
|
|
129
|
+
* whose id is `tavily-bocha-fallback`.
|
|
130
|
+
*
|
|
131
|
+
* The Tier-3 DeepSeek provider is constructed (not registered) so we can
|
|
132
|
+
* invoke its `search()` directly inside our Tier-3 fallback path. The
|
|
133
|
+
* upstream `web-search-deepseek` plugin row is kept enabled in
|
|
134
|
+
* `cordis.patch.yml` so its `registerSearchProvider('deepseek-official')`
|
|
135
|
+
* also fires — but DSH's seam then sees TWO registered search providers
|
|
136
|
+
* (ours + deepseek-official) and would throw `WEB_PROVIDER_AMBIGUOUS`.
|
|
137
|
+
*
|
|
138
|
+
* To avoid that, the cordis.patch.yml disables `web-search-deepseek` when
|
|
139
|
+
* `web-search-tavily-bocha` is enabled; the Tier-3 fallback inside the
|
|
140
|
+
* aggregator reaches the same DeepSeek HTTP endpoint by holding a private
|
|
141
|
+
* `DeepSeekSearchProvider` instance, so we never lose the backstop.
|
|
142
|
+
*
|
|
143
|
+
* @param ctx - Cordis context with `ctx.web` available per `inject`.
|
|
144
|
+
*/
|
|
145
|
+
export declare function apply(ctx: Context, _config: Config): Promise<void>;
|
|
146
|
+
declare const _default: {
|
|
147
|
+
name: string;
|
|
148
|
+
inject: string[];
|
|
149
|
+
apply: typeof apply;
|
|
150
|
+
};
|
|
151
|
+
export default _default;
|
|
152
|
+
//# sourceMappingURL=web-search-tavily-bocha.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"web-search-tavily-bocha.d.ts","sourceRoot":"","sources":["../../src/dsh-adapter/web-search-tavily-bocha.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AAEH,OAAO,EAAY,KAAK,iBAAiB,EAAE,KAAK,gBAAgB,EAAE,KAAK,eAAe,EAAE,MAAM,sBAAsB,CAAA;AACpH,OAAO,EAAE,sBAAsB,EAAE,MAAM,sCAAsC,CAAA;AAC7E,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAClD,OAAO,MAAM,MAAM,0BAA0B,CAAA;AAW7C,6EAA6E;AAC7E,eAAO,MAAM,wBAAwB,0BAA0B,CAAA;AAE/D;;GAEG;AACH,eAAO,MAAM,IAAI,4BAA4B,CAAA;AAE7C,4EAA4E;AAC5E,eAAO,MAAM,MAAM,UAAU,CAAA;AAE7B;;;;;;GAMG;AACH,MAAM,WAAW,MAAM;IACrB,yDAAyD;IACzD,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAClC;AAED,eAAO,MAAM,MAAM,EAAE,MAAM,CAAC,MAAM,CAEhC,CAAA;AAEF;;;GAGG;AACH,qBAAa,2BAA4B,YAAW,iBAAiB;IAIjE,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB;;;;;;;;;OASG;IACH,OAAO,CAAC,QAAQ,CAAC,KAAK;IAjBxB,QAAQ,CAAC,EAAE,2BAA2B;gBAGnB,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,KAAK,EAAE,MAAM,GAAG,SAAS,EACzB,QAAQ,EAAE,sBAAsB;IACjD;;;;;;;;;OASG;IACc,KAAK,GAAE;QAAE,aAAa,CAAC,EAAE,OAAO,CAAC;QAAC,YAAY,CAAC,EAAE,OAAO,CAAA;KAAO;IAGlF,OAAO,KAAK,aAAa,GAGxB;IAED,OAAO,KAAK,YAAY,GAGvB;IAED;;;;;;;;;;;;;;OAcG;IACH,SAAS,IAAI,OAAO;IAId,MAAM,CAAC,OAAO,EAAE,gBAAgB,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,eAAe,CAAC;YA+CzE,YAAY;YAwCZ,WAAW;CA8D1B;AAGD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAsB,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAsCxE;;;;;;AAED,wBAAsC"}
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* web-search-tavily-bocha — Phase 5 (§7.82) 3-tier fallback search provider.
|
|
3
|
+
*
|
|
4
|
+
* Provider chain (resolved per call, on `ctx.web.search`):
|
|
5
|
+
* 1. Tavily — primary (English/global, AI-tuned results, `tavilyApiKey`)
|
|
6
|
+
* 2. 博查 AI — secondary (Chinese search optimization, `bochaApiKey`)
|
|
7
|
+
* 3. DeepSeek — tertiary (re-imports `@deepseek-ai/dsh-web-search-deepseek`,
|
|
8
|
+
* uses native `web_search_20250305` server tool on Anthropic
|
|
9
|
+
* Messages; final backstop when both Tavily and Bocha fail
|
|
10
|
+
* or when neither key is set and only DEEPSEEK_API_KEY is present)
|
|
11
|
+
*
|
|
12
|
+
* Configuration (.env-managed via §7.75 .env.sh source chain — bare names,
|
|
13
|
+
* no DSH_/XDG_/DYLD_/BASH_FUNC_ prefix to bypass dsh-app-boot blacklist):
|
|
14
|
+
* - TAVILY_API_KEY (Tier 1) — bare name, lives in repo .env
|
|
15
|
+
* - BOCHA_API_KEY (Tier 2) — bare name, lives in repo .env
|
|
16
|
+
* - DEEPSEEK_API_KEY (Tier 3 fallback, already required for cloud-llm)
|
|
17
|
+
* - DEEPSEEK_BASE_URL (optional, default https://api.deepseek.com/anthropic/v1)
|
|
18
|
+
*
|
|
19
|
+
* Where the keys come from (§7.102, 2026-10-21): config/edge.json
|
|
20
|
+
* `secrets.tavilyApiKey` / `secrets.bochaApiKey` / `secrets.deepseekApiKey`.
|
|
21
|
+
* The old flow was: repo .env → install.sh `source .env.sh` → process.env
|
|
22
|
+
* → this read. That chain is gone — the desktop GUI never sourced it, so
|
|
23
|
+
* the desktop build had no search keys at all. The env names below stay
|
|
24
|
+
* honoured as a per-process override.
|
|
25
|
+
*
|
|
26
|
+
* Why bare names (NOT DSH_EDGE_* prefix)? DSH dsh-app-boot 0.1.2-rc.1's
|
|
27
|
+
* BOOTSTRAP_PREFIXES = ["DSH_", "XDG_", "DYLD_", "BASH_FUNC_"] rejects any
|
|
28
|
+
* DSH_-prefixed KEY in .env files when dsh itself parses them
|
|
29
|
+
* (dsh-app-boot/lib/index.js:933-1015). Bare names stay compatible with
|
|
30
|
+
* that launcher behaviour for the env override path.
|
|
31
|
+
*
|
|
32
|
+
* Cordis integration: registers as a single search provider with id
|
|
33
|
+
* `tavily-bocha-fallback` in `ctx.web`. `available()` returns true iff at
|
|
34
|
+
* least one tier has its key configured — when both Tavily and Bocha keys
|
|
35
|
+
* are missing but DEEPSEEK_API_KEY is set, the aggregator still registers
|
|
36
|
+
* (Tier-3 DeepSeek only), avoiding the seam's `WEB_PROVIDER_UNAVAILABLE`.
|
|
37
|
+
*
|
|
38
|
+
* Fallback semantics mirror lingguard's `internal/tools/web.go` (Tavily →
|
|
39
|
+
* 博查) but extend to a 3rd tier for resilience: any exception from a tier
|
|
40
|
+
* (HTTP non-2xx, malformed JSON, AbortError) is logged and the next tier
|
|
41
|
+
* takes over. Final WebError `WEB_PROVIDER_UNAVAILABLE` only fires when ALL
|
|
42
|
+
* three tiers fail or are unavailable.
|
|
43
|
+
*/
|
|
44
|
+
import { WebError } from '@deepseek-ai/dsh-web';
|
|
45
|
+
import { DeepSeekSearchProvider } from '@deepseek-ai/dsh-web-search-deepseek';
|
|
46
|
+
import Schema from '@deepseek-ai/schemastery';
|
|
47
|
+
import { loadEdgeConfig } from './edge-config.js';
|
|
48
|
+
// ── Endpoints (fixed; these are the only authoritative hosts for each provider) ─────
|
|
49
|
+
const TAVILY_ENDPOINT = 'https://api.tavily.com/search';
|
|
50
|
+
// 博查 AI web-search endpoint (§7.82.2 修正域名 —— 旧 `api.bocha.cn` 是误传,
|
|
51
|
+
// 官方开放平台 docs https://open.bochaai.com 明确指向 `api.bochaai.com`;
|
|
52
|
+
// 旧域名持任何 key 都会 403 quota 错误,实际是路由不可达伪装成 quota)。
|
|
53
|
+
const BOCHA_ENDPOINT = 'https://api.bochaai.com/v1/web-search';
|
|
54
|
+
const DEEPSEEK_DEFAULT_BASE_URL = 'https://api.deepseek.com/anthropic/v1';
|
|
55
|
+
/** Stable id for `ctx.web.searchProviderId` selection and audit messages. */
|
|
56
|
+
export const TAVILY_BOCHA_PROVIDER_ID = 'tavily-bocha-fallback';
|
|
57
|
+
/**
|
|
58
|
+
* Cordis plugin name — must match the `id:` row in `cordis.patch.yml`.
|
|
59
|
+
*/
|
|
60
|
+
export const name = 'web-search-tavily-bocha';
|
|
61
|
+
/** Inject `web` (seam) so `ctx.web.registerSearchProvider` is available. */
|
|
62
|
+
export const inject = ['web'];
|
|
63
|
+
export const Config = Schema.object({
|
|
64
|
+
reserved: Schema.dict(Schema.any()).default({}),
|
|
65
|
+
});
|
|
66
|
+
/**
|
|
67
|
+
* Aggregator provider: Tier-1 Tavily → Tier-2 Bocha → Tier-3 DeepSeek.
|
|
68
|
+
* Implements the `WebSearchProvider` interface from `@deepseek-ai/dsh-web`.
|
|
69
|
+
*/
|
|
70
|
+
export class TavilyBochaFallbackProvider {
|
|
71
|
+
tavilyKey;
|
|
72
|
+
bochaKey;
|
|
73
|
+
dsKey;
|
|
74
|
+
deepseek;
|
|
75
|
+
tiers;
|
|
76
|
+
id = TAVILY_BOCHA_PROVIDER_ID;
|
|
77
|
+
constructor(tavilyKey, bochaKey, dsKey, deepseek,
|
|
78
|
+
/**
|
|
79
|
+
* §7.107 (2026-10-24): per-tier switches. `config/edge.json` has always
|
|
80
|
+
* declared `webSearch.tavilyEnabled` / `bochaEnabled` — and NOTHING read
|
|
81
|
+
* them: the tiers were gated purely on "is the key present". An operator
|
|
82
|
+
* who wanted DeepSeek Search as the backend had no way to say so; every
|
|
83
|
+
* search burned two 401 round-trips on the way down the chain to it.
|
|
84
|
+
* §7.182 (2026-11-11, owner ruling): the shipped default is **OFF** for
|
|
85
|
+
* both tiers ⇒ out of the box only the DeepSeek tier is on the chain.
|
|
86
|
+
* An unset switch therefore means DISABLED (opt-in, not opt-out).
|
|
87
|
+
*/
|
|
88
|
+
tiers = {}) {
|
|
89
|
+
this.tavilyKey = tavilyKey;
|
|
90
|
+
this.bochaKey = bochaKey;
|
|
91
|
+
this.dsKey = dsKey;
|
|
92
|
+
this.deepseek = deepseek;
|
|
93
|
+
this.tiers = tiers;
|
|
94
|
+
}
|
|
95
|
+
get tavilyEnabled() {
|
|
96
|
+
// §7.182: unset = disabled (the shipped default is off).
|
|
97
|
+
return this.tiers.tavilyEnabled === true;
|
|
98
|
+
}
|
|
99
|
+
get bochaEnabled() {
|
|
100
|
+
// §7.182: unset = disabled (the shipped default is off).
|
|
101
|
+
return this.tiers.bochaEnabled === true;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Cheap local availability check; no network calls. Returns true iff any
|
|
105
|
+
* tier can serve — at least one of Tavily/Bocha/DeepSeek key set.
|
|
106
|
+
*
|
|
107
|
+
* We deliberately do NOT delegate to `this.deepseek.available()` because
|
|
108
|
+
* upstream's check returns true whenever a `resolveApiKey` thunk is
|
|
109
|
+
* defined, regardless of whether the resolved key string is empty.
|
|
110
|
+
* Our caller may legitimately invoke us with `dsKey=undefined` (Tiers 1/2
|
|
111
|
+
* only config), so we must check the key string ourselves.
|
|
112
|
+
*
|
|
113
|
+
* When ALL three are unavailable, `ctx.web` will filter us out and the
|
|
114
|
+
* seam will throw `WEB_PROVIDER_UNAVAILABLE` only if no other provider
|
|
115
|
+
* is registered as a fallback. (We always register this row, so an
|
|
116
|
+
* absent-key env is a no-op rather than an error.)
|
|
117
|
+
*/
|
|
118
|
+
available() {
|
|
119
|
+
return Boolean((this.tavilyEnabled && this.tavilyKey) || (this.bochaEnabled && this.bochaKey) || this.dsKey);
|
|
120
|
+
}
|
|
121
|
+
async search(request, signal) {
|
|
122
|
+
// Tier 1 — Tavily
|
|
123
|
+
if (this.tavilyEnabled && this.tavilyKey) {
|
|
124
|
+
try {
|
|
125
|
+
return await this.searchTavily(request, signal);
|
|
126
|
+
}
|
|
127
|
+
catch (err) {
|
|
128
|
+
// Tier 1 failed; fall through to Tier 2. We log but do not throw —
|
|
129
|
+
// this is the explicit lingguard-style "fail-down" contract.
|
|
130
|
+
console.warn(`[web-search-tavily-bocha] Tavily failed, falling through to 博查: ` +
|
|
131
|
+
(err instanceof Error ? err.message : String(err)));
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
// Tier 2 — 博查 AI
|
|
135
|
+
if (this.bochaEnabled && this.bochaKey) {
|
|
136
|
+
try {
|
|
137
|
+
return await this.searchBocha(request, signal);
|
|
138
|
+
}
|
|
139
|
+
catch (err) {
|
|
140
|
+
console.warn(`[web-search-tavily-bocha] 博查 failed, falling through to DeepSeek: ` +
|
|
141
|
+
(err instanceof Error ? err.message : String(err)));
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
// Tier 3 — DeepSeek (imported provider; uses native server-tool search).
|
|
145
|
+
// We gate on `this.dsKey` directly rather than `this.deepseek.available()`
|
|
146
|
+
// because upstream's available() returns true whenever a `resolveApiKey`
|
|
147
|
+
// thunk is defined, even when it resolves to an empty string.
|
|
148
|
+
if (this.dsKey) {
|
|
149
|
+
return await this.deepseek.search(request, signal);
|
|
150
|
+
}
|
|
151
|
+
// All tiers failed or unavailable.
|
|
152
|
+
throw new WebError('all web search providers failed or none configured ' +
|
|
153
|
+
'(set TAVILY_API_KEY / BOCHA_API_KEY / DEEPSEEK_API_KEY in .env)', 'WEB_PROVIDER_UNAVAILABLE');
|
|
154
|
+
}
|
|
155
|
+
// ── Tier 1: Tavily ───────────────────────────────────────────────────────
|
|
156
|
+
// API: https://docs.tavily.com/docs/rest-api/api-reference#endpoint-search
|
|
157
|
+
// Body: { api_key, query, max_results, search_depth, include_answer }
|
|
158
|
+
// 200 OK response: { answer?: string, results: [{ title, url, content, score }] }
|
|
159
|
+
async searchTavily(req, signal) {
|
|
160
|
+
const r = await fetch(TAVILY_ENDPOINT, {
|
|
161
|
+
method: 'POST',
|
|
162
|
+
headers: { 'Content-Type': 'application/json' },
|
|
163
|
+
body: JSON.stringify({
|
|
164
|
+
api_key: this.tavilyKey,
|
|
165
|
+
query: req.query,
|
|
166
|
+
max_results: req.maxResults ?? 5,
|
|
167
|
+
search_depth: 'basic',
|
|
168
|
+
include_answer: true,
|
|
169
|
+
}),
|
|
170
|
+
signal,
|
|
171
|
+
});
|
|
172
|
+
if (!r.ok) {
|
|
173
|
+
const body = await r.text().catch(() => '');
|
|
174
|
+
throw new Error(`Tavily HTTP ${r.status}: ${body.slice(0, 200)}`);
|
|
175
|
+
}
|
|
176
|
+
const json = (await r.json());
|
|
177
|
+
const sources = (json.results ?? [])
|
|
178
|
+
.filter((it) => it.url)
|
|
179
|
+
.map((it) => ({
|
|
180
|
+
url: it.url,
|
|
181
|
+
...(it.title ? { title: it.title } : {}),
|
|
182
|
+
...(it.content ? { snippet: it.content } : {}),
|
|
183
|
+
}));
|
|
184
|
+
return {
|
|
185
|
+
...(json.answer ? { content: json.answer } : {}),
|
|
186
|
+
sources,
|
|
187
|
+
truncated: false,
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
// ── Tier 2: 博查 AI ────────────────────────────────────────────────────────
|
|
191
|
+
// API: https://open.bochaai.com(§7.82.2 修正:旧 feishu docx 链接已迁移,权威 spec 在开放平台)
|
|
192
|
+
// Body: { query, count, summary, freshness } —— 4 个字段,无额外必填
|
|
193
|
+
// 200 OK response: { code: 200, msg: null, log_id, data: { _type, queryContext, webPages: { webSearchUrl, totalEstimatedMatches, value: [...] }, images?, videos? } }
|
|
194
|
+
// 错误 response: { code: 4xx, msg: '...', log_id } —— 无 data 字段
|
|
195
|
+
async searchBocha(req, signal) {
|
|
196
|
+
const r = await fetch(BOCHA_ENDPOINT, {
|
|
197
|
+
method: 'POST',
|
|
198
|
+
headers: {
|
|
199
|
+
'Content-Type': 'application/json',
|
|
200
|
+
Authorization: `Bearer ${this.bochaKey}`,
|
|
201
|
+
},
|
|
202
|
+
body: JSON.stringify({
|
|
203
|
+
query: req.query,
|
|
204
|
+
count: req.maxResults ?? 5,
|
|
205
|
+
summary: true,
|
|
206
|
+
freshness: 'noLimit',
|
|
207
|
+
}),
|
|
208
|
+
signal,
|
|
209
|
+
});
|
|
210
|
+
if (!r.ok) {
|
|
211
|
+
const body = await r.text().catch(() => '');
|
|
212
|
+
throw new Error(`博查 HTTP ${r.status}: ${body.slice(0, 200)}`);
|
|
213
|
+
}
|
|
214
|
+
const json = (await r.json());
|
|
215
|
+
if (json.code !== 200) {
|
|
216
|
+
throw new Error(`博查 API code ${json.code}: ${json.msg ?? 'unknown error'} (log_id=${json.log_id ?? 'n/a'})`);
|
|
217
|
+
}
|
|
218
|
+
const pages = json.data?.webPages?.value ?? [];
|
|
219
|
+
const sources = pages
|
|
220
|
+
.filter((it) => it.url)
|
|
221
|
+
.map((it) => ({
|
|
222
|
+
url: it.url,
|
|
223
|
+
...(it.name ? { title: it.name } : {}),
|
|
224
|
+
...(it.summary ? { snippet: it.summary } : it.snippet ? { snippet: it.snippet } : {}),
|
|
225
|
+
...(it.datePublished ? { datePublished: it.datePublished } : {}),
|
|
226
|
+
}));
|
|
227
|
+
return {
|
|
228
|
+
...(json.data?.summary ? { content: json.data.summary } : {}),
|
|
229
|
+
sources,
|
|
230
|
+
truncated: false,
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
// ── Cordis plugin entry point ───────────────────────────────────────────
|
|
235
|
+
/**
|
|
236
|
+
* The dsh loader passes the parent Cordis `ctx` here; `inject: ['web']`
|
|
237
|
+
* guarantees `ctx.web` is non-null. We register one aggregator provider
|
|
238
|
+
* whose id is `tavily-bocha-fallback`.
|
|
239
|
+
*
|
|
240
|
+
* The Tier-3 DeepSeek provider is constructed (not registered) so we can
|
|
241
|
+
* invoke its `search()` directly inside our Tier-3 fallback path. The
|
|
242
|
+
* upstream `web-search-deepseek` plugin row is kept enabled in
|
|
243
|
+
* `cordis.patch.yml` so its `registerSearchProvider('deepseek-official')`
|
|
244
|
+
* also fires — but DSH's seam then sees TWO registered search providers
|
|
245
|
+
* (ours + deepseek-official) and would throw `WEB_PROVIDER_AMBIGUOUS`.
|
|
246
|
+
*
|
|
247
|
+
* To avoid that, the cordis.patch.yml disables `web-search-deepseek` when
|
|
248
|
+
* `web-search-tavily-bocha` is enabled; the Tier-3 fallback inside the
|
|
249
|
+
* aggregator reaches the same DeepSeek HTTP endpoint by holding a private
|
|
250
|
+
* `DeepSeekSearchProvider` instance, so we never lose the backstop.
|
|
251
|
+
*
|
|
252
|
+
* @param ctx - Cordis context with `ctx.web` available per `inject`.
|
|
253
|
+
*/
|
|
254
|
+
export async function apply(ctx, _config) {
|
|
255
|
+
// §7.102 (2026-10-21): the search keys come from config/edge.json
|
|
256
|
+
// (secrets.tavilyApiKey / secrets.bochaApiKey / secrets.deepseekApiKey).
|
|
257
|
+
// The old flow was: repo .env → install.sh `source .env.sh` → process.env
|
|
258
|
+
// → this read. That chain is gone — the desktop GUI never sourced it.
|
|
259
|
+
// Env names stay honoured as a per-process override.
|
|
260
|
+
const cfg = loadEdgeConfig(true);
|
|
261
|
+
const secrets = cfg.secrets ?? {};
|
|
262
|
+
const tavilyKey = (secrets.tavilyApiKey ?? process.env.TAVILY_API_KEY)?.trim() || undefined;
|
|
263
|
+
const bochaKey = (secrets.bochaApiKey ?? process.env.BOCHA_API_KEY)?.trim() || undefined;
|
|
264
|
+
const dsKey = (secrets.deepseekApiKey ?? process.env.DEEPSEEK_API_KEY)?.trim() || undefined;
|
|
265
|
+
const dsBaseURL = process.env.DEEPSEEK_BASE_URL?.trim() || DEEPSEEK_DEFAULT_BASE_URL;
|
|
266
|
+
// Construct upstream DeepSeek provider for Tier-3 fallback. We bypass its
|
|
267
|
+
// settings-section / credential-ref machinery because we read env vars
|
|
268
|
+
// ourselves (consistent with `local-llm-adapter` pattern). The provider
|
|
269
|
+
// class is stateless across calls — re-reading env on each `search()`.
|
|
270
|
+
const dsProvider = new DeepSeekSearchProvider(() => ({
|
|
271
|
+
resolveApiKey: async () => dsKey,
|
|
272
|
+
baseURL: dsBaseURL,
|
|
273
|
+
model: 'deepseek-v4-flash',
|
|
274
|
+
apiVersion: '2023-06-01',
|
|
275
|
+
maxTokens: 4096,
|
|
276
|
+
maxUses: 5,
|
|
277
|
+
}));
|
|
278
|
+
const provider = new TavilyBochaFallbackProvider(tavilyKey, bochaKey, dsKey, dsProvider, {
|
|
279
|
+
// §7.107: the per-tier switches, finally read. §7.182: default =
|
|
280
|
+
// disabled ⇒ an unset config keeps only the DeepSeek tier on the chain.
|
|
281
|
+
tavilyEnabled: cfg.webSearch?.tavilyEnabled,
|
|
282
|
+
bochaEnabled: cfg.webSearch?.bochaEnabled,
|
|
283
|
+
});
|
|
284
|
+
// The `web` seam is registered by `@deepseek-ai/dsh-tool-web` upstream;
|
|
285
|
+
// it's not part of the stock `Context` interface, so cast narrowly.
|
|
286
|
+
const webCtx = ctx;
|
|
287
|
+
webCtx.web.registerSearchProvider(provider);
|
|
288
|
+
}
|
|
289
|
+
export default { name, inject, apply };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http-api.d.ts","sourceRoot":"","sources":["../src/http-api.ts"],"names":[],"mappings":"AACA,cAAc,2BAA2B,CAAA"}
|
package/lib/http-api.js
ADDED
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dsh-edge-agent — package root (flat layout, matching dsh-context's pattern).
|
|
3
|
+
*
|
|
4
|
+
* The aggregate plugin (`name='dsh-edge-agent'`) that cordis 0.2.1-rc.2
|
|
5
|
+
* mounts when the patch row uses the bare package name lives in
|
|
6
|
+
* `./dsh-adapter/dsh-edge-agent.ts` so the adapter boundary holds:
|
|
7
|
+
* `verify-adapter-boundary` only allows `@deepseek-ai/*` imports under
|
|
8
|
+
* `src/dsh-adapter/`. This file stays as a thin re-export shell — it must
|
|
9
|
+
* NOT import upstream packages directly, only re-export sub-plugin modules
|
|
10
|
+
* and the aggregate bootstrap.
|
|
11
|
+
*
|
|
12
|
+
* For `cordis require('@scope/pkg')` to resolve to the aggregate plugin,
|
|
13
|
+
* `package.json#main` → `lib/index.js` (this file compiled) AND the
|
|
14
|
+
* `lib/` directory must hold `index.js` flat (no dotted `lib/types/` prefix).
|
|
15
|
+
* The dotted layout silently misroutes cordis to `lib/types/<name>.js`,
|
|
16
|
+
* which would cause the whole bundle to never mount — flat layout is
|
|
17
|
+
* mandatory (see docs/dev-notes/cordis-rc021-patch-loader.md).
|
|
18
|
+
*
|
|
19
|
+
* Sub-plugin re-exports preserve the public API: a consumer can still do
|
|
20
|
+
* `import { auditLog } from '@scope/pkg/audit-log'`
|
|
21
|
+
* because every adapter subpath is `export *`-ed here.
|
|
22
|
+
*/
|
|
23
|
+
export { name, inject, Config, apply, type Config as AggregatorConfig, } from './dsh-adapter/dsh-edge-agent.js';
|
|
24
|
+
export * from './dsh-adapter/tcm.js';
|
|
25
|
+
export * from './dsh-adapter/local-storage.js';
|
|
26
|
+
export * from './dsh-adapter/sessions.js';
|
|
27
|
+
export * from './dsh-adapter/audit-log.js';
|
|
28
|
+
export * from './dsh-adapter/privacy-policy.js';
|
|
29
|
+
export * from './dsh-adapter/tools.js';
|
|
30
|
+
export * from './dsh-adapter/realm-skill-bridge.js';
|
|
31
|
+
export * from './dsh-adapter/prompt-guard.js';
|
|
32
|
+
export * from './dsh-adapter/intent-router.js';
|
|
33
|
+
export * from './dsh-adapter/model-router.js';
|
|
34
|
+
export * from './dsh-adapter/local-llm-adapter.js';
|
|
35
|
+
export * from './dsh-adapter/cloud-llm-adapter.js';
|
|
36
|
+
export * from './dsh-adapter/sanitizer-pipeline.js';
|
|
37
|
+
export * from './dsh-adapter/ohttp-gateway.js';
|
|
38
|
+
export * from './dsh-adapter/secure-bridge.js';
|
|
39
|
+
export * from './dsh-adapter/attestation.js';
|
|
40
|
+
export * from './dsh-adapter/td-bus.js';
|
|
41
|
+
export * from './dsh-adapter/voice-input.js';
|
|
42
|
+
export * from './dsh-adapter/plugin-host.js';
|
|
43
|
+
export { name as httpApiName, inject as httpApiInject, Config as HttpApiConfig, HttpApiServiceImpl, apply as httpApiApply } from './dsh-adapter/http-api.js';
|
|
44
|
+
export type { HttpApiService } from './dsh-adapter/http-api.js';
|
|
45
|
+
export * from './dsh-adapter/llm-adapters.js';
|
|
46
|
+
export * from './dsh-adapter/edge-context-view.js';
|
|
47
|
+
export * from './dsh-adapter/desktop-cookie-janitor.js';
|
|
48
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EACL,IAAI,EACJ,MAAM,EACN,MAAM,EACN,KAAK,EACL,KAAK,MAAM,IAAI,gBAAgB,GAChC,MAAM,iCAAiC,CAAA;AAKxC,cAAc,sBAAsB,CAAA;AACpC,cAAc,gCAAgC,CAAA;AAC9C,cAAc,2BAA2B,CAAA;AACzC,cAAc,4BAA4B,CAAA;AAC1C,cAAc,iCAAiC,CAAA;AAC/C,cAAc,wBAAwB,CAAA;AACtC,cAAc,qCAAqC,CAAA;AACnD,cAAc,+BAA+B,CAAA;AAC7C,cAAc,gCAAgC,CAAA;AAC9C,cAAc,+BAA+B,CAAA;AAC7C,cAAc,oCAAoC,CAAA;AAClD,cAAc,oCAAoC,CAAA;AAClD,cAAc,qCAAqC,CAAA;AACnD,cAAc,gCAAgC,CAAA;AAC9C,cAAc,gCAAgC,CAAA;AAC9C,cAAc,8BAA8B,CAAA;AAC5C,cAAc,yBAAyB,CAAA;AACvC,cAAc,8BAA8B,CAAA;AAC5C,cAAc,8BAA8B,CAAA;AAC5C,OAAO,EAAE,IAAI,IAAI,WAAW,EAAE,MAAM,IAAI,aAAa,EAAE,MAAM,IAAI,aAAa,EAAE,kBAAkB,EAAE,KAAK,IAAI,YAAY,EAAE,MAAM,2BAA2B,CAAA;AAC5J,YAAY,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAA;AAC/D,cAAc,+BAA+B,CAAA;AAC7C,cAAc,oCAAoC,CAAA;AAClD,cAAc,yCAAyC,CAAA"}
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dsh-edge-agent — package root (flat layout, matching dsh-context's pattern).
|
|
3
|
+
*
|
|
4
|
+
* The aggregate plugin (`name='dsh-edge-agent'`) that cordis 0.2.1-rc.2
|
|
5
|
+
* mounts when the patch row uses the bare package name lives in
|
|
6
|
+
* `./dsh-adapter/dsh-edge-agent.ts` so the adapter boundary holds:
|
|
7
|
+
* `verify-adapter-boundary` only allows `@deepseek-ai/*` imports under
|
|
8
|
+
* `src/dsh-adapter/`. This file stays as a thin re-export shell — it must
|
|
9
|
+
* NOT import upstream packages directly, only re-export sub-plugin modules
|
|
10
|
+
* and the aggregate bootstrap.
|
|
11
|
+
*
|
|
12
|
+
* For `cordis require('@scope/pkg')` to resolve to the aggregate plugin,
|
|
13
|
+
* `package.json#main` → `lib/index.js` (this file compiled) AND the
|
|
14
|
+
* `lib/` directory must hold `index.js` flat (no dotted `lib/types/` prefix).
|
|
15
|
+
* The dotted layout silently misroutes cordis to `lib/types/<name>.js`,
|
|
16
|
+
* which would cause the whole bundle to never mount — flat layout is
|
|
17
|
+
* mandatory (see docs/dev-notes/cordis-rc021-patch-loader.md).
|
|
18
|
+
*
|
|
19
|
+
* Sub-plugin re-exports preserve the public API: a consumer can still do
|
|
20
|
+
* `import { auditLog } from '@scope/pkg/audit-log'`
|
|
21
|
+
* because every adapter subpath is `export *`-ed here.
|
|
22
|
+
*/
|
|
23
|
+
export { name, inject, Config, apply, } from './dsh-adapter/dsh-edge-agent.js';
|
|
24
|
+
// Convenience: re-export the canonical subpath plugins so that consumers
|
|
25
|
+
// who `import '@dsh-edge/dsh-edge-agent'` can also
|
|
26
|
+
// pull individual cordis rows out of the same module.
|
|
27
|
+
export * from './dsh-adapter/tcm.js';
|
|
28
|
+
export * from './dsh-adapter/local-storage.js';
|
|
29
|
+
export * from './dsh-adapter/sessions.js';
|
|
30
|
+
export * from './dsh-adapter/audit-log.js';
|
|
31
|
+
export * from './dsh-adapter/privacy-policy.js';
|
|
32
|
+
export * from './dsh-adapter/tools.js';
|
|
33
|
+
export * from './dsh-adapter/realm-skill-bridge.js';
|
|
34
|
+
export * from './dsh-adapter/prompt-guard.js';
|
|
35
|
+
export * from './dsh-adapter/intent-router.js';
|
|
36
|
+
export * from './dsh-adapter/model-router.js';
|
|
37
|
+
export * from './dsh-adapter/local-llm-adapter.js';
|
|
38
|
+
export * from './dsh-adapter/cloud-llm-adapter.js';
|
|
39
|
+
export * from './dsh-adapter/sanitizer-pipeline.js';
|
|
40
|
+
export * from './dsh-adapter/ohttp-gateway.js';
|
|
41
|
+
export * from './dsh-adapter/secure-bridge.js';
|
|
42
|
+
export * from './dsh-adapter/attestation.js';
|
|
43
|
+
export * from './dsh-adapter/td-bus.js';
|
|
44
|
+
export * from './dsh-adapter/voice-input.js';
|
|
45
|
+
export * from './dsh-adapter/plugin-host.js';
|
|
46
|
+
export { name as httpApiName, inject as httpApiInject, Config as HttpApiConfig, HttpApiServiceImpl, apply as httpApiApply } from './dsh-adapter/http-api.js';
|
|
47
|
+
export * from './dsh-adapter/llm-adapters.js';
|
|
48
|
+
export * from './dsh-adapter/edge-context-view.js';
|
|
49
|
+
export * from './dsh-adapter/desktop-cookie-janitor.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"intent-router.d.ts","sourceRoot":"","sources":["../src/intent-router.ts"],"names":[],"mappings":"AACA,cAAc,gCAAgC,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"llm-adapters.d.ts","sourceRoot":"","sources":["../src/llm-adapters.ts"],"names":[],"mappings":"AACA,cAAc,+BAA+B,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"local-llm-adapter.d.ts","sourceRoot":"","sources":["../src/local-llm-adapter.ts"],"names":[],"mappings":"AACA,cAAc,oCAAoC,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"local-storage.d.ts","sourceRoot":"","sources":["../src/local-storage.ts"],"names":[],"mappings":"AACA,cAAc,gCAAgC,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model-router.d.ts","sourceRoot":"","sources":["../src/model-router.ts"],"names":[],"mappings":"AACA,cAAc,+BAA+B,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ohttp-gateway.d.ts","sourceRoot":"","sources":["../src/ohttp-gateway.ts"],"names":[],"mappings":"AACA,cAAc,gCAAgC,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin-host.d.ts","sourceRoot":"","sources":["../src/plugin-host.ts"],"names":[],"mappings":"AACA,cAAc,8BAA8B,CAAA"}
|