metheus-governance-mcp-cli 0.2.208 → 0.2.209

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
@@ -2634,7 +2634,8 @@ function resolveRunnerRequestClaimIntent({
2634
2634
  if (explicitIntent) {
2635
2635
  return explicitIntent;
2636
2636
  }
2637
- return inferRunnerRequestClaimIntent(selectedRecord);
2637
+ const inferredIntent = inferRunnerRequestClaimIntent(selectedRecord);
2638
+ return inferredIntent === "status_query" ? inferredIntent : "";
2638
2639
  }
2639
2640
 
2640
2641
  function buildSyntheticReplyChainConversationID(normalizedRoute, chatID, anchorMessageID) {
@@ -3339,8 +3340,6 @@ async function claimRunnerRequestForHumanComment({
3339
3340
  ).trim();
3340
3341
  const preferredNormalizedIntent = String(
3341
3342
  normalizedSharedHumanIntent.intentType
3342
- || sharedConversationSource.normalized_intent
3343
- || referencedRequest.normalized_intent
3344
3343
  || resolvedNormalizedIntent
3345
3344
  || "",
3346
3345
  ).trim().toLowerCase();
@@ -3381,50 +3380,70 @@ async function claimRunnerRequestForHumanComment({
3381
3380
  conversation_id: resolvedConversationID,
3382
3381
  selected_bot_usernames: uniqueOrderedStrings(selectedBotUsernames, normalizeTelegramMentionUsername),
3383
3382
  conversation_intent_mode: String(
3384
- existing.conversation_intent_mode || sharedConversationSource.conversation_intent_mode || referencedRequest.conversation_intent_mode || normalizedSharedHumanIntent.intentMode || "",
3383
+ normalizedSharedHumanIntent.intentMode
3384
+ || existing.conversation_intent_mode
3385
+ || sharedConversationSource.conversation_intent_mode
3386
+ || referencedRequest.conversation_intent_mode
3387
+ || "",
3385
3388
  ).trim().toLowerCase(),
3386
3389
  conversation_lead_bot: normalizeTelegramMentionUsername(
3387
- existing.conversation_lead_bot || sharedConversationSource.conversation_lead_bot || referencedRequest.conversation_lead_bot || normalizedSharedHumanIntent.leadBotSelector,
3390
+ normalizedSharedHumanIntent.leadBotSelector
3391
+ || existing.conversation_lead_bot
3392
+ || sharedConversationSource.conversation_lead_bot
3393
+ || referencedRequest.conversation_lead_bot,
3388
3394
  ),
3389
3395
  conversation_summary_bot: normalizeTelegramMentionUsername(
3390
- existing.conversation_summary_bot || sharedConversationSource.conversation_summary_bot || referencedRequest.conversation_summary_bot || normalizedSharedHumanIntent.summaryBotSelector,
3396
+ normalizedSharedHumanIntent.summaryBotSelector
3397
+ || existing.conversation_summary_bot
3398
+ || sharedConversationSource.conversation_summary_bot
3399
+ || referencedRequest.conversation_summary_bot,
3391
3400
  ),
3392
3401
  conversation_participants: uniqueOrderedStrings(
3393
- ensureArray(existing.conversation_participants).length
3394
- ? existing.conversation_participants
3395
- : ensureArray(sharedConversationSource.conversation_participants).length
3396
- ? sharedConversationSource.conversation_participants
3397
- : ensureArray(referencedRequest.conversation_participants).length
3398
- ? referencedRequest.conversation_participants
3399
- : normalizedSharedHumanIntent.participantSelectors,
3402
+ ensureArray(normalizedSharedHumanIntent.participantSelectors).length
3403
+ ? normalizedSharedHumanIntent.participantSelectors
3404
+ : ensureArray(existing.conversation_participants).length
3405
+ ? existing.conversation_participants
3406
+ : ensureArray(sharedConversationSource.conversation_participants).length
3407
+ ? sharedConversationSource.conversation_participants
3408
+ : ensureArray(referencedRequest.conversation_participants).length
3409
+ ? referencedRequest.conversation_participants
3410
+ : [],
3400
3411
  normalizeTelegramMentionUsername,
3401
3412
  ),
3402
3413
  conversation_initial_responders: uniqueOrderedStrings(
3403
- ensureArray(existing.conversation_initial_responders).length
3404
- ? existing.conversation_initial_responders
3405
- : ensureArray(sharedConversationSource.conversation_initial_responders).length
3406
- ? sharedConversationSource.conversation_initial_responders
3407
- : ensureArray(referencedRequest.conversation_initial_responders).length
3408
- ? referencedRequest.conversation_initial_responders
3409
- : normalizedSharedHumanIntent.initialResponderSelectors,
3414
+ ensureArray(normalizedSharedHumanIntent.initialResponderSelectors).length
3415
+ ? normalizedSharedHumanIntent.initialResponderSelectors
3416
+ : ensureArray(existing.conversation_initial_responders).length
3417
+ ? existing.conversation_initial_responders
3418
+ : ensureArray(sharedConversationSource.conversation_initial_responders).length
3419
+ ? sharedConversationSource.conversation_initial_responders
3420
+ : ensureArray(referencedRequest.conversation_initial_responders).length
3421
+ ? referencedRequest.conversation_initial_responders
3422
+ : [],
3410
3423
  normalizeTelegramMentionUsername,
3411
3424
  ),
3412
3425
  conversation_allowed_responders: uniqueOrderedStrings(
3413
- ensureArray(existing.conversation_allowed_responders).length
3414
- ? existing.conversation_allowed_responders
3415
- : ensureArray(sharedConversationSource.conversation_allowed_responders).length
3416
- ? sharedConversationSource.conversation_allowed_responders
3417
- : ensureArray(referencedRequest.conversation_allowed_responders).length
3418
- ? referencedRequest.conversation_allowed_responders
3419
- : normalizedSharedHumanIntent.allowedResponderSelectors,
3426
+ ensureArray(normalizedSharedHumanIntent.allowedResponderSelectors).length
3427
+ ? normalizedSharedHumanIntent.allowedResponderSelectors
3428
+ : ensureArray(existing.conversation_allowed_responders).length
3429
+ ? existing.conversation_allowed_responders
3430
+ : ensureArray(sharedConversationSource.conversation_allowed_responders).length
3431
+ ? sharedConversationSource.conversation_allowed_responders
3432
+ : ensureArray(referencedRequest.conversation_allowed_responders).length
3433
+ ? referencedRequest.conversation_allowed_responders
3434
+ : [],
3420
3435
  normalizeTelegramMentionUsername,
3421
3436
  ),
3422
- conversation_allow_bot_to_bot: existing.conversation_allow_bot_to_bot === true
3437
+ conversation_allow_bot_to_bot: normalizedSharedHumanIntent.allowBotToBot === true
3438
+ || existing.conversation_allow_bot_to_bot === true
3423
3439
  || sharedConversationSource.conversation_allow_bot_to_bot === true
3424
- || referencedRequest.conversation_allow_bot_to_bot === true
3425
- || normalizedSharedHumanIntent.allowBotToBot === true,
3440
+ || referencedRequest.conversation_allow_bot_to_bot === true,
3426
3441
  conversation_reply_expectation: String(
3427
- existing.conversation_reply_expectation || sharedConversationSource.conversation_reply_expectation || referencedRequest.conversation_reply_expectation || normalizedSharedHumanIntent.replyExpectation || "",
3442
+ normalizedSharedHumanIntent.replyExpectation
3443
+ || existing.conversation_reply_expectation
3444
+ || sharedConversationSource.conversation_reply_expectation
3445
+ || referencedRequest.conversation_reply_expectation
3446
+ || "",
3428
3447
  ).trim().toLowerCase(),
3429
3448
  execution_contract_type: String(
3430
3449
  existing.execution_contract_type || sharedConversationSource.execution_contract_type || referencedRequest.execution_contract_type || "",
@@ -3450,7 +3469,7 @@ async function claimRunnerRequestForHumanComment({
3450
3469
  : [],
3451
3470
  normalizeTelegramMentionUsername,
3452
3471
  ),
3453
- normalized_intent: preferredNormalizedIntent,
3472
+ normalized_intent: String(preferredNormalizedIntent || existing.normalized_intent || "").trim().toLowerCase(),
3454
3473
  status: "claimed",
3455
3474
  claimed_by_route: String(routeKey || "").trim(),
3456
3475
  claimed_at: firstNonEmptyString([existing.claimed_at, nowISO]) || nowISO,
@@ -3501,6 +3520,89 @@ function isActionableRunnerRequestIntent(rawIntent) {
3501
3520
  return Boolean(normalizedIntent) && !isInformationalRunnerRequestIntent(normalizedIntent);
3502
3521
  }
3503
3522
 
3523
+ function runnerRequestHasConversationContract(requestRaw) {
3524
+ const request = safeObject(requestRaw);
3525
+ return Boolean(
3526
+ String(request.conversation_id || "").trim()
3527
+ || String(request.conversation_intent_mode || "").trim()
3528
+ || String(request.conversation_reply_expectation || "").trim()
3529
+ || ensureArray(request.conversation_participants).length
3530
+ || ensureArray(request.conversation_initial_responders).length
3531
+ || ensureArray(request.conversation_allowed_responders).length
3532
+ );
3533
+ }
3534
+
3535
+ function runnerRequestHasCompleteConversationContract(requestRaw) {
3536
+ const request = safeObject(requestRaw);
3537
+ const intentMode = String(request.conversation_intent_mode || "").trim().toLowerCase();
3538
+ const conversationID = String(request.conversation_id || "").trim();
3539
+ const replyExpectation = String(request.conversation_reply_expectation || "").trim().toLowerCase();
3540
+ const participants = ensureArray(request.conversation_participants);
3541
+ const initialResponders = ensureArray(request.conversation_initial_responders);
3542
+ const allowedResponders = ensureArray(request.conversation_allowed_responders);
3543
+ if (!runnerRequestHasConversationContract(request)) {
3544
+ return false;
3545
+ }
3546
+ if (!conversationID || !intentMode || !replyExpectation) {
3547
+ return false;
3548
+ }
3549
+ if (intentMode === "single_bot") {
3550
+ return allowedResponders.length > 0 || participants.length > 0;
3551
+ }
3552
+ return participants.length > 0 && initialResponders.length > 0 && allowedResponders.length > 0;
3553
+ }
3554
+
3555
+ function runnerRequestHasExecutionContract(requestRaw) {
3556
+ const request = safeObject(requestRaw);
3557
+ return Boolean(
3558
+ String(request.execution_contract_type || "").trim()
3559
+ || request.execution_contract_actionable === true
3560
+ || ensureArray(request.execution_contract_targets).length
3561
+ || ensureArray(request.next_expected_responders).length
3562
+ );
3563
+ }
3564
+
3565
+ function runnerRequestHasCompleteExecutionContract(requestRaw) {
3566
+ const request = safeObject(requestRaw);
3567
+ const executionContractType = String(request.execution_contract_type || "").trim().toLowerCase();
3568
+ const executionTargets = ensureArray(request.execution_contract_targets);
3569
+ const nextExpectedResponders = ensureArray(request.next_expected_responders);
3570
+ if (!runnerRequestHasExecutionContract(request)) {
3571
+ return false;
3572
+ }
3573
+ if (!executionContractType) {
3574
+ return false;
3575
+ }
3576
+ if (executionContractType === "delegation") {
3577
+ return executionTargets.length > 0 || nextExpectedResponders.length > 0;
3578
+ }
3579
+ if (executionContractType === "summary_request") {
3580
+ return nextExpectedResponders.length > 0 || executionTargets.length > 0;
3581
+ }
3582
+ return true;
3583
+ }
3584
+
3585
+ function runnerRequestHasContractSignals(requestRaw) {
3586
+ const request = safeObject(requestRaw);
3587
+ return runnerRequestHasConversationContract(request) || runnerRequestHasExecutionContract(request);
3588
+ }
3589
+
3590
+ function runnerRequestRequiresActionableContract(requestRaw) {
3591
+ const request = safeObject(requestRaw);
3592
+ const replyExpectation = String(request.conversation_reply_expectation || "").trim().toLowerCase();
3593
+ const executionContractType = String(request.execution_contract_type || "").trim().toLowerCase();
3594
+ if (request.execution_contract_actionable === true) {
3595
+ return true;
3596
+ }
3597
+ if (replyExpectation === "actionable") {
3598
+ return true;
3599
+ }
3600
+ if (["delegation", "direct_result"].includes(executionContractType)) {
3601
+ return true;
3602
+ }
3603
+ return false;
3604
+ }
3605
+
3504
3606
  function loadRunnerRequestByKey(requestKey) {
3505
3607
  const key = String(requestKey || "").trim();
3506
3608
  if (!key) {
@@ -3512,7 +3614,7 @@ function loadRunnerRequestByKey(requestKey) {
3512
3614
  function actionableRunnerRequestMissingRootWorkItem(requestRaw) {
3513
3615
  const request = safeObject(requestRaw);
3514
3616
  return (
3515
- isActionableRunnerRequestIntent(request.normalized_intent)
3617
+ runnerRequestRequiresActionableContract(request)
3516
3618
  && !String(request.root_work_item_id || "").trim()
3517
3619
  );
3518
3620
  }
@@ -3824,7 +3926,7 @@ async function ensureRunnerRootWorkItemForRequest({
3824
3926
  requestKey: key,
3825
3927
  };
3826
3928
  }
3827
- if (!isActionableRunnerRequestIntent(existing.normalized_intent)) {
3929
+ if (!runnerRequestRequiresActionableContract(existing)) {
3828
3930
  return {
3829
3931
  ok: true,
3830
3932
  requestKey: key,
@@ -4216,7 +4318,7 @@ function resolveRunnerContinuationRequestForBotReply({
4216
4318
  execution_contract_actionable: session.last_execution_contract_actionable === true,
4217
4319
  execution_contract_targets: ensureArray(session.last_execution_contract_targets),
4218
4320
  next_expected_responders: ensureArray(session.next_expected_responders),
4219
- normalized_intent: String(safeObject(sessionMatch.routeState).last_intent_type || "").trim().toLowerCase(),
4321
+ normalized_intent: String(fallbackRequest.normalized_intent || "").trim().toLowerCase(),
4220
4322
  status: "running",
4221
4323
  claimed_by_route: String(sessionMatch.routeKey || "").trim(),
4222
4324
  claimed_at: firstNonEmptyString([session.started_at, nowISO]),
@@ -4333,6 +4435,26 @@ function markRunnerRequestLifecycle({
4333
4435
  normalizeTelegramMentionUsername,
4334
4436
  ).filter((selector) => selector && selector !== normalizedCurrentBotSelector);
4335
4437
  const shouldRemainRunningAfterReply = continuationSelectors.length > 0;
4438
+ const nextConversationIntentMode = String(
4439
+ conversationIntentMode
4440
+ || existing.conversation_intent_mode
4441
+ || "",
4442
+ ).trim().toLowerCase();
4443
+ const nextExecutionContractType = String(
4444
+ executionContractType
4445
+ || existing.execution_contract_type
4446
+ || "",
4447
+ ).trim().toLowerCase();
4448
+ const nextNormalizedIntent = (() => {
4449
+ const explicitIntent = String(normalizedIntent || "").trim().toLowerCase();
4450
+ if (explicitIntent) {
4451
+ return explicitIntent;
4452
+ }
4453
+ if (nextConversationIntentMode && !nextExecutionContractType) {
4454
+ return "";
4455
+ }
4456
+ return String(existing.normalized_intent || "").trim().toLowerCase();
4457
+ })();
4336
4458
  const normalizedOutcome = String(outcome || "").trim().toLowerCase();
4337
4459
  const nextStatus = (() => {
4338
4460
  if (normalizedOutcome === "claimed") return "claimed";
@@ -4364,11 +4486,7 @@ function markRunnerRequestLifecycle({
4364
4486
  ensureArray(allowedResponders).length ? allowedResponders : existing.conversation_allowed_responders,
4365
4487
  normalizeTelegramMentionUsername,
4366
4488
  ),
4367
- conversation_intent_mode: String(
4368
- conversationIntentMode
4369
- || existing.conversation_intent_mode
4370
- || "",
4371
- ).trim().toLowerCase(),
4489
+ conversation_intent_mode: nextConversationIntentMode,
4372
4490
  conversation_lead_bot: normalizeTelegramMentionUsername(
4373
4491
  conversationLeadBot
4374
4492
  || existing.conversation_lead_bot
@@ -4386,11 +4504,7 @@ function markRunnerRequestLifecycle({
4386
4504
  || existing.conversation_reply_expectation
4387
4505
  || "",
4388
4506
  ).trim().toLowerCase(),
4389
- execution_contract_type: String(
4390
- executionContractType
4391
- || existing.execution_contract_type
4392
- || "",
4393
- ).trim().toLowerCase(),
4507
+ execution_contract_type: nextExecutionContractType,
4394
4508
  execution_contract_actionable: executionContractActionable === true
4395
4509
  || existing.execution_contract_actionable === true,
4396
4510
  execution_contract_targets: uniqueOrderedStrings(
@@ -4401,7 +4515,7 @@ function markRunnerRequestLifecycle({
4401
4515
  ensureArray(nextExpectedResponders).length ? nextExpectedResponders : existing.next_expected_responders,
4402
4516
  normalizeTelegramMentionUsername,
4403
4517
  ),
4404
- normalized_intent: String(normalizedIntent || existing.normalized_intent || "").trim().toLowerCase(),
4518
+ normalized_intent: nextNormalizedIntent,
4405
4519
  status: nextStatus,
4406
4520
  started_at: firstNonEmptyString([existing.started_at, nowISO]),
4407
4521
  completed_at: nextStatus === "completed" ? nowISO : String(existing.completed_at || "").trim(),
@@ -4842,7 +4956,7 @@ async function syncRunnerRequestLedgerForProjectToServer({ normalizedRoute, runt
4842
4956
  })[String(request.request_key || "").trim()]
4843
4957
  : request;
4844
4958
  if (
4845
- isActionableRunnerRequestIntent(recoveredRequest.normalized_intent)
4959
+ runnerRequestRequiresActionableContract(recoveredRequest)
4846
4960
  && !String(recoveredRequest.root_work_item_id || "").trim()
4847
4961
  ) {
4848
4962
  continue;
@@ -2085,7 +2085,9 @@ export function serializeLocalAIResult(result) {
2085
2085
  function buildConversationIntentAnalysisPrompt({
2086
2086
  messageText,
2087
2087
  managedBots,
2088
+ contractGuardrail = null,
2088
2089
  }) {
2090
+ const guardrail = safeObject(contractGuardrail);
2089
2091
  const bots = ensureArray(managedBots).map((item) => {
2090
2092
  const bot = safeObject(item);
2091
2093
  return {
@@ -2129,6 +2131,15 @@ function buildConversationIntentAnalysisPrompt({
2129
2131
  "- If the human asks bots to discuss, debate, review, brainstorm, compare opinions, or hold a conversation about a topic, default to reply_expectation=informational unless they explicitly ask for concrete execution/output now.",
2130
2132
  "- reply_expectation=actionable when the human is asking the bot(s) to actually do work now, produce concrete results, create/update files, delegate concrete tasks, or otherwise execute immediately.",
2131
2133
  "- reply_expectation=informational when the human is only asking for explanation, status, location, clarification, discussion, review, brainstorming, or other non-execution information.",
2134
+ guardrail.require_complete_contract === true
2135
+ ? "- Contract guardrail: do not leave intent_type, reply_expectation, participants, initial_responders, or allowed_responders empty when the message clearly addresses managed bots."
2136
+ : "",
2137
+ guardrail.require_explicit_actionable_intent === true
2138
+ ? "- Contract guardrail: if reply_expectation=actionable, intent_type must be exactly one of ctxpack_mutation, workitem_mutation, or general_execution."
2139
+ : "",
2140
+ String(guardrail.reason || "").trim()
2141
+ ? `- Contract guardrail reason: ${String(guardrail.reason || "").trim()}`
2142
+ : "",
2132
2143
  "",
2133
2144
  `managed_bots=${JSON.stringify(bots)}`,
2134
2145
  `human_message=${JSON.stringify(String(messageText || "").trim())}`,
@@ -2471,6 +2482,7 @@ export function analyzeHumanConversationIntentWithAI({
2471
2482
  messageText,
2472
2483
  managedBots,
2473
2484
  workspaceDir,
2485
+ contractGuardrail = null,
2474
2486
  client = "",
2475
2487
  model = "",
2476
2488
  env = process.env,
@@ -2498,6 +2510,7 @@ export function analyzeHumanConversationIntentWithAI({
2498
2510
  promptText: buildConversationIntentAnalysisPrompt({
2499
2511
  messageText,
2500
2512
  managedBots: bots,
2513
+ contractGuardrail,
2501
2514
  }),
2502
2515
  workspaceDir,
2503
2516
  model: parserModel,