@hilbras/omninode 2.0.0-alpha.8 → 2.0.0-alpha.9

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 CHANGED
@@ -18,9 +18,9 @@ existing intelligence; it does not try to become another model.
18
18
 
19
19
  ## Status
20
20
 
21
- **v2.0.0-alpha.8 — v2 Phase 8: Remembera Integration v2**
21
+ **v2.0.0-alpha.9 — v2 Phase 9: Report System v2**
22
22
  (the reliability & interoperability line: architecture → execution →
23
- protocol → adapters → pipelines → providers → OmniHilbras → memory) (the v2
23
+ protocol → adapters → pipelines → providers → OmniHilbras → memory → reports) (the v2
24
24
  roadmap is [docs/ROADMAP_V2.md](docs/ROADMAP_V2.md); the v1 plan is complete:
25
25
  the
26
26
  full loop from §28 runs in one command —
@@ -31,6 +31,13 @@ for the plan and [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) for the design).
31
31
 
32
32
  ### What's new in v2 (so far)
33
33
 
34
+ - **Phase 9 — Report System v2**: reports are validated machine-readable
35
+ objects — `validateReport()` runs on everything entering the system and
36
+ malformed reports are rejected with diagnostics instead of poisoning
37
+ aggregation. Findings gain description/confidence/source/recommendation,
38
+ evidence is typed (`file`, `line`, `url`, `command`, `observation`,
39
+ `artifact`), and reports carry artifacts and run provenance.
40
+ Details: [REPORTS.md](docs/REPORTS.md).
34
41
  - **Phase 8 — Remembera / Memory v2**: the memory contract is formalized
