@praneeth_54/agentdoctor 2.0.0 → 2.1.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 (168) hide show
  1. package/CHANGELOG.md +141 -4
  2. package/README.md +329 -153
  3. package/dist/agent/approvals.d.ts +24 -0
  4. package/dist/agent/approvals.js +64 -0
  5. package/dist/agent/chat/index.d.ts +7 -0
  6. package/dist/agent/chat/index.js +5 -0
  7. package/dist/agent/chat/memory.d.ts +44 -0
  8. package/dist/agent/chat/memory.js +103 -0
  9. package/dist/agent/chat/project-summary.d.ts +16 -0
  10. package/dist/agent/chat/project-summary.js +70 -0
  11. package/dist/agent/chat/prompts.d.ts +6 -0
  12. package/dist/agent/chat/prompts.js +39 -0
  13. package/dist/agent/chat/response.d.ts +19 -0
  14. package/dist/agent/chat/response.js +109 -0
  15. package/dist/agent/chat/service.d.ts +42 -0
  16. package/dist/agent/chat/service.js +252 -0
  17. package/dist/agent/chat/types.d.ts +48 -0
  18. package/dist/agent/chat/types.js +1 -0
  19. package/dist/agent/context/retrieve.d.ts +21 -0
  20. package/dist/agent/context/retrieve.js +117 -0
  21. package/dist/agent/context/truth.d.ts +6 -0
  22. package/dist/agent/context/truth.js +19 -0
  23. package/dist/agent/context/types.d.ts +23 -0
  24. package/dist/agent/context/types.js +4 -0
  25. package/dist/agent/index.d.ts +26 -0
  26. package/dist/agent/index.js +14 -0
  27. package/dist/agent/loop.d.ts +51 -0
  28. package/dist/agent/loop.js +222 -0
  29. package/dist/agent/modes.d.ts +18 -0
  30. package/dist/agent/modes.js +102 -0
  31. package/dist/agent/plan.d.ts +30 -0
  32. package/dist/agent/plan.js +121 -0
  33. package/dist/agent/runtime.d.ts +67 -0
  34. package/dist/agent/runtime.js +180 -0
  35. package/dist/agent/state.d.ts +30 -0
  36. package/dist/agent/state.js +95 -0
  37. package/dist/agent/student.d.ts +56 -0
  38. package/dist/agent/student.js +230 -0
  39. package/dist/agent/tools/execute.d.ts +18 -0
  40. package/dist/agent/tools/execute.js +355 -0
  41. package/dist/agent/tools/index.d.ts +6 -0
  42. package/dist/agent/tools/index.js +5 -0
  43. package/dist/agent/tools/registry.d.ts +7 -0
  44. package/dist/agent/tools/registry.js +212 -0
  45. package/dist/agent/tools/run.d.ts +24 -0
  46. package/dist/agent/tools/run.js +44 -0
  47. package/dist/agent/tools/types.d.ts +32 -0
  48. package/dist/agent/tools/types.js +10 -0
  49. package/dist/agent/tools/write.d.ts +24 -0
  50. package/dist/agent/tools/write.js +121 -0
  51. package/dist/agent/verify.d.ts +29 -0
  52. package/dist/agent/verify.js +210 -0
  53. package/dist/ai/config.d.ts +22 -0
  54. package/dist/ai/config.js +68 -0
  55. package/dist/ai/index.d.ts +17 -0
  56. package/dist/ai/index.js +52 -0
  57. package/dist/ai/providers/mock.d.ts +20 -0
  58. package/dist/ai/providers/mock.js +84 -0
  59. package/dist/ai/providers/none.d.ts +6 -0
  60. package/dist/ai/providers/none.js +23 -0
  61. package/dist/ai/providers/openai-compatible.d.ts +21 -0
  62. package/dist/ai/providers/openai-compatible.js +151 -0
  63. package/dist/ai/redact.d.ts +8 -0
  64. package/dist/ai/redact.js +21 -0
  65. package/dist/ai/types.d.ts +71 -0
  66. package/dist/ai/types.js +6 -0
  67. package/dist/architecture/contract.d.ts +64 -0
  68. package/dist/architecture/contract.js +373 -0
  69. package/dist/assurance/change.d.ts +211 -0
  70. package/dist/assurance/change.js +624 -0
  71. package/dist/assurance/proof.d.ts +136 -0
  72. package/dist/assurance/proof.js +338 -0
  73. package/dist/auth/index.d.ts +81 -0
  74. package/dist/auth/index.js +130 -0
  75. package/dist/auth/rbac.d.ts +9 -0
  76. package/dist/auth/rbac.js +26 -0
  77. package/dist/cli/commands/agent.d.ts +27 -0
  78. package/dist/cli/commands/agent.js +153 -0
  79. package/dist/cli/commands/architecture.d.ts +6 -0
  80. package/dist/cli/commands/architecture.js +109 -0
  81. package/dist/cli/commands/assurance.d.ts +67 -0
  82. package/dist/cli/commands/assurance.js +210 -0
  83. package/dist/cli/commands/chat.d.ts +14 -0
  84. package/dist/cli/commands/chat.js +155 -0
  85. package/dist/cli/commands/complete.js +2 -2
  86. package/dist/cli/commands/learn.d.ts +12 -0
  87. package/dist/cli/commands/learn.js +107 -0
  88. package/dist/cli/commands/platform.d.ts +2 -0
  89. package/dist/cli/commands/platform.js +5 -1
  90. package/dist/cli/commands/policy-graph-run.d.ts +40 -0
  91. package/dist/cli/commands/policy-graph-run.js +200 -0
  92. package/dist/cli/commands/workspace.d.ts +9 -0
  93. package/dist/cli/commands/workspace.js +82 -0
  94. package/dist/cli/program.js +627 -17
  95. package/dist/constants.d.ts +1 -1
  96. package/dist/constants.js +1 -1
  97. package/dist/contracts/index.d.ts +1 -1
  98. package/dist/contracts/index.js +2 -2
  99. package/dist/core/brain-product/init.js +5 -2
  100. package/dist/core/secrets/scan.d.ts +7 -1
  101. package/dist/core/secrets/scan.js +9 -9
  102. package/dist/coverage/cobertura.d.ts +6 -0
  103. package/dist/coverage/cobertura.js +52 -0
  104. package/dist/coverage/istanbul.d.ts +7 -0
  105. package/dist/coverage/istanbul.js +134 -0
  106. package/dist/coverage/lcov.d.ts +7 -0
  107. package/dist/coverage/lcov.js +59 -0
  108. package/dist/coverage/load.d.ts +9 -0
  109. package/dist/coverage/load.js +39 -0
  110. package/dist/coverage/types.d.ts +23 -0
  111. package/dist/coverage/types.js +5 -0
  112. package/dist/dashboard/server.d.ts +7 -1
  113. package/dist/dashboard/server.js +110 -6
  114. package/dist/enforcement/runner.d.ts +52 -3
  115. package/dist/enforcement/runner.js +467 -16
  116. package/dist/index.d.ts +36 -2
  117. package/dist/index.js +23 -2
  118. package/dist/intelligence/graph/build.d.ts +2 -0
  119. package/dist/intelligence/graph/build.js +49 -5
  120. package/dist/intelligence/graph/incremental.d.ts +68 -0
  121. package/dist/intelligence/graph/incremental.js +234 -0
  122. package/dist/intelligence/resolve/imports.d.ts +36 -0
  123. package/dist/intelligence/resolve/imports.js +245 -0
  124. package/dist/languages/go.d.ts +9 -0
  125. package/dist/languages/go.js +50 -0
  126. package/dist/languages/index.d.ts +11 -0
  127. package/dist/languages/index.js +76 -0
  128. package/dist/languages/php.d.ts +8 -0
  129. package/dist/languages/php.js +202 -0
  130. package/dist/languages/python.d.ts +11 -0
  131. package/dist/languages/python.js +180 -0
  132. package/dist/languages/types.d.ts +42 -0
  133. package/dist/languages/types.js +26 -0
  134. package/dist/languages/typescript.d.ts +3 -0
  135. package/dist/languages/typescript.js +95 -0
  136. package/dist/mcp/agent/registry.d.ts +13 -0
  137. package/dist/mcp/agent/registry.js +234 -0
  138. package/dist/mcp/agentdoctor/server.js +8 -1
  139. package/dist/mcp/intelligence/handlers.d.ts +5 -0
  140. package/dist/mcp/intelligence/handlers.js +229 -40
  141. package/dist/mcp/intelligence/path-safety.d.ts +2 -4
  142. package/dist/mcp/intelligence/path-safety.js +25 -44
  143. package/dist/mcp/intelligence/registry.d.ts +1 -1
  144. package/dist/mcp/intelligence/registry.js +69 -1
  145. package/dist/platform/firewall/evaluate.d.ts +6 -3
  146. package/dist/platform/firewall/evaluate.js +66 -15
  147. package/dist/platform/graph/build.js +19 -23
  148. package/dist/platform/health/analyze.js +23 -17
  149. package/dist/platform/index.js +2 -0
  150. package/dist/platform/test-impact/analyze.d.ts +21 -3
  151. package/dist/platform/test-impact/analyze.js +214 -28
  152. package/dist/platform/tokens/plan.js +39 -20
  153. package/dist/platform/types.d.ts +9 -0
  154. package/dist/policy/compose.d.ts +34 -0
  155. package/dist/policy/compose.js +118 -0
  156. package/dist/policy/packs.js +33 -1
  157. package/dist/security/paths.d.ts +21 -0
  158. package/dist/security/paths.js +133 -0
  159. package/dist/storage/postgres.d.ts +26 -0
  160. package/dist/storage/postgres.js +90 -0
  161. package/dist/storage/provider.d.ts +2 -7
  162. package/dist/storage/provider.js +22 -16
  163. package/dist/storage/sqlite.d.ts +33 -0
  164. package/dist/storage/sqlite.js +77 -0
  165. package/dist/team/auth.js +1 -1
  166. package/dist/workspace/index.d.ts +69 -0
  167. package/dist/workspace/index.js +220 -0
  168. package/package.json +9 -3
