@noodleseed/one 0.175.1 → 0.176.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 (53) hide show
  1. package/dist/commands/knowledge.d.ts.map +1 -1
  2. package/dist/commands/knowledge.js +4 -7
  3. package/dist/commands/knowledge.js.map +1 -1
  4. package/dist/commands/whatsapp-ops.d.ts.map +1 -1
  5. package/dist/commands/whatsapp-ops.js +12 -2
  6. package/dist/commands/whatsapp-ops.js.map +1 -1
  7. package/dist/commands/whatsapp-output.d.ts.map +1 -1
  8. package/dist/commands/whatsapp-output.js +1 -0
  9. package/dist/commands/whatsapp-output.js.map +1 -1
  10. package/node_modules/@noodle-borg/assistant-gateway/dist/channel-coordinator.d.ts +14 -8
  11. package/node_modules/@noodle-borg/assistant-gateway/dist/channel-coordinator.js +11 -2
  12. package/node_modules/@noodle-borg/assistant-gateway/dist/channel-types.d.ts +14 -5
  13. package/node_modules/@noodle-borg/cli-catalog/dist/catalog-data-channels.js +5 -3
  14. package/node_modules/@noodle-borg/knowledge-operations/dist/executor.d.ts +3 -5
  15. package/node_modules/@noodle-borg/knowledge-operations/dist/executor.js +2 -7
  16. package/node_modules/@noodle-borg/knowledge-operations/dist/portable.d.ts +1 -1
  17. package/node_modules/@noodle-borg/knowledge-operations/dist/portable.js +1 -1
  18. package/node_modules/@noodle-borg/knowledge-operations/dist/publication.d.ts +0 -1
  19. package/node_modules/@noodle-borg/knowledge-operations/dist/publication.js +1 -10
  20. package/node_modules/@noodle-borg/knowledge-operations/dist/routes.d.ts +0 -4
  21. package/node_modules/@noodle-borg/knowledge-operations/dist/routes.js +1 -17
  22. package/node_modules/@noodle-borg/knowledge-operations/dist/search-port.d.ts +1 -2
  23. package/node_modules/@noodle-borg/knowledge-operations/dist/search-port.js +1 -3
  24. package/node_modules/@noodle-borg/knowledge-operations/dist/service-wiring.d.ts +4 -4
  25. package/node_modules/@noodle-borg/knowledge-operations/dist/service-wiring.js +3 -8
  26. package/node_modules/@noodle-borg/knowledge-operations/dist/status-routes.d.ts +1 -2
  27. package/node_modules/@noodle-borg/knowledge-operations/dist/status-routes.js +0 -14
  28. package/node_modules/@noodle-borg/protocol/dist/knowledge-tool.js +3 -5
  29. package/node_modules/@noodle-borg/runtime/dist/execute.d.ts +2 -3
  30. package/node_modules/@noodle-borg/service/dist/assistant-composition.js +9 -0
  31. package/node_modules/@noodle-borg/service/dist/business-information/validation.js +1 -22
  32. package/node_modules/@noodle-borg/service/dist/channels/360dialog.js +41 -145
  33. package/node_modules/@noodle-borg/service/dist/channels/composition.js +1 -0
  34. package/node_modules/@noodle-borg/service/dist/channels/meta-cloud.js +102 -0
  35. package/node_modules/@noodle-borg/service/dist/channels/provider.js +151 -0
  36. package/node_modules/@noodle-borg/service/dist/channels/runtime.js +49 -21
  37. package/node_modules/@noodle-borg/service/dist/conversation-history/capture.js +99 -0
  38. package/node_modules/@noodle-borg/service/dist/conversation-history/contracts.js +17 -0
  39. package/node_modules/@noodle-borg/service/dist/payment-card.js +34 -0
  40. package/node_modules/@noodle-borg/service/dist/routes/assistant-elevation.js +2 -0
  41. package/node_modules/@noodle-borg/service/dist/routes/assistant-interaction-stream.js +4 -2
  42. package/node_modules/@noodle-borg/service/dist/routes/assistant-interactions.js +15 -0
  43. package/node_modules/@noodle-borg/service/dist/routes/assistant-knowledge.js +1 -3
  44. package/node_modules/@noodle-borg/service/dist/routes/assistant.js +8 -6
  45. package/node_modules/@noodle-borg/service/dist/routes/whatsapp-meta.js +130 -0
  46. package/node_modules/@noodle-borg/service/dist/routes/whatsapp.js +18 -9
  47. package/node_modules/@noodle-borg/transport-http/dist/index.d.ts +1 -1
  48. package/node_modules/@noodle-borg/transport-http/dist/index.js +1 -1
  49. package/node_modules/@noodle-borg/transport-http/dist/request-body.d.ts +9 -0
  50. package/node_modules/@noodle-borg/transport-http/dist/request-body.js +2 -1
  51. package/node_modules/@noodle-borg/wire-contracts/dist/whatsapp.d.ts +29 -7
  52. package/node_modules/@noodle-borg/wire-contracts/dist/whatsapp.js +27 -4
  53. package/package.json +1 -1
