@payconductor/houndsman 0.11.0 → 0.12.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 (118) hide show
  1. package/README.md +4 -19
  2. package/package.json +1 -1
  3. package/src/cli/help.ts +0 -7
  4. package/src/cli/index.ts +0 -2
  5. package/src/core/ai/ai-client.ts +3 -7
  6. package/src/core/ai/ai-sdk-model-provider.ts +4 -24
  7. package/src/core/ai/features/brief-enricher.ts +0 -1
  8. package/src/core/ai/features/classifier.ts +0 -1
  9. package/src/core/ai/features/context-summarizer.ts +0 -1
  10. package/src/core/ai/features/pre-analyzer.ts +0 -1
  11. package/src/core/ai/features/technical-summarizer.ts +0 -1
  12. package/src/core/ai/features/verifier.ts +0 -1
  13. package/src/core/ai/index.ts +0 -3
  14. package/src/core/ai/prompts/pre-analysis.ts +3 -2
  15. package/src/core/ai/schemas.ts +1 -23
  16. package/src/core/ai/types.ts +2 -15
  17. package/src/core/ai/usage-budget.ts +1 -1
  18. package/src/core/ast/diff.ts +37 -2
  19. package/src/core/ast/service.ts +1 -1
  20. package/src/core/brief.ts +54 -5
  21. package/src/core/changed-scope.ts +207 -0
  22. package/src/core/commands/scaffold.ts +1 -18
  23. package/src/core/config.ts +2 -15
  24. package/src/core/defaults.ts +0 -6
  25. package/src/core/engine/review-scope.ts +55 -8
  26. package/src/core/jev/jev-detector.ts +7 -2
  27. package/src/core/logging/index.ts +1 -1
  28. package/src/core/logging/logger.ts +3 -3
  29. package/src/core/orchestrator.ts +19 -14
  30. package/src/core/project-config.ts +0 -28
  31. package/src/core/registry-check.ts +2 -12
  32. package/src/core/rule-text.ts +3 -55
  33. package/src/core/services/git-repository.ts +8 -0
  34. package/src/core/storage/types.ts +0 -1
  35. package/src/core/types.ts +3 -14
  36. package/src/index.ts +25 -3
  37. package/src/server/client/utils/constants.ts +3 -6
  38. package/src/server/client/utils/types.ts +0 -1
  39. package/src/skill/OUTPUT-FORMAT.md +5 -0
  40. package/src/skill/README.md +2 -1
  41. package/src/skill/SETUP.md +1 -1
  42. package/src/skill/fix.md +1 -1
  43. package/src/skill/hunt.md +2 -6
  44. package/src/tests/__snapshots__/baseline.test.ts.snap +50 -32
  45. package/src/tests/changed-scope.test.ts +197 -0
  46. package/src/tests/fallback.test.ts +1 -96
  47. package/src/tests/fixtures/fake-model-provider.ts +0 -1
  48. package/src/tests/review-scope.test.ts +33 -0
  49. package/src/tests/worktree.test.ts +1 -3
  50. package/src/cli/commands/hunt-command.ts +0 -51
  51. package/src/harness/README.md +0 -15
  52. package/src/harness/agent/agent-context.ts +0 -33
  53. package/src/harness/agent/agent-errors.ts +0 -30
  54. package/src/harness/agent/agent-factory.ts +0 -46
  55. package/src/harness/agent/agent-reporter.ts +0 -34
  56. package/src/harness/agent/agent-result.ts +0 -16
  57. package/src/harness/agent/agent-runner.ts +0 -32
  58. package/src/harness/agent/agent-scheduler.ts +0 -37
  59. package/src/harness/agent/review-agent.ts +0 -401
  60. package/src/harness/config/harness-config.ts +0 -89
  61. package/src/harness/hunt/hunt-harness.ts +0 -83
  62. package/src/harness/hunt/hunt-options.ts +0 -92
  63. package/src/harness/hunt/hunt-result.ts +0 -17
  64. package/src/harness/hunt/hunt-session.ts +0 -359
  65. package/src/harness/hunt/review-planner.ts +0 -98
  66. package/src/harness/index.ts +0 -36
  67. package/src/harness/investigation/agent-state.ts +0 -134
  68. package/src/harness/investigation/evidence-verifier.ts +0 -129
  69. package/src/harness/investigation/evidence.ts +0 -110
  70. package/src/harness/investigation/finding-critic.ts +0 -96
  71. package/src/harness/investigation/investigation-planner.ts +0 -118
  72. package/src/harness/investigation/investigation-task.ts +0 -10
  73. package/src/harness/investigation/review-journal.ts +0 -87
  74. package/src/harness/investigation/rule-analyzer.ts +0 -178
  75. package/src/harness/investigation/rule-signals.ts +0 -110
  76. package/src/harness/prompts/agent-schemas.ts +0 -57
  77. package/src/harness/prompts/reviewer-prompt-builder.ts +0 -101
  78. package/src/harness/prompts/states/conclude-prompt.ts +0 -16
  79. package/src/harness/prompts/states/investigate-prompt.ts +0 -37
  80. package/src/harness/prompts/states/orient-prompt.ts +0 -15
  81. package/src/harness/prompts/states/verify-prompt.ts +0 -22
  82. package/src/harness/tools/agent-tool.ts +0 -102
  83. package/src/harness/tools/builtin/changed-symbols-tool.ts +0 -48
  84. package/src/harness/tools/builtin/doc-context-tool.ts +0 -74
  85. package/src/harness/tools/builtin/file-outline-tool.ts +0 -66
  86. package/src/harness/tools/builtin/find-references-tool.ts +0 -66
  87. package/src/harness/tools/builtin/find-symbol-tool.ts +0 -91
  88. package/src/harness/tools/builtin/helpers.ts +0 -115
  89. package/src/harness/tools/builtin/impact-tool.ts +0 -48
  90. package/src/harness/tools/builtin/imports-exports-tool.ts +0 -53
  91. package/src/harness/tools/builtin/index.ts +0 -59
  92. package/src/harness/tools/builtin/list-files-tool.ts +0 -85
  93. package/src/harness/tools/builtin/pr-context-tool.ts +0 -41
  94. package/src/harness/tools/builtin/read-file-range-tool.ts +0 -73
  95. package/src/harness/tools/builtin/rule-signals-tool.ts +0 -36
  96. package/src/harness/tools/builtin/search-code-tool.ts +0 -65
  97. package/src/harness/tools/builtin/symbol-context-tool.ts +0 -106
  98. package/src/harness/tools/builtin/tests-for-symbol-tool.ts +0 -61
  99. package/src/harness/tools/path-guard.ts +0 -90
  100. package/src/harness/tools/result-limiter.ts +0 -49
  101. package/src/harness/tools/secret-redactor.ts +0 -54
  102. package/src/harness/tools/tool-cache.ts +0 -50
  103. package/src/harness/tools/tool-call-budget.ts +0 -60
  104. package/src/harness/tools/tool-context.ts +0 -5
  105. package/src/harness/tools/tool-middleware.ts +0 -105
  106. package/src/harness/tools/tool-policy.ts +0 -51
  107. package/src/harness/tools/tool-registry.ts +0 -112
  108. package/src/harness/tools/tool-result.ts +0 -15
  109. package/src/harness/tools/toolbox.ts +0 -36
  110. package/src/harness/validation/finding-validator.ts +0 -119
  111. package/src/tests/fixtures/fake-agent-tools.ts +0 -69
  112. package/src/tests/fixtures/fake-investigation.ts +0 -64
  113. package/src/tests/harness-agent.test.ts +0 -141
  114. package/src/tests/harness-investigation.test.ts +0 -206
  115. package/src/tests/harness-tools.test.ts +0 -148
  116. package/src/tests/hunt-command.test.ts +0 -212
  117. package/src/tests/rule-analyzer.test.ts +0 -61
  118. package/src/tests/tool-contracts.test.ts +0 -82
