@gajae-code/coding-agent 0.2.4 → 0.3.0
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 +27 -0
- package/README.md +1 -1
- package/dist/types/async/job-manager.d.ts +145 -2
- package/dist/types/commands/harness.d.ts +37 -0
- package/dist/types/config/settings-schema.d.ts +13 -3
- package/dist/types/config/settings.d.ts +3 -1
- package/dist/types/deep-interview/render-middleware.d.ts +5 -0
- package/dist/types/discovery/helpers.d.ts +1 -0
- package/dist/types/exec/bash-executor.d.ts +8 -1
- package/dist/types/extensibility/custom-tools/types.d.ts +1 -0
- package/dist/types/extensibility/extensions/types.d.ts +6 -0
- package/dist/types/extensibility/shared-events.d.ts +1 -0
- package/dist/types/gjc-runtime/restricted-role-agent-bash.d.ts +2 -0
- package/dist/types/gjc-runtime/state-graph.d.ts +4 -0
- package/dist/types/gjc-runtime/state-migrations.d.ts +24 -0
- package/dist/types/gjc-runtime/state-renderer.d.ts +65 -0
- package/dist/types/gjc-runtime/state-runtime.d.ts +2 -0
- package/dist/types/gjc-runtime/state-validation.d.ts +6 -0
- package/dist/types/gjc-runtime/state-writer.d.ts +137 -0
- package/dist/types/gjc-runtime/team-runtime.d.ts +81 -7
- package/dist/types/gjc-runtime/workflow-manifest.d.ts +54 -0
- package/dist/types/harness-control-plane/classifier.d.ts +13 -0
- package/dist/types/harness-control-plane/control-endpoint.d.ts +30 -0
- package/dist/types/harness-control-plane/finalize.d.ts +47 -0
- package/dist/types/harness-control-plane/frame-mapper.d.ts +29 -0
- package/dist/types/harness-control-plane/operate.d.ts +35 -0
- package/dist/types/harness-control-plane/owner.d.ts +46 -0
- package/dist/types/harness-control-plane/preserve.d.ts +19 -0
- package/dist/types/harness-control-plane/receipts.d.ts +88 -0
- package/dist/types/harness-control-plane/rpc-adapter.d.ts +66 -0
- package/dist/types/harness-control-plane/seams.d.ts +21 -0
- package/dist/types/harness-control-plane/session-lease.d.ts +65 -0
- package/dist/types/harness-control-plane/state-machine.d.ts +19 -0
- package/dist/types/harness-control-plane/storage.d.ts +53 -0
- package/dist/types/harness-control-plane/types.d.ts +162 -0
- package/dist/types/hooks/skill-keywords.d.ts +2 -1
- package/dist/types/hooks/skill-state.d.ts +2 -29
- package/dist/types/modes/acp/acp-client-bridge.d.ts +1 -1
- package/dist/types/modes/components/hook-selector.d.ts +1 -0
- package/dist/types/modes/components/skill-hud/render.d.ts +1 -1
- package/dist/types/modes/interactive-mode.d.ts +2 -0
- package/dist/types/modes/theme/defaults/index.d.ts +45 -9477
- package/dist/types/modes/theme/theme.d.ts +1 -5
- package/dist/types/modes/types.d.ts +2 -0
- package/dist/types/sdk.d.ts +4 -0
- package/dist/types/session/agent-session.d.ts +8 -0
- package/dist/types/session/streaming-output.d.ts +11 -0
- package/dist/types/skill-state/active-state.d.ts +3 -0
- package/dist/types/skill-state/deep-interview-mutation-guard.d.ts +1 -1
- package/dist/types/skill-state/workflow-state-contract.d.ts +24 -0
- package/dist/types/task/executor.d.ts +3 -0
- package/dist/types/task/types.d.ts +56 -3
- package/dist/types/tools/bash-allowed-prefixes.d.ts +5 -0
- package/dist/types/tools/bash.d.ts +24 -0
- package/dist/types/tools/cron.d.ts +110 -0
- package/dist/types/tools/index.d.ts +4 -0
- package/dist/types/tools/monitor.d.ts +54 -0
- package/dist/types/tools/subagent.d.ts +11 -1
- package/dist/types/web/search/index.d.ts +1 -0
- package/dist/types/web/search/provider.d.ts +11 -4
- package/dist/types/web/search/providers/duckduckgo.d.ts +57 -0
- package/dist/types/web/search/types.d.ts +1 -1
- package/package.json +7 -7
- package/src/async/job-manager.ts +522 -6
- package/src/cli/agents-cli.ts +3 -0
- package/src/cli/auth-broker-cli.ts +1 -0
- package/src/cli/config-cli.ts +10 -2
- package/src/cli.ts +2 -0
- package/src/commands/harness.ts +592 -0
- package/src/commands/team.ts +36 -39
- package/src/config/settings-schema.ts +15 -2
- package/src/config/settings.ts +49 -7
- package/src/deep-interview/render-middleware.ts +366 -0
- package/src/defaults/gjc/skills/deep-interview/SKILL.md +9 -2
- package/src/defaults/gjc/skills/ralplan/SKILL.md +8 -4
- package/src/defaults/gjc/skills/team/SKILL.md +47 -21
- package/src/defaults/gjc/skills/ultragoal/SKILL.md +78 -11
- package/src/discovery/helpers.ts +5 -0
- package/src/eval/js/shared/rewrite-imports.ts +1 -2
- package/src/exec/bash-executor.ts +20 -9
- package/src/extensibility/custom-tools/types.ts +1 -0
- package/src/extensibility/extensions/types.ts +6 -0
- package/src/extensibility/shared-events.ts +1 -0
- package/src/gjc-runtime/deep-interview-runtime.ts +40 -21
- package/src/gjc-runtime/goal-mode-request.ts +11 -3
- package/src/gjc-runtime/ralplan-runtime.ts +27 -10
- package/src/gjc-runtime/restricted-role-agent-bash.ts +5 -0
- package/src/gjc-runtime/state-graph.ts +86 -0
- package/src/gjc-runtime/state-migrations.ts +132 -0
- package/src/gjc-runtime/state-renderer.ts +345 -0
- package/src/gjc-runtime/state-runtime.ts +733 -21
- package/src/gjc-runtime/state-validation.ts +49 -0
- package/src/gjc-runtime/state-writer.ts +718 -0
- package/src/gjc-runtime/team-runtime.ts +1083 -89
- package/src/gjc-runtime/ultragoal-runtime.ts +348 -19
- package/src/gjc-runtime/workflow-manifest.generated.json +1497 -0
- package/src/gjc-runtime/workflow-manifest.ts +425 -0
- package/src/harness-control-plane/classifier.ts +128 -0
- package/src/harness-control-plane/control-endpoint.ts +137 -0
- package/src/harness-control-plane/finalize.ts +222 -0
- package/src/harness-control-plane/frame-mapper.ts +286 -0
- package/src/harness-control-plane/operate.ts +225 -0
- package/src/harness-control-plane/owner.ts +553 -0
- package/src/harness-control-plane/preserve.ts +102 -0
- package/src/harness-control-plane/receipts.ts +216 -0
- package/src/harness-control-plane/rpc-adapter.ts +276 -0
- package/src/harness-control-plane/seams.ts +39 -0
- package/src/harness-control-plane/session-lease.ts +388 -0
- package/src/harness-control-plane/state-machine.ts +97 -0
- package/src/harness-control-plane/storage.ts +257 -0
- package/src/harness-control-plane/types.ts +214 -0
- package/src/hooks/skill-keywords.ts +4 -2
- package/src/hooks/skill-state.ts +25 -42
- package/src/internal-urls/docs-index.generated.ts +6 -4
- package/src/lsp/render.ts +1 -1
- package/src/modes/acp/acp-agent.ts +1 -1
- package/src/modes/acp/acp-client-bridge.ts +1 -1
- package/src/modes/components/agent-dashboard.ts +1 -1
- package/src/modes/components/assistant-message.ts +5 -1
- package/src/modes/components/diff.ts +2 -2
- package/src/modes/components/hook-selector.ts +72 -2
- package/src/modes/components/skill-hud/render.ts +7 -2
- package/src/modes/controllers/event-controller.ts +71 -6
- package/src/modes/controllers/extension-ui-controller.ts +6 -0
- package/src/modes/controllers/input-controller.ts +19 -3
- package/src/modes/controllers/selector-controller.ts +3 -2
- package/src/modes/interactive-mode.ts +21 -2
- package/src/modes/theme/defaults/index.ts +0 -196
- package/src/modes/theme/theme.ts +35 -35
- package/src/modes/types.ts +2 -0
- package/src/prompts/agents/architect.md +5 -1
- package/src/prompts/agents/critic.md +5 -1
- package/src/prompts/agents/executor.md +13 -0
- package/src/prompts/agents/frontmatter.md +1 -0
- package/src/prompts/agents/planner.md +5 -1
- package/src/prompts/tools/bash.md +9 -0
- package/src/prompts/tools/cron.md +25 -0
- package/src/prompts/tools/monitor.md +30 -0
- package/src/prompts/tools/subagent.md +33 -3
- package/src/runtime-mcp/oauth-flow.ts +4 -2
- package/src/sdk.ts +7 -0
- package/src/session/agent-session.ts +247 -38
- package/src/session/session-manager.ts +13 -1
- package/src/session/streaming-output.ts +21 -0
- package/src/skill-state/active-state.ts +222 -78
- package/src/skill-state/deep-interview-mutation-guard.ts +91 -13
- package/src/skill-state/initial-phase.ts +2 -0
- package/src/skill-state/workflow-state-contract.ts +26 -0
- package/src/task/agents.ts +1 -0
- package/src/task/executor.ts +51 -8
- package/src/task/index.ts +120 -8
- package/src/task/render.ts +6 -3
- package/src/task/types.ts +57 -3
- package/src/tools/ask.ts +28 -7
- package/src/tools/bash-allowed-prefixes.ts +169 -0
- package/src/tools/bash.ts +190 -29
- package/src/tools/browser/tab-worker.ts +1 -1
- package/src/tools/cron.ts +665 -0
- package/src/tools/index.ts +20 -2
- package/src/tools/monitor.ts +136 -0
- package/src/tools/subagent.ts +255 -64
- package/src/vim/engine.ts +3 -3
- package/src/web/search/index.ts +31 -18
- package/src/web/search/provider.ts +57 -12
- package/src/web/search/providers/duckduckgo.ts +279 -0
- package/src/web/search/types.ts +2 -0
- package/src/modes/theme/dark.json +0 -95
- package/src/modes/theme/defaults/alabaster.json +0 -93
- package/src/modes/theme/defaults/amethyst.json +0 -96
- package/src/modes/theme/defaults/anthracite.json +0 -93
- package/src/modes/theme/defaults/basalt.json +0 -91
- package/src/modes/theme/defaults/birch.json +0 -95
- package/src/modes/theme/defaults/dark-abyss.json +0 -91
- package/src/modes/theme/defaults/dark-arctic.json +0 -104
- package/src/modes/theme/defaults/dark-aurora.json +0 -95
- package/src/modes/theme/defaults/dark-catppuccin.json +0 -107
- package/src/modes/theme/defaults/dark-cavern.json +0 -91
- package/src/modes/theme/defaults/dark-copper.json +0 -95
- package/src/modes/theme/defaults/dark-cosmos.json +0 -90
- package/src/modes/theme/defaults/dark-cyberpunk.json +0 -102
- package/src/modes/theme/defaults/dark-dracula.json +0 -98
- package/src/modes/theme/defaults/dark-eclipse.json +0 -91
- package/src/modes/theme/defaults/dark-ember.json +0 -95
- package/src/modes/theme/defaults/dark-equinox.json +0 -90
- package/src/modes/theme/defaults/dark-forest.json +0 -96
- package/src/modes/theme/defaults/dark-github.json +0 -105
- package/src/modes/theme/defaults/dark-gruvbox.json +0 -112
- package/src/modes/theme/defaults/dark-lavender.json +0 -95
- package/src/modes/theme/defaults/dark-lunar.json +0 -89
- package/src/modes/theme/defaults/dark-midnight.json +0 -95
- package/src/modes/theme/defaults/dark-monochrome.json +0 -94
- package/src/modes/theme/defaults/dark-monokai.json +0 -98
- package/src/modes/theme/defaults/dark-nebula.json +0 -90
- package/src/modes/theme/defaults/dark-nord.json +0 -97
- package/src/modes/theme/defaults/dark-ocean.json +0 -101
- package/src/modes/theme/defaults/dark-one.json +0 -100
- package/src/modes/theme/defaults/dark-poimandres.json +0 -141
- package/src/modes/theme/defaults/dark-rainforest.json +0 -91
- package/src/modes/theme/defaults/dark-reef.json +0 -91
- package/src/modes/theme/defaults/dark-retro.json +0 -92
- package/src/modes/theme/defaults/dark-rose-pine.json +0 -96
- package/src/modes/theme/defaults/dark-sakura.json +0 -95
- package/src/modes/theme/defaults/dark-slate.json +0 -95
- package/src/modes/theme/defaults/dark-solarized.json +0 -97
- package/src/modes/theme/defaults/dark-solstice.json +0 -90
- package/src/modes/theme/defaults/dark-starfall.json +0 -91
- package/src/modes/theme/defaults/dark-sunset.json +0 -99
- package/src/modes/theme/defaults/dark-swamp.json +0 -90
- package/src/modes/theme/defaults/dark-synthwave.json +0 -103
- package/src/modes/theme/defaults/dark-taiga.json +0 -91
- package/src/modes/theme/defaults/dark-terminal.json +0 -95
- package/src/modes/theme/defaults/dark-tokyo-night.json +0 -101
- package/src/modes/theme/defaults/dark-tundra.json +0 -91
- package/src/modes/theme/defaults/dark-twilight.json +0 -91
- package/src/modes/theme/defaults/dark-volcanic.json +0 -91
- package/src/modes/theme/defaults/graphite.json +0 -92
- package/src/modes/theme/defaults/light-arctic.json +0 -107
- package/src/modes/theme/defaults/light-aurora-day.json +0 -91
- package/src/modes/theme/defaults/light-canyon.json +0 -91
- package/src/modes/theme/defaults/light-catppuccin.json +0 -106
- package/src/modes/theme/defaults/light-cirrus.json +0 -90
- package/src/modes/theme/defaults/light-coral.json +0 -95
- package/src/modes/theme/defaults/light-cyberpunk.json +0 -96
- package/src/modes/theme/defaults/light-dawn.json +0 -90
- package/src/modes/theme/defaults/light-dunes.json +0 -91
- package/src/modes/theme/defaults/light-eucalyptus.json +0 -95
- package/src/modes/theme/defaults/light-forest.json +0 -100
- package/src/modes/theme/defaults/light-frost.json +0 -95
- package/src/modes/theme/defaults/light-github.json +0 -115
- package/src/modes/theme/defaults/light-glacier.json +0 -91
- package/src/modes/theme/defaults/light-gruvbox.json +0 -108
- package/src/modes/theme/defaults/light-haze.json +0 -90
- package/src/modes/theme/defaults/light-honeycomb.json +0 -95
- package/src/modes/theme/defaults/light-lagoon.json +0 -91
- package/src/modes/theme/defaults/light-lavender.json +0 -95
- package/src/modes/theme/defaults/light-meadow.json +0 -91
- package/src/modes/theme/defaults/light-mint.json +0 -95
- package/src/modes/theme/defaults/light-monochrome.json +0 -101
- package/src/modes/theme/defaults/light-ocean.json +0 -99
- package/src/modes/theme/defaults/light-one.json +0 -99
- package/src/modes/theme/defaults/light-opal.json +0 -91
- package/src/modes/theme/defaults/light-orchard.json +0 -91
- package/src/modes/theme/defaults/light-paper.json +0 -95
- package/src/modes/theme/defaults/light-poimandres.json +0 -141
- package/src/modes/theme/defaults/light-prism.json +0 -90
- package/src/modes/theme/defaults/light-retro.json +0 -98
- package/src/modes/theme/defaults/light-sand.json +0 -95
- package/src/modes/theme/defaults/light-savanna.json +0 -91
- package/src/modes/theme/defaults/light-solarized.json +0 -102
- package/src/modes/theme/defaults/light-soleil.json +0 -90
- package/src/modes/theme/defaults/light-sunset.json +0 -99
- package/src/modes/theme/defaults/light-synthwave.json +0 -98
- package/src/modes/theme/defaults/light-tokyo-night.json +0 -111
- package/src/modes/theme/defaults/light-wetland.json +0 -91
- package/src/modes/theme/defaults/light-zenith.json +0 -89
- package/src/modes/theme/defaults/limestone.json +0 -94
- package/src/modes/theme/defaults/mahogany.json +0 -97
- package/src/modes/theme/defaults/marble.json +0 -93
- package/src/modes/theme/defaults/obsidian.json +0 -91
- package/src/modes/theme/defaults/onyx.json +0 -91
- package/src/modes/theme/defaults/pearl.json +0 -93
- package/src/modes/theme/defaults/porcelain.json +0 -91
- package/src/modes/theme/defaults/quartz.json +0 -96
- package/src/modes/theme/defaults/sandstone.json +0 -95
- package/src/modes/theme/defaults/titanium.json +0 -90
- package/src/modes/theme/light.json +0 -93
package/src/vim/engine.ts
CHANGED
|
@@ -858,7 +858,7 @@ export class VimEngine {
|
|
|
858
858
|
}
|
|
859
859
|
case "r": {
|
|
860
860
|
const replacement = tokens[nextIndex + 1];
|
|
861
|
-
if (
|
|
861
|
+
if (replacement?.value.length !== 1) {
|
|
862
862
|
throw new VimError("Visual replace requires a literal character", opToken);
|
|
863
863
|
}
|
|
864
864
|
const visual = expandVisualOffsets(
|
|
@@ -1109,7 +1109,7 @@ export class VimEngine {
|
|
|
1109
1109
|
return nextIndex + 1;
|
|
1110
1110
|
case "r": {
|
|
1111
1111
|
const replacement = tokens[nextIndex + 1];
|
|
1112
|
-
if (
|
|
1112
|
+
if (replacement?.value.length !== 1) {
|
|
1113
1113
|
throw new VimError("r requires a replacement character", token);
|
|
1114
1114
|
}
|
|
1115
1115
|
await this.#applyAtomicChange(["r", replacement.value], () => {
|
|
@@ -1746,7 +1746,7 @@ export class VimEngine {
|
|
|
1746
1746
|
case "t":
|
|
1747
1747
|
case "T": {
|
|
1748
1748
|
const searchToken = tokens[index + 1];
|
|
1749
|
-
if (
|
|
1749
|
+
if (searchToken?.value.length !== 1) {
|
|
1750
1750
|
throw new VimError(`${token.value} requires a literal character`, token);
|
|
1751
1751
|
}
|
|
1752
1752
|
this.lastCharFind = { char: searchToken.value, mode: token.value as "f" | "F" | "t" | "T" };
|
package/src/web/search/index.ts
CHANGED
|
@@ -8,6 +8,7 @@ import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallb
|
|
|
8
8
|
import type { AuthStorage } from "@gajae-code/ai";
|
|
9
9
|
import { prompt } from "@gajae-code/utils";
|
|
10
10
|
import * as z from "zod/v4";
|
|
11
|
+
import { parseModelString } from "../../config/model-resolver";
|
|
11
12
|
import type { CustomTool, CustomToolContext, RenderResultOptions } from "../../extensibility/custom-tools/types";
|
|
12
13
|
import type { Theme } from "../../modes/theme/theme";
|
|
13
14
|
import webSearchSystemPrompt from "../../prompts/system/web-search.md" with { type: "text" };
|
|
@@ -16,7 +17,7 @@ import { discoverAuthStorage } from "../../sdk";
|
|
|
16
17
|
import type { ToolSession } from "../../tools";
|
|
17
18
|
import { formatAge } from "../../tools/render-utils";
|
|
18
19
|
import { throwIfAborted } from "../../tools/tool-errors";
|
|
19
|
-
import {
|
|
20
|
+
import { getSearchProviderLabel, resolveProviderChain, type SearchProvider } from "./provider";
|
|
20
21
|
import { renderSearchCall, renderSearchResult, type SearchRenderDetails } from "./render";
|
|
21
22
|
import type { SearchProviderId, SearchResponse } from "./types";
|
|
22
23
|
import { SearchProviderError } from "./types";
|
|
@@ -115,10 +116,21 @@ function formatForLLM(response: SearchResponse): string {
|
|
|
115
116
|
return parts.join("\n");
|
|
116
117
|
}
|
|
117
118
|
|
|
119
|
+
/** Best-effort active model provider: prefer the resolved Model, fall back to parsing the model string. */
|
|
120
|
+
function resolveActiveModelProvider(
|
|
121
|
+
modelProvider: string | undefined,
|
|
122
|
+
modelString: string | undefined,
|
|
123
|
+
): string | undefined {
|
|
124
|
+
if (modelProvider) return modelProvider;
|
|
125
|
+
if (modelString) return parseModelString(modelString)?.provider;
|
|
126
|
+
return undefined;
|
|
127
|
+
}
|
|
128
|
+
|
|
118
129
|
interface ExecuteSearchOptions {
|
|
119
130
|
authStorage: AuthStorage;
|
|
120
131
|
sessionId?: string;
|
|
121
132
|
signal?: AbortSignal;
|
|
133
|
+
activeModelProvider?: string;
|
|
122
134
|
}
|
|
123
135
|
|
|
124
136
|
/** Execute web search */
|
|
@@ -127,20 +139,11 @@ async function executeSearch(
|
|
|
127
139
|
params: SearchQueryParams,
|
|
128
140
|
options: ExecuteSearchOptions,
|
|
129
141
|
): Promise<{ content: Array<{ type: "text"; text: string }>; details: SearchRenderDetails }> {
|
|
130
|
-
const { authStorage, sessionId, signal } = options;
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
)
|
|
136
|
-
: await resolveProviderChain(authStorage);
|
|
137
|
-
if (providers.length === 0) {
|
|
138
|
-
const message = "No web search provider configured.";
|
|
139
|
-
return {
|
|
140
|
-
content: [{ type: "text" as const, text: `Error: ${message}` }],
|
|
141
|
-
details: { response: { provider: "none", sources: [] }, error: message },
|
|
142
|
-
};
|
|
143
|
-
}
|
|
142
|
+
const { authStorage, sessionId, signal, activeModelProvider } = options;
|
|
143
|
+
// Pass `params.provider` straight through: when omitted (the normal model-facing
|
|
144
|
+
// path) it is `undefined`, so `resolveProviderChain` applies the settings-configured
|
|
145
|
+
// preferred provider. Coalescing to "auto" here would silently bypass that preference.
|
|
146
|
+
const providers = await resolveProviderChain(authStorage, params.provider, activeModelProvider);
|
|
144
147
|
|
|
145
148
|
const failures: Array<{ provider: SearchProvider; error: unknown }> = [];
|
|
146
149
|
let lastProvider = providers[0];
|
|
@@ -207,13 +210,14 @@ async function executeSearch(
|
|
|
207
210
|
*/
|
|
208
211
|
export async function runSearchQuery(
|
|
209
212
|
params: SearchQueryParams,
|
|
210
|
-
options: { authStorage?: AuthStorage; sessionId?: string; signal?: AbortSignal } = {},
|
|
213
|
+
options: { authStorage?: AuthStorage; sessionId?: string; signal?: AbortSignal; activeModelProvider?: string } = {},
|
|
211
214
|
): Promise<{ content: Array<{ type: "text"; text: string }>; details: SearchRenderDetails }> {
|
|
212
215
|
const authStorage = options.authStorage ?? (await discoverAuthStorage());
|
|
213
216
|
return executeSearch("cli-web-search", params, {
|
|
214
217
|
authStorage,
|
|
215
218
|
sessionId: options.sessionId,
|
|
216
219
|
signal: options.signal,
|
|
220
|
+
activeModelProvider: options.activeModelProvider,
|
|
217
221
|
});
|
|
218
222
|
}
|
|
219
223
|
|
|
@@ -247,7 +251,11 @@ export class WebSearchTool implements AgentTool<typeof webSearchSchema, SearchRe
|
|
|
247
251
|
): Promise<AgentToolResult<SearchRenderDetails>> {
|
|
248
252
|
const authStorage = this.#session.authStorage ?? (await discoverAuthStorage());
|
|
249
253
|
const sessionId = this.#session.getSessionId?.() ?? undefined;
|
|
250
|
-
|
|
254
|
+
const activeModelProvider = resolveActiveModelProvider(
|
|
255
|
+
this.#session.model?.provider,
|
|
256
|
+
this.#session.getActiveModelString?.(),
|
|
257
|
+
);
|
|
258
|
+
return executeSearch(_toolCallId, params, { authStorage, sessionId, signal, activeModelProvider });
|
|
251
259
|
}
|
|
252
260
|
}
|
|
253
261
|
|
|
@@ -267,7 +275,12 @@ export const webSearchCustomTool: CustomTool<typeof webSearchSchema, SearchRende
|
|
|
267
275
|
) {
|
|
268
276
|
const authStorage = ctx.modelRegistry?.authStorage ?? (await discoverAuthStorage());
|
|
269
277
|
const sessionId = ctx.sessionManager.getSessionId();
|
|
270
|
-
return executeSearch(toolCallId, params, {
|
|
278
|
+
return executeSearch(toolCallId, params, {
|
|
279
|
+
authStorage,
|
|
280
|
+
sessionId,
|
|
281
|
+
signal,
|
|
282
|
+
activeModelProvider: ctx.model?.provider,
|
|
283
|
+
});
|
|
271
284
|
},
|
|
272
285
|
|
|
273
286
|
renderCall(args: SearchToolParams, options: RenderResultOptions, theme: Theme) {
|
|
@@ -93,6 +93,11 @@ const PROVIDER_META: Record<SearchProviderId, ProviderMeta> = {
|
|
|
93
93
|
label: "SearXNG",
|
|
94
94
|
load: async () => new (await import("./providers/searxng")).SearXNGProvider(),
|
|
95
95
|
},
|
|
96
|
+
duckduckgo: {
|
|
97
|
+
id: "duckduckgo",
|
|
98
|
+
label: "DuckDuckGo",
|
|
99
|
+
load: async () => new (await import("./providers/duckduckgo")).DuckDuckGoProvider(),
|
|
100
|
+
},
|
|
96
101
|
};
|
|
97
102
|
|
|
98
103
|
const instanceCache = new Map<SearchProviderId, SearchProvider>();
|
|
@@ -119,6 +124,7 @@ export async function getSearchProvider(id: SearchProviderId): Promise<SearchPro
|
|
|
119
124
|
}
|
|
120
125
|
|
|
121
126
|
export const SEARCH_PROVIDER_ORDER: SearchProviderId[] = [
|
|
127
|
+
"duckduckgo",
|
|
122
128
|
"tavily",
|
|
123
129
|
"perplexity",
|
|
124
130
|
"brave",
|
|
@@ -135,6 +141,30 @@ export const SEARCH_PROVIDER_ORDER: SearchProviderId[] = [
|
|
|
135
141
|
"searxng",
|
|
136
142
|
];
|
|
137
143
|
|
|
144
|
+
/**
|
|
145
|
+
* Map an active model's provider string to its own native web-search provider.
|
|
146
|
+
* Keys are real model provider ids (see packages/ai/src/types.ts KnownProvider);
|
|
147
|
+
* a few aliases (gemini/kimi) and API strings (openai-responses) are tolerated
|
|
148
|
+
* defensively. Providers absent from this map (custom/unknown) fall through to
|
|
149
|
+
* DuckDuckGo.
|
|
150
|
+
*/
|
|
151
|
+
const MODEL_PROVIDER_TO_SEARCH: Record<string, SearchProviderId> = {
|
|
152
|
+
openai: "codex",
|
|
153
|
+
"openai-codex": "codex",
|
|
154
|
+
"openai-responses": "codex",
|
|
155
|
+
anthropic: "anthropic",
|
|
156
|
+
google: "gemini",
|
|
157
|
+
"google-gemini-cli": "gemini",
|
|
158
|
+
"google-antigravity": "gemini",
|
|
159
|
+
gemini: "gemini",
|
|
160
|
+
moonshot: "kimi",
|
|
161
|
+
"kimi-code": "kimi",
|
|
162
|
+
kimi: "kimi",
|
|
163
|
+
zai: "zai",
|
|
164
|
+
perplexity: "perplexity",
|
|
165
|
+
synthetic: "synthetic",
|
|
166
|
+
};
|
|
167
|
+
|
|
138
168
|
/** Preferred provider set via settings (default: auto) */
|
|
139
169
|
let preferredProvId: SearchProviderId | "auto" = "auto";
|
|
140
170
|
|
|
@@ -144,30 +174,45 @@ export function setPreferredSearchProvider(provider: SearchProviderId | "auto"):
|
|
|
144
174
|
}
|
|
145
175
|
|
|
146
176
|
/**
|
|
147
|
-
*
|
|
148
|
-
*
|
|
149
|
-
* is
|
|
177
|
+
* Resolve the ordered provider chain for a search request.
|
|
178
|
+
*
|
|
179
|
+
* Resolution is active-model-gated, never credential-scanning:
|
|
180
|
+
* 1. An explicitly preferred provider (settings) that is available is primary.
|
|
181
|
+
* 2. Otherwise the active model's own native search is primary, but only when
|
|
182
|
+
* that provider's own credentials are present (its `isAvailable()`).
|
|
183
|
+
* 3. DuckDuckGo (keyless) is always appended as the terminal fallback, so a
|
|
184
|
+
* missing primary — or a primary runtime failure — still returns results
|
|
185
|
+
* with zero configuration. Keyed standalone providers are never
|
|
186
|
+
* auto-selected; they are reachable only via explicit selection (step 1).
|
|
150
187
|
*/
|
|
151
188
|
export async function resolveProviderChain(
|
|
152
189
|
authStorage: AuthStorage,
|
|
153
190
|
preferredProvider: SearchProviderId | "auto" = preferredProvId,
|
|
191
|
+
activeModelProvider?: string,
|
|
154
192
|
): Promise<SearchProvider[]> {
|
|
155
|
-
const
|
|
193
|
+
const chain: SearchProviderId[] = [];
|
|
156
194
|
|
|
157
195
|
if (preferredProvider !== "auto") {
|
|
158
196
|
const provider = await getSearchProvider(preferredProvider);
|
|
159
197
|
if (await provider.isAvailable(authStorage)) {
|
|
160
|
-
|
|
198
|
+
chain.push(preferredProvider);
|
|
161
199
|
}
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
200
|
+
} else if (activeModelProvider) {
|
|
201
|
+
const nativeId = MODEL_PROVIDER_TO_SEARCH[activeModelProvider.toLowerCase()];
|
|
202
|
+
if (nativeId) {
|
|
203
|
+
const provider = await getSearchProvider(nativeId);
|
|
204
|
+
if (await provider.isAvailable(authStorage)) {
|
|
205
|
+
chain.push(nativeId);
|
|
206
|
+
}
|
|
169
207
|
}
|
|
170
208
|
}
|
|
171
209
|
|
|
210
|
+
// DuckDuckGo is the permissionless terminal fallback (deduped).
|
|
211
|
+
if (!chain.includes("duckduckgo")) chain.push("duckduckgo");
|
|
212
|
+
|
|
213
|
+
const providers: SearchProvider[] = [];
|
|
214
|
+
for (const id of chain) {
|
|
215
|
+
providers.push(await getSearchProvider(id));
|
|
216
|
+
}
|
|
172
217
|
return providers;
|
|
173
218
|
}
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DuckDuckGo Web Search Provider
|
|
3
|
+
*
|
|
4
|
+
* Keyless, permissionless web search. Scrapes DuckDuckGo's no-JavaScript HTML
|
|
5
|
+
* endpoints and maps anchors/snippets into the unified SearchResponse shape
|
|
6
|
+
* (sources only — DuckDuckGo does not synthesize an answer).
|
|
7
|
+
*
|
|
8
|
+
* This is the zero-config default/fallback backend: it requires no API key and
|
|
9
|
+
* no OAuth, so `isAvailable()` is always true. Because DuckDuckGo applies
|
|
10
|
+
* anti-bot rate limiting (HTTP 202 / 403 / empty responses) from datacenter and
|
|
11
|
+
* VPN IPs, the provider is best-effort: it retries with backoff, rotates the
|
|
12
|
+
* user-agent, and alternates between the `html` and `lite` endpoints. When every
|
|
13
|
+
* attempt fails it throws a {@link SearchProviderError} rather than returning an
|
|
14
|
+
* empty success — it never falls through to keyed providers.
|
|
15
|
+
*
|
|
16
|
+
* Endpoints:
|
|
17
|
+
* https://html.duckduckgo.com/html/ (primary)
|
|
18
|
+
* https://lite.duckduckgo.com/lite/ (fallback markup)
|
|
19
|
+
*
|
|
20
|
+
* The HTML markup is liable to drift; the parser is deliberately small and is
|
|
21
|
+
* pinned by fixture-driven tests (see test/tools/web-search-duckduckgo.test.ts).
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import type { AuthStorage } from "@gajae-code/ai";
|
|
25
|
+
|
|
26
|
+
import type { SearchResponse, SearchSource } from "../../../web/search/types";
|
|
27
|
+
import { SearchProviderError } from "../../../web/search/types";
|
|
28
|
+
import { clampNumResults } from "../utils";
|
|
29
|
+
import type { SearchParams } from "./base";
|
|
30
|
+
import { SearchProvider } from "./base";
|
|
31
|
+
import { classifyProviderHttpError, withHardTimeout } from "./utils";
|
|
32
|
+
|
|
33
|
+
const HTML_ENDPOINT = "https://html.duckduckgo.com/html/";
|
|
34
|
+
const LITE_ENDPOINT = "https://lite.duckduckgo.com/lite/";
|
|
35
|
+
|
|
36
|
+
const DEFAULT_NUM_RESULTS = 10;
|
|
37
|
+
const MAX_NUM_RESULTS = 20;
|
|
38
|
+
|
|
39
|
+
/** Endpoint order across retry attempts; rotates markup and user-agent. */
|
|
40
|
+
const ATTEMPTS: Array<"html" | "lite"> = ["html", "lite", "html"];
|
|
41
|
+
|
|
42
|
+
/** Backoff (ms) applied between attempts. Index 0 is unused (first attempt). */
|
|
43
|
+
const BACKOFF_MS = [0, 400, 800];
|
|
44
|
+
|
|
45
|
+
/** Realistic desktop user-agents rotated per attempt to dodge naive blocks. */
|
|
46
|
+
const USER_AGENTS = [
|
|
47
|
+
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36",
|
|
48
|
+
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36",
|
|
49
|
+
"Mozilla/5.0 (X11; Linux x86_64; rv:125.0) Gecko/20100101 Firefox/125.0",
|
|
50
|
+
];
|
|
51
|
+
|
|
52
|
+
/** Map our recency filter to DuckDuckGo's `df` time parameter. */
|
|
53
|
+
const RECENCY_MAP: Record<"day" | "week" | "month" | "year", string> = {
|
|
54
|
+
day: "d",
|
|
55
|
+
week: "w",
|
|
56
|
+
month: "m",
|
|
57
|
+
year: "y",
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
interface ParsedResult {
|
|
61
|
+
title: string;
|
|
62
|
+
url: string;
|
|
63
|
+
snippet?: string;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** Decode a small set of HTML entities without pulling in a DOM library. */
|
|
67
|
+
function decodeEntities(input: string): string {
|
|
68
|
+
return input
|
|
69
|
+
.replace(/&/g, "&")
|
|
70
|
+
.replace(/</g, "<")
|
|
71
|
+
.replace(/>/g, ">")
|
|
72
|
+
.replace(/"/g, '"')
|
|
73
|
+
.replace(/�*39;|�*27;|'/gi, "'")
|
|
74
|
+
.replace(/�*2f;/gi, "/")
|
|
75
|
+
.replace(/&#(\d+);/g, (_, dec: string) => String.fromCodePoint(Number(dec)))
|
|
76
|
+
.replace(/&#x([0-9a-f]+);/gi, (_, hex: string) => String.fromCodePoint(Number.parseInt(hex, 16)))
|
|
77
|
+
.replace(/ /g, " ");
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** Strip tags, decode entities, and collapse whitespace from an HTML fragment. */
|
|
81
|
+
function cleanText(fragment: string): string {
|
|
82
|
+
return decodeEntities(fragment.replace(/<[^>]+>/g, ""))
|
|
83
|
+
.replace(/\s+/g, " ")
|
|
84
|
+
.trim();
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Resolve a DuckDuckGo result href to the real destination URL. DuckDuckGo wraps
|
|
89
|
+
* external links in a `/l/?uddg=<encoded>` redirect; `lite` sometimes links
|
|
90
|
+
* directly. Returns null for unusable or internal links (so ads/redirect shells
|
|
91
|
+
* are dropped).
|
|
92
|
+
*/
|
|
93
|
+
export function decodeResultUrl(href: string): string | null {
|
|
94
|
+
let h = decodeEntities(href.trim());
|
|
95
|
+
if (!h || h.startsWith("#")) return null;
|
|
96
|
+
if (h.startsWith("//")) h = `https:${h}`;
|
|
97
|
+
let parsed: URL;
|
|
98
|
+
try {
|
|
99
|
+
parsed = new URL(h, "https://duckduckgo.com");
|
|
100
|
+
} catch {
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
const uddg = parsed.searchParams.get("uddg");
|
|
104
|
+
if (uddg) {
|
|
105
|
+
try {
|
|
106
|
+
const target = new URL(uddg);
|
|
107
|
+
if (target.protocol !== "http:" && target.protocol !== "https:") return null;
|
|
108
|
+
if (target.hostname.endsWith("duckduckgo.com")) return null;
|
|
109
|
+
return target.toString();
|
|
110
|
+
} catch {
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
// No redirect wrapper: accept only real external http(s) links.
|
|
115
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") return null;
|
|
116
|
+
if (parsed.hostname.endsWith("duckduckgo.com")) return null;
|
|
117
|
+
return parsed.toString();
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/** Parse results from the `html.duckduckgo.com/html/` markup. */
|
|
121
|
+
export function parseHtmlResults(html: string): ParsedResult[] {
|
|
122
|
+
const titleRe = /<a\b[^>]*class="[^"]*\bresult__a\b[^"]*"[^>]*href="([^"]+)"[^>]*>([\s\S]*?)<\/a>/gi;
|
|
123
|
+
const snippetRe = /<a\b[^>]*class="[^"]*\bresult__snippet\b[^"]*"[^>]*>([\s\S]*?)<\/a>/gi;
|
|
124
|
+
const snippets: string[] = [];
|
|
125
|
+
for (const m of html.matchAll(snippetRe)) snippets.push(cleanText(m[1]));
|
|
126
|
+
|
|
127
|
+
const results: ParsedResult[] = [];
|
|
128
|
+
let idx = 0;
|
|
129
|
+
for (const m of html.matchAll(titleRe)) {
|
|
130
|
+
const url = decodeResultUrl(m[1]);
|
|
131
|
+
const title = cleanText(m[2]);
|
|
132
|
+
const snippet = snippets[idx];
|
|
133
|
+
idx++;
|
|
134
|
+
if (!url || !title) continue;
|
|
135
|
+
results.push({ title, url, snippet: snippet || undefined });
|
|
136
|
+
}
|
|
137
|
+
return results;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/** Parse results from the `lite.duckduckgo.com/lite/` markup. */
|
|
141
|
+
export function parseLiteResults(html: string): ParsedResult[] {
|
|
142
|
+
const linkRe = /<a\b[^>]*class="[^"]*\bresult-link\b[^"]*"[^>]*href="([^"]+)"[^>]*>([\s\S]*?)<\/a>/gi;
|
|
143
|
+
const snippetRe = /<td\b[^>]*class="[^"]*\bresult-snippet\b[^"]*"[^>]*>([\s\S]*?)<\/td>/gi;
|
|
144
|
+
const snippets: string[] = [];
|
|
145
|
+
for (const m of html.matchAll(snippetRe)) snippets.push(cleanText(m[1]));
|
|
146
|
+
|
|
147
|
+
const results: ParsedResult[] = [];
|
|
148
|
+
let idx = 0;
|
|
149
|
+
for (const m of html.matchAll(linkRe)) {
|
|
150
|
+
const url = decodeResultUrl(m[1]);
|
|
151
|
+
const title = cleanText(m[2]);
|
|
152
|
+
const snippet = snippets[idx];
|
|
153
|
+
idx++;
|
|
154
|
+
if (!url || !title) continue;
|
|
155
|
+
results.push({ title, url, snippet: snippet || undefined });
|
|
156
|
+
}
|
|
157
|
+
return results;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function delay(ms: number, signal?: AbortSignal): Promise<void> {
|
|
161
|
+
return new Promise((resolve, reject) => {
|
|
162
|
+
if (signal?.aborted) {
|
|
163
|
+
reject(new DOMException("Aborted", "AbortError"));
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
const timer = setTimeout(resolve, ms);
|
|
167
|
+
signal?.addEventListener(
|
|
168
|
+
"abort",
|
|
169
|
+
() => {
|
|
170
|
+
clearTimeout(timer);
|
|
171
|
+
reject(new DOMException("Aborted", "AbortError"));
|
|
172
|
+
},
|
|
173
|
+
{ once: true },
|
|
174
|
+
);
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/** Fetch one endpoint and parse it. Throws on HTTP error, rate-limit, or empty parse. */
|
|
179
|
+
async function fetchAndParse(
|
|
180
|
+
endpoint: "html" | "lite",
|
|
181
|
+
query: string,
|
|
182
|
+
df: string | undefined,
|
|
183
|
+
userAgent: string,
|
|
184
|
+
signal: AbortSignal | undefined,
|
|
185
|
+
): Promise<ParsedResult[]> {
|
|
186
|
+
const url = endpoint === "html" ? HTML_ENDPOINT : LITE_ENDPOINT;
|
|
187
|
+
const body = new URLSearchParams({ q: query });
|
|
188
|
+
if (df) body.set("df", df);
|
|
189
|
+
|
|
190
|
+
const response = await fetch(url, {
|
|
191
|
+
method: "POST",
|
|
192
|
+
headers: {
|
|
193
|
+
"User-Agent": userAgent,
|
|
194
|
+
Accept: "text/html,application/xhtml+xml",
|
|
195
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
196
|
+
"Accept-Language": "en-US,en;q=0.9",
|
|
197
|
+
},
|
|
198
|
+
body,
|
|
199
|
+
signal: withHardTimeout(signal),
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
// DuckDuckGo signals soft blocks with 202 (which is still response.ok).
|
|
203
|
+
if (response.status === 202) {
|
|
204
|
+
throw new SearchProviderError("duckduckgo", "duckduckgo: rate-limited (202)", 202);
|
|
205
|
+
}
|
|
206
|
+
if (!response.ok) {
|
|
207
|
+
const errorText = await response.text();
|
|
208
|
+
const classified = classifyProviderHttpError("duckduckgo", response.status, errorText);
|
|
209
|
+
if (classified) throw classified;
|
|
210
|
+
throw new SearchProviderError("duckduckgo", `DuckDuckGo error (${response.status})`, response.status);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const text = await response.text();
|
|
214
|
+
const parsed = endpoint === "html" ? parseHtmlResults(text) : parseLiteResults(text);
|
|
215
|
+
if (parsed.length === 0) {
|
|
216
|
+
throw new SearchProviderError("duckduckgo", "duckduckgo: no parseable results (possible block)");
|
|
217
|
+
}
|
|
218
|
+
return parsed;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/** Execute a keyless DuckDuckGo web search with light resilience. */
|
|
222
|
+
export async function searchDuckDuckGo(params: {
|
|
223
|
+
query: string;
|
|
224
|
+
num_results?: number;
|
|
225
|
+
recency?: "day" | "week" | "month" | "year";
|
|
226
|
+
signal?: AbortSignal;
|
|
227
|
+
}): Promise<SearchResponse> {
|
|
228
|
+
const numResults = clampNumResults(params.num_results, DEFAULT_NUM_RESULTS, MAX_NUM_RESULTS);
|
|
229
|
+
const df = params.recency ? RECENCY_MAP[params.recency] : undefined;
|
|
230
|
+
|
|
231
|
+
let lastError: unknown;
|
|
232
|
+
for (let attempt = 0; attempt < ATTEMPTS.length; attempt++) {
|
|
233
|
+
if (params.signal?.aborted) throw new DOMException("Aborted", "AbortError");
|
|
234
|
+
if (BACKOFF_MS[attempt] > 0) await delay(BACKOFF_MS[attempt], params.signal);
|
|
235
|
+
|
|
236
|
+
const endpoint = ATTEMPTS[attempt];
|
|
237
|
+
const userAgent = USER_AGENTS[attempt % USER_AGENTS.length];
|
|
238
|
+
try {
|
|
239
|
+
const parsed = await fetchAndParse(endpoint, params.query, df, userAgent, params.signal);
|
|
240
|
+
const sources: SearchSource[] = parsed.slice(0, numResults).map(result => ({
|
|
241
|
+
title: result.title,
|
|
242
|
+
url: result.url,
|
|
243
|
+
snippet: result.snippet,
|
|
244
|
+
}));
|
|
245
|
+
return { provider: "duckduckgo", sources };
|
|
246
|
+
} catch (error) {
|
|
247
|
+
// A caller cancellation must abort immediately, never silently retry.
|
|
248
|
+
if (params.signal?.aborted) throw error;
|
|
249
|
+
lastError = error;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
if (lastError instanceof SearchProviderError) throw lastError;
|
|
254
|
+
throw new SearchProviderError(
|
|
255
|
+
"duckduckgo",
|
|
256
|
+
`DuckDuckGo search failed after ${ATTEMPTS.length} attempts${
|
|
257
|
+
lastError instanceof Error ? `: ${lastError.message}` : ""
|
|
258
|
+
}`,
|
|
259
|
+
);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/** Keyless, permissionless web search provider backed by DuckDuckGo. */
|
|
263
|
+
export class DuckDuckGoProvider extends SearchProvider {
|
|
264
|
+
readonly id = "duckduckgo";
|
|
265
|
+
readonly label = "DuckDuckGo";
|
|
266
|
+
|
|
267
|
+
isAvailable(_authStorage: AuthStorage): boolean {
|
|
268
|
+
return true;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
search(params: SearchParams): Promise<SearchResponse> {
|
|
272
|
+
return searchDuckDuckGo({
|
|
273
|
+
query: params.query,
|
|
274
|
+
num_results: params.numSearchResults ?? params.limit,
|
|
275
|
+
recency: params.recency,
|
|
276
|
+
signal: params.signal,
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
}
|
package/src/web/search/types.ts
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
/** Supported web search providers */
|
|
8
8
|
export type SearchProviderId =
|
|
9
|
+
| "duckduckgo"
|
|
9
10
|
| "exa"
|
|
10
11
|
| "brave"
|
|
11
12
|
| "jina"
|
|
@@ -23,6 +24,7 @@ export type SearchProviderId =
|
|
|
23
24
|
|
|
24
25
|
export function isSearchProviderId(value: string): value is SearchProviderId {
|
|
25
26
|
return [
|
|
27
|
+
"duckduckgo",
|
|
26
28
|
"exa",
|
|
27
29
|
"brave",
|
|
28
30
|
"jina",
|
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"$schema": "https://raw.githubusercontent.com/can1357/gajae-code/main/packages/coding-agent/theme-schema.json",
|
|
3
|
-
"name": "dark",
|
|
4
|
-
"vars": {
|
|
5
|
-
"cyan": "#0088fa",
|
|
6
|
-
"blue": "#178fb9",
|
|
7
|
-
"green": "#89d281",
|
|
8
|
-
"red": "#fc3a4b",
|
|
9
|
-
"yellow": "#e4c00f",
|
|
10
|
-
"gray": "#777d88",
|
|
11
|
-
"dimGray": "#5f6673",
|
|
12
|
-
"darkGray": "#3d424a",
|
|
13
|
-
"accent": "#febc38",
|
|
14
|
-
"selectedBg": "#31363f",
|
|
15
|
-
"userMsgBg": "#221d1a",
|
|
16
|
-
"toolPendingBg": "#1d2129",
|
|
17
|
-
"toolSuccessBg": "#161a1f",
|
|
18
|
-
"toolErrorBg": "#291d1d",
|
|
19
|
-
"customMsgBg": "#2a2530"
|
|
20
|
-
},
|
|
21
|
-
"colors": {
|
|
22
|
-
"accent": "accent",
|
|
23
|
-
"border": "blue",
|
|
24
|
-
"borderAccent": "cyan",
|
|
25
|
-
"borderMuted": "darkGray",
|
|
26
|
-
"success": "green",
|
|
27
|
-
"error": "red",
|
|
28
|
-
"warning": "yellow",
|
|
29
|
-
"muted": "gray",
|
|
30
|
-
"dim": "dimGray",
|
|
31
|
-
"text": "",
|
|
32
|
-
"thinkingText": "gray",
|
|
33
|
-
"selectedBg": "selectedBg",
|
|
34
|
-
"userMessageBg": "userMsgBg",
|
|
35
|
-
"userMessageText": "",
|
|
36
|
-
"customMessageBg": "customMsgBg",
|
|
37
|
-
"customMessageText": "",
|
|
38
|
-
"customMessageLabel": "#b281d6",
|
|
39
|
-
"toolPendingBg": "toolPendingBg",
|
|
40
|
-
"toolSuccessBg": "toolSuccessBg",
|
|
41
|
-
"toolErrorBg": "toolErrorBg",
|
|
42
|
-
"toolTitle": "",
|
|
43
|
-
"toolOutput": "gray",
|
|
44
|
-
"mdHeading": "#febc38",
|
|
45
|
-
"mdLink": "#0088fa",
|
|
46
|
-
"mdLinkUrl": "dimGray",
|
|
47
|
-
"mdCode": "#e5c1ff",
|
|
48
|
-
"mdCodeBlock": "#9CDCFE",
|
|
49
|
-
"mdCodeBlockBorder": "darkGray",
|
|
50
|
-
"mdQuote": "gray",
|
|
51
|
-
"mdQuoteBorder": "darkGray",
|
|
52
|
-
"mdHr": "darkGray",
|
|
53
|
-
"mdListBullet": "accent",
|
|
54
|
-
"toolDiffAdded": "green",
|
|
55
|
-
"toolDiffRemoved": "red",
|
|
56
|
-
"toolDiffContext": "gray",
|
|
57
|
-
"link": "#0088fa",
|
|
58
|
-
"syntaxComment": "#6A9955",
|
|
59
|
-
"syntaxKeyword": "#569CD6",
|
|
60
|
-
"syntaxFunction": "#DCDCAA",
|
|
61
|
-
"syntaxVariable": "#9CDCFE",
|
|
62
|
-
"syntaxString": "#CE9178",
|
|
63
|
-
"syntaxNumber": "#B5CEA8",
|
|
64
|
-
"syntaxType": "#4EC9B0",
|
|
65
|
-
"syntaxOperator": "#D4D4D4",
|
|
66
|
-
"syntaxPunctuation": "#D4D4D4",
|
|
67
|
-
"thinkingOff": "darkGray",
|
|
68
|
-
"thinkingMinimal": "dimGray",
|
|
69
|
-
"thinkingLow": "#178fb9",
|
|
70
|
-
"thinkingMedium": "#0088fa",
|
|
71
|
-
"thinkingHigh": "#b281d6",
|
|
72
|
-
"thinkingXhigh": "#e5c1ff",
|
|
73
|
-
"bashMode": "cyan",
|
|
74
|
-
"statusLineBg": "#121212",
|
|
75
|
-
"statusLineSep": 244,
|
|
76
|
-
"statusLineModel": "#d787af",
|
|
77
|
-
"statusLinePath": "#00afaf",
|
|
78
|
-
"statusLineGitClean": "#5faf5f",
|
|
79
|
-
"statusLineGitDirty": "#d7af5f",
|
|
80
|
-
"statusLineContext": "#8787af",
|
|
81
|
-
"statusLineSpend": "#5fafaf",
|
|
82
|
-
"statusLineStaged": 70,
|
|
83
|
-
"statusLineDirty": 178,
|
|
84
|
-
"statusLineUntracked": 39,
|
|
85
|
-
"statusLineOutput": 205,
|
|
86
|
-
"statusLineCost": 205,
|
|
87
|
-
"statusLineSubagents": "accent",
|
|
88
|
-
"pythonMode": "yellow"
|
|
89
|
-
},
|
|
90
|
-
"export": {
|
|
91
|
-
"pageBg": "#18181e",
|
|
92
|
-
"cardBg": "#1e1e24",
|
|
93
|
-
"infoBg": "#3c3728"
|
|
94
|
-
}
|
|
95
|
-
}
|