@hilbras/omninode 2.0.0-alpha.7 → 2.0.0-alpha.8
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 +9 -3
- package/dist/cli/index.js +146 -39
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +122 -31
- package/dist/index.js +149 -39
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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.
|
|
21
|
+
**v2.0.0-alpha.8 — v2 Phase 8: Remembera Integration v2**
|
|
22
22
|
(the reliability & interoperability line: architecture → execution →
|
|
23
|
-
protocol → adapters → pipelines → providers → OmniHilbras) (the v2
|
|
23
|
+
protocol → adapters → pipelines → providers → OmniHilbras → memory) (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,12 @@ 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 8 — Remembera / Memory v2**: the memory contract is formalized
|
|
35
|
+
(`retrieve` / `store` / `search` / `metadata` with v1 `query` / `write`
|
|
36
|
+
aliases kept working), memory gains structured **categories** (decisions,
|
|
37
|
+
problems, conventions, tasks…), retrieval is **task-oriented and budgeted**
|
|
38
|
+
(never "load all memory"), and `memory.required: true` makes backend
|
|
39
|
+
failures fatal instead of tolerated. Details: [MEMORY.md](docs/MEMORY.md).
|
|
34
40
|
- **Phase 7 — OmniHilbras Integration v2**: the dedicated adapter now
|
|
35
41
|
captures gateway-level metadata (version, tier, region) and attaches it
|
|
36
42
|
to discovered models, honors provider-level `timeout_ms`, and supports
|
|
@@ -333,7 +339,7 @@ required for the core engine.
|
|
|
333
339
|
- [Architecture](docs/ARCHITECTURE.md) — module map, workflow, local state
|
|
334
340
|
- [Security model](docs/SECURITY.md) — secrets, process execution, env isolation, audit log
|
|
335
341
|
- [API stability policy](docs/API_STABILITY.md) and [public API inventory](docs/API.md)
|
|
336
|
-
- [Agent Protocol v2 spec](docs/PROTOCOL.md) · [Providers](docs/PROVIDERS.md) · [Deprecations](docs/DEPRECATIONS.md) · [Changelog](CHANGELOG.md) · [v2 roadmap](docs/ROADMAP_V2.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)
|
|
337
343
|
- [Example project](examples/demo/) — a runnable end-to-end demo (`./demo.sh`)
|
|
338
344
|
|
|
339
345
|
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.
|
|
9
|
+
var OMNINODE_VERSION = "2.0.0-alpha.8";
|
|
10
10
|
|
|
11
11
|
// src/errors/index.ts
|
|
12
12
|
var OmniNodeError = class extends Error {
|
|
@@ -134,7 +134,8 @@ var projectConfigSchema = z.object({
|
|
|
134
134
|
provider: z.string().min(1).default("local"),
|
|
135
135
|
base_url: z.string().url().optional(),
|
|
136
136
|
api_key_env_var: z.string().min(1).optional(),
|
|
137
|
-
|
|
137
|
+
/** When true, memory failures fail tasks instead of being tolerated (§12). */
|
|
138
|
+
required: z.boolean().optional()
|
|
138
139
|
}).strict().optional()
|
|
139
140
|
}).strict();
|
|
140
141
|
var appConfigSchema = z.object({
|
|
@@ -216,7 +217,8 @@ function toAppConfig(yaml) {
|
|
|
216
217
|
memory: {
|
|
217
218
|
provider: yaml.project.memory.provider,
|
|
218
219
|
baseUrl: yaml.project.memory.base_url,
|
|
219
|
-
apiKeyEnvVar: yaml.project.memory.api_key_env_var
|
|
220
|
+
apiKeyEnvVar: yaml.project.memory.api_key_env_var,
|
|
221
|
+
required: yaml.project.memory.required
|
|
220
222
|
}
|
|
221
223
|
} : {}
|
|
222
224
|
},
|
|
@@ -2490,6 +2492,23 @@ var FileAuditLog = class {
|
|
|
2490
2492
|
}
|
|
2491
2493
|
};
|
|
2492
2494
|
|
|
2495
|
+
// src/memory/base.ts
|
|
2496
|
+
function defaultSearch(provider, text, options = {}) {
|
|
2497
|
+
return provider.retrieve({
|
|
2498
|
+
text,
|
|
2499
|
+
...options.scope !== void 0 ? { scope: options.scope } : {},
|
|
2500
|
+
...options.limit !== void 0 ? { limit: options.limit } : {}
|
|
2501
|
+
});
|
|
2502
|
+
}
|
|
2503
|
+
function defaultMetadata(name, options = {}) {
|
|
2504
|
+
return {
|
|
2505
|
+
name,
|
|
2506
|
+
capabilities: options.capabilities ?? ["retrieve", "store", "search"],
|
|
2507
|
+
relevanceRanking: options.relevanceRanking ?? false,
|
|
2508
|
+
...options.backend !== void 0 ? { backend: options.backend } : {}
|
|
2509
|
+
};
|
|
2510
|
+
}
|
|
2511
|
+
|
|
2493
2512
|
// src/memory/local.ts
|
|
2494
2513
|
var STOPWORDS = /* @__PURE__ */ new Set([
|
|
2495
2514
|
"the",
|
|
@@ -2552,9 +2571,27 @@ var LocalMemoryProvider = class extends JsonFileStore {
|
|
|
2552
2571
|
constructor(directory) {
|
|
2553
2572
|
super({ ...directory !== void 0 ? { directory } : {}, fileName: "memory.json" });
|
|
2554
2573
|
}
|
|
2555
|
-
|
|
2574
|
+
metadata() {
|
|
2575
|
+
return defaultMetadata(this.name, {
|
|
2576
|
+
capabilities: ["retrieve", "store", "search", "categories", "tags"],
|
|
2577
|
+
relevanceRanking: true,
|
|
2578
|
+
backend: { kind: "json-file", deterministicScoring: true }
|
|
2579
|
+
});
|
|
2580
|
+
}
|
|
2581
|
+
search(text, options = {}) {
|
|
2582
|
+
return defaultSearch(this, text, options);
|
|
2583
|
+
}
|
|
2584
|
+
/** @deprecated v1 alias of {@link retrieve}. */
|
|
2585
|
+
query(query) {
|
|
2586
|
+
return this.retrieve(query);
|
|
2587
|
+
}
|
|
2588
|
+
/** @deprecated v1 alias of {@link store}. */
|
|
2589
|
+
write(entry2) {
|
|
2590
|
+
return this.store(entry2);
|
|
2591
|
+
}
|
|
2592
|
+
async retrieve(query) {
|
|
2556
2593
|
const entries = await this.readAll();
|
|
2557
|
-
const filtered = entries.filter((entry2) => query.scope ? entry2.scope === query.scope : true).filter(
|
|
2594
|
+
const filtered = entries.filter((entry2) => query.scope ? entry2.scope === query.scope : true).filter((entry2) => query.category ? entry2.category === query.category : true).filter(
|
|
2558
2595
|
(entry2) => query.tags && query.tags.length > 0 ? query.tags.every((tag) => (entry2.tags ?? []).includes(tag)) : true
|
|
2559
2596
|
);
|
|
2560
2597
|
if (!query.text || query.text.trim().length === 0) {
|
|
@@ -2563,7 +2600,7 @@ var LocalMemoryProvider = class extends JsonFileStore {
|
|
|
2563
2600
|
const queryKeywords = keywords(query.text);
|
|
2564
2601
|
return filtered.map((entry2) => ({ entry: entry2, score: relevanceScore(queryKeywords, entry2) })).filter(({ score }) => score > 0).sort((a, b) => b.score - a.score).slice(0, query.limit ?? 10).map(({ entry: entry2 }) => entry2);
|
|
2565
2602
|
}
|
|
2566
|
-
|
|
2603
|
+
store(entry2) {
|
|
2567
2604
|
return this.mutate((entries) => {
|
|
2568
2605
|
const index = entries.findIndex((e) => e.key === entry2.key);
|
|
2569
2606
|
const stamped = {
|
|
@@ -2594,11 +2631,40 @@ var RememberaMemoryProvider = class {
|
|
|
2594
2631
|
this.env = config.env ?? process.env;
|
|
2595
2632
|
this.log = log.child({ component: "remembera" });
|
|
2596
2633
|
}
|
|
2634
|
+
get authenticationConfigured() {
|
|
2635
|
+
return this.apiKeyEnvVar === void 0 || Boolean(this.env[this.apiKeyEnvVar]);
|
|
2636
|
+
}
|
|
2597
2637
|
headers() {
|
|
2598
2638
|
const key = this.apiKeyEnvVar ? resolveApiKey({ name: "remembera", apiKeyEnvVar: this.apiKeyEnvVar }, this.env) : void 0;
|
|
2599
2639
|
return key ? { authorization: `Bearer ${key}` } : {};
|
|
2600
2640
|
}
|
|
2601
|
-
|
|
2641
|
+
/** Search delegates to the gateway's relevance query. */
|
|
2642
|
+
search(text, options = {}) {
|
|
2643
|
+
return defaultSearch(
|
|
2644
|
+
this,
|
|
2645
|
+
text,
|
|
2646
|
+
{
|
|
2647
|
+
...options.scope !== void 0 ? { scope: options.scope } : {},
|
|
2648
|
+
...options.limit !== void 0 ? { limit: options.limit } : {}
|
|
2649
|
+
}
|
|
2650
|
+
);
|
|
2651
|
+
}
|
|
2652
|
+
metadata() {
|
|
2653
|
+
return defaultMetadata(this.name, {
|
|
2654
|
+
capabilities: ["retrieve", "store", "search", "categories", "tags"],
|
|
2655
|
+
relevanceRanking: true,
|
|
2656
|
+
backend: { kind: "http", baseUrl: this.baseUrl, authenticated: this.authenticationConfigured }
|
|
2657
|
+
});
|
|
2658
|
+
}
|
|
2659
|
+
/** @deprecated v1 alias of {@link retrieve}. */
|
|
2660
|
+
query(query) {
|
|
2661
|
+
return this.retrieve(query);
|
|
2662
|
+
}
|
|
2663
|
+
/** @deprecated v1 alias of {@link store}. */
|
|
2664
|
+
write(entry2) {
|
|
2665
|
+
return this.store(entry2);
|
|
2666
|
+
}
|
|
2667
|
+
async retrieve(query) {
|
|
2602
2668
|
const { status, body } = await this.request(`${this.baseUrl}/api/memory/query`, query);
|
|
2603
2669
|
if (status !== 200) {
|
|
2604
2670
|
throw new MemoryError(`Remembera query returned HTTP ${status}.`);
|
|
@@ -2609,7 +2675,7 @@ var RememberaMemoryProvider = class {
|
|
|
2609
2675
|
}
|
|
2610
2676
|
return entries.map((entry2) => this.toEntry(entry2)).filter((entry2) => entry2 !== void 0);
|
|
2611
2677
|
}
|
|
2612
|
-
async
|
|
2678
|
+
async store(entry2) {
|
|
2613
2679
|
const { status } = await this.request(`${this.baseUrl}/api/memory`, entry2);
|
|
2614
2680
|
if (status !== 200 && status !== 201) {
|
|
2615
2681
|
throw new MemoryError(`Remembera write returned HTTP ${status}.`);
|
|
@@ -2644,6 +2710,7 @@ var RememberaMemoryProvider = class {
|
|
|
2644
2710
|
content: entry2.content,
|
|
2645
2711
|
scope: scope === "project" || scope === "role" || scope === "task" || scope === "knowledge" ? scope : "knowledge",
|
|
2646
2712
|
...Array.isArray(entry2.tags) ? { tags: entry2.tags.filter((t) => typeof t === "string") } : {},
|
|
2713
|
+
...typeof entry2.category === "string" ? { category: entry2.category } : {},
|
|
2647
2714
|
...typeof entry2.createdAt === "string" ? { createdAt: entry2.createdAt } : {},
|
|
2648
2715
|
...typeof entry2.updatedAt === "string" ? { updatedAt: entry2.updatedAt } : {}
|
|
2649
2716
|
};
|
|
@@ -2674,57 +2741,54 @@ function createMemoryProvider(config) {
|
|
|
2674
2741
|
}
|
|
2675
2742
|
|
|
2676
2743
|
// src/memory/service.ts
|
|
2744
|
+
var DEFAULT_ENTRY_LIMIT = 8;
|
|
2677
2745
|
var MAX_CONTEXT_CHARS = 4e3;
|
|
2678
2746
|
var MemoryService = class {
|
|
2679
|
-
constructor(provider, log = logger) {
|
|
2747
|
+
constructor(provider, log = logger, options = {}) {
|
|
2680
2748
|
this.provider = provider;
|
|
2681
2749
|
this.log = log.child({ component: "memory", provider: provider.name });
|
|
2750
|
+
this.required = options.required ?? false;
|
|
2751
|
+
this.maxContextChars = options.maxContextChars ?? MAX_CONTEXT_CHARS;
|
|
2682
2752
|
}
|
|
2683
2753
|
provider;
|
|
2684
2754
|
log;
|
|
2755
|
+
required;
|
|
2756
|
+
maxContextChars;
|
|
2685
2757
|
get providerRef() {
|
|
2686
2758
|
return this.provider;
|
|
2687
2759
|
}
|
|
2688
2760
|
get providerName() {
|
|
2689
2761
|
return this.provider.name;
|
|
2690
2762
|
}
|
|
2691
|
-
|
|
2692
|
-
|
|
2763
|
+
get isRequired() {
|
|
2764
|
+
return this.required;
|
|
2765
|
+
}
|
|
2766
|
+
/** Structured, budgeted context for a task (§12 — avoid loading all memory). */
|
|
2767
|
+
async contextFor(query) {
|
|
2768
|
+
let entries;
|
|
2693
2769
|
try {
|
|
2694
|
-
|
|
2770
|
+
entries = await this.provider.retrieve({
|
|
2695
2771
|
text: query.objective,
|
|
2696
2772
|
...query.role ? { tags: [query.role] } : {},
|
|
2697
|
-
limit:
|
|
2773
|
+
limit: query.limit ?? DEFAULT_ENTRY_LIMIT
|
|
2698
2774
|
});
|
|
2699
|
-
if (entries.length === 0) return "";
|
|
2700
|
-
const byScope = /* @__PURE__ */ new Map();
|
|
2701
|
-
for (const entry2 of entries) {
|
|
2702
|
-
byScope.set(entry2.scope, [...byScope.get(entry2.scope) ?? [], entry2]);
|
|
2703
|
-
}
|
|
2704
|
-
const parts = [];
|
|
2705
|
-
for (const scope of ["project", "task", "role", "knowledge"]) {
|
|
2706
|
-
const scopeEntries = byScope.get(scope) ?? [];
|
|
2707
|
-
if (scopeEntries.length === 0) continue;
|
|
2708
|
-
const lines = scopeEntries.map((entry2) => `- ${entry2.content}`).join("\n");
|
|
2709
|
-
parts.push(`### Relevant memory (${scope})
|
|
2710
|
-
${lines}`);
|
|
2711
|
-
}
|
|
2712
|
-
const context = parts.join("\n\n");
|
|
2713
|
-
return context.length > MAX_CONTEXT_CHARS ? `${context.slice(0, MAX_CONTEXT_CHARS)}\u2026` : context;
|
|
2714
2775
|
} catch (error) {
|
|
2715
|
-
this.
|
|
2716
|
-
`Memory query failed (continuing without memory): ${error instanceof Error ? error.message : String(error)}`
|
|
2717
|
-
);
|
|
2718
|
-
return "";
|
|
2776
|
+
return this.handleFailure("retrieve", error, { text: "", entries: [], truncated: false });
|
|
2719
2777
|
}
|
|
2778
|
+
return this.format(entries);
|
|
2779
|
+
}
|
|
2780
|
+
/** Prompt-ready context text (v1 convenience wrapper). */
|
|
2781
|
+
async gatherContext(query) {
|
|
2782
|
+
return (await this.contextFor(query)).text;
|
|
2720
2783
|
}
|
|
2721
|
-
/** Records a finished task's outcome —
|
|
2784
|
+
/** Records a finished task's outcome — task history plus "known problems" (§12). */
|
|
2722
2785
|
async recordTaskOutcome(task) {
|
|
2723
2786
|
const outcome = task.result?.summary ?? task.result?.error ?? "(no output)";
|
|
2724
2787
|
try {
|
|
2725
|
-
await this.provider.
|
|
2788
|
+
await this.provider.store({
|
|
2726
2789
|
key: `task:${task.id}`,
|
|
2727
2790
|
scope: "task",
|
|
2791
|
+
category: "tasks",
|
|
2728
2792
|
content: `Objective: ${task.objective}
|
|
2729
2793
|
Status: ${task.status}
|
|
2730
2794
|
Outcome: ${outcome}`,
|
|
@@ -2735,18 +2799,53 @@ Outcome: ${outcome}`,
|
|
|
2735
2799
|
(report) => report.findings.filter((finding) => finding.severity === "high" || finding.severity === "critical").map((finding) => ({ report, finding }))
|
|
2736
2800
|
);
|
|
2737
2801
|
for (const { report, finding } of significant) {
|
|
2738
|
-
await this.provider.
|
|
2802
|
+
await this.provider.store({
|
|
2739
2803
|
key: `finding:${report.id}:${finding.id}`,
|
|
2740
2804
|
scope: "project",
|
|
2805
|
+
category: "problems",
|
|
2741
2806
|
content: `[${finding.severity}] ${finding.title} \u2014 ${finding.detail}`,
|
|
2742
2807
|
tags: ["known-problem", report.agent]
|
|
2743
2808
|
});
|
|
2744
2809
|
}
|
|
2745
2810
|
} catch (error) {
|
|
2746
|
-
this.
|
|
2747
|
-
|
|
2811
|
+
this.handleFailure("store", error, void 0);
|
|
2812
|
+
}
|
|
2813
|
+
}
|
|
2814
|
+
format(entries) {
|
|
2815
|
+
if (entries.length === 0) return { text: "", entries, truncated: false };
|
|
2816
|
+
const byScope = /* @__PURE__ */ new Map();
|
|
2817
|
+
for (const entry2 of entries) {
|
|
2818
|
+
byScope.set(entry2.scope, [...byScope.get(entry2.scope) ?? [], entry2]);
|
|
2819
|
+
}
|
|
2820
|
+
const parts = [];
|
|
2821
|
+
for (const scope of ["project", "task", "role", "knowledge"]) {
|
|
2822
|
+
const scopeEntries = byScope.get(scope) ?? [];
|
|
2823
|
+
if (scopeEntries.length === 0) continue;
|
|
2824
|
+
const lines = scopeEntries.map((entry2) => {
|
|
2825
|
+
const category = entry2.category ? `[${entry2.category}] ` : "";
|
|
2826
|
+
return `- ${category}${entry2.content}`;
|
|
2827
|
+
}).join("\n");
|
|
2828
|
+
parts.push(`### Relevant memory (${scope})
|
|
2829
|
+
${lines}`);
|
|
2830
|
+
}
|
|
2831
|
+
let text = parts.join("\n\n");
|
|
2832
|
+
const truncated = text.length > this.maxContextChars;
|
|
2833
|
+
if (truncated) text = `${text.slice(0, this.maxContextChars - 1)}\u2026`;
|
|
2834
|
+
return { text, entries, truncated };
|
|
2835
|
+
}
|
|
2836
|
+
/**
|
|
2837
|
+
* Failure policy (§12): continue unless memory is explicitly required.
|
|
2838
|
+
*/
|
|
2839
|
+
handleFailure(operation, error, fallback) {
|
|
2840
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2841
|
+
if (this.required) {
|
|
2842
|
+
throw new MemoryError(
|
|
2843
|
+
`Memory is marked required, but ${operation} failed: ${message}`,
|
|
2844
|
+
{ cause: error }
|
|
2748
2845
|
);
|
|
2749
2846
|
}
|
|
2847
|
+
this.log.warn(`Memory ${operation} failed (continuing without memory): ${message}`);
|
|
2848
|
+
return fallback ?? { text: "", entries: [], truncated: false };
|
|
2750
2849
|
}
|
|
2751
2850
|
};
|
|
2752
2851
|
|
|
@@ -2780,7 +2879,11 @@ function createTaskEngine(config, log = logger, directory) {
|
|
|
2780
2879
|
agents,
|
|
2781
2880
|
roles,
|
|
2782
2881
|
store: new FileTaskStore(directory),
|
|
2783
|
-
...config.project.memory !== void 0 ? {
|
|
2882
|
+
...config.project.memory !== void 0 ? {
|
|
2883
|
+
memory: new MemoryService(createMemoryProvider(config.project.memory), log, {
|
|
2884
|
+
required: config.project.memory.required
|
|
2885
|
+
})
|
|
2886
|
+
} : {},
|
|
2784
2887
|
audit: new FileAuditLog(),
|
|
2785
2888
|
log
|
|
2786
2889
|
});
|
|
@@ -4056,7 +4159,11 @@ function buildPipelineEngine(config, log = logger) {
|
|
|
4056
4159
|
agents,
|
|
4057
4160
|
roles,
|
|
4058
4161
|
store: new FileTaskStore(),
|
|
4059
|
-
...config.project.memory !== void 0 ? {
|
|
4162
|
+
...config.project.memory !== void 0 ? {
|
|
4163
|
+
memory: new MemoryService(createMemoryProvider(config.project.memory), log, {
|
|
4164
|
+
required: config.project.memory.required
|
|
4165
|
+
})
|
|
4166
|
+
} : {},
|
|
4060
4167
|
audit: new FileAuditLog(),
|
|
4061
4168
|
log
|
|
4062
4169
|
});
|