metheus-governance-mcp-cli 0.2.159 → 0.2.161
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.md +5 -0
- package/lib/local-ai-adapters.mjs +2 -2
- package/lib/runner-orchestration.mjs +66 -1
- package/lib/selftest-runner-scenarios.mjs +824 -33
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -516,6 +516,8 @@ Workspace/source-of-truth model:
|
|
|
516
516
|
- `ctxpack` is project guidance/specification content stored inside the workspace and on the server; it is not the local workspace authority
|
|
517
517
|
- `.metheus/ctxpack-cache/` is local synced cache for ctxpack content; do not treat it as the final authoring target for project documents
|
|
518
518
|
- `.metheus/runner-runtime/local-ai-scratch/` is local runner scratch space for AI CLI runtime support; do not treat it as a project artifact location or a user-facing document location
|
|
519
|
+
- `.metheus_ctxpack_sync.json` is sync metadata only; do not treat it as an official ctxpack source document
|
|
520
|
+
- Official ctxpack authoring may live under normal project files or `.metheus/...` authoring paths, but exclude cache/runtime/metadata paths such as `.metheus/ctxpack-cache/`, `.metheus/runner-runtime/`, and `.metheus_ctxpack_sync.json`
|
|
519
521
|
|
|
520
522
|
Role profile fields:
|
|
521
523
|
- `client`: `gpt`, `claude`, `gemini`, or `sample`
|
|
@@ -664,6 +666,9 @@ metheus-governance-mcp-cli ctxpack pull --project-id <project_uuid> --base-url h
|
|
|
664
666
|
Canonical ctxpack write tool name:
|
|
665
667
|
- use `ctxpack.update`
|
|
666
668
|
- do not use legacy names like `ctxpack.push` or `ctxpack.save`
|
|
669
|
+
- chat/runner guidance:
|
|
670
|
+
- use `ctxpack.update` for official project guidance/instruction/policy/spec requests
|
|
671
|
+
- do not force `ctxpack.update` for normal README or generic implementation-document requests unless the request explicitly asks for official project guidance
|
|
667
672
|
|
|
668
673
|
When `workitem.list` returns empty, proxy appends a hint to call `project.summary` first.
|
|
669
674
|
|
|
@@ -1499,7 +1499,7 @@ export function buildLocalBotPrompt(payload, { terse = true } = {}) {
|
|
|
1499
1499
|
"Do the work for this step now. Do not say you will start later, wait, come back later, or plan first.",
|
|
1500
1500
|
"If this is a worker step, actually create/update the required project artifacts now and include them in artifacts.",
|
|
1501
1501
|
executionStep.ctxpack_update_required === true || executionStep.ctxpackUpdateRequired === true
|
|
1502
|
-
? "This step must update project guidance/specification content that belongs in ctxpack-backed workspace source files. Return every official guide/instruction/spec file in ctxpack_files with path, content, and doc_type so the runner can materialize those files and update server ctxpack. Prefer official source paths under .metheus/project-guides/, .metheus/rules/, or other explicit workspace authoring paths when you are writing project guidance or instructions. Do not treat .metheus/ctxpack-cache, .metheus/runner-runtime/local-ai-scratch, .metheus_ctxpack_sync.json, or .claude/ as final authoring targets."
|
|
1502
|
+
? "This step must update project guidance/specification content that belongs in ctxpack-backed workspace source files. Return every official guide/instruction/spec file in ctxpack_files with path, content, and doc_type so the runner can materialize those files and update server ctxpack. A ctxpack update step is incomplete if ctxpack_files is empty. Prefer official source paths under .metheus/project-guides/, .metheus/rules/, or other explicit workspace authoring paths when you are writing project guidance or instructions. Do not treat .metheus/ctxpack-cache, .metheus/runner-runtime/local-ai-scratch, .metheus_ctxpack_sync.json, or .claude/ as final authoring targets."
|
|
1503
1503
|
: "",
|
|
1504
1504
|
executionStep.work_items_required === true || executionStep.workItemsRequired === true
|
|
1505
1505
|
? "This step must also break the work into executable governance work items. Return them in work_items with atomic titles and useful descriptions."
|
|
@@ -1559,7 +1559,7 @@ export function buildLocalBotPrompt(payload, { terse = true } = {}) {
|
|
|
1559
1559
|
"- Do not claim that a file, plan, document, or code change is complete unless the corresponding artifact path is present in artifacts.",
|
|
1560
1560
|
"",
|
|
1561
1561
|
isInternalExecutionStep
|
|
1562
|
-
? "Return JSON only in one line: {\"reply\":\"what was completed in this step\",\"artifacts\":[{\"path\":\"relative/or/absolute/path\",\"kind\":\"plan|code|doc|spec|test\",\"operation\":\"create|update|delete\"}],\"ctxpack_files\":[{\"path\":\"relative/path.md\",\"content\":\"full document text\",\"doc_type\":\"guide|readme|agenda|rule|architecture|manifest\",\"operation\":\"create|update|delete\"}],\"work_items\":[{\"title\":\"short atomic task\",\"description\":\"useful implementation detail\"}],\"contract\":{\"type\":\"direct_result|summary_request|final_summary\",\"actionable\":true,\"summary_bot\":\"username\",\"next_responders\":[\"username\"]}}. Use ctxpack_files when ctxpack-backed guidance/instruction files must be authored. Use artifacts: [] only if this step truly changes no project files, and use work_items: [] only if this step truly creates no governance tasks."
|
|
1562
|
+
? "Return JSON only in one line: {\"reply\":\"what was completed in this step\",\"artifacts\":[{\"path\":\"relative/or/absolute/path\",\"kind\":\"plan|code|doc|spec|test\",\"operation\":\"create|update|delete\"}],\"ctxpack_files\":[{\"path\":\"relative/path.md\",\"content\":\"full document text\",\"doc_type\":\"guide|readme|agenda|rule|architecture|manifest\",\"operation\":\"create|update|delete\"}],\"work_items\":[{\"title\":\"short atomic task\",\"description\":\"useful implementation detail\"}],\"contract\":{\"type\":\"direct_result|summary_request|final_summary\",\"actionable\":true,\"summary_bot\":\"username\",\"next_responders\":[\"username\"]}}. Use ctxpack_files when ctxpack-backed guidance/instruction files must be authored. If execution_step.ctxpack_update_required is true, ctxpack_files must not be empty. Use artifacts: [] only if this step truly changes no project files, and use work_items: [] only if this step truly creates no governance tasks."
|
|
1563
1563
|
: responseContract.must_reply === true
|
|
1564
1564
|
? "Return JSON only in one line: {\"reply\":\"...\",\"artifacts\":[],\"ctxpack_files\":[],\"work_items\":[]} or {\"reply\":\"...\",\"artifacts\":[],\"ctxpack_files\":[],\"work_items\":[],\"contract\":{\"type\":\"direct_result|delegation|summary_request|final_summary\",\"actionable\":true,\"assignments\":[{\"target_bot\":\"username\",\"task\":\"...\"}],\"summary_bot\":\"username\",\"next_responders\":[\"username\"]}}."
|
|
1565
1565
|
: terse
|
|
@@ -174,6 +174,21 @@ function buildExecutionFailureReplyText(detail) {
|
|
|
174
174
|
if (/ctxpack\.update requires project ctxpack write access|ctxpack write access|ctxpack update permission|forbidden/i.test(normalizedDetail) && /ctxpack/i.test(normalizedDetail)) {
|
|
175
175
|
return "I could not complete this request because the authenticated actor does not have ctxpack update permission for this project.";
|
|
176
176
|
}
|
|
177
|
+
if (/ctxpack version_id is missing/i.test(normalizedDetail)) {
|
|
178
|
+
return "I could not complete this request because the project ctxpack version is missing. Refresh the project summary or ctxpack first.";
|
|
179
|
+
}
|
|
180
|
+
if (/ctxpack source path is not an official project authoring target|ctxpack source path is outside workspace|ctxpack source file \".*\" is missing content|ctxpack_files entry is missing path|ctxpack update step returned no ctxpack_files|did not produce any official ctxpack source files|ctxpack update produced no pushable ctxpack files|ctxpack\.update requires at least one validated official ctxpack source file/i.test(normalizedDetail)) {
|
|
181
|
+
return "I could not complete this request because the ctxpack update did not produce valid official ctxpack source files inside the workspace.";
|
|
182
|
+
}
|
|
183
|
+
if (/ctxpack source artifact is missing from workspace/i.test(normalizedDetail)) {
|
|
184
|
+
return "I could not complete this request because the ctxpack source files were not present in the workspace when the update ran.";
|
|
185
|
+
}
|
|
186
|
+
if (/invalid json response from ctxpack update/i.test(normalizedDetail)) {
|
|
187
|
+
return "I could not complete this request because the server returned an invalid response for the ctxpack update.";
|
|
188
|
+
}
|
|
189
|
+
if (/ctxpack/i.test(normalizedDetail) && /ECONNABORTED|ECONNRESET|ETIMEDOUT|http timeout|socket hang up|aborted/i.test(normalizedDetail)) {
|
|
190
|
+
return "I could not complete this request because the server ctxpack update request did not complete.";
|
|
191
|
+
}
|
|
177
192
|
if (/ctxpack/i.test(normalizedDetail)) {
|
|
178
193
|
return "I could not complete this request because the required ctxpack update did not succeed.";
|
|
179
194
|
}
|
|
@@ -875,6 +890,18 @@ async function pushExecutionStepCtxpack({
|
|
|
875
890
|
errors: ["runner execution dependency \"replaceProjectCtxpackFiles\" is required for ctxpack update steps"],
|
|
876
891
|
};
|
|
877
892
|
}
|
|
893
|
+
const pushableArtifacts = ensureArray(validatedArtifacts)
|
|
894
|
+
.filter((item) => {
|
|
895
|
+
const artifact = safeObject(item);
|
|
896
|
+
const artifactPath = String(artifact.relativePath || artifact.path || "").trim();
|
|
897
|
+
return artifactPath && !isDisallowedCtxpackSourceRelativePath(artifactPath);
|
|
898
|
+
});
|
|
899
|
+
if (!pushableArtifacts.length) {
|
|
900
|
+
return {
|
|
901
|
+
ctxpack: null,
|
|
902
|
+
errors: ["ctxpack.update requires at least one validated official ctxpack source file inside the workspace"],
|
|
903
|
+
};
|
|
904
|
+
}
|
|
878
905
|
try {
|
|
879
906
|
const response = await replaceProjectCtxpackFiles({
|
|
880
907
|
siteBaseURL: runtime.baseURL,
|
|
@@ -883,7 +910,7 @@ async function pushExecutionStepCtxpack({
|
|
|
883
910
|
actorUserID: safeObject(runtime.actor).user_id,
|
|
884
911
|
projectID: normalizedRoute.projectID,
|
|
885
912
|
workspaceDir: String(runtime.workspaceDir || "").trim() || String(normalizedRoute.workspaceDir || "").trim(),
|
|
886
|
-
artifacts:
|
|
913
|
+
artifacts: pushableArtifacts,
|
|
887
914
|
});
|
|
888
915
|
return {
|
|
889
916
|
ctxpack: {
|
|
@@ -907,6 +934,36 @@ async function pushExecutionStepCtxpack({
|
|
|
907
934
|
],
|
|
908
935
|
};
|
|
909
936
|
}
|
|
937
|
+
if (/ctxpack version_id is missing/i.test(detail)) {
|
|
938
|
+
return {
|
|
939
|
+
ctxpack: null,
|
|
940
|
+
errors: ["ctxpack version_id is missing; run ctxpack pull or refresh project summary first"],
|
|
941
|
+
};
|
|
942
|
+
}
|
|
943
|
+
if (/ctxpack update produced no pushable ctxpack files/i.test(detail)) {
|
|
944
|
+
return {
|
|
945
|
+
ctxpack: null,
|
|
946
|
+
errors: ["ctxpack update produced no pushable ctxpack source files"],
|
|
947
|
+
};
|
|
948
|
+
}
|
|
949
|
+
if (/ctxpack source artifact is missing from workspace/i.test(detail)) {
|
|
950
|
+
return {
|
|
951
|
+
ctxpack: null,
|
|
952
|
+
errors: [detail],
|
|
953
|
+
};
|
|
954
|
+
}
|
|
955
|
+
if (/invalid json response from ctxpack update/i.test(detail)) {
|
|
956
|
+
return {
|
|
957
|
+
ctxpack: null,
|
|
958
|
+
errors: ["server ctxpack update returned an invalid JSON response"],
|
|
959
|
+
};
|
|
960
|
+
}
|
|
961
|
+
if (/ECONNABORTED|ECONNRESET|ETIMEDOUT|http timeout|socket hang up|aborted/i.test(detail)) {
|
|
962
|
+
return {
|
|
963
|
+
ctxpack: null,
|
|
964
|
+
errors: [`ctxpack.update server request aborted before completion: ${detail}`],
|
|
965
|
+
};
|
|
966
|
+
}
|
|
910
967
|
return {
|
|
911
968
|
ctxpack: null,
|
|
912
969
|
errors: [`failed to update project ctxpack for step "${String(safeObject(step).goal || safeObject(step).role || "ctxpack update").trim()}": ${detail}`],
|
|
@@ -1573,6 +1630,14 @@ async function maybeExecuteDynamicRolePlan({
|
|
|
1573
1630
|
) {
|
|
1574
1631
|
stepErrors.push(`${String(step.role || "").trim()} step only produced internal runtime scratch files; create official workspace files for ctxpack source material`);
|
|
1575
1632
|
}
|
|
1633
|
+
if (safeObject(step).ctxpackUpdateRequired === true) {
|
|
1634
|
+
if (ensureArray(stepResult?.ctxpackFiles).length === 0) {
|
|
1635
|
+
stepErrors.push(`${String(step.role || "").trim()} step returned no ctxpack_files for a required ctxpack update`);
|
|
1636
|
+
}
|
|
1637
|
+
if (ensureArray(ctxpackMaterialization.artifacts).length === 0) {
|
|
1638
|
+
stepErrors.push(`${String(step.role || "").trim()} step did not produce any official ctxpack source files inside the workspace`);
|
|
1639
|
+
}
|
|
1640
|
+
}
|
|
1576
1641
|
if (requiresArtifactsForExecutionStep(step) && ensureArray(stepValidation.artifacts).length === 0) {
|
|
1577
1642
|
if (reportedArtifactPaths.length > 0) {
|
|
1578
1643
|
stepErrors.push(`${String(step.role || "").trim()} step reported project artifacts that were not observed in the workspace`);
|
|
@@ -4137,6 +4137,13 @@ export async function runSelftestRunnerScenarios(push, deps) {
|
|
|
4137
4137
|
skip: false,
|
|
4138
4138
|
reply: "Created the implementation plan and broke the work into governance tasks.",
|
|
4139
4139
|
artifacts: [{ path: "doc/implementation-plan.md", kind: "plan", operation: "create" }],
|
|
4140
|
+
ctxpackFiles: [
|
|
4141
|
+
{
|
|
4142
|
+
path: "doc/implementation-plan.md",
|
|
4143
|
+
docType: "guide",
|
|
4144
|
+
content: "# Implementation Plan\n",
|
|
4145
|
+
},
|
|
4146
|
+
],
|
|
4140
4147
|
workItems: [
|
|
4141
4148
|
{ title: "Create API specification", description: "Draft the first API contract from the implementation plan." },
|
|
4142
4149
|
{ title: "Implement onboarding flow", description: "Build the first user onboarding screens and state handling." },
|
|
@@ -4575,6 +4582,13 @@ export async function runSelftestRunnerScenarios(push, deps) {
|
|
|
4575
4582
|
skip: false,
|
|
4576
4583
|
reply: "Updated the implementation guide in the workspace.",
|
|
4577
4584
|
artifacts: [{ path: "docs/implementation-guide.md", kind: "doc", operation: "update" }],
|
|
4585
|
+
ctxpackFiles: [
|
|
4586
|
+
{
|
|
4587
|
+
path: "docs/implementation-guide.md",
|
|
4588
|
+
docType: "guide",
|
|
4589
|
+
content: "# Implementation Guide\n",
|
|
4590
|
+
},
|
|
4591
|
+
],
|
|
4578
4592
|
workItems: [],
|
|
4579
4593
|
};
|
|
4580
4594
|
}
|
|
@@ -4987,6 +5001,13 @@ export async function runSelftestRunnerScenarios(push, deps) {
|
|
|
4987
5001
|
skip: false,
|
|
4988
5002
|
reply: "Updated the implementation guide in the workspace.",
|
|
4989
5003
|
artifacts: [{ path: "docs/implementation-guide.md", kind: "doc", operation: "update" }],
|
|
5004
|
+
ctxpackFiles: [
|
|
5005
|
+
{
|
|
5006
|
+
path: "docs/implementation-guide.md",
|
|
5007
|
+
docType: "guide",
|
|
5008
|
+
content: "# Implementation Guide\n",
|
|
5009
|
+
},
|
|
5010
|
+
],
|
|
4990
5011
|
workItems: [],
|
|
4991
5012
|
};
|
|
4992
5013
|
}
|
|
@@ -5081,13 +5102,13 @@ export async function runSelftestRunnerScenarios(push, deps) {
|
|
|
5081
5102
|
let deliveryCalls = 0;
|
|
5082
5103
|
let deliveredText = "";
|
|
5083
5104
|
let ctxpackUpdateCalls = 0;
|
|
5084
|
-
const workspaceDir = fs.mkdtempSync(path.join(os.tmpdir(), "metheus-runner-selftest-ctxpack-
|
|
5105
|
+
const workspaceDir = fs.mkdtempSync(path.join(os.tmpdir(), "metheus-runner-selftest-ctxpack-requires-files-"));
|
|
5085
5106
|
const expectedPath = ".metheus/project-guides/project-operating-guide.md";
|
|
5086
5107
|
const expectedAbsolutePath = path.join(workspaceDir, ".metheus", "project-guides", "project-operating-guide.md");
|
|
5087
5108
|
const processed = await processRunnerSelectedRecord({
|
|
5088
|
-
routeKey: "single-bot-ctxpack-
|
|
5109
|
+
routeKey: "single-bot-ctxpack-requires-files-key",
|
|
5089
5110
|
normalizedRoute: normalizeRunnerRoute({
|
|
5090
|
-
name: "telegram-monitor-single-bot-ctxpack-
|
|
5111
|
+
name: "telegram-monitor-single-bot-ctxpack-requires-files",
|
|
5091
5112
|
project_id: selftestProjectID,
|
|
5092
5113
|
provider: "telegram",
|
|
5093
5114
|
role: "monitor",
|
|
@@ -5110,16 +5131,16 @@ export async function runSelftestRunnerScenarios(push, deps) {
|
|
|
5110
5131
|
dry_run_delivery: true,
|
|
5111
5132
|
}),
|
|
5112
5133
|
selectedRecord: {
|
|
5113
|
-
id: "comment-single-bot-ctxpack-
|
|
5114
|
-
createdAt: "2026-03-17T01:00:05.
|
|
5134
|
+
id: "comment-single-bot-ctxpack-requires-files",
|
|
5135
|
+
createdAt: "2026-03-17T01:00:05.625Z",
|
|
5115
5136
|
parsedArchive: {
|
|
5116
5137
|
kind: "telegram_message",
|
|
5117
5138
|
chatID: "-100123",
|
|
5118
5139
|
chatType: "supergroup",
|
|
5119
5140
|
senderIsBot: false,
|
|
5120
|
-
body: "@RyoAI_bot
|
|
5141
|
+
body: "@RyoAI_bot update the operating guide in ctxpack.",
|
|
5121
5142
|
mentionUsernames: ["RyoAI_bot"],
|
|
5122
|
-
messageID:
|
|
5143
|
+
messageID: 13095,
|
|
5123
5144
|
},
|
|
5124
5145
|
},
|
|
5125
5146
|
pendingOrdered: [],
|
|
@@ -5167,7 +5188,197 @@ export async function runSelftestRunnerScenarios(push, deps) {
|
|
|
5167
5188
|
if (stepRole === "monitor") {
|
|
5168
5189
|
return {
|
|
5169
5190
|
skip: false,
|
|
5170
|
-
reply: "
|
|
5191
|
+
reply: "Classified this as a ctxpack guidance update request.",
|
|
5192
|
+
};
|
|
5193
|
+
}
|
|
5194
|
+
if (stepRole === "worker") {
|
|
5195
|
+
fs.mkdirSync(path.dirname(expectedAbsolutePath), { recursive: true });
|
|
5196
|
+
fs.writeFileSync(expectedAbsolutePath, "# Project Operating Guide\n", "utf8");
|
|
5197
|
+
return {
|
|
5198
|
+
skip: false,
|
|
5199
|
+
reply: "Updated the guide file.",
|
|
5200
|
+
artifacts: [{ path: expectedPath, kind: "doc", operation: "update" }],
|
|
5201
|
+
workItems: [],
|
|
5202
|
+
};
|
|
5203
|
+
}
|
|
5204
|
+
throw new Error(`unexpected step role ${stepRole}`);
|
|
5205
|
+
},
|
|
5206
|
+
performLocalBotDelivery: async ({ text }) => {
|
|
5207
|
+
deliveryCalls += 1;
|
|
5208
|
+
deliveredText = String(text || "");
|
|
5209
|
+
return {
|
|
5210
|
+
delivery: { dryRun: true, body: {} },
|
|
5211
|
+
archive: {},
|
|
5212
|
+
};
|
|
5213
|
+
},
|
|
5214
|
+
serializeRunnerTriggerPolicy: (value) => value,
|
|
5215
|
+
serializeRunnerArchivePolicy: (value) => value,
|
|
5216
|
+
buildRunnerExecutionDeps: () => ({
|
|
5217
|
+
validateWorkspaceArtifacts,
|
|
5218
|
+
analyzeHumanConversationIntentWithAI: async () => ({
|
|
5219
|
+
mode: "single_bot",
|
|
5220
|
+
lead_bot: "ryoai_bot",
|
|
5221
|
+
participants: ["ryoai_bot"],
|
|
5222
|
+
initial_responders: ["ryoai_bot"],
|
|
5223
|
+
allowed_responders: ["ryoai_bot"],
|
|
5224
|
+
summary_bot: "",
|
|
5225
|
+
allow_bot_to_bot: false,
|
|
5226
|
+
reply_expectation: "actionable",
|
|
5227
|
+
}),
|
|
5228
|
+
planRoleExecutionWithAI: async () => ({
|
|
5229
|
+
requiresExecution: true,
|
|
5230
|
+
summaryRole: "worker",
|
|
5231
|
+
steps: [
|
|
5232
|
+
{ role: "monitor", goal: "Inspect the guidance request" },
|
|
5233
|
+
{
|
|
5234
|
+
role: "worker",
|
|
5235
|
+
goal: "Update the operating guide in ctxpack",
|
|
5236
|
+
artifactsRequired: true,
|
|
5237
|
+
ctxpackUpdateRequired: true,
|
|
5238
|
+
outputHint: expectedPath,
|
|
5239
|
+
},
|
|
5240
|
+
],
|
|
5241
|
+
}),
|
|
5242
|
+
auditRoleExecutionPlanWithAI: async () => ({
|
|
5243
|
+
requires_execution: true,
|
|
5244
|
+
requires_worker_step: true,
|
|
5245
|
+
requires_ctxpack_update_step: true,
|
|
5246
|
+
plan_satisfies_request: true,
|
|
5247
|
+
reason: "worker ctxpack step present",
|
|
5248
|
+
}),
|
|
5249
|
+
resolveRunnerExecutionPlanForRole: (_route, _bot, roleName) => ({
|
|
5250
|
+
mode: "role_profile",
|
|
5251
|
+
roleProfileName: String(roleName || "").trim(),
|
|
5252
|
+
roleProfile: {
|
|
5253
|
+
client: "sample",
|
|
5254
|
+
model: "",
|
|
5255
|
+
permissionMode: String(roleName || "").trim() === "worker" ? "workspace_write" : "read_only",
|
|
5256
|
+
reasoningEffort: String(roleName || "").trim() === "worker" ? "medium" : "low",
|
|
5257
|
+
},
|
|
5258
|
+
workspaceDir,
|
|
5259
|
+
workspaceSource: "selftest",
|
|
5260
|
+
usedCommandFallback: false,
|
|
5261
|
+
}),
|
|
5262
|
+
replaceProjectCtxpackFiles: async () => {
|
|
5263
|
+
ctxpackUpdateCalls += 1;
|
|
5264
|
+
throw new Error("ctxpack.update should not run when ctxpack_files are missing");
|
|
5265
|
+
},
|
|
5266
|
+
loadBotRunnerConfig: () => ({
|
|
5267
|
+
roleProfiles: {},
|
|
5268
|
+
botBindings: {},
|
|
5269
|
+
routes: [],
|
|
5270
|
+
projectMappings: {},
|
|
5271
|
+
}),
|
|
5272
|
+
}),
|
|
5273
|
+
buildRunnerDeliveryDeps: () => ({}),
|
|
5274
|
+
buildRunnerRuntimeDeps: () => ({}),
|
|
5275
|
+
resolveConversationPeerBots: () => [
|
|
5276
|
+
{ id: "bot-lead-1", name: "RyoAI_bot" },
|
|
5277
|
+
],
|
|
5278
|
+
},
|
|
5279
|
+
});
|
|
5280
|
+
push(
|
|
5281
|
+
"single_bot_ctxpack_update_requires_ctxpack_files",
|
|
5282
|
+
processed.kind === "error"
|
|
5283
|
+
&& String(processed.result?.outcome || "") === "execution_failed"
|
|
5284
|
+
&& ctxpackUpdateCalls === 0
|
|
5285
|
+
&& deliveryCalls === 1
|
|
5286
|
+
&& /valid official ctxpack source files/i.test(deliveredText),
|
|
5287
|
+
`kind=${String(processed.kind || "(none)")} outcome=${String(processed.result?.outcome || "(none)")} ctxpack_updates=${ctxpackUpdateCalls} delivery_calls=${deliveryCalls} delivered=${deliveredText} detail=${String(processed.result?.detail || "(none)")}`,
|
|
5288
|
+
);
|
|
5289
|
+
} catch (err) {
|
|
5290
|
+
push("single_bot_ctxpack_update_requires_ctxpack_files", false, String(err?.message || err));
|
|
5291
|
+
}
|
|
5292
|
+
|
|
5293
|
+
try {
|
|
5294
|
+
let deliveryCalls = 0;
|
|
5295
|
+
let deliveredText = "";
|
|
5296
|
+
const workspaceDir = fs.mkdtempSync(path.join(os.tmpdir(), "metheus-runner-selftest-ctxpack-version-missing-"));
|
|
5297
|
+
const expectedPath = ".metheus/project-guides/project-operating-guide.md";
|
|
5298
|
+
const processed = await processRunnerSelectedRecord({
|
|
5299
|
+
routeKey: "single-bot-ctxpack-version-missing-key",
|
|
5300
|
+
normalizedRoute: normalizeRunnerRoute({
|
|
5301
|
+
name: "telegram-monitor-single-bot-ctxpack-version-missing",
|
|
5302
|
+
project_id: selftestProjectID,
|
|
5303
|
+
provider: "telegram",
|
|
5304
|
+
role: "monitor",
|
|
5305
|
+
role_profile: "monitor",
|
|
5306
|
+
destination_id: "dest-1",
|
|
5307
|
+
destination_label: "Main Room",
|
|
5308
|
+
server_bot_name: "RyoAI_bot",
|
|
5309
|
+
server_bot_id: "bot-lead-1",
|
|
5310
|
+
trigger_policy: {
|
|
5311
|
+
mentions_only: true,
|
|
5312
|
+
direct_messages: true,
|
|
5313
|
+
reply_to_bot_messages: true,
|
|
5314
|
+
},
|
|
5315
|
+
archive_policy: {
|
|
5316
|
+
mirror_replies: true,
|
|
5317
|
+
dedupe_inbound: true,
|
|
5318
|
+
dedupe_outbound: true,
|
|
5319
|
+
skip_bot_messages: true,
|
|
5320
|
+
},
|
|
5321
|
+
dry_run_delivery: true,
|
|
5322
|
+
}),
|
|
5323
|
+
selectedRecord: {
|
|
5324
|
+
id: "comment-single-bot-ctxpack-version-missing",
|
|
5325
|
+
createdAt: "2026-03-17T01:00:05.675Z",
|
|
5326
|
+
parsedArchive: {
|
|
5327
|
+
kind: "telegram_message",
|
|
5328
|
+
chatID: "-100123",
|
|
5329
|
+
chatType: "supergroup",
|
|
5330
|
+
senderIsBot: false,
|
|
5331
|
+
body: "@RyoAI_bot update the operating guide in ctxpack.",
|
|
5332
|
+
mentionUsernames: ["RyoAI_bot"],
|
|
5333
|
+
messageID: 13096,
|
|
5334
|
+
},
|
|
5335
|
+
},
|
|
5336
|
+
pendingOrdered: [],
|
|
5337
|
+
bot: {
|
|
5338
|
+
id: "bot-lead-1",
|
|
5339
|
+
name: "RyoAI_bot",
|
|
5340
|
+
username: "RyoAI_bot",
|
|
5341
|
+
role: "monitor",
|
|
5342
|
+
provider: "telegram",
|
|
5343
|
+
},
|
|
5344
|
+
destination: {
|
|
5345
|
+
id: "dest-1",
|
|
5346
|
+
label: "Main Room",
|
|
5347
|
+
provider: "telegram",
|
|
5348
|
+
chatID: "-100123",
|
|
5349
|
+
},
|
|
5350
|
+
archiveThread: {
|
|
5351
|
+
threadID: "thread-1",
|
|
5352
|
+
workItemID: "work-item-1",
|
|
5353
|
+
},
|
|
5354
|
+
executionPlan: {
|
|
5355
|
+
mode: "role_profile",
|
|
5356
|
+
roleProfileName: "monitor",
|
|
5357
|
+
roleProfile: {
|
|
5358
|
+
client: "sample",
|
|
5359
|
+
model: "",
|
|
5360
|
+
permissionMode: "read_only",
|
|
5361
|
+
reasoningEffort: "low",
|
|
5362
|
+
},
|
|
5363
|
+
workspaceDir,
|
|
5364
|
+
workspaceSource: "selftest",
|
|
5365
|
+
usedCommandFallback: false,
|
|
5366
|
+
},
|
|
5367
|
+
runtime: {
|
|
5368
|
+
baseURL: "https://example.test",
|
|
5369
|
+
token: "selftest-token",
|
|
5370
|
+
timeoutSeconds: 30,
|
|
5371
|
+
actor: { user_id: "user-1" },
|
|
5372
|
+
},
|
|
5373
|
+
deps: {
|
|
5374
|
+
saveRunnerRouteState: () => {},
|
|
5375
|
+
startRunnerTypingHeartbeat: () => ({ async stop() {} }),
|
|
5376
|
+
runRunnerAIExecution: async ({ route }) => {
|
|
5377
|
+
const stepRole = String(route?.role || "").trim().toLowerCase();
|
|
5378
|
+
if (stepRole === "monitor") {
|
|
5379
|
+
return {
|
|
5380
|
+
skip: false,
|
|
5381
|
+
reply: "Classified this as a ctxpack guidance update request.",
|
|
5171
5382
|
};
|
|
5172
5383
|
}
|
|
5173
5384
|
if (stepRole === "worker") {
|
|
@@ -5179,7 +5390,7 @@ export async function runSelftestRunnerScenarios(push, deps) {
|
|
|
5179
5390
|
{
|
|
5180
5391
|
path: expectedPath,
|
|
5181
5392
|
docType: "guide",
|
|
5182
|
-
content: "# Project Operating Guide\n\
|
|
5393
|
+
content: "# Project Operating Guide\n\nVersion check required.\n",
|
|
5183
5394
|
},
|
|
5184
5395
|
],
|
|
5185
5396
|
workItems: [],
|
|
@@ -5216,16 +5427,19 @@ export async function runSelftestRunnerScenarios(push, deps) {
|
|
|
5216
5427
|
{ role: "monitor", goal: "Inspect the guidance request" },
|
|
5217
5428
|
{
|
|
5218
5429
|
role: "worker",
|
|
5219
|
-
goal: "
|
|
5430
|
+
goal: "Update the operating guide in ctxpack",
|
|
5220
5431
|
artifactsRequired: true,
|
|
5432
|
+
ctxpackUpdateRequired: true,
|
|
5433
|
+
outputHint: expectedPath,
|
|
5221
5434
|
},
|
|
5222
5435
|
],
|
|
5223
5436
|
}),
|
|
5224
5437
|
auditRoleExecutionPlanWithAI: async () => ({
|
|
5225
5438
|
requires_execution: true,
|
|
5226
5439
|
requires_worker_step: true,
|
|
5440
|
+
requires_ctxpack_update_step: true,
|
|
5227
5441
|
plan_satisfies_request: true,
|
|
5228
|
-
reason: "worker step present",
|
|
5442
|
+
reason: "worker ctxpack step present",
|
|
5229
5443
|
}),
|
|
5230
5444
|
resolveRunnerExecutionPlanForRole: (_route, _bot, roleName) => ({
|
|
5231
5445
|
mode: "role_profile",
|
|
@@ -5240,20 +5454,8 @@ export async function runSelftestRunnerScenarios(push, deps) {
|
|
|
5240
5454
|
workspaceSource: "selftest",
|
|
5241
5455
|
usedCommandFallback: false,
|
|
5242
5456
|
}),
|
|
5243
|
-
replaceProjectCtxpackFiles: async (
|
|
5244
|
-
|
|
5245
|
-
return {
|
|
5246
|
-
ctxpack_id: "ctxpack-3",
|
|
5247
|
-
version_id: "version-ctxpack-3",
|
|
5248
|
-
version: "0.3.0",
|
|
5249
|
-
status: "draft",
|
|
5250
|
-
files: ensureArray(artifacts).map((artifact) => ({
|
|
5251
|
-
path: String(artifact.path || artifact.relativePath || "").trim(),
|
|
5252
|
-
doc_type: "guide",
|
|
5253
|
-
})),
|
|
5254
|
-
pushed_file_count: ensureArray(artifacts).length,
|
|
5255
|
-
changed_paths: ensureArray(artifacts).map((artifact) => String(artifact.path || artifact.relativePath || "").trim()),
|
|
5256
|
-
};
|
|
5457
|
+
replaceProjectCtxpackFiles: async () => {
|
|
5458
|
+
throw new Error("ctxpack version_id is missing; run ctxpack pull or refresh project summary first");
|
|
5257
5459
|
},
|
|
5258
5460
|
loadBotRunnerConfig: () => ({
|
|
5259
5461
|
roleProfiles: {},
|
|
@@ -5270,17 +5472,606 @@ export async function runSelftestRunnerScenarios(push, deps) {
|
|
|
5270
5472
|
},
|
|
5271
5473
|
});
|
|
5272
5474
|
push(
|
|
5273
|
-
"
|
|
5274
|
-
processed.kind === "
|
|
5275
|
-
&&
|
|
5276
|
-
&& fs.existsSync(expectedAbsolutePath)
|
|
5277
|
-
&& ensureArray(processed.result?.ctxpack_changed_paths).includes(expectedPath)
|
|
5475
|
+
"single_bot_ctxpack_update_reports_missing_version",
|
|
5476
|
+
processed.kind === "error"
|
|
5477
|
+
&& String(processed.result?.outcome || "") === "execution_failed"
|
|
5278
5478
|
&& deliveryCalls === 1
|
|
5279
|
-
&& /
|
|
5280
|
-
`kind=${String(processed.kind || "(none)")}
|
|
5479
|
+
&& /ctxpack version is missing/i.test(deliveredText),
|
|
5480
|
+
`kind=${String(processed.kind || "(none)")} outcome=${String(processed.result?.outcome || "(none)")} delivery_calls=${deliveryCalls} delivered=${deliveredText} detail=${String(processed.result?.detail || "(none)")}`,
|
|
5281
5481
|
);
|
|
5282
5482
|
} catch (err) {
|
|
5283
|
-
push("
|
|
5483
|
+
push("single_bot_ctxpack_update_reports_missing_version", false, String(err?.message || err));
|
|
5484
|
+
}
|
|
5485
|
+
|
|
5486
|
+
try {
|
|
5487
|
+
let deliveryCalls = 0;
|
|
5488
|
+
let deliveredText = "";
|
|
5489
|
+
const workspaceDir = fs.mkdtempSync(path.join(os.tmpdir(), "metheus-runner-selftest-ctxpack-server-abort-"));
|
|
5490
|
+
const expectedPath = ".metheus/project-guides/project-operating-guide.md";
|
|
5491
|
+
const processed = await processRunnerSelectedRecord({
|
|
5492
|
+
routeKey: "single-bot-ctxpack-server-abort-key",
|
|
5493
|
+
normalizedRoute: normalizeRunnerRoute({
|
|
5494
|
+
name: "telegram-monitor-single-bot-ctxpack-server-abort",
|
|
5495
|
+
project_id: selftestProjectID,
|
|
5496
|
+
provider: "telegram",
|
|
5497
|
+
role: "monitor",
|
|
5498
|
+
role_profile: "monitor",
|
|
5499
|
+
destination_id: "dest-1",
|
|
5500
|
+
destination_label: "Main Room",
|
|
5501
|
+
server_bot_name: "RyoAI_bot",
|
|
5502
|
+
server_bot_id: "bot-lead-1",
|
|
5503
|
+
trigger_policy: {
|
|
5504
|
+
mentions_only: true,
|
|
5505
|
+
direct_messages: true,
|
|
5506
|
+
reply_to_bot_messages: true,
|
|
5507
|
+
},
|
|
5508
|
+
archive_policy: {
|
|
5509
|
+
mirror_replies: true,
|
|
5510
|
+
dedupe_inbound: true,
|
|
5511
|
+
dedupe_outbound: true,
|
|
5512
|
+
skip_bot_messages: true,
|
|
5513
|
+
},
|
|
5514
|
+
dry_run_delivery: true,
|
|
5515
|
+
}),
|
|
5516
|
+
selectedRecord: {
|
|
5517
|
+
id: "comment-single-bot-ctxpack-server-abort",
|
|
5518
|
+
createdAt: "2026-03-17T01:00:05.700Z",
|
|
5519
|
+
parsedArchive: {
|
|
5520
|
+
kind: "telegram_message",
|
|
5521
|
+
chatID: "-100123",
|
|
5522
|
+
chatType: "supergroup",
|
|
5523
|
+
senderIsBot: false,
|
|
5524
|
+
body: "@RyoAI_bot update the operating guide in ctxpack.",
|
|
5525
|
+
mentionUsernames: ["RyoAI_bot"],
|
|
5526
|
+
messageID: 13097,
|
|
5527
|
+
},
|
|
5528
|
+
},
|
|
5529
|
+
pendingOrdered: [],
|
|
5530
|
+
bot: {
|
|
5531
|
+
id: "bot-lead-1",
|
|
5532
|
+
name: "RyoAI_bot",
|
|
5533
|
+
username: "RyoAI_bot",
|
|
5534
|
+
role: "monitor",
|
|
5535
|
+
provider: "telegram",
|
|
5536
|
+
},
|
|
5537
|
+
destination: {
|
|
5538
|
+
id: "dest-1",
|
|
5539
|
+
label: "Main Room",
|
|
5540
|
+
provider: "telegram",
|
|
5541
|
+
chatID: "-100123",
|
|
5542
|
+
},
|
|
5543
|
+
archiveThread: {
|
|
5544
|
+
threadID: "thread-1",
|
|
5545
|
+
workItemID: "work-item-1",
|
|
5546
|
+
},
|
|
5547
|
+
executionPlan: {
|
|
5548
|
+
mode: "role_profile",
|
|
5549
|
+
roleProfileName: "monitor",
|
|
5550
|
+
roleProfile: {
|
|
5551
|
+
client: "sample",
|
|
5552
|
+
model: "",
|
|
5553
|
+
permissionMode: "read_only",
|
|
5554
|
+
reasoningEffort: "low",
|
|
5555
|
+
},
|
|
5556
|
+
workspaceDir,
|
|
5557
|
+
workspaceSource: "selftest",
|
|
5558
|
+
usedCommandFallback: false,
|
|
5559
|
+
},
|
|
5560
|
+
runtime: {
|
|
5561
|
+
baseURL: "https://example.test",
|
|
5562
|
+
token: "selftest-token",
|
|
5563
|
+
timeoutSeconds: 30,
|
|
5564
|
+
actor: { user_id: "user-1" },
|
|
5565
|
+
},
|
|
5566
|
+
deps: {
|
|
5567
|
+
saveRunnerRouteState: () => {},
|
|
5568
|
+
startRunnerTypingHeartbeat: () => ({ async stop() {} }),
|
|
5569
|
+
runRunnerAIExecution: async ({ route }) => {
|
|
5570
|
+
const stepRole = String(route?.role || "").trim().toLowerCase();
|
|
5571
|
+
if (stepRole === "monitor") {
|
|
5572
|
+
return {
|
|
5573
|
+
skip: false,
|
|
5574
|
+
reply: "Classified this as a ctxpack guidance update request.",
|
|
5575
|
+
};
|
|
5576
|
+
}
|
|
5577
|
+
if (stepRole === "worker") {
|
|
5578
|
+
return {
|
|
5579
|
+
skip: false,
|
|
5580
|
+
reply: "Prepared the official operating guide content.",
|
|
5581
|
+
artifacts: [],
|
|
5582
|
+
ctxpackFiles: [
|
|
5583
|
+
{
|
|
5584
|
+
path: expectedPath,
|
|
5585
|
+
docType: "guide",
|
|
5586
|
+
content: "# Project Operating Guide\n\nServer write must complete.\n",
|
|
5587
|
+
},
|
|
5588
|
+
],
|
|
5589
|
+
workItems: [],
|
|
5590
|
+
};
|
|
5591
|
+
}
|
|
5592
|
+
throw new Error(`unexpected step role ${stepRole}`);
|
|
5593
|
+
},
|
|
5594
|
+
performLocalBotDelivery: async ({ text }) => {
|
|
5595
|
+
deliveryCalls += 1;
|
|
5596
|
+
deliveredText = String(text || "");
|
|
5597
|
+
return {
|
|
5598
|
+
delivery: { dryRun: true, body: {} },
|
|
5599
|
+
archive: {},
|
|
5600
|
+
};
|
|
5601
|
+
},
|
|
5602
|
+
serializeRunnerTriggerPolicy: (value) => value,
|
|
5603
|
+
serializeRunnerArchivePolicy: (value) => value,
|
|
5604
|
+
buildRunnerExecutionDeps: () => ({
|
|
5605
|
+
validateWorkspaceArtifacts,
|
|
5606
|
+
analyzeHumanConversationIntentWithAI: async () => ({
|
|
5607
|
+
mode: "single_bot",
|
|
5608
|
+
lead_bot: "ryoai_bot",
|
|
5609
|
+
participants: ["ryoai_bot"],
|
|
5610
|
+
initial_responders: ["ryoai_bot"],
|
|
5611
|
+
allowed_responders: ["ryoai_bot"],
|
|
5612
|
+
summary_bot: "",
|
|
5613
|
+
allow_bot_to_bot: false,
|
|
5614
|
+
reply_expectation: "actionable",
|
|
5615
|
+
}),
|
|
5616
|
+
planRoleExecutionWithAI: async () => ({
|
|
5617
|
+
requiresExecution: true,
|
|
5618
|
+
summaryRole: "worker",
|
|
5619
|
+
steps: [
|
|
5620
|
+
{ role: "monitor", goal: "Inspect the guidance request" },
|
|
5621
|
+
{
|
|
5622
|
+
role: "worker",
|
|
5623
|
+
goal: "Update the operating guide in ctxpack",
|
|
5624
|
+
artifactsRequired: true,
|
|
5625
|
+
ctxpackUpdateRequired: true,
|
|
5626
|
+
outputHint: expectedPath,
|
|
5627
|
+
},
|
|
5628
|
+
],
|
|
5629
|
+
}),
|
|
5630
|
+
auditRoleExecutionPlanWithAI: async () => ({
|
|
5631
|
+
requires_execution: true,
|
|
5632
|
+
requires_worker_step: true,
|
|
5633
|
+
requires_ctxpack_update_step: true,
|
|
5634
|
+
plan_satisfies_request: true,
|
|
5635
|
+
reason: "worker ctxpack step present",
|
|
5636
|
+
}),
|
|
5637
|
+
resolveRunnerExecutionPlanForRole: (_route, _bot, roleName) => ({
|
|
5638
|
+
mode: "role_profile",
|
|
5639
|
+
roleProfileName: String(roleName || "").trim(),
|
|
5640
|
+
roleProfile: {
|
|
5641
|
+
client: "sample",
|
|
5642
|
+
model: "",
|
|
5643
|
+
permissionMode: String(roleName || "").trim() === "worker" ? "workspace_write" : "read_only",
|
|
5644
|
+
reasoningEffort: String(roleName || "").trim() === "worker" ? "medium" : "low",
|
|
5645
|
+
},
|
|
5646
|
+
workspaceDir,
|
|
5647
|
+
workspaceSource: "selftest",
|
|
5648
|
+
usedCommandFallback: false,
|
|
5649
|
+
}),
|
|
5650
|
+
replaceProjectCtxpackFiles: async () => {
|
|
5651
|
+
throw new Error("write ECONNABORTED");
|
|
5652
|
+
},
|
|
5653
|
+
loadBotRunnerConfig: () => ({
|
|
5654
|
+
roleProfiles: {},
|
|
5655
|
+
botBindings: {},
|
|
5656
|
+
routes: [],
|
|
5657
|
+
projectMappings: {},
|
|
5658
|
+
}),
|
|
5659
|
+
}),
|
|
5660
|
+
buildRunnerDeliveryDeps: () => ({}),
|
|
5661
|
+
buildRunnerRuntimeDeps: () => ({}),
|
|
5662
|
+
resolveConversationPeerBots: () => [
|
|
5663
|
+
{ id: "bot-lead-1", name: "RyoAI_bot" },
|
|
5664
|
+
],
|
|
5665
|
+
},
|
|
5666
|
+
});
|
|
5667
|
+
push(
|
|
5668
|
+
"single_bot_ctxpack_update_reports_server_abort",
|
|
5669
|
+
processed.kind === "error"
|
|
5670
|
+
&& String(processed.result?.outcome || "") === "execution_failed"
|
|
5671
|
+
&& deliveryCalls === 1
|
|
5672
|
+
&& /server ctxpack update request did not complete/i.test(deliveredText),
|
|
5673
|
+
`kind=${String(processed.kind || "(none)")} outcome=${String(processed.result?.outcome || "(none)")} delivery_calls=${deliveryCalls} delivered=${deliveredText} detail=${String(processed.result?.detail || "(none)")}`,
|
|
5674
|
+
);
|
|
5675
|
+
} catch (err) {
|
|
5676
|
+
push("single_bot_ctxpack_update_reports_server_abort", false, String(err?.message || err));
|
|
5677
|
+
}
|
|
5678
|
+
|
|
5679
|
+
try {
|
|
5680
|
+
let deliveryCalls = 0;
|
|
5681
|
+
let deliveredText = "";
|
|
5682
|
+
let ctxpackUpdateCalls = 0;
|
|
5683
|
+
const workspaceDir = fs.mkdtempSync(path.join(os.tmpdir(), "metheus-runner-selftest-ctxpack-guidance-force-"));
|
|
5684
|
+
const expectedPath = ".metheus/project-guides/project-operating-guide.md";
|
|
5685
|
+
const expectedAbsolutePath = path.join(workspaceDir, ".metheus", "project-guides", "project-operating-guide.md");
|
|
5686
|
+
const processed = await processRunnerSelectedRecord({
|
|
5687
|
+
routeKey: "single-bot-ctxpack-guidance-force-key",
|
|
5688
|
+
normalizedRoute: normalizeRunnerRoute({
|
|
5689
|
+
name: "telegram-monitor-single-bot-ctxpack-guidance-force",
|
|
5690
|
+
project_id: selftestProjectID,
|
|
5691
|
+
provider: "telegram",
|
|
5692
|
+
role: "monitor",
|
|
5693
|
+
role_profile: "monitor",
|
|
5694
|
+
destination_id: "dest-1",
|
|
5695
|
+
destination_label: "Main Room",
|
|
5696
|
+
server_bot_name: "RyoAI_bot",
|
|
5697
|
+
server_bot_id: "bot-lead-1",
|
|
5698
|
+
trigger_policy: {
|
|
5699
|
+
mentions_only: true,
|
|
5700
|
+
direct_messages: true,
|
|
5701
|
+
reply_to_bot_messages: true,
|
|
5702
|
+
},
|
|
5703
|
+
archive_policy: {
|
|
5704
|
+
mirror_replies: true,
|
|
5705
|
+
dedupe_inbound: true,
|
|
5706
|
+
dedupe_outbound: true,
|
|
5707
|
+
skip_bot_messages: true,
|
|
5708
|
+
},
|
|
5709
|
+
dry_run_delivery: true,
|
|
5710
|
+
}),
|
|
5711
|
+
selectedRecord: {
|
|
5712
|
+
id: "comment-single-bot-ctxpack-guidance-force",
|
|
5713
|
+
createdAt: "2026-03-17T01:00:05.750Z",
|
|
5714
|
+
parsedArchive: {
|
|
5715
|
+
kind: "telegram_message",
|
|
5716
|
+
chatID: "-100123",
|
|
5717
|
+
chatType: "supergroup",
|
|
5718
|
+
senderIsBot: false,
|
|
5719
|
+
body: "@RyoAI_bot 프로젝트 운영 가이드를 작성해서 ctxpack에 반영해줘.",
|
|
5720
|
+
mentionUsernames: ["RyoAI_bot"],
|
|
5721
|
+
messageID: 1310,
|
|
5722
|
+
},
|
|
5723
|
+
},
|
|
5724
|
+
pendingOrdered: [],
|
|
5725
|
+
bot: {
|
|
5726
|
+
id: "bot-lead-1",
|
|
5727
|
+
name: "RyoAI_bot",
|
|
5728
|
+
username: "RyoAI_bot",
|
|
5729
|
+
role: "monitor",
|
|
5730
|
+
provider: "telegram",
|
|
5731
|
+
},
|
|
5732
|
+
destination: {
|
|
5733
|
+
id: "dest-1",
|
|
5734
|
+
label: "Main Room",
|
|
5735
|
+
provider: "telegram",
|
|
5736
|
+
chatID: "-100123",
|
|
5737
|
+
},
|
|
5738
|
+
archiveThread: {
|
|
5739
|
+
threadID: "thread-1",
|
|
5740
|
+
workItemID: "work-item-1",
|
|
5741
|
+
},
|
|
5742
|
+
executionPlan: {
|
|
5743
|
+
mode: "role_profile",
|
|
5744
|
+
roleProfileName: "monitor",
|
|
5745
|
+
roleProfile: {
|
|
5746
|
+
client: "sample",
|
|
5747
|
+
model: "",
|
|
5748
|
+
permissionMode: "read_only",
|
|
5749
|
+
reasoningEffort: "low",
|
|
5750
|
+
},
|
|
5751
|
+
workspaceDir,
|
|
5752
|
+
workspaceSource: "selftest",
|
|
5753
|
+
usedCommandFallback: false,
|
|
5754
|
+
},
|
|
5755
|
+
runtime: {
|
|
5756
|
+
baseURL: "https://example.test",
|
|
5757
|
+
token: "selftest-token",
|
|
5758
|
+
timeoutSeconds: 30,
|
|
5759
|
+
actor: { user_id: "user-1" },
|
|
5760
|
+
},
|
|
5761
|
+
deps: {
|
|
5762
|
+
saveRunnerRouteState: () => {},
|
|
5763
|
+
startRunnerTypingHeartbeat: () => ({ async stop() {} }),
|
|
5764
|
+
runRunnerAIExecution: async ({ route }) => {
|
|
5765
|
+
const stepRole = String(route?.role || "").trim().toLowerCase();
|
|
5766
|
+
if (stepRole === "monitor") {
|
|
5767
|
+
return {
|
|
5768
|
+
skip: false,
|
|
5769
|
+
reply: "Identified this as an official project guidance update request.",
|
|
5770
|
+
};
|
|
5771
|
+
}
|
|
5772
|
+
if (stepRole === "worker") {
|
|
5773
|
+
return {
|
|
5774
|
+
skip: false,
|
|
5775
|
+
reply: "Prepared the official operating guide content.",
|
|
5776
|
+
artifacts: [],
|
|
5777
|
+
ctxpackFiles: [
|
|
5778
|
+
{
|
|
5779
|
+
path: expectedPath,
|
|
5780
|
+
docType: "guide",
|
|
5781
|
+
content: "# Project Operating Guide\n\nUse ctxpack-backed official guidance files.\n",
|
|
5782
|
+
},
|
|
5783
|
+
],
|
|
5784
|
+
workItems: [],
|
|
5785
|
+
};
|
|
5786
|
+
}
|
|
5787
|
+
throw new Error(`unexpected step role ${stepRole}`);
|
|
5788
|
+
},
|
|
5789
|
+
performLocalBotDelivery: async ({ text }) => {
|
|
5790
|
+
deliveryCalls += 1;
|
|
5791
|
+
deliveredText = String(text || "");
|
|
5792
|
+
return {
|
|
5793
|
+
delivery: { dryRun: true, body: {} },
|
|
5794
|
+
archive: {},
|
|
5795
|
+
};
|
|
5796
|
+
},
|
|
5797
|
+
serializeRunnerTriggerPolicy: (value) => value,
|
|
5798
|
+
serializeRunnerArchivePolicy: (value) => value,
|
|
5799
|
+
buildRunnerExecutionDeps: () => ({
|
|
5800
|
+
validateWorkspaceArtifacts,
|
|
5801
|
+
analyzeHumanConversationIntentWithAI: async () => ({
|
|
5802
|
+
mode: "single_bot",
|
|
5803
|
+
lead_bot: "ryoai_bot",
|
|
5804
|
+
participants: ["ryoai_bot"],
|
|
5805
|
+
initial_responders: ["ryoai_bot"],
|
|
5806
|
+
allowed_responders: ["ryoai_bot"],
|
|
5807
|
+
summary_bot: "",
|
|
5808
|
+
allow_bot_to_bot: false,
|
|
5809
|
+
reply_expectation: "actionable",
|
|
5810
|
+
}),
|
|
5811
|
+
planRoleExecutionWithAI: async () => ({
|
|
5812
|
+
requiresExecution: true,
|
|
5813
|
+
summaryRole: "worker",
|
|
5814
|
+
steps: [
|
|
5815
|
+
{ role: "monitor", goal: "Inspect the guidance request" },
|
|
5816
|
+
{
|
|
5817
|
+
role: "worker",
|
|
5818
|
+
goal: "Draft the project operating guide",
|
|
5819
|
+
artifactsRequired: true,
|
|
5820
|
+
},
|
|
5821
|
+
],
|
|
5822
|
+
}),
|
|
5823
|
+
auditRoleExecutionPlanWithAI: async () => ({
|
|
5824
|
+
requires_execution: true,
|
|
5825
|
+
requires_worker_step: true,
|
|
5826
|
+
plan_satisfies_request: true,
|
|
5827
|
+
reason: "worker step present",
|
|
5828
|
+
}),
|
|
5829
|
+
resolveRunnerExecutionPlanForRole: (_route, _bot, roleName) => ({
|
|
5830
|
+
mode: "role_profile",
|
|
5831
|
+
roleProfileName: String(roleName || "").trim(),
|
|
5832
|
+
roleProfile: {
|
|
5833
|
+
client: "sample",
|
|
5834
|
+
model: "",
|
|
5835
|
+
permissionMode: String(roleName || "").trim() === "worker" ? "workspace_write" : "read_only",
|
|
5836
|
+
reasoningEffort: String(roleName || "").trim() === "worker" ? "medium" : "low",
|
|
5837
|
+
},
|
|
5838
|
+
workspaceDir,
|
|
5839
|
+
workspaceSource: "selftest",
|
|
5840
|
+
usedCommandFallback: false,
|
|
5841
|
+
}),
|
|
5842
|
+
replaceProjectCtxpackFiles: async ({ artifacts }) => {
|
|
5843
|
+
ctxpackUpdateCalls += 1;
|
|
5844
|
+
return {
|
|
5845
|
+
ctxpack_id: "ctxpack-3",
|
|
5846
|
+
version_id: "version-ctxpack-3",
|
|
5847
|
+
version: "0.3.0",
|
|
5848
|
+
status: "draft",
|
|
5849
|
+
files: ensureArray(artifacts).map((artifact) => ({
|
|
5850
|
+
path: String(artifact.path || artifact.relativePath || "").trim(),
|
|
5851
|
+
doc_type: "guide",
|
|
5852
|
+
})),
|
|
5853
|
+
pushed_file_count: ensureArray(artifacts).length,
|
|
5854
|
+
changed_paths: ensureArray(artifacts).map((artifact) => String(artifact.path || artifact.relativePath || "").trim()),
|
|
5855
|
+
};
|
|
5856
|
+
},
|
|
5857
|
+
loadBotRunnerConfig: () => ({
|
|
5858
|
+
roleProfiles: {},
|
|
5859
|
+
botBindings: {},
|
|
5860
|
+
routes: [],
|
|
5861
|
+
projectMappings: {},
|
|
5862
|
+
}),
|
|
5863
|
+
}),
|
|
5864
|
+
buildRunnerDeliveryDeps: () => ({}),
|
|
5865
|
+
buildRunnerRuntimeDeps: () => ({}),
|
|
5866
|
+
resolveConversationPeerBots: () => [
|
|
5867
|
+
{ id: "bot-lead-1", name: "RyoAI_bot" },
|
|
5868
|
+
],
|
|
5869
|
+
},
|
|
5870
|
+
});
|
|
5871
|
+
push(
|
|
5872
|
+
"single_bot_guide_request_forces_ctxpack_update_when_planner_omits_it",
|
|
5873
|
+
processed.kind === "replied"
|
|
5874
|
+
&& ctxpackUpdateCalls === 1
|
|
5875
|
+
&& fs.existsSync(expectedAbsolutePath)
|
|
5876
|
+
&& ensureArray(processed.result?.ctxpack_changed_paths).includes(expectedPath)
|
|
5877
|
+
&& deliveryCalls === 1
|
|
5878
|
+
&& /operating guide/i.test(deliveredText),
|
|
5879
|
+
`kind=${String(processed.kind || "(none)")} ctxpack_updates=${ctxpackUpdateCalls} exists=${String(fs.existsSync(expectedAbsolutePath))} changed=${ensureArray(processed.result?.ctxpack_changed_paths).join(",")} delivered=${deliveredText}`,
|
|
5880
|
+
);
|
|
5881
|
+
} catch (err) {
|
|
5882
|
+
push("single_bot_guide_request_forces_ctxpack_update_when_planner_omits_it", false, String(err?.message || err));
|
|
5883
|
+
}
|
|
5884
|
+
|
|
5885
|
+
try {
|
|
5886
|
+
let deliveryCalls = 0;
|
|
5887
|
+
let deliveredText = "";
|
|
5888
|
+
let ctxpackUpdateCalls = 0;
|
|
5889
|
+
const workspaceDir = fs.mkdtempSync(path.join(os.tmpdir(), "metheus-runner-selftest-readme-not-ctxpack-"));
|
|
5890
|
+
const readmePath = path.join(workspaceDir, "README.md");
|
|
5891
|
+
const processed = await processRunnerSelectedRecord({
|
|
5892
|
+
routeKey: "single-bot-readme-not-ctxpack-key",
|
|
5893
|
+
normalizedRoute: normalizeRunnerRoute({
|
|
5894
|
+
name: "telegram-monitor-single-bot-readme-not-ctxpack",
|
|
5895
|
+
project_id: selftestProjectID,
|
|
5896
|
+
provider: "telegram",
|
|
5897
|
+
role: "monitor",
|
|
5898
|
+
role_profile: "monitor",
|
|
5899
|
+
destination_id: "dest-1",
|
|
5900
|
+
destination_label: "Main Room",
|
|
5901
|
+
server_bot_name: "RyoAI_bot",
|
|
5902
|
+
server_bot_id: "bot-lead-1",
|
|
5903
|
+
trigger_policy: {
|
|
5904
|
+
mentions_only: true,
|
|
5905
|
+
direct_messages: true,
|
|
5906
|
+
reply_to_bot_messages: true,
|
|
5907
|
+
},
|
|
5908
|
+
archive_policy: {
|
|
5909
|
+
mirror_replies: true,
|
|
5910
|
+
dedupe_inbound: true,
|
|
5911
|
+
dedupe_outbound: true,
|
|
5912
|
+
skip_bot_messages: true,
|
|
5913
|
+
},
|
|
5914
|
+
dry_run_delivery: true,
|
|
5915
|
+
}),
|
|
5916
|
+
selectedRecord: {
|
|
5917
|
+
id: "comment-single-bot-readme-not-ctxpack",
|
|
5918
|
+
createdAt: "2026-03-17T01:00:05.875Z",
|
|
5919
|
+
parsedArchive: {
|
|
5920
|
+
kind: "telegram_message",
|
|
5921
|
+
chatID: "-100123",
|
|
5922
|
+
chatType: "supergroup",
|
|
5923
|
+
senderIsBot: false,
|
|
5924
|
+
body: "@RyoAI_bot create the implementation README now.",
|
|
5925
|
+
mentionUsernames: ["RyoAI_bot"],
|
|
5926
|
+
messageID: 1311,
|
|
5927
|
+
},
|
|
5928
|
+
},
|
|
5929
|
+
pendingOrdered: [],
|
|
5930
|
+
bot: {
|
|
5931
|
+
id: "bot-lead-1",
|
|
5932
|
+
name: "RyoAI_bot",
|
|
5933
|
+
username: "RyoAI_bot",
|
|
5934
|
+
role: "monitor",
|
|
5935
|
+
provider: "telegram",
|
|
5936
|
+
},
|
|
5937
|
+
destination: {
|
|
5938
|
+
id: "dest-1",
|
|
5939
|
+
label: "Main Room",
|
|
5940
|
+
provider: "telegram",
|
|
5941
|
+
chatID: "-100123",
|
|
5942
|
+
},
|
|
5943
|
+
archiveThread: {
|
|
5944
|
+
threadID: "thread-1",
|
|
5945
|
+
workItemID: "work-item-1",
|
|
5946
|
+
},
|
|
5947
|
+
executionPlan: {
|
|
5948
|
+
mode: "role_profile",
|
|
5949
|
+
roleProfileName: "monitor",
|
|
5950
|
+
roleProfile: {
|
|
5951
|
+
client: "sample",
|
|
5952
|
+
model: "",
|
|
5953
|
+
permissionMode: "read_only",
|
|
5954
|
+
reasoningEffort: "low",
|
|
5955
|
+
},
|
|
5956
|
+
workspaceDir,
|
|
5957
|
+
workspaceSource: "selftest",
|
|
5958
|
+
usedCommandFallback: false,
|
|
5959
|
+
},
|
|
5960
|
+
runtime: {
|
|
5961
|
+
baseURL: "https://example.test",
|
|
5962
|
+
token: "selftest-token",
|
|
5963
|
+
timeoutSeconds: 30,
|
|
5964
|
+
actor: { user_id: "user-1" },
|
|
5965
|
+
},
|
|
5966
|
+
deps: {
|
|
5967
|
+
saveRunnerRouteState: () => {},
|
|
5968
|
+
startRunnerTypingHeartbeat: () => ({ async stop() {} }),
|
|
5969
|
+
runRunnerAIExecution: async ({ route }) => {
|
|
5970
|
+
const stepRole = String(route?.role || "").trim().toLowerCase();
|
|
5971
|
+
if (stepRole === "monitor") {
|
|
5972
|
+
return {
|
|
5973
|
+
skip: false,
|
|
5974
|
+
reply: "Identified this as a normal project README request.",
|
|
5975
|
+
};
|
|
5976
|
+
}
|
|
5977
|
+
if (stepRole === "worker") {
|
|
5978
|
+
fs.writeFileSync(readmePath, "# README\n", "utf8");
|
|
5979
|
+
return {
|
|
5980
|
+
skip: false,
|
|
5981
|
+
reply: "Created the implementation README.",
|
|
5982
|
+
artifacts: [
|
|
5983
|
+
{ path: "README.md", kind: "document", operation: "create" },
|
|
5984
|
+
],
|
|
5985
|
+
workItems: [],
|
|
5986
|
+
};
|
|
5987
|
+
}
|
|
5988
|
+
throw new Error(`unexpected step role ${stepRole}`);
|
|
5989
|
+
},
|
|
5990
|
+
performLocalBotDelivery: async ({ text }) => {
|
|
5991
|
+
deliveryCalls += 1;
|
|
5992
|
+
deliveredText = String(text || "");
|
|
5993
|
+
return {
|
|
5994
|
+
delivery: { dryRun: true, body: {} },
|
|
5995
|
+
archive: {},
|
|
5996
|
+
};
|
|
5997
|
+
},
|
|
5998
|
+
serializeRunnerTriggerPolicy: (value) => value,
|
|
5999
|
+
serializeRunnerArchivePolicy: (value) => value,
|
|
6000
|
+
buildRunnerExecutionDeps: () => ({
|
|
6001
|
+
validateWorkspaceArtifacts,
|
|
6002
|
+
analyzeHumanConversationIntentWithAI: async () => ({
|
|
6003
|
+
mode: "single_bot",
|
|
6004
|
+
lead_bot: "ryoai_bot",
|
|
6005
|
+
participants: ["ryoai_bot"],
|
|
6006
|
+
initial_responders: ["ryoai_bot"],
|
|
6007
|
+
allowed_responders: ["ryoai_bot"],
|
|
6008
|
+
summary_bot: "",
|
|
6009
|
+
allow_bot_to_bot: false,
|
|
6010
|
+
reply_expectation: "actionable",
|
|
6011
|
+
}),
|
|
6012
|
+
planRoleExecutionWithAI: async () => ({
|
|
6013
|
+
requiresExecution: true,
|
|
6014
|
+
summaryRole: "worker",
|
|
6015
|
+
steps: [
|
|
6016
|
+
{ role: "monitor", goal: "Inspect the README request" },
|
|
6017
|
+
{
|
|
6018
|
+
role: "worker",
|
|
6019
|
+
goal: "Create the README",
|
|
6020
|
+
artifactsRequired: true,
|
|
6021
|
+
},
|
|
6022
|
+
],
|
|
6023
|
+
}),
|
|
6024
|
+
auditRoleExecutionPlanWithAI: async () => ({
|
|
6025
|
+
requires_execution: true,
|
|
6026
|
+
requires_worker_step: true,
|
|
6027
|
+
requires_ctxpack_update_step: false,
|
|
6028
|
+
plan_satisfies_request: true,
|
|
6029
|
+
reason: "worker step present",
|
|
6030
|
+
}),
|
|
6031
|
+
resolveRunnerExecutionPlanForRole: (_route, _bot, roleName) => ({
|
|
6032
|
+
mode: "role_profile",
|
|
6033
|
+
roleProfileName: String(roleName || "").trim(),
|
|
6034
|
+
roleProfile: {
|
|
6035
|
+
client: "sample",
|
|
6036
|
+
model: "",
|
|
6037
|
+
permissionMode: String(roleName || "").trim() === "worker" ? "workspace_write" : "read_only",
|
|
6038
|
+
reasoningEffort: String(roleName || "").trim() === "worker" ? "medium" : "low",
|
|
6039
|
+
},
|
|
6040
|
+
workspaceDir,
|
|
6041
|
+
workspaceSource: "selftest",
|
|
6042
|
+
usedCommandFallback: false,
|
|
6043
|
+
}),
|
|
6044
|
+
replaceProjectCtxpackFiles: async () => {
|
|
6045
|
+
ctxpackUpdateCalls += 1;
|
|
6046
|
+
throw new Error("ctxpack update should not run for a plain README request");
|
|
6047
|
+
},
|
|
6048
|
+
loadBotRunnerConfig: () => ({
|
|
6049
|
+
roleProfiles: {},
|
|
6050
|
+
botBindings: {},
|
|
6051
|
+
routes: [],
|
|
6052
|
+
projectMappings: {},
|
|
6053
|
+
}),
|
|
6054
|
+
}),
|
|
6055
|
+
buildRunnerDeliveryDeps: () => ({}),
|
|
6056
|
+
buildRunnerRuntimeDeps: () => ({}),
|
|
6057
|
+
resolveConversationPeerBots: () => [
|
|
6058
|
+
{ id: "bot-lead-1", name: "RyoAI_bot" },
|
|
6059
|
+
],
|
|
6060
|
+
},
|
|
6061
|
+
});
|
|
6062
|
+
push(
|
|
6063
|
+
"single_bot_readme_request_does_not_force_ctxpack_update",
|
|
6064
|
+
processed.kind === "replied"
|
|
6065
|
+
&& ctxpackUpdateCalls === 0
|
|
6066
|
+
&& fs.existsSync(readmePath)
|
|
6067
|
+
&& ensureArray(processed.result?.artifact_paths).includes("README.md")
|
|
6068
|
+
&& ensureArray(processed.result?.ctxpack_changed_paths).length === 0
|
|
6069
|
+
&& deliveryCalls === 1
|
|
6070
|
+
&& /README/i.test(deliveredText),
|
|
6071
|
+
`kind=${String(processed.kind || "(none)")} ctxpack_updates=${ctxpackUpdateCalls} exists=${String(fs.existsSync(readmePath))} artifacts=${ensureArray(processed.result?.artifact_paths).join(",")} changed=${ensureArray(processed.result?.ctxpack_changed_paths).join(",")} delivered=${deliveredText}`,
|
|
6072
|
+
);
|
|
6073
|
+
} catch (err) {
|
|
6074
|
+
push("single_bot_readme_request_does_not_force_ctxpack_update", false, String(err?.message || err));
|
|
5284
6075
|
}
|
|
5285
6076
|
|
|
5286
6077
|
try {
|