@noodleseed/one 0.183.1 → 0.185.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/commands/solutions-conversations.d.ts.map +1 -1
- package/dist/commands/solutions-conversations.js +3 -3
- package/dist/commands/solutions-conversations.js.map +1 -1
- package/dist/commands/solutions-history.d.ts.map +1 -1
- package/dist/commands/solutions-history.js +6 -3
- package/dist/commands/solutions-history.js.map +1 -1
- package/dist/commands/whatsapp-ops.d.ts +2 -1
- package/dist/commands/whatsapp-ops.d.ts.map +1 -1
- package/dist/commands/whatsapp-ops.js +63 -27
- package/dist/commands/whatsapp-ops.js.map +1 -1
- package/dist/commands/whatsapp-output.d.ts +1 -1
- package/dist/commands/whatsapp-output.d.ts.map +1 -1
- package/dist/commands/whatsapp-output.js +13 -7
- package/dist/commands/whatsapp-output.js.map +1 -1
- package/node_modules/@noodle-borg/agent-kit/dist/generated/example-files.js +1 -1
- package/node_modules/@noodle-borg/agent-kit/dist/skill-embedded-assistant-ref.js +2 -2
- package/node_modules/@noodle-borg/agent-kit/package.json +1 -1
- package/node_modules/@noodle-borg/assistant-gateway/dist/assistant-model-context.js +7 -2
- package/node_modules/@noodle-borg/assistant-gateway/dist/assistant-turn-context.d.ts +2 -1
- package/node_modules/@noodle-borg/assistant-gateway/dist/channel-coordinator.d.ts +88 -8
- package/node_modules/@noodle-borg/assistant-gateway/dist/channel-coordinator.js +42 -30
- package/node_modules/@noodle-borg/assistant-gateway/dist/channel-spend.js +2 -2
- package/node_modules/@noodle-borg/assistant-gateway/dist/channel-types.d.ts +60 -11
- package/node_modules/@noodle-borg/assistant-gateway/dist/channel-types.js +31 -3
- package/node_modules/@noodle-borg/assistant-gateway/dist/model-request.d.ts +2 -1
- package/node_modules/@noodle-borg/assistant-gateway/dist/public-surface.d.ts +4 -2
- package/node_modules/@noodle-borg/assistant-gateway/dist/public-surface.js +4 -3
- package/node_modules/@noodle-borg/authoring/dist/assistant.d.ts +2 -1
- package/node_modules/@noodle-borg/authoring/dist/assistant.js +5 -4
- package/node_modules/@noodle-borg/cli-catalog/dist/catalog-data-channels.js +72 -51
- package/node_modules/@noodle-borg/cli-catalog/dist/catalog-data-solution-conversations.js +1 -1
- package/node_modules/@noodle-borg/cli-catalog/dist/catalog-data-solution-history.js +1 -0
- package/node_modules/@noodle-borg/compiler/dist/manifest/schema.d.ts +16 -4
- package/node_modules/@noodle-borg/compiler/dist/manifest/schema.js +8 -3
- package/node_modules/@noodle-borg/runtime/dist/connector/types.d.ts +1 -1
- package/node_modules/@noodle-borg/service/dist/application-history-settings.js +6 -2
- package/node_modules/@noodle-borg/service/dist/build-info.js +3 -2
- package/node_modules/@noodle-borg/service/dist/channels/instagram-meta.js +196 -0
- package/node_modules/@noodle-borg/service/dist/channels/instagram-webhook.js +78 -0
- package/node_modules/@noodle-borg/service/dist/channels/provider.js +1 -1
- package/node_modules/@noodle-borg/service/dist/channels/reply-bound.js +24 -0
- package/node_modules/@noodle-borg/service/dist/channels/runtime.js +35 -24
- package/node_modules/@noodle-borg/service/dist/conversation-history/capture.js +5 -5
- package/node_modules/@noodle-borg/service/dist/conversation-history/contracts.js +2 -1
- package/node_modules/@noodle-borg/service/dist/conversation-history/operator.js +2 -2
- package/node_modules/@noodle-borg/service/dist/routes/instagram-meta.js +79 -0
- package/node_modules/@noodle-borg/service/dist/routes/meta-webhook.js +36 -0
- package/node_modules/@noodle-borg/service/dist/routes/service-probes.js +4 -1
- package/node_modules/@noodle-borg/service/dist/routes/whatsapp-meta.js +3 -34
- package/node_modules/@noodle-borg/service/dist/routes/whatsapp.js +45 -24
- package/node_modules/@noodle-borg/service/dist/service.js +1 -1
- package/node_modules/@noodle-borg/service/package.json +1 -1
- package/node_modules/@noodle-borg/wire-contracts/dist/application-history-settings.d.ts +7 -0
- package/node_modules/@noodle-borg/wire-contracts/dist/application-history-settings.js +1 -0
- package/node_modules/@noodle-borg/wire-contracts/dist/conversation-history.d.ts +12 -0
- package/node_modules/@noodle-borg/wire-contracts/dist/conversation-history.js +1 -1
- 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/instagram.d.ts +181 -0
- package/node_modules/@noodle-borg/wire-contracts/dist/instagram.js +50 -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 +3 -0
- package/package.json +3 -3
|
@@ -27,17 +27,14 @@ export type ChannelCapability = {
|
|
|
27
27
|
readonly kind: 'tool' | 'knowledge';
|
|
28
28
|
readonly name: string;
|
|
29
29
|
};
|
|
30
|
-
|
|
30
|
+
/** One binding per channel per environment; each channel owns its own provider asset. */
|
|
31
|
+
export declare const CHANNEL_KINDS: readonly ["whatsapp", "instagram"];
|
|
32
|
+
export type ChannelKind = (typeof CHANNEL_KINDS)[number];
|
|
33
|
+
export interface ChannelBindingBase {
|
|
31
34
|
readonly id: string;
|
|
32
35
|
readonly tenant: TenantRef;
|
|
33
|
-
readonly provider: ChannelProvider;
|
|
34
|
-
readonly phoneNumberId: string;
|
|
35
|
-
/** The WhatsApp Business Account that owns the number; present only for `meta`. */
|
|
36
|
-
readonly wabaId?: string | undefined;
|
|
37
36
|
/** Tenant secret name of the provider credential: 360dialog key or Meta business token. */
|
|
38
37
|
readonly apiKeySecret: string;
|
|
39
|
-
/** Tenant secret name of the per-binding callback secret; present only for `360dialog`. */
|
|
40
|
-
readonly webhookSecret?: string | undefined;
|
|
41
38
|
readonly deploymentId: string;
|
|
42
39
|
readonly capabilities: readonly ChannelCapability[];
|
|
43
40
|
readonly supportEmail: string;
|
|
@@ -54,11 +51,35 @@ export interface ChannelBinding {
|
|
|
54
51
|
readonly readyRevision?: number | undefined;
|
|
55
52
|
readonly readyAt?: number | undefined;
|
|
56
53
|
}
|
|
57
|
-
export
|
|
54
|
+
export interface WhatsAppBinding extends ChannelBindingBase {
|
|
55
|
+
/** Stored rows and operator responses omit it; `requireChannel` supplies it on read. */
|
|
56
|
+
readonly channel: 'whatsapp';
|
|
57
|
+
readonly provider: ChannelProvider;
|
|
58
|
+
readonly phoneNumberId: string;
|
|
59
|
+
/** The WhatsApp Business Account that owns the number; present only for `meta`. */
|
|
60
|
+
readonly wabaId?: string | undefined;
|
|
61
|
+
/** Tenant secret name of the per-binding callback secret; present only for `360dialog`. */
|
|
62
|
+
readonly webhookSecret?: string | undefined;
|
|
63
|
+
}
|
|
64
|
+
/** An Instagram professional account reached through its linked Facebook Page (Messenger Platform). */
|
|
65
|
+
export interface InstagramBinding extends ChannelBindingBase {
|
|
66
|
+
readonly channel: 'instagram';
|
|
67
|
+
readonly provider: 'meta';
|
|
68
|
+
readonly instagramAccountId: string;
|
|
69
|
+
readonly pageId: string;
|
|
70
|
+
}
|
|
71
|
+
export type ChannelBinding = WhatsAppBinding | InstagramBinding;
|
|
72
|
+
type ChannelConfigureBase = Pick<ChannelBindingBase, 'tenant' | 'apiKeySecret' | 'deploymentId' | 'capabilities' | 'supportEmail'> & {
|
|
73
|
+
readonly limits?: ChannelLimitsInput | undefined;
|
|
74
|
+
};
|
|
75
|
+
export type WhatsAppConfigure = ChannelConfigureBase & Pick<WhatsAppBinding, 'phoneNumberId' | 'wabaId' | 'webhookSecret'> & {
|
|
76
|
+
/** Absent in WhatsApp operator requests, so their idempotency fingerprints stay unchanged. */
|
|
77
|
+
readonly channel?: 'whatsapp' | undefined;
|
|
58
78
|
/** Defaults to `360dialog`. */
|
|
59
79
|
readonly provider?: ChannelProvider | undefined;
|
|
60
|
-
readonly limits?: ChannelLimitsInput | undefined;
|
|
61
80
|
};
|
|
81
|
+
export type InstagramConfigure = ChannelConfigureBase & Pick<InstagramBinding, 'channel' | 'instagramAccountId' | 'pageId'>;
|
|
82
|
+
export type ChannelConfigure = WhatsAppConfigure | InstagramConfigure;
|
|
62
83
|
export interface ChannelAddress {
|
|
63
84
|
readonly kind: 'phone' | 'opaque';
|
|
64
85
|
readonly value: string;
|
|
@@ -120,7 +141,11 @@ export declare class ChannelError extends Error {
|
|
|
120
141
|
constructor(code: string);
|
|
121
142
|
}
|
|
122
143
|
export declare function channelDigest(value: string): string;
|
|
123
|
-
|
|
144
|
+
/** WhatsApp keeps its original key so existing registry rows resolve without a migration. */
|
|
145
|
+
export declare function channelTenantKey(tenant: TenantRef, channel: ChannelKind): string;
|
|
146
|
+
/** The provider asset a binding owns; WhatsApp keeps its original unprefixed key. */
|
|
147
|
+
export declare function channelAssetKey(channel: ChannelKind, assetId: string): string;
|
|
148
|
+
export declare function channelBindingAsset(binding: ChannelBinding): string;
|
|
124
149
|
export declare function channelParticipantId(binding: ChannelBinding, address: ChannelAddress): string;
|
|
125
150
|
export declare function sameChannelTenant(left: TenantRef, right: TenantRef): boolean;
|
|
126
151
|
export declare function channelLimits(input?: ChannelLimitsInput): ChannelLimits;
|
|
@@ -131,14 +156,37 @@ export declare function channelRow(id: string, kind: ChannelRow['kind'], value:
|
|
|
131
156
|
expiresAt?: number;
|
|
132
157
|
}): ChannelRow;
|
|
133
158
|
export declare function writeChannelEvent(tx: ChannelTransaction, scope: string, event: ChannelEvent, now: number): Promise<void>;
|
|
159
|
+
export declare function channelBindingRow(binding: ChannelBinding, now: number): ChannelRow;
|
|
160
|
+
/** Rows written before Instagram carry no `channel`; they are WhatsApp. Nothing is written back. */
|
|
161
|
+
export declare function readChannelBinding(stored: unknown): ChannelBinding;
|
|
134
162
|
export declare function publicChannelBinding(binding: ChannelBinding): {
|
|
163
|
+
channel: "instagram";
|
|
164
|
+
provider: "meta";
|
|
165
|
+
instagramAccountId: string;
|
|
166
|
+
pageId: string;
|
|
135
167
|
id: string;
|
|
136
168
|
tenant: TenantRef;
|
|
169
|
+
apiKeySecret: string;
|
|
170
|
+
deploymentId: string;
|
|
171
|
+
capabilities: readonly ChannelCapability[];
|
|
172
|
+
supportEmail: string;
|
|
173
|
+
revision: number;
|
|
174
|
+
generation: number;
|
|
175
|
+
state: "paused" | "enabled" | "disconnected";
|
|
176
|
+
limits: ChannelLimits;
|
|
177
|
+
createdAt: number;
|
|
178
|
+
updatedAt: number;
|
|
179
|
+
actor: string;
|
|
180
|
+
readyRevision?: number | undefined;
|
|
181
|
+
readyAt?: number | undefined;
|
|
182
|
+
} | {
|
|
137
183
|
provider: ChannelProvider;
|
|
138
184
|
phoneNumberId: string;
|
|
139
185
|
wabaId?: string | undefined;
|
|
140
|
-
apiKeySecret: string;
|
|
141
186
|
webhookSecret?: string | undefined;
|
|
187
|
+
id: string;
|
|
188
|
+
tenant: TenantRef;
|
|
189
|
+
apiKeySecret: string;
|
|
142
190
|
deploymentId: string;
|
|
143
191
|
capabilities: readonly ChannelCapability[];
|
|
144
192
|
supportEmail: string;
|
|
@@ -152,4 +200,5 @@ export declare function publicChannelBinding(binding: ChannelBinding): {
|
|
|
152
200
|
readyRevision?: number | undefined;
|
|
153
201
|
readyAt?: number | undefined;
|
|
154
202
|
};
|
|
203
|
+
export {};
|
|
155
204
|
//# sourceMappingURL=channel-types.d.ts.map
|
|
@@ -14,6 +14,8 @@ export const CHANNEL_DEFAULT_LIMITS = Object.freeze({
|
|
|
14
14
|
textCharacters: 4000,
|
|
15
15
|
dailyMicroUsd: 20_000_000,
|
|
16
16
|
});
|
|
17
|
+
/** One binding per channel per environment; each channel owns its own provider asset. */
|
|
18
|
+
export const CHANNEL_KINDS = ['whatsapp', 'instagram'];
|
|
17
19
|
export class ChannelError extends Error {
|
|
18
20
|
code;
|
|
19
21
|
constructor(code) {
|
|
@@ -25,8 +27,19 @@ export class ChannelError extends Error {
|
|
|
25
27
|
export function channelDigest(value) {
|
|
26
28
|
return createHash('sha256').update(value).digest('hex');
|
|
27
29
|
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
+
/** WhatsApp keeps its original key so existing registry rows resolve without a migration. */
|
|
31
|
+
export function channelTenantKey(tenant, channel) {
|
|
32
|
+
const parts = [tenant.org, tenant.app, tenant.env];
|
|
33
|
+
return `tenant:${channelDigest(JSON.stringify(channel === 'whatsapp' ? parts : [...parts, channel]))}`;
|
|
34
|
+
}
|
|
35
|
+
/** The provider asset a binding owns; WhatsApp keeps its original unprefixed key. */
|
|
36
|
+
export function channelAssetKey(channel, assetId) {
|
|
37
|
+
return channel === 'whatsapp'
|
|
38
|
+
? `asset:${channelDigest(assetId)}`
|
|
39
|
+
: `asset:${channel}:${channelDigest(assetId)}`;
|
|
40
|
+
}
|
|
41
|
+
export function channelBindingAsset(binding) {
|
|
42
|
+
return channelAssetKey(binding.channel, binding.channel === 'whatsapp' ? binding.phoneNumberId : binding.instagramAccountId);
|
|
30
43
|
}
|
|
31
44
|
export function channelParticipantId(binding, address) {
|
|
32
45
|
return `p_${createHmac('sha256', Buffer.from(binding.indexKey, 'base64'))
|
|
@@ -65,8 +78,23 @@ export async function writeChannelEvent(tx, scope, event, now) {
|
|
|
65
78
|
expiresAt: event.receivedAt + CHANNEL_RETENTION_MS,
|
|
66
79
|
}));
|
|
67
80
|
}
|
|
81
|
+
/** WhatsApp rows are stored without `channel`, byte-identical to rows written before Instagram. */
|
|
82
|
+
function storedChannelBinding(binding) {
|
|
83
|
+
if (binding.channel === 'instagram')
|
|
84
|
+
return binding;
|
|
85
|
+
const { channel: _whatsapp, ...stored } = binding;
|
|
86
|
+
return stored;
|
|
87
|
+
}
|
|
88
|
+
export function channelBindingRow(binding, now) {
|
|
89
|
+
return channelRow('binding', 'binding', storedChannelBinding(binding), now);
|
|
90
|
+
}
|
|
91
|
+
/** Rows written before Instagram carry no `channel`; they are WhatsApp. Nothing is written back. */
|
|
92
|
+
export function readChannelBinding(stored) {
|
|
93
|
+
const binding = stored;
|
|
94
|
+
return binding.channel === 'instagram' ? binding : { ...binding, channel: 'whatsapp' };
|
|
95
|
+
}
|
|
68
96
|
export function publicChannelBinding(binding) {
|
|
69
|
-
const { indexKey: _privateIndexKey, credentialDigest: _credentialDigest, ...visible } = binding;
|
|
97
|
+
const { indexKey: _privateIndexKey, credentialDigest: _credentialDigest, ...visible } = storedChannelBinding(binding);
|
|
70
98
|
return visible;
|
|
71
99
|
}
|
|
72
100
|
//# sourceMappingURL=channel-types.js.map
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ChannelKind } from './channel-types.js';
|
|
1
2
|
import { type ModelCompletion, type ModelToolCall, type ModelUsage } from './model-stream.js';
|
|
2
3
|
export type AssistantModelSource = 'operator' | 'noodle-managed';
|
|
3
4
|
export type AssistantModelTransport = 'chat-completions' | 'responses';
|
|
@@ -81,7 +82,7 @@ export type ResolvedAssistantModel = ResolvedAssistantModelBase & ({
|
|
|
81
82
|
});
|
|
82
83
|
export interface ManagedAssistantModelResolver {
|
|
83
84
|
resolve(input: {
|
|
84
|
-
readonly channel?:
|
|
85
|
+
readonly channel?: ChannelKind;
|
|
85
86
|
readonly tenant: {
|
|
86
87
|
readonly org: string;
|
|
87
88
|
readonly app: string;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { SurfaceCapabilityRef } from './artifact-projection.js';
|
|
2
|
+
import type { ChannelKind } from './channel-types.js';
|
|
2
3
|
/**
|
|
3
4
|
* The one reader of "does this deployment face the public, and on what terms".
|
|
4
5
|
*
|
|
@@ -45,12 +46,13 @@ export declare function surfaceBindingForOrigin(assistant: unknown, origin: stri
|
|
|
45
46
|
export declare function publicSurfaceOf(assistant: unknown): PublicSurface | undefined;
|
|
46
47
|
export interface MessagingSurface {
|
|
47
48
|
readonly kind: 'messaging';
|
|
48
|
-
readonly channel:
|
|
49
|
+
readonly channel: ChannelKind;
|
|
49
50
|
readonly mode: 'public';
|
|
50
51
|
readonly capabilities: readonly SurfaceCapabilityRef[];
|
|
51
52
|
readonly instructions?: string;
|
|
52
53
|
/** @see PublicSurface.history */
|
|
53
54
|
readonly history?: false;
|
|
54
55
|
}
|
|
55
|
-
|
|
56
|
+
/** The public messaging surface for one channel; each channel's binding reads only its own. */
|
|
57
|
+
export declare function messagingSurfaceOf(assistant: unknown, channel: ChannelKind): MessagingSurface | undefined;
|
|
56
58
|
//# sourceMappingURL=public-surface.d.ts.map
|
|
@@ -67,18 +67,19 @@ export function publicSurfaceOf(assistant) {
|
|
|
67
67
|
}
|
|
68
68
|
return undefined;
|
|
69
69
|
}
|
|
70
|
-
|
|
70
|
+
/** The public messaging surface for one channel; each channel's binding reads only its own. */
|
|
71
|
+
export function messagingSurfaceOf(assistant, channel) {
|
|
71
72
|
const surfaces = assistant?.surfaces;
|
|
72
73
|
if (!Array.isArray(surfaces))
|
|
73
74
|
return undefined;
|
|
74
75
|
for (const entry of surfaces) {
|
|
75
|
-
if (entry?.kind !== 'messaging' || entry.channel !==
|
|
76
|
+
if (entry?.kind !== 'messaging' || entry.channel !== channel || entry.mode !== 'public')
|
|
76
77
|
continue;
|
|
77
78
|
if (!Array.isArray(entry.capabilities))
|
|
78
79
|
return undefined;
|
|
79
80
|
return {
|
|
80
81
|
kind: 'messaging',
|
|
81
|
-
channel
|
|
82
|
+
channel,
|
|
82
83
|
mode: 'public',
|
|
83
84
|
capabilities: entry.capabilities,
|
|
84
85
|
...(typeof entry.instructions === 'string' ? { instructions: entry.instructions } : {}),
|
|
@@ -223,7 +223,8 @@ export interface PublicWebsiteAccess {
|
|
|
223
223
|
readonly history?: false;
|
|
224
224
|
}
|
|
225
225
|
export interface PublicMessagingInput {
|
|
226
|
-
|
|
226
|
+
/** One surface per channel; the operator binds each channel's account separately. */
|
|
227
|
+
readonly channel: 'whatsapp' | 'instagram';
|
|
227
228
|
readonly capabilities: readonly CapabilityRef[];
|
|
228
229
|
readonly instructions?: string;
|
|
229
230
|
/** @see AuthenticatedWebsiteInput.history */
|
|
@@ -9,7 +9,7 @@ export function publicMessaging(input) {
|
|
|
9
9
|
throw new Error(`publicMessaging does not accept ${key}`);
|
|
10
10
|
}
|
|
11
11
|
}
|
|
12
|
-
if (input.channel !== 'whatsapp')
|
|
12
|
+
if (input.channel !== 'whatsapp' && input.channel !== 'instagram')
|
|
13
13
|
throw new Error('Unsupported public messaging channel');
|
|
14
14
|
return {
|
|
15
15
|
kind: 'messaging',
|
|
@@ -151,9 +151,10 @@ function assertDistinctSurfaces(surfaces) {
|
|
|
151
151
|
throw new Error('embeddedAssistant requires at least one access surface');
|
|
152
152
|
}
|
|
153
153
|
const websites = surfaces.filter((surface) => surface.kind !== 'messaging');
|
|
154
|
-
const
|
|
155
|
-
|
|
156
|
-
|
|
154
|
+
const channels = surfaces.flatMap((surface) => surface.kind === 'messaging' ? [surface.channel] : []);
|
|
155
|
+
const repeated = channels.find((channel, index) => channels.indexOf(channel) !== index);
|
|
156
|
+
if (repeated)
|
|
157
|
+
throw new Error(`embeddedAssistant accepts only one ${repeated} surface`);
|
|
157
158
|
const publicSurfaces = websites.filter((surface) => surface.mode !== 'authenticated');
|
|
158
159
|
if (publicSurfaces.length > 1) {
|
|
159
160
|
throw new Error('embeddedAssistant accepts at most one public surface (public or mixed)');
|
|
@@ -41,6 +41,44 @@ const leaf = (name, summary, flags = []) => ({
|
|
|
41
41
|
jsonOutput: { mode: 'single' },
|
|
42
42
|
});
|
|
43
43
|
const group = (name, summary, subcommands) => ({ name, summary, subcommands, flags: [], arguments: [] });
|
|
44
|
+
/** Every channel's verbs; only configuration and phone-based blocking differ. */
|
|
45
|
+
const operations = (configure, blocks) => [
|
|
46
|
+
leaf('status', 'Show configuration and current revision.'),
|
|
47
|
+
configure,
|
|
48
|
+
leaf('doctor', 'Check deployment, storage, worker, model cost bound, provider setup, and capability compatibility.', mutation),
|
|
49
|
+
leaf('enable', 'Enable after a successful current readiness check.', [revision, ...mutation]),
|
|
50
|
+
leaf('pause', 'Stop new work and unsent replies.', [revision, ...mutation]),
|
|
51
|
+
leaf('disconnect', 'Fence this binding and retain governed receipts.', [revision, ...mutation]),
|
|
52
|
+
group('webhook', 'Inspect or configure the owned provider callback.', [
|
|
53
|
+
leaf('inspect', 'Check URL and authentication without disclosing headers.'),
|
|
54
|
+
leaf('configure', 'Register the service-owned callback.', [revision, ...mutation]),
|
|
55
|
+
]),
|
|
56
|
+
group('limits', 'Inspect or lower the approved limits.', [
|
|
57
|
+
leaf('get', 'Show limits.'),
|
|
58
|
+
leaf('set', 'Lower selected limits.', [revision, ...mutation, ...limits]),
|
|
59
|
+
]),
|
|
60
|
+
leaf('usage', 'Show UTC-day settled and reserved inference spend.'),
|
|
61
|
+
group('blocks', 'Inspect local blocks.', [
|
|
62
|
+
leaf('list', 'List local and provider block evidence.', [
|
|
63
|
+
flag('after', 'Cursor from the previous page.'),
|
|
64
|
+
]),
|
|
65
|
+
]),
|
|
66
|
+
...blocks,
|
|
67
|
+
group('cooldown', 'Inspect or clear an automatic cooldown.', [
|
|
68
|
+
leaf('inspect', 'Show a participant cooldown.', [participant]),
|
|
69
|
+
leaf('clear', 'Clear cooldown without resetting quotas.', [participant, ...mutation]),
|
|
70
|
+
]),
|
|
71
|
+
group('events', 'Inspect content-free transport receipts.', [
|
|
72
|
+
leaf('list', 'List a page of events.', [
|
|
73
|
+
flag('after', 'Cursor returned by the previous page.'),
|
|
74
|
+
]),
|
|
75
|
+
leaf('inspect', 'Inspect one event.', [flag('event-id', 'Event identifier.')]),
|
|
76
|
+
leaf('reconcile', 'Read current receipt evidence without resending.', [
|
|
77
|
+
flag('event-id', 'Event identifier.'),
|
|
78
|
+
...mutation,
|
|
79
|
+
]),
|
|
80
|
+
]),
|
|
81
|
+
];
|
|
44
82
|
export const CHANNELS_COMMAND = {
|
|
45
83
|
name: 'channels',
|
|
46
84
|
section: 'operate',
|
|
@@ -48,44 +86,18 @@ export const CHANNELS_COMMAND = {
|
|
|
48
86
|
arguments: [],
|
|
49
87
|
flags: [],
|
|
50
88
|
subcommands: [
|
|
51
|
-
group('whatsapp', 'Configure a 360dialog or Meta Cloud API number, test readiness, and control public messaging.', [
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
...limits,
|
|
64
|
-
]),
|
|
65
|
-
leaf('doctor', 'Check deployment, storage, worker, model cost bound, provider setup, and capability compatibility.', mutation),
|
|
66
|
-
leaf('enable', 'Enable after a successful current readiness check.', [
|
|
67
|
-
revision,
|
|
68
|
-
...mutation,
|
|
69
|
-
]),
|
|
70
|
-
leaf('pause', 'Stop new work and unsent replies.', [revision, ...mutation]),
|
|
71
|
-
leaf('disconnect', 'Fence this binding and retain governed receipts.', [
|
|
72
|
-
revision,
|
|
73
|
-
...mutation,
|
|
74
|
-
]),
|
|
75
|
-
group('webhook', 'Inspect or configure the owned provider callback.', [
|
|
76
|
-
leaf('inspect', 'Check URL and authentication without disclosing headers.'),
|
|
77
|
-
leaf('configure', 'Register the service-owned callback.', [revision, ...mutation]),
|
|
78
|
-
]),
|
|
79
|
-
group('limits', 'Inspect or lower the approved limits.', [
|
|
80
|
-
leaf('get', 'Show limits.'),
|
|
81
|
-
leaf('set', 'Lower selected limits.', [revision, ...mutation, ...limits]),
|
|
82
|
-
]),
|
|
83
|
-
leaf('usage', 'Show UTC-day settled and reserved inference spend.'),
|
|
84
|
-
group('blocks', 'Inspect local blocks.', [
|
|
85
|
-
leaf('list', 'List local and provider block evidence.', [
|
|
86
|
-
flag('after', 'Cursor from the previous page.'),
|
|
87
|
-
]),
|
|
88
|
-
]),
|
|
89
|
+
group('whatsapp', 'Configure a 360dialog or Meta Cloud API number, test readiness, and control public messaging.', operations(leaf('configure', 'Save a paused binding using managed secret references.', [
|
|
90
|
+
revision,
|
|
91
|
+
...mutation,
|
|
92
|
+
flag('provider', '360dialog (default) or meta for the Meta Cloud API.'),
|
|
93
|
+
flag('phone-number-id', '360dialog/Meta phone-number asset ID.'),
|
|
94
|
+
flag('waba-id', 'WhatsApp Business Account ID; required with --provider meta.'),
|
|
95
|
+
flag('api-key-secret', 'Managed secret name for the 360dialog key or the Meta business token.'),
|
|
96
|
+
flag('webhook-secret', 'Independent managed callback secret name; 360dialog only.'),
|
|
97
|
+
flag('capabilities', 'Comma-separated knowledge:name or tool:name selections.'),
|
|
98
|
+
flag('support-email', 'Contact shown when an answer cannot be completed.'),
|
|
99
|
+
...limits,
|
|
100
|
+
]), [
|
|
89
101
|
leaf('block', 'Block locally; optionally request a provider block.', [
|
|
90
102
|
participant,
|
|
91
103
|
blockScope,
|
|
@@ -100,21 +112,30 @@ export const CHANNELS_COMMAND = {
|
|
|
100
112
|
flag('phone-stdin', 'Supply the number if retained participant context has expired.', 'boolean'),
|
|
101
113
|
...mutation,
|
|
102
114
|
]),
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
115
|
+
])),
|
|
116
|
+
group('instagram', 'Configure an Instagram professional account through its Facebook Page, test readiness, and control DMs.', operations(leaf('configure', 'Save a paused binding using a managed secret reference.', [
|
|
117
|
+
revision,
|
|
118
|
+
...mutation,
|
|
119
|
+
flag('instagram-account-id', 'Instagram professional account ID (17841...).'),
|
|
120
|
+
flag('page-id', 'ID of the Facebook Page linked to the account.'),
|
|
121
|
+
flag('api-key-secret', 'Managed secret name for the system-user token with Page access.'),
|
|
122
|
+
flag('capabilities', 'Comma-separated knowledge:name or tool:name selections.'),
|
|
123
|
+
flag('support-email', 'Contact shown when an answer cannot be completed.'),
|
|
124
|
+
...limits,
|
|
125
|
+
]), [
|
|
126
|
+
leaf('block', 'Block locally; Instagram has no provider block.', [
|
|
127
|
+
participant,
|
|
128
|
+
blockScope,
|
|
129
|
+
flag('until', 'Expiry as an ISO timestamp.'),
|
|
130
|
+
flag('indefinite', 'Explicitly retain this block until removed.', 'boolean'),
|
|
131
|
+
...mutation,
|
|
106
132
|
]),
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
leaf('inspect', 'Inspect one event.', [flag('event-id', 'Event identifier.')]),
|
|
112
|
-
leaf('reconcile', 'Read current receipt evidence without resending.', [
|
|
113
|
-
flag('event-id', 'Event identifier.'),
|
|
114
|
-
...mutation,
|
|
115
|
-
]),
|
|
133
|
+
leaf('unblock', 'Remove an explicitly selected block without resetting usage.', [
|
|
134
|
+
participant,
|
|
135
|
+
blockScope,
|
|
136
|
+
...mutation,
|
|
116
137
|
]),
|
|
117
|
-
]),
|
|
138
|
+
])),
|
|
118
139
|
],
|
|
119
140
|
};
|
|
120
141
|
//# sourceMappingURL=catalog-data-channels.js.map
|
|
@@ -31,7 +31,7 @@ const paging = (maximum) => [
|
|
|
31
31
|
type: 'string',
|
|
32
32
|
value: '<channel>',
|
|
33
33
|
summary: 'Only conversations from this channel.',
|
|
34
|
-
constraints: { choices: ['website', 'whatsapp'] },
|
|
34
|
+
constraints: { choices: ['website', 'whatsapp', 'instagram'] },
|
|
35
35
|
},
|
|
36
36
|
{
|
|
37
37
|
...OPTIONAL_FLAG,
|
|
@@ -58,6 +58,7 @@ export const SOLUTION_HISTORY = {
|
|
|
58
58
|
channel('website-visitors', 'Record anonymous website visitors.'),
|
|
59
59
|
channel('signed-in-customers', 'Record signed-in customers.'),
|
|
60
60
|
channel('whatsapp', 'Record WhatsApp conversations.'),
|
|
61
|
+
channel('instagram', 'Record Instagram DM conversations.'),
|
|
61
62
|
{
|
|
62
63
|
...OPTIONAL_FLAG,
|
|
63
64
|
required: true,
|
|
@@ -361,7 +361,10 @@ export declare const manifestV1Schema: z.ZodObject<{
|
|
|
361
361
|
allowedOrigins: z.ZodArray<z.ZodString>;
|
|
362
362
|
surfaces: z.ZodOptional<z.ZodArray<z.ZodUnion<readonly [z.ZodObject<{
|
|
363
363
|
kind: z.ZodLiteral<"messaging">;
|
|
364
|
-
channel: z.
|
|
364
|
+
channel: z.ZodEnum<{
|
|
365
|
+
whatsapp: "whatsapp";
|
|
366
|
+
instagram: "instagram";
|
|
367
|
+
}>;
|
|
365
368
|
mode: z.ZodLiteral<"public">;
|
|
366
369
|
capabilities: z.ZodArray<z.ZodObject<{
|
|
367
370
|
kind: z.ZodEnum<{
|
|
@@ -868,7 +871,10 @@ export declare const manifestV2Schema: z.ZodObject<{
|
|
|
868
871
|
allowedOrigins: z.ZodArray<z.ZodString>;
|
|
869
872
|
surfaces: z.ZodOptional<z.ZodArray<z.ZodUnion<readonly [z.ZodObject<{
|
|
870
873
|
kind: z.ZodLiteral<"messaging">;
|
|
871
|
-
channel: z.
|
|
874
|
+
channel: z.ZodEnum<{
|
|
875
|
+
whatsapp: "whatsapp";
|
|
876
|
+
instagram: "instagram";
|
|
877
|
+
}>;
|
|
872
878
|
mode: z.ZodLiteral<"public">;
|
|
873
879
|
capabilities: z.ZodArray<z.ZodObject<{
|
|
874
880
|
kind: z.ZodEnum<{
|
|
@@ -1669,7 +1675,10 @@ export declare const manifestSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
1669
1675
|
allowedOrigins: z.ZodArray<z.ZodString>;
|
|
1670
1676
|
surfaces: z.ZodOptional<z.ZodArray<z.ZodUnion<readonly [z.ZodObject<{
|
|
1671
1677
|
kind: z.ZodLiteral<"messaging">;
|
|
1672
|
-
channel: z.
|
|
1678
|
+
channel: z.ZodEnum<{
|
|
1679
|
+
whatsapp: "whatsapp";
|
|
1680
|
+
instagram: "instagram";
|
|
1681
|
+
}>;
|
|
1673
1682
|
mode: z.ZodLiteral<"public">;
|
|
1674
1683
|
capabilities: z.ZodArray<z.ZodObject<{
|
|
1675
1684
|
kind: z.ZodEnum<{
|
|
@@ -2175,7 +2184,10 @@ export declare const manifestSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
2175
2184
|
allowedOrigins: z.ZodArray<z.ZodString>;
|
|
2176
2185
|
surfaces: z.ZodOptional<z.ZodArray<z.ZodUnion<readonly [z.ZodObject<{
|
|
2177
2186
|
kind: z.ZodLiteral<"messaging">;
|
|
2178
|
-
channel: z.
|
|
2187
|
+
channel: z.ZodEnum<{
|
|
2188
|
+
whatsapp: "whatsapp";
|
|
2189
|
+
instagram: "instagram";
|
|
2190
|
+
}>;
|
|
2179
2191
|
mode: z.ZodLiteral<"public">;
|
|
2180
2192
|
capabilities: z.ZodArray<z.ZodObject<{
|
|
2181
2193
|
kind: z.ZodEnum<{
|
|
@@ -196,7 +196,7 @@ const embeddedAssistantSchema = z
|
|
|
196
196
|
z
|
|
197
197
|
.object({
|
|
198
198
|
kind: z.literal('messaging'),
|
|
199
|
-
channel: z.
|
|
199
|
+
channel: z.enum(['whatsapp', 'instagram']),
|
|
200
200
|
mode: z.literal('public'),
|
|
201
201
|
capabilities: z.array(assistantCapabilitySchema),
|
|
202
202
|
instructions: z.string().trim().min(1).max(MAX_SERVER_INSTRUCTIONS).optional(),
|
|
@@ -247,8 +247,13 @@ const embeddedAssistantSchema = z
|
|
|
247
247
|
.strict()
|
|
248
248
|
.superRefine((assistant, ctx) => {
|
|
249
249
|
const messaging = assistant.surfaces?.filter((surface) => surface.kind === 'messaging') ?? [];
|
|
250
|
-
|
|
251
|
-
|
|
250
|
+
const repeated = messaging.find((surface, index) => messaging.slice(0, index).some((earlier) => earlier.channel === surface.channel));
|
|
251
|
+
if (repeated)
|
|
252
|
+
ctx.addIssue({
|
|
253
|
+
code: 'custom',
|
|
254
|
+
path: ['surfaces'],
|
|
255
|
+
message: `duplicate ${repeated.channel} surface`,
|
|
256
|
+
});
|
|
252
257
|
if (assistant.allowedOrigins.length === 0 && messaging.length === 0) {
|
|
253
258
|
ctx.addIssue({
|
|
254
259
|
code: 'custom',
|
|
@@ -92,7 +92,7 @@ export interface PublicAdmissionContext {
|
|
|
92
92
|
readonly messaging?: string;
|
|
93
93
|
readonly provenance?: {
|
|
94
94
|
readonly kind: 'messaging';
|
|
95
|
-
readonly channel: 'whatsapp';
|
|
95
|
+
readonly channel: 'whatsapp' | 'instagram';
|
|
96
96
|
};
|
|
97
97
|
}
|
|
98
98
|
/** A single connector-operation invocation, fully resolved and credentialed by the runtime. */
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { authenticatedSurfaceOf, messagingSurfaceOf, publicSurfaceOf, } from '@noodle-borg/assistant-gateway/portable';
|
|
1
|
+
import { authenticatedSurfaceOf, CHANNEL_KINDS, messagingSurfaceOf, publicSurfaceOf, } from '@noodle-borg/assistant-gateway/portable';
|
|
2
2
|
import { ALL_CONVERSATION_SOURCES, } from './conversation-history/contracts.js';
|
|
3
3
|
const WIRE_SOURCES = {
|
|
4
4
|
websiteVisitors: 'website_visitors',
|
|
5
5
|
signedInCustomers: 'signed_in_customers',
|
|
6
6
|
whatsapp: 'whatsapp',
|
|
7
|
+
instagram: 'instagram',
|
|
7
8
|
};
|
|
8
9
|
/**
|
|
9
10
|
* The surfaces the served application declared `history: false` on (ADR 0241 decision 11), so staff
|
|
@@ -15,7 +16,9 @@ export function historyDisabledSurfaces(assistant) {
|
|
|
15
16
|
...(authenticatedSurfaceOf(assistant)?.history === false
|
|
16
17
|
? ['authenticatedWebsite']
|
|
17
18
|
: []),
|
|
18
|
-
...(messagingSurfaceOf(assistant)?.history === false
|
|
19
|
+
...(CHANNEL_KINDS.some((channel) => messagingSurfaceOf(assistant, channel)?.history === false)
|
|
20
|
+
? ['publicMessaging']
|
|
21
|
+
: []),
|
|
19
22
|
];
|
|
20
23
|
}
|
|
21
24
|
/**
|
|
@@ -54,6 +57,7 @@ export function historySettingsProjection(setting, allowance, revision, canEdit)
|
|
|
54
57
|
websiteVisitors: setting.sources.website_visitors,
|
|
55
58
|
signedInCustomers: setting.sources.signed_in_customers,
|
|
56
59
|
whatsapp: setting.sources.whatsapp,
|
|
60
|
+
instagram: setting.sources.instagram,
|
|
57
61
|
},
|
|
58
62
|
},
|
|
59
63
|
};
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { MIXED_CUSTOMER_AUTH_FEATURE_VERSION, serviceInfoResponseSchema, } from '@noodle-borg/wire-contracts';
|
|
2
|
-
export function serviceInfoPayload(buildInfo, developerMcp,
|
|
2
|
+
export function serviceInfoPayload(buildInfo, developerMcp, channels = {}) {
|
|
3
3
|
return serviceInfoResponseSchema.parse({
|
|
4
4
|
ok: true,
|
|
5
5
|
status: 'ok',
|
|
6
6
|
...buildInfo,
|
|
7
7
|
features: {
|
|
8
8
|
mixedCustomerAuth: MIXED_CUSTOMER_AUTH_FEATURE_VERSION,
|
|
9
|
-
...(whatsapp ? { whatsapp: 1 } : {}),
|
|
9
|
+
...(channels.whatsapp ? { whatsapp: 1 } : {}),
|
|
10
|
+
...(channels.instagram ? { instagram: 1 } : {}),
|
|
10
11
|
},
|
|
11
12
|
...(developerMcp
|
|
12
13
|
? { developerPlugin: { mcpCapabilityVersion: DEVELOPER_MCP_CAPABILITY_VERSION } }
|