gdc-sdk-node-ts 2.4.24 → 2.4.30

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 (32) hide show
  1. package/README.md +15 -6
  2. package/dist/constants/lifecycle.d.ts +8 -0
  3. package/dist/constants/lifecycle.js +8 -0
  4. package/dist/device-activation.d.ts +48 -1
  5. package/dist/device-activation.js +157 -1
  6. package/dist/digital-twin.d.ts +4 -0
  7. package/dist/digital-twin.js +16 -4
  8. package/dist/host-onboarding.d.ts +7 -0
  9. package/dist/node-managed-wallet.d.ts +41 -1
  10. package/dist/node-managed-wallet.js +52 -0
  11. package/dist/node-runtime-client.d.ts +29 -1
  12. package/dist/node-runtime-client.js +92 -1
  13. package/dist/orchestration/client-port.d.ts +11 -1
  14. package/dist/orchestration/digital-twin-sdk.js +3 -1
  15. package/dist/orchestration/host-onboarding-sdk.d.ts +9 -3
  16. package/dist/orchestration/host-onboarding-sdk.js +10 -1
  17. package/dist/orchestration/individual-controller-sdk.d.ts +29 -3
  18. package/dist/orchestration/individual-controller-sdk.js +48 -2
  19. package/dist/orchestration/organization-controller-sdk.d.ts +19 -6
  20. package/dist/orchestration/organization-controller-sdk.js +27 -6
  21. package/dist/organization-controller-recovery.d.ts +4 -2
  22. package/dist/organization-controller-recovery.js +14 -8
  23. package/dist/organization-employee-lifecycle.d.ts +4 -0
  24. package/dist/organization-employee-lifecycle.js +17 -1
  25. package/dist/organization-license-order.d.ts +8 -3
  26. package/dist/resource-operations.d.ts +67 -0
  27. package/dist/resource-operations.js +62 -1
  28. package/dist/runtime-client-paths.d.ts +10 -0
  29. package/dist/runtime-client-paths.js +10 -0
  30. package/dist/server-profile-session.d.ts +32 -3
  31. package/dist/server-profile-session.js +21 -17
  32. package/package.json +1 -1
@@ -1,6 +1,7 @@
1
1
  import { buildAppHeaders, createBootstrapFacade, decodeTransportResponse, renderCommunicationOutboxRequest, renderGatewayMessageRequest, renderTransportPollRequest, resolveAppInfo, TransportProfiles, } from 'gdc-sdk-core-ts';
2
2
  import { pollUntilCompleteWithMethod } from './async-polling.js';
3
3
  import { confirmLegalOrganizationOrderWithDeps, HostLifecycleRequestType, HostedTenantLifecycleRequestType, submitHostedTenantLifecycleWithDeps, } from './host-onboarding.js';
4
+ import { GwCoreLifecycleRequestType } from './constants/lifecycle.js';
4
5
  import { confirmIndividualOrganizationOrderWithDeps, } from './individual-onboarding.js';
5
6
  import { ensureFamilyOrganizationRegistrationWithDeps, } from './family-organization-registration.js';
6
7
  import { searchFamilyOrganizationWithDeps } from './family-organization-search.js';
@@ -9,7 +10,7 @@ import { activateEmployeeDeviceWithActivationCodeWithDeps, activateEmployeeDevic
9
10
  import { extractOfferIdFromResponseBody, extractOfferPreviewFromResponseBody, } from './order-offer-summary.js';
10
11
  import { confirmOrganizationLicenseOrderWithDeps } from './organization-license-order.js';
11
12
  import { startIndividualOrganizationWithDeps } from './individual-start.js';
12
- import { createOrganizationEmployeeWithDeps, issueOrganizationEmployeeLicenseWithDeps, disableIndividualMemberWithDeps, disableIndividualOrganizationWithDeps, disableOrganizationEmployeeWithDeps, listIndividualLicenseOffersWithDeps, listIndividualLicenseOrdersWithDeps, listIndividualLicensesWithDeps, addFreeIndividualMemberLicensesWithDeps, issueIndividualMemberLicenseWithDeps, transitionIndividualMemberLicenseWithDeps, listOrganizationLicenseOffersWithDeps, listOrganizationLicenseOrdersWithDeps, listOrganizationLicensesWithDeps, grantProfessionalAccessWithDeps, buildClinicalSectionUpdateIngestion, buildClinicalSummaryUpdateIngestion, ingestCommunicationAndUpdateIndexWithDeps, requestProfessionalAccessWithDeps, buildVitalSignBatchCommunicationFromSearchResponse, registerBlockchainArtifactAndUpdateIndexWithDeps, requestClinicalSummaryWithDeps, revokeProfessionalAccessWithDeps, searchCommunicationParticipantsWithDeps, purgeIndividualMemberWithDeps, purgeIndividualOrganizationWithDeps, purgeOrganizationEmployeeWithDeps, prepareEmployeeLifecycleMessageForTransport, searchIndividualLicensesWithDeps, searchIndividualLicenseOffersWithDeps, searchIndividualLicenseOrdersWithDeps, searchOrganizationLicensesWithDeps, searchOrganizationLicenseOffersWithDeps, searchOrganizationLicenseOrdersWithDeps, searchOrganizationEmployeesWithDeps, searchClinicalBundleWithDeps, searchLatestIpsWithDeps, upsertRelatedPersonAndPollWithDeps, normalizeCommunicationPathFormatSegment, addFreeOrganizationEmployeeLicensesWithDeps, requestOrganizationEmployeeLicenseOfferWithDeps, } from './resource-operations.js';
13
+ import { createOrganizationEmployeeWithDeps, issueOrganizationEmployeeLicenseWithDeps, disableIndividualMemberWithDeps, disableIndividualOrganizationWithDeps, disableOrganizationEmployeeWithDeps, listIndividualLicenseOffersWithDeps, listIndividualLicenseOrdersWithDeps, listIndividualLicensesWithDeps, addFreeIndividualMemberLicensesWithDeps, issueIndividualMemberLicenseWithDeps, transitionIndividualMemberLicenseWithDeps, listOrganizationLicenseOffersWithDeps, listOrganizationLicenseOrdersWithDeps, listOrganizationLicensesWithDeps, grantProfessionalAccessWithDeps, setDigitalTwinSecondaryUseConsentWithDeps, purgeDigitalTwinSubjectLinkWithDeps, buildClinicalSectionUpdateIngestion, buildClinicalSummaryUpdateIngestion, ingestCommunicationAndUpdateIndexWithDeps, requestProfessionalAccessWithDeps, buildVitalSignBatchCommunicationFromSearchResponse, registerBlockchainArtifactAndUpdateIndexWithDeps, requestClinicalSummaryWithDeps, revokeProfessionalAccessWithDeps, searchCommunicationParticipantsWithDeps, purgeIndividualMemberWithDeps, purgeIndividualOrganizationWithDeps, purgeOrganizationEmployeeWithDeps, prepareEmployeeLifecycleMessageForTransport, searchIndividualLicensesWithDeps, searchIndividualLicenseOffersWithDeps, searchIndividualLicenseOrdersWithDeps, searchOrganizationLicensesWithDeps, searchOrganizationLicenseOffersWithDeps, searchOrganizationLicenseOrdersWithDeps, searchOrganizationEmployeesWithDeps, searchClinicalBundleWithDeps, searchLatestIpsWithDeps, upsertRelatedPersonAndPollWithDeps, normalizeCommunicationPathFormatSegment, addFreeOrganizationEmployeeLicensesWithDeps, requestOrganizationEmployeeLicenseOfferWithDeps, } from './resource-operations.js';
13
14
  import { listOrganizationEmployeeLifecycleWithDeps, provisionOrganizationEmployeeWithDeps, } from './organization-employee-lifecycle.js';
14
15
  import { buildRuntimeHeaders, fetchWithTimeout, parseResponseBody, postRenderedWithRuntimeConfig, } from './runtime-transport.js';
15
16
  import { activateOrganizationInGatewayFromIcaProofWithDeps, submitLegalOrganizationCredentialReissuanceWithDeps, submitLegalOrganizationVerificationTransactionWithDeps, submitOrganizationDidBindingWithDeps, } from './runtime-host-submission.js';
@@ -355,6 +356,20 @@ export class HttpRuntimeClient {
355
356
  defaultIntervalMs: pollOptions?.intervalMs,
356
357
  });
