@hasna/knowledge 0.2.27 → 0.2.28

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.
Files changed (58) hide show
  1. package/README.md +41 -0
  2. package/bin/open-knowledge-mcp.js +15 -7
  3. package/bin/open-knowledge.js +17 -17
  4. package/dist/agent.d.ts +35 -0
  5. package/dist/artifact-store.d.ts +63 -0
  6. package/dist/auth.d.ts +35 -0
  7. package/dist/embeddings.d.ts +77 -0
  8. package/dist/index.d.ts +20 -0
  9. package/dist/index.js +5709 -0
  10. package/dist/knowledge-db.d.ts +27 -0
  11. package/dist/manifest-ingest.d.ts +35 -0
  12. package/dist/outbox-consume.d.ts +25 -0
  13. package/dist/provenance.d.ts +50 -0
  14. package/dist/providers.d.ts +89 -0
  15. package/dist/reindex.d.ts +37 -0
  16. package/dist/remote-client.d.ts +108 -0
  17. package/dist/retrieval.d.ts +71 -0
  18. package/dist/safety.d.ts +70 -0
  19. package/dist/sdk.d.ts +72 -0
  20. package/dist/search.d.ts +65 -0
  21. package/dist/service.d.ts +117 -0
  22. package/dist/source-ingest.d.ts +18 -0
  23. package/dist/source-ref.d.ts +30 -0
  24. package/dist/source-resolver.d.ts +92 -0
  25. package/dist/storage-contract.d.ts +106 -0
  26. package/dist/web-search.d.ts +40 -0
  27. package/dist/wiki-compiler.d.ts +67 -0
  28. package/dist/wiki-layout.d.ts +23 -0
  29. package/dist/workspace.d.ts +111 -0
  30. package/package.json +15 -7
  31. package/src/agent.ts +0 -367
  32. package/src/artifact-store.ts +0 -184
  33. package/src/auth.ts +0 -123
  34. package/src/cli.ts +0 -1184
  35. package/src/embeddings.ts +0 -516
  36. package/src/knowledge-db.ts +0 -354
  37. package/src/manifest-ingest.ts +0 -515
  38. package/src/mcp-http.js +0 -110
  39. package/src/mcp.js +0 -1503
  40. package/src/outbox-consume.ts +0 -463
  41. package/src/provenance.ts +0 -93
  42. package/src/providers.ts +0 -308
  43. package/src/reindex.ts +0 -260
  44. package/src/remote-client.ts +0 -268
  45. package/src/retrieval.ts +0 -326
  46. package/src/safety.ts +0 -265
  47. package/src/schema.js +0 -25
  48. package/src/search.ts +0 -510
  49. package/src/service.ts +0 -443
  50. package/src/source-ingest.ts +0 -268
  51. package/src/source-ref.ts +0 -104
  52. package/src/source-resolver.ts +0 -436
  53. package/src/storage-contract.ts +0 -346
  54. package/src/store.ts +0 -113
  55. package/src/web-search.ts +0 -330
  56. package/src/wiki-compiler.ts +0 -711
  57. package/src/wiki-layout.ts +0 -251
  58. package/src/workspace.ts +0 -251
