@forg3t/sdk 0.1.4 → 0.1.6

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/README.md CHANGED
@@ -33,7 +33,26 @@ There is now also a live bearer-token bootstrap path for first-time tenant creat
33
33
  - public hostname: `https://api.forg3t.io`
34
34
  - auth mode: dashboard bearer token, not project API key
35
35
 
36
- This closes the biggest admin-led onboarding gap, but the published npm package and official customer docs still need to catch up before this can be called fully self-serve.
36
+ This closes the biggest admin-led onboarding gap. The published npm package now includes the bootstrap flow and request-creation surface, but the overall product is still not fully self-serve enterprise onboarding because tenant lifecycle cleanup, whitebox activation, and some runtime specialization paths remain operator-led.
37
+
38
+ ## Execution Modes
39
+
40
+ Forg3t now has three materially different execution lanes. Keeping them separate is important:
41
+
42
+ 1. **Managed black-box lane**
43
+ - default baseline path for API-only and retrieval-style first runs
44
+ - request enters via `https://api.forg3t.io`
45
+ - control-plane managed worker claims the job
46
+ - a private executor service runs the black-box workload
47
+ - this is the path verified by the published SDK cleanroom smoke
48
+
49
+ 2. **Customer-scoped generic worker**
50
+ - used when a customer wants execution tied to their own project-scoped worker runtime
51
+ - not the same thing as the default managed black-box lane
52
+
53
+ 3. **Customer-side whitebox worker**
54
+ - required when the customer wants staged model-weight intervention
55
+ - this remains a separate install and acceptance path
37
56
 
38
57
  ## Local Development
39
58
  1. **Build**: `npm run build`
@@ -43,66 +62,45 @@ This closes the biggest admin-led onboarding gap, but the published npm package
43
62
 
44
63
  ### Customer Onboarding Smoke
45
64
 
46
- Use this as the first real integration gate for a provisioned customer project.
65
+ Use this as the first real integration gate for a new signed-in customer session.
47
66
  The production API hostname is:
48
67
 
49
68
  ```bash
50
69
  export FORG3T_API_URL=https://api.forg3t.io
51
- export FORG3T_API_KEY=fg_...
52
- export FORG3T_PROJECT_ID=... # optional if default project exists
70
+ export FORG3T_BEARER_TOKEN=eyJ...
53
71
  ```
54
72
 
55
- Then run a clean smoke script from your own workspace:
56
-
57
- ```typescript
58
- import { Forg3tClient } from '@forg3t/sdk';
59
-
60
- const client = new Forg3tClient({
61
- apiUrl: process.env.FORG3T_API_URL,
62
- apiKey: process.env.FORG3T_API_KEY,
63
- timeoutMs: 30000,
64
- });
65
-
66
- async function main() {
67
- const me = await client.getCurrentUser();
68
- const projectId = process.env.FORG3T_PROJECT_ID || me.defaultProjectId;
69
-
70
- if (!projectId) {
71
- throw new Error('Set FORG3T_PROJECT_ID or configure a default project first.');
72
- }
73
-
74
- const request = await client.createUnlearningRequest(projectId, {
75
- target: { type: 'phrase', value: 'customer-onboarding-smoke' },
76
- scope: {},
77
- accessLevel: 'layer_a_only',
78
- execution: {
79
- target: {
80
- provider: 'custom',
81
- endpoint: 'https://example.com/inference'
82
- }
83
- }
84
- });
85
-
86
- console.log({
87
- requestId: request.id,
88
- jobId: request.job?.id,
89
- jobType: request.job?.type,
90
- jobStatus: request.job?.status
91
- });
92
- }
73
+ Then run the official onboarding smoke:
93
74
 
94
- main().catch((error) => {
95
- console.error(error);
96
- process.exit(1);
97
- });
75
+ ```bash
76
+ npm run smoke:customer-onboarding
98
77
  ```
99
78
 
100
79
  This validates:
101
- - API authentication
80
+ - bearer-token bootstrap
102
81
  - project resolution
82
+ - API key creation
103
83
  - unlearning request creation
84
+ - request detail fetch
85
+ - terminal job success
86
+ - evidence readiness
87
+ - artifact download readiness
88
+ - API key revoke
104
89
  - job contract correctness
105
- - job lifecycle visibility
90
+ - first-customer path through the public API hostname
91
+
92
+ ### Cleanroom Release Smoke
93
+
94
+ For release verification from a clean temporary install:
95
+
96
+ ```bash
97
+ export FORG3T_ONBOARDING_SMOKE_EMAIL=onboarding-smoke@forg3t.io
98
+ export FORG3T_ONBOARDING_SMOKE_PASSWORD=...
99
+ export SUPABASE_ANON_KEY=...
100
+ npm run smoke:cleanroom
101
+ ```
102
+
103
+ This installs the package into a fresh temporary directory, runs bootstrap, creates a first request, verifies terminal job success plus evidence/artifact readiness, and revokes the temporary API key. It is the release-candidate gate we use before claiming the SDK package is ready to publish.
106
104
 
107
105
  ### First-Time Tenant Bootstrap
108
106
 
@@ -145,14 +143,31 @@ Notes:
145
143
  - Repeating bootstrap with the same bearer token is idempotent and reuses the tenant resources.
146
144
  - This flow is live on the control plane today.
147
145
 
146
+ ### First-Time Bootstrap + First Request
147
+
148
+ If you want the full SDK example in one run:
149
+
150
+ ```bash
151
+ export FORG3T_API_URL=https://api.forg3t.io
152
+ export FORG3T_BEARER_TOKEN=eyJ...
153
+ npm run example:bootstrap
154
+ ```
155
+
156
+ This example:
157
+
158
+ 1. bootstraps the tenant
159
+ 2. creates a temporary project API key
160
+ 3. submits the first request
161
+ 4. fetches request detail
162
+ 5. revokes the temporary API key
163
+
148
164
  ### Repo Maintainer Smoke
149
165
 
150
166
  If you are working inside the Forg3t SDK repo itself, you can also run the bundled smoke script:
151
167
 
152
168
  ```bash
153
169
  export FORG3T_API_URL=https://api.forg3t.io
154
- export FORG3T_API_KEY=fg_...
155
- export FORG3T_PROJECT_ID=...
170
+ export FORG3T_BEARER_TOKEN=eyJ...
156
171
  npm run smoke:customer-onboarding
