@objectstack/service-ai 4.0.1 → 4.0.3

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.
Files changed (44) hide show
  1. package/.turbo/turbo-build.log +11 -11
  2. package/CHANGELOG.md +17 -0
  3. package/dist/index.cjs +1632 -355
  4. package/dist/index.cjs.map +1 -1
  5. package/dist/index.d.cts +330 -87
  6. package/dist/index.d.ts +330 -87
  7. package/dist/index.js +1623 -352
  8. package/dist/index.js.map +1 -1
  9. package/package.json +27 -5
  10. package/src/__tests__/ai-service.test.ts +260 -27
  11. package/src/__tests__/auth-and-toolcalling.test.ts +81 -29
  12. package/src/__tests__/chatbot-features.test.ts +397 -102
  13. package/src/__tests__/metadata-tools.test.ts +970 -0
  14. package/src/__tests__/objectql-conversation-service.test.ts +34 -16
  15. package/src/__tests__/tool-routes.test.ts +191 -0
  16. package/src/__tests__/vercel-stream-encoder.test.ts +310 -0
  17. package/src/adapters/index.ts +2 -0
  18. package/src/adapters/memory-adapter.ts +17 -9
  19. package/src/adapters/vercel-adapter.ts +148 -0
  20. package/src/agent-runtime.ts +27 -3
  21. package/src/agents/index.ts +1 -0
  22. package/src/agents/metadata-assistant-agent.ts +87 -0
  23. package/src/ai-service.ts +75 -36
  24. package/src/conversation/in-memory-conversation-service.ts +2 -2
  25. package/src/conversation/objectql-conversation-service.ts +67 -18
  26. package/src/index.ts +22 -2
  27. package/src/plugin.ts +237 -30
  28. package/src/routes/agent-routes.ts +68 -12
  29. package/src/routes/ai-routes.ts +93 -14
  30. package/src/routes/index.ts +1 -0
  31. package/src/routes/message-utils.ts +90 -0
  32. package/src/routes/tool-routes.ts +142 -0
  33. package/src/stream/index.ts +3 -0
  34. package/src/stream/vercel-stream-encoder.ts +153 -0
  35. package/src/tools/add-field.tool.ts +70 -0
  36. package/src/tools/create-object.tool.ts +66 -0
  37. package/src/tools/data-tools.ts +4 -101
  38. package/src/tools/delete-field.tool.ts +38 -0
  39. package/src/tools/describe-object.tool.ts +31 -0
  40. package/src/tools/index.ts +12 -1
  41. package/src/tools/list-objects.tool.ts +34 -0
  42. package/src/tools/metadata-tools.ts +430 -0
  43. package/src/tools/modify-field.tool.ts +44 -0
  44. package/src/tools/tool-registry.ts +32 -9
@@ -0,0 +1,66 @@
1
+ // Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
2
+
3
+ import { defineTool } from '@objectstack/spec/ai';
4
+
5
+ /**
6
+ * create_object — AI Tool Metadata
7
+ *
8
+ * Creates a new data object (table) with schema validation.
9
+ * Validates snake_case naming for object and initial fields,
10
+ * checks for duplicates, and registers the object definition.
11
+ */
12
+ export const createObjectTool = defineTool({
13
+ name: 'create_object',
14
+ label: 'Create Object',
15
+ description:
16
+ 'Creates a new data object (table) with the specified name, label, and optional field definitions. ' +
17
+ 'Use this when the user wants to create a new entity, table, or data model.',
18
+ category: 'data',
19
+ builtIn: true,
20
+ // NOTE: requiresConfirmation is intentionally false (default) because the
21
+ // server-side tool-call loop in AIService.chatWithTools/streamChatWithTools
22
+ // executes tool calls immediately without checking this flag. The flag
23
+ // should only be set once server-side approval gating is implemented to
24
+ // avoid giving users a false sense of safety.
25
+ parameters: {
26
+ type: 'object',
27
+ properties: {
28
+ name: {
29
+ type: 'string',
30
+ description: 'Machine name for the object (snake_case, e.g. project_task)',
31
+ },
32
+ label: {
33
+ type: 'string',
34
+ description: 'Human-readable display name (e.g. Project Task)',
35
+ },
36
+ fields: {
37
+ type: 'array',
38
+ description: 'Initial fields to create with the object',
39
+ items: {
40
+ type: 'object',
41
+ properties: {
42
+ name: { type: 'string', description: 'Field machine name (snake_case)' },
43
+ label: { type: 'string', description: 'Field display name' },
44
+ type: {
45
+ type: 'string',
46
+ description: 'Field data type',
47
+ enum: ['text', 'textarea', 'number', 'boolean', 'date', 'datetime', 'select', 'lookup', 'formula', 'autonumber'],
48
+ },
49
+ required: { type: 'boolean', description: 'Whether the field is required' },
50
+ },
51
+ required: ['name', 'type'],
52
+ },
53
+ },
54
+ enableFeatures: {
55
+ type: 'object',
56
+ description: 'Object capability flags',
57
+ properties: {
58
+ trackHistory: { type: 'boolean' },
59
+ apiEnabled: { type: 'boolean' },
60
+ },
61
+ },
62
+ },
63
+ required: ['name', 'label'],
64
+ additionalProperties: false,
65
+ },
66
+ });
@@ -1,30 +1,9 @@
1
1
  // Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
