@opengeni/worker-bundle 0.29.0-canary.0 → 0.29.0-canary.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.
Files changed (49) hide show
  1. package/dist/activities/agent-turn/knowledge-source-tools.d.ts +9 -0
  2. package/dist/activities/agent-turn/knowledge-sources.d.ts +20 -0
  3. package/dist/activities/agent-turn/stream-attempt.d.ts +1 -0
  4. package/dist/activities/agent-turn/tool-environment.d.ts +1 -0
  5. package/dist/activities/knowledge-indexing.d.ts +10 -0
  6. package/dist/activities/run-input.d.ts +1 -0
  7. package/dist/activities/types.d.ts +10 -3
  8. package/dist/{activities-control-MKGEOBXT.js → activities-control-HMFGSKC5.js} +331 -3381
  9. package/dist/activities-control-HMFGSKC5.js.map +1 -0
  10. package/dist/activities-control.d.ts +12 -0
  11. package/dist/{activities-turn-5MZZ6HQA.js → activities-turn-AP67JADS.js} +1296 -1021
  12. package/dist/activities-turn-AP67JADS.js.map +1 -0
  13. package/dist/activities.d.ts +12 -0
  14. package/dist/{chunk-OA5CSY5N.js → chunk-CYITU5UH.js} +3348 -174
  15. package/dist/chunk-CYITU5UH.js.map +1 -0
  16. package/dist/index.d.ts +13 -0
  17. package/dist/index.js +48 -1
  18. package/dist/index.js.map +1 -1
  19. package/dist/workflow-bundle.js +65 -35
  20. package/dist/workflows/knowledge-indexing.d.ts +2 -0
  21. package/dist/workflows.d.ts +1 -0
  22. package/package.json +19 -18
  23. package/src/activities/agent-turn/knowledge-source-tools.ts +98 -0
  24. package/src/activities/agent-turn/knowledge-sources.ts +86 -0
  25. package/src/activities/agent-turn/run.ts +1244 -1112
  26. package/src/activities/agent-turn/skill-install.ts +1 -1
  27. package/src/activities/agent-turn/skill-save.ts +1 -1
  28. package/src/activities/agent-turn/skill-selection.ts +2 -0
  29. package/src/activities/agent-turn/skill-tools.ts +5 -12
  30. package/src/activities/agent-turn/stream-attempt.ts +3 -0
  31. package/src/activities/agent-turn/tool-environment.ts +24 -0
  32. package/src/activities/codex-image-generation.ts +1 -2
  33. package/src/activities/documents.ts +17 -52
  34. package/src/activities/knowledge-indexing.ts +130 -0
  35. package/src/activities/knowledge-source-sync.ts +120 -16
  36. package/src/activities/mcp-credentials.ts +2 -0
  37. package/src/activities/run-input.ts +2 -0
  38. package/src/activities/scheduled-tasks.ts +25 -62
  39. package/src/activities/types.ts +8 -2
  40. package/src/activities/video-generation-reconciliation.ts +8 -1
  41. package/src/activities-control.ts +2 -0
  42. package/src/activities.ts +1 -0
  43. package/src/index.ts +49 -0
  44. package/src/pr-review-credentials.ts +2 -2
  45. package/src/workflows/knowledge-indexing.ts +12 -0
  46. package/src/workflows.ts +1 -0
  47. package/dist/activities-control-MKGEOBXT.js.map +0 -1
  48. package/dist/activities-turn-5MZZ6HQA.js.map +0 -1
  49. package/dist/chunk-OA5CSY5N.js.map +0 -1
