@elevasis/core 0.42.0 → 0.43.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 +6 -1
- package/dist/auth/index.js +6 -0
- package/dist/business/entities-published.d.ts +1 -1
- package/dist/index.d.ts +3 -4
- package/dist/index.js +37 -15
- package/dist/knowledge/index.d.ts +92 -4
- package/dist/knowledge/index.js +168 -1
- package/dist/organization-model/index.d.ts +3 -4
- package/dist/organization-model/index.js +37 -15
- package/dist/test-utils/index.d.ts +3 -4
- package/dist/test-utils/index.js +12 -6
- package/package.json +1 -1
- package/src/auth/__tests__/access-test-fixtures.ts +9 -7
- package/src/auth/access-keys.ts +6 -0
- package/src/business/base-entities.ts +1 -1
- package/src/business/clients/api-schemas.test.ts +0 -1
- package/src/execution/engine/tools/integration/server/adapters/gmail/gmail-adapter.ts +9 -14
- package/src/knowledge/cli-helpers.ts +211 -0
- package/src/knowledge/index.ts +13 -0
- package/src/knowledge/published.ts +18 -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 +41 -5
- package/src/organization-model/defaults.ts +2 -2
- package/src/organization-model/domains/actions.ts +1 -1
- package/src/organization-model/domains/systems.ts +0 -4
- package/src/organization-model/organization-graph.mdx +9 -8
- package/src/organization-model/resolve.ts +9 -7
- 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/constants/versions.ts +1 -1
- package/src/platform/registry/__tests__/validation.test.ts +28 -0
- package/src/platform/registry/validation.ts +18 -0
- package/src/scaffold-registry/index.ts +11 -11
- package/src/scaffold-registry/schema.ts +30 -12
- package/src/test-utils/mocks/supabase.ts +1 -1
- package/src/test-utils/mocks/workos.ts +2 -2
|
@@ -1101,8 +1101,6 @@ var SystemEntrySchema = z.object({
|
|
|
1101
1101
|
path: PathSchema.optional(),
|
|
1102
1102
|
/** @deprecated Use ui.icon. Kept for one-cycle Feature compatibility. */
|
|
1103
1103
|
icon: IconNameSchema.optional(),
|
|
1104
|
-
/** @deprecated Feature color token, retained for one-cycle compatibility. */
|
|
1105
|
-
color: ColorTokenSchema.optional(),
|
|
1106
1104
|
/** @deprecated UI placement hint, retained for one-cycle compatibility. */
|
|
1107
1105
|
uiPosition: UiPositionSchema.optional(),
|
|
1108
1106
|
/** @deprecated Use lifecycle. */
|
|
@@ -1803,7 +1801,8 @@ var ONTOLOGY_REFERENCE_KEY_KINDS = {
|
|
|
1803
1801
|
surfaceType: "surface",
|
|
1804
1802
|
stepCatalog: "catalog"
|
|
1805
1803
|
};
|
|
1806
|
-
|
|
1804
|
+
var omCompilationContextCache = /* @__PURE__ */ new WeakMap();
|
|
1805
|
+
function buildOmCrossRefIndexFromOntology(model, ontologyCompilation) {
|
|
1807
1806
|
const systemsById = /* @__PURE__ */ new Map();
|
|
1808
1807
|
for (const { path, system } of listAllSystems(model)) {
|
|
1809
1808
|
systemsById.set(path, system);
|
|
@@ -1817,7 +1816,6 @@ function buildOmCrossRefIndex(model) {
|
|
|
1817
1816
|
const actionIds = new Set(Object.keys(model.actions ?? {}));
|
|
1818
1817
|
const customerSegmentIds = new Set(Object.keys(model.customers ?? {}));
|
|
1819
1818
|
const offeringIds = new Set(Object.keys(model.offerings ?? {}));
|
|
1820
|
-
const ontologyCompilation = compileOrganizationOntology(model);
|
|
1821
1819
|
const ontologyIndexByKind = {
|
|
1822
1820
|
object: ontologyCompilation.ontology.objectTypes,
|
|
1823
1821
|
link: ontologyCompilation.ontology.linkTypes,
|
|
@@ -1856,6 +1854,15 @@ function buildOmCrossRefIndex(model) {
|
|
|
1856
1854
|
stageIds
|
|
1857
1855
|
};
|
|
1858
1856
|
}
|
|
1857
|
+
function buildOmCompilationContext(model) {
|
|
1858
|
+
const cached = omCompilationContextCache.get(model);
|
|
1859
|
+
if (cached !== void 0) return cached;
|
|
1860
|
+
const ontologyCompilation = compileOrganizationOntology(model);
|
|
1861
|
+
const crossRefIndex = buildOmCrossRefIndexFromOntology(model, ontologyCompilation);
|
|
1862
|
+
const context = { crossRefIndex, ontologyCompilation };
|
|
1863
|
+
omCompilationContextCache.set(model, context);
|
|
1864
|
+
return context;
|
|
1865
|
+
}
|
|
1859
1866
|
function knowledgeTargetExists(index, kind, id) {
|
|
1860
1867
|
if (kind === "system") return index.systemsById.has(id);
|
|
1861
1868
|
if (kind === "client") return index.clientIds.has(id);
|
|
@@ -2192,9 +2199,8 @@ function refineOrganizationModel(model, ctx) {
|
|
|
2192
2199
|
}
|
|
2193
2200
|
});
|
|
2194
2201
|
});
|
|
2195
|
-
const idx =
|
|
2202
|
+
const { crossRefIndex: idx, ontologyCompilation } = buildOmCompilationContext(model);
|
|
2196
2203
|
const { ontologyIndexByKind, ontologyIds } = idx;
|
|
2197
|
-
const ontologyCompilation = compileOrganizationOntology(model);
|
|
2198
2204
|
function topologyTargetExists(ref) {
|
|
2199
2205
|
if (ref.kind === "system") return systemsById.has(ref.id);
|
|
2200
2206
|
if (ref.kind === "resource") return resourcesById.has(ref.id);
|
|
@@ -3369,12 +3375,34 @@ Capture the durable operating knowledge here.
|
|
|
3369
3375
|
}
|
|
3370
3376
|
|
|
3371
3377
|
// src/organization-model/scaffolders/scaffoldOntologyRecord.ts
|
|
3378
|
+
function kindSpecificFields(spec) {
|
|
3379
|
+
if (spec.kind === "link") {
|
|
3380
|
+
return {
|
|
3381
|
+
from: makeOntologyId(spec.systemPath, "object", "source-object"),
|
|
3382
|
+
to: makeOntologyId(spec.systemPath, "object", "target-object")
|
|
3383
|
+
};
|
|
3384
|
+
}
|
|
3385
|
+
if (spec.kind === "action") {
|
|
3386
|
+
return { actsOn: [] };
|
|
3387
|
+
}
|
|
3388
|
+
if (spec.kind === "group") {
|
|
3389
|
+
return { members: [] };
|
|
3390
|
+
}
|
|
3391
|
+
return {};
|
|
3392
|
+
}
|
|
3372
3393
|
function scaffoldOntologyRecord(model, spec) {
|
|
3373
3394
|
assertSystemExists(model, spec.systemPath);
|
|
3374
3395
|
const localId = spec.localId ?? spec.id;
|
|
3375
3396
|
const ontologyId = makeOntologyId(spec.systemPath, spec.kind, localId);
|
|
3376
3397
|
const label = spec.label ?? titleize(localId);
|
|
3377
3398
|
const mapName = ontologyMapName(spec.kind);
|
|
3399
|
+
const snippetRecord = {
|
|
3400
|
+
id: ontologyId,
|
|
3401
|
+
label,
|
|
3402
|
+
ownerSystemId: spec.systemPath,
|
|
3403
|
+
...spec.description === void 0 ? {} : { description: spec.description },
|
|
3404
|
+
...kindSpecificFields(spec)
|
|
3405
|
+
};
|
|
3378
3406
|
return {
|
|
3379
3407
|
intent: "ontology",
|
|
3380
3408
|
id: ontologyId,
|
|
@@ -3384,12 +3412,7 @@ function scaffoldOntologyRecord(model, spec) {
|
|
|
3384
3412
|
{
|
|
3385
3413
|
path: "packages/elevasis-core/src/organization-model/systems.ts",
|
|
3386
3414
|
description: `Add this record under ${spec.systemPath}.ontology.${mapName}.`,
|
|
3387
|
-
snippet: `${JSON.stringify(ontologyId)}: {
|
|
3388
|
-
id: ${JSON.stringify(ontologyId)},
|
|
3389
|
-
label: ${JSON.stringify(label)},
|
|
3390
|
-
ownerSystemId: ${JSON.stringify(spec.systemPath)}${spec.description === void 0 ? "" : `,
|
|
3391
|
-
description: ${JSON.stringify(spec.description)}`}
|
|
3392
|
-
}`
|
|
3415
|
+
snippet: `${JSON.stringify(ontologyId)}: ${JSON.stringify(snippetRecord, null, 2)}`
|
|
3393
3416
|
}
|
|
3394
3417
|
],
|
|
3395
3418
|
warnings: [],
|
|
@@ -3503,9 +3526,8 @@ function scaffoldSystem(model, spec) {
|
|
|
3503
3526
|
content: `import type { SystemModule } from '@repo/ui'
|
|
3504
3527
|
|
|
3505
3528
|
export const ${featureSlug.replaceAll("-", "_")}Manifest: SystemModule = {
|
|
3506
|
-
|
|
3507
|
-
|
|
3508
|
-
routes: []
|
|
3529
|
+
key: ${JSON.stringify(featureSlug)},
|
|
3530
|
+
systemId: ${JSON.stringify(systemPath)}
|
|
3509
3531
|
}
|
|
3510
3532
|
`
|
|
3511
3533
|
},
|
|
@@ -3448,7 +3448,7 @@ interface MockSupabaseFixtures {
|
|
|
3448
3448
|
*
|
|
3449
3449
|
* Usage:
|
|
3450
3450
|
* ```typescript
|
|
3451
|
-
* import { createMockSupabaseClient, TEST_USERS, TEST_ORGS } from '@
|
|
3451
|
+
* import { createMockSupabaseClient, TEST_USERS, TEST_ORGS } from '@elevasis/core/test-utils'
|
|
3452
3452
|
*
|
|
3453
3453
|
* const mockClient = createMockSupabaseClient({
|
|
3454
3454
|
* users: [TEST_USERS.admin, TEST_USERS.regularUser],
|
|
@@ -3501,7 +3501,7 @@ interface UserContext {
|
|
|
3501
3501
|
*
|
|
3502
3502
|
* Usage:
|
|
3503
3503
|
* ```typescript
|
|
3504
|
-
* import { createMockWorkOSClient, TEST_USERS } from '@
|
|
3504
|
+
* import { createMockWorkOSClient, TEST_USERS } from '@elevasis/core/test-utils'
|
|
3505
3505
|
*
|
|
3506
3506
|
* const mockWorkOS = createMockWorkOSClient({
|
|
3507
3507
|
* validTokens: {
|
|
@@ -3542,7 +3542,7 @@ declare function createMockWorkOSClient(options?: {
|
|
|
3542
3542
|
*
|
|
3543
3543
|
* Usage:
|
|
3544
3544
|
* ```typescript
|
|
3545
|
-
* import { createMockVerifyJWT, TEST_USERS } from '@
|
|
3545
|
+
* import { createMockVerifyJWT, TEST_USERS } from '@elevasis/core/test-utils'
|
|
3546
3546
|
*
|
|
3547
3547
|
* const mockVerifyJWT = createMockVerifyJWT({
|
|
3548
3548
|
* 'valid-token': {
|
|
@@ -3843,7 +3843,6 @@ interface SystemEntry {
|
|
|
3843
3843
|
status?: 'active' | 'deprecated' | 'archived';
|
|
3844
3844
|
path?: string;
|
|
3845
3845
|
icon?: string;
|
|
3846
|
-
color?: string;
|
|
3847
3846
|
uiPosition?: 'sidebar-primary' | 'sidebar-bottom';
|
|
3848
3847
|
enabled?: boolean;
|
|
3849
3848
|
devOnly?: boolean;
|
package/dist/test-utils/index.js
CHANGED
|
@@ -20415,8 +20415,6 @@ var SystemEntrySchema = z.object({
|
|
|
20415
20415
|
path: PathSchema.optional(),
|
|
20416
20416
|
/** @deprecated Use ui.icon. Kept for one-cycle Feature compatibility. */
|
|
20417
20417
|
icon: IconNameSchema.optional(),
|
|
20418
|
-
/** @deprecated Feature color token, retained for one-cycle compatibility. */
|
|
20419
|
-
color: ColorTokenSchema.optional(),
|
|
20420
20418
|
/** @deprecated UI placement hint, retained for one-cycle compatibility. */
|
|
20421
20419
|
uiPosition: UiPositionSchema.optional(),
|
|
20422
20420
|
/** @deprecated Use lifecycle. */
|
|
@@ -20942,7 +20940,8 @@ var ONTOLOGY_REFERENCE_KEY_KINDS = {
|
|
|
20942
20940
|
surfaceType: "surface",
|
|
20943
20941
|
stepCatalog: "catalog"
|
|
20944
20942
|
};
|
|
20945
|
-
|
|
20943
|
+
var omCompilationContextCache = /* @__PURE__ */ new WeakMap();
|
|
20944
|
+
function buildOmCrossRefIndexFromOntology(model, ontologyCompilation) {
|
|
20946
20945
|
const systemsById = /* @__PURE__ */ new Map();
|
|
20947
20946
|
for (const { path, system } of listAllSystems(model)) {
|
|
20948
20947
|
systemsById.set(path, system);
|
|
@@ -20956,7 +20955,6 @@ function buildOmCrossRefIndex(model) {
|
|
|
20956
20955
|
const actionIds = new Set(Object.keys(model.actions ?? {}));
|
|
20957
20956
|
const customerSegmentIds = new Set(Object.keys(model.customers ?? {}));
|
|
20958
20957
|
const offeringIds = new Set(Object.keys(model.offerings ?? {}));
|
|
20959
|
-
const ontologyCompilation = compileOrganizationOntology(model);
|
|
20960
20958
|
const ontologyIndexByKind = {
|
|
20961
20959
|
object: ontologyCompilation.ontology.objectTypes,
|
|
20962
20960
|
link: ontologyCompilation.ontology.linkTypes,
|
|
@@ -20995,6 +20993,15 @@ function buildOmCrossRefIndex(model) {
|
|
|
20995
20993
|
stageIds
|
|
20996
20994
|
};
|
|
20997
20995
|
}
|
|
20996
|
+
function buildOmCompilationContext(model) {
|
|
20997
|
+
const cached = omCompilationContextCache.get(model);
|
|
20998
|
+
if (cached !== void 0) return cached;
|
|
20999
|
+
const ontologyCompilation = compileOrganizationOntology(model);
|
|
21000
|
+
const crossRefIndex = buildOmCrossRefIndexFromOntology(model, ontologyCompilation);
|
|
21001
|
+
const context = { crossRefIndex, ontologyCompilation };
|
|
21002
|
+
omCompilationContextCache.set(model, context);
|
|
21003
|
+
return context;
|
|
21004
|
+
}
|
|
20998
21005
|
function knowledgeTargetExists(index2, kind, id) {
|
|
20999
21006
|
if (kind === "system") return index2.systemsById.has(id);
|
|
21000
21007
|
if (kind === "client") return index2.clientIds.has(id);
|
|
@@ -21331,9 +21338,8 @@ function refineOrganizationModel(model, ctx) {
|
|
|
21331
21338
|
}
|
|
21332
21339
|
});
|
|
21333
21340
|
});
|
|
21334
|
-
const idx =
|
|
21341
|
+
const { crossRefIndex: idx, ontologyCompilation } = buildOmCompilationContext(model);
|
|
21335
21342
|
const { ontologyIndexByKind, ontologyIds } = idx;
|
|
21336
|
-
const ontologyCompilation = compileOrganizationOntology(model);
|
|
21337
21343
|
function topologyTargetExists(ref) {
|
|
21338
21344
|
if (ref.kind === "system") return systemsById.has(ref.id);
|
|
21339
21345
|
if (ref.kind === "resource") return resourcesById.has(ref.id);
|
package/package.json
CHANGED
|
@@ -6,7 +6,15 @@ export const accessTestOrganizationModel = {
|
|
|
6
6
|
id: 'platform',
|
|
7
7
|
label: 'Platform',
|
|
8
8
|
lifecycle: 'active',
|
|
9
|
-
order: 10
|
|
9
|
+
order: 10,
|
|
10
|
+
systems: {
|
|
11
|
+
projects: {
|
|
12
|
+
id: 'projects',
|
|
13
|
+
label: 'Projects',
|
|
14
|
+
lifecycle: 'active',
|
|
15
|
+
order: 10
|
|
16
|
+
}
|
|
17
|
+
}
|
|
10
18
|
},
|
|
11
19
|
sales: {
|
|
12
20
|
id: 'sales',
|
|
@@ -28,12 +36,6 @@ export const accessTestOrganizationModel = {
|
|
|
28
36
|
}
|
|
29
37
|
}
|
|
30
38
|
},
|
|
31
|
-
projects: {
|
|
32
|
-
id: 'projects',
|
|
33
|
-
label: 'Projects',
|
|
34
|
-
lifecycle: 'active',
|
|
35
|
-
order: 30
|
|
36
|
-
},
|
|
37
39
|
clients: {
|
|
38
40
|
id: 'clients',
|
|
39
41
|
label: 'Clients',
|
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 }
|
|
@@ -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:
|
|
@@ -159,20 +159,15 @@ export class GmailAdapter implements BaseIntegrationAdapter {
|
|
|
159
159
|
requestBody: {
|
|
160
160
|
raw: encodedMessage
|
|
161
161
|
}
|
|
162
|
-
})
|
|
163
|
-
|
|
164
|
-
if (context?.logger) {
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
threadId: response.data.threadId
|
|
172
|
-
})
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
return {
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
if (context?.logger) {
|
|
165
|
+
context.logger.info(
|
|
166
|
+
`[GmailAdapter] Email sent: organizationId=${context.organizationId} executionId=${context.executionId} messageId=${response.data.id} threadId=${response.data.threadId} to=${params.to}`
|
|
167
|
+
)
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return {
|
|
176
171
|
messageId: response.data.id!,
|
|
177
172
|
threadId: response.data.threadId!
|
|
178
173
|
}
|
|
@@ -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'
|
|
@@ -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)
|