@@ -1,251 +0,0 @@
1
- import { createHash } from 'node:crypto';
2
- import type { Database } from 'bun:sqlite';
3
- import type { ArtifactStore } from './artifact-store';
4
- import { generatedArtifactProvenance, type GeneratedArtifactProvenance } from './provenance';
5
- import {
6
- artifactKindForKey,
7
- hashArtifactBody,
8
- type GeneratedStorageObject,
9
- } from './storage-contract';
10
-
11
- export interface WikiLayoutInitResult {
12
- schema_key: string;
13
- root_index_key: string;
14
- wiki_readme_key: string;
15
- log_key: string;
16
- artifacts: GeneratedStorageObject[];
17
- written: string[];
18
- }
19
-
20
- interface CatalogArtifact {
21
- key: string;
22
- uri: string;
23
- hash?: string;
24
- metadata?: Record<string, unknown>;
25
- }
26
-
27
- function todayParts(now: Date): { year: string; month: string; day: string } {
28
- const year = String(now.getUTCFullYear());
29
- const month = String(now.getUTCMonth() + 1).padStart(2, '0');
30
- const day = String(now.getUTCDate()).padStart(2, '0');
31
- return { year, month, day };
32
- }
33
-
34
- function stableId(prefix: string, value: string): string {
35
- return `${prefix}_${createHash('sha256').update(value).digest('hex').slice(0, 20)}`;
36
- }
37
-
38
- function estimateTokenCount(text: string): number {
39
- const words = text.trim().split(/\s+/).filter(Boolean).length;
40
- return Math.max(1, Math.ceil(words * 1.25));
41
- }
42
-
43
- export function agentSchemaTemplate(): string {
44
- return `# Knowledge Agent Schema v1
45
-
46
- ## Source Rules
47
-
48
- - Treat open-files source references as the preferred source of truth.
49
- - Do not copy raw source files into open-knowledge.
50
- - Cite every durable fact with a source URI, revision/hash when available, and optional span.
51
- - Mark uncertainty explicitly when sources disagree or are incomplete.
52
-
53
- ## Wiki Rules
54
-
55
- - Write generated knowledge as Markdown pages under wiki/.
56
- - Keep root indexes small; use topic, team, project, and machine-readable shards for scale.
57
- - Preserve backlinks between related pages and decisions.
58
- - Prefer updating existing pages over creating near-duplicates.
59
-
60
- ## Query Rules
61
-
62
- - Search wiki pages first, then source chunks, then deeper read-only source refs.
63
- - Use web search only when requested or when current external context is required.
64
- - File useful answers back into the wiki only after approval or approved auto-write mode.
65
-
66
- ## Lint Rules
67
-
68
- - Flag stale pages, missing citations, contradictions, orphan pages, duplicate pages, and unresolved source refs.
69
- `;
70
- }
71
-
72
- export function rootIndexTemplate(): string {
73
- return `# Knowledge Index
74
-
75
- This is a compact orientation index for agents. It is not the full search index.
76
-
77
- ## Shards
78
-
79
- - wiki/
80
- - indexes/
81
- - schemas/
82
- - logs/
83
-
84
- ## Source Ownership
85
-
86
- Raw source files are resolved through open-files. This app stores source refs,
87
- citations, chunks, generated wiki artifacts, indexes, and run records.
88
- `;
89
- }
90
-
91
- export function wikiReadmeTemplate(): string {
92
- return `# Wiki
93
-
94
- Generated durable knowledge pages live here.
95
-
96
- Pages should be concise, cited, and organized for both humans and agents.
97
- `;
98
- }
99
-
100
- export async function initializeWikiLayout(store: ArtifactStore, now = new Date()): Promise<WikiLayoutInitResult> {
101
- const { year, month, day } = todayParts(now);
102
- const schemaKey = 'schemas/v1.md';
103
- const rootIndexKey = 'indexes/root.md';
104
- const wikiReadmeKey = 'wiki/README.md';
105
- const logKey = `logs/${year}/${month}/${day}.jsonl`;
106
- const event = {
107
- ts: now.toISOString(),
108
- event: 'wiki_layout_initialized',
109
- schema_key: schemaKey,
110
- root_index_key: rootIndexKey,
111
- wiki_readme_key: wikiReadmeKey,
112
- };
113
-
114
- const entries = [
115
- { key: schemaKey, body: agentSchemaTemplate(), content_type: 'text/markdown' },
116
- { key: rootIndexKey, body: rootIndexTemplate(), content_type: 'text/markdown' },
117
- { key: wikiReadmeKey, body: wikiReadmeTemplate(), content_type: 'text/markdown' },
118
- { key: logKey, body: `${JSON.stringify(event)}\n`, content_type: 'application/x-ndjson' },
119
- ];
120
-
121
- const artifacts = await Promise.all(entries.map(async (entry) => {
122
- const result = await store.put(entry);
123
- return {
124
- key: result.key,
125
- uri: result.uri,
126
- kind: artifactKindForKey(entry.key),
127
- content_type: entry.content_type,
128
- metadata: {
129
- provenance: generatedArtifactProvenance({
130
- generated_from: 'wiki_layout_init',
131
- artifact_key: entry.key,
132
- citation_required: entry.key.startsWith('wiki/') || entry.key.startsWith('indexes/'),
133
- }),
134
- },
135
- ...hashArtifactBody(entry.body),
136
- };
137
- }));
138
- return {
139
- schema_key: schemaKey,
140
- root_index_key: rootIndexKey,
141
- wiki_readme_key: wikiReadmeKey,
142
- log_key: logKey,
143
- artifacts,
144
- written: [schemaKey, rootIndexKey, wikiReadmeKey, logKey],
145
- };
146
- }
147
-
148
- function provenanceFor(artifact: CatalogArtifact): GeneratedArtifactProvenance {
149
- const existing = artifact.metadata?.provenance;
150
- if (existing && typeof existing === 'object' && !Array.isArray(existing)) {
151
- return existing as GeneratedArtifactProvenance;
152
- }
153
- return generatedArtifactProvenance({
154
- generated_from: 'wiki_layout_init',
155
- artifact_key: artifact.key,
156
- });
157
- }
158
-
159
- function recordWikiChunk(db: Database, pageId: string, title: string, artifact: CatalogArtifact, body: string, now: string): void {
160
- const provenance = provenanceFor(artifact);
161
- const chunkId = stableId('chk', `${pageId}\u0000${artifact.hash ?? artifact.uri}`);
162
- const existing = db.query<{ id: string }, [string]>('SELECT id FROM chunks WHERE wiki_page_id = ?').all(pageId);
163
- for (const row of existing) db.run('DELETE FROM chunks_fts WHERE chunk_id = ?', [row.id]);
164
- db.run('DELETE FROM chunks WHERE wiki_page_id = ?', [pageId]);
165
- db.run(
166
- `INSERT INTO chunks (id, wiki_page_id, kind, ordinal, text, token_count, start_offset, end_offset, metadata_json, created_at)
167
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
168
- [
169
- chunkId,
170
- pageId,
171
- 'wiki',
172
- 0,
173
- body,
174
- estimateTokenCount(body),
175
- 0,
176
- body.length,
177
- JSON.stringify({
178
- artifact_key: artifact.key,
179
- artifact_uri: artifact.uri,
180
- content_hash: artifact.hash ?? null,
181
- provenance,
182
- }),
183
- now,
184
- ],
185
- );
186
- db.run(
187
- 'INSERT INTO chunks_fts (chunk_id, text, title, source_uri) VALUES (?, ?, ?, ?)',
188
- [chunkId, body, title, artifact.uri],
189
- );
190
- }
191
-
192
- export function recordWikiLayoutCatalog(db: Database, artifacts: CatalogArtifact[], now = new Date()): void {
193
- const timestamp = now.toISOString();
194
- const rootIndex = artifacts.find((artifact) => artifact.key.endsWith('indexes/root.md'));
195
- const wikiReadme = artifacts.find((artifact) => artifact.key.endsWith('wiki/README.md'));
196
-
197
- if (rootIndex) {
198
- db.run(
199
- `INSERT INTO knowledge_indexes (id, kind, name, artifact_uri, shard_key, metadata_json, created_at, updated_at)
200
- VALUES (?, ?, ?, ?, ?, ?, ?, ?)
201
- ON CONFLICT(kind, name, shard_key) DO UPDATE SET
202
- artifact_uri = excluded.artifact_uri,
203
- metadata_json = excluded.metadata_json,
204
- updated_at = excluded.updated_at`,
205
- [
206
- stableId('idx', 'root:indexes/root.md'),
207
- 'root',
208
- 'root',
209
- rootIndex.uri,
210
- 'root',
211
- JSON.stringify({
212
- artifact_key: rootIndex.key,
213
- content_hash: rootIndex.hash ?? null,
214
- provenance: provenanceFor(rootIndex),
215
- }),
216
- timestamp,
217
- timestamp,
218
- ],
219
- );
220
- }
221
-
222
- if (wikiReadme) {
223
- const wikiPageId = stableId('wiki', 'wiki/README.md');
224
- db.run(
225
- `INSERT INTO wiki_pages (id, path, title, artifact_uri, content_hash, status, metadata_json, created_at, updated_at)
226
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
227
- ON CONFLICT(path) DO UPDATE SET
228
- title = excluded.title,
229
- artifact_uri = excluded.artifact_uri,
230
- content_hash = excluded.content_hash,
231
- status = excluded.status,
232
- metadata_json = excluded.metadata_json,
233
- updated_at = excluded.updated_at`,
234
- [
235
- wikiPageId,
236
- 'wiki/README.md',
237
- 'Wiki',
238
- wikiReadme.uri,
239
- wikiReadme.hash ?? null,
240
- 'active',
241
- JSON.stringify({
242
- artifact_key: wikiReadme.key,
243
- provenance: provenanceFor(wikiReadme),
244
- }),
245
- timestamp,
246
- timestamp,
247
- ],
248
- );
249
- recordWikiChunk(db, wikiPageId, 'Wiki', wikiReadme, wikiReadmeTemplate(), timestamp);
250
- }
251
- }
package/src/workspace.ts DELETED
@@ -1,251 +0,0 @@
1
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
2
- import { homedir } from 'node:os';
3
- import { dirname, join, resolve } from 'node:path';
4
-
5
- export const HASNA_KNOWLEDGE_APP_PATH = join('.hasna', 'apps', 'knowledge');
6
-
7
- export interface KnowledgeWorkspace {
8
- home: string;
9
- configPath: string;
10
- jsonStorePath: string;
11
- knowledgeDbPath: string;
12
- artifactsDir: string;
13
- cacheDir: string;
14
- exportsDir: string;
15
- indexesDir: string;
16
- logsDir: string;
17
- runsDir: string;
18
- schemasDir: string;
19
- wikiDir: string;
20
- }
21
-
22
- export interface KnowledgeConfig {
23
- version: 1;
24
- mode: 'local' | 'hosted';
25
- hosted?: {
26
- api_url?: string;
27
- };
28
- storage: {
29
- type: 'local' | 's3';
30
- artifacts_root: string;
31
- s3?: {
32
- bucket: string;
33
- prefix?: string;
34
- region?: string;
35
- profile?: string;
36
- max_attempts?: number;
37
- server_side_encryption?: 'AES256' | 'aws:kms';
38
- kms_key_id?: string;
39
- };
40
- };
41
- sources: {
42
- preferred_ref: 'open-files';
43
- allowed_schemes: string[];
44
- };
45
- embeddings?: {
46
- default_model?: string;
47
- dimensions?: number;
48
- batch_size?: number;
49
- max_parallel_calls?: number;
50
- };
51
- providers?: {
52
- default_model?: string;
53
- aliases?: Record<string, string>;
54
- openai?: {
55
- api_key_env?: string;
56
- base_url?: string;
57
- default_model?: string;
58
- };
59
- anthropic?: {
60
- api_key_env?: string;
61
- base_url?: string;
62
- default_model?: string;
63
- };
64
- deepseek?: {
65
- api_key_env?: string;
66
- base_url?: string;
67
- default_model?: string;
68
- };
69
- };
70
- safety?: {
71
- network?: {
72
- web_search_enabled?: boolean;
73
- s3_reads_enabled?: boolean;
74
- allowed_s3_buckets?: string[];
75
- };
76
- redaction?: {
77
- enabled?: boolean;
78
- };
79
- approvals?: {
80
- generated_writes_require_approval?: boolean;
81
- };
82
- };
83
- }
84
-
85
- export const HASNA_XYZ_KNOWLEDGE_CANONICAL = {
86
- division: 'xyz',
87
- app_type: 'opensource',
88
- app: 'knowledge',
89
- env: 'prod',
90
- local_path: HASNA_KNOWLEDGE_APP_PATH,
91
- s3: {
92
- bucket: 'hasna-xyz-opensource-knowledge-prod',
93
- region: 'us-east-1',
94
- profile: 'hasna-xyz-infra',
95
- prefix: '.hasna/apps/knowledge',
96
- server_side_encryption: 'AES256',
97
- },
98
- secrets: {
99
- env: 'hasna/xyz/opensource/knowledge/prod/env',
100
- aws: 'hasna/xyz/opensource/knowledge/prod/aws',
101
- s3: 'hasna/xyz/opensource/knowledge/prod/s3',
102
- rds: null,
103
- future_rds: 'hasna/xyz/opensource/knowledge/prod/rds',
104
- },
105
- source_owner: 'open-files',
106
- evidence_doc: 'docs/canonical-secrets-bootstrap-2026-06-08.md',
107
- } as const;
108
-
109
- export function canonicalHasnaXyzKnowledgeStorage(): KnowledgeConfig['storage'] {
110
- return {
111
- type: 's3',
112
- artifacts_root: 'artifacts',
113
- s3: {
114
- bucket: HASNA_XYZ_KNOWLEDGE_CANONICAL.s3.bucket,
115
- prefix: HASNA_XYZ_KNOWLEDGE_CANONICAL.s3.prefix,
116
- region: HASNA_XYZ_KNOWLEDGE_CANONICAL.s3.region,
117
- profile: HASNA_XYZ_KNOWLEDGE_CANONICAL.s3.profile,
118
- server_side_encryption: HASNA_XYZ_KNOWLEDGE_CANONICAL.s3.server_side_encryption,
119
- },
120
- };
121
- }
122
-
123
- export function legacyGlobalStorePath(): string {
124
- return join(homedir(), '.open-knowledge', 'db.json');
125
- }
126
-
127
- export function globalKnowledgeHome(): string {
128
- return join(homedir(), '.hasna', 'apps', 'knowledge');
129
- }
130
-
131
- export function projectKnowledgeHome(cwd = process.cwd()): string {
132
- return resolve(cwd, HASNA_KNOWLEDGE_APP_PATH);
133
- }
134
-
135
- export function workspaceForHome(home: string): KnowledgeWorkspace {
136
- return {
137
- home,
138
- configPath: join(home, 'config.json'),
139
- jsonStorePath: join(home, 'db.json'),
140
- knowledgeDbPath: join(home, 'knowledge.db'),
141
- artifactsDir: join(home, 'artifacts'),
142
- cacheDir: join(home, 'cache'),
143
- exportsDir: join(home, 'exports'),
144
- indexesDir: join(home, 'indexes'),
145
- logsDir: join(home, 'logs'),
146
- runsDir: join(home, 'runs'),
147
- schemasDir: join(home, 'schemas'),
148
- wikiDir: join(home, 'wiki'),
149
- };
150
- }
151
-
152
- export function defaultKnowledgeConfig(): KnowledgeConfig {
153
- return {
154
- version: 1,
155
- mode: 'local',
156
- hosted: {
157
- api_url: 'https://knowledge.hasna.xyz',
158
- },
159
- storage: {
160
- type: 'local',
161
- artifacts_root: 'artifacts',
162
- },
163
- sources: {
164
- preferred_ref: 'open-files',
165
- allowed_schemes: ['open-files', 's3', 'file', 'https', 'http'],
166
- },
167
- providers: {
168
- default_model: 'openai:gpt-5.2',
169
- aliases: {
170
- fast: 'openai:gpt-5-mini',
171
- reasoning: 'anthropic:claude-opus-4-6',
172
- sonnet: 'anthropic:claude-sonnet-4-6',
173
- deepseek: 'deepseek:deepseek-chat',
174
- 'deepseek-reasoning': 'deepseek:deepseek-reasoner',
175
- },
176
- openai: {
177
- api_key_env: 'OPENAI_API_KEY',
178
- default_model: 'gpt-5.2',
179
- },
180
- anthropic: {
181
- api_key_env: 'ANTHROPIC_API_KEY',
182
- default_model: 'claude-sonnet-4-6',
183
- },
184
- deepseek: {
185
- api_key_env: 'DEEPSEEK_API_KEY',
186
- default_model: 'deepseek-chat',
187
- },
188
- },
189
- embeddings: {
190
- default_model: 'openai:text-embedding-3-small',
191
- dimensions: 1536,
192
- batch_size: 64,
193
- max_parallel_calls: 4,
194
- },
195
- safety: {
196
- network: {
197
- web_search_enabled: false,
198
- s3_reads_enabled: false,
199
- allowed_s3_buckets: [],
200
- },
201
- redaction: {
202
- enabled: true,
203
- },
204
- approvals: {
205
- generated_writes_require_approval: true,
206
- },
207
- },
208
- };
209
- }
210
-
211
- export function ensureKnowledgeWorkspace(home: string): KnowledgeWorkspace {
212
- const workspace = workspaceForHome(home);
213
- mkdirSync(workspace.home, { recursive: true });
214
- for (const dir of [
215
- workspace.artifactsDir,
216
- workspace.cacheDir,
217
- workspace.exportsDir,
218
- workspace.indexesDir,
219
- workspace.logsDir,
220
- workspace.runsDir,
221
- workspace.schemasDir,
222
- workspace.wikiDir,
223
- ]) {
224
- mkdirSync(dir, { recursive: true });
225
- }
226
- if (!existsSync(workspace.configPath)) {
227
- writeFileSync(workspace.configPath, `${JSON.stringify(defaultKnowledgeConfig(), null, 2)}\n`);
228
- }
229
- return workspace;
230
- }
231
-
232
- export function resolveScopedWorkspace(scope: string | undefined, cwd = process.cwd()): KnowledgeWorkspace {
233
- if (scope === 'project' || scope === 'local') {
234
- return workspaceForHome(projectKnowledgeHome(cwd));
235
- }
236
- return workspaceForHome(globalKnowledgeHome());
237
- }
238
-
239
- export function ensureParentDir(path: string): void {
240
- mkdirSync(dirname(path), { recursive: true });
241
- }
242
-
243
- export function readKnowledgeConfig(path: string): KnowledgeConfig {
244
- const raw = readFileSync(path, 'utf8');
245
- return JSON.parse(raw) as KnowledgeConfig;
246
- }
247
-
248
- export function writeKnowledgeConfig(path: string, config: KnowledgeConfig): void {
249
- ensureParentDir(path);
250
- writeFileSync(path, `${JSON.stringify(config, null, 2)}\n`);
251
- }