2
2
 
3
- import type { AIToolDefinition, IDataEngine, IMetadataService } from '@objectstack/spec/contracts';
3
+ import type { AIToolDefinition, IDataEngine } from '@objectstack/spec/contracts';
4
4
  import type { ToolHandler } from './tool-registry.js';
5
5
  import type { ToolRegistry } from './tool-registry.js';
6
6
 
7
- // ---------------------------------------------------------------------------
8
- // Internal type aliases for metadata payloads (returned as `unknown` from
9
- // IMetadataService — we cast to these lightweight shapes for field access).
10
- // ---------------------------------------------------------------------------
11
-
12
- /** Minimal shape of an object definition as returned by IMetadataService. */
13
- interface ObjectDef {
14
- name: string;
15
- label?: string;
16
- fields?: Record<string, FieldDef>;
17
- }
18
-
19
- /** Minimal shape of a field definition inside an object. */
20
- interface FieldDef {
21
- type?: string;
22
- label?: string;
23
- required?: boolean;
24
- reference?: string;
25
- options?: unknown;
26
- }
27
-
28
7
  // ---------------------------------------------------------------------------
29
8
  // Data context — injected once at registration time
30
9
  // ---------------------------------------------------------------------------
@@ -38,8 +17,6 @@ interface FieldDef {
38
17
  export interface DataToolContext {
39
18
  /** ObjectQL data engine for record-level operations. */
40
19
  dataEngine: IDataEngine;
41
- /** Metadata service for schema/object introspection. */
42
- metadataService: IMetadataService;
43
20
  }
44
21
 
45
22
  // ---------------------------------------------------------------------------
@@ -52,34 +29,6 @@ const MAX_QUERY_LIMIT = 200;
52
29
  /** Default record limit when not specified. */
53
30
  const DEFAULT_QUERY_LIMIT = 20;
54
31
 
55
- export const LIST_OBJECTS_TOOL: AIToolDefinition = {
56
- name: 'list_objects',
57
- description: 'List all available data objects (tables) in the system. Returns object names and labels.',
58
- parameters: {
59
- type: 'object',
60
- properties: {},
61
- additionalProperties: false,
62
- },
63
- };
64
-
65
- export const DESCRIBE_OBJECT_TOOL: AIToolDefinition = {
66
- name: 'describe_object',
67
- description:
68
- 'Get the schema (fields, types, labels) of a specific data object. ' +
69
- 'Use this to understand the structure of a table before querying it.',
70
- parameters: {
71
- type: 'object',
72
- properties: {
73
- objectName: {
74
- type: 'string',
75
- description: 'The snake_case name of the object to describe',
76
- },
77
- },
78
- required: ['objectName'],
79
- additionalProperties: false,
80
- },
81
- };
82
-
83
32
  export const QUERY_RECORDS_TOOL: AIToolDefinition = {
84
33
  name: 'query_records',
85
34
  description:
@@ -203,10 +152,8 @@ export const AGGREGATE_DATA_TOOL: AIToolDefinition = {
203
152
  },
204
153
  };
205
154
 
206
- /** All built-in data tool definitions. */
155
+ /** All built-in data tools definitions. */
207
156
  export const DATA_TOOL_DEFINITIONS: AIToolDefinition[] = [
208
- LIST_OBJECTS_TOOL,
209
- DESCRIBE_OBJECT_TOOL,
210
157
  QUERY_RECORDS_TOOL,
211
158
  GET_RECORD_TOOL,
212
159
  AGGREGATE_DATA_TOOL,
@@ -216,46 +163,6 @@ export const DATA_TOOL_DEFINITIONS: AIToolDefinition[] = [
216
163
  // Handler Factories
217
164
  // ---------------------------------------------------------------------------
218
165
 
219
- function createListObjectsHandler(ctx: DataToolContext): ToolHandler {
220
- return async () => {
221
- const objects = await ctx.metadataService.listObjects();
222
- const summary = (objects as ObjectDef[]).map(o => ({
223
- name: o.name,
224
- label: o.label ?? o.name,
225
- }));
226
- return JSON.stringify(summary);
227
- };
228
- }
229
-
230
- function createDescribeObjectHandler(ctx: DataToolContext): ToolHandler {
231
- return async (args) => {
232
- const { objectName } = args as { objectName: string };
233
- const objectDef = await ctx.metadataService.getObject(objectName);
234
- if (!objectDef) {
235
- return JSON.stringify({ error: `Object "${objectName}" not found` });
236
- }
237
-
238
- const def = objectDef as ObjectDef;
239
- const fields = def.fields ?? {};
240
- const fieldSummary: Record<string, Record<string, unknown>> = {};
241
- for (const [key, f] of Object.entries(fields)) {
242
- fieldSummary[key] = {
243
- type: f.type,
244
- label: f.label ?? key,
245
- required: f.required ?? false,
246
- ...(f.reference ? { reference: f.reference } : {}),
247
- ...(f.options ? { options: f.options } : {}),
248
- };
249
- }
250
-
251
- return JSON.stringify({
252
- name: def.name,
253
- label: def.label ?? def.name,
254
- fields: fieldSummary,
255
- });
256
- };
257
- }
258
-
259
166
  function createQueryRecordsHandler(ctx: DataToolContext): ToolHandler {
260
167
  return async (args) => {
261
168
  const {
@@ -366,15 +273,13 @@ function createAggregateDataHandler(ctx: DataToolContext): ToolHandler {
366
273
  /**
367
274
  * Register all built-in data tools on the given {@link ToolRegistry}.
368
275
  *
369
- * Typically called from the `ai:ready` hook after both the data engine
370
- * and metadata service are available.
276
+ * Typically called from the `ai:ready` hook after the data engine is available.
371
277
  *
372
278
  * @example
373
279
  * ```ts
374
280
  * ctx.hook('ai:ready', async (aiService) => {
375
281
  * const dataEngine = ctx.getService<IDataEngine>('data');
376
- * const metadataService = ctx.getService<IMetadataService>('metadata');
377
- * registerDataTools(aiService.toolRegistry, { dataEngine, metadataService });
282
+ * registerDataTools(aiService.toolRegistry, { dataEngine });
378
283
  * });
379
284
  * ```
380
285
  */
@@ -382,8 +287,6 @@ export function registerDataTools(
382
287
  registry: ToolRegistry,
383
288
  context: DataToolContext,
384
289
  ): void {
385
- registry.register(LIST_OBJECTS_TOOL, createListObjectsHandler(context));
386
- registry.register(DESCRIBE_OBJECT_TOOL, createDescribeObjectHandler(context));
387
290
  registry.register(QUERY_RECORDS_TOOL, createQueryRecordsHandler(context));
388
291
  registry.register(GET_RECORD_TOOL, createGetRecordHandler(context));
389
292
  registry.register(AGGREGATE_DATA_TOOL, createAggregateDataHandler(context));
@@ -0,0 +1,38 @@
1
+ // Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
2
+
3
+ import { defineTool } from '@objectstack/spec/ai';
4
+
5
+ /**
6
+ * delete_field — AI Tool Metadata
7
+ *
8
+ * Removes a field (column) from an existing data object.
9
+ * This is a destructive operation.
10
+ */
11
+ export const deleteFieldTool = defineTool({
12
+ name: 'delete_field',
13
+ label: 'Delete Field',
14
+ description:
15
+ 'Removes a field (column) from an existing data object. This is a destructive operation. ' +
16
+ 'Use this when the user explicitly wants to remove an attribute or column from a table.',
17
+ category: 'data',
18
+ builtIn: true,
19
+ // NOTE: requiresConfirmation is intentionally false (default) because the
20
+ // server-side tool-call loop in AIService.chatWithTools/streamChatWithTools
21
+ // executes tool calls immediately without checking this flag. The flag
22
+ // should only be set once server-side approval gating is implemented.
23
+ parameters: {
24
+ type: 'object',
25
+ properties: {
26
+ objectName: {
27
+ type: 'string',
28
+ description: 'Target object machine name (snake_case)',
29
+ },
30
+ fieldName: {
31
+ type: 'string',
32
+ description: 'Field machine name to delete (snake_case)',
33
+ },
34
+ },
35
+ required: ['objectName', 'fieldName'],
36
+ additionalProperties: false,
37
+ },
38
+ });
@@ -0,0 +1,31 @@
1
+ // Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
2
+
3
+ import { defineTool } from '@objectstack/spec/ai';
4
+
5
+ /**
6
+ * describe_object — AI Tool Metadata
7
+ *
8
+ * Returns the full schema of a data object including all fields, types,
9
+ * relationships, and configuration. This is the single, unified tool for
10
+ * describing objects — used by both data_chat and metadata_assistant agents.
11
+ */
12
+ export const describeObjectTool = defineTool({
13
+ name: 'describe_object',
14
+ label: 'Describe Object',
15
+ description:
16
+ 'Returns the full schema details of a data object, including all fields, types, relationships, and configuration. ' +
17
+ 'Use this to understand the structure of a table before querying or modifying it.',
18
+ category: 'data',
19
+ builtIn: true,
20
+ parameters: {
21
+ type: 'object',
22
+ properties: {
23
+ objectName: {
24
+ type: 'string',
25
+ description: 'Object machine name to describe (snake_case)',
26
+ },
27
+ },
28
+ required: ['objectName'],
29
+ additionalProperties: false,
30
+ },
31
+ });
@@ -1,7 +1,18 @@
1
1
  // Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
2
2
 
3
3
  export { ToolRegistry } from './tool-registry.js';
4
- export type { ToolHandler } from './tool-registry.js';
4
+ export type { ToolHandler, ToolExecutionResult } from './tool-registry.js';
5
5
 
6
6
  export { registerDataTools, DATA_TOOL_DEFINITIONS } from './data-tools.js';
7
7
  export type { DataToolContext } from './data-tools.js';
8
+
9
+ export { registerMetadataTools, METADATA_TOOL_DEFINITIONS } from './metadata-tools.js';
10
+ export type { MetadataToolContext } from './metadata-tools.js';
11
+
12
+ // Individual tool metadata exports
13
+ export { createObjectTool } from './create-object.tool.js';
14
+ export { addFieldTool } from './add-field.tool.js';
15
+ export { modifyFieldTool } from './modify-field.tool.js';
16
+ export { deleteFieldTool } from './delete-field.tool.js';
17
+ export { listObjectsTool } from './list-objects.tool.js';
18
+ export { describeObjectTool } from './describe-object.tool.js';
@@ -0,0 +1,34 @@
1
+ // Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
2
+
3
+ import { defineTool } from '@objectstack/spec/ai';
4
+
5
+ /**
6
+ * list_objects — AI Tool Metadata
7
+ *
8
+ * Lists all registered data objects (tables) with optional filtering
9
+ * and field summaries. This is the single, unified tool for listing
10
+ * objects — used by both data_chat and metadata_assistant agents.
11
+ */
12
+ export const listObjectsTool = defineTool({
13
+ name: 'list_objects',
14
+ label: 'List Objects',
15
+ description:
16
+ 'Lists all registered data objects (tables) in the current environment. ' +
17
+ 'Use this when the user wants to see what tables, entities, or data models are available.',
18
+ category: 'data',
19
+ builtIn: true,
20
+ parameters: {
21
+ type: 'object',
22
+ properties: {
23
+ filter: {
24
+ type: 'string',
25
+ description: 'Optional name or label substring to filter objects',
26
+ },
27
+ includeFields: {
28
+ type: 'boolean',
29
+ description: 'Whether to include field summaries for each object (default: false)',
30
+ },
31
+ },
32
+ additionalProperties: false,
33
+ },
34
+ });