@castlemilk/omega 0.3.0 → 0.4.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/dist/cli.js +70 -0
- package/dist/server.js +183 -2
- package/package.json +3 -3
package/dist/cli.js
CHANGED
|
@@ -638,6 +638,75 @@ skillCmd.command("generate").description("Generate a harness adapter from a SKIL
|
|
|
638
638
|
console.log(`Output directory: ${path3.join(outputDir, "generated")}`);
|
|
639
639
|
});
|
|
640
640
|
|
|
641
|
+
// ../../apps/cli/src/commands/agent.ts
|
|
642
|
+
import { Command as Command7 } from "commander";
|
|
643
|
+
var agentCmd = new Command7("agent").description("Autonomous agent loop");
|
|
644
|
+
agentCmd.command("run").description("Run a single agent task").requiredOption("--project <id>", "project id").requiredOption("--title <title>", "task title").option("--description <text>", "task description").option("--complexity <level>", "simple | medium | complex", "complex").option("--auto-publish", "publish if validation passes", false).action(
|
|
645
|
+
async (opts) => {
|
|
646
|
+
const tags = ["agent"];
|
|
647
|
+
if (opts.autoPublish) tags.push("publish");
|
|
648
|
+
const task = await apiFetch("/tasks", {
|
|
649
|
+
method: "POST",
|
|
650
|
+
headers: { "Content-Type": "application/json" },
|
|
651
|
+
body: JSON.stringify({
|
|
652
|
+
projectId: opts.project,
|
|
653
|
+
title: opts.title,
|
|
654
|
+
description: opts.description,
|
|
655
|
+
complexity: opts.complexity,
|
|
656
|
+
tags
|
|
657
|
+
})
|
|
658
|
+
});
|
|
659
|
+
console.log(JSON.stringify(task, null, 2));
|
|
660
|
+
const result = await apiFetch(`/tasks/${task.id}/run`, { method: "POST" });
|
|
661
|
+
console.log(JSON.stringify(result, null, 2));
|
|
662
|
+
}
|
|
663
|
+
);
|
|
664
|
+
agentCmd.command("loop").description("Continuously run agent tasks").requiredOption("--project <id>", "project id").option("--interval <min>", "minutes between loops", "30").option("--auto-publish", "publish if validation passes", false).option("--prompt <text>", "overriding self-improvement prompt").action(
|
|
665
|
+
async (opts) => {
|
|
666
|
+
const intervalMin = parseInt(opts.interval, 10);
|
|
667
|
+
const intervalMs = intervalMin * 60 * 1e3;
|
|
668
|
+
const runOnce = async () => {
|
|
669
|
+
const title = opts.prompt ?? "Improve the Omega harness: fix a TODO, add a test, refactor a function, or improve documentation";
|
|
670
|
+
const tags = ["agent", "self-improve"];
|
|
671
|
+
if (opts.autoPublish) tags.push("publish");
|
|
672
|
+
const task = await apiFetch("/tasks", {
|
|
673
|
+
method: "POST",
|
|
674
|
+
headers: { "Content-Type": "application/json" },
|
|
675
|
+
body: JSON.stringify({
|
|
676
|
+
projectId: opts.project,
|
|
677
|
+
title,
|
|
678
|
+
description: "Use the available tools to make a small, verifiable improvement to the codebase. Run lint and tests. Finish with a summary.",
|
|
679
|
+
complexity: "complex",
|
|
680
|
+
tags
|
|
681
|
+
})
|
|
682
|
+
});
|
|
683
|
+
console.log(`Created task ${task.id}`);
|
|
684
|
+
const result = await apiFetch(`/tasks/${task.id}/run`, { method: "POST" });
|
|
685
|
+
console.log(JSON.stringify(result, null, 2));
|
|
686
|
+
return result;
|
|
687
|
+
};
|
|
688
|
+
await runOnce();
|
|
689
|
+
if (intervalMs > 0) {
|
|
690
|
+
console.log(`Looping every ${opts.interval} minute(s). Press Ctrl+C to stop.`);
|
|
691
|
+
setInterval(() => {
|
|
692
|
+
void runOnce();
|
|
693
|
+
}, intervalMs);
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
);
|
|
697
|
+
agentCmd.command("steps").description("Show steps for a task").argument("<id>", "task id").action(async (id) => {
|
|
698
|
+
const steps = await apiFetch(`/tasks/${id}/steps`);
|
|
699
|
+
console.log(JSON.stringify(steps, null, 2));
|
|
700
|
+
});
|
|
701
|
+
agentCmd.command("traces").description("Show traces for a task").argument("<id>", "task id").action(async (id) => {
|
|
702
|
+
const traces = await apiFetch(`/tasks/${id}/traces`);
|
|
703
|
+
console.log(JSON.stringify(traces, null, 2));
|
|
704
|
+
});
|
|
705
|
+
agentCmd.command("diffs").description("Show diffs for a task").argument("<id>", "task id").action(async (id) => {
|
|
706
|
+
const diffs = await apiFetch(`/tasks/${id}/diffs`);
|
|
707
|
+
console.log(JSON.stringify(diffs, null, 2));
|
|
708
|
+
});
|
|
709
|
+
|
|
641
710
|
// ../../apps/cli/src/index.ts
|
|
642
711
|
program2.name("harness").description("Omega harness CLI").version("0.1.0").option("--api <url>", "API base URL", "http://localhost:4000");
|
|
643
712
|
program2.addCommand(projectCmd);
|
|
@@ -645,4 +714,5 @@ program2.addCommand(taskCmd);
|
|
|
645
714
|
program2.addCommand(uiCmd);
|
|
646
715
|
program2.addCommand(consoleCmd);
|
|
647
716
|
program2.addCommand(skillCmd);
|
|
717
|
+
program2.addCommand(agentCmd);
|
|
648
718
|
program2.parse();
|
package/dist/server.js
CHANGED
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
// src/server.ts
|
|
4
4
|
import express from "express";
|
|
5
5
|
import cors from "cors";
|
|
6
|
-
import path from "path";
|
|
7
6
|
import { fileURLToPath } from "url";
|
|
8
7
|
import { z } from "zod";
|
|
9
8
|
import * as grpc from "@grpc/grpc-js";
|
|
@@ -49,6 +48,48 @@ var OpenAIProvider = class {
|
|
|
49
48
|
const data = await res.json();
|
|
50
49
|
return data.choices?.[0]?.message?.content ?? "";
|
|
51
50
|
}
|
|
51
|
+
async sendWithTools(prompt, tools, opts) {
|
|
52
|
+
const res = await fetch(`${this.baseUrl}/chat/completions`, {
|
|
53
|
+
method: "POST",
|
|
54
|
+
headers: {
|
|
55
|
+
"Content-Type": "application/json",
|
|
56
|
+
...this.authHeaders()
|
|
57
|
+
},
|
|
58
|
+
body: JSON.stringify({
|
|
59
|
+
model: opts?.model ?? this.config.defaultModel,
|
|
60
|
+
messages: [
|
|
61
|
+
...opts?.system ? [{ role: "system", content: opts.system }] : [],
|
|
62
|
+
{ role: "user", content: prompt }
|
|
63
|
+
],
|
|
64
|
+
tools: tools.map((t) => ({
|
|
65
|
+
type: "function",
|
|
66
|
+
function: { name: t.name, description: t.description, parameters: t.parameters }
|
|
67
|
+
})),
|
|
68
|
+
temperature: opts?.temperature
|
|
69
|
+
})
|
|
70
|
+
});
|
|
71
|
+
if (!res.ok) {
|
|
72
|
+
throw new Error(`OpenAI tools request failed: ${res.status.toString()} ${res.statusText}`);
|
|
73
|
+
}
|
|
74
|
+
const data = await res.json();
|
|
75
|
+
const message = data.choices?.[0]?.message;
|
|
76
|
+
const toolCalls = message?.tool_calls;
|
|
77
|
+
if (toolCalls && toolCalls.length > 0) {
|
|
78
|
+
const normalized = toolCalls.map((tc) => ({
|
|
79
|
+
id: tc.id ?? "",
|
|
80
|
+
name: tc.function?.name ?? "",
|
|
81
|
+
arguments: (() => {
|
|
82
|
+
try {
|
|
83
|
+
return JSON.parse(tc.function?.arguments ?? "{}");
|
|
84
|
+
} catch {
|
|
85
|
+
return {};
|
|
86
|
+
}
|
|
87
|
+
})()
|
|
88
|
+
})).filter((tc) => tc.id && tc.name);
|
|
89
|
+
return JSON.stringify({ tool_calls: normalized });
|
|
90
|
+
}
|
|
91
|
+
return message?.content ?? "";
|
|
92
|
+
}
|
|
52
93
|
authHeaders() {
|
|
53
94
|
return this.config.apiKey ? { Authorization: `Bearer ${this.config.apiKey}` } : {};
|
|
54
95
|
}
|
|
@@ -189,7 +230,7 @@ var KimiProvider = class extends OpenAIProvider {
|
|
|
189
230
|
constructor(config) {
|
|
190
231
|
super({
|
|
191
232
|
...config,
|
|
192
|
-
baseUrl: config.baseUrl ?? "https://api.moonshot.
|
|
233
|
+
baseUrl: config.baseUrl ?? "https://api.moonshot.cn/v1"
|
|
193
234
|
});
|
|
194
235
|
this.config = config;
|
|
195
236
|
}
|
|
@@ -278,6 +319,11 @@ function selectProvider(configs, rules2, task) {
|
|
|
278
319
|
}
|
|
279
320
|
|
|
280
321
|
// src/server.ts
|
|
322
|
+
import fs from "node:fs/promises";
|
|
323
|
+
import path from "node:path";
|
|
324
|
+
import { execFile } from "node:child_process";
|
|
325
|
+
import { promisify } from "node:util";
|
|
326
|
+
var execFileAsync = promisify(execFile);
|
|
281
327
|
var __filename = fileURLToPath(import.meta.url);
|
|
282
328
|
var __dirname = path.dirname(__filename);
|
|
283
329
|
function asyncHandler(fn) {
|
|
@@ -498,10 +544,145 @@ function parseGrpcRequest(req) {
|
|
|
498
544
|
function isValidComplexity(value) {
|
|
499
545
|
return ["simple", "medium", "complex"].includes(value);
|
|
500
546
|
}
|
|
547
|
+
var AGENT_TOOLS = [
|
|
548
|
+
{
|
|
549
|
+
name: "read_file",
|
|
550
|
+
description: "Read a file relative to project root.",
|
|
551
|
+
parameters: { type: "object", properties: { path: { type: "string" } }, required: ["path"] }
|
|
552
|
+
},
|
|
553
|
+
{
|
|
554
|
+
name: "write_file",
|
|
555
|
+
description: "Write content to a file relative to project root.",
|
|
556
|
+
parameters: {
|
|
557
|
+
type: "object",
|
|
558
|
+
properties: { path: { type: "string" }, content: { type: "string" } },
|
|
559
|
+
required: ["path", "content"]
|
|
560
|
+
}
|
|
561
|
+
},
|
|
562
|
+
{
|
|
563
|
+
name: "run_command",
|
|
564
|
+
description: "Run a shell command in the project root.",
|
|
565
|
+
parameters: { type: "object", properties: { command: { type: "string" } }, required: ["command"] }
|
|
566
|
+
},
|
|
567
|
+
{
|
|
568
|
+
name: "finish",
|
|
569
|
+
description: "Mark the task complete.",
|
|
570
|
+
parameters: {
|
|
571
|
+
type: "object",
|
|
572
|
+
properties: { summary: { type: "string" }, success: { type: "boolean" } },
|
|
573
|
+
required: ["summary", "success"]
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
];
|
|
577
|
+
var AGENT_SYSTEM_PROMPT = `You are Omega, an autonomous software engineering agent. Respond with JSON: {"tool_calls":[{"id":"1","name":"tool_name","arguments":{}}]}. Available tools: read_file, write_file, run_command, finish. Work in small steps, run tests, and finish when done.`;
|
|
578
|
+
function argString(value) {
|
|
579
|
+
if (value === void 0 || value === null) return "";
|
|
580
|
+
if (typeof value === "string") return value;
|
|
581
|
+
if (typeof value === "number" || typeof value === "boolean") return value.toString();
|
|
582
|
+
return JSON.stringify(value);
|
|
583
|
+
}
|
|
584
|
+
async function executeBundleTool(projectPath, name, args) {
|
|
585
|
+
const target = (file) => {
|
|
586
|
+
const resolved = path.resolve(projectPath, file);
|
|
587
|
+
if (!resolved.startsWith(path.resolve(projectPath))) throw new Error("Path traversal blocked");
|
|
588
|
+
return resolved;
|
|
589
|
+
};
|
|
590
|
+
try {
|
|
591
|
+
switch (name) {
|
|
592
|
+
case "read_file": {
|
|
593
|
+
const content = await fs.readFile(target(argString(args.path)), "utf-8");
|
|
594
|
+
return { success: true, output: content };
|
|
595
|
+
}
|
|
596
|
+
case "write_file": {
|
|
597
|
+
const p = target(argString(args.path));
|
|
598
|
+
await fs.mkdir(path.dirname(p), { recursive: true });
|
|
599
|
+
await fs.writeFile(p, argString(args.content), "utf-8");
|
|
600
|
+
return { success: true, output: `Wrote ${argString(args.path)}` };
|
|
601
|
+
}
|
|
602
|
+
case "run_command": {
|
|
603
|
+
const [cmd, ...cmdArgs] = argString(args.command).split(" ");
|
|
604
|
+
const { stdout, stderr } = await execFileAsync(cmd, cmdArgs, {
|
|
605
|
+
cwd: projectPath,
|
|
606
|
+
timeout: 12e4,
|
|
607
|
+
shell: false
|
|
608
|
+
});
|
|
609
|
+
return { success: true, output: stdout + stderr };
|
|
610
|
+
}
|
|
611
|
+
default:
|
|
612
|
+
return { success: false, output: `Unknown tool: ${name}` };
|
|
613
|
+
}
|
|
614
|
+
} catch (err) {
|
|
615
|
+
return { success: false, output: err instanceof Error ? err.message : String(err) };
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
function parseBundleToolCalls(raw) {
|
|
619
|
+
try {
|
|
620
|
+
const cleaned = raw.trim().replace(/^```[a-z]*\n?/, "").replace(/\n?```$/, "");
|
|
621
|
+
const parsed = JSON.parse(cleaned);
|
|
622
|
+
if (!Array.isArray(parsed.tool_calls)) return [];
|
|
623
|
+
const calls = parsed.tool_calls;
|
|
624
|
+
return calls.filter((t) => Boolean(t.id && t.name)).map((t) => ({ id: t.id, name: t.name, arguments: t.arguments }));
|
|
625
|
+
} catch {
|
|
626
|
+
return [];
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
async function executeAgentTask(task) {
|
|
630
|
+
task.status = "in_progress";
|
|
631
|
+
task.error = void 0;
|
|
632
|
+
task.result = void 0;
|
|
633
|
+
const project = projects.find((p) => p.id === task.projectId);
|
|
634
|
+
if (!project) {
|
|
635
|
+
task.status = "failed";
|
|
636
|
+
task.error = "Project not found for agent task";
|
|
637
|
+
return;
|
|
638
|
+
}
|
|
639
|
+
const selection = selectProvider(providerConfigs, rules, task);
|
|
640
|
+
if (!selection) {
|
|
641
|
+
task.status = "failed";
|
|
642
|
+
task.error = "No provider available for this task";
|
|
643
|
+
return;
|
|
644
|
+
}
|
|
645
|
+
try {
|
|
646
|
+
const provider = createProvider(selection.provider);
|
|
647
|
+
const prompt = [task.title, task.description].filter(Boolean).join("\n\n");
|
|
648
|
+
let response;
|
|
649
|
+
if ("sendWithTools" in provider && typeof provider.sendWithTools === "function") {
|
|
650
|
+
response = await provider.sendWithTools(prompt, AGENT_TOOLS, {
|
|
651
|
+
system: AGENT_SYSTEM_PROMPT,
|
|
652
|
+
model: selection.model
|
|
653
|
+
});
|
|
654
|
+
} else {
|
|
655
|
+
response = await provider.send(prompt, { system: AGENT_SYSTEM_PROMPT, model: selection.model });
|
|
656
|
+
}
|
|
657
|
+
const calls = parseBundleToolCalls(response);
|
|
658
|
+
const results = [];
|
|
659
|
+
for (const call of calls) {
|
|
660
|
+
if (call.name === "finish") {
|
|
661
|
+
task.status = call.arguments.success ? "done" : "failed";
|
|
662
|
+
const summaryArg = call.arguments.summary;
|
|
663
|
+
task.result = typeof summaryArg === "string" ? summaryArg : "";
|
|
664
|
+
break;
|
|
665
|
+
}
|
|
666
|
+
const result = await executeBundleTool(project.path, call.name, call.arguments);
|
|
667
|
+
results.push(`${call.name}: ${result.output}`);
|
|
668
|
+
}
|
|
669
|
+
if (calls.length > 0 && calls.every((c) => c.name !== "finish")) {
|
|
670
|
+
task.status = "done";
|
|
671
|
+
task.result = results.join("\n");
|
|
672
|
+
}
|
|
673
|
+
task.assignedModel = { provider: selection.provider.name, model: selection.model };
|
|
674
|
+
} catch (err) {
|
|
675
|
+
task.status = "failed";
|
|
676
|
+
task.error = err instanceof Error ? err.message : String(err);
|
|
677
|
+
}
|
|
678
|
+
}
|
|
501
679
|
async function executeTask(task) {
|
|
502
680
|
task.status = "in_progress";
|
|
503
681
|
task.error = void 0;
|
|
504
682
|
task.result = void 0;
|
|
683
|
+
if (task.tags.includes("agent") || task.tags.includes("self-improve")) {
|
|
684
|
+
return executeAgentTask(task);
|
|
685
|
+
}
|
|
505
686
|
const selection = selectProvider(providerConfigs, rules, task);
|
|
506
687
|
if (!selection) {
|
|
507
688
|
task.status = "failed";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@castlemilk/omega",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Omega Harness CLI - installable via npx",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/cli.js",
|
|
@@ -30,8 +30,8 @@
|
|
|
30
30
|
"@types/node": "^20.14.0",
|
|
31
31
|
"esbuild": "^0.23.0",
|
|
32
32
|
"@omega/core": "0.1.0",
|
|
33
|
-
"@omega/
|
|
34
|
-
"@omega/
|
|
33
|
+
"@omega/router": "0.1.0",
|
|
34
|
+
"@omega/providers": "0.1.0"
|
|
35
35
|
},
|
|
36
36
|
"publishConfig": {
|
|
37
37
|
"access": "public"
|