metheus-governance-mcp-cli 0.2.202 → 0.2.204

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)) {
@@ -6365,8 +6628,8 @@ function buildInformationalMiniExecutionOverride({ route, executionPlan } = {})
6365
6628
  safeExecutionPlan.roleProfileName || safeRoute.roleProfile || safeRoute.role || "monitor",
6366
6629
  ) || "monitor";
6367
6630
  const lightweightModel = String(
6368
- resolveResponderAdjudicatorModelDisplayName({ client: "gpt", env: process.env }) || "gpt-5.3-codex-spark",
6369
- ).trim() || "gpt-5.3-codex-spark";
6631
+ resolveResponderAdjudicatorModelDisplayName({ client: "gpt", env: process.env }) || "gpt-5.4-mini",
6632
+ ).trim() || "gpt-5.4-mini";
6370
6633
  return {
6371
6634
  mode: "role_profile",
6372
6635
  role_profile_name: roleProfileName,
@@ -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(
@@ -14550,28 +14829,28 @@ TELEGRAM_BOT_REVIEW_TOKEN=review-token
14550
14829
  summarizeProviderSupport("kakaotalk"),
14551
14830
  );
14552
14831
  push(
14553
- "intent_parser_default_model_uses_codex_spark_without_openai_api_key",
14554
- 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",
14555
14834
  resolveIntentParserModelDisplayName({ client: "gpt", env: {} }),
14556
14835
  );
14557
14836
  push(
14558
- "intent_parser_prefers_gpt_5_mini_when_openai_api_key_present",
14559
- 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",
14560
14839
  resolveIntentParserModelDisplayName({ client: "gpt", env: { OPENAI_API_KEY: "sk-selftest" } }),
14561
14840
  );
14562
14841
  push(
14563
- "responder_adjudicator_default_model_uses_codex_spark_without_openai_api_key",
14564
- 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",
14565
14844
  resolveResponderAdjudicatorModelDisplayName({ client: "gpt", env: {} }),
14566
14845
  );
14567
14846
  push(
14568
- "responder_adjudicator_prefers_gpt_5_mini_when_openai_api_key_present",
14569
- 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",
14570
14849
  resolveResponderAdjudicatorModelDisplayName({ client: "gpt", env: { OPENAI_API_KEY: "sk-selftest" } }),
14571
14850
  );
14572
14851
  push(
14573
- "role_planner_default_model_uses_gpt_5_mini",
14574
- 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",
14575
14854
  resolveRolePlannerModelDisplayName({ client: "gpt", env: {} }),
14576
14855
  );
14577
14856
  push(
@@ -15931,7 +16210,7 @@ TELEGRAM_BOT_REVIEW_TOKEN=review-token
15931
16210
  client: "gpt",
15932
16211
  model: "gpt-5.4",
15933
16212
  planner_client: "gpt",
15934
- planner_model: "gpt-5-mini",
16213
+ planner_model: "gpt-5.4-mini",
15935
16214
  intent_type: "status_query",
15936
16215
  source_message_id: 128,
15937
16216
  next_run_at: Date.now() + 5000,
@@ -15961,7 +16240,7 @@ TELEGRAM_BOT_REVIEW_TOKEN=review-token
15961
16240
  && frame.includes("telegram-monitor-self...")
15962
16241
  && frame.includes("AI_RUNNING")
15963
16242
  && frame.includes("gpt/gpt-5.4")
15964
- && frame.includes("planner=gpt/gpt-5-mini")
16243
+ && frame.includes("planner=gpt/gpt-5.4-mini")
15965
16244
  && frame.includes("status_query")
15966
16245
  && frame.includes("warning=active execution is taking longer than expected"),
15967
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
  }
@@ -2660,7 +2664,7 @@ export function adjudicateRunnerStartupLoopWithAI({
2660
2664
  || env?.METHEUS_STARTUP_LOOP_ADJUDICATOR_MODEL
2661
2665
  || env?.METHEUS_RESPONDER_ADJUDICATOR_MODEL
2662
2666
  || env?.METHEUS_INTENT_PARSER_MODEL
2663
- || (adjudicatorClient === "gpt" ? "gpt-5.3-codex-spark" : suggestLocalAIModelDisplayName(adjudicatorClient, ""))
2667
+ || (adjudicatorClient === "gpt" ? "gpt-5.4-mini" : suggestLocalAIModelDisplayName(adjudicatorClient, ""))
2664
2668
  || "",
2665
2669
  ).trim();
2666
2670
  const rawText = runLocalAIPromptRawText({
@@ -339,8 +339,19 @@ export async function listProjectRunnerRequests(
339
339
  root_comment_kind: String(row.root_comment_kind || row.rootCommentKind || row.RootCommentKind || "").trim(),
340
340
  conversation_id: String(row.conversation_id || row.conversationID || row.ConversationID || "").trim(),
341
341
  selected_bot_usernames: ensureArray(row.selected_bot_usernames || row.selectedBotUsernames || row.SelectedBotUsernames).map((value) => String(value || "").trim()).filter(Boolean),
342
+ conversation_intent_mode: String(row.conversation_intent_mode || row.conversationIntentMode || row.ConversationIntentMode || "").trim(),
343
+ conversation_lead_bot: String(row.conversation_lead_bot || row.conversationLeadBot || row.ConversationLeadBot || "").trim(),
344
+ conversation_summary_bot: String(row.conversation_summary_bot || row.conversationSummaryBot || row.ConversationSummaryBot || "").trim(),
345
+ conversation_participants: ensureArray(row.conversation_participants || row.conversationParticipants || row.ConversationParticipants).map((value) => String(value || "").trim()).filter(Boolean),
346
+ conversation_initial_responders: ensureArray(row.conversation_initial_responders || row.conversationInitialResponders || row.ConversationInitialResponders).map((value) => String(value || "").trim()).filter(Boolean),
342
347
  conversation_allowed_responders: ensureArray(row.conversation_allowed_responders || row.conversationAllowedResponders || row.ConversationAllowedResponders).map((value) => String(value || "").trim()).filter(Boolean),
348
+ conversation_allow_bot_to_bot: row.conversation_allow_bot_to_bot === true || row.conversationAllowBotToBot === true || row.ConversationAllowBotToBot === true,
349
+ conversation_reply_expectation: String(row.conversation_reply_expectation || row.conversationReplyExpectation || row.ConversationReplyExpectation || "").trim(),
343
350
  normalized_intent: String(row.normalized_intent || row.normalizedIntent || row.NormalizedIntent || "").trim(),
351
+ execution_contract_type: String(row.execution_contract_type || row.executionContractType || row.ExecutionContractType || "").trim(),
352
+ execution_contract_actionable: row.execution_contract_actionable === true || row.executionContractActionable === true || row.ExecutionContractActionable === true,
353
+ execution_contract_targets: ensureArray(row.execution_contract_targets || row.executionContractTargets || row.ExecutionContractTargets).map((value) => String(value || "").trim()).filter(Boolean),
354
+ next_expected_responders: ensureArray(row.next_expected_responders || row.nextExpectedResponders || row.NextExpectedResponders).map((value) => String(value || "").trim()).filter(Boolean),
344
355
  status: String(row.status || row.Status || "").trim(),
345
356
  claimed_by_route: String(row.claimed_by_route || row.claimedByRoute || row.ClaimedByRoute || "").trim(),
346
357
  claimed_at: String(row.claimed_at || row.claimedAt || row.ClaimedAt || "").trim(),
@@ -390,8 +401,19 @@ export async function upsertProjectRunnerRequest(
390
401
  root_comment_kind: String(raw.root_comment_kind || "").trim(),
391
402
  conversation_id: String(raw.conversation_id || "").trim(),
392
403
  selected_bot_usernames: ensureArray(raw.selected_bot_usernames).map((value) => String(value || "").trim()).filter(Boolean),
404
+ conversation_intent_mode: String(raw.conversation_intent_mode || "").trim(),
405
+ conversation_lead_bot: String(raw.conversation_lead_bot || "").trim(),
406
+ conversation_summary_bot: String(raw.conversation_summary_bot || "").trim(),
407
+ conversation_participants: ensureArray(raw.conversation_participants).map((value) => String(value || "").trim()).filter(Boolean),
408
+ conversation_initial_responders: ensureArray(raw.conversation_initial_responders).map((value) => String(value || "").trim()).filter(Boolean),
393
409
  conversation_allowed_responders: ensureArray(raw.conversation_allowed_responders).map((value) => String(value || "").trim()).filter(Boolean),
410
+ conversation_allow_bot_to_bot: raw.conversation_allow_bot_to_bot === true,
411
+ conversation_reply_expectation: String(raw.conversation_reply_expectation || "").trim(),
394
412
  normalized_intent: String(raw.normalized_intent || "").trim(),
413
+ execution_contract_type: String(raw.execution_contract_type || "").trim(),
414
+ execution_contract_actionable: raw.execution_contract_actionable === true,
415
+ execution_contract_targets: ensureArray(raw.execution_contract_targets).map((value) => String(value || "").trim()).filter(Boolean),
416
+ next_expected_responders: ensureArray(raw.next_expected_responders).map((value) => String(value || "").trim()).filter(Boolean),
395
417
  status: String(raw.status || "").trim(),
396
418
  claimed_by_route: String(raw.claimed_by_route || "").trim(),
397
419
  claimed_at: String(raw.claimed_at || "").trim() || undefined,