@@ -1,105 +0,0 @@
1
- /**
2
- * Middleware de execução das tools.
3
- *
4
- * Cadeia: validação Zod → cache → budget → execução → limites → redaction →
5
- * auditoria. Toda tool passa por aqui; nenhuma executa direto.
6
- */
7
-
8
- import type { ToolResult } from "./agent-tool";
9
- import { limitText, TOOL_LIMITS } from "./result-limiter";
10
- import { redactSecrets, redactValue } from "./secret-redactor";
11
- import { ToolCache } from "./tool-cache";
12
- import { ToolBudgetError } from "./tool-call-budget";
13
- import type { ToolContext } from "./tool-context";
14
- import type { AgentStateName } from "./tool-policy";
15
- import type { RegisteredTool } from "./tool-registry";
16
-
17
- /** Executor com validação, cache, budget, limite e auditoria. */
18
- export class ToolMiddleware {
19
- async run(
20
- tool: RegisteredTool,
21
- input: unknown,
22
- context: ToolContext,
23
- state: AgentStateName,
24
- ): Promise<ToolResult<unknown>> {
25
- const parsed = tool.inputSchema.safeParse(input);
26
- if (!parsed.success) {
27
- return {
28
- ok: false,
29
- error: `input inválido para '${tool.metadata.name}': ${parsed.error.message}`,
30
- };
31
- }
32
-
33
- const maxPerAgent = tool.metadata.maxCallsPerAgent;
34
- if (
35
- maxPerAgent !== undefined &&
36
- context.state.callsOf(tool.metadata.name) >= maxPerAgent
37
- ) {
38
- return {
39
- ok: false,
40
- error: `Limite de ${maxPerAgent} chamada(s) da tool '${tool.metadata.name}'.`,
41
- };
42
- }
43
-
44
- const key = ToolCache.key(tool.metadata.name, parsed.data);
45
- const cached = context.cache.get<ToolResult<unknown>>(key);
46
- if (cached) return cached;
47
-
48
- try {
49
- context.budget.consume(tool.metadata.name);
50
- } catch (error) {
51
- return {
52
- ok: false,
53
- error: error instanceof ToolBudgetError ? error.message : String(error),
54
- };
55
- }
56
-
57
- const started = performance.now();
58
- try {
59
- // Contravariância: a tool concreta aceita seu próprio input.
60
- const result = (await tool.execute(
61
- parsed.data as never,
62
- context,
63
- )) as ToolResult<unknown>;
64
- const limited = limitResult(result);
65
- const redacted = redactValue(limited);
66
- context.cache.set(key, redacted, tool.metadata.cacheTtlMs ?? 60_000);
67
- context.state.recordToolCall(tool.metadata.name);
68
- const stored = (redacted.evidence ?? []).map(
69
- (evidence) =>
70
- context.state.getEvidence(evidence.id) ??
71
- context.state.addEvidence(evidence),
72
- );
73
- if (stored.length > 0) redacted.evidence = stored;
74
- context.logger.journal.recordToolCall(
75
- tool.metadata.name,
76
- state,
77
- performance.now() - started,
78
- redacted,
79
- );
80
- return redacted;
81
- } catch (error) {
82
- const message = redactSecrets(
83
- error instanceof Error ? error.message : String(error),
84
- );
85
- context.logger.journal.recordToolCall(
86
- tool.metadata.name,
87
- state,
88
- performance.now() - started,
89
- { ok: false, error: message },
90
- );
91
- return { ok: false, error: message };
92
- }
93
- }
94
- }
95
-
96
- /** Aplica tetos de texto ao resultado da tool. */
97
- function limitResult(result: ToolResult<unknown>): ToolResult<unknown> {
98
- const copy: ToolResult<unknown> = { ...result };
99
- if (typeof copy.data === "string") {
100
- const limited = limitText(copy.data, TOOL_LIMITS.maxBytes);
101
- copy.data = limited.text;
102
- if (limited.truncated) copy.truncated = true;
103
- }
104
- return copy;
105
- }
@@ -1,51 +0,0 @@
1
- /**
2
- * Política de tools por estado do agente.
3
- *
4
- * O modelo só vê as tools permitidas no estado atual. Adicionar uma tool não
5
- * exige alterar o agente — basta declarar capabilities compatíveis.
6
- */
7
-
8
- import type { ToolCapability, ToolMetadata } from "./agent-tool";
9
-
10
- /** Estados da máquina de investigação. */
11
- export type AgentStateName = "ORIENT" | "PLAN" | "INVESTIGATE" | "VERIFY" | "CONCLUDE";
12
-
13
- /** Capabilities liberadas por estado. */
14
- const DEFAULT_POLICY: Record<AgentStateName, readonly ToolCapability[]> = {
15
- ORIENT: ["read", "outline", "ast", "impact", "rules", "symbol"],
16
- PLAN: ["rules", "symbol", "impact", "ast"],
17
- INVESTIGATE: [
18
- "read",
19
- "search",
20
- "outline",
21
- "symbol",
22
- "references",
23
- "ast",
24
- "impact",
25
- "rules",
26
- "tests",
27
- "docs",
28
- ],
29
- VERIFY: ["symbol", "references", "search", "read"],
30
- CONCLUDE: [],
31
- };
32
-
33
- /** Política substituível. */
34
- export class ToolPolicy {
35
- private readonly policy: Record<AgentStateName, readonly ToolCapability[]>;
36
-
37
- constructor(policy: Partial<Record<AgentStateName, readonly ToolCapability[]>> = {}) {
38
- this.policy = { ...DEFAULT_POLICY, ...policy };
39
- }
40
-
41
- /** Capabilities liberadas no estado. */
42
- capabilities(state: AgentStateName): readonly ToolCapability[] {
43
- return this.policy[state];
44
- }
45
-
46
- /** `true` se a tool pode ser usada no estado. */
47
- allows(state: AgentStateName, metadata: ToolMetadata): boolean {
48
- const allowed = this.policy[state];
49
- return metadata.capabilities.some((capability) => allowed.includes(capability));
50
- }
51
- }
@@ -1,112 +0,0 @@
1
- /**
2
- * Registro das tools do harness.
3
- *
4
- * Módulos registram tools; nomes duplicados falham no startup. O registry gera
5
- * as descrições do prompt a partir do metadata (nunca hardcoded).
6
- */
7
-
8
- import type { AiTool } from "../../core/ai";
9
- import type {
10
- RegisterableTool,
11
- ToolContext,
12
- ToolMetadata,
13
- ToolModule,
14
- ToolRegistryLike,
15
- ToolResult,
16
- } from "./agent-tool";
17
- import type { ToolMiddleware } from "./tool-middleware";
18
- import type { AgentStateName, ToolPolicy } from "./tool-policy";
19
-
20
- /** Tool registrada, com a forma type-erased aceita pelo middleware. */
21
- export type RegisteredTool = RegisterableTool;
22
-
23
- /** Registry de tools. */
24
- export class ToolRegistry implements ToolRegistryLike {
25
- private readonly tools = new Map<string, RegisteredTool>();
26
-
27
- /** Registra uma tool; nome duplicado é erro de configuração. */
28
- registerTool(tool: RegisterableTool): void {
29
- if (this.tools.has(tool.metadata.name)) {
30
- throw new Error(`Tool duplicada: '${tool.metadata.name}'.`);
31
- }
32
- this.tools.set(tool.metadata.name, tool);
33
- }
34
-
35
- /** Registra um módulo (agrupa tools). */
36
- registerModule(module: ToolModule): void {
37
- module.register(this);
38
- }
39
-
40
- /** Tool por nome. */
41
- get(name: string): RegisteredTool | undefined {
42
- return this.tools.get(name);
43
- }
44
-
45
- /** Todas as tools (ordem de registro). */
46
- list(): RegisteredTool[] {
47
- return [...this.tools.values()];
48
- }
49
-
50
- /** Tools com uma capability. */
51
- listByCapability(capability: string): RegisteredTool[] {
52
- return this.list().filter((tool) =>
53
- (tool.metadata.capabilities as readonly string[]).includes(capability),
54
- );
55
- }
56
-
57
- /** Descrição compacta das tools permitidas (para o prompt). */
58
- describeForPrompt(policy: ToolPolicy, state: AgentStateName): string {
59
- return this.list()
60
- .filter((tool) => policy.allows(state, tool.metadata))
61
- .map(
62
- (tool) =>
63
- `- ${tool.metadata.name} (${tool.metadata.capabilities.join(", ")}): ${tool.metadata.description}`,
64
- )
65
- .join("\n");
66
- }
67
-
68
- /** Converte as tools permitidas no formato do modelo. */
69
- toModelTools(
70
- policy: ToolPolicy,
71
- state: AgentStateName,
72
- context: ToolContext,
73
- middleware: ToolMiddleware,
74
- ): Record<string, AiTool> {
75
- const entries = this.list()
76
- .filter((tool) => policy.allows(state, tool.metadata))
77
- .map((tool) => [
78
- tool.metadata.name,
79
- {
80
- name: tool.metadata.name,
81
- description: tool.metadata.description,
82
- inputSchema: tool.inputSchema,
83
- execute: async (input: unknown): Promise<string> => {
84
- const result = await middleware.run(tool, input, context, state);
85
- return JSON.stringify(result);
86
- },
87
- },
88
- ]);
89
- return Object.fromEntries(entries);
90
- }
91
-
92
- /** Metadados registrados. */
93
- metadata(): ToolMetadata[] {
94
- return this.list().map((tool) => tool.metadata);
95
- }
96
-
97
- /** Executa uma tool com middleware (usado nos testes). */
98
- execute(
99
- name: string,
100
- input: unknown,
101
- context: ToolContext,
102
- middleware: ToolMiddleware,
103
- state: AgentStateName = "INVESTIGATE",
104
- ): Promise<ToolResult<unknown>> {
105
- const tool = this.tools.get(name);
106
- if (!tool)
107
- return Promise.resolve({ ok: false, error: `tool desconhecida: ${name}` });
108
- return middleware.run(tool, input, context, state) as Promise<
109
- ToolResult<unknown>
110
- >;
111
- }
112
- }
@@ -1,15 +0,0 @@
1
- /**
2
- * Resultado padrão de uma tool (reexport de contrato).
3
- */
4
-
5
- export type { ToolResult } from "./agent-tool";
6
-
7
- /** Resultado de sucesso. */
8
- export function ok<T>(data: T, extra: Record<string, unknown> = {}) {
9
- return { ok: true as const, data, ...extra };
10
- }
11
-
12
- /** Resultado de erro estruturado. */
13
- export function fail(error: string) {
14
- return { ok: false as const, error };
15
- }
@@ -1,36 +0,0 @@
1
- /**
2
- * Visão de tools por agente (registry + policy + middleware).
3
- *
4
- * O agente pede `toModelTools(state)` e recebe apenas o que pode usar naquele
5
- * estado, já com cache/budget/redaction aplicados.
6
- */
7
-
8
- import type { AiTool } from "../../core/ai";
9
- import type { ToolContext } from "./tool-context";
10
- import type { ToolMiddleware } from "./tool-middleware";
11
- import type { AgentStateName, ToolPolicy } from "./tool-policy";
12
- import type { ToolRegistry } from "./tool-registry";
13
-
14
- /** Fachada por agente. */
15
- export class Toolbox {
16
- constructor(
17
- readonly registry: ToolRegistry,
18
- readonly policy: ToolPolicy,
19
- readonly middleware: ToolMiddleware,
20
- ) {}
21
-
22
- /** Tools permitidas no estado, no formato do modelo. */
23
- toModelTools(state: AgentStateName, context: ToolContext): Record<string, AiTool> {
24
- return this.registry.toModelTools(this.policy, state, context, this.middleware);
25
- }
26
-
27
- /** Descrição das tools permitidas (prompt). */
28
- describe(state: AgentStateName): string {
29
- return this.registry.describeForPrompt(this.policy, state);
30
- }
31
-
32
- /** Executa uma tool fora do modelo (testes/uso determinístico). */
33
- execute(name: string, input: unknown, context: ToolContext, state: AgentStateName) {
34
- return this.registry.execute(name, input, context, this.middleware, state);
35
- }
36
- }
@@ -1,119 +0,0 @@
1
- /**
2
- * Validação final do relatório de um watchdog.
3
- *
4
- * Descarta findings que citam regra inexistente, arquivo fora do domínio/impacto,
5
- * overflow (não lido) ou linha inválida. `reviewedFiles` só aceita arquivos
6
- * existentes; overflow entra em `skipped`.
7
- */
8
-
9
- import { readFileSync } from "node:fs";
10
- import type { WatchdogReport } from "../../core/report-builder";
11
- import { emergentText, isEmergentRule } from "../../core/rule-text";
12
- import { SEVERITY_ORDER, type Severity } from "../../core/types";
13
- import { capSeverity } from "../investigation/finding-critic";
14
-
15
- /** Contexto de validação. */
16
- export type ValidationContext = {
17
- watchdogId: string;
18
- knownRules: ReadonlySet<string>;
19
- allowedFiles: ReadonlySet<string>;
20
- overflow: ReadonlySet<string>;
21
- readFile: (path: string) => string | null;
22
- /** Aceita findings `Lógica:`/`Domínio:` fora das regras. */
23
- allowEmergentFindings?: boolean;
24
- };
25
-
26
- /** Resultado da validação. */
27
- export type ValidationResult = {
28
- report: WatchdogReport;
29
- dropped: { finding: string; reason: string }[];
30
- };
31
-
32
- const SEVERITIES = new Set<string>(SEVERITY_ORDER);
33
-
34
- /** Validador de relatórios. */
35
- export class FindingValidator {
36
- private readonly context: ValidationContext;
37
-
38
- constructor(context: ValidationContext) {
39
- this.context = context;
40
- }
41
-
42
- /** Valida/normaliza o relatório. */
43
- validate(report: WatchdogReport): ValidationResult {
44
- const dropped: { finding: string; reason: string }[] = [];
45
- const findings = report.findings
46
- .filter((finding) => {
47
- const reason = this.reasonToDrop(finding);
48
- if (!reason) return true;
49
- dropped.push({
50
- finding: `${finding.file}:${finding.line ?? "?"}`,
51
- reason,
52
- });
53
- return false;
54
- })
55
- .map((finding) =>
56
- isEmergentRule(finding.rule)
57
- ? {
58
- ...finding,
59
- severity: capSeverity(finding.severity, "attention"),
60
- }
61
- : finding,
62
- );
63
-
64
- const reviewedFiles = report.reviewedFiles
65
- .filter((file) => this.context.allowedFiles.has(file))
66
- .filter((file) => this.context.readFile(file) !== null);
67
-
68
- const skipped = new Set(report.skipped);
69
- for (const file of this.context.overflow) skipped.add(`${file} (budget)`);
70
-
71
- return {
72
- report: {
73
- watchdogId: this.context.watchdogId,
74
- reviewedFiles: [...new Set(reviewedFiles)],
75
- skipped: [...skipped],
76
- findings,
77
- },
78
- dropped,
79
- };
80
- }
81
-
82
- /** Motivo para descartar (ou `null` se válido). */
83
- private reasonToDrop(finding: {
84
- rule: string;
85
- severity: Severity;
86
- file: string;
87
- line?: number;
88
- }): string | null {
89
- if (isEmergentRule(finding.rule)) {
90
- if (!this.context.allowEmergentFindings)
91
- return "finding emergente desabilitado";
92
- if (!emergentText(finding.rule)) return "finding emergente sem descrição";
93
- } else if (!this.context.knownRules.has(finding.rule.trim())) {
94
- return "regra inexistente";
95
- }
96
- if (!SEVERITIES.has(finding.severity)) return "severidade inválida";
97
- if (this.context.overflow.has(finding.file)) return "arquivo fora do orçamento";
98
- if (!this.context.allowedFiles.has(finding.file))
99
- return "arquivo fora do domínio";
100
- const content = this.context.readFile(finding.file);
101
- if (content === null) return "arquivo ausente";
102
- if (finding.line !== undefined) {
103
- const total = content.split("\n").length;
104
- if (finding.line < 1 || finding.line > total) return "linha fora do arquivo";
105
- }
106
- return null;
107
- }
108
- }
109
-
110
- /** Leitor de arquivos padrão (UTF-8; `null` se ausente). */
111
- export function defaultFileReader(root: string): (path: string) => string | null {
112
- return (path: string) => {
113
- try {
114
- return readFileSync(`${root}/${path}`, "utf8");
115
- } catch {
116
- return null;
117
- }
118
- };
119
- }
@@ -1,69 +0,0 @@
1
- /**
2
- * Tools fake para a suíte do harness.
3
- */
4
-
5
- import { z } from "zod";
6
- import type {
7
- AgentTool,
8
- ToolContext,
9
- ToolMetadata,
10
- ToolResult,
11
- } from "../../harness/tools/agent-tool";
12
-
13
- /** Tool de eco (testa registry/middleware). */
14
- export class EchoTool implements AgentTool<{ value: string }, { value: string }> {
15
- readonly metadata: ToolMetadata = {
16
- name: "echo",
17
- description: "Devolve o valor recebido.",
18
- category: "context",
19
- capabilities: ["read"],
20
- readOnly: true,
21
- cost: 1,
22
- cacheTtlMs: 60_000,
23
- };
24
-
25
- readonly inputSchema = z.object({ value: z.string() });
26
-
27
- async execute(
28
- input: { value: string },
29
- _context: ToolContext,
30
- ): Promise<ToolResult<{ value: string }>> {
31
- return { ok: true, data: { value: input.value } };
32
- }
33
- }
34
-
35
- /** Tool que produz evidência para o estado. */
36
- export class EvidenceTool implements AgentTool<{ file: string }, unknown> {
37
- readonly metadata: ToolMetadata = {
38
- name: "evidence",
39
- description: "Produz uma evidência fake.",
40
- category: "evidence",
41
- capabilities: ["read"],
42
- readOnly: true,
43
- cost: 1,
44
- };
45
-
46
- readonly inputSchema = z.object({ file: z.string() });
47
-
48
- async execute(
49
- input: { file: string },
50
- _context: ToolContext,
51
- ): Promise<ToolResult<unknown>> {
52
- return {
53
- ok: true,
54
- data: { file: input.file },
55
- evidence: [
56
- {
57
- id: "",
58
- file: input.file,
59
- startLine: 1,
60
- endLine: 1,
61
- snippet: "const x: any = 1;",
62
- kind: "definition",
63
- sourceTool: "evidence",
64
- confidence: 0.9,
65
- },
66
- ],
67
- };
68
- }
69
- }
@@ -1,64 +0,0 @@
1
- /**
2
- * Fixtures de investigação/agente (sem rede, sem git).
3
- */
4
-
5
- import { AstService } from "../../core/ast/service";
6
- import { Watchdog } from "../../core/domain/watchdog";
7
- import type { AgentContext } from "../../harness/agent/agent-context";
8
- import { SilentReporter } from "../../harness/agent/agent-reporter";
9
- import type { ReviewAgentDeps } from "../../harness/agent/review-agent";
10
- import { RuleAnalyzer } from "../../harness/investigation/rule-analyzer";
11
- import { BuiltinToolsModule } from "../../harness/tools/builtin";
12
- import { ToolRegistry } from "../../harness/tools/tool-registry";
13
- import type { FakeModelProvider } from "./fake-model-provider";
14
- import { fakeHoundsmanConfig } from "./fake-project";
15
-
16
- /** Contexto de agente para o watchdog `alpha`. */
17
- export function fakeAgentContext(overrides: Partial<AgentContext> = {}): AgentContext {
18
- const watchdog = new Watchdog(fakeHoundsmanConfig.watchdogs.alpha);
19
- const rules = new RuleAnalyzer().analyze(watchdog.rules);
20
- return {
21
- watchdog,
22
- projectName: "Fake",
23
- branch: "main",
24
- files: ["src/alpha/a.ts"],
25
- overflow: [],
26
- deleted: [],
27
- docs: [],
28
- context: null,
29
- ast: new Map(),
30
- outputPath: "findings/alpha.json",
31
- maxSteps: 4,
32
- maxCalls: 8,
33
- repoRoot: process.cwd(),
34
- diff: { byFile: new Map(), churn: new Map() },
35
- rules,
36
- ...overrides,
37
- };
38
- }
39
-
40
- /** Registry com tools built-in. */
41
- export function builtinRegistry(): ToolRegistry {
42
- const registry = new ToolRegistry();
43
- registry.registerModule(new BuiltinToolsModule());
44
- return registry;
45
- }
46
-
47
- /** Deps completas de um `ReviewAgent` com provider fake. */
48
- export function fakeAgentDeps(
49
- provider: FakeModelProvider,
50
- overrides: Partial<ReviewAgentDeps> = {},
51
- ): ReviewAgentDeps {
52
- const context = overrides.context ?? fakeAgentContext();
53
- return {
54
- context,
55
- provider,
56
- registry: builtinRegistry(),
57
- reporter: new SilentReporter(),
58
- maxOutputTokens: 512,
59
- temperature: 0,
60
- timeoutMs: 5_000,
61
- ast: new AstService(fakeHoundsmanConfig),
62
- ...overrides,
63
- };
64
- }