@mstar-harness/dsh 3.8.0 → 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.
- package/README.i18n.yaml +2 -2
- package/README.md +142 -194
- package/README.zh.md +29 -17
- package/bundle/README.md +83 -171
- package/dist/client/index.d.ts +17 -8
- package/dist/client/panel/MstarPanelTitle.d.ts +13 -0
- package/dist/client/panel/PanelView.d.ts +56 -52
- package/dist/client/panel/TabNav.d.ts +17 -14
- package/dist/client/panel/definition.d.ts +23 -0
- package/dist/client/panel/engine-status-client.d.ts +84 -6
- package/dist/client/panel/graph/project-graph.d.ts +35 -64
- package/dist/client/panel/graph/schema.d.ts +1 -2
- package/dist/client/panel/guards.d.ts +41 -1
- package/dist/client/panel/locale.d.ts +1 -1
- package/dist/client/panel/mstar-glyph.d.ts +22 -0
- package/dist/client/panel/pages/AgentListPage.d.ts +71 -0
- package/dist/client/panel/pages/EventLogPage.d.ts +7 -4
- package/dist/client/panel/pages/IterationInfoSection.d.ts +16 -13
- package/dist/client/panel/pages/IterationTaskPage.d.ts +13 -14
- package/dist/client/panel/panel-store.d.ts +28 -0
- package/dist/client/panel/sidebar.d.ts +13 -7
- package/dist/client/panel/state-section.d.ts +25 -3
- package/dist/client/panel/use-mstar-engine-status.d.ts +39 -14
- package/dist/client/panel/zones/Legend.d.ts +5 -3
- package/dist/client/panel/zones/TaskBoard.d.ts +13 -9
- package/dist/client.js +1038 -1242
- package/dist/engine-status-endpoint.d.ts +85 -8
- package/dist/engine-status-store.d.ts +91 -1
- package/dist/engine-status-wire.d.ts +9 -0
- package/dist/gates/_shared.d.ts +61 -9
- package/dist/gates/adapter.d.ts +32 -2
- package/dist/gates/agent-flow.d.ts +312 -60
- package/dist/gates/catalog.d.ts +59 -38
- package/dist/gates/dispatch.d.ts +11 -2
- package/dist/gates/goal-bridge.d.ts +10 -130
- package/dist/gates/plan-mode-bridge.d.ts +20 -11
- package/dist/gates/role-persona.d.ts +16 -0
- package/dist/gates/steering.d.ts +41 -0
- package/dist/gates/workflow-ledger.d.ts +31 -4
- package/dist/gates/workflow-selection.d.ts +41 -20
- package/dist/index.js +1208 -394
- package/dist/types.d.ts +50 -18
- package/harness-commands/amazing-pr-review.md +2 -0
- package/harness-commands/codebase-audit.md +2 -0
- package/harness-skills/mstar-host/SKILL.md +3 -1
- package/harness-skills/mstar-host/references/dsh-workflow-scripts.md +424 -0
- package/harness-skills/mstar-host/references/dsh.md +259 -266
- package/harness-skills/mstar-roles/references/project-manager.md +2 -0
- package/harness-skills/mstar-sdd/SKILL.md +2 -0
- package/package.json +66 -64
- package/dist/client/panel/pages/AgentCanvasPage.d.ts +0 -345
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
4577
|
+
const registry = doc.workflows;
|
|
4578
|
+
if (!Array.isArray(registry)) {
|
|
4454
4579
|
return {
|
|
4455
4580
|
kind: "error",
|
|
4456
|
-
code: "workflow.selection.
|
|
4457
|
-
message:
|
|
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
|
-
|
|
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.
|
|
4468
|
-
message:
|
|
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
|
-
|
|
4472
|
-
|
|
4473
|
-
|
|
4474
|
-
|
|
4475
|
-
|
|
4476
|
-
|
|
4477
|
-
|
|
4478
|
-
|
|
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 {
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
5376
|
+
const ref = {
|
|
5206
5377
|
harnessDir: input.harnessDir,
|
|
5207
5378
|
workflowDir,
|
|
5208
5379
|
...agent !== undefined ? { agent } : {},
|
|
5209
|
-
role
|
|
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
|
-
|
|
5605
|
-
|
|
5606
|
-
|
|
5607
|
-
if (value
|
|
5608
|
-
|
|
5609
|
-
|
|
5610
|
-
|
|
5611
|
-
|
|
5612
|
-
|
|
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
|
|
5873
|
+
function recordJobSettle(snapshot, pairing) {
|
|
5632
5874
|
try {
|
|
5633
|
-
const dispatchRef = pairing.
|
|
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.
|
|
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 {
|
|
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
|
-
|
|
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) =>
|
|
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;
|
|
@@ -5896,50 +6487,88 @@ function pluginVersion() {
|
|
|
5896
6487
|
return "0.0.0";
|
|
5897
6488
|
}
|
|
5898
6489
|
function engineStatusSource() {
|
|
5899
|
-
return { kind: "plugin", plugin: "mstar-engine
|
|
6490
|
+
return { kind: "plugin", plugin: "mstar-engine", form: "catalog" };
|
|
5900
6491
|
}
|
|
5901
|
-
function engineStatusPayload(harnessDir) {
|
|
5902
|
-
const
|
|
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
|
|
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
|
-
|
|
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
|
|
5921
|
-
if (
|
|
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
|
-
|
|
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
|
|
5928
|
-
|
|
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,
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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:
|
|
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
|
|
6919
|
+
let doc;
|
|
6284
6920
|
try {
|
|
6285
|
-
|
|
6921
|
+
doc = parseCompassFrontmatter(compassPath);
|
|
6286
6922
|
} catch {
|
|
6287
6923
|
return;
|
|
6288
6924
|
}
|
|
6289
|
-
|
|
6290
|
-
|
|
6291
|
-
|
|
6292
|
-
|
|
6293
|
-
|
|
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
|
|
6309
|
-
|
|
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
|
|
6324
|
-
|
|
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 =
|
|
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,
|
|
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
|
|
6353
|
-
const catalogPayload = catalogPayloadFor(ctx, cache,
|
|
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
|
-
|
|
6371
|
-
|
|
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,9 +7028,9 @@ function persistEngineStatusSnapshot(ctx, harnessDir, cwd, turn, catalogPayload,
|
|
|
6391
7028
|
}
|
|
6392
7029
|
|
|
6393
7030
|
// src/engine-status-endpoint.ts
|
|
6394
|
-
import { isAbsolute as
|
|
7031
|
+
import { isAbsolute as isAbsolute9 } from "node:path";
|
|
6395
7032
|
|
|
6396
|
-
// ../../node_modules/.bun/@deepseek-ai+dsh-typert-protocol@0.1.5-rc.
|
|
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";
|
|
6398
7035
|
var TYPERT_REMOTE_SEGMENT_PATTERN = /^[A-Za-z0-9_$.-]+$/;
|
|
6399
7036
|
function isTypertRemoteSegment(value) {
|
|
@@ -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 (!
|
|
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
|
|
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 =
|
|
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 =
|
|
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(
|
|
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(
|
|
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
|
|
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 =
|
|
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 (
|
|
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) +
|
|
7537
|
+
return roots.some((root) => resolvedPath.startsWith(resolve12(root) + sep5));
|
|
6788
7538
|
}
|
|
6789
7539
|
function skillNameOf(target) {
|
|
6790
|
-
return
|
|
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:
|
|
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
|
|
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 =
|
|
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(
|
|
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(
|
|
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(
|
|
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 ?
|
|
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 =
|
|
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")) +
|
|
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 =
|
|
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(`${
|
|
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(`${
|
|
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
|
-
|
|
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
|
|
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
|
|
7742
|
-
import { join as join21, resolve as resolve17, sep as
|
|
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 +
|
|
8559
|
+
if (!file.startsWith(root + sep8))
|
|
7786
8560
|
return;
|
|
7787
8561
|
let stat;
|
|
7788
8562
|
try {
|
|
7789
|
-
stat =
|
|
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
|
|
8237
|
-
const harnessDir = resolver.forWorkspace(
|
|
9057
|
+
const workspace = sessionCwdOf2(session);
|
|
9058
|
+
const harnessDir = resolver.forWorkspace(workspace);
|
|
8238
9059
|
if (harnessDir === null)
|
|
8239
9060
|
return;
|
|
8240
|
-
const
|
|
8241
|
-
|
|
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
|
|
8246
|
-
if (
|
|
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
|
|
8276
|
-
if (
|
|
9154
|
+
const endSeq = sessionEndSeqOf(session);
|
|
9155
|
+
if (endSeq === undefined)
|
|
8277
9156
|
return;
|
|
8278
|
-
|
|
8279
|
-
|
|
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
|
-
|
|
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,
|
|
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
|
|
8568
|
-
|
|
8569
|
-
|
|
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
|
|
8572
|
-
|
|
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
|
-
|
|
8575
|
-
|
|
9359
|
+
if (!existsSync21(compassPath))
|
|
9360
|
+
return false;
|
|
9361
|
+
let fields;
|
|
8576
9362
|
try {
|
|
8577
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
9315
|
-
|
|
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
|
}
|