@corbat-tech/coco 2.35.0 → 2.36.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.
- package/README.md +7 -0
- package/dist/cli/index.js +485 -109
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +1268 -1
- package/dist/index.js +2213 -241
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { Logger } from 'tslog';
|
|
2
2
|
import * as fs4 from 'fs';
|
|
3
|
-
import fs4__default, { readFileSync, constants } from 'fs';
|
|
3
|
+
import fs4__default, { readFileSync, mkdirSync, appendFileSync, writeFileSync, constants } from 'fs';
|
|
4
4
|
import * as path17 from 'path';
|
|
5
5
|
import path17__default, { dirname, join, basename, resolve } from 'path';
|
|
6
6
|
import * as fs16 from 'fs/promises';
|
|
7
|
-
import fs16__default, { access, readFile, readdir, writeFile, mkdir } from 'fs/promises';
|
|
7
|
+
import fs16__default, { access, readFile, readdir, writeFile, mkdir, rm } from 'fs/promises';
|
|
8
8
|
import { randomUUID, randomBytes, createHash } from 'crypto';
|
|
9
9
|
import * as http from 'http';
|
|
10
10
|
import { fileURLToPath, URL as URL$1 } from 'url';
|
|
@@ -23,7 +23,7 @@ import Anthropic from '@anthropic-ai/sdk';
|
|
|
23
23
|
import { jsonrepair } from 'jsonrepair';
|
|
24
24
|
import OpenAI from 'openai';
|
|
25
25
|
import { GoogleGenAI, FunctionCallingConfigMode } from '@google/genai';
|
|
26
|
-
import 'events';
|
|
26
|
+
import { EventEmitter } from 'events';
|
|
27
27
|
import 'minimatch';
|
|
28
28
|
import { simpleGit } from 'simple-git';
|
|
29
29
|
import { diffWords } from 'diff';
|
|
@@ -275,6 +275,10 @@ function getCatalogDefaultModel(provider) {
|
|
|
275
275
|
function getCatalogModel(provider, modelId) {
|
|
276
276
|
return getProviderCatalogEntry(provider).models.find((modelEntry) => modelEntry.id === modelId);
|
|
277
277
|
}
|
|
278
|
+
function getCatalogRecommendedModel(provider) {
|
|
279
|
+
const entry = getProviderCatalogEntry(provider);
|
|
280
|
+
return entry.models.find((modelEntry) => modelEntry.recommended) ?? entry.models[0];
|
|
281
|
+
}
|
|
278
282
|
function getCatalogContextWindow(provider, modelId, fallback) {
|
|
279
283
|
if (!modelId) return fallback;
|
|
280
284
|
const exact = getCatalogModel(provider, modelId);
|
|
@@ -10603,16 +10607,16 @@ var QualityEvaluator = class {
|
|
|
10603
10607
|
* Find source files in project, adapting to the detected language stack.
|
|
10604
10608
|
*/
|
|
10605
10609
|
async findSourceFiles() {
|
|
10606
|
-
const { access:
|
|
10607
|
-
const { join:
|
|
10610
|
+
const { access: access14 } = await import('fs/promises');
|
|
10611
|
+
const { join: join20 } = await import('path');
|
|
10608
10612
|
let isJava = false;
|
|
10609
10613
|
try {
|
|
10610
|
-
await
|
|
10614
|
+
await access14(join20(this.projectPath, "pom.xml"));
|
|
10611
10615
|
isJava = true;
|
|
10612
10616
|
} catch {
|
|
10613
10617
|
for (const f of ["build.gradle", "build.gradle.kts"]) {
|
|
10614
10618
|
try {
|
|
10615
|
-
await
|
|
10619
|
+
await access14(join20(this.projectPath, f));
|
|
10616
10620
|
isJava = true;
|
|
10617
10621
|
break;
|
|
10618
10622
|
} catch {
|
|
@@ -18747,6 +18751,89 @@ function createResilientProvider(provider, config) {
|
|
|
18747
18751
|
|
|
18748
18752
|
// src/providers/runtime-capabilities.ts
|
|
18749
18753
|
init_catalog();
|
|
18754
|
+
function hasCapability(model2, capability) {
|
|
18755
|
+
return model2?.capabilities.includes(capability) ?? false;
|
|
18756
|
+
}
|
|
18757
|
+
function selectEndpoint(provider, model2) {
|
|
18758
|
+
if (hasCapability(model2, "anthropic-messages")) return "anthropic-messages";
|
|
18759
|
+
if (hasCapability(model2, "gemini-generate-content")) return "gemini-generate-content";
|
|
18760
|
+
if (provider === "openai" || provider === "codex") {
|
|
18761
|
+
if (hasCapability(model2, "openai-responses")) return "openai-responses";
|
|
18762
|
+
}
|
|
18763
|
+
return "openai-chat";
|
|
18764
|
+
}
|
|
18765
|
+
function buildRestrictions(provider, model2, endpoint, supportsReasoning, supportsToolUse) {
|
|
18766
|
+
const restrictions = [];
|
|
18767
|
+
if (provider === "copilot" && endpoint === "openai-chat" && supportsToolUse) {
|
|
18768
|
+
restrictions.push(
|
|
18769
|
+
"Copilot uses an OpenAI-compatible Chat Completions route; Coco omits reasoning_effort on tool calls to avoid upstream 400 errors."
|
|
18770
|
+
);
|
|
18771
|
+
}
|
|
18772
|
+
if (provider !== "openai" && provider !== "codex" && endpoint === "openai-chat" && supportsReasoning) {
|
|
18773
|
+
restrictions.push(
|
|
18774
|
+
"OpenAI-compatible providers only receive reasoning fields when the provider is explicitly verified."
|
|
18775
|
+
);
|
|
18776
|
+
}
|
|
18777
|
+
if (!supportsToolUse) {
|
|
18778
|
+
restrictions.push("Function tools are not advertised for this model.");
|
|
18779
|
+
}
|
|
18780
|
+
if (model2.toLowerCase().includes("deprecated")) {
|
|
18781
|
+
restrictions.push(
|
|
18782
|
+
"Model name suggests deprecation; prefer a catalog current/recommended model."
|
|
18783
|
+
);
|
|
18784
|
+
}
|
|
18785
|
+
return restrictions;
|
|
18786
|
+
}
|
|
18787
|
+
function getProviderRuntimeCapability(provider, modelId) {
|
|
18788
|
+
const providerCatalog = getProviderCatalogEntry(provider);
|
|
18789
|
+
const model2 = modelId ?? providerCatalog.defaultModel;
|
|
18790
|
+
const catalogModel = getCatalogModel(provider, model2);
|
|
18791
|
+
const thinking = getThinkingCapability(provider, model2);
|
|
18792
|
+
const endpoint = selectEndpoint(provider, catalogModel);
|
|
18793
|
+
const supportsToolUse = hasCapability(catalogModel, "tool-use");
|
|
18794
|
+
const supportsReasoning = thinking.supported;
|
|
18795
|
+
return {
|
|
18796
|
+
provider,
|
|
18797
|
+
model: model2,
|
|
18798
|
+
catalogModel,
|
|
18799
|
+
status: catalogModel?.status ?? "unverified",
|
|
18800
|
+
endpoint,
|
|
18801
|
+
supportsStreaming: hasCapability(catalogModel, "streaming"),
|
|
18802
|
+
supportsToolUse,
|
|
18803
|
+
supportsVision: hasCapability(catalogModel, "vision"),
|
|
18804
|
+
supportsReasoning,
|
|
18805
|
+
reasoningKinds: thinking.kinds,
|
|
18806
|
+
defaultReasoning: thinking.defaultMode,
|
|
18807
|
+
contextWindow: catalogModel?.contextWindow ?? 0,
|
|
18808
|
+
maxOutputTokens: catalogModel?.maxOutputTokens,
|
|
18809
|
+
sourceUrl: catalogModel?.source.url,
|
|
18810
|
+
restrictions: buildRestrictions(provider, model2, endpoint, supportsReasoning, supportsToolUse)
|
|
18811
|
+
};
|
|
18812
|
+
}
|
|
18813
|
+
async function probeProviderRuntimeCapability(provider, modelId, checkAvailability) {
|
|
18814
|
+
const capability = getProviderRuntimeCapability(provider, modelId);
|
|
18815
|
+
if (!checkAvailability) {
|
|
18816
|
+
return {
|
|
18817
|
+
...capability,
|
|
18818
|
+
available: "not-checked",
|
|
18819
|
+
checkedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
18820
|
+
};
|
|
18821
|
+
}
|
|
18822
|
+
try {
|
|
18823
|
+
return {
|
|
18824
|
+
...capability,
|
|
18825
|
+
available: await checkAvailability(),
|
|
18826
|
+
checkedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
18827
|
+
};
|
|
18828
|
+
} catch (error) {
|
|
18829
|
+
return {
|
|
18830
|
+
...capability,
|
|
18831
|
+
available: false,
|
|
18832
|
+
checkedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
18833
|
+
error: error instanceof Error ? error.message : String(error)
|
|
18834
|
+
};
|
|
18835
|
+
}
|
|
18836
|
+
}
|
|
18750
18837
|
|
|
18751
18838
|
// src/providers/index.ts
|
|
18752
18839
|
init_copilot();
|
|
@@ -19346,151 +19433,1366 @@ z.string().regex(
|
|
|
19346
19433
|
/^\d+\.\d+\.\d+$/,
|
|
19347
19434
|
"Version must be in semver format (e.g., 1.0.0)"
|
|
19348
19435
|
);
|
|
19349
|
-
init_errors();
|
|
19350
|
-
init_allowed_paths();
|
|
19351
|
-
function levenshtein(a, b) {
|
|
19352
|
-
if (a === b) return 0;
|
|
19353
|
-
if (a.length === 0) return b.length;
|
|
19354
|
-
if (b.length === 0) return a.length;
|
|
19355
|
-
const row = Array.from({ length: b.length + 1 }, (_, i) => i);
|
|
19356
|
-
for (let i = 1; i <= a.length; i++) {
|
|
19357
|
-
let prev = i;
|
|
19358
|
-
for (let j = 1; j <= b.length; j++) {
|
|
19359
|
-
const cost = a[i - 1] === b[j - 1] ? 0 : 1;
|
|
19360
|
-
const val = Math.min(
|
|
19361
|
-
row[j] + 1,
|
|
19362
|
-
// deletion
|
|
19363
|
-
prev + 1,
|
|
19364
|
-
// insertion
|
|
19365
|
-
row[j - 1] + cost
|
|
19366
|
-
// substitution
|
|
19367
|
-
);
|
|
19368
|
-
row[j - 1] = prev;
|
|
19369
|
-
prev = val;
|
|
19370
|
-
}
|
|
19371
|
-
row[b.length] = prev;
|
|
19372
|
-
}
|
|
19373
|
-
return row[b.length];
|
|
19374
|
-
}
|
|
19375
19436
|
|
|
19376
|
-
// src/
|
|
19377
|
-
|
|
19378
|
-
|
|
19379
|
-
|
|
19380
|
-
|
|
19381
|
-
|
|
19382
|
-
|
|
19383
|
-
|
|
19384
|
-
|
|
19385
|
-
|
|
19386
|
-
|
|
19387
|
-
|
|
19388
|
-
|
|
19389
|
-
|
|
19390
|
-
|
|
19391
|
-
|
|
19392
|
-
|
|
19393
|
-
|
|
19394
|
-
|
|
19395
|
-
|
|
19396
|
-
|
|
19397
|
-
|
|
19398
|
-
|
|
19399
|
-
|
|
19400
|
-
|
|
19401
|
-
|
|
19402
|
-
|
|
19403
|
-
|
|
19404
|
-
|
|
19437
|
+
// src/cli/repl/agents/manager.ts
|
|
19438
|
+
init_logger();
|
|
19439
|
+
|
|
19440
|
+
// src/cli/repl/agents/prompts.ts
|
|
19441
|
+
var EXPLORE_PROMPT = `You are an exploration agent for Corbat-Coco.
|
|
19442
|
+
Your purpose is to search the codebase to answer questions and gather information.
|
|
19443
|
+
|
|
19444
|
+
Your capabilities:
|
|
19445
|
+
- Search for files using glob patterns
|
|
19446
|
+
- Read file contents to understand code structure
|
|
19447
|
+
- Search for text patterns across the codebase
|
|
19448
|
+
- List directory contents to understand project structure
|
|
19449
|
+
|
|
19450
|
+
When exploring:
|
|
19451
|
+
1. Start broad, then narrow down based on findings
|
|
19452
|
+
2. Look for patterns in naming conventions and file organization
|
|
19453
|
+
3. Read relevant files to understand implementations
|
|
19454
|
+
4. Summarize your findings clearly and concisely
|
|
19455
|
+
|
|
19456
|
+
Focus on gathering accurate information. Do not make changes to files.
|
|
19457
|
+
Report what you find with specific file paths and code references.`;
|
|
19458
|
+
var PLAN_PROMPT = `You are a planning agent for Corbat-Coco.
|
|
19459
|
+
Your purpose is to design implementation approaches and create detailed plans.
|
|
19460
|
+
|
|
19461
|
+
Your capabilities:
|
|
19462
|
+
- Read existing code to understand architecture
|
|
19463
|
+
- Search for related implementations and patterns
|
|
19464
|
+
- Analyze dependencies and relationships
|
|
19465
|
+
- Review documentation and comments
|
|
19466
|
+
|
|
19467
|
+
When planning:
|
|
19468
|
+
1. Understand the current state of the codebase
|
|
19469
|
+
2. Identify affected areas and dependencies
|
|
19470
|
+
3. Break down tasks into concrete steps
|
|
19471
|
+
4. Consider edge cases and potential issues
|
|
19472
|
+
5. Propose a clear implementation strategy
|
|
19473
|
+
|
|
19474
|
+
Output a structured plan with:
|
|
19475
|
+
- Overview of the approach
|
|
19476
|
+
- Step-by-step implementation tasks
|
|
19477
|
+
- Potential risks or considerations
|
|
19478
|
+
- Estimated complexity
|
|
19479
|
+
|
|
19480
|
+
Do not implement changes - only create the plan.`;
|
|
19481
|
+
var TEST_PROMPT = `You are a testing agent for Corbat-Coco.
|
|
19482
|
+
Your purpose is to write and run tests to ensure code quality.
|
|
19483
|
+
|
|
19484
|
+
Your capabilities:
|
|
19485
|
+
- Read source files to understand what needs testing
|
|
19486
|
+
- Write test files with comprehensive test cases
|
|
19487
|
+
- Run tests and analyze results
|
|
19488
|
+
- Check code coverage
|
|
19489
|
+
- Identify untested code paths
|
|
19490
|
+
|
|
19491
|
+
When testing:
|
|
19492
|
+
1. Understand the code being tested
|
|
19493
|
+
2. Identify test scenarios (happy path, edge cases, errors)
|
|
19494
|
+
3. Write clear, maintainable tests
|
|
19495
|
+
4. Run tests and verify they pass
|
|
19496
|
+
5. Check coverage and add tests for uncovered areas
|
|
19497
|
+
|
|
19498
|
+
Follow these testing principles:
|
|
19499
|
+
- One assertion per test when possible
|
|
19500
|
+
- Clear test names that describe the scenario
|
|
19501
|
+
- Proper setup and teardown
|
|
19502
|
+
- Mock external dependencies appropriately
|
|
19503
|
+
- Test behavior, not implementation details
|
|
19504
|
+
|
|
19505
|
+
Report test results clearly with pass/fail status and coverage metrics.`;
|
|
19506
|
+
var DEBUG_PROMPT = `You are a debugging agent for Corbat-Coco.
|
|
19507
|
+
Your purpose is to analyze errors, identify root causes, and fix issues.
|
|
19508
|
+
|
|
19509
|
+
Your capabilities:
|
|
19510
|
+
- Read error messages and stack traces
|
|
19511
|
+
- Search for related code and error handlers
|
|
19512
|
+
- Execute code to reproduce issues
|
|
19513
|
+
- Analyze logs and outputs
|
|
19514
|
+
- Make targeted fixes to resolve issues
|
|
19515
|
+
|
|
19516
|
+
When debugging:
|
|
19517
|
+
1. Understand the error symptoms completely
|
|
19518
|
+
2. Reproduce the issue if possible
|
|
19519
|
+
3. Trace the error to its source
|
|
19520
|
+
4. Identify the root cause (not just symptoms)
|
|
19521
|
+
5. Propose and implement a fix
|
|
19522
|
+
6. Verify the fix resolves the issue
|
|
19523
|
+
|
|
19524
|
+
Focus on:
|
|
19525
|
+
- Understanding the actual vs expected behavior
|
|
19526
|
+
- Checking input validation and edge cases
|
|
19527
|
+
- Looking for off-by-one errors, null references, type mismatches
|
|
19528
|
+
- Considering race conditions or async issues
|
|
19529
|
+
- Reviewing recent changes that might have introduced the bug
|
|
19530
|
+
|
|
19531
|
+
Provide a clear explanation of what caused the issue and how you fixed it.`;
|
|
19532
|
+
var REVIEW_PROMPT = `You are a code review agent for Corbat-Coco.
|
|
19533
|
+
Your purpose is to review code for quality, maintainability, and best practices.
|
|
19534
|
+
|
|
19535
|
+
Your capabilities:
|
|
19536
|
+
- Read source files and understand implementations
|
|
19537
|
+
- Search for coding patterns and conventions
|
|
19538
|
+
- Analyze code complexity and structure
|
|
19539
|
+
- Check for security issues and anti-patterns
|
|
19540
|
+
|
|
19541
|
+
When reviewing:
|
|
19542
|
+
1. Read the code thoroughly
|
|
19543
|
+
2. Check for correctness and logic errors
|
|
19544
|
+
3. Evaluate code style and consistency
|
|
19545
|
+
4. Identify potential bugs or edge cases
|
|
19546
|
+
5. Assess maintainability and readability
|
|
19547
|
+
6. Look for security vulnerabilities
|
|
19548
|
+
|
|
19549
|
+
Review criteria:
|
|
19550
|
+
- **Correctness**: Does the code do what it's supposed to?
|
|
19551
|
+
- **Clarity**: Is the code easy to understand?
|
|
19552
|
+
- **Efficiency**: Are there unnecessary computations or memory usage?
|
|
19553
|
+
- **Security**: Are there potential vulnerabilities?
|
|
19554
|
+
- **Testing**: Is the code testable? Are there tests?
|
|
19555
|
+
- **Documentation**: Are complex parts documented?
|
|
19556
|
+
|
|
19557
|
+
Provide specific, actionable feedback with code references.
|
|
19558
|
+
Prioritize issues by severity: critical > major > minor > suggestion.`;
|
|
19559
|
+
var ARCHITECT_PROMPT = `You are an architecture agent for Corbat-Coco.
|
|
19560
|
+
Your purpose is to design system architecture and evaluate architectural decisions.
|
|
19561
|
+
|
|
19562
|
+
Your capabilities:
|
|
19563
|
+
- Read source files to understand current architecture
|
|
19564
|
+
- Search for design patterns and structural boundaries
|
|
19565
|
+
- Review module dependencies and coupling
|
|
19566
|
+
- Analyze layer separation and interface design
|
|
19567
|
+
|
|
19568
|
+
When designing architecture:
|
|
19569
|
+
1. Understand the existing architecture and constraints
|
|
19570
|
+
2. Identify architectural concerns and trade-offs
|
|
19571
|
+
3. Propose designs aligned with corbat-coco patterns:
|
|
19572
|
+
- Tool Registry Pattern: register tools centrally, discover by name
|
|
19573
|
+
- Zod Config Pattern: all config validated with Zod schemas
|
|
19574
|
+
- Provider-Agnostic Pattern: abstract LLM providers behind interfaces
|
|
19575
|
+
- REPL Skill Pattern: skills as self-contained SKILL.md + handler
|
|
19576
|
+
- Phase Context Pattern: pass context through COCO phases immutably
|
|
19577
|
+
4. Document decisions as Architecture Decision Records (ADRs)
|
|
19578
|
+
|
|
19579
|
+
ADR format:
|
|
19580
|
+
- Title: short noun phrase
|
|
19581
|
+
- Status: proposed | accepted | deprecated | superseded
|
|
19582
|
+
- Context: forces at play
|
|
19583
|
+
- Decision: the chosen solution
|
|
19584
|
+
- Consequences: trade-offs accepted
|
|
19585
|
+
|
|
19586
|
+
Constraints for corbat-coco:
|
|
19587
|
+
- TypeScript ESM only \u2014 no CommonJS
|
|
19588
|
+
- Node.js 22+ runtime
|
|
19589
|
+
- Prefer functional patterns over classes
|
|
19590
|
+
- Files must stay under 500 LOC
|
|
19591
|
+
- All public APIs need JSDoc
|
|
19592
|
+
|
|
19593
|
+
Output a structured architectural analysis or ADR. Do not write implementation code.`;
|
|
19594
|
+
var SECURITY_PROMPT = `You are a security audit agent for Corbat-Coco.
|
|
19595
|
+
Your purpose is to identify security vulnerabilities and recommend fixes.
|
|
19596
|
+
|
|
19597
|
+
Your capabilities:
|
|
19598
|
+
- Read source files to analyze security posture
|
|
19599
|
+
- Search for dangerous patterns across the codebase
|
|
19600
|
+
- Run dependency vulnerability checks
|
|
19601
|
+
- Audit configuration for secrets exposure
|
|
19602
|
+
|
|
19603
|
+
Security checklist (OWASP Top 10 + corbat-coco specifics):
|
|
19604
|
+
|
|
19605
|
+
1. **Secrets exposure**: No API keys/tokens hardcoded or logged
|
|
19606
|
+
2. **Command injection**: Shell commands must use array args via execa, never template strings
|
|
19607
|
+
3. **Path traversal**: File paths must be validated and confined to safe directories
|
|
19608
|
+
4. **Injection (SQL/NoSQL)**: Parameterized queries only, never string concatenation
|
|
19609
|
+
5. **Input validation**: All external inputs validated with Zod schemas at boundaries
|
|
19610
|
+
6. **LLM output safety**: Treat LLM output as untrusted \u2014 sanitize before eval/exec
|
|
19611
|
+
7. **Dependency vulnerabilities**: Check for known CVEs with \`pnpm audit\`
|
|
19612
|
+
8. **Insecure deserialization**: Safe JSON parsing with error handling, no eval
|
|
19613
|
+
9. **Sensitive data logging**: No PII, tokens, or credentials in log output
|
|
19614
|
+
10. **Type safety**: No \`any\` that bypasses security-relevant checks
|
|
19615
|
+
|
|
19616
|
+
When auditing:
|
|
19617
|
+
1. Search for common dangerous patterns (exec, eval, dangerouslySet, etc.)
|
|
19618
|
+
2. Check all environment variable usage for proper validation
|
|
19619
|
+
3. Verify tool implementations for path/command safety
|
|
19620
|
+
4. Review LLM provider integrations for key exposure
|
|
19621
|
+
|
|
19622
|
+
Severity levels:
|
|
19623
|
+
- CRITICAL: Exploitable with immediate impact (must block release)
|
|
19624
|
+
- HIGH: Likely exploitable under realistic conditions
|
|
19625
|
+
- MEDIUM: Exploitable in specific scenarios
|
|
19626
|
+
- LOW: Defense-in-depth improvement
|
|
19627
|
+
|
|
19628
|
+
Output a structured security report. Do not make code changes \u2014 report findings only.`;
|
|
19629
|
+
var TDD_PROMPT = `You are a TDD (Test-Driven Development) agent for Corbat-Coco.
|
|
19630
|
+
Your purpose is to enforce test-first methodology with RED-GREEN-REFACTOR discipline.
|
|
19631
|
+
|
|
19632
|
+
Your capabilities:
|
|
19633
|
+
- Read source files to understand interfaces
|
|
19634
|
+
- Write failing tests BEFORE implementation
|
|
19635
|
+
- Run tests to confirm RED state
|
|
19636
|
+
- Write minimal code to make tests GREEN
|
|
19637
|
+
- Refactor while keeping tests passing
|
|
19638
|
+
- Check coverage with Vitest
|
|
19639
|
+
|
|
19640
|
+
TDD workflow:
|
|
19641
|
+
1. **Interface first**: Define types and function signatures (no implementation)
|
|
19642
|
+
2. **RED**: Write failing tests that describe the desired behavior
|
|
19643
|
+
- Run \`pnpm test\` \u2014 tests must FAIL at this point
|
|
19644
|
+
- If tests pass without implementation, the test is wrong
|
|
19645
|
+
3. **GREEN**: Write the minimum code to make tests pass
|
|
19646
|
+
- Run \`pnpm test\` \u2014 all tests must pass
|
|
19647
|
+
4. **REFACTOR**: Clean up code while keeping tests green
|
|
19648
|
+
- Run \`pnpm test\` after each refactor step
|
|
19649
|
+
|
|
19650
|
+
Testing stack (corbat-coco):
|
|
19651
|
+
- Framework: Vitest
|
|
19652
|
+
- Assertions: expect() from vitest
|
|
19653
|
+
- Mocking: vi.mock(), vi.fn(), vi.spyOn()
|
|
19654
|
+
- Run: \`pnpm test\`
|
|
19655
|
+
- Coverage: \`pnpm test -- --coverage\` (target: 80%+)
|
|
19656
|
+
|
|
19657
|
+
Test structure:
|
|
19658
|
+
\`\`\`typescript
|
|
19659
|
+
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
19660
|
+
|
|
19661
|
+
describe("ModuleName", () => {
|
|
19662
|
+
describe("functionName", () => {
|
|
19663
|
+
it("should [expected behavior] when [condition]", async () => {
|
|
19664
|
+
// Arrange
|
|
19665
|
+
// Act
|
|
19666
|
+
// Assert
|
|
19667
|
+
});
|
|
19668
|
+
});
|
|
19669
|
+
});
|
|
19670
|
+
\`\`\`
|
|
19671
|
+
|
|
19672
|
+
Rules:
|
|
19673
|
+
- NEVER write implementation before tests
|
|
19674
|
+
- One assertion per test when possible
|
|
19675
|
+
- Test behavior, not implementation details
|
|
19676
|
+
- Mock all external dependencies (LLM providers, filesystem, execa)`;
|
|
19677
|
+
var REFACTOR_PROMPT = `You are a refactoring agent for Corbat-Coco.
|
|
19678
|
+
Your purpose is to improve code structure, readability, and maintainability without changing behavior.
|
|
19679
|
+
|
|
19680
|
+
Your capabilities:
|
|
19681
|
+
- Read and analyze existing code for improvement opportunities
|
|
19682
|
+
- Edit files to improve structure
|
|
19683
|
+
- Run tests to verify behavior is preserved
|
|
19684
|
+
- Run linting and type checking
|
|
19685
|
+
|
|
19686
|
+
Refactoring techniques (apply in order of safety):
|
|
19687
|
+
1. **Extract function**: Break large functions into focused helpers (\u226450 lines each)
|
|
19688
|
+
2. **Rename for clarity**: Improve variable/function names to reveal intent
|
|
19689
|
+
3. **Remove duplication**: Extract shared logic to reusable functions/modules
|
|
19690
|
+
4. **Simplify conditionals**: Replace complex boolean logic with named predicates
|
|
19691
|
+
5. **Eliminate magic numbers**: Replace literals with named constants
|
|
19692
|
+
6. **Flatten nesting**: Reduce arrow anti-pattern via early returns
|
|
19693
|
+
7. **Split large files**: Files over 500 LOC should be split by cohesion
|
|
19694
|
+
|
|
19695
|
+
Safety rules:
|
|
19696
|
+
- Run \`pnpm test\` BEFORE starting \u2014 all tests must be green
|
|
19697
|
+
- Make one refactoring at a time
|
|
19698
|
+
- Run \`pnpm test\` after EVERY change \u2014 stop if tests break
|
|
19699
|
+
- Run \`pnpm typecheck\` to catch type regressions
|
|
19700
|
+
- Never change behavior \u2014 refactoring is structural only
|
|
19701
|
+
- If tests don't cover the code being refactored, write tests first
|
|
19702
|
+
|
|
19703
|
+
corbat-coco specific patterns to introduce:
|
|
19704
|
+
- Use Zod schemas for all config/input types
|
|
19705
|
+
- Apply Provider-Agnostic Pattern for LLM calls
|
|
19706
|
+
- Apply Tool Registry Pattern for tool management
|
|
19707
|
+
- Replace class-heavy code with factory functions where simpler
|
|
19708
|
+
|
|
19709
|
+
Output: describe each refactoring applied and run test/typecheck results.`;
|
|
19710
|
+
var E2E_PROMPT = `You are an end-to-end testing agent for Corbat-Coco.
|
|
19711
|
+
Your purpose is to write and run integration tests that cover complete user workflows.
|
|
19712
|
+
|
|
19713
|
+
Your capabilities:
|
|
19714
|
+
- Read source files to understand end-to-end flows
|
|
19715
|
+
- Write integration tests that exercise full workflows
|
|
19716
|
+
- Run tests and analyze failures
|
|
19717
|
+
- Check coverage across integration paths
|
|
19718
|
+
|
|
19719
|
+
E2E testing principles:
|
|
19720
|
+
1. Test complete workflows from entry point to output
|
|
19721
|
+
2. Use realistic inputs (not just happy paths)
|
|
19722
|
+
3. Test COCO phase transitions: Converge \u2192 Orchestrate \u2192 Complete \u2192 Output
|
|
19723
|
+
4. Test CLI commands end-to-end with subprocess spawning
|
|
19724
|
+
5. Test error propagation across phase boundaries
|
|
19725
|
+
6. Test provider fallback behavior
|
|
19726
|
+
7. Test tool execution chains
|
|
19727
|
+
|
|
19728
|
+
For corbat-coco, focus on:
|
|
19729
|
+
- Full COCO run: specification \u2192 backlog \u2192 task execution \u2192 output generation
|
|
19730
|
+
- CLI command integration: \`coco run\`, \`coco repl\`, \`coco init\`
|
|
19731
|
+
- LLM provider switching and error recovery
|
|
19732
|
+
- Tool registry tool execution chains
|
|
19733
|
+
- Quality scoring over multiple iterations
|
|
19734
|
+
- Checkpoint save/restore across phase boundaries
|
|
19735
|
+
|
|
19736
|
+
Test setup:
|
|
19737
|
+
\`\`\`typescript
|
|
19738
|
+
// Use mock LLM provider to avoid real API calls in tests
|
|
19739
|
+
import { createMockProvider } from "../mocks/provider.js";
|
|
19740
|
+
|
|
19741
|
+
// Test full COCO orchestration
|
|
19742
|
+
it("should complete a full run from spec to output", async () => {
|
|
19743
|
+
const provider = createMockProvider([
|
|
19744
|
+
{ content: "specification output" },
|
|
19745
|
+
{ content: "backlog output" },
|
|
19746
|
+
]);
|
|
19747
|
+
// ...
|
|
19748
|
+
});
|
|
19749
|
+
\`\`\`
|
|
19750
|
+
|
|
19751
|
+
Report coverage of integration paths and any workflow gaps found.`;
|
|
19752
|
+
var DOCS_PROMPT = `You are a documentation agent for Corbat-Coco.
|
|
19753
|
+
Your purpose is to generate and maintain clear, accurate documentation.
|
|
19754
|
+
|
|
19755
|
+
Your capabilities:
|
|
19756
|
+
- Read source files to understand what needs documenting
|
|
19757
|
+
- Write JSDoc for public APIs
|
|
19758
|
+
- Create or update README files
|
|
19759
|
+
- Generate architecture documentation
|
|
19760
|
+
- Update changelogs
|
|
19761
|
+
|
|
19762
|
+
Documentation types:
|
|
19763
|
+
1. **JSDoc**: All exported functions, types, and classes
|
|
19764
|
+
\`\`\`typescript
|
|
19765
|
+
/**
|
|
19766
|
+
* Brief description.
|
|
19767
|
+
*
|
|
19768
|
+
* @param paramName - What it is and valid values
|
|
19769
|
+
* @returns What is returned and when
|
|
19770
|
+
* @throws What errors can be thrown and why
|
|
19771
|
+
* @example
|
|
19772
|
+
* \`\`\`typescript
|
|
19773
|
+
* const result = myFunction(input);
|
|
19774
|
+
* \`\`\`
|
|
19775
|
+
*/
|
|
19776
|
+
\`\`\`
|
|
19777
|
+
2. **README**: Project overview, installation, usage, examples, API reference
|
|
19778
|
+
3. **ADR**: Architecture Decision Records (see architect agent format)
|
|
19779
|
+
4. **CHANGELOG**: Conventional commit-based changelog entries
|
|
19780
|
+
5. **CODING_STANDARDS.md**: Language-specific standards for user projects
|
|
19781
|
+
|
|
19782
|
+
corbat-coco documentation conventions:
|
|
19783
|
+
- Public APIs must have JSDoc with @param, @returns, @example
|
|
19784
|
+
- Complex logic must have inline comments explaining WHY, not WHAT
|
|
19785
|
+
- COCO phases must be documented with input/output contracts
|
|
19786
|
+
- Tool implementations must document their parameters and return format
|
|
19787
|
+
- Provider implementations must document their configuration requirements
|
|
19788
|
+
|
|
19789
|
+
Output well-structured documentation. Prefer accuracy over completeness.`;
|
|
19790
|
+
var DATABASE_PROMPT = `You are a database engineering agent for Corbat-Coco.
|
|
19791
|
+
Your purpose is to design database schemas, write migrations, and optimize queries.
|
|
19792
|
+
|
|
19793
|
+
Your capabilities:
|
|
19794
|
+
- Read source files to understand data models
|
|
19795
|
+
- Write SQL migrations and ORM schema definitions
|
|
19796
|
+
- Design schema changes for zero-downtime deployment
|
|
19797
|
+
- Review queries for N+1 problems and missing indexes
|
|
19798
|
+
|
|
19799
|
+
Migration safety rules:
|
|
19800
|
+
1. **Never destructive in one step**: Split DROP/rename into multiple deployments
|
|
19801
|
+
2. **Backward compatible first**: New columns must be nullable or have defaults
|
|
19802
|
+
3. **Zero-downtime patterns**:
|
|
19803
|
+
- Add column \u2192 deploy app \u2192 backfill \u2192 add constraint \u2192 drop old column
|
|
19804
|
+
- Never rename columns directly \u2014 add new, migrate data, drop old
|
|
19805
|
+
4. **Always reversible**: Every migration needs a rollback script
|
|
19806
|
+
5. **Test migrations**: Run on a copy of production data before applying
|
|
19807
|
+
|
|
19808
|
+
ORM support:
|
|
19809
|
+
- **Prisma** (Node.js): \`schema.prisma\` + \`prisma migrate dev\`
|
|
19810
|
+
- **TypeORM** (TypeScript): entity classes + \`migration:generate\`
|
|
19811
|
+
- **Alembic** (Python): \`alembic revision --autogenerate\`
|
|
19812
|
+
- **Flyway** (Java): versioned SQL files in \`db/migration/\`
|
|
19813
|
+
- **golang-migrate** (Go): numbered SQL files
|
|
19814
|
+
|
|
19815
|
+
Index design:
|
|
19816
|
+
- Index all foreign keys
|
|
19817
|
+
- Composite indexes: most selective column first
|
|
19818
|
+
- Partial indexes for filtered queries
|
|
19819
|
+
- Avoid over-indexing (slows writes)
|
|
19820
|
+
|
|
19821
|
+
Query patterns:
|
|
19822
|
+
- Use pagination (LIMIT/OFFSET or cursor-based)
|
|
19823
|
+
- Avoid N+1: use JOIN or batch loading
|
|
19824
|
+
- Use query analysis tools (EXPLAIN ANALYZE)
|
|
19825
|
+
- Keep transactions short and focused
|
|
19826
|
+
|
|
19827
|
+
Output migration files with up/down scripts and a schema change summary.`;
|
|
19828
|
+
var AGENT_PROMPTS = {
|
|
19829
|
+
explore: EXPLORE_PROMPT,
|
|
19830
|
+
plan: PLAN_PROMPT,
|
|
19831
|
+
test: TEST_PROMPT,
|
|
19832
|
+
debug: DEBUG_PROMPT,
|
|
19833
|
+
review: REVIEW_PROMPT,
|
|
19834
|
+
architect: ARCHITECT_PROMPT,
|
|
19835
|
+
security: SECURITY_PROMPT,
|
|
19836
|
+
tdd: TDD_PROMPT,
|
|
19837
|
+
refactor: REFACTOR_PROMPT,
|
|
19838
|
+
e2e: E2E_PROMPT,
|
|
19839
|
+
docs: DOCS_PROMPT,
|
|
19840
|
+
database: DATABASE_PROMPT
|
|
19405
19841
|
};
|
|
19406
|
-
|
|
19407
|
-
|
|
19408
|
-
|
|
19409
|
-
|
|
19410
|
-
|
|
19411
|
-
|
|
19412
|
-
|
|
19413
|
-
|
|
19414
|
-
|
|
19415
|
-
|
|
19416
|
-
|
|
19417
|
-
|
|
19418
|
-
|
|
19419
|
-
|
|
19842
|
+
var AGENT_TOOLS = {
|
|
19843
|
+
explore: [
|
|
19844
|
+
"glob",
|
|
19845
|
+
"read_file",
|
|
19846
|
+
"list_dir",
|
|
19847
|
+
"bash_exec",
|
|
19848
|
+
"git_status",
|
|
19849
|
+
"git_diff",
|
|
19850
|
+
"git_log",
|
|
19851
|
+
"git_branch"
|
|
19852
|
+
],
|
|
19853
|
+
plan: ["glob", "read_file", "list_dir", "git_status", "git_diff", "git_log", "git_branch"],
|
|
19854
|
+
test: [
|
|
19855
|
+
"glob",
|
|
19856
|
+
"read_file",
|
|
19857
|
+
"write_file",
|
|
19858
|
+
"edit_file",
|
|
19859
|
+
"run_tests",
|
|
19860
|
+
"bash_exec",
|
|
19861
|
+
"git_status",
|
|
19862
|
+
"git_diff"
|
|
19863
|
+
],
|
|
19864
|
+
debug: [
|
|
19865
|
+
"glob",
|
|
19866
|
+
"read_file",
|
|
19867
|
+
"write_file",
|
|
19868
|
+
"edit_file",
|
|
19869
|
+
"bash_exec",
|
|
19870
|
+
"run_tests",
|
|
19871
|
+
"git_status",
|
|
19872
|
+
"git_diff",
|
|
19873
|
+
"git_log"
|
|
19874
|
+
],
|
|
19875
|
+
review: ["glob", "read_file", "list_dir", "git_status", "git_diff", "git_log", "git_branch"],
|
|
19876
|
+
architect: ["glob", "read_file", "list_dir", "git_log", "git_branch"],
|
|
19877
|
+
security: ["glob", "read_file", "list_dir", "bash_exec", "git_diff", "git_log"],
|
|
19878
|
+
tdd: [
|
|
19879
|
+
"glob",
|
|
19880
|
+
"read_file",
|
|
19881
|
+
"write_file",
|
|
19882
|
+
"edit_file",
|
|
19883
|
+
"run_tests",
|
|
19884
|
+
"bash_exec",
|
|
19885
|
+
"git_status",
|
|
19886
|
+
"git_diff"
|
|
19887
|
+
],
|
|
19888
|
+
refactor: [
|
|
19889
|
+
"glob",
|
|
19890
|
+
"read_file",
|
|
19891
|
+
"write_file",
|
|
19892
|
+
"edit_file",
|
|
19893
|
+
"run_tests",
|
|
19894
|
+
"bash_exec",
|
|
19895
|
+
"git_status",
|
|
19896
|
+
"git_diff"
|
|
19897
|
+
],
|
|
19898
|
+
e2e: [
|
|
19899
|
+
"glob",
|
|
19900
|
+
"read_file",
|
|
19901
|
+
"write_file",
|
|
19902
|
+
"edit_file",
|
|
19903
|
+
"run_tests",
|
|
19904
|
+
"bash_exec",
|
|
19905
|
+
"git_status",
|
|
19906
|
+
"git_diff"
|
|
19907
|
+
],
|
|
19908
|
+
docs: ["glob", "read_file", "write_file", "edit_file", "list_dir", "git_log"],
|
|
19909
|
+
database: [
|
|
19910
|
+
"glob",
|
|
19911
|
+
"read_file",
|
|
19912
|
+
"write_file",
|
|
19913
|
+
"edit_file",
|
|
19914
|
+
"bash_exec",
|
|
19915
|
+
"git_status",
|
|
19916
|
+
"sql_query",
|
|
19917
|
+
"inspect_schema"
|
|
19918
|
+
]
|
|
19919
|
+
};
|
|
19920
|
+
var AGENT_MAX_TURNS = {
|
|
19921
|
+
explore: 10,
|
|
19922
|
+
plan: 8,
|
|
19923
|
+
test: 15,
|
|
19924
|
+
debug: 12,
|
|
19925
|
+
review: 6,
|
|
19926
|
+
architect: 12,
|
|
19927
|
+
security: 10,
|
|
19928
|
+
tdd: 20,
|
|
19929
|
+
refactor: 15,
|
|
19930
|
+
e2e: 15,
|
|
19931
|
+
docs: 12,
|
|
19932
|
+
database: 12
|
|
19933
|
+
};
|
|
19934
|
+
function getAgentConfig(type) {
|
|
19935
|
+
return {
|
|
19936
|
+
type,
|
|
19937
|
+
systemPrompt: AGENT_PROMPTS[type],
|
|
19938
|
+
tools: AGENT_TOOLS[type],
|
|
19939
|
+
maxTurns: AGENT_MAX_TURNS[type]
|
|
19940
|
+
};
|
|
19941
|
+
}
|
|
19942
|
+
var AGENT_NAMES = {
|
|
19943
|
+
explore: "Explorer",
|
|
19944
|
+
plan: "Planner",
|
|
19945
|
+
test: "Tester",
|
|
19946
|
+
debug: "Debugger",
|
|
19947
|
+
review: "Reviewer",
|
|
19948
|
+
architect: "Architect",
|
|
19949
|
+
security: "Security Auditor",
|
|
19950
|
+
tdd: "TDD Guide",
|
|
19951
|
+
refactor: "Refactorer",
|
|
19952
|
+
e2e: "E2E Tester",
|
|
19953
|
+
docs: "Docs Writer",
|
|
19954
|
+
database: "Database Engineer"
|
|
19955
|
+
};
|
|
19956
|
+
var AGENT_DESCRIPTIONS = {
|
|
19957
|
+
explore: "Search the codebase to answer questions and gather information",
|
|
19958
|
+
plan: "Design implementation approaches and create detailed plans",
|
|
19959
|
+
test: "Write and run tests to ensure code quality",
|
|
19960
|
+
debug: "Analyze errors and fix issues",
|
|
19961
|
+
review: "Review code for quality and best practices",
|
|
19962
|
+
architect: "Design system architecture and create architectural decision records",
|
|
19963
|
+
security: "Audit code for security vulnerabilities using OWASP Top 10",
|
|
19964
|
+
tdd: "Drive development with test-first methodology and RED-GREEN-REFACTOR cycle",
|
|
19965
|
+
refactor: "Improve code structure and quality without changing behavior",
|
|
19966
|
+
e2e: "Write and run end-to-end tests covering full user workflows",
|
|
19967
|
+
docs: "Generate and maintain documentation for code, APIs, and architecture",
|
|
19968
|
+
database: "Design database schemas, write migrations, and optimize queries"
|
|
19969
|
+
};
|
|
19970
|
+
|
|
19971
|
+
// src/cli/repl/agents/manager.ts
|
|
19972
|
+
var MAX_CONCURRENT_AGENTS = 3;
|
|
19973
|
+
var DEFAULT_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
19974
|
+
var AgentManager = class extends EventEmitter {
|
|
19975
|
+
activeAgents = /* @__PURE__ */ new Map();
|
|
19976
|
+
completedAgents = /* @__PURE__ */ new Map();
|
|
19977
|
+
abortControllers = /* @__PURE__ */ new Map();
|
|
19978
|
+
provider;
|
|
19979
|
+
toolRegistry;
|
|
19980
|
+
logger = getLogger();
|
|
19981
|
+
/**
|
|
19982
|
+
* Create a new AgentManager
|
|
19983
|
+
* @param provider - LLM provider for agent execution
|
|
19984
|
+
* @param toolRegistry - Tool registry for agent tool access
|
|
19985
|
+
*/
|
|
19986
|
+
constructor(provider, toolRegistry) {
|
|
19987
|
+
super();
|
|
19988
|
+
this.provider = provider;
|
|
19989
|
+
this.toolRegistry = toolRegistry;
|
|
19990
|
+
}
|
|
19991
|
+
/**
|
|
19992
|
+
* Spawn a new subagent for a specific task
|
|
19993
|
+
*
|
|
19994
|
+
* @description Creates and executes a specialized subagent for the given task.
|
|
19995
|
+
* The agent will run autonomously, making LLM calls and executing tools until
|
|
19996
|
+
* it completes the task or reaches the maximum turn limit.
|
|
19997
|
+
*
|
|
19998
|
+
* @param type - Type of agent to spawn (explore, plan, test, debug, review, architect, security, tdd, refactor, e2e, docs, database)
|
|
19999
|
+
* @param task - Task description for the agent to execute
|
|
20000
|
+
* @param options - Optional spawn configuration including callbacks, abort signal, and timeout
|
|
20001
|
+
* @returns Promise resolving to the agent result with output and usage stats
|
|
20002
|
+
*
|
|
20003
|
+
* @example
|
|
20004
|
+
* ```typescript
|
|
20005
|
+
* const result = await manager.spawn('test', 'Write tests for UserService', {
|
|
20006
|
+
* onStatusChange: (agent) => console.log(`Status: ${agent.status}`),
|
|
20007
|
+
* onOutput: (agent, text) => console.log(text),
|
|
20008
|
+
* timeout: 60000, // 1 minute timeout
|
|
20009
|
+
* });
|
|
20010
|
+
* ```
|
|
20011
|
+
*/
|
|
20012
|
+
async spawn(type, task, options = {}) {
|
|
20013
|
+
if (this.activeAgents.size >= MAX_CONCURRENT_AGENTS) {
|
|
20014
|
+
const error = `Cannot spawn agent: maximum concurrent agents (${MAX_CONCURRENT_AGENTS}) reached`;
|
|
20015
|
+
this.logger.warn(error);
|
|
20016
|
+
const failedAgent = this.createAgent(type, task);
|
|
20017
|
+
failedAgent.status = "failed";
|
|
20018
|
+
failedAgent.error = error;
|
|
20019
|
+
failedAgent.completedAt = /* @__PURE__ */ new Date();
|
|
20020
|
+
return {
|
|
20021
|
+
agent: failedAgent,
|
|
20022
|
+
success: false,
|
|
20023
|
+
output: error
|
|
20024
|
+
};
|
|
20025
|
+
}
|
|
20026
|
+
const agent = this.createAgent(type, task);
|
|
20027
|
+
this.activeAgents.set(agent.id, agent);
|
|
20028
|
+
options.onStatusChange?.(agent);
|
|
20029
|
+
const internalAbortController = new AbortController();
|
|
20030
|
+
this.abortControllers.set(agent.id, internalAbortController);
|
|
20031
|
+
if (options.signal) {
|
|
20032
|
+
if (options.signal.aborted) {
|
|
20033
|
+
internalAbortController.abort();
|
|
20034
|
+
} else {
|
|
20035
|
+
options.signal.addEventListener("abort", () => {
|
|
20036
|
+
internalAbortController.abort();
|
|
20037
|
+
});
|
|
20038
|
+
}
|
|
20039
|
+
}
|
|
20040
|
+
const timeout = options.timeout ?? DEFAULT_TIMEOUT_MS;
|
|
20041
|
+
const timeoutId = setTimeout(() => {
|
|
20042
|
+
if (this.activeAgents.has(agent.id)) {
|
|
20043
|
+
this.logger.warn(`Agent ${agent.id} timed out after ${timeout}ms`);
|
|
20044
|
+
internalAbortController.abort();
|
|
20045
|
+
agent.error = `Agent timed out after ${timeout}ms`;
|
|
20046
|
+
this.emitEvent("timeout", agent);
|
|
20047
|
+
}
|
|
20048
|
+
}, timeout);
|
|
20049
|
+
this.logger.info(`Spawned ${type} agent: ${agent.id}`, { task, timeout });
|
|
20050
|
+
this.emitEvent("spawn", agent);
|
|
19420
20051
|
try {
|
|
19421
|
-
const
|
|
19422
|
-
|
|
19423
|
-
|
|
19424
|
-
|
|
19425
|
-
|
|
19426
|
-
|
|
19427
|
-
|
|
19428
|
-
|
|
19429
|
-
|
|
19430
|
-
|
|
19431
|
-
|
|
19432
|
-
if (entryNameLower === targetLower) {
|
|
19433
|
-
distance = 0;
|
|
19434
|
-
} else {
|
|
19435
|
-
distance = levenshtein(targetLower, entryNameLower);
|
|
19436
|
-
}
|
|
19437
|
-
const maxDistance = Math.max(target.length * 0.6, 3);
|
|
19438
|
-
if (distance <= maxDistance) {
|
|
19439
|
-
results.push({ path: entryPath, distance });
|
|
19440
|
-
}
|
|
19441
|
-
}
|
|
19442
|
-
if (entry.isDirectory() && !opts.excludeDirs.has(entryName)) {
|
|
19443
|
-
queue.push([entryPath, depth + 1]);
|
|
19444
|
-
}
|
|
20052
|
+
const result = await this.executeAgent(agent, {
|
|
20053
|
+
...options,
|
|
20054
|
+
signal: internalAbortController.signal
|
|
20055
|
+
});
|
|
20056
|
+
clearTimeout(timeoutId);
|
|
20057
|
+
if (result.success) {
|
|
20058
|
+
this.emitEvent("complete", agent, result);
|
|
20059
|
+
} else if (agent.error?.includes("Aborted")) {
|
|
20060
|
+
this.emitEvent("cancel", agent, result);
|
|
20061
|
+
} else {
|
|
20062
|
+
this.emitEvent("fail", agent, result);
|
|
19445
20063
|
}
|
|
19446
|
-
|
|
19447
|
-
|
|
20064
|
+
return result;
|
|
20065
|
+
} catch (error) {
|
|
20066
|
+
clearTimeout(timeoutId);
|
|
20067
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
20068
|
+
agent.status = "failed";
|
|
20069
|
+
agent.error = errorMessage;
|
|
20070
|
+
agent.completedAt = /* @__PURE__ */ new Date();
|
|
20071
|
+
this.moveToCompleted(agent.id);
|
|
20072
|
+
this.abortControllers.delete(agent.id);
|
|
20073
|
+
options.onStatusChange?.(agent);
|
|
20074
|
+
this.logger.error(`Agent ${agent.id} failed unexpectedly`, { error: errorMessage });
|
|
20075
|
+
this.emitEvent("fail", agent);
|
|
20076
|
+
return {
|
|
20077
|
+
agent,
|
|
20078
|
+
success: false,
|
|
20079
|
+
output: errorMessage
|
|
20080
|
+
};
|
|
19448
20081
|
}
|
|
19449
20082
|
}
|
|
19450
|
-
|
|
19451
|
-
|
|
19452
|
-
|
|
19453
|
-
|
|
19454
|
-
|
|
19455
|
-
|
|
19456
|
-
|
|
19457
|
-
|
|
19458
|
-
|
|
19459
|
-
|
|
19460
|
-
|
|
19461
|
-
|
|
19462
|
-
}
|
|
19463
|
-
|
|
19464
|
-
const absPath = path17__default.resolve(missingPath);
|
|
19465
|
-
const target = path17__default.basename(absPath);
|
|
19466
|
-
const parentDir = path17__default.dirname(absPath);
|
|
19467
|
-
try {
|
|
19468
|
-
const entries = await fs16__default.readdir(parentDir, { withFileTypes: true });
|
|
19469
|
-
const dirs = entries.filter((e) => e.isDirectory());
|
|
19470
|
-
const scored = dirs.map((d) => ({
|
|
19471
|
-
path: path17__default.join(parentDir, d.name),
|
|
19472
|
-
distance: levenshtein(target.toLowerCase(), d.name.toLowerCase())
|
|
19473
|
-
})).filter((s) => s.distance <= Math.max(target.length * 0.6, 3)).sort((a, b) => a.distance - b.distance).slice(0, options?.maxResults ?? MAX_SUGGESTIONS);
|
|
19474
|
-
if (scored.length > 0) {
|
|
19475
|
-
return scored;
|
|
20083
|
+
/**
|
|
20084
|
+
* Cancel a running agent
|
|
20085
|
+
*
|
|
20086
|
+
* @description Cancels an active agent by triggering its abort signal.
|
|
20087
|
+
* The agent will stop at the next safe point (beginning of next iteration).
|
|
20088
|
+
*
|
|
20089
|
+
* @param agentId - ID of the agent to cancel
|
|
20090
|
+
* @returns True if the agent was cancelled, false if not found or already completed
|
|
20091
|
+
*/
|
|
20092
|
+
cancel(agentId) {
|
|
20093
|
+
const controller = this.abortControllers.get(agentId);
|
|
20094
|
+
if (!controller) {
|
|
20095
|
+
this.logger.warn(`Cannot cancel agent ${agentId}: not found or already completed`);
|
|
20096
|
+
return false;
|
|
19476
20097
|
}
|
|
19477
|
-
|
|
20098
|
+
this.logger.info(`Cancelling agent ${agentId}`);
|
|
20099
|
+
controller.abort();
|
|
20100
|
+
return true;
|
|
19478
20101
|
}
|
|
19479
|
-
|
|
19480
|
-
|
|
19481
|
-
|
|
19482
|
-
|
|
19483
|
-
|
|
19484
|
-
|
|
19485
|
-
|
|
19486
|
-
|
|
19487
|
-
|
|
19488
|
-
|
|
19489
|
-
|
|
19490
|
-
|
|
19491
|
-
|
|
19492
|
-
|
|
19493
|
-
|
|
20102
|
+
/**
|
|
20103
|
+
* Get the status of a specific agent
|
|
20104
|
+
*
|
|
20105
|
+
* @description Returns the current status of an agent.
|
|
20106
|
+
*
|
|
20107
|
+
* @param agentId - ID of the agent
|
|
20108
|
+
* @returns The agent's status or undefined if not found
|
|
20109
|
+
*/
|
|
20110
|
+
getStatus(agentId) {
|
|
20111
|
+
return this.getAgent(agentId)?.status;
|
|
20112
|
+
}
|
|
20113
|
+
/**
|
|
20114
|
+
* Get the current status of an agent
|
|
20115
|
+
*
|
|
20116
|
+
* @description Retrieves an agent by ID from either active or completed agents.
|
|
20117
|
+
*
|
|
20118
|
+
* @param agentId - Unique agent identifier (UUID)
|
|
20119
|
+
* @returns The agent if found, undefined otherwise
|
|
20120
|
+
*/
|
|
20121
|
+
getAgent(agentId) {
|
|
20122
|
+
return this.activeAgents.get(agentId) ?? this.completedAgents.get(agentId);
|
|
20123
|
+
}
|
|
20124
|
+
/**
|
|
20125
|
+
* Get all active agents
|
|
20126
|
+
* @returns Array of currently running agents
|
|
20127
|
+
*/
|
|
20128
|
+
getActiveAgents() {
|
|
20129
|
+
return Array.from(this.activeAgents.values());
|
|
20130
|
+
}
|
|
20131
|
+
/**
|
|
20132
|
+
* Get all completed agents
|
|
20133
|
+
* @returns Array of completed agents (succeeded or failed)
|
|
20134
|
+
*/
|
|
20135
|
+
getCompletedAgents() {
|
|
20136
|
+
return Array.from(this.completedAgents.values());
|
|
20137
|
+
}
|
|
20138
|
+
/**
|
|
20139
|
+
* Get the number of active agents
|
|
20140
|
+
* @returns Count of running agents
|
|
20141
|
+
*/
|
|
20142
|
+
getActiveCount() {
|
|
20143
|
+
return this.activeAgents.size;
|
|
20144
|
+
}
|
|
20145
|
+
/**
|
|
20146
|
+
* Check if more agents can be spawned
|
|
20147
|
+
* @returns True if under the concurrent limit
|
|
20148
|
+
*/
|
|
20149
|
+
canSpawn() {
|
|
20150
|
+
return this.activeAgents.size < MAX_CONCURRENT_AGENTS;
|
|
20151
|
+
}
|
|
20152
|
+
/**
|
|
20153
|
+
* Clear completed agents from history
|
|
20154
|
+
*/
|
|
20155
|
+
clearCompleted() {
|
|
20156
|
+
this.completedAgents.clear();
|
|
20157
|
+
this.logger.debug("Cleared completed agents history");
|
|
20158
|
+
}
|
|
20159
|
+
/**
|
|
20160
|
+
* Get available agent types with their descriptions
|
|
20161
|
+
*
|
|
20162
|
+
* @description Returns all supported agent types with their names, descriptions,
|
|
20163
|
+
* and configurations. Useful for building UI or help text.
|
|
20164
|
+
*
|
|
20165
|
+
* @returns Array of agent registry entries with type, name, description, and config
|
|
20166
|
+
*/
|
|
20167
|
+
getAvailableAgentTypes() {
|
|
20168
|
+
return Object.keys(AGENT_NAMES).map((type) => ({
|
|
20169
|
+
type,
|
|
20170
|
+
name: AGENT_NAMES[type],
|
|
20171
|
+
description: AGENT_DESCRIPTIONS[type],
|
|
20172
|
+
config: getAgentConfig(type)
|
|
20173
|
+
}));
|
|
20174
|
+
}
|
|
20175
|
+
/**
|
|
20176
|
+
* Create a new agent instance
|
|
20177
|
+
*/
|
|
20178
|
+
createAgent(type, task) {
|
|
20179
|
+
return {
|
|
20180
|
+
id: randomUUID(),
|
|
20181
|
+
type,
|
|
20182
|
+
status: "idle",
|
|
20183
|
+
task,
|
|
20184
|
+
createdAt: /* @__PURE__ */ new Date()
|
|
20185
|
+
};
|
|
20186
|
+
}
|
|
20187
|
+
/**
|
|
20188
|
+
* Execute an agent's task
|
|
20189
|
+
*/
|
|
20190
|
+
async executeAgent(agent, options) {
|
|
20191
|
+
const config = getAgentConfig(agent.type);
|
|
20192
|
+
agent.status = "running";
|
|
20193
|
+
options.onStatusChange?.(agent);
|
|
20194
|
+
const tools = this.getToolsForAgent(config);
|
|
20195
|
+
const messages = [{ role: "user", content: agent.task }];
|
|
20196
|
+
let totalInputTokens = 0;
|
|
20197
|
+
let totalOutputTokens = 0;
|
|
20198
|
+
let finalOutput = "";
|
|
20199
|
+
let iteration = 0;
|
|
20200
|
+
const maxTurns = config.maxTurns ?? 10;
|
|
20201
|
+
while (iteration < maxTurns) {
|
|
20202
|
+
iteration++;
|
|
20203
|
+
if (options.signal?.aborted) {
|
|
20204
|
+
agent.status = "failed";
|
|
20205
|
+
agent.error = "Aborted by user";
|
|
20206
|
+
agent.completedAt = /* @__PURE__ */ new Date();
|
|
20207
|
+
this.moveToCompleted(agent.id);
|
|
20208
|
+
options.onStatusChange?.(agent);
|
|
20209
|
+
return {
|
|
20210
|
+
agent,
|
|
20211
|
+
success: false,
|
|
20212
|
+
output: "Agent execution was aborted",
|
|
20213
|
+
usage: { inputTokens: totalInputTokens, outputTokens: totalOutputTokens }
|
|
20214
|
+
};
|
|
20215
|
+
}
|
|
20216
|
+
const response = await this.provider.chatWithTools(messages, {
|
|
20217
|
+
system: config.systemPrompt,
|
|
20218
|
+
tools,
|
|
20219
|
+
maxTokens: 4096
|
|
20220
|
+
});
|
|
20221
|
+
totalInputTokens += response.usage.inputTokens;
|
|
20222
|
+
totalOutputTokens += response.usage.outputTokens;
|
|
20223
|
+
if (response.content) {
|
|
20224
|
+
finalOutput += response.content;
|
|
20225
|
+
options.onOutput?.(agent, response.content);
|
|
20226
|
+
}
|
|
20227
|
+
if (!response.toolCalls || response.toolCalls.length === 0) {
|
|
20228
|
+
messages.push({ role: "assistant", content: response.content });
|
|
20229
|
+
break;
|
|
20230
|
+
}
|
|
20231
|
+
const toolResults = await this.executeToolCalls(response.toolCalls, config);
|
|
20232
|
+
const toolUses = response.toolCalls.map((tc) => ({
|
|
20233
|
+
type: "tool_use",
|
|
20234
|
+
id: tc.id,
|
|
20235
|
+
name: tc.name,
|
|
20236
|
+
input: tc.input,
|
|
20237
|
+
geminiThoughtSignature: tc.geminiThoughtSignature
|
|
20238
|
+
}));
|
|
20239
|
+
const assistantContent = response.content ? [{ type: "text", text: response.content }, ...toolUses] : toolUses;
|
|
20240
|
+
messages.push({ role: "assistant", content: assistantContent });
|
|
20241
|
+
messages.push({ role: "user", content: toolResults });
|
|
20242
|
+
this.logger.debug(`Agent ${agent.id} completed iteration ${iteration}`, {
|
|
20243
|
+
toolCalls: response.toolCalls.length
|
|
20244
|
+
});
|
|
20245
|
+
}
|
|
20246
|
+
agent.status = "completed";
|
|
20247
|
+
agent.result = finalOutput;
|
|
20248
|
+
agent.completedAt = /* @__PURE__ */ new Date();
|
|
20249
|
+
this.moveToCompleted(agent.id);
|
|
20250
|
+
options.onStatusChange?.(agent);
|
|
20251
|
+
this.logger.info(`Agent ${agent.id} completed`, {
|
|
20252
|
+
iterations: iteration,
|
|
20253
|
+
outputLength: finalOutput.length
|
|
20254
|
+
});
|
|
20255
|
+
return {
|
|
20256
|
+
agent,
|
|
20257
|
+
success: true,
|
|
20258
|
+
output: finalOutput,
|
|
20259
|
+
usage: { inputTokens: totalInputTokens, outputTokens: totalOutputTokens }
|
|
20260
|
+
};
|
|
20261
|
+
}
|
|
20262
|
+
/**
|
|
20263
|
+
* Get tool definitions filtered for the agent's allowed tools
|
|
20264
|
+
*/
|
|
20265
|
+
getToolsForAgent(config) {
|
|
20266
|
+
const allTools = this.toolRegistry.getToolDefinitionsForLLM();
|
|
20267
|
+
const allowedSet = new Set(config.tools);
|
|
20268
|
+
return allTools.filter((tool) => allowedSet.has(tool.name));
|
|
20269
|
+
}
|
|
20270
|
+
/**
|
|
20271
|
+
* Execute tool calls and return results
|
|
20272
|
+
*/
|
|
20273
|
+
async executeToolCalls(toolCalls, config) {
|
|
20274
|
+
const results = [];
|
|
20275
|
+
const allowedTools = new Set(config.tools);
|
|
20276
|
+
for (const toolCall of toolCalls) {
|
|
20277
|
+
if (!allowedTools.has(toolCall.name)) {
|
|
20278
|
+
results.push({
|
|
20279
|
+
type: "tool_result",
|
|
20280
|
+
tool_use_id: toolCall.id,
|
|
20281
|
+
content: `Tool '${toolCall.name}' is not available to this agent type`,
|
|
20282
|
+
is_error: true
|
|
20283
|
+
});
|
|
20284
|
+
continue;
|
|
20285
|
+
}
|
|
20286
|
+
try {
|
|
20287
|
+
const result = await this.toolRegistry.execute(toolCall.name, toolCall.input);
|
|
20288
|
+
results.push({
|
|
20289
|
+
type: "tool_result",
|
|
20290
|
+
tool_use_id: toolCall.id,
|
|
20291
|
+
content: result.success ? String(result.data ?? "Success") : `Error: ${result.error}`,
|
|
20292
|
+
is_error: !result.success
|
|
20293
|
+
});
|
|
20294
|
+
} catch (error) {
|
|
20295
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
20296
|
+
results.push({
|
|
20297
|
+
type: "tool_result",
|
|
20298
|
+
tool_use_id: toolCall.id,
|
|
20299
|
+
content: `Tool execution failed: ${errorMessage}`,
|
|
20300
|
+
is_error: true
|
|
20301
|
+
});
|
|
20302
|
+
}
|
|
20303
|
+
}
|
|
20304
|
+
return results;
|
|
20305
|
+
}
|
|
20306
|
+
/**
|
|
20307
|
+
* Move an agent from active to completed
|
|
20308
|
+
*/
|
|
20309
|
+
moveToCompleted(agentId) {
|
|
20310
|
+
const agent = this.activeAgents.get(agentId);
|
|
20311
|
+
if (agent) {
|
|
20312
|
+
this.activeAgents.delete(agentId);
|
|
20313
|
+
this.completedAgents.set(agentId, agent);
|
|
20314
|
+
this.abortControllers.delete(agentId);
|
|
20315
|
+
}
|
|
20316
|
+
}
|
|
20317
|
+
/**
|
|
20318
|
+
* Emit an agent event
|
|
20319
|
+
*/
|
|
20320
|
+
emitEvent(type, agent, result) {
|
|
20321
|
+
const event = { type, agent, result };
|
|
20322
|
+
this.emit(type, event);
|
|
20323
|
+
this.emit("agent", event);
|
|
20324
|
+
}
|
|
20325
|
+
};
|
|
20326
|
+
|
|
20327
|
+
// src/agents/provider-bridge.ts
|
|
20328
|
+
var agentProvider = null;
|
|
20329
|
+
var agentToolRegistry = null;
|
|
20330
|
+
var agentManagerInstance = null;
|
|
20331
|
+
function setAgentProvider(provider) {
|
|
20332
|
+
agentProvider = provider;
|
|
20333
|
+
agentManagerInstance = null;
|
|
20334
|
+
}
|
|
20335
|
+
function getAgentProvider() {
|
|
20336
|
+
return agentProvider;
|
|
20337
|
+
}
|
|
20338
|
+
function setAgentToolRegistry(registry) {
|
|
20339
|
+
agentToolRegistry = registry;
|
|
20340
|
+
agentManagerInstance = null;
|
|
20341
|
+
}
|
|
20342
|
+
function getAgentToolRegistry() {
|
|
20343
|
+
return agentToolRegistry;
|
|
20344
|
+
}
|
|
20345
|
+
function getAgentManager() {
|
|
20346
|
+
if (!agentProvider || !agentToolRegistry) {
|
|
20347
|
+
return null;
|
|
20348
|
+
}
|
|
20349
|
+
if (!agentManagerInstance) {
|
|
20350
|
+
agentManagerInstance = new AgentManager(agentProvider, agentToolRegistry);
|
|
20351
|
+
}
|
|
20352
|
+
return agentManagerInstance;
|
|
20353
|
+
}
|
|
20354
|
+
|
|
20355
|
+
// src/cli/repl/modes.ts
|
|
20356
|
+
var AGENT_MODES = {
|
|
20357
|
+
ask: {
|
|
20358
|
+
id: "ask",
|
|
20359
|
+
label: "Ask",
|
|
20360
|
+
description: "Answer questions and explain code without modifying files.",
|
|
20361
|
+
readOnly: true,
|
|
20362
|
+
preferredTools: ["read_file", "grep", "glob", "codebase_map", "lsp_definition"],
|
|
20363
|
+
requiresVerification: false
|
|
20364
|
+
},
|
|
20365
|
+
plan: {
|
|
20366
|
+
id: "plan",
|
|
20367
|
+
label: "Plan",
|
|
20368
|
+
description: "Explore and produce an implementation plan with read-only tools.",
|
|
20369
|
+
readOnly: true,
|
|
20370
|
+
preferredTools: ["read_file", "grep", "glob", "codebase_map", "lsp_workspace_symbols"],
|
|
20371
|
+
requiresVerification: false
|
|
20372
|
+
},
|
|
20373
|
+
build: {
|
|
20374
|
+
id: "build",
|
|
20375
|
+
label: "Build",
|
|
20376
|
+
description: "Implement code changes and verify them.",
|
|
20377
|
+
readOnly: false,
|
|
20378
|
+
preferredTools: ["read_file", "edit_file", "write_file", "bash_exec", "run_tests"],
|
|
20379
|
+
requiresVerification: true
|
|
20380
|
+
},
|
|
20381
|
+
debug: {
|
|
20382
|
+
id: "debug",
|
|
20383
|
+
label: "Debug",
|
|
20384
|
+
description: "Reproduce failures, trace root cause, patch, and verify.",
|
|
20385
|
+
readOnly: false,
|
|
20386
|
+
preferredTools: ["bash_exec", "read_file", "grep", "lsp_references", "edit_file"],
|
|
20387
|
+
requiresVerification: true
|
|
20388
|
+
},
|
|
20389
|
+
review: {
|
|
20390
|
+
id: "review",
|
|
20391
|
+
label: "Review",
|
|
20392
|
+
description: "Inspect code quality, security, behavior changes, and test gaps.",
|
|
20393
|
+
readOnly: true,
|
|
20394
|
+
preferredTools: ["git_diff", "read_file", "grep", "review_code", "calculate_quality"],
|
|
20395
|
+
requiresVerification: false
|
|
20396
|
+
},
|
|
20397
|
+
architect: {
|
|
20398
|
+
id: "architect",
|
|
20399
|
+
label: "Architect",
|
|
20400
|
+
description: "Design architecture and split work for architect/editor execution.",
|
|
20401
|
+
readOnly: true,
|
|
20402
|
+
preferredTools: ["codebase_map", "lsp_workspace_symbols", "grep", "create_agent_plan"],
|
|
20403
|
+
requiresVerification: false
|
|
20404
|
+
}
|
|
20405
|
+
};
|
|
20406
|
+
function getAgentMode(mode) {
|
|
20407
|
+
return AGENT_MODES[mode];
|
|
20408
|
+
}
|
|
20409
|
+
function listAgentModes() {
|
|
20410
|
+
return Object.values(AGENT_MODES);
|
|
20411
|
+
}
|
|
20412
|
+
|
|
20413
|
+
// src/cli/repl/sessions/storage.ts
|
|
20414
|
+
init_paths();
|
|
20415
|
+
var DEFAULT_CONFIG2 = {
|
|
20416
|
+
storageDir: CONFIG_PATHS.sessions,
|
|
20417
|
+
autoSaveInterval: 3e4,
|
|
20418
|
+
maxSessionsPerProject: 20,
|
|
20419
|
+
compressOldSessions: false
|
|
20420
|
+
};
|
|
20421
|
+
var SessionStore = class {
|
|
20422
|
+
config;
|
|
20423
|
+
initialized = false;
|
|
20424
|
+
constructor(config = {}) {
|
|
20425
|
+
this.config = { ...DEFAULT_CONFIG2, ...config };
|
|
20426
|
+
}
|
|
20427
|
+
async ensureInitialized() {
|
|
20428
|
+
if (this.initialized) return;
|
|
20429
|
+
await mkdir(this.config.storageDir, { recursive: true });
|
|
20430
|
+
this.initialized = true;
|
|
20431
|
+
}
|
|
20432
|
+
getSessionFiles(sessionId) {
|
|
20433
|
+
const sessionDir = this.getSessionDir(sessionId);
|
|
20434
|
+
return {
|
|
20435
|
+
metadata: join(sessionDir, "metadata.json"),
|
|
20436
|
+
conversation: join(sessionDir, "conversation.jsonl"),
|
|
20437
|
+
context: join(sessionDir, "context.json")
|
|
20438
|
+
};
|
|
20439
|
+
}
|
|
20440
|
+
getSessionDir(sessionId) {
|
|
20441
|
+
return join(this.config.storageDir, sessionId);
|
|
20442
|
+
}
|
|
20443
|
+
async exists(sessionId) {
|
|
20444
|
+
try {
|
|
20445
|
+
const files = this.getSessionFiles(sessionId);
|
|
20446
|
+
await access(files.metadata);
|
|
20447
|
+
return true;
|
|
20448
|
+
} catch {
|
|
20449
|
+
return false;
|
|
20450
|
+
}
|
|
20451
|
+
}
|
|
20452
|
+
async save(session) {
|
|
20453
|
+
await this.ensureInitialized();
|
|
20454
|
+
const files = this.getSessionFiles(session.id);
|
|
20455
|
+
const sessionDir = this.getSessionDir(session.id);
|
|
20456
|
+
await mkdir(sessionDir, { recursive: true });
|
|
20457
|
+
const metadata = {
|
|
20458
|
+
id: session.id,
|
|
20459
|
+
projectPath: session.projectPath,
|
|
20460
|
+
startedAt: session.startedAt,
|
|
20461
|
+
lastSavedAt: /* @__PURE__ */ new Date(),
|
|
20462
|
+
config: session.config,
|
|
20463
|
+
messageCount: session.messages.length,
|
|
20464
|
+
totalTokens: this.calculateTokens(session),
|
|
20465
|
+
title: this.generateTitle(session),
|
|
20466
|
+
status: "active"
|
|
20467
|
+
};
|
|
20468
|
+
await writeFile(files.metadata, JSON.stringify(metadata, null, 2), "utf-8");
|
|
20469
|
+
const conversationLines = session.messages.map((msg) => {
|
|
20470
|
+
const serialized = {
|
|
20471
|
+
role: msg.role,
|
|
20472
|
+
content: msg.content,
|
|
20473
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
20474
|
+
};
|
|
20475
|
+
return JSON.stringify(serialized);
|
|
20476
|
+
});
|
|
20477
|
+
await writeFile(files.conversation, conversationLines.join("\n"), "utf-8");
|
|
20478
|
+
const context = {
|
|
20479
|
+
tokenUsage: this.calculateTokens(session)
|
|
20480
|
+
};
|
|
20481
|
+
await writeFile(files.context, JSON.stringify(context, null, 2), "utf-8");
|
|
20482
|
+
}
|
|
20483
|
+
async load(sessionId) {
|
|
20484
|
+
await this.ensureInitialized();
|
|
20485
|
+
const files = this.getSessionFiles(sessionId);
|
|
20486
|
+
try {
|
|
20487
|
+
const metadataContent = await readFile(files.metadata, "utf-8");
|
|
20488
|
+
const metadata = JSON.parse(metadataContent);
|
|
20489
|
+
const conversationContent = await readFile(files.conversation, "utf-8");
|
|
20490
|
+
const messages = conversationContent.split("\n").filter((line) => line.trim()).map((line) => {
|
|
20491
|
+
const parsed = JSON.parse(line);
|
|
20492
|
+
return {
|
|
20493
|
+
role: parsed.role,
|
|
20494
|
+
content: parsed.content
|
|
20495
|
+
};
|
|
20496
|
+
});
|
|
20497
|
+
const session = {
|
|
20498
|
+
id: metadata.id,
|
|
20499
|
+
startedAt: new Date(metadata.startedAt),
|
|
20500
|
+
messages,
|
|
20501
|
+
projectPath: metadata.projectPath,
|
|
20502
|
+
config: metadata.config,
|
|
20503
|
+
trustedTools: /* @__PURE__ */ new Set()
|
|
20504
|
+
};
|
|
20505
|
+
return session;
|
|
20506
|
+
} catch {
|
|
20507
|
+
return null;
|
|
20508
|
+
}
|
|
20509
|
+
}
|
|
20510
|
+
async listSessions(projectPath) {
|
|
20511
|
+
await this.ensureInitialized();
|
|
20512
|
+
const sessions = [];
|
|
20513
|
+
try {
|
|
20514
|
+
const entries = await readdir(this.config.storageDir, {
|
|
20515
|
+
withFileTypes: true
|
|
20516
|
+
});
|
|
20517
|
+
for (const entry of entries) {
|
|
20518
|
+
if (!entry.isDirectory()) continue;
|
|
20519
|
+
const metadataPath = join(this.config.storageDir, entry.name, "metadata.json");
|
|
20520
|
+
try {
|
|
20521
|
+
const content = await readFile(metadataPath, "utf-8");
|
|
20522
|
+
const metadata = JSON.parse(content);
|
|
20523
|
+
metadata.startedAt = new Date(metadata.startedAt);
|
|
20524
|
+
metadata.lastSavedAt = new Date(metadata.lastSavedAt);
|
|
20525
|
+
if (!projectPath || metadata.projectPath === projectPath) {
|
|
20526
|
+
sessions.push(metadata);
|
|
20527
|
+
}
|
|
20528
|
+
} catch {
|
|
20529
|
+
continue;
|
|
20530
|
+
}
|
|
20531
|
+
}
|
|
20532
|
+
} catch {
|
|
20533
|
+
return [];
|
|
20534
|
+
}
|
|
20535
|
+
sessions.sort((a, b) => new Date(b.lastSavedAt).getTime() - new Date(a.lastSavedAt).getTime());
|
|
20536
|
+
return sessions;
|
|
20537
|
+
}
|
|
20538
|
+
async delete(sessionId) {
|
|
20539
|
+
await this.ensureInitialized();
|
|
20540
|
+
const sessionDir = this.getSessionDir(sessionId);
|
|
20541
|
+
try {
|
|
20542
|
+
await rm(sessionDir, { recursive: true, force: true });
|
|
20543
|
+
return true;
|
|
20544
|
+
} catch {
|
|
20545
|
+
return false;
|
|
20546
|
+
}
|
|
20547
|
+
}
|
|
20548
|
+
async getMostRecent(projectPath) {
|
|
20549
|
+
const sessions = await this.listSessions(projectPath);
|
|
20550
|
+
return sessions[0] ?? null;
|
|
20551
|
+
}
|
|
20552
|
+
/**
|
|
20553
|
+
* Append messages to an existing session's conversation file
|
|
20554
|
+
* Creates the file if it doesn't exist
|
|
20555
|
+
* @param sessionId - The session ID
|
|
20556
|
+
* @param messages - Messages to append
|
|
20557
|
+
*/
|
|
20558
|
+
async appendMessages(sessionId, messages) {
|
|
20559
|
+
await this.ensureInitialized();
|
|
20560
|
+
const files = this.getSessionFiles(sessionId);
|
|
20561
|
+
const sessionDir = this.getSessionDir(sessionId);
|
|
20562
|
+
await mkdir(sessionDir, { recursive: true });
|
|
20563
|
+
const newLines = messages.map((msg) => {
|
|
20564
|
+
const serialized = {
|
|
20565
|
+
role: msg.role,
|
|
20566
|
+
content: msg.content,
|
|
20567
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
20568
|
+
};
|
|
20569
|
+
return JSON.stringify(serialized);
|
|
20570
|
+
});
|
|
20571
|
+
let existingContent = "";
|
|
20572
|
+
try {
|
|
20573
|
+
existingContent = await readFile(files.conversation, "utf-8");
|
|
20574
|
+
if (existingContent && !existingContent.endsWith("\n")) {
|
|
20575
|
+
existingContent += "\n";
|
|
20576
|
+
}
|
|
20577
|
+
} catch {
|
|
20578
|
+
}
|
|
20579
|
+
await writeFile(files.conversation, existingContent + newLines.join("\n"), "utf-8");
|
|
20580
|
+
}
|
|
20581
|
+
/**
|
|
20582
|
+
* Prune old sessions to keep storage manageable
|
|
20583
|
+
* Keeps the most recent maxSessionsPerProject sessions per project
|
|
20584
|
+
* @param projectPath - Optional project path to prune (prunes all if not specified)
|
|
20585
|
+
* @returns Number of sessions deleted
|
|
20586
|
+
*/
|
|
20587
|
+
async pruneOldSessions(projectPath) {
|
|
20588
|
+
await this.ensureInitialized();
|
|
20589
|
+
const allSessions = await this.listSessions();
|
|
20590
|
+
const sessionsByProject = /* @__PURE__ */ new Map();
|
|
20591
|
+
for (const session of allSessions) {
|
|
20592
|
+
if (projectPath && session.projectPath !== projectPath) {
|
|
20593
|
+
continue;
|
|
20594
|
+
}
|
|
20595
|
+
const existing = sessionsByProject.get(session.projectPath) ?? [];
|
|
20596
|
+
existing.push(session);
|
|
20597
|
+
sessionsByProject.set(session.projectPath, existing);
|
|
20598
|
+
}
|
|
20599
|
+
let deletedCount = 0;
|
|
20600
|
+
for (const [, sessions] of sessionsByProject) {
|
|
20601
|
+
const sessionsToDelete = sessions.slice(this.config.maxSessionsPerProject);
|
|
20602
|
+
for (const session of sessionsToDelete) {
|
|
20603
|
+
const deleted = await this.delete(session.id);
|
|
20604
|
+
if (deleted) {
|
|
20605
|
+
deletedCount++;
|
|
20606
|
+
}
|
|
20607
|
+
}
|
|
20608
|
+
}
|
|
20609
|
+
return deletedCount;
|
|
20610
|
+
}
|
|
20611
|
+
calculateTokens(session) {
|
|
20612
|
+
if (session.contextManager) {
|
|
20613
|
+
const stats = session.contextManager.getUsageStats();
|
|
20614
|
+
return {
|
|
20615
|
+
input: stats.used,
|
|
20616
|
+
output: 0
|
|
20617
|
+
};
|
|
20618
|
+
}
|
|
20619
|
+
let input = 0;
|
|
20620
|
+
let output = 0;
|
|
20621
|
+
for (const msg of session.messages) {
|
|
20622
|
+
const content = typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content);
|
|
20623
|
+
const tokens = Math.ceil(content.length / 4);
|
|
20624
|
+
if (msg.role === "user") {
|
|
20625
|
+
input += tokens;
|
|
20626
|
+
} else {
|
|
20627
|
+
output += tokens;
|
|
20628
|
+
}
|
|
20629
|
+
}
|
|
20630
|
+
return { input, output };
|
|
20631
|
+
}
|
|
20632
|
+
generateTitle(session) {
|
|
20633
|
+
for (const msg of session.messages) {
|
|
20634
|
+
if (msg.role === "user" && typeof msg.content === "string") {
|
|
20635
|
+
const content = msg.content.trim();
|
|
20636
|
+
if (content.length > 10) {
|
|
20637
|
+
const firstLine = content.split("\n")[0] ?? content;
|
|
20638
|
+
return firstLine.length > 50 ? firstLine.slice(0, 47) + "..." : firstLine;
|
|
20639
|
+
}
|
|
20640
|
+
}
|
|
20641
|
+
}
|
|
20642
|
+
return "Untitled session";
|
|
20643
|
+
}
|
|
20644
|
+
};
|
|
20645
|
+
function createSessionStore(config) {
|
|
20646
|
+
return new SessionStore(config);
|
|
20647
|
+
}
|
|
20648
|
+
|
|
20649
|
+
// src/runtime/agent-runtime.ts
|
|
20650
|
+
init_env();
|
|
20651
|
+
init_errors();
|
|
20652
|
+
init_allowed_paths();
|
|
20653
|
+
function levenshtein(a, b) {
|
|
20654
|
+
if (a === b) return 0;
|
|
20655
|
+
if (a.length === 0) return b.length;
|
|
20656
|
+
if (b.length === 0) return a.length;
|
|
20657
|
+
const row = Array.from({ length: b.length + 1 }, (_, i) => i);
|
|
20658
|
+
for (let i = 1; i <= a.length; i++) {
|
|
20659
|
+
let prev = i;
|
|
20660
|
+
for (let j = 1; j <= b.length; j++) {
|
|
20661
|
+
const cost = a[i - 1] === b[j - 1] ? 0 : 1;
|
|
20662
|
+
const val = Math.min(
|
|
20663
|
+
row[j] + 1,
|
|
20664
|
+
// deletion
|
|
20665
|
+
prev + 1,
|
|
20666
|
+
// insertion
|
|
20667
|
+
row[j - 1] + cost
|
|
20668
|
+
// substitution
|
|
20669
|
+
);
|
|
20670
|
+
row[j - 1] = prev;
|
|
20671
|
+
prev = val;
|
|
20672
|
+
}
|
|
20673
|
+
row[b.length] = prev;
|
|
20674
|
+
}
|
|
20675
|
+
return row[b.length];
|
|
20676
|
+
}
|
|
20677
|
+
|
|
20678
|
+
// src/utils/file-suggestions.ts
|
|
20679
|
+
var MAX_DIR_ENTRIES = 200;
|
|
20680
|
+
var MAX_SUGGESTIONS = 5;
|
|
20681
|
+
var DEFAULT_EXCLUDE_DIRS = /* @__PURE__ */ new Set([
|
|
20682
|
+
"node_modules",
|
|
20683
|
+
".git",
|
|
20684
|
+
"dist",
|
|
20685
|
+
"build",
|
|
20686
|
+
"coverage",
|
|
20687
|
+
".next",
|
|
20688
|
+
"vendor",
|
|
20689
|
+
"__pycache__",
|
|
20690
|
+
".venv",
|
|
20691
|
+
"venv",
|
|
20692
|
+
".tox",
|
|
20693
|
+
".pytest_cache",
|
|
20694
|
+
".mypy_cache",
|
|
20695
|
+
".ruff_cache",
|
|
20696
|
+
".idea",
|
|
20697
|
+
".vscode",
|
|
20698
|
+
".DS_Store"
|
|
20699
|
+
]);
|
|
20700
|
+
var DEFAULT_FIND_OPTIONS = {
|
|
20701
|
+
maxDepth: 8,
|
|
20702
|
+
timeoutMs: 3e3,
|
|
20703
|
+
includeHidden: true,
|
|
20704
|
+
excludeDirs: DEFAULT_EXCLUDE_DIRS,
|
|
20705
|
+
maxResults: 5,
|
|
20706
|
+
type: "file"
|
|
20707
|
+
};
|
|
20708
|
+
async function findFileRecursive(rootDir, target, options = {}) {
|
|
20709
|
+
const opts = { ...DEFAULT_FIND_OPTIONS, ...options };
|
|
20710
|
+
const targetLower = target.toLowerCase();
|
|
20711
|
+
const results = [];
|
|
20712
|
+
const startTime = Date.now();
|
|
20713
|
+
const isTimedOut = () => Date.now() - startTime > opts.timeoutMs;
|
|
20714
|
+
const queue = [[path17__default.resolve(rootDir), 0]];
|
|
20715
|
+
const visited = /* @__PURE__ */ new Set();
|
|
20716
|
+
while (queue.length > 0 && results.length < opts.maxResults) {
|
|
20717
|
+
if (isTimedOut()) break;
|
|
20718
|
+
const [currentDir, depth] = queue.shift();
|
|
20719
|
+
if (visited.has(currentDir)) continue;
|
|
20720
|
+
visited.add(currentDir);
|
|
20721
|
+
if (depth > opts.maxDepth) continue;
|
|
20722
|
+
try {
|
|
20723
|
+
const entries = await fs16__default.readdir(currentDir, { withFileTypes: true });
|
|
20724
|
+
for (const entry of entries) {
|
|
20725
|
+
if (isTimedOut()) break;
|
|
20726
|
+
const entryName = entry.name;
|
|
20727
|
+
const entryPath = path17__default.join(currentDir, entryName);
|
|
20728
|
+
if (!opts.includeHidden && entryName.startsWith(".")) continue;
|
|
20729
|
+
if (entry.isDirectory() && opts.excludeDirs.has(entryName)) continue;
|
|
20730
|
+
const isMatch = opts.type === "file" && entry.isFile() || opts.type === "directory" && entry.isDirectory() || opts.type === "both";
|
|
20731
|
+
if (isMatch) {
|
|
20732
|
+
const entryNameLower = entryName.toLowerCase();
|
|
20733
|
+
let distance;
|
|
20734
|
+
if (entryNameLower === targetLower) {
|
|
20735
|
+
distance = 0;
|
|
20736
|
+
} else {
|
|
20737
|
+
distance = levenshtein(targetLower, entryNameLower);
|
|
20738
|
+
}
|
|
20739
|
+
const maxDistance = Math.max(target.length * 0.6, 3);
|
|
20740
|
+
if (distance <= maxDistance) {
|
|
20741
|
+
results.push({ path: entryPath, distance });
|
|
20742
|
+
}
|
|
20743
|
+
}
|
|
20744
|
+
if (entry.isDirectory() && !opts.excludeDirs.has(entryName)) {
|
|
20745
|
+
queue.push([entryPath, depth + 1]);
|
|
20746
|
+
}
|
|
20747
|
+
}
|
|
20748
|
+
} catch {
|
|
20749
|
+
continue;
|
|
20750
|
+
}
|
|
20751
|
+
}
|
|
20752
|
+
return results.sort((a, b) => a.distance - b.distance).slice(0, opts.maxResults);
|
|
20753
|
+
}
|
|
20754
|
+
async function suggestSimilarFilesDeep(missingPath, rootDir = process.cwd(), options) {
|
|
20755
|
+
const fastResults = await suggestSimilarFiles(missingPath, {
|
|
20756
|
+
maxResults: MAX_SUGGESTIONS
|
|
20757
|
+
});
|
|
20758
|
+
if (fastResults.length > 0) {
|
|
20759
|
+
return fastResults;
|
|
20760
|
+
}
|
|
20761
|
+
const absPath = path17__default.resolve(missingPath);
|
|
20762
|
+
const target = path17__default.basename(absPath);
|
|
20763
|
+
return findFileRecursive(rootDir, target, options);
|
|
20764
|
+
}
|
|
20765
|
+
async function suggestSimilarDirsDeep(missingPath, rootDir = process.cwd(), options) {
|
|
20766
|
+
const absPath = path17__default.resolve(missingPath);
|
|
20767
|
+
const target = path17__default.basename(absPath);
|
|
20768
|
+
const parentDir = path17__default.dirname(absPath);
|
|
20769
|
+
try {
|
|
20770
|
+
const entries = await fs16__default.readdir(parentDir, { withFileTypes: true });
|
|
20771
|
+
const dirs = entries.filter((e) => e.isDirectory());
|
|
20772
|
+
const scored = dirs.map((d) => ({
|
|
20773
|
+
path: path17__default.join(parentDir, d.name),
|
|
20774
|
+
distance: levenshtein(target.toLowerCase(), d.name.toLowerCase())
|
|
20775
|
+
})).filter((s) => s.distance <= Math.max(target.length * 0.6, 3)).sort((a, b) => a.distance - b.distance).slice(0, options?.maxResults ?? MAX_SUGGESTIONS);
|
|
20776
|
+
if (scored.length > 0) {
|
|
20777
|
+
return scored;
|
|
20778
|
+
}
|
|
20779
|
+
} catch {
|
|
20780
|
+
}
|
|
20781
|
+
return findFileRecursive(rootDir, target, { ...options, type: "directory" });
|
|
20782
|
+
}
|
|
20783
|
+
async function suggestSimilarFiles(missingPath, options) {
|
|
20784
|
+
const absPath = path17__default.resolve(missingPath);
|
|
20785
|
+
const dir = path17__default.dirname(absPath);
|
|
20786
|
+
const target = path17__default.basename(absPath);
|
|
20787
|
+
const maxResults = options?.maxResults;
|
|
20788
|
+
try {
|
|
20789
|
+
const entries = await fs16__default.readdir(dir);
|
|
20790
|
+
const limited = entries.slice(0, MAX_DIR_ENTRIES);
|
|
20791
|
+
const scored = limited.map((name) => ({
|
|
20792
|
+
path: path17__default.join(dir, name),
|
|
20793
|
+
distance: levenshtein(target.toLowerCase(), name.toLowerCase())
|
|
20794
|
+
})).filter((s) => s.distance <= Math.max(target.length * 0.6, 3)).sort((a, b) => a.distance - b.distance);
|
|
20795
|
+
return scored.slice(0, maxResults);
|
|
19494
20796
|
} catch {
|
|
19495
20797
|
return [];
|
|
19496
20798
|
}
|
|
@@ -20290,7 +21592,7 @@ function escapeRegex(str) {
|
|
|
20290
21592
|
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
20291
21593
|
}
|
|
20292
21594
|
init_errors();
|
|
20293
|
-
var
|
|
21595
|
+
var DEFAULT_TIMEOUT_MS2 = 12e4;
|
|
20294
21596
|
var MAX_OUTPUT_SIZE = 1024 * 1024;
|
|
20295
21597
|
var DANGEROUS_PATTERNS_FULL = [
|
|
20296
21598
|
{ pattern: /\brm\s+-rf\s+\/(?!\w)/, rule: "rm -rf on root filesystem" },
|
|
@@ -20412,7 +21714,7 @@ Examples:
|
|
|
20412
21714
|
}
|
|
20413
21715
|
}
|
|
20414
21716
|
const startTime = performance.now();
|
|
20415
|
-
const timeoutMs = timeout ??
|
|
21717
|
+
const timeoutMs = timeout ?? DEFAULT_TIMEOUT_MS2;
|
|
20416
21718
|
const { CommandHeartbeat: CommandHeartbeat2 } = await Promise.resolve().then(() => (init_heartbeat(), heartbeat_exports));
|
|
20417
21719
|
const heartbeat = new CommandHeartbeat2({
|
|
20418
21720
|
onUpdate: (stats) => {
|
|
@@ -21071,93 +22373,45 @@ var SimpleAutoCommitSchema = z.object({
|
|
|
21071
22373
|
message: z.string().optional()
|
|
21072
22374
|
});
|
|
21073
22375
|
var simpleAutoCommitTool = defineTool({
|
|
21074
|
-
name: "simpleAutoCommit",
|
|
21075
|
-
description: "Auto-commit staged changes with generated message",
|
|
21076
|
-
category: "git",
|
|
21077
|
-
parameters: SimpleAutoCommitSchema,
|
|
21078
|
-
async execute(input) {
|
|
21079
|
-
try {
|
|
21080
|
-
try {
|
|
21081
|
-
execSync("git diff --cached --quiet", { cwd: process.cwd(), stdio: "ignore" });
|
|
21082
|
-
return {
|
|
21083
|
-
stdout: "",
|
|
21084
|
-
stderr: "No staged changes to commit",
|
|
21085
|
-
exitCode: 1,
|
|
21086
|
-
duration: 0
|
|
21087
|
-
};
|
|
21088
|
-
} catch {
|
|
21089
|
-
}
|
|
21090
|
-
const message = input.message || generateSimpleCommitMessage();
|
|
21091
|
-
execSync(`git commit -m "${message}"`, {
|
|
21092
|
-
encoding: "utf-8",
|
|
21093
|
-
cwd: process.cwd(),
|
|
21094
|
-
stdio: "pipe"
|
|
21095
|
-
});
|
|
21096
|
-
return {
|
|
21097
|
-
stdout: `\u2713 Committed: ${message}`,
|
|
21098
|
-
stderr: "",
|
|
21099
|
-
exitCode: 0,
|
|
21100
|
-
duration: 0
|
|
21101
|
-
};
|
|
21102
|
-
} catch (error) {
|
|
21103
|
-
return {
|
|
21104
|
-
stdout: "",
|
|
21105
|
-
stderr: error instanceof Error ? error.message : "Commit failed",
|
|
21106
|
-
exitCode: 1,
|
|
21107
|
-
duration: 0
|
|
21108
|
-
};
|
|
21109
|
-
}
|
|
21110
|
-
}
|
|
21111
|
-
});
|
|
21112
|
-
var gitSimpleTools = [checkProtectedBranchTool, simpleAutoCommitTool];
|
|
21113
|
-
|
|
21114
|
-
// src/cli/repl/agents/manager.ts
|
|
21115
|
-
init_logger();
|
|
21116
|
-
var AGENT_NAMES = {
|
|
21117
|
-
explore: "Explorer",
|
|
21118
|
-
plan: "Planner",
|
|
21119
|
-
test: "Tester",
|
|
21120
|
-
debug: "Debugger",
|
|
21121
|
-
review: "Reviewer",
|
|
21122
|
-
architect: "Architect",
|
|
21123
|
-
security: "Security Auditor",
|
|
21124
|
-
tdd: "TDD Guide",
|
|
21125
|
-
refactor: "Refactorer",
|
|
21126
|
-
e2e: "E2E Tester",
|
|
21127
|
-
docs: "Docs Writer",
|
|
21128
|
-
database: "Database Engineer"
|
|
21129
|
-
};
|
|
21130
|
-
var AGENT_DESCRIPTIONS = {
|
|
21131
|
-
explore: "Search the codebase to answer questions and gather information",
|
|
21132
|
-
plan: "Design implementation approaches and create detailed plans",
|
|
21133
|
-
test: "Write and run tests to ensure code quality",
|
|
21134
|
-
debug: "Analyze errors and fix issues",
|
|
21135
|
-
review: "Review code for quality and best practices",
|
|
21136
|
-
architect: "Design system architecture and create architectural decision records",
|
|
21137
|
-
security: "Audit code for security vulnerabilities using OWASP Top 10",
|
|
21138
|
-
tdd: "Drive development with test-first methodology and RED-GREEN-REFACTOR cycle",
|
|
21139
|
-
refactor: "Improve code structure and quality without changing behavior",
|
|
21140
|
-
e2e: "Write and run end-to-end tests covering full user workflows",
|
|
21141
|
-
docs: "Generate and maintain documentation for code, APIs, and architecture",
|
|
21142
|
-
database: "Design database schemas, write migrations, and optimize queries"
|
|
21143
|
-
};
|
|
21144
|
-
|
|
21145
|
-
// src/agents/provider-bridge.ts
|
|
21146
|
-
var agentProvider = null;
|
|
21147
|
-
var agentToolRegistry = null;
|
|
21148
|
-
function getAgentProvider() {
|
|
21149
|
-
return agentProvider;
|
|
21150
|
-
}
|
|
21151
|
-
function getAgentToolRegistry() {
|
|
21152
|
-
return agentToolRegistry;
|
|
21153
|
-
}
|
|
21154
|
-
function getAgentManager() {
|
|
21155
|
-
{
|
|
21156
|
-
return null;
|
|
22376
|
+
name: "simpleAutoCommit",
|
|
22377
|
+
description: "Auto-commit staged changes with generated message",
|
|
22378
|
+
category: "git",
|
|
22379
|
+
parameters: SimpleAutoCommitSchema,
|
|
22380
|
+
async execute(input) {
|
|
22381
|
+
try {
|
|
22382
|
+
try {
|
|
22383
|
+
execSync("git diff --cached --quiet", { cwd: process.cwd(), stdio: "ignore" });
|
|
22384
|
+
return {
|
|
22385
|
+
stdout: "",
|
|
22386
|
+
stderr: "No staged changes to commit",
|
|
22387
|
+
exitCode: 1,
|
|
22388
|
+
duration: 0
|
|
22389
|
+
};
|
|
22390
|
+
} catch {
|
|
22391
|
+
}
|
|
22392
|
+
const message = input.message || generateSimpleCommitMessage();
|
|
22393
|
+
execSync(`git commit -m "${message}"`, {
|
|
22394
|
+
encoding: "utf-8",
|
|
22395
|
+
cwd: process.cwd(),
|
|
22396
|
+
stdio: "pipe"
|
|
22397
|
+
});
|
|
22398
|
+
return {
|
|
22399
|
+
stdout: `\u2713 Committed: ${message}`,
|
|
22400
|
+
stderr: "",
|
|
22401
|
+
exitCode: 0,
|
|
22402
|
+
duration: 0
|
|
22403
|
+
};
|
|
22404
|
+
} catch (error) {
|
|
22405
|
+
return {
|
|
22406
|
+
stdout: "",
|
|
22407
|
+
stderr: error instanceof Error ? error.message : "Commit failed",
|
|
22408
|
+
exitCode: 1,
|
|
22409
|
+
duration: 0
|
|
22410
|
+
};
|
|
22411
|
+
}
|
|
21157
22412
|
}
|
|
21158
|
-
}
|
|
21159
|
-
|
|
21160
|
-
// src/tools/simple-agent.ts
|
|
22413
|
+
});
|
|
22414
|
+
var gitSimpleTools = [checkProtectedBranchTool, simpleAutoCommitTool];
|
|
21161
22415
|
var AGENT_TYPES = [
|
|
21162
22416
|
"explore",
|
|
21163
22417
|
"plan",
|
|
@@ -21270,7 +22524,7 @@ var checkAgentCapabilityTool = defineTool({
|
|
|
21270
22524
|
async execute() {
|
|
21271
22525
|
const provider = getAgentProvider();
|
|
21272
22526
|
const toolRegistry = getAgentToolRegistry();
|
|
21273
|
-
const isReady = provider !== null;
|
|
22527
|
+
const isReady = provider !== null && toolRegistry !== null;
|
|
21274
22528
|
const agentTypes = Object.entries(AGENT_DESCRIPTIONS).map(([type, description]) => ({
|
|
21275
22529
|
type,
|
|
21276
22530
|
name: AGENT_NAMES[type],
|
|
@@ -21284,12 +22538,12 @@ var checkAgentCapabilityTool = defineTool({
|
|
|
21284
22538
|
ready: isReady,
|
|
21285
22539
|
availableTypes: agentTypes,
|
|
21286
22540
|
features: {
|
|
21287
|
-
taskDelegation: "requires provider initialization",
|
|
21288
|
-
parallelSpawn: "requires provider initialization",
|
|
21289
|
-
multiTurnToolUse: "requires provider initialization",
|
|
21290
|
-
specializedAgents: "requires provider initialization"
|
|
22541
|
+
taskDelegation: isReady ? "ready" : "requires provider initialization",
|
|
22542
|
+
parallelSpawn: isReady ? "ready" : "requires provider initialization",
|
|
22543
|
+
multiTurnToolUse: isReady ? "ready" : "requires provider initialization",
|
|
22544
|
+
specializedAgents: isReady ? "ready" : "requires provider initialization"
|
|
21291
22545
|
},
|
|
21292
|
-
status: "Provider not initialized. Call setAgentProvider() during startup."
|
|
22546
|
+
status: isReady ? "Multi-agent system is ready with 12 specialized agent types." : "Provider not initialized. Call setAgentProvider() during startup."
|
|
21293
22547
|
}),
|
|
21294
22548
|
stderr: "",
|
|
21295
22549
|
exitCode: 0,
|
|
@@ -30161,7 +31415,7 @@ var HTTPTransport = class {
|
|
|
30161
31415
|
|
|
30162
31416
|
// src/mcp/transport/sse.ts
|
|
30163
31417
|
init_errors2();
|
|
30164
|
-
var
|
|
31418
|
+
var DEFAULT_CONFIG3 = {
|
|
30165
31419
|
initialReconnectDelay: 1e3,
|
|
30166
31420
|
maxReconnectDelay: 3e4,
|
|
30167
31421
|
maxReconnectAttempts: 10
|
|
@@ -30177,7 +31431,7 @@ var SSETransport = class {
|
|
|
30177
31431
|
errorHandler = null;
|
|
30178
31432
|
closeHandler = null;
|
|
30179
31433
|
constructor(config) {
|
|
30180
|
-
this.config = { ...
|
|
31434
|
+
this.config = { ...DEFAULT_CONFIG3, ...config };
|
|
30181
31435
|
}
|
|
30182
31436
|
/**
|
|
30183
31437
|
* Connect to the SSE endpoint
|
|
@@ -30646,6 +31900,235 @@ function getMCPServerManager() {
|
|
|
30646
31900
|
|
|
30647
31901
|
// src/mcp/tools.ts
|
|
30648
31902
|
init_errors2();
|
|
31903
|
+
var DEFAULT_OPTIONS = {
|
|
31904
|
+
namePrefix: "mcp",
|
|
31905
|
+
category: "deploy",
|
|
31906
|
+
requestTimeout: 6e4
|
|
31907
|
+
};
|
|
31908
|
+
function buildMcpToolDescription(serverName, tool) {
|
|
31909
|
+
const base = tool.description || `Tool '${tool.name}' exposed by MCP server '${serverName}'`;
|
|
31910
|
+
const lowerServer = serverName.toLowerCase();
|
|
31911
|
+
if (lowerServer.includes("atlassian") || lowerServer.includes("jira") || lowerServer.includes("confluence")) {
|
|
31912
|
+
return `${base}. Use this MCP tool for Atlassian/Jira/Confluence data. Prefer it over direct web_fetch or http_fetch for Atlassian content.`;
|
|
31913
|
+
}
|
|
31914
|
+
return `${base}. Exposed by MCP server '${serverName}'. Prefer this MCP tool over generic web/http fetch when accessing data from that connected service.`;
|
|
31915
|
+
}
|
|
31916
|
+
function jsonSchemaToZod(schema) {
|
|
31917
|
+
if (schema.enum && Array.isArray(schema.enum)) {
|
|
31918
|
+
const values = schema.enum;
|
|
31919
|
+
if (values.length > 0 && values.every((v) => typeof v === "string")) {
|
|
31920
|
+
return z.enum(values);
|
|
31921
|
+
}
|
|
31922
|
+
const literals = values.map((v) => z.literal(v));
|
|
31923
|
+
if (literals.length < 2) {
|
|
31924
|
+
return literals[0] ?? z.any();
|
|
31925
|
+
}
|
|
31926
|
+
return z.union(literals);
|
|
31927
|
+
}
|
|
31928
|
+
if (schema.const !== void 0) {
|
|
31929
|
+
return z.literal(schema.const);
|
|
31930
|
+
}
|
|
31931
|
+
if (schema.oneOf && Array.isArray(schema.oneOf)) {
|
|
31932
|
+
const schemas = schema.oneOf.map(jsonSchemaToZod);
|
|
31933
|
+
if (schemas.length >= 2) {
|
|
31934
|
+
return z.union(schemas);
|
|
31935
|
+
}
|
|
31936
|
+
return schemas[0] ?? z.unknown();
|
|
31937
|
+
}
|
|
31938
|
+
if (schema.anyOf && Array.isArray(schema.anyOf)) {
|
|
31939
|
+
const schemas = schema.anyOf.map(jsonSchemaToZod);
|
|
31940
|
+
if (schemas.length >= 2) {
|
|
31941
|
+
return z.union(schemas);
|
|
31942
|
+
}
|
|
31943
|
+
return schemas[0] ?? z.unknown();
|
|
31944
|
+
}
|
|
31945
|
+
if (schema.allOf && Array.isArray(schema.allOf)) {
|
|
31946
|
+
const schemas = schema.allOf.map(jsonSchemaToZod);
|
|
31947
|
+
return schemas.reduce((acc, s) => z.intersection(acc, s));
|
|
31948
|
+
}
|
|
31949
|
+
const type = schema.type;
|
|
31950
|
+
const makeNullable = (s) => {
|
|
31951
|
+
if (schema.nullable === true) return s.nullable();
|
|
31952
|
+
return s;
|
|
31953
|
+
};
|
|
31954
|
+
switch (type) {
|
|
31955
|
+
case "string": {
|
|
31956
|
+
let s = z.string();
|
|
31957
|
+
if (schema.format) {
|
|
31958
|
+
switch (schema.format) {
|
|
31959
|
+
case "uri":
|
|
31960
|
+
case "url":
|
|
31961
|
+
s = z.string().url();
|
|
31962
|
+
break;
|
|
31963
|
+
case "email":
|
|
31964
|
+
s = z.string().email();
|
|
31965
|
+
break;
|
|
31966
|
+
case "date-time":
|
|
31967
|
+
case "datetime":
|
|
31968
|
+
s = z.string().datetime();
|
|
31969
|
+
break;
|
|
31970
|
+
}
|
|
31971
|
+
}
|
|
31972
|
+
if (typeof schema.minLength === "number") s = s.min(schema.minLength);
|
|
31973
|
+
if (typeof schema.maxLength === "number") s = s.max(schema.maxLength);
|
|
31974
|
+
return makeNullable(s);
|
|
31975
|
+
}
|
|
31976
|
+
case "number": {
|
|
31977
|
+
let n = z.number();
|
|
31978
|
+
if (typeof schema.minimum === "number") n = n.min(schema.minimum);
|
|
31979
|
+
if (typeof schema.maximum === "number") n = n.max(schema.maximum);
|
|
31980
|
+
if (typeof schema.exclusiveMinimum === "number") n = n.gt(schema.exclusiveMinimum);
|
|
31981
|
+
if (typeof schema.exclusiveMaximum === "number") n = n.lt(schema.exclusiveMaximum);
|
|
31982
|
+
return makeNullable(n);
|
|
31983
|
+
}
|
|
31984
|
+
case "integer": {
|
|
31985
|
+
let n = z.number().int();
|
|
31986
|
+
if (typeof schema.minimum === "number") n = n.min(schema.minimum);
|
|
31987
|
+
if (typeof schema.maximum === "number") n = n.max(schema.maximum);
|
|
31988
|
+
return makeNullable(n);
|
|
31989
|
+
}
|
|
31990
|
+
case "boolean":
|
|
31991
|
+
return makeNullable(z.boolean());
|
|
31992
|
+
case "null":
|
|
31993
|
+
return z.null();
|
|
31994
|
+
case "array":
|
|
31995
|
+
if (schema.items) {
|
|
31996
|
+
const itemSchema = jsonSchemaToZod(schema.items);
|
|
31997
|
+
let arr = z.array(itemSchema);
|
|
31998
|
+
if (typeof schema.minItems === "number") arr = arr.min(schema.minItems);
|
|
31999
|
+
if (typeof schema.maxItems === "number") arr = arr.max(schema.maxItems);
|
|
32000
|
+
return makeNullable(arr);
|
|
32001
|
+
}
|
|
32002
|
+
return makeNullable(z.array(z.unknown()));
|
|
32003
|
+
case "object": {
|
|
32004
|
+
const properties = schema.properties;
|
|
32005
|
+
const required = schema.required;
|
|
32006
|
+
if (!properties) {
|
|
32007
|
+
return makeNullable(z.record(z.string(), z.unknown()));
|
|
32008
|
+
}
|
|
32009
|
+
const shape = {};
|
|
32010
|
+
for (const [key, propSchema] of Object.entries(properties)) {
|
|
32011
|
+
let fieldSchema = jsonSchemaToZod(propSchema);
|
|
32012
|
+
if (!required?.includes(key)) {
|
|
32013
|
+
fieldSchema = fieldSchema.optional();
|
|
32014
|
+
}
|
|
32015
|
+
if (propSchema.description && typeof propSchema.description === "string") {
|
|
32016
|
+
fieldSchema = fieldSchema.describe(propSchema.description);
|
|
32017
|
+
}
|
|
32018
|
+
shape[key] = fieldSchema;
|
|
32019
|
+
}
|
|
32020
|
+
return makeNullable(z.object(shape));
|
|
32021
|
+
}
|
|
32022
|
+
default:
|
|
32023
|
+
if (Array.isArray(schema.type)) {
|
|
32024
|
+
const types = schema.type;
|
|
32025
|
+
if (types.includes("null")) {
|
|
32026
|
+
const nonNullType = types.find((t) => t !== "null");
|
|
32027
|
+
if (nonNullType) {
|
|
32028
|
+
return jsonSchemaToZod({ ...schema, type: nonNullType }).nullable();
|
|
32029
|
+
}
|
|
32030
|
+
}
|
|
32031
|
+
}
|
|
32032
|
+
return z.unknown();
|
|
32033
|
+
}
|
|
32034
|
+
}
|
|
32035
|
+
function createToolParametersSchema(tool) {
|
|
32036
|
+
const schema = tool.inputSchema;
|
|
32037
|
+
if (!schema || schema.type !== "object") {
|
|
32038
|
+
return z.object({});
|
|
32039
|
+
}
|
|
32040
|
+
return jsonSchemaToZod(schema);
|
|
32041
|
+
}
|
|
32042
|
+
function formatToolResult(result) {
|
|
32043
|
+
if (result.isError) {
|
|
32044
|
+
throw new Error(result.content.map((c) => c.text || "").join("\n"));
|
|
32045
|
+
}
|
|
32046
|
+
return result.content.map((item) => {
|
|
32047
|
+
switch (item.type) {
|
|
32048
|
+
case "text":
|
|
32049
|
+
return item.text || "";
|
|
32050
|
+
case "image":
|
|
32051
|
+
return `[Image: ${item.mimeType || "unknown"}]`;
|
|
32052
|
+
case "resource":
|
|
32053
|
+
return `[Resource: ${item.resource?.uri || "unknown"}]`;
|
|
32054
|
+
default:
|
|
32055
|
+
return "";
|
|
32056
|
+
}
|
|
32057
|
+
}).filter(Boolean).join("\n");
|
|
32058
|
+
}
|
|
32059
|
+
function createToolName(serverName, toolName, prefix) {
|
|
32060
|
+
return `${prefix}_${serverName}_${toolName}`.replace(/[^a-zA-Z0-9_]/g, "_");
|
|
32061
|
+
}
|
|
32062
|
+
function wrapMCPTool(tool, serverName, client, options = {}) {
|
|
32063
|
+
const opts = { ...DEFAULT_OPTIONS, ...options };
|
|
32064
|
+
const wrappedName = createToolName(serverName, tool.name, opts.namePrefix);
|
|
32065
|
+
const parametersSchema = createToolParametersSchema(tool);
|
|
32066
|
+
const cocoTool = {
|
|
32067
|
+
name: wrappedName,
|
|
32068
|
+
description: buildMcpToolDescription(serverName, tool),
|
|
32069
|
+
category: opts.category,
|
|
32070
|
+
parameters: parametersSchema,
|
|
32071
|
+
execute: async (params) => {
|
|
32072
|
+
const timeout = opts.requestTimeout;
|
|
32073
|
+
try {
|
|
32074
|
+
const result = await Promise.race([
|
|
32075
|
+
client.callTool({
|
|
32076
|
+
name: tool.name,
|
|
32077
|
+
arguments: params
|
|
32078
|
+
}),
|
|
32079
|
+
new Promise((_, reject) => {
|
|
32080
|
+
setTimeout(() => {
|
|
32081
|
+
reject(new MCPTimeoutError(`Tool '${tool.name}' timed out after ${timeout}ms`));
|
|
32082
|
+
}, timeout);
|
|
32083
|
+
})
|
|
32084
|
+
]);
|
|
32085
|
+
return formatToolResult(result);
|
|
32086
|
+
} catch (error) {
|
|
32087
|
+
if (error instanceof MCPError) {
|
|
32088
|
+
throw error;
|
|
32089
|
+
}
|
|
32090
|
+
throw new MCPError(
|
|
32091
|
+
-32603,
|
|
32092
|
+
`Tool execution failed: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
32093
|
+
);
|
|
32094
|
+
}
|
|
32095
|
+
}
|
|
32096
|
+
};
|
|
32097
|
+
const wrapped = {
|
|
32098
|
+
originalTool: tool,
|
|
32099
|
+
serverName,
|
|
32100
|
+
wrappedName
|
|
32101
|
+
};
|
|
32102
|
+
return { tool: cocoTool, wrapped };
|
|
32103
|
+
}
|
|
32104
|
+
function wrapMCPTools(tools, serverName, client, options = {}) {
|
|
32105
|
+
const cocoTools = [];
|
|
32106
|
+
const wrappedTools = [];
|
|
32107
|
+
for (const tool of tools) {
|
|
32108
|
+
const { tool: cocoTool, wrapped } = wrapMCPTool(tool, serverName, client, options);
|
|
32109
|
+
cocoTools.push(cocoTool);
|
|
32110
|
+
wrappedTools.push(wrapped);
|
|
32111
|
+
}
|
|
32112
|
+
return { tools: cocoTools, wrapped: wrappedTools };
|
|
32113
|
+
}
|
|
32114
|
+
async function createToolsFromMCPServer(serverName, client, options = {}) {
|
|
32115
|
+
if (!client.isConnected()) {
|
|
32116
|
+
await client.initialize({
|
|
32117
|
+
protocolVersion: "2024-11-05",
|
|
32118
|
+
capabilities: {},
|
|
32119
|
+
clientInfo: { name: "coco-mcp-client", version: "0.2.0" }
|
|
32120
|
+
});
|
|
32121
|
+
}
|
|
32122
|
+
const { tools } = await client.listTools();
|
|
32123
|
+
return wrapMCPTools(tools, serverName, client, options);
|
|
32124
|
+
}
|
|
32125
|
+
async function registerMCPTools(registry, serverName, client, options = {}) {
|
|
32126
|
+
const { tools, wrapped } = await createToolsFromMCPServer(serverName, client, options);
|
|
32127
|
+
for (const tool of tools) {
|
|
32128
|
+
registry.register(tool);
|
|
32129
|
+
}
|
|
32130
|
+
return wrapped;
|
|
32131
|
+
}
|
|
30649
32132
|
|
|
30650
32133
|
// src/tools/mcp.ts
|
|
30651
32134
|
async function loadConfiguredServers(projectPath) {
|
|
@@ -30742,6 +32225,10 @@ built-in MCP OAuth browser login flow. Do not ask the user for raw tokens when t
|
|
|
30742
32225
|
await manager.stopServer(target.name);
|
|
30743
32226
|
}
|
|
30744
32227
|
const connection = await manager.startServer(target);
|
|
32228
|
+
const toolRegistry = getAgentToolRegistry();
|
|
32229
|
+
if (toolRegistry) {
|
|
32230
|
+
await registerMCPTools(toolRegistry, connection.name, connection.client);
|
|
32231
|
+
}
|
|
30745
32232
|
let tools;
|
|
30746
32233
|
if (includeTools) {
|
|
30747
32234
|
try {
|
|
@@ -30910,12 +32397,497 @@ function createFullToolRegistry() {
|
|
|
30910
32397
|
registerAllTools(registry);
|
|
30911
32398
|
return registry;
|
|
30912
32399
|
}
|
|
32400
|
+
var InMemoryEventLog = class {
|
|
32401
|
+
events = [];
|
|
32402
|
+
record(type, data = {}) {
|
|
32403
|
+
const event = {
|
|
32404
|
+
id: randomUUID(),
|
|
32405
|
+
type,
|
|
32406
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
32407
|
+
data
|
|
32408
|
+
};
|
|
32409
|
+
this.events.push(event);
|
|
32410
|
+
return event;
|
|
32411
|
+
}
|
|
32412
|
+
list() {
|
|
32413
|
+
return [...this.events];
|
|
32414
|
+
}
|
|
32415
|
+
count() {
|
|
32416
|
+
return this.events.length;
|
|
32417
|
+
}
|
|
32418
|
+
clear() {
|
|
32419
|
+
this.events = [];
|
|
32420
|
+
}
|
|
32421
|
+
};
|
|
32422
|
+
var FileEventLog = class {
|
|
32423
|
+
constructor(filePath) {
|
|
32424
|
+
this.filePath = filePath;
|
|
32425
|
+
try {
|
|
32426
|
+
mkdirSync(dirname(filePath), { recursive: true });
|
|
32427
|
+
} catch {
|
|
32428
|
+
this.writable = false;
|
|
32429
|
+
}
|
|
32430
|
+
}
|
|
32431
|
+
filePath;
|
|
32432
|
+
memory = new InMemoryEventLog();
|
|
32433
|
+
writable = true;
|
|
32434
|
+
record(type, data = {}) {
|
|
32435
|
+
const event = this.memory.record(type, data);
|
|
32436
|
+
if (this.writable) {
|
|
32437
|
+
try {
|
|
32438
|
+
appendFileSync(this.filePath, JSON.stringify(event) + "\n", "utf-8");
|
|
32439
|
+
} catch {
|
|
32440
|
+
this.writable = false;
|
|
32441
|
+
}
|
|
32442
|
+
}
|
|
32443
|
+
return event;
|
|
32444
|
+
}
|
|
32445
|
+
list() {
|
|
32446
|
+
if (!this.writable) return this.memory.list();
|
|
32447
|
+
try {
|
|
32448
|
+
const raw = readFileSync(this.filePath, "utf-8");
|
|
32449
|
+
return raw.split("\n").filter(Boolean).flatMap((line) => {
|
|
32450
|
+
try {
|
|
32451
|
+
return [JSON.parse(line)];
|
|
32452
|
+
} catch {
|
|
32453
|
+
return [];
|
|
32454
|
+
}
|
|
32455
|
+
});
|
|
32456
|
+
} catch {
|
|
32457
|
+
return this.memory.list();
|
|
32458
|
+
}
|
|
32459
|
+
}
|
|
32460
|
+
count() {
|
|
32461
|
+
return this.list().length;
|
|
32462
|
+
}
|
|
32463
|
+
clear() {
|
|
32464
|
+
this.memory.clear();
|
|
32465
|
+
if (this.writable) {
|
|
32466
|
+
try {
|
|
32467
|
+
writeFileSync(this.filePath, "", "utf-8");
|
|
32468
|
+
} catch {
|
|
32469
|
+
this.writable = false;
|
|
32470
|
+
}
|
|
32471
|
+
}
|
|
32472
|
+
}
|
|
32473
|
+
};
|
|
32474
|
+
function createEventLog() {
|
|
32475
|
+
return new InMemoryEventLog();
|
|
32476
|
+
}
|
|
32477
|
+
function createFileEventLog(filePath) {
|
|
32478
|
+
return new FileEventLog(filePath);
|
|
32479
|
+
}
|
|
32480
|
+
|
|
32481
|
+
// src/runtime/permission-policy.ts
|
|
32482
|
+
var READ_ONLY_CATEGORIES = /* @__PURE__ */ new Set(["search", "web", "document"]);
|
|
32483
|
+
var WRITE_CATEGORIES = /* @__PURE__ */ new Set(["file", "git", "test", "build", "memory"]);
|
|
32484
|
+
var READ_ONLY_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
32485
|
+
"glob",
|
|
32486
|
+
"read_file",
|
|
32487
|
+
"list_dir",
|
|
32488
|
+
"tree",
|
|
32489
|
+
"grep",
|
|
32490
|
+
"find_in_file",
|
|
32491
|
+
"semantic_search",
|
|
32492
|
+
"codebase_map",
|
|
32493
|
+
"repo_context",
|
|
32494
|
+
"lsp_status",
|
|
32495
|
+
"lsp_document_symbols",
|
|
32496
|
+
"lsp_workspace_symbols",
|
|
32497
|
+
"lsp_definition",
|
|
32498
|
+
"lsp_references",
|
|
32499
|
+
"git_status",
|
|
32500
|
+
"git_log",
|
|
32501
|
+
"git_diff",
|
|
32502
|
+
"git_show",
|
|
32503
|
+
"git_branch",
|
|
32504
|
+
"recall_memory",
|
|
32505
|
+
"list_memories",
|
|
32506
|
+
"list_checkpoints",
|
|
32507
|
+
"spawnSimpleAgent",
|
|
32508
|
+
"checkAgentCapability"
|
|
32509
|
+
]);
|
|
32510
|
+
var WRITE_CAPABLE_TOOL_NAMES = /* @__PURE__ */ new Set(["run_linter"]);
|
|
32511
|
+
var DESTRUCTIVE_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
32512
|
+
"bash_exec",
|
|
32513
|
+
"write_file",
|
|
32514
|
+
"edit_file",
|
|
32515
|
+
"delete_file",
|
|
32516
|
+
"restore_checkpoint",
|
|
32517
|
+
"git_commit",
|
|
32518
|
+
"git_push"
|
|
32519
|
+
]);
|
|
32520
|
+
function riskForTool(tool) {
|
|
32521
|
+
if (READ_ONLY_TOOL_NAMES.has(tool.name)) return "read-only";
|
|
32522
|
+
if (DESTRUCTIVE_TOOL_NAMES.has(tool.name)) return "destructive";
|
|
32523
|
+
if (WRITE_CAPABLE_TOOL_NAMES.has(tool.name)) return "write";
|
|
32524
|
+
if (tool.category === "web") return "network";
|
|
32525
|
+
if (WRITE_CATEGORIES.has(tool.category)) return "write";
|
|
32526
|
+
if (tool.category === "quality") return "write";
|
|
32527
|
+
return "read-only";
|
|
32528
|
+
}
|
|
32529
|
+
var DefaultPermissionPolicy = class {
|
|
32530
|
+
canExecuteTool(mode, tool) {
|
|
32531
|
+
const definition = getAgentMode(mode);
|
|
32532
|
+
const risk = riskForTool(tool);
|
|
32533
|
+
const readOnlyTool = READ_ONLY_TOOL_NAMES.has(tool.name) || READ_ONLY_CATEGORIES.has(tool.category);
|
|
32534
|
+
if (definition.readOnly && !readOnlyTool) {
|
|
32535
|
+
return {
|
|
32536
|
+
allowed: false,
|
|
32537
|
+
reason: `${definition.label} mode is read-only; ${tool.name} is a ${tool.category} tool.`,
|
|
32538
|
+
risk
|
|
32539
|
+
};
|
|
32540
|
+
}
|
|
32541
|
+
if (risk === "destructive") {
|
|
32542
|
+
return {
|
|
32543
|
+
allowed: true,
|
|
32544
|
+
requiresConfirmation: true,
|
|
32545
|
+
reason: `${tool.name} can change repository state and should be confirmed.`,
|
|
32546
|
+
risk
|
|
32547
|
+
};
|
|
32548
|
+
}
|
|
32549
|
+
return { allowed: true, risk };
|
|
32550
|
+
}
|
|
32551
|
+
};
|
|
32552
|
+
function createPermissionPolicy() {
|
|
32553
|
+
return new DefaultPermissionPolicy();
|
|
32554
|
+
}
|
|
32555
|
+
|
|
32556
|
+
// src/runtime/provider-registry.ts
|
|
32557
|
+
init_catalog();
|
|
32558
|
+
var ProviderRegistry = class {
|
|
32559
|
+
listProviders() {
|
|
32560
|
+
return Object.values(PROVIDER_CATALOG);
|
|
32561
|
+
}
|
|
32562
|
+
getProvider(provider) {
|
|
32563
|
+
return getProviderCatalogEntry(provider);
|
|
32564
|
+
}
|
|
32565
|
+
listModels(provider) {
|
|
32566
|
+
return this.getProvider(provider).models;
|
|
32567
|
+
}
|
|
32568
|
+
getModel(provider, model2) {
|
|
32569
|
+
return getCatalogModel(provider, model2);
|
|
32570
|
+
}
|
|
32571
|
+
getDefaultModel(provider) {
|
|
32572
|
+
return getCatalogDefaultModel(provider);
|
|
32573
|
+
}
|
|
32574
|
+
getRecommendedModel(provider) {
|
|
32575
|
+
return getCatalogRecommendedModel(provider);
|
|
32576
|
+
}
|
|
32577
|
+
getCapability(provider, model2) {
|
|
32578
|
+
return getProviderRuntimeCapability(provider, model2);
|
|
32579
|
+
}
|
|
32580
|
+
async createProvider(provider, config = {}) {
|
|
32581
|
+
return createProvider(provider, config);
|
|
32582
|
+
}
|
|
32583
|
+
async probe(provider, model2, checkAvailability) {
|
|
32584
|
+
return probeProviderRuntimeCapability(provider, model2, checkAvailability);
|
|
32585
|
+
}
|
|
32586
|
+
};
|
|
32587
|
+
function createProviderRegistry() {
|
|
32588
|
+
return new ProviderRegistry();
|
|
32589
|
+
}
|
|
32590
|
+
|
|
32591
|
+
// src/runtime/agent-runtime.ts
|
|
32592
|
+
var AgentRuntime = class {
|
|
32593
|
+
constructor(options) {
|
|
32594
|
+
this.options = options;
|
|
32595
|
+
this.providerRegistry = createProviderRegistry();
|
|
32596
|
+
this.toolRegistry = options.toolRegistry ?? createFullToolRegistry();
|
|
32597
|
+
this.sessionStore = options.sessionStore ?? createSessionStore({});
|
|
32598
|
+
this.permissionPolicy = options.permissionPolicy ?? createPermissionPolicy();
|
|
32599
|
+
this.eventLog = options.eventLog ?? (options.eventLogPath ? createFileEventLog(options.eventLogPath) : createEventLog());
|
|
32600
|
+
this.providerType = options.providerType;
|
|
32601
|
+
this.model = options.model ?? options.providerConfig?.model ?? getDefaultModel(options.providerType);
|
|
32602
|
+
}
|
|
32603
|
+
options;
|
|
32604
|
+
providerRegistry;
|
|
32605
|
+
toolRegistry;
|
|
32606
|
+
sessionStore;
|
|
32607
|
+
permissionPolicy;
|
|
32608
|
+
eventLog;
|
|
32609
|
+
providerType;
|
|
32610
|
+
model;
|
|
32611
|
+
async initialize() {
|
|
32612
|
+
const providerInjected = Boolean(this.options.provider);
|
|
32613
|
+
const provider = this.options.provider ?? await this.providerRegistry.createProvider(this.providerType, {
|
|
32614
|
+
...this.options.providerConfig,
|
|
32615
|
+
model: this.getModel()
|
|
32616
|
+
});
|
|
32617
|
+
this.publishToGlobalBridge(provider);
|
|
32618
|
+
this.eventLog.record(providerInjected ? "provider.attached" : "provider.created", {
|
|
32619
|
+
provider: this.providerType,
|
|
32620
|
+
model: this.getModel(),
|
|
32621
|
+
createdByRuntime: !providerInjected
|
|
32622
|
+
});
|
|
32623
|
+
this.eventLog.record("runtime.initialized", { snapshot: this.snapshot() });
|
|
32624
|
+
}
|
|
32625
|
+
getModel() {
|
|
32626
|
+
return this.model;
|
|
32627
|
+
}
|
|
32628
|
+
updateProvider(providerType, model2, provider) {
|
|
32629
|
+
this.providerType = providerType;
|
|
32630
|
+
this.model = model2 ?? getDefaultModel(providerType);
|
|
32631
|
+
this.publishToGlobalBridge(provider);
|
|
32632
|
+
this.eventLog.record("provider.updated", {
|
|
32633
|
+
provider: this.providerType,
|
|
32634
|
+
model: this.model
|
|
32635
|
+
});
|
|
32636
|
+
}
|
|
32637
|
+
publishToGlobalBridge(provider) {
|
|
32638
|
+
if (this.options.publishToGlobalBridge !== true) return;
|
|
32639
|
+
setAgentProvider(provider);
|
|
32640
|
+
setAgentToolRegistry(this.toolRegistry);
|
|
32641
|
+
}
|
|
32642
|
+
snapshot() {
|
|
32643
|
+
const capability = this.providerRegistry.getCapability(this.providerType, this.getModel());
|
|
32644
|
+
const toolNames = this.toolRegistry.getAll().map((tool) => tool.name).sort();
|
|
32645
|
+
return {
|
|
32646
|
+
provider: {
|
|
32647
|
+
type: this.providerType,
|
|
32648
|
+
model: this.getModel(),
|
|
32649
|
+
capability
|
|
32650
|
+
},
|
|
32651
|
+
tools: {
|
|
32652
|
+
count: toolNames.length,
|
|
32653
|
+
names: toolNames
|
|
32654
|
+
},
|
|
32655
|
+
modes: listAgentModes()
|
|
32656
|
+
};
|
|
32657
|
+
}
|
|
32658
|
+
assertToolAllowed(mode, toolName) {
|
|
32659
|
+
const tool = this.toolRegistry.get(toolName);
|
|
32660
|
+
if (!tool) {
|
|
32661
|
+
this.eventLog.record("tool.blocked", {
|
|
32662
|
+
mode,
|
|
32663
|
+
tool: toolName,
|
|
32664
|
+
reason: "Tool not registered."
|
|
32665
|
+
});
|
|
32666
|
+
return false;
|
|
32667
|
+
}
|
|
32668
|
+
const decision = this.permissionPolicy.canExecuteTool(mode, tool);
|
|
32669
|
+
this.eventLog.record(decision.allowed ? "tool.allowed" : "tool.blocked", {
|
|
32670
|
+
mode,
|
|
32671
|
+
tool: toolName,
|
|
32672
|
+
...decision
|
|
32673
|
+
});
|
|
32674
|
+
return decision.allowed;
|
|
32675
|
+
}
|
|
32676
|
+
};
|
|
32677
|
+
async function createAgentRuntime(options) {
|
|
32678
|
+
const runtime = new AgentRuntime(options);
|
|
32679
|
+
await runtime.initialize();
|
|
32680
|
+
return runtime;
|
|
32681
|
+
}
|
|
32682
|
+
|
|
32683
|
+
// src/runtime/extension-manifests.ts
|
|
32684
|
+
function createMcpToolPolicy(server, tool, risk, allowedModes = ["ask", "plan", "build", "debug", "review", "architect"]) {
|
|
32685
|
+
return {
|
|
32686
|
+
server,
|
|
32687
|
+
tool,
|
|
32688
|
+
risk,
|
|
32689
|
+
requiresConfirmation: risk === "destructive" || risk === "secrets-sensitive",
|
|
32690
|
+
allowedModes
|
|
32691
|
+
};
|
|
32692
|
+
}
|
|
32693
|
+
|
|
32694
|
+
// src/runtime/workflow-registry.ts
|
|
32695
|
+
function cloneWorkflow(workflow) {
|
|
32696
|
+
return {
|
|
32697
|
+
...workflow,
|
|
32698
|
+
checks: [...workflow.checks],
|
|
32699
|
+
steps: workflow.steps.map((step) => ({
|
|
32700
|
+
...step,
|
|
32701
|
+
requiredTools: [...step.requiredTools]
|
|
32702
|
+
}))
|
|
32703
|
+
};
|
|
32704
|
+
}
|
|
32705
|
+
var WorkflowCatalog = class {
|
|
32706
|
+
workflows = /* @__PURE__ */ new Map();
|
|
32707
|
+
constructor(workflows = DEFAULT_WORKFLOWS) {
|
|
32708
|
+
for (const workflow of workflows) {
|
|
32709
|
+
this.register(workflow);
|
|
32710
|
+
}
|
|
32711
|
+
}
|
|
32712
|
+
register(workflow) {
|
|
32713
|
+
this.workflows.set(workflow.id, cloneWorkflow(workflow));
|
|
32714
|
+
}
|
|
32715
|
+
get(id) {
|
|
32716
|
+
const workflow = this.workflows.get(id);
|
|
32717
|
+
return workflow ? cloneWorkflow(workflow) : void 0;
|
|
32718
|
+
}
|
|
32719
|
+
list() {
|
|
32720
|
+
return [...this.workflows.values()].map(cloneWorkflow).sort((a, b) => a.id.localeCompare(b.id));
|
|
32721
|
+
}
|
|
32722
|
+
createPlan(workflowId, input, eventLog) {
|
|
32723
|
+
const workflow = this.get(workflowId);
|
|
32724
|
+
if (!workflow) {
|
|
32725
|
+
throw new Error(`Unknown workflow: ${workflowId}`);
|
|
32726
|
+
}
|
|
32727
|
+
const plan = {
|
|
32728
|
+
id: `${workflowId}-${Date.now().toString(36)}`,
|
|
32729
|
+
workflowId,
|
|
32730
|
+
input,
|
|
32731
|
+
status: "planned",
|
|
32732
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
32733
|
+
};
|
|
32734
|
+
eventLog?.record("workflow.planned", {
|
|
32735
|
+
workflowId,
|
|
32736
|
+
planId: plan.id,
|
|
32737
|
+
replayable: workflow.replayable,
|
|
32738
|
+
checks: workflow.checks
|
|
32739
|
+
});
|
|
32740
|
+
return plan;
|
|
32741
|
+
}
|
|
32742
|
+
};
|
|
32743
|
+
var DEFAULT_WORKFLOWS = [
|
|
32744
|
+
{
|
|
32745
|
+
id: "architect-editor-verifier",
|
|
32746
|
+
name: "Architect / Editor / Verifier",
|
|
32747
|
+
description: "Plan read-only, apply approved changes, then verify and summarize risks.",
|
|
32748
|
+
inputSchema: "task: string; approvedPlan?: string",
|
|
32749
|
+
outputKind: "patch",
|
|
32750
|
+
replayable: true,
|
|
32751
|
+
checks: ["pnpm check", "diff summary", "review risks"],
|
|
32752
|
+
steps: [
|
|
32753
|
+
{
|
|
32754
|
+
id: "architect",
|
|
32755
|
+
description: "Inspect context and produce a read-only implementation plan.",
|
|
32756
|
+
requiredTools: ["repo_context", "read_file", "git_diff"],
|
|
32757
|
+
risk: "read-only"
|
|
32758
|
+
},
|
|
32759
|
+
{
|
|
32760
|
+
id: "editor",
|
|
32761
|
+
description: "Apply the approved plan without reinterpreting the objective.",
|
|
32762
|
+
requiredTools: ["read_file", "edit_file", "write_file"],
|
|
32763
|
+
risk: "write"
|
|
32764
|
+
},
|
|
32765
|
+
{
|
|
32766
|
+
id: "verifier",
|
|
32767
|
+
description: "Run checks, review diff, and report residual risk.",
|
|
32768
|
+
requiredTools: ["bash_exec", "git_diff", "review_code"],
|
|
32769
|
+
risk: "destructive"
|
|
32770
|
+
}
|
|
32771
|
+
]
|
|
32772
|
+
},
|
|
32773
|
+
{
|
|
32774
|
+
id: "provider-diagnosis",
|
|
32775
|
+
name: "Provider Diagnosis",
|
|
32776
|
+
description: "Probe provider/model capabilities, endpoint strategy, credentials, and fallbacks.",
|
|
32777
|
+
inputSchema: "provider?: string; model?: string; live?: boolean",
|
|
32778
|
+
outputKind: "json",
|
|
32779
|
+
replayable: true,
|
|
32780
|
+
checks: ["provider capability matrix", "optional live probe"],
|
|
32781
|
+
steps: [
|
|
32782
|
+
{
|
|
32783
|
+
id: "capability",
|
|
32784
|
+
description: "Resolve catalog metadata and runtime endpoint strategy.",
|
|
32785
|
+
requiredTools: [],
|
|
32786
|
+
risk: "read-only"
|
|
32787
|
+
},
|
|
32788
|
+
{
|
|
32789
|
+
id: "fallbacks",
|
|
32790
|
+
description: "Suggest fallback provider/model choices when unsupported.",
|
|
32791
|
+
requiredTools: [],
|
|
32792
|
+
risk: "read-only"
|
|
32793
|
+
}
|
|
32794
|
+
]
|
|
32795
|
+
},
|
|
32796
|
+
{
|
|
32797
|
+
id: "review-pr",
|
|
32798
|
+
name: "Review PR",
|
|
32799
|
+
description: "Review a branch or PR read-only and emit severity-ranked findings.",
|
|
32800
|
+
inputSchema: "target: string",
|
|
32801
|
+
outputKind: "markdown",
|
|
32802
|
+
replayable: true,
|
|
32803
|
+
checks: ["git diff", "tests gap review", "security review"],
|
|
32804
|
+
steps: [
|
|
32805
|
+
{
|
|
32806
|
+
id: "collect-diff",
|
|
32807
|
+
description: "Collect PR diff and related context.",
|
|
32808
|
+
requiredTools: ["git_diff", "repo_context"],
|
|
32809
|
+
risk: "read-only"
|
|
32810
|
+
},
|
|
32811
|
+
{
|
|
32812
|
+
id: "findings",
|
|
32813
|
+
description: "Produce prioritized findings with file and line references.",
|
|
32814
|
+
requiredTools: ["read_file", "review_code"],
|
|
32815
|
+
risk: "read-only"
|
|
32816
|
+
}
|
|
32817
|
+
]
|
|
32818
|
+
},
|
|
32819
|
+
{
|
|
32820
|
+
id: "best-of-n",
|
|
32821
|
+
name: "Best Of N",
|
|
32822
|
+
description: "Run multiple isolated attempts, score them, and select a winning patch.",
|
|
32823
|
+
inputSchema: "task: string; attempts: number",
|
|
32824
|
+
outputKind: "patch",
|
|
32825
|
+
replayable: true,
|
|
32826
|
+
checks: ["worktree isolation", "checks pass", "diff risk score"],
|
|
32827
|
+
steps: [
|
|
32828
|
+
{
|
|
32829
|
+
id: "fanout",
|
|
32830
|
+
description: "Create isolated attempts in temporary worktrees.",
|
|
32831
|
+
requiredTools: ["git_status", "bash_exec"],
|
|
32832
|
+
risk: "destructive"
|
|
32833
|
+
},
|
|
32834
|
+
{
|
|
32835
|
+
id: "score",
|
|
32836
|
+
description: "Run checks and compare quality, cost, latency, and diff risk.",
|
|
32837
|
+
requiredTools: ["bash_exec", "git_diff"],
|
|
32838
|
+
risk: "destructive"
|
|
32839
|
+
},
|
|
32840
|
+
{
|
|
32841
|
+
id: "apply-winner",
|
|
32842
|
+
description: "Apply the winning patch only if conservative checks pass.",
|
|
32843
|
+
requiredTools: ["git_diff", "edit_file"],
|
|
32844
|
+
risk: "write"
|
|
32845
|
+
}
|
|
32846
|
+
]
|
|
32847
|
+
},
|
|
32848
|
+
{
|
|
32849
|
+
id: "release",
|
|
32850
|
+
name: "Release",
|
|
32851
|
+
description: "Follow the project release skill: changelog, version bump, PR, merge, tag, publish verify.",
|
|
32852
|
+
inputSchema: "bump?: patch|minor|major",
|
|
32853
|
+
outputKind: "release",
|
|
32854
|
+
replayable: true,
|
|
32855
|
+
checks: ["pnpm check", "PR checks", "release.yml", "npm view"],
|
|
32856
|
+
steps: [
|
|
32857
|
+
{
|
|
32858
|
+
id: "preflight",
|
|
32859
|
+
description: "Verify branch, clean tree, GitHub auth, and remote state.",
|
|
32860
|
+
requiredTools: ["git_status", "bash_exec"],
|
|
32861
|
+
risk: "destructive"
|
|
32862
|
+
},
|
|
32863
|
+
{
|
|
32864
|
+
id: "version",
|
|
32865
|
+
description: "Update changelog and package versions using the release skill.",
|
|
32866
|
+
requiredTools: ["read_file", "edit_file", "bash_exec"],
|
|
32867
|
+
risk: "destructive"
|
|
32868
|
+
},
|
|
32869
|
+
{
|
|
32870
|
+
id: "publish",
|
|
32871
|
+
description: "Merge release PR, tag main, and verify release workflow outputs.",
|
|
32872
|
+
requiredTools: ["bash_exec"],
|
|
32873
|
+
risk: "destructive"
|
|
32874
|
+
}
|
|
32875
|
+
]
|
|
32876
|
+
}
|
|
32877
|
+
];
|
|
32878
|
+
var WorkflowRegistry = WorkflowCatalog;
|
|
32879
|
+
function createWorkflowCatalog(workflows) {
|
|
32880
|
+
return new WorkflowCatalog(workflows);
|
|
32881
|
+
}
|
|
32882
|
+
function createWorkflowRegistry(workflows) {
|
|
32883
|
+
return createWorkflowCatalog(workflows);
|
|
32884
|
+
}
|
|
30913
32885
|
|
|
30914
32886
|
// src/index.ts
|
|
30915
32887
|
init_errors();
|
|
30916
32888
|
init_logger();
|
|
30917
32889
|
init_proxy();
|
|
30918
32890
|
|
|
30919
|
-
export { ADRGenerator, AnthropicProvider, ArchitectureGenerator, BacklogGenerator, CICDGenerator, CocoError, CodeGenerator, CodeReviewer, CompleteExecutor, ConfigError, ConvergeExecutor, DiscoveryEngine, DockerGenerator, DocsGenerator, OrchestrateExecutor, OutputExecutor, PhaseError, SessionManager, SpecificationGenerator, TaskError, TaskIterator, ToolRegistry, VERSION, configExists, createADRGenerator, createAnthropicProvider, createArchitectureGenerator, createBacklogGenerator, createCICDGenerator, createCodeGenerator, createCodeReviewer, createCompleteExecutor, createConvergeExecutor, createDefaultConfig, createDiscoveryEngine, createDockerGenerator, createDocsGenerator, createFullToolRegistry, createLogger, createOrchestrateExecutor, createOrchestrator, createOutputExecutor, createProvider, createSessionManager, createSpecificationGenerator, createTaskIterator, createToolRegistry, installProxyDispatcher, loadConfig, registerAllTools, saveConfig };
|
|
32891
|
+
export { ADRGenerator, AgentRuntime, AnthropicProvider, ArchitectureGenerator, BacklogGenerator, CICDGenerator, CocoError, CodeGenerator, CodeReviewer, CompleteExecutor, ConfigError, ConvergeExecutor, DEFAULT_WORKFLOWS, DefaultPermissionPolicy, DiscoveryEngine, DockerGenerator, DocsGenerator, FileEventLog, InMemoryEventLog, OrchestrateExecutor, OutputExecutor, PhaseError, ProviderRegistry, SessionManager, SpecificationGenerator, TaskError, TaskIterator, ToolRegistry, VERSION, WorkflowCatalog, WorkflowRegistry, configExists, createADRGenerator, createAgentRuntime, createAnthropicProvider, createArchitectureGenerator, createBacklogGenerator, createCICDGenerator, createCodeGenerator, createCodeReviewer, createCompleteExecutor, createConvergeExecutor, createDefaultConfig, createDiscoveryEngine, createDockerGenerator, createDocsGenerator, createEventLog, createFileEventLog, createFullToolRegistry, createLogger, createMcpToolPolicy, createOrchestrateExecutor, createOrchestrator, createOutputExecutor, createPermissionPolicy, createProvider, createProviderRegistry, createSessionManager, createSpecificationGenerator, createTaskIterator, createToolRegistry, createWorkflowCatalog, createWorkflowRegistry, installProxyDispatcher, loadConfig, registerAllTools, saveConfig };
|
|
30920
32892
|
//# sourceMappingURL=index.js.map
|
|
30921
32893
|
//# sourceMappingURL=index.js.map
|