@mstar-harness/dsh 2.2.0 → 2.3.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.
Files changed (62) hide show
  1. package/README.i18n.yaml +2 -3
  2. package/README.md +118 -3
  3. package/README.zh.md +118 -3
  4. package/bundle/README.md +11 -1
  5. package/dist/client/panel/graph/event-log.d.ts +18 -3
  6. package/dist/client/panel/graph/project-graph.d.ts +40 -14
  7. package/dist/client/panel/locale.d.ts +1 -1
  8. package/dist/client.js +131 -17
  9. package/dist/gates/_shared.d.ts +104 -5
  10. package/dist/gates/adapter.d.ts +34 -1
  11. package/dist/gates/agent-flow.d.ts +186 -4
  12. package/dist/gates/agent-personas.d.ts +44 -0
  13. package/dist/gates/dispatch.d.ts +64 -2
  14. package/dist/gates/fallbacks-advisory.d.ts +77 -0
  15. package/dist/gates/fallbacks-decoration.d.ts +108 -0
  16. package/dist/gates/fallbacks-probe.d.ts +63 -0
  17. package/dist/gates/fallbacks-seeds.d.ts +98 -0
  18. package/dist/gates/goal-bridge.d.ts +151 -0
  19. package/dist/gates/plan-mode-bridge.d.ts +70 -0
  20. package/dist/gates/skill-lint.d.ts +4 -3
  21. package/dist/gates/system-prompt.d.ts +121 -0
  22. package/dist/gates/workflow-ledger.d.ts +149 -0
  23. package/dist/gates/workflow-policy.d.ts +203 -0
  24. package/dist/index.d.ts +10 -4
  25. package/dist/index.js +1833 -60
  26. package/dist/types.d.ts +30 -5
  27. package/harness-agents/architect.md +35 -0
  28. package/harness-agents/code-reviewer.md +98 -0
  29. package/harness-agents/frontend-dev.md +33 -0
  30. package/harness-agents/fullstack-dev-2.md +33 -0
  31. package/harness-agents/fullstack-dev.md +33 -0
  32. package/harness-agents/ops-engineer.md +33 -0
  33. package/harness-agents/product-manager.md +35 -0
  34. package/harness-agents/project-manager.md +32 -0
  35. package/harness-agents/prompt-engineer.md +33 -0
  36. package/harness-agents/qa-engineer.md +35 -0
  37. package/harness-agents/qc-specialist-2.md +62 -0
  38. package/harness-agents/qc-specialist-3.md +62 -0
  39. package/harness-agents/qc-specialist.md +62 -0
  40. package/harness-agents/writing-specialist.md +35 -0
  41. package/harness-commands/codebase-audit.md +2 -2
  42. package/harness-skills/mstar-audit/SKILL.md +20 -0
  43. package/harness-skills/mstar-audit/references/audit-playbook.md +27 -0
  44. package/harness-skills/mstar-branch-worktree/SKILL.md +7 -0
  45. package/harness-skills/mstar-coding-behavior/SKILL.md +5 -0
  46. package/harness-skills/mstar-compound/references/compound-workflow.md +16 -0
  47. package/harness-skills/mstar-compound-refresh/SKILL.md +5 -0
  48. package/harness-skills/mstar-iteration/references/phase-4-5-pr-delivery.md +1 -0
  49. package/harness-skills/mstar-plan-artifacts/SKILL.md +1 -0
  50. package/harness-skills/mstar-plan-artifacts/references/status-and-residuals.md +8 -0
  51. package/harness-skills/mstar-review-qc/SKILL.md +6 -0
  52. package/harness-skills/mstar-roles/references/project-manager/qc-and-residuals.md +2 -1
  53. package/harness-skills/mstar-roles/references/qc-specialist/deep-review-lenses.md +16 -7
  54. package/harness-skills/mstar-roles/references/qc-specialist/report-template.md +17 -1
  55. package/harness-skills/mstar-roles/references/qc-specialist-shared.md +3 -1
  56. package/harness-skills/mstar-roles/references/writing-specialist.md +33 -0
  57. package/harness-skills/mstar-sdd/SKILL.md +5 -1
  58. package/harness-skills/mstar-sdd/references/file-handoffs.md +2 -0
  59. package/harness-skills/mstar-skill-authoring/SKILL.md +4 -2
  60. package/harness-skills/mstar-skill-authoring/references/regression-fixation.md +46 -0
  61. package/harness-skills/mstar-skill-authoring/references/skillsbench-authoring.md +37 -0
  62. package/package.json +7 -4
package/dist/index.js CHANGED
@@ -1106,8 +1106,8 @@ var require_lib2 = __commonJS((exports, module) => {
1106
1106
  });
1107
1107
 
1108
1108
  // src/index.ts
1109
- import { existsSync as existsSync16, readFileSync as readFileSync12, readdirSync as readdirSync7 } from "node:fs";
1110
- import { join as join16 } from "node:path";
1109
+ import { existsSync as existsSync18, readFileSync as readFileSync15, readdirSync as readdirSync9 } from "node:fs";
1110
+ import { join as join20 } from "node:path";
1111
1111
  import { fileURLToPath as fileURLToPath3 } from "node:url";