357
358
  }
359
+ /** Re-enables one suspended hosted tenant without changing its legal identity. */
360
+ async enableTenant(hostCtx, input, pollOptions) {
361
+ return submitHostedTenantLifecycleWithDeps({
362
+ hostCtx,
363
+ input,
364
+ requestType: GwCoreLifecycleRequestType.TenantEnable,
365
+ submitPath: this.paths.hostRegistryOrganizationEnablePath.bind(this.paths),
366
+ pollPath: this.paths.hostRegistryOrganizationEnablePollPath.bind(this.paths),
367
+ thidPrefix: 'tenant-enable',
368
+ submitAndPoll: this.submitAndPoll.bind(this),
369
+ defaultTimeoutMs: pollOptions?.timeoutMs,
370
+ defaultIntervalMs: pollOptions?.intervalMs,
371
+ });
372
+ }
358
373
  /**
359
374
  * Purges one already-disabled hosted tenant through the host registry.
360
375
  */
@@ -371,6 +386,53 @@ export class HttpRuntimeClient {
371
386
  defaultIntervalMs: pollOptions?.intervalMs,
372
387
  });
373
388
  }
389
+ /** Reads authoritative tenant and descendant lifecycle state from the host registry. */
390
+ async getTenantLifecycleStatus(hostCtx, input, pollOptions) {
391
+ return submitHostedTenantLifecycleWithDeps({
392
+ hostCtx,
393
+ input,
394
+ requestType: GwCoreLifecycleRequestType.TenantStatus,
395
+ submitPath: this.paths.hostRegistryOrganizationStatusPath.bind(this.paths),
396
+ pollPath: this.paths.hostRegistryOrganizationStatusPollPath.bind(this.paths),
397
+ thidPrefix: 'tenant-status',
398
+ submitAndPoll: this.submitAndPoll.bind(this),
399
+ defaultTimeoutMs: pollOptions?.timeoutMs,
400
+ defaultIntervalMs: pollOptions?.intervalMs,
401
+ });
402
+ }
403
+ /** Explicitly disables one descendant group; tenant disable itself never cascades. */
404
+ async disableTenantDescendants(hostCtx, input, pollOptions) {
405
+ return this.submitTenantDescendantLifecycle(hostCtx, input, false, pollOptions);
406
+ }
407
+ /** Explicitly purges one descendant group; tenant purge itself never cascades. */
408
+ async purgeTenantDescendants(hostCtx, input, pollOptions) {
409
+ return this.submitTenantDescendantLifecycle(hostCtx, input, true, pollOptions);
410
+ }
411
+ submitTenantDescendantLifecycle(hostCtx, input, purge, pollOptions) {
412
+ return submitHostedTenantLifecycleWithDeps({
413
+ hostCtx,
414
+ input: {
415
+ ...input,
416
+ additionalClaims: {
417
+ ...input.additionalClaims,
418
+ 'org.schema.Action.lifecycle.descendantKind': input.descendantKind,
419
+ },
420
+ },
421
+ requestType: purge
422
+ ? GwCoreLifecycleRequestType.TenantPurgeDescendants
423
+ : GwCoreLifecycleRequestType.TenantDisableDescendants,
424
+ submitPath: purge
425
+ ? this.paths.hostRegistryOrganizationPurgeDescendantsPath.bind(this.paths)
426
+ : this.paths.hostRegistryOrganizationDisableDescendantsPath.bind(this.paths),
427
+ pollPath: purge
428
+ ? this.paths.hostRegistryOrganizationPurgeDescendantsPollPath.bind(this.paths)
429
+ : this.paths.hostRegistryOrganizationDisableDescendantsPollPath.bind(this.paths),
430
+ thidPrefix: purge ? 'tenant-purge-descendants' : 'tenant-disable-descendants',
431
+ submitAndPoll: this.submitAndPoll.bind(this),
432
+ defaultTimeoutMs: pollOptions?.timeoutMs,
433
+ defaultIntervalMs: pollOptions?.intervalMs,
434
+ });
435
+ }
374
436
  /**
375
437
  * Creates an employee or professional entry under an existing organization tenant.
376
438
  */
@@ -763,6 +825,26 @@ export class HttpRuntimeClient {
763
825
  submitAndPoll: this.submitAndPoll.bind(this),
764
826
  });
765
827
  }
