metheus-governance-mcp-cli 0.2.276 → 0.2.277
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 +15 -1
- package/lib/runner-orchestration-adjudication.mjs +60 -0
- package/lib/runner-recovery.mjs +19 -0
- package/lib/selftest-runner-scenarios.mjs +141 -5
- package/package.json +1 -1
package/cli.mjs
CHANGED
|
@@ -5770,7 +5770,21 @@ async function finalizePreparedRunnerSelectedRecordExecutionContext({
|
|
|
5770
5770
|
};
|
|
5771
5771
|
}
|
|
5772
5772
|
if (mode === "continuation_finalize") {
|
|
5773
|
-
const requestClaim = normalizePreparedRunnerRequestClaim(
|
|
5773
|
+
const requestClaim = normalizePreparedRunnerRequestClaim(resolveRunnerContinuationRequestForBotReplyImpl({
|
|
5774
|
+
normalizedRoute,
|
|
5775
|
+
routeKey,
|
|
5776
|
+
selectedRecord,
|
|
5777
|
+
persist: true,
|
|
5778
|
+
deps: buildRunnerRecoveryDeps(),
|
|
5779
|
+
}));
|
|
5780
|
+
if (!requestClaim.ok) {
|
|
5781
|
+
return finalizeRunnerRequestClaimFailureRecorderState({
|
|
5782
|
+
normalizedRoute,
|
|
5783
|
+
runtime,
|
|
5784
|
+
requestClaim,
|
|
5785
|
+
syncRunnerRequestLedgerForProjectToServer,
|
|
5786
|
+
});
|
|
5787
|
+
}
|
|
5774
5788
|
const finalizedRequest = await finalizePreparedRunnerRequestForExecution({
|
|
5775
5789
|
normalizedRoute,
|
|
5776
5790
|
routeKey,
|
|
@@ -16,6 +16,10 @@ function ensureArray(value) {
|
|
|
16
16
|
return Array.isArray(value) ? value : [];
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
+
function normalizeMentionSelector(rawValue) {
|
|
20
|
+
return String(rawValue || "").trim().replace(/^@+/, "").toLowerCase();
|
|
21
|
+
}
|
|
22
|
+
|
|
19
23
|
function intFromRawAllowZero(raw, fallback = 0) {
|
|
20
24
|
if (raw === null || raw === undefined || raw === "") {
|
|
21
25
|
return fallback;
|
|
@@ -48,6 +52,45 @@ function buildRunnerContinuationContractTriggerDecision(triggerDecision) {
|
|
|
48
52
|
};
|
|
49
53
|
}
|
|
50
54
|
|
|
55
|
+
function isRunnerBotReplyContinuationAuthorizedForCurrentBot({
|
|
56
|
+
selectedRecord,
|
|
57
|
+
persistedRequest = null,
|
|
58
|
+
currentBotSelector = "",
|
|
59
|
+
}) {
|
|
60
|
+
const normalizedCurrentBotSelector = normalizeMentionSelector(currentBotSelector);
|
|
61
|
+
if (!normalizedCurrentBotSelector) {
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
const parsed = safeObject(selectedRecord?.parsedArchive);
|
|
65
|
+
const normalizedSenderSelectors = ensureArray([
|
|
66
|
+
parsed.botUsername,
|
|
67
|
+
parsed.senderUsername,
|
|
68
|
+
parsed.sender,
|
|
69
|
+
])
|
|
70
|
+
.map((value) => normalizeMentionSelector(value))
|
|
71
|
+
.filter(Boolean);
|
|
72
|
+
if (normalizedSenderSelectors.includes(normalizedCurrentBotSelector)) {
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
const explicitReplyTargets = ensureArray([
|
|
76
|
+
...(Array.isArray(parsed.mentionUsernames) ? parsed.mentionUsernames : []),
|
|
77
|
+
parsed.replyToBotUsername,
|
|
78
|
+
parsed.targetBotUsername,
|
|
79
|
+
parsed.assignBotUsername,
|
|
80
|
+
parsed.nextResponderBotUsername,
|
|
81
|
+
])
|
|
82
|
+
.map((value) => normalizeMentionSelector(value))
|
|
83
|
+
.filter(Boolean);
|
|
84
|
+
if (explicitReplyTargets.includes(normalizedCurrentBotSelector)) {
|
|
85
|
+
return true;
|
|
86
|
+
}
|
|
87
|
+
const request = safeObject(persistedRequest);
|
|
88
|
+
const nextExpectedResponders = ensureArray(request.next_expected_responders)
|
|
89
|
+
.map((value) => normalizeMentionSelector(value))
|
|
90
|
+
.filter(Boolean);
|
|
91
|
+
return nextExpectedResponders.includes(normalizedCurrentBotSelector);
|
|
92
|
+
}
|
|
93
|
+
|
|
51
94
|
export async function resolveRunnerPrecomputedResponderAdjudication({
|
|
52
95
|
selectedRecord,
|
|
53
96
|
pendingOrdered,
|
|
@@ -189,6 +232,11 @@ export async function resolveRunnerPrecomputedBotReplyAuthorityContext({
|
|
|
189
232
|
persistedRequest: resolveRunnerContinuationLinkedRequest(normalizedRequestClaim),
|
|
190
233
|
triggerDecision,
|
|
191
234
|
});
|
|
235
|
+
const continuationAuthorizedForCurrentBot = isRunnerBotReplyContinuationAuthorizedForCurrentBot({
|
|
236
|
+
selectedRecord,
|
|
237
|
+
persistedRequest: resolveRunnerContinuationLinkedRequest(normalizedRequestClaim),
|
|
238
|
+
currentBotSelector,
|
|
239
|
+
});
|
|
192
240
|
const currentBotSelected = ensureArray(continuationAdjudication?.selected_bot_usernames)
|
|
193
241
|
.map((value) => String(value || "").trim().replace(/^@+/, "").toLowerCase())
|
|
194
242
|
.includes(currentBotSelector);
|
|
@@ -204,6 +252,18 @@ export async function resolveRunnerPrecomputedBotReplyAuthorityContext({
|
|
|
204
252
|
sharedHumanIntentContext: null,
|
|
205
253
|
};
|
|
206
254
|
}
|
|
255
|
+
if (!continuationAuthorizedForCurrentBot) {
|
|
256
|
+
return {
|
|
257
|
+
shouldSkip: true,
|
|
258
|
+
requestless: false,
|
|
259
|
+
skipAction: "adjudication_skipped",
|
|
260
|
+
skipReason: "bot_reply_not_explicitly_handed_off",
|
|
261
|
+
skipTrigger: "request_contract",
|
|
262
|
+
skipRecordPatch: {},
|
|
263
|
+
adjudication: continuationAdjudication,
|
|
264
|
+
sharedHumanIntentContext: null,
|
|
265
|
+
};
|
|
266
|
+
}
|
|
207
267
|
return {
|
|
208
268
|
shouldSkip: false,
|
|
209
269
|
requestless: false,
|
package/lib/runner-recovery.mjs
CHANGED
|
@@ -431,6 +431,7 @@ export function peekRunnerContinuationRequestForBotReply({
|
|
|
431
431
|
export function recoverRunnerContinuationRequestForBotReply({
|
|
432
432
|
normalizedRoute,
|
|
433
433
|
continuationPreview,
|
|
434
|
+
persist = true,
|
|
434
435
|
deps = {},
|
|
435
436
|
}) {
|
|
436
437
|
const upsertRunnerRequest = requireDependency(deps, "upsertRunnerRequest");
|
|
@@ -506,6 +507,18 @@ export function recoverRunnerContinuationRequestForBotReply({
|
|
|
506
507
|
|| "",
|
|
507
508
|
).trim().toLowerCase(),
|
|
508
509
|
};
|
|
510
|
+
if (persist !== true) {
|
|
511
|
+
return {
|
|
512
|
+
ok: true,
|
|
513
|
+
linkedRequestKey: fallbackRequestKey,
|
|
514
|
+
linkedRequest: seedRequest,
|
|
515
|
+
currentState,
|
|
516
|
+
conversationID: String(preview.conversationID || "").trim(),
|
|
517
|
+
commentKind: String(preview.commentKind || "").trim().toLowerCase(),
|
|
518
|
+
linkResolutionKind: "recovered_request_link",
|
|
519
|
+
linkRecovered: true,
|
|
520
|
+
};
|
|
521
|
+
}
|
|
509
522
|
const seededRequest = upsertRunnerRequest(currentState, fallbackRequestKey, seedRequest);
|
|
510
523
|
currentState.requests = seededRequest.requests;
|
|
511
524
|
saveBotRunnerState({
|
|
@@ -531,6 +544,7 @@ export function resolveRunnerContinuationRequestForBotReply({
|
|
|
531
544
|
normalizedRoute,
|
|
532
545
|
routeKey,
|
|
533
546
|
selectedRecord,
|
|
547
|
+
persist = true,
|
|
534
548
|
deps = {},
|
|
535
549
|
}) {
|
|
536
550
|
const upsertRunnerConsumedComment = requireDependency(deps, "upsertRunnerConsumedComment");
|
|
@@ -547,12 +561,16 @@ export function resolveRunnerContinuationRequestForBotReply({
|
|
|
547
561
|
continuation = recoverRunnerContinuationRequestForBotReply({
|
|
548
562
|
normalizedRoute,
|
|
549
563
|
continuationPreview: continuation,
|
|
564
|
+
persist,
|
|
550
565
|
deps,
|
|
551
566
|
});
|
|
552
567
|
}
|
|
553
568
|
if (!continuation.ok) {
|
|
554
569
|
return continuation;
|
|
555
570
|
}
|
|
571
|
+
if (persist !== true) {
|
|
572
|
+
return continuation;
|
|
573
|
+
}
|
|
556
574
|
const {
|
|
557
575
|
currentState,
|
|
558
576
|
linkedRequest,
|
|
@@ -609,6 +627,7 @@ export function prepareRunnerBotReplyContinuationContext({
|
|
|
609
627
|
normalizedRoute,
|
|
610
628
|
routeKey,
|
|
611
629
|
selectedRecord,
|
|
630
|
+
persist: false,
|
|
612
631
|
deps,
|
|
613
632
|
})
|
|
614
633
|
: continuationPreview;
|
|
@@ -16340,11 +16340,82 @@ export async function runSelftestRunnerScenarios(push, deps) {
|
|
|
16340
16340
|
&& !Object.prototype.hasOwnProperty.call(continuation, "request"),
|
|
16341
16341
|
`linked_key=${String(continuation.linkedRequestKey || "(none)")} has_request=${String(Object.prototype.hasOwnProperty.call(continuation, "request"))}`,
|
|
16342
16342
|
);
|
|
16343
|
-
|
|
16344
|
-
|
|
16345
|
-
|
|
16346
|
-
|
|
16347
|
-
|
|
16343
|
+
saveBotRunnerState({
|
|
16344
|
+
routes: {
|
|
16345
|
+
[provenanceRouteKey]: {
|
|
16346
|
+
conversation_sessions: {
|
|
16347
|
+
"conversation-provenance-seed": {
|
|
16348
|
+
status: "open",
|
|
16349
|
+
started_at: "2026-03-27T00:00:00.000Z",
|
|
16350
|
+
participants: ["ryoai_bot"],
|
|
16351
|
+
initial_responders: ["ryoai_bot"],
|
|
16352
|
+
allowed_responders: ["ryoai_bot"],
|
|
16353
|
+
intent_mode: "single_bot",
|
|
16354
|
+
allow_bot_to_bot: false,
|
|
16355
|
+
},
|
|
16356
|
+
},
|
|
16357
|
+
recent_local_inbound_receipts: {
|
|
16358
|
+
"-100123:779": {
|
|
16359
|
+
chat_id: "-100123",
|
|
16360
|
+
message_id: 779,
|
|
16361
|
+
receipt_origin: "local_telegram_inbound",
|
|
16362
|
+
receipt_route_key: provenanceRouteKey,
|
|
16363
|
+
receipt_bot_username: "ryoai_bot",
|
|
16364
|
+
source_origin: "local_telegram_inbound",
|
|
16365
|
+
source_route_key: provenanceRouteKey,
|
|
16366
|
+
source_bot_username: "ryoai_bot",
|
|
16367
|
+
body: "hello provenance",
|
|
16368
|
+
message_thread_id: 43,
|
|
16369
|
+
},
|
|
16370
|
+
},
|
|
16371
|
+
last_followup_source_message_envelope: {
|
|
16372
|
+
message_id: 779,
|
|
16373
|
+
message_thread_id: 43,
|
|
16374
|
+
body: "hello provenance",
|
|
16375
|
+
source_origin: "local_telegram_inbound",
|
|
16376
|
+
source_route_key: provenanceRouteKey,
|
|
16377
|
+
source_bot_username: "ryoai_bot",
|
|
16378
|
+
},
|
|
16379
|
+
},
|
|
16380
|
+
},
|
|
16381
|
+
sharedInboxes: {},
|
|
16382
|
+
excludedComments: {},
|
|
16383
|
+
requests: {},
|
|
16384
|
+
consumedComments: {},
|
|
16385
|
+
});
|
|
16386
|
+
const stateBeforePreviewOnlyContinuation = loadBotRunnerState();
|
|
16387
|
+
const previewOnlyRecoveryContext = prepareRunnerSelectedRecordRecoveryContext({
|
|
16388
|
+
normalizedRoute: provenanceRoute,
|
|
16389
|
+
routeKey: provenanceRouteKey,
|
|
16390
|
+
selectedRecord: {
|
|
16391
|
+
id: "bot-reply-preview-only-seed",
|
|
16392
|
+
parsedArchive: {
|
|
16393
|
+
kind: "bot_reply",
|
|
16394
|
+
chatID: "-100123",
|
|
16395
|
+
messageID: 881,
|
|
16396
|
+
senderIsBot: true,
|
|
16397
|
+
conversationID: "conversation-provenance-seed",
|
|
16398
|
+
botUsername: "@ryoai_bot",
|
|
16399
|
+
},
|
|
16400
|
+
},
|
|
16401
|
+
});
|
|
16402
|
+
const stateAfterPreviewOnlyContinuation = loadBotRunnerState();
|
|
16403
|
+
const previewRequestsBefore = Object.keys(normalizeBotRunnerRequests(stateBeforePreviewOnlyContinuation.requests)).length;
|
|
16404
|
+
const previewRequestsAfter = Object.keys(normalizeBotRunnerRequests(stateAfterPreviewOnlyContinuation.requests)).length;
|
|
16405
|
+
const previewConsumedBefore = Object.keys(safeObject(stateBeforePreviewOnlyContinuation.consumedComments || stateBeforePreviewOnlyContinuation.consumed_comments)).length;
|
|
16406
|
+
const previewConsumedAfter = Object.keys(safeObject(stateAfterPreviewOnlyContinuation.consumedComments || stateAfterPreviewOnlyContinuation.consumed_comments)).length;
|
|
16407
|
+
push(
|
|
16408
|
+
"runner_continuation_preview_does_not_write_request_or_consumed_state",
|
|
16409
|
+
safeObject(previewOnlyRecoveryContext.botReplyContinuationContext).continuationLinkRecoverable === true
|
|
16410
|
+
&& previewRequestsBefore === previewRequestsAfter
|
|
16411
|
+
&& previewConsumedBefore === previewConsumedAfter,
|
|
16412
|
+
`recoverable=${String(safeObject(previewOnlyRecoveryContext.botReplyContinuationContext).continuationLinkRecoverable)} requests=${String(previewRequestsBefore)}->${String(previewRequestsAfter)} consumed=${String(previewConsumedBefore)}->${String(previewConsumedAfter)}`,
|
|
16413
|
+
);
|
|
16414
|
+
} finally {
|
|
16415
|
+
process.env.HOME = previousHome;
|
|
16416
|
+
process.env.USERPROFILE = previousUserProfile;
|
|
16417
|
+
try {
|
|
16418
|
+
fs.rmSync(provenanceTempRoot, { recursive: true, force: true });
|
|
16348
16419
|
} catch {}
|
|
16349
16420
|
}
|
|
16350
16421
|
} catch (err) {
|
|
@@ -17447,6 +17518,7 @@ export async function runSelftestRunnerScenarios(push, deps) {
|
|
|
17447
17518
|
linkedRequest: {
|
|
17448
17519
|
request_key: "req-1",
|
|
17449
17520
|
selected_bot_usernames: ["alpha_bot"],
|
|
17521
|
+
next_expected_responders: ["alpha_bot"],
|
|
17450
17522
|
},
|
|
17451
17523
|
},
|
|
17452
17524
|
},
|
|
@@ -17493,6 +17565,7 @@ export async function runSelftestRunnerScenarios(push, deps) {
|
|
|
17493
17565
|
linkedRequest: {
|
|
17494
17566
|
request_key: "req-1",
|
|
17495
17567
|
selected_bot_usernames: ["alpha_bot"],
|
|
17568
|
+
next_expected_responders: ["alpha_bot"],
|
|
17496
17569
|
},
|
|
17497
17570
|
},
|
|
17498
17571
|
},
|
|
@@ -17529,6 +17602,69 @@ export async function runSelftestRunnerScenarios(push, deps) {
|
|
|
17529
17602
|
push("runner_selected_record_execution_context_promotes_continuation_only_after_recovery", false, String(err?.message || err));
|
|
17530
17603
|
}
|
|
17531
17604
|
|
|
17605
|
+
try {
|
|
17606
|
+
const preparation = await resolveRunnerPrecomputedSelectedRecordExecutionContext({
|
|
17607
|
+
selectedRecord: {
|
|
17608
|
+
id: "comment-continuation-no-explicit-handoff",
|
|
17609
|
+
parsedArchive: {
|
|
17610
|
+
kind: "bot_reply",
|
|
17611
|
+
chatID: "-100123",
|
|
17612
|
+
chatType: "supergroup",
|
|
17613
|
+
messageID: 3504,
|
|
17614
|
+
body: "foreign bot follow up",
|
|
17615
|
+
sender: "other bot",
|
|
17616
|
+
senderIsBot: true,
|
|
17617
|
+
botUsername: "@other_bot",
|
|
17618
|
+
mentionUsernames: [],
|
|
17619
|
+
},
|
|
17620
|
+
},
|
|
17621
|
+
recoveryContext: {
|
|
17622
|
+
selectedRecordKind: "bot_reply",
|
|
17623
|
+
botReplyContinuationContext: {
|
|
17624
|
+
continuationLinkRecoverable: true,
|
|
17625
|
+
continuationLinkResolution: {
|
|
17626
|
+
ok: true,
|
|
17627
|
+
linkedRequestKey: "req-no-explicit-handoff",
|
|
17628
|
+
linkedRequest: {
|
|
17629
|
+
request_key: "req-no-explicit-handoff",
|
|
17630
|
+
selected_bot_usernames: ["alpha_bot"],
|
|
17631
|
+
next_expected_responders: [],
|
|
17632
|
+
},
|
|
17633
|
+
},
|
|
17634
|
+
},
|
|
17635
|
+
},
|
|
17636
|
+
normalizedRoute: {
|
|
17637
|
+
provider: "telegram",
|
|
17638
|
+
triggerPolicy: {
|
|
17639
|
+
replyToBotMessages: true,
|
|
17640
|
+
},
|
|
17641
|
+
},
|
|
17642
|
+
routeKey: "telegram-monitor-alpha::project::telegram::monitor::dest::actor",
|
|
17643
|
+
routeState: {},
|
|
17644
|
+
fallbackRouteState: {},
|
|
17645
|
+
bot: {
|
|
17646
|
+
username: "alpha_bot",
|
|
17647
|
+
name: "alpha_bot",
|
|
17648
|
+
},
|
|
17649
|
+
executionPlan: {},
|
|
17650
|
+
pendingOrdered: [],
|
|
17651
|
+
currentBotSelector: "alpha_bot",
|
|
17652
|
+
routingExecutionDeps: {
|
|
17653
|
+
managedConversationBots: [],
|
|
17654
|
+
},
|
|
17655
|
+
});
|
|
17656
|
+
push(
|
|
17657
|
+
"runner_selected_record_execution_context_requires_explicit_handoff_for_bot_reply_continuation",
|
|
17658
|
+
preparation.kind === "skip"
|
|
17659
|
+
&& String(preparation?.skipAction || "") === "adjudication_skipped"
|
|
17660
|
+
&& String(preparation?.skipReason || "") === "bot_reply_not_explicitly_handed_off"
|
|
17661
|
+
&& String(preparation?.skipTrigger || "") === "request_contract",
|
|
17662
|
+
`kind=${String(preparation?.kind || "(none)")} action=${String(preparation?.skipAction || "(none)")} reason=${String(preparation?.skipReason || "(none)")} trigger=${String(preparation?.skipTrigger || "(none)")}`,
|
|
17663
|
+
);
|
|
17664
|
+
} catch (err) {
|
|
17665
|
+
push("runner_selected_record_execution_context_requires_explicit_handoff_for_bot_reply_continuation", false, String(err?.message || err));
|
|
17666
|
+
}
|
|
17667
|
+
|
|
17532
17668
|
try {
|
|
17533
17669
|
const preparation = await resolveRunnerPrecomputedSelectedRecordExecutionContext({
|
|
17534
17670
|
selectedRecord: {
|