@open-mercato/ai-assistant 0.6.8-develop.7100.1.fbf66fca35 → 0.7.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/.turbo/turbo-build.log +1 -1
- package/AGENTS.md +1 -1
- package/dist/modules/ai_assistant/ai-tools/search-pack.js +3 -93
- package/dist/modules/ai_assistant/ai-tools/search-pack.js.map +3 -3
- package/dist/modules/ai_assistant/backend/config/ai-assistant/moderation-flags/AiModerationFlagsPageClient.js +0 -2
- package/dist/modules/ai_assistant/backend/config/ai-assistant/moderation-flags/AiModerationFlagsPageClient.js.map +2 -2
- package/dist/modules/ai_assistant/lib/codemode-tools.js +6 -14
- package/dist/modules/ai_assistant/lib/codemode-tools.js.map +2 -2
- package/dist/modules/ai_assistant/lib/generated-registry-loader.js +2 -10
- package/dist/modules/ai_assistant/lib/generated-registry-loader.js.map +2 -2
- package/dist/modules/ai_assistant/lib/http-server.js +1 -3
- package/dist/modules/ai_assistant/lib/http-server.js.map +2 -2
- package/dist/modules/ai_assistant/lib/in-process-client.js +1 -3
- package/dist/modules/ai_assistant/lib/in-process-client.js.map +2 -2
- package/dist/modules/ai_assistant/lib/mcp-client.js +1 -2
- package/dist/modules/ai_assistant/lib/mcp-client.js.map +2 -2
- package/dist/modules/ai_assistant/lib/mcp-dev-server.js +1 -3
- package/dist/modules/ai_assistant/lib/mcp-dev-server.js.map +2 -2
- package/dist/modules/ai_assistant/lib/mcp-server.js +1 -3
- package/dist/modules/ai_assistant/lib/mcp-server.js.map +2 -2
- package/package.json +7 -8
- package/src/modules/ai_assistant/__tests__/integration/ws-c-tool-pack-coverage.test.ts +0 -5
- package/src/modules/ai_assistant/ai-tools/__tests__/search-pack.test.ts +5 -211
- package/src/modules/ai_assistant/ai-tools/search-pack.ts +4 -110
- package/src/modules/ai_assistant/backend/config/ai-assistant/moderation-flags/AiModerationFlagsPageClient.tsx +0 -3
- package/src/modules/ai_assistant/lib/__tests__/generated-registry-loader.test.ts +0 -16
- package/src/modules/ai_assistant/lib/__tests__/mcp-client.test.ts +0 -30
- package/src/modules/ai_assistant/lib/codemode-tools.ts +7 -21
- package/src/modules/ai_assistant/lib/generated-registry-loader.ts +2 -10
- package/src/modules/ai_assistant/lib/http-server.ts +0 -2
- package/src/modules/ai_assistant/lib/in-process-client.ts +0 -2
- package/src/modules/ai_assistant/lib/mcp-client.ts +0 -1
- package/src/modules/ai_assistant/lib/mcp-dev-server.ts +0 -2
- package/src/modules/ai_assistant/lib/mcp-server.ts +0 -2
- package/src/modules/ai_assistant/lib/types.ts +0 -11
- package/dist/modules/ai_assistant/lib/mcp-tool-annotations.js +0 -18
- package/dist/modules/ai_assistant/lib/mcp-tool-annotations.js.map +0 -7
- package/src/modules/ai_assistant/lib/__tests__/codemode-tool-annotations.test.ts +0 -58
- package/src/modules/ai_assistant/lib/__tests__/mcp-server-tool-annotations.test.ts +0 -120
- package/src/modules/ai_assistant/lib/__tests__/mcp-tool-annotations.test.ts +0 -57
- package/src/modules/ai_assistant/lib/mcp-tool-annotations.ts +0 -35
|
@@ -5,7 +5,6 @@ import {
|
|
|
5
5
|
CallToolRequestSchema,
|
|
6
6
|
} from '@modelcontextprotocol/sdk/types.js'
|
|
7
7
|
import { toolInputJsonSchema } from './tool-input-schema'
|
|
8
|
-
import { buildMcpToolAnnotations } from './mcp-tool-annotations'
|
|
9
8
|
import { getToolRegistry } from './tool-registry'
|
|
10
9
|
import { executeTool } from './tool-executor'
|
|
11
10
|
import { loadAllModuleTools, indexToolsForSearch } from './tool-loader'
|
|
@@ -134,7 +133,6 @@ export async function createMcpServer(options: McpServerOptions): Promise<Server
|
|
|
134
133
|
name: tool.name,
|
|
135
134
|
description: tool.description,
|
|
136
135
|
inputSchema: toolInputJsonSchema(tool.inputSchema),
|
|
137
|
-
annotations: buildMcpToolAnnotations(tool),
|
|
138
136
|
})),
|
|
139
137
|
}
|
|
140
138
|
})
|
|
@@ -249,17 +249,6 @@ export type ToolInfo = {
|
|
|
249
249
|
name: string
|
|
250
250
|
description: string
|
|
251
251
|
inputSchema: Record<string, unknown>
|
|
252
|
-
/** MCP capability hints advertised for the tool (see `buildMcpToolAnnotations`). */
|
|
253
|
-
annotations?: McpToolAnnotations
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
/** MCP `ToolAnnotations` values carried through remote and in-process clients. */
|
|
257
|
-
export type McpToolAnnotations = {
|
|
258
|
-
title?: string
|
|
259
|
-
readOnlyHint?: boolean
|
|
260
|
-
destructiveHint?: boolean
|
|
261
|
-
idempotentHint?: boolean
|
|
262
|
-
openWorldHint?: boolean
|
|
263
252
|
}
|
|
264
253
|
|
|
265
254
|
/**
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
function buildMcpToolAnnotations(tool) {
|
|
2
|
-
const definition = tool;
|
|
3
|
-
if (definition.isMutation !== true) {
|
|
4
|
-
return { readOnlyHint: true };
|
|
5
|
-
}
|
|
6
|
-
const { isDestructive } = definition;
|
|
7
|
-
if (isDestructive === void 0) {
|
|
8
|
-
return { readOnlyHint: false };
|
|
9
|
-
}
|
|
10
|
-
return {
|
|
11
|
-
readOnlyHint: false,
|
|
12
|
-
destructiveHint: typeof isDestructive === "function" ? true : isDestructive
|
|
13
|
-
};
|
|
14
|
-
}
|
|
15
|
-
export {
|
|
16
|
-
buildMcpToolAnnotations
|
|
17
|
-
};
|
|
18
|
-
//# sourceMappingURL=mcp-tool-annotations.js.map
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../../../src/modules/ai_assistant/lib/mcp-tool-annotations.ts"],
|
|
4
|
-
"sourcesContent": ["import type { AiToolDefinition, McpToolAnnotations, McpToolDefinition } from './types'\n\n/**\n * Build the MCP annotations advertised for a registered tool.\n *\n * `readOnlyHint` follows the registry's existing mutation contract: a tool is\n * read-only unless it declares `isMutation: true`. Every write tool is required\n * to set that flag (see the module AGENTS.md) and the agent mutation-policy gate\n * already reads it the same way.\n *\n * `destructiveHint` only carries meaning for mutating tools. A predicate\n * `isDestructive` cannot be evaluated without call input, so it resolves to\n * `true` rather than under-warning the client. When the flag is absent the hint\n * is omitted so clients fall back to the conservative MCP default (`true`).\n *\n * `idempotentHint` is deliberately not emitted: no tool-definition field\n * carries that information, so clients keep the MCP default (`false`).\n */\nexport function buildMcpToolAnnotations(tool: McpToolDefinition): McpToolAnnotations {\n const definition = tool as AiToolDefinition\n\n if (definition.isMutation !== true) {\n return { readOnlyHint: true }\n }\n\n const { isDestructive } = definition\n if (isDestructive === undefined) {\n return { readOnlyHint: false }\n }\n\n return {\n readOnlyHint: false,\n destructiveHint: typeof isDestructive === 'function' ? true : isDestructive,\n }\n}\n"],
|
|
5
|
-
"mappings": "AAkBO,SAAS,wBAAwB,MAA6C;AACnF,QAAM,aAAa;AAEnB,MAAI,WAAW,eAAe,MAAM;AAClC,WAAO,EAAE,cAAc,KAAK;AAAA,EAC9B;AAEA,QAAM,EAAE,cAAc,IAAI;AAC1B,MAAI,kBAAkB,QAAW;AAC/B,WAAO,EAAE,cAAc,MAAM;AAAA,EAC/B;AAEA,SAAO;AAAA,IACL,cAAc;AAAA,IACd,iBAAiB,OAAO,kBAAkB,aAAa,OAAO;AAAA,EAChE;AACF;",
|
|
6
|
-
"names": []
|
|
7
|
-
}
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import { loadCodeModeTools } from '../codemode-tools'
|
|
2
|
-
import { buildMcpToolAnnotations } from '../mcp-tool-annotations'
|
|
3
|
-
import { registerMcpTool } from '../tool-registry'
|
|
4
|
-
import type { McpToolDefinition } from '../types'
|
|
5
|
-
|
|
6
|
-
jest.mock('../api-endpoint-index', () => ({
|
|
7
|
-
getApiEndpoints: jest.fn(async () => []),
|
|
8
|
-
getRawOpenApiSpec: jest.fn(async () => null),
|
|
9
|
-
}))
|
|
10
|
-
|
|
11
|
-
jest.mock('../tool-registry', () => ({
|
|
12
|
-
registerMcpTool: jest.fn(),
|
|
13
|
-
}))
|
|
14
|
-
|
|
15
|
-
const mockedRegisterMcpTool = jest.mocked(registerMcpTool)
|
|
16
|
-
|
|
17
|
-
async function loadRegisteredCodeModeTools(): Promise<Map<string, McpToolDefinition>> {
|
|
18
|
-
await loadCodeModeTools()
|
|
19
|
-
const tools = new Map<string, McpToolDefinition>()
|
|
20
|
-
for (const call of mockedRegisterMcpTool.mock.calls) {
|
|
21
|
-
const tool = call[0] as McpToolDefinition
|
|
22
|
-
tools.set(tool.name, tool)
|
|
23
|
-
}
|
|
24
|
-
return tools
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
describe('issue #5283 — Code Mode tools declare their mutation surface', () => {
|
|
28
|
-
beforeEach(() => {
|
|
29
|
-
mockedRegisterMcpTool.mockClear()
|
|
30
|
-
})
|
|
31
|
-
|
|
32
|
-
it('advertises the spec-querying search tool as read-only', async () => {
|
|
33
|
-
const tools = await loadRegisteredCodeModeTools()
|
|
34
|
-
const search = tools.get('search')
|
|
35
|
-
|
|
36
|
-
expect(search).toBeDefined()
|
|
37
|
-
expect(buildMcpToolAnnotations(search!)).toEqual({ readOnlyHint: true })
|
|
38
|
-
})
|
|
39
|
-
|
|
40
|
-
it('never advertises the api.request() execute tool as read-only', async () => {
|
|
41
|
-
const tools = await loadRegisteredCodeModeTools()
|
|
42
|
-
const execute = tools.get('execute')
|
|
43
|
-
|
|
44
|
-
expect(execute).toBeDefined()
|
|
45
|
-
expect(buildMcpToolAnnotations(execute!)).toEqual({
|
|
46
|
-
readOnlyHint: false,
|
|
47
|
-
destructiveHint: true,
|
|
48
|
-
})
|
|
49
|
-
})
|
|
50
|
-
|
|
51
|
-
it('registers both Code Mode tools under the codemode module id', async () => {
|
|
52
|
-
await loadRegisteredCodeModeTools()
|
|
53
|
-
|
|
54
|
-
for (const call of mockedRegisterMcpTool.mock.calls) {
|
|
55
|
-
expect(call[1]).toEqual({ moduleId: 'codemode' })
|
|
56
|
-
}
|
|
57
|
-
})
|
|
58
|
-
})
|
|
@@ -1,120 +0,0 @@
|
|
|
1
|
-
import { z } from 'zod'
|
|
2
|
-
import { ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js'
|
|
3
|
-
import { createMcpServer } from '../mcp-server'
|
|
4
|
-
import type { AiToolDefinition, McpServerOptions, McpToolDefinition } from '../types'
|
|
5
|
-
|
|
6
|
-
const mockCapturedHandlers = new Map<unknown, (request: unknown) => Promise<unknown>>()
|
|
7
|
-
const mockRegisteredTools = new Map<string, McpToolDefinition>()
|
|
8
|
-
|
|
9
|
-
jest.mock('@modelcontextprotocol/sdk/server/index.js', () => ({
|
|
10
|
-
Server: class {
|
|
11
|
-
setRequestHandler(schema: unknown, handler: (request: unknown) => Promise<unknown>) {
|
|
12
|
-
mockCapturedHandlers.set(schema, handler)
|
|
13
|
-
}
|
|
14
|
-
},
|
|
15
|
-
}))
|
|
16
|
-
jest.mock('@modelcontextprotocol/sdk/server/stdio.js', () => ({
|
|
17
|
-
StdioServerTransport: class {},
|
|
18
|
-
}))
|
|
19
|
-
jest.mock('@modelcontextprotocol/sdk/types.js', () => ({
|
|
20
|
-
ListToolsRequestSchema: { id: 'tools/list' },
|
|
21
|
-
CallToolRequestSchema: { id: 'tools/call' },
|
|
22
|
-
}))
|
|
23
|
-
jest.mock('../tool-registry', () => ({
|
|
24
|
-
getToolRegistry: () => ({
|
|
25
|
-
getTools: () => mockRegisteredTools,
|
|
26
|
-
listToolNames: () => Array.from(mockRegisteredTools.keys()),
|
|
27
|
-
}),
|
|
28
|
-
}))
|
|
29
|
-
jest.mock('../tool-executor', () => ({ executeTool: jest.fn() }))
|
|
30
|
-
jest.mock('../tool-loader', () => ({
|
|
31
|
-
loadAllModuleTools: jest.fn(),
|
|
32
|
-
indexToolsForSearch: jest.fn(),
|
|
33
|
-
}))
|
|
34
|
-
jest.mock('../auth', () => ({
|
|
35
|
-
authenticateMcpRequest: jest.fn(),
|
|
36
|
-
hasRequiredFeatures: jest.fn(() => true),
|
|
37
|
-
}))
|
|
38
|
-
|
|
39
|
-
function registerTool(overrides: Partial<AiToolDefinition> & { name: string }): void {
|
|
40
|
-
mockRegisteredTools.set(overrides.name, {
|
|
41
|
-
description: `${overrides.name} description`,
|
|
42
|
-
inputSchema: z.object({ id: z.string() }),
|
|
43
|
-
handler: async () => ({}),
|
|
44
|
-
...overrides,
|
|
45
|
-
} as McpToolDefinition)
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
function makeContainer(): McpServerOptions['container'] {
|
|
49
|
-
return {
|
|
50
|
-
resolve: () => ({ loadAcl: jest.fn() }),
|
|
51
|
-
} as unknown as McpServerOptions['container']
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
type ListedTool = {
|
|
55
|
-
name: string
|
|
56
|
-
description: string
|
|
57
|
-
inputSchema: unknown
|
|
58
|
-
annotations?: { readOnlyHint?: boolean; destructiveHint?: boolean }
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
async function listTools(): Promise<ListedTool[]> {
|
|
62
|
-
await createMcpServer({
|
|
63
|
-
config: { name: 'test-mcp', version: '0.0.0' },
|
|
64
|
-
container: makeContainer(),
|
|
65
|
-
allowUnauthenticatedSuperadmin: true,
|
|
66
|
-
})
|
|
67
|
-
const handler = mockCapturedHandlers.get(ListToolsRequestSchema)
|
|
68
|
-
if (!handler) throw new Error('[internal] tools/list handler was not registered')
|
|
69
|
-
const response = (await handler({})) as { tools: ListedTool[] }
|
|
70
|
-
return response.tools
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
describe('issue #5283 — MCP tools/list publishes readOnlyHint annotations', () => {
|
|
74
|
-
beforeEach(() => {
|
|
75
|
-
mockRegisteredTools.clear()
|
|
76
|
-
mockCapturedHandlers.clear()
|
|
77
|
-
jest.spyOn(process.stderr, 'write').mockImplementation(() => true)
|
|
78
|
-
})
|
|
79
|
-
|
|
80
|
-
afterEach(() => {
|
|
81
|
-
jest.restoreAllMocks()
|
|
82
|
-
})
|
|
83
|
-
|
|
84
|
-
it('marks read operations as read-only so clients can skip approval', async () => {
|
|
85
|
-
registerTool({ name: 'customers.get_company' })
|
|
86
|
-
registerTool({ name: 'customers.list_people' })
|
|
87
|
-
registerTool({ name: 'search_status', isMutation: false })
|
|
88
|
-
|
|
89
|
-
const tools = await listTools()
|
|
90
|
-
|
|
91
|
-
expect(tools).toHaveLength(3)
|
|
92
|
-
for (const tool of tools) {
|
|
93
|
-
expect(tool.annotations?.readOnlyHint).toBe(true)
|
|
94
|
-
}
|
|
95
|
-
})
|
|
96
|
-
|
|
97
|
-
it('never marks mutating operations as read-only', async () => {
|
|
98
|
-
registerTool({ name: 'catalog.update_product', isMutation: true })
|
|
99
|
-
registerTool({ name: 'customers.manage_deal_comment', isMutation: true, isDestructive: true })
|
|
100
|
-
|
|
101
|
-
const tools = await listTools()
|
|
102
|
-
|
|
103
|
-
const byName = new Map(tools.map((tool) => [tool.name, tool]))
|
|
104
|
-
expect(byName.get('catalog.update_product')?.annotations?.readOnlyHint).toBe(false)
|
|
105
|
-
expect(byName.get('customers.manage_deal_comment')?.annotations).toEqual({
|
|
106
|
-
readOnlyHint: false,
|
|
107
|
-
destructiveHint: true,
|
|
108
|
-
})
|
|
109
|
-
})
|
|
110
|
-
|
|
111
|
-
it('keeps the existing name / description / inputSchema fields intact', async () => {
|
|
112
|
-
registerTool({ name: 'customers.get_company' })
|
|
113
|
-
|
|
114
|
-
const [tool] = await listTools()
|
|
115
|
-
|
|
116
|
-
expect(tool.name).toBe('customers.get_company')
|
|
117
|
-
expect(tool.description).toBe('customers.get_company description')
|
|
118
|
-
expect(tool.inputSchema).toBeDefined()
|
|
119
|
-
})
|
|
120
|
-
})
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
import { z } from 'zod'
|
|
2
|
-
import { buildMcpToolAnnotations } from '../mcp-tool-annotations'
|
|
3
|
-
import type { AiToolDefinition } from '../types'
|
|
4
|
-
|
|
5
|
-
function makeTool(overrides: Partial<AiToolDefinition> = {}): AiToolDefinition {
|
|
6
|
-
return {
|
|
7
|
-
name: 'customers.get_company',
|
|
8
|
-
description: 'Fetch a single company record.',
|
|
9
|
-
inputSchema: z.object({ id: z.string() }),
|
|
10
|
-
handler: async () => ({}),
|
|
11
|
-
...overrides,
|
|
12
|
-
} as AiToolDefinition
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
describe('buildMcpToolAnnotations', () => {
|
|
16
|
-
it('advertises readOnlyHint for tools that do not declare a mutation', () => {
|
|
17
|
-
expect(buildMcpToolAnnotations(makeTool())).toEqual({ readOnlyHint: true })
|
|
18
|
-
})
|
|
19
|
-
|
|
20
|
-
it('advertises readOnlyHint for tools that explicitly declare isMutation: false', () => {
|
|
21
|
-
expect(buildMcpToolAnnotations(makeTool({ isMutation: false }))).toEqual({ readOnlyHint: true })
|
|
22
|
-
})
|
|
23
|
-
|
|
24
|
-
it('never advertises readOnlyHint for mutating tools', () => {
|
|
25
|
-
expect(buildMcpToolAnnotations(makeTool({ isMutation: true }))).toEqual({ readOnlyHint: false })
|
|
26
|
-
})
|
|
27
|
-
|
|
28
|
-
it('advertises destructiveHint when the mutation declares isDestructive', () => {
|
|
29
|
-
expect(buildMcpToolAnnotations(makeTool({ isMutation: true, isDestructive: true }))).toEqual({
|
|
30
|
-
readOnlyHint: false,
|
|
31
|
-
destructiveHint: true,
|
|
32
|
-
})
|
|
33
|
-
})
|
|
34
|
-
|
|
35
|
-
it('carries an explicit non-destructive declaration through', () => {
|
|
36
|
-
expect(buildMcpToolAnnotations(makeTool({ isMutation: true, isDestructive: false }))).toEqual({
|
|
37
|
-
readOnlyHint: false,
|
|
38
|
-
destructiveHint: false,
|
|
39
|
-
})
|
|
40
|
-
})
|
|
41
|
-
|
|
42
|
-
it('resolves a per-input isDestructive predicate to true rather than under-warning', () => {
|
|
43
|
-
const annotations = buildMcpToolAnnotations(
|
|
44
|
-
makeTool({ isMutation: true, isDestructive: (input: unknown) => (input as { op: string }).op === 'delete' }),
|
|
45
|
-
)
|
|
46
|
-
expect(annotations).toEqual({ readOnlyHint: false, destructiveHint: true })
|
|
47
|
-
})
|
|
48
|
-
|
|
49
|
-
it('omits destructiveHint when a mutation does not declare isDestructive', () => {
|
|
50
|
-
expect(buildMcpToolAnnotations(makeTool({ isMutation: true }))).not.toHaveProperty('destructiveHint')
|
|
51
|
-
})
|
|
52
|
-
|
|
53
|
-
it('never advertises idempotentHint, which no tool definition carries', () => {
|
|
54
|
-
expect(buildMcpToolAnnotations(makeTool())).not.toHaveProperty('idempotentHint')
|
|
55
|
-
expect(buildMcpToolAnnotations(makeTool({ isMutation: true }))).not.toHaveProperty('idempotentHint')
|
|
56
|
-
})
|
|
57
|
-
})
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import type { AiToolDefinition, McpToolAnnotations, McpToolDefinition } from './types'
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Build the MCP annotations advertised for a registered tool.
|
|
5
|
-
*
|
|
6
|
-
* `readOnlyHint` follows the registry's existing mutation contract: a tool is
|
|
7
|
-
* read-only unless it declares `isMutation: true`. Every write tool is required
|
|
8
|
-
* to set that flag (see the module AGENTS.md) and the agent mutation-policy gate
|
|
9
|
-
* already reads it the same way.
|
|
10
|
-
*
|
|
11
|
-
* `destructiveHint` only carries meaning for mutating tools. A predicate
|
|
12
|
-
* `isDestructive` cannot be evaluated without call input, so it resolves to
|
|
13
|
-
* `true` rather than under-warning the client. When the flag is absent the hint
|
|
14
|
-
* is omitted so clients fall back to the conservative MCP default (`true`).
|
|
15
|
-
*
|
|
16
|
-
* `idempotentHint` is deliberately not emitted: no tool-definition field
|
|
17
|
-
* carries that information, so clients keep the MCP default (`false`).
|
|
18
|
-
*/
|
|
19
|
-
export function buildMcpToolAnnotations(tool: McpToolDefinition): McpToolAnnotations {
|
|
20
|
-
const definition = tool as AiToolDefinition
|
|
21
|
-
|
|
22
|
-
if (definition.isMutation !== true) {
|
|
23
|
-
return { readOnlyHint: true }
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
const { isDestructive } = definition
|
|
27
|
-
if (isDestructive === undefined) {
|
|
28
|
-
return { readOnlyHint: false }
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
return {
|
|
32
|
-
readOnlyHint: false,
|
|
33
|
-
destructiveHint: typeof isDestructive === 'function' ? true : isDestructive,
|
|
34
|
-
}
|
|
35
|
-
}
|