@elevasis/core 0.21.0 → 0.22.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.d.ts +416 -6
- package/dist/index.js +240 -15
- package/dist/knowledge/index.d.ts +97 -1
- package/dist/organization-model/index.d.ts +416 -6
- package/dist/organization-model/index.js +240 -15
- package/dist/test-utils/index.d.ts +216 -1
- package/dist/test-utils/index.js +230 -14
- package/package.json +3 -3
- package/src/_gen/__tests__/__snapshots__/contracts.md.snap +495 -302
- package/src/auth/multi-tenancy/permissions.ts +20 -8
- package/src/business/README.md +2 -2
- package/src/business/acquisition/api-schemas.test.ts +173 -0
- package/src/business/acquisition/api-schemas.ts +125 -7
- package/src/business/acquisition/index.ts +12 -0
- package/src/business/clients/api-schemas.test.ts +115 -0
- package/src/business/clients/api-schemas.ts +158 -0
- package/src/business/clients/index.ts +1 -0
- package/src/business/deals/api-schemas.ts +8 -0
- package/src/business/index.ts +5 -2
- package/src/business/projects/types.ts +19 -0
- package/src/execution/engine/__tests__/fixtures/test-agents.ts +10 -8
- package/src/execution/engine/agent/core/__tests__/agent.test.ts +16 -12
- package/src/execution/engine/agent/core/__tests__/error-passthrough.test.ts +4 -3
- package/src/execution/engine/agent/core/types.ts +25 -15
- package/src/execution/engine/agent/index.ts +6 -4
- package/src/execution/engine/agent/reasoning/__tests__/request-builder.test.ts +24 -18
- package/src/execution/engine/index.ts +3 -0
- package/src/execution/engine/workflow/types.ts +7 -0
- package/src/organization-model/README.md +10 -3
- package/src/organization-model/__tests__/defaults.test.ts +6 -0
- package/src/organization-model/__tests__/domains/resources.test.ts +188 -0
- package/src/organization-model/__tests__/domains/roles.test.ts +402 -347
- package/src/organization-model/__tests__/domains/systems.test.ts +193 -0
- package/src/organization-model/__tests__/knowledge.test.ts +39 -0
- package/src/organization-model/__tests__/resolve.test.ts +1 -1
- package/src/organization-model/defaults.ts +24 -3
- package/src/organization-model/domains/knowledge.ts +3 -2
- package/src/organization-model/domains/resources.ts +88 -0
- package/src/organization-model/domains/roles.ts +93 -55
- package/src/organization-model/domains/systems.ts +46 -0
- package/src/organization-model/icons.ts +1 -0
- package/src/organization-model/index.ts +2 -0
- package/src/organization-model/organization-model.mdx +33 -14
- package/src/organization-model/published.ts +52 -1
- package/src/organization-model/schema.ts +121 -0
- package/src/organization-model/types.ts +46 -1
- package/src/platform/api/types.ts +38 -35
- package/src/platform/registry/__tests__/resource-registry.test.ts +2051 -2005
- package/src/platform/registry/__tests__/validation.test.ts +1343 -1086
- package/src/platform/registry/index.ts +14 -0
- package/src/platform/registry/resource-registry.ts +40 -2
- package/src/platform/registry/serialization.ts +241 -202
- package/src/platform/registry/serialized-types.ts +1 -0
- package/src/platform/registry/types.ts +411 -361
- package/src/platform/registry/validation.ts +743 -513
- package/src/projects/api-schemas.ts +290 -267
- package/src/reference/_generated/contracts.md +495 -302
- package/src/reference/glossary.md +8 -3
- package/src/supabase/database.types.ts +121 -0
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import {
|
|
3
|
+
DEFAULT_ORGANIZATION_MODEL_SYSTEMS,
|
|
4
|
+
SystemEntrySchema,
|
|
5
|
+
SystemKindSchema,
|
|
6
|
+
SystemsDomainSchema,
|
|
7
|
+
SystemStatusSchema
|
|
8
|
+
} from '../../domains/systems'
|
|
9
|
+
import { resolveOrganizationModel } from '../../resolve'
|
|
10
|
+
|
|
11
|
+
const VALID_SYSTEM = {
|
|
12
|
+
id: 'sys.lead-gen',
|
|
13
|
+
title: 'Lead Generation Pipeline',
|
|
14
|
+
description: 'Coordinates prospecting, enrichment, qualification, and outreach preparation.',
|
|
15
|
+
kind: 'operational' as const,
|
|
16
|
+
status: 'active' as const
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
describe('SystemEntrySchema - positive parse', () => {
|
|
20
|
+
it('accepts a minimal tenant-defined system', () => {
|
|
21
|
+
const result = SystemEntrySchema.safeParse(VALID_SYSTEM)
|
|
22
|
+
|
|
23
|
+
expect(result.success).toBe(true)
|
|
24
|
+
if (result.success) {
|
|
25
|
+
expect(result.data.governedByKnowledge).toEqual([])
|
|
26
|
+
expect(result.data.drivesGoals).toEqual([])
|
|
27
|
+
}
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
it('accepts all optional governance references when present', () => {
|
|
31
|
+
const result = SystemEntrySchema.safeParse({
|
|
32
|
+
...VALID_SYSTEM,
|
|
33
|
+
responsibleRoleId: 'role.sales-ops',
|
|
34
|
+
governedByKnowledge: ['knowledge.lead-gen-playbook'],
|
|
35
|
+
drivesGoals: ['goal.pipeline-coverage']
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
expect(result.success).toBe(true)
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
it('trims id, title, and description', () => {
|
|
42
|
+
const result = SystemEntrySchema.safeParse({
|
|
43
|
+
...VALID_SYSTEM,
|
|
44
|
+
id: ' sys.crm ',
|
|
45
|
+
title: ' CRM ',
|
|
46
|
+
description: ' Owns relationship and deal pipeline operations. '
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
expect(result.success).toBe(true)
|
|
50
|
+
if (result.success) {
|
|
51
|
+
expect(result.data.id).toBe('sys.crm')
|
|
52
|
+
expect(result.data.title).toBe('CRM')
|
|
53
|
+
expect(result.data.description).toBe('Owns relationship and deal pipeline operations.')
|
|
54
|
+
}
|
|
55
|
+
})
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
describe('SystemEntrySchema - negative parse', () => {
|
|
59
|
+
it('rejects missing required fields', () => {
|
|
60
|
+
expect(SystemEntrySchema.safeParse({ id: 'sys.missing' }).success).toBe(false)
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
it('rejects an unknown system kind', () => {
|
|
64
|
+
expect(SystemEntrySchema.safeParse({ ...VALID_SYSTEM, kind: 'sales' }).success).toBe(false)
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
it('rejects an unknown system status', () => {
|
|
68
|
+
expect(SystemEntrySchema.safeParse({ ...VALID_SYSTEM, status: 'paused' }).success).toBe(false)
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
it('rejects IDs that do not match the OM model-id format', () => {
|
|
72
|
+
expect(SystemEntrySchema.safeParse({ ...VALID_SYSTEM, id: 'Sys Lead Gen' }).success).toBe(false)
|
|
73
|
+
})
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
describe('SystemsDomainSchema', () => {
|
|
77
|
+
it('accepts an empty systems array', () => {
|
|
78
|
+
expect(SystemsDomainSchema.safeParse({ systems: [] }).success).toBe(true)
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
it('defaults systems to an empty array when omitted', () => {
|
|
82
|
+
const result = SystemsDomainSchema.safeParse({})
|
|
83
|
+
|
|
84
|
+
expect(result.success).toBe(true)
|
|
85
|
+
if (result.success) {
|
|
86
|
+
expect(result.data).toEqual(DEFAULT_ORGANIZATION_MODEL_SYSTEMS)
|
|
87
|
+
}
|
|
88
|
+
})
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
describe('SystemKindSchema and SystemStatusSchema', () => {
|
|
92
|
+
it.each(['product', 'operational', 'platform', 'diagnostic'] as const)('accepts kind "%s"', (kind) => {
|
|
93
|
+
expect(SystemKindSchema.safeParse(kind).success).toBe(true)
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
it.each(['active', 'deprecated', 'archived'] as const)('accepts status "%s"', (status) => {
|
|
97
|
+
expect(SystemStatusSchema.safeParse(status).success).toBe(true)
|
|
98
|
+
})
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
describe('resolveOrganizationModel - systems domain integration', () => {
|
|
102
|
+
it('omitting systems resolves to the empty default catalog', () => {
|
|
103
|
+
const model = resolveOrganizationModel({})
|
|
104
|
+
|
|
105
|
+
expect(model.systems).toEqual(DEFAULT_ORGANIZATION_MODEL_SYSTEMS)
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
it('accepts valid responsible role, knowledge, and goal references', () => {
|
|
109
|
+
expect(() =>
|
|
110
|
+
resolveOrganizationModel({
|
|
111
|
+
roles: { roles: [{ id: 'role.sales-ops', title: 'Sales Ops' }] },
|
|
112
|
+
goals: {
|
|
113
|
+
objectives: [
|
|
114
|
+
{
|
|
115
|
+
id: 'goal.pipeline-coverage',
|
|
116
|
+
description: 'Improve sales pipeline coverage',
|
|
117
|
+
periodStart: '2026-01-01',
|
|
118
|
+
periodEnd: '2026-12-31'
|
|
119
|
+
}
|
|
120
|
+
]
|
|
121
|
+
},
|
|
122
|
+
knowledge: {
|
|
123
|
+
nodes: [
|
|
124
|
+
{
|
|
125
|
+
id: 'knowledge.lead-gen-playbook',
|
|
126
|
+
kind: 'playbook',
|
|
127
|
+
title: 'Lead Gen Playbook',
|
|
128
|
+
summary: 'How lead generation is governed.',
|
|
129
|
+
body: '## Lead Gen',
|
|
130
|
+
updatedAt: '2026-05-08'
|
|
131
|
+
}
|
|
132
|
+
]
|
|
133
|
+
},
|
|
134
|
+
systems: {
|
|
135
|
+
systems: [
|
|
136
|
+
{
|
|
137
|
+
...VALID_SYSTEM,
|
|
138
|
+
responsibleRoleId: 'role.sales-ops',
|
|
139
|
+
governedByKnowledge: ['knowledge.lead-gen-playbook'],
|
|
140
|
+
drivesGoals: ['goal.pipeline-coverage']
|
|
141
|
+
}
|
|
142
|
+
]
|
|
143
|
+
}
|
|
144
|
+
})
|
|
145
|
+
).not.toThrow()
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
it('throws when system IDs are duplicated', () => {
|
|
149
|
+
expect(() =>
|
|
150
|
+
resolveOrganizationModel({
|
|
151
|
+
systems: {
|
|
152
|
+
systems: [
|
|
153
|
+
VALID_SYSTEM,
|
|
154
|
+
{
|
|
155
|
+
...VALID_SYSTEM,
|
|
156
|
+
title: 'Duplicate System'
|
|
157
|
+
}
|
|
158
|
+
]
|
|
159
|
+
}
|
|
160
|
+
})
|
|
161
|
+
).toThrow(/System id \\"sys\.lead-gen\\" must be unique/)
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
it('throws when responsibleRoleId references an unknown role', () => {
|
|
165
|
+
expect(() =>
|
|
166
|
+
resolveOrganizationModel({
|
|
167
|
+
systems: {
|
|
168
|
+
systems: [{ ...VALID_SYSTEM, responsibleRoleId: 'role.missing' }]
|
|
169
|
+
}
|
|
170
|
+
})
|
|
171
|
+
).toThrow(/unknown responsibleRoleId \\"role\.missing\\"/)
|
|
172
|
+
})
|
|
173
|
+
|
|
174
|
+
it('throws when governedByKnowledge references an unknown knowledge node', () => {
|
|
175
|
+
expect(() =>
|
|
176
|
+
resolveOrganizationModel({
|
|
177
|
+
systems: {
|
|
178
|
+
systems: [{ ...VALID_SYSTEM, governedByKnowledge: ['knowledge.missing'] }]
|
|
179
|
+
}
|
|
180
|
+
})
|
|
181
|
+
).toThrow(/unknown knowledge node \\"knowledge\.missing\\"/)
|
|
182
|
+
})
|
|
183
|
+
|
|
184
|
+
it('throws when drivesGoals references an unknown goal', () => {
|
|
185
|
+
expect(() =>
|
|
186
|
+
resolveOrganizationModel({
|
|
187
|
+
systems: {
|
|
188
|
+
systems: [{ ...VALID_SYSTEM, drivesGoals: ['goal.missing'] }]
|
|
189
|
+
}
|
|
190
|
+
})
|
|
191
|
+
).toThrow(/unknown goal \\"goal\.missing\\"/)
|
|
192
|
+
})
|
|
193
|
+
})
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
import { OrganizationGraphEdgeKindSchema } from '../graph/schema'
|
|
9
9
|
import { buildOrganizationGraph } from '../graph/build'
|
|
10
10
|
import { DEFAULT_ORGANIZATION_MODEL, DEFAULT_ORGANIZATION_MODEL_KNOWLEDGE } from '../defaults'
|
|
11
|
+
import { resolveOrganizationModel } from '../resolve'
|
|
11
12
|
import type { OrganizationModel } from '../types'
|
|
12
13
|
|
|
13
14
|
// ---------------------------------------------------------------------------
|
|
@@ -74,6 +75,15 @@ describe('OrgKnowledgeNodeSchema', () => {
|
|
|
74
75
|
expect(node.ownerIds).toEqual([])
|
|
75
76
|
})
|
|
76
77
|
|
|
78
|
+
it('accepts role ids as knowledge owners', () => {
|
|
79
|
+
const node = OrgKnowledgeNodeSchema.parse({
|
|
80
|
+
...makeNode('strategy'),
|
|
81
|
+
ownerIds: ['role.ops-lead', 'role.ceo']
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
expect(node.ownerIds).toEqual(['role.ops-lead', 'role.ceo'])
|
|
85
|
+
})
|
|
86
|
+
|
|
77
87
|
it('accepts an optional semantic icon token', () => {
|
|
78
88
|
const node = OrgKnowledgeNodeSchema.parse({
|
|
79
89
|
...makeNode('playbook'),
|
|
@@ -93,6 +103,35 @@ describe('OrgKnowledgeNodeSchema', () => {
|
|
|
93
103
|
})
|
|
94
104
|
})
|
|
95
105
|
|
|
106
|
+
describe('resolveOrganizationModel with knowledge owner roles', () => {
|
|
107
|
+
const node = {
|
|
108
|
+
id: 'knowledge.test-owner',
|
|
109
|
+
kind: 'playbook' as const,
|
|
110
|
+
title: 'Owned Knowledge',
|
|
111
|
+
summary: 'A knowledge node with role ownership.',
|
|
112
|
+
body: '## Body\n\nContent.',
|
|
113
|
+
ownerIds: ['role.ops-lead'],
|
|
114
|
+
updatedAt: '2026-05-08'
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
it('passes when ownerIds reference declared role ids', () => {
|
|
118
|
+
expect(() =>
|
|
119
|
+
resolveOrganizationModel({
|
|
120
|
+
roles: { roles: [{ id: 'role.ops-lead', title: 'Ops Lead' }] },
|
|
121
|
+
knowledge: { nodes: [node] }
|
|
122
|
+
})
|
|
123
|
+
).not.toThrow()
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
it('throws when ownerIds reference unknown role ids', () => {
|
|
127
|
+
expect(() =>
|
|
128
|
+
resolveOrganizationModel({
|
|
129
|
+
knowledge: { nodes: [node] }
|
|
130
|
+
})
|
|
131
|
+
).toThrow(/unknown owner role/)
|
|
132
|
+
})
|
|
133
|
+
})
|
|
134
|
+
|
|
96
135
|
// ---------------------------------------------------------------------------
|
|
97
136
|
// OrgKnowledgeKindSchema — enum shape
|
|
98
137
|
// ---------------------------------------------------------------------------
|
|
@@ -7,7 +7,7 @@ describe('organization-model resolve', () => {
|
|
|
7
7
|
|
|
8
8
|
expect(model.version).toBe(1)
|
|
9
9
|
expect(model.features.find((feature) => feature.id === 'dashboard')?.path).toBe('/')
|
|
10
|
-
expect(model.features.find((feature) => feature.id === 'sales')?.path).
|
|
10
|
+
expect(model.features.find((feature) => feature.id === 'sales')?.path).toBe('/sales')
|
|
11
11
|
expect(model.features.find((feature) => feature.id === 'sales.crm')?.path).toBe('/crm')
|
|
12
12
|
expect(model.features.find((feature) => feature.id === 'admin')?.requiresAdmin).toBe(true)
|
|
13
13
|
expect(model.features.find((feature) => feature.id === 'archive')?.devOnly).toBe(true)
|
|
@@ -20,6 +20,8 @@ import { DEFAULT_ORGANIZATION_MODEL_PROSPECTING } from './domains/prospecting'
|
|
|
20
20
|
import { DEFAULT_ORGANIZATION_MODEL_OPERATIONS } from './domains/operations'
|
|
21
21
|
import { DEFAULT_ORGANIZATION_MODEL_ROLES } from './domains/roles'
|
|
22
22
|
import { DEFAULT_ORGANIZATION_MODEL_GOALS } from './domains/goals'
|
|
23
|
+
import { DEFAULT_ORGANIZATION_MODEL_SYSTEMS } from './domains/systems'
|
|
24
|
+
import { DEFAULT_ORGANIZATION_MODEL_RESOURCES } from './domains/resources'
|
|
23
25
|
import { DEFAULT_ORGANIZATION_MODEL_STATUSES } from './domains/statuses'
|
|
24
26
|
import type { KnowledgeDomain } from './domains/knowledge'
|
|
25
27
|
|
|
@@ -61,6 +63,15 @@ export const DEFAULT_ORGANIZATION_MODEL: OrganizationModel = {
|
|
|
61
63
|
color: 'green',
|
|
62
64
|
icon: 'feature.finance'
|
|
63
65
|
},
|
|
66
|
+
{
|
|
67
|
+
id: 'business',
|
|
68
|
+
label: 'Business',
|
|
69
|
+
description: 'Revenue, client relationships, and project delivery',
|
|
70
|
+
enabled: true,
|
|
71
|
+
color: 'blue',
|
|
72
|
+
icon: 'feature.business',
|
|
73
|
+
uiPosition: 'sidebar-primary'
|
|
74
|
+
},
|
|
64
75
|
{
|
|
65
76
|
id: 'sales',
|
|
66
77
|
label: 'Sales',
|
|
@@ -68,7 +79,7 @@ export const DEFAULT_ORGANIZATION_MODEL: OrganizationModel = {
|
|
|
68
79
|
enabled: true,
|
|
69
80
|
color: 'blue',
|
|
70
81
|
icon: 'feature.sales',
|
|
71
|
-
|
|
82
|
+
path: '/sales'
|
|
72
83
|
},
|
|
73
84
|
{
|
|
74
85
|
id: 'sales.crm',
|
|
@@ -95,8 +106,16 @@ export const DEFAULT_ORGANIZATION_MODEL: OrganizationModel = {
|
|
|
95
106
|
enabled: true,
|
|
96
107
|
color: 'orange',
|
|
97
108
|
icon: 'feature.projects',
|
|
98
|
-
path: '/projects'
|
|
99
|
-
|
|
109
|
+
path: '/projects'
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
id: 'clients',
|
|
113
|
+
label: 'Clients',
|
|
114
|
+
description: 'Client relationships, accounts, and business context',
|
|
115
|
+
enabled: true,
|
|
116
|
+
color: 'orange',
|
|
117
|
+
icon: 'feature.projects',
|
|
118
|
+
path: '/business/clients'
|
|
100
119
|
},
|
|
101
120
|
{
|
|
102
121
|
id: 'operations',
|
|
@@ -341,6 +360,8 @@ export const DEFAULT_ORGANIZATION_MODEL: OrganizationModel = {
|
|
|
341
360
|
offerings: DEFAULT_ORGANIZATION_MODEL_OFFERINGS,
|
|
342
361
|
roles: DEFAULT_ORGANIZATION_MODEL_ROLES,
|
|
343
362
|
goals: DEFAULT_ORGANIZATION_MODEL_GOALS,
|
|
363
|
+
systems: DEFAULT_ORGANIZATION_MODEL_SYSTEMS,
|
|
364
|
+
resources: DEFAULT_ORGANIZATION_MODEL_RESOURCES,
|
|
344
365
|
statuses: DEFAULT_ORGANIZATION_MODEL_STATUSES,
|
|
345
366
|
operations: DEFAULT_ORGANIZATION_MODEL_OPERATIONS,
|
|
346
367
|
knowledge: DEFAULT_ORGANIZATION_MODEL_KNOWLEDGE
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { z } from 'zod'
|
|
2
2
|
import { IconNameSchema, ModelIdSchema } from './shared'
|
|
3
3
|
import { NodeIdStringSchema } from './features'
|
|
4
|
+
import { RoleIdSchema } from './roles'
|
|
4
5
|
|
|
5
6
|
// ---------------------------------------------------------------------------
|
|
6
7
|
// KnowledgeLink — a typed graph link pointing to another OM node.
|
|
@@ -40,8 +41,8 @@ export const OrgKnowledgeNodeSchema = z.object({
|
|
|
40
41
|
skills: z.array(KnowledgeSkillBindingSchema).optional(),
|
|
41
42
|
/** Domain key used to derive fast graph->skill registries. */
|
|
42
43
|
domain: KnowledgeDomainBindingSchema.optional(),
|
|
43
|
-
/**
|
|
44
|
-
ownerIds: z.array(
|
|
44
|
+
/** Role identifiers that own this knowledge node. */
|
|
45
|
+
ownerIds: z.array(RoleIdSchema).default([]),
|
|
45
46
|
/** ISO date string (YYYY-MM-DD or full ISO 8601) of last meaningful update. */
|
|
46
47
|
updatedAt: z.string().trim().min(1).max(50)
|
|
47
48
|
})
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
import { ModelIdSchema } from './shared'
|
|
3
|
+
import { SystemIdSchema } from './systems'
|
|
4
|
+
|
|
5
|
+
// ---------------------------------------------------------------------------
|
|
6
|
+
// Resources domain
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
//
|
|
9
|
+
// Resources are governance-only OM descriptors. Runtime code imports these
|
|
10
|
+
// descriptors and attaches executable behavior; it does not re-author identity.
|
|
11
|
+
|
|
12
|
+
export const ResourceKindSchema = z.enum(['workflow', 'agent', 'integration'])
|
|
13
|
+
export const ResourceGovernanceStatusSchema = z.enum(['active', 'deprecated', 'archived'])
|
|
14
|
+
export const AgentKindSchema = z.enum(['orchestrator', 'specialist', 'utility', 'system'])
|
|
15
|
+
|
|
16
|
+
export const ResourceIdSchema = z
|
|
17
|
+
.string()
|
|
18
|
+
.trim()
|
|
19
|
+
.min(1)
|
|
20
|
+
.max(255)
|
|
21
|
+
.regex(/^[A-Za-z0-9]+(?:[-._][A-Za-z0-9]+)*$/, 'Resource IDs must use letters, numbers, -, _, or . separators')
|
|
22
|
+
|
|
23
|
+
const ResourceEntryBaseSchema = z.object({
|
|
24
|
+
/** Canonical resource id; runtime resourceId derives from this value. */
|
|
25
|
+
id: ResourceIdSchema,
|
|
26
|
+
/** Required single System membership. */
|
|
27
|
+
systemId: SystemIdSchema,
|
|
28
|
+
/** Optional role responsible for maintaining this resource. */
|
|
29
|
+
ownerRoleId: ModelIdSchema.optional(),
|
|
30
|
+
status: ResourceGovernanceStatusSchema
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
export const WorkflowResourceEntrySchema = ResourceEntryBaseSchema.extend({
|
|
34
|
+
kind: z.literal('workflow'),
|
|
35
|
+
/** Mirrors WorkflowConfig.capabilityKey when the runtime workflow has one. */
|
|
36
|
+
capabilityKey: z.string().trim().min(1).max(255).optional()
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
export const AgentResourceEntrySchema = ResourceEntryBaseSchema.extend({
|
|
40
|
+
kind: z.literal('agent'),
|
|
41
|
+
/** Mirrors code-side AgentConfig.kind. */
|
|
42
|
+
agentKind: AgentKindSchema,
|
|
43
|
+
/** Role this agent embodies, if any. */
|
|
44
|
+
actsAsRoleId: ModelIdSchema.optional(),
|
|
45
|
+
/** Mirrors AgentConfig.sessionCapable. */
|
|
46
|
+
sessionCapable: z.boolean()
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
export const IntegrationResourceEntrySchema = ResourceEntryBaseSchema.extend({
|
|
50
|
+
kind: z.literal('integration'),
|
|
51
|
+
provider: z.string().trim().min(1).max(100)
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
export const ResourceEntrySchema = z.discriminatedUnion('kind', [
|
|
55
|
+
WorkflowResourceEntrySchema,
|
|
56
|
+
AgentResourceEntrySchema,
|
|
57
|
+
IntegrationResourceEntrySchema
|
|
58
|
+
])
|
|
59
|
+
|
|
60
|
+
export const ResourcesDomainSchema = z.object({
|
|
61
|
+
entries: z.array(ResourceEntrySchema).default([])
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
export const DEFAULT_ORGANIZATION_MODEL_RESOURCES: z.infer<typeof ResourcesDomainSchema> = {
|
|
65
|
+
entries: []
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function defineResource<const TResource extends ResourceEntry>(resource: TResource): TResource {
|
|
69
|
+
return ResourceEntrySchema.parse(resource) as TResource
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function defineResources<const TResources extends Record<string, ResourceEntry>>(resources: TResources): TResources {
|
|
73
|
+
for (const resource of Object.values(resources)) {
|
|
74
|
+
ResourceEntrySchema.parse(resource)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return resources
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export type ResourceId = z.infer<typeof ResourceIdSchema>
|
|
81
|
+
export type ResourceKind = z.infer<typeof ResourceKindSchema>
|
|
82
|
+
export type ResourceGovernanceStatus = z.infer<typeof ResourceGovernanceStatusSchema>
|
|
83
|
+
export type ResourceAgentKind = z.infer<typeof AgentKindSchema>
|
|
84
|
+
export type WorkflowResourceEntry = z.infer<typeof WorkflowResourceEntrySchema>
|
|
85
|
+
export type AgentResourceEntry = z.infer<typeof AgentResourceEntrySchema>
|
|
86
|
+
export type IntegrationResourceEntry = z.infer<typeof IntegrationResourceEntrySchema>
|
|
87
|
+
export type ResourceEntry = z.infer<typeof ResourceEntrySchema>
|
|
88
|
+
export type ResourcesDomain = z.infer<typeof ResourcesDomainSchema>
|
|
@@ -1,55 +1,93 @@
|
|
|
1
|
-
import { z } from 'zod'
|
|
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
|
-
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
import { ResourceIdSchema } from './resources'
|
|
3
|
+
import { ModelIdSchema } from './shared'
|
|
4
|
+
import { SystemIdSchema } from './systems'
|
|
5
|
+
|
|
6
|
+
// ---------------------------------------------------------------------------
|
|
7
|
+
// Role schema - one entry per distinct role in the organization's chart.
|
|
8
|
+
// Inspired by the EOS Accountability Chart but uses plain-language field names
|
|
9
|
+
// throughout. No EOS jargon: "title" (not seatTitle), "responsibilities"
|
|
10
|
+
// (not accountabilities), "reportsToId", "heldBy".
|
|
11
|
+
//
|
|
12
|
+
// Cross-reference enforcement lives in `OrganizationModelSchema.superRefine()`:
|
|
13
|
+
// `reportsToId` must resolve to another Role, `responsibleFor` entries must
|
|
14
|
+
// resolve to Systems, and agent holders must resolve to Agent Resources.
|
|
15
|
+
// Cycle detection is NOT enforced (known limitation; document if needed).
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
|
|
18
|
+
export const RoleIdSchema = ModelIdSchema
|
|
19
|
+
|
|
20
|
+
export const HumanRoleHolderSchema = z.object({
|
|
21
|
+
kind: z.literal('human'),
|
|
22
|
+
userId: z.string().trim().min(1).max(200)
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
export const AgentRoleHolderSchema = z.object({
|
|
26
|
+
kind: z.literal('agent'),
|
|
27
|
+
agentId: ResourceIdSchema
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
export const TeamRoleHolderSchema = z.object({
|
|
31
|
+
kind: z.literal('team'),
|
|
32
|
+
memberIds: z.array(z.string().trim().min(1).max(200)).min(1)
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
export const RoleHolderSchema = z.discriminatedUnion('kind', [
|
|
36
|
+
HumanRoleHolderSchema,
|
|
37
|
+
AgentRoleHolderSchema,
|
|
38
|
+
TeamRoleHolderSchema
|
|
39
|
+
])
|
|
40
|
+
|
|
41
|
+
export const RoleHoldersSchema = z.union([RoleHolderSchema, z.array(RoleHolderSchema).min(1)])
|
|
42
|
+
|
|
43
|
+
export const RoleSchema = z.object({
|
|
44
|
+
/** Stable unique identifier for the role (e.g. "role-ceo", "role-head-of-sales"). */
|
|
45
|
+
id: RoleIdSchema,
|
|
46
|
+
/** Human-readable title shown to agents and in UI (e.g. "CEO", "Head of Sales"). */
|
|
47
|
+
title: z.string().trim().min(1).max(200),
|
|
48
|
+
/**
|
|
49
|
+
* List of responsibilities this role owns - plain-language descriptions of
|
|
50
|
+
* what the person in this role is accountable for delivering.
|
|
51
|
+
* Defaults to empty array so minimal role definitions stay concise.
|
|
52
|
+
*/
|
|
53
|
+
responsibilities: z.array(z.string().trim().max(500)).default([]),
|
|
54
|
+
/**
|
|
55
|
+
* Optional: ID of another role this role reports to.
|
|
56
|
+
* When present, must reference another `roles[].id` in the same organization.
|
|
57
|
+
*/
|
|
58
|
+
reportsToId: RoleIdSchema.optional(),
|
|
59
|
+
/**
|
|
60
|
+
* Optional: human, agent, or team holder currently filling this role.
|
|
61
|
+
* Agent holders reference OM Resource IDs and are validated at the model level.
|
|
62
|
+
*/
|
|
63
|
+
heldBy: RoleHoldersSchema.optional(),
|
|
64
|
+
/**
|
|
65
|
+
* Optional Systems this role is accountable for.
|
|
66
|
+
* Cross-reference enforced in `OrganizationModelSchema.superRefine()`.
|
|
67
|
+
*/
|
|
68
|
+
responsibleFor: z.array(SystemIdSchema).optional()
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
// ---------------------------------------------------------------------------
|
|
72
|
+
// Roles domain schema - a collection of roles.
|
|
73
|
+
// ---------------------------------------------------------------------------
|
|
74
|
+
|
|
75
|
+
export const RolesDomainSchema = z.object({
|
|
76
|
+
roles: z.array(RoleSchema).default([])
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
// ---------------------------------------------------------------------------
|
|
80
|
+
// Seed - empty by default; adapters populate with real role definitions.
|
|
81
|
+
// ---------------------------------------------------------------------------
|
|
82
|
+
|
|
83
|
+
export const DEFAULT_ORGANIZATION_MODEL_ROLES: z.infer<typeof RolesDomainSchema> = {
|
|
84
|
+
roles: []
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export type RoleId = z.infer<typeof RoleIdSchema>
|
|
88
|
+
export type HumanRoleHolder = z.infer<typeof HumanRoleHolderSchema>
|
|
89
|
+
export type AgentRoleHolder = z.infer<typeof AgentRoleHolderSchema>
|
|
90
|
+
export type TeamRoleHolder = z.infer<typeof TeamRoleHolderSchema>
|
|
91
|
+
export type RoleHolder = z.infer<typeof RoleHolderSchema>
|
|
92
|
+
export type Role = z.infer<typeof RoleSchema>
|
|
93
|
+
export type RolesDomain = z.infer<typeof RolesDomainSchema>
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
import { DescriptionSchema, LabelSchema, ModelIdSchema, ReferenceIdsSchema } from './shared'
|
|
3
|
+
|
|
4
|
+
// ---------------------------------------------------------------------------
|
|
5
|
+
// Systems domain
|
|
6
|
+
// ---------------------------------------------------------------------------
|
|
7
|
+
//
|
|
8
|
+
// A System is a tenant-defined bounded context that groups operational
|
|
9
|
+
// resources and carries governance metadata. The shared schema validates the
|
|
10
|
+
// shape and references; each tenant supplies its own catalog.
|
|
11
|
+
|
|
12
|
+
export const SystemKindSchema = z.enum(['product', 'operational', 'platform', 'diagnostic'])
|
|
13
|
+
export const SystemStatusSchema = z.enum(['active', 'deprecated', 'archived'])
|
|
14
|
+
export const SystemIdSchema = ModelIdSchema
|
|
15
|
+
|
|
16
|
+
export const SystemEntrySchema = z.object({
|
|
17
|
+
/** Stable tenant-defined system id (e.g. "sys.lead-gen"). */
|
|
18
|
+
id: SystemIdSchema,
|
|
19
|
+
/** Human-readable system title shown in governance and operations UI. */
|
|
20
|
+
title: LabelSchema,
|
|
21
|
+
/** One-paragraph purpose statement for the bounded context. */
|
|
22
|
+
description: DescriptionSchema,
|
|
23
|
+
/** Closed system shape enum; catalog values remain tenant-defined. */
|
|
24
|
+
kind: SystemKindSchema,
|
|
25
|
+
/** Optional role responsible for this system. */
|
|
26
|
+
responsibleRoleId: ModelIdSchema.optional(),
|
|
27
|
+
/** Optional knowledge nodes that govern this system. */
|
|
28
|
+
governedByKnowledge: ReferenceIdsSchema,
|
|
29
|
+
/** Optional goals this system contributes to. */
|
|
30
|
+
drivesGoals: ReferenceIdsSchema,
|
|
31
|
+
status: SystemStatusSchema
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
export const SystemsDomainSchema = z.object({
|
|
35
|
+
systems: z.array(SystemEntrySchema).default([])
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
export const DEFAULT_ORGANIZATION_MODEL_SYSTEMS: z.infer<typeof SystemsDomainSchema> = {
|
|
39
|
+
systems: []
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export type SystemId = z.infer<typeof SystemIdSchema>
|
|
43
|
+
export type SystemKind = z.infer<typeof SystemKindSchema>
|
|
44
|
+
export type SystemStatus = z.infer<typeof SystemStatusSchema>
|
|
45
|
+
export type SystemEntry = z.infer<typeof SystemEntrySchema>
|
|
46
|
+
export type SystemsDomain = z.infer<typeof SystemsDomainSchema>
|