@opengeni/core 3.1.2 → 3.2.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/dist/index.js CHANGED
@@ -1011,10 +1011,10 @@ import { requireSession as requireSession3, requireWorkspace as requireWorkspace
1011
1011
  // src/domain/capabilities.ts
1012
1012
  import { Client } from "@modelcontextprotocol/sdk/client/index.js";
1013
1013
  import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
1014
- import { isDeepStrictEqual } from "util";
1014
+ import { isDeepStrictEqual as isDeepStrictEqual2 } from "util";
1015
1015
  import { withLockedCapabilityInstallation } from "@opengeni/db/capability-reconciliation";
1016
1016
  import {
1017
- environmentsEncryptionKeyBytes,
1017
+ environmentsEncryptionKeyBytes as environmentsEncryptionKeyBytes2,
1018
1018
  findIntegrationsOauthClient,
1019
1019
  parseIntegrationsOauthClientsJson
1020
1020
  } from "@opengeni/config";
@@ -1041,7 +1041,7 @@ import {
1041
1041
  encryptVariableSetValue,
1042
1042
  getCapabilityCatalogItem,
1043
1043
  getCapabilityInstallation,
1044
- getConnectionMetadata as getConnectionMetadata2,
1044
+ getConnectionMetadata as getConnectionMetadata3,
1045
1045
  readCodexCutoverDisposition,
1046
1046
  resolveSubscriptionCoreCodexAppsDesignation,
1047
1047
  rlsContextForWorkspace,
@@ -1099,14 +1099,263 @@ function preferredFikenConnection(connections) {
1099
1099
 
1100
1100
  // src/domain/vaults/provider-registry.ts
1101
1101
  import {
1102
+ getConnectionMetadata as getConnectionMetadata2,
1103
+ getProviderConnectionHomeWorkspace as getProviderConnectionHomeWorkspace2,
1104
+ loadConnectionCredentialForBroker as loadConnectionCredentialForBroker2
1105
+ } from "@opengeni/db";
1106
+ import {
1107
+ createVaultProviderRegistry,
1108
+ VaultProviderError as VaultProviderError2
1109
+ } from "@opengeni/vaults";
1110
+
1111
+ // src/domain/vaults/external-provider-connections.ts
1112
+ import { environmentsEncryptionKeyBytes } from "@opengeni/config";
1113
+ import { isDeepStrictEqual } from "util";
1114
+ import {
1115
+ ExternalVaultProviderKind,
1116
+ VaultProviderCredentials,
1117
+ VaultProviderOperations
1118
+ } from "@opengeni/contracts";
1119
+ import {
1120
+ encryptEnvironmentValue,
1102
1121
  getConnectionMetadata,
1103
1122
  getProviderConnectionHomeWorkspace,
1104
- loadConnectionCredentialForBroker
1123
+ loadConnectionCredentialForBroker,
1124
+ updateConnection
1105
1125
  } from "@opengeni/db";
1106
1126
  import {
1107
- createVaultProviderRegistry,
1127
+ providerOperations,
1108
1128
  VaultProviderError
1109
1129
  } from "@opengeni/vaults";
1130
+ import { z } from "zod";
1131
+ var EXTERNAL_VAULT_CREDENTIAL_ROLE = "external_vault_provider";
1132
+ var ExternalVaultConnectionDetails = z.object({
1133
+ version: z.literal(1),
1134
+ provider: ExternalVaultProviderKind,
1135
+ ownership: z.enum(["user", "workspace", "organization"]),
1136
+ accountId: z.string().min(1).max(300).nullable(),
1137
+ accountName: z.string().max(300).nullable(),
1138
+ label: z.string().max(120).nullable(),
1139
+ verifiedAt: z.string().datetime(),
1140
+ operations: VaultProviderOperations,
1141
+ verificationStatus: z.enum(["ready", "pending"]).optional()
1142
+ }).strict();
1143
+ function externalVaultProviderDomain(provider) {
1144
+ return `vault:${provider}`;
1145
+ }
1146
+ function externalVaultConnectionDetails(metadata) {
1147
+ if (metadata?.credentialRole !== EXTERNAL_VAULT_CREDENTIAL_ROLE) return null;
1148
+ const result = ExternalVaultConnectionDetails.safeParse(metadata.vaultProvider);
1149
+ return result.success ? result.data : null;
1150
+ }
1151
+ function isExternalVaultConnection(connection) {
1152
+ const details = externalVaultConnectionDetails(connection.metadata);
1153
+ return details !== null && connection.kind === "api_key" && connection.providerDomain === externalVaultProviderDomain(details.provider) && details.ownership === "user" === (connection.subjectId !== null);
1154
+ }
1155
+ var StoredVaultProviderCredentials = z.union([
1156
+ VaultProviderCredentials.options[0],
1157
+ VaultProviderCredentials.options[1],
1158
+ VaultProviderCredentials.options[2],
1159
+ z.object({
1160
+ provider: z.literal("keeper"),
1161
+ configuration: z.record(z.string().max(100), z.string().max(65536)).refine((value) => Object.keys(value).length <= 32)
1162
+ }).strict()
1163
+ ]);
1164
+ var PendingVaultCredentialEnvelope = z.object({
1165
+ vaultCredentialEnvelope: z.literal(1),
1166
+ current: StoredVaultProviderCredentials.nullable(),
1167
+ pending: StoredVaultProviderCredentials
1168
+ }).strict();
1169
+ function pendingVaultCredentialBundle(current, pending) {
1170
+ return { vaultCredentialEnvelope: 1, current, pending };
1171
+ }
1172
+ function externalVaultConnectionMetadata(details) {
1173
+ return {
1174
+ credentialRole: EXTERNAL_VAULT_CREDENTIAL_ROLE,
1175
+ vaultProvider: ExternalVaultConnectionDetails.parse(details)
1176
+ };
1177
+ }
1178
+ function unavailable() {
1179
+ return new VaultProviderError(
1180
+ "provider_auth_invalid",
1181
+ "This vault connection was removed or is not available to you. Reconnect it in Vaults."
1182
+ );
1183
+ }
1184
+ async function loadExternalVaultConnection(db, settings, input) {
1185
+ let connection = await getConnectionMetadata(
1186
+ db,
1187
+ input.workspaceId,
1188
+ input.connectionId,
1189
+ input.subjectId
1190
+ );
1191
+ if (!connection) {
1192
+ const home = await getProviderConnectionHomeWorkspace(
1193
+ db,
1194
+ input.workspaceId,
1195
+ input.connectionId,
1196
+ input.subjectId
1197
+ );
1198
+ if (home && home !== input.workspaceId)
1199
+ connection = await getConnectionMetadata(db, home, input.connectionId, input.subjectId);
1200
+ }
1201
+ if (!connection || !isExternalVaultConnection(connection)) throw unavailable();
1202
+ const details = externalVaultConnectionDetails(connection.metadata);
1203
+ if (details.provider !== input.provider) throw unavailable();
1204
+ if (connection.status !== "active" && !(input.allowPendingVerification && (connection.status === "needs_reauth" || connection.status === "error")))
1205
+ throw new VaultProviderError(
1206
+ "provider_auth_expired",
1207
+ "This vault connection needs to be reconnected."
1208
+ );
1209
+ if (connection.subjectId !== null && connection.subjectId !== input.subjectId)
1210
+ throw unavailable();
1211
+ const loaded = await loadConnectionCredentialForBroker(db, settings, {
1212
+ workspaceId: connection.workspaceId,
1213
+ connectionId: connection.id,
1214
+ providerDomain: externalVaultProviderDomain(input.provider),
1215
+ kind: "api_key",
1216
+ ...connection.subjectId !== null ? { allowSubjectOwned: true, subjectId: input.subjectId } : {}
1217
+ });
1218
+ if (!loaded || loaded.status !== connection.status || loaded.version !== connection.version || loaded.accountId !== connection.accountId || loaded.subjectId !== connection.subjectId || loaded.providerDomain !== connection.providerDomain || loaded.kind !== "api_key" || externalVaultConnectionDetails(loaded.metadata)?.provider !== input.provider)
1219
+ throw unavailable();
1220
+ const envelope = PendingVaultCredentialEnvelope.safeParse(loaded.credential);
1221
+ const raw = envelope.success ? input.allowPendingVerification ? envelope.data.pending : envelope.data.current : loaded.credential;
1222
+ const parsed = StoredVaultProviderCredentials.safeParse(raw);
1223
+ if (!parsed.success || parsed.data.provider !== input.provider) throw unavailable();
1224
+ if (envelope.success && (envelope.data.pending.provider !== input.provider || envelope.data.current !== null && envelope.data.current.provider !== input.provider))
1225
+ throw unavailable();
1226
+ return {
1227
+ connection,
1228
+ details,
1229
+ credentials: parsed.data,
1230
+ currentCredentials: envelope.success ? envelope.data.current : parsed.data,
1231
+ pendingCredentials: envelope.success ? envelope.data.pending : null
1232
+ };
1233
+ }
1234
+ function bindVaultProviderAuthority(provider, input) {
1235
+ async function context(ctx) {
1236
+ ctx.signal?.throwIfAborted();
1237
+ await input.revalidate();
1238
+ ctx.signal?.throwIfAborted();
1239
+ return { ...ctx, connectionId: input.connectionId, authority: { ...input.authority } };
1240
+ }
1241
+ return {
1242
+ id: provider.id,
1243
+ capabilities: () => ({
1244
+ ...provider.capabilities(),
1245
+ operations: providerOperations(provider.capabilities(), { operations: input.operations })
1246
+ }),
1247
+ validate: async (ctx) => provider.validate(await context(ctx)),
1248
+ listVaults: async (ctx) => provider.listVaults(await context(ctx)),
1249
+ listItems: async (ctx, vaultId) => provider.listItems(await context(ctx), vaultId),
1250
+ readFields: async (ctx, vaultId, itemId, fields) => provider.readFields(await context(ctx), vaultId, itemId, fields),
1251
+ writeItem: async (ctx, vaultId, item) => provider.writeItem(await context(ctx), vaultId, item),
1252
+ archiveItem: async (ctx, vaultId, itemId) => provider.archiveItem(await context(ctx), vaultId, itemId),
1253
+ ...provider.listVaultPage ? { listVaultPage: async (ctx, page) => provider.listVaultPage(await context(ctx), page) } : {},
1254
+ ...provider.listItemPage ? {
1255
+ listItemPage: async (ctx, vaultId, page) => provider.listItemPage(await context(ctx), vaultId, page)
1256
+ } : {},
1257
+ ...provider.createVault ? { createVault: async (ctx, value) => provider.createVault(await context(ctx), value) } : {}
1258
+ };
1259
+ }
1260
+ async function externalVaultProviderForConnection(input) {
1261
+ const loaded = await loadExternalVaultConnection(input.db, input.settings, input);
1262
+ let connection = loaded.connection;
1263
+ const prepared = await input.factory({
1264
+ credentials: loaded.credentials,
1265
+ connectionId: connection.id,
1266
+ signal: AbortSignal.timeout(3e4),
1267
+ operations: loaded.details.operations,
1268
+ persistCredentials: async (next) => {
1269
+ const parsed = StoredVaultProviderCredentials.safeParse(next);
1270
+ if (!parsed.success || parsed.data.provider !== loaded.details.provider) throw unavailable();
1271
+ const credentials = parsed.data;
1272
+ const key = environmentsEncryptionKeyBytes(input.settings);
1273
+ if (!key)
1274
+ throw new VaultProviderError(
1275
+ "provider_unavailable",
1276
+ "Vault credential encryption is unavailable."
1277
+ );
1278
+ const updated = await updateConnection(input.db, {
1279
+ workspaceId: connection.workspaceId,
1280
+ connectionId: connection.id,
1281
+ visibleToSubjectId: input.subjectId,
1282
+ expectedVersion: connection.version,
1283
+ credentialEncrypted: encryptEnvironmentValue(
1284
+ key,
1285
+ JSON.stringify(
1286
+ loaded.pendingCredentials ? pendingVaultCredentialBundle(credentials, loaded.pendingCredentials) : credentials
1287
+ )
1288
+ )
1289
+ });
1290
+ if (updated) {
1291
+ connection = updated;
1292
+ return;
1293
+ }
1294
+ const live = await loadExternalVaultConnection(input.db, input.settings, input);
1295
+ if (isDeepStrictEqual(
1296
+ rotationConnectionIdentity(live.connection),
1297
+ rotationConnectionIdentity(connection)
1298
+ ) && isDeepStrictEqual(live.currentCredentials, credentials) && isDeepStrictEqual(live.pendingCredentials, loaded.pendingCredentials)) {
1299
+ connection = live.connection;
1300
+ return;
1301
+ }
1302
+ throw new VaultProviderError(
1303
+ "provider_conflict",
1304
+ "This vault connection changed while its credentials were renewed."
1305
+ );
1306
+ }
1307
+ });
1308
+ if (prepared.provider.id !== input.provider) throw unavailable();
1309
+ return bindVaultProviderAuthority(prepared.provider, {
1310
+ connectionId: connection.id,
1311
+ authority: {
1312
+ accountId: connection.accountId,
1313
+ workspaceId: input.workspaceId,
1314
+ connectionWorkspaceId: connection.workspaceId,
1315
+ subjectId: input.subjectId,
1316
+ ...input.executionContext
1317
+ },
1318
+ operations: loaded.details.operations,
1319
+ revalidate: async () => {
1320
+ if (connection.workspaceId !== input.workspaceId) {
1321
+ const home = await getProviderConnectionHomeWorkspace(
1322
+ input.db,
1323
+ input.workspaceId,
1324
+ connection.id,
1325
+ input.subjectId
1326
+ );
1327
+ if (home !== connection.workspaceId) throw unavailable();
1328
+ }
1329
+ const live = await getConnectionMetadata(
1330
+ input.db,
1331
+ connection.workspaceId,
1332
+ connection.id,
1333
+ input.subjectId
1334
+ );
1335
+ if (!live || !isExternalVaultConnection(live) || live.status !== "active" || live.accountId !== connection.accountId || live.subjectId !== connection.subjectId || live.providerDomain !== connection.providerDomain)
1336
+ throw unavailable();
1337
+ if (live.version !== connection.version)
1338
+ throw new VaultProviderError(
1339
+ "provider_conflict",
1340
+ "This vault connection changed. Reload it before starting another operation."
1341
+ );
1342
+ }
1343
+ });
1344
+ }
1345
+ function rotationConnectionIdentity(connection) {
1346
+ const {
1347
+ version: _version,
1348
+ connectionAuthorityGeneration: _authorityGeneration,
1349
+ updatedAt: _updatedAt,
1350
+ updatedBySubjectId: _updatedBy,
1351
+ lastUsedAt: _lastUsedAt,
1352
+ lastRefreshAt: _lastRefreshAt,
1353
+ ...identity
1354
+ } = connection;
1355
+ return identity;
1356
+ }
1357
+
1358
+ // src/domain/vaults/provider-registry.ts
1110
1359
  function vaultErrorCodeForProvider(code, operation) {
1111
1360
  if (code === "provider_not_found") {
1112
1361
  return operation === "read" ? "vault_field_not_found" : "vault_not_found";
@@ -1143,19 +1392,19 @@ function onePasswordCredentialBundle(token) {
1143
1392
  return { token };
1144
1393
  }
1145
1394
  async function loadOnePasswordConnectionToken(db, settings, input) {
1146
- const unavailable = () => new VaultProviderError(
1395
+ const unavailable2 = () => new VaultProviderError2(
1147
1396
  "provider_auth_invalid",
1148
1397
  "The 1Password connection for this vault was removed or is not available to you; reconnect 1Password"
1149
1398
  );
1150
1399
  let homeWorkspaceId = input.workspaceId;
1151
- let connection = await getConnectionMetadata(
1400
+ let connection = await getConnectionMetadata2(
1152
1401
  db,
1153
1402
  input.workspaceId,
1154
1403
  input.connectionId,
1155
1404
  input.subjectId
1156
1405
  );
1157
1406
  if (!connection) {
1158
- const home = await getProviderConnectionHomeWorkspace(
1407
+ const home = await getProviderConnectionHomeWorkspace2(
1159
1408
  db,
1160
1409
  input.workspaceId,
1161
1410
  input.connectionId,
@@ -1163,18 +1412,18 @@ async function loadOnePasswordConnectionToken(db, settings, input) {
1163
1412
  );
1164
1413
  if (home && home !== input.workspaceId) {
1165
1414
  homeWorkspaceId = home;
1166
- connection = await getConnectionMetadata(db, home, input.connectionId, input.subjectId);
1415
+ connection = await getConnectionMetadata2(db, home, input.connectionId, input.subjectId);
1167
1416
  }
1168
1417
  }
1169
- if (!connection || !isOnePasswordConnection(connection)) throw unavailable();
1418
+ if (!connection || !isOnePasswordConnection(connection)) throw unavailable2();
1170
1419
  if (connection.status !== "active") {
1171
- throw new VaultProviderError(
1420
+ throw new VaultProviderError2(
1172
1421
  "provider_auth_expired",
1173
1422
  "The 1Password connection needs a new service account token"
1174
1423
  );
1175
1424
  }
1176
1425
  const personal = connection.subjectId !== null;
1177
- const credential = await loadConnectionCredentialForBroker(db, settings, {
1426
+ const credential = await loadConnectionCredentialForBroker2(db, settings, {
1178
1427
  workspaceId: homeWorkspaceId,
1179
1428
  connectionId: connection.id,
1180
1429
  providerDomain: ONEPASSWORD_PROVIDER_DOMAIN,
@@ -1182,7 +1431,7 @@ async function loadOnePasswordConnectionToken(db, settings, input) {
1182
1431
  ...personal ? { allowSubjectOwned: true, subjectId: connection.subjectId } : {}
1183
1432
  });
1184
1433
  const token = credential?.credential?.token;
1185
- if (!credential || typeof token !== "string" || !token) throw unavailable();
1434
+ if (!credential || typeof token !== "string" || !token) throw unavailable2();
1186
1435
  return { token, metadata: onePasswordConnectionMetadata(connection.metadata) };
1187
1436
  }
1188
1437
  function createWorkspaceVaultProviderRegistry(input) {
@@ -1192,7 +1441,7 @@ function createWorkspaceVaultProviderRegistry(input) {
1192
1441
  ...factory ? {
1193
1442
  async onepassword(connectionId) {
1194
1443
  if (!connectionId) {
1195
- throw new VaultProviderError(
1444
+ throw new VaultProviderError2(
1196
1445
  "provider_unavailable",
1197
1446
  "This 1Password vault has no connection"
1198
1447
  );
@@ -1209,7 +1458,29 @@ function createWorkspaceVaultProviderRegistry(input) {
1209
1458
  });
1210
1459
  }
1211
1460
  } : {},
1212
- ...host ? { host: async () => host } : {}
1461
+ ...host ? { host: async () => host } : {},
1462
+ ...Object.fromEntries(
1463
+ Object.entries(input.externalVaultProviders ?? {}).map(([provider, providerFactory]) => [
1464
+ provider,
1465
+ async (connectionId) => {
1466
+ if (!connectionId || !providerFactory)
1467
+ throw new VaultProviderError2(
1468
+ "provider_unavailable",
1469
+ "This vault has no available connection."
1470
+ );
1471
+ return externalVaultProviderForConnection({
1472
+ db: input.db,
1473
+ settings: input.settings,
1474
+ workspaceId: input.workspaceId,
1475
+ subjectId: input.subjectId,
1476
+ connectionId,
1477
+ provider,
1478
+ factory: providerFactory,
1479
+ ...input.executionContext ? { executionContext: input.executionContext } : {}
1480
+ });
1481
+ }
1482
+ ])
1483
+ )
1213
1484
  });
1214
1485
  }
1215
1486
 
@@ -1440,7 +1711,7 @@ async function prepareCapabilityEnable(input) {
1440
1711
  item,
1441
1712
  installationConfig
1442
1713
  );
1443
- if (!current || !isDeepStrictEqual(current, unchanged)) {
1714
+ if (!current || !isDeepStrictEqual2(current, unchanged)) {
1444
1715
  throw new HTTPException5(409, {
1445
1716
  message: "Capability installation changed; reload before reconciling"
1446
1717
  });
@@ -1494,7 +1765,7 @@ async function validateVerifiedMcpConnection(input, item) {
1494
1765
  const ref = input.payload.connectionRef;
1495
1766
  if (!proof || !ref || item.endpointUrl !== proof.endpointUrl || ref.resource !== proof.endpointUrl)
1496
1767
  throw new HTTPException5(409, { message: "MCP verification no longer matches the connection" });
1497
- const connection = await getConnectionMetadata2(
1768
+ const connection = await getConnectionMetadata3(
1498
1769
  input.db,
1499
1770
  input.workspaceId,
1500
1771
  proof.id,
@@ -1521,7 +1792,7 @@ async function unchangedMcpInstallation(input, item, requestedConfig) {
1521
1792
  const config = { ...requestedConfig, ...connectionRef ? { connectionRef } : {} };
1522
1793
  const { mcpConnectivity: _storedConnectivity, ...storedMetadata } = existing.metadata;
1523
1794
  const { mcpConnectivity: _requestedConnectivity, ...requestedMetadata } = input.payload.metadata;
1524
- if (!isDeepStrictEqual(config, storedConfig) || !isDeepStrictEqual(requestedMetadata, storedMetadata))
1795
+ if (!isDeepStrictEqual2(config, storedConfig) || !isDeepStrictEqual2(requestedMetadata, storedMetadata))
1525
1796
  return null;
1526
1797
  const provided = normalizedMcpCredentialHeaders(input.payload.headers);
1527
1798
  if (provided) {
@@ -1529,7 +1800,7 @@ async function unchangedMcpInstallation(input, item, requestedConfig) {
1529
1800
  { ...input, payload: { ...input.payload, headers: {} } },
1530
1801
  item
1531
1802
  );
1532
- if (!isDeepStrictEqual(provided, stored2)) return null;
1803
+ if (!isDeepStrictEqual2(provided, stored2)) return null;
1533
1804
  }
1534
1805
  return existing;
1535
1806
  }
@@ -1630,7 +1901,7 @@ async function validateMcpCapabilityConnectionRef(input, item, ref) {
1630
1901
  assertNativeMcpConnectionRef(normalized);
1631
1902
  return normalized;
1632
1903
  }
1633
- let connection = normalized.connectionId ? await getConnectionMetadata2(
1904
+ let connection = normalized.connectionId ? await getConnectionMetadata3(
1634
1905
  input.db,
1635
1906
  input.workspaceId,
1636
1907
  normalized.connectionId,
@@ -1712,7 +1983,7 @@ function requiredCapabilityHeaders(metadata) {
1712
1983
  return value.filter((name) => typeof name === "string" && name.trim().length > 0).map((name) => name.trim());
1713
1984
  }
1714
1985
  function requireCapabilityHeaderEncryption(settings) {
1715
- const key = environmentsEncryptionKeyBytes(settings);
1986
+ const key = environmentsEncryptionKeyBytes2(settings);
1716
1987
  if (!key) {
1717
1988
  throw new HTTPException5(503, {
1718
1989
  message: "MCP credential headers require OPENGENI_ENVIRONMENTS_ENCRYPTION_KEY"
@@ -2001,7 +2272,7 @@ function settingsWithMcpCapabilityServers(settings, enabled) {
2001
2272
  if (enabled.length === 0) {
2002
2273
  return settings;
2003
2274
  }
2004
- const encryptionKey = environmentsEncryptionKeyBytes(settings);
2275
+ const encryptionKey = environmentsEncryptionKeyBytes2(settings);
2005
2276
  const existingIds = new Set(settings.mcpServers.map((server) => server.id));
2006
2277
  const dynamicServers = enabled.filter((server) => !existingIds.has(server.id)).flatMap((server) => {
2007
2278
  const headers = decryptedCapabilityHeaders(server, encryptionKey);
@@ -3040,7 +3311,7 @@ import {
3040
3311
  getSessionTurnPersonalConnectionDelegations,
3041
3312
  getSessionTurnMcpAccountBindings,
3042
3313
  getSessionTurnConnectionAccountIntent,
3043
- getConnectionMetadata as getConnectionMetadata3,
3314
+ getConnectionMetadata as getConnectionMetadata4,
3044
3315
  getSocialConnection,
3045
3316
  listConnectionsMetadata as listConnectionsMetadata2,
3046
3317
  listSocialConnections as listSocialConnections2,
@@ -3478,7 +3749,7 @@ async function listOwnConnectionMetadata(db, input) {
3478
3749
  );
3479
3750
  const connections = [];
3480
3751
  for (const account of accounts) {
3481
- const connection = await getConnectionMetadata3(
3752
+ const connection = await getConnectionMetadata4(
3482
3753
  db,
3483
3754
  account.originWorkspaceId,
3484
3755
  account.connectionId,
@@ -3941,7 +4212,7 @@ async function visibleMcpAccountConnections(db, input) {
3941
4212
  async function visibleMcpAccountConnectionsByIds(db, input) {
3942
4213
  const workspace = [];
3943
4214
  for (const connectionId of new Set(input.workspaceConnectionIds)) {
3944
- const connection = await getConnectionMetadata3(db, input.workspaceId, connectionId, null);
4215
+ const connection = await getConnectionMetadata4(db, input.workspaceId, connectionId, null);
3945
4216
  if (connection && connection.subjectId === null) workspace.push(connection);
3946
4217
  }
3947
4218
  const personalIds = new Set(input.personalConnectionIds);
@@ -4163,7 +4434,7 @@ import {
4163
4434
  resolveNamedManagedPersonalWorkspaceGrant
4164
4435
  } from "@opengeni/db";
4165
4436
  import { HTTPException as HTTPException8 } from "hono/http-exception";
4166
- import { z } from "zod";
4437
+ import { z as z2 } from "zod";
4167
4438
 
4168
4439
  // src/access/service-initiator.ts
4169
4440
  import { ServiceTurnInitiatorContext } from "@opengeni/contracts";
@@ -4273,19 +4544,19 @@ function requireExplicitPermissionDelegation(grant, requested) {
4273
4544
  }
4274
4545
  }
4275
4546
  var canonicalLocalHumanContexts = /* @__PURE__ */ new WeakSet();
4276
- var delegatedHumanAuthorizationSchema = z.object({
4277
- organizationId: z.string().uuid(),
4278
- subjectId: z.string().max(1024).regex(/^user:[^\s\u0000-\u001f\u007f]+$/),
4279
- permissions: z.array(Permission),
4547
+ var delegatedHumanAuthorizationSchema = z2.object({
4548
+ organizationId: z2.string().uuid(),
4549
+ subjectId: z2.string().max(1024).regex(/^user:[^\s\u0000-\u001f\u007f]+$/),
4550
+ permissions: z2.array(Permission),
4280
4551
  workspaceScope: OrganizationWorkspaceScope
4281
4552
  });
4282
4553
  var delegatedHumanRequests = /* @__PURE__ */ new WeakMap();
4283
4554
  var delegatedHumanContexts = /* @__PURE__ */ new WeakMap();
4284
- var delegatedHumanProfileSchema = z.object({
4285
- id: z.string(),
4286
- name: z.string().nullable(),
4287
- email: z.string(),
4288
- emailVerified: z.boolean().optional()
4555
+ var delegatedHumanProfileSchema = z2.object({
4556
+ id: z2.string(),
4557
+ name: z2.string().nullable(),
4558
+ email: z2.string(),
4559
+ emailVerified: z2.boolean().optional()
4289
4560
  });
4290
4561
  var delegatedHumanProfiles = /* @__PURE__ */ new WeakMap();
4291
4562
  var verifiedDelegatedHumanAuthorizations = /* @__PURE__ */ new WeakMap();
@@ -6494,7 +6765,7 @@ function sessionUsageLimitRefusalFromError(error) {
6494
6765
  }
6495
6766
 
6496
6767
  // src/sandbox/fleet.ts
6497
- import { environmentsEncryptionKeyBytes as environmentsEncryptionKeyBytes2 } from "@opengeni/config";
6768
+ import { environmentsEncryptionKeyBytes as environmentsEncryptionKeyBytes3 } from "@opengeni/config";
6498
6769
  import {
6499
6770
  agentCapabilityEnabled
6500
6771
  } from "@opengeni/contracts";
@@ -7026,6 +7297,7 @@ import {
7026
7297
  isValidTotpInput,
7027
7298
  parseTotpInput,
7028
7299
  referenceKey,
7300
+ secretNameKey,
7029
7301
  SecretReferenceError,
7030
7302
  VAULT_LIMITS as VAULT_LIMITS2
7031
7303
  } from "@opengeni/contracts";
@@ -7049,14 +7321,42 @@ import {
7049
7321
  } from "@opengeni/contracts";
7050
7322
  import {
7051
7323
  getProviderVaultSyncTarget,
7052
- setVaultSyncStatus,
7053
- upsertProviderVaultItems
7324
+ isVaultError,
7325
+ setVaultSyncStatus
7054
7326
  } from "@opengeni/db";
7055
7327
  import {
7056
7328
  isVaultProviderError,
7057
- VaultProviderError as VaultProviderError2
7329
+ providerOperations as providerOperations2,
7330
+ readProviderItemInventory,
7331
+ readProviderVaultInventory,
7332
+ VaultProviderError as VaultProviderError3
7058
7333
  } from "@opengeni/vaults";
7059
7334
  var PROVIDER_RATE_LIMIT_DEFAULT_BACKOFF_MS = 15 * 6e4;
7335
+ function incompleteInventory() {
7336
+ return new VaultProviderError3(
7337
+ "provider_inventory_incomplete",
7338
+ `This vault has more than ${VAULT_LIMITS.itemsPerVault} items, more than Opengeni mirrors. The previous inventory was kept.`
7339
+ );
7340
+ }
7341
+ function oversizedItem() {
7342
+ return new VaultProviderError3(
7343
+ "provider_inventory_incomplete",
7344
+ `An item in this vault has more than ${VAULT_LIMITS.fieldsPerItem} fields or ${VAULT_LIMITS.websitesPerItem} websites (or a website over ${VAULT_LIMITS.websiteMaxChars} characters), more than Opengeni mirrors. The previous inventory was kept.`
7345
+ );
7346
+ }
7347
+ function unprovenInventory(listing) {
7348
+ return new VaultProviderError3(
7349
+ "provider_inventory_incomplete",
7350
+ listing === "items" ? "The provider listed this vault in several pages without a snapshot, so Opengeni cannot tell which items were removed. The previous inventory was kept." : "The provider listed its vaults in several pages without a snapshot and this vault was not among them, so Opengeni cannot confirm it was removed."
7351
+ );
7352
+ }
7353
+ function assertMirrorable(item) {
7354
+ if (!Array.isArray(item.fields) || !Array.isArray(item.websites) || item.fields.length > VAULT_LIMITS.fieldsPerItem || item.websites.length > VAULT_LIMITS.websitesPerItem || item.websites.some(
7355
+ (website) => typeof website !== "string" || website.length > VAULT_LIMITS.websiteMaxChars
7356
+ )) {
7357
+ throw oversizedItem();
7358
+ }
7359
+ }
7060
7360
  var FORBIDDEN_NAME_CHARACTERS = /[/{}#\\\u0000-\u001f\u007f-\u009f]/g;
7061
7361
  function nameKey(name) {
7062
7362
  return name.normalize("NFC").trim().toLowerCase();
@@ -7103,15 +7403,16 @@ function providerPrimaryFieldId(item) {
7103
7403
  return candidates.find((field) => field.kind === "concealed")?.externalFieldId ?? null;
7104
7404
  }
7105
7405
  function planProviderMirror(items, contentVersion) {
7406
+ if (items.length > VAULT_LIMITS.itemsPerVault) throw incompleteInventory();
7407
+ for (const item of items) assertMirrorable(item);
7106
7408
  const ordered = [...items].sort(
7107
7409
  (a, b) => nameKey(a.name).localeCompare(nameKey(b.name)) || a.externalItemId.localeCompare(b.externalItemId)
7108
7410
  );
7109
- const kept = ordered.slice(0, VAULT_LIMITS.itemsPerVault);
7110
7411
  const takenItems = /* @__PURE__ */ new Set();
7111
- const mirrored = kept.map((item) => {
7412
+ const mirrored = ordered.map((item) => {
7112
7413
  const name = uniqueProviderName(providerSecretName(item.name), takenItems);
7113
7414
  const takenFields = /* @__PURE__ */ new Set();
7114
- const fields = item.fields.slice(0, VAULT_LIMITS.fieldsPerItem).map((field, position) => {
7415
+ const fields = item.fields.map((field, position) => {
7115
7416
  let fieldName = providerSecretName(
7116
7417
  field.name,
7117
7418
  field.kind === "totp" ? "one-time password" : "field"
@@ -7129,7 +7430,7 @@ function planProviderMirror(items, contentVersion) {
7129
7430
  name,
7130
7431
  providerName: item.name,
7131
7432
  type: item.type,
7132
- websites: item.websites.filter((website) => website.length <= VAULT_LIMITS.websiteMaxChars).slice(0, VAULT_LIMITS.websitesPerItem),
7433
+ websites: [...item.websites],
7133
7434
  fields,
7134
7435
  primaryExternalFieldId: providerPrimaryFieldId({ type: item.type, fields: item.fields }),
7135
7436
  hasTotp: item.fields.some((field) => field.kind === "totp"),
@@ -7137,7 +7438,32 @@ function planProviderMirror(items, contentVersion) {
7137
7438
  updatedAt: item.updatedAt
7138
7439
  };
7139
7440
  });
7140
- return { contentVersion, items: mirrored, truncated: ordered.length - kept.length };
7441
+ return { contentVersion, items: mirrored, truncated: 0 };
7442
+ }
7443
+ function validTime(value) {
7444
+ if (typeof value !== "string" || value.length > 64) return null;
7445
+ const time = Date.parse(value);
7446
+ return Number.isFinite(time) ? time : null;
7447
+ }
7448
+ function aggregateProviderFreshness(observations, checkedAt) {
7449
+ if (observations.length === 0) return null;
7450
+ let cache = false;
7451
+ let unknown = false;
7452
+ let unknownTime = false;
7453
+ let oldest = null;
7454
+ for (const observation of observations) {
7455
+ const source = observation?.source;
7456
+ if (source === "cache") cache = true;
7457
+ else if (source !== "live") unknown = true;
7458
+ const time = validTime(observation?.sourceSyncedAt);
7459
+ if (time !== null) oldest = oldest === null ? time : Math.min(oldest, time);
7460
+ else if (source !== "live") unknownTime = true;
7461
+ }
7462
+ const syncedAt = unknownTime || oldest === null ? null : new Date(Math.min(oldest, checkedAt.getTime()));
7463
+ return {
7464
+ source: cache ? "cache" : unknown ? "unknown" : "live",
7465
+ sourceSyncedAt: syncedAt ? syncedAt.toISOString() : null
7466
+ };
7141
7467
  }
7142
7468
  function providerSyncFailure(error, now) {
7143
7469
  const code = isVaultProviderError(error) ? error.code : "provider_unavailable";
@@ -7160,24 +7486,59 @@ function providerSyncFailure(error, now) {
7160
7486
  lastSyncedAt: null
7161
7487
  };
7162
7488
  }
7489
+ async function providerStep(call) {
7490
+ try {
7491
+ return await call();
7492
+ } catch (error) {
7493
+ if (isVaultProviderError(error)) throw error;
7494
+ throw new VaultProviderError3(
7495
+ "provider_unavailable",
7496
+ "the vault provider could not be reached or gave an invalid answer"
7497
+ );
7498
+ }
7499
+ }
7163
7500
  async function syncProviderVault(input) {
7164
7501
  const now = input.now ?? (() => /* @__PURE__ */ new Date());
7165
7502
  const { target } = input;
7166
- if (target.rateLimitedUntil && target.rateLimitedUntil.getTime() > now().getTime() && !input.force) {
7503
+ if (target.rateLimitedUntil && target.rateLimitedUntil.getTime() > now().getTime()) {
7167
7504
  return { status: "skipped_rate_limited", rateLimitedUntil: target.rateLimitedUntil };
7168
7505
  }
7506
+ const observations = [];
7507
+ const outer = input.ctx.onFreshness;
7508
+ const ctx = {
7509
+ ...input.ctx,
7510
+ ...target.cacheEpoch !== void 0 && input.ctx.cacheEpoch === void 0 ? { cacheEpoch: target.cacheEpoch } : {},
7511
+ ...input.force ? { forceRefresh: true } : {},
7512
+ onFreshness(freshness) {
7513
+ observations.push(freshness);
7514
+ outer?.(freshness);
7515
+ }
7516
+ };
7517
+ const succeeded = () => {
7518
+ const checkedAt = now();
7519
+ return {
7520
+ syncStatus: "ok",
7521
+ syncErrorCode: null,
7522
+ syncErrorMessage: null,
7523
+ rateLimitedUntil: null,
7524
+ lastSyncedAt: checkedAt,
7525
+ freshness: aggregateProviderFreshness(observations, checkedAt)
7526
+ };
7527
+ };
7169
7528
  try {
7170
- const vaults = await input.provider.listVaults(input.ctx);
7171
- const vault = vaults.find((candidate) => candidate.externalVaultId === target.externalVaultId);
7529
+ const vaults = await providerStep(() => readProviderVaultInventory(input.provider, ctx));
7530
+ const vault = vaults.items.find(
7531
+ (candidate) => candidate.externalVaultId === target.externalVaultId
7532
+ );
7172
7533
  if (!vault) {
7173
7534
  const failure = providerSyncFailure(
7174
- new VaultProviderError2(
7535
+ vaults.consistent ? new VaultProviderError3(
7175
7536
  "provider_not_found",
7176
7537
  "the linked provider vault no longer exists or is no longer shared with this connection"
7177
- ),
7538
+ ) : unprovenInventory("vaults"),
7178
7539
  now()
7179
7540
  );
7180
- await input.store.setSyncStatus(failure);
7541
+ if (!await input.store.settleRefresh(failure)) return { status: "superseded" };
7181
7542
  return {
7182
7543
  status: "failed",
7183
7544
  syncStatus: failure.syncStatus,
@@ -7185,26 +7546,19 @@ async function syncProviderVault(input) {
7185
7546
  rateLimitedUntil: null
7186
7547
  };
7187
7548
  }
7549
+ const operations = providerOperations2(input.provider.capabilities(), vault);
7188
7550
  if (!input.force && vault.contentVersion !== null && vault.contentVersion === target.mirroredContentVersion) {
7189
- await input.store.setSyncStatus({
7190
- syncStatus: "ok",
7191
- syncErrorCode: null,
7192
- syncErrorMessage: null,
7193
- rateLimitedUntil: null,
7194
- lastSyncedAt: now()
7195
- });
7551
+ if (!await input.store.settleRefresh({ ...succeeded(), operations }))
7552
+ return { status: "superseded" };
7196
7553
  return { status: "unchanged", contentVersion: vault.contentVersion };
7197
7554
  }
7198
- const items = await input.provider.listItems(input.ctx, target.externalVaultId);
7199
- const plan = planProviderMirror(items, vault.contentVersion);
7200
- await input.store.replaceMirror(plan);
7201
- await input.store.setSyncStatus({
7202
- syncStatus: "ok",
7203
- syncErrorCode: null,
7204
- syncErrorMessage: null,
7205
- rateLimitedUntil: null,
7206
- lastSyncedAt: now()
7207
- });
7555
+ const inventory = await providerStep(
7556
+ () => readProviderItemInventory(input.provider, ctx, target.externalVaultId)
7557
+ );
7558
+ if (!inventory.consistent) throw unprovenInventory("items");
7559
+ const plan = planProviderMirror(inventory.items, vault.contentVersion);
7560
+ if (!await input.store.settleRefresh({ ...succeeded(), operations }, plan))
7561
+ return { status: "superseded" };
7208
7562
  return {
7209
7563
  status: "synced",
7210
7564
  contentVersion: plan.contentVersion,
@@ -7214,7 +7568,7 @@ async function syncProviderVault(input) {
7214
7568
  } catch (error) {
7215
7569
  if (!isVaultProviderError(error)) throw error;
7216
7570
  const failure = providerSyncFailure(error, now());
7217
- await input.store.setSyncStatus(failure);
7571
+ if (!await input.store.settleRefresh(failure)) return { status: "superseded" };
7218
7572
  return {
7219
7573
  status: "failed",
7220
7574
  syncStatus: failure.syncStatus,
@@ -7223,11 +7577,10 @@ async function syncProviderVault(input) {
7223
7577
  };
7224
7578
  }
7225
7579
  }
7226
- function databaseProviderMirrorStore(db, context, vaultId) {
7580
+ function databaseProviderMirrorStore(db, context, vaultId, expectedCacheEpoch) {
7227
7581
  return {
7228
- async replaceMirror(plan) {
7229
- await upsertProviderVaultItems(db, context, {
7230
- vaultId,
7582
+ async settleRefresh(update, plan) {
7583
+ const mirror = plan ? {
7231
7584
  contentVersion: plan.contentVersion,
7232
7585
  items: plan.items.map((item) => ({
7233
7586
  providerItemRef: item.externalItemId,
@@ -7245,22 +7598,41 @@ function databaseProviderMirrorStore(db, context, vaultId) {
7245
7598
  kind: field.kind
7246
7599
  }))
7247
7600
  }))
7248
- });
7249
- },
7250
- async setSyncStatus(update) {
7251
- await setVaultSyncStatus(db, context, {
7252
- vaultId,
7253
- status: update.syncStatus,
7254
- errorCode: update.syncErrorCode,
7255
- errorMessage: update.syncErrorMessage,
7256
- rateLimitedUntil: update.rateLimitedUntil?.toISOString() ?? null
7257
- });
7601
+ } : void 0;
7602
+ try {
7603
+ await setVaultSyncStatus(db, context, {
7604
+ vaultId,
7605
+ expectedCacheEpoch,
7606
+ ...mirror ? { mirror } : {},
7607
+ status: update.syncStatus,
7608
+ errorCode: update.syncErrorCode,
7609
+ errorMessage: update.syncErrorMessage,
7610
+ rateLimitedUntil: update.rateLimitedUntil?.toISOString() ?? null,
7611
+ ...update.operations !== void 0 ? { operations: update.operations } : {},
7612
+ ...update.freshness !== void 0 ? { freshness: update.freshness } : {}
7613
+ });
7614
+ return true;
7615
+ } catch (error) {
7616
+ if (isVaultError(error) && error.code === "provider_conflict" && error.details?.superseded === true)
7617
+ return false;
7618
+ throw error;
7619
+ }
7258
7620
  }
7259
7621
  };
7260
7622
  }
7261
7623
  async function syncLinkedProviderVault(input) {
7262
7624
  const target = await getProviderVaultSyncTarget(input.db, input.context, input.vaultId);
7263
- const store = databaseProviderMirrorStore(input.db, input.context, input.vaultId);
7625
+ const store = databaseProviderMirrorStore(
7626
+ input.db,
7627
+ input.context,
7628
+ input.vaultId,
7629
+ target.providerCacheEpoch ?? "0"
7630
+ );
7631
+ const now = input.now ?? (() => /* @__PURE__ */ new Date());
7632
+ const rateLimitedUntil = target.rateLimitedUntil ? new Date(target.rateLimitedUntil) : null;
7633
+ if (rateLimitedUntil && rateLimitedUntil.getTime() > now().getTime()) {
7634
+ return { status: "skipped_rate_limited", rateLimitedUntil };
7635
+ }
7264
7636
  let provider;
7265
7637
  try {
7266
7638
  provider = await input.providers.forVault({
@@ -7269,8 +7641,8 @@ async function syncLinkedProviderVault(input) {
7269
7641
  });
7270
7642
  } catch (error) {
7271
7643
  if (!isVaultProviderError(error)) throw error;
7272
- const failure = providerSyncFailure(error, (input.now ?? (() => /* @__PURE__ */ new Date()))());
7273
- await store.setSyncStatus(failure);
7644
+ const failure = providerSyncFailure(error, now());
7645
+ if (!await store.settleRefresh(failure)) return { status: "superseded" };
7274
7646
  return {
7275
7647
  status: "failed",
7276
7648
  syncStatus: failure.syncStatus,
@@ -7283,12 +7655,16 @@ async function syncLinkedProviderVault(input) {
7283
7655
  ctx: {
7284
7656
  connectionId: target.providerConnectionId,
7285
7657
  budget: input.budget ?? { take() {
7286
- } }
7658
+ } },
7659
+ cacheEpoch: target.providerCacheEpoch ?? null,
7660
+ ...input.signal ? { signal: input.signal } : {},
7661
+ ...input.onFreshness ? { onFreshness: input.onFreshness } : {}
7287
7662
  },
7288
7663
  target: {
7289
7664
  externalVaultId: target.providerVaultRef,
7290
7665
  mirroredContentVersion: target.providerContentVersion,
7291
- rateLimitedUntil: target.rateLimitedUntil ? new Date(target.rateLimitedUntil) : null
7666
+ rateLimitedUntil,
7667
+ cacheEpoch: target.providerCacheEpoch ?? null
7292
7668
  },
7293
7669
  store,
7294
7670
  ...input.now ? { now: input.now } : {},
@@ -7299,6 +7675,7 @@ var LINKED_VAULT_SEARCH_MAX_AGE_MS = 10 * 6e4;
7299
7675
  var LINKED_VAULT_MISS_MAX_AGE_MS = 2 * 6e4;
7300
7676
  var LINKED_VAULT_REFRESH_WAIT_MS = 8e3;
7301
7677
  var LINKED_VAULT_OPEN_WAIT_MS = 3e3;
7678
+ var LINKED_VAULT_REFRESH_TIMEOUT_MS = 6e4;
7302
7679
  function linkedVaultNeedsRefresh(vault, maxAgeMs, now) {
7303
7680
  if (vault.source === "opengeni") return false;
7304
7681
  if (vault.rateLimitedUntil && new Date(vault.rateLimitedUntil).getTime() > now.getTime()) {
@@ -7308,38 +7685,124 @@ function linkedVaultNeedsRefresh(vault, maxAgeMs, now) {
7308
7685
  if (!last) return true;
7309
7686
  return now.getTime() - new Date(last).getTime() >= maxAgeMs;
7310
7687
  }
7688
+ function isoOrNull(value) {
7689
+ const time = validTime(value);
7690
+ return time === null ? null : new Date(time).toISOString();
7691
+ }
7692
+ function providerVaultFreshness(vault, now, options = {}) {
7693
+ if (vault.source === "opengeni") return void 0;
7694
+ const stored2 = vault.providerFreshness ?? null;
7695
+ const failed = vault.syncStatus !== "ok" && vault.syncStatus !== "syncing";
7696
+ const base = {
7697
+ lastCheckedAt: isoOrNull(vault.lastSyncedAt),
7698
+ sourceSyncedAt: isoOrNull(stored2?.sourceSyncedAt),
7699
+ source: stored2?.source === "live" || stored2?.source === "cache" ? stored2.source : "unknown",
7700
+ errorCode: failed && vault.syncErrorCode ? vault.syncErrorCode.slice(0, 100) : null,
7701
+ retryAt: null
7702
+ };
7703
+ if (options.refreshing || vault.syncStatus === "syncing") return { state: "refreshing", ...base };
7704
+ if (vault.syncStatus === "rate_limited") {
7705
+ const retryAt = isoOrNull(vault.rateLimitedUntil);
7706
+ if (retryAt !== null && Date.parse(retryAt) > now.getTime()) {
7707
+ return { state: "unavailable", ...base, retryAt };
7708
+ }
7709
+ return { state: "stale", ...base };
7710
+ }
7711
+ if (failed) return { state: "unavailable", ...base };
7712
+ const checked = base.lastCheckedAt === null ? null : Date.parse(base.lastCheckedAt);
7713
+ const maxAgeMs = options.maxAgeMs ?? LINKED_VAULT_SEARCH_MAX_AGE_MS;
7714
+ if (checked === null || now.getTime() - checked >= maxAgeMs) return { state: "stale", ...base };
7715
+ return { state: "current", ...base };
7716
+ }
7717
+ function vaultRefreshScopeKey(context) {
7718
+ if (context.kind === "subject") {
7719
+ return `${context.accountId}|${context.workspaceId}|subject|${context.subjectId}`;
7720
+ }
7721
+ const principal = context.principal;
7722
+ switch (principal.kind) {
7723
+ case "attempt":
7724
+ return `${principal.accountId}|${principal.workspaceId}|attempt|${principal.attemptId}|${principal.executionGeneration}`;
7725
+ case "site_viewer":
7726
+ return `${principal.accountId}|${principal.workspaceId}|site|${principal.siteId}|${principal.siteVersion}|${principal.subjectId}`;
7727
+ case "human":
7728
+ return `${principal.accountId}|${principal.workspaceId}|human|${principal.subjectId}|${principal.viaApiKey ? "key" : principal.verifiedHuman ? "verified" : "unverified"}`;
7729
+ }
7730
+ }
7311
7731
  var linkedVaultRefreshes = /* @__PURE__ */ new Map();
7312
- async function refreshStaleLinkedVaults(input) {
7732
+ async function refreshLinkedVaults(input) {
7313
7733
  const now = (input.now ?? (() => /* @__PURE__ */ new Date()))();
7314
- const stale = input.vaults.filter((vault) => linkedVaultNeedsRefresh(vault, input.maxAgeMs, now));
7315
- if (stale.length === 0) return [];
7734
+ const maxAgeMs = input.maxAgeMs ?? LINKED_VAULT_SEARCH_MAX_AGE_MS;
7735
+ const scope = vaultRefreshScopeKey(input.context);
7736
+ const outcomes = /* @__PURE__ */ new Map();
7316
7737
  const changed = [];
7317
- const runs = stale.map((vault) => {
7318
- let run = linkedVaultRefreshes.get(vault.id);
7738
+ const runs = [];
7739
+ for (const vault of input.vaults) {
7740
+ if (outcomes.has(vault.id)) continue;
7741
+ const limited = vault.rateLimitedUntil !== null && new Date(vault.rateLimitedUntil).getTime() > now.getTime();
7742
+ const due = vault.source !== "opengeni" && !limited && (input.force || linkedVaultNeedsRefresh(vault, maxAgeMs, now));
7743
+ const outcome = {
7744
+ vaultId: vault.id,
7745
+ state: due ? "pending" : "not_due",
7746
+ result: null
7747
+ };
7748
+ outcomes.set(vault.id, outcome);
7749
+ if (!due) continue;
7750
+ const key = `${scope}|${vault.id}|${input.force ? "force" : "stale"}`;
7751
+ let run = linkedVaultRefreshes.get(key);
7319
7752
  if (!run) {
7320
- run = syncLinkedProviderVault({
7321
- db: input.db,
7322
- context: input.context,
7323
- vaultId: vault.id,
7324
- providers: input.providers,
7325
- ...input.now ? { now: input.now } : {}
7326
- }).catch(() => null).finally(() => linkedVaultRefreshes.delete(vault.id));
7327
- linkedVaultRefreshes.set(vault.id, run);
7753
+ let timer;
7754
+ run = Promise.race([
7755
+ syncLinkedProviderVault({
7756
+ db: input.db,
7757
+ context: input.context,
7758
+ vaultId: vault.id,
7759
+ providers: input.providers,
7760
+ signal: AbortSignal.timeout(LINKED_VAULT_REFRESH_TIMEOUT_MS),
7761
+ ...input.force ? { force: true } : {},
7762
+ ...input.now ? { now: input.now } : {}
7763
+ }),
7764
+ new Promise((resolve) => {
7765
+ timer = setTimeout(() => resolve(null), LINKED_VAULT_REFRESH_TIMEOUT_MS);
7766
+ timer.unref?.();
7767
+ })
7768
+ ]).catch(() => {
7769
+ outcome.state = "failed";
7770
+ return null;
7771
+ }).finally(() => {
7772
+ if (timer) clearTimeout(timer);
7773
+ linkedVaultRefreshes.delete(key);
7774
+ });
7775
+ linkedVaultRefreshes.set(key, run);
7328
7776
  }
7329
- return run.then((result) => {
7330
- if (result?.status === "synced") changed.push(vault.id);
7331
- });
7332
- });
7333
- let timer;
7334
- await Promise.race([
7335
- Promise.all(runs),
7336
- new Promise((resolve) => {
7337
- timer = setTimeout(resolve, input.waitMs ?? LINKED_VAULT_REFRESH_WAIT_MS);
7338
- timer.unref?.();
7339
- })
7340
- ]);
7341
- if (timer) clearTimeout(timer);
7342
- return [...changed];
7777
+ runs.push(
7778
+ run.then((result) => {
7779
+ if (outcome.state === "pending") outcome.state = result ? "done" : "failed";
7780
+ outcome.result = result;
7781
+ if (result?.status === "synced") changed.push(vault.id);
7782
+ })
7783
+ );
7784
+ }
7785
+ if (runs.length > 0) {
7786
+ let timer;
7787
+ await Promise.race([
7788
+ Promise.all(runs),
7789
+ new Promise((resolve) => {
7790
+ timer = setTimeout(resolve, input.waitMs ?? LINKED_VAULT_REFRESH_WAIT_MS);
7791
+ timer.unref?.();
7792
+ })
7793
+ ]);
7794
+ if (timer) clearTimeout(timer);
7795
+ }
7796
+ const list = [...outcomes.values()].map((outcome) => ({ ...outcome }));
7797
+ return {
7798
+ outcomes: list,
7799
+ changed: [...changed],
7800
+ pending: list.filter((outcome) => outcome.state === "pending").map((outcome) => outcome.vaultId)
7801
+ };
7802
+ }
7803
+ async function refreshStaleLinkedVaults(input) {
7804
+ const report = await refreshLinkedVaults(input);
7805
+ return report.changed;
7343
7806
  }
7344
7807
 
7345
7808
  // src/domain/vaults/secret-resolver.ts
@@ -7473,6 +7936,51 @@ function providerFailureCode(code) {
7473
7936
  if (code === "provider_permission_denied") return "denied";
7474
7937
  return "provider_unavailable";
7475
7938
  }
7939
+ function relevantProviderVaults(reference, candidates, reachable) {
7940
+ const connected = reachable.filter((vault) => vault.source !== "opengeni");
7941
+ if (connected.length === 0) return [];
7942
+ const where = possibleProviderVaults(reference, reachable, connected);
7943
+ const live = candidates.filter((candidate) => !candidate.archived);
7944
+ if (live.length === 0) return where;
7945
+ const deciding = Math.min(...live.map((candidate) => candidate.tier));
7946
+ const lacking = new Set(
7947
+ live.filter(
7948
+ (candidate) => candidate.tier <= deciding && candidate.source !== "opengeni" && (candidate.missing === "field" || candidate.missing === "totp_field")
7949
+ ).map((candidate) => candidate.vaultId)
7950
+ );
7951
+ const matched = new Set(live.map((candidate) => candidate.vaultId));
7952
+ const unmatched = new Set(
7953
+ where.filter((vault) => vault.tier <= deciding && !matched.has(vault.id)).map((vault) => vault.id)
7954
+ );
7955
+ return connected.filter((vault) => lacking.has(vault.id) || unmatched.has(vault.id));
7956
+ }
7957
+ function possibleProviderVaults(reference, reachable, connected) {
7958
+ if (reference.target.form !== "path") return [];
7959
+ const segments = reference.target.segments;
7960
+ if (segments.length === 1) return [...connected];
7961
+ const nameKey2 = secretNameKey(segments[0]);
7962
+ const named = reachable.filter((vault) => secretNameKey(vault.name) === nameKey2);
7963
+ if (segments.length === 2 && named.length === 0) return [...connected];
7964
+ return named.filter((vault) => vault.source !== "opengeni");
7965
+ }
7966
+ function uncheckedSourceFailure(reference, unchecked) {
7967
+ const authExpired = unchecked.filter(({ vault }) => vault.syncStatus === "auth_expired");
7968
+ const rateLimited = unchecked.filter(
7969
+ ({ freshness }) => freshness.state === "unavailable" && freshness.retryAt !== null
7970
+ );
7971
+ const [code, named, why] = authExpired.length > 0 ? ["provider_auth_expired", authExpired, "needs to be reconnected"] : rateLimited.length > 0 ? ["provider_rate_limited", rateLimited, "is rate limited by its provider"] : [
7972
+ "provider_unavailable",
7973
+ unchecked,
7974
+ unchecked.every(({ freshness }) => freshness.state === "refreshing") ? "is still refreshing" : "could not be checked"
7975
+ ];
7976
+ const names = named.slice(0, 3).map(({ vault }) => vault.name).join(", ");
7977
+ const retryAt = rateLimited.map(({ freshness }) => freshness.retryAt).sort().at(0);
7978
+ return {
7979
+ raw: reference.raw,
7980
+ code,
7981
+ message: `not found in the vaults that could be checked, but ${names} ${why}, so it may be there` + (code === "provider_rate_limited" && retryAt ? `; try again after ${retryAt}` : "; try again later")
7982
+ };
7983
+ }
7476
7984
  function decodeBase64(value) {
7477
7985
  return new Uint8Array(Buffer.from(value, "base64"));
7478
7986
  }
@@ -7535,35 +8043,74 @@ function createSecretResolver(deps, principal, opts) {
7535
8043
  const { meta: meta2, candidates } = await resolveSecretReferenceCandidates(deps.db, context, {
7536
8044
  refs: references.map((reference, index) => lookupFor(reference, index))
7537
8045
  });
7538
- const failures2 = [];
7539
- const chosen2 = [];
7540
- references.forEach((reference, index) => {
7541
- const outcome = chooseSecretCandidate(
7542
- reference,
7543
- candidates.filter((candidate) => candidate.refIndex === index),
7544
- meta2
7545
- );
7546
- if ("failure" in outcome) failures2.push(outcome.failure);
7547
- else chosen2.push({ reference, candidate: outcome.candidate });
8046
+ const outcomes2 = references.map((reference, index) => {
8047
+ const own = candidates.filter((candidate) => candidate.refIndex === index);
8048
+ return { reference, candidates: own, outcome: chooseSecretCandidate(reference, own, meta2) };
7548
8049
  });
7549
- return { meta: meta2, failures: failures2, chosen: chosen2 };
8050
+ return { meta: meta2, outcomes: outcomes2 };
7550
8051
  };
7551
- let { meta, failures, chosen } = await choose();
7552
- if (failures.some((failure) => failure.code === "not_found")) {
7553
- const registry = deps.providers ?? opts.providersForSubject?.(meta.subjectId);
7554
- if (registry) {
7555
- const reachable = await listVaultsForSubject(deps.db, context, {
7556
- workspaceId: principal.workspaceId
7557
- });
7558
- const changed = await refreshStaleLinkedVaults({
8052
+ let { meta, outcomes } = await choose();
8053
+ let registry;
8054
+ const providers = () => {
8055
+ if (registry === void 0) {
8056
+ registry = deps.providers ?? opts.providersForSubject?.(meta.subjectId) ?? null;
8057
+ }
8058
+ return registry;
8059
+ };
8060
+ const misses = () => outcomes.filter(
8061
+ (entry) => "failure" in entry.outcome && entry.outcome.failure.code === "not_found"
8062
+ );
8063
+ let reachable = null;
8064
+ let refreshing = /* @__PURE__ */ new Set();
8065
+ if (misses().length > 0) {
8066
+ reachable = await listVaultsForSubject(deps.db, context, {
8067
+ workspaceId: principal.workspaceId
8068
+ });
8069
+ const relevant = /* @__PURE__ */ new Map();
8070
+ for (const entry of misses()) {
8071
+ for (const vault of relevantProviderVaults(entry.reference, entry.candidates, reachable)) {
8072
+ relevant.set(vault.id, vault);
8073
+ }
8074
+ }
8075
+ const refreshProviders = relevant.size > 0 ? providers() : null;
8076
+ if (refreshProviders) {
8077
+ const report = await refreshLinkedVaults({
7559
8078
  db: deps.db,
7560
8079
  context,
7561
- vaults: reachable,
7562
- providers: registry,
8080
+ vaults: [...relevant.values()],
8081
+ providers: refreshProviders,
7563
8082
  maxAgeMs: LINKED_VAULT_MISS_MAX_AGE_MS
7564
8083
  });
7565
- if (changed.length > 0) ({ meta, failures, chosen } = await choose());
8084
+ refreshing = new Set(report.pending);
8085
+ if (report.changed.length > 0 || report.outcomes.some((outcome) => outcome.result?.status === "superseded"))
8086
+ ({ meta, outcomes } = await choose());
8087
+ if (report.outcomes.some((outcome) => outcome.state === "done" || outcome.state === "failed")) {
8088
+ reachable = await listVaultsForSubject(deps.db, context, {
8089
+ workspaceId: principal.workspaceId
8090
+ });
8091
+ }
8092
+ }
8093
+ }
8094
+ const failures = [];
8095
+ const chosen = [];
8096
+ const checkedAt = /* @__PURE__ */ new Date();
8097
+ for (const entry of outcomes) {
8098
+ if ("candidate" in entry.outcome) {
8099
+ chosen.push({ reference: entry.reference, candidate: entry.outcome.candidate });
8100
+ continue;
7566
8101
  }
8102
+ let failure = entry.outcome.failure;
8103
+ if (failure.code === "not_found" && reachable) {
8104
+ const unchecked = relevantProviderVaults(entry.reference, entry.candidates, reachable).map((vault) => ({
8105
+ vault,
8106
+ freshness: providerVaultFreshness(vault, checkedAt, {
8107
+ maxAgeMs: LINKED_VAULT_MISS_MAX_AGE_MS,
8108
+ refreshing: refreshing.has(vault.id)
8109
+ })
8110
+ })).filter(({ freshness }) => freshness.state !== "current");
8111
+ if (unchecked.length > 0) failure = uncheckedSourceFailure(entry.reference, unchecked);
8112
+ }
8113
+ failures.push(failure);
7567
8114
  }
7568
8115
  const hasActingHuman = meta.humanExpected && meta.humanValid;
7569
8116
  if (failures.length > 0) {
@@ -7582,13 +8129,16 @@ function createSecretResolver(deps, principal, opts) {
7582
8129
  for (const group of providerGroups.values()) {
7583
8130
  const first = group[0].candidate;
7584
8131
  try {
7585
- const registry = deps.providers ?? opts.providersForSubject?.(meta.subjectId) ?? EMPTY_VAULT_PROVIDER_REGISTRY;
7586
- const provider = await registry.forVault({
8132
+ const provider = await (providers() ?? EMPTY_VAULT_PROVIDER_REGISTRY).forVault({
7587
8133
  source: first.source,
7588
8134
  providerConnectionId: first.providerConnectionId
7589
8135
  });
7590
8136
  const values = await provider.readFields(
7591
- { connectionId: first.providerConnectionId, budget },
8137
+ {
8138
+ connectionId: first.providerConnectionId,
8139
+ budget,
8140
+ cacheEpoch: first.providerCacheEpoch ?? null
8141
+ },
7592
8142
  first.providerVaultRef ?? "",
7593
8143
  first.providerItemRef ?? "",
7594
8144
  [...new Set(group.map((entry) => entry.candidate.providerFieldRef ?? ""))]
@@ -7602,7 +8152,7 @@ function createSecretResolver(deps, principal, opts) {
7602
8152
  failures.push({
7603
8153
  raw: entry.reference.raw,
7604
8154
  code,
7605
- message: code === "provider_rate_limited" ? "the connected vault is rate limited; try again later" : code === "provider_auth_expired" ? "the connected vault needs to be reconnected" : "the connected vault is unavailable"
8155
+ message: code === "provider_rate_limited" ? "the connected vault is rate limited; try again later" : code === "provider_auth_expired" ? "the connected vault needs to be reconnected" : code === "not_found" ? "the connected vault no longer has this item" : "the connected vault is unavailable"
7606
8156
  });
7607
8157
  }
7608
8158
  }
@@ -8416,7 +8966,7 @@ async function swapActiveSandbox(services, ctx, target, workingDir) {
8416
8966
  async function createRunOnSecretResolver(services, attempt, providersForSubject) {
8417
8967
  const session = await getSession3(services.db, attempt.workspaceId, attempt.sessionId);
8418
8968
  return createSecretResolver(
8419
- { db: services.db, encryptionKey: environmentsEncryptionKeyBytes2(services.settings) ?? null },
8969
+ { db: services.db, encryptionKey: environmentsEncryptionKeyBytes3(services.settings) ?? null },
8420
8970
  { kind: "attempt", ...attempt },
8421
8971
  {
8422
8972
  capabilityEnabled: session?.agent ? agentCapabilityEnabled(session.agent.capabilities, "secrets") : true,
@@ -10603,10 +11153,10 @@ async function validateSubmittedTimelineAnnotations(db, workspaceId, sessionId,
10603
11153
  }
10604
11154
 
10605
11155
  // src/domain/encryption-key.ts
10606
- import { environmentsEncryptionKeyBytes as environmentsEncryptionKeyBytes3 } from "@opengeni/config";
11156
+ import { environmentsEncryptionKeyBytes as environmentsEncryptionKeyBytes4 } from "@opengeni/config";
10607
11157
  import { HTTPException as HTTPException19 } from "hono/http-exception";
10608
11158
  function requireEnvironmentEncryption(settings) {
10609
- const key = environmentsEncryptionKeyBytes3(settings);
11159
+ const key = environmentsEncryptionKeyBytes4(settings);
10610
11160
  if (!key) {
10611
11161
  throw new HTTPException19(503, {
10612
11162
  message: "secrets require OPENGENI_ENVIRONMENTS_ENCRYPTION_KEY"
@@ -10630,7 +11180,7 @@ import {
10630
11180
  areOpenGeniSlackBotScopesAccepted
10631
11181
  } from "@opengeni/contracts";
10632
11182
  import {
10633
- getConnectionMetadata as getConnectionMetadata4,
11183
+ getConnectionMetadata as getConnectionMetadata5,
10634
11184
  availableSlackBotConnectionMetadata,
10635
11185
  requireWorkspace as requireWorkspace4
10636
11186
  } from "@opengeni/db";
@@ -10651,7 +11201,7 @@ function hasReservedOpenGeniSlackBotSessionMetadata(metadata) {
10651
11201
  );
10652
11202
  }
10653
11203
  async function requireOpenGeniSlackBotConnection(db, workspaceId, connectionId) {
10654
- const local = await getConnectionMetadata4(db, workspaceId, connectionId, null);
11204
+ const local = await getConnectionMetadata5(db, workspaceId, connectionId, null);
10655
11205
  const workspace = await requireWorkspace4(db, workspaceId);
10656
11206
  const connection = local ?? (await availableSlackBotConnectionMetadata(db, { accountId: workspace.accountId, workspaceId })).find((candidate) => candidate.id === connectionId) ?? null;
10657
11207
  if (!connection || !isOpenGeniSlackBotConnection(connection)) {
@@ -12621,9 +13171,9 @@ async function createSessionForRequestInFileScope(unresolvedDeps, grant, workspa
12621
13171
  const payload = CreateSessionRequest.parse(rawPayload);
12622
13172
  await assertSiteAgentMayChooseCompute(unresolvedDeps.db, grant, payload);
12623
13173
  if (payload.firstPartyMcpTools) {
12624
- const unavailable = deploymentUnavailableFirstPartyMcpTools(unresolvedDeps.settings);
13174
+ const unavailable2 = deploymentUnavailableFirstPartyMcpTools(unresolvedDeps.settings);
12625
13175
  payload.firstPartyMcpTools = payload.firstPartyMcpTools.filter(
12626
- (tool) => !unavailable.has(tool)
13176
+ (tool) => !unavailable2.has(tool)
12627
13177
  );
12628
13178
  }
12629
13179
  const surface = resolveTurnSurface({
@@ -15710,7 +16260,10 @@ function createRealtimeVoiceBilling(deps) {
15710
16260
  };
15711
16261
  }
15712
16262
  const attribution = input.callerSubjectId === facts.ownerSubjectId ? input.attribution : { kind: "unknown" };
15713
- const cost = realtimeVoiceMinuteCreditMicros(voice.pricing);
16263
+ const cost = realtimeVoiceMinuteCreditMicros(
16264
+ voice.pricing,
16265
+ deps.settings.creditMarginBpsOverride
16266
+ );
15714
16267
  const minutes = facts.connections.flatMap(
15715
16268
  (connection) => Array.from(
15716
16269
  { length: realtimeVoiceStartedMinutes(connection.startedAt, connection.observedUntil) },
@@ -15898,7 +16451,11 @@ function createVoiceInputBilling(deps) {
15898
16451
  usage: input.usage,
15899
16452
  trustedDurationSeconds: input.billing.trustedDurationSeconds
15900
16453
  });
15901
- const cost = calculateVoiceInputCost(input.pricing, usage);
16454
+ const cost = calculateVoiceInputCost(
16455
+ input.pricing,
16456
+ usage,
16457
+ deps.settings.creditMarginBpsOverride
16458
+ );
15902
16459
  return await settleVoiceInputUsage(deps.db, {
15903
16460
  accountId: input.accountId,
15904
16461
  workspaceId: input.workspaceId,
@@ -16106,6 +16663,121 @@ function createWebSearchBilling(deps) {
16106
16663
  };
16107
16664
  }
16108
16665
 
16666
+ // src/domain/vaults/remote-provider-runtime.ts
16667
+ import {
16668
+ DestinationPolicyError,
16669
+ ResponseBodyLimitError,
16670
+ isLocalTestEnvironment,
16671
+ pinnedFetch as pinnedFetch2,
16672
+ readResponseTextBounded,
16673
+ validateHttpUrl
16674
+ } from "@opengeni/network";
16675
+ import {
16676
+ RemoteVaultTransportError,
16677
+ VaultProviderError as VaultProviderError4,
16678
+ createRemoteVaultProvider
16679
+ } from "@opengeni/vaults";
16680
+ function createPinnedRemoteVaultTransport(settings, fetchImpl = pinnedFetch2) {
16681
+ return {
16682
+ async post(request) {
16683
+ if (request.signal?.aborted) throw new RemoteVaultTransportError("aborted", false);
16684
+ const controller = new AbortController();
16685
+ let timedOut = false;
16686
+ const timer = setTimeout(() => {
16687
+ timedOut = true;
16688
+ controller.abort();
16689
+ }, request.timeoutMs);
16690
+ const onAbort = () => controller.abort();
16691
+ request.signal?.addEventListener("abort", onAbort, { once: true });
16692
+ let rejectAborted;
16693
+ try {
16694
+ const aborted = new Promise((_resolve, reject) => {
16695
+ rejectAborted = () => reject(new RemoteVaultTransportError(timedOut ? "timeout" : "aborted", true));
16696
+ controller.signal.addEventListener("abort", rejectAborted, { once: true });
16697
+ if (controller.signal.aborted) rejectAborted();
16698
+ });
16699
+ const exchange = async () => {
16700
+ const response = await fetchImpl(
16701
+ request.url,
16702
+ {
16703
+ method: "POST",
16704
+ headers: { ...request.headers },
16705
+ body: request.body,
16706
+ redirect: "manual",
16707
+ signal: controller.signal
16708
+ },
16709
+ settings,
16710
+ { label: "Vault provider", requireHttpsOutsideLocalTest: true }
16711
+ );
16712
+ if (response.status >= 300 && response.status < 400) {
16713
+ await response.body?.cancel().catch(() => void 0);
16714
+ throw new RemoteVaultTransportError("redirect", true);
16715
+ }
16716
+ return {
16717
+ status: response.status,
16718
+ headers: response.headers,
16719
+ body: await readResponseTextBounded(
16720
+ response,
16721
+ request.maxResponseBytes,
16722
+ "Vault provider",
16723
+ { signal: controller.signal }
16724
+ )
16725
+ };
16726
+ };
16727
+ return await Promise.race([exchange(), aborted]);
16728
+ } catch (error) {
16729
+ if (error instanceof RemoteVaultTransportError) throw error;
16730
+ if (error instanceof DestinationPolicyError)
16731
+ throw new RemoteVaultTransportError("refused", false);
16732
+ if (error instanceof ResponseBodyLimitError)
16733
+ throw new RemoteVaultTransportError("too_large", true);
16734
+ throw new RemoteVaultTransportError(
16735
+ timedOut ? "timeout" : controller.signal.aborted ? "aborted" : "network",
16736
+ true
16737
+ );
16738
+ } finally {
16739
+ clearTimeout(timer);
16740
+ request.signal?.removeEventListener("abort", onAbort);
16741
+ if (rejectAborted) controller.signal.removeEventListener("abort", rejectAborted);
16742
+ }
16743
+ }
16744
+ };
16745
+ }
16746
+ function createRemoteExternalVaultFactories(settings) {
16747
+ const transport = createPinnedRemoteVaultTransport(settings);
16748
+ const factory = async ({
16749
+ credentials,
16750
+ operations
16751
+ }) => {
16752
+ if (credentials.provider !== "external" && credentials.provider !== "bitwarden")
16753
+ throw new VaultProviderError4("provider_auth_invalid", "Vault provider does not match.");
16754
+ let endpoint2;
16755
+ try {
16756
+ endpoint2 = validateHttpUrl(credentials.endpoint, {
16757
+ allowLoopbackHttp: isLocalTestEnvironment(settings.environment),
16758
+ label: "Vault provider"
16759
+ });
16760
+ } catch {
16761
+ throw new VaultProviderError4(
16762
+ "provider_auth_invalid",
16763
+ "Use an HTTPS vault endpoint without URL credentials or a fragment."
16764
+ );
16765
+ }
16766
+ return {
16767
+ credentials: { ...credentials, endpoint: endpoint2 },
16768
+ provider: createRemoteVaultProvider({
16769
+ provider: credentials.provider,
16770
+ endpoint: endpoint2,
16771
+ signingSecret: credentials.signingSecret,
16772
+ transport,
16773
+ allowInsecureHttp: isLocalTestEnvironment(settings.environment),
16774
+ ...operations ? { operations } : {}
16775
+ })
16776
+ };
16777
+ };
16778
+ return { external: factory, bitwarden: factory };
16779
+ }
16780
+
16109
16781
  // src/workflow-wake-contract.ts
16110
16782
  var SESSION_WORKFLOW_WAKE_DISPATCHER_SCHEDULE_ID = "opengeni-session-workflow-wake-dispatcher";
16111
16783
  var SESSION_WORKFLOW_WAKE_DISPATCHER_WORKFLOW_TYPE = "sessionWorkflowWakeDispatcherWorkflow";
@@ -17182,7 +17854,7 @@ async function executeConnectOperation(input) {
17182
17854
 
17183
17855
  // src/application/prepared-mcp-connection.ts
17184
17856
  import { createHash as createHash3 } from "crypto";
17185
- import { isDeepStrictEqual as isDeepStrictEqual2 } from "util";
17857
+ import { isDeepStrictEqual as isDeepStrictEqual3 } from "util";
17186
17858
  import {
17187
17859
  CapabilityCatalogItem as CapabilityCatalogItem2,
17188
17860
  McpServerConnectionRef,
@@ -17266,7 +17938,7 @@ async function prepareMcpConnectionActivation(input) {
17266
17938
  const installation = await prepared.commit(db);
17267
17939
  const savedRef = McpServerConnectionRef.safeParse(installation.config.connectionRef);
17268
17940
  const retainedExactPin = savedRef.success && savedRef.data.authoritySource !== "host" && savedRef.data.connectionId === connection.id && savedRef.data.providerDomain === connectionRef.providerDomain && (savedRef.data.subjectScope ?? "workspace") === connectionRef.subjectScope && (savedRef.data.kind ?? connection.kind) === connection.kind && (!savedRef.data.resource || savedRef.data.resource === endpoint2);
17269
- if (installation.status !== "active" || !retainedExactPin && !isDeepStrictEqual2(installation.config.connectionRef, connectionRef)) {
17941
+ if (installation.status !== "active" || !retainedExactPin && !isDeepStrictEqual3(installation.config.connectionRef, connectionRef)) {
17270
17942
  throw new HTTPException26(409, {
17271
17943
  message: "MCP account selection changed; review the existing connection before setup"
17272
17944
  });
@@ -17977,7 +18649,7 @@ function sha256Hex2(bytes) {
17977
18649
  }
17978
18650
 
17979
18651
  // src/domain/skill-search.ts
17980
- import { pinnedFetch as pinnedFetch2, readResponseJsonBounded } from "@opengeni/network";
18652
+ import { pinnedFetch as pinnedFetch3, readResponseJsonBounded } from "@opengeni/network";
17981
18653
  var PublicSkillSearchError = class extends Error {
17982
18654
  constructor(code, message, retryAfterSeconds = null) {
17983
18655
  super(message);
@@ -17992,7 +18664,7 @@ var maxResponseBytes = 256 * 1024;
17992
18664
  var maxResults = 20;
17993
18665
  var ownerPattern = /^[a-z0-9](?:[a-z0-9-]{0,38})$/iu;
17994
18666
  var segmentPattern = /^[a-z0-9](?:[a-z0-9._-]{0,98}[a-z0-9])?$/iu;
17995
- function createPublicSkillSearchClient(settings, fetcher = pinnedFetch2) {
18667
+ function createPublicSkillSearchClient(settings, fetcher = pinnedFetch3) {
17996
18668
  return {
17997
18669
  async search(input) {
17998
18670
  const query = input.query.trim();
@@ -18119,7 +18791,7 @@ function parseRetryAfter(value) {
18119
18791
 
18120
18792
  // src/domain/github-skill-source.ts
18121
18793
  import { HTTPException as HTTPException28 } from "hono/http-exception";
18122
- import { pinnedFetch as pinnedFetch3, readResponseJsonBounded as readResponseJsonBounded2, readResponseTextBounded } from "@opengeni/network";
18794
+ import { pinnedFetch as pinnedFetch4, readResponseJsonBounded as readResponseJsonBounded2, readResponseTextBounded as readResponseTextBounded2 } from "@opengeni/network";
18123
18795
  import {
18124
18796
  PORTABLE_SKILL_MAX_FILES as PORTABLE_SKILL_MAX_FILES2,
18125
18797
  PORTABLE_SKILL_MAX_FILE_BYTES as PORTABLE_SKILL_MAX_FILE_BYTES2,
@@ -18220,7 +18892,7 @@ function createGitHubSkillSourceClient(settings, requestJson = (path, maxBytes,
18220
18892
  }
18221
18893
  async function downloadSkillSnapshot(settings, owner, repository, slug) {
18222
18894
  const signal = AbortSignal.timeout(githubRequestTimeoutMs);
18223
- const response = await pinnedFetch3(
18895
+ const response = await pinnedFetch4(
18224
18896
  `https://skills.sh/api/download/${[owner, repository, slug].map(encodeURIComponent).join("/")}`,
18225
18897
  { headers: { accept: "application/json" }, credentials: "omit", redirect: "manual", signal },
18226
18898
  settings,
@@ -18255,7 +18927,7 @@ async function githubJson(settings, path, maxBytes, label) {
18255
18927
  const controller = new AbortController();
18256
18928
  const timeout = setTimeout(() => controller.abort(), githubRequestTimeoutMs);
18257
18929
  try {
18258
- const response = await pinnedFetch3(
18930
+ const response = await pinnedFetch4(
18259
18931
  `${githubApiBase}${path}`,
18260
18932
  {
18261
18933
  method: "GET",
@@ -18270,7 +18942,7 @@ async function githubJson(settings, path, maxBytes, label) {
18270
18942
  { label, requireHttpsOutsideLocalTest: true }
18271
18943
  );
18272
18944
  if (!response.ok) {
18273
- await readResponseTextBounded(response, 8192, `${label} error`).catch(() => void 0);
18945
+ await readResponseTextBounded2(response, 8192, `${label} error`).catch(() => void 0);
18274
18946
  if (response.status === 404) throw new Error(`${label} was not found or is not public`);
18275
18947
  if (response.status === 403 || response.status === 429) {
18276
18948
  throw new HTTPException28(429, {
@@ -18298,20 +18970,72 @@ function stringValue(value) {
18298
18970
 
18299
18971
  // src/domain/vaults/index.ts
18300
18972
  import { buildMissingSecretAskCard as buildMissingSecretAskCard2 } from "@opengeni/contracts";
18301
- import { isVaultError, VaultError as VaultError3, vaultErrorFromDatabase } from "@opengeni/db";
18973
+ import { isVaultError as isVaultError3, VaultError as VaultError3, vaultErrorFromDatabase } from "@opengeni/db";
18302
18974
 
18303
18975
  // src/domain/vaults/mapping.ts
18304
18976
  import {
18305
18977
  formatSecretReference as formatSecretReference2,
18306
- secretNameKey
18978
+ secretNameKey as secretNameKey2,
18979
+ VaultProviderOperations as VaultProviderOperations2
18307
18980
  } from "@opengeni/contracts";
18308
18981
  import {
18309
18982
  decryptEnvironmentValue as decryptEnvironmentValue2
18310
18983
  } from "@opengeni/db";
18311
- function vaultCanWrite(row, authority) {
18312
- if (row.mode !== "read_write" || !authority.write) return false;
18313
- if (row.ownerKind === "user") return row.ownedByCaller;
18314
- return true;
18984
+ function withoutProviderWrites(operations) {
18985
+ return {
18986
+ ...operations,
18987
+ createItemTypes: [],
18988
+ updateItem: false,
18989
+ removeField: false,
18990
+ archiveItem: false,
18991
+ archiveKind: "none",
18992
+ deleteItem: false,
18993
+ restoreItem: false,
18994
+ writeFiles: false,
18995
+ createTotp: false,
18996
+ updateTotp: false,
18997
+ createVault: false,
18998
+ restoreFieldHistory: false
18999
+ };
19000
+ }
19001
+ function intersectProviderOperations(left, right) {
19002
+ const archiveItem = left.archiveItem && right.archiveItem && left.archiveKind === right.archiveKind && left.archiveKind !== "none";
19003
+ return {
19004
+ createItemTypes: left.createItemTypes.filter(
19005
+ (type) => type !== "passkey" && right.createItemTypes.includes(type)
19006
+ ),
19007
+ updateItem: left.updateItem && right.updateItem,
19008
+ removeField: left.removeField && right.removeField,
19009
+ archiveItem,
19010
+ archiveKind: archiveItem ? left.archiveKind : "none",
19011
+ deleteItem: false,
19012
+ restoreItem: left.restoreItem && right.restoreItem,
19013
+ readFiles: left.readFiles && right.readFiles,
19014
+ writeFiles: left.writeFiles && right.writeFiles,
19015
+ createTotp: left.createTotp && right.createTotp,
19016
+ updateTotp: left.updateTotp && right.updateTotp,
19017
+ createVault: left.createVault && right.createVault,
19018
+ restoreFieldHistory: left.restoreFieldHistory && right.restoreFieldHistory
19019
+ };
19020
+ }
19021
+ function storedProviderOperations(row) {
19022
+ if (row.source === "opengeni" || !row.providerOperations) return null;
19023
+ const parsed = VaultProviderOperations2.safeParse(row.providerOperations);
19024
+ return parsed.success ? providerOperationsForMode(parsed.data, row.mode) : null;
19025
+ }
19026
+ function providerOperationsForMode(operations, mode) {
19027
+ const normalized = intersectProviderOperations(operations, operations);
19028
+ return mode === "read_write" ? normalized : withoutProviderWrites(normalized);
19029
+ }
19030
+ function providerAllowsAnyWrite(operations) {
19031
+ if (!operations) return true;
19032
+ return operations.createItemTypes.length > 0 || operations.updateItem || operations.archiveItem;
19033
+ }
19034
+ function vaultCanWrite(row, authority) {
19035
+ if (row.mode !== "read_write" || !authority.write) return false;
19036
+ if (!providerAllowsAnyWrite(storedProviderOperations(row))) return false;
19037
+ if (row.ownerKind === "user") return row.ownedByCaller;
19038
+ return true;
18315
19039
  }
18316
19040
  function vaultCanManage(row, authority) {
18317
19041
  switch (row.ownerKind) {
@@ -18325,7 +19049,11 @@ function vaultCanManage(row, authority) {
18325
19049
  return authority.write;
18326
19050
  }
18327
19051
  }
18328
- function toVaultSummary(row, authority) {
19052
+ function toVaultSummary(row, authority, options = {}) {
19053
+ const operations = storedProviderOperations(row);
19054
+ const freshness = providerVaultFreshness(row, options.now ?? /* @__PURE__ */ new Date(), {
19055
+ ...options.refreshing ? { refreshing: true } : {}
19056
+ });
18329
19057
  return {
18330
19058
  id: row.id,
18331
19059
  workspaceId: row.workspaceId,
@@ -18342,6 +19070,8 @@ function toVaultSummary(row, authority) {
18342
19070
  lastSyncedAt: row.lastSyncedAt,
18343
19071
  rateLimitedUntil: row.rateLimitedUntil,
18344
19072
  providerConnectionId: row.providerConnectionId,
19073
+ ...operations ? { providerOperations: operations } : {},
19074
+ ...freshness ? { freshness } : {},
18345
19075
  availability: row.availability,
18346
19076
  isDefault: row.isDefault,
18347
19077
  itemCount: Number(row.itemCount),
@@ -18381,7 +19111,7 @@ function vaultItemReferences(item) {
18381
19111
  }
18382
19112
  function usernameOf(row, key) {
18383
19113
  const field = row.fields.find(
18384
- (candidate) => candidate.kind === "text" && secretNameKey(candidate.name) === "username"
19114
+ (candidate) => candidate.kind === "text" && secretNameKey2(candidate.name) === "username"
18385
19115
  );
18386
19116
  return field ? decryptOrNull(key, field.valueEncrypted) : null;
18387
19117
  }
@@ -18418,7 +19148,7 @@ function toVaultItemField(item, field, key) {
18418
19148
  }
18419
19149
  function toVaultItem(row, key) {
18420
19150
  const textField = (name) => {
18421
- const field = row.fields.find((candidate) => secretNameKey(candidate.name) === name);
19151
+ const field = row.fields.find((candidate) => secretNameKey2(candidate.name) === name);
18422
19152
  return field ? decryptOrNull(key, field.valueEncrypted) : null;
18423
19153
  };
18424
19154
  const credentialId = row.type === "passkey" ? textField("credentialid") ?? "" : "";
@@ -18441,68 +19171,25 @@ function toVaultItem(row, key) {
18441
19171
  };
18442
19172
  }
18443
19173
 
18444
- // src/domain/vaults/vault-service.ts
18445
- import { createHash as createHash6 } from "crypto";
18446
- import {
18447
- generatePassword as generatePassword2,
18448
- isValidTotpInput as isValidTotpInput3,
18449
- parseSecretReference,
18450
- parseTotpInput as parseTotpInput2,
18451
- secretNameKey as secretNameKey3,
18452
- SecretReferenceError as SecretReferenceError2,
18453
- totpCodeSync,
18454
- totpRemainingMs,
18455
- systemSecretClock,
18456
- VAULT_LIMITS as VAULT_LIMITS3
18457
- } from "@opengeni/contracts";
18458
- import {
18459
- archiveVaultItem,
18460
- createVault,
18461
- decryptEnvironmentValue as decryptEnvironmentValue3,
18462
- deleteVault,
18463
- deleteVaultItemPermanently,
18464
- encryptEnvironmentValue,
18465
- ensureDefaultVault,
18466
- ensureTaskVault,
18467
- getSession as getSession6,
18468
- getSessionSecretsView,
18469
- getVault,
18470
- getVaultItem,
18471
- listVaultFieldVersions,
18472
- listVaultItems,
18473
- listVaultsForSubject as listVaultsForSubject2,
18474
- vaultItemCursor,
18475
- moveVaultItem,
18476
- readVaultFieldCiphertext,
18477
- resolveSecretReferenceCandidates as resolveSecretReferenceCandidates2,
18478
- restoreVaultItem,
18479
- setSessionVaultPolicy,
18480
- updateVault,
18481
- upsertVaultItem,
18482
- VaultError as VaultError2
18483
- } from "@opengeni/db";
18484
- import {
18485
- createProviderCallBudget as createProviderCallBudget2,
18486
- EMPTY_VAULT_PROVIDER_REGISTRY as EMPTY_VAULT_PROVIDER_REGISTRY2,
18487
- isVaultProviderError as isVaultProviderError4
18488
- } from "@opengeni/vaults";
18489
-
18490
19174
  // src/domain/vaults/provider-writes.ts
19175
+ import { randomUUID } from "crypto";
18491
19176
  import {
18492
19177
  generatePassword,
18493
19178
  isValidTotpInput as isValidTotpInput2,
18494
- secretNameKey as secretNameKey2
19179
+ secretNameKey as secretNameKey3
18495
19180
  } from "@opengeni/contracts";
18496
19181
  import {
18497
19182
  getProviderVaultItemRef,
18498
19183
  VaultError
18499
19184
  } from "@opengeni/db";
18500
19185
  import {
18501
- isVaultProviderError as isVaultProviderError3
19186
+ isVaultProviderError as isVaultProviderError3,
19187
+ providerOperations as providerOperations3,
19188
+ readProviderItemInventory as readProviderItemInventory2
18502
19189
  } from "@opengeni/vaults";
18503
19190
  var TEXT_FIELD_NAMES = ["username", "user", "email", "login", "account"];
18504
19191
  function defaultKind(name) {
18505
- return TEXT_FIELD_NAMES.includes(secretNameKey2(name)) ? "text" : "concealed";
19192
+ return TEXT_FIELD_NAMES.includes(secretNameKey3(name)) ? "text" : "concealed";
18506
19193
  }
18507
19194
  function assertProviderVaultWritable(vault) {
18508
19195
  if (vault.source === "opengeni") {
@@ -18515,11 +19202,43 @@ function assertProviderVaultWritable(vault) {
18515
19202
  throw new VaultError("vault_not_found", "vault not found", { vaultId: vault.id });
18516
19203
  }
18517
19204
  }
18518
- async function providerFieldWrites(fields, existing, resolveReference, generated) {
18519
- const writes = [];
18520
- for (const input of fields) {
19205
+ var NO_PROVIDER_OPERATIONS = withoutProviderWrites({
19206
+ createItemTypes: [],
19207
+ updateItem: false,
19208
+ removeField: false,
19209
+ archiveItem: false,
19210
+ archiveKind: "none",
19211
+ deleteItem: false,
19212
+ restoreItem: false,
19213
+ readFiles: false,
19214
+ writeFiles: false,
19215
+ createTotp: false,
19216
+ updateTotp: false,
19217
+ createVault: false,
19218
+ restoreFieldHistory: false
19219
+ });
19220
+ function effectiveProviderOperations(vault, provider) {
19221
+ if (vault.source === "opengeni") return NO_PROVIDER_OPERATIONS;
19222
+ const stored2 = storedProviderOperations(vault);
19223
+ const live = provider ? providerOperations3(provider.capabilities()) : null;
19224
+ const operations = live && stored2 ? intersectProviderOperations(live, stored2) : stored2 ?? live;
19225
+ return operations ? providerOperationsForMode(operations, vault.mode) : NO_PROVIDER_OPERATIONS;
19226
+ }
19227
+ function refuse(vault, operation, message, details = {}) {
19228
+ throw new VaultError("vault_read_only", message, { vaultId: vault.id, operation, ...details });
19229
+ }
19230
+ function refuseProviderOperation(vault, operation) {
19231
+ const messages = {
19232
+ deleteItem: `Opengeni never permanently deletes items in ${vault.name}. Archive the item, or delete it in the provider itself.`,
19233
+ restoreItem: `Items archived in ${vault.name} are restored in the provider itself; the restored item appears after the next sync.`,
19234
+ restoreFieldHistory: `${vault.name} keeps field history in the provider; restore an earlier value there.`
19235
+ };
19236
+ return refuse(vault, operation, messages[operation]);
19237
+ }
19238
+ function planFieldWrites(vault, operations, creating, fields, existing) {
19239
+ return fields.map((input) => {
18521
19240
  const current = existing.find(
18522
- (field) => input.fieldId !== void 0 && field.id === input.fieldId || secretNameKey2(field.name) === secretNameKey2(input.name)
19241
+ (field) => input.fieldId !== void 0 && field.id === input.fieldId || secretNameKey3(field.name) === secretNameKey3(input.name)
18523
19242
  );
18524
19243
  const kind = input.kind ?? current?.kind ?? defaultKind(input.name);
18525
19244
  const base = {
@@ -18527,24 +19246,51 @@ async function providerFieldWrites(fields, existing, resolveReference, generated
18527
19246
  name: input.name,
18528
19247
  kind
18529
19248
  };
18530
- if (input.remove) {
18531
- writes.push({ ...base, remove: true });
18532
- continue;
18533
- }
18534
19249
  if (input.restoreVersion !== void 0) {
18535
- throw new VaultError(
18536
- "provider_unavailable",
18537
- "connected vaults keep their history in the provider; restore it there",
18538
- { field: input.name }
18539
- );
19250
+ return refuseProviderOperation(vault, "restoreFieldHistory");
18540
19251
  }
18541
- if (input.fileBase64 !== void 0 || kind === "file") {
19252
+ if (input.fileBase64 !== void 0 || kind === "file" || current?.kind === "file") {
18542
19253
  throw new VaultError(
18543
19254
  "provider_unavailable",
18544
19255
  "files in connected vaults are read-only in Opengeni",
18545
- { field: input.name }
19256
+ { field: input.name, vaultId: vault.id, operation: "writeFiles" }
18546
19257
  );
18547
19258
  }
19259
+ const totp = kind === "totp" || current?.kind === "totp";
19260
+ if (input.remove) {
19261
+ if (!operations.removeField) {
19262
+ refuse(vault, "removeField", `${vault.name} does not allow removing fields here`, {
19263
+ field: input.name
19264
+ });
19265
+ }
19266
+ if (totp && !operations.updateTotp) {
19267
+ refuse(vault, "updateTotp", `${vault.name} does not allow changing one-time passwords`, {
19268
+ field: input.name
19269
+ });
19270
+ }
19271
+ return { input, base, kind };
19272
+ }
19273
+ if (totp) {
19274
+ const allowed = creating ? operations.createTotp : operations.updateTotp;
19275
+ if (!allowed) {
19276
+ refuse(
19277
+ vault,
19278
+ creating ? "createTotp" : "updateTotp",
19279
+ `${vault.name} does not allow ${creating ? "saving" : "changing"} one-time passwords`,
19280
+ { field: input.name }
19281
+ );
19282
+ }
19283
+ }
19284
+ return { input, base, kind };
19285
+ });
19286
+ }
19287
+ async function providerFieldValues(planned, resolveReference, generated) {
19288
+ const writes = [];
19289
+ for (const { input, base, kind } of planned) {
19290
+ if (input.remove) {
19291
+ writes.push({ ...base, remove: true });
19292
+ continue;
19293
+ }
18548
19294
  let value;
18549
19295
  if (input.generate !== void 0) {
18550
19296
  value = generatePassword(input.generate);
@@ -18574,6 +19320,16 @@ function providerFailure(error) {
18574
19320
  }
18575
19321
  throw error;
18576
19322
  }
19323
+ function mutationFailure(vault, error, what) {
19324
+ if (isVaultProviderError3(error) && error.code !== "provider_outcome_unknown") {
19325
+ return providerFailure(error);
19326
+ }
19327
+ throw new VaultError(
19328
+ "provider_outcome_unknown",
19329
+ `${vault.name} did not confirm ${what}. It may have been applied: check the vault before trying again.`,
19330
+ { vaultId: vault.id }
19331
+ );
19332
+ }
18577
19333
  async function providerFor(deps, vault) {
18578
19334
  try {
18579
19335
  return await deps.providers.forVault({
@@ -18584,38 +19340,68 @@ async function providerFor(deps, vault) {
18584
19340
  return providerFailure(error);
18585
19341
  }
18586
19342
  }
19343
+ function mutationContext(deps, vault) {
19344
+ return {
19345
+ connectionId: vault.providerConnectionId,
19346
+ budget: deps.budget ?? { take() {
19347
+ } },
19348
+ cacheEpoch: vault.providerCacheEpoch ?? null,
19349
+ // Read-before-write must see the provider's current item, never a cached copy.
19350
+ forceRefresh: true,
19351
+ idempotencyKey: randomUUID()
19352
+ };
19353
+ }
19354
+ async function refreshAfterWrite(deps, vault) {
19355
+ try {
19356
+ const result = await syncLinkedProviderVault({
19357
+ db: deps.db,
19358
+ context: deps.context,
19359
+ vaultId: vault.id,
19360
+ providers: deps.providers,
19361
+ force: true,
19362
+ ...deps.budget ? { budget: deps.budget } : {}
19363
+ });
19364
+ return result.status;
19365
+ } catch {
19366
+ return "error";
19367
+ }
19368
+ }
18587
19369
  async function refreshAndFind(deps, vault, providerItemRef) {
18588
- await syncLinkedProviderVault({
18589
- db: deps.db,
18590
- context: deps.context,
18591
- vaultId: vault.id,
18592
- providers: deps.providers,
18593
- force: true,
18594
- ...deps.budget ? { budget: deps.budget } : {}
18595
- });
19370
+ const sync = await refreshAfterWrite(deps, vault);
18596
19371
  try {
18597
19372
  const ref = await getProviderVaultItemRef(deps.db, deps.context, {
18598
19373
  vaultId: vault.id,
18599
19374
  providerItemRef
18600
19375
  });
18601
- return ref.itemId;
19376
+ return { status: "listed", itemId: ref.itemId };
18602
19377
  } catch (error) {
18603
19378
  if (error instanceof VaultError && error.code === "vault_item_not_found") {
18604
- throw new VaultError(
18605
- "provider_unavailable",
18606
- "saved in the provider; Opengeni will show it after the next sync",
18607
- { vaultId: vault.id }
18608
- );
19379
+ return { status: "saved_not_listed", vaultId: vault.id, sync };
18609
19380
  }
18610
19381
  throw error;
18611
19382
  }
18612
19383
  }
18613
- async function writeProviderVaultItem(deps, input) {
19384
+ async function writeProviderVaultItemOutcome(deps, input) {
18614
19385
  const { vault, existing, change } = input;
18615
19386
  assertProviderVaultWritable(vault);
18616
- if ((change.type ?? existing?.type) === "passkey") {
19387
+ const type = change.type ?? existing?.type ?? "login";
19388
+ if (type === "passkey") {
18617
19389
  throw new VaultError("vault_permission_denied", "passkeys live only in Opengeni vaults");
18618
19390
  }
19391
+ const provider = await providerFor(deps, vault);
19392
+ const operations = effectiveProviderOperations(vault, provider);
19393
+ if (existing) {
19394
+ if (!operations.updateItem) {
19395
+ refuse(vault, "updateItem", `${vault.name} does not allow changing items here`);
19396
+ }
19397
+ } else if (!operations.createItemTypes.includes(type)) {
19398
+ refuse(
19399
+ vault,
19400
+ "createItem",
19401
+ operations.createItemTypes.length === 0 ? `${vault.name} does not allow creating items here` : `${vault.name} cannot hold ${type} items; it accepts ${operations.createItemTypes.join(", ")}`,
19402
+ { itemType: type, allowedItemTypes: operations.createItemTypes }
19403
+ );
19404
+ }
18619
19405
  let externalItemId;
18620
19406
  let expectedVersion;
18621
19407
  if (existing) {
@@ -18632,60 +19418,190 @@ async function writeProviderVaultItem(deps, input) {
18632
19418
  externalItemId = ref.providerItemRef;
18633
19419
  expectedVersion = ref.providerVersion;
18634
19420
  }
18635
- const fields = await providerFieldWrites(
19421
+ const planned = planFieldWrites(
19422
+ vault,
19423
+ operations,
19424
+ existing === null,
18636
19425
  change.fields ?? [],
18637
- existing?.fields ?? [],
18638
- deps.resolveReference,
18639
- input.generated ?? []
19426
+ existing?.fields ?? []
18640
19427
  );
18641
- const provider = await providerFor(deps, vault);
19428
+ const fields = await providerFieldValues(planned, deps.resolveReference, input.generated ?? []);
19429
+ const name = change.name ?? existing?.name ?? "";
18642
19430
  let written;
18643
19431
  try {
18644
- written = await provider.writeItem(
18645
- { connectionId: vault.providerConnectionId, budget: deps.budget ?? { take() {
18646
- } } },
18647
- vault.providerVaultRef ?? "",
19432
+ written = await provider.writeItem(mutationContext(deps, vault), vault.providerVaultRef ?? "", {
19433
+ ...externalItemId ? { externalItemId, expectedVersion: expectedVersion ?? null } : {},
19434
+ name,
19435
+ type,
19436
+ websites: change.websites ?? existing?.websites ?? [],
19437
+ fields
19438
+ });
19439
+ } catch (error) {
19440
+ return mutationFailure(vault, error, existing ? `the change to ${name}` : `saving ${name}`);
19441
+ }
19442
+ return await refreshAndFind(deps, vault, written.externalItemId);
19443
+ }
19444
+ function savedNotListedError(vault, itemName, outcome) {
19445
+ return new VaultError(
19446
+ "provider_inventory_incomplete",
19447
+ `Saved ${itemName} in ${vault.name}, but Opengeni could not list it yet. Do not save it again: it appears after the next successful sync.`,
19448
+ { saved: true, vaultId: outcome.vaultId, itemName, sync: outcome.sync }
19449
+ );
19450
+ }
19451
+ async function writeProviderVaultItem(deps, input) {
19452
+ const outcome = await writeProviderVaultItemOutcome(deps, input);
19453
+ if (outcome.status === "listed") return outcome.itemId;
19454
+ throw savedNotListedError(
19455
+ input.vault,
19456
+ input.change.name ?? input.existing?.name ?? "the item",
19457
+ outcome
19458
+ );
19459
+ }
19460
+ function providerArchiveKind(vault, provider) {
19461
+ const operations = effectiveProviderOperations(vault, provider);
19462
+ if (!operations.archiveItem) return null;
19463
+ return operations.archiveKind === "archive" || operations.archiveKind === "trash" ? operations.archiveKind : null;
19464
+ }
19465
+ async function assertProviderVaultAccepts(deps, vault, item) {
19466
+ assertProviderVaultWritable(vault);
19467
+ if (item.type === "passkey") {
19468
+ throw new VaultError("vault_permission_denied", "passkeys live only in Opengeni vaults");
19469
+ }
19470
+ if (item.fields.some((field) => field.kind === "file")) {
19471
+ throw new VaultError(
19472
+ "provider_unavailable",
19473
+ "files in connected vaults are read-only in Opengeni; copy the item into an Opengeni vault instead",
19474
+ { vaultId: vault.id, operation: "writeFiles" }
19475
+ );
19476
+ }
19477
+ const operations = effectiveProviderOperations(vault, await providerFor(deps, vault));
19478
+ if (!operations.createItemTypes.includes(item.type)) {
19479
+ refuse(
19480
+ vault,
19481
+ "createItem",
19482
+ operations.createItemTypes.length === 0 ? `${vault.name} does not allow creating items here` : `${vault.name} cannot hold ${item.type} items; it accepts ${operations.createItemTypes.join(", ")}`,
19483
+ { itemType: item.type, allowedItemTypes: operations.createItemTypes }
19484
+ );
19485
+ }
19486
+ if (item.fields.some((field) => field.kind === "totp") && !operations.createTotp) {
19487
+ refuse(vault, "createTotp", `${vault.name} does not allow saving one-time passwords`);
19488
+ }
19489
+ }
19490
+ async function assertProviderVaultReleases(deps, vault) {
19491
+ assertProviderVaultWritable(vault);
19492
+ const archiveKind = providerArchiveKind(vault, await providerFor(deps, vault));
19493
+ if (archiveKind === null) {
19494
+ refuse(
19495
+ vault,
19496
+ "archiveItem",
19497
+ `${vault.name} cannot archive the original recoverably, so the item cannot be moved out of it; copy it instead`
19498
+ );
19499
+ }
19500
+ return archiveKind;
19501
+ }
19502
+ async function observeProviderItemVersion(deps, input) {
19503
+ const { vault } = input;
19504
+ const provider = await providerFor(deps, vault);
19505
+ try {
19506
+ const inventory = await readProviderItemInventory2(
19507
+ provider,
18648
19508
  {
18649
- ...externalItemId ? { externalItemId, expectedVersion: expectedVersion ?? null } : {},
18650
- name: change.name ?? existing?.name ?? "",
18651
- type: change.type ?? existing?.type ?? "login",
18652
- websites: change.websites ?? existing?.websites ?? [],
18653
- fields
18654
- }
19509
+ connectionId: vault.providerConnectionId,
19510
+ budget: deps.budget ?? { take() {
19511
+ } },
19512
+ cacheEpoch: vault.providerCacheEpoch ?? null,
19513
+ forceRefresh: true
19514
+ },
19515
+ vault.providerVaultRef ?? ""
18655
19516
  );
19517
+ const item = inventory.items.find((meta) => meta.externalItemId === input.providerItemRef);
19518
+ if (!item) return { listed: false, consistent: inventory.consistent };
19519
+ return {
19520
+ listed: true,
19521
+ version: typeof item.version === "string" && item.version.length > 0 ? item.version : null
19522
+ };
18656
19523
  } catch (error) {
18657
- return providerFailure(error);
19524
+ if (isVaultProviderError3(error)) {
19525
+ throw new VaultError(vaultErrorCodeForProvider(error.code, "vault"), error.message, {
19526
+ vaultId: vault.id,
19527
+ ...error.retryAfterMs ? { retryAfterMs: error.retryAfterMs } : {}
19528
+ });
19529
+ }
19530
+ throw error;
18658
19531
  }
18659
- return await refreshAndFind(deps, vault, written.externalItemId);
18660
19532
  }
18661
19533
  async function archiveProviderVaultItem(deps, input) {
18662
- assertProviderVaultWritable(input.vault);
19534
+ const { vault } = input;
19535
+ assertProviderVaultWritable(vault);
18663
19536
  const ref = await getProviderVaultItemRef(deps.db, deps.context, {
18664
- vaultId: input.vault.id,
19537
+ vaultId: vault.id,
18665
19538
  itemId: input.itemId
18666
19539
  });
18667
- const provider = await providerFor(deps, input.vault);
19540
+ const provider = await providerFor(deps, vault);
19541
+ const archiveKind = providerArchiveKind(vault, provider);
19542
+ if (archiveKind === null) {
19543
+ refuse(vault, "archiveItem", `${vault.name} does not allow archiving items here`);
19544
+ }
18668
19545
  try {
18669
19546
  await provider.archiveItem(
18670
- { connectionId: input.vault.providerConnectionId, budget: deps.budget ?? { take() {
18671
- } } },
18672
- input.vault.providerVaultRef ?? "",
19547
+ mutationContext(deps, vault),
19548
+ vault.providerVaultRef ?? "",
18673
19549
  ref.providerItemRef
18674
19550
  );
18675
19551
  } catch (error) {
18676
- providerFailure(error);
19552
+ mutationFailure(vault, error, "archiving the item");
18677
19553
  }
18678
- await syncLinkedProviderVault({
18679
- db: deps.db,
18680
- context: deps.context,
18681
- vaultId: input.vault.id,
18682
- providers: deps.providers,
18683
- force: true,
18684
- ...deps.budget ? { budget: deps.budget } : {}
18685
- });
19554
+ await refreshAfterWrite(deps, vault);
19555
+ return { archiveKind };
18686
19556
  }
18687
19557
 
18688
19558
  // src/domain/vaults/vault-service.ts
19559
+ import { createHash as createHash6 } from "crypto";
19560
+ import {
19561
+ generatePassword as generatePassword2,
19562
+ isValidTotpInput as isValidTotpInput3,
19563
+ parseSecretReference,
19564
+ parseTotpInput as parseTotpInput2,
19565
+ secretNameKey as secretNameKey4,
19566
+ SecretReferenceError as SecretReferenceError2,
19567
+ totpCodeSync,
19568
+ totpRemainingMs,
19569
+ systemSecretClock,
19570
+ VAULT_LIMITS as VAULT_LIMITS3
19571
+ } from "@opengeni/contracts";
19572
+ import {
19573
+ archiveVaultItem,
19574
+ createVault,
19575
+ decryptEnvironmentValue as decryptEnvironmentValue3,
19576
+ deleteVault,
19577
+ deleteVaultItemPermanently,
19578
+ encryptEnvironmentValue as encryptEnvironmentValue2,
19579
+ ensureDefaultVault,
19580
+ ensureTaskVault,
19581
+ getSession as getSession6,
19582
+ getSessionSecretsView,
19583
+ getProviderVaultItemRef as getProviderVaultItemRef2,
19584
+ getVault,
19585
+ getVaultItem,
19586
+ isVaultError as isVaultError2,
19587
+ listVaultFieldVersions,
19588
+ listVaultItems,
19589
+ listVaultsForSubject as listVaultsForSubject2,
19590
+ vaultItemCursor,
19591
+ moveVaultItem,
19592
+ readVaultFieldCiphertext,
19593
+ resolveSecretReferenceCandidates as resolveSecretReferenceCandidates2,
19594
+ restoreVaultItem,
19595
+ setSessionVaultPolicy,
19596
+ updateVault,
19597
+ upsertVaultItem,
19598
+ VaultError as VaultError2
19599
+ } from "@opengeni/db";
19600
+ import {
19601
+ createProviderCallBudget as createProviderCallBudget2,
19602
+ EMPTY_VAULT_PROVIDER_REGISTRY as EMPTY_VAULT_PROVIDER_REGISTRY2,
19603
+ isVaultProviderError as isVaultProviderError4
19604
+ } from "@opengeni/vaults";
18689
19605
  function subjectContext(actor, workspaceId) {
18690
19606
  return {
18691
19607
  kind: "subject",
@@ -18722,13 +19638,13 @@ function requireKey(deps) {
18722
19638
  return deps.encryptionKey;
18723
19639
  }
18724
19640
  function defaultFieldKind(name) {
18725
- return ["username", "user", "email", "login", "account", "rpid"].includes(secretNameKey3(name)) ? "text" : "concealed";
19641
+ return ["username", "user", "email", "login", "account", "rpid"].includes(secretNameKey4(name)) ? "text" : "concealed";
18726
19642
  }
18727
19643
  function createVaultService(deps) {
18728
19644
  const clock = deps.clock ?? systemSecretClock;
18729
19645
  async function fieldWrite(input, existing, resolveReference, generated) {
18730
19646
  const current = existing?.fields.find(
18731
- (field) => input.fieldId !== void 0 && field.id === input.fieldId || secretNameKey3(field.name) === secretNameKey3(input.name)
19647
+ (field) => input.fieldId !== void 0 && field.id === input.fieldId || secretNameKey4(field.name) === secretNameKey4(input.name)
18732
19648
  );
18733
19649
  const name = current?.name ?? input.name;
18734
19650
  const write = { name };
@@ -18781,7 +19697,7 @@ function createVaultService(deps) {
18781
19697
  return {
18782
19698
  ...write,
18783
19699
  kind,
18784
- valueEncrypted: encryptEnvironmentValue(requireKey(deps), plaintext),
19700
+ valueEncrypted: encryptEnvironmentValue2(requireKey(deps), plaintext),
18785
19701
  valueSize: size
18786
19702
  };
18787
19703
  }
@@ -18848,6 +19764,11 @@ function createVaultService(deps) {
18848
19764
  function key() {
18849
19765
  return deps.encryptionKey;
18850
19766
  }
19767
+ async function refuseForConnectedItem(context, itemId, operation) {
19768
+ const item = await getVaultItem(deps.db, context, { itemId });
19769
+ if (item.vaultSource === "opengeni") return;
19770
+ refuseProviderOperation(await getVault(deps.db, context, { vaultId: item.vaultId }), operation);
19771
+ }
18851
19772
  async function moveAcrossProviders(input) {
18852
19773
  const source = await getVaultItem(deps.db, input.context, { itemId: input.itemId });
18853
19774
  const target = await getVault(deps.db, input.context, { vaultId: input.targetVaultId });
@@ -18873,6 +19794,46 @@ function createVaultService(deps) {
18873
19794
  }
18874
19795
  );
18875
19796
  }
19797
+ if (target.source !== "opengeni") {
19798
+ await assertProviderVaultAccepts(input.providerWrites, target, source);
19799
+ }
19800
+ if (input.mode === "move" && source.vaultSource !== "opengeni") {
19801
+ await assertProviderVaultReleases(input.providerWrites, sourceVault);
19802
+ }
19803
+ let pinned = null;
19804
+ if (input.mode === "move" && source.vaultSource !== "opengeni") {
19805
+ const mirrored = await getProviderVaultItemRef2(deps.db, input.context, {
19806
+ vaultId: sourceVault.id,
19807
+ itemId: source.id
19808
+ });
19809
+ const { providerItemRef } = mirrored;
19810
+ const observed = await observeProviderItemVersion(input.providerWrites, {
19811
+ vault: sourceVault,
19812
+ providerItemRef
19813
+ });
19814
+ if (!observed.listed) {
19815
+ throw new VaultError2(
19816
+ "vault_item_not_found",
19817
+ `${source.name} is not listed in ${sourceVault.name} right now; sync the vault and try again`,
19818
+ { itemId: source.id, vaultId: sourceVault.id }
19819
+ );
19820
+ }
19821
+ if (observed.version !== null) {
19822
+ if (mirrored.providerVersion !== observed.version || mirrored.generation !== source.generation) {
19823
+ throw new VaultError2(
19824
+ "provider_conflict",
19825
+ `${source.name} changed in ${sourceVault.name} since Opengeni last synced it, so its fields here may be out of date. Nothing was moved: sync ${sourceVault.name}, then move it again.`,
19826
+ {
19827
+ itemId: source.id,
19828
+ vaultId: sourceVault.id,
19829
+ mirroredVersion: mirrored.providerVersion,
19830
+ providerVersion: observed.version
19831
+ }
19832
+ );
19833
+ }
19834
+ pinned = { providerItemRef, version: observed.version };
19835
+ }
19836
+ }
18876
19837
  const name = input.newName ?? source.name;
18877
19838
  const fields = [];
18878
19839
  for (const field of source.fields) {
@@ -18899,14 +19860,19 @@ function createVaultService(deps) {
18899
19860
  ...source.expiresAt ? { expiresAt: source.expiresAt } : {},
18900
19861
  fields
18901
19862
  };
18902
- let created;
19863
+ let created = null;
19864
+ let notListed = null;
18903
19865
  if (target.source !== "opengeni") {
18904
- const itemId = await writeProviderVaultItem(input.providerWrites, {
19866
+ const outcome = await writeProviderVaultItemOutcome(input.providerWrites, {
18905
19867
  vault: target,
18906
19868
  existing: null,
18907
19869
  change
18908
19870
  });
18909
- created = await getVaultItem(deps.db, input.context, { itemId });
19871
+ if (outcome.status === "listed") {
19872
+ created = await getVaultItem(deps.db, input.context, { itemId: outcome.itemId });
19873
+ } else {
19874
+ notListed = outcome;
19875
+ }
18910
19876
  } else {
18911
19877
  const write = await itemWrite(
18912
19878
  change,
@@ -18921,17 +19887,54 @@ function createVaultService(deps) {
18921
19887
  });
18922
19888
  }
18923
19889
  if (input.mode === "move") {
18924
- if (source.vaultSource !== "opengeni") {
18925
- await archiveProviderVaultItem(input.providerWrites, {
18926
- vault: sourceVault,
18927
- itemId: source.id
18928
- });
18929
- } else {
18930
- await archiveVaultItem(deps.db, input.context, { itemId: source.id });
19890
+ try {
19891
+ if (source.vaultSource !== "opengeni") {
19892
+ if (pinned) await assertSourceUnchanged(input.providerWrites, sourceVault, pinned);
19893
+ await archiveProviderVaultItem(input.providerWrites, {
19894
+ vault: sourceVault,
19895
+ itemId: source.id
19896
+ });
19897
+ } else {
19898
+ await archiveVaultItem(deps.db, input.context, { itemId: source.id });
19899
+ }
19900
+ } catch (error) {
19901
+ const changed = isVaultError2(error) && error.details?.sourceChanged === true;
19902
+ throw new VaultError2(
19903
+ isVaultError2(error) ? error.code : "provider_outcome_unknown",
19904
+ changed ? `Saved ${name} in ${target.name} with the values that were read, but the original in ${sourceVault.name} ${error.message}, so it was kept. Do not move it again: compare the two, then update the copy or archive the original in ${sourceVault.name}.` : `Saved ${name} in ${target.name}, but archiving the original in ${sourceVault.name} did not complete${isVaultError2(error) ? ` (${error.message})` : ""}. Do not move it again: check ${sourceVault.name} and archive the original there if it is still listed.`,
19905
+ {
19906
+ saved: true,
19907
+ sourceArchived: false,
19908
+ ...changed ? { sourceChanged: true } : {},
19909
+ vaultId: target.id,
19910
+ ...created ? { itemId: created.id } : {},
19911
+ sourceItemId: source.id,
19912
+ sourceVaultId: sourceVault.id
19913
+ }
19914
+ );
18931
19915
  }
18932
19916
  }
19917
+ if (notListed) {
19918
+ const error = savedNotListedError(target, name, notListed);
19919
+ throw new VaultError2(error.code, error.message, {
19920
+ ...error.details,
19921
+ ...input.mode === "move" ? { sourceArchived: true } : {}
19922
+ });
19923
+ }
18933
19924
  return created;
18934
19925
  }
19926
+ async function assertSourceUnchanged(providerWrites, vault, pinned) {
19927
+ const now = await observeProviderItemVersion(providerWrites, {
19928
+ vault,
19929
+ providerItemRef: pinned.providerItemRef
19930
+ });
19931
+ if (now.listed && now.version === pinned.version) return;
19932
+ throw new VaultError2(
19933
+ now.listed ? "vault_generation_conflict" : "vault_item_not_found",
19934
+ now.listed ? "changed after its values were read" : "is no longer listed at the version that was read",
19935
+ { sourceChanged: true, vaultId: vault.id }
19936
+ );
19937
+ }
18935
19938
  function toItem(row) {
18936
19939
  return toVaultItem(row, key());
18937
19940
  }
@@ -18951,7 +19954,12 @@ function createVaultService(deps) {
18951
19954
  });
18952
19955
  const budget = createProviderCallBudget2(VAULT_LIMITS3.providerReadsPerAttempt);
18953
19956
  const [value] = await provider.readFields(
18954
- { connectionId: read.providerConnectionId, budget },
19957
+ // The durable epoch invalidates provider value caches after a refresh anywhere.
19958
+ {
19959
+ connectionId: read.providerConnectionId,
19960
+ budget,
19961
+ cacheEpoch: read.providerCacheEpoch ?? null
19962
+ },
18955
19963
  read.providerVaultRef ?? "",
18956
19964
  read.providerItemRef ?? "",
18957
19965
  [read.providerFieldRef ?? ""]
@@ -19008,9 +20016,12 @@ function createVaultService(deps) {
19008
20016
  const vaults = await listVaultsForSubject2(deps.db, principalContext(principal), {
19009
20017
  workspaceId: principal.workspaceId
19010
20018
  });
20019
+ return agentVaultFrom(vaults, nameOrId);
20020
+ }
20021
+ function agentVaultFrom(vaults, nameOrId) {
19011
20022
  const byId = vaults.find((vault) => vault.id === nameOrId.toLowerCase());
19012
20023
  if (byId) return byId;
19013
- const matches = vaults.filter((vault) => secretNameKey3(vault.name) === secretNameKey3(nameOrId));
20024
+ const matches = vaults.filter((vault) => secretNameKey4(vault.name) === secretNameKey4(nameOrId));
19014
20025
  if (matches.length === 0) {
19015
20026
  throw new VaultError2("vault_not_found", `no reachable vault is named ${nameOrId}`);
19016
20027
  }
@@ -19237,12 +20248,14 @@ function createVaultService(deps) {
19237
20248
  await archiveVaultItem(deps.db, context, { itemId });
19238
20249
  },
19239
20250
  async restoreItem(actor, workspaceId, itemId) {
19240
- return toItem(
19241
- await restoreVaultItem(deps.db, subjectContext(actor, workspaceId), { itemId })
19242
- );
20251
+ const context = subjectContext(actor, workspaceId);
20252
+ await refuseForConnectedItem(context, itemId, "restoreItem");
20253
+ return toItem(await restoreVaultItem(deps.db, context, { itemId }));
19243
20254
  },
19244
20255
  async deleteItemPermanently(actor, workspaceId, itemId) {
19245
- await deleteVaultItemPermanently(deps.db, subjectContext(actor, workspaceId), { itemId });
20256
+ const context = subjectContext(actor, workspaceId);
20257
+ await refuseForConnectedItem(context, itemId, "deleteItem");
20258
+ await deleteVaultItemPermanently(deps.db, context, { itemId });
19246
20259
  },
19247
20260
  async moveItem(actor, workspaceId, itemId, req) {
19248
20261
  const crossed = await moveAcrossProviders({
@@ -19282,6 +20295,12 @@ function createVaultService(deps) {
19282
20295
  async restoreFieldVersion(actor, workspaceId, itemId, fieldId, version) {
19283
20296
  const context = subjectContext(actor, workspaceId);
19284
20297
  const existing = await getVaultItem(deps.db, context, { itemId });
20298
+ if (existing.vaultSource !== "opengeni") {
20299
+ refuseProviderOperation(
20300
+ await getVault(deps.db, context, { vaultId: existing.vaultId }),
20301
+ "restoreFieldHistory"
20302
+ );
20303
+ }
19285
20304
  const field = existing.fields.find((candidate) => candidate.id === fieldId);
19286
20305
  if (!field) throw new VaultError2("vault_field_not_found", "field not found", { fieldId });
19287
20306
  return toItem(
@@ -19318,26 +20337,37 @@ function createVaultService(deps) {
19318
20337
  let reachable = await listVaultsForSubject2(deps.db, context, {
19319
20338
  workspaceId: principal.workspaceId
19320
20339
  });
19321
- if (deps.providers) {
19322
- const changed = await refreshStaleLinkedVaults({
20340
+ let scoped = input.vault !== void 0 ? [agentVaultFrom(reachable, input.vault)] : reachable;
20341
+ let refreshing = /* @__PURE__ */ new Set();
20342
+ const connected = scoped.filter((vault) => vault.source !== "opengeni");
20343
+ if (deps.providers && connected.length > 0) {
20344
+ const report = await refreshLinkedVaults({
19323
20345
  db: deps.db,
19324
20346
  context,
19325
- vaults: reachable,
20347
+ vaults: connected,
19326
20348
  providers: deps.providers,
19327
- maxAgeMs: LINKED_VAULT_SEARCH_MAX_AGE_MS
20349
+ maxAgeMs: LINKED_VAULT_SEARCH_MAX_AGE_MS,
20350
+ ...input.refresh && input.cursor === void 0 ? { force: true } : {}
19328
20351
  });
19329
- if (changed.length > 0) {
20352
+ refreshing = new Set(report.pending);
20353
+ if (report.outcomes.some((outcome) => outcome.state === "done" || outcome.state === "failed")) {
19330
20354
  reachable = await listVaultsForSubject2(deps.db, context, {
19331
20355
  workspaceId: principal.workspaceId
19332
20356
  });
20357
+ scoped = scoped.map((vault) => reachable.find((row) => row.id === vault.id) ?? vault);
19333
20358
  }
19334
20359
  }
20360
+ const checkedAt = /* @__PURE__ */ new Date();
20361
+ const freshnessOf = (vault) => providerVaultFreshness(vault, checkedAt, { refreshing: refreshing.has(vault.id) });
20362
+ const sources = scoped.flatMap((vault) => {
20363
+ const freshness = freshnessOf(vault);
20364
+ return freshness ? [{ vaultId: vault.id, freshness }] : [];
20365
+ });
20366
+ const complete = sources.every((source) => source.freshness.state === "current");
19335
20367
  const byTier = (vaults2) => [...vaults2].sort(
19336
20368
  (left, right) => left.tier - right.tier || left.name.localeCompare(right.name)
19337
20369
  );
19338
- const vaults = byTier(
19339
- input.vault !== void 0 ? [await agentVault(principal, input.vault)] : reachable
19340
- );
20370
+ const vaults = byTier(scoped);
19341
20371
  const limit = input.limit ?? 20;
19342
20372
  const filters = agentSearchFilterKey(input);
19343
20373
  let startIndex = 0;
@@ -19403,15 +20433,21 @@ function createVaultService(deps) {
19403
20433
  truncated: next !== null,
19404
20434
  nextCursor: next === null ? null : encodeAgentSearchCursor(next),
19405
20435
  ...input.cursor === void 0 ? {
19406
- vaults: byTier(reachable).map((vault) => ({
19407
- id: vault.id,
19408
- name: vault.name,
19409
- ownerKind: vault.ownerKind,
19410
- source: vault.source,
19411
- mode: vault.mode,
19412
- itemCount: vault.itemCount
19413
- }))
19414
- } : {}
20436
+ vaults: byTier(reachable).map((vault) => {
20437
+ const freshness = freshnessOf(vault);
20438
+ return {
20439
+ id: vault.id,
20440
+ name: vault.name,
20441
+ ownerKind: vault.ownerKind,
20442
+ source: vault.source,
20443
+ mode: vault.mode,
20444
+ itemCount: vault.itemCount,
20445
+ ...freshness ? { freshness } : {}
20446
+ };
20447
+ })
20448
+ } : {},
20449
+ complete,
20450
+ sources
19415
20451
  };
19416
20452
  function agentSearchResult(vault, row) {
19417
20453
  const summary = toVaultItemSummary(row, key());
@@ -19446,7 +20482,7 @@ function createVaultService(deps) {
19446
20482
  q: input.item,
19447
20483
  limit: VAULT_LIMITS3.searchResultsMax
19448
20484
  });
19449
- const existing = existingPage.items.find((item) => secretNameKey3(item.name) === secretNameKey3(input.item)) ?? null;
20485
+ const existing = existingPage.items.find((item) => secretNameKey4(item.name) === secretNameKey4(input.item)) ?? null;
19450
20486
  if (input.mode === "create" && existing) {
19451
20487
  throw new VaultError2(
19452
20488
  "vault_item_name_conflict",
@@ -19554,7 +20590,7 @@ function createVaultService(deps) {
19554
20590
  const current = await getVaultItem(deps.db, context, { itemId: target.itemId });
19555
20591
  if (current.vaultSource !== "opengeni") {
19556
20592
  const vault = await getVault(deps.db, context, { vaultId: current.vaultId });
19557
- await archiveProviderVaultItem(
20593
+ const { archiveKind } = await archiveProviderVaultItem(
19558
20594
  {
19559
20595
  db: deps.db,
19560
20596
  context,
@@ -19565,7 +20601,7 @@ function createVaultService(deps) {
19565
20601
  );
19566
20602
  return {
19567
20603
  archived: true,
19568
- restoreHint: `A person can restore ${current.name} from the provider's archive.`
20604
+ restoreHint: `A person can restore ${current.name} from the ${archiveKind === "trash" ? "trash" : "archive"} in ${vault.name}'s provider; it reappears here after the next sync.`
19569
20605
  };
19570
20606
  }
19571
20607
  const row = await archiveVaultItem(deps.db, context, {
@@ -20040,39 +21076,39 @@ import {
20040
21076
  AutomationSessionTemplate,
20041
21077
  OPENGENI_PR_REVIEW_SESSION_ROLE
20042
21078
  } from "@opengeni/contracts";
20043
- import { z as z2 } from "zod";
21079
+ import { z as z3 } from "zod";
20044
21080
  var PR_REVIEW_AUTOMATION_ADAPTER_ID = "source-control.pull-request.v1";
20045
- var PrReviewSourceConfiguration = z2.object({
20046
- provider: z2.enum(["github", "gitlab", "azure_devops"]),
20047
- providerBaseUrl: z2.string().url().max(2048),
20048
- registrationId: z2.string().uuid(),
20049
- webhookUsername: z2.string().min(1).max(512).nullable().default(null)
21081
+ var PrReviewSourceConfiguration = z3.object({
21082
+ provider: z3.enum(["github", "gitlab", "azure_devops"]),
21083
+ providerBaseUrl: z3.string().url().max(2048),
21084
+ registrationId: z3.string().uuid(),
21085
+ webhookUsername: z3.string().min(1).max(512).nullable().default(null)
20050
21086
  }).strict();
20051
- var PrReviewTriggerParameters = z2.object({
20052
- registrationId: z2.string().uuid(),
20053
- repositoryBindingId: z2.string().uuid(),
20054
- provider: z2.enum(["github", "gitlab", "azure_devops"]),
20055
- repositoryUri: z2.string().url().max(2048),
20056
- repositoryFullName: z2.string().min(1).max(1024),
20057
- providerRepositoryId: z2.string().min(1).max(512),
20058
- installationId: z2.string().min(1).max(512).nullable(),
20059
- projectId: z2.string().min(1).max(512).nullable(),
20060
- model: z2.string().min(1).max(512).nullable(),
20061
- additionalInstructions: z2.string().max(16384).nullable()
21087
+ var PrReviewTriggerParameters = z3.object({
21088
+ registrationId: z3.string().uuid(),
21089
+ repositoryBindingId: z3.string().uuid(),
21090
+ provider: z3.enum(["github", "gitlab", "azure_devops"]),
21091
+ repositoryUri: z3.string().url().max(2048),
21092
+ repositoryFullName: z3.string().min(1).max(1024),
21093
+ providerRepositoryId: z3.string().min(1).max(512),
21094
+ installationId: z3.string().min(1).max(512).nullable(),
21095
+ projectId: z3.string().min(1).max(512).nullable(),
21096
+ model: z3.string().min(1).max(512).nullable(),
21097
+ additionalInstructions: z3.string().max(16384).nullable()
20062
21098
  }).strict();
20063
- var PrReviewEventPayload = z2.object({
20064
- provider: z2.enum(["github", "gitlab", "azure_devops"]),
20065
- eventName: z2.string().max(200),
20066
- action: z2.string().max(200),
20067
- providerRepositoryId: z2.string().max(512).nullable(),
20068
- installationId: z2.string().max(512).nullable(),
20069
- projectId: z2.string().max(512).nullable(),
20070
- pullRequestId: z2.string().max(512).nullable(),
20071
- headSha: z2.string().regex(/^[0-9a-f]{40}$/).nullable(),
20072
- baseSha: z2.string().regex(/^[0-9a-f]{40}$/).nullable(),
20073
- headRef: z2.string().max(1024).nullable(),
20074
- baseRef: z2.string().max(1024).nullable(),
20075
- ignoredReason: z2.string().max(200).nullable()
21099
+ var PrReviewEventPayload = z3.object({
21100
+ provider: z3.enum(["github", "gitlab", "azure_devops"]),
21101
+ eventName: z3.string().max(200),
21102
+ action: z3.string().max(200),
21103
+ providerRepositoryId: z3.string().max(512).nullable(),
21104
+ installationId: z3.string().max(512).nullable(),
21105
+ projectId: z3.string().max(512).nullable(),
21106
+ pullRequestId: z3.string().max(512).nullable(),
21107
+ headSha: z3.string().regex(/^[0-9a-f]{40}$/).nullable(),
21108
+ baseSha: z3.string().regex(/^[0-9a-f]{40}$/).nullable(),
21109
+ headRef: z3.string().max(1024).nullable(),
21110
+ baseRef: z3.string().max(1024).nullable(),
21111
+ ignoredReason: z3.string().max(200).nullable()
20076
21112
  }).strict();
20077
21113
  var OPENGENI_PR_REVIEW_SKILL = {
20078
21114
  name: "pr-review",
@@ -21044,7 +22080,7 @@ import {
21044
22080
  saveAgentLearningSettings as saveAgentLearningSettings2,
21045
22081
  dbSql,
21046
22082
  deleteScheduledTask,
21047
- getConnectionMetadata as getConnectionMetadata5,
22083
+ getConnectionMetadata as getConnectionMetadata6,
21048
22084
  getEnrollment as getEnrollment3,
21049
22085
  getLiveEnrollmentConnection as getLiveEnrollmentConnection3,
21050
22086
  getKnowledgeSourceForSyncAuthority,
@@ -21070,7 +22106,7 @@ import {
21070
22106
  import { HTTPException as HTTPException32 } from "hono/http-exception";
21071
22107
  import { GOOGLE_DRIVE_PUBLICATION_SERVER_ID as GOOGLE_DRIVE_PUBLICATION_SERVER_ID3 } from "@opengeni/contracts/google-drive";
21072
22108
  import { PERSONAL_GITHUB_CONNECTION_SURFACE_ID as PERSONAL_GITHUB_CONNECTION_SURFACE_ID3 } from "@opengeni/contracts/personal-github";
21073
- import { isDeepStrictEqual as isDeepStrictEqual4 } from "util";
22109
+ import { isDeepStrictEqual as isDeepStrictEqual5 } from "util";
21074
22110
 
21075
22111
  // src/domain/scheduled-task-audience.ts
21076
22112
  import {
@@ -21185,7 +22221,7 @@ async function resolveScheduledTaskVisibility(input) {
21185
22221
 
21186
22222
  // src/domain/scheduled-task-account-audit.ts
21187
22223
  import { recordAuditEvent as recordAuditEvent2 } from "@opengeni/db";
21188
- import { isDeepStrictEqual as isDeepStrictEqual3 } from "util";
22224
+ import { isDeepStrictEqual as isDeepStrictEqual4 } from "util";
21189
22225
  var SCHEDULED_TASK_ACCOUNT_CHOICES_AUDIT_ACTION = "scheduled_task.account_choices_changed";
21190
22226
  function choicesOf(config) {
21191
22227
  return {
@@ -21205,7 +22241,7 @@ function scheduledTaskAccountChoiceChanges(before, after) {
21205
22241
  const next = choicesOf(after);
21206
22242
  const changes = {};
21207
22243
  for (const key of ["pinnedAccounts", "pinnedAccountsRequired", "connectors"]) {
21208
- if (!isDeepStrictEqual3(prior[key], next[key]))
22244
+ if (!isDeepStrictEqual4(prior[key], next[key]))
21209
22245
  changes[key] = { before: prior[key], after: next[key] };
21210
22246
  }
21211
22247
  return Object.keys(changes).length > 0 ? changes : null;
@@ -22193,7 +23229,7 @@ async function validatedScheduledTaskUpdateWithoutAudience(input) {
22193
23229
  throw new HTTPException32(422, { message: "Source ingestion uses an ordinary agent task" });
22194
23230
  }
22195
23231
  const knowledgeSource = input.payload.agentConfig?.knowledgeSource ?? scheduledTaskKnowledgeSource(input.existing);
22196
- if (knowledgeSource && requestedKnowledgeSource && !isDeepStrictEqual4(requestedKnowledgeSource, existingKnowledgeSource)) {
23232
+ if (knowledgeSource && requestedKnowledgeSource && !isDeepStrictEqual5(requestedKnowledgeSource, existingKnowledgeSource)) {
22197
23233
  await validateKnowledgeSourceSyncAction({
22198
23234
  db: input.db,
22199
23235
  grant: input.grant,
@@ -22330,7 +23366,7 @@ async function validatedScheduledTaskUpdateWithoutAudience(input) {
22330
23366
  const pins = nextRunMode === "existing_session" ? [] : input.payload.pinnedAccounts ?? input.existing.agentConfig.pinnedAccounts ?? [];
22331
23367
  const required = input.payload.pinnedAccountsRequired ?? input.existing.agentConfig.pinnedAccountsRequired === true;
22332
23368
  const next = withScheduledTaskPins(base, pins, required);
22333
- if (update.agentConfig || !isDeepStrictEqual4(next, input.existing.agentConfig))
23369
+ if (update.agentConfig || !isDeepStrictEqual5(next, input.existing.agentConfig))
22334
23370
  update.agentConfig = next;
22335
23371
  }
22336
23372
  const nextAgentConfig = {
@@ -22359,7 +23395,7 @@ async function validatedScheduledTaskUpdateWithoutAudience(input) {
22359
23395
  verifySlackChannel: input.verifySlackChannel
22360
23396
  });
22361
23397
  const authorityTargetChanged = nextRunMode !== input.existing.runMode || nextTargetSessionId !== input.existing.targetSessionId || input.payload.variableSetId !== void 0 && input.payload.variableSetId !== input.existing.variableSetId || input.payload.rigId !== void 0 && input.payload.rigId !== input.existing.rigId;
22362
- const materialExecutionChange = authorityTargetChanged || pinsProvided || !isDeepStrictEqual4(nextAgentConfig, input.existing.agentConfig) || input.payload.action !== void 0 && !isDeepStrictEqual4(input.payload.action, input.existing.action) || input.payload.schedule !== void 0 && !isDeepStrictEqual4(input.payload.schedule, input.existing.schedule) || input.payload.overlapPolicy !== void 0 && input.payload.overlapPolicy !== input.existing.overlapPolicy || input.payload.metadata !== void 0 && !isDeepStrictEqual4(input.payload.metadata, input.existing.metadata) || input.existing.status === "paused" && input.payload.status === "active";
23398
+ const materialExecutionChange = authorityTargetChanged || pinsProvided || !isDeepStrictEqual5(nextAgentConfig, input.existing.agentConfig) || input.payload.action !== void 0 && !isDeepStrictEqual5(input.payload.action, input.existing.action) || input.payload.schedule !== void 0 && !isDeepStrictEqual5(input.payload.schedule, input.existing.schedule) || input.payload.overlapPolicy !== void 0 && input.payload.overlapPolicy !== input.existing.overlapPolicy || input.payload.metadata !== void 0 && !isDeepStrictEqual5(input.payload.metadata, input.existing.metadata) || input.existing.status === "paused" && input.payload.status === "active";
22363
23399
  if (materialExecutionChange && nextRunMode !== "existing_session" && (!materializedReusable || nextAgentConfig.model !== input.existing.agentConfig.model)) {
22364
23400
  const beforeUpdateCommit = workspaceCustomModelCommitGuard({
22365
23401
  settings: input.settings,
@@ -22512,7 +23548,7 @@ async function validatedScheduledTaskUpdateWithoutAudience(input) {
22512
23548
  rigId: input.payload.rigId !== void 0 ? input.payload.rigId : input.existing.rigId,
22513
23549
  agentConfig: update.agentConfig ?? input.existing.agentConfig
22514
23550
  });
22515
- if (!knowledgeSource && !(materializedReusable && isDeepStrictEqual4(nextAgentConfig.machineTarget, input.existing.agentConfig.machineTarget) && nextAgentConfig.sandboxBackend === input.existing.agentConfig.sandboxBackend)) {
23551
+ if (!knowledgeSource && !(materializedReusable && isDeepStrictEqual5(nextAgentConfig.machineTarget, input.existing.agentConfig.machineTarget) && nextAgentConfig.sandboxBackend === input.existing.agentConfig.sandboxBackend)) {
22516
23552
  await validateScheduledTaskMachineTarget({
22517
23553
  settings: input.settings,
22518
23554
  db: input.db,
@@ -22590,7 +23626,7 @@ async function validateKnowledgeSourceSyncAction(input) {
22590
23626
  message: "knowledge source authority or generation changed"
22591
23627
  });
22592
23628
  }
22593
- const connection = await getConnectionMetadata5(
23629
+ const connection = await getConnectionMetadata6(
22594
23630
  input.db,
22595
23631
  input.grant.workspaceId,
22596
23632
  input.action.connection.connectionId,
@@ -22681,7 +23717,7 @@ async function requireUnchangedScheduledTaskForCompensation(db, expected) {
22681
23717
  );
22682
23718
  if (!current) throw new ScheduledTaskHeadChangedError();
22683
23719
  const { deletedAt, ...task } = current;
22684
- if (deletedAt !== null || !isDeepStrictEqual4(task, expected))
23720
+ if (deletedAt !== null || !isDeepStrictEqual5(task, expected))
22685
23721
  throw new ScheduledTaskHeadChangedError();
22686
23722
  }
22687
23723
  async function syncUpdatedScheduledTask(input) {
@@ -22748,7 +23784,7 @@ async function validateScheduledTaskAgentConfig(input) {
22748
23784
  },
22749
23785
  toolsProvided: true
22750
23786
  };
22751
- const unchangedCreationField = (key) => input.retainedCreationConfig !== void 0 && isDeepStrictEqual4(input.payload.agentConfig[key], input.retainedCreationConfig[key]);
23787
+ const unchangedCreationField = (key) => input.retainedCreationConfig !== void 0 && isDeepStrictEqual5(input.payload.agentConfig[key], input.retainedCreationConfig[key]);
22752
23788
  const actor = scheduledTaskInitiatorForGrant(input.grant).actor;
22753
23789
  const parent = actor && !existingChat ? await getSession8(input.db, input.workspaceId, actor.sessionId) : null;
22754
23790
  if (!existingChat && actor && (!parent || parent.accountId !== input.grant.accountId)) {
@@ -22871,7 +23907,7 @@ function validateIncidentTelemetryPreflightSelection(settings, agentConfig) {
22871
23907
  });
22872
23908
  }
22873
23909
  for (const required of preflight.requiredResources) {
22874
- if (!agentConfig.resources.some((selected) => isDeepStrictEqual4(selected, required))) {
23910
+ if (!agentConfig.resources.some((selected) => isDeepStrictEqual5(selected, required))) {
22875
23911
  throw new HTTPException32(422, {
22876
23912
  message: "incidentTelemetryPreflight.requiredResources must be exact selected resources"
22877
23913
  });
@@ -22971,7 +24007,7 @@ import {
22971
24007
  ScheduledTaskHeadChangedError as ScheduledTaskHeadChangedError2
22972
24008
  } from "@opengeni/db";
22973
24009
  import { HTTPException as HTTPException33 } from "hono/http-exception";
22974
- import { z as z3 } from "zod";
24010
+ import { z as z4 } from "zod";
22975
24011
  var ALWAYS_ATTACHED_CONNECTOR = "opengeni";
22976
24012
  var PRESERVED_ACCOUNT_SURFACES = /* @__PURE__ */ new Set([
22977
24013
  GOOGLE_DRIVE_PUBLICATION_SERVER_ID4,
@@ -22986,10 +24022,10 @@ function uniqueById(items) {
22986
24022
  return items.filter((item) => seen.has(item.id) ? false : (seen.add(item.id), true)).slice(0, SCHEDULED_TASK_ACCESS_CONNECTORS_MAX);
22987
24023
  }
22988
24024
  function planScheduledTaskConnectors(input) {
22989
- const unavailable = input.taskTools.filter(
24025
+ const unavailable2 = input.taskTools.filter(
22990
24026
  (tool) => tool.id !== ALWAYS_ATTACHED_CONNECTOR && !input.availableServerIds.has(tool.id)
22991
24027
  );
22992
- const unavailableIds = new Set(unavailable.map((tool) => tool.id));
24028
+ const unavailableIds = new Set(unavailable2.map((tool) => tool.id));
22993
24029
  const kept = input.taskTools.filter((tool) => !unavailableIds.has(tool.id));
22994
24030
  const keptIds = new Set(kept.map((tool) => tool.id));
22995
24031
  const missing = input.defaultTools.filter(
@@ -22998,7 +24034,7 @@ function planScheduledTaskConnectors(input) {
22998
24034
  return {
22999
24035
  tools: mergeToolRefs3([...kept], [...missing]),
23000
24036
  missing: uniqueById(missing.map((tool) => connector(tool.id, input.names))),
23001
- unavailable: uniqueById(unavailable.map((tool) => connector(tool.id, input.names)))
24037
+ unavailable: uniqueById(unavailable2.map((tool) => connector(tool.id, input.names)))
23002
24038
  };
23003
24039
  }
23004
24040
  function planScheduledTaskPinnedAccounts(input) {
@@ -23009,7 +24045,7 @@ function planScheduledTaskPinnedAccounts(input) {
23009
24045
  )
23010
24046
  );
23011
24047
  const kept = [];
23012
- const unavailable = [];
24048
+ const unavailable2 = [];
23013
24049
  const dropped = [];
23014
24050
  for (const pin of input.pins) {
23015
24051
  if (PRESERVED_ACCOUNT_SURFACES.has(pin.serverId)) {
@@ -23022,11 +24058,11 @@ function planScheduledTaskPinnedAccounts(input) {
23022
24058
  }
23023
24059
  kept.push(pin);
23024
24060
  if (!available.has(JSON.stringify([pin.serverId, pin.connectionId])))
23025
- unavailable.push(connector(pin.serverId, input.names));
24061
+ unavailable2.push(connector(pin.serverId, input.names));
23026
24062
  }
23027
24063
  return {
23028
24064
  kept,
23029
- unavailable: uniqueById(unavailable),
24065
+ unavailable: uniqueById(unavailable2),
23030
24066
  dropped: uniqueById(dropped)
23031
24067
  };
23032
24068
  }
@@ -23081,13 +24117,13 @@ function planScheduledTaskOpenGeniTools(input) {
23081
24117
  policy: unchanged ? null : { firstPartyMcpTools: tools, firstPartyMcpPermissions: permissions }
23082
24118
  };
23083
24119
  }
23084
- var AuthNeededFact = z3.object({
23085
- serverId: z3.string().min(1),
23086
- canonicalServerId: z3.string().min(1).optional(),
23087
- providerDomain: z3.string().min(1),
24120
+ var AuthNeededFact = z4.object({
24121
+ serverId: z4.string().min(1),
24122
+ canonicalServerId: z4.string().min(1).optional(),
24123
+ providerDomain: z4.string().min(1),
23088
24124
  reason: ToolAuthNeededReason,
23089
- capability: z3.unknown().optional(),
23090
- setupRequest: z3.unknown().optional()
24125
+ capability: z4.unknown().optional(),
24126
+ setupRequest: z4.unknown().optional()
23091
24127
  });
23092
24128
  function scheduledTaskRunAccessFailures(events, names) {
23093
24129
  const byKey = /* @__PURE__ */ new Map();
@@ -27860,7 +28896,7 @@ import { createPinnedIntegrationTransport as createPinnedIntegrationTransport2 }
27860
28896
  import { mcpToolRecommendsApproval } from "@opengeni/contracts";
27861
28897
  import {
27862
28898
  buildConnectionTokenResolver,
27863
- getConnectionMetadata as getConnectionMetadata6,
28899
+ getConnectionMetadata as getConnectionMetadata7,
27864
28900
  listEnabledMcpCapabilityServers as listEnabledMcpCapabilityServers3,
27865
28901
  listConnectorToolPermissionPolicies,
27866
28902
  listChangedConnectorToolPermissions,
@@ -27913,7 +28949,7 @@ async function resolveTarget2(input) {
27913
28949
  const server2 = settingsWithApiIntegrationServers({ ...input.settings, mcpServers: [] }, [
27914
28950
  integration
27915
28951
  ]).mcpServers[0];
27916
- const connection2 = integration.connectionRef?.connectionId ? await getConnectionMetadata6(
28952
+ const connection2 = integration.connectionRef?.connectionId ? await getConnectionMetadata7(
27917
28953
  input.db,
27918
28954
  input.workspaceId,
27919
28955
  integration.connectionRef.connectionId,
@@ -28269,6 +29305,7 @@ export {
28269
29305
  EDITABLE_ARTIFACT_OPERATION_PROTOCOL_VERSION,
28270
29306
  EDITABLE_ARTIFACT_ORIGINAL_IMPORT_MAX_BYTES,
28271
29307
  EXTERNAL_FIRST_USE_MEMBER_PERMISSIONS,
29308
+ EXTERNAL_VAULT_CREDENTIAL_ROLE,
28272
29309
  EditableArtifactAgentApplication,
28273
29310
  EditableArtifactApplication,
28274
29311
  EditableArtifactCausalChainError,
@@ -28309,6 +29346,7 @@ export {
28309
29346
  KnowledgeVectorQueryRejectedError,
28310
29347
  LINKED_VAULT_MISS_MAX_AGE_MS,
28311
29348
  LINKED_VAULT_OPEN_WAIT_MS,
29349
+ LINKED_VAULT_REFRESH_TIMEOUT_MS,
28312
29350
  LINKED_VAULT_REFRESH_WAIT_MS,
28313
29351
  LINKED_VAULT_SEARCH_MAX_AGE_MS,
28314
29352
  MAX_CHECKS_PER_RIG,
@@ -28378,6 +29416,7 @@ export {
28378
29416
  SessionTenancyManagedHumanRequiredError,
28379
29417
  SessionTenancyPersistenceOutcomeUnknownError,
28380
29418
  SessionUsageLimitHttpError,
29419
+ StoredVaultProviderCredentials,
28381
29420
  SystemEditableArtifactClock,
28382
29421
  SystemEditableArtifactLiveClock,
28383
29422
  SystemEditableArtifactLiveScheduler,
@@ -28413,6 +29452,7 @@ export {
28413
29452
  agentAccessPermitsCrossTreeAccess,
28414
29453
  agentConfigParentFor,
28415
29454
  agentRunAdmissionDenial,
29455
+ aggregateProviderFreshness,
28416
29456
  apiIntegrationsMatchingDelegations,
28417
29457
  appendArchivedSessionEventsForRequest,
28418
29458
  appendRigSetupCommand,
@@ -28455,6 +29495,7 @@ export {
28455
29495
  automationRequestDigest,
28456
29496
  availableMcpAccountBindings,
28457
29497
  availableToolRefs,
29498
+ bindVaultProviderAuthority,
28458
29499
  buildApiIntegrationMcpServers,
28459
29500
  buildAutomationAcceptedExecution,
28460
29501
  buildCapabilityCatalog,
@@ -28499,8 +29540,10 @@ export {
28499
29540
  createCompanyProfileAgentAdminRouter,
28500
29541
  createCompanyProfileDurableLearningAdapter,
28501
29542
  createGitHubSkillSourceClient,
29543
+ createPinnedRemoteVaultTransport,
28502
29544
  createPublicSkillSearchClient,
28503
29545
  createRealtimeVoiceBilling,
29546
+ createRemoteExternalVaultFactories,
28504
29547
  createRigForApi,
28505
29548
  createRigVersionForApi,
28506
29549
  createRunOnSecretResolver,
@@ -28566,6 +29609,7 @@ export {
28566
29609
  editableArtifactTransactionId,
28567
29610
  effectiveFirstPartyMcpToolCeiling,
28568
29611
  effectiveOrganizationPrivacy,
29612
+ effectiveProviderOperations,
28569
29613
  enableCapability,
28570
29614
  enabledCapabilityMcpToolRefs,
28571
29615
  encodeEditableArtifactBrokerHint,
@@ -28581,6 +29625,10 @@ export {
28581
29625
  externalActorContinuationForAuthorization,
28582
29626
  externalAttributionForAuthorization,
28583
29627
  externalContinuationCommitAuthorizer,
29628
+ externalVaultConnectionDetails,
29629
+ externalVaultConnectionMetadata,
29630
+ externalVaultProviderDomain,
29631
+ externalVaultProviderForConnection,
28584
29632
  fikenConnectionMetadata,
28585
29633
  fileOwnerContextForAccess,
28586
29634
  fileOwnerContextForAgent,
@@ -28637,6 +29685,7 @@ export {
28637
29685
  installSkill,
28638
29686
  integrationKeyForConnectProvider,
28639
29687
  integrationSourceForOrganizationPolicy,
29688
+ intersectProviderOperations,
28640
29689
  isAcceptedMimeType,
28641
29690
  isAuthenticatedPersonAuthorization,
28642
29691
  isAuthoritativeGitHubRepositorySelectionError,
@@ -28644,6 +29693,7 @@ export {
28644
29693
  isDeveloperSetupAuthorization,
28645
29694
  isDeveloperSetupDelegatedPermissionAllowed,
28646
29695
  isDeveloperSetupGrant,
29696
+ isExternalVaultConnection,
28647
29697
  isFikenConnection,
28648
29698
  isOnePasswordConnection,
28649
29699
  isOpenGeniSlackBotConnection,
@@ -28651,7 +29701,7 @@ export {
28651
29701
  isTerminalVideoGenerationState,
28652
29702
  isTrustedScheduledSlackBotSession,
28653
29703
  isUserMember,
28654
- isVaultError,
29704
+ isVaultError3 as isVaultError,
28655
29705
  isVerifiedDelegatedHumanAuthorization,
28656
29706
  isVerifiedOrganizationServiceAuthorization,
28657
29707
  isWorkspaceCustomModelId,
@@ -28677,6 +29727,7 @@ export {
28677
29727
  listSkills,
28678
29728
  loadCoreRoutedClaudeCatalogReadiness,
28679
29729
  loadCoreRoutedXaiCatalogReadiness,
29730
+ loadExternalVaultConnection,
28680
29731
  loadOnePasswordConnectionToken,
28681
29732
  loadWorkspaceClaudeSubscriptionReadiness,
28682
29733
  loadWorkspaceCodexCatalogReadiness,
@@ -28731,6 +29782,7 @@ export {
28731
29782
  parseSkillSource,
28732
29783
  pausePrivateScheduledTaskAsAdmin,
28733
29784
  pauseScheduledTaskAsAdmin,
29785
+ pendingVaultCredentialBundle,
28734
29786
  personalAtlassianDelegationsFromVisibleConnections,
28735
29787
  personalConnectionDelegationForServer,
28736
29788
  personalConnectionDelegationSourceForGrant,
@@ -28771,10 +29823,12 @@ export {
28771
29823
  promoteSetupAppendChange,
28772
29824
  promoteVerifiedDefinitionEditChangeForApi,
28773
29825
  proposeRigChangeForApi,
29826
+ providerOperationsForMode,
28774
29827
  providerPrimaryFieldId,
28775
29828
  providerSecretName,
28776
29829
  providerSettingsForSessionSandboxRuntime,
28777
29830
  providerSyncFailure,
29831
+ providerVaultFreshness,
28778
29832
  provisionSandbox,
28779
29833
  publishGovernedLearningEventToSlack,
28780
29834
  readSessionAttachmentFiles,
@@ -28790,6 +29844,7 @@ export {
28790
29844
  recordUserMessageAccepted,
28791
29845
  recordWorkspaceUsage,
28792
29846
  refreshCoreCodexModelEntitlements,
29847
+ refreshLinkedVaults,
28793
29848
  refreshScheduledTaskAccess,
28794
29849
  refreshStaleLinkedVaults,
28795
29850
  registerAutomationAdapter,
@@ -28972,6 +30027,7 @@ export {
28972
30027
  statusForVoiceInputError,
28973
30028
  steerAgentSession,
28974
30029
  steerHumanQueuePrompt,
30030
+ storedProviderOperations,
28975
30031
  submitComposerDraftForRequest,
28976
30032
  suggestCapabilityCatalogItems,
28977
30033
  swapActiveSandbox,
@@ -29028,6 +30084,7 @@ export {
29028
30084
  vaultErrorCodeForProvider,
29029
30085
  vaultErrorFromDatabase,
29030
30086
  vaultItemReferences,
30087
+ vaultRefreshScopeKey,
29031
30088
  verifiedDelegatedHumanAuthorizationForRequest,
29032
30089
  verifyDirectModelAccess,
29033
30090
  verifyPrReviewWebhook,