@elevasis/core 0.20.0 → 0.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/dist/index.d.ts +524 -6
  2. package/dist/index.js +417 -42
  3. package/dist/knowledge/index.d.ts +151 -1
  4. package/dist/organization-model/index.d.ts +524 -6
  5. package/dist/organization-model/index.js +417 -42
  6. package/dist/test-utils/index.d.ts +270 -1
  7. package/dist/test-utils/index.js +407 -41
  8. package/package.json +5 -5
  9. package/src/_gen/__tests__/__snapshots__/contracts.md.snap +501 -303
  10. package/src/auth/multi-tenancy/permissions.ts +20 -8
  11. package/src/business/README.md +2 -2
  12. package/src/business/acquisition/api-schemas.test.ts +198 -0
  13. package/src/business/acquisition/api-schemas.ts +250 -9
  14. package/src/business/acquisition/build-templates.test.ts +28 -0
  15. package/src/business/acquisition/build-templates.ts +20 -8
  16. package/src/business/acquisition/index.ts +12 -0
  17. package/src/business/acquisition/types.ts +6 -1
  18. package/src/business/clients/api-schemas.test.ts +115 -0
  19. package/src/business/clients/api-schemas.ts +158 -0
  20. package/src/business/clients/index.ts +1 -0
  21. package/src/business/deals/api-schemas.ts +8 -0
  22. package/src/business/index.ts +5 -2
  23. package/src/business/projects/types.ts +19 -0
  24. package/src/execution/engine/__tests__/fixtures/test-agents.ts +10 -8
  25. package/src/execution/engine/agent/core/__tests__/agent.test.ts +16 -12
  26. package/src/execution/engine/agent/core/__tests__/error-passthrough.test.ts +4 -3
  27. package/src/execution/engine/agent/core/types.ts +25 -15
  28. package/src/execution/engine/agent/index.ts +6 -4
  29. package/src/execution/engine/agent/reasoning/__tests__/request-builder.test.ts +24 -18
  30. package/src/execution/engine/index.ts +3 -0
  31. package/src/execution/engine/tools/integration/server/adapters/apify/apify-adapter.test.ts +55 -0
  32. package/src/execution/engine/tools/integration/server/adapters/apify/apify-adapter.ts +107 -41
  33. package/src/execution/engine/tools/integration/server/adapters/apollo/apollo-adapter.test.ts +48 -0
  34. package/src/execution/engine/tools/integration/server/adapters/apollo/apollo-adapter.ts +99 -0
  35. package/src/execution/engine/tools/integration/server/adapters/apollo/index.ts +1 -0
  36. package/src/execution/engine/tools/integration/server/adapters/clickup/clickup-adapter.test.ts +18 -0
  37. package/src/execution/engine/tools/integration/server/adapters/clickup/clickup-adapter.ts +194 -0
  38. package/src/execution/engine/tools/integration/server/adapters/clickup/index.ts +7 -0
  39. package/src/execution/engine/workflow/types.ts +7 -0
  40. package/src/integrations/credentials/api-schemas.ts +21 -2
  41. package/src/integrations/credentials/schemas.ts +200 -164
  42. package/src/organization-model/README.md +10 -3
  43. package/src/organization-model/__tests__/defaults.test.ts +6 -0
  44. package/src/organization-model/__tests__/domains/resources.test.ts +188 -0
  45. package/src/organization-model/__tests__/domains/roles.test.ts +402 -347
  46. package/src/organization-model/__tests__/domains/systems.test.ts +193 -0
  47. package/src/organization-model/__tests__/knowledge.test.ts +39 -0
  48. package/src/organization-model/__tests__/prospecting-ssot.test.ts +7 -4
  49. package/src/organization-model/__tests__/resolve.test.ts +1 -1
  50. package/src/organization-model/defaults.ts +24 -3
  51. package/src/organization-model/domains/knowledge.ts +3 -2
  52. package/src/organization-model/domains/prospecting.ts +182 -25
  53. package/src/organization-model/domains/resources.ts +88 -0
  54. package/src/organization-model/domains/roles.ts +93 -55
  55. package/src/organization-model/domains/sales.ts +24 -3
  56. package/src/organization-model/domains/systems.ts +46 -0
  57. package/src/organization-model/icons.ts +1 -0
  58. package/src/organization-model/index.ts +2 -0
  59. package/src/organization-model/organization-model.mdx +33 -14
  60. package/src/organization-model/published.ts +52 -1
  61. package/src/organization-model/schema.ts +121 -0
  62. package/src/organization-model/types.ts +46 -1
  63. package/src/platform/api/types.ts +38 -35
  64. package/src/platform/constants/versions.ts +1 -1
  65. package/src/platform/registry/__tests__/resource-registry.test.ts +2051 -2005
  66. package/src/platform/registry/__tests__/validation.test.ts +1343 -1086
  67. package/src/platform/registry/index.ts +14 -0
  68. package/src/platform/registry/resource-registry.ts +40 -2
  69. package/src/platform/registry/serialization.ts +241 -202
  70. package/src/platform/registry/serialized-types.ts +1 -0
  71. package/src/platform/registry/types.ts +411 -361
  72. package/src/platform/registry/validation.ts +743 -513
  73. package/src/projects/api-schemas.ts +290 -267
  74. package/src/reference/_generated/contracts.md +501 -303
  75. package/src/reference/glossary.md +8 -3
  76. package/src/server.ts +2 -0
  77. package/src/supabase/database.types.ts +121 -0
