refactor: rename store services to domain-specific terms

- rename ToolStore to ToolState
- rename CronTaskStore to CronTaskPersistence
- rename SkillCatalogStore to SkillDiscovery
- rename WorkspaceStore to WorkspacePersistence
- rename IFileStore to IFileService and update server call sites
This commit is contained in:
haozhe.yang 2026-07-03 13:27:32 +08:00
parent f8e7114109
commit b2cb0c7ede
43 changed files with 141 additions and 141 deletions

View file

@ -98,8 +98,8 @@ const DOMAIN_LAYER = new Map([
// L2 — data & cross-cutting capabilities
['records', 2],
['wireRecord', 2],
['blobStore', 2],
['filestore', 2],
['blob', 2],
['file', 2],
['config', 2],
['agentFs', 2],
['process', 2],
@ -120,7 +120,7 @@ const DOMAIN_LAYER = new Map([
['flag', 3],
['toolExecutor', 3],
['toolRegistry', 3],
['toolStore', 3],
['toolState', 3],
['userTool', 3],
['permissionMode', 3],
['permissionPolicy', 3],
@ -163,7 +163,7 @@ const DOMAIN_LAYER = new Map([
['background', 5],
['mcp', 5],
['cron', 5],
['cronStore', 5],
['cronPersistence', 5],
// `btw` forks a single side-question sub-agent via `agentLifecycle`,
// parallel to how the `Agent` tool spawns child agents. Agent-scope, L5.
['btw', 5],

View file

@ -76,8 +76,8 @@ const FRAMEWORK_BINDINGS: readonly { token: string; scope: ServiceScope; impl: s
* Production composition-root bindings seeded by `bootstrap()` via
* `ScopeOptions.extra`. `buildCollection` applies `extra` AFTER the static
* `registerScopedService` registry, so these take precedence at runtime: they
* override a static default where one exists (e.g. `ISkillCatalogStore`
* `FileSkillCatalogStore`) and supply the binding where the layer ships no
* override a static default where one exists (e.g. `ISkillDiscovery`
* `FileSkillDiscovery`) and supply the binding where the layer ships no
* in-package default (the Storage-layer tokens `FileStorageService`, whose
* in-memory backend is no longer auto-registered). The analyzer mirrors that
* so the graph reflects the backend that actually runs in production.
@ -91,7 +91,7 @@ const PRODUCTION_OVERRIDES: readonly { token: string; scope: ServiceScope; impl:
{ token: 'IAppendLogStorage', scope: 'App', impl: 'FileStorageService' },
{ token: 'IAtomicDocumentStorage', scope: 'App', impl: 'FileStorageService' },
{ token: 'IBlobStorage', scope: 'App', impl: 'FileStorageService' },
{ token: 'ISkillCatalogStore', scope: 'App', impl: 'FileSkillCatalogStore' },
{ token: 'ISkillDiscovery', scope: 'App', impl: 'FileSkillDiscovery' },
];
/**

View file

@ -2,7 +2,7 @@
* `cron` domain barrel re-exports cron utilities (expression parser, jitter,
* format, clock, config) and registers the three cron tools (`CronCreate` /
* `CronList` / `CronDelete`) via side-effect imports. The cron task record
* type lives in `app/cronStore`; the scheduling engine lives in `session/cron`.
* type lives in `app/cronPersistence`; the scheduling engine lives in `session/cron`.
*/
import './configSection';

View file

@ -4,7 +4,7 @@
* cron cadence (`recurring: true`, the default).
*
* Tasks live in `ISessionCronService` (Session scope) and are persisted
* through the App-scoped `ICronTaskStore` under the project's cron
* through the App-scoped `ICronTaskPersistence` under the project's cron
* scope, so a `kimi resume` of the same session reloads them and the
* scheduler picks up where it left off (fires that fell during downtime
* are collapsed into a single delivery with `coalescedCount`). Tasks do

View file

@ -46,7 +46,7 @@ import type { ExecutableTool as BuiltinTool, ToolExecution } from '#/agent/tool'
import { registerTool } from '#/agent/toolRegistry';
import { toInputJsonSchema } from '#/_base/tools/support/input-schema';
import { ISessionCronService } from '#/session/cron';
import type { CronTask } from '#/app/cronStore';
import type { CronTask } from '#/app/cronPersistence';
import {
cronToHuman,
parseCronExpression,

View file

@ -25,7 +25,7 @@ import {
renderTodoList,
type TodoItem,
} from '#/agent/todoList/tools/todo-list';
import { IAgentToolStoreService } from '#/agent/toolStore';
import { IAgentToolState } from '#/agent/toolState';
import { IAgentTurnService } from '#/agent/turn';
import {
APIContextOverflowError,
@ -102,7 +102,7 @@ export class AgentFullCompactionService extends Disposable implements IAgentFull
@IAgentContextSizeService private readonly contextSize: IAgentContextSizeService,
@IAgentLLMRequesterService private readonly llmRequester: IAgentLLMRequesterService,
@IAgentProfileService private readonly profile: IAgentProfileService,
@IAgentToolStoreService private readonly toolStore: IAgentToolStoreService,
@IAgentToolState private readonly toolStore: IAgentToolState,
@ITelemetryService private readonly telemetry: ITelemetryService,
@IAgentRecordService private readonly record: IAgentRecordService,
@IAgentExternalHooksService private readonly externalHooks: IAgentExternalHooksService,

View file

@ -17,7 +17,7 @@ import { IAgentContextMemoryService } from '#/agent/contextMemory';
import { IAgentContextInjectorService } from '#/agent/contextInjector';
import { IAgentProfileService } from '#/agent/profile';
import { IAgentToolRegistryService } from '#/agent/toolRegistry';
import { IAgentToolStoreService } from '#/agent/toolStore';
import { IAgentToolState } from '#/agent/toolState';
import { IAgentTodoListService } from './todoList';
import { InstantiationType } from '#/_base/di/extensions';
import { LifecycleScope, registerScopedService } from '#/_base/di/scope';
@ -28,7 +28,7 @@ export class AgentTodoListService extends Disposable implements IAgentTodoListSe
constructor(
@IAgentContextMemoryService private readonly context: IAgentContextMemoryService,
@IAgentProfileService private readonly profile: IAgentProfileService,
@IAgentToolStoreService private readonly toolStore: IAgentToolStoreService,
@IAgentToolState private readonly toolStore: IAgentToolState,
@IAgentToolRegistryService toolRegistry: IAgentToolRegistryService,
@IAgentContextInjectorService dynamicInjector: IAgentContextInjectorService,
@IInstantiationService private readonly instantiationService: IInstantiationService,

View file

@ -18,7 +18,7 @@ import { z } from 'zod';
import type { BuiltinTool } from '#/agent/tool';
import type { ToolExecution } from '#/agent/tool';
import { toInputJsonSchema } from '#/_base/tools/support/input-schema';
import { IAgentToolStoreService } from '#/agent/toolStore';
import { IAgentToolState } from '#/agent/toolState';
import DESCRIPTION from './todo-list.md?raw';
import TODO_LIST_WRITE_REMINDER from './todo-list-write-reminder.md?raw';
@ -42,7 +42,7 @@ export function readTodoItems(raw: unknown): readonly TodoItem[] {
}));
}
declare module '#/agent/toolStore' {
declare module '#/agent/toolState' {
interface ToolStoreData {
todo: readonly TodoItem[];
}
@ -111,7 +111,7 @@ export class TodoListTool implements BuiltinTool<TodoListInput> {
readonly description: string = DESCRIPTION;
readonly parameters: Record<string, unknown> = toInputJsonSchema(TodoListInputSchema);
constructor(@IAgentToolStoreService private readonly store: IAgentToolStoreService) {}
constructor(@IAgentToolState private readonly store: IAgentToolState) {}
resolveExecution(args: TodoListInput): ToolExecution {
const description =

View file

@ -0,0 +1,6 @@
/**
* `toolState` domain barrel - re-exports the tool state service contract and implementation.
*/
export * from './toolState';
export * from './toolStateService';

View file

@ -15,7 +15,7 @@ export interface ToolStoreUpdate<K extends ToolStoreKey = ToolStoreKey> {
readonly value: ToolStoreData[K];
}
export interface IAgentToolStoreService extends ToolStore {
export interface IAgentToolState extends ToolStore {
readonly _serviceBrand: undefined;
data(): Readonly<Partial<ToolStoreData>>;
@ -24,4 +24,4 @@ export interface IAgentToolStoreService extends ToolStore {
}>;
}
export const IAgentToolStoreService = createDecorator<IAgentToolStoreService>('agentToolStoreService');
export const IAgentToolState = createDecorator<IAgentToolState>('agentToolState');

View file

@ -4,7 +4,7 @@ import {
import { InstantiationType } from '#/_base/di/extensions';
import { LifecycleScope, registerScopedService } from '#/_base/di/scope';
import { OrderedHookSlot } from '#/hooks';
import { IAgentToolStoreService, type ToolStoreData, type ToolStoreKey } from './toolStore';
import { IAgentToolState, type ToolStoreData, type ToolStoreKey } from './toolState';
import { IAgentRecordService, type AgentRecord } from '#/agent/record';
declare module '#/agent/wireRecord' {
@ -16,7 +16,7 @@ declare module '#/agent/wireRecord' {
}
}
export class AgentToolStoreService extends Disposable implements IAgentToolStoreService {
export class AgentToolStateService extends Disposable implements IAgentToolState {
declare readonly _serviceBrand: undefined;
private readonly store: Partial<ToolStoreData> = {};
@ -66,8 +66,8 @@ export class AgentToolStoreService extends Disposable implements IAgentToolStore
registerScopedService(
LifecycleScope.Agent,
IAgentToolStoreService,
AgentToolStoreService,
IAgentToolState,
AgentToolStateService,
InstantiationType.Delayed,
'toolStore',
'toolState',
);

View file

@ -1,6 +0,0 @@
/**
* `toolStore` domain barrel - re-exports the toolStore service contract and implementation.
*/
export * from './toolStore';
export * from './toolStoreService';

View file

@ -2,7 +2,7 @@
* `cron` domain (L5) shared `CronTask` data record.
*
* The authoritative definition of a cron task's persistent shape. Used by
* `ICronTaskStore` (App scope) for project-level persistence and by
* `ICronTaskPersistence` (App scope) for project-level persistence and by
* `ISessionCronService` (Session scope) for the live scheduling engine.
* The `tags` map carries arbitrary metadata (e.g. `sessionId`) that the
* Session projection uses to filter tasks belonging to the current session.

View file

@ -1,11 +1,11 @@
/**
* `cron` domain (L5) `ICronTaskStore` contract.
* `cron` domain (L5) `ICronTaskPersistence` contract.
*
* Project-level persistence catalog for cron tasks. Stores tasks under
* Project-level persistence for cron tasks. Persists tasks under
* `bootstrap.scope('cron')` as atomic documents keyed by
* `<workspaceId>/<taskId>.json`. Provides CRUD and query-by-workspace.
* The store is a pure data layer scheduling, timers, and fire delivery
* are owned by `ISessionCronService` at Session scope. Bound at App scope.
* A pure data layer scheduling, timers, and fire delivery are owned by
* `ISessionCronService` at Session scope. Bound at App scope.
*/
import { createDecorator } from '#/_base/di';
@ -16,7 +16,7 @@ export interface CronTaskQuery {
readonly workspaceId: string;
}
export interface ICronTaskStore {
export interface ICronTaskPersistence {
readonly _serviceBrand: undefined;
get(workspaceId: string, taskId: string): Promise<CronTask | undefined>;
list(query: CronTaskQuery): Promise<readonly CronTask[]>;
@ -24,4 +24,4 @@ export interface ICronTaskStore {
delete(workspaceId: string, taskId: string): Promise<void>;
}
export const ICronTaskStore = createDecorator<ICronTaskStore>('cronTaskStore');
export const ICronTaskPersistence = createDecorator<ICronTaskPersistence>('cronTaskPersistence');

View file

@ -1,5 +1,5 @@
/**
* `cron` domain (L5) `ICronTaskStore` implementation.
* `cron` domain (L5) `ICronTaskPersistence` implementation.
*
* Persists cron tasks as atomic JSON documents under the `cron` persistence
* scope (`bootstrap.scope('cron')`), laid out as `<workspaceId>/<id>.json`.
@ -12,7 +12,7 @@ import { LifecycleScope, registerScopedService } from '#/_base/di/scope';
import { IAtomicDocumentStore } from '#/persistence/interface';
import { IBootstrapService } from '#/app/bootstrap';
import { ICronTaskStore, type CronTaskQuery } from './cronTaskStore';
import { ICronTaskPersistence, type CronTaskQuery } from './cronTaskPersistence';
import type { CronTask } from './cronTask';
export const CRON_ID_REGEX: RegExp = /^[0-9a-f]{8}$/;
@ -41,7 +41,7 @@ export function isValidCronTask(obj: unknown): obj is CronTask {
return true;
}
export class CronTaskStoreService extends Disposable implements ICronTaskStore {
export class CronTaskPersistenceService extends Disposable implements ICronTaskPersistence {
declare readonly _serviceBrand: undefined;
private readonly cronScope: string;
@ -93,8 +93,8 @@ export class CronTaskStoreService extends Disposable implements ICronTaskStore {
registerScopedService(
LifecycleScope.App,
ICronTaskStore,
CronTaskStoreService,
ICronTaskPersistence,
CronTaskPersistenceService,
InstantiationType.Delayed,
'cron',
);

View file

@ -0,0 +1,8 @@
/**
* `cron` domain barrel re-exports the cron task data record, the
* `ICronTaskPersistence` contract, and registers the App-scoped persistence service.
*/
export * from './cronTask';
export * from './cronTaskPersistence';
export * from './cronTaskPersistenceService';

View file

@ -1,8 +0,0 @@
/**
* `cron` domain barrel re-exports the cron task data record, the
* `ICronTaskStore` contract, and registers the App-scoped store service.
*/
export * from './cronTask';
export * from './cronTaskStore';
export * from './cronTaskStoreService';

View file

@ -3,7 +3,7 @@
*
* Registers the code-defined builtin skills into an in-memory catalog. Builtin
* skills are constants (not discovered from storage), so they bypass the
* `ISkillCatalogStore` and are registered directly by the global catalog.
* `ISkillDiscovery` and are registered directly by the global catalog.
*/
import type { InMemorySkillCatalog } from '#/app/globalSkillCatalog/registry';

View file

@ -1,10 +1,10 @@
/**
* `globalSkillCatalog` domain (L5) filesystem `ISkillCatalogStore` backend.
* `globalSkillCatalog` domain (L5) filesystem `ISkillDiscovery` backend.
*
* Discovers skill bundles by walking skill roots on the local filesystem and
* parsing each SKILL.md through `parser`. This is the only file in the skill
* domain that imports `node:fs`; the rest of the domain depends on the
* `ISkillCatalogStore` interface and stays filesystem-agnostic. Bound at App
* `ISkillDiscovery` interface and stays filesystem-agnostic. Bound at App
* scope by the composition root (tests register the in-memory backend instead).
*/
@ -16,7 +16,7 @@ import {
UnsupportedSkillTypeError,
parseSkillText,
} from './parser';
import type { SkillDiscoveryResult, ISkillCatalogStore } from './skillCatalogStore';
import type { SkillDiscoveryResult, ISkillDiscovery } from './skillDiscovery';
import type { SkillDefinition, SkillRoot, SkillSource, SkippedSkill } from './types';
import { normalizeSkillName } from './types';
@ -31,7 +31,7 @@ const PROJECT_GENERIC_DIRS = ['.agents/skills'] as const;
// loop forever. Real skill trees are 1-3 levels deep.
const MAX_SKILL_SCAN_DEPTH = 8;
export class FileSkillCatalogStore implements ISkillCatalogStore {
export class FileSkillDiscovery implements ISkillDiscovery {
declare readonly _serviceBrand: undefined;
async discoverProject(

View file

@ -2,7 +2,7 @@
* `globalSkillCatalog` domain (L5) `IGlobalSkillCatalog` implementation.
*
* Registers the builtin skills and discovers user / brand skills through the
* `ISkillCatalogStore`, using the user home directories from `bootstrap`. The
* `ISkillDiscovery`, using the user home directories from `bootstrap`. The
* result is cached after the first `load()`. Bound at App scope.
*/
@ -13,7 +13,7 @@ import { IBootstrapService } from '#/app/bootstrap';
import { registerBuiltinSkills } from '#/app/globalSkillCatalog/builtin';
import { IGlobalSkillCatalog } from './globalSkillCatalog';
import { InMemorySkillCatalog } from './registry';
import { ISkillCatalogStore } from './skillCatalogStore';
import { ISkillDiscovery } from './skillDiscovery';
import type { SkillCatalog } from './types';
export class GlobalSkillCatalogService implements IGlobalSkillCatalog {
@ -23,7 +23,7 @@ export class GlobalSkillCatalogService implements IGlobalSkillCatalog {
private loaded = false;
constructor(
@ISkillCatalogStore private readonly store: ISkillCatalogStore,
@ISkillDiscovery private readonly store: ISkillDiscovery,
@IBootstrapService private readonly bootstrap: IBootstrapService,
) {}

View file

@ -1,5 +1,5 @@
/**
* `globalSkillCatalog` domain (L5) in-memory `ISkillCatalogStore` backend.
* `globalSkillCatalog` domain (L5) in-memory `ISkillDiscovery` backend.
*
* Returns preset skill lists for project / user discovery without any IO.
* Registered as the App-scope default so tests and scopes work without a
@ -10,11 +10,11 @@
import { InstantiationType } from '#/_base/di/extensions';
import { LifecycleScope, registerScopedService } from '#/_base/di/scope';
import type { SkillDiscoveryResult } from './skillCatalogStore';
import { ISkillCatalogStore } from './skillCatalogStore';
import type { SkillDiscoveryResult } from './skillDiscovery';
import { ISkillDiscovery } from './skillDiscovery';
import type { SkillDefinition } from './types';
export class InMemorySkillCatalogStore implements ISkillCatalogStore {
export class InMemorySkillDiscovery implements ISkillDiscovery {
declare readonly _serviceBrand: undefined;
private projectSkills: readonly SkillDefinition[] = [];
@ -39,8 +39,8 @@ export class InMemorySkillCatalogStore implements ISkillCatalogStore {
registerScopedService(
LifecycleScope.App,
ISkillCatalogStore,
InMemorySkillCatalogStore,
ISkillDiscovery,
InMemorySkillDiscovery,
InstantiationType.Delayed,
'skill',
);

View file

@ -2,13 +2,13 @@
* `globalSkillCatalog` domain barrel re-exports the skill catalog
* contracts, parsers, registry, and the App-scope catalog services. Importing
* this barrel registers the `IGlobalSkillCatalog` and the default in-memory
* `ISkillCatalogStore` bindings into the scope registry.
* `ISkillDiscovery` bindings into the scope registry.
*/
export * from './types';
export * from './parser';
export * from './registry';
export * from './skillCatalogStore';
export * from './inMemorySkillCatalogStore';
export * from './skillDiscovery';
export * from './inMemorySkillDiscovery';
export * from './globalSkillCatalog';
export * from './globalSkillCatalogService';

View file

@ -1,7 +1,7 @@
/**
* `globalSkillCatalog` domain (L5) catalog Store contract.
* `globalSkillCatalog` domain (L5) catalog discovery contract.
*
* `ISkillCatalogStore` is a business-specific Store that hides how skill
* `ISkillDiscovery` is a business-specific interface that hides how skill
* bundles are discovered: a backend walks a skill root, reads each SKILL.md,
* and parses it into `SkillDefinition`s. The skill domain depends on this
* interface only and never touches `node:fs` / `hostFs`; the backend is chosen
@ -19,7 +19,7 @@ export interface SkillDiscoveryResult {
readonly scannedRoots: readonly string[];
}
export interface ISkillCatalogStore {
export interface ISkillDiscovery {
readonly _serviceBrand: undefined;
discoverProject(
@ -30,4 +30,4 @@ export interface ISkillCatalogStore {
discoverUser(homeDir: string, osHomeDir: string): Promise<SkillDiscoveryResult>;
}
export const ISkillCatalogStore = createDecorator<ISkillCatalogStore>('skillCatalogStore');
export const ISkillDiscovery = createDecorator<ISkillDiscovery>('skillDiscovery');

View file

@ -1,7 +1,7 @@
/**
* `workspaceRegistry` domain (L1) `FileWorkspaceStore` implementation.
* `workspaceRegistry` domain (L1) `FileWorkspacePersistence` implementation.
*
* File backend of `IWorkspaceStore`. Persists the catalog as a single
* File backend of `IWorkspacePersistence`. Persists the catalog as a single
* v1-compatible `workspaces.json` document at the storage root
* (`<homeDir>/workspaces.json`, via `scope = ''`) through the
* `IAtomicDocumentStore` access-pattern Store. Bound at App scope.
@ -13,10 +13,10 @@ import { IAtomicDocumentStore } from '#/app/storage';
import type { Workspace } from './workspaceRegistry';
import {
IWorkspaceStore,
IWorkspacePersistence,
type PersistedWorkspaceEntry,
type PersistedWorkspaceFile,
} from './workspaceStore';
} from './workspacePersistence';
const WORKSPACE_REGISTRY_VERSION = 1;
// Empty scope resolves to `<homeDir>/<key>` (join skips empty segments),
@ -24,7 +24,7 @@ const WORKSPACE_REGISTRY_VERSION = 1;
const WORKSPACE_REGISTRY_SCOPE = '';
const WORKSPACE_REGISTRY_KEY = 'workspaces.json';
export class FileWorkspaceStore implements IWorkspaceStore {
export class FileWorkspacePersistence implements IWorkspacePersistence {
declare readonly _serviceBrand: undefined;
constructor(@IAtomicDocumentStore private readonly docs: IAtomicDocumentStore) {}
@ -105,8 +105,8 @@ function parseTime(value: string, fallback: number): number {
registerScopedService(
LifecycleScope.App,
IWorkspaceStore,
FileWorkspaceStore,
IWorkspacePersistence,
FileWorkspacePersistence,
InstantiationType.Delayed,
'workspaceRegistry',
);

View file

@ -7,5 +7,5 @@
export * from './workspaceRegistry';
export * from './workspaceRegistryService';
export * from './workspaceStore';
export * from './fileWorkspaceStore';
export * from './workspacePersistence';
export * from './fileWorkspacePersistence';

View file

@ -1,5 +1,5 @@
/**
* `workspaceRegistry` domain (L1) `IWorkspaceStore` contract.
* `workspaceRegistry` domain (L1) `IWorkspacePersistence` contract.
*
* Domain-specific persistence Store for the known-workspaces catalog. It hides
* the on-disk document layout (`<homeDir>/workspaces.json`, the v1-compatible
@ -30,7 +30,7 @@ export interface PersistedWorkspaceFile {
readonly workspaces: Record<string, PersistedWorkspaceEntry>;
}
export interface IWorkspaceStore {
export interface IWorkspacePersistence {
readonly _serviceBrand: undefined;
/**
@ -46,5 +46,5 @@ export interface IWorkspaceStore {
save(workspaces: readonly Workspace[]): Promise<void>;
}
export const IWorkspaceStore: ServiceIdentifier<IWorkspaceStore> =
createDecorator<IWorkspaceStore>('workspaceStore');
export const IWorkspacePersistence: ServiceIdentifier<IWorkspacePersistence> =
createDecorator<IWorkspacePersistence>('workspacePersistence');

View file

@ -2,7 +2,7 @@
* `cron` domain (L5) `ISessionCronService` contract.
*
* Session-level scheduling engine for cron tasks. Owns the live task set
* (filtered from `ICronTaskStore` by `sessionId` tag), the polling timer,
* (filtered from `ICronTaskPersistence` by `sessionId` tag), the polling timer,
* and the fire/coalesce/jitter logic. On fire, borrows the main agent's
* `IAgentPromptService` via `IAgentLifecycleService` handle to steer a new
* turn. Bound at Session scope.
@ -12,7 +12,7 @@ import type { ContentPart } from '#/app/llmProtocol';
import { createDecorator } from '#/_base/di';
import type { Turn } from '#/agent/turn';
import type { CronTask, CronTaskInit } from '#/app/cronStore';
import type { CronTask, CronTaskInit } from '#/app/cronPersistence';
export interface CronLoadOptions {
readonly replace?: boolean;

View file

@ -2,9 +2,9 @@
* `cron` domain (L5) `SessionCronService` implementation.
*
* Session-level scheduling engine. Holds the in-memory task map (filtered
* from `ICronTaskStore` by `sessionId` tag), runs the polling timer
* from `ICronTaskPersistence` by `sessionId` tag), runs the polling timer
* (tick / coalesce / jitter / cursor), persists mutations through the
* App-scoped `ICronTaskStore`, mirrors mutations onto `wireRecord` for
* App-scoped `ICronTaskPersistence`, mirrors mutations onto `wireRecord` for
* replay via the main agent's `IAgentRecordService` (cross-scope borrow),
* and steers the main agent through `IAgentPromptService` when a task fires.
* Bound at Session scope.
@ -22,7 +22,7 @@ import { IntervalTimer } from '#/_base/utils';
import { IConfigService } from '#/app/config';
import { ITelemetryService } from '#/app/telemetry';
import { ICronTaskStore, type CronTask, type CronTaskInit } from '#/app/cronStore';
import { ICronTaskPersistence, type CronTask, type CronTaskInit } from '#/app/cronPersistence';
import { ISessionContext } from '#/session/sessionContext';
import { IAgentLifecycleService } from '#/session/agentLifecycle';
import type { ContextMessage } from '#/agent/contextMemory';
@ -98,7 +98,7 @@ export class SessionCronServiceImpl extends Disposable implements ISessionCronSe
constructor(
@ISessionContext private readonly ctx: ISessionContext,
@ICronTaskStore private readonly store: ICronTaskStore,
@ICronTaskPersistence private readonly store: ICronTaskPersistence,
@IAgentLifecycleService private readonly agentLifecycle: IAgentLifecycleService,
@ITelemetryService private readonly telemetry: ITelemetryService,
@IConfigService private readonly config: IConfigService,

View file

@ -2,7 +2,7 @@
* `sessionSkillCatalog` domain (L5) `ISessionSkillCatalog` implementation.
*
* Merges the global catalog (`IGlobalSkillCatalog`) with the project skills
* discovered through `ISkillCatalogStore` for the session's current workDir
* discovered through `ISkillDiscovery` for the session's current workDir
* (`workspaceContext`). Project skills override global skills on name
* collision. `ready` resolves once the first `load()` completes, so consumers
* (e.g. skill activation) can await it instead of racing the asynchronous
@ -18,7 +18,7 @@ import { ISessionWorkspaceContext } from '#/session/workspaceContext';
import { IGlobalSkillCatalog } from '#/app/globalSkillCatalog/globalSkillCatalog';
import { InMemorySkillCatalog } from '#/app/globalSkillCatalog/registry';
import { ISessionSkillCatalog } from './skillCatalog';
import { ISkillCatalogStore } from '#/app/globalSkillCatalog/skillCatalogStore';
import { ISkillDiscovery } from '#/app/globalSkillCatalog/skillDiscovery';
import type { SkillCatalog } from '#/app/globalSkillCatalog/types';
export class SessionSkillCatalogService extends Disposable implements ISessionSkillCatalog {
@ -30,7 +30,7 @@ export class SessionSkillCatalogService extends Disposable implements ISessionSk
constructor(
@IGlobalSkillCatalog private readonly global: IGlobalSkillCatalog,
@ISkillCatalogStore private readonly store: ISkillCatalogStore,
@ISkillDiscovery private readonly store: ISkillDiscovery,
@ISessionWorkspaceContext private readonly workspace: ISessionWorkspaceContext,
@IPluginService private readonly plugins: IPluginService,
) {

View file

@ -15,7 +15,7 @@ import { AgentSystemReminderService } from '#/agent/systemReminder/systemReminde
import { IAgentTodoListService, TODO_LIST_REMINDER_VARIANT } from '#/agent/todoList';
import { AgentTodoListService } from '#/agent/todoList/todoListService';
import { IAgentToolRegistryService } from '#/agent/toolRegistry';
import { IAgentToolStoreService } from '#/agent/toolStore';
import { IAgentToolState } from '#/agent/toolState';
import { IAgentTurnService } from '#/agent/turn';
import { registerContextMemoryServices } from '../contextMemory/stubs';
import { stubLoopWithHooks, stubTurnWithHooks } from '../turn/stubs';
@ -227,7 +227,7 @@ describe('AgentContextInjectorService registration', () => {
reg.definePartialInstance(IAgentProfileService, {
isToolActive: () => false,
});
reg.definePartialInstance(IAgentToolStoreService, {
reg.definePartialInstance(IAgentToolState, {
data: () => ({}),
});
reg.definePartialInstance(IAgentToolRegistryService, {

View file

@ -7,7 +7,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import type { ContentPart } from '#/app/llmProtocol/kosong';
import type { CronTask } from '#/app/cronStore';
import type { CronTask } from '#/app/cronPersistence';
import {
CRON_FIRED,
CRON_MISSED,

View file

@ -1,7 +1,7 @@
import { describe, expect, it } from 'vitest';
import type { CronTask } from '#/app/cronStore';
import { CRON_ID_REGEX, isValidCronTask } from '#/app/cronStore';
import type { CronTask } from '#/app/cronPersistence';
import { CRON_ID_REGEX, isValidCronTask } from '#/app/cronPersistence';
const validTask: CronTask = {
id: '0123abcd',

View file

@ -18,7 +18,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import type { ContentPart } from '#/app/llmProtocol/kosong';
import type { ContextMessage, PromptOrigin } from '#/agent/contextMemory';
import { IAgentPromptService } from '#/agent/prompt';
import type { CronTask } from '#/app/cronStore';
import type { CronTask } from '#/app/cronPersistence';
import { ISessionCronService } from '#/session/cron';
import { IBootstrapService } from '#/app/bootstrap';
import { IAtomicDocumentStore } from '#/app/storage';

View file

@ -8,7 +8,7 @@ import type {
RunnableToolExecution,
ToolExecution,
} from '#/agent/tool';
import type { CronTask, CronTaskInit } from '#/app/cronStore';
import type { CronTask, CronTaskInit } from '#/app/cronPersistence';
import type { ISessionCronService } from '#/session/cron';
import {
computeNextCronRun,

View file

@ -33,7 +33,7 @@ import {
IAgentMicroCompactionService,
IOAuthService,
IAgentProfileService,
IAgentToolStoreService,
IAgentToolState,
} from '#/index';
import { TODO_STORE_KEY } from '#/agent/todoList/tools/todo-list';
@ -2080,7 +2080,7 @@ describe('FullCompaction', () => {
ctx.appendExchange(1, 'old user one', 'old assistant one', 20);
ctx.appendExchange(2, 'recent user two', 'recent assistant two', 80);
ctx.get(IAgentToolStoreService).set(TODO_STORE_KEY, [
ctx.get(IAgentToolState).set(TODO_STORE_KEY, [
{ title: 'Fix the auth bug', status: 'in_progress' },
{ title: 'Add tests', status: 'pending' },
]);

View file

@ -4,9 +4,9 @@ import { tmpdir } from 'node:os';
import { dirname, join } from 'pathe';
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
import { FileSkillCatalogStore } from '#/app/globalSkillCatalog/fileSkillCatalogStore';
import { FileSkillDiscovery } from '#/app/globalSkillCatalog/fileSkillDiscovery';
describe('FileSkillCatalogStore', () => {
describe('FileSkillDiscovery', () => {
let root: string;
beforeEach(async () => {
@ -31,7 +31,7 @@ describe('FileSkillCatalogStore', () => {
await markGitRoot();
await writeSkill('.kimi-code/skills/commit/SKILL.md', 'name: commit\ndescription: commit changes');
const result = await new FileSkillCatalogStore().discoverProject(root);
const result = await new FileSkillDiscovery().discoverProject(root);
expect(result.skills.map((s) => s.name)).toEqual(['commit']);
expect(result.skills[0]?.source).toBe('project');
@ -41,7 +41,7 @@ describe('FileSkillCatalogStore', () => {
await markGitRoot();
await writeSkill('.agents/skills/review/SKILL.md', 'name: review\ndescription: review code');
const result = await new FileSkillCatalogStore().discoverProject(root);
const result = await new FileSkillDiscovery().discoverProject(root);
expect(result.skills.map((s) => s.name)).toEqual(['review']);
});
@ -49,7 +49,7 @@ describe('FileSkillCatalogStore', () => {
it('returns an empty result when no skill directories exist', async () => {
await markGitRoot();
const result = await new FileSkillCatalogStore().discoverProject(root);
const result = await new FileSkillDiscovery().discoverProject(root);
expect(result.skills).toEqual([]);
expect(result.scannedRoots).toEqual([]);
@ -59,7 +59,7 @@ describe('FileSkillCatalogStore', () => {
await markGitRoot();
await writeSkill('.kimi-code/skills/summarize.md', 'name: summarize\ndescription: summarize text');
const result = await new FileSkillCatalogStore().discoverProject(root);
const result = await new FileSkillDiscovery().discoverProject(root);
expect(result.skills.map((s) => s.name)).toEqual(['summarize']);
});
@ -69,7 +69,7 @@ describe('FileSkillCatalogStore', () => {
await writeSkill('.kimi-code/skills/dup/SKILL.md', 'name: dup\ndescription: from brand');
await writeSkill('.agents/skills/dup/SKILL.md', 'name: dup\ndescription: from generic');
const result = await new FileSkillCatalogStore().discoverProject(root);
const result = await new FileSkillDiscovery().discoverProject(root);
expect(result.skills).toHaveLength(1);
expect(result.skills[0]?.description).toBe('from brand');
@ -78,7 +78,7 @@ describe('FileSkillCatalogStore', () => {
it('discovers user skills under homeDir/skills', async () => {
await writeSkill('skills/notes/SKILL.md', 'name: notes\ndescription: personal notes');
const result = await new FileSkillCatalogStore().discoverUser(root, root);
const result = await new FileSkillDiscovery().discoverUser(root, root);
expect(result.skills.map((s) => s.name)).toEqual(['notes']);
expect(result.skills[0]?.source).toBe('user');
@ -95,7 +95,7 @@ describe('FileSkillCatalogStore', () => {
'name: child\ndescription: child skill',
);
const result = await new FileSkillCatalogStore().discoverProject(root);
const result = await new FileSkillDiscovery().discoverProject(root);
const names = result.skills.map((s) => s.name).toSorted();
expect(names).toEqual(['parent', 'parent.child']);
@ -109,7 +109,7 @@ describe('FileSkillCatalogStore', () => {
'name: hidden\ndescription: hidden',
);
const result = await new FileSkillCatalogStore().discoverProject(root);
const result = await new FileSkillDiscovery().discoverProject(root);
expect(result.skills.map((s) => s.name)).not.toContain('hidden');
});

View file

@ -8,9 +8,9 @@ import { ISessionWorkspaceContext } from '#/session/workspaceContext';
import '#/app/globalSkillCatalog';
import '#/session/sessionSkillCatalog';
import '#/agent/skill';;
import { InMemorySkillCatalogStore } from '#/app/globalSkillCatalog/inMemorySkillCatalogStore';
import { InMemorySkillDiscovery } from '#/app/globalSkillCatalog/inMemorySkillDiscovery';
import { ISessionSkillCatalog } from '#/session/sessionSkillCatalog/skillCatalog';
import { ISkillCatalogStore } from '#/app/globalSkillCatalog/skillCatalogStore';
import { ISkillDiscovery } from '#/app/globalSkillCatalog/skillDiscovery';
import type { SkillRoot } from '#/app/globalSkillCatalog/types';
import { stubSkill } from './stubs';
@ -65,12 +65,12 @@ function workspaceStub(workDir: string): {
}
function makeHost(
store: ISkillCatalogStore,
store: ISkillDiscovery,
ws: ISessionWorkspaceContext,
pluginRoots: readonly SkillRoot[] = [],
) {
const host = createScopedTestHost([
stubPair(ISkillCatalogStore, store),
stubPair(ISkillDiscovery, store),
stubPair(IBootstrapService, bootstrapStub),
stubPair(IPluginService, pluginStub(pluginRoots)),
]);
@ -80,7 +80,7 @@ function makeHost(
describe('SessionSkillCatalogService', () => {
it('merges global and project skills; project wins on name collision', async () => {
const store = new InMemorySkillCatalogStore();
const store = new InMemorySkillDiscovery();
store.setUserSkills([
stubSkill('global-only'),
stubSkill('shared', { description: 'from user' }),
@ -104,7 +104,7 @@ describe('SessionSkillCatalogService', () => {
});
it('reload replaces project skills when the workDir changes', async () => {
const store = new InMemorySkillCatalogStore();
const store = new InMemorySkillDiscovery();
store.setUserSkills([stubSkill('global-only')]);
store.setProjectSkills([stubSkill('first')]);
const { stub: ws, setWorkDir } = workspaceStub('/work1');
@ -125,7 +125,7 @@ describe('SessionSkillCatalogService', () => {
});
it('does not reload when the workDir is unchanged', async () => {
const store = new InMemorySkillCatalogStore();
const store = new InMemorySkillDiscovery();
store.setProjectSkills([stubSkill('first')]);
const { stub: ws } = workspaceStub('/work');
const { host, session } = makeHost(store, ws);
@ -147,7 +147,7 @@ describe('SessionSkillCatalogService', () => {
source: 'extra',
plugin: { id: 'demo', instructions: 'Use the demo tools.' },
};
class ExtraRootStore implements ISkillCatalogStore {
class ExtraRootStore implements ISkillDiscovery {
declare readonly _serviceBrand: undefined;
receivedRoots: readonly SkillRoot[] | undefined;
async discoverProject(_workDir: string, extraRoots?: readonly SkillRoot[]) {

View file

@ -7,13 +7,13 @@ import {
TodoListTool,
type TodoItem,
} from '#/agent/todoList/tools/todo-list';
import type { IAgentToolStoreService } from '#/agent/toolStore';
import type { IAgentToolState } from '#/agent/toolState';
import { executeTool } from '../tools/fixtures/execute-tool';
const signal = new AbortController().signal;
function makeStore(initial: readonly TodoItem[] = []): {
readonly store: IAgentToolStoreService;
readonly store: IAgentToolState;
readonly getTodos: () => readonly TodoItem[];
} {
let todos = [...initial];
@ -28,7 +28,7 @@ function makeStore(initial: readonly TodoItem[] = []): {
},
data: () => ({ [TODO_STORE_KEY]: todos }),
hooks: { onUpdated: { register: () => ({ dispose: () => {} }) } },
} as unknown as IAgentToolStoreService,
} as unknown as IAgentToolState,
getTodos: () => todos,
};
}

View file

@ -5,7 +5,7 @@
* GET /files/{file_id} download a file (binary stream)
* DELETE /files/{file_id} delete a file { deleted: true }
*
* Backed by the v2 `IFileStore` (Core scope), which stores bytes in
* Backed by the v2 `IFileService` (Core scope), which stores bytes in
* `IBlobStorage` and the metadata index alongside them. Mirrors the v1 server's
* wire behavior (envelope codes 40407 / 41301, 50 MiB cap, content-disposition)
* but resolves the store through `core.accessor.get` and streams downloads from
@ -17,7 +17,7 @@ import multipart from '@fastify/multipart';
import {
DEFAULT_MAX_UPLOAD_BYTES,
ErrorCodes,
IFileStore,
IFileService,
KimiError,
type Scope,
} from '@moonshot-ai/agent-core-v2';
@ -107,7 +107,7 @@ export function registerFilesRoutes(app: FilesRouteHost, core: Scope): void {
const nameOverride = readFieldString(part.fields['name']);
const expiresInSec = readFieldNumber(part.fields['expires_in_sec']);
const store = core.accessor.get(IFileStore);
const store = core.accessor.get(IFileService);
const partFile = part.file as NodeJS.ReadableStream & { truncated?: boolean };
let busboyTruncated = false;
@ -168,7 +168,7 @@ export function registerFilesRoutes(app: FilesRouteHost, core: Scope): void {
async (req, reply) => {
try {
const { file_id } = req.params;
const store = core.accessor.get(IFileStore);
const store = core.accessor.get(IFileService);
const { meta, stream } = await store.get(file_id);
const r = reply as unknown as FilesReply;
r.type(meta.media_type)
@ -201,7 +201,7 @@ export function registerFilesRoutes(app: FilesRouteHost, core: Scope): void {
async (req, reply) => {
try {
const { file_id } = req.params;
const store = core.accessor.get(IFileStore);
const store = core.accessor.get(IFileService);
await store.delete(file_id);
reply.send(okEnvelope({ deleted: true as const }, req.id));
} catch (err) {

View file

@ -51,7 +51,7 @@ import {
ISessionMetadata,
IAgentSwarmService,
IAgentToolRegistryService,
IAgentToolStoreService,
IAgentToolState,
IAgentUsageService,
ISessionWorkspaceContext,
IWorkspaceRegistry,
@ -222,9 +222,9 @@ export const actionMap: Record<ScopeKind, Record<string, ActionTarget>> = {
'messages:list': { service: IAgentContextMemoryService, method: 'get', readonly: true },
'messages:splice': { service: IAgentContextMemoryService, method: 'splice' },
'toolStore:get': { service: IAgentToolStoreService, method: 'get', readonly: true },
'toolStore:data': { service: IAgentToolStoreService, method: 'data', readonly: true },
'toolStore:set': { service: IAgentToolStoreService, method: 'set' },
'toolStore:get': { service: IAgentToolState, method: 'get', readonly: true },
'toolStore:data': { service: IAgentToolState, method: 'data', readonly: true },
'toolStore:set': { service: IAgentToolState, method: 'set' },
'mcp:list': { service: IAgentMcpService, method: 'list', readonly: true },
'mcp:reconnect': { service: IAgentMcpService, method: 'reconnect' },

View file

@ -13,7 +13,7 @@ import {
} from '@moonshot-ai/protocol';
import { z } from 'zod';
import { DEFAULT_MAX_UPLOAD_BYTES, FileNotFoundError, FileTooLargeError, IFileStore, type IInstantiationService } from '@moonshot-ai/agent-core';
import { DEFAULT_MAX_UPLOAD_BYTES, FileNotFoundError, FileTooLargeError, IFileService, type IInstantiationService } from '@moonshot-ai/agent-core';
import { errEnvelope, okEnvelope } from '../envelope';
import { defineRoute } from '../middleware/defineRoute';
@ -119,7 +119,7 @@ export function registerFilesRoutes(
const nameOverride = readFieldString(part.fields['name']);
const expiresInSec = readFieldNumber(part.fields['expires_in_sec']);
const store = ix.invokeFunction((a) => a.get(IFileStore));
const store = ix.invokeFunction((a) => a.get(IFileService));
const partFile = part.file as NodeJS.ReadableStream & { truncated?: boolean };
let busboyTruncated = false;
@ -178,7 +178,7 @@ export function registerFilesRoutes(
async (req, reply) => {
try {
const { file_id } = req.params;
const store = ix.invokeFunction((a) => a.get(IFileStore));
const store = ix.invokeFunction((a) => a.get(IFileService));
const { meta, blobPath } = await store.get(file_id);
const r = reply as unknown as FilesReply;
r.type(meta.media_type)
@ -212,7 +212,7 @@ export function registerFilesRoutes(
async (req, reply) => {
try {
const { file_id } = req.params;
const store = ix.invokeFunction((a) => a.get(IFileStore));
const store = ix.invokeFunction((a) => a.get(IFileService));
await store.delete(file_id);
reply.send(okEnvelope({ deleted: true as const }, req.id));
} catch (err) {

View file

@ -12,7 +12,7 @@ import {
promptSteerResultSchema,
type PromptSubmission,
} from '@moonshot-ai/protocol';
import { IPromptService, AuthModelNotResolvedError, AuthProvisioningRequiredError, AuthTokenMissingError, AuthTokenUnauthorizedError, PromptAlreadyCompletedError, PromptNotFoundError, SessionBusyError, SessionNotFoundError, FileNotFoundError, IFileStore, type IInstantiationService, type GetResult } from '@moonshot-ai/agent-core';
import { IPromptService, AuthModelNotResolvedError, AuthProvisioningRequiredError, AuthTokenMissingError, AuthTokenUnauthorizedError, PromptAlreadyCompletedError, PromptNotFoundError, SessionBusyError, SessionNotFoundError, FileNotFoundError, IFileService, type IInstantiationService, type GetResult } from '@moonshot-ai/agent-core';
import { z } from 'zod';
@ -126,7 +126,7 @@ export function registerPromptsRoutes(
const result = await ix.invokeFunction(async (a) =>
a.get(IPromptService).submit(
session_id,
await resolvePromptMediaFiles(body, a.get(IFileStore)),
await resolvePromptMediaFiles(body, a.get(IFileService)),
),
);
reply.send(okEnvelope(result, req.id));
@ -249,7 +249,7 @@ export function registerPromptsRoutes(
async function resolvePromptMediaFiles(
body: PromptSubmission,
store: IFileStore,
store: IFileService,
): Promise<PromptSubmission> {
let changed = false;
const content: PromptSubmission['content'] = [];

View file

@ -1,4 +1,4 @@
import { InstantiationService, resolveConfigPath, resolveKimiHome, setUnexpectedErrorHandler, IApprovalService, IAuthSummaryService, IEnvironmentService, IEventService, ICoreProcessService, IModelCatalogService, IMcpService, IMessageService, IOAuthService, IFileStore, IFsGitService, IFsSearchService, IFsService, IFsWatcher, ILogService, IPromptService, IQuestionService, ISessionService, ISkillService, ITaskService, ITerminalService, IToolService, IWorkspaceFsService, IWorkspaceRegistry, FsPathEscapesError, FsWatchLimitError, FsWatcherService, SessionNotFoundError, createConnectionLookup, resolveSafePath, type ServiceIdentifier, type CoreProcessServiceOptions } from '@moonshot-ai/agent-core';
import { InstantiationService, resolveConfigPath, resolveKimiHome, setUnexpectedErrorHandler, IApprovalService, IAuthSummaryService, IEnvironmentService, IEventService, ICoreProcessService, IModelCatalogService, IMcpService, IMessageService, IOAuthService, IFileService, IFsGitService, IFsSearchService, IFsService, IFsWatcher, ILogService, IPromptService, IQuestionService, ISessionService, ISkillService, ITaskService, ITerminalService, IToolService, IWorkspaceFsService, IWorkspaceRegistry, FsPathEscapesError, FsWatchLimitError, FsWatcherService, SessionNotFoundError, createConnectionLookup, resolveSafePath, type ServiceIdentifier, type CoreProcessServiceOptions } from '@moonshot-ai/agent-core';
import { ErrorCode, createAsyncApiDocument } from '@moonshot-ai/protocol';
import Fastify from 'fastify';
import { promises as fspPromises } from 'node:fs';
@ -518,7 +518,7 @@ export async function startServer(opts: ServerStartOptions): Promise<RunningServ
};
wsGw.setFsWatchHandler(fsWatchHandler);
a.get(IFileStore);
a.get(IFileService);
a.get(IWorkspaceRegistry);