@praneeth_54/agentdoctor 3.0.2 → 3.0.3
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/CHANGELOG.md +33 -2
- package/README.md +5 -4
- package/dist/agent/chat/deterministic.d.ts +2 -0
- package/dist/agent/chat/deterministic.js +91 -3
- package/dist/agent/chat/response.js +25 -4
- package/dist/agent/chat/service.d.ts +1 -0
- package/dist/agent/chat/service.js +28 -10
- package/dist/agent/chat/types.d.ts +3 -0
- package/dist/agent/context/brain-evidence.d.ts +26 -0
- package/dist/agent/context/brain-evidence.js +296 -0
- package/dist/agent/context/retrieve.d.ts +7 -1
- package/dist/agent/context/retrieve.js +204 -63
- package/dist/agent/progress.d.ts +10 -0
- package/dist/agent/progress.js +4 -0
- package/dist/cli/ask-progress.d.ts +17 -0
- package/dist/cli/ask-progress.js +85 -0
- package/dist/cli/commands/chat.d.ts +6 -0
- package/dist/cli/commands/chat.js +16 -0
- package/dist/cli/program.js +2 -1
- package/dist/constants.d.ts +1 -1
- package/dist/constants.js +1 -1
- package/dist/core/understanding/brain/storage/store.js +0 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,36 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [3.0.3] — 2026-09-27
|
|
11
|
+
|
|
12
|
+
Patch release on top of public **3.0.2**: Brain → Project Chat evidence bridge and
|
|
13
|
+
real operation-based progress UX for `agentdoctor ask`.
|
|
14
|
+
|
|
15
|
+
Public relationship:
|
|
16
|
+
|
|
17
|
+
- **3.0.2** remains the immutable public discovery patch (`v3.0.2` → `8d41936`).
|
|
18
|
+
- **3.0.3** packages the finalize work from `a586cdf` (Brain→Chat + ask progress).
|
|
19
|
+
- Does **not** rewrite **3.0.0**, **3.0.1**, or **3.0.2** history.
|
|
20
|
+
|
|
21
|
+
### Fixed
|
|
22
|
+
|
|
23
|
+
- Deterministic `agentdoctor ask` / Project Chat now consumes searchable Project Brain claims/components as first-class **INFERRED** evidence (aligned with `brain search`).
|
|
24
|
+
- Brain-derived source paths are ownership-gated before excerpts; Brain claims are never auto-upgraded to **VERIFIED**.
|
|
25
|
+
- `.agentdoctor/**` control-plane paths cannot become **VERIFIED** application source evidence.
|
|
26
|
+
- Architecture/module answers prioritize Brain + owned sources over generic minified graph noise.
|
|
27
|
+
- Ownership, containment, HOME/broad-root, `.private`, nested-repo, symlink, and stale-brain protections remain enforced.
|
|
28
|
+
|
|
29
|
+
### Added
|
|
30
|
+
|
|
31
|
+
- `agentdoctor ask` progress UX driven by real pipeline stages (understand → search → retrieve → verify → prepare).
|
|
32
|
+
- Progress writes to stderr (TTY spinner / non-TTY stable lines); answers and `--json` stay on stdout without contamination.
|
|
33
|
+
- No artificial delays — spinner frames only animate while real work is in flight.
|
|
34
|
+
|
|
35
|
+
### Tests
|
|
36
|
+
|
|
37
|
+
- Added `tests/unit/agent/chat-brain-context.test.ts` for Brain → ask evidence, control-plane hostility, stale brain, and UNKNOWN.
|
|
38
|
+
- Added `tests/unit/cli/ask-progress.test.ts` for TTY/non-TTY/JSON/error progress lifecycle.
|
|
39
|
+
|
|
10
40
|
## [3.0.2] — 2026-09-27
|
|
11
41
|
|
|
12
42
|
Patch release: fix current-directory project discovery for `agentdoctor start`.
|
|
@@ -14,7 +44,7 @@ Patch release: fix current-directory project discovery for `agentdoctor start`.
|
|
|
14
44
|
Public relationship:
|
|
15
45
|
|
|
16
46
|
- **3.0.0** and **3.0.1** remain immutable public releases.
|
|
17
|
-
- **3.0.2**
|
|
47
|
+
- **3.0.2** is the public discovery patch (`v3.0.2` → `8d41936`). Brain→Chat and ask progress ship in **3.0.3**.
|
|
18
48
|
|
|
19
49
|
### Fixed
|
|
20
50
|
|
|
@@ -622,7 +652,8 @@ First public beta.
|
|
|
622
652
|
- Not a complete secret scanner
|
|
623
653
|
- Git “tracked secret” detection deferred
|
|
624
654
|
|
|
625
|
-
[Unreleased]: https://github.com/pranee54/AgentDoctor/compare/v3.0.
|
|
655
|
+
[Unreleased]: https://github.com/pranee54/AgentDoctor/compare/v3.0.3...HEAD
|
|
656
|
+
[3.0.3]: https://github.com/pranee54/AgentDoctor/compare/v3.0.2...v3.0.3
|
|
626
657
|
[3.0.2]: https://github.com/pranee54/AgentDoctor/compare/v3.0.1...v3.0.2
|
|
627
658
|
[3.0.1]: https://github.com/pranee54/AgentDoctor/compare/v3.0.0...v3.0.1
|
|
628
659
|
[3.0.0]: https://github.com/pranee54/AgentDoctor/compare/v2.1.0...v3.0.0
|
package/README.md
CHANGED
|
@@ -281,15 +281,15 @@ Student path: `learn` → ask → viva → docs → Build With Me (with `--appro
|
|
|
281
281
|
Requires **Node.js 20+**.
|
|
282
282
|
|
|
283
283
|
```bash
|
|
284
|
-
npm install -g @praneeth_54/agentdoctor@3.0.
|
|
285
|
-
agentdoctor --version # 3.0.
|
|
284
|
+
npm install -g @praneeth_54/agentdoctor@3.0.3
|
|
285
|
+
agentdoctor --version # 3.0.3
|
|
286
286
|
agentdoctor --help
|
|
287
287
|
```
|
|
288
288
|
|
|
289
289
|
Or without global install:
|
|
290
290
|
|
|
291
291
|
```bash
|
|
292
|
-
npx @praneeth_54/agentdoctor@3.0.
|
|
292
|
+
npx @praneeth_54/agentdoctor@3.0.3 --help
|
|
293
293
|
```
|
|
294
294
|
|
|
295
295
|
---
|
|
@@ -298,7 +298,7 @@ npx @praneeth_54/agentdoctor@3.0.2 --help
|
|
|
298
298
|
|
|
299
299
|
```bash
|
|
300
300
|
# 1. Install
|
|
301
|
-
npm install -g @praneeth_54/agentdoctor@3.0.
|
|
301
|
+
npm install -g @praneeth_54/agentdoctor@3.0.3
|
|
302
302
|
|
|
303
303
|
# 2. Enter YOUR project (not your home folder)
|
|
304
304
|
cd /path/to/my-project
|
|
@@ -586,6 +586,7 @@ Post-acceptance ideas for later releases: maintainers see [docs/internal/POST_3_
|
|
|
586
586
|
| **3.0.0 (released)** | Original public release (`v3.0.0` → `c21faf1`) |
|
|
587
587
|
| **3.0.1 (released)** | Freeze remediation / ownership & security hardening follow-up |
|
|
588
588
|
| **3.0.2 (released)** | `start` discovery aligned with deterministic `detectProject` |
|
|
589
|
+
| **3.0.3 (released)** | Brain → Project Chat evidence + `ask` progress UX |
|
|
589
590
|
| **Coming soon** | Maturity, language depth, runtime adapters, DX — not promised ship dates |
|
|
590
591
|
|
|
591
592
|
---
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { ChatTurnResponse } from "./types.js";
|
|
2
|
+
import { type AskProgressReporter } from "../progress.js";
|
|
2
3
|
export declare function answerDeterministicProjectQuestion(options: {
|
|
3
4
|
root: string;
|
|
4
5
|
question: string;
|
|
5
6
|
sessionId: string;
|
|
7
|
+
progress?: AskProgressReporter;
|
|
6
8
|
}): Promise<ChatTurnResponse>;
|
|
@@ -4,11 +4,14 @@ import { analyzeDependencies } from "../../product/deps/analyze.js";
|
|
|
4
4
|
import { buildIntelligenceGraph } from "../../intelligence/graph/build.js";
|
|
5
5
|
import { resolveRepoRoot } from "../../utils/path.js";
|
|
6
6
|
import { retrieveProjectContext } from "../context/retrieve.js";
|
|
7
|
+
import { extractQueryTerms } from "../context/brain-evidence.js";
|
|
7
8
|
import { buildChatTurnResponse } from "./response.js";
|
|
9
|
+
import { noopAskProgress } from "../progress.js";
|
|
8
10
|
const ARCH_RE = /architecture|structure|module|layout|what (is|does) (this|my) project|project overview|understand (my |this )?project/i;
|
|
9
11
|
const AUTH_RE = /auth|login|password|session|jwt|oauth/i;
|
|
10
12
|
const DEPS_RE = /depend|package|lockfile|npm|yarn|pnpm/i;
|
|
11
13
|
const DB_RE = /database|db\.|sql|prisma|query\(/i;
|
|
14
|
+
const MODULE_RE = /\bmodule\b|\bcomponent\b|\bprovides?\b|\bbelong/i;
|
|
12
15
|
/** ReDoS-safe: avoid `where.*…` / `how.*…` backtracking on uncontrolled chat input. */
|
|
13
16
|
function isStartQuestion(q) {
|
|
14
17
|
if (/application start|entry\s*point/i.test(q))
|
|
@@ -28,14 +31,42 @@ function isArchitectureQuestion(q) {
|
|
|
28
31
|
return true;
|
|
29
32
|
return false;
|
|
30
33
|
}
|
|
34
|
+
function isModuleQuestion(q) {
|
|
35
|
+
return MODULE_RE.test(q) || extractQueryTerms(q).length > 0;
|
|
36
|
+
}
|
|
31
37
|
export async function answerDeterministicProjectQuestion(options) {
|
|
32
38
|
const root = resolveRepoRoot(options.root);
|
|
33
39
|
const q = options.question.trim();
|
|
40
|
+
const progress = options.progress ?? noopAskProgress;
|
|
41
|
+
progress.stage("understand", "start");
|
|
42
|
+
const terms = extractQueryTerms(q);
|
|
43
|
+
progress.stage("understand", "ok", terms.length ? `focus: ${terms.slice(0, 4).join(", ")}` : undefined);
|
|
34
44
|
const context = await retrieveProjectContext({
|
|
35
45
|
root,
|
|
36
46
|
query: q,
|
|
37
47
|
budgetTokens: 4_000,
|
|
48
|
+
maxSourceFiles: 8,
|
|
49
|
+
progress,
|
|
38
50
|
});
|
|
51
|
+
progress.stage("prepare", "start");
|
|
52
|
+
try {
|
|
53
|
+
const response = await buildDeterministicAnswer({
|
|
54
|
+
root,
|
|
55
|
+
q,
|
|
56
|
+
terms,
|
|
57
|
+
context,
|
|
58
|
+
sessionId: options.sessionId,
|
|
59
|
+
});
|
|
60
|
+
progress.stage("prepare", "ok");
|
|
61
|
+
return response;
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
progress.stage("prepare", "fail", error instanceof Error ? error.message : String(error));
|
|
65
|
+
throw error;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
async function buildDeterministicAnswer(options) {
|
|
69
|
+
const { root, q, terms, context, sessionId } = options;
|
|
39
70
|
const sections = [
|
|
40
71
|
"Deterministic project answer (no LLM). Evidence from DNA, graph, brain, and dependency analyzers.",
|
|
41
72
|
"",
|
|
@@ -46,6 +77,27 @@ export async function answerDeterministicProjectQuestion(options) {
|
|
|
46
77
|
sections.push(`Frameworks: ${dna.frameworks.join(", ") || "UNKNOWN"}`);
|
|
47
78
|
sections.push(`Monorepo: ${dna.monorepo.isMonorepo ? dna.monorepo.tool : "no"}`);
|
|
48
79
|
sections.push("");
|
|
80
|
+
const brainCitations = context.citations.filter((c) => c.source === "brain" || c.evidenceType === "project-brain");
|
|
81
|
+
const sourceCitations = context.citations.filter((c) => c.confidence === "VERIFIED" && c.evidenceType === "source-code" && c.path);
|
|
82
|
+
if (brainCitations.length > 0 && (isModuleQuestion(q) || isArchitectureQuestion(q))) {
|
|
83
|
+
const focus = terms.length ? terms.join(", ") : "project";
|
|
84
|
+
sections.push(`[INFERRED] Project Brain evidence for: ${focus}`);
|
|
85
|
+
for (const c of brainCitations.slice(0, 14)) {
|
|
86
|
+
sections.push(` - ${c.excerpt ?? c.note ?? "brain claim"}`);
|
|
87
|
+
}
|
|
88
|
+
sections.push("");
|
|
89
|
+
if (sourceCitations.length) {
|
|
90
|
+
sections.push("[VERIFIED] Owned source paths linked from Brain / retrieval:");
|
|
91
|
+
for (const c of sourceCitations.slice(0, 10)) {
|
|
92
|
+
sections.push(` - ${c.path}`);
|
|
93
|
+
}
|
|
94
|
+
sections.push("");
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
sections.push("[INFERRED] No owned source excerpts resolved for these Brain claims; structural evidence only.");
|
|
98
|
+
sections.push("");
|
|
99
|
+
}
|
|
100
|
+
}
|
|
49
101
|
if (AUTH_RE.test(q)) {
|
|
50
102
|
const authFiles = context.citations
|
|
51
103
|
.filter((c) => c.path && /auth|login|session|password/i.test(c.path))
|
|
@@ -157,11 +209,28 @@ export async function answerDeterministicProjectQuestion(options) {
|
|
|
157
209
|
sections.push(`Lockfiles: ${deps.lockfilesPresent.join(", ") || "none"}`);
|
|
158
210
|
sections.push("");
|
|
159
211
|
}
|
|
160
|
-
if (isArchitectureQuestion(q)) {
|
|
212
|
+
if (isArchitectureQuestion(q) && brainCitations.length === 0) {
|
|
161
213
|
try {
|
|
162
214
|
const graph = await buildIntelligenceGraph({ root, mode: "auto" });
|
|
163
215
|
sections.push(`[INFERRED] Graph: ${graph.nodes.length} nodes, ${graph.edges.length} edges (${graph.builder})`);
|
|
164
|
-
const
|
|
216
|
+
const termLower = terms.map((t) => t.toLowerCase());
|
|
217
|
+
const filtered = graph.nodes
|
|
218
|
+
.filter((n) => n.path && n.label)
|
|
219
|
+
.filter((n) => {
|
|
220
|
+
if (/\.min\.js$/i.test(n.path) || /chart\.umd|node_modules/i.test(n.path))
|
|
221
|
+
return false;
|
|
222
|
+
if (termLower.length === 0)
|
|
223
|
+
return n.kind === "file" || n.kind === "module";
|
|
224
|
+
const blob = `${n.label} ${n.path}`.toLowerCase();
|
|
225
|
+
return termLower.some((t) => blob.includes(t));
|
|
226
|
+
})
|
|
227
|
+
.slice(0, 8);
|
|
228
|
+
const sample = filtered.length
|
|
229
|
+
? filtered
|
|
230
|
+
: graph.nodes
|
|
231
|
+
.filter((n) => n.path && !/\.min\.js$/i.test(n.path))
|
|
232
|
+
.filter((n) => n.kind === "file")
|
|
233
|
+
.slice(0, 6);
|
|
165
234
|
for (const n of sample) {
|
|
166
235
|
sections.push(` - ${n.kind}: ${n.label}${n.path ? ` @ ${n.path}` : ""}`);
|
|
167
236
|
}
|
|
@@ -171,6 +240,17 @@ export async function answerDeterministicProjectQuestion(options) {
|
|
|
171
240
|
}
|
|
172
241
|
sections.push("");
|
|
173
242
|
}
|
|
243
|
+
else if (isArchitectureQuestion(q)) {
|
|
244
|
+
// Brain already rendered; add a short graph filter if present in context.
|
|
245
|
+
const graphCite = context.citations.find((c) => c.source === "graph");
|
|
246
|
+
if (graphCite?.excerpt) {
|
|
247
|
+
sections.push("[INFERRED] Query-filtered graph nodes:");
|
|
248
|
+
for (const line of graphCite.excerpt.split("\n").slice(0, 6)) {
|
|
249
|
+
sections.push(` - ${line}`);
|
|
250
|
+
}
|
|
251
|
+
sections.push("");
|
|
252
|
+
}
|
|
253
|
+
}
|
|
174
254
|
const brain = await getBrainStatus(root);
|
|
175
255
|
if (brain.hasSnapshot) {
|
|
176
256
|
sections.push(`[INFERRED] Project brain snapshot present (${brain.snapshotCount} stored; latest ${brain.latestSnapshotId ?? "unknown"}).`);
|
|
@@ -178,6 +258,14 @@ export async function answerDeterministicProjectQuestion(options) {
|
|
|
178
258
|
else {
|
|
179
259
|
sections.push("[UNKNOWN] No project brain snapshot — run brain compile for richer answers.");
|
|
180
260
|
}
|
|
261
|
+
if (brainCitations.length === 0 &&
|
|
262
|
+
sourceCitations.length === 0 &&
|
|
263
|
+
!AUTH_RE.test(q) &&
|
|
264
|
+
!DEPS_RE.test(q) &&
|
|
265
|
+
!isArchitectureQuestion(q)) {
|
|
266
|
+
sections.push("");
|
|
267
|
+
sections.push("[UNKNOWN] Insufficient Brain/source evidence for this question — try a more specific module or path name.");
|
|
268
|
+
}
|
|
181
269
|
sections.push("");
|
|
182
270
|
sections.push("Limitations: deterministic answers cannot invent behavior not present in evidence.");
|
|
183
271
|
const bundle = {
|
|
@@ -188,7 +276,7 @@ export async function answerDeterministicProjectQuestion(options) {
|
|
|
188
276
|
],
|
|
189
277
|
};
|
|
190
278
|
return buildChatTurnResponse({
|
|
191
|
-
sessionId
|
|
279
|
+
sessionId,
|
|
192
280
|
modelText: sections.join("\n"),
|
|
193
281
|
context: bundle,
|
|
194
282
|
provider: "deterministic",
|
|
@@ -4,8 +4,12 @@ import { truthLabelHelp } from "../context/truth.js";
|
|
|
4
4
|
* Never invents line ranges. Citations come only from the context bundle.
|
|
5
5
|
*/
|
|
6
6
|
export function buildChatTurnResponse(options) {
|
|
7
|
-
const
|
|
8
|
-
const
|
|
7
|
+
const verifiedSource = options.context.citations.filter((c) => c.path && c.confidence === "VERIFIED" && c.evidenceType === "source-code");
|
|
8
|
+
const brainCitations = options.context.citations.filter((c) => c.source === "brain" || c.evidenceType === "project-brain");
|
|
9
|
+
const graphCitations = options.context.citations.filter((c) => c.source === "graph" || c.evidenceType === "graph");
|
|
10
|
+
// Surface verified source + brain/graph evidence (brain is never upgraded to VERIFIED).
|
|
11
|
+
const citations = [...verifiedSource, ...brainCitations, ...graphCitations];
|
|
12
|
+
const contextPaths = verifiedSource.map((c) => c.path).filter(Boolean);
|
|
9
13
|
const mentioned = extractMentionedPaths(options.modelText, contextPaths);
|
|
10
14
|
const truthClaims = [];
|
|
11
15
|
for (const path of mentioned) {
|
|
@@ -15,13 +19,21 @@ export function buildChatTurnResponse(options) {
|
|
|
15
19
|
citationPaths: [path],
|
|
16
20
|
});
|
|
17
21
|
}
|
|
18
|
-
|
|
22
|
+
const hasUsableEvidence = verifiedSource.length > 0 || brainCitations.length > 0;
|
|
23
|
+
if (!hasUsableEvidence) {
|
|
19
24
|
truthClaims.push({
|
|
20
25
|
text: "Insufficient repository evidence was retrieved for this question.",
|
|
21
26
|
label: "UNKNOWN",
|
|
22
27
|
citationPaths: [],
|
|
23
28
|
});
|
|
24
29
|
}
|
|
30
|
+
else if (brainCitations.length > 0 && verifiedSource.length === 0) {
|
|
31
|
+
truthClaims.push({
|
|
32
|
+
text: "Answer grounded in Project Brain structural evidence (INFERRED; no owned source excerpts).",
|
|
33
|
+
label: "INFERRED",
|
|
34
|
+
citationPaths: [],
|
|
35
|
+
});
|
|
36
|
+
}
|
|
25
37
|
else if (options.modelText.trim()) {
|
|
26
38
|
truthClaims.push({
|
|
27
39
|
text: "Interpretive explanation based on retrieved project evidence.",
|
|
@@ -83,7 +95,16 @@ export function formatChatResponseForCli(response) {
|
|
|
83
95
|
}
|
|
84
96
|
if (response.citations.length > 0) {
|
|
85
97
|
lines.push("Evidence:");
|
|
86
|
-
for (const c of response.citations.slice(0,
|
|
98
|
+
for (const c of response.citations.slice(0, 16)) {
|
|
99
|
+
if (c.source === "brain" || c.evidenceType === "project-brain") {
|
|
100
|
+
lines.push(` - ${c.excerpt ?? c.note ?? "brain claim"} (${c.confidence})`);
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
if (c.source === "graph" || c.evidenceType === "graph") {
|
|
104
|
+
const first = (c.excerpt ?? c.note ?? "graph").split("\n")[0] ?? "graph";
|
|
105
|
+
lines.push(` - ${first} (${c.confidence})`);
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
87
108
|
const path = c.path ?? "(unknown)";
|
|
88
109
|
lines.push(` - ${path}${c.range ? `:${c.range}` : ""} (${c.confidence})`);
|
|
89
110
|
}
|
|
@@ -9,6 +9,7 @@ import { PROJECT_CHAT_SYSTEM_PROMPT, wrapProjectData } from "./prompts.js";
|
|
|
9
9
|
import { formatProjectSummary, summarizeProjectForChat, } from "./project-summary.js";
|
|
10
10
|
import { buildChatTurnResponse, formatChatResponseForCli } from "./response.js";
|
|
11
11
|
import { answerDeterministicProjectQuestion } from "./deterministic.js";
|
|
12
|
+
import { noopAskProgress } from "../progress.js";
|
|
12
13
|
export const CHAT_PROVIDER_NONE_MESSAGE = `AI chat is not configured.
|
|
13
14
|
|
|
14
15
|
Configure an AI provider to use AgentDoctor Project Chat.
|
|
@@ -28,6 +29,7 @@ export class ChatService {
|
|
|
28
29
|
budgetTokens;
|
|
29
30
|
persistAudit;
|
|
30
31
|
systemPrompt;
|
|
32
|
+
progress;
|
|
31
33
|
auditSession;
|
|
32
34
|
graphCache;
|
|
33
35
|
lastContext;
|
|
@@ -37,6 +39,7 @@ export class ChatService {
|
|
|
37
39
|
this.provider = options.provider ?? createModelProvider(loadAiConfig());
|
|
38
40
|
this.budgetTokens = options.budgetTokens ?? 6_000;
|
|
39
41
|
this.persistAudit = options.persistAudit !== false;
|
|
42
|
+
this.progress = options.progress ?? noopAskProgress;
|
|
40
43
|
this.systemPrompt = options.systemPromptAddon
|
|
41
44
|
? `${PROJECT_CHAT_SYSTEM_PROMPT}\n\n${options.systemPromptAddon}`
|
|
42
45
|
: PROJECT_CHAT_SYSTEM_PROMPT;
|
|
@@ -104,27 +107,40 @@ export class ChatService {
|
|
|
104
107
|
root: this.root,
|
|
105
108
|
question: safeUser,
|
|
106
109
|
sessionId: this.sessionId,
|
|
110
|
+
progress: this.progress,
|
|
107
111
|
});
|
|
112
|
+
this.lastContext = undefined;
|
|
108
113
|
this.memory.addAssistant(response.message, response.contextPaths);
|
|
109
114
|
await this.audit("prompt", "CHAT_DETERMINISTIC", { status: response.status });
|
|
110
115
|
return response;
|
|
111
116
|
}
|
|
117
|
+
this.progress.stage("understand", "start");
|
|
112
118
|
const query = this.memory.resolveQuery(safeUser);
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
119
|
+
this.progress.stage("understand", "ok");
|
|
120
|
+
let context;
|
|
121
|
+
try {
|
|
122
|
+
context = await retrieveProjectContext({
|
|
123
|
+
root: this.root,
|
|
124
|
+
query,
|
|
125
|
+
budgetTokens: this.budgetTokens,
|
|
126
|
+
includePaths: [
|
|
127
|
+
...this.memory.getReferencedPaths().slice(-8),
|
|
128
|
+
...this.memory.getLastContextPaths().slice(-8),
|
|
129
|
+
],
|
|
130
|
+
...(this.graphCache ? { graph: this.graphCache } : {}),
|
|
131
|
+
progress: this.progress,
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
catch (error) {
|
|
135
|
+
this.progress.stop();
|
|
136
|
+
throw error;
|
|
137
|
+
}
|
|
123
138
|
this.lastContext = context;
|
|
124
139
|
await this.audit("file-read", "CHAT_CONTEXT_RETRIEVED", {
|
|
125
140
|
citations: String(context.citations.length),
|
|
126
141
|
tokens: String(context.estimatedTokens),
|
|
127
142
|
});
|
|
143
|
+
this.progress.stage("prepare", "start");
|
|
128
144
|
const history = this.memory.historyForModel().slice(0, -1); // exclude current user (added separately)
|
|
129
145
|
const messages = [
|
|
130
146
|
{ role: "system", content: this.systemPrompt },
|
|
@@ -142,6 +158,7 @@ export class ChatService {
|
|
|
142
158
|
maxTokens: 2048,
|
|
143
159
|
});
|
|
144
160
|
if (modelResult.error) {
|
|
161
|
+
this.progress.stage("prepare", "fail", sanitizeProviderError(modelResult.error));
|
|
145
162
|
const response = buildChatTurnResponse({
|
|
146
163
|
sessionId: this.sessionId,
|
|
147
164
|
modelText: `AgentDoctor could not reach the configured AI provider.\n\nProvider: ${modelResult.provider}\nModel: ${modelResult.model}\nError: ${sanitizeProviderError(modelResult.error)}`,
|
|
@@ -158,6 +175,7 @@ export class ChatService {
|
|
|
158
175
|
});
|
|
159
176
|
return response;
|
|
160
177
|
}
|
|
178
|
+
this.progress.stage("prepare", "ok");
|
|
161
179
|
const text = redactForModel(modelResult.message.content || "");
|
|
162
180
|
const response = buildChatTurnResponse({
|
|
163
181
|
sessionId: this.sessionId,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { TruthLabel, ContextCitation } from "../context/types.js";
|
|
2
2
|
import type { AiProviderId, ModelProvider, TokenUsage } from "../../ai/types.js";
|
|
3
|
+
import type { AskProgressReporter } from "../progress.js";
|
|
3
4
|
export type ChatRole = "system" | "user" | "assistant";
|
|
4
5
|
export interface ChatMessage {
|
|
5
6
|
role: ChatRole;
|
|
@@ -45,4 +46,6 @@ export interface ChatServiceOptions {
|
|
|
45
46
|
persistAudit?: boolean;
|
|
46
47
|
/** Appended to PROJECT_CHAT_SYSTEM_PROMPT (modes) */
|
|
47
48
|
systemPromptAddon?: string;
|
|
49
|
+
/** Optional ask progress reporter (CLI TTY UX). */
|
|
50
|
+
progress?: AskProgressReporter;
|
|
48
51
|
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { type BrainSearchHit } from "../../core/brain-cli/service.js";
|
|
2
|
+
import type { ProjectBrain } from "../../core/understanding/brain/index.js";
|
|
3
|
+
import type { ContextCitation } from "./types.js";
|
|
4
|
+
/**
|
|
5
|
+
* AgentDoctor control-plane under `.agentdoctor/**` — usable internally,
|
|
6
|
+
* never as ordinary VERIFIED application source evidence.
|
|
7
|
+
*/
|
|
8
|
+
export declare function isControlPlaneRelativePath(relativePath: string): boolean;
|
|
9
|
+
/** Significant tokens from a natural-language project question. */
|
|
10
|
+
export declare function extractQueryTerms(query: string): string[];
|
|
11
|
+
export interface BrainEvidencePack {
|
|
12
|
+
brain: ProjectBrain | null;
|
|
13
|
+
hits: BrainSearchHit[];
|
|
14
|
+
citations: ContextCitation[];
|
|
15
|
+
/** Owned-looking source paths derived from claims/components/evidence (not yet ownership-gated). */
|
|
16
|
+
candidateSourcePaths: string[];
|
|
17
|
+
limitations: string[];
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Search the latest usable Project Brain for query-relevant claims/components.
|
|
21
|
+
* Brain hits are INFERRED structural evidence — never auto-upgraded to VERIFIED.
|
|
22
|
+
* Stale ownership-boundary brains load as null (same as CLI brain status).
|
|
23
|
+
*/
|
|
24
|
+
export declare function collectBrainEvidenceForQuery(root: string, query: string, options?: {
|
|
25
|
+
maxHits?: number;
|
|
26
|
+
}): Promise<BrainEvidencePack>;
|