157
172
  ```
158
173
 
package/dist/index.d.mts CHANGED
@@ -82,7 +82,12 @@ interface CreateApiKeyRequest {
82
82
  expiresAt?: string;
83
83
  }
84
84
  interface CreateApiKeyResponse extends ApiKey {
85
- plaintextKey: string;
85
+ key: string;
86
+ plaintextKey?: string;
87
+ }
88
+ interface RevokeApiKeyResponse {
89
+ status: 'revoked' | string;
90
+ revokedAt?: string | null;
86
91
  }
87
92
  interface AuditEvent {
88
93
  id: string;
@@ -264,6 +269,8 @@ declare enum IntegrationType {
264
269
  LANGCHAIN = "langchain",
265
270
  OPENROUTER = "openrouter",
266
271
  OPENAI = "openai",
272
+ PGVECTOR = "pgvector",
273
+ ADAPTER = "adapter",
267
274
  CUSTOM = "custom"
268
275
  }
269
276
  declare enum IntegrationMode {
@@ -285,6 +292,29 @@ interface OpenRouterAdapterConfig extends BaseAdapterConfig {
285
292
  routingStrategy: 'cost' | 'latency' | 'quality';
286
293
  fallbackModels?: string[];
287
294
  }
295
+ interface PgVectorIntegrationConfig extends BaseAdapterConfig {
296
+ backend: 'pgvector_postgres';
297
+ schema?: string;
298
+ table: string;
299
+ document_id_column?: string;
300
+ metadata_column?: string;
301
+ metadata_document_id_key?: string;
302
+ content_column?: string;
303
+ embedding_column?: string;
304
+ database_url_env_var?: string;
305
+ healthcheck_url?: string;
306
+ healthcheck_method?: 'GET' | 'POST';
307
+ worker_mode?: 'rag';
308
+ }
309
+ interface AdapterIntegrationConfig extends BaseAdapterConfig {
310
+ backend: 'peft_lora_safetensors';
311
+ format?: 'safetensors';
312
+ adapter_uri?: string;
313
+ adapter_uri_env_var?: string;
314
+ healthcheck_url?: string;
315
+ healthcheck_method?: 'GET' | 'POST';
316
+ worker_mode?: 'adapter';
317
+ }
288
318
  interface Integration {
289
319
  id: string;
290
320
  projectId: string;
@@ -292,7 +322,7 @@ interface Integration {
292
322
  name: string;
293
323
  description?: string;
294
324
  status: 'active' | 'disconnected' | 'error' | 'pending';
295
- config: BaseAdapterConfig | LangChainAdapterConfig | OpenRouterAdapterConfig | Record<string, unknown>;
325
+ config: BaseAdapterConfig | LangChainAdapterConfig | OpenRouterAdapterConfig | PgVectorIntegrationConfig | AdapterIntegrationConfig | Record<string, unknown>;
296
326
  capabilities: string[];
297
327
  createdAt: string;
298
328
  updatedAt: string;
@@ -348,9 +378,29 @@ interface UnlearningExecutionTargetSpec {
348
378
  responsePath?: string;
349
379
  apiKey?: string;
350
380
  }
381
+ interface UnlearningExecutionRagSpec {
382
+ backend: 'pgvector_postgres';
383
+ mode?: 'delete_documents';
384
+ integrationId?: string;
385
+ dryRun?: boolean;
386
+ }
387
+ interface UnlearningExecutionAdapterSpec {
388
+ backend: 'peft_lora_safetensors';
389
+ integrationId?: string;
390
+ method?: 'disable_adapter' | 'attenuate_adapter';
391
+ scaleFactor?: number;
392
+ dryRun?: boolean;
393
+ updatedAdapterUri?: string;
394
+ }
395
+ interface UnlearningExecutionRoutingSpec {
396
+ lane?: 'managed' | 'project_scoped';
397
+ }
351
398
  interface UnlearningExecutionSpec {
352
399
  target?: UnlearningExecutionTargetSpec;
400
+ rag?: UnlearningExecutionRagSpec;
401
+ adapter?: UnlearningExecutionAdapterSpec;
353
402
  model?: UnlearningExecutionModelRef;
403
+ routing?: UnlearningExecutionRoutingSpec;
354
404
  plan?: UnlearningExecutionPlanSpec;
355
405
  }
356
406
  interface UnlearningJobSummary {
@@ -412,8 +462,8 @@ declare class Forg3tClient {
412
462
  getUnlearningRequest(projectId: string, unlearningRequestId: string, requestId?: string): Promise<UnlearningRequest>;
413
463
  listApiKeys(projectId: string, requestId?: string): Promise<ApiKey[]>;
414
464
  createApiKey(projectId: string, data: CreateApiKeyRequest, requestId?: string): Promise<CreateApiKeyResponse>;
415
- rotateApiKey(keyId: string, requestId?: string): Promise<CreateApiKeyResponse>;
416
- revokeApiKey(keyId: string, requestId?: string): Promise<ApiKey>;
465
+ rotateApiKey(projectId: string, keyId: string, requestId?: string): Promise<CreateApiKeyResponse>;
466
+ revokeApiKey(projectId: string, keyId: string, requestId?: string): Promise<RevokeApiKeyResponse>;
417
467
  submitJob(projectId: string, data: SubmitJobRequest, requestId?: string): Promise<Job>;
418
468
  /**
419
469
  * Creates a new job with deduplication support via an optional idempotency key.
@@ -475,6 +525,7 @@ declare class Forg3tClient {
475
525
  }, requestId?: string): Promise<void>;
476
526
  private assertCreateUnlearningRequest;
477
527
  private assertModelUnlearnPreflight;
528
+ private assertAdapterUnlearnPreflight;
478
529
  }
479
530
 
480
531
  declare class Forg3tError extends Error {
@@ -754,4 +805,4 @@ declare const ENTERPRISE_STRICT_QUALITY_GATE_POLICY: Required<WhiteboxQualityGat
754
805
  declare function assertDeploymentCompatibility(options: DeploymentCompatibilityOptions): void;
755
806
  declare function buildWhiteboxWorkerOptions(profile: Forg3tDeploymentProfile, baseOptions: WhiteboxWorkerBaseOptions): WhiteboxWorkerOptions;
756
807
 
757
- export { AccessLevel, type AdapterCommandSpec, type ApiKey, type AuditEvent, type AuditEventFilters, type AuditEventListResponse, type BaseAdapterConfig, type BootstrapTenantRequest, type BootstrapTenantResponse, type BuildModelUnlearningJobInput, type ClaimAssertion, type ClaimJobsRequest, type ClaimScope, type ClaimType, type CreateApiKeyRequest, type CreateApiKeyResponse, type CreateIntegrationRequest, type CreateUnlearningRequestParams, type CreateWebhookRequest, type CurrentUserResponse, DEFAULT_WHITEBOX_QUALITY_GATE_POLICY, type DeploymentCompatibilityOptions, ENTERPRISE_STRICT_QUALITY_GATE_POLICY, type EvidenceArtifact, type EvidencePdfResponse, type EvidenceStatusResponse, type ExecutionPlan, type ExecutionStep, type Forg3tAdapterMode, Forg3tApiConnectionError, Forg3tAuthenticationError, Forg3tClient, type Forg3tClientOptions, type Forg3tDeploymentProfile, Forg3tError, Forg3tNotFoundError, Forg3tNotImplementedError, Forg3tRateLimitError, HttpTrainingAdapter, type HttpTrainingAdapterOptions, type Integration, IntegrationMode, IntegrationType, type Job, type JobConfig, type JobError, type JobMutationOptions, type JobResult, type JobStatus, type JobType, type LangChainAdapterConfig, type LocalCommandAdapterOptions, LocalCommandTrainingAdapter, type ModelArtifactReference, type NativeAdapterConfig, type OpenRouterAdapterConfig, type PaginationParams, type Project, type SubmitJobRequest, type TrainingBackendAdapter, type UnlearningClaim, type UnlearningExecutionModelRef, type UnlearningExecutionPlanSpec, type UnlearningExecutionSpec, type UnlearningExecutionTargetSpec, type UnlearningJobSummary, type UnlearningRequest, type UnlearningScope, type UnlearningTarget, type UpdateWebhookRequest, WHITEBOX_QUALITY_GATE_PRESETS, WHITEBOX_QUALITY_GATE_PRESET_EXPLANATIONS, type Webhook, type WebhookDelivery, type WebhookDeliveryFilters, type WhiteboxEvaluationInput, type WhiteboxEvaluationOutput, type WhiteboxJobContext, type WhiteboxQualityGateDecision, type WhiteboxQualityGatePolicy, type WhiteboxQualityGatePreset, type WhiteboxTargetSpec, type WhiteboxUnlearningInput, type WhiteboxUnlearningOutput, type WhiteboxUnlearningPlan, WhiteboxWorker, type WhiteboxWorkerBaseOptions, type WhiteboxWorkerLogger, type WhiteboxWorkerOptions, assertDeploymentCompatibility, buildModelUnlearningPayload, buildWhiteboxWorkerOptions, createNativeTrainingAdapter, evaluateWhiteboxQualityGate, resolveWhiteboxQualityGatePolicy };
808
+ export { AccessLevel, type AdapterCommandSpec, type AdapterIntegrationConfig, type ApiKey, type AuditEvent, type AuditEventFilters, type AuditEventListResponse, type BaseAdapterConfig, type BootstrapTenantRequest, type BootstrapTenantResponse, type BuildModelUnlearningJobInput, type ClaimAssertion, type ClaimJobsRequest, type ClaimScope, type ClaimType, type CreateApiKeyRequest, type CreateApiKeyResponse, type CreateIntegrationRequest, type CreateUnlearningRequestParams, type CreateWebhookRequest, type CurrentUserResponse, DEFAULT_WHITEBOX_QUALITY_GATE_POLICY, type DeploymentCompatibilityOptions, ENTERPRISE_STRICT_QUALITY_GATE_POLICY, type EvidenceArtifact, type EvidencePdfResponse, type EvidenceStatusResponse, type ExecutionPlan, type ExecutionStep, type Forg3tAdapterMode, Forg3tApiConnectionError, Forg3tAuthenticationError, Forg3tClient, type Forg3tClientOptions, type Forg3tDeploymentProfile, Forg3tError, Forg3tNotFoundError, Forg3tNotImplementedError, Forg3tRateLimitError, HttpTrainingAdapter, type HttpTrainingAdapterOptions, type Integration, IntegrationMode, IntegrationType, type Job, type JobConfig, type JobError, type JobMutationOptions, type JobResult, type JobStatus, type JobType, type LangChainAdapterConfig, type LocalCommandAdapterOptions, LocalCommandTrainingAdapter, type ModelArtifactReference, type NativeAdapterConfig, type OpenRouterAdapterConfig, type PaginationParams, type PgVectorIntegrationConfig, type Project, type RevokeApiKeyResponse, type SubmitJobRequest, type TrainingBackendAdapter, type UnlearningClaim, type UnlearningExecutionAdapterSpec, type UnlearningExecutionModelRef, type UnlearningExecutionPlanSpec, type UnlearningExecutionRagSpec, type UnlearningExecutionRoutingSpec, type UnlearningExecutionSpec, type UnlearningExecutionTargetSpec, type UnlearningJobSummary, type UnlearningRequest, type UnlearningScope, type UnlearningTarget, type UpdateWebhookRequest, WHITEBOX_QUALITY_GATE_PRESETS, WHITEBOX_QUALITY_GATE_PRESET_EXPLANATIONS, type Webhook, type WebhookDelivery, type WebhookDeliveryFilters, type WhiteboxEvaluationInput, type WhiteboxEvaluationOutput, type WhiteboxJobContext, type WhiteboxQualityGateDecision, type WhiteboxQualityGatePolicy, type WhiteboxQualityGatePreset, type WhiteboxTargetSpec, type WhiteboxUnlearningInput, type WhiteboxUnlearningOutput, type WhiteboxUnlearningPlan, WhiteboxWorker, type WhiteboxWorkerBaseOptions, type WhiteboxWorkerLogger, type WhiteboxWorkerOptions, assertDeploymentCompatibility, buildModelUnlearningPayload, buildWhiteboxWorkerOptions, createNativeTrainingAdapter, evaluateWhiteboxQualityGate, resolveWhiteboxQualityGatePolicy };
package/dist/index.d.ts CHANGED
@@ -82,7 +82,12 @@ interface CreateApiKeyRequest {
82
82
  expiresAt?: string;
83
83
  }
84
84
  interface CreateApiKeyResponse extends ApiKey {
85
- plaintextKey: string;
85
+ key: string;
86
+ plaintextKey?: string;
87
+ }
88
+ interface RevokeApiKeyResponse {
89
+ status: 'revoked' | string;
90
+ revokedAt?: string | null;
86
91
  }
87
92
  interface AuditEvent {
88
93
  id: string;
@@ -264,6 +269,8 @@ declare enum IntegrationType {
264
269
  LANGCHAIN = "langchain",
265
270
  OPENROUTER = "openrouter",
266
271
  OPENAI = "openai",
272
+ PGVECTOR = "pgvector",
273
+ ADAPTER = "adapter",
267
274
  CUSTOM = "custom"
268
275
  }
269
276
  declare enum IntegrationMode {
@@ -285,6 +292,29 @@ interface OpenRouterAdapterConfig extends BaseAdapterConfig {
285
292
  routingStrategy: 'cost' | 'latency' | 'quality';
286
293
  fallbackModels?: string[];
287
294
  }
295
+ interface PgVectorIntegrationConfig extends BaseAdapterConfig {
296
+ backend: 'pgvector_postgres';
297
+ schema?: string;
298
+ table: string;
299
+ document_id_column?: string;
300
+ metadata_column?: string;
301
+ metadata_document_id_key?: string;
302
+ content_column?: string;
303
+ embedding_column?: string;
304
+ database_url_env_var?: string;
305
+ healthcheck_url?: string;
306
+ healthcheck_method?: 'GET' | 'POST';
307
+ worker_mode?: 'rag';
308
+ }
309
+ interface AdapterIntegrationConfig extends BaseAdapterConfig {
310
+ backend: 'peft_lora_safetensors';
311
+ format?: 'safetensors';
312
+ adapter_uri?: string;
313
+ adapter_uri_env_var?: string;
314
+ healthcheck_url?: string;
315
+ healthcheck_method?: 'GET' | 'POST';
316
+ worker_mode?: 'adapter';
317
+ }
288
318
  interface Integration {
289
319
  id: string;
290
320
  projectId: string;
@@ -292,7 +322,7 @@ interface Integration {
292
322
  name: string;
293
323
  description?: string;
294
324
  status: 'active' | 'disconnected' | 'error' | 'pending';
295
- config: BaseAdapterConfig | LangChainAdapterConfig | OpenRouterAdapterConfig | Record<string, unknown>;
325
+ config: BaseAdapterConfig | LangChainAdapterConfig | OpenRouterAdapterConfig | PgVectorIntegrationConfig | AdapterIntegrationConfig | Record<string, unknown>;
296
326
  capabilities: string[];
297
327
  createdAt: string;
298
328
  updatedAt: string;
@@ -348,9 +378,29 @@ interface UnlearningExecutionTargetSpec {
348
378
  responsePath?: string;
349
379
  apiKey?: string;
350
380
  }
381
+ interface UnlearningExecutionRagSpec {
382
+ backend: 'pgvector_postgres';
383
+ mode?: 'delete_documents';
384
+ integrationId?: string;
385
+ dryRun?: boolean;
386
+ }
387
+ interface UnlearningExecutionAdapterSpec {
388
+ backend: 'peft_lora_safetensors';
389
+ integrationId?: string;
390
+ method?: 'disable_adapter' | 'attenuate_adapter';
391
+ scaleFactor?: number;
392
+ dryRun?: boolean;
393
+ updatedAdapterUri?: string;
394
+ }
395
+ interface UnlearningExecutionRoutingSpec {
396
+ lane?: 'managed' | 'project_scoped';
397
+ }
351
398
  interface UnlearningExecutionSpec {
352
399
  target?: UnlearningExecutionTargetSpec;
400
+ rag?: UnlearningExecutionRagSpec;
401
+ adapter?: UnlearningExecutionAdapterSpec;
353
402
  model?: UnlearningExecutionModelRef;
403
+ routing?: UnlearningExecutionRoutingSpec;
354
404
  plan?: UnlearningExecutionPlanSpec;
355
405
  }
356
406
  interface UnlearningJobSummary {
@@ -412,8 +462,8 @@ declare class Forg3tClient {
412
462
  getUnlearningRequest(projectId: string, unlearningRequestId: string, requestId?: string): Promise<UnlearningRequest>;
413
463
  listApiKeys(projectId: string, requestId?: string): Promise<ApiKey[]>;
414
464
  createApiKey(projectId: string, data: CreateApiKeyRequest, requestId?: string): Promise<CreateApiKeyResponse>;
415
- rotateApiKey(keyId: string, requestId?: string): Promise<CreateApiKeyResponse>;
416
- revokeApiKey(keyId: string, requestId?: string): Promise<ApiKey>;
465
+ rotateApiKey(projectId: string, keyId: string, requestId?: string): Promise<CreateApiKeyResponse>;
466
+ revokeApiKey(projectId: string, keyId: string, requestId?: string): Promise<RevokeApiKeyResponse>;
417
467
  submitJob(projectId: string, data: SubmitJobRequest, requestId?: string): Promise<Job>;
418
468
  /**
419
469
  * Creates a new job with deduplication support via an optional idempotency key.
@@ -475,6 +525,7 @@ declare class Forg3tClient {
475
525
  }, requestId?: string): Promise<void>;
476
526
  private assertCreateUnlearningRequest;
477
527
  private assertModelUnlearnPreflight;
528
+ private assertAdapterUnlearnPreflight;
478
529
  }
479
530
 
480
531
  declare class Forg3tError extends Error {
@@ -754,4 +805,4 @@ declare const ENTERPRISE_STRICT_QUALITY_GATE_POLICY: Required<WhiteboxQualityGat
754
805
  declare function assertDeploymentCompatibility(options: DeploymentCompatibilityOptions): void;
755
806
  declare function buildWhiteboxWorkerOptions(profile: Forg3tDeploymentProfile, baseOptions: WhiteboxWorkerBaseOptions): WhiteboxWorkerOptions;
756
807
 
757
- export { AccessLevel, type AdapterCommandSpec, type ApiKey, type AuditEvent, type AuditEventFilters, type AuditEventListResponse, type BaseAdapterConfig, type BootstrapTenantRequest, type BootstrapTenantResponse, type BuildModelUnlearningJobInput, type ClaimAssertion, type ClaimJobsRequest, type ClaimScope, type ClaimType, type CreateApiKeyRequest, type CreateApiKeyResponse, type CreateIntegrationRequest, type CreateUnlearningRequestParams, type CreateWebhookRequest, type CurrentUserResponse, DEFAULT_WHITEBOX_QUALITY_GATE_POLICY, type DeploymentCompatibilityOptions, ENTERPRISE_STRICT_QUALITY_GATE_POLICY, type EvidenceArtifact, type EvidencePdfResponse, type EvidenceStatusResponse, type ExecutionPlan, type ExecutionStep, type Forg3tAdapterMode, Forg3tApiConnectionError, Forg3tAuthenticationError, Forg3tClient, type Forg3tClientOptions, type Forg3tDeploymentProfile, Forg3tError, Forg3tNotFoundError, Forg3tNotImplementedError, Forg3tRateLimitError, HttpTrainingAdapter, type HttpTrainingAdapterOptions, type Integration, IntegrationMode, IntegrationType, type Job, type JobConfig, type JobError, type JobMutationOptions, type JobResult, type JobStatus, type JobType, type LangChainAdapterConfig, type LocalCommandAdapterOptions, LocalCommandTrainingAdapter, type ModelArtifactReference, type NativeAdapterConfig, type OpenRouterAdapterConfig, type PaginationParams, type Project, type SubmitJobRequest, type TrainingBackendAdapter, type UnlearningClaim, type UnlearningExecutionModelRef, type UnlearningExecutionPlanSpec, type UnlearningExecutionSpec, type UnlearningExecutionTargetSpec, type UnlearningJobSummary, type UnlearningRequest, type UnlearningScope, type UnlearningTarget, type UpdateWebhookRequest, WHITEBOX_QUALITY_GATE_PRESETS, WHITEBOX_QUALITY_GATE_PRESET_EXPLANATIONS, type Webhook, type WebhookDelivery, type WebhookDeliveryFilters, type WhiteboxEvaluationInput, type WhiteboxEvaluationOutput, type WhiteboxJobContext, type WhiteboxQualityGateDecision, type WhiteboxQualityGatePolicy, type WhiteboxQualityGatePreset, type WhiteboxTargetSpec, type WhiteboxUnlearningInput, type WhiteboxUnlearningOutput, type WhiteboxUnlearningPlan, WhiteboxWorker, type WhiteboxWorkerBaseOptions, type WhiteboxWorkerLogger, type WhiteboxWorkerOptions, assertDeploymentCompatibility, buildModelUnlearningPayload, buildWhiteboxWorkerOptions, createNativeTrainingAdapter, evaluateWhiteboxQualityGate, resolveWhiteboxQualityGatePolicy };
808
+ export { AccessLevel, type AdapterCommandSpec, type AdapterIntegrationConfig, type ApiKey, type AuditEvent, type AuditEventFilters, type AuditEventListResponse, type BaseAdapterConfig, type BootstrapTenantRequest, type BootstrapTenantResponse, type BuildModelUnlearningJobInput, type ClaimAssertion, type ClaimJobsRequest, type ClaimScope, type ClaimType, type CreateApiKeyRequest, type CreateApiKeyResponse, type CreateIntegrationRequest, type CreateUnlearningRequestParams, type CreateWebhookRequest, type CurrentUserResponse, DEFAULT_WHITEBOX_QUALITY_GATE_POLICY, type DeploymentCompatibilityOptions, ENTERPRISE_STRICT_QUALITY_GATE_POLICY, type EvidenceArtifact, type EvidencePdfResponse, type EvidenceStatusResponse, type ExecutionPlan, type ExecutionStep, type Forg3tAdapterMode, Forg3tApiConnectionError, Forg3tAuthenticationError, Forg3tClient, type Forg3tClientOptions, type Forg3tDeploymentProfile, Forg3tError, Forg3tNotFoundError, Forg3tNotImplementedError, Forg3tRateLimitError, HttpTrainingAdapter, type HttpTrainingAdapterOptions, type Integration, IntegrationMode, IntegrationType, type Job, type JobConfig, type JobError, type JobMutationOptions, type JobResult, type JobStatus, type JobType, type LangChainAdapterConfig, type LocalCommandAdapterOptions, LocalCommandTrainingAdapter, type ModelArtifactReference, type NativeAdapterConfig, type OpenRouterAdapterConfig, type PaginationParams, type PgVectorIntegrationConfig, type Project, type RevokeApiKeyResponse, type SubmitJobRequest, type TrainingBackendAdapter, type UnlearningClaim, type UnlearningExecutionAdapterSpec, type UnlearningExecutionModelRef, type UnlearningExecutionPlanSpec, type UnlearningExecutionRagSpec, type UnlearningExecutionRoutingSpec, type UnlearningExecutionSpec, type UnlearningExecutionTargetSpec, type UnlearningJobSummary, type UnlearningRequest, type UnlearningScope, type UnlearningTarget, type UpdateWebhookRequest, WHITEBOX_QUALITY_GATE_PRESETS, WHITEBOX_QUALITY_GATE_PRESET_EXPLANATIONS, type Webhook, type WebhookDelivery, type WebhookDeliveryFilters, type WhiteboxEvaluationInput, type WhiteboxEvaluationOutput, type WhiteboxJobContext, type WhiteboxQualityGateDecision, type WhiteboxQualityGatePolicy, type WhiteboxQualityGatePreset, type WhiteboxTargetSpec, type WhiteboxUnlearningInput, type WhiteboxUnlearningOutput, type WhiteboxUnlearningPlan, WhiteboxWorker, type WhiteboxWorkerBaseOptions, type WhiteboxWorkerLogger, type WhiteboxWorkerOptions, assertDeploymentCompatibility, buildModelUnlearningPayload, buildWhiteboxWorkerOptions, createNativeTrainingAdapter, evaluateWhiteboxQualityGate, resolveWhiteboxQualityGatePolicy };
package/dist/index.js CHANGED
@@ -132,10 +132,14 @@ var Transport = class {
132
132
  async request(path, options = {}, requestId) {
133
133
  const url = `${this.baseUrl}${path.startsWith("/") ? "" : "/"}${path}`;
134
134
  const headers = {
135
- "Content-Type": "application/json",
136
135
  "Accept": "application/json",
137
136
  ...options.headers || {}
138
137
  };
138
+ const hasExplicitContentType = Object.keys(headers).some((key) => key.toLowerCase() === "content-type");
139
+ const hasBody = options.body !== void 0 && options.body !== null;
140
+ if (hasBody && !hasExplicitContentType) {
141
+ headers["Content-Type"] = "application/json";
142
+ }
139
143
  if (this.apiKey) {
140
144
  headers["x-api-key"] = this.apiKey;
141
145
  }
@@ -395,11 +399,29 @@ var Forg3tClient = class {
395
399
  body: JSON.stringify(data)
396
400
  }, requestId);
397
401
  }
398
- async rotateApiKey(keyId, requestId) {
399
- return this.transport.request(`/v1/api-keys/${keyId}/rotate`, { method: "POST" }, requestId);
402
+ async rotateApiKey(projectId, keyId, requestId) {
403
+ if (!projectId || !keyId) {
404
+ throw new Error("rotateApiKey requires both projectId and keyId.");
405
+ }
406
+ const existingKeys = await this.listApiKeys(projectId, requestId);
407
+ const current = existingKeys.find((item) => item.id === keyId);
408
+ if (!current) {
409
+ throw new Forg3tNotFoundError(`API key ${keyId} was not found in project ${projectId}.`, requestId);
410
+ }
411
+ const rotated = await this.createApiKey(projectId, {
412
+ name: current.name,
413
+ ...current.expiresAt ? { expiresAt: current.expiresAt } : {}
414
+ }, requestId);
415
+ await this.revokeApiKey(projectId, keyId, requestId);
416
+ return rotated;
400
417
  }
401
- async revokeApiKey(keyId, requestId) {
402
- return this.transport.request(`/v1/api-keys/${keyId}/revoke`, { method: "POST" }, requestId);
418
+ async revokeApiKey(projectId, keyId, requestId) {
419
+ if (!projectId || !keyId) {
420
+ throw new Error("revokeApiKey requires both projectId and keyId.");
421
+ }
422
+ return this.transport.request(`/v1/projects/${projectId}/api-keys/${keyId}`, {
423
+ method: "DELETE"
424
+ }, requestId);
403
425
  }
404
426
  // --- Jobs ---
405
427
  async submitJob(projectId, data, requestId) {
@@ -409,6 +431,8 @@ var Forg3tClient = class {
409
431
  };
410
432
  if (data.type === "MODEL_UNLEARN") {
411
433
  this.assertModelUnlearnPreflight(normalizedPayload);
434
+ } else if (data.type === "ADAPTER_UNLEARN") {
435
+ this.assertAdapterUnlearnPreflight(normalizedPayload);
412
436
  }
413
437
  const res = await this.transport.request(`/v1/projects/${projectId}/jobs`, {
414
438
  method: "POST",
@@ -633,18 +657,67 @@ var Forg3tClient = class {
633
657
  }
634
658
  }
635
659
  if (data.accessLevel === "layer_a_only") {
660
+ const rag = data.execution?.rag;
636
661
  const target = data.execution?.target;
637
- if (!target) {
638
- throw new Error("createUnlearningRequest: execution.target is required for layer_a_only.");
662
+ const adapter = data.execution?.adapter;
663
+ if (adapter) {
664
+ throw new Error("createUnlearningRequest: execution.adapter is only valid for layer_a_and_b.");
639
665
  }
640
- if ((target.provider === "openai" || target.provider === "groq") && !target.model?.trim()) {
641
- throw new Error(`createUnlearningRequest: execution.target.model is required when provider=${target.provider}.`);
666
+ if (target && rag) {
667
+ throw new Error("createUnlearningRequest: execution.target and execution.rag are mutually exclusive for layer_a_only.");
642
668
  }
643
- if ((target.provider === "http_generic" || target.provider === "custom") && !target.endpoint?.trim()) {
644
- throw new Error(`createUnlearningRequest: execution.target.endpoint is required when provider=${target.provider}.`);
669
+ if (rag) {
670
+ if (rag.backend !== "pgvector_postgres") {
671
+ throw new Error("createUnlearningRequest: execution.rag.backend must be pgvector_postgres.");
672
+ }
673
+ if (data.target.type !== "document_id") {
674
+ throw new Error("createUnlearningRequest: the official RAG lane currently supports target.type=document_id only.");
675
+ }
676
+ if (!Array.isArray(data.scope?.integrationIds) || data.scope.integrationIds.length !== 1) {
677
+ throw new Error("createUnlearningRequest: the official RAG lane requires exactly one scope.integrationIds entry.");
678
+ }
679
+ if (data.execution?.routing?.lane && data.execution.routing.lane !== "project_scoped") {
680
+ throw new Error("createUnlearningRequest: execution.routing.lane must be project_scoped for the official RAG lane.");
681
+ }
682
+ } else {
683
+ if (!target) {
684
+ throw new Error("createUnlearningRequest: execution.target is required for layer_a_only unless execution.rag is provided.");
685
+ }
686
+ if ((target.provider === "openai" || target.provider === "groq") && !target.model?.trim()) {
687
+ throw new Error(`createUnlearningRequest: execution.target.model is required when provider=${target.provider}.`);
688
+ }
689
+ if ((target.provider === "http_generic" || target.provider === "custom") && !target.endpoint?.trim()) {
690
+ throw new Error(`createUnlearningRequest: execution.target.endpoint is required when provider=${target.provider}.`);
691
+ }
645
692
  }
646
693
  }
647
694
  if (data.accessLevel === "layer_a_and_b") {
695
+ const adapter = data.execution?.adapter;
696
+ if (adapter) {
697
+ if (adapter.backend !== "peft_lora_safetensors") {
698
+ throw new Error("createUnlearningRequest: execution.adapter.backend must be peft_lora_safetensors.");
699
+ }
700
+ if (data.execution?.model?.uri?.trim()) {
701
+ throw new Error("createUnlearningRequest: execution.adapter and execution.model are mutually exclusive for layer_a_and_b.");
702
+ }
703
+ if (!Array.isArray(data.scope?.integrationIds) || data.scope.integrationIds.length !== 1) {
704
+ throw new Error("createUnlearningRequest: the official adapter lane requires exactly one scope.integrationIds entry.");
705
+ }
706
+ if (data.execution?.routing?.lane && data.execution.routing.lane !== "project_scoped") {
707
+ throw new Error("createUnlearningRequest: execution.routing.lane must be project_scoped for the official adapter lane.");
708
+ }
709
+ const method = String(adapter.method || "attenuate_adapter").trim();
710
+ if (!["disable_adapter", "attenuate_adapter"].includes(method)) {
711
+ throw new Error("createUnlearningRequest: execution.adapter.method must be disable_adapter or attenuate_adapter.");
712
+ }
713
+ if (method === "attenuate_adapter") {
714
+ const scaleFactor = Number(adapter.scaleFactor);
715
+ if (!Number.isFinite(scaleFactor) || scaleFactor <= 0 || scaleFactor >= 1) {
716
+ throw new Error("createUnlearningRequest: execution.adapter.scaleFactor must be a finite number between 0 and 1 for attenuate_adapter.");
717
+ }
718
+ }
719
+ return;
720
+ }
648
721
  if (!data.execution?.model?.uri?.trim()) {
649
722
  throw new Error("createUnlearningRequest: execution.model.uri is required for layer_a_and_b.");
650
723
  }
@@ -716,6 +789,40 @@ var Forg3tClient = class {
716
789
  );
717
790
  }
718
791
  }
792
+ assertAdapterUnlearnPreflight(payload) {
793
+ if (!payload || typeof payload !== "object") {
794
+ throw new Error("ADAPTER_UNLEARN preflight failed: payload must be an object.");
795
+ }
796
+ const asRecord = payload;
797
+ const config = asRecord.config && typeof asRecord.config === "object" && !Array.isArray(asRecord.config) ? asRecord.config : null;
798
+ if (!config) {
799
+ throw new Error("ADAPTER_UNLEARN preflight failed: payload.config is required.");
800
+ }
801
+ const targetConfig = config.target_config && typeof config.target_config === "object" && !Array.isArray(config.target_config) ? config.target_config : null;
802
+ const parameters = config.parameters && typeof config.parameters === "object" && !Array.isArray(config.parameters) ? config.parameters : null;
803
+ const adapter = parameters?.adapter && typeof parameters.adapter === "object" && !Array.isArray(parameters.adapter) ? parameters.adapter : null;
804
+ if (!targetConfig) {
805
+ throw new Error("ADAPTER_UNLEARN preflight failed: payload.config.target_config is required.");
806
+ }
807
+ if (String(targetConfig.backend || "").trim() !== "peft_lora_safetensors") {
808
+ throw new Error("ADAPTER_UNLEARN preflight failed: payload.config.target_config.backend must be peft_lora_safetensors.");
809
+ }
810
+ const adapterUri = String(targetConfig.adapter_uri || "").trim();
811
+ const adapterUriEnvVar = String(targetConfig.adapter_uri_env_var || "").trim();
812
+ if (!adapterUri && !adapterUriEnvVar) {
813
+ throw new Error("ADAPTER_UNLEARN preflight failed: payload.config.target_config requires adapter_uri or adapter_uri_env_var.");
814
+ }
815
+ const method = String(adapter?.method || "attenuate_adapter").trim();
816
+ if (!["disable_adapter", "attenuate_adapter"].includes(method)) {
817
+ throw new Error("ADAPTER_UNLEARN preflight failed: parameters.adapter.method must be disable_adapter or attenuate_adapter.");
818
+ }
819
+ if (method === "attenuate_adapter") {
820
+ const scaleFactor = Number(adapter?.scaleFactor);
821
+ if (!Number.isFinite(scaleFactor) || scaleFactor <= 0 || scaleFactor >= 1) {
822
+ throw new Error("ADAPTER_UNLEARN preflight failed: parameters.adapter.scaleFactor must be between 0 and 1 for attenuate_adapter.");
823
+ }
824
+ }
825
+ }
719
826
  };
720
827
 
721
828
  // src/types.ts
@@ -728,6 +835,8 @@ var IntegrationType = /* @__PURE__ */ ((IntegrationType2) => {
728
835
  IntegrationType2["LANGCHAIN"] = "langchain";
729
836
  IntegrationType2["OPENROUTER"] = "openrouter";
730
837
  IntegrationType2["OPENAI"] = "openai";
838
+ IntegrationType2["PGVECTOR"] = "pgvector";
839
+ IntegrationType2["ADAPTER"] = "adapter";
731
840
  IntegrationType2["CUSTOM"] = "custom";
732
841
  return IntegrationType2;
733
842
  })(IntegrationType || {});
@@ -1431,10 +1540,19 @@ var ENTERPRISE_STRICT_QUALITY_GATE_POLICY = {
1431
1540
  ...WHITEBOX_QUALITY_GATE_PRESETS.strict
1432
1541
  };
1433
1542
  function assertDeploymentCompatibility(options) {
1434
- const normalizedHost = parseHost(options.apiUrl);
1435
- if (!normalizedHost) {
1543
+ const parsedApiUrl = parseApiUrl(options.apiUrl);
1544
+ if (!parsedApiUrl) {
1436
1545
  throw new Error(`Invalid FORG3T_API_URL: ${options.apiUrl}`);
1437
1546
  }
1547
+ const normalizedHost = parsedApiUrl.hostname.toLowerCase();
1548
+ if (options.profile === "customer_online") {
1549
+ const publicHost = !isPrivateHost(normalizedHost);
1550
+ if (publicHost && parsedApiUrl.protocol !== "https:") {
1551
+ throw new Error(
1552
+ `Customer online profile requires HTTPS for public control-plane hosts. Received ${options.apiUrl}.`
1553
+ );
1554
+ }
1555
+ }
1438
1556
  if (options.profile !== "customer_airgapped") {
1439
1557
  return;
1440
1558
  }
@@ -1465,10 +1583,9 @@ function buildWhiteboxWorkerOptions(profile, baseOptions) {
1465
1583
  logger: baseOptions.logger
1466
1584
  };
1467
1585
  }
1468
- function parseHost(apiUrl) {
1586
+ function parseApiUrl(apiUrl) {
1469
1587
  try {
1470
- const parsed = new URL(apiUrl);
1471
- return parsed.hostname.toLowerCase();
1588
+ return new URL(apiUrl);
1472
1589
  } catch {
1473
1590
  return null;
1474
1591
  }
package/dist/index.mjs CHANGED
@@ -74,10 +74,14 @@ var Transport = class {
74
74
  async request(path, options = {}, requestId) {
75
75
  const url = `${this.baseUrl}${path.startsWith("/") ? "" : "/"}${path}`;
76
76
  const headers = {
77
- "Content-Type": "application/json",
78
77
  "Accept": "application/json",
79
78
  ...options.headers || {}
80
79
  };
80
+ const hasExplicitContentType = Object.keys(headers).some((key) => key.toLowerCase() === "content-type");
81
+ const hasBody = options.body !== void 0 && options.body !== null;
82
+ if (hasBody && !hasExplicitContentType) {
83
+ headers["Content-Type"] = "application/json";
84
+ }
81
85
  if (this.apiKey) {
82
86
  headers["x-api-key"] = this.apiKey;
83
87
  }
@@ -337,11 +341,29 @@ var Forg3tClient = class {
337
341
  body: JSON.stringify(data)
338
342
  }, requestId);
339
343
  }
340
- async rotateApiKey(keyId, requestId) {
341
- return this.transport.request(`/v1/api-keys/${keyId}/rotate`, { method: "POST" }, requestId);
344
+ async rotateApiKey(projectId, keyId, requestId) {
345
+ if (!projectId || !keyId) {
346
+ throw new Error("rotateApiKey requires both projectId and keyId.");
347
+ }
348
+ const existingKeys = await this.listApiKeys(projectId, requestId);
349
+ const current = existingKeys.find((item) => item.id === keyId);
350
+ if (!current) {
351
+ throw new Forg3tNotFoundError(`API key ${keyId} was not found in project ${projectId}.`, requestId);
352
+ }
353
+ const rotated = await this.createApiKey(projectId, {
354
+ name: current.name,
355
+ ...current.expiresAt ? { expiresAt: current.expiresAt } : {}
356
+ }, requestId);
357
+ await this.revokeApiKey(projectId, keyId, requestId);
358
+ return rotated;
342
359
  }
343
- async revokeApiKey(keyId, requestId) {
344
- return this.transport.request(`/v1/api-keys/${keyId}/revoke`, { method: "POST" }, requestId);
360
+ async revokeApiKey(projectId, keyId, requestId) {
361
+ if (!projectId || !keyId) {
362
+ throw new Error("revokeApiKey requires both projectId and keyId.");
363
+ }
364
+ return this.transport.request(`/v1/projects/${projectId}/api-keys/${keyId}`, {
365
+ method: "DELETE"
366
+ }, requestId);
345
367
  }
346
368
  // --- Jobs ---
347
369
  async submitJob(projectId, data, requestId) {
@@ -351,6 +373,8 @@ var Forg3tClient = class {
351
373
  };
352
374
  if (data.type === "MODEL_UNLEARN") {
353
375
  this.assertModelUnlearnPreflight(normalizedPayload);
376
+ } else if (data.type === "ADAPTER_UNLEARN") {
377
+ this.assertAdapterUnlearnPreflight(normalizedPayload);
354
378
  }
355
379
  const res = await this.transport.request(`/v1/projects/${projectId}/jobs`, {
356
380
  method: "POST",
@@ -575,18 +599,67 @@ var Forg3tClient = class {
575
599
  }
576
600
  }
577
601
  if (data.accessLevel === "layer_a_only") {
602
+ const rag = data.execution?.rag;
578
603
  const target = data.execution?.target;
579
- if (!target) {
580
- throw new Error("createUnlearningRequest: execution.target is required for layer_a_only.");
604
+ const adapter = data.execution?.adapter;
605
+ if (adapter) {
606
+ throw new Error("createUnlearningRequest: execution.adapter is only valid for layer_a_and_b.");
581
607
  }
582
- if ((target.provider === "openai" || target.provider === "groq") && !target.model?.trim()) {
583
- throw new Error(`createUnlearningRequest: execution.target.model is required when provider=${target.provider}.`);
608
+ if (target && rag) {
609
+ throw new Error("createUnlearningRequest: execution.target and execution.rag are mutually exclusive for layer_a_only.");
584
610
  }
585
- if ((target.provider === "http_generic" || target.provider === "custom") && !target.endpoint?.trim()) {
586
- throw new Error(`createUnlearningRequest: execution.target.endpoint is required when provider=${target.provider}.`);
611
+ if (rag) {
612
+ if (rag.backend !== "pgvector_postgres") {
613
+ throw new Error("createUnlearningRequest: execution.rag.backend must be pgvector_postgres.");
614
+ }
615
+ if (data.target.type !== "document_id") {
616
+ throw new Error("createUnlearningRequest: the official RAG lane currently supports target.type=document_id only.");
617
+ }
618
+ if (!Array.isArray(data.scope?.integrationIds) || data.scope.integrationIds.length !== 1) {
619
+ throw new Error("createUnlearningRequest: the official RAG lane requires exactly one scope.integrationIds entry.");
620
+ }
621
+ if (data.execution?.routing?.lane && data.execution.routing.lane !== "project_scoped") {
622
+ throw new Error("createUnlearningRequest: execution.routing.lane must be project_scoped for the official RAG lane.");
623
+ }
624
+ } else {
625
+ if (!target) {
626
+ throw new Error("createUnlearningRequest: execution.target is required for layer_a_only unless execution.rag is provided.");
627
+ }
628
+ if ((target.provider === "openai" || target.provider === "groq") && !target.model?.trim()) {
629
+ throw new Error(`createUnlearningRequest: execution.target.model is required when provider=${target.provider}.`);
630
+ }
631
+ if ((target.provider === "http_generic" || target.provider === "custom") && !target.endpoint?.trim()) {
632
+ throw new Error(`createUnlearningRequest: execution.target.endpoint is required when provider=${target.provider}.`);
633
+ }
587
634
  }
588
635
  }
589
636
  if (data.accessLevel === "layer_a_and_b") {
637
+ const adapter = data.execution?.adapter;
638
+ if (adapter) {
639
+ if (adapter.backend !== "peft_lora_safetensors") {
640
+ throw new Error("createUnlearningRequest: execution.adapter.backend must be peft_lora_safetensors.");
641
+ }
642
+ if (data.execution?.model?.uri?.trim()) {
643
+ throw new Error("createUnlearningRequest: execution.adapter and execution.model are mutually exclusive for layer_a_and_b.");
644
+ }
645
+ if (!Array.isArray(data.scope?.integrationIds) || data.scope.integrationIds.length !== 1) {
646
+ throw new Error("createUnlearningRequest: the official adapter lane requires exactly one scope.integrationIds entry.");
647
+ }
648
+ if (data.execution?.routing?.lane && data.execution.routing.lane !== "project_scoped") {
649
+ throw new Error("createUnlearningRequest: execution.routing.lane must be project_scoped for the official adapter lane.");
650
+ }
651
+ const method = String(adapter.method || "attenuate_adapter").trim();
652
+ if (!["disable_adapter", "attenuate_adapter"].includes(method)) {
653
+ throw new Error("createUnlearningRequest: execution.adapter.method must be disable_adapter or attenuate_adapter.");
654
+ }
655
+ if (method === "attenuate_adapter") {
656
+ const scaleFactor = Number(adapter.scaleFactor);
657
+ if (!Number.isFinite(scaleFactor) || scaleFactor <= 0 || scaleFactor >= 1) {
658
+ throw new Error("createUnlearningRequest: execution.adapter.scaleFactor must be a finite number between 0 and 1 for attenuate_adapter.");
659
+ }
660
+ }
661
+ return;
662
+ }
590
663
  if (!data.execution?.model?.uri?.trim()) {
591
664
  throw new Error("createUnlearningRequest: execution.model.uri is required for layer_a_and_b.");
592
665
  }
@@ -658,6 +731,40 @@ var Forg3tClient = class {
658
731
  );
659
732
  }
660
733
  }
734
+ assertAdapterUnlearnPreflight(payload) {
735
+ if (!payload || typeof payload !== "object") {
736
+ throw new Error("ADAPTER_UNLEARN preflight failed: payload must be an object.");
737
+ }
738
+ const asRecord = payload;
739
+ const config = asRecord.config && typeof asRecord.config === "object" && !Array.isArray(asRecord.config) ? asRecord.config : null;
740
+ if (!config) {
741
+ throw new Error("ADAPTER_UNLEARN preflight failed: payload.config is required.");
742
+ }
743
+ const targetConfig = config.target_config && typeof config.target_config === "object" && !Array.isArray(config.target_config) ? config.target_config : null;
744
+ const parameters = config.parameters && typeof config.parameters === "object" && !Array.isArray(config.parameters) ? config.parameters : null;
745
+ const adapter = parameters?.adapter && typeof parameters.adapter === "object" && !Array.isArray(parameters.adapter) ? parameters.adapter : null;
746
+ if (!targetConfig) {
747
+ throw new Error("ADAPTER_UNLEARN preflight failed: payload.config.target_config is required.");
748
+ }
749
+ if (String(targetConfig.backend || "").trim() !== "peft_lora_safetensors") {
750
+ throw new Error("ADAPTER_UNLEARN preflight failed: payload.config.target_config.backend must be peft_lora_safetensors.");
751
+ }
752
+ const adapterUri = String(targetConfig.adapter_uri || "").trim();
753
+ const adapterUriEnvVar = String(targetConfig.adapter_uri_env_var || "").trim();
754
+ if (!adapterUri && !adapterUriEnvVar) {
755
+ throw new Error("ADAPTER_UNLEARN preflight failed: payload.config.target_config requires adapter_uri or adapter_uri_env_var.");
756
+ }
757
+ const method = String(adapter?.method || "attenuate_adapter").trim();
758
+ if (!["disable_adapter", "attenuate_adapter"].includes(method)) {
759
+ throw new Error("ADAPTER_UNLEARN preflight failed: parameters.adapter.method must be disable_adapter or attenuate_adapter.");
760
+ }
761
+ if (method === "attenuate_adapter") {
762
+ const scaleFactor = Number(adapter?.scaleFactor);
763
+ if (!Number.isFinite(scaleFactor) || scaleFactor <= 0 || scaleFactor >= 1) {
764
+ throw new Error("ADAPTER_UNLEARN preflight failed: parameters.adapter.scaleFactor must be between 0 and 1 for attenuate_adapter.");
765
+ }
766
+ }
767
+ }
661
768
  };
662
769
 
663
770
  // src/types.ts
@@ -670,6 +777,8 @@ var IntegrationType = /* @__PURE__ */ ((IntegrationType2) => {
670
777
  IntegrationType2["LANGCHAIN"] = "langchain";
671
778
  IntegrationType2["OPENROUTER"] = "openrouter";
672
779
  IntegrationType2["OPENAI"] = "openai";
780
+ IntegrationType2["PGVECTOR"] = "pgvector";
781
+ IntegrationType2["ADAPTER"] = "adapter";
673
782
  IntegrationType2["CUSTOM"] = "custom";
674
783
  return IntegrationType2;
675
784
  })(IntegrationType || {});
@@ -1373,10 +1482,19 @@ var ENTERPRISE_STRICT_QUALITY_GATE_POLICY = {
1373
1482
  ...WHITEBOX_QUALITY_GATE_PRESETS.strict
1374
1483
  };
1375
1484
  function assertDeploymentCompatibility(options) {
1376
- const normalizedHost = parseHost(options.apiUrl);
1377
- if (!normalizedHost) {
1485
+ const parsedApiUrl = parseApiUrl(options.apiUrl);
1486
+ if (!parsedApiUrl) {
1378
1487
  throw new Error(`Invalid FORG3T_API_URL: ${options.apiUrl}`);
1379
1488
  }
1489
+ const normalizedHost = parsedApiUrl.hostname.toLowerCase();
1490
+ if (options.profile === "customer_online") {
1491
+ const publicHost = !isPrivateHost(normalizedHost);
1492
+ if (publicHost && parsedApiUrl.protocol !== "https:") {
1493
+ throw new Error(
1494
+ `Customer online profile requires HTTPS for public control-plane hosts. Received ${options.apiUrl}.`
1495
+ );
1496
+ }
1497
+ }
1380
1498
  if (options.profile !== "customer_airgapped") {
1381
1499
  return;
1382
1500
  }
@@ -1407,10 +1525,9 @@ function buildWhiteboxWorkerOptions(profile, baseOptions) {
1407
1525
  logger: baseOptions.logger
1408
1526
  };
1409
1527
  }
1410
- function parseHost(apiUrl) {
1528
+ function parseApiUrl(apiUrl) {
1411
1529
  try {
1412
- const parsed = new URL(apiUrl);
1413
- return parsed.hostname.toLowerCase();
1530
+ return new URL(apiUrl);
1414
1531
  } catch {
1415
1532
  return null;
1416
1533
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forg3t/sdk",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "description": "Official TypeScript SDK for Forg3t Protocol",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -28,7 +28,9 @@
28
28
  "test:whitebox": "node ../../node_modules/ts-node/dist/bin.js tools/whitebox_hardening_smoke.ts",
29
29
  "test:example": "npx tsx examples/quickstart.ts",
30
30
  "smoke:staging": "node ../../node_modules/ts-node/dist/bin.js examples/staging-smoke.ts",
31
- "smoke:customer-onboarding": "node ../../node_modules/ts-node/dist/bin.js examples/staging-smoke.ts",
31
+ "smoke:customer-onboarding": "npx tsx examples/bootstrap-onboarding.ts",
32
+ "smoke:cleanroom": "node scripts/run_cleanroom_smoke.js",
33
+ "example:bootstrap": "npx tsx examples/bootstrap-onboarding.ts",
32
34
  "test:breaking": "node scripts/detect-breaking-changes.js",
33
35
  "test:contract": "node tools/probe_contract.mjs",
34
36
  "test:exports": "node tools/exports_smoke_esm.mjs && node tools/exports_smoke_cjs.cjs",