35
42
  (`retrieve` / `store` / `search` / `metadata` with v1 `query` / `write`
36
43
  aliases kept working), memory gains structured **categories** (decisions,
@@ -339,7 +346,7 @@ required for the core engine.
339
346
  - [Architecture](docs/ARCHITECTURE.md) — module map, workflow, local state
340
347
  - [Security model](docs/SECURITY.md) — secrets, process execution, env isolation, audit log
341
348
  - [API stability policy](docs/API_STABILITY.md) and [public API inventory](docs/API.md)
342
- - [Agent Protocol v2 spec](docs/PROTOCOL.md) · [Providers](docs/PROVIDERS.md) · [Memory](docs/MEMORY.md) · [Deprecations](docs/DEPRECATIONS.md) · [Changelog](CHANGELOG.md) · [v2 roadmap](docs/ROADMAP_V2.md)
349
+ - [Agent Protocol v2 spec](docs/PROTOCOL.md) · [Providers](docs/PROVIDERS.md) · [Memory](docs/MEMORY.md) · [Reports](docs/REPORTS.md) · [Deprecations](docs/DEPRECATIONS.md) · [Changelog](CHANGELOG.md) · [v2 roadmap](docs/ROADMAP_V2.md)
343
350
  - [Example project](examples/demo/) — a runnable end-to-end demo (`./demo.sh`)
344
351
 
345
352
  Prefer a container? `docker build -t omninode .` then
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 = "2.0.0-alpha.8";
9
+ var OMNINODE_VERSION = "2.0.0-alpha.9";
10
10
 
11
11
  // src/errors/index.ts
12
12
  var OmniNodeError = class extends Error {
@@ -2997,6 +2997,84 @@ function truncate2(text, max) {
2997
2997
 
2998
2998
  // src/reports/aggregate.ts
2999
2999
  import { randomBytes as randomBytes2 } from "crypto";
3000
+
3001
+ // src/reports/schema.ts
3002
+ import { z as z2 } from "zod";
3003
+ var evidenceSchema = z2.union([
3004
+ z2.string().transform((excerpt) => ({ kind: "observation", excerpt })),
3005
+ z2.object({
3006
+ kind: z2.enum(["file", "line", "url", "command", "observation", "artifact"]),
3007
+ ref: z2.string().optional(),
3008
+ line: z2.number().int().optional(),
3009
+ excerpt: z2.string().optional(),
3010
+ agent: z2.string().optional()
3011
+ })
3012
+ ]);
3013
+ var findingSchema = z2.object({
3014
+ id: z2.string().min(1),
3015
+ title: z2.string().min(1),
3016
+ detail: z2.string().default(""),
3017
+ description: z2.string().optional(),
3018
+ severity: z2.enum(["info", "low", "medium", "high", "critical"]).optional(),
3019
+ confidence: z2.enum(["low", "medium", "high"]).optional(),
3020
+ evidence: z2.array(evidenceSchema).optional(),
3021
+ location: z2.string().optional(),
3022
+ tags: z2.array(z2.string()).optional(),
3023
+ source: z2.object({ agent: z2.string().optional(), reportId: z2.string().optional(), pipelineRunId: z2.string().optional() }).optional(),
3024
+ recommendation: z2.string().optional()
3025
+ });
3026
+ var artifactSchema = z2.object({
3027
+ kind: z2.enum(["file", "patch", "log", "screenshot", "document", "data"]),
3028
+ ref: z2.string().min(1),
3029
+ description: z2.string().optional(),
3030
+ mediaType: z2.string().optional(),
3031
+ bytes: z2.number().int().optional()
3032
+ });
3033
+ var reportSchema = z2.object({
3034
+ id: z2.string().min(1),
3035
+ taskId: z2.string().min(1),
3036
+ agent: z2.string().min(1),
3037
+ agentId: z2.string().optional(),
3038
+ pipelineRunId: z2.string().optional(),
3039
+ model: z2.string().optional(),
3040
+ summary: z2.string(),
3041
+ findings: z2.array(findingSchema),
3042
+ recommendations: z2.array(z2.string()),
3043
+ confidence: z2.enum(["low", "medium", "high"]).optional(),
3044
+ evidence: z2.array(evidenceSchema).optional(),
3045
+ artifacts: z2.array(artifactSchema).optional(),
3046
+ createdAt: z2.string().min(1),
3047
+ metadata: z2.record(z2.unknown()).optional()
3048
+ });
3049
+ function validateReport(value) {
3050
+ const parsed = reportSchema.safeParse(value);
3051
+ if (!parsed.success) {
3052
+ const issues = parsed.error.issues.map((issue) => ({
3053
+ path: issue.path.join(".") || "(root)",
3054
+ message: issue.message
3055
+ }));
3056
+ return {
3057
+ valid: false,
3058
+ issues,
3059
+ reason: `report failed schema validation (${issues.length} issue(s))`
3060
+ };
3061
+ }
3062
+ const report = parsed.data;
3063
+ report.findings = report.findings.map((finding) => ({
3064
+ ...finding,
3065
+ description: finding.description ?? finding.detail,
3066
+ ...finding.source === void 0 ? { source: { agent: report.agent, reportId: report.id, ...report.pipelineRunId !== void 0 ? { pipelineRunId: report.pipelineRunId } : {} } } : {}
3067
+ }));
3068
+ return { valid: true, report };
3069
+ }
3070
+ function normalizeEvidence(input) {
3071
+ if (input === void 0) return void 0;
3072
+ return input.map(
3073
+ (item) => typeof item === "string" ? { kind: "observation", excerpt: item } : item
3074
+ );
3075
+ }
3076
+
3077
+ // src/reports/aggregate.ts
3000
3078
  var SEVERITY_RANK = {
3001
3079
  info: 0,
3002
3080
  low: 1,
@@ -3043,7 +3121,7 @@ function mergeFinding(groups, finding, report, threshold) {
3043
3121
  group.occurrences += 1;
3044
3122
  group.details.push(finding.detail);
3045
3123
  group.sources.push(source);
3046
- group.evidence.push(...finding.evidence ?? []);
3124
+ group.evidence.push(...normalizeEvidence(finding.evidence) ?? []);
3047
3125
  if (finding.severity && (!group.severity || SEVERITY_RANK[finding.severity] > SEVERITY_RANK[group.severity])) {
3048
3126
  group.severity = finding.severity;
3049
3127
  }
@@ -3055,7 +3133,7 @@ function mergeFinding(groups, finding, report, threshold) {
3055
3133
  sources: [source],
3056
3134
  occurrences: 1,
3057
3135
  ...finding.severity ? { severity: finding.severity } : {},
3058
- evidence: [...finding.evidence ?? []]
3136
+ evidence: [...normalizeEvidence(finding.evidence) ?? []]
3059
3137
  });
3060
3138
  }
3061
3139
  function mergeRecommendation(groups, text, report, threshold) {
@@ -3137,6 +3215,7 @@ function extractReportFromText(raw, meta) {
3137
3215
  id: `${meta.taskId}-f-${index + 1}`,
3138
3216
  title: truncate4(text, 120),
3139
3217
  detail: text,
3218
+ description: text,
3140
3219
  severity: guessSeverity(text)
3141
3220
  }));
3142
3221
  return {
@@ -3147,7 +3226,8 @@ function extractReportFromText(raw, meta) {
3147
3226
  summary,
3148
3227
  findings,
3149
3228
  recommendations: recommendationTexts.map((t) => truncate4(t, 200)),
3150
- metadata: evidenceTexts.length > 0 ? { evidence: evidenceTexts } : void 0,
3229
+ ...evidenceTexts.length > 0 ? { evidence: evidenceTexts.map((excerpt) => ({ kind: "observation", excerpt })) } : {},
3230
+ metadata: evidenceTexts.length > 0 ? { evidenceCount: evidenceTexts.length } : void 0,
3151
3231
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
3152
3232
  };
3153
3233
  }
@@ -3163,27 +3243,46 @@ var ReportService = class {
3163
3243
  }
3164
3244
  store;
3165
3245
  log;
3246
+ /** Rejected reports from the last collection (§13 — malformed never enters aggregation). */
3247
+ lastDiagnostics = { collected: 0, accepted: 0, rejected: [] };
3166
3248
  get storeRef() {
3167
3249
  return this.store;
3168
3250
  }
3169
- /** Normalizes finished tasks into reports (§17 — normalizer). */
3251
+ /** Normalizes finished tasks into reports (§17 — normalizer), validating each
3252
+ * one so malformed reports never reach aggregation (§13). */
3170
3253
  async collectFromTasks(tasks) {
3171
- const reports = [];
3254
+ const candidates = [];
3172
3255
  for (const task of tasks) {
3173
3256
  const structured = task.result?.reports ?? [];
3174
3257
  if (structured.length > 0) {
3175
- reports.push(...structured);
3258
+ candidates.push(...structured);
3176
3259
  continue;
3177
3260
  }
3178
3261
  const text = task.result?.rawOutput ?? task.result?.summary ?? "";
3179
3262
  if (text.trim().length === 0) continue;
3180
- reports.push(
3263
+ candidates.push(
3181
3264
  extractReportFromText(text, {
3182
3265
  taskId: task.id,
3183
3266
  agent: task.agent ?? "unknown"
3184
3267
  })
3185
3268
  );
3186
3269
  }
3270
+ const reports = [];
3271
+ const rejected = [];
3272
+ for (const candidate of candidates) {
3273
+ const result = validateReport(candidate);
3274
+ if (result.valid) {
3275
+ reports.push(result.report);
3276
+ } else {
3277
+ rejected.push({
3278
+ key: candidate?.id ?? "(unknown)",
3279
+ reason: result.reason,
3280
+ issues: result.issues.map((issue) => `${issue.path}: ${issue.message}`)
3281
+ });
3282
+ this.log.warn(`Rejected malformed report: ${result.reason}`);
3283
+ }
3284
+ }
3285
+ this.lastDiagnostics = { collected: candidates.length, accepted: reports.length, rejected };
3187
3286
  return reports;
3188
3287
  }
3189
3288
  async saveReports(reports) {
@@ -3403,7 +3502,7 @@ function truncate5(text, max) {
3403
3502
 
3404
3503
  // src/planner/model.ts
3405
3504
  import { randomBytes as randomBytes4 } from "crypto";
3406
- import { z as z2 } from "zod";
3505
+ import { z as z3 } from "zod";
3407
3506
 
3408
3507
  // src/planner/context.ts
3409
3508
  function buildPlannerContext(request) {
@@ -3456,21 +3555,21 @@ ${request.context}`);
3456
3555
  }
3457
3556
 
3458
3557
  // src/planner/model.ts
3459
- var planStepJsonSchema = z2.object({
3460
- id: z2.string().optional(),
3461
- title: z2.string().min(1),
3462
- description: z2.string().optional(),
3463
- order: z2.number().int().optional(),
3464
- targets: z2.array(z2.string()).optional(),
3465
- acceptance_criteria: z2.array(z2.string()).optional(),
3466
- depends_on: z2.array(z2.string()).optional(),
3467
- source_findings: z2.array(z2.string()).optional()
3558
+ var planStepJsonSchema = z3.object({
3559
+ id: z3.string().optional(),
3560
+ title: z3.string().min(1),
3561
+ description: z3.string().optional(),
3562
+ order: z3.number().int().optional(),
3563
+ targets: z3.array(z3.string()).optional(),
3564
+ acceptance_criteria: z3.array(z3.string()).optional(),
3565
+ depends_on: z3.array(z3.string()).optional(),
3566
+ source_findings: z3.array(z3.string()).optional()
3468
3567
  }).strict();
3469
- var planJsonSchema = z2.object({
3470
- summary: z2.string().min(1),
3471
- steps: z2.array(planStepJsonSchema).min(1),
3472
- risks: z2.array(z2.string()).optional(),
3473
- notes: z2.array(z2.string()).optional()
3568
+ var planJsonSchema = z3.object({
3569
+ summary: z3.string().min(1),
3570
+ steps: z3.array(planStepJsonSchema).min(1),
3571
+ risks: z3.array(z3.string()).optional(),
3572
+ notes: z3.array(z3.string()).optional()
3474
3573
  }).strict();
3475
3574
  var ModelPlanner = class {
3476
3575
  constructor(options) {