metheus-governance-mcp-cli 0.2.205 → 0.2.206

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
@@ -2065,14 +2065,176 @@ function saveBotRunnerState(nextState) {
2065
2065
  try {
2066
2066
  current = safeObject(tryJsonParse(fs.readFileSync(filePath, "utf8")));
2067
2067
  } catch {}
2068
+ const stateEntryTimestampMs = (...values) => {
2069
+ for (const value of values) {
2070
+ const ms = Date.parse(String(value || "").trim());
2071
+ if (Number.isFinite(ms)) {
2072
+ return ms;
2073
+ }
2074
+ }
2075
+ return 0;
2076
+ };
2077
+ const mergeRunnerStateRoutes = (currentRoutesRaw, nextRoutesRaw) => {
2078
+ const currentRoutes = safeObject(currentRoutesRaw);
2079
+ const nextRoutes = safeObject(nextRoutesRaw);
2080
+ const merged = {
2081
+ ...currentRoutes,
2082
+ };
2083
+ const mergeConversationSessions = (currentSessionsRaw, nextSessionsRaw) => {
2084
+ const currentSessions = safeObject(currentSessionsRaw);
2085
+ const nextSessions = safeObject(nextSessionsRaw);
2086
+ const mergedSessions = {
2087
+ ...currentSessions,
2088
+ };
2089
+ for (const [conversationID, nextSessionRaw] of Object.entries(nextSessions)) {
2090
+ const currentSession = safeObject(currentSessions[conversationID]);
2091
+ const nextSession = safeObject(nextSessionRaw);
2092
+ if (!Object.keys(currentSession).length) {
2093
+ mergedSessions[conversationID] = nextSession;
2094
+ continue;
2095
+ }
2096
+ const currentMs = stateEntryTimestampMs(
2097
+ currentSession.updated_at,
2098
+ currentSession.last_activity_at,
2099
+ currentSession.closed_at,
2100
+ currentSession.started_at,
2101
+ currentSession.expires_at,
2102
+ );
2103
+ const nextMs = stateEntryTimestampMs(
2104
+ nextSession.updated_at,
2105
+ nextSession.last_activity_at,
2106
+ nextSession.closed_at,
2107
+ nextSession.started_at,
2108
+ nextSession.expires_at,
2109
+ );
2110
+ mergedSessions[conversationID] = nextMs >= currentMs
2111
+ ? {
2112
+ ...currentSession,
2113
+ ...nextSession,
2114
+ }
2115
+ : {
2116
+ ...nextSession,
2117
+ ...currentSession,
2118
+ };
2119
+ }
2120
+ return mergedSessions;
2121
+ };
2122
+ for (const [routeKey, nextRouteRaw] of Object.entries(nextRoutes)) {
2123
+ const currentRoute = safeObject(currentRoutes[routeKey]);
2124
+ const nextRoute = safeObject(nextRouteRaw);
2125
+ if (!Object.keys(currentRoute).length) {
2126
+ merged[routeKey] = nextRoute;
2127
+ continue;
2128
+ }
2129
+ const preferredRoute = prefersRunnerStateRecord(nextRoute, currentRoute) ? nextRoute : currentRoute;
2130
+ const fallbackRoute = preferredRoute === nextRoute ? currentRoute : nextRoute;
2131
+ merged[routeKey] = cleanupRunnerStateRecord({
2132
+ ...mergeRunnerStateRecords(preferredRoute, fallbackRoute),
2133
+ conversation_sessions: mergeConversationSessions(
2134
+ currentRoute.conversation_sessions,
2135
+ nextRoute.conversation_sessions,
2136
+ ),
2137
+ });
2138
+ }
2139
+ return merged;
2140
+ };
2141
+ const mergeRunnerStateRequests = (currentRequestsRaw, nextRequestsRaw) => {
2142
+ const currentRequests = normalizeBotRunnerRequests(currentRequestsRaw);
2143
+ const nextRequests = normalizeBotRunnerRequests(nextRequestsRaw);
2144
+ const merged = {
2145
+ ...currentRequests,
2146
+ };
2147
+ for (const [requestKey, nextRequestRaw] of Object.entries(nextRequests)) {
2148
+ const currentRequest = safeObject(currentRequests[requestKey]);
2149
+ const nextRequest = safeObject(nextRequestRaw);
2150
+ const currentMs = stateEntryTimestampMs(
2151
+ currentRequest.updated_at,
2152
+ currentRequest.completed_at,
2153
+ currentRequest.closed_at,
2154
+ currentRequest.claimed_at,
2155
+ );
2156
+ const nextMs = stateEntryTimestampMs(
2157
+ nextRequest.updated_at,
2158
+ nextRequest.completed_at,
2159
+ nextRequest.closed_at,
2160
+ nextRequest.claimed_at,
2161
+ );
2162
+ if (!Object.keys(currentRequest).length) {
2163
+ merged[requestKey] = nextRequest;
2164
+ continue;
2165
+ }
2166
+ merged[requestKey] = nextMs >= currentMs
2167
+ ? {
2168
+ ...currentRequest,
2169
+ ...nextRequest,
2170
+ }
2171
+ : currentRequest;
2172
+ }
2173
+ return normalizeBotRunnerRequests(merged);
2174
+ };
2175
+ const mergeRunnerStateExcludedComments = (currentExcludedRaw, nextExcludedRaw) => {
2176
+ const currentExcluded = normalizeBotRunnerExcludedComments(currentExcludedRaw);
2177
+ const nextExcluded = normalizeBotRunnerExcludedComments(nextExcludedRaw);
2178
+ const merged = {
2179
+ ...currentExcluded,
2180
+ };
2181
+ for (const [commentID, nextEntryRaw] of Object.entries(nextExcluded)) {
2182
+ const currentEntry = safeObject(currentExcluded[commentID]);
2183
+ const nextEntry = safeObject(nextEntryRaw);
2184
+ const currentMs = stateEntryTimestampMs(currentEntry.excluded_at);
2185
+ const nextMs = stateEntryTimestampMs(nextEntry.excluded_at);
2186
+ if (!Object.keys(currentEntry).length || nextMs >= currentMs) {
2187
+ merged[commentID] = {
2188
+ ...currentEntry,
2189
+ ...nextEntry,
2190
+ };
2191
+ }
2192
+ }
2193
+ return normalizeBotRunnerExcludedComments(merged);
2194
+ };
2195
+ const mergeRunnerStateConsumedComments = (currentConsumedRaw, nextConsumedRaw) => {
2196
+ const currentConsumed = normalizeBotRunnerConsumedComments(currentConsumedRaw);
2197
+ const nextConsumed = normalizeBotRunnerConsumedComments(nextConsumedRaw);
2198
+ const merged = {
2199
+ ...currentConsumed,
2200
+ };
2201
+ for (const [commentID, nextEntryRaw] of Object.entries(nextConsumed)) {
2202
+ const currentEntry = safeObject(currentConsumed[commentID]);
2203
+ const nextEntry = safeObject(nextEntryRaw);
2204
+ const currentMs = stateEntryTimestampMs(currentEntry.consumed_at);
2205
+ const nextMs = stateEntryTimestampMs(nextEntry.consumed_at);
2206
+ if (!Object.keys(currentEntry).length || nextMs >= currentMs) {
2207
+ merged[commentID] = {
2208
+ ...currentEntry,
2209
+ ...nextEntry,
2210
+ };
2211
+ }
2212
+ }
2213
+ return normalizeBotRunnerConsumedComments(merged);
2214
+ };
2068
2215
  const payload = {
2069
2216
  version: 1,
2070
2217
  updated_at: new Date().toISOString(),
2071
- routes: safeObject(nextState?.routes ?? current.routes),
2072
- shared_inboxes: safeObject(nextState?.sharedInboxes ?? nextState?.shared_inboxes ?? current.shared_inboxes ?? current.sharedInboxes),
2073
- excluded_comments: normalizeBotRunnerExcludedComments(nextState?.excludedComments ?? nextState?.excluded_comments ?? current.excluded_comments ?? current.excludedComments),
2074
- requests: normalizeBotRunnerRequests(nextState?.requests ?? current.requests),
2075
- consumed_comments: normalizeBotRunnerConsumedComments(nextState?.consumedComments ?? nextState?.consumed_comments ?? current.consumed_comments ?? current.consumedComments),
2218
+ routes: mergeRunnerStateRoutes(
2219
+ current.routes,
2220
+ nextState?.routes ?? current.routes,
2221
+ ),
2222
+ shared_inboxes: {
2223
+ ...safeObject(current.shared_inboxes ?? current.sharedInboxes),
2224
+ ...safeObject(nextState?.sharedInboxes ?? nextState?.shared_inboxes),
2225
+ },
2226
+ excluded_comments: mergeRunnerStateExcludedComments(
2227
+ current.excluded_comments ?? current.excludedComments,
2228
+ nextState?.excludedComments ?? nextState?.excluded_comments ?? current.excluded_comments ?? current.excludedComments,
2229
+ ),
2230
+ requests: mergeRunnerStateRequests(
2231
+ current.requests,
2232
+ nextState?.requests ?? current.requests,
2233
+ ),
2234
+ consumed_comments: mergeRunnerStateConsumedComments(
2235
+ current.consumed_comments ?? current.consumedComments,
2236
+ nextState?.consumedComments ?? nextState?.consumed_comments ?? current.consumed_comments ?? current.consumedComments,
2237
+ ),
2076
2238
  };
2077
2239
  fs.mkdirSync(path.dirname(filePath), { recursive: true });
2078
2240
  fs.writeFileSync(filePath, `${JSON.stringify(payload, null, 2)}\n`, "utf8");
@@ -2507,6 +2669,89 @@ function findRunnerRequestsForScope(state, normalizedRoute, selectors = {}) {
2507
2669
  });