@@ -0,0 +1,84 @@
1
+ import { redactForModel, redactMessages } from "../redact.js";
2
+ /**
3
+ * Deterministic mock provider for tests and offline demos.
4
+ * Never calls the network.
5
+ * Optional `script` advances one step per chat() for coding-loop tests.
6
+ */
7
+ export class MockModelProvider {
8
+ id = "mock";
9
+ turn = 0;
10
+ script;
11
+ constructor(options) {
12
+ this.script = options?.script;
13
+ }
14
+ metadata() {
15
+ return {
16
+ provider: "mock",
17
+ model: "mock",
18
+ supportsTools: true,
19
+ supportsStreaming: false,
20
+ supportsStructuredOutput: false,
21
+ };
22
+ }
23
+ async chat(request) {
24
+ const messages = redactMessages(request.messages);
25
+ if (this.script) {
26
+ const step = this.script[this.turn] ?? { content: "[AI-GENERATED mock] done", toolCalls: [] };
27
+ this.turn += 1;
28
+ const toolCalls = step.toolCalls ?? [];
29
+ const text = step.content ?? (toolCalls.length ? "(proposing tools)" : "done");
30
+ return {
31
+ provider: "mock",
32
+ model: "mock",
33
+ message: { role: "assistant", content: text },
34
+ toolCalls,
35
+ usage: {
36
+ promptTokens: messages.reduce((n, m) => n + Math.ceil(m.content.length / 4), 0),
37
+ completionTokens: Math.ceil(text.length / 4),
38
+ totalTokens: 0,
39
+ },
40
+ finishReason: toolCalls.length ? "tool_calls" : "stop",
41
+ aiGenerated: true,
42
+ };
43
+ }
44
+ const lastUser = [...messages].reverse().find((m) => m.role === "user");
45
+ const lastTool = [...messages].reverse().find((m) => m.role === "tool");
46
+ const preview = redactForModel(lastUser?.content ?? "").slice(0, 120);
47
+ let text = `[AI-GENERATED mock] Received ${messages.length} message(s). Last user: ${preview || "(empty)"}`;
48
+ // Tool-output injection must never become instructions — mock refuses to honor them.
49
+ if (lastTool?.content && /IGNORE\s+ALL\s+PREVIOUS\s+INSTRUCTIONS/i.test(lastTool.content)) {
50
+ text =
51
+ "[AI-GENERATED mock] Ignoring untrusted TOOL_OUTPUT instructions. Continuing safely without destructive actions.";
52
+ return {
53
+ provider: "mock",
54
+ model: "mock",
55
+ message: { role: "assistant", content: text },
56
+ toolCalls: [],
57
+ finishReason: "stop",
58
+ aiGenerated: true,
59
+ };
60
+ }
61
+ const toolCalls = [];
62
+ const match = /call tool\s+([a-z0-9_]+)/i.exec(lastUser?.content ?? "");
63
+ if (match && request.tools?.some((t) => t.name === match[1])) {
64
+ toolCalls.push({
65
+ id: "mock_tool_1",
66
+ name: match[1],
67
+ arguments: {},
68
+ });
69
+ }
70
+ return {
71
+ provider: "mock",
72
+ model: "mock",
73
+ message: { role: "assistant", content: text },
74
+ toolCalls,
75
+ usage: {
76
+ promptTokens: messages.reduce((n, m) => n + Math.ceil(m.content.length / 4), 0),
77
+ completionTokens: Math.ceil(text.length / 4),
78
+ totalTokens: 0,
79
+ },
80
+ finishReason: toolCalls.length ? "tool_calls" : "stop",
81
+ aiGenerated: true,
82
+ };
83
+ }
84
+ }
@@ -0,0 +1,6 @@
1
+ import type { ChatRequest, ChatResponse, ModelMetadata, ModelProvider } from "../types.js";
2
+ export declare class NoneModelProvider implements ModelProvider {
3
+ readonly id: "none";
4
+ metadata(): ModelMetadata;
5
+ chat(_request: ChatRequest): Promise<ChatResponse>;
6
+ }
@@ -0,0 +1,23 @@
1
+ import { AI_PROVIDER_REQUIRED_MESSAGE } from "../types.js";
2
+ export class NoneModelProvider {
3
+ id = "none";
4
+ metadata() {
5
+ return {
6
+ provider: "none",
7
+ model: "none",
8
+ supportsTools: false,
9
+ supportsStreaming: false,
10
+ supportsStructuredOutput: false,
11
+ };
12
+ }
13
+ async chat(_request) {
14
+ return {
15
+ provider: "none",
16
+ model: "none",
17
+ message: { role: "assistant", content: "" },
18
+ toolCalls: [],
19
+ aiGenerated: true,
20
+ error: AI_PROVIDER_REQUIRED_MESSAGE,
21
+ };
22
+ }
23
+ }
@@ -0,0 +1,21 @@
1
+ import type { ChatRequest, ChatResponse, ModelMetadata, ModelProvider } from "../types.js";
2
+ export interface OpenAiCompatibleOptions {
3
+ apiKey?: string;
4
+ baseUrl?: string;
5
+ model: string;
6
+ timeoutMs?: number;
7
+ }
8
+ /**
9
+ * Minimal OpenAI-compatible Chat Completions client (custom base URL supported).
10
+ * Uses Node fetch. Does not silently fall back to another provider.
11
+ */
12
+ export declare class OpenAiCompatibleProvider implements ModelProvider {
13
+ readonly id: "openai-compatible";
14
+ private readonly apiKey;
15
+ private readonly baseUrl;
16
+ private readonly model;
17
+ private readonly timeoutMs;
18
+ constructor(options: OpenAiCompatibleOptions);
19
+ metadata(): ModelMetadata;
20
+ chat(request: ChatRequest): Promise<ChatResponse>;
21
+ }
@@ -0,0 +1,151 @@
1
+ import { redactMessages } from "../redact.js";
2
+ /**
3
+ * Minimal OpenAI-compatible Chat Completions client (custom base URL supported).
4
+ * Uses Node fetch. Does not silently fall back to another provider.
5
+ */
6
+ export class OpenAiCompatibleProvider {
7
+ id = "openai-compatible";
8
+ apiKey;
9
+ baseUrl;
10
+ model;
11
+ timeoutMs;
12
+ constructor(options) {
13
+ this.apiKey = options.apiKey;
14
+ this.baseUrl = (options.baseUrl ?? "https://api.openai.com/v1").replace(/\/$/, "");
15
+ this.model = options.model;
16
+ this.timeoutMs = options.timeoutMs ?? 60_000;
17
+ }
18
+ metadata() {
19
+ return {
20
+ provider: "openai-compatible",
21
+ model: this.model,
22
+ supportsTools: true,
23
+ supportsStreaming: false,
24
+ supportsStructuredOutput: false,
25
+ };
26
+ }
27
+ async chat(request) {
28
+ if (!this.apiKey &&
29
+ !this.baseUrl.includes("localhost") &&
30
+ !this.baseUrl.includes("127.0.0.1")) {
31
+ return {
32
+ provider: "openai-compatible",
33
+ model: this.model,
34
+ message: { role: "assistant", content: "" },
35
+ toolCalls: [],
36
+ aiGenerated: true,
37
+ error: "AI provider unavailable: AGENTDOCTOR_AI_API_KEY is not set.",
38
+ };
39
+ }
40
+ const messages = redactMessages(request.messages).map((m) => ({
41
+ role: m.role === "tool" ? "tool" : m.role,
42
+ content: m.content,
43
+ ...(m.toolCallId ? { tool_call_id: m.toolCallId } : {}),
44
+ ...(m.name ? { name: m.name } : {}),
45
+ }));
46
+ const body = {
47
+ model: request.model ?? this.model,
48
+ messages,
49
+ temperature: request.temperature ?? 0.2,
50
+ max_tokens: request.maxTokens ?? 2048,
51
+ };
52
+ if (request.tools && request.tools.length > 0) {
53
+ body.tools = request.tools.map((t) => ({
54
+ type: "function",
55
+ function: {
56
+ name: t.name,
57
+ description: t.description,
58
+ parameters: t.parameters,
59
+ },
60
+ }));
61
+ }
62
+ const controller = new AbortController();
63
+ const timer = setTimeout(() => controller.abort(), this.timeoutMs);
64
+ const onAbort = () => controller.abort();
65
+ request.signal?.addEventListener("abort", onAbort);
66
+ try {
67
+ const headers = {
68
+ "Content-Type": "application/json",
69
+ };
70
+ if (this.apiKey) {
71
+ headers.Authorization = `Bearer ${this.apiKey}`;
72
+ }
73
+ const res = await fetch(`${this.baseUrl}/chat/completions`, {
74
+ method: "POST",
75
+ headers,
76
+ body: JSON.stringify(body),
77
+ signal: controller.signal,
78
+ });
79
+ if (!res.ok) {
80
+ const errText = await res.text().catch(() => "");
81
+ return {
82
+ provider: "openai-compatible",
83
+ model: this.model,
84
+ message: { role: "assistant", content: "" },
85
+ toolCalls: [],
86
+ aiGenerated: true,
87
+ error: `AI provider unavailable: HTTP ${res.status}${errText ? ` (${errText.slice(0, 200)})` : ""}`,
88
+ };
89
+ }
90
+ const data = (await res.json());
91
+ const choice = data.choices?.[0];
92
+ const rawTools = choice?.message?.tool_calls ?? [];
93
+ const toolCalls = rawTools.map((tc) => {
94
+ let args = {};
95
+ try {
96
+ args = JSON.parse(tc.function?.arguments ?? "{}");
97
+ }
98
+ catch {
99
+ args = {};
100
+ }
101
+ return {
102
+ id: tc.id,
103
+ name: tc.function?.name ?? "unknown",
104
+ arguments: args,
105
+ };
106
+ });
107
+ const message = {
108
+ role: "assistant",
109
+ content: String(choice?.message?.content ?? ""),
110
+ };
111
+ return {
112
+ provider: "openai-compatible",
113
+ model: data.model ?? this.model,
114
+ message,
115
+ toolCalls,
116
+ ...(data.usage
117
+ ? {
118
+ usage: {
119
+ ...(data.usage.prompt_tokens !== undefined
120
+ ? { promptTokens: data.usage.prompt_tokens }
121
+ : {}),
122
+ ...(data.usage.completion_tokens !== undefined
123
+ ? { completionTokens: data.usage.completion_tokens }
124
+ : {}),
125
+ ...(data.usage.total_tokens !== undefined
126
+ ? { totalTokens: data.usage.total_tokens }
127
+ : {}),
128
+ },
129
+ }
130
+ : {}),
131
+ ...(choice?.finish_reason ? { finishReason: choice.finish_reason } : {}),
132
+ aiGenerated: true,
133
+ };
134
+ }
135
+ catch (error) {
136
+ const msg = error instanceof Error ? error.message : String(error);
137
+ return {
138
+ provider: "openai-compatible",
139
+ model: this.model,
140
+ message: { role: "assistant", content: "" },
141
+ toolCalls: [],
142
+ aiGenerated: true,
143
+ error: `AI provider unavailable: ${msg}`,
144
+ };
145
+ }
146
+ finally {
147
+ clearTimeout(timer);
148
+ request.signal?.removeEventListener("abort", onAbort);
149
+ }
150
+ }
151
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Redaction for outbound model payloads.
3
+ * Extends the local-ai redaction patterns; never send raw secrets to providers.
4
+ */
5
+ export declare function redactForModel(text: string): string;
6
+ export declare function redactMessages<T extends {
7
+ content: string;
8
+ }>(messages: T[]): T[];
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Redaction for outbound model payloads.
3
+ * Extends the local-ai redaction patterns; never send raw secrets to providers.
4
+ */
5
+ const SECRET_PATTERNS = [
6
+ /\b(AKIA[0-9A-Z]{16})\b/g,
7
+ /\b(ghp_[A-Za-z0-9]{36})\b/g,
8
+ /\b(sk-[A-Za-z0-9]{20,})\b/g,
9
+ /-----BEGIN [^-]+PRIVATE KEY-----[\s\S]*?-----END [^-]+PRIVATE KEY-----/g,
10
+ /\b((?:export\s+)?(?:AWS_SECRET_ACCESS_KEY|GITHUB_TOKEN|NPM_TOKEN|OPENAI_API_KEY|ANTHROPIC_API_KEY|STRIPE_SECRET_KEY|AGENTDOCTOR_AI_API_KEY)\s*=\s*)(['"]?)[^\s'"]+\2/gi,
11
+ ];
12
+ export function redactForModel(text) {
13
+ let out = text;
14
+ for (const re of SECRET_PATTERNS) {
15
+ out = out.replace(re, "[REDACTED]");
16
+ }
17
+ return out;
18
+ }
19
+ export function redactMessages(messages) {
20
+ return messages.map((m) => ({ ...m, content: redactForModel(m.content) }));
21
+ }
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Provider-agnostic model types for AgentDoctor 2.1 Project AI Agent.
3
+ * The model reasons; AgentDoctor owns context, tools, and verification.
4
+ */
5
+ export type AiProviderId = "none" | "mock" | "openai-compatible" | "ollama" | "anthropic" | "gemini";
6
+ export type ChatRole = "system" | "user" | "assistant" | "tool";
7
+ export interface ChatMessage {
8
+ role: ChatRole;
9
+ content: string;
10
+ /** Tool call id when role is tool */
11
+ toolCallId?: string;
12
+ name?: string;
13
+ }
14
+ export interface ToolSpec {
15
+ name: string;
16
+ description: string;
17
+ /** JSON Schema object for arguments */
18
+ parameters: Record<string, unknown>;
19
+ }
20
+ export interface ToolCallRequest {
21
+ id: string;
22
+ name: string;
23
+ arguments: Record<string, unknown>;
24
+ }
25
+ export interface TokenUsage {
26
+ promptTokens?: number;
27
+ completionTokens?: number;
28
+ totalTokens?: number;
29
+ }
30
+ export interface ChatRequest {
31
+ messages: ChatMessage[];
32
+ tools?: ToolSpec[];
33
+ model?: string;
34
+ temperature?: number;
35
+ maxTokens?: number;
36
+ /** AbortSignal for cancellation when supported */
37
+ signal?: AbortSignal;
38
+ }
39
+ export interface ChatResponse {
40
+ provider: AiProviderId;
41
+ model: string;
42
+ message: ChatMessage;
43
+ toolCalls: ToolCallRequest[];
44
+ usage?: TokenUsage;
45
+ finishReason?: string;
46
+ /** Always true for model-generated content */
47
+ aiGenerated: true;
48
+ error?: string;
49
+ }
50
+ export interface ModelMetadata {
51
+ provider: AiProviderId;
52
+ model: string;
53
+ supportsTools: boolean;
54
+ supportsStreaming: boolean;
55
+ supportsStructuredOutput: boolean;
56
+ }
57
+ export interface ModelProvider {
58
+ readonly id: AiProviderId;
59
+ metadata(): ModelMetadata;
60
+ chat(request: ChatRequest): Promise<ChatResponse>;
61
+ /**
62
+ * Optional streaming. Providers that do not support streaming reject.
63
+ */
64
+ streamChat?(request: ChatRequest): AsyncIterable<{
65
+ delta: string;
66
+ done: boolean;
67
+ response?: ChatResponse;
68
+ }>;
69
+ }
70
+ /** User-facing copy when AI is not configured. */
71
+ export declare const AI_PROVIDER_REQUIRED_MESSAGE = "AI features require an AI provider. AgentDoctor's project analysis features continue to work without AI.";
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Provider-agnostic model types for AgentDoctor 2.1 Project AI Agent.
3
+ * The model reasons; AgentDoctor owns context, tools, and verification.
4
+ */
5
+ /** User-facing copy when AI is not configured. */
6
+ export const AI_PROVIDER_REQUIRED_MESSAGE = "AI features require an AI provider. AgentDoctor's project analysis features continue to work without AI.";
@@ -0,0 +1,64 @@
1
+ import type { RepositoryGraph } from "../platform/types.js";
2
+ export interface ArchitectureLayer {
3
+ id: string;
4
+ paths: string[];
5
+ }
6
+ export interface ForbiddenDependency {
7
+ fromLayer: string;
8
+ toLayer: string;
9
+ id: string;
10
+ description: string;
11
+ }
12
+ export interface AllowedDependency {
13
+ fromLayer: string;
14
+ toLayer: string;
15
+ }
16
+ export interface ArchitectureContract {
17
+ version: "1";
18
+ layers: ArchitectureLayer[];
19
+ forbidden: ForbiddenDependency[];
20
+ allowed: AllowedDependency[];
21
+ }
22
+ export interface ArchitectureViolation {
23
+ ruleId: string;
24
+ kind: "forbidden" | "not-allowed";
25
+ description: string;
26
+ fromPath: string;
27
+ toPath: string;
28
+ fromLayer: string;
29
+ toLayer: string;
30
+ evidence: {
31
+ edgeId: string;
32
+ edgeKind: string;
33
+ evidenceKind: string;
34
+ };
35
+ }
36
+ export interface ArchitectureCheckResult {
37
+ root: string;
38
+ contractPath: string | null;
39
+ contract: ArchitectureContract | null;
40
+ violations: ArchitectureViolation[];
41
+ importEdgesChecked: number;
42
+ limitations: string[];
43
+ }
44
+ export declare const DEFAULT_ARCHITECTURE_CONTRACT: ArchitectureContract;
45
+ export declare function layerForPath(filePath: string, layers: ArchitectureLayer[]): string | null;
46
+ /**
47
+ * Minimal YAML subset parser for architecture contracts.
48
+ * Also accepts JSON content inside .yml files.
49
+ */
50
+ export declare function parseArchitectureYamlSubset(text: string): ArchitectureContract;
51
+ export declare function parseArchitectureJson(text: string): ArchitectureContract;
52
+ export declare function loadArchitectureContract(rootInput: string): Promise<{
53
+ contract: ArchitectureContract;
54
+ path: string;
55
+ } | null>;
56
+ export declare function initArchitecture(rootInput: string): Promise<string>;
57
+ /**
58
+ * Check import edges against the architecture contract.
59
+ * Forbidden rules always apply. When `allowed` is non-empty, cross-layer
60
+ * imports not listed in allowed (and not same-layer) are reported as not-allowed.
61
+ */
62
+ export declare function checkArchitecture(rootInput: string, graph: RepositoryGraph, contract?: ArchitectureContract | null, contractPath?: string | null): ArchitectureCheckResult;
63
+ export declare function checkArchitectureAtRoot(rootInput: string, graph: RepositoryGraph): Promise<ArchitectureCheckResult>;
64
+ export declare function explainArchitecture(contract: ArchitectureContract | null, check?: ArchitectureCheckResult | null): string;