/** * Harness-rules system-prompt injection (visible to the root session AND every dispatched * child — on their own names and layers (the child persona rides the NATIVE * subagent persona channel since plan * Task 3 — no child-scoped * `mstar:role-persona` section exists anymore; duplicate-name throws remain * per name per layer, verified `scope/src/store.ts`). * * Content discipline: * - The section is a POINTER block (presence / enforcement word / resolved * `{HARNESS_DIR}` / one read-mstar-harness-core directive) — deliberately * minimal, never a rules dump (plan pointer-block constraint). The * OUTPUT is zero-complete-`{{...}}`-groups text: dsh system-prompt * renders section AND context text with STRICT `{{variable}}` * interpolation and throws on unknown/malformed/undefined references * (`interpolate` in `@deepseek-ai/dsh-system-prompt`), so every injected * string must carry no complete group. The mechanism is LIVE, not static * : every operator-controlled value embedded below * (harness dir, plan ids, iteration id, lease fields, direction prose) * is passed through `stripInterpolationHazard` — complete `{{…}}` groups * are screened so a hostile value can never break prompt assembly, while * a lone `{{` stays literal prose. * - The harness dir is resolved PER ASSEMBLY from the assembly context's * agent : the * session cwd of the agent whose prompt is being assembled, via * `resolver.forAgent`, with the boot value (`forWorkspace(undefined)`, * the explicit config or null) as the fallback when the assembly carries * no agent. Zero-config deployments (no explicit `harnessDir`, the * probe-discovers-`.mstar/` default) therefore resolve the pointer and * the status context to the session's own workspace instead of rendering * a permanent `none`/`soft`. The enforcement word is LIVE — the section * text is a provider (the plan:policy precedent) that re-reads * `resolveCompassEnforcement` per assembly (the same existing read the * gates and the catalog use — no new config key), so a mid-session * compass soft/hard flip lands on the next assembly without * re-registration, in zero-config and explicit-config deployments alike. * - The context provider reuses the catalog's unified machine-summary * payload (`buildCatalogPayloadWithStore` — the same ACTIVE graph-backed * builder used by the pre-step catalog row) and projects the SLIM digest. * The prompt API's provider is synchronous, so the configured workspace is * resolved once when the service is injected; additional per-assembly * workspaces are loaded asynchronously and cached for their next assembly. * The cache is TTL-refreshed per resolved harness dir. * * Degradation (boot is never affected — the persona channel's contained-degrade * discipline): * - Structural existence check via `ctx.get('systemPrompt')` — the * `ctx.get('agents')` precedent: a DIRECT `ctx.systemPrompt` property * read throws "cannot get property without inject" on a started cordis * fiber when the service is not composed. Absent service → return `false` * + exactly one debug log. * - Registration is deferred through `ctx.inject(['systemPrompt'], …)` * (HMR-safe re-apply): the `section()`/`context()` calls run on the * inject child, and the exact disposers they return are collected on * that child via `systemPromptCtx.effect` — the * registrations therefore unwind with THIS plugin's apply by explicit * ownership, so a re-apply disposes the old registrations before * registering fresh ones (no duplicate-name throw, no stale closure from * the previous apply). A direct global registration through the service * instance without the collected disposers would instead rely on the * cordis traceable-proxy `this.ctx` rebind for ownership — implicit and * version-fragile; the explicit collection removes that dependency. * - Registration errors are contained (warn + return `false`); a throwing * log sink is contained inside the log helper (never-throws invariant); * the collected disposers run inside a try/catch so an exotic disposal * throw can never break the fiber teardown. * * Module boundary: no barrel — the entry imports this module by explicit * relative path and does NOT re-export its public names (plan constraint); * tests import from this module directly. */ import type { Context } from '@deepseek-ai/cordis'; import { type HarnessResolver } from './_shared.ts'; /** Logger label for the harness-prompt injection (dsh logger naming: `/`). */ export declare const HARNESS_PROMPT_LOGGER = "mstar/harness-prompt"; /** The global harness-rules pointer section name (root AND child assemblies). */ export declare const HARNESS_RULES_SECTION_NAME = "mstar:harness-rules"; /** Prompt order of the harness-rules section — after the deployment persona (0) and the child role persona (1), before plan:policy (50). */ export declare const HARNESS_RULES_SECTION_ORDER = 2; /** The engine-status runtime-context contribution name (durable user-role snapshot). */ export declare const ENGINE_STATUS_CONTEXT_NAME = "mstar:engine-status"; /** Prompt order of the engine-status context — first in the runtime snapshot, before the policy sentences (110+). */ export declare const ENGINE_STATUS_CONTEXT_ORDER = 100; /** Harness-prompt log levels the module sink understands. */ export type HarnessPromptLogLevel = 'debug' | 'warn'; /** Module-level harness-prompt log sink — bound by `apply` to `ctx.logger(HARNESS_PROMPT_LOGGER)` (module-sink precedent). */ export type HarnessPromptLogSink = (level: HarnessPromptLogLevel, message: string) => void; /** * Bind the harness-prompt log sink (the entry `apply` binds it to * `ctx.logger(HARNESS_PROMPT_LOGGER)`). Returns the PRIOR sink so a caller * can restore it (test pattern: {@link setDecorationLogger}). */ export declare function setHarnessPromptLogger(sink: HarnessPromptLogSink): HarnessPromptLogSink; /** * Lifecycle hooks for {@link registerHarnessPrompt} — the scheduling-independent * registration signal. `onRegistered` fires ONLY after both the * `mstar:harness-rules` section and the `mstar:engine-status` context have * actually landed (never from the contained failure path); `onDisposed` fires * from the registration disposer after those entries are removed (HMR * re-apply / service removal), so the fallback can return. Both hooks are * contained: a throw is logged and can never change the registration outcome * or escape the fiber teardown. */ export interface HarnessPromptRegistrationHooks { /** Both registrations landed (last successful step of the inject callback). */ onRegistered?: () => void; /** The registrations were disposed; nothing from this registration is live. */ onDisposed?: () => void; } /** * Register the harness-rules pointer section + the engine-status context on * the GLOBAL prompt layer. * * @param ctx - the registrant context (the plugin's apply ctx; unscoped → * the registrations land on the global layer). * @param options.resolver - the per-workspace `{HARNESS_DIR}` resolver. * The BOOT value (`forWorkspace(undefined)`, the explicit config or null) * is the fallback; the section and context providers resolve the harness * dir PER ASSEMBLY from the assembly context's agent (the session * workspace), and the section's enforcement word is re-read from the * compass per assembly — so both stay correct in zero-config deployments * and follow mid-session compass flips. * @param options.onRegistered - fires after BOTH registrations have landed * (the last successful step of the inject callback; a contained * registration failure never calls it). This — not the return value — is * the confirmed-live signal a consumer must base fallback rendering on. * @param options.onDisposed - fires from the collected disposer after the * section and context are removed (HMR re-apply / service removal), so a * consumer's confirmed-live state can return to the fallback. * @returns `true` when the service exists and registration was scheduled; * `false` when `ctx.systemPrompt` is structurally absent (one debug log, * boot unaffected) or the synchronous registration path threw (contained * warn). The actual registration runs in an inject child that settles * ASYNC — a failure there is contained to a warn and cannot be observed * through this return value (`true` only guarantees scheduling, not * landing; `apply` ignores the value, so boot is never affected). Use * {@link HarnessPromptRegistrationHooks} for landed-state decisions. Never * throws. */ export declare function registerHarnessPrompt(ctx: Context, options: { resolver: HarnessResolver; } & HarnessPromptRegistrationHooks): boolean;