metheus-governance-mcp-cli 0.2.266 → 0.2.268

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 CHANGED
@@ -6223,20 +6223,26 @@ function markRunnerRequestLifecycle({
6223
6223
  return String(existing.normalized_intent || "").trim().toLowerCase();
6224
6224
  })();
6225
6225
  const normalizedOutcome = String(outcome || "").trim().toLowerCase();
6226
- const nextStatus = (() => {
6227
- if (normalizedOutcome === "claimed") return "claimed";
6228
- if (normalizedOutcome === "running") return "running";
6229
- if (normalizedOutcome === "delivery_failed_after_generation") return "running";
6230
- if (normalizedOutcome === "replied") {
6231
- return shouldRemainRunningAfterReply ? "running" : "completed";
6232
- }
6233
- if (normalizedOutcome === "loop_closed") return "loop_closed";
6234
- if (normalizedOutcome === "expired") return "expired";
6235
- if (normalizedOutcome === "error" || normalizedOutcome === "skipped" || normalizedOutcome === "closed") {
6236
- return "closed";
6237
- }
6238
- return normalizeRunnerRequestStatus(existing.status);
6239
- })();
6226
+ const nextStatus = (() => {
6227
+ if (normalizedOutcome === "claimed") return "claimed";
6228
+ if (normalizedOutcome === "running") return "running";
6229
+ if (normalizedOutcome === "delivery_failed_after_generation") return "running";
6230
+ if (normalizedOutcome === "replied") {
6231
+ return shouldRemainRunningAfterReply ? "running" : "completed";
6232
+ }
6233
+ if (normalizedOutcome === "loop_closed") return "loop_closed";
6234
+ if (normalizedOutcome === "expired") return "expired";
6235
+ if (
6236
+ normalizedOutcome === "error"
6237
+ || normalizedOutcome === "skipped"
6238
+ || normalizedOutcome === "closed"
6239
+ || normalizedOutcome === "execution_failed"
6240
+ || normalizedOutcome === "policy_violation"
6241
+ ) {
6242
+ return "closed";
6243
+ }
6244
+ return normalizeRunnerRequestStatus(existing.status);
6245
+ })();
6240
6246
  const nowISO = new Date().toISOString();
6241
6247
  const commentKind = String(parsed.kind || "").trim().toLowerCase();
6242
6248
  const isRootHumanComment = ["telegram_message", "telegram_edited_message"].includes(commentKind);
@@ -9,6 +9,73 @@ function ensureArray(value) {
9
9
  return Array.isArray(value) ? value : [];
10
10
  }
11
11
 
