@noodleseed/one 0.150.0 → 0.151.1
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/assistant-ops.d.ts.map +1 -1
- package/dist/commands/assistant-ops.js +5 -1
- package/dist/commands/assistant-ops.js.map +1 -1
- package/dist/commands/assistant-sponsorship-ops.d.ts +3 -0
- package/dist/commands/assistant-sponsorship-ops.d.ts.map +1 -0
- package/dist/commands/assistant-sponsorship-ops.js +170 -0
- package/dist/commands/assistant-sponsorship-ops.js.map +1 -0
- package/dist/commands/assistant-surface-ops.d.ts.map +1 -1
- package/dist/commands/assistant-surface-ops.js +3 -1
- package/dist/commands/assistant-surface-ops.js.map +1 -1
- package/dist/commands/intents-ops.js +1 -1
- package/dist/commands/intents-ops.js.map +1 -1
- package/node_modules/@noodle-borg/admission-limits/dist/counter-store.d.ts +11 -0
- package/node_modules/@noodle-borg/admission-limits/dist/in-memory-counter-store.d.ts +3 -2
- package/node_modules/@noodle-borg/admission-limits/dist/in-memory-counter-store.js +22 -0
- 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 +1 -1
- package/node_modules/@noodle-borg/agent-kit/package.json +1 -1
- package/node_modules/@noodle-borg/assistant-gateway/dist/app-tool-call.js +6 -3
- package/node_modules/@noodle-borg/assistant-gateway/dist/managed-spend.d.ts +29 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/managed-spend.js +27 -1
- package/node_modules/@noodle-borg/assistant-gateway/dist/model-error.js +6 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/model-request.d.ts +11 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/model-request.js +22 -1
- package/node_modules/@noodle-borg/assistant-gateway/package.json +1 -1
- package/node_modules/@noodle-borg/cli-catalog/dist/catalog-data-assistant.js +94 -0
- package/node_modules/@noodle-borg/cli-catalog/dist/catalog-data-hosted-observability.js +1 -1
- package/node_modules/@noodle-borg/module/dist/index.d.ts +1 -0
- package/node_modules/@noodle-borg/module/dist/index.js +1 -0
- package/node_modules/@noodle-borg/module/dist/logger.d.ts +15 -0
- package/node_modules/@noodle-borg/module/dist/logger.js +16 -0
- package/node_modules/@noodle-borg/observability/dist/assistant-app-tool-usage.js +23 -1
- package/node_modules/@noodle-borg/service/dist/observability-runtime.js +6 -4
- package/node_modules/@noodle-borg/service/dist/routes/assistant-agent.js +2 -2
- package/node_modules/@noodle-borg/service/dist/routes/assistant.js +26 -16
- package/node_modules/@noodle-borg/service/dist/routes/intent-capture.js +12 -0
- package/node_modules/@noodle-borg/service/dist/service.js +3 -2
- package/node_modules/@noodle-borg/service/package.json +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/managed-assistant-sponsorship.d.ts +297 -0
- package/node_modules/@noodle-borg/wire-contracts/dist/managed-assistant-sponsorship.js +94 -0
- package/node_modules/@noodleseed/assistant/package.json +1 -1
- package/package.json +2 -2
|
@@ -154,6 +154,99 @@ const BUDGET_SUBCOMMAND = subcommand({
|
|
|
154
154
|
}),
|
|
155
155
|
],
|
|
156
156
|
});
|
|
157
|
+
const SPONSORSHIP_ACCOUNT_ARGUMENT = argument({
|
|
158
|
+
...ARGUMENT_FIELDS,
|
|
159
|
+
name: 'billing-account-id',
|
|
160
|
+
type: 'string',
|
|
161
|
+
summary: 'Billing account receiving the sponsored allowance.',
|
|
162
|
+
required: true,
|
|
163
|
+
});
|
|
164
|
+
const SPONSORSHIP_FLAGS = [SERVICE_FLAG, AUTH_TOKEN_FLAG, JSON_FLAG];
|
|
165
|
+
const SPONSORSHIP_REASON_FLAG = flag({
|
|
166
|
+
...FLAG_FIELDS,
|
|
167
|
+
name: 'reason',
|
|
168
|
+
type: 'string',
|
|
169
|
+
value: '<text>',
|
|
170
|
+
summary: 'Auditable reason for granting or revoking sponsored usage.',
|
|
171
|
+
required: true,
|
|
172
|
+
});
|
|
173
|
+
const SPONSORSHIP_IDEMPOTENCY_FLAG = flag({
|
|
174
|
+
...FLAG_FIELDS,
|
|
175
|
+
name: 'idempotency-key',
|
|
176
|
+
type: 'string',
|
|
177
|
+
value: '<key>',
|
|
178
|
+
summary: 'Stable retry key for exactly-once mutation semantics.',
|
|
179
|
+
required: true,
|
|
180
|
+
});
|
|
181
|
+
const SPONSORSHIP_SUBCOMMAND = subcommand({
|
|
182
|
+
...SUBCOMMAND_FIELDS,
|
|
183
|
+
name: 'sponsorship',
|
|
184
|
+
summary: 'Inspect sponsored managed-assistant usage or operate expiring bonus grants.',
|
|
185
|
+
subcommands: [
|
|
186
|
+
subcommand({
|
|
187
|
+
...SUBCOMMAND_FIELDS,
|
|
188
|
+
name: 'inspect',
|
|
189
|
+
summary: 'Show one billing account’s allowance, usage, reset, and grants.',
|
|
190
|
+
arguments: [SPONSORSHIP_ACCOUNT_ARGUMENT],
|
|
191
|
+
flags: [...SPONSORSHIP_FLAGS],
|
|
192
|
+
jsonOutput: { mode: 'single' },
|
|
193
|
+
}),
|
|
194
|
+
subcommand({
|
|
195
|
+
...SUBCOMMAND_FIELDS,
|
|
196
|
+
name: 'grant',
|
|
197
|
+
summary: 'Add a bounded daily allowance until an explicit expiry.',
|
|
198
|
+
arguments: [SPONSORSHIP_ACCOUNT_ARGUMENT],
|
|
199
|
+
flags: [
|
|
200
|
+
flag({
|
|
201
|
+
...FLAG_FIELDS,
|
|
202
|
+
name: 'turns-per-day',
|
|
203
|
+
type: 'integer',
|
|
204
|
+
value: '<count>',
|
|
205
|
+
summary: 'Additional sponsored turns per UTC day.',
|
|
206
|
+
required: true,
|
|
207
|
+
constraints: { minimum: 1, maximum: 100_000 },
|
|
208
|
+
}),
|
|
209
|
+
flag({
|
|
210
|
+
...FLAG_FIELDS,
|
|
211
|
+
name: 'starts-at',
|
|
212
|
+
type: 'string',
|
|
213
|
+
value: '<timestamp>',
|
|
214
|
+
summary: 'Optional ISO timestamp; defaults to now.',
|
|
215
|
+
constraints: { default: 'now' },
|
|
216
|
+
}),
|
|
217
|
+
flag({
|
|
218
|
+
...FLAG_FIELDS,
|
|
219
|
+
name: 'expires-at',
|
|
220
|
+
type: 'string',
|
|
221
|
+
value: '<timestamp>',
|
|
222
|
+
summary: 'Required ISO timestamp after which the grant stops applying.',
|
|
223
|
+
required: true,
|
|
224
|
+
}),
|
|
225
|
+
SPONSORSHIP_REASON_FLAG,
|
|
226
|
+
SPONSORSHIP_IDEMPOTENCY_FLAG,
|
|
227
|
+
...SPONSORSHIP_FLAGS,
|
|
228
|
+
],
|
|
229
|
+
jsonOutput: { mode: 'single' },
|
|
230
|
+
}),
|
|
231
|
+
subcommand({
|
|
232
|
+
...SUBCOMMAND_FIELDS,
|
|
233
|
+
name: 'revoke',
|
|
234
|
+
summary: 'Revoke one bonus grant without deleting its audit history.',
|
|
235
|
+
arguments: [
|
|
236
|
+
SPONSORSHIP_ACCOUNT_ARGUMENT,
|
|
237
|
+
argument({
|
|
238
|
+
...ARGUMENT_FIELDS,
|
|
239
|
+
name: 'grant-id',
|
|
240
|
+
type: 'string',
|
|
241
|
+
summary: 'Managed-assistant sponsorship grant identifier.',
|
|
242
|
+
required: true,
|
|
243
|
+
}),
|
|
244
|
+
],
|
|
245
|
+
flags: [SPONSORSHIP_REASON_FLAG, SPONSORSHIP_IDEMPOTENCY_FLAG, ...SPONSORSHIP_FLAGS],
|
|
246
|
+
jsonOutput: { mode: 'single' },
|
|
247
|
+
}),
|
|
248
|
+
],
|
|
249
|
+
});
|
|
157
250
|
const CLIENTS_SUBCOMMAND = subcommand({
|
|
158
251
|
...SUBCOMMAND_FIELDS,
|
|
159
252
|
name: 'clients',
|
|
@@ -269,6 +362,7 @@ export const ASSISTANT_COMMAND = {
|
|
|
269
362
|
CLIENTS_SUBCOMMAND,
|
|
270
363
|
EMBEDS_SUBCOMMAND,
|
|
271
364
|
BUDGET_SUBCOMMAND,
|
|
365
|
+
SPONSORSHIP_SUBCOMMAND,
|
|
272
366
|
subcommand({
|
|
273
367
|
...SUBCOMMAND_FIELDS,
|
|
274
368
|
name: 'usage',
|
|
@@ -352,7 +352,7 @@ export const CATALOG_HOSTED_OBSERVABILITY = [
|
|
|
352
352
|
name: 'intents',
|
|
353
353
|
section: 'operate',
|
|
354
354
|
helpRank: 5,
|
|
355
|
-
summary: 'Optional model-supplied user-goal insights
|
|
355
|
+
summary: 'Optional model-supplied user-goal insights for private-preview environments.',
|
|
356
356
|
arguments: [],
|
|
357
357
|
subcommands: [
|
|
358
358
|
subcommand({
|
|
@@ -4,6 +4,7 @@ export * from './deployment-activation.js';
|
|
|
4
4
|
export * from './deployment-automation.js';
|
|
5
5
|
export * from './endpoint-url.js';
|
|
6
6
|
export * from './intent.js';
|
|
7
|
+
export * from './logger.js';
|
|
7
8
|
export * from './organization-provisioning.js';
|
|
8
9
|
export * from './platform-human-identity.js';
|
|
9
10
|
export * from './request-analytics.js';
|
|
@@ -4,6 +4,7 @@ export * from './deployment-activation.js';
|
|
|
4
4
|
export * from './deployment-automation.js';
|
|
5
5
|
export * from './endpoint-url.js';
|
|
6
6
|
export * from './intent.js';
|
|
7
|
+
export * from './logger.js';
|
|
7
8
|
export * from './organization-provisioning.js';
|
|
8
9
|
export * from './platform-human-identity.js';
|
|
9
10
|
export * from './request-analytics.js';
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { ModuleLogger } from './contract.js';
|
|
2
|
+
export type ModuleLogLevel = 'debug' | 'info' | 'warn' | 'error';
|
|
3
|
+
export type ModuleLogFields = Readonly<Record<string, unknown>>;
|
|
4
|
+
/** Adapt a module host logger to the child-capable shape used by service integrations. */
|
|
5
|
+
export interface AdaptedModuleLogger {
|
|
6
|
+
readonly level: ModuleLogLevel;
|
|
7
|
+
log(level: ModuleLogLevel, event: string, fields?: ModuleLogFields): void;
|
|
8
|
+
debug(event: string, fields?: ModuleLogFields): void;
|
|
9
|
+
info(event: string, fields?: ModuleLogFields): void;
|
|
10
|
+
warn(event: string, fields?: ModuleLogFields): void;
|
|
11
|
+
error(event: string, fields?: ModuleLogFields): void;
|
|
12
|
+
child(fields: ModuleLogFields): AdaptedModuleLogger;
|
|
13
|
+
}
|
|
14
|
+
export declare function adaptModuleLogger(logger: ModuleLogger, base?: ModuleLogFields): AdaptedModuleLogger;
|
|
15
|
+
//# sourceMappingURL=logger.d.ts.map
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export function adaptModuleLogger(logger, base = {}) {
|
|
2
|
+
const fields = (extra) => ({ ...base, ...extra });
|
|
3
|
+
const log = (level, event, extra) => {
|
|
4
|
+
logger[level](event, fields(extra));
|
|
5
|
+
};
|
|
6
|
+
return {
|
|
7
|
+
level: 'debug',
|
|
8
|
+
log,
|
|
9
|
+
debug: (event, extra) => log('debug', event, extra),
|
|
10
|
+
info: (event, extra) => log('info', event, extra),
|
|
11
|
+
warn: (event, extra) => log('warn', event, extra),
|
|
12
|
+
error: (event, extra) => log('error', event, extra),
|
|
13
|
+
child: (extra) => adaptModuleLogger(logger, fields(extra)),
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=logger.js.map
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { captureAssistantUsage } from './assistant-usage.js';
|
|
2
3
|
/** Project one apps-bridge tool call onto the shared tenant request stream. */
|
|
3
4
|
export function assistantAppToolCallRequestEvent(session, input) {
|
|
4
5
|
const isPublic = session.publicEmbedId !== undefined;
|
|
@@ -13,7 +14,7 @@ export function assistantAppToolCallRequestEvent(session, input) {
|
|
|
13
14
|
surface: input.bridged ? 'webmcp' : isPublic ? 'assistant-public' : 'assistant-authenticated',
|
|
14
15
|
method: 'tools/call',
|
|
15
16
|
kind: 'usage',
|
|
16
|
-
toolName: input.toolName,
|
|
17
|
+
...(input.toolName === undefined ? {} : { toolName: input.toolName }),
|
|
17
18
|
outcome: input.outcome === 'ok' ? 'ok' : input.outcome === 'refused' ? 'tool_error' : 'mcp_error',
|
|
18
19
|
...(input.errorKind === undefined ? {} : { errorKind: input.errorKind }),
|
|
19
20
|
durationMs: Number.isFinite(input.durationMs) && input.durationMs > 0 ? input.durationMs : 0,
|
|
@@ -22,4 +23,25 @@ export function assistantAppToolCallRequestEvent(session, input) {
|
|
|
22
23
|
details: { eventKind: 'appToolCall', bridged: input.bridged },
|
|
23
24
|
};
|
|
24
25
|
}
|
|
26
|
+
/**
|
|
27
|
+
* Bind one apps-bridge tool call's fixed facts, leaving the caller a recorder it can run at any
|
|
28
|
+
* exit. The route reaches several of them -- a budget refusal before any resolution work, an
|
|
29
|
+
* unprojected tool, bad arguments, a raised confirmation, a result -- and assembling the event at
|
|
30
|
+
* each one is how one of them ends up forgotten, which is what happened to the budget refusal.
|
|
31
|
+
*/
|
|
32
|
+
export function assistantAppToolCallRecorder(input) {
|
|
33
|
+
const startedAt = input.clock();
|
|
34
|
+
return (outcome, errorKind) => {
|
|
35
|
+
captureAssistantUsage(input.capture, assistantAppToolCallRequestEvent(input.session, {
|
|
36
|
+
// Absent when the caller named no tool. That row still matters: a nameless call that
|
|
37
|
+
// exhausts a budget is the shape abusive traffic takes, and it is the row an operator
|
|
38
|
+
// watching a browser agent would miss if a missing name suppressed the event.
|
|
39
|
+
...(input.toolName === undefined ? {} : { toolName: input.toolName }),
|
|
40
|
+
bridged: input.bridged,
|
|
41
|
+
outcome,
|
|
42
|
+
...(errorKind === undefined ? {} : { errorKind }),
|
|
43
|
+
durationMs: input.clock() - startedAt,
|
|
44
|
+
}));
|
|
45
|
+
};
|
|
46
|
+
}
|
|
25
47
|
//# sourceMappingURL=assistant-app-tool-usage.js.map
|
|
@@ -8,16 +8,17 @@ export function createObservabilityStores(options) {
|
|
|
8
8
|
options.intentCaptureSettingsStore ?? new InMemoryIntentCaptureSettingsStore(),
|
|
9
9
|
options.intentEventStore ?? new InMemoryIntentEventStore(),
|
|
10
10
|
options.requestEventStore ?? new InMemoryRequestEventStore(),
|
|
11
|
+
new Set(options.intentCapturePreviewOrgs ?? []),
|
|
11
12
|
];
|
|
12
13
|
}
|
|
13
|
-
export async function resolveIntentMode(target, ref, settings) {
|
|
14
|
+
export async function resolveIntentMode(target, ref, previewOrgs, settings) {
|
|
14
15
|
if (target === undefined)
|
|
15
16
|
return undefined;
|
|
16
17
|
const resolved = ref ??
|
|
17
18
|
(target.org !== undefined && target.app !== undefined && target.environment !== undefined
|
|
18
19
|
? { org: target.org, app: target.app, env: target.environment }
|
|
19
20
|
: undefined);
|
|
20
|
-
if (resolved === undefined)
|
|
21
|
+
if (resolved === undefined || !previewOrgs.has(resolved.org))
|
|
21
22
|
return target;
|
|
22
23
|
try {
|
|
23
24
|
const setting = await settings.get(resolved);
|
|
@@ -27,8 +28,8 @@ export async function resolveIntentMode(target, ref, settings) {
|
|
|
27
28
|
return target;
|
|
28
29
|
}
|
|
29
30
|
}
|
|
30
|
-
export function createIntentTargetResolver(settings) {
|
|
31
|
-
return (target, ref) => resolveIntentMode(target, ref, settings);
|
|
31
|
+
export function createIntentTargetResolver(settings, previewOrgs) {
|
|
32
|
+
return (target, ref) => resolveIntentMode(target, ref, previewOrgs, settings);
|
|
32
33
|
}
|
|
33
34
|
export function createObservabilityDispatcher(deps) {
|
|
34
35
|
return (req, res, url) => dispatchAnalyticsReads(req, res, url, {
|
|
@@ -49,6 +50,7 @@ export function createObservabilityDispatcher(deps) {
|
|
|
49
50
|
requests: deps.requestEvents,
|
|
50
51
|
audit: deps.audit,
|
|
51
52
|
maxBody: deps.maxBody,
|
|
53
|
+
previewOrgs: deps.previewOrgs,
|
|
52
54
|
applySecurityHeaders,
|
|
53
55
|
enforceHttps,
|
|
54
56
|
sendJson,
|
|
@@ -1,6 +1,6 @@
|
|
|
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, selectAssistantModelTools, toolTouchesDelegatedAuth, withAssistantSessionExecutionAuthority, } from '@noodle-borg/assistant-gateway/portable';
|
|
3
|
+
import { assistantGuideModelContext, assistantModelToolOncePerSession, assistantModelToolRequiredWhenVisible, assistantOmittedToolResult, assistantViewAvailableData, dispatchAssistantTool, projectAssistantGuide, publicSurfaceOf, 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
6
|
import { evaluateToolAuthorization } from '@noodle-borg/protocol';
|
|
@@ -122,7 +122,7 @@ export async function runAgentTurn(target, session, message, context, deps, emit
|
|
|
122
122
|
stats.reasoningTokens += completion.usage?.reasoningTokens ?? 0;
|
|
123
123
|
stats.totalTokens += completion.usage?.totalTokens ?? 0;
|
|
124
124
|
if (remainingTokens !== undefined) {
|
|
125
|
-
remainingTokens =
|
|
125
|
+
remainingTokens = remainingTurnTokens(remainingTokens, completion.usage, requestTokenLimit);
|
|
126
126
|
}
|
|
127
127
|
const response = completion.choices[0]?.message;
|
|
128
128
|
if (!response)
|
|
@@ -3,7 +3,7 @@ import { assistantModelFailure, assistantModelSource, assistantModelTransport, }
|
|
|
3
3
|
import { ASSISTANT_SESSION_IDLE_MS, effectiveAssistantBrowserConfiguration, executeAssistantAppToolCall, isAssistantPageContext, isAssistantVerifiedClaims, parseAssistantContextPreferences, parseAssistantCustomerRouting, refuseBridgeToolCall, refusePublicTurn, resolveInvocationContextSnapshot, resumeTurnMessage, resumeUnavailableMessage, shouldAutoResume, surfaceBindingForOrigin, withAssistantSessionExecutionAuthority, } from '@noodle-borg/assistant-gateway/portable';
|
|
4
4
|
import { canonicalizeAuthorizationClaimValues } from '@noodle-borg/auth';
|
|
5
5
|
import { tenantMcpUrl } from '@noodle-borg/module';
|
|
6
|
-
import {
|
|
6
|
+
import { assistantAppToolCallRecorder, assistantRefusedTurnUsageRequestEvent, assistantSessionUsageRequestEvent, assistantTurnNumber, assistantTurnUsageRequestEvent, captureAssistantUsage, } from '@noodle-borg/observability';
|
|
7
7
|
import { mapExecutionError, mapResourceContents, mapResourcesList, mapToolOutput, mapToolsList, } from '@noodle-borg/protocol';
|
|
8
8
|
import { executeResource } from '@noodle-borg/runtime';
|
|
9
9
|
import { assistantMessageTurnRequestSchema, assistantResumeTurnRequestSchema, assistantSessionResponseSchema, } from '@noodle-borg/wire-contracts';
|
|
@@ -385,9 +385,21 @@ export async function handleAssistantAppRequest(req, res, deps) {
|
|
|
385
385
|
// Answered before any resolution work, so a refused browser agent costs nothing (ADR 0220). The
|
|
386
386
|
// marker is client-declared: it selects a budget, never authority, which the session below decides.
|
|
387
387
|
const bridged = method === 'tools/call' && body.value.bridge === 'webmcp';
|
|
388
|
+
// Bound before the budget gate below, because that refusal is the one an operator watching a
|
|
389
|
+
// browser agent most needs to see, and answering it before any resolution work must not mean
|
|
390
|
+
// answering it silently. `bridged` is attribution only: it labels the row, never authority.
|
|
391
|
+
const record = assistantAppToolCallRecorder({
|
|
392
|
+
capture: deps.captureRequestEvent,
|
|
393
|
+
session,
|
|
394
|
+
...(typeof params.name === 'string' ? { toolName: params.name } : { toolName: undefined }),
|
|
395
|
+
bridged,
|
|
396
|
+
clock: () => performance.now(),
|
|
397
|
+
});
|
|
388
398
|
const refusal = bridged ? await refuseBridgeToolCall(deps, session) : undefined;
|
|
389
|
-
if (refusal)
|
|
399
|
+
if (refusal) {
|
|
400
|
+
record('refused', refusal.code);
|
|
390
401
|
return sendJson(res, refusal.status, { error: refusal.message, code: refusal.code });
|
|
402
|
+
}
|
|
391
403
|
const knowledge = await resolveAssistantKnowledge(target.served);
|
|
392
404
|
if (method === 'tools/list') {
|
|
393
405
|
return sendJson(res, 200, mapToolsList(target.served.artifact, session.caller, {
|
|
@@ -423,25 +435,23 @@ export async function handleAssistantAppRequest(req, res, deps) {
|
|
|
423
435
|
return sendJson(res, 200, mapResourceContents(resource.uri, resource.mimeType, execution.output, resource._meta));
|
|
424
436
|
}
|
|
425
437
|
if (method === 'tools/call') {
|
|
426
|
-
if (typeof params.name !== 'string')
|
|
438
|
+
if (typeof params.name !== 'string') {
|
|
439
|
+
record('refused', 'tool_name_required');
|
|
427
440
|
return sendJson(res, 400, { error: 'tool name required' });
|
|
428
|
-
|
|
441
|
+
}
|
|
429
442
|
const toolName = params.name;
|
|
430
|
-
// Recorded on every exit below, including the refusals: a rejected browser-agent call is
|
|
431
|
-
// exactly the traffic an operator watching one needs to see (ADR 0220 decision 6).
|
|
432
|
-
const record = (outcome, errorKind) => captureAssistantUsage(deps.captureRequestEvent, assistantAppToolCallRequestEvent(session, {
|
|
433
|
-
toolName,
|
|
434
|
-
// Attribution only. The marker labels the row; the session decided authority above.
|
|
435
|
-
bridged,
|
|
436
|
-
outcome,
|
|
437
|
-
...(errorKind === undefined ? {} : { errorKind }),
|
|
438
|
-
durationMs: performance.now() - startedAt,
|
|
439
|
-
}));
|
|
440
443
|
const knowledgeComponent = findAssistantKnowledgeComponent(knowledge, toolName);
|
|
441
444
|
if (knowledge !== undefined && knowledgeComponent !== undefined) {
|
|
442
445
|
const content = await executeAssistantKnowledgeSearch(knowledge, knowledgeComponent, params.arguments ?? {});
|
|
443
|
-
|
|
444
|
-
|
|
446
|
+
// A knowledge search reports failure in its payload rather than by throwing, so reading the
|
|
447
|
+
// envelope is the only way not to file a refused or failed search as a successful one.
|
|
448
|
+
const payload = JSON.parse(content);
|
|
449
|
+
const failure = isRecord(payload) && typeof payload.error === 'string' ? payload.error : undefined;
|
|
450
|
+
if (failure === undefined)
|
|
451
|
+
record('ok');
|
|
452
|
+
else
|
|
453
|
+
record(failure === 'invalid_arguments' ? 'refused' : 'failed', failure);
|
|
454
|
+
return sendJson(res, 200, mapToolOutput(payload));
|
|
445
455
|
}
|
|
446
456
|
const call = await executeAssistantAppToolCall({
|
|
447
457
|
artifact: target.served.artifact,
|
|
@@ -14,6 +14,9 @@ export async function handleIntentCaptureSettings(req, res, ref, deps) {
|
|
|
14
14
|
const identity = await authorizeTenantControl(req, res, deps.gate, deps.controlPlane, ref.org);
|
|
15
15
|
if (identity === false)
|
|
16
16
|
return;
|
|
17
|
+
if (!deps.previewOrgs.has(ref.org)) {
|
|
18
|
+
return sendJson(res, 403, { ok: false, error: 'intent capture preview is unavailable' });
|
|
19
|
+
}
|
|
17
20
|
const setting = await deps.settings.get(ref);
|
|
18
21
|
return sendJson(res, 200, {
|
|
19
22
|
ok: true,
|
|
@@ -29,6 +32,9 @@ export async function handleIntentCaptureSettings(req, res, ref, deps) {
|
|
|
29
32
|
const identity = await authorizeOwner(req, res, ref, deps);
|
|
30
33
|
if (identity === undefined)
|
|
31
34
|
return;
|
|
35
|
+
if (!deps.previewOrgs.has(ref.org)) {
|
|
36
|
+
return sendJson(res, 403, { ok: false, error: 'intent capture preview is unavailable' });
|
|
37
|
+
}
|
|
32
38
|
const body = await readJsonBody(req, deps.maxBody);
|
|
33
39
|
if (!body.ok)
|
|
34
40
|
return sendJson(res, body.status, { error: body.error });
|
|
@@ -68,6 +74,9 @@ export async function handleIntentCaptureSettings(req, res, ref, deps) {
|
|
|
68
74
|
const identity = await authorizeOwner(req, res, ref, deps);
|
|
69
75
|
if (identity === undefined)
|
|
70
76
|
return;
|
|
77
|
+
if (!deps.previewOrgs.has(ref.org)) {
|
|
78
|
+
return sendJson(res, 403, { ok: false, error: 'intent capture preview is unavailable' });
|
|
79
|
+
}
|
|
71
80
|
await deps.settings.delete(ref);
|
|
72
81
|
const purged = await deps.intents.purge(ref);
|
|
73
82
|
await deps.audit.emit({
|
|
@@ -89,6 +98,9 @@ export async function handleIntentInsights(req, res, ref, url, deps) {
|
|
|
89
98
|
const identity = await authorizeTenantControl(req, res, deps.gate, deps.controlPlane, ref.org);
|
|
90
99
|
if (identity === false)
|
|
91
100
|
return;
|
|
101
|
+
if (!deps.previewOrgs.has(ref.org)) {
|
|
102
|
+
return sendJson(res, 403, { ok: false, error: 'intent capture preview is unavailable' });
|
|
103
|
+
}
|
|
92
104
|
const windowName = url.searchParams.get('window') ?? '7d';
|
|
93
105
|
const windowMs = WINDOWS_MS.get(windowName);
|
|
94
106
|
if (windowMs === undefined) {
|
|
@@ -57,7 +57,7 @@ export function createServiceHandler(registry, options = {}) {
|
|
|
57
57
|
let activeAudit;
|
|
58
58
|
const configStore = options.configStore ?? registry.configStore;
|
|
59
59
|
const alertRuleStore = options.alertRuleStore ?? new InMemoryAlertRuleStore();
|
|
60
|
-
const [intentSettings, intentEvents, requestEvents] = createObservabilityStores(options);
|
|
60
|
+
const [intentSettings, intentEvents, requestEvents, intentPreviewOrgs] = createObservabilityStores(options);
|
|
61
61
|
const assistantStore = options.assistantStore ?? new InMemoryAssistantStore();
|
|
62
62
|
const assistantAppearance = options.assistantAppearance ?? new InMemoryAssistantAppearanceSettingsStore();
|
|
63
63
|
const anonymousLimiter = new AnonymousConsumerLimiter();
|
|
@@ -76,7 +76,7 @@ export function createServiceHandler(registry, options = {}) {
|
|
|
76
76
|
activeAudit = audit;
|
|
77
77
|
const { publicTenantRouting } = mcp.mcpRoutingOptions(options, controlPlane);
|
|
78
78
|
const resolveEndpointOptions = (org) => mcp.endpointUrlOptionsForOrg(options, controlPlane, org);
|
|
79
|
-
const withIntentMode = createIntentTargetResolver(intentSettings);
|
|
79
|
+
const withIntentMode = createIntentTargetResolver(intentSettings, intentPreviewOrgs);
|
|
80
80
|
const router = createMcpRouter(async (id) => withIntentMode(await registry.getServing(id)), {
|
|
81
81
|
logger,
|
|
82
82
|
tls,
|
|
@@ -122,6 +122,7 @@ export function createServiceHandler(registry, options = {}) {
|
|
|
122
122
|
requestEvents,
|
|
123
123
|
intentEvents,
|
|
124
124
|
intentSettings,
|
|
125
|
+
previewOrgs: intentPreviewOrgs,
|
|
125
126
|
audit,
|
|
126
127
|
maxBody,
|
|
127
128
|
tls,
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"dependencies": {
|
|
40
40
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
41
41
|
"@noodle-borg/admission-limits": "0.0.0",
|
|
42
|
-
"@noodle-borg/agent-kit": "0.
|
|
42
|
+
"@noodle-borg/agent-kit": "0.91.0",
|
|
43
43
|
"@noodle-borg/app-package": "0.0.0",
|
|
44
44
|
"@noodle-borg/assistant-gateway": "0.0.0",
|
|
45
45
|
"@noodle-borg/auth": "0.0.0",
|
|
@@ -18,6 +18,7 @@ export * from './deployment-package.js';
|
|
|
18
18
|
export * from './distribution.js';
|
|
19
19
|
export * from './feedback.js';
|
|
20
20
|
export * from './knowledge.js';
|
|
21
|
+
export * from './managed-assistant-sponsorship.js';
|
|
21
22
|
export * from './platform-account-reset.js';
|
|
22
23
|
export * from './platform-auth.js';
|
|
23
24
|
/** Informational origin of a deployment; never an authorization or policy input. */
|
|
@@ -20,6 +20,7 @@ export * from './deployment-package.js';
|
|
|
20
20
|
export * from './distribution.js';
|
|
21
21
|
export * from './feedback.js';
|
|
22
22
|
export * from './knowledge.js';
|
|
23
|
+
export * from './managed-assistant-sponsorship.js';
|
|
23
24
|
export * from './platform-account-reset.js';
|
|
24
25
|
export * from './platform-auth.js';
|
|
25
26
|
// ─── Deploy ─────────────────────────────────────────────────────────────────────
|