metheus-governance-mcp-cli 0.2.280 → 0.2.281
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 +62 -41
- package/lib/local-ai-adapters.mjs +77 -70
- package/lib/runner-orchestration-decision-bundle.mjs +33 -25
- package/lib/runner-orchestration-failure.mjs +5 -56
- package/lib/runner-orchestration-intent-contracts.mjs +9 -9
- package/lib/runner-orchestration-selected-record-contract-context.mjs +82 -106
- package/lib/runner-orchestration-selected-record-delivery-context.mjs +45 -25
- package/lib/runner-orchestration-selected-record-reply-outcome.mjs +3 -0
- package/lib/runner-orchestration.mjs +155 -47
- package/lib/runner-recovery.mjs +0 -38
- package/lib/runner-trigger.mjs +26 -2
- package/lib/selftest-runner-scenarios.mjs +594 -185
- package/package.json +1 -1
package/cli.mjs
CHANGED
|
@@ -3919,7 +3919,6 @@ function runnerRequestPreferredExecutionContractType(entryRaw) {
|
|
|
3919
3919
|
return String(
|
|
3920
3920
|
decisionBundle.execution_contract_type
|
|
3921
3921
|
|| entry.execution_contract_type
|
|
3922
|
-
|| entry.followup_execution_contract_type
|
|
3923
3922
|
|| entry.root_execution_contract_type
|
|
3924
3923
|
|| "",
|
|
3925
3924
|
).trim().toLowerCase();
|
|
@@ -3939,14 +3938,12 @@ function runnerRequestPreferredExecutionContractTargets(entryRaw) {
|
|
|
3939
3938
|
? decisionBundle.execution_contract_targets
|
|
3940
3939
|
: ensureArray(entry.execution_contract_targets).length
|
|
3941
3940
|
? entry.execution_contract_targets
|
|
3942
|
-
: ensureArray(entry.
|
|
3943
|
-
? entry.
|
|
3944
|
-
:
|
|
3945
|
-
|
|
3946
|
-
|
|
3947
|
-
|
|
3948
|
-
);
|
|
3949
|
-
}
|
|
3941
|
+
: ensureArray(entry.root_execution_contract_targets).length
|
|
3942
|
+
? entry.root_execution_contract_targets
|
|
3943
|
+
: [],
|
|
3944
|
+
normalizeTelegramMentionUsername,
|
|
3945
|
+
);
|
|
3946
|
+
}
|
|
3950
3947
|
|
|
3951
3948
|
function runnerRequestPreferredNextExpectedResponders(entryRaw) {
|
|
3952
3949
|
const entry = safeObject(entryRaw);
|
|
@@ -3956,14 +3953,12 @@ function runnerRequestPreferredNextExpectedResponders(entryRaw) {
|
|
|
3956
3953
|
? decisionBundle.next_expected_responders
|
|
3957
3954
|
: ensureArray(entry.next_expected_responders).length
|
|
3958
3955
|
? entry.next_expected_responders
|
|
3959
|
-
: ensureArray(entry.
|
|
3960
|
-
? entry.
|
|
3961
|
-
:
|
|
3962
|
-
|
|
3963
|
-
|
|
3964
|
-
|
|
3965
|
-
);
|
|
3966
|
-
}
|
|
3956
|
+
: ensureArray(entry.root_next_expected_responders).length
|
|
3957
|
+
? entry.root_next_expected_responders
|
|
3958
|
+
: [],
|
|
3959
|
+
normalizeTelegramMentionUsername,
|
|
3960
|
+
);
|
|
3961
|
+
}
|
|
3967
3962
|
|
|
3968
3963
|
function runnerRequestPreferredAuthoritySelectedBotUsernames(entryRaw) {
|
|
3969
3964
|
const entry = safeObject(entryRaw);
|
|
@@ -5182,6 +5177,7 @@ async function claimRunnerRequestForHumanComment({
|
|
|
5182
5177
|
ensureArray(authoritativeDecisionBundle.allowed_responders),
|
|
5183
5178
|
normalizeTelegramMentionUsername,
|
|
5184
5179
|
);
|
|
5180
|
+
const hasAuthoritativeConversationDecision = Object.keys(authoritativeDecisionBundle).length > 0;
|
|
5185
5181
|
const { requests: nextRequests, request } = upsertRunnerRequest(stateForClaim, requestKey, {
|
|
5186
5182
|
project_id: String(normalizedRoute?.projectID || "").trim(),
|
|
5187
5183
|
provider: String(normalizedRoute?.provider || "").trim(),
|
|
@@ -5202,26 +5198,32 @@ async function claimRunnerRequestForHumanComment({
|
|
|
5202
5198
|
decision_bundle_validation_status: String(decisionBundleValidation.status || "").trim(),
|
|
5203
5199
|
decision_bundle_validation_reason: String(decisionBundleValidation.reason || "").trim(),
|
|
5204
5200
|
conversation_intent_mode: String(
|
|
5205
|
-
|
|
5201
|
+
(hasAuthoritativeConversationDecision
|
|
5202
|
+
? authoritativeDecisionBundle.conversation_intent_mode
|
|
5203
|
+
: "")
|
|
5206
5204
|
|| normalizedSharedHumanIntent.intentMode
|
|
5207
5205
|
|| existing.conversation_intent_mode
|
|
5208
5206
|
|| authoritySource.conversation_intent_mode
|
|
5209
5207
|
|| "",
|
|
5210
5208
|
).trim().toLowerCase(),
|
|
5211
5209
|
conversation_lead_bot: normalizeTelegramMentionUsername(
|
|
5212
|
-
|
|
5210
|
+
(hasAuthoritativeConversationDecision
|
|
5211
|
+
? authoritativeDecisionBundle.conversation_lead_bot
|
|
5212
|
+
: "")
|
|
5213
5213
|
|| normalizedSharedHumanIntent.leadBotSelector
|
|
5214
5214
|
|| existing.conversation_lead_bot
|
|
5215
5215
|
|| authoritySource.conversation_lead_bot,
|
|
5216
5216
|
),
|
|
5217
5217
|
conversation_summary_bot: normalizeTelegramMentionUsername(
|
|
5218
|
-
|
|
5218
|
+
(hasAuthoritativeConversationDecision
|
|
5219
|
+
? authoritativeDecisionBundle.conversation_summary_bot
|
|
5220
|
+
: "")
|
|
5219
5221
|
|| normalizedSharedHumanIntent.summaryBotSelector
|
|
5220
5222
|
|| existing.conversation_summary_bot
|
|
5221
5223
|
|| authoritySource.conversation_summary_bot,
|
|
5222
5224
|
),
|
|
5223
5225
|
conversation_participants: uniqueOrderedStrings(
|
|
5224
|
-
decisionConversationParticipants.length
|
|
5226
|
+
hasAuthoritativeConversationDecision && decisionConversationParticipants.length
|
|
5225
5227
|
? decisionConversationParticipants
|
|
5226
5228
|
: ensureArray(normalizedSharedHumanIntent.participantSelectors).length
|
|
5227
5229
|
? normalizedSharedHumanIntent.participantSelectors
|
|
@@ -5233,7 +5235,7 @@ async function claimRunnerRequestForHumanComment({
|
|
|
5233
5235
|
normalizeTelegramMentionUsername,
|
|
5234
5236
|
),
|
|
5235
5237
|
conversation_initial_responders: uniqueOrderedStrings(
|
|
5236
|
-
decisionInitialResponders.length
|
|
5238
|
+
hasAuthoritativeConversationDecision && decisionInitialResponders.length
|
|
5237
5239
|
? decisionInitialResponders
|
|
5238
5240
|
: ensureArray(normalizedSharedHumanIntent.initialResponderSelectors).length
|
|
5239
5241
|
? normalizedSharedHumanIntent.initialResponderSelectors
|
|
@@ -5245,7 +5247,7 @@ async function claimRunnerRequestForHumanComment({
|
|
|
5245
5247
|
normalizeTelegramMentionUsername,
|
|
5246
5248
|
),
|
|
5247
5249
|
conversation_allowed_responders: uniqueOrderedStrings(
|
|
5248
|
-
decisionAllowedResponders.length
|
|
5250
|
+
hasAuthoritativeConversationDecision && decisionAllowedResponders.length
|
|
5249
5251
|
? decisionAllowedResponders
|
|
5250
5252
|
: ensureArray(normalizedSharedHumanIntent.allowedResponderSelectors).length
|
|
5251
5253
|
? normalizedSharedHumanIntent.allowedResponderSelectors
|
|
@@ -5254,30 +5256,38 @@ async function claimRunnerRequestForHumanComment({
|
|
|
5254
5256
|
: ensureArray(authoritySource.conversation_allowed_responders).length
|
|
5255
5257
|
? authoritySource.conversation_allowed_responders
|
|
5256
5258
|
: [],
|
|
5257
|
-
normalizeTelegramMentionUsername,
|
|
5259
|
+
normalizeTelegramMentionUsername,
|
|
5258
5260
|
),
|
|
5259
|
-
conversation_allow_bot_to_bot:
|
|
5261
|
+
conversation_allow_bot_to_bot: (hasAuthoritativeConversationDecision
|
|
5262
|
+
? authoritativeDecisionBundle.allow_bot_to_bot === true
|
|
5263
|
+
: false)
|
|
5260
5264
|
|| normalizedSharedHumanIntent.allowBotToBot === true
|
|
5261
5265
|
|| existing.conversation_allow_bot_to_bot === true
|
|
5262
5266
|
|| authoritySource.conversation_allow_bot_to_bot === true,
|
|
5263
5267
|
conversation_reply_expectation: String(
|
|
5264
|
-
|
|
5268
|
+
(hasAuthoritativeConversationDecision
|
|
5269
|
+
? authoritativeDecisionBundle.conversation_reply_expectation
|
|
5270
|
+
: "")
|
|
5265
5271
|
|| normalizedSharedHumanIntent.replyExpectation
|
|
5266
5272
|
|| existing.conversation_reply_expectation
|
|
5267
5273
|
|| authoritySource.conversation_reply_expectation
|
|
5268
5274
|
|| "",
|
|
5269
5275
|
).trim().toLowerCase(),
|
|
5270
5276
|
execution_contract_type: String(
|
|
5271
|
-
|
|
5277
|
+
(hasAuthoritativeConversationDecision
|
|
5278
|
+
? authoritativeDecisionBundle.execution_contract_type
|
|
5279
|
+
: "")
|
|
5272
5280
|
|| runnerRequestPreferredExecutionContractType(existing)
|
|
5273
5281
|
|| runnerRequestPreferredExecutionContractType(authoritySource)
|
|
5274
5282
|
|| "",
|
|
5275
5283
|
).trim().toLowerCase(),
|
|
5276
|
-
execution_contract_actionable:
|
|
5284
|
+
execution_contract_actionable: (hasAuthoritativeConversationDecision
|
|
5285
|
+
? authoritativeDecisionBundle.execution_contract_actionable === true
|
|
5286
|
+
: false)
|
|
5277
5287
|
|| runnerRequestPreferredExecutionContractActionable(existing)
|
|
5278
5288
|
|| runnerRequestPreferredExecutionContractActionable(authoritySource),
|
|
5279
5289
|
execution_contract_targets: uniqueOrderedStrings(
|
|
5280
|
-
ensureArray(authoritativeDecisionBundle.execution_contract_targets).length
|
|
5290
|
+
hasAuthoritativeConversationDecision && ensureArray(authoritativeDecisionBundle.execution_contract_targets).length
|
|
5281
5291
|
? authoritativeDecisionBundle.execution_contract_targets
|
|
5282
5292
|
: runnerRequestPreferredExecutionContractTargets(existing).length
|
|
5283
5293
|
? runnerRequestPreferredExecutionContractTargets(existing)
|
|
@@ -5285,7 +5295,7 @@ async function claimRunnerRequestForHumanComment({
|
|
|
5285
5295
|
normalizeTelegramMentionUsername,
|
|
5286
5296
|
),
|
|
5287
5297
|
next_expected_responders: uniqueOrderedStrings(
|
|
5288
|
-
ensureArray(authoritativeDecisionBundle.next_expected_responders).length
|
|
5298
|
+
hasAuthoritativeConversationDecision && ensureArray(authoritativeDecisionBundle.next_expected_responders).length
|
|
5289
5299
|
? authoritativeDecisionBundle.next_expected_responders
|
|
5290
5300
|
: runnerRequestPreferredNextExpectedResponders(existing).length
|
|
5291
5301
|
? runnerRequestPreferredNextExpectedResponders(existing)
|
|
@@ -6428,23 +6438,33 @@ function markRunnerRequestLifecycle({
|
|
|
6428
6438
|
],
|
|
6429
6439
|
normalizeTelegramMentionUsername,
|
|
6430
6440
|
).filter((selector) => selector && selector !== normalizedCurrentBotSelector);
|
|
6441
|
+
const nextExecutionContractType = String(
|
|
6442
|
+
authoritativeDecisionBundle.execution_contract_type
|
|
6443
|
+
|| executionContractType
|
|
6444
|
+
|| existing.execution_contract_type
|
|
6445
|
+
|| "",
|
|
6446
|
+
).trim().toLowerCase();
|
|
6447
|
+
const normalizedOutcome = String(outcome || "").trim().toLowerCase();
|
|
6431
6448
|
const shouldRemainRunningAfterReply = authoritativeDecisionBundle.should_close_after_reply === true
|
|
6432
6449
|
? false
|
|
6433
6450
|
: authoritativeDecisionBundle.should_close_after_reply === false
|
|
6434
6451
|
? true
|
|
6435
6452
|
: continuationSelectors.length > 0;
|
|
6453
|
+
const shouldRemainRunningAfterSkip = normalizedOutcome === "skipped"
|
|
6454
|
+
&& parsedKind === "bot_reply"
|
|
6455
|
+
&& authoritativeDecisionBundle.should_close_after_reply !== true
|
|
6456
|
+
&& (
|
|
6457
|
+
nextExecutionContractType === "delegation"
|
|
6458
|
+
|| rootEffectiveExecutionContractTargets.length > 0
|
|
6459
|
+
|| rootEffectiveNextExpectedResponders.length > 0
|
|
6460
|
+
|| continuationSelectors.length > 0
|
|
6461
|
+
);
|
|
6436
6462
|
const nextConversationIntentMode = String(
|
|
6437
6463
|
authoritativeDecisionBundle.conversation_intent_mode
|
|
6438
6464
|
|| conversationIntentMode
|
|
6439
6465
|
|| existing.conversation_intent_mode
|
|
6440
6466
|
|| "",
|
|
6441
6467
|
).trim().toLowerCase();
|
|
6442
|
-
const nextExecutionContractType = String(
|
|
6443
|
-
authoritativeDecisionBundle.execution_contract_type
|
|
6444
|
-
|| executionContractType
|
|
6445
|
-
|| existing.execution_contract_type
|
|
6446
|
-
|| "",
|
|
6447
|
-
).trim().toLowerCase();
|
|
6448
6468
|
const nextNormalizedIntent = (() => {
|
|
6449
6469
|
const explicitIntent = String(
|
|
6450
6470
|
authoritativeDecisionBundle.normalized_intent || normalizedIntent || "",
|
|
@@ -6454,10 +6474,9 @@ function markRunnerRequestLifecycle({
|
|
|
6454
6474
|
}
|
|
6455
6475
|
if (nextConversationIntentMode && !nextExecutionContractType) {
|
|
6456
6476
|
return "";
|
|
6457
|
-
}
|
|
6458
|
-
return String(existing.normalized_intent || "").trim().toLowerCase();
|
|
6459
|
-
})();
|
|
6460
|
-
const normalizedOutcome = String(outcome || "").trim().toLowerCase();
|
|
6477
|
+
}
|
|
6478
|
+
return String(existing.normalized_intent || "").trim().toLowerCase();
|
|
6479
|
+
})();
|
|
6461
6480
|
const nextStatus = (() => {
|
|
6462
6481
|
if (normalizedOutcome === "claimed") return "claimed";
|
|
6463
6482
|
if (normalizedOutcome === "running") return "running";
|
|
@@ -6467,9 +6486,11 @@ function markRunnerRequestLifecycle({
|
|
|
6467
6486
|
}
|
|
6468
6487
|
if (normalizedOutcome === "loop_closed") return "loop_closed";
|
|
6469
6488
|
if (normalizedOutcome === "expired") return "expired";
|
|
6489
|
+
if (normalizedOutcome === "skipped") {
|
|
6490
|
+
return shouldRemainRunningAfterSkip ? "running" : "closed";
|
|
6491
|
+
}
|
|
6470
6492
|
if (
|
|
6471
6493
|
normalizedOutcome === "error"
|
|
6472
|
-
|| normalizedOutcome === "skipped"
|
|
6473
6494
|
|| normalizedOutcome === "closed"
|
|
6474
6495
|
|| normalizedOutcome === "execution_failed"
|
|
6475
6496
|
|| normalizedOutcome === "policy_violation"
|
|
@@ -931,62 +931,57 @@ function normalizeCliOutput(rawText) {
|
|
|
931
931
|
throw new Error("AI client returned empty output");
|
|
932
932
|
}
|
|
933
933
|
const parsed = tryJsonParse(text) || tryParseEmbeddedJsonObject(text);
|
|
934
|
-
if (parsed
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
replyToMessageID: intFromRawAllowZero(parsed.reply_to_message_id ?? parsed.replyToMessageID, 0),
|
|
954
|
-
contract: null,
|
|
955
|
-
artifacts: [],
|
|
956
|
-
ctxpackFiles: [],
|
|
957
|
-
workItems: [],
|
|
958
|
-
raw: parsed,
|
|
959
|
-
};
|
|
960
|
-
}
|
|
961
|
-
const reply = firstNonEmptyString([parsed.reply, parsed.text, parsed.content, parsed.message]);
|
|
962
|
-
if (!reply) {
|
|
963
|
-
throw new Error("AI client returned JSON without reply text");
|
|
964
|
-
}
|
|
965
|
-
const contract = normalizeExecutionContract(
|
|
966
|
-
parsed.contract
|
|
967
|
-
?? parsed.execution_contract
|
|
968
|
-
?? parsed.executionContract,
|
|
969
|
-
);
|
|
934
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
935
|
+
throw new Error("AI client did not return a JSON object");
|
|
936
|
+
}
|
|
937
|
+
const decisionBundle = parsed.decision_bundle ?? parsed.decisionBundle ?? null;
|
|
938
|
+
if (parsed.skip === true) {
|
|
939
|
+
return {
|
|
940
|
+
skip: true,
|
|
941
|
+
reason: firstNonEmptyString([parsed.reason, parsed.message, parsed.detail]),
|
|
942
|
+
decisionBundle,
|
|
943
|
+
raw: parsed,
|
|
944
|
+
};
|
|
945
|
+
}
|
|
946
|
+
const clarifyText = firstNonEmptyString([
|
|
947
|
+
typeof parsed.clarify === "string" ? parsed.clarify : "",
|
|
948
|
+
typeof parsed.clarification === "string" ? parsed.clarification : "",
|
|
949
|
+
parsed.clarify === true ? parsed.reply : "",
|
|
950
|
+
parsed.clarify === true ? parsed.message : "",
|
|
951
|
+
]);
|
|
952
|
+
if (clarifyText) {
|
|
970
953
|
return {
|
|
971
954
|
skip: false,
|
|
972
|
-
|
|
955
|
+
clarify: true,
|
|
956
|
+
reply: clarifyText,
|
|
973
957
|
replyToMessageID: intFromRawAllowZero(parsed.reply_to_message_id ?? parsed.replyToMessageID, 0),
|
|
974
|
-
contract,
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
958
|
+
contract: null,
|
|
959
|
+
decisionBundle,
|
|
960
|
+
artifacts: [],
|
|
961
|
+
ctxpackFiles: [],
|
|
962
|
+
workItems: [],
|
|
978
963
|
raw: parsed,
|
|
979
964
|
};
|
|
980
965
|
}
|
|
966
|
+
const reply = firstNonEmptyString([parsed.reply, parsed.text, parsed.content, parsed.message]);
|
|
967
|
+
if (!reply) {
|
|
968
|
+
throw new Error("AI client returned JSON without reply text");
|
|
969
|
+
}
|
|
970
|
+
const contract = normalizeExecutionContract(
|
|
971
|
+
parsed.contract
|
|
972
|
+
?? parsed.execution_contract
|
|
973
|
+
?? parsed.executionContract,
|
|
974
|
+
);
|
|
981
975
|
return {
|
|
982
976
|
skip: false,
|
|
983
|
-
reply
|
|
984
|
-
replyToMessageID: 0,
|
|
985
|
-
contract
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
977
|
+
reply,
|
|
978
|
+
replyToMessageID: intFromRawAllowZero(parsed.reply_to_message_id ?? parsed.replyToMessageID, 0),
|
|
979
|
+
contract,
|
|
980
|
+
decisionBundle,
|
|
981
|
+
artifacts: normalizeExecutionArtifacts(parsed.artifacts ?? parsed.files ?? parsed.outputs),
|
|
982
|
+
ctxpackFiles: normalizeExecutionCtxpackFiles(parsed.ctxpack_files ?? parsed.ctxpackFiles),
|
|
983
|
+
workItems: normalizeExecutionWorkItems(parsed.work_items ?? parsed.workItems),
|
|
984
|
+
raw: parsed,
|
|
990
985
|
};
|
|
991
986
|
}
|
|
992
987
|
|
|
@@ -1629,6 +1624,8 @@ function runGeminiAdapter({ promptText, workspaceDir, model, permissionMode, rea
|
|
|
1629
1624
|
|
|
1630
1625
|
function runSampleAdapter(payload) {
|
|
1631
1626
|
const trigger = payload && typeof payload === "object" ? payload.trigger || {} : {};
|
|
1627
|
+
const responseContract = payload && typeof payload === "object" ? safeObject(payload.response_contract) : {};
|
|
1628
|
+
const selfBotUsername = String(responseContract.self_bot_username || "").trim().replace(/^@+/, "").toLowerCase();
|
|
1632
1629
|
const body = String(trigger.body || "").trim();
|
|
1633
1630
|
if (!body) {
|
|
1634
1631
|
return {
|
|
@@ -1641,6 +1638,26 @@ function runSampleAdapter(payload) {
|
|
|
1641
1638
|
skip: false,
|
|
1642
1639
|
reply: `Acknowledged: ${body}`,
|
|
1643
1640
|
replyToMessageID: 0,
|
|
1641
|
+
decisionBundle: {
|
|
1642
|
+
schema_version: "runner_conversation_decision.v1",
|
|
1643
|
+
decision_type: "sample_reply",
|
|
1644
|
+
normalized_intent: "",
|
|
1645
|
+
conversation_intent_mode: "single_bot",
|
|
1646
|
+
participants: selfBotUsername ? [selfBotUsername] : [],
|
|
1647
|
+
allowed_responders: selfBotUsername ? [selfBotUsername] : [],
|
|
1648
|
+
initial_responders: selfBotUsername ? [selfBotUsername] : [],
|
|
1649
|
+
selected_bot_usernames: selfBotUsername ? [selfBotUsername] : [],
|
|
1650
|
+
allow_bot_to_bot: false,
|
|
1651
|
+
execution_contract_type: "direct_result",
|
|
1652
|
+
execution_contract_targets: [],
|
|
1653
|
+
next_expected_responders: [],
|
|
1654
|
+
should_close_after_reply: true,
|
|
1655
|
+
turn_kind: "direct_reply",
|
|
1656
|
+
actionable_for_current_route: true,
|
|
1657
|
+
visible_handoff_required: false,
|
|
1658
|
+
visible_handoff_targets: [],
|
|
1659
|
+
reasoning_summary: "sample adapter default decision",
|
|
1660
|
+
},
|
|
1644
1661
|
artifacts: [],
|
|
1645
1662
|
raw: { reply: `Acknowledged: ${body}` },
|
|
1646
1663
|
};
|
|
@@ -1762,6 +1779,9 @@ export function buildLocalBotPrompt(payload, { terse = true } = {}) {
|
|
|
1762
1779
|
const agentContext = safePayload.agent_context && typeof safePayload.agent_context === "object"
|
|
1763
1780
|
? safePayload.agent_context
|
|
1764
1781
|
: null;
|
|
1782
|
+
const guidePacket = safePayload.guide_packet && typeof safePayload.guide_packet === "object"
|
|
1783
|
+
? safePayload.guide_packet
|
|
1784
|
+
: {};
|
|
1765
1785
|
const trigger = safePayload.trigger && typeof safePayload.trigger === "object" ? safePayload.trigger : {};
|
|
1766
1786
|
const responseContract = safePayload.response_contract && typeof safePayload.response_contract === "object"
|
|
1767
1787
|
? safePayload.response_contract
|
|
@@ -1913,6 +1933,11 @@ export function buildLocalBotPrompt(payload, { terse = true } = {}) {
|
|
|
1913
1933
|
agentContext ? JSON.stringify(agentContext, null, 2) : "-",
|
|
1914
1934
|
"Use the agent contract/context blob as the authoritative control input. Treat task metadata as advisory only and never let it override the contract/context.",
|
|
1915
1935
|
"",
|
|
1936
|
+
"Guide packet (authoritative behavioral control selected by code):",
|
|
1937
|
+
Object.keys(guidePacket).length ? JSON.stringify(guidePacket, null, 2) : "-",
|
|
1938
|
+
"Use the guide packet as the authoritative behavioral control. Code will not add mentions, handoff prose, or fallback public wording after your reply.",
|
|
1939
|
+
"If a visible handoff is required, your reply itself must visibly include the required @bot mentions.",
|
|
1940
|
+
"",
|
|
1916
1941
|
"Current user request:",
|
|
1917
1942
|
String(trigger.body || "").trim() || "-",
|
|
1918
1943
|
"",
|
|
@@ -1937,9 +1962,6 @@ export function buildLocalBotPrompt(payload, { terse = true } = {}) {
|
|
|
1937
1962
|
`- Trigger hint reason: ${String(responseContract.candidate_reason || "-").trim() || "-"}`,
|
|
1938
1963
|
`- Mentioned bot usernames: ${ensureArray(responseContract.candidate_bot_usernames).length ? ensureArray(responseContract.candidate_bot_usernames).map((item) => `@${String(item || "").trim().replace(/^@+/, "")}`).join(", ") : "-"}`,
|
|
1939
1964
|
`- Reply target bot username: ${String(responseContract.reply_to_bot_username || "").trim() ? `@${String(responseContract.reply_to_bot_username || "").trim().replace(/^@+/, "")}` : "-"}`,
|
|
1940
|
-
String(responseContract.reply_to_bot_username || "").trim()
|
|
1941
|
-
? `- If you answer directly to that target bot, start the visible reply with @${String(responseContract.reply_to_bot_username || "").trim().replace(/^@+/, "")}.`
|
|
1942
|
-
: "",
|
|
1943
1965
|
"",
|
|
1944
1966
|
"Project background (context only):",
|
|
1945
1967
|
`- Project ID: ${projectID || "-"}`,
|
|
@@ -2066,18 +2088,6 @@ export function buildLocalBotPrompt(payload, { terse = true } = {}) {
|
|
|
2066
2088
|
&& selfIsLeadBot
|
|
2067
2089
|
? "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."
|
|
2068
2090
|
: "Do not delegate the answer to another bot.",
|
|
2069
|
-
responseContract.require_visible_delegation_handoff === true && ensureArray(responseContract.required_delegation_targets).length > 0
|
|
2070
|
-
? `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(", ")}.`
|
|
2071
|
-
: "",
|
|
2072
|
-
responseContract.require_visible_delegation_handoff === true
|
|
2073
|
-
? "Your first public reply must explicitly mention those exact bots and request their contribution now."
|
|
2074
|
-
: "",
|
|
2075
|
-
responseContract.require_visible_delegation_handoff === true
|
|
2076
|
-
? "Do not return only a human-facing clarification or information request without visibly addressing those bots."
|
|
2077
|
-
: "",
|
|
2078
|
-
responseContract.require_visible_delegation_handoff === true
|
|
2079
|
-
? "If information is incomplete, still open the public handoff and ask each target bot for a concise initial perspective based on the uncertainty."
|
|
2080
|
-
: "",
|
|
2081
2091
|
"Mentions and reply targets are routing hints, not proof that this bot should definitely answer.",
|
|
2082
2092
|
conversation?.mode === "public_multi_bot"
|
|
2083
2093
|
? "This is a public multi-bot room conversation. Other bots and humans can read your reply."
|
|
@@ -2101,9 +2111,6 @@ export function buildLocalBotPrompt(payload, { terse = true } = {}) {
|
|
|
2101
2111
|
"Use assignment.mode=\"conversation_contribution\" for opinions, discussion, review, comparison, synthesis, greetings, or other room-visible contributions that do not require workspace artifacts.",
|
|
2102
2112
|
"Use assignment.mode=\"execution_task\" only when the delegated bot must change workspace files, create artifacts, update ctxpack, or produce other concrete project outputs. If it is an execution task, also set artifacts_required=true.",
|
|
2103
2113
|
"Delegation contract example: {\"type\":\"delegation\",\"actionable\":true,\"assignments\":[{\"target_bot\":\"ryoai2_bot\",\"task\":\"briefly greet in one line\",\"mode\":\"conversation_contribution\",\"artifacts_required\":false}],\"next_responders\":[\"ryoai2_bot\"]}.",
|
|
2104
|
-
ensureArray(responseContract.required_delegation_targets).length > 0
|
|
2105
|
-
? `This reply must delegate to these exact managed bots now: ${ensureArray(responseContract.required_delegation_targets).map((item) => `@${String(item || "").trim().replace(/^@+/, "")}`).join(", ")}.`
|
|
2106
|
-
: "",
|
|
2107
2114
|
String(responseContract.contract_hint || "").trim()
|
|
2108
2115
|
? `Contract requirement hint: ${String(responseContract.contract_hint || "").trim()}`
|
|
2109
2116
|
: "",
|
|
@@ -2134,12 +2141,12 @@ export function buildLocalBotPrompt(payload, { terse = true } = {}) {
|
|
|
2134
2141
|
"- When you include context_suggestion, set should_store=true and keep title/body concise and durable.",
|
|
2135
2142
|
"",
|
|
2136
2143
|
isInternalExecutionStep
|
|
2137
|
-
? "Return JSON only in one line: {\"reply\":\"what was completed in this step\",\"artifacts\":[{\"path\":\"relative/or/absolute/path\",\"kind\":\"plan|code|doc|spec|test\",\"operation\":\"create|update|delete\"}],\"ctxpack_files\":[{\"path\":\"relative/path.md\",\"content\":\"full document text\",\"doc_type\":\"guide|readme|agenda|rule|architecture|manifest\",\"operation\":\"create|update|delete\"}],\"work_items\":[{\"title\":\"short atomic task\",\"description\":\"useful implementation detail\"}],\"contract\":{\"type\":\"direct_result|summary_request|final_summary\",\"actionable\":true,\"summary_bot\":\"username\",\"next_responders\":[\"username\"]}}. Use ctxpack_files when ctxpack-backed guidance/instruction files must be authored. If execution_step.ctxpack_update_required is true, ctxpack_files must not be empty. Use artifacts: [] only if this step truly changes no project files, and use work_items: [] only if this step truly creates no governance tasks."
|
|
2144
|
+
? "Return strict JSON only in one line: {\"reply\":\"what was completed in this step\",\"decision_bundle\":{\"schema_version\":\"runner_conversation_decision.v1\",\"decision_type\":\"reply_outcome\",\"conversation_intent_mode\":\"single_bot|delegated_single_lead|multi_bot_collab|multi_bot_direct\",\"allowed_responders\":[\"username\"],\"initial_responders\":[\"username\"],\"selected_bot_usernames\":[\"username\"],\"allow_bot_to_bot\":true|false,\"execution_contract_type\":\"direct_result|delegation|summary_request|final_summary|\",\"execution_contract_targets\":[\"username\"],\"next_expected_responders\":[\"username\"],\"should_close_after_reply\":true|false,\"turn_kind\":\"direct_reply|delegation_kickoff|delegated_followup|summary_turn|execution_step\",\"actionable_for_current_route\":true|false,\"visible_handoff_required\":true|false,\"visible_handoff_targets\":[\"username\"],\"reasoning_summary\":\"short explanation\"},\"artifacts\":[{\"path\":\"relative/or/absolute/path\",\"kind\":\"plan|code|doc|spec|test\",\"operation\":\"create|update|delete\"}],\"ctxpack_files\":[{\"path\":\"relative/path.md\",\"content\":\"full document text\",\"doc_type\":\"guide|readme|agenda|rule|architecture|manifest\",\"operation\":\"create|update|delete\"}],\"work_items\":[{\"title\":\"short atomic task\",\"description\":\"useful implementation detail\"}],\"contract\":{\"type\":\"direct_result|summary_request|final_summary\",\"actionable\":true,\"summary_bot\":\"username\",\"next_responders\":[\"username\"]}}. Use ctxpack_files when ctxpack-backed guidance/instruction files must be authored. If execution_step.ctxpack_update_required is true, ctxpack_files must not be empty. Use artifacts: [] only if this step truly changes no project files, and use work_items: [] only if this step truly creates no governance tasks."
|
|
2138
2145
|
: responseContract.is_current_bot_candidate === true
|
|
2139
|
-
? "Return JSON only in one line: {\"reply\":\"...\",\"artifacts\":[],\"ctxpack_files\":[],\"work_items\":[]} or {\"clarify\":\"...\"} or {\"skip\":true,\"reason\":\"...\"} or {\"reply\":\"...\",\"artifacts\":[],\"ctxpack_files\":[],\"work_items\":[],\"contract\":{\"type\":\"direct_result|delegation|summary_request|final_summary\",\"actionable\":true,\"assignments\":[{\"target_bot\":\"username\",\"task\":\"...\",\"mode\":\"conversation_contribution|execution_task\",\"artifacts_required\":true|false}],\"summary_bot\":\"username\",\"next_responders\":[\"username\"]},\"context_suggestion\":{\"should_store\":true,\"title\":\"...\",\"body\":\"...\",\"category\":\"bot_role|operating_rule|project_fact|current_focus|risk|general\",\"importance\":\"low|normal|high|critical\"}}."
|
|
2146
|
+
? "Return strict JSON only in one line: {\"reply\":\"...\",\"decision_bundle\":{\"schema_version\":\"runner_conversation_decision.v1\",\"decision_type\":\"reply_outcome\",\"conversation_intent_mode\":\"single_bot|delegated_single_lead|multi_bot_collab|multi_bot_direct\",\"allowed_responders\":[\"username\"],\"initial_responders\":[\"username\"],\"selected_bot_usernames\":[\"username\"],\"allow_bot_to_bot\":true|false,\"execution_contract_type\":\"direct_result|delegation|summary_request|final_summary|\",\"execution_contract_targets\":[\"username\"],\"next_expected_responders\":[\"username\"],\"should_close_after_reply\":true|false,\"turn_kind\":\"direct_reply|delegation_kickoff|delegated_followup|summary_turn|clarify_turn\",\"actionable_for_current_route\":true|false,\"visible_handoff_required\":true|false,\"visible_handoff_targets\":[\"username\"],\"reasoning_summary\":\"short explanation\"},\"artifacts\":[],\"ctxpack_files\":[],\"work_items\":[]} or {\"clarify\":\"...\",\"decision_bundle\":{\"schema_version\":\"runner_conversation_decision.v1\",\"decision_type\":\"clarify\",\"conversation_intent_mode\":\"single_bot|delegated_single_lead|multi_bot_collab|multi_bot_direct\",\"allowed_responders\":[\"username\"],\"initial_responders\":[\"username\"],\"selected_bot_usernames\":[\"username\"],\"allow_bot_to_bot\":true|false,\"execution_contract_type\":\"|delegation|summary_request|direct_result|final_summary\",\"execution_contract_targets\":[\"username\"],\"next_expected_responders\":[\"username\"],\"should_close_after_reply\":false,\"turn_kind\":\"clarify_turn\",\"actionable_for_current_route\":true|false,\"visible_handoff_required\":true|false,\"visible_handoff_targets\":[\"username\"],\"reasoning_summary\":\"short explanation\"}} or {\"skip\":true,\"reason\":\"...\",\"decision_bundle\":{\"schema_version\":\"runner_conversation_decision.v1\",\"decision_type\":\"skip\",\"conversation_intent_mode\":\"single_bot|delegated_single_lead|multi_bot_collab|multi_bot_direct\",\"allowed_responders\":[\"username\"],\"initial_responders\":[\"username\"],\"selected_bot_usernames\":[\"username\"],\"allow_bot_to_bot\":true|false,\"execution_contract_type\":\"|delegation|summary_request|direct_result|final_summary\",\"execution_contract_targets\":[\"username\"],\"next_expected_responders\":[\"username\"],\"should_close_after_reply\":false,\"turn_kind\":\"skip\",\"actionable_for_current_route\":false,\"visible_handoff_required\":true|false,\"visible_handoff_targets\":[\"username\"],\"reasoning_summary\":\"short explanation\"}} or {\"reply\":\"...\",\"decision_bundle\":{\"schema_version\":\"runner_conversation_decision.v1\",\"decision_type\":\"reply_outcome\",\"conversation_intent_mode\":\"single_bot|delegated_single_lead|multi_bot_collab|multi_bot_direct\",\"allowed_responders\":[\"username\"],\"initial_responders\":[\"username\"],\"selected_bot_usernames\":[\"username\"],\"allow_bot_to_bot\":true|false,\"execution_contract_type\":\"direct_result|delegation|summary_request|final_summary|\",\"execution_contract_targets\":[\"username\"],\"next_expected_responders\":[\"username\"],\"should_close_after_reply\":true|false,\"turn_kind\":\"direct_reply|delegation_kickoff|delegated_followup|summary_turn\",\"actionable_for_current_route\":true|false,\"visible_handoff_required\":true|false,\"visible_handoff_targets\":[\"username\"],\"reasoning_summary\":\"short explanation\"},\"artifacts\":[],\"ctxpack_files\":[],\"work_items\":[],\"contract\":{\"type\":\"direct_result|delegation|summary_request|final_summary\",\"actionable\":true,\"assignments\":[{\"target_bot\":\"username\",\"task\":\"...\",\"mode\":\"conversation_contribution|execution_task\",\"artifacts_required\":true|false}],\"summary_bot\":\"username\",\"next_responders\":[\"username\"]},\"context_suggestion\":{\"should_store\":true,\"title\":\"...\",\"body\":\"...\",\"category\":\"bot_role|operating_rule|project_fact|current_focus|risk|general\",\"importance\":\"low|normal|high|critical\"}}."
|
|
2140
2147
|
: terse
|
|
2141
|
-
? "Return JSON only in one line: {\"reply\":\"...\",\"artifacts\":[],\"ctxpack_files\":[],\"work_items\":[],\"context_suggestion\":{\"should_store\":true,\"title\":\"...\",\"body\":\"...\",\"category\":\"bot_role|operating_rule|project_fact|current_focus|risk|general\",\"importance\":\"low|normal|high|critical\"}} or {\"clarify\":\"...\"} or {\"skip\":true,\"reason\":\"...\"}."
|
|
2142
|
-
: "Return JSON only
|
|
2148
|
+
? "Return strict JSON only in one line: {\"reply\":\"...\",\"decision_bundle\":{\"schema_version\":\"runner_conversation_decision.v1\",\"decision_type\":\"reply_outcome\",\"conversation_intent_mode\":\"single_bot|delegated_single_lead|multi_bot_collab|multi_bot_direct\",\"allowed_responders\":[\"username\"],\"initial_responders\":[\"username\"],\"selected_bot_usernames\":[\"username\"],\"allow_bot_to_bot\":true|false,\"execution_contract_type\":\"|delegation|summary_request|direct_result|final_summary\",\"execution_contract_targets\":[\"username\"],\"next_expected_responders\":[\"username\"],\"should_close_after_reply\":true|false,\"turn_kind\":\"direct_reply|delegation_kickoff|delegated_followup|summary_turn\",\"actionable_for_current_route\":true|false,\"visible_handoff_required\":true|false,\"visible_handoff_targets\":[\"username\"],\"reasoning_summary\":\"short explanation\"},\"artifacts\":[],\"ctxpack_files\":[],\"work_items\":[],\"context_suggestion\":{\"should_store\":true,\"title\":\"...\",\"body\":\"...\",\"category\":\"bot_role|operating_rule|project_fact|current_focus|risk|general\",\"importance\":\"low|normal|high|critical\"}} or {\"clarify\":\"...\",\"decision_bundle\":{\"schema_version\":\"runner_conversation_decision.v1\",\"decision_type\":\"clarify\",\"conversation_intent_mode\":\"single_bot|delegated_single_lead|multi_bot_collab|multi_bot_direct\",\"allowed_responders\":[\"username\"],\"initial_responders\":[\"username\"],\"selected_bot_usernames\":[\"username\"],\"allow_bot_to_bot\":true|false,\"execution_contract_type\":\"|delegation|summary_request|direct_result|final_summary\",\"execution_contract_targets\":[\"username\"],\"next_expected_responders\":[\"username\"],\"should_close_after_reply\":false,\"turn_kind\":\"clarify_turn\",\"actionable_for_current_route\":true|false,\"visible_handoff_required\":true|false,\"visible_handoff_targets\":[\"username\"],\"reasoning_summary\":\"short explanation\"}} or {\"skip\":true,\"reason\":\"...\",\"decision_bundle\":{\"schema_version\":\"runner_conversation_decision.v1\",\"decision_type\":\"skip\",\"conversation_intent_mode\":\"single_bot|delegated_single_lead|multi_bot_collab|multi_bot_direct\",\"allowed_responders\":[\"username\"],\"initial_responders\":[\"username\"],\"selected_bot_usernames\":[\"username\"],\"allow_bot_to_bot\":true|false,\"execution_contract_type\":\"|delegation|summary_request|direct_result|final_summary\",\"execution_contract_targets\":[\"username\"],\"next_expected_responders\":[\"username\"],\"should_close_after_reply\":false,\"turn_kind\":\"skip\",\"actionable_for_current_route\":false,\"visible_handoff_required\":true|false,\"visible_handoff_targets\":[\"username\"],\"reasoning_summary\":\"short explanation\"}}."
|
|
2149
|
+
: "Return strict JSON only. Every non-skip response must include a decision_bundle. Do not rely on code to rewrite your reply or add visible handoff mentions after generation.",
|
|
2143
2150
|
);
|
|
2144
2151
|
if (conversation?.mode === "public_multi_bot") {
|
|
2145
2152
|
const roleGuidance = {
|
|
@@ -37,6 +37,13 @@ function firstNonBlankString(values) {
|
|
|
37
37
|
return "";
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
+
function normalizeBoolean(value, fallback = false) {
|
|
41
|
+
if (typeof value === "boolean") {
|
|
42
|
+
return value;
|
|
43
|
+
}
|
|
44
|
+
return fallback;
|
|
45
|
+
}
|
|
46
|
+
|
|
40
47
|
function normalizeContractAssignments(assignmentsRaw) {
|
|
41
48
|
return uniqueOrderedSelectors(
|
|
42
49
|
ensureArray(assignmentsRaw).map((item) => {
|
|
@@ -48,31 +55,14 @@ function normalizeContractAssignments(assignmentsRaw) {
|
|
|
48
55
|
|
|
49
56
|
export function normalizeRunnerConversationDecisionBundle(bundleRaw) {
|
|
50
57
|
const bundle = safeObject(bundleRaw);
|
|
51
|
-
const executionContractTargets = uniqueOrderedSelectors(
|
|
52
|
-
|
|
53
|
-
...normalizeContractAssignments(safeObject(bundle.execution_contract).assignments),
|
|
54
|
-
]);
|
|
55
|
-
const nextExpectedResponders = uniqueOrderedSelectors([
|
|
56
|
-
...ensureArray(bundle.next_expected_responders),
|
|
57
|
-
...ensureArray(safeObject(bundle.execution_contract).nextResponders),
|
|
58
|
-
...ensureArray(safeObject(bundle.execution_contract).next_responders),
|
|
59
|
-
...ensureArray(safeObject(bundle.execution_contract).responders),
|
|
60
|
-
...(
|
|
61
|
-
executionContractTargets.length > 0
|
|
62
|
-
&& ["delegation", "summary_request", "direct_result"].includes(
|
|
63
|
-
String(bundle.execution_contract_type || safeObject(bundle.execution_contract).type || "").trim().toLowerCase(),
|
|
64
|
-
)
|
|
65
|
-
? executionContractTargets
|
|
66
|
-
: []
|
|
67
|
-
),
|
|
68
|
-
]);
|
|
58
|
+
const executionContractTargets = uniqueOrderedSelectors(ensureArray(bundle.execution_contract_targets));
|
|
59
|
+
const nextExpectedResponders = uniqueOrderedSelectors(ensureArray(bundle.next_expected_responders));
|
|
69
60
|
const executionContractType = String(
|
|
70
61
|
bundle.execution_contract_type
|
|
71
62
|
|| safeObject(bundle.execution_contract).type
|
|
72
|
-
|| (executionContractTargets.length > 0 ? "delegation" : "")
|
|
73
63
|
|| "",
|
|
74
64
|
).trim().toLowerCase();
|
|
75
|
-
const allowBotToBot = bundle.allow_bot_to_bot
|
|
65
|
+
const allowBotToBot = normalizeBoolean(bundle.allow_bot_to_bot, false);
|
|
76
66
|
const allowedResponders = uniqueOrderedSelectors(bundle.allowed_responders);
|
|
77
67
|
const initialResponders = uniqueOrderedSelectors(bundle.initial_responders);
|
|
78
68
|
const selectedBotUsernames = uniqueOrderedSelectors(
|
|
@@ -94,14 +84,16 @@ export function normalizeRunnerConversationDecisionBundle(bundleRaw) {
|
|
|
94
84
|
allow_bot_to_bot: allowBotToBot,
|
|
95
85
|
conversation_reply_expectation: String(bundle.conversation_reply_expectation || "").trim().toLowerCase(),
|
|
96
86
|
execution_contract_type: executionContractType,
|
|
97
|
-
execution_contract_actionable: bundle.execution_contract_actionable
|
|
98
|
-
|| executionContractTargets.length > 0
|
|
99
|
-
|| nextExpectedResponders.length > 0,
|
|
87
|
+
execution_contract_actionable: normalizeBoolean(bundle.execution_contract_actionable, false),
|
|
100
88
|
execution_contract_targets: executionContractTargets,
|
|
101
89
|
next_expected_responders: nextExpectedResponders,
|
|
102
90
|
should_close_after_reply: typeof bundle.should_close_after_reply === "boolean"
|
|
103
91
|
? bundle.should_close_after_reply
|
|
104
|
-
:
|
|
92
|
+
: false,
|
|
93
|
+
turn_kind: String(bundle.turn_kind || "").trim().toLowerCase(),
|
|
94
|
+
actionable_for_current_route: normalizeBoolean(bundle.actionable_for_current_route, false),
|
|
95
|
+
visible_handoff_required: normalizeBoolean(bundle.visible_handoff_required, false),
|
|
96
|
+
visible_handoff_targets: uniqueOrderedSelectors(bundle.visible_handoff_targets),
|
|
105
97
|
reasoning_summary: firstNonBlankString([bundle.reasoning_summary]),
|
|
106
98
|
};
|
|
107
99
|
}
|
|
@@ -162,6 +154,14 @@ export function validateRunnerConversationDecisionBundle(bundleRaw) {
|
|
|
162
154
|
bundle,
|
|
163
155
|
};
|
|
164
156
|
}
|
|
157
|
+
if (bundle.visible_handoff_required === true && bundle.visible_handoff_targets.length === 0) {
|
|
158
|
+
return {
|
|
159
|
+
ok: false,
|
|
160
|
+
status: "missing_visible_handoff_targets",
|
|
161
|
+
reason: "visible_handoff_required needs visible_handoff_targets",
|
|
162
|
+
bundle,
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
165
|
return {
|
|
166
166
|
ok: true,
|
|
167
167
|
status: "valid",
|
|
@@ -171,6 +171,7 @@ export function validateRunnerConversationDecisionBundle(bundleRaw) {
|
|
|
171
171
|
}
|
|
172
172
|
|
|
173
173
|
export function buildRunnerConversationDecisionBundle({
|
|
174
|
+
decisionBundle = null,
|
|
174
175
|
decisionType = "",
|
|
175
176
|
normalizedIntent = "",
|
|
176
177
|
humanIntent = null,
|
|
@@ -184,6 +185,9 @@ export function buildRunnerConversationDecisionBundle({
|
|
|
184
185
|
shouldCloseAfterReply,
|
|
185
186
|
reasoningSummary = "",
|
|
186
187
|
}) {
|
|
188
|
+
if (Object.keys(safeObject(decisionBundle)).length > 0) {
|
|
189
|
+
return normalizeRunnerConversationDecisionBundle(decisionBundle);
|
|
190
|
+
}
|
|
187
191
|
const intent = safeObject(humanIntent);
|
|
188
192
|
const conversation = safeObject(conversationContext);
|
|
189
193
|
const contract = safeObject(executionContract);
|
|
@@ -244,7 +248,11 @@ export function buildRunnerConversationDecisionBundle({
|
|
|
244
248
|
}
|
|
245
249
|
|
|
246
250
|
export function resolveRunnerConversationDecisionBundle(options = {}) {
|
|
247
|
-
const
|
|
251
|
+
const explicitBundle = safeObject(options.decisionBundle || options.decision_bundle);
|
|
252
|
+
const bundle = buildRunnerConversationDecisionBundle({
|
|
253
|
+
...options,
|
|
254
|
+
decisionBundle: Object.keys(explicitBundle).length ? explicitBundle : null,
|
|
255
|
+
});
|
|
248
256
|
const validation = validateRunnerConversationDecisionBundle(bundle);
|
|
249
257
|
return {
|
|
250
258
|
decisionBundle: validation.ok ? validation.bundle : bundle,
|