828
+ /**
829
+ * Applies the subject's reversible secondary-use decision and lets GW
830
+ * rebuild or pause the tenant-private digital-twin projection.
831
+ */
832
+ async setDigitalTwinSecondaryUseConsent(ctx, input) {
833
+ return setDigitalTwinSecondaryUseConsentWithDeps(ctx, input, {
834
+ buildConsentClaimsWithCid: this.buildConsentClaimsWithCid.bind(this),
835
+ individualConsentR4BatchPath: this.paths.individualConsentR4BatchPath.bind(this.paths),
836
+ individualConsentR4PollPath: this.paths.individualConsentR4PollPath.bind(this.paths),
837
+ submitAndPoll: this.submitAndPoll.bind(this),
838
+ });
839
+ }
840
+ /** Offboards the subject from this index provider without deleting the anonymous twin. */
841
+ async purgeDigitalTwinSubjectLink(ctx, input) {
842
+ return purgeDigitalTwinSubjectLinkWithDeps(ctx, input, {
843
+ individualResearchSubjectPurgePath: this.paths.individualResearchSubjectPurgePath.bind(this.paths),
844
+ individualResearchSubjectPurgePollPath: this.paths.individualResearchSubjectPurgePollPath.bind(this.paths),
845
+ submitAndPoll: this.submitAndPoll.bind(this),
846
+ });
847
+ }
766
848
  /**
767
849
  * Closes an existing professional consent by setting its period end and
768
850
  * resubmitting the updated consent resource.
@@ -820,6 +902,13 @@ export class HttpRuntimeClient {
820
902
  }),
821
903
  });
822
904
  }
905
+ /**
906
+ * Actor-facade name retained for organization controllers and employees.
907
+ * The implementation is actor-neutral and shares the profile-device flow.
908
+ */
909
+ async activateEmployeeDeviceWithActivationRequest(input) {
910
+ return this.activateProfileDeviceWithActivationRequest(input);
911
+ }
823
912
  /**
824
913
  * Creates or updates a `RelatedPerson` for non-employee family/caregiver
825
914
  * roles such as a grandfather, guardian, or external caregiver.
@@ -1164,6 +1253,8 @@ export class HttpRuntimeClient {
1164
1253
  individualRelatedPersonPurgePollPath(ctx) { return this.paths.individualRelatedPersonPurgePollPath(ctx); }
1165
1254
  individualConsentR4BatchPath(ctx) { return this.paths.individualConsentR4BatchPath(ctx); }
1166
1255
  individualConsentR4PollPath(ctx) { return this.paths.individualConsentR4PollPath(ctx); }
1256
+ individualResearchSubjectPurgePath(ctx) { return this.paths.individualResearchSubjectPurgePath(ctx); }
1257
+ individualResearchSubjectPurgePollPath(ctx) { return this.paths.individualResearchSubjectPurgePollPath(ctx); }
1167
1258
  individualCommunicationBatchPath(ctx, format) { return this.paths.individualCommunicationBatchPath(ctx, format); }
1168
1259
  individualCommunicationPollPath(ctx, format) { return this.paths.individualCommunicationPollPath(ctx, format); }
1169
1260
  individualCommunicationSearchPath(ctx) { return this.paths.individualCommunicationSearchPath(ctx); }
@@ -18,7 +18,7 @@ import type { EnsureFamilyOrganizationRegistrationInput, EnsureFamilyOrganizatio
18
18
  import type { OrganizationLicenseOrderConfirmInput } from '../organization-license-order.js';
19
19
  import type { SmartTokenExchangeResult, SmartTokenRequestInput } from '../smart-token.js';
20
20
  import type { DigitalTwinMaterializationInput, DigitalTwinSearchInput, DigitalTwinSelectionInput } from '../digital-twin.js';
21
- import type { CommunicationIngestionInput, BlockchainArtifactRegistrationInput, CommunicationParticipantRuntimeSearchInput, ClinicalBundleSearchInput, ClinicalSectionUpdateInput, ClinicalSummaryReadResult, ClinicalSummaryRequestInput, ClinicalSummaryUpdateInput, VitalSignBatchCommunicationFromSearchResponseInput, DigitalTwinGenerationInput, GrantProfessionalAccessInput, GrantProfessionalAccessResult, IndividualMemberLifecycleInput, IndividualMemberLicenseAddInput, IndividualMemberLicenseInvitationInput, IndividualMemberLicenseTransitionInput, IpsOrFhirImportInput, OrganizationEmployeeCreationInput, OrganizationEmployeeLicenseAddInput, OrganizationEmployeeLicenseOfferInput, OrganizationEmployeeLicenseInvitationInput, OrganizationEmployeeLifecycleInput, OrganizationEmployeeSearchInput, ProfessionalAccessRequestInput, ProfessionalAccessRequestResult, RevokeProfessionalAccessInput, RevokeProfessionalAccessResult, RelatedPersonUpsertInput } from '../resource-operations.js';
21
+ import type { CommunicationIngestionInput, BlockchainArtifactRegistrationInput, CommunicationParticipantRuntimeSearchInput, ClinicalBundleSearchInput, ClinicalSectionUpdateInput, ClinicalSummaryReadResult, ClinicalSummaryRequestInput, ClinicalSummaryUpdateInput, VitalSignBatchCommunicationFromSearchResponseInput, DigitalTwinGenerationInput, DigitalTwinSecondaryUseConsentInput, DigitalTwinSecondaryUseConsentResult, DigitalTwinSubjectLinkPurgeInput, DigitalTwinSubjectLinkPurgeResult, GrantProfessionalAccessInput, GrantProfessionalAccessResult, IndividualMemberLifecycleInput, IndividualMemberLicenseAddInput, IndividualMemberLicenseInvitationInput, IndividualMemberLicenseTransitionInput, IpsOrFhirImportInput, OrganizationEmployeeCreationInput, OrganizationEmployeeLicenseAddInput, OrganizationEmployeeLicenseOfferInput, OrganizationEmployeeLicenseInvitationInput, OrganizationEmployeeLifecycleInput, OrganizationEmployeeSearchInput, ProfessionalAccessRequestInput, ProfessionalAccessRequestResult, RevokeProfessionalAccessInput, RevokeProfessionalAccessResult, RelatedPersonUpsertInput } from '../resource-operations.js';
22
22
  /**
23
23
  * Shared node-runtime activation input.
24
24
  *
@@ -73,7 +73,15 @@ export type RuntimeClient = {
73
73
  disableHost?: (hostCtx: HostRouteContext, input: HostedTenantLifecycleInput, pollOptions?: PollOptions) => Promise<SubmitAndPollResult>;
74
74
  purgeHost?: (hostCtx: HostRouteContext, input: HostedTenantLifecycleInput, pollOptions?: PollOptions) => Promise<SubmitAndPollResult>;
75
75
  disableTenant?: (hostCtx: HostRouteContext, input: HostedTenantLifecycleInput, pollOptions?: PollOptions) => Promise<SubmitAndPollResult>;
76
+ enableTenant?: (hostCtx: HostRouteContext, input: HostedTenantLifecycleInput, pollOptions?: PollOptions) => Promise<SubmitAndPollResult>;
76
77
  purgeTenant?: (hostCtx: HostRouteContext, input: HostedTenantLifecycleInput, pollOptions?: PollOptions) => Promise<SubmitAndPollResult>;
78
+ getTenantLifecycleStatus?: (hostCtx: HostRouteContext, input: HostedTenantLifecycleInput, pollOptions?: PollOptions) => Promise<SubmitAndPollResult>;
79
+ disableTenantDescendants?: (hostCtx: HostRouteContext, input: HostedTenantLifecycleInput & {
80
+ descendantKind: 'individuals';
81
+ }, pollOptions?: PollOptions) => Promise<SubmitAndPollResult>;
82
+ purgeTenantDescendants?: (hostCtx: HostRouteContext, input: HostedTenantLifecycleInput & {
83
+ descendantKind: 'individuals';
84
+ }, pollOptions?: PollOptions) => Promise<SubmitAndPollResult>;
77
85
  createOrganizationEmployee?: (ctx: RouteContext, input: OrganizationEmployeeCreationInput, pollOptions?: PollOptions) => Promise<SubmitAndPollResult>;
78
86
  provisionOrganizationEmployee?: (ctx: RouteContext, input: OrganizationEmployeeProvisioningInput) => Promise<OrganizationEmployeeProvisioningResult>;
79
87
  issueOrganizationEmployeeLicense?: (ctx: RouteContext, input: OrganizationEmployeeLicenseInvitationInput) => Promise<SubmitAndPollResult>;
@@ -127,6 +135,8 @@ export type RuntimeClient = {
127
135
  importIpsOrFhirAndUpdateIndex?: (ctx: RouteContext, input: IpsOrFhirImportInput) => Promise<SubmitAndPollResult>;
128
136
  upsertRelatedPersonAndPoll?: (ctx: RouteContext, input: RelatedPersonUpsertInput) => Promise<SubmitAndPollResult>;
129
137
  generateDigitalTwinFromSubjectData?: (ctx: RouteContext, input: DigitalTwinGenerationInput) => Promise<SubmitAndPollResult>;
138
+ setDigitalTwinSecondaryUseConsent?: (ctx: RouteContext, input: DigitalTwinSecondaryUseConsentInput) => Promise<DigitalTwinSecondaryUseConsentResult>;
139
+ purgeDigitalTwinSubjectLink?: (ctx: RouteContext, input: DigitalTwinSubjectLinkPurgeInput) => Promise<DigitalTwinSubjectLinkPurgeResult>;
130
140
  searchClinicalBundle?: (ctx: RouteContext, input: ClinicalBundleSearchInput) => Promise<SubmitAndPollResult>;
131
141
  searchDigitalTwins?: (ctx: RouteContext, input: DigitalTwinSearchInput) => Promise<SubmitAndPollResult>;
132
142
  saveDigitalTwinSelection?: (ctx: RouteContext, input: DigitalTwinSelectionInput) => Promise<SubmitAndPollResult>;
@@ -1,7 +1,7 @@
1
1
  // Copyright 2026 Antifraud Services Inc. under the Apache License, Version 2.0.
2
2
  import { HealthcareConsentPurposes, ServiceCapability } from 'gdc-common-utils-ts/constants';
3
3
  import { CompositionClaim } from 'gdc-common-utils-ts/models';
4
- import { DigitalTwinSearchParameter, readDigitalTwinSearchResult } from '../digital-twin.js';
4
+ import { assertDigitalTwinSubjectId, DigitalTwinSearchParameter, readDigitalTwinSearchResult, } from '../digital-twin.js';
5
5
  import { requireClientMethod } from './client-port.js';
6
6
  /** Public research facade for licensed digital-twin access. */
7
7
  export class DigitalTwinSdk {
@@ -62,6 +62,7 @@ export class DigitalTwinSdk {
62
62
  * The canonical clinical twin is never modified.
63
63
  */
64
64
  saveSelection(ctx, input) {
65
+ assertDigitalTwinSubjectId(input.twinSubjectId);
65
66
  const requestedAuthorDid = String(input.authorDid || '').trim() || undefined;
66
67
  if (this.researcherDid && requestedAuthorDid && requestedAuthorDid !== this.researcherDid) {
67
68
  throw new Error('Digital twin selection authorDid must match the actor session.');
@@ -92,6 +93,7 @@ export class DigitalTwinSdk {
92
93
  }
93
94
  /** Materializes one selected research subject through `ResearchSubject/$summary`. */
94
95
  materialize(ctx, input) {
96
+ assertDigitalTwinSubjectId(input.twinSubjectId);
95
97
  return requireClientMethod(this.client, 'materializeDigitalTwin')(ctx, {
96
98
  ...input,
97
99
  accessToken: input.accessToken || this.smartAccessToken,
@@ -1,10 +1,16 @@
1
- import { type NodeRuntimeClient, type NodeOrganizationActivationInput, type PollOptions, type SubmitAndPollResult, type SubmitPayload } from './client-port.js';
1
+ import { type NodeRuntimeClient, type NodeLegalOrganizationVerificationTransactionInput, type NodeOrganizationActivationInput, type PollOptions, type SubmitAndPollResult, type SubmitPayload } from './client-port.js';
2
2
  import type { HostingControllerFacade, HostLifecycleInput, HostRouteContext, LegalOrganizationOrderInput } from '../host-onboarding.js';
3
3
  import type { NodeCapability } from '../session.js';
4
4
  export declare class HostOnboardingSdk implements HostingControllerFacade {
5
5
  private readonly client;
6
- private readonly capabilities;
7
- constructor(client: NodeRuntimeClient, capabilities?: readonly NodeCapability[]);
6
+ private readonly capabilities?;
7
+ constructor(client: NodeRuntimeClient, capabilities?: readonly NodeCapability[] | undefined);
8
+ /**
9
+ * Starts the canonical host-side legal-organization verification
10
+ * transaction. Confirm the returned Offer through
11
+ * `confirmLegalOrganizationOrder(...)`.
12
+ */
13
+ submitLegalOrganizationVerificationTransaction(hostCtx: HostRouteContext, input: NodeLegalOrganizationVerificationTransactionInput, pollOptions?: PollOptions): Promise<SubmitAndPollResult>;
8
14
  /**
9
15
  * Submits the legal organization activation proof and required declared
10
16
  * service capabilities to GW CORE.
@@ -4,10 +4,19 @@ import { requireClientMethod, submitAndPollWithClient, } from './client-port.js'
4
4
  import { ActorCapabilities, ActorKinds } from 'gdc-common-utils-ts/constants/actor-session';
5
5
  import { assertFacadeCapability } from './capability-guard.js';
6
6
  export class HostOnboardingSdk {
7
- constructor(client, capabilities = []) {
7
+ constructor(client, capabilities) {
8
8
  this.client = client;
9
9
  this.capabilities = capabilities;
10
10
  }
11
+ /**
12
+ * Starts the canonical host-side legal-organization verification
13
+ * transaction. Confirm the returned Offer through
14
+ * `confirmLegalOrganizationOrder(...)`.
15
+ */
16
+ submitLegalOrganizationVerificationTransaction(hostCtx, input, pollOptions) {
17
+ assertFacadeCapability(this.capabilities, ActorCapabilities.HostingActivateOrganization, ActorKinds.HostOnboarding, 'submitLegalOrganizationVerificationTransaction');
18
+ return requireClientMethod(this.client, 'submitLegalOrganizationVerificationTransaction')(hostCtx, input, pollOptions);
19
+ }
11
20
  /**
12
21
  * Submits the legal organization activation proof and required declared
13
22
  * service capabilities to GW CORE.
@@ -1,3 +1,4 @@
1
+ import { type ConsentRule } from 'gdc-common-utils-ts/models/consent-rule';
1
2
  import { type IndividualControllerCredentialInput, type IndividualControllerVpPayloadInput, type IndividualSubjectCredentialInput } from 'gdc-common-utils-ts';
2
3
  import { type NodeRuntimeClient, type PollOptions, type SubmitAndPollResult, type SubmitPayload } from './client-port.js';
3
4
  import type { FamilyOrganizationSummary } from 'gdc-common-utils-ts/utils/family-organization-summary';
@@ -7,7 +8,7 @@ import type { IndividualOrganizationConfirmOrderInput, RouteContext } from '../i
7
8
  import type { IndividualOrganizationBootstrapInput, IndividualOrganizationStartResult } from '../individual-start.js';
8
9
  import type { NodeCapability } from '../session.js';
9
10
  import type { IndividualOrganizationLifecycleInput } from 'gdc-sdk-core-ts';
10
- import type { BlockchainArtifactRegistrationInput, ClinicalBundleSearchInput, ClinicalSectionUpdateInput, ClinicalSummaryReadResult, ClinicalSummaryRequestInput, ClinicalSummaryUpdateInput, CommunicationIngestionInput, CommunicationParticipantRuntimeSearchInput, DigitalTwinGenerationInput, GrantProfessionalAccessInput, GrantProfessionalAccessResult, IndividualMemberLifecycleInput, IndividualMemberLicenseAddInput, IndividualMemberLicenseInvitationInput, IndividualMemberLicenseTransitionInput, IpsOrFhirImportInput, LicenseListRuntimeSearchInput, LicenseOfferRuntimeSearchInput, LicenseOrderRuntimeSearchInput, RevokeProfessionalAccessInput, RevokeProfessionalAccessResult, ProfessionalAccessRequestDecisionInput, ProfessionalAccessRequestSearchInput, RelatedPersonUpsertInput } from '../resource-operations.js';
11
+ import type { BlockchainArtifactRegistrationInput, ClinicalBundleSearchInput, ClinicalSectionUpdateInput, ClinicalSummaryReadResult, ClinicalSummaryRequestInput, ClinicalSummaryUpdateInput, CommunicationIngestionInput, CommunicationParticipantRuntimeSearchInput, DigitalTwinGenerationInput, DigitalTwinSecondaryUseConsentInput, DigitalTwinSecondaryUseConsentResult, DigitalTwinSubjectLinkPurgeInput, DigitalTwinSubjectLinkPurgeResult, GrantProfessionalAccessInput, GrantProfessionalAccessResult, IndividualMemberLifecycleInput, IndividualMemberLicenseAddInput, IndividualMemberLicenseInvitationInput, IndividualMemberLicenseTransitionInput, IpsOrFhirImportInput, LicenseListRuntimeSearchInput, LicenseOfferRuntimeSearchInput, LicenseOrderRuntimeSearchInput, RevokeProfessionalAccessInput, RevokeProfessionalAccessResult, ProfessionalAccessRequestDecisionInput, ProfessionalAccessRequestSearchInput, RelatedPersonUpsertInput } from '../resource-operations.js';
11
12
  import type { SmartTokenExchangeResult, SmartTokenRequestInput } from '../smart-token.js';
12
13
  /**
13
14
  * Individual-controller oriented facade over a `NodeRuntimeClient`.
@@ -117,10 +118,35 @@ export declare class IndividualControllerSdk {
117
118
  */
118
119
  searchCommunicationParticipants(ctx: RouteContext, input: CommunicationParticipantRuntimeSearchInput): Promise<SubmitAndPollResult>;
119
120
  /**
120
- * Explicitly packages subject data for a study/transfer flow.
121
- * Normal clinical ingestion is projected into the tenant research index by GW.
121
+ * Legacy direct Composition transfer hook.
122
+ *
123
+ * @deprecated Canonical twins are created only by GW from current subject
124
+ * data after `setDigitalTwinSecondaryUseConsent(..., { decision: 'permit' })`.
125
+ * The Node runtime intentionally does not publish canonical Compositions.
122
126
  */
123
127
  generateDigitalTwinFromSubjectData(ctx: RouteContext, input: DigitalTwinGenerationInput): Promise<SubmitAndPollResult>;
128
+ /**
129
+ * Enables or disables the subject's secondary-use digital-twin projection.
130
+ * This is the canonical patient-side operation; application code must not
131
+ * publish a canonical Composition directly into the research index.
132
+ */
133
+ setDigitalTwinSecondaryUseConsent(ctx: RouteContext, input: DigitalTwinSecondaryUseConsentInput): Promise<DigitalTwinSecondaryUseConsentResult>;
134
+ /**
135
+ * Offboards the subject from the current index provider. This removes only
136
+ * the private subject/twin correspondence; it never deletes anonymous twin
137
+ * data already shared for research.
138
+ */
139
+ purgeDigitalTwinSubjectLink(ctx: RouteContext, input: DigitalTwinSubjectLinkPurgeInput): Promise<DigitalTwinSubjectLinkPurgeResult>;
140
+ /** Returns whether the subject currently permits projection by its index provider. */
141
+ getDigitalTwinSecondaryUseConsentStatus(ctx: RouteContext, input: Readonly<{
142
+ subjectDid: string;
143
+ indexProviderOrganizationDid: string;
144
+ consentIdentifier: string;
145
+ }>): Promise<Readonly<{
146
+ exists: boolean;
147
+ enabled: boolean;
148
+ consent?: ConsentRule;
149
+ }>>;
124
150
  /**
125
151
  * Searches indexed clinical bundles for the current subject/controller context.
126
152
  */
@@ -1,8 +1,11 @@
1
1
  // Copyright 2026 Antifraud Services Inc. under the Apache License, Version 2.0.
2
2
  import { ActorCapabilities, ActorKinds } from 'gdc-common-utils-ts/constants/actor-session';
3
+ import { HealthcareConsentPurposes, ServiceCapability } from 'gdc-common-utils-ts/constants';
4
+ import { ClaimConsent } from 'gdc-common-utils-ts/models/consent-rule';
3
5
  import { buildIndividualControllerIdentityVpPayload, buildUnsignedIndividualControllerIdentityVpJwt, getIndividualControllerIdentitySameAs, getIndividualControllerIdentityVC, getIndividualSubjectVC, } from 'gdc-common-utils-ts';
4
6
  import { requireClientMethod, submitAndPollWithClient, } from './client-port.js';
5
7
  import { assertFacadeCapability } from './capability-guard.js';
8
+ import { GatewayActiveConsentProvider } from '../gateway-active-consent-provider.js';
6
9
  import { buildProfessionalAccessRequestDecisionGrant } from '../resource-operations.js';
7
10
  import { buildProfessionalAccessRequestSearchInput } from '../resource-operations.js';
8
11
  /**
@@ -175,13 +178,56 @@ export class IndividualControllerSdk {
175
178
  return requireClientMethod(this.client, 'searchCommunicationParticipants')(ctx, input);
176
179
  }
177
180
  /**
178
- * Explicitly packages subject data for a study/transfer flow.
179
- * Normal clinical ingestion is projected into the tenant research index by GW.
181
+ * Legacy direct Composition transfer hook.
182
+ *
183
+ * @deprecated Canonical twins are created only by GW from current subject
184
+ * data after `setDigitalTwinSecondaryUseConsent(..., { decision: 'permit' })`.
185
+ * The Node runtime intentionally does not publish canonical Compositions.
180
186
  */
181
187
  generateDigitalTwinFromSubjectData(ctx, input) {
182
188
  assertFacadeCapability(this.capabilities, ActorCapabilities.IndividualGenerateDigitalTwin, ActorKinds.IndividualController, 'generateDigitalTwinFromSubjectData');
183
189
  return requireClientMethod(this.client, 'generateDigitalTwinFromSubjectData')(ctx, input);
184
190
  }
191
+ /**
192
+ * Enables or disables the subject's secondary-use digital-twin projection.
193
+ * This is the canonical patient-side operation; application code must not
194
+ * publish a canonical Composition directly into the research index.
195
+ */
196
+ setDigitalTwinSecondaryUseConsent(ctx, input) {
197
+ assertFacadeCapability(this.capabilities, ActorCapabilities.IndividualGenerateDigitalTwin, ActorKinds.IndividualController, 'setDigitalTwinSecondaryUseConsent');
198
+ return requireClientMethod(this.client, 'setDigitalTwinSecondaryUseConsent')(ctx, input);
199
+ }
200
+ /**
201
+ * Offboards the subject from the current index provider. This removes only
202
+ * the private subject/twin correspondence; it never deletes anonymous twin
203
+ * data already shared for research.
204
+ */
205
+ purgeDigitalTwinSubjectLink(ctx, input) {
206
+ assertFacadeCapability(this.capabilities, ActorCapabilities.IndividualGenerateDigitalTwin, ActorKinds.IndividualController, 'purgeDigitalTwinSubjectLink');
207
+ return requireClientMethod(this.client, 'purgeDigitalTwinSubjectLink')(ctx, input);
208
+ }
209
+ /** Returns whether the subject currently permits projection by its index provider. */
210
+ async getDigitalTwinSecondaryUseConsentStatus(ctx, input) {
211
+ assertFacadeCapability(this.capabilities, ActorCapabilities.IndividualGenerateDigitalTwin, ActorKinds.IndividualController, 'getDigitalTwinSecondaryUseConsentStatus');
212
+ const activeConsents = await new GatewayActiveConsentProvider(this.client, ctx)
213
+ .getActiveConsentsForSubject(input.subjectDid);
214
+ const consentIdentifier = String(input.consentIdentifier || '').trim();
215
+ if (!consentIdentifier)
216
+ throw new Error('consentIdentifier is required to distinguish the portal rule from study consents.');
217
+ const indexProviderOrganizationDid = String(input.indexProviderOrganizationDid || '').trim();
218
+ if (!indexProviderOrganizationDid)
219
+ throw new Error('indexProviderOrganizationDid is required.');
220
+ const consent = activeConsents.find((rule) => {
221
+ const actions = String(rule[ClaimConsent.action] || '').split(',').map((value) => value.trim());
222
+ return String(rule[ClaimConsent.identifier] || '').trim() === consentIdentifier
223
+ && String(rule[ClaimConsent.actorIdentifier] || '').trim() === indexProviderOrganizationDid
224
+ && String(rule[ClaimConsent.purpose] || '').trim().toUpperCase() === String(HealthcareConsentPurposes.Research).toUpperCase()
225
+ && actions.includes(ServiceCapability.DigitalTwinReader);
226
+ });
227
+ return consent
228
+ ? { exists: true, enabled: String(consent[ClaimConsent.decision] || '').trim().toLowerCase() === 'permit', consent }
229
+ : { exists: false, enabled: false };
230
+ }
185
231
  /**
186
232
  * Searches indexed clinical bundles for the current subject/controller context.
187
233
  */
@@ -107,17 +107,18 @@ export declare class OrganizationControllerSdk {
107
107
  listLicenseOrders(ctx: RouteContext, input?: LicenseOrderRuntimeSearchInput): Promise<SubmitAndPollResult>;
108
108
  /**
109
109
  * Confirms an already paid organization-side license order so GW CORE can
110
- * activate additional tenant seats once the public route exists.
110
+ * activate additional tenant seats.
111
111
  *
112
112
  * The commercial/payment step happens outside GW CORE. This method models
113
113
  * the follow-up confirmation that should materialize new seats from the
114
114
  * accepted order.
115
115
  *
116
- * Current runtime note:
117
- * - search/list of organization license offers and orders already exists
118
- * - the public/write post-payment seat-activation route is not converged yet
119
- * - current runtime clients therefore throw an explicit unsupported-flow
120
- * error instead of fabricating an unstable payload contract
116
+ * Security/routing contract:
117
+ * - the request is routed through the host `Order/_batch` endpoint
118
+ * - the signed `iss` is still the exact controller DID registered by DCR for
119
+ * `ctx.tenantId`; its `kid`/`skid` keys belong to that tenant, not the host
120
+ * - the configured runtime transport profile applies unchanged, including
121
+ * DIDComm encryption in protected staging/production runtimes
121
122
  */
122
123
  confirmOrganizationLicenseOrder(ctx: RouteContext, input: OrganizationLicenseOrderConfirmInput, pollOptions?: PollOptions): Promise<SubmitAndPollResult>;
123
124
  /**
@@ -133,11 +134,23 @@ export declare class OrganizationControllerSdk {
133
134
  * employees or individual/member descendants remain.
134
135
  */
135
136
  disableTenant(hostCtx: HostRouteContext, input: HostedTenantLifecycleInput, pollOptions?: PollOptions): Promise<SubmitAndPollResult>;
137
+ /** Re-enables the same suspended tenant and immutable legal identifier. */
138
+ enableTenant(hostCtx: HostRouteContext, input: HostedTenantLifecycleInput, pollOptions?: PollOptions): Promise<SubmitAndPollResult>;
136
139
  /**
137
140
  * Purges the hosted tenant through the host registry after tenant disable and
138
141
  * descendant purges have both completed.
139
142
  */
140
143
  purgeTenant(hostCtx: HostRouteContext, input: HostedTenantLifecycleInput, pollOptions?: PollOptions): Promise<SubmitAndPollResult>;
144
+ /** Returns host-confirmed tenant state and descendant counters. */
145
+ getTenantLifecycleStatus(hostCtx: HostRouteContext, input: HostedTenantLifecycleInput, pollOptions?: PollOptions): Promise<SubmitAndPollResult>;
146
+ /** Explicitly disables individuals before tenant disable. Employees use their dedicated lifecycle. */
147
+ disableTenantDescendants(hostCtx: HostRouteContext, input: HostedTenantLifecycleInput & {
148
+ descendantKind: 'individuals';
149
+ }, pollOptions?: PollOptions): Promise<SubmitAndPollResult>;
150
+ /** Explicitly purges individuals before tenant purge. Employees use their dedicated lifecycle. */
151
+ purgeTenantDescendants(hostCtx: HostRouteContext, input: HostedTenantLifecycleInput & {
152
+ descendantKind: 'individuals';
153
+ }, pollOptions?: PollOptions): Promise<SubmitAndPollResult>;
141
154
  /**
142
155
  * Activates the employee device from a previously issued activation request.
143
156
  */
@@ -147,17 +147,18 @@ export class OrganizationControllerSdk {
147
147
  }
148
148
  /**
149
149
  * Confirms an already paid organization-side license order so GW CORE can
150
- * activate additional tenant seats once the public route exists.
150
+ * activate additional tenant seats.
151
151
  *
152
152
  * The commercial/payment step happens outside GW CORE. This method models
153
153
  * the follow-up confirmation that should materialize new seats from the
154
154
  * accepted order.
155
155
  *
156
- * Current runtime note:
157
- * - search/list of organization license offers and orders already exists
158
- * - the public/write post-payment seat-activation route is not converged yet
159
- * - current runtime clients therefore throw an explicit unsupported-flow
160
- * error instead of fabricating an unstable payload contract
156
+ * Security/routing contract:
157
+ * - the request is routed through the host `Order/_batch` endpoint
158
+ * - the signed `iss` is still the exact controller DID registered by DCR for
159
+ * `ctx.tenantId`; its `kid`/`skid` keys belong to that tenant, not the host
160
+ * - the configured runtime transport profile applies unchanged, including
161
+ * DIDComm encryption in protected staging/production runtimes
161
162
  */
162
163
  confirmOrganizationLicenseOrder(ctx, input, pollOptions) {
163
164
  return requireClientMethod(this.client, 'confirmOrganizationLicenseOrder')(ctx, input, pollOptions);
@@ -184,6 +185,11 @@ export class OrganizationControllerSdk {
184
185
  assertFacadeCapability(this.capabilities, ActorCapabilities.OrganizationDisableTenant, ActorKinds.OrganizationController, 'disableTenant');
185
186
  return requireClientMethod(this.client, 'disableTenant')(hostCtx, input, pollOptions);
186
187
  }
188
+ /** Re-enables the same suspended tenant and immutable legal identifier. */
189
+ enableTenant(hostCtx, input, pollOptions) {
190
+ assertFacadeCapability(this.capabilities, ActorCapabilities.OrganizationDisableTenant, ActorKinds.OrganizationController, 'enableTenant');
191
+ return requireClientMethod(this.client, 'enableTenant')(hostCtx, input, pollOptions);
192
+ }
187
193
  /**
188
194
  * Purges the hosted tenant through the host registry after tenant disable and
189
195
  * descendant purges have both completed.
@@ -192,6 +198,21 @@ export class OrganizationControllerSdk {
192
198
  assertFacadeCapability(this.capabilities, ActorCapabilities.OrganizationPurgeTenant, ActorKinds.OrganizationController, 'purgeTenant');
193
199
  return requireClientMethod(this.client, 'purgeTenant')(hostCtx, input, pollOptions);
194
200
  }
201
+ /** Returns host-confirmed tenant state and descendant counters. */
202
+ getTenantLifecycleStatus(hostCtx, input, pollOptions) {
203
+ assertFacadeCapability(this.capabilities, ActorCapabilities.OrganizationDisableTenant, ActorKinds.OrganizationController, 'getTenantLifecycleStatus');
204
+ return requireClientMethod(this.client, 'getTenantLifecycleStatus')(hostCtx, input, pollOptions);
205
+ }
206
+ /** Explicitly disables individuals before tenant disable. Employees use their dedicated lifecycle. */
207
+ disableTenantDescendants(hostCtx, input, pollOptions) {
208
+ assertFacadeCapability(this.capabilities, ActorCapabilities.OrganizationDisableTenant, ActorKinds.OrganizationController, 'disableTenantDescendants');
209
+ return requireClientMethod(this.client, 'disableTenantDescendants')(hostCtx, input, pollOptions);
210
+ }
211
+ /** Explicitly purges individuals before tenant purge. Employees use their dedicated lifecycle. */
212
+ purgeTenantDescendants(hostCtx, input, pollOptions) {
213
+ assertFacadeCapability(this.capabilities, ActorCapabilities.OrganizationPurgeTenant, ActorKinds.OrganizationController, 'purgeTenantDescendants');
214
+ return requireClientMethod(this.client, 'purgeTenantDescendants')(hostCtx, input, pollOptions);
215
+ }
195
216
  /**
196
217
  * Activates the employee device from a previously issued activation request.
197
218
  */
@@ -1,13 +1,15 @@
1
1
  import type { NodeLegalOrganizationVerificationTransactionInput, PollOptions, SubmitAndPollResult } from './orchestration/client-port.js';
2
2
  import type { HostRouteContext } from './host-onboarding.js';
3
3
  import type { RouteContext } from './individual-onboarding.js';
4
- import type { EmployeeDeviceActivationResult } from './device-activation.js';
4
+ import type { EmployeeDeviceActivationResult, ProfileDeviceRegistrationInput } from './device-activation.js';
5
5
  export type OrganizationControllerRecoveryInput = {
6
6
  credentialReissuanceInput?: NodeLegalOrganizationVerificationTransactionInput;
7
7
  /** @deprecated Use `credentialReissuanceInput`. */
8
8
  issueInput?: NodeLegalOrganizationVerificationTransactionInput;
9
9
  controllerIdToken: string;
10
- dcrPayload: Record<string, unknown>;
10
+ deviceRegistration?: ProfileDeviceRegistrationInput;
11
+ /** @deprecated Use a profile-device draft and pass its `deviceRegistration`. */
12
+ dcrPayload?: Record<string, unknown>;
11
13
  credentialReissuancePollOptions?: PollOptions;
12
14
  /** @deprecated Use `credentialReissuancePollOptions`. */
13
15
  issuePollOptions?: PollOptions;
@@ -1,5 +1,5 @@
1
1
  // Copyright 2026 Antifraud Services Inc. under the Apache License, Version 2.0.
2
- import { activateEmployeeDeviceWithActivationCodeWithDeps } from './device-activation.js';
2
+ import { activateEmployeeDeviceWithActivationCodeWithDeps, activateEmployeeDeviceWithActivationRequestWithDeps, } from './device-activation.js';
3
3
  export async function recoverOrganizationControllerWithCredentialReissuanceWithDeps(deps) {
4
4
  /**
5
5
  * Existing-tenant controller recovery contract:
@@ -24,19 +24,25 @@ export async function recoverOrganizationControllerWithCredentialReissuanceWithD
24
24
  if (!activationCode) {
25
25
  throw new Error('recoverOrganizationControllerWithCredentialReissuance: missing org.schema.IndividualProduct.serialNumber in Organization/_issue response.');
26
26
  }
27
- const activation = await activateEmployeeDeviceWithActivationCodeWithDeps({
27
+ const activation = await activateEmployeeDeviceWithActivationRequestWithDeps({
28
28
  routeCtx: deps.tenantCtx,
29
29
  input: {
30
30
  activationCode,
31
31
  idToken: deps.input.controllerIdToken,
32
+ deviceRegistration: deps.input.deviceRegistration,
32
33
  dcrPayload: deps.input.dcrPayload,
33
- pollOptions: deps.input.activationPollOptions,
34
34
  },
35
- identityTokenExchangePath: deps.identityTokenExchangePath,
36
- identityTokenExchangePollPath: deps.identityTokenExchangePollPath,
37
- identityDeviceDcrPath: deps.identityDeviceDcrPath,
38
- identityDeviceDcrPollPath: deps.identityDeviceDcrPollPath,
39
- submitAndPollWithBearerToken: deps.submitAndPollWithBearerToken,
35
+ defaultTimeoutMs: deps.input.activationPollOptions?.timeoutMs,
36
+ defaultIntervalMs: deps.input.activationPollOptions?.intervalMs,
37
+ activateEmployeeDeviceWithActivationCode: (routeCtx, input) => activateEmployeeDeviceWithActivationCodeWithDeps({
38
+ routeCtx,
39
+ input,
40
+ identityTokenExchangePath: deps.identityTokenExchangePath,
41
+ identityTokenExchangePollPath: deps.identityTokenExchangePollPath,
42
+ identityDeviceDcrPath: deps.identityDeviceDcrPath,
43
+ identityDeviceDcrPollPath: deps.identityDeviceDcrPollPath,
44
+ submitAndPollWithBearerToken: deps.submitAndPollWithBearerToken,
45
+ }),
40
46
  });
41
47
  return {
42
48
  credentialReissuance,
@@ -21,6 +21,8 @@ export type OrganizationEmployeeProvisioningResult = Readonly<{
21
21
  license: SubmitAndPollResult;
22
22
  licenseOrder?: SubmitAndPollResult;
23
23
  activationCode: string;
24
+ /** Effective installation allowance returned by the reserved GW seat. */
25
+ maxDevices?: number;
24
26
  }>;
25
27
  /** Shared dependencies for the create plus seat issuance workflow. */
26
28
  export type OrganizationEmployeeProvisioningDeps = Readonly<{
@@ -34,6 +36,8 @@ export type OrganizationEmployeeProvisioningDeps = Readonly<{
34
36
  * returns the exact GW activation credential.
35
37
  */
36
38
  export declare function provisionOrganizationEmployeeWithDeps(routeContext: RouteContext, input: OrganizationEmployeeProvisioningInput, deps: OrganizationEmployeeProvisioningDeps): Promise<OrganizationEmployeeProvisioningResult>;
39
+ /** Reads the effective positive installation allowance returned by `License/_issue`. */
40
+ export declare function readEmployeeLicenseMaxDevices(value: unknown): number | undefined;
37
41
  /**
38
42
  * Reads the pending seat Offer from an employee-create response.
39
43
  *
@@ -35,7 +35,23 @@ export async function provisionOrganizationEmployeeWithDeps(routeContext, input,
35
35
  if (!activationCode) {
36
36
  throw new Error('provisionOrganizationEmployee: GW response did not contain an employee activation credential.');
37
37
  }
38
- return { employee, license, licenseOrder, activationCode };
38
+ const maxDevices = readEmployeeLicenseMaxDevices(license.poll.body);
39
+ return {
40
+ employee,
41
+ license,
42
+ licenseOrder,
43
+ activationCode,
44
+ ...(maxDevices ? { maxDevices } : {}),
45
+ };
46
+ }
47
+ /** Reads the effective positive installation allowance returned by `License/_issue`. */
48
+ export function readEmployeeLicenseMaxDevices(value) {
49
+ for (const candidate of nestedRecords(value)) {
50
+ const configured = Number(record(candidate.meta)?.maxDevices ?? candidate.maxDevices);
51
+ if (Number.isInteger(configured) && configured > 0)
52
+ return configured;
53
+ }
54
+ return undefined;
39
55
  }
40
56
  /**
41
57
  * Reads the pending seat Offer from an employee-create response.
@@ -14,11 +14,16 @@ import type { RouteContext } from './individual-onboarding.js';
14
14
  * Transport note:
15
15
  * - current GW CORE exposes this confirmation step through the host
16
16
  * `registry/org.schema/Order/_batch` route
17
- * - the organization controller still reasons in tenant context, so the
18
- * runtime adapts that higher-level intent onto the current host route
17
+ * - that host route owns commercial routing and persistence only; it does not
18
+ * change the security principal or move controller keys into the host vault
19
+ * - `issuerDid` must be the exact controller DID registered through DCR for the
20
+ * tenant in `routeCtx`; signed/encrypted transports therefore emit its
21
+ * registered `kid`/`skid`, and GW resolves those public keys from that tenant
22
+ * - `aud` remains the tenant id so GW cannot confuse a host-routed Order with a
23
+ * host-authored controller request
19
24
  */
20
25
  export type OrganizationLicenseOrderConfirmInput = Readonly<{
21
- /** Exact DCR-registered controller DID that signs the protected request. */
26
+ /** Exact tenant DCR-registered controller DID used as `iss` and key owner. */
22
27
  issuerDid: string;
23
28
  offerId: string;
24
29
  hostNetwork?: string;