2508
2670
  }
2509
2671
 
2672
+ function findScopedConversationSessionState(state, normalizedRoute, conversationIDRaw = "") {
2673
+ const conversationID = String(conversationIDRaw || "").trim();
2674
+ if (!conversationID) {
2675
+ return {};
2676
+ }
2677
+ const config = loadBotRunnerConfig({ persistIfNeeded: true });
2678
+ const nextRoutes = safeObject(state?.routes);
2679
+ const candidates = [];
2680
+ const seenRouteKeys = new Set();
2681
+ const pushCandidateRoute = (routeRaw) => {
2682
+ const routeObject = safeObject(routeRaw);
2683
+ if (!Object.keys(routeObject).length) {
2684
+ return;
2685
+ }
2686
+ const candidateRoute = normalizeRunnerRoute(routeObject);
2687
+ const candidateRouteKey = runnerRouteKey(candidateRoute);
2688
+ if (seenRouteKeys.has(candidateRouteKey)) {
2689
+ return;
2690
+ }
2691
+ seenRouteKeys.add(candidateRouteKey);
2692
+ candidates.push({
2693
+ route: candidateRoute,
2694
+ routeKey: candidateRouteKey,
2695
+ routeState: safeObject(nextRoutes[candidateRouteKey]),
2696
+ });
2697
+ };
2698
+ pushCandidateRoute(normalizedRoute);
2699
+ for (const candidateRouteRaw of ensureArray(config.routes)) {
2700
+ if (!runnerRouteMatchesProjectConversationScope(candidateRouteRaw, normalizedRoute)) {
2701
+ continue;
2702
+ }
2703
+ pushCandidateRoute(candidateRouteRaw);
2704
+ }
2705
+ const ranked = candidates
2706
+ .map((candidate) => {
2707
+ const session = safeObject(safeObject(candidate.routeState.conversation_sessions)[conversationID]);
2708
+ if (!Object.keys(session).length) {
2709
+ return null;
2710
+ }
2711
+ const status = String(session.status || "").trim().toLowerCase();
2712
+ const lastActivity = firstNonEmptyString([session.last_activity_at, session.closed_at, session.started_at]);
2713
+ return {
2714
+ ...candidate,
2715
+ session,
2716
+ status,
2717
+ lastActivity,
2718
+ };
2719
+ })
2720
+ .filter(Boolean)
2721
+ .sort((left, right) => {
2722
+ if (left.status !== right.status) {
2723
+ if (left.status === "open") return -1;
2724
+ if (right.status === "open") return 1;
2725
+ }
2726
+ if (left.lastActivity && right.lastActivity && left.lastActivity !== right.lastActivity) {
2727
+ return left.lastActivity < right.lastActivity ? 1 : -1;
2728
+ }
2729
+ return String(left.routeKey || "").localeCompare(String(right.routeKey || ""));
2730
+ });
2731
+ return safeObject(ranked[0]);
2732
+ }
2733
+
2734
+ function sessionAllowsConversationResponder(sessionRaw, responderSelectorRaw = "") {
2735
+ const session = safeObject(sessionRaw);
2736
+ const responderSelector = normalizeTelegramMentionUsername(responderSelectorRaw);
2737
+ if (!responderSelector) {
2738
+ return false;
2739
+ }
2740
+ if (String(session.status || "").trim().toLowerCase() !== "open") {
2741
+ return false;
2742
+ }
2743
+ const nextExpectedResponders = ensureArray(session.next_expected_responders)
2744
+ .map((value) => normalizeTelegramMentionUsername(value))
2745
+ .filter(Boolean);
2746
+ if (nextExpectedResponders.length > 0) {
2747
+ return nextExpectedResponders.includes(responderSelector);
2748
+ }
2749
+ const allowedResponders = ensureArray(session.allowed_responders)
2750
+ .map((value) => normalizeTelegramMentionUsername(value))
2751
+ .filter(Boolean);
2752
+ return allowedResponders.includes(responderSelector);
2753
+ }
2754
+
2510
2755
  function findRunnerRequestsForMessageID(state, normalizedRoute, selectors = {}) {
2511
2756
  const chatID = String(selectors.chatID || "").trim();
2512
2757
  const messageID = intFromRawAllowZero(selectors.messageID, 0);
@@ -3895,11 +4140,80 @@ function resolveRunnerContinuationRequestForBotReply({
3895
4140
  };
3896
4141
  }
3897
4142
  const currentState = loadBotRunnerState();
3898
- const requests = findRunnerRequestsForScope(currentState, normalizedRoute, {
4143
+ let requests = findRunnerRequestsForScope(currentState, normalizedRoute, {
3899
4144
  conversationID,
3900
4145
  chatID: String(parsed.chatID || parsed.chatId || "").trim(),
3901
4146
  }).filter((entry) => isActiveRunnerRequestStatus(entry.status));
3902
- const request = safeObject(requests[0]);
4147
+ let request = safeObject(requests[0]);
4148
+ if (!Object.keys(request).length) {
4149
+ const sessionMatch = findScopedConversationSessionState(currentState, normalizedRoute, conversationID);
4150
+ const session = safeObject(sessionMatch.session);
4151
+ const fallbackRequestKey = String(
4152
+ safeObject(sessionMatch.routeState).active_request_key
4153
+ || safeObject(sessionMatch.routeState).last_request_key
4154
+ || "",
4155
+ ).trim();
4156
+ if (
4157
+ fallbackRequestKey
4158
+ && String(session.status || "").trim().toLowerCase() === "open"
4159
+ ) {
4160
+ const fallbackRequest = safeObject(normalizeBotRunnerRequests(currentState.requests)[fallbackRequestKey]);
4161
+ const nowISO = new Date().toISOString();
4162
+ const seedRequest = Object.keys(fallbackRequest).length
4163
+ ? fallbackRequest
4164
+ : {
4165
+ request_key: fallbackRequestKey,
4166
+ project_id: String(normalizedRoute?.projectID || "").trim(),
4167
+ provider: String(normalizedRoute?.provider || "").trim(),
4168
+ chat_id: String(parsed.chatID || parsed.chatId || "").trim(),
4169
+ conversation_id: conversationID,
4170
+ selected_bot_usernames: ensureArray(session.participants),
4171
+ conversation_allowed_responders: ensureArray(session.allowed_responders),
4172
+ conversation_intent_mode: String(session.intent_mode || "").trim().toLowerCase(),
4173
+ conversation_lead_bot: normalizeTelegramMentionUsername(session.lead_bot_username),
4174
+ conversation_summary_bot: normalizeTelegramMentionUsername(session.summary_bot_username),
4175
+ conversation_participants: ensureArray(session.participants),
4176
+ conversation_initial_responders: ensureArray(session.initial_responders),
4177
+ conversation_allow_bot_to_bot: session.allow_bot_to_bot === true,
4178
+ conversation_reply_expectation: "",
4179
+ execution_contract_type: String(session.last_execution_contract_type || "").trim().toLowerCase(),
4180
+ execution_contract_actionable: session.last_execution_contract_actionable === true,
4181
+ execution_contract_targets: ensureArray(session.last_execution_contract_targets),
4182
+ next_expected_responders: ensureArray(session.next_expected_responders),
4183
+ normalized_intent: String(safeObject(sessionMatch.routeState).last_intent_type || "").trim().toLowerCase(),
4184
+ status: "running",
4185
+ claimed_by_route: String(sessionMatch.routeKey || "").trim(),
4186
+ claimed_at: firstNonEmptyString([session.started_at, nowISO]),
4187
+ started_at: firstNonEmptyString([session.started_at, nowISO]),
4188
+ root_work_item_id: String(
4189
+ safeObject(sessionMatch.routeState).active_root_work_item_id
4190
+ || safeObject(sessionMatch.routeState).last_root_work_item_id
4191
+ || "",
4192
+ ).trim(),
4193
+ root_work_item_title: String(
4194
+ safeObject(sessionMatch.routeState).active_root_work_item_title
4195
+ || safeObject(sessionMatch.routeState).last_root_work_item_title
4196
+ || "",
4197
+ ).trim(),
4198
+ root_work_item_status: String(
4199
+ safeObject(sessionMatch.routeState).active_root_work_item_status
4200
+ || safeObject(sessionMatch.routeState).last_root_work_item_status
4201
+ || "",
4202
+ ).trim().toLowerCase(),
4203
+ };
4204
+ const seededRequest = upsertRunnerRequest(currentState, fallbackRequestKey, seedRequest);
4205
+ currentState.requests = seededRequest.requests;
4206
+ request = safeObject(seededRequest.request);
4207
+ saveBotRunnerState({
4208
+ routes: currentState.routes,
4209
+ sharedInboxes: currentState.sharedInboxes || currentState.shared_inboxes,
4210
+ excludedComments: currentState.excludedComments || currentState.excluded_comments,
4211
+ requests: currentState.requests,
4212
+ consumedComments: currentState.consumedComments || currentState.consumed_comments,
4213
+ });
4214
+ requests = [request];
4215
+ }
4216
+ }
3903
4217
  if (!Object.keys(request).length) {
3904
4218
  return {
3905
4219
  ok: false,
@@ -4149,7 +4463,10 @@ function cleanupBotRunnerRequestState({
4149
4463
  && String(entry.conversation_id || "").trim() === String(conversationID || "").trim()
4150
4464
  && isActiveRunnerRequestStatus(entry.status)
4151
4465
  ));
4152
- if (!expired && activeRequests.length > 0) {
4466
+ const pendingContinuationResponders = ensureArray(session.next_expected_responders)
4467
+ .map((value) => normalizeTelegramMentionUsername(value))
4468
+ .filter(Boolean);
4469
+ if (!expired && (activeRequests.length > 0 || pendingContinuationResponders.length > 0)) {
4153
4470
  continue;
4154
4471
  }
4155
4472
  const closedReason = expired ? "expired_session" : "orphaned_open_session";
@@ -7487,7 +7804,11 @@ async function processRunnerRouteOnce(route, runtime, mode, options = {}) {
7487
7804
  && String(entry.conversation_id || "").trim() === conversationID
7488
7805
  && isActiveRunnerRequestStatus(entry.status)
7489
7806
  ));
7490
- return activeRequests.length > 0;
7807
+ if (activeRequests.length > 0) {
7808
+ return true;
7809
+ }
7810
+ const sessionMatch = findScopedConversationSessionState(latestRunnerState, normalizedRoute, conversationID);
7811
+ return sessionAllowsConversationResponder(sessionMatch.session, currentBotSelector);
7491
7812
  }
7492
7813
  return true;
7493
7814
  });
@@ -2229,6 +2229,134 @@ export async function runSelftestRunnerScenarios(push, deps) {
2229
2229
  }
2230
2230
  }
2231
2231
 
2232
+ const originalStateMergeHome = process.env.HOME;
2233
+ const originalStateMergeUserProfile = process.env.USERPROFILE;
2234
+ let runnerStateMergeTempRoot = "";
2235
+ try {
2236
+ runnerStateMergeTempRoot = fs.mkdtempSync(path.join(os.tmpdir(), "metheus-runner-state-merge-selftest-"));
2237
+ const runnerStateMergeHome = path.join(runnerStateMergeTempRoot, "home");
2238
+ fs.mkdirSync(path.join(runnerStateMergeHome, ".metheus"), { recursive: true });
2239
+ process.env.HOME = runnerStateMergeHome;
2240
+ process.env.USERPROFILE = runnerStateMergeHome;
2241
+ const mergeRoute = normalizeRunnerRoute({
2242
+ name: "telegram-monitor-state-merge",
2243
+ project_id: selftestProjectID,
2244
+ provider: "telegram",
2245
+ role: "monitor",
2246
+ destination_label: "Main Room",
2247
+ });
2248
+ const mergeRouteKey = runnerRouteKey(mergeRoute);
2249
+ saveBotRunnerState({
2250
+ routes: {
2251
+ [mergeRouteKey]: {
2252
+ updated_at: "2026-03-24T06:29:05.000Z",
2253
+ last_request_key: "request-key-597",
2254
+ last_action: "running",
2255
+ conversation_sessions: {
2256
+ "conversation-597": {
2257
+ conversation_id: "conversation-597",
2258
+ status: "open",
2259
+ updated_at: "2026-03-24T06:29:05.000Z",
2260
+ last_activity_at: "2026-03-24T06:29:05.000Z",
2261
+ last_execution_contract_type: "delegation",
2262
+ next_expected_responders: ["ryoai2_bot", "ryoai3_bot"],
2263
+ },
2264
+ },
2265
+ },
2266
+ },
2267
+ sharedInboxes: {},
2268
+ excludedComments: {},
2269
+ requests: {
2270
+ "request-key-597": {
2271
+ request_key: "request-key-597",
2272
+ project_id: selftestProjectID,
2273
+ provider: "telegram",
2274
+ chat_id: "-100123",
2275
+ source_message_id: 597,
2276
+ conversation_id: "conversation-597",
2277
+ normalized_intent: "explanation_query",
2278
+ execution_contract_type: "delegation",
2279
+ execution_contract_actionable: true,
2280
+ next_expected_responders: ["ryoai2_bot", "ryoai3_bot"],
2281
+ status: "running",
2282
+ claimed_by_route: mergeRouteKey,
2283
+ root_work_item_id: "root-work-item-597",
2284
+ root_thread_id: "root-thread-597",
2285
+ updated_at: "2026-03-24T06:29:05.000Z",
2286
+ },
2287
+ },
2288
+ consumedComments: {},
2289
+ });
2290
+ saveBotRunnerState({
2291
+ routes: {
2292
+ [mergeRouteKey]: {
2293
+ updated_at: "2026-03-24T06:29:06.000Z",
2294
+ last_request_key: "request-key-597",
2295
+ last_action: "replied",
2296
+ conversation_sessions: {
2297
+ "conversation-597": {
2298
+ conversation_id: "conversation-597",
2299
+ status: "open",
2300
+ updated_at: "2026-03-24T06:29:06.000Z",
2301
+ },
2302
+ },
2303
+ },
2304
+ },
2305
+ sharedInboxes: {},
2306
+ excludedComments: {},
2307
+ requests: {
2308
+ "request-key-597": {
2309
+ request_key: "request-key-597",
2310
+ project_id: selftestProjectID,
2311
+ provider: "telegram",
2312
+ chat_id: "-100123",
2313
+ source_message_id: 597,
2314
+ conversation_id: "conversation-597",
2315
+ normalized_intent: "general_execution",
2316
+ status: "claimed",
2317
+ claimed_by_route: mergeRouteKey,
2318
+ root_work_item_id: "",
2319
+ root_thread_id: "",
2320
+ next_expected_responders: [],
2321
+ updated_at: "2026-03-24T06:28:30.000Z",
2322
+ },
2323
+ },
2324
+ consumedComments: {},
2325
+ });
2326
+ const mergedState = loadBotRunnerState();
2327
+ const mergedRouteState = safeObject(safeObject(mergedState.routes)[mergeRouteKey]);
2328
+ const mergedSession = safeObject(safeObject(mergedRouteState.conversation_sessions)["conversation-597"]);
2329
+ const mergedRequest = safeObject(safeObject(mergedState.requests)["request-key-597"]);
2330
+ push(
2331
+ "runner_state_save_preserves_newer_request_and_conversation_session",
2332
+ String(mergedRequest.status || "") === "running"
2333
+ && String(mergedRequest.root_work_item_id || "") === "root-work-item-597"
2334
+ && ensureArray(mergedRequest.next_expected_responders).includes("ryoai2_bot")
2335
+ && String(mergedSession.status || "") === "open"
2336
+ && String(mergedSession.last_execution_contract_type || "") === "delegation"
2337
+ && ensureArray(mergedSession.next_expected_responders).includes("ryoai3_bot"),
2338
+ `request_status=${String(mergedRequest.status || "(none)")} root_work_item=${String(mergedRequest.root_work_item_id || "(none)")} session_contract=${String(mergedSession.last_execution_contract_type || "(none)")} next=${ensureArray(mergedSession.next_expected_responders).join(",")}`,
2339
+ );
2340
+ } catch (err) {
2341
+ push("runner_state_save_preserves_newer_request_and_conversation_session", false, String(err?.message || err));
2342
+ } finally {
2343
+ if (typeof originalStateMergeHome === "string") {
2344
+ process.env.HOME = originalStateMergeHome;
2345
+ } else {
2346
+ delete process.env.HOME;
2347
+ }
2348
+ if (typeof originalStateMergeUserProfile === "string") {
2349
+ process.env.USERPROFILE = originalStateMergeUserProfile;
2350
+ } else {
2351
+ delete process.env.USERPROFILE;
2352
+ }
2353
+ if (runnerStateMergeTempRoot) {
2354
+ try {
2355
+ fs.rmSync(runnerStateMergeTempRoot, { recursive: true, force: true });
2356
+ } catch {}
2357
+ }
2358
+ }
2359
+
2232
2360
  const defaultMonitorTriggerPolicy = normalizeRunnerTriggerPolicy({}, { role: "monitor" });
2233
2361
  push(
2234
2362
  "bot_runner_default_monitor_trigger_policy",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "metheus-governance-mcp-cli",
3
- "version": "0.2.205",
3
+ "version": "0.2.206",
4
4
  "description": "Metheus Governance MCP CLI (setup + stdio proxy)",
5
5
  "type": "module",
6
6
  "files": [