mirror of
https://github.com/MoonshotAI/kimi-code.git
synced 2026-07-10 01:39:25 +00:00
refactor(agent-core-v2): move web to app, split WebSearch into auth
- Move the `web` domain from src/agent/web to src/app/web; it now owns only the auth-independent FetchURL (tool, UrlFetcher contract, and the local and Moonshot fetch providers). - Move WebSearch (tool, WebSearchProvider contract, MoonshotWebSearchProvider) into the KimiOAuth auth domain under src/app/auth/webSearch, since it needs an authenticated backend. - Replace the Agent-scope IAgentWebService registrar with registerTool contributions fed by new App-scope seams (IWebFetchService, IWebSearchProviderService), because the tool registry is Agent-scoped. - Add documented auth>tool / auth>toolRegistry layer exceptions and update the DI scope-domain graph.
This commit is contained in:
parent
ab64d48154
commit
3287b8fc0f
20 changed files with 220 additions and 124 deletions
|
|
@ -32,7 +32,8 @@ package "App scope (process-wide)" #EAF3FB {
|
|||
rectangle "<b>workspaceRegistry</b>\n<size:9><i>App</i></size>\n IWorkspaceRegistry" as workspaceRegistry #D6EAF8
|
||||
rectangle "<b>hostFolderBrowser</b>\n<size:9><i>App</i></size>\n IHostFolderBrowser" as hostFolderBrowser #D6EAF8
|
||||
rectangle "<b>hostEnvironment</b>\n<size:9><i>App</i></size>\n IHostEnvironment" as hostEnvironment #D6EAF8
|
||||
rectangle "<b>auth</b>\n<size:9><i>App</i></size>\n IOAuthService\n IAuthSummaryService" as auth #D6EAF8
|
||||
rectangle "<b>auth</b>\n<size:9><i>App</i></size>\n IOAuthService\n IAuthSummaryService\n IWebSearchProviderService" as auth #D6EAF8
|
||||
rectangle "<b>web</b>\n<size:9><i>App</i></size>\n IWebFetchService" as web #D6EAF8
|
||||
rectangle "<b>provider</b>\n<size:9><i>App</i></size>\n IProviderService" as provider #D6EAF8
|
||||
rectangle "<b>flag</b>\n<size:9><i>App</i></size>\n IFlagService\n IFlagRegistry" as flag #D6EAF8
|
||||
rectangle "<b>config</b>\n<size:9><i>App</i></size>\n IConfigRegistry\n IConfigService" as config #D6EAF8
|
||||
|
|
@ -337,6 +338,8 @@ shellTools --> process #34495E
|
|||
shellTools --> hostEnvironment
|
||||
shellTools --> execContext #34495E
|
||||
shellTools --> background #34495E
|
||||
web --> toolRegistry #34495E
|
||||
auth --> toolRegistry #34495E
|
||||
|
||||
' ---- event-driven (dashed) ----
|
||||
flag ..> config #16A085 : onDidChangeConfiguration
|
||||
|
|
|
|||
|
|
@ -56,16 +56,13 @@ const DOMAIN_LAYER = new Map([
|
|||
['bootstrap', 1],
|
||||
// `environment` is the App-scope resolved startup snapshot: host facts, the
|
||||
// app path layout, and the env bag; low-level substrate that any domain may
|
||||
// read for paths/facts, so it sits in L1 beside `bootstrap` and
|
||||
// `hostEnvironment`.
|
||||
// read for paths/facts, so it sits in L1 beside `bootstrap` and the
|
||||
// `os/interface` host facts.
|
||||
['environment', 1],
|
||||
// `event` is the App-scope pub/sub bus, a thin wrapper over the
|
||||
// `_base/event` `Emitter`. Foundational substrate that any domain may
|
||||
// publish/subscribe through, so it sits in L1 (not the edge boundary).
|
||||
['event', 1],
|
||||
// `hostEnvironment` is the App-scope OS/shell/path/home probe snapshot;
|
||||
// low-level substrate that any Session/Agent domain may read synchronously.
|
||||
['hostEnvironment', 1],
|
||||
// `execContext` is the Session-scope seeded immutable value (`cwd`,
|
||||
// `envLayers`); same layer as the other low-level bridges.
|
||||
['execContext', 1],
|
||||
|
|
@ -73,15 +70,15 @@ const DOMAIN_LAYER = new Map([
|
|||
// (`sessionId`/`workspaceId`/`sessionDir`/`metaScope`); like `execContext`
|
||||
// it is a pure seed with no IO, so it sits in L1.
|
||||
['sessionContext', 1],
|
||||
['hostFs', 1],
|
||||
// `git` is the App-scope `IGitService` that runs `git status` / `git diff`
|
||||
// against a local repo via `node:child_process`; it depends only on `_base`
|
||||
// against a local repo. Process spawning goes through `os/interface`
|
||||
// (`IHostProcessService`) and the lone path-existence probe through
|
||||
// `IHostFileSystem`; besides those host bridges it depends only on `_base`
|
||||
// and the `errors` facade, so it sits in L1 beside the other host bridges.
|
||||
['git', 1],
|
||||
['workspaceContext', 1],
|
||||
['protocol', 1],
|
||||
['hooks', 1],
|
||||
['storage', 1],
|
||||
// `task` is the managed-concurrent-execution primitive (run + defer).
|
||||
// Depends only on `_base`; sits in L1 beside the other program-control
|
||||
// layer substrates.
|
||||
|
|
@ -263,10 +260,17 @@ const ALLOWED_EXCEPTIONS = new Set([
|
|||
'bootstrap>globalSkillCatalog',
|
||||
// bootstrap is the composition root — it wires backends by design.
|
||||
'bootstrap>persistence/backends',
|
||||
// `auth` (KimiOAuth, L2) owns the OAuth-backed `WebSearch` tool and registers
|
||||
// it through the tool contribution API, so it reaches up to the L3 tool
|
||||
// contract and registry. Surfaced for review: the tool needs an authenticated
|
||||
// backend, which is why it lives beside the OAuth toolkit rather than in the
|
||||
// auth-independent `web` domain.
|
||||
'auth>tool',
|
||||
'auth>toolRegistry',
|
||||
// path-access (base tool policy) needs the `IHostEnvironment` type to stay
|
||||
// host-aware (path class, home dir). Structural type dependency only —
|
||||
// path-access does not construct or resolve the service.
|
||||
'_base>hostEnvironment',
|
||||
'_base>os/interface',
|
||||
'permissionGate>approval',
|
||||
'userTool>interaction',
|
||||
'permissionPolicy>plan',
|
||||
|
|
@ -310,13 +314,7 @@ const ALLOWED_EXCEPTIONS = new Set([
|
|||
'wireRecord>contextMemory',
|
||||
'wireRecord>loop',
|
||||
'wireRecord>tool',
|
||||
// Compatibility barrels: old domain barrels (L1-L2) re-export from the new
|
||||
// persistence/os backends directories. These are pure re-export shims and
|
||||
// will be removed once all consumers migrate to canonical paths.
|
||||
'hostEnvironment>os/backends',
|
||||
'hostFs>os/backends',
|
||||
'hostFolderBrowser>os/backends',
|
||||
'storage>persistence/backends',
|
||||
'filestore>persistence/backends',
|
||||
'process>os/backends',
|
||||
'terminal>os/backends',
|
||||
|
|
|
|||
|
|
@ -1,8 +0,0 @@
|
|||
/**
|
||||
* `web` domain barrel — re-exports the web contract (`web`) and its scoped
|
||||
* service (`webService`). Importing this barrel registers the `IAgentWebService`
|
||||
* binding into the scope registry.
|
||||
*/
|
||||
|
||||
export * from './web';
|
||||
export * from './webService';
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
/**
|
||||
* `web` domain (L4) — web tool registration contract and provider options.
|
||||
*
|
||||
* `IAgentWebService` is a marker: its implementation registers the built-in
|
||||
* `FetchURL` tool (always, falling back to `LocalFetchURLProvider`) and the
|
||||
* `WebSearch` tool (only when a `WebSearchProvider` is supplied) into the
|
||||
* agent `IAgentToolRegistryService` on construction. Bound at Agent scope.
|
||||
*
|
||||
* The actual fetch/search backends are host-injected through
|
||||
* `WebServiceOptions` so this domain stays independent of config and OAuth.
|
||||
*/
|
||||
|
||||
import { createDecorator, type ServiceIdentifier } from '#/_base/di/instantiation';
|
||||
|
||||
import type { UrlFetcher } from '#/agent/web/tools/fetch-url';
|
||||
import type { WebSearchProvider } from '#/agent/web/tools/web-search';
|
||||
|
||||
export interface WebServiceOptions {
|
||||
/** URL fetch backend. Defaults to the built-in `LocalFetchURLProvider`. */
|
||||
readonly urlFetcher?: UrlFetcher;
|
||||
/** Web search backend. When omitted, `WebSearch` is not registered. */
|
||||
readonly webSearcher?: WebSearchProvider;
|
||||
}
|
||||
|
||||
export interface IAgentWebService {
|
||||
readonly _serviceBrand: undefined;
|
||||
}
|
||||
|
||||
export const IAgentWebService: ServiceIdentifier<IAgentWebService> =
|
||||
createDecorator<IAgentWebService>('agentWebService');
|
||||
|
|
@ -1,53 +0,0 @@
|
|||
/**
|
||||
* `web` domain (L4) — `IAgentWebService` implementation.
|
||||
*
|
||||
* Eager Agent-scope registration service for the built-in web tools: `FetchURL`
|
||||
* is always registered (using the host-injected `UrlFetcher` or the built-in
|
||||
* `LocalFetchURLProvider` fallback); `WebSearch` is registered only when a
|
||||
* `WebSearchProvider` is supplied via options. Each tool is created via
|
||||
* `IInstantiationService.createInstance` (the provider is passed as a leading
|
||||
* static argument) and registered into the agent `IAgentToolRegistryService`.
|
||||
* Eager so the tools are registered when the Agent scope is created.
|
||||
*/
|
||||
|
||||
import { InstantiationType } from '#/_base/di/extensions';
|
||||
import { IInstantiationService } from '#/_base/di/instantiation';
|
||||
import { Disposable } from '#/_base/di/lifecycle';
|
||||
import { LifecycleScope, registerScopedService } from '#/_base/di/scope';
|
||||
import { IAgentToolRegistryService } from '#/agent/toolRegistry';
|
||||
|
||||
import { LocalFetchURLProvider } from '#/agent/web/providers/local-fetch-url';
|
||||
import { FetchURLTool } from '#/agent/web/tools/fetch-url';
|
||||
import { WebSearchTool } from '#/agent/web/tools/web-search';
|
||||
import { IAgentWebService, type WebServiceOptions } from './web';
|
||||
|
||||
export class AgentWebService extends Disposable implements IAgentWebService {
|
||||
declare readonly _serviceBrand: undefined;
|
||||
|
||||
constructor(
|
||||
private readonly options: WebServiceOptions = {},
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
||||
@IAgentToolRegistryService toolRegistry: IAgentToolRegistryService,
|
||||
) {
|
||||
super();
|
||||
const fetcher = options.urlFetcher ?? new LocalFetchURLProvider();
|
||||
this._register(
|
||||
toolRegistry.register(instantiationService.createInstance(FetchURLTool, fetcher)),
|
||||
);
|
||||
if (options.webSearcher !== undefined) {
|
||||
this._register(
|
||||
toolRegistry.register(
|
||||
instantiationService.createInstance(WebSearchTool, options.webSearcher),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
registerScopedService(
|
||||
LifecycleScope.Agent,
|
||||
IAgentWebService,
|
||||
AgentWebService,
|
||||
InstantiationType.Eager,
|
||||
'web',
|
||||
);
|
||||
|
|
@ -1,8 +1,17 @@
|
|||
/**
|
||||
* `auth` domain barrel — re-exports the auth contract (`auth`) and its scoped
|
||||
* services (`authService`). Importing this barrel registers the `IOAuthService`
|
||||
* and `IAuthSummaryService` bindings into the scope registry.
|
||||
* `auth` domain barrel — re-exports the auth contract (`auth`), its scoped
|
||||
* services (`authService`), the OAuth-backed web-search seam (`webSearch`),
|
||||
* and a side-effect import of the `WebSearch` tool so its `registerTool(...)`
|
||||
* call runs at module load. Importing this barrel registers the
|
||||
* `IOAuthService`, `IAuthSummaryService`, and `IWebSearchProviderService`
|
||||
* bindings into the scope registry and conditionally adds `WebSearch` to the
|
||||
* tool contribution list.
|
||||
*/
|
||||
|
||||
import './webSearch/tools/web-search';
|
||||
|
||||
export * from './auth';
|
||||
export * from './authService';
|
||||
export * from './webSearch/webSearch';
|
||||
export * from './webSearch/webSearchService';
|
||||
export * from './webSearch/providers/moonshot-web-search';
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import type { WebSearchProvider, WebSearchResult } from '#/agent/web/tools/web-search';
|
||||
import type { WebSearchProvider, WebSearchResult } from '../tools/web-search';
|
||||
|
||||
export interface BearerTokenProvider {
|
||||
getAccessToken(options?: { readonly force?: boolean | undefined }): Promise<string>;
|
||||
|
|
@ -1,9 +1,14 @@
|
|||
/**
|
||||
* WebSearchTool — host-injected web search.
|
||||
* `auth` domain (cross-cutting) — `WebSearch` builtin tool and its
|
||||
* `WebSearchProvider` contract.
|
||||
*
|
||||
* agent-core-v2 defines the interface; the host provides the real search
|
||||
* implementation via `WebSearchProvider`. If no provider is supplied,
|
||||
* the tool is not registered (there is no local search backend).
|
||||
* Defines the `WebSearch` tool and the host-injected `WebSearchProvider`
|
||||
* interface (plus `WebSearchResult`). Web search needs an authenticated
|
||||
* Moonshot backend, so the tool lives in the KimiOAuth `auth` domain: it reads
|
||||
* its provider from the App-scope `IWebSearchProviderService` at
|
||||
* registry-construction time and self-registers via `registerTool(...)` at
|
||||
* module load, but only when a provider is configured (there is no local
|
||||
* search backend).
|
||||
*/
|
||||
|
||||
import { z } from 'zod';
|
||||
|
|
@ -18,7 +23,9 @@ import type {
|
|||
} from '#/agent/tool';
|
||||
import { ToolAccesses } from '#/agent/tool';
|
||||
import { ToolResultBuilder } from '#/agent/tool/result-builder';
|
||||
import { registerTool } from '#/agent/toolRegistry';
|
||||
|
||||
import { IWebSearchProviderService } from '../webSearch';
|
||||
import DESCRIPTION from './web-search.md?raw';
|
||||
|
||||
// ── Provider interface (host-injected) ───────────────────────────────
|
||||
|
|
@ -172,3 +179,14 @@ function classifySearchError(error: unknown): string {
|
|||
}
|
||||
return `Search failed: ${message}`;
|
||||
}
|
||||
|
||||
registerTool(WebSearchTool, {
|
||||
when: (accessor) => accessor.get(IWebSearchProviderService).getWebSearchProvider() !== undefined,
|
||||
staticArgs: (accessor) => {
|
||||
const provider = accessor.get(IWebSearchProviderService).getWebSearchProvider();
|
||||
if (provider === undefined) {
|
||||
throw new Error('WebSearchProviderService returned no provider during tool registration.');
|
||||
}
|
||||
return [provider];
|
||||
},
|
||||
});
|
||||
31
packages/agent-core-v2/src/app/auth/webSearch/webSearch.ts
Normal file
31
packages/agent-core-v2/src/app/auth/webSearch/webSearch.ts
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
/**
|
||||
* `auth` domain (cross-cutting) — OAuth-backed web search seam.
|
||||
*
|
||||
* Owns the host-injection seam for the `WebSearch` backend. `WebSearch` needs
|
||||
* an authenticated Moonshot search provider, so it lives here beside the OAuth
|
||||
* toolkit rather than in the auth-independent `web` domain.
|
||||
* `IWebSearchProviderService` yields the configured `WebSearchProvider` (or
|
||||
* `undefined` when search is not configured, in which case the `WebSearch` tool
|
||||
* is not registered). The host builds a `MoonshotWebSearchProvider` from the
|
||||
* OAuth token (resolved through `IOAuthService`) and binds it here. Bound at
|
||||
* App scope.
|
||||
*/
|
||||
|
||||
import { createDecorator, type ServiceIdentifier } from '#/_base/di/instantiation';
|
||||
|
||||
import type { WebSearchProvider } from './tools/web-search';
|
||||
|
||||
export type { WebSearchProvider, WebSearchResult } from './tools/web-search';
|
||||
|
||||
export interface WebSearchProviderOptions {
|
||||
/** Search backend. When omitted, `WebSearch` is not registered. */
|
||||
readonly provider?: WebSearchProvider;
|
||||
}
|
||||
|
||||
export interface IWebSearchProviderService {
|
||||
readonly _serviceBrand: undefined;
|
||||
getWebSearchProvider(): WebSearchProvider | undefined;
|
||||
}
|
||||
|
||||
export const IWebSearchProviderService: ServiceIdentifier<IWebSearchProviderService> =
|
||||
createDecorator<IWebSearchProviderService>('webSearchProviderService');
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
/**
|
||||
* `auth` domain (cross-cutting) — `IWebSearchProviderService` implementation.
|
||||
*
|
||||
* Holds the host-injected `WebSearchProvider` (a `MoonshotWebSearchProvider`
|
||||
* the host builds from the OAuth token) and exposes it to the `WebSearch` tool
|
||||
* through `IWebSearchProviderService`. Returns `undefined` when no provider is
|
||||
* bound so the `WebSearch` tool is skipped. Owns no tool registration — the
|
||||
* `WebSearch` tool self-registers via `registerTool(...)` and reads this
|
||||
* service from the Agent-scope accessor. Bound at App scope.
|
||||
*/
|
||||
|
||||
import { InstantiationType } from '#/_base/di/extensions';
|
||||
import { LifecycleScope, registerScopedService } from '#/_base/di/scope';
|
||||
|
||||
import type { WebSearchProvider } from './tools/web-search';
|
||||
import { IWebSearchProviderService, type WebSearchProviderOptions } from './webSearch';
|
||||
|
||||
export class WebSearchProviderService implements IWebSearchProviderService {
|
||||
declare readonly _serviceBrand: undefined;
|
||||
private readonly provider: WebSearchProvider | undefined;
|
||||
|
||||
constructor(options: WebSearchProviderOptions = {}) {
|
||||
this.provider = options.provider;
|
||||
}
|
||||
|
||||
getWebSearchProvider(): WebSearchProvider | undefined {
|
||||
return this.provider;
|
||||
}
|
||||
}
|
||||
|
||||
registerScopedService(
|
||||
LifecycleScope.App,
|
||||
IWebSearchProviderService,
|
||||
WebSearchProviderService,
|
||||
InstantiationType.Delayed,
|
||||
'auth',
|
||||
);
|
||||
14
packages/agent-core-v2/src/app/web/index.ts
Normal file
14
packages/agent-core-v2/src/app/web/index.ts
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
/**
|
||||
* `web` domain barrel — re-exports the web contract (`web`), its scoped
|
||||
* service (`webService`), the fetch providers, and a side-effect import of the
|
||||
* `FetchURL` tool so its `registerTool(...)` call runs at module load.
|
||||
* Importing this barrel registers the `IWebFetchService` binding into the scope
|
||||
* registry and adds `FetchURL` to the tool contribution list.
|
||||
*/
|
||||
|
||||
import './tools/fetch-url';
|
||||
|
||||
export * from './web';
|
||||
export * from './webService';
|
||||
export * from './providers/local-fetch-url';
|
||||
export * from './providers/moonshot-fetch-url';
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
import { Readability } from '@mozilla/readability';
|
||||
import { parseHTML as rawParseHTML } from 'linkedom';
|
||||
|
||||
import { HttpFetchError, type UrlFetcher, type UrlFetchResult } from '#/agent/web/tools/fetch-url';
|
||||
import { HttpFetchError, type UrlFetcher, type UrlFetchResult } from '../tools/fetch-url';
|
||||
|
||||
// Readability's .d.ts references the global `Document` type, but this
|
||||
// package compiles with `lib: ES2023` (no DOM). Extracting the
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
import { HttpFetchError, type UrlFetcher, type UrlFetchResult } from '#/agent/web/tools/fetch-url';
|
||||
import { HttpFetchError, type UrlFetcher, type UrlFetchResult } from '../tools/fetch-url';
|
||||
|
||||
export interface BearerTokenProvider {
|
||||
getAccessToken(options?: { readonly force?: boolean | undefined }): Promise<string>;
|
||||
|
|
@ -1,9 +1,12 @@
|
|||
/**
|
||||
* FetchURLTool — host-injected URL fetcher.
|
||||
* `web` domain (L4) — `FetchURL` builtin tool and its `UrlFetcher` contract.
|
||||
*
|
||||
* agent-core-v2 defines the interface; the host provides the real fetch
|
||||
* implementation via `UrlFetcher`. If no fetcher is supplied, the tool
|
||||
* falls back to the built-in `LocalFetchURLProvider`.
|
||||
* Defines the `FetchURL` tool and the host-injected `UrlFetcher` interface
|
||||
* (plus `UrlFetchResult` / `HttpFetchError`). The tool reads its fetcher from
|
||||
* the App-scope `IWebFetchService` at registry-construction time and
|
||||
* self-registers via `registerTool(...)` at module load; the default service
|
||||
* falls back to the built-in `LocalFetchURLProvider`, so `FetchURL` is always
|
||||
* available without OAuth.
|
||||
*/
|
||||
|
||||
import { z } from 'zod';
|
||||
|
|
@ -18,7 +21,9 @@ import type {
|
|||
} from '#/agent/tool';
|
||||
import { ToolAccesses } from '#/agent/tool';
|
||||
import { ToolResultBuilder } from '#/agent/tool/result-builder';
|
||||
import { registerTool } from '#/agent/toolRegistry';
|
||||
|
||||
import { IWebFetchService } from '../web';
|
||||
import DESCRIPTION from './fetch-url.md?raw';
|
||||
|
||||
// ── Provider interface (host-injected) ───────────────────────────────
|
||||
|
|
@ -135,3 +140,7 @@ export class FetchURLTool implements BuiltinTool<FetchURLInput> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
registerTool(FetchURLTool, {
|
||||
staticArgs: (accessor) => [accessor.get(IWebFetchService).getUrlFetcher()],
|
||||
});
|
||||
30
packages/agent-core-v2/src/app/web/web.ts
Normal file
30
packages/agent-core-v2/src/app/web/web.ts
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
/**
|
||||
* `web` domain (L4) — auth-independent URL fetching.
|
||||
*
|
||||
* Owns the built-in `FetchURL` tool and the host-injection seam for its fetch
|
||||
* backend. `IWebFetchService` yields the `UrlFetcher` the `FetchURL` tool uses;
|
||||
* the default implementation falls back to the built-in `LocalFetchURLProvider`,
|
||||
* so `FetchURL` works without any OAuth configuration. The `MoonshotFetchURLProvider`
|
||||
* is exported as a building block for hosts that want to route fetches through
|
||||
* the Moonshot fetch service. Bound at App scope.
|
||||
*/
|
||||
|
||||
import { createDecorator, type ServiceIdentifier } from '#/_base/di/instantiation';
|
||||
|
||||
import type { UrlFetcher } from './tools/fetch-url';
|
||||
|
||||
export type { UrlFetcher, UrlFetchKind, UrlFetchResult } from './tools/fetch-url';
|
||||
export { HttpFetchError } from './tools/fetch-url';
|
||||
|
||||
export interface WebFetchServiceOptions {
|
||||
/** URL fetch backend. Defaults to the built-in `LocalFetchURLProvider`. */
|
||||
readonly urlFetcher?: UrlFetcher;
|
||||
}
|
||||
|
||||
export interface IWebFetchService {
|
||||
readonly _serviceBrand: undefined;
|
||||
getUrlFetcher(): UrlFetcher;
|
||||
}
|
||||
|
||||
export const IWebFetchService: ServiceIdentifier<IWebFetchService> =
|
||||
createDecorator<IWebFetchService>('webFetchService');
|
||||
37
packages/agent-core-v2/src/app/web/webService.ts
Normal file
37
packages/agent-core-v2/src/app/web/webService.ts
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
/**
|
||||
* `web` domain (L4) — `IWebFetchService` implementation.
|
||||
*
|
||||
* Holds the host-injected `UrlFetcher` (defaulting to the built-in
|
||||
* `LocalFetchURLProvider`) and hands it to the `FetchURL` tool through
|
||||
* `IWebFetchService`. Owns no tool registration of its own — the `FetchURL`
|
||||
* tool self-registers via `registerTool(...)` and reads this service from the
|
||||
* Agent-scope accessor. Bound at App scope.
|
||||
*/
|
||||
|
||||
import { InstantiationType } from '#/_base/di/extensions';
|
||||
import { LifecycleScope, registerScopedService } from '#/_base/di/scope';
|
||||
|
||||
import { LocalFetchURLProvider } from './providers/local-fetch-url';
|
||||
import type { UrlFetcher } from './tools/fetch-url';
|
||||
import { IWebFetchService, type WebFetchServiceOptions } from './web';
|
||||
|
||||
export class WebFetchService implements IWebFetchService {
|
||||
declare readonly _serviceBrand: undefined;
|
||||
private readonly urlFetcher: UrlFetcher;
|
||||
|
||||
constructor(options: WebFetchServiceOptions = {}) {
|
||||
this.urlFetcher = options.urlFetcher ?? new LocalFetchURLProvider();
|
||||
}
|
||||
|
||||
getUrlFetcher(): UrlFetcher {
|
||||
return this.urlFetcher;
|
||||
}
|
||||
}
|
||||
|
||||
registerScopedService(
|
||||
LifecycleScope.App,
|
||||
IWebFetchService,
|
||||
WebFetchService,
|
||||
InstantiationType.Delayed,
|
||||
'web',
|
||||
);
|
||||
|
|
@ -74,6 +74,7 @@ export * from '#/persistence/backends/memory';
|
|||
export * from '#/app/auth';
|
||||
export * from '#/app/authLegacy';
|
||||
export * from '#/app/file';
|
||||
export * from '#/app/web';
|
||||
|
||||
// Ported agent services. These keep the current service boundaries during the migration.
|
||||
export * from '#/agent/blob';
|
||||
|
|
|
|||
|
|
@ -10,8 +10,8 @@
|
|||
import { describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import type { ExecutableToolContext, ExecutableToolResult, ToolExecution } from '#/agent/tool';
|
||||
import { LocalFetchURLProvider } from '#/agent/web/providers/local-fetch-url';
|
||||
import { FetchURLTool, type UrlFetcher, type UrlFetchResult } from '#/agent/web/tools/fetch-url';
|
||||
import { LocalFetchURLProvider } from '#/app/web/providers/local-fetch-url';
|
||||
import { FetchURLTool, type UrlFetcher, type UrlFetchResult } from '#/app/web/tools/fetch-url';
|
||||
|
||||
function isPromiseLike(value: ToolExecution | Promise<ToolExecution>): value is Promise<ToolExecution> {
|
||||
return typeof (value as Promise<ToolExecution>).then === 'function';
|
||||
|
|
@ -25,7 +25,7 @@ async function execute(
|
|||
const resolved = tool.resolveExecution({ url });
|
||||
const execution = isPromiseLike(resolved) ? await resolved : resolved;
|
||||
if (execution.isError === true) return execution;
|
||||
const ctx: ExecutableToolContext = { turnId: '0', toolCallId: 'call_fetch', signal };
|
||||
const ctx: ExecutableToolContext = { turnId: 0, toolCallId: 'call_fetch', signal };
|
||||
return execution.execute(ctx);
|
||||
}
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue