@soleri/core 7.0.0 → 8.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/dist/agency/agency-manager.d.ts +27 -1
- package/dist/agency/agency-manager.d.ts.map +1 -1
- package/dist/agency/agency-manager.js +180 -9
- package/dist/agency/agency-manager.js.map +1 -1
- package/dist/agency/default-rules.d.ts +7 -0
- package/dist/agency/default-rules.d.ts.map +1 -0
- package/dist/agency/default-rules.js +79 -0
- package/dist/agency/default-rules.js.map +1 -0
- package/dist/agency/types.d.ts +48 -0
- package/dist/agency/types.d.ts.map +1 -1
- package/dist/brain/brain.d.ts +17 -2
- package/dist/brain/brain.d.ts.map +1 -1
- package/dist/brain/brain.js +118 -8
- package/dist/brain/brain.js.map +1 -1
- package/dist/brain/knowledge-synthesizer.d.ts +37 -0
- package/dist/brain/knowledge-synthesizer.d.ts.map +1 -0
- package/dist/brain/knowledge-synthesizer.js +159 -0
- package/dist/brain/knowledge-synthesizer.js.map +1 -0
- package/dist/brain/learning-radar.d.ts +96 -0
- package/dist/brain/learning-radar.d.ts.map +1 -0
- package/dist/brain/learning-radar.js +202 -0
- package/dist/brain/learning-radar.js.map +1 -0
- package/dist/brain/types.d.ts +15 -0
- package/dist/brain/types.d.ts.map +1 -1
- package/dist/context/context-engine.d.ts.map +1 -1
- package/dist/context/context-engine.js +82 -17
- package/dist/context/context-engine.js.map +1 -1
- package/dist/context/types.d.ts +5 -0
- package/dist/context/types.d.ts.map +1 -1
- package/dist/control/intent-router.d.ts +12 -1
- package/dist/control/intent-router.d.ts.map +1 -1
- package/dist/control/intent-router.js +68 -0
- package/dist/control/intent-router.js.map +1 -1
- package/dist/control/types.d.ts +17 -0
- package/dist/control/types.d.ts.map +1 -1
- package/dist/curator/classifier.d.ts +18 -0
- package/dist/curator/classifier.d.ts.map +1 -0
- package/dist/curator/classifier.js +59 -0
- package/dist/curator/classifier.js.map +1 -0
- package/dist/curator/quality-gate.d.ts +29 -0
- package/dist/curator/quality-gate.d.ts.map +1 -0
- package/dist/curator/quality-gate.js +86 -0
- package/dist/curator/quality-gate.js.map +1 -0
- package/dist/domain-packs/index.d.ts +0 -3
- package/dist/domain-packs/index.d.ts.map +1 -1
- package/dist/domain-packs/index.js +0 -3
- package/dist/domain-packs/index.js.map +1 -1
- package/dist/domain-packs/loader.d.ts.map +1 -1
- package/dist/domain-packs/loader.js +20 -4
- package/dist/domain-packs/loader.js.map +1 -1
- package/dist/domain-packs/pack-runtime.d.ts +5 -5
- package/dist/domain-packs/pack-runtime.d.ts.map +1 -1
- package/dist/domain-packs/pack-runtime.js +2 -2
- package/dist/domain-packs/pack-runtime.js.map +1 -1
- package/dist/domain-packs/types.d.ts +8 -2
- package/dist/domain-packs/types.d.ts.map +1 -1
- package/dist/domain-packs/types.js.map +1 -1
- package/dist/engine/bin/soleri-engine.js +13 -2
- package/dist/engine/bin/soleri-engine.js.map +1 -1
- package/dist/engine/index.d.ts +2 -0
- package/dist/engine/index.d.ts.map +1 -1
- package/dist/engine/index.js +1 -0
- package/dist/engine/index.js.map +1 -1
- package/dist/engine/module-manifest.d.ts +28 -0
- package/dist/engine/module-manifest.d.ts.map +1 -0
- package/dist/engine/module-manifest.js +85 -0
- package/dist/engine/module-manifest.js.map +1 -0
- package/dist/engine/register-engine.d.ts +19 -0
- package/dist/engine/register-engine.d.ts.map +1 -1
- package/dist/engine/register-engine.js +15 -2
- package/dist/engine/register-engine.js.map +1 -1
- package/dist/events/event-bus.d.ts +30 -0
- package/dist/events/event-bus.d.ts.map +1 -0
- package/dist/events/event-bus.js +51 -0
- package/dist/events/event-bus.js.map +1 -0
- package/dist/flows/chain-runner.d.ts +46 -0
- package/dist/flows/chain-runner.d.ts.map +1 -0
- package/dist/flows/chain-runner.js +271 -0
- package/dist/flows/chain-runner.js.map +1 -0
- package/dist/flows/chain-types.d.ts +103 -0
- package/dist/flows/chain-types.d.ts.map +1 -0
- package/dist/flows/chain-types.js +23 -0
- package/dist/flows/chain-types.js.map +1 -0
- package/dist/health/doctor-checks.d.ts +15 -0
- package/dist/health/doctor-checks.d.ts.map +1 -0
- package/dist/health/doctor-checks.js +98 -0
- package/dist/health/doctor-checks.js.map +1 -0
- package/dist/index.d.ts +0 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -1
- package/dist/index.js.map +1 -1
- package/dist/intake/content-classifier.d.ts.map +1 -1
- package/dist/intake/content-classifier.js +0 -2
- package/dist/intake/content-classifier.js.map +1 -1
- package/dist/intake/text-ingester.d.ts +52 -0
- package/dist/intake/text-ingester.d.ts.map +1 -0
- package/dist/intake/text-ingester.js +181 -0
- package/dist/intake/text-ingester.js.map +1 -0
- package/dist/llm/llm-client.d.ts.map +1 -1
- package/dist/llm/llm-client.js +45 -5
- package/dist/llm/llm-client.js.map +1 -1
- package/dist/llm/oauth-discovery.d.ts +18 -0
- package/dist/llm/oauth-discovery.d.ts.map +1 -0
- package/dist/llm/oauth-discovery.js +130 -0
- package/dist/llm/oauth-discovery.js.map +1 -0
- package/dist/llm/types.d.ts +4 -2
- package/dist/llm/types.d.ts.map +1 -1
- package/dist/packs/pack-installer.d.ts +2 -1
- package/dist/packs/pack-installer.d.ts.map +1 -1
- package/dist/packs/pack-installer.js +10 -1
- package/dist/packs/pack-installer.js.map +1 -1
- package/dist/persistence/index.d.ts +0 -1
- package/dist/persistence/index.d.ts.map +1 -1
- package/dist/persistence/index.js +0 -1
- package/dist/persistence/index.js.map +1 -1
- package/dist/persistence/types.d.ts +2 -6
- package/dist/persistence/types.d.ts.map +1 -1
- package/dist/planning/evidence-collector.d.ts +41 -0
- package/dist/planning/evidence-collector.d.ts.map +1 -0
- package/dist/planning/evidence-collector.js +194 -0
- package/dist/planning/evidence-collector.js.map +1 -0
- package/dist/planning/planner.d.ts +4 -0
- package/dist/planning/planner.d.ts.map +1 -1
- package/dist/planning/planner.js +11 -0
- package/dist/planning/planner.js.map +1 -1
- package/dist/plugins/index.d.ts +4 -0
- package/dist/plugins/index.d.ts.map +1 -1
- package/dist/plugins/index.js +4 -0
- package/dist/plugins/index.js.map +1 -1
- package/dist/plugins/plugin-registry.d.ts +4 -0
- package/dist/plugins/plugin-registry.d.ts.map +1 -1
- package/dist/plugins/plugin-registry.js +4 -0
- package/dist/plugins/plugin-registry.js.map +1 -1
- package/dist/plugins/types.d.ts +32 -27
- package/dist/plugins/types.d.ts.map +1 -1
- package/dist/plugins/types.js +6 -3
- package/dist/plugins/types.js.map +1 -1
- package/dist/queue/job-queue.d.ts +92 -0
- package/dist/queue/job-queue.d.ts.map +1 -0
- package/dist/queue/job-queue.js +180 -0
- package/dist/queue/job-queue.js.map +1 -0
- package/dist/queue/pipeline-runner.d.ts +62 -0
- package/dist/queue/pipeline-runner.d.ts.map +1 -0
- package/dist/queue/pipeline-runner.js +126 -0
- package/dist/queue/pipeline-runner.js.map +1 -0
- package/dist/runtime/admin-setup-ops.d.ts +20 -0
- package/dist/runtime/admin-setup-ops.d.ts.map +1 -0
- package/dist/runtime/admin-setup-ops.js +583 -0
- package/dist/runtime/admin-setup-ops.js.map +1 -0
- package/dist/runtime/chain-ops.d.ts +9 -0
- package/dist/runtime/chain-ops.d.ts.map +1 -0
- package/dist/runtime/chain-ops.js +107 -0
- package/dist/runtime/chain-ops.js.map +1 -0
- package/dist/runtime/claude-md-helpers.d.ts +56 -0
- package/dist/runtime/claude-md-helpers.d.ts.map +1 -0
- package/dist/runtime/claude-md-helpers.js +160 -0
- package/dist/runtime/claude-md-helpers.js.map +1 -0
- package/dist/runtime/curator-extra-ops.d.ts +3 -2
- package/dist/runtime/curator-extra-ops.d.ts.map +1 -1
- package/dist/runtime/curator-extra-ops.js +81 -3
- package/dist/runtime/curator-extra-ops.js.map +1 -1
- package/dist/runtime/facades/admin-facade.d.ts.map +1 -1
- package/dist/runtime/facades/admin-facade.js +5 -2
- package/dist/runtime/facades/admin-facade.js.map +1 -1
- package/dist/runtime/facades/agency-facade.d.ts.map +1 -1
- package/dist/runtime/facades/agency-facade.js +64 -0
- package/dist/runtime/facades/agency-facade.js.map +1 -1
- package/dist/runtime/facades/brain-facade.d.ts.map +1 -1
- package/dist/runtime/facades/brain-facade.js +122 -1
- package/dist/runtime/facades/brain-facade.js.map +1 -1
- package/dist/runtime/facades/control-facade.d.ts.map +1 -1
- package/dist/runtime/facades/control-facade.js +42 -0
- package/dist/runtime/facades/control-facade.js.map +1 -1
- package/dist/runtime/facades/memory-facade.d.ts.map +1 -1
- package/dist/runtime/facades/memory-facade.js +20 -2
- package/dist/runtime/facades/memory-facade.js.map +1 -1
- package/dist/runtime/facades/plan-facade.d.ts.map +1 -1
- package/dist/runtime/facades/plan-facade.js +2 -0
- package/dist/runtime/facades/plan-facade.js.map +1 -1
- package/dist/runtime/facades/vault-facade.d.ts.map +1 -1
- package/dist/runtime/facades/vault-facade.js +25 -5
- package/dist/runtime/facades/vault-facade.js.map +1 -1
- package/dist/runtime/intake-ops.d.ts +7 -5
- package/dist/runtime/intake-ops.d.ts.map +1 -1
- package/dist/runtime/intake-ops.js +98 -5
- package/dist/runtime/intake-ops.js.map +1 -1
- package/dist/runtime/memory-extra-ops.d.ts +6 -3
- package/dist/runtime/memory-extra-ops.d.ts.map +1 -1
- package/dist/runtime/memory-extra-ops.js +292 -4
- package/dist/runtime/memory-extra-ops.js.map +1 -1
- package/dist/runtime/pack-ops.d.ts +3 -0
- package/dist/runtime/pack-ops.d.ts.map +1 -1
- package/dist/runtime/pack-ops.js +18 -1
- package/dist/runtime/pack-ops.js.map +1 -1
- package/dist/runtime/planning-extra-ops.d.ts.map +1 -1
- package/dist/runtime/planning-extra-ops.js +85 -0
- package/dist/runtime/planning-extra-ops.js.map +1 -1
- package/dist/runtime/playbook-ops.js +1 -1
- package/dist/runtime/playbook-ops.js.map +1 -1
- package/dist/runtime/plugin-ops.d.ts.map +1 -1
- package/dist/runtime/plugin-ops.js +3 -0
- package/dist/runtime/plugin-ops.js.map +1 -1
- package/dist/runtime/runtime.d.ts.map +1 -1
- package/dist/runtime/runtime.js +143 -2
- package/dist/runtime/runtime.js.map +1 -1
- package/dist/runtime/session-briefing.d.ts +23 -0
- package/dist/runtime/session-briefing.d.ts.map +1 -0
- package/dist/runtime/session-briefing.js +154 -0
- package/dist/runtime/session-briefing.js.map +1 -0
- package/dist/runtime/types.d.ts +23 -0
- package/dist/runtime/types.d.ts.map +1 -1
- package/dist/runtime/vault-linking-ops.d.ts.map +1 -1
- package/dist/runtime/vault-linking-ops.js +3 -7
- package/dist/runtime/vault-linking-ops.js.map +1 -1
- package/dist/vault/vault.d.ts +34 -0
- package/dist/vault/vault.d.ts.map +1 -1
- package/dist/vault/vault.js +89 -3
- package/dist/vault/vault.js.map +1 -1
- package/package.json +6 -4
- package/src/__tests__/admin-setup-ops.test.ts +355 -0
- package/src/__tests__/async-infrastructure.test.ts +307 -0
- package/src/__tests__/cognee-client-gaps.test.ts +6 -2
- package/src/__tests__/cognee-hybrid-search.test.ts +49 -35
- package/src/__tests__/cognee-sync-manager-deep.test.ts +89 -65
- package/src/__tests__/curator-extra-ops.test.ts +6 -2
- package/src/__tests__/curator-pipeline-e2e.test.ts +545 -0
- package/src/__tests__/memory-extra-ops.test.ts +2 -2
- package/src/__tests__/module-manifest-drift.test.ts +59 -0
- package/src/__tests__/planning-extra-ops.test.ts +2 -2
- package/src/__tests__/second-brain-features.test.ts +583 -0
- package/src/agency/agency-manager.ts +217 -9
- package/src/agency/default-rules.ts +83 -0
- package/src/agency/types.ts +61 -0
- package/src/brain/brain.ts +110 -8
- package/src/brain/knowledge-synthesizer.ts +216 -0
- package/src/brain/learning-radar.ts +340 -0
- package/src/brain/types.ts +16 -0
- package/src/context/context-engine.ts +114 -15
- package/src/context/types.ts +5 -0
- package/src/control/intent-router.ts +107 -0
- package/src/control/types.ts +10 -0
- package/src/curator/classifier.ts +86 -0
- package/src/curator/quality-gate.ts +127 -0
- package/src/domain-packs/index.ts +0 -6
- package/src/domain-packs/loader.ts +25 -5
- package/src/domain-packs/pack-runtime.ts +6 -6
- package/src/domain-packs/types.ts +8 -2
- package/src/engine/bin/soleri-engine.ts +18 -2
- package/src/engine/index.ts +2 -0
- package/src/engine/module-manifest.ts +99 -0
- package/src/engine/register-engine.ts +21 -2
- package/src/events/event-bus.ts +58 -0
- package/src/flows/chain-runner.ts +369 -0
- package/src/flows/chain-types.ts +57 -0
- package/src/index.ts +0 -1
- package/src/intake/content-classifier.ts +0 -2
- package/src/intake/text-ingester.ts +234 -0
- package/src/llm/llm-client.ts +50 -7
- package/src/llm/oauth-discovery.ts +151 -0
- package/src/llm/types.ts +4 -2
- package/src/packs/pack-installer.ts +16 -1
- package/src/persistence/index.ts +0 -1
- package/src/persistence/types.ts +2 -6
- package/src/planning/evidence-collector.ts +247 -0
- package/src/planning/planner.ts +11 -0
- package/src/plugins/index.ts +4 -0
- package/src/plugins/plugin-registry.ts +6 -1
- package/src/plugins/types.ts +10 -5
- package/src/queue/job-queue.ts +281 -0
- package/src/queue/pipeline-runner.ts +149 -0
- package/src/runtime/admin-setup-ops.ts +664 -0
- package/src/runtime/chain-ops.ts +121 -0
- package/src/runtime/claude-md-helpers.ts +218 -0
- package/src/runtime/curator-extra-ops.ts +86 -3
- package/src/runtime/facades/admin-facade.ts +5 -2
- package/src/runtime/facades/agency-facade.ts +68 -0
- package/src/runtime/facades/brain-facade.ts +142 -1
- package/src/runtime/facades/control-facade.ts +45 -0
- package/src/runtime/facades/memory-facade.ts +20 -2
- package/src/runtime/facades/plan-facade.ts +2 -0
- package/src/runtime/facades/vault-facade.ts +28 -5
- package/src/runtime/intake-ops.ts +107 -5
- package/src/runtime/memory-extra-ops.ts +312 -4
- package/src/runtime/pack-ops.ts +26 -1
- package/src/runtime/planning-extra-ops.ts +94 -0
- package/src/runtime/playbook-ops.ts +1 -1
- package/src/runtime/plugin-ops.ts +3 -0
- package/src/runtime/runtime.ts +138 -2
- package/src/runtime/session-briefing.ts +175 -0
- package/src/runtime/types.ts +23 -0
- package/src/runtime/vault-linking-ops.ts +3 -7
- package/src/vault/vault.ts +105 -4
- package/src/__tests__/postgres-provider.test.ts +0 -116
- package/src/persistence/postgres-provider.ts +0 -310
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Text Ingester — ingest articles, transcripts, and plain text into the vault.
|
|
3
|
+
*
|
|
4
|
+
* Reuses existing content-classifier (LLM extraction) and dedup-gate (TF-IDF).
|
|
5
|
+
* No new dependencies — fetch() is built-in, HTML stripping is regex-based.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { Vault } from '../vault/vault.js';
|
|
9
|
+
import type { LLMClient } from '../llm/llm-client.js';
|
|
10
|
+
import type { IntelligenceEntry } from '../intelligence/types.js';
|
|
11
|
+
import type { ClassifiedItem } from './types.js';
|
|
12
|
+
import { classifyChunk } from './content-classifier.js';
|
|
13
|
+
import { dedupItems } from './dedup-gate.js';
|
|
14
|
+
|
|
15
|
+
// ─── Types ───────────────────────────────────────────────────────────
|
|
16
|
+
|
|
17
|
+
export interface IngestSource {
|
|
18
|
+
type: 'article' | 'transcript' | 'notes' | 'documentation';
|
|
19
|
+
title: string;
|
|
20
|
+
url?: string;
|
|
21
|
+
author?: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface IngestOptions {
|
|
25
|
+
domain?: string;
|
|
26
|
+
tags?: string[];
|
|
27
|
+
/** Max chars per chunk for LLM classification. Default 4000. */
|
|
28
|
+
chunkSize?: number;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface IngestResult {
|
|
32
|
+
source: IngestSource;
|
|
33
|
+
ingested: number;
|
|
34
|
+
duplicates: number;
|
|
35
|
+
entries: Array<{ id: string; title: string; type: string }>;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// ─── Constants ───────────────────────────────────────────────────────
|
|
39
|
+
|
|
40
|
+
const DEFAULT_CHUNK_SIZE = 4000;
|
|
41
|
+
const FETCH_TIMEOUT_MS = 15000;
|
|
42
|
+
|
|
43
|
+
// ─── Class ───────────────────────────────────────────────────────────
|
|
44
|
+
|
|
45
|
+
export class TextIngester {
|
|
46
|
+
private vault: Vault;
|
|
47
|
+
private llm: LLMClient | null;
|
|
48
|
+
|
|
49
|
+
constructor(vault: Vault, llm: LLMClient | null) {
|
|
50
|
+
this.vault = vault;
|
|
51
|
+
this.llm = llm;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Ingest a URL — fetch, strip HTML, classify, dedup, store.
|
|
56
|
+
*/
|
|
57
|
+
async ingestUrl(url: string, opts?: IngestOptions): Promise<IngestResult> {
|
|
58
|
+
if (!this.llm) {
|
|
59
|
+
return { source: { type: 'article', title: url }, ingested: 0, duplicates: 0, entries: [] };
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
let text: string;
|
|
63
|
+
let title = url;
|
|
64
|
+
try {
|
|
65
|
+
const response = await fetch(url, {
|
|
66
|
+
signal: AbortSignal.timeout(FETCH_TIMEOUT_MS),
|
|
67
|
+
headers: { 'User-Agent': 'Soleri/1.0 (knowledge ingestion)' },
|
|
68
|
+
});
|
|
69
|
+
if (!response.ok) {
|
|
70
|
+
return { source: { type: 'article', title }, ingested: 0, duplicates: 0, entries: [] };
|
|
71
|
+
}
|
|
72
|
+
const html = await response.text();
|
|
73
|
+
title = extractTitle(html) ?? url;
|
|
74
|
+
text = stripHtml(html);
|
|
75
|
+
} catch {
|
|
76
|
+
return { source: { type: 'article', title }, ingested: 0, duplicates: 0, entries: [] };
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (text.length < 50) {
|
|
80
|
+
return { source: { type: 'article', title }, ingested: 0, duplicates: 0, entries: [] };
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const source: IngestSource = { type: 'article', title, url };
|
|
84
|
+
return this.ingestText(text, source, opts);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Ingest raw text — classify, dedup, store.
|
|
89
|
+
*/
|
|
90
|
+
async ingestText(
|
|
91
|
+
text: string,
|
|
92
|
+
source: IngestSource,
|
|
93
|
+
opts?: IngestOptions,
|
|
94
|
+
): Promise<IngestResult> {
|
|
95
|
+
if (!this.llm) {
|
|
96
|
+
return { source, ingested: 0, duplicates: 0, entries: [] };
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const chunkSize = opts?.chunkSize ?? DEFAULT_CHUNK_SIZE;
|
|
100
|
+
const chunks = splitIntoChunks(text, chunkSize);
|
|
101
|
+
const domain = opts?.domain ?? 'general';
|
|
102
|
+
const extraTags = opts?.tags ?? [];
|
|
103
|
+
|
|
104
|
+
// Classify all chunks
|
|
105
|
+
const allItems: ClassifiedItem[] = [];
|
|
106
|
+
for (const chunk of chunks) {
|
|
107
|
+
const items = await classifyChunk(this.llm, chunk, `${source.type}: ${source.title}`);
|
|
108
|
+
allItems.push(...items);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (allItems.length === 0) {
|
|
112
|
+
return { source, ingested: 0, duplicates: 0, entries: [] };
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Dedup against vault
|
|
116
|
+
const dedupResults = dedupItems(allItems, this.vault);
|
|
117
|
+
const unique = dedupResults.filter((r) => !r.isDuplicate).map((r) => r.item);
|
|
118
|
+
const duplicateCount = dedupResults.filter((r) => r.isDuplicate).length;
|
|
119
|
+
|
|
120
|
+
// Build source attribution for context field
|
|
121
|
+
const attribution = buildAttribution(source);
|
|
122
|
+
|
|
123
|
+
// Store in vault
|
|
124
|
+
const entries: IntelligenceEntry[] = unique.map((item, i) => ({
|
|
125
|
+
id: `ingest-${source.type}-${Date.now()}-${i}-${Math.random().toString(36).slice(2, 6)}`,
|
|
126
|
+
type: mapType(item.type),
|
|
127
|
+
domain,
|
|
128
|
+
title: item.title,
|
|
129
|
+
description: item.description,
|
|
130
|
+
severity: mapSeverity(item.severity),
|
|
131
|
+
tags: [...(item.tags ?? []), ...extraTags, 'ingested', source.type],
|
|
132
|
+
context: attribution,
|
|
133
|
+
origin: 'user' as const,
|
|
134
|
+
}));
|
|
135
|
+
|
|
136
|
+
if (entries.length > 0) {
|
|
137
|
+
this.vault.seed(entries);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return {
|
|
141
|
+
source,
|
|
142
|
+
ingested: entries.length,
|
|
143
|
+
duplicates: duplicateCount,
|
|
144
|
+
entries: entries.map((e) => ({ id: e.id, title: e.title, type: e.type })),
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Ingest multiple items in sequence.
|
|
150
|
+
*/
|
|
151
|
+
async ingestBatch(
|
|
152
|
+
items: Array<{ text: string; source: IngestSource; opts?: IngestOptions }>,
|
|
153
|
+
): Promise<IngestResult[]> {
|
|
154
|
+
const results: IngestResult[] = [];
|
|
155
|
+
for (const item of items) {
|
|
156
|
+
const result = await this.ingestText(item.text, item.source, item.opts);
|
|
157
|
+
results.push(result);
|
|
158
|
+
}
|
|
159
|
+
return results;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// ─── Helpers ─────────────────────────────────────────────────────────
|
|
164
|
+
|
|
165
|
+
function stripHtml(html: string): string {
|
|
166
|
+
return (
|
|
167
|
+
html
|
|
168
|
+
// Remove script and style blocks
|
|
169
|
+
.replace(/<script[\s\S]*?<\/script>/gi, '')
|
|
170
|
+
.replace(/<style[\s\S]*?<\/style>/gi, '')
|
|
171
|
+
// Remove nav, header, footer, aside
|
|
172
|
+
.replace(/<(nav|header|footer|aside)[\s\S]*?<\/\1>/gi, '')
|
|
173
|
+
// Remove all HTML tags
|
|
174
|
+
.replace(/<[^>]+>/g, ' ')
|
|
175
|
+
// Decode common entities
|
|
176
|
+
.replace(/&/g, '&')
|
|
177
|
+
.replace(/</g, '<')
|
|
178
|
+
.replace(/>/g, '>')
|
|
179
|
+
.replace(/"/g, '"')
|
|
180
|
+
.replace(/'/g, "'")
|
|
181
|
+
.replace(/ /g, ' ')
|
|
182
|
+
// Collapse whitespace
|
|
183
|
+
.replace(/\s+/g, ' ')
|
|
184
|
+
.trim()
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function extractTitle(html: string): string | null {
|
|
189
|
+
const match = html.match(/<title[^>]*>(.*?)<\/title>/i);
|
|
190
|
+
if (match) {
|
|
191
|
+
return match[1].replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').trim();
|
|
192
|
+
}
|
|
193
|
+
return null;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function splitIntoChunks(text: string, chunkSize: number): string[] {
|
|
197
|
+
if (text.length <= chunkSize) return [text];
|
|
198
|
+
|
|
199
|
+
const chunks: string[] = [];
|
|
200
|
+
let start = 0;
|
|
201
|
+
while (start < text.length) {
|
|
202
|
+
let end = start + chunkSize;
|
|
203
|
+
// Try to break at a sentence boundary
|
|
204
|
+
if (end < text.length) {
|
|
205
|
+
const lastPeriod = text.lastIndexOf('. ', end);
|
|
206
|
+
if (lastPeriod > start + chunkSize * 0.5) {
|
|
207
|
+
end = lastPeriod + 2;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
chunks.push(text.slice(start, end).trim());
|
|
211
|
+
start = end;
|
|
212
|
+
}
|
|
213
|
+
return chunks.filter((c) => c.length > 0);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function buildAttribution(source: IngestSource): string {
|
|
217
|
+
const parts = [`Source: ${source.type}`];
|
|
218
|
+
if (source.title) parts.push(`Title: ${source.title}`);
|
|
219
|
+
if (source.url) parts.push(`URL: ${source.url}`);
|
|
220
|
+
if (source.author) parts.push(`Author: ${source.author}`);
|
|
221
|
+
return parts.join(' | ');
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function mapType(type: string): IntelligenceEntry['type'] {
|
|
225
|
+
if (type === 'pattern') return 'pattern';
|
|
226
|
+
if (type === 'anti-pattern') return 'anti-pattern';
|
|
227
|
+
return 'rule';
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function mapSeverity(severity: string | undefined): IntelligenceEntry['severity'] {
|
|
231
|
+
if (severity === 'critical') return 'critical';
|
|
232
|
+
if (severity === 'warning') return 'warning';
|
|
233
|
+
return 'suggestion';
|
|
234
|
+
}
|
package/src/llm/llm-client.ts
CHANGED
|
@@ -25,8 +25,45 @@ const OPENAI_API_URL = 'https://api.openai.com/v1/chat/completions';
|
|
|
25
25
|
// =============================================================================
|
|
26
26
|
|
|
27
27
|
function loadRoutingConfig(agentId: string): RoutingConfig {
|
|
28
|
+
// Default task→model routing: cheap models for routine, powerful for reasoning.
|
|
29
|
+
// Anthropic routes use extended thinking for quality decisions when available.
|
|
30
|
+
// Agents can override via ~/.{agentId}/model-routing.json.
|
|
31
|
+
const defaultRoutes: RouteEntry[] = [
|
|
32
|
+
// OpenAI routes (default — works without Anthropic key)
|
|
33
|
+
{ caller: 'quality-gate', task: 'evaluate', model: 'gpt-4o', provider: 'openai' },
|
|
34
|
+
{ caller: 'classifier', task: 'classify', model: 'gpt-4o-mini', provider: 'openai' },
|
|
35
|
+
{ caller: 'knowledge-synthesizer', task: 'synthesize', model: 'gpt-4o', provider: 'openai' },
|
|
36
|
+
{ caller: 'content-classifier', model: 'gpt-4o-mini', provider: 'openai' },
|
|
37
|
+
{ caller: 'vault-linking', task: 'evaluate-links', model: 'gpt-4o-mini', provider: 'openai' },
|
|
38
|
+
// Anthropic routes (higher quality when key available — extended thinking capable)
|
|
39
|
+
{
|
|
40
|
+
caller: 'quality-gate-anthropic',
|
|
41
|
+
task: 'evaluate',
|
|
42
|
+
model: 'claude-sonnet-4-20250514',
|
|
43
|
+
provider: 'anthropic',
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
caller: 'contradiction-evaluator',
|
|
47
|
+
task: 'evaluate',
|
|
48
|
+
model: 'claude-sonnet-4-20250514',
|
|
49
|
+
provider: 'anthropic',
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
caller: 'knowledge-synthesizer-anthropic',
|
|
53
|
+
task: 'synthesize',
|
|
54
|
+
model: 'claude-sonnet-4-20250514',
|
|
55
|
+
provider: 'anthropic',
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
caller: 'classifier-anthropic',
|
|
59
|
+
task: 'classify',
|
|
60
|
+
model: 'claude-haiku-4-5-20251001',
|
|
61
|
+
provider: 'anthropic',
|
|
62
|
+
},
|
|
63
|
+
];
|
|
64
|
+
|
|
28
65
|
const defaultConfig: RoutingConfig = {
|
|
29
|
-
routes:
|
|
66
|
+
routes: defaultRoutes,
|
|
30
67
|
defaultOpenAIModel: 'gpt-4o-mini',
|
|
31
68
|
defaultAnthropicModel: 'claude-sonnet-4-20250514',
|
|
32
69
|
};
|
|
@@ -123,6 +160,8 @@ interface AnthropicClient {
|
|
|
123
160
|
};
|
|
124
161
|
}
|
|
125
162
|
|
|
163
|
+
type ResolvedLLMOptions = LLMCallOptions & { model: string; provider: 'openai' | 'anthropic' };
|
|
164
|
+
|
|
126
165
|
export class LLMClient {
|
|
127
166
|
private openaiKeyPool: KeyPool;
|
|
128
167
|
private anthropicKeyPool: KeyPool;
|
|
@@ -144,11 +183,15 @@ export class LLMClient {
|
|
|
144
183
|
|
|
145
184
|
async complete(options: LLMCallOptions): Promise<LLMCallResult> {
|
|
146
185
|
const routed = this.router.resolve(options.caller, options.task, options.model);
|
|
147
|
-
const
|
|
186
|
+
const resolved: ResolvedLLMOptions = {
|
|
187
|
+
...options,
|
|
188
|
+
model: options.model ?? routed.model,
|
|
189
|
+
provider: options.provider ?? routed.provider,
|
|
190
|
+
};
|
|
148
191
|
|
|
149
|
-
return
|
|
150
|
-
? this.callAnthropic(
|
|
151
|
-
: this.callOpenAI(
|
|
192
|
+
return resolved.provider === 'anthropic'
|
|
193
|
+
? this.callAnthropic(resolved)
|
|
194
|
+
: this.callOpenAI(resolved);
|
|
152
195
|
}
|
|
153
196
|
|
|
154
197
|
isAvailable(): { openai: boolean; anthropic: boolean } {
|
|
@@ -166,7 +209,7 @@ export class LLMClient {
|
|
|
166
209
|
// OPENAI
|
|
167
210
|
// ===========================================================================
|
|
168
211
|
|
|
169
|
-
private async callOpenAI(options:
|
|
212
|
+
private async callOpenAI(options: ResolvedLLMOptions): Promise<LLMCallResult> {
|
|
170
213
|
const keyPool = this.openaiKeyPool.hasKeys ? this.openaiKeyPool : null;
|
|
171
214
|
|
|
172
215
|
if (!keyPool) {
|
|
@@ -238,7 +281,7 @@ export class LLMClient {
|
|
|
238
281
|
// ANTHROPIC
|
|
239
282
|
// ===========================================================================
|
|
240
283
|
|
|
241
|
-
private async callAnthropic(options:
|
|
284
|
+
private async callAnthropic(options: ResolvedLLMOptions): Promise<LLMCallResult> {
|
|
242
285
|
const client = await this.getAnthropicClient();
|
|
243
286
|
if (!client) {
|
|
244
287
|
throw new LLMError('Anthropic API key not configured', { retryable: false });
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OAuth Token Discovery — find Claude Code OAuth tokens on macOS and Linux.
|
|
3
|
+
*
|
|
4
|
+
* Priority:
|
|
5
|
+
* 1. ANTHROPIC_API_KEY env var (explicit, highest priority)
|
|
6
|
+
* 2. Claude Code credentials file (~/.claude/.credentials.json or similar)
|
|
7
|
+
* 3. macOS Keychain (security find-generic-password)
|
|
8
|
+
* 4. Linux GNOME Keyring (secret-tool lookup)
|
|
9
|
+
* 5. null (graceful fallback → use OpenAI or no LLM)
|
|
10
|
+
*
|
|
11
|
+
* Cached for 5 minutes to avoid repeated I/O.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { execFileSync } from 'node:child_process';
|
|
15
|
+
import { readFileSync, existsSync } from 'node:fs';
|
|
16
|
+
import { join } from 'node:path';
|
|
17
|
+
import { homedir, platform } from 'node:os';
|
|
18
|
+
|
|
19
|
+
// ─── Cache ───────────────────────────────────────────────────────────
|
|
20
|
+
|
|
21
|
+
let cachedToken: string | null = null;
|
|
22
|
+
let cacheTimestamp = 0;
|
|
23
|
+
const CACHE_TTL_MS = 5 * 60 * 1000;
|
|
24
|
+
|
|
25
|
+
// ─── Public API ──────────────────────────────────────────────────────
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Discover an Anthropic API token. Returns null if none found.
|
|
29
|
+
* Results cached for 5 minutes.
|
|
30
|
+
*/
|
|
31
|
+
export function discoverAnthropicToken(): string | null {
|
|
32
|
+
if (cachedToken && Date.now() - cacheTimestamp < CACHE_TTL_MS) {
|
|
33
|
+
return cachedToken;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const token = tryEnvVar() ?? tryCredentialsFile() ?? tryPlatformKeychain();
|
|
37
|
+
|
|
38
|
+
if (token) {
|
|
39
|
+
cachedToken = token;
|
|
40
|
+
cacheTimestamp = Date.now();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return token;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// ─── Discovery Methods ───────────────────────────────────────────────
|
|
47
|
+
|
|
48
|
+
function tryEnvVar(): string | null {
|
|
49
|
+
return process.env.ANTHROPIC_API_KEY ?? null;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function tryCredentialsFile(): string | null {
|
|
53
|
+
const candidates = [
|
|
54
|
+
join(homedir(), '.claude', '.credentials.json'),
|
|
55
|
+
join(homedir(), '.claude', 'credentials.json'),
|
|
56
|
+
join(homedir(), '.config', 'claude', 'credentials.json'),
|
|
57
|
+
];
|
|
58
|
+
|
|
59
|
+
for (const path of candidates) {
|
|
60
|
+
try {
|
|
61
|
+
if (!existsSync(path)) continue;
|
|
62
|
+
const raw = readFileSync(path, 'utf-8');
|
|
63
|
+
const parsed = JSON.parse(raw) as Record<string, unknown>;
|
|
64
|
+
|
|
65
|
+
// Claude Code OAuth format: { claudeAiOauth: { accessToken: "..." } }
|
|
66
|
+
const oauth = parsed.claudeAiOauth as Record<string, unknown> | undefined;
|
|
67
|
+
if (oauth?.accessToken && typeof oauth.accessToken === 'string') {
|
|
68
|
+
return oauth.accessToken;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Alternative: direct token field
|
|
72
|
+
if (parsed.accessToken && typeof parsed.accessToken === 'string') {
|
|
73
|
+
return parsed.accessToken as string;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Alternative: API key field
|
|
77
|
+
if (parsed.apiKey && typeof parsed.apiKey === 'string') {
|
|
78
|
+
return parsed.apiKey as string;
|
|
79
|
+
}
|
|
80
|
+
} catch {
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function tryPlatformKeychain(): string | null {
|
|
89
|
+
const os = platform();
|
|
90
|
+
|
|
91
|
+
if (os === 'darwin') return tryMacKeychain();
|
|
92
|
+
if (os === 'linux') return tryLinuxKeyring();
|
|
93
|
+
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function tryMacKeychain(): string | null {
|
|
98
|
+
try {
|
|
99
|
+
const raw = execFileSync(
|
|
100
|
+
'security',
|
|
101
|
+
['find-generic-password', '-s', 'Claude Code-credentials', '-w'],
|
|
102
|
+
{ encoding: 'utf-8', timeout: 5000, stdio: ['pipe', 'pipe', 'pipe'] },
|
|
103
|
+
).trim();
|
|
104
|
+
|
|
105
|
+
if (!raw) return null;
|
|
106
|
+
|
|
107
|
+
// Try JSON parse
|
|
108
|
+
try {
|
|
109
|
+
const parsed = JSON.parse(raw) as Record<string, unknown>;
|
|
110
|
+
const oauth = parsed.claudeAiOauth as Record<string, unknown> | undefined;
|
|
111
|
+
if (oauth?.accessToken && typeof oauth.accessToken === 'string') {
|
|
112
|
+
return oauth.accessToken;
|
|
113
|
+
}
|
|
114
|
+
} catch {
|
|
115
|
+
// JSON might be truncated — try regex fallback
|
|
116
|
+
const match = raw.match(/"accessToken"\s*:\s*"([^"]+)"/);
|
|
117
|
+
if (match) return match[1];
|
|
118
|
+
}
|
|
119
|
+
} catch {
|
|
120
|
+
// Keychain not available or no entry
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function tryLinuxKeyring(): string | null {
|
|
127
|
+
try {
|
|
128
|
+
// GNOME Keyring via secret-tool
|
|
129
|
+
const token = execFileSync(
|
|
130
|
+
'secret-tool',
|
|
131
|
+
['lookup', 'service', 'Claude Code', 'type', 'credentials'],
|
|
132
|
+
{ encoding: 'utf-8', timeout: 5000, stdio: ['pipe', 'pipe', 'pipe'] },
|
|
133
|
+
).trim();
|
|
134
|
+
|
|
135
|
+
if (token) {
|
|
136
|
+
// May be JSON or raw token
|
|
137
|
+
try {
|
|
138
|
+
const parsed = JSON.parse(token) as Record<string, unknown>;
|
|
139
|
+
const oauth = parsed.claudeAiOauth as Record<string, unknown> | undefined;
|
|
140
|
+
if (oauth?.accessToken) return oauth.accessToken as string;
|
|
141
|
+
} catch {
|
|
142
|
+
// Treat as raw token
|
|
143
|
+
if (token.length > 20) return token;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
} catch {
|
|
147
|
+
// secret-tool not available or no entry
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return null;
|
|
151
|
+
}
|
package/src/llm/types.ts
CHANGED
|
@@ -43,8 +43,10 @@ export class LLMError extends Error {
|
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
export interface LLMCallOptions {
|
|
46
|
-
|
|
47
|
-
|
|
46
|
+
/** Provider override. If omitted, the model router selects based on caller/task. */
|
|
47
|
+
provider?: 'openai' | 'anthropic';
|
|
48
|
+
/** Model override. If omitted, the model router selects based on caller/task. */
|
|
49
|
+
model?: string;
|
|
48
50
|
systemPrompt: string;
|
|
49
51
|
userPrompt: string;
|
|
50
52
|
temperature?: number;
|
|
@@ -21,6 +21,7 @@ import { loadIntelligenceData } from '../intelligence/loader.js';
|
|
|
21
21
|
import type { Vault } from '../vault/vault.js';
|
|
22
22
|
import type { PluginRegistry } from '../plugins/plugin-registry.js';
|
|
23
23
|
import type { PluginContext } from '../plugins/types.js';
|
|
24
|
+
import type { PackRuntime } from '../domain-packs/pack-runtime.js';
|
|
24
25
|
|
|
25
26
|
const MANIFEST_FILENAME = 'soleri-pack.json';
|
|
26
27
|
|
|
@@ -108,7 +109,11 @@ export class PackInstaller {
|
|
|
108
109
|
/**
|
|
109
110
|
* Install a knowledge pack from a directory.
|
|
110
111
|
*/
|
|
111
|
-
async install(
|
|
112
|
+
async install(
|
|
113
|
+
packDir: string,
|
|
114
|
+
runtimeCtx?: unknown,
|
|
115
|
+
packRuntime?: PackRuntime,
|
|
116
|
+
): Promise<InstallResult> {
|
|
112
117
|
// Validate first
|
|
113
118
|
const validation = this.validate(packDir);
|
|
114
119
|
if (!validation.valid || !validation.manifest) {
|
|
@@ -174,6 +179,16 @@ export class PackInstaller {
|
|
|
174
179
|
}
|
|
175
180
|
|
|
176
181
|
const ctx: PluginContext = {
|
|
182
|
+
packRuntime:
|
|
183
|
+
packRuntime ??
|
|
184
|
+
({
|
|
185
|
+
vault: {},
|
|
186
|
+
getProject: () => undefined,
|
|
187
|
+
listProjects: () => [],
|
|
188
|
+
createCheck: () => '',
|
|
189
|
+
validateCheck: () => null,
|
|
190
|
+
validateAndConsume: () => null,
|
|
191
|
+
} as unknown as PackRuntime),
|
|
177
192
|
runtime: runtimeCtx ?? {},
|
|
178
193
|
manifest: pluginLoaded.manifest,
|
|
179
194
|
directory: packDir,
|
package/src/persistence/index.ts
CHANGED
package/src/persistence/types.ts
CHANGED
|
@@ -35,7 +35,7 @@ export interface PersistenceProvider {
|
|
|
35
35
|
transaction<T>(fn: () => T): T;
|
|
36
36
|
|
|
37
37
|
/** Identifies the backend engine. */
|
|
38
|
-
readonly backend: 'sqlite'
|
|
38
|
+
readonly backend: 'sqlite';
|
|
39
39
|
|
|
40
40
|
/** Full-text search abstraction. */
|
|
41
41
|
ftsSearch<T = Record<string, unknown>>(
|
|
@@ -52,12 +52,8 @@ export interface PersistenceProvider {
|
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
export interface PersistenceConfig {
|
|
55
|
-
type: 'sqlite'
|
|
55
|
+
type: 'sqlite';
|
|
56
56
|
path: string;
|
|
57
|
-
/** PostgreSQL connection string. */
|
|
58
|
-
connectionString?: string;
|
|
59
|
-
/** PostgreSQL pool size. */
|
|
60
|
-
poolSize?: number;
|
|
61
57
|
}
|
|
62
58
|
|
|
63
59
|
export interface FtsSearchOptions {
|