@genesislcap/ai-assistant 15.34.1 → 15.35.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/custom-elements.json +449 -0
- package/dist/dts/genesis/config.d.ts +22 -0
- package/dist/dts/genesis/config.d.ts.map +1 -0
- package/dist/dts/genesis/criteria.d.ts +83 -0
- package/dist/dts/genesis/criteria.d.ts.map +1 -0
- package/dist/dts/genesis/filter-fields.d.ts +62 -0
- package/dist/dts/genesis/filter-fields.d.ts.map +1 -0
- package/dist/dts/genesis/index.d.ts +19 -0
- package/dist/dts/genesis/index.d.ts.map +1 -0
- package/dist/dts/genesis/register-genesis-assistant.d.ts +52 -0
- package/dist/dts/genesis/register-genesis-assistant.d.ts.map +1 -0
- package/dist/dts/genesis/resource-tools.d.ts +86 -0
- package/dist/dts/genesis/resource-tools.d.ts.map +1 -0
- package/dist/dts/genesis/types.d.ts +115 -0
- package/dist/dts/genesis/types.d.ts.map +1 -0
- package/dist/esm/genesis/config.js +86 -0
- package/dist/esm/genesis/criteria.js +426 -0
- package/dist/esm/genesis/filter-fields.js +124 -0
- package/dist/esm/genesis/index.js +15 -0
- package/dist/esm/genesis/register-genesis-assistant.js +160 -0
- package/dist/esm/genesis/resource-tools.js +378 -0
- package/dist/esm/genesis/types.js +6 -0
- package/docs/genesis.md +180 -0
- package/package.json +32 -16
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { type TieredAIProviderSwitcherOptions } from '../provider/tiered-provider-switcher';
|
|
2
|
+
import type { GenesisAiConfig, GenesisAssistantRegistration, RegisterGenesisAssistantOptions } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* The name of the agent {@link registerGenesisAssistant} builds.
|
|
5
|
+
*
|
|
6
|
+
* @beta
|
|
7
|
+
*/
|
|
8
|
+
export declare const GENESIS_AGENT_NAME = "genesis-assistant";
|
|
9
|
+
/**
|
|
10
|
+
* The system-prompt line behind ADR P9. The tools stamp their results as untrusted app data;
|
|
11
|
+
* this tells the model what that means. Defence in depth — no other rule relies on it.
|
|
12
|
+
*
|
|
13
|
+
* @internal
|
|
14
|
+
*/
|
|
15
|
+
export declare const UNTRUSTED_DATA_PROMPT: string;
|
|
16
|
+
/**
|
|
17
|
+
* The exported app's own AI proxy: `<PUBLIC_PATH>/gwf/ai-service`.
|
|
18
|
+
*
|
|
19
|
+
* @internal
|
|
20
|
+
*/
|
|
21
|
+
export declare function defaultEndpointBase(publicPath?: string): string;
|
|
22
|
+
/**
|
|
23
|
+
* The provider options {@link registerGenesisAssistant} registers — split out so the choice of
|
|
24
|
+
* vendor, tier and endpoint can be checked without building providers.
|
|
25
|
+
*
|
|
26
|
+
* @remarks
|
|
27
|
+
* No model id appears anywhere: the vendor and tier name a row of the platform tier table, and
|
|
28
|
+
* that row supplies the model, the output ceiling and both timeouts. The app's proxy may clamp
|
|
29
|
+
* the ceiling further.
|
|
30
|
+
*
|
|
31
|
+
* @internal
|
|
32
|
+
*/
|
|
33
|
+
export declare function genesisProviderOptions(config: GenesisAiConfig, options?: Pick<RegisterGenesisAssistantOptions, 'endpointBase' | 'vendors'>): Omit<TieredAIProviderSwitcherOptions, 'container'>;
|
|
34
|
+
/**
|
|
35
|
+
* Sets up the AI assistant for a Genesis app from its generated AI config: providers by tier,
|
|
36
|
+
* and one agent whose tools read the app's resources.
|
|
37
|
+
*
|
|
38
|
+
* @remarks
|
|
39
|
+
* Call it once at start-up and bind the returned `agents` to the assistant element. It is
|
|
40
|
+
* synchronous on purpose — the element resolves its providers as it connects, and one that
|
|
41
|
+
* connects before they are registered keeps the empty fallback for the whole page load.
|
|
42
|
+
*
|
|
43
|
+
* It never throws. Anything that stops the assistant working — AI switched off for the build,
|
|
44
|
+
* an invalid config, an extension tool reusing a generated tool's name — is logged as an error
|
|
45
|
+
* and returned as `blockedReason`, with no agents. The mount code applies it with the element's
|
|
46
|
+
* `setBlocked(true, blockedReason)`. The app keeps working: one misnamed chat tool, for instance
|
|
47
|
+
* after a regeneration renames a resource onto an extension's tool, must never take it down.
|
|
48
|
+
*
|
|
49
|
+
* @beta
|
|
50
|
+
*/
|
|
51
|
+
export declare function registerGenesisAssistant(options: RegisterGenesisAssistantOptions): GenesisAssistantRegistration;
|
|
52
|
+
//# sourceMappingURL=register-genesis-assistant.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"register-genesis-assistant.d.ts","sourceRoot":"","sources":["../../../src/genesis/register-genesis-assistant.ts"],"names":[],"mappings":"AAIA,OAAO,EAEL,KAAK,+BAA+B,EACrC,MAAM,sCAAsC,CAAC;AAI9C,OAAO,KAAK,EACV,eAAe,EACf,4BAA4B,EAC5B,+BAA+B,EAChC,MAAM,SAAS,CAAC;AAEjB;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,sBAAsB,CAAC;AAEtD;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,QAGN,CAAC;AAE7B;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,UAAU,GAAE,MAAoB,GAAG,MAAM,CAE5E;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,eAAe,EACvB,OAAO,GAAE,IAAI,CAAC,+BAA+B,EAAE,cAAc,GAAG,SAAS,CAAM,GAC9E,IAAI,CAAC,+BAA+B,EAAE,WAAW,CAAC,CAUpD;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,+BAA+B,GACvC,4BAA4B,CAuG9B"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import type { ChatToolDefinition, ChatToolHandlers } from '@genesislcap/foundation-ai';
|
|
2
|
+
import { type Connect } from '@genesislcap/foundation-comms';
|
|
3
|
+
import type { GenesisAiResource } from './types';
|
|
4
|
+
/**
|
|
5
|
+
* The provenance stamped on everything a tool returns from the app (ADR P9). Rows are written by
|
|
6
|
+
* the app's other users, so the model must treat their text as data, never as instructions; the
|
|
7
|
+
* system prompt says the same thing in words.
|
|
8
|
+
*
|
|
9
|
+
* @beta
|
|
10
|
+
*/
|
|
11
|
+
export declare const GENESIS_DATA_SOURCE = "app-database";
|
|
12
|
+
/**
|
|
13
|
+
* What a read tool returns to the model.
|
|
14
|
+
*
|
|
15
|
+
* @beta
|
|
16
|
+
*/
|
|
17
|
+
export interface GenesisReadResult {
|
|
18
|
+
source: typeof GENESIS_DATA_SOURCE;
|
|
19
|
+
untrusted: true;
|
|
20
|
+
resource: string;
|
|
21
|
+
rows: unknown[];
|
|
22
|
+
/** True when more rows match than this call returned. */
|
|
23
|
+
truncated: boolean;
|
|
24
|
+
/** When `truncated`: that these rows are not the full set, and how to narrow the read. */
|
|
25
|
+
note?: string;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* What any Genesis tool returns instead of throwing. A thrown tool error invites the model to
|
|
29
|
+
* "retry it", which for a write can mean doing it twice (ADR P7), so none of these tools throw.
|
|
30
|
+
*
|
|
31
|
+
* @beta
|
|
32
|
+
*/
|
|
33
|
+
export interface GenesisToolError {
|
|
34
|
+
source: typeof GENESIS_DATA_SOURCE;
|
|
35
|
+
untrusted: true;
|
|
36
|
+
resource: string;
|
|
37
|
+
error: 'not_connected' | 'invalid_filter' | 'rejected' | 'request_failed';
|
|
38
|
+
message: string;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Options for {@link createGenesisResourceTools}.
|
|
42
|
+
*
|
|
43
|
+
* @beta
|
|
44
|
+
*/
|
|
45
|
+
export interface GenesisResourceToolsOptions {
|
|
46
|
+
/** The connection to read through. Defaults to the app's own, resolved from DI on first use. */
|
|
47
|
+
connect?: Connect;
|
|
48
|
+
/**
|
|
49
|
+
* How long a metadata fetch and a read may take before they are abandoned. Both exist because
|
|
50
|
+
* a request Connect drops on reconnect never settles; the defaults suit a real app, and tests
|
|
51
|
+
* shorten them.
|
|
52
|
+
*
|
|
53
|
+
* @internal
|
|
54
|
+
*/
|
|
55
|
+
timeouts?: {
|
|
56
|
+
metadataMs?: number;
|
|
57
|
+
readMs?: number;
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* The tools built from an app's resources, in the shape an agent config takes.
|
|
62
|
+
*
|
|
63
|
+
* @beta
|
|
64
|
+
*/
|
|
65
|
+
export interface GenesisResourceTools {
|
|
66
|
+
/**
|
|
67
|
+
* Resolves the definitions lazily — schemas are fetched on the first turn, not at
|
|
68
|
+
* registration — and returns the same list, in name order, every time after, so the tool
|
|
69
|
+
* list does not re-serialise between iterations and break prompt caching.
|
|
70
|
+
*/
|
|
71
|
+
toolDefinitions: () => Promise<ChatToolDefinition[]>;
|
|
72
|
+
toolHandlers: ChatToolHandlers;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Builds the assistant's tools from an app's resources.
|
|
76
|
+
*
|
|
77
|
+
* @remarks
|
|
78
|
+
* Each request resource becomes a read tool: filters from the resource's own schema, a row cap
|
|
79
|
+
* (ADR P10: 50 by default, never more than the resource's `maxRows` whatever the model asks),
|
|
80
|
+
* no paging (see `truncationNote`), and every result stamped as untrusted app data (P9). Event resources are not turned
|
|
81
|
+
* into tools yet; the write path, with its review table, comes separately.
|
|
82
|
+
*
|
|
83
|
+
* @beta
|
|
84
|
+
*/
|
|
85
|
+
export declare function createGenesisResourceTools(resources: readonly GenesisAiResource[], options?: GenesisResourceToolsOptions): GenesisResourceTools;
|
|
86
|
+
//# sourceMappingURL=resource-tools.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resource-tools.d.ts","sourceRoot":"","sources":["../../../src/genesis/resource-tools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AACvF,OAAO,EAAc,KAAK,OAAO,EAAE,MAAM,+BAA+B,CAAC;AAMzE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAEjD;;;;;;GAMG;AACH,eAAO,MAAM,mBAAmB,iBAAiB,CAAC;AAalD;;;;GAIG;AACH,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,OAAO,mBAAmB,CAAC;IACnC,SAAS,EAAE,IAAI,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,OAAO,EAAE,CAAC;IAChB,yDAAyD;IACzD,SAAS,EAAE,OAAO,CAAC;IACnB,0FAA0F;IAC1F,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;;GAKG;AACH,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,OAAO,mBAAmB,CAAC;IACnC,SAAS,EAAE,IAAI,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,eAAe,GAAG,gBAAgB,GAAG,UAAU,GAAG,gBAAgB,CAAC;IAC1E,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;GAIG;AACH,MAAM,WAAW,2BAA2B;IAC1C,gGAAgG;IAChG,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE;QAAE,UAAU,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CACrD;AAED;;;;GAIG;AACH,MAAM,WAAW,oBAAoB;IACnC;;;;OAIG;IACH,eAAe,EAAE,MAAM,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAAC;IACrD,YAAY,EAAE,gBAAgB,CAAC;CAChC;AAmID;;;;;;;;;;GAUG;AACH,wBAAgB,0BAA0B,CACxC,SAAS,EAAE,SAAS,iBAAiB,EAAE,EACvC,OAAO,GAAE,2BAAgC,GACxC,oBAAoB,CAkFtB"}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import type { AITierId, AITierVendor, ChatToolDefinition, ChatToolHandlers } from '@genesislcap/foundation-ai';
|
|
2
|
+
import type { Connect } from '@genesislcap/foundation-comms';
|
|
3
|
+
import type { Container } from '@genesislcap/web-core';
|
|
4
|
+
import type { AgentConfig } from '../config/config';
|
|
5
|
+
import type { TieredAIProviderSwitcher } from '../provider/tiered-provider-switcher';
|
|
6
|
+
/**
|
|
7
|
+
* What a Genesis resource is to the assistant: a request/reply it can read, or an event it
|
|
8
|
+
* can propose a write to.
|
|
9
|
+
*
|
|
10
|
+
* @beta
|
|
11
|
+
*/
|
|
12
|
+
export type GenesisResourceKind = 'request' | 'event';
|
|
13
|
+
/**
|
|
14
|
+
* The wire vocabulary for an event resource's operation. Custom handlers keep their own name.
|
|
15
|
+
*
|
|
16
|
+
* @beta
|
|
17
|
+
*/
|
|
18
|
+
export declare const GENESIS_EVENT_OPS: readonly ["insert", "modify", "delete", "custom"];
|
|
19
|
+
/**
|
|
20
|
+
* One of {@link GENESIS_EVENT_OPS}. Declared from the array so the type and the validator
|
|
21
|
+
* cannot drift apart, the way the platform's own tier ids are.
|
|
22
|
+
*
|
|
23
|
+
* @beta
|
|
24
|
+
*/
|
|
25
|
+
export type GenesisEventOp = (typeof GENESIS_EVENT_OPS)[number];
|
|
26
|
+
/**
|
|
27
|
+
* One app resource exposed to the assistant — an entry of the generated `ai-config.json`.
|
|
28
|
+
*
|
|
29
|
+
* @beta
|
|
30
|
+
*/
|
|
31
|
+
export interface GenesisAiResource {
|
|
32
|
+
/** `REQ_<NAME>` for a request, `EVENT_<NAME>` for an event. Unique within the config. */
|
|
33
|
+
name: string;
|
|
34
|
+
kind: GenesisResourceKind;
|
|
35
|
+
/** Events only. */
|
|
36
|
+
op?: GenesisEventOp;
|
|
37
|
+
/** What the resource is for, in plain words. Becomes the tool's description. */
|
|
38
|
+
context: string;
|
|
39
|
+
/** Requests only: the most rows one read may return (1–1000). Defaults to 50. */
|
|
40
|
+
maxRows?: number;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* The AI block of a generated app's configuration (`client/src/ai/generated/ai-config.json`).
|
|
44
|
+
*
|
|
45
|
+
* @remarks
|
|
46
|
+
* Carries no model id, endpoint, key or budget: the model comes from the platform tier table,
|
|
47
|
+
* the endpoint from the app's own proxy, and the key never leaves the server.
|
|
48
|
+
*
|
|
49
|
+
* @beta
|
|
50
|
+
*/
|
|
51
|
+
export interface GenesisAiConfig {
|
|
52
|
+
enabled: boolean;
|
|
53
|
+
/** Defaults to `'gemini'`. */
|
|
54
|
+
vendor?: AITierVendor;
|
|
55
|
+
/** Defaults to `'high'`. */
|
|
56
|
+
tier?: AITierId;
|
|
57
|
+
systemPrompt?: string;
|
|
58
|
+
resources?: GenesisAiResource[];
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* An app's own additions to the assistant — the hand-written tools under
|
|
62
|
+
* `client/src/ai/extensions/`.
|
|
63
|
+
*
|
|
64
|
+
* @beta
|
|
65
|
+
*/
|
|
66
|
+
export interface GenesisAssistantExtensions {
|
|
67
|
+
toolDefinitions?: ChatToolDefinition[];
|
|
68
|
+
toolHandlers?: ChatToolHandlers;
|
|
69
|
+
/** Appended to the configured system prompt. */
|
|
70
|
+
systemPrompt?: string;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Options for {@link registerGenesisAssistant}.
|
|
74
|
+
*
|
|
75
|
+
* @beta
|
|
76
|
+
*/
|
|
77
|
+
export interface RegisterGenesisAssistantOptions {
|
|
78
|
+
config: GenesisAiConfig;
|
|
79
|
+
/** DI container the providers are registered on. Defaults to the DOM container. */
|
|
80
|
+
container?: Container;
|
|
81
|
+
/**
|
|
82
|
+
* Base of the app's AI proxy routes; `/<vendor>/chat` is appended. Defaults to the exported
|
|
83
|
+
* app's own proxy, `<PUBLIC_PATH>/gwf/ai-service`. A preview build passes its own.
|
|
84
|
+
*/
|
|
85
|
+
endpointBase?: string;
|
|
86
|
+
/**
|
|
87
|
+
* The Genesis connection the tools read through. Defaults to the app's own, resolved from DI
|
|
88
|
+
* when a tool first runs rather than at registration, so registering stays synchronous.
|
|
89
|
+
*/
|
|
90
|
+
connect?: Connect;
|
|
91
|
+
extensions?: GenesisAssistantExtensions;
|
|
92
|
+
/**
|
|
93
|
+
* Vendors the user may switch between. Defaults to the configured vendor alone: an app whose
|
|
94
|
+
* proxy holds one key would otherwise offer a switch to a vendor that cannot answer.
|
|
95
|
+
*/
|
|
96
|
+
vendors?: readonly AITierVendor[];
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* What {@link registerGenesisAssistant} hands back to the app's mount code.
|
|
100
|
+
*
|
|
101
|
+
* @beta
|
|
102
|
+
*/
|
|
103
|
+
export interface GenesisAssistantRegistration {
|
|
104
|
+
/** Bind to the assistant element's `agents` property. Empty when blocked. */
|
|
105
|
+
agents: AgentConfig[];
|
|
106
|
+
/** The vendor switcher, when providers were registered. */
|
|
107
|
+
switcher?: TieredAIProviderSwitcher;
|
|
108
|
+
/**
|
|
109
|
+
* Set when the assistant cannot run — AI switched off, an invalid config, a tool-name clash.
|
|
110
|
+
* Apply it with the element's `setBlocked(true, blockedReason)`. The app itself keeps working:
|
|
111
|
+
* a problem with one chat tool must never take the whole app down at start-up.
|
|
112
|
+
*/
|
|
113
|
+
blockedReason?: string;
|
|
114
|
+
}
|
|
115
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/genesis/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,QAAQ,EACR,YAAY,EACZ,kBAAkB,EAClB,gBAAgB,EACjB,MAAM,4BAA4B,CAAC;AACpC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,+BAA+B,CAAC;AAC7D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,sCAAsC,CAAC;AAErF;;;;;GAKG;AACH,MAAM,MAAM,mBAAmB,GAAG,SAAS,GAAG,OAAO,CAAC;AAEtD;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,mDAAoD,CAAC;AAEnF;;;;;GAKG;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,iBAAiB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEhE;;;;GAIG;AACH,MAAM,WAAW,iBAAiB;IAChC,yFAAyF;IACzF,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,mBAAmB,CAAC;IAC1B,mBAAmB;IACnB,EAAE,CAAC,EAAE,cAAc,CAAC;IACpB,gFAAgF;IAChF,OAAO,EAAE,MAAM,CAAC;IAChB,iFAAiF;IACjF,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,OAAO,CAAC;IACjB,8BAA8B;IAC9B,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,4BAA4B;IAC5B,IAAI,CAAC,EAAE,QAAQ,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,iBAAiB,EAAE,CAAC;CACjC;AAED;;;;;GAKG;AACH,MAAM,WAAW,0BAA0B;IACzC,eAAe,CAAC,EAAE,kBAAkB,EAAE,CAAC;IACvC,YAAY,CAAC,EAAE,gBAAgB,CAAC;IAChC,gDAAgD;IAChD,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;;GAIG;AACH,MAAM,WAAW,+BAA+B;IAC9C,MAAM,EAAE,eAAe,CAAC;IACxB,mFAAmF;IACnF,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,UAAU,CAAC,EAAE,0BAA0B,CAAC;IACxC;;;OAGG;IACH,OAAO,CAAC,EAAE,SAAS,YAAY,EAAE,CAAC;CACnC;AAED;;;;GAIG;AACH,MAAM,WAAW,4BAA4B;IAC3C,6EAA6E;IAC7E,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,2DAA2D;IAC3D,QAAQ,CAAC,EAAE,wBAAwB,CAAC;IACpC;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { AI_TIER_IDS, AI_TIER_VENDORS } from '@genesislcap/foundation-ai';
|
|
2
|
+
import { GENESIS_EVENT_OPS } from './types';
|
|
3
|
+
/** The default read cap, and the ceiling the config may raise it to. */
|
|
4
|
+
export const DEFAULT_MAX_ROWS = 50;
|
|
5
|
+
export const MAX_ROWS_CEILING = 1000;
|
|
6
|
+
/** Tool names are the lowercased resource name; vendors reject names over 64 characters. */
|
|
7
|
+
const MAX_TOOL_NAME_LENGTH = 64;
|
|
8
|
+
const RESOURCE_NAME = /^(REQ|EVENT)_[A-Z0-9_]+$/;
|
|
9
|
+
/**
|
|
10
|
+
* The model-facing tool name for a resource. Deterministic and collision-free (resource names
|
|
11
|
+
* are unique), and trivially mapped back to the resource in a debug log. What the tool is FOR
|
|
12
|
+
* reaches the model through its description, not its name.
|
|
13
|
+
*/
|
|
14
|
+
export const toolNameOf = (resourceName) => resourceName.toLowerCase();
|
|
15
|
+
/**
|
|
16
|
+
* Checks a generated AI config before anything is registered, returning every problem found.
|
|
17
|
+
*
|
|
18
|
+
* @remarks
|
|
19
|
+
* The config is machine-generated and schema-checked upstream, so a violation here is a bug in
|
|
20
|
+
* that pipeline, not something to work around. The caller turns problems into a blocked
|
|
21
|
+
* assistant with the reason on screen — scoped to the assistant, never an exception that could
|
|
22
|
+
* take the app down at start-up — rather than running with part of the config quietly missing.
|
|
23
|
+
*
|
|
24
|
+
* @internal
|
|
25
|
+
*/
|
|
26
|
+
export function validateGenesisAiConfig(config) {
|
|
27
|
+
if (!config || typeof config !== 'object')
|
|
28
|
+
return ['the AI config is missing or not an object'];
|
|
29
|
+
const c = config;
|
|
30
|
+
const problems = [];
|
|
31
|
+
if (c.enabled !== true)
|
|
32
|
+
problems.push(`enabled must be true (got ${String(c.enabled)})`);
|
|
33
|
+
if (c.vendor !== undefined && !AI_TIER_VENDORS.includes(c.vendor)) {
|
|
34
|
+
problems.push(`unknown vendor '${String(c.vendor)}' (expected ${AI_TIER_VENDORS.join(' or ')})`);
|
|
35
|
+
}
|
|
36
|
+
if (c.tier !== undefined && !AI_TIER_IDS.includes(c.tier)) {
|
|
37
|
+
problems.push(`unknown tier '${String(c.tier)}' (expected ${AI_TIER_IDS.join(', ')})`);
|
|
38
|
+
}
|
|
39
|
+
if (c.systemPrompt !== undefined && typeof c.systemPrompt !== 'string') {
|
|
40
|
+
problems.push('systemPrompt must be a string');
|
|
41
|
+
}
|
|
42
|
+
if (c.resources === undefined)
|
|
43
|
+
return problems;
|
|
44
|
+
if (!Array.isArray(c.resources))
|
|
45
|
+
return [...problems, 'resources must be an array'];
|
|
46
|
+
const seen = new Set();
|
|
47
|
+
c.resources.forEach((resource, i) => {
|
|
48
|
+
const at = `resources[${i}]`;
|
|
49
|
+
const name = resource === null || resource === void 0 ? void 0 : resource.name;
|
|
50
|
+
if (typeof name !== 'string' || !RESOURCE_NAME.test(name)) {
|
|
51
|
+
problems.push(`${at}: name must look like REQ_<NAME> or EVENT_<NAME> (got ${String(name)})`);
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
if (toolNameOf(name).length > MAX_TOOL_NAME_LENGTH) {
|
|
55
|
+
problems.push(`${name}: longer than the ${MAX_TOOL_NAME_LENGTH} characters a tool name allows`);
|
|
56
|
+
}
|
|
57
|
+
// A duplicate is not harmless: two tools with one name are rejected by the vendor, and the
|
|
58
|
+
// same list feeds the generated MCP script, which refuses a repeated name at load.
|
|
59
|
+
if (seen.has(name))
|
|
60
|
+
problems.push(`${name}: appears more than once`);
|
|
61
|
+
seen.add(name);
|
|
62
|
+
const expectedKind = name.startsWith('REQ_') ? 'request' : 'event';
|
|
63
|
+
if (resource.kind !== expectedKind) {
|
|
64
|
+
problems.push(`${name}: kind must be '${expectedKind}' (got ${String(resource.kind)})`);
|
|
65
|
+
}
|
|
66
|
+
if (typeof resource.context !== 'string')
|
|
67
|
+
problems.push(`${name}: context must be a string`);
|
|
68
|
+
if (expectedKind === 'event') {
|
|
69
|
+
if (!GENESIS_EVENT_OPS.includes(resource.op)) {
|
|
70
|
+
problems.push(`${name}: op must be one of ${GENESIS_EVENT_OPS.join(', ')}`);
|
|
71
|
+
}
|
|
72
|
+
if (resource.maxRows !== undefined)
|
|
73
|
+
problems.push(`${name}: maxRows applies to requests only`);
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
if (resource.op !== undefined)
|
|
77
|
+
problems.push(`${name}: op applies to events only`);
|
|
78
|
+
const { maxRows } = resource;
|
|
79
|
+
if (maxRows !== undefined &&
|
|
80
|
+
!(Number.isInteger(maxRows) && maxRows >= 1 && maxRows <= MAX_ROWS_CEILING)) {
|
|
81
|
+
problems.push(`${name}: maxRows must be a whole number from 1 to ${MAX_ROWS_CEILING}`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
return problems;
|
|
86
|
+
}
|