@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,252 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { AI_PROVIDER_REQUIRED_MESSAGE, createModelProvider, loadAiConfig, publicAiConfig, redactForModel, } from "../../ai/index.js";
3
+ import { buildIntelligenceGraph } from "../../intelligence/graph/build.js";
4
+ import { appendSessionEvent, createSession, endSession, } from "../../platform/sessions/store.js";
5
+ import { resolveRepoRoot } from "../../utils/path.js";
6
+ import { retrieveProjectContext } from "../context/retrieve.js";
7
+ import { ChatMemory } from "./memory.js";
8
+ import { PROJECT_CHAT_SYSTEM_PROMPT, wrapProjectData } from "./prompts.js";
9
+ import { formatProjectSummary, summarizeProjectForChat, } from "./project-summary.js";
10
+ import { buildChatTurnResponse, formatChatResponseForCli } from "./response.js";
11
+ export const CHAT_PROVIDER_NONE_MESSAGE = `AI chat is not configured.
12
+
13
+ Configure an AI provider to use AgentDoctor Project Chat.
14
+
15
+ Set AGENTDOCTOR_AI_PROVIDER (and AGENTDOCTOR_AI_API_KEY / AGENTDOCTOR_AI_BASE_URL as needed).
16
+
17
+ AgentDoctor's non-AI project analysis remains available.`;
18
+ /**
19
+ * Project Chat service (Milestone 2).
20
+ * Orchestrates memory → context → model → truth/citations.
21
+ * Does not edit files or execute commands.
22
+ */
23
+ export class ChatService {
24
+ root;
25
+ memory;
26
+ provider;
27
+ budgetTokens;
28
+ persistAudit;
29
+ systemPrompt;
30
+ auditSession;
31
+ graphCache;
32
+ lastContext;
33
+ started = false;
34
+ constructor(options) {
35
+ this.root = resolveRepoRoot(options.root);
36
+ this.provider = options.provider ?? createModelProvider(loadAiConfig());
37
+ this.budgetTokens = options.budgetTokens ?? 6_000;
38
+ this.persistAudit = options.persistAudit !== false;
39
+ this.systemPrompt = options.systemPromptAddon
40
+ ? `${PROJECT_CHAT_SYSTEM_PROMPT}\n\n${options.systemPromptAddon}`
41
+ : PROJECT_CHAT_SYSTEM_PROMPT;
42
+ this.memory = new ChatMemory({
43
+ root: this.root,
44
+ ...(options.maxMemoryTurns ? { maxTurns: options.maxMemoryTurns } : {}),
45
+ ...(options.maxMemoryChars ? { maxChars: options.maxMemoryChars } : {}),
46
+ });
47
+ }
48
+ get sessionId() {
49
+ return this.memory.sessionId;
50
+ }
51
+ getProviderId() {
52
+ return this.provider.id;
53
+ }
54
+ getLastContextPaths() {
55
+ return (this.lastContext?.citations ?? [])
56
+ .map((c) => c.path)
57
+ .filter((p) => Boolean(p));
58
+ }
59
+ getLastContext() {
60
+ return this.lastContext;
61
+ }
62
+ async start() {
63
+ if (this.started) {
64
+ return {
65
+ summary: await summarizeProjectForChat(this.root),
66
+ providerLabel: this.providerLabel(),
67
+ };
68
+ }
69
+ this.started = true;
70
+ if (this.persistAudit) {
71
+ const cfg = publicAiConfig(loadAiConfig());
72
+ this.auditSession = await createSession({
73
+ root: this.root,
74
+ agentId: "project-chat",
75
+ model: `${this.provider.id}:${cfg.model}`,
76
+ });
77
+ await this.audit("prompt", "CHAT_STARTED", {
78
+ provider: this.provider.id,
79
+ sessionId: this.sessionId,
80
+ });
81
+ }
82
+ // Warm graph once per chat session
83
+ try {
84
+ this.graphCache = await buildIntelligenceGraph({ root: this.root, mode: "auto" });
85
+ }
86
+ catch {
87
+ this.graphCache = undefined;
88
+ }
89
+ return {
90
+ summary: await summarizeProjectForChat(this.root),
91
+ providerLabel: this.providerLabel(),
92
+ };
93
+ }
94
+ async ask(userMessage) {
95
+ await this.start();
96
+ const safeUser = redactForModel(userMessage);
97
+ this.memory.addUser(safeUser);
98
+ await this.audit("prompt", "CHAT_USER_MESSAGE", {
99
+ chars: String(safeUser.length),
100
+ });
101
+ if (this.provider.id === "none") {
102
+ const response = {
103
+ sessionId: this.sessionId,
104
+ message: CHAT_PROVIDER_NONE_MESSAGE,
105
+ truthClaims: [],
106
+ citations: [],
107
+ provider: "none",
108
+ model: "none",
109
+ status: "provider-none",
110
+ error: AI_PROVIDER_REQUIRED_MESSAGE,
111
+ contextPaths: [],
112
+ contextTruncated: false,
113
+ limitations: [],
114
+ };
115
+ await this.audit("error", "CHAT_FAILED", { reason: "provider-none" });
116
+ return response;
117
+ }
118
+ const query = this.memory.resolveQuery(safeUser);
119
+ const context = await retrieveProjectContext({
120
+ root: this.root,
121
+ query,
122
+ budgetTokens: this.budgetTokens,
123
+ includePaths: [
124
+ ...this.memory.getReferencedPaths().slice(-8),
125
+ ...this.memory.getLastContextPaths().slice(-8),
126
+ ],
127
+ ...(this.graphCache ? { graph: this.graphCache } : {}),
128
+ });
129
+ this.lastContext = context;
130
+ await this.audit("file-read", "CHAT_CONTEXT_RETRIEVED", {
131
+ citations: String(context.citations.length),
132
+ tokens: String(context.estimatedTokens),
133
+ });
134
+ const history = this.memory.historyForModel().slice(0, -1); // exclude current user (added separately)
135
+ const messages = [
136
+ { role: "system", content: this.systemPrompt },
137
+ { role: "system", content: wrapProjectData(context.rendered) },
138
+ ...history.map((m) => ({ role: m.role, content: redactForModel(m.content) })),
139
+ { role: "user", content: safeUser },
140
+ ];
141
+ await this.audit("prompt", "CHAT_MODEL_REQUESTED", {
142
+ provider: this.provider.id,
143
+ messages: String(messages.length),
144
+ });
145
+ const modelResult = await this.provider.chat({
146
+ messages,
147
+ temperature: 0.2,
148
+ maxTokens: 2048,
149
+ });
150
+ if (modelResult.error) {
151
+ const response = buildChatTurnResponse({
152
+ sessionId: this.sessionId,
153
+ modelText: `AgentDoctor could not reach the configured AI provider.\n\nProvider: ${modelResult.provider}\nModel: ${modelResult.model}\nError: ${sanitizeProviderError(modelResult.error)}`,
154
+ context,
155
+ provider: modelResult.provider,
156
+ model: modelResult.model,
157
+ status: "provider-error",
158
+ error: sanitizeProviderError(modelResult.error),
159
+ ...(modelResult.usage ? { usage: modelResult.usage } : {}),
160
+ });
161
+ await this.audit("error", "CHAT_FAILED", {
162
+ reason: "provider-error",
163
+ provider: modelResult.provider,
164
+ });
165
+ return response;
166
+ }
167
+ const text = redactForModel(modelResult.message.content || "");
168
+ const response = buildChatTurnResponse({
169
+ sessionId: this.sessionId,
170
+ modelText: text,
171
+ context,
172
+ provider: modelResult.provider,
173
+ model: modelResult.model,
174
+ status: "ok",
175
+ ...(modelResult.usage ? { usage: modelResult.usage } : {}),
176
+ });
177
+ this.memory.addAssistant(response.message, response.contextPaths);
178
+ await this.audit("prompt", "CHAT_MODEL_RESPONSE", {
179
+ status: response.status,
180
+ citations: String(response.citations.length),
181
+ });
182
+ await this.audit("prompt", "CHAT_COMPLETED", {
183
+ turn: String(this.memory.snapshot().turnCount),
184
+ });
185
+ return response;
186
+ }
187
+ clearMemory() {
188
+ this.memory.clear();
189
+ this.lastContext = undefined;
190
+ }
191
+ async end() {
192
+ if (this.auditSession) {
193
+ await endSession(this.auditSession);
194
+ this.auditSession = undefined;
195
+ }
196
+ }
197
+ formatResponse(response) {
198
+ return formatChatResponseForCli(response);
199
+ }
200
+ formatProject() {
201
+ return summarizeProjectForChat(this.root).then(formatProjectSummary);
202
+ }
203
+ formatContext() {
204
+ const paths = this.getLastContextPaths();
205
+ if (!paths.length) {
206
+ return "No context retrieved yet. Ask a project question first.\n";
207
+ }
208
+ const lines = ["Context used for the last answer:", ""];
209
+ for (const p of paths) {
210
+ lines.push(` ✓ ${p}`);
211
+ }
212
+ if (this.lastContext?.limitations.length) {
213
+ lines.push("");
214
+ lines.push("Limitations:");
215
+ for (const l of this.lastContext.limitations.slice(0, 5)) {
216
+ lines.push(` - ${l}`);
217
+ }
218
+ }
219
+ lines.push("");
220
+ return `${lines.join("\n")}\n`;
221
+ }
222
+ providerLabel() {
223
+ const cfg = publicAiConfig(loadAiConfig());
224
+ if (this.provider.id === "none")
225
+ return "none (AI chat disabled)";
226
+ if (this.provider.id === "mock")
227
+ return "mock (tests/demo)";
228
+ return `${this.provider.id} / ${cfg.model}`;
229
+ }
230
+ async audit(type, summary, detail) {
231
+ if (!this.persistAudit || !this.auditSession)
232
+ return;
233
+ await appendSessionEvent(this.auditSession, {
234
+ type,
235
+ summary,
236
+ risk: type === "error" ? "low" : "none",
237
+ ...(detail ? { detail } : {}),
238
+ });
239
+ }
240
+ }
241
+ function sanitizeProviderError(message) {
242
+ return redactForModel(message)
243
+ .replace(/Bearer\s+\S+/gi, "Bearer [REDACTED]")
244
+ .replace(/sk-[A-Za-z0-9]+/g, "[REDACTED]");
245
+ }
246
+ export function createChatService(options) {
247
+ return new ChatService(options);
248
+ }
249
+ /** Test helper: unique id without IO */
250
+ export function newChatSessionId() {
251
+ return randomUUID();
252
+ }
@@ -0,0 +1,48 @@
1
+ import type { TruthLabel, ContextCitation } from "../context/types.js";
2
+ import type { AiProviderId, ModelProvider, TokenUsage } from "../../ai/types.js";
3
+ export type ChatRole = "system" | "user" | "assistant";
4
+ export interface ChatMessage {
5
+ role: ChatRole;
6
+ content: string;
7
+ at: string;
8
+ }
9
+ export interface TruthClaim {
10
+ text: string;
11
+ label: TruthLabel;
12
+ citationPaths: string[];
13
+ }
14
+ export interface ChatTurnResponse {
15
+ sessionId: string;
16
+ message: string;
17
+ truthClaims: TruthClaim[];
18
+ citations: ContextCitation[];
19
+ provider: AiProviderId;
20
+ model: string;
21
+ status: "ok" | "provider-none" | "provider-error" | "failed";
22
+ error?: string;
23
+ usage?: TokenUsage;
24
+ contextPaths: string[];
25
+ contextTruncated: boolean;
26
+ limitations: string[];
27
+ }
28
+ export interface ChatMemorySnapshot {
29
+ sessionId: string;
30
+ root: string;
31
+ topic?: string;
32
+ messages: ChatMessage[];
33
+ referencedPaths: string[];
34
+ lastContextPaths: string[];
35
+ turnCount: number;
36
+ }
37
+ export interface ChatServiceOptions {
38
+ root: string;
39
+ /** Inject provider for tests; otherwise load from env */
40
+ provider?: ModelProvider;
41
+ budgetTokens?: number;
42
+ maxMemoryTurns?: number;
43
+ maxMemoryChars?: number;
44
+ /** Persist audit via platform sessions (default true) */
45
+ persistAudit?: boolean;
46
+ /** Appended to PROJECT_CHAT_SYSTEM_PROMPT (modes) */
47
+ systemPromptAddon?: string;
48
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,21 @@
1
+ import type { ContextBundle } from "./types.js";
2
+ import type { RepositoryGraph } from "../../platform/types.js";
3
+ export interface RetrieveContextOptions {
4
+ root: string;
5
+ query: string;
6
+ budgetTokens?: number;
7
+ /** Optional relative paths the caller already wants included */
8
+ includePaths?: string[];
9
+ maxExcerptChars?: number;
10
+ /** Reuse a previously built graph (session cache) */
11
+ graph?: RepositoryGraph & {
12
+ builder?: string;
13
+ astFilesParsed?: number;
14
+ };
15
+ }
16
+ /**
17
+ * Retrieve a budgeted project context pack for the agent.
18
+ * Reuses graph + planContext. Does not invent facts.
19
+ * Hostile paths are rejected via resolveSafeRepoPath.
20
+ */
21
+ export declare function retrieveProjectContext(options: RetrieveContextOptions): Promise<ContextBundle>;
@@ -0,0 +1,117 @@
1
+ import fs from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { planContext } from "../../platform/tokens/plan.js";
4
+ import { buildIntelligenceGraph } from "../../intelligence/graph/build.js";
5
+ import { resolveRepoRoot } from "../../utils/path.js";
6
+ import { resolveSafeRepoPath, PathEscapeError } from "../../security/paths.js";
7
+ function estimateTokens(text) {
8
+ return Math.ceil(text.length / 4);
9
+ }
10
+ /**
11
+ * Retrieve a budgeted project context pack for the agent.
12
+ * Reuses graph + planContext. Does not invent facts.
13
+ * Hostile paths are rejected via resolveSafeRepoPath.
14
+ */
15
+ export async function retrieveProjectContext(options) {
16
+ const root = resolveRepoRoot(options.root);
17
+ const query = options.query.trim() || "project overview";
18
+ const maxExcerpt = options.maxExcerptChars ?? 1_200;
19
+ const citations = [];
20
+ const limitations = [];
21
+ const chunks = [];
22
+ let graph = options.graph;
23
+ try {
24
+ if (!graph) {
25
+ graph = await buildIntelligenceGraph({ root, mode: "auto" });
26
+ }
27
+ }
28
+ catch (error) {
29
+ limitations.push(`Graph unavailable: ${error instanceof Error ? error.message : String(error)}`);
30
+ return {
31
+ root,
32
+ query,
33
+ citations: [
34
+ {
35
+ source: "repository",
36
+ evidenceType: "metadata",
37
+ confidence: "UNKNOWN",
38
+ note: "Repository graph could not be built for this query.",
39
+ },
40
+ ],
41
+ rendered: `Query: ${query}\n(No graph context available.)`,
42
+ estimatedTokens: 32,
43
+ limitations,
44
+ };
45
+ }
46
+ const plan = await planContext({
47
+ root,
48
+ graph,
49
+ query,
50
+ budgetTokens: options.budgetTokens ?? 6_000,
51
+ });
52
+ limitations.push(...plan.limitations);
53
+ const paths = new Set(plan.selected.map((s) => s.path));
54
+ for (const p of options.includePaths ?? []) {
55
+ paths.add(p);
56
+ }
57
+ for (const rel of paths) {
58
+ try {
59
+ const abs = resolveSafeRepoPath(root, rel);
60
+ const text = await fs.readFile(abs, "utf8");
61
+ const excerpt = text.slice(0, maxExcerpt);
62
+ const citation = {
63
+ source: "repository",
64
+ path: rel.split(path.sep).join("/"),
65
+ evidenceType: "source-code",
66
+ confidence: "VERIFIED",
67
+ excerpt,
68
+ };
69
+ citations.push(citation);
70
+ chunks.push(`--- FILE ${citation.path} (VERIFIED excerpt) ---\n${excerpt}`);
71
+ }
72
+ catch (error) {
73
+ if (error instanceof PathEscapeError) {
74
+ citations.push({
75
+ source: "repository",
76
+ path: rel,
77
+ evidenceType: "metadata",
78
+ confidence: "UNKNOWN",
79
+ note: "path_escape: rejected",
80
+ });
81
+ continue;
82
+ }
83
+ citations.push({
84
+ source: "repository",
85
+ path: rel,
86
+ evidenceType: "metadata",
87
+ confidence: "UNKNOWN",
88
+ note: error instanceof Error ? error.message : String(error),
89
+ });
90
+ }
91
+ }
92
+ if (citations.length === 0) {
93
+ citations.push({
94
+ source: "repository",
95
+ evidenceType: "metadata",
96
+ confidence: "UNKNOWN",
97
+ note: "No files selected for this query within the token budget.",
98
+ });
99
+ limitations.push("No file excerpts retrieved — answer must use UNKNOWN where evidence is missing.");
100
+ }
101
+ const rendered = [
102
+ `Project root: ${root}`,
103
+ `Query: ${query}`,
104
+ `Selected files: ${plan.selected.length}`,
105
+ `Budget tokens: ${plan.budgetTokens} (est. used ${plan.estimatedTokens})`,
106
+ "",
107
+ ...chunks,
108
+ ].join("\n");
109
+ return {
110
+ root,
111
+ query,
112
+ citations,
113
+ rendered,
114
+ estimatedTokens: estimateTokens(rendered),
115
+ limitations,
116
+ };
117
+ }
@@ -0,0 +1,6 @@
1
+ import type { TruthLabel } from "./types.js";
2
+ export declare function truthLabelHelp(label: TruthLabel): string;
3
+ /**
4
+ * Downgrade helper: never promote INFERRED/UNKNOWN to VERIFIED accidentally.
5
+ */
6
+ export declare function minTruth(a: TruthLabel, b: TruthLabel): TruthLabel;
@@ -0,0 +1,19 @@
1
+ export function truthLabelHelp(label) {
2
+ switch (label) {
3
+ case "VERIFIED":
4
+ return "Directly supported by repository/project evidence.";
5
+ case "INFERRED":
6
+ return "Reasonable conclusion from available evidence but not directly established.";
7
+ case "UNKNOWN":
8
+ return "Cannot be determined from current project evidence.";
9
+ case "EXTERNAL":
10
+ return "Requires information outside the repository.";
11
+ }
12
+ }
13
+ /**
14
+ * Downgrade helper: never promote INFERRED/UNKNOWN to VERIFIED accidentally.
15
+ */
16
+ export function minTruth(a, b) {
17
+ const order = ["VERIFIED", "INFERRED", "UNKNOWN", "EXTERNAL"];
18
+ return order[Math.max(order.indexOf(a), order.indexOf(b))];
19
+ }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Context citation and truth labels for evidence-backed agent answers.
3
+ */
4
+ export type TruthLabel = "VERIFIED" | "INFERRED" | "UNKNOWN" | "EXTERNAL";
5
+ export type EvidenceType = "source-code" | "project-brain" | "graph" | "architecture" | "test" | "git" | "finding" | "documentation" | "metadata";
6
+ export interface ContextCitation {
7
+ source: "repository" | "brain" | "graph" | "session" | "external";
8
+ path?: string;
9
+ range?: string;
10
+ evidenceType: EvidenceType;
11
+ confidence: TruthLabel;
12
+ excerpt?: string;
13
+ note?: string;
14
+ }
15
+ export interface ContextBundle {
16
+ root: string;
17
+ query: string;
18
+ citations: ContextCitation[];
19
+ /** Rendered text for model (DATA channel — untrusted) */
20
+ rendered: string;
21
+ estimatedTokens: number;
22
+ limitations: string[];
23
+ }
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Context citation and truth labels for evidence-backed agent answers.
3
+ */
4
+ export {};
@@ -0,0 +1,26 @@
1
+ export { AgentState, AgentStateMachine } from "./state.js";
2
+ export type { AgentStateTransition } from "./state.js";
3
+ export { AgentRuntime, DEFAULT_AGENT_LIMITS } from "./runtime.js";
4
+ export type { AgentLimits, AgentAuditEvent, AgentRuntimeOptions, AgentTurnResult, } from "./runtime.js";
5
+ export { retrieveProjectContext } from "./context/retrieve.js";
6
+ export type { RetrieveContextOptions } from "./context/retrieve.js";
7
+ export type { ContextBundle, ContextCitation, EvidenceType, TruthLabel } from "./context/types.js";
8
+ export { truthLabelHelp, minTruth } from "./context/truth.js";
9
+ /** Project Chat (2.1 Milestone 2) */
10
+ export { ChatService, createChatService, ChatMemory, CHAT_PROVIDER_NONE_MESSAGE, buildChatTurnResponse, formatChatResponseForCli, describeTruthLabels, PROJECT_CHAT_SYSTEM_PROMPT, wrapProjectData, summarizeProjectForChat, formatProjectSummary, } from "./chat/index.js";
11
+ export type { ChatTurnResponse, ChatMemorySnapshot, ChatMessage as ProjectChatMessage, TruthClaim, ChatServiceOptions, ProjectChatSummary, } from "./chat/index.js";
12
+ /** Agent tools + plan/approval (2.1 Milestone 3) */
13
+ export { listAgentToolSpecs, getToolSpec, riskForTool, executeAgentTool, isReadTool, newToolCall, } from "./tools/index.js";
14
+ export type { AgentToolName, AgentToolSpec, AgentToolCall, AgentToolResult, AgentRiskLevel, } from "./tools/index.js";
15
+ export { evaluateApproval, formatApprovalPrompt } from "./approvals.js";
16
+ export type { ApprovalRequest, ApprovalResult, ApprovalDecision } from "./approvals.js";
17
+ export { buildAgentPlan, formatAgentPlan, approvePlan } from "./plan.js";
18
+ export type { AgentPlan, AgentPlanStep } from "./plan.js";
19
+ export { runCodingLoop } from "./loop.js";
20
+ export type { CodingLoopOptions, CodingLoopResult } from "./loop.js";
21
+ export { verifyAgentWork, formatVerificationReport } from "./verify.js";
22
+ export type { AgentVerificationReport, VerificationCheck } from "./verify.js";
23
+ export { getModeProfile, defaultStudentMode, parseAgentMode, modeAllowsMutation, modeBlocksToolCategory, } from "./modes.js";
24
+ export type { AgentMode, ModeProfile } from "./modes.js";
25
+ export { StudentService } from "./student.js";
26
+ export type { StudentDocSection, BuildWithMeResult } from "./student.js";
@@ -0,0 +1,14 @@
1
+ export { AgentState, AgentStateMachine } from "./state.js";
2
+ export { AgentRuntime, DEFAULT_AGENT_LIMITS } from "./runtime.js";
3
+ export { retrieveProjectContext } from "./context/retrieve.js";
4
+ export { truthLabelHelp, minTruth } from "./context/truth.js";
5
+ /** Project Chat (2.1 Milestone 2) */
6
+ export { ChatService, createChatService, ChatMemory, CHAT_PROVIDER_NONE_MESSAGE, buildChatTurnResponse, formatChatResponseForCli, describeTruthLabels, PROJECT_CHAT_SYSTEM_PROMPT, wrapProjectData, summarizeProjectForChat, formatProjectSummary, } from "./chat/index.js";
7
+ /** Agent tools + plan/approval (2.1 Milestone 3) */
8
+ export { listAgentToolSpecs, getToolSpec, riskForTool, executeAgentTool, isReadTool, newToolCall, } from "./tools/index.js";
9
+ export { evaluateApproval, formatApprovalPrompt } from "./approvals.js";
10
+ export { buildAgentPlan, formatAgentPlan, approvePlan } from "./plan.js";
11
+ export { runCodingLoop } from "./loop.js";
12
+ export { verifyAgentWork, formatVerificationReport } from "./verify.js";
13
+ export { getModeProfile, defaultStudentMode, parseAgentMode, modeAllowsMutation, modeBlocksToolCategory, } from "./modes.js";
14
+ export { StudentService } from "./student.js";
@@ -0,0 +1,51 @@
1
+ import type { ModelProvider } from "../ai/index.js";
2
+ import { AgentState } from "./state.js";
3
+ import { type AgentLimits } from "./runtime.js";
4
+ import { type AgentPlan } from "./plan.js";
5
+ import type { AgentToolName, AgentToolResult } from "./tools/types.js";
6
+ import { type AgentVerificationReport } from "./verify.js";
7
+ import { type AgentMode } from "./modes.js";
8
+ import type { WorkspaceModel } from "../workspace/index.js";
9
+ export interface CodingLoopOptions {
10
+ root: string;
11
+ goal: string;
12
+ provider?: ModelProvider;
13
+ limits?: Partial<AgentLimits>;
14
+ /** Required for any write/execute */
15
+ approvedByHuman: boolean;
16
+ /** Optional pre-built plan */
17
+ plan?: AgentPlan;
18
+ /** Deterministic tool calls (tests / CLI apply) — preferred when provided */
19
+ toolCalls?: Array<{
20
+ name: AgentToolName;
21
+ arguments: Record<string, unknown>;
22
+ }>;
23
+ /** When true and provider present, run model tool-call loop after plan */
24
+ useModelLoop?: boolean;
25
+ /** M5: run post-change verification (default true after writes) */
26
+ verify?: boolean;
27
+ /** M5: also run controlled tests during verification */
28
+ runTests?: boolean;
29
+ /** Agent mode — LEARN hard-blocks mutations */
30
+ mode?: AgentMode;
31
+ /** Optional workspace isolation context */
32
+ workspace?: WorkspaceModel | null;
33
+ }
34
+ export interface CodingLoopResult {
35
+ state: AgentState;
36
+ plan: AgentPlan;
37
+ toolResults: AgentToolResult[];
38
+ filesChanged: string[];
39
+ diffs: string[];
40
+ responseText: string;
41
+ stoppedReason: "completed" | "awaiting-approval" | "limit" | "failed" | "cancelled" | "mode_forbidden";
42
+ iterations: number;
43
+ toolCalls: number;
44
+ verification?: AgentVerificationReport;
45
+ teachingNotes?: string[];
46
+ }
47
+ /**
48
+ * PLAN → APPROVAL → READ/EDIT/RUN → OBSERVE → (optional model loop) → STOP.
49
+ * Hard limits enforced. Model cannot approve itself. Mode allowWrites is enforced.
50
+ */
51
+ export declare function runCodingLoop(options: CodingLoopOptions): Promise<CodingLoopResult>;