@mstar-harness/dsh 3.7.3 → 3.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.i18n.yaml +2 -2
- package/README.md +2 -2
- package/README.zh.md +2 -2
- package/bundle/README.md +8 -1
- package/dist/client/index.d.ts +12 -6
- package/dist/client/panel/PanelView.d.ts +38 -16
- package/dist/client/panel/engine-status-client.d.ts +187 -0
- package/dist/client/panel/graph/project-graph.d.ts +4 -4
- package/dist/client/panel/guards.d.ts +52 -3
- package/dist/client/panel/locale.d.ts +1 -1
- package/dist/client/panel/panel-meta.d.ts +2 -2
- package/dist/client/panel/sidebar.d.ts +2 -2
- package/dist/client/panel/state-section.d.ts +2 -2
- package/dist/client/panel/use-mstar-engine-status.d.ts +104 -32
- package/dist/client.js +333 -50
- package/dist/engine-status-endpoint.d.ts +154 -0
- package/dist/engine-status-store.d.ts +190 -0
- package/dist/engine-status-wire.d.ts +29 -0
- package/dist/gates/_shared.d.ts +9 -0
- package/dist/gates/catalog.d.ts +7 -7
- package/dist/gates/system-prompt.d.ts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +528 -94
- package/dist/types.d.ts +33 -19
- package/harness-skills/mstar-artifacts/references/status-and-residuals.md +1 -1
- package/harness-skills/mstar-host/references/dsh.md +13 -4
- package/package.json +4 -2
package/dist/index.js
CHANGED
|
@@ -1122,8 +1122,8 @@ var require_lib2 = __commonJS(function(exports, module) {
|
|
|
1122
1122
|
});
|
|
1123
1123
|
|
|
1124
1124
|
// src/index.ts
|
|
1125
|
-
import { existsSync as existsSync22, readFileSync as
|
|
1126
|
-
import { join as
|
|
1125
|
+
import { existsSync as existsSync22, readFileSync as readFileSync19, readdirSync as readdirSync14 } from "node:fs";
|
|
1126
|
+
import { join as join25 } from "node:path";
|
|
1127
1127
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
1128
1128
|
import {
|
|
1129
1129
|
apply as applySkillLocal,
|
|
@@ -4353,6 +4353,11 @@ function sessionCwdOf(agent) {
|
|
|
4353
4353
|
const cwd = session?.header?.cwd;
|
|
4354
4354
|
return typeof cwd === "string" && cwd.trim() !== "" ? cwd : undefined;
|
|
4355
4355
|
}
|
|
4356
|
+
function sessionHeaderIdOf(agent) {
|
|
4357
|
+
const session = agent?.session;
|
|
4358
|
+
const id = session?.header?.id;
|
|
4359
|
+
return typeof id === "string" && id.trim() !== "" ? id : undefined;
|
|
4360
|
+
}
|
|
4356
4361
|
function actorAgentOf(actor) {
|
|
4357
4362
|
return actor?.agent;
|
|
4358
4363
|
}
|
|
@@ -4377,8 +4382,8 @@ function skillLocalConfig(config) {
|
|
|
4377
4382
|
}
|
|
4378
4383
|
|
|
4379
4384
|
// src/gates/catalog.ts
|
|
4380
|
-
import { existsSync as existsSync14, readFileSync as
|
|
4381
|
-
import { join as
|
|
4385
|
+
import { existsSync as existsSync14, readFileSync as readFileSync11, readdirSync as readdirSync4 } from "node:fs";
|
|
4386
|
+
import { join as join12 } from "node:path";
|
|
4382
4387
|
import { createUserMessage } from "@deepseek-ai/dsh-llm";
|
|
4383
4388
|
|
|
4384
4389
|
// src/gates/agent-flow.ts
|
|
@@ -4655,9 +4660,15 @@ function assignmentHeaderValue(headerRegion, label) {
|
|
|
4655
4660
|
const line = headerRegion.match(bold)?.[1] ?? headerRegion.match(plain)?.[1];
|
|
4656
4661
|
if (line === undefined)
|
|
4657
4662
|
return;
|
|
4658
|
-
const value = line.trim();
|
|
4663
|
+
const value = stripWrappingCodeSpan(line.trim());
|
|
4659
4664
|
return value === "" ? undefined : value;
|
|
4660
4665
|
}
|
|
4666
|
+
function stripWrappingCodeSpan(value) {
|
|
4667
|
+
if (value.length < 2 || !value.startsWith("`") || !value.endsWith("`"))
|
|
4668
|
+
return value;
|
|
4669
|
+
const inner = value.slice(1, -1);
|
|
4670
|
+
return inner.includes("`") ? value : inner.trim();
|
|
4671
|
+
}
|
|
4661
4672
|
function firstToken(value) {
|
|
4662
4673
|
const token = value.split(/\s+/)[0];
|
|
4663
4674
|
return token === "" ? undefined : token;
|
|
@@ -5639,6 +5650,228 @@ function recordTaskSettle(snapshot, pairing) {
|
|
|
5639
5650
|
}
|
|
5640
5651
|
}
|
|
5641
5652
|
|
|
5653
|
+
// src/engine-status-store.ts
|
|
5654
|
+
import { mkdirSync as mkdirSync3, readFileSync as readFileSync8, renameSync as renameSync3, rmSync, writeFileSync as writeFileSync5 } from "node:fs";
|
|
5655
|
+
import { dirname as dirname9, join as join8 } from "node:path";
|
|
5656
|
+
var ENGINE_STATUS_SNAPSHOT_VERSION = 1;
|
|
5657
|
+
var ENGINE_STATUS_SNAPSHOT_ENTRY_VERSION = 1;
|
|
5658
|
+
var ENGINE_STATUS_SNAPSHOT_MAX_PER_SESSION = 50;
|
|
5659
|
+
var ENGINE_STATUS_SNAPSHOT_MAX_AGE_MS = 30 * 24 * 60 * 60 * 1000;
|
|
5660
|
+
var ENGINE_STATUS_SNAPSHOT_MAX_BYTES = 16 * 1024 * 1024;
|
|
5661
|
+
var ENGINE_STATUS_SNAPSHOT_LOCK_TIMEOUT_MS = 250;
|
|
5662
|
+
var ENGINE_STATUS_SNAPSHOT_RELATIVE_PATH = "snapshots/engine-status.json";
|
|
5663
|
+
function engineStatusSnapshotPath(harnessDir) {
|
|
5664
|
+
return join8(harnessDir, ENGINE_STATUS_SNAPSHOT_RELATIVE_PATH);
|
|
5665
|
+
}
|
|
5666
|
+
function engineStatusUnavailable(reason) {
|
|
5667
|
+
return { kind: "unavailable", reason };
|
|
5668
|
+
}
|
|
5669
|
+
function isPlainObject6(value) {
|
|
5670
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
5671
|
+
}
|
|
5672
|
+
function nonEmptyString(value) {
|
|
5673
|
+
return typeof value === "string" && value !== "" ? value : undefined;
|
|
5674
|
+
}
|
|
5675
|
+
function asEntry(value) {
|
|
5676
|
+
if (!isPlainObject6(value))
|
|
5677
|
+
return;
|
|
5678
|
+
if (value.rv !== ENGINE_STATUS_SNAPSHOT_ENTRY_VERSION)
|
|
5679
|
+
return;
|
|
5680
|
+
const cwd = nonEmptyString(value.cwd);
|
|
5681
|
+
const at = nonEmptyString(value.at);
|
|
5682
|
+
if (cwd === undefined || at === undefined)
|
|
5683
|
+
return;
|
|
5684
|
+
if (typeof value.turn !== "number" || !Number.isFinite(value.turn))
|
|
5685
|
+
return;
|
|
5686
|
+
if (!isPlainObject6(value.payload))
|
|
5687
|
+
return;
|
|
5688
|
+
return { rv: value.rv, cwd, at, turn: value.turn, payload: value.payload };
|
|
5689
|
+
}
|
|
5690
|
+
function entryAge(at, nowMs) {
|
|
5691
|
+
const parsed = Date.parse(at);
|
|
5692
|
+
if (!Number.isFinite(parsed))
|
|
5693
|
+
return;
|
|
5694
|
+
return nowMs - parsed;
|
|
5695
|
+
}
|
|
5696
|
+
function readEngineStatusSnapshot(harnessDir, sessionId) {
|
|
5697
|
+
if (harnessDir === null || harnessDir === "")
|
|
5698
|
+
return engineStatusUnavailable("no-harness-dir");
|
|
5699
|
+
if (sessionId === "")
|
|
5700
|
+
return engineStatusUnavailable("no-session-entry");
|
|
5701
|
+
let raw;
|
|
5702
|
+
try {
|
|
5703
|
+
raw = readFileSync8(engineStatusSnapshotPath(harnessDir), "utf8");
|
|
5704
|
+
} catch (error) {
|
|
5705
|
+
const code = error.code;
|
|
5706
|
+
return engineStatusUnavailable(code === "ENOENT" ? "absent" : "unreadable");
|
|
5707
|
+
}
|
|
5708
|
+
let doc;
|
|
5709
|
+
try {
|
|
5710
|
+
doc = JSON.parse(raw);
|
|
5711
|
+
} catch {
|
|
5712
|
+
return engineStatusUnavailable("invalid-json");
|
|
5713
|
+
}
|
|
5714
|
+
if (!isPlainObject6(doc) || doc.sv !== ENGINE_STATUS_SNAPSHOT_VERSION) {
|
|
5715
|
+
return engineStatusUnavailable("envelope-schema");
|
|
5716
|
+
}
|
|
5717
|
+
const entries = doc.entries;
|
|
5718
|
+
if (!isPlainObject6(entries))
|
|
5719
|
+
return engineStatusUnavailable("envelope-schema");
|
|
5720
|
+
if (!Object.hasOwn(entries, sessionId))
|
|
5721
|
+
return engineStatusUnavailable("no-session-entry");
|
|
5722
|
+
const bucket = entries[sessionId];
|
|
5723
|
+
if (!Array.isArray(bucket) || bucket.length === 0)
|
|
5724
|
+
return engineStatusUnavailable("no-session-entry");
|
|
5725
|
+
const entry = asEntry(bucket[bucket.length - 1]);
|
|
5726
|
+
if (entry === undefined)
|
|
5727
|
+
return engineStatusUnavailable("entry-schema");
|
|
5728
|
+
return { kind: "ok", entry };
|
|
5729
|
+
}
|
|
5730
|
+
function sessionMap() {
|
|
5731
|
+
return Object.create(null);
|
|
5732
|
+
}
|
|
5733
|
+
function loadForWrite(harnessDir) {
|
|
5734
|
+
let raw;
|
|
5735
|
+
try {
|
|
5736
|
+
raw = readFileSync8(engineStatusSnapshotPath(harnessDir), "utf8");
|
|
5737
|
+
} catch (error) {
|
|
5738
|
+
if (error.code === "ENOENT") {
|
|
5739
|
+
return { kind: "ok", doc: { sv: ENGINE_STATUS_SNAPSHOT_VERSION, entries: sessionMap() } };
|
|
5740
|
+
}
|
|
5741
|
+
return { kind: "refused", reason: "store-unreadable" };
|
|
5742
|
+
}
|
|
5743
|
+
let parsed;
|
|
5744
|
+
try {
|
|
5745
|
+
parsed = JSON.parse(raw);
|
|
5746
|
+
} catch {
|
|
5747
|
+
return { kind: "refused", reason: "store-invalid-json" };
|
|
5748
|
+
}
|
|
5749
|
+
if (!isPlainObject6(parsed) || parsed.sv !== ENGINE_STATUS_SNAPSHOT_VERSION || !isPlainObject6(parsed.entries)) {
|
|
5750
|
+
return { kind: "refused", reason: "store-envelope-schema" };
|
|
5751
|
+
}
|
|
5752
|
+
const entries = sessionMap();
|
|
5753
|
+
for (const [key, value] of Object.entries(parsed.entries)) {
|
|
5754
|
+
if (!Array.isArray(value))
|
|
5755
|
+
return { kind: "refused", reason: "store-entry-schema" };
|
|
5756
|
+
const kept = [];
|
|
5757
|
+
for (const candidate of value) {
|
|
5758
|
+
const entry = asEntry(candidate);
|
|
5759
|
+
if (entry === undefined)
|
|
5760
|
+
return { kind: "refused", reason: "store-entry-schema" };
|
|
5761
|
+
kept.push(entry);
|
|
5762
|
+
}
|
|
5763
|
+
if (kept.length > 0)
|
|
5764
|
+
entries[key] = kept;
|
|
5765
|
+
}
|
|
5766
|
+
return { kind: "ok", doc: { sv: ENGINE_STATUS_SNAPSHOT_VERSION, entries } };
|
|
5767
|
+
}
|
|
5768
|
+
function pruneBucket(bucket, nowMs) {
|
|
5769
|
+
const fresh = [];
|
|
5770
|
+
for (const entry of bucket) {
|
|
5771
|
+
const age = entryAge(entry.at, nowMs);
|
|
5772
|
+
if (age === undefined || age > ENGINE_STATUS_SNAPSHOT_MAX_AGE_MS)
|
|
5773
|
+
continue;
|
|
5774
|
+
fresh.push(entry);
|
|
5775
|
+
}
|
|
5776
|
+
if (fresh.length <= ENGINE_STATUS_SNAPSHOT_MAX_PER_SESSION)
|
|
5777
|
+
return fresh;
|
|
5778
|
+
return fresh.slice(fresh.length - ENGINE_STATUS_SNAPSHOT_MAX_PER_SESSION);
|
|
5779
|
+
}
|
|
5780
|
+
function bucketRecency(bucket) {
|
|
5781
|
+
let newest = 0;
|
|
5782
|
+
for (const entry of bucket) {
|
|
5783
|
+
const parsed = Date.parse(entry.at);
|
|
5784
|
+
if (Number.isFinite(parsed) && parsed > newest)
|
|
5785
|
+
newest = parsed;
|
|
5786
|
+
}
|
|
5787
|
+
return newest;
|
|
5788
|
+
}
|
|
5789
|
+
function enforceGlobalBound(pruned, keep, maxBytes, sizeOf) {
|
|
5790
|
+
if (sizeOf(pruned) <= maxBytes)
|
|
5791
|
+
return 0;
|
|
5792
|
+
const candidates = Object.keys(pruned).filter((key) => key !== keep).map((key) => ({ key, recency: bucketRecency(pruned[key]) })).sort((left, right) => left.recency - right.recency);
|
|
5793
|
+
let evicted = 0;
|
|
5794
|
+
for (const candidate of candidates) {
|
|
5795
|
+
if (sizeOf(pruned) <= maxBytes)
|
|
5796
|
+
break;
|
|
5797
|
+
delete pruned[candidate.key];
|
|
5798
|
+
evicted += 1;
|
|
5799
|
+
}
|
|
5800
|
+
return evicted;
|
|
5801
|
+
}
|
|
5802
|
+
var oversizeWarned = new Set;
|
|
5803
|
+
function writeEngineStatusSnapshot(harnessDir, input) {
|
|
5804
|
+
if (harnessDir === null || harnessDir === "")
|
|
5805
|
+
return { kind: "degraded", reason: "no-harness-dir" };
|
|
5806
|
+
if (input.sessionId === "")
|
|
5807
|
+
return { kind: "degraded", reason: "no-session-id" };
|
|
5808
|
+
if (input.cwd === "")
|
|
5809
|
+
return { kind: "degraded", reason: "no-session-cwd" };
|
|
5810
|
+
if (!Number.isFinite(input.turn))
|
|
5811
|
+
return { kind: "degraded", reason: "invalid-turn" };
|
|
5812
|
+
if (!isPlainObject6(input.payload))
|
|
5813
|
+
return { kind: "degraded", reason: "payload-not-object" };
|
|
5814
|
+
const at = (input.now ?? new Date).toISOString();
|
|
5815
|
+
if (!Number.isFinite(Date.parse(at)))
|
|
5816
|
+
return { kind: "degraded", reason: "invalid-timestamp" };
|
|
5817
|
+
const dir = dirname9(engineStatusSnapshotPath(harnessDir));
|
|
5818
|
+
const file = engineStatusSnapshotPath(harnessDir);
|
|
5819
|
+
const tmp = `${file}.${process.pid}.${Math.random().toString(36).slice(2, 10)}.tmp`;
|
|
5820
|
+
try {
|
|
5821
|
+
mkdirSync3(dir, { recursive: true });
|
|
5822
|
+
return withWorkflowDirLock(dir, () => {
|
|
5823
|
+
const loaded = loadForWrite(harnessDir);
|
|
5824
|
+
if (loaded.kind === "refused")
|
|
5825
|
+
return { kind: "degraded", reason: loaded.reason };
|
|
5826
|
+
const doc = loaded.doc;
|
|
5827
|
+
const nowMs = Date.parse(at);
|
|
5828
|
+
const bucket = pruneBucket(doc.entries[input.sessionId] ?? [], nowMs);
|
|
5829
|
+
bucket.push({
|
|
5830
|
+
rv: ENGINE_STATUS_SNAPSHOT_ENTRY_VERSION,
|
|
5831
|
+
cwd: input.cwd,
|
|
5832
|
+
at,
|
|
5833
|
+
turn: input.turn,
|
|
5834
|
+
payload: input.payload
|
|
5835
|
+
});
|
|
5836
|
+
doc.entries[input.sessionId] = pruneBucket(bucket, nowMs);
|
|
5837
|
+
const pruned = sessionMap();
|
|
5838
|
+
for (const [key, value] of Object.entries(doc.entries)) {
|
|
5839
|
+
const kept = pruneBucket(value, nowMs);
|
|
5840
|
+
if (kept.length === 0)
|
|
5841
|
+
continue;
|
|
5842
|
+
pruned[key] = kept;
|
|
5843
|
+
}
|
|
5844
|
+
const maxBytes = input.maxBytes ?? ENGINE_STATUS_SNAPSHOT_MAX_BYTES;
|
|
5845
|
+
const serialized = (entries) => JSON.stringify({ sv: ENGINE_STATUS_SNAPSHOT_VERSION, entries });
|
|
5846
|
+
const evicted = enforceGlobalBound(pruned, input.sessionId, maxBytes, (entries) => Buffer.byteLength(serialized(entries), "utf8"));
|
|
5847
|
+
const payload = serialized(pruned);
|
|
5848
|
+
const oversized = Buffer.byteLength(payload, "utf8") > maxBytes;
|
|
5849
|
+
let total = 0;
|
|
5850
|
+
for (const kept of Object.values(pruned))
|
|
5851
|
+
total += kept.length;
|
|
5852
|
+
writeFileSync5(tmp, payload);
|
|
5853
|
+
renameSync3(tmp, file);
|
|
5854
|
+
let warn;
|
|
5855
|
+
if (oversized && !oversizeWarned.has(file)) {
|
|
5856
|
+
oversizeWarned.add(file);
|
|
5857
|
+
warn = `engine-status snapshot store ${file} is above its ${maxBytes}-byte ceiling ` + `(kept ${total} entries, shed ${evicted} oldest session bucket(s) this write); ` + "recovery: delete the file — it is prunable plugin-owned state and readers answer unavailable for it";
|
|
5858
|
+
}
|
|
5859
|
+
return {
|
|
5860
|
+
kind: "written",
|
|
5861
|
+
path: file,
|
|
5862
|
+
entries: total,
|
|
5863
|
+
evicted,
|
|
5864
|
+
...warn === undefined ? {} : { warn }
|
|
5865
|
+
};
|
|
5866
|
+
}, { timeoutMs: ENGINE_STATUS_SNAPSHOT_LOCK_TIMEOUT_MS });
|
|
5867
|
+
} catch (error) {
|
|
5868
|
+
try {
|
|
5869
|
+
rmSync(tmp, { force: true });
|
|
5870
|
+
} catch {}
|
|
5871
|
+
return { kind: "degraded", reason: error?.message ?? "write-failed" };
|
|
5872
|
+
}
|
|
5873
|
+
}
|
|
5874
|
+
|
|
5642
5875
|
// src/gates/catalog.ts
|
|
5643
5876
|
var CATALOG_LOGGER = "mstar/engine-status-catalog";
|
|
5644
5877
|
var DEFAULT_CATALOG_TTL_MS = 60000;
|
|
@@ -5655,18 +5888,19 @@ function residualSeverityIndex(severity) {
|
|
|
5655
5888
|
function pluginVersion() {
|
|
5656
5889
|
for (const rel of ["../package.json", "../../package.json"]) {
|
|
5657
5890
|
try {
|
|
5658
|
-
const pkg = JSON.parse(
|
|
5891
|
+
const pkg = JSON.parse(readFileSync11(new URL(rel, import.meta.url), "utf8"));
|
|
5659
5892
|
if (typeof pkg.version === "string" && pkg.version !== "")
|
|
5660
5893
|
return pkg.version;
|
|
5661
5894
|
} catch {}
|
|
5662
5895
|
}
|
|
5663
5896
|
return "0.0.0";
|
|
5664
5897
|
}
|
|
5665
|
-
function engineStatusSource(
|
|
5898
|
+
function engineStatusSource() {
|
|
5899
|
+
return { kind: "plugin", plugin: "mstar-engine-status", form: "catalog" };
|
|
5900
|
+
}
|
|
5901
|
+
function engineStatusPayload(harnessDir) {
|
|
5666
5902
|
const iteration = harnessDir !== null ? iterationGateSource(harnessDir) : undefined;
|
|
5667
5903
|
return {
|
|
5668
|
-
kind: "mstar-engine-status",
|
|
5669
|
-
form: "catalog",
|
|
5670
5904
|
version: pluginVersion(),
|
|
5671
5905
|
harnessDir,
|
|
5672
5906
|
enforcement: harnessDir !== null ? resolveRepoEnforcement(harnessDir) : { hard: false, source: "none" },
|
|
@@ -5690,23 +5924,23 @@ function createCatalogInvalidation(cache) {
|
|
|
5690
5924
|
}
|
|
5691
5925
|
};
|
|
5692
5926
|
}
|
|
5693
|
-
function
|
|
5694
|
-
const
|
|
5695
|
-
if (
|
|
5927
|
+
function buildCatalogPayload(ctx, harnessDir) {
|
|
5928
|
+
const payload = engineStatusPayload(harnessDir);
|
|
5929
|
+
if (payload.version === "0.0.0") {
|
|
5696
5930
|
ctx.logger(CATALOG_LOGGER).warn("plugin manifest version unavailable — falling back to 0.0.0 for the engine-status catalog watermark");
|
|
5697
5931
|
}
|
|
5698
|
-
return
|
|
5932
|
+
return payload;
|
|
5699
5933
|
}
|
|
5700
|
-
function
|
|
5934
|
+
function catalogPayloadFor(ctx, cache, register, key, harnessDir, ttlMs) {
|
|
5701
5935
|
const entry = cache.get(key);
|
|
5702
5936
|
if (entry !== undefined && Date.now() - entry.builtAt < ttlMs) {
|
|
5703
5937
|
register(harnessDir, key);
|
|
5704
|
-
return entry.
|
|
5938
|
+
return entry.payload;
|
|
5705
5939
|
}
|
|
5706
|
-
const
|
|
5707
|
-
cache.set(key, {
|
|
5940
|
+
const payload = buildCatalogPayload(ctx, harnessDir);
|
|
5941
|
+
cache.set(key, { payload, builtAt: Date.now() });
|
|
5708
5942
|
register(harnessDir, key);
|
|
5709
|
-
return
|
|
5943
|
+
return payload;
|
|
5710
5944
|
}
|
|
5711
5945
|
function renderEngineStatusCatalog(source) {
|
|
5712
5946
|
const enforcement = `${source.enforcement.hard ? "hard" : "soft"}${source.enforcement.source === "none" ? "" : ` (${source.enforcement.source})`}`;
|
|
@@ -5784,7 +6018,7 @@ function hhmm(ts) {
|
|
|
5784
6018
|
function harnessStateSource(harnessDir) {
|
|
5785
6019
|
if (harnessDir === null)
|
|
5786
6020
|
return null;
|
|
5787
|
-
const statusPath =
|
|
6021
|
+
const statusPath = join12(harnessDir, STATUS_FILE);
|
|
5788
6022
|
if (!existsSync14(statusPath))
|
|
5789
6023
|
return null;
|
|
5790
6024
|
try {
|
|
@@ -5804,7 +6038,7 @@ function harnessStateSource(harnessDir) {
|
|
|
5804
6038
|
if (selection.kind === "error") {
|
|
5805
6039
|
return selectionErrorState(selection, rollup, harnessDir, compass);
|
|
5806
6040
|
}
|
|
5807
|
-
const snapshotPath =
|
|
6041
|
+
const snapshotPath = join12(harnessDir, selection.dir, WORKFLOW_SNAPSHOT_FILE);
|
|
5808
6042
|
let snapshot;
|
|
5809
6043
|
if (!existsSync14(snapshotPath)) {
|
|
5810
6044
|
return selectionErrorState({
|
|
@@ -5868,7 +6102,7 @@ function harnessStateSource(harnessDir) {
|
|
|
5868
6102
|
leases,
|
|
5869
6103
|
knowledge: knowledgeDigest(harnessDir),
|
|
5870
6104
|
direction: compass !== undefined ? compassDirection(compass.compassPath) : null,
|
|
5871
|
-
agentFlow: readAgentFlow(
|
|
6105
|
+
agentFlow: readAgentFlow(join12(harnessDir, selection.dir), 50)
|
|
5872
6106
|
};
|
|
5873
6107
|
} catch {
|
|
5874
6108
|
return null;
|
|
@@ -5908,11 +6142,11 @@ function projectRollupSource(harnessDir, residuals) {
|
|
|
5908
6142
|
for (const entry of entries) {
|
|
5909
6143
|
if (!entry.isDirectory())
|
|
5910
6144
|
continue;
|
|
5911
|
-
const roadmapPath =
|
|
6145
|
+
const roadmapPath = join12(projectsDir, entry.name, PROJECT_ROADMAP_FILE);
|
|
5912
6146
|
if (!existsSync14(roadmapPath))
|
|
5913
6147
|
continue;
|
|
5914
6148
|
try {
|
|
5915
|
-
const doc = parseCompassFrontmatterText(
|
|
6149
|
+
const doc = parseCompassFrontmatterText(readFileSync11(roadmapPath, "utf8"), roadmapPath);
|
|
5916
6150
|
if (Array.isArray(doc.milestones)) {
|
|
5917
6151
|
for (const milestone of doc.milestones) {
|
|
5918
6152
|
if (typeof milestone === "string" && milestone.trim() !== "")
|
|
@@ -5986,7 +6220,7 @@ function projectRegisters(harnessDir) {
|
|
|
5986
6220
|
for (const entry of entries) {
|
|
5987
6221
|
if (!entry.isDirectory())
|
|
5988
6222
|
continue;
|
|
5989
|
-
const registerPath =
|
|
6223
|
+
const registerPath = join12(projectsDir, entry.name, PROJECT_REGISTER_FILE);
|
|
5990
6224
|
if (!existsSync14(registerPath))
|
|
5991
6225
|
continue;
|
|
5992
6226
|
try {
|
|
@@ -5998,13 +6232,13 @@ function projectRegisters(harnessDir) {
|
|
|
5998
6232
|
return registers;
|
|
5999
6233
|
}
|
|
6000
6234
|
function knowledgeDigest(harnessDir) {
|
|
6001
|
-
const indexPath =
|
|
6235
|
+
const indexPath = join12(harnessDir, "knowledge", "README.md");
|
|
6002
6236
|
if (!existsSync14(indexPath))
|
|
6003
6237
|
return null;
|
|
6004
6238
|
try {
|
|
6005
6239
|
const categories = new Set;
|
|
6006
6240
|
let docCount = 0;
|
|
6007
|
-
for (const line of
|
|
6241
|
+
for (const line of readFileSync11(indexPath, "utf8").split(/\r?\n/)) {
|
|
6008
6242
|
const row = line.trim().match(/^\|(.+)\|$/);
|
|
6009
6243
|
if (row === null)
|
|
6010
6244
|
continue;
|
|
@@ -6027,7 +6261,7 @@ function knowledgeDigest(harnessDir) {
|
|
|
6027
6261
|
}
|
|
6028
6262
|
function compassDirection(compassPath) {
|
|
6029
6263
|
try {
|
|
6030
|
-
const content =
|
|
6264
|
+
const content = readFileSync11(compassPath, "utf8");
|
|
6031
6265
|
const section = content.match(/^## Direction lock[^\n]*\n+([\s\S]*?)(?=\n## |$)/m);
|
|
6032
6266
|
if (section === null)
|
|
6033
6267
|
return null;
|
|
@@ -6055,12 +6289,12 @@ function steeringCompassPath(harnessDir) {
|
|
|
6055
6289
|
for (const entry of entries) {
|
|
6056
6290
|
if (!entry.isDirectory())
|
|
6057
6291
|
continue;
|
|
6058
|
-
const compassPath =
|
|
6292
|
+
const compassPath = join12(iterationsDir, entry.name, "delivery-compass.md");
|
|
6059
6293
|
if (!existsSync14(compassPath))
|
|
6060
6294
|
continue;
|
|
6061
6295
|
let content;
|
|
6062
6296
|
try {
|
|
6063
|
-
content =
|
|
6297
|
+
content = readFileSync11(compassPath, "utf8");
|
|
6064
6298
|
} catch {
|
|
6065
6299
|
continue;
|
|
6066
6300
|
}
|
|
@@ -6074,7 +6308,7 @@ function steeringCompassPath(harnessDir) {
|
|
|
6074
6308
|
function iterationGateSource(harnessDir) {
|
|
6075
6309
|
if (harnessDir === null)
|
|
6076
6310
|
return;
|
|
6077
|
-
const statusPath =
|
|
6311
|
+
const statusPath = join12(harnessDir, STATUS_FILE);
|
|
6078
6312
|
if (!existsSync14(statusPath))
|
|
6079
6313
|
return;
|
|
6080
6314
|
const compass = steeringCompassPath(harnessDir);
|
|
@@ -6083,7 +6317,7 @@ function iterationGateSource(harnessDir) {
|
|
|
6083
6317
|
const selection = resolveReadWorkflow(harnessDir);
|
|
6084
6318
|
if (selection.kind === "error")
|
|
6085
6319
|
return;
|
|
6086
|
-
const snapshotPath =
|
|
6320
|
+
const snapshotPath = join12(harnessDir, selection.dir, WORKFLOW_SNAPSHOT_FILE);
|
|
6087
6321
|
try {
|
|
6088
6322
|
const snapshotDoc = readJson(snapshotPath);
|
|
6089
6323
|
const compassDoc = parseCompassFrontmatter(compass.compassPath);
|
|
@@ -6116,13 +6350,14 @@ async function preStepCatalogListener(ctx, resolver, explicitKey, cache, ttlMs,
|
|
|
6116
6350
|
const cwd = sessionCwdOf(payload.agent);
|
|
6117
6351
|
const harnessDir = resolver.forWorkspace(cwd);
|
|
6118
6352
|
const key = explicitKey ?? cwd ?? "";
|
|
6119
|
-
const
|
|
6353
|
+
const catalogPayload = catalogPayloadFor(ctx, cache, register, key, harnessDir, ttlMs);
|
|
6120
6354
|
const messages = [...decision.messages];
|
|
6121
|
-
const text = renderEngineStatusCatalog(
|
|
6355
|
+
const text = renderEngineStatusCatalog(catalogPayload);
|
|
6122
6356
|
const digestKey = agentDigestKey(payload.agent, cwd);
|
|
6123
6357
|
const prior = digests.get(digestKey);
|
|
6124
6358
|
if (prior === undefined || prior.turn !== payload.turn || prior.text !== text) {
|
|
6125
|
-
messages.push(createUserMessage({ source:
|
|
6359
|
+
messages.push(createUserMessage({ source: engineStatusSource(), content: [{ type: "text", text }] }));
|
|
6360
|
+
persistEngineStatusSnapshot(ctx, harnessDir, cwd, payload.turn, catalogPayload, payload.agent);
|
|
6126
6361
|
}
|
|
6127
6362
|
digests.set(digestKey, { turn: payload.turn, text });
|
|
6128
6363
|
return { kind: "enter", messages };
|
|
@@ -6135,10 +6370,208 @@ function agentDigestKey(agent, cwd) {
|
|
|
6135
6370
|
const id = agent?.id;
|
|
6136
6371
|
return `${typeof id === "string" ? id : "<unknown>"}\x00${cwd ?? ""}`;
|
|
6137
6372
|
}
|
|
6373
|
+
function persistEngineStatusSnapshot(ctx, harnessDir, cwd, turn, catalogPayload, agent) {
|
|
6374
|
+
const sessionId = sessionHeaderIdOf(agent);
|
|
6375
|
+
if (sessionId === undefined || cwd === undefined)
|
|
6376
|
+
return;
|
|
6377
|
+
if (harnessDir === null)
|
|
6378
|
+
return;
|
|
6379
|
+
const result = writeEngineStatusSnapshot(harnessDir, {
|
|
6380
|
+
sessionId,
|
|
6381
|
+
cwd,
|
|
6382
|
+
turn,
|
|
6383
|
+
payload: catalogPayload
|
|
6384
|
+
});
|
|
6385
|
+
if (result.kind === "degraded") {
|
|
6386
|
+
ctx.logger(CATALOG_LOGGER).warn(`engine-status snapshot not persisted for ${sessionId} (readers answer unavailable): ${result.reason}`);
|
|
6387
|
+
return;
|
|
6388
|
+
}
|
|
6389
|
+
if (result.warn !== undefined)
|
|
6390
|
+
ctx.logger(CATALOG_LOGGER).warn(result.warn);
|
|
6391
|
+
}
|
|
6392
|
+
|
|
6393
|
+
// src/engine-status-endpoint.ts
|
|
6394
|
+
import { isAbsolute as isAbsolute3 } from "node:path";
|
|
6395
|
+
|
|
6396
|
+
// ../../node_modules/.bun/@deepseek-ai+dsh-typert-protocol@0.1.5-rc.1+1e4e7adc57ac8890/node_modules/@deepseek-ai/dsh-typert-protocol/lib/index.js
|
|
6397
|
+
import { Service as Service2 } from "@deepseek-ai/cordis";
|
|
6398
|
+
var TYPERT_REMOTE_SEGMENT_PATTERN = /^[A-Za-z0-9_$.-]+$/;
|
|
6399
|
+
function isTypertRemoteSegment(value) {
|
|
6400
|
+
return value !== "." && value !== ".." && TYPERT_REMOTE_SEGMENT_PATTERN.test(value);
|
|
6401
|
+
}
|
|
6402
|
+
function bindTypertRemote(service, serviceKey, options = {}) {
|
|
6403
|
+
validateName("service key", serviceKey);
|
|
6404
|
+
const namespace = options.namespace ?? serviceKey;
|
|
6405
|
+
validateName("namespace", namespace);
|
|
6406
|
+
return Object.freeze({
|
|
6407
|
+
service,
|
|
6408
|
+
serviceKey,
|
|
6409
|
+
namespace
|
|
6410
|
+
});
|
|
6411
|
+
}
|
|
6412
|
+
var TypertRemoteService = class extends Service2 {
|
|
6413
|
+
typertRemote;
|
|
6414
|
+
constructor(ctx, serviceKey, options = {}) {
|
|
6415
|
+
super(ctx, serviceKey);
|
|
6416
|
+
this.typertRemote = bindTypertRemote(this, this.name, options);
|
|
6417
|
+
}
|
|
6418
|
+
};
|
|
6419
|
+
function validateName(subject, value) {
|
|
6420
|
+
if (!isTypertRemoteSegment(value))
|
|
6421
|
+
throw new TypeError(`typert-protocol: ${subject} must contain only RPC endpoint segment characters`);
|
|
6422
|
+
}
|
|
6423
|
+
|
|
6424
|
+
// src/engine-status-wire.ts
|
|
6425
|
+
var MSTAR_ENGINE_STATUS_NAMESPACE = "mstar";
|
|
6426
|
+
var MSTAR_ENGINE_STATUS_METHOD = "engineStatus";
|
|
6427
|
+
|
|
6428
|
+
// src/engine-status-endpoint.ts
|
|
6429
|
+
var CONTRIBUTING_PACKAGE = "@mstar-harness/dsh";
|
|
6430
|
+
var ENDPOINT_LOGGER = "mstar-engine-status-endpoint";
|
|
6431
|
+
function unavailable(reason) {
|
|
6432
|
+
return { status: "unavailable", reason };
|
|
6433
|
+
}
|
|
6434
|
+
function nonEmptyString2(value) {
|
|
6435
|
+
return typeof value === "string" && value.trim() !== "" ? value : undefined;
|
|
6436
|
+
}
|
|
6437
|
+
function headerCwdOf(value) {
|
|
6438
|
+
const header = value?.header;
|
|
6439
|
+
return nonEmptyString2(header?.cwd);
|
|
6440
|
+
}
|
|
6441
|
+
function isRefusedPath(cwd) {
|
|
6442
|
+
if (cwd.includes("\x00"))
|
|
6443
|
+
return true;
|
|
6444
|
+
if (!isAbsolute3(cwd))
|
|
6445
|
+
return true;
|
|
6446
|
+
return cwd.split(/[\\/]+/).includes("..");
|
|
6447
|
+
}
|
|
6448
|
+
|
|
6449
|
+
class MstarEngineStatusGateway extends TypertRemoteService {
|
|
6450
|
+
resolver;
|
|
6451
|
+
bootHarnessDir;
|
|
6452
|
+
constructor(ctx, options) {
|
|
6453
|
+
super(ctx, MSTAR_ENGINE_STATUS_NAMESPACE);
|
|
6454
|
+
this.resolver = options.resolver;
|
|
6455
|
+
this.bootHarnessDir = options.bootHarnessDir;
|
|
6456
|
+
}
|
|
6457
|
+
async engineStatus(sessionId, cwd) {
|
|
6458
|
+
try {
|
|
6459
|
+
return await this.serve(sessionId, cwd);
|
|
6460
|
+
} catch (error) {
|
|
6461
|
+
this.ctx.logger(ENDPOINT_LOGGER).warn(`engineStatus failed for ${String(sessionId)} (answering unavailable): ${error?.message ?? error}`);
|
|
6462
|
+
return unavailable("internal-error");
|
|
6463
|
+
}
|
|
6464
|
+
}
|
|
6465
|
+
async serve(sessionId, cwd) {
|
|
6466
|
+
const sid = nonEmptyString2(sessionId);
|
|
6467
|
+
if (sid === undefined)
|
|
6468
|
+
return unavailable("invalid-session-id");
|
|
6469
|
+
const claimed = nonEmptyString2(cwd);
|
|
6470
|
+
if (claimed === undefined)
|
|
6471
|
+
return unavailable("invalid-cwd");
|
|
6472
|
+
if (isRefusedPath(claimed))
|
|
6473
|
+
return unavailable("cwd-refused");
|
|
6474
|
+
const session = await this.resolveSessionCwd(sid);
|
|
6475
|
+
if (session.kind !== "ok")
|
|
6476
|
+
return unavailable(session.reason);
|
|
6477
|
+
const harnessDir = this.bootHarnessDir ?? this.resolver.forWorkspace(session.cwd);
|
|
6478
|
+
if (harnessDir === null || harnessDir === "")
|
|
6479
|
+
return unavailable("no-harness-dir");
|
|
6480
|
+
const read = readEngineStatusSnapshot(harnessDir, sid);
|
|
6481
|
+
if (read.kind !== "ok")
|
|
6482
|
+
return unavailable(`store-${read.reason}`);
|
|
6483
|
+
const entry = read.entry;
|
|
6484
|
+
if (entry.cwd !== claimed)
|
|
6485
|
+
return unavailable("cwd-mismatch");
|
|
6486
|
+
if (entry.cwd !== session.cwd)
|
|
6487
|
+
return unavailable("session-cwd-mismatch");
|
|
6488
|
+
return {
|
|
6489
|
+
status: "ok",
|
|
6490
|
+
sessionId: sid,
|
|
6491
|
+
cwd: entry.cwd,
|
|
6492
|
+
at: entry.at,
|
|
6493
|
+
turn: entry.turn,
|
|
6494
|
+
payload: entry.payload
|
|
6495
|
+
};
|
|
6496
|
+
}
|
|
6497
|
+
async resolveSessionCwd(sessionId) {
|
|
6498
|
+
const sessions = this.ctx.get("sessions");
|
|
6499
|
+
const live = typeof sessions?.get === "function" ? sessions.get(sessionId) : undefined;
|
|
6500
|
+
const liveCwd = headerCwdOf(live);
|
|
6501
|
+
if (liveCwd !== undefined)
|
|
6502
|
+
return { kind: "ok", cwd: liveCwd, source: "live" };
|
|
6503
|
+
const controller = this.ctx.get("sessionController");
|
|
6504
|
+
if (typeof controller?.inspect !== "function") {
|
|
6505
|
+
return { kind: "unavailable", reason: live === undefined ? "session-absent" : "session-cwd-absent" };
|
|
6506
|
+
}
|
|
6507
|
+
let inspection;
|
|
6508
|
+
try {
|
|
6509
|
+
inspection = await controller.inspect(sessionId);
|
|
6510
|
+
} catch {
|
|
6511
|
+
return { kind: "unavailable", reason: "session-inspect-failed" };
|
|
6512
|
+
}
|
|
6513
|
+
const persistedCwd = headerCwdOf(inspection);
|
|
6514
|
+
if (persistedCwd === undefined)
|
|
6515
|
+
return { kind: "unavailable", reason: "session-cwd-absent" };
|
|
6516
|
+
return { kind: "ok", cwd: persistedCwd, source: "persisted" };
|
|
6517
|
+
}
|
|
6518
|
+
}
|
|
6519
|
+
function mstarEngineStatusContribution() {
|
|
6520
|
+
return {
|
|
6521
|
+
package: CONTRIBUTING_PACKAGE,
|
|
6522
|
+
face: "host",
|
|
6523
|
+
schemas: [],
|
|
6524
|
+
model: { services: [], events: [], objects: [] },
|
|
6525
|
+
invocations: [
|
|
6526
|
+
{
|
|
6527
|
+
id: `${CONTRIBUTING_PACKAGE}#${MSTAR_ENGINE_STATUS_NAMESPACE}/${MSTAR_ENGINE_STATUS_METHOD}`,
|
|
6528
|
+
service: MSTAR_ENGINE_STATUS_NAMESPACE,
|
|
6529
|
+
namespace: MSTAR_ENGINE_STATUS_NAMESPACE,
|
|
6530
|
+
method: MSTAR_ENGINE_STATUS_METHOD,
|
|
6531
|
+
invocation: { kind: "direct" },
|
|
6532
|
+
parameters: [
|
|
6533
|
+
{ name: "sessionId", wire: "sessionId", source: "json", codec: { mode: "src-json" } },
|
|
6534
|
+
{ name: "cwd", wire: "cwd", source: "json", codec: { mode: "src-json" } }
|
|
6535
|
+
],
|
|
6536
|
+
result: { mode: "src-json" }
|
|
6537
|
+
}
|
|
6538
|
+
]
|
|
6539
|
+
};
|
|
6540
|
+
}
|
|
6541
|
+
function engineStatusServicePresent(ctx) {
|
|
6542
|
+
try {
|
|
6543
|
+
return ctx.get(MSTAR_ENGINE_STATUS_NAMESPACE) !== undefined;
|
|
6544
|
+
} catch {
|
|
6545
|
+
return false;
|
|
6546
|
+
}
|
|
6547
|
+
}
|
|
6548
|
+
function installEngineStatusEndpoint(ctx, options) {
|
|
6549
|
+
if (engineStatusServicePresent(ctx)) {
|
|
6550
|
+
ctx.logger(ENDPOINT_LOGGER).debug("mstar engine-status gateway already registered — kept as-is (multi-fiber dedupe)");
|
|
6551
|
+
} else {
|
|
6552
|
+
try {
|
|
6553
|
+
new MstarEngineStatusGateway(ctx, options);
|
|
6554
|
+
} catch (error) {
|
|
6555
|
+
if (!(error instanceof Error) || !error.message.includes("has been registered"))
|
|
6556
|
+
throw error;
|
|
6557
|
+
ctx.logger(ENDPOINT_LOGGER).debug("mstar engine-status gateway registered concurrently — kept the first (multi-fiber dedupe)");
|
|
6558
|
+
}
|
|
6559
|
+
}
|
|
6560
|
+
ctx.inject(["typert"], (tctx) => {
|
|
6561
|
+
try {
|
|
6562
|
+
return tctx.typert.register(mstarEngineStatusContribution());
|
|
6563
|
+
} catch (error) {
|
|
6564
|
+
if (!(error instanceof Error) || !error.message.includes("already registered"))
|
|
6565
|
+
throw error;
|
|
6566
|
+
tctx.logger(ENDPOINT_LOGGER).debug("mstar engine-status endpoints registered concurrently — none on this fiber (multi-fiber dedupe)");
|
|
6567
|
+
return () => {};
|
|
6568
|
+
}
|
|
6569
|
+
});
|
|
6570
|
+
}
|
|
6138
6571
|
|
|
6139
6572
|
// src/gates/status.ts
|
|
6140
6573
|
import { existsSync as existsSync15, readdirSync as readdirSync7 } from "node:fs";
|
|
6141
|
-
import { basename as basename5, join as
|
|
6574
|
+
import { basename as basename5, join as join14, relative as relative7, resolve as resolve10 } from "node:path";
|
|
6142
6575
|
var LOGGER_NAME = "mstar/status-gate";
|
|
6143
6576
|
function harnessDocKindOfTarget(harnessDir, targetPath) {
|
|
6144
6577
|
const resolved = resolve10(targetPath);
|
|
@@ -6154,8 +6587,8 @@ function harnessDocKindOfTarget(harnessDir, targetPath) {
|
|
|
6154
6587
|
workflowDir = resolveWorkflowDir(harnessDir, { harnessDir });
|
|
6155
6588
|
projectDir = resolveProjectDir(harnessDir, { harnessDir });
|
|
6156
6589
|
} catch {
|
|
6157
|
-
workflowDir =
|
|
6158
|
-
projectDir =
|
|
6590
|
+
workflowDir = join14(harnessDir, "workflows");
|
|
6591
|
+
projectDir = join14(harnessDir, "projects");
|
|
6159
6592
|
}
|
|
6160
6593
|
if (name === WORKFLOW_SNAPSHOT_FILE && /^[^/]+\/snapshot\.json$/.test(relative7(workflowDir, resolved)))
|
|
6161
6594
|
return "snapshot";
|
|
@@ -6219,7 +6652,7 @@ function projectRegisterDocs(harnessDir) {
|
|
|
6219
6652
|
for (const entry of entries) {
|
|
6220
6653
|
if (!entry.isDirectory())
|
|
6221
6654
|
continue;
|
|
6222
|
-
const registerPath =
|
|
6655
|
+
const registerPath = join14(projectsDir, entry.name, PROJECT_REGISTER_FILE);
|
|
6223
6656
|
if (!existsSync15(registerPath))
|
|
6224
6657
|
continue;
|
|
6225
6658
|
try {
|
|
@@ -6293,8 +6726,8 @@ async function editIntentListener(ctx, resolver, config, adapter, target, actor,
|
|
|
6293
6726
|
}
|
|
6294
6727
|
|
|
6295
6728
|
// src/gates/skill-lint.ts
|
|
6296
|
-
import { existsSync as existsSync16, readFileSync as
|
|
6297
|
-
import { basename as basename8, dirname as
|
|
6729
|
+
import { existsSync as existsSync16, readFileSync as readFileSync13 } from "node:fs";
|
|
6730
|
+
import { basename as basename8, dirname as dirname10, resolve as resolve12, sep as sep2 } from "node:path";
|
|
6298
6731
|
var SKILL_LINT_LOGGER = "mstar/skill-lint";
|
|
6299
6732
|
|
|
6300
6733
|
class SkillLintVetoError extends Error {
|
|
@@ -6335,7 +6768,7 @@ function lintSkillDoc(doc, options = {}) {
|
|
|
6335
6768
|
return violations.length === 0 ? { ok: true, violations } : { ok: false, violations };
|
|
6336
6769
|
}
|
|
6337
6770
|
function lintSkillWrite(doc, options) {
|
|
6338
|
-
const skillId = basename8(
|
|
6771
|
+
const skillId = basename8(dirname10(resolve12(options.target)));
|
|
6339
6772
|
const result = lintSkillDoc(doc, { skillId });
|
|
6340
6773
|
if (options.hard && !result.ok) {
|
|
6341
6774
|
throw new SkillLintVetoError(options.target, result.violations);
|
|
@@ -6354,7 +6787,7 @@ function isSkillTarget(roots, target) {
|
|
|
6354
6787
|
return roots.some((root) => resolvedPath.startsWith(resolve12(root) + sep2));
|
|
6355
6788
|
}
|
|
6356
6789
|
function skillNameOf(target) {
|
|
6357
|
-
return basename8(
|
|
6790
|
+
return basename8(dirname10(target.displayPath));
|
|
6358
6791
|
}
|
|
6359
6792
|
function skillCanonicalForm(target) {
|
|
6360
6793
|
return resolveSkillRoot("dsh", { skill: skillNameOf(target), rel: "SKILL.md" });
|
|
@@ -6371,7 +6804,7 @@ function gateSkillIntent(ctx, harnessDir, config, target) {
|
|
|
6371
6804
|
return;
|
|
6372
6805
|
let doc;
|
|
6373
6806
|
try {
|
|
6374
|
-
doc =
|
|
6807
|
+
doc = readFileSync13(skillPath, "utf8");
|
|
6375
6808
|
} catch (error) {
|
|
6376
6809
|
ctx.logger(SKILL_LINT_LOGGER).error(`skill lint degraded to allow (cannot read ${skillPath}): ${error.message}`);
|
|
6377
6810
|
ctx.emit("mstar/skill-lint", {
|
|
@@ -6384,7 +6817,7 @@ function gateSkillIntent(ctx, harnessDir, config, target) {
|
|
|
6384
6817
|
});
|
|
6385
6818
|
return;
|
|
6386
6819
|
}
|
|
6387
|
-
const result = lintSkillDoc(doc, { skillId: basename8(
|
|
6820
|
+
const result = lintSkillDoc(doc, { skillId: basename8(dirname10(skillPath)) });
|
|
6388
6821
|
if (result.ok)
|
|
6389
6822
|
return;
|
|
6390
6823
|
const hard = resolveSeamHard(harnessDir, config);
|
|
@@ -6418,8 +6851,8 @@ async function skillWriteIntentListener(ctx, resolver, config, target, actor, ne
|
|
|
6418
6851
|
}
|
|
6419
6852
|
|
|
6420
6853
|
// src/gates/seams.ts
|
|
6421
|
-
import { existsSync as existsSync17, readFileSync as
|
|
6422
|
-
import { basename as basename10, dirname as
|
|
6854
|
+
import { existsSync as existsSync17, readFileSync as readFileSync14, readdirSync as readdirSync10 } from "node:fs";
|
|
6855
|
+
import { basename as basename10, dirname as dirname11, join as join17, resolve as resolve14, sep as sep3 } from "node:path";
|
|
6423
6856
|
|
|
6424
6857
|
// ../engine/dist/audit.js
|
|
6425
6858
|
import { AsyncLocalStorage as AsyncLocalStorage2 } from "node:async_hooks";
|
|
@@ -6651,8 +7084,8 @@ function rolesDirOf(path) {
|
|
|
6651
7084
|
const segments = resolve14(path).split(sep3);
|
|
6652
7085
|
const idx = segments.indexOf("mstar-roles");
|
|
6653
7086
|
if (idx === -1)
|
|
6654
|
-
return
|
|
6655
|
-
return idx === 0 ? sep3 :
|
|
7087
|
+
return dirname11(path);
|
|
7088
|
+
return idx === 0 ? sep3 : join17(sep3, ...segments.slice(1, idx + 1));
|
|
6656
7089
|
}
|
|
6657
7090
|
function isSeamTarget(seam, harnessDir, target) {
|
|
6658
7091
|
const path = resolve14(target.displayPath);
|
|
@@ -6666,7 +7099,7 @@ function isSeamTarget(seam, harnessDir, target) {
|
|
|
6666
7099
|
case "compound": {
|
|
6667
7100
|
if (harnessDir === null)
|
|
6668
7101
|
return false;
|
|
6669
|
-
return path.startsWith(resolve14(
|
|
7102
|
+
return path.startsWith(resolve14(join17(harnessDir, "knowledge")) + sep3) && isMarkdownDoc(path);
|
|
6670
7103
|
}
|
|
6671
7104
|
case "roles":
|
|
6672
7105
|
return isRolesTarget(path);
|
|
@@ -6684,10 +7117,10 @@ function auditSecretsViolations(findings, file) {
|
|
|
6684
7117
|
function validateDesignDoc(doc, path) {
|
|
6685
7118
|
const violations = [...validateDesignTokenFrontmatter(doc).violations];
|
|
6686
7119
|
const isDark = basename10(path) === "DESIGN.dark.md";
|
|
6687
|
-
const sibling =
|
|
7120
|
+
const sibling = join17(dirname11(path), isDark ? "DESIGN.md" : "DESIGN.dark.md");
|
|
6688
7121
|
if (existsSync17(sibling)) {
|
|
6689
|
-
const light = isDark ?
|
|
6690
|
-
const dark = isDark ? doc :
|
|
7122
|
+
const light = isDark ? readFileSync14(sibling, "utf8") : doc;
|
|
7123
|
+
const dark = isDark ? doc : readFileSync14(sibling, "utf8");
|
|
6691
7124
|
violations.push(...assertLightDarkParity(light, dark).violations);
|
|
6692
7125
|
}
|
|
6693
7126
|
return violations.length === 0 ? { ok: true, violations } : { ok: false, violations };
|
|
@@ -6701,21 +7134,21 @@ function validateAuditDoc(doc, path) {
|
|
|
6701
7134
|
function validateCompoundDoc(doc, _path, harnessDir) {
|
|
6702
7135
|
const violations = [...validateSchemaYaml(doc).violations];
|
|
6703
7136
|
if (harnessDir !== null) {
|
|
6704
|
-
violations.push(...referenceExists(
|
|
7137
|
+
violations.push(...referenceExists(dirname11(harnessDir), doc).violations);
|
|
6705
7138
|
}
|
|
6706
7139
|
return violations.length === 0 ? { ok: true, violations } : { ok: false, violations };
|
|
6707
7140
|
}
|
|
6708
|
-
function validateRolesState(rolesDir, skillsRoot =
|
|
7141
|
+
function validateRolesState(rolesDir, skillsRoot = dirname11(rolesDir)) {
|
|
6709
7142
|
const violations = [...validateRoleMapping(rolesDir).violations];
|
|
6710
7143
|
const skillTexts = {};
|
|
6711
7144
|
for (const entry of readdirSync10(skillsRoot, { withFileTypes: true })) {
|
|
6712
7145
|
if (!entry.isDirectory() || !entry.name.startsWith("mstar-"))
|
|
6713
7146
|
continue;
|
|
6714
|
-
const skillFile =
|
|
7147
|
+
const skillFile = join17(skillsRoot, entry.name, "SKILL.md");
|
|
6715
7148
|
if (!existsSync17(skillFile))
|
|
6716
7149
|
continue;
|
|
6717
7150
|
try {
|
|
6718
|
-
skillTexts[entry.name] =
|
|
7151
|
+
skillTexts[entry.name] = readFileSync14(skillFile, "utf8");
|
|
6719
7152
|
} catch {}
|
|
6720
7153
|
}
|
|
6721
7154
|
violations.push(...lintLoadOrder(skillTexts).violations);
|
|
@@ -6750,7 +7183,7 @@ function gateSeamIntent(ctx, harnessDir, config, seam, target) {
|
|
|
6750
7183
|
return;
|
|
6751
7184
|
let doc;
|
|
6752
7185
|
try {
|
|
6753
|
-
doc =
|
|
7186
|
+
doc = readFileSync14(path, "utf8");
|
|
6754
7187
|
} catch (error) {
|
|
6755
7188
|
logger.error(`seam lint degraded to allow (cannot read ${path}): ${error.message}`);
|
|
6756
7189
|
emitSeamAdvisory(ctx, seam, target.displayPath, { ok: true, violations: [] }, false, { degraded: true });
|
|
@@ -6816,8 +7249,8 @@ function lintRolesWrite(doc, options) {
|
|
|
6816
7249
|
}
|
|
6817
7250
|
|
|
6818
7251
|
// src/gates/tools.ts
|
|
6819
|
-
import { existsSync as existsSync18, readFileSync as
|
|
6820
|
-
import { join as
|
|
7252
|
+
import { existsSync as existsSync18, readFileSync as readFileSync15 } from "node:fs";
|
|
7253
|
+
import { join as join19, resolve as resolve16 } from "node:path";
|
|
6821
7254
|
import { defineTool } from "@deepseek-ai/dsh-tools";
|
|
6822
7255
|
var ITERATION_VIOLATION_SCHEMA = {
|
|
6823
7256
|
type: "object",
|
|
@@ -7038,10 +7471,10 @@ function registerSeamTools(ctx, resolver) {
|
|
|
7038
7471
|
isConcurrencySafe: () => false,
|
|
7039
7472
|
async execute(args) {
|
|
7040
7473
|
const abs = resolve16(args.dir);
|
|
7041
|
-
const lightPath =
|
|
7474
|
+
const lightPath = join19(abs, "DESIGN.md");
|
|
7042
7475
|
if (!existsSync18(lightPath))
|
|
7043
7476
|
throw new Error(`design file not found: ${lightPath}`);
|
|
7044
|
-
const light =
|
|
7477
|
+
const light = readFileSync15(lightPath, "utf8");
|
|
7045
7478
|
const result = validateDesignDoc(light, lightPath);
|
|
7046
7479
|
const level = completenessLevel(light);
|
|
7047
7480
|
return {
|
|
@@ -7094,7 +7527,7 @@ function registerSeamTools(ctx, resolver) {
|
|
|
7094
7527
|
const abs = resolve16(args.plan_path);
|
|
7095
7528
|
if (!existsSync18(abs))
|
|
7096
7529
|
throw new Error(`plan file not found: ${abs}`);
|
|
7097
|
-
const text =
|
|
7530
|
+
const text = readFileSync15(abs, "utf8");
|
|
7098
7531
|
const result = validateAuditDoc(text, abs);
|
|
7099
7532
|
return { ok: result.ok, violations: result.violations.map(iterationViolationView), secrets: result.findings };
|
|
7100
7533
|
}
|
|
@@ -7134,7 +7567,7 @@ function registerSeamTools(ctx, resolver) {
|
|
|
7134
7567
|
const abs = resolve16(args.doc_path);
|
|
7135
7568
|
if (!existsSync18(abs))
|
|
7136
7569
|
throw new Error(`knowledge doc not found: ${abs}`);
|
|
7137
|
-
const text =
|
|
7570
|
+
const text = readFileSync15(abs, "utf8");
|
|
7138
7571
|
const base = validateCompoundDoc(text, abs, args.repo_root !== undefined ? null : resolver.forAgent(exec.agent));
|
|
7139
7572
|
const violations = [...base.violations];
|
|
7140
7573
|
if (args.repo_root !== undefined) {
|
|
@@ -7177,7 +7610,7 @@ function registerSeamTools(ctx, resolver) {
|
|
|
7177
7610
|
isConcurrencySafe: () => false,
|
|
7178
7611
|
async execute(args) {
|
|
7179
7612
|
const rolesDir = resolve16(args.roles_dir);
|
|
7180
|
-
if (!existsSync18(
|
|
7613
|
+
if (!existsSync18(join19(rolesDir, "SKILL.md")))
|
|
7181
7614
|
throw new Error(`roles dir not found: ${rolesDir}`);
|
|
7182
7615
|
const result = validateRolesState(rolesDir, args.skills_root !== undefined ? resolve16(args.skills_root) : undefined);
|
|
7183
7616
|
return { ok: result.ok, violations: result.violations.map(iterationViolationView) };
|
|
@@ -7188,8 +7621,8 @@ function registerSeamTools(ctx, resolver) {
|
|
|
7188
7621
|
|
|
7189
7622
|
// src/gates/adapter.ts
|
|
7190
7623
|
import { existsSync as existsSync19 } from "node:fs";
|
|
7191
|
-
import { join as
|
|
7192
|
-
import { Service as
|
|
7624
|
+
import { join as join20 } from "node:path";
|
|
7625
|
+
import { Service as Service3 } from "@deepseek-ai/cordis";
|
|
7193
7626
|
var HOST_LOGGER = "mstar/host-adapter";
|
|
7194
7627
|
function mergeLeasesMatch(left, right) {
|
|
7195
7628
|
const a = left ?? {};
|
|
@@ -7197,7 +7630,7 @@ function mergeLeasesMatch(left, right) {
|
|
|
7197
7630
|
return ["holder", "claimed_at", "plan_id", "source_branch", "target_branch"].every((key) => a[key] === b[key]);
|
|
7198
7631
|
}
|
|
7199
7632
|
|
|
7200
|
-
class DshHostAdapter extends
|
|
7633
|
+
class DshHostAdapter extends Service3 {
|
|
7201
7634
|
host = "dsh";
|
|
7202
7635
|
resolver;
|
|
7203
7636
|
config;
|
|
@@ -7279,7 +7712,7 @@ class DshHostAdapter extends Service2 {
|
|
|
7279
7712
|
if (harnessDir !== null) {
|
|
7280
7713
|
const selection = resolveActiveWorkflow(harnessDir);
|
|
7281
7714
|
if (selection.kind === "active") {
|
|
7282
|
-
const snapshotPath =
|
|
7715
|
+
const snapshotPath = join20(harnessDir, selection.dir, WORKFLOW_SNAPSHOT_FILE);
|
|
7283
7716
|
try {
|
|
7284
7717
|
const snapshot = readJson(snapshotPath);
|
|
7285
7718
|
const stored = snapshot?.integration_merge_lease;
|
|
@@ -7305,8 +7738,8 @@ class DshHostAdapter extends Service2 {
|
|
|
7305
7738
|
}
|
|
7306
7739
|
|
|
7307
7740
|
// src/gates/agent-personas.ts
|
|
7308
|
-
import { readFileSync as
|
|
7309
|
-
import { join as
|
|
7741
|
+
import { readFileSync as readFileSync16, readdirSync as readdirSync12, statSync as statSync7 } from "node:fs";
|
|
7742
|
+
import { join as join21, resolve as resolve17, sep as sep5 } from "node:path";
|
|
7310
7743
|
var ROLE_ID_PATTERN = /^[a-z0-9-]{1,32}$/;
|
|
7311
7744
|
var defaultCache = new Map;
|
|
7312
7745
|
function personaFor(roleId, lookup, warn) {
|
|
@@ -7334,7 +7767,7 @@ function subagentRoleIds(agentsDir) {
|
|
|
7334
7767
|
const roleId = name.slice(0, -3);
|
|
7335
7768
|
let content;
|
|
7336
7769
|
try {
|
|
7337
|
-
content =
|
|
7770
|
+
content = readFileSync16(join21(agentsDir, name), "utf8");
|
|
7338
7771
|
} catch {
|
|
7339
7772
|
continue;
|
|
7340
7773
|
}
|
|
@@ -7364,7 +7797,7 @@ function defaultFromMirror(agentsDir, roleId, warn) {
|
|
|
7364
7797
|
return hit.text;
|
|
7365
7798
|
let content;
|
|
7366
7799
|
try {
|
|
7367
|
-
content =
|
|
7800
|
+
content = readFileSync16(file, "utf8");
|
|
7368
7801
|
} catch {
|
|
7369
7802
|
return;
|
|
7370
7803
|
}
|
|
@@ -7595,8 +8028,8 @@ function log2(level, message) {
|
|
|
7595
8028
|
}
|
|
7596
8029
|
|
|
7597
8030
|
// src/gates/workflow-ledger.ts
|
|
7598
|
-
import { readFileSync as
|
|
7599
|
-
import { join as
|
|
8031
|
+
import { readFileSync as readFileSync17, renameSync as renameSync4, writeFileSync as writeFileSync7 } from "node:fs";
|
|
8032
|
+
import { join as join22 } from "node:path";
|
|
7600
8033
|
var WORKFLOW_LEDGER_LOGGER = "mstar/workflow-ledger";
|
|
7601
8034
|
var TOOL_WORKFLOW_RUN_START = "tool-workflow/run-start";
|
|
7602
8035
|
var TOOL_WORKFLOW_AGENT_START = "tool-workflow/agent-start";
|
|
@@ -7640,7 +8073,7 @@ function loadWatermark(workflowDir, fresh = false) {
|
|
|
7640
8073
|
return cached;
|
|
7641
8074
|
const watermark = new Map;
|
|
7642
8075
|
try {
|
|
7643
|
-
const raw =
|
|
8076
|
+
const raw = readFileSync17(join22(workflowDir, WORKFLOW_LEDGER_WATERMARK_FILE), "utf8");
|
|
7644
8077
|
const record = asRecord(JSON.parse(raw));
|
|
7645
8078
|
const cursors = asRecord(record?.cursors);
|
|
7646
8079
|
if (cursors !== undefined) {
|
|
@@ -7663,10 +8096,10 @@ function loadWatermark(workflowDir, fresh = false) {
|
|
|
7663
8096
|
}
|
|
7664
8097
|
function saveWatermark(workflowDir, watermark) {
|
|
7665
8098
|
try {
|
|
7666
|
-
const file =
|
|
8099
|
+
const file = join22(workflowDir, WORKFLOW_LEDGER_WATERMARK_FILE);
|
|
7667
8100
|
const tmp = `${file}.tmp`;
|
|
7668
|
-
|
|
7669
|
-
|
|
8101
|
+
writeFileSync7(tmp, JSON.stringify({ v: 1, cursors: Object.fromEntries(watermark) }));
|
|
8102
|
+
renameSync4(tmp, file);
|
|
7670
8103
|
} catch (error) {
|
|
7671
8104
|
log3("warn", `workflow-ledger watermark write failed for ${workflowDir} — in-memory only (restart re-records): ${errorMessage3(error)}`);
|
|
7672
8105
|
}
|
|
@@ -7879,8 +8312,8 @@ function registerWorkflowLedger(ctx, resolver, workflowAskCache) {
|
|
|
7879
8312
|
}
|
|
7880
8313
|
|
|
7881
8314
|
// src/gates/goal-bridge.ts
|
|
7882
|
-
import { existsSync as existsSync20, readFileSync as
|
|
7883
|
-
import { join as
|
|
8315
|
+
import { existsSync as existsSync20, readFileSync as readFileSync18, readdirSync as readdirSync13 } from "node:fs";
|
|
8316
|
+
import { join as join23 } from "node:path";
|
|
7884
8317
|
var GOAL_BRIDGE_LOGGER = "mstar/goal-bridge";
|
|
7885
8318
|
var DEFAULT_MAX_GOAL_ROUNDS = 256;
|
|
7886
8319
|
var FLOW_SEQUENCE = "iteration-start → per-plan cycles → iteration-close → PR delivery → merge-ready";
|
|
@@ -7907,12 +8340,12 @@ function projectRegisterPointer(harnessDir) {
|
|
|
7907
8340
|
for (const entry of readdirSync13(projectsDir, { withFileTypes: true })) {
|
|
7908
8341
|
if (!entry.isDirectory())
|
|
7909
8342
|
continue;
|
|
7910
|
-
const registerPath =
|
|
8343
|
+
const registerPath = join23(projectsDir, entry.name, PROJECT_REGISTER_FILE);
|
|
7911
8344
|
if (existsSync20(registerPath))
|
|
7912
8345
|
return registerPath;
|
|
7913
8346
|
}
|
|
7914
8347
|
} catch {}
|
|
7915
|
-
return
|
|
8348
|
+
return join23(projectsDir, _DEFAULT_PROJECT, PROJECT_REGISTER_FILE);
|
|
7916
8349
|
}
|
|
7917
8350
|
function isRootLikeAgent(agent) {
|
|
7918
8351
|
const header = agent?.session?.header;
|
|
@@ -7934,12 +8367,12 @@ function steeringCompass(harnessDir) {
|
|
|
7934
8367
|
for (const entry of entries) {
|
|
7935
8368
|
if (!entry.isDirectory())
|
|
7936
8369
|
continue;
|
|
7937
|
-
const compassPath =
|
|
8370
|
+
const compassPath = join23(iterationsDir, entry.name, "delivery-compass.md");
|
|
7938
8371
|
if (!existsSync20(compassPath))
|
|
7939
8372
|
continue;
|
|
7940
8373
|
let content;
|
|
7941
8374
|
try {
|
|
7942
|
-
content =
|
|
8375
|
+
content = readFileSync18(compassPath, "utf8");
|
|
7943
8376
|
} catch {
|
|
7944
8377
|
continue;
|
|
7945
8378
|
}
|
|
@@ -8116,7 +8549,7 @@ function registerGoalBridge(ctx, resolver, config) {
|
|
|
8116
8549
|
|
|
8117
8550
|
// src/gates/plan-mode-bridge.ts
|
|
8118
8551
|
import { existsSync as existsSync21 } from "node:fs";
|
|
8119
|
-
import { join as
|
|
8552
|
+
import { join as join24 } from "node:path";
|
|
8120
8553
|
var PLAN_MODE_BRIDGE_LOGGER = "mstar/plan-mode-bridge";
|
|
8121
8554
|
var PLAN_STATUS_TODO = "Todo";
|
|
8122
8555
|
var planModeBridgeLogSink = () => {};
|
|
@@ -8132,13 +8565,13 @@ function errorMessage5(error) {
|
|
|
8132
8565
|
return error instanceof Error ? error.message : String(error);
|
|
8133
8566
|
}
|
|
8134
8567
|
function hasPrepareWindow(harnessDir) {
|
|
8135
|
-
const statusPath =
|
|
8568
|
+
const statusPath = join24(harnessDir, STATUS_FILE);
|
|
8136
8569
|
if (!existsSync21(statusPath))
|
|
8137
8570
|
return false;
|
|
8138
8571
|
const selection = resolveReadWorkflow(harnessDir);
|
|
8139
8572
|
if (selection.kind === "error")
|
|
8140
8573
|
return false;
|
|
8141
|
-
const snapshotPath =
|
|
8574
|
+
const snapshotPath = join24(harnessDir, selection.dir, WORKFLOW_SNAPSHOT_FILE);
|
|
8142
8575
|
let doc;
|
|
8143
8576
|
try {
|
|
8144
8577
|
doc = readJson(snapshotPath);
|
|
@@ -8584,16 +9017,16 @@ function engineStatusProvider(ctx, resolver, bootHarnessDir) {
|
|
|
8584
9017
|
const now = Date.now();
|
|
8585
9018
|
let entry = memo.get(harnessDir);
|
|
8586
9019
|
if (entry === undefined || now - entry.builtAt >= DEFAULT_CATALOG_TTL_MS) {
|
|
8587
|
-
entry = {
|
|
9020
|
+
entry = { payload: buildCatalogPayload(ctx, harnessDir), builtAt: now };
|
|
8588
9021
|
memo.set(harnessDir, entry);
|
|
8589
9022
|
}
|
|
8590
|
-
return engineStatusSummary(entry.
|
|
9023
|
+
return engineStatusSummary(entry.payload);
|
|
8591
9024
|
};
|
|
8592
9025
|
}
|
|
8593
9026
|
var DIGEST_PLAN_CAP = 8;
|
|
8594
|
-
function engineStatusSummary(
|
|
8595
|
-
const lines = [`mstar engine status: v${
|
|
8596
|
-
const state =
|
|
9027
|
+
function engineStatusSummary(payload) {
|
|
9028
|
+
const lines = [`mstar engine status: v${payload.version}`];
|
|
9029
|
+
const state = payload.state;
|
|
8597
9030
|
if (state !== null) {
|
|
8598
9031
|
const selection = state.selection;
|
|
8599
9032
|
if (selection.kind === "active") {
|
|
@@ -8678,7 +9111,7 @@ function registerMstarCommands(ctx) {
|
|
|
8678
9111
|
for (const file of readdirSync14(dir).sort()) {
|
|
8679
9112
|
if (!file.endsWith(".md"))
|
|
8680
9113
|
continue;
|
|
8681
|
-
const parsed = parseCommandMarkdown(
|
|
9114
|
+
const parsed = parseCommandMarkdown(readFileSync19(join25(dir, file), "utf8"));
|
|
8682
9115
|
if (parsed === undefined)
|
|
8683
9116
|
continue;
|
|
8684
9117
|
commandsCtx.commands.register({
|
|
@@ -8871,7 +9304,7 @@ function apply(ctx, config) {
|
|
|
8871
9304
|
const explicitKey = bootHarnessDir !== null ? EXPLICIT_CACHE_KEY : undefined;
|
|
8872
9305
|
const catalogCache = new Map;
|
|
8873
9306
|
if (explicitKey !== undefined) {
|
|
8874
|
-
catalogCache.set(explicitKey, {
|
|
9307
|
+
catalogCache.set(explicitKey, { payload: buildCatalogPayload(ctx, bootHarnessDir), builtAt: Date.now() });
|
|
8875
9308
|
}
|
|
8876
9309
|
const catalogInvalidation = createCatalogInvalidation(catalogCache);
|
|
8877
9310
|
if (explicitKey !== undefined)
|
|
@@ -8879,6 +9312,7 @@ function apply(ctx, config) {
|
|
|
8879
9312
|
setAgentFlowInvalidator(catalogInvalidation.invalidate);
|
|
8880
9313
|
const catalogDigests = new Map;
|
|
8881
9314
|
ctx.on("agent/pre-step", (payload, next) => preStepCatalogListener(ctx, resolver, explicitKey, catalogCache, ttlMs, catalogInvalidation.register, catalogDigests, payload, next));
|
|
9315
|
+
installEngineStatusEndpoint(ctx, { resolver, bootHarnessDir });
|
|
8882
9316
|
registerSddIterationTools(ctx, resolver);
|
|
8883
9317
|
registerSeamTools(ctx, resolver);
|
|
8884
9318
|
}
|