@nestbox-ai/cli
Version:
The cli tools that helps developers to build agents
225 lines • 10.6 kB
JavaScript
;
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.DEFAULT_MODEL = void 0;
exports.runDocProcAgent = runDocProcAgent;
const sdk_1 = __importDefault(require("@anthropic-ai/sdk"));
const fs_1 = __importDefault(require("fs"));
const path_1 = __importDefault(require("path"));
const js_yaml_1 = __importDefault(require("js-yaml"));
const ajv_1 = __importDefault(require("ajv"));
// ─── Constants ────────────────────────────────────────────────────────────────
const AGENTS_DIR = __dirname;
exports.DEFAULT_MODEL = 'claude-sonnet-4-6';
const DEFAULT_MAX_ITERATIONS = 8;
// ─── Helpers ─────────────────────────────────────────────────────────────────
function readLocalFile(filename) {
return fs_1.default.readFileSync(path_1.default.join(AGENTS_DIR, filename), 'utf8');
}
function validateYaml(content, schemaContent) {
var _a;
// Replace ${ENV_VAR} references with a plain string so YAML parses cleanly
// and AJV treats them as valid string values.
const preprocessed = content.replace(/\$\{[^}]+\}/g, 'env-var-placeholder');
let data;
try {
data = js_yaml_1.default.load(preprocessed);
}
catch (e) {
return { valid: false, errors: [`YAML parse error: ${e.message}`] };
}
let schema;
try {
schema = js_yaml_1.default.load(schemaContent);
}
catch (e) {
return { valid: false, errors: [`Schema parse error: ${e.message}`] };
}
const ajv = new ajv_1.default({ strict: false, allErrors: true });
// Suppress "unknown format 'uri' ignored" console warnings from AJV
ajv.addFormat('uri', () => true);
const validate = ajv.compile(schema);
const ok = validate(data);
if (ok)
return { valid: true, errors: [] };
const errors = ((_a = validate.errors) !== null && _a !== void 0 ? _a : []).map((err) => {
const loc = err.instancePath || '(root)';
return `[${loc}] ${err.message}`;
});
return { valid: false, errors };
}
// ─── System Prompt ────────────────────────────────────────────────────────────
function buildSystemPrompt() {
const systemPrompt = readLocalFile('SYSTEM_PROMPT.md');
const configGuide = readLocalFile('CONFIG_GUIDE.md');
const evalGuide = readLocalFile('EVAL_GUIDE.md');
return `${systemPrompt}\n\n---\n\n${configGuide}\n\n---\n\n${evalGuide}`;
}
// ─── Tool Definitions ─────────────────────────────────────────────────────────
const TOOLS = [
{
name: 'write_and_validate_config',
description: 'Write the config.yaml content and validate it against the schema. Returns "VALID" on success or a list of validation errors to fix.',
input_schema: {
type: 'object',
properties: {
yaml_content: {
type: 'string',
description: 'The complete YAML content for config.yaml',
},
},
required: ['yaml_content'],
},
},
{
name: 'write_and_validate_eval',
description: 'Write the eval.yaml content and validate it against the schema. Returns "VALID" on success or a list of validation errors to fix.',
input_schema: {
type: 'object',
properties: {
yaml_content: {
type: 'string',
description: 'The complete YAML content for eval.yaml',
},
},
required: ['yaml_content'],
},
},
{
name: 'finish',
description: 'Signal that both files are complete and valid. Call this only after both write_and_validate_config and write_and_validate_eval have returned "VALID".',
input_schema: {
type: 'object',
properties: {
summary: {
type: 'string',
description: 'Brief summary of what was generated and why key choices were made',
},
},
required: ['summary'],
},
},
];
// ─── Agent ────────────────────────────────────────────────────────────────────
function runDocProcAgent(options) {
return __awaiter(this, void 0, void 0, function* () {
const { instructions, anthropicApiKey, model = exports.DEFAULT_MODEL, maxIterations = DEFAULT_MAX_ITERATIONS, maxTokens = 8096, onProgress = () => { }, } = options;
const configSchema = readLocalFile('config.schema.yaml');
const evalSchema = readLocalFile('eval-test-cases.schema.yaml');
const client = new sdk_1.default({ apiKey: anthropicApiKey });
let latestConfig = '';
let latestEval = '';
let configValid = false;
let evalValid = false;
let finished = false;
let iteration = 0;
function executeTool(name, input) {
var _a, _b;
if (name === 'write_and_validate_config') {
const content = (_a = input.yaml_content) !== null && _a !== void 0 ? _a : '';
latestConfig = content;
const result = validateYaml(content, configSchema);
configValid = result.valid;
if (result.valid)
return 'VALID';
return `VALIDATION ERRORS — fix all of these before calling again:\n${result.errors.map((e) => ` • ${e}`).join('\n')}`;
}
if (name === 'write_and_validate_eval') {
const content = (_b = input.yaml_content) !== null && _b !== void 0 ? _b : '';
latestEval = content;
const result = validateYaml(content, evalSchema);
evalValid = result.valid;
if (result.valid)
return 'VALID';
return `VALIDATION ERRORS — fix all of these before calling again:\n${result.errors.map((e) => ` • ${e}`).join('\n')}`;
}
if (name === 'finish') {
finished = true;
return 'Done.';
}
return `Unknown tool: ${name}`;
}
// Build the system prompt once and mark it for caching.
// On iteration 1 the prompt is written to the cache (normal price).
// On iterations 2+ the large system prompt is read from cache at
// 10% of the normal input token price.
const systemPromptText = buildSystemPrompt();
const systemPrompt = [
{
type: 'text',
text: systemPromptText,
cache_control: { type: 'ephemeral' },
},
];
const messages = [
{
role: 'user',
content: `Here are the instructions for the pipeline you need to configure:\n\n${instructions}\n\nGenerate the config.yaml and eval.yaml files now. Use the tools to write and validate them.`,
},
];
onProgress('Starting agent...');
while (iteration < maxIterations && !finished) {
iteration++;
onProgress(`Iteration ${iteration}/${maxIterations} — calling Claude...`);
const response = yield client.messages.create({
model,
max_tokens: maxTokens,
system: systemPrompt,
tools: TOOLS,
// Force Claude to call a tool every turn — prevents it from
// replying with plain text and exiting the loop prematurely.
tool_choice: { type: 'any' },
messages,
});
// Append assistant turn
messages.push({ role: 'assistant', content: response.content });
if (response.stop_reason !== 'tool_use') {
onProgress(`Agent stopped unexpectedly: ${response.stop_reason}`);
break;
}
// Process all tool calls and collect results
const toolResults = [];
for (const block of response.content) {
if (block.type !== 'tool_use')
continue;
onProgress(` → tool: ${block.name}`);
const result = executeTool(block.name, block.input);
onProgress(` ${result.startsWith('VALID') ? '✓ valid' : result.split('\n')[0]}`);
toolResults.push({
type: 'tool_result',
tool_use_id: block.id,
content: result,
});
if (finished)
break;
}
if (toolResults.length > 0) {
messages.push({ role: 'user', content: toolResults });
}
if (finished)
break;
}
if (iteration >= maxIterations && !finished) {
onProgress(`Warning: reached max iterations (${maxIterations}) without finishing.`);
}
return {
configYaml: latestConfig,
evalYaml: latestEval,
iterations: iteration,
configValid,
evalValid,
};
});
}
//# sourceMappingURL=anthropic.js.map