@noodleseed/one 0.170.0 → 0.171.0

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 (105) hide show
  1. package/dist/cli.d.ts.map +1 -1
  2. package/dist/cli.js +3 -0
  3. package/dist/cli.js.map +1 -1
  4. package/dist/commands/catalog.d.ts.map +1 -1
  5. package/dist/commands/catalog.js +2 -1
  6. package/dist/commands/catalog.js.map +1 -1
  7. package/dist/commands/whatsapp-ops.d.ts +13 -0
  8. package/dist/commands/whatsapp-ops.d.ts.map +1 -0
  9. package/dist/commands/whatsapp-ops.js +244 -0
  10. package/dist/commands/whatsapp-ops.js.map +1 -0
  11. package/dist/commands/whatsapp-output.d.ts +3 -0
  12. package/dist/commands/whatsapp-output.d.ts.map +1 -0
  13. package/dist/commands/whatsapp-output.js +70 -0
  14. package/dist/commands/whatsapp-output.js.map +1 -0
  15. package/dist/dev.js +2 -0
  16. package/dist/dev.js.map +1 -1
  17. package/dist/index.d.ts +1 -1
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +1 -1
  20. package/dist/index.js.map +1 -1
  21. package/dist/plugin-mode/plugin-operation-contract.d.ts +1 -1
  22. package/node_modules/@noodle-borg/agent-kit/dist/curated/command-groups.js +4 -0
  23. package/node_modules/@noodle-borg/agent-kit/dist/curated/error-fixes.js +1 -0
  24. package/node_modules/@noodle-borg/agent-kit/dist/generated/example-files.js +1 -1
  25. package/node_modules/@noodle-borg/agent-kit/dist/generated/surface.js +3 -0
  26. package/node_modules/@noodle-borg/agent-kit/dist/skill-embedded-assistant-ref.js +6 -0
  27. package/node_modules/@noodle-borg/agent-kit/package.json +1 -1
  28. package/node_modules/@noodle-borg/app-audit/dist/audit-embedded-assistant.js +2 -0
  29. package/node_modules/@noodle-borg/assistant-gateway/dist/assistant-configuration.js +2 -1
  30. package/node_modules/@noodle-borg/assistant-gateway/dist/assistant-model-context.js +9 -4
  31. package/node_modules/@noodle-borg/assistant-gateway/dist/assistant-turn-context.d.ts +16 -0
  32. package/node_modules/@noodle-borg/assistant-gateway/dist/assistant-turn-context.js +19 -0
  33. package/node_modules/@noodle-borg/assistant-gateway/dist/channel-coordinator.d.ts +145 -0
  34. package/node_modules/@noodle-borg/assistant-gateway/dist/channel-coordinator.js +378 -0
  35. package/node_modules/@noodle-borg/assistant-gateway/dist/channel-inbox.js +160 -0
  36. package/node_modules/@noodle-borg/assistant-gateway/dist/channel-provider-blocks.d.ts +18 -0
  37. package/node_modules/@noodle-borg/assistant-gateway/dist/channel-provider-blocks.js +69 -0
  38. package/node_modules/@noodle-borg/assistant-gateway/dist/channel-spend.js +82 -0
  39. package/node_modules/@noodle-borg/assistant-gateway/dist/channel-store.d.ts +42 -0
  40. package/node_modules/@noodle-borg/assistant-gateway/dist/channel-store.js +76 -0
  41. package/node_modules/@noodle-borg/assistant-gateway/dist/channel-types.d.ts +134 -0
  42. package/node_modules/@noodle-borg/assistant-gateway/dist/channel-types.js +72 -0
  43. package/node_modules/@noodle-borg/assistant-gateway/dist/channel-work.d.ts +20 -0
  44. package/node_modules/@noodle-borg/assistant-gateway/dist/channel-work.js +221 -0
  45. package/node_modules/@noodle-borg/assistant-gateway/dist/messaging-tool-dispatch.d.ts +15 -0
  46. package/node_modules/@noodle-borg/assistant-gateway/dist/messaging-tool-dispatch.js +34 -0
  47. package/node_modules/@noodle-borg/assistant-gateway/dist/model-request.d.ts +19 -1
  48. package/node_modules/@noodle-borg/assistant-gateway/dist/model-request.js +22 -6
  49. package/node_modules/@noodle-borg/assistant-gateway/dist/portable.d.ts +7 -0
  50. package/node_modules/@noodle-borg/assistant-gateway/dist/portable.js +7 -0
  51. package/node_modules/@noodle-borg/assistant-gateway/dist/postgres-assistant-appearance-store.js +83 -0
  52. package/node_modules/@noodle-borg/assistant-gateway/dist/postgres-assistant-elevation.js +151 -0
  53. package/node_modules/@noodle-borg/assistant-gateway/dist/postgres-assistant-interaction-writes.js +45 -0
  54. package/node_modules/@noodle-borg/assistant-gateway/dist/postgres-assistant-interactions.js +340 -0
  55. package/node_modules/@noodle-borg/assistant-gateway/dist/postgres-assistant.js +439 -0
  56. package/node_modules/@noodle-borg/assistant-gateway/dist/postgres-channel-store.js +102 -0
  57. package/node_modules/@noodle-borg/assistant-gateway/dist/postgres-continuity-store.js +159 -0
  58. package/node_modules/@noodle-borg/assistant-gateway/dist/postgres-elevation-store.js +110 -0
  59. package/node_modules/@noodle-borg/assistant-gateway/dist/postgres-embed-store.js +134 -0
  60. package/node_modules/@noodle-borg/assistant-gateway/dist/postgres.js +11 -0
  61. package/node_modules/@noodle-borg/assistant-gateway/dist/public-surface.d.ts +8 -0
  62. package/node_modules/@noodle-borg/assistant-gateway/dist/public-surface.js +23 -0
  63. package/node_modules/@noodle-borg/authoring/dist/assistant.d.ts +17 -2
  64. package/node_modules/@noodle-borg/authoring/dist/assistant.js +48 -19
  65. package/node_modules/@noodle-borg/authoring/dist/index.d.ts +1 -1
  66. package/node_modules/@noodle-borg/authoring/dist/index.js +1 -1
  67. package/node_modules/@noodle-borg/authoring/dist/load-authored.js +1 -1
  68. package/node_modules/@noodle-borg/cli-catalog/dist/catalog-data-channels.js +121 -0
  69. package/node_modules/@noodle-borg/cli-catalog/dist/index.js +1 -0
  70. package/node_modules/@noodle-borg/compiler/dist/artifact/config.js +1 -1
  71. package/node_modules/@noodle-borg/compiler/dist/errors.d.ts +1 -1
  72. package/node_modules/@noodle-borg/compiler/dist/manifest/schema.d.ts +72 -12
  73. package/node_modules/@noodle-borg/compiler/dist/manifest/schema.js +51 -27
  74. package/node_modules/@noodle-borg/compiler/dist/manifest/website-projection.d.ts +1 -0
  75. package/node_modules/@noodle-borg/compiler/dist/manifest/website-projection.js +19 -0
  76. package/node_modules/@noodle-borg/module-audit/dist/index.js +37 -0
  77. package/node_modules/@noodle-borg/module-audit/dist/postgres-audit.js +126 -0
  78. package/node_modules/@noodle-borg/module-audit/dist/route.js +66 -0
  79. package/node_modules/@noodle-borg/runtime/dist/managed-origins.js +8 -4
  80. package/node_modules/@noodle-borg/runtime/dist/postgres-state-handles.js +311 -0
  81. package/node_modules/@noodle-borg/runtime/dist/postgres.js +4 -0
  82. package/node_modules/@noodle-borg/service/dist/assistant-composition.js +18 -0
  83. package/node_modules/@noodle-borg/service/dist/build-info.js +5 -2
  84. package/node_modules/@noodle-borg/service/dist/business-information/source-ingestion-coordinator.js +7 -0
  85. package/node_modules/@noodle-borg/service/dist/channels/360dialog.js +233 -0
  86. package/node_modules/@noodle-borg/service/dist/channels/cipher.js +37 -0
  87. package/node_modules/@noodle-borg/service/dist/channels/composition.js +37 -0
  88. package/node_modules/@noodle-borg/service/dist/channels/inference-guard.js +53 -0
  89. package/node_modules/@noodle-borg/service/dist/channels/runtime.js +337 -0
  90. package/node_modules/@noodle-borg/service/dist/channels/worker-loop.js +50 -0
  91. package/node_modules/@noodle-borg/service/dist/routes/assistant-agent.js +62 -34
  92. package/node_modules/@noodle-borg/service/dist/routes/assistant-model-binding.js +6 -2
  93. package/node_modules/@noodle-borg/service/dist/routes/business-information-channels.js +14 -1
  94. package/node_modules/@noodle-borg/service/dist/routes/whatsapp.js +238 -0
  95. package/node_modules/@noodle-borg/service/dist/service.js +30 -32
  96. package/node_modules/@noodle-borg/service/package.json +1 -1
  97. package/node_modules/@noodle-borg/wire-contracts/dist/application-channels.d.ts +33 -0
  98. package/node_modules/@noodle-borg/wire-contracts/dist/application-channels.js +9 -0
  99. package/node_modules/@noodle-borg/wire-contracts/dist/index.d.ts +1 -0
  100. package/node_modules/@noodle-borg/wire-contracts/dist/index.js +1 -0
  101. package/node_modules/@noodle-borg/wire-contracts/dist/service-info.d.ts +2 -0
  102. package/node_modules/@noodle-borg/wire-contracts/dist/service-info.js +10 -2
  103. package/node_modules/@noodle-borg/wire-contracts/dist/whatsapp.d.ts +426 -0
  104. package/node_modules/@noodle-borg/wire-contracts/dist/whatsapp.js +213 -0
  105. package/package.json +2 -2
