mirror of
https://github.com/MoonshotAI/kimi-code.git
synced 2026-08-19 13:45:28 +00:00
refactor(agent-core-v2): extract externalHooks into a scope-organized feature (#2805)
Move the external hook services out of app/externalHooksRunner, session/externalHooks, and agent/externalHooks into features/externalHooks, assembled as the ExternalHooksFeature unit: - services live under per-scope subdirectories (app/, session/, agent/); shared pure helpers (types, hook matching/dispatch, process spawn, prompt result rendering) live under internal/ - the runner and the two observers are contributed through the Feature seams (ScopeUnits materialization); the hooks config section stays on the static import=register channel - update the package entry leaf exports, the plugin domain imports, the kap-server events-zod import, and the affected tests; regenerate the state manifest
This commit is contained in:
parent
cb8a7e5f81
commit
16499408d5
36 changed files with 186 additions and 96 deletions
|
|
@ -65,7 +65,7 @@ import type {
|
|||
ToolCallDelta,
|
||||
} from '@moonshot-ai/agent-core-v2/agent/loop/turnEvents';
|
||||
import type { TurnStepRetrying } from '@moonshot-ai/agent-core-v2/agent/stepRetry/stepRetryService';
|
||||
import type { HookResult } from '@moonshot-ai/agent-core-v2/agent/externalHooks/externalHooksService';
|
||||
import type { HookResult } from '@moonshot-ai/agent-core-v2/features/externalHooks/agent/agentExternalHooksService';
|
||||
import type {
|
||||
ToolCallStarted,
|
||||
ToolProgress,
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ The DI kernel (`src/_base/di/`) owns the unit layer on top of the scoped registr
|
|||
|
||||
The four contribution seams (token → fold): config sections — `ConfigSectionContribution` → `ConfigRegistry` fold (`src/app/config/`; module-level `registerConfigSection` stays the static built-in channel drained at construction, a withdrawn runtime record unregisters the section while user TOML values survive); agent tools — `AgentToolContribution` → `AgentToolActivationService` fold (built-in records provided once at App scope by `builtinToolAssemblyService`; `registerAgentToolService` stays the static channel: Agent-scope DI `OnDemand` registration + module table); agent profiles — `AgentProfileContribution` → `IAgentProfileRegistry` fold (see Scopes); event/state vocabulary — `EventStateContribution` → `IEventDispatcher` fold (a record bundles `events`; `registerEvent2Class` stays the static channel drained at fold time, while `defineState(...).replayable(...)` keys are explicit owner-service contributions: each replayable key's owning service contributes it via `contributeState` at construction — Agent-scope owners are eager, the session-domain todo/cron/interaction keys bridge through `agentLifecycle.onDidCreate` before `restore()` — and replaying a withdrawn domain's history lands on the unknown-type skip-and-count path). A fifth seam: executable commands — `CommandContribution` → `IAgentCommandService` fold (`src/agent/command/`; a contributed command runs engine-side — `run(ctx)` gets `ctx.get` resolving through the agent container, valid only during the synchronous part of `run`; name-level dedup, last record wins; surfaced over RPC as `agentCommandService.list` / `run`).
|
||||
|
||||
`src/features/` — built-in capabilities authored as self-contained Feature units (`plan` was the first, extracted from `agent/plan` + `agent/tools/plan`; `swarm` followed, extracted from `agent/swarm` + `session/swarm` + `agent/tools/agent-swarm` into a scope-organized `agent/` + `session/` + `tools/` layout; `tower` lives here as `features/tower/` — protocol store, rate limit, tower-mode service, eleven `Tower*` tools, the `tower-worker` profile, and the `/tower` skill body). A `Feature` (`src/features/feature.ts`) is an App-scope unit recipe with a `static override readonly name` and `contribute*` helpers composing the seams: `contributeService(scope, id, ctor)` / `contributeAgentService` (per-scope materialization via `ScopeUnits` — provider death retracts everywhere, 连坐), `contributeTool` (per-agent `OnDemand` registration + the `AgentToolContribution` record), `contributeProfiles`, `contributeConfig`, `contributeCommand`, plus `onDispose`. Feature modules self-register at import (`registerFeature`, `src/features/featureRegistry.ts`); the App-scope `IFeatureAssemblyService` drains the table through `IFeatureManager.provideUnit`, so every feature is a named, introspectable, retractable managed unit. Built-in features keep user-facing static contracts — config sections, agent profiles, wire vocabulary — on the static import=register channels (the config/state manifest generators read static tables / call sites; wire records must stay replayable); the Feature unit carries the runtime capabilities (services, tools, commands). The string form of the unit `on(...)` capability (`this.on('turn.ended', …)`) is backed by the production `FiberEventResolver` registered in `src/app/event/fiberEventResolver.ts`, resolving against the scope's `IEventBus`.
|
||||
`src/features/` — built-in capabilities authored as self-contained Feature units (`plan` was the first, extracted from `agent/plan` + `agent/tools/plan`; `externalHooks` from `app/externalHooksRunner` + `session/externalHooks` + `agent/externalHooks`; `swarm` followed, extracted from `agent/swarm` + `session/swarm` + `agent/tools/agent-swarm` into a scope-organized `agent/` + `session/` + `tools/` layout; `tower` lives here as `features/tower/` — protocol store, rate limit, tower-mode service, eleven `Tower*` tools, the `tower-worker` profile, and the `/tower` skill body). A `Feature` (`src/features/feature.ts`) is an App-scope unit recipe with a `static override readonly name` and `contribute*` helpers composing the seams: `contributeService(scope, id, ctor)` / `contributeAgentService` (per-scope materialization via `ScopeUnits` — provider death retracts everywhere, 连坐), `contributeTool` (per-agent `OnDemand` registration + the `AgentToolContribution` record), `contributeProfiles`, `contributeConfig`, `contributeCommand`, plus `onDispose`. Feature modules self-register at import (`registerFeature`, `src/features/featureRegistry.ts`); the App-scope `IFeatureAssemblyService` drains the table through `IFeatureManager.provideUnit`, so every feature is a named, introspectable, retractable managed unit. Built-in features keep user-facing static contracts — config sections, agent profiles, wire vocabulary — on the static import=register channels (the config/state manifest generators read static tables / call sites; wire records must stay replayable); the Feature unit carries the runtime capabilities (services, tools, commands). The string form of the unit `on(...)` capability (`this.on('turn.ended', …)`) is backed by the production `FiberEventResolver` registered in `src/app/event/fiberEventResolver.ts`, resolving against the scope's `IEventBus`.
|
||||
|
||||
## Ledger and cascade (L0/L2)
|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
# experimental src/app/flag/flag.ts
|
||||
# extraAgentDirs src/workspace/workspaceAgentProfileLoader/configSection.ts
|
||||
# extraSkillDirs src/app/skillCatalog/configSection.ts
|
||||
# hooks src/agent/externalHooks/configSection.ts
|
||||
# hooks src/features/externalHooks/configSection.ts
|
||||
# identity src/app/agentIdentity/configSection.ts
|
||||
# image src/agent/media/configSection.ts
|
||||
# loopControl src/agent/loop/configSection.ts
|
||||
|
|
@ -135,7 +135,7 @@ extra_skill_dirs = []
|
|||
|
||||
# ##########################################################################
|
||||
# hooks
|
||||
# owner: src/agent/externalHooks/configSection.ts
|
||||
# owner: src/features/externalHooks/configSection.ts
|
||||
# scope: core
|
||||
# hooks: custom fromToml · custom toToml
|
||||
# ##########################################################################
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@
|
|||
// contextProjector.lastRepairSignature src/agent/contextProjector/contextProjectorService.ts
|
||||
// cron src/session/cron/cronOps.ts
|
||||
// dateChange.seed src/features/dateChange/dateChangeService.ts
|
||||
// externalHooks.stopHookContinuationUsed src/agent/externalHooks/externalHooksService.ts
|
||||
// externalHooks.stopHookContinuationUsed src/features/externalHooks/agent/agentExternalHooksService.ts
|
||||
// fullCompaction src/agent/fullCompaction/compactionOps.ts
|
||||
// fullCompaction.activeTurnId src/agent/fullCompaction/fullCompactionService.ts
|
||||
// fullCompaction.compactionCountInTurn src/agent/fullCompaction/fullCompactionService.ts
|
||||
|
|
@ -1188,8 +1188,6 @@ export interface AgentStateSnapshot {
|
|||
})[];
|
||||
// src/agent/contextProjector/contextProjectorService.ts
|
||||
'contextProjector.lastRepairSignature': string | null;
|
||||
// src/agent/externalHooks/externalHooksService.ts
|
||||
'externalHooks.stopHookContinuationUsed': boolean;
|
||||
// src/agent/fullCompaction/compactionOps.ts
|
||||
// replayable · durable — folds: FullCompactionBegin, FullCompactionCancel, FullCompactionComplete
|
||||
'fullCompaction': /* CompactionState — packages/agent-core-v2/src/agent/fullCompaction/compactionOps.ts */ {
|
||||
|
|
@ -1575,6 +1573,8 @@ export interface AgentStateSnapshot {
|
|||
readonly timeZone: string;
|
||||
readonly renderGeneration: number;
|
||||
} | undefined;
|
||||
// src/features/externalHooks/agent/agentExternalHooksService.ts
|
||||
'externalHooks.stopHookContinuationUsed': boolean;
|
||||
// src/features/plan/injection/planModeInjection.ts
|
||||
'plan.wasActive': boolean;
|
||||
// src/features/plan/planOps.ts
|
||||
|
|
|
|||
|
|
@ -1,2 +0,0 @@
|
|||
export * from './externalHooksRunner';
|
||||
export * from './externalHooksRunnerService';
|
||||
|
|
@ -3,7 +3,7 @@ import { tmpdir } from 'node:os';
|
|||
import path from 'node:path';
|
||||
|
||||
import { BugIndicatingError, Error2, ErrorCodes, PluginErrors } from '#/errors';
|
||||
import type { HookDef } from '#/agent/externalHooks/types';
|
||||
import type { HookDef } from '#/features/externalHooks/internal/types';
|
||||
import type { McpServerConfig } from '#/mcpCore/config-schema';
|
||||
import type { PluginAgentRoot } from './types';
|
||||
import { discoverFileSkills } from '#/app/skillCatalog/fileSkillDiscovery';
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { readdir, readFile, realpath, stat } from 'node:fs/promises';
|
||||
import path from 'node:path';
|
||||
|
||||
import { HookDefSchema, type HookDefConfig } from '#/agent/externalHooks/configSection';
|
||||
import { HookDefSchema, type HookDefConfig } from '#/features/externalHooks/configSection';
|
||||
import { McpServerConfigSchema, type McpServerConfig } from '#/mcpCore/config-schema';
|
||||
|
||||
import {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { createDecorator, type ServiceIdentifier } from '#/_base/di/instantiation';
|
||||
import type { Event } from '#/_base/event';
|
||||
import type { HookDef } from '#/agent/externalHooks/types';
|
||||
import type { HookDef } from '#/features/externalHooks/internal/types';
|
||||
import type { McpServerConfig } from '#/mcpCore/config-schema';
|
||||
import type { SkillRoot } from '#/app/skillCatalog/types';
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import { BugIndicatingError, Error2, PluginErrors } from '#/errors';
|
|||
import { IBootstrapService } from '#/app/bootstrap/bootstrap';
|
||||
import { IProviderService } from '#/kosong/provider/provider';
|
||||
import { ISkillDiscovery } from '#/app/skillCatalog/skillDiscovery';
|
||||
import type { HookDef } from '#/agent/externalHooks/types';
|
||||
import type { HookDef } from '#/features/externalHooks/internal/types';
|
||||
import type { McpServerConfig } from '#/mcpCore/config-schema';
|
||||
import type { SkillRoot } from '#/app/skillCatalog/types';
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import type { HookDefConfig } from '#/agent/externalHooks/configSection';
|
||||
import type { HookDefConfig } from '#/features/externalHooks/configSection';
|
||||
import type { McpServerConfig } from '#/mcpCore/config-schema';
|
||||
|
||||
export type PluginDiagnosticSeverity = 'error' | 'warn' | 'info';
|
||||
|
|
|
|||
|
|
@ -1,11 +1,5 @@
|
|||
import { createDecorator } from '#/_base/di/instantiation';
|
||||
|
||||
export interface RenderedExternalHookResult {
|
||||
readonly event: string;
|
||||
readonly message: string;
|
||||
readonly text: string;
|
||||
}
|
||||
|
||||
export interface IAgentExternalHooksService {
|
||||
readonly _serviceBrand: undefined;
|
||||
}
|
||||
|
|
@ -1,8 +1,6 @@
|
|||
/* oxlint-disable typescript-eslint/no-unsafe-declaration-merging, eslint-plugin-import/namespace -- Event2 class+payload-interface declaration merging is the sanctioned event-declaration idiom. */
|
||||
import { IInstantiationService } from '#/_base/di/instantiation';
|
||||
import { Service } from '#/_base/di/service';
|
||||
import { LifecycleScope } from '#/app/scopes';
|
||||
import { ScopeActivation, registerScopedService } from '#/_base/di/scope';
|
||||
import { defineState } from '#/state/state';
|
||||
import { isPlainRecord } from '#/_base/utils/canonical-args';
|
||||
import { IAgentStateService } from '#/agent/state/agentState';
|
||||
|
|
@ -39,13 +37,13 @@ import { ISessionContext } from '#/session/sessionContext/sessionContext';
|
|||
import { ISessionMetadata } from '#/session/sessionMetadata/sessionMetadata';
|
||||
import { IEventDispatcher } from '#/state/eventDispatcher';
|
||||
|
||||
import { IAgentExternalHooksService } from './externalHooks';
|
||||
import { IExternalHooksRunnerService } from '#/app/externalHooksRunner/externalHooksRunner';
|
||||
import type { HookMatcherValue } from './types';
|
||||
import { IAgentExternalHooksService } from './agentExternalHooks';
|
||||
import { IExternalHooksRunnerService } from '../app/externalHooksRunner';
|
||||
import type { HookMatcherValue } from '../internal/types';
|
||||
import {
|
||||
renderUserPromptHookBlockResult,
|
||||
renderUserPromptHookResult,
|
||||
} from './user-prompt';
|
||||
} from '../internal/userPrompt';
|
||||
|
||||
export interface HookResultPayload {
|
||||
readonly turnId?: number;
|
||||
|
|
@ -471,11 +469,3 @@ function toolOutputText(output: ExecutableToolResult['output']): string {
|
|||
.map((part) => part.text)
|
||||
.join('');
|
||||
}
|
||||
|
||||
registerScopedService(
|
||||
LifecycleScope.Agent,
|
||||
IAgentExternalHooksService,
|
||||
AgentExternalHooksService,
|
||||
ScopeActivation.OnScopeCreated,
|
||||
'externalHooks',
|
||||
);
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import { createDecorator, type ServiceIdentifier } from '#/_base/di/instantiation';
|
||||
import type { Event } from '#/_base/event';
|
||||
import type { HookBlockDecision, HookMatcherValue, HookResult } from '#/agent/externalHooks/types';
|
||||
import type { HookBlockDecision, HookMatcherValue, HookResult } from '../internal/types';
|
||||
|
||||
export interface ExternalHooksRunnerTriggerArgs {
|
||||
readonly matcherValue?: HookMatcherValue;
|
||||
|
|
@ -1,20 +1,18 @@
|
|||
import { Disposable } from '#/_base/di/lifecycle';
|
||||
import { LifecycleScope } from '#/app/scopes';
|
||||
import { ScopeActivation, registerScopedService } from '#/_base/di/scope';
|
||||
import { Emitter, type Event } from '#/_base/event';
|
||||
import { IBootstrapService } from '#/app/bootstrap/bootstrap';
|
||||
import { IConfigService } from '#/app/config/config';
|
||||
import { IPluginService } from '#/app/plugin/plugin';
|
||||
import { HOOKS_SECTION, type HookDefConfig } from '#/agent/externalHooks/configSection';
|
||||
import type { HookBlockDecision, HookDef, HookResult } from '#/agent/externalHooks/types';
|
||||
import { IHostProcessService } from '#/os/interface/hostProcess';
|
||||
|
||||
import { HOOKS_SECTION, type HookDefConfig } from '../configSection';
|
||||
import {
|
||||
IExternalHooksRunnerService,
|
||||
type ExternalHooksRunnerTriggerArgs,
|
||||
} from './externalHooksRunner';
|
||||
import { blockDecision, indexHooks, runMatchedHooks } from './runner';
|
||||
import type { HookRunCallbacks } from './runner';
|
||||
import { blockDecision, indexHooks, runMatchedHooks } from '../internal/matchHooks';
|
||||
import type { HookRunCallbacks } from '../internal/matchHooks';
|
||||
import type { HookBlockDecision, HookDef, HookResult } from '../internal/types';
|
||||
|
||||
export class ExternalHooksRunnerService extends Disposable implements IExternalHooksRunnerService {
|
||||
declare readonly _serviceBrand: undefined;
|
||||
|
|
@ -120,11 +118,3 @@ export class ExternalHooksRunnerService extends Disposable implements IExternalH
|
|||
this._onDidReload.fire();
|
||||
}
|
||||
}
|
||||
|
||||
registerScopedService(
|
||||
LifecycleScope.App,
|
||||
IExternalHooksRunnerService,
|
||||
ExternalHooksRunnerService,
|
||||
ScopeActivation.OnScopeCreated,
|
||||
'externalHooksRunner',
|
||||
);
|
||||
|
|
@ -3,7 +3,7 @@ import { z } from 'zod';
|
|||
import { registerConfigSection } from '#/app/config/configSectionContributions';
|
||||
import { isPlainObject, plainObjectToToml, transformPlainObject } from '#/app/config/toml';
|
||||
|
||||
import { HOOK_EVENT_TYPES } from './types';
|
||||
import { HOOK_EVENT_TYPES } from './internal/types';
|
||||
|
||||
export const HOOKS_SECTION = 'hooks';
|
||||
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
import { LifecycleScope } from '#/app/scopes';
|
||||
import { Feature } from '#/features/feature';
|
||||
import { registerFeature } from '#/features/featureRegistry';
|
||||
|
||||
import './configSection';
|
||||
import { IAgentExternalHooksService } from './agent/agentExternalHooks';
|
||||
import { AgentExternalHooksService } from './agent/agentExternalHooksService';
|
||||
import { IExternalHooksRunnerService } from './app/externalHooksRunner';
|
||||
import { ExternalHooksRunnerService } from './app/externalHooksRunnerService';
|
||||
import { ISessionExternalHooksService } from './session/sessionExternalHooks';
|
||||
import { SessionExternalHooksService } from './session/sessionExternalHooksService';
|
||||
|
||||
export class ExternalHooksFeature extends Feature {
|
||||
static override readonly name = 'externalHooks';
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.contributeService(
|
||||
LifecycleScope.App,
|
||||
IExternalHooksRunnerService,
|
||||
ExternalHooksRunnerService,
|
||||
);
|
||||
this.contributeService(
|
||||
LifecycleScope.Session,
|
||||
ISessionExternalHooksService,
|
||||
SessionExternalHooksService,
|
||||
);
|
||||
this.contributeAgentService(IAgentExternalHooksService, AgentExternalHooksService);
|
||||
}
|
||||
}
|
||||
|
||||
registerFeature(ExternalHooksFeature);
|
||||
|
|
@ -1,13 +1,14 @@
|
|||
import { runHook } from '#/agent/externalHooks/runner';
|
||||
import type { IHostProcessService } from '#/os/interface/hostProcess';
|
||||
|
||||
import { runHook } from './runHook';
|
||||
import type {
|
||||
HookBlockDecision,
|
||||
HookDef,
|
||||
HookMatcherValue,
|
||||
HookResult,
|
||||
} from '#/agent/externalHooks/types';
|
||||
import type { IHostProcessService } from '#/os/interface/hostProcess';
|
||||
} from './types';
|
||||
|
||||
import type { ExternalHooksRunnerTriggerArgs } from './externalHooksRunner';
|
||||
import type { ExternalHooksRunnerTriggerArgs } from '../app/externalHooksRunner';
|
||||
|
||||
const DEFAULT_HOOK_TIMEOUT_SECONDS = 30;
|
||||
|
||||
|
|
@ -1,8 +1,5 @@
|
|||
import { Service } from '#/_base/di/service';
|
||||
import { LifecycleScope } from '#/app/scopes';
|
||||
import { ScopeActivation, registerScopedService } from '#/_base/di/scope';
|
||||
import { IntervalTimer } from '#/_base/utils/timer';
|
||||
import { IExternalHooksRunnerService } from '#/app/externalHooksRunner/externalHooksRunner';
|
||||
import { ISessionManager } from '#/app/sessionManager/sessionManager';
|
||||
import { IModelService } from '#/kosong/model/model';
|
||||
import {
|
||||
|
|
@ -20,7 +17,8 @@ import {
|
|||
type SessionCreateSource,
|
||||
} from '#/workspace/sessionLifecycle/sessionLifecycle';
|
||||
|
||||
import { ISessionExternalHooksService } from './externalHooks';
|
||||
import { IExternalHooksRunnerService } from '../app/externalHooksRunner';
|
||||
import { ISessionExternalHooksService } from './sessionExternalHooks';
|
||||
|
||||
type SessionStartHookSource = Exclude<SessionCreateSource, 'fork'>;
|
||||
|
||||
|
|
@ -183,11 +181,3 @@ export class SessionExternalHooksService
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
registerScopedService(
|
||||
LifecycleScope.Session,
|
||||
ISessionExternalHooksService,
|
||||
SessionExternalHooksService,
|
||||
ScopeActivation.OnScopeCreated,
|
||||
'externalHooks',
|
||||
);
|
||||
|
|
@ -322,6 +322,14 @@ export * from '#/features/plan/plan';
|
|||
export * from '#/features/plan/planOps';
|
||||
export * from '#/features/plan/planService';
|
||||
import '#/features/plan/planFeature';
|
||||
export * from '#/features/externalHooks/configSection';
|
||||
export * from '#/features/externalHooks/app/externalHooksRunner';
|
||||
export * from '#/features/externalHooks/app/externalHooksRunnerService';
|
||||
export * from '#/features/externalHooks/session/sessionExternalHooks';
|
||||
export * from '#/features/externalHooks/session/sessionExternalHooksService';
|
||||
export * from '#/features/externalHooks/agent/agentExternalHooks';
|
||||
export * from '#/features/externalHooks/agent/agentExternalHooksService';
|
||||
import '#/features/externalHooks/externalHooksFeature';
|
||||
export * from '#/features/debugEvents/debugEvents';
|
||||
export * from '#/features/debugEvents/debugEventsService';
|
||||
import '#/features/debugEvents/debugEventsFeature';
|
||||
|
|
@ -455,8 +463,6 @@ export * from '#/workspace/sessionLifecycle/sessionLifecycleEvents';
|
|||
export * from '#/workspace/sessionLifecycle/sessionLifecycleService';
|
||||
export * from '#/workspace/sessionLifecycle/coldSessionArchive';
|
||||
export * from '#/workspace/sessionLifecycle/internal/addressing';
|
||||
export * from '#/session/externalHooks/externalHooks';
|
||||
export * from '#/session/externalHooks/externalHooksService';
|
||||
import '#/app/sessionExport/errors';
|
||||
export * from '#/app/sessionExport/sessionExport';
|
||||
export * from '#/app/sessionExport/sessionExportService';
|
||||
|
|
@ -574,8 +580,6 @@ export * from '#/app/edit/editService';
|
|||
export * from '#/app/edit/textModel';
|
||||
export * from '#/agent/tools/edit/edit';
|
||||
import '#/agent/tools/edit/editTool';
|
||||
export * from '#/app/externalHooksRunner/externalHooksRunner';
|
||||
export * from '#/app/externalHooksRunner/externalHooksRunnerService';
|
||||
export * from '#/agent/tools/fetch-url/fetch-url';
|
||||
import '#/agent/tools/fetch-url/fetchUrlTool';
|
||||
export * from '#/app/web/web';
|
||||
|
|
@ -611,9 +615,6 @@ export * from '#/agent/contextInjector/contextInjectorService';
|
|||
export * from '#/agent/plugin/agentPlugin';
|
||||
export * from '#/agent/plugin/agentPluginOps';
|
||||
export * from '#/agent/plugin/agentPluginService';
|
||||
import '#/agent/externalHooks/configSection';
|
||||
export * from '#/agent/externalHooks/externalHooks';
|
||||
export * from '#/agent/externalHooks/externalHooksService';
|
||||
export * from '#/agent/fullCompaction/strategy';
|
||||
export * from '#/agent/fullCompaction/fullCompaction';
|
||||
export * from '#/agent/fullCompaction/fullCompactionService';
|
||||
|
|
|
|||
|
|
@ -1,2 +0,0 @@
|
|||
export * from './externalHooks';
|
||||
export * from './externalHooksService';
|
||||
|
|
@ -18,8 +18,8 @@ import {
|
|||
DefaultCompactionStrategy,
|
||||
} from '#/agent/fullCompaction/strategy';
|
||||
import { COMPACTION_SUMMARY_PREFIX } from '#/agent/contextMemory/compactionHandoff';
|
||||
import { makeHookRunner } from '../externalHooks/runner-stub';
|
||||
import type { IExternalHooksRunnerService } from '#/app/externalHooksRunner/externalHooksRunner';
|
||||
import { makeHookRunner } from '../../features/externalHooks/runner-stub';
|
||||
import type { IExternalHooksRunnerService } from '#/features/externalHooks/app/externalHooksRunner';
|
||||
import { MASTER_ENV } from '#/app/flag/flagService';
|
||||
import { estimateTokensForMessages } from '#/kosong/contract/tokens';
|
||||
import { recordingTelemetry, type TelemetryRecord } from '../../app/telemetry/stubs';
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ import {
|
|||
import { ProcessTask } from '#/agent/tools/os/bash/process-task';
|
||||
import { IAgentContextMemoryService } from '#/agent/contextMemory/contextMemory';
|
||||
import { IEventBus } from '#/app/event/eventBus';
|
||||
import type { IExternalHooksRunnerService } from '#/app/externalHooksRunner/externalHooksRunner';
|
||||
import type { IExternalHooksRunnerService } from '#/features/externalHooks/app/externalHooksRunner';
|
||||
import { IAgentLoopService } from '#/agent/loop/loop';
|
||||
import { MessageStepRequest } from '#/agent/loop/stepRequest';
|
||||
import { IAgentConversationUndoService } from '#/agent/undo/undo';
|
||||
|
|
|
|||
|
|
@ -0,0 +1,89 @@
|
|||
import { beforeEach, describe, expect, it } from 'vitest';
|
||||
|
||||
import { type CollectionToken, type CollectionView } from '#/_base/di/collection';
|
||||
import { ScopeUnits } from '#/_base/di/fiber';
|
||||
import { ScopeActivation } from '#/_base/di/instantiation';
|
||||
import { type InstantiationService } from '#/_base/di/instantiationService';
|
||||
import { _clearScopedRegistryForTests, registerScopedService, type Scope } from '#/_base/di/scope';
|
||||
import { createScopedTestHost } from '#/_base/di/test';
|
||||
import { Event } from '#/_base/event';
|
||||
import { IBootstrapService } from '#/app/bootstrap/bootstrap';
|
||||
import { IConfigService } from '#/app/config/config';
|
||||
import { IFeatureManager } from '#/app/feature/featureManager';
|
||||
import { FeatureManagerService } from '#/app/feature/featureManagerService';
|
||||
import { IPluginService } from '#/app/plugin/plugin';
|
||||
import { LifecycleScope } from '#/app/scopes';
|
||||
import { IFeatureAssemblyService } from '#/features/featureAssembly';
|
||||
import { FeatureAssemblyService } from '#/features/featureAssemblyService';
|
||||
import { IAgentExternalHooksService } from '#/features/externalHooks/agent/agentExternalHooks';
|
||||
import { IExternalHooksRunnerService } from '#/features/externalHooks/app/externalHooksRunner';
|
||||
import { ExternalHooksRunnerService } from '#/features/externalHooks/app/externalHooksRunnerService';
|
||||
import '#/features/externalHooks/externalHooksFeature';
|
||||
import { ISessionExternalHooksService } from '#/features/externalHooks/session/sessionExternalHooks';
|
||||
import { IHostProcessService } from '#/os/interface/hostProcess';
|
||||
|
||||
import { stubBootstrap } from '../../app/bootstrap/stubs';
|
||||
|
||||
function collectionViewOf<T>(scope: Scope, token: CollectionToken<T>): CollectionView<T> {
|
||||
return (scope.instantiation as InstantiationService).fiberHost.collectionView(token);
|
||||
}
|
||||
|
||||
describe('ExternalHooksFeature — assembly (src/features/externalHooks)', () => {
|
||||
beforeEach(() => {
|
||||
_clearScopedRegistryForTests();
|
||||
registerScopedService(
|
||||
LifecycleScope.App,
|
||||
IFeatureManager,
|
||||
FeatureManagerService,
|
||||
ScopeActivation.OnScopeCreated,
|
||||
'feature',
|
||||
);
|
||||
registerScopedService(
|
||||
LifecycleScope.App,
|
||||
IFeatureAssemblyService,
|
||||
FeatureAssemblyService,
|
||||
ScopeActivation.OnScopeCreated,
|
||||
'features',
|
||||
);
|
||||
});
|
||||
|
||||
it('assembles the feature and retracts all contributions on unprovide', async () => {
|
||||
const host = createScopedTestHost([
|
||||
[IBootstrapService, stubBootstrap()],
|
||||
[
|
||||
IConfigService,
|
||||
{ _serviceBrand: undefined, ready: Promise.resolve(), get: () => undefined },
|
||||
],
|
||||
[
|
||||
IPluginService,
|
||||
{ _serviceBrand: undefined, enabledHooks: async () => [], onDidReload: Event.None },
|
||||
],
|
||||
[IHostProcessService, { _serviceBrand: undefined }],
|
||||
]);
|
||||
const manager = host.app.accessor.get(IFeatureManager);
|
||||
expect(manager.units().map((unit) => unit.name)).toContain('externalHooks');
|
||||
|
||||
const runner = host.app.accessor.get(IExternalHooksRunnerService);
|
||||
expect(runner).toBeInstanceOf(ExternalHooksRunnerService);
|
||||
|
||||
const sessionUnits = collectionViewOf(host.app, ScopeUnits(LifecycleScope.Session));
|
||||
expect(sessionUnits.items.map((item) => item.name)).toEqual([
|
||||
`externalHooks:${String(ISessionExternalHooksService)}`,
|
||||
]);
|
||||
const agentUnits = collectionViewOf(host.app, ScopeUnits(LifecycleScope.Agent));
|
||||
expect(agentUnits.items.map((item) => item.name)).toEqual([
|
||||
`externalHooks:${String(IAgentExternalHooksService)}`,
|
||||
]);
|
||||
|
||||
await manager.unprovideUnit('externalHooks');
|
||||
await host.app.instantiation.cascade.whenIdle();
|
||||
await new Promise((resolve) => setTimeout(resolve, 0));
|
||||
|
||||
expect(manager.units()).toHaveLength(0);
|
||||
expect(() => host.app.accessor.get(IExternalHooksRunnerService)).toThrow();
|
||||
expect(sessionUnits.items).toHaveLength(0);
|
||||
expect(agentUnits.items).toHaveLength(0);
|
||||
|
||||
host.dispose();
|
||||
});
|
||||
});
|
||||
|
|
@ -4,7 +4,7 @@ import { tmpdir } from 'node:os';
|
|||
import type { ContentPart } from '#/kosong/contract/message';
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import { makeHookRunner } from '../../agent/externalHooks/runner-stub';
|
||||
import { makeHookRunner } from './runner-stub';
|
||||
|
||||
function nodeCommand(source: string): string {
|
||||
return `node -e ${JSON.stringify(source.replaceAll(/\s*\n\s*/g, ' '))}`;
|
||||
|
|
@ -26,9 +26,9 @@ import {
|
|||
HOOKS_SECTION,
|
||||
hooksFromToml,
|
||||
hooksToToml,
|
||||
} from '#/agent/externalHooks/configSection';
|
||||
import { IAgentExternalHooksService } from '#/agent/externalHooks/externalHooks';
|
||||
import { AgentExternalHooksService } from '#/agent/externalHooks/externalHooksService';
|
||||
} from '#/features/externalHooks/configSection';
|
||||
import { IAgentExternalHooksService } from '#/features/externalHooks/agent/agentExternalHooks';
|
||||
import { AgentExternalHooksService } from '#/features/externalHooks/agent/agentExternalHooksService';
|
||||
import { IAgentFullCompactionService } from '#/agent/fullCompaction/fullCompaction';
|
||||
import { IAgentLoopService, type AfterStepContext } from '#/agent/loop/loop';
|
||||
import { TurnStarted } from '#/agent/loop/turnEvents';
|
||||
|
|
@ -43,9 +43,9 @@ import {
|
|||
PermissionApprovalResolved,
|
||||
} from '#/agent/toolApproval/toolApprovalService';
|
||||
import { IAgentToolExecutorService } from '#/agent/toolExecutor/toolExecutor';
|
||||
import { IExternalHooksRunnerService } from '#/app/externalHooksRunner/externalHooksRunner';
|
||||
import { ExternalHooksRunnerService } from '#/app/externalHooksRunner/externalHooksRunnerService';
|
||||
import { makeHookRunner } from '../../agent/externalHooks/runner-stub';
|
||||
import { IExternalHooksRunnerService } from '#/features/externalHooks/app/externalHooksRunner';
|
||||
import { ExternalHooksRunnerService } from '#/features/externalHooks/app/externalHooksRunnerService';
|
||||
import { makeHookRunner } from './runner-stub';
|
||||
import type { AgentTaskInfo } from '#/agent/task/task';
|
||||
import { IBootstrapService } from '#/app/bootstrap/bootstrap';
|
||||
import { IConfigService } from '#/app/config/config';
|
||||
|
|
@ -69,15 +69,15 @@ import {
|
|||
type AgentTaskStopHookContext,
|
||||
ISessionSubagentService,
|
||||
} from '#/session/subagent/subagent';
|
||||
import { ISessionExternalHooksService } from '#/session/externalHooks/externalHooks';
|
||||
import { SessionExternalHooksService } from '#/session/externalHooks/externalHooksService';
|
||||
import { ISessionExternalHooksService } from '#/features/externalHooks/session/sessionExternalHooks';
|
||||
import { SessionExternalHooksService } from '#/features/externalHooks/session/sessionExternalHooksService';
|
||||
import {
|
||||
ISessionAgentProfileCatalog,
|
||||
} from '#/session/sessionAgentProfileCatalog/sessionAgentProfileCatalog';
|
||||
import { ISessionMetadata } from '#/session/sessionMetadata/sessionMetadata';
|
||||
import { IModelService } from '#/kosong/model/model';
|
||||
|
||||
import { stubBootstrap } from '../bootstrap/stubs';
|
||||
import { stubBootstrap } from '../../app/bootstrap/stubs';
|
||||
import { stubLoopWithHooks, stubToolExecutor } from '../../agent/loop/stubs';
|
||||
import { registerStateServices } from '../../state/stubs';
|
||||
import { registerTestAgentWireServices } from '../../wire/stubs';
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
import { Event } from '#/_base/event';
|
||||
import { ExternalHooksRunnerService } from '#/app/externalHooksRunner/externalHooksRunnerService';
|
||||
import { HOOKS_SECTION } from '#/agent/externalHooks/configSection';
|
||||
import type { HookDef } from '#/agent/externalHooks/types';
|
||||
import { ExternalHooksRunnerService } from '#/features/externalHooks/app/externalHooksRunnerService';
|
||||
import { HOOKS_SECTION } from '#/features/externalHooks/configSection';
|
||||
import type { HookDef } from '#/features/externalHooks/internal/types';
|
||||
import { IBootstrapService } from '#/app/bootstrap/bootstrap';
|
||||
import { IConfigService } from '#/app/config/config';
|
||||
import { IPluginService } from '#/app/plugin/plugin';
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import { buildHookSpawnOptions, runHook } from '#/agent/externalHooks/runner';
|
||||
import { buildHookSpawnOptions, runHook } from '#/features/externalHooks/internal/runHook';
|
||||
import { HostProcessService } from '#/os/backends/node-local/hostProcessService';
|
||||
|
||||
const hostProcess = new HostProcessService();
|
||||
|
|
@ -1183,6 +1183,13 @@ export class AgentTestContext {
|
|||
'app',
|
||||
);
|
||||
this.root = createAppScope({ seeds: appSeeds });
|
||||
const hookRunnerSeed = appSeeds.find(([id]) => id === IExternalHooksRunnerService);
|
||||
if (hookRunnerSeed !== undefined) {
|
||||
this.root.instantiation.provide(
|
||||
IExternalHooksRunnerService,
|
||||
hookRunnerSeed[1] as IExternalHooksRunnerService,
|
||||
);
|
||||
}
|
||||
|
||||
const initialConfig = this.root.accessor.get(IConfigService);
|
||||
this.root.accessor
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ import { IAgentContextInjectorService } from '#/agent/contextInjector/contextInj
|
|||
import { IAgentTaskService } from '#/agent/task/task';
|
||||
import { IAgentContextMemoryService } from '#/agent/contextMemory/contextMemory';
|
||||
import { IAgentTokenCountingService } from '#/agent/tokenCounting/tokenCounting';
|
||||
import { makeHookRunner } from '../agent/externalHooks/runner-stub';
|
||||
import { makeHookRunner } from '../features/externalHooks/runner-stub';
|
||||
import { IAgentProfileService, type ProfileData } from '#/agent/profile/profile';
|
||||
import { IAgentPermissionModeService } from '#/agent/permissionMode/permissionMode';
|
||||
import { IAgentRuntimeService } from '#/agent/runtimeBinding/agentRuntime';
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ import type {
|
|||
UserPromptOrigin,
|
||||
} from '@moonshot-ai/agent-core-v2/agent/contextMemory/types';
|
||||
import { messageContentSchema } from './message';
|
||||
import type { HookResultPayload } from '@moonshot-ai/agent-core-v2/agent/externalHooks/externalHooksService';
|
||||
import type { HookResultPayload } from '@moonshot-ai/agent-core-v2/features/externalHooks/agent/agentExternalHooksService';
|
||||
import type {
|
||||
CompactionBlockedPayload,
|
||||
CompactionCompletedPayload,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import type { AgentActivityUpdated } from '@moonshot-ai/agent-core-v2/agent/activityView/activityView';
|
||||
import type { ContextSpliced } from '@moonshot-ai/agent-core-v2/agent/contextMemory/contextEvents';
|
||||
import type { HookResult } from '@moonshot-ai/agent-core-v2/agent/externalHooks/externalHooksService';
|
||||
import type { HookResult } from '@moonshot-ai/agent-core-v2/features/externalHooks/agent/agentExternalHooksService';
|
||||
import type {
|
||||
CompactionBlocked,
|
||||
CompactionCancelled,
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
* `pluginService` — plugin management and consumption. Mirrors
|
||||
* `agent-core-v2/app/plugin/plugin.ts` and `agent-core-v2/app/plugin/types.ts`;
|
||||
* nested `McpServerConfig` mirrors `agent-core-v2/mcpCore/config-schema.ts`,
|
||||
* `HookDefConfig` mirrors `agent-core-v2/agent/externalHooks/configSection.ts`.
|
||||
* `HookDefConfig` mirrors `agent-core-v2/features/externalHooks/configSection.ts`.
|
||||
* `pluginSkillRoots`, `enabledSessionStarts`, `enabledSystemPrompts`,
|
||||
* `enabledMcpServers`, and `enabledHooks` are excluded (not part of the
|
||||
* klient wire surface).
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue