@mstar-harness/dsh 3.8.1 → 3.8.3
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 -2
- package/README.md +16 -6
- package/README.zh.md +16 -6
- package/dist/client/panel/engine-status-client.d.ts +84 -6
- package/dist/client/panel/graph/project-graph.d.ts +26 -13
- package/dist/client/panel/guards.d.ts +41 -1
- package/dist/client/panel/locale.d.ts +1 -1
- package/dist/client/panel/pages/AgentListPage.d.ts +1 -1
- package/dist/client/panel/sidebar.d.ts +3 -2
- package/dist/client/panel/state-section.d.ts +25 -3
- package/dist/client/panel/use-mstar-engine-status.d.ts +28 -4
- package/dist/client.js +346 -48
- package/dist/engine-status-endpoint.d.ts +85 -8
- package/dist/engine-status-store.d.ts +91 -1
- package/dist/engine-status-wire.d.ts +9 -0
- package/dist/gates/_shared.d.ts +61 -9
- package/dist/gates/adapter.d.ts +32 -2
- package/dist/gates/agent-flow.d.ts +312 -60
- package/dist/gates/catalog.d.ts +58 -37
- package/dist/gates/dispatch.d.ts +11 -2
- package/dist/gates/goal-bridge.d.ts +10 -130
- package/dist/gates/plan-mode-bridge.d.ts +20 -11
- package/dist/gates/role-persona.d.ts +16 -0
- package/dist/gates/steering.d.ts +41 -0
- package/dist/gates/workflow-ledger.d.ts +31 -4
- package/dist/gates/workflow-selection.d.ts +41 -20
- package/dist/index.js +1206 -392
- package/dist/types.d.ts +36 -11
- package/harness-commands/amazing-e2e-check.md +10 -0
- package/harness-commands/amazing-pr-review.md +2 -0
- package/harness-commands/codebase-audit.md +2 -0
- package/harness-commands/iteration-drive.md +1 -1
- package/harness-skills/mstar-artifacts/references/plan-files-and-reports.md +2 -2
- package/harness-skills/mstar-artifacts/references/plan-quality-bar.md +14 -12
- package/harness-skills/mstar-artifacts/templates/plan.main.md +19 -6
- package/harness-skills/mstar-audit/SKILL.md +5 -5
- package/harness-skills/mstar-coding-behavior/SKILL.md +8 -8
- package/harness-skills/mstar-dispatch-gates/SKILL.md +9 -7
- package/harness-skills/mstar-e2e/SKILL.md +40 -0
- package/harness-skills/mstar-e2e/references/report-template.md +32 -0
- package/harness-skills/mstar-engine-legacy/references/qc-seat-n-restatements.md +3 -3
- package/harness-skills/mstar-harness-core/SKILL.md +14 -1
- package/harness-skills/mstar-host/SKILL.md +3 -1
- package/harness-skills/mstar-host/references/_shared/host-role-binding-core.md +1 -1
- package/harness-skills/mstar-host/references/cursor.md +1 -1
- package/harness-skills/mstar-host/references/dsh-workflow-scripts.md +424 -0
- package/harness-skills/mstar-host/references/dsh.md +180 -51
- package/harness-skills/mstar-host/references/kimi.md +3 -3
- package/harness-skills/mstar-host/references/omp.md +3 -3
- package/harness-skills/mstar-host/references/parallel-dispatch.md +6 -6
- package/harness-skills/mstar-host/references/zcode.md +4 -4
- package/harness-skills/mstar-iteration/SKILL.md +1 -1
- package/harness-skills/mstar-iteration/references/phase-1-prepare.md +2 -2
- package/harness-skills/mstar-iteration/references/phase-2-worktree-lease.md +3 -3
- package/harness-skills/mstar-review-qc/SKILL.md +4 -4
- package/harness-skills/mstar-review-qc/references/review-responsibility-boundaries.md +9 -7
- package/harness-skills/mstar-roles/SKILL.md +2 -0
- package/harness-skills/mstar-roles/references/_shared/leaf-executor-core.md +9 -0
- package/harness-skills/mstar-roles/references/ops-engineer.md +3 -0
- package/harness-skills/mstar-roles/references/project-manager/dispatch-and-assignment.md +12 -9
- package/harness-skills/mstar-roles/references/project-manager/qa-trigger-matrix.md +7 -5
- package/harness-skills/mstar-roles/references/project-manager/qc-and-residuals.md +2 -2
- package/harness-skills/mstar-roles/references/project-manager/routing-and-dev-allocation.md +2 -2
- package/harness-skills/mstar-roles/references/project-manager.md +4 -2
- package/harness-skills/mstar-roles/references/qa-engineer/acceptance-gate.md +12 -13
- package/harness-skills/mstar-roles/references/qa-engineer.md +5 -4
- package/harness-skills/mstar-roles/references/qc-specialist/deep-review-lenses.md +5 -5
- package/harness-skills/mstar-roles/references/qc-specialist/report-template.md +2 -0
- package/harness-skills/mstar-roles/references/qc-specialist/reviewer-checklist.md +1 -1
- package/harness-skills/mstar-roles/references/qc-specialist/reviewer-workflow.md +5 -4
- package/harness-skills/mstar-roles/references/qc-specialist-shared.md +3 -1
- package/harness-skills/mstar-sdd/SKILL.md +17 -9
- package/harness-skills/mstar-sdd/references/file-handoffs.md +40 -20
- package/harness-skills/mstar-sdd/references/implementer-continuation-prompt.md +9 -4
- package/harness-skills/mstar-sdd/references/implementer-prompt.md +11 -6
- package/harness-skills/mstar-sdd/references/sticky-implementer-session.md +4 -2
- package/harness-skills/mstar-sdd/references/task-reviewer-prompt.md +8 -4
- package/package.json +2 -2
package/dist/gates/catalog.d.ts
CHANGED
|
@@ -3,49 +3,67 @@ import { type UserMessage } from '@deepseek-ai/dsh-llm';
|
|
|
3
3
|
import type { PreStepDecision } from '@deepseek-ai/dsh-agent';
|
|
4
4
|
import type { MstarEngineStatusPayload } from '../types.ts';
|
|
5
5
|
import { HarnessResolver } from './_shared.ts';
|
|
6
|
+
import { type SessionHint } from './workflow-selection.ts';
|
|
6
7
|
/** Default catalog cache refresh interval (ms) — see Config `catalogTtlMs`. */
|
|
7
8
|
export declare const DEFAULT_CATALOG_TTL_MS = 60000;
|
|
8
|
-
/** Catalog cache key for the explicit-`harnessDir` app-wide entry (one entry for every session). */
|
|
9
|
-
export declare const EXPLICIT_CACHE_KEY = "\0explicit";
|
|
10
9
|
/** One TTL cache entry: the unified payload plus the build timestamp. */
|
|
11
10
|
export interface CatalogCacheEntry {
|
|
12
11
|
payload: MstarEngineStatusPayload;
|
|
13
12
|
builtAt: number;
|
|
14
13
|
}
|
|
15
14
|
/**
|
|
16
|
-
* The apply-scoped
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
* a destroyed cache.
|
|
15
|
+
* The apply-scoped catalog invalidation (D4): the `harnessDir → { session →
|
|
16
|
+
* cache key }` reverse map + the digest set, created per-apply by the entry
|
|
17
|
+
* (same lifetime as the cache — module-level state would survive an HMR fiber
|
|
18
|
+
* restart and point at a destroyed cache).
|
|
19
|
+
*
|
|
20
|
+
* WHY a set per harness: two sessions in one workspace key their OWN catalog
|
|
21
|
+
* payloads, so a ledger record for that harness must drop EVERY current
|
|
22
|
+
* session key (never a single shared one) — and a picker commit must drop
|
|
23
|
+
* exactly ONE session's key without touching its neighbours.
|
|
26
24
|
*/
|
|
27
25
|
export interface CatalogInvalidation {
|
|
28
26
|
/**
|
|
29
|
-
* Register `key` as
|
|
30
|
-
* `catalogPayloadFor` on
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
* no cache entry to invalidate → no-op.
|
|
27
|
+
* Register `key` as one session's cache key for `harnessDir` — called by
|
|
28
|
+
* `catalogPayloadFor` on hit AND build. A session whose hint changed
|
|
29
|
+
* (a picker commit) has its OLD key dropped here, so the reverse map never
|
|
30
|
+
* grows a key history. A null harness dir or a session with no stable id
|
|
31
|
+
* has no cached entry to invalidate → no-op.
|
|
35
32
|
*/
|
|
36
|
-
register(harnessDir: string | null, key: string): void;
|
|
33
|
+
register(harnessDir: string | null, sessionId: string | undefined, key: string): void;
|
|
37
34
|
/**
|
|
38
|
-
* Delete
|
|
39
|
-
* invokes this through the apply-bound hook). A missing mapping
|
|
40
|
-
* no-op, and a missing entry after a previous invalidation is a
|
|
41
|
-
* (the next pre-step rebuilds regardless). A throwing
|
|
42
|
-
* contained by the record path (log-only — it never blocks
|
|
43
|
-
* record).
|
|
35
|
+
* Delete every current session's cache entry for `harnessDir` (the ledger
|
|
36
|
+
* record path invokes this through the apply-bound hook). A missing mapping
|
|
37
|
+
* is a safe no-op, and a missing entry after a previous invalidation is a
|
|
38
|
+
* no-op too (the next pre-step rebuilds regardless). A throwing
|
|
39
|
+
* invalidation is contained by the record path (log-only — it never blocks
|
|
40
|
+
* the ledger record).
|
|
44
41
|
*/
|
|
45
42
|
invalidate(harnessDir: string): void;
|
|
43
|
+
/**
|
|
44
|
+
* Delete ONE session's cache entry (and its re-emission digest) — the
|
|
45
|
+
* picker commit path: the session's durable hint changed, so the next
|
|
46
|
+
* pre-step rebuilds from the acknowledged binding and re-emits the row
|
|
47
|
+
* within the same turn instead of serving the pre-pick payload until the
|
|
48
|
+
* TTL expires.
|
|
49
|
+
*/
|
|
50
|
+
invalidateSession(harnessDir: string | null, sessionId: string | undefined): void;
|
|
46
51
|
}
|
|
47
|
-
/** Create the apply-scoped invalidation bound to ONE catalog cache
|
|
48
|
-
export declare function createCatalogInvalidation(cache: Map<string, CatalogCacheEntry>): CatalogInvalidation;
|
|
52
|
+
/** Create the apply-scoped invalidation bound to ONE catalog cache + its digests. */
|
|
53
|
+
export declare function createCatalogInvalidation(cache: Map<string, CatalogCacheEntry>, digests: Map<string, TurnDigest>): CatalogInvalidation;
|
|
54
|
+
/**
|
|
55
|
+
* The cache key of ONE session's catalog payload (D4): the resolved harness
|
|
56
|
+
* dir + the durable `session.header.id` + the session cwd + the EFFECTIVE
|
|
57
|
+
* hint fields that can change the selection (the opaque lease holder and the
|
|
58
|
+
* durable pick — cwd and session id are already in the tuple). Two sessions in
|
|
59
|
+
* the same cwd therefore never share a payload, and a picker commit is a
|
|
60
|
+
* different key rather than a mutated entry.
|
|
61
|
+
*
|
|
62
|
+
* Encoded as a JSON string array so a delimiter (NUL / quote / comma) inside
|
|
63
|
+
* an opaque field cannot fuse two distinct tuples — `\u0000`-join was not
|
|
64
|
+
* collision-free for those values.
|
|
65
|
+
*/
|
|
66
|
+
export declare function catalogCacheKey(harnessDir: string | null, sessionId: string, cwd: string, hint: SessionHint | undefined): string;
|
|
49
67
|
/**
|
|
50
68
|
* Build the unified catalog payload for one harness dir (boot for the
|
|
51
69
|
* explicit config, first-use per workspace otherwise, then TTL-refreshed —
|
|
@@ -54,8 +72,11 @@ export declare function createCatalogInvalidation(cache: Map<string, CatalogCach
|
|
|
54
72
|
* fallback is never silent.
|
|
55
73
|
* @param ctx - registrant context (logger for the manifest fallback).
|
|
56
74
|
* @param harnessDir - the resolved `{HARNESS_DIR}` (null when none found).
|
|
75
|
+
* @param hint - the carrying session's structural identity + durable pick
|
|
76
|
+
* (omitted ⇒ the automatic rungs miss and only a unique active lifecycle —
|
|
77
|
+
* or the picker error — resolves).
|
|
57
78
|
*/
|
|
58
|
-
export declare function buildCatalogPayload(ctx: Context, harnessDir: string | null): MstarEngineStatusPayload;
|
|
79
|
+
export declare function buildCatalogPayload(ctx: Context, harnessDir: string | null, hint?: SessionHint): MstarEngineStatusPayload;
|
|
59
80
|
/**
|
|
60
81
|
* Advisory `agent/pre-step` waterfall listener (agent
|
|
61
82
|
* catalog): delegates through `next()` (never `reject` — that would block the
|
|
@@ -89,26 +110,26 @@ export declare function buildCatalogPayload(ctx: Context, harnessDir: string | n
|
|
|
89
110
|
* @param ctx - registrant context (logger for the containment path).
|
|
90
111
|
* @param resolver - the per-workspace `{HARNESS_DIR}` resolver (the probe
|
|
91
112
|
* never starts from the process cwd).
|
|
92
|
-
* @param
|
|
93
|
-
* `
|
|
94
|
-
*
|
|
95
|
-
*
|
|
96
|
-
* workspace root and TTL-refreshed — Config `catalogTtlMs`).
|
|
113
|
+
* @param cache - per-session TTL catalog cache: each session's payload is
|
|
114
|
+
* keyed by harness dir + `session.header.id` + cwd + effective hint, so two
|
|
115
|
+
* sessions in one workspace never share a selection; a session with no stable
|
|
116
|
+
* id is built uncached.
|
|
97
117
|
* @param ttlMs - catalog refresh interval in milliseconds.
|
|
98
|
-
* @param
|
|
99
|
-
*
|
|
118
|
+
* @param invalidation - the apply-scoped session-keyed invalidation (ledger
|
|
119
|
+
* records evict every session of a harness; a picker commit evicts one).
|
|
120
|
+
* @param digests - per session+workspace turn digests (last rendered text)
|
|
100
121
|
* for the digest-gated re-emission.
|
|
101
122
|
* @param payload - the proposed step the loop is about to enter.
|
|
102
123
|
* @param next - the remaining pre-step chain; its value is the delegated decision.
|
|
103
124
|
*/
|
|
104
|
-
export declare function preStepCatalogListener(ctx: Context, resolver: HarnessResolver,
|
|
125
|
+
export declare function preStepCatalogListener(ctx: Context, resolver: HarnessResolver, cache: Map<string, CatalogCacheEntry>, ttlMs: number, invalidation: CatalogInvalidation, digests: Map<string, TurnDigest>, payload: {
|
|
105
126
|
agent: unknown;
|
|
106
127
|
messages: UserMessage[];
|
|
107
128
|
turn: number;
|
|
108
129
|
step: number;
|
|
109
130
|
signal: AbortSignal;
|
|
110
131
|
}, next: () => Promise<PreStepDecision>): Promise<PreStepDecision>;
|
|
111
|
-
/** Per
|
|
132
|
+
/** Per session+workspace turn digest: the rendered catalog text as of the last injection. */
|
|
112
133
|
export interface TurnDigest {
|
|
113
134
|
turn: number;
|
|
114
135
|
text: string;
|
package/dist/gates/dispatch.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ import type { AssignmentFields, GateResult, ValidationResult } from '@mstar-harn
|
|
|
3
3
|
import type { PreToolDecision, ToolExecution } from '@deepseek-ai/dsh-tools';
|
|
4
4
|
import { HarnessResolver } from './_shared.ts';
|
|
5
5
|
import type { Config } from './_shared.ts';
|
|
6
|
+
import type { SessionHint } from './workflow-selection.ts';
|
|
6
7
|
import type { DshHostAdapter } from './adapter.ts';
|
|
7
8
|
/** Logger label for the dispatch gate (dsh logger naming: `<scope>/<subject>`). */
|
|
8
9
|
export declare const DISPATCH_LOGGER = "mstar/dispatch-gate";
|
|
@@ -116,8 +117,13 @@ export declare function sessionIdOf(exec: ToolExecution): string | undefined;
|
|
|
116
117
|
* claim-before-InProgress red line needs the plan's execution_lease, and a
|
|
117
118
|
* missing root/snapshot cannot confirm it — `lease.dispatch.unverifiable`
|
|
118
119
|
* fires (advisory in warn, deny under hard).
|
|
120
|
+
* @param hint - the carrying session's selection hint: it decides WHICH
|
|
121
|
+
* active lifecycle's snapshot the lease is re-verified against, so a
|
|
122
|
+
* session that is not bound to the lifecycle holding the plan row stops
|
|
123
|
+
* with `lease.dispatch.plan-not-found` instead of verifying a lease it
|
|
124
|
+
* does not own.
|
|
119
125
|
*/
|
|
120
|
-
export declare function leaseGateViolations(harnessDir: string | null, exec: ToolExecution, writable: boolean | undefined, prompt: string): ValidationResult[];
|
|
126
|
+
export declare function leaseGateViolations(harnessDir: string | null, exec: ToolExecution, writable: boolean | undefined, prompt: string, hint?: SessionHint): ValidationResult[];
|
|
121
127
|
/**
|
|
122
128
|
* The dispatch-gate validation core — the engine's SINGLE dispatch-gate
|
|
123
129
|
* composition (`dispatch.composeDispatchGate`, opencode/omp/CLI parity — the
|
|
@@ -142,8 +148,11 @@ export declare function leaseGateViolations(harnessDir: string | null, exec: Too
|
|
|
142
148
|
*
|
|
143
149
|
* @returns the violations plus the writable flag (false for read-only
|
|
144
150
|
* roles — the listener feeds it to the lease gate).
|
|
151
|
+
* @param hint - the carrying session's selection hint (the adapter derives
|
|
152
|
+
* it once per dispatch and shares it with the lease gate, the ledger
|
|
153
|
+
* record and every selection read below).
|
|
145
154
|
*/
|
|
146
|
-
export declare function dispatchGateCore(config: Config, harnessDir: string | null, prompt: string): {
|
|
155
|
+
export declare function dispatchGateCore(config: Config, harnessDir: string | null, prompt: string, hint?: SessionHint): {
|
|
147
156
|
violations: ValidationResult[];
|
|
148
157
|
writable: boolean | undefined;
|
|
149
158
|
};
|
|
@@ -1,13 +1,7 @@
|
|
|
1
1
|
import type { Context } from '@deepseek-ai/cordis';
|
|
2
|
-
import type {
|
|
2
|
+
import type { HarnessResolver } from './_shared.ts';
|
|
3
3
|
/** Logger label for the goal bridge (dsh logger naming: `<scope>/<subject>`). */
|
|
4
4
|
export declare const GOAL_BRIDGE_LOGGER = "mstar/goal-bridge";
|
|
5
|
-
/**
|
|
6
|
-
* Flat `maxGoalRounds` config fallback (architect decision — plan
|
|
7
|
-
* ): 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
5
|
/** Consumer log levels the module sink understands. */
|
|
12
6
|
export type GoalBridgeLogLevel = 'debug' | 'warn';
|
|
13
7
|
/** Module-level consumer log sink — bound by `apply` to `ctx.logger(GOAL_BRIDGE_LOGGER)` (agent-flow ledger precedent). */
|
|
@@ -19,130 +13,16 @@ export type GoalBridgeLogSink = (level: GoalBridgeLogLevel, message: string) =>
|
|
|
19
13
|
*/
|
|
20
14
|
export declare function setGoalBridgeLogger(sink: GoalBridgeLogSink): GoalBridgeLogSink;
|
|
21
15
|
/**
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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. */
|
|
50
|
-
export interface GoalsServiceView {
|
|
51
|
-
get(agent: unknown): GoalView | undefined;
|
|
52
|
-
create(agent: unknown, request: {
|
|
53
|
-
objective: string;
|
|
54
|
-
maxGoalRounds?: number;
|
|
55
|
-
}): unknown;
|
|
56
|
-
complete(agent: unknown, ref: GoalRefView): unknown;
|
|
57
|
-
}
|
|
58
|
-
/** The `agents` service surface the `subagent/start` root walk reads. */
|
|
59
|
-
interface AgentsView {
|
|
60
|
-
get(id: string): unknown;
|
|
61
|
-
}
|
|
62
|
-
/**
|
|
63
|
-
* The mirrored goal objective: the COMPLETE iteration flow with the exit
|
|
64
|
-
* definition (mstar-host `/goal` rule — advancing an iteration means the
|
|
65
|
-
* entire flow, never a sub-stage). Session-level text only — `status.json`
|
|
66
|
-
* stays the harness SSOT.
|
|
67
|
-
*/
|
|
68
|
-
export declare function iterationGoalObjective(iterationId: string): string;
|
|
69
|
-
/**
|
|
70
|
-
* Locate the steering iteration compass (mirror of the engine's
|
|
71
|
-
* `resolveCompassEnforcement` scan + the catalog's `steeringCompassPath`):
|
|
72
|
-
* the FIRST `{ITERATION_DIR}/<id>/delivery-compass.md` whose frontmatter
|
|
73
|
-
* `status` is `active` or `locked` — the directory name IS the iteration id
|
|
74
|
-
* (plan-conventions `{ITERATION_DIR}/<id>/`). Completed/status-less/archived
|
|
75
|
-
* compasses do not steer. Silent on any read failure (advisory degrade).
|
|
76
|
-
* Shared with the planMode bridge via explicit no-barrel import (the same "is an active iteration steering" read).
|
|
77
|
-
* @param harnessDir - the resolved `{HARNESS_DIR}`.
|
|
78
|
-
*/
|
|
79
|
-
export declare function steeringCompass(harnessDir: string): {
|
|
80
|
-
iterationId: string;
|
|
81
|
-
} | undefined;
|
|
82
|
-
/** Inputs of the mirror: the structural goals view, the per-workspace resolver, and the resolved round cap. */
|
|
83
|
-
export interface MirrorIterationGoalInput {
|
|
84
|
-
resolver: HarnessResolver;
|
|
85
|
-
/** The structural goals view (`ctx.get('goals')`); absent → the mirror is inert. */
|
|
86
|
-
goals?: GoalsServiceView;
|
|
87
|
-
/** The resolved `maxGoalRounds` (flat config key, absent → {@link DEFAULT_MAX_GOAL_ROUNDS}). */
|
|
88
|
-
maxGoalRounds: number;
|
|
89
|
-
}
|
|
90
|
-
/**
|
|
91
|
-
* Mirror the steering iteration objective into the goal of ONE agent.
|
|
92
|
-
* Root-like agent (`header.parentSession === undefined`) + active iteration
|
|
93
|
-
* (compass `status: active|locked`): `get` → absent → `create` (get-先行 —
|
|
94
|
-
* no `GOAL_ALREADY_EXISTS`); present with drifted objective → REPLACE with
|
|
95
|
-
* a fresh goal for the new iteration (see {@link replaceDriftedGoal} — a
|
|
96
|
-
* completed goal is `create`d directly, a live goal is `complete`d first;
|
|
97
|
-
* the new goal starts with a CLEAN round budget); present with the matching
|
|
98
|
-
* objective → no-op (idempotent decision-point re-evaluation). ONE stale
|
|
99
|
-
* re-read retry; a second stale failure is warned and abandoned —
|
|
100
|
-
* goal-service-side concurrency is rare.
|
|
101
|
-
*
|
|
102
|
-
* @returns `true` when the mirror is ensured for this agent (created,
|
|
103
|
-
* replaced, or already in place); `false` when not applicable or a
|
|
104
|
-
* contained failure occurred. Never throws — the caller's listener stays
|
|
105
|
-
* contained.
|
|
106
|
-
*/
|
|
107
|
-
export declare function mirrorIterationGoal(agent: unknown, input: MirrorIterationGoalInput): boolean;
|
|
108
|
-
/**
|
|
109
|
-
* Resolve the ROOT agent of a published child via the `parentSession` walk
|
|
110
|
-
* (upstream `subagent/src/continuation.ts:819-831` precedent): in-process
|
|
111
|
-
* subagent children stamp `header.parentSession` = the parent SESSION id,
|
|
112
|
-
* which IS the parent agent id (a session per agent); the walk stops at the
|
|
113
|
-
* first root-like ancestor. `undefined` when unresolvable (fork lineage,
|
|
114
|
-
* non-in-process provider, registry gap, or a cycle) — the decision point
|
|
115
|
-
* then silently skips. Cycle guard: a `seen` set over visited session ids
|
|
116
|
-
* (the upstream `liveLineage` guard) breaks on ANY revisited id — a 1-hop self-loop, a 2+ hop cycle
|
|
117
|
-
* (A→B→A), or a longer malformed lineage — instead of spinning forever on
|
|
118
|
-
* the synchronous `subagent/start` decision-point listeners (reachable via
|
|
119
|
-
* HMR remounts, resumed/forked sessions with stale headers, or a future
|
|
120
|
-
* host change). Shared with the planMode bridge via explicit no-barrel
|
|
121
|
-
* import (the same `subagent/start` decision-point root walk).
|
|
122
|
-
*/
|
|
123
|
-
export declare function rootAgentOf(agent: unknown, agents: AgentsView): unknown | undefined;
|
|
124
|
-
/**
|
|
125
|
-
* Register the goal bridge: an `agent/session-start` listener (root filter
|
|
126
|
-
* inside the mirror — root and children alike fire, `runtime-types.ts:217`)
|
|
127
|
-
* plus a decision-point re-evaluation on `subagent/start` (the existing
|
|
128
|
-
* decision point — index.ts advisory slot), resolving the delegating ROOT
|
|
129
|
-
* via the `parentSession` walk — the two mirror edges are idempotent (get +
|
|
130
|
-
* compare when the mirror is in place — no churn) — plus a THIRD, advisory
|
|
131
|
-
* listener on the `session/event` firehose : a `goal/change`
|
|
132
|
-
* envelope whose goal is blocked logs ONE warn (code + objective summary +
|
|
133
|
-
* project-register residual pointer) with ZERO harness writes
|
|
134
|
-
* (the one-way mirror; see {@link warnBlockedGoal}). The goals service is an
|
|
135
|
-
* OPTIONAL seam (`ctx.get('goals')` structural read): absent → ONE debug log
|
|
136
|
-
* + the mirror stays inert, never a boot failure — the blocked advisory is
|
|
137
|
-
* independent of it (it only needs the firehose + resolver). Every listener
|
|
138
|
-
* body is try/catch-contained.
|
|
16
|
+
* Register the goal bridge: ONE `session/event` firehose listener
|
|
17
|
+
* (workflow-ledger consumer precedent) that structurally filters the durable
|
|
18
|
+
* `goal/change` envelopes and logs the blocked advisory (see
|
|
19
|
+
* {@link warnBlockedGoal}). It resolves no service — the envelope carries
|
|
20
|
+
* the facts, and the workspace attribution comes from the goal-owning
|
|
21
|
+
* session's `header.cwd`; an unresolvable harness → silent skip. Observe-only:
|
|
22
|
+
* no goal mutation of any kind (`create` / `edit` / `complete` / `pause` /
|
|
23
|
+
* `resume`), and the goal-owning agent is never read.
|
|
139
24
|
*
|
|
140
25
|
* @param ctx - the plugin's registrant context (the app composition root).
|
|
141
26
|
* @param resolver - the shared per-workspace `{HARNESS_DIR}` resolver.
|
|
142
|
-
* @param config - validated plugin configuration (flat `maxGoalRounds`,
|
|
143
|
-
* absent → {@link DEFAULT_MAX_GOAL_ROUNDS}; a non-positive / non-integer
|
|
144
|
-
* value warns once and falls back to the default — see
|
|
145
|
-
* {@link resolveGoalRounds}).
|
|
146
27
|
*/
|
|
147
|
-
export declare function registerGoalBridge(ctx: Context, resolver: HarnessResolver
|
|
148
|
-
export {};
|
|
28
|
+
export declare function registerGoalBridge(ctx: Context, resolver: HarnessResolver): void;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Context } from '@deepseek-ai/cordis';
|
|
2
2
|
import type { HarnessResolver } from './_shared.ts';
|
|
3
|
+
import type { SessionHint } from './workflow-selection.ts';
|
|
3
4
|
/** Logger label for the planMode bridge (dsh logger naming: `<scope>/<subject>`). */
|
|
4
5
|
export declare const PLAN_MODE_BRIDGE_LOGGER = "mstar/plan-mode-bridge";
|
|
5
6
|
/** Consumer log levels the module sink understands. */
|
|
@@ -27,13 +28,20 @@ export interface PlanModeServiceView {
|
|
|
27
28
|
set(agent: unknown, active: boolean): unknown;
|
|
28
29
|
}
|
|
29
30
|
/**
|
|
30
|
-
* The planMode target for one harness: `true` iff
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
31
|
+
* The planMode target for one harness + carrying session: `true` iff the
|
|
32
|
+
* SELECTED lifecycle's own compass still steers (compass `status:
|
|
33
|
+
* active|locked`, `iteration_id` matching the snapshot) AND the selected
|
|
34
|
+
* snapshot carries a Prepare window (≥1 plan row `Todo`). Otherwise the
|
|
35
|
+
* target is OFF.
|
|
34
36
|
* @param harnessDir - the resolved `{HARNESS_DIR}`.
|
|
37
|
+
* @param hint - the root session's carrying hint (lease / cwd / durable pick).
|
|
38
|
+
* @returns `true`/`false` for a resolved selection, or `undefined` when this
|
|
39
|
+
* session has NO selected lifecycle (unbound multi-active / unreadable
|
|
40
|
+
* binding record) — the caller must leave plan mode alone rather than
|
|
41
|
+
* fabricate `false`, which would assert a state for a workflow the session
|
|
42
|
+
* never selected.
|
|
35
43
|
*/
|
|
36
|
-
export declare function planModeTarget(harnessDir: string): boolean;
|
|
44
|
+
export declare function planModeTarget(harnessDir: string, hint?: SessionHint): boolean | undefined;
|
|
37
45
|
/** Inputs of the sync: the structural planMode view and the per-workspace resolver. */
|
|
38
46
|
export interface PlanModeSyncInput {
|
|
39
47
|
resolver: HarnessResolver;
|
|
@@ -43,11 +51,12 @@ export interface PlanModeSyncInput {
|
|
|
43
51
|
/**
|
|
44
52
|
* Mirror the harness Prepare state into the planMode selection of ONE agent:
|
|
45
53
|
* root-like agent (`header.parentSession === undefined`) → resolve the
|
|
46
|
-
* workspace → compute {@link planModeTarget}
|
|
47
|
-
* The service's `'noop'` return
|
|
48
|
-
* decision points churn-free (no new
|
|
49
|
-
* target). Non-root agent / unresolvable
|
|
50
|
-
*
|
|
54
|
+
* workspace → compute {@link planModeTarget} from THAT root session's selected
|
|
55
|
+
* lifecycle → `planMode.set(agent, target)`. The service's `'noop'` return
|
|
56
|
+
* makes repeated evaluation at multiple decision points churn-free (no new
|
|
57
|
+
* `plan/mode` event when already in target). Non-root agent / unresolvable
|
|
58
|
+
* harness / missing planMode service / an UNBOUND session (no selected
|
|
59
|
+
* lifecycle) → no set.
|
|
51
60
|
*
|
|
52
61
|
* @returns `true` when the sync ran for this agent (set called); `false`
|
|
53
62
|
* when not applicable or a contained failure occurred. Never throws — the
|
|
@@ -57,7 +66,7 @@ export declare function syncPlanMode(agent: unknown, input: PlanModeSyncInput):
|
|
|
57
66
|
/**
|
|
58
67
|
* Register the planMode bridge: an `agent/session-start` listener (root
|
|
59
68
|
* filter inside — root and children alike fire, `runtime-types.ts:217`) plus
|
|
60
|
-
* the EXISTING `subagent/start` decision point
|
|
69
|
+
* the EXISTING `subagent/start` decision point,
|
|
61
70
|
* resolving the delegating ROOT via the shared `parentSession` walk — the
|
|
62
71
|
* two edges are idempotent (`'noop'` when already in target — no churn). The
|
|
63
72
|
* planMode service is an OPTIONAL seam (`ctx.get('planMode')` structural
|
|
@@ -228,6 +228,22 @@ export declare function setRolePersonaAgentsDir(dir: string | undefined): string
|
|
|
228
228
|
* applying fiber — an HMR fiber swap unwinds it (reads return the raw
|
|
229
229
|
* service again) and a re-apply restores it.
|
|
230
230
|
*
|
|
231
|
+
* Composition-order independence (`prepend`): the SAME seam is wrapped by a
|
|
232
|
+
* mounted role-identity layer (`dsh-llm-fallbacks` — its dispatch seam resolves
|
|
233
|
+
* the Assignment's declared role and merges that role's declared `persona`),
|
|
234
|
+
* and BOTH wrappers fill the SAME native `persona` slot, each only when it is
|
|
235
|
+
* free — so whichever wrapper runs FIRST owns the slot. A waterfall runs
|
|
236
|
+
* listeners outermost-first, and `prepend` registers this one at the front,
|
|
237
|
+
* so the harness channel is outermost on ANY row order: the default profile
|
|
238
|
+
* mounts the mstar row first, while a re-ordered profile (or a re-applied
|
|
239
|
+
* plugin row) mounts it last. The operator's `rolePersonas` override — the
|
|
240
|
+
* harness's authoritative role identity for a role it resolves — therefore
|
|
241
|
+
* wins the slot over a fallbacks-side persona for the same role instead of
|
|
242
|
+
* losing it to whichever layer happened to apply first. A role the harness
|
|
243
|
+
* resolves NOTHING for still falls through to the fallbacks seam (the
|
|
244
|
+
* outermost wrapper delegates the caller's own request object on every skip
|
|
245
|
+
* path), and reads of every other service are returned untouched.
|
|
246
|
+
*
|
|
231
247
|
* Never throws: the wrap step is contained — on any internal error the read
|
|
232
248
|
* returns the UNWRAPPED service value (persona delivery degrades, the
|
|
233
249
|
* runtime is untouched).
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/** The `agents` service surface the `subagent/start` root walk reads. */
|
|
2
|
+
interface AgentsView {
|
|
3
|
+
get(id: string): unknown;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Root-agent discriminator (the root filter the planMode bridge decides on):
|
|
7
|
+
* `header.parentSession === undefined` ⇒ root-like. Conversation forks also
|
|
8
|
+
* carry `parentSession` (seed lineage) → conservatively excluded (accepted
|
|
9
|
+
* boundary).
|
|
10
|
+
*/
|
|
11
|
+
export declare function isRootLikeAgent(agent: unknown): boolean;
|
|
12
|
+
/**
|
|
13
|
+
* Resolve the ROOT agent of a published child via the `parentSession` walk
|
|
14
|
+
* (upstream `subagent/src/continuation.ts:819-831` precedent): in-process
|
|
15
|
+
* subagent children stamp `header.parentSession` = the parent SESSION id,
|
|
16
|
+
* which IS the parent agent id (a session per agent); the walk stops at the
|
|
17
|
+
* first root-like ancestor. `undefined` when unresolvable (fork lineage,
|
|
18
|
+
* non-in-process provider, registry gap, or a cycle) — the decision point
|
|
19
|
+
* then silently skips. Cycle guard: a `seen` set over visited session ids
|
|
20
|
+
* (the upstream `liveLineage` guard) breaks on ANY revisited id — a 1-hop self-loop, a 2+ hop cycle
|
|
21
|
+
* (A→B→A), or a longer malformed lineage — instead of spinning forever on
|
|
22
|
+
* the synchronous `subagent/start` decision-point listeners (reachable via
|
|
23
|
+
* HMR remounts, resumed/forked sessions with stale headers, or a future
|
|
24
|
+
* host change). The same `subagent/start` decision-point root walk the
|
|
25
|
+
* planMode bridge consumes.
|
|
26
|
+
*/
|
|
27
|
+
export declare function rootAgentOf(agent: unknown, agents: AgentsView): unknown | undefined;
|
|
28
|
+
/**
|
|
29
|
+
* Locate the steering iteration compass (mirror of the engine's
|
|
30
|
+
* `resolveCompassEnforcement` scan + the catalog's `steeringCompassPath`):
|
|
31
|
+
* the FIRST `{ITERATION_DIR}/<id>/delivery-compass.md` whose frontmatter
|
|
32
|
+
* `status` is `active` or `locked` — the directory name IS the iteration id
|
|
33
|
+
* (plan-conventions `{ITERATION_DIR}/<id>/`). Completed/status-less/archived
|
|
34
|
+
* compasses do not steer. Silent on any read failure (advisory degrade).
|
|
35
|
+
* The same "is an active iteration steering" read the planMode bridge consumes.
|
|
36
|
+
* @param harnessDir - the resolved `{HARNESS_DIR}`.
|
|
37
|
+
*/
|
|
38
|
+
export declare function steeringCompass(harnessDir: string): {
|
|
39
|
+
iterationId: string;
|
|
40
|
+
} | undefined;
|
|
41
|
+
export {};
|
|
@@ -4,11 +4,16 @@
|
|
|
4
4
|
* into the CALLING PARENT session's log (top-level runs only — nested
|
|
5
5
|
* transport calls record nothing upstream; Task 1 seam notes §4). The
|
|
6
6
|
* consumer has THREE halves (the architect-verified seam):
|
|
7
|
-
* 1. COLD SCAN at apply — iterate `ctx.get('sessions').list()`,
|
|
8
|
-
* session's
|
|
7
|
+
* 1. COLD SCAN at apply — iterate `ctx.get('sessions').list()`, walk each
|
|
8
|
+
* session's log through the installed `seq` + `eventAt(seq)` surface
|
|
9
|
+
* (never a whole-log copy), and record any `tool-workflow/*` rows
|
|
9
10
|
* already present. Constructor-seeded events (replay/resume/fork) NEVER
|
|
10
11
|
* publish on the `session/event` firehose (`firstLiveSeq`), so without
|
|
11
|
-
* the cold scan pre-restart runs would be invisible.
|
|
12
|
+
* the cold scan pre-restart runs would be invisible. A forked
|
|
13
|
+
* conversation's INHERITED prefix (`Session.inheritedEventCount`) is
|
|
14
|
+
* never part of that walk: those envelopes are the fork PARENT's rows,
|
|
15
|
+
* so a child records only its own events (never a second copy of the
|
|
16
|
+
* parent's rows, attributed to the child, in the same workflow dir).
|
|
12
17
|
* 2. LIVE FIREHOSE — `ctx.events.on('session/event', …)`: the post-commit
|
|
13
18
|
* append feed, delivered to ALL sessions for a root-context listener
|
|
14
19
|
* (scope-null event, untagged listeners admitted).
|
|
@@ -80,6 +85,28 @@
|
|
|
80
85
|
* record degrades the observation with one warn — the ledger row is already
|
|
81
86
|
* appended, the run is never affected.
|
|
82
87
|
*
|
|
88
|
+
* D4 session binding: every row is attributed to the lifecycle the CARRYING
|
|
89
|
+
* session is bound to — its durable picker record (`session.header.id` +
|
|
90
|
+
* exact cwd) folded into the structural hint the shared write resolver
|
|
91
|
+
* consumes — and the record's durable `excludedBeforeSeq` is an intentional
|
|
92
|
+
* EXCLUSION floor: rows the session observed while unbound (below a pick) are
|
|
93
|
+
* never replayed, including after a restart. Rows observed while unbound are
|
|
94
|
+
* not written anywhere; their next seq is persisted as the floor (with zero
|
|
95
|
+
* workflow-dir writes) so the later pick cannot backfill them. An unreadable
|
|
96
|
+
* binding record pauses attribution for that session (no write, no floor,
|
|
97
|
+
* row re-evaluated next scan) — an unverifiable record is never treated as
|
|
98
|
+
* "no pick".
|
|
99
|
+
*
|
|
100
|
+
* The hint ALSO carries the session's VERIFIED live lease holder, read at
|
|
101
|
+
* this edge from the `agents` service (`ctx.get('agents')` → `get(sessionId)`
|
|
102
|
+
* → the shared {@link verifiedLeaseHolderOf} check). The dispatch gate
|
|
103
|
+
* forwards the same identity, so a session whose lifecycle is only decidable
|
|
104
|
+
* by its lease (two active lifecycles sharing one `control_worktree_path`)
|
|
105
|
+
* records its rows under the lifecycle whose lease authorized the dispatch
|
|
106
|
+
* instead of resolving unbound and excluding them permanently. A cold/resumed
|
|
107
|
+
* session with no live Agent carries no holder — the hint then falls back to
|
|
108
|
+
* the picker identity, exactly as before.
|
|
109
|
+
*
|
|
83
110
|
* Observe-only (plan Global Constraints: W3 / N5): ZERO gating — every read
|
|
84
111
|
* and append is try/catch-contained; a throwing session read logs one warn
|
|
85
112
|
* and the run is unaffected; all appends go through `recordWorkflowEvent`
|
|
@@ -184,7 +211,7 @@ export declare function advanceWatermark(workflowDir: string, sid: string, nextS
|
|
|
184
211
|
* Register the workflow-ledger consumer: (1) a `session/created` backfill
|
|
185
212
|
* listener (registered FIRST — — so no apply-time window exists
|
|
186
213
|
* between the snapshot and the attach); (2) a bounded cold scan over
|
|
187
|
-
* `ctx.sessions.list()`
|
|
214
|
+
* `ctx.sessions.list()` walking each session's `seq` + `eventAt(seq)` log for
|
|
188
215
|
* `tool-workflow/*` rows (covers pre-restart runs — constructor-seeded
|
|
189
216
|
* events never hit the firehose, `firstLiveSeq`); (3) a live
|
|
190
217
|
* `ctx.events.on('session/event', …)` listener filtering the four types.
|
|
@@ -1,6 +1,21 @@
|
|
|
1
1
|
import type { WorkflowSelectionView } from '../types.ts';
|
|
2
|
-
/** The active-set resolver result: the
|
|
2
|
+
/** The active-set resolver result: the session's active lifecycle or a clear error. */
|
|
3
3
|
export type ActiveWorkflowSelection = WorkflowSelectionView;
|
|
4
|
+
/**
|
|
5
|
+
* What the carrying session can tell the resolver (structural — no
|
|
6
|
+
* dsh-session import, so cold/raw Session consumers can build one too).
|
|
7
|
+
* `sessionId` is the durable picker key (`session.header.id`), NEVER a
|
|
8
|
+
* holder fallback; `leaseHolder` is the dispatching dsh `Agent.id` when an
|
|
9
|
+
* Agent exists; `selectedWorkflowId` is the session's durable pick (loaded
|
|
10
|
+
* by the caller from the engine-status store). Every field is optional —
|
|
11
|
+
* an omitted hint just misses the corresponding rung.
|
|
12
|
+
*/
|
|
13
|
+
export interface SessionHint {
|
|
14
|
+
cwd?: string;
|
|
15
|
+
sessionId?: string;
|
|
16
|
+
leaseHolder?: string;
|
|
17
|
+
selectedWorkflowId?: string;
|
|
18
|
+
}
|
|
4
19
|
/**
|
|
5
20
|
* Test-only observability hook :
|
|
6
21
|
* whether a snapshot path is still cached. Production code never calls
|
|
@@ -9,30 +24,36 @@ export type ActiveWorkflowSelection = WorkflowSelectionView;
|
|
|
9
24
|
*/
|
|
10
25
|
export declare function _terminalStatusCacheHas(snapshotPath: string): boolean;
|
|
11
26
|
/**
|
|
12
|
-
* Resolve the ACTIVE lifecycle
|
|
13
|
-
* the list holds non-terminal lifecycles
|
|
14
|
-
* is the ONLY resolver the agent-flow
|
|
15
|
-
* entry → a clear error, never a terminal
|
|
16
|
-
* file.
|
|
27
|
+
* Resolve the ACTIVE lifecycle this session writes under (root v2
|
|
28
|
+
* `status.json` `workflows[]` — the list holds non-terminal lifecycles
|
|
29
|
+
* only, removal-at-terminal). This is the ONLY resolver the agent-flow
|
|
30
|
+
* writer / ledger may use: no bound entry → a clear error, never a terminal
|
|
31
|
+
* snapshot, never the root v1 file, and never the registry's first entry.
|
|
17
32
|
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
33
|
+
* EVERY registry entry is validated before N or the candidates are exposed:
|
|
34
|
+
* the engine `validateWorkflowEntry` contract (`id`/`type`/`started_at` and a
|
|
35
|
+
* harness-relative `dir` with no absolute path or `..` segment) plus
|
|
36
|
+
* duplicate-id rejection. A bad row is an error, not a smaller active set —
|
|
37
|
+
* and never a path the resolver would read outside the harness. Only a real
|
|
38
|
+
* empty array is `workflow.selection.no-active`. Active-set definition:
|
|
39
|
+
* membership in `workflows[]` — the engine lifecycle enum's non-terminal
|
|
40
|
+
* states are `running` AND `paused`, so a PAUSED lifecycle stays in the
|
|
41
|
+
* active set (it is still the operator's current lifecycle; only a TERMINAL
|
|
42
|
+
* lifecycle is never a write target).
|
|
26
43
|
* @param harnessDir - the resolved `{HARNESS_DIR}`.
|
|
44
|
+
* @param hint - the carrying session's structural identity, when it has one
|
|
45
|
+
* (omitted ⇒ the automatic rungs and the durable pick miss; unique-active
|
|
46
|
+
* still works).
|
|
27
47
|
*/
|
|
28
|
-
export declare function resolveActiveWorkflow(harnessDir: string): ActiveWorkflowSelection;
|
|
48
|
+
export declare function resolveActiveWorkflow(harnessDir: string, hint?: SessionHint): ActiveWorkflowSelection;
|
|
29
49
|
/**
|
|
30
50
|
* Resolve the workflow the catalog/panel READ path aggregates (compass
|
|
31
|
-
* v3.0.0 § Catalog selection rule):
|
|
32
|
-
*
|
|
33
|
-
*
|
|
51
|
+
* v3.0.0 § Catalog selection rule): the SAME binding order as the write
|
|
52
|
+
* path, else the latest terminal snapshot by mtime (history view — only
|
|
53
|
+
* when the active registry is EMPTY), else a clear error. Never reads the
|
|
34
54
|
* root v1 `plans[]` / root `agent-flow.jsonl` as primary or as a quiet
|
|
35
|
-
* fallback.
|
|
55
|
+
* fallback, and never substitutes history for an unbound active set.
|
|
36
56
|
* @param harnessDir - the resolved `{HARNESS_DIR}`.
|
|
57
|
+
* @param hint - forwarded verbatim to the active-set resolver.
|
|
37
58
|
*/
|
|
38
|
-
export declare function resolveReadWorkflow(harnessDir: string): WorkflowSelectionView;
|
|
59
|
+
export declare function resolveReadWorkflow(harnessDir: string, hint?: SessionHint): WorkflowSelectionView;
|