@djibrilm/lazy-review 0.0.1
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/LICENSE +21 -0
- package/README.md +95 -0
- package/dist/bin.d.ts +3 -0
- package/dist/bin.d.ts.map +1 -0
- package/dist/bin.js +17 -0
- package/dist/bin.js.map +1 -0
- package/dist/client/assets/geist-cyrillic-ext-wght-normal-DjL33-gN.woff2 +0 -0
- package/dist/client/assets/geist-cyrillic-wght-normal-BEAKL7Jp.woff2 +0 -0
- package/dist/client/assets/geist-latin-ext-wght-normal-DC-KSUi6.woff2 +0 -0
- package/dist/client/assets/geist-latin-wght-normal-BgDaEnEv.woff2 +0 -0
- package/dist/client/assets/geist-vietnamese-wght-normal-6IgcOCM7.woff2 +0 -0
- package/dist/client/assets/index-4oL_AGyI.css +1 -0
- package/dist/client/assets/index-CJtKCyBg.js +848 -0
- package/dist/client/favicon.png +0 -0
- package/dist/client/favicon.svg +1 -0
- package/dist/client/icons.svg +24 -0
- package/dist/client/index.html +17 -0
- package/dist/client/resources/images/logo.png +0 -0
- package/dist/client/resources/images/openai.png +0 -0
- package/dist/constants.d.ts +3 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +3 -0
- package/dist/constants.js.map +1 -0
- package/dist/dev.d.ts +2 -0
- package/dist/dev.d.ts.map +1 -0
- package/dist/dev.js +10 -0
- package/dist/dev.js.map +1 -0
- package/dist/modules/ai-agent/agent-context-manager.d.ts +57 -0
- package/dist/modules/ai-agent/agent-context-manager.d.ts.map +1 -0
- package/dist/modules/ai-agent/agent-context-manager.js +159 -0
- package/dist/modules/ai-agent/agent-context-manager.js.map +1 -0
- package/dist/modules/ai-agent/ai-agent.module.d.ts +17 -0
- package/dist/modules/ai-agent/ai-agent.module.d.ts.map +1 -0
- package/dist/modules/ai-agent/ai-agent.module.js +24 -0
- package/dist/modules/ai-agent/ai-agent.module.js.map +1 -0
- package/dist/modules/ai-agent/ai-agent.utils.d.ts +10 -0
- package/dist/modules/ai-agent/ai-agent.utils.d.ts.map +1 -0
- package/dist/modules/ai-agent/ai-agent.utils.js +389 -0
- package/dist/modules/ai-agent/ai-agent.utils.js.map +1 -0
- package/dist/modules/ai-agent/base.agent.d.ts +26 -0
- package/dist/modules/ai-agent/base.agent.d.ts.map +1 -0
- package/dist/modules/ai-agent/base.agent.js +101 -0
- package/dist/modules/ai-agent/base.agent.js.map +1 -0
- package/dist/modules/ai-agent/chat.agent.d.ts +35 -0
- package/dist/modules/ai-agent/chat.agent.d.ts.map +1 -0
- package/dist/modules/ai-agent/chat.agent.js +590 -0
- package/dist/modules/ai-agent/chat.agent.js.map +1 -0
- package/dist/modules/ai-agent/code-base-indexer.agent.d.ts +13 -0
- package/dist/modules/ai-agent/code-base-indexer.agent.d.ts.map +1 -0
- package/dist/modules/ai-agent/code-base-indexer.agent.js +506 -0
- package/dist/modules/ai-agent/code-base-indexer.agent.js.map +1 -0
- package/dist/modules/ai-agent/codebase-scanner.d.ts +37 -0
- package/dist/modules/ai-agent/codebase-scanner.d.ts.map +1 -0
- package/dist/modules/ai-agent/codebase-scanner.js +260 -0
- package/dist/modules/ai-agent/codebase-scanner.js.map +1 -0
- package/dist/modules/ai-agent/model-loader.d.ts +7 -0
- package/dist/modules/ai-agent/model-loader.d.ts.map +1 -0
- package/dist/modules/ai-agent/model-loader.js +129 -0
- package/dist/modules/ai-agent/model-loader.js.map +1 -0
- package/dist/modules/ai-agent/orientation-loader.d.ts +2 -0
- package/dist/modules/ai-agent/orientation-loader.d.ts.map +1 -0
- package/dist/modules/ai-agent/orientation-loader.js +70 -0
- package/dist/modules/ai-agent/orientation-loader.js.map +1 -0
- package/dist/modules/ai-agent/pr-review-session.manager.d.ts +36 -0
- package/dist/modules/ai-agent/pr-review-session.manager.d.ts.map +1 -0
- package/dist/modules/ai-agent/pr-review-session.manager.js +106 -0
- package/dist/modules/ai-agent/pr-review-session.manager.js.map +1 -0
- package/dist/modules/ai-agent/pr-review.agent.d.ts +22 -0
- package/dist/modules/ai-agent/pr-review.agent.d.ts.map +1 -0
- package/dist/modules/ai-agent/pr-review.agent.js +269 -0
- package/dist/modules/ai-agent/pr-review.agent.js.map +1 -0
- package/dist/modules/ai-agent/research-loop.d.ts +3 -0
- package/dist/modules/ai-agent/research-loop.d.ts.map +1 -0
- package/dist/modules/ai-agent/research-loop.js +691 -0
- package/dist/modules/ai-agent/research-loop.js.map +1 -0
- package/dist/modules/ai-agent/review-pipeline/agent-loop.d.ts +36 -0
- package/dist/modules/ai-agent/review-pipeline/agent-loop.d.ts.map +1 -0
- package/dist/modules/ai-agent/review-pipeline/agent-loop.js +178 -0
- package/dist/modules/ai-agent/review-pipeline/agent-loop.js.map +1 -0
- package/dist/modules/ai-agent/review-pipeline/change-analyzer.d.ts +8 -0
- package/dist/modules/ai-agent/review-pipeline/change-analyzer.d.ts.map +1 -0
- package/dist/modules/ai-agent/review-pipeline/change-analyzer.js +93 -0
- package/dist/modules/ai-agent/review-pipeline/change-analyzer.js.map +1 -0
- package/dist/modules/ai-agent/review-pipeline/finding-ranker.d.ts +47 -0
- package/dist/modules/ai-agent/review-pipeline/finding-ranker.d.ts.map +1 -0
- package/dist/modules/ai-agent/review-pipeline/finding-ranker.js +91 -0
- package/dist/modules/ai-agent/review-pipeline/finding-ranker.js.map +1 -0
- package/dist/modules/ai-agent/review-pipeline/finding-verifier.d.ts +29 -0
- package/dist/modules/ai-agent/review-pipeline/finding-verifier.d.ts.map +1 -0
- package/dist/modules/ai-agent/review-pipeline/finding-verifier.js +130 -0
- package/dist/modules/ai-agent/review-pipeline/finding-verifier.js.map +1 -0
- package/dist/modules/ai-agent/review-pipeline/repository-explorer.d.ts +21 -0
- package/dist/modules/ai-agent/review-pipeline/repository-explorer.d.ts.map +1 -0
- package/dist/modules/ai-agent/review-pipeline/repository-explorer.js +247 -0
- package/dist/modules/ai-agent/review-pipeline/repository-explorer.js.map +1 -0
- package/dist/modules/ai-agent/review-pipeline/review-controller.d.ts +22 -0
- package/dist/modules/ai-agent/review-pipeline/review-controller.d.ts.map +1 -0
- package/dist/modules/ai-agent/review-pipeline/review-controller.js +102 -0
- package/dist/modules/ai-agent/review-pipeline/review-controller.js.map +1 -0
- package/dist/modules/ai-agent/review-pipeline/review-writer.d.ts +4 -0
- package/dist/modules/ai-agent/review-pipeline/review-writer.d.ts.map +1 -0
- package/dist/modules/ai-agent/review-pipeline/review-writer.js +52 -0
- package/dist/modules/ai-agent/review-pipeline/review-writer.js.map +1 -0
- package/dist/modules/ai-agent/review-pipeline/secret-scanner.d.ts +25 -0
- package/dist/modules/ai-agent/review-pipeline/secret-scanner.d.ts.map +1 -0
- package/dist/modules/ai-agent/review-pipeline/secret-scanner.js +159 -0
- package/dist/modules/ai-agent/review-pipeline/secret-scanner.js.map +1 -0
- package/dist/modules/ai-agent/review-pipeline/specialist-reviewer.d.ts +9 -0
- package/dist/modules/ai-agent/review-pipeline/specialist-reviewer.d.ts.map +1 -0
- package/dist/modules/ai-agent/review-pipeline/specialist-reviewer.js +114 -0
- package/dist/modules/ai-agent/review-pipeline/specialist-reviewer.js.map +1 -0
- package/dist/modules/ai-agent/review-pipeline/types.d.ts +53 -0
- package/dist/modules/ai-agent/review-pipeline/types.d.ts.map +1 -0
- package/dist/modules/ai-agent/review-pipeline/types.js +45 -0
- package/dist/modules/ai-agent/review-pipeline/types.js.map +1 -0
- package/dist/modules/ai-agent/tool-executor.d.ts +37 -0
- package/dist/modules/ai-agent/tool-executor.d.ts.map +1 -0
- package/dist/modules/ai-agent/tool-executor.js +199 -0
- package/dist/modules/ai-agent/tool-executor.js.map +1 -0
- package/dist/modules/ai-agent/tool-types.d.ts +72 -0
- package/dist/modules/ai-agent/tool-types.d.ts.map +1 -0
- package/dist/modules/ai-agent/tool-types.js +2 -0
- package/dist/modules/ai-agent/tool-types.js.map +1 -0
- package/dist/modules/ai-agent/tools/fs-tools.d.ts +54 -0
- package/dist/modules/ai-agent/tools/fs-tools.d.ts.map +1 -0
- package/dist/modules/ai-agent/tools/fs-tools.js +237 -0
- package/dist/modules/ai-agent/tools/fs-tools.js.map +1 -0
- package/dist/modules/ai-agent/tools/git-tools.d.ts +322 -0
- package/dist/modules/ai-agent/tools/git-tools.d.ts.map +1 -0
- package/dist/modules/ai-agent/tools/git-tools.js +557 -0
- package/dist/modules/ai-agent/tools/git-tools.js.map +1 -0
- package/dist/modules/ai-agent/tools/pr-chat-tools.d.ts +15 -0
- package/dist/modules/ai-agent/tools/pr-chat-tools.d.ts.map +1 -0
- package/dist/modules/ai-agent/tools/pr-chat-tools.js +232 -0
- package/dist/modules/ai-agent/tools/pr-chat-tools.js.map +1 -0
- package/dist/modules/database/database.module.d.ts +10 -0
- package/dist/modules/database/database.module.d.ts.map +1 -0
- package/dist/modules/database/database.module.js +36 -0
- package/dist/modules/database/database.module.js.map +1 -0
- package/dist/modules/database/vector-database.service.d.ts +27 -0
- package/dist/modules/database/vector-database.service.d.ts.map +1 -0
- package/dist/modules/database/vector-database.service.js +170 -0
- package/dist/modules/database/vector-database.service.js.map +1 -0
- package/dist/modules/github/github.module.d.ts +964 -0
- package/dist/modules/github/github.module.d.ts.map +1 -0
- package/dist/modules/github/github.module.js +316 -0
- package/dist/modules/github/github.module.js.map +1 -0
- package/dist/modules/main.module.d.ts +20 -0
- package/dist/modules/main.module.d.ts.map +1 -0
- package/dist/modules/main.module.js +59 -0
- package/dist/modules/main.module.js.map +1 -0
- package/dist/modules/qvac/qvac.module.d.ts +28 -0
- package/dist/modules/qvac/qvac.module.d.ts.map +1 -0
- package/dist/modules/qvac/qvac.module.js +146 -0
- package/dist/modules/qvac/qvac.module.js.map +1 -0
- package/dist/modules/server/dtos/create-project.dto.d.ts +9 -0
- package/dist/modules/server/dtos/create-project.dto.d.ts.map +1 -0
- package/dist/modules/server/dtos/create-project.dto.js +2 -0
- package/dist/modules/server/dtos/create-project.dto.js.map +1 -0
- package/dist/modules/server/entities/agent-task.entity.d.ts +16 -0
- package/dist/modules/server/entities/agent-task.entity.d.ts.map +1 -0
- package/dist/modules/server/entities/agent-task.entity.js +72 -0
- package/dist/modules/server/entities/agent-task.entity.js.map +1 -0
- package/dist/modules/server/entities/ai-provider.entity.d.ts +11 -0
- package/dist/modules/server/entities/ai-provider.entity.d.ts.map +1 -0
- package/dist/modules/server/entities/ai-provider.entity.js +47 -0
- package/dist/modules/server/entities/ai-provider.entity.js.map +1 -0
- package/dist/modules/server/entities/deployment-plan-approval.entity.d.ts +10 -0
- package/dist/modules/server/entities/deployment-plan-approval.entity.d.ts.map +1 -0
- package/dist/modules/server/entities/deployment-plan-approval.entity.js +42 -0
- package/dist/modules/server/entities/deployment-plan-approval.entity.js.map +1 -0
- package/dist/modules/server/entities/deployment-plan-artifact.entity.d.ts +10 -0
- package/dist/modules/server/entities/deployment-plan-artifact.entity.d.ts.map +1 -0
- package/dist/modules/server/entities/deployment-plan-artifact.entity.js +42 -0
- package/dist/modules/server/entities/deployment-plan-artifact.entity.js.map +1 -0
- package/dist/modules/server/entities/deployment-plan-feedback.entity.d.ts +9 -0
- package/dist/modules/server/entities/deployment-plan-feedback.entity.d.ts.map +1 -0
- package/dist/modules/server/entities/deployment-plan-feedback.entity.js +37 -0
- package/dist/modules/server/entities/deployment-plan-feedback.entity.js.map +1 -0
- package/dist/modules/server/entities/deployment-plan-revision.entity.d.ts +11 -0
- package/dist/modules/server/entities/deployment-plan-revision.entity.d.ts.map +1 -0
- package/dist/modules/server/entities/deployment-plan-revision.entity.js +47 -0
- package/dist/modules/server/entities/deployment-plan-revision.entity.js.map +1 -0
- package/dist/modules/server/entities/deployment-plan.entity.d.ts +13 -0
- package/dist/modules/server/entities/deployment-plan.entity.d.ts.map +1 -0
- package/dist/modules/server/entities/deployment-plan.entity.js +58 -0
- package/dist/modules/server/entities/deployment-plan.entity.js.map +1 -0
- package/dist/modules/server/entities/project.entity.d.ts +26 -0
- package/dist/modules/server/entities/project.entity.d.ts.map +1 -0
- package/dist/modules/server/entities/project.entity.js +122 -0
- package/dist/modules/server/entities/project.entity.js.map +1 -0
- package/dist/modules/server/entities/settings.entity.d.ts +12 -0
- package/dist/modules/server/entities/settings.entity.d.ts.map +1 -0
- package/dist/modules/server/entities/settings.entity.js +62 -0
- package/dist/modules/server/entities/settings.entity.js.map +1 -0
- package/dist/modules/server/entities/terminal-log.entity.d.ts +10 -0
- package/dist/modules/server/entities/terminal-log.entity.d.ts.map +1 -0
- package/dist/modules/server/entities/terminal-log.entity.js +42 -0
- package/dist/modules/server/entities/terminal-log.entity.js.map +1 -0
- package/dist/modules/server/middleware/auth.middleware.d.ts +8 -0
- package/dist/modules/server/middleware/auth.middleware.d.ts.map +1 -0
- package/dist/modules/server/middleware/auth.middleware.js +26 -0
- package/dist/modules/server/middleware/auth.middleware.js.map +1 -0
- package/dist/modules/server/routes/auth.routes.d.ts +13 -0
- package/dist/modules/server/routes/auth.routes.d.ts.map +1 -0
- package/dist/modules/server/routes/auth.routes.js +59 -0
- package/dist/modules/server/routes/auth.routes.js.map +1 -0
- package/dist/modules/server/routes/github.routes.d.ts +14 -0
- package/dist/modules/server/routes/github.routes.d.ts.map +1 -0
- package/dist/modules/server/routes/github.routes.js +124 -0
- package/dist/modules/server/routes/github.routes.js.map +1 -0
- package/dist/modules/server/routes/project.routes.d.ts +11 -0
- package/dist/modules/server/routes/project.routes.d.ts.map +1 -0
- package/dist/modules/server/routes/project.routes.js +78 -0
- package/dist/modules/server/routes/project.routes.js.map +1 -0
- package/dist/modules/server/routes/qvac.routes.d.ts +11 -0
- package/dist/modules/server/routes/qvac.routes.d.ts.map +1 -0
- package/dist/modules/server/routes/qvac.routes.js +24 -0
- package/dist/modules/server/routes/qvac.routes.js.map +1 -0
- package/dist/modules/server/routes/routes.d.ts +20 -0
- package/dist/modules/server/routes/routes.d.ts.map +1 -0
- package/dist/modules/server/routes/routes.js +35 -0
- package/dist/modules/server/routes/routes.js.map +1 -0
- package/dist/modules/server/routes/settings.controller.d.ts +14 -0
- package/dist/modules/server/routes/settings.controller.d.ts.map +1 -0
- package/dist/modules/server/routes/settings.controller.js +65 -0
- package/dist/modules/server/routes/settings.controller.js.map +1 -0
- package/dist/modules/server/server.module.d.ts +19 -0
- package/dist/modules/server/server.module.d.ts.map +1 -0
- package/dist/modules/server/server.module.js +63 -0
- package/dist/modules/server/server.module.js.map +1 -0
- package/dist/modules/server/services/auth.services.d.ts +19 -0
- package/dist/modules/server/services/auth.services.d.ts.map +1 -0
- package/dist/modules/server/services/auth.services.js +87 -0
- package/dist/modules/server/services/auth.services.js.map +1 -0
- package/dist/modules/server/services/deployment-plan.services.d.ts +6 -0
- package/dist/modules/server/services/deployment-plan.services.d.ts.map +1 -0
- package/dist/modules/server/services/deployment-plan.services.js +8 -0
- package/dist/modules/server/services/deployment-plan.services.js.map +1 -0
- package/dist/modules/server/services/github.services.d.ts +939 -0
- package/dist/modules/server/services/github.services.d.ts.map +1 -0
- package/dist/modules/server/services/github.services.js +46 -0
- package/dist/modules/server/services/github.services.js.map +1 -0
- package/dist/modules/server/services/project.services.d.ts +41 -0
- package/dist/modules/server/services/project.services.d.ts.map +1 -0
- package/dist/modules/server/services/project.services.js +360 -0
- package/dist/modules/server/services/project.services.js.map +1 -0
- package/dist/modules/server/services/qvac.services.d.ts +10 -0
- package/dist/modules/server/services/qvac.services.d.ts.map +1 -0
- package/dist/modules/server/services/qvac.services.js +58 -0
- package/dist/modules/server/services/qvac.services.js.map +1 -0
- package/dist/modules/server/services/settings.service.d.ts +60 -0
- package/dist/modules/server/services/settings.service.d.ts.map +1 -0
- package/dist/modules/server/services/settings.service.js +125 -0
- package/dist/modules/server/services/settings.service.js.map +1 -0
- package/dist/modules/socket-gateway/interfaces/project-creation-log.interface.d.ts +14 -0
- package/dist/modules/socket-gateway/interfaces/project-creation-log.interface.d.ts.map +1 -0
- package/dist/modules/socket-gateway/interfaces/project-creation-log.interface.js +2 -0
- package/dist/modules/socket-gateway/interfaces/project-creation-log.interface.js.map +1 -0
- package/dist/modules/socket-gateway/socket-gateway.module.d.ts +28 -0
- package/dist/modules/socket-gateway/socket-gateway.module.d.ts.map +1 -0
- package/dist/modules/socket-gateway/socket-gateway.module.js +92 -0
- package/dist/modules/socket-gateway/socket-gateway.module.js.map +1 -0
- package/package.json +95 -0
|
@@ -0,0 +1,691 @@
|
|
|
1
|
+
import { completion } from '@qvac/sdk';
|
|
2
|
+
import * as fs from 'fs/promises';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
import { createGitTools } from './tools/git-tools.js';
|
|
5
|
+
import { createFsTools } from './tools/fs-tools.js';
|
|
6
|
+
import { extractJson } from './ai-agent.utils.js';
|
|
7
|
+
import { AgentContextManager } from './agent-context-manager.js';
|
|
8
|
+
import ProjectEntity from '../server/entities/project.entity.js';
|
|
9
|
+
const finishResearchSchema = z.object({
|
|
10
|
+
coverage: z.object({
|
|
11
|
+
entryPoints: z.boolean(),
|
|
12
|
+
runtimeArchitecture: z.boolean(),
|
|
13
|
+
moduleRelationships: z.boolean(),
|
|
14
|
+
primaryDataFlows: z.boolean(),
|
|
15
|
+
persistence: z.boolean(),
|
|
16
|
+
communication: z.boolean(),
|
|
17
|
+
configuration: z.boolean(),
|
|
18
|
+
}),
|
|
19
|
+
remainingUnknowns: z.array(z.string()).default([]),
|
|
20
|
+
});
|
|
21
|
+
const REQUIRED_COVERAGE = [
|
|
22
|
+
'entryPoints',
|
|
23
|
+
'runtimeArchitecture',
|
|
24
|
+
'moduleRelationships',
|
|
25
|
+
'primaryDataFlows',
|
|
26
|
+
'persistence',
|
|
27
|
+
'communication',
|
|
28
|
+
'configuration',
|
|
29
|
+
];
|
|
30
|
+
function normalizeToolArguments(input) {
|
|
31
|
+
if (!input)
|
|
32
|
+
return {};
|
|
33
|
+
if (typeof input === 'string') {
|
|
34
|
+
try {
|
|
35
|
+
const parsed = JSON.parse(input);
|
|
36
|
+
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
|
|
37
|
+
return parsed;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return {};
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
if (typeof input === 'object' && !Array.isArray(input)) {
|
|
45
|
+
return input;
|
|
46
|
+
}
|
|
47
|
+
return {};
|
|
48
|
+
}
|
|
49
|
+
function parseFallbackToolCalls(rawText) {
|
|
50
|
+
const calls = [];
|
|
51
|
+
try {
|
|
52
|
+
const jsonRegex = /\{(?:[^{}]|(?:\{(?:[^{}]|(?:\{[^{}]*\}))*\}))*\}/g;
|
|
53
|
+
let match;
|
|
54
|
+
while ((match = jsonRegex.exec(rawText)) !== null) {
|
|
55
|
+
try {
|
|
56
|
+
const parsed = JSON.parse(match[0]);
|
|
57
|
+
if (parsed.name && parsed.arguments !== undefined) {
|
|
58
|
+
calls.push({
|
|
59
|
+
name: parsed.name,
|
|
60
|
+
arguments: parsed.arguments,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
else if (parsed.name && parsed.args !== undefined) {
|
|
64
|
+
calls.push({
|
|
65
|
+
name: parsed.name,
|
|
66
|
+
arguments: parsed.args,
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
// Ignore malformed JSON fragments.
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
const toolCallRegex = /<\|tool_call\|?>?\s*:?\s*(?:call\s*:\s*)?([a-zA-Z0-9_-]+)\{(.*?)\}/g;
|
|
75
|
+
let toolMatch;
|
|
76
|
+
while ((toolMatch = toolCallRegex.exec(rawText)) !== null) {
|
|
77
|
+
try {
|
|
78
|
+
const body = toolMatch[2]?.trim();
|
|
79
|
+
const args = body ? JSON.parse(`{${body}}`) : {};
|
|
80
|
+
calls.push({
|
|
81
|
+
name: toolMatch[1] || '',
|
|
82
|
+
arguments: args,
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
try {
|
|
87
|
+
calls.push({
|
|
88
|
+
name: toolMatch[1] || '',
|
|
89
|
+
arguments: JSON.parse(toolMatch[2] || '{}'),
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
// Ignore malformed tool-call syntax.
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
// Fall back to no tool calls.
|
|
100
|
+
}
|
|
101
|
+
return calls;
|
|
102
|
+
}
|
|
103
|
+
function getMissingCoverage(coverage) {
|
|
104
|
+
return REQUIRED_COVERAGE.filter((key) => coverage[key] !== true);
|
|
105
|
+
}
|
|
106
|
+
export async function runResearchAgentLoop(project, absoluteRoot, gemma4ModelId, orientationFileContents, progress, checkCancelled, awaitCompletion) {
|
|
107
|
+
const gitTools = createGitTools(absoluteRoot);
|
|
108
|
+
const fsTools = createFsTools(absoluteRoot);
|
|
109
|
+
// Prefer the filesystem implementation of read_file.
|
|
110
|
+
const filteredGitTools = gitTools.filter((tool) => tool.name !== 'read_file');
|
|
111
|
+
const finishTool = {
|
|
112
|
+
name: 'finish_research',
|
|
113
|
+
description: 'Call this only after investigating the major architectural areas of the repository. Report which areas have been covered and any remaining unknowns.',
|
|
114
|
+
parameters: finishResearchSchema,
|
|
115
|
+
handler: (args) => ({
|
|
116
|
+
action: 'finish',
|
|
117
|
+
...finishResearchSchema.parse(args),
|
|
118
|
+
}),
|
|
119
|
+
};
|
|
120
|
+
const researchTools = [...filteredGitTools, ...fsTools, finishTool];
|
|
121
|
+
const MAX_ITERATIONS = 12;
|
|
122
|
+
const MAX_TOOL_CALLS = 20;
|
|
123
|
+
const MAX_SOURCE_TOKENS = 16_000;
|
|
124
|
+
let iteration = 0;
|
|
125
|
+
let toolCallsCount = 0;
|
|
126
|
+
let sourceTokensUsed = 0;
|
|
127
|
+
let isDone = false;
|
|
128
|
+
const jsonSchema = `{
|
|
129
|
+
"project_name": "<string: exact project name>",
|
|
130
|
+
|
|
131
|
+
"application_type": "<string: type of application>",
|
|
132
|
+
|
|
133
|
+
"architecture_pattern": "<string: dominant observed architecture pattern>",
|
|
134
|
+
|
|
135
|
+
"explanation": "<string: concise explanation of purpose, runtime layers, major data flows, persistence, and AI behavior>",
|
|
136
|
+
|
|
137
|
+
"tech_stack": [
|
|
138
|
+
"<string: technology directly observed in repository>"
|
|
139
|
+
],
|
|
140
|
+
|
|
141
|
+
"core_modules": [
|
|
142
|
+
{
|
|
143
|
+
"path": "<string: module path>",
|
|
144
|
+
"desc": "<string: concise role of module>",
|
|
145
|
+
"responsibilities": [
|
|
146
|
+
"<string: responsibility>"
|
|
147
|
+
],
|
|
148
|
+
"depends_on": [
|
|
149
|
+
"<string: important module or subsystem dependency>"
|
|
150
|
+
],
|
|
151
|
+
"used_by": [
|
|
152
|
+
"<string: important callers or consumers>"
|
|
153
|
+
],
|
|
154
|
+
"evidence": [
|
|
155
|
+
"<string: file path, symbol, or line reference>"
|
|
156
|
+
]
|
|
157
|
+
}
|
|
158
|
+
],
|
|
159
|
+
|
|
160
|
+
"folder_structure": "<string: concise description of important directories and their responsibilities>",
|
|
161
|
+
|
|
162
|
+
"important_details": "<string: important setup, build, runtime, indexing, or development details>",
|
|
163
|
+
|
|
164
|
+
"key_conventions": [
|
|
165
|
+
"<string: codebase convention directly observed>"
|
|
166
|
+
],
|
|
167
|
+
|
|
168
|
+
"required_secrets": [
|
|
169
|
+
{
|
|
170
|
+
"key": "<string: environment variable key>",
|
|
171
|
+
"description": "<string: observed purpose>"
|
|
172
|
+
}
|
|
173
|
+
],
|
|
174
|
+
|
|
175
|
+
"entry_points": [
|
|
176
|
+
{
|
|
177
|
+
"path": "<string: file path>",
|
|
178
|
+
"role": "<string: how this entry point starts or participates in the application>",
|
|
179
|
+
"evidence": [
|
|
180
|
+
"<string: file path, symbol, or line reference>"
|
|
181
|
+
]
|
|
182
|
+
}
|
|
183
|
+
],
|
|
184
|
+
|
|
185
|
+
"runtime_components": [
|
|
186
|
+
{
|
|
187
|
+
"name": "<string: runtime component such as CLI, web frontend, backend, worker, local model runtime>",
|
|
188
|
+
"responsibility": "<string: responsibility>",
|
|
189
|
+
"communicates_with": [
|
|
190
|
+
"<string: other component>"
|
|
191
|
+
],
|
|
192
|
+
"evidence": [
|
|
193
|
+
"<string: file path or symbol>"
|
|
194
|
+
]
|
|
195
|
+
}
|
|
196
|
+
],
|
|
197
|
+
|
|
198
|
+
"data_flows": [
|
|
199
|
+
{
|
|
200
|
+
"name": "<string: flow name>",
|
|
201
|
+
"trigger": "<string: what starts the flow>",
|
|
202
|
+
"steps": [
|
|
203
|
+
{
|
|
204
|
+
"component": "<string: module or subsystem>",
|
|
205
|
+
"action": "<string: what happens at this step>"
|
|
206
|
+
}
|
|
207
|
+
],
|
|
208
|
+
"evidence": [
|
|
209
|
+
"<string: file path or symbol>"
|
|
210
|
+
]
|
|
211
|
+
}
|
|
212
|
+
],
|
|
213
|
+
|
|
214
|
+
"communication": [
|
|
215
|
+
{
|
|
216
|
+
"mechanism": "<string: HTTP, Socket.io, IPC, CLI, events, etc.>",
|
|
217
|
+
"purpose": "<string: what this channel is used for>",
|
|
218
|
+
"important_events_or_routes": [
|
|
219
|
+
"<string: event, route, command, or message>"
|
|
220
|
+
],
|
|
221
|
+
"evidence": [
|
|
222
|
+
"<string: file path or symbol>"
|
|
223
|
+
]
|
|
224
|
+
}
|
|
225
|
+
],
|
|
226
|
+
|
|
227
|
+
"persistence": [
|
|
228
|
+
{
|
|
229
|
+
"technology": "<string: persistence technology>",
|
|
230
|
+
"responsibility": "<string: what it stores or retrieves>",
|
|
231
|
+
"stores": [
|
|
232
|
+
"<string: persisted concept or entity>"
|
|
233
|
+
],
|
|
234
|
+
"evidence": [
|
|
235
|
+
"<string: file path or symbol>"
|
|
236
|
+
]
|
|
237
|
+
}
|
|
238
|
+
],
|
|
239
|
+
|
|
240
|
+
"domain_concepts": [
|
|
241
|
+
{
|
|
242
|
+
"name": "<string: important domain concept>",
|
|
243
|
+
"description": "<string: meaning in this application>",
|
|
244
|
+
"related_modules": [
|
|
245
|
+
"<string: relevant module>"
|
|
246
|
+
],
|
|
247
|
+
"evidence": [
|
|
248
|
+
"<string: file path or symbol>"
|
|
249
|
+
]
|
|
250
|
+
}
|
|
251
|
+
],
|
|
252
|
+
|
|
253
|
+
"architectural_invariants": [
|
|
254
|
+
{
|
|
255
|
+
"rule": "<string: architectural rule or constraint that should remain true>",
|
|
256
|
+
"reason": "<string: evidence-backed reason>",
|
|
257
|
+
"evidence": [
|
|
258
|
+
"<string: file path or symbol>"
|
|
259
|
+
]
|
|
260
|
+
}
|
|
261
|
+
],
|
|
262
|
+
|
|
263
|
+
"known_unknowns": [
|
|
264
|
+
"<string: architectural detail that could not be confidently established>"
|
|
265
|
+
]
|
|
266
|
+
}`;
|
|
267
|
+
let topLevelFiles = '';
|
|
268
|
+
try {
|
|
269
|
+
const entries = await fs.readdir(absoluteRoot, {
|
|
270
|
+
withFileTypes: true,
|
|
271
|
+
});
|
|
272
|
+
topLevelFiles = entries
|
|
273
|
+
.filter((entry) => !entry.name.startsWith('.') && entry.name !== 'node_modules')
|
|
274
|
+
.map((entry) => (entry.isDirectory() ? `[DIR] ${entry.name}/` : `[FILE] ${entry.name}`))
|
|
275
|
+
.join('\n');
|
|
276
|
+
}
|
|
277
|
+
catch {
|
|
278
|
+
topLevelFiles = 'Could not read directory structure.';
|
|
279
|
+
}
|
|
280
|
+
const systemPrompt = `
|
|
281
|
+
You are an expert software architect investigating a repository.
|
|
282
|
+
|
|
283
|
+
Your job is not to summarize files one by one.
|
|
284
|
+
|
|
285
|
+
Your job is to build an evidence-backed mental model of how the
|
|
286
|
+
application actually works.
|
|
287
|
+
|
|
288
|
+
RESEARCH GOALS
|
|
289
|
+
|
|
290
|
+
Before finishing, investigate:
|
|
291
|
+
|
|
292
|
+
1. ENTRY POINTS
|
|
293
|
+
Determine what starts the application and where runtime initialization occurs.
|
|
294
|
+
|
|
295
|
+
2. RUNTIME ARCHITECTURE
|
|
296
|
+
Identify the major runtime components, such as CLI, frontend, backend,
|
|
297
|
+
workers, databases, local AI runtimes, or other processes.
|
|
298
|
+
|
|
299
|
+
3. MODULE RELATIONSHIPS
|
|
300
|
+
Determine how important modules depend on, call, or communicate with one another.
|
|
301
|
+
|
|
302
|
+
4. PRIMARY DATA FLOWS
|
|
303
|
+
Trace the most important end-to-end behaviors through the application.
|
|
304
|
+
Examples include startup, indexing, user requests, AI inference,
|
|
305
|
+
persistence, synchronization, review generation, and streaming.
|
|
306
|
+
|
|
307
|
+
5. PERSISTENCE
|
|
308
|
+
Determine what is persisted, where it is persisted, and which modules
|
|
309
|
+
read or write it.
|
|
310
|
+
|
|
311
|
+
6. COMMUNICATION
|
|
312
|
+
Investigate HTTP routes, Socket.io events, IPC, CLI commands,
|
|
313
|
+
internal events, or other important communication boundaries.
|
|
314
|
+
|
|
315
|
+
7. CONFIGURATION
|
|
316
|
+
Determine important configuration files and environment variables only
|
|
317
|
+
when they are directly evidenced by repository contents.
|
|
318
|
+
|
|
319
|
+
8. DOMAIN CONCEPTS
|
|
320
|
+
Identify important application concepts represented by entities,
|
|
321
|
+
services, types, state, or workflows.
|
|
322
|
+
|
|
323
|
+
9. ARCHITECTURAL INVARIANTS
|
|
324
|
+
Identify constraints future changes should preserve when those
|
|
325
|
+
constraints are supported by concrete repository evidence.
|
|
326
|
+
|
|
327
|
+
EVIDENCE RULES
|
|
328
|
+
|
|
329
|
+
- Prefer source-code evidence over guesses.
|
|
330
|
+
- Never invent environment variables.
|
|
331
|
+
- Never infer a technology merely because it is common for the stack.
|
|
332
|
+
- Never claim a data flow without following enough code to support it.
|
|
333
|
+
- Record exact file paths and important symbols for major conclusions.
|
|
334
|
+
- Clearly preserve uncertainty.
|
|
335
|
+
- If something cannot be established, add it to known unknowns.
|
|
336
|
+
- Architectural patterns may be inferred, but the implementation evidence
|
|
337
|
+
supporting the inference must be understood first.
|
|
338
|
+
|
|
339
|
+
RESEARCH STRATEGY
|
|
340
|
+
|
|
341
|
+
- Start with entry points, package configuration, and orientation files.
|
|
342
|
+
- Follow imports and calls into major subsystems.
|
|
343
|
+
- Prefer search, outlines, and symbol-level inspection over entire files.
|
|
344
|
+
- Follow execution across module boundaries when investigating important flows.
|
|
345
|
+
- Do not repeatedly inspect the same file unless necessary.
|
|
346
|
+
- Do not exhaustively read the repository.
|
|
347
|
+
- Spend the research budget on architectural relationships rather than
|
|
348
|
+
collecting isolated code snippets.
|
|
349
|
+
|
|
350
|
+
FINISHING
|
|
351
|
+
|
|
352
|
+
The finish_research coverage booleans mean that an area has been
|
|
353
|
+
investigated sufficiently to determine how it works, whether it is
|
|
354
|
+
applicable, or that a remaining uncertainty has been explicitly recorded.
|
|
355
|
+
|
|
356
|
+
Do not call finish_research merely because the technology stack and
|
|
357
|
+
several important files are known.
|
|
358
|
+
|
|
359
|
+
Do not output the final manifest during research.
|
|
360
|
+
`;
|
|
361
|
+
const contextManager = new AgentContextManager(systemPrompt, gemma4ModelId, 128_000, 2_500, project.id.toString());
|
|
362
|
+
contextManager.addRecent({
|
|
363
|
+
role: 'user',
|
|
364
|
+
content: `/no_think
|
|
365
|
+
## Repository: ${project.name}
|
|
366
|
+
|
|
367
|
+
## Top-level repository structure
|
|
368
|
+
|
|
369
|
+
${topLevelFiles}
|
|
370
|
+
|
|
371
|
+
## Orientation files
|
|
372
|
+
|
|
373
|
+
${orientationFileContents.join('\n\n')}
|
|
374
|
+
|
|
375
|
+
Explore the repository using your tools and build an architectural mental model.`,
|
|
376
|
+
});
|
|
377
|
+
// ─────────────────────────────────────────────
|
|
378
|
+
// PHASE 1: RESEARCH
|
|
379
|
+
// ─────────────────────────────────────────────
|
|
380
|
+
while (!isDone &&
|
|
381
|
+
iteration < MAX_ITERATIONS &&
|
|
382
|
+
toolCallsCount < MAX_TOOL_CALLS &&
|
|
383
|
+
sourceTokensUsed < MAX_SOURCE_TOKENS) {
|
|
384
|
+
checkCancelled();
|
|
385
|
+
iteration++;
|
|
386
|
+
const budget = {
|
|
387
|
+
iterationsRemaining: MAX_ITERATIONS - iteration,
|
|
388
|
+
toolCallsRemaining: MAX_TOOL_CALLS - toolCallsCount,
|
|
389
|
+
sourceTokensRemaining: MAX_SOURCE_TOKENS - sourceTokensUsed,
|
|
390
|
+
};
|
|
391
|
+
if (contextManager.needsCompaction(0, budget)) {
|
|
392
|
+
await contextManager.compactWithLLM(progress);
|
|
393
|
+
}
|
|
394
|
+
const researchRun = completion({
|
|
395
|
+
modelId: gemma4ModelId,
|
|
396
|
+
history: contextManager.buildHistory(budget),
|
|
397
|
+
tools: researchTools,
|
|
398
|
+
toolDialect: 'json',
|
|
399
|
+
stream: false,
|
|
400
|
+
maxTokens: 2048,
|
|
401
|
+
kvCache: project.id.toString(),
|
|
402
|
+
});
|
|
403
|
+
const researchResult = await awaitCompletion(researchRun);
|
|
404
|
+
checkCancelled();
|
|
405
|
+
const rawText = researchResult.raw?.fullText || researchResult.contentText || '';
|
|
406
|
+
let parsedToolCalls = [];
|
|
407
|
+
const resolvedToolCalls = await researchResult.toolCalls;
|
|
408
|
+
if (resolvedToolCalls && resolvedToolCalls.length > 0) {
|
|
409
|
+
parsedToolCalls = resolvedToolCalls.map((toolCall) => ({
|
|
410
|
+
name: toolCall.name || toolCall.function?.name || '',
|
|
411
|
+
arguments: toolCall.arguments || toolCall.function?.arguments || toolCall.args || {},
|
|
412
|
+
}));
|
|
413
|
+
}
|
|
414
|
+
else {
|
|
415
|
+
parsedToolCalls = parseFallbackToolCalls(rawText);
|
|
416
|
+
}
|
|
417
|
+
if (parsedToolCalls.length === 0) {
|
|
418
|
+
if (!rawText.trim()) {
|
|
419
|
+
continue;
|
|
420
|
+
}
|
|
421
|
+
contextManager.addRecent({
|
|
422
|
+
role: 'assistant',
|
|
423
|
+
content: researchResult.cacheableAssistantContent || rawText,
|
|
424
|
+
});
|
|
425
|
+
if (rawText.includes('<execute_tool>')) {
|
|
426
|
+
contextManager.addRecent({
|
|
427
|
+
role: 'user',
|
|
428
|
+
content: `/no_think
|
|
429
|
+
CRITICAL ERROR: <execute_tool> is not a supported tool-call format.
|
|
430
|
+
|
|
431
|
+
Use the configured JSON tool calling format.
|
|
432
|
+
|
|
433
|
+
Example:
|
|
434
|
+
{"name":"read_file","arguments":{"filePath":"src/index.ts"}}`,
|
|
435
|
+
});
|
|
436
|
+
}
|
|
437
|
+
else {
|
|
438
|
+
contextManager.addRecent({
|
|
439
|
+
role: 'user',
|
|
440
|
+
content: `/no_think
|
|
441
|
+
Continue researching with a tool call.
|
|
442
|
+
|
|
443
|
+
If the architectural research goals have all been investigated, call
|
|
444
|
+
finish_research with the required coverage object.
|
|
445
|
+
|
|
446
|
+
Do not respond with a prose summary yet.`,
|
|
447
|
+
});
|
|
448
|
+
}
|
|
449
|
+
continue;
|
|
450
|
+
}
|
|
451
|
+
contextManager.addRecent({
|
|
452
|
+
role: 'assistant',
|
|
453
|
+
content: researchResult.cacheableAssistantContent || rawText,
|
|
454
|
+
});
|
|
455
|
+
const executableCalls = [];
|
|
456
|
+
for (const toolCall of parsedToolCalls) {
|
|
457
|
+
if (toolCallsCount >= MAX_TOOL_CALLS) {
|
|
458
|
+
break;
|
|
459
|
+
}
|
|
460
|
+
toolCallsCount++;
|
|
461
|
+
const args = normalizeToolArguments(toolCall.arguments);
|
|
462
|
+
if (toolCall.name === 'finish_research' || toolCall.name === 'finish') {
|
|
463
|
+
const finishResult = finishResearchSchema.safeParse(args);
|
|
464
|
+
if (!finishResult.success) {
|
|
465
|
+
contextManager.addRecent({
|
|
466
|
+
role: 'tool',
|
|
467
|
+
content: JSON.stringify({
|
|
468
|
+
error: 'finish_research requires the complete coverage object and remainingUnknowns array.',
|
|
469
|
+
details: finishResult.error.flatten(),
|
|
470
|
+
}),
|
|
471
|
+
});
|
|
472
|
+
continue;
|
|
473
|
+
}
|
|
474
|
+
const missingCoverage = getMissingCoverage(finishResult.data.coverage);
|
|
475
|
+
if (missingCoverage.length > 0) {
|
|
476
|
+
contextManager.addRecent({
|
|
477
|
+
role: 'tool',
|
|
478
|
+
content: JSON.stringify({
|
|
479
|
+
action: 'continue_research',
|
|
480
|
+
error: 'Research coverage is incomplete.',
|
|
481
|
+
missingCoverage,
|
|
482
|
+
instruction: 'Investigate these areas before finishing. If an area is genuinely unknown after investigation, record the uncertainty in remainingUnknowns and mark the area covered.',
|
|
483
|
+
}),
|
|
484
|
+
});
|
|
485
|
+
continue;
|
|
486
|
+
}
|
|
487
|
+
if (finishResult.data.remainingUnknowns.length > 0) {
|
|
488
|
+
contextManager.addRecent({
|
|
489
|
+
role: 'tool',
|
|
490
|
+
content: JSON.stringify({
|
|
491
|
+
action: 'finish',
|
|
492
|
+
remainingUnknowns: finishResult.data.remainingUnknowns,
|
|
493
|
+
}),
|
|
494
|
+
});
|
|
495
|
+
}
|
|
496
|
+
isDone = true;
|
|
497
|
+
break;
|
|
498
|
+
}
|
|
499
|
+
if (toolCall.name === 'read_file') {
|
|
500
|
+
const filePath = args.filePath || args.file_path;
|
|
501
|
+
if (filePath && contextManager.hasInspected(filePath) && !args.force) {
|
|
502
|
+
contextManager.addRecent({
|
|
503
|
+
role: 'tool',
|
|
504
|
+
content: JSON.stringify({
|
|
505
|
+
error: 'This file has already been inspected. Re-reading it wastes research context.',
|
|
506
|
+
alreadyInspected: true,
|
|
507
|
+
filePath,
|
|
508
|
+
instruction: 'Use search or inspect another related module. Pass force=true only if re-reading this file is necessary.',
|
|
509
|
+
}),
|
|
510
|
+
});
|
|
511
|
+
continue;
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
executableCalls.push({
|
|
515
|
+
name: toolCall.name,
|
|
516
|
+
args,
|
|
517
|
+
});
|
|
518
|
+
}
|
|
519
|
+
if (isDone) {
|
|
520
|
+
break;
|
|
521
|
+
}
|
|
522
|
+
if (executableCalls.length === 0) {
|
|
523
|
+
continue;
|
|
524
|
+
}
|
|
525
|
+
// Research tools are read-only, so independent calls may execute
|
|
526
|
+
// concurrently.
|
|
527
|
+
const toolExecutionResults = await Promise.all(executableCalls.map(async ({ name, args }) => {
|
|
528
|
+
const tool = researchTools.find((candidate) => candidate.name === name);
|
|
529
|
+
if (!tool) {
|
|
530
|
+
return {
|
|
531
|
+
name,
|
|
532
|
+
args,
|
|
533
|
+
toolResult: {
|
|
534
|
+
error: `Unknown tool: ${name}`,
|
|
535
|
+
},
|
|
536
|
+
};
|
|
537
|
+
}
|
|
538
|
+
let toolMessage = `🔧 Using tool: ${name}`;
|
|
539
|
+
if (name === 'read_file' && (args.file_path || args.filePath)) {
|
|
540
|
+
toolMessage = `📂 Reading file: ${args.file_path || args.filePath}`;
|
|
541
|
+
}
|
|
542
|
+
else if (name === 'search_in_files' && args.keyword) {
|
|
543
|
+
toolMessage = `🔎 Searching codebase for: "${args.keyword}"`;
|
|
544
|
+
}
|
|
545
|
+
console.log(`\n[AI Tool Execution] ${toolMessage}`);
|
|
546
|
+
checkCancelled();
|
|
547
|
+
try {
|
|
548
|
+
let toolResult;
|
|
549
|
+
if ('invoke' in tool && typeof tool.invoke === 'function') {
|
|
550
|
+
toolResult = await tool.invoke(args);
|
|
551
|
+
}
|
|
552
|
+
else if ('handler' in tool && typeof tool.handler === 'function') {
|
|
553
|
+
toolResult = await tool.handler(args);
|
|
554
|
+
}
|
|
555
|
+
else {
|
|
556
|
+
toolResult = {
|
|
557
|
+
error: 'Tool has no executable handler.',
|
|
558
|
+
};
|
|
559
|
+
}
|
|
560
|
+
return {
|
|
561
|
+
name,
|
|
562
|
+
args,
|
|
563
|
+
toolResult,
|
|
564
|
+
};
|
|
565
|
+
}
|
|
566
|
+
catch (error) {
|
|
567
|
+
console.error(`\n[AI Tool Error] ${error.message}`);
|
|
568
|
+
return {
|
|
569
|
+
name,
|
|
570
|
+
args,
|
|
571
|
+
toolResult: {
|
|
572
|
+
error: error.message,
|
|
573
|
+
},
|
|
574
|
+
};
|
|
575
|
+
}
|
|
576
|
+
}));
|
|
577
|
+
for (const { name, args, toolResult } of toolExecutionResults) {
|
|
578
|
+
console.log(`[AI Tool Result] Returned payload of ${Buffer.byteLength(JSON.stringify(toolResult) || '', 'utf8')} bytes\n`);
|
|
579
|
+
const estimatedToolTokens = contextManager.estimateTokens(toolResult);
|
|
580
|
+
sourceTokensUsed += estimatedToolTokens;
|
|
581
|
+
if (contextManager.needsCompaction(estimatedToolTokens, {
|
|
582
|
+
iterationsRemaining: MAX_ITERATIONS - iteration,
|
|
583
|
+
toolCallsRemaining: MAX_TOOL_CALLS - toolCallsCount,
|
|
584
|
+
sourceTokensRemaining: MAX_SOURCE_TOKENS - sourceTokensUsed,
|
|
585
|
+
})) {
|
|
586
|
+
await contextManager.compactWithLLM(progress);
|
|
587
|
+
}
|
|
588
|
+
contextManager.addRecent({
|
|
589
|
+
role: 'tool',
|
|
590
|
+
content: typeof toolResult === 'string' ? toolResult : JSON.stringify(toolResult),
|
|
591
|
+
});
|
|
592
|
+
// Research records what has already been inspected,
|
|
593
|
+
// but it does NOT modify the vector database.
|
|
594
|
+
if (name === 'read_file' && toolResult && !toolResult.error) {
|
|
595
|
+
const filePath = args.filePath || args.file_path;
|
|
596
|
+
if (filePath) {
|
|
597
|
+
contextManager.markFileInspected(filePath, 'Architecture research', []);
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
// ─────────────────────────────────────────────
|
|
603
|
+
// PHASE 2: SYNTHESIS
|
|
604
|
+
// ─────────────────────────────────────────────
|
|
605
|
+
checkCancelled();
|
|
606
|
+
contextManager.addRecent({
|
|
607
|
+
role: 'user',
|
|
608
|
+
content: `/no_think
|
|
609
|
+
The repository research phase is complete.
|
|
610
|
+
|
|
611
|
+
Using only information established during research and the Research Memory,
|
|
612
|
+
produce the final architectural manifest.
|
|
613
|
+
|
|
614
|
+
RULES:
|
|
615
|
+
|
|
616
|
+
- Use direct repository evidence.
|
|
617
|
+
- Do not invent technologies, secrets, routes, events, entities, or flows.
|
|
618
|
+
- Keep uncertain conclusions in known_unknowns.
|
|
619
|
+
- Preserve evidence for important architectural claims.
|
|
620
|
+
- data_flows should describe behavior across components rather than individual functions.
|
|
621
|
+
- architectural_invariants must be supported by repository behavior or configuration.
|
|
622
|
+
- Keep the legacy fields application_type, architecture_pattern,
|
|
623
|
+
explanation, tech_stack, core_modules, folder_structure,
|
|
624
|
+
important_details, key_conventions, and required_secrets populated
|
|
625
|
+
because they are consumed by the existing project-summary renderer.
|
|
626
|
+
|
|
627
|
+
Output strictly valid JSON matching this schema:
|
|
628
|
+
|
|
629
|
+
\`\`\`json
|
|
630
|
+
${jsonSchema}
|
|
631
|
+
\`\`\`
|
|
632
|
+
|
|
633
|
+
Provide ONLY the JSON object.`,
|
|
634
|
+
});
|
|
635
|
+
const synthesisRun = completion({
|
|
636
|
+
modelId: gemma4ModelId,
|
|
637
|
+
history: contextManager.buildHistory(),
|
|
638
|
+
stream: true,
|
|
639
|
+
maxTokens: 16_384,
|
|
640
|
+
kvCache: project.id.toString(),
|
|
641
|
+
});
|
|
642
|
+
const synthesisResult = await awaitCompletion(synthesisRun);
|
|
643
|
+
const manifestResultContent = synthesisResult.contentText || '';
|
|
644
|
+
try {
|
|
645
|
+
const extractedFacts = extractJson(manifestResultContent);
|
|
646
|
+
return {
|
|
647
|
+
...extractedFacts,
|
|
648
|
+
project_name: extractedFacts.project_name || project.name,
|
|
649
|
+
application_type: extractedFacts.application_type || 'Unknown',
|
|
650
|
+
architecture_pattern: extractedFacts.architecture_pattern || 'Unknown',
|
|
651
|
+
explanation: extractedFacts.explanation || '',
|
|
652
|
+
tech_stack: extractedFacts.tech_stack || [],
|
|
653
|
+
core_modules: extractedFacts.core_modules || [],
|
|
654
|
+
folder_structure: extractedFacts.folder_structure || '',
|
|
655
|
+
important_details: extractedFacts.important_details || '',
|
|
656
|
+
key_conventions: extractedFacts.key_conventions || [],
|
|
657
|
+
required_secrets: extractedFacts.required_secrets || [],
|
|
658
|
+
entry_points: extractedFacts.entry_points || [],
|
|
659
|
+
runtime_components: extractedFacts.runtime_components || [],
|
|
660
|
+
data_flows: extractedFacts.data_flows || [],
|
|
661
|
+
communication: extractedFacts.communication || [],
|
|
662
|
+
persistence: extractedFacts.persistence || [],
|
|
663
|
+
domain_concepts: extractedFacts.domain_concepts || [],
|
|
664
|
+
architectural_invariants: extractedFacts.architectural_invariants || [],
|
|
665
|
+
known_unknowns: extractedFacts.known_unknowns || [],
|
|
666
|
+
};
|
|
667
|
+
}
|
|
668
|
+
catch {
|
|
669
|
+
return {
|
|
670
|
+
project_name: project.name,
|
|
671
|
+
application_type: 'Unknown',
|
|
672
|
+
architecture_pattern: 'Unknown (LLM Parse Error)',
|
|
673
|
+
explanation: 'Failed to parse the architecture research output.',
|
|
674
|
+
tech_stack: [],
|
|
675
|
+
core_modules: [],
|
|
676
|
+
folder_structure: '',
|
|
677
|
+
important_details: '',
|
|
678
|
+
key_conventions: [],
|
|
679
|
+
required_secrets: [],
|
|
680
|
+
entry_points: [],
|
|
681
|
+
runtime_components: [],
|
|
682
|
+
data_flows: [],
|
|
683
|
+
communication: [],
|
|
684
|
+
persistence: [],
|
|
685
|
+
domain_concepts: [],
|
|
686
|
+
architectural_invariants: [],
|
|
687
|
+
known_unknowns: ['The final research manifest could not be parsed.'],
|
|
688
|
+
};
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
//# sourceMappingURL=research-loop.js.map
|