@elevasis/core 0.42.1 → 0.44.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/auth/index.d.ts +8 -3
- package/dist/auth/index.js +6 -0
- package/dist/business/entities-published.d.ts +1 -1
- package/dist/index.d.ts +12 -13
- package/dist/index.js +48 -29
- package/dist/knowledge/index.d.ts +94 -6
- package/dist/knowledge/index.js +172 -8
- package/dist/organization-model/index.d.ts +12 -13
- package/dist/organization-model/index.js +48 -29
- package/dist/test-utils/index.d.ts +5 -6
- package/dist/test-utils/index.js +21 -18
- package/package.json +3 -3
- package/src/auth/access-keys.ts +6 -0
- package/src/business/acquisition/api-schemas.ts +1 -1
- package/src/business/base-entities.ts +1 -1
- package/src/knowledge/cli-helpers.ts +211 -0
- package/src/knowledge/index.ts +13 -0
- package/src/knowledge/published.ts +18 -5
- package/src/knowledge/queries.ts +5 -5
- package/src/organization-model/__tests__/cross-ref.test.ts +11 -1
- package/src/organization-model/__tests__/domains/systems.test.ts +34 -8
- package/src/organization-model/__tests__/scaffolders.test.ts +30 -1
- package/src/organization-model/__tests__/schema-refinements.test.ts +178 -0
- package/src/organization-model/cross-ref.ts +43 -7
- package/src/organization-model/defaults.ts +2 -2
- package/src/organization-model/domains/actions.ts +1 -1
- package/src/organization-model/domains/resources.ts +1 -1
- package/src/organization-model/domains/systems.ts +0 -4
- package/src/organization-model/ontology.ts +13 -18
- package/src/organization-model/organization-graph.mdx +9 -8
- package/src/organization-model/published.ts +9 -3
- package/src/organization-model/resolve.ts +9 -7
- package/src/organization-model/scaffolders/helpers.ts +1 -1
- package/src/organization-model/scaffolders/scaffoldKnowledgeNode.ts +1 -0
- package/src/organization-model/scaffolders/scaffoldOntologyRecord.ts +28 -6
- package/src/organization-model/scaffolders/scaffoldResource.ts +1 -0
- package/src/organization-model/scaffolders/scaffoldSystem.ts +2 -1
- package/src/organization-model/schema-refinements.ts +3 -5
- package/src/platform/registry/__tests__/validation.test.ts +28 -0
- package/src/platform/registry/validation.ts +20 -2
- package/src/scaffold-registry/__tests__/index.test.ts +380 -206
- package/src/scaffold-registry/index.ts +392 -381
- package/src/test-utils/mocks/supabase.ts +1 -1
- package/src/test-utils/mocks/workos.ts +2 -2
package/dist/test-utils/index.js
CHANGED
|
@@ -19426,16 +19426,13 @@ var OntologyKindSchema = z.enum([
|
|
|
19426
19426
|
"value-type",
|
|
19427
19427
|
"property",
|
|
19428
19428
|
"group",
|
|
19429
|
-
"
|
|
19429
|
+
"endpoint"
|
|
19430
19430
|
]);
|
|
19431
19431
|
var SYSTEM_PATH_PATTERN = "[a-z0-9][a-z0-9-]*(?:\\.[a-z0-9][a-z0-9-]*)*";
|
|
19432
19432
|
var LOCAL_ID_PATTERN = "[a-z0-9][a-z0-9._-]*";
|
|
19433
19433
|
var ONTOLOGY_ID_PATTERN = `^(global|${SYSTEM_PATH_PATTERN}):(${OntologyKindSchema.options.join("|")})\\/(${LOCAL_ID_PATTERN})$`;
|
|
19434
19434
|
var ONTOLOGY_ID_REGEX = new RegExp(ONTOLOGY_ID_PATTERN);
|
|
19435
|
-
var OntologyIdSchema = z.string().trim().min(1).max(300).regex(
|
|
19436
|
-
ONTOLOGY_ID_REGEX,
|
|
19437
|
-
"Ontology IDs must use <system-path>:<kind>/<local-id> or global:<kind>/<local-id>"
|
|
19438
|
-
);
|
|
19435
|
+
var OntologyIdSchema = z.string().trim().min(1).max(300).regex(ONTOLOGY_ID_REGEX, "Ontology IDs must use <system-path>:<kind>/<local-id> or global:<kind>/<local-id>");
|
|
19439
19436
|
function parseOntologyId(id) {
|
|
19440
19437
|
const normalized = OntologyIdSchema.parse(id);
|
|
19441
19438
|
const match = ONTOLOGY_ID_REGEX.exec(normalized);
|
|
@@ -19498,7 +19495,7 @@ var OntologySharedPropertySchema = OntologyRecordBaseSchema.extend({
|
|
|
19498
19495
|
var OntologyGroupSchema = OntologyRecordBaseSchema.extend({
|
|
19499
19496
|
members: OntologyReferenceListSchema
|
|
19500
19497
|
});
|
|
19501
|
-
var
|
|
19498
|
+
var OntologyEndpointTypeSchema = OntologyRecordBaseSchema.extend({
|
|
19502
19499
|
route: z.string().trim().min(1).max(500).optional()
|
|
19503
19500
|
});
|
|
19504
19501
|
var OntologyScopeSchema = z.object({
|
|
@@ -19511,7 +19508,7 @@ var OntologyScopeSchema = z.object({
|
|
|
19511
19508
|
valueTypes: z.record(OntologyIdSchema, OntologyValueTypeSchema).default({}).optional(),
|
|
19512
19509
|
sharedProperties: z.record(OntologyIdSchema, OntologySharedPropertySchema).default({}).optional(),
|
|
19513
19510
|
groups: z.record(OntologyIdSchema, OntologyGroupSchema).default({}).optional(),
|
|
19514
|
-
|
|
19511
|
+
endpoints: z.record(OntologyIdSchema, OntologyEndpointTypeSchema).default({}).optional()
|
|
19515
19512
|
}).default({});
|
|
19516
19513
|
var DEFAULT_ONTOLOGY_SCOPE = {
|
|
19517
19514
|
valueTypes: {
|
|
@@ -19547,7 +19544,7 @@ var SCOPE_KIND = {
|
|
|
19547
19544
|
valueTypes: "value-type",
|
|
19548
19545
|
sharedProperties: "property",
|
|
19549
19546
|
groups: "group",
|
|
19550
|
-
|
|
19547
|
+
endpoints: "endpoint"
|
|
19551
19548
|
};
|
|
19552
19549
|
var SCOPE_KEYS = Object.keys(SCOPE_KIND);
|
|
19553
19550
|
function listResolvedOntologyRecords(index2) {
|
|
@@ -19580,7 +19577,7 @@ function createEmptyIndex() {
|
|
|
19580
19577
|
valueTypes: {},
|
|
19581
19578
|
sharedProperties: {},
|
|
19582
19579
|
groups: {},
|
|
19583
|
-
|
|
19580
|
+
endpoints: {}
|
|
19584
19581
|
};
|
|
19585
19582
|
}
|
|
19586
19583
|
function sortResolvedOntologyIndex(index2) {
|
|
@@ -20415,8 +20412,6 @@ var SystemEntrySchema = z.object({
|
|
|
20415
20412
|
path: PathSchema.optional(),
|
|
20416
20413
|
/** @deprecated Use ui.icon. Kept for one-cycle Feature compatibility. */
|
|
20417
20414
|
icon: IconNameSchema.optional(),
|
|
20418
|
-
/** @deprecated Feature color token, retained for one-cycle compatibility. */
|
|
20419
|
-
color: ColorTokenSchema.optional(),
|
|
20420
20415
|
/** @deprecated UI placement hint, retained for one-cycle compatibility. */
|
|
20421
20416
|
uiPosition: UiPositionSchema.optional(),
|
|
20422
20417
|
/** @deprecated Use lifecycle. */
|
|
@@ -20498,7 +20493,7 @@ var ResourceOntologyBindingSchema = z.object({
|
|
|
20498
20493
|
/**
|
|
20499
20494
|
* Optional typed contract binding for this resource's workflow I/O.
|
|
20500
20495
|
* Each ref is a `package/subpath#ExportName` string that resolves to a
|
|
20501
|
-
* Zod schema in
|
|
20496
|
+
* Zod schema in the tenant-owned organization model package.
|
|
20502
20497
|
*
|
|
20503
20498
|
* Absence of this field preserves all existing behavior — it is additive + optional.
|
|
20504
20499
|
* Tier-1 validation (schema.ts): ref-string shape only (browser-safe, no imports).
|
|
@@ -20939,10 +20934,11 @@ var ONTOLOGY_REFERENCE_KEY_KINDS = {
|
|
|
20939
20934
|
interfaceType: "interface",
|
|
20940
20935
|
propertyType: "property",
|
|
20941
20936
|
groupType: "group",
|
|
20942
|
-
|
|
20937
|
+
endpointType: "endpoint",
|
|
20943
20938
|
stepCatalog: "catalog"
|
|
20944
20939
|
};
|
|
20945
|
-
|
|
20940
|
+
var omCompilationContextCache = /* @__PURE__ */ new WeakMap();
|
|
20941
|
+
function buildOmCrossRefIndexFromOntology(model, ontologyCompilation) {
|
|
20946
20942
|
const systemsById = /* @__PURE__ */ new Map();
|
|
20947
20943
|
for (const { path, system } of listAllSystems(model)) {
|
|
20948
20944
|
systemsById.set(path, system);
|
|
@@ -20956,7 +20952,6 @@ function buildOmCrossRefIndex(model) {
|
|
|
20956
20952
|
const actionIds = new Set(Object.keys(model.actions ?? {}));
|
|
20957
20953
|
const customerSegmentIds = new Set(Object.keys(model.customers ?? {}));
|
|
20958
20954
|
const offeringIds = new Set(Object.keys(model.offerings ?? {}));
|
|
20959
|
-
const ontologyCompilation = compileOrganizationOntology(model);
|
|
20960
20955
|
const ontologyIndexByKind = {
|
|
20961
20956
|
object: ontologyCompilation.ontology.objectTypes,
|
|
20962
20957
|
link: ontologyCompilation.ontology.linkTypes,
|
|
@@ -20967,7 +20962,7 @@ function buildOmCrossRefIndex(model) {
|
|
|
20967
20962
|
"value-type": ontologyCompilation.ontology.valueTypes,
|
|
20968
20963
|
property: ontologyCompilation.ontology.sharedProperties,
|
|
20969
20964
|
group: ontologyCompilation.ontology.groups,
|
|
20970
|
-
|
|
20965
|
+
endpoint: ontologyCompilation.ontology.endpoints
|
|
20971
20966
|
};
|
|
20972
20967
|
const ontologyIds = new Set(Object.values(ontologyIndexByKind).flatMap((index2) => Object.keys(index2)));
|
|
20973
20968
|
const stageIds = /* @__PURE__ */ new Set();
|
|
@@ -20995,6 +20990,15 @@ function buildOmCrossRefIndex(model) {
|
|
|
20995
20990
|
stageIds
|
|
20996
20991
|
};
|
|
20997
20992
|
}
|
|
20993
|
+
function buildOmCompilationContext(model) {
|
|
20994
|
+
const cached = omCompilationContextCache.get(model);
|
|
20995
|
+
if (cached !== void 0) return cached;
|
|
20996
|
+
const ontologyCompilation = compileOrganizationOntology(model);
|
|
20997
|
+
const crossRefIndex = buildOmCrossRefIndexFromOntology(model, ontologyCompilation);
|
|
20998
|
+
const context = { crossRefIndex, ontologyCompilation };
|
|
20999
|
+
omCompilationContextCache.set(model, context);
|
|
21000
|
+
return context;
|
|
21001
|
+
}
|
|
20998
21002
|
function knowledgeTargetExists(index2, kind, id) {
|
|
20999
21003
|
if (kind === "system") return index2.systemsById.has(id);
|
|
21000
21004
|
if (kind === "client") return index2.clientIds.has(id);
|
|
@@ -21331,9 +21335,8 @@ function refineOrganizationModel(model, ctx) {
|
|
|
21331
21335
|
}
|
|
21332
21336
|
});
|
|
21333
21337
|
});
|
|
21334
|
-
const idx =
|
|
21338
|
+
const { crossRefIndex: idx, ontologyCompilation } = buildOmCompilationContext(model);
|
|
21335
21339
|
const { ontologyIndexByKind, ontologyIds } = idx;
|
|
21336
|
-
const ontologyCompilation = compileOrganizationOntology(model);
|
|
21337
21340
|
function topologyTargetExists(ref) {
|
|
21338
21341
|
if (ref.kind === "system") return systemsById.has(ref.id);
|
|
21339
21342
|
if (ref.kind === "resource") return resourcesById.has(ref.id);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elevasis/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.44.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "Minimal shared constants across Elevasis monorepo",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -45,8 +45,8 @@
|
|
|
45
45
|
"rollup-plugin-dts": "^6.3.0",
|
|
46
46
|
"tsup": "^8.0.0",
|
|
47
47
|
"typescript": "5.9.2",
|
|
48
|
-
"@repo/
|
|
49
|
-
"@repo/
|
|
48
|
+
"@repo/typescript-config": "0.0.0",
|
|
49
|
+
"@repo/eslint-config": "0.0.0"
|
|
50
50
|
},
|
|
51
51
|
"dependencies": {
|
|
52
52
|
"@anthropic-ai/sdk": "^0.62.0",
|
package/src/auth/access-keys.ts
CHANGED
|
@@ -51,14 +51,20 @@ export const AccessKeys = {
|
|
|
51
51
|
organizationManage: { systemPath: 'permission.org', action: 'manage' },
|
|
52
52
|
membersManage: { systemPath: 'permission.members', action: 'manage' },
|
|
53
53
|
rolesManage: { systemPath: 'permission.roles', action: 'manage' },
|
|
54
|
+
/** @reserved Reserved for first-class secret administration routes and command surfaces. */
|
|
54
55
|
secretsManage: { systemPath: 'permission.secrets', action: 'manage' },
|
|
55
56
|
operationsRead: { systemPath: 'permission.operations', action: DEFAULT_ACCESS_ACTION },
|
|
57
|
+
/** @reserved Reserved for write-level operations administration beyond read-only monitoring. */
|
|
56
58
|
operationsManage: { systemPath: 'permission.operations', action: 'manage' },
|
|
57
59
|
leadGenManage: { systemPath: 'sales.lead-gen', action: 'manage' },
|
|
60
|
+
/** @reserved Reserved for acquisition administration once that permission namespace is wired. */
|
|
58
61
|
acquisitionManage: { systemPath: 'permission.acquisition', action: 'manage' },
|
|
62
|
+
/** @reserved Reserved for project administration permission catalog compatibility. */
|
|
59
63
|
projectsManage: { systemPath: 'permission.projects', action: 'manage' },
|
|
64
|
+
/** @reserved Reserved for client administration permission catalog compatibility. */
|
|
60
65
|
clientsManage: { systemPath: 'permission.clients', action: 'manage' },
|
|
61
66
|
operationsOverview: { systemPath: 'diagnostic.operations.overview', action: DEFAULT_ACCESS_ACTION },
|
|
67
|
+
/** @reserved Reserved for a diagnostics surface that lists recent operations executions. */
|
|
62
68
|
operationsRecentExecutions: { systemPath: 'diagnostic.operations.recent-executions', action: DEFAULT_ACCESS_ACTION },
|
|
63
69
|
monitoringExecutionLogs: { systemPath: 'diagnostic.monitoring.execution-logs', action: DEFAULT_ACCESS_ACTION },
|
|
64
70
|
monitoringNotifications: { systemPath: 'diagnostic.monitoring.notifications', action: DEFAULT_ACCESS_ACTION }
|
|
@@ -10,7 +10,7 @@ export const LeadGenStageKeySchema = z.string().trim().min(1)
|
|
|
10
10
|
|
|
11
11
|
export const LeadGenActionKeySchema = z.string().trim().min(1)
|
|
12
12
|
|
|
13
|
-
// CRM stage/state catalogs are model-owned (authored in
|
|
13
|
+
// CRM stage/state catalogs are model-owned (authored in the tenant-owned organization model package
|
|
14
14
|
// canonicalOrganizationModel). The published core schema validates only the
|
|
15
15
|
// transport shape; closed-catalog membership is enforced by the caller/API
|
|
16
16
|
// layer via model-injected validators (mirrors LeadGenStageKeySchema).
|
|
@@ -12,7 +12,7 @@ import { z } from 'zod'
|
|
|
12
12
|
* layer; use the row types internally for direct Supabase access.
|
|
13
13
|
*
|
|
14
14
|
* Usage in a client project:
|
|
15
|
-
* import { BaseProject } from '@elevasis/core'
|
|
15
|
+
* import { BaseProject } from '@elevasis/core'
|
|
16
16
|
* type Project = BaseProject<{ budget: number; riskScore: 'low' | 'medium' | 'high' }>
|
|
17
17
|
*
|
|
18
18
|
* Extending a Zod schema in a client project:
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
OrganizationModel,
|
|
3
|
+
OrganizationModelAction,
|
|
4
|
+
OrganizationModelActionInvocation,
|
|
5
|
+
OrganizationModelAgentResourceEntry
|
|
6
|
+
} from '../organization-model/types'
|
|
7
|
+
import { getSystem } from '../organization-model/helpers'
|
|
8
|
+
import type { listAllResources, listAllRoles, listAllSystemsFlat, OmSearchHitKind } from './queries'
|
|
9
|
+
|
|
10
|
+
export const OM_SEARCH_HIT_KINDS: OmSearchHitKind[] = ['system', 'resource', 'knowledge', 'ontology', 'role', 'policy']
|
|
11
|
+
|
|
12
|
+
export function parseKnowledgeSearchLimit(raw: string | undefined, commandName = 'knowledge:search'): number {
|
|
13
|
+
if (raw === undefined) return 10
|
|
14
|
+
const n = Number.parseInt(raw, 10)
|
|
15
|
+
if (Number.isNaN(n) || n < 0) {
|
|
16
|
+
throw new Error(`${commandName}: --limit must be a non-negative integer (got "${raw}")`)
|
|
17
|
+
}
|
|
18
|
+
return n
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function parseKnowledgeSearchKinds(
|
|
22
|
+
raw: string | undefined,
|
|
23
|
+
commandName = 'knowledge:search'
|
|
24
|
+
): OmSearchHitKind[] | undefined {
|
|
25
|
+
if (raw === undefined) return undefined
|
|
26
|
+
const requested = raw
|
|
27
|
+
.split(',')
|
|
28
|
+
.map((s) => s.trim().toLowerCase())
|
|
29
|
+
.filter((s) => s.length > 0)
|
|
30
|
+
const valid: OmSearchHitKind[] = []
|
|
31
|
+
const invalid: string[] = []
|
|
32
|
+
for (const kind of requested) {
|
|
33
|
+
if (OM_SEARCH_HIT_KINDS.includes(kind as OmSearchHitKind)) {
|
|
34
|
+
valid.push(kind as OmSearchHitKind)
|
|
35
|
+
} else {
|
|
36
|
+
invalid.push(kind)
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
if (invalid.length > 0) {
|
|
40
|
+
throw new Error(`${commandName}: unknown kind(s) "${invalid.join(', ')}". Valid: ${OM_SEARCH_HIT_KINDS.join(', ')}`)
|
|
41
|
+
}
|
|
42
|
+
return valid.length > 0 ? valid : undefined
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function formatKnowledgeSystemsList(entries: ReturnType<typeof listAllSystemsFlat>): string {
|
|
46
|
+
if (entries.length === 0) return '(no results)'
|
|
47
|
+
const pathWidth = Math.max(...entries.map((e) => e.path.length), 4)
|
|
48
|
+
const header = `${'PATH'.padEnd(pathWidth)} LABEL`
|
|
49
|
+
const divider = '-'.repeat(header.length + 20)
|
|
50
|
+
const rows = entries.map((e) => {
|
|
51
|
+
const label = e.system.label ?? e.system.title ?? e.path
|
|
52
|
+
return `${e.path.padEnd(pathWidth)} ${label}`
|
|
53
|
+
})
|
|
54
|
+
return [header, divider, ...rows].join('\n')
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function formatKnowledgeResourcesList(resources: ReturnType<typeof listAllResources>): string {
|
|
58
|
+
if (resources.length === 0) return '(no results)'
|
|
59
|
+
const idWidth = Math.max(...resources.map((r) => r.id.length), 4)
|
|
60
|
+
const kindWidth = Math.max(...resources.map((r) => r.kind.length), 4)
|
|
61
|
+
const header = `${'ID'.padEnd(idWidth)} ${'KIND'.padEnd(kindWidth)} TITLE`
|
|
62
|
+
const divider = '-'.repeat(header.length + 20)
|
|
63
|
+
const rows = resources.map((r) => {
|
|
64
|
+
const title = r.title ?? r.id
|
|
65
|
+
return `${r.id.padEnd(idWidth)} ${r.kind.padEnd(kindWidth)} ${title}`
|
|
66
|
+
})
|
|
67
|
+
return [header, divider, ...rows].join('\n')
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function formatKnowledgeRolesList(roles: ReturnType<typeof listAllRoles>): string {
|
|
71
|
+
if (roles.length === 0) return '(no results)'
|
|
72
|
+
const idWidth = Math.max(...roles.map((r) => r.id.length), 4)
|
|
73
|
+
const header = `${'ID'.padEnd(idWidth)} TITLE`
|
|
74
|
+
const divider = '-'.repeat(header.length + 20)
|
|
75
|
+
const rows = roles.map((r) => `${r.id.padEnd(idWidth)} ${r.title}`)
|
|
76
|
+
return [header, divider, ...rows].join('\n')
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export interface KnowledgeInvocationSource {
|
|
80
|
+
kind: 'action' | 'agent-resource'
|
|
81
|
+
id: string
|
|
82
|
+
label: string
|
|
83
|
+
targetNodeId: string
|
|
84
|
+
invocations: OrganizationModelActionInvocation[]
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export interface KnowledgeInvocationNeighborsResult {
|
|
88
|
+
id: string
|
|
89
|
+
title: string
|
|
90
|
+
invocationSources: KnowledgeInvocationSource[]
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export function normalizeKnowledgeNodeId(id: string): string {
|
|
94
|
+
return id.startsWith('knowledge:') ? id.slice('knowledge:'.length) : id
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export function formatKnowledgeInvocationLabel(invocation: OrganizationModelActionInvocation): string {
|
|
98
|
+
switch (invocation.kind) {
|
|
99
|
+
case 'slash-command':
|
|
100
|
+
return invocation.command
|
|
101
|
+
case 'mcp-tool':
|
|
102
|
+
return `${invocation.server}/${invocation.name}`
|
|
103
|
+
case 'api-endpoint':
|
|
104
|
+
return `${invocation.method} ${invocation.path}`
|
|
105
|
+
case 'script-execution':
|
|
106
|
+
return invocation.resourceId
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function pushActionSource(
|
|
111
|
+
sources: KnowledgeInvocationSource[],
|
|
112
|
+
seen: Set<string>,
|
|
113
|
+
action: OrganizationModelAction | undefined
|
|
114
|
+
): void {
|
|
115
|
+
if (!action || action.invocations.length === 0) return
|
|
116
|
+
|
|
117
|
+
const key = `action:${action.id}`
|
|
118
|
+
if (seen.has(key)) return
|
|
119
|
+
seen.add(key)
|
|
120
|
+
|
|
121
|
+
sources.push({
|
|
122
|
+
kind: 'action',
|
|
123
|
+
id: action.id,
|
|
124
|
+
label: action.label,
|
|
125
|
+
targetNodeId: `action:${action.id}`,
|
|
126
|
+
invocations: action.invocations
|
|
127
|
+
})
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function pushAgentResourceSource(
|
|
131
|
+
sources: KnowledgeInvocationSource[],
|
|
132
|
+
seen: Set<string>,
|
|
133
|
+
resource: OrganizationModel['resources'][string] | undefined
|
|
134
|
+
): void {
|
|
135
|
+
if (!resource || resource.kind !== 'agent' || resource.invocations.length === 0) return
|
|
136
|
+
|
|
137
|
+
const agentResource = resource as OrganizationModelAgentResourceEntry
|
|
138
|
+
const key = `agent-resource:${agentResource.id}`
|
|
139
|
+
if (seen.has(key)) return
|
|
140
|
+
seen.add(key)
|
|
141
|
+
|
|
142
|
+
sources.push({
|
|
143
|
+
kind: 'agent-resource',
|
|
144
|
+
id: agentResource.id,
|
|
145
|
+
label: agentResource.id,
|
|
146
|
+
targetNodeId: `resource:${agentResource.id}`,
|
|
147
|
+
invocations: agentResource.invocations
|
|
148
|
+
})
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export function resolveKnowledgeInvocationNeighbors(
|
|
152
|
+
model: OrganizationModel,
|
|
153
|
+
id: string
|
|
154
|
+
): KnowledgeInvocationNeighborsResult | undefined {
|
|
155
|
+
const normalizedNodeId = normalizeKnowledgeNodeId(id)
|
|
156
|
+
const node = model.knowledge[normalizedNodeId]
|
|
157
|
+
if (!node) return undefined
|
|
158
|
+
|
|
159
|
+
const sources: KnowledgeInvocationSource[] = []
|
|
160
|
+
const seen = new Set<string>()
|
|
161
|
+
|
|
162
|
+
for (const link of node.links) {
|
|
163
|
+
const target = link.target
|
|
164
|
+
|
|
165
|
+
if (target.kind === 'action') {
|
|
166
|
+
pushActionSource(sources, seen, model.actions[target.id])
|
|
167
|
+
continue
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (target.kind === 'resource') {
|
|
171
|
+
pushAgentResourceSource(sources, seen, model.resources[target.id])
|
|
172
|
+
continue
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (target.kind === 'system') {
|
|
176
|
+
const system = getSystem(model, target.id)
|
|
177
|
+
for (const actionRef of system?.actions ?? []) {
|
|
178
|
+
pushActionSource(sources, seen, model.actions[actionRef.actionId])
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
for (const resource of Object.values(model.resources)) {
|
|
182
|
+
if (resource.systemPath === target.id) {
|
|
183
|
+
pushAgentResourceSource(sources, seen, resource)
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return {
|
|
190
|
+
id: node.id,
|
|
191
|
+
title: node.title,
|
|
192
|
+
invocationSources: sources.sort((a, b) => a.targetNodeId.localeCompare(b.targetNodeId))
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
export function formatKnowledgeInvocationNeighbors(result: KnowledgeInvocationNeighborsResult): string {
|
|
197
|
+
const lines = [`Invocation-bearing graph neighbors for ${result.id}`, '']
|
|
198
|
+
|
|
199
|
+
if (result.invocationSources.length === 0) {
|
|
200
|
+
lines.push(' (none)')
|
|
201
|
+
} else {
|
|
202
|
+
for (const source of result.invocationSources) {
|
|
203
|
+
lines.push(`${source.kind}: ${source.id}`)
|
|
204
|
+
for (const invocation of source.invocations) {
|
|
205
|
+
lines.push(` ${invocation.kind}: ${formatKnowledgeInvocationLabel(invocation)}`)
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
return lines.join('\n')
|
|
211
|
+
}
|
package/src/knowledge/index.ts
CHANGED
|
@@ -29,3 +29,16 @@ export type {
|
|
|
29
29
|
|
|
30
30
|
export { formatText, formatJson, formatIdsOnly, formatOmSearchHits, formatOmDescribe } from './format'
|
|
31
31
|
export type { KnowledgeJsonEnvelope } from './format'
|
|
32
|
+
export {
|
|
33
|
+
OM_SEARCH_HIT_KINDS,
|
|
34
|
+
parseKnowledgeSearchLimit,
|
|
35
|
+
parseKnowledgeSearchKinds,
|
|
36
|
+
formatKnowledgeSystemsList,
|
|
37
|
+
formatKnowledgeResourcesList,
|
|
38
|
+
formatKnowledgeRolesList,
|
|
39
|
+
normalizeKnowledgeNodeId,
|
|
40
|
+
formatKnowledgeInvocationLabel,
|
|
41
|
+
resolveKnowledgeInvocationNeighbors,
|
|
42
|
+
formatKnowledgeInvocationNeighbors
|
|
43
|
+
} from './cli-helpers'
|
|
44
|
+
export type { KnowledgeInvocationSource, KnowledgeInvocationNeighborsResult } from './cli-helpers'
|
|
@@ -1,5 +1,18 @@
|
|
|
1
|
-
export { bySystem, byKind, byOwner, governs, governedBy, parsePath } from './queries'
|
|
2
|
-
export type { KnowledgeMount, ParsedKnowledgePath } from './queries'
|
|
3
|
-
|
|
4
|
-
export { formatText, formatJson, formatIdsOnly } from './format'
|
|
5
|
-
export type { KnowledgeJsonEnvelope } from './format'
|
|
1
|
+
export { bySystem, byKind, byOwner, governs, governedBy, parsePath } from './queries'
|
|
2
|
+
export type { KnowledgeMount, ParsedKnowledgePath, OmSearchHitKind } from './queries'
|
|
3
|
+
|
|
4
|
+
export { formatText, formatJson, formatIdsOnly } from './format'
|
|
5
|
+
export type { KnowledgeJsonEnvelope } from './format'
|
|
6
|
+
export {
|
|
7
|
+
OM_SEARCH_HIT_KINDS,
|
|
8
|
+
parseKnowledgeSearchLimit,
|
|
9
|
+
parseKnowledgeSearchKinds,
|
|
10
|
+
formatKnowledgeSystemsList,
|
|
11
|
+
formatKnowledgeResourcesList,
|
|
12
|
+
formatKnowledgeRolesList,
|
|
13
|
+
normalizeKnowledgeNodeId,
|
|
14
|
+
formatKnowledgeInvocationLabel,
|
|
15
|
+
resolveKnowledgeInvocationNeighbors,
|
|
16
|
+
formatKnowledgeInvocationNeighbors
|
|
17
|
+
} from './cli-helpers'
|
|
18
|
+
export type { KnowledgeInvocationSource, KnowledgeInvocationNeighborsResult } from './cli-helpers'
|
package/src/knowledge/queries.ts
CHANGED
|
@@ -719,7 +719,7 @@ function collectOntologyEntries(scope: {
|
|
|
719
719
|
valueTypes?: Record<string, OntologyRecordLike>
|
|
720
720
|
sharedProperties?: Record<string, OntologyRecordLike>
|
|
721
721
|
groups?: Record<string, OntologyRecordLike>
|
|
722
|
-
|
|
722
|
+
endpoints?: Record<string, OntologyRecordLike>
|
|
723
723
|
}): SearchableEntry[] {
|
|
724
724
|
const entries: SearchableEntry[] = []
|
|
725
725
|
|
|
@@ -733,7 +733,7 @@ function collectOntologyEntries(scope: {
|
|
|
733
733
|
['value-type', scope.valueTypes],
|
|
734
734
|
['property', scope.sharedProperties],
|
|
735
735
|
['group', scope.groups],
|
|
736
|
-
['
|
|
736
|
+
['endpoint', scope.endpoints]
|
|
737
737
|
]
|
|
738
738
|
|
|
739
739
|
for (const [subKind, records] of buckets) {
|
|
@@ -874,7 +874,7 @@ export interface OmDescribePolicy {
|
|
|
874
874
|
* - Matches a key in `model.resources` → resource
|
|
875
875
|
*/
|
|
876
876
|
function detectKind(model: OrganizationModel, id: string): OmSearchHitKind | undefined {
|
|
877
|
-
if (/:(object|action|event|catalog|link|interface|value-type|property|group|
|
|
877
|
+
if (/:(object|action|event|catalog|link|interface|value-type|property|group|endpoint)\//.test(id)) {
|
|
878
878
|
return 'ontology'
|
|
879
879
|
}
|
|
880
880
|
if (id.startsWith('knowledge.')) return 'knowledge'
|
|
@@ -972,7 +972,7 @@ function describeSystem(model: OrganizationModel, path: string): OmDescribeSyste
|
|
|
972
972
|
['value-type', ontology.valueTypes],
|
|
973
973
|
['property', ontology.sharedProperties],
|
|
974
974
|
['group', ontology.groups],
|
|
975
|
-
['
|
|
975
|
+
['endpoint', ontology.endpoints]
|
|
976
976
|
]
|
|
977
977
|
for (const [k, records] of buckets) {
|
|
978
978
|
const count = records ? Object.keys(records).length : 0
|
|
@@ -1058,7 +1058,7 @@ function describeOntology(model: OrganizationModel, id: string): OmDescribeOntol
|
|
|
1058
1058
|
['value-type', 'valueTypes'],
|
|
1059
1059
|
['property', 'sharedProperties'],
|
|
1060
1060
|
['group', 'groups'],
|
|
1061
|
-
['
|
|
1061
|
+
['endpoint', 'endpoints']
|
|
1062
1062
|
]
|
|
1063
1063
|
const bucketKey = buckets.find((b) => b[0] === ontologyKind)?.[1]
|
|
1064
1064
|
if (!bucketKey) return undefined
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { describe, expect, it } from 'vitest'
|
|
2
2
|
import { DEFAULT_ORGANIZATION_MODEL } from '../defaults'
|
|
3
|
-
import { buildOmCrossRefIndex, knowledgeTargetExists } from '../cross-ref'
|
|
3
|
+
import { buildOmCompilationContext, buildOmCrossRefIndex, knowledgeTargetExists } from '../cross-ref'
|
|
4
4
|
import { OrganizationModelSchema } from '../schema'
|
|
5
5
|
import type { OrganizationModel } from '../types'
|
|
6
6
|
|
|
@@ -80,6 +80,16 @@ function makeModelWithStage(): OrganizationModel {
|
|
|
80
80
|
// ---------------------------------------------------------------------------
|
|
81
81
|
|
|
82
82
|
describe('knowledgeTargetExists — stage kind', () => {
|
|
83
|
+
it('buildOmCompilationContext returns a memoized cross-ref index plus ontology compilation', () => {
|
|
84
|
+
const model = makeModelWithStage()
|
|
85
|
+
const context = buildOmCompilationContext(model)
|
|
86
|
+
|
|
87
|
+
expect(buildOmCompilationContext(model)).toBe(context)
|
|
88
|
+
expect(context.crossRefIndex.stageIds.has('prospect')).toBe(true)
|
|
89
|
+
expect(context.ontologyCompilation.ontology.catalogTypes['sales.lead-gen:catalog/company-stage']).toBeDefined()
|
|
90
|
+
expect(buildOmCrossRefIndex(model)).toBe(context.crossRefIndex)
|
|
91
|
+
})
|
|
92
|
+
|
|
83
93
|
it('returns true for a stage id that exists in a stage-kind catalog', () => {
|
|
84
94
|
const model = makeModelWithStage()
|
|
85
95
|
const idx = buildOmCrossRefIndex(model)
|
|
@@ -123,10 +123,10 @@ describe('SystemEntrySchema - positive parse', () => {
|
|
|
123
123
|
}
|
|
124
124
|
})
|
|
125
125
|
|
|
126
|
-
it('accepts deprecated status and maps it to lifecycle for one-cycle compatibility', () => {
|
|
127
|
-
const warn = vi.spyOn(console, 'warn').mockImplementation(() => undefined)
|
|
128
|
-
|
|
129
|
-
const result = SystemEntrySchema.safeParse({
|
|
126
|
+
it('accepts deprecated status and maps it to lifecycle for one-cycle compatibility', () => {
|
|
127
|
+
const warn = vi.spyOn(console, 'warn').mockImplementation(() => undefined)
|
|
128
|
+
|
|
129
|
+
const result = SystemEntrySchema.safeParse({
|
|
130
130
|
...VALID_SYSTEM,
|
|
131
131
|
status: 'deprecated'
|
|
132
132
|
})
|
|
@@ -137,10 +137,32 @@ describe('SystemEntrySchema - positive parse', () => {
|
|
|
137
137
|
expect(result.data.lifecycle).toBe('deprecated')
|
|
138
138
|
}
|
|
139
139
|
expect(warn).toHaveBeenCalledWith('[organization-model] System.status is deprecated; use System.lifecycle instead.')
|
|
140
|
-
|
|
141
|
-
warn.mockRestore()
|
|
142
|
-
})
|
|
143
|
-
|
|
140
|
+
|
|
141
|
+
warn.mockRestore()
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
it('keeps non-color compatibility fields readable', () => {
|
|
145
|
+
const result = SystemEntrySchema.safeParse({
|
|
146
|
+
...VALID_SYSTEM,
|
|
147
|
+
path: '/lead-gen',
|
|
148
|
+
icon: 'settings',
|
|
149
|
+
uiPosition: 'sidebar-primary',
|
|
150
|
+
enabled: true,
|
|
151
|
+
devOnly: false
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
expect(result.success).toBe(true)
|
|
155
|
+
if (result.success) {
|
|
156
|
+
expect(result.data).toMatchObject({
|
|
157
|
+
path: '/lead-gen',
|
|
158
|
+
icon: 'settings',
|
|
159
|
+
uiPosition: 'sidebar-primary',
|
|
160
|
+
enabled: true,
|
|
161
|
+
devOnly: false
|
|
162
|
+
})
|
|
163
|
+
}
|
|
164
|
+
})
|
|
165
|
+
})
|
|
144
166
|
|
|
145
167
|
describe('SystemEntrySchema - negative parse', () => {
|
|
146
168
|
it('rejects missing required fields', () => {
|
|
@@ -159,6 +181,10 @@ describe('SystemEntrySchema - negative parse', () => {
|
|
|
159
181
|
expect(SystemEntrySchema.safeParse({ ...VALID_SYSTEM, id: 'Sys Lead Gen' }).success).toBe(false)
|
|
160
182
|
})
|
|
161
183
|
|
|
184
|
+
it('rejects the retired color compatibility field', () => {
|
|
185
|
+
expect(SystemEntrySchema.safeParse({ ...VALID_SYSTEM, color: 'blue' }).success).toBe(false)
|
|
186
|
+
})
|
|
187
|
+
|
|
162
188
|
it('rejects malformed System Interface states', () => {
|
|
163
189
|
expect(SystemApiInterfaceSchema.safeParse({ kind: 'api', lifecycle: 'active' }).success).toBe(false)
|
|
164
190
|
expect(SystemApiInterfaceSchema.safeParse({ lifecycle: 'paused' }).success).toBe(false)
|
|
@@ -44,7 +44,11 @@ describe('organization-model scaffolders', () => {
|
|
|
44
44
|
'packages/elevasis-core/src/organization-model/knowledge.ts'
|
|
45
45
|
])
|
|
46
46
|
)
|
|
47
|
-
|
|
47
|
+
const manifestStub = plan.writes.find((write) => write.path.endsWith('manifest.stub.ts'))
|
|
48
|
+
expect(manifestStub?.content).toContain('key: "sales-partners"')
|
|
49
|
+
expect(manifestStub?.content).toContain('systemId: "sales.partners"')
|
|
50
|
+
expect(manifestStub?.content).not.toContain('label:')
|
|
51
|
+
expect(manifestStub?.content).not.toContain('routes:')
|
|
48
52
|
expect(plan.writes.some((write) => write.path.includes('/routes/'))).toBe(false)
|
|
49
53
|
expect(plan.projectCommand).toBeUndefined()
|
|
50
54
|
})
|
|
@@ -82,6 +86,31 @@ describe('organization-model scaffolders', () => {
|
|
|
82
86
|
expect(knowledge.projectCommand).toBeUndefined()
|
|
83
87
|
})
|
|
84
88
|
|
|
89
|
+
it('emits required link ontology stubs', () => {
|
|
90
|
+
const plan = scaffoldOrganizationModel(BASE_MODEL, {
|
|
91
|
+
intent: 'ontology',
|
|
92
|
+
id: 'deal-owner',
|
|
93
|
+
systemPath: 'sales.crm',
|
|
94
|
+
kind: 'link'
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
expect(plan.edits[0]?.snippet).toContain('"sales.crm:link/deal-owner": {')
|
|
98
|
+
expect(plan.edits[0]?.snippet).toContain('"from": "sales.crm:object/source-object"')
|
|
99
|
+
expect(plan.edits[0]?.snippet).toContain('"to": "sales.crm:object/target-object"')
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
it('emits action ontology actsOn stubs', () => {
|
|
103
|
+
const plan = scaffoldOrganizationModel(BASE_MODEL, {
|
|
104
|
+
intent: 'ontology',
|
|
105
|
+
id: 'update-deal',
|
|
106
|
+
systemPath: 'sales.crm',
|
|
107
|
+
kind: 'action'
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
expect(plan.edits[0]?.snippet).toContain('"sales.crm:action/update-deal": {')
|
|
111
|
+
expect(plan.edits[0]?.snippet).toContain('"actsOn": []')
|
|
112
|
+
})
|
|
113
|
+
|
|
85
114
|
it('rejects duplicate system paths during planning', () => {
|
|
86
115
|
expect(() =>
|
|
87
116
|
scaffoldOrganizationModel(BASE_MODEL, {
|