metheus-governance-mcp-cli 0.2.211 → 0.2.213
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 +129 -16
- package/lib/local-ai-adapters.mjs +6 -0
- package/lib/runner-orchestration.mjs +111 -48
- package/lib/runner-trigger.mjs +36 -18
- package/lib/selftest-runner-scenarios.mjs +185 -23
- package/package.json +1 -1
package/cli.mjs
CHANGED
|
@@ -2358,6 +2358,16 @@ function normalizeBotRunnerRequests(rawRequests, nowMs = Date.now()) {
|
|
|
2358
2358
|
next_expected_responders: ensureArray(entry.next_expected_responders || entry.nextExpectedResponders)
|
|
2359
2359
|
.map((value) => normalizeTelegramMentionUsername(value))
|
|
2360
2360
|
.filter(Boolean),
|
|
2361
|
+
ai_reply_generated: boolFromRaw(
|
|
2362
|
+
entry.ai_reply_generated ?? entry.aiReplyGenerated,
|
|
2363
|
+
false,
|
|
2364
|
+
),
|
|
2365
|
+
ai_reply_generated_at: firstNonEmptyString([entry.ai_reply_generated_at, entry.aiReplyGeneratedAt]),
|
|
2366
|
+
ai_reply_preview: String(entry.ai_reply_preview || entry.aiReplyPreview || "").trim(),
|
|
2367
|
+
delivery_status: String(entry.delivery_status || entry.deliveryStatus || "").trim().toLowerCase(),
|
|
2368
|
+
archive_status: String(entry.archive_status || entry.archiveStatus || "").trim().toLowerCase(),
|
|
2369
|
+
transport_error: String(entry.transport_error || entry.transportError || "").trim(),
|
|
2370
|
+
archive_error: String(entry.archive_error || entry.archiveError || "").trim(),
|
|
2361
2371
|
normalized_intent: String(entry.normalized_intent || entry.normalizedIntent || "").trim().toLowerCase(),
|
|
2362
2372
|
status,
|
|
2363
2373
|
claimed_by_route: String(entry.claimed_by_route || entry.claimedByRoute || "").trim(),
|
|
@@ -2764,6 +2774,62 @@ function sessionAllowsConversationResponder(sessionRaw, responderSelectorRaw = "
|
|
|
2764
2774
|
return allowedResponders.includes(responderSelector);
|
|
2765
2775
|
}
|
|
2766
2776
|
|
|
2777
|
+
function shouldBypassRunnerStartupLoopForContractFollowup({
|
|
2778
|
+
selectedRecord,
|
|
2779
|
+
bot,
|
|
2780
|
+
conversationSessionFacts = null,
|
|
2781
|
+
}) {
|
|
2782
|
+
const parsed = safeObject(selectedRecord?.parsedArchive);
|
|
2783
|
+
if (String(parsed.kind || "").trim().toLowerCase() !== "bot_reply") {
|
|
2784
|
+
return false;
|
|
2785
|
+
}
|
|
2786
|
+
const currentBotSelector = normalizeTelegramMentionUsername(bot?.username || bot?.name);
|
|
2787
|
+
if (!currentBotSelector) {
|
|
2788
|
+
return false;
|
|
2789
|
+
}
|
|
2790
|
+
const conversationID = String(parsed.conversationID || "").trim();
|
|
2791
|
+
if (!conversationID) {
|
|
2792
|
+
return false;
|
|
2793
|
+
}
|
|
2794
|
+
const executionContractType = String(parsed.executionContractType || "").trim().toLowerCase();
|
|
2795
|
+
if (executionContractType !== "delegation") {
|
|
2796
|
+
return false;
|
|
2797
|
+
}
|
|
2798
|
+
const senderBotSelector = normalizeTelegramMentionUsername(
|
|
2799
|
+
parsed.botUsername
|
|
2800
|
+
|| parsed.botName
|
|
2801
|
+
|| parsed.username
|
|
2802
|
+
|| parsed.sender,
|
|
2803
|
+
);
|
|
2804
|
+
if (!senderBotSelector || senderBotSelector === currentBotSelector) {
|
|
2805
|
+
return false;
|
|
2806
|
+
}
|
|
2807
|
+
const assignmentTargets = ensureArray(parsed.executionContractAssignments)
|
|
2808
|
+
.map((item) => normalizeTelegramMentionUsername(safeObject(item).targetBot || safeObject(item).target_bot))
|
|
2809
|
+
.filter(Boolean);
|
|
2810
|
+
const nextResponders = ensureArray(parsed.executionContractNextResponders)
|
|
2811
|
+
.map((item) => normalizeTelegramMentionUsername(item))
|
|
2812
|
+
.filter(Boolean);
|
|
2813
|
+
const conversationTargetBot = normalizeTelegramMentionUsername(parsed.conversationTargetBotUsername);
|
|
2814
|
+
const currentBotIsExpectedResponder = assignmentTargets.includes(currentBotSelector)
|
|
2815
|
+
|| nextResponders.includes(currentBotSelector)
|
|
2816
|
+
|| conversationTargetBot === currentBotSelector;
|
|
2817
|
+
if (!currentBotIsExpectedResponder) {
|
|
2818
|
+
return false;
|
|
2819
|
+
}
|
|
2820
|
+
const sessionFacts = safeObject(conversationSessionFacts);
|
|
2821
|
+
const sessionExpectedResponders = ensureArray(sessionFacts.next_expected_responders)
|
|
2822
|
+
.map((item) => normalizeTelegramMentionUsername(item))
|
|
2823
|
+
.filter(Boolean);
|
|
2824
|
+
if (sessionExpectedResponders.length > 0 && !sessionExpectedResponders.includes(currentBotSelector)) {
|
|
2825
|
+
return false;
|
|
2826
|
+
}
|
|
2827
|
+
if (sessionFacts.any_closed === true && sessionFacts.any_open !== true) {
|
|
2828
|
+
return false;
|
|
2829
|
+
}
|
|
2830
|
+
return true;
|
|
2831
|
+
}
|
|
2832
|
+
|
|
2767
2833
|
function findRunnerRequestsForMessageID(state, normalizedRoute, selectors = {}) {
|
|
2768
2834
|
const chatID = String(selectors.chatID || "").trim();
|
|
2769
2835
|
const messageID = intFromRawAllowZero(selectors.messageID, 0);
|
|
@@ -4426,6 +4492,13 @@ function markRunnerRequestLifecycle({
|
|
|
4426
4492
|
conversationReplyExpectation = "",
|
|
4427
4493
|
normalizedIntent = "",
|
|
4428
4494
|
closedReason = "",
|
|
4495
|
+
aiReplyGenerated = false,
|
|
4496
|
+
aiReplyGeneratedAt = "",
|
|
4497
|
+
aiReplyPreview = "",
|
|
4498
|
+
deliveryStatus = "",
|
|
4499
|
+
archiveStatus = "",
|
|
4500
|
+
transportError = "",
|
|
4501
|
+
archiveError = "",
|
|
4429
4502
|
}) {
|
|
4430
4503
|
const key = String(requestKey || "").trim();
|
|
4431
4504
|
if (!key) return null;
|
|
@@ -4475,6 +4548,7 @@ function markRunnerRequestLifecycle({
|
|
|
4475
4548
|
const nextStatus = (() => {
|
|
4476
4549
|
if (normalizedOutcome === "claimed") return "claimed";
|
|
4477
4550
|
if (normalizedOutcome === "running") return "running";
|
|
4551
|
+
if (normalizedOutcome === "delivery_failed_after_generation") return "running";
|
|
4478
4552
|
if (normalizedOutcome === "replied") {
|
|
4479
4553
|
return shouldRemainRunningAfterReply ? "running" : "completed";
|
|
4480
4554
|
}
|
|
@@ -4531,6 +4605,15 @@ function markRunnerRequestLifecycle({
|
|
|
4531
4605
|
ensureArray(nextExpectedResponders).length ? nextExpectedResponders : existing.next_expected_responders,
|
|
4532
4606
|
normalizeTelegramMentionUsername,
|
|
4533
4607
|
),
|
|
4608
|
+
ai_reply_generated: aiReplyGenerated === true || existing.ai_reply_generated === true,
|
|
4609
|
+
ai_reply_generated_at: aiReplyGenerated === true
|
|
4610
|
+
? firstNonEmptyString([aiReplyGeneratedAt, existing.ai_reply_generated_at, nowISO])
|
|
4611
|
+
: String(existing.ai_reply_generated_at || "").trim(),
|
|
4612
|
+
ai_reply_preview: String(aiReplyPreview || existing.ai_reply_preview || "").trim(),
|
|
4613
|
+
delivery_status: String(deliveryStatus || existing.delivery_status || "").trim().toLowerCase(),
|
|
4614
|
+
archive_status: String(archiveStatus || existing.archive_status || "").trim().toLowerCase(),
|
|
4615
|
+
transport_error: String(transportError || existing.transport_error || "").trim(),
|
|
4616
|
+
archive_error: String(archiveError || existing.archive_error || "").trim(),
|
|
4534
4617
|
normalized_intent: nextNormalizedIntent,
|
|
4535
4618
|
status: nextStatus,
|
|
4536
4619
|
started_at: firstNonEmptyString([existing.started_at, nowISO]),
|
|
@@ -7707,6 +7790,13 @@ async function maybeHandleRunnerStartupLoopCandidate({
|
|
|
7707
7790
|
}
|
|
7708
7791
|
const conversationID = String(parsed.conversationID || "").trim();
|
|
7709
7792
|
const conversationSessionFacts = collectBotRunnerConversationSessionFacts(normalizedRoute, conversationID);
|
|
7793
|
+
if (shouldBypassRunnerStartupLoopForContractFollowup({
|
|
7794
|
+
selectedRecord,
|
|
7795
|
+
bot,
|
|
7796
|
+
conversationSessionFacts,
|
|
7797
|
+
})) {
|
|
7798
|
+
return null;
|
|
7799
|
+
}
|
|
7710
7800
|
const adjudication = await resolveRunnerStartupLoopAdjudication({
|
|
7711
7801
|
selectedRecord,
|
|
7712
7802
|
pendingOrdered,
|
|
@@ -8678,7 +8768,9 @@ async function processRunnerRouteOnce(route, runtime, mode, options = {}) {
|
|
|
8678
8768
|
requestKey: requestClaim.requestKey,
|
|
8679
8769
|
selectedRecord,
|
|
8680
8770
|
routeKey,
|
|
8681
|
-
outcome:
|
|
8771
|
+
outcome: processed.kind === "delivery_failed"
|
|
8772
|
+
? "delivery_failed_after_generation"
|
|
8773
|
+
: String(processed.result?.outcome || "replied").trim().toLowerCase(),
|
|
8682
8774
|
conversationIDRaw: String(processed.result?.conversation_id || "").trim(),
|
|
8683
8775
|
conversationParticipants: ensureArray(processed.result?.conversation_participants),
|
|
8684
8776
|
conversationInitialResponders: ensureArray(processed.result?.conversation_initial_responders),
|
|
@@ -8694,14 +8786,23 @@ async function processRunnerRouteOnce(route, runtime, mode, options = {}) {
|
|
|
8694
8786
|
currentBotSelector,
|
|
8695
8787
|
conversationIntentMode: String(processed.result?.conversation_intent_mode || "").trim(),
|
|
8696
8788
|
normalizedIntent: resolvedIntentType,
|
|
8789
|
+
aiReplyGenerated: processed.result?.ai_reply_generated === true,
|
|
8790
|
+
aiReplyGeneratedAt: String(processed.result?.ai_reply_generated_at || "").trim(),
|
|
8791
|
+
aiReplyPreview: String(processed.result?.ai_reply_preview || "").trim(),
|
|
8792
|
+
deliveryStatus: String(processed.result?.delivery_status || "").trim(),
|
|
8793
|
+
archiveStatus: String(processed.result?.archive_status || "").trim(),
|
|
8794
|
+
transportError: String(processed.result?.transport_error || "").trim(),
|
|
8795
|
+
archiveError: String(processed.result?.archive_error || "").trim(),
|
|
8697
8796
|
});
|
|
8698
|
-
|
|
8699
|
-
|
|
8700
|
-
|
|
8701
|
-
|
|
8702
|
-
|
|
8703
|
-
|
|
8704
|
-
|
|
8797
|
+
if (processed.kind !== "delivery_failed") {
|
|
8798
|
+
await ensureRunnerRootWorkItemForRequest({
|
|
8799
|
+
normalizedRoute,
|
|
8800
|
+
routeKey,
|
|
8801
|
+
selectedRecord,
|
|
8802
|
+
runtime,
|
|
8803
|
+
requestKey: requestClaim.requestKey,
|
|
8804
|
+
});
|
|
8805
|
+
}
|
|
8705
8806
|
const rootWorkItemSync = await syncRunnerRequestRootWorkItemForOutcome({
|
|
8706
8807
|
normalizedRoute,
|
|
8707
8808
|
runtime,
|
|
@@ -11080,7 +11181,9 @@ async function runRunnerStartResolvedRoutes(routes, flags, options = {}) {
|
|
|
11080
11181
|
requestKey: deferredExecution.requestKey,
|
|
11081
11182
|
selectedRecord: deferredExecution.selectedRecord,
|
|
11082
11183
|
routeKey: deferredExecution.routeKey,
|
|
11083
|
-
outcome:
|
|
11184
|
+
outcome: processed.kind === "delivery_failed"
|
|
11185
|
+
? "delivery_failed_after_generation"
|
|
11186
|
+
: String(processed.result?.outcome || "replied").trim().toLowerCase(),
|
|
11084
11187
|
conversationIDRaw: String(processed.result?.conversation_id || "").trim(),
|
|
11085
11188
|
conversationParticipants: ensureArray(processed.result?.conversation_participants),
|
|
11086
11189
|
conversationInitialResponders: ensureArray(processed.result?.conversation_initial_responders),
|
|
@@ -11098,14 +11201,23 @@ async function runRunnerStartResolvedRoutes(routes, flags, options = {}) {
|
|
|
11098
11201
|
),
|
|
11099
11202
|
conversationIntentMode: String(processed.result?.conversation_intent_mode || "").trim(),
|
|
11100
11203
|
normalizedIntent: resolvedIntentType,
|
|
11204
|
+
aiReplyGenerated: processed.result?.ai_reply_generated === true,
|
|
11205
|
+
aiReplyGeneratedAt: String(processed.result?.ai_reply_generated_at || "").trim(),
|
|
11206
|
+
aiReplyPreview: String(processed.result?.ai_reply_preview || "").trim(),
|
|
11207
|
+
deliveryStatus: String(processed.result?.delivery_status || "").trim(),
|
|
11208
|
+
archiveStatus: String(processed.result?.archive_status || "").trim(),
|
|
11209
|
+
transportError: String(processed.result?.transport_error || "").trim(),
|
|
11210
|
+
archiveError: String(processed.result?.archive_error || "").trim(),
|
|
11101
11211
|
});
|
|
11102
|
-
|
|
11103
|
-
|
|
11104
|
-
|
|
11105
|
-
|
|
11106
|
-
|
|
11107
|
-
|
|
11108
|
-
|
|
11212
|
+
if (processed.kind !== "delivery_failed") {
|
|
11213
|
+
await ensureRunnerRootWorkItemForRequest({
|
|
11214
|
+
normalizedRoute: deferredExecution.normalizedRoute,
|
|
11215
|
+
routeKey: deferredExecution.routeKey,
|
|
11216
|
+
selectedRecord: deferredExecution.selectedRecord,
|
|
11217
|
+
runtime: deferredExecution.runtime,
|
|
11218
|
+
requestKey: deferredExecution.requestKey,
|
|
11219
|
+
});
|
|
11220
|
+
}
|
|
11109
11221
|
const rootWorkItemSync = await syncRunnerRequestRootWorkItemForOutcome({
|
|
11110
11222
|
normalizedRoute: deferredExecution.normalizedRoute,
|
|
11111
11223
|
runtime: deferredExecution.runtime,
|
|
@@ -15488,6 +15600,7 @@ TELEGRAM_BOT_REVIEW_TOKEN=review-token
|
|
|
15488
15600
|
selectRunnerPendingWork,
|
|
15489
15601
|
processRunnerSelectedRecord,
|
|
15490
15602
|
resolveRunnerStartupLoopAdjudication,
|
|
15603
|
+
shouldBypassRunnerStartupLoopForContractFollowup,
|
|
15491
15604
|
claimRunnerRequestForHumanComment,
|
|
15492
15605
|
markRunnerRequestLifecycle,
|
|
15493
15606
|
resolveRunnerContinuationRequestForBotReply,
|
|
@@ -1657,6 +1657,9 @@ function inferCurrentTurnPurpose({ trigger, conversation, selfBotUsername, other
|
|
|
1657
1657
|
export function buildLocalBotPrompt(payload, { terse = true } = {}) {
|
|
1658
1658
|
const safePayload = payload && typeof payload === "object" ? payload : {};
|
|
1659
1659
|
const taskName = String(safePayload.task || "").trim();
|
|
1660
|
+
const agentContext = safePayload.agent_context && typeof safePayload.agent_context === "object"
|
|
1661
|
+
? safePayload.agent_context
|
|
1662
|
+
: null;
|
|
1660
1663
|
const trigger = safePayload.trigger && typeof safePayload.trigger === "object" ? safePayload.trigger : {};
|
|
1661
1664
|
const responseContract = safePayload.response_contract && typeof safePayload.response_contract === "object"
|
|
1662
1665
|
? safePayload.response_contract
|
|
@@ -1786,6 +1789,9 @@ export function buildLocalBotPrompt(payload, { terse = true } = {}) {
|
|
|
1786
1789
|
"Current turn purpose:",
|
|
1787
1790
|
currentTurnPurpose,
|
|
1788
1791
|
"",
|
|
1792
|
+
"Agent contract/context blob (primary control and state input):",
|
|
1793
|
+
agentContext ? JSON.stringify(agentContext, null, 2) : "-",
|
|
1794
|
+
"",
|
|
1789
1795
|
"Current user request:",
|
|
1790
1796
|
String(trigger.body || "").trim() || "-",
|
|
1791
1797
|
"",
|
|
@@ -5406,55 +5406,114 @@ export async function processRunnerSelectedRecord({
|
|
|
5406
5406
|
messageID: intFromRawAllowZero(selectedRecord?.parsedArchive?.messageID, 0),
|
|
5407
5407
|
},
|
|
5408
5408
|
};
|
|
5409
|
-
}
|
|
5410
|
-
}
|
|
5409
|
+
}
|
|
5410
|
+
}
|
|
5411
|
+
const aiReplyGeneratedAt = new Date().toISOString();
|
|
5412
|
+
const aiReplyPreview = String(sanitizedReplyText || "").replace(/\s+/g, " ").trim().slice(0, 280);
|
|
5411
5413
|
emitRunnerStage("delivering", "sending reply and mirroring archive");
|
|
5412
|
-
|
|
5413
|
-
|
|
5414
|
-
|
|
5415
|
-
|
|
5416
|
-
|
|
5417
|
-
|
|
5418
|
-
|
|
5419
|
-
|
|
5420
|
-
|
|
5421
|
-
|
|
5422
|
-
|
|
5423
|
-
|
|
5424
|
-
|
|
5425
|
-
|
|
5426
|
-
|
|
5427
|
-
|
|
5428
|
-
|
|
5429
|
-
|
|
5430
|
-
|
|
5431
|
-
|
|
5432
|
-
|
|
5433
|
-
|
|
5434
|
-
|
|
5435
|
-
|
|
5436
|
-
|
|
5437
|
-
|
|
5438
|
-
|
|
5439
|
-
|
|
5440
|
-
|
|
5441
|
-
|
|
5442
|
-
|
|
5443
|
-
|
|
5444
|
-
|
|
5445
|
-
|
|
5446
|
-
|
|
5447
|
-
|
|
5448
|
-
|
|
5449
|
-
|
|
5450
|
-
|
|
5451
|
-
|
|
5452
|
-
|
|
5453
|
-
|
|
5454
|
-
|
|
5455
|
-
|
|
5456
|
-
|
|
5457
|
-
|
|
5414
|
+
let deliveryResult;
|
|
5415
|
+
try {
|
|
5416
|
+
deliveryResult = await performLocalBotDelivery({
|
|
5417
|
+
siteBaseURL: runtime.baseURL,
|
|
5418
|
+
token: runtime.token,
|
|
5419
|
+
timeoutSeconds: runtime.timeoutSeconds,
|
|
5420
|
+
actorUserID: runtime.actor.user_id,
|
|
5421
|
+
bot: {
|
|
5422
|
+
id: bot.id,
|
|
5423
|
+
name: bot.name,
|
|
5424
|
+
username: bot.username,
|
|
5425
|
+
role: bot.role,
|
|
5426
|
+
provider: bot.provider,
|
|
5427
|
+
},
|
|
5428
|
+
projectID: normalizedRoute.projectID,
|
|
5429
|
+
provider: normalizedRoute.provider,
|
|
5430
|
+
destinationSelectors: {
|
|
5431
|
+
destinationID: destination.id,
|
|
5432
|
+
destinationLabel: destination.label,
|
|
5433
|
+
},
|
|
5434
|
+
text: sanitizedReplyText,
|
|
5435
|
+
disableWebPagePreview: true,
|
|
5436
|
+
replyToMessageID,
|
|
5437
|
+
archiveReplies: normalizedRoute.archivePolicy.mirrorReplies,
|
|
5438
|
+
archiveDedupeOutbound: normalizedRoute.archivePolicy.dedupeOutbound,
|
|
5439
|
+
archiveThreadID: archiveThread.threadID,
|
|
5440
|
+
archiveWorkItemID: archiveThread.workItemID,
|
|
5441
|
+
archiveConversation: effectiveConversationContext
|
|
5442
|
+
? {
|
|
5443
|
+
id: effectiveConversationContext.id,
|
|
5444
|
+
mode: effectiveConversationContext.mode,
|
|
5445
|
+
stage: effectiveConversationContext.stage,
|
|
5446
|
+
intentMode: effectiveConversationContext.intentMode,
|
|
5447
|
+
allowBotToBot: effectiveConversationContext.allowBotToBot === true,
|
|
5448
|
+
leadBotUsername: effectiveConversationContext.leadBotUsername,
|
|
5449
|
+
summaryBotUsername: effectiveConversationContext.summaryBotUsername,
|
|
5450
|
+
participants: effectiveConversationContext.participantSelectors,
|
|
5451
|
+
participantSelectors: effectiveConversationContext.participantSelectors,
|
|
5452
|
+
initialResponders: effectiveConversationContext.initialResponderSelectors,
|
|
5453
|
+
initialResponderSelectors: effectiveConversationContext.initialResponderSelectors,
|
|
5454
|
+
allowedResponders: effectiveConversationContext.allowedResponderSelectors,
|
|
5455
|
+
allowedResponderSelectors: effectiveConversationContext.allowedResponderSelectors,
|
|
5456
|
+
executionContract,
|
|
5457
|
+
}
|
|
5458
|
+
: null,
|
|
5459
|
+
dryRun: normalizedRoute.dryRunDelivery,
|
|
5460
|
+
deps: buildRunnerDeliveryDeps(),
|
|
5461
|
+
});
|
|
5462
|
+
} catch (err) {
|
|
5463
|
+
const transportError = String(err?.message || err).trim() || "delivery_failed";
|
|
5464
|
+
saveRunnerRouteState(
|
|
5465
|
+
routeKey,
|
|
5466
|
+
buildRunnerRouteStateFromComment(selectedRecord, {
|
|
5467
|
+
last_action: "delivery_failed_after_generation",
|
|
5468
|
+
last_reason: transportError,
|
|
5469
|
+
last_trigger: String(effectiveTriggerDecision.trigger || "").trim(),
|
|
5470
|
+
last_conversation_id: String(effectiveConversationContext?.id || "").trim(),
|
|
5471
|
+
last_conversation_stage: String(effectiveConversationContext?.stage || "").trim(),
|
|
5472
|
+
last_speaker_bot_username: normalizeMentionSelector(bot?.username || bot?.name),
|
|
5473
|
+
last_workspace_dir: String(effectiveExecutionPlan.workspaceDir || "").trim(),
|
|
5474
|
+
...intentStatePatch,
|
|
5475
|
+
}),
|
|
5476
|
+
);
|
|
5477
|
+
return {
|
|
5478
|
+
kind: "delivery_failed",
|
|
5479
|
+
result: {
|
|
5480
|
+
route_key: routeKey,
|
|
5481
|
+
route_name: normalizedRoute.name,
|
|
5482
|
+
outcome: "delivery_failed_after_generation",
|
|
5483
|
+
detail: `ai reply generated but delivery failed (${transportError})`,
|
|
5484
|
+
thread_id: archiveThread.threadID,
|
|
5485
|
+
comment_id: selectedRecord.id,
|
|
5486
|
+
trigger_kind: String(effectiveTriggerDecision.trigger || "").trim(),
|
|
5487
|
+
conversation_id: String(effectiveConversationContext?.id || "").trim(),
|
|
5488
|
+
conversation_stage: String(effectiveConversationContext?.stage || "").trim(),
|
|
5489
|
+
conversation_intent_mode: String(effectiveConversationContext?.intentMode || "").trim(),
|
|
5490
|
+
conversation_lead_bot: String(effectiveConversationContext?.leadBotUsername || "").trim(),
|
|
5491
|
+
conversation_summary_bot: String(effectiveConversationContext?.summaryBotUsername || "").trim(),
|
|
5492
|
+
conversation_participants: ensureArray(effectiveConversationContext?.participantSelectors),
|
|
5493
|
+
conversation_initial_responders: ensureArray(effectiveConversationContext?.initialResponderSelectors),
|
|
5494
|
+
conversation_allowed_responders: ensureArray(effectiveConversationContext?.allowedResponderSelectors),
|
|
5495
|
+
conversation_allow_bot_to_bot: effectiveConversationContext?.allowBotToBot === true,
|
|
5496
|
+
conversation_reply_expectation: String(effectiveConversationContext?.replyExpectation || "").trim(),
|
|
5497
|
+
execution_contract_type: String(executionContract?.type || "").trim(),
|
|
5498
|
+
execution_contract_actionable: executionContract?.actionable === true,
|
|
5499
|
+
execution_contract_targets: ensureArray(executionContract?.assignments).map((item) => normalizeMentionSelector(item.targetBot)).filter(Boolean),
|
|
5500
|
+
next_expected_responders: collectExecutionContractNextResponders(executionContract),
|
|
5501
|
+
artifact_validation: String(artifactValidation.status || "").trim() || "none",
|
|
5502
|
+
artifact_paths: summarizeValidatedArtifactPaths(artifactValidation),
|
|
5503
|
+
ai_reply_generated: true,
|
|
5504
|
+
ai_reply_generated_at: aiReplyGeneratedAt,
|
|
5505
|
+
ai_reply_preview: aiReplyPreview,
|
|
5506
|
+
delivery_status: "failed_transport",
|
|
5507
|
+
archive_status: "not_attempted",
|
|
5508
|
+
transport_error: transportError,
|
|
5509
|
+
archive_error: "",
|
|
5510
|
+
reply_chars: String(sanitizedReplyText || "").length,
|
|
5511
|
+
execution_mode: effectiveExecutionPlan.mode,
|
|
5512
|
+
role_profile: effectiveExecutionPlan.roleProfileName,
|
|
5513
|
+
executed_role_plan: executedRolePlan && Object.keys(executedRolePlan).length > 0 ? executedRolePlan : undefined,
|
|
5514
|
+
},
|
|
5515
|
+
};
|
|
5516
|
+
}
|
|
5458
5517
|
emitRunnerStage("context_suggesting", "evaluating shared project context suggestion");
|
|
5459
5518
|
const projectContextSuggestion = await maybeCreateSuggestedProjectContext({
|
|
5460
5519
|
aiResult,
|
|
@@ -5629,6 +5688,10 @@ export async function processRunnerSelectedRecord({
|
|
|
5629
5688
|
evidence_ids: ensureArray(aiResult?.evidenceItems).map((item) => String(safeObject(item).id || "").trim()).filter(Boolean),
|
|
5630
5689
|
evidence_paths: ensureArray(aiResult?.evidenceItems).map((item) => String(safeObject(item).path || "").trim()).filter(Boolean),
|
|
5631
5690
|
reply_chars: String(sanitizedReplyText || "").length,
|
|
5691
|
+
ai_reply_generated: true,
|
|
5692
|
+
ai_reply_generated_at: aiReplyGeneratedAt,
|
|
5693
|
+
ai_reply_preview: aiReplyPreview,
|
|
5694
|
+
delivery_status: deliveryResult.delivery.dryRun ? "dry_run" : "delivered",
|
|
5632
5695
|
execution_mode: effectiveExecutionPlan.mode,
|
|
5633
5696
|
role_profile: effectiveExecutionPlan.roleProfileName,
|
|
5634
5697
|
executed_role_plan: executedRolePlan && Object.keys(executedRolePlan).length > 0 ? executedRolePlan : undefined,
|
package/lib/runner-trigger.mjs
CHANGED
|
@@ -94,19 +94,7 @@ function buildTriggerCandidateBotUsernames(parsedArchive) {
|
|
|
94
94
|
function buildRunnerTaskName(humanIntentType, humanIntentContract = null) {
|
|
95
95
|
const normalizedIntentType = String(humanIntentType || "").trim().toLowerCase();
|
|
96
96
|
const contract = safeObject(humanIntentContract);
|
|
97
|
-
const replyExpectation = String(contract.replyExpectation || contract.reply_expectation || "").trim().toLowerCase();
|
|
98
97
|
const executionContractType = String(contract.executionContractType || contract.execution_contract_type || "").trim().toLowerCase();
|
|
99
|
-
const intentMode = String(contract.intentMode || contract.intent_mode || "").trim().toLowerCase();
|
|
100
|
-
const executionIntentRequested = [
|
|
101
|
-
"general_execution",
|
|
102
|
-
"ctxpack_mutation",
|
|
103
|
-
"workitem_mutation",
|
|
104
|
-
].includes(normalizedIntentType);
|
|
105
|
-
const collaborativeIntentMode = [
|
|
106
|
-
"delegated_single_lead",
|
|
107
|
-
"multi_bot_collab",
|
|
108
|
-
"multi_bot_direct",
|
|
109
|
-
].includes(intentMode);
|
|
110
98
|
if (normalizedIntentType === "status_query") return "answer_project_status_query";
|
|
111
99
|
if (normalizedIntentType === "workspace_query") return "answer_project_workspace_query";
|
|
112
100
|
if (normalizedIntentType === "bot_role_query") return "answer_project_bot_role_query";
|
|
@@ -115,12 +103,6 @@ function buildRunnerTaskName(humanIntentType, humanIntentContract = null) {
|
|
|
115
103
|
if (executionContractType) {
|
|
116
104
|
return "reply_to_project_actionable_message";
|
|
117
105
|
}
|
|
118
|
-
if (collaborativeIntentMode && replyExpectation === "actionable") {
|
|
119
|
-
return "reply_to_project_chat_message";
|
|
120
|
-
}
|
|
121
|
-
if (executionIntentRequested) {
|
|
122
|
-
return "reply_to_project_actionable_message";
|
|
123
|
-
}
|
|
124
106
|
return "reply_to_project_chat_message";
|
|
125
107
|
}
|
|
126
108
|
|
|
@@ -199,6 +181,9 @@ export function buildRunnerInputPayload({
|
|
|
199
181
|
const directHumanIntent = safeObject(humanIntentContract);
|
|
200
182
|
const safeDirectQueryReply = safeObject(directQueryReply);
|
|
201
183
|
const candidateBotUsernames = buildTriggerCandidateBotUsernames(parsed);
|
|
184
|
+
const executionContractType = String(
|
|
185
|
+
directHumanIntent.executionContractType || directHumanIntent.execution_contract_type || "",
|
|
186
|
+
).trim().toLowerCase();
|
|
202
187
|
return {
|
|
203
188
|
task: buildRunnerTaskName(directHumanIntent.intentType, directHumanIntent),
|
|
204
189
|
project_id: String(route?.projectID || "").trim(),
|
|
@@ -279,6 +264,39 @@ export function buildRunnerInputPayload({
|
|
|
279
264
|
require_actionable_contract: directHumanIntent.requiresActionableContract === true,
|
|
280
265
|
allowed_contract_types: ensureArray(directHumanIntent.allowedContractTypes),
|
|
281
266
|
},
|
|
267
|
+
agent_context: {
|
|
268
|
+
bot_identity: {
|
|
269
|
+
id: String(bot?.id || "").trim(),
|
|
270
|
+
name: String(bot?.name || "").trim(),
|
|
271
|
+
username: String(bot?.username || "").trim(),
|
|
272
|
+
role: String(bot?.role || route?.role || "").trim(),
|
|
273
|
+
},
|
|
274
|
+
request_facts: {
|
|
275
|
+
comment_id: selectedRecord?.id,
|
|
276
|
+
trigger_kind: String(triggerDecision?.trigger || "").trim(),
|
|
277
|
+
body: String(parsed.body || "").trim(),
|
|
278
|
+
message_id: Number.parseInt(String(parsed.messageID ?? 0), 10) || 0,
|
|
279
|
+
reply_to_message_id: Number.parseInt(String(parsed.replyToMessageID ?? 0), 10) || 0,
|
|
280
|
+
},
|
|
281
|
+
conversation_contract: {
|
|
282
|
+
conversation_id: String(directHumanIntent.conversationID || directHumanIntent.conversation_id || parsed.conversationID || "").trim(),
|
|
283
|
+
mode: String(directHumanIntent.intentMode || directHumanIntent.intent_mode || "").trim(),
|
|
284
|
+
intent_type: String(directHumanIntent.intentType || directHumanIntent.intent_type || "").trim(),
|
|
285
|
+
lead_bot: String(directHumanIntent.leadBotSelector || directHumanIntent.lead_bot || "").trim(),
|
|
286
|
+
summary_bot: String(directHumanIntent.summaryBotSelector || directHumanIntent.summary_bot || "").trim(),
|
|
287
|
+
participants: ensureArray(directHumanIntent.participantSelectors || directHumanIntent.participants),
|
|
288
|
+
initial_responders: ensureArray(directHumanIntent.initialResponderSelectors || directHumanIntent.initial_responders),
|
|
289
|
+
allowed_responders: ensureArray(directHumanIntent.allowedResponderSelectors || directHumanIntent.allowed_responders),
|
|
290
|
+
reply_expectation: String(directHumanIntent.replyExpectation || directHumanIntent.reply_expectation || "").trim(),
|
|
291
|
+
execution_contract_type: executionContractType,
|
|
292
|
+
},
|
|
293
|
+
validation_contract: {
|
|
294
|
+
current_bot_is_candidate: true,
|
|
295
|
+
require_actionable_contract: directHumanIntent.requiresActionableContract === true,
|
|
296
|
+
allowed_contract_types: ensureArray(directHumanIntent.allowedContractTypes),
|
|
297
|
+
candidate_bot_usernames: candidateBotUsernames,
|
|
298
|
+
},
|
|
299
|
+
},
|
|
282
300
|
conversation: conversationContext
|
|
283
301
|
? {
|
|
284
302
|
mode: String(conversationContext.mode || "").trim(),
|
|
@@ -118,16 +118,17 @@ export async function runSelftestRunnerScenarios(push, deps) {
|
|
|
118
118
|
const safeObject = requireDependency(deps, "safeObject");
|
|
119
119
|
const normalizeRunnerTriggerPolicy = requireDependency(deps, "normalizeRunnerTriggerPolicy");
|
|
120
120
|
const evaluateTelegramRunnerTrigger = requireDependency(deps, "evaluateTelegramRunnerTrigger");
|
|
121
|
-
const resolveRunnerResponderAdjudication = requireDependency(deps, "resolveRunnerResponderAdjudication");
|
|
122
|
-
const selectPendingArchiveComments = requireDependency(deps, "selectPendingArchiveComments");
|
|
123
|
-
const selectRunnerPendingWork = requireDependency(deps, "selectRunnerPendingWork");
|
|
124
|
-
const processRunnerSelectedRecord = requireDependency(deps, "processRunnerSelectedRecord");
|
|
125
|
-
const resolveRunnerStartupLoopAdjudication = requireDependency(deps, "resolveRunnerStartupLoopAdjudication");
|
|
126
|
-
const
|
|
127
|
-
const
|
|
128
|
-
const
|
|
129
|
-
const
|
|
130
|
-
const
|
|
121
|
+
const resolveRunnerResponderAdjudication = requireDependency(deps, "resolveRunnerResponderAdjudication");
|
|
122
|
+
const selectPendingArchiveComments = requireDependency(deps, "selectPendingArchiveComments");
|
|
123
|
+
const selectRunnerPendingWork = requireDependency(deps, "selectRunnerPendingWork");
|
|
124
|
+
const processRunnerSelectedRecord = requireDependency(deps, "processRunnerSelectedRecord");
|
|
125
|
+
const resolveRunnerStartupLoopAdjudication = requireDependency(deps, "resolveRunnerStartupLoopAdjudication");
|
|
126
|
+
const shouldBypassRunnerStartupLoopForContractFollowup = requireDependency(deps, "shouldBypassRunnerStartupLoopForContractFollowup");
|
|
127
|
+
const runRunnerAIExecution = requireDependency(deps, "runRunnerAIExecution");
|
|
128
|
+
const formatBotReplyArchiveComment = requireDependency(deps, "formatBotReplyArchiveComment");
|
|
129
|
+
const findArchivedBotReplyRecord = requireDependency(deps, "findArchivedBotReplyRecord");
|
|
130
|
+
const parseArchivedChatComment = requireDependency(deps, "parseArchivedChatComment");
|
|
131
|
+
const validateWorkspaceArtifacts = requireDependency(deps, "validateWorkspaceArtifacts");
|
|
131
132
|
const buildLocalBotPrompt = requireDependency(deps, "buildLocalBotPrompt");
|
|
132
133
|
|
|
133
134
|
try {
|
|
@@ -367,13 +368,53 @@ export async function runSelftestRunnerScenarios(push, deps) {
|
|
|
367
368
|
&& adjudication?.startup_facts?.repeated_same_delegation_without_new_human === true,
|
|
368
369
|
`decision=${String(adjudication?.decision || "")} action=${String(adjudication?.action || "")} imported=${String(adjudication?.startup_facts?.imported_on_current_poll)} repeated_delegation=${String(adjudication?.startup_facts?.repeated_same_delegation_without_new_human)}`,
|
|
369
370
|
);
|
|
370
|
-
} catch (err) {
|
|
371
|
-
push(
|
|
372
|
-
"runner_loop_adjudication_detects_repeated_same_delegation_without_new_human_input",
|
|
373
|
-
false,
|
|
374
|
-
String(err?.message || err),
|
|
375
|
-
);
|
|
376
|
-
}
|
|
371
|
+
} catch (err) {
|
|
372
|
+
push(
|
|
373
|
+
"runner_loop_adjudication_detects_repeated_same_delegation_without_new_human_input",
|
|
374
|
+
false,
|
|
375
|
+
String(err?.message || err),
|
|
376
|
+
);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
try {
|
|
380
|
+
const shouldBypass = shouldBypassRunnerStartupLoopForContractFollowup({
|
|
381
|
+
selectedRecord: {
|
|
382
|
+
id: "bot-reply-4",
|
|
383
|
+
parsedArchive: {
|
|
384
|
+
kind: "bot_reply",
|
|
385
|
+
messageID: 52,
|
|
386
|
+
senderIsBot: true,
|
|
387
|
+
botUsername: "@bot-a",
|
|
388
|
+
conversationID: "conv-4",
|
|
389
|
+
executionContractType: "delegation",
|
|
390
|
+
executionContractAssignments: [
|
|
391
|
+
{ targetBot: "@bot-b", task: "say hello" },
|
|
392
|
+
],
|
|
393
|
+
executionContractNextResponders: ["@bot-b"],
|
|
394
|
+
conversationTargetBotUsername: "@bot-b",
|
|
395
|
+
},
|
|
396
|
+
},
|
|
397
|
+
bot: {
|
|
398
|
+
username: "@bot-b",
|
|
399
|
+
},
|
|
400
|
+
conversationSessionFacts: {
|
|
401
|
+
any_open: true,
|
|
402
|
+
any_closed: false,
|
|
403
|
+
next_expected_responders: ["bot-b"],
|
|
404
|
+
},
|
|
405
|
+
});
|
|
406
|
+
push(
|
|
407
|
+
"runner_startup_loop_bypasses_expected_delegation_followup_for_target_bot",
|
|
408
|
+
shouldBypass === true,
|
|
409
|
+
`should_bypass=${String(shouldBypass)}`,
|
|
410
|
+
);
|
|
411
|
+
} catch (err) {
|
|
412
|
+
push(
|
|
413
|
+
"runner_startup_loop_bypasses_expected_delegation_followup_for_target_bot",
|
|
414
|
+
false,
|
|
415
|
+
String(err?.message || err),
|
|
416
|
+
);
|
|
417
|
+
}
|
|
377
418
|
|
|
378
419
|
try {
|
|
379
420
|
const pendingSelection = selectPendingArchiveComments(
|
|
@@ -11944,12 +11985,133 @@ export async function runSelftestRunnerScenarios(push, deps) {
|
|
|
11944
11985
|
&& !/project-operating-guide\.md/i.test(deliveredText),
|
|
11945
11986
|
`kind=${String(processed.kind || "(none)")} outcome=${String(processed.result?.outcome || "(none)")} delivery_calls=${deliveryCalls} delivered=${deliveredText} detail=${String(processed.result?.detail || "(none)")}`,
|
|
11946
11987
|
);
|
|
11947
|
-
} catch (err) {
|
|
11948
|
-
push("runner_direct_failure_reply_uses_generic_message_for_unobserved_reported_artifacts", false, String(err?.message || err));
|
|
11949
|
-
}
|
|
11950
|
-
|
|
11951
|
-
try {
|
|
11952
|
-
const
|
|
11988
|
+
} catch (err) {
|
|
11989
|
+
push("runner_direct_failure_reply_uses_generic_message_for_unobserved_reported_artifacts", false, String(err?.message || err));
|
|
11990
|
+
}
|
|
11991
|
+
|
|
11992
|
+
try {
|
|
11993
|
+
const processed = await processRunnerSelectedRecord({
|
|
11994
|
+
routeKey: "delivery-failed-after-generation-key",
|
|
11995
|
+
normalizedRoute: normalizeRunnerRoute({
|
|
11996
|
+
name: "telegram-monitor-delivery-failed-after-generation",
|
|
11997
|
+
project_id: selftestProjectID,
|
|
11998
|
+
provider: "telegram",
|
|
11999
|
+
role: "monitor",
|
|
12000
|
+
role_profile: "monitor",
|
|
12001
|
+
destination_id: "dest-1",
|
|
12002
|
+
destination_label: "Main Room",
|
|
12003
|
+
server_bot_name: "RyoAI_bot",
|
|
12004
|
+
server_bot_id: "bot-1",
|
|
12005
|
+
trigger_policy: {
|
|
12006
|
+
mentions_only: true,
|
|
12007
|
+
direct_messages: true,
|
|
12008
|
+
reply_to_bot_messages: true,
|
|
12009
|
+
},
|
|
12010
|
+
archive_policy: {
|
|
12011
|
+
mirror_replies: true,
|
|
12012
|
+
dedupe_inbound: true,
|
|
12013
|
+
dedupe_outbound: true,
|
|
12014
|
+
skip_bot_messages: true,
|
|
12015
|
+
},
|
|
12016
|
+
dry_run_delivery: false,
|
|
12017
|
+
}),
|
|
12018
|
+
selectedRecord: {
|
|
12019
|
+
id: "comment-delivery-failed-after-generation",
|
|
12020
|
+
createdAt: "2026-03-17T00:00:21.000Z",
|
|
12021
|
+
parsedArchive: {
|
|
12022
|
+
kind: "telegram_message",
|
|
12023
|
+
chatID: "-100123",
|
|
12024
|
+
chatType: "supergroup",
|
|
12025
|
+
body: "@RyoAI_bot hi",
|
|
12026
|
+
messageID: 126,
|
|
12027
|
+
sender: "human",
|
|
12028
|
+
senderIsBot: false,
|
|
12029
|
+
mentionUsernames: ["ryoai_bot"],
|
|
12030
|
+
},
|
|
12031
|
+
},
|
|
12032
|
+
pendingOrdered: [],
|
|
12033
|
+
bot: {
|
|
12034
|
+
id: "bot-1",
|
|
12035
|
+
name: "RyoAI_bot",
|
|
12036
|
+
username: "RyoAI_bot",
|
|
12037
|
+
role: "monitor",
|
|
12038
|
+
provider: "telegram",
|
|
12039
|
+
},
|
|
12040
|
+
destination: {
|
|
12041
|
+
id: "dest-1",
|
|
12042
|
+
label: "Main Room",
|
|
12043
|
+
provider: "telegram",
|
|
12044
|
+
chatID: "-100123",
|
|
12045
|
+
},
|
|
12046
|
+
archiveThread: {
|
|
12047
|
+
threadID: "thread-1",
|
|
12048
|
+
workItemID: "work-item-1",
|
|
12049
|
+
},
|
|
12050
|
+
executionPlan: {
|
|
12051
|
+
mode: "role_profile",
|
|
12052
|
+
roleProfileName: "monitor",
|
|
12053
|
+
roleProfile: {
|
|
12054
|
+
client: "sample",
|
|
12055
|
+
model: "",
|
|
12056
|
+
permissionMode: "read_only",
|
|
12057
|
+
reasoningEffort: "low",
|
|
12058
|
+
},
|
|
12059
|
+
workspaceDir: "",
|
|
12060
|
+
workspaceSource: "selftest",
|
|
12061
|
+
usedCommandFallback: false,
|
|
12062
|
+
},
|
|
12063
|
+
runtime: {
|
|
12064
|
+
baseURL: "https://example.test",
|
|
12065
|
+
token: "selftest-token",
|
|
12066
|
+
timeoutSeconds: 30,
|
|
12067
|
+
actor: { user_id: "user-1" },
|
|
12068
|
+
},
|
|
12069
|
+
deps: {
|
|
12070
|
+
saveRunnerRouteState: () => {},
|
|
12071
|
+
startRunnerTypingHeartbeat: () => ({ async stop() {} }),
|
|
12072
|
+
runRunnerAIExecution: async () => ({
|
|
12073
|
+
skip: false,
|
|
12074
|
+
reply: "Hello from RyoAI_bot.",
|
|
12075
|
+
}),
|
|
12076
|
+
performLocalBotDelivery: async () => {
|
|
12077
|
+
throw new Error("read ECONNRESET");
|
|
12078
|
+
},
|
|
12079
|
+
serializeRunnerTriggerPolicy: (value) => value,
|
|
12080
|
+
serializeRunnerArchivePolicy: (value) => value,
|
|
12081
|
+
buildRunnerExecutionDeps: () => ({
|
|
12082
|
+
validateWorkspaceArtifacts,
|
|
12083
|
+
analyzeHumanConversationIntentWithAI: async () => ({
|
|
12084
|
+
mode: "single_bot",
|
|
12085
|
+
lead_bot: "ryoai_bot",
|
|
12086
|
+
participants: ["ryoai_bot"],
|
|
12087
|
+
initial_responders: ["ryoai_bot"],
|
|
12088
|
+
allowed_responders: ["ryoai_bot"],
|
|
12089
|
+
summary_bot: "",
|
|
12090
|
+
allow_bot_to_bot: false,
|
|
12091
|
+
reply_expectation: "informational",
|
|
12092
|
+
intent_type: "small_talk",
|
|
12093
|
+
}),
|
|
12094
|
+
}),
|
|
12095
|
+
buildRunnerDeliveryDeps: () => ({}),
|
|
12096
|
+
buildRunnerRuntimeDeps: () => ({}),
|
|
12097
|
+
resolveConversationPeerBots: () => [],
|
|
12098
|
+
},
|
|
12099
|
+
});
|
|
12100
|
+
push(
|
|
12101
|
+
"runner_delivery_failure_after_generation_records_ai_state_without_execution_error",
|
|
12102
|
+
processed.kind === "delivery_failed"
|
|
12103
|
+
&& String(processed.result?.outcome || "") === "delivery_failed_after_generation"
|
|
12104
|
+
&& processed.result?.ai_reply_generated === true
|
|
12105
|
+
&& String(processed.result?.delivery_status || "") === "failed_transport"
|
|
12106
|
+
&& /ECONNRESET/i.test(String(processed.result?.transport_error || "")),
|
|
12107
|
+
`kind=${String(processed.kind || "(none)")} outcome=${String(processed.result?.outcome || "(none)")} ai_generated=${String(processed.result?.ai_reply_generated || false)} delivery=${String(processed.result?.delivery_status || "(none)")} transport=${String(processed.result?.transport_error || "(none)")}`,
|
|
12108
|
+
);
|
|
12109
|
+
} catch (err) {
|
|
12110
|
+
push("runner_delivery_failure_after_generation_records_ai_state_without_execution_error", false, String(err?.message || err));
|
|
12111
|
+
}
|
|
12112
|
+
|
|
12113
|
+
try {
|
|
12114
|
+
const workspaceDir = fs.mkdtempSync(path.join(os.tmpdir(), "metheus-runner-selftest-observed-artifacts-"));
|
|
11953
12115
|
const scriptDir = path.join(workspaceDir, ".metheus", "runner-runtime", "local-ai-scratch");
|
|
11954
12116
|
fs.mkdirSync(scriptDir, { recursive: true });
|
|
11955
12117
|
const scriptPath = path.join(scriptDir, "emit-artifact.js");
|