-
Notifications
You must be signed in to change notification settings - Fork 6
Expand file tree
/
Copy pathagent.zod.ts
More file actions
280 lines (243 loc) · 11.8 KB
/
Copy pathagent.zod.ts
File metadata and controls
280 lines (243 loc) · 11.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
import { z } from 'zod';
import { ProtectionSchema } from '../shared/protection.zod';
import { MetadataProtectionFields } from '../kernel/metadata-protection.zod';
import { StateMachineSchema } from '../automation/state-machine.zod';
/**
* AI Model Configuration
*/
import { lazySchema } from '../shared/lazy-schema';
export const AIModelConfigSchema = lazySchema(() => z.object({
provider: z.enum(['openai', 'azure_openai', 'anthropic', 'local']).default('openai'),
model: z.string().describe('Model name (e.g. gpt-4, claude-3-opus)'),
temperature: z.number().min(0).max(2).default(0.7),
maxTokens: z.number().optional(),
topP: z.number().optional(),
}));
/**
* AI Tool Definition
* References to Actions, Flows, or Objects available to the Agent.
*/
export const AIToolSchema = lazySchema(() => z.object({
type: z.enum(['action', 'flow', 'query', 'vector_search']),
name: z.string().describe('Reference name (Action Name, Flow Name)'),
description: z.string().optional().describe('Override description for the LLM'),
}));
/**
* AI Knowledge Base
* RAG configuration.
*/
export const AIKnowledgeSchema = lazySchema(() => z.object({
sources: z.array(z.string()).optional().describe('Knowledge sources/tags to recruit RAG context from. Canonical key consumed by the agent renderer (objectui AgentPreview KnowledgeSummary).'),
topics: z.array(z.string()).optional().describe('Deprecated alias for `sources` (spec key ≠ consumed key drift, liveness audit #1878/#1891). Prefer `sources`; `topics` is retained for back-compat but the renderer reads `sources`.'),
indexes: z.array(z.string()).describe('Vector Store Indexes'),
}));
/**
* Structured Output Format
* Defines the expected output format for agent responses
*/
export const StructuredOutputFormatSchema = lazySchema(() => z.enum([
'json_object',
'json_schema',
'regex',
'grammar',
'xml',
]).describe('Output format for structured agent responses'));
/**
* Transform Pipeline Step
* Post-processing steps applied to structured output
*/
export const TransformPipelineStepSchema = lazySchema(() => z.enum([
'trim',
'parse_json',
'validate',
'coerce_types',
]).describe('Post-processing step for structured output'));
/**
* Structured Output Configuration
* Controls how the agent formats and validates its output
*/
export const StructuredOutputConfigSchema = lazySchema(() => z.object({
/** Output format type */
format: StructuredOutputFormatSchema.describe('Expected output format'),
/** JSON Schema definition for output validation */
schema: z.record(z.string(), z.unknown()).optional().describe('JSON Schema definition for output'),
/** Whether to enforce exact schema compliance */
strict: z.boolean().default(false).describe('Enforce exact schema compliance'),
/** Retry on validation failure */
retryOnValidationFailure: z.boolean().default(true).describe('Retry generation when output fails validation'),
/** Maximum retry attempts */
maxRetries: z.number().int().min(0).default(3).describe('Maximum retries on validation failure'),
/** Fallback format if primary format fails */
fallbackFormat: StructuredOutputFormatSchema.optional().describe('Fallback format if primary format fails'),
/** Post-processing pipeline steps */
transformPipeline: z.array(TransformPipelineStepSchema).optional().describe('Post-processing steps applied to output'),
}).describe('Structured output configuration for agent responses'));
export type StructuredOutputFormat = z.infer<typeof StructuredOutputFormatSchema>;
export type TransformPipelineStep = z.infer<typeof TransformPipelineStepSchema>;
export type StructuredOutputConfig = z.infer<typeof StructuredOutputConfigSchema>;
/**
* AI Agent Schema
* Definition of an autonomous agent specialized for a domain.
*
* The Agent → Skill → Tool three-tier architecture aligns with
* Salesforce Agentforce, Microsoft Copilot Studio, and ServiceNow
* Now Assist metadata patterns.
*
* - **skills**: Primary capability model — references skill names.
* - **tools**: Fallback / direct tool references (legacy inline format).
*
* @example Agent-Skill Architecture
* ```ts
* defineAgent({
* name: 'support_tier_1',
* label: 'First Line Support',
* role: 'Help Desk Assistant',
* instructions: 'You are a helpful assistant. Always verify user identity first.',
* skills: ['case_management', 'knowledge_search'],
* knowledge: { topics: ['faq', 'policies'], indexes: ['support_docs'] },
* });
* ```
*
* @example Legacy Tool References (backward-compatible)
* ```ts
* defineAgent({
* name: 'support_tier_1',
* label: 'First Line Support',
* role: 'Help Desk Assistant',
* instructions: 'You are a helpful assistant.',
* tools: [
* { type: 'flow', name: 'reset_password', description: 'Trigger password reset email' },
* { type: 'query', name: 'get_order_status', description: 'Check order shipping status' },
* ],
* });
* ```
*/
export const AgentSchema = lazySchema(() => z.object({
/** Identity */
name: z.string().regex(/^[a-z_][a-z0-9_]*$/).describe('Agent unique identifier'),
label: z.string().describe('Agent display name'),
avatar: z.string().optional(),
role: z.string().describe('The persona/role (e.g. "Senior Support Engineer")'),
/** Cognition */
instructions: z.string().describe('System Prompt / Prime Directives'),
model: AIModelConfigSchema.optional(),
lifecycle: StateMachineSchema.optional().describe('State machine defining the agent conversation follow and constraints'),
/**
* ADR-0063 §1 / ADR-0064 — the product surface this agent IS. The kernel
* ships exactly two: `ask` (data product, surface `'ask'`) and `build`
* (authoring product, surface `'build'`). A skill may only bind to an
* agent whose surface it matches (`'both'` skills bind to either), and the
* agent's tool set is the union of those skills' tools — nothing falls
* through to the global registry. Defaults to `'ask'`.
*/
surface: z.enum(['ask', 'build']).default('ask').describe(
"Product surface this agent binds ('ask' | 'build') — ADR-0063 §1",
),
/** Capabilities — Skill-based (primary) */
skills: z.array(z.string().regex(/^[a-z_][a-z0-9_]*$/)).optional().describe('Skill names to attach (Agent→Skill→Tool architecture)'),
/** Capabilities — Direct tool references (fallback / legacy) */
tools: z.array(AIToolSchema).optional().describe('Direct tool references (legacy fallback)'),
/** Knowledge */
knowledge: AIKnowledgeSchema.optional().describe('RAG access'),
/** Interface */
active: z.boolean().default(true),
access: z.array(z.string()).optional().describe('Who can chat with this agent'),
/** Permission-set capabilities required to use this agent */
permissions: z.array(z.string()).optional().describe('Required permission-set capabilities'),
// Two agent-scoping fields were REMOVED as unenforced security properties
// (ADR-0049 / ADR-0056 D8 "design+enforce or remove"), not merely marked:
// • `tenantId` — 16.x line (#2377): had no runtime reader and did NOT scope
// the agent to a tenant. Tenancy comes from the request context
// (resolveAuthzContext), never a field on the artifact.
// • `visibility` (`global`/`organization`/`private`) — removed 2026-07 (#1901).
// No runtime consumer ever read it: the chat-access evaluator excluded it
// and the agent list route did not filter by it, so `private` never hid an
// agent. Enforcing it correctly needs owner/org anchors that do not exist
// yet (agents have no owner field; the `EXTERNAL` posture rung is never
// derived — see #1901). Carrying a security-shaped field that lies is a
// liability, so it was dropped rather than left marked. Restrict who can
// use an agent with `access` / `permissions` — both ENFORCED at the chat
// route (#1884). Re-introduce `visibility` when the listing surface gains
// real owner/org semantics.
/** Autonomous Reasoning */
planning: z.object({
/** Maximum reasoning iterations before stopping */
maxIterations: z.number().int().min(1).max(100).default(10).describe('Maximum planning loop iterations'),
}).optional().describe('Autonomous reasoning and planning configuration'),
/** Memory Management */
memory: z.object({
// NOTE: `shortTerm` ({maxMessages,maxTokens}) was removed (ADR-0013 D3,
// cloud#339). It declared a working-memory window that NOTHING in the
// runtime consumed — a config that lies. Cross-turn grounding is done by
// tools reading live state, and the context budget is governed elsewhere
// (the per-request token guardrail), not by this field. `longTerm` /
// `reflectionInterval` are kept as forward-looking, off-by-default config.
/** Long-term (persistent) memory configuration */
longTerm: z.object({
/** Whether long-term memory is enabled */
enabled: z.boolean().default(false).describe('Enable long-term memory persistence'),
/** Storage backend for long-term memory */
store: z.enum(['vector', 'database', 'redis']).default('vector').describe('Long-term memory storage backend'),
/** Maximum number of persisted memory entries */
maxEntries: z.number().int().min(1).optional().describe('Max entries in long-term memory'),
}).optional().describe('Long-term / persistent memory'),
/** Reflection interval — how often the agent reflects on past actions */
reflectionInterval: z.number().int().min(1).optional().describe('Reflect every N interactions to improve behavior'),
}).optional().describe('Agent memory management'),
/** Guardrails */
guardrails: z.object({
/** Maximum tokens the agent may consume per invocation */
maxTokensPerInvocation: z.number().int().min(1).optional().describe('Token budget per single invocation'),
/** Maximum wall-clock time per invocation in seconds */
maxExecutionTimeSec: z.number().int().min(1).optional().describe('Max execution time in seconds'),
/** Topics or actions the agent must avoid */
blockedTopics: z.array(z.string()).optional().describe('Forbidden topics or action names'),
}).optional().describe('Safety guardrails for the agent'),
/** Structured Output */
structuredOutput: StructuredOutputConfigSchema.optional().describe('Structured output format and validation configuration'),
/**
* ADR-0010 §3.7 — Package-level protection envelope. Package
* authors declare lock policy here; the loader translates it
* into the private `_lock` envelope at registration time and
* strips this block before persistence. See
* `shared/protection.zod.ts`.
*/
protection: ProtectionSchema.optional().describe(
'Package author protection block — lock policy for this agent.',
),
// ADR-0010 — runtime protection envelope (internal — set by loader).
...MetadataProtectionFields,
}));
/**
* Type-safe factory for creating AI agent definitions.
*
* Validates the config at creation time using Zod `.parse()`.
*
* @example Agent-Skill Architecture (recommended)
* ```ts
* const supportAgent = defineAgent({
* name: 'support_agent',
* label: 'Support Agent',
* role: 'Senior Support Engineer',
* instructions: 'You help customers resolve technical issues.',
* skills: ['case_management', 'knowledge_search'],
* });
* ```
*
* @example Legacy Tool References (backward-compatible)
* ```ts
* const supportAgent = defineAgent({
* name: 'support_agent',
* label: 'Support Agent',
* role: 'Senior Support Engineer',
* instructions: 'You help customers resolve technical issues.',
* tools: [{ type: 'action', name: 'create_ticket' }],
* });
* ```
*/
export function defineAgent(config: z.input<typeof AgentSchema>): Agent {
return AgentSchema.parse(config);
}
export type Agent = z.infer<typeof AgentSchema>;
export type AITool = z.infer<typeof AIToolSchema>;