@elevasis/core 0.21.0 → 0.23.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 +2518 -2169
- package/dist/index.js +2495 -1095
- package/dist/knowledge/index.d.ts +706 -1044
- package/dist/knowledge/index.js +9 -9
- package/dist/organization-model/index.d.ts +2518 -2169
- package/dist/organization-model/index.js +2495 -1095
- package/dist/test-utils/index.d.ts +826 -1014
- package/dist/test-utils/index.js +1894 -1032
- package/package.json +3 -3
- package/src/__tests__/template-core-compatibility.test.ts +11 -79
- package/src/_gen/__tests__/__snapshots__/contracts.md.snap +852 -397
- package/src/auth/multi-tenancy/permissions.ts +20 -8
- package/src/business/README.md +2 -2
- package/src/business/acquisition/api-schemas.test.ts +175 -2
- package/src/business/acquisition/api-schemas.ts +132 -16
- package/src/business/acquisition/build-templates.test.ts +4 -4
- package/src/business/acquisition/build-templates.ts +72 -30
- package/src/business/acquisition/crm-state-actions.test.ts +13 -11
- package/src/business/acquisition/index.ts +12 -0
- package/src/business/acquisition/types.ts +7 -3
- 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 +9 -2
- package/src/knowledge/README.md +8 -7
- package/src/knowledge/__tests__/queries.test.ts +74 -73
- package/src/knowledge/format.ts +10 -9
- package/src/knowledge/index.ts +1 -1
- package/src/knowledge/published.ts +1 -1
- package/src/knowledge/queries.ts +26 -25
- package/src/organization-model/README.md +73 -26
- package/src/organization-model/__tests__/content-kinds-registry.test.ts +210 -0
- package/src/organization-model/__tests__/defaults.test.ts +76 -96
- package/src/organization-model/__tests__/domains/actions.test.ts +56 -0
- package/src/organization-model/__tests__/domains/customers.test.ts +299 -295
- package/src/organization-model/__tests__/domains/entities.test.ts +56 -0
- package/src/organization-model/__tests__/domains/goals.test.ts +493 -479
- package/src/organization-model/__tests__/domains/identity.test.ts +280 -279
- package/src/organization-model/__tests__/domains/navigation.test.ts +268 -212
- package/src/organization-model/__tests__/domains/offerings.test.ts +414 -419
- package/src/organization-model/__tests__/domains/policies.test.ts +323 -0
- package/src/organization-model/__tests__/domains/resource-mappings.test.ts +271 -271
- package/src/organization-model/__tests__/domains/resources.test.ts +310 -0
- package/src/organization-model/__tests__/domains/roles.test.ts +463 -347
- package/src/organization-model/__tests__/domains/statuses.test.ts +246 -243
- package/src/organization-model/__tests__/domains/systems.test.ts +209 -0
- package/src/organization-model/__tests__/flatten-additive-merge.test.ts +361 -0
- package/src/organization-model/__tests__/foundation.test.ts +74 -102
- package/src/organization-model/__tests__/get-resources-for-system.test.ts +144 -0
- package/src/organization-model/__tests__/graph.test.ts +899 -71
- package/src/organization-model/__tests__/knowledge.test.ts +209 -49
- package/src/organization-model/__tests__/lookup-helpers.test.ts +438 -0
- package/src/organization-model/__tests__/migration-helpers.test.ts +591 -0
- package/src/organization-model/__tests__/prospecting-ssot.test.ts +36 -27
- package/src/organization-model/__tests__/recursive-system-schema.test.ts +520 -0
- package/src/organization-model/__tests__/resolve.test.ts +174 -23
- package/src/organization-model/__tests__/schema.test.ts +291 -114
- package/src/organization-model/__tests__/surface-projection.test.ts +207 -97
- package/src/organization-model/catalogs/lead-gen.ts +144 -0
- package/src/organization-model/content-kinds/config.ts +36 -0
- package/src/organization-model/content-kinds/index.ts +74 -0
- package/src/organization-model/content-kinds/pipeline.ts +68 -0
- package/src/organization-model/content-kinds/registry.ts +44 -0
- package/src/organization-model/content-kinds/status.ts +71 -0
- package/src/organization-model/content-kinds/template.ts +83 -0
- package/src/organization-model/content-kinds/types.ts +117 -0
- package/src/organization-model/contracts.ts +13 -3
- package/src/organization-model/defaults.ts +499 -86
- package/src/organization-model/domains/actions.ts +239 -0
- package/src/organization-model/domains/customers.ts +78 -75
- package/src/organization-model/domains/entities.ts +144 -0
- package/src/organization-model/domains/goals.ts +83 -80
- package/src/organization-model/domains/knowledge.ts +76 -17
- package/src/organization-model/domains/navigation.ts +107 -384
- package/src/organization-model/domains/offerings.ts +71 -66
- package/src/organization-model/domains/policies.ts +102 -0
- package/src/organization-model/domains/projects.ts +14 -48
- package/src/organization-model/domains/prospecting.ts +62 -181
- package/src/organization-model/domains/resources.ts +145 -0
- package/src/organization-model/domains/roles.ts +96 -55
- package/src/organization-model/domains/sales.ts +10 -219
- package/src/organization-model/domains/shared.ts +57 -57
- package/src/organization-model/domains/statuses.ts +339 -130
- package/src/organization-model/domains/systems.ts +203 -0
- package/src/organization-model/foundation.ts +54 -67
- package/src/organization-model/graph/build.ts +682 -54
- package/src/organization-model/graph/link.ts +1 -1
- package/src/organization-model/graph/schema.ts +24 -9
- package/src/organization-model/graph/types.ts +20 -7
- package/src/organization-model/helpers.ts +231 -26
- package/src/organization-model/icons.ts +1 -0
- package/src/organization-model/index.ts +118 -5
- package/src/organization-model/migration-helpers.ts +249 -0
- package/src/organization-model/organization-graph.mdx +16 -15
- package/src/organization-model/organization-model.mdx +111 -44
- package/src/organization-model/published.ts +172 -19
- package/src/organization-model/resolve.ts +117 -54
- package/src/organization-model/schema.ts +654 -112
- package/src/organization-model/surface-projection.ts +116 -122
- package/src/organization-model/types.ts +146 -20
- package/src/platform/api/types.ts +38 -35
- package/src/platform/constants/versions.ts +1 -1
- package/src/platform/registry/__tests__/command-view.test.ts +6 -8
- package/src/platform/registry/__tests__/resource-link.test.ts +13 -8
- package/src/platform/registry/__tests__/resource-registry.integration.test.ts +16 -31
- package/src/platform/registry/__tests__/resource-registry.nested-systems.test.ts +245 -0
- package/src/platform/registry/__tests__/resource-registry.test.ts +2053 -2005
- package/src/platform/registry/__tests__/validation.test.ts +1347 -1086
- package/src/platform/registry/index.ts +14 -0
- package/src/platform/registry/resource-registry.ts +52 -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 +745 -513
- package/src/projects/api-schemas.ts +290 -267
- package/src/reference/_generated/contracts.md +853 -397
- package/src/reference/glossary.md +23 -18
- package/src/supabase/database.types.ts +181 -0
- package/src/test-utils/test-utils.test.ts +1 -6
- package/src/organization-model/__tests__/domains/operations.test.ts +0 -203
- package/src/organization-model/domains/features.ts +0 -31
- package/src/organization-model/domains/operations.ts +0 -85
|
@@ -35,9 +35,10 @@ vi.mock('../../memory/processor', () => ({
|
|
|
35
35
|
const mockAdapterFactory: LLMAdapterFactory = vi.fn() as unknown as LLMAdapterFactory
|
|
36
36
|
|
|
37
37
|
const createTestAgent = (): Agent => {
|
|
38
|
-
const config: AgentConfig = {
|
|
39
|
-
type: 'agent',
|
|
40
|
-
|
|
38
|
+
const config: AgentConfig = {
|
|
39
|
+
type: 'agent',
|
|
40
|
+
kind: 'utility',
|
|
41
|
+
resourceId: 'test-agent',
|
|
41
42
|
name: 'Test Agent',
|
|
42
43
|
description: 'Agent for error passthrough testing',
|
|
43
44
|
environment: 'dev',
|
|
@@ -3,9 +3,10 @@
|
|
|
3
3
|
* Types for autonomous agents with tools, memory, and constraints
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import type { Contract, ExecutionContext } from '../../base/types'
|
|
7
|
-
import type { ResourceDefinition } from '../../../../platform/registry/types'
|
|
8
|
-
import {
|
|
6
|
+
import type { Contract, ExecutionContext } from '../../base/types'
|
|
7
|
+
import type { ResourceDefinition } from '../../../../platform/registry/types'
|
|
8
|
+
import type { AgentResourceEntry } from '../../../../organization-model/domains/resources'
|
|
9
|
+
import { Tool } from '../../tools/types'
|
|
9
10
|
import type { AgentMemory } from '../memory/types'
|
|
10
11
|
import type { LLMAdapter } from '../../llm/types'
|
|
11
12
|
import type { ModelConfig } from '../../llm/model-info'
|
|
@@ -27,17 +28,22 @@ export type { AgentMemory }
|
|
|
27
28
|
* Uses `any` for optional params so both the real createLLMAdapter (with typed
|
|
28
29
|
* AIUsageCollector/AICallContext) and the worker proxy (which ignores them) satisfy the type.
|
|
29
30
|
*/
|
|
30
|
-
export type LLMAdapterFactory = (
|
|
31
|
-
config: ModelConfig,
|
|
32
|
-
...args: any[]
|
|
33
|
-
) => LLMAdapter
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
31
|
+
export type LLMAdapterFactory = (
|
|
32
|
+
config: ModelConfig,
|
|
33
|
+
...args: any[]
|
|
34
|
+
) => LLMAdapter
|
|
35
|
+
|
|
36
|
+
export type AgentKind = 'orchestrator' | 'specialist' | 'utility' | 'platform'
|
|
37
|
+
|
|
38
|
+
// Agent configuration
|
|
39
|
+
export interface AgentConfig extends ResourceDefinition {
|
|
40
|
+
type: 'agent'
|
|
41
|
+
/** OM descriptor backing canonical identity and governance metadata. */
|
|
42
|
+
resource?: AgentResourceEntry
|
|
43
|
+
kind: AgentKind
|
|
44
|
+
|
|
45
|
+
// Agent behavior
|
|
46
|
+
systemPrompt: string // System prompt defining agent behavior
|
|
41
47
|
|
|
42
48
|
// Execution constraints (simplified for v1)
|
|
43
49
|
constraints?: AgentConstraints
|
|
@@ -83,7 +89,11 @@ export interface AgentConfig extends ResourceDefinition {
|
|
|
83
89
|
memoryPreferences?: string
|
|
84
90
|
|
|
85
91
|
// Lifecycle callbacks for observability (optional) - DEFERRED
|
|
86
|
-
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export type DescriptorBackedAgentConfig = Omit<AgentConfig, 'resourceId' | 'type' | 'resource'> & {
|
|
95
|
+
resource: AgentResourceEntry
|
|
96
|
+
}
|
|
87
97
|
|
|
88
98
|
// Execution constraints to prevent runaway agents
|
|
89
99
|
export interface AgentConstraints {
|
|
@@ -7,10 +7,12 @@
|
|
|
7
7
|
export { Agent } from './core/agent'
|
|
8
8
|
|
|
9
9
|
// Types
|
|
10
|
-
export type {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
export type {
|
|
11
|
+
AgentKind,
|
|
12
|
+
AgentConfig,
|
|
13
|
+
DescriptorBackedAgentConfig,
|
|
14
|
+
AgentConstraints,
|
|
15
|
+
AgentDefinition,
|
|
14
16
|
IterationContext,
|
|
15
17
|
LLMAdapterFactory
|
|
16
18
|
} from './core/types'
|
|
@@ -8,9 +8,10 @@ import { z } from 'zod'
|
|
|
8
8
|
describe('buildReasoningRequest', () => {
|
|
9
9
|
// Helper: Create minimal iteration context
|
|
10
10
|
const createMockContext = (overrides?: Partial<IterationContext>): IterationContext => {
|
|
11
|
-
const config: AgentConfig = {
|
|
12
|
-
type: 'agent',
|
|
13
|
-
|
|
11
|
+
const config: AgentConfig = {
|
|
12
|
+
type: 'agent',
|
|
13
|
+
kind: 'utility',
|
|
14
|
+
resourceId: 'test-agent',
|
|
14
15
|
name: 'Test Agent',
|
|
15
16
|
description: 'Test agent',
|
|
16
17
|
version: '1.0.0',
|
|
@@ -95,9 +96,10 @@ describe('buildReasoningRequest', () => {
|
|
|
95
96
|
|
|
96
97
|
it('should set includeMessageAction to true when sessionCapable is true', () => {
|
|
97
98
|
const context = createMockContext({
|
|
98
|
-
config: {
|
|
99
|
-
type: 'agent',
|
|
100
|
-
|
|
99
|
+
config: {
|
|
100
|
+
type: 'agent',
|
|
101
|
+
kind: 'utility',
|
|
102
|
+
resourceId: 'test-agent',
|
|
101
103
|
name: 'Test Agent',
|
|
102
104
|
description: 'Test agent',
|
|
103
105
|
version: '1.0.0',
|
|
@@ -113,9 +115,10 @@ describe('buildReasoningRequest', () => {
|
|
|
113
115
|
|
|
114
116
|
it('should set includeMessageAction to false when sessionCapable is false', () => {
|
|
115
117
|
const context = createMockContext({
|
|
116
|
-
config: {
|
|
117
|
-
type: 'agent',
|
|
118
|
-
|
|
118
|
+
config: {
|
|
119
|
+
type: 'agent',
|
|
120
|
+
kind: 'utility',
|
|
121
|
+
resourceId: 'test-agent',
|
|
119
122
|
name: 'Test Agent',
|
|
120
123
|
description: 'Test agent',
|
|
121
124
|
version: '1.0.0',
|
|
@@ -142,9 +145,10 @@ describe('buildReasoningRequest', () => {
|
|
|
142
145
|
|
|
143
146
|
it('should include hardened security rules when sessionCapable is true', () => {
|
|
144
147
|
const context = createMockContext({
|
|
145
|
-
config: {
|
|
146
|
-
type: 'agent',
|
|
147
|
-
|
|
148
|
+
config: {
|
|
149
|
+
type: 'agent',
|
|
150
|
+
kind: 'utility',
|
|
151
|
+
resourceId: 'test-agent',
|
|
148
152
|
name: 'Test Agent',
|
|
149
153
|
description: 'Test agent',
|
|
150
154
|
version: '1.0.0',
|
|
@@ -161,9 +165,10 @@ describe('buildReasoningRequest', () => {
|
|
|
161
165
|
|
|
162
166
|
it('should omit security section when securityLevel is none', () => {
|
|
163
167
|
const context = createMockContext({
|
|
164
|
-
config: {
|
|
165
|
-
type: 'agent',
|
|
166
|
-
|
|
168
|
+
config: {
|
|
169
|
+
type: 'agent',
|
|
170
|
+
kind: 'utility',
|
|
171
|
+
resourceId: 'test-agent',
|
|
167
172
|
name: 'Test Agent',
|
|
168
173
|
description: 'Test agent',
|
|
169
174
|
version: '1.0.0',
|
|
@@ -179,9 +184,10 @@ describe('buildReasoningRequest', () => {
|
|
|
179
184
|
|
|
180
185
|
it('should allow explicit hardened override on non-session agent', () => {
|
|
181
186
|
const context = createMockContext({
|
|
182
|
-
config: {
|
|
183
|
-
type: 'agent',
|
|
184
|
-
|
|
187
|
+
config: {
|
|
188
|
+
type: 'agent',
|
|
189
|
+
kind: 'utility',
|
|
190
|
+
resourceId: 'test-agent',
|
|
185
191
|
name: 'Test Agent',
|
|
186
192
|
description: 'Test agent',
|
|
187
193
|
version: '1.0.0',
|
|
@@ -14,6 +14,7 @@ export {
|
|
|
14
14
|
type WorkflowStepDefinition,
|
|
15
15
|
type StepHandler,
|
|
16
16
|
type WorkflowConfig,
|
|
17
|
+
type DescriptorBackedWorkflowConfig,
|
|
17
18
|
type WorkflowDefinition,
|
|
18
19
|
type NextConfig,
|
|
19
20
|
type LinearNext,
|
|
@@ -59,7 +60,9 @@ export {
|
|
|
59
60
|
// Agent exports (from agent module)
|
|
60
61
|
export {
|
|
61
62
|
Agent,
|
|
63
|
+
type AgentKind,
|
|
62
64
|
type AgentConfig,
|
|
65
|
+
type DescriptorBackedAgentConfig,
|
|
63
66
|
type AgentConstraints,
|
|
64
67
|
type AgentMemory,
|
|
65
68
|
type AgentDefinition,
|
|
@@ -7,14 +7,21 @@ import type { z } from 'zod'
|
|
|
7
7
|
|
|
8
8
|
import type { ExecutionContext } from '../base/types'
|
|
9
9
|
import type { ResourceDefinition } from '../../../platform/registry/types'
|
|
10
|
+
import type { WorkflowResourceEntry } from '../../../organization-model/domains/resources'
|
|
10
11
|
import type { ResourceMetricsConfig } from '../../../operations/observability/types'
|
|
11
12
|
import type { ExecutionInterface } from '../interface/types'
|
|
12
13
|
|
|
13
14
|
// Workflow configuration
|
|
14
15
|
export interface WorkflowConfig extends ResourceDefinition {
|
|
15
16
|
type: 'workflow'
|
|
16
|
-
/**
|
|
17
|
-
|
|
17
|
+
/** OM descriptor backing canonical identity and governance metadata. */
|
|
18
|
+
resource?: WorkflowResourceEntry
|
|
19
|
+
/** Lead-gen action key for registry derivation (e.g. 'lead-gen.company.apollo-import') */
|
|
20
|
+
actionKey?: string
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export type DescriptorBackedWorkflowConfig = Omit<WorkflowConfig, 'resourceId' | 'type' | 'resource'> & {
|
|
24
|
+
resource: WorkflowResourceEntry
|
|
18
25
|
}
|
|
19
26
|
|
|
20
27
|
// Workflow step definition
|
package/src/knowledge/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# @elevasis/core/knowledge
|
|
1
|
+
# @elevasis/core/knowledge
|
|
2
2
|
|
|
3
3
|
Pure query layer over the organization graph. Browser-safe (no Node APIs); shared by the SDK CLI, platform CLI, and the `@elevasis/ui/knowledge` browser.
|
|
4
4
|
|
|
@@ -6,18 +6,18 @@ Pure query layer over the organization graph. Browser-safe (no Node APIs); share
|
|
|
6
6
|
|
|
7
7
|
| Export | Purpose |
|
|
8
8
|
| --------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
|
|
9
|
-
| `
|
|
9
|
+
| `bySystem(graph, systemId, knowledgeNodes)` | Knowledge nodes that govern the given system. |
|
|
10
10
|
| `byKind(graph, kind, knowledgeNodes)` | Filter knowledge nodes by `OrgKnowledgeKind`. |
|
|
11
11
|
| `byOwner(graph, ownerId, knowledgeNodes)` | Knowledge nodes whose `ownerIds` includes the given owner. |
|
|
12
|
-
| `governs(graph, nodeId)` | Outgoing `governs` targets (governed-
|
|
13
|
-
| `governedBy(graph, nodeId)` | Incoming `governs` sources (governing knowledge-node ids) into a
|
|
14
|
-
| `parsePath(pathString)` | Parse `/by-
|
|
12
|
+
| `governs(graph, nodeId)` | Outgoing `governs` targets (governed-system ids) from a knowledge node. |
|
|
13
|
+
| `governedBy(graph, nodeId)` | Incoming `governs` sources (governing knowledge-node ids) into a system. |
|
|
14
|
+
| `parsePath(pathString)` | Parse `/by-system/$id`, `/by-kind/$kind`, `/by-owner/$id`, `/graph/$id/{governs,governed-by}`, or `/$id`. Throws on invalid input. |
|
|
15
15
|
| `formatText`, `formatJson`, `formatIdsOnly` | Output formatters used by the `knowledge:*` CLI subcommands. |
|
|
16
16
|
|
|
17
17
|
## Path syntax
|
|
18
18
|
|
|
19
19
|
```
|
|
20
|
-
/by-
|
|
20
|
+
/by-system/<systemId>
|
|
21
21
|
/by-kind/<playbook|strategy|reference>
|
|
22
22
|
/by-owner/<ownerId>
|
|
23
23
|
/graph/<nodeId>/governs
|
|
@@ -27,6 +27,7 @@ Pure query layer over the organization graph. Browser-safe (no Node APIs); share
|
|
|
27
27
|
|
|
28
28
|
## JSON envelope
|
|
29
29
|
|
|
30
|
-
`formatJson` returns `{ path, mount, args, results }`
|
|
30
|
+
`formatJson` returns `{ path, mount, args, results }` — the same wrapped envelope used by `pnpm exec elevasis knowledge:ls --json` and `pnpm exec elevasis-sdk knowledge:ls --json`.
|
|
31
31
|
|
|
32
32
|
`governs` and `governedBy` accept either bare or graph-namespaced ids (`knowledge.foo` or `knowledge:knowledge.foo`).
|
|
33
|
+
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest'
|
|
2
|
-
import {
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import { bySystem, byKind, byOwner, governs, governedBy, parsePath } from '../queries'
|
|
3
3
|
import { formatText, formatJson, formatIdsOnly } from '../format'
|
|
4
4
|
import type { OrganizationGraph } from '../../organization-model/graph/types'
|
|
5
5
|
import type { OrgKnowledgeNode } from '../../organization-model/domains/knowledge'
|
|
@@ -10,8 +10,8 @@ import type { OrgKnowledgeNode } from '../../organization-model/domains/knowledg
|
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* Three knowledge nodes covering all three kinds.
|
|
13
|
-
* - playbook-a governs
|
|
14
|
-
* - strategy-b governs
|
|
13
|
+
* - playbook-a governs system:sales.crm and system:sales.lead-gen
|
|
14
|
+
* - strategy-b governs system:sales.lead-gen
|
|
15
15
|
* - reference-c has no links (no governs edges)
|
|
16
16
|
* - playbook-d is owned by "role.ops-lead"
|
|
17
17
|
*/
|
|
@@ -22,7 +22,7 @@ const NODES: OrgKnowledgeNode[] = [
|
|
|
22
22
|
title: 'Playbook A',
|
|
23
23
|
summary: 'Playbook A summary.',
|
|
24
24
|
body: '## Playbook A\n\nContent.',
|
|
25
|
-
links: [{ nodeId: '
|
|
25
|
+
links: [{ nodeId: 'system:sales.crm' }, { nodeId: 'system:sales.lead-gen' }],
|
|
26
26
|
ownerIds: [],
|
|
27
27
|
updatedAt: '2026-01-01'
|
|
28
28
|
},
|
|
@@ -32,7 +32,7 @@ const NODES: OrgKnowledgeNode[] = [
|
|
|
32
32
|
title: 'Strategy B',
|
|
33
33
|
summary: 'Strategy B summary.',
|
|
34
34
|
body: '## Strategy B\n\nContent.',
|
|
35
|
-
links: [{ nodeId: '
|
|
35
|
+
links: [{ nodeId: 'system:sales.lead-gen' }],
|
|
36
36
|
ownerIds: ['role.ops-lead'],
|
|
37
37
|
updatedAt: '2026-01-02'
|
|
38
38
|
},
|
|
@@ -67,13 +67,13 @@ const GRAPH: OrganizationGraph = {
|
|
|
67
67
|
organizationModelVersion: 1,
|
|
68
68
|
nodes: [
|
|
69
69
|
{ id: 'organization-model', kind: 'organization', label: 'Organization Model' },
|
|
70
|
-
{ id: '
|
|
70
|
+
{ id: 'system:sales.crm', kind: 'system', label: 'CRM', sourceId: 'sales.crm', systemId: 'sales.crm' },
|
|
71
71
|
{
|
|
72
|
-
id: '
|
|
73
|
-
kind: '
|
|
72
|
+
id: 'system:sales.lead-gen',
|
|
73
|
+
kind: 'system',
|
|
74
74
|
label: 'Lead Gen',
|
|
75
75
|
sourceId: 'sales.lead-gen',
|
|
76
|
-
|
|
76
|
+
systemId: 'sales.lead-gen'
|
|
77
77
|
},
|
|
78
78
|
{
|
|
79
79
|
id: 'knowledge:knowledge.playbook-a',
|
|
@@ -128,50 +128,50 @@ const GRAPH: OrganizationGraph = {
|
|
|
128
128
|
},
|
|
129
129
|
// governs edges
|
|
130
130
|
{
|
|
131
|
-
id: 'edge:governs:knowledge:knowledge.playbook-a:
|
|
131
|
+
id: 'edge:governs:knowledge:knowledge.playbook-a:system:sales.crm',
|
|
132
132
|
kind: 'governs',
|
|
133
133
|
sourceId: 'knowledge:knowledge.playbook-a',
|
|
134
|
-
targetId: '
|
|
134
|
+
targetId: 'system:sales.crm'
|
|
135
135
|
},
|
|
136
136
|
{
|
|
137
|
-
id: 'edge:governs:knowledge:knowledge.playbook-a:
|
|
137
|
+
id: 'edge:governs:knowledge:knowledge.playbook-a:system:sales.lead-gen',
|
|
138
138
|
kind: 'governs',
|
|
139
139
|
sourceId: 'knowledge:knowledge.playbook-a',
|
|
140
|
-
targetId: '
|
|
140
|
+
targetId: 'system:sales.lead-gen'
|
|
141
141
|
},
|
|
142
142
|
{
|
|
143
|
-
id: 'edge:governs:knowledge:knowledge.strategy-b:
|
|
143
|
+
id: 'edge:governs:knowledge:knowledge.strategy-b:system:sales.lead-gen',
|
|
144
144
|
kind: 'governs',
|
|
145
145
|
sourceId: 'knowledge:knowledge.strategy-b',
|
|
146
|
-
targetId: '
|
|
146
|
+
targetId: 'system:sales.lead-gen'
|
|
147
147
|
}
|
|
148
148
|
]
|
|
149
149
|
}
|
|
150
150
|
|
|
151
151
|
// ---------------------------------------------------------------------------
|
|
152
|
-
//
|
|
152
|
+
// bySystem
|
|
153
153
|
// ---------------------------------------------------------------------------
|
|
154
154
|
|
|
155
|
-
describe('
|
|
156
|
-
it('returns nodes that have governs edges to the given
|
|
157
|
-
const result =
|
|
155
|
+
describe('bySystem', () => {
|
|
156
|
+
it('returns nodes that have governs edges to the given system', () => {
|
|
157
|
+
const result = bySystem(GRAPH, 'sales.crm', NODES)
|
|
158
158
|
expect(result).toHaveLength(1)
|
|
159
159
|
expect(result[0].id).toBe('knowledge.playbook-a')
|
|
160
160
|
})
|
|
161
161
|
|
|
162
|
-
it('returns multiple nodes when multiple knowledge nodes govern the same
|
|
163
|
-
const result =
|
|
162
|
+
it('returns multiple nodes when multiple knowledge nodes govern the same system', () => {
|
|
163
|
+
const result = bySystem(GRAPH, 'sales.lead-gen', NODES)
|
|
164
164
|
const ids = result.map((n) => n.id).sort()
|
|
165
165
|
expect(ids).toEqual(['knowledge.playbook-a', 'knowledge.strategy-b'].sort())
|
|
166
166
|
})
|
|
167
167
|
|
|
168
|
-
it('returns empty array when no node governs the
|
|
169
|
-
const result =
|
|
168
|
+
it('returns empty array when no node governs the system', () => {
|
|
169
|
+
const result = bySystem(GRAPH, 'system.does-not-exist', NODES)
|
|
170
170
|
expect(result).toHaveLength(0)
|
|
171
171
|
})
|
|
172
172
|
|
|
173
173
|
it('returns full OrgKnowledgeNode objects (not just graph stubs)', () => {
|
|
174
|
-
const result =
|
|
174
|
+
const result = bySystem(GRAPH, 'sales.crm', NODES)
|
|
175
175
|
expect(result[0].body).toBeDefined()
|
|
176
176
|
expect(result[0].links).toBeDefined()
|
|
177
177
|
expect(result[0].ownerIds).toBeDefined()
|
|
@@ -237,17 +237,17 @@ describe('byOwner', () => {
|
|
|
237
237
|
describe('governs', () => {
|
|
238
238
|
it('returns target graph node IDs for outgoing governs edges (OM node id input)', () => {
|
|
239
239
|
const result = governs(GRAPH, 'knowledge.playbook-a')
|
|
240
|
-
expect(result.sort()).toEqual(['
|
|
240
|
+
expect(result.sort()).toEqual(['system:sales.crm', 'system:sales.lead-gen'].sort())
|
|
241
241
|
})
|
|
242
242
|
|
|
243
243
|
it('accepts graph node ID format input', () => {
|
|
244
244
|
const result = governs(GRAPH, 'knowledge:knowledge.playbook-a')
|
|
245
|
-
expect(result.sort()).toEqual(['
|
|
245
|
+
expect(result.sort()).toEqual(['system:sales.crm', 'system:sales.lead-gen'].sort())
|
|
246
246
|
})
|
|
247
247
|
|
|
248
248
|
it('returns single target for a node with one link', () => {
|
|
249
249
|
const result = governs(GRAPH, 'knowledge.strategy-b')
|
|
250
|
-
expect(result).toEqual(['
|
|
250
|
+
expect(result).toEqual(['system:sales.lead-gen'])
|
|
251
251
|
})
|
|
252
252
|
|
|
253
253
|
it('returns empty array for a node with no governs edges', () => {
|
|
@@ -266,69 +266,69 @@ describe('governs', () => {
|
|
|
266
266
|
// ---------------------------------------------------------------------------
|
|
267
267
|
|
|
268
268
|
describe('governedBy', () => {
|
|
269
|
-
it('returns knowledge graph node IDs for incoming governs edges (prefixed
|
|
270
|
-
const result = governedBy(GRAPH, '
|
|
269
|
+
it('returns knowledge graph node IDs for incoming governs edges (prefixed system id)', () => {
|
|
270
|
+
const result = governedBy(GRAPH, 'system:sales.lead-gen')
|
|
271
271
|
const sorted = result.sort()
|
|
272
272
|
expect(sorted).toEqual(['knowledge:knowledge.playbook-a', 'knowledge:knowledge.strategy-b'].sort())
|
|
273
273
|
})
|
|
274
274
|
|
|
275
|
-
it('accepts bare
|
|
275
|
+
it('accepts bare system id (auto-prefixes with system:)', () => {
|
|
276
276
|
const result = governedBy(GRAPH, 'sales.crm')
|
|
277
277
|
expect(result).toEqual(['knowledge:knowledge.playbook-a'])
|
|
278
278
|
})
|
|
279
279
|
|
|
280
280
|
it('returns empty array when no knowledge node governs the target', () => {
|
|
281
|
-
const result = governedBy(GRAPH, '
|
|
281
|
+
const result = governedBy(GRAPH, 'system:dashboard')
|
|
282
282
|
expect(result).toHaveLength(0)
|
|
283
283
|
})
|
|
284
284
|
|
|
285
285
|
it('returns empty array for unknown target node id', () => {
|
|
286
|
-
const result = governedBy(GRAPH, '
|
|
286
|
+
const result = governedBy(GRAPH, 'system:does-not-exist')
|
|
287
287
|
expect(result).toHaveLength(0)
|
|
288
288
|
})
|
|
289
289
|
})
|
|
290
290
|
|
|
291
291
|
// ---------------------------------------------------------------------------
|
|
292
|
-
// parsePath
|
|
292
|
+
// parsePath — happy paths (all five mount axes)
|
|
293
293
|
// ---------------------------------------------------------------------------
|
|
294
294
|
|
|
295
|
-
describe('parsePath
|
|
296
|
-
it('/by-kind/playbook
|
|
295
|
+
describe('parsePath — happy paths', () => {
|
|
296
|
+
it('/by-kind/playbook → mount:by-kind args:[playbook]', () => {
|
|
297
297
|
expect(parsePath('/by-kind/playbook')).toEqual({ mount: 'by-kind', args: ['playbook'] })
|
|
298
298
|
})
|
|
299
299
|
|
|
300
|
-
it('/by-kind/strategy
|
|
300
|
+
it('/by-kind/strategy → mount:by-kind args:[strategy]', () => {
|
|
301
301
|
expect(parsePath('/by-kind/strategy')).toEqual({ mount: 'by-kind', args: ['strategy'] })
|
|
302
302
|
})
|
|
303
303
|
|
|
304
|
-
it('/by-
|
|
305
|
-
expect(parsePath('/by-
|
|
304
|
+
it('/by-system/sales.crm → mount:by-system args:[sales.crm]', () => {
|
|
305
|
+
expect(parsePath('/by-system/sales.crm')).toEqual({ mount: 'by-system', args: ['sales.crm'] })
|
|
306
306
|
})
|
|
307
307
|
|
|
308
|
-
it('/by-
|
|
309
|
-
//
|
|
310
|
-
expect(parsePath('/by-
|
|
308
|
+
it('/by-system/sales/crm (slash-delimited) → mount:by-system args:[sales/crm]', () => {
|
|
309
|
+
// systemId with slashes is joined and passed as-is for the caller to interpret
|
|
310
|
+
expect(parsePath('/by-system/sales/crm')).toEqual({ mount: 'by-system', args: ['sales/crm'] })
|
|
311
311
|
})
|
|
312
312
|
|
|
313
|
-
it('/by-owner/role.ops-lead
|
|
313
|
+
it('/by-owner/role.ops-lead → mount:by-owner args:[role.ops-lead]', () => {
|
|
314
314
|
expect(parsePath('/by-owner/role.ops-lead')).toEqual({ mount: 'by-owner', args: ['role.ops-lead'] })
|
|
315
315
|
})
|
|
316
316
|
|
|
317
|
-
it('/graph/knowledge.outreach-playbook/governs
|
|
317
|
+
it('/graph/knowledge.outreach-playbook/governs → mount:graph args:[knowledge.outreach-playbook,governs]', () => {
|
|
318
318
|
expect(parsePath('/graph/knowledge.outreach-playbook/governs')).toEqual({
|
|
319
319
|
mount: 'graph',
|
|
320
320
|
args: ['knowledge.outreach-playbook', 'governs']
|
|
321
321
|
})
|
|
322
322
|
})
|
|
323
323
|
|
|
324
|
-
it('/graph/knowledge.outreach-playbook/governed-by
|
|
324
|
+
it('/graph/knowledge.outreach-playbook/governed-by → mount:graph args:[knowledge.outreach-playbook,governed-by]', () => {
|
|
325
325
|
expect(parsePath('/graph/knowledge.outreach-playbook/governed-by')).toEqual({
|
|
326
326
|
mount: 'graph',
|
|
327
327
|
args: ['knowledge.outreach-playbook', 'governed-by']
|
|
328
328
|
})
|
|
329
329
|
})
|
|
330
330
|
|
|
331
|
-
it('/<nodeId> single segment
|
|
331
|
+
it('/<nodeId> single segment → mount:node args:[nodeId]', () => {
|
|
332
332
|
expect(parsePath('/knowledge.outreach-playbook')).toEqual({
|
|
333
333
|
mount: 'node',
|
|
334
334
|
args: ['knowledge.outreach-playbook']
|
|
@@ -341,10 +341,10 @@ describe('parsePath — happy paths', () => {
|
|
|
341
341
|
})
|
|
342
342
|
|
|
343
343
|
// ---------------------------------------------------------------------------
|
|
344
|
-
// parsePath
|
|
344
|
+
// parsePath — invalid inputs (5+)
|
|
345
345
|
// ---------------------------------------------------------------------------
|
|
346
346
|
|
|
347
|
-
describe('parsePath
|
|
347
|
+
describe('parsePath — invalid inputs', () => {
|
|
348
348
|
it('throws on empty string', () => {
|
|
349
349
|
expect(() => parsePath('')).toThrow('parsePath: path must be a non-empty string')
|
|
350
350
|
})
|
|
@@ -357,8 +357,8 @@ describe('parsePath — invalid inputs', () => {
|
|
|
357
357
|
expect(() => parsePath('/')).toThrow('parsePath: path resolves to root with no mount')
|
|
358
358
|
})
|
|
359
359
|
|
|
360
|
-
it('throws on /by-
|
|
361
|
-
expect(() => parsePath('/by-
|
|
360
|
+
it('throws on /by-system with no systemId argument', () => {
|
|
361
|
+
expect(() => parsePath('/by-system')).toThrow('/by-system requires a systemId argument')
|
|
362
362
|
})
|
|
363
363
|
|
|
364
364
|
it('throws on /by-kind with no kind argument', () => {
|
|
@@ -385,11 +385,11 @@ describe('parsePath — invalid inputs', () => {
|
|
|
385
385
|
})
|
|
386
386
|
|
|
387
387
|
// ---------------------------------------------------------------------------
|
|
388
|
-
// parsePath
|
|
388
|
+
// parsePath — edge cases
|
|
389
389
|
// ---------------------------------------------------------------------------
|
|
390
390
|
|
|
391
391
|
describe('parsePath edge cases', () => {
|
|
392
|
-
// Whitespace handling
|
|
392
|
+
// Whitespace handling — the parser does NOT trim the incoming string.
|
|
393
393
|
// A path with leading whitespace does not start with '/' so it throws.
|
|
394
394
|
it('throws on path with leading whitespace', () => {
|
|
395
395
|
expect(() => parsePath(' /by-kind/playbook')).toThrow('parsePath: path must start with "/"')
|
|
@@ -409,13 +409,13 @@ describe('parsePath edge cases', () => {
|
|
|
409
409
|
expect(parsePath('/by-kind/playbook///')).toEqual({ mount: 'by-kind', args: ['playbook'] })
|
|
410
410
|
})
|
|
411
411
|
|
|
412
|
-
// Lone-slash variants
|
|
412
|
+
// Lone-slash variants — a path of only slashes normalises to an empty
|
|
413
413
|
// segments list and must throw the root-with-no-mount error.
|
|
414
414
|
it('throws on path consisting only of slashes', () => {
|
|
415
415
|
expect(() => parsePath('///')).toThrow('parsePath: path resolves to root with no mount')
|
|
416
416
|
})
|
|
417
417
|
|
|
418
|
-
// Case sensitivity
|
|
418
|
+
// Case sensitivity — parsePath does not normalise case.
|
|
419
419
|
// 'Playbook' (capitalised) is passed through as-is to the caller.
|
|
420
420
|
it('preserves node-id case (no case normalisation)', () => {
|
|
421
421
|
// /by-kind takes rest[0] verbatim
|
|
@@ -423,16 +423,16 @@ describe('parsePath edge cases', () => {
|
|
|
423
423
|
expect(result.args[0]).toBe('Playbook')
|
|
424
424
|
})
|
|
425
425
|
|
|
426
|
-
//
|
|
427
|
-
it('/by-
|
|
428
|
-
expect(parsePath('/by-
|
|
426
|
+
// system id: dot notation passes through unchanged.
|
|
427
|
+
it('/by-system/sales.crm preserves dot notation', () => {
|
|
428
|
+
expect(parsePath('/by-system/sales.crm')).toEqual({ mount: 'by-system', args: ['sales.crm'] })
|
|
429
429
|
})
|
|
430
430
|
|
|
431
|
-
//
|
|
432
|
-
// The parser does NOT convert slashes to dots
|
|
433
|
-
it('/by-
|
|
434
|
-
const result = parsePath('/by-
|
|
435
|
-
expect(result.mount).toBe('by-
|
|
431
|
+
// system id: slash notation joins rest segments with '/'.
|
|
432
|
+
// The parser does NOT convert slashes to dots — the caller must normalise.
|
|
433
|
+
it('/by-system/sales/crm joins segments with slash (no dot conversion)', () => {
|
|
434
|
+
const result = parsePath('/by-system/sales/crm')
|
|
435
|
+
expect(result.mount).toBe('by-system')
|
|
436
436
|
// args[0] is 'sales/crm', NOT 'sales.crm'
|
|
437
437
|
expect(result.args[0]).toBe('sales/crm')
|
|
438
438
|
})
|
|
@@ -446,7 +446,7 @@ describe('parsePath edge cases', () => {
|
|
|
446
446
|
expect(result.args).toHaveLength(1)
|
|
447
447
|
})
|
|
448
448
|
|
|
449
|
-
// /by-owner joins rest with '/' (same behaviour as /by-
|
|
449
|
+
// /by-owner joins rest with '/' (same behaviour as /by-system).
|
|
450
450
|
it('/by-owner/role.ops-lead/sub joins segments with slash', () => {
|
|
451
451
|
const result = parsePath('/by-owner/role.ops-lead/sub')
|
|
452
452
|
expect(result.mount).toBe('by-owner')
|
|
@@ -454,7 +454,7 @@ describe('parsePath edge cases', () => {
|
|
|
454
454
|
})
|
|
455
455
|
|
|
456
456
|
// Graph node ID may contain a colon prefix (e.g. knowledge:knowledge.foo).
|
|
457
|
-
// The parser accepts it
|
|
457
|
+
// The parser accepts it — the rest before the verb is joined with '/'.
|
|
458
458
|
it('/graph/<prefixed-nodeId>/governs round-trips the prefixed id', () => {
|
|
459
459
|
const result = parsePath('/graph/knowledge:knowledge.test-node/governs')
|
|
460
460
|
expect(result.mount).toBe('graph')
|
|
@@ -467,7 +467,7 @@ describe('parsePath edge cases', () => {
|
|
|
467
467
|
expect(() => parsePath('/graph')).toThrow('/graph requires <nodeId>/<verb>')
|
|
468
468
|
})
|
|
469
469
|
|
|
470
|
-
// /node mount
|
|
470
|
+
// /node mount — single-segment path works for any non-reserved first segment.
|
|
471
471
|
it('single-segment non-reserved path is treated as node mount', () => {
|
|
472
472
|
expect(parsePath('/knowledge.my-doc')).toEqual({ mount: 'node', args: ['knowledge.my-doc'] })
|
|
473
473
|
})
|
|
@@ -504,7 +504,7 @@ describe('formatText', () => {
|
|
|
504
504
|
})
|
|
505
505
|
|
|
506
506
|
// ---------------------------------------------------------------------------
|
|
507
|
-
// formatJson
|
|
507
|
+
// formatJson — envelope shape
|
|
508
508
|
// ---------------------------------------------------------------------------
|
|
509
509
|
|
|
510
510
|
describe('formatJson', () => {
|
|
@@ -538,8 +538,8 @@ describe('formatJson', () => {
|
|
|
538
538
|
|
|
539
539
|
it('preserves args array in envelope', () => {
|
|
540
540
|
const raw = formatJson({
|
|
541
|
-
path: '/by-
|
|
542
|
-
parsed: { mount: 'by-
|
|
541
|
+
path: '/by-system/sales.crm',
|
|
542
|
+
parsed: { mount: 'by-system', args: ['sales.crm'] },
|
|
543
543
|
results: []
|
|
544
544
|
})
|
|
545
545
|
expect(JSON.parse(raw)).toMatchObject({ args: ['sales.crm'] })
|
|
@@ -549,13 +549,13 @@ describe('formatJson', () => {
|
|
|
549
549
|
const raw = formatJson({
|
|
550
550
|
path: '/graph/knowledge.playbook-a/governs',
|
|
551
551
|
parsed: { mount: 'graph', args: ['knowledge.playbook-a', 'governs'] },
|
|
552
|
-
results: ['
|
|
552
|
+
results: ['system:sales.crm', 'system:sales.lead-gen']
|
|
553
553
|
})
|
|
554
554
|
const parsed = JSON.parse(raw) as { results: string[] }
|
|
555
|
-
expect(parsed.results).toEqual(['
|
|
555
|
+
expect(parsed.results).toEqual(['system:sales.crm', 'system:sales.lead-gen'])
|
|
556
556
|
})
|
|
557
557
|
|
|
558
|
-
it('envelope is NOT flat { results }
|
|
558
|
+
it('envelope is NOT flat { results } — must have path/mount/args siblings', () => {
|
|
559
559
|
const raw = formatJson({
|
|
560
560
|
path: '/by-kind/playbook',
|
|
561
561
|
parsed: { mount: 'by-kind', args: ['playbook'] },
|
|
@@ -587,7 +587,8 @@ describe('formatIdsOnly', () => {
|
|
|
587
587
|
})
|
|
588
588
|
|
|
589
589
|
it('returns newline-separated strings for string[] (governs results)', () => {
|
|
590
|
-
const output = formatIdsOnly(['
|
|
591
|
-
expect(output).toBe('
|
|
590
|
+
const output = formatIdsOnly(['system:sales.crm', 'system:sales.lead-gen'])
|
|
591
|
+
expect(output).toBe('system:sales.crm\nsystem:sales.lead-gen')
|
|
592
592
|
})
|
|
593
593
|
})
|
|
594
|
+
|