1112
1112
  import {
1113
1113
  apply as applySkillLocal,
@@ -1473,7 +1473,7 @@ function parseAssignmentBranchForms(assignmentText) {
1473
1473
  if (fields.branchPolicy !== undefined && fields.branchPolicy !== "") {
1474
1474
  const direct = fields.branchPolicy.match(/^direct\s+on\s+(\S+)/i);
1475
1475
  if (direct) {
1476
- const strict = fields.branchPolicy.match(/^direct\s+on\s+(\S+)(?:\s*(?:[—–]|--|-)\s*(.+))?$/);
1476
+ const strict = fields.branchPolicy.match(/^direct\s+on\s+(\S+)(?:\s*(?:[\u2014\u2013]|--|-)\s*(.+))?$/);
1477
1477
  forms.directOn = { branch: direct[1].trim(), reason: strict ? (strict[2] ?? "").trim() : "" };
1478
1478
  }
1479
1479
  }
@@ -3337,6 +3337,26 @@ function scopeGuard(path, allowedRoots) {
3337
3337
  function violation9(severity, code, message, fix) {
3338
3338
  return { ok: false, severity, code, message, fix };
3339
3339
  }
3340
+ var TASK_ARTIFACT_RE = /\btask-\d+(?:-(?:brief|report|fix-report|diff)|\.diff)\b/g;
3341
+ var SDD_DEEPLINK_RE = /\.(?:mstar|agents)\/sdd\/([^\s/<>{}\[\]"'\*\?]+)/g;
3342
+ function findEphemeralCitations(skillText) {
3343
+ const citations = [];
3344
+ const lines = skillText.split(/\r?\n/);
3345
+ for (let i = 0;i < lines.length; i++) {
3346
+ const found = [];
3347
+ for (const m of lines[i].matchAll(TASK_ARTIFACT_RE)) {
3348
+ found.push({ index: m.index, match: m[0], kind: "task-artifact" });
3349
+ }
3350
+ for (const m of lines[i].matchAll(SDD_DEEPLINK_RE)) {
3351
+ found.push({ index: m.index, match: m[0], kind: "sdd-deeplink" });
3352
+ }
3353
+ found.sort((a, b) => a.index - b.index);
3354
+ for (const f of found) {
3355
+ citations.push({ line: i + 1, match: f.match, kind: f.kind });
3356
+ }
3357
+ }
3358
+ return citations;
3359
+ }
3340
3360
  var WORKFLOW_VERB_START_RE = /^(?:explains?|describes?|covers?|provides?|walks?|guides?|shows?|lists?|details?|demonstrates?|outlines?|teaches?|summarizes?)\b/i;
3341
3361
  var PRONOUN_RE = /\bI\b(?!\/)|\b(?:we|you|my|our|your|us)\b/gi;
3342
3362
  var DESCRIPTION_MAX_WORDS = 120;
@@ -3640,6 +3660,19 @@ import { existsSync as existsSync7 } from "node:fs";
3640
3660
  import { resolve as resolve7 } from "node:path";
3641
3661
  import { fileURLToPath as fileURLToPath2 } from "node:url";
3642
3662
  var STATUS_FILE = "status.json";
3663
+ var PERSONA_INTERPOLATION_HAZARD = /\{\{[\s\S]*\}\}/;
3664
+ function stripInterpolationHazard(text) {
3665
+ let out = text;
3666
+ for (;; ) {
3667
+ const open = out.indexOf("{{");
3668
+ if (open < 0)
3669
+ return out;
3670
+ const close = out.indexOf("}}", open + 2);
3671
+ if (close < 0)
3672
+ return out;
3673
+ out = `${out.slice(0, open)}{ {${out.slice(open + 2, close)}} }${out.slice(close + 2)}`;
3674
+ }
3675
+ }
3643
3676
  var Config = import_schemastery.default.object({
3644
3677
  harnessDir: import_schemastery.default.string(),
3645
3678
  enforcement: import_schemastery.default.union(["hard", "soft"]),
@@ -3647,7 +3680,19 @@ var Config = import_schemastery.default.object({
3647
3680
  dispatchBinding: import_schemastery.default.string().default(undefined),
3648
3681
  skillRoots: import_schemastery.default.array(import_schemastery.default.string()).default(undefined),
3649
3682
  bundledSkillDir: import_schemastery.default.string().default(undefined),
3650
- catalogTtlMs: import_schemastery.default.number().default(undefined)
3683
+ catalogTtlMs: import_schemastery.default.number().default(undefined),
3684
+ roleMap: import_schemastery.default.dict(import_schemastery.default.string()).default(undefined),
3685
+ rolePersonas: import_schemastery.default.transform(import_schemastery.default.dict(import_schemastery.default.string()), (value) => {
3686
+ for (const [role, persona] of Object.entries(value)) {
3687
+ if (PERSONA_INTERPOLATION_HAZARD.test(persona)) {
3688
+ throw new import_schemastery.default.ValidationError(`rolePersonas["${role}"] must not contain a "{{" paired with a later "}}" (dsh system-prompt strict interpolation renders persona text and throws on unknown or malformed references — use single braces or reword)`, {});
3689
+ }
3690
+ }
3691
+ return value;
3692
+ }).default(undefined),
3693
+ workflowGate: import_schemastery.default.union(["off", "warn", "ask", "hard"]).default("warn"),
3694
+ workflowNames: import_schemastery.default.array(import_schemastery.default.string()).default(undefined),
3695
+ maxGoalRounds: import_schemastery.default.number().default(undefined)
3651
3696
  });
3652
3697
  function formatViolation(violation12) {
3653
3698
  return `[${violation12.severity}] ${violation12.code}: ${violation12.message}${violation12.fix !== undefined ? ` (fix: ${violation12.fix})` : ""}`;
@@ -3681,6 +3726,25 @@ function packagedSkillsDir() {
3681
3726
  packagedSkillsDirMemo = dir;
3682
3727
  return dir;
3683
3728
  }
3729
+ function resolvePackagedAgentsDir(fileUrl) {
3730
+ for (const rel of ["../harness-agents", "../../harness-agents"]) {
3731
+ try {
3732
+ const dir = fileURLToPath2(new URL(rel, fileUrl));
3733
+ if (existsSync7(dir))
3734
+ return dir;
3735
+ } catch {}
3736
+ }
3737
+ return;
3738
+ }
3739
+ var packagedAgentsDirMemo;
3740
+ function packagedAgentsDir() {
3741
+ if (packagedAgentsDirMemo !== undefined)
3742
+ return packagedAgentsDirMemo;
3743
+ const dir = resolvePackagedAgentsDir(import.meta.url);
3744
+ if (dir !== undefined)
3745
+ packagedAgentsDirMemo = dir;
3746
+ return dir;
3747
+ }
3684
3748
 
3685
3749
  class HarnessResolver {
3686
3750
  explicit;
@@ -3745,8 +3809,99 @@ import { join as join10 } from "node:path";
3745
3809
  // src/gates/dispatch.ts
3746
3810
  import { existsSync as existsSync9 } from "node:fs";
3747
3811
  import { basename as basename5, join as join6, resolve as resolve9 } from "node:path";
3812
+
3813
+ // src/gates/workflow-policy.ts
3814
+ var WORKFLOW_NAME_UNKNOWN_CODE = "workflow.name.unknown";
3815
+ var WORKFLOW_LEASE_UNCOVERED_CODE = "workflow.lease.uncovered";
3816
+ var WORKFLOW_NAME_CONTROL_CHARS = /[\u0000-\u001F\u007F]/g;
3817
+ function normalizeWorkflowName(value) {
3818
+ return value.replace(WORKFLOW_NAME_CONTROL_CHARS, "");
3819
+ }
3820
+ function workflowNameUnknown(config, metaName) {
3821
+ const names = config.workflowNames;
3822
+ if (names === undefined || names.length === 0)
3823
+ return true;
3824
+ return !names.some((entry) => normalizeWorkflowName(entry) === metaName);
3825
+ }
3826
+
3827
+ class WorkflowAskCache {
3828
+ decisions = new Map;
3829
+ asked = new Set;
3830
+ get(name) {
3831
+ return this.decisions.get(name);
3832
+ }
3833
+ record(name, decision) {
3834
+ this.decisions.set(normalizeWorkflowName(name), decision);
3835
+ }
3836
+ markAsked(name) {
3837
+ this.asked.add(normalizeWorkflowName(name));
3838
+ }
3839
+ wasAsked(name) {
3840
+ return this.asked.has(normalizeWorkflowName(name));
3841
+ }
3842
+ }
3843
+ function workflowPolicy(config, cache, input) {
3844
+ const mode = config.workflowGate ?? "warn";
3845
+ if (mode === "off")
3846
+ return { decision: "allow" };
3847
+ if (input.uncoveredPlanId !== undefined) {
3848
+ if (mode === "hard") {
3849
+ return {
3850
+ decision: "deny",
3851
+ code: WORKFLOW_LEASE_UNCOVERED_CODE,
3852
+ reason: `workflow gate (${input.tool}) vetoed: plan "${input.uncoveredPlanId}" is InProgress without execution_lease coverage (workflowGate: hard — no writable fan-out until the plan is recovered)`
3853
+ };
3854
+ }
3855
+ return {
3856
+ decision: "warn",
3857
+ code: WORKFLOW_LEASE_UNCOVERED_CODE,
3858
+ reason: `plan "${input.uncoveredPlanId}" is InProgress without execution_lease coverage (workflowGate: warn — advisory only)`
3859
+ };
3860
+ }
3861
+ if (input.metaName === undefined) {
3862
+ return { decision: "allow" };
3863
+ }
3864
+ const name = input.metaName;
3865
+ if (!workflowNameUnknown(config, name))
3866
+ return { decision: "allow" };
3867
+ switch (mode) {
3868
+ case "warn":
3869
+ return {
3870
+ decision: "warn",
3871
+ code: WORKFLOW_NAME_UNKNOWN_CODE,
3872
+ reason: `workflow name "${name}" is not in the workflowNames allowlist (workflowGate: warn — advisory only)`
3873
+ };
3874
+ case "hard":
3875
+ return {
3876
+ decision: "deny",
3877
+ code: WORKFLOW_NAME_UNKNOWN_CODE,
3878
+ reason: `workflow gate (${input.tool}) vetoed: workflow name "${name}" is not in the workflowNames allowlist (workflowGate: hard)`
3879
+ };
3880
+ case "ask": {
3881
+ const cached = cache.get(name);
3882
+ if (cached === "allow")
3883
+ return { decision: "allow" };
3884
+ if (cached === "deny") {
3885
+ return {
3886
+ decision: "deny",
3887
+ code: WORKFLOW_NAME_UNKNOWN_CODE,
3888
+ reason: `workflow gate (${input.tool}) vetoed: workflow name "${name}" was denied earlier in this session (workflowGate: ask — cached decision)`
3889
+ };
3890
+ }
3891
+ cache.markAsked(name);
3892
+ return {
3893
+ decision: "ask",
3894
+ code: WORKFLOW_NAME_UNKNOWN_CODE,
3895
+ reason: `workflow name "${name}" is not in the workflowNames allowlist (workflowGate: ask — first-seen; approve to allow this session, or deny to veto; the decision is cached)`
3896
+ };
3897
+ }
3898
+ }
3899
+ }
3900
+
3901
+ // src/gates/dispatch.ts
3748
3902
  var DISPATCH_LOGGER = "mstar/dispatch-gate";
3749
- var DEFAULT_DISPATCH_TOOLS = ["subagent"];
3903
+ var DEFAULT_DISPATCH_TOOLS = ["subagent", "subagent_fork"];
3904
+ var DEFAULT_WORKFLOW_TOOLS = ["workflow", "ralph"];
3750
3905
  var ASSIGNMENT_HEADING_RE2 = /^#{1,6}\s+Assignment\s*$/m;
3751
3906
  var ASSIGNMENT_FIELD_RE2 = /^[ \t]*(?:[-*][ \t]+)?\*{0,2}(Execute as|Delegation|Task category)\*{0,2}[ \t]*:[ \t]*(\S.*)$/gm;
3752
3907
  function isAssignmentShaped2(assignmentText) {
@@ -3931,6 +4086,30 @@ function worktreeL1Violations(harnessDir, header) {
3931
4086
  }
3932
4087
  return l1PreDispatchCheck({ controlWorktreePath, leaseWorktreePath, leaseWorkingBranch, planId }).violations;
3933
4088
  }
4089
+ function writableFanOutUncovered(harnessDir) {
4090
+ if (harnessDir === null)
4091
+ return { unreadable: false };
4092
+ const statusPath = join6(harnessDir, STATUS_FILE);
4093
+ if (!existsSync9(statusPath))
4094
+ return { unreadable: false };
4095
+ let doc;
4096
+ try {
4097
+ doc = readJson(statusPath);
4098
+ } catch {
4099
+ return { unreadable: true };
4100
+ }
4101
+ if (!Array.isArray(doc.plans))
4102
+ return { unreadable: true };
4103
+ for (const raw of doc.plans) {
4104
+ const row = asRecord(raw);
4105
+ if (row === undefined || row.status !== "InProgress")
4106
+ continue;
4107
+ const planId = typeof row.id === "string" && row.id !== "" ? row.id : typeof row.plan_id === "string" && row.plan_id !== "" ? row.plan_id : "in-progress-plan";
4108
+ if (!verifyPlanExecutionLease(row, planId).ok)
4109
+ return { uncoveredPlanId: planId, unreadable: false };
4110
+ }
4111
+ return { unreadable: false };
4112
+ }
3934
4113
  function dispatchGateCore(config, harnessDir, prompt) {
3935
4114
  const header = assignmentHeaderRegion(prompt);
3936
4115
  const violations = [...worktreeL2Violations(header), ...worktreeL1Violations(harnessDir, header)];
@@ -3938,8 +4117,96 @@ function dispatchGateCore(config, harnessDir, prompt) {
3938
4117
  violations.push(...composeDispatchGate(header, { agent: config.dispatchBinding ?? "", writable }).violations);
3939
4118
  return { violations, writable };
3940
4119
  }
4120
+ function workflowGateInputOf(exec) {
4121
+ const args = asRecord(exec.arguments);
4122
+ if (args === undefined)
4123
+ return;
4124
+ if (exec.name === "ralph") {
4125
+ const objective = typeof args.objective === "string" ? args.objective : undefined;
4126
+ if (objective === undefined)
4127
+ return;
4128
+ return { tool: exec.name, objective, exec };
4129
+ }
4130
+ if (exec.name === "workflow") {
4131
+ const meta = asRecord(args.meta);
4132
+ const metaName = typeof meta?.name === "string" ? normalizeWorkflowName(meta.name) : undefined;
4133
+ if (meta === undefined || metaName === undefined || metaName === "")
4134
+ return;
4135
+ return { tool: exec.name, metaName, exec };
4136
+ }
4137
+ return;
4138
+ }
4139
+ function gateWorkflow(ctx, harnessDir, config, adapter, exec) {
4140
+ const toolName = exec.name;
4141
+ if (!DEFAULT_WORKFLOW_TOOLS.includes(toolName))
4142
+ return;
4143
+ const mode = config.workflowGate ?? "warn";
4144
+ if (mode === "off")
4145
+ return;
4146
+ const input = workflowGateInputOf(exec);
4147
+ if (input === undefined) {
4148
+ ctx.logger(DISPATCH_LOGGER).warn(`workflow gate (${toolName}) (fail-open): malformed arguments — no ${toolName === "ralph" ? "objective" : "meta"} to gate on; call allowed`);
4149
+ return;
4150
+ }
4151
+ const pb = writableFanOutUncovered(harnessDir);
4152
+ if (pb.unreadable) {
4153
+ const statusLabel = harnessDir === null ? STATUS_FILE : join6(harnessDir, STATUS_FILE);
4154
+ ctx.logger(DISPATCH_LOGGER).warn(`workflow gate (${toolName}) (fail-open): cannot read ${statusLabel} — P-b lease attribution degraded; call allowed`);
4155
+ }
4156
+ const verdict = workflowPolicy(config, adapter.workflowAskCache, {
4157
+ ...input,
4158
+ ...pb.uncoveredPlanId !== undefined ? { uncoveredPlanId: pb.uncoveredPlanId } : {}
4159
+ });
4160
+ const recordVerdict = (v, code) => {
4161
+ if (harnessDir === null)
4162
+ return;
4163
+ adapter.recordWorkflowVerdict({
4164
+ harnessDir,
4165
+ exec,
4166
+ tool: input.tool,
4167
+ ...input.metaName !== undefined ? { workflow: input.metaName } : {},
4168
+ ...input.objective !== undefined ? { objective: input.objective } : {},
4169
+ mode,
4170
+ verdict: v,
4171
+ ...code !== undefined ? { code } : {}
4172
+ });
4173
+ };
4174
+ switch (verdict.decision) {
4175
+ case "allow":
4176
+ recordVerdict("ok");
4177
+ return;
4178
+ case "warn":
4179
+ ctx.logger(DISPATCH_LOGGER).warn(`workflow gate (${toolName}) (advisory): ${verdict.reason}`);
4180
+ ctx.emit("mstar/dispatch-gate", {
4181
+ tool: toolName,
4182
+ role: "",
4183
+ result: {
4184
+ ok: false,
4185
+ violations: [{
4186
+ ok: false,
4187
+ severity: "medium",
4188
+ code: verdict.code,
4189
+ message: verdict.reason
4190
+ }]
4191
+ },
4192
+ hard: false
4193
+ });
4194
+ recordVerdict("advisory", verdict.code);
4195
+ return;
4196
+ case "ask":
4197
+ recordVerdict("ask", verdict.code);
4198
+ return { kind: "ask", reason: verdict.reason };
4199
+ case "deny":
4200
+ recordVerdict("denied", verdict.code);
4201
+ ctx.logger(DISPATCH_LOGGER).error(`workflow gate (${toolName}) vetoed: ${verdict.reason}`);
4202
+ return { kind: "deny", reason: verdict.reason };
4203
+ }
4204
+ }
3941
4205
  function gateDispatch(ctx, harnessDir, config, adapter, exec) {
3942
4206
  const toolName = exec.name;
4207
+ const workflowDecision = gateWorkflow(ctx, harnessDir, config, adapter, exec);
4208
+ if (workflowDecision !== undefined)
4209
+ return workflowDecision;
3943
4210
  if (!(config.dispatchTools ?? [...DEFAULT_DISPATCH_TOOLS]).includes(toolName))
3944
4211
  return;
3945
4212
  const args = asRecord(exec.arguments);
@@ -4002,6 +4269,19 @@ var AGENT_FLOW_FILE = "agent-flow.jsonl";
4002
4269
  var AGENT_FLOW_MAX_EVENTS = 500;
4003
4270
  var AGENT_FLOW_DEFAULT_LIMIT = 50;
4004
4271
  var AGENT_FLOW_SIZE_GATE_BYTES = 64 * 1024;
4272
+ var WORKFLOW_LEDGER_MAX_ID_LENGTH = 512;
4273
+ var WORKFLOW_LEDGER_MAX_LABEL_LENGTH = 512;
4274
+ var WORKFLOW_LEDGER_MAX_NAME_LENGTH = 1024;
4275
+ var WORKFLOW_LEDGER_TRUNCATION_MARKER = "…";
4276
+ var WORKFLOW_LEDGER_MAX_SEQ = 2 ** 31;
4277
+ function truncateLedgerField(value, cap) {
4278
+ const cps = Array.from(value);
4279
+ if (cps.length <= cap)
4280
+ return value;
4281
+ const keep = cap - WORKFLOW_LEDGER_TRUNCATION_MARKER.length;
4282
+ const prefix = keep > 0 ? cps.slice(0, keep).join("") : "";
4283
+ return prefix + WORKFLOW_LEDGER_TRUNCATION_MARKER;
4284
+ }
4005
4285
  var AGENT_FLOW_LOGGER = "mstar/agent-flow";
4006
4286
  var SETTLE_SEAM = "tools/post-execute";
4007
4287
  var SETTLE_SEAM_PAIRING_NOTE = `settle seam "${SETTLE_SEAM}" IS part of the verified dsh-tools registry surface (runPostExecute dispatches it for every tool call) — foreground dispatch calls settle here, background subagents settle via ctx.jobs.onJobDone pairing; only UNPAIRED payloads (non-dispatch tools, calls outside the apply-scoped pairing window) stay dispatch-only — never a fabricated settle`;
@@ -4140,13 +4420,53 @@ function recordSettle(input) {
4140
4420
  log("error", `settle record failed (contained): ${errorMessage(error)}`);
4141
4421
  }
4142
4422
  }
4423
+ function recordWorkflowEvent(input) {
4424
+ try {
4425
+ appendEvent(input.harnessDir, input.event);
4426
+ try {
4427
+ invalidator?.(input.harnessDir);
4428
+ } catch (error) {
4429
+ log("error", `catalog invalidation failed (contained): ${errorMessage(error)}`);
4430
+ }
4431
+ return true;
4432
+ } catch (error) {
4433
+ log("error", `workflow record failed (contained — the workflow run proceeds): ${errorMessage(error)}`);
4434
+ return false;
4435
+ }
4436
+ }
4437
+ function recordWorkflowVerdict(input) {
4438
+ try {
4439
+ const agent = input.exec !== undefined ? agentOfExec(input.exec) : undefined;
4440
+ const event = {
4441
+ v: 1,
4442
+ ts: Date.now(),
4443
+ kind: "workflow-verdict",
4444
+ ...agent !== undefined ? { agent } : {},
4445
+ tool: input.tool,
4446
+ ...input.workflow !== undefined && input.workflow !== "" ? { workflow: truncateLedgerField(input.workflow, WORKFLOW_LEDGER_MAX_NAME_LENGTH) } : {},
4447
+ ...input.objective !== undefined && input.objective !== "" ? { objective: truncateLedgerField(normalizeWorkflowName(input.objective), WORKFLOW_LEDGER_MAX_NAME_LENGTH) } : {},
4448
+ mode: input.mode,
4449
+ verdict: input.verdict,
4450
+ ...input.code !== undefined && input.code !== "" ? { code: input.code } : {}
4451
+ };
4452
+ appendEvent(input.harnessDir, event);
4453
+ try {
4454
+ invalidator?.(input.harnessDir);
4455
+ } catch (error) {
4456
+ log("error", `catalog invalidation failed (contained): ${errorMessage(error)}`);
4457
+ }
4458
+ } catch (error) {
4459
+ log("error", `workflow verdict record failed (contained — the gate proceeds): ${errorMessage(error)}`);
4460
+ }
4461
+ }
4143
4462
  function eventFromUnknown(value) {
4144
4463
  const rec = asRecord(value);
4145
4464
  if (rec === undefined || rec.v !== 1 || typeof rec.ts !== "number" || !Number.isFinite(rec.ts))
4146
4465
  return;
4147
4466
  const kind = rec.kind;
4148
- if (kind !== "dispatch" && kind !== "settle")
4467
+ if (kind !== "dispatch" && kind !== "settle" && kind !== "workflow-verdict" && kind !== "workflow-run" && kind !== "workflow-agent" && kind !== "workflow-run-end") {
4149
4468
  return;
4469
+ }
4150
4470
  const agent = typeof rec.agent === "string" && rec.agent !== "" ? rec.agent : undefined;
4151
4471
  if (kind === "dispatch") {
4152
4472
  if (typeof rec.role !== "string" || typeof rec.hard !== "boolean")
@@ -4167,19 +4487,92 @@ function eventFromUnknown(value) {
4167
4487
  hard: rec.hard
4168
4488
  };
4169
4489
  }
4170
- const outcome = rec.outcome;
4171
- if (outcome !== "ok" && outcome !== "error" && outcome !== "denied")
4490
+ if (kind === "settle") {
4491
+ const outcome = rec.outcome;
4492
+ if (outcome !== "ok" && outcome !== "error" && outcome !== "denied")
4493
+ return;
4494
+ return {
4495
+ v: 1,
4496
+ ts: rec.ts,
4497
+ kind: "settle",
4498
+ ...agent !== undefined ? { agent } : {},
4499
+ outcome,
4500
+ ...typeof rec.durationMs === "number" && Number.isFinite(rec.durationMs) ? { durationMs: rec.durationMs } : {},
4501
+ ...typeof rec.role === "string" ? { role: rec.role } : {},
4502
+ ...typeof rec.planId === "string" && rec.planId !== "" ? { planId: rec.planId } : {},
4503
+ ...typeof rec.taskId === "string" && rec.taskId !== "" ? { taskId: rec.taskId } : {}
4504
+ };
4505
+ }
4506
+ if (kind === "workflow-verdict") {
4507
+ if (rec.tool !== "workflow" && rec.tool !== "ralph")
4508
+ return;
4509
+ const mode = rec.mode;
4510
+ if (mode !== "off" && mode !== "warn" && mode !== "ask" && mode !== "hard")
4511
+ return;
4512
+ const verdict = rec.verdict;
4513
+ if (verdict !== "ok" && verdict !== "advisory" && verdict !== "denied" && verdict !== "ask")
4514
+ return;
4515
+ if (rec.tool === "workflow") {
4516
+ if (typeof rec.workflow !== "string" || rec.workflow === "")
4517
+ return;
4518
+ } else if (typeof rec.objective !== "string" || rec.objective === "") {
4519
+ return;
4520
+ }
4521
+ return {
4522
+ v: 1,
4523
+ ts: rec.ts,
4524
+ kind: "workflow-verdict",
4525
+ ...agent !== undefined ? { agent } : {},
4526
+ tool: rec.tool,
4527
+ ...typeof rec.workflow === "string" && rec.workflow !== "" ? { workflow: truncateLedgerField(rec.workflow, WORKFLOW_LEDGER_MAX_NAME_LENGTH) } : {},
4528
+ ...typeof rec.objective === "string" && rec.objective !== "" ? { objective: truncateLedgerField(rec.objective, WORKFLOW_LEDGER_MAX_NAME_LENGTH) } : {},
4529
+ mode,
4530
+ verdict,
4531
+ ...typeof rec.code === "string" && rec.code !== "" ? { code: rec.code } : {}
4532
+ };
4533
+ }
4534
+ if (typeof rec.runId !== "string" || rec.runId === "" || rec.runId.length > WORKFLOW_LEDGER_MAX_ID_LENGTH)
4535
+ return;
4536
+ if (kind === "workflow-run") {
4537
+ if (typeof rec.name !== "string" || rec.name === "")
4538
+ return;
4539
+ return {
4540
+ v: 1,
4541
+ ts: rec.ts,
4542
+ kind: "workflow-run",
4543
+ ...agent !== undefined ? { agent } : {},
4544
+ runId: rec.runId,
4545
+ name: truncateLedgerField(rec.name, WORKFLOW_LEDGER_MAX_NAME_LENGTH)
4546
+ };
4547
+ }
4548
+ if (kind === "workflow-agent") {
4549
+ if (typeof rec.seq !== "number" || !Number.isInteger(rec.seq) || rec.seq < 1 || rec.seq >= WORKFLOW_LEDGER_MAX_SEQ) {
4550
+ return;
4551
+ }
4552
+ if (typeof rec.label !== "string" || rec.label === "")
4553
+ return;
4554
+ if (typeof rec.childId !== "string" || rec.childId === "" || rec.childId.length > WORKFLOW_LEDGER_MAX_ID_LENGTH)
4555
+ return;
4556
+ return {
4557
+ v: 1,
4558
+ ts: rec.ts,
4559
+ kind: "workflow-agent",
4560
+ runId: rec.runId,
4561
+ seq: rec.seq,
4562
+ label: truncateLedgerField(rec.label, WORKFLOW_LEDGER_MAX_LABEL_LENGTH),
4563
+ ...typeof rec.phase === "string" ? { phase: truncateLedgerField(rec.phase, WORKFLOW_LEDGER_MAX_LABEL_LENGTH) } : {},
4564
+ childId: rec.childId
4565
+ };
4566
+ }
4567
+ const stopReason = rec.stopReason;
4568
+ if (stopReason !== "completed" && stopReason !== "cancelled" && stopReason !== "error")
4172
4569
  return;
4173
4570
  return {
4174
4571
  v: 1,
4175
4572
  ts: rec.ts,
4176
- kind: "settle",
4177
- ...agent !== undefined ? { agent } : {},
4178
- outcome,
4179
- ...typeof rec.durationMs === "number" && Number.isFinite(rec.durationMs) ? { durationMs: rec.durationMs } : {},
4180
- ...typeof rec.role === "string" ? { role: rec.role } : {},
4181
- ...typeof rec.planId === "string" && rec.planId !== "" ? { planId: rec.planId } : {},
4182
- ...typeof rec.taskId === "string" && rec.taskId !== "" ? { taskId: rec.taskId } : {}
4573
+ kind: "workflow-run-end",
4574
+ runId: rec.runId,
4575
+ stopReason
4183
4576
  };
4184
4577
  }
4185
4578
  function eventView(event) {
@@ -4196,6 +4589,65 @@ function eventView(event) {
4196
4589
  ...event.hard !== undefined ? { hard: event.hard } : {}
4197
4590
  };
4198
4591
  }
4592
+ if (event.kind === "workflow-run") {
4593
+ return {
4594
+ ts: event.ts,
4595
+ kind: "workflow-run",
4596
+ agent: event.agent ?? null,
4597
+ role: "",
4598
+ planId: null,
4599
+ taskId: null,
4600
+ taskCategory: null,
4601
+ runId: event.runId,
4602
+ name: event.name
4603
+ };
4604
+ }
4605
+ if (event.kind === "workflow-agent") {
4606
+ return {
4607
+ ts: event.ts,
4608
+ kind: "workflow-agent",
4609
+ agent: null,
4610
+ role: "",
4611
+ planId: null,
4612
+ taskId: null,
4613
+ taskCategory: null,
4614
+ runId: event.runId,
4615
+ seq: event.seq,
4616
+ label: event.label,
4617
+ ...event.phase !== undefined ? { phase: event.phase } : {},
4618
+ childId: event.childId
4619
+ };
4620
+ }
4621
+ if (event.kind === "workflow-run-end") {
4622
+ return {
4623
+ ts: event.ts,
4624
+ kind: "workflow-run-end",
4625
+ agent: null,
4626
+ role: "",
4627
+ planId: null,
4628
+ taskId: null,
4629
+ taskCategory: null,
4630
+ runId: event.runId,
4631
+ stopReason: event.stopReason
4632
+ };
4633
+ }
4634
+ if (event.kind === "workflow-verdict") {
4635
+ return {
4636
+ ts: event.ts,
4637
+ kind: "workflow-verdict",
4638
+ agent: event.agent ?? null,
4639
+ role: "",
4640
+ planId: null,
4641
+ taskId: null,
4642
+ taskCategory: null,
4643
+ tool: event.tool,
4644
+ ...event.workflow !== undefined ? { workflow: event.workflow } : {},
4645
+ ...event.objective !== undefined ? { objective: event.objective } : {},
4646
+ mode: event.mode,
4647
+ verdict: event.verdict,
4648
+ ...event.code !== undefined ? { code: event.code } : {}
4649
+ };
4650
+ }
4199
4651
  return {
4200
4652
  ts: event.ts,
4201
4653
  kind: "settle",
@@ -4212,8 +4664,8 @@ function eventView(event) {
4212
4664
  function summaryOf(events) {
4213
4665
  const counts = new Map;
4214
4666
  for (const event of events) {
4215
- const role = event.kind === "dispatch" ? event.role : "";
4216
- const outcome = event.kind === "dispatch" ? event.verdict : event.outcome;
4667
+ const role = event.kind === "dispatch" ? event.role : event.kind === "workflow-run" || event.kind === "workflow-agent" || event.kind === "workflow-run-end" || event.kind === "workflow-verdict" ? "workflow" : "";
4668
+ const outcome = event.kind === "dispatch" ? event.verdict : event.kind === "workflow-verdict" ? event.verdict : event.kind === "settle" ? event.outcome : event.kind;
4217
4669
  const key = `${role}\x00${outcome}`;
4218
4670
  counts.set(key, (counts.get(key) ?? 0) + 1);
4219
4671
  }
@@ -4832,6 +5284,18 @@ function stripFrontmatter(text) {
4832
5284
  }
4833
5285
  return text;
4834
5286
  }
5287
+ var EPHEMERAL_CODES = {
5288
+ "task-artifact": "skill.ephemeral.task-artifact",
5289
+ "sdd-deeplink": "skill.ephemeral.sdd-deeplink"
5290
+ };
5291
+ function ephemeralViolation(citation) {
5292
+ return {
5293
+ ok: false,
5294
+ severity: "medium",
5295
+ code: EPHEMERAL_CODES[citation.kind],
5296
+ message: `ephemeral citation \`${citation.match}\` on line ${citation.line} — durable skill text must reference in-repo artifacts only (knowledge conventions/skill-content-porting-discipline.md §3)`
5297
+ };
5298
+ }
4835
5299
  function lintSkillDoc(doc) {
4836
5300
  const violations = [];
4837
5301
  const frontmatter = lintSkillFrontmatter(doc);
@@ -4840,6 +5304,7 @@ function lintSkillDoc(doc) {
4840
5304
  const body = lintFiveQuestion(stripFrontmatter(doc));
4841
5305
  if (!body.ok)
4842
5306
  violations.push(...body.violations);
5307
+ violations.push(...findEphemeralCitations(doc).map(ephemeralViolation));
4843
5308
  return violations.length === 0 ? { ok: true, violations } : { ok: false, violations };
4844
5309
  }
4845
5310
  function lintSkillWrite(doc, options) {
@@ -5512,12 +5977,14 @@ class DshHostAdapter extends Service2 {
5512
5977
  resolver;
5513
5978
  config;
5514
5979
  pairing;
5980
+ workflowAskCache;
5515
5981
  logSink;
5516
5982
  constructor(ctx, options) {
5517
5983
  super(ctx, "dshHostAdapter");
5518
5984
  this.resolver = options.resolver;
5519
5985
  this.config = options.config;
5520
5986
  this.pairing = options.pairing;
5987
+ this.workflowAskCache = options.workflowAskCache ?? new WorkflowAskCache;
5521
5988
  this.logSink = options.log ?? ((level, msg) => {
5522
5989
  const logger = ctx.logger(HOST_LOGGER);
5523
5990
  if (level === "warn")
@@ -5558,6 +6025,9 @@ class DshHostAdapter extends Service2 {
5558
6025
  }
5559
6026
  return { ok: violations.length === 0, violations };
5560
6027
  }
6028
+ recordWorkflowVerdict(input) {
6029
+ recordWorkflowVerdict(input);
6030
+ }
5561
6031
  async beforeStatusWrite(path, doc) {
5562
6032
  const gate2 = doc !== undefined ? validateStatusValue(doc) : this.statusGate(path);
5563
6033
  if (!gate2.ok) {
@@ -5578,28 +6048,91 @@ class DshHostAdapter extends Service2 {
5578
6048
  }
5579
6049
  }
5580
6050
 
5581
- // src/index.ts
5582
- var name = "dsh";
5583
- var inject = [];
5584
- function packagedCommandsDir() {
6051
+ // src/gates/agent-personas.ts
6052
+ import { readFileSync as readFileSync12, readdirSync as readdirSync7, statSync as statSync4 } from "node:fs";
6053
+ import { join as join16, resolve as resolve14, sep as sep4 } from "node:path";
6054
+ var ROLE_ID_PATTERN = /^[a-z0-9-]{1,32}$/;
6055
+ var defaultCache = new Map;
6056
+ function personaFor(roleId, lookup, warn) {
6057
+ const configured = lookup.rolePersonas?.[roleId];
6058
+ if (configured !== undefined && configured !== "")
6059
+ return { text: configured, source: "config" };
6060
+ if (lookup.agentsDir === undefined)
6061
+ return;
6062
+ const text = defaultFromMirror(lookup.agentsDir, roleId, warn);
6063
+ if (text === undefined || text.trim() === "")
6064
+ return;
6065
+ return { text, source: "default" };
6066
+ }
6067
+ function subagentRoleIds(agentsDir) {
6068
+ let names;
5585
6069
  try {
5586
- const dir = fileURLToPath3(new URL("../harness-commands", import.meta.url));
5587
- return existsSync16(dir) ? dir : undefined;
6070
+ names = readdirSync7(agentsDir);
5588
6071
  } catch {
5589
- return;
6072
+ return [];
6073
+ }
6074
+ const ids = [];
6075
+ for (const name of names) {
6076
+ if (!name.endsWith(".md"))
6077
+ continue;
6078
+ const roleId = name.slice(0, -3);
6079
+ let content;
6080
+ try {
6081
+ content = readFileSync12(join16(agentsDir, name), "utf8");
6082
+ } catch {
6083
+ continue;
6084
+ }
6085
+ const mode = parseShellFrontmatter(content).mode;
6086
+ if (mode === undefined || mode === "subagent")
6087
+ ids.push(roleId);
5590
6088
  }
6089
+ return ids.sort();
5591
6090
  }
5592
- function commandFrontmatterField(frontmatter, label) {
5593
- const match = new RegExp(`^${label}[ \\t]*:[ \\t]*(.+)$`, "m").exec(frontmatter);
5594
- return match?.[1]?.trim();
6091
+ function defaultFromMirror(agentsDir, roleId, warn) {
6092
+ if (!ROLE_ID_PATTERN.test(roleId))
6093
+ return;
6094
+ const root = resolve14(agentsDir);
6095
+ const file = resolve14(agentsDir, `${roleId}.md`);
6096
+ if (!file.startsWith(root + sep4))
6097
+ return;
6098
+ let stat;
6099
+ try {
6100
+ stat = statSync4(file);
6101
+ } catch {
6102
+ return;
6103
+ }
6104
+ if (!stat.isFile())
6105
+ return;
6106
+ const hit = defaultCache.get(file);
6107
+ if (hit !== undefined && hit.mtimeMs === stat.mtimeMs)
6108
+ return hit.text;
6109
+ let content;
6110
+ try {
6111
+ content = readFileSync12(file, "utf8");
6112
+ } catch {
6113
+ return;
6114
+ }
6115
+ const text = extractShellPersona(content, roleId, warn);
6116
+ defaultCache.set(file, { mtimeMs: stat.mtimeMs, text });
6117
+ return text;
5595
6118
  }
5596
- function unquote(value) {
5597
- return value.length >= 2 && value.startsWith('"') && value.endsWith('"') ? value.slice(1, -1) : value;
6119
+ function extractShellPersona(content, roleId, warn) {
6120
+ const parsed = parseShellFrontmatter(content);
6121
+ const description = parsed.description;
6122
+ if (description === undefined || description.trim() === "")
6123
+ return;
6124
+ if (parsed.mode !== undefined && parsed.mode !== "subagent")
6125
+ return;
6126
+ if (PERSONA_INTERPOLATION_HAZARD.test(description)) {
6127
+ warn?.(`harness-agents default skipped for role '${roleId}' — description contains a "{{" paired with a later "}}" (dsh system-prompt strict interpolation renders persona text and throws on unknown or malformed references — use single braces or reword)`);
6128
+ return;
6129
+ }
6130
+ return description;
5598
6131
  }
5599
- function parseCommandMarkdown(content) {
6132
+ function parseShellFrontmatter(content) {
5600
6133
  const lines = content.replace(/^\uFEFF/, "").split(/\r?\n/);
5601
6134
  if (lines.length === 0 || lines[0].trim() !== "---")
5602
- return;
6135
+ return {};
5603
6136
  let end = -1;
5604
6137
  for (let i = 1;i < lines.length; i++) {
5605
6138
  if (lines[i].trim() === "---") {
@@ -5608,34 +6141,1169 @@ function parseCommandMarkdown(content) {
5608
6141
  }
5609
6142
  }
5610
6143
  if (end === -1)
5611
- return;
5612
- const frontmatter = lines.slice(1, end).join(`
5613
- `);
5614
- const name2 = commandFrontmatterField(frontmatter, "name");
5615
- const description = commandFrontmatterField(frontmatter, "description");
5616
- if (name2 === undefined || description === undefined)
5617
- return;
5618
- const input = commandFrontmatterField(frontmatter, "input");
5619
- return {
5620
- name: name2,
5621
- description: unquote(description),
5622
- ...input !== undefined && input !== "" ? { input: unquote(input) } : {},
5623
- body: lines.slice(end + 1).join(`
5624
- `).trim()
5625
- };
6144
+ return {};
6145
+ const frontmatter = lines.slice(1, end);
6146
+ const result = {};
6147
+ for (let i = 0;i < frontmatter.length; i++) {
6148
+ const line = frontmatter[i];
6149
+ const key = /^([A-Za-z][A-Za-z0-9_-]*)[ \t]*:[ \t]*(.*)$/.exec(line);
6150
+ if (key === null)
6151
+ continue;
6152
+ const label = key[1];
6153
+ const rest = key[2];
6154
+ if (label === "description") {
6155
+ const block = /^\|[+-]?(?<indent>\d*)[ \t]*$/.exec(rest);
6156
+ result.description = block !== null ? extractBlockScalar(frontmatter, i + 1, block.groups?.indent) : unquote(rest.trim());
6157
+ } else if (label === "mode") {
6158
+ result.mode = unquote(rest.trim());
6159
+ }
6160
+ }
6161
+ return result;
5626
6162
  }
5627
- function registerMstarCommands(ctx) {
5628
- const dir = packagedCommandsDir();
5629
- if (dir === undefined)
5630
- return;
5631
- ctx.inject(["commands"], (commandsCtx) => {
5632
- for (const file of readdirSync7(dir).sort()) {
5633
- if (!file.endsWith(".md"))
5634
- continue;
5635
- const parsed = parseCommandMarkdown(readFileSync12(join16(dir, file), "utf8"));
5636
- if (parsed === undefined)
5637
- continue;
5638
- commandsCtx.commands.register({
6163
+ function extractBlockScalar(frontmatter, start, explicitIndent) {
6164
+ const blockLines = [];
6165
+ for (let i = start;i < frontmatter.length; i++) {
6166
+ const line = frontmatter[i];
6167
+ if (line.trim() === "") {
6168
+ blockLines.push("");
6169
+ continue;
6170
+ }
6171
+ if (/^[ \t]/.test(line)) {
6172
+ blockLines.push(line);
6173
+ continue;
6174
+ }
6175
+ break;
6176
+ }
6177
+ let indent = explicitIndent !== undefined && explicitIndent !== "" ? Number(explicitIndent) : undefined;
6178
+ if (indent === undefined) {
6179
+ const first = blockLines.find((line) => line.trim() !== "");
6180
+ indent = first !== undefined ? /^[ \t]*/.exec(first)[0].length : 0;
6181
+ }
6182
+ return blockLines.map((line) => line.trim() === "" ? "" : line.slice(Math.min(indent, line.length))).join(`
6183
+ `).replace(/\n+$/, "");
6184
+ }
6185
+ function unquote(value) {
6186
+ return value.length >= 2 && value.startsWith('"') && value.endsWith('"') ? value.slice(1, -1) : value;
6187
+ }
6188
+
6189
+ // src/gates/fallbacks-probe.ts
6190
+ var FALLBACKS_ENTRY_NAME = "dsh-llm-fallbacks";
6191
+ function fallbacksService(ctx) {
6192
+ return ctx.get("llm-fallbacks");
6193
+ }
6194
+ function fallbacksEntry(ctx) {
6195
+ const loader = ctx.get("loader");
6196
+ if (loader === undefined)
6197
+ return;
6198
+ for (const entry of loader.entries()) {
6199
+ if (entry.options.name !== FALLBACKS_ENTRY_NAME)
6200
+ continue;
6201
+ if (entry.options.group)
6202
+ continue;
6203
+ if (entry.disabled)
6204
+ continue;
6205
+ return entry;
6206
+ }
6207
+ return;
6208
+ }
6209
+ function fallbacksMounted(ctx) {
6210
+ if (fallbacksService(ctx) !== undefined)
6211
+ return true;
6212
+ const entry = fallbacksEntry(ctx);
6213
+ if (entry === undefined)
6214
+ return false;
6215
+ return entry.fiber !== undefined;
6216
+ }
6217
+
6218
+ // src/gates/fallbacks-decoration.ts
6219
+ var DECORATION_LOGGER = "mstar/subagent-decoration";
6220
+ var PERSONA_SECTION_NAME = "mstar:role-persona";
6221
+ var PERSONA_SECTION_ORDER = 1;
6222
+ var decorationLogSink = () => {};
6223
+ function setDecorationLogger(sink) {
6224
+ const prior = decorationLogSink;
6225
+ decorationLogSink = sink;
6226
+ return prior;
6227
+ }
6228
+ var decorationAgentsDir;
6229
+ var mirrorAbsentDebugged = false;
6230
+ function setDecorationAgentsDir(dir) {
6231
+ const prior = decorationAgentsDir;
6232
+ decorationAgentsDir = dir;
6233
+ mirrorAbsentDebugged = false;
6234
+ return prior;
6235
+ }
6236
+ function seededTaskPrompt(child) {
6237
+ for (const event of child.session.events) {
6238
+ if (event.type !== "user/message")
6239
+ continue;
6240
+ const text = event.data.content.filter((block) => block.type === "text").map((block) => block.text).join(`
6241
+ `);
6242
+ if (text.trim().length > 0)
6243
+ return text;
6244
+ }
6245
+ return;
6246
+ }
6247
+ function decorateSubagentStart(ctx, config, info) {
6248
+ try {
6249
+ const personas = config.rolePersonas;
6250
+ const agentsDir = decorationAgentsDir;
6251
+ if ((personas == null || Object.keys(personas).length === 0) && agentsDir === undefined)
6252
+ return;
6253
+ const agents = ctx.get("agents");
6254
+ if (agents === undefined) {
6255
+ log2("debug", "agents service absent — mstar:role-persona decoration skipped (composition without dsh-agent)");
6256
+ return;
6257
+ }
6258
+ const child = agents.get(info.id);
6259
+ if (child === undefined)
6260
+ return;
6261
+ const taskPrompt = seededTaskPrompt(child);
6262
+ if (taskPrompt === undefined)
6263
+ return;
6264
+ const executeAs = parseAssignmentFields(assignmentHeaderRegion(taskPrompt)).executeAs;
6265
+ if (executeAs === undefined)
6266
+ return;
6267
+ const persona = personaFor(executeAs, { rolePersonas: config.rolePersonas, agentsDir }, (message) => log2("warn", message));
6268
+ if (persona === undefined) {
6269
+ if (agentsDir === undefined && !mirrorAbsentDebugged) {
6270
+ mirrorAbsentDebugged = true;
6271
+ log2("debug", "harness-agents mirror absent — mstar:role-persona skipped (config-only lookups; mirror defaults unavailable)");
6272
+ }
6273
+ return;
6274
+ }
6275
+ child.ctx.systemPrompt.section({
6276
+ name: PERSONA_SECTION_NAME,
6277
+ order: PERSONA_SECTION_ORDER,
6278
+ text: persona.text
6279
+ });
6280
+ if (fallbacksMounted(ctx)) {
6281
+ log2("info", `dsh-llm-fallbacks mounted (v${fallbacksService(ctx)?.version ?? "unknown"}) — mstar:role-persona injected for role '${executeAs}'`);
6282
+ } else {
6283
+ log2("debug", `dsh-llm-fallbacks unmounted — mstar:role-persona injected from ${persona.source === "config" ? "mstar Config" : "harness-agents default"} for role '${executeAs}' (decoration channel unchanged)`);
6284
+ }
6285
+ } catch (error) {
6286
+ log2("warn", `mstar:role-persona decoration aborted (degraded — subagent dispatch unaffected): ${errorMessage2(error)}`);
6287
+ }
6288
+ }
6289
+ function errorMessage2(error) {
6290
+ return error instanceof Error ? error.message : String(error);
6291
+ }
6292
+ function log2(level, message) {
6293
+ try {
6294
+ decorationLogSink(level, message);
6295
+ } catch {}
6296
+ }
6297
+
6298
+ // src/gates/workflow-ledger.ts
6299
+ import { readFileSync as readFileSync13, renameSync as renameSync3, writeFileSync as writeFileSync4 } from "node:fs";
6300
+ import { join as join17 } from "node:path";
6301
+ var WORKFLOW_LEDGER_LOGGER = "mstar/workflow-ledger";
6302
+ var TOOL_WORKFLOW_RUN_START = "tool-workflow/run-start";
6303
+ var TOOL_WORKFLOW_AGENT_START = "tool-workflow/agent-start";
6304
+ var TOOL_WORKFLOW_AGENT_END = "tool-workflow/agent-end";
6305
+ var TOOL_WORKFLOW_RUN_END = "tool-workflow/run-end";
6306
+ var DEPTH_ADVISORY_THRESHOLD = 2;
6307
+ var WORKFLOW_LEDGER_WATERMARK_FILE = "workflow-ledger-cursors.json";
6308
+ var WORKFLOW_LEDGER_WATERMARK_MAX_SESSIONS = 256;
6309
+ var workflowLedgerLogSink = () => {};
6310
+ function setWorkflowLedgerLogger(sink) {
6311
+ const prior = workflowLedgerLogSink;
6312
+ workflowLedgerLogSink = sink;
6313
+ return prior;
6314
+ }
6315
+ function log3(level, message) {
6316
+ try {
6317
+ workflowLedgerLogSink(level, message);
6318
+ } catch {}
6319
+ }
6320
+ function errorMessage3(error) {
6321
+ return error instanceof Error ? error.message : String(error);
6322
+ }
6323
+ function sessionIdOf2(session) {
6324
+ const id = session?.id;
6325
+ return typeof id === "string" && id !== "" ? id : undefined;
6326
+ }
6327
+ var watermarkCache = new Map;
6328
+ function loadWatermark(harnessDir) {
6329
+ const cached = watermarkCache.get(harnessDir);
6330
+ if (cached !== undefined)
6331
+ return cached;
6332
+ const watermark = new Map;
6333
+ try {
6334
+ const raw = readFileSync13(join17(harnessDir, WORKFLOW_LEDGER_WATERMARK_FILE), "utf8");
6335
+ const record = asRecord(JSON.parse(raw));
6336
+ const cursors = asRecord(record?.cursors);
6337
+ if (cursors !== undefined) {
6338
+ for (const [sid, next] of Object.entries(cursors)) {
6339
+ if (typeof next === "number" && Number.isInteger(next) && next >= 1 && next < WORKFLOW_LEDGER_MAX_SEQ && sid !== "") {
6340
+ watermark.set(sid, next);
6341
+ }
6342
+ }
6343
+ }
6344
+ } catch (error) {
6345
+ const err = error;
6346
+ if (err?.code !== "ENOENT") {
6347
+ log3("warn", `workflow-ledger watermark unreadable for ${harnessDir} — degrading to in-memory-only (restart re-records): ${errorMessage3(error)}`);
6348
+ }
6349
+ }
6350
+ watermarkCache.set(harnessDir, watermark);
6351
+ return watermark;
6352
+ }
6353
+ function saveWatermark(harnessDir, watermark) {
6354
+ try {
6355
+ const file = join17(harnessDir, WORKFLOW_LEDGER_WATERMARK_FILE);
6356
+ const tmp = `${file}.tmp`;
6357
+ writeFileSync4(tmp, JSON.stringify({ v: 1, cursors: Object.fromEntries(watermark) }));
6358
+ renameSync3(tmp, file);
6359
+ } catch (error) {
6360
+ log3("warn", `workflow-ledger watermark write failed for ${harnessDir} — in-memory only (restart re-records): ${errorMessage3(error)}`);
6361
+ }
6362
+ }
6363
+ function advanceWatermark(harnessDir, sid, nextSeq, isEvictable) {
6364
+ const watermark = loadWatermark(harnessDir);
6365
+ if (!watermark.has(sid) && watermark.size >= WORKFLOW_LEDGER_WATERMARK_MAX_SESSIONS) {
6366
+ let victim;
6367
+ for (const key of watermark.keys()) {
6368
+ if (isEvictable(key)) {
6369
+ victim = key;
6370
+ break;
6371
+ }
6372
+ }
6373
+ victim ??= watermark.keys().next().value;
6374
+ if (victim !== undefined) {
6375
+ watermark.delete(victim);
6376
+ log3("warn", `workflow-ledger watermark capped at ${WORKFLOW_LEDGER_WATERMARK_MAX_SESSIONS} sessions — evicted ${victim} (a restored evicted session re-records; bounded by the cap)`);
6377
+ }
6378
+ }
6379
+ watermark.set(sid, nextSeq);
6380
+ saveWatermark(harnessDir, watermark);
6381
+ }
6382
+ function rowOf(session, envelope) {
6383
+ const record = asRecord(envelope);
6384
+ if (record === undefined)
6385
+ return;
6386
+ const type = record.type;
6387
+ if (typeof type !== "string")
6388
+ return;
6389
+ const seq = record.seq;
6390
+ if (typeof seq !== "number" || !Number.isInteger(seq) || seq < 0 || seq >= WORKFLOW_LEDGER_MAX_SEQ)
6391
+ return;
6392
+ const time = record.time;
6393
+ if (typeof time !== "number" || !Number.isFinite(time))
6394
+ return;
6395
+ const data = asRecord(record.data);
6396
+ if (data === undefined)
6397
+ return;
6398
+ const runId = data.runId;
6399
+ if (typeof runId !== "string" || runId === "" || runId.length > WORKFLOW_LEDGER_MAX_ID_LENGTH)
6400
+ return;
6401
+ if (type === TOOL_WORKFLOW_RUN_START) {
6402
+ const name = typeof data.name === "string" ? normalizeWorkflowName(data.name) : data.name;
6403
+ if (typeof name !== "string" || name === "")
6404
+ return;
6405
+ const agent = sessionIdOf2(session);
6406
+ return {
6407
+ seq,
6408
+ runName: name,
6409
+ event: {
6410
+ v: 1,
6411
+ ts: time,
6412
+ kind: "workflow-run",
6413
+ runId,
6414
+ name: truncateLedgerField(name, WORKFLOW_LEDGER_MAX_NAME_LENGTH),
6415
+ ...agent !== undefined ? { agent } : {}
6416
+ }
6417
+ };
6418
+ }
6419
+ if (type === TOOL_WORKFLOW_AGENT_START) {
6420
+ if (typeof data.seq !== "number" || !Number.isInteger(data.seq) || data.seq < 1 || data.seq >= WORKFLOW_LEDGER_MAX_SEQ) {
6421
+ return;
6422
+ }
6423
+ const label = typeof data.label === "string" ? normalizeWorkflowName(data.label) : data.label;
6424
+ if (typeof label !== "string" || label === "")
6425
+ return;
6426
+ if (typeof data.childId !== "string" || data.childId === "" || data.childId.length > WORKFLOW_LEDGER_MAX_ID_LENGTH)
6427
+ return;
6428
+ return {
6429
+ seq,
6430
+ childId: data.childId,
6431
+ event: {
6432
+ v: 1,
6433
+ ts: time,
6434
+ kind: "workflow-agent",
6435
+ runId,
6436
+ seq: data.seq,
6437
+ label: truncateLedgerField(label, WORKFLOW_LEDGER_MAX_LABEL_LENGTH),
6438
+ ...typeof data.phase === "string" ? { phase: truncateLedgerField(normalizeWorkflowName(data.phase), WORKFLOW_LEDGER_MAX_LABEL_LENGTH) } : {},
6439
+ childId: data.childId
6440
+ }
6441
+ };
6442
+ }
6443
+ if (type === TOOL_WORKFLOW_AGENT_END) {
6444
+ return;
6445
+ }
6446
+ if (type === TOOL_WORKFLOW_RUN_END) {
6447
+ const stopReason = data.stopReason;
6448
+ if (stopReason !== "completed" && stopReason !== "cancelled" && stopReason !== "error")
6449
+ return;
6450
+ return {
6451
+ seq,
6452
+ event: { v: 1, ts: time, kind: "workflow-run-end", runId, stopReason }
6453
+ };
6454
+ }
6455
+ return;
6456
+ }
6457
+ function depthAdvisory(sessions, row, warned) {
6458
+ if (row.event.kind !== "workflow-agent" || row.childId === undefined)
6459
+ return;
6460
+ if (warned.has(row.event.runId))
6461
+ return;
6462
+ const child = sessions.get(row.childId);
6463
+ const delegationDepth = child?.header?.delegationDepth;
6464
+ if (typeof delegationDepth !== "number" || !Number.isFinite(delegationDepth) || delegationDepth < DEPTH_ADVISORY_THRESHOLD) {
6465
+ return;
6466
+ }
6467
+ warned.add(row.event.runId);
6468
+ log3("warn", `workflow run ${row.event.runId} member '${row.event.label}' (child ${row.childId}) is at delegation depth ${delegationDepth} (>= ${DEPTH_ADVISORY_THRESHOLD}) — advisory only, the run proceeds`);
6469
+ }
6470
+ function registerWorkflowLedger(ctx, resolver, workflowAskCache) {
6471
+ const sessions = ctx.get("sessions");
6472
+ if (sessions === undefined) {
6473
+ log3("debug", "sessions service absent — workflow-ledger consumer disabled (composition without dsh-session)");
6474
+ return;
6475
+ }
6476
+ const depthWarned = new Set;
6477
+ const consume = (session, envelope) => {
6478
+ const sid = sessionIdOf2(session);
6479
+ if (sid === undefined)
6480
+ return;
6481
+ const row = rowOf(session, envelope);
6482
+ if (row === undefined)
6483
+ return;
6484
+ const cwd = session?.header?.cwd;
6485
+ const harnessDir = resolver.forWorkspace(typeof cwd === "string" && cwd.trim() !== "" ? cwd : undefined);
6486
+ if (harnessDir === null)
6487
+ return;
6488
+ const watermark = loadWatermark(harnessDir);
6489
+ let next = watermark.get(sid) ?? 0;
6490
+ const events = session.events;
6491
+ if (Array.isArray(events) && events.length < next)
6492
+ next = 0;
6493
+ if (row.seq < next)
6494
+ return;
6495
+ if (recordWorkflowEvent({ harnessDir, event: row.event })) {
6496
+ advanceWatermark(harnessDir, sid, row.seq + 1, (candidate) => sessions.get(candidate) === undefined);
6497
+ if (workflowAskCache !== undefined && row.event.kind === "workflow-run") {
6498
+ try {
6499
+ const runName = row.runName ?? row.event.name;
6500
+ if (workflowAskCache.wasAsked(runName)) {
6501
+ workflowAskCache.record(runName, "allow");
6502
+ }
6503
+ } catch (error) {
6504
+ log3("warn", `workflow P-c allow observation degraded (contained — the ledger row stays): ${errorMessage3(error)}`);
6505
+ }
6506
+ }
6507
+ }
6508
+ if (row.event.kind === "workflow-agent") {
6509
+ try {
6510
+ depthAdvisory(sessions, row, depthWarned);
6511
+ } catch (error) {
6512
+ log3("warn", `workflow depth advisory degraded (contained — the run proceeds): ${errorMessage3(error)}`);
6513
+ }
6514
+ }
6515
+ };
6516
+ const scanSession = (session) => {
6517
+ const sid = sessionIdOf2(session);
6518
+ if (sid === undefined)
6519
+ return;
6520
+ const events = session.events;
6521
+ if (!Array.isArray(events))
6522
+ return;
6523
+ for (const envelope of events)
6524
+ consume(session, envelope);
6525
+ };
6526
+ ctx.events.on("session/created", (session) => {
6527
+ try {
6528
+ scanSession(session);
6529
+ } catch (error) {
6530
+ log3("warn", `workflow-ledger session-created backfill failed (contained — the session publication proceeds): ${errorMessage3(error)}`);
6531
+ }
6532
+ });
6533
+ let sessionsSnapshot;
6534
+ try {
6535
+ sessionsSnapshot = sessions.list();
6536
+ } catch (error) {
6537
+ log3("warn", `workflow-ledger cold scan could not list sessions (contained — the live firehose and session-created backfill stay active): ${errorMessage3(error)}`);
6538
+ sessionsSnapshot = [];
6539
+ }
6540
+ for (const session of sessionsSnapshot) {
6541
+ const sid = sessionIdOf2(session);
6542
+ if (sid === undefined)
6543
+ continue;
6544
+ try {
6545
+ scanSession(session);
6546
+ } catch (error) {
6547
+ log3("warn", `workflow-ledger cold scan failed for session ${sid} (contained — other sessions unaffected): ${errorMessage3(error)}`);
6548
+ }
6549
+ }
6550
+ ctx.events.on("session/event", (session, envelope) => {
6551
+ try {
6552
+ consume(session, envelope);
6553
+ } catch (error) {
6554
+ log3("warn", `workflow-ledger live consume failed (contained — the workflow run proceeds): ${errorMessage3(error)}`);
6555
+ }
6556
+ });
6557
+ }
6558
+
6559
+ // src/gates/goal-bridge.ts
6560
+ import { existsSync as existsSync16, readFileSync as readFileSync14, readdirSync as readdirSync8 } from "node:fs";
6561
+ import { join as join18 } from "node:path";
6562
+ var GOAL_BRIDGE_LOGGER = "mstar/goal-bridge";
6563
+ var DEFAULT_MAX_GOAL_ROUNDS = 256;
6564
+ var FLOW_SEQUENCE = "iteration-start → per-plan cycles → iteration-close → PR delivery → merge-ready";
6565
+ var GOAL_CHANGE_EVENT_TYPE = "goal/change";
6566
+ var GOAL_CHANGE_VERSION = 1;
6567
+ var GOAL_ADVISORY_OBJECTIVE_CAP = 512;
6568
+ var GOAL_ADVISORY_MESSAGE_CAP = 512;
6569
+ var GOAL_ADVISORY_CODE_CAP = 128;
6570
+ var goalBridgeLogSink = () => {};
6571
+ function setGoalBridgeLogger(sink) {
6572
+ const prior = goalBridgeLogSink;
6573
+ goalBridgeLogSink = sink;
6574
+ return prior;
6575
+ }
6576
+ function log4(level, message) {
6577
+ goalBridgeLogSink(level, message);
6578
+ }
6579
+ function errorMessage4(error) {
6580
+ return error instanceof Error ? error.message : String(error);
6581
+ }
6582
+ function isRootLikeAgent(agent) {
6583
+ const header = agent?.session?.header;
6584
+ return header?.parentSession === undefined;
6585
+ }
6586
+ function iterationGoalObjective(iterationId) {
6587
+ return `Run iteration ${iterationId} through the complete flow to its exit: ${FLOW_SEQUENCE}. ` + "Exit: the iteration's delivery PR is merged to the target branch and the merge-ready loop closes; the harness status.json stays the source of truth.";
6588
+ }
6589
+ function steeringCompass(harnessDir) {
6590
+ const iterationsDir = resolveIterationDir(harnessDir);
6591
+ if (!existsSync16(iterationsDir))
6592
+ return;
6593
+ let entries;
6594
+ try {
6595
+ entries = readdirSync8(iterationsDir, { withFileTypes: true });
6596
+ } catch {
6597
+ return;
6598
+ }
6599
+ for (const entry of entries) {
6600
+ if (!entry.isDirectory())
6601
+ continue;
6602
+ const compassPath = join18(iterationsDir, entry.name, "delivery-compass.md");
6603
+ if (!existsSync16(compassPath))
6604
+ continue;
6605
+ let content;
6606
+ try {
6607
+ content = readFileSync14(compassPath, "utf8");
6608
+ } catch {
6609
+ continue;
6610
+ }
6611
+ const frontmatter = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
6612
+ if (frontmatter === null || !/^status[ \t]*:[ \t]*(?:active|locked)[ \t]*$/m.test(frontmatter[1]))
6613
+ continue;
6614
+ return { iterationId: entry.name };
6615
+ }
6616
+ return;
6617
+ }
6618
+ var GOAL_STALE_REVISION = "GOAL_STALE_REVISION";
6619
+ function goalErrorCode(error) {
6620
+ if (typeof error !== "object" || error === null)
6621
+ return;
6622
+ if (!("code" in error))
6623
+ return;
6624
+ const code = error.code;
6625
+ return typeof code === "string" ? code : undefined;
6626
+ }
6627
+ function resolveGoalRounds(configValue) {
6628
+ if (configValue === undefined)
6629
+ return DEFAULT_MAX_GOAL_ROUNDS;
6630
+ if (!Number.isSafeInteger(configValue) || configValue < 1) {
6631
+ log4("warn", `invalid maxGoalRounds config ${String(configValue)} (must be a positive safe integer) — falling back to the default ${DEFAULT_MAX_GOAL_ROUNDS}`);
6632
+ return DEFAULT_MAX_GOAL_ROUNDS;
6633
+ }
6634
+ return configValue;
6635
+ }
6636
+ function replaceDriftedGoal(goals, agent, current, objective, maxGoalRounds) {
6637
+ if (current.phase === "complete") {
6638
+ goals.create(agent, { objective, maxGoalRounds });
6639
+ return;
6640
+ }
6641
+ goals.complete(agent, { id: current.id, revision: current.revision });
6642
+ goals.create(agent, { objective, maxGoalRounds });
6643
+ }
6644
+ function mirrorIterationGoal(agent, input) {
6645
+ const { resolver, goals, maxGoalRounds } = input;
6646
+ if (goals === undefined)
6647
+ return false;
6648
+ if (!isRootLikeAgent(agent))
6649
+ return false;
6650
+ const harnessDir = resolver.forAgent(agent);
6651
+ if (harnessDir === null)
6652
+ return false;
6653
+ const compass = steeringCompass(harnessDir);
6654
+ if (compass === undefined)
6655
+ return false;
6656
+ const objective = iterationGoalObjective(compass.iterationId);
6657
+ try {
6658
+ const current = goals.get(agent);
6659
+ if (current === undefined) {
6660
+ goals.create(agent, { objective, maxGoalRounds });
6661
+ return true;
6662
+ }
6663
+ if (current.objective === objective)
6664
+ return true;
6665
+ try {
6666
+ replaceDriftedGoal(goals, agent, current, objective, maxGoalRounds);
6667
+ return true;
6668
+ } catch (error) {
6669
+ if (goalErrorCode(error) !== GOAL_STALE_REVISION)
6670
+ throw error;
6671
+ const fresh = goals.get(agent);
6672
+ if (fresh === undefined) {
6673
+ goals.create(agent, { objective, maxGoalRounds });
6674
+ return true;
6675
+ }
6676
+ if (fresh.objective === objective)
6677
+ return true;
6678
+ replaceDriftedGoal(goals, agent, fresh, objective, maxGoalRounds);
6679
+ return true;
6680
+ }
6681
+ } catch (error) {
6682
+ const code = goalErrorCode(error);
6683
+ log4("warn", `goal mirror failed (contained — the session/decision point proceeds)${code !== undefined ? ` [${code}]` : ""}: ${errorMessage4(error)}`);
6684
+ return false;
6685
+ }
6686
+ }
6687
+ function blockedAdvisoryOf(envelope) {
6688
+ const record = asRecord(envelope);
6689
+ if (record === undefined || record.type !== GOAL_CHANGE_EVENT_TYPE)
6690
+ return;
6691
+ const data = asRecord(record.data);
6692
+ if (data === undefined)
6693
+ return;
6694
+ if (data.version !== GOAL_CHANGE_VERSION)
6695
+ return;
6696
+ const goal = asRecord(data.goal);
6697
+ if (goal === undefined)
6698
+ return;
6699
+ if (data.operation !== "block" && goal.phase !== "blocked")
6700
+ return;
6701
+ const blockedReason = asRecord(goal.blockedReason);
6702
+ if (blockedReason === undefined)
6703
+ return;
6704
+ const code = blockedReason.code;
6705
+ const message = blockedReason.message;
6706
+ const objective = goal.objective;
6707
+ if (typeof code !== "string" || code === "" || typeof message !== "string" || message === "" || typeof objective !== "string" || objective === "") {
6708
+ return;
6709
+ }
6710
+ return { code, message, objective };
6711
+ }
6712
+ function warnBlockedGoal(harnessDir, advisory) {
6713
+ const code = truncateLedgerField(advisory.code, GOAL_ADVISORY_CODE_CAP);
6714
+ const reason = truncateLedgerField(normalizeWorkflowName(advisory.message), GOAL_ADVISORY_MESSAGE_CAP);
6715
+ const objective = truncateLedgerField(normalizeWorkflowName(advisory.objective), GOAL_ADVISORY_OBJECTIVE_CAP);
6716
+ log4("warn", `goal blocked [${code}] — ${reason}; objective: ${objective}; residuals: see ${harnessDir}/${STATUS_FILE} (residual_findings) — advisory only, zero harness writes`);
6717
+ }
6718
+ function rootAgentOf(agent, agents) {
6719
+ let current = agent;
6720
+ const seen = new Set;
6721
+ for (;; ) {
6722
+ const header = current?.session?.header;
6723
+ if (header === undefined)
6724
+ return;
6725
+ if (header.parentSession === undefined)
6726
+ return current;
6727
+ if (typeof header.parentSession !== "string")
6728
+ return;
6729
+ if (seen.has(header.parentSession))
6730
+ return;
6731
+ seen.add(header.parentSession);
6732
+ const parent = agents.get(header.parentSession);
6733
+ if (parent === undefined)
6734
+ return;
6735
+ current = parent;
6736
+ }
6737
+ }
6738
+ function registerGoalBridge(ctx, resolver, config) {
6739
+ const goals = ctx.get("goals");
6740
+ if (goals === undefined) {
6741
+ log4("debug", "goals service absent — goal bridge disabled (composition without @deepseek-ai/dsh-goal)");
6742
+ }
6743
+ const maxGoalRounds = resolveGoalRounds(config.maxGoalRounds);
6744
+ const mirror = (agent) => {
6745
+ try {
6746
+ mirrorIterationGoal(agent, { resolver, goals, maxGoalRounds });
6747
+ } catch (error) {
6748
+ log4("warn", `goal bridge mirror failed (contained — the session/decision point proceeds): ${errorMessage4(error)}`);
6749
+ }
6750
+ };
6751
+ ctx.events.on("agent/session-start", (payload) => {
6752
+ if (payload.agent !== undefined)
6753
+ mirror(payload.agent);
6754
+ });
6755
+ ctx.events.on("subagent/start", (info) => {
6756
+ const agents = ctx.get("agents");
6757
+ if (agents === undefined || typeof info.id !== "string")
6758
+ return;
6759
+ const child = agents.get(info.id);
6760
+ if (child === undefined)
6761
+ return;
6762
+ const root = rootAgentOf(child, agents);
6763
+ if (root !== undefined)
6764
+ mirror(root);
6765
+ });
6766
+ ctx.events.on("session/event", (session, envelope) => {
6767
+ try {
6768
+ const advisory = blockedAdvisoryOf(envelope);
6769
+ if (advisory === undefined)
6770
+ return;
6771
+ const cwd = session?.header?.cwd;
6772
+ const harnessDir = resolver.forWorkspace(typeof cwd === "string" && cwd.trim() !== "" ? cwd : undefined);
6773
+ if (harnessDir === null)
6774
+ return;
6775
+ warnBlockedGoal(harnessDir, advisory);
6776
+ } catch (error) {
6777
+ log4("warn", `goal blocked advisory degraded (contained — the session proceeds): ${errorMessage4(error)}`);
6778
+ }
6779
+ });
6780
+ }
6781
+
6782
+ // src/gates/plan-mode-bridge.ts
6783
+ import { existsSync as existsSync17 } from "node:fs";
6784
+ import { join as join19 } from "node:path";
6785
+ var PLAN_MODE_BRIDGE_LOGGER = "mstar/plan-mode-bridge";
6786
+ var PLAN_STATUS_TODO = "Todo";
6787
+ var planModeBridgeLogSink = () => {};
6788
+ function setPlanModeBridgeLogger(sink) {
6789
+ const prior = planModeBridgeLogSink;
6790
+ planModeBridgeLogSink = sink;
6791
+ return prior;
6792
+ }
6793
+ function log5(level, message) {
6794
+ planModeBridgeLogSink(level, message);
6795
+ }
6796
+ function errorMessage5(error) {
6797
+ return error instanceof Error ? error.message : String(error);
6798
+ }
6799
+ function hasPrepareWindow(harnessDir) {
6800
+ const statusPath = join19(harnessDir, STATUS_FILE);
6801
+ if (!existsSync17(statusPath))
6802
+ return false;
6803
+ let doc;
6804
+ try {
6805
+ doc = readJson(statusPath);
6806
+ } catch {
6807
+ return false;
6808
+ }
6809
+ const plans = doc.plans;
6810
+ if (!Array.isArray(plans))
6811
+ return false;
6812
+ return plans.some((row) => asRecord(row)?.status === PLAN_STATUS_TODO);
6813
+ }
6814
+ function planModeTarget(harnessDir) {
6815
+ if (steeringCompass(harnessDir) === undefined)
6816
+ return false;
6817
+ return hasPrepareWindow(harnessDir);
6818
+ }
6819
+ function syncPlanMode(agent, input) {
6820
+ const { resolver, planMode } = input;
6821
+ if (planMode === undefined)
6822
+ return false;
6823
+ if (!isRootLikeAgent(agent))
6824
+ return false;
6825
+ const harnessDir = resolver.forAgent(agent);
6826
+ if (harnessDir === null)
6827
+ return false;
6828
+ try {
6829
+ planMode.set(agent, planModeTarget(harnessDir));
6830
+ return true;
6831
+ } catch (error) {
6832
+ log5("warn", `planMode sync failed (contained — the session/decision point proceeds): ${errorMessage5(error)}`);
6833
+ return false;
6834
+ }
6835
+ }
6836
+ function registerPlanModeBridge(ctx, resolver) {
6837
+ const planMode = ctx.get("planMode");
6838
+ if (planMode === undefined) {
6839
+ log5("debug", "planMode service absent — plan-mode bridge disabled (composition without @deepseek-ai/dsh-plan-mode)");
6840
+ }
6841
+ const sync = (agent) => {
6842
+ try {
6843
+ syncPlanMode(agent, { resolver, planMode });
6844
+ } catch (error) {
6845
+ log5("warn", `planMode bridge sync failed (contained — the session/decision point proceeds): ${errorMessage5(error)}`);
6846
+ }
6847
+ };
6848
+ ctx.events.on("agent/session-start", (payload) => {
6849
+ if (payload.agent !== undefined)
6850
+ sync(payload.agent);
6851
+ });
6852
+ ctx.events.on("subagent/start", (info) => {
6853
+ const agents = ctx.get("agents");
6854
+ if (agents === undefined || typeof info.id !== "string")
6855
+ return;
6856
+ const child = agents.get(info.id);
6857
+ if (child === undefined)
6858
+ return;
6859
+ const root = rootAgentOf(child, agents);
6860
+ if (root !== undefined)
6861
+ sync(root);
6862
+ });
6863
+ }
6864
+
6865
+ // src/gates/fallbacks-seeds.ts
6866
+ var SEEDS_LOGGER = "mstar/fallbacks-seeds";
6867
+ var EMPTY_OUTCOME = { applied: [], skipped: [], conflicts: [] };
6868
+ function errorMessage6(error) {
6869
+ return error instanceof Error ? error.message : String(error);
6870
+ }
6871
+ function mstarLoadLine(roleId) {
6872
+ return `Load mstar-roles (references/${roleId}.md) and the role's Required Skill Dependencies before acting.`;
6873
+ }
6874
+ async function declareMstarSeeds(service, options) {
6875
+ const { agentsDir, log: log6 } = options;
6876
+ let readback;
6877
+ try {
6878
+ readback = service.getEffectiveRoles();
6879
+ } catch (error) {
6880
+ log6("warn", `fallbacks seed readback failed — mstar seeds not declared: ${errorMessage6(error)}`);
6881
+ return { declared: [], skipped: [], preserved: [], outcome: EMPTY_OUTCOME };
6882
+ }
6883
+ const mstarIds = agentsDir === undefined ? [] : subagentRoleIds(agentsDir);
6884
+ const mstarSet = new Set(mstarIds);
6885
+ const preservedCandidates = [];
6886
+ for (const row of readback.roles) {
6887
+ if (!row.seeded)
6888
+ continue;
6889
+ const id = row.id.trim();
6890
+ if (id === "" || mstarSet.has(id))
6891
+ continue;
6892
+ if (row.seedPersona === undefined || row.seedPersona.trim() === "") {
6893
+ log6("debug", `seeded row '${id}' carries no seedPersona (upstream contract: present iff seeded) — not preserved`);
6894
+ continue;
6895
+ }
6896
+ preservedCandidates.push({ id, persona: row.seedPersona });
6897
+ }
6898
+ const declared = [];
6899
+ const skipped = [];
6900
+ const forwardWarn = (message) => log6("warn", message);
6901
+ for (const roleId of mstarIds) {
6902
+ const persona = personaFor(roleId, { agentsDir }, forwardWarn);
6903
+ if (persona === undefined) {
6904
+ skipped.push({ id: roleId, reason: "no-persona" });
6905
+ continue;
6906
+ }
6907
+ if (PERSONA_INTERPOLATION_HAZARD.test(persona.text)) {
6908
+ log6("warn", `seed persona for mstar role '${roleId}' contains a "{{" paired with a later "}}" (dsh system-prompt strict interpolation renders persona text and throws on unknown or malformed references) — skipped`);
6909
+ skipped.push({ id: roleId, reason: "interpolation" });
6910
+ continue;
6911
+ }
6912
+ declared.push({ id: roleId, persona: `${persona.text}
6913
+
6914
+ ${mstarLoadLine(roleId)}` });
6915
+ }
6916
+ const preserved = [];
6917
+ for (const entry of preservedCandidates) {
6918
+ if (PERSONA_INTERPOLATION_HAZARD.test(entry.persona)) {
6919
+ log6("warn", `preserved seed persona for '${entry.id}' contains a "{{" paired with a later "}}" (dsh system-prompt strict interpolation renders persona text and throws on unknown or malformed references) — skipped`);
6920
+ skipped.push({ id: entry.id, reason: "interpolation" });
6921
+ continue;
6922
+ }
6923
+ declared.push({ id: entry.id, persona: entry.persona });
6924
+ preserved.push(entry);
6925
+ }
6926
+ if (declared.length === 0) {
6927
+ log6("debug", "no mstar or preserved seeds to declare — empty batch skipped (registry untouched)");
6928
+ return { declared, skipped, preserved, outcome: EMPTY_OUTCOME };
6929
+ }
6930
+ const outcome = await service.declareSeeds(declared);
6931
+ log6("debug", `mstar seeds declared: ${declared.length} ids (${mstarIds.length} mstar + ${preserved.length} preserved); ${skipped.length} skipped locally`);
6932
+ return { declared, skipped, preserved, outcome };
6933
+ }
6934
+
6935
+ // src/gates/fallbacks-advisory.ts
6936
+ var ADVISORY_LOGGER = "mstar/fallbacks-advisory";
6937
+ var advisoryLogSink = () => {};
6938
+ function setAdvisoryLogger(sink) {
6939
+ const prior = advisoryLogSink;
6940
+ advisoryLogSink = sink;
6941
+ return prior;
6942
+ }
6943
+ var ADVISORY_ID_LIST_CAP = 20;
6944
+ function capIdList(items) {
6945
+ if (items.length <= ADVISORY_ID_LIST_CAP)
6946
+ return items.join(", ");
6947
+ return `${items.slice(0, ADVISORY_ID_LIST_CAP).join(", ")}… and ${items.length - ADVISORY_ID_LIST_CAP} more`;
6948
+ }
6949
+ async function runFallbacksAdvisory(ctx, agentsDir) {
6950
+ try {
6951
+ if (!fallbacksMounted(ctx))
6952
+ return false;
6953
+ const config = readRowConfig(ctx);
6954
+ if (config === undefined) {
6955
+ log6("debug", "fallbacks row config unreadable (absent or non-object) — adoption advisory skipped");
6956
+ return true;
6957
+ }
6958
+ const service = fallbacksService(ctx);
6959
+ if (service !== undefined) {
6960
+ return await runSeedsAdvisory(service, config, agentsDir);
6961
+ }
6962
+ return runStructuralAdvisory(config, agentsDir);
6963
+ } catch (error) {
6964
+ log6("warn", `fallbacks adoption advisory aborted (degraded — warn-only, deployment config untouched): ${errorMessage7(error)}`);
6965
+ return true;
6966
+ }
6967
+ }
6968
+ async function runSeedsAdvisory(service, config, agentsDir) {
6969
+ const legacy = service.detectLegacyKeys(config);
6970
+ if (legacy.length > 0) {
6971
+ log6("warn", `fallbacks config carries legacy keys (detectLegacyKeys): ${capIdList(legacy)} — migrate to the current shape (role entities use 'persona'; 'chains', 'roles.default', 'label' and 'description' are removed)`);
6972
+ }
6973
+ if (agentsDir === undefined) {
6974
+ log6("debug", "harness-agents mirror absent — fallbacks adoption taxonomy check skipped (config-only advisory)");
6975
+ return true;
6976
+ }
6977
+ const mstarIds = subagentRoleIds(agentsDir);
6978
+ if (mstarIds.length === 0) {
6979
+ log6("debug", "harness-agents mirror has no subagent-mode shells — fallbacks adoption taxonomy check skipped");
6980
+ return true;
6981
+ }
6982
+ const seedsLog = (_level, message) => log6("debug", message);
6983
+ const view = await declareMstarSeeds(service, { agentsDir, log: seedsLog });
6984
+ let readback;
6985
+ try {
6986
+ readback = service.getEffectiveRoles();
6987
+ } catch (error) {
6988
+ log6("warn", `fallbacks effective-state readback failed — adoption state unavailable: ${errorMessage7(error)}`);
6989
+ return true;
6990
+ }
6991
+ reportEffectiveState(mstarIds, readback);
6992
+ reportDeclareOutcome(view);
6993
+ return true;
6994
+ }
6995
+ function runStructuralAdvisory(config, agentsDir) {
6996
+ if (agentsDir === undefined) {
6997
+ log6("debug", "harness-agents mirror absent — fallbacks adoption taxonomy check skipped (config-only advisory)");
6998
+ return true;
6999
+ }
7000
+ const mstarIds = subagentRoleIds(agentsDir);
7001
+ if (mstarIds.length === 0) {
7002
+ log6("debug", "harness-agents mirror has no subagent-mode shells — fallbacks adoption taxonomy check skipped");
7003
+ return true;
7004
+ }
7005
+ const list = readRolesList(config);
7006
+ if (list === undefined) {
7007
+ log6("debug", "fallbacks roles block not structurally readable (roles.list absent or non-array) — adoption advisory skipped");
7008
+ return true;
7009
+ }
7010
+ const declared = new Map;
7011
+ for (let index = 0;index < list.length; index++) {
7012
+ const entity = list[index];
7013
+ if (entity === null || typeof entity !== "object") {
7014
+ log6("debug", `fallbacks roles.list entry ${index} is not an object (${entity === null ? "null" : typeof entity}) — skipped`);
7015
+ continue;
7016
+ }
7017
+ const view = entity;
7018
+ if (typeof view.id !== "string" || view.id === "")
7019
+ continue;
7020
+ declared.set(view.id, view);
7021
+ }
7022
+ const missing = mstarIds.filter((id) => !declared.has(id));
7023
+ if (missing.length > 0) {
7024
+ log6("warn", `fallbacks roles.list is missing mstar roles: ${capIdList(missing)} — declare them under the mstar role id (or map via a taxonomy that uses those ids) so role-matched fallback chains resolve`);
7025
+ }
7026
+ const emptyPersona = [...declared.values()].filter((entity) => {
7027
+ const persona = entity.persona;
7028
+ return typeof persona !== "string" || persona.trim() === "";
7029
+ }).map((entity) => entity.id);
7030
+ if (emptyPersona.length > 0) {
7031
+ log6("warn", `fallbacks roles.list declares roles with an empty persona: ${capIdList(emptyPersona)} — declare a persona (or remove the role)`);
7032
+ }
7033
+ return true;
7034
+ }
7035
+ function reportEffectiveState(mstarIds, readback) {
7036
+ const byId = new Map;
7037
+ for (const row of readback.roles) {
7038
+ const key = row.id.trim();
7039
+ if (byId.has(key)) {
7040
+ log6("debug", `effective readback carries a duplicate id '${key}' after trimming — first row wins for the three-state report`);
7041
+ continue;
7042
+ }
7043
+ byId.set(key, row);
7044
+ }
7045
+ const missing = [];
7046
+ const overridden = [];
7047
+ const seeded = [];
7048
+ for (const id of mstarIds) {
7049
+ const row = byId.get(id);
7050
+ if (row === undefined) {
7051
+ missing.push(id);
7052
+ continue;
7053
+ }
7054
+ if (row.personaOverridden) {
7055
+ overridden.push(id);
7056
+ continue;
7057
+ }
7058
+ if (row.seeded)
7059
+ seeded.push(id);
7060
+ }
7061
+ if (missing.length > 0) {
7062
+ log6("warn", `fallbacks taxonomy is missing mstar roles: ${capIdList(missing)} — declare them under the mstar role id (or map via a taxonomy that uses those ids) so role-matched fallback chains resolve`);
7063
+ }
7064
+ if (seeded.length > 0) {
7065
+ log6("debug", `fallbacks taxonomy: mstar roles seeded at their defaults — ${capIdList(seeded)}`);
7066
+ }
7067
+ if (overridden.length > 0) {
7068
+ log6("warn", `fallbacks taxonomy overrides the seed persona for mstar roles: ${capIdList(overridden)} — the operator override is retained; revert to the seed default via the fallbacks settings card rollback button (fallbacks/revert-seed gateway)`);
7069
+ }
7070
+ const emptyPersona = readback.roles.filter((row) => {
7071
+ const persona = row.persona;
7072
+ if (typeof persona === "string" && persona.trim() !== "")
7073
+ return false;
7074
+ return !row.seeded || row.personaOverridden;
7075
+ }).map((row) => row.id);
7076
+ if (emptyPersona.length > 0) {
7077
+ log6("warn", `fallbacks taxonomy declares roles with an empty persona: ${capIdList(emptyPersona)} — declare a persona (or remove the role)`);
7078
+ }
7079
+ }
7080
+ function reportDeclareOutcome(view) {
7081
+ const { skipped, outcome } = view;
7082
+ const conflicts = outcome.conflicts;
7083
+ if (skipped.length === 0 && outcome.skipped.length === 0 && conflicts.length === 0)
7084
+ return;
7085
+ const parts = [];
7086
+ if (skipped.length > 0) {
7087
+ parts.push(`skipped locally: ${capIdList(skipped.map((s) => `${s.id} (${s.reason})`))}`);
7088
+ }
7089
+ if (outcome.skipped.length > 0) {
7090
+ parts.push(`skipped upstream: ${capIdList(outcome.skipped.map((s) => `${s.id} (${s.reason})`))}`);
7091
+ }
7092
+ if (conflicts.length > 0) {
7093
+ parts.push(`conflicts: ${capIdList(conflicts.map((c) => `${c.id} (${c.kind} — operator override retained)`))}`);
7094
+ }
7095
+ const revertNote = conflicts.length > 0 ? " — revert an override via the fallbacks settings card rollback button (fallbacks/revert-seed gateway)" : "";
7096
+ log6("warn", `fallbacks seed declaration: ${parts.join("; ")}${revertNote}`);
7097
+ }
7098
+ function errorMessage7(error) {
7099
+ return error instanceof Error ? error.message : String(error);
7100
+ }
7101
+ function readRowConfig(ctx) {
7102
+ const entry = fallbacksEntry(ctx);
7103
+ if (entry === undefined)
7104
+ return;
7105
+ const config = entry.options.config;
7106
+ if (config === undefined || config === null || typeof config !== "object" || Array.isArray(config))
7107
+ return;
7108
+ return config;
7109
+ }
7110
+ function readRolesList(config) {
7111
+ const roles = config.roles;
7112
+ if (roles === undefined || roles === null || typeof roles !== "object" || Array.isArray(roles))
7113
+ return;
7114
+ const list = roles.list;
7115
+ if (!Array.isArray(list))
7116
+ return;
7117
+ return list;
7118
+ }
7119
+ function log6(level, message) {
7120
+ try {
7121
+ advisoryLogSink(level, message);
7122
+ } catch {}
7123
+ }
7124
+
7125
+ // src/gates/system-prompt.ts
7126
+ var HARNESS_PROMPT_LOGGER = "mstar/harness-prompt";
7127
+ var HARNESS_RULES_SECTION_NAME = "mstar:harness-rules";
7128
+ var HARNESS_RULES_SECTION_ORDER = 2;
7129
+ var ENGINE_STATUS_CONTEXT_NAME = "mstar:engine-status";
7130
+ var ENGINE_STATUS_CONTEXT_ORDER = 100;
7131
+ var harnessPromptLogSink = () => {};
7132
+ function setHarnessPromptLogger(sink) {
7133
+ const prior = harnessPromptLogSink;
7134
+ harnessPromptLogSink = sink;
7135
+ return prior;
7136
+ }
7137
+ function resolveAssemblyHarnessDir(context, resolver, fallback) {
7138
+ const agent = context?.agent;
7139
+ if (agent === undefined || agent === null)
7140
+ return fallback;
7141
+ try {
7142
+ return resolver.forAgent(agent);
7143
+ } catch (error) {
7144
+ log7("debug", `mstar:harness-rules per-assembly harness-dir resolution failed (falling back to the boot value): ${errorMessage8(error)}`);
7145
+ return fallback;
7146
+ }
7147
+ }
7148
+ function registerHarnessPrompt(ctx, options) {
7149
+ const systemPrompt = ctx.get("systemPrompt");
7150
+ if (systemPrompt === undefined || typeof systemPrompt.section !== "function" || typeof systemPrompt.context !== "function") {
7151
+ log7("debug", "systemPrompt service absent — mstar:harness-rules injection skipped (composition without dsh-system-prompt)");
7152
+ return false;
7153
+ }
7154
+ let bootHarnessDir;
7155
+ try {
7156
+ bootHarnessDir = options.resolver.forWorkspace(undefined);
7157
+ } catch (error) {
7158
+ log7("warn", `mstar:harness-rules injection aborted (degraded — session boot unaffected): ${errorMessage8(error)}`);
7159
+ return false;
7160
+ }
7161
+ ctx.inject(["systemPrompt"], (systemPromptCtx) => {
7162
+ try {
7163
+ const view = systemPromptCtx.systemPrompt;
7164
+ const disposeSection = view.section({
7165
+ name: HARNESS_RULES_SECTION_NAME,
7166
+ order: HARNESS_RULES_SECTION_ORDER,
7167
+ text: (context) => {
7168
+ const harnessDir = resolveAssemblyHarnessDir(context, options.resolver, bootHarnessDir);
7169
+ return harnessRulesText(harnessDir, sectionEnforcement(harnessDir));
7170
+ }
7171
+ });
7172
+ const disposeContext = view.context({
7173
+ name: ENGINE_STATUS_CONTEXT_NAME,
7174
+ order: ENGINE_STATUS_CONTEXT_ORDER,
7175
+ text: engineStatusProvider(ctx, options.resolver, bootHarnessDir)
7176
+ });
7177
+ systemPromptCtx.effect(function* () {
7178
+ yield () => {
7179
+ try {
7180
+ disposeSection();
7181
+ } catch {}
7182
+ try {
7183
+ disposeContext();
7184
+ } catch {}
7185
+ };
7186
+ }, "mstar:harness-rules disposer collection");
7187
+ } catch (error) {
7188
+ log7("warn", `mstar:harness-rules registration failed (contained — session boot unaffected): ${errorMessage8(error)}`);
7189
+ }
7190
+ });
7191
+ return true;
7192
+ }
7193
+ function sectionEnforcement(harnessDir) {
7194
+ return harnessDir === null ? { hard: false, source: "none" } : resolveCompassEnforcement(harnessDir);
7195
+ }
7196
+ function harnessRulesText(harnessDir, enforcement) {
7197
+ return [
7198
+ "This session runs under the Morning Star (mstar) harness: prompts and tool calls are governed by the harness gates and rules.",
7199
+ `enforcement: ${enforcement.hard ? "hard" : "soft"}`,
7200
+ `harness dir: ${stripInterpolationHazard(harnessDir ?? "none")}`,
7201
+ "Before non-trivial work, read the skill `skill://mstar-harness-core` — the authoritative harness entry point (mstar-harness-core first, then topic skills)."
7202
+ ].join(`
7203
+ `);
7204
+ }
7205
+ function engineStatusProvider(ctx, resolver, bootHarnessDir) {
7206
+ const memo = new Map;
7207
+ return (context) => {
7208
+ const harnessDir = resolveAssemblyHarnessDir(context, resolver, bootHarnessDir);
7209
+ const now = Date.now();
7210
+ let entry = memo.get(harnessDir);
7211
+ if (entry === undefined || now - entry.builtAt >= DEFAULT_CATALOG_TTL_MS) {
7212
+ entry = { source: buildCatalogSources(ctx, harnessDir), builtAt: now };
7213
+ memo.set(harnessDir, entry);
7214
+ }
7215
+ return engineStatusSummary(entry.source);
7216
+ };
7217
+ }
7218
+ function engineStatusSummary(source) {
7219
+ const lines = [
7220
+ `mstar engine status: v${source.version} | harness ${stripInterpolationHazard(source.harnessDir ?? "none")} | enforcement ${source.enforcement.hard ? "hard" : "soft"}`
7221
+ ];
7222
+ const iteration = source.iteration;
7223
+ if (iteration !== undefined) {
7224
+ const gate2 = iteration.gate;
7225
+ const codes = gate2.violations.map((v) => v.code).join(", ");
7226
+ lines.push(`iteration ${stripInterpolationHazard(iteration.iterationId)}: gate ${gate2.ok ? "PASS" : `FAIL (${codes})`} | transition ${gate2.transition} | all plans done ${gate2.all_plans_done}`);
7227
+ }
7228
+ const state = source.state;
7229
+ if (state !== null) {
7230
+ const plans = state.plans.length === 0 ? "none" : state.plans.map((p) => `${stripInterpolationHazard(p.id)}(${stripInterpolationHazard(p.status)})`).join(" ");
7231
+ const residuals = state.residuals.length === 0 ? "none" : state.residuals.map((r) => `${r.severity} ${r.count}`).join(", ");
7232
+ const leases = state.leases.length === 0 ? "none active" : state.leases.map((l) => `${stripInterpolationHazard(l.planId)} → ${stripInterpolationHazard(l.holder)}`).join("; ");
7233
+ lines.push(`plans: ${plans} | residuals: ${residuals} | leases: ${leases}`);
7234
+ if (state.direction !== null)
7235
+ lines.push(`direction: ${stripInterpolationHazard(state.direction)}`);
7236
+ }
7237
+ return lines.join(`
7238
+ `);
7239
+ }
7240
+ function errorMessage8(error) {
7241
+ return error instanceof Error ? error.message : String(error);
7242
+ }
7243
+ function log7(level, message) {
7244
+ try {
7245
+ harnessPromptLogSink(level, message);
7246
+ } catch {}
7247
+ }
7248
+
7249
+ // src/index.ts
7250
+ var name = "dsh";
7251
+ var inject = ["loader"];
7252
+ function packagedCommandsDir() {
7253
+ try {
7254
+ const dir = fileURLToPath3(new URL("../harness-commands", import.meta.url));
7255
+ return existsSync18(dir) ? dir : undefined;
7256
+ } catch {
7257
+ return;
7258
+ }
7259
+ }
7260
+ function commandFrontmatterField(frontmatter, label) {
7261
+ const match = new RegExp(`^${label}[ \\t]*:[ \\t]*(.+)$`, "m").exec(frontmatter);
7262
+ return match?.[1]?.trim();
7263
+ }
7264
+ function unquote2(value) {
7265
+ return value.length >= 2 && value.startsWith('"') && value.endsWith('"') ? value.slice(1, -1) : value;
7266
+ }
7267
+ function parseCommandMarkdown(content) {
7268
+ const lines = content.replace(/^\uFEFF/, "").split(/\r?\n/);
7269
+ if (lines.length === 0 || lines[0].trim() !== "---")
7270
+ return;
7271
+ let end = -1;
7272
+ for (let i = 1;i < lines.length; i++) {
7273
+ if (lines[i].trim() === "---") {
7274
+ end = i;
7275
+ break;
7276
+ }
7277
+ }
7278
+ if (end === -1)
7279
+ return;
7280
+ const frontmatter = lines.slice(1, end).join(`
7281
+ `);
7282
+ const name2 = commandFrontmatterField(frontmatter, "name");
7283
+ const description = commandFrontmatterField(frontmatter, "description");
7284
+ if (name2 === undefined || description === undefined)
7285
+ return;
7286
+ const input = commandFrontmatterField(frontmatter, "input");
7287
+ return {
7288
+ name: name2,
7289
+ description: unquote2(description),
7290
+ ...input !== undefined && input !== "" ? { input: unquote2(input) } : {},
7291
+ body: lines.slice(end + 1).join(`
7292
+ `).trim()
7293
+ };
7294
+ }
7295
+ function registerMstarCommands(ctx) {
7296
+ const dir = packagedCommandsDir();
7297
+ if (dir === undefined)
7298
+ return;
7299
+ ctx.inject(["commands"], (commandsCtx) => {
7300
+ for (const file of readdirSync9(dir).sort()) {
7301
+ if (!file.endsWith(".md"))
7302
+ continue;
7303
+ const parsed = parseCommandMarkdown(readFileSync15(join20(dir, file), "utf8"));
7304
+ if (parsed === undefined)
7305
+ continue;
7306
+ commandsCtx.commands.register({
5639
7307
  name: parsed.name,
5640
7308
  description: parsed.description,
5641
7309
  ...parsed.input !== undefined ? { input: { hint: parsed.input } } : {},
@@ -5675,7 +7343,98 @@ function apply(ctx, config) {
5675
7343
  else
5676
7344
  logger.info(message);
5677
7345
  });
7346
+ setDecorationLogger((level, message) => {
7347
+ const logger = ctx.logger(DECORATION_LOGGER);
7348
+ if (level === "debug")
7349
+ logger.debug(message);
7350
+ else if (level === "info")
7351
+ logger.info(message);
7352
+ else
7353
+ logger.warn(message);
7354
+ });
7355
+ setDecorationAgentsDir(packagedAgentsDir());
7356
+ setHarnessPromptLogger((level, message) => {
7357
+ const logger = ctx.logger(HARNESS_PROMPT_LOGGER);
7358
+ if (level === "debug")
7359
+ logger.debug(message);
7360
+ else
7361
+ logger.warn(message);
7362
+ });
7363
+ registerHarnessPrompt(ctx, { resolver });
7364
+ setAdvisoryLogger((level, message) => {
7365
+ const logger = ctx.logger(ADVISORY_LOGGER);
7366
+ if (level === "debug")
7367
+ logger.debug(message);
7368
+ else
7369
+ logger.warn(message);
7370
+ });
7371
+ let advisoryPassed = false;
7372
+ let advisoryPassInFlight = false;
7373
+ let advisoryGeneration = 0;
7374
+ const runAdvisoryPass = () => {
7375
+ if (advisoryPassed || advisoryPassInFlight)
7376
+ return;
7377
+ advisoryPassInFlight = true;
7378
+ const generation = advisoryGeneration;
7379
+ runFallbacksAdvisory(ctx, packagedAgentsDir()).then((ran) => {
7380
+ if (ran && generation === advisoryGeneration)
7381
+ advisoryPassed = true;
7382
+ }).finally(() => {
7383
+ advisoryPassInFlight = false;
7384
+ });
7385
+ };
7386
+ runAdvisoryPass();
7387
+ if (!fallbacksMounted(ctx)) {
7388
+ ctx.logger(SEEDS_LOGGER).debug("fallbacks not mounted at apply — mstar seeds inject child armed; declare fires when the llm-fallbacks service appears (apply/HMR)");
7389
+ }
7390
+ ctx.inject(["llm-fallbacks"], (serviceCtx) => {
7391
+ const service = fallbacksService(serviceCtx);
7392
+ if (service === undefined)
7393
+ return;
7394
+ declareMstarSeeds(service, {
7395
+ agentsDir: packagedAgentsDir(),
7396
+ log: (level, message) => {
7397
+ const logger = ctx.logger(SEEDS_LOGGER);
7398
+ if (level === "warn")
7399
+ logger.warn(message);
7400
+ else if (level === "error")
7401
+ logger.error(message);
7402
+ else
7403
+ logger.debug(message);
7404
+ }
7405
+ }).catch((error) => {
7406
+ ctx.logger(SEEDS_LOGGER).error(`mstar seeds declaration failed (contained — fallbacks taxonomy unchanged): ${error instanceof Error ? error.message : String(error)}`);
7407
+ });
7408
+ return () => {
7409
+ advisoryPassed = false;
7410
+ advisoryGeneration += 1;
7411
+ };
7412
+ });
5678
7413
  registerSettleListener(ctx, config, pairing);
7414
+ setWorkflowLedgerLogger((level, message) => {
7415
+ const logger = ctx.logger(WORKFLOW_LEDGER_LOGGER);
7416
+ if (level === "warn")
7417
+ logger.warn(message);
7418
+ else
7419
+ logger.debug(message);
7420
+ });
7421
+ registerWorkflowLedger(ctx, resolver, adapter.workflowAskCache);
7422
+ setGoalBridgeLogger((level, message) => {
7423
+ const logger = ctx.logger(GOAL_BRIDGE_LOGGER);
7424
+ if (level === "debug")
7425
+ logger.debug(message);
7426
+ else
7427
+ logger.warn(message);
7428
+ });
7429
+ registerGoalBridge(ctx, resolver, config);
7430
+ setPlanModeBridgeLogger((level, message) => {
7431
+ const logger = ctx.logger(PLAN_MODE_BRIDGE_LOGGER);
7432
+ if (level === "debug")
7433
+ logger.debug(message);
7434
+ else
7435
+ logger.warn(message);
7436
+ });
7437
+ registerPlanModeBridge(ctx, resolver);
5679
7438
  ctx.inject(["jobs"], (jobsCtx) => {
5680
7439
  const jobs = jobsCtx.jobs;
5681
7440
  try {
@@ -5696,7 +7455,7 @@ function apply(ctx, config) {
5696
7455
  ctx.logger(DISPATCH_LOGGER).warn("Enforcement: hard is active but dispatchBinding is unset — the anti-recursion precheck is skipped (an Assignment whose Execute as equals the dispatching agent cannot be detected)");
5697
7456
  }
5698
7457
  if (effectiveHard && config.dispatchTools === undefined) {
5699
- ctx.logger(DISPATCH_LOGGER).warn('Enforcement: hard is active but dispatchTools is unset — the dispatch gate matches the default tool name "subagent"; a deployment renaming the dsh subagent tool (toolName) without declaring dispatchTools silently disables the gate');
7458
+ ctx.logger(DISPATCH_LOGGER).warn('Enforcement: hard is active but dispatchTools is unset — the dispatch gate matches the default tool names "subagent" and "subagent_fork"; a deployment renaming either dsh subagent tool (toolName) without declaring dispatchTools silently disables the gate');
5700
7459
  }
5701
7460
  ctx.on("fs/write-intent", (target, actor, next) => writeIntentListener(ctx, resolver, config, adapter, target, actor, next), { prepend: true });
5702
7461
  ctx.on("fs/edit-intent", (target, actor, next) => editIntentListener(ctx, resolver, config, adapter, target, actor, next), { prepend: true });
@@ -5706,6 +7465,10 @@ function apply(ctx, config) {
5706
7465
  ctx.on("fs/write-intent", (target, actor, next) => seamWriteIntentListener(ctx, resolver, config, seam, target, actor, next), { prepend: true });
5707
7466
  }
5708
7467
  ctx.on("tools/pre-execute", (exec, next) => preExecuteListener(ctx, resolver, config, adapter, exec, next), { prepend: true });
7468
+ ctx.events.on("subagent/start", (info) => {
7469
+ runAdvisoryPass();
7470
+ decorateSubagentStart(ctx, config, info);
7471
+ });
5709
7472
  const ttlMs = config.catalogTtlMs ?? DEFAULT_CATALOG_TTL_MS;
5710
7473
  const explicitKey = bootHarnessDir !== null ? EXPLICIT_CACHE_KEY : undefined;
5711
7474
  const catalogCache = new Map;
@@ -5723,6 +7486,11 @@ function apply(ctx, config) {
5723
7486
  }
5724
7487
  export {
5725
7488
  skillLocalConfig,
7489
+ setDecorationLogger,
7490
+ setDecorationAgentsDir,
7491
+ setAdvisoryLogger,
7492
+ runFallbacksAdvisory,
7493
+ recordWorkflowVerdict,
5726
7494
  recordSettle,
5727
7495
  recordDispatch,
5728
7496
  readAgentFlow,
@@ -5735,14 +7503,19 @@ export {
5735
7503
  lintCompoundWrite,
5736
7504
  lintAuditWrite,
5737
7505
  inject,
7506
+ decorateSubagentStart,
5738
7507
  apply,
5739
7508
  SkillLintVetoError,
5740
7509
  SeamVetoError,
5741
7510
  SETTLE_SEAM,
7511
+ PERSONA_SECTION_ORDER,
7512
+ PERSONA_SECTION_NAME,
5742
7513
  HarnessResolver,
5743
7514
  DshMstar,
5744
7515
  DshHostAdapter,
7516
+ DECORATION_LOGGER,
5745
7517
  Config,
5746
7518
  AGENT_FLOW_MAX_EVENTS,
5747
- AGENT_FLOW_FILE
7519
+ AGENT_FLOW_FILE,
7520
+ ADVISORY_LOGGER
5748
7521
  };