@agent-native/core 0.145.0 → 0.145.2
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/brain/server/lib/capture-sanitization.ts +6 -2
- package/corpus/templates/clips/changelog/2026-08-07-full-screen-recordings-on-a-multi-monitor-mac-now-let-you-pi.md +6 -0
- package/corpus/templates/clips/chrome-extension/src/styles.css +13 -4
- package/corpus/templates/clips/desktop/src/app.tsx +38 -1
- package/corpus/templates/clips/desktop/src/components/MediaDeviceRow.tsx +123 -104
- package/corpus/templates/clips/desktop/src/lib/recorder.ts +165 -2
- package/corpus/templates/clips/desktop/src/main.tsx +3 -0
- package/corpus/templates/clips/desktop/src/overlays/monitor-picker.tsx +70 -0
- package/corpus/templates/clips/desktop/src/styles.css +70 -11
- package/corpus/templates/clips/desktop/src-tauri/capabilities/default.json +2 -1
- package/corpus/templates/clips/desktop/src-tauri/src/clips/mod.rs +163 -1
- package/corpus/templates/clips/desktop/src-tauri/src/lib.rs +6 -1
- package/corpus/templates/clips/desktop/src-tauri/src/native_screen.rs +141 -22
- package/corpus/templates/clips/desktop/src-tauri/src/state.rs +27 -1
- package/corpus/templates/clips/desktop/src-tauri/src/util.rs +12 -1
- package/dist/agent/engine/index.d.ts +1 -1
- package/dist/agent/engine/index.js +1 -1
- package/dist/agent/engine/registry.d.ts +15 -0
- package/dist/agent/engine/registry.js +39 -10
- package/dist/agent/production-agent.d.ts +40 -1
- package/dist/agent/production-agent.js +81 -40
- 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/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/integrations/webhook-handler.d.ts +3 -2
- package/dist/integrations/webhook-handler.js +8 -38
- package/dist/mcp/screen-memory-stdio.d.ts +7 -7
- package/dist/notifications/routes.d.ts +3 -3
- package/dist/observability/routes.d.ts +5 -5
- 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 +4 -4
- 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 +6 -6
- package/dist/server/agent-chat-plugin.js +83 -37
- 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/complete-text.js +14 -7
- package/dist/server/core-routes-plugin.js +3 -0
- package/dist/server/index.d.ts +1 -1
- package/dist/server/index.js +1 -1
- package/dist/server/request-context.d.ts +7 -1
- 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/server/transcribe-voice.d.ts +1 -1
- 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/triggers/condition-evaluator.js +11 -27
- package/docs/content/deployment.mdx +48 -0
- package/docs/content/environment-variables.mdx +11 -0
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
use std::sync::Mutex;
|
|
2
2
|
use std::time::Instant;
|
|
3
|
-
use tauri::Rect;
|
|
3
|
+
use tauri::{AppHandle, Manager, Rect};
|
|
4
4
|
|
|
5
5
|
use crate::tray_meetings::MeetingItem;
|
|
6
6
|
|
|
@@ -63,3 +63,29 @@ pub struct VoiceTargetBundle(pub Mutex<Option<String>>);
|
|
|
63
63
|
/// Last dictation result for "paste last".
|
|
64
64
|
#[derive(Default)]
|
|
65
65
|
pub struct LastTranscript(pub Mutex<Option<String>>);
|
|
66
|
+
|
|
67
|
+
/// CGDirectDisplayID the user picked in the multi-monitor screen picker,
|
|
68
|
+
/// applying for the whole lifetime of the recording it was picked for (not
|
|
69
|
+
/// just the first read) — `tray_display_id` and `tray_monitor_physical_rect`
|
|
70
|
+
/// both read it via `get`. Cleared via `set(app, None)` by `show_monitor_picker`
|
|
71
|
+
/// (next pick), `hide_recording_chrome` (stop), and
|
|
72
|
+
/// `native_fullscreen_recording_cancel` (aborted start) — a stale pick must
|
|
73
|
+
/// never leak into a later recording that skipped the picker (e.g. only one
|
|
74
|
+
/// monitor connected).
|
|
75
|
+
#[derive(Default)]
|
|
76
|
+
pub struct SelectedRecordingDisplay(pub Mutex<Option<u32>>);
|
|
77
|
+
|
|
78
|
+
impl SelectedRecordingDisplay {
|
|
79
|
+
pub fn get(app: &AppHandle) -> Option<u32> {
|
|
80
|
+
app.try_state::<Self>()
|
|
81
|
+
.and_then(|s| s.0.lock().ok().and_then(|g| *g))
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
pub fn set(app: &AppHandle, display_id: Option<u32>) {
|
|
85
|
+
if let Some(state) = app.try_state::<Self>() {
|
|
86
|
+
if let Ok(mut guard) = state.0.lock() {
|
|
87
|
+
*guard = display_id;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
@@ -2,7 +2,8 @@ use tauri::{AppHandle, Emitter, Manager, WebviewUrl, WebviewWindow, WebviewWindo
|
|
|
2
2
|
|
|
3
3
|
use crate::dlog;
|
|
4
4
|
use crate::state::{
|
|
5
|
-
DictationActive, PopoverShownAt, RecordingActive,
|
|
5
|
+
DictationActive, PopoverShownAt, RecordingActive, SelectedRecordingDisplay, TrayAnchor,
|
|
6
|
+
VoiceWakePopover,
|
|
6
7
|
};
|
|
7
8
|
|
|
8
9
|
const POPOVER_SHADOW_GUTTER_LOGICAL: f64 = 24.0;
|
|
@@ -369,6 +370,16 @@ pub fn present_interactive_window(window: &WebviewWindow) {
|
|
|
369
370
|
/// instead of `primary_monitor_physical_size` for any overlay that should appear
|
|
370
371
|
/// on the same screen as the recording.
|
|
371
372
|
pub fn tray_monitor_physical_rect(app: &AppHandle) -> (i32, i32, u32, u32) {
|
|
373
|
+
// A monitor picked in the multi-monitor screen picker wins over the tray
|
|
374
|
+
// icon's monitor for every overlay shown during that recording
|
|
375
|
+
// (countdown, toolbar, finalizing, region guides, ...) — see
|
|
376
|
+
// `SelectedRecordingDisplay`'s doc comment for its lifecycle.
|
|
377
|
+
if let Some(id) = SelectedRecordingDisplay::get(app) {
|
|
378
|
+
if let Some(rect) = crate::native_screen::monitor_rect_for_display_id(app, id) {
|
|
379
|
+
return rect;
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
372
383
|
let tray_rect = app
|
|
373
384
|
.try_state::<TrayAnchor>()
|
|
374
385
|
.and_then(|a| a.0.lock().ok().and_then(|g| *g));
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Public exports for the pluggable agent engine system.
|
|
3
3
|
*/
|
|
4
4
|
export type { AgentEngine, EngineCapabilities, EngineTool, EngineMessage, EngineContentPart, EngineTextPart, EngineImagePart, EngineToolCallPart, EngineToolResultPart, EngineThinkingPart, EngineEvent, EngineStreamOptions, } from "./types.js";
|
|
5
|
-
export { registerAgentEngine, getAgentEngineEntry, listAgentEngines, resolveEngine, getConfiguredEngineNameForRequest, getStoredModelForEngine, normalizeModelForEngine, resolveDelegatedRunModel, resolveEnginePreservesCustomModels, type NormalizeModelOptions, detectEngineFromEnv, detectEngineFromEnvForRequest, detectEngineFromUserSecrets, isAgentEngineSettingConfigured, isAgentEnginePackageInstalled, isStoredEngineUsable, isStoredEngineUsableForRequest, isResolvedEngineUsableForRequest, type AgentEngineEntry, type ResolveEngineConfig, } from "./registry.js";
|
|
5
|
+
export { registerAgentEngine, getAgentEngineEntry, listAgentEngines, resolveEngine, explicitEngineName, getConfiguredEngineNameForRequest, getStoredModelForEngine, normalizeModelForEngine, resolveDelegatedRunModel, resolveEnginePreservesCustomModels, type NormalizeModelOptions, detectEngineFromEnv, detectEngineFromEnvForRequest, detectEngineFromUserSecrets, isAgentEngineSettingConfigured, isAgentEnginePackageInstalled, isStoredEngineUsable, isStoredEngineUsableForRequest, isResolvedEngineUsableForRequest, type AgentEngineEntry, type ResolveEngineConfig, } from "./registry.js";
|
|
6
6
|
export { createBuilderEngine, BUILDER_DEFAULT_MODEL, BUILDER_SUPPORTED_MODELS, BUILDER_CAPABILITIES, } from "./builder-engine.js";
|
|
7
7
|
export { createAnthropicEngine, ANTHROPIC_DEFAULT_MODEL, ANTHROPIC_SUPPORTED_MODELS, ANTHROPIC_CAPABILITIES, } from "./anthropic-engine.js";
|
|
8
8
|
export { createAISDKEngine, type AISDKProvider } from "./ai-sdk-engine.js";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Public exports for the pluggable agent engine system.
|
|
3
3
|
*/
|
|
4
|
-
export { registerAgentEngine, getAgentEngineEntry, listAgentEngines, resolveEngine, getConfiguredEngineNameForRequest, getStoredModelForEngine, normalizeModelForEngine, resolveDelegatedRunModel, resolveEnginePreservesCustomModels, detectEngineFromEnv, detectEngineFromEnvForRequest, detectEngineFromUserSecrets, isAgentEngineSettingConfigured, isAgentEnginePackageInstalled, isStoredEngineUsable, isStoredEngineUsableForRequest, isResolvedEngineUsableForRequest, } from "./registry.js";
|
|
4
|
+
export { registerAgentEngine, getAgentEngineEntry, listAgentEngines, resolveEngine, explicitEngineName, getConfiguredEngineNameForRequest, getStoredModelForEngine, normalizeModelForEngine, resolveDelegatedRunModel, resolveEnginePreservesCustomModels, detectEngineFromEnv, detectEngineFromEnvForRequest, detectEngineFromUserSecrets, isAgentEngineSettingConfigured, isAgentEnginePackageInstalled, isStoredEngineUsable, isStoredEngineUsableForRequest, isResolvedEngineUsableForRequest, } from "./registry.js";
|
|
5
5
|
export { createBuilderEngine, BUILDER_DEFAULT_MODEL, BUILDER_SUPPORTED_MODELS, BUILDER_CAPABILITIES, } from "./builder-engine.js";
|
|
6
6
|
export { createAnthropicEngine, ANTHROPIC_DEFAULT_MODEL, ANTHROPIC_SUPPORTED_MODELS, ANTHROPIC_CAPABILITIES, } from "./anthropic-engine.js";
|
|
7
7
|
export { createAISDKEngine } from "./ai-sdk-engine.js";
|
|
@@ -174,11 +174,26 @@ export interface ResolveEngineConfig {
|
|
|
174
174
|
};
|
|
175
175
|
/** API key (used as config for the resolved engine) */
|
|
176
176
|
apiKey?: string;
|
|
177
|
+
/**
|
|
178
|
+
* Env var name `apiKey` was issued for, when the caller knows it. Declaring
|
|
179
|
+
* it keeps a provider-specific credential from reaching a different
|
|
180
|
+
* provider's engine; omit it for opaque keys.
|
|
181
|
+
*/
|
|
182
|
+
apiKeyEnvVar?: string;
|
|
177
183
|
/** Model override (used as part of engine config) */
|
|
178
184
|
model?: string;
|
|
179
185
|
/** App/template id used for org-scoped per-app model defaults. */
|
|
180
186
|
appId?: string;
|
|
181
187
|
}
|
|
188
|
+
/**
|
|
189
|
+
* Engine name a caller explicitly selected, when {@link resolveEngine} will
|
|
190
|
+
* honor it as a name. Callers resolve the API key before they call
|
|
191
|
+
* `resolveEngine`, so they need the same answer the registry will reach:
|
|
192
|
+
* an untagged "active" key resolved against a different provider's setting
|
|
193
|
+
* would otherwise ride along to whichever engine this names. Returns
|
|
194
|
+
* `undefined` for an engine instance, which carries its own credential.
|
|
195
|
+
*/
|
|
196
|
+
export declare function explicitEngineName(engineOption: ResolveEngineConfig["engineOption"]): string | undefined;
|
|
182
197
|
/**
|
|
183
198
|
* Return the usable engine explicitly selected by the current user/org.
|
|
184
199
|
*
|
|
@@ -559,9 +559,18 @@ async function apiKeyBelongsToDifferentProvider(apiKey, selectedEntry) {
|
|
|
559
559
|
}
|
|
560
560
|
return false;
|
|
561
561
|
}
|
|
562
|
-
async function engineCreateConfigForEntry(entry, apiKey, extra, preferResolvedCredential = false) {
|
|
562
|
+
async function engineCreateConfigForEntry(entry, apiKey, extra, preferResolvedCredential = false, apiKeyEnvVar) {
|
|
563
563
|
const safeExtra = { ...(extra ?? {}) };
|
|
564
564
|
let matchingApiKey = apiKey;
|
|
565
|
+
// A declared provenance settles the question without inspecting values: a
|
|
566
|
+
// credential issued for another provider's env var is never this entry's
|
|
567
|
+
// key, so drop it on explicit branches too. Value comparison below cannot
|
|
568
|
+
// cover this — a host-supplied key (plugin `options.apiKey`) matches no
|
|
569
|
+
// stored secret, so it would otherwise reach whichever provider was picked.
|
|
570
|
+
if (apiKeyEnvVar !== undefined &&
|
|
571
|
+
!entry.requiredEnvVars.includes(apiKeyEnvVar)) {
|
|
572
|
+
matchingApiKey = undefined;
|
|
573
|
+
}
|
|
565
574
|
// Automatic engine selection must also select that engine's credential.
|
|
566
575
|
// Callers historically passed one untagged "active" key before the registry
|
|
567
576
|
// chose an engine, which could hand an Anthropic key to an app-default
|
|
@@ -687,6 +696,26 @@ export async function isResolvedEngineUsableForRequest(engine, options = {}) {
|
|
|
687
696
|
}
|
|
688
697
|
return true;
|
|
689
698
|
}
|
|
699
|
+
/**
|
|
700
|
+
* Engine name a caller explicitly selected, when {@link resolveEngine} will
|
|
701
|
+
* honor it as a name. Callers resolve the API key before they call
|
|
702
|
+
* `resolveEngine`, so they need the same answer the registry will reach:
|
|
703
|
+
* an untagged "active" key resolved against a different provider's setting
|
|
704
|
+
* would otherwise ride along to whichever engine this names. Returns
|
|
705
|
+
* `undefined` for an engine instance, which carries its own credential.
|
|
706
|
+
*/
|
|
707
|
+
export function explicitEngineName(engineOption) {
|
|
708
|
+
if (!engineOption)
|
|
709
|
+
return undefined;
|
|
710
|
+
if (typeof engineOption === "string")
|
|
711
|
+
return engineOption;
|
|
712
|
+
if (typeof engineOption === "object" &&
|
|
713
|
+
!("stream" in engineOption) &&
|
|
714
|
+
typeof engineOption.name === "string") {
|
|
715
|
+
return engineOption.name;
|
|
716
|
+
}
|
|
717
|
+
return undefined;
|
|
718
|
+
}
|
|
690
719
|
/**
|
|
691
720
|
* Return the usable engine explicitly selected by the current user/org.
|
|
692
721
|
*
|
|
@@ -734,7 +763,7 @@ export async function getConfiguredEngineNameForRequest(options = {}) {
|
|
|
734
763
|
* 7. Default "anthropic" (requires ANTHROPIC_API_KEY)
|
|
735
764
|
*/
|
|
736
765
|
export async function resolveEngine(config) {
|
|
737
|
-
const { engineOption, apiKey, model: _model, appId } = config;
|
|
766
|
+
const { engineOption, apiKey, apiKeyEnvVar, model: _model, appId } = config;
|
|
738
767
|
// 1. Explicit instance passed directly
|
|
739
768
|
if (engineOption &&
|
|
740
769
|
typeof engineOption === "object" &&
|
|
@@ -750,7 +779,7 @@ export async function resolveEngine(config) {
|
|
|
750
779
|
if (!entry)
|
|
751
780
|
throw new Error(`[agent-engine] Unknown engine: "${name}". Registered: ${[..._registry.keys()].join(", ")}`);
|
|
752
781
|
assertAgentEnginePackageInstalled(entry);
|
|
753
|
-
return entry.create(await engineCreateConfigForEntry(entry, apiKey, engineConfig));
|
|
782
|
+
return entry.create(await engineCreateConfigForEntry(entry, apiKey, engineConfig, false, apiKeyEnvVar));
|
|
754
783
|
}
|
|
755
784
|
// 3. Explicit string name from options
|
|
756
785
|
if (typeof engineOption === "string") {
|
|
@@ -758,7 +787,7 @@ export async function resolveEngine(config) {
|
|
|
758
787
|
if (!entry)
|
|
759
788
|
throw new Error(`[agent-engine] Unknown engine: "${engineOption}". Registered: ${[..._registry.keys()].join(", ")}`);
|
|
760
789
|
assertAgentEnginePackageInstalled(entry);
|
|
761
|
-
return entry.create(await engineCreateConfigForEntry(entry, apiKey));
|
|
790
|
+
return entry.create(await engineCreateConfigForEntry(entry, apiKey, undefined, false, apiKeyEnvVar));
|
|
762
791
|
}
|
|
763
792
|
// 4. Env var — explicit engine name override
|
|
764
793
|
const envEngine = process.env.AGENT_ENGINE;
|
|
@@ -766,14 +795,14 @@ export async function resolveEngine(config) {
|
|
|
766
795
|
const entry = _registry.get(envEngine);
|
|
767
796
|
if (entry) {
|
|
768
797
|
assertAgentEnginePackageInstalled(entry);
|
|
769
|
-
return entry.create(await engineCreateConfigForEntry(entry, apiKey, undefined, true));
|
|
798
|
+
return entry.create(await engineCreateConfigForEntry(entry, apiKey, undefined, true, apiKeyEnvVar));
|
|
770
799
|
}
|
|
771
800
|
}
|
|
772
801
|
const appDefault = await getAgentAppModelDefaultForCurrentRequest(appId);
|
|
773
802
|
if (appDefault?.engine) {
|
|
774
803
|
const entry = _registry.get(appDefault.engine);
|
|
775
804
|
if (entry && (await isStoredEngineUsableForRequest(appDefault, entry))) {
|
|
776
|
-
return entry.create(await engineCreateConfigForEntry(entry, apiKey, undefined, true));
|
|
805
|
+
return entry.create(await engineCreateConfigForEntry(entry, apiKey, undefined, true, apiKeyEnvVar));
|
|
777
806
|
}
|
|
778
807
|
}
|
|
779
808
|
let stored = null;
|
|
@@ -797,25 +826,25 @@ export async function resolveEngine(config) {
|
|
|
797
826
|
if (storedRaw && typeof storedEngine === "string") {
|
|
798
827
|
const entry = _registry.get(storedEngine);
|
|
799
828
|
if (entry && (await isStoredEngineUsableForRequest(storedRaw, entry))) {
|
|
800
|
-
return entry.create(await engineCreateConfigForEntry(entry, apiKey, stripInlineApiKeyConfig(storedConfig), true));
|
|
829
|
+
return entry.create(await engineCreateConfigForEntry(entry, apiKey, stripInlineApiKeyConfig(storedConfig), true, apiKeyEnvVar));
|
|
801
830
|
}
|
|
802
831
|
}
|
|
803
832
|
if (detectedFromUser) {
|
|
804
|
-
return detectedFromUser.create(await engineCreateConfigForEntry(detectedFromUser, apiKey, undefined, true));
|
|
833
|
+
return detectedFromUser.create(await engineCreateConfigForEntry(detectedFromUser, apiKey, undefined, true, apiKeyEnvVar));
|
|
805
834
|
}
|
|
806
835
|
// 8. Auto-detect from any provider env var — so just dropping a key in
|
|
807
836
|
// .env works without also setting AGENT_ENGINE. Skip keys with active
|
|
808
837
|
// auth-failure markers so a rejected deploy key cannot permanently win.
|
|
809
838
|
const detected = await detectEngineFromEnvForRequest();
|
|
810
839
|
if (detected) {
|
|
811
|
-
return detected.create(await engineCreateConfigForEntry(detected, apiKey, undefined, true));
|
|
840
|
+
return detected.create(await engineCreateConfigForEntry(detected, apiKey, undefined, true, apiKeyEnvVar));
|
|
812
841
|
}
|
|
813
842
|
// 9. Default: anthropic
|
|
814
843
|
const anthropicEntry = _registry.get("anthropic");
|
|
815
844
|
if (!anthropicEntry) {
|
|
816
845
|
throw new Error("[agent-engine] Default Anthropic engine is not registered. Did builtin.ts fail to load?");
|
|
817
846
|
}
|
|
818
|
-
return anthropicEntry.create(await engineCreateConfigForEntry(anthropicEntry, apiKey, undefined, true));
|
|
847
|
+
return anthropicEntry.create(await engineCreateConfigForEntry(anthropicEntry, apiKey, undefined, true, apiKeyEnvVar));
|
|
819
848
|
}
|
|
820
849
|
/**
|
|
821
850
|
* Read the user-selected model for an engine from the `agent-engine` setting.
|
|
@@ -3,6 +3,7 @@ import type { Task } from "../a2a/types.js";
|
|
|
3
3
|
import { type ActionAutomationContext, type ActionCaller } from "../action.js";
|
|
4
4
|
import { fireInternalDispatch } from "../server/self-dispatch.js";
|
|
5
5
|
import { type ReasoningEffort } from "../shared/reasoning-effort.js";
|
|
6
|
+
import { type ResolveEngineConfig } from "./engine/index.js";
|
|
6
7
|
import { PROVIDER_TO_ENV } from "./engine/provider-env-vars.js";
|
|
7
8
|
import type { AgentEngine, EngineTool, EngineMessage, EngineContentPart } from "./engine/types.js";
|
|
8
9
|
import { type Processor } from "./processors.js";
|
|
@@ -99,18 +100,56 @@ export declare function getOwnerApiKey(provider: string, ownerEmail: string | nu
|
|
|
99
100
|
* "ai-sdk:openai" → "openai", "anthropic" → "anthropic"
|
|
100
101
|
*/
|
|
101
102
|
export declare function engineToProvider(engineName: string): string;
|
|
103
|
+
/** An API key together with the env var it was issued for. */
|
|
104
|
+
export interface ResolvedOwnerApiKey {
|
|
105
|
+
apiKey: string | undefined;
|
|
106
|
+
/** Undefined when no key was found, or when the key's provider is unknown. */
|
|
107
|
+
apiKeyEnvVar: string | undefined;
|
|
108
|
+
}
|
|
102
109
|
/**
|
|
103
|
-
* Resolve the
|
|
110
|
+
* Resolve the owner's key for one named engine, tagged with the env var it was
|
|
111
|
+
* issued for.
|
|
104
112
|
*
|
|
105
113
|
* If the owner has no scoped key, fall back to provider keys supplied by the
|
|
106
114
|
* hosting environment only when the current request can safely use deploy-level
|
|
107
115
|
* credentials. This is a read from process-level config, not a request-scoped
|
|
108
116
|
* write to `process.env`.
|
|
117
|
+
*/
|
|
118
|
+
export declare function getOwnerApiKeyForEngine(engineName: string, ownerEmail: string | null | undefined): Promise<ResolvedOwnerApiKey>;
|
|
119
|
+
/**
|
|
120
|
+
* Resolve the active engine's provider and look up the user's API key for it.
|
|
109
121
|
*
|
|
110
122
|
* Callers that layer another deployment-key fallback after this should keep the
|
|
111
123
|
* same precedence: scoped key first, host-provided env key second.
|
|
124
|
+
*
|
|
125
|
+
* The returned key is untagged, so it is only safe to hand to `resolveEngine`
|
|
126
|
+
* when the caller has no explicit engine of its own — otherwise the active
|
|
127
|
+
* setting and the selected engine can name different providers. Prefer
|
|
128
|
+
* {@link resolveOwnerEngineApiKey}, which decides that per call site.
|
|
112
129
|
*/
|
|
113
130
|
export declare function getOwnerActiveApiKey(ownerEmail: string | null | undefined): Promise<string | undefined>;
|
|
131
|
+
/**
|
|
132
|
+
* Resolve the credential to hand `resolveEngine` alongside `engineOption`.
|
|
133
|
+
*
|
|
134
|
+
* The provenance is the point. An explicitly named engine skips the registry's
|
|
135
|
+
* value-comparison path, so an untagged key resolved from the saved
|
|
136
|
+
* `agent-engine` setting would ride along to whatever provider the caller
|
|
137
|
+
* named — shipping, say, a live Anthropic secret to OpenAI's endpoint and
|
|
138
|
+
* making the resulting 401 blame a key the user never saved. Resolving the key
|
|
139
|
+
* for the named engine keeps the two in step; only the no-explicit-engine case
|
|
140
|
+
* may stay untagged, because the registry's automatic branches re-derive the
|
|
141
|
+
* credential themselves.
|
|
142
|
+
*/
|
|
143
|
+
export declare function resolveOwnerEngineApiKey(input: {
|
|
144
|
+
engineOption?: ResolveEngineConfig["engineOption"];
|
|
145
|
+
ownerEmail: string | null | undefined;
|
|
146
|
+
/**
|
|
147
|
+
* Host-provided credential, which is Anthropic by contract
|
|
148
|
+
* (`AgentChatPluginOptions.apiKey` / `WebhookHandlerOptions.apiKey`). Used
|
|
149
|
+
* only when no owner key was found.
|
|
150
|
+
*/
|
|
151
|
+
anthropicFallback?: string;
|
|
152
|
+
}): Promise<ResolvedOwnerApiKey>;
|
|
114
153
|
/** @deprecated Use getOwnerApiKey("anthropic", ownerEmail) instead */
|
|
115
154
|
export declare function getOwnerAnthropicApiKey(ownerEmail: string | null | undefined): Promise<string | undefined>;
|
|
116
155
|
/**
|
|
@@ -27,7 +27,7 @@ import { applyContextXrayTransformForIteration } from "./engine/context-directiv
|
|
|
27
27
|
import { attemptContinuationDispatch } from "./engine/continuation-dispatch-retry.js";
|
|
28
28
|
import { LLM_MISSING_CREDENTIALS_ERROR_CODE, LLM_MISSING_CREDENTIALS_MESSAGE, userFacingLlmCredentialError, } from "./engine/credential-errors.js";
|
|
29
29
|
import { isProviderConnectionErrorMessage } from "./engine/error-detail.js";
|
|
30
|
-
import { resolveEngine, registerBuiltinEngines, getStoredModelForEngine, normalizeModelForEngine, isResolvedEngineUsableForRequest, } from "./engine/index.js";
|
|
30
|
+
import { resolveEngine, explicitEngineName, registerBuiltinEngines, getStoredModelForEngine, normalizeModelForEngine, isResolvedEngineUsableForRequest, } from "./engine/index.js";
|
|
31
31
|
import { resolveEmptyResponseRetryMaxOutputTokens, resolveMainChatMaxOutputTokens, resolveMaxOutputTokensForEngine, } from "./engine/output-tokens.js";
|
|
32
32
|
import { PROVIDER_TO_ENV } from "./engine/provider-env-vars.js";
|
|
33
33
|
import { loadPriorTurnToolCallJournal } from "./engine/tool-call-journal-seed.js";
|
|
@@ -300,41 +300,92 @@ export async function getOwnerApiKey(provider, ownerEmail) {
|
|
|
300
300
|
export function engineToProvider(engineName) {
|
|
301
301
|
return engineName.startsWith("ai-sdk:") ? engineName.slice(7) : engineName;
|
|
302
302
|
}
|
|
303
|
+
const NO_OWNER_API_KEY = {
|
|
304
|
+
apiKey: undefined,
|
|
305
|
+
apiKeyEnvVar: undefined,
|
|
306
|
+
};
|
|
303
307
|
/**
|
|
304
|
-
* Resolve the
|
|
308
|
+
* Resolve the owner's key for one named engine, tagged with the env var it was
|
|
309
|
+
* issued for.
|
|
305
310
|
*
|
|
306
311
|
* If the owner has no scoped key, fall back to provider keys supplied by the
|
|
307
312
|
* hosting environment only when the current request can safely use deploy-level
|
|
308
313
|
* credentials. This is a read from process-level config, not a request-scoped
|
|
309
314
|
* write to `process.env`.
|
|
310
|
-
*
|
|
311
|
-
* Callers that layer another deployment-key fallback after this should keep the
|
|
312
|
-
* same precedence: scoped key first, host-provided env key second.
|
|
313
315
|
*/
|
|
314
|
-
export async function
|
|
316
|
+
export async function getOwnerApiKeyForEngine(engineName, ownerEmail) {
|
|
315
317
|
try {
|
|
316
|
-
const
|
|
317
|
-
const
|
|
318
|
-
const activeEngine = engineSetting?.engine ?? "anthropic";
|
|
319
|
-
const provider = engineToProvider(activeEngine);
|
|
318
|
+
const provider = engineToProvider(engineName);
|
|
319
|
+
const envVar = PROVIDER_TO_ENV[provider];
|
|
320
320
|
const userKey = await getOwnerApiKey(provider, ownerEmail);
|
|
321
321
|
if (userKey)
|
|
322
|
-
return userKey;
|
|
323
|
-
const envVar = PROVIDER_TO_ENV[provider];
|
|
322
|
+
return { apiKey: userKey, apiKeyEnvVar: envVar };
|
|
324
323
|
if (!envVar || !canUseDeployCredentialFallbackForRequest(envVar)) {
|
|
325
|
-
return
|
|
324
|
+
return NO_OWNER_API_KEY;
|
|
326
325
|
}
|
|
327
326
|
const envKey = readDeployCredentialEnv(envVar);
|
|
328
327
|
if (envKey &&
|
|
329
328
|
!(await getProviderCredentialAuthFailure({ key: envVar, value: envKey }))) {
|
|
330
|
-
return envKey;
|
|
329
|
+
return { apiKey: envKey, apiKeyEnvVar: envVar };
|
|
331
330
|
}
|
|
332
|
-
return
|
|
331
|
+
return NO_OWNER_API_KEY;
|
|
332
|
+
}
|
|
333
|
+
catch {
|
|
334
|
+
return NO_OWNER_API_KEY;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Resolve the active engine's provider and look up the user's API key for it.
|
|
339
|
+
*
|
|
340
|
+
* Callers that layer another deployment-key fallback after this should keep the
|
|
341
|
+
* same precedence: scoped key first, host-provided env key second.
|
|
342
|
+
*
|
|
343
|
+
* The returned key is untagged, so it is only safe to hand to `resolveEngine`
|
|
344
|
+
* when the caller has no explicit engine of its own — otherwise the active
|
|
345
|
+
* setting and the selected engine can name different providers. Prefer
|
|
346
|
+
* {@link resolveOwnerEngineApiKey}, which decides that per call site.
|
|
347
|
+
*/
|
|
348
|
+
export async function getOwnerActiveApiKey(ownerEmail) {
|
|
349
|
+
try {
|
|
350
|
+
const { getSetting } = await import("../settings/store.js");
|
|
351
|
+
const engineSetting = await getSetting("agent-engine");
|
|
352
|
+
const activeEngine = engineSetting?.engine ?? "anthropic";
|
|
353
|
+
return (await getOwnerApiKeyForEngine(activeEngine, ownerEmail)).apiKey;
|
|
333
354
|
}
|
|
334
355
|
catch {
|
|
335
356
|
return undefined;
|
|
336
357
|
}
|
|
337
358
|
}
|
|
359
|
+
/**
|
|
360
|
+
* Resolve the credential to hand `resolveEngine` alongside `engineOption`.
|
|
361
|
+
*
|
|
362
|
+
* The provenance is the point. An explicitly named engine skips the registry's
|
|
363
|
+
* value-comparison path, so an untagged key resolved from the saved
|
|
364
|
+
* `agent-engine` setting would ride along to whatever provider the caller
|
|
365
|
+
* named — shipping, say, a live Anthropic secret to OpenAI's endpoint and
|
|
366
|
+
* making the resulting 401 blame a key the user never saved. Resolving the key
|
|
367
|
+
* for the named engine keeps the two in step; only the no-explicit-engine case
|
|
368
|
+
* may stay untagged, because the registry's automatic branches re-derive the
|
|
369
|
+
* credential themselves.
|
|
370
|
+
*/
|
|
371
|
+
export async function resolveOwnerEngineApiKey(input) {
|
|
372
|
+
const engineName = explicitEngineName(input.engineOption);
|
|
373
|
+
if (engineName) {
|
|
374
|
+
const resolved = await getOwnerApiKeyForEngine(engineName, input.ownerEmail);
|
|
375
|
+
if (resolved.apiKey)
|
|
376
|
+
return resolved;
|
|
377
|
+
}
|
|
378
|
+
else {
|
|
379
|
+
const activeKey = await getOwnerActiveApiKey(input.ownerEmail);
|
|
380
|
+
if (activeKey)
|
|
381
|
+
return { apiKey: activeKey, apiKeyEnvVar: undefined };
|
|
382
|
+
}
|
|
383
|
+
const fallback = input.anthropicFallback?.trim();
|
|
384
|
+
return fallback &&
|
|
385
|
+
canUseDeployCredentialFallbackForRequest("ANTHROPIC_API_KEY")
|
|
386
|
+
? { apiKey: fallback, apiKeyEnvVar: "ANTHROPIC_API_KEY" }
|
|
387
|
+
: NO_OWNER_API_KEY;
|
|
388
|
+
}
|
|
338
389
|
/** @deprecated Use getOwnerApiKey("anthropic", ownerEmail) instead */
|
|
339
390
|
export async function getOwnerAnthropicApiKey(ownerEmail) {
|
|
340
391
|
return getOwnerApiKey("anthropic", ownerEmail);
|
|
@@ -5884,34 +5935,22 @@ export function createProductionAgentHandler(options) {
|
|
|
5884
5935
|
}
|
|
5885
5936
|
// DIAGNOSTIC-ONLY: attachment upload + persistence finished.
|
|
5886
5937
|
workerStep("attach_done");
|
|
5887
|
-
//
|
|
5888
|
-
//
|
|
5938
|
+
// Resolve the key for the engine this request will actually select — the
|
|
5939
|
+
// per-request override when there is one, otherwise the plugin's own
|
|
5940
|
+
// engine — instead of the global active engine's provider.
|
|
5941
|
+
// `options.apiKey` is the value the template constructed the plugin with
|
|
5942
|
+
// (often wired from a deployment env var). Honor it as host-provided
|
|
5943
|
+
// read-only configuration after scoped keys.
|
|
5889
5944
|
// DIAGNOSTIC-ONLY: bracket per-owner API-key resolution (settings/app_secrets reads).
|
|
5890
5945
|
workerStep("apikey_start");
|
|
5891
|
-
|
|
5892
|
-
|
|
5893
|
-
|
|
5894
|
-
|
|
5895
|
-
|
|
5896
|
-
|
|
5897
|
-
envVar &&
|
|
5898
|
-
canUseDeployCredentialFallbackForRequest(envVar)) {
|
|
5899
|
-
// Read-only env fallback for the requested provider.
|
|
5900
|
-
userApiKey = envVar ? readDeployCredentialEnv(envVar) : undefined;
|
|
5901
|
-
}
|
|
5902
|
-
}
|
|
5903
|
-
else {
|
|
5904
|
-
userApiKey = await getOwnerActiveApiKey(ownerEmail);
|
|
5905
|
-
}
|
|
5946
|
+
const engineOption = requestEngine ?? options.engine;
|
|
5947
|
+
const { apiKey: effectiveApiKey, apiKeyEnvVar: effectiveApiKeyEnvVar } = await resolveOwnerEngineApiKey({
|
|
5948
|
+
engineOption,
|
|
5949
|
+
ownerEmail,
|
|
5950
|
+
anthropicFallback: options.apiKey ?? readDeployCredentialEnv("ANTHROPIC_API_KEY"),
|
|
5951
|
+
});
|
|
5906
5952
|
// DIAGNOSTIC-ONLY: API-key resolution finished.
|
|
5907
5953
|
workerStep("apikey_done");
|
|
5908
|
-
// `options.apiKey` is the value the template constructed the plugin with
|
|
5909
|
-
// (often wired from a deployment env var). Honor it as host-provided
|
|
5910
|
-
// read-only configuration after scoped keys when deploy fallback is safe.
|
|
5911
|
-
const hostApiKey = canUseDeployCredentialFallbackForRequest("ANTHROPIC_API_KEY")
|
|
5912
|
-
? (options.apiKey ?? readDeployCredentialEnv("ANTHROPIC_API_KEY"))
|
|
5913
|
-
: undefined;
|
|
5914
|
-
const effectiveApiKey = userApiKey ?? hostApiKey;
|
|
5915
5954
|
// Resolve engine — per-request engine override takes priority
|
|
5916
5955
|
// DIAGNOSTIC-ONLY: bracket engine resolution (Builder credential / app-default
|
|
5917
5956
|
// settings reads inside resolveEngine).
|
|
@@ -5919,8 +5958,9 @@ export function createProductionAgentHandler(options) {
|
|
|
5919
5958
|
let engine;
|
|
5920
5959
|
try {
|
|
5921
5960
|
engine = await resolveEngine({
|
|
5922
|
-
engineOption
|
|
5961
|
+
engineOption,
|
|
5923
5962
|
apiKey: effectiveApiKey,
|
|
5963
|
+
apiKeyEnvVar: effectiveApiKeyEnvVar,
|
|
5924
5964
|
model: configuredModel,
|
|
5925
5965
|
appId: options.appId,
|
|
5926
5966
|
});
|
|
@@ -5928,6 +5968,7 @@ export function createProductionAgentHandler(options) {
|
|
|
5928
5968
|
catch {
|
|
5929
5969
|
engine = await resolveEngine({
|
|
5930
5970
|
apiKey: effectiveApiKey,
|
|
5971
|
+
apiKeyEnvVar: effectiveApiKeyEnvVar,
|
|
5931
5972
|
appId: options.appId,
|
|
5932
5973
|
});
|
|
5933
5974
|
}
|
|
@@ -181,13 +181,56 @@ export declare const SQL_SUBSCRIPTION_IDLE_POLL_MS = 500;
|
|
|
181
181
|
export declare const SQL_SUBSCRIPTION_ACTIVE_GRACE_MS = 2000;
|
|
182
182
|
/** Keep terminal/status probes at the historical cadence to bound DB work. */
|
|
183
183
|
export declare const SQL_SUBSCRIPTION_STATUS_POLL_MS = 500;
|
|
184
|
+
/**
|
|
185
|
+
* Consecutive empty polls before the IDLE cadence starts decaying toward
|
|
186
|
+
* `SQL_SUBSCRIPTION_IDLE_MAX_POLL_MS`.
|
|
187
|
+
*
|
|
188
|
+
* The active grace already covers a streaming producer, so decay only ever
|
|
189
|
+
* applies to a subscriber watching a run that is producing nothing: a long tool
|
|
190
|
+
* call, a slow first token, or a wedged producer. Those cost one poll per
|
|
191
|
+
* 500ms each, forever, per subscriber — the single largest source of idle
|
|
192
|
+
* `agent_run_events` reads.
|
|
193
|
+
*/
|
|
194
|
+
export declare const SQL_SUBSCRIPTION_IDLE_DECAY_AFTER_POLLS = 4;
|
|
195
|
+
/**
|
|
196
|
+
* Ceiling for the decayed idle cadence. Bounds the WORST-CASE added latency to
|
|
197
|
+
* the next token a quiet run eventually produces; a row already waiting when the
|
|
198
|
+
* timer fires is delivered immediately, so this is not added to a streaming run.
|
|
199
|
+
*/
|
|
200
|
+
export declare const SQL_SUBSCRIPTION_IDLE_MAX_POLL_MS = 2000;
|
|
201
|
+
/**
|
|
202
|
+
* Cadence for the opportunistic `reapIfStale` probe inside the SSE poll loop,
|
|
203
|
+
* kept SEPARATE from (and much slower than) the status probe next to it.
|
|
204
|
+
*
|
|
205
|
+
* A reap can only ever act on a run whose liveness basis is older than the
|
|
206
|
+
* tightest staleness window any sweep enforces — `RUN_STALE_MS`, 15s — so
|
|
207
|
+
* running it on the 500ms status cadence issued ~30 rounds of
|
|
208
|
+
* `reconcileTerminalRunFromEvents` + reap-eligibility queries (2-4 round trips
|
|
209
|
+
* each) before the first one could possibly match a row. This cadence still
|
|
210
|
+
* detects a stale producer well inside the client's own idle timeout while
|
|
211
|
+
* cutting that probe traffic ~10x. `getRunById` deliberately stays on the fast
|
|
212
|
+
* status cadence: it is one indexed read and it is what closes the stream on a
|
|
213
|
+
* NORMAL finish, which must stay prompt.
|
|
214
|
+
*/
|
|
215
|
+
export declare const SQL_SUBSCRIPTION_REAP_POLL_MS = 5000;
|
|
184
216
|
/** Initial retry delay after a transient cross-isolate SQL polling failure. */
|
|
185
217
|
export declare const SQL_SUBSCRIPTION_RETRY_BASE_MS = 250;
|
|
186
218
|
/** Bound consecutive SQL polling failures so a dead subscription fails loud. */
|
|
187
219
|
export declare const SQL_SUBSCRIPTION_MAX_CONSECUTIVE_FAILURES = 4;
|
|
188
220
|
/** Cap retry delay even if the failure bound changes independently. */
|
|
189
221
|
export declare const SQL_SUBSCRIPTION_RETRY_MAX_MS = 2000;
|
|
190
|
-
export declare function resolveSqlSubscriptionPollMs(now: number, activePollUntil: number): number;
|
|
222
|
+
export declare function resolveSqlSubscriptionPollMs(now: number, activePollUntil: number, consecutiveEmptyPolls?: number): number;
|
|
223
|
+
/**
|
|
224
|
+
* Advance the empty-poll counter that drives `resolveSqlSubscriptionPollMs`'s
|
|
225
|
+
* decay.
|
|
226
|
+
*
|
|
227
|
+
* Only IDLE polls count. Counting the ~16 fast polls inside the active grace
|
|
228
|
+
* window would push the ladder to its cap the instant the grace expired, so a
|
|
229
|
+
* producer that pauses a few seconds between tokens would resume at the 2s cap
|
|
230
|
+
* instead of 500ms — a visible mid-stream stutter rather than the intended
|
|
231
|
+
* "this run has gone quiet" backoff.
|
|
232
|
+
*/
|
|
233
|
+
export declare function nextSqlSubscriptionEmptyPolls(current: number, hadEvents: boolean, now: number, activePollUntil: number): number;
|
|
191
234
|
export declare function resolveSqlSubscriptionRetryMs(consecutiveFailures: number): number;
|
|
192
235
|
export interface StartRunOptions {
|
|
193
236
|
/** Keep a request-scoped serverless invocation alive for this run. */
|