@alfe.ai/gateway 0.9.10 → 0.9.12

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 (2) hide show
  1. package/dist/health.js +533 -180
  2. package/package.json +2 -2
package/dist/health.js CHANGED
@@ -287,151 +287,6 @@ function pluginConnectionId() {
287
287
  return createId(ID_PREFIXES.pluginConnection);
288
288
  }
289
289
  //#endregion
290
- //#region ../../packages-internal/api-client/dist/client.js
291
- /**
292
- * @alfe/api-client — Typed HTTP client for Alfe services.
293
- *
294
- * Platform-agnostic: works in React Native and browser environments.
295
- * Uses standard fetch() API — no Node.js dependencies.
296
- */
297
- var AlfeApiClient = class {
298
- apiBaseUrl;
299
- getToken;
300
- onAuthFailure;
301
- constructor(options) {
302
- this.apiBaseUrl = options.apiBaseUrl.replace(/\/+$/, "");
303
- this.getToken = options.getToken;
304
- this.onAuthFailure = options.onAuthFailure;
305
- }
306
- /**
307
- * Shared fetch logic — handles auth, 401, and network errors.
308
- *
309
- * `skipAuth` callers (public endpoints like OAuth device-code) opt out of
310
- * the auth-header injection AND the synthetic 401 short-circuit. Without
311
- * this opt-out, a CLI calling /auth/device-code (no token yet by design)
312
- * would never reach the network — the `getToken: () => null` path would
313
- * synthesize a 401 and fire onAuthFailure, breaking the entire flow.
314
- */
315
- async _fetch(path, options, skipAuth = false) {
316
- try {
317
- const url = `${this.apiBaseUrl}${path}`;
318
- const headers = new Headers(options?.headers);
319
- if (typeof options?.body === "string" && !headers.has("Content-Type")) headers.set("Content-Type", "application/json");
320
- headers.set("x-correlation-id", correlationId());
321
- if (!skipAuth) {
322
- const token = await this.getToken();
323
- if (!token) {
324
- this.onAuthFailure?.();
325
- return {
326
- ok: false,
327
- result: {
328
- ok: false,
329
- error: "No auth token available",
330
- status: 401
331
- }
332
- };
333
- }
334
- headers.set("Authorization", `Bearer ${token}`);
335
- }
336
- const res = await fetch(url, {
337
- ...options,
338
- headers
339
- });
340
- if (res.status === 401 && !skipAuth) {
341
- this.onAuthFailure?.();
342
- return {
343
- ok: false,
344
- result: {
345
- ok: false,
346
- error: "Session expired",
347
- status: 401
348
- }
349
- };
350
- }
351
- const text = res.status === 204 ? "" : await res.text();
352
- let body;
353
- if (text) try {
354
- body = JSON.parse(text);
355
- } catch {
356
- body = text;
357
- }
358
- if (!res.ok) {
359
- const errBody = body;
360
- return {
361
- ok: false,
362
- result: {
363
- ok: false,
364
- error: (typeof errBody === "object" ? errBody.message : void 0) ?? (typeof body === "string" ? body : `API error: ${String(res.status)}`),
365
- status: res.status
366
- }
367
- };
368
- }
369
- return {
370
- ok: true,
371
- res,
372
- body
373
- };
374
- } catch (err) {
375
- return {
376
- ok: false,
377
- result: {
378
- ok: false,
379
- error: err instanceof Error ? err.message : "Network error"
380
- }
381
- };
382
- }
383
- }
384
- /**
385
- * Make an authenticated request to an Alfe API endpoint.
386
- * Unwraps the @auriclabs/api-core `{ data, timestamp, requestId }` envelope.
387
- */
388
- async request(path, options) {
389
- const result = await this._fetch(path, options);
390
- if (!result.ok) return result.result;
391
- if (result.body === void 0) return {
392
- ok: true,
393
- data: void 0
394
- };
395
- return {
396
- ok: true,
397
- data: result.body.data
398
- };
399
- }
400
- /**
401
- * Make a request to a PUBLIC endpoint that does not require authentication.
402
- * Skips both the Authorization header injection AND the onAuthFailure
403
- * callback. Use for endpoints like /auth/device-code that the CLI hits
404
- * before it has a token.
405
- */
406
- async publicRequest(path, options) {
407
- const result = await this._fetch(path, options, true);
408
- if (!result.ok) return result.result;
409
- if (result.body === void 0) return {
410
- ok: true,
411
- data: void 0
412
- };
413
- return {
414
- ok: true,
415
- data: result.body.data
416
- };
417
- }
418
- /**
419
- * Make an authenticated request that returns the body directly (no envelope unwrap).
420
- * Use for APIs that don't use the @auriclabs/api-core response format (e.g. gateway).
421
- */
422
- async rawRequest(path, options) {
423
- const result = await this._fetch(path, options);
424
- if (!result.ok) return result.result;
425
- return {
426
- ok: true,
427
- data: result.body
428
- };
429
- }
430
- getApiBaseUrl() {
431
- return this.apiBaseUrl;
432
- }
433
- };
434
- //#endregion
435
290
  //#region ../../packages-internal/types/dist/lib/enum-values.js
