@agent-native/core 0.204.0 → 0.204.1-nightly-20261008065100
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/dist/action.js +5 -6
- package/dist/agent/auto-continue.d.ts +35 -0
- package/dist/agent/auto-continue.js +38 -0
- package/dist/agent/connection-required-note.d.ts +52 -0
- package/dist/agent/connection-required-note.js +187 -0
- package/dist/agent/jev-tool-prefetch.d.ts +5 -1
- package/dist/agent/jev-tool-prefetch.js +19 -6
- package/dist/agent/previous-turn-note.d.ts +9 -0
- package/dist/agent/previous-turn-note.js +48 -0
- package/dist/agent/production-agent.d.ts +17 -13
- package/dist/agent/production-agent.js +185 -43
- package/dist/agent/reaped-run-thread.d.ts +13 -0
- package/dist/agent/reaped-run-thread.js +45 -0
- package/dist/agent/run-store.d.ts +29 -5
- package/dist/agent/run-store.js +144 -30
- package/dist/agent/thread-data-builder.d.ts +16 -0
- package/dist/agent/thread-data-builder.js +30 -8
- package/dist/agent/thread-message-projection.js +5 -3
- package/dist/agent/tool-search.js +5 -1
- package/dist/agent/types.d.ts +3 -0
- package/dist/audit/config.d.ts +6 -0
- package/dist/audit/config.js +12 -1
- package/dist/audit/index.d.ts +1 -1
- package/dist/audit/index.js +1 -1
- package/dist/audit/store.js +39 -4
- package/dist/automation/index.d.ts +1 -1
- package/dist/cli/skills.d.ts +1 -0
- package/dist/cli/skills.js +1 -1
- package/dist/client/agent-chat/index.d.ts +1 -1
- package/dist/client/agent-chat/index.js +1 -1
- package/dist/client/chat/agentkit-agent-native.js +83 -7
- package/dist/client/chat/agentkit-protocol.js +140 -67
- package/dist/client/chat/runtime.js +10 -4
- package/dist/client/embed-auth.d.ts +15 -0
- package/dist/client/embed-auth.js +77 -7
- package/dist/client/host/index.d.ts +1 -1
- package/dist/client/host/index.js +1 -1
- package/dist/client/mcp-app-host.d.ts +19 -0
- package/dist/client/mcp-app-host.js +88 -2
- package/dist/client/use-chat-threads.d.ts +1 -1
- package/dist/client/use-chat-threads.js +2 -0
- package/dist/client/use-run-stuck-detection.d.ts +18 -1
- package/dist/client/use-run-stuck-detection.js +115 -75
- package/dist/collab/routes.d.ts +1 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/db/request-telemetry.d.ts +7 -1
- package/dist/db/request-telemetry.js +20 -1
- package/dist/deploy/build.d.ts +25 -1
- package/dist/deploy/build.js +176 -3
- package/dist/deploy/workspace-deploy.js +8 -3
- package/dist/extensions/fetch-tool.js +50 -14
- package/dist/file-upload/builder.js +1 -1
- package/dist/file-upload/s3.js +1 -1
- package/dist/file-upload/types.d.ts +1 -0
- package/dist/integrations/adapters/email.js +11 -3
- package/dist/integrations/adapters/telegram.js +4 -2
- package/dist/jobs/actions/automation-no-op.d.ts +12 -0
- package/dist/jobs/actions/automation-no-op.js +20 -0
- package/dist/jobs/actions/manage-recurring-job.d.ts +1 -1
- package/dist/jobs/background-automation-runner.d.ts +8 -1
- package/dist/jobs/background-automation-runner.js +102 -19
- package/dist/jobs/run-history.d.ts +1 -1
- package/dist/jobs/run-history.js +8 -4
- package/dist/jobs/scheduler.js +25 -12
- package/dist/localization/automation-outcome-messages.d.ts +22 -0
- package/dist/localization/automation-outcome-messages.js +90 -0
- package/dist/localization/default-messages.d.ts +2 -0
- package/dist/localization/default-messages.js +4 -2
- package/dist/mcp/build-server.js +22 -11
- package/dist/mcp/embed-app.js +143 -9
- package/dist/mcp/index.d.ts +1 -1
- package/dist/mcp/index.js +1 -1
- package/dist/observability/metrics.d.ts +3 -0
- package/dist/observability/metrics.js +10 -0
- package/dist/observability/migrations.js +22 -0
- package/dist/observability/routes.d.ts +10 -5
- package/dist/observability/routes.js +88 -14
- package/dist/observability/sentiment.d.ts +5 -0
- package/dist/observability/sentiment.js +75 -25
- package/dist/observability/store.d.ts +28 -0
- package/dist/observability/store.js +58 -0
- package/dist/observability/traces.js +37 -5
- package/dist/org/handlers.d.ts +1 -1
- package/dist/progress/actions.js +1 -0
- package/dist/provider-api/actions/custom-provider-registration.d.ts +13 -13
- package/dist/provider-api/actions/provider-api.d.ts +13 -13
- package/dist/provider-api/actions/provider-api.js +6 -2
- package/dist/provider-api/index.d.ts +1 -0
- package/dist/provider-api/index.js +18 -0
- package/dist/provider-api/result-outcome.d.ts +1 -0
- package/dist/provider-api/result-outcome.js +20 -0
- package/dist/provider-api/staging.js +10 -9
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/resources/script-helpers.d.ts +1 -0
- package/dist/resources/script-helpers.js +1 -1
- package/dist/resources/store.js +81 -0
- package/dist/scripts/resources/save-memory.js +3 -0
- package/dist/server/action-routes.js +34 -6
- package/dist/server/agent-chat/prompt-resources.d.ts +8 -2
- package/dist/server/agent-chat/prompt-resources.js +31 -8
- package/dist/server/agent-chat/recurring-jobs-runtime.d.ts +19 -4
- package/dist/server/agent-chat/recurring-jobs-runtime.js +52 -16
- package/dist/server/agent-chat/script-entries.js +30 -3
- package/dist/server/agent-chat/sweep-trigger-auth.d.ts +18 -0
- package/dist/server/agent-chat/sweep-trigger-auth.js +36 -0
- package/dist/server/agent-chat-plugin.js +51 -47
- package/dist/server/agent-engine-default-model-route.d.ts +2 -2
- package/dist/server/agent-engine-ollama-models-route.d.ts +2 -2
- package/dist/server/auth.js +21 -6
- package/dist/server/embed-route.js +5 -2
- package/dist/server/framework-request-handler.js +19 -0
- package/dist/server/http-response-telemetry.js +4 -1
- package/dist/server/realtime-token.d.ts +1 -1
- package/dist/server/request-context.d.ts +6 -0
- package/dist/shared/context-status.d.ts +21 -0
- package/dist/shared/context-status.js +54 -0
- package/dist/shared/mcp-app-display.d.ts +20 -0
- package/dist/shared/mcp-app-display.js +34 -0
- package/dist/shared/mcp-app-widget-embed.d.ts +18 -0
- package/dist/shared/mcp-app-widget-embed.js +41 -0
- package/dist/templates/chat/app/components/chat/ChatRouteContent.tsx +9 -2
- package/dist/templates/factory/.agents/skills/review-latest-feedback/SKILL.md +9 -2
- package/dist/templates/workspace-core/.agents/skills/automations/SKILL.md +8 -1
- package/dist/templates/workspace-core/.agents/skills/recurring-jobs/SKILL.md +3 -1
- package/dist/triggers/actions/manage-automation.d.ts +5 -5
- package/dist/triggers/dispatcher.js +10 -7
- package/docs/content/audit-log.mdx +3 -3
- package/docs/content/cloudflare.mdx +27 -0
- package/docs/content/deployment-environment-variables.mdx +2 -0
- package/docs/content/durable-resume.mdx +3 -1
- package/docs/content/environment-variables.mdx +2 -0
- package/docs/content/mcp-apps.mdx +1 -1
- package/docs/content/recurring-jobs.mdx +29 -9
- package/docs/content/vercel.mdx +30 -0
- package/package.json +4 -3
- package/src/templates/chat/app/components/chat/ChatRouteContent.tsx +9 -2
- package/src/templates/factory/.agents/skills/review-latest-feedback/SKILL.md +9 -2
- package/src/templates/workspace-core/.agents/skills/automations/SKILL.md +8 -1
- package/src/templates/workspace-core/.agents/skills/recurring-jobs/SKILL.md +3 -1
|
@@ -10,12 +10,14 @@ import { buildAssistantMessage, buildUserMessage, extractThreadMeta, foldAssista
|
|
|
10
10
|
import { attachToolSearch } from "../agent/tool-search.js";
|
|
11
11
|
import { lookupOwnerAccount, resolveAutomationExecutionIdentity, } from "../automations/service.js";
|
|
12
12
|
import { createThread, getThread, updateThreadData, withThreadDataLock, } from "../chat-threads/store.js";
|
|
13
|
+
import { automationOutcomeMessagesForUser } from "../localization/automation-outcome-messages.js";
|
|
13
14
|
import { queryOrgMembers } from "../org/context.js";
|
|
14
15
|
import { organizationIdFromResourceOwner, organizationResourceOwner, } from "../resources/store.js";
|
|
15
16
|
import { captureError } from "../server/capture-error.js";
|
|
16
17
|
import { BuilderCredentialLookupError, CredentialStoreUnavailableError, } from "../server/credential-provider.js";
|
|
17
18
|
import { runWithRequestContext, } from "../server/request-context.js";
|
|
18
19
|
import { normalizeReasoningEffortForRequest } from "../shared/reasoning-effort.js";
|
|
20
|
+
import automationNoOpAction, { AUTOMATION_NO_OP_TOOL, automationNoOpSchema, } from "./actions/automation-no-op.js";
|
|
19
21
|
import { applyAutomationFailure, automationOwnerKind, classifyAutomationFailure, CONFIG_INVALID_ERROR_CODE, MISSING_TOOLS_ERROR_CODE, OWNER_MISSING_ERROR_CODE, pausedMessage, withDeliveryNote, } from "./automation-outcome.js";
|
|
20
22
|
import { recoveredFactoryOwnerOrgId, } from "./frontmatter.js";
|
|
21
23
|
import { attachAutomationRunThread, finishAutomationRun, startAutomationRun, } from "./run-history.js";
|
|
@@ -352,7 +354,7 @@ export async function runBackgroundAutomation(options, deps) {
|
|
|
352
354
|
!(failure.precondition && !transition.pause));
|
|
353
355
|
throw err;
|
|
354
356
|
}
|
|
355
|
-
await recordRunOutcome(historyId, "
|
|
357
|
+
await recordRunOutcome(historyId, result.status, result.status === "skipped" ? result.reason : undefined);
|
|
356
358
|
return result;
|
|
357
359
|
}
|
|
358
360
|
async function recordRunThread(historyId, threadId, runId) {
|
|
@@ -445,6 +447,45 @@ async function recordRunOutcome(historyId, status, error, errorCode, notify = tr
|
|
|
445
447
|
console.error(`[automations] Could not record run ${historyId} as ${status}:`, err);
|
|
446
448
|
}
|
|
447
449
|
}
|
|
450
|
+
async function confirmAutomationWork(automation, ownerEmail, run, responseText, actions, noOpReason) {
|
|
451
|
+
const events = run.events ?? [];
|
|
452
|
+
const hasConfirmedAction = events.some(({ event }) => event.type === "tool_done" &&
|
|
453
|
+
!event.isError &&
|
|
454
|
+
event.completedSideEffect === true &&
|
|
455
|
+
actions[event.tool]?.confirmsAutomationWork !== false);
|
|
456
|
+
const lastFailedTool = [...events]
|
|
457
|
+
.reverse()
|
|
458
|
+
.find(({ event }) => event.type === "tool_done" && event.isError);
|
|
459
|
+
if (noOpReason && !hasConfirmedAction && !lastFailedTool) {
|
|
460
|
+
return { status: "skipped", reason: noOpReason };
|
|
461
|
+
}
|
|
462
|
+
const { deliveryPlatform, deliveryDestination } = automation.meta;
|
|
463
|
+
if ((!noOpReason || hasConfirmedAction) &&
|
|
464
|
+
deliveryPlatform &&
|
|
465
|
+
deliveryDestination &&
|
|
466
|
+
responseText.trim()) {
|
|
467
|
+
const { getDefaultAdapter } = await import("../integrations/adapters/index.js");
|
|
468
|
+
const adapter = getDefaultAdapter(deliveryPlatform);
|
|
469
|
+
if (!adapter?.sendMessageToTarget) {
|
|
470
|
+
throw new BackgroundAutomationRunError(`Automation delivery is not supported for ${deliveryPlatform}`, CONFIG_INVALID_ERROR_CODE);
|
|
471
|
+
}
|
|
472
|
+
await adapter.sendMessageToTarget(adapter.formatAgentResponse(responseText), {
|
|
473
|
+
destination: deliveryDestination,
|
|
474
|
+
threadRef: automation.meta.deliveryThreadRef ?? null,
|
|
475
|
+
tenantId: automation.meta.deliveryTenantId,
|
|
476
|
+
});
|
|
477
|
+
return { status: "success" };
|
|
478
|
+
}
|
|
479
|
+
if (hasConfirmedAction)
|
|
480
|
+
return { status: "success" };
|
|
481
|
+
const messages = await automationOutcomeMessagesForUser(ownerEmail);
|
|
482
|
+
const detail = lastFailedTool?.event.type === "tool_done"
|
|
483
|
+
? lastFailedTool.event.result
|
|
484
|
+
: undefined;
|
|
485
|
+
throw new BackgroundAutomationRunError(`${deliveryPlatform && deliveryDestination ? messages.emptyDelivery : messages.noWork}${detail ? ` ${detail}` : ""}`, lastFailedTool?.event.type === "tool_done"
|
|
486
|
+
? (lastFailedTool.event.errorCode ?? "automation_no_confirmed_work")
|
|
487
|
+
: "automation_no_confirmed_work");
|
|
488
|
+
}
|
|
448
489
|
/**
|
|
449
490
|
* Everything below runs before any thread or `agent_runs` row exists, so an
|
|
450
491
|
* automation that cannot run fails here, once, instead of leaving a "Job:"
|
|
@@ -514,7 +555,38 @@ async function executeBackgroundAutomation(options, deps, historyId, runIdRef) {
|
|
|
514
555
|
orgId,
|
|
515
556
|
}, async () => {
|
|
516
557
|
assertHardDeadline(options.hardDeadlineAt);
|
|
517
|
-
|
|
558
|
+
let noOpReason;
|
|
559
|
+
const messages = await automationOutcomeMessagesForUser(ownerEmail);
|
|
560
|
+
const baseActions = {
|
|
561
|
+
...(await deps.getActions(automation)),
|
|
562
|
+
[AUTOMATION_NO_OP_TOOL]: {
|
|
563
|
+
...automationNoOpAction,
|
|
564
|
+
agentTool: true,
|
|
565
|
+
confirmsAutomationWork: false,
|
|
566
|
+
tool: {
|
|
567
|
+
...automationNoOpAction.tool,
|
|
568
|
+
description: messages.noOpInstruction,
|
|
569
|
+
parameters: {
|
|
570
|
+
...automationNoOpAction.tool.parameters,
|
|
571
|
+
type: "object",
|
|
572
|
+
properties: {
|
|
573
|
+
...automationNoOpAction.tool.parameters?.properties,
|
|
574
|
+
reason: {
|
|
575
|
+
...automationNoOpAction.tool.parameters?.properties
|
|
576
|
+
?.reason,
|
|
577
|
+
type: "string",
|
|
578
|
+
description: messages.noOpReason,
|
|
579
|
+
},
|
|
580
|
+
},
|
|
581
|
+
},
|
|
582
|
+
},
|
|
583
|
+
run: async (args, ctx) => {
|
|
584
|
+
const result = await automationNoOpAction.run(automationNoOpSchema.parse(args), ctx);
|
|
585
|
+
noOpReason = result.reason;
|
|
586
|
+
return result;
|
|
587
|
+
},
|
|
588
|
+
},
|
|
589
|
+
};
|
|
518
590
|
assertHardDeadline(options.hardDeadlineAt);
|
|
519
591
|
assertRequestedMcpToolsAvailable(automation, baseActions);
|
|
520
592
|
const configuredInitialTools = deps.getInitialToolNames?.(automation);
|
|
@@ -524,6 +596,10 @@ async function executeBackgroundAutomation(options, deps, historyId, runIdRef) {
|
|
|
524
596
|
...(automation.meta.mcpTools ?? []),
|
|
525
597
|
])
|
|
526
598
|
: undefined;
|
|
599
|
+
if (initialToolNames &&
|
|
600
|
+
!initialToolNames.includes(AUTOMATION_NO_OP_TOOL)) {
|
|
601
|
+
initialToolNames.push(AUTOMATION_NO_OP_TOOL);
|
|
602
|
+
}
|
|
527
603
|
const actions = initialToolNames
|
|
528
604
|
? attachToolSearch({ ...baseActions })
|
|
529
605
|
: baseActions;
|
|
@@ -535,7 +611,7 @@ async function executeBackgroundAutomation(options, deps, historyId, runIdRef) {
|
|
|
535
611
|
assertHardDeadline(options.hardDeadlineAt);
|
|
536
612
|
const model = await resolveBackgroundAutomationModel(engine, automation.meta.model, deps);
|
|
537
613
|
assertHardDeadline(options.hardDeadlineAt);
|
|
538
|
-
const systemPrompt = await deps.getSystemPrompt(ownerEmail)
|
|
614
|
+
const systemPrompt = `${await deps.getSystemPrompt(ownerEmail)}\n\n${messages.noOpInstruction}`;
|
|
539
615
|
assertHardDeadline(options.hardDeadlineAt);
|
|
540
616
|
const thread = await createThread(ownerEmail, {
|
|
541
617
|
title: threadTitle,
|
|
@@ -553,6 +629,7 @@ async function executeBackgroundAutomation(options, deps, historyId, runIdRef) {
|
|
|
553
629
|
const softTimeoutMs = resolveBackgroundAutomationSoftTimeoutMs();
|
|
554
630
|
const usageRef = { current: null };
|
|
555
631
|
let responseText = "";
|
|
632
|
+
let outcome;
|
|
556
633
|
let hardAbortTimer = null;
|
|
557
634
|
let hardTimedOut = false;
|
|
558
635
|
assertHardDeadline(options.hardDeadlineAt);
|
|
@@ -631,12 +708,25 @@ async function executeBackgroundAutomation(options, deps, historyId, runIdRef) {
|
|
|
631
708
|
clearTimeout(hardAbortTimer);
|
|
632
709
|
hardAbortTimer = null;
|
|
633
710
|
}
|
|
634
|
-
|
|
711
|
+
let persistFailure = backgroundAutomationPersistFailure({
|
|
635
712
|
run,
|
|
636
713
|
hardTimedOut,
|
|
637
714
|
hardTimeoutMs,
|
|
638
715
|
});
|
|
639
716
|
try {
|
|
717
|
+
responseText = collectFinalResponseTextFromAgentEvents((run.events ?? []).map((entry) => entry.event), { fallbackToPreToolText: false });
|
|
718
|
+
if (!persistFailure && run.status === "completed") {
|
|
719
|
+
try {
|
|
720
|
+
outcome = await confirmAutomationWork(automation, ownerEmail, run, responseText, actions, noOpReason);
|
|
721
|
+
}
|
|
722
|
+
catch (error) {
|
|
723
|
+
const failure = classifyAutomationFailure(error);
|
|
724
|
+
persistFailure = {
|
|
725
|
+
message: failure.message,
|
|
726
|
+
errorCode: failure.code,
|
|
727
|
+
};
|
|
728
|
+
}
|
|
729
|
+
}
|
|
640
730
|
await persistBackgroundAutomationTurn({
|
|
641
731
|
threadId: thread.id,
|
|
642
732
|
threadTitle,
|
|
@@ -652,6 +742,11 @@ async function executeBackgroundAutomation(options, deps, historyId, runIdRef) {
|
|
|
652
742
|
if (hardTimedOut)
|
|
653
743
|
return;
|
|
654
744
|
if (persistFailure) {
|
|
745
|
+
run.continuationTerminalEvent = {
|
|
746
|
+
type: "error",
|
|
747
|
+
error: persistFailure.message,
|
|
748
|
+
errorCode: persistFailure.errorCode,
|
|
749
|
+
};
|
|
655
750
|
reject(new BackgroundAutomationRunError(persistFailure.message, persistFailure.errorCode));
|
|
656
751
|
return;
|
|
657
752
|
}
|
|
@@ -661,7 +756,6 @@ async function executeBackgroundAutomation(options, deps, historyId, runIdRef) {
|
|
|
661
756
|
`Background automation ended with status: ${run.status} and recorded no error detail`, cause?.errorCode ?? `background_automation_${run.status}`));
|
|
662
757
|
return;
|
|
663
758
|
}
|
|
664
|
-
responseText = collectFinalResponseTextFromAgentEvents((run.events ?? []).map((entry) => entry.event));
|
|
665
759
|
resolve();
|
|
666
760
|
}, {
|
|
667
761
|
softTimeoutMs,
|
|
@@ -722,20 +816,9 @@ async function executeBackgroundAutomation(options, deps, historyId, runIdRef) {
|
|
|
722
816
|
// Usage attribution must not break an otherwise successful run.
|
|
723
817
|
}
|
|
724
818
|
}
|
|
725
|
-
if (
|
|
726
|
-
|
|
727
|
-
automation.meta.deliveryDestination) {
|
|
728
|
-
const { getDefaultAdapter } = await import("../integrations/adapters/index.js");
|
|
729
|
-
const adapter = getDefaultAdapter(automation.meta.deliveryPlatform);
|
|
730
|
-
if (!adapter?.sendMessageToTarget) {
|
|
731
|
-
throw new BackgroundAutomationRunError(`Automation delivery is not supported for ${automation.meta.deliveryPlatform}`, CONFIG_INVALID_ERROR_CODE);
|
|
732
|
-
}
|
|
733
|
-
await adapter.sendMessageToTarget(adapter.formatAgentResponse(responseText), {
|
|
734
|
-
destination: automation.meta.deliveryDestination,
|
|
735
|
-
threadRef: automation.meta.deliveryThreadRef ?? null,
|
|
736
|
-
tenantId: automation.meta.deliveryTenantId,
|
|
737
|
-
});
|
|
819
|
+
if (!outcome) {
|
|
820
|
+
throw new BackgroundAutomationRunError(messages.noWork, "automation_no_confirmed_work");
|
|
738
821
|
}
|
|
739
|
-
return { responseText, runId };
|
|
822
|
+
return { ...outcome, responseText, runId };
|
|
740
823
|
});
|
|
741
824
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type MigrationEntry } from "../db/migrations.js";
|
|
2
|
-
export type AutomationRunStatus = "running" | "success" | "error" | "interrupted";
|
|
2
|
+
export type AutomationRunStatus = "running" | "success" | "error" | "interrupted" | "skipped";
|
|
3
3
|
export interface AutomationRun {
|
|
4
4
|
id: string;
|
|
5
5
|
owner: string;
|
package/dist/jobs/run-history.js
CHANGED
|
@@ -18,7 +18,7 @@ registerEvent({
|
|
|
18
18
|
orgId: z.string().nullable(),
|
|
19
19
|
runId: z.string().nullable(),
|
|
20
20
|
threadId: z.string().nullable(),
|
|
21
|
-
status: z.enum(["success", "error", "interrupted"]),
|
|
21
|
+
status: z.enum(["success", "error", "interrupted", "skipped"]),
|
|
22
22
|
error: z.string().nullable(),
|
|
23
23
|
errorCode: z.string().nullable(),
|
|
24
24
|
durationMs: z.number().nullable(),
|
|
@@ -575,7 +575,7 @@ export async function finishAutomationRun(id, status, error, errorCode, options
|
|
|
575
575
|
});
|
|
576
576
|
const row = existing.rows?.[0];
|
|
577
577
|
const finishedAt = Date.now();
|
|
578
|
-
const shouldQueueFailureAlert = status
|
|
578
|
+
const shouldQueueFailureAlert = (status === "error" || status === "interrupted") &&
|
|
579
579
|
options.notify !== false &&
|
|
580
580
|
Boolean(row?.notification_email);
|
|
581
581
|
const claimGuard = options.expectedClaimedAt !== undefined;
|
|
@@ -589,7 +589,9 @@ export async function finishAutomationRun(id, status, error, errorCode, options
|
|
|
589
589
|
status,
|
|
590
590
|
finishedAt,
|
|
591
591
|
error?.slice(0, MAX_ERROR_LENGTH) ?? null,
|
|
592
|
-
|
|
592
|
+
status === "skipped"
|
|
593
|
+
? null
|
|
594
|
+
: (errorCode?.slice(0, MAX_ERROR_CODE_LENGTH) ?? null),
|
|
593
595
|
shouldQueueFailureAlert ? "evaluating" : null,
|
|
594
596
|
shouldQueueFailureAlert ? finishedAt : null,
|
|
595
597
|
id,
|
|
@@ -611,7 +613,9 @@ export async function finishAutomationRun(id, status, error, errorCode, options
|
|
|
611
613
|
threadId: row.thread_id == null ? null : stringifyValue(row.thread_id),
|
|
612
614
|
status,
|
|
613
615
|
error: error?.slice(0, MAX_ERROR_LENGTH) ?? null,
|
|
614
|
-
errorCode:
|
|
616
|
+
errorCode: status === "skipped"
|
|
617
|
+
? null
|
|
618
|
+
: (errorCode?.slice(0, MAX_ERROR_CODE_LENGTH) ?? null),
|
|
615
619
|
durationMs: startedAt === null ? null : Math.max(0, finishedAt - startedAt),
|
|
616
620
|
}, { owner: stringifyValue(row.owner) });
|
|
617
621
|
}
|
package/dist/jobs/scheduler.js
CHANGED
|
@@ -267,7 +267,7 @@ async function processRecurringJobsWithLease(deps) {
|
|
|
267
267
|
});
|
|
268
268
|
continue;
|
|
269
269
|
}
|
|
270
|
-
await recordIdentityFailure(candidate.resource, candidate.meta, candidate.body, now, identity.reason);
|
|
270
|
+
await recordIdentityFailure(candidate.resource, candidate.meta, candidate.body, now, identity.reason, identity.code);
|
|
271
271
|
continue;
|
|
272
272
|
}
|
|
273
273
|
if (isReservedIdentityBlocked(identity.identity.userEmail)) {
|
|
@@ -386,23 +386,32 @@ async function recoverStaleAutomationHistory(owner, path, now) {
|
|
|
386
386
|
}
|
|
387
387
|
}
|
|
388
388
|
export const jobRunCutOffReason = backgroundRunCutOffReason;
|
|
389
|
-
async function recordIdentityFailure(resource, meta, body, now, reason, historyId) {
|
|
389
|
+
async function recordIdentityFailure(resource, meta, body, now, reason, errorCode = "owner_unverifiable", historyId) {
|
|
390
390
|
const jobName = resource.path.replace(/^jobs\//, "").replace(/\.md$/, "");
|
|
391
391
|
console.warn(`[recurring-jobs] Skipping job "${jobName}": ${reason}. ` +
|
|
392
392
|
`User/membership no longer valid — leaving cron entry for admin review.`);
|
|
393
393
|
const alreadyRecorded = meta.lastError === reason && hasRecentIdentityFailure(meta, now);
|
|
394
394
|
meta.lastCheck = now.toISOString();
|
|
395
|
-
meta.lastStatus = "
|
|
395
|
+
meta.lastStatus = "error";
|
|
396
396
|
meta.lastError = reason;
|
|
397
|
+
meta.lastErrorCode = errorCode;
|
|
397
398
|
if (!alreadyRecorded)
|
|
398
|
-
await updateResource(resource, meta, body);
|
|
399
|
+
await updateResource(resource, meta, body, { lastErrorCode: errorCode });
|
|
399
400
|
if (historyId) {
|
|
400
|
-
await finishAutomationRun(historyId, "error", `Automation did not run: ${reason}. No delivery was confirmed
|
|
401
|
+
await finishAutomationRun(historyId, "error", `Automation did not run: ${reason}. No delivery was confirmed.`, errorCode);
|
|
401
402
|
}
|
|
402
|
-
return { status: "
|
|
403
|
+
return { status: "error", error: reason };
|
|
403
404
|
}
|
|
404
405
|
function hasRecentIdentityFailure(meta, now) {
|
|
405
|
-
if (meta.lastStatus !== "
|
|
406
|
+
if (meta.lastStatus !== "error" ||
|
|
407
|
+
!meta.lastErrorCode ||
|
|
408
|
+
![
|
|
409
|
+
OWNER_MISSING_ERROR_CODE,
|
|
410
|
+
CONFIG_INVALID_ERROR_CODE,
|
|
411
|
+
"owner_unverifiable",
|
|
412
|
+
].includes(meta.lastErrorCode) ||
|
|
413
|
+
!meta.lastCheck ||
|
|
414
|
+
!meta.lastError) {
|
|
406
415
|
return false;
|
|
407
416
|
}
|
|
408
417
|
const lastCheckMs = Date.parse(meta.lastCheck);
|
|
@@ -571,7 +580,7 @@ async function executeJob(resource, meta, body, deps, now, options = {}) {
|
|
|
571
580
|
// failure; leave the cron entry alone so an admin can purge after
|
|
572
581
|
// investigation.
|
|
573
582
|
if (!identity.ok) {
|
|
574
|
-
return recordIdentityFailure(resource, meta, body, now, identity.reason, options.historyId);
|
|
583
|
+
return recordIdentityFailure(resource, meta, body, now, identity.reason, identity.code, options.historyId);
|
|
575
584
|
}
|
|
576
585
|
const jobUserEmail = identity.identity.userEmail;
|
|
577
586
|
const jobOrgId = identity.identity.orgId;
|
|
@@ -693,12 +702,16 @@ async function executeJob(resource, meta, body, deps, now, options = {}) {
|
|
|
693
702
|
}, deps);
|
|
694
703
|
await recordExecutionOutcome(resource, {
|
|
695
704
|
lastRun: meta.lastRun,
|
|
696
|
-
lastStatus:
|
|
697
|
-
lastError: undefined,
|
|
705
|
+
lastStatus: result.status,
|
|
706
|
+
lastError: result.status === "skipped" ? result.reason : undefined,
|
|
698
707
|
advanceSchedule: options.advanceSchedule,
|
|
699
708
|
});
|
|
700
|
-
console.log(`[recurring-jobs] Job "${jobName}"
|
|
701
|
-
return {
|
|
709
|
+
console.log(`[recurring-jobs] Job "${jobName}" ${result.status}.`);
|
|
710
|
+
return {
|
|
711
|
+
status: result.status,
|
|
712
|
+
runId: result.runId,
|
|
713
|
+
...(result.status === "skipped" ? { error: result.reason } : {}),
|
|
714
|
+
};
|
|
702
715
|
}
|
|
703
716
|
catch (err) {
|
|
704
717
|
const failure = classifyAutomationFailure(err);
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { type BuiltinLocaleCode } from "./shared.js";
|
|
2
|
+
export declare const AUTOMATION_OUTCOME_MESSAGES: Record<BuiltinLocaleCode, {
|
|
3
|
+
noWork: string;
|
|
4
|
+
emptyDelivery: string;
|
|
5
|
+
emailNotSent: string;
|
|
6
|
+
noOpInstruction: string;
|
|
7
|
+
noOpReason: string;
|
|
8
|
+
}>;
|
|
9
|
+
export declare function automationOutcomeMessagesForLocale(locale: string): {
|
|
10
|
+
noWork: string;
|
|
11
|
+
emptyDelivery: string;
|
|
12
|
+
emailNotSent: string;
|
|
13
|
+
noOpInstruction: string;
|
|
14
|
+
noOpReason: string;
|
|
15
|
+
};
|
|
16
|
+
export declare function automationOutcomeMessagesForUser(userEmail: string | null | undefined): Promise<{
|
|
17
|
+
noWork: string;
|
|
18
|
+
emptyDelivery: string;
|
|
19
|
+
emailNotSent: string;
|
|
20
|
+
noOpInstruction: string;
|
|
21
|
+
noOpReason: string;
|
|
22
|
+
}>;
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { getUserSetting } from "../settings/user-settings.js";
|
|
2
|
+
import { DEFAULT_LOCALE, isLocaleCode, LOCALIZATION_SETTING_KEY, normalizeLocalizationPreference, } from "./shared.js";
|
|
3
|
+
export const AUTOMATION_OUTCOME_MESSAGES = {
|
|
4
|
+
"en-US": {
|
|
5
|
+
noOpInstruction: "If nothing needs doing, call automation-no-op with a short reason. Failed tools, sends, or missing credentials are errors, not no-ops.",
|
|
6
|
+
noOpReason: "A short reason why no action or report was needed (1–500 characters).",
|
|
7
|
+
emailNotSent: "The email was not sent by a delivery provider.",
|
|
8
|
+
noWork: "The automation ended without a confirmed action. Configure a delivery destination for read-only reports.",
|
|
9
|
+
emptyDelivery: "The automation produced no message for its configured delivery destination.",
|
|
10
|
+
},
|
|
11
|
+
"es-ES": {
|
|
12
|
+
noOpInstruction: "Si no hay nada que hacer, llama a automation-no-op con un motivo breve. Las herramientas o los envíos fallidos y las credenciales ausentes son errores, no casos sin trabajo.",
|
|
13
|
+
noOpReason: "Motivo breve por el que no se necesitaba ninguna acción ni informe (1–500 caracteres).",
|
|
14
|
+
emailNotSent: "El correo no fue enviado por un proveedor de entrega.",
|
|
15
|
+
noWork: "La automatización terminó sin una acción confirmada. Configura un destino de entrega para los informes de solo lectura.",
|
|
16
|
+
emptyDelivery: "La automatización no produjo ningún mensaje para su destino de entrega configurado.",
|
|
17
|
+
},
|
|
18
|
+
"fr-FR": {
|
|
19
|
+
noOpInstruction: "Si rien n’est à faire, appelez automation-no-op avec une courte raison. Les échecs d’outils ou d’envoi et les identifiants manquants sont des erreurs, pas une absence de travail.",
|
|
20
|
+
noOpReason: "Courte raison pour laquelle aucune action ni aucun rapport n’était nécessaire (1–500 caractères).",
|
|
21
|
+
emailNotSent: "L’e-mail n’a pas été envoyé par un fournisseur de messagerie.",
|
|
22
|
+
noWork: "L’automatisation s’est terminée sans action confirmée. Configurez une destination de livraison pour les rapports en lecture seule.",
|
|
23
|
+
emptyDelivery: "L’automatisation n’a produit aucun message pour sa destination de livraison configurée.",
|
|
24
|
+
},
|
|
25
|
+
"de-DE": {
|
|
26
|
+
noOpInstruction: "Wenn nichts zu tun ist, rufen Sie automation-no-op mit einer kurzen Begründung auf. Fehlgeschlagene Tools oder Zustellungen und fehlende Zugangsdaten sind Fehler, keine Fälle ohne Handlungsbedarf.",
|
|
27
|
+
noOpReason: "Kurze Begründung, warum keine Aktion oder kein Bericht nötig war (1–500 Zeichen).",
|
|
28
|
+
emailNotSent: "Die E-Mail wurde von keinem Zustellanbieter versendet.",
|
|
29
|
+
noWork: "Die Automatisierung endete ohne bestätigte Aktion. Konfigurieren Sie ein Zustellungsziel für schreibgeschützte Berichte.",
|
|
30
|
+
emptyDelivery: "Die Automatisierung hat keine Nachricht für ihr konfiguriertes Zustellungsziel erzeugt.",
|
|
31
|
+
},
|
|
32
|
+
"pt-BR": {
|
|
33
|
+
noOpInstruction: "Se não houver nada a fazer, chame automation-no-op com um motivo breve. Falhas de ferramentas ou envios e credenciais ausentes são erros, não ausência de trabalho.",
|
|
34
|
+
noOpReason: "Motivo breve pelo qual nenhuma ação ou relatório era necessário (1–500 caracteres).",
|
|
35
|
+
emailNotSent: "O e-mail não foi enviado por um provedor de entrega.",
|
|
36
|
+
noWork: "A automação terminou sem uma ação confirmada. Configure um destino de entrega para relatórios somente de leitura.",
|
|
37
|
+
emptyDelivery: "A automação não produziu nenhuma mensagem para seu destino de entrega configurado.",
|
|
38
|
+
},
|
|
39
|
+
"zh-CN": {
|
|
40
|
+
noOpInstruction: "如果没有需要执行的工作,请调用 automation-no-op 并提供简短原因。工具失败、发送失败或缺少凭据属于错误,不是无需操作。",
|
|
41
|
+
noOpReason: "简要说明为何无需执行操作或发送报告(1–500 个字符)。",
|
|
42
|
+
emailNotSent: "电子邮件未通过邮件服务提供商发送。",
|
|
43
|
+
noWork: "自动化结束时没有已确认的操作。请为只读报告配置发送目标。",
|
|
44
|
+
emptyDelivery: "自动化未生成要发送到已配置目标的消息。",
|
|
45
|
+
},
|
|
46
|
+
"zh-TW": {
|
|
47
|
+
noOpInstruction: "如果沒有需要執行的工作,請呼叫 automation-no-op 並提供簡短原因。工具失敗、傳送失敗或缺少憑證屬於錯誤,不是無需操作。",
|
|
48
|
+
noOpReason: "簡要說明為何無需執行操作或傳送報告(1–500 個字元)。",
|
|
49
|
+
emailNotSent: "電子郵件未透過郵件服務供應商傳送。",
|
|
50
|
+
noWork: "自動化結束時沒有已確認的操作。請為唯讀報告設定傳送目標。",
|
|
51
|
+
emptyDelivery: "自動化未產生要傳送到已設定目標的訊息。",
|
|
52
|
+
},
|
|
53
|
+
"ja-JP": {
|
|
54
|
+
noOpInstruction: "対応が不要な場合は、短い理由を添えて automation-no-op を呼び出してください。ツールや送信の失敗、認証情報の不足はエラーであり、対応不要には該当しません。",
|
|
55
|
+
noOpReason: "操作やレポートが不要だった短い理由(1~500文字)。",
|
|
56
|
+
emailNotSent: "メールは配信プロバイダーから送信されませんでした。",
|
|
57
|
+
noWork: "自動化は操作が確認されないまま終了しました。読み取り専用のレポートには配信先を設定してください。",
|
|
58
|
+
emptyDelivery: "自動化は設定された配信先へのメッセージを生成しませんでした。",
|
|
59
|
+
},
|
|
60
|
+
"ko-KR": {
|
|
61
|
+
noOpInstruction: "할 일이 없으면 짧은 이유와 함께 automation-no-op을 호출하세요. 도구 또는 전송 실패와 자격 증명 누락은 작업 없음이 아닌 오류입니다.",
|
|
62
|
+
noOpReason: "작업이나 보고서가 필요하지 않았던 짧은 이유(1–500자).",
|
|
63
|
+
emailNotSent: "이메일이 전송 제공업체를 통해 전송되지 않았습니다.",
|
|
64
|
+
noWork: "확인된 작업 없이 자동화가 종료되었습니다. 읽기 전용 보고서에는 전송 대상을 설정하세요.",
|
|
65
|
+
emptyDelivery: "자동화가 설정된 전송 대상에 보낼 메시지를 생성하지 않았습니다.",
|
|
66
|
+
},
|
|
67
|
+
"hi-IN": {
|
|
68
|
+
noOpInstruction: "यदि कुछ करने की आवश्यकता नहीं है, तो संक्षिप्त कारण के साथ automation-no-op कॉल करें। विफल टूल, विफल भेजने की कोशिश या अनुपलब्ध क्रेडेंशियल त्रुटियाँ हैं, काम न होने की स्थिति नहीं।",
|
|
69
|
+
noOpReason: "किसी कार्रवाई या रिपोर्ट की आवश्यकता न होने का संक्षिप्त कारण (1–500 अक्षर)।",
|
|
70
|
+
emailNotSent: "ईमेल किसी डिलीवरी प्रदाता द्वारा नहीं भेजा गया।",
|
|
71
|
+
noWork: "ऑटोमेशन किसी पुष्ट कार्रवाई के बिना समाप्त हो गया। केवल पढ़ने वाली रिपोर्ट के लिए डिलीवरी गंतव्य कॉन्फ़िगर करें।",
|
|
72
|
+
emptyDelivery: "ऑटोमेशन ने अपने कॉन्फ़िगर किए गए डिलीवरी गंतव्य के लिए कोई संदेश नहीं बनाया।",
|
|
73
|
+
},
|
|
74
|
+
"ar-SA": {
|
|
75
|
+
noOpInstruction: "إذا لم يكن هناك ما يجب فعله، استدعِ automation-no-op مع سبب موجز. فشل الأدوات أو الإرسال أو غياب بيانات الاعتماد أخطاء، وليس حالات لا تتطلب عملاً.",
|
|
76
|
+
noOpReason: "سبب موجز لعدم الحاجة إلى إجراء أو تقرير (1–500 حرف).",
|
|
77
|
+
emailNotSent: "لم تُرسل رسالة البريد الإلكتروني عبر مزوّد تسليم.",
|
|
78
|
+
noWork: "انتهت الأتمتة دون إجراء مؤكد. اضبط وجهة تسليم للتقارير المخصصة للقراءة فقط.",
|
|
79
|
+
emptyDelivery: "لم تنتج الأتمتة أي رسالة لوجهة التسليم المحددة.",
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
export function automationOutcomeMessagesForLocale(locale) {
|
|
83
|
+
return AUTOMATION_OUTCOME_MESSAGES[isLocaleCode(locale) ? locale : DEFAULT_LOCALE];
|
|
84
|
+
}
|
|
85
|
+
export async function automationOutcomeMessagesForUser(userEmail) {
|
|
86
|
+
const preference = normalizeLocalizationPreference(userEmail
|
|
87
|
+
? await getUserSetting(userEmail, LOCALIZATION_SETTING_KEY)
|
|
88
|
+
: undefined);
|
|
89
|
+
return automationOutcomeMessagesForLocale(preference.locale);
|
|
90
|
+
}
|
|
@@ -1018,9 +1018,11 @@ declare const messages: {
|
|
|
1018
1018
|
scheduleUnavailableLocalTitle: string;
|
|
1019
1019
|
scheduleUnavailableDisabled: string;
|
|
1020
1020
|
scheduleUnavailableNoScheduler: string;
|
|
1021
|
+
scheduleUnavailableMissingSecret: string;
|
|
1021
1022
|
scheduleUnavailableLocal: string;
|
|
1022
1023
|
scheduleUnavailableDisabledFix: string;
|
|
1023
1024
|
scheduleUnavailableLocalFix: string;
|
|
1025
|
+
scheduleUnavailableMissingSecretFix: string;
|
|
1024
1026
|
scheduleUnavailableFixLabel: string;
|
|
1025
1027
|
scheduleUnavailableFixLabelOpen: string;
|
|
1026
1028
|
};
|
|
@@ -940,11 +940,13 @@ const messages = {
|
|
|
940
940
|
scheduleUnknownDetail: "The scheduler status check failed, so the next run times below are unconfirmed. Reload to check again.",
|
|
941
941
|
scheduleUnavailableTitle: "Schedules won't run in this deploy",
|
|
942
942
|
scheduleUnavailableLocalTitle: "Schedules don't run in local development",
|
|
943
|
-
scheduleUnavailableDisabled: "This app was built with recurring jobs turned off, so no scheduled automation will fire.
|
|
944
|
-
scheduleUnavailableNoScheduler: "This
|
|
943
|
+
scheduleUnavailableDisabled: "This app was built with recurring jobs turned off, so no scheduled automation will fire. Webhook-triggered automations and Run now still work.",
|
|
944
|
+
scheduleUnavailableNoScheduler: "This deploy has no scheduler, so scheduled automations won't fire, event-triggered automations stay queued, and crashed runs aren't recovered. Webhook-triggered automations and Run now still work.",
|
|
945
|
+
scheduleUnavailableMissingSecret: "This deploy has a scheduler, but {{secret}} isn't set, so every scheduler request is rejected. Scheduled automations won't fire, event-triggered automations stay queued, and crashed runs aren't recovered. Webhook-triggered automations and Run now still work.",
|
|
945
946
|
scheduleUnavailableLocal: "Schedules stay off on a dev machine unless you opt in. Event- and webhook-triggered automations and Run now still work.",
|
|
946
947
|
scheduleUnavailableDisabledFix: "To enable recurring jobs, set AGENT_NATIVE_DISABLE_RECURRING_JOBS=false in the build environment.",
|
|
947
948
|
scheduleUnavailableLocalFix: "Set AGENT_NATIVE_ENABLE_LOCAL_RECURRING_JOBS=true to run schedules on this machine.",
|
|
949
|
+
scheduleUnavailableMissingSecretFix: "Set {{secret}} in this deployment's environment variables, then redeploy.",
|
|
948
950
|
scheduleUnavailableFixLabel: "Show more",
|
|
949
951
|
scheduleUnavailableFixLabelOpen: "Show less",
|
|
950
952
|
},
|
package/dist/mcp/build-server.js
CHANGED
|
@@ -24,6 +24,14 @@ import { MCP_APP_REQUEST_ORIGIN_CSP_SOURCE } from "./embed-app.js";
|
|
|
24
24
|
import { hasMcpOAuthScope, parseMcpOAuthOrgIdClaim, verifyMcpOAuthAccessToken, } from "./oauth-token.js";
|
|
25
25
|
import { mcpToolInputSchema } from "./tool-input-schema.js";
|
|
26
26
|
const PRESERVE_MCP_OBJECT_RESULT = Symbol("preserveMcpObjectResult");
|
|
27
|
+
// A GET action is a query, so its result is the payload the model asked for
|
|
28
|
+
// even when it declares readOnly: false because the read also repairs or caches
|
|
29
|
+
// (Slides get-deck). Collapsing that result into a write confirmation leaves
|
|
30
|
+
// the model with only "<title> is ready.".
|
|
31
|
+
function returnsQueryPayload(entry) {
|
|
32
|
+
return (entry.readOnly === true ||
|
|
33
|
+
(entry.http !== false && entry.http?.method === "GET"));
|
|
34
|
+
}
|
|
27
35
|
function hasVerifiedMcpUserIdentity(identity) {
|
|
28
36
|
return (identity?.identityAssurance === "user" &&
|
|
29
37
|
Boolean(identity.userEmail?.trim()));
|
|
@@ -237,15 +245,10 @@ export function validateMcpDirectoryProfile(config, sourceActions = config.produ
|
|
|
237
245
|
if (unprofiledWidgetReadAction) {
|
|
238
246
|
throw new McpDirectoryProfileValidationError(`[agent-native] MCP directory widget read "${unprofiledWidgetReadAction}" must be listed in its scoped read category.`);
|
|
239
247
|
}
|
|
240
|
-
const widgetTargetNames = Object.keys(profile?.widgetTargets ?? {});
|
|
241
248
|
if (profile && profile.widgets !== false && profile.widgetTargets) {
|
|
242
|
-
const
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
throw new McpDirectoryProfileValidationError(`[agent-native] MCP directory widget target resolvers must match the listed widget actions (missing: ${missingTarget ?? "none"}; unknown: ${unknownTarget ?? "none"}).`);
|
|
246
|
-
}
|
|
247
|
-
if (widgetActionNames.length > 0 && !profile?.widgetTargets) {
|
|
248
|
-
throw new McpDirectoryProfileValidationError("[agent-native] MCP directory widgets require a server-owned target resolver for every widget action.");
|
|
249
|
+
const unknownTarget = Object.keys(profile.widgetTargets).find((name) => !widgetActionNames.includes(name));
|
|
250
|
+
if (unknownTarget) {
|
|
251
|
+
throw new McpDirectoryProfileValidationError(`[agent-native] MCP directory widget target "${unknownTarget}" must name a listed action with an mcpApp resource. Listed widget actions without a target resolver serve as plain tools.`);
|
|
249
252
|
}
|
|
250
253
|
}
|
|
251
254
|
for (const name of widgetReadOnlyActions) {
|
|
@@ -1095,7 +1098,7 @@ function safeUiSegment(value, fallback) {
|
|
|
1095
1098
|
return normalized || fallback;
|
|
1096
1099
|
}
|
|
1097
1100
|
const MCP_APP_RESOURCE_SHELL_VERSION = "shell-v65";
|
|
1098
|
-
const MCP_DIRECTORY_APP_RESOURCE_SHELL_VERSION = "shell-
|
|
1101
|
+
const MCP_DIRECTORY_APP_RESOURCE_SHELL_VERSION = "shell-v68";
|
|
1099
1102
|
export function getMcpDirectoryWidgetResourceUri(appId, fallback = "agent-native") {
|
|
1100
1103
|
const app = safeUiSegment(appId, fallback);
|
|
1101
1104
|
return (versionMcpAppResourceUri(`ui://${app}/${MCP_DIRECTORY_APP_RESOURCE_SHELL_VERSION}`, MCP_DIRECTORY_APP_RESOURCE_SHELL_VERSION)?.uri ?? `ui://${app}/${MCP_DIRECTORY_APP_RESOURCE_SHELL_VERSION}`);
|
|
@@ -1307,6 +1310,14 @@ async function resolveMcpAppResource(config, actionName, entry, requestMeta) {
|
|
|
1307
1310
|
const resource = entry.mcpApp?.resource;
|
|
1308
1311
|
if (!resource)
|
|
1309
1312
|
return null;
|
|
1313
|
+
// Directory widgets open a host pane on every call, so only the profile's
|
|
1314
|
+
// widgetTargets (create/present tools) attach one; a read tool whose action
|
|
1315
|
+
// still carries mcpApp.resource for the non-directory surface must not.
|
|
1316
|
+
const widgetTargets = config.directoryProfile?.widgetTargets;
|
|
1317
|
+
if (config.catalogMode === "directory" &&
|
|
1318
|
+
(!widgetTargets || !Object.hasOwn(widgetTargets, actionName))) {
|
|
1319
|
+
return null;
|
|
1320
|
+
}
|
|
1310
1321
|
const resolvedUri = getMcpAppResourceUri(config, actionName, entry);
|
|
1311
1322
|
if (!resolvedUri)
|
|
1312
1323
|
return null;
|
|
@@ -2251,7 +2262,7 @@ export async function createMCPServerForRequest(config, identity, requestMeta) {
|
|
|
2251
2262
|
const isAppOnlyVisibility = Array.isArray(toolVisibility) &&
|
|
2252
2263
|
toolVisibility.length > 0 &&
|
|
2253
2264
|
toolVisibility.every((v) => v === "app");
|
|
2254
|
-
const structuredResult = (entry
|
|
2265
|
+
const structuredResult = (returnsQueryPayload(entry) ||
|
|
2255
2266
|
entry.mcpApp?.structuredContent === true) &&
|
|
2256
2267
|
actionResultForClient &&
|
|
2257
2268
|
typeof actionResultForClient === "object"
|
|
@@ -2272,7 +2283,7 @@ export async function createMCPServerForRequest(config, identity, requestMeta) {
|
|
|
2272
2283
|
const text = mcpAppResource
|
|
2273
2284
|
? conciseMcpAppToolText(name, textResultForClient, structuredContent)
|
|
2274
2285
|
: conciseToolResultText(name, textResultForClient, {
|
|
2275
|
-
preserveObjectResult: entry
|
|
2286
|
+
preserveObjectResult: returnsQueryPayload(entry) ||
|
|
2276
2287
|
entry[PRESERVE_MCP_OBJECT_RESULT] ===
|
|
2277
2288
|
true,
|
|
2278
2289
|
});
|