metheus-governance-mcp-cli 0.2.201 → 0.2.203

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
@@ -2167,6 +2167,33 @@ function normalizeBotRunnerRequests(rawRequests, nowMs = Date.now()) {
2167
2167
  conversation_allowed_responders: ensureArray(entry.conversation_allowed_responders || entry.conversationAllowedResponders)
2168
2168
  .map((value) => normalizeTelegramMentionUsername(value))
2169
2169
  .filter(Boolean),
2170
+ conversation_intent_mode: String(entry.conversation_intent_mode || entry.conversationIntentMode || "").trim().toLowerCase(),
2171
+ conversation_lead_bot: normalizeTelegramMentionUsername(entry.conversation_lead_bot || entry.conversationLeadBot),
2172
+ conversation_summary_bot: normalizeTelegramMentionUsername(entry.conversation_summary_bot || entry.conversationSummaryBot),
2173
+ conversation_participants: ensureArray(entry.conversation_participants || entry.conversationParticipants)
2174
+ .map((value) => normalizeTelegramMentionUsername(value))
2175
+ .filter(Boolean),
2176
+ conversation_initial_responders: ensureArray(entry.conversation_initial_responders || entry.conversationInitialResponders)
2177
+ .map((value) => normalizeTelegramMentionUsername(value))
2178
+ .filter(Boolean),
2179
+ conversation_allow_bot_to_bot: boolFromRaw(
2180
+ entry.conversation_allow_bot_to_bot ?? entry.conversationAllowBotToBot,
2181
+ false,
2182
+ ),
2183
+ conversation_reply_expectation: String(
2184
+ entry.conversation_reply_expectation || entry.conversationReplyExpectation || "",
2185
+ ).trim().toLowerCase(),
2186
+ execution_contract_type: String(entry.execution_contract_type || entry.executionContractType || "").trim().toLowerCase(),
2187
+ execution_contract_actionable: boolFromRaw(
2188
+ entry.execution_contract_actionable ?? entry.executionContractActionable,
2189
+ false,
2190
+ ),
2191
+ execution_contract_targets: ensureArray(entry.execution_contract_targets || entry.executionContractTargets)
2192
+ .map((value) => normalizeTelegramMentionUsername(value))
2193
+ .filter(Boolean),
2194
+ next_expected_responders: ensureArray(entry.next_expected_responders || entry.nextExpectedResponders)
2195
+ .map((value) => normalizeTelegramMentionUsername(value))
2196
+ .filter(Boolean),
2170
2197
  normalized_intent: String(entry.normalized_intent || entry.normalizedIntent || "").trim().toLowerCase(),
2171
2198
  status,
2172
2199
  claimed_by_route: String(entry.claimed_by_route || entry.claimedByRoute || "").trim(),
@@ -2553,6 +2580,86 @@ async function findServerRunnerRequestForMessageID({
2553
2580
  }
2554
2581
  }
2555
2582
 
2583
+ function runnerRequestHasConversationContractData(entryRaw) {
2584
+ const entry = safeObject(entryRaw);
2585
+ return Boolean(
2586
+ String(entry.conversation_intent_mode || "").trim()
2587
+ || String(entry.conversation_lead_bot || "").trim()
2588
+ || String(entry.conversation_summary_bot || "").trim()
2589
+ || ensureArray(entry.conversation_participants).length
2590
+ || ensureArray(entry.conversation_initial_responders).length
2591
+ || ensureArray(entry.conversation_allowed_responders).length
2592
+ || entry.conversation_allow_bot_to_bot === true
2593
+ || String(entry.conversation_reply_expectation || "").trim()
2594
+ || String(entry.execution_contract_type || "").trim()
2595
+ || entry.execution_contract_actionable === true
2596
+ || ensureArray(entry.execution_contract_targets).length
2597
+ || ensureArray(entry.next_expected_responders).length
2598
+ );
2599
+ }
2600
+
2601
+ function pickRunnerSharedConversationSourceRequest(entries = [], excludeRequestKey = "") {
2602
+ const excludedKey = String(excludeRequestKey || "").trim();
2603
+ const matched = sortRunnerRequestEntriesNewestFirst(ensureArray(entries))
2604
+ .filter((entryRaw) => String(safeObject(entryRaw).request_key || "").trim() !== excludedKey)
2605
+ .filter((entryRaw) => {
2606
+ const entry = safeObject(entryRaw);
2607
+ return runnerRequestHasConversationContractData(entry)
2608
+ || String(entry.root_work_item_id || "").trim()
2609
+ || String(entry.root_thread_id || "").trim();
2610
+ });
2611
+ return safeObject(matched[0]);
2612
+ }
2613
+
2614
+ async function findServerRunnerConversationSourceRequestForMessageID({
2615
+ normalizedRoute,
2616
+ runtime,
2617
+ chatID,
2618
+ messageID,
2619
+ excludeRequestKey = "",
2620
+ }) {
2621
+ const projectID = String(normalizedRoute?.projectID || "").trim();
2622
+ const provider = String(normalizedRoute?.provider || "").trim();
2623
+ const normalizedChatID = String(chatID || "").trim();
2624
+ const normalizedMessageID = intFromRawAllowZero(messageID, 0);
2625
+ if (
2626
+ !projectID
2627
+ || !provider
2628
+ || !normalizedChatID
2629
+ || normalizedMessageID <= 0
2630
+ || !runtime?.baseURL
2631
+ || !runtime?.token
2632
+ ) {
2633
+ return null;
2634
+ }
2635
+ try {
2636
+ const serverRequests = await listProjectRunnerRequests({
2637
+ siteBaseURL: runtime.baseURL,
2638
+ projectID,
2639
+ token: runtime.token,
2640
+ timeoutSeconds: runtime.timeoutSeconds,
2641
+ actorUserID: runtime.actor?.user_id,
2642
+ limit: 500,
2643
+ offset: 0,
2644
+ });
2645
+ const matched = serverRequests.filter((entryRaw) => {
2646
+ const entry = safeObject(entryRaw);
2647
+ return (
2648
+ String(entry.project_id || "").trim() === projectID
2649
+ && String(entry.provider || "").trim() === provider
2650
+ && String(entry.chat_id || "").trim() === normalizedChatID
2651
+ && (
2652
+ intFromRawAllowZero(entry.source_message_id, 0) === normalizedMessageID
2653
+ || intFromRawAllowZero(entry.last_source_message_id, 0) === normalizedMessageID
2654
+ )
2655
+ );
2656
+ });
2657
+ return pickRunnerSharedConversationSourceRequest(matched, excludeRequestKey);
2658
+ } catch {
2659
+ return null;
2660
+ }
2661
+ }
2662
+
2556
2663
  async function loadRunnerArchiveThreadMessageIndex({
2557
2664
  runtime,
2558
2665
  threadID,
@@ -2900,7 +3007,6 @@ async function claimRunnerRequestForHumanComment({
2900
3007
  });
2901
3008
  const replyChainContext = safeObject(replyChainResolution.replyChainContext);
2902
3009
  const referencedRequest = safeObject(replyChainContext.referencedRequest);
2903
- const conversationID = String(parsed.conversationID || replyChainContext.conversationID || "").trim();
2904
3010
  const resolvedNormalizedIntent = resolveRunnerRequestClaimIntent({
2905
3011
  normalizedIntent,
2906
3012
  selectedRecord,
@@ -2912,14 +3018,19 @@ async function claimRunnerRequestForHumanComment({
2912
3018
  normalizedIntent: resolvedNormalizedIntent,
2913
3019
  });
2914
3020
  let stateForClaim = safeObject(replyChainResolution.state);
3021
+ const baseConversationID = String(
3022
+ parsed.conversationID
3023
+ || replyChainContext.conversationID
3024
+ || "",
3025
+ ).trim();
2915
3026
  if (
2916
3027
  Object.keys(referencedRequest).length > 0
2917
- && conversationID
3028
+ && baseConversationID
2918
3029
  && !String(referencedRequest.conversation_id || "").trim()
2919
3030
  && String(referencedRequest.request_key || "").trim()
2920
3031
  ) {
2921
3032
  const backfilled = upsertRunnerRequest(stateForClaim, referencedRequest.request_key, {
2922
- conversation_id: conversationID,
3033
+ conversation_id: baseConversationID,
2923
3034
  });
2924
3035
  stateForClaim = {
2925
3036
  ...stateForClaim,
@@ -2928,6 +3039,35 @@ async function claimRunnerRequestForHumanComment({
2928
3039
  }
2929
3040
  const requests = normalizeBotRunnerRequests(stateForClaim.requests);
2930
3041
  const existing = safeObject(requests[requestKey]);
3042
+ const currentMessageID = intFromRawAllowZero(parsed.messageID, 0);
3043
+ let sharedConversationSource = currentMessageID > 0
3044
+ ? pickRunnerSharedConversationSourceRequest(
3045
+ findRunnerRequestsForMessageID(stateForClaim, normalizedRoute, {
3046
+ chatID: String(parsed.chatID || parsed.chatId || "").trim(),
3047
+ messageID: currentMessageID,
3048
+ }),
3049
+ requestKey,
3050
+ )
3051
+ : {};
3052
+ if (
3053
+ !Object.keys(sharedConversationSource).length
3054
+ && currentMessageID > 0
3055
+ && runtime?.baseURL
3056
+ && runtime?.token
3057
+ ) {
3058
+ sharedConversationSource = safeObject(await findServerRunnerConversationSourceRequestForMessageID({
3059
+ normalizedRoute,
3060
+ runtime,
3061
+ chatID: String(parsed.chatID || parsed.chatId || "").trim(),
3062
+ messageID: currentMessageID,
3063
+ excludeRequestKey: requestKey,
3064
+ }));
3065
+ }
3066
+ const resolvedConversationID = String(
3067
+ baseConversationID
3068
+ || sharedConversationSource.conversation_id
3069
+ || "",
3070
+ ).trim();
2931
3071
  if (isFinalRunnerRequestStatus(existing.status)) {
2932
3072
  return {
2933
3073
  ok: false,
@@ -2954,23 +3094,87 @@ async function claimRunnerRequestForHumanComment({
2954
3094
  source_message_id: intFromRawAllowZero(parsed.messageID, 0) || undefined,
2955
3095
  root_comment_id: String(selectedRecord?.id || "").trim(),
2956
3096
  root_comment_kind: commentKind,
2957
- conversation_id: conversationID,
3097
+ conversation_id: resolvedConversationID,
2958
3098
  selected_bot_usernames: uniqueOrderedStrings(selectedBotUsernames, normalizeTelegramMentionUsername),
3099
+ conversation_intent_mode: String(
3100
+ existing.conversation_intent_mode || sharedConversationSource.conversation_intent_mode || referencedRequest.conversation_intent_mode || "",
3101
+ ).trim().toLowerCase(),
3102
+ conversation_lead_bot: normalizeTelegramMentionUsername(
3103
+ existing.conversation_lead_bot || sharedConversationSource.conversation_lead_bot || referencedRequest.conversation_lead_bot,
3104
+ ),
3105
+ conversation_summary_bot: normalizeTelegramMentionUsername(
3106
+ existing.conversation_summary_bot || sharedConversationSource.conversation_summary_bot || referencedRequest.conversation_summary_bot,
3107
+ ),
3108
+ conversation_participants: uniqueOrderedStrings(
3109
+ ensureArray(existing.conversation_participants).length
3110
+ ? existing.conversation_participants
3111
+ : ensureArray(sharedConversationSource.conversation_participants).length
3112
+ ? sharedConversationSource.conversation_participants
3113
+ : referencedRequest.conversation_participants,
3114
+ normalizeTelegramMentionUsername,
3115
+ ),
3116
+ conversation_initial_responders: uniqueOrderedStrings(
3117
+ ensureArray(existing.conversation_initial_responders).length
3118
+ ? existing.conversation_initial_responders
3119
+ : ensureArray(sharedConversationSource.conversation_initial_responders).length
3120
+ ? sharedConversationSource.conversation_initial_responders
3121
+ : referencedRequest.conversation_initial_responders,
3122
+ normalizeTelegramMentionUsername,
3123
+ ),
3124
+ conversation_allowed_responders: uniqueOrderedStrings(
3125
+ ensureArray(existing.conversation_allowed_responders).length
3126
+ ? existing.conversation_allowed_responders
3127
+ : ensureArray(sharedConversationSource.conversation_allowed_responders).length
3128
+ ? sharedConversationSource.conversation_allowed_responders
3129
+ : referencedRequest.conversation_allowed_responders,
3130
+ normalizeTelegramMentionUsername,
3131
+ ),
3132
+ conversation_allow_bot_to_bot: existing.conversation_allow_bot_to_bot === true
3133
+ || sharedConversationSource.conversation_allow_bot_to_bot === true
3134
+ || referencedRequest.conversation_allow_bot_to_bot === true,
3135
+ conversation_reply_expectation: String(
3136
+ existing.conversation_reply_expectation || sharedConversationSource.conversation_reply_expectation || referencedRequest.conversation_reply_expectation || "",
3137
+ ).trim().toLowerCase(),
3138
+ execution_contract_type: String(
3139
+ existing.execution_contract_type || sharedConversationSource.execution_contract_type || referencedRequest.execution_contract_type || "",
3140
+ ).trim().toLowerCase(),
3141
+ execution_contract_actionable: existing.execution_contract_actionable === true
3142
+ || sharedConversationSource.execution_contract_actionable === true
3143
+ || referencedRequest.execution_contract_actionable === true,
3144
+ execution_contract_targets: uniqueOrderedStrings(
3145
+ ensureArray(existing.execution_contract_targets).length
3146
+ ? existing.execution_contract_targets
3147
+ : ensureArray(sharedConversationSource.execution_contract_targets).length
3148
+ ? sharedConversationSource.execution_contract_targets
3149
+ : referencedRequest.execution_contract_targets,
3150
+ normalizeTelegramMentionUsername,
3151
+ ),
3152
+ next_expected_responders: uniqueOrderedStrings(
3153
+ ensureArray(existing.next_expected_responders).length
3154
+ ? existing.next_expected_responders
3155
+ : ensureArray(sharedConversationSource.next_expected_responders).length
3156
+ ? sharedConversationSource.next_expected_responders
3157
+ : referencedRequest.next_expected_responders,
3158
+ normalizeTelegramMentionUsername,
3159
+ ),
2959
3160
  normalized_intent: resolvedNormalizedIntent,
2960
3161
  status: "claimed",
2961
3162
  claimed_by_route: String(routeKey || "").trim(),
2962
3163
  claimed_at: firstNonEmptyString([existing.claimed_at, nowISO]) || nowISO,
2963
- root_work_item_id: String(existing.root_work_item_id || referencedRequest.root_work_item_id || "").trim(),
2964
- root_work_item_title: String(existing.root_work_item_title || referencedRequest.root_work_item_title || "").trim(),
3164
+ root_work_item_id: String(existing.root_work_item_id || sharedConversationSource.root_work_item_id || referencedRequest.root_work_item_id || "").trim(),
3165
+ root_work_item_title: String(existing.root_work_item_title || sharedConversationSource.root_work_item_title || referencedRequest.root_work_item_title || "").trim(),
2965
3166
  root_work_item_status: normalizeRunnerWorkItemStatus(
2966
- existing.root_work_item_status || referencedRequest.root_work_item_status,
3167
+ existing.root_work_item_status || sharedConversationSource.root_work_item_status || referencedRequest.root_work_item_status,
2967
3168
  ),
2968
- root_thread_id: String(existing.root_thread_id || referencedRequest.root_thread_id || "").trim(),
3169
+ root_thread_id: String(existing.root_thread_id || sharedConversationSource.root_thread_id || referencedRequest.root_thread_id || "").trim(),
2969
3170
  root_work_item_created_at: firstNonEmptyString([
2970
3171
  existing.root_work_item_created_at,
3172
+ sharedConversationSource.root_work_item_created_at,
2971
3173
  referencedRequest.root_work_item_created_at,
2972
3174
  ]),
2973
- root_work_item_last_error: String(existing.root_work_item_last_error || "").trim(),
3175
+ root_work_item_last_error: String(
3176
+ existing.root_work_item_last_error || sharedConversationSource.root_work_item_last_error || "",
3177
+ ).trim(),
2974
3178
  last_comment_id: String(selectedRecord?.id || "").trim(),
2975
3179
  last_comment_kind: commentKind,
2976
3180
  last_source_message_id: intFromRawAllowZero(parsed.messageID, 0) || undefined,
@@ -2980,7 +3184,7 @@ async function claimRunnerRequestForHumanComment({
2980
3184
  provider: String(normalizedRoute?.provider || "").trim(),
2981
3185
  request_key: requestKey,
2982
3186
  route_key: String(routeKey || "").trim(),
2983
- conversation_id: conversationID,
3187
+ conversation_id: resolvedConversationID,
2984
3188
  source_message_id: intFromRawAllowZero(parsed.messageID, 0) || undefined,
2985
3189
  comment_kind: commentKind,
2986
3190
  request_status: "claimed",
@@ -3728,11 +3932,18 @@ function markRunnerRequestLifecycle({
3728
3932
  outcome,
3729
3933
  conversationIDRaw = "",
3730
3934
  allowedResponders = [],
3935
+ conversationParticipants = [],
3936
+ conversationInitialResponders = [],
3731
3937
  executionContractType = "",
3938
+ executionContractActionable = false,
3732
3939
  executionContractTargets = [],
3733
3940
  nextExpectedResponders = [],
3734
3941
  currentBotSelector = "",
3735
3942
  conversationIntentMode = "",
3943
+ conversationLeadBot = "",
3944
+ conversationSummaryBot = "",
3945
+ conversationAllowBotToBot = false,
3946
+ conversationReplyExpectation = "",
3736
3947
  normalizedIntent = "",
3737
3948
  closedReason = "",
3738
3949
  }) {
@@ -3777,6 +3988,16 @@ function markRunnerRequestLifecycle({
3777
3988
  const nowISO = new Date().toISOString();
3778
3989
  const patch = {
3779
3990
  conversation_id: conversationID,
3991
+ conversation_participants: uniqueOrderedStrings(
3992
+ ensureArray(conversationParticipants).length ? conversationParticipants : existing.conversation_participants,
3993
+ normalizeTelegramMentionUsername,
3994
+ ),
3995
+ conversation_initial_responders: uniqueOrderedStrings(
3996
+ ensureArray(conversationInitialResponders).length
3997
+ ? conversationInitialResponders
3998
+ : existing.conversation_initial_responders,
3999
+ normalizeTelegramMentionUsername,
4000
+ ),
3780
4001
  conversation_allowed_responders: uniqueOrderedStrings(
3781
4002
  ensureArray(allowedResponders).length ? allowedResponders : existing.conversation_allowed_responders,
3782
4003
  normalizeTelegramMentionUsername,
@@ -3786,11 +4007,30 @@ function markRunnerRequestLifecycle({
3786
4007
  || existing.conversation_intent_mode
3787
4008
  || "",
3788
4009
  ).trim().toLowerCase(),
4010
+ conversation_lead_bot: normalizeTelegramMentionUsername(
4011
+ conversationLeadBot
4012
+ || existing.conversation_lead_bot
4013
+ || "",
4014
+ ),
4015
+ conversation_summary_bot: normalizeTelegramMentionUsername(
4016
+ conversationSummaryBot
4017
+ || existing.conversation_summary_bot
4018
+ || "",
4019
+ ),
4020
+ conversation_allow_bot_to_bot: conversationAllowBotToBot === true
4021
+ || existing.conversation_allow_bot_to_bot === true,
4022
+ conversation_reply_expectation: String(
4023
+ conversationReplyExpectation
4024
+ || existing.conversation_reply_expectation
4025
+ || "",
4026
+ ).trim().toLowerCase(),
3789
4027
  execution_contract_type: String(
3790
4028
  executionContractType
3791
4029
  || existing.execution_contract_type
3792
4030
  || "",
3793
4031
  ).trim().toLowerCase(),
4032
+ execution_contract_actionable: executionContractActionable === true
4033
+ || existing.execution_contract_actionable === true,
3794
4034
  execution_contract_targets: uniqueOrderedStrings(
3795
4035
  ensureArray(executionContractTargets).length ? executionContractTargets : existing.execution_contract_targets,
3796
4036
  normalizeTelegramMentionUsername,
@@ -3999,13 +4239,36 @@ function mergeRunnerRequestForServerHydration(localEntryRaw, serverEntryRaw) {
3999
4239
  merged[fieldName] = localValue;
4000
4240
  }
4001
4241
  };
4242
+ const preserveLocalArrayWhenServerEmpty = (fieldName) => {
4243
+ const serverValues = ensureArray(serverEntry[fieldName]).filter(Boolean);
4244
+ const localValues = ensureArray(localEntry[fieldName]).filter(Boolean);
4245
+ if (serverValues.length === 0 && localValues.length > 0) {
4246
+ merged[fieldName] = localValues;
4247
+ }
4248
+ };
4002
4249
  preserveLocalStringWhenServerBlank("conversation_id");
4250
+ preserveLocalStringWhenServerBlank("conversation_intent_mode");
4251
+ preserveLocalStringWhenServerBlank("conversation_lead_bot");
4252
+ preserveLocalStringWhenServerBlank("conversation_summary_bot");
4253
+ preserveLocalStringWhenServerBlank("conversation_reply_expectation");
4254
+ preserveLocalStringWhenServerBlank("execution_contract_type");
4003
4255
  preserveLocalStringWhenServerBlank("root_work_item_id");
4004
4256
  preserveLocalStringWhenServerBlank("root_work_item_title");
4005
4257
  preserveLocalStringWhenServerBlank("root_work_item_status");
4006
4258
  preserveLocalStringWhenServerBlank("root_thread_id");
4007
4259
  preserveLocalStringWhenServerBlank("root_work_item_created_at");
4008
4260
  preserveLocalStringWhenServerBlank("root_work_item_last_error");
4261
+ preserveLocalArrayWhenServerEmpty("conversation_participants");
4262
+ preserveLocalArrayWhenServerEmpty("conversation_initial_responders");
4263
+ preserveLocalArrayWhenServerEmpty("conversation_allowed_responders");
4264
+ preserveLocalArrayWhenServerEmpty("execution_contract_targets");
4265
+ preserveLocalArrayWhenServerEmpty("next_expected_responders");
4266
+ if (serverEntry.conversation_allow_bot_to_bot !== true && localEntry.conversation_allow_bot_to_bot === true) {
4267
+ merged.conversation_allow_bot_to_bot = true;
4268
+ }
4269
+ if (serverEntry.execution_contract_actionable !== true && localEntry.execution_contract_actionable === true) {
4270
+ merged.execution_contract_actionable = true;
4271
+ }
4009
4272
  const localStatus = normalizeRunnerRequestStatus(localEntry.status);
4010
4273
  const serverStatus = normalizeRunnerRequestStatus(serverEntry.status);
4011
4274
  if (isFinalRunnerRequestStatus(localStatus) && !isFinalRunnerRequestStatus(serverStatus)) {
@@ -6319,6 +6582,7 @@ function stableTextModulo(rawValue, modulo = 1) {
6319
6582
  return hash % normalizedModulo;
6320
6583
  }
6321
6584
 
6585
+ /* Dead small_talk helper kept only as commented legacy code after lookup-only migration.
6322
6586
  function buildRunnerSmallTalkReply({ route, executionPlan } = {}) {
6323
6587
  const normalizedRoute = safeObject(route);
6324
6588
  const roleProfileName = normalizeRunnerRoleProfileName(
@@ -6355,6 +6619,7 @@ function buildRunnerSmallTalkReply({ route, executionPlan } = {}) {
6355
6619
  const templatePool = roleSpecificReplies[roleProfileName] || roleSpecificReplies.monitor;
6356
6620
  return templatePool[stableTextModulo(`${displayName}:${roleProfileName}`, templatePool.length)];
6357
6621
  }
6622
+ */
6358
6623
 
6359
6624
  function buildInformationalMiniExecutionOverride({ route, executionPlan } = {}) {
6360
6625
  const safeRoute = safeObject(route);
@@ -6363,8 +6628,8 @@ function buildInformationalMiniExecutionOverride({ route, executionPlan } = {})
6363
6628
  safeExecutionPlan.roleProfileName || safeRoute.roleProfile || safeRoute.role || "monitor",
6364
6629
  ) || "monitor";
6365
6630
  const lightweightModel = String(
6366
- resolveResponderAdjudicatorModelDisplayName({ client: "gpt", env: process.env }) || "gpt-5.3-codex-spark",
6367
- ).trim() || "gpt-5.3-codex-spark";
6631
+ resolveResponderAdjudicatorModelDisplayName({ client: "gpt", env: process.env }) || "gpt-5.4-mini",
6632
+ ).trim() || "gpt-5.4-mini";
6368
6633
  return {
6369
6634
  mode: "role_profile",
6370
6635
  role_profile_name: roleProfileName,
@@ -6616,11 +6881,11 @@ async function resolveInformationalQueryReply({
6616
6881
  user_message: messageText,
6617
6882
  bot_name: firstNonEmptyString([route?.botName, route?.serverBotName, route?.server_bot_name, route?.name]),
6618
6883
  bot_role: String(route?.role || route?.roleProfile || "").trim(),
6619
- proposed_summary: buildRunnerSmallTalkReply({ route, executionPlan }),
6620
6884
  },
6621
6885
  execution_override: executionOverride,
6622
6886
  };
6623
6887
  }
6888
+ /* Dead legacy direct small_talk branch kept commented for reference.
6624
6889
  if (false && normalizedIntentType === "small_talk") {
6625
6890
  return {
6626
6891
  handled: true,
@@ -6628,6 +6893,7 @@ async function resolveInformationalQueryReply({
6628
6893
  source: "small_talk",
6629
6894
  };
6630
6895
  }
6896
+ */
6631
6897
  if (normalizedIntentType === "workspace_query") {
6632
6898
  const workspace = resolveProjectWorkspaceBindingSummary(route?.projectID, executionPlan?.workspaceDir || route?.workspaceDir || "");
6633
6899
  const workspaceDir = String(workspace.workspace_dir || "").trim();
@@ -6641,7 +6907,6 @@ async function resolveInformationalQueryReply({
6641
6907
  source: "project.workspace",
6642
6908
  lookup: {
6643
6909
  ...workspace,
6644
- proposed_summary: reply,
6645
6910
  },
6646
6911
  execution_override: executionOverride,
6647
6912
  };
@@ -6660,7 +6925,6 @@ async function resolveInformationalQueryReply({
6660
6925
  source: "project.bot_roles",
6661
6926
  lookup: {
6662
6927
  ...payload,
6663
- proposed_summary: buildProjectBotRolesText(payload),
6664
6928
  },
6665
6929
  execution_override: executionOverride,
6666
6930
  };
@@ -6730,7 +6994,6 @@ async function resolveInformationalQueryReply({
6730
6994
  source: "project.file.locate",
6731
6995
  lookup: {
6732
6996
  ...payload,
6733
- proposed_summary: buildProjectFileLocateText(payload),
6734
6997
  },
6735
6998
  execution_override: executionOverride,
6736
6999
  };
@@ -7832,6 +8095,7 @@ async function processRunnerRouteOnce(route, runtime, mode, options = {}) {
7832
8095
  runtime,
7833
8096
  triggerDecision,
7834
8097
  responderAdjudication: inlineAdjudication,
8098
+ persistedHumanIntentRequest: claimedRequest,
7835
8099
  deps: {
7836
8100
  saveRunnerRouteState,
7837
8101
  startRunnerTypingHeartbeat,
@@ -7886,8 +8150,15 @@ async function processRunnerRouteOnce(route, runtime, mode, options = {}) {
7886
8150
  routeKey,
7887
8151
  outcome: String(processed.result?.outcome || "replied").trim().toLowerCase(),
7888
8152
  conversationIDRaw: String(processed.result?.conversation_id || "").trim(),
8153
+ conversationParticipants: ensureArray(processed.result?.conversation_participants),
8154
+ conversationInitialResponders: ensureArray(processed.result?.conversation_initial_responders),
7889
8155
  allowedResponders: ensureArray(processed.result?.conversation_allowed_responders),
8156
+ conversationLeadBot: String(processed.result?.conversation_lead_bot || "").trim(),
8157
+ conversationSummaryBot: String(processed.result?.conversation_summary_bot || "").trim(),
8158
+ conversationAllowBotToBot: processed.result?.conversation_allow_bot_to_bot === true,
8159
+ conversationReplyExpectation: String(processed.result?.conversation_reply_expectation || "").trim(),
7890
8160
  executionContractType: String(processed.result?.execution_contract_type || "").trim(),
8161
+ executionContractActionable: processed.result?.execution_contract_actionable === true,
7891
8162
  executionContractTargets: ensureArray(processed.result?.execution_contract_targets),
7892
8163
  nextExpectedResponders: ensureArray(processed.result?.next_expected_responders),
7893
8164
  currentBotSelector,
@@ -10193,6 +10464,7 @@ async function runRunnerStartResolvedRoutes(routes, flags, options = {}) {
10193
10464
  runtime: deferredExecution.runtime,
10194
10465
  triggerDecision: deferredExecution.triggerDecision,
10195
10466
  responderAdjudication: deferredExecution.responderAdjudication,
10467
+ persistedHumanIntentRequest: loadRunnerRequestByKey(deferredExecution.requestKey),
10196
10468
  deps: {
10197
10469
  saveRunnerRouteState,
10198
10470
  startRunnerTypingHeartbeat,
@@ -10278,8 +10550,15 @@ async function runRunnerStartResolvedRoutes(routes, flags, options = {}) {
10278
10550
  routeKey: deferredExecution.routeKey,
10279
10551
  outcome: String(processed.result?.outcome || "replied").trim().toLowerCase(),
10280
10552
  conversationIDRaw: String(processed.result?.conversation_id || "").trim(),
10553
+ conversationParticipants: ensureArray(processed.result?.conversation_participants),
10554
+ conversationInitialResponders: ensureArray(processed.result?.conversation_initial_responders),
10281
10555
  allowedResponders: ensureArray(processed.result?.conversation_allowed_responders),
10556
+ conversationLeadBot: String(processed.result?.conversation_lead_bot || "").trim(),
10557
+ conversationSummaryBot: String(processed.result?.conversation_summary_bot || "").trim(),
10558
+ conversationAllowBotToBot: processed.result?.conversation_allow_bot_to_bot === true,
10559
+ conversationReplyExpectation: String(processed.result?.conversation_reply_expectation || "").trim(),
10282
10560
  executionContractType: String(processed.result?.execution_contract_type || "").trim(),
10561
+ executionContractActionable: processed.result?.execution_contract_actionable === true,
10283
10562
  executionContractTargets: ensureArray(processed.result?.execution_contract_targets),
10284
10563
  nextExpectedResponders: ensureArray(processed.result?.next_expected_responders),
10285
10564
  currentBotSelector: normalizeTelegramMentionUsername(
@@ -14394,21 +14673,6 @@ async function runSelftest(flags = {}) {
14394
14673
  String(parsedEnv.SLACK_BOT_TOKEN || "") === "xoxb-test",
14395
14674
  `token=${String(parsedEnv.SLACK_BOT_TOKEN || "(missing)")}`,
14396
14675
  );
14397
- const monitorGreeting = buildRunnerSmallTalkReply({
14398
- route: { botName: "RyoAI_bot", roleProfile: "monitor" },
14399
- executionPlan: { roleProfileName: "monitor" },
14400
- });
14401
- const reviewGreeting = buildRunnerSmallTalkReply({
14402
- route: { botName: "RyoAI2_bot", roleProfile: "review" },
14403
- executionPlan: { roleProfileName: "review" },
14404
- });
14405
- push(
14406
- "small_talk_reply_varies_by_bot_and_role",
14407
- monitorGreeting.includes("RyoAI_bot")
14408
- && reviewGreeting.includes("RyoAI2_bot")
14409
- && monitorGreeting !== reviewGreeting,
14410
- `monitor=${monitorGreeting} review=${reviewGreeting}`,
14411
- );
14412
14676
  const informationalMiniReply = await resolveInformationalQueryReply({
14413
14677
  intentType: "small_talk",
14414
14678
  route: { botName: "RyoAI_bot", role: "monitor", roleProfile: "monitor" },
@@ -14441,6 +14705,7 @@ async function runSelftest(flags = {}) {
14441
14705
  safeObject(informationalMiniReply).handled === true
14442
14706
  && String(safeObject(informationalMiniReply).response_mode || "") === "lookup_only"
14443
14707
  && String(safeObject(informationalMiniReply).reply || "") === ""
14708
+ && !Object.prototype.hasOwnProperty.call(safeObject(safeObject(informationalMiniReply).lookup), "proposed_summary")
14444
14709
  && String(safeObject(safeObject(informationalMiniReply).execution_override).role_profile?.client || "") === "gpt"
14445
14710
  && String(safeObject(safeObject(informationalMiniReply).execution_override).role_profile?.model || "") === String(expectedInformationalModel || "")
14446
14711
  && String(safeObject(safeObject(informationalMiniReply).execution_override).role_profile?.permissionMode || "") === "read_only"
@@ -14564,28 +14829,28 @@ TELEGRAM_BOT_REVIEW_TOKEN=review-token
14564
14829
  summarizeProviderSupport("kakaotalk"),
14565
14830
  );
14566
14831
  push(
14567
- "intent_parser_default_model_uses_codex_spark_without_openai_api_key",
14568
- resolveIntentParserModelDisplayName({ client: "gpt", env: {} }) === "gpt-5.3-codex-spark",
14832
+ "intent_parser_default_model_uses_gpt_5_4_mini_without_openai_api_key",
14833
+ resolveIntentParserModelDisplayName({ client: "gpt", env: {} }) === "gpt-5.4-mini",
14569
14834
  resolveIntentParserModelDisplayName({ client: "gpt", env: {} }),
14570
14835
  );
14571
14836
  push(
14572
- "intent_parser_prefers_gpt_5_mini_when_openai_api_key_present",
14573
- resolveIntentParserModelDisplayName({ client: "gpt", env: { OPENAI_API_KEY: "sk-selftest" } }) === "gpt-5-mini",
14837
+ "intent_parser_defaults_to_gpt_5_4_mini_when_openai_api_key_present",
14838
+ resolveIntentParserModelDisplayName({ client: "gpt", env: { OPENAI_API_KEY: "sk-selftest" } }) === "gpt-5.4-mini",
14574
14839
  resolveIntentParserModelDisplayName({ client: "gpt", env: { OPENAI_API_KEY: "sk-selftest" } }),
14575
14840
  );
14576
14841
  push(
14577
- "responder_adjudicator_default_model_uses_codex_spark_without_openai_api_key",
14578
- resolveResponderAdjudicatorModelDisplayName({ client: "gpt", env: {} }) === "gpt-5.3-codex-spark",
14842
+ "responder_adjudicator_default_model_uses_gpt_5_4_mini_without_openai_api_key",
14843
+ resolveResponderAdjudicatorModelDisplayName({ client: "gpt", env: {} }) === "gpt-5.4-mini",
14579
14844
  resolveResponderAdjudicatorModelDisplayName({ client: "gpt", env: {} }),
14580
14845
  );
14581
14846
  push(
14582
- "responder_adjudicator_prefers_gpt_5_mini_when_openai_api_key_present",
14583
- resolveResponderAdjudicatorModelDisplayName({ client: "gpt", env: { OPENAI_API_KEY: "sk-selftest" } }) === "gpt-5-mini",
14847
+ "responder_adjudicator_defaults_to_gpt_5_4_mini_when_openai_api_key_present",
14848
+ resolveResponderAdjudicatorModelDisplayName({ client: "gpt", env: { OPENAI_API_KEY: "sk-selftest" } }) === "gpt-5.4-mini",
14584
14849
  resolveResponderAdjudicatorModelDisplayName({ client: "gpt", env: { OPENAI_API_KEY: "sk-selftest" } }),
14585
14850
  );
14586
14851
  push(
14587
- "role_planner_default_model_uses_gpt_5_mini",
14588
- resolveRolePlannerModelDisplayName({ client: "gpt", env: {} }) === "gpt-5-mini",
14852
+ "role_planner_default_model_uses_gpt_5_4_mini",
14853
+ resolveRolePlannerModelDisplayName({ client: "gpt", env: {} }) === "gpt-5.4-mini",
14589
14854
  resolveRolePlannerModelDisplayName({ client: "gpt", env: {} }),
14590
14855
  );
14591
14856
  push(
@@ -15945,7 +16210,7 @@ TELEGRAM_BOT_REVIEW_TOKEN=review-token
15945
16210
  client: "gpt",
15946
16211
  model: "gpt-5.4",
15947
16212
  planner_client: "gpt",
15948
- planner_model: "gpt-5-mini",
16213
+ planner_model: "gpt-5.4-mini",
15949
16214
  intent_type: "status_query",
15950
16215
  source_message_id: 128,
15951
16216
  next_run_at: Date.now() + 5000,
@@ -15975,7 +16240,7 @@ TELEGRAM_BOT_REVIEW_TOKEN=review-token
15975
16240
  && frame.includes("telegram-monitor-self...")
15976
16241
  && frame.includes("AI_RUNNING")
15977
16242
  && frame.includes("gpt/gpt-5.4")
15978
- && frame.includes("planner=gpt/gpt-5-mini")
16243
+ && frame.includes("planner=gpt/gpt-5.4-mini")
15979
16244
  && frame.includes("status_query")
15980
16245
  && frame.includes("warning=active execution is taking longer than expected"),
15981
16246
  frame.replace(/\r?\n/g, " | "),
@@ -25,6 +25,11 @@ const LOCAL_AI_MODEL_MAPPINGS = {
25
25
  execution: "gpt-5.4",
26
26
  aliases: ["gpt-5.4", "gpt 5.4", "GPT-5.4"],
27
27
  },
28
+ {
29
+ display: "gpt-5.4-mini",
30
+ execution: "gpt-5.4-mini",
31
+ aliases: ["gpt-5.4-mini", "gpt 5.4 mini", "GPT-5.4-mini", "gpt5.4-mini", "gpt54-mini"],
32
+ },
28
33
  {
29
34
  display: "gpt-5-mini",
30
35
  execution: "gpt-5-mini",
@@ -1068,12 +1073,11 @@ function truthyEnvFlag(value) {
1068
1073
  function defaultAdjudicationModelForClient(clientName, env = process.env) {
1069
1074
  const normalizedClient = normalizeLocalAIClientName(clientName, "");
1070
1075
  if (normalizedClient === "gpt") {
1071
- const hasOpenAIAPIKey = String(env?.OPENAI_API_KEY || "").trim().length > 0;
1072
1076
  const prefersMini = truthyEnvFlag(env?.METHEUS_PREFER_GPT_5_MINI_ADJUDICATOR);
1073
- if (hasOpenAIAPIKey || prefersMini) {
1077
+ if (prefersMini) {
1074
1078
  return "gpt-5-mini";
1075
1079
  }
1076
- return "gpt-5.3-codex-spark";
1080
+ return "gpt-5.4-mini";
1077
1081
  }
1078
1082
  return suggestLocalAIModelDisplayName(normalizedClient, "");
1079
1083
  }
@@ -1081,7 +1085,7 @@ function defaultAdjudicationModelForClient(clientName, env = process.env) {
1081
1085
  function defaultRolePlannerModelForClient(clientName) {
1082
1086
  const normalizedClient = normalizeLocalAIClientName(clientName, "");
1083
1087
  if (normalizedClient === "gpt") {
1084
- return "gpt-5-mini";
1088
+ return "gpt-5.4-mini";
1085
1089
  }
1086
1090
  return suggestLocalAIModelDisplayName(normalizedClient, "");
1087
1091
  }
@@ -1786,9 +1790,6 @@ export function buildLocalBotPrompt(payload, { terse = true } = {}) {
1786
1790
  if (queryLookupResponseMode) {
1787
1791
  lines.push(`- Response mode: ${queryLookupResponseMode}`);
1788
1792
  }
1789
- if (String(queryLookup.proposed_reply || "").trim()) {
1790
- lines.push(`- Proposed factual reply: ${String(queryLookup.proposed_reply || "").trim()}`);
1791
- }
1792
1793
  if (queryLookupFacts != null) {
1793
1794
  lines.push("Structured lookup facts:");
1794
1795
  lines.push(JSON.stringify(queryLookupFacts, null, 2));
@@ -1994,7 +1995,7 @@ export function buildLocalBotPrompt(payload, { terse = true } = {}) {
1994
1995
  String(conversation.intent_mode || "").trim() === "delegated_single_lead"
1995
1996
  ? (selfIsLeadBot
1996
1997
  ? "This bot is the lead bot for a delegated public collaboration. It may assign work publicly only to allowed responders, and it may not expand the participant set."
1997
- : "This bot is not the lead bot. Respond only when the lead bot or the human explicitly addresses this bot, and do not start peer-to-peer chains.")
1998
+ : "This bot is not the lead bot. Prefer replying when the human, the lead bot, or another managed bot explicitly addresses this bot in the live room context. Do not expand the participant set beyond the allowed responders.")
1998
1999
  : "Use the human conversation contract as the source of truth for who may reply.",
1999
2000
  conversation.allow_bot_to_bot === true
2000
2001
  ? "If another bot explicitly mentioned you and you are in the allowed responders list, you may answer that bot publicly in the room."
@@ -2663,7 +2664,7 @@ export function adjudicateRunnerStartupLoopWithAI({
2663
2664
  || env?.METHEUS_STARTUP_LOOP_ADJUDICATOR_MODEL
2664
2665
  || env?.METHEUS_RESPONDER_ADJUDICATOR_MODEL
2665
2666
  || env?.METHEUS_INTENT_PARSER_MODEL
2666
- || (adjudicatorClient === "gpt" ? "gpt-5.3-codex-spark" : suggestLocalAIModelDisplayName(adjudicatorClient, ""))
2667
+ || (adjudicatorClient === "gpt" ? "gpt-5.4-mini" : suggestLocalAIModelDisplayName(adjudicatorClient, ""))
2667
2668
  || "",
2668
2669
  ).trim();
2669
2670
  const rawText = runLocalAIPromptRawText({