12
+ function normalizeConversationHeuristicText(text) {
13
+ return String(text || "")
14
+ .trim()
15
+ .toLowerCase()
16
+ .replace(/\s+/g, " ");
17
+ }
18
+
19
+ function isImplicitSingleBotConversationContribution(taskText = "") {
20
+ const normalizedText = normalizeConversationHeuristicText(taskText);
21
+ if (!normalizedText) {
22
+ return false;
23
+ }
24
+ const workspaceArtifactPatterns = [
25
+ /\bfile\b/,
26
+ /\bfiles\b/,
27
+ /\bcode\b/,
28
+ /\bpatch\b/,
29
+ /\bcommit\b/,
30
+ /\bworkspace\b/,
31
+ /\brepo\b/,
32
+ /\brepository\b/,
33
+ /\bctxpack\b/,
34
+ /\bwork ?item\b/,
35
+ /\bevidence\b/,
36
+ /\bartifact\b/,
37
+ /\bimplement\b/,
38
+ /\bimplementation\b/,
39
+ /\bfix\b/,
40
+ /\bupdate\b/,
41
+ /\bedit\b/,
42
+ /\bcreate\b.*\b(document|doc|guide|plan|readme)\b/,
43
+ /\bwrite\b.*\b(document|doc|guide|plan|readme)\b/,
44
+ /\uD30C\uC77C/,
45
+ /\uCF54\uB4DC/,
46
+ /\uBB38\uC11C/,
47
+ /\uAD6C\uD604/,
48
+ /\uC218\uC815/,
49
+ /\uC791\uC131/,
50
+ /\uC544\uD2F0\uD329\uD2B8/,
51
+ /\uC99D\uAC70/,
52
+ /\uC6CC\uD06C\uC2A4\uD398\uC774\uC2A4/,
53
+ /\uCEE4\uBC0B/,
54
+ ];
55
+ if (workspaceArtifactPatterns.some((pattern) => pattern.test(normalizedText))) {
56
+ return false;
57
+ }
58
+ const conversationalPatterns = [
59
+ /\bgreet\b/,
60
+ /\bhello\b/,
61
+ /\bhi\b/,
62
+ /\bsay hello\b/,
63
+ /\bintroduce yourself\b/,
64
+ /\banswer briefly\b/,
65
+ /\breply briefly\b/,
66
+ /\bin this room\b/,
67
+ /\bone line\b/,
68
+ /\uC778\uC0AC/,
69
+ /\uC548\uB155/,
70
+ /\uD558\uC774/,
71
+ /\uC18C\uAC1C/,
72
+ /\uD55C ?\uC904/,
73
+ /\uC9E7\uAC8C/,
74
+ /\uB2F5\uD574/,
75
+ ];
76
+ return conversationalPatterns.some((pattern) => pattern.test(normalizedText));
77
+ }
78
+
12
79
  export function resolveInformationalQueryExecutionPlan(executionPlan, directInformationalReply, route) {
13
80
  const basePlan = safeObject(executionPlan);
14
81
  const override = safeObject(safeObject(directInformationalReply).execution_override);
@@ -151,6 +218,27 @@ export function summarizeCurrentAssignmentExecutionValidation(
151
218
  )
152
219
  && Boolean(implicitExecutionTask);
153
220
  if (!assignments.length) {
221
+ if (shouldUseImplicitSingleBotExecution && isImplicitSingleBotConversationContribution(implicitExecutionTask)) {
222
+ const implicitConversationAssignment = {
223
+ targetBot: currentSelector,
224
+ task: implicitExecutionTask,
225
+ mode: "conversation_contribution",
226
+ artifactsRequired: false,
227
+ workspaceAction: false,
228
+ requiresExecution: false,
229
+ };
230
+ return {
231
+ status: "conversation_assignment",
232
+ reason: "single-bot public conversational contribution does not require planner/worker execution",
233
+ assignmentModes: ["conversation_contribution"],
234
+ assignments: [implicitConversationAssignment],
235
+ executionAssignments: [],
236
+ conversationAssignments: [implicitConversationAssignment],
237
+ executionTasks: [],
238
+ allTasks: [implicitExecutionTask],
239
+ requiresPlanner: false,
240
+ };
241
+ }
154
242
  if (shouldUseImplicitSingleBotExecution) {
155
243
  const implicitAssignment = {
156
244
  targetBot: currentSelector,
@@ -2615,16 +2615,61 @@ export async function runSelftestRunnerScenarios(push, deps) {
2615
2615
  executionContractTargets: ["@RyoAI3_bot"],
2616
2616
  nextExpectedResponders: [],
2617
2617
  });
2618
- push(
2619
- "runner_request_lifecycle_reply_ignores_session_state_when_continuation_targets_remain",
2620
- String(continuedRequest?.status || "") === "running",
2621
- `status=${String(continuedRequest?.status || "(none)")}`,
2622
- );
2623
-
2624
- saveBotRunnerState({
2625
- routes: {
2626
- [requestRouteKey]: {},
2627
- },
2618
+ push(
2619
+ "runner_request_lifecycle_reply_ignores_session_state_when_continuation_targets_remain",
2620
+ String(continuedRequest?.status || "") === "running",
2621
+ `status=${String(continuedRequest?.status || "(none)")}`,
2622
+ );
2623
+
2624
+ saveBotRunnerState({
2625
+ routes: {
2626
+ [requestRouteKey]: {},
2627
+ },
2628
+ sharedInboxes: {},
2629
+ excludedComments: {},
2630
+ requests: {
2631
+ "request-key-2b": {
2632
+ request_key: "request-key-2b",
2633
+ project_id: selftestProjectID,
2634
+ provider: "telegram",
2635
+ chat_id: "-100123",
2636
+ source_message_id: 711,
2637
+ conversation_id: "conv-request-2b",
2638
+ status: "running",
2639
+ claimed_by_route: requestRouteKey,
2640
+ },
2641
+ },
2642
+ consumedComments: {},
2643
+ });
2644
+ const failedRequest = markRunnerRequestLifecycle({
2645
+ normalizedRoute: requestRoute,
2646
+ requestKey: "request-key-2b",
2647
+ selectedRecord: {
2648
+ id: "comment-request-finish-2b",
2649
+ parsedArchive: {
2650
+ kind: "bot_reply",
2651
+ chatID: "-100123",
2652
+ messageID: 712,
2653
+ conversationID: "conv-request-2b",
2654
+ },
2655
+ },
2656
+ routeKey: requestRouteKey,
2657
+ outcome: "execution_failed",
2658
+ closedReason: "worker step completed without any validated project artifacts",
2659
+ currentBotSelector: "@RyoAI_bot",
2660
+ });
2661
+ push(
2662
+ "runner_request_lifecycle_execution_failed_closes_request",
2663
+ String(failedRequest?.status || "") === "closed"
2664
+ && String(failedRequest?.closed_reason || "") === "worker step completed without any validated project artifacts"
2665
+ && String(failedRequest?.closed_at || "").trim().length > 0,
2666
+ `status=${String(failedRequest?.status || "(none)")} reason=${String(failedRequest?.closed_reason || "(none)")} closed_at=${String(failedRequest?.closed_at || "(none)")}`,
2667
+ );
2668
+
2669
+ saveBotRunnerState({
2670
+ routes: {
2671
+ [requestRouteKey]: {},
2672
+ },
2628
2673
  sharedInboxes: {},
2629
2674
  excludedComments: {},
2630
2675
  requests: {
@@ -2679,12 +2724,13 @@ export async function runSelftestRunnerScenarios(push, deps) {
2679
2724
  );
2680
2725
  } catch (err) {
2681
2726
  push("runner_request_claim_persists_request_and_consumed_comment", false, String(err?.message || err));
2682
- push("runner_stale_open_session_cleanup_excludes_bot_reply_replay", false, String(err?.message || err));
2683
- push("runner_request_lifecycle_reply_completes_without_continuation_targets", false, String(err?.message || err));
2684
- push("runner_request_lifecycle_reply_ignores_session_state_when_continuation_targets_remain", false, String(err?.message || err));
2685
- push("runner_request_claim_preserves_authoritative_source_message_envelope", false, String(err?.message || err));
2686
- push("runner_request_lifecycle_preserves_stronger_existing_source_ownership", false, String(err?.message || err));
2687
- } finally {
2727
+ push("runner_stale_open_session_cleanup_excludes_bot_reply_replay", false, String(err?.message || err));
2728
+ push("runner_request_lifecycle_reply_completes_without_continuation_targets", false, String(err?.message || err));
2729
+ push("runner_request_lifecycle_reply_ignores_session_state_when_continuation_targets_remain", false, String(err?.message || err));
2730
+ push("runner_request_lifecycle_execution_failed_closes_request", false, String(err?.message || err));
2731
+ push("runner_request_claim_preserves_authoritative_source_message_envelope", false, String(err?.message || err));
2732
+ push("runner_request_lifecycle_preserves_stronger_existing_source_ownership", false, String(err?.message || err));
2733
+ } finally {
2688
2734
  if (typeof originalRequestLedgerHome === "string") {
2689
2735
  process.env.HOME = originalRequestLedgerHome;
2690
2736
  } else {
@@ -6044,15 +6090,159 @@ export async function runSelftestRunnerScenarios(push, deps) {
6044
6090
  && deliveredText === "I will inspect the project first and come back later.",
6045
6091
  `kind=${String(processed.kind || "(none)")} outcome=${String(processed.result?.outcome || "(none)")} ai_calls=${aiCalls} delivery_calls=${deliveryCalls} delivered=${deliveredText} reason=${String(processed.result?.detail || "(none)")}`,
6046
6092
  );
6047
- } catch (err) {
6048
- push("single_bot_human_work_request_requires_actionable_contract", false, String(err?.message || err));
6049
- }
6050
-
6051
- try {
6052
- let aiCalls = 0;
6053
- let deliveryCalls = 0;
6054
- let deliveredText = "";
6055
- let capturedFailureFacts = null;
6093
+ } catch (err) {
6094
+ push("single_bot_human_work_request_requires_actionable_contract", false, String(err?.message || err));
6095
+ }
6096
+
6097
+ try {
6098
+ let aiCalls = 0;
6099
+ let plannerCalls = 0;
6100
+ let deliveryCalls = 0;
6101
+ let deliveredText = "";
6102
+ const processed = await processRunnerSelectedRecord({
6103
+ routeKey: "single-bot-conversation-greeting-request-key",
6104
+ normalizedRoute: normalizeRunnerRoute({
6105
+ name: "telegram-monitor-single-bot-conversation-greeting",
6106
+ project_id: selftestProjectID,
6107
+ provider: "telegram",
6108
+ role: "monitor",
6109
+ role_profile: "monitor",
6110
+ destination_id: "dest-1",
6111
+ destination_label: "Main Room",
6112
+ server_bot_name: "RyoAI_bot",
6113
+ server_bot_id: "bot-lead-1",
6114
+ trigger_policy: {
6115
+ mentions_only: true,
6116
+ direct_messages: true,
6117
+ reply_to_bot_messages: true,
6118
+ },
6119
+ archive_policy: {
6120
+ mirror_replies: true,
6121
+ dedupe_inbound: true,
6122
+ dedupe_outbound: true,
6123
+ skip_bot_messages: true,
6124
+ },
6125
+ dry_run_delivery: true,
6126
+ }),
6127
+ selectedRecord: {
6128
+ id: "comment-single-bot-conversation-greeting-request",
6129
+ createdAt: "2026-03-31T08:37:56.000Z",
6130
+ parsedArchive: {
6131
+ kind: "telegram_message",
6132
+ chatID: "-100123",
6133
+ chatType: "supergroup",
6134
+ senderIsBot: false,
6135
+ body: "@RyoAI_bot 당신이 @SangHoon01_bot 에게 인사해보세요.",
6136
+ mentionUsernames: ["RyoAI_bot", "SangHoon01_bot"],
6137
+ messageID: 1057,
6138
+ },
6139
+ },
6140
+ pendingOrdered: [],
6141
+ bot: {
6142
+ id: "bot-lead-1",
6143
+ name: "RyoAI_bot",
6144
+ username: "RyoAI_bot",
6145
+ role: "monitor",
6146
+ provider: "telegram",
6147
+ },
6148
+ destination: {
6149
+ id: "dest-1",
6150
+ label: "Main Room",
6151
+ provider: "telegram",
6152
+ chatID: "-100123",
6153
+ },
6154
+ archiveThread: {
6155
+ threadID: "thread-1",
6156
+ workItemID: "work-item-1",
6157
+ },
6158
+ executionPlan: {
6159
+ mode: "role_profile",
6160
+ roleProfileName: "monitor",
6161
+ roleProfile: {
6162
+ client: "sample",
6163
+ model: "",
6164
+ permissionMode: "read_only",
6165
+ reasoningEffort: "low",
6166
+ },
6167
+ workspaceDir: path.join(os.tmpdir(), "metheus-runner-selftest-single-bot-conversation-greeting"),
6168
+ workspaceSource: "selftest",
6169
+ usedCommandFallback: false,
6170
+ },
6171
+ runtime: {
6172
+ baseURL: "https://example.test",
6173
+ token: "selftest-token",
6174
+ timeoutSeconds: 30,
6175
+ actor: { user_id: "user-1" },
6176
+ },
6177
+ deps: {
6178
+ saveRunnerRouteState: () => {},
6179
+ startRunnerTypingHeartbeat: () => ({ async stop() {} }),
6180
+ runRunnerAIExecution: async () => {
6181
+ aiCalls += 1;
6182
+ return {
6183
+ skip: false,
6184
+ reply: "@SangHoon01_bot 안녕하세요.",
6185
+ replyToMessageID: 0,
6186
+ };
6187
+ },
6188
+ performLocalBotDelivery: async ({ text }) => {
6189
+ deliveryCalls += 1;
6190
+ deliveredText = String(text || "").trim();
6191
+ return {
6192
+ delivery: { dryRun: true, body: {} },
6193
+ archive: {},
6194
+ };
6195
+ },
6196
+ serializeRunnerTriggerPolicy: (value) => value,
6197
+ serializeRunnerArchivePolicy: (value) => value,
6198
+ buildRunnerExecutionDeps: () => ({
6199
+ analyzeHumanConversationIntentWithAI: async () => ({
6200
+ mode: "single_bot",
6201
+ lead_bot: "ryoai_bot",
6202
+ participants: ["ryoai_bot"],
6203
+ initial_responders: ["ryoai_bot"],
6204
+ allowed_responders: ["ryoai_bot"],
6205
+ summary_bot: "",
6206
+ allow_bot_to_bot: false,
6207
+ reply_expectation: "actionable",
6208
+ intent_type: "general_execution",
6209
+ }),
6210
+ planRoleExecutionWithAI: async () => {
6211
+ plannerCalls += 1;
6212
+ return {
6213
+ requiresExecution: true,
6214
+ summaryRole: "worker",
6215
+ steps: [{ role: "worker", goal: "unexpected", artifactsRequired: true }],
6216
+ };
6217
+ },
6218
+ }),
6219
+ buildRunnerDeliveryDeps: () => ({}),
6220
+ buildRunnerRuntimeDeps: () => ({}),
6221
+ resolveConversationPeerBots: () => [
6222
+ { id: "bot-lead-1", name: "RyoAI_bot" },
6223
+ ],
6224
+ },
6225
+ });
6226
+ push(
6227
+ "single_bot_conversational_greeting_request_skips_planner_and_worker",
6228
+ processed.kind === "replied"
6229
+ && aiCalls === 1
6230
+ && plannerCalls === 0
6231
+ && deliveryCalls === 1
6232
+ && String(processed.result?.assignment_validation_status || "") === "conversation_assignment"
6233
+ && ensureArray(processed.result?.assignment_validation_modes).includes("conversation_contribution")
6234
+ && deliveredText === "@SangHoon01_bot 안녕하세요.",
6235
+ `kind=${String(processed.kind || "(none)")} ai_calls=${aiCalls} planner_calls=${plannerCalls} delivery_calls=${deliveryCalls} assignment_status=${String(processed.result?.assignment_validation_status || "(none)")} modes=${JSON.stringify(processed.result?.assignment_validation_modes || [])} delivered=${String(deliveredText || "(none)")}`,
6236
+ );
6237
+ } catch (err) {
6238
+ push("single_bot_conversational_greeting_request_skips_planner_and_worker", false, String(err?.message || err));
6239
+ }
6240
+
6241
+ try {
6242
+ let aiCalls = 0;
6243
+ let deliveryCalls = 0;
6244
+ let deliveredText = "";
6245
+ let capturedFailureFacts = null;
6056
6246
  const workspaceDir = fs.mkdtempSync(path.join(os.tmpdir(), "metheus-runner-selftest-ai-failure-explainer-"));
6057
6247
  const processed = await processRunnerSelectedRecord({
6058
6248
  routeKey: "single-bot-human-work-request-ai-failure-explainer-key",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "metheus-governance-mcp-cli",
3
- "version": "0.2.266",
3
+ "version": "0.2.268",
4
4
  "description": "Metheus Governance MCP CLI (setup + stdio proxy)",
5
5
  "type": "module",
6
6
  "files": [