@agexhq/core 1.0.0 → 1.0.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agexhq/core",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "AGEX protocol primitives — crypto, schemas, policy engine",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -4,7 +4,7 @@
4
4
  * Pure JavaScript — works on Termux without native compilation
5
5
  */
6
6
 
7
- import { x25519 } from '@noble/curves/ed25519'
7
+ import { x25519, edwardsToMontgomeryPub } from '@noble/curves/ed25519'
8
8
  import { hkdf } from '@noble/hashes/hkdf'
9
9
  import { sha256 } from '@noble/hashes/sha256'
10
10
  import { randomBytes } from '@noble/hashes/utils'
@@ -105,8 +105,6 @@ export function ed25519PrivateToX25519 (ed25519PrivateBase64url) {
105
105
  * Uses the birational map from Ed25519 to X25519 (Montgomery form).
106
106
  */
107
107
  export function ed25519PublicToX25519 (publicKeyJWK) {
108
- // @noble/curves provides this conversion
109
- const { edwardsToMontgomeryPub } = await import('@noble/curves/ed25519')
110
108
  const edPub = base64url.decode(publicKeyJWK.x)
111
109
  return edwardsToMontgomeryPub(edPub)
112
110
  }
@@ -1,14 +0,0 @@
1
- export {
2
- generateKeypair, publicKeyToJWK, jwkToPublicKeyBytes,
3
- sign, verify,
4
- canonicalJson,
5
- sha3Hash, sha256Hash,
6
- computeEventHash,
7
- generateId, generateNonce,
8
- base64url, randomBytes
9
- } from './keys.js'
10
-
11
- export {
12
- encryptEnvelope, decryptEnvelope,
13
- ed25519PrivateToX25519
14
- } from './ecdh.js'
@@ -1,119 +0,0 @@
1
- /**
2
- * @agexhq/core — Cryptographic primitives
3
- * Ed25519 key generation, signing, verification
4
- * All pure JavaScript via @noble — zero native deps
5
- */
6
-
7
- import * as ed from '@noble/ed25519'
8
- import { sha256 } from '@noble/hashes/sha256'
9
- import { sha3_256 } from '@noble/hashes/sha3'
10
- import { randomBytes } from '@noble/hashes/utils'
11
- import { base64url } from 'jose'
12
- import { v4 as uuidv4 } from 'uuid'
13
-
14
- // ── Key Generation ────────────────────────────────────────────────────────
15
-
16
- export function generateKeypair () {
17
- const privateKey = ed.utils.randomPrivateKey()
18
- const publicKey = ed.getPublicKeySync(privateKey)
19
- return {
20
- privateKey: base64url.encode(privateKey),
21
- publicKey: base64url.encode(publicKey),
22
- jwk: publicKeyToJWK(publicKey)
23
- }
24
- }
25
-
26
- export function publicKeyToJWK (publicKeyBytes) {
27
- return { kty: 'OKP', crv: 'Ed25519', x: base64url.encode(publicKeyBytes) }
28
- }
29
-
30
- export function jwkToPublicKeyBytes (jwk) {
31
- return base64url.decode(jwk.x)
32
- }
33
-
34
- // ── Signing ───────────────────────────────────────────────────────────────
35
-
36
- export async function sign (message, privateKeyBase64url) {
37
- const privBytes = base64url.decode(privateKeyBase64url)
38
- const msgBytes = typeof message === 'string'
39
- ? new TextEncoder().encode(message)
40
- : message
41
- const sig = await ed.signAsync(msgBytes, privBytes)
42
- return base64url.encode(sig)
43
- }
44
-
45
- export async function verify (message, signatureBase64url, publicKeyJWK) {
46
- try {
47
- const pubBytes = jwkToPublicKeyBytes(publicKeyJWK)
48
- const sigBytes = base64url.decode(signatureBase64url)
49
- const msgBytes = typeof message === 'string'
50
- ? new TextEncoder().encode(message)
51
- : message
52
- return await ed.verifyAsync(sigBytes, msgBytes, pubBytes)
53
- } catch {
54
- return false
55
- }
56
- }
57
-
58
- // ── Canonical JSON (RFC 8785 — fixed edge cases from audit 3.3) ──────────
59
-
60
- export function canonicalJson (obj) {
61
- if (obj === null) return 'null'
62
- if (obj === undefined) return undefined
63
- if (typeof obj === 'boolean') return String(obj)
64
- if (typeof obj === 'number') {
65
- if (!Number.isFinite(obj)) return 'null'
66
- return Object.is(obj, -0) ? '0' : String(obj)
67
- }
68
- if (typeof obj === 'string') return JSON.stringify(obj)
69
- if (obj instanceof Date) return JSON.stringify(obj.toISOString())
70
- if (Array.isArray(obj)) {
71
- return '[' + obj.map(v => {
72
- const s = canonicalJson(v)
73
- return s === undefined ? 'null' : s
74
- }).join(',') + ']'
75
- }
76
- if (typeof obj === 'object') {
77
- const pairs = Object.keys(obj)
78
- .sort()
79
- .map(k => {
80
- const v = canonicalJson(obj[k])
81
- if (v === undefined) return undefined
82
- return JSON.stringify(k) + ':' + v
83
- })
84
- .filter(p => p !== undefined)
85
- return '{' + pairs.join(',') + '}'
86
- }
87
- return 'null'
88
- }
89
-
90
- // ── Hashing ───────────────────────────────────────────────────────────────
91
-
92
- export function sha3Hash (data) {
93
- const bytes = typeof data === 'string'
94
- ? new TextEncoder().encode(data) : data
95
- return Buffer.from(sha3_256(bytes)).toString('hex')
96
- }
97
-
98
- export function sha256Hash (data) {
99
- const bytes = typeof data === 'string'
100
- ? new TextEncoder().encode(data) : data
101
- return Buffer.from(sha256(bytes)).toString('hex')
102
- }
103
-
104
- // ── Audit Hash Chain ──────────────────────────────────────────────────────
105
-
106
- export function computeEventHash (event, prevHash) {
107
- const payload = canonicalJson({ ...event, prev_hash: prevHash })
108
- return sha3Hash(payload)
109
- }
110
-
111
- // ── ID Generation ─────────────────────────────────────────────────────────
112
-
113
- export function generateId () { return uuidv4() }
114
-
115
- export function generateNonce () {
116
- return base64url.encode(randomBytes(16))
117
- }
118
-
119
- export { base64url, randomBytes }
package/src/index.js DELETED
@@ -1,40 +0,0 @@
1
- /**
2
- * @agexhq/core — AGEX Protocol Primitives
3
- * The foundation package for the entire AGEX ecosystem.
4
- * Pure JavaScript — zero native dependencies — works everywhere including Termux.
5
- */
6
-
7
- // ── Crypto ────────────────────────────────────────────────────────────────
8
- export {
9
- generateKeypair, publicKeyToJWK, jwkToPublicKeyBytes,
10
- sign, verify,
11
- canonicalJson,
12
- sha3Hash, sha256Hash,
13
- computeEventHash,
14
- generateId, generateNonce,
15
- base64url, randomBytes
16
- } from './crypto/keys.js'
17
-
18
- export {
19
- encryptEnvelope, decryptEnvelope,
20
- ed25519PrivateToX25519
21
- } from './crypto/ecdh.js'
22
-
23
- // ── Schemas ───────────────────────────────────────────────────────────────
24
- export {
25
- AIDSchema, ManifestSchema, CLCSchema,
26
- ServiceProviderSchema,
27
- PolicyDocSchema, PolicyRuleSchema, PolicyConditionSchema
28
- } from './schemas/index.js'
29
-
30
- // ── Protocol ──────────────────────────────────────────────────────────────
31
- export {
32
- verifyAIDSignature, selfSignAID,
33
- signManifest, verifyManifest,
34
- buildAgexHeaders, signRequest, verifyRequest,
35
- AgexError,
36
- AGEX_VERSION, AUDIT_EVENTS
37
- } from './protocol/index.js'
38
-
39
- // ── Policy Engine ─────────────────────────────────────────────────────────
40
- export { evaluatePolicy, evaluateCondition } from './policy/index.js'
@@ -1,133 +0,0 @@
1
- /**
2
- * @agexhq/core — AGEX Policy Language (APL) Evaluation Engine
3
- * Deterministic, stateless policy evaluation
4
- */
5
-
6
- export function evaluatePolicy (policy, aid, manifest) {
7
- const rules = [...(policy.rules || [])].sort((a, b) => (a.priority || 99) - (b.priority || 99))
8
-
9
- for (const rule of rules) {
10
- const matched = evaluateCondition(rule.condition, aid, manifest)
11
- if (matched) {
12
- return {
13
- action: rule.action,
14
- rule_id: rule.rule_id,
15
- reason: rule.description || `Matched rule: ${rule.rule_id}`
16
- }
17
- }
18
- }
19
-
20
- return {
21
- action: policy.default_action || 'reject',
22
- reason: 'No rules matched; applying default_action'
23
- }
24
- }
25
-
26
- export function evaluateCondition (condition, aid, manifest) {
27
- if (!condition) return true
28
-
29
- switch (condition.type) {
30
-
31
- case 'trust_tier': {
32
- const tier = typeof aid.trust_tier === 'number' ? aid.trust_tier : parseInt(aid.trust_tier)
33
- switch (condition.operator) {
34
- case 'gte': return tier >= condition.value
35
- case 'lte': return tier <= condition.value
36
- case 'eq': return tier === condition.value
37
- case 'gt': return tier > condition.value
38
- case 'lt': return tier < condition.value
39
- default: return false
40
- }
41
- }
42
-
43
- case 'scope': {
44
- const requested = manifest.target?.requested_scopes || []
45
- if (condition.operator === 'contains_any') {
46
- return condition.values.some(s => requested.includes(s))
47
- }
48
- if (condition.operator === 'contains_all') {
49
- return condition.values.every(s => requested.includes(s))
50
- }
51
- if (condition.operator === 'subset_of') {
52
- return requested.every(s => condition.values.includes(s))
53
- }
54
- return false
55
- }
56
-
57
- case 'intent_type': {
58
- const taskType = manifest.intent?.task_type
59
- if (condition.operator === 'eq') return taskType === condition.value
60
- if (condition.operator === 'in') return (condition.values || []).includes(taskType)
61
- return false
62
- }
63
-
64
- case 'data_classification': {
65
- const dc = manifest.intent?.data_classification
66
- const levels = ['public', 'internal', 'confidential', 'restricted']
67
- const reqLevel = levels.indexOf(dc)
68
- const condLevel = levels.indexOf(condition.value)
69
- if (condition.operator === 'lte') return reqLevel <= condLevel
70
- if (condition.operator === 'gte') return reqLevel >= condLevel
71
- if (condition.operator === 'eq') return reqLevel === condLevel
72
- return false
73
- }
74
-
75
- case 'pii_processing':
76
- return manifest.data_handling?.pii_processing === condition.value
77
-
78
- case 'environment': {
79
- const env = manifest.target?.environment
80
- if (condition.operator === 'eq') return env === condition.value
81
- if (condition.operator === 'in') return (condition.values || []).includes(env)
82
- return false
83
- }
84
-
85
- case 'time_window': {
86
- const now = new Date()
87
- const hour = now.getUTCHours()
88
- const day = now.getUTCDay()
89
- const inHours = hour >= (condition.start_hour || 0) && hour < (condition.end_hour || 24)
90
- const inDays = !condition.days_of_week ||
91
- condition.days_of_week.includes(['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat'][day])
92
- return inHours && inDays
93
- }
94
-
95
- // FIX: audit 2.6 — clarified geography semantics
96
- case 'geography': {
97
- const agentJurisdiction = aid.agent?.principal?.jurisdiction ||
98
- (typeof aid.restrictions === 'string' ? JSON.parse(aid.restrictions) : aid.restrictions)?.geo_restrictions?.[0]
99
- const allowedRegions = (typeof aid.restrictions === 'string' ? JSON.parse(aid.restrictions) : aid.restrictions)?.geo_restrictions || []
100
-
101
- if (condition.operator === 'agent_in') {
102
- return condition.values.includes(agentJurisdiction)
103
- }
104
- if (condition.operator === 'agent_not_in') {
105
- return !condition.values.includes(agentJurisdiction)
106
- }
107
- if (condition.operator === 'restricted_to') {
108
- return condition.values.every(v => allowedRegions.includes(v))
109
- }
110
- // Backwards compat with old in/not_in operators
111
- if (condition.operator === 'in') {
112
- return condition.values.some(v => allowedRegions.includes(v))
113
- }
114
- if (condition.operator === 'not_in') {
115
- return !condition.values.some(v => allowedRegions.includes(v))
116
- }
117
- return false
118
- }
119
-
120
- case 'and':
121
- return (condition.conditions || []).every(c => evaluateCondition(c, aid, manifest))
122
-
123
- case 'or':
124
- return (condition.conditions || []).some(c => evaluateCondition(c, aid, manifest))
125
-
126
- case 'not':
127
- return !evaluateCondition(condition.condition, aid, manifest)
128
-
129
- default:
130
- console.warn(`[APL] Unknown condition type: ${condition.type}`)
131
- return false
132
- }
133
- }
@@ -1,107 +0,0 @@
1
- /**
2
- * @agexhq/core — Protocol operations
3
- * AID verification, manifest signing, AGEX header construction
4
- */
5
-
6
- import { sign, verify, canonicalJson, generateId } from '../crypto/keys.js'
7
-
8
- // ── AID Signature Verification (FIX: audit 1.1) ──────────────────────────
9
- // ALWAYS verifies — never silently bypasses
10
-
11
- export async function verifyAIDSignature (aid, iaPublicKeyJWK) {
12
- if (!iaPublicKeyJWK) {
13
- throw new AgexError('IA_KEY_REQUIRED', 'IA public key required for AID verification', 400)
14
- }
15
- const { ia_signature, ...aidBody } = aid
16
- const canonical = canonicalJson(aidBody)
17
- const valid = await verify(canonical, ia_signature, iaPublicKeyJWK)
18
- if (!valid) {
19
- throw new AgexError('IA_SIGNATURE_INVALID', 'AID ia_signature failed verification', 401)
20
- }
21
- return true
22
- }
23
-
24
- /**
25
- * Self-sign an AID using the Hub's own key (dev/self-signed IA mode).
26
- * The AID is still cryptographically signed — just by the hub instead of an external IA.
27
- */
28
- export async function selfSignAID (aidBody, hubPrivateKey) {
29
- const { ia_signature, ...body } = aidBody
30
- const canonical = canonicalJson(body)
31
- const signature = await sign(canonical, hubPrivateKey)
32
- return { ...body, ia_signature: signature }
33
- }
34
-
35
- // ── Manifest Signing (FIX: audit 1.3) ─────────────────────────────────────
36
- // Real Ed25519 signatures instead of placeholder strings
37
-
38
- export async function signManifest (manifest, privateKey) {
39
- const { agent_signature, ...body } = manifest
40
- const canonical = canonicalJson(body)
41
- return await sign(canonical, privateKey)
42
- }
43
-
44
- export async function verifyManifest (manifest, publicKeyJWK) {
45
- const { agent_signature, ...body } = manifest
46
- if (!agent_signature || agent_signature === 'sdk-placeholder') return false
47
- const canonical = canonicalJson(body)
48
- return await verify(canonical, agent_signature, publicKeyJWK)
49
- }
50
-
51
- // ── AGEX Request Headers ──────────────────────────────────────────────────
52
-
53
- export function buildAgexHeaders (aidId, version = '1.0') {
54
- return {
55
- 'X-AGEX-Version': version,
56
- 'X-AGEX-Request-ID': generateId(),
57
- 'X-AGEX-Timestamp': new Date().toISOString(),
58
- 'X-AGEX-AID': aidId
59
- }
60
- }
61
-
62
- export async function signRequest (body, timestamp, requestId, privateKey) {
63
- const bodyStr = body ? canonicalJson(body) : ''
64
- const sigInput = `${bodyStr}|${timestamp}|${requestId}`
65
- return await sign(sigInput, privateKey)
66
- }
67
-
68
- export async function verifyRequest (body, timestamp, requestId, signature, publicKeyJWK) {
69
- const bodyStr = body ? canonicalJson(body) : ''
70
- const sigInput = `${bodyStr}|${timestamp}|${requestId}`
71
- return await verify(sigInput, signature, publicKeyJWK)
72
- }
73
-
74
- // ── Error class ───────────────────────────────────────────────────────────
75
-
76
- export class AgexError extends Error {
77
- constructor (code, message, statusCode = 400) {
78
- super(message)
79
- this.name = 'AgexError'
80
- this.code = code
81
- this.statusCode = statusCode
82
- }
83
- }
84
-
85
- // ── Constants ─────────────────────────────────────────────────────────────
86
-
87
- export const AGEX_VERSION = '1.0'
88
-
89
- export const AUDIT_EVENTS = {
90
- AID_REGISTERED: 'aid.registered',
91
- AID_REVOKED: 'aid.revoked',
92
- CREDENTIAL_REQUESTED: 'credential.requested',
93
- CREDENTIAL_ISSUED: 'credential.issued',
94
- CREDENTIAL_REJECTED: 'credential.rejected',
95
- CREDENTIAL_PENDING: 'credential.pending_approval',
96
- ROTATION_INITIATED: 'rotation.initiated',
97
- ROTATION_COMPLETED: 'rotation.completed',
98
- ROTATION_FAILED: 'rotation.failed',
99
- DELEGATION_CREATED: 'delegation.created',
100
- ERS_INITIATED: 'ers.initiated',
101
- ERS_COMPLETED: 'ers.completed',
102
- CLC_REVOKED: 'clc.revoked',
103
- CLC_SUSPENDED: 'clc.suspended',
104
- CLC_RESUMED: 'clc.resumed',
105
- APPROVAL_GRANTED: 'approval.granted',
106
- APPROVAL_REJECTED: 'approval.rejected'
107
- }
@@ -1,153 +0,0 @@
1
- /**
2
- * @agexhq/core — Zod schemas for all AGEX protocol objects
3
- * Single source of truth — used by hub, SDK, and CLI
4
- */
5
-
6
- import { z } from 'zod'
7
-
8
- // ── AID Schema (Agent Identity Document) ──────────────────────────────────
9
-
10
- export const AIDSchema = z.object({
11
- aid_version: z.literal('1.0'),
12
- aid_id: z.string().uuid(),
13
- issuer: z.object({
14
- ia_id: z.string(),
15
- ia_name: z.string(),
16
- ia_cert_id: z.string()
17
- }),
18
- issued_at: z.string().datetime(),
19
- expires_at: z.string().datetime(),
20
- agent: z.object({
21
- name: z.string().optional(),
22
- type: z.enum(['orchestrator', 'worker', 'specialist', 'gateway']),
23
- capabilities: z.array(z.string()).default([]),
24
- principal: z.object({
25
- organisation: z.string(),
26
- org_id: z.string(),
27
- contact: z.string().email(),
28
- jurisdiction: z.string()
29
- })
30
- }),
31
- trust_tier: z.number().int().min(0).max(3),
32
- public_key: z.object({ kty: z.string(), crv: z.string(), x: z.string() }),
33
- restrictions: z.object({
34
- allowed_services: z.array(z.string()).optional(),
35
- max_clc_duration_seconds: z.number().int().optional(),
36
- max_delegation_depth: z.number().int().min(0).max(5).optional(),
37
- geo_restrictions: z.array(z.string()).optional()
38
- }).default({}),
39
- ia_signature: z.string()
40
- })
41
-
42
- // ── Intent Manifest Schema ────────────────────────────────────────────────
43
-
44
- export const ManifestSchema = z.object({
45
- manifest_version: z.literal('1.0'),
46
- manifest_id: z.string().uuid(),
47
- requesting_aid: z.string().uuid(),
48
- target: z.object({
49
- service_id: z.string(),
50
- requested_scopes: z.array(z.string()).min(1),
51
- minimum_scopes: z.array(z.string()).optional(),
52
- environment: z.enum(['production', 'staging', 'development']).default('production')
53
- }),
54
- intent: z.object({
55
- summary: z.string().max(500),
56
- task_type: z.enum(['read', 'write', 'read_write', 'admin', 'transact', 'notify']),
57
- data_classification: z.enum(['public', 'internal', 'confidential', 'restricted']).default('internal'),
58
- automated: z.boolean().default(true),
59
- reversible: z.boolean().default(true),
60
- human_visible: z.boolean().default(false)
61
- }),
62
- duration: z.object({
63
- max_duration_seconds: z.number().int().min(60).max(604800),
64
- idle_timeout_seconds: z.number().int().min(60).default(3600)
65
- }),
66
- data_handling: z.object({
67
- pii_processing: z.boolean().default(false),
68
- cross_border_transfer: z.boolean().default(false),
69
- deletion_on_completion: z.boolean().default(false)
70
- }).default({}),
71
- agent_signature: z.string()
72
- })
73
-
74
- // ── CLC Schema (Credential Lifecycle Contract) ────────────────────────────
75
-
76
- export const CLCSchema = z.object({
77
- clc_version: z.literal('1.0'),
78
- clc_id: z.string().uuid(),
79
- beneficiary_aid: z.string().uuid(),
80
- manifest_id: z.string().uuid(),
81
- manifest_hash: z.string(),
82
- credential_envelope: z.object({
83
- algorithm: z.string(),
84
- epk: z.object({ kty: z.string(), crv: z.string(), x: z.string() }),
85
- ciphertext: z.string(),
86
- nonce: z.string(),
87
- tag: z.string()
88
- }),
89
- granted_scopes: z.array(z.string()),
90
- scope_ceiling: z.array(z.string()),
91
- validity: z.object({
92
- not_before: z.string().datetime(),
93
- not_after: z.string().datetime(),
94
- idle_timeout_seconds: z.number().int().default(3600)
95
- }),
96
- rotation_policy: z.object({
97
- rotation_interval_seconds: z.number().int(),
98
- rotation_overlap_seconds: z.number().int(),
99
- key_derivation_function: z.string().default('HKDF-SHA256')
100
- }),
101
- delegation_policy: z.object({
102
- delegation_permitted: z.boolean(),
103
- max_delegation_depth: z.number().int().min(0).max(5),
104
- max_further_delegation: z.number().int().min(0).default(0)
105
- }),
106
- chain_provenance: z.array(z.string()).default([]),
107
- provider_signature: z.string(),
108
- hub_binding: z.object({
109
- hub_id: z.string(),
110
- hub_signature: z.string()
111
- })
112
- })
113
-
114
- // ── Service Provider Schema (NEW — fixes audit 3.1) ──────────────────────
115
-
116
- export const ServiceProviderSchema = z.object({
117
- sp_id: z.string().min(1).max(128).regex(/^[a-z0-9][a-z0-9._-]*$/, 'sp_id must be lowercase alphanumeric with dots/hyphens/underscores'),
118
- sp_name: z.string().min(1).max(256),
119
- service_url: z.string().url(),
120
- credential_endpoint: z.string().url(),
121
- policy_endpoint: z.string().url().optional(),
122
- public_key_jwk: z.object({ kty: z.string(), crv: z.string(), x: z.string() }),
123
- supported_scopes: z.array(z.string()).default([])
124
- })
125
-
126
- // ── APL Policy Schema ─────────────────────────────────────────────────────
127
-
128
- export const PolicyConditionSchema = z.lazy(() => z.discriminatedUnion('type', [
129
- z.object({ type: z.literal('trust_tier'), operator: z.enum(['eq', 'gt', 'gte', 'lt', 'lte']), value: z.number() }),
130
- z.object({ type: z.literal('scope'), operator: z.enum(['contains_any', 'contains_all', 'subset_of']), values: z.array(z.string()) }),
131
- z.object({ type: z.literal('intent_type'), operator: z.enum(['eq', 'in']), value: z.string().optional(), values: z.array(z.string()).optional() }),
132
- z.object({ type: z.literal('data_classification'), operator: z.enum(['eq', 'gte', 'lte']), value: z.string() }),
133
- z.object({ type: z.literal('pii_processing'), value: z.boolean() }),
134
- z.object({ type: z.literal('environment'), operator: z.enum(['eq', 'in']), value: z.string().optional(), values: z.array(z.string()).optional() }),
135
- z.object({ type: z.literal('time_window'), start_hour: z.number().optional(), end_hour: z.number().optional(), days_of_week: z.array(z.string()).optional() }),
136
- z.object({ type: z.literal('geography'), operator: z.enum(['agent_in', 'agent_not_in', 'restricted_to']), values: z.array(z.string()) }),
137
- z.object({ type: z.literal('and'), conditions: z.array(PolicyConditionSchema) }),
138
- z.object({ type: z.literal('or'), conditions: z.array(PolicyConditionSchema) }),
139
- z.object({ type: z.literal('not'), condition: PolicyConditionSchema }),
140
- ]))
141
-
142
- export const PolicyRuleSchema = z.object({
143
- rule_id: z.string(),
144
- priority: z.number().int().default(99),
145
- condition: PolicyConditionSchema.optional(),
146
- action: z.enum(['approve', 'reject', 'review']),
147
- description: z.string().optional()
148
- })
149
-
150
- export const PolicyDocSchema = z.object({
151
- rules: z.array(PolicyRuleSchema),
152
- default_action: z.enum(['approve', 'reject', 'review']).default('reject')
153
- })