@@ -0,0 +1,238 @@
1
+ import { ChannelError, channelParticipantId, recordChannelDelivery, } from '@noodle-borg/assistant-gateway/portable';
2
+ import { readJsonBody, sendJson } from '@noodle-borg/transport-http';
3
+ import * as wire from '@noodle-borg/wire-contracts';
4
+ import { parseWhatsAppWebhook, verifyWhatsAppWebhookSecret, WHATSAPP_CALLBACK_HEADER, } from '../channels/360dialog.js';
5
+ import { authorizeTenantControl, developerGrantRouteAccess } from './control-plane.js';
6
+ const participant = /^p_[a-f0-9]{64}$/;
7
+ const eventId = /^e_[0-9_]+$/;
8
+ function eventView(event) {
9
+ return {
10
+ id: event.id,
11
+ participantId: event.participantId,
12
+ state: event.state,
13
+ eventAt: event.eventAt,
14
+ receivedAt: event.receivedAt,
15
+ attempts: event.attempts,
16
+ deploymentId: event.deploymentId,
17
+ ...(event.code ? { code: event.code } : {}),
18
+ };
19
+ }
20
+ function respond(res, schema, data) {
21
+ sendJson(res, 200, schema.parse({ ok: true, data }));
22
+ }
23
+ async function body(req, schema) {
24
+ const input = await readJsonBody(req, 16_384);
25
+ if (!input.ok)
26
+ throw new ChannelError('request_invalid');
27
+ const parsed = schema.safeParse(input.value);
28
+ if (!parsed.success)
29
+ throw new ChannelError('request_invalid');
30
+ return parsed.data;
31
+ }
32
+ export function dispatchWhatsAppRoutes(req, res, url, runtime, grants) {
33
+ const callback = /^\/v1\/channels\/whatsapp\/webhooks\/([a-z0-9-]+)$/.exec(url.pathname);
34
+ const operator = /^\/v1\/orgs\/([^/]+)\/apps\/([^/]+)\/envs\/([^/]+)\/channels\/whatsapp(?:\/(.*))?$/.exec(url.pathname);
35
+ if (!callback && !operator)
36
+ return false;
37
+ res.setHeader('Cache-Control', 'no-store');
38
+ if (!runtime) {
39
+ sendJson(res, 503, {
40
+ error: 'WhatsApp is unavailable on this service',
41
+ code: 'whatsapp_unavailable',
42
+ });
43
+ return true;
44
+ }
45
+ const run = Promise.resolve().then(() => callback
46
+ ? webhook(req, res, callback[1], runtime)
47
+ : operate(req, res, url, {
48
+ org: decodeURIComponent(operator[1]),
49
+ app: decodeURIComponent(operator[2]),
50
+ env: decodeURIComponent(operator[3]),
51
+ }, operator[4] ?? '', runtime, grants));
52
+ void run.catch((error) => {
53
+ const code = error instanceof ChannelError ? error.code : 'channel_unavailable';
54
+ const status = code === 'channel_not_found'
55
+ ? 404
56
+ : [
57
+ 'revision_conflict',
58
+ 'idempotency_conflict',
59
+ 'asset_in_use',
60
+ 'not_ready',
61
+ 'deployment_changed',
62
+ ].includes(code)
63
+ ? 409
64
+ : code === 'channel_unavailable' || code === 'ingress_capacity'
65
+ ? 503
66
+ : 400;
67
+ if (!res.headersSent)
68
+ sendJson(res, status, { error: code, code });
69
+ });
70
+ return true;
71
+ }
72
+ async function webhook(req, res, id, runtime) {
73
+ if (req.method !== 'POST')
74
+ return sendJson(res, 405, { error: 'method_not_allowed' });
75
+ const binding = await runtime.channels.internal(id);
76
+ const { webhookSecret } = await runtime.provider(binding);
77
+ if (!verifyWhatsAppWebhookSecret(req.headers[WHATSAPP_CALLBACK_HEADER], webhookSecret))
78
+ return sendJson(res, 401, { error: 'unauthorized' });
79
+ const input = await readJsonBody(req, 1 << 20);
80
+ if (!input.ok)
81
+ return sendJson(res, input.status, { error: 'webhook_invalid' });
82
+ const batch = parseWhatsAppWebhook(input.value, binding.phoneNumberId);
83
+ await runtime.channels.receive(id, batch.messages, await runtime.retention(binding));
84
+ for (const status of batch.statuses)
85
+ await recordChannelDelivery(runtime.channels.store, id, status.providerMessageId, status.state, runtime.channels.now());
86
+ sendJson(res, 200, { ok: true });
87
+ runtime.options.worker.wake();
88
+ }
89
+ async function operate(req, res, url, tenant, path, runtime, grants) {
90
+ const { deps, channels } = runtime;
91
+ const read = req.method === 'GET';
92
+ const identity = await authorizeTenantControl(req, res, deps.gate, deps.controlPlane, tenant.org, developerGrantRouteAccess(grants, read ? 'cloud:read' : 'config:write'));
93
+ if (identity === false)
94
+ return;
95
+ const member = await deps.controlPlane.getOrgMember({
96
+ org: tenant.org,
97
+ subject: identity.subject,
98
+ });
99
+ if (!read && !identity.superAdmin && member?.role !== 'owner' && member?.role !== 'developer')
100
+ return sendJson(res, 403, { error: 'operator_required' });
101
+ const key = req.headers['idempotency-key'];
102
+ if (!read && (typeof key !== 'string' || !key || key.length > 200))
103
+ throw new ChannelError('idempotency_key_invalid');
104
+ const idempotency = typeof key === 'string' ? key : '';
105
+ const visible = await channels.get(tenant);
106
+ const audit = async (action) => deps.audit.emit({
107
+ eventType: `assistant.channel.${action}`,
108
+ ...tenant,
109
+ actorSubject: identity.subject,
110
+ decision: 'allow',
111
+ status: 200,
112
+ });
113
+ if (!path && req.method === 'GET')
114
+ return respond(res, wire.WhatsAppBindingResponseSchema, visible ?? null);
115
+ if (!path && req.method === 'PUT') {
116
+ const parsed = await body(req, wire.WhatsAppConfigureRequestSchema);
117
+ const target = await deps.registry.getActiveByTenant(tenant);
118
+ if (!target?.deploymentId)
119
+ throw new ChannelError('deployment_missing');
120
+ const { expectedRevision, ...configuration } = parsed;
121
+ const configured = await channels.configure({ ...configuration, tenant, deploymentId: target.deploymentId }, identity.subject, idempotency, expectedRevision);
122
+ await audit('configured');
123
+ return respond(res, wire.WhatsAppBindingResponseSchema, configured);
124
+ }
125
+ if (!visible)
126
+ throw new ChannelError('channel_not_found');
127
+ const id = visible.id;
128
+ if (path === 'readiness' && req.method === 'POST') {
129
+ await body(req, wire.WhatsAppEmptyRequestSchema);
130
+ return respond(res, wire.WhatsAppReadinessResponseSchema, await runtime.doctor(id, req));
131
+ }
132
+ if ((path === 'state' && req.method === 'PATCH') || (!path && req.method === 'DELETE')) {
133
+ const parsed = path
134
+ ? await body(req, wire.WhatsAppStateRequestSchema)
135
+ : {
136
+ ...(await body(req, wire.WhatsAppRevisionRequestSchema)),
137
+ state: 'disconnected',
138
+ };
139
+ const updated = await channels.setState(id, parsed.state, identity.subject, idempotency, parsed.expectedRevision);
140
+ await audit(parsed.state);
141
+ return respond(res, wire.WhatsAppBindingResponseSchema, updated);
142
+ }
143
+ if (path === 'limits' && req.method === 'GET')
144
+ return respond(res, wire.WhatsAppBindingResponseSchema, visible);
145
+ if (path === 'limits' && req.method === 'PATCH') {
146
+ const parsed = await body(req, wire.WhatsAppLimitsRequestSchema);
147
+ const updated = await channels.updateLimits(id, parsed.limits, identity.subject, idempotency, parsed.expectedRevision);
148
+ await audit('limits_changed');
149
+ return respond(res, wire.WhatsAppBindingResponseSchema, updated);
150
+ }
151
+ if (path === 'usage' && read)
152
+ return respond(res, wire.WhatsAppUsageResponseSchema, await channels.usage(id));
153
+ if (path === 'webhook' && (read || req.method === 'POST')) {
154
+ if (!read) {
155
+ const parsed = await body(req, wire.WhatsAppRevisionRequestSchema);
156
+ if (parsed.expectedRevision !== visible.revision)
157
+ throw new ChannelError('revision_conflict');
158
+ await runtime.configureWebhook(id, req);
159
+ await audit('webhook_configured');
160
+ }
161
+ return respond(res, wire.WhatsAppWebhookResponseSchema, await runtime.webhook(id, req));
162
+ }
163
+ if (path === 'blocks' && read) {
164
+ const after = url.searchParams.get('after') ?? undefined;
165
+ if (after && !participant.test(after))
166
+ throw new ChannelError('request_invalid');
167
+ return sendJson(res, 200, wire.WhatsAppBlocksResponseSchema.parse({ ok: true, ...(await runtime.blocks(id, after)) }));
168
+ }
169
+ if (path === 'blocks' && req.method === 'POST') {
170
+ const parsed = await body(req, wire.WhatsAppBlockRequestSchema);
171
+ const participantId = parsed.participantId ??
172
+ channelParticipantId(await channels.internal(id), {
173
+ kind: 'phone',
174
+ value: parsed.phone.slice(1),
175
+ });
176
+ await channels.block(id, participantId, identity.subject, idempotency, parsed.until);
177
+ if (parsed.scope === 'provider')
178
+ await runtime.providerBlock(id, participantId, true, identity.subject, idempotency);
179
+ await audit('blocked');
180
+ return sendJson(res, 200, wire.WhatsAppMutationResponseSchema.parse({ ok: true }));
181
+ }
182
+ const [kind, value, action] = path.split('/');
183
+ if (kind === 'blocks' && value && participant.test(value) && req.method === 'DELETE') {
184
+ const parsed = await body(req, wire.WhatsAppUnblockRequestSchema);
185
+ if (parsed.scope === 'provider')
186
+ await runtime.providerBlock(id, value, false, identity.subject, idempotency, parsed.phone);
187
+ else
188
+ await channels.unblock(id, value, identity.subject, idempotency);
189
+ await audit('unblocked');
190
+ return sendJson(res, 200, { ok: true });
191
+ }
192
+ if (kind === 'cooldowns' &&
193
+ value &&
194
+ participant.test(value) &&
195
+ (read || req.method === 'DELETE')) {
196
+ if (!read) {
197
+ await body(req, wire.WhatsAppEmptyRequestSchema);
198
+ await channels.clearCooldown(id, value, identity.subject, idempotency);
199
+ await audit('cooldown_cleared');
200
+ }
201
+ return respond(res, wire.WhatsAppCooldownResponseSchema, await channels.cooldown(id, value));
202
+ }
203
+ if (kind === 'participants' &&
204
+ value &&
205
+ participant.test(value) &&
206
+ action === 'forget' &&
207
+ req.method === 'POST') {
208
+ await body(req, wire.WhatsAppEmptyRequestSchema);
209
+ await channels.forget(id, value, identity.subject, idempotency);
210
+ await audit('conversation_forgotten');
211
+ return sendJson(res, 200, { ok: true });
212
+ }
213
+ if (kind === 'events' &&
214
+ (!value || eventId.test(value)) &&
215
+ (read || (action === 'reconcile' && req.method === 'POST'))) {
216
+ if (!read) {
217
+ await body(req, wire.WhatsAppEmptyRequestSchema);
218
+ await audit('receipt_inspected');
219
+ }
220
+ if (value) {
221
+ const event = await channels.event(id, value);
222
+ if (!event)
223
+ throw new ChannelError('event_not_found');
224
+ return respond(res, wire.WhatsAppEventsResponseSchema, [eventView(event)]);
225
+ }
226
+ const after = url.searchParams.get('after') ?? undefined;
227
+ if (after && !eventId.test(after))
228
+ throw new ChannelError('cursor_invalid');
229
+ const events = await channels.events(id, after);
230
+ return sendJson(res, 200, wire.WhatsAppEventsResponseSchema.parse({
231
+ ok: true,
232
+ data: events.map(eventView),
233
+ ...(events.length === 100 ? { next: events.at(-1).id } : {}),
234
+ }));
235
+ }
236
+ sendJson(res, 404, { error: 'not_found' });
237
+ }
238
+ //# sourceMappingURL=whatsapp.js.map
@@ -11,9 +11,11 @@ import { createApplicationServingRuntime } from './application-runtime-target.js
11
11
  import { createArchivePreflight } from './archive-preflight.js';
