inferoa 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +176 -0
- package/README.md +154 -0
- package/dist/src/app.d.ts +16 -0
- package/dist/src/app.js +17 -0
- package/dist/src/app.js.map +1 -0
- package/dist/src/autoresearch/state.d.ts +106 -0
- package/dist/src/autoresearch/state.js +469 -0
- package/dist/src/autoresearch/state.js.map +1 -0
- package/dist/src/cli.d.ts +2 -0
- package/dist/src/cli.js +415 -0
- package/dist/src/cli.js.map +1 -0
- package/dist/src/code-intelligence/codegraph-engine.d.ts +55 -0
- package/dist/src/code-intelligence/codegraph-engine.js +593 -0
- package/dist/src/code-intelligence/codegraph-engine.js.map +1 -0
- package/dist/src/code-intelligence/hub.d.ts +37 -0
- package/dist/src/code-intelligence/hub.js +65 -0
- package/dist/src/code-intelligence/hub.js.map +1 -0
- package/dist/src/config/config.d.ts +12 -0
- package/dist/src/config/config.js +229 -0
- package/dist/src/config/config.js.map +1 -0
- package/dist/src/config/defaults.d.ts +2 -0
- package/dist/src/config/defaults.js +44 -0
- package/dist/src/config/defaults.js.map +1 -0
- package/dist/src/config/secret-vault.d.ts +3 -0
- package/dist/src/config/secret-vault.js +106 -0
- package/dist/src/config/secret-vault.js.map +1 -0
- package/dist/src/context/compressor.d.ts +33 -0
- package/dist/src/context/compressor.js +501 -0
- package/dist/src/context/compressor.js.map +1 -0
- package/dist/src/context/prompt.d.ts +26 -0
- package/dist/src/context/prompt.js +572 -0
- package/dist/src/context/prompt.js.map +1 -0
- package/dist/src/daemon/serve.d.ts +2 -0
- package/dist/src/daemon/serve.js +11 -0
- package/dist/src/daemon/serve.js.map +1 -0
- package/dist/src/daemon/supervisor.d.ts +33 -0
- package/dist/src/daemon/supervisor.js +252 -0
- package/dist/src/daemon/supervisor.js.map +1 -0
- package/dist/src/goals/state.d.ts +105 -0
- package/dist/src/goals/state.js +736 -0
- package/dist/src/goals/state.js.map +1 -0
- package/dist/src/model/endpoint-signals.d.ts +15 -0
- package/dist/src/model/endpoint-signals.js +186 -0
- package/dist/src/model/endpoint-signals.js.map +1 -0
- package/dist/src/model/gateway.d.ts +11 -0
- package/dist/src/model/gateway.js +455 -0
- package/dist/src/model/gateway.js.map +1 -0
- package/dist/src/plans/state.d.ts +28 -0
- package/dist/src/plans/state.js +123 -0
- package/dist/src/plans/state.js.map +1 -0
- package/dist/src/runtime.d.ts +92 -0
- package/dist/src/runtime.js +757 -0
- package/dist/src/runtime.js.map +1 -0
- package/dist/src/session/store.d.ts +84 -0
- package/dist/src/session/store.js +593 -0
- package/dist/src/session/store.js.map +1 -0
- package/dist/src/session/workspace.d.ts +2 -0
- package/dist/src/session/workspace.js +14 -0
- package/dist/src/session/workspace.js.map +1 -0
- package/dist/src/skills/registry.d.ts +24 -0
- package/dist/src/skills/registry.js +203 -0
- package/dist/src/skills/registry.js.map +1 -0
- package/dist/src/tools/autoresearch-tools.d.ts +6 -0
- package/dist/src/tools/autoresearch-tools.js +412 -0
- package/dist/src/tools/autoresearch-tools.js.map +1 -0
- package/dist/src/tools/clarify-tool.d.ts +3 -0
- package/dist/src/tools/clarify-tool.js +107 -0
- package/dist/src/tools/clarify-tool.js.map +1 -0
- package/dist/src/tools/code-intelligence.d.ts +15 -0
- package/dist/src/tools/code-intelligence.js +391 -0
- package/dist/src/tools/code-intelligence.js.map +1 -0
- package/dist/src/tools/context.d.ts +11 -0
- package/dist/src/tools/context.js +2 -0
- package/dist/src/tools/context.js.map +1 -0
- package/dist/src/tools/goal-tools.d.ts +3 -0
- package/dist/src/tools/goal-tools.js +279 -0
- package/dist/src/tools/goal-tools.js.map +1 -0
- package/dist/src/tools/omni-tools.d.ts +8 -0
- package/dist/src/tools/omni-tools.js +349 -0
- package/dist/src/tools/omni-tools.js.map +1 -0
- package/dist/src/tools/permissions.d.ts +11 -0
- package/dist/src/tools/permissions.js +74 -0
- package/dist/src/tools/permissions.js.map +1 -0
- package/dist/src/tools/plan-tools.d.ts +3 -0
- package/dist/src/tools/plan-tools.js +314 -0
- package/dist/src/tools/plan-tools.js.map +1 -0
- package/dist/src/tools/process-tools.d.ts +6 -0
- package/dist/src/tools/process-tools.js +199 -0
- package/dist/src/tools/process-tools.js.map +1 -0
- package/dist/src/tools/registry.d.ts +20 -0
- package/dist/src/tools/registry.js +187 -0
- package/dist/src/tools/registry.js.map +1 -0
- package/dist/src/tools/schemas.d.ts +3 -0
- package/dist/src/tools/schemas.js +500 -0
- package/dist/src/tools/schemas.js.map +1 -0
- package/dist/src/tools/skill-tools.d.ts +6 -0
- package/dist/src/tools/skill-tools.js +124 -0
- package/dist/src/tools/skill-tools.js.map +1 -0
- package/dist/src/tools/text-args.d.ts +5 -0
- package/dist/src/tools/text-args.js +22 -0
- package/dist/src/tools/text-args.js.map +1 -0
- package/dist/src/tools/web-search.d.ts +5 -0
- package/dist/src/tools/web-search.js +602 -0
- package/dist/src/tools/web-search.js.map +1 -0
- package/dist/src/tools/workspace-tools.d.ts +17 -0
- package/dist/src/tools/workspace-tools.js +561 -0
- package/dist/src/tools/workspace-tools.js.map +1 -0
- package/dist/src/tui/activity.d.ts +11 -0
- package/dist/src/tui/activity.js +75 -0
- package/dist/src/tui/activity.js.map +1 -0
- package/dist/src/tui/ansi.d.ts +24 -0
- package/dist/src/tui/ansi.js +131 -0
- package/dist/src/tui/ansi.js.map +1 -0
- package/dist/src/tui/app.d.ts +163 -0
- package/dist/src/tui/app.js +4204 -0
- package/dist/src/tui/app.js.map +1 -0
- package/dist/src/tui/cache-footer.d.ts +21 -0
- package/dist/src/tui/cache-footer.js +75 -0
- package/dist/src/tui/cache-footer.js.map +1 -0
- package/dist/src/tui/clarify.d.ts +14 -0
- package/dist/src/tui/clarify.js +187 -0
- package/dist/src/tui/clarify.js.map +1 -0
- package/dist/src/tui/composer.d.ts +79 -0
- package/dist/src/tui/composer.js +592 -0
- package/dist/src/tui/composer.js.map +1 -0
- package/dist/src/tui/event-view.d.ts +5 -0
- package/dist/src/tui/event-view.js +392 -0
- package/dist/src/tui/event-view.js.map +1 -0
- package/dist/src/tui/home.d.ts +7 -0
- package/dist/src/tui/home.js +92 -0
- package/dist/src/tui/home.js.map +1 -0
- package/dist/src/tui/markdown.d.ts +18 -0
- package/dist/src/tui/markdown.js +271 -0
- package/dist/src/tui/markdown.js.map +1 -0
- package/dist/src/tui/mode-footer.d.ts +9 -0
- package/dist/src/tui/mode-footer.js +62 -0
- package/dist/src/tui/mode-footer.js.map +1 -0
- package/dist/src/tui/plan-view.d.ts +8 -0
- package/dist/src/tui/plan-view.js +45 -0
- package/dist/src/tui/plan-view.js.map +1 -0
- package/dist/src/tui/prompt-queue.d.ts +18 -0
- package/dist/src/tui/prompt-queue.js +27 -0
- package/dist/src/tui/prompt-queue.js.map +1 -0
- package/dist/src/tui/resize.d.ts +7 -0
- package/dist/src/tui/resize.js +15 -0
- package/dist/src/tui/resize.js.map +1 -0
- package/dist/src/tui/session-picker.d.ts +10 -0
- package/dist/src/tui/session-picker.js +17 -0
- package/dist/src/tui/session-picker.js.map +1 -0
- package/dist/src/tui/session-transcript.d.ts +2 -0
- package/dist/src/tui/session-transcript.js +44 -0
- package/dist/src/tui/session-transcript.js.map +1 -0
- package/dist/src/tui/slash-notice.d.ts +2 -0
- package/dist/src/tui/slash-notice.js +9 -0
- package/dist/src/tui/slash-notice.js.map +1 -0
- package/dist/src/tui/slash.d.ts +21 -0
- package/dist/src/tui/slash.js +103 -0
- package/dist/src/tui/slash.js.map +1 -0
- package/dist/src/tui/splash.d.ts +4 -0
- package/dist/src/tui/splash.js +64 -0
- package/dist/src/tui/splash.js.map +1 -0
- package/dist/src/tui/tool-renderer.d.ts +6 -0
- package/dist/src/tui/tool-renderer.js +1024 -0
- package/dist/src/tui/tool-renderer.js.map +1 -0
- package/dist/src/tui/transcript-spacing.d.ts +1 -0
- package/dist/src/tui/transcript-spacing.js +4 -0
- package/dist/src/tui/transcript-spacing.js.map +1 -0
- package/dist/src/types.d.ts +220 -0
- package/dist/src/types.js +2 -0
- package/dist/src/types.js.map +1 -0
- package/dist/src/util/abort.d.ts +3 -0
- package/dist/src/util/abort.js +19 -0
- package/dist/src/util/abort.js.map +1 -0
- package/dist/src/util/clock.d.ts +2 -0
- package/dist/src/util/clock.js +7 -0
- package/dist/src/util/clock.js.map +1 -0
- package/dist/src/util/fs.d.ts +13 -0
- package/dist/src/util/fs.js +75 -0
- package/dist/src/util/fs.js.map +1 -0
- package/dist/src/util/hash.d.ts +6 -0
- package/dist/src/util/hash.js +50 -0
- package/dist/src/util/hash.js.map +1 -0
- package/dist/src/util/limit.d.ts +11 -0
- package/dist/src/util/limit.js +29 -0
- package/dist/src/util/limit.js.map +1 -0
- package/dist/src/util/types.d.ts +22 -0
- package/dist/src/util/types.js +33 -0
- package/dist/src/util/types.js.map +1 -0
- package/dist/src/validation/acceptance.d.ts +12 -0
- package/dist/src/validation/acceptance.js +251 -0
- package/dist/src/validation/acceptance.js.map +1 -0
- package/dist/src/validation/milestone.d.ts +2 -0
- package/dist/src/validation/milestone.js +141 -0
- package/dist/src/validation/milestone.js.map +1 -0
- package/docs/final-acceptance-task.md +193 -0
- package/docs/public-source-hygiene.md +21 -0
- package/docs/roadmap.md +265 -0
- package/docs/tui-product-design.md +270 -0
- package/package.json +67 -0
- package/skills/coding-workflow/SKILL.md +16 -0
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import { promises as fs } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { pathExists } from "../util/fs.js";
|
|
6
|
+
export class SkillRegistry {
|
|
7
|
+
workspace;
|
|
8
|
+
config;
|
|
9
|
+
constructor(workspace, config) {
|
|
10
|
+
this.workspace = workspace;
|
|
11
|
+
this.config = config;
|
|
12
|
+
}
|
|
13
|
+
async discover() {
|
|
14
|
+
const descriptors = [];
|
|
15
|
+
const roots = await this.discoveryRoots();
|
|
16
|
+
for (const root of roots) {
|
|
17
|
+
if (!(await pathExists(root.path))) {
|
|
18
|
+
continue;
|
|
19
|
+
}
|
|
20
|
+
if (root.importer) {
|
|
21
|
+
descriptors.push(...(await this.discoverImported(root.path, root.trust)));
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
descriptors.push(...(await this.discoverNative(root.path, root.trust)));
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return sortDescriptors(dedupe(descriptors));
|
|
28
|
+
}
|
|
29
|
+
async loadEnabled(descriptors) {
|
|
30
|
+
descriptors ??= await this.discover();
|
|
31
|
+
const enabled = new Set(this.config.skills.enabled);
|
|
32
|
+
const loaded = [];
|
|
33
|
+
for (const descriptor of descriptors) {
|
|
34
|
+
if (!enabled.has(descriptor.id) && !enabled.has(descriptor.name)) {
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
if (descriptor.path) {
|
|
38
|
+
loaded.push({ ...descriptor, body: await fs.readFile(descriptor.path, "utf8") });
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return loaded;
|
|
42
|
+
}
|
|
43
|
+
async discoveryRoots() {
|
|
44
|
+
const home = os.homedir();
|
|
45
|
+
const packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..", "..", "..");
|
|
46
|
+
const roots = [
|
|
47
|
+
{ path: path.join(packageRoot, "skills"), trust: "package" },
|
|
48
|
+
{ path: path.join(home, ".inferoa", "skills"), trust: "user" },
|
|
49
|
+
{ path: path.join(this.workspace.root, ".inferoa", "skills"), trust: "workspace" },
|
|
50
|
+
{ path: path.join(home, ".agents", "skills"), trust: "user" },
|
|
51
|
+
{ path: path.join(this.workspace.root, "AGENTS.md"), trust: "imported", importer: true },
|
|
52
|
+
];
|
|
53
|
+
roots.push(...configuredRoots("INFEROA_SKILL_ROOTS", "user"));
|
|
54
|
+
roots.push(...configuredRoots("INFEROA_INSTRUCTION_ROOTS", "imported", true));
|
|
55
|
+
roots.push(...(await findNestedSkillRoots(path.join(home, ".inferoa", "plugins", "cache"))));
|
|
56
|
+
return roots;
|
|
57
|
+
}
|
|
58
|
+
async discoverNative(root, trust) {
|
|
59
|
+
const entries = sortDirents(await fs.readdir(root, { withFileTypes: true }).catch(() => []));
|
|
60
|
+
const descriptors = [];
|
|
61
|
+
for (const entry of entries) {
|
|
62
|
+
const skillPath = entry.isDirectory() ? path.join(root, entry.name, "SKILL.md") : path.join(root, entry.name);
|
|
63
|
+
if (!skillPath.endsWith(".md") || !(await pathExists(skillPath))) {
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
const body = await fs.readFile(skillPath, "utf8");
|
|
67
|
+
const meta = parseFrontmatter(body);
|
|
68
|
+
const name = String(meta.name ?? path.basename(path.dirname(skillPath)));
|
|
69
|
+
descriptors.push({
|
|
70
|
+
id: slug(name),
|
|
71
|
+
name,
|
|
72
|
+
description: String(meta.description ?? firstParagraph(body) ?? ""),
|
|
73
|
+
source: root,
|
|
74
|
+
path: skillPath,
|
|
75
|
+
trust,
|
|
76
|
+
required_tools: arrayOfStrings(meta.required_tools),
|
|
77
|
+
activation: arrayOfStrings(meta.activation),
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
return descriptors;
|
|
81
|
+
}
|
|
82
|
+
async discoverImported(target, trust) {
|
|
83
|
+
const stat = await fs.stat(target).catch(() => undefined);
|
|
84
|
+
if (!stat) {
|
|
85
|
+
return [];
|
|
86
|
+
}
|
|
87
|
+
const files = stat.isDirectory()
|
|
88
|
+
? sortDirents(await fs.readdir(target, { withFileTypes: true }))
|
|
89
|
+
.filter((entry) => entry.isFile() && /\.(md|mdc|txt)$/.test(entry.name))
|
|
90
|
+
.map((entry) => path.join(target, entry.name))
|
|
91
|
+
: [target];
|
|
92
|
+
return await Promise.all(files.map(async (file) => {
|
|
93
|
+
const body = await fs.readFile(file, "utf8");
|
|
94
|
+
const name = path.basename(file).replace(/\.(md|mdc|txt)$/, "");
|
|
95
|
+
return {
|
|
96
|
+
id: slug(name),
|
|
97
|
+
name,
|
|
98
|
+
description: firstParagraph(body) ?? "Imported local instruction",
|
|
99
|
+
source: file,
|
|
100
|
+
path: file,
|
|
101
|
+
trust,
|
|
102
|
+
required_tools: [],
|
|
103
|
+
activation: [],
|
|
104
|
+
};
|
|
105
|
+
}));
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
function parseFrontmatter(body) {
|
|
109
|
+
const match = /^---\n([\s\S]*?)\n---/.exec(body);
|
|
110
|
+
if (!match) {
|
|
111
|
+
return {};
|
|
112
|
+
}
|
|
113
|
+
const out = {};
|
|
114
|
+
for (const line of match[1].split(/\r?\n/)) {
|
|
115
|
+
const kv = /^([A-Za-z0-9_-]+):\s*(.*)$/.exec(line);
|
|
116
|
+
if (kv?.[1]) {
|
|
117
|
+
out[kv[1]] = kv[2]?.replace(/^["']|["']$/g, "") ?? "";
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return out;
|
|
121
|
+
}
|
|
122
|
+
function firstParagraph(body) {
|
|
123
|
+
return body
|
|
124
|
+
.replace(/^---[\s\S]*?---/, "")
|
|
125
|
+
.split(/\n\s*\n/)
|
|
126
|
+
.map((part) => part.trim())
|
|
127
|
+
.find(Boolean);
|
|
128
|
+
}
|
|
129
|
+
function arrayOfStrings(value) {
|
|
130
|
+
return Array.isArray(value) ? value.map(String) : [];
|
|
131
|
+
}
|
|
132
|
+
function slug(value) {
|
|
133
|
+
return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
|
|
134
|
+
}
|
|
135
|
+
function dedupe(items) {
|
|
136
|
+
const seen = new Set();
|
|
137
|
+
const out = [];
|
|
138
|
+
for (const item of items) {
|
|
139
|
+
const key = `${item.id}:${item.path ?? item.source}`;
|
|
140
|
+
if (!seen.has(key)) {
|
|
141
|
+
seen.add(key);
|
|
142
|
+
out.push(item);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return out;
|
|
146
|
+
}
|
|
147
|
+
function sortDirents(entries) {
|
|
148
|
+
return entries.slice().sort((left, right) => left.name.localeCompare(right.name));
|
|
149
|
+
}
|
|
150
|
+
function sortDescriptors(items) {
|
|
151
|
+
return items.slice().sort((left, right) => {
|
|
152
|
+
const id = left.id.localeCompare(right.id);
|
|
153
|
+
if (id !== 0)
|
|
154
|
+
return id;
|
|
155
|
+
const source = left.source.localeCompare(right.source);
|
|
156
|
+
if (source !== 0)
|
|
157
|
+
return source;
|
|
158
|
+
return (left.path ?? "").localeCompare(right.path ?? "");
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
async function findNestedSkillRoots(root) {
|
|
162
|
+
const out = [];
|
|
163
|
+
async function visit(dir, depth) {
|
|
164
|
+
if (depth > 6) {
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
const entries = sortDirents(await fs.readdir(dir, { withFileTypes: true }).catch(() => []));
|
|
168
|
+
for (const entry of entries) {
|
|
169
|
+
if (!entry.isDirectory()) {
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
const full = path.join(dir, entry.name);
|
|
173
|
+
if (entry.name === "skills") {
|
|
174
|
+
out.push({ path: full, trust: "user" });
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
await visit(full, depth + 1);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
await visit(root, 0);
|
|
181
|
+
return out.sort((left, right) => left.path.localeCompare(right.path));
|
|
182
|
+
}
|
|
183
|
+
function configuredRoots(envName, trust, importer = false) {
|
|
184
|
+
return (process.env[envName] ?? "")
|
|
185
|
+
.split(path.delimiter)
|
|
186
|
+
.map((item) => item.trim())
|
|
187
|
+
.filter(Boolean)
|
|
188
|
+
.map((item) => ({
|
|
189
|
+
path: expandHome(item),
|
|
190
|
+
trust,
|
|
191
|
+
importer,
|
|
192
|
+
}));
|
|
193
|
+
}
|
|
194
|
+
function expandHome(value) {
|
|
195
|
+
if (value === "~") {
|
|
196
|
+
return os.homedir();
|
|
197
|
+
}
|
|
198
|
+
if (value.startsWith(`~${path.sep}`)) {
|
|
199
|
+
return path.join(os.homedir(), value.slice(2));
|
|
200
|
+
}
|
|
201
|
+
return value;
|
|
202
|
+
}
|
|
203
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../../src/skills/registry.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { JsonObject, ToolResult } from "../types.js";
|
|
2
|
+
import type { ToolExecutionContext } from "./context.js";
|
|
3
|
+
export declare function initExperiment(args: JsonObject, context: ToolExecutionContext): Promise<ToolResult>;
|
|
4
|
+
export declare function runExperiment(args: JsonObject, context: ToolExecutionContext): Promise<ToolResult>;
|
|
5
|
+
export declare function logExperiment(args: JsonObject, context: ToolExecutionContext): Promise<ToolResult>;
|
|
6
|
+
export declare function updateNotes(args: JsonObject, context: ToolExecutionContext): Promise<ToolResult>;
|
|
@@ -0,0 +1,412 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { access } from "node:fs/promises";
|
|
4
|
+
import { fail, ok, truncateText } from "../util/limit.js";
|
|
5
|
+
import { createExperiment, logPendingRun, parseAsiLines, parseMetricLines, readAutoresearchState, recordRun, setAutoresearchMode, summarizeAutoresearchProgress, writeAutoresearchState, } from "../autoresearch/state.js";
|
|
6
|
+
const HARNESS = "autoresearch.sh";
|
|
7
|
+
const HARNESS_COMMAND = `bash ${HARNESS}`;
|
|
8
|
+
const HARNESS_VALIDATION_TIMEOUT_MS = 60_000;
|
|
9
|
+
const HARNESS_TIMEOUT_GRACE_MS = 500;
|
|
10
|
+
const HARNESS_OUTPUT_LIMIT_BYTES = 2 * 1024 * 1024;
|
|
11
|
+
export async function initExperiment(args, context) {
|
|
12
|
+
try {
|
|
13
|
+
await access(path.join(context.workspace.root, HARNESS));
|
|
14
|
+
const state = ensureAutoresearchEnabled(context);
|
|
15
|
+
const primaryMetric = requiredString(args.primary_metric, "primary_metric");
|
|
16
|
+
const shouldValidate = booleanArg(args.validate_harness) !== false;
|
|
17
|
+
let harnessStatus;
|
|
18
|
+
if (shouldValidate) {
|
|
19
|
+
harnessStatus = await validateHarness(context, primaryMetric);
|
|
20
|
+
if (!harnessStatus.ok) {
|
|
21
|
+
return fail("harness_validation_failed", harnessStatus.message, autoresearchFailureData(state, {
|
|
22
|
+
harness_status: harnessStatus,
|
|
23
|
+
}));
|
|
24
|
+
}
|
|
25
|
+
if (harnessStatus.parsed_primary === null) {
|
|
26
|
+
return fail("harness_metric_missing", `Harness did not print required METRIC ${primaryMetric}=value.`, autoresearchFailureData(state, {
|
|
27
|
+
harness_status: harnessStatus,
|
|
28
|
+
}));
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
const experiment = createExperiment({
|
|
32
|
+
name: requiredString(args.name, "name"),
|
|
33
|
+
goal: stringArg(args.goal) ?? state.goal,
|
|
34
|
+
primary_metric: primaryMetric,
|
|
35
|
+
metric_unit: stringArg(args.metric_unit),
|
|
36
|
+
direction: directionArg(args.direction),
|
|
37
|
+
scope_paths: stringArrayArg(args.scope_paths),
|
|
38
|
+
off_limits: stringArrayArg(args.off_limits),
|
|
39
|
+
constraints: stringArrayArg(args.constraints),
|
|
40
|
+
max_iterations: positiveIntArg(args.max_iterations),
|
|
41
|
+
harness_status: harnessStatus,
|
|
42
|
+
});
|
|
43
|
+
const next = writeAutoresearchState(context.store, context.session_id, {
|
|
44
|
+
enabled: true,
|
|
45
|
+
goal: experiment.goal ?? state.goal,
|
|
46
|
+
experiment,
|
|
47
|
+
}, context.run_id);
|
|
48
|
+
return ok(`Autoresearch initialized: ${experiment.name}\nMetric: ${experiment.primary_metric}\nBenchmark: ${HARNESS_COMMAND}`, {
|
|
49
|
+
autoresearch: next,
|
|
50
|
+
progress: summarizeAutoresearchProgress(experiment),
|
|
51
|
+
harness_status: harnessStatus,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
return fail("init_experiment_failed", error instanceof Error ? error.message : String(error));
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
export async function runExperiment(args, context) {
|
|
59
|
+
const state = readAutoresearchState(context.store, context.session_id);
|
|
60
|
+
if (!state.enabled || !state.experiment) {
|
|
61
|
+
return fail("autoresearch_not_initialized", "no active autoresearch experiment; call /autoresearch and init_experiment first", autoresearchFailureData(state));
|
|
62
|
+
}
|
|
63
|
+
if (state.experiment.pending_run) {
|
|
64
|
+
return fail("autoresearch_pending_run", `run ${state.experiment.pending_run.id} is still pending; call log_experiment before starting another run`, autoresearchFailureData(state, { pending_run: state.experiment.pending_run }));
|
|
65
|
+
}
|
|
66
|
+
let timeoutMs;
|
|
67
|
+
try {
|
|
68
|
+
timeoutMs = timeoutArg(args);
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
return fail("autoresearch_timeout_invalid", error instanceof Error ? error.message : String(error), autoresearchFailureData(state));
|
|
72
|
+
}
|
|
73
|
+
const started = Date.now();
|
|
74
|
+
const result = await runHarness(context.workspace.root, timeoutMs);
|
|
75
|
+
const durationMs = Date.now() - started;
|
|
76
|
+
const resource = context.store.putResource(context.session_id, "autoresearch.run.output", result.output, {
|
|
77
|
+
command: HARNESS_COMMAND,
|
|
78
|
+
exit_code: result.exitCode,
|
|
79
|
+
duration_ms: durationMs,
|
|
80
|
+
timed_out: result.timedOut,
|
|
81
|
+
output_truncated: result.outputTruncated,
|
|
82
|
+
});
|
|
83
|
+
const parsedMetrics = result.parsedMetrics;
|
|
84
|
+
const parsedPrimary = parsedMetrics[state.experiment.primary_metric] ?? null;
|
|
85
|
+
const nextExperiment = recordRun(state.experiment, {
|
|
86
|
+
command: HARNESS_COMMAND,
|
|
87
|
+
exit_code: result.exitCode,
|
|
88
|
+
duration_ms: durationMs,
|
|
89
|
+
output_resource_uri: resource.uri,
|
|
90
|
+
timed_out: result.timedOut,
|
|
91
|
+
output_truncated: result.outputTruncated,
|
|
92
|
+
parsed_metrics: parsedMetrics,
|
|
93
|
+
parsed_primary: parsedPrimary,
|
|
94
|
+
asi: result.asi,
|
|
95
|
+
completed_at: new Date().toISOString(),
|
|
96
|
+
});
|
|
97
|
+
const next = writeAutoresearchState(context.store, context.session_id, { ...state, experiment: nextExperiment }, context.run_id);
|
|
98
|
+
const preview = truncateText(result.output, 4000).text;
|
|
99
|
+
const status = result.timedOut ? `timed out exit=${result.exitCode}` : result.exitCode === 0 ? "passed" : `failed exit=${result.exitCode}`;
|
|
100
|
+
return ok(`Run ${nextExperiment.pending_run?.id} ${status} in ${(durationMs / 1000).toFixed(1)}s`, {
|
|
101
|
+
command: HARNESS_COMMAND,
|
|
102
|
+
exit_code: result.exitCode,
|
|
103
|
+
duration_ms: durationMs,
|
|
104
|
+
timed_out: result.timedOut,
|
|
105
|
+
output_truncated: result.outputTruncated,
|
|
106
|
+
output_resource_uri: resource.uri,
|
|
107
|
+
parsed_metrics: parsedMetrics,
|
|
108
|
+
parsed_primary: parsedPrimary,
|
|
109
|
+
output_preview: preview,
|
|
110
|
+
autoresearch: next,
|
|
111
|
+
progress: summarizeAutoresearchProgress(nextExperiment),
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
async function validateHarness(context, primaryMetric) {
|
|
115
|
+
const started = Date.now();
|
|
116
|
+
const result = await runHarness(context.workspace.root, HARNESS_VALIDATION_TIMEOUT_MS);
|
|
117
|
+
const durationMs = Date.now() - started;
|
|
118
|
+
const parsedMetrics = result.parsedMetrics;
|
|
119
|
+
const parsedPrimary = parsedMetrics[primaryMetric] ?? null;
|
|
120
|
+
const resource = context.store.putResource(context.session_id, "autoresearch.harness.validation", result.output, {
|
|
121
|
+
command: HARNESS_COMMAND,
|
|
122
|
+
exit_code: result.exitCode,
|
|
123
|
+
duration_ms: durationMs,
|
|
124
|
+
primary_metric: primaryMetric,
|
|
125
|
+
timed_out: result.timedOut,
|
|
126
|
+
output_truncated: result.outputTruncated,
|
|
127
|
+
});
|
|
128
|
+
const ok = result.exitCode === 0;
|
|
129
|
+
const metricMessage = parsedPrimary === null ? `missing METRIC ${primaryMetric}=value` : `${primaryMetric}=${parsedPrimary}`;
|
|
130
|
+
return {
|
|
131
|
+
ok,
|
|
132
|
+
command: HARNESS_COMMAND,
|
|
133
|
+
exit_code: result.exitCode,
|
|
134
|
+
duration_ms: durationMs,
|
|
135
|
+
parsed_metrics: parsedMetrics,
|
|
136
|
+
parsed_primary: parsedPrimary,
|
|
137
|
+
output_resource_uri: resource.uri,
|
|
138
|
+
timed_out: result.timedOut,
|
|
139
|
+
output_truncated: result.outputTruncated,
|
|
140
|
+
message: result.timedOut ? `harness timed out; ${metricMessage}` : ok ? `validated ${metricMessage}` : `harness exited ${result.exitCode}; ${metricMessage}`,
|
|
141
|
+
validated_at: new Date().toISOString(),
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
export async function logExperiment(args, context) {
|
|
145
|
+
let state;
|
|
146
|
+
try {
|
|
147
|
+
state = readAutoresearchState(context.store, context.session_id);
|
|
148
|
+
if (!state.enabled || !state.experiment) {
|
|
149
|
+
return fail("autoresearch_not_initialized", "no active autoresearch experiment; call init_experiment first", autoresearchFailureData(state));
|
|
150
|
+
}
|
|
151
|
+
if (!state.experiment.pending_run) {
|
|
152
|
+
return fail("log_experiment_failed", "no pending autoresearch run; call run_experiment first", autoresearchFailureData(state));
|
|
153
|
+
}
|
|
154
|
+
const status = statusArg(args.status);
|
|
155
|
+
const nextExperiment = logPendingRun(state.experiment, {
|
|
156
|
+
status,
|
|
157
|
+
metric: metricArg(args.metric, state.experiment.pending_run.parsed_primary, status),
|
|
158
|
+
description: requiredString(args.description, "description"),
|
|
159
|
+
metrics: numericRecordArg(args.metrics),
|
|
160
|
+
asi: objectArg(args.asi),
|
|
161
|
+
});
|
|
162
|
+
const reachedLimit = nextExperiment.max_iterations !== undefined &&
|
|
163
|
+
nextExperiment.results.filter((result) => result.status === "keep").length >= nextExperiment.max_iterations;
|
|
164
|
+
const next = writeAutoresearchState(context.store, context.session_id, { ...state, enabled: reachedLimit ? false : state.enabled, experiment: nextExperiment }, context.run_id);
|
|
165
|
+
if (reachedLimit) {
|
|
166
|
+
setAutoresearchMode(context.store, context.session_id, { mode: "off", goal: state.goal }, context.run_id);
|
|
167
|
+
}
|
|
168
|
+
const latest = nextExperiment.results.at(-1);
|
|
169
|
+
return ok(`Logged run ${latest.run_id}: ${latest.status} ${nextExperiment.primary_metric}=${latest.metric === null ? "missing" : latest.metric}`, {
|
|
170
|
+
result: latest,
|
|
171
|
+
autoresearch: next,
|
|
172
|
+
progress: summarizeAutoresearchProgress(nextExperiment),
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
catch (error) {
|
|
176
|
+
return fail("log_experiment_failed", error instanceof Error ? error.message : String(error), state ? autoresearchFailureData(state) : undefined);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
export async function updateNotes(args, context) {
|
|
180
|
+
const state = readAutoresearchState(context.store, context.session_id);
|
|
181
|
+
if (!state.enabled || !state.experiment) {
|
|
182
|
+
return fail("autoresearch_not_initialized", "no active autoresearch experiment; call init_experiment first", autoresearchFailureData(state));
|
|
183
|
+
}
|
|
184
|
+
const body = stringArg(args.body) ?? state.experiment.notes;
|
|
185
|
+
const appendIdea = stringArg(args.append_idea);
|
|
186
|
+
const notes = appendIdea ? appendIdeaToNotes(body, appendIdea) : body;
|
|
187
|
+
const experiment = { ...state.experiment, notes };
|
|
188
|
+
const next = writeAutoresearchState(context.store, context.session_id, { ...state, experiment }, context.run_id);
|
|
189
|
+
return ok(appendIdea ? "Autoresearch idea appended." : "Autoresearch notes updated.", {
|
|
190
|
+
notes,
|
|
191
|
+
autoresearch: next,
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
function autoresearchFailureData(state, extra = {}) {
|
|
195
|
+
return {
|
|
196
|
+
autoresearch: state,
|
|
197
|
+
...(state.experiment ? { progress: summarizeAutoresearchProgress(state.experiment) } : {}),
|
|
198
|
+
...extra,
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
function ensureAutoresearchEnabled(context) {
|
|
202
|
+
const state = readAutoresearchState(context.store, context.session_id);
|
|
203
|
+
if (state.enabled) {
|
|
204
|
+
return state;
|
|
205
|
+
}
|
|
206
|
+
return setAutoresearchMode(context.store, context.session_id, { mode: "on", goal: state.goal }, context.run_id);
|
|
207
|
+
}
|
|
208
|
+
function runHarness(cwd, timeoutMs) {
|
|
209
|
+
return new Promise((resolve) => {
|
|
210
|
+
const child = spawn("bash", [HARNESS], { cwd, detached: true, stdio: ["ignore", "pipe", "pipe"] });
|
|
211
|
+
let output = "";
|
|
212
|
+
let outputBytes = 0;
|
|
213
|
+
let outputTruncated = false;
|
|
214
|
+
let parseBuffer = "";
|
|
215
|
+
const parsedMetrics = {};
|
|
216
|
+
const asi = {};
|
|
217
|
+
let timedOut = false;
|
|
218
|
+
let settled = false;
|
|
219
|
+
let hardKillTimer;
|
|
220
|
+
const appendNotice = (message) => {
|
|
221
|
+
output += `${output.endsWith("\n") || output.length === 0 ? "" : "\n"}${message}\n`;
|
|
222
|
+
};
|
|
223
|
+
const markTruncated = () => {
|
|
224
|
+
if (!outputTruncated) {
|
|
225
|
+
outputTruncated = true;
|
|
226
|
+
appendNotice(`[autoresearch output truncated at ${HARNESS_OUTPUT_LIMIT_BYTES} bytes]`);
|
|
227
|
+
}
|
|
228
|
+
};
|
|
229
|
+
const appendOutput = (chunk) => {
|
|
230
|
+
const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk, "utf8");
|
|
231
|
+
parseHarnessChunk(buffer.toString("utf8"));
|
|
232
|
+
if (outputBytes >= HARNESS_OUTPUT_LIMIT_BYTES) {
|
|
233
|
+
markTruncated();
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
const remaining = HARNESS_OUTPUT_LIMIT_BYTES - outputBytes;
|
|
237
|
+
if (buffer.length <= remaining) {
|
|
238
|
+
output += buffer.toString("utf8");
|
|
239
|
+
outputBytes += buffer.length;
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
output += buffer.subarray(0, remaining).toString("utf8");
|
|
243
|
+
outputBytes = HARNESS_OUTPUT_LIMIT_BYTES;
|
|
244
|
+
markTruncated();
|
|
245
|
+
};
|
|
246
|
+
const parseHarnessChunk = (text) => {
|
|
247
|
+
parseBuffer += text;
|
|
248
|
+
const lines = parseBuffer.split(/\r?\n/);
|
|
249
|
+
parseBuffer = lines.pop() ?? "";
|
|
250
|
+
for (const line of lines) {
|
|
251
|
+
Object.assign(parsedMetrics, parseMetricLines(line));
|
|
252
|
+
Object.assign(asi, parseAsiLines(line));
|
|
253
|
+
}
|
|
254
|
+
};
|
|
255
|
+
const flushHarnessParser = () => {
|
|
256
|
+
if (!parseBuffer.trim()) {
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
Object.assign(parsedMetrics, parseMetricLines(parseBuffer));
|
|
260
|
+
Object.assign(asi, parseAsiLines(parseBuffer));
|
|
261
|
+
parseBuffer = "";
|
|
262
|
+
};
|
|
263
|
+
const signalHarness = (signal) => {
|
|
264
|
+
if (!child.pid) {
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
try {
|
|
268
|
+
process.kill(-child.pid, signal);
|
|
269
|
+
}
|
|
270
|
+
catch {
|
|
271
|
+
try {
|
|
272
|
+
child.kill(signal);
|
|
273
|
+
}
|
|
274
|
+
catch {
|
|
275
|
+
// The process may already have exited between timeout and cleanup.
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
};
|
|
279
|
+
const finish = (exitCode) => {
|
|
280
|
+
if (settled) {
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
settled = true;
|
|
284
|
+
clearTimeout(timeout);
|
|
285
|
+
if (hardKillTimer) {
|
|
286
|
+
clearTimeout(hardKillTimer);
|
|
287
|
+
}
|
|
288
|
+
flushHarnessParser();
|
|
289
|
+
resolve({ exitCode, output, timedOut, outputTruncated, parsedMetrics, asi });
|
|
290
|
+
};
|
|
291
|
+
const timeout = setTimeout(() => {
|
|
292
|
+
timedOut = true;
|
|
293
|
+
appendNotice(`[autoresearch timed out after ${timeoutMs}ms]`);
|
|
294
|
+
signalHarness("SIGTERM");
|
|
295
|
+
hardKillTimer = setTimeout(() => {
|
|
296
|
+
appendNotice(`[autoresearch hard kill after ${timeoutMs + HARNESS_TIMEOUT_GRACE_MS}ms]`);
|
|
297
|
+
signalHarness("SIGKILL");
|
|
298
|
+
}, HARNESS_TIMEOUT_GRACE_MS);
|
|
299
|
+
hardKillTimer.unref();
|
|
300
|
+
}, timeoutMs);
|
|
301
|
+
timeout.unref();
|
|
302
|
+
child.stdout.on("data", (chunk) => {
|
|
303
|
+
appendOutput(chunk);
|
|
304
|
+
});
|
|
305
|
+
child.stderr.on("data", (chunk) => {
|
|
306
|
+
appendOutput(chunk);
|
|
307
|
+
});
|
|
308
|
+
child.on("error", (error) => {
|
|
309
|
+
appendNotice(error.message);
|
|
310
|
+
finish(127);
|
|
311
|
+
});
|
|
312
|
+
child.on("close", (code) => {
|
|
313
|
+
finish(code);
|
|
314
|
+
});
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
function requiredString(value, name) {
|
|
318
|
+
const text = stringArg(value)?.trim();
|
|
319
|
+
if (!text) {
|
|
320
|
+
throw new Error(`${name} is required`);
|
|
321
|
+
}
|
|
322
|
+
return text;
|
|
323
|
+
}
|
|
324
|
+
function stringArg(value) {
|
|
325
|
+
return typeof value === "string" ? value : undefined;
|
|
326
|
+
}
|
|
327
|
+
function stringArrayArg(value) {
|
|
328
|
+
return Array.isArray(value) ? value.filter((item) => typeof item === "string") : undefined;
|
|
329
|
+
}
|
|
330
|
+
function directionArg(value) {
|
|
331
|
+
return value === "lower" || value === "higher" ? value : undefined;
|
|
332
|
+
}
|
|
333
|
+
function statusArg(value) {
|
|
334
|
+
if (value === "keep" || value === "discard" || value === "crash" || value === "checks_failed") {
|
|
335
|
+
return value;
|
|
336
|
+
}
|
|
337
|
+
throw new Error("status must be keep, discard, crash, or checks_failed");
|
|
338
|
+
}
|
|
339
|
+
function positiveIntArg(value) {
|
|
340
|
+
if (value === undefined) {
|
|
341
|
+
return undefined;
|
|
342
|
+
}
|
|
343
|
+
if (typeof value !== "number" || !Number.isInteger(value) || value <= 0) {
|
|
344
|
+
throw new Error("max_iterations must be a positive integer when provided");
|
|
345
|
+
}
|
|
346
|
+
return value;
|
|
347
|
+
}
|
|
348
|
+
function finiteNumberArg(value, name) {
|
|
349
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
350
|
+
throw new Error(`${name} must be a finite number`);
|
|
351
|
+
}
|
|
352
|
+
return value;
|
|
353
|
+
}
|
|
354
|
+
function metricArg(value, fallback, status) {
|
|
355
|
+
if (value !== undefined) {
|
|
356
|
+
return finiteNumberArg(value, "metric");
|
|
357
|
+
}
|
|
358
|
+
if (typeof fallback === "number" && Number.isFinite(fallback)) {
|
|
359
|
+
return fallback;
|
|
360
|
+
}
|
|
361
|
+
if (status !== "keep") {
|
|
362
|
+
return null;
|
|
363
|
+
}
|
|
364
|
+
throw new Error("metric is required when the pending run did not parse the primary metric");
|
|
365
|
+
}
|
|
366
|
+
function timeoutArg(args) {
|
|
367
|
+
const timeoutMs = args.timeout_ms;
|
|
368
|
+
if (timeoutMs !== undefined) {
|
|
369
|
+
if (typeof timeoutMs === "number" && Number.isFinite(timeoutMs) && timeoutMs > 0) {
|
|
370
|
+
return Math.trunc(timeoutMs);
|
|
371
|
+
}
|
|
372
|
+
throw new Error("timeout_ms must be a positive finite number when provided");
|
|
373
|
+
}
|
|
374
|
+
const timeoutSeconds = args.timeout_seconds;
|
|
375
|
+
if (timeoutSeconds !== undefined) {
|
|
376
|
+
if (typeof timeoutSeconds === "number" && Number.isFinite(timeoutSeconds) && timeoutSeconds > 0) {
|
|
377
|
+
return Math.trunc(timeoutSeconds * 1000);
|
|
378
|
+
}
|
|
379
|
+
throw new Error("timeout_seconds must be a positive finite number when provided");
|
|
380
|
+
}
|
|
381
|
+
return 600_000;
|
|
382
|
+
}
|
|
383
|
+
function numericRecordArg(value) {
|
|
384
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
385
|
+
return undefined;
|
|
386
|
+
}
|
|
387
|
+
const out = {};
|
|
388
|
+
for (const [key, raw] of Object.entries(value)) {
|
|
389
|
+
if (typeof raw === "number" && Number.isFinite(raw)) {
|
|
390
|
+
out[key] = raw;
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
return out;
|
|
394
|
+
}
|
|
395
|
+
function objectArg(value) {
|
|
396
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : undefined;
|
|
397
|
+
}
|
|
398
|
+
function booleanArg(value) {
|
|
399
|
+
return typeof value === "boolean" ? value : undefined;
|
|
400
|
+
}
|
|
401
|
+
function appendIdeaToNotes(notes, idea) {
|
|
402
|
+
const trimmed = notes.trimEnd();
|
|
403
|
+
const bullet = `- ${idea.trim()}`;
|
|
404
|
+
if (!trimmed) {
|
|
405
|
+
return `## Ideas\n${bullet}\n`;
|
|
406
|
+
}
|
|
407
|
+
if (!trimmed.includes("## Ideas")) {
|
|
408
|
+
return `${trimmed}\n\n## Ideas\n${bullet}\n`;
|
|
409
|
+
}
|
|
410
|
+
return `${trimmed}\n${bullet}\n`;
|
|
411
|
+
}
|
|
412
|
+
//# sourceMappingURL=autoresearch-tools.js.map
|