metheus-governance-mcp-cli 0.2.148 → 0.2.152
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/cli.mjs +419 -35
- package/lib/local-ai-adapters.mjs +209 -9
- package/lib/local-project-dispatch.mjs +0 -12
- package/lib/runner-data.mjs +88 -0
- package/lib/runner-execution.mjs +6 -1
- package/lib/runner-helpers.mjs +15 -0
- package/lib/runner-orchestration.mjs +713 -31
- package/lib/runner-runtime.mjs +172 -25
- package/lib/selftest-runner-scenarios.mjs +1413 -126
- package/lib/selftest-telegram-e2e.mjs +194 -0
- package/package.json +1 -1
package/cli.mjs
CHANGED
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
auditDirectHumanReplyWithAI,
|
|
17
17
|
normalizeExecutionArtifacts,
|
|
18
18
|
planRoleExecutionWithAI,
|
|
19
|
+
repairRoleExecutionPlanWithAI,
|
|
19
20
|
resolveLocalAIExecutionModel,
|
|
20
21
|
resolveGeminiReasoningConfig,
|
|
21
22
|
suggestLocalAIModelDisplayName,
|
|
@@ -129,8 +130,12 @@ import {
|
|
|
129
130
|
printRunnerResult,
|
|
130
131
|
} from "./lib/runner-helpers.mjs";
|
|
131
132
|
import {
|
|
133
|
+
createProjectEvidence as createProjectEvidenceImpl,
|
|
134
|
+
createProjectWorkItem as createProjectWorkItemImpl,
|
|
132
135
|
createThreadComment as createThreadCommentImpl,
|
|
136
|
+
createWorkItemThread as createWorkItemThreadImpl,
|
|
133
137
|
discoverArchiveThreadForDestination as discoverArchiveThreadForDestinationImpl,
|
|
138
|
+
linkWorkItemEvidence as linkWorkItemEvidenceImpl,
|
|
134
139
|
listProjectChatDestinations as listProjectChatDestinationsImpl,
|
|
135
140
|
listThreadComments as listThreadCommentsImpl,
|
|
136
141
|
listUserBotsForRunner as listUserBotsForRunnerImpl,
|
|
@@ -1359,7 +1364,6 @@ function loadBotRunnerWorkspaceRegistry(options = {}) {
|
|
|
1359
1364
|
}
|
|
1360
1365
|
|
|
1361
1366
|
function saveBotRunnerConfig(nextConfig, filePath = botRunnerConfigFilePath()) {
|
|
1362
|
-
saveBotRunnerWorkspaceRegistry(safeObject(nextConfig).projectMappings);
|
|
1363
1367
|
const payload = serializeBotRunnerConfig(nextConfig);
|
|
1364
1368
|
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
1365
1369
|
fs.writeFileSync(filePath, `${JSON.stringify(payload, null, 2)}\n`, "utf8");
|
|
@@ -1520,6 +1524,9 @@ function loadBotRunnerConfig(options = {}) {
|
|
|
1520
1524
|
normalized.projectMappings = mergedProjectMappings;
|
|
1521
1525
|
normalized.workspaceRegistryFilePath = workspaceRegistry.filePath;
|
|
1522
1526
|
if (persistIfNeeded && (normalized.migrated || !fs.existsSync(filePath) || registryChanged || legacyProjectMappingsPresent)) {
|
|
1527
|
+
if (registryChanged || legacyProjectMappingsPresent) {
|
|
1528
|
+
saveBotRunnerWorkspaceRegistry(mergedProjectMappings, workspaceRegistry.filePath);
|
|
1529
|
+
}
|
|
1523
1530
|
saveBotRunnerConfig(normalized, filePath);
|
|
1524
1531
|
normalized.migrated = false;
|
|
1525
1532
|
}
|
|
@@ -1533,6 +1540,42 @@ function loadBotRunnerConfig(options = {}) {
|
|
|
1533
1540
|
}
|
|
1534
1541
|
}
|
|
1535
1542
|
|
|
1543
|
+
function normalizeProjectWorkspaceMappingSource(rawSource) {
|
|
1544
|
+
return String(rawSource || "").trim().toLowerCase();
|
|
1545
|
+
}
|
|
1546
|
+
|
|
1547
|
+
function isAutomaticProjectWorkspaceMappingSource(rawSource) {
|
|
1548
|
+
const source = normalizeProjectWorkspaceMappingSource(rawSource);
|
|
1549
|
+
return [
|
|
1550
|
+
"ctxpack_sync",
|
|
1551
|
+
"ctxpack_sync_current",
|
|
1552
|
+
"ctxpack_sync_write",
|
|
1553
|
+
"project_tool_workspace_signal",
|
|
1554
|
+
].includes(source);
|
|
1555
|
+
}
|
|
1556
|
+
|
|
1557
|
+
function resolveStableProjectWorkspaceCandidate(projectID, workspaceDir) {
|
|
1558
|
+
const normalizedWorkspaceDir = sanitizeWorkspaceCandidate(workspaceDir);
|
|
1559
|
+
if (!isUUID(projectID)) {
|
|
1560
|
+
return normalizedWorkspaceDir;
|
|
1561
|
+
}
|
|
1562
|
+
const config = loadBotRunnerConfig({ persistIfNeeded: true });
|
|
1563
|
+
const existing = normalizeBotRunnerProjectMapping(projectID, config.projectMappings?.[projectID]);
|
|
1564
|
+
if (!existing.workspaceDir) {
|
|
1565
|
+
return normalizedWorkspaceDir;
|
|
1566
|
+
}
|
|
1567
|
+
if (!normalizedWorkspaceDir) {
|
|
1568
|
+
return existing.workspaceDir;
|
|
1569
|
+
}
|
|
1570
|
+
if (
|
|
1571
|
+
isSameOrChildPath(normalizedWorkspaceDir, existing.workspaceDir)
|
|
1572
|
+
|| isSameOrChildPath(existing.workspaceDir, normalizedWorkspaceDir)
|
|
1573
|
+
) {
|
|
1574
|
+
return existing.workspaceDir;
|
|
1575
|
+
}
|
|
1576
|
+
return existing.workspaceDir;
|
|
1577
|
+
}
|
|
1578
|
+
|
|
1536
1579
|
function rememberProjectWorkspaceMapping({ projectID, workspaceDir, source }) {
|
|
1537
1580
|
if (!isUUID(projectID)) {
|
|
1538
1581
|
return { ok: false, updated: false, reason: "invalid project_id" };
|
|
@@ -1546,6 +1589,7 @@ function rememberProjectWorkspaceMapping({ projectID, workspaceDir, source }) {
|
|
|
1546
1589
|
}
|
|
1547
1590
|
const config = loadBotRunnerConfig({ persistIfNeeded: true });
|
|
1548
1591
|
const existing = normalizeBotRunnerProjectMapping(projectID, config.projectMappings?.[projectID]);
|
|
1592
|
+
const normalizedSource = String(source || existing.source || "ctxpack_sync").trim() || "ctxpack_sync";
|
|
1549
1593
|
if (existing.workspaceDir && isSameOrChildPath(normalizedWorkspaceDir, existing.workspaceDir)) {
|
|
1550
1594
|
return {
|
|
1551
1595
|
ok: true,
|
|
@@ -1570,24 +1614,38 @@ function rememberProjectWorkspaceMapping({ projectID, workspaceDir, source }) {
|
|
|
1570
1614
|
workspaceDir: normalizedWorkspaceDir,
|
|
1571
1615
|
};
|
|
1572
1616
|
}
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1617
|
+
if (
|
|
1618
|
+
existing.workspaceDir
|
|
1619
|
+
&& !isSameOrChildPath(existing.workspaceDir, normalizedWorkspaceDir)
|
|
1620
|
+
&& !isSameOrChildPath(normalizedWorkspaceDir, existing.workspaceDir)
|
|
1621
|
+
&& isAutomaticProjectWorkspaceMappingSource(normalizedSource)
|
|
1622
|
+
) {
|
|
1623
|
+
return {
|
|
1624
|
+
ok: true,
|
|
1625
|
+
updated: false,
|
|
1626
|
+
filePath: config.filePath,
|
|
1627
|
+
workspaceRegistryFilePath: config.workspaceRegistryFilePath,
|
|
1628
|
+
projectID,
|
|
1629
|
+
workspaceDir: existing.workspaceDir,
|
|
1630
|
+
reason: "existing workspace mapping preserved over unrelated automatic signal",
|
|
1631
|
+
};
|
|
1632
|
+
}
|
|
1633
|
+
const nextProjectMappings = {
|
|
1634
|
+
...safeObject(config.projectMappings),
|
|
1635
|
+
[projectID]: {
|
|
1636
|
+
projectID,
|
|
1637
|
+
workspaceDir: normalizedWorkspaceDir,
|
|
1638
|
+
source: normalizedSource,
|
|
1639
|
+
updatedAt: new Date().toISOString(),
|
|
1583
1640
|
},
|
|
1584
1641
|
};
|
|
1585
|
-
const
|
|
1642
|
+
const workspaceRegistryFilePath = config.workspaceRegistryFilePath || botRunnerWorkspaceRegistryFilePath();
|
|
1643
|
+
saveBotRunnerWorkspaceRegistry(nextProjectMappings, workspaceRegistryFilePath);
|
|
1586
1644
|
return {
|
|
1587
1645
|
ok: true,
|
|
1588
1646
|
updated: true,
|
|
1589
|
-
filePath,
|
|
1590
|
-
workspaceRegistryFilePath
|
|
1647
|
+
filePath: config.filePath,
|
|
1648
|
+
workspaceRegistryFilePath,
|
|
1591
1649
|
projectID,
|
|
1592
1650
|
workspaceDir: normalizedWorkspaceDir,
|
|
1593
1651
|
};
|
|
@@ -1808,6 +1866,7 @@ function loadBotRunnerState() {
|
|
|
1808
1866
|
return {
|
|
1809
1867
|
filePath,
|
|
1810
1868
|
routes: {},
|
|
1869
|
+
sharedInboxes: {},
|
|
1811
1870
|
migrated: false,
|
|
1812
1871
|
migratedKeys: [],
|
|
1813
1872
|
remainingAnonymousKeys: [],
|
|
@@ -1817,11 +1876,15 @@ function loadBotRunnerState() {
|
|
|
1817
1876
|
const runnerConfig = loadBotRunnerConfig({ persistIfNeeded: true });
|
|
1818
1877
|
const migratedState = migrateBotRunnerStateRoutes(safeObject(parsed?.routes), runnerConfig);
|
|
1819
1878
|
if (migratedState.changed) {
|
|
1820
|
-
saveBotRunnerState({
|
|
1879
|
+
saveBotRunnerState({
|
|
1880
|
+
routes: migratedState.routes,
|
|
1881
|
+
sharedInboxes: safeObject(parsed?.shared_inboxes || parsed?.sharedInboxes),
|
|
1882
|
+
});
|
|
1821
1883
|
}
|
|
1822
1884
|
return {
|
|
1823
1885
|
filePath,
|
|
1824
1886
|
routes: migratedState.routes,
|
|
1887
|
+
sharedInboxes: safeObject(parsed?.shared_inboxes || parsed?.sharedInboxes),
|
|
1825
1888
|
migrated: migratedState.changed,
|
|
1826
1889
|
migratedKeys: migratedState.migratedKeys,
|
|
1827
1890
|
remainingAnonymousKeys: migratedState.remainingAnonymousKeys,
|
|
@@ -1830,6 +1893,7 @@ function loadBotRunnerState() {
|
|
|
1830
1893
|
return {
|
|
1831
1894
|
filePath,
|
|
1832
1895
|
routes: {},
|
|
1896
|
+
sharedInboxes: {},
|
|
1833
1897
|
migrated: false,
|
|
1834
1898
|
migratedKeys: [],
|
|
1835
1899
|
remainingAnonymousKeys: [],
|
|
@@ -1839,10 +1903,15 @@ function loadBotRunnerState() {
|
|
|
1839
1903
|
|
|
1840
1904
|
function saveBotRunnerState(nextState) {
|
|
1841
1905
|
const filePath = botRunnerStateFilePath();
|
|
1906
|
+
let current = {};
|
|
1907
|
+
try {
|
|
1908
|
+
current = safeObject(tryJsonParse(fs.readFileSync(filePath, "utf8")));
|
|
1909
|
+
} catch {}
|
|
1842
1910
|
const payload = {
|
|
1843
1911
|
version: 1,
|
|
1844
1912
|
updated_at: new Date().toISOString(),
|
|
1845
|
-
routes: safeObject(nextState?.routes),
|
|
1913
|
+
routes: safeObject(nextState?.routes ?? current.routes),
|
|
1914
|
+
shared_inboxes: safeObject(nextState?.sharedInboxes ?? nextState?.shared_inboxes ?? current.shared_inboxes ?? current.sharedInboxes),
|
|
1846
1915
|
};
|
|
1847
1916
|
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
1848
1917
|
fs.writeFileSync(filePath, `${JSON.stringify(payload, null, 2)}\n`, "utf8");
|
|
@@ -2978,6 +3047,235 @@ async function postJSONWithAuthHeaders(urlText, timeoutSeconds, token, payload,
|
|
|
2978
3047
|
});
|
|
2979
3048
|
}
|
|
2980
3049
|
|
|
3050
|
+
async function putJSONWithAuthHeaders(urlText, timeoutSeconds, token, payload, extraHeaders = {}) {
|
|
3051
|
+
return new Promise((resolve, reject) => {
|
|
3052
|
+
const url = new URL(urlText);
|
|
3053
|
+
const body = Buffer.from(JSON.stringify(payload));
|
|
3054
|
+
const transport = url.protocol === "http:" ? http : https;
|
|
3055
|
+
const req = transport.request(
|
|
3056
|
+
{
|
|
3057
|
+
protocol: url.protocol,
|
|
3058
|
+
hostname: url.hostname,
|
|
3059
|
+
port: url.port || (url.protocol === "http:" ? 80 : 443),
|
|
3060
|
+
path: `${url.pathname}${url.search}`,
|
|
3061
|
+
method: "PUT",
|
|
3062
|
+
headers: {
|
|
3063
|
+
"content-type": "application/json",
|
|
3064
|
+
accept: "application/json",
|
|
3065
|
+
authorization: `Bearer ${token}`,
|
|
3066
|
+
"content-length": String(body.length),
|
|
3067
|
+
...extraHeaders,
|
|
3068
|
+
},
|
|
3069
|
+
timeout: Math.max(3, timeoutSeconds) * 1000,
|
|
3070
|
+
},
|
|
3071
|
+
(res) => {
|
|
3072
|
+
const chunks = [];
|
|
3073
|
+
res.on("data", (chunk) => chunks.push(Buffer.from(chunk)));
|
|
3074
|
+
res.on("end", () => {
|
|
3075
|
+
const text = Buffer.concat(chunks).toString("utf8");
|
|
3076
|
+
const statusCode = Number(res.statusCode || 0);
|
|
3077
|
+
if (statusCode >= 200 && statusCode < 300) {
|
|
3078
|
+
resolve(text.trim());
|
|
3079
|
+
return;
|
|
3080
|
+
}
|
|
3081
|
+
const err = new Error(text.trim() || `http ${statusCode}`);
|
|
3082
|
+
err.statusCode = statusCode;
|
|
3083
|
+
err.responseBody = text;
|
|
3084
|
+
reject(err);
|
|
3085
|
+
});
|
|
3086
|
+
},
|
|
3087
|
+
);
|
|
3088
|
+
req.on("timeout", () => {
|
|
3089
|
+
req.destroy(new Error("http timeout"));
|
|
3090
|
+
});
|
|
3091
|
+
req.on("error", reject);
|
|
3092
|
+
req.write(body);
|
|
3093
|
+
req.end();
|
|
3094
|
+
});
|
|
3095
|
+
}
|
|
3096
|
+
|
|
3097
|
+
function inferCtxpackDocTypeFromPath(relativePath, artifactKind = "", existingDocType = "") {
|
|
3098
|
+
const explicit = String(existingDocType || "").trim().toLowerCase();
|
|
3099
|
+
if (explicit) {
|
|
3100
|
+
return explicit;
|
|
3101
|
+
}
|
|
3102
|
+
const normalizedPath = sanitizeCtxpackRelativePath(relativePath).toLowerCase();
|
|
3103
|
+
const normalizedKind = String(artifactKind || "").trim().toLowerCase();
|
|
3104
|
+
if (!normalizedPath) {
|
|
3105
|
+
return normalizedKind || "guide";
|
|
3106
|
+
}
|
|
3107
|
+
if (normalizedPath === "ctxpack.yaml") return "manifest";
|
|
3108
|
+
if (normalizedPath === "readme.md") return "readme";
|
|
3109
|
+
if (normalizedPath === "agenda.md" || normalizedPath.endsWith("/agenda.md")) return "agenda";
|
|
3110
|
+
if (normalizedPath.startsWith("repo_map/")) return "repo_map";
|
|
3111
|
+
if (normalizedPath.startsWith("evals/")) return "eval_rules";
|
|
3112
|
+
if (normalizedPath.includes("invariant")) return "invariants";
|
|
3113
|
+
if (normalizedPath.startsWith("rules/")) return "rule";
|
|
3114
|
+
if (normalizedPath.includes("architecture")) return "architecture";
|
|
3115
|
+
if (normalizedPath.includes("glossary")) return "glossary";
|
|
3116
|
+
if (normalizedPath.includes("style")) return "style";
|
|
3117
|
+
if (normalizedPath.includes("error") || normalizedPath.includes("logging")) return "errors_logging";
|
|
3118
|
+
if (normalizedPath.includes("playbook") && normalizedPath.includes("debug")) return "playbook_debugging";
|
|
3119
|
+
if (normalizedPath.includes("playbook") && normalizedPath.includes("release")) return "playbook_release";
|
|
3120
|
+
if (normalizedPath.includes("playbook") && normalizedPath.includes("migration")) return "playbook_migrations";
|
|
3121
|
+
if (normalizedKind === "spec") return "architecture";
|
|
3122
|
+
if (normalizedKind === "plan") return "guide";
|
|
3123
|
+
if (normalizedKind === "doc") return "guide";
|
|
3124
|
+
return normalizedPath.endsWith(".yaml") || normalizedPath.endsWith(".yml") ? "manifest" : "guide";
|
|
3125
|
+
}
|
|
3126
|
+
|
|
3127
|
+
function shouldIgnoreCtxpackArtifactPath(relativePath) {
|
|
3128
|
+
const normalizedPath = sanitizeCtxpackRelativePath(relativePath).toLowerCase();
|
|
3129
|
+
if (!normalizedPath) return true;
|
|
3130
|
+
return (
|
|
3131
|
+
normalizedPath === CTXPACK_META_FILENAME.toLowerCase()
|
|
3132
|
+
|| normalizedPath.startsWith(".metheus/")
|
|
3133
|
+
|| normalizedPath.startsWith(".claude/")
|
|
3134
|
+
|| normalizedPath.startsWith(".git/")
|
|
3135
|
+
|| normalizedPath.startsWith("node_modules/")
|
|
3136
|
+
);
|
|
3137
|
+
}
|
|
3138
|
+
|
|
3139
|
+
function writeWorkspaceCtxpackMeta({
|
|
3140
|
+
siteBaseURL,
|
|
3141
|
+
projectID,
|
|
3142
|
+
workspaceDir,
|
|
3143
|
+
ctxpackResponse,
|
|
3144
|
+
}) {
|
|
3145
|
+
const resolvedWorkspaceDir = resolveWorkspaceDir(workspaceDir);
|
|
3146
|
+
if (!resolvedWorkspaceDir) {
|
|
3147
|
+
return;
|
|
3148
|
+
}
|
|
3149
|
+
const files = normalizeCtxpackFiles(safeObject(ctxpackResponse).files);
|
|
3150
|
+
const payload = {
|
|
3151
|
+
project_id: projectID,
|
|
3152
|
+
ctxpack_id: String(ctxpackResponse?.ctxpack_id || "").trim(),
|
|
3153
|
+
version_id: String(ctxpackResponse?.version_id || "").trim(),
|
|
3154
|
+
version: String(ctxpackResponse?.version || "").trim(),
|
|
3155
|
+
status: String(ctxpackResponse?.status || "").trim() || "draft",
|
|
3156
|
+
files_count: files.length,
|
|
3157
|
+
files: files.map((item) => ({
|
|
3158
|
+
path: item.path,
|
|
3159
|
+
doc_type: item.docType || "",
|
|
3160
|
+
})),
|
|
3161
|
+
source: `${String(siteBaseURL || "").replace(/\/+$/, "")}/api/v1/projects/${encodeURIComponent(projectID)}/ctxpack`,
|
|
3162
|
+
synced_at: new Date().toISOString(),
|
|
3163
|
+
};
|
|
3164
|
+
fs.writeFileSync(
|
|
3165
|
+
path.join(resolvedWorkspaceDir, CTXPACK_META_FILENAME),
|
|
3166
|
+
`${JSON.stringify(payload, null, 2)}\n`,
|
|
3167
|
+
"utf8",
|
|
3168
|
+
);
|
|
3169
|
+
}
|
|
3170
|
+
|
|
3171
|
+
async function replaceProjectCtxpackFiles({
|
|
3172
|
+
siteBaseURL,
|
|
3173
|
+
token,
|
|
3174
|
+
timeoutSeconds,
|
|
3175
|
+
actorUserID,
|
|
3176
|
+
projectID,
|
|
3177
|
+
workspaceDir,
|
|
3178
|
+
artifacts = [],
|
|
3179
|
+
}) {
|
|
3180
|
+
const resolvedWorkspaceDir = resolveStableProjectWorkspaceCandidate(projectID, workspaceDir) || resolveWorkspaceDir(workspaceDir);
|
|
3181
|
+
if (!resolvedWorkspaceDir) {
|
|
3182
|
+
throw new Error("workspace_dir is required to update project ctxpack");
|
|
3183
|
+
}
|
|
3184
|
+
if (!isUUID(projectID)) {
|
|
3185
|
+
throw new Error("invalid project_id for ctxpack update");
|
|
3186
|
+
}
|
|
3187
|
+
const ctxpackURL = `${siteBaseURL}/api/v1/projects/${encodeURIComponent(projectID)}/ctxpack`;
|
|
3188
|
+
const currentCtxpack = safeObject(await getJSONWithAuthHeaders(
|
|
3189
|
+
ctxpackURL,
|
|
3190
|
+
timeoutSeconds,
|
|
3191
|
+
token,
|
|
3192
|
+
{ "X-Actor-User-Id": actorUserID },
|
|
3193
|
+
));
|
|
3194
|
+
const versionID = firstNonEmptyString([
|
|
3195
|
+
currentCtxpack.version_id,
|
|
3196
|
+
loadWorkspaceMeta(resolvedWorkspaceDir).version_id,
|
|
3197
|
+
loadWorkspaceMeta(resolvedWorkspaceDir).base_version_id,
|
|
3198
|
+
]);
|
|
3199
|
+
if (!versionID) {
|
|
3200
|
+
throw new Error("ctxpack version_id is missing; run ctxpack pull or refresh project summary first");
|
|
3201
|
+
}
|
|
3202
|
+
const baselineFiles = normalizeCtxpackFiles(currentCtxpack.files);
|
|
3203
|
+
const fileMap = new Map();
|
|
3204
|
+
baselineFiles.forEach((file) => {
|
|
3205
|
+
fileMap.set(file.path, {
|
|
3206
|
+
path: file.path,
|
|
3207
|
+
doc_type: inferCtxpackDocTypeFromPath(file.path, "", file.docType),
|
|
3208
|
+
content: String(file.content || ""),
|
|
3209
|
+
is_generated: false,
|
|
3210
|
+
});
|
|
3211
|
+
});
|
|
3212
|
+
const changedPaths = [];
|
|
3213
|
+
const skippedPaths = [];
|
|
3214
|
+
for (const artifactRaw of ensureArray(artifacts)) {
|
|
3215
|
+
const artifact = safeObject(artifactRaw);
|
|
3216
|
+
const relativePath = sanitizeCtxpackRelativePath(firstNonEmptyString([artifact.relativePath, artifact.path]));
|
|
3217
|
+
if (!relativePath) {
|
|
3218
|
+
continue;
|
|
3219
|
+
}
|
|
3220
|
+
if (shouldIgnoreCtxpackArtifactPath(relativePath)) {
|
|
3221
|
+
skippedPaths.push(relativePath);
|
|
3222
|
+
continue;
|
|
3223
|
+
}
|
|
3224
|
+
const operation = String(artifact.operation || "").trim().toLowerCase() || "update";
|
|
3225
|
+
changedPaths.push(relativePath);
|
|
3226
|
+
if (operation === "delete") {
|
|
3227
|
+
fileMap.delete(relativePath);
|
|
3228
|
+
continue;
|
|
3229
|
+
}
|
|
3230
|
+
const absolutePath = path.join(resolvedWorkspaceDir, relativePath);
|
|
3231
|
+
if (!fs.existsSync(absolutePath)) {
|
|
3232
|
+
throw new Error(`ctxpack source artifact is missing from workspace: ${absolutePath}`);
|
|
3233
|
+
}
|
|
3234
|
+
const content = fs.readFileSync(absolutePath, "utf8");
|
|
3235
|
+
const existingDocType = String(safeObject(fileMap.get(relativePath)).doc_type || "").trim();
|
|
3236
|
+
fileMap.set(relativePath, {
|
|
3237
|
+
path: relativePath,
|
|
3238
|
+
doc_type: inferCtxpackDocTypeFromPath(relativePath, artifact.kind, existingDocType),
|
|
3239
|
+
content,
|
|
3240
|
+
is_generated: true,
|
|
3241
|
+
});
|
|
3242
|
+
}
|
|
3243
|
+
const files = Array.from(fileMap.values())
|
|
3244
|
+
.filter((item) => item.path && !shouldIgnoreCtxpackArtifactPath(item.path))
|
|
3245
|
+
.sort((left, right) => left.path.localeCompare(right.path));
|
|
3246
|
+
if (!files.length) {
|
|
3247
|
+
throw new Error("ctxpack update produced no pushable ctxpack files");
|
|
3248
|
+
}
|
|
3249
|
+
const responseText = await putJSONWithAuthHeaders(
|
|
3250
|
+
ctxpackURL,
|
|
3251
|
+
timeoutSeconds,
|
|
3252
|
+
token,
|
|
3253
|
+
{
|
|
3254
|
+
version_id: versionID,
|
|
3255
|
+
files,
|
|
3256
|
+
},
|
|
3257
|
+
{
|
|
3258
|
+
"X-Actor-User-Id": actorUserID,
|
|
3259
|
+
},
|
|
3260
|
+
);
|
|
3261
|
+
const parsed = safeObject(parseJSONText(responseText));
|
|
3262
|
+
if (!Object.keys(parsed).length) {
|
|
3263
|
+
throw new Error("invalid json response from ctxpack update");
|
|
3264
|
+
}
|
|
3265
|
+
writeWorkspaceCtxpackMeta({
|
|
3266
|
+
siteBaseURL,
|
|
3267
|
+
projectID,
|
|
3268
|
+
workspaceDir: resolvedWorkspaceDir,
|
|
3269
|
+
ctxpackResponse: parsed,
|
|
3270
|
+
});
|
|
3271
|
+
return {
|
|
3272
|
+
...parsed,
|
|
3273
|
+
changed_paths: uniqueOrdered(changedPaths),
|
|
3274
|
+
skipped_paths: uniqueOrdered(skippedPaths),
|
|
3275
|
+
pushed_file_count: files.length,
|
|
3276
|
+
};
|
|
3277
|
+
}
|
|
3278
|
+
|
|
2981
3279
|
async function listThreadComments(params) {
|
|
2982
3280
|
return listThreadCommentsImpl(params, buildRunnerDataDeps());
|
|
2983
3281
|
}
|
|
@@ -2986,6 +3284,22 @@ async function createThreadComment(params) {
|
|
|
2986
3284
|
return createThreadCommentImpl(params, buildRunnerDataDeps());
|
|
2987
3285
|
}
|
|
2988
3286
|
|
|
3287
|
+
async function createProjectWorkItem(params) {
|
|
3288
|
+
return createProjectWorkItemImpl(params, buildRunnerDataDeps());
|
|
3289
|
+
}
|
|
3290
|
+
|
|
3291
|
+
async function createProjectEvidence(params) {
|
|
3292
|
+
return createProjectEvidenceImpl(params, buildRunnerDataDeps());
|
|
3293
|
+
}
|
|
3294
|
+
|
|
3295
|
+
async function createWorkItemThread(params) {
|
|
3296
|
+
return createWorkItemThreadImpl(params, buildRunnerDataDeps());
|
|
3297
|
+
}
|
|
3298
|
+
|
|
3299
|
+
async function linkWorkItemEvidence(params) {
|
|
3300
|
+
return linkWorkItemEvidenceImpl(params, buildRunnerDataDeps());
|
|
3301
|
+
}
|
|
3302
|
+
|
|
2989
3303
|
function buildRunnerDeliveryDeps() {
|
|
2990
3304
|
return {
|
|
2991
3305
|
listProjectChatDestinations,
|
|
@@ -3005,6 +3319,7 @@ function buildRunnerExecutionDeps() {
|
|
|
3005
3319
|
auditRoleExecutionPlanWithAI,
|
|
3006
3320
|
auditDirectHumanReplyWithAI,
|
|
3007
3321
|
planRoleExecutionWithAI,
|
|
3322
|
+
repairRoleExecutionPlanWithAI,
|
|
3008
3323
|
normalizeRunnerRoleProfileName,
|
|
3009
3324
|
normalizeRunnerRoleProfile,
|
|
3010
3325
|
normalizeBotRunnerProjectMapping,
|
|
@@ -3019,12 +3334,19 @@ function buildRunnerExecutionDeps() {
|
|
|
3019
3334
|
resolveRunnerExecutionPlanForRole,
|
|
3020
3335
|
validateWorkspaceArtifacts,
|
|
3021
3336
|
tryJsonParse,
|
|
3337
|
+
createProjectEvidence,
|
|
3338
|
+
createProjectWorkItem,
|
|
3339
|
+
createWorkItemThread,
|
|
3340
|
+
linkWorkItemEvidence,
|
|
3341
|
+
replaceProjectCtxpackFiles,
|
|
3022
3342
|
};
|
|
3023
3343
|
}
|
|
3024
3344
|
|
|
3025
3345
|
function buildRunnerRuntimeDeps() {
|
|
3026
3346
|
return {
|
|
3027
3347
|
loadProviderEnvConfig,
|
|
3348
|
+
loadBotRunnerState,
|
|
3349
|
+
saveBotRunnerState,
|
|
3028
3350
|
getTelegramWebhookInfo,
|
|
3029
3351
|
deleteTelegramWebhook,
|
|
3030
3352
|
saveRunnerRouteState,
|
|
@@ -7015,7 +7337,7 @@ function syncCtxpackToLocalCache({
|
|
|
7015
7337
|
const versionID = String(ctxpack?.version_id || ctxpack?.current_version_id || "").trim();
|
|
7016
7338
|
const status = String(ctxpack?.status || "").trim() || "draft";
|
|
7017
7339
|
const files = normalizeCtxpackFiles(ctxpack?.files);
|
|
7018
|
-
const resolvedWorkspaceDir = resolveWorkspaceDir(workspaceDir);
|
|
7340
|
+
const resolvedWorkspaceDir = resolveStableProjectWorkspaceCandidate(projectID, workspaceDir) || resolveWorkspaceDir(workspaceDir);
|
|
7019
7341
|
const isHomeFallback = isHomeWorkspaceRoot(resolvedWorkspaceDir) && !allowHomeWorkspaceRoot();
|
|
7020
7342
|
const cacheDir = path.join(
|
|
7021
7343
|
isHomeFallback ? homeCtxpackCacheRootDir() : ctxpackCacheRootDir(resolvedWorkspaceDir),
|
|
@@ -7070,13 +7392,6 @@ function syncCtxpackToLocalCache({
|
|
|
7070
7392
|
} catch {
|
|
7071
7393
|
// Best-effort metadata refresh for workspace-root auto-detection.
|
|
7072
7394
|
}
|
|
7073
|
-
if (!isHomeFallback) {
|
|
7074
|
-
rememberProjectWorkspaceMapping({
|
|
7075
|
-
projectID,
|
|
7076
|
-
workspaceDir: resolvedWorkspaceDir,
|
|
7077
|
-
source: "ctxpack_sync_current",
|
|
7078
|
-
});
|
|
7079
|
-
}
|
|
7080
7395
|
return {
|
|
7081
7396
|
sync_status: isHomeFallback ? "home_fallback" : "current",
|
|
7082
7397
|
sync_message: isHomeFallback
|
|
@@ -7100,14 +7415,6 @@ function syncCtxpackToLocalCache({
|
|
|
7100
7415
|
|
|
7101
7416
|
fs.writeFileSync(metaPath, `${JSON.stringify(payload, null, 2)}\n`, "utf8");
|
|
7102
7417
|
fs.writeFileSync(workspaceMetaPath, `${JSON.stringify(payload, null, 2)}\n`, "utf8");
|
|
7103
|
-
if (!isHomeFallback) {
|
|
7104
|
-
rememberProjectWorkspaceMapping({
|
|
7105
|
-
projectID,
|
|
7106
|
-
workspaceDir: resolvedWorkspaceDir,
|
|
7107
|
-
source: "ctxpack_sync_write",
|
|
7108
|
-
});
|
|
7109
|
-
}
|
|
7110
|
-
|
|
7111
7418
|
return {
|
|
7112
7419
|
sync_status: isHomeFallback ? "home_fallback" : (previousMeta ? "updated" : "downloaded"),
|
|
7113
7420
|
sync_message: isHomeFallback
|
|
@@ -8083,6 +8390,8 @@ TELEGRAM_BOT_REVIEW_TOKEN=review-token
|
|
|
8083
8390
|
performLocalBotDelivery,
|
|
8084
8391
|
buildRunnerDeliveryDeps,
|
|
8085
8392
|
parseJSONText,
|
|
8393
|
+
archiveLocalTelegramMessagesForRoute,
|
|
8394
|
+
buildRunnerRuntimeDeps,
|
|
8086
8395
|
});
|
|
8087
8396
|
|
|
8088
8397
|
await runSelftestWorkspaceEnvScenarios(push, {
|
|
@@ -8193,14 +8502,14 @@ TELEGRAM_BOT_REVIEW_TOKEN=review-token
|
|
|
8193
8502
|
safeObject(runnerConfigAfterProjectTool.projectMappings)?.[selftestProjectID]?.workspaceDir || "";
|
|
8194
8503
|
const projectToolWorkspaceSignalOk =
|
|
8195
8504
|
String(safeObject(response).error || "").trim() === ""
|
|
8196
|
-
&&
|
|
8505
|
+
&& !String(storedProjectToolWorkspace || "").trim();
|
|
8197
8506
|
push(
|
|
8198
|
-
"
|
|
8507
|
+
"project_summary_workspace_signal_does_not_update_runner_mapping",
|
|
8199
8508
|
projectToolWorkspaceSignalOk,
|
|
8200
8509
|
`workspace=${storedProjectToolWorkspace || "(none)"} source=${String(safeObject(runnerConfigAfterProjectTool.projectMappings)?.[selftestProjectID]?.source || "(none)")}`,
|
|
8201
8510
|
);
|
|
8202
8511
|
} catch (err) {
|
|
8203
|
-
push("
|
|
8512
|
+
push("project_summary_workspace_signal_does_not_update_runner_mapping", false, String(err?.message || err));
|
|
8204
8513
|
} finally {
|
|
8205
8514
|
if (previousProjectToolUserProfile === undefined) delete process.env.USERPROFILE;
|
|
8206
8515
|
else process.env.USERPROFILE = previousProjectToolUserProfile;
|
|
@@ -8215,6 +8524,81 @@ TELEGRAM_BOT_REVIEW_TOKEN=review-token
|
|
|
8215
8524
|
}
|
|
8216
8525
|
}
|
|
8217
8526
|
|
|
8527
|
+
let projectToolWorkspaceDriftTempHome = "";
|
|
8528
|
+
const previousProjectToolDriftUserProfile = process.env.USERPROFILE;
|
|
8529
|
+
const previousProjectToolDriftHome = process.env.HOME;
|
|
8530
|
+
try {
|
|
8531
|
+
projectToolWorkspaceDriftTempHome = fs.mkdtempSync(path.join(os.tmpdir(), "metheus-project-tool-drift-selftest-"));
|
|
8532
|
+
const stableWorkspaceDir = path.join(projectToolWorkspaceDriftTempHome, "stable-workspace");
|
|
8533
|
+
const unrelatedWorkspaceDir = path.join(projectToolWorkspaceDriftTempHome, "unrelated-workspace");
|
|
8534
|
+
fs.mkdirSync(stableWorkspaceDir, { recursive: true });
|
|
8535
|
+
fs.mkdirSync(unrelatedWorkspaceDir, { recursive: true });
|
|
8536
|
+
process.env.USERPROFILE = projectToolWorkspaceDriftTempHome;
|
|
8537
|
+
process.env.HOME = projectToolWorkspaceDriftTempHome;
|
|
8538
|
+
rememberProjectWorkspaceMapping({
|
|
8539
|
+
projectID: selftestProjectID,
|
|
8540
|
+
workspaceDir: stableWorkspaceDir,
|
|
8541
|
+
source: "manual_override",
|
|
8542
|
+
});
|
|
8543
|
+
await handleLocalProjectToolDispatchImpl(
|
|
8544
|
+
{
|
|
8545
|
+
requestObj: {
|
|
8546
|
+
jsonrpc: "2.0",
|
|
8547
|
+
id: 2,
|
|
8548
|
+
method: "tools/call",
|
|
8549
|
+
params: {
|
|
8550
|
+
name: "project.summary",
|
|
8551
|
+
arguments: {
|
|
8552
|
+
project_id: selftestProjectID,
|
|
8553
|
+
},
|
|
8554
|
+
},
|
|
8555
|
+
},
|
|
8556
|
+
toolName: "project.summary",
|
|
8557
|
+
toolArgs: {
|
|
8558
|
+
project_id: selftestProjectID,
|
|
8559
|
+
},
|
|
8560
|
+
args: {
|
|
8561
|
+
baseURL: DEFAULT_SITE_URL,
|
|
8562
|
+
timeoutSeconds: 30,
|
|
8563
|
+
},
|
|
8564
|
+
token: "selftest-token",
|
|
8565
|
+
workspaceDir: unrelatedWorkspaceDir,
|
|
8566
|
+
workspaceSignalTrusted: true,
|
|
8567
|
+
},
|
|
8568
|
+
{
|
|
8569
|
+
...buildLocalProjectDispatchDeps(),
|
|
8570
|
+
loadProjectSummaryForTool: async () => ({
|
|
8571
|
+
project_id: selftestProjectID,
|
|
8572
|
+
name: "Selftest Project",
|
|
8573
|
+
access: "granted",
|
|
8574
|
+
}),
|
|
8575
|
+
buildProjectSummaryText: (summary) => `Project: ${String(summary?.name || "").trim()}`,
|
|
8576
|
+
},
|
|
8577
|
+
);
|
|
8578
|
+
const runnerConfigAfterProjectToolDrift = loadBotRunnerConfig({ persistIfNeeded: true });
|
|
8579
|
+
const storedWorkspaceAfterDrift =
|
|
8580
|
+
safeObject(runnerConfigAfterProjectToolDrift.projectMappings)?.[selftestProjectID]?.workspaceDir || "";
|
|
8581
|
+
push(
|
|
8582
|
+
"project_summary_workspace_signal_preserves_existing_manual_mapping",
|
|
8583
|
+
normalizedPathForCompare(storedWorkspaceAfterDrift) === normalizedPathForCompare(stableWorkspaceDir),
|
|
8584
|
+
`stored=${storedWorkspaceAfterDrift || "(none)"} stable=${stableWorkspaceDir}`,
|
|
8585
|
+
);
|
|
8586
|
+
} catch (err) {
|
|
8587
|
+
push("project_summary_workspace_signal_preserves_existing_manual_mapping", false, String(err?.message || err));
|
|
8588
|
+
} finally {
|
|
8589
|
+
if (previousProjectToolDriftUserProfile === undefined) delete process.env.USERPROFILE;
|
|
8590
|
+
else process.env.USERPROFILE = previousProjectToolDriftUserProfile;
|
|
8591
|
+
if (previousProjectToolDriftHome === undefined) delete process.env.HOME;
|
|
8592
|
+
else process.env.HOME = previousProjectToolDriftHome;
|
|
8593
|
+
if (projectToolWorkspaceDriftTempHome) {
|
|
8594
|
+
try {
|
|
8595
|
+
fs.rmSync(projectToolWorkspaceDriftTempHome, { recursive: true, force: true });
|
|
8596
|
+
} catch {
|
|
8597
|
+
// ignore selftest cleanup error
|
|
8598
|
+
}
|
|
8599
|
+
}
|
|
8600
|
+
}
|
|
8601
|
+
|
|
8218
8602
|
await runSelftestBotCommands(push, {
|
|
8219
8603
|
cliPath: fileURLToPath(import.meta.url),
|
|
8220
8604
|
parseSimpleEnvText,
|