/** * DshRoleboxReloader — dsh (DeepSeek Harness) in-process, NON-DESTRUCTIVE role * reload. * * Mirrors the full-reload path of the opencode-only HotReloadService * (`src/core/services/hot-reload-service.ts:299-385`) with the dsh-specific * substitutes for the platform seams that service reaches through * `PluginContext`: * * | HotReloadService (opencode) | dsh substitute | * | ---------------------------------------- | --------------------------------------- | * | `ctx.resolvedRoles` / `ctx.roleFunctionsMap` | the SAME mutable objects injected here | * | `new OpencodeAgentRegistrar()` per reload | the EXISTING {@link DshAgentRegistrar} instance | * | `ctx.core.restartService("dispatch-service")` (:372) | `refreshRoleSnapshotTools` (subtask 3 seam: dispose + re-register the four role-snapshot tools) + `refreshSkills` (skill-catalog invalidation) | * * The dsh platform has no dispatch service to restart: graph nodes and loop * rounds dispatch through {@link DshDispatchAdapter}, which resolves providers * from the registrar at spawn time, so re-syncing the registrar IS the refresh * for dispatch. The two consumers that hold a *captured* snapshot of role * state are the role-snapshot tools (`asset_search` / `asset_inspect` / * `asset_validate` / `reference_search`, built from the roles array) and the * lazy dsh skill provider (which reads the same array at `list()` time). * * ── Why the injected state is mutated IN PLACE ───────────────────────────── * Downstream consumers capture the *references*: the skill provider receives * the roles ARRAY (`DshSkillProviderDeps.roles`), the system-prompt adapter * and the spawn-context provider close over the role-functions Map, and * `resolveAllRoles` only ever ADDS to the shared map (it never clears it, so a * stale entry survives a naive re-resolve). A reload therefore clears and * refills the existing containers instead of replacing them * (`hot-reload-service.ts:327-334`). * * ── Atomicity ────────────────────────────────────────────────────────────── * Discovery, re-resolution and agent sync run against LOCAL containers, so a * failure in any of them leaves the live state untouched. Every mutation after * that point is guarded by an in-memory rollback: if the swap or any post-swap * refresh throws, the previous roles, functions and open-role registry are * restored and the agent catalog is re-synced, so no partially-swapped state * is observable. * * This module performs NO filesystem or git mutation — role configs, skills * and memory files are only ever read. * * @module */ import type { RoleboxDirectories } from "../../factory.ts"; import type { IAgentRegistrar } from "../../ports/agent-registrar.ts"; import type { ResolvedFunction, ResolvedRole } from "../../../types.ts"; /** * Result of {@link DshRoleboxReloader.reload}. Shaped like * `HotReloadResult` (`src/core/services/hot-reload-service.ts:32-44`) so the * web route can report status identically to the opencode tool. */ export interface DshRoleboxReloadResult { success: boolean; /** Set when the reload is disabled via env var (not an error). */ disabled?: boolean; /** Error message when success is false (and not disabled). */ error?: string; /** Number of roles discovered on disk. */ discovered?: number; /** Number of roles successfully resolved. */ resolved?: number; /** Number of discovered roles that failed to resolve. */ skipped?: number; } /** Dependencies for {@link DshRoleboxReloader}. */ export interface DshRoleboxReloaderOptions { /** The role directories the runtime was booted from (factory.ts). */ directories: RoleboxDirectories; /** * The workspace's MUTABLE resolved-roles array — the SAME reference the * runtime handed to every consumer. Mutated in place, never reassigned. */ resolvedRoles: ResolvedRole[]; /** * The workspace's MUTABLE roleId/subagentId → functions map — mutated in * place (cleared + refilled), never reassigned. */ roleFunctionsMap: Map; /** * The EXISTING registrar instance the boot path built. Its `sync` is * diff/idempotent (`agent-registrar.ts:793-842`), so re-registering an * unchanged role is a no-op and only changed/added/removed agents are * touched. */ registrar: IAgentRegistrar; /** * Role-snapshot tool refresh seam (subtask 3): disposes the previously * registered generation of `asset_search` / `asset_inspect` / * `asset_validate` / `reference_search` and registers a fresh one built * from the passed roles. This is the dsh-plugin's * `registerRoleSnapshotTools` handle. */ refreshRoleSnapshotTools: (roles: ResolvedRole[]) => number; /** * Skill-catalog refresh seam: invalidates the dsh `ctx.skills` catalog so * the re-resolved roles are advertised on the next lookup. This is the * dsh-plugin's `refreshSkillCatalog` (a no-op when no skill provider is * registered, e.g. a headless profile). */ refreshSkills: () => void; /** * Optional project-level default role, re-promoted after each reload * (mirrors `dsh-plugin.ts:948-951`). */ defaultRole?: string; } /** * In-process role reloader for the dsh web plugin. Construct once per plugin * boot, then call {@link reload} on demand (the `POST /rolebox/reload` route). */ export declare class DshRoleboxReloader { private readonly directories; private readonly resolvedRoles; private readonly roleFunctionsMap; private readonly registrar; private readonly refreshRoleSnapshotTools; private readonly refreshSkills; private readonly defaultRole; /** `ROLEBOX_HOT_RELOAD` kill switch, evaluated once at construction. */ private readonly disabled; /** Single-flight guard: a second concurrent reload is rejected. */ private isReloading; constructor(options: DshRoleboxReloaderOptions); /** True when the `ROLEBOX_HOT_RELOAD` kill switch disabled this reloader. */ get isDisabled(): boolean; /** * Re-discover and re-resolve every role, then refresh every consumer that * captured the previous role state. * * Never throws: a failure is caught and reported as `{success: false, * error}`, with the previous state preserved. A second concurrent call is * rejected by the single-flight guard. */ reload(): Promise; /** * Full re-discovery + re-resolution (mirrors * `hot-reload-service.ts:299-385`). */ private performFullReload; /** Restore the pre-swap in-memory state, in place. */ private restore; } //# sourceMappingURL=rolebox-reload.d.ts.map