@hilbras/omninode 0.9.0 → 0.10.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 +23 -4
- package/dist/cli/index.js +143 -24
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +7 -1
- package/dist/index.js +34 -3
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -18,8 +18,9 @@ existing intelligence; it does not try to become another model.
|
|
|
18
18
|
|
|
19
19
|
## Status
|
|
20
20
|
|
|
21
|
-
**v0.
|
|
22
|
-
for the full plan and roadmap).
|
|
21
|
+
**v0.10.0 — Phase 9: End-to-End Workflow** (see [docs/DEVELOPMENT_PLAN.md](docs/DEVELOPMENT_PLAN.md)
|
|
22
|
+
for the full plan and roadmap). The full loop from §28 works in one command:
|
|
23
|
+
`User → project context → memory → research agents → reports → aggregation → planner → plan → execution → result`.
|
|
23
24
|
|
|
24
25
|
What exists today:
|
|
25
26
|
|
|
@@ -97,6 +98,15 @@ What exists today:
|
|
|
97
98
|
- Plans persist to `.omninode/plans.json`; pipeline runs record `planId`
|
|
98
99
|
- Configure with `planner: { kind: model | heuristic, model, instruction }`
|
|
99
100
|
— never hardcoded to any provider
|
|
101
|
+
- **End-to-end workflow** (§28, §27 Phase 9):
|
|
102
|
+
- One-command execution via the top-level `run` alias (`omninode run <pipeline> "<objective>"`)
|
|
103
|
+
- `omninode status` — full project state overview: providers, agents,
|
|
104
|
+
roles, pipelines, task/run/report/plan counts, memory provider + entries
|
|
105
|
+
- Pipeline runs persist their objective and a final `resultSummary` (§28 "Result")
|
|
106
|
+
- Error recovery: `task retry [--run]` (reset a failed/cancelled task) and
|
|
107
|
+
`pipeline retry <run-id>` (re-run a recorded failed run as a new run)
|
|
108
|
+
- Full integration test (`tests/e2e.test.ts`) drives the entire §28 workflow
|
|
109
|
+
through the real CLI against a local gateway
|
|
100
110
|
- Typed error hierarchy (`src/errors/`)
|
|
101
111
|
- Leveled logger with pluggable sink (`src/logger/`)
|
|
102
112
|
- Configuration system: `omninode.yaml` with `${ENV_VAR}` expansion and strict
|
|
@@ -190,6 +200,14 @@ project:
|
|
|
190
200
|
model: my-gateway:chatgpt
|
|
191
201
|
```
|
|
192
202
|
|
|
203
|
+
```bash
|
|
204
|
+
omninode init
|
|
205
|
+
omninode status
|
|
206
|
+
omninode run repo-audit "Audit the authentication module" # the whole workflow
|
|
207
|
+
omninode pipeline retry run-xxxx # recover a failed run
|
|
208
|
+
omninode task retry task-yyyy --run # recover a failed task
|
|
209
|
+
```
|
|
210
|
+
|
|
193
211
|
Persistent memory (§19–§20) — opt in with `memory: { provider: local }` or
|
|
194
212
|
`provider: remembera` in `omninode.yaml`:
|
|
195
213
|
|
|
@@ -236,8 +254,9 @@ Phases 0–11 are laid out in [docs/DEVELOPMENT_PLAN.md](docs/DEVELOPMENT_PLAN.m
|
|
|
236
254
|
| v0.7.0 | Multi-AI Reports |
|
|
237
255
|
| v0.7.0 | Multi-AI Reports |
|
|
238
256
|
| v0.8.0 | Remembera / Memory |
|
|
239
|
-
|
|
|
240
|
-
| v0.
|
|
257
|
+
| v0.8.0 | Remembera / Memory |
|
|
258
|
+
| v0.9.0 | Planner |
|
|
259
|
+
| **v0.10.0** | **End-to-End Workflow (current)** |
|
|
241
260
|
| v1.0.0 | Production Release |
|
|
242
261
|
|
|
243
262
|
Core architectural rule: OmniNode must run successfully with **zero Hilbras
|
package/dist/cli/index.js
CHANGED
|
@@ -6,7 +6,7 @@ import { realpathSync } from "fs";
|
|
|
6
6
|
import { Command } from "commander";
|
|
7
7
|
|
|
8
8
|
// src/version.ts
|
|
9
|
-
var OMNINODE_VERSION = "0.
|
|
9
|
+
var OMNINODE_VERSION = "0.10.0";
|
|
10
10
|
|
|
11
11
|
// src/errors/index.ts
|
|
12
12
|
var OmniNodeError = class extends Error {
|
|
@@ -1475,6 +1475,25 @@ var TaskEngine = class {
|
|
|
1475
1475
|
}
|
|
1476
1476
|
return this.transition(task, "cancelled");
|
|
1477
1477
|
}
|
|
1478
|
+
/** Error recovery (§27 Phase 9): reset a failed/cancelled task so it can run again. */
|
|
1479
|
+
async retry(id) {
|
|
1480
|
+
const task = await this.mustGet(id);
|
|
1481
|
+
if (task.status !== "failed" && task.status !== "cancelled") {
|
|
1482
|
+
throw new TaskError(
|
|
1483
|
+
"TASK_INVALID",
|
|
1484
|
+
`Only failed or cancelled tasks can be retried \u2014 task ${id} is "${task.status}".`
|
|
1485
|
+
);
|
|
1486
|
+
}
|
|
1487
|
+
const { result: _discarded, ...rest } = task;
|
|
1488
|
+
const reset = {
|
|
1489
|
+
...rest,
|
|
1490
|
+
status: "created",
|
|
1491
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1492
|
+
};
|
|
1493
|
+
await this.options.store.save(reset);
|
|
1494
|
+
this.log.info(`Task ${id} reset for retry.`);
|
|
1495
|
+
return reset;
|
|
1496
|
+
}
|
|
1478
1497
|
async get(id) {
|
|
1479
1498
|
return this.options.store.get(id);
|
|
1480
1499
|
}
|
|
@@ -1894,6 +1913,17 @@ function registerTaskCommands(program) {
|
|
|
1894
1913
|
const cancelled = await engine.cancel(taskId);
|
|
1895
1914
|
console.log(`Task ${cancelled.id} is now "${cancelled.status}".`);
|
|
1896
1915
|
});
|
|
1916
|
+
task.command("retry <task-id>").description("Reset a failed/cancelled task to created; combine with --run to execute it again.").option("--run", "Run the task again immediately after resetting it.").action(async (taskId, options) => {
|
|
1917
|
+
const config = loadConfig();
|
|
1918
|
+
const engine = createTaskEngine(config);
|
|
1919
|
+
const reset = await engine.retry(taskId);
|
|
1920
|
+
console.log(`Task ${reset.id} reset to "${reset.status}".`);
|
|
1921
|
+
if (!options.run) return;
|
|
1922
|
+
console.log(`Running task ${reset.id}...`);
|
|
1923
|
+
const finished = await engine.run(reset.id);
|
|
1924
|
+
printOutcome(finished);
|
|
1925
|
+
if (finished.status !== "completed") process.exitCode = 1;
|
|
1926
|
+
});
|
|
1897
1927
|
}
|
|
1898
1928
|
function printOutcome(task, options = {}) {
|
|
1899
1929
|
console.log(` id: ${task.id}`);
|
|
@@ -2639,6 +2669,7 @@ var PipelineEngine = class {
|
|
|
2639
2669
|
const run2 = {
|
|
2640
2670
|
id: `run-${Date.now().toString(36)}-${randomBytes5(3).toString("hex")}`,
|
|
2641
2671
|
pipelineId: def.id,
|
|
2672
|
+
objective,
|
|
2642
2673
|
status: "running",
|
|
2643
2674
|
startedAt: now,
|
|
2644
2675
|
stepRuns: []
|
|
@@ -2698,9 +2729,19 @@ ${contribution}`);
|
|
|
2698
2729
|
const failed = [...outcomes.values()].some((o) => o.status === "failed");
|
|
2699
2730
|
const finalStatus = failed ? "failed" : "completed";
|
|
2700
2731
|
const planId = [...outcomes.values()].find((o) => o.planId !== void 0)?.planId;
|
|
2732
|
+
let resultSummary;
|
|
2733
|
+
for (let i = def.steps.length - 1; i >= 0; i -= 1) {
|
|
2734
|
+
const step = def.steps[i];
|
|
2735
|
+
const outcome = outcomes.get(step.id);
|
|
2736
|
+
if (outcome?.status !== "completed" || outcome.taskIds.length === 0) continue;
|
|
2737
|
+
const stepTasks = (await Promise.all(outcome.taskIds.map((id) => this.options.tasks.get(id)))).filter((task) => task !== void 0);
|
|
2738
|
+
const summaries = stepTasks.map((task) => task.result?.summary).filter((summary) => summary !== void 0);
|
|
2739
|
+
if (summaries.length > 0) resultSummary = summaries.join("\n");
|
|
2740
|
+
break;
|
|
2741
|
+
}
|
|
2701
2742
|
let combinedReportId;
|
|
2702
2743
|
if (this.options.reports) {
|
|
2703
|
-
const taskIds =
|
|
2744
|
+
const taskIds = def.steps.filter((step) => step.kind === "research" || step.kind === "analyze").flatMap((step) => outcomes.get(step.id)?.taskIds ?? []);
|
|
2704
2745
|
const tasks = (await Promise.all(taskIds.map((id) => this.options.tasks.get(id)))).filter((task) => task !== void 0);
|
|
2705
2746
|
const collected = await this.options.reports.collectFromTasks(tasks);
|
|
2706
2747
|
await this.options.reports.saveReports(collected);
|
|
@@ -2720,7 +2761,8 @@ ${contribution}`);
|
|
|
2720
2761
|
finishedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2721
2762
|
...run2.reports !== void 0 && run2.reports.length > 0 ? { reports: run2.reports } : {},
|
|
2722
2763
|
...combinedReportId !== void 0 ? { combinedReportId } : {},
|
|
2723
|
-
...planId !== void 0 ? { planId } : {}
|
|
2764
|
+
...planId !== void 0 ? { planId } : {},
|
|
2765
|
+
...resultSummary !== void 0 ? { resultSummary } : {}
|
|
2724
2766
|
};
|
|
2725
2767
|
await this.options.store.save(finished);
|
|
2726
2768
|
this.log.info(`Pipeline ${def.id} finished: ${finalStatus}.`);
|
|
@@ -3100,31 +3142,27 @@ function registerPipelineCommands(program) {
|
|
|
3100
3142
|
});
|
|
3101
3143
|
pipeline.command("run <id> [objective]").description(
|
|
3102
3144
|
"Run a pipeline. The objective argument overrides the definition's default objective."
|
|
3103
|
-
).action(async (id, objective) =>
|
|
3145
|
+
).action(async (id, objective) => runPipelineAction(id, objective));
|
|
3146
|
+
pipeline.command("retry <run-id> [objective]").description(
|
|
3147
|
+
"Error recovery: re-run a recorded pipeline run as a new run, reusing its objective."
|
|
3148
|
+
).action(async (runId, objective) => {
|
|
3104
3149
|
const config = loadConfig();
|
|
3105
|
-
const def = findPipeline(config, id);
|
|
3106
3150
|
const engine = buildPipelineEngine(config);
|
|
3107
|
-
|
|
3108
|
-
|
|
3109
|
-
|
|
3110
|
-
|
|
3111
|
-
|
|
3112
|
-
|
|
3113
|
-
const suffix = stepRun.error ? ` \u2014 ${stepRun.error}` : "";
|
|
3114
|
-
console.log(` [${stepRun.stepId}] ${stepRun.status}${suffix}`);
|
|
3115
|
-
}
|
|
3116
|
-
});
|
|
3117
|
-
if (finished.reports && finished.reports.length > 0) {
|
|
3118
|
-
console.log(` reports: ${finished.reports.length} structured report(s)`);
|
|
3119
|
-
}
|
|
3120
|
-
if (finished.combinedReportId) {
|
|
3121
|
-
console.log(` combined report: ${finished.combinedReportId}`);
|
|
3151
|
+
const previous = await engine.get(runId);
|
|
3152
|
+
if (!previous) {
|
|
3153
|
+
throw new PipelineError(
|
|
3154
|
+
"PIPELINE_NOT_FOUND",
|
|
3155
|
+
`Pipeline run "${runId}" does not exist in this project's run store.`
|
|
3156
|
+
);
|
|
3122
3157
|
}
|
|
3123
|
-
|
|
3124
|
-
|
|
3158
|
+
const def = config.project.pipelines.find((p) => p.id === previous.pipelineId);
|
|
3159
|
+
if (!def) {
|
|
3160
|
+
throw new PipelineError(
|
|
3161
|
+
"PIPELINE_NOT_FOUND",
|
|
3162
|
+
`Pipeline "${previous.pipelineId}" (run ${runId}) is no longer configured in omninode.yaml.`
|
|
3163
|
+
);
|
|
3125
3164
|
}
|
|
3126
|
-
|
|
3127
|
-
if (finished.status !== "completed") process.exitCode = 1;
|
|
3165
|
+
await runPipelineAction(def.id, objective ?? previous.objective);
|
|
3128
3166
|
});
|
|
3129
3167
|
pipeline.command("runs <id>").description("List recorded runs of a pipeline.").action(async (id) => {
|
|
3130
3168
|
const config = loadConfig();
|
|
@@ -3153,6 +3191,7 @@ function registerPipelineCommands(program) {
|
|
|
3153
3191
|
console.log(` status: ${run2.status}`);
|
|
3154
3192
|
console.log(` started: ${run2.startedAt ?? "?"}`);
|
|
3155
3193
|
if (run2.finishedAt) console.log(` finished: ${run2.finishedAt}`);
|
|
3194
|
+
if (run2.resultSummary) console.log(` result: ${run2.resultSummary}`);
|
|
3156
3195
|
for (const stepRun of run2.stepRuns) {
|
|
3157
3196
|
console.log(` [${stepRun.stepId}] ${stepRun.status}`);
|
|
3158
3197
|
}
|
|
@@ -3161,6 +3200,38 @@ function registerPipelineCommands(program) {
|
|
|
3161
3200
|
}
|
|
3162
3201
|
});
|
|
3163
3202
|
}
|
|
3203
|
+
async function runPipelineAction(id, objective) {
|
|
3204
|
+
const config = loadConfig();
|
|
3205
|
+
const def = findPipeline(config, id);
|
|
3206
|
+
const engine = buildPipelineEngine(config);
|
|
3207
|
+
console.log(
|
|
3208
|
+
`Running pipeline "${def.id}" (${def.steps.length} step(s))${objective ? ` \u2014 "${objective}"` : ""}...`
|
|
3209
|
+
);
|
|
3210
|
+
const finished = await engine.run(def, {
|
|
3211
|
+
...objective !== void 0 ? { objective } : {},
|
|
3212
|
+
onStep: (stepRun) => {
|
|
3213
|
+
const suffix = stepRun.error ? ` \u2014 ${stepRun.error}` : "";
|
|
3214
|
+
console.log(` [${stepRun.stepId}] ${stepRun.status}${suffix}`);
|
|
3215
|
+
}
|
|
3216
|
+
});
|
|
3217
|
+
if (finished.reports && finished.reports.length > 0) {
|
|
3218
|
+
console.log(` reports: ${finished.reports.length} structured report(s)`);
|
|
3219
|
+
}
|
|
3220
|
+
if (finished.combinedReportId) {
|
|
3221
|
+
console.log(` combined report: ${finished.combinedReportId}`);
|
|
3222
|
+
}
|
|
3223
|
+
if (finished.planId) {
|
|
3224
|
+
console.log(` plan: ${finished.planId}`);
|
|
3225
|
+
}
|
|
3226
|
+
if (finished.resultSummary) {
|
|
3227
|
+
console.log(` result: ${finished.resultSummary}`);
|
|
3228
|
+
}
|
|
3229
|
+
console.log(`Pipeline ${finished.status}. Run id: ${finished.id}`);
|
|
3230
|
+
if (finished.status !== "completed") process.exitCode = 1;
|
|
3231
|
+
}
|
|
3232
|
+
function registerRunAlias(program) {
|
|
3233
|
+
program.command("run <id> [objective]").description("Alias of `omninode pipeline run` \u2014 execute a configured pipeline end to end.").action(async (id, objective) => runPipelineAction(id, objective));
|
|
3234
|
+
}
|
|
3164
3235
|
function findPipeline(config, id) {
|
|
3165
3236
|
const def = config.project.pipelines.find((p) => p.id === id);
|
|
3166
3237
|
if (!def) {
|
|
@@ -3272,6 +3343,52 @@ function registerPlanCommands(program) {
|
|
|
3272
3343
|
});
|
|
3273
3344
|
}
|
|
3274
3345
|
|
|
3346
|
+
// src/cli/commands/status.ts
|
|
3347
|
+
function countBy(items, key) {
|
|
3348
|
+
const counts = /* @__PURE__ */ new Map();
|
|
3349
|
+
for (const item of items) {
|
|
3350
|
+
const k = key(item);
|
|
3351
|
+
counts.set(k, (counts.get(k) ?? 0) + 1);
|
|
3352
|
+
}
|
|
3353
|
+
if (counts.size === 0) return "0";
|
|
3354
|
+
return `${items.length} (${[...counts.entries()].map(([k, v]) => `${k} ${v}`).join(", ")})`;
|
|
3355
|
+
}
|
|
3356
|
+
function registerStatusCommand(program) {
|
|
3357
|
+
program.command("status").description("Show the project's OmniNode state overview (config, tasks, runs, reports, plans, memory).").action(async () => {
|
|
3358
|
+
const config = loadConfig();
|
|
3359
|
+
console.log(`OmniNode v${OMNINODE_VERSION} \u2014 project "${config.project.name}"`);
|
|
3360
|
+
console.log(
|
|
3361
|
+
`providers: ${config.project.providers.length > 0 ? config.project.providers.map((p) => p.name).join(", ") : "none"}`
|
|
3362
|
+
);
|
|
3363
|
+
console.log(
|
|
3364
|
+
`agents: ${config.project.agents.length > 0 ? config.project.agents.map((a) => a.name).join(", ") : "none"}`
|
|
3365
|
+
);
|
|
3366
|
+
console.log(
|
|
3367
|
+
`roles: ${config.project.roles.length > 0 ? config.project.roles.map((r) => r.id).join(", ") : "none"}`
|
|
3368
|
+
);
|
|
3369
|
+
console.log(
|
|
3370
|
+
`pipelines: ${config.project.pipelines.length > 0 ? config.project.pipelines.map((p) => p.id).join(", ") : "none"}`
|
|
3371
|
+
);
|
|
3372
|
+
const tasks = await new FileTaskStore().list();
|
|
3373
|
+
console.log(`tasks: ${countBy(tasks, (t) => t.status)}`);
|
|
3374
|
+
const runs = await new FilePipelineRunStore().list();
|
|
3375
|
+
console.log(`runs: ${countBy(runs, (r) => r.status)}`);
|
|
3376
|
+
const reportStore = new FileReportStore();
|
|
3377
|
+
const reports = await reportStore.listReports();
|
|
3378
|
+
const combined = await reportStore.listCombined();
|
|
3379
|
+
console.log(`reports: ${reports.length} raw, ${combined.length} combined`);
|
|
3380
|
+
const plans = await new FilePlanStore().list();
|
|
3381
|
+
console.log(`plans: ${plans.length}`);
|
|
3382
|
+
if (config.project.memory !== void 0) {
|
|
3383
|
+
const memory = new MemoryService(createMemoryProvider(config.project.memory));
|
|
3384
|
+
const entries = await memory.providerRef.query({ limit: 1e4 });
|
|
3385
|
+
console.log(`memory: ${memory.providerName}, ${entries.length} entr(ies)`);
|
|
3386
|
+
} else {
|
|
3387
|
+
console.log("memory: not configured (local provider available \u2014 add a `memory:` section)");
|
|
3388
|
+
}
|
|
3389
|
+
});
|
|
3390
|
+
}
|
|
3391
|
+
|
|
3275
3392
|
// src/cli/index.ts
|
|
3276
3393
|
function createProgram() {
|
|
3277
3394
|
const program = new Command();
|
|
@@ -3289,6 +3406,8 @@ function createProgram() {
|
|
|
3289
3406
|
registerPipelineCommands(program);
|
|
3290
3407
|
registerMemoryCommands(program);
|
|
3291
3408
|
registerPlanCommands(program);
|
|
3409
|
+
registerStatusCommand(program);
|
|
3410
|
+
registerRunAlias(program);
|
|
3292
3411
|
return program;
|
|
3293
3412
|
}
|
|
3294
3413
|
async function run(argv) {
|