@agent-native/core 0.114.3 → 0.114.5

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 (93) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/core/CHANGELOG.md +14 -0
  3. package/corpus/core/package.json +1 -1
  4. package/corpus/core/src/a2a/artifact-response.ts +42 -16
  5. package/corpus/core/src/a2a/client.ts +4 -0
  6. package/corpus/core/src/a2a/handlers.ts +133 -2
  7. package/corpus/core/src/a2a/index.ts +1 -0
  8. package/corpus/core/src/a2a/types.ts +14 -0
  9. package/corpus/core/src/integrations/index.ts +6 -0
  10. package/corpus/core/src/integrations/pending-tasks-store.ts +89 -0
  11. package/corpus/core/src/integrations/webhook-handler.ts +17 -7
  12. package/corpus/core/src/scripts/call-agent.ts +54 -10
  13. package/corpus/core/src/server/agent-discovery.ts +22 -0
  14. package/corpus/templates/clips/.agents/skills/meetings/SKILL.md +11 -1
  15. package/corpus/templates/clips/AGENTS.md +14 -4
  16. package/corpus/templates/clips/actions/update-meeting.ts +13 -3
  17. package/corpus/templates/clips/actions/view-screen.ts +1 -0
  18. package/corpus/templates/clips/app/components/meetings/meeting-card.tsx +2 -1
  19. package/corpus/templates/clips/app/components/meetings/share-meeting-dialog.tsx +96 -4
  20. package/corpus/templates/clips/app/i18n/en-US.ts +11 -0
  21. package/corpus/templates/clips/app/lib/public-meeting.ts +85 -0
  22. package/corpus/templates/clips/app/routes/_app.meetings.$meetingId.tsx +11 -1
  23. package/corpus/templates/clips/app/routes/share.$shareId.tsx +6 -7
  24. package/corpus/templates/clips/app/routes/share.meeting.$meetingId.tsx +241 -135
  25. package/corpus/templates/clips/changelog/2026-07-20-meeting-share-links-can-include-the-full-transcript-with-an-.md +6 -0
  26. package/corpus/templates/clips/changelog/2026-07-20-recording-retries-the-default-mac-microphone-when-a-saved-in.md +6 -0
  27. package/corpus/templates/clips/changelog/2026-07-20-shared-clips-now-tell-agents-to-wait-while-uploads-and-trans.md +6 -0
  28. package/corpus/templates/clips/desktop/src/lib/meeting-join-url.ts +1 -33
  29. package/corpus/templates/clips/desktop/src/lib/transcription-engine.ts +65 -2
  30. package/corpus/templates/clips/server/db/schema.ts +3 -0
  31. package/corpus/templates/clips/server/lib/public-agent-context.ts +31 -21
  32. package/corpus/templates/clips/server/plugins/auth.ts +1 -0
  33. package/corpus/templates/clips/server/plugins/db.ts +6 -0
  34. package/corpus/templates/clips/server/routes/api/agent-transcript.json.get.ts +13 -3
  35. package/corpus/templates/clips/server/routes/api/public-meeting.get.ts +152 -0
  36. package/corpus/templates/clips/shared/agent-context.ts +60 -0
  37. package/corpus/templates/clips/shared/meeting-join-url.ts +36 -0
  38. package/corpus/templates/content/.agents/skills/content/SKILL.md +19 -5
  39. package/corpus/templates/content/changelog/2026-07-17-slack-created-database-entries-now-record-slack-as-their-sub.md +6 -0
  40. package/dist/a2a/artifact-response.d.ts +5 -0
  41. package/dist/a2a/artifact-response.d.ts.map +1 -1
  42. package/dist/a2a/artifact-response.js +20 -5
  43. package/dist/a2a/artifact-response.js.map +1 -1
  44. package/dist/a2a/client.d.ts +3 -1
  45. package/dist/a2a/client.d.ts.map +1 -1
  46. package/dist/a2a/client.js +2 -0
  47. package/dist/a2a/client.js.map +1 -1
  48. package/dist/a2a/handlers.d.ts.map +1 -1
  49. package/dist/a2a/handlers.js +100 -8
  50. package/dist/a2a/handlers.js.map +1 -1
  51. package/dist/a2a/index.d.ts +1 -1
  52. package/dist/a2a/index.d.ts.map +1 -1
  53. package/dist/a2a/index.js.map +1 -1
  54. package/dist/a2a/types.d.ts +12 -0
  55. package/dist/a2a/types.d.ts.map +1 -1
  56. package/dist/a2a/types.js.map +1 -1
  57. package/dist/collab/routes.d.ts +1 -1
  58. package/dist/collab/struct-routes.d.ts +1 -1
  59. package/dist/file-upload/actions/upload-image.d.ts +1 -1
  60. package/dist/integrations/index.d.ts +1 -0
  61. package/dist/integrations/index.d.ts.map +1 -1
  62. package/dist/integrations/index.js +1 -0
  63. package/dist/integrations/index.js.map +1 -1
  64. package/dist/integrations/pending-tasks-store.d.ts +9 -0
  65. package/dist/integrations/pending-tasks-store.d.ts.map +1 -1
  66. package/dist/integrations/pending-tasks-store.js +60 -0
  67. package/dist/integrations/pending-tasks-store.js.map +1 -1
  68. package/dist/integrations/webhook-handler.d.ts.map +1 -1
  69. package/dist/integrations/webhook-handler.js +13 -7
  70. package/dist/integrations/webhook-handler.js.map +1 -1
  71. package/dist/notifications/routes.d.ts +1 -1
  72. package/dist/observability/routes.d.ts +5 -5
  73. package/dist/progress/routes.d.ts +1 -1
  74. package/dist/scripts/call-agent.d.ts.map +1 -1
  75. package/dist/scripts/call-agent.js +39 -10
  76. package/dist/scripts/call-agent.js.map +1 -1
  77. package/dist/secrets/routes.d.ts +9 -9
  78. package/dist/server/agent-discovery.d.ts +2 -0
  79. package/dist/server/agent-discovery.d.ts.map +1 -1
  80. package/dist/server/agent-discovery.js +19 -0
  81. package/dist/server/agent-discovery.js.map +1 -1
  82. package/dist/server/transcribe-voice.d.ts +1 -1
  83. package/package.json +2 -2
  84. package/src/a2a/artifact-response.ts +42 -16
  85. package/src/a2a/client.ts +4 -0
  86. package/src/a2a/handlers.ts +133 -2
  87. package/src/a2a/index.ts +1 -0
  88. package/src/a2a/types.ts +14 -0
  89. package/src/integrations/index.ts +6 -0
  90. package/src/integrations/pending-tasks-store.ts +89 -0
  91. package/src/integrations/webhook-handler.ts +17 -7
  92. package/src/scripts/call-agent.ts +54 -10
  93. package/src/server/agent-discovery.ts +22 -0
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/integrations/index.ts"],"names":[],"mappings":"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","sourcesContent":["// Types\nexport type {\n PlatformAdapter,\n IncomingMessage,\n OutgoingMessage,\n IntegrationStatus,\n IntegrationsPluginOptions,\n ImmediateWebhookResponse,\n PlatformAdapterCapabilities,\n UnsupportedPlatformCapabilityError,\n IntegrationExecutionContext,\n IntegrationActorTrust,\n IntegrationContextMessage,\n IntegrationFileReference,\n IntegrationConversationType,\n IntegrationTriggerKind,\n PlatformRunProgress,\n PlatformDeliveryReceipt,\n} from \"./types.js\";\nexport { assertPlatformCapability } from \"./types.js\";\nexport { mergeIntegrationAdapters } from \"./adapter-overrides.js\";\nexport {\n deliverJsonWebhook,\n escapeSlackMrkdwn,\n isWebhookUrlAllowed,\n type DeliverJsonWebhookOptions,\n type JsonWebhookDeliveryResult,\n} from \"./webhook-delivery.js\";\n\nexport { resolveDefaultIntegrationExecutionContext } from \"./identity.js\";\nexport {\n upsertVerifiedIntegrationIdentity,\n type IntegrationIdentityLink,\n} from \"./identity-links-store.js\";\n\nexport {\n BUILT_IN_INTEGRATION_CATALOG,\n INTEGRATION_CATEGORIES,\n getIntegrationCatalogEntry,\n listBuiltInChannelIntegrations,\n listIntegrationCatalog,\n type BuiltInChannelId,\n type AutomationCapabilities,\n type ChannelCapabilities,\n type IntegrationAvailability,\n type IntegrationCatalogEntry,\n type IntegrationCategory,\n type IntegrationCredentialRequirement,\n type IntegrationIconKey,\n type IntegrationSupportMaturity,\n} from \"./catalog.js\";\n\n// Plugin\nexport {\n BUILT_IN_INTEGRATION_ADAPTER_FACTORIES,\n BUILT_IN_INTEGRATION_ADAPTER_IDS,\n createBuiltInIntegrationAdapters,\n createIntegrationsPlugin,\n defaultIntegrationsPlugin,\n enqueueRemoteCommand,\n} from \"./plugin.js\";\n\nexport {\n getRemoteComputerCapabilities,\n listRemoteDevicesForOwner,\n revokeRemoteDeviceForOwner,\n unregisterRemoteDevice,\n} from \"./remote-devices-store.js\";\nexport {\n claimNextComputerCommand,\n enqueueComputerCommand,\n listRemoteCommandsForOwner,\n} from \"./remote-commands-store.js\";\nexport {\n assertValidComputerCommandEnvelope,\n computeComputerActionHash,\n computerOperationRequiresApproval,\n ComputerSupervisionError,\n} from \"./computer-supervision.js\";\nexport {\n authorizeComputerOperation,\n createComputerApprovalRequest,\n decideComputerApproval,\n getComputerApprovalForOwner,\n listComputerApprovalsForOwner,\n type ComputerApprovalRecord,\n type ComputerApprovalStatus,\n} from \"./computer-supervision-store.js\";\nexport { insertRemoteLiveViewEvents } from \"./remote-run-events-store.js\";\nexport {\n listRemotePushNotificationsForOwner,\n listRemotePushRegistrationsForOwner,\n queueRemotePushNotifications,\n toPublicRemotePushRegistration,\n unregisterRemotePushRegistrationForOwner,\n upsertRemotePushRegistration,\n} from \"./remote-push-store.js\";\nexport { deliverPendingRemotePushNotifications } from \"./remote-push-delivery.js\";\nexport type {\n ComputerApprovalScope,\n ComputerCommandAction,\n ComputerCommandEnvelope,\n ComputerOperationClass,\n PublicRemotePushRegistration,\n PublicRemoteDevice,\n RemoteComputerCapabilities,\n RemoteCommand,\n RemoteDevice,\n RemoteDeviceMetadata,\n RemoteLiveViewEvent,\n RemotePushNotification,\n RemotePushRegistration,\n RemoteRunEvent,\n} from \"./remote-types.js\";\n\n// Adapters\nexport { slackAdapter, type SlackAdapterOptions } from \"./adapters/slack.js\";\nexport { telegramAdapter } from \"./adapters/telegram.js\";\nexport { whatsappAdapter } from \"./adapters/whatsapp.js\";\nexport { discordAdapter } from \"./adapters/discord.js\";\nexport {\n clearMicrosoftTeamsAccessTokenCache,\n getMicrosoftTeamsAccessToken,\n microsoftTeamsAdapter,\n} from \"./adapters/microsoft-teams.js\";\nexport { googleDocsAdapter } from \"./adapters/google-docs.js\";\nexport { emailAdapter } from \"./adapters/email.js\";\n\n// Google Docs integration\nexport {\n startGoogleDocsPoller,\n stopGoogleDocsPoller,\n handlePushNotification,\n} from \"./google-docs-poller.js\";\n\n// Stores\nexport {\n getIntegrationConfig,\n saveIntegrationConfig,\n deleteIntegrationConfig,\n listIntegrationConfigs,\n type IntegrationConfig,\n} from \"./config-store.js\";\n\nexport {\n disconnectIntegrationInstallation,\n getActiveIntegrationInstallationByKey,\n getActiveIntegrationInstallationForTenant,\n getIntegrationInstallation,\n listIntegrationInstallations,\n resolveIntegrationTokenBundle,\n updateIntegrationInstallation,\n upsertIntegrationInstallation,\n} from \"./installations-store.js\";\nexport type {\n InstallationActor,\n IntegrationInstallation,\n IntegrationInstallationHealth,\n IntegrationInstallationStatus,\n IntegrationTokenBundle,\n} from \"./installations-store.js\";\n\nexport {\n SLACK_AUTHORIZE_URL,\n SLACK_TOKEN_URL,\n assertSlackInstallAccess,\n buildSlackAuthorizeUrl,\n exchangeSlackOAuthCode,\n refreshSlackOAuthToken,\n slackInstallationKey,\n slackOAuthResponseToInstallation,\n testSlackAuth,\n type SlackOAuthAccessResponse,\n} from \"./slack-oauth.js\";\n\nexport {\n SLACK_AGENT_BOT_EVENTS,\n buildSlackAgentManifest,\n type SlackAgentManifestUrls,\n} from \"./slack-manifest.js\";\n\nexport {\n DEFAULT_INTEGRATION_SCOPE_POLICY,\n deleteIntegrationScope,\n evaluateIntegrationScopePolicy,\n getIntegrationScope,\n integrationScopeSubjectKey,\n listIntegrationScopes,\n saveIntegrationScope,\n} from \"./scope-store.js\";\nexport type {\n IntegrationConversationTrust,\n IntegrationScope,\n IntegrationScopeAccess,\n IntegrationScopePolicy,\n} from \"./scope-store.js\";\n\nexport {\n getIntegrationBudgetSnapshot,\n getIntegrationUsageBudget,\n listIntegrationUsageBudgets,\n listIntegrationBudgetThresholdEvents,\n releaseIntegrationUsageBudget,\n reserveIntegrationUsageBudget,\n saveIntegrationUsageBudget,\n settleIntegrationUsageBudget,\n} from \"./usage-budget-store.js\";\nexport type {\n IntegrationBudgetPeriod,\n IntegrationBudgetSubject,\n IntegrationUsageBudget,\n} from \"./usage-budget-store.js\";\n\nexport {\n getThreadMapping,\n saveThreadMapping,\n deleteThreadMapping,\n listThreadMappings,\n type ThreadMapping,\n} from \"./thread-mapping-store.js\";\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/integrations/index.ts"],"names":[],"mappings":"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","sourcesContent":["// Types\nexport type {\n PlatformAdapter,\n IncomingMessage,\n OutgoingMessage,\n IntegrationStatus,\n IntegrationsPluginOptions,\n ImmediateWebhookResponse,\n PlatformAdapterCapabilities,\n UnsupportedPlatformCapabilityError,\n IntegrationExecutionContext,\n IntegrationActorTrust,\n IntegrationContextMessage,\n IntegrationFileReference,\n IntegrationConversationType,\n IntegrationTriggerKind,\n PlatformRunProgress,\n PlatformDeliveryReceipt,\n} from \"./types.js\";\nexport { assertPlatformCapability } from \"./types.js\";\nexport { mergeIntegrationAdapters } from \"./adapter-overrides.js\";\nexport {\n deliverJsonWebhook,\n escapeSlackMrkdwn,\n isWebhookUrlAllowed,\n type DeliverJsonWebhookOptions,\n type JsonWebhookDeliveryResult,\n} from \"./webhook-delivery.js\";\n\nexport { resolveDefaultIntegrationExecutionContext } from \"./identity.js\";\nexport {\n upsertVerifiedIntegrationIdentity,\n type IntegrationIdentityLink,\n} from \"./identity-links-store.js\";\n\nexport {\n BUILT_IN_INTEGRATION_CATALOG,\n INTEGRATION_CATEGORIES,\n getIntegrationCatalogEntry,\n listBuiltInChannelIntegrations,\n listIntegrationCatalog,\n type BuiltInChannelId,\n type AutomationCapabilities,\n type ChannelCapabilities,\n type IntegrationAvailability,\n type IntegrationCatalogEntry,\n type IntegrationCategory,\n type IntegrationCredentialRequirement,\n type IntegrationIconKey,\n type IntegrationSupportMaturity,\n} from \"./catalog.js\";\n\n// Plugin\nexport {\n BUILT_IN_INTEGRATION_ADAPTER_FACTORIES,\n BUILT_IN_INTEGRATION_ADAPTER_IDS,\n createBuiltInIntegrationAdapters,\n createIntegrationsPlugin,\n defaultIntegrationsPlugin,\n enqueueRemoteCommand,\n} from \"./plugin.js\";\n\nexport {\n getRemoteComputerCapabilities,\n listRemoteDevicesForOwner,\n revokeRemoteDeviceForOwner,\n unregisterRemoteDevice,\n} from \"./remote-devices-store.js\";\nexport {\n claimNextComputerCommand,\n enqueueComputerCommand,\n listRemoteCommandsForOwner,\n} from \"./remote-commands-store.js\";\nexport {\n assertValidComputerCommandEnvelope,\n computeComputerActionHash,\n computerOperationRequiresApproval,\n ComputerSupervisionError,\n} from \"./computer-supervision.js\";\nexport {\n authorizeComputerOperation,\n createComputerApprovalRequest,\n decideComputerApproval,\n getComputerApprovalForOwner,\n listComputerApprovalsForOwner,\n type ComputerApprovalRecord,\n type ComputerApprovalStatus,\n} from \"./computer-supervision-store.js\";\nexport { insertRemoteLiveViewEvents } from \"./remote-run-events-store.js\";\nexport {\n listRemotePushNotificationsForOwner,\n listRemotePushRegistrationsForOwner,\n queueRemotePushNotifications,\n toPublicRemotePushRegistration,\n unregisterRemotePushRegistrationForOwner,\n upsertRemotePushRegistration,\n} from \"./remote-push-store.js\";\nexport { deliverPendingRemotePushNotifications } from \"./remote-push-delivery.js\";\nexport type {\n ComputerApprovalScope,\n ComputerCommandAction,\n ComputerCommandEnvelope,\n ComputerOperationClass,\n PublicRemotePushRegistration,\n PublicRemoteDevice,\n RemoteComputerCapabilities,\n RemoteCommand,\n RemoteDevice,\n RemoteDeviceMetadata,\n RemoteLiveViewEvent,\n RemotePushNotification,\n RemotePushRegistration,\n RemoteRunEvent,\n} from \"./remote-types.js\";\n\n// Adapters\nexport { slackAdapter, type SlackAdapterOptions } from \"./adapters/slack.js\";\nexport { telegramAdapter } from \"./adapters/telegram.js\";\nexport { whatsappAdapter } from \"./adapters/whatsapp.js\";\nexport { discordAdapter } from \"./adapters/discord.js\";\nexport {\n clearMicrosoftTeamsAccessTokenCache,\n getMicrosoftTeamsAccessToken,\n microsoftTeamsAdapter,\n} from \"./adapters/microsoft-teams.js\";\nexport { googleDocsAdapter } from \"./adapters/google-docs.js\";\nexport { emailAdapter } from \"./adapters/email.js\";\n\n// Google Docs integration\nexport {\n startGoogleDocsPoller,\n stopGoogleDocsPoller,\n handlePushNotification,\n} from \"./google-docs-poller.js\";\n\n// Stores\nexport {\n getIntegrationConfig,\n saveIntegrationConfig,\n deleteIntegrationConfig,\n listIntegrationConfigs,\n type IntegrationConfig,\n} from \"./config-store.js\";\n\nexport {\n disconnectIntegrationInstallation,\n getActiveIntegrationInstallationByKey,\n getActiveIntegrationInstallationForTenant,\n getIntegrationInstallation,\n listIntegrationInstallations,\n resolveIntegrationTokenBundle,\n updateIntegrationInstallation,\n upsertIntegrationInstallation,\n} from \"./installations-store.js\";\nexport type {\n InstallationActor,\n IntegrationInstallation,\n IntegrationInstallationHealth,\n IntegrationInstallationStatus,\n IntegrationTokenBundle,\n} from \"./installations-store.js\";\n\nexport {\n SLACK_AUTHORIZE_URL,\n SLACK_TOKEN_URL,\n assertSlackInstallAccess,\n buildSlackAuthorizeUrl,\n exchangeSlackOAuthCode,\n refreshSlackOAuthToken,\n slackInstallationKey,\n slackOAuthResponseToInstallation,\n testSlackAuth,\n type SlackOAuthAccessResponse,\n} from \"./slack-oauth.js\";\n\nexport {\n SLACK_AGENT_BOT_EVENTS,\n buildSlackAgentManifest,\n type SlackAgentManifestUrls,\n} from \"./slack-manifest.js\";\n\nexport {\n DEFAULT_INTEGRATION_SCOPE_POLICY,\n deleteIntegrationScope,\n evaluateIntegrationScopePolicy,\n getIntegrationScope,\n integrationScopeSubjectKey,\n listIntegrationScopes,\n saveIntegrationScope,\n} from \"./scope-store.js\";\nexport type {\n IntegrationConversationTrust,\n IntegrationScope,\n IntegrationScopeAccess,\n IntegrationScopePolicy,\n} from \"./scope-store.js\";\n\nexport {\n resolveIntegrationSourceContext,\n sourceContextFromPendingTask,\n type ResolvedIntegrationSourceContext,\n} from \"./pending-tasks-store.js\";\n\nexport {\n getIntegrationBudgetSnapshot,\n getIntegrationUsageBudget,\n listIntegrationUsageBudgets,\n listIntegrationBudgetThresholdEvents,\n releaseIntegrationUsageBudget,\n reserveIntegrationUsageBudget,\n saveIntegrationUsageBudget,\n settleIntegrationUsageBudget,\n} from \"./usage-budget-store.js\";\nexport type {\n IntegrationBudgetPeriod,\n IntegrationBudgetSubject,\n IntegrationUsageBudget,\n} from \"./usage-budget-store.js\";\n\nexport {\n getThreadMapping,\n saveThreadMapping,\n deleteThreadMapping,\n listThreadMappings,\n type ThreadMapping,\n} from \"./thread-mapping-store.js\";\n"]}
@@ -52,6 +52,14 @@ export declare function insertPendingTask(input: {
52
52
  export declare function isDuplicateEventError(err: unknown): boolean;
53
53
  /** Fetch a pending task by id. */
54
54
  export declare function getPendingTask(id: string): Promise<PendingTask | null>;
55
+ export interface ResolvedIntegrationSourceContext {
56
+ platform: "slack";
57
+ sourceUrl: string;
58
+ }
59
+ type PendingTaskSourceRow = Pick<PendingTask, "platform" | "payload" | "ownerEmail" | "orgId">;
60
+ export declare function sourceContextFromPendingTask(task: PendingTaskSourceRow | null, ownerEmail: string, orgId: string | null): ResolvedIntegrationSourceContext | null;
61
+ /** Resolve trusted Slack provenance without exposing the stored task payload. */
62
+ export declare function resolveIntegrationSourceContext(id: string, ownerEmail: string, orgId: string | null): Promise<ResolvedIntegrationSourceContext | null>;
55
63
  /**
56
64
  * Atomically claim a task: transition pending → processing and increment
57
65
  * attempts. Returns the updated task if the transition succeeded, otherwise
@@ -73,4 +81,5 @@ export declare function markTaskDeliveryRetryable(id: string, payload: string, e
73
81
  export declare function failTaskDeliveryTransition(id: string, errorMessage: string): Promise<void>;
74
82
  /** Mark a task as failed and stash an error message. */
75
83
  export declare function markTaskFailed(id: string, errorMessage: string): Promise<void>;
84
+ export {};
76
85
  //# sourceMappingURL=pending-tasks-store.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"pending-tasks-store.d.ts","sourceRoot":"","sources":["../../src/integrations/pending-tasks-store.ts"],"names":[],"mappings":"AAoBA,eAAO,MAAM,yBAAyB,IAAI,CAAC;AAyF3C,qDAAqD;AACrD,MAAM,MAAM,iBAAiB,GACzB,SAAS,GACT,YAAY,GACZ,WAAW,GACX,QAAQ,CAAC;AAEb,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,gBAAgB,EAAE,MAAM,CAAC;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,MAAM,EAAE,iBAAiB,CAAC;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED;;;;;;GAMG;AACH,wBAAsB,oBAAoB,CACxC,QAAQ,EAAE,MAAM,EAChB,gBAAgB,EAAE,MAAM,GACvB,OAAO,CAAC,OAAO,CAAC,CAalB;AAoBD;;;;;;;;GAQG;AACH,wBAAsB,iBAAiB,CAAC,KAAK,EAAE;IAC7C,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,gBAAgB,EAAE,MAAM,CAAC;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAClC,GAAG,OAAO,CAAC,IAAI,CAAC,CAsBhB;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAU3D;AAED,kCAAkC;AAClC,wBAAsB,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAU5E;AAED;;;;GAIG;AACH,wBAAsB,gBAAgB,CACpC,EAAE,EAAE,MAAM,GACT,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CA0E7B;AAED,+EAA+E;AAC/E,wBAAsB,6BAA6B,CACjD,QAAQ,EAAE,MAAM,EAChB,gBAAgB,EAAE,MAAM,GACvB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CASxB;AAED,gCAAgC;AAChC,wBAAsB,iBAAiB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAajE;AAED;;;;GAIG;AACH,wBAAsB,iBAAiB,CACrC,EAAE,EAAE,MAAM,EACV,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,IAAI,CAAC,CAUf;AAED,wBAAsB,wBAAwB,CAC5C,EAAE,EAAE,MAAM,EACV,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,IAAI,CAAC,CAkBf;AAED,wBAAsB,yBAAyB,CAC7C,EAAE,EAAE,MAAM,EACV,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,IAAI,CAAC,CAmBf;AAED,wBAAsB,0BAA0B,CAC9C,EAAE,EAAE,MAAM,EACV,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,IAAI,CAAC,CAkBf;AAED,wDAAwD;AACxD,wBAAsB,cAAc,CAClC,EAAE,EAAE,MAAM,EACV,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,IAAI,CAAC,CAUf"}
1
+ {"version":3,"file":"pending-tasks-store.d.ts","sourceRoot":"","sources":["../../src/integrations/pending-tasks-store.ts"],"names":[],"mappings":"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"}
@@ -177,6 +177,66 @@ export async function getPendingTask(id) {
177
177
  return null;
178
178
  return rowToTask(rows[0]);
179
179
  }
180
+ export function sourceContextFromPendingTask(task, ownerEmail, orgId) {
181
+ if (!task ||
182
+ task.ownerEmail !== ownerEmail ||
183
+ task.orgId !== orgId ||
184
+ task.platform !== "slack") {
185
+ return null;
186
+ }
187
+ try {
188
+ const payload = JSON.parse(task.payload);
189
+ const incoming = payload.incoming;
190
+ if (!incoming || typeof incoming !== "object")
191
+ return null;
192
+ const incomingRecord = incoming;
193
+ if (incomingRecord.platform !== "slack")
194
+ return null;
195
+ const sourceUrl = incomingRecord.sourceUrl;
196
+ if (typeof sourceUrl !== "string" ||
197
+ !sourceUrl ||
198
+ sourceUrl !== sourceUrl.trim()) {
199
+ return null;
200
+ }
201
+ const parsed = new URL(sourceUrl);
202
+ const isSlackHost = parsed.hostname === "slack.com" || parsed.hostname.endsWith(".slack.com");
203
+ if (parsed.protocol !== "https:" ||
204
+ !isSlackHost ||
205
+ parsed.username ||
206
+ parsed.password ||
207
+ parsed.port) {
208
+ return null;
209
+ }
210
+ return { platform: "slack", sourceUrl };
211
+ }
212
+ catch {
213
+ return null;
214
+ }
215
+ }
216
+ /** Resolve trusted Slack provenance without exposing the stored task payload. */
217
+ export async function resolveIntegrationSourceContext(id, ownerEmail, orgId) {
218
+ await ensureTable();
219
+ const client = getDbExec();
220
+ const { rows } = await client.execute({
221
+ sql: `SELECT platform, payload, owner_email, org_id
222
+ FROM integration_pending_tasks
223
+ WHERE id = ?
224
+ AND owner_email = ?
225
+ AND (org_id = ? OR (org_id IS NULL AND ? IS NULL))
226
+ AND platform = 'slack'
227
+ LIMIT 1`,
228
+ args: [id, ownerEmail, orgId, orgId],
229
+ });
230
+ if (rows.length === 0)
231
+ return null;
232
+ const row = rows[0];
233
+ return sourceContextFromPendingTask({
234
+ platform: row.platform,
235
+ payload: row.payload,
236
+ ownerEmail: row.owner_email,
237
+ orgId: row.org_id ?? null,
238
+ }, ownerEmail, orgId);
239
+ }
180
240
  /**
181
241
  * Atomically claim a task: transition pending → processing and increment
182
242
  * attempts. Returns the updated task if the transition succeeded, otherwise
@@ -1 +1 @@
1
- {"version":3,"file":"pending-tasks-store.js","sourceRoot":"","sources":["../../src/integrations/pending-tasks-store.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * SQL-backed pending task queue for integration webhooks.\n *\n * Why this exists: serverless platforms (Netlify Lambda, Vercel, Cloudflare\n * Workers) freeze the function execution as soon as the HTTP response is\n * returned. Fire-and-forget background `Promise`s get killed mid-flight,\n * meaning agent loops triggered from a Slack/Telegram webhook never finish.\n *\n * Solution: persist the inbound message to SQL inside the webhook handler,\n * then dispatch a fresh HTTP POST to a separate processor endpoint. Each\n * invocation gets its own fresh function timeout budget.\n */\nimport { getDbExec, isPostgres, intType } from \"../db/client.js\";\nimport {\n ensureTableExists,\n ensureColumnExists,\n ensureIndexExists,\n} from \"../db/ddl-guard.js\";\n\nlet _initPromise: Promise<void> | undefined;\nexport const MAX_PENDING_TASK_ATTEMPTS = 3;\n\nasync function ensureTable(): Promise<void> {\n if (!_initPromise) {\n _initPromise = (async () => {\n const client = getDbExec();\n const createSql = `CREATE TABLE IF NOT EXISTS integration_pending_tasks (\n id TEXT PRIMARY KEY,\n platform TEXT NOT NULL,\n external_thread_id TEXT NOT NULL,\n payload TEXT NOT NULL,\n owner_email TEXT NOT NULL,\n org_id TEXT,\n status TEXT NOT NULL,\n attempts ${intType()} NOT NULL DEFAULT 0,\n error_message TEXT,\n created_at ${intType()} NOT NULL,\n updated_at ${intType()} NOT NULL,\n completed_at ${intType()}\n)`;\n\n if (isPostgres()) {\n // PG guard: probe via information_schema, only issue DDL if missing, bounded lock_timeout\n await ensureTableExists(\"integration_pending_tasks\", createSql);\n await ensureColumnExists(\n \"integration_pending_tasks\",\n \"external_event_key\",\n `ALTER TABLE integration_pending_tasks ADD COLUMN IF NOT EXISTS external_event_key TEXT`,\n );\n await ensureIndexExists(\n \"idx_pending_tasks_status_created\",\n `CREATE INDEX IF NOT EXISTS idx_pending_tasks_status_created ON integration_pending_tasks(status, created_at)`,\n );\n await ensureIndexExists(\n \"idx_pending_tasks_event_key\",\n `CREATE UNIQUE INDEX IF NOT EXISTS idx_pending_tasks_event_key ON integration_pending_tasks(platform, external_event_key)`,\n );\n return;\n }\n\n await client.execute(createSql);\n await client.execute(\n `CREATE INDEX IF NOT EXISTS idx_pending_tasks_status_created ON integration_pending_tasks(status, created_at)`,\n );\n // Additive migration: add a stable per-event dedup key so duplicate\n // webhook deliveries from the same platform get rejected at the SQL\n // layer instead of via an in-memory Map (which doesn't survive\n // serverless cold starts — H3 in the webhook security audit). The\n // unique index ensures a duplicate INSERT raises an error we can\n // catch as \"already-enqueued\".\n await ensureExternalEventKey(client);\n await client.execute(\n `CREATE UNIQUE INDEX IF NOT EXISTS idx_pending_tasks_event_key ON integration_pending_tasks(platform, external_event_key)`,\n );\n })().catch((err) => {\n // Retry init on the next call after a failed startup.\n _initPromise = undefined;\n throw err;\n });\n }\n return _initPromise;\n}\n\nasync function ensureExternalEventKey(\n client: ReturnType<typeof getDbExec>,\n): Promise<void> {\n if (isPostgres()) {\n await client.execute(\n `ALTER TABLE integration_pending_tasks ADD COLUMN IF NOT EXISTS external_event_key TEXT`,\n );\n return;\n }\n // SQLite doesn't support `ADD COLUMN IF NOT EXISTS` until 3.35; swallow\n // the duplicate-column error so reruns are idempotent.\n try {\n await client.execute(\n `ALTER TABLE integration_pending_tasks ADD COLUMN external_event_key TEXT`,\n );\n } catch (err: any) {\n if (\n !String(err?.message ?? err)\n .toLowerCase()\n .includes(\"duplicate\")\n ) {\n throw err;\n }\n }\n}\n\n/** Status values for an integration pending task. */\nexport type PendingTaskStatus =\n | \"pending\"\n | \"processing\"\n | \"completed\"\n | \"failed\";\n\nexport interface PendingTask {\n id: string;\n platform: string;\n externalThreadId: string;\n payload: string;\n ownerEmail: string;\n orgId: string | null;\n status: PendingTaskStatus;\n attempts: number;\n errorMessage: string | null;\n createdAt: number;\n updatedAt: number;\n completedAt: number | null;\n}\n\n/**\n * Whether a provider thread currently has queued or executing work.\n *\n * Messaging adapters use this to accept unmentioned replies only while an\n * agent task is active. This prevents broad message subscriptions from turning\n * every channel message into an agent invocation.\n */\nexport async function hasActivePendingTask(\n platform: string,\n externalThreadId: string,\n): Promise<boolean> {\n await ensureTable();\n const client = getDbExec();\n const { rows } = await client.execute({\n sql: `SELECT 1 AS active\n FROM integration_pending_tasks\n WHERE platform = ?\n AND external_thread_id = ?\n AND status IN ('pending', 'processing')\n LIMIT 1`,\n args: [platform, externalThreadId],\n });\n return rows.length > 0;\n}\n\nfunction rowToTask(row: Record<string, unknown>): PendingTask {\n return {\n id: row.id as string,\n platform: row.platform as string,\n externalThreadId: row.external_thread_id as string,\n payload: row.payload as string,\n ownerEmail: row.owner_email as string,\n orgId: (row.org_id as string | null) ?? null,\n status: row.status as PendingTaskStatus,\n attempts: Number(row.attempts ?? 0),\n errorMessage: (row.error_message as string | null) ?? null,\n createdAt: Number(row.created_at ?? 0),\n updatedAt: Number(row.updated_at ?? 0),\n completedAt:\n row.completed_at == null ? null : Number(row.completed_at as number),\n };\n}\n\n/**\n * Insert a new pending task. Returns the generated task id.\n *\n * If `externalEventKey` is supplied, the unique index on\n * `(platform, external_event_key)` will reject duplicates — callers should\n * catch the resulting constraint-violation error and treat it as\n * \"already enqueued\" instead of a hard failure (H3 in the webhook security\n * audit). This is the SQL-backed replacement for the in-memory dedup map.\n */\nexport async function insertPendingTask(input: {\n id: string;\n platform: string;\n externalThreadId: string;\n payload: string;\n ownerEmail: string;\n orgId?: string | null;\n externalEventKey?: string | null;\n}): Promise<void> {\n await ensureTable();\n const client = getDbExec();\n const now = Date.now();\n await client.execute({\n sql: `INSERT INTO integration_pending_tasks\n (id, platform, external_thread_id, payload, owner_email, org_id, status, attempts, created_at, updated_at, external_event_key)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n args: [\n input.id,\n input.platform,\n input.externalThreadId,\n input.payload,\n input.ownerEmail,\n input.orgId ?? null,\n \"pending\",\n 0,\n now,\n now,\n input.externalEventKey ?? null,\n ],\n });\n}\n\n/**\n * Returns whether a duplicate-event error from `insertPendingTask` looks\n * like a unique-constraint violation on `(platform, external_event_key)`.\n *\n * Postgres surfaces these as `error.code === \"23505\"`, while SQLite uses\n * a substring match on the error text. Used by the webhook handler to\n * distinguish \"already enqueued\" (silently OK) from genuine insert failures.\n */\nexport function isDuplicateEventError(err: unknown): boolean {\n const e = err as { code?: string; message?: string } | null;\n if (!e) return false;\n if (e.code === \"23505\") return true; // Postgres unique-violation\n const msg = String(e.message ?? \"\").toLowerCase();\n return (\n msg.includes(\"unique\") ||\n msg.includes(\"duplicate entry\") ||\n msg.includes(\"duplicate key\")\n );\n}\n\n/** Fetch a pending task by id. */\nexport async function getPendingTask(id: string): Promise<PendingTask | null> {\n await ensureTable();\n const client = getDbExec();\n const { rows } = await client.execute({\n sql: `SELECT id, platform, external_thread_id, payload, owner_email, org_id, status, attempts, error_message, created_at, updated_at, completed_at\n FROM integration_pending_tasks WHERE id = ? LIMIT 1`,\n args: [id],\n });\n if (rows.length === 0) return null;\n return rowToTask(rows[0] as Record<string, unknown>);\n}\n\n/**\n * Atomically claim a task: transition pending → processing and increment\n * attempts. Returns the updated task if the transition succeeded, otherwise\n * null (e.g. the task was already claimed by a concurrent worker).\n */\nexport async function claimPendingTask(\n id: string,\n): Promise<PendingTask | null> {\n await ensureTable();\n const client = getDbExec();\n const now = Date.now();\n\n // Conditional update: only flip if currently pending. Failed tasks are\n // terminal unless an explicit retry path resets them to pending first.\n const result = await client.execute({\n sql: isPostgres()\n ? `UPDATE integration_pending_tasks\n SET status = ?, attempts = attempts + 1, updated_at = ?\n WHERE id = ? AND status = 'pending'\n AND NOT EXISTS (\n SELECT 1 FROM integration_pending_tasks active\n WHERE active.platform = integration_pending_tasks.platform\n AND active.external_thread_id = integration_pending_tasks.external_thread_id\n AND active.status = 'processing'\n AND active.id <> integration_pending_tasks.id\n )\n AND NOT EXISTS (\n SELECT 1 FROM integration_pending_tasks earlier\n WHERE earlier.platform = integration_pending_tasks.platform\n AND earlier.external_thread_id = integration_pending_tasks.external_thread_id\n AND earlier.status = 'pending'\n AND (\n earlier.created_at < integration_pending_tasks.created_at\n OR (\n earlier.created_at = integration_pending_tasks.created_at\n AND earlier.id < integration_pending_tasks.id\n )\n )\n )\n RETURNING id, platform, external_thread_id, payload, owner_email, org_id, status, attempts, error_message, created_at, updated_at, completed_at`\n : `UPDATE integration_pending_tasks\n SET status = ?, attempts = attempts + 1, updated_at = ?\n WHERE id = ? AND status = 'pending'\n AND NOT EXISTS (\n SELECT 1 FROM integration_pending_tasks active\n WHERE active.platform = integration_pending_tasks.platform\n AND active.external_thread_id = integration_pending_tasks.external_thread_id\n AND active.status = 'processing'\n AND active.id <> integration_pending_tasks.id\n )\n AND NOT EXISTS (\n SELECT 1 FROM integration_pending_tasks earlier\n WHERE earlier.platform = integration_pending_tasks.platform\n AND earlier.external_thread_id = integration_pending_tasks.external_thread_id\n AND earlier.status = 'pending'\n AND (\n earlier.created_at < integration_pending_tasks.created_at\n OR (\n earlier.created_at = integration_pending_tasks.created_at\n AND earlier.id < integration_pending_tasks.id\n )\n )\n )`,\n args: [\"processing\", now, id],\n });\n const rows = result.rows ?? [];\n\n if (isPostgres()) {\n if (rows.length === 0) return null;\n return rowToTask(rows[0] as Record<string, unknown>);\n }\n\n // SQLite: no RETURNING, so re-read after the update. Confirm we actually\n // moved it into 'processing' (vs. lost the race).\n const affected =\n (result as { rowsAffected?: number; rowCount?: number }).rowsAffected ??\n (result as { rowsAffected?: number; rowCount?: number }).rowCount;\n if (affected === 0) return null;\n const fetched = await getPendingTask(id);\n if (!fetched || fetched.status !== \"processing\") return null;\n return fetched;\n}\n\n/** Next queued turn for a provider thread after its current task completes. */\nexport async function getNextPendingTaskIdForThread(\n platform: string,\n externalThreadId: string,\n): Promise<string | null> {\n await ensureTable();\n const { rows } = await getDbExec().execute({\n sql: `SELECT id FROM integration_pending_tasks\n WHERE platform = ? AND external_thread_id = ? AND status = 'pending'\n ORDER BY created_at ASC, id ASC LIMIT 1`,\n args: [platform, externalThreadId],\n });\n return rows[0]?.id ? String(rows[0].id) : null;\n}\n\n/** Mark a task as completed. */\nexport async function markTaskCompleted(id: string): Promise<void> {\n await ensureTable();\n const client = getDbExec();\n const now = Date.now();\n await client.execute({\n sql: `UPDATE integration_pending_tasks\n SET status = ?, updated_at = ?, completed_at = ?, payload = ?\n WHERE id = ?`,\n // The payload can contain short-lived provider credentials such as a\n // Discord interaction token. Once terminal, no retry needs the inbound\n // body, so erase it instead of retaining secrets or user text indefinitely.\n args: [\"completed\", now, now, \"{}\", id],\n });\n}\n\n/**\n * Return a transiently failed task to the retryable queue without erasing its\n * payload. The payload may contain the only copy of the inbound message and is\n * scrubbed only when the task reaches a permanent terminal state.\n */\nexport async function markTaskRetryable(\n id: string,\n errorMessage: string,\n): Promise<void> {\n await ensureTable();\n const client = getDbExec();\n const now = Date.now();\n await client.execute({\n sql: `UPDATE integration_pending_tasks\n SET status = ?, updated_at = ?, error_message = ?\n WHERE id = ? AND status = 'processing'`,\n args: [\"pending\", now, errorMessage.slice(0, 2000), id],\n });\n}\n\nexport async function stageTaskDeliveryPayload(\n id: string,\n payload: string,\n): Promise<void> {\n await ensureTable();\n const client = getDbExec();\n const now = Date.now();\n const result = await client.execute({\n sql: `UPDATE integration_pending_tasks\n SET payload = ?, updated_at = ?\n WHERE id = ? AND status = 'processing'`,\n args: [payload, now, id],\n });\n const affected = Number(\n (result as { rowsAffected?: number }).rowsAffected ??\n (result as { rowCount?: number }).rowCount ??\n 0,\n );\n if (affected === 0) {\n throw new Error(\"Integration task is no longer claimable for delivery\");\n }\n}\n\nexport async function markTaskDeliveryRetryable(\n id: string,\n payload: string,\n errorMessage: string,\n): Promise<void> {\n await ensureTable();\n const client = getDbExec();\n const result = await client.execute({\n sql: `UPDATE integration_pending_tasks\n SET status = ?, payload = ?, updated_at = ?, error_message = ?\n WHERE id = ? AND status = 'processing'`,\n args: [\"pending\", payload, Date.now(), errorMessage.slice(0, 2000), id],\n });\n const affected = Number(\n (result as { rowsAffected?: number }).rowsAffected ??\n (result as { rowCount?: number }).rowCount ??\n 0,\n );\n if (affected === 0) {\n throw new Error(\n \"Integration task is no longer claimable for delivery retry\",\n );\n }\n}\n\nexport async function failTaskDeliveryTransition(\n id: string,\n errorMessage: string,\n): Promise<void> {\n await ensureTable();\n const result = await getDbExec().execute({\n sql: `UPDATE integration_pending_tasks\n SET status = ?, updated_at = ?, error_message = ?, payload = ?\n WHERE id = ? AND status = 'processing'`,\n args: [\"failed\", Date.now(), errorMessage.slice(0, 2000), \"{}\", id],\n });\n const affected = Number(\n (result as { rowsAffected?: number }).rowsAffected ??\n (result as { rowCount?: number }).rowCount ??\n 0,\n );\n if (affected === 0) {\n throw new Error(\n \"Integration task delivery failure transition lost its race\",\n );\n }\n}\n\n/** Mark a task as failed and stash an error message. */\nexport async function markTaskFailed(\n id: string,\n errorMessage: string,\n): Promise<void> {\n await ensureTable();\n const client = getDbExec();\n const now = Date.now();\n await client.execute({\n sql: `UPDATE integration_pending_tasks\n SET status = ?, updated_at = ?, error_message = ?, payload = ?, external_event_key = NULL\n WHERE id = ?`,\n args: [\"failed\", now, errorMessage.slice(0, 2000), \"{}\", id],\n });\n}\n"]}
1
+ {"version":3,"file":"pending-tasks-store.js","sourceRoot":"","sources":["../../src/integrations/pending-tasks-store.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * SQL-backed pending task queue for integration webhooks.\n *\n * Why this exists: serverless platforms (Netlify Lambda, Vercel, Cloudflare\n * Workers) freeze the function execution as soon as the HTTP response is\n * returned. Fire-and-forget background `Promise`s get killed mid-flight,\n * meaning agent loops triggered from a Slack/Telegram webhook never finish.\n *\n * Solution: persist the inbound message to SQL inside the webhook handler,\n * then dispatch a fresh HTTP POST to a separate processor endpoint. Each\n * invocation gets its own fresh function timeout budget.\n */\nimport { getDbExec, isPostgres, intType } from \"../db/client.js\";\nimport {\n ensureTableExists,\n ensureColumnExists,\n ensureIndexExists,\n} from \"../db/ddl-guard.js\";\n\nlet _initPromise: Promise<void> | undefined;\nexport const MAX_PENDING_TASK_ATTEMPTS = 3;\n\nasync function ensureTable(): Promise<void> {\n if (!_initPromise) {\n _initPromise = (async () => {\n const client = getDbExec();\n const createSql = `CREATE TABLE IF NOT EXISTS integration_pending_tasks (\n id TEXT PRIMARY KEY,\n platform TEXT NOT NULL,\n external_thread_id TEXT NOT NULL,\n payload TEXT NOT NULL,\n owner_email TEXT NOT NULL,\n org_id TEXT,\n status TEXT NOT NULL,\n attempts ${intType()} NOT NULL DEFAULT 0,\n error_message TEXT,\n created_at ${intType()} NOT NULL,\n updated_at ${intType()} NOT NULL,\n completed_at ${intType()}\n)`;\n\n if (isPostgres()) {\n // PG guard: probe via information_schema, only issue DDL if missing, bounded lock_timeout\n await ensureTableExists(\"integration_pending_tasks\", createSql);\n await ensureColumnExists(\n \"integration_pending_tasks\",\n \"external_event_key\",\n `ALTER TABLE integration_pending_tasks ADD COLUMN IF NOT EXISTS external_event_key TEXT`,\n );\n await ensureIndexExists(\n \"idx_pending_tasks_status_created\",\n `CREATE INDEX IF NOT EXISTS idx_pending_tasks_status_created ON integration_pending_tasks(status, created_at)`,\n );\n await ensureIndexExists(\n \"idx_pending_tasks_event_key\",\n `CREATE UNIQUE INDEX IF NOT EXISTS idx_pending_tasks_event_key ON integration_pending_tasks(platform, external_event_key)`,\n );\n return;\n }\n\n await client.execute(createSql);\n await client.execute(\n `CREATE INDEX IF NOT EXISTS idx_pending_tasks_status_created ON integration_pending_tasks(status, created_at)`,\n );\n // Additive migration: add a stable per-event dedup key so duplicate\n // webhook deliveries from the same platform get rejected at the SQL\n // layer instead of via an in-memory Map (which doesn't survive\n // serverless cold starts — H3 in the webhook security audit). The\n // unique index ensures a duplicate INSERT raises an error we can\n // catch as \"already-enqueued\".\n await ensureExternalEventKey(client);\n await client.execute(\n `CREATE UNIQUE INDEX IF NOT EXISTS idx_pending_tasks_event_key ON integration_pending_tasks(platform, external_event_key)`,\n );\n })().catch((err) => {\n // Retry init on the next call after a failed startup.\n _initPromise = undefined;\n throw err;\n });\n }\n return _initPromise;\n}\n\nasync function ensureExternalEventKey(\n client: ReturnType<typeof getDbExec>,\n): Promise<void> {\n if (isPostgres()) {\n await client.execute(\n `ALTER TABLE integration_pending_tasks ADD COLUMN IF NOT EXISTS external_event_key TEXT`,\n );\n return;\n }\n // SQLite doesn't support `ADD COLUMN IF NOT EXISTS` until 3.35; swallow\n // the duplicate-column error so reruns are idempotent.\n try {\n await client.execute(\n `ALTER TABLE integration_pending_tasks ADD COLUMN external_event_key TEXT`,\n );\n } catch (err: any) {\n if (\n !String(err?.message ?? err)\n .toLowerCase()\n .includes(\"duplicate\")\n ) {\n throw err;\n }\n }\n}\n\n/** Status values for an integration pending task. */\nexport type PendingTaskStatus =\n | \"pending\"\n | \"processing\"\n | \"completed\"\n | \"failed\";\n\nexport interface PendingTask {\n id: string;\n platform: string;\n externalThreadId: string;\n payload: string;\n ownerEmail: string;\n orgId: string | null;\n status: PendingTaskStatus;\n attempts: number;\n errorMessage: string | null;\n createdAt: number;\n updatedAt: number;\n completedAt: number | null;\n}\n\n/**\n * Whether a provider thread currently has queued or executing work.\n *\n * Messaging adapters use this to accept unmentioned replies only while an\n * agent task is active. This prevents broad message subscriptions from turning\n * every channel message into an agent invocation.\n */\nexport async function hasActivePendingTask(\n platform: string,\n externalThreadId: string,\n): Promise<boolean> {\n await ensureTable();\n const client = getDbExec();\n const { rows } = await client.execute({\n sql: `SELECT 1 AS active\n FROM integration_pending_tasks\n WHERE platform = ?\n AND external_thread_id = ?\n AND status IN ('pending', 'processing')\n LIMIT 1`,\n args: [platform, externalThreadId],\n });\n return rows.length > 0;\n}\n\nfunction rowToTask(row: Record<string, unknown>): PendingTask {\n return {\n id: row.id as string,\n platform: row.platform as string,\n externalThreadId: row.external_thread_id as string,\n payload: row.payload as string,\n ownerEmail: row.owner_email as string,\n orgId: (row.org_id as string | null) ?? null,\n status: row.status as PendingTaskStatus,\n attempts: Number(row.attempts ?? 0),\n errorMessage: (row.error_message as string | null) ?? null,\n createdAt: Number(row.created_at ?? 0),\n updatedAt: Number(row.updated_at ?? 0),\n completedAt:\n row.completed_at == null ? null : Number(row.completed_at as number),\n };\n}\n\n/**\n * Insert a new pending task. Returns the generated task id.\n *\n * If `externalEventKey` is supplied, the unique index on\n * `(platform, external_event_key)` will reject duplicates — callers should\n * catch the resulting constraint-violation error and treat it as\n * \"already enqueued\" instead of a hard failure (H3 in the webhook security\n * audit). This is the SQL-backed replacement for the in-memory dedup map.\n */\nexport async function insertPendingTask(input: {\n id: string;\n platform: string;\n externalThreadId: string;\n payload: string;\n ownerEmail: string;\n orgId?: string | null;\n externalEventKey?: string | null;\n}): Promise<void> {\n await ensureTable();\n const client = getDbExec();\n const now = Date.now();\n await client.execute({\n sql: `INSERT INTO integration_pending_tasks\n (id, platform, external_thread_id, payload, owner_email, org_id, status, attempts, created_at, updated_at, external_event_key)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n args: [\n input.id,\n input.platform,\n input.externalThreadId,\n input.payload,\n input.ownerEmail,\n input.orgId ?? null,\n \"pending\",\n 0,\n now,\n now,\n input.externalEventKey ?? null,\n ],\n });\n}\n\n/**\n * Returns whether a duplicate-event error from `insertPendingTask` looks\n * like a unique-constraint violation on `(platform, external_event_key)`.\n *\n * Postgres surfaces these as `error.code === \"23505\"`, while SQLite uses\n * a substring match on the error text. Used by the webhook handler to\n * distinguish \"already enqueued\" (silently OK) from genuine insert failures.\n */\nexport function isDuplicateEventError(err: unknown): boolean {\n const e = err as { code?: string; message?: string } | null;\n if (!e) return false;\n if (e.code === \"23505\") return true; // Postgres unique-violation\n const msg = String(e.message ?? \"\").toLowerCase();\n return (\n msg.includes(\"unique\") ||\n msg.includes(\"duplicate entry\") ||\n msg.includes(\"duplicate key\")\n );\n}\n\n/** Fetch a pending task by id. */\nexport async function getPendingTask(id: string): Promise<PendingTask | null> {\n await ensureTable();\n const client = getDbExec();\n const { rows } = await client.execute({\n sql: `SELECT id, platform, external_thread_id, payload, owner_email, org_id, status, attempts, error_message, created_at, updated_at, completed_at\n FROM integration_pending_tasks WHERE id = ? LIMIT 1`,\n args: [id],\n });\n if (rows.length === 0) return null;\n return rowToTask(rows[0] as Record<string, unknown>);\n}\n\nexport interface ResolvedIntegrationSourceContext {\n platform: \"slack\";\n sourceUrl: string;\n}\n\ntype PendingTaskSourceRow = Pick<\n PendingTask,\n \"platform\" | \"payload\" | \"ownerEmail\" | \"orgId\"\n>;\n\nexport function sourceContextFromPendingTask(\n task: PendingTaskSourceRow | null,\n ownerEmail: string,\n orgId: string | null,\n): ResolvedIntegrationSourceContext | null {\n if (\n !task ||\n task.ownerEmail !== ownerEmail ||\n task.orgId !== orgId ||\n task.platform !== \"slack\"\n ) {\n return null;\n }\n\n try {\n const payload = JSON.parse(task.payload) as Record<string, unknown>;\n const incoming = payload.incoming;\n if (!incoming || typeof incoming !== \"object\") return null;\n const incomingRecord = incoming as Record<string, unknown>;\n if (incomingRecord.platform !== \"slack\") return null;\n const sourceUrl = incomingRecord.sourceUrl;\n if (\n typeof sourceUrl !== \"string\" ||\n !sourceUrl ||\n sourceUrl !== sourceUrl.trim()\n ) {\n return null;\n }\n\n const parsed = new URL(sourceUrl);\n const isSlackHost =\n parsed.hostname === \"slack.com\" || parsed.hostname.endsWith(\".slack.com\");\n if (\n parsed.protocol !== \"https:\" ||\n !isSlackHost ||\n parsed.username ||\n parsed.password ||\n parsed.port\n ) {\n return null;\n }\n return { platform: \"slack\", sourceUrl };\n } catch {\n return null;\n }\n}\n\n/** Resolve trusted Slack provenance without exposing the stored task payload. */\nexport async function resolveIntegrationSourceContext(\n id: string,\n ownerEmail: string,\n orgId: string | null,\n): Promise<ResolvedIntegrationSourceContext | null> {\n await ensureTable();\n const client = getDbExec();\n const { rows } = await client.execute({\n sql: `SELECT platform, payload, owner_email, org_id\n FROM integration_pending_tasks\n WHERE id = ?\n AND owner_email = ?\n AND (org_id = ? OR (org_id IS NULL AND ? IS NULL))\n AND platform = 'slack'\n LIMIT 1`,\n args: [id, ownerEmail, orgId, orgId],\n });\n if (rows.length === 0) return null;\n const row = rows[0] as Record<string, unknown>;\n return sourceContextFromPendingTask(\n {\n platform: row.platform as string,\n payload: row.payload as string,\n ownerEmail: row.owner_email as string,\n orgId: (row.org_id as string | null) ?? null,\n },\n ownerEmail,\n orgId,\n );\n}\n\n/**\n * Atomically claim a task: transition pending → processing and increment\n * attempts. Returns the updated task if the transition succeeded, otherwise\n * null (e.g. the task was already claimed by a concurrent worker).\n */\nexport async function claimPendingTask(\n id: string,\n): Promise<PendingTask | null> {\n await ensureTable();\n const client = getDbExec();\n const now = Date.now();\n\n // Conditional update: only flip if currently pending. Failed tasks are\n // terminal unless an explicit retry path resets them to pending first.\n const result = await client.execute({\n sql: isPostgres()\n ? `UPDATE integration_pending_tasks\n SET status = ?, attempts = attempts + 1, updated_at = ?\n WHERE id = ? AND status = 'pending'\n AND NOT EXISTS (\n SELECT 1 FROM integration_pending_tasks active\n WHERE active.platform = integration_pending_tasks.platform\n AND active.external_thread_id = integration_pending_tasks.external_thread_id\n AND active.status = 'processing'\n AND active.id <> integration_pending_tasks.id\n )\n AND NOT EXISTS (\n SELECT 1 FROM integration_pending_tasks earlier\n WHERE earlier.platform = integration_pending_tasks.platform\n AND earlier.external_thread_id = integration_pending_tasks.external_thread_id\n AND earlier.status = 'pending'\n AND (\n earlier.created_at < integration_pending_tasks.created_at\n OR (\n earlier.created_at = integration_pending_tasks.created_at\n AND earlier.id < integration_pending_tasks.id\n )\n )\n )\n RETURNING id, platform, external_thread_id, payload, owner_email, org_id, status, attempts, error_message, created_at, updated_at, completed_at`\n : `UPDATE integration_pending_tasks\n SET status = ?, attempts = attempts + 1, updated_at = ?\n WHERE id = ? AND status = 'pending'\n AND NOT EXISTS (\n SELECT 1 FROM integration_pending_tasks active\n WHERE active.platform = integration_pending_tasks.platform\n AND active.external_thread_id = integration_pending_tasks.external_thread_id\n AND active.status = 'processing'\n AND active.id <> integration_pending_tasks.id\n )\n AND NOT EXISTS (\n SELECT 1 FROM integration_pending_tasks earlier\n WHERE earlier.platform = integration_pending_tasks.platform\n AND earlier.external_thread_id = integration_pending_tasks.external_thread_id\n AND earlier.status = 'pending'\n AND (\n earlier.created_at < integration_pending_tasks.created_at\n OR (\n earlier.created_at = integration_pending_tasks.created_at\n AND earlier.id < integration_pending_tasks.id\n )\n )\n )`,\n args: [\"processing\", now, id],\n });\n const rows = result.rows ?? [];\n\n if (isPostgres()) {\n if (rows.length === 0) return null;\n return rowToTask(rows[0] as Record<string, unknown>);\n }\n\n // SQLite: no RETURNING, so re-read after the update. Confirm we actually\n // moved it into 'processing' (vs. lost the race).\n const affected =\n (result as { rowsAffected?: number; rowCount?: number }).rowsAffected ??\n (result as { rowsAffected?: number; rowCount?: number }).rowCount;\n if (affected === 0) return null;\n const fetched = await getPendingTask(id);\n if (!fetched || fetched.status !== \"processing\") return null;\n return fetched;\n}\n\n/** Next queued turn for a provider thread after its current task completes. */\nexport async function getNextPendingTaskIdForThread(\n platform: string,\n externalThreadId: string,\n): Promise<string | null> {\n await ensureTable();\n const { rows } = await getDbExec().execute({\n sql: `SELECT id FROM integration_pending_tasks\n WHERE platform = ? AND external_thread_id = ? AND status = 'pending'\n ORDER BY created_at ASC, id ASC LIMIT 1`,\n args: [platform, externalThreadId],\n });\n return rows[0]?.id ? String(rows[0].id) : null;\n}\n\n/** Mark a task as completed. */\nexport async function markTaskCompleted(id: string): Promise<void> {\n await ensureTable();\n const client = getDbExec();\n const now = Date.now();\n await client.execute({\n sql: `UPDATE integration_pending_tasks\n SET status = ?, updated_at = ?, completed_at = ?, payload = ?\n WHERE id = ?`,\n // The payload can contain short-lived provider credentials such as a\n // Discord interaction token. Once terminal, no retry needs the inbound\n // body, so erase it instead of retaining secrets or user text indefinitely.\n args: [\"completed\", now, now, \"{}\", id],\n });\n}\n\n/**\n * Return a transiently failed task to the retryable queue without erasing its\n * payload. The payload may contain the only copy of the inbound message and is\n * scrubbed only when the task reaches a permanent terminal state.\n */\nexport async function markTaskRetryable(\n id: string,\n errorMessage: string,\n): Promise<void> {\n await ensureTable();\n const client = getDbExec();\n const now = Date.now();\n await client.execute({\n sql: `UPDATE integration_pending_tasks\n SET status = ?, updated_at = ?, error_message = ?\n WHERE id = ? AND status = 'processing'`,\n args: [\"pending\", now, errorMessage.slice(0, 2000), id],\n });\n}\n\nexport async function stageTaskDeliveryPayload(\n id: string,\n payload: string,\n): Promise<void> {\n await ensureTable();\n const client = getDbExec();\n const now = Date.now();\n const result = await client.execute({\n sql: `UPDATE integration_pending_tasks\n SET payload = ?, updated_at = ?\n WHERE id = ? AND status = 'processing'`,\n args: [payload, now, id],\n });\n const affected = Number(\n (result as { rowsAffected?: number }).rowsAffected ??\n (result as { rowCount?: number }).rowCount ??\n 0,\n );\n if (affected === 0) {\n throw new Error(\"Integration task is no longer claimable for delivery\");\n }\n}\n\nexport async function markTaskDeliveryRetryable(\n id: string,\n payload: string,\n errorMessage: string,\n): Promise<void> {\n await ensureTable();\n const client = getDbExec();\n const result = await client.execute({\n sql: `UPDATE integration_pending_tasks\n SET status = ?, payload = ?, updated_at = ?, error_message = ?\n WHERE id = ? AND status = 'processing'`,\n args: [\"pending\", payload, Date.now(), errorMessage.slice(0, 2000), id],\n });\n const affected = Number(\n (result as { rowsAffected?: number }).rowsAffected ??\n (result as { rowCount?: number }).rowCount ??\n 0,\n );\n if (affected === 0) {\n throw new Error(\n \"Integration task is no longer claimable for delivery retry\",\n );\n }\n}\n\nexport async function failTaskDeliveryTransition(\n id: string,\n errorMessage: string,\n): Promise<void> {\n await ensureTable();\n const result = await getDbExec().execute({\n sql: `UPDATE integration_pending_tasks\n SET status = ?, updated_at = ?, error_message = ?, payload = ?\n WHERE id = ? AND status = 'processing'`,\n args: [\"failed\", Date.now(), errorMessage.slice(0, 2000), \"{}\", id],\n });\n const affected = Number(\n (result as { rowsAffected?: number }).rowsAffected ??\n (result as { rowCount?: number }).rowCount ??\n 0,\n );\n if (affected === 0) {\n throw new Error(\n \"Integration task delivery failure transition lost its race\",\n );\n }\n}\n\n/** Mark a task as failed and stash an error message. */\nexport async function markTaskFailed(\n id: string,\n errorMessage: string,\n): Promise<void> {\n await ensureTable();\n const client = getDbExec();\n const now = Date.now();\n await client.execute({\n sql: `UPDATE integration_pending_tasks\n SET status = ?, updated_at = ?, error_message = ?, payload = ?, external_event_key = NULL\n WHERE id = ?`,\n args: [\"failed\", now, errorMessage.slice(0, 2000), \"{}\", id],\n });\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"webhook-handler.d.ts","sourceRoot":"","sources":["../../src/integrations/webhook-handler.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"webhook-handler.d.ts","sourceRoot":"","sources":["../../src/integrations/webhook-handler.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { appendA2AArtifactLinks, buildA2AVerifiedMutationReceipt, extractA2AArtifactIdentities, } from "../a2a/artifact-response.js";
1
+ import { buildA2AVerifiedMutationReceipt, extractA2AArtifactIdentities, guardA2AArtifactResponse, } from "../a2a/artifact-response.js";
2
2
  import { collectFinalResponseTextFromAgentEvents } from "../a2a/response-text.js";
3
3
  import { formatLlmCredentialErrorMessage, isLlmCredentialError, } from "../agent/engine/credential-errors.js";
4
4
  import { getConfiguredEngineNameForRequest, getStoredModelForEngine, normalizeModelForEngine, resolveEngine, } from "../agent/engine/index.js";
@@ -235,7 +235,7 @@ function immediateWebhookResponse(adapter, incoming) {
235
235
  * server, but each handler still runs to completion.
236
236
  */
237
237
  async function enqueueAndDispatch(event, incoming, options, handlerStartedAt = Date.now()) {
238
- const taskId = `task-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
238
+ const taskId = crypto.randomUUID();
239
239
  // Resolve the org id once at enqueue-time so the processor doesn't have to
240
240
  // re-derive it (and so we can drop it on the row for observability).
241
241
  let orgId = options.orgId ?? null;
@@ -701,7 +701,7 @@ async function processIncomingMessage(incoming, options, opts = {}) {
701
701
  responseText = recoverableA2AArtifactText;
702
702
  }
703
703
  }
704
- const suppressPlatformReply = queuedA2AContinuation &&
704
+ let suppressPlatformReply = queuedA2AContinuation &&
705
705
  isQueuedA2AContinuationDeferral(responseText);
706
706
  // Compute trusted tool receipts before choosing the empty-answer
707
707
  // fallback. A completed write must not be reported as though nothing
@@ -749,10 +749,16 @@ async function processIncomingMessage(incoming, options, opts = {}) {
749
749
  // platforms with rich blocks (Slack) can render a button instead
750
750
  // of inlining a `<url|text>` link that auto-unfurls into a giant
751
751
  // preview card.
752
- if (!suppressPlatformReply) {
753
- responseText = appendA2AArtifactLinks(responseText, toolResults, {
754
- baseUrl: appBaseUrl || undefined,
755
- });
752
+ const guardedResponse = guardA2AArtifactResponse(responseText, toolResults, { baseUrl: appBaseUrl || undefined });
753
+ const queuedArtifactRejection = queuedA2AContinuation &&
754
+ guardedResponse.rejectedUnverifiedArtifactReferences;
755
+ if (queuedArtifactRejection && verifiedMutationReceipt) {
756
+ responseText = verifiedMutationReceipt;
757
+ suppressPlatformReply = false;
758
+ }
759
+ else {
760
+ responseText = guardedResponse.text;
761
+ suppressPlatformReply ||= queuedArtifactRejection;
756
762
  }
757
763
  const threadDeepLinkUrl = appBaseUrl && threadId
758
764
  ? `${appBaseUrl}/chat/${encodeURIComponent(threadId)}`