@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.
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +3 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/catalog.d.ts.map +1 -1
- package/dist/commands/catalog.js +2 -1
- package/dist/commands/catalog.js.map +1 -1
- package/dist/commands/whatsapp-ops.d.ts +13 -0
- package/dist/commands/whatsapp-ops.d.ts.map +1 -0
- package/dist/commands/whatsapp-ops.js +244 -0
- package/dist/commands/whatsapp-ops.js.map +1 -0
- package/dist/commands/whatsapp-output.d.ts +3 -0
- package/dist/commands/whatsapp-output.d.ts.map +1 -0
- package/dist/commands/whatsapp-output.js +70 -0
- package/dist/commands/whatsapp-output.js.map +1 -0
- package/dist/dev.js +2 -0
- package/dist/dev.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/plugin-mode/plugin-operation-contract.d.ts +1 -1
- package/node_modules/@noodle-borg/agent-kit/dist/curated/command-groups.js +4 -0
- package/node_modules/@noodle-borg/agent-kit/dist/curated/error-fixes.js +1 -0
- package/node_modules/@noodle-borg/agent-kit/dist/generated/example-files.js +1 -1
- package/node_modules/@noodle-borg/agent-kit/dist/generated/surface.js +3 -0
- package/node_modules/@noodle-borg/agent-kit/dist/skill-embedded-assistant-ref.js +6 -0
- package/node_modules/@noodle-borg/agent-kit/package.json +1 -1
- package/node_modules/@noodle-borg/app-audit/dist/audit-embedded-assistant.js +2 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/assistant-configuration.js +2 -1
- package/node_modules/@noodle-borg/assistant-gateway/dist/assistant-model-context.js +9 -4
- package/node_modules/@noodle-borg/assistant-gateway/dist/assistant-turn-context.d.ts +16 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/assistant-turn-context.js +19 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/channel-coordinator.d.ts +145 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/channel-coordinator.js +378 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/channel-inbox.js +160 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/channel-provider-blocks.d.ts +18 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/channel-provider-blocks.js +69 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/channel-spend.js +82 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/channel-store.d.ts +42 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/channel-store.js +76 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/channel-types.d.ts +134 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/channel-types.js +72 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/channel-work.d.ts +20 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/channel-work.js +221 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/messaging-tool-dispatch.d.ts +15 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/messaging-tool-dispatch.js +34 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/model-request.d.ts +19 -1
- package/node_modules/@noodle-borg/assistant-gateway/dist/model-request.js +22 -6
- package/node_modules/@noodle-borg/assistant-gateway/dist/portable.d.ts +7 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/portable.js +7 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/postgres-assistant-appearance-store.js +83 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/postgres-assistant-elevation.js +151 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/postgres-assistant-interaction-writes.js +45 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/postgres-assistant-interactions.js +340 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/postgres-assistant.js +439 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/postgres-channel-store.js +102 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/postgres-continuity-store.js +159 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/postgres-elevation-store.js +110 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/postgres-embed-store.js +134 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/postgres.js +11 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/public-surface.d.ts +8 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/public-surface.js +23 -0
- package/node_modules/@noodle-borg/authoring/dist/assistant.d.ts +17 -2
- package/node_modules/@noodle-borg/authoring/dist/assistant.js +48 -19
- package/node_modules/@noodle-borg/authoring/dist/index.d.ts +1 -1
- package/node_modules/@noodle-borg/authoring/dist/index.js +1 -1
- package/node_modules/@noodle-borg/authoring/dist/load-authored.js +1 -1
- package/node_modules/@noodle-borg/cli-catalog/dist/catalog-data-channels.js +121 -0
- package/node_modules/@noodle-borg/cli-catalog/dist/index.js +1 -0
- package/node_modules/@noodle-borg/compiler/dist/artifact/config.js +1 -1
- package/node_modules/@noodle-borg/compiler/dist/errors.d.ts +1 -1
- package/node_modules/@noodle-borg/compiler/dist/manifest/schema.d.ts +72 -12
- package/node_modules/@noodle-borg/compiler/dist/manifest/schema.js +51 -27
- package/node_modules/@noodle-borg/compiler/dist/manifest/website-projection.d.ts +1 -0
- package/node_modules/@noodle-borg/compiler/dist/manifest/website-projection.js +19 -0
- package/node_modules/@noodle-borg/module-audit/dist/index.js +37 -0
- package/node_modules/@noodle-borg/module-audit/dist/postgres-audit.js +126 -0
- package/node_modules/@noodle-borg/module-audit/dist/route.js +66 -0
- package/node_modules/@noodle-borg/runtime/dist/managed-origins.js +8 -4
- package/node_modules/@noodle-borg/runtime/dist/postgres-state-handles.js +311 -0
- package/node_modules/@noodle-borg/runtime/dist/postgres.js +4 -0
- package/node_modules/@noodle-borg/service/dist/assistant-composition.js +18 -0
- package/node_modules/@noodle-borg/service/dist/build-info.js +5 -2
- package/node_modules/@noodle-borg/service/dist/business-information/source-ingestion-coordinator.js +7 -0
- package/node_modules/@noodle-borg/service/dist/channels/360dialog.js +233 -0
- package/node_modules/@noodle-borg/service/dist/channels/cipher.js +37 -0
- package/node_modules/@noodle-borg/service/dist/channels/composition.js +37 -0
- package/node_modules/@noodle-borg/service/dist/channels/inference-guard.js +53 -0
- package/node_modules/@noodle-borg/service/dist/channels/runtime.js +337 -0
- package/node_modules/@noodle-borg/service/dist/channels/worker-loop.js +50 -0
- package/node_modules/@noodle-borg/service/dist/routes/assistant-agent.js +62 -34
- package/node_modules/@noodle-borg/service/dist/routes/assistant-model-binding.js +6 -2
- package/node_modules/@noodle-borg/service/dist/routes/business-information-channels.js +14 -1
- package/node_modules/@noodle-borg/service/dist/routes/whatsapp.js +238 -0
- package/node_modules/@noodle-borg/service/dist/service.js +30 -32
- package/node_modules/@noodle-borg/service/package.json +1 -1
- package/node_modules/@noodle-borg/wire-contracts/dist/application-channels.d.ts +33 -0
- package/node_modules/@noodle-borg/wire-contracts/dist/application-channels.js +9 -0
- package/node_modules/@noodle-borg/wire-contracts/dist/index.d.ts +1 -0
- package/node_modules/@noodle-borg/wire-contracts/dist/index.js +1 -0
- package/node_modules/@noodle-borg/wire-contracts/dist/service-info.d.ts +2 -0
- package/node_modules/@noodle-borg/wire-contracts/dist/service-info.js +10 -2
- package/node_modules/@noodle-borg/wire-contracts/dist/whatsapp.d.ts +426 -0
- package/node_modules/@noodle-borg/wire-contracts/dist/whatsapp.js +213 -0
- 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
|
-
|
|
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.
|
|
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({
|
|
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
|
|
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
|