@mstar-harness/dsh 3.8.1 → 3.8.2

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 (36) hide show
  1. package/README.i18n.yaml +2 -2
  2. package/README.md +16 -6
  3. package/README.zh.md +16 -6
  4. package/dist/client/panel/engine-status-client.d.ts +84 -6
  5. package/dist/client/panel/graph/project-graph.d.ts +26 -13
  6. package/dist/client/panel/guards.d.ts +41 -1
  7. package/dist/client/panel/locale.d.ts +1 -1
  8. package/dist/client/panel/pages/AgentListPage.d.ts +1 -1
  9. package/dist/client/panel/sidebar.d.ts +3 -2
  10. package/dist/client/panel/state-section.d.ts +25 -3
  11. package/dist/client/panel/use-mstar-engine-status.d.ts +28 -4
  12. package/dist/client.js +346 -48
  13. package/dist/engine-status-endpoint.d.ts +85 -8
  14. package/dist/engine-status-store.d.ts +91 -1
  15. package/dist/engine-status-wire.d.ts +9 -0
  16. package/dist/gates/_shared.d.ts +61 -9
  17. package/dist/gates/adapter.d.ts +32 -2
  18. package/dist/gates/agent-flow.d.ts +312 -60
  19. package/dist/gates/catalog.d.ts +58 -37
  20. package/dist/gates/dispatch.d.ts +11 -2
  21. package/dist/gates/goal-bridge.d.ts +10 -130
  22. package/dist/gates/plan-mode-bridge.d.ts +20 -11
  23. package/dist/gates/role-persona.d.ts +16 -0
  24. package/dist/gates/steering.d.ts +41 -0
  25. package/dist/gates/workflow-ledger.d.ts +31 -4
  26. package/dist/gates/workflow-selection.d.ts +41 -20
  27. package/dist/index.js +1206 -392
  28. package/dist/types.d.ts +36 -11
  29. package/harness-commands/amazing-pr-review.md +2 -0
  30. package/harness-commands/codebase-audit.md +2 -0
  31. package/harness-skills/mstar-host/SKILL.md +3 -1
  32. package/harness-skills/mstar-host/references/dsh-workflow-scripts.md +424 -0
  33. package/harness-skills/mstar-host/references/dsh.md +170 -43
  34. package/harness-skills/mstar-roles/references/project-manager.md +2 -0
  35. package/harness-skills/mstar-sdd/SKILL.md +2 -0
  36. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -1122,7 +1122,7 @@ var require_lib2 = __commonJS(function(exports, module) {
1122
1122
  });
1123
1123
 
1124
1124
  // src/index.ts
1125
- import { existsSync as existsSync22, readFileSync as readFileSync19, readdirSync as readdirSync14 } from "node:fs";
1125
+ import { existsSync as existsSync22, readFileSync as readFileSync18, readdirSync as readdirSync14 } from "node:fs";
1126
1126
  import { join as join25 } from "node:path";
1127
1127
  import { fileURLToPath as fileURLToPath3 } from "node:url";
1128
1128
  import {
@@ -4263,8 +4263,7 @@ var Config = import_schemastery.default.object({
4263
4263
  return value;
4264
4264
  }).default(undefined),
4265
4265
  workflowGate: import_schemastery.default.union(["off", "warn", "ask", "hard"]).default("warn"),
4266
- workflowNames: import_schemastery.default.array(import_schemastery.default.string()).default(undefined),
4267
- maxGoalRounds: import_schemastery.default.number().default(undefined)
4266
+ workflowNames: import_schemastery.default.array(import_schemastery.default.string()).default(undefined)
4268
4267
  });