436
291
  /**
437
292
  * Converts a const enum object into a non-empty readonly tuple.
@@ -4154,7 +4009,8 @@ enumValues({
4154
4009
  ClaudeMax: "claude-max",
4155
4010
  OpenAICodexMax: "openai-codex-max",
4156
4011
  OpenAICodexSubscription: "openai-codex-subscription",
4157
- GeminiMax: "gemini-max"
4012
+ GeminiMax: "gemini-max",
4013
+ AlfeAws: "alfe-aws"
4158
4014
  });
4159
4015
  enumValues({
4160
4016
  Month: "month",
@@ -4227,6 +4083,179 @@ object({
4227
4083
  ttsModel: _enum(TTS_MODELS).optional(),
4228
4084
  enabled: boolean().optional()
4229
4085
  });
4086
+ //#endregion
4087
+ //#region ../../packages-internal/types/dist/access.js
4088
+ /**
4089
+ * Runtime catalog of declared permission subjects + allowed actions.
4090
+ *
4091
+ * Mirrors the compile-time `PermissionDefinitions` augmentation in
4092
+ * `@alfe/api-core/auriclabs-roles.ts`. Kept here so browser code
4093
+ * (dashboard) can consume it without pulling in server-only deps
4094
+ * (`@middy/core`, `aws-lambda`, etc.).
4095
+ */
4096
+ const PERMISSION_CATALOG = {
4097
+ all: ["manage"],
4098
+ user: [
4099
+ "create",
4100
+ "read",
4101
+ "update",
4102
+ "delete",
4103
+ "manage"
4104
+ ],
4105
+ agent: [
4106
+ "create",
4107
+ "read",
4108
+ "update",
4109
+ "delete",
4110
+ "exec",
4111
+ "chat",
4112
+ "manage"
4113
+ ],
4114
+ chat: [
4115
+ "read",
4116
+ "write",
4117
+ "upload",
4118
+ "manage"
4119
+ ],
4120
+ sync: [
4121
+ "read",
4122
+ "write",
4123
+ "manage"
4124
+ ],
4125
+ integration: [
4126
+ "create",
4127
+ "read",
4128
+ "update",
4129
+ "delete",
4130
+ "manage"
4131
+ ],
4132
+ gateway: ["connect", "manage"],
4133
+ voice: ["read", "manage"],
4134
+ identity: ["read", "manage"],
4135
+ token: [
4136
+ "create",
4137
+ "read",
4138
+ "update",
4139
+ "delete",
4140
+ "manage"
4141
+ ],
4142
+ billing: ["read", "manage"],
4143
+ template: [
4144
+ "create",
4145
+ "read",
4146
+ "update",
4147
+ "delete",
4148
+ "manage"
4149
+ ],
4150
+ team: [
4151
+ "create",
4152
+ "read",
4153
+ "update",
4154
+ "delete",
4155
+ "manage"
4156
+ ],
4157
+ project: [
4158
+ "create",
4159
+ "read",
4160
+ "update",
4161
+ "delete",
4162
+ "manage"
4163
+ ],
4164
+ org_file: ["read", "write"],
4165
+ org_knowledge: ["read", "write"],
4166
+ model_policy: ["read", "manage"],
4167
+ data_boundary: ["read", "manage"],
4168
+ secret: [
4169
+ "create",
4170
+ "read",
4171
+ "update",
4172
+ "delete",
4173
+ "manage"
4174
+ ],
4175
+ database: [
4176
+ "read",
4177
+ "write",
4178
+ "manage"
4179
+ ],
4180
+ role_management: ["read", "manage"],
4181
+ org: ["read", "manage"],
4182
+ connection: [
4183
+ "create",
4184
+ "read",
4185
+ "update",
4186
+ "delete",
4187
+ "manage"
4188
+ ],
4189
+ remote: ["control", "terminal"],
4190
+ subscription: ["read", "manage"],
4191
+ referral: ["manage"],
4192
+ promo_code: ["manage"],
4193
+ pricing: ["manage"],
4194
+ access_gate: ["manage"],
4195
+ dlq: ["read", "manage"],
4196
+ support: ["read"],
4197
+ discounts: ["manage"],
4198
+ stats: ["read"],
4199
+ mobile: ["read", "manage"],
4200
+ impersonation: ["manage"],
4201
+ partner: ["manage"]
4202
+ };
4203
+ /**
4204
+ * Subjects a tenant may grant via a custom role.
4205
+ *
4206
+ * Excludes `all` (platform-admin escape hatch) and every platform-only
4207
+ * permission subject listed after it in the catalog.
4208
+ */
4209
+ const TENANT_ALLOWED_SUBJECTS = [
4210
+ "agent",
4211
+ "chat",
4212
+ "sync",
4213
+ "integration",
4214
+ "secret",
4215
+ "database",
4216
+ "template",
4217
+ "org_file",
4218
+ "org_knowledge",
4219
+ "team",
4220
+ "project",
4221
+ "model_policy",
4222
+ "data_boundary",
4223
+ "role_management",
4224
+ "billing",
4225
+ "identity",
4226
+ "token",
4227
+ "user",
4228
+ "gateway",
4229
+ "voice",
4230
+ "org",
4231
+ "connection",
4232
+ "remote",
4233
+ "subscription"
4234
+ ];
4235
+ /**
4236
+ * Subjects a PLATFORM ADMIN may grant via a custom **admin** role (authored at
4237
+ * the flat `admin:` scope, stored under the `"system"` tenant). Two groups:
4238
+ *
4239
+ * 1. Platform-only subjects — every catalog subject that is NOT in
4240
+ * `TENANT_ALLOWED_SUBJECTS` (and never `all`). Derived from
4241
+ * `PERMISSION_CATALOG` so a newly-declared admin-only subject becomes
4242
+ * grantable in custom admin roles automatically.
4243
+ * 2. Dual-surface subjects an admin also curates platform-wide: `user`
4244
+ * (platform user management), `role_management` (admin role authoring),
4245
+ * and `subscription` (reviewing every tenant's plan/startup application).
4246
+ *
4247
+ * `all` is deliberately excluded — it is reserved for the built-in
4248
+ * `sys:admin:admin` super-admin role and must never be grantable through a
4249
+ * custom admin role. The complement of `TENANT_ALLOWED_SUBJECTS` keeps the
4250
+ * tenant vocabulary and the admin vocabulary from drifting into each other.
4251
+ */
4252
+ const ADMIN_DUAL_SURFACE_SUBJECTS = [
4253
+ "user",
4254
+ "role_management",
4255
+ "subscription"
4256
+ ];
4257
+ const TENANT_ALLOWED_SUBJECT_LOOKUP = new Set(TENANT_ALLOWED_SUBJECTS);
4258
+ [...Object.keys(PERMISSION_CATALOG).filter((subject) => subject !== "all" && !TENANT_ALLOWED_SUBJECT_LOOKUP.has(subject)), ...ADMIN_DUAL_SURFACE_SUBJECTS];
4230
4259
  enumValues({
4231
4260
  Org: "org",
4232
4261
  Team: "team",
@@ -4353,6 +4382,26 @@ enumValues({
4353
4382
  Demo: "demo",
4354
4383
  Live: "live"
4355
4384
  });
4385
+ function createClerkReverificationResponse(afterMinutes) {
4386
+ if (!Number.isInteger(afterMinutes) || afterMinutes < 0) throw new TypeError("afterMinutes must be a non-negative integer");
4387
+ return { clerk_error: {
4388
+ type: "forbidden",
4389
+ reason: "reverification-error",
4390
+ metadata: { reverification: {
4391
+ level: "second_factor",
4392
+ afterMinutes
4393
+ } }
4394
+ } };
4395
+ }
4396
+ /** Strictly identify the only authorization hint the API client may expose. */
4397
+ function isClerkReverificationResponse(value) {
4398
+ if (!value || typeof value !== "object") return false;
4399
+ const clerkError = value.clerk_error;
4400
+ if (!clerkError || typeof clerkError !== "object") return false;
4401
+ const error = clerkError;
4402
+ const reverification = error.metadata?.reverification;
4403
+ return error.type === "forbidden" && error.reason === "reverification-error" && reverification?.level === "second_factor" && Number.isInteger(reverification.afterMinutes) && reverification.afterMinutes >= 0;
4404
+ }
4356
4405
  //#endregion
4357
4406
  //#region ../../packages-internal/types/dist/models.js
4358
4407
  const AnthropicModel = {
@@ -4480,6 +4529,35 @@ _enum(QWEN_MODELS);
4480
4529
  string().trim().min(1);
4481
4530
  AnthropicModel.Opus5, AnthropicModel.Opus48, AnthropicModel.Opus47, AnthropicModel.Opus46, AnthropicModel.Sonnet5, AnthropicModel.Sonnet46, AnthropicModel.Haiku45, OpenAIModel.GPT4o, OpenAIModel.GPT4oMini, OpenAIModel.O3, OpenAIModel.GPT41, OpenAIModel.GPT41Mini, OpenAIModel.GPT41Nano, OpenAIModel.GPT54, OpenAIModel.GPT54Mini, OpenAIModel.GPT54Nano, OpenAIModel.GPT54Pro, OpenAIModel.GPT55, OpenAIModel.GPT55Pro, OpenAIModel.GPT56Sol, OpenAIModel.GPT56Terra, OpenAIModel.GPT56Luna, OpenAIModel.O3Mini, OpenAIModel.O4Mini, OpenAIModel.TextEmbedding3Small, OpenAIModel.TextEmbedding3Large, DeepSeekModel.Chat, DeepSeekModel.Reasoner, DeepSeekModel.V4Flash, DeepSeekModel.V4Pro, GoogleModel.Gemini35Flash, GoogleModel.Gemini31Pro, GoogleModel.Gemini31FlashLite, GoogleModel.Gemini25Pro, GoogleModel.Gemini25Flash, GoogleModel.Gemini25FlashLite, GoogleModel.Gemini20Flash, MiniMaxModel.M3, MiniMaxModel.M27, MiniMaxModel.M27HighSpeed, MiniMaxModel.M25, MiniMaxModel.M21, MiniMaxModel.M2, MistralModel.Large, MistralModel.Medium, MistralModel.Small, MistralModel.Codestral, MistralModel.Ministral8b, MistralModel.Ministral3b, MistralModel.MagistralMedium, MistralModel.MagistralSmall, MistralModel.DevstralMedium, XAIModel.Grok45, XAIModel.Grok43, XAIModel.Grok4, XAIModel.Grok41Fast, ZhipuModel.GLM52, ZhipuModel.GLM51, ZhipuModel.GLM46, ZhipuModel.GLM45, ZhipuModel.GLM45Air, MoonshotModel.K3, MoonshotModel.K26, MoonshotModel.K27Code, MoonshotModel.K27CodeHighSpeed, QwenModel.Qwen37Max, QwenModel.Qwen37Plus, QwenModel.Qwen36Flash, QwenModel.Qwen35Flash;
4482
4531
  AnthropicModel.Opus5, AnthropicModel.Opus48, AnthropicModel.Opus47, AnthropicModel.Opus46, AnthropicModel.Sonnet5, AnthropicModel.Sonnet46, AnthropicModel.Haiku45, OpenAIModel.GPT4o, OpenAIModel.GPT4oMini, OpenAIModel.O3, OpenAIModel.GPT41, OpenAIModel.GPT41Mini, OpenAIModel.GPT41Nano, OpenAIModel.GPT54, OpenAIModel.GPT54Mini, OpenAIModel.GPT54Nano, OpenAIModel.GPT54Pro, OpenAIModel.GPT55, OpenAIModel.GPT55Pro, OpenAIModel.GPT56Sol, OpenAIModel.GPT56Terra, OpenAIModel.GPT56Luna, OpenAIModel.O3Mini, OpenAIModel.O4Mini, OpenAIModel.TextEmbedding3Small, OpenAIModel.TextEmbedding3Large, DeepSeekModel.Chat, DeepSeekModel.Reasoner, DeepSeekModel.V4Flash, DeepSeekModel.V4Pro, GoogleModel.Gemini35Flash, GoogleModel.Gemini31Pro, GoogleModel.Gemini31FlashLite, GoogleModel.Gemini25Pro, GoogleModel.Gemini25Flash, GoogleModel.Gemini25FlashLite, GoogleModel.Gemini20Flash, MiniMaxModel.M3, MiniMaxModel.M27, MiniMaxModel.M27HighSpeed, MiniMaxModel.M25, MiniMaxModel.M21, MiniMaxModel.M2, MistralModel.Large, MistralModel.Medium, MistralModel.Small, MistralModel.Codestral, MistralModel.Ministral8b, MistralModel.Ministral3b, MistralModel.MagistralMedium, MistralModel.MagistralSmall, MistralModel.DevstralMedium, XAIModel.Grok45, XAIModel.Grok43, XAIModel.Grok4, XAIModel.Grok41Fast, ZhipuModel.GLM52, ZhipuModel.GLM51, ZhipuModel.GLM46, ZhipuModel.GLM45, ZhipuModel.GLM45Air, MoonshotModel.K3, MoonshotModel.K26, MoonshotModel.K27Code, MoonshotModel.K27CodeHighSpeed, QwenModel.Qwen37Max, QwenModel.Qwen37Plus, QwenModel.Qwen36Flash, QwenModel.Qwen35Flash;
4532
+ enumValues({
4533
+ Standard: "standard",
4534
+ AwsPrivate: "aws_private"
4535
+ });
4536
+ enumValues({ ApSoutheast2: "ap-southeast-2" });
4537
+ enumValues({
4538
+ Standard: "standard",
4539
+ Requested: "requested",
4540
+ Provisioning: "provisioning",
4541
+ Ready: "ready",
4542
+ CuttingOver: "cutting_over",
4543
+ Active: "active",
4544
+ Failing: "failing",
4545
+ Decommissioning: "decommissioning"
4546
+ });
4547
+ enumValues({
4548
+ RegionalShared: "regional_shared",
4549
+ Dedicated: "dedicated"
4550
+ });
4551
+ enumValues({
4552
+ Inference: "inference",
4553
+ AuthProjection: "auth_projection",
4554
+ ChatGateway: "chat_gateway",
4555
+ AgentCompute: "agent_compute",
4556
+ Attachments: "attachments",
4557
+ Memory: "memory",
4558
+ CapabilityPolicy: "capability_policy",
4559
+ Observability: "observability"
4560
+ });
4483
4561
  enumValues({
4484
4562
  PendingChallenge: "pending_challenge",
4485
4563
  Creating: "creating",
@@ -4568,6 +4646,8 @@ const NotificationType = {
4568
4646
  BrowserTakeoverRequested: "browser_takeover_requested",
4569
4647
  InviteCreated: "onboarding.invite.created",
4570
4648
  OrgClaimed: "onboarding.org.claimed",
4649
+ ElevatedSupportRequested: "partner.elevated_support.requested",
4650
+ ElevatedSupportStarted: "partner.elevated_support.started",
4571
4651
  TeamMemberAdded: "team.member.added",
4572
4652
  ProjectMemberAdded: "project.member.added",
4573
4653
  IntegrationInstalled: "integration.installed",
@@ -4582,7 +4662,213 @@ const RecipientStrategy = {
4582
4662
  TenantAdmins: "tenant_admins",
4583
4663
  SpecificUser: "specific_user"
4584
4664
  };
4585
- NotificationType.PaymentSucceeded, NotificationCategory.Billing, RecipientStrategy.TenantAdmins, NotificationChannel.Email, NotificationChannel.Push, NotificationType.PaymentFailed, NotificationCategory.Billing, RecipientStrategy.TenantAdmins, NotificationChannel.Email, NotificationChannel.Push, NotificationChannel.Sms, NotificationType.TopUpCompleted, NotificationCategory.Billing, RecipientStrategy.TenantAdmins, NotificationChannel.Email, NotificationChannel.Push, NotificationType.AutoRechargeCompleted, NotificationCategory.Billing, RecipientStrategy.TenantAdmins, NotificationChannel.Email, NotificationType.AutoRechargeFailed, NotificationCategory.Billing, RecipientStrategy.TenantAdmins, NotificationChannel.Email, NotificationChannel.Push, NotificationChannel.Sms, NotificationType.SubscriptionCreated, NotificationCategory.Billing, RecipientStrategy.TenantAdmins, NotificationChannel.Email, NotificationType.SubscriptionCancelled, NotificationCategory.Billing, RecipientStrategy.TenantAdmins, NotificationChannel.Email, NotificationChannel.Push, NotificationType.StartupGrantEnded, NotificationCategory.Billing, RecipientStrategy.TenantAdmins, NotificationChannel.Email, NotificationChannel.Push, NotificationType.GrantEnded, NotificationCategory.Billing, RecipientStrategy.TenantAdmins, NotificationChannel.Email, NotificationChannel.Push, NotificationType.EnterpriseGrantEnded, NotificationCategory.Billing, RecipientStrategy.TenantAdmins, NotificationChannel.Email, NotificationChannel.Push, NotificationType.SubscriptionPastDue, NotificationCategory.Billing, RecipientStrategy.TenantAdmins, NotificationChannel.Email, NotificationChannel.Push, NotificationChannel.Sms, NotificationType.BalanceThresholdWarning, NotificationCategory.Billing, RecipientStrategy.TenantAdmins, NotificationChannel.Email, NotificationChannel.Push, NotificationType.PlatformTierPriceIncrease, NotificationCategory.Billing, RecipientStrategy.TenantAdmins, NotificationChannel.Email, NotificationType.AgentCreated, NotificationCategory.Agents, RecipientStrategy.TenantAdmins, NotificationChannel.Email, NotificationChannel.Push, NotificationType.AgentProvisionFailed, NotificationCategory.Agents, RecipientStrategy.TenantAdmins, NotificationChannel.Email, NotificationChannel.Push, NotificationType.AgentBillingSuspended, NotificationCategory.Agents, RecipientStrategy.TenantAdmins, NotificationChannel.Email, NotificationChannel.Push, NotificationType.AgentDisconnectedExtended, NotificationCategory.Agents, RecipientStrategy.TenantAdmins, NotificationChannel.Push, NotificationType.BrowserTakeoverRequested, NotificationCategory.Agents, RecipientStrategy.TenantAdmins, NotificationChannel.Email, NotificationChannel.Push, NotificationType.InviteCreated, NotificationCategory.System, RecipientStrategy.TenantAdmins, NotificationChannel.Email, NotificationType.OrgClaimed, NotificationCategory.System, RecipientStrategy.TenantAdmins, NotificationChannel.Email, NotificationChannel.Push, NotificationType.TeamMemberAdded, NotificationCategory.Team, RecipientStrategy.SpecificUser, NotificationChannel.Email, NotificationChannel.Push, NotificationType.ProjectMemberAdded, NotificationCategory.Team, RecipientStrategy.SpecificUser, NotificationChannel.Email, NotificationChannel.Push, NotificationType.IntegrationInstalled, NotificationCategory.Integrations, RecipientStrategy.TenantAdmins, NotificationChannel.Push, NotificationType.IntegrationRemoved, NotificationCategory.Integrations, RecipientStrategy.TenantAdmins, NotificationChannel.Push;
4665
+ NotificationType.PaymentSucceeded, NotificationCategory.Billing, RecipientStrategy.TenantAdmins, NotificationChannel.Email, NotificationChannel.Push, NotificationType.PaymentFailed, NotificationCategory.Billing, RecipientStrategy.TenantAdmins, NotificationChannel.Email, NotificationChannel.Push, NotificationChannel.Sms, NotificationType.TopUpCompleted, NotificationCategory.Billing, RecipientStrategy.TenantAdmins, NotificationChannel.Email, NotificationChannel.Push, NotificationType.AutoRechargeCompleted, NotificationCategory.Billing, RecipientStrategy.TenantAdmins, NotificationChannel.Email, NotificationType.AutoRechargeFailed, NotificationCategory.Billing, RecipientStrategy.TenantAdmins, NotificationChannel.Email, NotificationChannel.Push, NotificationChannel.Sms, NotificationType.SubscriptionCreated, NotificationCategory.Billing, RecipientStrategy.TenantAdmins, NotificationChannel.Email, NotificationType.SubscriptionCancelled, NotificationCategory.Billing, RecipientStrategy.TenantAdmins, NotificationChannel.Email, NotificationChannel.Push, NotificationType.StartupGrantEnded, NotificationCategory.Billing, RecipientStrategy.TenantAdmins, NotificationChannel.Email, NotificationChannel.Push, NotificationType.GrantEnded, NotificationCategory.Billing, RecipientStrategy.TenantAdmins, NotificationChannel.Email, NotificationChannel.Push, NotificationType.EnterpriseGrantEnded, NotificationCategory.Billing, RecipientStrategy.TenantAdmins, NotificationChannel.Email, NotificationChannel.Push, NotificationType.SubscriptionPastDue, NotificationCategory.Billing, RecipientStrategy.TenantAdmins, NotificationChannel.Email, NotificationChannel.Push, NotificationChannel.Sms, NotificationType.BalanceThresholdWarning, NotificationCategory.Billing, RecipientStrategy.TenantAdmins, NotificationChannel.Email, NotificationChannel.Push, NotificationType.PlatformTierPriceIncrease, NotificationCategory.Billing, RecipientStrategy.TenantAdmins, NotificationChannel.Email, NotificationType.AgentCreated, NotificationCategory.Agents, RecipientStrategy.TenantAdmins, NotificationChannel.Email, NotificationChannel.Push, NotificationType.AgentProvisionFailed, NotificationCategory.Agents, RecipientStrategy.TenantAdmins, NotificationChannel.Email, NotificationChannel.Push, NotificationType.AgentBillingSuspended, NotificationCategory.Agents, RecipientStrategy.TenantAdmins, NotificationChannel.Email, NotificationChannel.Push, NotificationType.AgentDisconnectedExtended, NotificationCategory.Agents, RecipientStrategy.TenantAdmins, NotificationChannel.Push, NotificationType.BrowserTakeoverRequested, NotificationCategory.Agents, RecipientStrategy.TenantAdmins, NotificationChannel.Email, NotificationChannel.Push, NotificationType.InviteCreated, NotificationCategory.System, RecipientStrategy.TenantAdmins, NotificationChannel.Email, NotificationType.OrgClaimed, NotificationCategory.System, RecipientStrategy.TenantAdmins, NotificationChannel.Email, NotificationChannel.Push, NotificationType.ElevatedSupportRequested, NotificationCategory.System, RecipientStrategy.TenantAdmins, NotificationChannel.Email, NotificationChannel.Push, NotificationType.ElevatedSupportStarted, NotificationCategory.System, RecipientStrategy.TenantAdmins, NotificationChannel.Email, NotificationChannel.Push, NotificationType.TeamMemberAdded, NotificationCategory.Team, RecipientStrategy.SpecificUser, NotificationChannel.Email, NotificationChannel.Push, NotificationType.ProjectMemberAdded, NotificationCategory.Team, RecipientStrategy.SpecificUser, NotificationChannel.Email, NotificationChannel.Push, NotificationType.IntegrationInstalled, NotificationCategory.Integrations, RecipientStrategy.TenantAdmins, NotificationChannel.Push, NotificationType.IntegrationRemoved, NotificationCategory.Integrations, RecipientStrategy.TenantAdmins, NotificationChannel.Push;
4666
+ //#endregion
4667
+ //#region ../../packages-internal/api-client/dist/client.js
4668
+ /**
4669
+ * @alfe/api-client — Typed HTTP client for Alfe services.
4670
+ *
4671
+ * Platform-agnostic: works in React Native and browser environments.
4672
+ * Uses standard fetch() API — no Node.js dependencies.
4673
+ */
4674
+ var AlfeApiClient = class {
4675
+ apiBaseUrl;
4676
+ getToken;
4677
+ onAuthFailure;
4678
+ constructor(options) {
4679
+ this.apiBaseUrl = options.apiBaseUrl.replace(/\/+$/, "");
4680
+ this.getToken = options.getToken;
4681
+ this.onAuthFailure = options.onAuthFailure;
4682
+ }
4683
+ /**
4684
+ * Shared fetch logic — handles auth, 401, and network errors.
4685
+ *
4686
+ * `skipAuth` callers (public endpoints like OAuth device-code) opt out of
4687
+ * the auth-header injection AND the synthetic 401 short-circuit. Without
4688
+ * this opt-out, a CLI calling /auth/device-code (no token yet by design)
4689
+ * would never reach the network — the `getToken: () => null` path would
4690
+ * synthesize a 401 and fire onAuthFailure, breaking the entire flow.
4691
+ */
4692
+ async _fetchResponse(path, options, skipAuth = false) {
4693
+ try {
4694
+ const url = `${this.apiBaseUrl}${path}`;
4695
+ const headers = new Headers(options?.headers);
4696
+ if (typeof options?.body === "string" && !headers.has("Content-Type")) headers.set("Content-Type", "application/json");
4697
+ headers.set("x-correlation-id", correlationId());
4698
+ if (!skipAuth) {
4699
+ const token = await this.getToken();
4700
+ if (!token) {
4701
+ this.onAuthFailure?.();
4702
+ return {
4703
+ ok: false,
4704
+ result: {
4705
+ ok: false,
4706
+ error: "No auth token available",
4707
+ status: 401
4708
+ }
4709
+ };
4710
+ }
4711
+ headers.set("Authorization", `Bearer ${token}`);
4712
+ }
4713
+ const res = await fetch(url, {
4714
+ ...options,
4715
+ headers
4716
+ });
4717
+ if (res.status === 401 && !skipAuth) {
4718
+ this.onAuthFailure?.();
4719
+ return {
4720
+ ok: false,
4721
+ result: {
4722
+ ok: false,
4723
+ error: "Session expired",
4724
+ status: 401
4725
+ }
4726
+ };
4727
+ }
4728
+ if (!res.ok) {
4729
+ const text = res.status === 204 ? "" : await res.text();
4730
+ let body;
4731
+ if (text) try {
4732
+ body = JSON.parse(text);
4733
+ } catch {
4734
+ body = text;
4735
+ }
4736
+ const errBody = body;
4737
+ const errorMessage = typeof errBody === "object" ? errBody.message : void 0;
4738
+ return {
4739
+ ok: false,
4740
+ body,
4741
+ result: {
4742
+ ok: false,
4743
+ error: errorMessage ?? (typeof body === "string" ? body : `API error: ${String(res.status)}`),
4744
+ status: res.status
4745
+ }
4746
+ };
4747
+ }
4748
+ return {
4749
+ ok: true,
4750
+ res
4751
+ };
4752
+ } catch (err) {
4753
+ return {
4754
+ ok: false,
4755
+ result: {
4756
+ ok: false,
4757
+ error: err instanceof Error ? err.message : "Network error"
4758
+ }
4759
+ };
4760
+ }
4761
+ }
4762
+ async _fetch(path, options, skipAuth = false) {
4763
+ const response = await this._fetchResponse(path, options, skipAuth);
4764
+ if (!response.ok) return response;
4765
+ const text = response.res.status === 204 ? "" : await response.res.text();
4766
+ let body;
4767
+ if (text) try {
4768
+ body = JSON.parse(text);
4769
+ } catch {
4770
+ body = text;
4771
+ }
4772
+ return {
4773
+ ok: true,
4774
+ res: response.res,
4775
+ body
4776
+ };
4777
+ }
4778
+ /**
4779
+ * Make an authenticated request to an Alfe API endpoint.
4780
+ * Unwraps the @auriclabs/api-core `{ data, timestamp, requestId }` envelope.
4781
+ */
4782
+ async request(path, options) {
4783
+ const result = await this._fetch(path, options);
4784
+ if (!result.ok) return result.result;
4785
+ if (result.body === void 0) return {
4786
+ ok: true,
4787
+ data: void 0
4788
+ };
4789
+ return {
4790
+ ok: true,
4791
+ data: result.body.data
4792
+ };
4793
+ }
4794
+ /**
4795
+ * Make an authenticated request whose 403 response may ask Clerk to
4796
+ * reverify the current session. Unlike `request`, this preserves only the
4797
+ * strictly validated Clerk hint; all ordinary failures retain `ApiResult`.
4798
+ */
4799
+ async reverifiableRequest(path, options) {
4800
+ const result = await this._fetch(path, options);
4801
+ if (!result.ok) {
4802
+ if (isClerkReverificationResponse(result.body)) return createClerkReverificationResponse(result.body.clerk_error.metadata.reverification.afterMinutes);
4803
+ return result.result;
4804
+ }
4805
+ if (result.body === void 0) return {
4806
+ ok: true,
4807
+ data: void 0
4808
+ };
4809
+ return {
4810
+ ok: true,
4811
+ data: result.body.data
4812
+ };
4813
+ }
4814
+ /**
4815
+ * Make a request to a PUBLIC endpoint that does not require authentication.
4816
+ * Skips both the Authorization header injection AND the onAuthFailure
4817
+ * callback. Use for endpoints like /auth/device-code that the CLI hits
4818
+ * before it has a token.
4819
+ */
4820
+ async publicRequest(path, options) {
4821
+ const result = await this._fetch(path, options, true);
4822
+ if (!result.ok) return result.result;
4823
+ if (result.body === void 0) return {
4824
+ ok: true,
4825
+ data: void 0
4826
+ };
4827
+ return {
4828
+ ok: true,
4829
+ data: result.body.data
4830
+ };
4831
+ }
4832
+ /**
4833
+ * Make an authenticated request that returns the body directly (no envelope unwrap).
4834
+ * Use for APIs that don't use the @auriclabs/api-core response format (e.g. gateway).
4835
+ */
4836
+ async rawRequest(path, options) {
4837
+ const result = await this._fetch(path, options);
4838
+ if (!result.ok) return result.result;
4839
+ return {
4840
+ ok: true,
4841
+ data: result.body
4842
+ };
4843
+ }
4844
+ /**
4845
+ * Make an authenticated request without attempting text/JSON decoding.
4846
+ * Use for downloads such as a Teams app ZIP where UTF-8 conversion would
4847
+ * corrupt the payload.
4848
+ */
4849
+ async binaryRequest(path, options) {
4850
+ const result = await this._fetchResponse(path, options);
4851
+ if (!result.ok) return result.result;
4852
+ try {
4853
+ return {
4854
+ ok: true,
4855
+ data: {
4856
+ body: await result.res.arrayBuffer(),
4857
+ contentType: result.res.headers.get("Content-Type") ?? void 0,
4858
+ contentDisposition: result.res.headers.get("Content-Disposition") ?? void 0
4859
+ }
4860
+ };
4861
+ } catch (err) {
4862
+ return {
4863
+ ok: false,
4864
+ error: err instanceof Error ? err.message : "Failed to read binary response"
4865
+ };
4866
+ }
4867
+ }
4868
+ getApiBaseUrl() {
4869
+ return this.apiBaseUrl;
4870
+ }
4871
+ };
4586
4872
  //#endregion
4587
4873
  //#region ../../packages-internal/api-client/dist/services/auth.js
4588
4874
  var AuthService = class {
@@ -4592,7 +4878,7 @@ var AuthService = class {
4592
4878
  }
4593
4879
  prefix = "/auth";
4594
4880
  validate(token) {
4595
- return this.client.request(`${this.prefix}/validate`, {
4881
+ return this.client.publicRequest(`${this.prefix}/validate`, {
4596
4882
  method: "POST",
4597
4883
  body: JSON.stringify({ token })
4598
4884
  });
@@ -4609,6 +4895,24 @@ var AuthService = class {
4609
4895
  deleteToken(tokenId) {
4610
4896
  return this.client.request(`${this.prefix}/tokens/${encodeURIComponent(tokenId)}`, { method: "DELETE" });
4611
4897
  }
4898
+ mintPartnerDelegation(input) {
4899
+ return this.client.request(`${this.prefix}/partner-delegations`, {
4900
+ method: "POST",
4901
+ body: JSON.stringify(input)
4902
+ });
4903
+ }
4904
+ mintPartnerElevatedDelegation(input) {
4905
+ return this.client.request(`${this.prefix}/partner-delegations/elevated`, {
4906
+ method: "POST",
4907
+ body: JSON.stringify(input)
4908
+ });
4909
+ }
4910
+ refreshPartnerDelegation(delegationId) {
4911
+ return this.client.request(`${this.prefix}/partner-delegations/${encodeURIComponent(delegationId)}/refresh`, { method: "POST" });
4912
+ }
4913
+ revokePartnerDelegation(delegationId) {
4914
+ return this.client.request(`${this.prefix}/partner-delegations/${encodeURIComponent(delegationId)}`, { method: "DELETE" });
4915
+ }
4612
4916
  /**
4613
4917
  * Start a device-code flow. Called by the CLI when the user runs
4614
4918
  * `alfe login` and picks the browser path. The returned `device_code`
@@ -4967,6 +5271,12 @@ var IntegrationsService = class {
4967
5271
  listSlackChannels(agentId) {
4968
5272
  return this.client.request(`/slack/agents/${encodeURIComponent(agentId)}/channels`);
4969
5273
  }
5274
+ createSlackApp(agentId) {
5275
+ return this.client.request("/slack/apps/create", {
5276
+ method: "POST",
5277
+ body: JSON.stringify({ agentId })
5278
+ });
5279
+ }
4970
5280
  sendSlackMessage(agentId, channel, text) {
4971
5281
  return this.client.request(`/slack/agents/${encodeURIComponent(agentId)}/send`, {
4972
5282
  method: "POST",
@@ -6416,25 +6726,44 @@ var ConfigReconciler = class {
6416
6726
  status: "applied"
6417
6727
  };
6418
6728
  const failures = [];
6729
+ const inconclusiveKeys = [];
6419
6730
  for (const [key, want] of entries) try {
6420
6731
  if (getConfigRaw) {
6421
- if (await getConfigRaw(key) === want) {
6732
+ let current;
6733
+ try {
6734
+ current = await getConfigRaw(key);
6735
+ } catch (err) {
6736
+ log$4.warn({
6737
+ key,
6738
+ err: err instanceof Error ? err.message : String(err)
6739
+ }, "Config diff read failed — attempting the write");
6740
+ }
6741
+ if (current === want) {
6422
6742
  log$4.debug({ key }, "Config already at desired value — no-op");
6423
6743
  continue;
6424
6744
  }
6425
6745
  }
6426
6746
  await setConfigRaw(key, want);
6427
6747
  if (getConfigRaw) {
6428
- if (!await this.verify(getConfigRaw, key, want)) failures.push(`${key}: verify failed (read-back did not match)`);
6748
+ const outcome = await this.verify(getConfigRaw, key, want);
6749
+ if (outcome === "mismatched") failures.push(`${key}: verify failed (read-back did not match)`);
6750
+ else if (outcome === "inconclusive") inconclusiveKeys.push(key);
6429
6751
  }
6430
6752
  } catch (err) {
6431
6753
  const msg = err instanceof Error ? err.message : String(err);
6432
- if (getConfigRaw && await this.verify(getConfigRaw, key, want)) {
6433
- log$4.warn({
6434
- key,
6435
- err: msg
6436
- }, "config set errored but value landed — treating as applied");
6437
- continue;
6754
+ if (getConfigRaw) {
6755
+ const outcome = await this.verify(getConfigRaw, key, want);
6756
+ if (outcome === "matched") {
6757
+ log$4.warn({
6758
+ key,
6759
+ err: msg
6760
+ }, "config set errored but value landed — treating as applied");
6761
+ continue;
6762
+ }
6763
+ if (outcome === "inconclusive") {
6764
+ inconclusiveKeys.push(key);
6765
+ continue;
6766
+ }
6438
6767
  }
6439
6768
  failures.push(`${key}: ${msg}`);
6440
6769
  }
@@ -6451,6 +6780,13 @@ var ConfigReconciler = class {
6451
6780
  reason
6452
6781
  };
6453
6782
  }
6783
+ if (inconclusiveKeys.length > 0) {
6784
+ log$4.warn({
6785
+ version: desired.version,
6786
+ keys: inconclusiveKeys
6787
+ }, "Config reconcile verification was inconclusive — leaving version pending");
6788
+ return null;
6789
+ }
6454
6790
  log$4.info({
6455
6791
  version: desired.version,
6456
6792
  keys: entries.length
@@ -6461,11 +6797,21 @@ var ConfigReconciler = class {
6461
6797
  };
6462
6798
  }
6463
6799
  async verify(getConfigRaw, key, want) {
6800
+ let observedValue = false;
6464
6801
  for (let attempt = 0; attempt <= this.verifyRetries; attempt++) {
6465
- if (await getConfigRaw(key) === want) return true;
6802
+ try {
6803
+ const current = await getConfigRaw(key);
6804
+ if (current === want) return "matched";
6805
+ if (current !== void 0) observedValue = true;
6806
+ } catch (err) {
6807
+ log$4.debug({
6808
+ key,
6809
+ err: err instanceof Error ? err.message : String(err)
6810
+ }, "Config verify read failed");
6811
+ }
6466
6812
  if (attempt < this.verifyRetries && this.verifyRetryDelayMs > 0) await delay(this.verifyRetryDelayMs * 2 ** attempt);
6467
6813
  }
6468
- return false;
6814
+ return observedValue ? "mismatched" : "inconclusive";
6469
6815
  }
6470
6816
  };
6471
6817
  //#endregion
@@ -7735,7 +8081,7 @@ function warnFailedMcpServers(bundler, log, reason) {
7735
8081
  * List every server entry in the alfe bundler store. Lets agents inspect
7736
8082
  * what they've already registered before adding a new one.
7737
8083
  */
7738
- function handleMcpListServers(manager) {
8084
+ function handleMcpListServers(manager, liveStatusAvailable = manager?.serverStatuses !== void 0) {
7739
8085
  if (!manager) return {
7740
8086
  ok: false,
7741
8087
  error: {
@@ -7743,21 +8089,24 @@ function handleMcpListServers(manager) {
7743
8089
  message: "MCP manager not initialized"
7744
8090
  }
7745
8091
  };
7746
- const statuses = manager.serverStatuses ? manager.serverStatuses() : [];
8092
+ const statuses = liveStatusAvailable && manager.serverStatuses ? manager.serverStatuses() : [];
7747
8093
  const byName = new Map(statuses.map((s) => [s.name, s]));
7748
8094
  return {
7749
8095
  ok: true,
7750
- payload: { servers: manager.listServers().map(({ id, entry }) => ({
7751
- id,
7752
- entry: toPublicServerEntry(entry),
7753
- fingerprint: serverLaunchFingerprint(entry),
7754
- status: byName.get(id) ?? {
7755
- name: id,
7756
- connected: false,
7757
- toolCount: 0,
7758
- consecutiveFailures: 0
7759
- }
7760
- })) }
8096
+ payload: { servers: manager.listServers().map(({ id, entry }) => {
8097
+ const status = byName.get(id);
8098
+ return {
8099
+ id,
8100
+ entry: toPublicServerEntry(entry),
8101
+ fingerprint: serverLaunchFingerprint(entry),
8102
+ ...liveStatusAvailable ? { status: status ?? {
8103
+ name: id,
8104
+ connected: false,
8105
+ toolCount: 0,
8106
+ consecutiveFailures: 0
8107
+ } } : {}
8108
+ };
8109
+ }) }
7761
8110
  };
7762
8111
  }
7763
8112
  function toPublicServerEntry(entry) {
@@ -7786,10 +8135,12 @@ const MCP_ADD_WARM_TIMEOUT_MS = 12e3;
7786
8135
  * server actually works. Owned as `'manual'` so the agent can later remove it
7787
8136
  * without an expectedOwner conflict — matches what `alfe mcp add` does from the
7788
8137
  * CLI. Registration is durable regardless of the probe outcome: a probe
7789
- * failure (or a runtime with no daemon bundler, e.g. hermes) still returns
7790
- * `ok` with `connected: false`; the store watcher / runtime picks the entry up.
8138
+ * failure still returns `ok`; the store watcher / runtime picks the entry up.
8139
+ * A runtime that mirrors the store but owns its MCP children (Hermes and
8140
+ * Claude Code) has no daemon-side status authority, so it omits connected/tool
8141
+ * fields instead of presenting unknown as a definite disconnection.
7791
8142
  */
7792
- async function handleMcpAddServer(params, manager) {
8143
+ async function handleMcpAddServer(params, manager, liveStatusAvailable = manager?.warmServer !== void 0) {
7793
8144
  if (!manager) return {
7794
8145
  ok: false,
7795
8146
  error: {
@@ -7833,7 +8184,7 @@ async function handleMcpAddServer(params, manager) {
7833
8184
  };
7834
8185
  }
7835
8186
  let status = null;
7836
- if (manager.warmServer) try {
8187
+ if (liveStatusAvailable && manager.warmServer) try {
7837
8188
  status = await manager.warmServer(p.id, MCP_ADD_WARM_TIMEOUT_MS);
7838
8189
  } catch (err) {
7839
8190
  logger$1.warn({
@@ -7845,9 +8196,11 @@ async function handleMcpAddServer(params, manager) {
7845
8196
  ok: true,
7846
8197
  payload: {
7847
8198
  id: p.id,
7848
- connected: status?.connected ?? false,
7849
- toolCount: status?.toolCount ?? 0,
7850
- ...status?.lastError !== void 0 ? { error: status.lastError } : {}
8199
+ ...status ? {
8200
+ connected: status.connected,
8201
+ toolCount: status.toolCount,
8202
+ ...status.lastError !== void 0 ? { error: status.lastError } : {}
8203
+ } : {}
7851
8204
  }
7852
8205
  };
7853
8206
  }
@@ -25076,8 +25429,8 @@ function handlePluginRequest(method, params, pluginId) {
25076
25429
  case "integration.report": return Promise.resolve(handleIntegrationReport(params, pluginId));
25077
25430
  case "mcp.list_tools": return Promise.resolve(handleMcpListTools(mcpBundler));
25078
25431
  case "mcp.call_tool": return handleMcpCallTool(mcpBundler, params);
25079
- case "mcp.list_servers": return Promise.resolve(handleMcpListServers(mcpManagerRef));
25080
- case "mcp.add_server": return handleMcpAddServer(params, mcpManagerRef);
25432
+ case "mcp.list_servers": return Promise.resolve(handleMcpListServers(mcpManagerRef, mcpBundler !== null));
25433
+ case "mcp.add_server": return handleMcpAddServer(params, mcpManagerRef, mcpBundler !== null);
25081
25434
  case "mcp.remove_server": return handleMcpRemoveServer(params, mcpManagerRef);
25082
25435
  default: return Promise.resolve({
25083
25436
  ok: false,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alfe.ai/gateway",
3
- "version": "0.9.10",
3
+ "version": "0.9.12",
4
4
  "description": "Alfe local gateway daemon — persistent control plane for agent integrations",
5
5
  "type": "module",
6
6
  "bin": {
@@ -23,7 +23,7 @@
23
23
  "pino-roll": "^1.2.0",
24
24
  "smol-toml": ">=1.6.1",
25
25
  "ws": "^8.18.0",
26
- "@alfe.ai/agent-api-client": "^0.16.0",
26
+ "@alfe.ai/agent-api-client": "^0.17.0",
27
27
  "@alfe.ai/ai-proxy-local": "^0.0.16",
28
28
  "@alfe.ai/config": "^0.4.1",
29
29
  "@alfe.ai/integration-manifest": "^0.3.6",