@agent-native/core 0.144.3 → 0.145.1
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/corpus/README.md +1 -1
- package/corpus/templates/clips/changelog/2026-08-06-desktop-full-screen-recordings-now-normalize-microphone-leve.md +6 -0
- package/corpus/templates/clips/desktop/src-tauri/src/native_screen/custom_capture.rs +185 -69
- package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +47 -4
- package/corpus/templates/forms/server/plugins/agent-chat.ts +1 -1
- package/corpus/templates/slides/server/plugins/agent-chat.ts +1 -1
- package/dist/agent/production-agent.d.ts +6 -0
- package/dist/agent/run-manager.d.ts +44 -1
- package/dist/agent/run-manager.js +70 -8
- package/dist/chat-threads/store.js +6 -4
- package/dist/client/use-db-sync.js +9 -1
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/db/ddl-guard.d.ts +8 -0
- package/dist/db/ddl-guard.js +148 -0
- package/dist/deploy/build.d.ts +36 -0
- package/dist/deploy/build.js +78 -5
- package/dist/framework-tools.d.ts +126 -0
- package/dist/framework-tools.js +114 -0
- package/dist/notifications/routes.d.ts +3 -3
- package/dist/org/accept-pending.js +2 -2
- package/dist/org/auth-policy.js +4 -0
- package/dist/org/auto-join-domain.d.ts +9 -0
- package/dist/org/auto-join-domain.js +53 -4
- package/dist/org/context.js +12 -3
- package/dist/org/handlers.js +17 -5
- package/dist/org/request-org-cache.d.ts +24 -2
- package/dist/org/request-org-cache.js +57 -2
- package/dist/provider-api/actions/custom-provider-registration.d.ts +6 -6
- package/dist/provider-api/actions/provider-api.d.ts +6 -6
- package/dist/resources/store.d.ts +1 -1
- package/dist/resources/store.js +96 -7
- package/dist/secrets/routes.d.ts +9 -9
- package/dist/server/action-discovery.d.ts +27 -0
- package/dist/server/action-discovery.js +89 -0
- package/dist/server/agent-chat/action-filters-a2a.d.ts +11 -0
- package/dist/server/agent-chat/action-filters-a2a.js +17 -1
- package/dist/server/agent-chat/framework-prompts.d.ts +2 -0
- package/dist/server/agent-chat/plugin-options.d.ts +30 -0
- package/dist/server/agent-chat/prompt-resources.d.ts +4 -1
- package/dist/server/agent-chat/prompt-resources.js +8 -2
- package/dist/server/agent-chat-plugin.js +138 -75
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/auth.d.ts +3 -0
- package/dist/server/auth.js +22 -1
- package/dist/server/collab-plugin.js +10 -4
- package/dist/server/core-routes-plugin.js +3 -0
- package/dist/server/prompts/framework-core-compact.d.ts +5 -0
- package/dist/server/prompts/framework-core-compact.js +28 -12
- package/dist/server/prompts/framework-core.d.ts +5 -0
- package/dist/server/prompts/framework-core.js +33 -16
- package/dist/server/prompts/index.d.ts +1 -1
- package/dist/server/prompts/index.js +1 -1
- package/dist/server/prompts/shared-rules.d.ts +12 -4
- package/dist/server/prompts/shared-rules.js +14 -4
- package/dist/server/session-email-cache.d.ts +14 -0
- package/dist/server/session-email-cache.js +48 -0
- package/dist/server/sweep-runtime.d.ts +22 -0
- package/dist/server/sweep-runtime.js +28 -0
- package/dist/settings/store.js +63 -7
- package/dist/shared/ttl-cache.d.ts +59 -0
- package/dist/shared/ttl-cache.js +43 -0
- package/dist/sharing/actions/list-resource-shares.d.ts +3 -3
- package/dist/sharing/actions/list-resource-shares.js +4 -1
- package/dist/templates/workspace-core/.agents/skills/extensions/SKILL.md +6 -3
- package/dist/triggers/condition-evaluator.js +11 -27
- package/docs/content/deployment.mdx +95 -13
- package/docs/content/environment-variables.mdx +5 -0
- package/docs/content/key-concepts.mdx +13 -6
- package/package.json +1 -1
- package/src/templates/workspace-core/.agents/skills/extensions/SKILL.md +6 -3
|
@@ -24,6 +24,7 @@ import { adoptThreadScopeIfUnscoped, createThread, forkThread, getThread, regist
|
|
|
24
24
|
import { isCheckpointRestorePath } from "../checkpoints/route-match.js";
|
|
25
25
|
import { createDbAdminAgentTools } from "../db-admin/agent-tools.js";
|
|
26
26
|
import { isTransientDatabaseError } from "../db/client.js";
|
|
27
|
+
import { filterFrameworkToolGroups, resolveFrameworkTools, } from "../framework-tools.js";
|
|
27
28
|
import { verifyInternalToken, extractBearerToken, } from "../integrations/internal-token.js";
|
|
28
29
|
import { RECURRING_JOBS_SWEEP_PATH, RECURRING_JOBS_SWEEP_TOKEN_SUBJECT, } from "../jobs/scheduler-dispatch.js";
|
|
29
30
|
import { McpClientManager, loadMcpConfig, autoDetectMcpConfig, mcpToolsToActionEntries, syncMcpActionEntries, mountMcpServersRoutes, mountMcpHubRoutes, buildMergedConfig, startMcpConfigRefresh, getHubStatus, isHubServeEnabled, } from "../mcp-client/index.js";
|
|
@@ -116,6 +117,7 @@ import { isLocalhost, shouldBlockInProductCodeEditingSurface, } from "./agent-ch
|
|
|
116
117
|
import { loadRunCodeToolEntries } from "./agent-chat/run-code-tools.js";
|
|
117
118
|
import { createAgentEngineScriptEntries, createAgentLoopSettingsScriptEntries, createCallAgentScriptEntry, createChatScriptEntries, createDbScriptEntries, createDocsScriptEntries, createResourceScriptEntries, } from "./agent-chat/script-entries.js";
|
|
118
119
|
import { parseSkillFrontmatter } from "./agent-chat/skill-frontmatter.js";
|
|
120
|
+
import { shouldDisableInProcessSweeps } from "./sweep-runtime.js";
|
|
119
121
|
export { loadResourcesForPrompt };
|
|
120
122
|
export { _agentChatPromptSectionsForTests };
|
|
121
123
|
export { buildPublicAgentA2ASkills };
|
|
@@ -277,12 +279,19 @@ export function createAgentChatPlugin(options) {
|
|
|
277
279
|
// `canToggle` means "this environment allows toggling" (static); this
|
|
278
280
|
// function means "the user currently has dev mode ON" (live).
|
|
279
281
|
const isDevMode = () => currentDevMode;
|
|
282
|
+
// Resolve the framework's own tool surface once, folding the deprecated
|
|
283
|
+
// `databaseTools` / `extensionTools` into `frameworkTools` so every
|
|
284
|
+
// consumer below reads a single shape. Conflicting old/new values throw
|
|
285
|
+
// here rather than booting with a surface nobody chose.
|
|
286
|
+
const frameworkTools = resolveFrameworkTools(options);
|
|
287
|
+
const disabledFrameworkGroups = frameworkTools.disabledGroups;
|
|
280
288
|
// Build the four assembled system prompt strings. These are static for the
|
|
281
289
|
// lifetime of this plugin instance — examples come from options once at
|
|
282
290
|
// startup, not per-request.
|
|
283
291
|
const { PROD_FRAMEWORK_PROMPT, DEV_FRAMEWORK_PROMPT, PROD_FRAMEWORK_PROMPT_COMPACT, DEV_FRAMEWORK_PROMPT_COMPACT, } = buildFrameworkPrompts(options?.promptExamples, {
|
|
284
|
-
databaseTools:
|
|
285
|
-
extensionTools:
|
|
292
|
+
databaseTools: frameworkTools.database,
|
|
293
|
+
extensionTools: frameworkTools.extensions,
|
|
294
|
+
disabledFrameworkGroups,
|
|
286
295
|
});
|
|
287
296
|
// Route readiness must not wait on settings scans, remote hub fetches, or
|
|
288
297
|
// third-party MCP handshakes. Build the action surface against an empty
|
|
@@ -397,13 +406,23 @@ export function createAgentChatPlugin(options) {
|
|
|
397
406
|
catch {
|
|
398
407
|
// Package action registration is optional.
|
|
399
408
|
}
|
|
400
|
-
// Resource, chat, docs, db, and cross-agent scripts are available in both
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
409
|
+
// Resource, chat, docs, db, and cross-agent scripts are available in both
|
|
410
|
+
// prod and dev modes, unless the app switched the group off through
|
|
411
|
+
// `frameworkTools`. Gating at construction is deliberate: an empty map
|
|
412
|
+
// flows through all thirteen registry composition sites below, whereas a
|
|
413
|
+
// condition at each spread site is the same omission waiting to happen
|
|
414
|
+
// thirteen times. Matching HTTP routes are mounted from `httpActions`,
|
|
415
|
+
// which is built from the ungated sets so the UI keeps working.
|
|
416
|
+
const resourceScripts = frameworkTools.isEnabled("resources")
|
|
417
|
+
? await createResourceScriptEntries()
|
|
418
|
+
: {};
|
|
419
|
+
const docsScripts = frameworkTools.isEnabled("docs")
|
|
420
|
+
? await createDocsScriptEntries()
|
|
421
|
+
: {};
|
|
422
|
+
const databaseToolsMode = normalizeDatabaseToolsMode(frameworkTools.database);
|
|
404
423
|
const databaseToolsEnabled = databaseToolsMode !== "off";
|
|
405
424
|
const databaseWriteToolsEnabled = databaseToolsMode === "write";
|
|
406
|
-
const extensionToolsEnabled =
|
|
425
|
+
const extensionToolsEnabled = frameworkTools.extensions;
|
|
407
426
|
const dbScripts = databaseToolsEnabled
|
|
408
427
|
? await createDbScriptEntries(databaseToolsMode, {
|
|
409
428
|
extensionTools: extensionToolsEnabled,
|
|
@@ -417,12 +436,20 @@ export function createAgentChatPlugin(options) {
|
|
|
417
436
|
const urlTools = createUrlTools();
|
|
418
437
|
const engineScripts = await createAgentEngineScriptEntries(options?.appId);
|
|
419
438
|
const loopSettingsScripts = await createAgentLoopSettingsScriptEntries();
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
439
|
+
// `httpActions` spreads engineScripts/loopSettingsScripts directly, so
|
|
440
|
+
// gating here removes them from the agent's tools without unmounting the
|
|
441
|
+
// routes the settings UI calls.
|
|
442
|
+
const chatScripts = frameworkTools.isEnabled("chat")
|
|
443
|
+
? {
|
|
444
|
+
...(await createChatScriptEntries()),
|
|
445
|
+
...engineScripts,
|
|
446
|
+
...loopSettingsScripts,
|
|
447
|
+
}
|
|
448
|
+
: {};
|
|
449
|
+
// Both `call-agent` and `describe-workspace-apps` come from this factory.
|
|
450
|
+
const callAgentScript = frameworkTools.isEnabled("workspaceApps")
|
|
451
|
+
? await createCallAgentScriptEntry(options?.appId)
|
|
452
|
+
: {};
|
|
426
453
|
let runNowSchedulerDeps = null;
|
|
427
454
|
const browserTools = createBuilderBrowserTool({
|
|
428
455
|
getOrigin: () => getRequestRunContext()?.requestOrigin ?? "http://localhost:3000",
|
|
@@ -582,7 +609,13 @@ export function createAgentChatPlugin(options) {
|
|
|
582
609
|
// surface, prompt, MCP/A2A list, and job/trigger runner below. The full
|
|
583
610
|
// `*All` sets are reserved for `httpActions`, which keeps agent-hidden
|
|
584
611
|
// actions reachable from the frontend / HTTP.
|
|
585
|
-
|
|
612
|
+
//
|
|
613
|
+
// The framework's own action kits (sharing, review, history, flags, …)
|
|
614
|
+
// arrive in this same registry through `autoDiscoverActions`, tagged with
|
|
615
|
+
// `frameworkGroup`. Subtracting disabled groups HERE is what removes them
|
|
616
|
+
// from every downstream agent surface at once; `httpActions` re-merges
|
|
617
|
+
// them ungated, so the share dialog and review threads keep their routes.
|
|
618
|
+
const templateScripts = filterFrameworkToolGroups(filterAgentTools(templateScriptsAll), disabledFrameworkGroups);
|
|
586
619
|
// Compact is the safe default for every app, including generated and
|
|
587
620
|
// third-party apps that have not curated a starter list yet. Keep the
|
|
588
621
|
// app's own action surface immediately callable; framework, provider,
|
|
@@ -592,7 +625,7 @@ export function createAgentChatPlugin(options) {
|
|
|
592
625
|
// (below, after the run-code tools are built) additionally folds in
|
|
593
626
|
// whatever `generateCorpusToolsPrompt` teaches by name for this request.
|
|
594
627
|
const templateInitialToolNames = resolveInitialToolNames(templateScripts, options?.initialToolNames);
|
|
595
|
-
const discoveredActions = filterAgentTools(discoveredActionsAll);
|
|
628
|
+
const discoveredActions = filterFrameworkToolGroups(filterAgentTools(discoveredActionsAll), disabledFrameworkGroups);
|
|
596
629
|
// Per-request owner is read from the AsyncLocalStorage run context
|
|
597
630
|
// (populated by prepareRun). Module-scope `let` would race across
|
|
598
631
|
// concurrent requests on a long-lived Node process — overlapping
|
|
@@ -628,22 +661,29 @@ export function createAgentChatPlugin(options) {
|
|
|
628
661
|
// Automation tools + fetch tool — depend on owner via callback.
|
|
629
662
|
// Each callback short-circuits with a clear error when the run context
|
|
630
663
|
// has no authenticated owner (see SECURITY note on getCurrentRunOwner).
|
|
664
|
+
const automationGroupEnabled = frameworkTools.isEnabled("automation");
|
|
631
665
|
let automationTools = {};
|
|
632
666
|
try {
|
|
633
|
-
|
|
634
|
-
|
|
667
|
+
if (automationGroupEnabled) {
|
|
668
|
+
const { createAutomationToolEntries } = await import("../triggers/actions.js");
|
|
669
|
+
automationTools = createAutomationToolEntries(() => requireCurrentRunOwner("manage automations"));
|
|
670
|
+
}
|
|
635
671
|
}
|
|
636
672
|
catch { }
|
|
637
673
|
let notificationTools = {};
|
|
638
674
|
try {
|
|
639
|
-
|
|
640
|
-
|
|
675
|
+
if (automationGroupEnabled) {
|
|
676
|
+
const { createNotificationToolEntries } = await import("../notifications/actions.js");
|
|
677
|
+
notificationTools = createNotificationToolEntries(() => requireCurrentRunOwner("manage notifications"));
|
|
678
|
+
}
|
|
641
679
|
}
|
|
642
680
|
catch { }
|
|
643
681
|
let progressTools = {};
|
|
644
682
|
try {
|
|
645
|
-
|
|
646
|
-
|
|
683
|
+
if (automationGroupEnabled) {
|
|
684
|
+
const { createProgressToolEntries } = await import("../progress/actions.js");
|
|
685
|
+
progressTools = createProgressToolEntries(() => requireCurrentRunOwner("manage progress"));
|
|
686
|
+
}
|
|
647
687
|
}
|
|
648
688
|
catch { }
|
|
649
689
|
let githubRepoTools = {};
|
|
@@ -661,48 +701,53 @@ export function createAgentChatPlugin(options) {
|
|
|
661
701
|
});
|
|
662
702
|
}
|
|
663
703
|
catch { }
|
|
704
|
+
const webGroupEnabled = frameworkTools.isEnabled("web");
|
|
664
705
|
let fetchTool = {};
|
|
665
706
|
try {
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
const
|
|
686
|
-
|
|
687
|
-
|
|
707
|
+
if (webGroupEnabled) {
|
|
708
|
+
const { createFetchToolEntry } = await import("../extensions/fetch-tool.js");
|
|
709
|
+
// Resolve `${keys.NAME}` through the same request-scope cascade
|
|
710
|
+
// already used by extension fetches (extensions/routes.ts) and
|
|
711
|
+
// automation connector headers (automation/index.ts): user scope
|
|
712
|
+
// first (personal overrides win), then the active org scope (the
|
|
713
|
+
// Dispatch vault syncs workspace secrets here), then workspace
|
|
714
|
+
// scope. Org/workspace vault rows are write-gated (org-admin +
|
|
715
|
+
// Dispatch vault UI), so this is safe to read by default — unlike
|
|
716
|
+
// the opt-in-only user→workspace fallback in resolveKeyReferences
|
|
717
|
+
// (see audit 05 H2 in secrets/substitution.ts), which stays off.
|
|
718
|
+
// Previously this tool only looked at scope "user", so a
|
|
719
|
+
// ${keys.NAME} reference to a key synced into the org/workspace
|
|
720
|
+
// vault could never resolve here even though the same key already
|
|
721
|
+
// worked for extension fetches and automations.
|
|
722
|
+
const { resolveKeyReferencesWithRequestScopes, validateUrlAllowlist, getKeyAllowlist, getResolvedKeyAllowlist, } = await import("../secrets/substitution.js");
|
|
723
|
+
fetchTool = createFetchToolEntry({
|
|
724
|
+
resolveKeys: async (text) => resolveKeyReferencesWithRequestScopes(text, requireCurrentRunOwner("resolve key references")),
|
|
725
|
+
validateUrl: async (url, usedKeys, resolvedKeys) => {
|
|
726
|
+
for (const keyName of usedKeys) {
|
|
727
|
+
const allowlist = await resolveFetchToolKeyAllowlist(keyName, resolvedKeys, requireCurrentRunOwner("validate URL allowlist"), { getKeyAllowlist, getResolvedKeyAllowlist });
|
|
728
|
+
if (allowlist && !validateUrlAllowlist(url, allowlist)) {
|
|
729
|
+
return false;
|
|
730
|
+
}
|
|
688
731
|
}
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
}
|
|
692
|
-
}
|
|
732
|
+
return true;
|
|
733
|
+
},
|
|
734
|
+
});
|
|
735
|
+
}
|
|
693
736
|
}
|
|
694
737
|
catch { }
|
|
695
738
|
let webSearchTool = {};
|
|
696
739
|
try {
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
740
|
+
if (webGroupEnabled) {
|
|
741
|
+
const { createWebSearchToolEntry } = await import("../extensions/web-search-tool.js");
|
|
742
|
+
const { getBuilderWebSearchBaseUrl, resolveBuilderCredentials, resolveSecret, } = await import("./credential-provider.js");
|
|
743
|
+
const { getBuilderGatewayRequestHeaders } = await import("../agent/engine/builder-gateway-headers.js");
|
|
744
|
+
webSearchTool = createWebSearchToolEntry({
|
|
745
|
+
resolveSecret,
|
|
746
|
+
resolveBuilderCredentials,
|
|
747
|
+
getBuilderWebSearchBaseUrl,
|
|
748
|
+
getBuilderRequestHeaders: getBuilderGatewayRequestHeaders,
|
|
749
|
+
});
|
|
750
|
+
}
|
|
706
751
|
}
|
|
707
752
|
catch { }
|
|
708
753
|
let workspaceFilesTool = {};
|
|
@@ -752,11 +797,13 @@ export function createAgentChatPlugin(options) {
|
|
|
752
797
|
let coreEmailTools = {};
|
|
753
798
|
let backgroundCoreEmailTools = {};
|
|
754
799
|
try {
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
800
|
+
if (frameworkTools.isEnabled("email")) {
|
|
801
|
+
const { createCoreEmailActionEntries } = await import("./email-actions.js");
|
|
802
|
+
coreEmailTools = createCoreEmailActionEntries();
|
|
803
|
+
backgroundCoreEmailTools = createCoreEmailActionEntries({
|
|
804
|
+
unattended: true,
|
|
805
|
+
});
|
|
806
|
+
}
|
|
760
807
|
}
|
|
761
808
|
catch { }
|
|
762
809
|
// Core read-attachment tool — always registered so the agent can page
|
|
@@ -1233,7 +1280,7 @@ export function createAgentChatPlugin(options) {
|
|
|
1233
1280
|
const devActive = isDevMode();
|
|
1234
1281
|
// Build the same system prompt the interactive agent uses
|
|
1235
1282
|
const owner = userEmail;
|
|
1236
|
-
const resources = await loadResourcesForPrompt(owner, lazyContext, options?.appId);
|
|
1283
|
+
const resources = await loadResourcesForPrompt(owner, lazyContext, options?.appId, undefined, { disabledFrameworkGroups });
|
|
1237
1284
|
const schemaBlock = lazyContext
|
|
1238
1285
|
? ""
|
|
1239
1286
|
: await buildSchemaBlock(owner, databaseToolsMode);
|
|
@@ -1701,7 +1748,7 @@ export function createAgentChatPlugin(options) {
|
|
|
1701
1748
|
// catalog on its very first request, undermining the compact MCP
|
|
1702
1749
|
// catalog this surface is supposed to keep external callers on.
|
|
1703
1750
|
const mcpToolSurface = createA2AEngineToolSurface(actionsToEngineTools(mcpActions), effectiveInitialToolNames);
|
|
1704
|
-
const resources = await loadResourcesForPrompt(SHARED_OWNER, lazyContext, options?.appId);
|
|
1751
|
+
const resources = await loadResourcesForPrompt(SHARED_OWNER, lazyContext, options?.appId, undefined, { disabledFrameworkGroups });
|
|
1705
1752
|
const schemaBlock = lazyContext
|
|
1706
1753
|
? ""
|
|
1707
1754
|
: await buildSchemaBlock(SHARED_OWNER, databaseToolsMode);
|
|
@@ -2156,8 +2203,10 @@ export function createAgentChatPlugin(options) {
|
|
|
2156
2203
|
// Job management tool (manage-jobs)
|
|
2157
2204
|
let jobTools = {};
|
|
2158
2205
|
try {
|
|
2159
|
-
|
|
2160
|
-
|
|
2206
|
+
if (automationGroupEnabled) {
|
|
2207
|
+
const { createJobTools } = await import("../jobs/tools.js");
|
|
2208
|
+
jobTools = createJobTools();
|
|
2209
|
+
}
|
|
2161
2210
|
}
|
|
2162
2211
|
catch { }
|
|
2163
2212
|
// Lean mode: only template actions + essential framework tools. Drop
|
|
@@ -2487,7 +2536,7 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
|
|
|
2487
2536
|
modelOverlay +
|
|
2488
2537
|
runtimeContext);
|
|
2489
2538
|
}
|
|
2490
|
-
const resources = await loadResourcesForPrompt(owner, lazyContext, options?.appId);
|
|
2539
|
+
const resources = await loadResourcesForPrompt(owner, lazyContext, options?.appId, undefined, { disabledFrameworkGroups });
|
|
2491
2540
|
// In lazy context mode, skip embedding the full schema. When database
|
|
2492
2541
|
// tools are enabled the agent can call `db-schema` on demand.
|
|
2493
2542
|
const schemaBlock = lazyContext
|
|
@@ -2720,7 +2769,7 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
|
|
|
2720
2769
|
});
|
|
2721
2770
|
return setSystemPromptOnContext(leanBasePrompt + extra + modelOverlay + runtimeContext);
|
|
2722
2771
|
}
|
|
2723
|
-
const resources = await loadResourcesForPrompt(owner, lazyContext, options?.appId);
|
|
2772
|
+
const resources = await loadResourcesForPrompt(owner, lazyContext, options?.appId, undefined, { disabledFrameworkGroups });
|
|
2724
2773
|
const schemaBlock = lazyContext || !databaseToolsEnabled
|
|
2725
2774
|
? ""
|
|
2726
2775
|
: await buildSchemaBlock(owner, databaseToolsMode);
|
|
@@ -4687,6 +4736,17 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
|
|
|
4687
4736
|
}));
|
|
4688
4737
|
const isBackgroundRuntime = isInBackgroundFunctionRuntime();
|
|
4689
4738
|
const disableRecurringJobsRuntime = isBackgroundRuntime || shouldDisableRecurringJobsRuntime();
|
|
4739
|
+
// Opt-in kill switch for the backstop sweep timers below. On serverless
|
|
4740
|
+
// these are billed per warm container, so their query rate scales with
|
|
4741
|
+
// instance count rather than with anything happening. See
|
|
4742
|
+
// `shouldDisableInProcessSweeps`.
|
|
4743
|
+
const sweepsDisabled = shouldDisableInProcessSweeps();
|
|
4744
|
+
if (sweepsDisabled) {
|
|
4745
|
+
console.log("[agent-native] In-process backstop sweeps disabled " +
|
|
4746
|
+
"(AGENT_NATIVE_DISABLE_INPROCESS_SWEEPS). A durable scheduler must " +
|
|
4747
|
+
"drive automation redispatch, agent-teams reconciliation, and " +
|
|
4748
|
+
"sandbox-execution recovery, or queued work will not be retried.");
|
|
4749
|
+
}
|
|
4690
4750
|
// ─── Recurring Jobs Scheduler ──────────────────────────────────────
|
|
4691
4751
|
// Poll every 60 seconds for due recurring jobs and execute them.
|
|
4692
4752
|
// Uses setInterval so it works in all deployment environments without
|
|
@@ -4696,7 +4756,7 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
|
|
|
4696
4756
|
const schedulerDeps = {
|
|
4697
4757
|
getActions: getBackgroundActionEntries,
|
|
4698
4758
|
getSystemPrompt: async (owner) => {
|
|
4699
|
-
const resources = await loadResourcesForPrompt(owner, lazyContext, options?.appId);
|
|
4759
|
+
const resources = await loadResourcesForPrompt(owner, lazyContext, options?.appId, undefined, { disabledFrameworkGroups });
|
|
4700
4760
|
const schemaBlock = lazyContext
|
|
4701
4761
|
? ""
|
|
4702
4762
|
: await buildSchemaBlock(owner, databaseToolsMode);
|
|
@@ -4712,8 +4772,11 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
|
|
|
4712
4772
|
// fetchTool/webSearchTool/toolActions catalog every tick.
|
|
4713
4773
|
getInitialToolNames: (job) => [
|
|
4714
4774
|
...effectiveInitialToolNames,
|
|
4715
|
-
|
|
4716
|
-
|
|
4775
|
+
// Only promotable while the group that supplies them is on; with
|
|
4776
|
+
// `automation` off the prompt no longer names them either.
|
|
4777
|
+
...(automationGroupEnabled
|
|
4778
|
+
? ["manage-jobs", "manage-progress"]
|
|
4779
|
+
: []),
|
|
4717
4780
|
...(job?.meta.mcpTools ?? []),
|
|
4718
4781
|
],
|
|
4719
4782
|
apiKey: options?.apiKey,
|
|
@@ -4781,7 +4844,7 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
|
|
|
4781
4844
|
// A non-Netlify self-dispatch only waits for the request to leave the
|
|
4782
4845
|
// current invocation. Keep the durable history row as a retryable queue
|
|
4783
4846
|
// so a frozen serverless handoff is recovered on the next sweep.
|
|
4784
|
-
if (!isBackgroundRuntime) {
|
|
4847
|
+
if (!isBackgroundRuntime && !sweepsDisabled) {
|
|
4785
4848
|
(() => {
|
|
4786
4849
|
let inFlight = false;
|
|
4787
4850
|
const sweep = async () => {
|
|
@@ -4813,7 +4876,7 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
|
|
|
4813
4876
|
// per instance; cheap (one indexed query when no active tasks are found).
|
|
4814
4877
|
// Throttled by the same per-owner interval guard inside reconcileAgentTeamRunsForOwner.
|
|
4815
4878
|
(() => {
|
|
4816
|
-
if (isBackgroundRuntime)
|
|
4879
|
+
if (isBackgroundRuntime || sweepsDisabled)
|
|
4817
4880
|
return;
|
|
4818
4881
|
// Track when this instance last ran the sweep so only one sweep fires
|
|
4819
4882
|
// per 2-min window even if multiple timers fire in overlapping invocations.
|
|
@@ -4962,7 +5025,7 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
|
|
|
4962
5025
|
// comment above for why this exists and the timing budget in
|
|
4963
5026
|
// run-store.ts's `UNCLAIMED_BACKGROUND_RUN_FAST_SWEEP_MS` doc comment.
|
|
4964
5027
|
(() => {
|
|
4965
|
-
if (isBackgroundRuntime)
|
|
5028
|
+
if (isBackgroundRuntime || sweepsDisabled)
|
|
4966
5029
|
return;
|
|
4967
5030
|
setTimeout(() => {
|
|
4968
5031
|
(async () => {
|
|
@@ -5005,7 +5068,7 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
|
|
|
5005
5068
|
// still fails loud, and it survives the fast sweep having missed a row
|
|
5006
5069
|
// entirely (e.g. a restart landed between fast-sweep ticks).
|
|
5007
5070
|
(() => {
|
|
5008
|
-
if (isBackgroundRuntime)
|
|
5071
|
+
if (isBackgroundRuntime || sweepsDisabled)
|
|
5009
5072
|
return;
|
|
5010
5073
|
let lastSweep = 0;
|
|
5011
5074
|
const SWEEP_INTERVAL_MS = 2 * 60 * 1000;
|
|
@@ -5073,7 +5136,7 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
|
|
|
5073
5136
|
await initTriggerDispatcher({
|
|
5074
5137
|
getActions: getBackgroundActionEntries,
|
|
5075
5138
|
getSystemPrompt: async (owner) => {
|
|
5076
|
-
const resources = await loadResourcesForPrompt(owner, lazyContext, options?.appId);
|
|
5139
|
+
const resources = await loadResourcesForPrompt(owner, lazyContext, options?.appId, undefined, { disabledFrameworkGroups });
|
|
5077
5140
|
const schemaBlock = lazyContext
|
|
5078
5141
|
? ""
|
|
5079
5142
|
: await buildSchemaBlock(owner, databaseToolsMode);
|
|
@@ -27,10 +27,10 @@ export declare function resolveAgentEngineApiKeyWriteTarget(event: H3Event, scop
|
|
|
27
27
|
export declare function createAgentEngineApiKeyHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
28
28
|
error: any;
|
|
29
29
|
} | {
|
|
30
|
+
error?: undefined;
|
|
30
31
|
ok: boolean;
|
|
31
32
|
key: string;
|
|
32
33
|
baseUrlKey?: string;
|
|
33
34
|
scope: AgentEngineApiKeyScope;
|
|
34
|
-
error?: undefined;
|
|
35
35
|
}>>;
|
|
36
36
|
export {};
|
package/dist/server/auth.d.ts
CHANGED
|
@@ -226,6 +226,9 @@ export declare function removeSession(token: string): Promise<void>;
|
|
|
226
226
|
/**
|
|
227
227
|
* Look up the email associated with a legacy session token.
|
|
228
228
|
* Returns null if the session doesn't exist, is expired, or has no email.
|
|
229
|
+
*
|
|
230
|
+
* Resolutions are cached across requests — see `session-email-cache.ts` for the
|
|
231
|
+
* TTL and the only-cache-successes rule.
|
|
229
232
|
*/
|
|
230
233
|
export declare function getSessionEmail(token: string): Promise<string | null>;
|
|
231
234
|
export interface DesktopExchangeErrorPayload {
|
package/dist/server/auth.js
CHANGED
|
@@ -71,6 +71,7 @@ import { isIdentitySsoEnabled } from "./identity-sso-store.js";
|
|
|
71
71
|
import { safeOAuthReturnUrl } from "./oauth-return-url.js";
|
|
72
72
|
import { getOnboardingHtml, getResetPasswordHtml, } from "./onboarding-html.js";
|
|
73
73
|
import { captureAuthError } from "./sentry.js";
|
|
74
|
+
import { forgetCachedSessionEmail, getCachedSessionEmail, invalidateSessionEmailCache, setCachedSessionEmail, } from "./session-email-cache.js";
|
|
74
75
|
import { isWorkspaceOAuthCallbackRelayEnabled } from "./workspace-oauth.js";
|
|
75
76
|
/**
|
|
76
77
|
* Get the configured session max age. Desktop SSO broker writes from
|
|
@@ -741,6 +742,9 @@ export async function addSession(token, email) {
|
|
|
741
742
|
: `INSERT OR REPLACE INTO sessions (token, email, created_at) VALUES (?, ?, ?)`,
|
|
742
743
|
args: [token, email ?? null, Date.now()],
|
|
743
744
|
}));
|
|
745
|
+
// The upsert can REBIND an existing token to a different email, so a cached
|
|
746
|
+
// resolution for it is now wrong.
|
|
747
|
+
invalidateSessionEmailCache();
|
|
744
748
|
}
|
|
745
749
|
export async function hasLegacySessionForEmail(email) {
|
|
746
750
|
await ensureSessionTable();
|
|
@@ -759,12 +763,20 @@ export async function removeSession(token) {
|
|
|
759
763
|
sql: `DELETE FROM sessions WHERE token = ?`,
|
|
760
764
|
args: [token],
|
|
761
765
|
}));
|
|
766
|
+
// Sign-out must take effect immediately, not after the cache TTL.
|
|
767
|
+
invalidateSessionEmailCache();
|
|
762
768
|
}
|
|
763
769
|
/**
|
|
764
770
|
* Look up the email associated with a legacy session token.
|
|
765
771
|
* Returns null if the session doesn't exist, is expired, or has no email.
|
|
772
|
+
*
|
|
773
|
+
* Resolutions are cached across requests — see `session-email-cache.ts` for the
|
|
774
|
+
* TTL and the only-cache-successes rule.
|
|
766
775
|
*/
|
|
767
776
|
export async function getSessionEmail(token) {
|
|
777
|
+
const cached = getCachedSessionEmail(token);
|
|
778
|
+
if (cached !== undefined)
|
|
779
|
+
return cached;
|
|
768
780
|
await ensureSessionTable();
|
|
769
781
|
const client = getDbExec();
|
|
770
782
|
const { rows } = await retryIfSessionsMissing(() => client.execute({
|
|
@@ -779,9 +791,13 @@ export async function getSessionEmail(token) {
|
|
|
779
791
|
sql: `DELETE FROM sessions WHERE token = ?`,
|
|
780
792
|
args: [token],
|
|
781
793
|
});
|
|
794
|
+
forgetCachedSessionEmail(token);
|
|
782
795
|
return null;
|
|
783
796
|
}
|
|
784
|
-
|
|
797
|
+
const email = rows[0].email ?? null;
|
|
798
|
+
if (email)
|
|
799
|
+
setCachedSessionEmail(token, email);
|
|
800
|
+
return email;
|
|
785
801
|
}
|
|
786
802
|
// ---------------------------------------------------------------------------
|
|
787
803
|
// getSession — the auth contract
|
|
@@ -937,6 +953,7 @@ async function consumeDesktopExchangeFromDB(flowId) {
|
|
|
937
953
|
sql: `DELETE FROM sessions WHERE token = ? AND created_at > ? RETURNING email`,
|
|
938
954
|
args: [`dex:${flowId}`, Date.now() - DESKTOP_EXCHANGE_TTL_MS],
|
|
939
955
|
});
|
|
956
|
+
forgetCachedSessionEmail(`dex:${flowId}`);
|
|
940
957
|
if (rows.length === 0)
|
|
941
958
|
return null;
|
|
942
959
|
const packed = (rows[0].email ?? rows[0][0]);
|
|
@@ -2706,6 +2723,9 @@ async function mountBetterAuthRoutes(app, options) {
|
|
|
2706
2723
|
args: [userEmail],
|
|
2707
2724
|
});
|
|
2708
2725
|
}
|
|
2726
|
+
// Deleted by email, so the removed tokens are unknown here — the
|
|
2727
|
+
// whole cache goes.
|
|
2728
|
+
invalidateSessionEmailCache();
|
|
2709
2729
|
}
|
|
2710
2730
|
}
|
|
2711
2731
|
catch {
|
|
@@ -2939,6 +2959,7 @@ async function mountBetterAuthRoutes(app, options) {
|
|
|
2939
2959
|
catch {
|
|
2940
2960
|
// Best-effort.
|
|
2941
2961
|
}
|
|
2962
|
+
invalidateSessionEmailCache();
|
|
2942
2963
|
// 3. Drop the current request's cookie and best-effort sign out
|
|
2943
2964
|
// of Better Auth (so the response sets the proper expiry header).
|
|
2944
2965
|
clearFrameworkSessionCookies(event);
|
|
@@ -211,8 +211,12 @@ export function createCollabPlugin(options = {}) {
|
|
|
211
211
|
(action === "json" && method === "POST") ||
|
|
212
212
|
(action === "patch" && method === "POST");
|
|
213
213
|
if (isWrite) {
|
|
214
|
-
// assertAccess throws ForbiddenError (→ 403) if no editor access
|
|
215
|
-
|
|
214
|
+
// assertAccess throws ForbiddenError (→ 403) if no editor access.
|
|
215
|
+
// Projected: only ownerEmail/orgId are read below, and a collab
|
|
216
|
+
// resource's body is the largest column it has — loading it here
|
|
217
|
+
// means every keystroke-driven update read the whole document
|
|
218
|
+
// twice, once for the ACL and once for the edit itself.
|
|
219
|
+
const access = await assertAccess(resourceType, resourceId, "editor", undefined, { skipResourceBody: true });
|
|
216
220
|
const resource = access.resource;
|
|
217
221
|
const awarenessScope = {
|
|
218
222
|
resourceType,
|
|
@@ -229,8 +233,10 @@ export function createCollabPlugin(options = {}) {
|
|
|
229
233
|
}
|
|
230
234
|
}
|
|
231
235
|
else {
|
|
232
|
-
// resolveAccess returns null when no access; return 404 to avoid leaking existence
|
|
233
|
-
|
|
236
|
+
// resolveAccess returns null when no access; return 404 to avoid leaking existence.
|
|
237
|
+
// Projected: only ownerEmail/orgId are read below, and a collab
|
|
238
|
+
// resource's body is the largest column it has.
|
|
239
|
+
const access = await resolveAccess(resourceType, resourceId, undefined, { skipResourceBody: true });
|
|
234
240
|
if (!access) {
|
|
235
241
|
setResponseStatus(event, 404);
|
|
236
242
|
return { error: "Not found" };
|
|
@@ -57,6 +57,7 @@ import { createPollHandler } from "./poll.js";
|
|
|
57
57
|
import { createRealtimeTokenHandler } from "./realtime-token.js";
|
|
58
58
|
import { getRequestContext, hasRequestContext, runWithRequestContext, } from "./request-context.js";
|
|
59
59
|
import { findUnsupportedScopedKeyNames, saveKeyValuesToScopedSecrets, ScopedKeyStorageError, } from "./scoped-key-storage.js";
|
|
60
|
+
import { shouldDisableInProcessSweeps } from "./sweep-runtime.js";
|
|
60
61
|
import { createTranscribeVoiceHandler } from "./transcribe-voice.js";
|
|
61
62
|
import { createVoiceProvidersStatusHandler } from "./voice-providers-status.js";
|
|
62
63
|
import { createWorkspaceProviderOAuthHandler } from "./workspace-provider-oauth.js";
|
|
@@ -1260,6 +1261,8 @@ export function createCoreRoutesPlugin(options = {}) {
|
|
|
1260
1261
|
// poll-time drain in run-code covers deployments where warm-instance
|
|
1261
1262
|
// timers rarely fire.
|
|
1262
1263
|
(() => {
|
|
1264
|
+
if (shouldDisableInProcessSweeps())
|
|
1265
|
+
return;
|
|
1263
1266
|
let lastSweep = 0;
|
|
1264
1267
|
const SWEEP_INTERVAL_MS = 2 * 60 * 1000;
|
|
1265
1268
|
setTimeout(() => {
|
|
@@ -8,11 +8,16 @@
|
|
|
8
8
|
* - Detailed "Parallel Tool Calls" prose (replaced with one-liner)
|
|
9
9
|
* - Detailed "Resources" section (agent uses resources tool)
|
|
10
10
|
*/
|
|
11
|
+
import { type FrameworkToolGroup } from "../../framework-tools.js";
|
|
11
12
|
import { type DatabaseToolsOption } from "../../scripts/db/tool-mode.js";
|
|
12
13
|
import { type PromptExamples } from "./shared-rules.js";
|
|
13
14
|
export interface FrameworkCoreCompactPromptOptions {
|
|
14
15
|
databaseTools?: DatabaseToolsOption;
|
|
15
16
|
extensionTools?: boolean;
|
|
17
|
+
/** Framework tool groups this app switched off. Every block below that names
|
|
18
|
+
* a group's tool by name is gated on this — a prompt naming an absent tool
|
|
19
|
+
* makes the model call it, fail, and often report the capability as missing. */
|
|
20
|
+
disabledFrameworkGroups?: ReadonlySet<FrameworkToolGroup>;
|
|
16
21
|
}
|
|
17
22
|
/**
|
|
18
23
|
* Build the compact FRAMEWORK_CORE prompt string.
|