@wildo-ai/saas-backend-lib 1.1.0 → 1.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/esm/base-classes/services/controllers/controller-resource.backend.service.d.ts +14 -3
- package/dist/esm/base-classes/services/controllers/controller-resource.backend.service.d.ts.map +1 -1
- package/dist/esm/base-classes/services/controllers/controller-resource.backend.service.js +115 -22
- package/dist/esm/base-classes/services/controllers/controller-resource.backend.service.js.map +1 -1
- package/dist/esm/base-classes/services/controllers/controllers-registry-handler.backend.service.d.ts +3 -1
- package/dist/esm/base-classes/services/controllers/controllers-registry-handler.backend.service.d.ts.map +1 -1
- package/dist/esm/base-classes/services/controllers/controllers-registry-handler.backend.service.js +8 -3
- package/dist/esm/base-classes/services/controllers/controllers-registry-handler.backend.service.js.map +1 -1
- package/dist/esm/base-classes/services/repositories/field-clearing-partition.backend.d.ts +11 -0
- package/dist/esm/base-classes/services/repositories/field-clearing-partition.backend.d.ts.map +1 -1
- package/dist/esm/base-classes/services/repositories/field-clearing-partition.backend.js +11 -2
- package/dist/esm/base-classes/services/repositories/field-clearing-partition.backend.js.map +1 -1
- package/dist/esm/base-classes/services/repositories/http-api/http-api-resource-repository.backend.service.d.ts +1 -0
- package/dist/esm/base-classes/services/repositories/http-api/http-api-resource-repository.backend.service.d.ts.map +1 -1
- package/dist/esm/base-classes/services/repositories/http-api/http-api-resource-repository.backend.service.js +6 -1
- package/dist/esm/base-classes/services/repositories/http-api/http-api-resource-repository.backend.service.js.map +1 -1
- package/dist/esm/base-classes/services/repositories/http-api/odata-query-compiler.backend.d.ts.map +1 -1
- package/dist/esm/base-classes/services/repositories/http-api/odata-query-compiler.backend.js +16 -0
- package/dist/esm/base-classes/services/repositories/http-api/odata-query-compiler.backend.js.map +1 -1
- package/dist/esm/base-classes/services/repositories/infrastructure-tier-contract.backend.d.ts +20 -6
- package/dist/esm/base-classes/services/repositories/infrastructure-tier-contract.backend.d.ts.map +1 -1
- package/dist/esm/base-classes/services/repositories/infrastructure-tier-contract.backend.js +88 -32
- package/dist/esm/base-classes/services/repositories/infrastructure-tier-contract.backend.js.map +1 -1
- package/dist/esm/base-classes/services/repositories/introspection/introspection-resource-repository.backend.service.d.ts +3 -1
- package/dist/esm/base-classes/services/repositories/introspection/introspection-resource-repository.backend.service.d.ts.map +1 -1
- package/dist/esm/base-classes/services/repositories/introspection/introspection-resource-repository.backend.service.js +32 -7
- package/dist/esm/base-classes/services/repositories/introspection/introspection-resource-repository.backend.service.js.map +1 -1
- package/dist/esm/base-classes/services/repositories/introspection/introspection-resource-source.backend.port.d.ts +8 -5
- package/dist/esm/base-classes/services/repositories/introspection/introspection-resource-source.backend.port.d.ts.map +1 -1
- package/dist/esm/base-classes/services/repositories/introspection/introspection-resource-source.backend.port.js.map +1 -1
- package/dist/esm/base-classes/services/repositories/postgresql-resource-repository.backend.service.d.ts +31 -0
- package/dist/esm/base-classes/services/repositories/postgresql-resource-repository.backend.service.d.ts.map +1 -1
- package/dist/esm/base-classes/services/repositories/postgresql-resource-repository.backend.service.js +127 -5
- package/dist/esm/base-classes/services/repositories/postgresql-resource-repository.backend.service.js.map +1 -1
- package/dist/esm/base-classes/services/repositories/repositories-registry-handler.backend.service.d.ts +71 -1
- package/dist/esm/base-classes/services/repositories/repositories-registry-handler.backend.service.d.ts.map +1 -1
- package/dist/esm/base-classes/services/repositories/repositories-registry-handler.backend.service.js +62 -2
- package/dist/esm/base-classes/services/repositories/repositories-registry-handler.backend.service.js.map +1 -1
- package/dist/esm/base-classes/services/repositories/repository-resource.backend.service.d.ts +21 -0
- package/dist/esm/base-classes/services/repositories/repository-resource.backend.service.d.ts.map +1 -1
- package/dist/esm/base-classes/services/repositories/repository-resource.backend.service.js +126 -4
- package/dist/esm/base-classes/services/repositories/repository-resource.backend.service.js.map +1 -1
- package/dist/esm/base-classes/services/services/resource-operation-implementation-coverage.backend.utils.d.ts +3 -26
- package/dist/esm/base-classes/services/services/resource-operation-implementation-coverage.backend.utils.d.ts.map +1 -1
- package/dist/esm/base-classes/services/services/resource-operation-implementation-coverage.backend.utils.js +3 -68
- package/dist/esm/base-classes/services/services/resource-operation-implementation-coverage.backend.utils.js.map +1 -1
- package/dist/esm/base-classes/services/services/services-registry-handler.backend.service.d.ts.map +1 -1
- package/dist/esm/base-classes/services/services/services-registry-handler.backend.service.js +4 -2
- package/dist/esm/base-classes/services/services/services-registry-handler.backend.service.js.map +1 -1
- package/dist/esm/billing/services/custom-impl/billing-account.mutations.custom-impl.backend.service.js +6 -6
- package/dist/esm/billing/services/custom-impl/billing-account.mutations.custom-impl.backend.service.js.map +1 -1
- package/dist/esm/compliance/audit-trails/services/audit-event-dispatcher.backend.service.d.ts +38 -0
- package/dist/esm/compliance/audit-trails/services/audit-event-dispatcher.backend.service.d.ts.map +1 -1
- package/dist/esm/compliance/audit-trails/services/audit-event-dispatcher.backend.service.js +40 -0
- package/dist/esm/compliance/audit-trails/services/audit-event-dispatcher.backend.service.js.map +1 -1
- package/dist/esm/compliance/audit-trails/services/audit-logs.backend.service.d.ts +20 -0
- package/dist/esm/compliance/audit-trails/services/audit-logs.backend.service.d.ts.map +1 -1
- package/dist/esm/compliance/audit-trails/services/audit-logs.backend.service.js +28 -0
- package/dist/esm/compliance/audit-trails/services/audit-logs.backend.service.js.map +1 -1
- package/dist/esm/compliance/audit-trails/services/siem-export-config-cache-invalidation.custom-impl.backend.service.d.ts +12 -0
- package/dist/esm/compliance/audit-trails/services/siem-export-config-cache-invalidation.custom-impl.backend.service.d.ts.map +1 -0
- package/dist/esm/compliance/audit-trails/services/siem-export-config-cache-invalidation.custom-impl.backend.service.js +118 -0
- package/dist/esm/compliance/audit-trails/services/siem-export-config-cache-invalidation.custom-impl.backend.service.js.map +1 -0
- package/dist/esm/compliance/audit-trails/services/siem-webhook-delivery.backend.service.d.ts.map +1 -1
- package/dist/esm/compliance/audit-trails/services/siem-webhook-delivery.backend.service.js +8 -5
- package/dist/esm/compliance/audit-trails/services/siem-webhook-delivery.backend.service.js.map +1 -1
- package/dist/esm/compliance/audit-trails/services/utils/ocsf-activity-mapping.backend.utils.d.ts.map +1 -1
- package/dist/esm/compliance/audit-trails/services/utils/ocsf-activity-mapping.backend.utils.js +14 -0
- package/dist/esm/compliance/audit-trails/services/utils/ocsf-activity-mapping.backend.utils.js.map +1 -1
- package/dist/esm/config/custom/application-config-controller.backend.service.d.ts.map +1 -1
- package/dist/esm/config/custom/application-config-controller.backend.service.js +7 -1
- package/dist/esm/config/custom/application-config-controller.backend.service.js.map +1 -1
- package/dist/esm/config/models/app-config.backend.schemas.d.ts +4 -0
- package/dist/esm/config/models/app-config.backend.schemas.d.ts.map +1 -1
- package/dist/esm/config/services/app-configuration.backend.utils.d.ts +10 -1
- package/dist/esm/config/services/app-configuration.backend.utils.d.ts.map +1 -1
- package/dist/esm/config/services/app-configuration.backend.utils.js +13 -2
- package/dist/esm/config/services/app-configuration.backend.utils.js.map +1 -1
- package/dist/esm/data-seeding/services/shared/data-seed-scope-iteration.shared.utils.d.ts +2 -2
- package/dist/esm/data-seeding/services/shared/data-seed-scope-iteration.shared.utils.js +2 -2
- package/dist/esm/data-seeding/services/shared/data-seed-scope-iteration.shared.utils.js.map +1 -1
- package/dist/esm/databases/models/filter.backend.schemas.js +2 -2
- package/dist/esm/databases/models/filter.backend.schemas.js.map +1 -1
- package/dist/esm/databases/services/aggregation/aggregation-authorization.backend.service.d.ts.map +1 -1
- package/dist/esm/databases/services/aggregation/aggregation-authorization.backend.service.js +20 -1
- package/dist/esm/databases/services/aggregation/aggregation-authorization.backend.service.js.map +1 -1
- package/dist/esm/databases/services/redis.service.d.ts +27 -0
- package/dist/esm/databases/services/redis.service.d.ts.map +1 -1
- package/dist/esm/databases/services/redis.service.js +29 -0
- package/dist/esm/databases/services/redis.service.js.map +1 -1
- package/dist/esm/errors/services/error-handler.backend.service.d.ts.map +1 -1
- package/dist/esm/errors/services/error-handler.backend.service.js +5 -0
- package/dist/esm/errors/services/error-handler.backend.service.js.map +1 -1
- package/dist/esm/external-data/controllers/external-data-admin.backend.contracts.d.ts +41 -0
- package/dist/esm/external-data/controllers/external-data-admin.backend.contracts.d.ts.map +1 -0
- package/dist/esm/external-data/controllers/external-data-admin.backend.contracts.js +40 -0
- package/dist/esm/external-data/controllers/external-data-admin.backend.contracts.js.map +1 -0
- package/dist/esm/external-data/controllers/external-data-admin.backend.controller.d.ts +64 -0
- package/dist/esm/external-data/controllers/external-data-admin.backend.controller.d.ts.map +1 -0
- package/dist/esm/external-data/controllers/external-data-admin.backend.controller.js +193 -0
- package/dist/esm/external-data/controllers/external-data-admin.backend.controller.js.map +1 -0
- package/dist/esm/external-data/external-data-pipeline-run.backend.service.d.ts +13 -2
- package/dist/esm/external-data/external-data-pipeline-run.backend.service.d.ts.map +1 -1
- package/dist/esm/external-data/external-data-pipeline-run.backend.service.js +8 -2
- package/dist/esm/external-data/external-data-pipeline-run.backend.service.js.map +1 -1
- package/dist/esm/external-data/external-data-transform.backend.d.ts.map +1 -1
- package/dist/esm/external-data/external-data-transform.backend.js +7 -1
- package/dist/esm/external-data/external-data-transform.backend.js.map +1 -1
- package/dist/esm/external-data/models/external-data-pipeline-state.backend.schemas.d.ts +2 -0
- package/dist/esm/external-data/models/external-data-pipeline-state.backend.schemas.d.ts.map +1 -1
- package/dist/esm/external-data/models/external-data-pipeline-state.backend.schemas.js +13 -0
- package/dist/esm/external-data/models/external-data-pipeline-state.backend.schemas.js.map +1 -1
- package/dist/esm/external-providers/services/external-providers-registry.backend.service.d.ts +14 -0
- package/dist/esm/external-providers/services/external-providers-registry.backend.service.d.ts.map +1 -1
- package/dist/esm/external-providers/services/external-providers-registry.backend.service.js +6 -0
- package/dist/esm/external-providers/services/external-providers-registry.backend.service.js.map +1 -1
- package/dist/esm/files/controllers/file.controller.d.ts +24 -1
- package/dist/esm/files/controllers/file.controller.d.ts.map +1 -1
- package/dist/esm/files/controllers/file.controller.js +66 -2
- package/dist/esm/files/controllers/file.controller.js.map +1 -1
- package/dist/esm/files/handlers/file-upload-route-locals.backend.types.d.ts +17 -0
- package/dist/esm/files/handlers/file-upload-route-locals.backend.types.d.ts.map +1 -0
- package/dist/esm/files/handlers/file-upload-route-locals.backend.types.js +1 -0
- package/dist/esm/files/handlers/file-upload-route-locals.backend.types.js.map +1 -0
- package/dist/esm/files/handlers/resource-file-upload-handler.backend.service.d.ts +5 -1
- package/dist/esm/files/handlers/resource-file-upload-handler.backend.service.d.ts.map +1 -1
- package/dist/esm/files/handlers/resource-file-upload-handler.backend.service.js +42 -3
- package/dist/esm/files/handlers/resource-file-upload-handler.backend.service.js.map +1 -1
- package/dist/esm/files/services/file-operation-handler.backend.service.d.ts +18 -0
- package/dist/esm/files/services/file-operation-handler.backend.service.d.ts.map +1 -1
- package/dist/esm/files/services/file-operation-handler.backend.service.js +36 -28
- package/dist/esm/files/services/file-operation-handler.backend.service.js.map +1 -1
- package/dist/esm/files/services/file-upload-grant.backend.service.d.ts +163 -0
- package/dist/esm/files/services/file-upload-grant.backend.service.d.ts.map +1 -0
- package/dist/esm/files/services/file-upload-grant.backend.service.js +442 -0
- package/dist/esm/files/services/file-upload-grant.backend.service.js.map +1 -0
- package/dist/esm/files/services/files.backend.service.d.ts +19 -0
- package/dist/esm/files/services/files.backend.service.d.ts.map +1 -1
- package/dist/esm/files/services/files.backend.service.js +78 -15
- package/dist/esm/files/services/files.backend.service.js.map +1 -1
- package/dist/esm/files/utils/file-uploader.utils.d.ts.map +1 -1
- package/dist/esm/files/utils/file-uploader.utils.js +12 -0
- package/dist/esm/files/utils/file-uploader.utils.js.map +1 -1
- package/dist/esm/flows-actors/a2a/services/a2a-task-cancellation.backend.service.js +5 -5
- package/dist/esm/flows-actors/a2a/services/a2a-task-cancellation.backend.service.js.map +1 -1
- package/dist/esm/flows-actors/services/agents.backend.service.d.ts +79 -0
- package/dist/esm/flows-actors/services/agents.backend.service.d.ts.map +1 -1
- package/dist/esm/flows-actors/services/agents.backend.service.js +319 -42
- package/dist/esm/flows-actors/services/agents.backend.service.js.map +1 -1
- package/dist/esm/flows-actors/utils/acp-session-runner.backend.utils.d.ts +151 -41
- package/dist/esm/flows-actors/utils/acp-session-runner.backend.utils.d.ts.map +1 -1
- package/dist/esm/flows-actors/utils/acp-session-runner.backend.utils.js +300 -139
- package/dist/esm/flows-actors/utils/acp-session-runner.backend.utils.js.map +1 -1
- package/dist/esm/index.d.ts +4 -1
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +14 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/inject/models/container.d.ts.map +1 -1
- package/dist/esm/inject/models/container.js +4 -0
- package/dist/esm/inject/models/container.js.map +1 -1
- package/dist/esm/inject/models/service-types.d.ts +3 -0
- package/dist/esm/inject/models/service-types.d.ts.map +1 -1
- package/dist/esm/inject/models/service-types.js +2 -0
- package/dist/esm/inject/models/service-types.js.map +1 -1
- package/dist/esm/mcp/controllers/mcp.controller.d.ts.map +1 -1
- package/dist/esm/mcp/controllers/mcp.controller.js +12 -4
- package/dist/esm/mcp/controllers/mcp.controller.js.map +1 -1
- package/dist/esm/mcp/models/mcp-tool.backend.schemas.d.ts +34 -0
- package/dist/esm/mcp/models/mcp-tool.backend.schemas.d.ts.map +1 -1
- package/dist/esm/mcp/models/mcp-tool.backend.schemas.js +42 -0
- package/dist/esm/mcp/models/mcp-tool.backend.schemas.js.map +1 -1
- package/dist/esm/mcp/services/mcp-server.backend.service.d.ts +29 -1
- package/dist/esm/mcp/services/mcp-server.backend.service.d.ts.map +1 -1
- package/dist/esm/mcp/services/mcp-server.backend.service.js +131 -3
- package/dist/esm/mcp/services/mcp-server.backend.service.js.map +1 -1
- package/dist/esm/mcp/services/mcp-tool-adapter.backend.service.d.ts +64 -2
- package/dist/esm/mcp/services/mcp-tool-adapter.backend.service.d.ts.map +1 -1
- package/dist/esm/mcp/services/mcp-tool-adapter.backend.service.js +130 -6
- package/dist/esm/mcp/services/mcp-tool-adapter.backend.service.js.map +1 -1
- package/dist/esm/notifications/services/notifications-dispatcher.backend.service.d.ts +9 -0
- package/dist/esm/notifications/services/notifications-dispatcher.backend.service.d.ts.map +1 -1
- package/dist/esm/notifications/services/notifications-dispatcher.backend.service.js +11 -36
- package/dist/esm/notifications/services/notifications-dispatcher.backend.service.js.map +1 -1
- package/dist/esm/notifications/webhooks/outbound/batches/m2m-webhook-delivery.batch.backend.service.d.ts.map +1 -1
- package/dist/esm/notifications/webhooks/outbound/batches/m2m-webhook-delivery.batch.backend.service.js +9 -7
- package/dist/esm/notifications/webhooks/outbound/batches/m2m-webhook-delivery.batch.backend.service.js.map +1 -1
- package/dist/esm/notifications/webhooks/outbound/batches/m2m-webhook-delivery.batch.utils.d.ts +5 -5
- package/dist/esm/notifications/webhooks/outbound/batches/m2m-webhook-delivery.batch.utils.d.ts.map +1 -1
- package/dist/esm/notifications/webhooks/outbound/batches/m2m-webhook-delivery.batch.utils.js +14 -13
- package/dist/esm/notifications/webhooks/outbound/batches/m2m-webhook-delivery.batch.utils.js.map +1 -1
- package/dist/esm/notifications/webhooks/outbound/m2m-webhook-signed-request.backend.utils.d.ts +2 -1
- package/dist/esm/notifications/webhooks/outbound/m2m-webhook-signed-request.backend.utils.d.ts.map +1 -1
- package/dist/esm/notifications/webhooks/outbound/m2m-webhook-signed-request.backend.utils.js +2 -2
- package/dist/esm/notifications/webhooks/outbound/m2m-webhook-signed-request.backend.utils.js.map +1 -1
- package/dist/esm/notifications/webhooks/outbound/services/webhook-config-test-endpoint.custom-impl.backend.service.d.ts.map +1 -1
- package/dist/esm/notifications/webhooks/outbound/services/webhook-config-test-endpoint.custom-impl.backend.service.js +9 -7
- package/dist/esm/notifications/webhooks/outbound/services/webhook-config-test-endpoint.custom-impl.backend.service.js.map +1 -1
- package/dist/esm/organizations/services/organization-unit-custom-implementation.backend.service.d.ts.map +1 -1
- package/dist/esm/organizations/services/organization-unit-custom-implementation.backend.service.js +4 -1
- package/dist/esm/organizations/services/organization-unit-custom-implementation.backend.service.js.map +1 -1
- package/dist/esm/requests/service/websocket.service.d.ts.map +1 -1
- package/dist/esm/requests/service/websocket.service.js +56 -7
- package/dist/esm/requests/service/websocket.service.js.map +1 -1
- package/dist/esm/resources/models/custom-service-implementations.backend.definition.d.ts.map +1 -1
- package/dist/esm/resources/models/custom-service-implementations.backend.definition.js +2 -0
- package/dist/esm/resources/models/custom-service-implementations.backend.definition.js.map +1 -1
- package/dist/esm/resources/services/resource-id.backend.service.js +3 -3
- package/dist/esm/resources/services/resource-id.backend.service.js.map +1 -1
- package/dist/esm/resources/services/resources-registry.backend.service.js +1 -1
- package/dist/esm/resources/services/resources-registry.backend.service.js.map +1 -1
- package/dist/esm/runtime/services/execution-context-creator.backend.service.d.ts.map +1 -1
- package/dist/esm/runtime/services/execution-context-creator.backend.service.js +13 -7
- package/dist/esm/runtime/services/execution-context-creator.backend.service.js.map +1 -1
- package/dist/esm/runtime/services/start-application.backend.service.d.ts +28 -0
- package/dist/esm/runtime/services/start-application.backend.service.d.ts.map +1 -1
- package/dist/esm/runtime/services/start-application.backend.service.js +82 -0
- package/dist/esm/runtime/services/start-application.backend.service.js.map +1 -1
- package/dist/esm/security/authentications/models/jwt.backend.schemas.d.ts +3 -23
- package/dist/esm/security/authentications/models/jwt.backend.schemas.d.ts.map +1 -1
- package/dist/esm/security/authentications/models/jwt.backend.schemas.js +20 -45
- package/dist/esm/security/authentications/models/jwt.backend.schemas.js.map +1 -1
- package/dist/esm/security/authentications/services/auth-methods/auth-method-management.backend.service.d.ts +9 -1
- package/dist/esm/security/authentications/services/auth-methods/auth-method-management.backend.service.d.ts.map +1 -1
- package/dist/esm/security/authentications/services/auth-methods/auth-method-management.backend.service.js +9 -1
- package/dist/esm/security/authentications/services/auth-methods/auth-method-management.backend.service.js.map +1 -1
- package/dist/esm/security/authentications/services/auth-methods/auth-mfa.backend.service.d.ts.map +1 -1
- package/dist/esm/security/authentications/services/auth-methods/auth-mfa.backend.service.js +5 -3
- package/dist/esm/security/authentications/services/auth-methods/auth-mfa.backend.service.js.map +1 -1
- package/dist/esm/security/authentications/services/auth-token-issuer.backend.service.d.ts +25 -1
- package/dist/esm/security/authentications/services/auth-token-issuer.backend.service.d.ts.map +1 -1
- package/dist/esm/security/authentications/services/auth-token-issuer.backend.service.js +55 -4
- package/dist/esm/security/authentications/services/auth-token-issuer.backend.service.js.map +1 -1
- package/dist/esm/security/authorizations/initiator-organization-confinement.backend.d.ts.map +1 -1
- package/dist/esm/security/authorizations/initiator-organization-confinement.backend.js +24 -1
- package/dist/esm/security/authorizations/initiator-organization-confinement.backend.js.map +1 -1
- package/dist/esm/security/authorizations/privileged-access-review.backend.utils.d.ts +117 -0
- package/dist/esm/security/authorizations/privileged-access-review.backend.utils.d.ts.map +1 -0
- package/dist/esm/security/authorizations/privileged-access-review.backend.utils.js +104 -0
- package/dist/esm/security/authorizations/privileged-access-review.backend.utils.js.map +1 -0
- package/dist/esm/security/authorizations/services/authorizations.backend.service.d.ts +101 -6
- package/dist/esm/security/authorizations/services/authorizations.backend.service.d.ts.map +1 -1
- package/dist/esm/security/authorizations/services/authorizations.backend.service.js +210 -30
- package/dist/esm/security/authorizations/services/authorizations.backend.service.js.map +1 -1
- package/dist/esm/security/consumable-tokens/batches/consumable-tokens-cleanup.batch.backend.service.d.ts +18 -1
- package/dist/esm/security/consumable-tokens/batches/consumable-tokens-cleanup.batch.backend.service.d.ts.map +1 -1
- package/dist/esm/security/consumable-tokens/batches/consumable-tokens-cleanup.batch.backend.service.js +110 -74
- package/dist/esm/security/consumable-tokens/batches/consumable-tokens-cleanup.batch.backend.service.js.map +1 -1
- package/dist/esm/security/consumable-tokens/consumable-token-models.exports.d.ts +22 -0
- package/dist/esm/security/consumable-tokens/consumable-token-models.exports.d.ts.map +1 -0
- package/dist/esm/security/consumable-tokens/consumable-token-models.exports.js +22 -0
- package/dist/esm/security/consumable-tokens/consumable-token-models.exports.js.map +1 -0
- package/dist/esm/security/consumable-tokens/models/consumable-tokens.backend.resources-config.schemas.d.ts +17 -3
- package/dist/esm/security/consumable-tokens/models/consumable-tokens.backend.resources-config.schemas.d.ts.map +1 -1
- package/dist/esm/security/consumable-tokens/models/consumable-tokens.backend.resources-config.schemas.js +12 -0
- package/dist/esm/security/consumable-tokens/models/consumable-tokens.backend.resources-config.schemas.js.map +1 -1
- package/dist/esm/security/consumable-tokens/services/consumable-token.backend.service.d.ts +20 -0
- package/dist/esm/security/consumable-tokens/services/consumable-token.backend.service.d.ts.map +1 -1
- package/dist/esm/security/consumable-tokens/services/consumable-token.backend.service.js +58 -5
- package/dist/esm/security/consumable-tokens/services/consumable-token.backend.service.js.map +1 -1
- package/dist/esm/security/jwt/services/jwt.backend.service.d.ts.map +1 -1
- package/dist/esm/security/jwt/services/jwt.backend.service.js +1 -18
- package/dist/esm/security/jwt/services/jwt.backend.service.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +15 -9
- package/dist/esm/.builder.pid +0 -9
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file-uploader.utils.d.ts","sourceRoot":"","sources":["../../../../../src/files/utils/file-uploader.utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,sBAAsB,EACvB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qEAAqE,CAAC;AA8B5G,MAAM,WAAW,qBAAqB;IACpC,UAAU,EAAE,MAAM,CAAC;IACnB,oBAAoB,EAAE,sBAAsB,CAAC;CAC9C;AAED;;;;;;GAMG;AACH,wBAAgB,4BAA4B,CAC1C,gBAAgB,EAAE,gBAAgB,CAAC,GAAG,CAAC,GACtC,qBAAqB,GAAG,SAAS,
|
|
1
|
+
{"version":3,"file":"file-uploader.utils.d.ts","sourceRoot":"","sources":["../../../../../src/files/utils/file-uploader.utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,sBAAsB,EACvB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qEAAqE,CAAC;AA8B5G,MAAM,WAAW,qBAAqB;IACpC,UAAU,EAAE,MAAM,CAAC;IACnB,oBAAoB,EAAE,sBAAsB,CAAC;CAC9C;AAED;;;;;;GAMG;AACH,wBAAgB,4BAA4B,CAC1C,gBAAgB,EAAE,gBAAgB,CAAC,GAAG,CAAC,GACtC,qBAAqB,GAAG,SAAS,CA8DnC"}
|
|
@@ -46,6 +46,18 @@ export function resolveFileUploaderPrincipal(executionContext) {
|
|
|
46
46
|
uploadedByEntityType: FileUploaderEntityType.APPLICATION_MACHINE,
|
|
47
47
|
}
|
|
48
48
|
: undefined;
|
|
49
|
+
// A consumable-token context carries the userId the token was MINTED for (set by the token bridge
|
|
50
|
+
// in `createExecutionContextFromRequest`). For a user-minted file upload grant that IS the uploader —
|
|
51
|
+
// the person who delegated the upload, not the bearer who performed it. A token with no `userId`
|
|
52
|
+
// (a machine-minted grant) resolves to nothing here; the upload handler then attributes the file
|
|
53
|
+
// from the grant's own `minter` record. Issue #312, D7.
|
|
54
|
+
case ExecutionContext_ExecutionType.CONSUMABLE_TOKEN:
|
|
55
|
+
return initiatorIds.userId
|
|
56
|
+
? {
|
|
57
|
+
uploadedBy: initiatorIds.userId,
|
|
58
|
+
uploadedByEntityType: FileUploaderEntityType.USER,
|
|
59
|
+
}
|
|
60
|
+
: undefined;
|
|
49
61
|
default:
|
|
50
62
|
return undefined;
|
|
51
63
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file-uploader.utils.js","sourceRoot":"","sources":["../../../../../src/files/utils/file-uploader.utils.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,8BAA8B,EAC9B,sBAAsB,GACvB,MAAM,uBAAuB,CAAC;AAqC/B;;;;;;GAMG;AACH,MAAM,UAAU,4BAA4B,CAC1C,gBAAuC;IAEvC,MAAM,YAAY,GAAG,gBAAgB,CAAC,YAAY,CAAC;IAEnD,IAAI,CAAC,YAAY,EAAE,CAAC;QAClB,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,QAAQ,gBAAgB,CAAC,aAAa,EAAE,CAAC;QACvC,KAAK,8BAA8B,CAAC,YAAY;YAC9C,OAAO,YAAY,CAAC,MAAM;gBACxB,CAAC,CAAC;oBACE,UAAU,EAAE,YAAY,CAAC,MAAM;oBAC/B,oBAAoB,EAAE,sBAAsB,CAAC,IAAI;iBAClD;gBACH,CAAC,CAAC,SAAS,CAAC;QAEhB,KAAK,8BAA8B,CAAC,iBAAiB;YACnD,OAAO,YAAY,CAAC,eAAe;gBACjC,CAAC,CAAC;oBACE,UAAU,EAAE,YAAY,CAAC,eAAe;oBACxC,oBAAoB,EAAE,sBAAsB,CAAC,cAAc;iBAC5D;gBACH,CAAC,CAAC,SAAS,CAAC;QAEhB,sFAAsF;QACtF,+FAA+F;QAC/F,+FAA+F;QAC/F,8FAA8F;QAC9F,gGAAgG;QAChG,+EAA+E;QAC/E,KAAK,8BAA8B,CAAC,oBAAoB;YACtD,OAAO,YAAY,CAAC,iBAAiB;gBACnC,CAAC,CAAC;oBACE,UAAU,EAAE,YAAY,CAAC,iBAAiB,CAAC,YAAY;oBACvD,oBAAoB,EAAE,sBAAsB,CAAC,oBAAoB;iBAClE;gBACH,CAAC,CAAC,SAAS,CAAC;QAEhB,KAAK,8BAA8B,CAAC,mBAAmB;YACrD,OAAO,YAAY,CAAC,iBAAiB;gBACnC,CAAC,CAAC;oBACE,UAAU,EAAE,YAAY,CAAC,iBAAiB,CAAC,YAAY;oBACvD,oBAAoB,EAAE,sBAAsB,CAAC,mBAAmB;iBACjE;gBACH,CAAC,CAAC,SAAS,CAAC;QAEhB;YACE,OAAO,SAAS,CAAC;IACrB,CAAC;AACH,CAAC","sourcesContent":["import {\n ExecutionContext_ExecutionType,\n FileUploaderEntityType,\n} from '@wildo-ai/saas-models';\n\nimport type { ExecutionContext } from '~wildo-backend-lib/runtime/models/execution-context.backend.schemas';\n\n/**\n * SINGLE SOURCE OF TRUTH — the machine file-uploader entity types are DERIVED-BY-CONTRACT from the\n * machine EXECUTION types, which are the authority.\n *\n * `FileUploaderEntityType.{ORGANIZATION,APPLICATION}_MACHINE` and\n * `ExecutionContext_ExecutionType.{ORGANIZATION,APPLICATION}_MACHINE` name the SAME machine\n * principal, so their string values MUST stay identical — the switch below projects the execution\n * type straight onto the entity type. They cannot physically share the literal: `FileUploaderEntityType`\n * lives in the upstream `zod-decorators` package (it cannot import the execution type), and TS\n * string-enum members must be literals (not computed from another enum). So instead these compile-time\n * assertions PIN the two equal. A future rename of a machine execution-type VALUE — exactly the\n * `org_api_key`→`organization_machine` recentering that silently left this uploader stranded on\n * `*_api_key` — then FAILS TO COMPILE here until the file-uploader enum is updated in lockstep.\n *\n * Only the MACHINE values are pinned. The human values legitimately DIFFER (`USER`=`user` vs\n * `USER_REQUEST`=`user_request`; `ANONYMOUS_USER`=`anonymous_user` vs `ANONYMOUS_SESSION`=\n * `anonymous_session`) — an entity/principal KIND is not the same concept as an execution KIND — so\n * pinning them would be wrong.\n */\ntype _StringEqual<A extends string, B extends string> = [A] extends [B] ? ([B] extends [A] ? true : false) : false;\ntype _Expect<T extends true> = T;\ntype _AssertOrgMachineUploaderTracksExecutionType = _Expect<\n _StringEqual<`${FileUploaderEntityType.ORGANIZATION_MACHINE}`, `${ExecutionContext_ExecutionType.ORGANIZATION_MACHINE}`>\n>;\ntype _AssertAppMachineUploaderTracksExecutionType = _Expect<\n _StringEqual<`${FileUploaderEntityType.APPLICATION_MACHINE}`, `${ExecutionContext_ExecutionType.APPLICATION_MACHINE}`>\n>;\n\nexport interface FileUploaderPrincipal {\n uploadedBy: string;\n uploadedByEntityType: FileUploaderEntityType;\n}\n\n/**\n * Derive the concrete uploader principal from the execution context.\n *\n * File ownership is intentionally stored as a lightweight raw id + entity type\n * pair. We do not resolve related entities here; callers can hydrate richer\n * display data separately when needed.\n */\nexport function resolveFileUploaderPrincipal(\n executionContext: ExecutionContext<any>,\n): FileUploaderPrincipal | undefined {\n const initiatorIds = executionContext.initiatorIds;\n\n if (!initiatorIds) {\n return undefined;\n }\n\n switch (executionContext.executionType) {\n case ExecutionContext_ExecutionType.USER_REQUEST:\n return initiatorIds.userId\n ? {\n uploadedBy: initiatorIds.userId,\n uploadedByEntityType: FileUploaderEntityType.USER,\n }\n : undefined;\n\n case ExecutionContext_ExecutionType.ANONYMOUS_SESSION:\n return initiatorIds.anonymousUserId\n ? {\n uploadedBy: initiatorIds.anonymousUserId,\n uploadedByEntityType: FileUploaderEntityType.ANONYMOUS_USER,\n }\n : undefined;\n\n // Machine principals (API key OR OAuth client): the uploader id is the authenticating\n // credential (`machineCredential.credentialId`), and the entity type is the credential-NEUTRAL\n // machine principal (`ORGANIZATION_MACHINE` / `APPLICATION_MACHINE`) — an api-key and an OAuth\n // client both resolve to the same machine principal, so labelling the upload `*_MACHINE` (not\n // `*_API_KEY`) is honest for both. The specific credential KIND (api-key vs OAuth client) is on\n // `machineCredential.authMethod` if a consumer ever needs to distinguish them.\n case ExecutionContext_ExecutionType.ORGANIZATION_MACHINE:\n return initiatorIds.machineCredential\n ? {\n uploadedBy: initiatorIds.machineCredential.credentialId,\n uploadedByEntityType: FileUploaderEntityType.ORGANIZATION_MACHINE,\n }\n : undefined;\n\n case ExecutionContext_ExecutionType.APPLICATION_MACHINE:\n return initiatorIds.machineCredential\n ? {\n uploadedBy: initiatorIds.machineCredential.credentialId,\n uploadedByEntityType: FileUploaderEntityType.APPLICATION_MACHINE,\n }\n : undefined;\n\n default:\n return undefined;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"file-uploader.utils.js","sourceRoot":"","sources":["../../../../../src/files/utils/file-uploader.utils.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,8BAA8B,EAC9B,sBAAsB,GACvB,MAAM,uBAAuB,CAAC;AAqC/B;;;;;;GAMG;AACH,MAAM,UAAU,4BAA4B,CAC1C,gBAAuC;IAEvC,MAAM,YAAY,GAAG,gBAAgB,CAAC,YAAY,CAAC;IAEnD,IAAI,CAAC,YAAY,EAAE,CAAC;QAClB,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,QAAQ,gBAAgB,CAAC,aAAa,EAAE,CAAC;QACvC,KAAK,8BAA8B,CAAC,YAAY;YAC9C,OAAO,YAAY,CAAC,MAAM;gBACxB,CAAC,CAAC;oBACE,UAAU,EAAE,YAAY,CAAC,MAAM;oBAC/B,oBAAoB,EAAE,sBAAsB,CAAC,IAAI;iBAClD;gBACH,CAAC,CAAC,SAAS,CAAC;QAEhB,KAAK,8BAA8B,CAAC,iBAAiB;YACnD,OAAO,YAAY,CAAC,eAAe;gBACjC,CAAC,CAAC;oBACE,UAAU,EAAE,YAAY,CAAC,eAAe;oBACxC,oBAAoB,EAAE,sBAAsB,CAAC,cAAc;iBAC5D;gBACH,CAAC,CAAC,SAAS,CAAC;QAEhB,sFAAsF;QACtF,+FAA+F;QAC/F,+FAA+F;QAC/F,8FAA8F;QAC9F,gGAAgG;QAChG,+EAA+E;QAC/E,KAAK,8BAA8B,CAAC,oBAAoB;YACtD,OAAO,YAAY,CAAC,iBAAiB;gBACnC,CAAC,CAAC;oBACE,UAAU,EAAE,YAAY,CAAC,iBAAiB,CAAC,YAAY;oBACvD,oBAAoB,EAAE,sBAAsB,CAAC,oBAAoB;iBAClE;gBACH,CAAC,CAAC,SAAS,CAAC;QAEhB,KAAK,8BAA8B,CAAC,mBAAmB;YACrD,OAAO,YAAY,CAAC,iBAAiB;gBACnC,CAAC,CAAC;oBACE,UAAU,EAAE,YAAY,CAAC,iBAAiB,CAAC,YAAY;oBACvD,oBAAoB,EAAE,sBAAsB,CAAC,mBAAmB;iBACjE;gBACH,CAAC,CAAC,SAAS,CAAC;QAEhB,kGAAkG;QAClG,sGAAsG;QACtG,iGAAiG;QACjG,iGAAiG;QACjG,wDAAwD;QACxD,KAAK,8BAA8B,CAAC,gBAAgB;YAClD,OAAO,YAAY,CAAC,MAAM;gBACxB,CAAC,CAAC;oBACE,UAAU,EAAE,YAAY,CAAC,MAAM;oBAC/B,oBAAoB,EAAE,sBAAsB,CAAC,IAAI;iBAClD;gBACH,CAAC,CAAC,SAAS,CAAC;QAEhB;YACE,OAAO,SAAS,CAAC;IACrB,CAAC;AACH,CAAC","sourcesContent":["import {\n ExecutionContext_ExecutionType,\n FileUploaderEntityType,\n} from '@wildo-ai/saas-models';\n\nimport type { ExecutionContext } from '~wildo-backend-lib/runtime/models/execution-context.backend.schemas';\n\n/**\n * SINGLE SOURCE OF TRUTH — the machine file-uploader entity types are DERIVED-BY-CONTRACT from the\n * machine EXECUTION types, which are the authority.\n *\n * `FileUploaderEntityType.{ORGANIZATION,APPLICATION}_MACHINE` and\n * `ExecutionContext_ExecutionType.{ORGANIZATION,APPLICATION}_MACHINE` name the SAME machine\n * principal, so their string values MUST stay identical — the switch below projects the execution\n * type straight onto the entity type. They cannot physically share the literal: `FileUploaderEntityType`\n * lives in the upstream `zod-decorators` package (it cannot import the execution type), and TS\n * string-enum members must be literals (not computed from another enum). So instead these compile-time\n * assertions PIN the two equal. A future rename of a machine execution-type VALUE — exactly the\n * `org_api_key`→`organization_machine` recentering that silently left this uploader stranded on\n * `*_api_key` — then FAILS TO COMPILE here until the file-uploader enum is updated in lockstep.\n *\n * Only the MACHINE values are pinned. The human values legitimately DIFFER (`USER`=`user` vs\n * `USER_REQUEST`=`user_request`; `ANONYMOUS_USER`=`anonymous_user` vs `ANONYMOUS_SESSION`=\n * `anonymous_session`) — an entity/principal KIND is not the same concept as an execution KIND — so\n * pinning them would be wrong.\n */\ntype _StringEqual<A extends string, B extends string> = [A] extends [B] ? ([B] extends [A] ? true : false) : false;\ntype _Expect<T extends true> = T;\ntype _AssertOrgMachineUploaderTracksExecutionType = _Expect<\n _StringEqual<`${FileUploaderEntityType.ORGANIZATION_MACHINE}`, `${ExecutionContext_ExecutionType.ORGANIZATION_MACHINE}`>\n>;\ntype _AssertAppMachineUploaderTracksExecutionType = _Expect<\n _StringEqual<`${FileUploaderEntityType.APPLICATION_MACHINE}`, `${ExecutionContext_ExecutionType.APPLICATION_MACHINE}`>\n>;\n\nexport interface FileUploaderPrincipal {\n uploadedBy: string;\n uploadedByEntityType: FileUploaderEntityType;\n}\n\n/**\n * Derive the concrete uploader principal from the execution context.\n *\n * File ownership is intentionally stored as a lightweight raw id + entity type\n * pair. We do not resolve related entities here; callers can hydrate richer\n * display data separately when needed.\n */\nexport function resolveFileUploaderPrincipal(\n executionContext: ExecutionContext<any>,\n): FileUploaderPrincipal | undefined {\n const initiatorIds = executionContext.initiatorIds;\n\n if (!initiatorIds) {\n return undefined;\n }\n\n switch (executionContext.executionType) {\n case ExecutionContext_ExecutionType.USER_REQUEST:\n return initiatorIds.userId\n ? {\n uploadedBy: initiatorIds.userId,\n uploadedByEntityType: FileUploaderEntityType.USER,\n }\n : undefined;\n\n case ExecutionContext_ExecutionType.ANONYMOUS_SESSION:\n return initiatorIds.anonymousUserId\n ? {\n uploadedBy: initiatorIds.anonymousUserId,\n uploadedByEntityType: FileUploaderEntityType.ANONYMOUS_USER,\n }\n : undefined;\n\n // Machine principals (API key OR OAuth client): the uploader id is the authenticating\n // credential (`machineCredential.credentialId`), and the entity type is the credential-NEUTRAL\n // machine principal (`ORGANIZATION_MACHINE` / `APPLICATION_MACHINE`) — an api-key and an OAuth\n // client both resolve to the same machine principal, so labelling the upload `*_MACHINE` (not\n // `*_API_KEY`) is honest for both. The specific credential KIND (api-key vs OAuth client) is on\n // `machineCredential.authMethod` if a consumer ever needs to distinguish them.\n case ExecutionContext_ExecutionType.ORGANIZATION_MACHINE:\n return initiatorIds.machineCredential\n ? {\n uploadedBy: initiatorIds.machineCredential.credentialId,\n uploadedByEntityType: FileUploaderEntityType.ORGANIZATION_MACHINE,\n }\n : undefined;\n\n case ExecutionContext_ExecutionType.APPLICATION_MACHINE:\n return initiatorIds.machineCredential\n ? {\n uploadedBy: initiatorIds.machineCredential.credentialId,\n uploadedByEntityType: FileUploaderEntityType.APPLICATION_MACHINE,\n }\n : undefined;\n\n // A consumable-token context carries the userId the token was MINTED for (set by the token bridge\n // in `createExecutionContextFromRequest`). For a user-minted file upload grant that IS the uploader —\n // the person who delegated the upload, not the bearer who performed it. A token with no `userId`\n // (a machine-minted grant) resolves to nothing here; the upload handler then attributes the file\n // from the grant's own `minter` record. Issue #312, D7.\n case ExecutionContext_ExecutionType.CONSUMABLE_TOKEN:\n return initiatorIds.userId\n ? {\n uploadedBy: initiatorIds.userId,\n uploadedByEntityType: FileUploaderEntityType.USER,\n }\n : undefined;\n\n default:\n return undefined;\n }\n}\n"]}
|
|
@@ -85,7 +85,7 @@ let A2ATaskCancellationBackendService = class A2ATaskCancellationBackendService
|
|
|
85
85
|
if (this.subscribeStarted)
|
|
86
86
|
return;
|
|
87
87
|
this.subscribeStarted = true;
|
|
88
|
-
const client = this.redisService.
|
|
88
|
+
const client = this.redisService.getReadyClient();
|
|
89
89
|
if (!client) {
|
|
90
90
|
this.logger.error('[A2ATaskCancel] Redis unavailable — cross-pod cancellation is DISABLED for this pod');
|
|
91
91
|
return;
|
|
@@ -150,7 +150,7 @@ let A2ATaskCancellationBackendService = class A2ATaskCancellationBackendService
|
|
|
150
150
|
// a non-terminal task with no lease and is told FAILED by derive-on-read.
|
|
151
151
|
const controller = new AbortController();
|
|
152
152
|
this.localTurns.set(taskId, controller);
|
|
153
|
-
const client = this.redisService.
|
|
153
|
+
const client = this.redisService.getReadyClient();
|
|
154
154
|
if (client) {
|
|
155
155
|
let leaseEverWritten = false;
|
|
156
156
|
const refresh = async () => {
|
|
@@ -235,7 +235,7 @@ let A2ATaskCancellationBackendService = class A2ATaskCancellationBackendService
|
|
|
235
235
|
const local = this.localTurns.get(taskId);
|
|
236
236
|
if (local)
|
|
237
237
|
local.abort();
|
|
238
|
-
const client = this.redisService.
|
|
238
|
+
const client = this.redisService.getReadyClient();
|
|
239
239
|
if (!client)
|
|
240
240
|
return Boolean(local);
|
|
241
241
|
try {
|
|
@@ -265,7 +265,7 @@ let A2ATaskCancellationBackendService = class A2ATaskCancellationBackendService
|
|
|
265
265
|
// is no client rather than throwing, so the catch below never fired for that case and the
|
|
266
266
|
// documented "fails safe toward alive" was untrue: with Redis down, every task on another
|
|
267
267
|
// pod would have been reported FAILED. Check the client explicitly.
|
|
268
|
-
if (!this.redisService.
|
|
268
|
+
if (!this.redisService.getReadyClient())
|
|
269
269
|
return true;
|
|
270
270
|
try {
|
|
271
271
|
return await this.redisService.exists(this.leaseKey(taskId));
|
|
@@ -292,7 +292,7 @@ let A2ATaskCancellationBackendService = class A2ATaskCancellationBackendService
|
|
|
292
292
|
* that catches it.
|
|
293
293
|
*/
|
|
294
294
|
async isLeaseMechanismWritable() {
|
|
295
|
-
if (!this.redisService.
|
|
295
|
+
if (!this.redisService.getReadyClient())
|
|
296
296
|
return false;
|
|
297
297
|
const probeKey = this.leaseKey('__sweep-health-probe__');
|
|
298
298
|
try {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"a2a-task-cancellation.backend.service.js","sourceRoot":"","sources":["../../../../../../src/flows-actors/a2a/services/a2a-task-cancellation.backend.service.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Cross-pod A2A task cancellation.\n *\n * ## You do not need to find the executing pod\n *\n * With several backend replicas behind a load balancer, a `tasks/cancel` request\n * almost never lands on the pod that is running that task's LLM turn. The obvious\n * design — discover which pod owns it and route there — needs a pod registry, a\n * liveness view of it, and an internal call path, and it is wrong on every one of\n * those axes when a pod restarts mid-turn.\n *\n * Instead this BROADCASTS. Any pod publishes `{taskId}` to one Redis channel; every\n * pod runs a single shared subscriber; only the pod holding that `taskId` in its\n * in-process registry reacts. One connection per pod, no routing table, and a pod\n * that died simply does not answer — which is the correct behaviour, not an error to\n * handle.\n *\n * Rejected alternative: polling a Redis flag inside the token loop. That puts a GET on\n * the hottest path in the system (per N tokens, per concurrent turn) and bounds\n * cancellation latency by token cadence. Pub/sub costs nothing per token.\n *\n * ## Liveness is the same mechanism's other half\n *\n * While a pod owns a turn it refreshes a TTL'd lease key. That is what lets\n * `tasks/get` report an abandoned task honestly instead of leaving it `working`\n * forever after a pod dies — see the lease helpers below.\n */\n\nimport { inject, injectable } from 'inversify';\nimport type { RedisClientType } from 'redis';\n\nimport { SAAS_SERVICE_TYPES } from '~wildo-backend-lib/inject/models/service-types';\nimport type { LoggerService } from '~wildo-backend-lib/monitoring/logger/services/logger.service';\nimport type { RedisService } from '~wildo-backend-lib/databases/services/redis.service';\nimport { projectCaughtErrorLogFields } from '~wildo-backend-lib/errors/utils/error-log-level-classification.backend.utils';\n\n/** Channel every replica subscribes to. App-prefixed so tenants of one Redis never cross. */\nconst CANCEL_CHANNEL_SUFFIX = 'a2a:task:cancel';\n/** Lease key prefix — presence means \"a live pod is working this task right now\". */\nconst LEASE_KEY_PREFIX = 'a2a:task:lease:';\n\n/**\n * Lease TTL and refresh cadence. The refresh interval is deliberately well under the\n * TTL: a single missed refresh (GC pause, a slow Redis round-trip) must not make a\n * healthy turn look abandoned. Only a genuinely dead pod stops refreshing.\n */\nexport const A2A_TASK_LEASE_TTL_SECONDS = 30;\nexport const A2A_TASK_LEASE_REFRESH_MS = 10_000;\n\n@injectable()\nexport class A2ATaskCancellationBackendService {\n /** Tasks this pod is currently executing → the controller that aborts them. */\n private readonly localTurns = new Map<string, AbortController>();\n /** Lease heartbeats keyed by task id, cleared on release. */\n private readonly leaseTimers = new Map<string, NodeJS.Timeout>();\n\n private subscriber: RedisClientType | null = null;\n private subscribeStarted = false;\n\n constructor(\n @inject(SAAS_SERVICE_TYPES.Logger) private readonly logger: LoggerService,\n @inject(SAAS_SERVICE_TYPES.RedisService) private readonly redisService: RedisService,\n ) {}\n\n private channel(): string {\n return `${this.redisService.getAppPrefix()}${CANCEL_CHANNEL_SUFFIX}`;\n }\n\n private leaseKey(taskId: string): string {\n return `${LEASE_KEY_PREFIX}${taskId}`;\n }\n\n /**\n * Start this pod's single shared subscriber. Idempotent — safe to call from every\n * turn, so no startup ordering is required.\n *\n * `node-redis` forbids commands on a subscribed connection, so the subscriber MUST be\n * a duplicate rather than the shared client. Same pattern the WebSocket adapter uses.\n *\n * Fails OPEN by design: if Redis is unreachable the turn still runs — losing the\n * ability to cancel is strictly better than refusing to answer at all. The failure is\n * logged at ERROR because a persistent one silently disables cancellation fleet-wide.\n */\n private async ensureSubscriber(): Promise<void> {\n if (this.subscribeStarted) return;\n this.subscribeStarted = true;\n\n const client = this.redisService.getClient();\n if (!client) {\n this.logger.error('[A2ATaskCancel] Redis unavailable — cross-pod cancellation is DISABLED for this pod');\n return;\n }\n\n let subscriber: RedisClientType | undefined;\n try {\n subscriber = client.duplicate() as RedisClientType;\n subscriber.on('error', (error: unknown) => {\n this.logger.error('[A2ATaskCancel] Cancel-channel subscriber error', { ...projectCaughtErrorLogFields(error) });\n });\n // REARM on a later death (review finding L-2). `subscribeStarted` was reset only inside the\n // SETUP catch, so a subscriber that connected and then dropped — a Redis restart, a failover,\n // an idle-timeout reap — logged from the handler above and was never rebuilt for the rest of\n // the pod's lifetime. Cross-pod cancellation then silently stopped working on that pod while\n // every other signal still looked healthy: the turns run, the leases refresh, and only the\n // broadcast never arrives. Clearing the latch lets the next turn rebuild it.\n subscriber.on('end', () => {\n this.logger.warn('[A2ATaskCancel] Cancel-channel subscriber closed; re-arming so the next turn rebuilds it');\n this.subscriber = undefined;\n this.subscribeStarted = false;\n });\n await subscriber.connect();\n await subscriber.subscribe(this.channel(), (message: string) => this.onCancelBroadcast(message));\n this.subscriber = subscriber;\n } catch (error) {\n // DESTROY the half-built connection before retrying. `connect()` can succeed and\n // `subscribe()` still throw, which leaves a live, unusable socket; since\n // `subscribeStarted` resets to allow a retry, every later turn would open another one\n // and leak a Redis connection per turn until the pod hit its file-descriptor limit.\n if (subscriber) {\n try { await subscriber.destroy(); } catch { /* already dead — nothing to reclaim */ }\n }\n this.subscribeStarted = false; // allow a later turn to retry\n this.logger.error('[A2ATaskCancel] Failed to subscribe to the cancel channel', { ...projectCaughtErrorLogFields(error) });\n }\n }\n\n /**\n * Handle a broadcast. Every pod receives every cancel; only the owner acts, and a pod\n * that does not own the task does nothing at all — that silence is the design, not a\n * missed message.\n */\n private onCancelBroadcast(taskId: string): void {\n const controller = this.localTurns.get(taskId);\n if (!controller) return;\n this.logger.info('[A2ATaskCancel] Aborting a locally-executing turn on broadcast', { taskId });\n controller.abort();\n }\n\n /**\n * Register a turn as executing on this pod: returns the signal to hand to the model\n * call, and starts the liveness lease.\n */\n public async beginTurn(taskId: string): Promise<AbortSignal> {\n // ORDER IS LOAD-BEARING. Register locally and write the lease BEFORE the subscriber\n // setup, which on the first turn performs a full Redis duplicate + connect + subscribe.\n // Awaiting that first would leave a window in which the task is materialised and the\n // client — which asked for a non-blocking turn precisely so it could poll at once — sees\n // a non-terminal task with no lease and is told FAILED by derive-on-read.\n const controller = new AbortController();\n this.localTurns.set(taskId, controller);\n\n const client = this.redisService.getClient();\n if (client) {\n let leaseEverWritten = false;\n const refresh = async (): Promise<void> => {\n try {\n await this.redisService.set(this.leaseKey(taskId), '1', { ttl: A2A_TASK_LEASE_TTL_SECONDS });\n leaseEverWritten = true;\n } catch (error) {\n // A missed REFRESH is survivable — the TTL is several refreshes long, so the lease\n // written earlier still answers for this turn.\n //\n // A lease that was NEVER successfully written is a different thing entirely (review\n // finding A3-4), and it used to log identically. No other pod can distinguish \"the\n // write failed\" from \"the pod died\": `tasks/get` on any other replica reports this\n // healthy running turn as FAILED, and the sweep then makes that answer DURABLE. This\n // is the write-side twin of the read-side hole G2-8 closed — the JSDoc claim that\n // \"only a genuinely dead pod stops refreshing\" was false in exactly this case.\n if (leaseEverWritten) {\n this.logger.warn('[A2ATaskCancel] Lease refresh failed', { taskId, ...projectCaughtErrorLogFields(error) });\n } else {\n this.logger.error('[A2ATaskCancel] Task lease has NEVER been written; other pods will read this live turn as abandoned', {\n taskId,\n consequence: 'tasks/get on another replica reports failed, and the sweep may record it durably',\n ...projectCaughtErrorLogFields(error),\n });\n }\n }\n };\n await refresh();\n // Clear any prior heartbeat for this id before replacing it. Unreachable today\n // (task ids are freshly minted uuids), but an overwritten `setInterval` handle is\n // unclearable — it would refresh a lease forever and keep a dead task looking alive.\n const existing = this.leaseTimers.get(taskId);\n if (existing) clearInterval(existing);\n\n const timer = setInterval(() => { void refresh(); }, A2A_TASK_LEASE_REFRESH_MS);\n // Never hold the event loop open on a heartbeat — a draining pod must still exit.\n timer.unref?.();\n this.leaseTimers.set(taskId, timer);\n }\n\n // Subscriber setup happens AFTER the lease exists (see the note above). Not awaited:\n // a slow Redis handshake must not delay the turn, and a cancel arriving before the\n // subscription is live is already handled — `requestCancel` aborts a locally-owned turn\n // directly, without a round-trip.\n void this.ensureSubscriber();\n\n return controller.signal;\n }\n\n /**\n * Release a finished turn: stop the heartbeat, drop the lease, forget the controller.\n *\n * Dropping the lease EAGERLY matters — a terminal task with a live lease would look\n * like it is still executing to anything reading liveness.\n */\n public async endTurn(taskId: string): Promise<void> {\n const timer = this.leaseTimers.get(taskId);\n if (timer) {\n clearInterval(timer);\n this.leaseTimers.delete(taskId);\n }\n this.localTurns.delete(taskId);\n try {\n await this.redisService.del(this.leaseKey(taskId));\n } catch (error) {\n // The TTL reaps it anyway; this only shortens the window.\n this.logger.warn('[A2ATaskCancel] Failed to drop a task lease', { taskId, ...projectCaughtErrorLogFields(error) });\n }\n }\n\n /**\n * Ask the fleet to cancel a task.\n *\n * Returns whether the request was BROADCAST, never whether it was honoured: the owning\n * pod may be gone, or the turn may already have finished. The caller must therefore\n * treat the task record — not this return value — as the truth about cancellation,\n * which is also what the A2A spec requires (`tasks/cancel` returns the task's updated\n * state, not an acknowledgement).\n */\n public async requestCancel(taskId: string): Promise<boolean> {\n // A turn owned by THIS pod is aborted directly — no round-trip, and it still works\n // when Redis is down, which is the single-replica and local-dev case.\n const local = this.localTurns.get(taskId);\n if (local) local.abort();\n\n const client = this.redisService.getClient();\n if (!client) return Boolean(local);\n\n try {\n await client.publish(this.channel(), taskId);\n return true;\n } catch (error) {\n this.logger.error('[A2ATaskCancel] Failed to broadcast a cancel', { taskId, ...projectCaughtErrorLogFields(error) });\n return Boolean(local);\n }\n }\n\n /**\n * Is some pod still working this task?\n *\n * This is the read half of the liveness design: a task whose stored status is\n * non-terminal but whose lease has expired was abandoned by a pod that died, and must\n * be reported as failed rather than left reading `working` forever.\n *\n * Fails SAFE toward \"alive\" when Redis is unreachable — reporting a healthy task as\n * abandoned because the cache blipped would be a far worse lie than a brief stale\n * `working`.\n */\n public async isTurnAlive(taskId: string): Promise<boolean> {\n if (this.localTurns.has(taskId)) return true;\n // Redis ABSENT is not evidence of death. `RedisService.exists` RETURNS FALSE when there\n // is no client rather than throwing, so the catch below never fired for that case and the\n // documented \"fails safe toward alive\" was untrue: with Redis down, every task on another\n // pod would have been reported FAILED. Check the client explicitly.\n if (!this.redisService.getClient()) return true;\n try {\n return await this.redisService.exists(this.leaseKey(taskId));\n } catch {\n return true;\n }\n }\n\n /**\n * Can the lease mechanism actually be WRITTEN right now?\n *\n * Read by the abandoned-task sweep before it repairs anything, and only by it (review\n * finding A3-4). The distinction that makes this necessary: `isTurnAlive` fails safe by\n * answering \"alive\", so a degraded Redis costs a stale `working` — recoverable, and the next\n * poll corrects it. The SWEEP is the one consumer that turns a liveness answer into a\n * DURABLE terminal status, so it is the one that must refuse to act on a signal it cannot\n * trust.\n *\n * A WRITE probe specifically, not a read: the dangerous failure mode is asymmetric — writes\n * failing while reads succeed (a read-only replica, an OOM/quota condition). Under that\n * failure every live turn's lease is missing while `exists` cheerfully reports it absent, so\n * a read-based health check would pass and the sweep would fail every healthy task in the\n * fleet. Probing the exact capability whose loss causes the false reading is the only check\n * that catches it.\n */\n public async isLeaseMechanismWritable(): Promise<boolean> {\n if (!this.redisService.getClient()) return false;\n const probeKey = this.leaseKey('__sweep-health-probe__');\n try {\n await this.redisService.set(probeKey, '1', { ttl: A2A_TASK_LEASE_TTL_SECONDS });\n const present = await this.redisService.exists(probeKey);\n await this.redisService.del(probeKey).catch(() => {});\n return present;\n } catch {\n return false;\n }\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"a2a-task-cancellation.backend.service.js","sourceRoot":"","sources":["../../../../../../src/flows-actors/a2a/services/a2a-task-cancellation.backend.service.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Cross-pod A2A task cancellation.\n *\n * ## You do not need to find the executing pod\n *\n * With several backend replicas behind a load balancer, a `tasks/cancel` request\n * almost never lands on the pod that is running that task's LLM turn. The obvious\n * design — discover which pod owns it and route there — needs a pod registry, a\n * liveness view of it, and an internal call path, and it is wrong on every one of\n * those axes when a pod restarts mid-turn.\n *\n * Instead this BROADCASTS. Any pod publishes `{taskId}` to one Redis channel; every\n * pod runs a single shared subscriber; only the pod holding that `taskId` in its\n * in-process registry reacts. One connection per pod, no routing table, and a pod\n * that died simply does not answer — which is the correct behaviour, not an error to\n * handle.\n *\n * Rejected alternative: polling a Redis flag inside the token loop. That puts a GET on\n * the hottest path in the system (per N tokens, per concurrent turn) and bounds\n * cancellation latency by token cadence. Pub/sub costs nothing per token.\n *\n * ## Liveness is the same mechanism's other half\n *\n * While a pod owns a turn it refreshes a TTL'd lease key. That is what lets\n * `tasks/get` report an abandoned task honestly instead of leaving it `working`\n * forever after a pod dies — see the lease helpers below.\n */\n\nimport { inject, injectable } from 'inversify';\nimport type { RedisClientType } from 'redis';\n\nimport { SAAS_SERVICE_TYPES } from '~wildo-backend-lib/inject/models/service-types';\nimport type { LoggerService } from '~wildo-backend-lib/monitoring/logger/services/logger.service';\nimport type { RedisService } from '~wildo-backend-lib/databases/services/redis.service';\nimport { projectCaughtErrorLogFields } from '~wildo-backend-lib/errors/utils/error-log-level-classification.backend.utils';\n\n/** Channel every replica subscribes to. App-prefixed so tenants of one Redis never cross. */\nconst CANCEL_CHANNEL_SUFFIX = 'a2a:task:cancel';\n/** Lease key prefix — presence means \"a live pod is working this task right now\". */\nconst LEASE_KEY_PREFIX = 'a2a:task:lease:';\n\n/**\n * Lease TTL and refresh cadence. The refresh interval is deliberately well under the\n * TTL: a single missed refresh (GC pause, a slow Redis round-trip) must not make a\n * healthy turn look abandoned. Only a genuinely dead pod stops refreshing.\n */\nexport const A2A_TASK_LEASE_TTL_SECONDS = 30;\nexport const A2A_TASK_LEASE_REFRESH_MS = 10_000;\n\n@injectable()\nexport class A2ATaskCancellationBackendService {\n /** Tasks this pod is currently executing → the controller that aborts them. */\n private readonly localTurns = new Map<string, AbortController>();\n /** Lease heartbeats keyed by task id, cleared on release. */\n private readonly leaseTimers = new Map<string, NodeJS.Timeout>();\n\n private subscriber: RedisClientType | null = null;\n private subscribeStarted = false;\n\n constructor(\n @inject(SAAS_SERVICE_TYPES.Logger) private readonly logger: LoggerService,\n @inject(SAAS_SERVICE_TYPES.RedisService) private readonly redisService: RedisService,\n ) {}\n\n private channel(): string {\n return `${this.redisService.getAppPrefix()}${CANCEL_CHANNEL_SUFFIX}`;\n }\n\n private leaseKey(taskId: string): string {\n return `${LEASE_KEY_PREFIX}${taskId}`;\n }\n\n /**\n * Start this pod's single shared subscriber. Idempotent — safe to call from every\n * turn, so no startup ordering is required.\n *\n * `node-redis` forbids commands on a subscribed connection, so the subscriber MUST be\n * a duplicate rather than the shared client. Same pattern the WebSocket adapter uses.\n *\n * Fails OPEN by design: if Redis is unreachable the turn still runs — losing the\n * ability to cancel is strictly better than refusing to answer at all. The failure is\n * logged at ERROR because a persistent one silently disables cancellation fleet-wide.\n */\n private async ensureSubscriber(): Promise<void> {\n if (this.subscribeStarted) return;\n this.subscribeStarted = true;\n\n const client = this.redisService.getReadyClient();\n if (!client) {\n this.logger.error('[A2ATaskCancel] Redis unavailable — cross-pod cancellation is DISABLED for this pod');\n return;\n }\n\n let subscriber: RedisClientType | undefined;\n try {\n subscriber = client.duplicate() as RedisClientType;\n subscriber.on('error', (error: unknown) => {\n this.logger.error('[A2ATaskCancel] Cancel-channel subscriber error', { ...projectCaughtErrorLogFields(error) });\n });\n // REARM on a later death (review finding L-2). `subscribeStarted` was reset only inside the\n // SETUP catch, so a subscriber that connected and then dropped — a Redis restart, a failover,\n // an idle-timeout reap — logged from the handler above and was never rebuilt for the rest of\n // the pod's lifetime. Cross-pod cancellation then silently stopped working on that pod while\n // every other signal still looked healthy: the turns run, the leases refresh, and only the\n // broadcast never arrives. Clearing the latch lets the next turn rebuild it.\n subscriber.on('end', () => {\n this.logger.warn('[A2ATaskCancel] Cancel-channel subscriber closed; re-arming so the next turn rebuilds it');\n this.subscriber = undefined;\n this.subscribeStarted = false;\n });\n await subscriber.connect();\n await subscriber.subscribe(this.channel(), (message: string) => this.onCancelBroadcast(message));\n this.subscriber = subscriber;\n } catch (error) {\n // DESTROY the half-built connection before retrying. `connect()` can succeed and\n // `subscribe()` still throw, which leaves a live, unusable socket; since\n // `subscribeStarted` resets to allow a retry, every later turn would open another one\n // and leak a Redis connection per turn until the pod hit its file-descriptor limit.\n if (subscriber) {\n try { await subscriber.destroy(); } catch { /* already dead — nothing to reclaim */ }\n }\n this.subscribeStarted = false; // allow a later turn to retry\n this.logger.error('[A2ATaskCancel] Failed to subscribe to the cancel channel', { ...projectCaughtErrorLogFields(error) });\n }\n }\n\n /**\n * Handle a broadcast. Every pod receives every cancel; only the owner acts, and a pod\n * that does not own the task does nothing at all — that silence is the design, not a\n * missed message.\n */\n private onCancelBroadcast(taskId: string): void {\n const controller = this.localTurns.get(taskId);\n if (!controller) return;\n this.logger.info('[A2ATaskCancel] Aborting a locally-executing turn on broadcast', { taskId });\n controller.abort();\n }\n\n /**\n * Register a turn as executing on this pod: returns the signal to hand to the model\n * call, and starts the liveness lease.\n */\n public async beginTurn(taskId: string): Promise<AbortSignal> {\n // ORDER IS LOAD-BEARING. Register locally and write the lease BEFORE the subscriber\n // setup, which on the first turn performs a full Redis duplicate + connect + subscribe.\n // Awaiting that first would leave a window in which the task is materialised and the\n // client — which asked for a non-blocking turn precisely so it could poll at once — sees\n // a non-terminal task with no lease and is told FAILED by derive-on-read.\n const controller = new AbortController();\n this.localTurns.set(taskId, controller);\n\n const client = this.redisService.getReadyClient();\n if (client) {\n let leaseEverWritten = false;\n const refresh = async (): Promise<void> => {\n try {\n await this.redisService.set(this.leaseKey(taskId), '1', { ttl: A2A_TASK_LEASE_TTL_SECONDS });\n leaseEverWritten = true;\n } catch (error) {\n // A missed REFRESH is survivable — the TTL is several refreshes long, so the lease\n // written earlier still answers for this turn.\n //\n // A lease that was NEVER successfully written is a different thing entirely (review\n // finding A3-4), and it used to log identically. No other pod can distinguish \"the\n // write failed\" from \"the pod died\": `tasks/get` on any other replica reports this\n // healthy running turn as FAILED, and the sweep then makes that answer DURABLE. This\n // is the write-side twin of the read-side hole G2-8 closed — the JSDoc claim that\n // \"only a genuinely dead pod stops refreshing\" was false in exactly this case.\n if (leaseEverWritten) {\n this.logger.warn('[A2ATaskCancel] Lease refresh failed', { taskId, ...projectCaughtErrorLogFields(error) });\n } else {\n this.logger.error('[A2ATaskCancel] Task lease has NEVER been written; other pods will read this live turn as abandoned', {\n taskId,\n consequence: 'tasks/get on another replica reports failed, and the sweep may record it durably',\n ...projectCaughtErrorLogFields(error),\n });\n }\n }\n };\n await refresh();\n // Clear any prior heartbeat for this id before replacing it. Unreachable today\n // (task ids are freshly minted uuids), but an overwritten `setInterval` handle is\n // unclearable — it would refresh a lease forever and keep a dead task looking alive.\n const existing = this.leaseTimers.get(taskId);\n if (existing) clearInterval(existing);\n\n const timer = setInterval(() => { void refresh(); }, A2A_TASK_LEASE_REFRESH_MS);\n // Never hold the event loop open on a heartbeat — a draining pod must still exit.\n timer.unref?.();\n this.leaseTimers.set(taskId, timer);\n }\n\n // Subscriber setup happens AFTER the lease exists (see the note above). Not awaited:\n // a slow Redis handshake must not delay the turn, and a cancel arriving before the\n // subscription is live is already handled — `requestCancel` aborts a locally-owned turn\n // directly, without a round-trip.\n void this.ensureSubscriber();\n\n return controller.signal;\n }\n\n /**\n * Release a finished turn: stop the heartbeat, drop the lease, forget the controller.\n *\n * Dropping the lease EAGERLY matters — a terminal task with a live lease would look\n * like it is still executing to anything reading liveness.\n */\n public async endTurn(taskId: string): Promise<void> {\n const timer = this.leaseTimers.get(taskId);\n if (timer) {\n clearInterval(timer);\n this.leaseTimers.delete(taskId);\n }\n this.localTurns.delete(taskId);\n try {\n await this.redisService.del(this.leaseKey(taskId));\n } catch (error) {\n // The TTL reaps it anyway; this only shortens the window.\n this.logger.warn('[A2ATaskCancel] Failed to drop a task lease', { taskId, ...projectCaughtErrorLogFields(error) });\n }\n }\n\n /**\n * Ask the fleet to cancel a task.\n *\n * Returns whether the request was BROADCAST, never whether it was honoured: the owning\n * pod may be gone, or the turn may already have finished. The caller must therefore\n * treat the task record — not this return value — as the truth about cancellation,\n * which is also what the A2A spec requires (`tasks/cancel` returns the task's updated\n * state, not an acknowledgement).\n */\n public async requestCancel(taskId: string): Promise<boolean> {\n // A turn owned by THIS pod is aborted directly — no round-trip, and it still works\n // when Redis is down, which is the single-replica and local-dev case.\n const local = this.localTurns.get(taskId);\n if (local) local.abort();\n\n const client = this.redisService.getReadyClient();\n if (!client) return Boolean(local);\n\n try {\n await client.publish(this.channel(), taskId);\n return true;\n } catch (error) {\n this.logger.error('[A2ATaskCancel] Failed to broadcast a cancel', { taskId, ...projectCaughtErrorLogFields(error) });\n return Boolean(local);\n }\n }\n\n /**\n * Is some pod still working this task?\n *\n * This is the read half of the liveness design: a task whose stored status is\n * non-terminal but whose lease has expired was abandoned by a pod that died, and must\n * be reported as failed rather than left reading `working` forever.\n *\n * Fails SAFE toward \"alive\" when Redis is unreachable — reporting a healthy task as\n * abandoned because the cache blipped would be a far worse lie than a brief stale\n * `working`.\n */\n public async isTurnAlive(taskId: string): Promise<boolean> {\n if (this.localTurns.has(taskId)) return true;\n // Redis ABSENT is not evidence of death. `RedisService.exists` RETURNS FALSE when there\n // is no client rather than throwing, so the catch below never fired for that case and the\n // documented \"fails safe toward alive\" was untrue: with Redis down, every task on another\n // pod would have been reported FAILED. Check the client explicitly.\n if (!this.redisService.getReadyClient()) return true;\n try {\n return await this.redisService.exists(this.leaseKey(taskId));\n } catch {\n return true;\n }\n }\n\n /**\n * Can the lease mechanism actually be WRITTEN right now?\n *\n * Read by the abandoned-task sweep before it repairs anything, and only by it (review\n * finding A3-4). The distinction that makes this necessary: `isTurnAlive` fails safe by\n * answering \"alive\", so a degraded Redis costs a stale `working` — recoverable, and the next\n * poll corrects it. The SWEEP is the one consumer that turns a liveness answer into a\n * DURABLE terminal status, so it is the one that must refuse to act on a signal it cannot\n * trust.\n *\n * A WRITE probe specifically, not a read: the dangerous failure mode is asymmetric — writes\n * failing while reads succeed (a read-only replica, an OOM/quota condition). Under that\n * failure every live turn's lease is missing while `exists` cheerfully reports it absent, so\n * a read-based health check would pass and the sweep would fail every healthy task in the\n * fleet. Probing the exact capability whose loss causes the false reading is the only check\n * that catches it.\n */\n public async isLeaseMechanismWritable(): Promise<boolean> {\n if (!this.redisService.getReadyClient()) return false;\n const probeKey = this.leaseKey('__sweep-health-probe__');\n try {\n await this.redisService.set(probeKey, '1', { ttl: A2A_TASK_LEASE_TTL_SECONDS });\n const present = await this.redisService.exists(probeKey);\n await this.redisService.del(probeKey).catch(() => {});\n return present;\n } catch {\n return false;\n }\n }\n}\n"]}
|
|
@@ -6,6 +6,7 @@ import { ExternalProvidersRegistryBackendService } from '../../external-provider
|
|
|
6
6
|
import { BackendProviderRegistryBackendService } from '../../external-providers/services/backend-provider-registry.backend.service';
|
|
7
7
|
import { PromptCompilerBackendService } from './prompt-compiler.backend.service';
|
|
8
8
|
import { FlowsActors_Agent, FlowsActors_Agent_Invocation, type AgentTraceabilityPolicy, type AgentTraceContext, type AgentCallTraceSink } from '@wildo-ai/saas-models';
|
|
9
|
+
import { CodingAgent_EndOfTurnClassification } from '@wildo-ai/external-connectors-models';
|
|
9
10
|
import type { FunctionToolExecutionContext } from '../../flows-actors/models/function-tools.backend.definitions';
|
|
10
11
|
/**
|
|
11
12
|
* Backend-only authority supplied by the installed host for an Agent call.
|
|
@@ -37,6 +38,45 @@ export interface AgentInvocationHostContext {
|
|
|
37
38
|
}) => void;
|
|
38
39
|
/** Operator-initiated cancellation of a coding session. Cooperative first, kill second. */
|
|
39
40
|
codingSessionAbortSignal?: AbortSignal;
|
|
41
|
+
/**
|
|
42
|
+
* Judge what a coding agent's END-OF-TURN message actually meant, so the turn
|
|
43
|
+
* ladder can auto-continue a self-imposed pause and park a genuine question.
|
|
44
|
+
*
|
|
45
|
+
* The JUDGMENT is host authority, not engine mechanism: it is an auxiliary LLM
|
|
46
|
+
* call, and the host (the companion) owns model resolution and the deliberation
|
|
47
|
+
* infrastructure. The engine owns the LADDER — when to call this, how many
|
|
48
|
+
* continuations to allow, how to record the result — and it FAILS OPEN: if this
|
|
49
|
+
* is absent, throws, or times out, the turn is treated as complete. An
|
|
50
|
+
* autonomous coding lane must never hang, or discard a mutated workspace, on an
|
|
51
|
+
* auxiliary call. Absence therefore reproduces the pre-ladder single-turn
|
|
52
|
+
* behaviour exactly, which is why every non-coding caller can ignore it.
|
|
53
|
+
*/
|
|
54
|
+
classifyCodingTurnEnd?: (input: {
|
|
55
|
+
readonly finalMessage: string;
|
|
56
|
+
readonly turnIndex: number;
|
|
57
|
+
}) => Promise<CodingAgent_EndOfTurnClassification>;
|
|
58
|
+
/**
|
|
59
|
+
* Live phase transitions for a coding session — distinct from
|
|
60
|
+
* {@link onCodingSessionEvent}, which relays the agent's own frames. This fires
|
|
61
|
+
* when the LADDER changes state: a new turn starting, or the session parking on
|
|
62
|
+
* a question. Best-effort, like the event sink: never allowed to fail the run.
|
|
63
|
+
*/
|
|
64
|
+
onCodingSessionStateChange?: (state: CodingSessionLadderState) => void;
|
|
65
|
+
}
|
|
66
|
+
/** The ladder phases a live coding session transitions through. */
|
|
67
|
+
export declare enum CodingSessionLadderPhase {
|
|
68
|
+
/** A turn is executing. */
|
|
69
|
+
RUNNING = "running",
|
|
70
|
+
/** The session parked on a question and is waiting for an operator answer. */
|
|
71
|
+
AWAITING_INPUT = "awaiting_input"
|
|
72
|
+
}
|
|
73
|
+
/** A phase transition in the turn ladder, surfaced to the host for live relay. */
|
|
74
|
+
export interface CodingSessionLadderState {
|
|
75
|
+
readonly phase: CodingSessionLadderPhase;
|
|
76
|
+
/** 1-based index of the turn this state refers to. */
|
|
77
|
+
readonly turnIndex: number;
|
|
78
|
+
/** The agent's question, present only when `phase` is `AWAITING_INPUT`. */
|
|
79
|
+
readonly question?: string;
|
|
40
80
|
}
|
|
41
81
|
/**
|
|
42
82
|
* Thrown by {@link AgentsBackendService.invokeAgentStreaming} when honest token
|
|
@@ -357,6 +397,45 @@ export declare class AgentsBackendService {
|
|
|
357
397
|
* adapter translates the abort into the TIMEOUT failure kind the retry policy already understands.
|
|
358
398
|
*/
|
|
359
399
|
private static readonly STRUCTURED_GENERATION_DEADLINE_MS;
|
|
400
|
+
/**
|
|
401
|
+
* The one prompt sent to auto-continue a coding agent past a self-imposed
|
|
402
|
+
* pause. Named here rather than improvised at the call site so a run's
|
|
403
|
+
* continuations are all identical and auditable, and so the wording — which
|
|
404
|
+
* tells the agent to keep working AND to stop only when genuinely done or
|
|
405
|
+
* genuinely blocked — lives in exactly one place.
|
|
406
|
+
*/
|
|
407
|
+
private static readonly CODING_AUTO_CONTINUE_PROMPT;
|
|
408
|
+
/** Characters of an end-of-turn message kept on a continuation record for audit. */
|
|
409
|
+
private static readonly CONTINUATION_MESSAGE_TAIL_CHARS;
|
|
410
|
+
/**
|
|
411
|
+
* Drive a coding session turn-by-turn: continue a self-imposed pause, park a
|
|
412
|
+
* genuine question, settle on completion — bounded by the profile's
|
|
413
|
+
* continuation budget.
|
|
414
|
+
*
|
|
415
|
+
* The MECHANISM lives here; the JUDGMENT (`classify`) is the host's. The whole
|
|
416
|
+
* loop fails toward settlement: any terminal stop reason, an exhausted budget,
|
|
417
|
+
* or a classifier that is absent or throws all end the run with what the agent
|
|
418
|
+
* has produced rather than hanging. A `NEEDS_INPUT` classification is the only
|
|
419
|
+
* path that returns a `suspension`, and the caller must not read that as a
|
|
420
|
+
* completed result.
|
|
421
|
+
*/
|
|
422
|
+
private driveCodingTurnLadder;
|
|
423
|
+
/**
|
|
424
|
+
* Run the host's end-of-turn classifier, mapping every failure to
|
|
425
|
+
* `CLASSIFIER_UNAVAILABLE` so the ladder can settle rather than hang.
|
|
426
|
+
*
|
|
427
|
+
* The whole point of a separate method: the fail-open contract is one place,
|
|
428
|
+
* and an unexpected classifier value (a host returning a string outside the
|
|
429
|
+
* enum) degrades the same way a throw does.
|
|
430
|
+
*/
|
|
431
|
+
private classifyCodingTurnEndSafely;
|
|
432
|
+
/**
|
|
433
|
+
* Combine per-turn usage into a session total: token counts and cost SUM
|
|
434
|
+
* across turns; the context-window pair is a snapshot, so the latest turn's
|
|
435
|
+
* value wins. Absence stays absence — a field nobody reported must not read as
|
|
436
|
+
* zero, per the usage contract.
|
|
437
|
+
*/
|
|
438
|
+
private mergeCodingUsage;
|
|
360
439
|
private buildBaseOptions;
|
|
361
440
|
/**
|
|
362
441
|
* Execute GENERATE_TEXT operation
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agents.backend.service.d.ts","sourceRoot":"","sources":["../../../../../src/flows-actors/services/agents.backend.service.ts"],"names":[],"mappings":"AAAA,OAAO,kBAAkB,CAAC;AAQ1B,OAAO,EAAE,aAAa,EAAE,MAAM,8DAA8D,CAAC;AAC7F,OAAO,EAAE,0BAA0B,EAAE,MAAM,kEAAkE,CAAC;AAE9G,OAAO,EAAE,0BAA0B,EAAE,MAAM,iCAAiC,CAAC;AAC7E,OAAO,EAAE,uCAAuC,EAAqC,MAAM,4FAA4F,CAAC;AACxL,OAAO,EAAE,qCAAqC,EAAE,MAAM,0FAA0F,CAAC;AACjJ,OAAO,EAAE,4BAA4B,EAAE,MAAM,mCAAmC,CAAC;AACjF,OAAO,EACL,iBAAiB,EAEjB,4BAA4B,EAe5B,KAAK,uBAAuB,EAC5B,KAAK,iBAAiB,EACtB,KAAK,kBAAkB,EAExB,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"agents.backend.service.d.ts","sourceRoot":"","sources":["../../../../../src/flows-actors/services/agents.backend.service.ts"],"names":[],"mappings":"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"}
|