@@ -0,0 +1,115 @@
1
+ import { describe, expect, it } from 'vitest'
2
+ import {
3
+ ClientDetailResponseSchema,
4
+ ClientListResponseSchema,
5
+ ClientStatusResponseSchema,
6
+ ListClientsQuerySchema
7
+ } from './api-schemas'
8
+
9
+ const VALID_UUID = '00000000-0000-4000-8000-000000000001'
10
+ const ISO_TS = '2026-05-08T00:00:00.000Z'
11
+
12
+ describe('client API schemas', () => {
13
+ it('coerces list pagination and accepts status/search filters', () => {
14
+ const result = ListClientsQuerySchema.safeParse({
15
+ status: 'active',
16
+ search: 'Acme',
17
+ limit: '10',
18
+ offset: '20'
19
+ })
20
+
21
+ expect(result.success).toBe(true)
22
+ if (result.success) {
23
+ expect(result.data).toEqual({
24
+ status: 'active',
25
+ search: 'Acme',
26
+ limit: 10,
27
+ offset: 20
28
+ })
29
+ }
30
+ })
31
+
32
+ it('rejects unknown list query fields', () => {
33
+ expect(ListClientsQuerySchema.safeParse({ includeDeals: true }).success).toBe(false)
34
+ })
35
+
36
+ it('accepts client list responses', () => {
37
+ expect(
38
+ ClientListResponseSchema.safeParse({
39
+ data: [
40
+ {
41
+ id: VALID_UUID,
42
+ organizationId: VALID_UUID,
43
+ name: 'Acme',
44
+ status: 'active',
45
+ sourceDealId: null,
46
+ primaryCompanyId: null,
47
+ primaryContactId: null,
48
+ convertedAt: null,
49
+ metadata: {},
50
+ createdAt: ISO_TS,
51
+ updatedAt: ISO_TS
52
+ }
53
+ ],
54
+ total: 1,
55
+ limit: 50,
56
+ offset: 0
57
+ }).success
58
+ ).toBe(true)
59
+ })
60
+
61
+ it('accepts client detail lineage refs', () => {
62
+ expect(
63
+ ClientDetailResponseSchema.safeParse({
64
+ id: VALID_UUID,
65
+ organizationId: VALID_UUID,
66
+ name: 'Acme',
67
+ status: 'onboarding',
68
+ sourceDealId: VALID_UUID,
69
+ primaryCompanyId: VALID_UUID,
70
+ primaryContactId: VALID_UUID,
71
+ convertedAt: ISO_TS,
72
+ metadata: { source: 'closed_won' },
73
+ createdAt: ISO_TS,
74
+ updatedAt: ISO_TS,
75
+ lineage: {
76
+ deals: [
77
+ {
78
+ id: VALID_UUID,
79
+ contactEmail: 'lead@example.com',
80
+ stageKey: 'closed_won',
81
+ stateKey: null,
82
+ updatedAt: ISO_TS
83
+ }
84
+ ],
85
+ projects: [{ id: VALID_UUID, name: 'Delivery', kind: 'client_engagement', status: 'active', updatedAt: ISO_TS }],
86
+ companies: [{ id: VALID_UUID, name: 'Acme Co', domain: 'acme.test', status: 'active' }],
87
+ contacts: [
88
+ {
89
+ id: VALID_UUID,
90
+ email: 'lead@example.com',
91
+ firstName: 'Ada',
92
+ lastName: null,
93
+ title: 'CEO',
94
+ companyId: VALID_UUID,
95
+ status: 'active'
96
+ }
97
+ ]
98
+ }
99
+ }).success
100
+ ).toBe(true)
101
+ })
102
+
103
+ it('accepts client portfolio status responses', () => {
104
+ expect(
105
+ ClientStatusResponseSchema.safeParse({
106
+ totalClients: 1,
107
+ byStatus: { active: 1, onboarding: 0, paused: 0, completed: 0, churned: 0 },
108
+ linkedDeals: 1,
109
+ linkedProjects: 1,
110
+ linkedCompanies: 1,
111
+ linkedContacts: 1
112
+ }).success
113
+ ).toBe(true)
114
+ })
115
+ })
@@ -0,0 +1,158 @@
1
+ import { z } from 'zod'
2
+ import { UuidSchema } from '../../platform/utils/validation'
3
+
4
+ export const ClientStatusSchema = z.enum(['active', 'onboarding', 'paused', 'completed', 'churned'])
5
+
6
+ export const ClientIdParamsSchema = z
7
+ .object({
8
+ clientId: UuidSchema
9
+ })
10
+ .strict()
11
+
12
+ export const ListClientsQuerySchema = z
13
+ .object({
14
+ status: ClientStatusSchema.optional(),
15
+ search: z.string().trim().min(1).max(255).optional(),
16
+ limit: z.coerce.number().int().min(1).max(100).default(50),
17
+ offset: z.coerce.number().int().min(0).default(0)
18
+ })
19
+ .strict()
20
+
21
+ export const ClientRefSchema = z.object({
22
+ id: z.string(),
23
+ name: z.string(),
24
+ status: ClientStatusSchema
25
+ })
26
+
27
+ export const ClientResponseSchema = z.object({
28
+ id: z.string(),
29
+ organizationId: z.string(),
30
+ name: z.string(),
31
+ status: ClientStatusSchema,
32
+ sourceDealId: z.string().nullable(),
33
+ primaryCompanyId: z.string().nullable(),
34
+ primaryContactId: z.string().nullable(),
35
+ convertedAt: z.string().nullable(),
36
+ metadata: z.record(z.string(), z.unknown()),
37
+ createdAt: z.string(),
38
+ updatedAt: z.string()
39
+ })
40
+
41
+ export const ClientDealRefSchema = z.object({
42
+ id: z.string(),
43
+ contactEmail: z.string(),
44
+ stageKey: z.string().nullable(),
45
+ stateKey: z.string().nullable(),
46
+ updatedAt: z.string()
47
+ })
48
+
49
+ export const ClientProjectRefSchema = z.object({
50
+ id: z.string(),
51
+ name: z.string(),
52
+ kind: z.string(),
53
+ status: z.string(),
54
+ updatedAt: z.string()
55
+ })
56
+
57
+ export const ClientCompanyRefSchema = z.object({
58
+ id: z.string(),
59
+ name: z.string(),
60
+ domain: z.string().nullable(),
61
+ status: z.string()
62
+ })
63
+
64
+ export const ClientContactRefSchema = z.object({
65
+ id: z.string(),
66
+ email: z.string(),
67
+ firstName: z.string().nullable(),
68
+ lastName: z.string().nullable(),
69
+ title: z.string().nullable(),
70
+ companyId: z.string().nullable(),
71
+ status: z.string()
72
+ })
73
+
74
+ export const ClientLineageSchema = z.object({
75
+ deals: z.array(ClientDealRefSchema),
76
+ projects: z.array(ClientProjectRefSchema),
77
+ companies: z.array(ClientCompanyRefSchema),
78
+ contacts: z.array(ClientContactRefSchema)
79
+ })
80
+
81
+ export const ClientDetailResponseSchema = ClientResponseSchema.extend({
82
+ lineage: ClientLineageSchema
83
+ })
84
+
85
+ export const ClientListResponseSchema = z.object({
86
+ data: z.array(ClientResponseSchema),
87
+ total: z.number().int().min(0),
88
+ limit: z.number().int().min(1),
89
+ offset: z.number().int().min(0)
90
+ })
91
+
92
+ export const ClientStatusResponseSchema = z.object({
93
+ totalClients: z.number().int().min(0),
94
+ byStatus: z.record(ClientStatusSchema, z.number().int().min(0)),
95
+ linkedDeals: z.number().int().min(0),
96
+ linkedProjects: z.number().int().min(0),
97
+ linkedCompanies: z.number().int().min(0),
98
+ linkedContacts: z.number().int().min(0)
99
+ })
100
+
101
+ export const CreateClientRequestSchema = z
102
+ .object({
103
+ name: z.string().trim().min(1).max(255),
104
+ status: ClientStatusSchema.optional(),
105
+ sourceDealId: UuidSchema.nullable().optional(),
106
+ primaryCompanyId: UuidSchema.nullable().optional(),
107
+ primaryContactId: UuidSchema.nullable().optional(),
108
+ metadata: z.record(z.string(), z.unknown()).nullable().optional()
109
+ })
110
+ .strict()
111
+
112
+ export const UpdateClientRequestSchema = z
113
+ .object({
114
+ name: z.string().trim().min(1).max(255).optional(),
115
+ status: ClientStatusSchema.optional(),
116
+ sourceDealId: UuidSchema.nullable().optional(),
117
+ primaryCompanyId: UuidSchema.nullable().optional(),
118
+ primaryContactId: UuidSchema.nullable().optional(),
119
+ metadata: z.record(z.string(), z.unknown()).nullable().optional()
120
+ })
121
+ .strict()
122
+ .refine((data) => Object.keys(data).length > 0, {
123
+ message: 'At least one field must be provided'
124
+ })
125
+
126
+ export const ClientSchemas = {
127
+ ClientStatus: ClientStatusSchema,
128
+ ClientIdParams: ClientIdParamsSchema,
129
+ ListClientsQuery: ListClientsQuerySchema,
130
+ ClientRef: ClientRefSchema,
131
+ ClientResponse: ClientResponseSchema,
132
+ ClientDealRef: ClientDealRefSchema,
133
+ ClientProjectRef: ClientProjectRefSchema,
134
+ ClientCompanyRef: ClientCompanyRefSchema,
135
+ ClientContactRef: ClientContactRefSchema,
136
+ ClientLineage: ClientLineageSchema,
137
+ ClientDetailResponse: ClientDetailResponseSchema,
138
+ ClientListResponse: ClientListResponseSchema,
139
+ ClientStatusResponse: ClientStatusResponseSchema,
140
+ CreateClientRequest: CreateClientRequestSchema,
141
+ UpdateClientRequest: UpdateClientRequestSchema
142
+ }
143
+
144
+ export type ClientStatus = z.infer<typeof ClientStatusSchema>
145
+ export type ClientIdParams = z.infer<typeof ClientIdParamsSchema>
146
+ export type ListClientsQuery = z.infer<typeof ListClientsQuerySchema>
147
+ export type ClientRef = z.infer<typeof ClientRefSchema>
148
+ export type ClientResponse = z.infer<typeof ClientResponseSchema>
149
+ export type ClientDealRef = z.infer<typeof ClientDealRefSchema>
150
+ export type ClientProjectRef = z.infer<typeof ClientProjectRefSchema>
151
+ export type ClientCompanyRef = z.infer<typeof ClientCompanyRefSchema>
152
+ export type ClientContactRef = z.infer<typeof ClientContactRefSchema>
153
+ export type ClientLineage = z.infer<typeof ClientLineageSchema>
154
+ export type ClientDetailResponse = z.infer<typeof ClientDetailResponseSchema>
155
+ export type ClientListResponse = z.infer<typeof ClientListResponseSchema>
156
+ export type ClientStatusResponse = z.infer<typeof ClientStatusResponseSchema>
157
+ export type CreateClientRequest = z.infer<typeof CreateClientRequestSchema>
158
+ export type UpdateClientRequest = z.infer<typeof UpdateClientRequestSchema>
@@ -0,0 +1 @@
1
+ export * from './api-schemas'
@@ -42,6 +42,10 @@ export {
42
42
  DealLookupResponseSchema,
43
43
  ConversationMessageSchema,
44
44
  DealConversationSchema,
45
+ DealLineageListRefSchema,
46
+ DealLineageProjectRefSchema,
47
+ DealLineageClientRefSchema,
48
+ DealLineageSchema,
45
49
  DealDetailResponseSchema,
46
50
  DealNoteResponseSchema,
47
51
  DealNoteListResponseSchema,
@@ -71,6 +75,10 @@ export {
71
75
  type DealLookupItem,
72
76
  type DealLookupResponse,
73
77
  type ConversationMessage,
78
+ type DealLineageListRef,
79
+ type DealLineageProjectRef,
80
+ type DealLineageClientRef,
81
+ type DealLineage,
74
82
  type DealDetailResponse,
75
83
  type DealNoteResponse,
76
84
  type DealNoteListResponse,
@@ -5,8 +5,11 @@
5
5
  * Note: PDF has its own subpath exports (./pdf, ./pdf/browser, ./pdf/server, ./pdf/sections)
6
6
  */
7
7
 
8
- // Acquisition types (lead database domain types)
9
- export * from './acquisition/index'
8
+ // Acquisition types (lead database domain types)
9
+ export * from './acquisition/index'
10
+
11
+ // Client hub schemas
12
+ export * from './clients/index'
10
13
 
11
14
  // SEO types (seo_pages, seo_metrics, chart types)
12
15
  export * from './seo/index'
@@ -75,6 +75,24 @@ export interface ProjectDetail extends ProjectRow {
75
75
  milestones: MilestoneRow[]
76
76
  tasks: TaskRow[]
77
77
  company: { id: string; name: string; domain: string | null } | null
78
+ deal: ProjectSourceDealRef | null
79
+ client: ProjectClientRef | null
80
+ }
81
+
82
+ export interface ProjectSourceDealRef {
83
+ id: string
84
+ clientId?: string | null
85
+ contactEmail: string
86
+ stageKey: string | null
87
+ stateKey: string | null
88
+ sourceListId: string | null
89
+ updatedAt: string
90
+ }
91
+
92
+ export interface ProjectClientRef {
93
+ id: string
94
+ name: string
95
+ status: string
78
96
  }
79
97
 
80
98
  export interface MilestoneWithTasks extends MilestoneRow {
@@ -104,6 +122,7 @@ export interface Project {
104
122
  status: ProjectStatus
105
123
  description: string | null
106
124
  dealId: string | null
125
+ clientId: string | null
107
126
  clientCompanyId: string | null
108
127
  startDate: string | null
109
128
  targetEndDate: string | null
@@ -11,10 +11,11 @@ export function createPlatformToolAgent(): AgentDefinition {
11
11
  config: {
12
12
  resourceId: 'platform-tool-agent',
13
13
  name: 'Platform Tool Agent',
14
- description: 'Test agent with platform tools for approval workflows',
15
- systemPrompt: 'You help with approvals and notifications',
16
- type: 'agent',
17
- status: 'dev',
14
+ description: 'Test agent with platform tools for approval workflows',
15
+ systemPrompt: 'You help with approvals and notifications',
16
+ type: 'agent',
17
+ kind: 'utility',
18
+ status: 'dev',
18
19
  version: '1.0.0'
19
20
  },
20
21
  contract: {
@@ -68,10 +69,11 @@ export function createKnowledgeMapAgent(): AgentDefinition {
68
69
  config: {
69
70
  resourceId: 'knowledge-agent',
70
71
  name: 'Knowledge Map Agent',
71
- description: 'Test agent with knowledge map for dynamic capability loading',
72
- systemPrompt: 'You can load knowledge nodes as needed',
73
- type: 'agent',
74
- status: 'dev',
72
+ description: 'Test agent with knowledge map for dynamic capability loading',
73
+ systemPrompt: 'You can load knowledge nodes as needed',
74
+ type: 'agent',
75
+ kind: 'utility',
76
+ status: 'dev',
75
77
  version: '1.0.0'
76
78
  },
77
79
  contract: {
@@ -19,9 +19,10 @@ describe('Agent Execution', () => {
19
19
 
20
20
  // Helper: Create minimal agent definition with mock provider
21
21
  const createAgentDefinition = (overrides?: Partial<AgentDefinition>): AgentDefinition => {
22
- const config: AgentConfig = {
23
- type: 'agent',
24
- resourceId: 'test-agent',
22
+ const config: AgentConfig = {
23
+ type: 'agent',
24
+ kind: 'utility',
25
+ resourceId: 'test-agent',
25
26
  name: 'Test Agent',
26
27
  description: 'Agent for testing',
27
28
  environment: 'dev',
@@ -165,9 +166,10 @@ describe('Agent Execution', () => {
165
166
 
166
167
  it('respects maxIterations constraint', async () => {
167
168
  const definition = createAgentDefinition({
168
- config: {
169
- type: 'agent',
170
- resourceId: 'test-agent',
169
+ config: {
170
+ type: 'agent',
171
+ kind: 'utility',
172
+ resourceId: 'test-agent',
171
173
  name: 'Test Agent',
172
174
  description: 'Agent for testing',
173
175
  environment: 'dev',
@@ -418,9 +420,10 @@ describe('Agent Execution', () => {
418
420
 
419
421
  it('handles maxIterations edge case (zero iterations)', async () => {
420
422
  const definition = createAgentDefinition({
421
- config: {
422
- type: 'agent',
423
- resourceId: 'test-agent',
423
+ config: {
424
+ type: 'agent',
425
+ kind: 'utility',
426
+ resourceId: 'test-agent',
424
427
  name: 'Test Agent',
425
428
  description: 'Agent for testing',
426
429
  environment: 'dev',
@@ -442,9 +445,10 @@ describe('Agent Execution', () => {
442
445
 
443
446
  it('handles negative maxIterations', async () => {
444
447
  const definition = createAgentDefinition({
445
- config: {
446
- type: 'agent',
447
- resourceId: 'test-agent',
448
+ config: {
449
+ type: 'agent',
450
+ kind: 'utility',
451
+ resourceId: 'test-agent',
448
452
  name: 'Test Agent',
449
453
  description: 'Agent for testing',
450
454
  environment: 'dev',
@@ -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
- resourceId: 'test-agent',
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 { Tool } from '../../tools/types'
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
- // Agent configuration
36
- export interface AgentConfig extends ResourceDefinition {
37
- type: 'agent'
38
-
39
- // Agent behavior
40
- systemPrompt: string // System prompt defining agent behavior
31
+ export type LLMAdapterFactory = (
32
+ config: ModelConfig,
33
+ ...args: any[]
34
+ ) => LLMAdapter
35
+
36
+ export type AgentKind = 'orchestrator' | 'specialist' | 'utility' | 'system'
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
- AgentConfig,
12
- AgentConstraints,
13
- AgentDefinition,
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
- resourceId: 'test-agent',
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
- resourceId: 'test-agent',
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
- resourceId: 'test-agent',
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
- resourceId: 'test-agent',
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
- resourceId: 'test-agent',
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
- resourceId: 'test-agent',
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,
@@ -0,0 +1,55 @@
1
+ import { afterEach, describe, expect, it, vi } from 'vitest'
2
+ import { ApifyAdapter } from './apify-adapter'
3
+
4
+ describe('ApifyAdapter', () => {
5
+ const adapter = new ApifyAdapter()
6
+ const originalFetch = globalThis.fetch
7
+
8
+ afterEach(() => {
9
+ vi.restoreAllMocks()
10
+ globalThis.fetch = originalFetch
11
+ })
12
+
13
+ it('accepts Apify tokens stored in provider-specific apiToken shape', () => {
14
+ expect(adapter.validateCredentials({ apiToken: 'apify_api_test' })).toBe(true)
15
+ })
16
+
17
+ it('accepts Apify tokens stored in generic api-key shape', () => {
18
+ expect(adapter.validateCredentials({ apiKey: 'apify_api_test' })).toBe(true)
19
+ })
20
+
21
+ it('rejects blank tokens', () => {
22
+ expect(adapter.validateCredentials({ apiKey: ' ' })).toBe(false)
23
+ })
24
+
25
+ it('verifies credentials against the Apify current-user endpoint', async () => {
26
+ const fetchMock = vi.fn().mockResolvedValue({
27
+ ok: true,
28
+ json: vi.fn().mockResolvedValue({
29
+ data: {
30
+ username: 'elevasis',
31
+ plan: {
32
+ id: 'TEAM',
33
+ description: 'Team plan'
34
+ }
35
+ }
36
+ })
37
+ })
38
+ globalThis.fetch = fetchMock as unknown as typeof fetch
39
+
40
+ await expect(adapter.verify({ apiKey: ' apify_api_test ' })).resolves.toEqual({
41
+ ok: true,
42
+ provider: 'apify',
43
+ username: 'elevasis',
44
+ plan: 'TEAM'
45
+ })
46
+
47
+ expect(fetchMock).toHaveBeenCalledWith('https://api.apify.com/v2/users/me', {
48
+ method: 'GET',
49
+ headers: {
50
+ Authorization: 'Bearer apify_api_test',
51
+ 'Content-Type': 'application/json'
52
+ }
53
+ })
54
+ })
55
+ })