12
12
  import { ArchiveSweeper, resolveArchiveRetentionDays } from './archive-sweeper.js';
13
13
  import { serveLocalAsset } from './assets.js';
14
+ import { assistantRouteDependencies } from './assistant-composition.js';
14
15
  import { resolveBuildInfo, serviceInfoPayload } from './build-info.js';
15
16
  import { createBusinessInformationRuntime, resolvePrivateInstallationDefinition, } from './business-information-runtime.js';
16
17
  import { serviceCapabilityReport } from './capabilities.js';
18
+ import { createWhatsAppRuntime } from './channels/composition.js';
17
19
  import { rejectIncompatibleCli } from './client-compatibility.js';
18
20
  import { createDataPlaneMembershipAuthorizer } from './data-plane-membership.js';
19
21
  import { DEVELOPER_MCP_PATH, handleDeveloperMcpRequest } from './developer-mcp/mount.js';
@@ -50,6 +52,7 @@ import { dispatchProtectedResourceMetadata } from './routes/protected-resource-m
50
52
  import { dispatchResourceReads } from './routes/resource-read-dispatch.js';
51
53
  import { handleRollback } from './routes/rollback.js';
52
54
  import { createServicePrincipalDispatcher } from './routes/service-principals-dispatch.js';
55
+ import { dispatchWhatsAppRoutes } from './routes/whatsapp.js';
53
56
  import { servicePrincipalDataPlaneHooks } from './service-principal-data-plane.js';
