metheus-governance-mcp-cli 0.2.221 → 0.2.223
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 +23 -2
- package/lib/local-ai-adapters.mjs +21 -0
- package/lib/runner-orchestration.mjs +29 -1
- package/lib/runner-trigger.mjs +41 -0
- package/lib/selftest-runner-scenarios.mjs +48 -0
- package/package.json +1 -1
package/cli.mjs
CHANGED
|
@@ -11085,8 +11085,10 @@ async function runRunnerStartResolvedRoutes(routes, flags, options = {}) {
|
|
|
11085
11085
|
route_name: normalizedRoute.name,
|
|
11086
11086
|
logical_signature: runnerRouteLogicalSignature(normalizedRoute),
|
|
11087
11087
|
});
|
|
11088
|
+
let cycleOutcome = "polling";
|
|
11088
11089
|
try {
|
|
11089
11090
|
const result = await processRunnerRouteOnce(normalizedRoute, runtime, "start", { deferExecution: true });
|
|
11091
|
+
cycleOutcome = String(result?.outcome || "").trim().toLowerCase() || "idle";
|
|
11090
11092
|
const deferredExecution = safeObject(result.deferred_execution);
|
|
11091
11093
|
if (deferredExecution.routeKey) {
|
|
11092
11094
|
tui?.reportExecutionStage({
|
|
@@ -11392,6 +11394,7 @@ async function runRunnerStartResolvedRoutes(routes, flags, options = {}) {
|
|
|
11392
11394
|
...result,
|
|
11393
11395
|
deferred_execution: undefined,
|
|
11394
11396
|
};
|
|
11397
|
+
cycleOutcome = String(acceptedResult?.outcome || "accepted").trim().toLowerCase() || "accepted";
|
|
11395
11398
|
runnerLogger?.append("route_result", {
|
|
11396
11399
|
route_key: String(acceptedResult?.route_key || routeKey).trim(),
|
|
11397
11400
|
route_name: String(acceptedResult?.route_name || normalizedRoute.name || "").trim(),
|
|
@@ -11430,6 +11433,7 @@ async function runRunnerStartResolvedRoutes(routes, flags, options = {}) {
|
|
|
11430
11433
|
const errorText = String(err?.message || err);
|
|
11431
11434
|
const fatalArchiveBootstrapError = errorText.includes("Archive thread is missing")
|
|
11432
11435
|
&& errorText.includes("write access is denied");
|
|
11436
|
+
cycleOutcome = fatalArchiveBootstrapError ? "blocked" : "error";
|
|
11433
11437
|
saveRunnerRouteState(routeKey, {
|
|
11434
11438
|
...safeObject(loadBotRunnerState().routes[routeKey]),
|
|
11435
11439
|
last_error: errorText,
|
|
@@ -11453,8 +11457,25 @@ async function runRunnerStartResolvedRoutes(routes, flags, options = {}) {
|
|
|
11453
11457
|
printRunnerResult("start", result, jsonMode);
|
|
11454
11458
|
}
|
|
11455
11459
|
} finally {
|
|
11456
|
-
|
|
11457
|
-
|
|
11460
|
+
let routeState = safeObject(loadBotRunnerState().routes[routeKey]);
|
|
11461
|
+
let activeExecutionState = resolveRunnerActiveExecutionState(routeState);
|
|
11462
|
+
const shouldClearStaleRouteError = !activeExecutionState.active
|
|
11463
|
+
&& [
|
|
11464
|
+
"idle",
|
|
11465
|
+
"primed",
|
|
11466
|
+
"skipped",
|
|
11467
|
+
"replied",
|
|
11468
|
+
"dry_run",
|
|
11469
|
+
"delivery_failed_after_generation",
|
|
11470
|
+
].includes(cycleOutcome);
|
|
11471
|
+
if (shouldClearStaleRouteError && (String(routeState.last_error || "").trim() || String(routeState.active_comment_id || "").trim())) {
|
|
11472
|
+
saveRunnerRouteState(routeKey, {
|
|
11473
|
+
...emptyRunnerActiveExecutionPatch(),
|
|
11474
|
+
last_error: "",
|
|
11475
|
+
});
|
|
11476
|
+
routeState = safeObject(loadBotRunnerState().routes[routeKey]);
|
|
11477
|
+
activeExecutionState = resolveRunnerActiveExecutionState(routeState);
|
|
11478
|
+
}
|
|
11458
11479
|
tui?.setRouteState(routeKey, {
|
|
11459
11480
|
intent_type: String(routeState.last_intent_type || "").trim(),
|
|
11460
11481
|
source_message_id: intFromRawAllowZero(routeState.last_source_message_id, 0),
|
|
@@ -1939,6 +1939,18 @@ export function buildLocalBotPrompt(payload, { terse = true } = {}) {
|
|
|
1939
1939
|
&& selfIsLeadBot
|
|
1940
1940
|
? "The human asked this bot to coordinate other bots publicly. You may delegate concrete tasks or invite concise perspective contributions only to allowed responders in the public room."
|
|
1941
1941
|
: "Do not delegate the answer to another bot.",
|
|
1942
|
+
responseContract.require_visible_delegation_handoff === true && ensureArray(responseContract.required_delegation_targets).length > 0
|
|
1943
|
+
? `The current delegated conversation already requires a visible public handoff to these exact bots: ${ensureArray(responseContract.required_delegation_targets).map((item) => `@${String(item || "").trim().replace(/^@+/, "")}`).join(", ")}.`
|
|
1944
|
+
: "",
|
|
1945
|
+
responseContract.require_visible_delegation_handoff === true
|
|
1946
|
+
? "Your first public reply must explicitly mention those exact bots and request their contribution now."
|
|
1947
|
+
: "",
|
|
1948
|
+
responseContract.require_visible_delegation_handoff === true
|
|
1949
|
+
? "Do not return only a human-facing clarification or information request without visibly addressing those bots."
|
|
1950
|
+
: "",
|
|
1951
|
+
responseContract.require_visible_delegation_handoff === true
|
|
1952
|
+
? "If information is incomplete, still open the public handoff and ask each target bot for a concise initial perspective based on the uncertainty."
|
|
1953
|
+
: "",
|
|
1942
1954
|
"Mentions and reply targets are routing hints, not proof that this bot should definitely answer.",
|
|
1943
1955
|
conversation?.mode === "public_multi_bot"
|
|
1944
1956
|
? "This is a public multi-bot room conversation. Other bots and humans can read your reply."
|
|
@@ -2117,6 +2129,15 @@ export function buildLocalBotPrompt(payload, { terse = true } = {}) {
|
|
|
2117
2129
|
if (selfIsLeadBot) {
|
|
2118
2130
|
lines.push(
|
|
2119
2131
|
"As the lead bot, your first public reply must be immediately actionable.",
|
|
2132
|
+
responseContract.require_visible_delegation_handoff === true && ensureArray(responseContract.required_delegation_targets).length > 0
|
|
2133
|
+
? `Because this delegated conversation already targets ${ensureArray(responseContract.required_delegation_targets).map((item) => `@${String(item || "").trim().replace(/^@+/, "")}`).join(", ")}, your first public reply must visibly call those exact bots in the room now.`
|
|
2134
|
+
: "",
|
|
2135
|
+
responseContract.require_visible_delegation_handoff === true
|
|
2136
|
+
? "A contract-only delegation is incomplete. The visible reply text must enact the handoff."
|
|
2137
|
+
: "",
|
|
2138
|
+
responseContract.require_visible_delegation_handoff === true
|
|
2139
|
+
? "Do not stop at asking only the human for more information. Open the bot handoff first, then note what remains unknown."
|
|
2140
|
+
: "",
|
|
2120
2141
|
"If you are delegating, include contract.type=\"delegation\" with explicit assignments or perspective handoffs for the exact bots you are tasking now.",
|
|
2121
2142
|
"If you mention any allowed responder in the reply text, the contract must name that responder in assignments or next_responders.",
|
|
2122
2143
|
"If you are completing the work yourself now, include contract.type=\"direct_result\".",
|
|
@@ -5103,7 +5103,7 @@ export async function processRunnerSelectedRecord({
|
|
|
5103
5103
|
&& executionContract.actionable === true
|
|
5104
5104
|
&& (!allowedActionableTypes.size || allowedActionableTypes.has(String(executionContract.type || "").trim().toLowerCase())),
|
|
5105
5105
|
);
|
|
5106
|
-
|
|
5106
|
+
let responseContractValidation = (() => {
|
|
5107
5107
|
if (requiresActionableContract && !hasValidActionableContract) {
|
|
5108
5108
|
return {
|
|
5109
5109
|
ok: false,
|
|
@@ -5286,6 +5286,34 @@ export async function processRunnerSelectedRecord({
|
|
|
5286
5286
|
triggerDecision: effectiveTriggerDecision,
|
|
5287
5287
|
conversationContext: effectiveConversationContext,
|
|
5288
5288
|
});
|
|
5289
|
+
const visibleDelegationTargets = uniqueOrdered(
|
|
5290
|
+
effectiveConversationContext?.mode === "public_multi_bot"
|
|
5291
|
+
&& String(effectiveConversationContext?.stage || "").trim() === "human_opening"
|
|
5292
|
+
&& effectiveResolvedIntentMode === "delegated_single_lead"
|
|
5293
|
+
&& currentBotSelector
|
|
5294
|
+
&& currentBotSelector === normalizeMentionSelector(effectiveConversationContext?.leadBotUsername)
|
|
5295
|
+
&& normalizedExecutionContractType === "delegation"
|
|
5296
|
+
? ensureArray(executionContract?.assignments)
|
|
5297
|
+
.map((item) => normalizeMentionSelector(item?.targetBot))
|
|
5298
|
+
.filter(Boolean)
|
|
5299
|
+
: [],
|
|
5300
|
+
);
|
|
5301
|
+
if (responseContractValidation.ok && visibleDelegationTargets.length > 0) {
|
|
5302
|
+
const visibleReplyTargets = extractManagedPeerMentionSelectors(
|
|
5303
|
+
sanitizedReplyText,
|
|
5304
|
+
directHumanPeerMap,
|
|
5305
|
+
currentBotSelector,
|
|
5306
|
+
);
|
|
5307
|
+
const missingVisibleDelegationTargets = visibleDelegationTargets.filter((item) => !visibleReplyTargets.includes(item));
|
|
5308
|
+
if (missingVisibleDelegationTargets.length > 0) {
|
|
5309
|
+
responseContractValidation = {
|
|
5310
|
+
ok: false,
|
|
5311
|
+
status: "delegation_targets_not_visible_in_reply",
|
|
5312
|
+
reason: `delegation contract targeted ${missingVisibleDelegationTargets.map((item) => `@${String(item || "").trim().replace(/^@+/, "")}`).join(", ")} but the public reply did not visibly hand off to them`,
|
|
5313
|
+
targets: missingVisibleDelegationTargets,
|
|
5314
|
+
};
|
|
5315
|
+
}
|
|
5316
|
+
}
|
|
5289
5317
|
if (effectiveConversationContext?.mode === "public_multi_bot") {
|
|
5290
5318
|
const currentBotSelector = normalizeMentionSelector(bot?.username || bot?.name);
|
|
5291
5319
|
const currentSession = safeObject(effectiveConversationContext.session);
|
package/lib/runner-trigger.mjs
CHANGED
|
@@ -11,6 +11,18 @@ function ensureArray(value) {
|
|
|
11
11
|
return Array.isArray(value) ? value : [];
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
+
function uniqueOrdered(items) {
|
|
15
|
+
const seen = new Set();
|
|
16
|
+
const result = [];
|
|
17
|
+
for (const item of ensureArray(items)) {
|
|
18
|
+
const text = String(item || "").trim();
|
|
19
|
+
if (!text || seen.has(text)) continue;
|
|
20
|
+
seen.add(text);
|
|
21
|
+
result.push(text);
|
|
22
|
+
}
|
|
23
|
+
return result;
|
|
24
|
+
}
|
|
25
|
+
|
|
14
26
|
function intFromRawAllowZero(raw, fallback = 0) {
|
|
15
27
|
const text = String(raw ?? "").trim();
|
|
16
28
|
if (!text) return fallback;
|
|
@@ -177,6 +189,31 @@ export function buildRunnerInputPayload({
|
|
|
177
189
|
const executionContractType = String(
|
|
178
190
|
directHumanIntent.executionContractType || directHumanIntent.execution_contract_type || "",
|
|
179
191
|
).trim().toLowerCase();
|
|
192
|
+
const currentBotSelector = normalizeTelegramMentionUsername(bot?.username || bot?.name);
|
|
193
|
+
const normalizedLeadBot = normalizeTelegramMentionUsername(
|
|
194
|
+
directHumanIntent.leadBotSelector || directHumanIntent.lead_bot,
|
|
195
|
+
);
|
|
196
|
+
const normalizedInitialResponders = uniqueOrdered(
|
|
197
|
+
ensureArray(directHumanIntent.initialResponderSelectors || directHumanIntent.initial_responders)
|
|
198
|
+
.map((value) => normalizeTelegramMentionUsername(value))
|
|
199
|
+
.filter(Boolean),
|
|
200
|
+
);
|
|
201
|
+
const normalizedAllowedResponders = uniqueOrdered(
|
|
202
|
+
ensureArray(directHumanIntent.allowedResponderSelectors || directHumanIntent.allowed_responders)
|
|
203
|
+
.map((value) => normalizeTelegramMentionUsername(value))
|
|
204
|
+
.filter(Boolean),
|
|
205
|
+
);
|
|
206
|
+
const requiredDelegationTargets = uniqueOrdered(
|
|
207
|
+
String(directHumanIntent.intentMode || directHumanIntent.intent_mode || "").trim() === "delegated_single_lead"
|
|
208
|
+
&& currentBotSelector
|
|
209
|
+
&& normalizedLeadBot
|
|
210
|
+
&& currentBotSelector === normalizedLeadBot
|
|
211
|
+
&& normalizedInitialResponders.length === 1
|
|
212
|
+
&& normalizedInitialResponders.includes(currentBotSelector)
|
|
213
|
+
? normalizedAllowedResponders.filter((value) => value && value !== currentBotSelector)
|
|
214
|
+
: [],
|
|
215
|
+
);
|
|
216
|
+
const requireVisibleDelegationHandoff = requiredDelegationTargets.length > 0;
|
|
180
217
|
const taskMetadata = buildRunnerTaskName(directHumanIntent.intentType, directHumanIntent);
|
|
181
218
|
return {
|
|
182
219
|
task: taskMetadata,
|
|
@@ -262,6 +299,8 @@ export function buildRunnerInputPayload({
|
|
|
262
299
|
human_reply_expectation: String(directHumanIntent.replyExpectation || "").trim(),
|
|
263
300
|
require_actionable_contract: directHumanIntent.requiresActionableContract === true,
|
|
264
301
|
allowed_contract_types: ensureArray(directHumanIntent.allowedContractTypes),
|
|
302
|
+
require_visible_delegation_handoff: requireVisibleDelegationHandoff,
|
|
303
|
+
required_delegation_targets: requiredDelegationTargets,
|
|
265
304
|
},
|
|
266
305
|
agent_context: {
|
|
267
306
|
bot_identity: {
|
|
@@ -294,6 +333,8 @@ export function buildRunnerInputPayload({
|
|
|
294
333
|
require_actionable_contract: directHumanIntent.requiresActionableContract === true,
|
|
295
334
|
allowed_contract_types: ensureArray(directHumanIntent.allowedContractTypes),
|
|
296
335
|
candidate_bot_usernames: candidateBotUsernames,
|
|
336
|
+
require_visible_delegation_handoff: requireVisibleDelegationHandoff,
|
|
337
|
+
required_delegation_targets: requiredDelegationTargets,
|
|
297
338
|
},
|
|
298
339
|
},
|
|
299
340
|
conversation: conversationContext
|
|
@@ -10156,6 +10156,54 @@ export async function runSelftestRunnerScenarios(push, deps) {
|
|
|
10156
10156
|
push("local_bot_prompt_treats_expected_followup_as_contract_authority", false, String(err?.message || err));
|
|
10157
10157
|
}
|
|
10158
10158
|
|
|
10159
|
+
try {
|
|
10160
|
+
const prompt = buildLocalBotPrompt({
|
|
10161
|
+
bot: {
|
|
10162
|
+
name: "RyoAI_bot",
|
|
10163
|
+
username: "RyoAI_bot",
|
|
10164
|
+
role: "monitor",
|
|
10165
|
+
},
|
|
10166
|
+
trigger: {
|
|
10167
|
+
body: "@RyoAI_bot lead the room and bring in @RyoAI2_bot and @RyoAI3_bot now",
|
|
10168
|
+
},
|
|
10169
|
+
response_contract: {
|
|
10170
|
+
is_current_bot_candidate: true,
|
|
10171
|
+
require_actionable_contract: true,
|
|
10172
|
+
allowed_contract_types: ["delegation"],
|
|
10173
|
+
require_visible_delegation_handoff: true,
|
|
10174
|
+
required_delegation_targets: ["ryoai2_bot", "ryoai3_bot"],
|
|
10175
|
+
},
|
|
10176
|
+
conversation: {
|
|
10177
|
+
mode: "public_multi_bot",
|
|
10178
|
+
id: "conversation-lead-visible-handoff",
|
|
10179
|
+
stage: "human_opening",
|
|
10180
|
+
intent_mode: "delegated_single_lead",
|
|
10181
|
+
allow_bot_to_bot: true,
|
|
10182
|
+
lead_bot_username: "ryoai_bot",
|
|
10183
|
+
allowed_responders: ["ryoai_bot", "ryoai2_bot", "ryoai3_bot"],
|
|
10184
|
+
initial_responders: ["ryoai_bot"],
|
|
10185
|
+
},
|
|
10186
|
+
destination: {
|
|
10187
|
+
label: "Main Room",
|
|
10188
|
+
chat_id: "-100123",
|
|
10189
|
+
},
|
|
10190
|
+
project: {
|
|
10191
|
+
id: selftestProjectID,
|
|
10192
|
+
},
|
|
10193
|
+
}, { terse: true });
|
|
10194
|
+
push(
|
|
10195
|
+
"local_bot_prompt_requires_visible_lead_handoff_for_delegated_opening",
|
|
10196
|
+
prompt.includes("requires a visible public handoff")
|
|
10197
|
+
&& prompt.includes("must explicitly mention those exact bots")
|
|
10198
|
+
&& prompt.includes("Do not return only a human-facing clarification or information request without visibly addressing those bots.")
|
|
10199
|
+
&& prompt.includes("The visible reply text must enact the handoff.")
|
|
10200
|
+
&& prompt.includes("Do not stop at asking only the human for more information."),
|
|
10201
|
+
prompt,
|
|
10202
|
+
);
|
|
10203
|
+
} catch (err) {
|
|
10204
|
+
push("local_bot_prompt_requires_visible_lead_handoff_for_delegated_opening", false, String(err?.message || err));
|
|
10205
|
+
}
|
|
10206
|
+
|
|
10159
10207
|
try {
|
|
10160
10208
|
const createCalls = [];
|
|
10161
10209
|
let lastSavedState = null;
|