@noodleseed/one 0.137.0 → 0.139.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.js +1 -1
- package/dist/cli.js.map +1 -1
- package/dist/commands/assistant-ops.d.ts.map +1 -1
- package/dist/commands/assistant-ops.js +30 -1
- package/dist/commands/assistant-ops.js.map +1 -1
- package/dist/commands/deploy-first-flow.d.ts +2 -0
- package/dist/commands/deploy-first-flow.d.ts.map +1 -1
- package/dist/commands/deploy-first-flow.js +46 -14
- package/dist/commands/deploy-first-flow.js.map +1 -1
- package/dist/commands/deploy-ops.d.ts.map +1 -1
- package/dist/commands/deploy-ops.js +27 -1
- package/dist/commands/deploy-ops.js.map +1 -1
- package/dist/commands/deploy-preflight.d.ts +1 -0
- package/dist/commands/deploy-preflight.d.ts.map +1 -1
- package/dist/commands/deploy-preflight.js +1 -0
- package/dist/commands/deploy-preflight.js.map +1 -1
- package/dist/commands/deploy-status-ops.d.ts +1 -0
- package/dist/commands/deploy-status-ops.d.ts.map +1 -1
- package/dist/commands/deploy-status-ops.js +59 -4
- package/dist/commands/deploy-status-ops.js.map +1 -1
- package/dist/commands/deployments-ops.d.ts.map +1 -1
- package/dist/commands/deployments-ops.js +2 -0
- package/dist/commands/deployments-ops.js.map +1 -1
- package/dist/commands/diagnostics-ops.d.ts +1 -0
- package/dist/commands/diagnostics-ops.d.ts.map +1 -1
- package/dist/commands/diagnostics-ops.js +3 -0
- package/dist/commands/diagnostics-ops.js.map +1 -1
- package/dist/commands/import.d.ts +4 -0
- package/dist/commands/import.d.ts.map +1 -0
- package/dist/commands/import.js +198 -0
- package/dist/commands/import.js.map +1 -0
- package/dist/commands/project-setup.d.ts +1 -1
- package/dist/commands/project-setup.d.ts.map +1 -1
- package/dist/commands/project-setup.js +2 -48
- package/dist/commands/project-setup.js.map +1 -1
- package/dist/commands/service-ops.d.ts.map +1 -1
- package/dist/commands/service-ops.js +44 -51
- package/dist/commands/service-ops.js.map +1 -1
- package/dist/deploy.d.ts +3 -0
- package/dist/deploy.d.ts.map +1 -1
- package/dist/deploy.js +4 -0
- package/dist/deploy.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/mcp-import.d.ts +22 -0
- package/dist/mcp-import.d.ts.map +1 -0
- package/dist/mcp-import.js +91 -0
- package/dist/mcp-import.js.map +1 -0
- package/dist/self-host-init.d.ts +23 -0
- package/dist/self-host-init.d.ts.map +1 -0
- package/dist/self-host-init.js +315 -0
- package/dist/self-host-init.js.map +1 -0
- package/node_modules/@noodle-borg/admission-limits/dist/envelope.js +1 -1
- package/node_modules/@noodle-borg/agent-kit/dist/generated/example-files.js +6 -6
- package/node_modules/@noodle-borg/agent-kit/dist/generated/surface.js +1 -0
- package/node_modules/@noodle-borg/agent-kit/dist/skill-authoring-refs.js +11 -2
- package/node_modules/@noodle-borg/agent-kit/dist/skill-connect-refs.js +31 -1
- package/node_modules/@noodle-borg/agent-kit/dist/skill-content.js +3 -2
- package/node_modules/@noodle-borg/agent-kit/dist/skill-embedded-assistant-ref.js +6 -7
- package/node_modules/@noodle-borg/agent-kit/package.json +1 -1
- package/node_modules/@noodle-borg/assistant-gateway/dist/assistant-guide.d.ts +1 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/assistant-guide.js +35 -1
- package/node_modules/@noodle-borg/assistant-gateway/dist/assistant-store.d.ts +6 -2
- package/node_modules/@noodle-borg/assistant-gateway/dist/assistant-store.js +5 -2
- package/node_modules/@noodle-borg/assistant-gateway/dist/model-request.js +37 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/model-runtime.js +4 -0
- package/node_modules/@noodle-borg/{service/dist/assistant-model-stream.js → assistant-gateway/dist/model-stream.js} +34 -2
- package/node_modules/@noodle-borg/assistant-gateway/dist/public-session.d.ts +3 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/public-session.js +2 -1
- package/node_modules/@noodle-borg/assistant-gateway/dist/public-turn.d.ts +4 -1
- package/node_modules/@noodle-borg/assistant-gateway/dist/public-turn.js +2 -1
- package/node_modules/@noodle-borg/assistant-gateway/dist/surface-budget.d.ts +6 -1
- package/node_modules/@noodle-borg/assistant-gateway/dist/surface-budget.js +11 -5
- package/node_modules/@noodle-borg/assistant-gateway/package.json +5 -1
- package/node_modules/@noodle-borg/authoring/dist/assistant.d.ts +10 -2
- package/node_modules/@noodle-borg/authoring/dist/assistant.js +4 -0
- package/node_modules/@noodle-borg/authoring/dist/connections.js +1 -0
- package/node_modules/@noodle-borg/authoring/dist/connectors.d.ts +25 -3
- package/node_modules/@noodle-borg/authoring/dist/connectors.js +82 -7
- package/node_modules/@noodle-borg/authoring/dist/index.d.ts +2 -2
- package/node_modules/@noodle-borg/authoring/dist/index.js +1 -1
- package/node_modules/@noodle-borg/authoring/dist/load-authored.js +1 -1
- package/node_modules/@noodle-borg/authoring/dist/model-tool-visibility.js +33 -0
- package/node_modules/@noodle-borg/authoring/dist/server.d.ts +5 -0
- package/node_modules/@noodle-borg/authoring/dist/server.js +7 -2
- package/node_modules/@noodle-borg/cli-catalog/dist/catalog-data-account.js +13 -1
- package/node_modules/@noodle-borg/cli-catalog/dist/catalog-data-assistant.js +17 -0
- package/node_modules/@noodle-borg/cli-catalog/dist/catalog-data-bootstrap-authoring.js +30 -1
- package/node_modules/@noodle-borg/cli-catalog/dist/catalog-data-hosted-deployment.js +14 -0
- package/node_modules/@noodle-borg/compiler/dist/artifact/types.d.ts +1 -7
- package/node_modules/@noodle-borg/compiler/dist/manifest/schema.d.ts +20 -8
- package/node_modules/@noodle-borg/compiler/dist/manifest/schema.js +12 -8
- package/node_modules/@noodle-borg/connector-defs/dist/call-graph.js +1 -1
- package/node_modules/@noodle-borg/connector-defs/dist/compile-mcp.js +95 -0
- package/node_modules/@noodle-borg/connector-defs/dist/compile.js +4 -0
- package/node_modules/@noodle-borg/connector-defs/dist/customer-routing.js +1 -1
- package/node_modules/@noodle-borg/connector-defs/dist/index.d.ts +1 -1
- package/node_modules/@noodle-borg/connector-defs/dist/schema.d.ts +642 -3
- package/node_modules/@noodle-borg/connector-defs/dist/schema.js +143 -1
- package/node_modules/@noodle-borg/connector-http/dist/index.d.ts +1 -0
- package/node_modules/@noodle-borg/connector-http/dist/index.js +1 -0
- package/node_modules/@noodle-borg/connector-http/dist/mcp/config.js +76 -0
- package/node_modules/@noodle-borg/connector-http/dist/mcp/egress.js +125 -0
- package/node_modules/@noodle-borg/connector-http/dist/mcp/failure.js +48 -0
- package/node_modules/@noodle-borg/connector-http/dist/mcp/index.d.ts +3 -0
- package/node_modules/@noodle-borg/connector-http/dist/mcp/index.js +2 -0
- package/node_modules/@noodle-borg/connector-http/dist/mcp/mcp-connector.d.ts +12 -0
- package/node_modules/@noodle-borg/connector-http/dist/mcp/mcp-connector.js +125 -0
- package/node_modules/@noodle-borg/connector-http/dist/mcp/result.js +55 -0
- package/node_modules/@noodle-borg/connector-http/dist/mcp/types.d.ts +52 -0
- package/node_modules/@noodle-borg/connector-http/dist/mcp/types.js +10 -0
- package/node_modules/@noodle-borg/connector-http/package.json +4 -1
- package/node_modules/@noodle-borg/control-plane/dist/portable.d.ts +2 -0
- package/node_modules/@noodle-borg/control-plane/dist/portable.js +1 -0
- package/node_modules/@noodle-borg/control-plane/dist/rollback-deployment.d.ts +61 -0
- package/node_modules/@noodle-borg/{service/dist/operations → control-plane/dist}/rollback-deployment.js +3 -2
- package/node_modules/@noodle-borg/control-plane/dist/rollback-result.d.ts +19 -0
- package/node_modules/@noodle-borg/control-plane/package.json +3 -1
- package/node_modules/@noodle-borg/developer-mcp/dist/contracts.js +4 -0
- package/node_modules/@noodle-borg/developer-mcp/dist/widgets/app-overview.js +1 -1
- package/node_modules/@noodle-borg/developer-mcp/dist/widgets/deployment-detail.js +1 -1
- package/node_modules/@noodle-borg/module/dist/contract.d.ts +1 -0
- package/node_modules/@noodle-borg/module/dist/deployment-automation.d.ts +1 -1
- package/node_modules/@noodle-borg/module/dist/endpoint-url.d.ts +11 -0
- package/node_modules/@noodle-borg/module/dist/endpoint-url.js +20 -0
- 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/observability/dist/assistant-usage.js +217 -0
- package/node_modules/@noodle-borg/observability/dist/index.js +2 -0
- package/node_modules/@noodle-borg/{service → observability}/dist/request-event-metrics.js +5 -2
- package/node_modules/@noodle-borg/observability/dist/request-events-postgres.js +6 -0
- package/node_modules/@noodle-borg/observability/dist/request-events.js +2 -0
- package/node_modules/@noodle-borg/openapi-import/dist/index.js +1 -0
- package/node_modules/@noodle-borg/openapi-import/dist/mcp/index.js +4 -0
- package/node_modules/@noodle-borg/openapi-import/dist/mcp/probe.js +165 -0
- package/node_modules/@noodle-borg/openapi-import/dist/mcp/render.js +99 -0
- package/node_modules/@noodle-borg/openapi-import/dist/mcp/snapshot.js +290 -0
- package/node_modules/@noodle-borg/openapi-import/package.json +6 -2
- package/node_modules/@noodle-borg/runtime/dist/connector/types.d.ts +2 -0
- package/node_modules/@noodle-borg/runtime/dist/operation-execution.js +1 -0
- package/node_modules/@noodle-borg/service/dist/assistant-bindings.js +3 -1
- package/node_modules/@noodle-borg/service/dist/developer-mcp/control-plane-adapter.js +3 -3
- package/node_modules/@noodle-borg/service/dist/developer-mcp/inspection.js +2 -1
- package/node_modules/@noodle-borg/service/dist/registry-access.js +73 -24
- package/node_modules/@noodle-borg/service/dist/registry-deploy-transaction.js +5 -2
- package/node_modules/@noodle-borg/service/dist/registry-helpers.d.ts +5 -9
- package/node_modules/@noodle-borg/service/dist/registry-helpers.js +4 -18
- package/node_modules/@noodle-borg/service/dist/registry-rollback.js +3 -1
- package/node_modules/@noodle-borg/service/dist/registry-status.js +4 -1
- package/node_modules/@noodle-borg/service/dist/registry-targets.js +4 -4
- package/node_modules/@noodle-borg/service/dist/registry-types.d.ts +11 -16
- package/node_modules/@noodle-borg/service/dist/registry.d.ts +8 -5
- package/node_modules/@noodle-borg/service/dist/registry.js +8 -5
- package/node_modules/@noodle-borg/service/dist/routes/access.js +19 -1
- package/node_modules/@noodle-borg/service/dist/routes/analytics-dispatch.js +17 -1
- package/node_modules/@noodle-borg/service/dist/routes/analytics.js +1 -1
- package/node_modules/@noodle-borg/service/dist/routes/assistant-agent.js +81 -64
- package/node_modules/@noodle-borg/service/dist/routes/assistant-doctor.js +1 -1
- package/node_modules/@noodle-borg/service/dist/routes/assistant-model-binding.js +28 -0
- package/node_modules/@noodle-borg/service/dist/routes/assistant-public-session.js +18 -0
- package/node_modules/@noodle-borg/service/dist/routes/assistant-public-turn.js +6 -0
- package/node_modules/@noodle-borg/service/dist/routes/assistant-usage.js +42 -0
- package/node_modules/@noodle-borg/service/dist/routes/assistant.js +20 -3
- package/node_modules/@noodle-borg/service/dist/routes/auth-doctor-live.js +1 -2
- package/node_modules/@noodle-borg/service/dist/routes/control-plane.js +49 -4
- package/node_modules/@noodle-borg/service/dist/routes/deploy-preflight.js +11 -1
- package/node_modules/@noodle-borg/service/dist/routes/endpoint-enrichment.js +2 -1
- package/node_modules/@noodle-borg/service/dist/routes/paths.js +4 -0
- package/node_modules/@noodle-borg/service/dist/routes/rollback.js +1 -1
- package/node_modules/@noodle-borg/service/dist/service.js +6 -0
- package/node_modules/@noodle-borg/service/dist/store/in-memory.d.ts +2 -5
- package/node_modules/@noodle-borg/service/dist/store/in-memory.js +9 -21
- package/node_modules/@noodle-borg/service/dist/store/json-file.d.ts +2 -5
- package/node_modules/@noodle-borg/service/dist/store/json-file.js +8 -20
- package/node_modules/@noodle-borg/service/dist/store/records.js +39 -0
- package/node_modules/@noodle-borg/service/dist/store.d.ts +21 -10
- package/node_modules/@noodle-borg/service/package.json +5 -1
- package/node_modules/@noodle-borg/wire-contracts/dist/control-plane.d.ts +120 -0
- package/node_modules/@noodle-borg/wire-contracts/dist/control-plane.js +123 -1
- package/node_modules/@noodle-borg/wire-contracts/dist/index.d.ts +4 -0
- package/node_modules/@noodle-borg/wire-contracts/dist/index.js +15 -1
- package/node_modules/@noodleseed/assistant/package.json +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { FetchLike, Tool } from '@modelcontextprotocol/client';
|
|
2
|
+
import type { CredentialProfile, OperationSignature } from '@noodle-borg/compiler';
|
|
3
|
+
import type { DnsLookup } from '../ssrf.js';
|
|
4
|
+
/** How a broker-minted credential is presented to an upstream MCP server. */
|
|
5
|
+
export type McpAuthScheme = CredentialProfile | {
|
|
6
|
+
readonly kind: 'cookie';
|
|
7
|
+
};
|
|
8
|
+
/** One curated Noodle operation mapped to one frozen upstream MCP tool. */
|
|
9
|
+
export interface McpOperation {
|
|
10
|
+
readonly upstreamTool: string;
|
|
11
|
+
readonly signature: OperationSignature;
|
|
12
|
+
/** Frozen upstream output schema; omitted when the imported tool had no output schema. */
|
|
13
|
+
readonly upstreamOutputSchema?: OperationSignature['output'];
|
|
14
|
+
readonly auth?: McpAuthScheme;
|
|
15
|
+
/** Inclusive decoded-result limit. Defaults to the connector-wide limit. */
|
|
16
|
+
readonly maxResponseBytes?: number;
|
|
17
|
+
readonly fake?: McpOperationFake;
|
|
18
|
+
}
|
|
19
|
+
export type McpOperationFake = {
|
|
20
|
+
readonly structuredContent: Readonly<Record<string, unknown>>;
|
|
21
|
+
} | {
|
|
22
|
+
readonly text: string;
|
|
23
|
+
};
|
|
24
|
+
/** Runtime configuration for one remote Streamable HTTP MCP server. */
|
|
25
|
+
export interface McpConnectorConfig {
|
|
26
|
+
readonly id: string;
|
|
27
|
+
readonly version: string;
|
|
28
|
+
/** Literal endpoint or one exact `${env.NAME}` managed-variable reference. */
|
|
29
|
+
readonly endpoint: string;
|
|
30
|
+
/** Exact egress origins. Required when `endpoint` is managed. */
|
|
31
|
+
readonly allowedOrigins?: readonly string[];
|
|
32
|
+
readonly operations: Readonly<Record<string, McpOperation>>;
|
|
33
|
+
readonly auth?: McpAuthScheme;
|
|
34
|
+
/** Total connection + call timeout. Default 10 seconds. */
|
|
35
|
+
readonly timeoutMs?: number;
|
|
36
|
+
/** Inclusive decoded MCP response limit. Default 1 MiB, maximum 6 MiB. */
|
|
37
|
+
readonly maxResponseBytes?: number;
|
|
38
|
+
/** Protocol-era policy. `auto` safely negotiates modern or legacy and is the default. */
|
|
39
|
+
readonly protocol?: 'auto' | 'legacy' | 'modern';
|
|
40
|
+
/** Compiler-owned deterministic mode; bypasses transport and credentials. */
|
|
41
|
+
readonly fakeMode?: boolean;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Hermetic-test seams. Production compilation never accepts or emits these values; outbound traffic uses
|
|
45
|
+
* the guarded transport and system DNS resolver.
|
|
46
|
+
*/
|
|
47
|
+
export interface McpConnectorDependencies {
|
|
48
|
+
readonly fetch?: FetchLike;
|
|
49
|
+
readonly lookup?: DnsLookup;
|
|
50
|
+
}
|
|
51
|
+
export declare function toolDefinition(operation: McpOperation): Tool;
|
|
52
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export function toolDefinition(operation) {
|
|
2
|
+
return {
|
|
3
|
+
name: operation.upstreamTool,
|
|
4
|
+
inputSchema: operation.signature.input,
|
|
5
|
+
...(operation.upstreamOutputSchema === undefined
|
|
6
|
+
? {}
|
|
7
|
+
: { outputSchema: operation.upstreamOutputSchema }),
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -22,12 +22,15 @@
|
|
|
22
22
|
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
+
"@modelcontextprotocol/client": "2.0.0",
|
|
25
26
|
"@noodle-borg/compiler": "0.0.0",
|
|
26
27
|
"@noodle-borg/runtime": "0.0.0",
|
|
27
28
|
"ipaddr.js": "^2.4.0",
|
|
28
29
|
"undici": "^8.7.0"
|
|
29
30
|
},
|
|
30
31
|
"devDependencies": {
|
|
31
|
-
"@
|
|
32
|
+
"@modelcontextprotocol/server": "2.0.0",
|
|
33
|
+
"@types/node": "^24.13.1",
|
|
34
|
+
"zod": "^4.4.3"
|
|
32
35
|
}
|
|
33
36
|
}
|
|
@@ -4,5 +4,7 @@ export { InMemoryControlPlaneStore } from './in-memory-control-plane-store.js';
|
|
|
4
4
|
export { bindInMemoryOrganizationStore, type InMemoryOrganizationOperations, InMemoryOrganizationStore, } from './in-memory-organization-store.js';
|
|
5
5
|
export { InMemoryMcpSubdomainClaimStore, McpSubdomainCooldownError, McpSubdomainIdempotencyConflictError, McpSubdomainOwnerRequiredError, type McpSubdomainRouteRef, McpSubdomainUnavailableError, mcpSubdomainEndpointOptions, type ResolvedMcpTenantRef, resolveMcpSubdomainTenant, } from './mcp-subdomain-claims.js';
|
|
6
6
|
export { domainFromEmail, domainKey, memberKey, normalizeSignupAllowlistValue, personalOrgSlug, personalOrgSlugSuffix, signupKey, } from './organization-helpers.js';
|
|
7
|
+
export { type RollbackControlPlane, type RollbackOperationDependencies, type RollbackOperationInput, type RollbackOperationResult, type RollbackRegistry, rollbackDeploymentOperation, } from './rollback-deployment.js';
|
|
8
|
+
export type { RollbackResult } from './rollback-result.js';
|
|
7
9
|
export { DOMAIN_PATTERN, isSystemOwnedOrgSlug, OPENAI_APPS_CHALLENGE_MAX_LENGTH, SLUG_PATTERN, validateDomain, validateMcpSubdomain, validateOpenAIAppsChallenge, validateOrgMembershipDomain, validateOrgRole, validateSignupAllowlistKind, validateSlug, validateUserOwnedOrgSlug, } from './validation.js';
|
|
8
10
|
//# sourceMappingURL=portable.d.ts.map
|
|
@@ -3,5 +3,6 @@ export { InMemoryControlPlaneStore } from './in-memory-control-plane-store.js';
|
|
|
3
3
|
export { bindInMemoryOrganizationStore, InMemoryOrganizationStore, } from './in-memory-organization-store.js';
|
|
4
4
|
export { InMemoryMcpSubdomainClaimStore, McpSubdomainCooldownError, McpSubdomainIdempotencyConflictError, McpSubdomainOwnerRequiredError, McpSubdomainUnavailableError, mcpSubdomainEndpointOptions, resolveMcpSubdomainTenant, } from './mcp-subdomain-claims.js';
|
|
5
5
|
export { domainFromEmail, domainKey, memberKey, normalizeSignupAllowlistValue, personalOrgSlug, personalOrgSlugSuffix, signupKey, } from './organization-helpers.js';
|
|
6
|
+
export { rollbackDeploymentOperation, } from './rollback-deployment.js';
|
|
6
7
|
export { DOMAIN_PATTERN, isSystemOwnedOrgSlug, OPENAI_APPS_CHALLENGE_MAX_LENGTH, SLUG_PATTERN, validateDomain, validateMcpSubdomain, validateOpenAIAppsChallenge, validateOrgMembershipDomain, validateOrgRole, validateSignupAllowlistKind, validateSlug, validateUserOwnedOrgSlug, } from './validation.js';
|
|
7
8
|
//# sourceMappingURL=portable.js.map
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { type AuditSink, type DeploymentOperationTarget, type EndpointUrlOptions } from '@noodle-borg/module';
|
|
2
|
+
import type { RollbackResult } from './rollback-result.js';
|
|
3
|
+
export interface RollbackRegistry {
|
|
4
|
+
getAppArchivedAt(org: string, app: string): Promise<string | undefined>;
|
|
5
|
+
rollback(target: DeploymentOperationTarget, deploymentId: string): Promise<RollbackResult>;
|
|
6
|
+
}
|
|
7
|
+
export interface RollbackControlPlane {
|
|
8
|
+
getOrgMember(input: {
|
|
9
|
+
readonly org: string;
|
|
10
|
+
readonly subject: string;
|
|
11
|
+
}): Promise<{
|
|
12
|
+
readonly role: string;
|
|
13
|
+
} | undefined>;
|
|
14
|
+
}
|
|
15
|
+
export interface RollbackOperationDependencies {
|
|
16
|
+
readonly registry: RollbackRegistry;
|
|
17
|
+
readonly controlPlane: RollbackControlPlane;
|
|
18
|
+
readonly audit: AuditSink;
|
|
19
|
+
}
|
|
20
|
+
export interface RollbackOperationInput {
|
|
21
|
+
readonly actor: {
|
|
22
|
+
readonly subject: string;
|
|
23
|
+
readonly email?: string;
|
|
24
|
+
readonly superAdmin: boolean;
|
|
25
|
+
};
|
|
26
|
+
readonly target: DeploymentOperationTarget;
|
|
27
|
+
readonly deploymentId: string;
|
|
28
|
+
readonly reason?: string;
|
|
29
|
+
readonly publicBaseUrl: string;
|
|
30
|
+
readonly endpointOptions?: EndpointUrlOptions;
|
|
31
|
+
}
|
|
32
|
+
export type RollbackOperationResult = {
|
|
33
|
+
readonly ok: true;
|
|
34
|
+
readonly view: {
|
|
35
|
+
readonly target: DeploymentOperationTarget;
|
|
36
|
+
readonly rollback: {
|
|
37
|
+
readonly deploymentId: string;
|
|
38
|
+
readonly serverVersion?: string;
|
|
39
|
+
readonly previousDeploymentId?: string;
|
|
40
|
+
readonly alreadyActive: boolean;
|
|
41
|
+
readonly endpointUrl: string;
|
|
42
|
+
readonly accessMode: string;
|
|
43
|
+
readonly ownerSubject?: string;
|
|
44
|
+
readonly previousAccessMode?: string;
|
|
45
|
+
readonly serverName: string;
|
|
46
|
+
readonly createdAt: string;
|
|
47
|
+
};
|
|
48
|
+
};
|
|
49
|
+
} | {
|
|
50
|
+
readonly ok: false;
|
|
51
|
+
readonly status: 403 | 404 | 409 | 503;
|
|
52
|
+
readonly code: 'forbidden' | 'app_archived' | 'deployment_not_found' | 'deployment_incompatible' | 'deployment_locked' | 'production_app_limit_exceeded' | 'billing_enforcement_unavailable';
|
|
53
|
+
readonly message: string;
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* Activate a previous deployment through the service's canonical governance boundary.
|
|
57
|
+
* HTTP routes, agent tools, and MCP App widgets must all call this operation rather than
|
|
58
|
+
* duplicating owner checks, commercial admission, endpoint construction, or audit behavior.
|
|
59
|
+
*/
|
|
60
|
+
export declare function rollbackDeploymentOperation(dependencies: RollbackOperationDependencies, input: RollbackOperationInput): Promise<RollbackOperationResult>;
|
|
61
|
+
//# sourceMappingURL=rollback-deployment.d.ts.map
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { DeploymentActivationError } from '@noodle-borg/module';
|
|
2
|
-
import { tenantMcpUrl } from '../registry-helpers.js';
|
|
1
|
+
import { DeploymentActivationError, tenantMcpUrl, } from '@noodle-borg/module';
|
|
3
2
|
/**
|
|
4
3
|
* Activate a previous deployment through the service's canonical governance boundary.
|
|
5
4
|
* HTTP routes, agent tools, and MCP App widgets must all call this operation rather than
|
|
@@ -81,6 +80,7 @@ export async function rollbackDeploymentOperation(dependencies, input) {
|
|
|
81
80
|
: {}),
|
|
82
81
|
alreadyActive: result.alreadyActive,
|
|
83
82
|
accessMode: result.accessMode,
|
|
83
|
+
...(result.ownerSubject !== undefined ? { ownerSubject: result.ownerSubject } : {}),
|
|
84
84
|
...(result.previousAccessMode !== undefined
|
|
85
85
|
? { previousAccessMode: result.previousAccessMode }
|
|
86
86
|
: {}),
|
|
@@ -101,6 +101,7 @@ export async function rollbackDeploymentOperation(dependencies, input) {
|
|
|
101
101
|
alreadyActive: result.alreadyActive,
|
|
102
102
|
endpointUrl,
|
|
103
103
|
accessMode: result.accessMode,
|
|
104
|
+
...(result.ownerSubject !== undefined ? { ownerSubject: result.ownerSubject } : {}),
|
|
104
105
|
...(result.previousAccessMode !== undefined
|
|
105
106
|
? { previousAccessMode: result.previousAccessMode }
|
|
106
107
|
: {}),
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { AccessMode } from '@noodle-borg/module';
|
|
2
|
+
export type RollbackResult = {
|
|
3
|
+
readonly ok: true;
|
|
4
|
+
readonly deploymentId: string;
|
|
5
|
+
readonly serverVersion?: string;
|
|
6
|
+
readonly previousDeploymentId?: string;
|
|
7
|
+
readonly alreadyActive: boolean;
|
|
8
|
+
readonly accessMode: AccessMode;
|
|
9
|
+
readonly ownerSubject?: string;
|
|
10
|
+
readonly previousAccessMode?: AccessMode;
|
|
11
|
+
readonly serverName: string;
|
|
12
|
+
readonly createdAt: string;
|
|
13
|
+
} | {
|
|
14
|
+
readonly ok: false;
|
|
15
|
+
readonly status: 404 | 409;
|
|
16
|
+
readonly code?: 'deployment_locked';
|
|
17
|
+
readonly error: string;
|
|
18
|
+
};
|
|
19
|
+
//# sourceMappingURL=rollback-result.d.ts.map
|
|
@@ -23,6 +23,7 @@ export const tenantSlugSchema = z
|
|
|
23
23
|
export const appSlugSchema = tenantSlugSchema;
|
|
24
24
|
export const environmentNameSchema = tenantSlugSchema;
|
|
25
25
|
export const boundedIdSchema = z.string().min(1).max(200);
|
|
26
|
+
const ownerSubjectSchema = z.string().min(1).max(512);
|
|
26
27
|
export const developerMcpContextSchema = z.strictObject({
|
|
27
28
|
subject: z.string().min(1).max(255),
|
|
28
29
|
clientId: z.string().min(1).max(255),
|
|
@@ -122,6 +123,7 @@ export const deploymentSummarySchema = z.strictObject({
|
|
|
122
123
|
serverName: z.string().min(1).max(200),
|
|
123
124
|
createdAt: isoTimestampSchema,
|
|
124
125
|
accessMode: z.string().min(1).max(64).optional(),
|
|
126
|
+
ownerSubject: ownerSubjectSchema.optional(),
|
|
125
127
|
endpointUrl: z.url().optional(),
|
|
126
128
|
});
|
|
127
129
|
export const appSummaryViewSchema = z.strictObject({
|
|
@@ -163,6 +165,7 @@ export const deploymentInspectionViewSchema = z.strictObject({
|
|
|
163
165
|
createdAt: isoTimestampSchema,
|
|
164
166
|
createdByEmail: z.email().optional(),
|
|
165
167
|
accessMode: z.string().min(1).max(64),
|
|
168
|
+
ownerSubject: ownerSubjectSchema.optional(),
|
|
166
169
|
}),
|
|
167
170
|
health: z.strictObject({
|
|
168
171
|
state: z.string().min(1).max(100),
|
|
@@ -203,6 +206,7 @@ export const rollbackViewSchema = z.strictObject({
|
|
|
203
206
|
alreadyActive: z.boolean(),
|
|
204
207
|
endpointUrl: z.url(),
|
|
205
208
|
accessMode: z.string().min(1).max(64),
|
|
209
|
+
ownerSubject: ownerSubjectSchema.optional(),
|
|
206
210
|
previousAccessMode: z.string().min(1).max(64).optional(),
|
|
207
211
|
serverName: z.string().min(1).max(200),
|
|
208
212
|
createdAt: isoTimestampSchema,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { ActionStatus, BoundValue, EmptyState, Panel, WidgetHeader } from './shared.js';
|
|
3
3
|
export function AppOverviewWidget() {
|
|
4
|
-
return (_jsxs("main", { children: [_jsx(WidgetHeader, { eyebrow: "Noodle Cloud", title: "App overview", description: "Deployment state for the organization and environments selected at connection time.", statusPath: "data.active" }), _jsx(Panel, { title: "Selected app", description: "This view is limited to the active developer grant.", children: _jsxs("dl", { className: "facts", children: [_jsx(BoundValue, { label: "App", path: "data.app" }), _jsx(BoundValue, { label: "Environment", path: "data.selectedEnvironment" }), _jsx(BoundValue, { label: "Active", path: "data.active" }), _jsx(BoundValue, { label: "Last activity", path: "data.lastActivityAt" })] }) }), _jsx(Panel, { title: "Granted environments", children: _jsxs("div", { className: "tag-list", "data-collection": "data.environments", "data-collection-item": "environment", children: [_jsx("template", { "data-collection-template": "", children: _jsx("span", { className: "tag", "data-bind": "environment", children: "Loading" }) }), _jsx(EmptyState, { children: "No environment is available in this result." })] }) }), _jsxs(Panel, { title: "Active deployment", children: [_jsxs("dl", { className: "facts", children: [_jsx(BoundValue, { label: "Deployment", path: "data.latest.deploymentId" }), _jsx(BoundValue, { label: "Created", path: "data.latest.createdAt" }), _jsx(BoundValue, { label: "Access", path: "data.latest.accessMode" }), _jsx(BoundValue, { label: "Endpoint", path: "data.latest.endpointUrl" })] }), _jsx("p", { className: "notice", "data-bind-if": "data.active", children: "An active deployment is serving this environment." })] }), _jsx(ActionStatus, {})] }));
|
|
4
|
+
return (_jsxs("main", { children: [_jsx(WidgetHeader, { eyebrow: "Noodle Cloud", title: "App overview", description: "Deployment state for the organization and environments selected at connection time.", statusPath: "data.active" }), _jsx(Panel, { title: "Selected app", description: "This view is limited to the active developer grant.", children: _jsxs("dl", { className: "facts", children: [_jsx(BoundValue, { label: "App", path: "data.app" }), _jsx(BoundValue, { label: "Environment", path: "data.selectedEnvironment" }), _jsx(BoundValue, { label: "Active", path: "data.active" }), _jsx(BoundValue, { label: "Last activity", path: "data.lastActivityAt" })] }) }), _jsx(Panel, { title: "Granted environments", children: _jsxs("div", { className: "tag-list", "data-collection": "data.environments", "data-collection-item": "environment", children: [_jsx("template", { "data-collection-template": "", children: _jsx("span", { className: "tag", "data-bind": "environment", children: "Loading" }) }), _jsx(EmptyState, { children: "No environment is available in this result." })] }) }), _jsxs(Panel, { title: "Active deployment", children: [_jsxs("dl", { className: "facts", children: [_jsx(BoundValue, { label: "Deployment", path: "data.latest.deploymentId" }), _jsx(BoundValue, { label: "Created", path: "data.latest.createdAt" }), _jsx(BoundValue, { label: "Access", path: "data.latest.accessMode" }), _jsx(BoundValue, { label: "Owner", path: "data.latest.ownerSubject" }), _jsx(BoundValue, { label: "Endpoint", path: "data.latest.endpointUrl" })] }), _jsx("p", { className: "notice", "data-bind-if": "data.active", children: "An active deployment is serving this environment." })] }), _jsx(ActionStatus, {})] }));
|
|
5
5
|
}
|
|
6
6
|
//# sourceMappingURL=app-overview.js.map
|
|
@@ -2,6 +2,6 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { ActionStatus, BoundValue, EmptyState, Panel, WidgetHeader } from './shared.js';
|
|
3
3
|
const ROLLBACK_ARGUMENTS = '{"app":"{{data.target.app}}","env":"{{data.target.env}}","deploymentId":"{{data.rollbackCandidate.deploymentId}}"}';
|
|
4
4
|
export function DeploymentDetailWidget() {
|
|
5
|
-
return (_jsxs("main", { children: [_jsx(WidgetHeader, { eyebrow: "Noodle Cloud", title: "Deployment detail", description: "Runtime health, surface compatibility, and governed rollback state.", statusPath: "data.health.state" }), _jsx(Panel, { title: "Deployment identity", children: _jsxs("dl", { className: "facts", children: [_jsx(BoundValue, { label: "App", path: "data.target.app" }), _jsx(BoundValue, { label: "Environment", path: "data.target.env" }), _jsx(BoundValue, { label: "Deployment", path: "data.deployment.deploymentId" }), _jsx(BoundValue, { label: "Created", path: "data.deployment.createdAt" }), _jsx(BoundValue, { label: "Access", path: "data.deployment.accessMode" }), _jsx(BoundValue, { label: "Endpoint", path: "data.deployment.endpointUrl" })] }) }), _jsxs(Panel, { title: "Runtime health", children: [_jsxs("dl", { className: "facts", children: [_jsx(BoundValue, { label: "State", path: "data.health.state" }), _jsx(BoundValue, { label: "MCP Apps", path: "data.surface.compatibility.mcpApps" }), _jsx(BoundValue, { label: "ChatGPT", path: "data.surface.compatibility.chatgpt" }), _jsx(BoundValue, { label: "Claude", path: "data.surface.compatibility.claude" })] }), _jsxs("div", { className: "collection", "data-collection": "data.health.missingSecrets", "data-collection-item": "missing", children: [_jsx("template", { "data-collection-template": "", children: _jsx("p", { className: "collection-card", "data-bind": "missing", children: "Loading" }) }), _jsx(EmptyState, { children: "No missing managed configuration was reported." })] })] }), _jsxs(Panel, { title: "Rollback", description: "Noodle Cloud rechecks ownership, selected environment, compatibility, and admission before activation.", children: [_jsxs("div", { className: "action-row", children: [_jsxs("dl", { className: "facts", children: [_jsx(BoundValue, { label: "Candidate", path: "data.rollbackCandidate.deploymentId" }), _jsx(BoundValue, { label: "Created", path: "data.rollbackCandidate.createdAt" })] }), _jsx("button", { type: "button", className: "primary-action danger-action", "data-action": "call", "data-action-tool": "rollback_deployment", "data-action-args": ROLLBACK_ARGUMENTS, "data-bind-if": "data.rollbackCandidate.deploymentId", hidden: true, children: "Roll back to candidate" })] }), _jsx("p", { className: "notice", children: "If no candidate is shown, use the headless deployment workflow to inspect an explicit deployment ID." })] }), _jsx(Panel, { title: "Rollback outcome", children: _jsxs("dl", { className: "facts", "data-bind-if": "data.rollback.deploymentId", children: [_jsx(BoundValue, { label: "Activated deployment", path: "data.rollback.deploymentId" }), _jsx(BoundValue, { label: "Previously active", path: "data.rollback.previousDeploymentId" }), _jsx(BoundValue, { label: "Already active", path: "data.rollback.alreadyActive" }), _jsx(BoundValue, { label: "Endpoint", path: "data.rollback.endpointUrl" })] }) }), _jsx(ActionStatus, {})] }));
|
|
5
|
+
return (_jsxs("main", { children: [_jsx(WidgetHeader, { eyebrow: "Noodle Cloud", title: "Deployment detail", description: "Runtime health, surface compatibility, and governed rollback state.", statusPath: "data.health.state" }), _jsx(Panel, { title: "Deployment identity", children: _jsxs("dl", { className: "facts", children: [_jsx(BoundValue, { label: "App", path: "data.target.app" }), _jsx(BoundValue, { label: "Environment", path: "data.target.env" }), _jsx(BoundValue, { label: "Deployment", path: "data.deployment.deploymentId" }), _jsx(BoundValue, { label: "Created", path: "data.deployment.createdAt" }), _jsx(BoundValue, { label: "Access", path: "data.deployment.accessMode" }), _jsx(BoundValue, { label: "Owner", path: "data.deployment.ownerSubject" }), _jsx(BoundValue, { label: "Endpoint", path: "data.deployment.endpointUrl" })] }) }), _jsxs(Panel, { title: "Runtime health", children: [_jsxs("dl", { className: "facts", children: [_jsx(BoundValue, { label: "State", path: "data.health.state" }), _jsx(BoundValue, { label: "MCP Apps", path: "data.surface.compatibility.mcpApps" }), _jsx(BoundValue, { label: "ChatGPT", path: "data.surface.compatibility.chatgpt" }), _jsx(BoundValue, { label: "Claude", path: "data.surface.compatibility.claude" })] }), _jsxs("div", { className: "collection", "data-collection": "data.health.missingSecrets", "data-collection-item": "missing", children: [_jsx("template", { "data-collection-template": "", children: _jsx("p", { className: "collection-card", "data-bind": "missing", children: "Loading" }) }), _jsx(EmptyState, { children: "No missing managed configuration was reported." })] })] }), _jsxs(Panel, { title: "Rollback", description: "Noodle Cloud rechecks ownership, selected environment, compatibility, and admission before activation.", children: [_jsxs("div", { className: "action-row", children: [_jsxs("dl", { className: "facts", children: [_jsx(BoundValue, { label: "Candidate", path: "data.rollbackCandidate.deploymentId" }), _jsx(BoundValue, { label: "Created", path: "data.rollbackCandidate.createdAt" })] }), _jsx("button", { type: "button", className: "primary-action danger-action", "data-action": "call", "data-action-tool": "rollback_deployment", "data-action-args": ROLLBACK_ARGUMENTS, "data-bind-if": "data.rollbackCandidate.deploymentId", hidden: true, children: "Roll back to candidate" })] }), _jsx("p", { className: "notice", children: "If no candidate is shown, use the headless deployment workflow to inspect an explicit deployment ID." })] }), _jsx(Panel, { title: "Rollback outcome", children: _jsxs("dl", { className: "facts", "data-bind-if": "data.rollback.deploymentId", children: [_jsx(BoundValue, { label: "Activated deployment", path: "data.rollback.deploymentId" }), _jsx(BoundValue, { label: "Previously active", path: "data.rollback.previousDeploymentId" }), _jsx(BoundValue, { label: "Already active", path: "data.rollback.alreadyActive" }), _jsx(BoundValue, { label: "Endpoint", path: "data.rollback.endpointUrl" }), _jsx(BoundValue, { label: "Owner", path: "data.rollback.ownerSubject" })] }) }), _jsx(ActionStatus, {})] }));
|
|
6
6
|
}
|
|
7
7
|
//# sourceMappingURL=deployment-detail.js.map
|
|
@@ -16,7 +16,6 @@ export type DeploymentAutomationInput = {
|
|
|
16
16
|
readonly previousAccessMode?: AccessMode;
|
|
17
17
|
readonly declaredAccessMode?: string;
|
|
18
18
|
readonly previousOwnerSubject?: string;
|
|
19
|
-
readonly previousOwnerEmail?: string;
|
|
20
19
|
};
|
|
21
20
|
export type DeploymentAutomationAuthorization = {
|
|
22
21
|
readonly kind: 'not-automation';
|
|
@@ -29,6 +28,7 @@ export type DeploymentAutomationAuthorization = {
|
|
|
29
28
|
readonly kind: 'authorized';
|
|
30
29
|
readonly automationId: string;
|
|
31
30
|
readonly actor: ControlPlaneIdentity;
|
|
31
|
+
readonly ownerSubject?: string;
|
|
32
32
|
readonly accessMode?: AccessMode;
|
|
33
33
|
readonly accessModeSource?: 'previous' | 'declared';
|
|
34
34
|
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export interface DeploymentOperationTarget {
|
|
2
|
+
readonly org: string;
|
|
3
|
+
readonly app: string;
|
|
4
|
+
readonly env: string;
|
|
5
|
+
}
|
|
6
|
+
export interface EndpointUrlOptions {
|
|
7
|
+
readonly publicBaseDomain?: string;
|
|
8
|
+
readonly mcpSubdomain?: string;
|
|
9
|
+
}
|
|
10
|
+
export declare function tenantMcpUrl(base: string, tenant: DeploymentOperationTarget, serverVersion?: string, options?: EndpointUrlOptions): string;
|
|
11
|
+
//# sourceMappingURL=endpoint-url.d.ts.map
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { formatPublicMcpUrl, serverVersionPathSegment } from './contract.js';
|
|
2
|
+
export function tenantMcpUrl(base, tenant, serverVersion, options = {}) {
|
|
3
|
+
if (options.publicBaseDomain !== undefined) {
|
|
4
|
+
if (options.mcpSubdomain === undefined) {
|
|
5
|
+
throw new Error('MCP subdomain is required to format a public endpoint URL');
|
|
6
|
+
}
|
|
7
|
+
return formatPublicMcpUrl(options.publicBaseDomain, {
|
|
8
|
+
mcpSubdomain: options.mcpSubdomain,
|
|
9
|
+
app: tenant.app,
|
|
10
|
+
env: tenant.env,
|
|
11
|
+
...(serverVersion !== undefined ? { serverVersion } : {}),
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
const versionPath = serverVersion !== undefined ? `/${serverVersionPathSegment(serverVersion)}` : '';
|
|
15
|
+
const origin = new URL(base).origin;
|
|
16
|
+
return tenant.env === 'prod'
|
|
17
|
+
? `${origin}/o/${tenant.org}/${tenant.app}${versionPath}/mcp`
|
|
18
|
+
: `${origin}/o/${tenant.org}/${tenant.app}/${tenant.env}${versionPath}/mcp`;
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=endpoint-url.js.map
|
|
@@ -2,6 +2,7 @@ export * from './asset-store.js';
|
|
|
2
2
|
export * from './contract.js';
|
|
3
3
|
export * from './deployment-activation.js';
|
|
4
4
|
export * from './deployment-automation.js';
|
|
5
|
+
export * from './endpoint-url.js';
|
|
5
6
|
export * from './intent.js';
|
|
6
7
|
export * from './organization-provisioning.js';
|
|
7
8
|
export * from './platform-human-identity.js';
|
|
@@ -2,6 +2,7 @@ export * from './asset-store.js';
|
|
|
2
2
|
export * from './contract.js';
|
|
3
3
|
export * from './deployment-activation.js';
|
|
4
4
|
export * from './deployment-automation.js';
|
|
5
|
+
export * from './endpoint-url.js';
|
|
5
6
|
export * from './intent.js';
|
|
6
7
|
export * from './organization-provisioning.js';
|
|
7
8
|
export * from './platform-human-identity.js';
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
export const ASSISTANT_USAGE_METHOD = 'assistant';
|
|
3
|
+
const EMPTY_TURN_COUNTERS = {
|
|
4
|
+
modelRequests: 0,
|
|
5
|
+
toolCalls: 0,
|
|
6
|
+
interactionCount: 0,
|
|
7
|
+
promptTokens: 0,
|
|
8
|
+
completionTokens: 0,
|
|
9
|
+
totalTokens: 0,
|
|
10
|
+
};
|
|
11
|
+
export function assistantSessionUsageRequestEvent(session, durationMs) {
|
|
12
|
+
return assistantUsageRequestEvent({
|
|
13
|
+
...session.tenant,
|
|
14
|
+
deploymentId: session.deploymentId,
|
|
15
|
+
eventKind: 'session',
|
|
16
|
+
sessionId: session.id,
|
|
17
|
+
surface: assistantSurface(session),
|
|
18
|
+
...(session.modelSource === undefined ? {} : { modelSource: session.modelSource }),
|
|
19
|
+
durationMs,
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
export function assistantTurnUsageRequestEvent(session, input) {
|
|
23
|
+
return assistantUsageRequestEvent({
|
|
24
|
+
...session.tenant,
|
|
25
|
+
deploymentId: session.deploymentId,
|
|
26
|
+
eventKind: 'turn',
|
|
27
|
+
sessionId: session.id,
|
|
28
|
+
surface: assistantSurface(session),
|
|
29
|
+
...(session.modelSource === undefined ? {} : { modelSource: session.modelSource }),
|
|
30
|
+
outcome: input.outcome,
|
|
31
|
+
turnNumber: input.turnNumber,
|
|
32
|
+
durationMs: input.durationMs,
|
|
33
|
+
...(input.counters ?? EMPTY_TURN_COUNTERS),
|
|
34
|
+
...(input.errorKind === undefined ? {} : { errorKind: input.errorKind }),
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
export function assistantRefusedTurnUsageRequestEvent(session, errorKind, durationMs) {
|
|
38
|
+
return assistantTurnUsageRequestEvent(session, {
|
|
39
|
+
outcome: 'refused',
|
|
40
|
+
turnNumber: session.turnCount,
|
|
41
|
+
durationMs,
|
|
42
|
+
errorKind,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
function assistantSurface(session) {
|
|
46
|
+
return session.publicEmbedId === undefined ? 'authenticated' : 'public';
|
|
47
|
+
}
|
|
48
|
+
export function assistantTurnNumber(session) {
|
|
49
|
+
if (session.publicEmbedId !== undefined)
|
|
50
|
+
return session.turnCount + 1;
|
|
51
|
+
return session.history.filter((message) => message.role === 'user').length + 1;
|
|
52
|
+
}
|
|
53
|
+
export function captureAssistantUsage(capture, event) {
|
|
54
|
+
try {
|
|
55
|
+
capture?.(event);
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
// Telemetry is best effort and never changes the assistant response path.
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Project one assistant lifecycle event onto the shared tenant request stream. The allowlist is
|
|
63
|
+
* deliberately scalar-only: conversation text, model messages, tool arguments/results, headers,
|
|
64
|
+
* credentials, and page context have no field to enter through.
|
|
65
|
+
*/
|
|
66
|
+
export function assistantUsageRequestEvent(input) {
|
|
67
|
+
const details = {
|
|
68
|
+
eventKind: input.eventKind,
|
|
69
|
+
surface: input.surface,
|
|
70
|
+
...(input.modelSource === undefined ? {} : { modelSource: input.modelSource }),
|
|
71
|
+
...(input.eventKind === 'turn'
|
|
72
|
+
? {
|
|
73
|
+
assistantOutcome: input.outcome,
|
|
74
|
+
turnNumber: input.turnNumber,
|
|
75
|
+
modelRequests: input.modelRequests,
|
|
76
|
+
toolCalls: input.toolCalls,
|
|
77
|
+
interactionCount: input.interactionCount,
|
|
78
|
+
promptTokens: input.promptTokens,
|
|
79
|
+
completionTokens: input.completionTokens,
|
|
80
|
+
totalTokens: input.totalTokens,
|
|
81
|
+
...(input.reasoningTokens === undefined
|
|
82
|
+
? {}
|
|
83
|
+
: { reasoningTokens: input.reasoningTokens }),
|
|
84
|
+
}
|
|
85
|
+
: {}),
|
|
86
|
+
};
|
|
87
|
+
return {
|
|
88
|
+
org: input.org,
|
|
89
|
+
app: input.app,
|
|
90
|
+
env: input.env,
|
|
91
|
+
deploymentId: input.deploymentId,
|
|
92
|
+
requestId: randomUUID(),
|
|
93
|
+
sessionId: input.sessionId,
|
|
94
|
+
sessionSource: 'synthetic',
|
|
95
|
+
subjectKind: input.surface === 'public' ? 'anonymous' : 'authenticated',
|
|
96
|
+
accessMode: input.surface === 'public' ? 'public' : 'authenticated',
|
|
97
|
+
method: ASSISTANT_USAGE_METHOD,
|
|
98
|
+
kind: 'usage',
|
|
99
|
+
outcome: input.eventKind === 'session' || input.outcome === 'delivered'
|
|
100
|
+
? 'ok'
|
|
101
|
+
: input.outcome === 'refused'
|
|
102
|
+
? 'tool_error'
|
|
103
|
+
: 'mcp_error',
|
|
104
|
+
...(input.eventKind === 'turn' && input.errorKind !== undefined
|
|
105
|
+
? { errorKind: input.errorKind }
|
|
106
|
+
: {}),
|
|
107
|
+
durationMs: finiteNonNegative(input.durationMs),
|
|
108
|
+
details,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
export function aggregateAssistantUsage(events) {
|
|
112
|
+
let minted = 0;
|
|
113
|
+
let publicSessions = 0;
|
|
114
|
+
let authenticatedSessions = 0;
|
|
115
|
+
let delivered = 0;
|
|
116
|
+
let failed = 0;
|
|
117
|
+
let refused = 0;
|
|
118
|
+
let modelRequests = 0;
|
|
119
|
+
let toolTurns = 0;
|
|
120
|
+
let interactionTurns = 0;
|
|
121
|
+
let prompt = 0;
|
|
122
|
+
let completion = 0;
|
|
123
|
+
let reasoning = 0;
|
|
124
|
+
let total = 0;
|
|
125
|
+
let noodleManaged = 0;
|
|
126
|
+
let operator = 0;
|
|
127
|
+
const latencies = [];
|
|
128
|
+
const sessionDepth = new Map();
|
|
129
|
+
for (const event of events) {
|
|
130
|
+
if (event.method !== ASSISTANT_USAGE_METHOD)
|
|
131
|
+
continue;
|
|
132
|
+
const details = event.details;
|
|
133
|
+
const eventKind = scalarString(details?.eventKind);
|
|
134
|
+
const surface = scalarString(details?.surface);
|
|
135
|
+
if (eventKind === 'session') {
|
|
136
|
+
minted += 1;
|
|
137
|
+
if (surface === 'public')
|
|
138
|
+
publicSessions += 1;
|
|
139
|
+
if (surface === 'authenticated')
|
|
140
|
+
authenticatedSessions += 1;
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
if (eventKind !== 'turn')
|
|
144
|
+
continue;
|
|
145
|
+
const outcome = scalarString(details?.assistantOutcome);
|
|
146
|
+
if (outcome === 'delivered')
|
|
147
|
+
delivered += 1;
|
|
148
|
+
else if (outcome === 'refused')
|
|
149
|
+
refused += 1;
|
|
150
|
+
else if (outcome === 'failed')
|
|
151
|
+
failed += 1;
|
|
152
|
+
else
|
|
153
|
+
continue;
|
|
154
|
+
latencies.push(finiteNonNegative(event.durationMs));
|
|
155
|
+
modelRequests += scalarCount(details?.modelRequests);
|
|
156
|
+
if (scalarCount(details?.toolCalls) > 0)
|
|
157
|
+
toolTurns += 1;
|
|
158
|
+
if (scalarCount(details?.interactionCount) > 0)
|
|
159
|
+
interactionTurns += 1;
|
|
160
|
+
prompt += scalarCount(details?.promptTokens);
|
|
161
|
+
completion += scalarCount(details?.completionTokens);
|
|
162
|
+
reasoning += scalarCount(details?.reasoningTokens);
|
|
163
|
+
total += scalarCount(details?.totalTokens);
|
|
164
|
+
const source = scalarString(details?.modelSource);
|
|
165
|
+
if (source === 'noodle-managed')
|
|
166
|
+
noodleManaged += 1;
|
|
167
|
+
if (source === 'operator')
|
|
168
|
+
operator += 1;
|
|
169
|
+
const turnNumber = scalarCount(details?.turnNumber);
|
|
170
|
+
if (event.sessionId !== undefined && turnNumber > 0) {
|
|
171
|
+
sessionDepth.set(event.sessionId, Math.max(sessionDepth.get(event.sessionId) ?? 0, turnNumber));
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
const depths = [...sessionDepth.values()].sort((a, b) => a - b);
|
|
175
|
+
const sortedLatencies = latencies.sort((a, b) => a - b);
|
|
176
|
+
return {
|
|
177
|
+
sessions: { minted, public: publicSessions, authenticated: authenticatedSessions },
|
|
178
|
+
turns: {
|
|
179
|
+
attempted: delivered + failed + refused,
|
|
180
|
+
delivered,
|
|
181
|
+
failed,
|
|
182
|
+
refused,
|
|
183
|
+
},
|
|
184
|
+
depth: {
|
|
185
|
+
p50: percentile(depths, 50),
|
|
186
|
+
p90: percentile(depths, 90),
|
|
187
|
+
max: depths.at(-1) ?? 0,
|
|
188
|
+
atLeast10: depths.filter((value) => value >= 10).length,
|
|
189
|
+
atLeast20: depths.filter((value) => value >= 20).length,
|
|
190
|
+
atLeast30: depths.filter((value) => value >= 30).length,
|
|
191
|
+
atLeast40: depths.filter((value) => value >= 40).length,
|
|
192
|
+
},
|
|
193
|
+
engagement: { modelRequests, toolTurns, interactionTurns },
|
|
194
|
+
latency: {
|
|
195
|
+
p50Ms: percentile(sortedLatencies, 50),
|
|
196
|
+
p95Ms: percentile(sortedLatencies, 95),
|
|
197
|
+
},
|
|
198
|
+
tokens: { prompt, completion, reasoning, total },
|
|
199
|
+
byModelSource: { noodleManaged, operator },
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
function scalarString(value) {
|
|
203
|
+
return typeof value === 'string' ? value : undefined;
|
|
204
|
+
}
|
|
205
|
+
function scalarCount(value) {
|
|
206
|
+
return typeof value === 'number' ? Math.floor(finiteNonNegative(value)) : 0;
|
|
207
|
+
}
|
|
208
|
+
function finiteNonNegative(value) {
|
|
209
|
+
return Number.isFinite(value) ? Math.max(0, value) : 0;
|
|
210
|
+
}
|
|
211
|
+
function percentile(sorted, percentileValue) {
|
|
212
|
+
if (sorted.length === 0)
|
|
213
|
+
return 0;
|
|
214
|
+
const rank = Math.ceil((percentileValue / 100) * sorted.length);
|
|
215
|
+
return sorted[Math.min(Math.max(rank, 1), sorted.length) - 1] ?? 0;
|
|
216
|
+
}
|
|
217
|
+
//# sourceMappingURL=assistant-usage.js.map
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
export * from './assistant-usage.js';
|
|
1
2
|
export * from './intent-capture.js';
|
|
2
3
|
export * from './request-event-buffer.js';
|
|
4
|
+
export * from './request-event-metrics.js';
|
|
3
5
|
export * from './request-events.js';
|
|
4
6
|
export * from './request-events-postgres.js';
|
|
5
7
|
export * from './runtime.js';
|
|
@@ -10,8 +10,11 @@ function hourBucket(iso) {
|
|
|
10
10
|
return `${iso.slice(0, 13)}:00:00.000Z`;
|
|
11
11
|
}
|
|
12
12
|
export function aggregateRequestEvents(events) {
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
// Assistant lifecycle records share the durable tenant stream but have their own aggregate. Keep
|
|
14
|
+
// the long-standing MCP metrics contract stable rather than counting one user turn as an MCP call.
|
|
15
|
+
const mcpEvents = events.filter((event) => event.method !== 'assistant');
|
|
16
|
+
const usage = mcpEvents.filter((event) => event.kind === 'usage');
|
|
17
|
+
const discovery = mcpEvents.length - usage.length;
|
|
15
18
|
const durations = usage.map((e) => e.durationMs).sort((a, b) => a - b);
|
|
16
19
|
let ok = 0;
|
|
17
20
|
let toolErrors = 0;
|
|
@@ -51,6 +51,10 @@ export async function ensureRequestEventSchema(pool) {
|
|
|
51
51
|
await pool.query(`
|
|
52
52
|
CREATE INDEX IF NOT EXISTS request_events_tenant_time_idx
|
|
53
53
|
ON request_events (org_slug, app_slug, environment, seq DESC)
|
|
54
|
+
`);
|
|
55
|
+
await pool.query(`
|
|
56
|
+
CREATE INDEX IF NOT EXISTS request_events_tenant_method_time_idx
|
|
57
|
+
ON request_events (org_slug, app_slug, environment, method, seq DESC)
|
|
54
58
|
`);
|
|
55
59
|
await pool.query(`
|
|
56
60
|
CREATE INDEX IF NOT EXISTS request_events_session_idx
|
|
@@ -128,6 +132,8 @@ export class PostgresRequestEventStore {
|
|
|
128
132
|
add('app_slug', filter.app);
|
|
129
133
|
if (filter.env !== undefined)
|
|
130
134
|
add('environment', filter.env);
|
|
135
|
+
if (filter.method !== undefined)
|
|
136
|
+
add('method', filter.method);
|
|
131
137
|
if (filter.outcome !== undefined)
|
|
132
138
|
add('outcome', filter.outcome);
|
|
133
139
|
if (filter.toolName !== undefined)
|
|
@@ -41,6 +41,8 @@ function matches(event, filter) {
|
|
|
41
41
|
return false;
|
|
42
42
|
if (filter.env !== undefined && event.env !== filter.env)
|
|
43
43
|
return false;
|
|
44
|
+
if (filter.method !== undefined && event.method !== filter.method)
|
|
45
|
+
return false;
|
|
44
46
|
if (filter.outcome !== undefined && event.outcome !== filter.outcome)
|
|
45
47
|
return false;
|
|
46
48
|
if (filter.toolName !== undefined && event.toolName !== filter.toolName)
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import YAML from 'yaml';
|
|
2
2
|
import { importResponseSchema, toOutputJsonSchema, } from './response-schema.js';
|
|
3
|
+
export * from './mcp/index.js';
|
|
3
4
|
export { importResponseSchema, MAX_RESPONSE_SCHEMA_DEPTH, MAX_RESPONSE_SCHEMA_NODES, toOutputJsonSchema, } from './response-schema.js';
|
|
4
5
|
const METHODS = new Set(['get', 'post', 'put', 'patch', 'delete']);
|
|
5
6
|
export function parseOpenApiToIr(source, options) {
|