@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
@@ -21,6 +21,9 @@ import { CONTRACTS_VERSION } from "../contracts/index.js";
21
21
  import { collectOpsHealth } from "../ops/health.js";
22
22
  import { listPolicyPacks } from "../policy/packs.js";
23
23
  import { sanitizeForOutput } from "../utils/path.js";
24
+ import { createModelProvider, loadAiConfig } from "../ai/index.js";
25
+ import { ChatService } from "../agent/chat/service.js";
26
+ import { formatChatResponseForCli } from "../agent/chat/response.js";
24
27
  function safeJsonError(error) {
25
28
  const raw = error instanceof Error ? error.message : String(error);
26
29
  const { text } = redactSecrets(sanitizeForOutput(raw));
@@ -92,7 +95,7 @@ function htmlPage() {
92
95
  <header>
93
96
  <h1>AgentDoctor 2.0</h1>
94
97
  <p>Local read-only dashboard — Safety + Brain + intelligence. No cloud writes.</p>
95
- <p class="notice muted">Action Policy Evaluator is evaluate-only. Local ?user= is not authentication. Team local-dev auth is separate and not SSO.</p>
98
+ <p class="notice muted">Action Policy Evaluator is evaluate-only. Query ?user= is a localDevIdentityHint only (not authentication). Team local-dev auth is separate and not SSO. OIDC JWT validation is a library path — full browser OAuth redirect is experimental.</p>
96
99
  </header>
97
100
  <main>
98
101
  <nav aria-label="Views" style="display:flex;flex-wrap:wrap;gap:0.75rem;font-size:0.9rem">
@@ -102,6 +105,7 @@ function htmlPage() {
102
105
  <a href="#brain">Brain</a>
103
106
  <a href="#graph">Graph</a>
104
107
  <a href="#knowledge">Knowledge</a>
108
+ <a href="#chat">Project Chat</a>
105
109
  <a href="#meta">Audit / baselines</a>
106
110
  </nav>
107
111
  <section id="overview">
@@ -128,6 +132,21 @@ function htmlPage() {
128
132
  <h2>Governed knowledge</h2>
129
133
  <pre id="knowledgeBody" class="muted">Loading…</pre>
130
134
  </section>
135
+ <section id="chat">
136
+ <h2>Project Chat</h2>
137
+ <p class="muted">Ask about this repository. Answers use evidence + truth labels. No file writes from this panel.</p>
138
+ <div style="display:grid;grid-template-columns:1.4fr 1fr;gap:1rem">
139
+ <div>
140
+ <textarea id="chatInput" rows="3" style="width:100%;background:#0d1520;color:#e8eef5;border:1px solid #243040;border-radius:8px;padding:0.6rem" placeholder="How does authentication work?"></textarea>
141
+ <button id="chatAsk" style="margin-top:0.5rem;background:var(--accent);color:#041018;border:0;border-radius:6px;padding:0.45rem 0.9rem;font-weight:600;cursor:pointer">Ask</button>
142
+ <pre id="chatBody" class="muted" style="margin-top:0.75rem">Ask a question…</pre>
143
+ </div>
144
+ <div>
145
+ <h3 style="margin:0 0 0.5rem;font-size:0.9rem">Evidence</h3>
146
+ <pre id="chatEvidence" class="muted">—</pre>
147
+ </div>
148
+ </div>
149
+ </section>
131
150
  <section id="meta">
132
151
  <h2>Safe Fix audit / baselines / sessions</h2>
133
152
  <pre id="metaBody" class="muted">Loading…</pre>
@@ -155,6 +174,28 @@ function htmlPage() {
155
174
  load().catch(err => {
156
175
  document.getElementById('status').textContent = String(err);
157
176
  });
177
+ document.getElementById('chatAsk').addEventListener('click', async () => {
178
+ const question = document.getElementById('chatInput').value.trim();
179
+ if (!question) return;
180
+ document.getElementById('chatBody').textContent = 'Thinking…';
181
+ try {
182
+ const res = await fetch('/api/chat', {
183
+ method: 'POST',
184
+ headers: { 'Content-Type': 'application/json' },
185
+ body: JSON.stringify({ question }),
186
+ });
187
+ const data = await res.json();
188
+ document.getElementById('chatBody').textContent = data.message || JSON.stringify(data, null, 2);
189
+ document.getElementById('chatEvidence').textContent = JSON.stringify({
190
+ truthClaims: data.truthClaims || [],
191
+ citations: data.citations || [],
192
+ limitations: data.limitations || [],
193
+ status: data.status,
194
+ }, null, 2);
195
+ } catch (err) {
196
+ document.getElementById('chatBody').textContent = String(err);
197
+ }
198
+ });
158
199
  </script>
159
200
  </body>
160
201
  </html>`;
@@ -186,8 +227,55 @@ export async function startDashboardServer(options) {
186
227
  return;
187
228
  }
188
229
  const url = new URL(rawUrl, `http://${host}:${port}`);
230
+ if (req.method === "POST" && url.pathname === "/api/chat") {
231
+ const chunks = [];
232
+ for await (const chunk of req) {
233
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
234
+ }
235
+ let body = {};
236
+ try {
237
+ body = JSON.parse(Buffer.concat(chunks).toString("utf8") || "{}");
238
+ }
239
+ catch {
240
+ sendJson(res, 400, { error: "invalid JSON body" });
241
+ return;
242
+ }
243
+ const question = typeof body.question === "string" ? body.question.trim() : "";
244
+ if (!question) {
245
+ sendJson(res, 400, { error: "question required" });
246
+ return;
247
+ }
248
+ const provider = options.chatProvider ?? createModelProvider(loadAiConfig());
249
+ if (provider.id === "none") {
250
+ sendJson(res, 503, {
251
+ error: "provider-none",
252
+ message: "AI chat is not configured. Set AGENTDOCTOR_AI_PROVIDER to mock or an OpenAI-compatible provider. Silent mock fallback is disabled.",
253
+ status: "provider-none",
254
+ });
255
+ return;
256
+ }
257
+ const chat = new ChatService({
258
+ root,
259
+ provider,
260
+ persistAudit: false,
261
+ });
262
+ try {
263
+ const response = await chat.ask(question);
264
+ sendJson(res, response.status === "ok" ? 200 : 502, {
265
+ ...response,
266
+ cliPreview: formatChatResponseForCli(response),
267
+ note: "Dashboard chat is ask-only; it does not edit files or run commands.",
268
+ });
269
+ }
270
+ finally {
271
+ await chat.end();
272
+ }
273
+ return;
274
+ }
189
275
  if (req.method !== "GET") {
190
- sendJson(res, 405, { error: "read-only dashboard; GET only" });
276
+ sendJson(res, 405, {
277
+ error: "dashboard is read-only except POST /api/chat (ask-only; no repo writes)",
278
+ });
191
279
  return;
192
280
  }
193
281
  if (pathnameLooksHostile(url.pathname)) {
@@ -208,7 +296,7 @@ export async function startDashboardServer(options) {
208
296
  host,
209
297
  loopbackOnly: isLoopbackHost(host),
210
298
  contractsVersion: CONTRACTS_VERSION,
211
- roleNote: "Local ?user= role selection is not authentication",
299
+ roleNote: "Query ?user= is a localDevIdentityHint only — not authentication. Privileged role elevation requires AGENTDOCTOR_ALLOW_LOCAL_IDENTITY_HINT=1",
212
300
  actionPolicyNote: EVALUATE_ONLY,
213
301
  adapters: agentRegistry.map((a) => a.id),
214
302
  policyPacks: listPolicyPacks(),
@@ -259,9 +347,20 @@ export async function startDashboardServer(options) {
259
347
  }
260
348
  if (url.pathname === "/api/platform") {
261
349
  const auth = await loadLocalAuthConfig(root);
262
- const role = resolveRole(auth, url.searchParams.get("user") ?? undefined);
350
+ const identityHint = url.searchParams.get("user");
351
+ const allowLocalHint = process.env.AGENTDOCTOR_ALLOW_LOCAL_IDENTITY_HINT === "1";
352
+ // Default: deny privileged elevation. ?user= is a label only unless opt-in env is set.
353
+ let role = auth.defaultRole;
354
+ const localDevIdentityHint = identityHint && identityHint.trim() ? identityHint.trim() : null;
355
+ if (localDevIdentityHint && allowLocalHint) {
356
+ role = resolveRole(auth, localDevIdentityHint);
357
+ }
263
358
  if (!canAccess(role, "read-findings")) {
264
- sendJson(res, 403, { error: "forbidden" });
359
+ sendJson(res, 403, {
360
+ error: "forbidden",
361
+ localDevIdentityHint,
362
+ notice: "?user= is not authentication; privileged ops denied without AGENTDOCTOR_ALLOW_LOCAL_IDENTITY_HINT=1",
363
+ });
265
364
  return;
266
365
  }
267
366
  const snapshot = await readJsonIfExists(path.join(platformDir(root), "snapshots", "latest.json"));
@@ -271,6 +370,8 @@ export async function startDashboardServer(options) {
271
370
  : [];
272
371
  sendJson(res, 200, {
273
372
  role,
373
+ localDevIdentityHint,
374
+ localIdentityHintElevated: Boolean(localDevIdentityHint && allowLocalHint),
274
375
  hasSnapshot: snapshot !== null,
275
376
  findingCount: snapshot?.findings.length ?? 0,
276
377
  graphNodes: snapshot?.graph.nodes.length ?? 0,
@@ -295,7 +396,10 @@ export async function startDashboardServer(options) {
295
396
  ...(snapshot?.limitations ?? [
296
397
  "No platform snapshot yet — run: agentdoctor platform scan",
297
398
  ]),
298
- "Local ?user= role selection is not authentication — spoofable on shared hosts",
399
+ "?user= is localDevIdentityHint only — not authentication",
400
+ allowLocalHint
401
+ ? "AGENTDOCTOR_ALLOW_LOCAL_IDENTITY_HINT=1 enabled — hint may elevate local roles (still not SSO)"
402
+ : "Privileged role elevation from ?user= denied unless AGENTDOCTOR_ALLOW_LOCAL_IDENTITY_HINT=1",
299
403
  EVALUATE_ONLY,
300
404
  ],
301
405
  sampleFindings: canExport
@@ -1,19 +1,45 @@
1
1
  import type { PolicyDecisionContract } from "../contracts/index.js";
2
+ /**
3
+ * Map argv to a portable executable form for shell:false execution.
4
+ * Policy evaluation still uses the original human-facing argv/command string.
5
+ */
6
+ export declare function resolveExecutableArgv(argv: string[]): string[];
7
+ export type ExecutionStatus = "executed" | "blocked-by-enforcement" | "not-executed" | "execution-failed";
2
8
  export interface EnforcementResult {
3
9
  decision: PolicyDecisionContract;
4
10
  enforced: boolean;
5
11
  notice: string;
12
+ stdout?: string;
13
+ stderr?: string;
14
+ exitCode?: number | null;
15
+ durationMs?: number;
6
16
  }
17
+ /**
18
+ * Parse a command string into argv without invoking a shell.
19
+ * Supports simple double/single quotes; rejects unclosed quotes.
20
+ */
21
+ export declare function parseArgv(command: string): string[];
22
+ export declare function buildAllowlistedEnv(source?: NodeJS.ProcessEnv): NodeJS.ProcessEnv;
23
+ /**
24
+ * Reject path-like args that resolve outside the repository root.
25
+ */
26
+ export declare function assertArgsInsideRoot(args: string[], root: string): void;
7
27
  /**
8
28
  * AgentDoctor-controlled command runner.
9
- * Only this boundary can mark execution as blocked-by-enforcement when it refuses to run.
29
+ * Executes only when executeIfAllowed===true AND policy decision===allow.
30
+ * Default path uses shell:false with argv parsing (no shell metacharacters).
10
31
  */
11
32
  export declare function runControlledCommand(options: {
12
33
  root: string;
13
- command: string;
34
+ command?: string;
35
+ argv?: string[];
14
36
  failClosed?: boolean;
15
- /** When true, actually attempt execution only if policy allows — still default false. */
37
+ /** When true, execute only if policy allows. */
16
38
  executeIfAllowed?: boolean;
39
+ /** Explicit high-risk shell mode — requires policy allow as well. */
40
+ useShell?: boolean;
41
+ timeoutMs?: number;
42
+ maxBuffer?: number;
17
43
  }): Promise<EnforcementResult>;
18
44
  /** Append-only tamper-evident audit chain (HMAC over previous hash). */
19
45
  export declare function appendAuditChain(previousHash: string | null, record: unknown, secret?: string): {
@@ -21,3 +47,26 @@ export declare function appendAuditChain(previousHash: string | null, record: un
21
47
  payload: string;
22
48
  };
23
49
  export declare function auditRecordId(parts: string): string;
50
+ /**
51
+ * Explain what the controlled runner would do for a command (evaluate-only).
52
+ * Does not execute.
53
+ */
54
+ export declare function explainControlledRun(options: {
55
+ root: string;
56
+ command?: string;
57
+ argv?: string[];
58
+ failClosed?: boolean;
59
+ useShell?: boolean;
60
+ }): Promise<{
61
+ command: string;
62
+ argv: string[];
63
+ useShell: boolean;
64
+ decision: string;
65
+ reason: string;
66
+ policyId: string | null;
67
+ riskLevel: string;
68
+ wouldExecute: boolean;
69
+ executionResult: "not-executed";
70
+ notice: string;
71
+ meaning: string;
72
+ }>;