@standardagents/builder 0.27.6 → 0.27.8-dev.b0d0355

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/runtime.d.ts CHANGED
@@ -3602,9 +3602,35 @@ interface ProviderLookupResult {
3602
3602
  */
3603
3603
  declare class ProviderRegistryImpl {
3604
3604
  private providerCache;
3605
+ /** Why the last thread-scoped resolution did (not) contribute user env,
3606
+ * keyed by the env object handed back — surfaced in missing-variable
3607
+ * errors so a silent user-env failure is diagnosable from the log. */
3608
+ private scopeDiagnostics;
3605
3609
  private providerOverrideFn;
3606
3610
  private providerOverrideRevision;
3607
3611
  setProviderOverride(fn: ProviderOverrideFn | null): void;
3612
+ /**
3613
+ * The env a provider resolves against for a given execution, scoped to the
3614
+ * THREAD rather than just the instance: thread env overrides win, then the
3615
+ * thread OWNER's user env, then instance/Worker env. This is what makes
3616
+ * per-user BYOK providers work — a credential a user installs in their own
3617
+ * account env (e.g. an OAuth token a companion service rotates) is honored
3618
+ * for that user's threads no matter which authorized actor plays the turn.
3619
+ *
3620
+ * Best-effort by design: a missing user id (super-admin/local threads) or
3621
+ * an older AgentBuilder DO without user-env storage simply contributes
3622
+ * nothing, leaving instance-env behavior untouched.
3623
+ */
3624
+ buildThreadScopedEnv(state: {
3625
+ env: Env;
3626
+ thread: {
3627
+ metadata?: {
3628
+ user_id?: string | null;
3629
+ env?: Record<string, unknown> | null;
3630
+ tenvs?: Record<string, unknown> | null;
3631
+ };
3632
+ };
3633
+ }): Promise<Env>;
3608
3634
  /**
3609
3635
  * Get provider for a model.
3610
3636
  *
package/dist/runtime.js CHANGED
@@ -884,12 +884,64 @@ var init_ProviderRegistry = __esm({
884
884
  init_factory();
885
885
  ProviderRegistryImpl = class {
886
886
  providerCache = /* @__PURE__ */ new Map();
887
+ /** Why the last thread-scoped resolution did (not) contribute user env,
888
+ * keyed by the env object handed back — surfaced in missing-variable
889
+ * errors so a silent user-env failure is diagnosable from the log. */
890
+ scopeDiagnostics = /* @__PURE__ */ new WeakMap();
887
891
  providerOverrideFn = null;
888
892
  providerOverrideRevision = 0;
889
893
  setProviderOverride(fn) {
890
894
  this.providerOverrideFn = fn;
891
895
  this.providerOverrideRevision += 1;
892
896
  }
897
+ /**
898
+ * The env a provider resolves against for a given execution, scoped to the
899
+ * THREAD rather than just the instance: thread env overrides win, then the
900
+ * thread OWNER's user env, then instance/Worker env. This is what makes
901
+ * per-user BYOK providers work — a credential a user installs in their own
902
+ * account env (e.g. an OAuth token a companion service rotates) is honored
903
+ * for that user's threads no matter which authorized actor plays the turn.
904
+ *
905
+ * Best-effort by design: a missing user id (super-admin/local threads) or
906
+ * an older AgentBuilder DO without user-env storage simply contributes
907
+ * nothing, leaving instance-env behavior untouched.
908
+ */
909
+ async buildThreadScopedEnv(state) {
910
+ const threadEnv = state.thread.metadata?.env ?? state.thread.metadata?.tenvs ?? null;
911
+ const userId = state.thread.metadata?.user_id ?? null;
912
+ let userEnv = null;
913
+ let diagnostic = userId ? "" : "no thread user_id";
914
+ if (userId) {
915
+ try {
916
+ const agentBuilderId = state.env.AGENT_BUILDER.idFromName("singleton");
917
+ const agentBuilder = state.env.AGENT_BUILDER.get(agentBuilderId);
918
+ if (typeof agentBuilder.getUserEnvEntries === "function") {
919
+ const entries = await agentBuilder.getUserEnvEntries(userId);
920
+ if (Array.isArray(entries) && entries.length > 0) {
921
+ userEnv = {};
922
+ for (const entry of entries) {
923
+ if (entry?.name) userEnv[entry.name] = entry.value;
924
+ }
925
+ diagnostic = `user env: ${Object.keys(userEnv).length} entries`;
926
+ } else {
927
+ diagnostic = `user env empty for ${userId} (entries: ${Array.isArray(entries) ? entries.length : typeof entries})`;
928
+ }
929
+ } else {
930
+ diagnostic = "singleton has no getUserEnvEntries";
931
+ }
932
+ } catch (error) {
933
+ diagnostic = `user env lookup failed: ${error instanceof Error ? error.message : String(error)}`;
934
+ console.warn("buildThreadScopedEnv:", diagnostic);
935
+ }
936
+ }
937
+ if (!userEnv && !threadEnv) {
938
+ this.scopeDiagnostics.set(state.env, diagnostic);
939
+ return state.env;
940
+ }
941
+ const merged = { ...state.env, ...userEnv ?? {}, ...threadEnv ?? {} };
942
+ this.scopeDiagnostics.set(merged, diagnostic);
943
+ return merged;
944
+ }
893
945
  /**
894
946
  * Get provider for a model.
895
947
  *
@@ -927,8 +979,9 @@ var init_ProviderRegistry = __esm({
927
979
  if (providerDefinition) {
928
980
  const resolved = resolveDefinedProviderConfig(providerDefinition, env);
929
981
  if (resolved.missing.length > 0) {
982
+ const scopeNote = this.scopeDiagnostics.get(env);
930
983
  throw new Error(
931
- `Missing required environment variables for provider '${providerName}': ${resolved.missing.join(", ")}.`
984
+ `Missing required environment variables for provider '${providerName}': ${resolved.missing.join(", ")}.` + (scopeNote ? ` [thread-scoped env: ${scopeNote}]` : "")
932
985
  );
933
986
  }
934
987
  const config = resolved.providerConfig;
@@ -1801,7 +1854,8 @@ var init_LLMRequest = __esm({
1801
1854
  */
1802
1855
  static async callModel(modelId, context, state, logId) {
1803
1856
  const { ProviderRegistry: ProviderRegistry2 } = await Promise.resolve().then(() => (init_providers(), providers_exports));
1804
- const { provider, modelName, modelDef, viaPlatform, platformProxyOrigin } = await ProviderRegistry2.getProvider(modelId, state.env, state.thread.instance);
1857
+ const providerEnv = await ProviderRegistry2.buildThreadScopedEnv(state);
1858
+ const { provider, modelName, modelDef, viaPlatform, platformProxyOrigin } = await ProviderRegistry2.getProvider(modelId, providerEnv, state.thread.instance);
1805
1859
  const streamAbort = new AbortController();
1806
1860
  const parentSignal = state.abortController?.signal;
1807
1861
  let onParentAbort;