54
57
  import { InMemoryAlertRuleStore } from './store/alert-rules.js';
55
58
  import { StdoutAuditSink } from './store/audit.js';
@@ -181,6 +184,23 @@ export function createServiceHandler(registry, options = {}) {
181
184
  ? {}
182
185
  : { developerGrants: options.developerGrantStore }),
183
186
  };
187
+ const assistantDeps = assistantRouteDependencies(options, {
188
+ registry,
189
+ resolveRuntimeTarget,
190
+ store: assistantStore,
191
+ appearance: assistantAppearance,
192
+ gate,
193
+ controlPlane,
194
+ audit: activeAudit,
195
+ maxBody,
196
+ serviceBase: (request) => options.publicBaseUrl ?? baseFromRequest(request, tls),
197
+ logger,
198
+ });
199
+ const whatsapp = createWhatsAppRuntime(options.whatsapp, assistantDeps, {
200
+ allowance: moduleHost.resolveActivityHistoryAllowance,
201
+ installations: businessInformationStore,
202
+ activity,
203
+ });
184
204
  return (req, res) => {
185
205
  const url = new URL(req.url ?? '/', 'http://localhost');
186
206
  // Liveness/readiness probes: un-gated and **not** HTTPS-enforced — Cloud Run's internal probe is plain
@@ -212,7 +232,7 @@ export function createServiceHandler(registry, options = {}) {
212
232
  // post-deploy smoke can confirm exactly which commit is live. Non-sensitive fields only.
213
233
  if (req.method === 'GET' && url.pathname === '/v1/service/info') {
214
234
  applySecurityHeaders(res, tls);
215
- return sendJson(res, 200, serviceInfoPayload(buildInfo, options.developerMcp === true));
235
+ return sendJson(res, 200, serviceInfoPayload(buildInfo, options.developerMcp === true, whatsapp !== undefined));
216
236
  }
217
237
  if (url.pathname.startsWith('/v1/') && rejectIncompatibleCli(req, res, buildInfo, tls))
218
238
  return;
@@ -285,6 +305,7 @@ export function createServiceHandler(registry, options = {}) {
285
305
  if (businessInformationStore !== undefined &&
286
306
  options.businessInformationEnabled !== false &&
287
307
  dispatchBusinessInformationRoutes(req, res, url, {
308
+ ...(whatsapp ? { readWhatsApp: whatsapp.projection.bind(whatsapp) } : {}),
288
309
  store: businessInformationStore,
289
310
  ...(businessOnboarding ? { businessOnboarding } : {}),
290
311
  activateInstallation,
@@ -330,38 +351,15 @@ export function createServiceHandler(registry, options = {}) {
330
351
  })) {
331
352
  return;
332
353
  }
354
+ if (url.pathname.includes('/channels/whatsapp')) {
355
+ applySecurityHeaders(res, tls);
356
+ if (enforceHttps(req, res, tls))
357
+ return;
358
+ if (dispatchWhatsAppRoutes(req, res, url, whatsapp, options.developerGrantStore))
359
+ return;
360
+ }
333
361
  if (dispatchAssistantRoutes(req, res, url, {
334
- registry,
335
- resolveRuntimeTarget,
336
- store: assistantStore,
337
- appearance: assistantAppearance,
338
- ...(options.publicEmbeds !== undefined ? { publicEmbeds: options.publicEmbeds } : {}),
339
- ...(options.elevations !== undefined ? { elevations: options.elevations } : {}),
340
- ...(options.elevationCoordinator !== undefined
341
- ? { elevationCoordinator: options.elevationCoordinator }
342
- : {}),
343
- ...(options.admissionCounters !== undefined
344
- ? { admissionCounters: options.admissionCounters }
345
- : {}),
346
- ...(options.admissionEnvelope !== undefined
347
- ? { admissionEnvelope: options.admissionEnvelope }
348
- : {}),
349
- gate,
350
- controlPlane,
351
- audit: activeAudit,
352
- maxBody,
353
- serviceBase: (request) => options.publicBaseUrl ?? baseFromRequest(request, tls),
354
- ...(options.assistantModelFetch !== undefined
355
- ? { modelFetch: options.assistantModelFetch }
356
- : {}),
357
- ...(options.managedAssistantModelResolver !== undefined
358
- ? { managedModelResolver: options.managedAssistantModelResolver }
359
- : {}),
360
- ...(options.captureRequestEvent !== undefined
361
- ? { captureRequestEvent: options.captureRequestEvent }
362
- : {}),
363
- ...(options.clock !== undefined ? { clock: options.clock } : {}),
364
- logger,
362
+ ...assistantDeps,
365
363
  applySecurityHeaders,
366
364
  enforceHttps,
367
365
  sendJson,
@@ -40,7 +40,7 @@
40
40
  "@noodle-borg/managed-capabilities": "0.0.0",
41
41
  "@modelcontextprotocol/sdk": "^1.29.0",
42
42
  "@noodle-borg/admission-limits": "0.0.0",
43
- "@noodle-borg/agent-kit": "0.105.0",
43
+ "@noodle-borg/agent-kit": "0.106.0",
44
44
  "@noodle-borg/app-package": "0.0.0",
45
45
  "@noodle-borg/assistant-gateway": "0.0.0",
46
46
  "@noodle-borg/auth": "0.0.0",
@@ -37,6 +37,17 @@ export declare const ApplicationChannelsProjectionSchema: z.ZodObject<{
37
37
  }, z.core.$strict>>;
38
38
  }, z.core.$strict>>;
39
39
  }, z.core.$strict>;
40
+ whatsapp: z.ZodOptional<z.ZodObject<{
41
+ status: z.ZodEnum<{
42
+ unavailable: "unavailable";
43
+ ready: "ready";
44
+ paused: "paused";
45
+ }>;
46
+ bindingId: z.ZodOptional<z.ZodString>;
47
+ revision: z.ZodOptional<z.ZodNumber>;
48
+ capabilities: z.ZodArray<z.ZodString>;
49
+ reason: z.ZodOptional<z.ZodString>;
50
+ }, z.core.$strict>>;
40
51
  }, z.core.$strict>;
41
52
  export type ApplicationChannelsProjection = z.infer<typeof ApplicationChannelsProjectionSchema>;
42
53
  export declare const ApplicationChannelsResponseSchema: z.ZodObject<{
@@ -79,6 +90,17 @@ export declare const ApplicationChannelsResponseSchema: z.ZodObject<{
79
90
  }, z.core.$strict>>;
80
91
  }, z.core.$strict>>;
81
92
  }, z.core.$strict>;
93
+ whatsapp: z.ZodOptional<z.ZodObject<{
94
+ status: z.ZodEnum<{
95
+ unavailable: "unavailable";
96
+ ready: "ready";
97
+ paused: "paused";
98
+ }>;
99
+ bindingId: z.ZodOptional<z.ZodString>;
100
+ revision: z.ZodOptional<z.ZodNumber>;
101
+ capabilities: z.ZodArray<z.ZodString>;
102
+ reason: z.ZodOptional<z.ZodString>;
103
+ }, z.core.$strict>>;
82
104
  }, z.core.$strict>;
83
105
  }, z.core.$strict>;
84
106
  export declare const ApplicationChannelsClientResponseSchema: z.ZodObject<{
@@ -97,6 +119,17 @@ export declare const ApplicationChannelsClientResponseSchema: z.ZodObject<{
97
119
  url: z.ZodOptional<z.ZodURL>;
98
120
  accessMode: z.ZodOptional<z.ZodString>;
99
121
  }, z.core.$strip>;
122
+ whatsapp: z.ZodOptional<z.ZodObject<{
123
+ status: z.ZodEnum<{
124
+ unavailable: "unavailable";
125
+ ready: "ready";
126
+ paused: "paused";
127
+ }>;
128
+ bindingId: z.ZodOptional<z.ZodString>;
129
+ revision: z.ZodOptional<z.ZodNumber>;
130
+ capabilities: z.ZodArray<z.ZodString>;
131
+ reason: z.ZodOptional<z.ZodString>;
132
+ }, z.core.$strip>>;
100
133
  assistant: z.ZodObject<{
101
134
  status: z.ZodEnum<{
102
135
  unavailable: "unavailable";
@@ -39,6 +39,13 @@ const assistant = z
39
39
  usage: usage.optional(),
40
40
  })
41
41
  .strict();
42
+ const whatsapp = z.strictObject({
43
+ status,
44
+ bindingId: z.string().max(256).optional(),
45
+ revision: z.number().int().positive().optional(),
46
+ capabilities: z.array(z.string().max(256)).max(64),
47
+ reason: z.string().max(200).optional(),
48
+ });
42
49
  export const ApplicationChannelsProjectionSchema = z
43
50
  .object({
44
51
  revision: z.number().int().positive(),
@@ -47,6 +54,7 @@ export const ApplicationChannelsProjectionSchema = z
47
54
  deploymentId: z.string().min(1).max(256).optional(),
48
55
  mcp,
49
56
  assistant,
57
+ whatsapp: whatsapp.optional(),
50
58
  })
51
59
  .strict();
52
60
  export const ApplicationChannelsResponseSchema = z
@@ -57,6 +65,7 @@ export const ApplicationChannelsClientResponseSchema = z
57
65
  ok: z.literal(true),
58
66
  data: ApplicationChannelsProjectionSchema.extend({
59
67
  mcp: mcp.strip(),
68
+ whatsapp: whatsapp.strip().optional(),
60
69
  assistant: assistant
61
70
  .extend({
62
71
  usage: usage
@@ -393,4 +393,5 @@ export * from './billing-catalog.js';
393
393
  export * from './deployment-deletion.js';
394
394
  export * from './operation-coordination.js';
395
395
  export { MIXED_CUSTOMER_AUTH_FEATURE_VERSION, type ServiceInfoClientResponse, type ServiceInfoResponse, serviceInfoClientResponseSchema, serviceInfoResponseSchema, } from './service-info.js';
396
+ export * from './whatsapp.js';
396
397
  //# sourceMappingURL=index.d.ts.map
@@ -232,4 +232,5 @@ export * from './billing-catalog.js';
232
232
  export * from './deployment-deletion.js';
233
233
  export * from './operation-coordination.js';
234
234
  export { MIXED_CUSTOMER_AUTH_FEATURE_VERSION, serviceInfoClientResponseSchema, serviceInfoResponseSchema, } from './service-info.js';
235
+ export * from './whatsapp.js';
235
236
  //# sourceMappingURL=index.js.map
@@ -7,6 +7,7 @@ export declare const serviceInfoResponseSchema: z.ZodObject<{
7
7
  }, z.core.$strict>>;
8
8
  features: z.ZodObject<{
9
9
  mixedCustomerAuth: z.ZodLiteral<1>;
10
+ whatsapp: z.ZodOptional<z.ZodLiteral<1>>;
10
11
  }, z.core.$strict>;
11
12
  ok: z.ZodLiteral<true>;
12
13
  status: z.ZodLiteral<"ok">;
@@ -25,6 +26,7 @@ export declare const serviceInfoClientResponseSchema: z.ZodObject<{
25
26
  }, z.core.$strip>>;
26
27
  features: z.ZodOptional<z.ZodObject<{
27
28
  mixedCustomerAuth: z.ZodOptional<z.ZodNumber>;
29
+ whatsapp: z.ZodOptional<z.ZodNumber>;
28
30
  }, z.core.$strip>>;
29
31
  ok: z.ZodLiteral<true>;
30
32
  status: z.ZodLiteral<"ok">;
@@ -15,12 +15,20 @@ const buildInfoShape = {
15
15
  export const serviceInfoResponseSchema = z.strictObject({
16
16
  ...buildInfoShape,
17
17
  developerPlugin: z.strictObject({ mcpCapabilityVersion: z.string().min(1) }).optional(),
18
- features: z.strictObject({ mixedCustomerAuth: z.literal(MIXED_CUSTOMER_AUTH_FEATURE_VERSION) }),
18
+ features: z.strictObject({
19
+ mixedCustomerAuth: z.literal(MIXED_CUSTOMER_AUTH_FEATURE_VERSION),
20
+ whatsapp: z.literal(1).optional(),
21
+ }),
19
22
  });
20
23
  /** Older services may omit features; future fields are stripped at each structured metadata layer. */
21
24
  export const serviceInfoClientResponseSchema = z.object({
22
25
  ...buildInfoShape,
23
26
  developerPlugin: z.object({ mcpCapabilityVersion: z.string().min(1) }).optional(),
24
- features: z.object({ mixedCustomerAuth: z.number().int().positive().optional() }).optional(),
27
+ features: z
28
+ .object({
29
+ mixedCustomerAuth: z.number().int().positive().optional(),
30
+ whatsapp: z.number().int().positive().optional(),
31
+ })
32
+ .optional(),
25
33
  });
26
34
  //# sourceMappingURL=service-info.js.map