@@ -246,7 +246,7 @@ function exactRequestedRepositories(refs, credentialBindingId, snapshots) {
246
246
  // src/pr-review-credentials.ts
247
247
  import { environmentsEncryptionKeyBytes } from "@opengeni/config";
248
248
  import { prReviewRegistrationIdFromCredentialBinding } from "@opengeni/core";
249
- import { createRemoteMcpCredentialsPort } from "@opengeni/core/remote-mcp-credentials";
249
+ import { createNativeRemoteMcpCredentialsPort } from "@opengeni/core/remote-mcp-credentials";
250
250
  import { decryptVariableSetValue, resolvePrReviewGitCredential } from "@opengeni/db";
251
251
  import {
252
252
  createGitHubAppInstallationTokenWithExpiry,
@@ -254,7 +254,7 @@ import {
254
254
  } from "@opengeni/github";
255
255
  function createStandaloneConnectionCredentialsPort(settings, db) {
256
256
  return {
257
- ...createRemoteMcpCredentialsPort(settings),
257
+ ...createNativeRemoteMcpCredentialsPort(settings, db),
258
258
  gitCredentials: async (request) => await resolveStandaloneGitCredentials(settings, db, request)
259
259
  };
260
260
  }
@@ -1115,6 +1115,82 @@ function createCodexCapacityActivities(services) {
1115
1115
  return { getCodexCapacityWait, reconcileCodexCapacityWait };
1116
1116
  }
1117
1117
 
1118
+ // src/activities/documents.ts
1119
+ import {
1120
+ resolveDocumentIndexAuthority,
1121
+ rlsContextForWorkspace,
1122
+ withSessionRlsActorContext,
1123
+ withWorkspaceUsageLock
1124
+ } from "@opengeni/db";
1125
+ function createDocumentActivities(services, resolveDocumentServices) {
1126
+ return {
1127
+ indexDocument: async (input) => {
1128
+ const { db, objectStorage } = await services();
1129
+ if (!objectStorage) {
1130
+ throw new Error("object storage is not configured");
1131
+ }
1132
+ if (!resolveDocumentServices) {
1133
+ throw new Error("document services are not configured");
1134
+ }
1135
+ const [{ getDocumentForIndexing, indexDocumentNow }, documentServices] = await Promise.all([
1136
+ import("@opengeni/documents"),
1137
+ resolveDocumentServices()
1138
+ ]);
1139
+ const context = await rlsContextForWorkspace(db, input.workspaceId);
1140
+ if (context.accountId !== input.accountId) {
1141
+ throw new Error("document account/workspace authority mismatch");
1142
+ }
1143
+ return await withWorkspaceUsageLock(db, input.workspaceId, async (lockedDb) => {
1144
+ const storedAuthority = await resolveDocumentIndexAuthority(lockedDb, input);
1145
+ if (!storedAuthority) {
1146
+ throw new Error("document authority was not found before indexing");
1147
+ }
1148
+ const suppliedAuthorityFields = [
1149
+ "authorityKind",
1150
+ "authorityWorkspaceId",
1151
+ "authoritySubjectId"
1152
+ ];
1153
+ const suppliedCount = suppliedAuthorityFields.filter(
1154
+ (field) => Object.prototype.hasOwnProperty.call(input, field)
1155
+ ).length;
1156
+ const suppliedMismatch = Object.prototype.hasOwnProperty.call(input, "authorityKind") && input.authorityKind !== storedAuthority.authorityKind || Object.prototype.hasOwnProperty.call(input, "authorityWorkspaceId") && input.authorityWorkspaceId !== storedAuthority.authorityWorkspaceId || Object.prototype.hasOwnProperty.call(input, "authoritySubjectId") && input.authoritySubjectId !== storedAuthority.authoritySubjectId;
1157
+ if (suppliedMismatch) {
1158
+ throw new Error("document authority changed before indexing");
1159
+ }
1160
+ if (suppliedCount !== 0 && suppliedCount !== suppliedAuthorityFields.length) {
1161
+ throw new Error("document authority tuple is partial");
1162
+ }
1163
+ const claimedDocument = await getDocumentForIndexing(
1164
+ lockedDb,
1165
+ input.workspaceId,
1166
+ input.documentId
1167
+ );
1168
+ if (!claimedDocument || claimedDocument.authorityKind !== storedAuthority.authorityKind || claimedDocument.authorityWorkspaceId !== storedAuthority.authorityWorkspaceId || claimedDocument.authoritySubjectId !== storedAuthority.authoritySubjectId) {
1169
+ throw new Error("document authority changed before indexing");
1170
+ }
1171
+ const document = await withSessionRlsActorContext(
1172
+ {
1173
+ subjectId: "service:document-preparation",
1174
+ privateFileOwnerSubjectId: storedAuthority.authorityKind === "personal" ? storedAuthority.authoritySubjectId : null
1175
+ },
1176
+ () => indexDocumentNow(
1177
+ lockedDb,
1178
+ objectStorage,
1179
+ input.workspaceId,
1180
+ input.documentId,
1181
+ documentServices,
1182
+ { viewerSubjectId: storedAuthority.authoritySubjectId }
1183
+ )
1184
+ );
1185
+ if (document.authorityKind !== storedAuthority.authorityKind || document.authorityWorkspaceId !== storedAuthority.authorityWorkspaceId || document.authoritySubjectId !== storedAuthority.authoritySubjectId) {
1186
+ throw new Error("document authority changed before indexing");
1187
+ }
1188
+ return document;
1189
+ });
1190
+ }
1191
+ };
1192
+ }
1193
+
1118
1194
  // src/activities/goals.ts
1119
1195
  import {
1120
1196
  allowedFirstPartyMcpToolsForSession,
@@ -1226,187 +1302,3282 @@ function createGoalActivities(services) {
1226
1302
  latencyMode: continuationLatencyMode,
1227
1303
  latencyModeSource: "continuation"
1228
1304
  });
1229
- const decision = await materializeGoalContinuation(db, {
1230
- accountId: input.accountId,
1231
- workspaceId: input.workspaceId,
1232
- sessionId: input.sessionId,
1233
- workflowId: input.workflowId,
1234
- defaultMaxAutoContinuations: settings.goalMaxAutoContinuations ?? null,
1235
- // Pacing between consecutive no-input continuations (never a cap). The
1236
- // materializer re-arms a delayed outbox wake and returns `deferred`.
1237
- idleBackoff: {
1238
- scheduleMs: settings.goalIdleBackoffMs,
1239
- maxMs: settings.goalIdleBackoffMaxMs
1240
- },
1241
- // A model-policy block takes precedence: it is deterministic (a budget
1242
- // pause can clear on its own; a policy pause needs a model/policy change)
1243
- // and rides the same visible-pause channel.
1244
- budgetBlocked: modelPolicyBlocked ?? budgetBlocked,
1245
- policy: {
1246
- model: continuationModel,
1247
- reasoningEffort: continuationReasoningEffort,
1248
- latencyMode: continuationLatencyMode,
1249
- turnExecutionPolicy,
1250
- tools: withFirstPartyTools(settings, session.tools),
1251
- sandboxBackend: session.sandboxBackend
1252
- },
1253
- // Long-wait guidance is only given when `wait_for_input` is actually in this
1254
- // session's effective first-party selection (the same source the worker
1255
- // signs into the delegated token and the API uses to register tools), so
1256
- // a pre-existing narrowed selection is never told to call a missing tool.
1257
- prompt: (goal, autoContinuation, cap) => {
1258
- const effectiveFirstPartyTools = allowedFirstPartyMcpToolsForSession(
1259
- settings,
1260
- session.firstPartyMcpTools
1261
- );
1262
- return goalContinuationPrompt(goal, autoContinuation, cap, {
1263
- inputWaitAvailable: effectiveFirstPartyTools.includes("wait_for_input"),
1264
- humanInputRespondAvailable: effectiveFirstPartyTools.includes(
1265
- "session_human_input_respond"
1266
- )
1267
- });
1305
+ const decision = await materializeGoalContinuation(db, {
1306
+ accountId: input.accountId,
1307
+ workspaceId: input.workspaceId,
1308
+ sessionId: input.sessionId,
1309
+ workflowId: input.workflowId,
1310
+ defaultMaxAutoContinuations: settings.goalMaxAutoContinuations ?? null,
1311
+ // Pacing between consecutive no-input continuations (never a cap). The
1312
+ // materializer re-arms a delayed outbox wake and returns `deferred`.
1313
+ idleBackoff: {
1314
+ scheduleMs: settings.goalIdleBackoffMs,
1315
+ maxMs: settings.goalIdleBackoffMaxMs
1316
+ },
1317
+ // A model-policy block takes precedence: it is deterministic (a budget
1318
+ // pause can clear on its own; a policy pause needs a model/policy change)
1319
+ // and rides the same visible-pause channel.
1320
+ budgetBlocked: modelPolicyBlocked ?? budgetBlocked,
1321
+ policy: {
1322
+ model: continuationModel,
1323
+ reasoningEffort: continuationReasoningEffort,
1324
+ latencyMode: continuationLatencyMode,
1325
+ turnExecutionPolicy,
1326
+ tools: withFirstPartyTools(settings, session.tools),
1327
+ sandboxBackend: session.sandboxBackend
1328
+ },
1329
+ // Long-wait guidance is only given when `wait_for_input` is actually in this
1330
+ // session's effective first-party selection (the same source the worker
1331
+ // signs into the delegated token and the API uses to register tools), so
1332
+ // a pre-existing narrowed selection is never told to call a missing tool.
1333
+ prompt: (goal, autoContinuation, cap) => {
1334
+ const effectiveFirstPartyTools = allowedFirstPartyMcpToolsForSession(
1335
+ settings,
1336
+ session.firstPartyMcpTools
1337
+ );
1338
+ return goalContinuationPrompt(goal, autoContinuation, cap, {
1339
+ inputWaitAvailable: effectiveFirstPartyTools.includes("wait_for_input"),
1340
+ humanInputRespondAvailable: effectiveFirstPartyTools.includes(
1341
+ "session_human_input_respond"
1342
+ )
1343
+ });
1344
+ }
1345
+ });
1346
+ if (decision.events.length > 0) {
1347
+ await bus.publish(input.workspaceId, input.sessionId, decision.events);
1348
+ }
1349
+ return { action: decision.action };
1350
+ }
1351
+ return {
1352
+ enqueueGoalRetryWake,
1353
+ maybeContinueGoal
1354
+ };
1355
+ }
1356
+ function goalContinuationModelDecision(input) {
1357
+ const catalogSettings = input.settings.supergrokSubscriptionEnabled ? withXaiSubscriptionCatalogProvider(
1358
+ input.settings.codexSubscriptionEnabled ? withCodexCatalogProvider(input.settings) : input.settings
1359
+ ) : input.settings.codexSubscriptionEnabled ? withCodexCatalogProvider(input.settings) : input.settings;
1360
+ const policyBlocks = (modelId) => input.workspaceModelPolicy !== null && !evaluateWorkspaceModelPolicy(input.workspaceModelPolicy, {
1361
+ providerId: policyProviderIdForModel(catalogSettings, modelId),
1362
+ modelId
1363
+ }).allowed;
1364
+ if (resolveModelProvider(catalogSettings, input.inheritedModel) && !policyBlocks(input.inheritedModel)) {
1365
+ return { model: input.inheritedModel, blocked: null };
1366
+ }
1367
+ if (!resolveModelProvider(catalogSettings, input.inheritedModel)) {
1368
+ return {
1369
+ model: input.inheritedModel,
1370
+ blocked: `model "${input.inheritedModel}" is no longer in the deployment or workspace catalog; choose an available model before resuming the goal`
1371
+ };
1372
+ }
1373
+ return {
1374
+ model: input.inheritedModel,
1375
+ blocked: `workspace model policy blocks model "${input.inheritedModel}"; pick an allowed model or change the workspace model policy`
1376
+ };
1377
+ }
1378
+ function goalContinuationFundedWithoutCredits(settings, model, codexBilled) {
1379
+ return modelFundingForAdmission(settings, model, codexBilled).fundedWithoutCredits;
1380
+ }
1381
+ function goalContinuationPrompt(_goal, _autoContinuation, _cap, options = {}) {
1382
+ const waitingGuidance = options.inputWaitAvailable ? [
1383
+ "Waiting on child sessions, background commands, or external events:",
1384
+ "- When further progress depends on work already in flight, do not sleep, loop, or poll session or command state repeatedly.",
1385
+ "- Re-check once; if the work is still in flight and the wait is long or uncertain, call opengeni__wait_for_input with a concrete reason and timeoutSeconds, then end your turn immediately. Relevant session input or the safety deadline will start a new turn, and this goal stays active.",
1386
+ "- If the immediately preceding user-facing update already reported this same unchanged wait, do not restate it or produce another equivalent final answer. Call opengeni__wait_for_input and end the turn. Report only material new state or a newly discovered blocker.",
1387
+ "- If you are blocked on a human decision, use opengeni__goal_pause under the blocked audit below instead.",
1388
+ ""
1389
+ ] : [];
1390
+ const childNoticeGuidance = [
1391
+ "Child lifecycle notices:",
1392
+ "- A `child_requires_action` update means a worker you spawned is blocked on a question or a tool approval and will not progress until it is answered. " + (options.humanInputRespondAvailable ? "If you know the answer to its question, answer it with opengeni__session_human_input_respond (pass the worker's sessionId, the requestId from the notice, and a response). " : "") + "Tool approvals can only be decided by a human. If you cannot resolve the blocker yourself, report the exact blocker (worker session id, the question) to the user and wait or pause instead of retrying the worker.",
1393
+ "- `child_requires_action_resolved`, `child_paused`, `child_waiting_capacity`, and `child_progress` updates are informational: a resolved notice means the worker is moving again; a paused worker needs a human or you to resume it; a capacity wait resumes by itself; a progress note needs no action.",
1394
+ ""
1395
+ ];
1396
+ return [
1397
+ "Reconcile the active session goal against authoritative current state, then carry the work through to the full requested end state and verify it.",
1398
+ "",
1399
+ "Goal recovery:",
1400
+ "- Treat this continuation as re-entry into the full objective, not as a request to perform one step and stop.",
1401
+ "- Do not rely on previous assistant claims of progress or completion; use them only to locate authoritative evidence.",
1402
+ "- If authoritative evidence already proves the full objective, call opengeni__goal_complete instead of manufacturing more work.",
1403
+ "- Before repeating a state-setting action, verify whether its desired state already holds. If it does, do not repeat it; continue reconciling the overall goal.",
1404
+ "",
1405
+ "Continuation behavior:",
1406
+ "- This goal persists across turns. A runtime boundary can end one turn without shrinking the objective; the next continuation resumes the same full objective.",
1407
+ "- Keep working until the requested end state is true and verified. Do not end the turn merely because one useful action completed, and do not redefine success around a smaller or easier task.",
1408
+ "- An incomplete-status report is not a substitute for continuing the work. If the remaining problem can be investigated or addressed within your current authority, continue that work in this turn rather than returning another equivalent status-only final.",
1409
+ "- Distinguish unfinished work from a blocker that actually requires human input or external change; use the existing waiting and blocked audits only when their conditions hold.",
1410
+ "- Temporary rough edges are acceptable while the work is moving in the right direction. Completion still requires the requested end state to be true and verified.",
1411
+ "",
1412
+ "Work from evidence:",
1413
+ "Use the current workspace and external state as authoritative. Conversation context can help locate relevant work, but it is not proof of the current state. Inspect the current state before relying on it. Improve, replace, or remove existing work as needed to satisfy the actual objective.",
1414
+ "",
1415
+ "Progress visibility:",
1416
+ "If a planning tool is available and the next work is meaningfully multi-step, use it to show a concise plan tied to the real objective. Keep the plan current as steps complete or the next best action changes. Skip planning overhead for trivial one-step progress, and do not treat a plan update as a substitute for doing the work.",
1417
+ "",
1418
+ "Fidelity:",
1419
+ "- Optimize each turn for movement toward the requested end state, not for the smallest stable-looking subset or easiest passing change.",
1420
+ "- Do not substitute a narrower, safer, smaller, merely compatible, or easier-to-test solution because it is more likely to pass current tests.",
1421
+ "- Treat alignment as movement toward the requested end state. An edit is aligned only if it makes the requested final state more true; useful-looking behavior that preserves a different end state is misaligned.",
1422
+ "",
1423
+ "Completion audit:",
1424
+ "Before deciding that the goal is achieved, treat completion as unproven and verify it against the actual current state:",
1425
+ "- Derive concrete requirements from the objective and any referenced files, plans, specifications, issues, or user instructions.",
1426
+ "- Preserve the original scope; do not redefine success around the work that already exists.",
1427
+ "- For every explicit requirement, named artifact, command, test, gate, invariant, and deliverable, identify and inspect the authoritative evidence that would prove it.",
1428
+ "- Match verification scope to requirement scope. Treat uncertain, indirect, incomplete, or missing evidence as not achieved and continue working.",
1429
+ "- The audit must prove completion, not merely fail to find obvious remaining work.",
1430
+ "",
1431
+ "Do not rely on intent, partial progress, memory of earlier work, or a plausible final answer as proof of completion. Call opengeni__goal_complete with concrete evidence only when the full objective is actually achieved and no required work remains.",
1432
+ "",
1433
+ ...waitingGuidance,
1434
+ ...childNoticeGuidance,
1435
+ "Blocked audit:",
1436
+ "- Do not call opengeni__goal_pause the first time a blocker appears.",
1437
+ "- Pause only when the same blocking condition has repeated for at least three consecutive goal turns and meaningful progress is impossible without user input or an external-state change.",
1438
+ "- Do not pause merely because the work is hard, slow, uncertain, incomplete, or would benefit from clarification.",
1439
+ "- Once that threshold is satisfied, call opengeni__goal_pause with the concrete blocker instead of repeatedly reporting it while leaving the goal active.",
1440
+ "",
1441
+ "Do not call opengeni__goal_complete or opengeni__goal_pause unless the corresponding audit above is satisfied."
1442
+ ].join("\n");
1443
+ }
1444
+ function withFirstPartyTools(settings, tools) {
1445
+ if (!settings.mcpServers.some((server) => server.id === "opengeni")) {
1446
+ return tools;
1447
+ }
1448
+ return mergeToolRefs(tools, [{ kind: "mcp", id: "opengeni" }]);
1449
+ }
1450
+ async function goalRunBudgetBlocked(settings, db, accountId, workspaceId, fundedWithoutCredits) {
1451
+ if (!fundedWithoutCredits && (settings.billingMode === "stripe" || settings.usageLimitsMode === "managed")) {
1452
+ const balance = await getBillingBalance(db, accountId);
1453
+ if (balance.balanceMicros <= 0) {
1454
+ return "insufficient OpenGeni credits";
1455
+ }
1456
+ }
1457
+ if (settings.usageLimitsMode === "static" || settings.usageLimitsMode === "managed") {
1458
+ const limits = configuredStaticUsageLimits(settings);
1459
+ if (!fundedWithoutCredits && limits.maxMonthlyCostMicrosPerAccount) {
1460
+ const used = await sumUsageQuantity(db, {
1461
+ accountId,
1462
+ eventType: "model.cost",
1463
+ since: startOfUtcMonth()
1464
+ });
1465
+ if (used >= limits.maxMonthlyCostMicrosPerAccount) {
1466
+ return `monthly model cost limit reached (${limits.maxMonthlyCostMicrosPerAccount} micros)`;
1467
+ }
1468
+ }
1469
+ if (limits.maxMonthlyAgentRunsPerWorkspace) {
1470
+ const used = await sumUsageQuantity(db, {
1471
+ workspaceId,
1472
+ eventType: "agent_run.created",
1473
+ since: startOfUtcMonth()
1474
+ });
1475
+ if (used + 1 > limits.maxMonthlyAgentRunsPerWorkspace) {
1476
+ return `monthly agent run limit reached (${limits.maxMonthlyAgentRunsPerWorkspace})`;
1477
+ }
1478
+ }
1479
+ }
1480
+ return null;
1481
+ }
1482
+ function startOfUtcMonth() {
1483
+ const now = /* @__PURE__ */ new Date();
1484
+ return new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), 1));
1485
+ }
1486
+
1487
+ // src/activities/knowledge-source-sync.ts
1488
+ import { isDeepStrictEqual as isDeepStrictEqual2 } from "util";
1489
+ import { AsyncLocalStorage } from "async_hooks";
1490
+ import { scheduledTaskKnowledgeSource } from "@opengeni/contracts";
1491
+ import {
1492
+ freezeAgentLearningPolicy,
1493
+ getScheduledTaskRunAcceptedExecution,
1494
+ ensureKnowledgeSourceSyncState,
1495
+ recordKnowledgeSourceSyncWake
1496
+ } from "@opengeni/db";
1497
+ import { createHash as createHash2 } from "crypto";
1498
+ import { heartbeat } from "@temporalio/activity";
1499
+ import {
1500
+ googleDriveProviderRetryOptions
1501
+ } from "@opengeni/config";
1502
+ import {
1503
+ KnowledgeSourceSyncRunSummary
1504
+ } from "@opengeni/contracts";
1505
+ import {
1506
+ ATLASSIAN_PROVIDER_DOMAIN,
1507
+ AtlassianConnectionMetadata,
1508
+ atlassianScopesAllowRead
1509
+ } from "@opengeni/contracts/atlassian";
1510
+ import {
1511
+ GoogleDriveConnectionMetadata,
1512
+ GOOGLE_DRIVE_PROVIDER_DOMAIN,
1513
+ googleDriveScopesAllowCapability
1514
+ } from "@opengeni/contracts/google-drive";
1515
+ import {
1516
+ googleDriveKnowledgeScope,
1517
+ inventoryGoogleDriveSource
1518
+ } from "@opengeni/documents/google-drive";
1519
+ import {
1520
+ atlassianKnowledgeScope,
1521
+ inventoryAtlassianSource
1522
+ } from "@opengeni/documents/atlassian";
1523
+ import { addDocumentToBase, ensureDefaultBase, getDocumentBase } from "@opengeni/documents";
1524
+ import {
1525
+ appendKnowledgeDocumentVersion,
1526
+ appendKnowledgeSourceAclVersion,
1527
+ beginKnowledgeSyncRun,
1528
+ beginGoogleDriveObjectAclRefresh,
1529
+ buildConnectionTokenResolver,
1530
+ checkpointKnowledgeSourceSync,
1531
+ claimKnowledgeSourceSyncLease,
1532
+ compareCanonicalDecimalProviderRevisions,
1533
+ completeKnowledgeSyncRun,
1534
+ completeKnowledgeSourceSyncWakeWithoutLease,
1535
+ deauthorizeKnowledgeSourceRetrieval,
1536
+ enqueueKnowledgeSourceSyncIndexObligation,
1537
+ ensureKnowledgeSourceBlobFile,
1538
+ getConnectionMetadata as getConnectionMetadata2,
1539
+ getKnowledgeDocumentVersionObservationForSyncAuthority,
1540
+ getKnowledgeSyncRunForSyncAuthority,
1541
+ getKnowledgeSourceAclForSyncAuthority,
1542
+ getKnowledgeSourceForSyncAuthority,
1543
+ getKnowledgeSourceObjectForSyncAuthority,
1544
+ getKnowledgeSourceSyncIndexObligationForVersion,
1545
+ getScheduledTask,
1546
+ listObservedKnowledgeSourceSyncExternalObjectIds,
1547
+ KnowledgeSourceSyncObservationFenceError,
1548
+ reconcileKnowledgeSourceSyncCompleteScan,
1549
+ reconcileKnowledgeSourceSyncLiveGeneration,
1550
+ recordGoogleDriveObjectAclEvidence,
1551
+ recordKnowledgeSourceSyncItemOutcomes,
1552
+ recordKnowledgeSourceSyncObjectObservations,
1553
+ recordUsageEvent,
1554
+ releaseKnowledgeSourceSyncLeaseForRetry,
1555
+ restoreKnowledgeSourceObject,
1556
+ retryKnowledgeSourceSyncIndexObligation,
1557
+ scopedKnowledgeScopeKey,
1558
+ settleKnowledgeSourceSyncLease,
1559
+ settleKnowledgeSourceSyncIndexObligation,
1560
+ updateKnowledgeSourceDocumentObservationMetadata,
1561
+ updateScheduledTaskRun,
1562
+ upsertKnowledgeSourceObject
1563
+ } from "@opengeni/db";
1564
+ import { readResponseJsonBounded } from "@opengeni/network";
1565
+
1566
+ // src/activities/google-drive-provider.ts
1567
+ var GoogleDriveProviderTransportError = class extends Error {
1568
+ constructor() {
1569
+ super("google_drive_provider_transport_failed");
1570
+ this.name = "GoogleDriveProviderTransportError";
1571
+ }
1572
+ };
1573
+ async function fetchGoogleDriveProvider(input) {
1574
+ const now = input.now ?? Date.now;
1575
+ const sleep3 = input.sleep ?? Bun.sleep;
1576
+ let retryDelaySpentMs = 0;
1577
+ for (let attempt = 1; attempt <= input.policy.attempts; attempt += 1) {
1578
+ let response;
1579
+ input.heartbeat?.({ operation: input.operation, attempt, phase: "request_start" });
1580
+ try {
1581
+ response = await input.fetchImpl(input.url, {
1582
+ ...input.init,
1583
+ redirect: "error",
1584
+ signal: AbortSignal.timeout(input.policy.requestTimeoutMs)
1585
+ });
1586
+ } catch {
1587
+ input.heartbeat?.({ operation: input.operation, attempt, phase: "request_end" });
1588
+ recordRequest(input.observability, input.operation, "retryable_error");
1589
+ const delayMs2 = retryDelayMs(input.policy, attempt, null);
1590
+ if (!canRetry(input.policy, attempt, retryDelaySpentMs, delayMs2)) {
1591
+ throw new GoogleDriveProviderTransportError();
1592
+ }
1593
+ recordRetry(input.observability, input.operation, "transport", attempt, delayMs2);
1594
+ retryDelaySpentMs += delayMs2;
1595
+ input.heartbeat?.({ operation: input.operation, attempt, phase: "retry_delay_start" });
1596
+ await sleep3(delayMs2);
1597
+ input.heartbeat?.({ operation: input.operation, attempt, phase: "retry_delay_end" });
1598
+ continue;
1599
+ }
1600
+ input.heartbeat?.({ operation: input.operation, attempt, phase: "request_end" });
1601
+ if (!isRetryableStatus(response.status)) {
1602
+ recordRequest(
1603
+ input.observability,
1604
+ input.operation,
1605
+ response.ok ? "succeeded" : "terminal_error"
1606
+ );
1607
+ return response;
1608
+ }
1609
+ recordRequest(input.observability, input.operation, "retryable_error");
1610
+ const delayMs = retryDelayMs(
1611
+ input.policy,
1612
+ attempt,
1613
+ retryAfterDelayMs(response.headers.get("retry-after"), now())
1614
+ );
1615
+ if (!canRetry(input.policy, attempt, retryDelaySpentMs, delayMs)) return response;
1616
+ await response.body?.cancel().catch(() => void 0);
1617
+ recordRetry(
1618
+ input.observability,
1619
+ input.operation,
1620
+ response.status === 429 ? "rate_limited" : "provider_unavailable",
1621
+ attempt,
1622
+ delayMs
1623
+ );
1624
+ retryDelaySpentMs += delayMs;
1625
+ input.heartbeat?.({ operation: input.operation, attempt, phase: "retry_delay_start" });
1626
+ await sleep3(delayMs);
1627
+ input.heartbeat?.({ operation: input.operation, attempt, phase: "retry_delay_end" });
1628
+ }
1629
+ throw new GoogleDriveProviderTransportError();
1630
+ }
1631
+ function canRetry(policy, attempt, retryDelaySpentMs, delayMs) {
1632
+ return attempt < policy.attempts && retryDelaySpentMs + delayMs <= policy.budgetMs;
1633
+ }
1634
+ function retryDelayMs(policy, attempt, retryAfterMs) {
1635
+ const exponential = Math.min(
1636
+ policy.maxDelayMs,
1637
+ policy.initialDelayMs * 2 ** Math.max(0, attempt - 1)
1638
+ );
1639
+ return Math.min(policy.maxDelayMs, Math.max(exponential, retryAfterMs ?? 0));
1640
+ }
1641
+ function retryAfterDelayMs(value, nowMs) {
1642
+ if (!value) return null;
1643
+ const seconds = Number(value);
1644
+ if (Number.isFinite(seconds) && seconds >= 0) return Math.round(seconds * 1e3);
1645
+ const timestamp = Date.parse(value);
1646
+ return Number.isFinite(timestamp) ? Math.max(0, timestamp - nowMs) : null;
1647
+ }
1648
+ function isRetryableStatus(status) {
1649
+ return status === 429 || status >= 500;
1650
+ }
1651
+ function recordRequest(observability, operation, outcome) {
1652
+ observability.incrementCounter({
1653
+ name: "opengeni_google_drive_provider_requests_total",
1654
+ help: "Google Drive provider request attempts by operation and bounded outcome.",
1655
+ labels: { operation, outcome }
1656
+ });
1657
+ }
1658
+ function recordRetry(observability, operation, reason, attempt, delayMs) {
1659
+ observability.incrementCounter({
1660
+ name: "opengeni_google_drive_provider_retries_total",
1661
+ help: "Bounded Google Drive provider retries by operation and reason.",
1662
+ labels: { operation, reason }
1663
+ });
1664
+ observability.observeHistogram({
1665
+ name: "opengeni_google_drive_provider_retry_delay_seconds",
1666
+ help: "Delay applied before a bounded Google Drive provider retry.",
1667
+ labels: { operation, reason },
1668
+ value: delayMs / 1e3,
1669
+ buckets: [0.1, 0.25, 0.5, 1, 2, 5, 10, 30, 60]
1670
+ });
1671
+ observability.warn("Google Drive provider request retry scheduled", {
1672
+ provider: "google_drive",
1673
+ operation,
1674
+ outcome: "retry_scheduled",
1675
+ reason,
1676
+ attempt,
1677
+ delayMs
1678
+ });
1679
+ }
1680
+
1681
+ // src/activities/google-drive-changes.ts
1682
+ import { createHash } from "crypto";
1683
+ import {
1684
+ GOOGLE_DRIVE_FOLDER_MIME_TYPE,
1685
+ planGoogleDriveTransfer
1686
+ } from "@opengeni/documents/google-drive";
1687
+ var MAX_TOKEN_CHARS = 4096;
1688
+ var MAX_BOUNDARY_ID_CHARS = 1024;
1689
+ var MAX_ANCESTOR_DEPTH = 100;
1690
+ function parseGoogleDriveChangesCursor(value, expected) {
1691
+ if (!value || value.version !== 1 || value.kind !== "google_drive_changes") return null;
1692
+ if (value.connectionId !== expected.connectionId || value.googlePermissionId !== expected.googlePermissionId || value.sourceId !== expected.sourceId || value.driveId !== expected.driveId || typeof value.boundaryId !== "string" || value.boundaryId.length < 1 || value.boundaryId.length > MAX_BOUNDARY_ID_CHARS || typeof value.pageToken !== "string" || value.pageToken.length < 1 || value.pageToken.length > MAX_TOKEN_CHARS || typeof value.cursorGeneration !== "number" || !Number.isSafeInteger(value.cursorGeneration) || value.cursorGeneration < 1 || typeof value.lastFullReconciliationAt !== "string" || !Number.isFinite(Date.parse(value.lastFullReconciliationAt)) || typeof value.nextFullReconciliationAt !== "string" || !Number.isFinite(Date.parse(value.nextFullReconciliationAt))) {
1693
+ return null;
1694
+ }
1695
+ return value;
1696
+ }
1697
+ function buildGoogleDriveChangesCursor(input) {
1698
+ return {
1699
+ version: 1,
1700
+ kind: "google_drive_changes",
1701
+ connectionId: input.connectionId,
1702
+ googlePermissionId: input.googlePermissionId,
1703
+ sourceId: input.sourceId,
1704
+ driveId: input.driveId,
1705
+ boundaryId: boundedBoundaryId(input.boundaryId),
1706
+ pageToken: boundedToken(input.pageToken),
1707
+ cursorGeneration: input.previousGeneration + 1,
1708
+ lastFullReconciliationAt: input.reconciledAt.toISOString(),
1709
+ nextFullReconciliationAt: new Date(
1710
+ input.reconciledAt.getTime() + input.fullReconciliationIntervalMs
1711
+ ).toISOString()
1712
+ };
1713
+ }
1714
+ function advanceGoogleDriveChangesCursor(cursor, pageToken) {
1715
+ return {
1716
+ ...cursor,
1717
+ pageToken: boundedToken(pageToken),
1718
+ cursorGeneration: cursor.cursorGeneration + 1
1719
+ };
1720
+ }
1721
+ function googleDriveFullReconciliationDue(cursor, now) {
1722
+ return Date.parse(cursor.nextFullReconciliationAt) <= now.getTime();
1723
+ }
1724
+ async function resolveGoogleDriveChangesBoundaryId(input) {
1725
+ if (input.source.id !== "root" || input.source.driveId !== null) {
1726
+ return boundedBoundaryId(input.source.id);
1727
+ }
1728
+ const root = await input.getFile("root");
1729
+ if (!root || root.mimeType !== GOOGLE_DRIVE_FOLDER_MIME_TYPE || root.trashed) {
1730
+ throw new GoogleDriveChangesProtocolError("google_drive_root_boundary_invalid");
1731
+ }
1732
+ return boundedBoundaryId(root.id);
1733
+ }
1734
+ function parseGoogleDriveChangesCheckpoint(value, limits) {
1735
+ try {
1736
+ return validatedCheckpoint(value, limits);
1737
+ } catch {
1738
+ return null;
1739
+ }
1740
+ }
1741
+ async function drainGoogleDriveChanges(input) {
1742
+ const now = input.now ?? Date.now;
1743
+ const startedAt = now();
1744
+ const checkpoint = input.checkpoint ? validatedCheckpoint(input.checkpoint, input) : null;
1745
+ let pageToken = boundedToken(checkpoint?.pageToken ?? input.cursor.pageToken);
1746
+ let requiresFullReconciliation = checkpoint?.requiresFullReconciliation ?? false;
1747
+ const seenPageTokenHashes = new Set(checkpoint?.seenPageTokenHashes ?? []);
1748
+ const initialBudget = checkpoint?.budget ?? emptyBudget();
1749
+ const budget = { ...initialBudget };
1750
+ const entries = /* @__PURE__ */ new Map();
1751
+ while (true) {
1752
+ const invocationElapsedMs = Math.max(0, now() - startedAt);
1753
+ budget.elapsedMs = initialBudget.elapsedMs + invocationElapsedMs;
1754
+ const stopReason = limitReason(budget, input, invocationElapsedMs);
1755
+ if (stopReason) {
1756
+ return {
1757
+ status: "paused",
1758
+ stopReason,
1759
+ entries: [...entries.values()],
1760
+ checkpoint: {
1761
+ version: 2,
1762
+ pageToken,
1763
+ requiresFullReconciliation,
1764
+ seenPageTokenHashes: [...seenPageTokenHashes],
1765
+ budget: { ...budget }
1766
+ },
1767
+ newStartPageToken: null,
1768
+ requiresFullReconciliation,
1769
+ providerRequests: budget.providerRequests,
1770
+ elapsedMs: budget.elapsedMs,
1771
+ budget: { ...budget },
1772
+ hardLimitReached: stopReason !== "elapsed_time_limit" || budget.elapsedMs >= input.maxElapsedMs
1773
+ };
1774
+ }
1775
+ const pageTokenHash = createHash("sha256").update(pageToken).digest("hex");
1776
+ if (seenPageTokenHashes.has(pageTokenHash)) {
1777
+ throw new GoogleDriveChangesProtocolError("google_drive_changes_page_token_cycle");
1778
+ }
1779
+ seenPageTokenHashes.add(pageTokenHash);
1780
+ budget.providerRequests += 1;
1781
+ const pageSize = Math.min(100, input.maxItems - budget.examinedItems);
1782
+ let page;
1783
+ try {
1784
+ page = await input.listChanges(pageToken, pageSize);
1785
+ } catch (error) {
1786
+ budget.elapsedMs = initialBudget.elapsedMs + Math.max(0, now() - startedAt);
1787
+ if (error instanceof GoogleDriveCursorInvalidError) error.budget = { ...budget };
1788
+ throw error;
1789
+ }
1790
+ if (page.changes.length > pageSize) {
1791
+ throw new GoogleDriveChangesProtocolError("google_drive_changes_page_exceeds_item_budget");
1792
+ }
1793
+ budget.examinedItems += page.changes.length;
1794
+ const repairCandidates = /* @__PURE__ */ new Set();
1795
+ for (const change of page.changes) {
1796
+ if (change.removed || !change.file || change.file.trashed) {
1797
+ repairCandidates.add(change.fileId);
1798
+ continue;
1799
+ }
1800
+ const membership = await isInsideSource({
1801
+ file: change.file,
1802
+ boundaryId: input.cursor.boundaryId,
1803
+ getFile: async (fileId) => {
1804
+ const ancestryElapsedMs = Math.max(0, now() - startedAt);
1805
+ budget.elapsedMs = initialBudget.elapsedMs + ancestryElapsedMs;
1806
+ if (budget.providerRequests >= input.maxProviderRequests || budget.elapsedMs >= input.maxElapsedMs || ancestryElapsedMs >= input.maxInvocationElapsedMs) {
1807
+ return null;
1808
+ }
1809
+ budget.providerRequests += 1;
1810
+ const parent = await input.getFile(fileId);
1811
+ budget.elapsedMs = initialBudget.elapsedMs + Math.max(0, now() - startedAt);
1812
+ return parent;
1813
+ }
1814
+ });
1815
+ if (change.file.mimeType === GOOGLE_DRIVE_FOLDER_MIME_TYPE) {
1816
+ entries.delete(change.fileId);
1817
+ if (membership !== "outside") requiresFullReconciliation = true;
1818
+ else repairCandidates.add(change.fileId);
1819
+ continue;
1820
+ }
1821
+ if (membership === "inside") {
1822
+ entries.set(change.fileId, toInventoryEntry(change.file, input.source, input.maxFileBytes));
1823
+ } else if (membership === "unknown") {
1824
+ entries.delete(change.fileId);
1825
+ requiresFullReconciliation = true;
1826
+ } else {
1827
+ repairCandidates.add(change.fileId);
1828
+ }
1829
+ }
1830
+ if (repairCandidates.size > 0) {
1831
+ for (const fileId of repairCandidates) {
1832
+ if (entries.delete(fileId)) requiresFullReconciliation = true;
1833
+ }
1834
+ const observed = await input.observedExternalObjectIds([...repairCandidates]);
1835
+ if (observed.size > 0) requiresFullReconciliation = true;
1836
+ }
1837
+ if (page.nextPageToken) {
1838
+ pageToken = boundedToken(page.nextPageToken);
1839
+ continue;
1840
+ }
1841
+ if (!page.newStartPageToken) {
1842
+ throw new GoogleDriveChangesProtocolError("google_drive_changes_terminal_token_missing");
1843
+ }
1844
+ budget.elapsedMs = initialBudget.elapsedMs + Math.max(0, now() - startedAt);
1845
+ return {
1846
+ status: "complete",
1847
+ stopReason: null,
1848
+ entries: [...entries.values()],
1849
+ checkpoint: null,
1850
+ newStartPageToken: boundedToken(page.newStartPageToken),
1851
+ requiresFullReconciliation,
1852
+ providerRequests: budget.providerRequests,
1853
+ elapsedMs: budget.elapsedMs,
1854
+ budget: { ...budget },
1855
+ hardLimitReached: false
1856
+ };
1857
+ }
1858
+ }
1859
+ async function isInsideSource(input) {
1860
+ if (input.file.id === input.boundaryId) return "inside";
1861
+ let parents = input.file.parents;
1862
+ const seen = /* @__PURE__ */ new Set([input.file.id]);
1863
+ for (let depth = 0; depth < MAX_ANCESTOR_DEPTH; depth += 1) {
1864
+ if (parents.includes(input.boundaryId)) return "inside";
1865
+ const next = parents.find((parent2) => !seen.has(parent2));
1866
+ if (!next) return "outside";
1867
+ seen.add(next);
1868
+ const parent = await input.getFile(next);
1869
+ if (!parent) return "unknown";
1870
+ parents = parent.parents;
1871
+ }
1872
+ return "unknown";
1873
+ }
1874
+ function toInventoryEntry(file, source, maxFileBytes) {
1875
+ return {
1876
+ externalObjectId: file.id,
1877
+ externalVersionId: file.version ?? file.modifiedTime ?? file.md5Checksum,
1878
+ sourceId: source.id,
1879
+ parentFolderId: file.parents[0] ?? source.id,
1880
+ driveId: file.driveId ?? source.driveId,
1881
+ title: file.name,
1882
+ mimeType: file.mimeType,
1883
+ modifiedTime: file.modifiedTime,
1884
+ createdTime: file.createdTime,
1885
+ sourceUri: file.webViewLink ?? `https://drive.google.com/open?id=${encodeURIComponent(file.id)}`,
1886
+ transfer: planGoogleDriveTransfer(file, maxFileBytes)
1887
+ };
1888
+ }
1889
+ function validatedCheckpoint(value, limits) {
1890
+ if (!value || typeof value !== "object") {
1891
+ throw new Error("google_drive_changes_checkpoint_invalid");
1892
+ }
1893
+ const checkpoint = value;
1894
+ const budget = checkpoint.budget;
1895
+ if (checkpoint.version !== 2 || typeof checkpoint.requiresFullReconciliation !== "boolean" || !Array.isArray(checkpoint.seenPageTokenHashes) || checkpoint.seenPageTokenHashes.some((hash) => !/^[a-f0-9]{64}$/.test(hash)) || new Set(checkpoint.seenPageTokenHashes).size !== checkpoint.seenPageTokenHashes.length || checkpoint.seenPageTokenHashes.length > limits.maxProviderRequests || !budget || typeof budget !== "object") {
1896
+ throw new Error("google_drive_changes_checkpoint_invalid");
1897
+ }
1898
+ if (typeof checkpoint.pageToken !== "string") {
1899
+ throw new Error("google_drive_changes_checkpoint_invalid");
1900
+ }
1901
+ boundedToken(checkpoint.pageToken);
1902
+ const budgetRow = budget;
1903
+ for (const [key, maximum] of [
1904
+ ["examinedItems", limits.maxItems],
1905
+ ["providerRequests", limits.maxProviderRequests],
1906
+ ["elapsedMs", limits.maxElapsedMs]
1907
+ ]) {
1908
+ const budgetValue = budgetRow[key];
1909
+ if (!Number.isSafeInteger(budgetValue) || Number(budgetValue) < 0 || Number(budgetValue) > maximum) {
1910
+ throw new Error("google_drive_changes_checkpoint_budget_invalid");
1911
+ }
1912
+ }
1913
+ if (checkpoint.seenPageTokenHashes.length > Number(budgetRow.providerRequests)) {
1914
+ throw new Error("google_drive_changes_checkpoint_request_history_invalid");
1915
+ }
1916
+ return value;
1917
+ }
1918
+ function limitReason(budget, input, invocationElapsedMs) {
1919
+ if (budget.examinedItems >= input.maxItems) return "item_limit";
1920
+ if (budget.providerRequests >= input.maxProviderRequests) return "api_request_limit";
1921
+ if (budget.elapsedMs >= input.maxElapsedMs || invocationElapsedMs >= input.maxInvocationElapsedMs) {
1922
+ return "elapsed_time_limit";
1923
+ }
1924
+ return null;
1925
+ }
1926
+ function emptyBudget() {
1927
+ return { examinedItems: 0, providerRequests: 0, elapsedMs: 0 };
1928
+ }
1929
+ function boundedToken(value) {
1930
+ if (value.length < 1 || value.length > MAX_TOKEN_CHARS) {
1931
+ throw new Error("google_drive_changes_token_invalid");
1932
+ }
1933
+ return value;
1934
+ }
1935
+ function boundedBoundaryId(value) {
1936
+ if (value.length < 1 || value.length > MAX_BOUNDARY_ID_CHARS) {
1937
+ throw new Error("google_drive_changes_boundary_invalid");
1938
+ }
1939
+ return value;
1940
+ }
1941
+ var GoogleDriveCursorInvalidError = class extends Error {
1942
+ budget = null;
1943
+ constructor() {
1944
+ super("google_drive_changes_cursor_invalid");
1945
+ this.name = "GoogleDriveCursorInvalidError";
1946
+ }
1947
+ };
1948
+ var GoogleDriveChangesProtocolError = class extends Error {
1949
+ constructor(message) {
1950
+ super(message);
1951
+ this.name = "GoogleDriveChangesProtocolError";
1952
+ }
1953
+ };
1954
+
1955
+ // src/activities/knowledge-source-sync.ts
1956
+ var sourceRequestAuthority = new AsyncLocalStorage();
1957
+ var sourceFetch = Object.assign(
1958
+ async (...args) => {
1959
+ await sourceRequestAuthority.getStore()?.();
1960
+ return fetch(...args);
1961
+ },
1962
+ { preconnect: fetch.preconnect }
1963
+ );
1964
+ var DRIVE_API_BASE = "https://www.googleapis.com/drive/v3";
1965
+ var DRIVE_JSON_MAX_BYTES = 2 * 1024 * 1024;
1966
+ var GOOGLE_DRIVE_FULL_RECONCILIATION_INTERVAL_MS = 24 * 60 * 60 * 1e3;
1967
+ var GOOGLE_DRIVE_SYNC_INVOCATION_SLICE_MS = 3e4;
1968
+ var GOOGLE_DRIVE_EXECUTION_CHECKPOINT_MAX_BYTES = 2 * 1024 * 1024;
1969
+ var GOOGLE_DRIVE_ACL_FRESHNESS_MS = 26 * 60 * 60 * 1e3;
1970
+ var GOOGLE_DRIVE_ACL_MAX_PRINCIPALS = 1e3;
1971
+ var ATLASSIAN_API_BASE = "https://api.atlassian.com";
1972
+ var ATLASSIAN_JSON_MAX_BYTES = 4 * 1024 * 1024;
1973
+ function knowledgeSyncObservationPolicy(kind) {
1974
+ return kind === "google_drive" ? { revisionOrdering: "canonical_decimal", filterWithDriveDurability: true } : { revisionOrdering: "first_observation", filterWithDriveDurability: false };
1975
+ }
1976
+ function createKnowledgeSourceSyncActivities(services, resolveDocumentServices) {
1977
+ const documentActivities = createDocumentActivities(services, resolveDocumentServices);
1978
+ return {
1979
+ runKnowledgeSourceSyncBatch: async (input) => {
1980
+ const { settings, db, objectStorage, observability } = await services();
1981
+ if (!objectStorage) throw new Error("object storage is not configured");
1982
+ if (!input.agent)
1983
+ return { action: "failed", bufferedWake: false, errorCode: "accepted_agent_required" };
1984
+ const context = {
1985
+ accountId: input.accountId,
1986
+ workspaceId: input.workspaceId,
1987
+ actor: input.agent
1988
+ };
1989
+ let boundSource = null;
1990
+ const authorize = async () => {
1991
+ const policy = await freezeAgentLearningPolicy(db, context);
1992
+ if (policy.scheduledTaskRunId !== input.scheduledTaskRunId)
1993
+ throw new Error("Source fetch does not belong to this accepted agent run");
1994
+ if (boundSource) {
1995
+ const [currentTask, connection] = await Promise.all([
1996
+ getScheduledTask(db, input.workspaceId, input.taskId),
1997
+ getConnectionMetadata2(
1998
+ db,
1999
+ input.workspaceId,
2000
+ boundSource.connection.connectionId,
2001
+ boundSource.initiatingSubjectId
2002
+ )
2003
+ ]);
2004
+ const control = currentTask?.metadata.knowledgeSourceSync;
2005
+ if (!currentTask || currentTask.status !== "active" || control?.sourceEnabled === false || control?.connectionPaused === true || !isDeepStrictEqual2(scheduledTaskKnowledgeSource(currentTask), boundSource) || !connection || connection.status !== "active" || connection.version !== boundSource.connection.connectionVersion || connection.subjectId !== boundSource.connection.ownerSubjectId)
2006
+ throw new SyncFailure("authority_changed", false);
2007
+ }
2008
+ return policy;
2009
+ };
2010
+ await authorize();
2011
+ const accepted = await getScheduledTaskRunAcceptedExecution(db, {
2012
+ workspaceId: input.workspaceId,
2013
+ runId: input.scheduledTaskRunId
2014
+ });
2015
+ const task = accepted?.task;
2016
+ const action = task ? scheduledTaskKnowledgeSource(task) : null;
2017
+ const liveTask = await getScheduledTask(db, input.workspaceId, input.taskId);
2018
+ if (!task || !action || !liveTask || liveTask.status !== "active" || task.id !== input.taskId || task.accountId !== input.accountId || task.action.kind !== "agent_turn" || action.sourceId !== input.sourceId || accepted.causalHumanSubjectId !== action.initiatingSubjectId) {
2019
+ return { action: "failed", bufferedWake: false };
2020
+ }
2021
+ boundSource = action;
2022
+ await authorize();
2023
+ await ensureKnowledgeSourceSyncState(db, task);
2024
+ await recordKnowledgeSourceSyncWake(db, {
2025
+ accountId: input.accountId,
2026
+ workspaceId: input.workspaceId,
2027
+ sourceId: input.sourceId,
2028
+ scheduledTaskId: task.id,
2029
+ scheduledTaskRunId: input.scheduledTaskRunId,
2030
+ cause: "scheduled",
2031
+ producerKey: `agent-source:${input.scheduledTaskRunId}`,
2032
+ sourceConfigGeneration: action.sourceConfigGeneration,
2033
+ sourceLifecycleGeneration: action.sourceLifecycleGeneration
2034
+ });
2035
+ const providerLabel = action.connection.providerDomain === ATLASSIAN_PROVIDER_DOMAIN ? "atlassian" : "google_drive";
2036
+ if (action.controlWorkspaceId !== input.workspaceId || action.sourceConfigGeneration <= 0 || !action.providerCoordinationKey) {
2037
+ return await failWithoutProvider("authority_changed");
2038
+ }
2039
+ const lease = await claimKnowledgeSourceSyncLease(db, {
2040
+ ...input,
2041
+ overlapPolicy: input.overlapPolicy
2042
+ });
2043
+ if (lease.action !== "claimed") {
2044
+ observability.incrementCounter({
2045
+ name: "opengeni_knowledge_source_sync_runs_total",
2046
+ labels: { provider: providerLabel, outcome: lease.action }
2047
+ });
2048
+ return { action: lease.action };
2049
+ }
2050
+ let knowledgeRunId = null;
2051
+ let activeSourceSyncGeneration = null;
2052
+ const startedAt = Date.now();
2053
+ const summary = {
2054
+ phase: "queued",
2055
+ scanned: 0,
2056
+ imported: 0,
2057
+ unchanged: 0,
2058
+ skipped: 0,
2059
+ failed: 0,
2060
+ bytes: 0,
2061
+ providerRequests: 0,
2062
+ elapsedMs: 0,
2063
+ indexed: 0,
2064
+ aclPending: 0,
2065
+ retryable: false,
2066
+ limitReached: null,
2067
+ checkpointed: false,
2068
+ reconnectRequired: false,
2069
+ failures: []
2070
+ };
2071
+ observability.info("Knowledge source sync started", {
2072
+ provider: providerLabel,
2073
+ outcome: "started"
2074
+ });
2075
+ try {
2076
+ summary.phase = "inventory";
2077
+ const resolved = await getKnowledgeSourceForSyncAuthority(db, {
2078
+ accountId: input.accountId,
2079
+ workspaceId: input.workspaceId,
2080
+ sourceId: input.sourceId,
2081
+ initiatingSubjectId: action.initiatingSubjectId
2082
+ });
2083
+ if (!resolved || resolved.source.lifecycleState !== "active") {
2084
+ throw new SyncFailure("authority_changed", false);
2085
+ }
2086
+ if (scopedKnowledgeScopeKey(resolved.source.scope) !== scopedKnowledgeScopeKey(action.destination)) {
2087
+ throw new SyncFailure("authority_changed", false);
2088
+ }
2089
+ const liveState = await reconcileKnowledgeSourceSyncLiveGeneration(db, {
2090
+ accountId: input.accountId,
2091
+ workspaceId: input.workspaceId,
2092
+ sourceId: input.sourceId,
2093
+ scheduledTaskRunId: input.scheduledTaskRunId,
2094
+ initiatingSubjectId: action.initiatingSubjectId,
2095
+ sourceConfigGeneration: action.sourceConfigGeneration,
2096
+ sourceLifecycleGeneration: action.sourceLifecycleGeneration,
2097
+ liveSourceSyncGeneration: resolved.source.syncGeneration
2098
+ });
2099
+ activeSourceSyncGeneration = resolved.source.syncGeneration;
2100
+ const syncOperationId = `scheduled-task-run:${input.scheduledTaskRunId}`;
2101
+ const replayedRun = await getKnowledgeSyncRunForSyncAuthority(db, {
2102
+ accountId: input.accountId,
2103
+ workspaceId: input.workspaceId,
2104
+ sourceId: input.sourceId,
2105
+ operationId: syncOperationId,
2106
+ initiatingSubjectId: action.initiatingSubjectId
2107
+ });
2108
+ if (replayedRun && replayedRun.state !== "started") {
2109
+ const replayedSummary = KnowledgeSourceSyncRunSummary.parse(replayedRun.metadata);
2110
+ const settlementInput = {
2111
+ accountId: input.accountId,
2112
+ workspaceId: input.workspaceId,
2113
+ sourceId: input.sourceId,
2114
+ scheduledTaskRunId: input.scheduledTaskRunId,
2115
+ knowledgeSyncRunId: replayedRun.id,
2116
+ status: replayedRun.state === "succeeded" ? "succeeded" : "failed",
2117
+ summary: replayedSummary,
2118
+ error: replayedRun.errorCode,
2119
+ sourceConfigGeneration: action.sourceConfigGeneration,
2120
+ sourceLifecycleGeneration: action.sourceLifecycleGeneration,
2121
+ sourceSyncGeneration: replayedRun.inputSyncGeneration,
2122
+ completedSourceSyncGeneration: replayedRun.state === "succeeded" ? replayedRun.inputSyncGeneration + 1 : null,
2123
+ ...replayedRun.state === "succeeded" && replayedRun.metadata.providerCursor && typeof replayedRun.metadata.providerCursor === "object" ? {
2124
+ providerCursor: replayedRun.metadata.providerCursor
2125
+ } : {}
2126
+ };
2127
+ const settled2 = replayedRun.state === "failed" && replayedSummary.reconnectRequired ? await deauthorizeAndSettleKnowledgeSourceSyncFailure(db, {
2128
+ deauthorization: {
2129
+ accountId: input.accountId,
2130
+ workspaceId: input.workspaceId,
2131
+ sourceId: input.sourceId,
2132
+ audience: action.destination,
2133
+ operationId: `knowledge-sync-deauthorize:${input.sourceId}:${action.connection.connectionVersion}:${replayedRun.errorCode ?? "connection_reconnect_required"}`,
2134
+ reasonCode: "connection_reconnect_required",
2135
+ actor: {
2136
+ kind: "service",
2137
+ subjectId: "knowledge-source-sync",
2138
+ initiatingHumanSubjectId: action.initiatingSubjectId
2139
+ }
2140
+ },
2141
+ settlement: settlementInput
2142
+ }) : await settleKnowledgeSourceSyncLease(db, settlementInput);
2143
+ await recordConnectorUsage("completed", replayedSummary.scanned, "item");
2144
+ await recordConnectorUsage("items", replayedSummary.imported, "item");
2145
+ await recordConnectorUsage("bytes", replayedSummary.bytes, "byte");
2146
+ return {
2147
+ action: replayedRun.state === "succeeded" ? "complete" : "failed",
2148
+ summary: replayedSummary,
2149
+ ...replayedRun.errorCode ? { errorCode: replayedRun.errorCode } : {},
2150
+ bufferedWake: settled2.bufferedWake,
2151
+ bufferedScheduledTaskRunId: settled2.bufferedScheduledTaskRunId
2152
+ };
2153
+ }
2154
+ const provider = await resolveKnowledgeSyncProvider({
2155
+ db,
2156
+ settings,
2157
+ observability,
2158
+ accountId: input.accountId,
2159
+ workspaceId: input.workspaceId,
2160
+ action,
2161
+ sourceId: input.sourceId,
2162
+ externalSourceId: resolved.source.externalSourceId
2163
+ });
2164
+ const selectedDestination = provider.selectedDestination;
2165
+ const observationPolicy = knowledgeSyncObservationPolicy(provider.kind);
2166
+ let aclGeneration = resolved.source.currentAclGeneration;
2167
+ let acl = aclGeneration ? await getKnowledgeSourceAclForSyncAuthority(db, {
2168
+ accountId: input.accountId,
2169
+ workspaceId: input.workspaceId,
2170
+ sourceId: input.sourceId,
2171
+ generation: aclGeneration,
2172
+ initiatingSubjectId: action.initiatingSubjectId
2173
+ }) : null;
2174
+ const actor = {
2175
+ kind: "service",
2176
+ subjectId: "knowledge-source-sync",
2177
+ initiatingHumanSubjectId: action.initiatingSubjectId
2178
+ };
2179
+ if (!acl) {
2180
+ acl = await appendKnowledgeSourceAclVersion(db, {
2181
+ accountId: input.accountId,
2182
+ workspaceId: input.workspaceId,
2183
+ sourceId: input.sourceId,
2184
+ audience: action.destination,
2185
+ expectedSourceLifecycleGeneration: resolved.source.lifecycleGeneration,
2186
+ expectedAclGeneration: 0,
2187
+ aclVersion: "opengeni-destination-v1",
2188
+ agentAccess: false,
2189
+ operationId: `source-initial-acl:${input.sourceId}`,
2190
+ reasonCode: "initial_destination_acl",
2191
+ actor
2192
+ });
2193
+ aclGeneration = acl.generation;
2194
+ }
2195
+ const knowledgeRun = replayedRun ?? await beginKnowledgeSyncRun(db, {
2196
+ accountId: input.accountId,
2197
+ workspaceId: input.workspaceId,
2198
+ sourceId: input.sourceId,
2199
+ expectedSourceLifecycleGeneration: resolved.source.lifecycleGeneration,
2200
+ expectedSyncGeneration: resolved.source.syncGeneration,
2201
+ inputCursor: null,
2202
+ operationId: syncOperationId,
2203
+ actor
2204
+ });
2205
+ knowledgeRunId = knowledgeRun.id;
2206
+ const originalDriver = provider.driver;
2207
+ const readAcl = originalDriver.readAcl;
2208
+ const driver = {
2209
+ ...originalDriver,
2210
+ inventory: (...args) => sourceRequestAuthority.run(authorize, () => originalDriver.inventory(...args)),
2211
+ fetchContent: (...args) => sourceRequestAuthority.run(authorize, () => originalDriver.fetchContent(...args)),
2212
+ ...readAcl ? {
2213
+ readAcl: (...args) => sourceRequestAuthority.run(authorize, () => readAcl(...args))
2214
+ } : {}
2215
+ };
2216
+ await authorize();
2217
+ const inventory = await driver.inventory(
2218
+ lease.state.executionCheckpoint,
2219
+ lease.state.providerCursor
2220
+ );
2221
+ summary.providerRequests = inventory.providerRequests;
2222
+ summary.elapsedMs = inventory.elapsedMs;
2223
+ const base = selectedDestination.collectionId ? await getDocumentBase(db, input.workspaceId, selectedDestination.collectionId) : await ensureDefaultBase(db, {
2224
+ accountId: input.accountId,
2225
+ workspaceId: input.workspaceId
2226
+ });
2227
+ if (!base) throw new SyncFailure("authority_changed", false);
2228
+ const durableObservations = await recordKnowledgeSourceSyncObjectObservations(db, {
2229
+ accountId: input.accountId,
2230
+ workspaceId: input.workspaceId,
2231
+ sourceId: input.sourceId,
2232
+ scheduledTaskRunId: input.scheduledTaskRunId,
2233
+ initiatingSubjectId: action.initiatingSubjectId,
2234
+ scanGeneration: liveState.activeScanGeneration,
2235
+ executionCheckpointGeneration: lease.state.executionCheckpointGeneration,
2236
+ revisionOrdering: observationPolicy.revisionOrdering,
2237
+ observations: inventory.entries.map((entry) => ({
2238
+ externalObjectId: entry.externalObjectId,
2239
+ providerRevision: entry.externalVersionId,
2240
+ metadataHash: entryMetadataHash(entry)
2241
+ }))
2242
+ });
2243
+ const entriesToProcess = observationPolicy.filterWithDriveDurability ? inventory.entries.filter(
2244
+ (entry, index) => shouldProcessGoogleDriveDurableObservation({
2245
+ entry,
2246
+ observation: durableObservations[index],
2247
+ sourceLifecycleGeneration: resolved.source.lifecycleGeneration,
2248
+ aclGeneration
2249
+ })
2250
+ ) : inventory.entries;
2251
+ const observationFloorsById = /* @__PURE__ */ new Map();
2252
+ for (const observation of durableObservations) {
2253
+ observationFloorsById.set(observation.externalObjectId, {
2254
+ externalObjectId: observation.externalObjectId,
2255
+ providerRevision: observation.providerRevision,
2256
+ metadataHash: observation.metadataHash
2257
+ });
2258
+ }
2259
+ const observationFloors = [...observationFloorsById.values()];
2260
+ const executionCheckpoint = mergeGoogleDriveDurableObservationFloors(
2261
+ inventory.checkpoint,
2262
+ observationFloors,
2263
+ action.limits.maxItems
2264
+ );
2265
+ const ensureVersionIndexed = async (details) => {
2266
+ if (!details.version.documentId) {
2267
+ throw new SyncFailure("authority_changed", false);
2268
+ }
2269
+ let obligation = details.obligation ?? await getKnowledgeSourceSyncIndexObligationForVersion(db, {
2270
+ accountId: input.accountId,
2271
+ workspaceId: input.workspaceId,
2272
+ knowledgeDocumentVersionId: details.version.id,
2273
+ initiatingSubjectId: action.initiatingSubjectId
2274
+ });
2275
+ if (!obligation) {
2276
+ obligation = await enqueueKnowledgeSourceSyncIndexObligation(db, {
2277
+ accountId: input.accountId,
2278
+ workspaceId: input.workspaceId,
2279
+ scheduledTaskRunId: input.scheduledTaskRunId,
2280
+ sourceId: input.sourceId,
2281
+ sourceSyncGeneration: knowledgeRun.inputSyncGeneration,
2282
+ initiatingSubjectId: action.initiatingSubjectId,
2283
+ externalObjectId: details.entry.externalObjectId,
2284
+ knowledgeSourceObjectId: details.object.id,
2285
+ knowledgeDocumentVersionId: details.version.id,
2286
+ documentId: details.version.documentId,
2287
+ sourceConfigGeneration: action.sourceConfigGeneration,
2288
+ sourceLifecycleGeneration: action.sourceLifecycleGeneration,
2289
+ objectLifecycleGeneration: details.object.lifecycleGeneration,
2290
+ objectVersionGeneration: details.version.versionGeneration,
2291
+ citationLocator: driver.citationLocator(details.entry)
2292
+ });
2293
+ }
2294
+ if (obligation.documentId !== details.version.documentId) {
2295
+ throw new SyncFailure("authority_changed", false);
2296
+ }
2297
+ if (obligation.status === "indexed" && !details.indexRequired) return obligation;
2298
+ if (obligation.status === "indexed" && details.indexRequired) {
2299
+ summary.phase = "index";
2300
+ try {
2301
+ const indexedDocument = await documentActivities.indexDocument({
2302
+ accountId: input.accountId,
2303
+ workspaceId: input.workspaceId,
2304
+ documentId: details.version.documentId,
2305
+ authorityKind: action.destination.kind,
2306
+ authorityWorkspaceId: action.destination.workspaceId,
2307
+ authoritySubjectId: action.destination.subjectId
2308
+ });
2309
+ if (indexedDocument.status !== "ready") {
2310
+ throw new Error("document indexing did not complete");
2311
+ }
2312
+ } catch {
2313
+ throw new SyncFailure("indexing_failed", true);
2314
+ }
2315
+ return obligation;
2316
+ }
2317
+ if (obligation.status === "failed") {
2318
+ const retried = await retryKnowledgeSourceSyncIndexObligation(db, {
2319
+ accountId: input.accountId,
2320
+ workspaceId: input.workspaceId,
2321
+ obligationId: obligation.id,
2322
+ scheduledTaskRunId: input.scheduledTaskRunId
2323
+ });
2324
+ if (retried === "invalidated") {
2325
+ throw new SyncFailure("authority_changed", false);
2326
+ }
2327
+ obligation = { ...obligation, status: retried };
2328
+ if (retried === "indexed") return obligation;
2329
+ }
2330
+ if (obligation.status !== "pending") {
2331
+ throw new SyncFailure("authority_changed", false);
2332
+ }
2333
+ if (details.indexRequired) {
2334
+ summary.phase = "index";
2335
+ try {
2336
+ const indexedDocument = await documentActivities.indexDocument({
2337
+ accountId: input.accountId,
2338
+ workspaceId: input.workspaceId,
2339
+ documentId: details.version.documentId,
2340
+ authorityKind: action.destination.kind,
2341
+ authorityWorkspaceId: action.destination.workspaceId,
2342
+ authoritySubjectId: action.destination.subjectId
2343
+ });
2344
+ if (indexedDocument.status !== "ready") {
2345
+ throw new Error("document indexing did not complete");
2346
+ }
2347
+ } catch {
2348
+ await settleKnowledgeSourceSyncIndexObligation(db, {
2349
+ accountId: input.accountId,
2350
+ workspaceId: input.workspaceId,
2351
+ obligationId: obligation.id,
2352
+ status: "failed",
2353
+ failureCode: "indexing_failed"
2354
+ }).catch(() => void 0);
2355
+ throw new SyncFailure("indexing_failed", true);
2356
+ }
2357
+ }
2358
+ const settlement = await settleKnowledgeSourceSyncIndexObligation(db, {
2359
+ accountId: input.accountId,
2360
+ workspaceId: input.workspaceId,
2361
+ obligationId: obligation.id,
2362
+ status: "indexed"
2363
+ });
2364
+ if (settlement === "invalidated") {
2365
+ throw new SyncFailure("authority_changed", false);
2366
+ }
2367
+ return { ...obligation, status: "indexed" };
2368
+ };
2369
+ const recordEntryAcl = async (details) => {
2370
+ if (!driver.readAcl || !provider.aclAuthority) {
2371
+ return { aclEligibility: "pending", aclEvidence: null };
2372
+ }
2373
+ await beginGoogleDriveObjectAclRefresh(db, {
2374
+ accountId: input.accountId,
2375
+ workspaceId: input.workspaceId,
2376
+ obligationId: details.obligation.id,
2377
+ sourceSyncGeneration: details.obligation.sourceSyncGeneration,
2378
+ sourceConfigGeneration: action.sourceConfigGeneration,
2379
+ sourceLifecycleGeneration: action.sourceLifecycleGeneration,
2380
+ objectLifecycleGeneration: details.object.lifecycleGeneration,
2381
+ objectVersionGeneration: details.version.versionGeneration
2382
+ });
2383
+ const remainingProviderRequests = action.limits.maxProviderRequests - summary.providerRequests;
2384
+ if (remainingProviderRequests < 1) {
2385
+ throw new SyncFailure("resource_limit", false);
2386
+ }
2387
+ await authorize();
2388
+ const evidence = await driver.readAcl(details.entry, remainingProviderRequests);
2389
+ summary.providerRequests += evidence.providerRequests;
2390
+ if (summary.providerRequests > action.limits.maxProviderRequests) {
2391
+ throw new SyncFailure("resource_limit", false);
2392
+ }
2393
+ const recorded = await recordGoogleDriveObjectAclEvidence(db, {
2394
+ accountId: input.accountId,
2395
+ workspaceId: input.workspaceId,
2396
+ obligationId: details.obligation.id,
2397
+ connectionId: provider.aclAuthority.connectionId,
2398
+ connectionVersion: provider.aclAuthority.connectionVersion,
2399
+ sourceGooglePermissionId: provider.aclAuthority.sourceGooglePermissionId,
2400
+ sourceSyncGeneration: details.obligation.sourceSyncGeneration,
2401
+ sourceConfigGeneration: action.sourceConfigGeneration,
2402
+ sourceLifecycleGeneration: action.sourceLifecycleGeneration,
2403
+ objectLifecycleGeneration: details.object.lifecycleGeneration,
2404
+ objectVersionGeneration: details.version.versionGeneration,
2405
+ providerRevision: evidence.providerRevision,
2406
+ driveId: evidence.driveId,
2407
+ aclRevision: evidence.aclRevision,
2408
+ eligibility: evidence.eligibility,
2409
+ observedAt: evidence.observedAt,
2410
+ expiresAt: evidence.expiresAt,
2411
+ citationLocator: driver.citationLocator(details.entry),
2412
+ operationId: `drive-acl:${details.obligation.id}:${evidence.aclRevision}`,
2413
+ principals: evidence.principals
2414
+ });
2415
+ return {
2416
+ aclEligibility: evidence.eligibility,
2417
+ aclEvidence: {
2418
+ version: 1,
2419
+ provider: "google_drive",
2420
+ evidenceId: recorded.evidenceId,
2421
+ aclRevision: evidence.aclRevision,
2422
+ aclHash: recorded.aclHash,
2423
+ observedAt: evidence.observedAt,
2424
+ expiresAt: evidence.expiresAt
2425
+ }
2426
+ };
2427
+ };
2428
+ const outcomes = [];
2429
+ for (const entry of entriesToProcess) {
2430
+ await authorize();
2431
+ heartbeat({ sourceId: input.sourceId, externalObjectId: entry.externalObjectId });
2432
+ const observationFloor = observationFloorsById.get(entry.externalObjectId);
2433
+ if (!observationFloor) throw new SyncFailure("provider_payload_invalid", false);
2434
+ const syncObservation = {
2435
+ scheduledTaskRunId: input.scheduledTaskRunId,
2436
+ scanGeneration: liveState.activeScanGeneration,
2437
+ providerRevision: observationFloor.providerRevision,
2438
+ metadataHash: observationFloor.metadataHash
2439
+ };
2440
+ summary.scanned += 1;
2441
+ if (entry.transfer.action === "skip") {
2442
+ summary.skipped += 1;
2443
+ outcomes.push({
2444
+ externalObjectId: entry.externalObjectId,
2445
+ outcome: "skipped",
2446
+ reasonCode: entry.transfer.reason
2447
+ });
2448
+ continue;
2449
+ }
2450
+ if (entry.transfer.action === "traverse") continue;
2451
+ try {
2452
+ const existingObject = await getKnowledgeSourceObjectForSyncAuthority(db, {
2453
+ accountId: input.accountId,
2454
+ workspaceId: input.workspaceId,
2455
+ sourceId: input.sourceId,
2456
+ externalObjectId: entry.externalObjectId,
2457
+ initiatingSubjectId: action.initiatingSubjectId
2458
+ });
2459
+ let object = existingObject ?? await upsertKnowledgeSourceObject(db, {
2460
+ accountId: input.accountId,
2461
+ workspaceId: input.workspaceId,
2462
+ sourceId: input.sourceId,
2463
+ externalObjectId: entry.externalObjectId,
2464
+ operationId: `source-object:${input.sourceId}:${entry.externalObjectId}`,
2465
+ actor
2466
+ });
2467
+ if (object.lifecycleState !== "active") {
2468
+ const restored = await restoreKnowledgeSourceObject(db, {
2469
+ accountId: input.accountId,
2470
+ workspaceId: input.workspaceId,
2471
+ targetId: object.id,
2472
+ expectedGeneration: object.lifecycleGeneration,
2473
+ operationId: `knowledge-sync-object-restore:${object.id}:${liveState.activeScanGeneration}`,
2474
+ reasonCode: "authoritative_scan_observed",
2475
+ actor
2476
+ });
2477
+ object = {
2478
+ ...object,
2479
+ lifecycleState: "active",
2480
+ lifecycleGeneration: restored.lifecycleGeneration
2481
+ };
2482
+ }
2483
+ const currentObservation = object.currentVersionId ? await getKnowledgeDocumentVersionObservationForSyncAuthority(db, {
2484
+ accountId: input.accountId,
2485
+ workspaceId: input.workspaceId,
2486
+ versionId: object.currentVersionId,
2487
+ initiatingSubjectId: action.initiatingSubjectId
2488
+ }) : null;
2489
+ const currentVersion = currentObservation?.version ?? null;
2490
+ const metadataHash = entryMetadataHash(entry);
2491
+ const observedProviderRevision = typeof currentObservation?.sourceMetadata.providerRevision === "string" ? currentObservation.sourceMetadata.providerRevision : null;
2492
+ const observedMetadataHash = typeof currentObservation?.sourceMetadata.metadataHash === "string" ? currentObservation.sourceMetadata.metadataHash : null;
2493
+ const observedSourceLifecycleGeneration = Number(
2494
+ currentObservation?.sourceMetadata.sourceLifecycleGeneration ?? 0
2495
+ );
2496
+ const observedObjectLifecycleGeneration = Number(
2497
+ currentObservation?.sourceMetadata.objectLifecycleGeneration ?? 0
2498
+ );
2499
+ const currentObligation = currentVersion ? await getKnowledgeSourceSyncIndexObligationForVersion(db, {
2500
+ accountId: input.accountId,
2501
+ workspaceId: input.workspaceId,
2502
+ knowledgeDocumentVersionId: currentVersion.id,
2503
+ initiatingSubjectId: action.initiatingSubjectId
2504
+ }) : null;
2505
+ if (currentVersion && currentVersion.documentId && observedProviderRevision === entry.externalVersionId && observedMetadataHash === metadataHash && currentVersion.aclGeneration === aclGeneration && observedSourceLifecycleGeneration === resolved.source.lifecycleGeneration && observedObjectLifecycleGeneration === object.lifecycleGeneration && currentObligation?.status !== "invalidated") {
2506
+ await updateKnowledgeSourceDocumentObservationMetadata(db, {
2507
+ accountId: input.accountId,
2508
+ workspaceId: input.workspaceId,
2509
+ initiatingSubjectId: action.initiatingSubjectId,
2510
+ sourceId: input.sourceId,
2511
+ expectedSourceLifecycleGeneration: resolved.source.lifecycleGeneration,
2512
+ objectId: object.id,
2513
+ expectedObjectLifecycleGeneration: object.lifecycleGeneration,
2514
+ versionId: currentVersion.id,
2515
+ documentId: currentVersion.documentId,
2516
+ title: entry.title,
2517
+ sourceUri: entry.sourceUri,
2518
+ sourceVersion: entry.externalVersionId ?? currentVersion.contentSha256,
2519
+ sourceUpdatedAt: entry.modifiedTime,
2520
+ syncObservation
2521
+ });
2522
+ const obligation2 = await ensureVersionIndexed({
2523
+ entry,
2524
+ object,
2525
+ version: currentVersion,
2526
+ indexRequired: (currentObservation?.documentChunkCount ?? 0) === 0,
2527
+ obligation: currentObligation
2528
+ });
2529
+ const objectAcl2 = await recordEntryAcl({
2530
+ entry,
2531
+ object,
2532
+ version: currentVersion,
2533
+ obligation: obligation2
2534
+ });
2535
+ summary.unchanged += 1;
2536
+ outcomes.push({
2537
+ externalObjectId: entry.externalObjectId,
2538
+ outcome: "unchanged",
2539
+ contentSha256: currentVersion.contentSha256,
2540
+ providerRevision: entry.externalVersionId,
2541
+ metadataHash,
2542
+ aclEligibility: objectAcl2.aclEligibility,
2543
+ aclEvidence: objectAcl2.aclEvidence,
2544
+ indexObligationId: obligation2.id
2545
+ });
2546
+ continue;
2547
+ }
2548
+ summary.phase = "transfer";
2549
+ await authorize();
2550
+ const bytes = await driver.fetchContent(entry, action.limits.maxFileBytes);
2551
+ const contentSha256 = createHash2("sha256").update(bytes).digest("hex");
2552
+ const observationKey = createHash2("sha256").update(
2553
+ JSON.stringify({
2554
+ externalObjectId: entry.externalObjectId,
2555
+ providerRevision: entry.externalVersionId,
2556
+ metadataHash,
2557
+ contentSha256,
2558
+ aclGeneration,
2559
+ sourceLifecycleGeneration: resolved.source.lifecycleGeneration,
2560
+ objectLifecycleGeneration: object.lifecycleGeneration
2561
+ })
2562
+ ).digest("hex");
2563
+ if (currentVersion?.contentSha256 === contentSha256 && currentVersion.documentId && currentVersion.fileId) {
2564
+ const version2 = await appendKnowledgeDocumentVersion(db, {
2565
+ accountId: input.accountId,
2566
+ workspaceId: input.workspaceId,
2567
+ objectId: object.id,
2568
+ expectedSourceLifecycleGeneration: resolved.source.lifecycleGeneration,
2569
+ expectedObjectLifecycleGeneration: object.lifecycleGeneration,
2570
+ expectedVersionGeneration: object.versionGeneration,
2571
+ externalVersionId: entry.externalVersionId ?? contentSha256,
2572
+ contentSha256,
2573
+ ingestionKey: `${entry.externalObjectId}:${observationKey}`,
2574
+ sourceCursor: null,
2575
+ sourceMetadata: {
2576
+ title: entry.title,
2577
+ mimeType: entry.mimeType,
2578
+ driveId: entry.driveId,
2579
+ providerRevision: entry.externalVersionId,
2580
+ metadataHash,
2581
+ aclEligibility: "pending",
2582
+ sourceLifecycleGeneration: resolved.source.lifecycleGeneration,
2583
+ objectLifecycleGeneration: object.lifecycleGeneration
2584
+ },
2585
+ sourceCreatedAt: entry.createdTime,
2586
+ sourceUpdatedAt: entry.modifiedTime,
2587
+ aclVersionId: acl.id,
2588
+ aclGeneration,
2589
+ documentId: currentVersion.documentId,
2590
+ fileId: currentVersion.fileId,
2591
+ locationMetadata: { sourceUri: entry.sourceUri },
2592
+ documentObservationMetadata: {
2593
+ title: entry.title,
2594
+ sourceUri: entry.sourceUri,
2595
+ sourceVersion: entry.externalVersionId ?? contentSha256,
2596
+ sourceUpdatedAt: entry.modifiedTime
2597
+ },
2598
+ syncObservation,
2599
+ operationId: `document-observation:${object.id}:${observationKey}`,
2600
+ reasonCode: "source_metadata_observed",
2601
+ actor
2602
+ });
2603
+ await updateKnowledgeSourceDocumentObservationMetadata(db, {
2604
+ accountId: input.accountId,
2605
+ workspaceId: input.workspaceId,
2606
+ initiatingSubjectId: action.initiatingSubjectId,
2607
+ sourceId: input.sourceId,
2608
+ expectedSourceLifecycleGeneration: resolved.source.lifecycleGeneration,
2609
+ objectId: object.id,
2610
+ expectedObjectLifecycleGeneration: object.lifecycleGeneration,
2611
+ versionId: version2.id,
2612
+ documentId: currentVersion.documentId,
2613
+ title: entry.title,
2614
+ sourceUri: entry.sourceUri,
2615
+ sourceVersion: entry.externalVersionId ?? contentSha256,
2616
+ sourceUpdatedAt: entry.modifiedTime,
2617
+ syncObservation
2618
+ });
2619
+ const obligation2 = await ensureVersionIndexed({
2620
+ entry,
2621
+ object,
2622
+ version: version2,
2623
+ indexRequired: (currentObservation?.documentChunkCount ?? 0) === 0
2624
+ });
2625
+ const objectAcl2 = await recordEntryAcl({ entry, object, version: version2, obligation: obligation2 });
2626
+ summary.unchanged += 1;
2627
+ summary.indexed += 1;
2628
+ if (objectAcl2.aclEligibility === "pending") summary.aclPending += 1;
2629
+ outcomes.push({
2630
+ externalObjectId: entry.externalObjectId,
2631
+ outcome: "unchanged",
2632
+ contentSha256,
2633
+ sizeBytes: bytes.byteLength,
2634
+ providerRevision: entry.externalVersionId,
2635
+ metadataHash,
2636
+ aclEligibility: objectAcl2.aclEligibility,
2637
+ aclEvidence: objectAcl2.aclEvidence,
2638
+ indexObligationId: obligation2.id
2639
+ });
2640
+ continue;
2641
+ }
2642
+ const fileId = deterministicUuid(
2643
+ `knowledge-blob:${input.accountId}:${input.workspaceId}:${contentSha256}`
2644
+ );
2645
+ const objectKey = `workspaces/${input.workspaceId}/knowledge/blobs/${contentSha256}`;
2646
+ await objectStorage.putObject({
2647
+ key: objectKey,
2648
+ contentType: entry.transfer.contentType,
2649
+ body: bytes,
2650
+ sha256: contentSha256
2651
+ });
2652
+ const file = await ensureKnowledgeSourceBlobFile(db, {
2653
+ accountId: input.accountId,
2654
+ workspaceId: input.workspaceId,
2655
+ fileId,
2656
+ filename: entry.transfer.filename,
2657
+ safeFilename: safeFilename(entry.transfer.filename),
2658
+ contentType: entry.transfer.contentType,
2659
+ sizeBytes: bytes.byteLength,
2660
+ sha256: contentSha256,
2661
+ bucket: objectStorage.bucket,
2662
+ objectKey
2663
+ });
2664
+ const document = await addDocumentToBase(db, {
2665
+ accountId: input.accountId,
2666
+ workspaceId: input.workspaceId,
2667
+ baseId: base.id,
2668
+ fileId: file.id,
2669
+ title: entry.title,
2670
+ sourceKind: "document",
2671
+ sourceUri: entry.sourceUri,
2672
+ sourceExternalId: entry.externalObjectId,
2673
+ sourceTitle: entry.title,
2674
+ sourceCreatedAt: entry.createdTime ?? void 0,
2675
+ sourceUpdatedAt: entry.modifiedTime ?? void 0,
2676
+ sourceVersion: entry.externalVersionId ?? contentSha256,
2677
+ knowledgeSourceIdentity: deterministicUuid(
2678
+ `knowledge-source-version:${object.id}:${observationKey}`
2679
+ ),
2680
+ authorityKind: action.destination.kind,
2681
+ createdBy: action.initiatingSubjectId,
2682
+ initiatingSubjectId: action.initiatingSubjectId,
2683
+ organizationAuthorityGranted: action.destination.kind === "organization",
2684
+ agentAccess: false,
2685
+ curationStatus: "none",
2686
+ access: { viewerSubjectId: action.initiatingSubjectId }
2687
+ });
2688
+ const version = await appendKnowledgeDocumentVersion(db, {
2689
+ accountId: input.accountId,
2690
+ workspaceId: input.workspaceId,
2691
+ objectId: object.id,
2692
+ expectedSourceLifecycleGeneration: resolved.source.lifecycleGeneration,
2693
+ expectedObjectLifecycleGeneration: object.lifecycleGeneration,
2694
+ expectedVersionGeneration: object.versionGeneration,
2695
+ externalVersionId: entry.externalVersionId ?? contentSha256,
2696
+ contentSha256,
2697
+ ingestionKey: `${entry.externalObjectId}:${observationKey}`,
2698
+ sourceCursor: null,
2699
+ sourceMetadata: {
2700
+ title: entry.title,
2701
+ mimeType: entry.mimeType,
2702
+ driveId: entry.driveId,
2703
+ providerRevision: entry.externalVersionId,
2704
+ metadataHash,
2705
+ aclEligibility: "pending",
2706
+ sourceLifecycleGeneration: resolved.source.lifecycleGeneration,
2707
+ objectLifecycleGeneration: object.lifecycleGeneration
2708
+ },
2709
+ sourceCreatedAt: entry.createdTime,
2710
+ sourceUpdatedAt: entry.modifiedTime,
2711
+ aclVersionId: acl.id,
2712
+ aclGeneration,
2713
+ documentId: document.id,
2714
+ fileId: file.id,
2715
+ locationMetadata: { sourceUri: entry.sourceUri },
2716
+ syncObservation,
2717
+ operationId: `document-version:${object.id}:${observationKey}`,
2718
+ reasonCode: "source_content_observed",
2719
+ actor
2720
+ });
2721
+ const obligation = await ensureVersionIndexed({
2722
+ entry,
2723
+ object,
2724
+ version,
2725
+ indexRequired: true
2726
+ });
2727
+ const objectAcl = await recordEntryAcl({ entry, object, version, obligation });
2728
+ summary.indexed += 1;
2729
+ summary.imported += 1;
2730
+ if (objectAcl.aclEligibility === "pending") summary.aclPending += 1;
2731
+ summary.bytes += bytes.byteLength;
2732
+ outcomes.push({
2733
+ externalObjectId: entry.externalObjectId,
2734
+ outcome: "imported",
2735
+ contentSha256,
2736
+ sizeBytes: bytes.byteLength,
2737
+ providerRevision: entry.externalVersionId,
2738
+ metadataHash,
2739
+ aclEligibility: objectAcl.aclEligibility,
2740
+ aclEvidence: objectAcl.aclEvidence,
2741
+ indexObligationId: obligation.id
2742
+ });
2743
+ } catch (error) {
2744
+ if (error instanceof KnowledgeSourceSyncObservationFenceError) {
2745
+ throw new SyncFailure("provider_payload_invalid", false);
2746
+ }
2747
+ const message = boundedError(error);
2748
+ const itemFailure = error instanceof SyncFailure ? error : new SyncFailure("item_processing_failed", false);
2749
+ summary.failed += 1;
2750
+ summary.retryable ||= itemFailure.retryable;
2751
+ if (summary.failures.length < action.limits.maxFailureDetails) {
2752
+ summary.failures.push({
2753
+ externalObjectId: entry.externalObjectId,
2754
+ code: itemFailure.code,
2755
+ retryable: itemFailure.retryable,
2756
+ message
2757
+ });
2758
+ }
2759
+ outcomes.push({
2760
+ externalObjectId: entry.externalObjectId,
2761
+ outcome: "failed",
2762
+ reasonCode: itemFailure.code,
2763
+ detail: message,
2764
+ providerRevision: entry.externalVersionId,
2765
+ metadataHash: entryMetadataHash(entry),
2766
+ aclEligibility: "pending"
2767
+ });
2768
+ }
2769
+ }
2770
+ await recordKnowledgeSourceSyncItemOutcomes(db, {
2771
+ accountId: input.accountId,
2772
+ workspaceId: input.workspaceId,
2773
+ scheduledTaskRunId: input.scheduledTaskRunId,
2774
+ knowledgeSyncRunId: knowledgeRun.id,
2775
+ sourceId: input.sourceId,
2776
+ sourceConfigGeneration: action.sourceConfigGeneration,
2777
+ sourceLifecycleGeneration: action.sourceLifecycleGeneration,
2778
+ outcomes
2779
+ });
2780
+ if (inventory.status === "paused" && inventory.checkpoint) {
2781
+ if (inventory.stopReason === "provider_error" || inventory.stopReason === "incomplete_search") {
2782
+ throw new SyncFailure(
2783
+ inventory.stopReason === "provider_error" ? "provider_unavailable" : "provider_payload_invalid",
2784
+ inventory.stopReason === "provider_error"
2785
+ );
2786
+ }
2787
+ if (inventory.hardLimitReached) {
2788
+ summary.limitReached = inventoryLimit(inventory.stopReason);
2789
+ throw new SyncFailure("resource_limit", false);
2790
+ }
2791
+ if (inventory.stopReason !== "elapsed_time_limit") {
2792
+ summary.limitReached = inventoryLimit(inventory.stopReason);
2793
+ throw new SyncFailure("resource_limit", false);
2794
+ }
2795
+ summary.checkpointed = true;
2796
+ summary.phase = "checkpoint";
2797
+ await checkpointKnowledgeSourceSync(db, {
2798
+ accountId: input.accountId,
2799
+ workspaceId: input.workspaceId,
2800
+ sourceId: input.sourceId,
2801
+ scheduledTaskRunId: input.scheduledTaskRunId,
2802
+ sourceConfigGeneration: action.sourceConfigGeneration,
2803
+ sourceLifecycleGeneration: action.sourceLifecycleGeneration,
2804
+ executionCheckpoint,
2805
+ observationFence: {
2806
+ initiatingSubjectId: action.initiatingSubjectId,
2807
+ scanGeneration: liveState.activeScanGeneration,
2808
+ executionCheckpointGeneration: lease.state.executionCheckpointGeneration,
2809
+ observations: observationFloors
2810
+ }
2811
+ });
2812
+ return { action: "continue", summary };
2813
+ }
2814
+ const tombstonedExternalObjectIds = inventory.authoritativeFullScan ? await reconcileKnowledgeSourceSyncCompleteScan(db, {
2815
+ accountId: input.accountId,
2816
+ workspaceId: input.workspaceId,
2817
+ sourceId: input.sourceId,
2818
+ scheduledTaskRunId: input.scheduledTaskRunId,
2819
+ initiatingSubjectId: action.initiatingSubjectId,
2820
+ sourceSyncGeneration: knowledgeRun.inputSyncGeneration,
2821
+ sourceConfigGeneration: action.sourceConfigGeneration,
2822
+ sourceLifecycleGeneration: action.sourceLifecycleGeneration,
2823
+ scanGeneration: liveState.activeScanGeneration
2824
+ }) : [];
2825
+ if (tombstonedExternalObjectIds.length > 0) {
2826
+ summary.skipped += tombstonedExternalObjectIds.length;
2827
+ await recordKnowledgeSourceSyncItemOutcomes(db, {
2828
+ accountId: input.accountId,
2829
+ workspaceId: input.workspaceId,
2830
+ scheduledTaskRunId: input.scheduledTaskRunId,
2831
+ knowledgeSyncRunId: knowledgeRun.id,
2832
+ sourceId: input.sourceId,
2833
+ sourceConfigGeneration: action.sourceConfigGeneration,
2834
+ sourceLifecycleGeneration: action.sourceLifecycleGeneration,
2835
+ outcomes: tombstonedExternalObjectIds.map((externalObjectId) => ({
2836
+ externalObjectId,
2837
+ outcome: "tombstoned",
2838
+ reasonCode: "authoritative_scan_absent",
2839
+ aclEligibility: "denied"
2840
+ }))
2841
+ });
2842
+ }
2843
+ await completeKnowledgeSyncRun(db, {
2844
+ accountId: input.accountId,
2845
+ workspaceId: input.workspaceId,
2846
+ initiatingSubjectId: action.initiatingSubjectId,
2847
+ runId: knowledgeRun.id,
2848
+ state: "succeeded",
2849
+ outputCursor: null,
2850
+ watermark: (/* @__PURE__ */ new Date()).toISOString(),
2851
+ metadata: { ...summary, providerCursor: inventory.providerCursor },
2852
+ reasonCode: "scheduled_source_sync"
2853
+ });
2854
+ summary.phase = "completed";
2855
+ summary.elapsedMs = Date.now() - startedAt;
2856
+ const settled = await settleKnowledgeSourceSyncLease(db, {
2857
+ accountId: input.accountId,
2858
+ workspaceId: input.workspaceId,
2859
+ sourceId: input.sourceId,
2860
+ scheduledTaskRunId: input.scheduledTaskRunId,
2861
+ knowledgeSyncRunId: knowledgeRun.id,
2862
+ status: "succeeded",
2863
+ summary,
2864
+ sourceConfigGeneration: action.sourceConfigGeneration,
2865
+ sourceLifecycleGeneration: action.sourceLifecycleGeneration,
2866
+ sourceSyncGeneration: knowledgeRun.inputSyncGeneration,
2867
+ completedSourceSyncGeneration: knowledgeRun.inputSyncGeneration + 1,
2868
+ providerCursor: inventory.providerCursor,
2869
+ observationFence: {
2870
+ initiatingSubjectId: action.initiatingSubjectId,
2871
+ scanGeneration: liveState.activeScanGeneration,
2872
+ executionCheckpointGeneration: lease.state.executionCheckpointGeneration,
2873
+ observations: observationFloors
2874
+ }
2875
+ });
2876
+ await recordConnectorUsage("completed", summary.scanned, "item");
2877
+ await recordConnectorUsage("items", summary.imported, "item");
2878
+ await recordConnectorUsage("bytes", summary.bytes, "byte");
2879
+ observability.incrementCounter({
2880
+ name: "opengeni_knowledge_source_sync_runs_total",
2881
+ labels: { provider: providerLabel, outcome: "succeeded" }
2882
+ });
2883
+ observability.incrementCounter({
2884
+ name: "opengeni_knowledge_source_sync_items_total",
2885
+ labels: { provider: providerLabel, outcome: "imported" },
2886
+ amount: summary.imported
2887
+ });
2888
+ for (const outcome of ["unchanged", "skipped", "failed"]) {
2889
+ observability.incrementCounter({
2890
+ name: "opengeni_knowledge_source_sync_items_total",
2891
+ labels: { provider: providerLabel, outcome },
2892
+ amount: summary[outcome]
2893
+ });
2894
+ }
2895
+ observability.incrementCounter({
2896
+ name: "opengeni_knowledge_source_sync_bytes_total",
2897
+ labels: { provider: providerLabel },
2898
+ amount: summary.bytes
2899
+ });
2900
+ recordSyncHealthTelemetry(observability, providerLabel, summary, "succeeded");
2901
+ return {
2902
+ action: "complete",
2903
+ summary,
2904
+ bufferedWake: settled.bufferedWake,
2905
+ bufferedScheduledTaskRunId: settled.bufferedScheduledTaskRunId
2906
+ };
2907
+ } catch (error) {
2908
+ const failure = error instanceof SyncFailure ? error : new SyncFailure("internal_failure", false);
2909
+ summary.phase = "failed";
2910
+ summary.retryable ||= failure.retryable;
2911
+ summary.elapsedMs = Date.now() - startedAt;
2912
+ summary.reconnectRequired = failure.reconnectRequired;
2913
+ summary.failed = Math.max(summary.failed, 1);
2914
+ if (summary.failures.length === 0) {
2915
+ summary.failures.push({
2916
+ externalObjectId: input.sourceId,
2917
+ code: failure.code,
2918
+ retryable: failure.retryable,
2919
+ message: boundedError(error)
2920
+ });
2921
+ }
2922
+ if (knowledgeRunId) {
2923
+ await completeKnowledgeSyncRun(db, {
2924
+ accountId: input.accountId,
2925
+ workspaceId: input.workspaceId,
2926
+ initiatingSubjectId: action.initiatingSubjectId,
2927
+ runId: knowledgeRunId,
2928
+ state: "failed",
2929
+ metadata: summary,
2930
+ errorCode: failure.code,
2931
+ reasonCode: "scheduled_source_sync_failed"
2932
+ }).catch(() => void 0);
2933
+ }
2934
+ const settlementInput = {
2935
+ accountId: input.accountId,
2936
+ workspaceId: input.workspaceId,
2937
+ sourceId: input.sourceId,
2938
+ scheduledTaskRunId: input.scheduledTaskRunId,
2939
+ knowledgeSyncRunId: knowledgeRunId,
2940
+ status: "failed",
2941
+ summary,
2942
+ error: failure.code,
2943
+ sourceConfigGeneration: action.sourceConfigGeneration,
2944
+ sourceLifecycleGeneration: action.sourceLifecycleGeneration,
2945
+ sourceSyncGeneration: activeSourceSyncGeneration ?? lease.state.sourceSyncGeneration,
2946
+ // Failed or incomplete provider walks are not authoritative scans.
2947
+ // A later scheduled run must restart from the root under a fresh
2948
+ // observation generation before it can infer object absence.
2949
+ executionCheckpoint: null
2950
+ };
2951
+ const settled = failure.reconnectRequired ? await deauthorizeAndSettleKnowledgeSourceSyncFailure(db, {
2952
+ deauthorization: {
2953
+ accountId: input.accountId,
2954
+ workspaceId: input.workspaceId,
2955
+ sourceId: input.sourceId,
2956
+ audience: action.destination,
2957
+ operationId: `knowledge-sync-deauthorize:${input.sourceId}:${action.connection.connectionVersion}:${failure.code}`,
2958
+ reasonCode: "connection_reconnect_required",
2959
+ actor: {
2960
+ kind: "service",
2961
+ subjectId: "knowledge-source-sync",
2962
+ initiatingHumanSubjectId: action.initiatingSubjectId
2963
+ }
2964
+ },
2965
+ settlement: settlementInput
2966
+ }) : await settleKnowledgeSourceSyncLease(db, settlementInput);
2967
+ await recordConnectorUsage("completed", summary.scanned, "item");
2968
+ await recordConnectorUsage("items", summary.imported, "item");
2969
+ await recordConnectorUsage("bytes", summary.bytes, "byte");
2970
+ observability.incrementCounter({
2971
+ name: "opengeni_knowledge_source_sync_runs_total",
2972
+ labels: { provider: providerLabel, outcome: "failed" }
2973
+ });
2974
+ for (const outcome of ["imported", "unchanged", "skipped", "failed"]) {
2975
+ observability.incrementCounter({
2976
+ name: "opengeni_knowledge_source_sync_items_total",
2977
+ labels: { provider: providerLabel, outcome },
2978
+ amount: summary[outcome]
2979
+ });
2980
+ }
2981
+ observability.incrementCounter({
2982
+ name: "opengeni_knowledge_source_sync_bytes_total",
2983
+ labels: { provider: providerLabel },
2984
+ amount: summary.bytes
2985
+ });
2986
+ recordSyncHealthTelemetry(observability, providerLabel, summary, "failed", failure);
2987
+ return {
2988
+ action: "failed",
2989
+ summary,
2990
+ errorCode: failure.code,
2991
+ bufferedWake: settled.bufferedWake,
2992
+ bufferedScheduledTaskRunId: settled.bufferedScheduledTaskRunId
2993
+ };
2994
+ }
2995
+ async function failWithoutProvider(code) {
2996
+ const terminalSummary = {
2997
+ phase: "failed",
2998
+ scanned: 0,
2999
+ imported: 0,
3000
+ unchanged: 0,
3001
+ skipped: 0,
3002
+ failed: 1,
3003
+ bytes: 0,
3004
+ providerRequests: 0,
3005
+ elapsedMs: 0,
3006
+ indexed: 0,
3007
+ aclPending: 0,
3008
+ retryable: false,
3009
+ limitReached: null,
3010
+ checkpointed: false,
3011
+ reconnectRequired: false,
3012
+ failures: [
3013
+ {
3014
+ externalObjectId: input.sourceId,
3015
+ code: "authority_changed",
3016
+ retryable: false,
3017
+ message: code
3018
+ }
3019
+ ]
3020
+ };
3021
+ await updateScheduledTaskRun(db, input.workspaceId, input.scheduledTaskRunId, {
3022
+ knowledgeSummary: terminalSummary
3023
+ }).catch(() => void 0);
3024
+ await completeKnowledgeSourceSyncWakeWithoutLease(db, {
3025
+ accountId: input.accountId,
3026
+ workspaceId: input.workspaceId,
3027
+ sourceId: input.sourceId,
3028
+ scheduledTaskRunId: input.scheduledTaskRunId
3029
+ }).catch(() => void 0);
3030
+ await recordConnectorUsage("completed", 0, "item").catch(() => void 0);
3031
+ observability.incrementCounter({
3032
+ name: "opengeni_knowledge_source_sync_runs_total",
3033
+ labels: { provider: "unknown", outcome: "failed_preflight" }
3034
+ });
3035
+ return { action: "failed", bufferedWake: false, summary: terminalSummary, errorCode: code };
3036
+ }
3037
+ async function recordConnectorUsage(kind, quantity, unit) {
3038
+ if (quantity === 0 && kind !== "completed") return;
3039
+ await recordUsageEvent(db, {
3040
+ accountId: input.accountId,
3041
+ workspaceId: input.workspaceId,
3042
+ eventType: `knowledge_source_sync.${kind}`,
3043
+ quantity: kind === "completed" ? 1 : quantity,
3044
+ unit,
3045
+ sourceResourceType: "scheduled_task_run",
3046
+ sourceResourceId: input.scheduledTaskRunId,
3047
+ initiator: { kind: "service", subjectId: "knowledge-source-sync" },
3048
+ initiatorContext: {
3049
+ scheduledTaskId: input.taskId,
3050
+ scheduledTaskRunId: input.scheduledTaskRunId
3051
+ },
3052
+ origin: "scheduled_task",
3053
+ idempotencyKey: `usage:knowledge_source_sync.${kind}:${input.scheduledTaskRunId}`
3054
+ });
3055
+ }
3056
+ }
3057
+ };
3058
+ }
3059
+ async function deauthorizeAndSettleKnowledgeSourceSyncFailure(db, input) {
3060
+ try {
3061
+ await deauthorizeKnowledgeSourceRetrieval(db, input.deauthorization);
3062
+ } catch (error) {
3063
+ await releaseKnowledgeSourceSyncLeaseForRetry(db, {
3064
+ accountId: input.settlement.accountId,
3065
+ workspaceId: input.settlement.workspaceId,
3066
+ sourceId: input.settlement.sourceId,
3067
+ scheduledTaskRunId: input.settlement.scheduledTaskRunId
3068
+ });
3069
+ throw error;
3070
+ }
3071
+ return await settleKnowledgeSourceSyncLease(db, input.settlement);
3072
+ }
3073
+ async function resolveKnowledgeSyncProvider(input) {
3074
+ const { action } = input;
3075
+ const connection = await getConnectionMetadata2(
3076
+ input.db,
3077
+ input.workspaceId,
3078
+ action.connection.connectionId,
3079
+ action.initiatingSubjectId
3080
+ );
3081
+ if (!connection || connection.accountId !== input.accountId || connection.subjectId !== action.connection.ownerSubjectId || connection.providerDomain.toLowerCase() !== action.connection.providerDomain.toLowerCase() || connection.kind !== action.connection.kind || connection.version < action.connection.connectionVersion || connection.status !== "active") {
3082
+ throw new SyncFailure("connection_reconnect_required", true, true);
3083
+ }
3084
+ const googleInitial = connection.providerDomain === GOOGLE_DRIVE_PROVIDER_DOMAIN ? GoogleDriveConnectionMetadata.safeParse(connection.metadata) : null;
3085
+ const atlassianInitial = connection.providerDomain === ATLASSIAN_PROVIDER_DOMAIN ? AtlassianConnectionMetadata.safeParse(connection.metadata) : null;
3086
+ const initialAtlassianMetadata = atlassianInitial?.success ? atlassianInitial.data : null;
3087
+ if (!googleInitial?.success && !initialAtlassianMetadata) {
3088
+ throw new SyncFailure("provider_rejected", false);
3089
+ }
3090
+ if (googleInitial?.success && !googleDriveScopesAllowCapability(connection.grantedScopes, "recursive_source_sync")) {
3091
+ throw new SyncFailure("connection_reconnect_required", true, true);
3092
+ }
3093
+ if (atlassianInitial?.success && !atlassianScopesAllowRead(connection.grantedScopes)) {
3094
+ throw new SyncFailure("connection_reconnect_required", true, true);
3095
+ }
3096
+ const initialLifecycle = googleInitial?.success ? googleInitial.data.lifecycle : initialAtlassianMetadata.lifecycle;
3097
+ if (initialLifecycle?.state && initialLifecycle.state !== "active") {
3098
+ throw new SyncFailure("connection_reconnect_required", true, true);
3099
+ }
3100
+ const initialSource = googleInitial?.success ? selectedDriveSource(googleInitial.data, input.externalSourceId) : selectedAtlassianSource(initialAtlassianMetadata, input.externalSourceId);
3101
+ if (!initialSource || !initialSource.syncEnabled || initialSource.configGeneration !== action.sourceConfigGeneration || !initialSource.destination || scopedKnowledgeScopeKey(
3102
+ googleInitial?.success ? googleDriveKnowledgeScope(initialSource.destination) : atlassianKnowledgeScope(initialSource.destination)
3103
+ ) !== scopedKnowledgeScopeKey(action.destination)) {
3104
+ throw new SyncFailure("authority_changed", false);
3105
+ }
3106
+ const token = await buildConnectionTokenResolver(
3107
+ input.db,
3108
+ input.settings
3109
+ )({
3110
+ workspaceId: input.workspaceId,
3111
+ subjectId: action.initiatingSubjectId,
3112
+ serverId: "knowledge-source-sync",
3113
+ connectionRef: {
3114
+ connectionId: connection.id,
3115
+ providerDomain: connection.providerDomain,
3116
+ kind: connection.kind,
3117
+ subjectScope: "subject"
3118
+ },
3119
+ destinationUrl: connection.providerDomain === ATLASSIAN_PROVIDER_DOMAIN ? ATLASSIAN_API_BASE : "https://www.googleapis.com"
3120
+ });
3121
+ if (token.status !== "ok") {
3122
+ throw new SyncFailure("connection_reconnect_required", true, true);
3123
+ }
3124
+ const resolvedConnection = await getConnectionMetadata2(
3125
+ input.db,
3126
+ input.workspaceId,
3127
+ action.connection.connectionId,
3128
+ action.initiatingSubjectId
3129
+ );
3130
+ if (!resolvedConnection || resolvedConnection.accountId !== input.accountId || resolvedConnection.subjectId !== action.connection.ownerSubjectId || resolvedConnection.providerDomain.toLowerCase() !== action.connection.providerDomain.toLowerCase() || resolvedConnection.kind !== action.connection.kind || resolvedConnection.version !== token.connectionVersion || resolvedConnection.status !== "active") {
3131
+ throw new SyncFailure("connection_reconnect_required", true, true);
3132
+ }
3133
+ const authorization = token.headers.Authorization ?? token.headers.authorization;
3134
+ if (resolvedConnection.providerDomain === GOOGLE_DRIVE_PROVIDER_DOMAIN) {
3135
+ const metadata = GoogleDriveConnectionMetadata.parse(resolvedConnection.metadata);
3136
+ if (!googleDriveSyncProviderAccessAllowed({
3137
+ connectionVersion: resolvedConnection.version,
3138
+ resolvedConnectionVersion: token.connectionVersion,
3139
+ connectionStatus: resolvedConnection.status,
3140
+ lifecycleState: metadata.lifecycle?.state,
3141
+ grantedScopes: resolvedConnection.grantedScopes
3142
+ })) {
3143
+ throw new SyncFailure("connection_reconnect_required", true, true);
3144
+ }
3145
+ const source = selectedDriveSource(metadata, input.externalSourceId);
3146
+ if (!validSelectedSource(source, action, googleDriveKnowledgeScope)) {
3147
+ throw new SyncFailure("authority_changed", false);
3148
+ }
3149
+ return {
3150
+ kind: "google_drive",
3151
+ selectedDestination: source.destination,
3152
+ aclAuthority: {
3153
+ connectionId: resolvedConnection.id,
3154
+ connectionVersion: resolvedConnection.version,
3155
+ sourceGooglePermissionId: metadata.googlePermissionId
3156
+ },
3157
+ driver: googleDriveSyncDriver({
3158
+ actionProviderCoordinationKey: action.providerCoordinationKey,
3159
+ metadata,
3160
+ selectedSource: source,
3161
+ accountId: input.accountId,
3162
+ workspaceId: input.workspaceId,
3163
+ initiatingSubjectId: action.initiatingSubjectId,
3164
+ authorization,
3165
+ retry: googleDriveProviderRetryOptions(input.settings),
3166
+ observability: input.observability,
3167
+ limits: action.limits,
3168
+ connectionId: resolvedConnection.id,
3169
+ fullReconciliationIntervalMs: GOOGLE_DRIVE_FULL_RECONCILIATION_INTERVAL_MS,
3170
+ observedExternalObjectIds: async (externalObjectIds) => await listObservedKnowledgeSourceSyncExternalObjectIds(input.db, {
3171
+ accountId: input.accountId,
3172
+ workspaceId: input.workspaceId,
3173
+ sourceId: input.sourceId,
3174
+ externalObjectIds
3175
+ })
3176
+ })
3177
+ };
3178
+ }
3179
+ if (resolvedConnection.providerDomain === ATLASSIAN_PROVIDER_DOMAIN) {
3180
+ const metadata = AtlassianConnectionMetadata.parse(resolvedConnection.metadata);
3181
+ if (metadata.lifecycle?.state !== "active" || !atlassianScopesAllowRead(resolvedConnection.grantedScopes)) {
3182
+ throw new SyncFailure("connection_reconnect_required", true, true);
3183
+ }
3184
+ const source = selectedAtlassianSource(metadata, input.externalSourceId);
3185
+ if (!validSelectedSource(source, action, atlassianKnowledgeScope)) {
3186
+ throw new SyncFailure("authority_changed", false);
3187
+ }
3188
+ return {
3189
+ kind: "atlassian",
3190
+ selectedDestination: source.destination,
3191
+ aclAuthority: null,
3192
+ driver: atlassianSyncDriver({
3193
+ actionProviderCoordinationKey: action.providerCoordinationKey,
3194
+ metadata,
3195
+ selectedSource: source,
3196
+ authorization,
3197
+ limits: action.limits
3198
+ })
3199
+ };
3200
+ }
3201
+ throw new SyncFailure("provider_rejected", false);
3202
+ }
3203
+ function validSelectedSource(source, action, scope) {
3204
+ return Boolean(
3205
+ source && source.syncEnabled && source.configGeneration === action.sourceConfigGeneration && source.destination && scopedKnowledgeScopeKey(scope(source.destination)) === scopedKnowledgeScopeKey(action.destination)
3206
+ );
3207
+ }
3208
+ function selectedAtlassianSource(metadata, externalSourceId) {
3209
+ return metadata.selectedSources.find((source) => source.id === externalSourceId) ?? null;
3210
+ }
3211
+ function atlassianSyncDriver(input) {
3212
+ return {
3213
+ providerKey: "atlassian",
3214
+ providerDomain: ATLASSIAN_PROVIDER_DOMAIN,
3215
+ providerCoordinationKey: input.actionProviderCoordinationKey,
3216
+ inventory: async (checkpoint) => {
3217
+ const inventory = await inventoryAtlassianSource({
3218
+ cloudId: input.selectedSource.cloudId,
3219
+ source: input.selectedSource,
3220
+ limits: {
3221
+ maxItems: input.limits.maxItems,
3222
+ maxApiRequests: input.limits.maxProviderRequests,
3223
+ maxElapsedMs: input.limits.maxElapsedSeconds * 1e3,
3224
+ pageSize: Math.min(input.limits.maxItems, 100)
3225
+ },
3226
+ checkpoint,
3227
+ listPage: async (cursor, pageSize) => await listAtlassianItems(input.selectedSource, cursor, pageSize, input.authorization)
3228
+ });
3229
+ return {
3230
+ ...inventory,
3231
+ providerCursor: null,
3232
+ authoritativeFullScan: inventory.status === "complete",
3233
+ cursorInvalidated: false,
3234
+ hardLimitReached: inventory.status === "paused" && inventory.stopReason !== "elapsed_time_limit" && inventory.stopReason !== "provider_error"
3235
+ };
3236
+ },
3237
+ fetchContent: async (entry, maxBytes) => await fetchAtlassianContent(input.selectedSource, entry, input.authorization, maxBytes),
3238
+ citationLocator: (entry) => ({
3239
+ version: 1,
3240
+ providerKey: "atlassian",
3241
+ providerCoordinationKey: input.actionProviderCoordinationKey,
3242
+ cloudId: input.selectedSource.cloudId,
3243
+ sourceKind: input.selectedSource.kind,
3244
+ externalObjectId: entry.externalObjectId,
3245
+ sourceUri: entry.sourceUri
3246
+ })
3247
+ };
3248
+ }
3249
+ function googleDriveSyncDriver(input) {
3250
+ const maxElapsedMs = input.limits.maxElapsedSeconds * 1e3;
3251
+ const provider = input.provider ?? {
3252
+ getStartPageToken: async (driveId) => await getDriveStartPageToken(driveId, input.authorization, input.retry, input.observability),
3253
+ listChanges: async (pageToken, driveId, pageSize) => await listDriveChanges(
3254
+ pageToken,
3255
+ driveId,
3256
+ pageSize,
3257
+ input.authorization,
3258
+ input.retry,
3259
+ input.observability
3260
+ ),
3261
+ getFile: async (fileId) => await getDriveFile(fileId, input.authorization, input.retry, input.observability),
3262
+ listChildren: async (request) => await listDriveChildren(request, input.authorization, input.retry, input.observability),
3263
+ listPermissions: async (request) => await listDrivePermissions(request, input.authorization, input.retry, input.observability)
3264
+ };
3265
+ const clock = provider.now ?? Date.now;
3266
+ const budgetLimits = {
3267
+ maxItems: input.limits.maxItems,
3268
+ maxProviderRequests: input.limits.maxProviderRequests,
3269
+ maxElapsedMs
3270
+ };
3271
+ return {
3272
+ providerKey: "google_drive",
3273
+ providerDomain: GOOGLE_DRIVE_PROVIDER_DOMAIN,
3274
+ providerCoordinationKey: input.actionProviderCoordinationKey,
3275
+ inventory: async (executionCheckpoint, providerCursor) => {
3276
+ const expectedCursor = {
3277
+ connectionId: input.connectionId,
3278
+ googlePermissionId: input.metadata.googlePermissionId,
3279
+ sourceId: input.selectedSource.id,
3280
+ driveId: input.selectedSource.driveId
3281
+ };
3282
+ const legacyFullCheckpoint = isLegacyGoogleDriveFullReconciliationCheckpoint(
3283
+ executionCheckpoint,
3284
+ expectedCursor
3285
+ );
3286
+ const cursor = parseGoogleDriveChangesCursor(providerCursor, expectedCursor);
3287
+ const checkpoint = parseGoogleDriveExecutionCheckpoint(
3288
+ executionCheckpoint,
3289
+ expectedCursor,
3290
+ budgetLimits
3291
+ );
3292
+ const now = new Date(clock());
3293
+ const runFullReconciliation = async (full) => {
3294
+ const inventoryItems = full.inventoryCheckpoint?.totals.itemCount ?? 0;
3295
+ const inventoryProviderRequests = full.inventoryCheckpoint?.totals.apiRequestCount ?? 0;
3296
+ const budgetBeforeInvocation = {
3297
+ examinedItems: full.budgetBeforeInventory.examinedItems + inventoryItems,
3298
+ providerRequests: full.budgetBeforeInventory.providerRequests + inventoryProviderRequests,
3299
+ elapsedMs: full.budgetBeforeInventory.elapsedMs + full.inventoryElapsedMs
3300
+ };
3301
+ const hardStopReason = googleDriveHardLimitReason(budgetBeforeInvocation, budgetLimits);
3302
+ const fullCheckpoint = buildGoogleDriveFullReconciliationCheckpoint({
3303
+ ...expectedCursor,
3304
+ ...full
3305
+ });
3306
+ if (hardStopReason) {
3307
+ return {
3308
+ status: "paused",
3309
+ stopReason: hardStopReason,
3310
+ entries: [],
3311
+ checkpoint: fullCheckpoint,
3312
+ providerCursor,
3313
+ authoritativeFullScan: false,
3314
+ cursorInvalidated: full.cursorInvalidated,
3315
+ providerRequests: budgetBeforeInvocation.providerRequests,
3316
+ elapsedMs: budgetBeforeInvocation.elapsedMs,
3317
+ hardLimitReached: true
3318
+ };
3319
+ }
3320
+ const phaseMaxItems = input.limits.maxItems - full.budgetBeforeInventory.examinedItems;
3321
+ const phaseMaxProviderRequests = input.limits.maxProviderRequests - full.budgetBeforeInventory.providerRequests;
3322
+ const remainingElapsedMs = maxElapsedMs - budgetBeforeInvocation.elapsedMs;
3323
+ const inventory = await inventoryGoogleDriveSource({
3324
+ googlePermissionId: input.metadata.googlePermissionId,
3325
+ googleEmail: input.metadata.googleEmail,
3326
+ source: input.selectedSource,
3327
+ accountId: input.accountId,
3328
+ workspaceId: input.workspaceId,
3329
+ connectionSubjectId: input.initiatingSubjectId,
3330
+ limits: {
3331
+ maxItems: phaseMaxItems,
3332
+ maxKnownBytes: input.limits.maxBytes,
3333
+ maxApiRequests: phaseMaxProviderRequests,
3334
+ maxElapsedMs: Math.min(GOOGLE_DRIVE_SYNC_INVOCATION_SLICE_MS, remainingElapsedMs),
3335
+ maxFileBytes: input.limits.maxFileBytes,
3336
+ maxFolders: Math.min(phaseMaxItems * 2, 2e3),
3337
+ pageSize: Math.min(phaseMaxItems, 100)
3338
+ },
3339
+ checkpoint: full.inventoryCheckpoint,
3340
+ listChildren: provider.listChildren,
3341
+ now: clock
3342
+ });
3343
+ const inventoryElapsedMs = full.inventoryElapsedMs + inventory.run.elapsedMs;
3344
+ const budget = {
3345
+ examinedItems: full.budgetBeforeInventory.examinedItems + inventory.totals.itemCount,
3346
+ providerRequests: full.budgetBeforeInventory.providerRequests + inventory.totals.apiRequestCount,
3347
+ elapsedMs: full.budgetBeforeInventory.elapsedMs + inventoryElapsedMs
3348
+ };
3349
+ const reconciled = reconcileGoogleDriveRevisionFloors(
3350
+ inventory.entries,
3351
+ full.revisionFloors,
3352
+ input.limits.maxItems
3353
+ );
3354
+ if (reconciled.conflict) {
3355
+ return {
3356
+ status: "paused",
3357
+ stopReason: "incomplete_search",
3358
+ entries: [],
3359
+ checkpoint: fullCheckpoint,
3360
+ providerCursor,
3361
+ authoritativeFullScan: false,
3362
+ cursorInvalidated: full.cursorInvalidated,
3363
+ providerRequests: budget.providerRequests,
3364
+ elapsedMs: budget.elapsedMs,
3365
+ hardLimitReached: false
3366
+ };
3367
+ }
3368
+ const nextCursor = inventory.status === "complete" ? buildGoogleDriveChangesCursor({
3369
+ ...expectedCursor,
3370
+ boundaryId: full.boundaryId,
3371
+ pageToken: full.startPageToken,
3372
+ previousGeneration: cursor?.cursorGeneration ?? 0,
3373
+ reconciledAt: now,
3374
+ fullReconciliationIntervalMs: input.fullReconciliationIntervalMs
3375
+ }) : providerCursor;
3376
+ return {
3377
+ status: inventory.status,
3378
+ stopReason: inventory.stopReason,
3379
+ entries: reconciled.entries,
3380
+ checkpoint: inventory.status === "paused" && inventory.checkpoint ? buildGoogleDriveFullReconciliationCheckpoint({
3381
+ ...expectedCursor,
3382
+ boundaryId: full.boundaryId,
3383
+ startPageToken: full.startPageToken,
3384
+ cursorInvalidated: full.cursorInvalidated,
3385
+ budgetBeforeInventory: full.budgetBeforeInventory,
3386
+ inventoryElapsedMs,
3387
+ inventoryCheckpoint: inventory.checkpoint,
3388
+ revisionFloors: reconciled.revisionFloors
3389
+ }) : null,
3390
+ providerCursor: nextCursor,
3391
+ authoritativeFullScan: inventory.status === "complete",
3392
+ cursorInvalidated: full.cursorInvalidated,
3393
+ providerRequests: budget.providerRequests,
3394
+ elapsedMs: budget.elapsedMs,
3395
+ hardLimitReached: inventory.status === "paused" && googleDriveInventoryStopIsHard(inventory.stopReason, budget, budgetLimits)
3396
+ };
3397
+ };
3398
+ const acquireFullReconciliation = async (details) => {
3399
+ const acquisitionStartedAt = clock();
3400
+ const initialElapsedMs = details.budget.elapsedMs;
3401
+ const budget = { ...details.budget };
3402
+ const refreshElapsed = () => {
3403
+ budget.elapsedMs = initialElapsedMs + Math.max(0, clock() - acquisitionStartedAt);
3404
+ };
3405
+ const requireProviderCapacity = () => {
3406
+ refreshElapsed();
3407
+ if (googleDriveHardLimitReason(budget, budgetLimits)) {
3408
+ throw new SyncFailure("resource_limit", false);
3409
+ }
3410
+ budget.providerRequests += 1;
3411
+ };
3412
+ requireProviderCapacity();
3413
+ const startPageToken = await provider.getStartPageToken(input.selectedSource.driveId);
3414
+ refreshElapsed();
3415
+ let boundaryId = details.boundaryId;
3416
+ if (!boundaryId) {
3417
+ if (input.selectedSource.id === "root" && input.selectedSource.driveId === null) {
3418
+ requireProviderCapacity();
3419
+ try {
3420
+ boundaryId = await resolveGoogleDriveChangesBoundaryId({
3421
+ source: input.selectedSource,
3422
+ getFile: provider.getFile
3423
+ });
3424
+ } catch (error) {
3425
+ if (error instanceof GoogleDriveChangesProtocolError) {
3426
+ throw new SyncFailure("provider_payload_invalid", false);
3427
+ }
3428
+ throw error;
3429
+ }
3430
+ refreshElapsed();
3431
+ } else {
3432
+ boundaryId = input.selectedSource.id;
3433
+ }
3434
+ }
3435
+ return {
3436
+ boundaryId,
3437
+ startPageToken,
3438
+ cursorInvalidated: details.cursorInvalidated,
3439
+ budgetBeforeInventory: budget,
3440
+ inventoryElapsedMs: 0,
3441
+ inventoryCheckpoint: null,
3442
+ revisionFloors: []
3443
+ };
3444
+ };
3445
+ if (checkpoint?.kind === "google_drive_full_reconciliation") {
3446
+ return await runFullReconciliation(checkpoint);
3447
+ }
3448
+ if (!cursor) {
3449
+ const budget = checkpoint?.kind === "google_drive_changes" ? checkpoint.changesCheckpoint.budget : emptyGoogleDriveSyncBudget();
3450
+ return await runFullReconciliation(
3451
+ await acquireFullReconciliation({
3452
+ budget,
3453
+ boundaryId: null,
3454
+ cursorInvalidated: providerCursor !== null
3455
+ })
3456
+ );
3457
+ }
3458
+ if (!checkpoint && !legacyFullCheckpoint && googleDriveFullReconciliationDue(cursor, now)) {
3459
+ return await runFullReconciliation(
3460
+ await acquireFullReconciliation({
3461
+ budget: emptyGoogleDriveSyncBudget(),
3462
+ boundaryId: cursor.boundaryId,
3463
+ cursorInvalidated: false
3464
+ })
3465
+ );
3466
+ }
3467
+ try {
3468
+ const forceFullReconciliation = legacyFullCheckpoint || googleDriveFullReconciliationDue(cursor, now);
3469
+ const changesCheckpoint = checkpoint?.kind === "google_drive_changes" ? {
3470
+ ...checkpoint.changesCheckpoint,
3471
+ requiresFullReconciliation: checkpoint.changesCheckpoint.requiresFullReconciliation || forceFullReconciliation
3472
+ } : null;
3473
+ const changes = await drainGoogleDriveChanges({
3474
+ source: input.selectedSource,
3475
+ cursor,
3476
+ checkpoint: changesCheckpoint,
3477
+ maxItems: input.limits.maxItems,
3478
+ maxProviderRequests: input.limits.maxProviderRequests,
3479
+ maxElapsedMs,
3480
+ maxInvocationElapsedMs: Math.min(GOOGLE_DRIVE_SYNC_INVOCATION_SLICE_MS, maxElapsedMs),
3481
+ maxFileBytes: input.limits.maxFileBytes,
3482
+ listChanges: async (pageToken, pageSize) => await provider.listChanges(pageToken, input.selectedSource.driveId, pageSize),
3483
+ getFile: provider.getFile,
3484
+ observedExternalObjectIds: input.observedExternalObjectIds,
3485
+ now: clock
3486
+ });
3487
+ if (changes.status === "paused" && changes.checkpoint) {
3488
+ return {
3489
+ status: "paused",
3490
+ stopReason: changes.stopReason,
3491
+ entries: changes.entries,
3492
+ checkpoint: {
3493
+ version: 2,
3494
+ kind: "google_drive_changes",
3495
+ ...expectedCursor,
3496
+ changesCheckpoint: {
3497
+ ...changes.checkpoint,
3498
+ requiresFullReconciliation: changes.checkpoint.requiresFullReconciliation || forceFullReconciliation
3499
+ }
3500
+ },
3501
+ providerCursor,
3502
+ authoritativeFullScan: false,
3503
+ cursorInvalidated: false,
3504
+ providerRequests: changes.providerRequests,
3505
+ elapsedMs: changes.elapsedMs,
3506
+ hardLimitReached: changes.hardLimitReached
3507
+ };
3508
+ }
3509
+ if (!changes.newStartPageToken) throw new Error("google_drive_changes_cursor_missing");
3510
+ if (changes.requiresFullReconciliation || forceFullReconciliation) {
3511
+ const hardStopReason = googleDriveHardLimitReason(changes.budget, budgetLimits);
3512
+ return {
3513
+ status: "paused",
3514
+ stopReason: hardStopReason ?? "elapsed_time_limit",
3515
+ entries: changes.entries,
3516
+ checkpoint: buildGoogleDriveFullReconciliationCheckpoint({
3517
+ ...expectedCursor,
3518
+ boundaryId: cursor.boundaryId,
3519
+ startPageToken: changes.newStartPageToken,
3520
+ cursorInvalidated: false,
3521
+ budgetBeforeInventory: changes.budget,
3522
+ inventoryElapsedMs: 0,
3523
+ inventoryCheckpoint: null,
3524
+ revisionFloors: revisionFloorsForEntries(changes.entries, input.limits.maxItems)
3525
+ }),
3526
+ providerCursor,
3527
+ authoritativeFullScan: false,
3528
+ cursorInvalidated: false,
3529
+ providerRequests: changes.providerRequests,
3530
+ elapsedMs: changes.elapsedMs,
3531
+ hardLimitReached: hardStopReason !== null
3532
+ };
3533
+ }
3534
+ return {
3535
+ status: "complete",
3536
+ stopReason: null,
3537
+ entries: changes.entries,
3538
+ checkpoint: null,
3539
+ providerCursor: advanceGoogleDriveChangesCursor(cursor, changes.newStartPageToken),
3540
+ authoritativeFullScan: false,
3541
+ cursorInvalidated: false,
3542
+ providerRequests: changes.providerRequests,
3543
+ elapsedMs: changes.elapsedMs,
3544
+ hardLimitReached: false
3545
+ };
3546
+ } catch (error) {
3547
+ if (error instanceof GoogleDriveChangesProtocolError) {
3548
+ throw new SyncFailure("provider_payload_invalid", false);
3549
+ }
3550
+ if (!(error instanceof GoogleDriveCursorInvalidError)) throw error;
3551
+ const full = await acquireFullReconciliation({
3552
+ budget: error.budget ?? emptyGoogleDriveSyncBudget(),
3553
+ boundaryId: cursor.boundaryId,
3554
+ cursorInvalidated: true
3555
+ });
3556
+ const hardStopReason = googleDriveHardLimitReason(full.budgetBeforeInventory, budgetLimits);
3557
+ return {
3558
+ status: "paused",
3559
+ stopReason: hardStopReason ?? "elapsed_time_limit",
3560
+ entries: [],
3561
+ checkpoint: buildGoogleDriveFullReconciliationCheckpoint({
3562
+ ...expectedCursor,
3563
+ ...full
3564
+ }),
3565
+ providerCursor,
3566
+ authoritativeFullScan: false,
3567
+ cursorInvalidated: true,
3568
+ providerRequests: full.budgetBeforeInventory.providerRequests,
3569
+ elapsedMs: full.budgetBeforeInventory.elapsedMs,
3570
+ hardLimitReached: hardStopReason !== null
3571
+ };
3572
+ }
3573
+ },
3574
+ fetchContent: async (entry, maxBytes) => await fetchDriveBytes(entry, input.authorization, maxBytes, input.retry, input.observability),
3575
+ readAcl: async (entry, maxProviderRequests) => {
3576
+ if (!provider.listPermissions) {
3577
+ throw new SyncFailure("provider_rejected", false);
3578
+ }
3579
+ return await collectGoogleDriveAclEvidence({
3580
+ entry,
3581
+ maxProviderRequests,
3582
+ listPermissions: provider.listPermissions,
3583
+ getFile: provider.getFile,
3584
+ now: clock
3585
+ });
3586
+ },
3587
+ citationLocator: (entry) => ({
3588
+ version: 2,
3589
+ providerKey: "google-drive",
3590
+ providerCoordinationKey: input.actionProviderCoordinationKey,
3591
+ externalObjectId: entry.externalObjectId,
3592
+ providerRevision: entry.externalVersionId,
3593
+ driveId: entry.driveId,
3594
+ sourceUri: entry.sourceUri
3595
+ })
3596
+ };
3597
+ }
3598
+ function isLegacyGoogleDriveFullReconciliationCheckpoint(value, expected) {
3599
+ return value?.version === 2 && value.kind === "google_drive_full_reconciliation" && value.connectionId === expected.connectionId && value.googlePermissionId === expected.googlePermissionId && value.sourceId === expected.sourceId && value.driveId === expected.driveId && encodedJsonBytes(value) <= GOOGLE_DRIVE_EXECUTION_CHECKPOINT_MAX_BYTES;
3600
+ }
3601
+ function parseGoogleDriveExecutionCheckpoint(value, expected, limits) {
3602
+ if (!value || value.connectionId !== expected.connectionId || value.googlePermissionId !== expected.googlePermissionId || value.sourceId !== expected.sourceId || value.driveId !== expected.driveId || encodedJsonBytes(value) > GOOGLE_DRIVE_EXECUTION_CHECKPOINT_MAX_BYTES) {
3603
+ return null;
3604
+ }
3605
+ if (value.version === 2 && value.kind === "google_drive_changes") {
3606
+ const changesCheckpoint = parseGoogleDriveChangesCheckpoint(value.changesCheckpoint, limits);
3607
+ return changesCheckpoint ? { ...value, changesCheckpoint } : null;
3608
+ }
3609
+ if (value.version === 3 && value.kind === "google_drive_full_reconciliation") {
3610
+ const budgetBeforeInventory = parseGoogleDriveSyncBudget(value.budgetBeforeInventory, limits);
3611
+ const revisionFloors = parseGoogleDriveRevisionFloors(value.revisionFloors, limits.maxItems);
3612
+ if (typeof value.boundaryId !== "string" || value.boundaryId.length < 1 || value.boundaryId.length > 1024 || typeof value.startPageToken !== "string" || value.startPageToken.length < 1 || value.startPageToken.length > 4096 || typeof value.cursorInvalidated !== "boolean" || !budgetBeforeInventory || !revisionFloors || !Number.isSafeInteger(value.inventoryElapsedMs) || Number(value.inventoryElapsedMs) < 0 || Number(value.inventoryElapsedMs) > limits.maxElapsedMs || budgetBeforeInventory.elapsedMs + Number(value.inventoryElapsedMs) > limits.maxElapsedMs || value.inventoryCheckpoint !== null && (!value.inventoryCheckpoint || typeof value.inventoryCheckpoint !== "object" || Array.isArray(value.inventoryCheckpoint))) {
3613
+ return null;
3614
+ }
3615
+ return {
3616
+ ...value,
3617
+ budgetBeforeInventory,
3618
+ inventoryElapsedMs: Number(value.inventoryElapsedMs),
3619
+ revisionFloors
3620
+ };
3621
+ }
3622
+ return null;
3623
+ }
3624
+ function buildGoogleDriveFullReconciliationCheckpoint(input) {
3625
+ const checkpoint = {
3626
+ version: 3,
3627
+ kind: "google_drive_full_reconciliation",
3628
+ ...input,
3629
+ revisionFloors: input.revisionFloors.map((floor) => [...floor])
3630
+ };
3631
+ if (encodedJsonBytes(checkpoint) > GOOGLE_DRIVE_EXECUTION_CHECKPOINT_MAX_BYTES) {
3632
+ throw new SyncFailure("resource_limit", false);
3633
+ }
3634
+ return checkpoint;
3635
+ }
3636
+ function parseGoogleDriveRevisionFloors(value, maxItems) {
3637
+ if (!Array.isArray(value) || value.length > maxItems) return null;
3638
+ const floors = [];
3639
+ const seen = /* @__PURE__ */ new Set();
3640
+ for (const floor of value) {
3641
+ if (!Array.isArray(floor) || floor.length !== 2 || typeof floor[0] !== "string" || floor[0].trim().length < 1 || floor[0].length > 1024 || floor[1] !== null && (typeof floor[1] !== "string" || floor[1].length < 1 || floor[1].length > 256) || seen.has(floor[0])) {
3642
+ return null;
3643
+ }
3644
+ seen.add(floor[0]);
3645
+ floors.push([floor[0], floor[1]]);
3646
+ }
3647
+ return floors;
3648
+ }
3649
+ function revisionFloorsForEntries(entries, maxItems) {
3650
+ const reconciled = reconcileGoogleDriveRevisionFloors(entries, [], maxItems);
3651
+ if (reconciled.conflict) throw new SyncFailure("provider_payload_invalid", false);
3652
+ return reconciled.revisionFloors;
3653
+ }
3654
+ function shouldProcessGoogleDriveDurableObservation(input) {
3655
+ if (input.observation.externalObjectId !== input.entry.externalObjectId) {
3656
+ throw new SyncFailure("provider_payload_invalid", false);
3657
+ }
3658
+ if (input.observation.disposition === "accepted" || input.observation.disposition === "replayed") {
3659
+ return true;
3660
+ }
3661
+ if (input.observation.disposition === "conflict") {
3662
+ throw new SyncFailure("provider_payload_invalid", false);
3663
+ }
3664
+ const current = input.observation.currentVersion;
3665
+ const comparison = current ? compareCanonicalDecimalProviderRevisions(
3666
+ current.providerRevision,
3667
+ input.observation.providerRevision
3668
+ ) : null;
3669
+ const fullyProcessed = current !== null && current.objectLifecycleState === "active" && current.sourceLifecycleGeneration === input.sourceLifecycleGeneration && current.objectLifecycleGeneration === current.currentObjectLifecycleGeneration && current.aclGeneration === input.aclGeneration && current.indexObligationStatus === "indexed" && comparison !== null && comparison >= 0 && (comparison > 0 || current.metadataHash === input.observation.metadataHash);
3670
+ if (!fullyProcessed) throw new SyncFailure("provider_payload_invalid", false);
3671
+ return false;
3672
+ }
3673
+ function mergeGoogleDriveDurableObservationFloors(checkpoint, observations, maxItems) {
3674
+ if (!checkpoint || checkpoint.version !== 3 || checkpoint.kind !== "google_drive_full_reconciliation") {
3675
+ return checkpoint;
3676
+ }
3677
+ const parsedFloors = parseGoogleDriveRevisionFloors(checkpoint.revisionFloors, maxItems);
3678
+ if (!parsedFloors) throw new SyncFailure("provider_payload_invalid", false);
3679
+ const floors = new Map(parsedFloors);
3680
+ for (const observation of observations) {
3681
+ floors.set(observation.externalObjectId, observation.providerRevision);
3682
+ }
3683
+ if (floors.size > maxItems) throw new SyncFailure("resource_limit", false);
3684
+ const merged = {
3685
+ ...checkpoint,
3686
+ revisionFloors: [...floors]
3687
+ };
3688
+ if (encodedJsonBytes(merged) > GOOGLE_DRIVE_EXECUTION_CHECKPOINT_MAX_BYTES) {
3689
+ throw new SyncFailure("resource_limit", false);
3690
+ }
3691
+ return merged;
3692
+ }
3693
+ function reconcileGoogleDriveRevisionFloors(entries, existingFloors, maxItems) {
3694
+ const floors = new Map(existingFloors);
3695
+ const accepted = [];
3696
+ for (const entry of entries) {
3697
+ if (!floors.has(entry.externalObjectId)) {
3698
+ floors.set(entry.externalObjectId, entry.externalVersionId);
3699
+ accepted.push(entry);
3700
+ } else {
3701
+ const comparison = compareCanonicalDecimalProviderRevisions(
3702
+ entry.externalVersionId,
3703
+ floors.get(entry.externalObjectId) ?? null
3704
+ );
3705
+ if (comparison === null) {
3706
+ return {
3707
+ entries: [],
3708
+ revisionFloors: [...floors],
3709
+ conflict: true
3710
+ };
3711
+ }
3712
+ if (comparison > 0) {
3713
+ floors.set(entry.externalObjectId, entry.externalVersionId);
3714
+ accepted.push(entry);
3715
+ }
3716
+ }
3717
+ if (floors.size > maxItems) throw new SyncFailure("resource_limit", false);
3718
+ }
3719
+ return {
3720
+ entries: accepted,
3721
+ revisionFloors: [...floors],
3722
+ conflict: false
3723
+ };
3724
+ }
3725
+ function encodedJsonBytes(value) {
3726
+ try {
3727
+ return new TextEncoder().encode(JSON.stringify(value)).byteLength;
3728
+ } catch {
3729
+ return Number.POSITIVE_INFINITY;
3730
+ }
3731
+ }
3732
+ function parseGoogleDriveSyncBudget(value, limits) {
3733
+ if (!value || typeof value !== "object" || Array.isArray(value)) return null;
3734
+ const row = value;
3735
+ for (const [key, maximum] of [
3736
+ ["examinedItems", limits.maxItems],
3737
+ ["providerRequests", limits.maxProviderRequests],
3738
+ ["elapsedMs", limits.maxElapsedMs]
3739
+ ]) {
3740
+ const budgetValue = row[key];
3741
+ if (!Number.isSafeInteger(budgetValue) || Number(budgetValue) < 0 || Number(budgetValue) > maximum) {
3742
+ return null;
3743
+ }
3744
+ }
3745
+ return value;
3746
+ }
3747
+ function emptyGoogleDriveSyncBudget() {
3748
+ return { examinedItems: 0, providerRequests: 0, elapsedMs: 0 };
3749
+ }
3750
+ function googleDriveHardLimitReason(budget, limits) {
3751
+ if (budget.examinedItems >= limits.maxItems) return "item_limit";
3752
+ if (budget.providerRequests >= limits.maxProviderRequests) return "api_request_limit";
3753
+ if (budget.elapsedMs >= limits.maxElapsedMs) return "elapsed_time_limit";
3754
+ return null;
3755
+ }
3756
+ function googleDriveInventoryStopIsHard(reason, budget, limits) {
3757
+ if (reason === "api_request_limit" || reason === "item_limit" || reason === "known_byte_limit") {
3758
+ return true;
3759
+ }
3760
+ return reason === "elapsed_time_limit" && budget.elapsedMs >= limits.maxElapsedMs;
3761
+ }
3762
+ async function getDriveStartPageToken(driveId, authorization, retry, observability) {
3763
+ if (!authorization) throw new SyncFailure("connection_reconnect_required", true, true);
3764
+ const url = new URL(`${DRIVE_API_BASE}/changes/startPageToken`);
3765
+ url.searchParams.set("supportsAllDrives", "true");
3766
+ if (driveId) url.searchParams.set("driveId", driveId);
3767
+ const response = await requestGoogleDrive(
3768
+ url,
3769
+ { headers: { authorization, accept: "application/json" } },
3770
+ "start_page_token",
3771
+ retry,
3772
+ observability
3773
+ );
3774
+ if (!response.ok) throwGoogleDriveResponse(response);
3775
+ const payload = await readResponseJsonBounded(
3776
+ response,
3777
+ DRIVE_JSON_MAX_BYTES,
3778
+ "Google Drive start page token"
3779
+ );
3780
+ return requiredString(payload.startPageToken, "start_page_token");
3781
+ }
3782
+ async function listDriveChanges(pageToken, driveId, pageSize, authorization, retry, observability) {
3783
+ if (!authorization) throw new SyncFailure("connection_reconnect_required", true, true);
3784
+ const url = new URL(`${DRIVE_API_BASE}/changes`);
3785
+ url.searchParams.set("pageToken", pageToken);
3786
+ url.searchParams.set("pageSize", String(pageSize));
3787
+ url.searchParams.set("supportsAllDrives", "true");
3788
+ url.searchParams.set("includeItemsFromAllDrives", "true");
3789
+ url.searchParams.set("spaces", "drive");
3790
+ url.searchParams.set(
3791
+ "fields",
3792
+ "nextPageToken,newStartPageToken,changes(fileId,removed,time,driveId,file(id,name,mimeType,driveId,parents,modifiedTime,createdTime,version,md5Checksum,size,webViewLink,trashed))"
3793
+ );
3794
+ if (driveId) url.searchParams.set("driveId", driveId);
3795
+ const response = await requestGoogleDrive(
3796
+ url,
3797
+ { headers: { authorization, accept: "application/json" } },
3798
+ "list_changes",
3799
+ retry,
3800
+ observability
3801
+ );
3802
+ if (response.status === 410) throw new GoogleDriveCursorInvalidError();
3803
+ if (!response.ok) throwGoogleDriveResponse(response);
3804
+ const payload = await readResponseJsonBounded(
3805
+ response,
3806
+ DRIVE_JSON_MAX_BYTES,
3807
+ "Google Drive changes list"
3808
+ );
3809
+ const changes = Array.isArray(payload.changes) ? payload.changes : [];
3810
+ return {
3811
+ changes: changes.map((value) => {
3812
+ const row = value && typeof value === "object" ? value : {};
3813
+ const fileId = requiredString(row.fileId, "change_file_id");
3814
+ return {
3815
+ fileId,
3816
+ removed: row.removed === true,
3817
+ file: row.file && typeof row.file === "object" ? parseDriveProviderItem(row.file) : null
3818
+ };
3819
+ }),
3820
+ nextPageToken: optionalString(payload.nextPageToken),
3821
+ newStartPageToken: optionalString(payload.newStartPageToken)
3822
+ };
3823
+ }
3824
+ async function getDriveFile(fileId, authorization, retry, observability) {
3825
+ if (!authorization) throw new SyncFailure("connection_reconnect_required", true, true);
3826
+ const url = new URL(`${DRIVE_API_BASE}/files/${encodeURIComponent(fileId)}`);
3827
+ url.searchParams.set("supportsAllDrives", "true");
3828
+ url.searchParams.set(
3829
+ "fields",
3830
+ "id,name,mimeType,driveId,parents,modifiedTime,createdTime,version,md5Checksum,size,webViewLink,trashed"
3831
+ );
3832
+ const response = await requestGoogleDrive(
3833
+ url,
3834
+ { headers: { authorization, accept: "application/json" } },
3835
+ "get_file",
3836
+ retry,
3837
+ observability
3838
+ );
3839
+ if (response.status === 404) return null;
3840
+ if (!response.ok) throwGoogleDriveResponse(response);
3841
+ return parseDriveProviderItem(
3842
+ await readResponseJsonBounded(response, DRIVE_JSON_MAX_BYTES, "Google Drive file metadata")
3843
+ );
3844
+ }
3845
+ async function listDrivePermissions(input, authorization, retry, observability) {
3846
+ if (!authorization) throw new SyncFailure("connection_reconnect_required", true, true);
3847
+ const url = new URL(`${DRIVE_API_BASE}/files/${encodeURIComponent(input.fileId)}/permissions`);
3848
+ url.searchParams.set("supportsAllDrives", "true");
3849
+ url.searchParams.set("pageSize", String(input.pageSize));
3850
+ url.searchParams.set(
3851
+ "fields",
3852
+ "nextPageToken,permissions(id,type,emailAddress,domain,role,allowFileDiscovery,expirationTime,deleted,permissionDetails(inherited))"
3853
+ );
3854
+ if (input.pageToken) url.searchParams.set("pageToken", input.pageToken);
3855
+ const response = await requestGoogleDrive(
3856
+ url,
3857
+ { headers: { authorization, accept: "application/json" } },
3858
+ "list_permissions",
3859
+ retry,
3860
+ observability
3861
+ );
3862
+ if (response.status === 403 || response.status === 404) {
3863
+ return { permissions: [], nextPageToken: null, denied: true };
3864
+ }
3865
+ if (!response.ok) throwGoogleDriveResponse(response);
3866
+ const payload = await readResponseJsonBounded(
3867
+ response,
3868
+ DRIVE_JSON_MAX_BYTES,
3869
+ "Google Drive permission list"
3870
+ );
3871
+ const rawPermissions = Array.isArray(payload.permissions) ? payload.permissions : null;
3872
+ if (!rawPermissions) throw new SyncFailure("provider_payload_invalid", false);
3873
+ return {
3874
+ permissions: rawPermissions.map(parseDrivePermission),
3875
+ nextPageToken: optionalString(payload.nextPageToken),
3876
+ denied: false
3877
+ };
3878
+ }
3879
+ async function collectGoogleDriveAclEvidence(input) {
3880
+ if (!Number.isSafeInteger(input.maxProviderRequests) || input.maxProviderRequests < 1) {
3881
+ throw new SyncFailure("resource_limit", false);
3882
+ }
3883
+ const now = input.now ?? Date.now;
3884
+ const observedAt = new Date(now());
3885
+ if (!Number.isFinite(observedAt.getTime())) {
3886
+ throw new SyncFailure("provider_payload_invalid", false);
3887
+ }
3888
+ const permissions = [];
3889
+ const seenTokens = /* @__PURE__ */ new Set();
3890
+ let pageToken = null;
3891
+ let providerRequests = 0;
3892
+ let permissionListDenied = false;
3893
+ do {
3894
+ if (providerRequests >= input.maxProviderRequests) {
3895
+ throw new SyncFailure("resource_limit", false);
3896
+ }
3897
+ providerRequests += 1;
3898
+ const page = await input.listPermissions({
3899
+ fileId: input.entry.externalObjectId,
3900
+ pageToken,
3901
+ pageSize: Math.min(100, GOOGLE_DRIVE_ACL_MAX_PRINCIPALS - permissions.length)
3902
+ });
3903
+ if (page.denied) {
3904
+ permissionListDenied = true;
3905
+ break;
3906
+ }
3907
+ permissions.push(...page.permissions);
3908
+ if (permissions.length > GOOGLE_DRIVE_ACL_MAX_PRINCIPALS) {
3909
+ throw new SyncFailure("resource_limit", false);
3910
+ }
3911
+ pageToken = page.nextPageToken;
3912
+ if (pageToken) {
3913
+ if (pageToken.length > 4096 || seenTokens.has(pageToken)) {
3914
+ throw new SyncFailure("provider_payload_invalid", false);
1268
3915
  }
3916
+ seenTokens.add(pageToken);
3917
+ }
3918
+ } while (pageToken);
3919
+ if (permissionListDenied) {
3920
+ if (!input.getFile || providerRequests >= input.maxProviderRequests) {
3921
+ return googleDriveDeniedAclEvidence(input.entry, observedAt, providerRequests);
3922
+ }
3923
+ providerRequests += 1;
3924
+ const current = await input.getFile(input.entry.externalObjectId);
3925
+ if (!current || current.trashed) {
3926
+ return googleDriveDeniedAclEvidence(input.entry, observedAt, providerRequests);
3927
+ }
3928
+ const aclRevision2 = createHash2("sha256").update(
3929
+ JSON.stringify({
3930
+ version: 1,
3931
+ mode: "source_owner_only",
3932
+ externalObjectId: input.entry.externalObjectId,
3933
+ providerRevision: input.entry.externalVersionId
3934
+ })
3935
+ ).digest("hex");
3936
+ return {
3937
+ eligibility: "eligible",
3938
+ providerRevision: input.entry.externalVersionId,
3939
+ driveId: input.entry.driveId,
3940
+ aclRevision: aclRevision2,
3941
+ observedAt: observedAt.toISOString(),
3942
+ expiresAt: new Date(observedAt.getTime() + GOOGLE_DRIVE_ACL_FRESHNESS_MS).toISOString(),
3943
+ providerRequests,
3944
+ principals: []
3945
+ };
3946
+ }
3947
+ const canonicalPermissions = permissions.map((permission) => ({
3948
+ id: permission.id,
3949
+ type: permission.type,
3950
+ role: permission.role,
3951
+ emailAddress: permission.emailAddress,
3952
+ domain: permission.domain,
3953
+ allowFileDiscovery: permission.allowFileDiscovery,
3954
+ expirationTime: permission.expirationTime,
3955
+ inherited: permission.inherited
3956
+ })).sort((left, right) => JSON.stringify(left).localeCompare(JSON.stringify(right)));
3957
+ const aclRevision = createHash2("sha256").update(
3958
+ JSON.stringify({
3959
+ version: 1,
3960
+ externalObjectId: input.entry.externalObjectId,
3961
+ providerRevision: input.entry.externalVersionId,
3962
+ permissions: canonicalPermissions
3963
+ })
3964
+ ).digest("hex");
3965
+ return {
3966
+ eligibility: "eligible",
3967
+ providerRevision: input.entry.externalVersionId,
3968
+ driveId: input.entry.driveId,
3969
+ aclRevision,
3970
+ observedAt: observedAt.toISOString(),
3971
+ expiresAt: new Date(observedAt.getTime() + GOOGLE_DRIVE_ACL_FRESHNESS_MS).toISOString(),
3972
+ providerRequests,
3973
+ principals: canonicalPermissions.map((permission) => ({
3974
+ type: permission.type,
3975
+ permissionId: permission.id,
3976
+ emailAddress: permission.emailAddress,
3977
+ domain: permission.domain,
3978
+ role: permission.role,
3979
+ inherited: permission.inherited,
3980
+ allowFileDiscovery: permission.allowFileDiscovery,
3981
+ expirationTime: permission.expirationTime
3982
+ }))
3983
+ };
3984
+ }
3985
+ function googleDriveDeniedAclEvidence(entry, observedAt, providerRequests) {
3986
+ return {
3987
+ eligibility: "denied",
3988
+ providerRevision: entry.externalVersionId,
3989
+ driveId: entry.driveId,
3990
+ aclRevision: createHash2("sha256").update(
3991
+ JSON.stringify({
3992
+ version: 1,
3993
+ mode: "provider_denied",
3994
+ externalObjectId: entry.externalObjectId,
3995
+ providerRevision: entry.externalVersionId
3996
+ })
3997
+ ).digest("hex"),
3998
+ observedAt: observedAt.toISOString(),
3999
+ expiresAt: new Date(observedAt.getTime() + GOOGLE_DRIVE_ACL_FRESHNESS_MS).toISOString(),
4000
+ providerRequests,
4001
+ principals: []
4002
+ };
4003
+ }
4004
+ function parseDrivePermission(value) {
4005
+ const row = objectRecord(value);
4006
+ if (row.deleted === true) throw new SyncFailure("provider_payload_invalid", false);
4007
+ const type = requiredString(row.type, "permission.type");
4008
+ if (type !== "user" && type !== "group" && type !== "domain" && type !== "anyone") {
4009
+ throw new SyncFailure("provider_payload_invalid", false);
4010
+ }
4011
+ const role = requiredString(row.role, "permission.role");
4012
+ if (role !== "owner" && role !== "organizer" && role !== "fileOrganizer" && role !== "writer" && role !== "commenter" && role !== "reader") {
4013
+ throw new SyncFailure("provider_payload_invalid", false);
4014
+ }
4015
+ const permissionDetails = Array.isArray(row.permissionDetails) ? row.permissionDetails : [];
4016
+ return {
4017
+ id: requiredString(row.id, "permission.id"),
4018
+ type,
4019
+ role,
4020
+ emailAddress: optionalString(row.emailAddress),
4021
+ domain: optionalString(row.domain),
4022
+ allowFileDiscovery: typeof row.allowFileDiscovery === "boolean" ? row.allowFileDiscovery : null,
4023
+ expirationTime: optionalExactProviderTimestamp(row.expirationTime),
4024
+ inherited: permissionDetails.some((detail) => objectRecord(detail).inherited === true)
4025
+ };
4026
+ }
4027
+ function optionalExactProviderTimestamp(value) {
4028
+ const timestamp = optionalString(value);
4029
+ if (!timestamp) return null;
4030
+ const parsed = new Date(timestamp);
4031
+ if (!Number.isFinite(parsed.getTime())) throw new SyncFailure("provider_payload_invalid", false);
4032
+ return parsed.toISOString();
4033
+ }
4034
+ function throwGoogleDriveResponse(response) {
4035
+ if (response.status === 401 || response.status === 403) {
4036
+ throw new SyncFailure("connection_reconnect_required", true, true);
4037
+ }
4038
+ throw new SyncFailure(
4039
+ response.status >= 500 || response.status === 429 ? "provider_unavailable" : "provider_rejected",
4040
+ response.status >= 500 || response.status === 429
4041
+ );
4042
+ }
4043
+ async function requestGoogleDrive(url, init, operation, retry, observability) {
4044
+ try {
4045
+ return await fetchGoogleDriveProvider({
4046
+ fetchImpl: sourceFetch,
4047
+ url,
4048
+ init,
4049
+ operation,
4050
+ policy: retry,
4051
+ observability,
4052
+ heartbeat: (details) => heartbeat({ provider: "google_drive", ...details })
1269
4053
  });
1270
- if (decision.events.length > 0) {
1271
- await bus.publish(input.workspaceId, input.sessionId, decision.events);
4054
+ } catch (error) {
4055
+ if (error instanceof GoogleDriveProviderTransportError) {
4056
+ throw new SyncFailure("provider_unavailable", true);
1272
4057
  }
1273
- return { action: decision.action };
4058
+ throw error;
4059
+ }
4060
+ }
4061
+ function selectedDriveSource(metadata, externalSourceId) {
4062
+ const sources = metadata.selectedSources ?? (metadata.selectedSource ? [metadata.selectedSource] : []);
4063
+ return sources.find((source) => source.id === externalSourceId) ?? null;
4064
+ }
4065
+ function googleDriveSyncProviderAccessAllowed(input) {
4066
+ return input.resolvedConnectionVersion !== void 0 && input.connectionVersion === input.resolvedConnectionVersion && input.connectionStatus === "active" && (input.lifecycleState === void 0 || input.lifecycleState === "active") && googleDriveScopesAllowCapability(input.grantedScopes, "recursive_source_sync");
4067
+ }
4068
+ async function listDriveChildren(input, authorization, retry, observability) {
4069
+ if (!authorization) throw new SyncFailure("connection_reconnect_required", true, true);
4070
+ const url = new URL(`${DRIVE_API_BASE}/files`);
4071
+ url.searchParams.set(
4072
+ "q",
4073
+ `'${input.folderId.replaceAll("'", "\\'")}' in parents and trashed = false`
4074
+ );
4075
+ url.searchParams.set("pageSize", String(input.pageSize));
4076
+ url.searchParams.set("supportsAllDrives", "true");
4077
+ url.searchParams.set("includeItemsFromAllDrives", "true");
4078
+ url.searchParams.set(
4079
+ "fields",
4080
+ "nextPageToken,incompleteSearch,files(id,name,mimeType,driveId,parents,modifiedTime,createdTime,version,md5Checksum,size,webViewLink,trashed)"
4081
+ );
4082
+ if (input.pageToken) url.searchParams.set("pageToken", input.pageToken);
4083
+ if (input.driveId) {
4084
+ url.searchParams.set("corpora", "drive");
4085
+ url.searchParams.set("driveId", input.driveId);
4086
+ }
4087
+ const response = await requestGoogleDrive(
4088
+ url,
4089
+ { headers: { authorization, accept: "application/json" } },
4090
+ "list_children",
4091
+ retry,
4092
+ observability
4093
+ );
4094
+ if (!response.ok) {
4095
+ if (response.status === 401 || response.status === 403) {
4096
+ throw new SyncFailure("connection_reconnect_required", true, true);
4097
+ }
4098
+ throw new SyncFailure(
4099
+ "provider_unavailable",
4100
+ response.status >= 500 || response.status === 429
4101
+ );
1274
4102
  }
4103
+ const payload = await readResponseJsonBounded(
4104
+ response,
4105
+ DRIVE_JSON_MAX_BYTES,
4106
+ "Google Drive file list"
4107
+ );
4108
+ const rawFiles = Array.isArray(payload.files) ? payload.files : [];
1275
4109
  return {
1276
- enqueueGoalRetryWake,
1277
- maybeContinueGoal
4110
+ items: rawFiles.map(parseDriveProviderItem),
4111
+ nextPageToken: typeof payload.nextPageToken === "string" ? payload.nextPageToken : null,
4112
+ incompleteSearch: payload.incompleteSearch === true
1278
4113
  };
1279
4114
  }
1280
- function goalContinuationModelDecision(input) {
1281
- const catalogSettings = input.settings.supergrokSubscriptionEnabled ? withXaiSubscriptionCatalogProvider(
1282
- input.settings.codexSubscriptionEnabled ? withCodexCatalogProvider(input.settings) : input.settings
1283
- ) : input.settings.codexSubscriptionEnabled ? withCodexCatalogProvider(input.settings) : input.settings;
1284
- const policyBlocks = (modelId) => input.workspaceModelPolicy !== null && !evaluateWorkspaceModelPolicy(input.workspaceModelPolicy, {
1285
- providerId: policyProviderIdForModel(catalogSettings, modelId),
1286
- modelId
1287
- }).allowed;
1288
- if (resolveModelProvider(catalogSettings, input.inheritedModel) && !policyBlocks(input.inheritedModel)) {
1289
- return { model: input.inheritedModel, blocked: null };
4115
+ function parseDriveProviderItem(value) {
4116
+ const row = value && typeof value === "object" ? value : {};
4117
+ return {
4118
+ id: requiredString(row.id, "id"),
4119
+ name: requiredString(row.name, "name"),
4120
+ mimeType: requiredString(row.mimeType, "mimeType"),
4121
+ driveId: optionalString(row.driveId),
4122
+ parents: Array.isArray(row.parents) ? row.parents.filter((item) => typeof item === "string") : [],
4123
+ modifiedTime: optionalString(row.modifiedTime),
4124
+ createdTime: optionalString(row.createdTime),
4125
+ version: optionalString(row.version),
4126
+ md5Checksum: optionalString(row.md5Checksum),
4127
+ size: optionalString(row.size),
4128
+ webViewLink: optionalString(row.webViewLink),
4129
+ trashed: row.trashed === true
4130
+ };
4131
+ }
4132
+ async function fetchDriveBytes(entry, authorization, maxBytes, retry, observability) {
4133
+ if (!authorization) throw new SyncFailure("connection_reconnect_required", true, true);
4134
+ const url = entry.transfer.action === "export" ? new URL(`${DRIVE_API_BASE}/files/${encodeURIComponent(entry.externalObjectId)}/export`) : new URL(`${DRIVE_API_BASE}/files/${encodeURIComponent(entry.externalObjectId)}`);
4135
+ if (entry.transfer.action === "export") {
4136
+ url.searchParams.set("mimeType", entry.transfer.contentType);
4137
+ } else {
4138
+ url.searchParams.set("alt", "media");
4139
+ url.searchParams.set("supportsAllDrives", "true");
4140
+ }
4141
+ const response = await requestGoogleDrive(
4142
+ url,
4143
+ { headers: { authorization } },
4144
+ entry.transfer.action === "export" ? "export" : "download",
4145
+ retry,
4146
+ observability
4147
+ );
4148
+ if (!response.ok) {
4149
+ if (response.status === 401 || response.status === 403) {
4150
+ throw new SyncFailure("connection_reconnect_required", true, true);
4151
+ }
4152
+ throw new SyncFailure(
4153
+ "provider_unavailable",
4154
+ response.status >= 500 || response.status === 429
4155
+ );
1290
4156
  }
1291
- if (!resolveModelProvider(catalogSettings, input.inheritedModel)) {
4157
+ const declared = Number(response.headers.get("content-length") ?? "0");
4158
+ if (Number.isFinite(declared) && declared > maxBytes) {
4159
+ throw new SyncFailure("content_too_large", false);
4160
+ }
4161
+ if (!response.body) {
4162
+ const bytes2 = new Uint8Array(await response.arrayBuffer());
4163
+ if (bytes2.byteLength > maxBytes) throw new SyncFailure("content_too_large", false);
4164
+ return bytes2;
4165
+ }
4166
+ const reader = response.body.getReader();
4167
+ const chunks = [];
4168
+ let total = 0;
4169
+ while (true) {
4170
+ const { done, value } = await reader.read();
4171
+ if (done) break;
4172
+ total += value.byteLength;
4173
+ if (total > maxBytes) {
4174
+ await reader.cancel();
4175
+ throw new SyncFailure("content_too_large", false);
4176
+ }
4177
+ chunks.push(value);
4178
+ }
4179
+ const bytes = new Uint8Array(total);
4180
+ let offset = 0;
4181
+ for (const chunk of chunks) {
4182
+ bytes.set(chunk, offset);
4183
+ offset += chunk.byteLength;
4184
+ }
4185
+ return bytes;
4186
+ }
4187
+ async function listAtlassianItems(source, cursor, pageSize, authorization) {
4188
+ if (!authorization) throw new SyncFailure("connection_reconnect_required", true, true);
4189
+ if (source.kind === "jira_project") {
4190
+ const url2 = new URL(
4191
+ `${ATLASSIAN_API_BASE}/ex/jira/${encodeURIComponent(source.cloudId)}/rest/api/3/search/jql`
4192
+ );
4193
+ url2.searchParams.set(
4194
+ "jql",
4195
+ `project = "${source.key.replaceAll('"', '\\"')}" ORDER BY key ASC`
4196
+ );
4197
+ url2.searchParams.set("maxResults", String(pageSize));
4198
+ url2.searchParams.set("fields", "summary,created,updated");
4199
+ if (cursor) url2.searchParams.set("nextPageToken", cursor);
4200
+ const payload2 = objectRecord(await atlassianJsonRequest(url2, authorization, "Jira issue list"));
4201
+ const issues = Array.isArray(payload2.issues) ? payload2.issues : [];
1292
4202
  return {
1293
- model: input.inheritedModel,
1294
- blocked: `model "${input.inheritedModel}" is no longer in the deployment or workspace catalog; choose an available model before resuming the goal`
4203
+ items: issues.map((raw) => {
4204
+ const row = objectRecord(raw);
4205
+ const fields = objectRecord(row.fields);
4206
+ const id = requiredAtlassianString(row.id, "issue.id");
4207
+ const key = requiredAtlassianString(row.key, "issue.key");
4208
+ const updatedAt = optionalString(fields.updated);
4209
+ return {
4210
+ id,
4211
+ key,
4212
+ title: requiredAtlassianString(fields.summary, "issue.summary"),
4213
+ version: updatedAt,
4214
+ createdAt: optionalString(fields.created),
4215
+ updatedAt,
4216
+ webUrl: new URL(`/browse/${encodeURIComponent(key)}`, source.siteUrl).toString()
4217
+ };
4218
+ }),
4219
+ nextCursor: optionalString(payload2.nextPageToken)
1295
4220
  };
1296
4221
  }
4222
+ const url = new URL(
4223
+ `${ATLASSIAN_API_BASE}/ex/confluence/${encodeURIComponent(source.cloudId)}/wiki/api/v2/spaces/${encodeURIComponent(source.resourceId)}/pages`
4224
+ );
4225
+ url.searchParams.set("limit", String(pageSize));
4226
+ url.searchParams.set("sort", "id");
4227
+ if (cursor) url.searchParams.set("cursor", cursor);
4228
+ const payload = objectRecord(
4229
+ await atlassianJsonRequest(url, authorization, "Confluence page list")
4230
+ );
4231
+ const pages = Array.isArray(payload.results) ? payload.results : [];
1297
4232
  return {
1298
- model: input.inheritedModel,
1299
- blocked: `workspace model policy blocks model "${input.inheritedModel}"; pick an allowed model or change the workspace model policy`
4233
+ items: pages.map((raw) => {
4234
+ const row = objectRecord(raw);
4235
+ const version = objectRecord(row.version);
4236
+ const id = requiredAtlassianString(row.id, "page.id");
4237
+ const webUi = optionalString(objectRecord(row._links).webui);
4238
+ const versionNumber = numberValue(version.number);
4239
+ return {
4240
+ id,
4241
+ key: id,
4242
+ title: requiredAtlassianString(row.title, "page.title"),
4243
+ version: versionNumber === void 0 ? null : String(versionNumber),
4244
+ createdAt: optionalString(row.createdAt),
4245
+ updatedAt: optionalString(version.createdAt),
4246
+ webUrl: webUi ? new URL(webUi.startsWith("/wiki/") ? webUi : `/wiki${webUi}`, source.siteUrl).toString() : new URL(
4247
+ `/wiki/spaces/${encodeURIComponent(source.key)}/pages/${encodeURIComponent(id)}`,
4248
+ source.siteUrl
4249
+ ).toString()
4250
+ };
4251
+ }),
4252
+ nextCursor: confluenceCursor(objectRecord(payload._links).next)
1300
4253
  };
1301
4254
  }
1302
- function goalContinuationFundedWithoutCredits(settings, model, codexBilled) {
1303
- return modelFundingForAdmission(settings, model, codexBilled).fundedWithoutCredits;
1304
- }
1305
- function goalContinuationPrompt(_goal, _autoContinuation, _cap, options = {}) {
1306
- const waitingGuidance = options.inputWaitAvailable ? [
1307
- "Waiting on child sessions, background commands, or external events:",
1308
- "- When further progress depends on work already in flight, do not sleep, loop, or poll session or command state repeatedly.",
1309
- "- Re-check once; if the work is still in flight and the wait is long or uncertain, call opengeni__wait_for_input with a concrete reason and timeoutSeconds, then end your turn immediately. Relevant session input or the safety deadline will start a new turn, and this goal stays active.",
1310
- "- If the immediately preceding user-facing update already reported this same unchanged wait, do not restate it or produce another equivalent final answer. Call opengeni__wait_for_input and end the turn. Report only material new state or a newly discovered blocker.",
1311
- "- If you are blocked on a human decision, use opengeni__goal_pause under the blocked audit below instead.",
1312
- ""
1313
- ] : [];
1314
- const childNoticeGuidance = [
1315
- "Child lifecycle notices:",
1316
- "- A `child_requires_action` update means a worker you spawned is blocked on a question or a tool approval and will not progress until it is answered. " + (options.humanInputRespondAvailable ? "If you know the answer to its question, answer it with opengeni__session_human_input_respond (pass the worker's sessionId, the requestId from the notice, and a response). " : "") + "Tool approvals can only be decided by a human. If you cannot resolve the blocker yourself, report the exact blocker (worker session id, the question) to the user and wait or pause instead of retrying the worker.",
1317
- "- `child_requires_action_resolved`, `child_paused`, `child_waiting_capacity`, and `child_progress` updates are informational: a resolved notice means the worker is moving again; a paused worker needs a human or you to resume it; a capacity wait resumes by itself; a progress note needs no action.",
1318
- ""
1319
- ];
1320
- return [
1321
- "Reconcile the active session goal against authoritative current state, then carry the work through to the full requested end state and verify it.",
1322
- "",
1323
- "Goal recovery:",
1324
- "- Treat this continuation as re-entry into the full objective, not as a request to perform one step and stop.",
1325
- "- Do not rely on previous assistant claims of progress or completion; use them only to locate authoritative evidence.",
1326
- "- If authoritative evidence already proves the full objective, call opengeni__goal_complete instead of manufacturing more work.",
1327
- "- Before repeating a state-setting action, verify whether its desired state already holds. If it does, do not repeat it; continue reconciling the overall goal.",
1328
- "",
1329
- "Continuation behavior:",
1330
- "- This goal persists across turns. A runtime boundary can end one turn without shrinking the objective; the next continuation resumes the same full objective.",
1331
- "- Keep working until the requested end state is true and verified. Do not end the turn merely because one useful action completed, and do not redefine success around a smaller or easier task.",
1332
- "- An incomplete-status report is not a substitute for continuing the work. If the remaining problem can be investigated or addressed within your current authority, continue that work in this turn rather than returning another equivalent status-only final.",
1333
- "- Distinguish unfinished work from a blocker that actually requires human input or external change; use the existing waiting and blocked audits only when their conditions hold.",
1334
- "- Temporary rough edges are acceptable while the work is moving in the right direction. Completion still requires the requested end state to be true and verified.",
1335
- "",
1336
- "Work from evidence:",
1337
- "Use the current workspace and external state as authoritative. Conversation context can help locate relevant work, but it is not proof of the current state. Inspect the current state before relying on it. Improve, replace, or remove existing work as needed to satisfy the actual objective.",
4255
+ async function fetchAtlassianContent(source, entry, authorization, maxBytes) {
4256
+ if (!authorization) throw new SyncFailure("connection_reconnect_required", true, true);
4257
+ const objectId = entry.externalObjectId.slice(entry.externalObjectId.indexOf(":") + 1);
4258
+ const markdown = source.kind === "jira_project" ? await jiraIssueMarkdown(source, objectId, entry, authorization) : await confluencePageMarkdown(source, objectId, entry, authorization);
4259
+ const bytes = new TextEncoder().encode(markdown);
4260
+ if (bytes.byteLength > maxBytes) throw new SyncFailure("content_too_large", false);
4261
+ return bytes;
4262
+ }
4263
+ async function jiraIssueMarkdown(source, issueId, entry, authorization) {
4264
+ const issueUrl = new URL(
4265
+ `${ATLASSIAN_API_BASE}/ex/jira/${encodeURIComponent(source.cloudId)}/rest/api/3/issue/${encodeURIComponent(issueId)}`
4266
+ );
4267
+ issueUrl.searchParams.set(
4268
+ "fields",
4269
+ "summary,description,status,issuetype,priority,assignee,reporter,labels,created,updated"
4270
+ );
4271
+ const issue = objectRecord(await atlassianJsonRequest(issueUrl, authorization, "Jira issue"));
4272
+ const fields = objectRecord(issue.fields);
4273
+ const lines = [
4274
+ `# ${entry.title}`,
1338
4275
  "",
1339
- "Progress visibility:",
1340
- "If a planning tool is available and the next work is meaningfully multi-step, use it to show a concise plan tied to the real objective. Keep the plan current as steps complete or the next best action changes. Skip planning overhead for trivial one-step progress, and do not treat a plan update as a substitute for doing the work.",
4276
+ `- Key: ${optionalString(issue.key) ?? entry.title}`,
4277
+ `- Type: ${optionalString(objectRecord(fields.issuetype).name) ?? "Unknown"}`,
4278
+ `- Status: ${optionalString(objectRecord(fields.status).name) ?? "Unknown"}`,
4279
+ `- Priority: ${optionalString(objectRecord(fields.priority).name) ?? "None"}`,
4280
+ `- Assignee: ${optionalString(objectRecord(fields.assignee).displayName) ?? "Unassigned"}`,
4281
+ `- Reporter: ${optionalString(objectRecord(fields.reporter).displayName) ?? "Unknown"}`,
4282
+ `- Updated: ${optionalString(fields.updated) ?? "Unknown"}`,
4283
+ `- Source: ${entry.sourceUri}`,
1341
4284
  "",
1342
- "Fidelity:",
1343
- "- Optimize each turn for movement toward the requested end state, not for the smallest stable-looking subset or easiest passing change.",
1344
- "- Do not substitute a narrower, safer, smaller, merely compatible, or easier-to-test solution because it is more likely to pass current tests.",
1345
- "- Treat alignment as movement toward the requested end state. An edit is aligned only if it makes the requested final state more true; useful-looking behavior that preserves a different end state is misaligned.",
4285
+ "## Description",
1346
4286
  "",
1347
- "Completion audit:",
1348
- "Before deciding that the goal is achieved, treat completion as unproven and verify it against the actual current state:",
1349
- "- Derive concrete requirements from the objective and any referenced files, plans, specifications, issues, or user instructions.",
1350
- "- Preserve the original scope; do not redefine success around the work that already exists.",
1351
- "- For every explicit requirement, named artifact, command, test, gate, invariant, and deliverable, identify and inspect the authoritative evidence that would prove it.",
1352
- "- Match verification scope to requirement scope. Treat uncertain, indirect, incomplete, or missing evidence as not achieved and continue working.",
1353
- "- The audit must prove completion, not merely fail to find obvious remaining work.",
4287
+ adfToMarkdown(fields.description) || "No description."
4288
+ ];
4289
+ const labels = Array.isArray(fields.labels) ? fields.labels.filter((value) => typeof value === "string") : [];
4290
+ if (labels.length > 0) lines.splice(9, 0, `- Labels: ${labels.join(", ")}`);
4291
+ const comments = await jiraComments(source, issueId, authorization);
4292
+ if (comments.length > 0) {
4293
+ lines.push("", "## Comments", "");
4294
+ for (const comment of comments) {
4295
+ const row = objectRecord(comment);
4296
+ const author = optionalString(objectRecord(row.author).displayName) ?? "Unknown author";
4297
+ const created = optionalString(row.created) ?? "Unknown date";
4298
+ lines.push(`### ${author} \xB7 ${created}`, "", adfToMarkdown(row.body), "");
4299
+ }
4300
+ }
4301
+ return lines.join("\n");
4302
+ }
4303
+ async function jiraComments(source, issueId, authorization) {
4304
+ const comments = [];
4305
+ let startAt = 0;
4306
+ for (let page = 0; page < 20; page += 1) {
4307
+ const url = new URL(
4308
+ `${ATLASSIAN_API_BASE}/ex/jira/${encodeURIComponent(source.cloudId)}/rest/api/3/issue/${encodeURIComponent(issueId)}/comment`
4309
+ );
4310
+ url.searchParams.set("startAt", String(startAt));
4311
+ url.searchParams.set("maxResults", "100");
4312
+ url.searchParams.set("orderBy", "created");
4313
+ const payload = objectRecord(await atlassianJsonRequest(url, authorization, "Jira comments"));
4314
+ const pageComments = Array.isArray(payload.comments) ? payload.comments : [];
4315
+ comments.push(...pageComments);
4316
+ const total = numberValue(payload.total) ?? comments.length;
4317
+ if (comments.length >= total || pageComments.length === 0) break;
4318
+ startAt += pageComments.length;
4319
+ }
4320
+ return comments;
4321
+ }
4322
+ async function confluencePageMarkdown(source, pageId, entry, authorization) {
4323
+ const pageUrl = new URL(
4324
+ `${ATLASSIAN_API_BASE}/ex/confluence/${encodeURIComponent(source.cloudId)}/wiki/api/v2/pages/${encodeURIComponent(pageId)}`
4325
+ );
4326
+ pageUrl.searchParams.set("body-format", "storage");
4327
+ const page = objectRecord(await atlassianJsonRequest(pageUrl, authorization, "Confluence page"));
4328
+ const storage = objectRecord(objectRecord(page.body).storage);
4329
+ const lines = [
4330
+ `# ${entry.title}`,
1354
4331
  "",
1355
- "Do not rely on intent, partial progress, memory of earlier work, or a plausible final answer as proof of completion. Call opengeni__goal_complete with concrete evidence only when the full objective is actually achieved and no required work remains.",
4332
+ `- Space: ${source.name} (${source.key})`,
4333
+ `- Updated: ${entry.modifiedTime ?? "Unknown"}`,
4334
+ `- Source: ${entry.sourceUri}`,
1356
4335
  "",
1357
- ...waitingGuidance,
1358
- ...childNoticeGuidance,
1359
- "Blocked audit:",
1360
- "- Do not call opengeni__goal_pause the first time a blocker appears.",
1361
- "- Pause only when the same blocking condition has repeated for at least three consecutive goal turns and meaningful progress is impossible without user input or an external-state change.",
1362
- "- Do not pause merely because the work is hard, slow, uncertain, incomplete, or would benefit from clarification.",
1363
- "- Once that threshold is satisfied, call opengeni__goal_pause with the concrete blocker instead of repeatedly reporting it while leaving the goal active.",
4336
+ "## Content",
1364
4337
  "",
1365
- "Do not call opengeni__goal_complete or opengeni__goal_pause unless the corresponding audit above is satisfied."
1366
- ].join("\n");
1367
- }
1368
- function withFirstPartyTools(settings, tools) {
1369
- if (!settings.mcpServers.some((server) => server.id === "opengeni")) {
1370
- return tools;
4338
+ optionalString(storage.value) ?? "No content."
4339
+ ];
4340
+ const comments = await confluenceFooterComments(source, pageId, authorization);
4341
+ if (comments.length > 0) {
4342
+ lines.push("", "## Comments", "");
4343
+ for (const comment of comments) {
4344
+ const row = objectRecord(comment);
4345
+ const body = objectRecord(objectRecord(row.body).storage);
4346
+ const version = objectRecord(row.version);
4347
+ lines.push(
4348
+ `### Comment \xB7 ${optionalString(version.createdAt) ?? "Unknown date"}`,
4349
+ "",
4350
+ optionalString(body.value) ?? "",
4351
+ ""
4352
+ );
4353
+ }
1371
4354
  }
1372
- return mergeToolRefs(tools, [{ kind: "mcp", id: "opengeni" }]);
4355
+ return lines.join("\n");
1373
4356
  }
1374
- async function goalRunBudgetBlocked(settings, db, accountId, workspaceId, fundedWithoutCredits) {
1375
- if (!fundedWithoutCredits && (settings.billingMode === "stripe" || settings.usageLimitsMode === "managed")) {
1376
- const balance = await getBillingBalance(db, accountId);
1377
- if (balance.balanceMicros <= 0) {
1378
- return "insufficient OpenGeni credits";
1379
- }
4357
+ async function confluenceFooterComments(source, pageId, authorization) {
4358
+ const comments = [];
4359
+ let cursor = null;
4360
+ for (let page = 0; page < 20; page += 1) {
4361
+ const url = new URL(
4362
+ `${ATLASSIAN_API_BASE}/ex/confluence/${encodeURIComponent(source.cloudId)}/wiki/api/v2/pages/${encodeURIComponent(pageId)}/footer-comments`
4363
+ );
4364
+ url.searchParams.set("body-format", "storage");
4365
+ url.searchParams.set("limit", "100");
4366
+ if (cursor) url.searchParams.set("cursor", cursor);
4367
+ const payload = objectRecord(
4368
+ await atlassianJsonRequest(url, authorization, "Confluence page comments")
4369
+ );
4370
+ const results = Array.isArray(payload.results) ? payload.results : [];
4371
+ comments.push(...results);
4372
+ cursor = confluenceCursor(objectRecord(payload._links).next);
4373
+ if (!cursor) break;
4374
+ }
4375
+ return comments;
4376
+ }
4377
+ async function atlassianJsonRequest(url, authorization, label) {
4378
+ const response = await sourceFetch(url, {
4379
+ headers: { authorization, accept: "application/json" },
4380
+ redirect: "error",
4381
+ signal: AbortSignal.timeout(2e4)
4382
+ });
4383
+ if (!response.ok) {
4384
+ await response.body?.cancel().catch(() => void 0);
4385
+ if (response.status === 401 || response.status === 403) {
4386
+ throw new SyncFailure("connection_reconnect_required", true, true);
4387
+ }
4388
+ throw new SyncFailure(
4389
+ "provider_unavailable",
4390
+ response.status >= 500 || response.status === 429
4391
+ );
1380
4392
  }
1381
- if (settings.usageLimitsMode === "static" || settings.usageLimitsMode === "managed") {
1382
- const limits = configuredStaticUsageLimits(settings);
1383
- if (!fundedWithoutCredits && limits.maxMonthlyCostMicrosPerAccount) {
1384
- const used = await sumUsageQuantity(db, {
1385
- accountId,
1386
- eventType: "model.cost",
1387
- since: startOfUtcMonth()
1388
- });
1389
- if (used >= limits.maxMonthlyCostMicrosPerAccount) {
1390
- return `monthly model cost limit reached (${limits.maxMonthlyCostMicrosPerAccount} micros)`;
1391
- }
1392
- }
1393
- if (limits.maxMonthlyAgentRunsPerWorkspace) {
1394
- const used = await sumUsageQuantity(db, {
1395
- workspaceId,
1396
- eventType: "agent_run.created",
1397
- since: startOfUtcMonth()
1398
- });
1399
- if (used + 1 > limits.maxMonthlyAgentRunsPerWorkspace) {
1400
- return `monthly agent run limit reached (${limits.maxMonthlyAgentRunsPerWorkspace})`;
4393
+ return await readResponseJsonBounded(response, ATLASSIAN_JSON_MAX_BYTES, label);
4394
+ }
4395
+ function confluenceCursor(value) {
4396
+ const next = optionalString(value);
4397
+ if (!next) return null;
4398
+ const url = new URL(next, ATLASSIAN_API_BASE);
4399
+ const cursor = url.searchParams.get("cursor");
4400
+ if (!cursor || cursor.length > 4096 || /[\u0000-\u001f]/.test(cursor)) {
4401
+ throw new Error("invalid Confluence cursor");
4402
+ }
4403
+ return cursor;
4404
+ }
4405
+ function adfToMarkdown(value) {
4406
+ const node = objectRecord(value);
4407
+ const type = optionalString(node.type);
4408
+ const children = Array.isArray(node.content) ? node.content.map(adfToMarkdown).join("") : "";
4409
+ if (type === "text") {
4410
+ let text = typeof node.text === "string" ? node.text : "";
4411
+ const marks = Array.isArray(node.marks) ? node.marks : [];
4412
+ for (const rawMark of marks) {
4413
+ const mark = objectRecord(rawMark);
4414
+ const markType = optionalString(mark.type);
4415
+ if (markType === "code") text = `\`${text}\``;
4416
+ if (markType === "strong") text = `**${text}**`;
4417
+ if (markType === "em") text = `*${text}*`;
4418
+ if (markType === "strike") text = `~~${text}~~`;
4419
+ if (markType === "link") {
4420
+ const href = optionalString(objectRecord(mark.attrs).href);
4421
+ if (href) text = `[${text}](${href})`;
1401
4422
  }
1402
4423
  }
4424
+ return text;
4425
+ }
4426
+ if (type === "paragraph") return `${children}
4427
+
4428
+ `;
4429
+ if (type === "heading") {
4430
+ const level = Math.min(Math.max(numberValue(objectRecord(node.attrs).level) ?? 2, 1), 6);
4431
+ return `${"#".repeat(level)} ${children.trim()}
4432
+
4433
+ `;
4434
+ }
4435
+ if (type === "hardBreak") return " \n";
4436
+ if (type === "rule") return "---\n\n";
4437
+ if (type === "blockquote") {
4438
+ return `${children.trim().split("\n").map((line) => `> ${line}`).join("\n")}
4439
+
4440
+ `;
4441
+ }
4442
+ if (type === "bulletList") {
4443
+ return `${children.trimEnd()}
4444
+ `;
1403
4445
  }
4446
+ if (type === "orderedList") {
4447
+ return `${children.trimEnd()}
4448
+ `;
4449
+ }
4450
+ if (type === "listItem") {
4451
+ return `- ${children.trim().replaceAll("\n", "\n ")}
4452
+ `;
4453
+ }
4454
+ if (type === "codeBlock") return `\`\`\`
4455
+ ${children.trimEnd()}
4456
+ \`\`\`
4457
+
4458
+ `;
4459
+ if (type === "mention") return `@${optionalString(objectRecord(node.attrs).text) ?? "user"}`;
4460
+ if (type === "emoji") return optionalString(objectRecord(node.attrs).text) ?? "";
4461
+ if (type === "inlineCard") {
4462
+ const url = optionalString(objectRecord(node.attrs).url);
4463
+ return url ? `[${url}](${url})` : "";
4464
+ }
4465
+ return children;
4466
+ }
4467
+ function objectRecord(value) {
4468
+ return value && typeof value === "object" && !Array.isArray(value) ? value : {};
4469
+ }
4470
+ function requiredAtlassianString(value, label) {
4471
+ if (typeof value !== "string" || !value.trim()) throw new Error(`invalid_atlassian_${label}`);
4472
+ return value;
4473
+ }
4474
+ function deterministicUuid(value) {
4475
+ const bytes = createHash2("sha256").update(value).digest().subarray(0, 16);
4476
+ bytes[6] = bytes[6] & 15 | 80;
4477
+ bytes[8] = bytes[8] & 63 | 128;
4478
+ const hex = bytes.toString("hex");
4479
+ return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
4480
+ }
4481
+ function safeFilename(value) {
4482
+ const normalized = value.normalize("NFKC").replace(/[\\/:*?"<>|\0-\x1f]/g, "_").trim();
4483
+ return (normalized || "document").slice(0, 240);
4484
+ }
4485
+ function requiredString(value, label) {
4486
+ if (typeof value !== "string" || !value.trim()) throw new Error(`invalid_google_drive_${label}`);
4487
+ return value;
4488
+ }
4489
+ function optionalString(value) {
4490
+ return typeof value === "string" && value.length > 0 ? value : null;
4491
+ }
4492
+ function numberValue(value) {
4493
+ return typeof value === "number" && Number.isFinite(value) ? value : void 0;
4494
+ }
4495
+ function boundedError(error) {
4496
+ return (error instanceof Error ? error.message : String(error)).slice(0, 1e3);
4497
+ }
4498
+ function entryMetadataHash(entry) {
4499
+ return createHash2("sha256").update(
4500
+ JSON.stringify({
4501
+ externalObjectId: entry.externalObjectId,
4502
+ externalVersionId: entry.externalVersionId,
4503
+ title: entry.title,
4504
+ mimeType: entry.mimeType,
4505
+ driveId: entry.driveId,
4506
+ modifiedTime: entry.modifiedTime,
4507
+ createdTime: entry.createdTime,
4508
+ sourceUri: entry.sourceUri
4509
+ })
4510
+ ).digest("hex");
4511
+ }
4512
+ function inventoryLimit(reason) {
4513
+ if (reason === "api_request_limit") return "provider_requests";
4514
+ if (reason === "elapsed_time_limit") return "elapsed_time";
4515
+ if (reason === "item_limit") return "items";
4516
+ if (reason === "known_byte_limit") return "bytes";
1404
4517
  return null;
1405
4518
  }
1406
- function startOfUtcMonth() {
1407
- const now = /* @__PURE__ */ new Date();
1408
- return new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), 1));
4519
+ function recordSyncHealthTelemetry(observability, provider, summary, outcome, failure) {
4520
+ observability.observeHistogram({
4521
+ name: "opengeni_knowledge_source_sync_terminal_batch_duration_seconds",
4522
+ help: "Terminal knowledge-source activity batch duration by provider and outcome.",
4523
+ labels: { provider, outcome },
4524
+ value: Math.max(0, summary.elapsedMs) / 1e3
4525
+ });
4526
+ if (summary.providerRequests > 0) {
4527
+ observability.incrementCounter({
4528
+ name: "opengeni_knowledge_source_sync_provider_requests_total",
4529
+ help: "Logical provider requests consumed by knowledge-source synchronization.",
4530
+ labels: { provider, outcome },
4531
+ amount: summary.providerRequests
4532
+ });
4533
+ }
4534
+ if (summary.limitReached) {
4535
+ observability.incrementCounter({
4536
+ name: "opengeni_knowledge_source_sync_limit_hits_total",
4537
+ help: "Knowledge-source synchronization runs stopped by an explicit resource budget.",
4538
+ labels: { provider, limit: summary.limitReached }
4539
+ });
4540
+ }
4541
+ if (summary.reconnectRequired) {
4542
+ observability.incrementCounter({
4543
+ name: "opengeni_knowledge_source_sync_reconnect_required_total",
4544
+ help: "Knowledge-source synchronization failures requiring connector reconnection.",
4545
+ labels: { provider }
4546
+ });
4547
+ }
4548
+ if (failure) {
4549
+ observability.incrementCounter({
4550
+ name: "opengeni_knowledge_source_sync_failures_total",
4551
+ help: "Knowledge-source synchronization terminal failures by bounded reason.",
4552
+ labels: {
4553
+ provider,
4554
+ reason: failure.code,
4555
+ retryable: failure.retryable ? "true" : "false"
4556
+ }
4557
+ });
4558
+ observability.warn("Knowledge source sync failed", {
4559
+ provider,
4560
+ outcome: failure.retryable ? "retryable_failed" : "failed",
4561
+ reason: failure.code,
4562
+ durationMs: summary.elapsedMs
4563
+ });
4564
+ return;
4565
+ }
4566
+ observability.info("Knowledge source sync completed", {
4567
+ provider,
4568
+ outcome: "succeeded",
4569
+ durationMs: summary.elapsedMs
4570
+ });
1409
4571
  }
4572
+ var SyncFailure = class extends Error {
4573
+ constructor(code, retryable, reconnectRequired = false) {
4574
+ super(code);
4575
+ this.code = code;
4576
+ this.retryable = retryable;
4577
+ this.reconnectRequired = reconnectRequired;
4578
+ this.name = "KnowledgeSourceSyncFailure";
4579
+ }
4580
+ };
1410
4581
 
1411
4582
  // src/activities/sandbox-images.ts
1412
4583
  import {
@@ -2557,18 +5728,18 @@ async function resumeBoxForTurn(services, ids, kind, holderId) {
2557
5728
  void release().catch(() => void 0);
2558
5729
  };
2559
5730
  holderLivenessTimer = setInterval(() => {
2560
- const heartbeat = holderLeaseHeartbeat;
2561
- if (heartbeat) {
5731
+ const heartbeat2 = holderLeaseHeartbeat;
5732
+ if (heartbeat2) {
2562
5733
  void heartbeatLeaseHolderStatus(db, {
2563
5734
  accountId: ids.accountId,
2564
5735
  workspaceId: ids.workspaceId,
2565
5736
  sandboxGroupId: ids.sandboxGroupId,
2566
5737
  kind,
2567
5738
  holderId,
2568
- expectedEpoch: heartbeat.expectedEpoch,
2569
- leaseTtlMs: heartbeat.leaseTtlMs
5739
+ expectedEpoch: heartbeat2.expectedEpoch,
5740
+ leaseTtlMs: heartbeat2.leaseTtlMs
2570
5741
  }).then((status) => {
2571
- if (heartbeat !== holderLeaseHeartbeat) return;
5742
+ if (heartbeat2 !== holderLeaseHeartbeat) return;
2572
5743
  if (!status.holderAlive) {
2573
5744
  releaseDeadHolder(status.fence);
2574
5745
  return;
@@ -2590,7 +5761,7 @@ async function resumeBoxForTurn(services, ids, kind, holderId) {
2590
5761
  kind,
2591
5762
  holderId
2592
5763
  }).then((touched) => {
2593
- if (heartbeat !== holderLeaseHeartbeat) return;
5764
+ if (heartbeat2 !== holderLeaseHeartbeat) return;
2594
5765
  if (!touched) {
2595
5766
  releaseDeadHolder("holder_gone");
2596
5767
  return;
@@ -4390,8 +7561,8 @@ import {
4390
7561
  IncidentTelemetryPreflight,
4391
7562
  stableJson
4392
7563
  } from "@opengeni/contracts";
4393
- import { createHash } from "crypto";
4394
- import { isDeepStrictEqual as isDeepStrictEqual2 } from "util";
7564
+ import { createHash as createHash3 } from "crypto";
7565
+ import { isDeepStrictEqual as isDeepStrictEqual3 } from "util";
4395
7566
  var INCIDENT_TELEMETRY_AUTHORITY_FENCE_LINEAGE_KEY = "incidentTelemetryAuthorityFence";
4396
7567
  function incidentTelemetryPreflightDeclaration(agentConfig, incidentTriggered) {
4397
7568
  const rawConfig = agentConfig;
@@ -4424,7 +7595,7 @@ function evaluateIncidentTelemetryPreflight(input) {
4424
7595
  return blocked("incident_preflight_metadata_missing");
4425
7596
  }
4426
7597
  if (preflight.requiredResources.some(
4427
- (required) => !input.responder.resources.some((selected) => isDeepStrictEqual2(selected, required))
7598
+ (required) => !input.responder.resources.some((selected) => isDeepStrictEqual3(selected, required))
4428
7599
  )) {
4429
7600
  return blocked("incident_responder_under_capable");
4430
7601
  }
@@ -4514,9 +7685,9 @@ function incidentTelemetryAuthorityFence(input) {
4514
7685
  return {
4515
7686
  version: 2,
4516
7687
  toolPolicyVersion: responder.toolPolicyVersion,
4517
- responderDigest: createHash("sha256").update(stableJson(normalized)).digest("hex"),
4518
- taskDigest: createHash("sha256").update(stableJson(task)).digest("hex"),
4519
- alertSelectorDigest: createHash("sha256").update(stableJson(selectorEntries)).digest("hex")
7688
+ responderDigest: createHash3("sha256").update(stableJson(normalized)).digest("hex"),
7689
+ taskDigest: createHash3("sha256").update(stableJson(task)).digest("hex"),
7690
+ alertSelectorDigest: createHash3("sha256").update(stableJson(selectorEntries)).digest("hex")
4520
7691
  };
4521
7692
  }
4522
7693
  function parseIncidentTelemetryAuthorityFence(lineage) {
@@ -4570,7 +7741,7 @@ import { defaultSessionMcpServerIds, resolveSessionToolPolicy } from "@opengeni/
4570
7741
  import {
4571
7742
  getRig,
4572
7743
  getScheduledScopedRigVersionMetadata,
4573
- getScheduledTaskRunAcceptedExecution,
7744
+ getScheduledTaskRunAcceptedExecution as getScheduledTaskRunAcceptedExecution2,
4574
7745
  getVariableSet,
4575
7746
  listEnabledMcpCapabilityServerIds,
4576
7747
  listInstalledApiIntegrationServerIdsForDelegations,
@@ -4744,7 +7915,7 @@ async function validateIncidentTelemetrySystemUpdateAuthority(input) {
4744
7915
  };
4745
7916
  if (!fence || typeof taskId !== "string" || !runId) return await reject();
4746
7917
  const session = await requireSession2(input.db, input.workspaceId, input.sessionId);
4747
- const acceptedExecution = await getScheduledTaskRunAcceptedExecution(input.db, {
7918
+ const acceptedExecution = await getScheduledTaskRunAcceptedExecution2(input.db, {
4748
7919
  workspaceId: input.workspaceId,
4749
7920
  runId
4750
7921
  });
@@ -5287,7 +8458,7 @@ async function drainAttemptOwnedSandboxWriters(input) {
5287
8458
  }
5288
8459
  async function releaseTurnSandboxAfterWriterDrain(sandbox, options = {}) {
5289
8460
  const maxAttempts = Math.max(1, options.maxAttempts ?? 3);
5290
- const retryDelayMs = Math.max(0, options.retryDelayMs ?? 50);
8461
+ const retryDelayMs2 = Math.max(0, options.retryDelayMs ?? 50);
5291
8462
  const wait = options.wait ?? sleep2;
5292
8463
  let lastError;
5293
8464
  for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
@@ -5297,7 +8468,7 @@ async function releaseTurnSandboxAfterWriterDrain(sandbox, options = {}) {
5297
8468
  } catch (error) {
5298
8469
  lastError = error;
5299
8470
  if (attempt < maxAttempts) {
5300
- await wait(retryDelayMs * attempt);
8471
+ await wait(retryDelayMs2 * attempt);
5301
8472
  }
5302
8473
  }
5303
8474
  }
@@ -5445,7 +8616,7 @@ import {
5445
8616
  FileResourceRef,
5446
8617
  resourceMountPath
5447
8618
  } from "@opengeni/contracts";
5448
- import { createHash as createHash2 } from "crypto";
8619
+ import { createHash as createHash4 } from "crypto";
5449
8620
  import {
5450
8621
  getActiveSessionHistoryItemsPaged,
5451
8622
  getFilesForSubject,
@@ -5563,7 +8734,7 @@ async function modelAttachmentContentForFiles(files, readFileBytes, options = {}
5563
8734
  });
5564
8735
  continue;
5565
8736
  }
5566
- if (createHash2("sha256").update(bytes).digest("hex") !== checksum) {
8737
+ if (createHash4("sha256").update(bytes).digest("hex") !== checksum) {
5567
8738
  console.error("model attachment checksum did not match finalized metadata", {
5568
8739
  fileId: file.id
5569
8740
  });
@@ -5741,7 +8912,8 @@ async function turnInput(db, runtime, agent, trigger, options) {
5741
8912
  ].join("\n") : void 0,
5742
8913
  options.unavailableSandboxFilesNote,
5743
8914
  options.runCredentialsNote,
5744
- options.mcpAvailabilityNote
8915
+ options.mcpAvailabilityNote,
8916
+ options.knowledgeSourcePreparationNote
5745
8917
  );
5746
8918
  if (trigger.type === "user.message") {
5747
8919
  const payload = trigger.payload;
@@ -6669,8 +9841,10 @@ export {
6669
9841
  signalPendingCodexCapacityWakeTargets,
6670
9842
  armAndReconcileCodexCapacityWait,
6671
9843
  createCodexCapacityActivities,
9844
+ createDocumentActivities,
6672
9845
  createGoalActivities,
6673
9846
  withFirstPartyTools,
9847
+ createKnowledgeSourceSyncActivities,
6674
9848
  rigProviderImageSourceImage,
6675
9849
  settingsWithPackSandboxImage,
6676
9850
  resolveWorkspacePackRuntime,
@@ -6761,4 +9935,4 @@ export {
6761
9935
  codexUsageLimitFailurePayload,
6762
9936
  CODEX_USAGE_LIMIT_MAX_RESUME_MS
6763
9937
  };
6764
- //# sourceMappingURL=chunk-OA5CSY5N.js.map
9938
+ //# sourceMappingURL=chunk-CYITU5UH.js.map