@@ -2,7 +2,9 @@ import { ChannelError, channelParticipantId, recordChannelDelivery, } from '@noo
2
2
  import { readJsonBody, sendJson } from '@noodle-borg/transport-http';
3
3
  import * as wire from '@noodle-borg/wire-contracts';
4
4
  import { parseWhatsAppWebhook, verifyWhatsAppWebhookSecret, WHATSAPP_CALLBACK_HEADER, } from '../channels/360dialog.js';
5
+ import { WHATSAPP_META_CALLBACK_PATH } from '../channels/meta-cloud.js';
5
6
  import { authorizeTenantControl, developerGrantRouteAccess } from './control-plane.js';
7
+ import { metaWebhook } from './whatsapp-meta.js';
6
8
  const participant = /^p_[a-f0-9]{64}$/;
7
9
  const eventId = /^e_[0-9_]+$/;
8
10
  function eventView(event) {
@@ -32,7 +34,8 @@ async function body(req, schema) {
32
34
  export function dispatchWhatsAppRoutes(req, res, url, runtime, grants) {
33
35
  const callback = /^\/v1\/channels\/whatsapp\/webhooks\/([a-z0-9-]+)$/.exec(url.pathname);
34
36
  const operator = /^\/v1\/orgs\/([^/]+)\/apps\/([^/]+)\/envs\/([^/]+)\/channels\/whatsapp(?:\/(.*))?$/.exec(url.pathname);
35
- if (!callback && !operator)
37
+ const meta = url.pathname === WHATSAPP_META_CALLBACK_PATH;
38
+ if (!callback && !operator && !meta)
36
39
  return false;
37
40
  res.setHeader('Cache-Control', 'no-store');
38
41
  if (!runtime) {
@@ -42,13 +45,15 @@ export function dispatchWhatsAppRoutes(req, res, url, runtime, grants) {
42
45
  });
43
46
  return true;
44
47
  }
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));
48
+ const run = Promise.resolve().then(() => meta
49
+ ? metaWebhook(req, res, url, runtime)
50
+ : callback
51
+ ? webhook(req, res, callback[1], runtime)
52
+ : operate(req, res, url, {
53
+ org: decodeURIComponent(operator[1]),
54
+ app: decodeURIComponent(operator[2]),
55
+ env: decodeURIComponent(operator[3]),
56
+ }, operator[4] ?? '', runtime, grants));
52
57
  void run.catch((error) => {
53
58
  const code = error instanceof ChannelError ? error.code : 'channel_unavailable';
54
59
  const status = code === 'channel_not_found'
@@ -73,8 +78,12 @@ async function webhook(req, res, id, runtime) {
73
78
  if (req.method !== 'POST')
74
79
  return sendJson(res, 405, { error: 'method_not_allowed' });
75
80
  const binding = await runtime.channels.internal(id);
81
+ // Meta numbers share the signed app callback; only 360dialog has a per-binding address.
82
+ if (binding.provider !== '360dialog')
83
+ return sendJson(res, 404, { error: 'not_found' });
76
84
  const { webhookSecret } = await runtime.provider(binding);
77
- if (!verifyWhatsAppWebhookSecret(req.headers[WHATSAPP_CALLBACK_HEADER], webhookSecret))
85
+ if (!webhookSecret ||
86
+ !verifyWhatsAppWebhookSecret(req.headers[WHATSAPP_CALLBACK_HEADER], webhookSecret))
78
87
  return sendJson(res, 401, { error: 'unauthorized' });
79
88
  const input = await readJsonBody(req, 1 << 20);
80
89
  if (!input.ok)
@@ -3,7 +3,7 @@ export { type AccessMode, type AdmissionCategory, type AdmissionContext, type Ad
3
3
  export { bearerToken } from './identity-authorization.js';
4
4
  export { createLogger, type LogFields, type Logger, type LoggerOptions, type LogLevel, type LogSink, noopLogger, stderrSink, stdoutSink, } from './logging.js';
5
5
  export { resolveTrustedPublicOrg, resolveTrustedTenantMcpResource, type TrustedPublicOrg, type TrustedPublicRef, type TrustedPublicRouting, type TrustedTenantMcpResource, trustedPublicMcpRef, trustedPublicOrgWellKnownRef, } from './public-routing.js';
6
- export { readBody, readDeployBody, readJsonBody } from './request-body.js';
6
+ export { readBody, readBodyBuffer, readDeployBody, readJsonBody } from './request-body.js';
7
7
  export { sendJson } from './responses.js';
8
8
  export { type RunningServer, type ServeOptions, serveHttp } from './serve.js';
9
9
  export type { HostedToolAuthorizationObservation, HostedToolAuthorizationObserver, ServicePrincipalToolAuthorizationObservation, } from './service-principal-authorization.js';
@@ -3,7 +3,7 @@ export { createMcpHttpHandler, createMcpRouter, } from './handler.js';
3
3
  export { bearerToken } from './identity-authorization.js';
4
4
  export { createLogger, noopLogger, stderrSink, stdoutSink, } from './logging.js';
5
5
  export { resolveTrustedPublicOrg, resolveTrustedTenantMcpResource, trustedPublicMcpRef, trustedPublicOrgWellKnownRef, } from './public-routing.js';
6
- export { readBody, readDeployBody, readJsonBody } from './request-body.js';
6
+ export { readBody, readBodyBuffer, readDeployBody, readJsonBody } from './request-body.js';
7
7
  export { sendJson } from './responses.js';
8
8
  export { serveHttp } from './serve.js';
9
9
  export { preflightServicePrincipalToolCall, sendServicePrincipalToolDenial, } from './service-principal-authorization.js';
@@ -7,6 +7,15 @@ export declare function readBody(req: IncomingMessage, max: number): Promise<{
7
7
  receivedBytes: number;
8
8
  maxBytes: number;
9
9
  }>;
10
+ /** The exact request bytes, bounded; for callers that must verify a signature before parsing. */
11
+ export declare function readBodyBuffer(req: IncomingMessage, max: number): Promise<{
12
+ ok: true;
13
+ buffer: Buffer;
14
+ } | {
15
+ ok: false;
16
+ receivedBytes: number;
17
+ maxBytes: number;
18
+ }>;
10
19
  export declare function readDeployBody(req: IncomingMessage, max: number): Promise<{
11
20
  ok: true;
12
21
  text: string;
@@ -3,7 +3,8 @@ export async function readBody(req, max) {
3
3
  const body = await readBodyBuffer(req, max);
4
4
  return body.ok ? { ok: true, text: body.buffer.toString('utf8') } : body;
5
5
  }
6
- async function readBodyBuffer(req, max) {
6
+ /** The exact request bytes, bounded; for callers that must verify a signature before parsing. */
7
+ export async function readBodyBuffer(req, max) {
7
8
  const chunks = [];
8
9
  let size = 0;
9
10
  for await (const chunk of req) {
@@ -11,11 +11,21 @@ export declare const WhatsAppLimitsSchema: z.ZodObject<{
11
11
  textCharacters: z.ZodNumber;
12
12
  dailyMicroUsd: z.ZodNumber;
13
13
  }, z.core.$strict>;
14
+ /** `360dialog` keeps a per-binding callback secret; `meta` names its WABA and uses the app signature. */
15
+ export declare const WhatsAppProviderSchema: z.ZodEnum<{
16
+ "360dialog": "360dialog";
17
+ meta: "meta";
18
+ }>;
14
19
  export declare const WhatsAppConfigureRequestSchema: z.ZodObject<{
15
20
  expectedRevision: z.ZodNumber;
21
+ provider: z.ZodDefault<z.ZodEnum<{
22
+ "360dialog": "360dialog";
23
+ meta: "meta";
24
+ }>>;
16
25
  phoneNumberId: z.ZodString;
26
+ wabaId: z.ZodOptional<z.ZodString>;
17
27
  apiKeySecret: z.ZodString;
18
- webhookSecret: z.ZodString;
28
+ webhookSecret: z.ZodOptional<z.ZodString>;
19
29
  capabilities: z.ZodArray<z.ZodObject<{
20
30
  kind: z.ZodEnum<{
21
31
  tool: "tool";
@@ -86,10 +96,14 @@ export declare const WhatsAppBindingSchema: z.ZodObject<{
86
96
  app: z.ZodString;
87
97
  env: z.ZodString;
88
98
  }, z.core.$strict>;
89
- provider: z.ZodLiteral<"360dialog">;
99
+ provider: z.ZodEnum<{
100
+ "360dialog": "360dialog";
101
+ meta: "meta";
102
+ }>;
90
103
  phoneNumberId: z.ZodString;
104
+ wabaId: z.ZodOptional<z.ZodString>;
91
105
  apiKeySecret: z.ZodString;
92
- webhookSecret: z.ZodString;
106
+ webhookSecret: z.ZodOptional<z.ZodString>;
93
107
  deploymentId: z.ZodString;
94
108
  capabilities: z.ZodArray<z.ZodObject<{
95
109
  kind: z.ZodEnum<{
@@ -133,10 +147,14 @@ export declare const WhatsAppBindingResponseSchema: z.ZodObject<{
133
147
  app: z.ZodString;
134
148
  env: z.ZodString;
135
149
  }, z.core.$strict>;
136
- provider: z.ZodLiteral<"360dialog">;
150
+ provider: z.ZodEnum<{
151
+ "360dialog": "360dialog";
152
+ meta: "meta";
153
+ }>;
137
154
  phoneNumberId: z.ZodString;
155
+ wabaId: z.ZodOptional<z.ZodString>;
138
156
  apiKeySecret: z.ZodString;
139
- webhookSecret: z.ZodString;
157
+ webhookSecret: z.ZodOptional<z.ZodString>;
140
158
  deploymentId: z.ZodString;
141
159
  capabilities: z.ZodArray<z.ZodObject<{
142
160
  kind: z.ZodEnum<{
@@ -176,10 +194,14 @@ export declare const WhatsAppBindingClientResponseSchema: z.ZodObject<{
176
194
  ok: z.ZodLiteral<true>;
177
195
  data: z.ZodNullable<z.ZodObject<{
178
196
  id: z.ZodString;
179
- provider: z.ZodLiteral<"360dialog">;
197
+ provider: z.ZodEnum<{
198
+ "360dialog": "360dialog";
199
+ meta: "meta";
200
+ }>;
180
201
  phoneNumberId: z.ZodString;
202
+ wabaId: z.ZodOptional<z.ZodString>;
181
203
  apiKeySecret: z.ZodString;
182
- webhookSecret: z.ZodString;
204
+ webhookSecret: z.ZodOptional<z.ZodString>;
183
205
  deploymentId: z.ZodString;
184
206
  supportEmail: z.ZodEmail;
185
207
  revision: z.ZodNumber;
@@ -20,14 +20,36 @@ export const WhatsAppLimitsSchema = z.strictObject({
20
20
  });
21
21
  const capability = z.strictObject({ kind: z.enum(['tool', 'knowledge']), name: id });
22
22
  const secretRef = z.string().regex(/^[A-Z][A-Z0-9_]{0,127}$/);
23
- export const WhatsAppConfigureRequestSchema = z.strictObject({
23
+ /** `360dialog` keeps a per-binding callback secret; `meta` names its WABA and uses the app signature. */
24
+ export const WhatsAppProviderSchema = z.enum(['360dialog', 'meta']);
25
+ export const WhatsAppConfigureRequestSchema = z
26
+ .strictObject({
24
27
  expectedRevision: revision,
28
+ provider: WhatsAppProviderSchema.default('360dialog'),
25
29
  phoneNumberId: id,
30
+ wabaId: id.optional(),
26
31
  apiKeySecret: secretRef,
27
- webhookSecret: secretRef,
32
+ webhookSecret: secretRef.optional(),
28
33
  capabilities: z.array(capability).max(64),
29
34
  supportEmail: z.email().max(254),
30
35
  limits: WhatsAppLimitsSchema.partial().optional(),
36
+ })
37
+ .superRefine((value, context) => {
38
+ const meta = value.provider === 'meta';
39
+ if (meta ? !value.wabaId : !value.webhookSecret)
40
+ context.addIssue({
41
+ code: 'custom',
42
+ path: [meta ? 'wabaId' : 'webhookSecret'],
43
+ message: meta ? 'Meta requires wabaId' : '360dialog requires webhookSecret',
44
+ });
45
+ if (meta ? value.webhookSecret !== undefined : value.wabaId !== undefined)
46
+ context.addIssue({
47
+ code: 'custom',
48
+ path: [meta ? 'webhookSecret' : 'wabaId'],
49
+ message: meta
50
+ ? 'Meta callbacks are authenticated by the app signature, not a webhook secret'
51
+ : 'wabaId applies only to the meta provider',
52
+ });
31
53
  });
32
54
  export const WhatsAppStateRequestSchema = z.strictObject({
33
55
  expectedRevision: revision,
@@ -64,10 +86,11 @@ const tenant = z.strictObject({ org: id, app: id, env: id });
64
86
  export const WhatsAppBindingSchema = z.strictObject({
65
87
  id,
66
88
  tenant,
67
- provider: z.literal('360dialog'),
89
+ provider: WhatsAppProviderSchema,
68
90
  phoneNumberId: id,
91
+ wabaId: id.optional(),
69
92
  apiKeySecret: secretRef,
70
- webhookSecret: secretRef,
93
+ webhookSecret: secretRef.optional(),
71
94
  deploymentId: id,
72
95
  capabilities: z.array(capability),
73
96
  supportEmail: z.email(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@noodleseed/one",
3
- "version": "0.175.1",
3
+ "version": "0.176.0",
4
4
  "private": false,
5
5
  "description": "Noodle CLI by Noodle Seed — author, run, and deploy declarative MCP servers. Embedding the assistant in your own web app is @noodleseed/assistant.",
6
6
  "license": "Apache-2.0",