@objectstack/service-ai 6.1.1 → 6.3.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/index.cjs +904 -50
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +156 -18
- package/dist/index.d.ts +156 -18
- package/dist/index.js +904 -50
- package/dist/index.js.map +1 -1
- package/package.json +6 -6
package/dist/index.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AIToolDefinition, ToolCallPart, ToolResultPart, IDataEngine, Logger, IAIService, IAIConversationService, LLMAdapter, ModelMessage, AIRequestOptions, AIResult, GenerateObjectOptions, AIObjectResult, TextStreamPart, ToolSet, ChatWithToolsOptions, AIConversation, IMetadataService } from '@objectstack/spec/contracts';
|
|
1
|
+
import { AIToolDefinition, ToolCallPart, ToolResultPart, IDataEngine, Logger, IAIService, IAIConversationService, LLMAdapter, ModelMessage, AIRequestOptions, AIResult, GenerateObjectOptions, AIObjectResult, TextStreamPart, ToolSet, ChatWithToolsOptions, ProposePendingActionInput, PendingActionStatus, PendingActionRow, AIConversation, IMetadataService, IAutomationService } from '@objectstack/spec/contracts';
|
|
2
2
|
export { LLMAdapter } from '@objectstack/spec/contracts';
|
|
3
3
|
import { z } from 'zod';
|
|
4
4
|
import * as AI from '@objectstack/spec/ai';
|
|
@@ -258,6 +258,13 @@ interface AIServiceConfig {
|
|
|
258
258
|
modelRegistry?: ModelRegistry;
|
|
259
259
|
/** Trace recorder for per-call observability. Defaults to no-op. */
|
|
260
260
|
traceRecorder?: TraceRecorder;
|
|
261
|
+
/**
|
|
262
|
+
* Data engine used to persist `ai_pending_actions` rows for the
|
|
263
|
+
* actions-as-tools HITL queue. Optional — when omitted, the
|
|
264
|
+
* `proposePendingAction` / `approvePendingAction` methods throw if
|
|
265
|
+
* called. Wired by `AIServicePlugin` after the data driver is up.
|
|
266
|
+
*/
|
|
267
|
+
dataEngine?: IDataEngine;
|
|
261
268
|
}
|
|
262
269
|
/**
|
|
263
270
|
* AIService — Unified AI capability service.
|
|
@@ -281,6 +288,15 @@ declare class AIService implements IAIService {
|
|
|
281
288
|
readonly conversationService: IAIConversationService;
|
|
282
289
|
readonly modelRegistry?: ModelRegistry;
|
|
283
290
|
readonly traceRecorder: TraceRecorder;
|
|
291
|
+
/**
|
|
292
|
+
* Map of tool-name → dispatcher used to re-run an approved pending
|
|
293
|
+
* action. Populated by `registerActionsAsTools()` when action
|
|
294
|
+
* approval is enabled. Kept private because callers should go
|
|
295
|
+
* through `approvePendingAction()`.
|
|
296
|
+
*/
|
|
297
|
+
private readonly pendingDispatchers;
|
|
298
|
+
/** Data engine for `ai_pending_actions` persistence. */
|
|
299
|
+
private readonly dataEngine?;
|
|
284
300
|
constructor(config?: AIServiceConfig);
|
|
285
301
|
/** The name of the active LLM adapter. */
|
|
286
302
|
get adapterName(): string;
|
|
@@ -335,6 +351,29 @@ declare class AIService implements IAIService {
|
|
|
335
351
|
* fed back until a final text stream is produced.
|
|
336
352
|
*/
|
|
337
353
|
streamChatWithTools(messages: ModelMessage[], options?: ChatWithToolsOptions): AsyncIterable<TextStreamPart<ToolSet>>;
|
|
354
|
+
/**
|
|
355
|
+
* Register a dispatcher callback for a tool. Called by
|
|
356
|
+
* `registerActionsAsTools()` when action approval is enabled so the
|
|
357
|
+
* approval handler can re-run the exact same code path the LLM
|
|
358
|
+
* would have triggered.
|
|
359
|
+
*/
|
|
360
|
+
registerPendingActionDispatcher(toolName: string, dispatch: (input: Record<string, unknown>) => Promise<unknown>): void;
|
|
361
|
+
proposePendingAction(input: ProposePendingActionInput): Promise<{
|
|
362
|
+
id: string;
|
|
363
|
+
}>;
|
|
364
|
+
approvePendingAction(id: string, actorId: string): Promise<{
|
|
365
|
+
status: 'executed' | 'failed';
|
|
366
|
+
result?: unknown;
|
|
367
|
+
error?: string;
|
|
368
|
+
}>;
|
|
369
|
+
rejectPendingAction(id: string, actorId: string, reason?: string): Promise<void>;
|
|
370
|
+
listPendingActions(filter?: {
|
|
371
|
+
status?: PendingActionStatus | PendingActionStatus[];
|
|
372
|
+
conversationId?: string;
|
|
373
|
+
objectName?: string;
|
|
374
|
+
limit?: number;
|
|
375
|
+
}): Promise<PendingActionRow[]>;
|
|
376
|
+
private loadPendingRow;
|
|
338
377
|
}
|
|
339
378
|
|
|
340
379
|
/**
|
|
@@ -356,6 +395,12 @@ interface AIServicePluginOptions {
|
|
|
356
395
|
models?: AI.ModelConfig[];
|
|
357
396
|
/** Default model id (must appear in `models`). */
|
|
358
397
|
defaultModelId?: string;
|
|
398
|
+
/**
|
|
399
|
+
* Explicit trace recorder override. When set, auto-detection
|
|
400
|
+
* of {@link ObjectQLTraceRecorder} is skipped.
|
|
401
|
+
*
|
|
402
|
+
* Set to `null` to disable tracing entirely.
|
|
403
|
+
*/
|
|
359
404
|
/**
|
|
360
405
|
* Explicit trace recorder override. When set, auto-detection
|
|
361
406
|
* of {@link ObjectQLTraceRecorder} is skipped.
|
|
@@ -363,6 +408,31 @@ interface AIServicePluginOptions {
|
|
|
363
408
|
* Set to `null` to disable tracing entirely.
|
|
364
409
|
*/
|
|
365
410
|
traceRecorder?: TraceRecorder | null;
|
|
411
|
+
/**
|
|
412
|
+
* Base URL prepended to relative `target` paths for `type:'api'`
|
|
413
|
+
* actions invoked by the AI tool runtime. When unset, falls back to
|
|
414
|
+
* `process.env.OS_AI_ACTION_API_BASE_URL`. If neither is set, api
|
|
415
|
+
* actions are skipped at registration with a clear reason.
|
|
416
|
+
*/
|
|
417
|
+
apiActionBaseUrl?: string;
|
|
418
|
+
/**
|
|
419
|
+
* Extra HTTP headers (e.g. `{ Authorization: 'Bearer ...' }`) applied
|
|
420
|
+
* to every `type:'api'` action dispatch. Useful for forwarding the
|
|
421
|
+
* caller's session token so server-side authorization still applies.
|
|
422
|
+
*/
|
|
423
|
+
apiActionHeaders?: Record<string, string>;
|
|
424
|
+
/**
|
|
425
|
+
* Opt into Human-In-The-Loop approval for dangerous actions exposed
|
|
426
|
+
* as AI tools. When `true`, actions with `confirmText`, `mode:'delete'`,
|
|
427
|
+
* or `variant:'danger'` are still registered as tools — but invoking
|
|
428
|
+
* them enqueues an `ai_pending_actions` row and returns
|
|
429
|
+
* `{ status: 'pending_approval' }` instead of running. A human
|
|
430
|
+
* operator approves via Studio's pending-actions inbox to execute.
|
|
431
|
+
*
|
|
432
|
+
* Defaults to `false` (safer: dangerous actions stay invisible to LLM
|
|
433
|
+
* until an operator explicitly enables this routing).
|
|
434
|
+
*/
|
|
435
|
+
enableActionApproval?: boolean;
|
|
366
436
|
}
|
|
367
437
|
/**
|
|
368
438
|
* AIServicePlugin — Kernel plugin for the unified AI capability service.
|
|
@@ -988,6 +1058,16 @@ interface ObjectDef {
|
|
|
988
1058
|
* subject record when a `recordIdParam` is configured and (b) dispatch
|
|
989
1059
|
* to the registered handler via `executeAction`.
|
|
990
1060
|
*
|
|
1061
|
+
* `automation` enables `type:'flow'` actions to dispatch into the
|
|
1062
|
+
* automation service's flow runner. When omitted, flow actions are
|
|
1063
|
+
* skipped at registration time with a clear reason.
|
|
1064
|
+
*
|
|
1065
|
+
* `apiClient` (or `apiBaseUrl`) enables `type:'api'` actions to perform
|
|
1066
|
+
* an HTTP call to the action's `target` URL. The default client uses
|
|
1067
|
+
* the global `fetch` and prepends `apiBaseUrl` to relative `target`s.
|
|
1068
|
+
* Supply a custom client when you need bespoke auth, in-process
|
|
1069
|
+
* routing, or stubbing in tests.
|
|
1070
|
+
*
|
|
991
1071
|
* `principal` lets callers attribute AI-initiated mutations to a known
|
|
992
1072
|
* user id; it defaults to a synthetic `'ai_agent'` user so traces /
|
|
993
1073
|
* audit always have *some* actor.
|
|
@@ -995,6 +1075,14 @@ interface ObjectDef {
|
|
|
995
1075
|
interface ActionToolsContext {
|
|
996
1076
|
metadata: IMetadataService;
|
|
997
1077
|
dataEngine: IDataEngine;
|
|
1078
|
+
/** Automation service for `type:'flow'` action dispatch. Optional. */
|
|
1079
|
+
automation?: IAutomationService;
|
|
1080
|
+
/** Custom API client for `type:'api'` actions. Defaults to a fetch-based client. */
|
|
1081
|
+
apiClient?: ApiActionClient;
|
|
1082
|
+
/** Base URL prepended to relative `target` paths for `type:'api'` actions. */
|
|
1083
|
+
apiBaseUrl?: string;
|
|
1084
|
+
/** Extra HTTP headers (e.g. auth bearer) applied to every `type:'api'` call. */
|
|
1085
|
+
apiHeaders?: Record<string, string>;
|
|
998
1086
|
/** Synthetic user attribution for AI-initiated calls. */
|
|
999
1087
|
principal?: {
|
|
1000
1088
|
id: string;
|
|
@@ -1002,14 +1090,57 @@ interface ActionToolsContext {
|
|
|
1002
1090
|
};
|
|
1003
1091
|
/** Tool-name prefix (default: `action_`). Keeps namespace separate from data tools. */
|
|
1004
1092
|
toolPrefix?: string;
|
|
1093
|
+
/**
|
|
1094
|
+
* AI service used to enqueue HITL approvals for dangerous actions.
|
|
1095
|
+
* When supplied together with `enableActionApproval: true`, actions
|
|
1096
|
+
* that would otherwise be skipped on safety grounds (`confirmText`,
|
|
1097
|
+
* `mode:'delete'`, `variant:'danger'`) are registered as tools whose
|
|
1098
|
+
* handler proposes a pending action and returns
|
|
1099
|
+
* `{ status: 'pending_approval' }` instead of executing.
|
|
1100
|
+
*/
|
|
1101
|
+
aiService?: {
|
|
1102
|
+
proposePendingAction?: (input: {
|
|
1103
|
+
objectName: string;
|
|
1104
|
+
actionName: string;
|
|
1105
|
+
toolName: string;
|
|
1106
|
+
toolInput: Record<string, unknown>;
|
|
1107
|
+
conversationId?: string;
|
|
1108
|
+
messageId?: string;
|
|
1109
|
+
proposedBy?: string;
|
|
1110
|
+
}) => Promise<{
|
|
1111
|
+
id: string;
|
|
1112
|
+
}>;
|
|
1113
|
+
registerPendingActionDispatcher?: (toolName: string, dispatch: (input: Record<string, unknown>) => Promise<unknown>) => void;
|
|
1114
|
+
};
|
|
1115
|
+
/**
|
|
1116
|
+
* Opt into the HITL approval queue for dangerous actions. Default
|
|
1117
|
+
* is `false` (safer: dangerous actions stay invisible to the LLM
|
|
1118
|
+
* until an operator explicitly enables approval routing).
|
|
1119
|
+
*/
|
|
1120
|
+
enableActionApproval?: boolean;
|
|
1005
1121
|
}
|
|
1006
1122
|
/**
|
|
1007
|
-
*
|
|
1123
|
+
* Minimal HTTP client shape used by `type:'api'` action dispatch.
|
|
1008
1124
|
*
|
|
1009
|
-
*
|
|
1010
|
-
*
|
|
1125
|
+
* Implementations are expected to return a JSON-deserialised body (or
|
|
1126
|
+
* `null` for empty responses) on 2xx, and throw on non-2xx so the tool
|
|
1127
|
+
* surfaces the failure to the LLM as a tool error.
|
|
1011
1128
|
*/
|
|
1012
|
-
|
|
1129
|
+
interface ApiActionClient {
|
|
1130
|
+
request(input: {
|
|
1131
|
+
url: string;
|
|
1132
|
+
method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
|
|
1133
|
+
body?: Record<string, unknown>;
|
|
1134
|
+
headers?: Record<string, string>;
|
|
1135
|
+
}): Promise<unknown>;
|
|
1136
|
+
}
|
|
1137
|
+
declare function actionSkipReason(action: Action, ctx?: {
|
|
1138
|
+
automation?: IAutomationService;
|
|
1139
|
+
apiClient?: ApiActionClient;
|
|
1140
|
+
apiBaseUrl?: string;
|
|
1141
|
+
enableActionApproval?: boolean;
|
|
1142
|
+
aiService?: ActionToolsContext['aiService'];
|
|
1143
|
+
}): string | null;
|
|
1013
1144
|
/** Compute the AI tool name for a given action (prefixed for namespacing). */
|
|
1014
1145
|
declare function actionToolName(action: Action, prefix?: string): string;
|
|
1015
1146
|
/**
|
|
@@ -1433,7 +1564,7 @@ declare const AiConversationObject: Omit<{
|
|
|
1433
1564
|
abstract: boolean;
|
|
1434
1565
|
datasource: string;
|
|
1435
1566
|
fields: Record<string, {
|
|
1436
|
-
type: "number" | "boolean" | "file" | "text" | "json" | "tags" | "currency" | "code" | "
|
|
1567
|
+
type: "number" | "boolean" | "file" | "text" | "json" | "tags" | "currency" | "code" | "date" | "avatar" | "vector" | "datetime" | "signature" | "progress" | "url" | "textarea" | "email" | "phone" | "password" | "markdown" | "html" | "richtext" | "percent" | "time" | "toggle" | "select" | "multiselect" | "radio" | "checkboxes" | "lookup" | "master_detail" | "tree" | "image" | "video" | "audio" | "formula" | "summary" | "autonumber" | "location" | "address" | "color" | "rating" | "slider" | "qrcode";
|
|
1437
1568
|
required: boolean;
|
|
1438
1569
|
searchable: boolean;
|
|
1439
1570
|
multiple: boolean;
|
|
@@ -1572,7 +1703,7 @@ declare const AiConversationObject: Omit<{
|
|
|
1572
1703
|
autoRotate: boolean;
|
|
1573
1704
|
} | undefined;
|
|
1574
1705
|
};
|
|
1575
|
-
scope: "
|
|
1706
|
+
scope: "record" | "field" | "database" | "table";
|
|
1576
1707
|
deterministicEncryption: boolean;
|
|
1577
1708
|
searchableEncryption: boolean;
|
|
1578
1709
|
} | undefined;
|
|
@@ -2005,7 +2136,7 @@ declare const AiConversationObject: Omit<{
|
|
|
2005
2136
|
trash: boolean;
|
|
2006
2137
|
mru: boolean;
|
|
2007
2138
|
clone: boolean;
|
|
2008
|
-
apiMethods?: ("restore" | "export" | "import" | "
|
|
2139
|
+
apiMethods?: ("restore" | "export" | "import" | "delete" | "purge" | "upsert" | "search" | "create" | "list" | "get" | "update" | "history" | "bulk" | "aggregate")[] | undefined;
|
|
2009
2140
|
} | undefined;
|
|
2010
2141
|
recordTypes?: string[] | undefined;
|
|
2011
2142
|
sharingModel?: "private" | "read" | "full" | "read_write" | undefined;
|
|
@@ -3358,7 +3489,7 @@ declare const AiMessageObject: Omit<{
|
|
|
3358
3489
|
abstract: boolean;
|
|
3359
3490
|
datasource: string;
|
|
3360
3491
|
fields: Record<string, {
|
|
3361
|
-
type: "number" | "boolean" | "file" | "text" | "json" | "tags" | "currency" | "code" | "
|
|
3492
|
+
type: "number" | "boolean" | "file" | "text" | "json" | "tags" | "currency" | "code" | "date" | "avatar" | "vector" | "datetime" | "signature" | "progress" | "url" | "textarea" | "email" | "phone" | "password" | "markdown" | "html" | "richtext" | "percent" | "time" | "toggle" | "select" | "multiselect" | "radio" | "checkboxes" | "lookup" | "master_detail" | "tree" | "image" | "video" | "audio" | "formula" | "summary" | "autonumber" | "location" | "address" | "color" | "rating" | "slider" | "qrcode";
|
|
3362
3493
|
required: boolean;
|
|
3363
3494
|
searchable: boolean;
|
|
3364
3495
|
multiple: boolean;
|
|
@@ -3497,7 +3628,7 @@ declare const AiMessageObject: Omit<{
|
|
|
3497
3628
|
autoRotate: boolean;
|
|
3498
3629
|
} | undefined;
|
|
3499
3630
|
};
|
|
3500
|
-
scope: "
|
|
3631
|
+
scope: "record" | "field" | "database" | "table";
|
|
3501
3632
|
deterministicEncryption: boolean;
|
|
3502
3633
|
searchableEncryption: boolean;
|
|
3503
3634
|
} | undefined;
|
|
@@ -3930,7 +4061,7 @@ declare const AiMessageObject: Omit<{
|
|
|
3930
4061
|
trash: boolean;
|
|
3931
4062
|
mru: boolean;
|
|
3932
4063
|
clone: boolean;
|
|
3933
|
-
apiMethods?: ("restore" | "export" | "import" | "
|
|
4064
|
+
apiMethods?: ("restore" | "export" | "import" | "delete" | "purge" | "upsert" | "search" | "create" | "list" | "get" | "update" | "history" | "bulk" | "aggregate")[] | undefined;
|
|
3934
4065
|
} | undefined;
|
|
3935
4066
|
recordTypes?: string[] | undefined;
|
|
3936
4067
|
sharingModel?: "private" | "read" | "full" | "read_write" | undefined;
|
|
@@ -5285,7 +5416,7 @@ declare const AiTraceObject: Omit<{
|
|
|
5285
5416
|
abstract: boolean;
|
|
5286
5417
|
datasource: string;
|
|
5287
5418
|
fields: Record<string, {
|
|
5288
|
-
type: "number" | "boolean" | "file" | "text" | "json" | "tags" | "currency" | "code" | "
|
|
5419
|
+
type: "number" | "boolean" | "file" | "text" | "json" | "tags" | "currency" | "code" | "date" | "avatar" | "vector" | "datetime" | "signature" | "progress" | "url" | "textarea" | "email" | "phone" | "password" | "markdown" | "html" | "richtext" | "percent" | "time" | "toggle" | "select" | "multiselect" | "radio" | "checkboxes" | "lookup" | "master_detail" | "tree" | "image" | "video" | "audio" | "formula" | "summary" | "autonumber" | "location" | "address" | "color" | "rating" | "slider" | "qrcode";
|
|
5289
5420
|
required: boolean;
|
|
5290
5421
|
searchable: boolean;
|
|
5291
5422
|
multiple: boolean;
|
|
@@ -5424,7 +5555,7 @@ declare const AiTraceObject: Omit<{
|
|
|
5424
5555
|
autoRotate: boolean;
|
|
5425
5556
|
} | undefined;
|
|
5426
5557
|
};
|
|
5427
|
-
scope: "
|
|
5558
|
+
scope: "record" | "field" | "database" | "table";
|
|
5428
5559
|
deterministicEncryption: boolean;
|
|
5429
5560
|
searchableEncryption: boolean;
|
|
5430
5561
|
} | undefined;
|
|
@@ -5857,7 +5988,7 @@ declare const AiTraceObject: Omit<{
|
|
|
5857
5988
|
trash: boolean;
|
|
5858
5989
|
mru: boolean;
|
|
5859
5990
|
clone: boolean;
|
|
5860
|
-
apiMethods?: ("restore" | "export" | "import" | "
|
|
5991
|
+
apiMethods?: ("restore" | "export" | "import" | "delete" | "purge" | "upsert" | "search" | "create" | "list" | "get" | "update" | "history" | "bulk" | "aggregate")[] | undefined;
|
|
5861
5992
|
} | undefined;
|
|
5862
5993
|
recordTypes?: string[] | undefined;
|
|
5863
5994
|
sharingModel?: "private" | "read" | "full" | "read_write" | undefined;
|
|
@@ -9970,19 +10101,26 @@ interface QueryDataToolContext {
|
|
|
9970
10101
|
* Kept small and strict — every property is documented so providers like
|
|
9971
10102
|
* OpenAI Structured Outputs and Anthropic Tool Use can render high-quality
|
|
9972
10103
|
* prompts from the schema metadata.
|
|
10104
|
+
*
|
|
10105
|
+
* NOTE: `where` is intentionally typed as a JSON string rather than a free-form
|
|
10106
|
+
* record. OpenAI's Structured Outputs surface rejects `propertyNames`
|
|
10107
|
+
* (which Zod's `z.record(z.string(), ...)` emits), and Anthropic's tool-use
|
|
10108
|
+
* surface dislikes open-ended object schemas without `additionalProperties`.
|
|
10109
|
+
* Having the model emit a JSON-encoded filter sidesteps both restrictions and
|
|
10110
|
+
* keeps the tool portable across providers.
|
|
9973
10111
|
*/
|
|
9974
10112
|
declare const QueryPlanSchema: z.ZodObject<{
|
|
9975
10113
|
objectName: z.ZodString;
|
|
9976
|
-
|
|
9977
|
-
fields: z.
|
|
9978
|
-
orderBy: z.
|
|
10114
|
+
whereJson: z.ZodNullable<z.ZodString>;
|
|
10115
|
+
fields: z.ZodNullable<z.ZodArray<z.ZodString>>;
|
|
10116
|
+
orderBy: z.ZodNullable<z.ZodArray<z.ZodObject<{
|
|
9979
10117
|
field: z.ZodString;
|
|
9980
10118
|
order: z.ZodEnum<{
|
|
9981
10119
|
asc: "asc";
|
|
9982
10120
|
desc: "desc";
|
|
9983
10121
|
}>;
|
|
9984
10122
|
}, z.core.$strip>>>;
|
|
9985
|
-
limit: z.
|
|
10123
|
+
limit: z.ZodNullable<z.ZodNumber>;
|
|
9986
10124
|
}, z.core.$strip>;
|
|
9987
10125
|
/** Strongly-typed query plan inferred from the LLM. */
|
|
9988
10126
|
type QueryPlan = z.infer<typeof QueryPlanSchema>;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AIToolDefinition, ToolCallPart, ToolResultPart, IDataEngine, Logger, IAIService, IAIConversationService, LLMAdapter, ModelMessage, AIRequestOptions, AIResult, GenerateObjectOptions, AIObjectResult, TextStreamPart, ToolSet, ChatWithToolsOptions, AIConversation, IMetadataService } from '@objectstack/spec/contracts';
|
|
1
|
+
import { AIToolDefinition, ToolCallPart, ToolResultPart, IDataEngine, Logger, IAIService, IAIConversationService, LLMAdapter, ModelMessage, AIRequestOptions, AIResult, GenerateObjectOptions, AIObjectResult, TextStreamPart, ToolSet, ChatWithToolsOptions, ProposePendingActionInput, PendingActionStatus, PendingActionRow, AIConversation, IMetadataService, IAutomationService } from '@objectstack/spec/contracts';
|
|
2
2
|
export { LLMAdapter } from '@objectstack/spec/contracts';
|
|
3
3
|
import { z } from 'zod';
|
|
4
4
|
import * as AI from '@objectstack/spec/ai';
|
|
@@ -258,6 +258,13 @@ interface AIServiceConfig {
|
|
|
258
258
|
modelRegistry?: ModelRegistry;
|
|
259
259
|
/** Trace recorder for per-call observability. Defaults to no-op. */
|
|
260
260
|
traceRecorder?: TraceRecorder;
|
|
261
|
+
/**
|
|
262
|
+
* Data engine used to persist `ai_pending_actions` rows for the
|
|
263
|
+
* actions-as-tools HITL queue. Optional — when omitted, the
|
|
264
|
+
* `proposePendingAction` / `approvePendingAction` methods throw if
|
|
265
|
+
* called. Wired by `AIServicePlugin` after the data driver is up.
|
|
266
|
+
*/
|
|
267
|
+
dataEngine?: IDataEngine;
|
|
261
268
|
}
|
|
262
269
|
/**
|
|
263
270
|
* AIService — Unified AI capability service.
|
|
@@ -281,6 +288,15 @@ declare class AIService implements IAIService {
|
|
|
281
288
|
readonly conversationService: IAIConversationService;
|
|
282
289
|
readonly modelRegistry?: ModelRegistry;
|
|
283
290
|
readonly traceRecorder: TraceRecorder;
|
|
291
|
+
/**
|
|
292
|
+
* Map of tool-name → dispatcher used to re-run an approved pending
|
|
293
|
+
* action. Populated by `registerActionsAsTools()` when action
|
|
294
|
+
* approval is enabled. Kept private because callers should go
|
|
295
|
+
* through `approvePendingAction()`.
|
|
296
|
+
*/
|
|
297
|
+
private readonly pendingDispatchers;
|
|
298
|
+
/** Data engine for `ai_pending_actions` persistence. */
|
|
299
|
+
private readonly dataEngine?;
|
|
284
300
|
constructor(config?: AIServiceConfig);
|
|
285
301
|
/** The name of the active LLM adapter. */
|
|
286
302
|
get adapterName(): string;
|
|
@@ -335,6 +351,29 @@ declare class AIService implements IAIService {
|
|
|
335
351
|
* fed back until a final text stream is produced.
|
|
336
352
|
*/
|
|
337
353
|
streamChatWithTools(messages: ModelMessage[], options?: ChatWithToolsOptions): AsyncIterable<TextStreamPart<ToolSet>>;
|
|
354
|
+
/**
|
|
355
|
+
* Register a dispatcher callback for a tool. Called by
|
|
356
|
+
* `registerActionsAsTools()` when action approval is enabled so the
|
|
357
|
+
* approval handler can re-run the exact same code path the LLM
|
|
358
|
+
* would have triggered.
|
|
359
|
+
*/
|
|
360
|
+
registerPendingActionDispatcher(toolName: string, dispatch: (input: Record<string, unknown>) => Promise<unknown>): void;
|
|
361
|
+
proposePendingAction(input: ProposePendingActionInput): Promise<{
|
|
362
|
+
id: string;
|
|
363
|
+
}>;
|
|
364
|
+
approvePendingAction(id: string, actorId: string): Promise<{
|
|
365
|
+
status: 'executed' | 'failed';
|
|
366
|
+
result?: unknown;
|
|
367
|
+
error?: string;
|
|
368
|
+
}>;
|
|
369
|
+
rejectPendingAction(id: string, actorId: string, reason?: string): Promise<void>;
|
|
370
|
+
listPendingActions(filter?: {
|
|
371
|
+
status?: PendingActionStatus | PendingActionStatus[];
|
|
372
|
+
conversationId?: string;
|
|
373
|
+
objectName?: string;
|
|
374
|
+
limit?: number;
|
|
375
|
+
}): Promise<PendingActionRow[]>;
|
|
376
|
+
private loadPendingRow;
|
|
338
377
|
}
|
|
339
378
|
|
|
340
379
|
/**
|
|
@@ -356,6 +395,12 @@ interface AIServicePluginOptions {
|
|
|
356
395
|
models?: AI.ModelConfig[];
|
|
357
396
|
/** Default model id (must appear in `models`). */
|
|
358
397
|
defaultModelId?: string;
|
|
398
|
+
/**
|
|
399
|
+
* Explicit trace recorder override. When set, auto-detection
|
|
400
|
+
* of {@link ObjectQLTraceRecorder} is skipped.
|
|
401
|
+
*
|
|
402
|
+
* Set to `null` to disable tracing entirely.
|
|
403
|
+
*/
|
|
359
404
|
/**
|
|
360
405
|
* Explicit trace recorder override. When set, auto-detection
|
|
361
406
|
* of {@link ObjectQLTraceRecorder} is skipped.
|
|
@@ -363,6 +408,31 @@ interface AIServicePluginOptions {
|
|
|
363
408
|
* Set to `null` to disable tracing entirely.
|
|
364
409
|
*/
|
|
365
410
|
traceRecorder?: TraceRecorder | null;
|
|
411
|
+
/**
|
|
412
|
+
* Base URL prepended to relative `target` paths for `type:'api'`
|
|
413
|
+
* actions invoked by the AI tool runtime. When unset, falls back to
|
|
414
|
+
* `process.env.OS_AI_ACTION_API_BASE_URL`. If neither is set, api
|
|
415
|
+
* actions are skipped at registration with a clear reason.
|
|
416
|
+
*/
|
|
417
|
+
apiActionBaseUrl?: string;
|
|
418
|
+
/**
|
|
419
|
+
* Extra HTTP headers (e.g. `{ Authorization: 'Bearer ...' }`) applied
|
|
420
|
+
* to every `type:'api'` action dispatch. Useful for forwarding the
|
|
421
|
+
* caller's session token so server-side authorization still applies.
|
|
422
|
+
*/
|
|
423
|
+
apiActionHeaders?: Record<string, string>;
|
|
424
|
+
/**
|
|
425
|
+
* Opt into Human-In-The-Loop approval for dangerous actions exposed
|
|
426
|
+
* as AI tools. When `true`, actions with `confirmText`, `mode:'delete'`,
|
|
427
|
+
* or `variant:'danger'` are still registered as tools — but invoking
|
|
428
|
+
* them enqueues an `ai_pending_actions` row and returns
|
|
429
|
+
* `{ status: 'pending_approval' }` instead of running. A human
|
|
430
|
+
* operator approves via Studio's pending-actions inbox to execute.
|
|
431
|
+
*
|
|
432
|
+
* Defaults to `false` (safer: dangerous actions stay invisible to LLM
|
|
433
|
+
* until an operator explicitly enables this routing).
|
|
434
|
+
*/
|
|
435
|
+
enableActionApproval?: boolean;
|
|
366
436
|
}
|
|
367
437
|
/**
|
|
368
438
|
* AIServicePlugin — Kernel plugin for the unified AI capability service.
|
|
@@ -988,6 +1058,16 @@ interface ObjectDef {
|
|
|
988
1058
|
* subject record when a `recordIdParam` is configured and (b) dispatch
|
|
989
1059
|
* to the registered handler via `executeAction`.
|
|
990
1060
|
*
|
|
1061
|
+
* `automation` enables `type:'flow'` actions to dispatch into the
|
|
1062
|
+
* automation service's flow runner. When omitted, flow actions are
|
|
1063
|
+
* skipped at registration time with a clear reason.
|
|
1064
|
+
*
|
|
1065
|
+
* `apiClient` (or `apiBaseUrl`) enables `type:'api'` actions to perform
|
|
1066
|
+
* an HTTP call to the action's `target` URL. The default client uses
|
|
1067
|
+
* the global `fetch` and prepends `apiBaseUrl` to relative `target`s.
|
|
1068
|
+
* Supply a custom client when you need bespoke auth, in-process
|
|
1069
|
+
* routing, or stubbing in tests.
|
|
1070
|
+
*
|
|
991
1071
|
* `principal` lets callers attribute AI-initiated mutations to a known
|
|
992
1072
|
* user id; it defaults to a synthetic `'ai_agent'` user so traces /
|
|
993
1073
|
* audit always have *some* actor.
|
|
@@ -995,6 +1075,14 @@ interface ObjectDef {
|
|
|
995
1075
|
interface ActionToolsContext {
|
|
996
1076
|
metadata: IMetadataService;
|
|
997
1077
|
dataEngine: IDataEngine;
|
|
1078
|
+
/** Automation service for `type:'flow'` action dispatch. Optional. */
|
|
1079
|
+
automation?: IAutomationService;
|
|
1080
|
+
/** Custom API client for `type:'api'` actions. Defaults to a fetch-based client. */
|
|
1081
|
+
apiClient?: ApiActionClient;
|
|
1082
|
+
/** Base URL prepended to relative `target` paths for `type:'api'` actions. */
|
|
1083
|
+
apiBaseUrl?: string;
|
|
1084
|
+
/** Extra HTTP headers (e.g. auth bearer) applied to every `type:'api'` call. */
|
|
1085
|
+
apiHeaders?: Record<string, string>;
|
|
998
1086
|
/** Synthetic user attribution for AI-initiated calls. */
|
|
999
1087
|
principal?: {
|
|
1000
1088
|
id: string;
|
|
@@ -1002,14 +1090,57 @@ interface ActionToolsContext {
|
|
|
1002
1090
|
};
|
|
1003
1091
|
/** Tool-name prefix (default: `action_`). Keeps namespace separate from data tools. */
|
|
1004
1092
|
toolPrefix?: string;
|
|
1093
|
+
/**
|
|
1094
|
+
* AI service used to enqueue HITL approvals for dangerous actions.
|
|
1095
|
+
* When supplied together with `enableActionApproval: true`, actions
|
|
1096
|
+
* that would otherwise be skipped on safety grounds (`confirmText`,
|
|
1097
|
+
* `mode:'delete'`, `variant:'danger'`) are registered as tools whose
|
|
1098
|
+
* handler proposes a pending action and returns
|
|
1099
|
+
* `{ status: 'pending_approval' }` instead of executing.
|
|
1100
|
+
*/
|
|
1101
|
+
aiService?: {
|
|
1102
|
+
proposePendingAction?: (input: {
|
|
1103
|
+
objectName: string;
|
|
1104
|
+
actionName: string;
|
|
1105
|
+
toolName: string;
|
|
1106
|
+
toolInput: Record<string, unknown>;
|
|
1107
|
+
conversationId?: string;
|
|
1108
|
+
messageId?: string;
|
|
1109
|
+
proposedBy?: string;
|
|
1110
|
+
}) => Promise<{
|
|
1111
|
+
id: string;
|
|
1112
|
+
}>;
|
|
1113
|
+
registerPendingActionDispatcher?: (toolName: string, dispatch: (input: Record<string, unknown>) => Promise<unknown>) => void;
|
|
1114
|
+
};
|
|
1115
|
+
/**
|
|
1116
|
+
* Opt into the HITL approval queue for dangerous actions. Default
|
|
1117
|
+
* is `false` (safer: dangerous actions stay invisible to the LLM
|
|
1118
|
+
* until an operator explicitly enables approval routing).
|
|
1119
|
+
*/
|
|
1120
|
+
enableActionApproval?: boolean;
|
|
1005
1121
|
}
|
|
1006
1122
|
/**
|
|
1007
|
-
*
|
|
1123
|
+
* Minimal HTTP client shape used by `type:'api'` action dispatch.
|
|
1008
1124
|
*
|
|
1009
|
-
*
|
|
1010
|
-
*
|
|
1125
|
+
* Implementations are expected to return a JSON-deserialised body (or
|
|
1126
|
+
* `null` for empty responses) on 2xx, and throw on non-2xx so the tool
|
|
1127
|
+
* surfaces the failure to the LLM as a tool error.
|
|
1011
1128
|
*/
|
|
1012
|
-
|
|
1129
|
+
interface ApiActionClient {
|
|
1130
|
+
request(input: {
|
|
1131
|
+
url: string;
|
|
1132
|
+
method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
|
|
1133
|
+
body?: Record<string, unknown>;
|
|
1134
|
+
headers?: Record<string, string>;
|
|
1135
|
+
}): Promise<unknown>;
|
|
1136
|
+
}
|
|
1137
|
+
declare function actionSkipReason(action: Action, ctx?: {
|
|
1138
|
+
automation?: IAutomationService;
|
|
1139
|
+
apiClient?: ApiActionClient;
|
|
1140
|
+
apiBaseUrl?: string;
|
|
1141
|
+
enableActionApproval?: boolean;
|
|
1142
|
+
aiService?: ActionToolsContext['aiService'];
|
|
1143
|
+
}): string | null;
|
|
1013
1144
|
/** Compute the AI tool name for a given action (prefixed for namespacing). */
|
|
1014
1145
|
declare function actionToolName(action: Action, prefix?: string): string;
|
|
1015
1146
|
/**
|
|
@@ -1433,7 +1564,7 @@ declare const AiConversationObject: Omit<{
|
|
|
1433
1564
|
abstract: boolean;
|
|
1434
1565
|
datasource: string;
|
|
1435
1566
|
fields: Record<string, {
|
|
1436
|
-
type: "number" | "boolean" | "file" | "text" | "json" | "tags" | "currency" | "code" | "
|
|
1567
|
+
type: "number" | "boolean" | "file" | "text" | "json" | "tags" | "currency" | "code" | "date" | "avatar" | "vector" | "datetime" | "signature" | "progress" | "url" | "textarea" | "email" | "phone" | "password" | "markdown" | "html" | "richtext" | "percent" | "time" | "toggle" | "select" | "multiselect" | "radio" | "checkboxes" | "lookup" | "master_detail" | "tree" | "image" | "video" | "audio" | "formula" | "summary" | "autonumber" | "location" | "address" | "color" | "rating" | "slider" | "qrcode";
|
|
1437
1568
|
required: boolean;
|
|
1438
1569
|
searchable: boolean;
|
|
1439
1570
|
multiple: boolean;
|
|
@@ -1572,7 +1703,7 @@ declare const AiConversationObject: Omit<{
|
|
|
1572
1703
|
autoRotate: boolean;
|
|
1573
1704
|
} | undefined;
|
|
1574
1705
|
};
|
|
1575
|
-
scope: "
|
|
1706
|
+
scope: "record" | "field" | "database" | "table";
|
|
1576
1707
|
deterministicEncryption: boolean;
|
|
1577
1708
|
searchableEncryption: boolean;
|
|
1578
1709
|
} | undefined;
|
|
@@ -2005,7 +2136,7 @@ declare const AiConversationObject: Omit<{
|
|
|
2005
2136
|
trash: boolean;
|
|
2006
2137
|
mru: boolean;
|
|
2007
2138
|
clone: boolean;
|
|
2008
|
-
apiMethods?: ("restore" | "export" | "import" | "
|
|
2139
|
+
apiMethods?: ("restore" | "export" | "import" | "delete" | "purge" | "upsert" | "search" | "create" | "list" | "get" | "update" | "history" | "bulk" | "aggregate")[] | undefined;
|
|
2009
2140
|
} | undefined;
|
|
2010
2141
|
recordTypes?: string[] | undefined;
|
|
2011
2142
|
sharingModel?: "private" | "read" | "full" | "read_write" | undefined;
|
|
@@ -3358,7 +3489,7 @@ declare const AiMessageObject: Omit<{
|
|
|
3358
3489
|
abstract: boolean;
|
|
3359
3490
|
datasource: string;
|
|
3360
3491
|
fields: Record<string, {
|
|
3361
|
-
type: "number" | "boolean" | "file" | "text" | "json" | "tags" | "currency" | "code" | "
|
|
3492
|
+
type: "number" | "boolean" | "file" | "text" | "json" | "tags" | "currency" | "code" | "date" | "avatar" | "vector" | "datetime" | "signature" | "progress" | "url" | "textarea" | "email" | "phone" | "password" | "markdown" | "html" | "richtext" | "percent" | "time" | "toggle" | "select" | "multiselect" | "radio" | "checkboxes" | "lookup" | "master_detail" | "tree" | "image" | "video" | "audio" | "formula" | "summary" | "autonumber" | "location" | "address" | "color" | "rating" | "slider" | "qrcode";
|
|
3362
3493
|
required: boolean;
|
|
3363
3494
|
searchable: boolean;
|
|
3364
3495
|
multiple: boolean;
|
|
@@ -3497,7 +3628,7 @@ declare const AiMessageObject: Omit<{
|
|
|
3497
3628
|
autoRotate: boolean;
|
|
3498
3629
|
} | undefined;
|
|
3499
3630
|
};
|
|
3500
|
-
scope: "
|
|
3631
|
+
scope: "record" | "field" | "database" | "table";
|
|
3501
3632
|
deterministicEncryption: boolean;
|
|
3502
3633
|
searchableEncryption: boolean;
|
|
3503
3634
|
} | undefined;
|
|
@@ -3930,7 +4061,7 @@ declare const AiMessageObject: Omit<{
|
|
|
3930
4061
|
trash: boolean;
|
|
3931
4062
|
mru: boolean;
|
|
3932
4063
|
clone: boolean;
|
|
3933
|
-
apiMethods?: ("restore" | "export" | "import" | "
|
|
4064
|
+
apiMethods?: ("restore" | "export" | "import" | "delete" | "purge" | "upsert" | "search" | "create" | "list" | "get" | "update" | "history" | "bulk" | "aggregate")[] | undefined;
|
|
3934
4065
|
} | undefined;
|
|
3935
4066
|
recordTypes?: string[] | undefined;
|
|
3936
4067
|
sharingModel?: "private" | "read" | "full" | "read_write" | undefined;
|
|
@@ -5285,7 +5416,7 @@ declare const AiTraceObject: Omit<{
|
|
|
5285
5416
|
abstract: boolean;
|
|
5286
5417
|
datasource: string;
|
|
5287
5418
|
fields: Record<string, {
|
|
5288
|
-
type: "number" | "boolean" | "file" | "text" | "json" | "tags" | "currency" | "code" | "
|
|
5419
|
+
type: "number" | "boolean" | "file" | "text" | "json" | "tags" | "currency" | "code" | "date" | "avatar" | "vector" | "datetime" | "signature" | "progress" | "url" | "textarea" | "email" | "phone" | "password" | "markdown" | "html" | "richtext" | "percent" | "time" | "toggle" | "select" | "multiselect" | "radio" | "checkboxes" | "lookup" | "master_detail" | "tree" | "image" | "video" | "audio" | "formula" | "summary" | "autonumber" | "location" | "address" | "color" | "rating" | "slider" | "qrcode";
|
|
5289
5420
|
required: boolean;
|
|
5290
5421
|
searchable: boolean;
|
|
5291
5422
|
multiple: boolean;
|
|
@@ -5424,7 +5555,7 @@ declare const AiTraceObject: Omit<{
|
|
|
5424
5555
|
autoRotate: boolean;
|
|
5425
5556
|
} | undefined;
|
|
5426
5557
|
};
|
|
5427
|
-
scope: "
|
|
5558
|
+
scope: "record" | "field" | "database" | "table";
|
|
5428
5559
|
deterministicEncryption: boolean;
|
|
5429
5560
|
searchableEncryption: boolean;
|
|
5430
5561
|
} | undefined;
|
|
@@ -5857,7 +5988,7 @@ declare const AiTraceObject: Omit<{
|
|
|
5857
5988
|
trash: boolean;
|
|
5858
5989
|
mru: boolean;
|
|
5859
5990
|
clone: boolean;
|
|
5860
|
-
apiMethods?: ("restore" | "export" | "import" | "
|
|
5991
|
+
apiMethods?: ("restore" | "export" | "import" | "delete" | "purge" | "upsert" | "search" | "create" | "list" | "get" | "update" | "history" | "bulk" | "aggregate")[] | undefined;
|
|
5861
5992
|
} | undefined;
|
|
5862
5993
|
recordTypes?: string[] | undefined;
|
|
5863
5994
|
sharingModel?: "private" | "read" | "full" | "read_write" | undefined;
|
|
@@ -9970,19 +10101,26 @@ interface QueryDataToolContext {
|
|
|
9970
10101
|
* Kept small and strict — every property is documented so providers like
|
|
9971
10102
|
* OpenAI Structured Outputs and Anthropic Tool Use can render high-quality
|
|
9972
10103
|
* prompts from the schema metadata.
|
|
10104
|
+
*
|
|
10105
|
+
* NOTE: `where` is intentionally typed as a JSON string rather than a free-form
|
|
10106
|
+
* record. OpenAI's Structured Outputs surface rejects `propertyNames`
|
|
10107
|
+
* (which Zod's `z.record(z.string(), ...)` emits), and Anthropic's tool-use
|
|
10108
|
+
* surface dislikes open-ended object schemas without `additionalProperties`.
|
|
10109
|
+
* Having the model emit a JSON-encoded filter sidesteps both restrictions and
|
|
10110
|
+
* keeps the tool portable across providers.
|
|
9973
10111
|
*/
|
|
9974
10112
|
declare const QueryPlanSchema: z.ZodObject<{
|
|
9975
10113
|
objectName: z.ZodString;
|
|
9976
|
-
|
|
9977
|
-
fields: z.
|
|
9978
|
-
orderBy: z.
|
|
10114
|
+
whereJson: z.ZodNullable<z.ZodString>;
|
|
10115
|
+
fields: z.ZodNullable<z.ZodArray<z.ZodString>>;
|
|
10116
|
+
orderBy: z.ZodNullable<z.ZodArray<z.ZodObject<{
|
|
9979
10117
|
field: z.ZodString;
|
|
9980
10118
|
order: z.ZodEnum<{
|
|
9981
10119
|
asc: "asc";
|
|
9982
10120
|
desc: "desc";
|
|
9983
10121
|
}>;
|
|
9984
10122
|
}, z.core.$strip>>>;
|
|
9985
|
-
limit: z.
|
|
10123
|
+
limit: z.ZodNullable<z.ZodNumber>;
|
|
9986
10124
|
}, z.core.$strip>;
|
|
9987
10125
|
/** Strongly-typed query plan inferred from the LLM. */
|
|
9988
10126
|
type QueryPlan = z.infer<typeof QueryPlanSchema>;
|