@noodleseed/one 0.169.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 +6 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/capabilities.d.ts +19 -0
- package/dist/commands/capabilities.d.ts.map +1 -0
- package/dist/commands/capabilities.js +128 -0
- package/dist/commands/capabilities.js.map +1 -0
- 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 +3 -3
- package/node_modules/@noodle-borg/agent-kit/dist/curated/command-groups.js +8 -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 +2 -2
- package/node_modules/@noodle-borg/agent-kit/dist/generated/surface.js +5 -0
- package/node_modules/@noodle-borg/agent-kit/dist/skill-authoring-refs.js +3 -2
- 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-interactive.d.ts +1 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/assistant-interactive.js +4 -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 +19 -4
- package/node_modules/@noodle-borg/authoring/dist/assistant.js +51 -20
- package/node_modules/@noodle-borg/authoring/dist/index.d.ts +2 -1
- package/node_modules/@noodle-borg/authoring/dist/index.js +2 -1
- package/node_modules/@noodle-borg/authoring/dist/load-authored.js +1 -1
- package/node_modules/@noodle-borg/authoring/dist/recording.d.ts +2 -1
- package/node_modules/@noodle-borg/authoring/dist/recording.js +29 -1
- package/node_modules/@noodle-borg/authoring/dist/server.d.ts +3 -0
- package/node_modules/@noodle-borg/authoring/dist/server.js +5 -1
- package/node_modules/@noodle-borg/authoring/dist/web-capabilities.d.ts +17 -0
- package/node_modules/@noodle-borg/authoring/dist/web-capabilities.js +15 -0
- package/node_modules/@noodle-borg/authoring/package.json +1 -0
- package/node_modules/@noodle-borg/capabilities/dist/index.js +7 -0
- package/node_modules/@noodle-borg/cli-catalog/dist/catalog-data-channels.js +121 -0
- package/node_modules/@noodle-borg/cli-catalog/dist/catalog-data-hosted-observability.js +102 -0
- package/node_modules/@noodle-borg/cli-catalog/dist/index.js +1 -0
- package/node_modules/@noodle-borg/compiler/dist/artifact/assemble.js +3 -0
- package/node_modules/@noodle-borg/compiler/dist/artifact/config.js +1 -1
- package/node_modules/@noodle-borg/compiler/dist/artifact/types.d.ts +2 -0
- package/node_modules/@noodle-borg/compiler/dist/artifact/version.d.ts +2 -1
- package/node_modules/@noodle-borg/compiler/dist/artifact/version.js +2 -1
- package/node_modules/@noodle-borg/compiler/dist/compile.js +2 -0
- package/node_modules/@noodle-borg/compiler/dist/errors.d.ts +1 -1
- package/node_modules/@noodle-borg/compiler/dist/fulfilment-emit.js +18 -3
- package/node_modules/@noodle-borg/compiler/dist/manifest/schema.d.ts +106 -12
- package/node_modules/@noodle-borg/compiler/dist/manifest/schema.js +60 -29
- 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/compiler/dist/web-capabilities.js +55 -0
- package/node_modules/@noodle-borg/compiler/package.json +1 -0
- package/node_modules/@noodle-borg/connector-http/dist/ssrf.js +5 -0
- package/node_modules/@noodle-borg/knowledge-crawl/dist/bounded-body.js +38 -0
- package/node_modules/@noodle-borg/knowledge-crawl/dist/first-party.js +2 -3
- package/node_modules/@noodle-borg/knowledge-crawl/dist/html-text.js +10 -2
- package/node_modules/@noodle-borg/knowledge-crawl/dist/index.d.ts +1 -0
- package/node_modules/@noodle-borg/knowledge-crawl/dist/index.js +1 -0
- package/node_modules/@noodle-borg/knowledge-crawl/dist/page-reader.d.ts +26 -0
- package/node_modules/@noodle-borg/knowledge-crawl/dist/page-reader.js +127 -0
- package/node_modules/@noodle-borg/knowledge-crawl/dist/robots.js +48 -23
- package/node_modules/@noodle-borg/knowledge-crawl/package.json +1 -0
- package/node_modules/@noodle-borg/managed-capabilities/dist/budget.d.ts +29 -0
- package/node_modules/@noodle-borg/managed-capabilities/dist/budget.js +124 -0
- package/node_modules/@noodle-borg/managed-capabilities/dist/contracts.d.ts +212 -0
- package/node_modules/@noodle-borg/managed-capabilities/dist/contracts.js +113 -0
- package/node_modules/@noodle-borg/managed-capabilities/dist/errors.d.ts +8 -0
- package/node_modules/@noodle-borg/managed-capabilities/dist/errors.js +21 -0
- package/node_modules/@noodle-borg/managed-capabilities/dist/executor.d.ts +26 -0
- package/node_modules/@noodle-borg/managed-capabilities/dist/executor.js +156 -0
- package/node_modules/@noodle-borg/managed-capabilities/dist/limits.d.ts +15 -0
- package/node_modules/@noodle-borg/managed-capabilities/dist/limits.js +15 -0
- package/node_modules/@noodle-borg/managed-capabilities/dist/operator-contracts.d.ts +159 -0
- package/node_modules/@noodle-borg/managed-capabilities/dist/operator-contracts.js +69 -0
- package/node_modules/@noodle-borg/managed-capabilities/dist/operator-route.d.ts +5 -0
- package/node_modules/@noodle-borg/managed-capabilities/dist/operator-route.js +160 -0
- package/node_modules/@noodle-borg/managed-capabilities/dist/policy-store.d.ts +34 -0
- package/node_modules/@noodle-borg/managed-capabilities/dist/policy-store.js +61 -0
- package/node_modules/@noodle-borg/managed-capabilities/dist/portable.d.ts +10 -0
- package/node_modules/@noodle-borg/managed-capabilities/dist/portable.js +10 -0
- package/node_modules/@noodle-borg/managed-capabilities/dist/service.d.ts +83 -0
- package/node_modules/@noodle-borg/managed-capabilities/dist/service.js +139 -0
- package/node_modules/@noodle-borg/managed-capabilities/dist/urls.d.ts +3 -0
- package/node_modules/@noodle-borg/managed-capabilities/dist/urls.js +30 -0
- package/node_modules/@noodle-borg/managed-capabilities/package.json +49 -0
- package/node_modules/@noodle-borg/module/dist/contract.d.ts +11 -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/confirmation.js +29 -0
- package/node_modules/@noodle-borg/runtime/dist/connector/types.d.ts +2 -0
- package/node_modules/@noodle-borg/runtime/dist/execute.d.ts +3 -0
- package/node_modules/@noodle-borg/runtime/dist/execute.js +6 -1
- package/node_modules/@noodle-borg/runtime/dist/index.d.ts +2 -1
- package/node_modules/@noodle-borg/runtime/dist/index.js +2 -1
- package/node_modules/@noodle-borg/runtime/dist/interactive.js +10 -1
- package/node_modules/@noodle-borg/runtime/dist/managed-origins.js +8 -4
- package/node_modules/@noodle-borg/runtime/dist/operation-execution.js +3 -0
- 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/runtime/dist/result-meta.d.ts +2 -0
- package/node_modules/@noodle-borg/runtime/dist/result-meta.js +6 -0
- package/node_modules/@noodle-borg/runtime/dist/result.d.ts +4 -0
- package/node_modules/@noodle-borg/runtime/dist/web-capabilities.d.ts +71 -0
- package/node_modules/@noodle-borg/runtime/dist/web-capabilities.js +41 -0
- package/node_modules/@noodle-borg/runtime/package.json +1 -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/local-options.d.ts +1 -0
- package/node_modules/@noodle-borg/service/dist/local-web-capabilities.js +13 -0
- package/node_modules/@noodle-borg/service/dist/mcp-protocol-runtime.js +21 -0
- package/node_modules/@noodle-borg/service/dist/modules/route-dispatch.js +13 -0
- package/node_modules/@noodle-borg/service/dist/registry-compile.js +3 -0
- package/node_modules/@noodle-borg/service/dist/registry-types.d.ts +1 -0
- package/node_modules/@noodle-borg/service/dist/registry.js +3 -0
- package/node_modules/@noodle-borg/service/dist/routes/assistant-agent.js +66 -35
- package/node_modules/@noodle-borg/service/dist/routes/assistant-interactions.js +7 -4
- 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/serve-local.js +14 -0
- package/node_modules/@noodle-borg/service/dist/serve-resource-auth.js +10 -0
- package/node_modules/@noodle-borg/service/dist/service.js +37 -33
- package/node_modules/@noodle-borg/service/dist/web-capabilities.js +39 -0
- package/node_modules/@noodle-borg/service/package.json +3 -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 +2 -0
- package/node_modules/@noodle-borg/wire-contracts/dist/index.js +2 -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/node_modules/@noodle-borg/wire-contracts/package.json +1 -0
- package/package.json +3 -2
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { ADMISSION_DEFAULTS } from '@noodle-borg/admission-limits/portable';
|
|
2
2
|
import { assistantCoreModelMessages, assistantTurnModelContextMessages, requestAssistantSuggestedPrompts, requestModelCompletion, resolveAssistantContextProviderModelResult, } from '@noodle-borg/assistant-gateway/model-runtime';
|
|
3
|
-
import { assistantGuideModelContext, assistantModelToolOncePerSession, assistantModelToolRequiredWhenVisible, assistantOmittedToolResult, assistantViewAvailableData, dispatchAssistantTool, projectAssistantGuide, publicSurfaceOf, recoverableAssistantView, remainingTurnTokens, selectAssistantModelTools, toolTouchesDelegatedAuth, withAssistantSessionExecutionAuthority, } from '@noodle-borg/assistant-gateway/portable';
|
|
3
|
+
import { assistantGuideModelContext, assistantModelToolOncePerSession, assistantModelToolRequiredWhenVisible, assistantOmittedToolResult, assistantViewAvailableData, dispatchAssistantTool, dispatchMessagingReadTool, isMessagingTurn, projectAssistantGuide, publicSurfaceOf, recoverableAssistantView, remainingTurnTokens, selectAssistantModelTools, toolTouchesDelegatedAuth, withAssistantSessionExecutionAuthority, } from '@noodle-borg/assistant-gateway/portable';
|
|
4
4
|
import { validateJsonSchemaWithDefaults } from '@noodle-borg/compiler';
|
|
5
5
|
import { guardedFetch } from '@noodle-borg/connector-http';
|
|
6
|
+
import { CapabilityBudget } from '@noodle-borg/managed-capabilities';
|
|
6
7
|
import { evaluateToolAuthorization } from '@noodle-borg/protocol';
|
|
7
8
|
import { interceptForElevation, offersSignIn } from './assistant-elevation.js';
|
|
8
9
|
import { assistantKnowledgeModelTools, executeAssistantKnowledgeSearch, findAssistantKnowledgeComponent, KNOWLEDGE_CITATION_GUIDANCE, resolveAssistantKnowledge, } from './assistant-knowledge.js';
|
|
@@ -33,14 +34,15 @@ export function createAssistantTurnStats() {
|
|
|
33
34
|
};
|
|
34
35
|
}
|
|
35
36
|
/** Run one model/tool loop against the immutable invocation-context snapshot for this turn. */
|
|
36
|
-
export async function runAgentTurn(target, session, message, context, deps, emit, modelContext, pageContext, stats = createAssistantTurnStats(), suggestions = false) {
|
|
37
|
+
export async function runAgentTurn(target, session, message, context, deps, emit, modelContext, pageContext, stats = createAssistantTurnStats(), suggestions = false, transport) {
|
|
37
38
|
// Every terminal failure in this loop is one shape: a code, and the turn ends. Naming it keeps the
|
|
38
39
|
// dozen sites readable and stops a new one inventing a different envelope.
|
|
39
40
|
const fail = (code) => emit({ event: 'error', data: { code } });
|
|
40
41
|
const assistant = target.served.artifact.server.assistant;
|
|
41
42
|
if (!assistant)
|
|
42
43
|
return fail('assistant_unavailable');
|
|
43
|
-
const binding =
|
|
44
|
+
const binding = transport?.binding ??
|
|
45
|
+
(await resolveAssistantModelBinding(target, session.tenant, session.deploymentId, deps));
|
|
44
46
|
if (!binding) {
|
|
45
47
|
return emit({
|
|
46
48
|
event: 'error',
|
|
@@ -98,12 +100,15 @@ export async function runAgentTurn(target, session, message, context, deps, emit
|
|
|
98
100
|
// Across every step of this turn, not per step: eight tool calls is eight, however the model splits
|
|
99
101
|
// them, or a model that loops one call per step would spend the budget a step at a time.
|
|
100
102
|
let toolCallsThisTurn = 0;
|
|
103
|
+
const capabilityBudget = new CapabilityBudget();
|
|
101
104
|
let omittedToolRecoveries = 0;
|
|
102
105
|
let remainingTokens = binding.requestPolicy?.maxTokensPerTurn;
|
|
103
106
|
const turnSignal = binding.requestPolicy?.maxTurnMs === undefined
|
|
104
107
|
? undefined
|
|
105
108
|
: AbortSignal.timeout(binding.requestPolicy.maxTurnMs);
|
|
106
109
|
for (let step = 0; step < bounds.steps; step += 1) {
|
|
110
|
+
await transport?.beforeStep();
|
|
111
|
+
transport?.signal.throwIfAborted();
|
|
107
112
|
if (remainingTokens !== undefined && remainingTokens <= 0)
|
|
108
113
|
return fail('model_token_budget_exhausted');
|
|
109
114
|
const requestTokenLimit = Math.min(binding.requestPolicy?.maxCompletionTokens ?? Number.MAX_SAFE_INTEGER, remainingTokens ?? Number.MAX_SAFE_INTEGER);
|
|
@@ -116,7 +121,11 @@ export async function runAgentTurn(target, session, message, context, deps, emit
|
|
|
116
121
|
streamedContent += delta;
|
|
117
122
|
emit({ event: 'content', data: { delta } });
|
|
118
123
|
}
|
|
119
|
-
: () => undefined, requiredToolForStep === undefined ? assistantKnowledgeModelTools(knowledge) : [], stepModelTools, requestTokenLimit === Number.MAX_SAFE_INTEGER ? undefined : requestTokenLimit,
|
|
124
|
+
: () => undefined, requiredToolForStep === undefined ? assistantKnowledgeModelTools(knowledge) : [], stepModelTools, requestTokenLimit === Number.MAX_SAFE_INTEGER ? undefined : requestTokenLimit, transport
|
|
125
|
+
? turnSignal
|
|
126
|
+
? AbortSignal.any([transport.signal, turnSignal])
|
|
127
|
+
: transport.signal
|
|
128
|
+
: turnSignal, requiredToolForStep === undefined ? undefined : 'required');
|
|
120
129
|
stats.promptTokens += completion.usage?.promptTokens ?? 0;
|
|
121
130
|
stats.completionTokens += completion.usage?.completionTokens ?? 0;
|
|
122
131
|
stats.reasoningTokens += completion.usage?.reasoningTokens ?? 0;
|
|
@@ -135,7 +144,10 @@ export async function runAgentTurn(target, session, message, context, deps, emit
|
|
|
135
144
|
const assistantContent = response.content || streamedContent;
|
|
136
145
|
if (assistantContent)
|
|
137
146
|
messages.push({ role: 'assistant', content: assistantContent });
|
|
138
|
-
if (
|
|
147
|
+
if (!isMessagingTurn(session) &&
|
|
148
|
+
suggestions &&
|
|
149
|
+
step + 1 < bounds.steps &&
|
|
150
|
+
(remainingTokens ?? 1) > 0) {
|
|
139
151
|
try {
|
|
140
152
|
const prompts = await requestAssistantSuggestedPrompts(binding, messages, assistantModelFetcher(deps.modelFetch), stats, remainingTokens, turnSignal);
|
|
141
153
|
if (prompts.length > 0) {
|
|
@@ -162,6 +174,7 @@ export async function runAgentTurn(target, session, message, context, deps, emit
|
|
|
162
174
|
stats.toolCalls += 1;
|
|
163
175
|
if (toolCallsThisTurn > bounds.toolCalls)
|
|
164
176
|
return fail('tool_call_budget_exhausted');
|
|
177
|
+
await transport?.beforeStep();
|
|
165
178
|
const knowledgeComponent = findAssistantKnowledgeComponent(knowledge, call.function.name);
|
|
166
179
|
if (knowledge !== undefined && knowledgeComponent !== undefined) {
|
|
167
180
|
let knowledgeArgs;
|
|
@@ -203,21 +216,23 @@ export async function runAgentTurn(target, session, message, context, deps, emit
|
|
|
203
216
|
// The hosted broker exposes its delegated binding keys so the intercept can classify on
|
|
204
217
|
// connector auth kind too; a broker without the method leaves the base classification alone.
|
|
205
218
|
const delegatedKeys = target.served.deps.broker.assistantDelegatedAuthKeys?.();
|
|
206
|
-
const elevation =
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
219
|
+
const elevation = isMessagingTurn(session)
|
|
220
|
+
? undefined
|
|
221
|
+
: await interceptForElevation({
|
|
222
|
+
tool,
|
|
223
|
+
session,
|
|
224
|
+
assistant,
|
|
225
|
+
...(target.served.artifact.server.state === undefined
|
|
226
|
+
? {}
|
|
227
|
+
: { state: target.served.artifact.server.state }),
|
|
228
|
+
elevations: deps.elevations,
|
|
229
|
+
...(delegatedKeys !== undefined && delegatedKeys.size > 0
|
|
230
|
+
? {
|
|
231
|
+
requiresDelegatedIdentity: (candidate) => toolTouchesDelegatedAuth(candidate, delegatedKeys),
|
|
232
|
+
}
|
|
233
|
+
: {}),
|
|
234
|
+
now: deps.clock?.() ?? new Date(),
|
|
235
|
+
});
|
|
221
236
|
if (elevation) {
|
|
222
237
|
stats.interactionCount += 1;
|
|
223
238
|
emit(elevation.event);
|
|
@@ -243,19 +258,34 @@ export async function runAgentTurn(target, session, message, context, deps, emit
|
|
|
243
258
|
if (coerced.issues.length > 0)
|
|
244
259
|
return fail('invalid_tool_arguments');
|
|
245
260
|
args = coerced.value;
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
261
|
+
await transport?.beforeStep();
|
|
262
|
+
const dispatch = isMessagingTurn(session)
|
|
263
|
+
? await dispatchMessagingReadTool({
|
|
264
|
+
artifact: target.served.artifact,
|
|
265
|
+
tool,
|
|
266
|
+
arguments: args,
|
|
267
|
+
executeDeps: {
|
|
268
|
+
...target.served.deps,
|
|
269
|
+
capabilityBudget,
|
|
270
|
+
...(transport?.signal ? { signal: transport.signal } : {}),
|
|
271
|
+
},
|
|
272
|
+
context,
|
|
273
|
+
session,
|
|
274
|
+
claimTool: transport?.claimTool ?? (async () => false),
|
|
275
|
+
})
|
|
276
|
+
: await dispatchAssistantTool({
|
|
277
|
+
artifact: target.served.artifact,
|
|
278
|
+
tool,
|
|
279
|
+
arguments: args,
|
|
280
|
+
executeDeps: withAssistantSessionExecutionAuthority({ ...target.served.deps, capabilityBudget }, target.served.artifact, session),
|
|
281
|
+
caller: session.caller,
|
|
282
|
+
context,
|
|
283
|
+
session,
|
|
284
|
+
store: deps.store,
|
|
285
|
+
audit: deps.audit,
|
|
286
|
+
now: () => deps.clock?.() ?? new Date(),
|
|
287
|
+
onToolStarted: () => emit({ event: 'tool_started', data: { id: call.id, tool: tool.name } }),
|
|
288
|
+
});
|
|
259
289
|
if (assistantModelToolOncePerSession(tool) &&
|
|
260
290
|
(dispatch.kind !== 'event' || dispatch.event !== 'error')) {
|
|
261
291
|
modelTools = modelTools.filter((candidate) => candidate.name !== tool.name);
|
|
@@ -272,8 +302,9 @@ export async function runAgentTurn(target, session, message, context, deps, emit
|
|
|
272
302
|
result: dispatch.output,
|
|
273
303
|
arguments: args,
|
|
274
304
|
}, (failure) => deps.logger?.warn('assistant.view.unresolved', { ...failure }));
|
|
275
|
-
if (view) {
|
|
276
|
-
|
|
305
|
+
if (view && !isMessagingTurn(session)) {
|
|
306
|
+
if (!dispatch.ephemeral)
|
|
307
|
+
await deps.store.replaceLatestView(session.id, recoverableAssistantView(view));
|
|
277
308
|
emit({ event: 'view_available', data: { ...view } });
|
|
278
309
|
}
|
|
279
310
|
messages.push({
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { AssistantInteractionCapacityError, assistantConfirmationReview, assistantConfirmationTransition, assistantInputTransition, assistantInteractionTool, assistantPreparedArgumentReview, assistantPreparedToolContinuation, assistantSafeOutput, assistantToolContinuation, assistantViewAvailableData, auditAssistantInteraction, boundedAssistantExecution, recoverableAssistantView, withAssistantSessionExecutionAuthority, } from '@noodle-borg/assistant-gateway/portable';
|
|
2
|
-
import { executePreparedTool, executeToolInteractive, resumeTool, resumeToolPreparation, } from '@noodle-borg/runtime';
|
|
2
|
+
import { executePreparedTool, executeToolInteractive, hasEphemeralEvidence, resumeTool, resumeToolPreparation, } from '@noodle-borg/runtime';
|
|
3
3
|
import { readJsonBody, sendJson } from '@noodle-borg/transport-http';
|
|
4
4
|
import { applyBrowserCors, authenticateSession, now, } from './assistant.js';
|
|
5
5
|
import { replayOrConflict, writeUnknownOutcome } from './assistant-interaction-replay.js';
|
|
@@ -347,7 +347,9 @@ async function finishExecution(res, deps, session, interaction, context, result,
|
|
|
347
347
|
}
|
|
348
348
|
const completedTool = target.served.artifact.tools.find((tool) => tool.name === toolName);
|
|
349
349
|
const safeOutput = assistantSafeOutput(completedTool?.outputSchema, result.output);
|
|
350
|
-
const
|
|
350
|
+
const ephemeral = hasEphemeralEvidence(result.output);
|
|
351
|
+
const retainedOutput = ephemeral ? { evidenceRetained: false } : safeOutput;
|
|
352
|
+
const completed = await completeAssistantInteractionExecution(deps, session, interaction, 'succeeded', 'tool_completed', toolName, { result: retainedOutput });
|
|
351
353
|
if (!completed)
|
|
352
354
|
return writeUnknownOutcome(res, deps, session, interaction, 'accept');
|
|
353
355
|
await auditAssistantInteraction({
|
|
@@ -364,7 +366,7 @@ async function finishExecution(res, deps, session, interaction, context, result,
|
|
|
364
366
|
// Model-facing scaffolding: the panel saw the tool_completed card, never this row's tool JSON.
|
|
365
367
|
{
|
|
366
368
|
role: 'assistant',
|
|
367
|
-
content: `Completed ${toolName}: ${JSON.stringify(
|
|
369
|
+
content: `Completed ${toolName}: ${JSON.stringify(retainedOutput)}`,
|
|
368
370
|
kind: 'narration',
|
|
369
371
|
},
|
|
370
372
|
]);
|
|
@@ -377,7 +379,8 @@ async function finishExecution(res, deps, session, interaction, context, result,
|
|
|
377
379
|
result: safeOutput,
|
|
378
380
|
}, (failure) => deps.logger?.warn('assistant.view.unresolved', { ...failure }));
|
|
379
381
|
if (view) {
|
|
380
|
-
|
|
382
|
+
if (!ephemeral)
|
|
383
|
+
await deps.store.replaceLatestView(session.id, recoverableAssistantView(view));
|
|
381
384
|
writeViewAvailable(res, view);
|
|
382
385
|
}
|
|
383
386
|
await narrateResolvedInteraction(res, deps, target, session, toolName, 'accept', safeOutput, context, suggestions);
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
import { resolveManagedVariablesInString } from '../managed-config-expressions.js';
|
|
2
2
|
import { resolveConfigScope } from '../store.js';
|
|
3
|
-
export async function resolveAssistantModelBinding(target, tenant, deploymentId, deps) {
|
|
3
|
+
export async function resolveAssistantModelBinding(target, tenant, deploymentId, deps, channel) {
|
|
4
4
|
const declaration = target.served.artifact.server.assistant?.model;
|
|
5
5
|
if (declaration === undefined)
|
|
6
6
|
return undefined;
|
|
7
7
|
if (declaration.kind === 'noodle-managed') {
|
|
8
|
-
return deps.managedModelResolver?.resolve({
|
|
8
|
+
return deps.managedModelResolver?.resolve({
|
|
9
|
+
tenant,
|
|
10
|
+
deploymentId,
|
|
11
|
+
...(channel ? { channel } : {}),
|
|
12
|
+
});
|
|
9
13
|
}
|
|
10
14
|
const scope = resolveConfigScope(tenant);
|
|
11
15
|
const [variables, secrets] = await Promise.all([
|
|
@@ -59,7 +59,20 @@ export async function projectBusinessChannels(installation, canEdit, base, deps)
|
|
|
59
59
|
};
|
|
60
60
|
const registered = await deps.registry.getActiveByTenant(tenant);
|
|
61
61
|
const target = registered && (await resolveTargetOrigins(registered));
|
|
62
|
-
const
|
|
62
|
+
const whatsapp = await deps.readWhatsApp?.(tenant);
|
|
63
|
+
const shared = {
|
|
64
|
+
revision: installation.revision,
|
|
65
|
+
active: installation.intakeActive,
|
|
66
|
+
canEdit,
|
|
67
|
+
...(whatsapp
|
|
68
|
+
? {
|
|
69
|
+
whatsapp: {
|
|
70
|
+
...whatsapp,
|
|
71
|
+
...(!installation.intakeActive ? { status: 'paused' } : {}),
|
|
72
|
+
},
|
|
73
|
+
}
|
|
74
|
+
: {}),
|
|
75
|
+
};
|
|
63
76
|
if (!target?.deploymentId)
|
|
64
77
|
return {
|
|
65
78
|
...shared,
|
|
@@ -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
|
|
@@ -3,6 +3,7 @@ import { InMemoryControlPlaneStore } from '@noodle-borg/control-plane/portable';
|
|
|
3
3
|
import { InMemoryAssetStore } from './assets.js';
|
|
4
4
|
import { createCustomerVerifierFactory, createHostedCustomerVerifierFactory, createLocalDevtoolsCustomerVerifierFactory, } from './customer-verifier.js';
|
|
5
5
|
import { createLocalDevtoolsDelegatedCredentialSource } from './local-devtools-delegated-credentials.js';
|
|
6
|
+
import { localWebCapabilities } from './local-web-capabilities.js';
|
|
6
7
|
import { ServerRegistry } from './registry.js';
|
|
7
8
|
import { isLoopbackHost } from './serve-resource-auth.js';
|
|
8
9
|
import { createServiceHandler } from './service.js';
|
|
@@ -48,7 +49,9 @@ export async function serveLocalService(options = {}) {
|
|
|
48
49
|
const delegatedCredentials = directCustomerAuth
|
|
49
50
|
? createLocalDevtoolsDelegatedCredentialSource()
|
|
50
51
|
: undefined;
|
|
52
|
+
const capabilities = options.runtime?.capabilities ?? localWebCapabilities();
|
|
51
53
|
const registry = new ServerRegistry(undefined, undefined, configStore, {
|
|
54
|
+
capabilities,
|
|
52
55
|
customerVerifierFactory,
|
|
53
56
|
...(delegatedCredentials === undefined
|
|
54
57
|
? {}
|
|
@@ -62,6 +65,17 @@ export async function serveLocalService(options = {}) {
|
|
|
62
65
|
: { localDevtoolsDelegatedExchange: options.localDevtoolsDelegatedExchange }),
|
|
63
66
|
});
|
|
64
67
|
const handler = createServiceHandler(registry, {
|
|
68
|
+
capabilities,
|
|
69
|
+
deployGate: {
|
|
70
|
+
authorize: () => ({
|
|
71
|
+
ok: true,
|
|
72
|
+
identity: {
|
|
73
|
+
subject: 'local-operator',
|
|
74
|
+
email: 'local-operator@localhost',
|
|
75
|
+
superAdmin: true,
|
|
76
|
+
},
|
|
77
|
+
}),
|
|
78
|
+
},
|
|
65
79
|
controlPlaneStore,
|
|
66
80
|
configStore,
|
|
67
81
|
assetStore: new InMemoryAssetStore(),
|
|
@@ -1,10 +1,20 @@
|
|
|
1
1
|
import { resolveMcpSubdomainTenant } from '@noodle-borg/control-plane/portable';
|
|
2
2
|
import { normalizePublicBaseDomain, parseCanonicalPublicMcpUrl, parseLegacyTenantMcpPath, } from '@noodle-borg/module';
|
|
3
3
|
import { resolveTenantBridgeAuthVariables } from './managed-config-expressions.js';
|
|
4
|
+
import { createHostedMcpRequestStateManager } from './mcp-protocol-runtime.js';
|
|
4
5
|
import { resolveConfigScope, } from './store.js';
|
|
5
6
|
export function isLoopbackHost(host) {
|
|
6
7
|
return host === '127.0.0.1' || host === '::1' || host === 'localhost';
|
|
7
8
|
}
|
|
9
|
+
export function resolveMcpRequestState(options, secretBox) {
|
|
10
|
+
return (options.mcpRequestState ??
|
|
11
|
+
createHostedMcpRequestStateManager({
|
|
12
|
+
...(options.wrappingMasterKey === undefined && options.secretMasterKey !== undefined
|
|
13
|
+
? { secretMasterKey: options.secretMasterKey }
|
|
14
|
+
: {}),
|
|
15
|
+
...(secretBox === undefined ? {} : { secretBox }),
|
|
16
|
+
}));
|
|
17
|
+
}
|
|
8
18
|
export function assertLocalDevtoolsServiceBoundary(host, options) {
|
|
9
19
|
if (options.localDevtoolsDirectFirebaseAuth === true && !isLoopbackHost(host)) {
|
|
10
20
|
throw new Error('local Devtools Firebase verification requires a loopback service bind');
|