@hed-hog/core 0.0.373 → 0.0.374
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/README.md +83 -1
- package/dist/integration-profile/integration-profile.secrets.d.ts.map +1 -1
- package/dist/integration-profile/integration-profile.secrets.js +4 -0
- package/dist/integration-profile/integration-profile.secrets.js.map +1 -1
- package/dist/integration-profile/integration-profile.utils.d.ts +6 -0
- package/dist/integration-profile/integration-profile.utils.d.ts.map +1 -1
- package/dist/integration-profile/integration-profile.utils.js +1 -1
- package/dist/integration-profile/integration-profile.utils.js.map +1 -1
- package/dist/oauth/oauth.controller.d.ts +11 -3
- package/dist/oauth/oauth.controller.d.ts.map +1 -1
- package/dist/oauth/oauth.controller.js +52 -14
- package/dist/oauth/oauth.controller.js.map +1 -1
- package/dist/oauth/oauth.module.d.ts.map +1 -1
- package/dist/oauth/oauth.module.js +4 -0
- package/dist/oauth/oauth.module.js.map +1 -1
- package/dist/oauth/oauth.service.d.ts +38 -2
- package/dist/oauth/oauth.service.d.ts.map +1 -1
- package/dist/oauth/oauth.service.js +122 -5
- package/dist/oauth/oauth.service.js.map +1 -1
- package/dist/oauth/providers/apple.provider.d.ts +34 -0
- package/dist/oauth/providers/apple.provider.d.ts.map +1 -0
- package/dist/oauth/providers/apple.provider.js +197 -0
- package/dist/oauth/providers/apple.provider.js.map +1 -0
- package/dist/oauth/providers/facebook.provider.d.ts +1 -1
- package/dist/oauth/providers/facebook.provider.d.ts.map +1 -1
- package/dist/oauth/providers/facebook.provider.js +3 -2
- package/dist/oauth/providers/facebook.provider.js.map +1 -1
- package/dist/oauth/providers/google.provider.d.ts +1 -1
- package/dist/oauth/providers/google.provider.d.ts.map +1 -1
- package/dist/oauth/providers/google.provider.js +3 -2
- package/dist/oauth/providers/google.provider.js.map +1 -1
- package/dist/oauth/providers/linkedin.provider.d.ts +25 -0
- package/dist/oauth/providers/linkedin.provider.d.ts.map +1 -0
- package/dist/oauth/providers/linkedin.provider.js +99 -0
- package/dist/oauth/providers/linkedin.provider.js.map +1 -0
- package/dist/oauth/providers/microsoft-entra-id.provider.js +1 -1
- package/dist/oauth/providers/microsoft-entra-id.provider.js.map +1 -1
- package/dist/oauth/providers/microsoft.provider.d.ts +1 -1
- package/dist/oauth/providers/microsoft.provider.d.ts.map +1 -1
- package/dist/oauth/providers/microsoft.provider.js +3 -2
- package/dist/oauth/providers/microsoft.provider.js.map +1 -1
- package/dist/pdf/pdf.service.d.ts.map +1 -1
- package/dist/pdf/pdf.service.js +19 -2
- package/dist/pdf/pdf.service.js.map +1 -1
- package/dist/setting/setting.service.d.ts.map +1 -1
- package/dist/setting/setting.service.js +4 -1
- package/dist/setting/setting.service.js.map +1 -1
- package/hedhog/data/integration_provider.yaml +16 -0
- package/hedhog/data/setting_group.yaml +98 -0
- package/hedhog/data/setting_subgroup.yaml +16 -0
- package/hedhog/frontend/app/configurations/[slug]/components/setting-field.tsx.ejs +2 -0
- package/hedhog/frontend/app/dashboard/dashboard.css.ejs +11 -11
- package/hedhog/frontend/app/integration/profiles/page.tsx.ejs +42 -0
- package/hedhog/frontend/app/menu/page.tsx.ejs +1 -1
- package/hedhog/frontend/app/users/user-sheet.tsx.ejs +8 -0
- package/hedhog/frontend/messages/en.json +35 -0
- package/hedhog/frontend/messages/pt.json +35 -0
- package/hedhog/table/user_account.yaml +1 -0
- package/package.json +3 -3
- package/src/access-log/access-log.service.spec.ts +235 -0
- package/src/ai-instruction/ai-prompt-builder.service.spec.ts +250 -0
- package/src/auth/auth.service.spec.ts +891 -0
- package/src/auth/guards/auth.guard.spec.ts +177 -0
- package/src/challenge/challenge.service.spec.ts +334 -0
- package/src/dashboard/dashboard-core/dashboard-core.service.spec.ts +193 -0
- package/src/file/image-optimization.service.spec.ts +283 -0
- package/src/file/provider/provider.factory.spec.ts +111 -0
- package/src/install/install.service.spec.ts +139 -0
- package/src/integration/services/event-subscriber.registry.spec.ts +143 -0
- package/src/integration/services/inbox.service.spec.ts +183 -0
- package/src/integration/services/integration-link.service.spec.ts +217 -0
- package/src/integration/services/outbox.service.spec.ts +290 -0
- package/src/integration-profile/integration-credential-crypto.service.spec.ts +289 -0
- package/src/integration-profile/integration-profile.secrets.ts +4 -0
- package/src/integration-profile/integration-profile.utils.spec.ts +332 -0
- package/src/integration-profile/integration-profile.utils.ts +14 -1
- package/src/locale/locale.service.spec.ts +78 -0
- package/src/mail/mail.service.spec.ts +257 -0
- package/src/mcp/guards/mcp-auth.guard.spec.ts +87 -0
- package/src/mcp/guards/mcp-permission.guard.spec.ts +64 -0
- package/src/mcp/mcp-api-key.service.spec.ts +125 -0
- package/src/mcp/mcp-registry.service.spec.ts +96 -0
- package/src/mcp/mcp-server.service.spec.ts +187 -0
- package/src/menu/menu.service.spec.ts +76 -0
- package/src/notification/notification.service.spec.ts +297 -0
- package/src/oauth/oauth.controller.ts +53 -10
- package/src/oauth/oauth.module.ts +4 -0
- package/src/oauth/oauth.service.spec.ts +332 -0
- package/src/oauth/oauth.service.ts +156 -1
- package/src/oauth/providers/apple.provider.ts +175 -0
- package/src/oauth/providers/facebook.provider.ts +3 -2
- package/src/oauth/providers/google.provider.ts +3 -2
- package/src/oauth/providers/linkedin.provider.ts +96 -0
- package/src/oauth/providers/microsoft-entra-id.provider.ts +1 -1
- package/src/oauth/providers/microsoft.provider.ts +3 -2
- package/src/pdf/pdf.service.ts +20 -2
- package/src/role/guards/role.guard.spec.ts +157 -0
- package/src/role/role.service.spec.ts +179 -0
- package/src/session/session.service.spec.ts +376 -0
- package/src/setting/setting.service.spec.ts +316 -0
- package/src/setting/setting.service.ts +4 -1
- package/src/task/task.service.spec.ts +47 -0
- package/src/token/token.service.spec.ts +351 -0
- package/src/user/user.service.spec.ts +256 -0
- package/src/utils/locale-context.spec.ts +22 -0
- package/src/utils/webauthn-origin.spec.ts +73 -0
- package/src/webhook-integration/integration-event-catalog.service.spec.ts +149 -0
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
import { describe, it, expect, jest, beforeEach, afterEach } from '@jest/globals';
|
|
2
|
+
import { AiPromptBuilderService } from './ai-prompt-builder.service';
|
|
3
|
+
|
|
4
|
+
const makeService = () => {
|
|
5
|
+
const instructionService = {
|
|
6
|
+
getLayer: jest.fn(),
|
|
7
|
+
getAgentInstruction: jest.fn(),
|
|
8
|
+
};
|
|
9
|
+
const service = new AiPromptBuilderService(instructionService as any);
|
|
10
|
+
return { service, instructionService };
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
describe('AiPromptBuilderService', () => {
|
|
14
|
+
afterEach(() => {
|
|
15
|
+
jest.clearAllMocks();
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
describe('build', () => {
|
|
19
|
+
it('inclui apenas a camada system quando é a única disponível', async () => {
|
|
20
|
+
const { service, instructionService } = makeService();
|
|
21
|
+
instructionService.getLayer.mockImplementation(async (layer: string) =>
|
|
22
|
+
layer === 'system' ? 'SYS' : null,
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
const result = await service.build({});
|
|
26
|
+
|
|
27
|
+
expect(result.layers).toEqual(['SYS']);
|
|
28
|
+
expect(result.layerNames).toEqual(['system']);
|
|
29
|
+
expect(result.systemPrompt).toBe('SYS');
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('junta system + product com o separador', async () => {
|
|
33
|
+
const { service, instructionService } = makeService();
|
|
34
|
+
instructionService.getLayer.mockImplementation(async (layer: string) => {
|
|
35
|
+
if (layer === 'system') return 'SYS';
|
|
36
|
+
if (layer === 'product') return 'PROD';
|
|
37
|
+
return null;
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
const result = await service.build({});
|
|
41
|
+
|
|
42
|
+
expect(result.layers).toEqual(['SYS', 'PROD']);
|
|
43
|
+
expect(result.layerNames).toEqual(['system', 'product']);
|
|
44
|
+
expect(result.systemPrompt).toBe('SYS\n\n---\n\nPROD');
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('inclui a camada module quando moduleSlug é fornecido e existe', async () => {
|
|
48
|
+
const { service, instructionService } = makeService();
|
|
49
|
+
instructionService.getLayer.mockImplementation(
|
|
50
|
+
async (layer: string, slug?: string) => {
|
|
51
|
+
if (layer === 'system') return 'SYS';
|
|
52
|
+
if (layer === 'module' && slug === 'crm') return 'MOD';
|
|
53
|
+
return null;
|
|
54
|
+
},
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
const result = await service.build({ moduleSlug: 'crm' });
|
|
58
|
+
|
|
59
|
+
expect(result.layerNames).toEqual(['system', 'module:crm']);
|
|
60
|
+
expect(instructionService.getLayer).toHaveBeenCalledWith('module', 'crm');
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('não inclui module quando moduleSlug é fornecido mas não há conteúdo', async () => {
|
|
64
|
+
const { service, instructionService } = makeService();
|
|
65
|
+
instructionService.getLayer.mockImplementation(async (layer: string) =>
|
|
66
|
+
layer === 'system' ? 'SYS' : null,
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
const result = await service.build({ moduleSlug: 'crm' });
|
|
70
|
+
|
|
71
|
+
expect(result.layerNames).toEqual(['system']);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('inclui a camada agent quando agentSlug é fornecido e existe', async () => {
|
|
75
|
+
const { service, instructionService } = makeService();
|
|
76
|
+
instructionService.getLayer.mockImplementation(async (layer: string) =>
|
|
77
|
+
layer === 'system' ? 'SYS' : null,
|
|
78
|
+
);
|
|
79
|
+
instructionService.getAgentInstruction.mockResolvedValue('AGENT');
|
|
80
|
+
|
|
81
|
+
const result = await service.build({ agentSlug: 'support' });
|
|
82
|
+
|
|
83
|
+
expect(result.layerNames).toEqual(['system', 'agent:support']);
|
|
84
|
+
expect(result.layers).toEqual(['SYS', 'AGENT']);
|
|
85
|
+
expect(instructionService.getAgentInstruction).toHaveBeenCalledWith('support');
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('compõe as quatro camadas na ordem de prioridade', async () => {
|
|
89
|
+
const { service, instructionService } = makeService();
|
|
90
|
+
instructionService.getLayer.mockImplementation(
|
|
91
|
+
async (layer: string, slug?: string) => {
|
|
92
|
+
if (layer === 'system') return 'L1';
|
|
93
|
+
if (layer === 'product') return 'L2';
|
|
94
|
+
if (layer === 'module' && slug === 'm') return 'L3';
|
|
95
|
+
return null;
|
|
96
|
+
},
|
|
97
|
+
);
|
|
98
|
+
instructionService.getAgentInstruction.mockResolvedValue('L4');
|
|
99
|
+
|
|
100
|
+
const result = await service.build({ moduleSlug: 'm', agentSlug: 'a' });
|
|
101
|
+
|
|
102
|
+
expect(result.layers).toEqual(['L1', 'L2', 'L3', 'L4']);
|
|
103
|
+
expect(result.systemPrompt).toBe('L1\n\n---\n\nL2\n\n---\n\nL3\n\n---\n\nL4');
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it('retorna prompt vazio quando nenhuma camada tem conteúdo', async () => {
|
|
107
|
+
const { service, instructionService } = makeService();
|
|
108
|
+
instructionService.getLayer.mockResolvedValue(null);
|
|
109
|
+
|
|
110
|
+
const result = await service.build({});
|
|
111
|
+
|
|
112
|
+
expect(result.layers).toEqual([]);
|
|
113
|
+
expect(result.layerNames).toEqual([]);
|
|
114
|
+
expect(result.systemPrompt).toBe('');
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
describe('buildRuntimeContext', () => {
|
|
119
|
+
it('inclui apenas o cabeçalho quando não há parâmetros', () => {
|
|
120
|
+
const { service } = makeService();
|
|
121
|
+
expect(service.buildRuntimeContext({})).toBe('[Runtime context]');
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it('inclui campos presentes na ordem esperada', () => {
|
|
125
|
+
const { service } = makeService();
|
|
126
|
+
const out = service.buildRuntimeContext({
|
|
127
|
+
userId: 7,
|
|
128
|
+
locale: 'pt',
|
|
129
|
+
userRoles: ['admin', 'user'],
|
|
130
|
+
currentDate: '2026-07-04',
|
|
131
|
+
});
|
|
132
|
+
expect(out).toBe(
|
|
133
|
+
[
|
|
134
|
+
'[Runtime context]',
|
|
135
|
+
'user_id: 7',
|
|
136
|
+
'locale: pt',
|
|
137
|
+
'roles: admin, user',
|
|
138
|
+
'date: 2026-07-04',
|
|
139
|
+
].join('\n'),
|
|
140
|
+
);
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it('ignora userRoles vazio', () => {
|
|
144
|
+
const { service } = makeService();
|
|
145
|
+
const out = service.buildRuntimeContext({ userId: 1, userRoles: [] });
|
|
146
|
+
expect(out).toBe('[Runtime context]\nuser_id: 1');
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it('anexa extraContext como pares chave: valor', () => {
|
|
150
|
+
const { service } = makeService();
|
|
151
|
+
const out = service.buildRuntimeContext({
|
|
152
|
+
userId: 1,
|
|
153
|
+
extraContext: { plan: 'pro', tz: 'UTC' },
|
|
154
|
+
});
|
|
155
|
+
expect(out).toContain('plan: pro');
|
|
156
|
+
expect(out).toContain('tz: UTC');
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
describe('summarizeHistory', () => {
|
|
161
|
+
it('retorna as mensagens intactas quando o total <= keepLast', () => {
|
|
162
|
+
const { service } = makeService();
|
|
163
|
+
const messages = [
|
|
164
|
+
{ role: 'user', content: 'a' },
|
|
165
|
+
{ role: 'assistant', content: 'b' },
|
|
166
|
+
];
|
|
167
|
+
expect(service.summarizeHistory(messages, 6)).toBe(messages);
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it('resume as mensagens antigas e mantém as recentes verbatim', () => {
|
|
171
|
+
const { service } = makeService();
|
|
172
|
+
const messages = Array.from({ length: 10 }, (_, i) => ({
|
|
173
|
+
role: i % 2 === 0 ? 'user' : 'assistant',
|
|
174
|
+
content: `msg-${i}`,
|
|
175
|
+
}));
|
|
176
|
+
|
|
177
|
+
const result = service.summarizeHistory(messages, 6);
|
|
178
|
+
|
|
179
|
+
// 1 mensagem de resumo + 6 recentes
|
|
180
|
+
expect(result).toHaveLength(7);
|
|
181
|
+
expect(result[0].role).toBe('user');
|
|
182
|
+
expect(result[0].content).toContain('[Earlier conversation summary: 4 messages]');
|
|
183
|
+
expect(result[0].content).toContain('First question: "msg-0"');
|
|
184
|
+
// As 6 últimas verbatim.
|
|
185
|
+
expect(result.slice(1)).toEqual(messages.slice(4));
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
it('inclui a contagem de tool_call quando há chamadas de ferramenta', () => {
|
|
189
|
+
const { service } = makeService();
|
|
190
|
+
const messages = [
|
|
191
|
+
{ role: 'user', content: 'q' },
|
|
192
|
+
{ role: 'tool_call', content: 't1' },
|
|
193
|
+
{ role: 'tool_call', content: 't2' },
|
|
194
|
+
{ role: 'assistant', content: 'r' },
|
|
195
|
+
{ role: 'user', content: 'q2' },
|
|
196
|
+
{ role: 'assistant', content: 'r2' },
|
|
197
|
+
{ role: 'user', content: 'q3' },
|
|
198
|
+
{ role: 'assistant', content: 'r3' },
|
|
199
|
+
];
|
|
200
|
+
|
|
201
|
+
const result = service.summarizeHistory(messages, 2);
|
|
202
|
+
|
|
203
|
+
expect(result[0].content).toContain('Tools called: 2');
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
it('trunca a primeira pergunta em 120 caracteres', () => {
|
|
207
|
+
const { service } = makeService();
|
|
208
|
+
const longQuestion = 'x'.repeat(200);
|
|
209
|
+
const messages = [
|
|
210
|
+
{ role: 'user', content: longQuestion },
|
|
211
|
+
...Array.from({ length: 6 }, (_, i) => ({
|
|
212
|
+
role: 'assistant',
|
|
213
|
+
content: `r-${i}`,
|
|
214
|
+
})),
|
|
215
|
+
];
|
|
216
|
+
|
|
217
|
+
const result = service.summarizeHistory(messages, 1);
|
|
218
|
+
|
|
219
|
+
expect(result[0].content).toContain(`"${'x'.repeat(120)}"`);
|
|
220
|
+
expect(result[0].content).not.toContain('x'.repeat(121));
|
|
221
|
+
});
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
describe('interpolate', () => {
|
|
225
|
+
it('substitui os placeholders {{var}} pelos valores', () => {
|
|
226
|
+
const { service } = makeService();
|
|
227
|
+
expect(
|
|
228
|
+
service.interpolate('Olá {{name}}, seu plano é {{plan}}', {
|
|
229
|
+
name: 'Ana',
|
|
230
|
+
plan: 'pro',
|
|
231
|
+
}),
|
|
232
|
+
).toBe('Olá Ana, seu plano é pro');
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
it('mantém o placeholder quando a variável não existe', () => {
|
|
236
|
+
const { service } = makeService();
|
|
237
|
+
expect(service.interpolate('valor {{missing}}', {})).toBe('valor {{missing}}');
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
it('substitui múltiplas ocorrências da mesma variável', () => {
|
|
241
|
+
const { service } = makeService();
|
|
242
|
+
expect(service.interpolate('{{x}}-{{x}}', { x: 'a' })).toBe('a-a');
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
it('não altera texto sem placeholders', () => {
|
|
246
|
+
const { service } = makeService();
|
|
247
|
+
expect(service.interpolate('sem variaveis', { a: '1' })).toBe('sem variaveis');
|
|
248
|
+
});
|
|
249
|
+
});
|
|
250
|
+
});
|