4269
4268
  function formatViolation(violation14) {
4270
4269
  return `[${violation14.severity}] ${violation14.code}: ${violation14.message}${violation14.fix !== undefined ? ` (fix: ${violation14.fix})` : ""}`;
@@ -4358,6 +4357,31 @@ function sessionHeaderIdOf(agent) {
4358
4357
  const id = session?.header?.id;
4359
4358
  return typeof id === "string" && id.trim() !== "" ? id : undefined;
4360
4359
  }
4360
+ function agentIdOf(agent) {
4361
+ const id = agent?.id;
4362
+ return typeof id === "string" && id.trim() !== "" ? id : undefined;
4363
+ }
4364
+ function verifiedLeaseHolderOf(agent, sessionId, cwd) {
4365
+ if (agent === undefined)
4366
+ return;
4367
+ if (sessionHeaderIdOf(agent) !== sessionId)
4368
+ return;
4369
+ if (cwd !== undefined && sessionCwdOf(agent) !== cwd)
4370
+ return;
4371
+ return agentIdOf(agent);
4372
+ }
4373
+ function sessionHintOf(agent) {
4374
+ const cwd = sessionCwdOf(agent);
4375
+ const sessionId = sessionHeaderIdOf(agent);
4376
+ const leaseHolder = agentIdOf(agent);
4377
+ if (cwd === undefined && sessionId === undefined && leaseHolder === undefined)
4378
+ return;
4379
+ return {
4380
+ ...cwd === undefined ? {} : { cwd },
4381
+ ...sessionId === undefined ? {} : { sessionId },
4382
+ ...leaseHolder === undefined ? {} : { leaseHolder }
4383
+ };
4384
+ }
4361
4385
  function actorAgentOf(actor) {
4362
4386
  return actor?.agent;
4363
4387
  }
@@ -4382,8 +4406,8 @@ function skillLocalConfig(config) {
4382
4406
  }
4383
4407
 
4384
4408
  // src/gates/catalog.ts
4385
- import { existsSync as existsSync14, readFileSync as readFileSync11, readdirSync as readdirSync4 } from "node:fs";
4386
- import { join as join12 } from "node:path";
4409
+ import { existsSync as existsSync14, readFileSync as readFileSync11, readdirSync as readdirSync4, realpathSync as realpathSync7 } from "node:fs";
4410
+ import { basename as basename5, isAbsolute as isAbsolute7, join as join12, relative as relative7, sep as sep3 } from "node:path";
4387
4411
  import { createUserMessage } from "@deepseek-ai/dsh-llm";
4388
4412
 
4389
4413
  // src/gates/agent-flow.ts
@@ -4395,8 +4419,8 @@ import { existsSync as existsSync8 } from "node:fs";
4395
4419
  import { basename as basename2, join as join5, resolve as resolve6 } from "node:path";
4396
4420
 
4397
4421
  // src/gates/workflow-selection.ts
4398
- import { existsSync as existsSync5, readdirSync as readdirSync2, statSync as statSync2 } from "node:fs";
4399
- import { dirname as dirname7, join as join3, relative as relative4 } from "node:path";
4422
+ import { existsSync as existsSync5, readdirSync as readdirSync2, realpathSync as realpathSync5, statSync as statSync2 } from "node:fs";
4423
+ import { dirname as dirname7, isAbsolute as isAbsolute3, join as join3, normalize, relative as relative4, sep as sep2 } from "node:path";
4400
4424
  var TERMINAL_STATUS_CACHE_MAX = 64;
4401
4425
  var terminalStatusCache = new Map;
4402
4426
  function terminalStatusOf(snapshotPath) {
@@ -4412,7 +4436,9 @@ function terminalStatusOf(snapshotPath) {
4412
4436
  return cached;
4413
4437
  let terminal = false;
4414
4438
  try {
4415
- const snapshot = readJson(snapshotPath);
4439
+ const snapshot = asRecord(readJson(snapshotPath));
4440
+ if (snapshot === undefined)
4441
+ return;
4416
4442
  const status = snapshot.status;
4417
4443
  terminal = typeof status === "string" && WORKFLOW_TERMINAL_STATUSES.includes(status);
4418
4444
  } catch {
@@ -4428,7 +4454,101 @@ function terminalStatusOf(snapshotPath) {
4428
4454
  }
4429
4455
  return verdict;
4430
4456
  }
4431
- function resolveActiveWorkflow(harnessDir) {
4457
+ function stripTrailingSeparators(path) {
4458
+ let out = path;
4459
+ while (out.length > 1 && out.endsWith(sep2))
4460
+ out = out.slice(0, -1);
4461
+ return out;
4462
+ }
4463
+ function lexical(path) {
4464
+ return stripTrailingSeparators(normalize(path));
4465
+ }
4466
+ function canonicalPath(path) {
4467
+ const fallback = lexical(path);
4468
+ try {
4469
+ return { kind: "ok", path: lexical(realpathSync5(path)) };
4470
+ } catch (error) {
4471
+ return error.code === "ENOENT" ? { kind: "missing", path: fallback } : { kind: "error" };
4472
+ }
4473
+ }
4474
+ function within(root, cwd) {
4475
+ if (root === "" || cwd === "" || !isAbsolute3(root) || !isAbsolute3(cwd))
4476
+ return false;
4477
+ const rootCanonical = canonicalPath(root);
4478
+ if (rootCanonical.kind === "error")
4479
+ return false;
4480
+ const cwdCanonical = canonicalPath(cwd);
4481
+ if (cwdCanonical.kind === "error")
4482
+ return false;
4483
+ const bothExist = rootCanonical.kind === "ok" && cwdCanonical.kind === "ok";
4484
+ const rootPath = bothExist ? rootCanonical.path : lexical(root);
4485
+ const cwdPath = bothExist ? cwdCanonical.path : lexical(cwd);
4486
+ if (rootPath === cwdPath)
4487
+ return true;
4488
+ const rel = relative4(rootPath, cwdPath);
4489
+ return rel !== "" && !isAbsolute3(rel) && rel !== ".." && !rel.startsWith(`..${sep2}`);
4490
+ }
4491
+ function validLeases(snapshot) {
4492
+ if (!Array.isArray(snapshot.plans))
4493
+ return [];
4494
+ const leases = [];
4495
+ for (const row of snapshot.plans) {
4496
+ const lease = asRecord(asRecord(row)?.execution_lease);
4497
+ if (lease === undefined || !validateExecutionLease(lease).ok)
4498
+ continue;
4499
+ leases.push(lease);
4500
+ }
4501
+ return leases;
4502
+ }
4503
+ function readSnapshot(harnessDir, entry) {
4504
+ const snapshotPath = join3(harnessDir, entry.dir, WORKFLOW_SNAPSHOT_FILE);
4505
+ if (!existsSync5(snapshotPath))
4506
+ return;
4507
+ try {
4508
+ return asRecord(readJson(snapshotPath));
4509
+ } catch {
4510
+ return;
4511
+ }
4512
+ }
4513
+ function soleEntry(matches) {
4514
+ return matches.size === 1 ? matches.values().next().value : undefined;
4515
+ }
4516
+ function automaticBinding(harnessDir, entries, hint) {
4517
+ const cwd = hint.cwd === "" ? undefined : hint.cwd;
4518
+ const holder = hint.leaseHolder === "" ? undefined : hint.leaseHolder;
4519
+ if (cwd === undefined && holder === undefined)
4520
+ return;
4521
+ const snapshots = new Map;
4522
+ const snapshotOf = (entry) => {
4523
+ const cached = snapshots.get(entry.dir);
4524
+ if (cached !== undefined || snapshots.has(entry.dir))
4525
+ return cached;
4526
+ const snapshot = readSnapshot(harnessDir, entry);
4527
+ snapshots.set(entry.dir, snapshot);
4528
+ return snapshot;
4529
+ };
4530
+ const leaseMatches = new Map;
4531
+ const cwdMatches = new Map;
4532
+ for (const entry of entries) {
4533
+ const snapshot = snapshotOf(entry);
4534
+ if (snapshot === undefined)
4535
+ continue;
4536
+ const leases = validLeases(snapshot);
4537
+ if (holder !== undefined && leases.some((lease) => lease.holder === holder) || cwd !== undefined && leases.some((lease) => within(String(lease.worktree_path), cwd))) {
4538
+ leaseMatches.set(entry.id, entry);
4539
+ }
4540
+ const controlWorktree = snapshot.control_worktree_path;
4541
+ if (cwd !== undefined && typeof controlWorktree === "string" && within(controlWorktree, cwd)) {
4542
+ cwdMatches.set(entry.id, entry);
4543
+ }
4544
+ }
4545
+ const byLease = soleEntry(leaseMatches);
4546
+ if (byLease !== undefined)
4547
+ return { kind: "active", workflowId: byLease.id, dir: byLease.dir };
4548
+ const byCwd = soleEntry(cwdMatches);
4549
+ return byCwd === undefined ? undefined : { kind: "active", workflowId: byCwd.id, dir: byCwd.dir };
4550
+ }
4551
+ function resolveActiveWorkflow(harnessDir, hint) {
4432
4552
  const statusPath = join3(harnessDir, STATUS_FILE);
4433
4553
  if (!existsSync5(statusPath)) {
4434
4554
  return {
@@ -4439,7 +4559,11 @@ function resolveActiveWorkflow(harnessDir) {
4439
4559
  }
4440
4560
  let doc;
4441
4561
  try {
4442
- doc = readJson(statusPath);
4562
+ const parsed = asRecord(readJson(statusPath));
4563
+ if (parsed === undefined) {
4564
+ return { kind: "error", code: "status.unreadable", message: `cannot read ${statusPath}` };
4565
+ }
4566
+ doc = parsed;
4443
4567
  } catch {
4444
4568
  return { kind: "error", code: "status.unreadable", message: `cannot read ${statusPath}` };
4445
4569
  }
@@ -4450,39 +4574,67 @@ function resolveActiveWorkflow(harnessDir) {
4450
4574
  message: `status.json schema version 2 required — got ${JSON.stringify(doc.version)} (v1 or unknown version); run \`mstar migrate\` to convert the tree`
4451
4575
  };
4452
4576
  }
4453
- if (!Array.isArray(doc.workflows) || doc.workflows.length === 0) {
4577
+ const registry = doc.workflows;
4578
+ if (!Array.isArray(registry)) {
4454
4579
  return {
4455
4580
  kind: "error",
4456
- code: "workflow.selection.no-active",
4457
- message: "no active lifecycle in status.json workflows[] — the agent-flow writer appends only to an active lifecycle"
4581
+ code: "workflow.selection.invalid-registry",
4582
+ message: `status.json workflows[] is not an array: ${JSON.stringify(registry)} — the registry cannot be validated`
4458
4583
  };
4459
4584
  }
4460
- const first = doc.workflows[0];
4461
- const entry = asRecord(first);
4462
- const workflowId = typeof entry?.id === "string" ? entry.id : undefined;
4463
- const dir = typeof entry?.dir === "string" ? entry.dir : undefined;
4464
- if (workflowId === undefined || dir === undefined) {
4585
+ if (registry.length === 0) {
4465
4586
  return {
4466
4587
  kind: "error",
4467
- code: "workflow.selection.invalid-entry",
4468
- message: `workflows[] first entry is missing id/dir: ${JSON.stringify(first)}`
4588
+ code: "workflow.selection.no-active",
4589
+ message: "no active lifecycle in status.json workflows[] — the agent-flow writer appends only to an active lifecycle"
4469
4590
  };
4470
4591
  }
4471
- if (doc.workflows.length > 1) {
4472
- return {
4473
- kind: "active",
4474
- workflowId,
4475
- dir,
4476
- warning: {
4477
- code: "workflow.selection.multi-active",
4478
- message: `${doc.workflows.length} active lifecycles in status.json workflows[] — selected the first (${workflowId}); no silent pick`
4479
- }
4480
- };
4592
+ const entries = [];
4593
+ const seen = new Set;
4594
+ for (const raw of registry) {
4595
+ const gate2 = validateWorkflowEntry(raw);
4596
+ if (!gate2.ok) {
4597
+ return {
4598
+ kind: "error",
4599
+ code: "workflow.selection.invalid-entry",
4600
+ message: `workflows[] entry fails the v2 workflow-entry contract: ${gate2.violations.map((violation14) => violation14.message).join("; ")}`
4601
+ };
4602
+ }
4603
+ const entry = raw;
4604
+ if (seen.has(entry.id)) {
4605
+ return {
4606
+ kind: "error",
4607
+ code: "workflow.selection.invalid-registry",
4608
+ message: `duplicate workflow id in status.json workflows[]: ${entry.id} — the registry must be keyed by unique active lifecycles`
4609
+ };
4610
+ }
4611
+ seen.add(entry.id);
4612
+ entries.push({ id: entry.id, dir: entry.dir });
4613
+ }
4614
+ if (hint !== undefined) {
4615
+ const automatic = automaticBinding(harnessDir, entries, hint);
4616
+ if (automatic !== undefined)
4617
+ return automatic;
4618
+ const selected = hint.selectedWorkflowId;
4619
+ if (selected !== undefined && selected !== "") {
4620
+ const picked = entries.find((entry) => entry.id === selected);
4621
+ if (picked !== undefined)
4622
+ return { kind: "active", workflowId: picked.id, dir: picked.dir };
4623
+ }
4624
+ }
4625
+ if (entries.length === 1) {
4626
+ const only = entries[0];
4627
+ return { kind: "active", workflowId: only.id, dir: only.dir };
4481
4628
  }
4482
- return { kind: "active", workflowId, dir };
4629
+ return {
4630
+ kind: "error",
4631
+ code: "workflow.selection.unbound-multi-active",
4632
+ message: `${entries.length} active lifecycles in status.json workflows[] — this session has no lease, is not inside a workflow worktree and has no stored selection; pick one for this session (writes pause until then)`,
4633
+ activeWorkflowIds: entries.map((entry) => entry.id)
4634
+ };
4483
4635
  }
4484
- function resolveReadWorkflow(harnessDir) {
4485
- const active = resolveActiveWorkflow(harnessDir);
4636
+ function resolveReadWorkflow(harnessDir, hint) {
4637
+ const active = resolveActiveWorkflow(harnessDir, hint);
4486
4638
  if (active.kind === "active")
4487
4639
  return active;
4488
4640
  if (active.kind === "error" && active.code !== "workflow.selection.no-active")
@@ -4707,7 +4859,7 @@ function sessionIdOf(exec) {
4707
4859
  const id = agent?.id;
4708
4860
  return typeof id === "string" && id.trim() !== "" ? id : undefined;
4709
4861
  }
4710
- function leaseGateViolations(harnessDir, exec, writable, prompt) {
4862
+ function leaseGateViolations(harnessDir, exec, writable, prompt, hint) {
4711
4863
  if (harnessDir === null || writable === false)
4712
4864
  return [];
4713
4865
  const header = assignmentHeaderRegion(prompt);
@@ -4716,7 +4868,7 @@ function leaseGateViolations(harnessDir, exec, writable, prompt) {
4716
4868
  const planId = planIdOf(header);
4717
4869
  if (planId === undefined || planId === "")
4718
4870
  return [];
4719
- const selection = resolveActiveWorkflow(harnessDir);
4871
+ const selection = resolveActiveWorkflow(harnessDir, hint);
4720
4872
  if (selection.kind !== "active") {
4721
4873
  if (!sdd)
4722
4874
  return [];
@@ -4799,10 +4951,10 @@ function worktreeL2Violations(header) {
4799
4951
  violations.push(...l2PreDispatchCheck({ tracks }).violations);
4800
4952
  return violations;
4801
4953
  }
4802
- function activeSnapshotRows(harnessDir) {
4954
+ function activeSnapshotRows(harnessDir, hint) {
4803
4955
  if (!existsSync8(join5(harnessDir, STATUS_FILE)))
4804
4956
  return { rows: null, controlWorktreePath: undefined, unreadable: false };
4805
- const selection = resolveActiveWorkflow(harnessDir);
4957
+ const selection = resolveActiveWorkflow(harnessDir, hint);
4806
4958
  if (selection.kind !== "active") {
4807
4959
  return {
4808
4960
  rows: null,
@@ -4825,13 +4977,13 @@ function activeSnapshotRows(harnessDir) {
4825
4977
  const rows = doc.plans.map((row) => asRecord(row)).filter((row) => row !== undefined);
4826
4978
  return { rows, controlWorktreePath, unreadable: false };
4827
4979
  }
4828
- function worktreeL1Violations(harnessDir, header) {
4980
+ function worktreeL1Violations(harnessDir, header, hint) {
4829
4981
  if (harnessDir === null)
4830
4982
  return [];
4831
4983
  const planId = planIdOf(header);
4832
4984
  if (planId === undefined || planId === "")
4833
4985
  return [];
4834
- const read = activeSnapshotRows(harnessDir);
4986
+ const read = activeSnapshotRows(harnessDir, hint);
4835
4987
  if (read.rows === null)
4836
4988
  return [];
4837
4989
  const controlWorktreePath = read.controlWorktreePath;
@@ -4846,10 +4998,10 @@ function worktreeL1Violations(harnessDir, header) {
4846
4998
  }
4847
4999
  return l1PreDispatchCheck({ controlWorktreePath, leaseWorktreePath, leaseWorkingBranch, planId }).violations;
4848
5000
  }
4849
- function writableFanOutUncovered(harnessDir) {
5001
+ function writableFanOutUncovered(harnessDir, hint) {
4850
5002
  if (harnessDir === null)
4851
5003
  return { unreadable: false };
4852
- const read = activeSnapshotRows(harnessDir);
5004
+ const read = activeSnapshotRows(harnessDir, hint);
4853
5005
  if (read.rows === null)
4854
5006
  return { unreadable: read.unreadable };
4855
5007
  for (const row of read.rows) {
@@ -4861,9 +5013,9 @@ function writableFanOutUncovered(harnessDir) {
4861
5013
  }
4862
5014
  return { unreadable: false };
4863
5015
  }
4864
- function dispatchGateCore(config, harnessDir, prompt) {
5016
+ function dispatchGateCore(config, harnessDir, prompt, hint) {
4865
5017
  const header = assignmentHeaderRegion(prompt);
4866
- const violations = [...worktreeL2Violations(header), ...worktreeL1Violations(harnessDir, header)];
5018
+ const violations = [...worktreeL2Violations(header), ...worktreeL1Violations(harnessDir, header, hint)];
4867
5019
  const writable = isReadOnlyAssignmentRole(parseAssignmentFields(header).executeAs ?? "") ? false : undefined;
4868
5020
  violations.push(...composeDispatchGate(header, { caller: config.dispatchBinding ?? "", callerRequired: true, writable }).violations);
4869
5021
  return { violations, writable };
@@ -4887,7 +5039,7 @@ function workflowGateInputOf(exec) {
4887
5039
  }
4888
5040
  return;
4889
5041
  }
4890
- function gateWorkflow(ctx, harnessDir, config, adapter, exec) {
5042
+ function gateWorkflow(ctx, harnessDir, config, adapter, exec, hintReadOf) {
4891
5043
  const toolName = exec.name;
4892
5044
  if (!DEFAULT_WORKFLOW_TOOLS.includes(toolName))
4893
5045
  return;
@@ -4899,7 +5051,7 @@ function gateWorkflow(ctx, harnessDir, config, adapter, exec) {
4899
5051
  ctx.logger(DISPATCH_LOGGER).warn(`workflow gate (${toolName}) (fail-open): malformed arguments — no ${toolName === "ralph" ? "objective" : "meta"} to gate on; call allowed`);
4900
5052
  return;
4901
5053
  }
4902
- const pb = writableFanOutUncovered(harnessDir);
5054
+ const pb = writableFanOutUncovered(harnessDir, hintReadOf().hint);
4903
5055
  if (pb.unreadable) {
4904
5056
  const statusLabel = harnessDir === null ? STATUS_FILE : join5(harnessDir, STATUS_FILE);
4905
5057
  ctx.logger(DISPATCH_LOGGER).warn(`workflow gate (${toolName}) (fail-open): cannot read ${statusLabel} — P-b lease attribution degraded; call allowed`);
@@ -4911,6 +5063,9 @@ function gateWorkflow(ctx, harnessDir, config, adapter, exec) {
4911
5063
  const recordVerdict = (v, code) => {
4912
5064
  if (harnessDir === null)
4913
5065
  return;
5066
+ const hintRead = hintReadOf();
5067
+ if (hintRead.kind === "unavailable")
5068
+ return;
4914
5069
  adapter.recordWorkflowVerdict({
4915
5070
  harnessDir,
4916
5071
  exec,
@@ -4919,7 +5074,8 @@ function gateWorkflow(ctx, harnessDir, config, adapter, exec) {
4919
5074
  ...input.objective !== undefined ? { objective: input.objective } : {},
4920
5075
  mode,
4921
5076
  verdict: v,
4922
- ...code !== undefined ? { code } : {}
5077
+ ...code !== undefined ? { code } : {},
5078
+ ...hintRead.hint !== undefined ? { hint: hintRead.hint } : {}
4923
5079
  });
4924
5080
  };
4925
5081
  switch (verdict.decision) {
@@ -4955,7 +5111,9 @@ function gateWorkflow(ctx, harnessDir, config, adapter, exec) {
4955
5111
  }
4956
5112
  function gateDispatch(ctx, harnessDir, config, adapter, exec) {
4957
5113
  const toolName = exec.name;
4958
- const workflowDecision = gateWorkflow(ctx, harnessDir, config, adapter, exec);
5114
+ let derived;
5115
+ const hintReadOf = () => derived ??= adapter.sessionHintFor(exec.agent);
5116
+ const workflowDecision = gateWorkflow(ctx, harnessDir, config, adapter, exec, hintReadOf);
4959
5117
  if (workflowDecision !== undefined)
4960
5118
  return workflowDecision;
4961
5119
  if (!(config.dispatchTools ?? [...DEFAULT_DISPATCH_TOOLS]).includes(toolName))
@@ -4968,7 +5126,7 @@ function gateDispatch(ctx, harnessDir, config, adapter, exec) {
4968
5126
  if (!isAssignmentShaped2(header))
4969
5127
  return;
4970
5128
  const hard = resolveDispatchHard(harnessDir, config, prompt);
4971
- const result = adapter.dispatchGate(prompt, exec, hard);
5129
+ const result = adapter.dispatchGate(prompt, exec, hard, hintReadOf());
4972
5130
  const verdict = applyEnforcement(result, { hard });
4973
5131
  if (verdict.hardBlocked) {
4974
5132
  ctx.logger(DISPATCH_LOGGER).error(`subagent dispatch (${toolName}) vetoed (Enforcement: hard):
@@ -5034,12 +5192,20 @@ function truncateLedgerField(value, cap) {
5034
5192
  const prefix = keep > 0 ? cps.slice(0, keep).join("") : "";
5035
5193
  return prefix + WORKFLOW_LEDGER_TRUNCATION_MARKER;
5036
5194
  }
5195
+ function normalizeRoleId(value) {
5196
+ const trimmed = value.trim();
5197
+ return trimmed.startsWith("@") ? trimmed.slice(1) : trimmed;
5198
+ }
5199
+ function optionalLedgerId(value) {
5200
+ return typeof value === "string" && value !== "" && value.length <= WORKFLOW_LEDGER_MAX_ID_LENGTH ? value : undefined;
5201
+ }
5037
5202
  var AGENT_FLOW_LOGGER = "mstar/agent-flow";
5038
5203
  var SETTLE_SEAM = "tools/post-execute";
5039
- 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`;
5204
+ 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.inject(['jobs']) → jobs.onJobDone pairing; only UNPAIRED payloads (non-dispatch tools, calls outside the apply-scoped pairing window) stay dispatch-only — never a fabricated settle`;
5040
5205
  var logSink;
5041
5206
  var settleNoteLogged = false;
5042
5207
  var noActiveWarned = false;
5208
+ var catalogCapacityWarned = false;
5043
5209
  var invalidator;
5044
5210
  function setAgentFlowInvalidator(invalidate) {
5045
5211
  const prior = invalidator;
@@ -5051,6 +5217,7 @@ function setAgentFlowLogger(sink) {
5051
5217
  logSink = sink;
5052
5218
  settleNoteLogged = false;
5053
5219
  noActiveWarned = false;
5220
+ catalogCapacityWarned = false;
5054
5221
  return prior;
5055
5222
  }
5056
5223
  function log(level, message) {
@@ -5088,16 +5255,19 @@ function callPairingKey(exec) {
5088
5255
  return;
5089
5256
  return `${sessionIdOf(exec) ?? ""}\x00${callId}`;
5090
5257
  }
5091
- function resolveAgentFlowWriteDir(harnessDir) {
5092
- const selection = resolveActiveWorkflow(harnessDir);
5258
+ function resolveAgentFlowWriteTarget(harnessDir, hint) {
5259
+ const selection = resolveActiveWorkflow(harnessDir, hint);
5093
5260
  if (selection.kind === "active")
5094
- return join6(harnessDir, selection.dir);
5261
+ return { dir: join6(harnessDir, selection.dir), selection };
5095
5262
  const message = selection.kind === "error" ? selection.message : "no active lifecycle (unexpected terminal selection — the active-set resolver has no history view)";
5096
5263
  if (!noActiveWarned) {
5097
5264
  noActiveWarned = true;
5098
5265
  log("warn", `agent-flow record skipped — ${message}`);
5099
5266
  }
5100
- return null;
5267
+ return { dir: null, selection };
5268
+ }
5269
+ function resolveAgentFlowWriteDir(harnessDir, hint) {
5270
+ return resolveAgentFlowWriteTarget(harnessDir, hint).dir;
5101
5271
  }
5102
5272
  var WORKFLOW_LEDGER_LOCKDIR = ".ledger-write.lockdir";
5103
5273
  var WORKFLOW_LOCKER_TIMEOUT_MS = 30000;
@@ -5171,7 +5341,7 @@ function appendEvent(workflowDir, event) {
5171
5341
  }
5172
5342
  function recordDispatch(input) {
5173
5343
  try {
5174
- const workflowDir = resolveAgentFlowWriteDir(input.harnessDir);
5344
+ const workflowDir = resolveAgentFlowWriteDir(input.harnessDir, input.hint);
5175
5345
  if (workflowDir === null)
5176
5346
  return;
5177
5347
  const header = assignmentHeaderRegion(input.prompt);
@@ -5179,13 +5349,14 @@ function recordDispatch(input) {
5179
5349
  const planId = planIdOf(header);
5180
5350
  const taskId = taskIdOf(input.prompt);
5181
5351
  const taskCategory = fields.taskCategory;
5352
+ const role = normalizeRoleId(fields.executeAs ?? "");
5182
5353
  const agent = input.exec !== undefined ? agentOfExec(input.exec) : undefined;
5183
5354
  const event = {
5184
5355
  v: 1,
5185
5356
  ts: Date.now(),
5186
5357
  kind: "dispatch",
5187
5358
  ...agent !== undefined ? { agent } : {},
5188
- role: fields.executeAs ?? "",
5359
+ role,
5189
5360
  ...planId !== undefined && !isNaValue(planId) ? { planId } : {},
5190
5361
  ...taskId !== undefined ? { taskId } : {},
5191
5362
  ...taskCategory !== undefined && taskCategory.trim() !== "" ? { taskCategory } : {},
@@ -5202,14 +5373,20 @@ function recordDispatch(input) {
5202
5373
  if (input.pairing !== undefined && input.exec !== undefined) {
5203
5374
  const key = callPairingKey(input.exec);
5204
5375
  if (key !== undefined) {
5205
- input.pairing.dispatchByCallId.set(key, {
5376
+ const ref = {
5206
5377
  harnessDir: input.harnessDir,
5207
5378
  workflowDir,
5208
5379
  ...agent !== undefined ? { agent } : {},
5209
- role: fields.executeAs ?? "",
5380
+ role,
5210
5381
  ...planId !== undefined && !isNaValue(planId) ? { planId } : {},
5211
5382
  ...taskId !== undefined ? { taskId } : {}
5212
- });
5383
+ };
5384
+ input.pairing.dispatchByCallId.set(key, ref);
5385
+ try {
5386
+ reserveCatalogCandidate(input.pairing, ref, input.exec);
5387
+ } catch (error) {
5388
+ log("error", `catalog candidate reservation failed (contained — the dispatch record and pairing stand): ${errorMessage(error)}`);
5389
+ }
5213
5390
  }
5214
5391
  }
5215
5392
  } catch (error) {
@@ -5221,9 +5398,11 @@ function recordDispatch(input) {
5221
5398
  }
5222
5399
  function recordSettle(input) {
5223
5400
  try {
5224
- const workflowDir = input.workflowDir ?? resolveAgentFlowWriteDir(input.harnessDir);
5401
+ const workflowDir = input.workflowDir ?? resolveAgentFlowWriteDir(input.harnessDir, input.hint);
5225
5402
  if (workflowDir === null)
5226
5403
  return;
5404
+ const childId = optionalLedgerId(input.childId);
5405
+ const taskRef = optionalLedgerId(input.taskRef);
5227
5406
  const event = {
5228
5407
  v: 1,
5229
5408
  ts: Date.now(),
@@ -5231,9 +5410,11 @@ function recordSettle(input) {
5231
5410
  ...input.agent !== undefined && input.agent.trim() !== "" ? { agent: input.agent } : {},
5232
5411
  outcome: input.outcome,
5233
5412
  ...input.durationMs !== undefined && Number.isFinite(input.durationMs) ? { durationMs: input.durationMs } : {},
5234
- ...input.role !== undefined ? { role: input.role } : {},
5413
+ ...input.role !== undefined ? { role: normalizeRoleId(input.role) } : {},
5235
5414
  ...input.planId !== undefined && input.planId !== "" ? { planId: input.planId } : {},
5236
- ...input.taskId !== undefined && input.taskId !== "" ? { taskId: input.taskId } : {}
5415
+ ...input.taskId !== undefined && input.taskId !== "" ? { taskId: input.taskId } : {},
5416
+ ...childId !== undefined ? { childId } : {},
5417
+ ...taskRef !== undefined ? { taskRef } : {}
5237
5418
  };
5238
5419
  appendEvent(workflowDir, event);
5239
5420
  try {
@@ -5247,7 +5428,7 @@ function recordSettle(input) {
5247
5428
  }
5248
5429
  function recordWorkflowEvent(input) {
5249
5430
  try {
5250
- const workflowDir = input.workflowDir ?? resolveAgentFlowWriteDir(input.harnessDir);
5431
+ const workflowDir = input.workflowDir ?? resolveAgentFlowWriteDir(input.harnessDir, input.hint);
5251
5432
  if (workflowDir === null)
5252
5433
  return false;
5253
5434
  appendEvent(workflowDir, input.event);
@@ -5264,7 +5445,7 @@ function recordWorkflowEvent(input) {
5264
5445
  }
5265
5446
  function recordWorkflowVerdict(input) {
5266
5447
  try {
5267
- const workflowDir = resolveAgentFlowWriteDir(input.harnessDir);
5448
+ const workflowDir = resolveAgentFlowWriteDir(input.harnessDir, input.hint);
5268
5449
  if (workflowDir === null)
5269
5450
  return;
5270
5451
  const agent = input.exec !== undefined ? agentOfExec(input.exec) : undefined;
@@ -5295,7 +5476,7 @@ function eventFromUnknown(value) {
5295
5476
  if (rec === undefined || rec.v !== 1 || typeof rec.ts !== "number" || !Number.isFinite(rec.ts))
5296
5477
  return;
5297
5478
  const kind = rec.kind;
5298
- if (kind !== "dispatch" && kind !== "settle" && kind !== "workflow-verdict" && kind !== "workflow-run" && kind !== "workflow-agent" && kind !== "workflow-run-end") {
5479
+ if (kind !== "dispatch" && kind !== "settle" && kind !== "subagent-link" && kind !== "workflow-verdict" && kind !== "workflow-run" && kind !== "workflow-agent" && kind !== "workflow-run-end") {
5299
5480
  return;
5300
5481
  }
5301
5482
  const agent = typeof rec.agent === "string" && rec.agent !== "" ? rec.agent : undefined;
@@ -5322,6 +5503,8 @@ function eventFromUnknown(value) {
5322
5503
  const outcome = rec.outcome;
5323
5504
  if (outcome !== "ok" && outcome !== "error" && outcome !== "denied")
5324
5505
  return;
5506
+ const childId = optionalLedgerId(rec.childId);
5507
+ const taskRef = optionalLedgerId(rec.taskRef);
5325
5508
  return {
5326
5509
  v: 1,
5327
5510
  ts: rec.ts,
@@ -5331,7 +5514,31 @@ function eventFromUnknown(value) {
5331
5514
  ...typeof rec.durationMs === "number" && Number.isFinite(rec.durationMs) ? { durationMs: rec.durationMs } : {},
5332
5515
  ...typeof rec.role === "string" ? { role: rec.role } : {},
5333
5516
  ...typeof rec.planId === "string" && rec.planId !== "" ? { planId: rec.planId } : {},
5334
- ...typeof rec.taskId === "string" && rec.taskId !== "" ? { taskId: rec.taskId } : {}
5517
+ ...typeof rec.taskId === "string" && rec.taskId !== "" ? { taskId: rec.taskId } : {},
5518
+ ...childId !== undefined ? { childId } : {},
5519
+ ...taskRef !== undefined ? { taskRef } : {}
5520
+ };
5521
+ }
5522
+ if (kind === "subagent-link") {
5523
+ const childId = optionalLedgerId(rec.childId);
5524
+ if (childId === undefined)
5525
+ return;
5526
+ if (typeof rec.role !== "string")
5527
+ return;
5528
+ if (typeof rec.label !== "string" || rec.label === "")
5529
+ return;
5530
+ const taskRef = optionalLedgerId(rec.taskRef);
5531
+ return {
5532
+ v: 1,
5533
+ ts: rec.ts,
5534
+ kind: "subagent-link",
5535
+ ...agent !== undefined ? { agent } : {},
5536
+ childId,
5537
+ label: truncateLedgerField(normalizeWorkflowName(rec.label), WORKFLOW_LEDGER_MAX_LABEL_LENGTH),
5538
+ role: rec.role,
5539
+ ...typeof rec.planId === "string" && rec.planId !== "" ? { planId: rec.planId } : {},
5540
+ ...typeof rec.taskId === "string" && rec.taskId !== "" ? { taskId: rec.taskId } : {},
5541
+ ...taskRef !== undefined ? { taskRef } : {}
5335
5542
  };
5336
5543
  }
5337
5544
  if (kind === "workflow-verdict") {
@@ -5479,6 +5686,20 @@ function eventView(event) {
5479
5686
  ...event.code !== undefined ? { code: event.code } : {}
5480
5687
  };
5481
5688
  }
5689
+ if (event.kind === "subagent-link") {
5690
+ return {
5691
+ ts: event.ts,
5692
+ kind: "subagent-link",
5693
+ agent: event.agent ?? null,
5694
+ role: event.role,
5695
+ planId: event.planId ?? null,
5696
+ taskId: event.taskId ?? null,
5697
+ taskCategory: null,
5698
+ label: event.label,
5699
+ childId: event.childId,
5700
+ ...event.taskRef !== undefined ? { taskRef: event.taskRef } : {}
5701
+ };
5702
+ }
5482
5703
  return {
5483
5704
  ts: event.ts,
5484
5705
  kind: "settle",
@@ -5489,13 +5710,15 @@ function eventView(event) {
5489
5710
  taskCategory: null,
5490
5711
  ...event.role !== undefined ? { paired: true } : {},
5491
5712
  ...event.outcome !== undefined ? { outcome: event.outcome } : {},
5492
- ...event.durationMs !== undefined ? { durationMs: event.durationMs } : {}
5713
+ ...event.durationMs !== undefined ? { durationMs: event.durationMs } : {},
5714
+ ...event.childId !== undefined ? { childId: event.childId } : {},
5715
+ ...event.taskRef !== undefined ? { taskRef: event.taskRef } : {}
5493
5716
  };
5494
5717
  }
5495
5718
  function summaryOf(events) {
5496
5719
  const counts = new Map;
5497
5720
  for (const event of events) {
5498
- 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" : "";
5721
+ const role = event.kind === "dispatch" || event.kind === "subagent-link" ? event.role : event.kind === "workflow-run" || event.kind === "workflow-agent" || event.kind === "workflow-run-end" || event.kind === "workflow-verdict" ? "workflow" : "";
5499
5722
  const outcome = event.kind === "dispatch" ? event.verdict : event.kind === "workflow-verdict" ? event.verdict : event.kind === "settle" ? event.outcome : event.kind;
5500
5723
  const key = `${role}\x00${outcome}`;
5501
5724
  counts.set(key, (counts.get(key) ?? 0) + 1);
@@ -5570,7 +5793,8 @@ function readAgentFlow(workflowDir, limit) {
5570
5793
  summary: summaryOf(latestFirst)
5571
5794
  };
5572
5795
  }
5573
- function recordSettleWithRef(ref, outcome, durationMs) {
5796
+ function recordSettleWithRef(ref, outcome, durationMs, childId) {
5797
+ const settleChildId = childId ?? ref.childId;
5574
5798
  recordSettle({
5575
5799
  harnessDir: ref.harnessDir,
5576
5800
  workflowDir: ref.workflowDir,
@@ -5579,7 +5803,9 @@ function recordSettleWithRef(ref, outcome, durationMs) {
5579
5803
  ...durationMs !== undefined && Number.isFinite(durationMs) ? { durationMs } : {},
5580
5804
  role: ref.role,
5581
5805
  ...ref.planId !== undefined ? { planId: ref.planId } : {},
5582
- ...ref.taskId !== undefined ? { taskId: ref.taskId } : {}
5806
+ ...ref.taskId !== undefined ? { taskId: ref.taskId } : {},
5807
+ ...settleChildId !== undefined ? { childId: settleChildId } : {},
5808
+ ...ref.taskRef !== undefined ? { taskRef: ref.taskRef } : {}
5583
5809
  });
5584
5810
  }
5585
5811
  function registerSettleListener(ctx, config, pairing) {
@@ -5600,18 +5826,34 @@ function registerSettleListener(ctx, config, pairing) {
5600
5826
  } else {
5601
5827
  const resultRec = asRecord(result);
5602
5828
  if (resultRec !== undefined) {
5829
+ const value = asRecord(resultRec.value);
5830
+ const childId = value !== undefined && value.kind === "foreground" && typeof value.runId === "string" ? value.runId : undefined;
5603
5831
  if (resultRec.isError === true || resultRec.error !== undefined) {
5604
- recordSettleWithRef(dispatchRef, "error", undefined);
5605
- } else {
5606
- const value = asRecord(resultRec.value);
5607
- if (value !== undefined && value.kind === "background") {
5608
- if (typeof value.taskId === "string" && value.taskId !== "") {
5609
- pairing.dispatchByTaskId.set(value.taskId, dispatchRef);
5610
- }
5611
- } else if (value !== undefined && value.kind === "continuable") {} else {
5612
- recordSettleWithRef(dispatchRef, "ok", undefined);
5832
+ retireCatalogCandidate(pairing, dispatchRef);
5833
+ recordSettleWithRef(dispatchRef, "error", undefined, childId);
5834
+ } else if (value !== undefined && value.kind === "background") {
5835
+ if (typeof value.jobId === "string" && value.jobId !== "") {
5836
+ pairing.dispatchByJobId.set(value.jobId, dispatchRef);
5837
+ const taskRef = optionalLedgerId(value.jobId);
5838
+ if (taskRef !== undefined)
5839
+ dispatchRef.taskRef = taskRef;
5840
+ advanceCatalogCandidate(pairing, dispatchRef, { kind: "background", jobId: value.jobId });
5841
+ } else {
5842
+ retireCatalogCandidate(pairing, dispatchRef);
5843
+ }
5844
+ } else if (value !== undefined && value.kind === "continuable") {
5845
+ const subagentId = optionalLedgerId(value.subagentId);
5846
+ if (subagentId !== undefined) {
5847
+ advanceCatalogCandidate(pairing, dispatchRef, { kind: "continuable", subagentId });
5848
+ } else {
5849
+ retireCatalogCandidate(pairing, dispatchRef);
5613
5850
  }
5851
+ } else {
5852
+ retireCatalogCandidate(pairing, dispatchRef);
5853
+ recordSettleWithRef(dispatchRef, "ok", undefined, childId);
5614
5854
  }
5855
+ } else {
5856
+ retireCatalogCandidate(pairing, dispatchRef);
5615
5857
  }
5616
5858
  }
5617
5859
  if (key !== undefined)
@@ -5628,9 +5870,9 @@ function registerSettleListener(ctx, config, pairing) {
5628
5870
  log("info", SETTLE_SEAM_PAIRING_NOTE);
5629
5871
  }
5630
5872
  }
5631
- function recordTaskSettle(snapshot, pairing) {
5873
+ function recordJobSettle(snapshot, pairing) {
5632
5874
  try {
5633
- const dispatchRef = pairing.dispatchByTaskId.get(snapshot.id);
5875
+ const dispatchRef = pairing.dispatchByJobId.get(snapshot.id);
5634
5876
  if (dispatchRef === undefined)
5635
5877
  return;
5636
5878
  let outcome;
@@ -5644,14 +5886,208 @@ function recordTaskSettle(snapshot, pairing) {
5644
5886
  return;
5645
5887
  const durationMs = typeof snapshot.startedAt === "number" && typeof snapshot.finishedAt === "number" ? snapshot.finishedAt - snapshot.startedAt : undefined;
5646
5888
  recordSettleWithRef(dispatchRef, outcome, durationMs);
5647
- pairing.dispatchByTaskId.delete(snapshot.id);
5889
+ pairing.dispatchByJobId.delete(snapshot.id);
5648
5890
  } catch (error) {
5649
5891
  log("error", `settle record failed (contained): ${errorMessage(error)}`);
5650
5892
  }
5651
5893
  }
5894
+ function reserveCatalogCandidate(pairing, ref, exec) {
5895
+ const session = asRecord(asRecord(exec)?.agent)?.session;
5896
+ if (typeof session !== "object" || session === null)
5897
+ return;
5898
+ const view = asRecord(session);
5899
+ if (view === undefined)
5900
+ return;
5901
+ if (typeof view.id !== "string" || view.id === "")
5902
+ return;
5903
+ const seq = view.seq;
5904
+ if (typeof seq !== "number" || !Number.isSafeInteger(seq) || seq < 0 || seq >= WORKFLOW_LEDGER_MAX_SEQ)
5905
+ return;
5906
+ if (typeof view.eventAt !== "function")
5907
+ return;
5908
+ const label = asRecord(asRecord(exec)?.arguments)?.description;
5909
+ if (typeof label !== "string" || label === "")
5910
+ return;
5911
+ if (normalizeWorkflowName(label) === "")
5912
+ return;
5913
+ if (label.length > WORKFLOW_LEDGER_MAX_NAME_LENGTH)
5914
+ return;
5915
+ let map = pairing.catalogBySession.get(session);
5916
+ if (map === undefined) {
5917
+ map = new Map;
5918
+ pairing.catalogBySession.set(session, map);
5919
+ }
5920
+ if (map.has(label))
5921
+ return;
5922
+ if (map.size >= AGENT_FLOW_MAX_EVENTS) {
5923
+ if (!catalogCapacityWarned) {
5924
+ catalogCapacityWarned = true;
5925
+ log("warn", `catalog slot capacity reached on parent session ${view.id} (${String(AGENT_FLOW_MAX_EVENTS)} slots) — new delegation labels admit no call-window candidate, so no child identity is linked for the rest of this session (existing entries retained; honest degrade)`);
5926
+ }
5927
+ return;
5928
+ }
5929
+ map.set(label, { ref, fromSeq: seq });
5930
+ ref.catalog = { session, label };
5931
+ }
5932
+ function matchCatalogEvent(session, label, join8, envelope) {
5933
+ const result = join8.result;
5934
+ if (result === undefined)
5935
+ return;
5936
+ const rec = asRecord(envelope);
5937
+ if (rec === undefined || rec.type !== "subagent/catalog")
5938
+ return;
5939
+ const data = asRecord(rec.data);
5940
+ if (data === undefined || data.version !== 0)
5941
+ return;
5942
+ if (typeof rec.time !== "number" || !Number.isFinite(rec.time))
5943
+ return;
5944
+ const seq = rec.seq;
5945
+ if (typeof seq !== "number" || !Number.isInteger(seq))
5946
+ return;
5947
+ if (seq < join8.fromSeq || seq >= session.seq || seq >= WORKFLOW_LEDGER_MAX_SEQ)
5948
+ return;
5949
+ const childCreatedAt = data.childCreatedAt;
5950
+ if (typeof childCreatedAt !== "number" || !Number.isInteger(childCreatedAt) || childCreatedAt < 0)
5951
+ return;
5952
+ const childId = data.childId;
5953
+ if (typeof childId !== "string" || childId === "" || childId.length > WORKFLOW_LEDGER_MAX_ID_LENGTH)
5954
+ return;
5955
+ if (data.label !== label)
5956
+ return;
5957
+ if (data.mode !== (result.kind === "background" ? "one-shot" : "continuable"))
5958
+ return;
5959
+ if (result.kind === "continuable" && result.subagentId !== childId)
5960
+ return;
5961
+ return childId;
5962
+ }
5963
+ function consumeCatalogCandidate(map, label, join8, childId) {
5964
+ map.set(label, null);
5965
+ const ref = join8.ref;
5966
+ ref.childId = childId;
5967
+ delete ref.catalog;
5968
+ recordSubagentLink({ ref, childId, label });
5969
+ }
5970
+ function advanceCatalogCandidate(pairing, ref, result) {
5971
+ try {
5972
+ const catalog = ref.catalog;
5973
+ if (catalog === undefined)
5974
+ return;
5975
+ const map = pairing.catalogBySession.get(catalog.session);
5976
+ const join8 = map?.get(catalog.label);
5977
+ if (map === undefined || join8 === undefined || join8 === null || join8.ref !== ref)
5978
+ return;
5979
+ join8.result = result;
5980
+ const end = catalog.session.seq;
5981
+ let skippedReads = 0;
5982
+ let firstSkippedSeq;
5983
+ let firstSkippedError;
5984
+ try {
5985
+ for (let seq = join8.fromSeq;seq < end; seq += 1) {
5986
+ let envelope;
5987
+ try {
5988
+ envelope = catalog.session.eventAt(seq);
5989
+ } catch (error) {
5990
+ skippedReads += 1;
5991
+ if (firstSkippedSeq === undefined) {
5992
+ firstSkippedSeq = seq;
5993
+ firstSkippedError = errorMessage(error);
5994
+ }
5995
+ continue;
5996
+ }
5997
+ const childId = matchCatalogEvent(catalog.session, catalog.label, join8, envelope);
5998
+ if (childId === undefined)
5999
+ continue;
6000
+ consumeCatalogCandidate(map, catalog.label, join8, childId);
6001
+ return;
6002
+ }
6003
+ } finally {
6004
+ if (skippedReads > 0) {
6005
+ log("warn", `catalog catch-up skipped ${String(skippedReads)} unreadable position(s) in parent session ${catalog.session.id} starting at seq ${String(firstSkippedSeq)} (contained — the window walk continues): ${firstSkippedError ?? "unknown"}`);
6006
+ }
6007
+ }
6008
+ } catch (error) {
6009
+ log("warn", `catalog candidate advance failed (contained — the result branch proceeds): ${errorMessage(error)}`);
6010
+ }
6011
+ }
6012
+ function retireCatalogCandidate(pairing, ref) {
6013
+ try {
6014
+ const catalog = ref.catalog;
6015
+ if (catalog === undefined)
6016
+ return;
6017
+ delete ref.catalog;
6018
+ const map = pairing.catalogBySession.get(catalog.session);
6019
+ const slot = map?.get(catalog.label);
6020
+ if (map !== undefined && slot !== undefined && slot !== null && slot.ref === ref)
6021
+ map.set(catalog.label, null);
6022
+ } catch (error) {
6023
+ log("warn", `catalog candidate retire failed (contained — the result branch proceeds): ${errorMessage(error)}`);
6024
+ }
6025
+ }
6026
+ function recordSubagentLink(input) {
6027
+ try {
6028
+ const childId = optionalLedgerId(input.childId);
6029
+ if (childId === undefined) {
6030
+ log("warn", "subagent-link row skipped — the observed catalog child id is missing, empty, or oversized (never truncated, never fabricated)");
6031
+ return;
6032
+ }
6033
+ const label = truncateLedgerField(normalizeWorkflowName(input.label), WORKFLOW_LEDGER_MAX_LABEL_LENGTH);
6034
+ if (label === "") {
6035
+ log("warn", "subagent-link row skipped — the matched delegation label carries no displayable text");
6036
+ return;
6037
+ }
6038
+ const ref = input.ref;
6039
+ if (typeof ref.role !== "string") {
6040
+ log("warn", "subagent-link row skipped — the dispatch ref carries no role identity");
6041
+ return;
6042
+ }
6043
+ const role = normalizeRoleId(ref.role);
6044
+ const taskRef = optionalLedgerId(ref.taskRef);
6045
+ const event = {
6046
+ v: 1,
6047
+ ts: Date.now(),
6048
+ kind: "subagent-link",
6049
+ ...ref.agent !== undefined && ref.agent.trim() !== "" ? { agent: ref.agent } : {},
6050
+ childId,
6051
+ label,
6052
+ role,
6053
+ ...ref.planId !== undefined && ref.planId !== "" ? { planId: ref.planId } : {},
6054
+ ...ref.taskId !== undefined && ref.taskId !== "" ? { taskId: ref.taskId } : {},
6055
+ ...taskRef !== undefined ? { taskRef } : {}
6056
+ };
6057
+ appendEvent(ref.workflowDir, event);
6058
+ try {
6059
+ invalidator?.(ref.harnessDir);
6060
+ } catch (error) {
6061
+ log("error", `catalog invalidation failed (contained): ${errorMessage(error)}`);
6062
+ }
6063
+ } catch (error) {
6064
+ log("error", `subagent-link record failed (contained): ${errorMessage(error)}`);
6065
+ }
6066
+ }
6067
+ function registerSubagentCatalogListener(ctx, pairing) {
6068
+ ctx.events.on("session/event", (session, envelope) => {
6069
+ try {
6070
+ if (typeof session !== "object" || session === null)
6071
+ return;
6072
+ const map = pairing.catalogBySession.get(session);
6073
+ if (map === undefined)
6074
+ return;
6075
+ for (const [label, join8] of map) {
6076
+ if (join8 === null || join8.result === undefined)
6077
+ continue;
6078
+ const childId = matchCatalogEvent(session, label, join8, envelope);
6079
+ if (childId === undefined)
6080
+ continue;
6081
+ consumeCatalogCandidate(map, label, join8, childId);
6082
+ }
6083
+ } catch (error) {
6084
+ log("warn", `subagent catalog observation failed (contained — the session append proceeds): ${errorMessage(error)}`);
6085
+ }
6086
+ });
6087
+ }
5652
6088
 
5653
6089
  // src/engine-status-store.ts
5654
- import { mkdirSync as mkdirSync3, readFileSync as readFileSync8, renameSync as renameSync3, rmSync, writeFileSync as writeFileSync5 } from "node:fs";
6090
+ import { mkdirSync as mkdirSync3, readFileSync as readFileSync8, renameSync as renameSync3, rmSync, statSync as statSync7, writeFileSync as writeFileSync5 } from "node:fs";
5655
6091
  import { dirname as dirname9, join as join8 } from "node:path";
5656
6092
  var ENGINE_STATUS_SNAPSHOT_VERSION = 1;
5657
6093
  var ENGINE_STATUS_SNAPSHOT_ENTRY_VERSION = 1;
@@ -5687,6 +6123,25 @@ function asEntry(value) {
5687
6123
  return;
5688
6124
  return { rv: value.rv, cwd, at, turn: value.turn, payload: value.payload };
5689
6125
  }
6126
+ function asBinding(value) {
6127
+ if (!isPlainObject6(value))
6128
+ return;
6129
+ const cwd = nonEmptyString(value.cwd);
6130
+ if (cwd === undefined)
6131
+ return;
6132
+ const seq = value.excludedBeforeSeq;
6133
+ if (typeof seq !== "number" || !Number.isInteger(seq) || seq < 0)
6134
+ return;
6135
+ const selectedRaw = value.selectedWorkflowId;
6136
+ const selected = nonEmptyString(selectedRaw);
6137
+ if (selectedRaw !== undefined && selected === undefined)
6138
+ return;
6139
+ return {
6140
+ cwd,
6141
+ ...selected === undefined ? {} : { selectedWorkflowId: selected },
6142
+ excludedBeforeSeq: seq
6143
+ };
6144
+ }
5690
6145
  function entryAge(at, nowMs) {
5691
6146
  const parsed = Date.parse(at);
5692
6147
  if (!Number.isFinite(parsed))
@@ -5730,13 +6185,23 @@ function readEngineStatusSnapshot(harnessDir, sessionId) {
5730
6185
  function sessionMap() {
5731
6186
  return Object.create(null);
5732
6187
  }
6188
+ function bindingMap() {
6189
+ return Object.create(null);
6190
+ }
6191
+ function serializeEnvelope(entries, bindings) {
6192
+ const base = { sv: ENGINE_STATUS_SNAPSHOT_VERSION, entries };
6193
+ return Object.keys(bindings).length === 0 ? JSON.stringify(base) : JSON.stringify({ ...base, bindings });
6194
+ }
5733
6195
  function loadForWrite(harnessDir) {
5734
6196
  let raw;
5735
6197
  try {
5736
6198
  raw = readFileSync8(engineStatusSnapshotPath(harnessDir), "utf8");
5737
6199
  } catch (error) {
5738
6200
  if (error.code === "ENOENT") {
5739
- return { kind: "ok", doc: { sv: ENGINE_STATUS_SNAPSHOT_VERSION, entries: sessionMap() } };
6201
+ return {
6202
+ kind: "ok",
6203
+ doc: { sv: ENGINE_STATUS_SNAPSHOT_VERSION, entries: sessionMap(), bindings: bindingMap() }
6204
+ };
5740
6205
  }
5741
6206
  return { kind: "refused", reason: "store-unreadable" };
5742
6207
  }
@@ -5763,7 +6228,18 @@ function loadForWrite(harnessDir) {
5763
6228
  if (kept.length > 0)
5764
6229
  entries[key] = kept;
5765
6230
  }
5766
- return { kind: "ok", doc: { sv: ENGINE_STATUS_SNAPSHOT_VERSION, entries } };
6231
+ const bindings = bindingMap();
6232
+ if (parsed.bindings !== undefined) {
6233
+ if (!isPlainObject6(parsed.bindings))
6234
+ return { kind: "refused", reason: "store-bindings-schema" };
6235
+ for (const [key, value] of Object.entries(parsed.bindings)) {
6236
+ const binding = asBinding(value);
6237
+ if (binding === undefined)
6238
+ return { kind: "refused", reason: "store-bindings-schema" };
6239
+ bindings[key] = binding;
6240
+ }
6241
+ }
6242
+ return { kind: "ok", doc: { sv: ENGINE_STATUS_SNAPSHOT_VERSION, entries, bindings } };
5767
6243
  }
5768
6244
  function pruneBucket(bucket, nowMs) {
5769
6245
  const fresh = [];
@@ -5842,7 +6318,7 @@ function writeEngineStatusSnapshot(harnessDir, input) {
5842
6318
  pruned[key] = kept;
5843
6319
  }
5844
6320
  const maxBytes = input.maxBytes ?? ENGINE_STATUS_SNAPSHOT_MAX_BYTES;
5845
- const serialized = (entries) => JSON.stringify({ sv: ENGINE_STATUS_SNAPSHOT_VERSION, entries });
6321
+ const serialized = (entries) => serializeEnvelope(entries, doc.bindings);
5846
6322
  const evicted = enforceGlobalBound(pruned, input.sessionId, maxBytes, (entries) => Buffer.byteLength(serialized(entries), "utf8"));
5847
6323
  const payload = serialized(pruned);
5848
6324
  const oversized = Buffer.byteLength(payload, "utf8") > maxBytes;
@@ -5851,6 +6327,7 @@ function writeEngineStatusSnapshot(harnessDir, input) {
5851
6327
  total += kept.length;
5852
6328
  writeFileSync5(tmp, payload);
5853
6329
  renameSync3(tmp, file);
6330
+ bindingStoreMemos.delete(file);
5854
6331
  let warn;
5855
6332
  if (oversized && !oversizeWarned.has(file)) {
5856
6333
  oversizeWarned.add(file);
@@ -5871,11 +6348,125 @@ function writeEngineStatusSnapshot(harnessDir, input) {
5871
6348
  return { kind: "degraded", reason: error?.message ?? "write-failed" };
5872
6349
  }
5873
6350
  }
6351
+ var BINDING_STORE_MEMO_CAP = 16;
6352
+ var bindingStoreMemos = new Map;
6353
+ function parseBindingStore(file) {
6354
+ let raw;
6355
+ try {
6356
+ raw = readFileSync8(file, "utf8");
6357
+ } catch {
6358
+ return { kind: "unavailable", reason: "store-unreadable" };
6359
+ }
6360
+ let parsed;
6361
+ try {
6362
+ parsed = JSON.parse(raw);
6363
+ } catch {
6364
+ return { kind: "unavailable", reason: "store-invalid-json" };
6365
+ }
6366
+ if (!isPlainObject6(parsed) || parsed.sv !== ENGINE_STATUS_SNAPSHOT_VERSION || !isPlainObject6(parsed.entries)) {
6367
+ return { kind: "unavailable", reason: "store-envelope-schema" };
6368
+ }
6369
+ const bindings = parsed.bindings;
6370
+ if (bindings === undefined)
6371
+ return { kind: "ok", bindings: undefined };
6372
+ if (!isPlainObject6(bindings))
6373
+ return { kind: "unavailable", reason: "store-bindings-schema" };
6374
+ return { kind: "ok", bindings };
6375
+ }
6376
+ function readBindingStoreSnapshot(file) {
6377
+ let stat;
6378
+ try {
6379
+ stat = statSync7(file);
6380
+ } catch (error) {
6381
+ return error.code === "ENOENT" ? { kind: "ok", bindings: undefined } : { kind: "unavailable", reason: "store-unreadable" };
6382
+ }
6383
+ const memo = bindingStoreMemos.get(file);
6384
+ if (memo !== undefined && memo.ino === stat.ino && memo.size === stat.size && memo.mtimeMs === stat.mtimeMs) {
6385
+ return memo.snapshot;
6386
+ }
6387
+ const snapshot = parseBindingStore(file);
6388
+ if (bindingStoreMemos.size >= BINDING_STORE_MEMO_CAP) {
6389
+ const oldest = bindingStoreMemos.keys().next();
6390
+ if (!oldest.done)
6391
+ bindingStoreMemos.delete(oldest.value);
6392
+ }
6393
+ bindingStoreMemos.set(file, { ino: stat.ino, size: stat.size, mtimeMs: stat.mtimeMs, snapshot });
6394
+ return snapshot;
6395
+ }
6396
+ function readWorkflowSessionBinding(harnessDir, sessionId, cwd) {
6397
+ if (harnessDir === "")
6398
+ return { kind: "unavailable", reason: "no-harness-dir" };
6399
+ if (sessionId === "")
6400
+ return { kind: "unavailable", reason: "no-session-id" };
6401
+ if (cwd === "")
6402
+ return { kind: "unavailable", reason: "no-session-cwd" };
6403
+ const snapshot = readBindingStoreSnapshot(engineStatusSnapshotPath(harnessDir));
6404
+ if (snapshot.kind === "unavailable")
6405
+ return { kind: "unavailable", reason: snapshot.reason };
6406
+ const bindings = snapshot.bindings;
6407
+ if (bindings === undefined || !Object.hasOwn(bindings, sessionId))
6408
+ return { kind: "ok" };
6409
+ const binding = asBinding(bindings[sessionId]);
6410
+ if (binding === undefined)
6411
+ return { kind: "unavailable", reason: "store-bindings-schema" };
6412
+ if (binding.cwd !== cwd)
6413
+ return { kind: "unavailable", reason: "cwd-mismatch" };
6414
+ return { kind: "ok", binding };
6415
+ }
6416
+ function updateWorkflowSessionBinding(harnessDir, sessionId, cwd, update) {
6417
+ if (harnessDir === "")
6418
+ return { kind: "degraded", reason: "no-harness-dir" };
6419
+ if (sessionId === "")
6420
+ return { kind: "degraded", reason: "no-session-id" };
6421
+ if (cwd === "")
6422
+ return { kind: "degraded", reason: "no-session-cwd" };
6423
+ if (!Number.isInteger(update.excludedBeforeSeq) || update.excludedBeforeSeq < 0) {
6424
+ return { kind: "degraded", reason: "invalid-excluded-before-seq" };
6425
+ }
6426
+ if (update.selectedWorkflowId !== undefined && update.selectedWorkflowId === "") {
6427
+ return { kind: "degraded", reason: "invalid-selected-workflow-id" };
6428
+ }
6429
+ const file = engineStatusSnapshotPath(harnessDir);
6430
+ const dir = dirname9(file);
6431
+ const tmp = `${file}.${process.pid}.${Math.random().toString(36).slice(2, 10)}.tmp`;
6432
+ try {
6433
+ mkdirSync3(dir, { recursive: true });
6434
+ return withWorkflowDirLock(dir, () => {
6435
+ const loaded = loadForWrite(harnessDir);
6436
+ if (loaded.kind === "refused")
6437
+ return { kind: "degraded", reason: loaded.reason };
6438
+ const doc = loaded.doc;
6439
+ const existing = Object.hasOwn(doc.bindings, sessionId) ? doc.bindings[sessionId] : undefined;
6440
+ if (existing !== undefined && existing.cwd !== cwd) {
6441
+ return { kind: "degraded", reason: "cwd-mismatch" };
6442
+ }
6443
+ const selected = update.selectedWorkflowId ?? existing?.selectedWorkflowId;
6444
+ doc.bindings[sessionId] = {
6445
+ cwd,
6446
+ ...selected === undefined ? {} : { selectedWorkflowId: selected },
6447
+ excludedBeforeSeq: Math.max(existing?.excludedBeforeSeq ?? 0, update.excludedBeforeSeq)
6448
+ };
6449
+ const payload = serializeEnvelope(doc.entries, doc.bindings);
6450
+ const maxBytes = update.maxBytes ?? ENGINE_STATUS_SNAPSHOT_MAX_BYTES;
6451
+ if (Buffer.byteLength(payload, "utf8") > maxBytes) {
6452
+ return { kind: "degraded", reason: "store-over-byte-ceiling" };
6453
+ }
6454
+ writeFileSync5(tmp, payload);
6455
+ renameSync3(tmp, file);
6456
+ bindingStoreMemos.delete(file);
6457
+ return { kind: "written" };
6458
+ }, { timeoutMs: ENGINE_STATUS_SNAPSHOT_LOCK_TIMEOUT_MS });
6459
+ } catch (error) {
6460
+ try {
6461
+ rmSync(tmp, { force: true });
6462
+ } catch {}
6463
+ return { kind: "degraded", reason: error?.message ?? "write-failed" };
6464
+ }
6465
+ }
5874
6466
 
5875
6467
  // src/gates/catalog.ts
5876
6468
  var CATALOG_LOGGER = "mstar/engine-status-catalog";
5877
6469
  var DEFAULT_CATALOG_TTL_MS = 60000;
5878
- var EXPLICIT_CACHE_KEY = "\x00explicit";
5879
6470
  var RESIDUAL_SEVERITIES = ["critical", "high", "medium", "low", "nit"];
5880
6471
  function isOpenResidualParity(entry) {
5881
6472
  const lifecycle = entry.lifecycle;
@@ -5898,48 +6489,86 @@ function pluginVersion() {
5898
6489
  function engineStatusSource() {
5899
6490
  return { kind: "plugin", plugin: "mstar-engine", form: "catalog" };
5900
6491
  }
5901
- function engineStatusPayload(harnessDir) {
5902
- const iteration = harnessDir !== null ? iterationGateSource(harnessDir) : undefined;
6492
+ function engineStatusPayload(harnessDir, hint) {
6493
+ const selection = harnessDir !== null ? resolveReadWorkflow(harnessDir, hint) : undefined;
6494
+ const iteration = harnessDir !== null ? iterationGateSource(harnessDir, selection) : undefined;
5903
6495
  return {
5904
6496
  version: pluginVersion(),
5905
6497
  harnessDir,
5906
6498
  enforcement: harnessDir !== null ? resolveRepoEnforcement(harnessDir) : { hard: false, source: "none" },
5907
6499
  ...iteration !== undefined ? { iteration } : {},
5908
- state: harnessDir !== null ? harnessStateSource(harnessDir) : null
6500
+ state: harnessDir !== null && selection !== undefined ? harnessStateSource(harnessDir, selection) : null
5909
6501
  };
5910
6502
  }
5911
- function createCatalogInvalidation(cache) {
5912
- const keyByHarnessDir = new Map;
6503
+ function createCatalogInvalidation(cache, digests) {
6504
+ const keysByHarnessDir = new Map;
6505
+ const sessionsOf = (harnessDir) => {
6506
+ let sessions = keysByHarnessDir.get(harnessDir);
6507
+ if (sessions === undefined) {
6508
+ sessions = new Map;
6509
+ keysByHarnessDir.set(harnessDir, sessions);
6510
+ }
6511
+ return sessions;
6512
+ };
5913
6513
  return {
5914
- register(harnessDir, key) {
5915
- if (harnessDir === null)
6514
+ register(harnessDir, sessionId, key) {
6515
+ if (harnessDir === null || sessionId === undefined)
5916
6516
  return;
5917
- keyByHarnessDir.set(harnessDir, key);
6517
+ const sessions = sessionsOf(harnessDir);
6518
+ const previous = sessions.get(sessionId);
6519
+ if (previous !== undefined && previous !== key)
6520
+ cache.delete(previous);
6521
+ sessions.set(sessionId, key);
5918
6522
  },
5919
6523
  invalidate(harnessDir) {
5920
- const key = keyByHarnessDir.get(harnessDir);
5921
- if (key === undefined)
6524
+ const sessions = keysByHarnessDir.get(harnessDir);
6525
+ if (sessions === undefined)
6526
+ return;
6527
+ for (const key of sessions.values())
6528
+ cache.delete(key);
6529
+ },
6530
+ invalidateSession(harnessDir, sessionId) {
6531
+ if (harnessDir === null || sessionId === undefined)
5922
6532
  return;
5923
- cache.delete(key);
6533
+ const key = keysByHarnessDir.get(harnessDir)?.get(sessionId);
6534
+ if (key !== undefined)
6535
+ cache.delete(key);
6536
+ const prefix = `[${JSON.stringify(sessionId)},`;
6537
+ for (const digestKey of [...digests.keys()]) {
6538
+ if (digestKey.startsWith(prefix))
6539
+ digests.delete(digestKey);
6540
+ }
5924
6541
  }
5925
6542
  };
5926
6543
  }
5927
- function buildCatalogPayload(ctx, harnessDir) {
5928
- const payload = engineStatusPayload(harnessDir);
6544
+ function catalogCacheKey(harnessDir, sessionId, cwd, hint) {
6545
+ return JSON.stringify([
6546
+ harnessDir ?? "",
6547
+ sessionId,
6548
+ cwd,
6549
+ hint?.leaseHolder ?? "",
6550
+ hint?.selectedWorkflowId ?? ""
6551
+ ]);
6552
+ }
6553
+ function buildCatalogPayload(ctx, harnessDir, hint) {
6554
+ const payload = engineStatusPayload(harnessDir, hint);
5929
6555
  if (payload.version === "0.0.0") {
5930
6556
  ctx.logger(CATALOG_LOGGER).warn("plugin manifest version unavailable — falling back to 0.0.0 for the engine-status catalog watermark");
5931
6557
  }
5932
6558
  return payload;
5933
6559
  }
5934
- function catalogPayloadFor(ctx, cache, register, key, harnessDir, ttlMs) {
6560
+ function catalogPayloadFor(ctx, cache, invalidation, harnessDir, sessionId, cwd, hint, ttlMs) {
6561
+ if (sessionId === undefined)
6562
+ return buildCatalogPayload(ctx, harnessDir, hint);
6563
+ const key = catalogCacheKey(harnessDir, sessionId, cwd, hint);
5935
6564
  const entry = cache.get(key);
5936
6565
  if (entry !== undefined && Date.now() - entry.builtAt < ttlMs) {
5937
- register(harnessDir, key);
6566
+ invalidation.register(harnessDir, sessionId, key);
5938
6567
  return entry.payload;
5939
6568
  }
5940
- const payload = buildCatalogPayload(ctx, harnessDir);
6569
+ const payload = buildCatalogPayload(ctx, harnessDir, hint);
5941
6570
  cache.set(key, { payload, builtAt: Date.now() });
5942
- register(harnessDir, key);
6571
+ invalidation.register(harnessDir, sessionId, key);
5943
6572
  return payload;
5944
6573
  }
5945
6574
  function renderEngineStatusCatalog(source) {
@@ -6000,7 +6629,7 @@ function renderEngineStatusCatalog(source) {
6000
6629
  function renderAgentFlowLine(flow) {
6001
6630
  const byRole = new Map;
6002
6631
  for (const row of flow.summary) {
6003
- if (row.role === "")
6632
+ if (row.role === "" || row.outcome === "subagent-link")
6004
6633
  continue;
6005
6634
  byRole.set(row.role, (byRole.get(row.role) ?? 0) + row.count);
6006
6635
  }
@@ -6015,28 +6644,16 @@ function hhmm(ts) {
6015
6644
  const date = new Date(ts);
6016
6645
  return `${String(date.getHours()).padStart(2, "0")}:${String(date.getMinutes()).padStart(2, "0")}`;
6017
6646
  }
6018
- function harnessStateSource(harnessDir) {
6019
- if (harnessDir === null)
6020
- return null;
6647
+ function harnessStateSource(harnessDir, selection) {
6021
6648
  const statusPath = join12(harnessDir, STATUS_FILE);
6022
6649
  if (!existsSync14(statusPath))
6023
6650
  return null;
6024
6651
  try {
6025
- const selection = resolveReadWorkflow(harnessDir);
6026
6652
  const rollup = residualRollup(harnessDir);
6027
6653
  const str = (value) => typeof value === "string" && value.trim() !== "" ? value.trim() : null;
6028
6654
  const iterationRefsOf = (value) => Array.isArray(value) ? value.filter((v) => typeof v === "string" && v.trim() !== "") : [];
6029
- const compass = steeringCompassPath(harnessDir);
6030
- let compassFields;
6031
- if (compass !== undefined) {
6032
- try {
6033
- compassFields = parseCompassFrontmatter(compass.compassPath);
6034
- } catch {
6035
- compassFields = undefined;
6036
- }
6037
- }
6038
6655
  if (selection.kind === "error") {
6039
- return selectionErrorState(selection, rollup, harnessDir, compass);
6656
+ return selectionErrorState(selection, rollup, harnessDir);
6040
6657
  }
6041
6658
  const snapshotPath = join12(harnessDir, selection.dir, WORKFLOW_SNAPSHOT_FILE);
6042
6659
  let snapshot;
@@ -6045,7 +6662,7 @@ function harnessStateSource(harnessDir) {
6045
6662
  kind: "error",
6046
6663
  code: "workflow.selection.snapshot-unreadable",
6047
6664
  message: `cannot read the selected workflow snapshot ${snapshotPath}`
6048
- }, rollup, harnessDir, compass);
6665
+ }, rollup, harnessDir);
6049
6666
  }
6050
6667
  try {
6051
6668
  snapshot = readJson(snapshotPath);
@@ -6054,8 +6671,10 @@ function harnessStateSource(harnessDir) {
6054
6671
  kind: "error",
6055
6672
  code: "workflow.selection.snapshot-unreadable",
6056
6673
  message: `cannot read the selected workflow snapshot ${snapshotPath}`
6057
- }, rollup, harnessDir, compass);
6674
+ }, rollup, harnessDir);
6058
6675
  }
6676
+ const compass = selectedCompass(harnessDir, selection.workflowId, snapshot);
6677
+ const compassFields = compass?.doc;
6059
6678
  const plans = [];
6060
6679
  const leases = [];
6061
6680
  if (Array.isArray(snapshot.plans)) {
@@ -6108,7 +6727,7 @@ function harnessStateSource(harnessDir) {
6108
6727
  return null;
6109
6728
  }
6110
6729
  }
6111
- function selectionErrorState(selection, rollup, harnessDir, compass) {
6730
+ function selectionErrorState(selection, rollup, harnessDir) {
6112
6731
  return {
6113
6732
  selection,
6114
6733
  workflowType: null,
@@ -6125,7 +6744,7 @@ function selectionErrorState(selection, rollup, harnessDir, compass) {
6125
6744
  controlWorktreePath: null,
6126
6745
  leases: [],
6127
6746
  knowledge: knowledgeDigest(harnessDir),
6128
- direction: compass !== undefined ? compassDirection(compass.compassPath) : null,
6747
+ direction: null,
6129
6748
  agentFlow: null
6130
6749
  };
6131
6750
  }
@@ -6273,55 +6892,59 @@ function compassDirection(compassPath) {
6273
6892
  return null;
6274
6893
  return cleaned.length > 160 ? `${cleaned.slice(0, 157)}…` : cleaned;
6275
6894
  } catch {
6276
- return null;
6895
+ return null;
6896
+ }
6897
+ }
6898
+ function selectedCompass(harnessDir, workflowId, snapshot) {
6899
+ const ref = snapshot.compass_ref;
6900
+ if (typeof ref !== "string" || ref.trim() === "" || isAbsolute7(ref))
6901
+ return;
6902
+ const compassPath = join12(harnessDir, ref);
6903
+ if (!pathInside(harnessDir, compassPath))
6904
+ return;
6905
+ if (basename5(compassPath) !== "delivery-compass.md")
6906
+ return;
6907
+ if (!existsSync14(compassPath))
6908
+ return;
6909
+ let resolvedHarness;
6910
+ let resolvedCompass;
6911
+ try {
6912
+ resolvedHarness = realpathSync7(harnessDir);
6913
+ resolvedCompass = realpathSync7(compassPath);
6914
+ } catch {
6915
+ return;
6277
6916
  }
6278
- }
6279
- function steeringCompassPath(harnessDir) {
6280
- const iterationsDir = resolveIterationDir(harnessDir);
6281
- if (!existsSync14(iterationsDir))
6917
+ if (!pathInside(resolvedHarness, resolvedCompass))
6282
6918
  return;
6283
- let entries;
6919
+ let doc;
6284
6920
  try {
6285
- entries = readdirSync4(iterationsDir, { withFileTypes: true });
6921
+ doc = parseCompassFrontmatter(compassPath);
6286
6922
  } catch {
6287
6923
  return;
6288
6924
  }
6289
- for (const entry of entries) {
6290
- if (!entry.isDirectory())
6291
- continue;
6292
- const compassPath = join12(iterationsDir, entry.name, "delivery-compass.md");
6293
- if (!existsSync14(compassPath))
6294
- continue;
6295
- let content;
6296
- try {
6297
- content = readFileSync11(compassPath, "utf8");
6298
- } catch {
6299
- continue;
6300
- }
6301
- const frontmatter = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
6302
- if (frontmatter === null || !/^status[ \t]*:[ \t]*(?:active|locked)[ \t]*$/m.test(frontmatter[1]))
6303
- continue;
6304
- return { iterationId: entry.name, compassPath };
6305
- }
6306
- return;
6925
+ if (doc.status !== "active" && doc.status !== "locked")
6926
+ return;
6927
+ if (doc.iteration_id !== workflowId)
6928
+ return;
6929
+ return { iterationId: workflowId, compassPath, doc };
6307
6930
  }
6308
- function iterationGateSource(harnessDir) {
6309
- if (harnessDir === null)
6931
+ function pathInside(root, candidate) {
6932
+ const rel = relative7(root, candidate);
6933
+ return rel !== "" && rel !== ".." && !rel.startsWith(`..${sep3}`) && !isAbsolute7(rel);
6934
+ }
6935
+ function iterationGateSource(harnessDir, selection) {
6936
+ if (harnessDir === null || selection === undefined || selection.kind === "error")
6310
6937
  return;
6311
6938
  const statusPath = join12(harnessDir, STATUS_FILE);
6312
6939
  if (!existsSync14(statusPath))
6313
6940
  return;
6314
- const compass = steeringCompassPath(harnessDir);
6315
- if (compass === undefined)
6316
- return;
6317
- const selection = resolveReadWorkflow(harnessDir);
6318
- if (selection.kind === "error")
6319
- return;
6320
6941
  const snapshotPath = join12(harnessDir, selection.dir, WORKFLOW_SNAPSHOT_FILE);
6321
6942
  try {
6322
6943
  const snapshotDoc = readJson(snapshotPath);
6323
- const compassDoc = parseCompassFrontmatter(compass.compassPath);
6324
- const result = evaluatePhaseGate(snapshotDoc, compassDoc);
6944
+ const compass = selectedCompass(harnessDir, selection.workflowId, snapshotDoc);
6945
+ if (compass === undefined)
6946
+ return;
6947
+ const result = evaluatePhaseGate(snapshotDoc, compass.doc);
6325
6948
  const gate2 = {
6326
6949
  transition: result.transition,
6327
6950
  all_plans_done: result.allPlansDone,
@@ -6330,7 +6953,7 @@ function iterationGateSource(harnessDir) {
6330
6953
  exit: iterationGateView(result.exit),
6331
6954
  violations: result.violations.map(iterationViolationView)
6332
6955
  };
6333
- const compassStatus = compassDoc.status === "active" || compassDoc.status === "locked" ? compassDoc.status : undefined;
6956
+ const compassStatus = compass.doc.status === "active" || compass.doc.status === "locked" ? compass.doc.status : undefined;
6334
6957
  return {
6335
6958
  iterationId: compass.iterationId,
6336
6959
  statusPath: snapshotPath,
@@ -6342,15 +6965,16 @@ function iterationGateSource(harnessDir) {
6342
6965
  return;
6343
6966
  }
6344
6967
  }
6345
- async function preStepCatalogListener(ctx, resolver, explicitKey, cache, ttlMs, register, digests, payload, next) {
6968
+ async function preStepCatalogListener(ctx, resolver, cache, ttlMs, invalidation, digests, payload, next) {
6346
6969
  const decision = await next();
6347
6970
  if (decision.kind === "reject" || payload.signal.aborted)
6348
6971
  return decision;
6349
6972
  try {
6350
6973
  const cwd = sessionCwdOf(payload.agent);
6974
+ const sessionId = sessionHeaderIdOf(payload.agent);
6351
6975
  const harnessDir = resolver.forWorkspace(cwd);
6352
- const key = explicitKey ?? cwd ?? "";
6353
- const catalogPayload = catalogPayloadFor(ctx, cache, register, key, harnessDir, ttlMs);
6976
+ const hint = sessionHintForCatalog(harnessDir, payload.agent);
6977
+ const catalogPayload = catalogPayloadFor(ctx, cache, invalidation, harnessDir, sessionId, cwd ?? "", hint, ttlMs);
6354
6978
  const messages = [...decision.messages];
6355
6979
  const text = renderEngineStatusCatalog(catalogPayload);
6356
6980
  const digestKey = agentDigestKey(payload.agent, cwd);
@@ -6366,9 +6990,22 @@ async function preStepCatalogListener(ctx, resolver, explicitKey, cache, ttlMs,
6366
6990
  return decision;
6367
6991
  }
6368
6992
  }
6993
+ function sessionHintForCatalog(harnessDir, agent) {
6994
+ const identity = sessionHintOf(agent);
6995
+ if (identity === undefined)
6996
+ return;
6997
+ const { cwd, sessionId } = identity;
6998
+ if (cwd === undefined || sessionId === undefined || harnessDir === null)
6999
+ return identity;
7000
+ const binding = readWorkflowSessionBinding(harnessDir, sessionId, cwd);
7001
+ const selected = binding.kind === "ok" ? binding.binding?.selectedWorkflowId : undefined;
7002
+ return selected === undefined ? identity : { ...identity, selectedWorkflowId: selected };
7003
+ }
6369
7004
  function agentDigestKey(agent, cwd) {
6370
- const id = agent?.id;
6371
- return `${typeof id === "string" ? id : "<unknown>"}\x00${cwd ?? ""}`;
7005
+ return JSON.stringify([
7006
+ sessionHeaderIdOf(agent) ?? agentIdOf(agent) ?? "<unknown>",
7007
+ cwd ?? ""
7008
+ ]);
6372
7009
  }
6373
7010
  function persistEngineStatusSnapshot(ctx, harnessDir, cwd, turn, catalogPayload, agent) {
6374
7011
  const sessionId = sessionHeaderIdOf(agent);
@@ -6391,7 +7028,7 @@ function persistEngineStatusSnapshot(ctx, harnessDir, cwd, turn, catalogPayload,
6391
7028
  }
6392
7029
 
6393
7030
  // src/engine-status-endpoint.ts
6394
- import { isAbsolute as isAbsolute3 } from "node:path";
7031
+ import { isAbsolute as isAbsolute9 } from "node:path";
6395
7032
 
6396
7033
  // ../../node_modules/.bun/@deepseek-ai+dsh-typert-protocol@0.1.5-rc.2+1e4e7adc57ac8890/node_modules/@deepseek-ai/dsh-typert-protocol/lib/index.js
6397
7034
  import { Service as Service2 } from "@deepseek-ai/cordis";
@@ -6424,6 +7061,8 @@ function validateName(subject, value) {
6424
7061
  // src/engine-status-wire.ts
6425
7062
  var MSTAR_ENGINE_STATUS_NAMESPACE = "mstar";
6426
7063
  var MSTAR_ENGINE_STATUS_METHOD = "engineStatus";
7064
+ var MSTAR_SELECT_WORKFLOW_METHOD = "selectWorkflow";
7065
+ var SELECT_WORKFLOW_ENDPOINT = `${MSTAR_ENGINE_STATUS_NAMESPACE}/${MSTAR_SELECT_WORKFLOW_METHOD}`;
6427
7066
 
6428
7067
  // src/engine-status-endpoint.ts
6429
7068
  var CONTRIBUTING_PACKAGE = "@mstar-harness/dsh";
@@ -6438,10 +7077,14 @@ function headerCwdOf(value) {
6438
7077
  const header = value?.header;
6439
7078
  return nonEmptyString2(header?.cwd);
6440
7079
  }
7080
+ function liveSeqOf(value) {
7081
+ const seq = value?.seq;
7082
+ return typeof seq === "number" && Number.isInteger(seq) && seq >= 0 ? seq : undefined;
7083
+ }
6441
7084
  function isRefusedPath(cwd) {
6442
7085
  if (cwd.includes("\x00"))
6443
7086
  return true;
6444
- if (!isAbsolute3(cwd))
7087
+ if (!isAbsolute9(cwd))
6445
7088
  return true;
6446
7089
  return cwd.split(/[\\/]+/).includes("..");
6447
7090
  }
@@ -6449,10 +7092,12 @@ function isRefusedPath(cwd) {
6449
7092
  class MstarEngineStatusGateway extends TypertRemoteService {
6450
7093
  resolver;
6451
7094
  bootHarnessDir;
7095
+ invalidateSelection;
6452
7096
  constructor(ctx, options) {
6453
7097
  super(ctx, MSTAR_ENGINE_STATUS_NAMESPACE);
6454
7098
  this.resolver = options.resolver;
6455
7099
  this.bootHarnessDir = options.bootHarnessDir;
7100
+ this.invalidateSelection = options.invalidateSelection;
6456
7101
  }
6457
7102
  async engineStatus(sessionId, cwd) {
6458
7103
  try {
@@ -6462,6 +7107,14 @@ class MstarEngineStatusGateway extends TypertRemoteService {
6462
7107
  return unavailable("internal-error");
6463
7108
  }
6464
7109
  }
7110
+ async selectWorkflow(sessionId, cwd, workflowId) {
7111
+ try {
7112
+ return await this.pick(sessionId, cwd, workflowId);
7113
+ } catch (error) {
7114
+ this.ctx.logger(ENDPOINT_LOGGER).warn(`selectWorkflow failed for ${String(sessionId)} (answering unavailable): ${error?.message ?? error}`);
7115
+ return unavailable("internal-error");
7116
+ }
7117
+ }
6465
7118
  async serve(sessionId, cwd) {
6466
7119
  const sid = nonEmptyString2(sessionId);
6467
7120
  if (sid === undefined)
@@ -6491,8 +7144,92 @@ class MstarEngineStatusGateway extends TypertRemoteService {
6491
7144
  cwd: entry.cwd,
6492
7145
  at: entry.at,
6493
7146
  turn: entry.turn,
6494
- payload: entry.payload
7147
+ payload: entry.payload,
7148
+ binding: { selection: this.currentSelection(harnessDir, sid, entry.cwd, session.source === "live") }
7149
+ };
7150
+ }
7151
+ async pick(sessionId, cwd, workflowId) {
7152
+ const sid = nonEmptyString2(sessionId);
7153
+ if (sid === undefined)
7154
+ return unavailable("invalid-session-id");
7155
+ const claimed = nonEmptyString2(cwd);
7156
+ if (claimed === undefined)
7157
+ return unavailable("invalid-cwd");
7158
+ if (isRefusedPath(claimed))
7159
+ return unavailable("cwd-refused");
7160
+ const wid = nonEmptyString2(workflowId);
7161
+ if (wid === undefined)
7162
+ return unavailable("invalid-workflow-id");
7163
+ const sessions = this.ctx.get("sessions");
7164
+ const live = typeof sessions?.get === "function" ? sessions.get(sid) : undefined;
7165
+ const liveCwd = headerCwdOf(live);
7166
+ if (liveCwd === undefined)
7167
+ return unavailable("session-not-live");
7168
+ if (liveCwd !== claimed)
7169
+ return unavailable("cwd-mismatch");
7170
+ const seq = liveSeqOf(live);
7171
+ if (seq === undefined)
7172
+ return unavailable("session-seq-unavailable");
7173
+ const harnessDir = this.bootHarnessDir ?? this.resolver.forWorkspace(liveCwd);
7174
+ if (harnessDir === null || harnessDir === "")
7175
+ return unavailable("no-harness-dir");
7176
+ const registry = resolveActiveWorkflow(harnessDir);
7177
+ if (registry.kind === "active") {
7178
+ if (registry.workflowId !== wid)
7179
+ return unavailable("workflow-not-active");
7180
+ } else if (registry.kind === "error") {
7181
+ if (registry.code !== "workflow.selection.unbound-multi-active") {
7182
+ return unavailable(`selection-unavailable:${registry.code}`);
7183
+ }
7184
+ if (!(registry.activeWorkflowIds ?? []).includes(wid))
7185
+ return unavailable("workflow-not-active");
7186
+ } else {
7187
+ return unavailable("selection-unavailable:terminal");
7188
+ }
7189
+ const holder = this.leaseHolderOf(sid, liveCwd);
7190
+ const structural = {
7191
+ cwd: liveCwd,
7192
+ sessionId: sid,
7193
+ ...holder === undefined ? {} : { leaseHolder: holder }
6495
7194
  };
7195
+ const automatic = resolveActiveWorkflow(harnessDir, structural);
7196
+ if (automatic.kind === "active" && automatic.workflowId !== wid)
7197
+ return unavailable("binding-conflict");
7198
+ const stored = readWorkflowSessionBinding(harnessDir, sid, liveCwd);
7199
+ if (stored.kind === "unavailable")
7200
+ return unavailable(`store-${stored.reason}`);
7201
+ if (stored.binding?.selectedWorkflowId === wid) {
7202
+ return { status: "selected", sessionId: sid, workflowId: wid };
7203
+ }
7204
+ const written = updateWorkflowSessionBinding(harnessDir, sid, liveCwd, {
7205
+ selectedWorkflowId: wid,
7206
+ excludedBeforeSeq: seq
7207
+ });
7208
+ if (written.kind === "degraded")
7209
+ return unavailable(`store-${written.reason}`);
7210
+ try {
7211
+ this.invalidateSelection?.(harnessDir, sid);
7212
+ } catch (error) {
7213
+ this.ctx.logger(ENDPOINT_LOGGER).warn(`selectWorkflow cache invalidation degraded for ${sid} (the durable pick stands): ${error?.message ?? error}`);
7214
+ }
7215
+ return { status: "selected", sessionId: sid, workflowId: wid };
7216
+ }
7217
+ currentSelection(harnessDir, sessionId, cwd, live) {
7218
+ const holder = live ? this.leaseHolderOf(sessionId, cwd) : undefined;
7219
+ const stored = readWorkflowSessionBinding(harnessDir, sessionId, cwd);
7220
+ const selected = stored.kind === "ok" ? stored.binding?.selectedWorkflowId : undefined;
7221
+ const hint = {
7222
+ cwd,
7223
+ sessionId,
7224
+ ...holder === undefined ? {} : { leaseHolder: holder },
7225
+ ...selected === undefined ? {} : { selectedWorkflowId: selected }
7226
+ };
7227
+ return resolveReadWorkflow(harnessDir, hint);
7228
+ }
7229
+ leaseHolderOf(sessionId, cwd) {
7230
+ const agents = this.ctx.get("agents");
7231
+ const agent = typeof agents?.get === "function" ? agents.get(sessionId) : undefined;
7232
+ return verifiedLeaseHolderOf(agent, sessionId, cwd);
6496
7233
  }
6497
7234
  async resolveSessionCwd(sessionId) {
6498
7235
  const sessions = this.ctx.get("sessions");
@@ -6534,6 +7271,19 @@ function mstarEngineStatusContribution() {
6534
7271
  { name: "cwd", wire: "cwd", source: "json", codec: { mode: "src-json" } }
6535
7272
  ],
6536
7273
  result: { mode: "src-json" }
7274
+ },
7275
+ {
7276
+ id: `${CONTRIBUTING_PACKAGE}#${MSTAR_ENGINE_STATUS_NAMESPACE}/${MSTAR_SELECT_WORKFLOW_METHOD}`,
7277
+ service: MSTAR_ENGINE_STATUS_NAMESPACE,
7278
+ namespace: MSTAR_ENGINE_STATUS_NAMESPACE,
7279
+ method: MSTAR_SELECT_WORKFLOW_METHOD,
7280
+ invocation: { kind: "direct" },
7281
+ parameters: [
7282
+ { name: "sessionId", wire: "sessionId", source: "json", codec: { mode: "src-json" } },
7283
+ { name: "cwd", wire: "cwd", source: "json", codec: { mode: "src-json" } },
7284
+ { name: "workflowId", wire: "workflowId", source: "json", codec: { mode: "src-json" } }
7285
+ ],
7286
+ result: { mode: "src-json" }
6537
7287
  }
6538
7288
  ]
6539
7289
  };
@@ -6571,14 +7321,14 @@ function installEngineStatusEndpoint(ctx, options) {
6571
7321
 
6572
7322
  // src/gates/status.ts
6573
7323
  import { existsSync as existsSync15, readdirSync as readdirSync7 } from "node:fs";
6574
- import { basename as basename5, join as join14, relative as relative7, resolve as resolve10 } from "node:path";
7324
+ import { basename as basename8, join as join14, relative as relative9, resolve as resolve10 } from "node:path";
6575
7325
  var LOGGER_NAME = "mstar/status-gate";
6576
7326
  function harnessDocKindOfTarget(harnessDir, targetPath) {
6577
7327
  const resolved = resolve10(targetPath);
6578
- const name = basename5(resolved);
7328
+ const name = basename8(resolved);
6579
7329
  if (name !== STATUS_FILE && name !== WORKFLOW_SNAPSHOT_FILE && name !== PROJECT_REGISTER_FILE)
6580
7330
  return null;
6581
- const rel = relative7(harnessDir, resolved);
7331
+ const rel = relative9(harnessDir, resolved);
6582
7332
  if (name === STATUS_FILE && rel === STATUS_FILE)
6583
7333
  return "status";
6584
7334
  let workflowDir;
@@ -6590,9 +7340,9 @@ function harnessDocKindOfTarget(harnessDir, targetPath) {
6590
7340
  workflowDir = join14(harnessDir, "workflows");
6591
7341
  projectDir = join14(harnessDir, "projects");
6592
7342
  }
6593
- if (name === WORKFLOW_SNAPSHOT_FILE && /^[^/]+\/snapshot\.json$/.test(relative7(workflowDir, resolved)))
7343
+ if (name === WORKFLOW_SNAPSHOT_FILE && /^[^/]+\/snapshot\.json$/.test(relative9(workflowDir, resolved)))
6594
7344
  return "snapshot";
6595
- if (name === PROJECT_REGISTER_FILE && /^[^/]+\/residuals\.json$/.test(relative7(projectDir, resolved)))
7345
+ if (name === PROJECT_REGISTER_FILE && /^[^/]+\/residuals\.json$/.test(relative9(projectDir, resolved)))
6596
7346
  return "register";
6597
7347
  return null;
6598
7348
  }
@@ -6727,7 +7477,7 @@ async function editIntentListener(ctx, resolver, config, adapter, target, actor,
6727
7477
 
6728
7478
  // src/gates/skill-lint.ts
6729
7479
  import { existsSync as existsSync16, readFileSync as readFileSync13 } from "node:fs";
6730
- import { basename as basename8, dirname as dirname10, resolve as resolve12, sep as sep2 } from "node:path";
7480
+ import { basename as basename10, dirname as dirname10, resolve as resolve12, sep as sep5 } from "node:path";
6731
7481
  var SKILL_LINT_LOGGER = "mstar/skill-lint";
6732
7482
 
6733
7483
  class SkillLintVetoError extends Error {
@@ -6768,7 +7518,7 @@ function lintSkillDoc(doc, options = {}) {
6768
7518
  return violations.length === 0 ? { ok: true, violations } : { ok: false, violations };
6769
7519
  }
6770
7520
  function lintSkillWrite(doc, options) {
6771
- const skillId = basename8(dirname10(resolve12(options.target)));
7521
+ const skillId = basename10(dirname10(resolve12(options.target)));
6772
7522
  const result = lintSkillDoc(doc, { skillId });
6773
7523
  if (options.hard && !result.ok) {
6774
7524
  throw new SkillLintVetoError(options.target, result.violations);
@@ -6781,13 +7531,13 @@ function skillRootsOf(config) {
6781
7531
  return roots.map((root) => root.trim()).filter((root) => root !== "");
6782
7532
  }
6783
7533
  function isSkillTarget(roots, target) {
6784
- if (basename8(target.displayPath) !== "SKILL.md")
7534
+ if (basename10(target.displayPath) !== "SKILL.md")
6785
7535
  return false;
6786
7536
  const resolvedPath = resolve12(target.displayPath);
6787
- return roots.some((root) => resolvedPath.startsWith(resolve12(root) + sep2));
7537
+ return roots.some((root) => resolvedPath.startsWith(resolve12(root) + sep5));
6788
7538
  }
6789
7539
  function skillNameOf(target) {
6790
- return basename8(dirname10(target.displayPath));
7540
+ return basename10(dirname10(target.displayPath));
6791
7541
  }
6792
7542
  function skillCanonicalForm(target) {
6793
7543
  return resolveSkillRoot("dsh", { skill: skillNameOf(target), rel: "SKILL.md" });
@@ -6817,7 +7567,7 @@ function gateSkillIntent(ctx, harnessDir, config, target) {
6817
7567
  });
6818
7568
  return;
6819
7569
  }
6820
- const result = lintSkillDoc(doc, { skillId: basename8(dirname10(skillPath)) });
7570
+ const result = lintSkillDoc(doc, { skillId: basename10(dirname10(skillPath)) });
6821
7571
  if (result.ok)
6822
7572
  return;
6823
7573
  const hard = resolveSeamHard(harnessDir, config);
@@ -6852,7 +7602,7 @@ async function skillWriteIntentListener(ctx, resolver, config, target, actor, ne
6852
7602
 
6853
7603
  // src/gates/seams.ts
6854
7604
  import { existsSync as existsSync17, readFileSync as readFileSync14, readdirSync as readdirSync10 } from "node:fs";
6855
- import { basename as basename10, dirname as dirname11, join as join17, resolve as resolve14, sep as sep3 } from "node:path";
7605
+ import { basename as basename11, dirname as dirname11, join as join17, resolve as resolve14, sep as sep7 } from "node:path";
6856
7606
 
6857
7607
  // ../engine/dist/audit.js
6858
7608
  import { AsyncLocalStorage as AsyncLocalStorage2 } from "node:async_hooks";
@@ -7051,7 +7801,7 @@ var SEAM_LOGGERS = {
7051
7801
  roles: "mstar/roles-lint"
7052
7802
  };
7053
7803
  function isIndexFile(path) {
7054
- const base = basename10(path);
7804
+ const base = basename11(path);
7055
7805
  return base === "README.md" || base === "index.md";
7056
7806
  }
7057
7807
  function isMarkdownDoc(path) {
@@ -7060,7 +7810,7 @@ function isMarkdownDoc(path) {
7060
7810
  function isAuditPlanTarget(path) {
7061
7811
  if (!isMarkdownDoc(path))
7062
7812
  return false;
7063
- const segments = resolve14(path).split(sep3);
7813
+ const segments = resolve14(path).split(sep7);
7064
7814
  for (let i = 0;i < segments.length; i++) {
7065
7815
  if (segments[i] !== "plans")
7066
7816
  continue;
@@ -7071,7 +7821,7 @@ function isAuditPlanTarget(path) {
7071
7821
  return false;
7072
7822
  }
7073
7823
  function isRolesTarget(path) {
7074
- const segments = resolve14(path).split(sep3);
7824
+ const segments = resolve14(path).split(sep7);
7075
7825
  const idx = segments.indexOf("mstar-roles");
7076
7826
  if (idx === -1)
7077
7827
  return false;
@@ -7081,17 +7831,17 @@ function isRolesTarget(path) {
7081
7831
  return rest[0] === "SKILL.md" || rest[0] === "references" && rest[rest.length - 1].endsWith(".md");
7082
7832
  }
7083
7833
  function rolesDirOf(path) {
7084
- const segments = resolve14(path).split(sep3);
7834
+ const segments = resolve14(path).split(sep7);
7085
7835
  const idx = segments.indexOf("mstar-roles");
7086
7836
  if (idx === -1)
7087
7837
  return dirname11(path);
7088
- return idx === 0 ? sep3 : join17(sep3, ...segments.slice(1, idx + 1));
7838
+ return idx === 0 ? sep7 : join17(sep7, ...segments.slice(1, idx + 1));
7089
7839
  }
7090
7840
  function isSeamTarget(seam, harnessDir, target) {
7091
7841
  const path = resolve14(target.displayPath);
7092
7842
  switch (seam) {
7093
7843
  case "design-md": {
7094
- const base = basename10(path);
7844
+ const base = basename11(path);
7095
7845
  return base === "DESIGN.md" || base === "DESIGN.dark.md";
7096
7846
  }
7097
7847
  case "audit":
@@ -7099,7 +7849,7 @@ function isSeamTarget(seam, harnessDir, target) {
7099
7849
  case "compound": {
7100
7850
  if (harnessDir === null)
7101
7851
  return false;
7102
- return path.startsWith(resolve14(join17(harnessDir, "knowledge")) + sep3) && isMarkdownDoc(path);
7852
+ return path.startsWith(resolve14(join17(harnessDir, "knowledge")) + sep7) && isMarkdownDoc(path);
7103
7853
  }
7104
7854
  case "roles":
7105
7855
  return isRolesTarget(path);
@@ -7116,7 +7866,7 @@ function auditSecretsViolations(findings, file) {
7116
7866
  }
7117
7867
  function validateDesignDoc(doc, path) {
7118
7868
  const violations = [...validateDesignTokenFrontmatter(doc).violations];
7119
- const isDark = basename10(path) === "DESIGN.dark.md";
7869
+ const isDark = basename11(path) === "DESIGN.dark.md";
7120
7870
  const sibling = join17(dirname11(path), isDark ? "DESIGN.md" : "DESIGN.dark.md");
7121
7871
  if (existsSync17(sibling)) {
7122
7872
  const light = isDark ? readFileSync14(sibling, "utf8") : doc;
@@ -7195,12 +7945,12 @@ function gateSeamIntent(ctx, harnessDir, config, seam, target) {
7195
7945
  const hard = resolveSeamHard(harnessDir, config);
7196
7946
  const verdict = applyEnforcement(result, { hard });
7197
7947
  if (verdict.hardBlocked) {
7198
- logger.error(`${basename10(path)} write to ${target.displayPath} ALLOWED as repair (Enforcement: hard; the current on-disk document is already invalid — the intent carries no incoming content, so the vetoable signal is only the pre-write state):
7948
+ logger.error(`${basename11(path)} write to ${target.displayPath} ALLOWED as repair (Enforcement: hard; the current on-disk document is already invalid — the intent carries no incoming content, so the vetoable signal is only the pre-write state):
7199
7949
  ${verdict.violations.map(formatViolation).join(`
7200
7950
  `)}`);
7201
7951
  emitSeamAdvisory(ctx, seam, target.displayPath, verdict, hard, { repair: true });
7202
7952
  } else {
7203
- logger.warn(`${basename10(path)} write to ${target.displayPath} (advisory):
7953
+ logger.warn(`${basename11(path)} write to ${target.displayPath} (advisory):
7204
7954
  ${verdict.violations.map(formatViolation).join(`
7205
7955
  `)}`);
7206
7956
  emitSeamAdvisory(ctx, seam, target.displayPath, verdict, hard);
@@ -7661,13 +8411,36 @@ class DshHostAdapter extends Service3 {
7661
8411
  return { ok: true, violations: [] };
7662
8412
  return validateStatusDoc(path, kind, harnessDir);
7663
8413
  }
7664
- dispatchGate(prompt, exec, hard) {
8414
+ sessionHintFor(agent) {
8415
+ const identity = sessionHintOf(agent);
8416
+ if (identity === undefined)
8417
+ return { kind: "ok" };
8418
+ const { sessionId, cwd } = identity;
8419
+ if (sessionId === undefined || cwd === undefined)
8420
+ return { kind: "ok", hint: identity };
8421
+ const harnessDir = this.resolver.forAgent(agent);
8422
+ if (harnessDir === null)
8423
+ return { kind: "ok", hint: identity };
8424
+ const read = readWorkflowSessionBinding(harnessDir, sessionId, cwd);
8425
+ if (read.kind === "unavailable") {
8426
+ this.log("warn", `session ${sessionId}: workflow selection binding ${read.reason} — attribution paused (no ledger write for this call)`);
8427
+ return { kind: "unavailable", reason: read.reason, hint: identity };
8428
+ }
8429
+ const selectedWorkflowId = read.binding?.selectedWorkflowId;
8430
+ return {
8431
+ kind: "ok",
8432
+ hint: selectedWorkflowId === undefined ? identity : { ...identity, selectedWorkflowId }
8433
+ };
8434
+ }
8435
+ dispatchGate(prompt, exec, hard, hintRead) {
7665
8436
  const harnessDir = this.resolver.forAgent(exec?.agent);
7666
- const { violations, writable } = dispatchGateCore(this.config, harnessDir, prompt);
8437
+ const session = hintRead ?? this.sessionHintFor(exec?.agent);
8438
+ const hint = session.hint;
8439
+ const { violations, writable } = dispatchGateCore(this.config, harnessDir, prompt, hint);
7667
8440
  if (exec !== undefined) {
7668
- violations.push(...leaseGateViolations(harnessDir, exec, writable, prompt));
8441
+ violations.push(...leaseGateViolations(harnessDir, exec, writable, prompt, hint));
7669
8442
  }
7670
- if (harnessDir !== null && isAssignmentShaped2(assignmentHeaderRegion(prompt))) {
8443
+ if (harnessDir !== null && session.kind === "ok" && isAssignmentShaped2(assignmentHeaderRegion(prompt))) {
7671
8444
  try {
7672
8445
  recordDispatch({
7673
8446
  harnessDir,
@@ -7675,7 +8448,8 @@ class DshHostAdapter extends Service3 {
7675
8448
  prompt,
7676
8449
  violations,
7677
8450
  hard: hard ?? resolveDispatchHard(harnessDir, this.config, prompt),
7678
- pairing: this.pairing
8451
+ pairing: this.pairing,
8452
+ ...hint !== undefined ? { hint } : {}
7679
8453
  });
7680
8454
  } catch (error) {
7681
8455
  this.ctx.logger(AGENT_FLOW_LOGGER).error(`agent-flow dispatch record failed (contained — dispatch proceeds): ${error.message}`);
@@ -7738,8 +8512,8 @@ class DshHostAdapter extends Service3 {
7738
8512
  }
7739
8513
 
7740
8514
  // src/gates/agent-personas.ts
7741
- import { readFileSync as readFileSync16, readdirSync as readdirSync12, statSync as statSync7 } from "node:fs";
7742
- import { join as join21, resolve as resolve17, sep as sep5 } from "node:path";
8515
+ import { readFileSync as readFileSync16, readdirSync as readdirSync12, statSync as statSync8 } from "node:fs";
8516
+ import { join as join21, resolve as resolve17, sep as sep8 } from "node:path";
7743
8517
  var ROLE_ID_PATTERN = /^[a-z0-9-]{1,32}$/;
7744
8518
  var defaultCache = new Map;
7745
8519
  function personaFor(roleId, lookup, warn) {
@@ -7782,11 +8556,11 @@ function defaultFromMirror(agentsDir, roleId, warn) {
7782
8556
  return;
7783
8557
  const root = resolve17(agentsDir);
7784
8558
  const file = resolve17(agentsDir, `${roleId}.md`);
7785
- if (!file.startsWith(root + sep5))
8559
+ if (!file.startsWith(root + sep8))
7786
8560
  return;
7787
8561
  let stat;
7788
8562
  try {
7789
- stat = statSync7(file);
8563
+ stat = statSync8(file);
7790
8564
  } catch {
7791
8565
  return;
7792
8566
  }
@@ -7907,7 +8681,7 @@ function registerRolePersonaChannel(ctx, config) {
7907
8681
  log2("warn", `role persona channel degraded to pass-through (subagent starts unaffected): ${errorMessage2(error)}`);
7908
8682
  return value;
7909
8683
  }
7910
- });
8684
+ }, { prepend: true });
7911
8685
  }
7912
8686
  var SEAM_WARN = `role persona channel not installed — cordis '${PERSONA_SEAM_EVENT}' seam missing or unrecognized (rolePersonas will not be merged into subagent starts)`;
7913
8687
  function evaluateSeamProbe({ dispatched, readError, value }) {
@@ -8054,9 +8828,25 @@ function errorMessage3(error) {
8054
8828
  return error instanceof Error ? error.message : String(error);
8055
8829
  }
8056
8830
  function sessionIdOf2(session) {
8057
- const id = session?.id;
8831
+ const id = session?.header?.id;
8058
8832
  return typeof id === "string" && id !== "" ? id : undefined;
8059
8833
  }
8834
+ function sessionCwdOf2(session) {
8835
+ const cwd = session?.header?.cwd;
8836
+ return typeof cwd === "string" && cwd.trim() !== "" ? cwd : undefined;
8837
+ }
8838
+ function sessionForkCutOf(session) {
8839
+ const inherited = session?.inheritedEventCount;
8840
+ return typeof inherited === "number" && Number.isSafeInteger(inherited) && inherited >= 0 && inherited < WORKFLOW_LEDGER_MAX_SEQ ? inherited : 0;
8841
+ }
8842
+ function sessionEndSeqOf(session) {
8843
+ const seq = session?.seq;
8844
+ return typeof seq === "number" && Number.isSafeInteger(seq) && seq >= 0 && seq < WORKFLOW_LEDGER_MAX_SEQ ? seq : undefined;
8845
+ }
8846
+ function sessionEventAtOf(session) {
8847
+ const eventAt = session?.eventAt;
8848
+ return typeof eventAt === "function" ? eventAt.bind(session) : undefined;
8849
+ }
8060
8850
  var watermarkCache = new Map;
8061
8851
  function cacheWatermark(workflowDir, watermark) {
8062
8852
  watermarkCache.set(workflowDir, watermark);
@@ -8226,6 +9016,37 @@ function registerWorkflowLedger(ctx, resolver, workflowAskCache) {
8226
9016
  return;
8227
9017
  }
8228
9018
  const depthWarned = new Set;
9019
+ const pendingFloors = new Map;
9020
+ let scanDepth = 0;
9021
+ const persistExclusionFloor = (harnessDir, sessionId, cwd, floor) => {
9022
+ const written = updateWorkflowSessionBinding(harnessDir, sessionId, cwd, { excludedBeforeSeq: floor });
9023
+ if (written.kind === "degraded") {
9024
+ log3("warn", `workflow-ledger exclusion floor not persisted for session ${sessionId} (${written.reason}) — attribution stays paused, the row is re-evaluated at the next scan`);
9025
+ }
9026
+ };
9027
+ const flushExclusionFloors = () => {
9028
+ for (const pending of pendingFloors.values()) {
9029
+ persistExclusionFloor(pending.harnessDir, pending.sessionId, pending.cwd, pending.floor);
9030
+ }
9031
+ pendingFloors.clear();
9032
+ };
9033
+ const observeExclusionFloor = (harnessDir, sessionId, cwd, floor) => {
9034
+ if (scanDepth === 0) {
9035
+ persistExclusionFloor(harnessDir, sessionId, cwd, floor);
9036
+ return;
9037
+ }
9038
+ const key = `${harnessDir}\x00${sessionId}`;
9039
+ const pending = pendingFloors.get(key);
9040
+ if (pending === undefined)
9041
+ pendingFloors.set(key, { harnessDir, sessionId, cwd, floor });
9042
+ else if (floor > pending.floor)
9043
+ pending.floor = floor;
9044
+ };
9045
+ const leaseHolderOf = (sessionId, cwd) => {
9046
+ const agents = ctx.get("agents");
9047
+ const agent = typeof agents?.get === "function" ? agents.get(sessionId) : undefined;
9048
+ return verifiedLeaseHolderOf(agent, sessionId, cwd);
9049
+ };
8229
9050
  const consume = (session, envelope) => {
8230
9051
  const sid = sessionIdOf2(session);
8231
9052
  if (sid === undefined)
@@ -8233,17 +9054,44 @@ function registerWorkflowLedger(ctx, resolver, workflowAskCache) {
8233
9054
  const row = rowOf(session, envelope);
8234
9055
  if (row === undefined)
8235
9056
  return;
8236
- const cwd = session?.header?.cwd;
8237
- const harnessDir = resolver.forWorkspace(typeof cwd === "string" && cwd.trim() !== "" ? cwd : undefined);
9057
+ const workspace = sessionCwdOf2(session);
9058
+ const harnessDir = resolver.forWorkspace(workspace);
8238
9059
  if (harnessDir === null)
8239
9060
  return;
8240
- const workflowDir = resolveAgentFlowWriteDir(harnessDir);
8241
- if (workflowDir === null)
9061
+ const holder = leaseHolderOf(sid, workspace);
9062
+ const identity = {
9063
+ sessionId: sid,
9064
+ ...workspace === undefined ? {} : { cwd: workspace },
9065
+ ...holder === undefined ? {} : { leaseHolder: holder }
9066
+ };
9067
+ let hint = identity;
9068
+ let floor = 0;
9069
+ if (workspace !== undefined) {
9070
+ const binding = readWorkflowSessionBinding(harnessDir, sid, workspace);
9071
+ if (binding.kind === "unavailable") {
9072
+ log3("warn", `workflow-ledger attribution paused for session ${sid} — binding store ${binding.reason} (no ledger write, row stays eligible)`);
9073
+ return;
9074
+ }
9075
+ floor = binding.binding?.excludedBeforeSeq ?? 0;
9076
+ const selected = binding.binding?.selectedWorkflowId;
9077
+ if (selected !== undefined)
9078
+ hint = { ...identity, selectedWorkflowId: selected };
9079
+ }
9080
+ if (row.seq < floor)
9081
+ return;
9082
+ const target = resolveAgentFlowWriteTarget(harnessDir, hint);
9083
+ const workflowDir = target.dir;
9084
+ if (workflowDir === null) {
9085
+ const unbound = target.selection.kind === "error" && target.selection.code === "workflow.selection.unbound-multi-active";
9086
+ if (unbound && workspace !== undefined && row.seq + 1 > floor) {
9087
+ observeExclusionFloor(harnessDir, sid, workspace, row.seq + 1);
9088
+ }
8242
9089
  return;
9090
+ }
8243
9091
  const watermark = loadWatermark(workflowDir);
8244
9092
  let next = watermark.get(sid) ?? 0;
8245
- const events = session.events;
8246
- if (Array.isArray(events) && events.length < next)
9093
+ const endSeq = sessionEndSeqOf(session);
9094
+ if (endSeq !== undefined && endSeq < next)
8247
9095
  next = 0;
8248
9096
  if (row.seq < next)
8249
9097
  return;
@@ -8268,15 +9116,59 @@ function registerWorkflowLedger(ctx, resolver, workflowAskCache) {
8268
9116
  }
8269
9117
  }
8270
9118
  };
9119
+ const scanStartSeq = (session, sid, endSeq) => {
9120
+ const cut = sessionForkCutOf(session);
9121
+ const workspace = sessionCwdOf2(session);
9122
+ if (workspace === undefined)
9123
+ return cut;
9124
+ const harnessDir = resolver.forWorkspace(workspace);
9125
+ if (harnessDir === null)
9126
+ return cut;
9127
+ const binding = readWorkflowSessionBinding(harnessDir, sid, workspace);
9128
+ if (binding.kind === "unavailable")
9129
+ return cut;
9130
+ const floor = binding.binding?.excludedBeforeSeq ?? 0;
9131
+ if (floor > endSeq) {
9132
+ log3("warn", `workflow-ledger session ${sid} exclusion floor ${floor} is past its log end ${endSeq} — identity/log-rebuild drift, scan skipped (the intentional exclusion record is preserved)`);
9133
+ return endSeq;
9134
+ }
9135
+ const selected = binding.binding?.selectedWorkflowId;
9136
+ const holder = leaseHolderOf(sid, workspace);
9137
+ const hint = {
9138
+ sessionId: sid,
9139
+ cwd: workspace,
9140
+ ...holder === undefined ? {} : { leaseHolder: holder },
9141
+ ...selected === undefined ? {} : { selectedWorkflowId: selected }
9142
+ };
9143
+ const bounded = Math.max(floor, cut);
9144
+ const workflowDir = resolveAgentFlowWriteTarget(harnessDir, hint).dir;
9145
+ if (workflowDir === null)
9146
+ return bounded;
9147
+ const cursor = loadWatermark(workflowDir).get(sid) ?? 0;
9148
+ return Math.max(bounded, cursor > endSeq ? 0 : cursor);
9149
+ };
8271
9150
  const scanSession = (session) => {
8272
9151
  const sid = sessionIdOf2(session);
8273
9152
  if (sid === undefined)
8274
9153
  return;
8275
- const events = session.events;
8276
- if (!Array.isArray(events))
9154
+ const endSeq = sessionEndSeqOf(session);
9155
+ if (endSeq === undefined)
8277
9156
  return;
8278
- for (const envelope of events)
8279
- consume(session, envelope);
9157
+ const startSeq = scanStartSeq(session, sid, endSeq);
9158
+ if (startSeq >= endSeq)
9159
+ return;
9160
+ const eventAt = sessionEventAtOf(session);
9161
+ if (eventAt === undefined)
9162
+ return;
9163
+ scanDepth += 1;
9164
+ try {
9165
+ for (let seq = startSeq;seq < endSeq; seq += 1)
9166
+ consume(session, eventAt(seq));
9167
+ } finally {
9168
+ scanDepth -= 1;
9169
+ if (scanDepth === 0)
9170
+ flushExclusionFloors();
9171
+ }
8280
9172
  };
8281
9173
  ctx.events.on("session/created", (session) => {
8282
9174
  try {
@@ -8293,13 +9185,14 @@ function registerWorkflowLedger(ctx, resolver, workflowAskCache) {
8293
9185
  sessionsSnapshot = [];
8294
9186
  }
8295
9187
  for (const session of sessionsSnapshot) {
8296
- const sid = sessionIdOf2(session);
8297
- if (sid === undefined)
8298
- continue;
9188
+ let sid;
8299
9189
  try {
9190
+ sid = sessionIdOf2(session);
9191
+ if (sid === undefined)
9192
+ continue;
8300
9193
  scanSession(session);
8301
9194
  } catch (error) {
8302
- log3("warn", `workflow-ledger cold scan failed for session ${sid} (contained — other sessions unaffected): ${errorMessage3(error)}`);
9195
+ log3("warn", `workflow-ledger cold scan failed for session ${sid ?? "unknown"} (contained — other sessions unaffected): ${errorMessage3(error)}`);
8303
9196
  }
8304
9197
  }
8305
9198
  ctx.events.on("session/event", (session, envelope) => {
@@ -8312,11 +9205,9 @@ function registerWorkflowLedger(ctx, resolver, workflowAskCache) {
8312
9205
  }
8313
9206
 
8314
9207
  // src/gates/goal-bridge.ts
8315
- import { existsSync as existsSync20, readFileSync as readFileSync18, readdirSync as readdirSync13 } from "node:fs";
9208
+ import { existsSync as existsSync20, readdirSync as readdirSync13 } from "node:fs";
8316
9209
  import { join as join23 } from "node:path";
8317
9210
  var GOAL_BRIDGE_LOGGER = "mstar/goal-bridge";
8318
- var DEFAULT_MAX_GOAL_ROUNDS = 256;
8319
- var FLOW_SEQUENCE = "iteration-start → per-plan cycles → iteration-close → PR delivery → merge-ready";
8320
9211
  var GOAL_CHANGE_EVENT_TYPE = "goal/change";
8321
9212
  var GOAL_CHANGE_VERSION = 1;
8322
9213
  var GOAL_ADVISORY_OBJECTIVE_CAP = 512;
@@ -8347,111 +9238,6 @@ function projectRegisterPointer(harnessDir) {
8347
9238
  } catch {}
8348
9239
  return join23(projectsDir, _DEFAULT_PROJECT, PROJECT_REGISTER_FILE);
8349
9240
  }
8350
- function isRootLikeAgent(agent) {
8351
- const header = agent?.session?.header;
8352
- return header?.parentSession === undefined;
8353
- }
8354
- function iterationGoalObjective(iterationId) {
8355
- 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.";
8356
- }
8357
- function steeringCompass(harnessDir) {
8358
- const iterationsDir = resolveIterationDir(harnessDir);
8359
- if (!existsSync20(iterationsDir))
8360
- return;
8361
- let entries;
8362
- try {
8363
- entries = readdirSync13(iterationsDir, { withFileTypes: true });
8364
- } catch {
8365
- return;
8366
- }
8367
- for (const entry of entries) {
8368
- if (!entry.isDirectory())
8369
- continue;
8370
- const compassPath = join23(iterationsDir, entry.name, "delivery-compass.md");
8371
- if (!existsSync20(compassPath))
8372
- continue;
8373
- let content;
8374
- try {
8375
- content = readFileSync18(compassPath, "utf8");
8376
- } catch {
8377
- continue;
8378
- }
8379
- const frontmatter = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
8380
- if (frontmatter === null || !/^status[ \t]*:[ \t]*(?:active|locked)[ \t]*$/m.test(frontmatter[1]))
8381
- continue;
8382
- return { iterationId: entry.name };
8383
- }
8384
- return;
8385
- }
8386
- var GOAL_STALE_REVISION = "GOAL_STALE_REVISION";
8387
- function goalErrorCode(error) {
8388
- if (typeof error !== "object" || error === null)
8389
- return;
8390
- if (!("code" in error))
8391
- return;
8392
- const code = error.code;
8393
- return typeof code === "string" ? code : undefined;
8394
- }
8395
- function resolveGoalRounds(configValue) {
8396
- if (configValue === undefined)
8397
- return DEFAULT_MAX_GOAL_ROUNDS;
8398
- if (!Number.isSafeInteger(configValue) || configValue < 1) {
8399
- log4("warn", `invalid maxGoalRounds config ${String(configValue)} (must be a positive safe integer) — falling back to the default ${DEFAULT_MAX_GOAL_ROUNDS}`);
8400
- return DEFAULT_MAX_GOAL_ROUNDS;
8401
- }
8402
- return configValue;
8403
- }
8404
- function replaceDriftedGoal(goals, agent, current, objective, maxGoalRounds) {
8405
- if (current.phase === "complete") {
8406
- goals.create(agent, { objective, maxGoalRounds });
8407
- return;
8408
- }
8409
- goals.complete(agent, { id: current.id, revision: current.revision });
8410
- goals.create(agent, { objective, maxGoalRounds });
8411
- }
8412
- function mirrorIterationGoal(agent, input) {
8413
- const { resolver, goals, maxGoalRounds } = input;
8414
- if (goals === undefined)
8415
- return false;
8416
- if (!isRootLikeAgent(agent))
8417
- return false;
8418
- const harnessDir = resolver.forAgent(agent);
8419
- if (harnessDir === null)
8420
- return false;
8421
- const compass = steeringCompass(harnessDir);
8422
- if (compass === undefined)
8423
- return false;
8424
- const objective = iterationGoalObjective(compass.iterationId);
8425
- try {
8426
- const current = goals.get(agent);
8427
- if (current === undefined) {
8428
- goals.create(agent, { objective, maxGoalRounds });
8429
- return true;
8430
- }
8431
- if (current.objective === objective)
8432
- return true;
8433
- try {
8434
- replaceDriftedGoal(goals, agent, current, objective, maxGoalRounds);
8435
- return true;
8436
- } catch (error) {
8437
- if (goalErrorCode(error) !== GOAL_STALE_REVISION)
8438
- throw error;
8439
- const fresh = goals.get(agent);
8440
- if (fresh === undefined) {
8441
- goals.create(agent, { objective, maxGoalRounds });
8442
- return true;
8443
- }
8444
- if (fresh.objective === objective)
8445
- return true;
8446
- replaceDriftedGoal(goals, agent, fresh, objective, maxGoalRounds);
8447
- return true;
8448
- }
8449
- } catch (error) {
8450
- const code = goalErrorCode(error);
8451
- log4("warn", `goal mirror failed (contained — the session/decision point proceeds)${code !== undefined ? ` [${code}]` : ""}: ${errorMessage4(error)}`);
8452
- return false;
8453
- }
8454
- }
8455
9241
  function blockedAdvisoryOf(envelope) {
8456
9242
  const record = asRecord(envelope);
8457
9243
  if (record === undefined || record.type !== GOAL_CHANGE_EVENT_TYPE)
@@ -8483,6 +9269,32 @@ function warnBlockedGoal(harnessDir, advisory) {
8483
9269
  const objective = truncateLedgerField(normalizeWorkflowName(advisory.objective), GOAL_ADVISORY_OBJECTIVE_CAP);
8484
9270
  log4("warn", `goal blocked [${code}] — ${reason}; objective: ${objective}; residuals: see ${projectRegisterPointer(harnessDir)} — advisory only, zero harness writes`);
8485
9271
  }
9272
+ function registerGoalBridge(ctx, resolver) {
9273
+ ctx.events.on("session/event", (session, envelope) => {
9274
+ try {
9275
+ const advisory = blockedAdvisoryOf(envelope);
9276
+ if (advisory === undefined)
9277
+ return;
9278
+ const cwd = session?.header?.cwd;
9279
+ const harnessDir = resolver.forWorkspace(typeof cwd === "string" && cwd.trim() !== "" ? cwd : undefined);
9280
+ if (harnessDir === null)
9281
+ return;
9282
+ warnBlockedGoal(harnessDir, advisory);
9283
+ } catch (error) {
9284
+ log4("warn", `goal blocked advisory degraded (contained — the session proceeds): ${errorMessage4(error)}`);
9285
+ }
9286
+ });
9287
+ }
9288
+
9289
+ // src/gates/plan-mode-bridge.ts
9290
+ import { existsSync as existsSync21 } from "node:fs";
9291
+ import { isAbsolute as isAbsolute10, join as join24, relative as relative10, sep as sep9 } from "node:path";
9292
+
9293
+ // src/gates/steering.ts
9294
+ function isRootLikeAgent(agent) {
9295
+ const header = agent?.session?.header;
9296
+ return header?.parentSession === undefined;
9297
+ }
8486
9298
  function rootAgentOf(agent, agents) {
8487
9299
  let current = agent;
8488
9300
  const seen = new Set;
@@ -8503,53 +9315,8 @@ function rootAgentOf(agent, agents) {
8503
9315
  current = parent;
8504
9316
  }
8505
9317
  }
8506
- function registerGoalBridge(ctx, resolver, config) {
8507
- const goals = ctx.get("goals");
8508
- if (goals === undefined) {
8509
- log4("debug", "goals service absent — goal bridge disabled (composition without @deepseek-ai/dsh-goal)");
8510
- }
8511
- const maxGoalRounds = resolveGoalRounds(config.maxGoalRounds);
8512
- const mirror = (agent) => {
8513
- try {
8514
- mirrorIterationGoal(agent, { resolver, goals, maxGoalRounds });
8515
- } catch (error) {
8516
- log4("warn", `goal bridge mirror failed (contained — the session/decision point proceeds): ${errorMessage4(error)}`);
8517
- }
8518
- };
8519
- ctx.events.on("agent/session-start", (payload) => {
8520
- if (payload.agent !== undefined)
8521
- mirror(payload.agent);
8522
- });
8523
- ctx.events.on("subagent/start", (info) => {
8524
- const agents = ctx.get("agents");
8525
- if (agents === undefined || typeof info.id !== "string")
8526
- return;
8527
- const child = agents.get(info.id);
8528
- if (child === undefined)
8529
- return;
8530
- const root = rootAgentOf(child, agents);
8531
- if (root !== undefined)
8532
- mirror(root);
8533
- });
8534
- ctx.events.on("session/event", (session, envelope) => {
8535
- try {
8536
- const advisory = blockedAdvisoryOf(envelope);
8537
- if (advisory === undefined)
8538
- return;
8539
- const cwd = session?.header?.cwd;
8540
- const harnessDir = resolver.forWorkspace(typeof cwd === "string" && cwd.trim() !== "" ? cwd : undefined);
8541
- if (harnessDir === null)
8542
- return;
8543
- warnBlockedGoal(harnessDir, advisory);
8544
- } catch (error) {
8545
- log4("warn", `goal blocked advisory degraded (contained — the session proceeds): ${errorMessage4(error)}`);
8546
- }
8547
- });
8548
- }
8549
9318
 
8550
9319
  // src/gates/plan-mode-bridge.ts
8551
- import { existsSync as existsSync21 } from "node:fs";
8552
- import { join as join24 } from "node:path";
8553
9320
  var PLAN_MODE_BRIDGE_LOGGER = "mstar/plan-mode-bridge";
8554
9321
  var PLAN_STATUS_TODO = "Todo";
8555
9322
  var planModeBridgeLogSink = () => {};
@@ -8564,29 +9331,73 @@ function log5(level, message) {
8564
9331
  function errorMessage5(error) {
8565
9332
  return error instanceof Error ? error.message : String(error);
8566
9333
  }
8567
- function hasPrepareWindow(harnessDir) {
8568
- const statusPath = join24(harnessDir, STATUS_FILE);
8569
- if (!existsSync21(statusPath))
9334
+ function selectedLifecycle(harnessDir, hint) {
9335
+ if (!existsSync21(join24(harnessDir, STATUS_FILE)))
9336
+ return { kind: "none" };
9337
+ const selection = resolveReadWorkflow(harnessDir, hint);
9338
+ if (selection.kind === "error") {
9339
+ return selection.code === "workflow.selection.unbound-multi-active" ? { kind: "unbound" } : { kind: "none" };
9340
+ }
9341
+ let doc;
9342
+ try {
9343
+ doc = readJson(join24(harnessDir, selection.dir, WORKFLOW_SNAPSHOT_FILE));
9344
+ } catch {
9345
+ return { kind: "none" };
9346
+ }
9347
+ if (asRecord(doc) === undefined)
9348
+ return { kind: "none" };
9349
+ return { kind: "selected", workflowId: selection.workflowId, doc };
9350
+ }
9351
+ function selectedCompassSteers(harnessDir, lifecycle) {
9352
+ const ref = lifecycle.doc.compass_ref;
9353
+ if (typeof ref !== "string" || ref.trim() === "" || isAbsolute10(ref))
8570
9354
  return false;
8571
- const selection = resolveReadWorkflow(harnessDir);
8572
- if (selection.kind === "error")
9355
+ const compassPath = join24(harnessDir, ref);
9356
+ const inside = relative10(harnessDir, compassPath);
9357
+ if (inside === "" || inside === ".." || inside.startsWith(`..${sep9}`) || isAbsolute10(inside))
8573
9358
  return false;
8574
- const snapshotPath = join24(harnessDir, selection.dir, WORKFLOW_SNAPSHOT_FILE);
8575
- let doc;
9359
+ if (!existsSync21(compassPath))
9360
+ return false;
9361
+ let fields;
8576
9362
  try {
8577
- doc = readJson(snapshotPath);
9363
+ fields = parseCompassFrontmatter(compassPath);
8578
9364
  } catch {
8579
9365
  return false;
8580
9366
  }
9367
+ if (fields.status !== "active" && fields.status !== "locked")
9368
+ return false;
9369
+ return fields.iteration_id === lifecycle.workflowId;
9370
+ }
9371
+ function hasPrepareWindow(doc) {
8581
9372
  const plans = doc.plans;
8582
9373
  if (!Array.isArray(plans))
8583
9374
  return false;
8584
9375
  return plans.some((row) => asRecord(row)?.status === PLAN_STATUS_TODO);
8585
9376
  }
8586
- function planModeTarget(harnessDir) {
8587
- if (steeringCompass(harnessDir) === undefined)
9377
+ function planModeTarget(harnessDir, hint) {
9378
+ const lifecycle = selectedLifecycle(harnessDir, hint);
9379
+ if (lifecycle.kind === "unbound")
9380
+ return;
9381
+ if (lifecycle.kind === "none")
9382
+ return false;
9383
+ if (!selectedCompassSteers(harnessDir, lifecycle))
8588
9384
  return false;
8589
- return hasPrepareWindow(harnessDir);
9385
+ return hasPrepareWindow(lifecycle.doc);
9386
+ }
9387
+ function rootSessionHint(agent, harnessDir) {
9388
+ const identity = sessionHintOf(agent);
9389
+ if (identity === undefined)
9390
+ return;
9391
+ const { cwd, sessionId } = identity;
9392
+ if (cwd === undefined || sessionId === undefined)
9393
+ return identity;
9394
+ const binding = readWorkflowSessionBinding(harnessDir, sessionId, cwd);
9395
+ if (binding.kind === "unavailable") {
9396
+ log5("warn", `planMode sync: session ${sessionId} binding store ${binding.reason} — selection falls back to lease/worktree evidence`);
9397
+ return identity;
9398
+ }
9399
+ const selected = binding.binding?.selectedWorkflowId;
9400
+ return selected === undefined ? identity : { ...identity, selectedWorkflowId: selected };
8590
9401
  }
8591
9402
  function syncPlanMode(agent, input) {
8592
9403
  const { resolver, planMode } = input;
@@ -8598,7 +9409,10 @@ function syncPlanMode(agent, input) {
8598
9409
  if (harnessDir === null)
8599
9410
  return false;
8600
9411
  try {
8601
- planMode.set(agent, planModeTarget(harnessDir));
9412
+ const target = planModeTarget(harnessDir, rootSessionHint(agent, harnessDir));
9413
+ if (target === undefined)
9414
+ return false;
9415
+ planMode.set(agent, target);
8602
9416
  return true;
8603
9417
  } catch (error) {
8604
9418
  log5("warn", `planMode sync failed (contained — the session/decision point proceeds): ${errorMessage5(error)}`);
@@ -9111,7 +9925,7 @@ function registerMstarCommands(ctx) {
9111
9925
  for (const file of readdirSync14(dir).sort()) {
9112
9926
  if (!file.endsWith(".md"))
9113
9927
  continue;
9114
- const parsed = parseCommandMarkdown(readFileSync19(join25(dir, file), "utf8"));
9928
+ const parsed = parseCommandMarkdown(readFileSync18(join25(dir, file), "utf8"));
9115
9929
  if (parsed === undefined)
9116
9930
  continue;
9117
9931
  commandsCtx.commands.register({
@@ -9142,7 +9956,8 @@ function apply(ctx, config) {
9142
9956
  new DshMstar(ctx, { harnessDir: bootHarnessDir });
9143
9957
  const pairing = {
9144
9958
  dispatchByCallId: new Map,
9145
- dispatchByTaskId: new Map
9959
+ dispatchByJobId: new Map,
9960
+ catalogBySession: new WeakMap
9146
9961
  };
9147
9962
  const adapter = new DshHostAdapter(ctx, { resolver, config, pairing });
9148
9963
  setAgentFlowLogger((level, message) => {
@@ -9241,6 +10056,7 @@ function apply(ctx, config) {
9241
10056
  };
9242
10057
  });
9243
10058
  registerSettleListener(ctx, config, pairing);
10059
+ registerSubagentCatalogListener(ctx, pairing);
9244
10060
  setWorkflowLedgerLogger((level, message) => {
9245
10061
  const logger = ctx.logger(WORKFLOW_LEDGER_LOGGER);
9246
10062
  if (level === "warn")
@@ -9256,7 +10072,7 @@ function apply(ctx, config) {
9256
10072
  else
9257
10073
  logger.warn(message);
9258
10074
  });
9259
- registerGoalBridge(ctx, resolver, config);
10075
+ registerGoalBridge(ctx, resolver);
9260
10076
  setPlanModeBridgeLogger((level, message) => {
9261
10077
  const logger = ctx.logger(PLAN_MODE_BRIDGE_LOGGER);
9262
10078
  if (level === "debug")
@@ -9269,7 +10085,7 @@ function apply(ctx, config) {
9269
10085
  const jobs = jobsCtx.jobs;
9270
10086
  try {
9271
10087
  jobs.onJobDone((snapshot, _owner) => {
9272
- recordTaskSettle(snapshot, pairing);
10088
+ recordJobSettle(snapshot, pairing);
9273
10089
  });
9274
10090
  } catch (error) {
9275
10091
  ctx.logger(AGENT_FLOW_LOGGER).error(`jobs.onJobDone registration failed (contained — background settle pairing degraded): ${error.message}`);
@@ -9301,18 +10117,16 @@ function apply(ctx, config) {
9301
10117
  runAdvisoryPass();
9302
10118
  });
9303
10119
  const ttlMs = config.catalogTtlMs ?? DEFAULT_CATALOG_TTL_MS;
9304
- const explicitKey = bootHarnessDir !== null ? EXPLICIT_CACHE_KEY : undefined;
9305
10120
  const catalogCache = new Map;
9306
- if (explicitKey !== undefined) {
9307
- catalogCache.set(explicitKey, { payload: buildCatalogPayload(ctx, bootHarnessDir), builtAt: Date.now() });
9308
- }
9309
- const catalogInvalidation = createCatalogInvalidation(catalogCache);
9310
- if (explicitKey !== undefined)
9311
- catalogInvalidation.register(bootHarnessDir, explicitKey);
9312
- setAgentFlowInvalidator(catalogInvalidation.invalidate);
9313
10121
  const catalogDigests = new Map;
9314
- ctx.on("agent/pre-step", (payload, next) => preStepCatalogListener(ctx, resolver, explicitKey, catalogCache, ttlMs, catalogInvalidation.register, catalogDigests, payload, next));
9315
- installEngineStatusEndpoint(ctx, { resolver, bootHarnessDir });
10122
+ const catalogInvalidation = createCatalogInvalidation(catalogCache, catalogDigests);
10123
+ setAgentFlowInvalidator(catalogInvalidation.invalidate);
10124
+ ctx.on("agent/pre-step", (payload, next) => preStepCatalogListener(ctx, resolver, catalogCache, ttlMs, catalogInvalidation, catalogDigests, payload, next));
10125
+ installEngineStatusEndpoint(ctx, {
10126
+ resolver,
10127
+ bootHarnessDir,
10128
+ invalidateSelection: (harnessDir, sessionId) => catalogInvalidation.invalidateSession(harnessDir, sessionId)
10129
+ });
9316
10130
  registerSddIterationTools(ctx, resolver);
9317
10131
  registerSeamTools(ctx, resolver);
9318
10132
  }