@opengeni/core 1.4.5-canary.38533733713001 → 2.0.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/dist/index.js CHANGED
@@ -948,6 +948,7 @@ function preferredFikenConnection(connections) {
948
948
  // src/domain/vaults/provider-registry.ts
949
949
  import {
950
950
  getConnectionMetadata,
951
+ getProviderConnectionHomeWorkspace,
951
952
  loadConnectionCredentialForBroker
952
953
  } from "@opengeni/db";
953
954
  import {
@@ -994,12 +995,25 @@ async function loadOnePasswordConnectionToken(db, settings, input) {
994
995
  "provider_auth_invalid",
995
996
  "The 1Password connection for this vault was removed or is not available to you; reconnect 1Password"
996
997
  );
997
- const connection = await getConnectionMetadata(
998
+ let homeWorkspaceId = input.workspaceId;
999
+ let connection = await getConnectionMetadata(
998
1000
  db,
999
1001
  input.workspaceId,
1000
1002
  input.connectionId,
1001
1003
  input.subjectId
1002
1004
  );
1005
+ if (!connection) {
1006
+ const home = await getProviderConnectionHomeWorkspace(
1007
+ db,
1008
+ input.workspaceId,
1009
+ input.connectionId,
1010
+ input.subjectId
1011
+ );
1012
+ if (home && home !== input.workspaceId) {
1013
+ homeWorkspaceId = home;
1014
+ connection = await getConnectionMetadata(db, home, input.connectionId, input.subjectId);
1015
+ }
1016
+ }
1003
1017
  if (!connection || !isOnePasswordConnection(connection)) throw unavailable();
1004
1018
  if (connection.status !== "active") {
1005
1019
  throw new VaultProviderError(
@@ -1009,7 +1023,7 @@ async function loadOnePasswordConnectionToken(db, settings, input) {
1009
1023
  }
1010
1024
  const personal = connection.subjectId !== null;
1011
1025
  const credential = await loadConnectionCredentialForBroker(db, settings, {
1012
- workspaceId: input.workspaceId,
1026
+ workspaceId: homeWorkspaceId,
1013
1027
  connectionId: connection.id,
1014
1028
  providerDomain: ONEPASSWORD_PROVIDER_DOMAIN,
1015
1029
  kind: "api_key",
@@ -2189,7 +2203,7 @@ function nativeConnectionCapabilityRecommendations(options = {}) {
2189
2203
  kind: "api",
2190
2204
  source: "built_in",
2191
2205
  name: "Connected Machine",
2192
- description: "Run this chat on the person's own computer or server (Mac, Linux or Windows), and optionally let agents use their Chrome through the OpenGeni Browser extension.",
2206
+ description: "Run this chat on the person's own computer or server (Mac, Linux or Windows), and optionally let agents use their Chrome through the Opengeni Browser extension.",
2193
2207
  category: "compute",
2194
2208
  tags: [
2195
2209
  "machine",
@@ -2217,7 +2231,7 @@ function nativeConnectionCapabilityRecommendations(options = {}) {
2217
2231
  tools: [{ kind: "mcp", id: "opengeni" }],
2218
2232
  runtime: {
2219
2233
  available: true,
2220
- notes: `The person runs a one-line connect command on their machine from the chat card. The OpenGeni Browser Chrome extension (${OPENGENI_BROWSER_EXTENSION_URL}) works only after the machine is connected.`
2234
+ notes: `The person runs a one-line connect command on their machine from the chat card. The Opengeni Browser Chrome extension (${OPENGENI_BROWSER_EXTENSION_URL}) works only after the machine is connected.`
2221
2235
  },
2222
2236
  lifecycle: {
2223
2237
  status: "available",
@@ -6805,18 +6819,321 @@ import {
6805
6819
  parseTotpInput,
6806
6820
  referenceKey,
6807
6821
  SecretReferenceError,
6808
- VAULT_LIMITS
6822
+ VAULT_LIMITS as VAULT_LIMITS2
6809
6823
  } from "@opengeni/contracts";
6810
6824
  import {
6811
6825
  decryptEnvironmentValue,
6826
+ listVaultsForSubject,
6812
6827
  recordVaultSecretUse,
6813
6828
  resolveSecretReferenceCandidates
6814
6829
  } from "@opengeni/db";
6815
6830
  import {
6816
6831
  createProviderCallBudget,
6817
6832
  EMPTY_VAULT_PROVIDER_REGISTRY,
6818
- isVaultProviderError
6833
+ isVaultProviderError as isVaultProviderError2
6819
6834
  } from "@opengeni/vaults";
6835
+
6836
+ // src/domain/vaults/provider-sync.ts
6837
+ import {
6838
+ isValidSecretName,
6839
+ VAULT_DEFAULT_PRIMARY_FIELD,
6840
+ VAULT_LIMITS
6841
+ } from "@opengeni/contracts";
6842
+ import {
6843
+ getProviderVaultSyncTarget,
6844
+ setVaultSyncStatus,
6845
+ upsertProviderVaultItems
6846
+ } from "@opengeni/db";
6847
+ import {
6848
+ isVaultProviderError,
6849
+ VaultProviderError as VaultProviderError2
6850
+ } from "@opengeni/vaults";
6851
+ var PROVIDER_RATE_LIMIT_DEFAULT_BACKOFF_MS = 15 * 6e4;
6852
+ var FORBIDDEN_NAME_CHARACTERS = /[/{}#\\\u0000-\u001f\u007f-\u009f]/g;
6853
+ function nameKey(name) {
6854
+ return name.normalize("NFC").trim().toLowerCase();
6855
+ }
6856
+ function truncateChars(value, max) {
6857
+ const chars = [...value];
6858
+ return chars.length > max ? chars.slice(0, max).join("") : value;
6859
+ }
6860
+ function providerSecretName(raw, fallback = "Untitled") {
6861
+ let name = truncateChars(
6862
+ raw.normalize("NFC").replace(FORBIDDEN_NAME_CHARACTERS, "-").trim(),
6863
+ VAULT_LIMITS.nameMaxChars
6864
+ ).trim();
6865
+ if (!name) name = fallback;
6866
+ if (/^id:/iu.test(name)) name = `id-${name.slice(3)}`.trim() || fallback;
6867
+ return isValidSecretName(name) ? name : fallback;
6868
+ }
6869
+ function uniqueProviderName(name, taken) {
6870
+ let candidate = name;
6871
+ for (let n = 2; taken.has(nameKey(candidate)); n += 1) {
6872
+ const suffix = ` (${n})`;
6873
+ candidate = `${truncateChars(name, VAULT_LIMITS.nameMaxChars - suffix.length).trimEnd()}${suffix}`;
6874
+ }
6875
+ taken.add(nameKey(candidate));
6876
+ return candidate;
6877
+ }
6878
+ var PRIMARY_FIELD_FALLBACK = ["password", "value", "credential", "note"];
6879
+ function providerPrimaryFieldId(item) {
6880
+ const candidates = item.fields.filter((field) => field.kind !== "totp");
6881
+ const byName = (name) => candidates.find((field) => nameKey(field.name) === name)?.externalFieldId ?? null;
6882
+ const typeDefault = VAULT_DEFAULT_PRIMARY_FIELD[item.type];
6883
+ if (typeDefault) {
6884
+ const found = byName(typeDefault.toLowerCase());
6885
+ if (found) return found;
6886
+ }
6887
+ for (const name of PRIMARY_FIELD_FALLBACK) {
6888
+ const found = byName(name);
6889
+ if (found) return found;
6890
+ }
6891
+ if (item.type === "file") {
6892
+ const file = candidates.find((field) => field.kind === "file");
6893
+ if (file) return file.externalFieldId;
6894
+ }
6895
+ return candidates.find((field) => field.kind === "concealed")?.externalFieldId ?? null;
6896
+ }
6897
+ function planProviderMirror(items, contentVersion) {
6898
+ const ordered = [...items].sort(
6899
+ (a, b) => nameKey(a.name).localeCompare(nameKey(b.name)) || a.externalItemId.localeCompare(b.externalItemId)
6900
+ );
6901
+ const kept = ordered.slice(0, VAULT_LIMITS.itemsPerVault);
6902
+ const takenItems = /* @__PURE__ */ new Set();
6903
+ const mirrored = kept.map((item) => {
6904
+ const name = uniqueProviderName(providerSecretName(item.name), takenItems);
6905
+ const takenFields = /* @__PURE__ */ new Set();
6906
+ const fields = item.fields.slice(0, VAULT_LIMITS.fieldsPerItem).map((field, position) => {
6907
+ let fieldName = providerSecretName(
6908
+ field.name,
6909
+ field.kind === "totp" ? "one-time password" : "field"
6910
+ );
6911
+ if (nameKey(fieldName) === "otp") fieldName = "otp code";
6912
+ return {
6913
+ externalFieldId: field.externalFieldId,
6914
+ name: uniqueProviderName(fieldName, takenFields),
6915
+ kind: field.kind,
6916
+ position
6917
+ };
6918
+ });
6919
+ return {
6920
+ externalItemId: item.externalItemId,
6921
+ name,
6922
+ providerName: item.name,
6923
+ type: item.type,
6924
+ websites: item.websites.filter((website) => website.length <= VAULT_LIMITS.websiteMaxChars).slice(0, VAULT_LIMITS.websitesPerItem),
6925
+ fields,
6926
+ primaryExternalFieldId: providerPrimaryFieldId({ type: item.type, fields: item.fields }),
6927
+ hasTotp: item.fields.some((field) => field.kind === "totp"),
6928
+ version: item.version,
6929
+ updatedAt: item.updatedAt
6930
+ };
6931
+ });
6932
+ return { contentVersion, items: mirrored, truncated: ordered.length - kept.length };
6933
+ }
6934
+ function providerSyncFailure(error, now) {
6935
+ const code = isVaultProviderError(error) ? error.code : "provider_unavailable";
6936
+ const message = isVaultProviderError(error) ? error.message : "the vault provider could not be reached";
6937
+ if (code === "provider_rate_limited") {
6938
+ const retryAfterMs = isVaultProviderError(error) && error.retryAfterMs && error.retryAfterMs > 0 ? error.retryAfterMs : PROVIDER_RATE_LIMIT_DEFAULT_BACKOFF_MS;
6939
+ return {
6940
+ syncStatus: "rate_limited",
6941
+ syncErrorCode: code,
6942
+ syncErrorMessage: message,
6943
+ rateLimitedUntil: new Date(now.getTime() + retryAfterMs),
6944
+ lastSyncedAt: null
6945
+ };
6946
+ }
6947
+ return {
6948
+ syncStatus: code === "provider_auth_expired" || code === "provider_auth_invalid" ? "auth_expired" : "error",
6949
+ syncErrorCode: code,
6950
+ syncErrorMessage: message,
6951
+ rateLimitedUntil: null,
6952
+ lastSyncedAt: null
6953
+ };
6954
+ }
6955
+ async function syncProviderVault(input) {
6956
+ const now = input.now ?? (() => /* @__PURE__ */ new Date());
6957
+ const { target } = input;
6958
+ if (target.rateLimitedUntil && target.rateLimitedUntil.getTime() > now().getTime() && !input.force) {
6959
+ return { status: "skipped_rate_limited", rateLimitedUntil: target.rateLimitedUntil };
6960
+ }
6961
+ try {
6962
+ const vaults = await input.provider.listVaults(input.ctx);
6963
+ const vault = vaults.find((candidate) => candidate.externalVaultId === target.externalVaultId);
6964
+ if (!vault) {
6965
+ const failure = providerSyncFailure(
6966
+ new VaultProviderError2(
6967
+ "provider_not_found",
6968
+ "the linked provider vault no longer exists or is no longer shared with this connection"
6969
+ ),
6970
+ now()
6971
+ );
6972
+ await input.store.setSyncStatus(failure);
6973
+ return {
6974
+ status: "failed",
6975
+ syncStatus: failure.syncStatus,
6976
+ code: failure.syncErrorCode,
6977
+ rateLimitedUntil: null
6978
+ };
6979
+ }
6980
+ if (!input.force && vault.contentVersion !== null && vault.contentVersion === target.mirroredContentVersion) {
6981
+ await input.store.setSyncStatus({
6982
+ syncStatus: "ok",
6983
+ syncErrorCode: null,
6984
+ syncErrorMessage: null,
6985
+ rateLimitedUntil: null,
6986
+ lastSyncedAt: now()
6987
+ });
6988
+ return { status: "unchanged", contentVersion: vault.contentVersion };
6989
+ }
6990
+ const items = await input.provider.listItems(input.ctx, target.externalVaultId);
6991
+ const plan = planProviderMirror(items, vault.contentVersion);
6992
+ await input.store.replaceMirror(plan);
6993
+ await input.store.setSyncStatus({
6994
+ syncStatus: "ok",
6995
+ syncErrorCode: null,
6996
+ syncErrorMessage: null,
6997
+ rateLimitedUntil: null,
6998
+ lastSyncedAt: now()
6999
+ });
7000
+ return {
7001
+ status: "synced",
7002
+ contentVersion: plan.contentVersion,
7003
+ itemCount: plan.items.length,
7004
+ truncated: plan.truncated
7005
+ };
7006
+ } catch (error) {
7007
+ if (!isVaultProviderError(error)) throw error;
7008
+ const failure = providerSyncFailure(error, now());
7009
+ await input.store.setSyncStatus(failure);
7010
+ return {
7011
+ status: "failed",
7012
+ syncStatus: failure.syncStatus,
7013
+ code: failure.syncErrorCode,
7014
+ rateLimitedUntil: failure.rateLimitedUntil
7015
+ };
7016
+ }
7017
+ }
7018
+ function databaseProviderMirrorStore(db, context, vaultId) {
7019
+ return {
7020
+ async replaceMirror(plan) {
7021
+ await upsertProviderVaultItems(db, context, {
7022
+ vaultId,
7023
+ contentVersion: plan.contentVersion,
7024
+ items: plan.items.map((item) => ({
7025
+ providerItemRef: item.externalItemId,
7026
+ providerVersion: item.version,
7027
+ // Passkeys live only in hosted vaults (D20); providers never mirror them.
7028
+ type: item.type === "passkey" ? "login" : item.type,
7029
+ // The base name: the routine de-duplicates against the stored mirror
7030
+ // so names stay sticky per provider item.
7031
+ name: providerSecretName(item.providerName),
7032
+ websites: item.websites,
7033
+ primaryFieldRef: item.primaryExternalFieldId,
7034
+ fields: item.fields.map((field) => ({
7035
+ providerFieldRef: field.externalFieldId,
7036
+ name: field.name,
7037
+ kind: field.kind
7038
+ }))
7039
+ }))
7040
+ });
7041
+ },
7042
+ async setSyncStatus(update) {
7043
+ await setVaultSyncStatus(db, context, {
7044
+ vaultId,
7045
+ status: update.syncStatus,
7046
+ errorCode: update.syncErrorCode,
7047
+ errorMessage: update.syncErrorMessage,
7048
+ rateLimitedUntil: update.rateLimitedUntil?.toISOString() ?? null
7049
+ });
7050
+ }
7051
+ };
7052
+ }
7053
+ async function syncLinkedProviderVault(input) {
7054
+ const target = await getProviderVaultSyncTarget(input.db, input.context, input.vaultId);
7055
+ const store = databaseProviderMirrorStore(input.db, input.context, input.vaultId);
7056
+ let provider;
7057
+ try {
7058
+ provider = await input.providers.forVault({
7059
+ source: target.source,
7060
+ providerConnectionId: target.providerConnectionId
7061
+ });
7062
+ } catch (error) {
7063
+ if (!isVaultProviderError(error)) throw error;
7064
+ const failure = providerSyncFailure(error, (input.now ?? (() => /* @__PURE__ */ new Date()))());
7065
+ await store.setSyncStatus(failure);
7066
+ return {
7067
+ status: "failed",
7068
+ syncStatus: failure.syncStatus,
7069
+ code: failure.syncErrorCode,
7070
+ rateLimitedUntil: failure.rateLimitedUntil
7071
+ };
7072
+ }
7073
+ return await syncProviderVault({
7074
+ provider,
7075
+ ctx: {
7076
+ connectionId: target.providerConnectionId,
7077
+ budget: input.budget ?? { take() {
7078
+ } }
7079
+ },
7080
+ target: {
7081
+ externalVaultId: target.providerVaultRef,
7082
+ mirroredContentVersion: target.providerContentVersion,
7083
+ rateLimitedUntil: target.rateLimitedUntil ? new Date(target.rateLimitedUntil) : null
7084
+ },
7085
+ store,
7086
+ ...input.now ? { now: input.now } : {},
7087
+ ...input.force ? { force: true } : {}
7088
+ });
7089
+ }
7090
+ var LINKED_VAULT_SEARCH_MAX_AGE_MS = 10 * 6e4;
7091
+ var LINKED_VAULT_MISS_MAX_AGE_MS = 2 * 6e4;
7092
+ var LINKED_VAULT_REFRESH_WAIT_MS = 8e3;
7093
+ function linkedVaultNeedsRefresh(vault, maxAgeMs, now) {
7094
+ if (vault.source === "opengeni") return false;
7095
+ if (vault.rateLimitedUntil && new Date(vault.rateLimitedUntil).getTime() > now.getTime()) {
7096
+ return false;
7097
+ }
7098
+ const last = vault.syncStatus === "ok" || vault.syncStatus === "syncing" ? vault.lastSyncedAt : vault.updatedAt;
7099
+ if (!last) return true;
7100
+ return now.getTime() - new Date(last).getTime() >= maxAgeMs;
7101
+ }
7102
+ var linkedVaultRefreshes = /* @__PURE__ */ new Map();
7103
+ async function refreshStaleLinkedVaults(input) {
7104
+ const now = (input.now ?? (() => /* @__PURE__ */ new Date()))();
7105
+ const stale = input.vaults.filter((vault) => linkedVaultNeedsRefresh(vault, input.maxAgeMs, now));
7106
+ if (stale.length === 0) return [];
7107
+ const changed = [];
7108
+ const runs = stale.map((vault) => {
7109
+ let run = linkedVaultRefreshes.get(vault.id);
7110
+ if (!run) {
7111
+ run = syncLinkedProviderVault({
7112
+ db: input.db,
7113
+ context: input.context,
7114
+ vaultId: vault.id,
7115
+ providers: input.providers,
7116
+ ...input.now ? { now: input.now } : {}
7117
+ }).catch(() => null).finally(() => linkedVaultRefreshes.delete(vault.id));
7118
+ linkedVaultRefreshes.set(vault.id, run);
7119
+ }
7120
+ return run.then((result) => {
7121
+ if (result?.status === "synced") changed.push(vault.id);
7122
+ });
7123
+ });
7124
+ let timer;
7125
+ await Promise.race([
7126
+ Promise.all(runs),
7127
+ new Promise((resolve) => {
7128
+ timer = setTimeout(resolve, input.waitMs ?? LINKED_VAULT_REFRESH_WAIT_MS);
7129
+ timer.unref?.();
7130
+ })
7131
+ ]);
7132
+ if (timer) clearTimeout(timer);
7133
+ return [...changed];
7134
+ }
7135
+
7136
+ // src/domain/vaults/secret-resolver.ts
6820
7137
  function lookupFor(reference, index) {
6821
7138
  return reference.target.form === "id" ? {
6822
7139
  index,
@@ -6971,12 +7288,12 @@ var attemptBudgets = /* @__PURE__ */ new Map();
6971
7288
  var ATTEMPT_BUDGET_CACHE_LIMIT = 4096;
6972
7289
  function sharedAttemptBudget(principal) {
6973
7290
  if (principal.kind !== "attempt") {
6974
- return createProviderCallBudget(VAULT_LIMITS.providerReadsPerAttempt);
7291
+ return createProviderCallBudget(VAULT_LIMITS2.providerReadsPerAttempt);
6975
7292
  }
6976
7293
  const key = `${principal.attemptId}:${principal.executionGeneration}`;
6977
7294
  let budget = attemptBudgets.get(key);
6978
7295
  if (!budget) {
6979
- budget = createProviderCallBudget(VAULT_LIMITS.providerReadsPerAttempt);
7296
+ budget = createProviderCallBudget(VAULT_LIMITS2.providerReadsPerAttempt);
6980
7297
  attemptBudgets.set(key, budget);
6981
7298
  while (attemptBudgets.size > ATTEMPT_BUDGET_CACHE_LIMIT) {
6982
7299
  attemptBudgets.delete(attemptBudgets.keys().next().value);
@@ -7005,20 +7322,40 @@ function createSecretResolver(deps, principal, opts) {
7005
7322
  null
7006
7323
  );
7007
7324
  }
7008
- const { meta, candidates } = await resolveSecretReferenceCandidates(deps.db, context, {
7009
- refs: references.map((reference, index) => lookupFor(reference, index))
7010
- });
7011
- const failures = [];
7012
- const chosen = [];
7013
- references.forEach((reference, index) => {
7014
- const outcome = chooseSecretCandidate(
7015
- reference,
7016
- candidates.filter((candidate) => candidate.refIndex === index),
7017
- meta
7018
- );
7019
- if ("failure" in outcome) failures.push(outcome.failure);
7020
- else chosen.push({ reference, candidate: outcome.candidate });
7021
- });
7325
+ const choose = async () => {
7326
+ const { meta: meta2, candidates } = await resolveSecretReferenceCandidates(deps.db, context, {
7327
+ refs: references.map((reference, index) => lookupFor(reference, index))
7328
+ });
7329
+ const failures2 = [];
7330
+ const chosen2 = [];
7331
+ references.forEach((reference, index) => {
7332
+ const outcome = chooseSecretCandidate(
7333
+ reference,
7334
+ candidates.filter((candidate) => candidate.refIndex === index),
7335
+ meta2
7336
+ );
7337
+ if ("failure" in outcome) failures2.push(outcome.failure);
7338
+ else chosen2.push({ reference, candidate: outcome.candidate });
7339
+ });
7340
+ return { meta: meta2, failures: failures2, chosen: chosen2 };
7341
+ };
7342
+ let { meta, failures, chosen } = await choose();
7343
+ if (failures.some((failure) => failure.code === "not_found")) {
7344
+ const registry = deps.providers ?? opts.providersForSubject?.(meta.subjectId);
7345
+ if (registry) {
7346
+ const reachable = await listVaultsForSubject(deps.db, context, {
7347
+ workspaceId: principal.workspaceId
7348
+ });
7349
+ const changed = await refreshStaleLinkedVaults({
7350
+ db: deps.db,
7351
+ context,
7352
+ vaults: reachable,
7353
+ providers: registry,
7354
+ maxAgeMs: LINKED_VAULT_MISS_MAX_AGE_MS
7355
+ });
7356
+ if (changed.length > 0) ({ meta, failures, chosen } = await choose());
7357
+ }
7358
+ }
7022
7359
  const hasActingHuman = meta.humanExpected && meta.humanValid;
7023
7360
  if (failures.length > 0) {
7024
7361
  throw new SecretReferenceError(
@@ -7051,7 +7388,7 @@ function createSecretResolver(deps, principal, opts) {
7051
7388
  providerValues.set(`${first.itemId}|${value.externalFieldId}`, value.material);
7052
7389
  }
7053
7390
  } catch (error) {
7054
- const code = isVaultProviderError(error) ? providerFailureCode(error.code) : "provider_unavailable";
7391
+ const code = isVaultProviderError2(error) ? providerFailureCode(error.code) : "provider_unavailable";
7055
7392
  for (const entry of group) {
7056
7393
  failures.push({
7057
7394
  raw: entry.reference.raw,
@@ -15483,261 +15820,6 @@ function createWebSearchBilling(deps) {
15483
15820
  };
15484
15821
  }
15485
15822
 
15486
- // src/domain/vaults/provider-sync.ts
15487
- import {
15488
- isValidSecretName,
15489
- VAULT_DEFAULT_PRIMARY_FIELD,
15490
- VAULT_LIMITS as VAULT_LIMITS2
15491
- } from "@opengeni/contracts";
15492
- import {
15493
- getProviderVaultSyncTarget,
15494
- setVaultSyncStatus,
15495
- upsertProviderVaultItems
15496
- } from "@opengeni/db";
15497
- import {
15498
- isVaultProviderError as isVaultProviderError2,
15499
- VaultProviderError as VaultProviderError2
15500
- } from "@opengeni/vaults";
15501
- var PROVIDER_RATE_LIMIT_DEFAULT_BACKOFF_MS = 15 * 6e4;
15502
- var FORBIDDEN_NAME_CHARACTERS = /[/{}#\\\u0000-\u001f\u007f-\u009f]/g;
15503
- function nameKey(name) {
15504
- return name.normalize("NFC").trim().toLowerCase();
15505
- }
15506
- function truncateChars(value, max) {
15507
- const chars = [...value];
15508
- return chars.length > max ? chars.slice(0, max).join("") : value;
15509
- }
15510
- function providerSecretName(raw, fallback = "Untitled") {
15511
- let name = truncateChars(
15512
- raw.normalize("NFC").replace(FORBIDDEN_NAME_CHARACTERS, "-").trim(),
15513
- VAULT_LIMITS2.nameMaxChars
15514
- ).trim();
15515
- if (!name) name = fallback;
15516
- if (/^id:/iu.test(name)) name = `id-${name.slice(3)}`.trim() || fallback;
15517
- return isValidSecretName(name) ? name : fallback;
15518
- }
15519
- function uniqueProviderName(name, taken) {
15520
- let candidate = name;
15521
- for (let n = 2; taken.has(nameKey(candidate)); n += 1) {
15522
- const suffix = ` (${n})`;
15523
- candidate = `${truncateChars(name, VAULT_LIMITS2.nameMaxChars - suffix.length).trimEnd()}${suffix}`;
15524
- }
15525
- taken.add(nameKey(candidate));
15526
- return candidate;
15527
- }
15528
- var PRIMARY_FIELD_FALLBACK = ["password", "value", "credential", "note"];
15529
- function providerPrimaryFieldId(item) {
15530
- const candidates = item.fields.filter((field) => field.kind !== "totp");
15531
- const byName = (name) => candidates.find((field) => nameKey(field.name) === name)?.externalFieldId ?? null;
15532
- const typeDefault = VAULT_DEFAULT_PRIMARY_FIELD[item.type];
15533
- if (typeDefault) {
15534
- const found = byName(typeDefault.toLowerCase());
15535
- if (found) return found;
15536
- }
15537
- for (const name of PRIMARY_FIELD_FALLBACK) {
15538
- const found = byName(name);
15539
- if (found) return found;
15540
- }
15541
- if (item.type === "file") {
15542
- const file = candidates.find((field) => field.kind === "file");
15543
- if (file) return file.externalFieldId;
15544
- }
15545
- return candidates.find((field) => field.kind === "concealed")?.externalFieldId ?? null;
15546
- }
15547
- function planProviderMirror(items, contentVersion) {
15548
- const ordered = [...items].sort(
15549
- (a, b) => nameKey(a.name).localeCompare(nameKey(b.name)) || a.externalItemId.localeCompare(b.externalItemId)
15550
- );
15551
- const kept = ordered.slice(0, VAULT_LIMITS2.itemsPerVault);
15552
- const takenItems = /* @__PURE__ */ new Set();
15553
- const mirrored = kept.map((item) => {
15554
- const name = uniqueProviderName(providerSecretName(item.name), takenItems);
15555
- const takenFields = /* @__PURE__ */ new Set();
15556
- const fields = item.fields.slice(0, VAULT_LIMITS2.fieldsPerItem).map((field, position) => {
15557
- let fieldName = providerSecretName(
15558
- field.name,
15559
- field.kind === "totp" ? "one-time password" : "field"
15560
- );
15561
- if (nameKey(fieldName) === "otp") fieldName = "otp code";
15562
- return {
15563
- externalFieldId: field.externalFieldId,
15564
- name: uniqueProviderName(fieldName, takenFields),
15565
- kind: field.kind,
15566
- position
15567
- };
15568
- });
15569
- return {
15570
- externalItemId: item.externalItemId,
15571
- name,
15572
- providerName: item.name,
15573
- type: item.type,
15574
- websites: item.websites.filter((website) => website.length <= VAULT_LIMITS2.websiteMaxChars).slice(0, VAULT_LIMITS2.websitesPerItem),
15575
- fields,
15576
- primaryExternalFieldId: providerPrimaryFieldId({ type: item.type, fields: item.fields }),
15577
- hasTotp: item.fields.some((field) => field.kind === "totp"),
15578
- version: item.version,
15579
- updatedAt: item.updatedAt
15580
- };
15581
- });
15582
- return { contentVersion, items: mirrored, truncated: ordered.length - kept.length };
15583
- }
15584
- function providerSyncFailure(error, now) {
15585
- const code = isVaultProviderError2(error) ? error.code : "provider_unavailable";
15586
- const message = isVaultProviderError2(error) ? error.message : "the vault provider could not be reached";
15587
- if (code === "provider_rate_limited") {
15588
- const retryAfterMs = isVaultProviderError2(error) && error.retryAfterMs && error.retryAfterMs > 0 ? error.retryAfterMs : PROVIDER_RATE_LIMIT_DEFAULT_BACKOFF_MS;
15589
- return {
15590
- syncStatus: "rate_limited",
15591
- syncErrorCode: code,
15592
- syncErrorMessage: message,
15593
- rateLimitedUntil: new Date(now.getTime() + retryAfterMs),
15594
- lastSyncedAt: null
15595
- };
15596
- }
15597
- return {
15598
- syncStatus: code === "provider_auth_expired" || code === "provider_auth_invalid" ? "auth_expired" : "error",
15599
- syncErrorCode: code,
15600
- syncErrorMessage: message,
15601
- rateLimitedUntil: null,
15602
- lastSyncedAt: null
15603
- };
15604
- }
15605
- async function syncProviderVault(input) {
15606
- const now = input.now ?? (() => /* @__PURE__ */ new Date());
15607
- const { target } = input;
15608
- if (target.rateLimitedUntil && target.rateLimitedUntil.getTime() > now().getTime() && !input.force) {
15609
- return { status: "skipped_rate_limited", rateLimitedUntil: target.rateLimitedUntil };
15610
- }
15611
- try {
15612
- const vaults = await input.provider.listVaults(input.ctx);
15613
- const vault = vaults.find((candidate) => candidate.externalVaultId === target.externalVaultId);
15614
- if (!vault) {
15615
- const failure = providerSyncFailure(
15616
- new VaultProviderError2(
15617
- "provider_not_found",
15618
- "the linked provider vault no longer exists or is no longer shared with this connection"
15619
- ),
15620
- now()
15621
- );
15622
- await input.store.setSyncStatus(failure);
15623
- return {
15624
- status: "failed",
15625
- syncStatus: failure.syncStatus,
15626
- code: failure.syncErrorCode,
15627
- rateLimitedUntil: null
15628
- };
15629
- }
15630
- if (!input.force && vault.contentVersion !== null && vault.contentVersion === target.mirroredContentVersion) {
15631
- await input.store.setSyncStatus({
15632
- syncStatus: "ok",
15633
- syncErrorCode: null,
15634
- syncErrorMessage: null,
15635
- rateLimitedUntil: null,
15636
- lastSyncedAt: now()
15637
- });
15638
- return { status: "unchanged", contentVersion: vault.contentVersion };
15639
- }
15640
- const items = await input.provider.listItems(input.ctx, target.externalVaultId);
15641
- const plan = planProviderMirror(items, vault.contentVersion);
15642
- await input.store.replaceMirror(plan);
15643
- await input.store.setSyncStatus({
15644
- syncStatus: "ok",
15645
- syncErrorCode: null,
15646
- syncErrorMessage: null,
15647
- rateLimitedUntil: null,
15648
- lastSyncedAt: now()
15649
- });
15650
- return {
15651
- status: "synced",
15652
- contentVersion: plan.contentVersion,
15653
- itemCount: plan.items.length,
15654
- truncated: plan.truncated
15655
- };
15656
- } catch (error) {
15657
- if (!isVaultProviderError2(error)) throw error;
15658
- const failure = providerSyncFailure(error, now());
15659
- await input.store.setSyncStatus(failure);
15660
- return {
15661
- status: "failed",
15662
- syncStatus: failure.syncStatus,
15663
- code: failure.syncErrorCode,
15664
- rateLimitedUntil: failure.rateLimitedUntil
15665
- };
15666
- }
15667
- }
15668
- function databaseProviderMirrorStore(db, context, vaultId) {
15669
- return {
15670
- async replaceMirror(plan) {
15671
- await upsertProviderVaultItems(db, context, {
15672
- vaultId,
15673
- contentVersion: plan.contentVersion,
15674
- items: plan.items.map((item) => ({
15675
- providerItemRef: item.externalItemId,
15676
- providerVersion: item.version,
15677
- // Passkeys live only in hosted vaults (D20); providers never mirror them.
15678
- type: item.type === "passkey" ? "login" : item.type,
15679
- // The base name: the routine de-duplicates against the stored mirror
15680
- // so names stay sticky per provider item.
15681
- name: providerSecretName(item.providerName),
15682
- websites: item.websites,
15683
- primaryFieldRef: item.primaryExternalFieldId,
15684
- fields: item.fields.map((field) => ({
15685
- providerFieldRef: field.externalFieldId,
15686
- name: field.name,
15687
- kind: field.kind
15688
- }))
15689
- }))
15690
- });
15691
- },
15692
- async setSyncStatus(update) {
15693
- await setVaultSyncStatus(db, context, {
15694
- vaultId,
15695
- status: update.syncStatus,
15696
- errorCode: update.syncErrorCode,
15697
- errorMessage: update.syncErrorMessage,
15698
- rateLimitedUntil: update.rateLimitedUntil?.toISOString() ?? null
15699
- });
15700
- }
15701
- };
15702
- }
15703
- async function syncLinkedProviderVault(input) {
15704
- const target = await getProviderVaultSyncTarget(input.db, input.context, input.vaultId);
15705
- const store = databaseProviderMirrorStore(input.db, input.context, input.vaultId);
15706
- let provider;
15707
- try {
15708
- provider = await input.providers.forVault({
15709
- source: target.source,
15710
- providerConnectionId: target.providerConnectionId
15711
- });
15712
- } catch (error) {
15713
- if (!isVaultProviderError2(error)) throw error;
15714
- const failure = providerSyncFailure(error, (input.now ?? (() => /* @__PURE__ */ new Date()))());
15715
- await store.setSyncStatus(failure);
15716
- return {
15717
- status: "failed",
15718
- syncStatus: failure.syncStatus,
15719
- code: failure.syncErrorCode,
15720
- rateLimitedUntil: failure.rateLimitedUntil
15721
- };
15722
- }
15723
- return await syncProviderVault({
15724
- provider,
15725
- ctx: {
15726
- connectionId: target.providerConnectionId,
15727
- budget: input.budget ?? { take() {
15728
- } }
15729
- },
15730
- target: {
15731
- externalVaultId: target.providerVaultRef,
15732
- mirroredContentVersion: target.providerContentVersion,
15733
- rateLimitedUntil: target.rateLimitedUntil ? new Date(target.rateLimitedUntil) : null
15734
- },
15735
- store,
15736
- ...input.now ? { now: input.now } : {},
15737
- ...input.force ? { force: true } : {}
15738
- });
15739
- }
15740
-
15741
15823
  // src/workflow-wake-contract.ts
15742
15824
  var SESSION_WORKFLOW_WAKE_DISPATCHER_SCHEDULE_ID = "opengeni-session-workflow-wake-dispatcher";
15743
15825
  var SESSION_WORKFLOW_WAKE_DISPATCHER_WORKFLOW_TYPE = "sessionWorkflowWakeDispatcherWorkflow";
@@ -18102,7 +18184,7 @@ import {
18102
18184
  getVaultItem,
18103
18185
  listVaultFieldVersions,
18104
18186
  listVaultItems,
18105
- listVaultsForSubject,
18187
+ listVaultsForSubject as listVaultsForSubject2,
18106
18188
  vaultItemCursor,
18107
18189
  moveVaultItem,
18108
18190
  readVaultFieldCiphertext,
@@ -18173,7 +18255,7 @@ async function providerFieldWrites(fields, existing, resolveReference, generated
18173
18255
  if (input.fileBase64 !== void 0 || kind === "file") {
18174
18256
  throw new VaultError(
18175
18257
  "provider_unavailable",
18176
- "files in connected vaults are read-only in OpenGeni",
18258
+ "files in connected vaults are read-only in Opengeni",
18177
18259
  { field: input.name }
18178
18260
  );
18179
18261
  }
@@ -18235,7 +18317,7 @@ async function refreshAndFind(deps, vault, providerItemRef) {
18235
18317
  if (error instanceof VaultError && error.code === "vault_item_not_found") {
18236
18318
  throw new VaultError(
18237
18319
  "provider_unavailable",
18238
- "saved in the provider; OpenGeni will show it after the next sync",
18320
+ "saved in the provider; Opengeni will show it after the next sync",
18239
18321
  { vaultId: vault.id }
18240
18322
  );
18241
18323
  }
@@ -18246,7 +18328,7 @@ async function writeProviderVaultItem(deps, input) {
18246
18328
  const { vault, existing, change } = input;
18247
18329
  assertProviderVaultWritable(vault);
18248
18330
  if ((change.type ?? existing?.type) === "passkey") {
18249
- throw new VaultError("vault_permission_denied", "passkeys live only in OpenGeni vaults");
18331
+ throw new VaultError("vault_permission_denied", "passkeys live only in Opengeni vaults");
18250
18332
  }
18251
18333
  let externalItemId;
18252
18334
  let expectedVersion;
@@ -18637,7 +18719,7 @@ function createVaultService(deps) {
18637
18719
  return session?.agent?.capabilities.secrets ?? true;
18638
18720
  }
18639
18721
  async function agentVault(principal, nameOrId) {
18640
- const vaults = await listVaultsForSubject(deps.db, principalContext(principal), {
18722
+ const vaults = await listVaultsForSubject2(deps.db, principalContext(principal), {
18641
18723
  workspaceId: principal.workspaceId
18642
18724
  });
18643
18725
  const byId = vaults.find((vault) => vault.id === nameOrId.toLowerCase());
@@ -18729,14 +18811,14 @@ function createVaultService(deps) {
18729
18811
  }
18730
18812
  return {
18731
18813
  async list(actor, workspaceId) {
18732
- const rows = await listVaultsForSubject(deps.db, subjectContext(actor, workspaceId), {
18814
+ const rows = await listVaultsForSubject2(deps.db, subjectContext(actor, workspaceId), {
18733
18815
  workspaceId
18734
18816
  });
18735
18817
  const authority = authorityOf(actor);
18736
18818
  return rows.map((row) => toVaultSummary(row, authority));
18737
18819
  },
18738
18820
  async get(actor, workspaceId, vaultId) {
18739
- const rows = await listVaultsForSubject(deps.db, subjectContext(actor, workspaceId), {
18821
+ const rows = await listVaultsForSubject2(deps.db, subjectContext(actor, workspaceId), {
18740
18822
  workspaceId
18741
18823
  });
18742
18824
  const row = rows.find((candidate) => candidate.id === vaultId);
@@ -18935,9 +19017,23 @@ function createVaultService(deps) {
18935
19017
  },
18936
19018
  async searchForAgent(principal, input) {
18937
19019
  const context = principalContext(principal);
18938
- const reachable = await listVaultsForSubject(deps.db, context, {
19020
+ let reachable = await listVaultsForSubject2(deps.db, context, {
18939
19021
  workspaceId: principal.workspaceId
18940
19022
  });
19023
+ if (deps.providers) {
19024
+ const changed = await refreshStaleLinkedVaults({
19025
+ db: deps.db,
19026
+ context,
19027
+ vaults: reachable,
19028
+ providers: deps.providers,
19029
+ maxAgeMs: LINKED_VAULT_SEARCH_MAX_AGE_MS
19030
+ });
19031
+ if (changed.length > 0) {
19032
+ reachable = await listVaultsForSubject2(deps.db, context, {
19033
+ workspaceId: principal.workspaceId
19034
+ });
19035
+ }
19036
+ }
18941
19037
  const byTier = (vaults2) => [...vaults2].sort(
18942
19038
  (left, right) => left.tier - right.tier || left.name.localeCompare(right.name)
18943
19039
  );
@@ -27870,6 +27966,9 @@ export {
27870
27966
  KnowledgeVectorCreditsDisabledError,
27871
27967
  KnowledgeVectorFundingError,
27872
27968
  KnowledgeVectorQueryRejectedError,
27969
+ LINKED_VAULT_MISS_MAX_AGE_MS,
27970
+ LINKED_VAULT_REFRESH_WAIT_MS,
27971
+ LINKED_VAULT_SEARCH_MAX_AGE_MS,
27873
27972
  MAX_CHECKS_PER_RIG,
27874
27973
  MAX_CREDENTIAL_HOOKS_PER_RIG,
27875
27974
  MAX_EDITABLE_ARTIFACT_SNAPSHOT_BYTES,
@@ -28216,6 +28315,7 @@ export {
28216
28315
  knowledgeContextForAccess,
28217
28316
  knowledgeContextForGateway,
28218
28317
  legacySessionAgentCapabilities,
28318
+ linkedVaultNeedsRefresh,
28219
28319
  listExternalActorWorkspaces,
28220
28320
  listFleet,
28221
28321
  listGitHubActionPolicyActors,
@@ -28342,6 +28442,7 @@ export {
28342
28442
  recordWorkspaceUsage,
28343
28443
  refreshCoreCodexModelEntitlements,
28344
28444
  refreshScheduledTaskAccess,
28445
+ refreshStaleLinkedVaults,
28345
28446
  registerAutomationAdapter,
28346
28447
  registerProductUsageMetricBaselines,
28347
28448
  rejectSkill,