@mstar-harness/dsh 2.2.0 → 2.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.i18n.yaml +2 -3
- package/README.md +124 -3
- package/README.zh.md +124 -3
- package/bundle/README.md +11 -1
- package/dist/client/panel/graph/event-log.d.ts +18 -3
- package/dist/client/panel/graph/project-graph.d.ts +40 -14
- package/dist/client/panel/locale.d.ts +1 -1
- package/dist/client.js +131 -17
- package/dist/gates/_shared.d.ts +104 -5
- package/dist/gates/adapter.d.ts +34 -1
- package/dist/gates/agent-flow.d.ts +186 -4
- package/dist/gates/agent-personas.d.ts +44 -0
- package/dist/gates/dispatch.d.ts +64 -2
- package/dist/gates/fallbacks-advisory.d.ts +77 -0
- package/dist/gates/fallbacks-decoration.d.ts +108 -0
- package/dist/gates/fallbacks-probe.d.ts +63 -0
- package/dist/gates/fallbacks-seeds.d.ts +98 -0
- package/dist/gates/goal-bridge.d.ts +151 -0
- package/dist/gates/plan-mode-bridge.d.ts +70 -0
- package/dist/gates/skill-lint.d.ts +4 -3
- package/dist/gates/system-prompt.d.ts +121 -0
- package/dist/gates/workflow-ledger.d.ts +149 -0
- package/dist/gates/workflow-policy.d.ts +203 -0
- package/dist/index.d.ts +10 -4
- package/dist/index.js +1865 -60
- package/dist/types.d.ts +30 -5
- package/harness-agents/architect.md +35 -0
- package/harness-agents/code-reviewer.md +98 -0
- package/harness-agents/frontend-dev.md +33 -0
- package/harness-agents/fullstack-dev-2.md +33 -0
- package/harness-agents/fullstack-dev.md +33 -0
- package/harness-agents/ops-engineer.md +33 -0
- package/harness-agents/product-manager.md +35 -0
- package/harness-agents/project-manager.md +32 -0
- package/harness-agents/prompt-engineer.md +33 -0
- package/harness-agents/qa-engineer.md +35 -0
- package/harness-agents/qc-specialist-2.md +62 -0
- package/harness-agents/qc-specialist-3.md +62 -0
- package/harness-agents/qc-specialist.md +62 -0
- package/harness-agents/writing-specialist.md +35 -0
- package/harness-commands/codebase-audit.md +2 -2
- package/harness-commands/iteration-drive.md +1 -1
- package/harness-skills/mstar-audit/SKILL.md +20 -0
- package/harness-skills/mstar-audit/references/audit-playbook.md +27 -0
- package/harness-skills/mstar-branch-worktree/SKILL.md +19 -2
- package/harness-skills/mstar-coding-behavior/SKILL.md +18 -0
- package/harness-skills/mstar-compound/SKILL.md +4 -0
- package/harness-skills/mstar-compound/references/compound-workflow.md +16 -0
- package/harness-skills/mstar-compound-refresh/SKILL.md +14 -0
- package/harness-skills/mstar-design-md/SKILL.md +1 -1
- package/harness-skills/mstar-dispatch-gates/SKILL.md +5 -1
- package/harness-skills/mstar-host/SKILL.md +20 -2
- package/harness-skills/mstar-iteration/SKILL.md +9 -1
- package/harness-skills/mstar-iteration/references/phase-4-5-pr-delivery.md +1 -0
- package/harness-skills/mstar-phase-gates/SKILL.md +6 -0
- package/harness-skills/mstar-plan-artifacts/SKILL.md +25 -3
- package/harness-skills/mstar-plan-artifacts/references/status-and-residuals.md +10 -1
- package/harness-skills/mstar-plan-conventions/SKILL.md +8 -0
- package/harness-skills/mstar-review-qc/SKILL.md +16 -0
- package/harness-skills/mstar-roles/SKILL.md +14 -1
- package/harness-skills/mstar-roles/references/project-manager/qc-and-residuals.md +2 -1
- package/harness-skills/mstar-roles/references/qc-specialist/deep-review-lenses.md +16 -7
- package/harness-skills/mstar-roles/references/qc-specialist/report-template.md +17 -1
- package/harness-skills/mstar-roles/references/qc-specialist-shared.md +3 -1
- package/harness-skills/mstar-roles/references/writing-specialist.md +33 -0
- package/harness-skills/mstar-sdd/SKILL.md +7 -3
- package/harness-skills/mstar-sdd/references/file-handoffs.md +2 -0
- package/harness-skills/mstar-skill-authoring/SKILL.md +24 -4
- package/harness-skills/mstar-skill-authoring/references/regression-fixation.md +46 -0
- package/harness-skills/mstar-skill-authoring/references/skillsbench-authoring.md +37 -0
- package/harness-skills/mstar-strategy/SKILL.md +14 -0
- package/package.json +7 -4
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Zero-config seed declaration for the OPTIONAL `dsh-llm-fallbacks` plugin
|
|
3
|
+
* (plan `20260816-dsh-b4-seeds` Task 2): when the `llm-fallbacks` service is
|
|
4
|
+
* applied, this module declares the 13 `mode: subagent` mstar roles into the
|
|
5
|
+
* fallbacks seed registry — persona = mirror `description` (verbatim, the
|
|
6
|
+
* SSOT stays `mstar-roles`) + one mandatory-load guide line.
|
|
7
|
+
*
|
|
8
|
+
* Batch assembly (per-apply, re-runnable):
|
|
9
|
+
*
|
|
10
|
+
* 1. `getEffectiveRoles()` readback → the currently-seeded NON-mstar ids are
|
|
11
|
+
* merge-preserved (`{ id: row.id.trim(), persona: row.seedPersona }` —
|
|
12
|
+
* seedPersona, NOT the row persona, so an operator override stays flagged
|
|
13
|
+
* `personaOverridden` upstream). Upstream `declare` REPLACES the whole
|
|
14
|
+
* registry, so without preservation a mstar-only batch would strip
|
|
15
|
+
* preset/companion ids of their seeded annotations (rows remain — R2).
|
|
16
|
+
* 2. `subagentRoleIds()` × `personaFor()` resolve the mstar personas from
|
|
17
|
+
* the `harness-agents/` mirror (the decoration's existing lookup surface;
|
|
18
|
+
* `mode: primary` shells like `project-manager` are excluded).
|
|
19
|
+
* 3. Interpolation gate (HARD): any persona carrying the dsh system-prompt
|
|
20
|
+
* STRICT `{{...}}` hazard is skipped + warned BEFORE `declareSeeds` —
|
|
21
|
+
* never declared, never throws (aligned with `agent-personas.ts`
|
|
22
|
+
* extraction semantics; a mirror default is already rejected at
|
|
23
|
+
* extraction, so the gate's live path is the UNFILTERED readback data
|
|
24
|
+
* of a preserved row).
|
|
25
|
+
*
|
|
26
|
+
* The upstream `SeedDeclareOutcome` is passed through verbatim; the
|
|
27
|
+
* structured `SeedOutcomeView` is this module's own view (the service is a
|
|
28
|
+
* structural parameter — fake-testable, no runtime value import; the type
|
|
29
|
+
* imports are type-only, mirroring `fallbacks-probe.ts` — the bundle keeps
|
|
30
|
+
* ZERO runtime references to `dsh-llm-fallbacks`).
|
|
31
|
+
*
|
|
32
|
+
* Failure semantics: a throwing readback is contained (skip + one warn —
|
|
33
|
+
* probe semantics); a rejecting `declareSeeds` PROPAGATES to the caller —
|
|
34
|
+
* the entry wiring attaches a terminal `.catch` (the upstream preset
|
|
35
|
+
* self-declare pattern), so declare never throws out of `apply`.
|
|
36
|
+
*
|
|
37
|
+
* Module boundary: no barrel — the entry imports this module by explicit
|
|
38
|
+
* relative path; the entry does not re-export it.
|
|
39
|
+
*/
|
|
40
|
+
import type { EffectiveRolesReadback, SeedDeclaration, SeedDeclareOutcome } from 'dsh-llm-fallbacks';
|
|
41
|
+
/** Logger label for the mstar seeds declaration (dsh logger naming: `<scope>/<subject>`). */
|
|
42
|
+
export declare const SEEDS_LOGGER = "mstar/fallbacks-seeds";
|
|
43
|
+
/** Seed-declaration log levels the module sink understands. */
|
|
44
|
+
export type SeedsLogLevel = 'debug' | 'warn' | 'error';
|
|
45
|
+
/** Per-call log sink — the entry binds it to `ctx.logger(SEEDS_LOGGER)`; tests pass a capture sink. */
|
|
46
|
+
export type SeedsLogSink = (level: SeedsLogLevel, message: string) => void;
|
|
47
|
+
/**
|
|
48
|
+
* The consumed service surface — a structural subset of the upstream
|
|
49
|
+
* `FallbacksService` (the two seed methods the declaration flow uses).
|
|
50
|
+
* Fake-testable: tests pass a spy object; the real service is assignable
|
|
51
|
+
* (structural typing anchors the contract against the installed `.d.ts`).
|
|
52
|
+
*/
|
|
53
|
+
export interface SeedsServiceView {
|
|
54
|
+
/** (a) Declare the companion's FULL current seed set (replacement semantics). */
|
|
55
|
+
declareSeeds(seeds: readonly SeedDeclaration[]): Promise<SeedDeclareOutcome>;
|
|
56
|
+
/** (b) Sync readback — effective taxonomy with seed annotations. */
|
|
57
|
+
getEffectiveRoles(): EffectiveRolesReadback;
|
|
58
|
+
}
|
|
59
|
+
/** Options for {@link declareMstarSeeds}. */
|
|
60
|
+
export interface DeclareMstarSeedsOptions {
|
|
61
|
+
/** The `harness-agents/` mirror root; absent → no mstar personas (preserved-only batch). */
|
|
62
|
+
agentsDir: string | undefined;
|
|
63
|
+
/** The module log sink (entry binds `ctx.logger(SEEDS_LOGGER)`). */
|
|
64
|
+
log: SeedsLogSink;
|
|
65
|
+
}
|
|
66
|
+
/** One locally-skipped id with its gate reason (never reached `declareSeeds`). */
|
|
67
|
+
export interface SeedSkipView {
|
|
68
|
+
id: string;
|
|
69
|
+
reason: 'interpolation' | 'no-persona';
|
|
70
|
+
}
|
|
71
|
+
/** One merge-preserved seeded non-mstar id (batch persona = upstream `seedPersona`). */
|
|
72
|
+
export interface PreservedSeedView {
|
|
73
|
+
id: string;
|
|
74
|
+
persona: string;
|
|
75
|
+
}
|
|
76
|
+
/** Structured result of one {@link declareMstarSeeds} call — this module's own view. */
|
|
77
|
+
export interface SeedOutcomeView {
|
|
78
|
+
/** The full declaration batch handed to `declareSeeds` (mstar personas + preserved ids). */
|
|
79
|
+
declared: SeedDeclaration[];
|
|
80
|
+
/** Locally skipped ids (interpolation gate / no usable default) — never declared. */
|
|
81
|
+
skipped: SeedSkipView[];
|
|
82
|
+
/** The seeded non-mstar ids preserved from the readback into the batch. */
|
|
83
|
+
preserved: PreservedSeedView[];
|
|
84
|
+
/** The upstream `SeedDeclareOutcome` — passed through verbatim. */
|
|
85
|
+
outcome: SeedDeclareOutcome;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Declare the mstar subagent seeds: readback → merge-preserve seeded
|
|
89
|
+
* non-mstar ids → resolve mirror personas → interpolation gate → declare.
|
|
90
|
+
* Idempotent by construction (the same inputs produce the same batch; the
|
|
91
|
+
* upstream manager's no-delta check skips the settings write).
|
|
92
|
+
*
|
|
93
|
+
* @param service - the structural seed surface (real service or test fake).
|
|
94
|
+
* @param options - mirror root + log sink.
|
|
95
|
+
* @returns the structured outcome view; rejects only when `declareSeeds`
|
|
96
|
+
* itself rejects (the wiring attaches the terminal catch).
|
|
97
|
+
*/
|
|
98
|
+
export declare function declareMstarSeeds(service: SeedsServiceView, options: DeclareMstarSeedsOptions): Promise<SeedOutcomeView>;
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
2
|
+
import type { Config, HarnessResolver } from './_shared.ts';
|
|
3
|
+
/** Logger label for the goal bridge (dsh logger naming: `<scope>/<subject>`). */
|
|
4
|
+
export declare const GOAL_BRIDGE_LOGGER = "mstar/goal-bridge";
|
|
5
|
+
/**
|
|
6
|
+
* Flat `maxGoalRounds` config fallback (architect decision — plan
|
|
7
|
+
* `20260816-dsh-nb2-goal-bridge`): 256, aligned with the GoalService default
|
|
8
|
+
* (`goal/src/index.ts:187`) and ralph `maxRounds` (`tool-ralph/src/index.ts:37`).
|
|
9
|
+
*/
|
|
10
|
+
export declare const DEFAULT_MAX_GOAL_ROUNDS = 256;
|
|
11
|
+
/** Consumer log levels the module sink understands. */
|
|
12
|
+
export type GoalBridgeLogLevel = 'debug' | 'warn';
|
|
13
|
+
/** Module-level consumer log sink — bound by `apply` to `ctx.logger(GOAL_BRIDGE_LOGGER)` (agent-flow ledger precedent). */
|
|
14
|
+
export type GoalBridgeLogSink = (level: GoalBridgeLogLevel, message: string) => void;
|
|
15
|
+
/**
|
|
16
|
+
* Bind the goal-bridge log sink (the entry `apply` binds it to
|
|
17
|
+
* `ctx.logger(GOAL_BRIDGE_LOGGER)`). Returns the PRIOR sink so a caller can
|
|
18
|
+
* restore it (test pattern: agent-flow `setAgentFlowLogger`).
|
|
19
|
+
*/
|
|
20
|
+
export declare function setGoalBridgeLogger(sink: GoalBridgeLogSink): GoalBridgeLogSink;
|
|
21
|
+
/**
|
|
22
|
+
* Root-agent discriminator (T1-verified; shared with the planMode bridge via
|
|
23
|
+
* explicit no-barrel import): `header.parentSession === undefined` ⇒
|
|
24
|
+
* root-like. Conversation forks also carry `parentSession` (seed lineage) →
|
|
25
|
+
* conservatively excluded from the goal mirror (accepted boundary).
|
|
26
|
+
*/
|
|
27
|
+
export declare function isRootLikeAgent(agent: unknown): boolean;
|
|
28
|
+
/** CAS identity for one exact goal revision (upstream `GoalRef`). */
|
|
29
|
+
export interface GoalRefView {
|
|
30
|
+
readonly id: string;
|
|
31
|
+
readonly revision: number;
|
|
32
|
+
}
|
|
33
|
+
/** The one goal surface the bridge reads (`GoalSnapshot` fields used by the mirror). */
|
|
34
|
+
export interface GoalView extends GoalRefView {
|
|
35
|
+
readonly objective: string;
|
|
36
|
+
readonly phase: string;
|
|
37
|
+
readonly maxGoalRounds: number;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Minimal structural view of the goals service the bridge consumes
|
|
41
|
+
* (`@deepseek-ai/dsh-goal` `GoalService` — every method is agent-scoped;
|
|
42
|
+
* the runtime read is `ctx.get('goals')` without the inject requirement,
|
|
43
|
+
* same pattern as the probe's service view). `create` throws
|
|
44
|
+
* `GOAL_ALREADY_EXISTS` on a live non-complete goal and REPLACES a
|
|
45
|
+
* completed one ("A completed goal may be replaced" — `goal/src/
|
|
46
|
+
* index.ts:244-257`); `complete` is a CAS by `{ id, revision }`
|
|
47
|
+
* (`GOAL_STALE_REVISION` on stale). The drift path uses complete+create
|
|
48
|
+
* (never `edit`) so each new iteration gets a FRESH goal with a clean
|
|
49
|
+
* round budget (plan QC fix wave — qc2 W-1 / qc3 F-001/F-008).
|
|
50
|
+
*/
|
|
51
|
+
export interface GoalsServiceView {
|
|
52
|
+
get(agent: unknown): GoalView | undefined;
|
|
53
|
+
create(agent: unknown, request: {
|
|
54
|
+
objective: string;
|
|
55
|
+
maxGoalRounds?: number;
|
|
56
|
+
}): unknown;
|
|
57
|
+
complete(agent: unknown, ref: GoalRefView): unknown;
|
|
58
|
+
}
|
|
59
|
+
/** The `agents` service surface the `subagent/start` root walk reads. */
|
|
60
|
+
interface AgentsView {
|
|
61
|
+
get(id: string): unknown;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* The mirrored goal objective: the COMPLETE iteration flow with the exit
|
|
65
|
+
* definition (mstar-host `/goal` rule — advancing an iteration means the
|
|
66
|
+
* entire flow, never a sub-stage). Session-level text only — `status.json`
|
|
67
|
+
* stays the harness SSOT.
|
|
68
|
+
*/
|
|
69
|
+
export declare function iterationGoalObjective(iterationId: string): string;
|
|
70
|
+
/**
|
|
71
|
+
* Locate the steering iteration compass (mirror of the engine's
|
|
72
|
+
* `resolveCompassEnforcement` scan + the catalog's `steeringCompassPath`):
|
|
73
|
+
* the FIRST `{ITERATION_DIR}/<id>/delivery-compass.md` whose frontmatter
|
|
74
|
+
* `status` is `active` or `locked` — the directory name IS the iteration id
|
|
75
|
+
* (plan-conventions `{ITERATION_DIR}/<id>/`). Completed/status-less/archived
|
|
76
|
+
* compasses do not steer. Silent on any read failure (advisory degrade).
|
|
77
|
+
* Shared with the planMode bridge via explicit no-barrel import (Task 4b —
|
|
78
|
+
* the same "is an active iteration steering" read).
|
|
79
|
+
* @param harnessDir - the resolved `{HARNESS_DIR}`.
|
|
80
|
+
*/
|
|
81
|
+
export declare function steeringCompass(harnessDir: string): {
|
|
82
|
+
iterationId: string;
|
|
83
|
+
} | undefined;
|
|
84
|
+
/** Inputs of the mirror: the structural goals view, the per-workspace resolver, and the resolved round cap. */
|
|
85
|
+
export interface MirrorIterationGoalInput {
|
|
86
|
+
resolver: HarnessResolver;
|
|
87
|
+
/** The structural goals view (`ctx.get('goals')`); absent → the mirror is inert. */
|
|
88
|
+
goals?: GoalsServiceView;
|
|
89
|
+
/** The resolved `maxGoalRounds` (flat config key, absent → {@link DEFAULT_MAX_GOAL_ROUNDS}). */
|
|
90
|
+
maxGoalRounds: number;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Mirror the steering iteration objective into the goal of ONE agent.
|
|
94
|
+
* Root-like agent (`header.parentSession === undefined`) + active iteration
|
|
95
|
+
* (compass `status: active|locked`): `get` → absent → `create` (get-先行 —
|
|
96
|
+
* no `GOAL_ALREADY_EXISTS`); present with drifted objective → REPLACE with
|
|
97
|
+
* a fresh goal for the new iteration (see {@link replaceDriftedGoal} — a
|
|
98
|
+
* completed goal is `create`d directly, a live goal is `complete`d first;
|
|
99
|
+
* the new goal starts with a CLEAN round budget); present with the matching
|
|
100
|
+
* objective → no-op (idempotent decision-point re-evaluation). ONE stale
|
|
101
|
+
* re-read retry; a second stale failure is warned and abandoned —
|
|
102
|
+
* goal-service-side concurrency is rare.
|
|
103
|
+
*
|
|
104
|
+
* @returns `true` when the mirror is ensured for this agent (created,
|
|
105
|
+
* replaced, or already in place); `false` when not applicable or a
|
|
106
|
+
* contained failure occurred. Never throws — the caller's listener stays
|
|
107
|
+
* contained.
|
|
108
|
+
*/
|
|
109
|
+
export declare function mirrorIterationGoal(agent: unknown, input: MirrorIterationGoalInput): boolean;
|
|
110
|
+
/**
|
|
111
|
+
* Resolve the ROOT agent of a published child via the `parentSession` walk
|
|
112
|
+
* (upstream `subagent/src/continuation.ts:819-831` precedent): in-process
|
|
113
|
+
* subagent children stamp `header.parentSession` = the parent SESSION id,
|
|
114
|
+
* which IS the parent agent id (a session per agent); the walk stops at the
|
|
115
|
+
* first root-like ancestor. `undefined` when unresolvable (fork lineage,
|
|
116
|
+
* non-in-process provider, registry gap, or a cycle) — the decision point
|
|
117
|
+
* then silently skips. Cycle guard: a `seen` set over visited session ids
|
|
118
|
+
* (the upstream `liveLineage` guard — plan QC fix wave qc2 W-2 / qc3
|
|
119
|
+
* F-003) breaks on ANY revisited id — a 1-hop self-loop, a 2+ hop cycle
|
|
120
|
+
* (A→B→A), or a longer malformed lineage — instead of spinning forever on
|
|
121
|
+
* the synchronous `subagent/start` decision-point listeners (reachable via
|
|
122
|
+
* HMR remounts, resumed/forked sessions with stale headers, or a future
|
|
123
|
+
* host change). Shared with the planMode bridge via explicit no-barrel
|
|
124
|
+
* import (Task 4b — the same `subagent/start` decision-point root walk).
|
|
125
|
+
*/
|
|
126
|
+
export declare function rootAgentOf(agent: unknown, agents: AgentsView): unknown | undefined;
|
|
127
|
+
/**
|
|
128
|
+
* Register the goal bridge: an `agent/session-start` listener (root filter
|
|
129
|
+
* inside the mirror — root and children alike fire, `runtime-types.ts:217`)
|
|
130
|
+
* plus a decision-point re-evaluation on `subagent/start` (the existing
|
|
131
|
+
* decision point — index.ts decoration slot), resolving the delegating ROOT
|
|
132
|
+
* via the `parentSession` walk — the two mirror edges are idempotent (get +
|
|
133
|
+
* compare when the mirror is in place — no churn) — plus a THIRD, advisory
|
|
134
|
+
* listener on the `session/event` firehose (Task 3): a `goal/change`
|
|
135
|
+
* envelope whose goal is blocked logs ONE warn (code + objective summary +
|
|
136
|
+
* `{HARNESS_DIR}/status.json` residual pointer) with ZERO harness writes
|
|
137
|
+
* (the one-way mirror; see {@link warnBlockedGoal}). The goals service is an
|
|
138
|
+
* OPTIONAL seam (`ctx.get('goals')` structural read): absent → ONE debug log
|
|
139
|
+
* + the mirror stays inert, never a boot failure — the blocked advisory is
|
|
140
|
+
* independent of it (it only needs the firehose + resolver). Every listener
|
|
141
|
+
* body is try/catch-contained.
|
|
142
|
+
*
|
|
143
|
+
* @param ctx - the plugin's registrant context (the app composition root).
|
|
144
|
+
* @param resolver - the shared per-workspace `{HARNESS_DIR}` resolver.
|
|
145
|
+
* @param config - validated plugin configuration (flat `maxGoalRounds`,
|
|
146
|
+
* absent → {@link DEFAULT_MAX_GOAL_ROUNDS}; a non-positive / non-integer
|
|
147
|
+
* value warns once and falls back to the default — see
|
|
148
|
+
* {@link resolveGoalRounds}).
|
|
149
|
+
*/
|
|
150
|
+
export declare function registerGoalBridge(ctx: Context, resolver: HarnessResolver, config: Config): void;
|
|
151
|
+
export {};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
2
|
+
import type { HarnessResolver } from './_shared.ts';
|
|
3
|
+
/** Logger label for the planMode bridge (dsh logger naming: `<scope>/<subject>`). */
|
|
4
|
+
export declare const PLAN_MODE_BRIDGE_LOGGER = "mstar/plan-mode-bridge";
|
|
5
|
+
/** Consumer log levels the module sink understands. */
|
|
6
|
+
export type PlanModeBridgeLogLevel = 'debug' | 'warn';
|
|
7
|
+
/** Module-level consumer log sink — bound by `apply` to `ctx.logger(PLAN_MODE_BRIDGE_LOGGER)` (goal-bridge precedent). */
|
|
8
|
+
export type PlanModeBridgeLogSink = (level: PlanModeBridgeLogLevel, message: string) => void;
|
|
9
|
+
/**
|
|
10
|
+
* Bind the planMode-bridge log sink (the entry `apply` binds it to
|
|
11
|
+
* `ctx.logger(PLAN_MODE_BRIDGE_LOGGER)`). Returns the PRIOR sink so a caller
|
|
12
|
+
* can restore it (test pattern: goal-bridge `setGoalBridgeLogger`).
|
|
13
|
+
*/
|
|
14
|
+
export declare function setPlanModeBridgeLogger(sink: PlanModeBridgeLogSink): PlanModeBridgeLogSink;
|
|
15
|
+
/**
|
|
16
|
+
* Minimal structural view of the planMode service the bridge consumes
|
|
17
|
+
* (`@deepseek-ai/dsh-plan-mode` `PlanModeController` — every method is
|
|
18
|
+
* agent-scoped; the runtime read is `ctx.get('planMode')` without the
|
|
19
|
+
* inject requirement, same pattern as the goal bridge). `set` is idempotent:
|
|
20
|
+
* `'noop'` when the target already matches (`plan-mode/src/index.ts:425-445`).
|
|
21
|
+
*/
|
|
22
|
+
export interface PlanModeServiceView {
|
|
23
|
+
get(agent: unknown): {
|
|
24
|
+
active?: unknown;
|
|
25
|
+
pending?: unknown;
|
|
26
|
+
} | undefined;
|
|
27
|
+
set(agent: unknown, active: boolean): unknown;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* The planMode target for one harness: `true` iff an active iteration steers
|
|
31
|
+
* (compass `status: active|locked`) AND a Prepare window exists (≥1 plan
|
|
32
|
+
* row `Todo`). No active iteration / no Prepare window → `false` (plan mode
|
|
33
|
+
* OFF — the host default).
|
|
34
|
+
* @param harnessDir - the resolved `{HARNESS_DIR}`.
|
|
35
|
+
*/
|
|
36
|
+
export declare function planModeTarget(harnessDir: string): boolean;
|
|
37
|
+
/** Inputs of the sync: the structural planMode view and the per-workspace resolver. */
|
|
38
|
+
export interface PlanModeSyncInput {
|
|
39
|
+
resolver: HarnessResolver;
|
|
40
|
+
/** The structural planMode view (`ctx.get('planMode')`); absent → the sync is inert. */
|
|
41
|
+
planMode?: PlanModeServiceView;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Mirror the harness Prepare state into the planMode selection of ONE agent:
|
|
45
|
+
* root-like agent (`header.parentSession === undefined`) → resolve the
|
|
46
|
+
* workspace → compute {@link planModeTarget} → `planMode.set(agent, target)`.
|
|
47
|
+
* The service's `'noop'` return makes repeated evaluation at multiple
|
|
48
|
+
* decision points churn-free (no new `plan/mode` event when already in
|
|
49
|
+
* target). Non-root agent / unresolvable harness / missing planMode service
|
|
50
|
+
* → no set.
|
|
51
|
+
*
|
|
52
|
+
* @returns `true` when the sync ran for this agent (set called); `false`
|
|
53
|
+
* when not applicable or a contained failure occurred. Never throws — the
|
|
54
|
+
* caller's listener stays contained.
|
|
55
|
+
*/
|
|
56
|
+
export declare function syncPlanMode(agent: unknown, input: PlanModeSyncInput): boolean;
|
|
57
|
+
/**
|
|
58
|
+
* Register the planMode bridge: an `agent/session-start` listener (root
|
|
59
|
+
* filter inside — root and children alike fire, `runtime-types.ts:217`) plus
|
|
60
|
+
* the EXISTING `subagent/start` decision point (the goal-bridge precedent),
|
|
61
|
+
* resolving the delegating ROOT via the shared `parentSession` walk — the
|
|
62
|
+
* two edges are idempotent (`'noop'` when already in target — no churn). The
|
|
63
|
+
* planMode service is an OPTIONAL seam (`ctx.get('planMode')` structural
|
|
64
|
+
* read): absent → ONE debug log + the bridge stays inert, never a boot
|
|
65
|
+
* failure. Every listener body is try/catch-contained.
|
|
66
|
+
*
|
|
67
|
+
* @param ctx - the plugin's registrant context (the app composition root).
|
|
68
|
+
* @param resolver - the shared per-workspace `{HARNESS_DIR}` resolver.
|
|
69
|
+
*/
|
|
70
|
+
export declare function registerPlanModeBridge(ctx: Context, resolver: HarnessResolver): void;
|
|
@@ -52,9 +52,10 @@ export declare class SkillLintVetoError extends Error {
|
|
|
52
52
|
}
|
|
53
53
|
/**
|
|
54
54
|
* Lint one SKILL.md document with the engine skill-authoring lints
|
|
55
|
-
* (`lintFrontmatter` + `lintFiveQuestion`
|
|
56
|
-
*
|
|
57
|
-
* `skill-authoring.five-question.*`
|
|
55
|
+
* (`lintFrontmatter` + `lintFiveQuestion` + `findEphemeralCitations` — the
|
|
56
|
+
* CLI `mstar skill lint` combination plus the ephemeral-citation gate;
|
|
57
|
+
* violation codes `lint.frontmatter.*` / `skill-authoring.five-question.*`
|
|
58
|
+
* / `skill.ephemeral.*`). Pure: no enforcement, no I/O.
|
|
58
59
|
* @param doc - the full SKILL.md text.
|
|
59
60
|
*/
|
|
60
61
|
export declare function lintSkillDoc(doc: string): GateResult;
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Harness-rules system-prompt injection (plan `20260816-dsh-nb1-systemprompt`
|
|
3
|
+
* Task 2): the root session's ONE `mstar:harness-rules` pointer section plus
|
|
4
|
+
* the `mstar:engine-status` runtime-context summary, both registered on the
|
|
5
|
+
* GLOBAL prompt layer — visible to the root session AND every dispatched
|
|
6
|
+
* child — without touching the child-scoped `mstar:role-persona` section
|
|
7
|
+
* (fallbacks-decoration; distinct name, distinct layer — duplicate-name
|
|
8
|
+
* throws are per name per layer, verified `scope/src/store.ts`).
|
|
9
|
+
*
|
|
10
|
+
* Content discipline:
|
|
11
|
+
* - The section is a POINTER block (presence / enforcement word / resolved
|
|
12
|
+
* `{HARNESS_DIR}` / one read-mstar-harness-core directive) — deliberately
|
|
13
|
+
* minimal, never a rules dump (plan pointer-block constraint). The
|
|
14
|
+
* OUTPUT is zero-complete-`{{...}}`-groups text: dsh system-prompt
|
|
15
|
+
* renders section AND context text with STRICT `{{variable}}`
|
|
16
|
+
* interpolation and throws on unknown/malformed/undefined references
|
|
17
|
+
* (`interpolate` in `@deepseek-ai/dsh-system-prompt`), so every injected
|
|
18
|
+
* string must carry no complete group. The mechanism is LIVE, not static
|
|
19
|
+
* (plan QC fix wave W-1): every operator-controlled value embedded below
|
|
20
|
+
* (harness dir, plan ids, iteration id, lease fields, direction prose)
|
|
21
|
+
* is passed through `stripInterpolationHazard` — complete `{{…}}` groups
|
|
22
|
+
* are screened so a hostile value can never break prompt assembly, while
|
|
23
|
+
* a lone `{{` stays literal prose.
|
|
24
|
+
* - The harness dir is resolved PER ASSEMBLY from the assembly context's
|
|
25
|
+
* agent (plan QC fix wave W-2 — the catalog pre-step precedent): the
|
|
26
|
+
* session cwd of the agent whose prompt is being assembled, via
|
|
27
|
+
* `resolver.forAgent`, with the boot value (`forWorkspace(undefined)`,
|
|
28
|
+
* the explicit config or null) as the fallback when the assembly carries
|
|
29
|
+
* no agent. Zero-config deployments (no explicit `harnessDir`, the
|
|
30
|
+
* probe-discovers-`.mstar/` default) therefore resolve the pointer and
|
|
31
|
+
* the status context to the session's own workspace instead of rendering
|
|
32
|
+
* a permanent `none`/`soft`. The enforcement word is LIVE — the section
|
|
33
|
+
* text is a provider (the plan:policy precedent) that re-reads
|
|
34
|
+
* `resolveCompassEnforcement` per assembly (the same existing read the
|
|
35
|
+
* gates and the catalog use — no new config key), so a mid-session
|
|
36
|
+
* compass soft/hard flip lands on the next assembly without
|
|
37
|
+
* re-registration, in zero-config and explicit-config deployments alike.
|
|
38
|
+
* - The context provider reuses the catalog's unified machine-summary
|
|
39
|
+
* source (`buildCatalogSources` — the SAME builder the engine-status
|
|
40
|
+
* pre-step catalog row uses) and projects a BOUNDED subset: watermark +
|
|
41
|
+
* iteration gate + compact state line. Full status.json content
|
|
42
|
+
* (residual detail, agent-flow events, knowledge digest, branch/policy
|
|
43
|
+
* anchors) stays out. The build is TTL-memoized PER RESOLVED HARNESS DIR
|
|
44
|
+
* (`DEFAULT_CATALOG_TTL_MS`) so the per-assembly hot path does not
|
|
45
|
+
* re-read status.json / the compass / the ledger on every prompt
|
|
46
|
+
* assembly (the catalog's documented staleness tradeoff).
|
|
47
|
+
*
|
|
48
|
+
* Degradation (boot is never affected — the decoration's contained-degrade
|
|
49
|
+
* discipline):
|
|
50
|
+
* - Structural existence check via `ctx.get('systemPrompt')` — the
|
|
51
|
+
* `ctx.get('agents')` precedent: a DIRECT `ctx.systemPrompt` property
|
|
52
|
+
* read throws "cannot get property without inject" on a started cordis
|
|
53
|
+
* fiber when the service is not composed. Absent service → return `false`
|
|
54
|
+
* + exactly one debug log.
|
|
55
|
+
* - Registration is deferred through `ctx.inject(['systemPrompt'], …)`
|
|
56
|
+
* (HMR-safe re-apply): the `section()`/`context()` calls run on the
|
|
57
|
+
* inject child, and the exact disposers they return are collected on
|
|
58
|
+
* that child via `systemPromptCtx.effect` (plan QC fix wave W-HMR) — the
|
|
59
|
+
* registrations therefore unwind with THIS plugin's apply by explicit
|
|
60
|
+
* ownership, so a re-apply disposes the old registrations before
|
|
61
|
+
* registering fresh ones (no duplicate-name throw, no stale closure from
|
|
62
|
+
* the previous apply). A direct global registration through the service
|
|
63
|
+
* instance without the collected disposers would instead rely on the
|
|
64
|
+
* cordis traceable-proxy `this.ctx` rebind for ownership — implicit and
|
|
65
|
+
* version-fragile; the explicit collection removes that dependency.
|
|
66
|
+
* - Registration errors are contained (warn + return `false`); a throwing
|
|
67
|
+
* log sink is contained inside the log helper (never-throws invariant);
|
|
68
|
+
* the collected disposers run inside a try/catch so an exotic disposal
|
|
69
|
+
* throw can never break the fiber teardown.
|
|
70
|
+
*
|
|
71
|
+
* Module boundary: no barrel — the entry imports this module by explicit
|
|
72
|
+
* relative path and does NOT re-export its public names (plan constraint);
|
|
73
|
+
* tests import from this module directly.
|
|
74
|
+
*/
|
|
75
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
76
|
+
import { type HarnessResolver } from './_shared.ts';
|
|
77
|
+
/** Logger label for the harness-prompt injection (dsh logger naming: `<scope>/<subject>`). */
|
|
78
|
+
export declare const HARNESS_PROMPT_LOGGER = "mstar/harness-prompt";
|
|
79
|
+
/** The global harness-rules pointer section name (root AND child assemblies). */
|
|
80
|
+
export declare const HARNESS_RULES_SECTION_NAME = "mstar:harness-rules";
|
|
81
|
+
/** Prompt order of the harness-rules section — after the deployment persona (0) and the child role persona (1), before plan:policy (50). */
|
|
82
|
+
export declare const HARNESS_RULES_SECTION_ORDER = 2;
|
|
83
|
+
/** The engine-status runtime-context contribution name (durable user-role snapshot). */
|
|
84
|
+
export declare const ENGINE_STATUS_CONTEXT_NAME = "mstar:engine-status";
|
|
85
|
+
/** Prompt order of the engine-status context — first in the runtime snapshot, before the policy sentences (110+). */
|
|
86
|
+
export declare const ENGINE_STATUS_CONTEXT_ORDER = 100;
|
|
87
|
+
/** Harness-prompt log levels the module sink understands. */
|
|
88
|
+
export type HarnessPromptLogLevel = 'debug' | 'warn';
|
|
89
|
+
/** Module-level harness-prompt log sink — bound by `apply` to `ctx.logger(HARNESS_PROMPT_LOGGER)` (decoration precedent). */
|
|
90
|
+
export type HarnessPromptLogSink = (level: HarnessPromptLogLevel, message: string) => void;
|
|
91
|
+
/**
|
|
92
|
+
* Bind the harness-prompt log sink (the entry `apply` binds it to
|
|
93
|
+
* `ctx.logger(HARNESS_PROMPT_LOGGER)`). Returns the PRIOR sink so a caller
|
|
94
|
+
* can restore it (test pattern: {@link setDecorationLogger}).
|
|
95
|
+
*/
|
|
96
|
+
export declare function setHarnessPromptLogger(sink: HarnessPromptLogSink): HarnessPromptLogSink;
|
|
97
|
+
/**
|
|
98
|
+
* Register the harness-rules pointer section + the engine-status context on
|
|
99
|
+
* the GLOBAL prompt layer.
|
|
100
|
+
*
|
|
101
|
+
* @param ctx - the registrant context (the plugin's apply ctx; unscoped →
|
|
102
|
+
* the registrations land on the global layer).
|
|
103
|
+
* @param options.resolver - the per-workspace `{HARNESS_DIR}` resolver.
|
|
104
|
+
* The BOOT value (`forWorkspace(undefined)`, the explicit config or null)
|
|
105
|
+
* is the fallback; the section and context providers resolve the harness
|
|
106
|
+
* dir PER ASSEMBLY from the assembly context's agent (the session
|
|
107
|
+
* workspace), and the section's enforcement word is re-read from the
|
|
108
|
+
* compass per assembly — so both stay correct in zero-config deployments
|
|
109
|
+
* and follow mid-session compass flips.
|
|
110
|
+
* @returns `true` when the service exists and registration was scheduled;
|
|
111
|
+
* `false` when `ctx.systemPrompt` is structurally absent (one debug log,
|
|
112
|
+
* boot unaffected) or the synchronous registration path threw (contained
|
|
113
|
+
* warn). The actual registration runs in an inject child that settles
|
|
114
|
+
* ASYNC — a failure there is contained to a warn and cannot be observed
|
|
115
|
+
* through this return value (`true` only guarantees scheduling, not
|
|
116
|
+
* landing; `apply` ignores the value, so boot is never affected). Never
|
|
117
|
+
* throws.
|
|
118
|
+
*/
|
|
119
|
+
export declare function registerHarnessPrompt(ctx: Context, options: {
|
|
120
|
+
resolver: HarnessResolver;
|
|
121
|
+
}): boolean;
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workflow-ledger session-event consumer (plan `20260815-dsh-workflow-ledger`
|
|
3
|
+
* Task 3 — the W-B2 producer half).
|
|
4
|
+
*
|
|
5
|
+
* Source of record: the durable `tool-workflow/*` session events appended
|
|
6
|
+
* into the CALLING PARENT session's log (top-level runs only — nested
|
|
7
|
+
* transport calls record nothing upstream; Task 1 seam notes §4). The
|
|
8
|
+
* consumer has THREE halves (the architect-verified seam):
|
|
9
|
+
* 1. COLD SCAN at apply — iterate `ctx.get('sessions').list()`, read each
|
|
10
|
+
* session's `events` snapshot, and record any `tool-workflow/*` rows
|
|
11
|
+
* already present. Constructor-seeded events (replay/resume/fork) NEVER
|
|
12
|
+
* publish on the `session/event` firehose (`firstLiveSeq`), so without
|
|
13
|
+
* the cold scan pre-restart runs would be invisible.
|
|
14
|
+
* 2. LIVE FIREHOSE — `ctx.events.on('session/event', …)`: the post-commit
|
|
15
|
+
* append feed, delivered to ALL sessions for a root-context listener
|
|
16
|
+
* (scope-null event, untagged listeners admitted).
|
|
17
|
+
* 3. SESSION-CREATED BACKFILL — `ctx.events.on('session/created', …)`: a
|
|
18
|
+
* session created AFTER apply with a constructor-seeded log (resumed /
|
|
19
|
+
* forked conversation — `session/created` fires after the seed enters
|
|
20
|
+
* the log, upstream `session/src/index.ts:961-995`) gets its snapshot
|
|
21
|
+
* cold-scanned ONCE on the creation announcement, closing the
|
|
22
|
+
* late-seeded-session gap (qc3 S-304 / qc2 W-1a).
|
|
23
|
+
*
|
|
24
|
+
* DEDUPE (qc2 W-1 / qc3 F-301 fix-wave): ONE DURABLE per-session watermark —
|
|
25
|
+
* the next expected envelope `seq` (session-log position) — persisted to
|
|
26
|
+
* `{HARNESS_DIR}/workflow-ledger-cursors.json` (a small bounded sidecar next
|
|
27
|
+
* to `agent-flow.jsonl`, written atomically temp-file + rename through the
|
|
28
|
+
* same containment discipline). The watermark is consulted AND advanced by
|
|
29
|
+
* every scan (cold / created-backfill / live): envelopes with `seq` below it
|
|
30
|
+
* were already recorded — across cold+live overlap AND across plugin
|
|
31
|
+
* re-applies (a re-registration no longer re-records the same live
|
|
32
|
+
* sessions). The watermark advances only AFTER the ledger row appended
|
|
33
|
+
* successfully (qc3 R-401 — a failing append leaves the cursor behind, so
|
|
34
|
+
* the row is re-attempted at the next scan, never permanently lost). The
|
|
35
|
+
* in-memory Map is the durable file's mirror (module-level
|
|
36
|
+
* cache, bounded by the session cap); any watermark read/write failure
|
|
37
|
+
* degrades to in-memory-only with one warn — a ledger row is never lost and
|
|
38
|
+
* the workflow run is never affected.
|
|
39
|
+
*
|
|
40
|
+
* Mapping (Task 2 schema): `tool-workflow/run-start` → `workflow-run`
|
|
41
|
+
* (`agent` = the carrying parent session id), `tool-workflow/agent-start` →
|
|
42
|
+
* `workflow-agent` (`childId` preserved), `tool-workflow/run-end` →
|
|
43
|
+
* `workflow-run-end`. `tool-workflow/agent-end` is upstream MEMBER
|
|
44
|
+
* bookkeeping with no ledger kind (Task 2 handoff + plan Interfaces — the
|
|
45
|
+
* member `outcome` is intentionally not persisted) and is filtered out.
|
|
46
|
+
* `ts` takes the envelope's `time`.
|
|
47
|
+
*
|
|
48
|
+
* P-c answer observation (plan `20260815-dsh-workflow-gate` Task 4 fold-in —
|
|
49
|
+
* the Task-2 Important handoff): the workflow GATE cannot observe the ask
|
|
50
|
+
* outcome — the tool registry's `serviceAsk` consumes the approval result
|
|
51
|
+
* internally, and the gate invents no answerer. The run-start observation
|
|
52
|
+
* IS the answer seam: when the approval waterfall ALLOWS a workflow call,
|
|
53
|
+
* the call executes and the durable `tool-workflow/run-start` session event
|
|
54
|
+
* (name carried) lands in the parent session log — the consumer maps it to
|
|
55
|
+
* the `workflow-run` row AND records `allow` for the run name into the
|
|
56
|
+
* apply-scoped {@link WorkflowAskCache} (`registerWorkflowLedger`'s third
|
|
57
|
+
* parameter — the host adapter's instance). W-1 (qc2 fix-wave): the record
|
|
58
|
+
* fires ONLY for names the policy marked asked in this apply
|
|
59
|
+
* (`WorkflowAskCache.markAsked` on every ask verdict; the observation
|
|
60
|
+
* promotes via `wasAsked`) — a run observed without a prior ask (P-b
|
|
61
|
+
* advisory under `ask` mode, `warn`/`off`-mode runs) is not an approval
|
|
62
|
+
* resolution and never pre-authorizes the name. A DENIED answer produces no
|
|
63
|
+
* run → no observation → the next same-name call under `ask` re-asks
|
|
64
|
+
* (fail-closed — no grant evidence, never an invented allow). The hook is
|
|
65
|
+
* bounded and contained: it fires only on the FIRST successful recording of
|
|
66
|
+
* a run-start (the watermark gate above), keyed on the UNCAPPED run name
|
|
67
|
+
* (`row.runName` — it must match the gate's `meta.name`, which is never
|
|
68
|
+
* truncated AND is normalized through the SAME `normalizeWorkflowName` the
|
|
69
|
+
* gate composes with — the Task 5 congruence fold-in), and a throwing cache
|
|
70
|
+
* record degrades the observation with one warn — the ledger row is already
|
|
71
|
+
* appended, the run is never affected.
|
|
72
|
+
*
|
|
73
|
+
* Observe-only (plan Global Constraints: W3 / N5): ZERO gating — every read
|
|
74
|
+
* and append is try/catch-contained; a throwing session read logs one warn
|
|
75
|
+
* and the run is unaffected; all appends go through `recordWorkflowEvent`
|
|
76
|
+
* (itself fully contained — a failing ledger write never crashes or alters
|
|
77
|
+
* a workflow run). The `sessions` service is read STRUCTURALLY via
|
|
78
|
+
* `ctx.get('sessions')` — an absent service (composition without
|
|
79
|
+
* dsh-session) → one debug log + consumer disabled. No runtime dependency
|
|
80
|
+
* on `@deepseek-ai/dsh-session` (same pattern as the agents/loader seams).
|
|
81
|
+
* The `session/created` listener NEVER throws synchronously — upstream
|
|
82
|
+
* vetoes a session publication on a throwing creation listener.
|
|
83
|
+
*
|
|
84
|
+
* Depth advisory (P-e / N5): on `agent-start`, resolve the child session
|
|
85
|
+
* via `sessions.get(childId)` and warn when its `header.delegationDepth`
|
|
86
|
+
* is >= 2 — ONCE per run (bounded by a per-runId latch). Observe-time only,
|
|
87
|
+
* NEVER a refusal path.
|
|
88
|
+
*/
|
|
89
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
90
|
+
import type { HarnessResolver } from './_shared.ts';
|
|
91
|
+
import type { WorkflowAskCache } from './workflow-policy.ts';
|
|
92
|
+
/** Logger label for the workflow-ledger consumer (dsh logger naming: `<scope>/<subject>`). */
|
|
93
|
+
export declare const WORKFLOW_LEDGER_LOGGER = "mstar/workflow-ledger";
|
|
94
|
+
/**
|
|
95
|
+
* The durable watermark file name under `{HARNESS_DIR}` (qc2 W-1 / qc3
|
|
96
|
+
* F-301 fix-wave): `{ "v": 1, "cursors": { "<sessionId>": <nextSeq> } }` —
|
|
97
|
+
* the next expected envelope seq per session id. Written atomically
|
|
98
|
+
* (temp-file + rename) after every recorded workflow row; read lazily per
|
|
99
|
+
* harness dir (module-level cache). Absent on first run (silent); a
|
|
100
|
+
* present-but-corrupt file degrades to in-memory-only with one warn.
|
|
101
|
+
*/
|
|
102
|
+
export declare const WORKFLOW_LEDGER_WATERMARK_FILE = "workflow-ledger-cursors.json";
|
|
103
|
+
/**
|
|
104
|
+
* Session-count cap for ONE watermark file (bounds the sidecar). Eviction
|
|
105
|
+
* prefers sessions that are no longer live; when every entry is live the
|
|
106
|
+
* oldest entry is dropped (documented residual — a later restore of an
|
|
107
|
+
* evicted session re-records its rows; bounded by the cap).
|
|
108
|
+
*/
|
|
109
|
+
export declare const WORKFLOW_LEDGER_WATERMARK_MAX_SESSIONS = 256;
|
|
110
|
+
/** Consumer log levels the module sink understands. */
|
|
111
|
+
export type WorkflowLedgerLogLevel = 'debug' | 'warn';
|
|
112
|
+
/** Module-level consumer log sink — bound by `apply` to `ctx.logger(WORKFLOW_LEDGER_LOGGER)` (agent-flow ledger precedent). */
|
|
113
|
+
export type WorkflowLedgerLogSink = (level: WorkflowLedgerLogLevel, message: string) => void;
|
|
114
|
+
/**
|
|
115
|
+
* Bind the consumer log sink (the entry `apply` binds it to
|
|
116
|
+
* `ctx.logger(WORKFLOW_LEDGER_LOGGER)`). Returns the PRIOR sink so a caller
|
|
117
|
+
* can restore it (test pattern: agent-flow `setAgentFlowLogger`).
|
|
118
|
+
*/
|
|
119
|
+
export declare function setWorkflowLedgerLogger(sink: WorkflowLedgerLogSink): WorkflowLedgerLogSink;
|
|
120
|
+
/**
|
|
121
|
+
* Register the workflow-ledger consumer: (1) a `session/created` backfill
|
|
122
|
+
* listener (registered FIRST — qc3 S-305 — so no apply-time window exists
|
|
123
|
+
* between the snapshot and the attach); (2) a bounded cold scan over
|
|
124
|
+
* `ctx.sessions.list()` reading each session's `events` snapshot for
|
|
125
|
+
* `tool-workflow/*` rows (covers pre-restart runs — constructor-seeded
|
|
126
|
+
* events never hit the firehose, `firstLiveSeq`); (3) a live
|
|
127
|
+
* `ctx.events.on('session/event', …)` listener filtering the four types.
|
|
128
|
+
* One DURABLE watermark per session id (session-log `seq` position,
|
|
129
|
+
* persisted to `{HARNESS_DIR}/workflow-ledger-cursors.json`) — re-applies
|
|
130
|
+
* never duplicate; no other cache. The watermark advances only AFTER a
|
|
131
|
+
* successful ledger append (qc3 R-401 — a failing append leaves the cursor
|
|
132
|
+
* behind so the row is re-attempted at the next scan, never lost). Every
|
|
133
|
+
* read/append is try/catch-contained — including `sessions.list()` itself
|
|
134
|
+
* (qc2 S-7: one warn, the cold scan skipped, the consumer stays live); the
|
|
135
|
+
* `sessions` service absent → one debug log + consumer disabled (composition
|
|
136
|
+
* without dsh-session). All appends go through `recordWorkflowEvent` (itself
|
|
137
|
+
* fully contained — a failing ledger write never crashes or alters a
|
|
138
|
+
* workflow run).
|
|
139
|
+
*
|
|
140
|
+
* @param ctx - the plugin's registrant context (the app composition root).
|
|
141
|
+
* @param resolver - the shared per-workspace `{HARNESS_DIR}` resolver
|
|
142
|
+
* (harnessDir attribution from the carrying session's `header.cwd`).
|
|
143
|
+
* @param workflowAskCache - the apply-scoped P-c ask cache (plan
|
|
144
|
+
* `20260815-dsh-workflow-gate` Task 4 fold-in — the host adapter's
|
|
145
|
+
* instance; see the module doc "P-c answer observation"). Absent → the
|
|
146
|
+
* observation hook is disabled (W-B2 tests / compositions without the
|
|
147
|
+
* workflow gate).
|
|
148
|
+
*/
|
|
149
|
+
export declare function registerWorkflowLedger(ctx: Context, resolver: HarnessResolver, workflowAskCache?: WorkflowAskCache): void;
|