baselineos 0.2.0-beta.1
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/LICENSE +17 -0
- package/README.md +198 -0
- package/dist/__evals__/runner.d.ts +2 -0
- package/dist/__evals__/runner.js +14687 -0
- package/dist/__evals__/runner.js.map +1 -0
- package/dist/api/server.d.ts +21 -0
- package/dist/api/server.js +1007 -0
- package/dist/api/server.js.map +1 -0
- package/dist/cli/bin.d.ts +1 -0
- package/dist/cli/bin.js +8427 -0
- package/dist/cli/bin.js.map +1 -0
- package/dist/core/agent-bus.d.ts +110 -0
- package/dist/core/agent-bus.js +242 -0
- package/dist/core/agent-bus.js.map +1 -0
- package/dist/core/cache.d.ts +66 -0
- package/dist/core/cache.js +160 -0
- package/dist/core/cache.js.map +1 -0
- package/dist/core/config.d.ts +1002 -0
- package/dist/core/config.js +429 -0
- package/dist/core/config.js.map +1 -0
- package/dist/core/indexer.d.ts +152 -0
- package/dist/core/indexer.js +481 -0
- package/dist/core/indexer.js.map +1 -0
- package/dist/core/llm-tracer.d.ts +2 -0
- package/dist/core/llm-tracer.js +241 -0
- package/dist/core/llm-tracer.js.map +1 -0
- package/dist/core/memory.d.ts +86 -0
- package/dist/core/memory.js +346 -0
- package/dist/core/memory.js.map +1 -0
- package/dist/core/opa-client.d.ts +51 -0
- package/dist/core/opa-client.js +157 -0
- package/dist/core/opa-client.js.map +1 -0
- package/dist/core/opa-policy-gate.d.ts +133 -0
- package/dist/core/opa-policy-gate.js +454 -0
- package/dist/core/opa-policy-gate.js.map +1 -0
- package/dist/core/orchestrator.d.ts +14 -0
- package/dist/core/orchestrator.js +1297 -0
- package/dist/core/orchestrator.js.map +1 -0
- package/dist/core/pii-detector.d.ts +82 -0
- package/dist/core/pii-detector.js +126 -0
- package/dist/core/pii-detector.js.map +1 -0
- package/dist/core/rag-engine.d.ts +121 -0
- package/dist/core/rag-engine.js +504 -0
- package/dist/core/rag-engine.js.map +1 -0
- package/dist/core/task-queue.d.ts +69 -0
- package/dist/core/task-queue.js +124 -0
- package/dist/core/task-queue.js.map +1 -0
- package/dist/core/telemetry.d.ts +56 -0
- package/dist/core/telemetry.js +94 -0
- package/dist/core/telemetry.js.map +1 -0
- package/dist/core/types.d.ts +328 -0
- package/dist/core/types.js +24 -0
- package/dist/core/types.js.map +1 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.js +12444 -0
- package/dist/index.js.map +1 -0
- package/dist/llm-tracer-CIIujuO-.d.ts +493 -0
- package/dist/mcp/server.d.ts +2651 -0
- package/dist/mcp/server.js +676 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/orchestrator-DF89k_AK.d.ts +506 -0
- package/package.json +157 -0
- package/templates/README.md +7 -0
- package/templates/baseline.config.ts +207 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/core/rag-engine.ts"],"names":["ext","chunkContent"],"mappings":"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","file":"rag-engine.js","sourcesContent":["/**\n * BaselineOS RAG Engine — Hybrid Retrieval (BM25 + Vector)\n *\n * Combines keyword search (MiniSearch/BM25) with optional vector search\n * (ChromaDB + OpenAI embeddings) for high-quality context retrieval.\n *\n * 6 Collections:\n * codebase — TypeScript/Python source files\n * protocols — Protocol specs, READMEs, architecture docs\n * decisions — ADRs, design decisions\n * errors — Error patterns, debugging context\n * compliance — Regulatory frameworks, audit evidence\n * domain — GTCX domain knowledge, CLAUDE.md files\n *\n * @license Apache-2.0\n */\n\nimport { readFileSync, existsSync, readdirSync, statSync } from 'fs';\nimport { join, relative, extname, basename } from 'path';\nimport { createHash } from 'crypto';\nimport MiniSearch from 'minisearch';\nimport type { KnowledgeIndexer } from './indexer.js';\n\n// ─── ChromaDB types (optional peer dependency) ──────────────────────────────\n\ninterface ChromaCollection {\n add(params: {\n ids: string[];\n documents: string[];\n metadatas?: Record<string, string>[];\n }): Promise<void>;\n query(params: {\n queryTexts: string[];\n nResults?: number;\n where?: Record<string, unknown>;\n include?: string[];\n }): Promise<{\n ids: string[][];\n documents: (string | null)[][];\n metadatas: (Record<string, string> | null)[][];\n distances: number[][];\n }>;\n count(): Promise<number>;\n}\n\ninterface ChromaClient {\n getOrCreateCollection(params: {\n name: string;\n metadata?: Record<string, string>;\n embeddingFunction?: unknown;\n }): Promise<ChromaCollection>;\n}\n\n// ─── Types ──────────────────────────────────────────────────────────────────\n\nexport type CollectionName =\n | 'codebase'\n | 'protocols'\n | 'decisions'\n | 'errors'\n | 'compliance'\n | 'domain';\n\nexport interface RAGConfig {\n projectRoot: string;\n /** OpenAI API key for embeddings (optional — BM25 works without it) */\n openaiApiKey?: string;\n /** ChromaDB host (optional — BM25 works without it) */\n chromaHost?: string;\n chromaPort?: number;\n /** Max chunks per collection */\n maxChunksPerCollection?: number;\n /** Chunk size in characters */\n chunkSize?: number;\n /** Chunk overlap in characters */\n chunkOverlap?: number;\n /** BM25 result limit */\n bm25Limit?: number;\n /** Vector result limit */\n vectorLimit?: number;\n /** Minimum relevance score (0-1) */\n minRelevance?: number;\n}\n\nexport interface RAGChunk {\n id: string;\n content: string;\n collection: CollectionName;\n source: string;\n repo: string;\n /** Section within the file */\n section?: string;\n /** Content hash for dedup */\n hash: string;\n /** Token estimate (content.length / 4) */\n tokenEstimate: number;\n metadata: Record<string, string>;\n}\n\nexport interface RAGResult {\n chunks: Array<{\n chunk: RAGChunk;\n score: number;\n matchType: 'bm25' | 'vector' | 'hybrid';\n }>;\n query: string;\n totalChunks: number;\n bm25Hits: number;\n vectorHits: number;\n duration: number;\n}\n\nexport interface IngestionResult {\n totalFiles: number;\n totalChunks: number;\n collections: Record<CollectionName, number>;\n duration: number;\n errors: string[];\n}\n\n// ─── Collection Classification ──────────────────────────────────────────────\n\nconst COLLECTION_RULES: Array<{\n collection: CollectionName;\n pathPatterns: RegExp[];\n extensions: string[];\n}> = [\n {\n collection: 'decisions',\n pathPatterns: [/decisions\\//, /ADR-/, /adr-/],\n extensions: ['.md'],\n },\n {\n collection: 'protocols',\n pathPatterns: [/3-protocols\\//, /SPEC\\.md$/, /protocol/i],\n extensions: ['.md'],\n },\n {\n collection: 'compliance',\n pathPatterns: [/compliance/, /regulatory/, /fatf/i, /esg/i, /10-compliance/],\n extensions: ['.md', '.json'],\n },\n {\n collection: 'errors',\n pathPatterns: [/error/, /debug/, /troubleshoot/],\n extensions: ['.md', '.ts', '.json'],\n },\n {\n collection: 'codebase',\n pathPatterns: [/src\\//, /lib\\//, /packages\\//],\n extensions: ['.ts', '.tsx', '.js', '.py'],\n },\n {\n collection: 'domain',\n pathPatterns: [/CLAUDE\\.md$/, /_cannon\\//, /_sop\\//, /README\\.md$/],\n extensions: ['.md'],\n },\n];\n\nfunction classifyFile(filePath: string): CollectionName {\n for (const rule of COLLECTION_RULES) {\n const ext = extname(filePath);\n if (!rule.extensions.includes(ext)) continue;\n for (const pattern of rule.pathPatterns) {\n if (pattern.test(filePath)) return rule.collection;\n }\n }\n // Default: domain for .md, codebase for code\n const ext = extname(filePath);\n if (['.ts', '.tsx', '.js', '.py'].includes(ext)) return 'codebase';\n return 'domain';\n}\n\n// ─── Chunking ───────────────────────────────────────────────────────────────\n\nfunction chunkContent(\n content: string,\n chunkSize: number,\n overlap: number\n): string[] {\n const chunks: string[] = [];\n\n // Try to split on section boundaries first (## headings for markdown)\n const sections = content.split(/(?=^##\\s)/m);\n\n if (sections.length > 1) {\n // Markdown with sections — chunk by section\n let current = '';\n for (const section of sections) {\n if (current.length + section.length > chunkSize && current.length > 0) {\n chunks.push(current.trim());\n // Keep overlap from end of previous chunk\n current = current.slice(-overlap) + section;\n } else {\n current += section;\n }\n }\n if (current.trim()) chunks.push(current.trim());\n } else {\n // No sections — chunk by character window\n for (let i = 0; i < content.length; i += chunkSize - overlap) {\n const chunk = content.slice(i, i + chunkSize);\n if (chunk.trim()) chunks.push(chunk.trim());\n }\n }\n\n return chunks.length > 0 ? chunks : [content.trim()];\n}\n\n// ─── RAG Engine ─────────────────────────────────────────────────────────────\n\nexport class RAGEngine {\n private config: Required<RAGConfig>;\n private chunks: Map<string, RAGChunk> = new Map();\n private searchIndex: MiniSearch;\n private initialized = false;\n private chromaCollections: Map<CollectionName, ChromaCollection> = new Map();\n private chromaAvailable = false;\n\n constructor(config: RAGConfig) {\n this.config = {\n projectRoot: config.projectRoot,\n openaiApiKey: config.openaiApiKey ?? process.env.OPENAI_API_KEY ?? '',\n chromaHost: config.chromaHost ?? 'localhost',\n chromaPort: config.chromaPort ?? 8000,\n maxChunksPerCollection: config.maxChunksPerCollection ?? 1000,\n chunkSize: config.chunkSize ?? 1500,\n chunkOverlap: config.chunkOverlap ?? 200,\n bm25Limit: config.bm25Limit ?? 10,\n vectorLimit: config.vectorLimit ?? 5,\n minRelevance: config.minRelevance ?? 0.3,\n };\n\n this.searchIndex = new MiniSearch({\n fields: ['content', 'source', 'section', 'repo'],\n storeFields: ['id', 'collection', 'source', 'repo', 'section'],\n searchOptions: {\n boost: { content: 2, section: 1.5, source: 1 },\n fuzzy: 0.2,\n prefix: true,\n },\n });\n }\n\n // ─── Ingestion ────────────────────────────────────────────────────────────\n\n async ingest(knowledgePaths?: string[]): Promise<IngestionResult> {\n const start = Date.now();\n const errors: string[] = [];\n const collectionCounts: Record<CollectionName, number> = {\n codebase: 0,\n protocols: 0,\n decisions: 0,\n errors: 0,\n compliance: 0,\n domain: 0,\n };\n\n // Default paths covering the GTCX ecosystem\n const paths = knowledgePaths ?? [\n '**/_sop/.gtcx/decisions/*.md',\n '**/CLAUDE.md',\n '**/_cannon/*.md',\n '3-protocols/**/SPEC.md',\n '3-protocols/**/README.md',\n '6-platforms/**/SPEC.md',\n '6-platforms/**/README.md',\n '5-intelligence/**/README.md',\n '2-core/packages/*/src/**/*.ts',\n 'compliance-os/**/*.md',\n 'ai-1-agile/_sop/**/*.md',\n ];\n\n const files = this.resolveFiles(paths);\n let totalChunks = 0;\n\n for (const filePath of files) {\n try {\n const content = readFileSync(filePath, 'utf-8');\n if (!content.trim()) continue;\n\n const relPath = relative(this.config.projectRoot, filePath);\n const collection = classifyFile(relPath);\n const repo = this.extractRepo(relPath);\n\n // Skip if collection is full\n if (collectionCounts[collection] >= this.config.maxChunksPerCollection) continue;\n\n const fileChunks = chunkContent(\n content,\n this.config.chunkSize,\n this.config.chunkOverlap\n );\n\n for (let i = 0; i < fileChunks.length; i++) {\n const chunkContent = fileChunks[i]!;\n const hash = createHash('sha256').update(chunkContent).digest('hex').slice(0, 16);\n const id = `${collection}:${hash}`;\n\n // Dedup\n if (this.chunks.has(id)) continue;\n\n const chunk: RAGChunk = {\n id,\n content: chunkContent,\n collection,\n source: relPath,\n repo,\n section: this.extractSection(chunkContent),\n hash,\n tokenEstimate: Math.ceil(chunkContent.length / 4),\n metadata: {\n file: basename(filePath),\n ext: extname(filePath),\n chunkIndex: String(i),\n totalChunks: String(fileChunks.length),\n },\n };\n\n this.chunks.set(id, chunk);\n collectionCounts[collection]++;\n totalChunks++;\n }\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n errors.push(`${filePath}: ${msg}`);\n }\n }\n\n // Build BM25 index\n this.rebuildSearchIndex();\n\n // Initialize ChromaDB vector store (optional — graceful fallback to BM25-only)\n await this.initChroma(errors);\n\n this.initialized = true;\n\n return {\n totalFiles: files.length,\n totalChunks,\n collections: collectionCounts,\n duration: Date.now() - start,\n errors,\n };\n }\n\n // ─── Hybrid Search ────────────────────────────────────────────────────────\n\n async search(\n query: string,\n options?: {\n collections?: CollectionName[];\n repo?: string;\n limit?: number;\n minScore?: number;\n }\n ): Promise<RAGResult> {\n const start = Date.now();\n const limit = options?.limit ?? this.config.bm25Limit;\n const minScore = options?.minScore ?? this.config.minRelevance;\n\n // BM25 search\n const bm25Results = this.bm25Search(query, {\n collections: options?.collections,\n repo: options?.repo,\n limit,\n });\n\n // Vector search via ChromaDB (graceful fallback — returns [] if unavailable)\n const vectorResults = await this.vectorSearch(query, {\n collections: options?.collections,\n repo: options?.repo,\n limit: this.config.vectorLimit,\n });\n\n // Merge with reciprocal rank fusion\n const merged = new Map<string, { chunk: RAGChunk; score: number; matchType: 'bm25' | 'vector' | 'hybrid' }>();\n\n // Add BM25 results with rank-based scoring\n for (let i = 0; i < bm25Results.length; i++) {\n const r = bm25Results[i]!;\n if (r.score < minScore) continue;\n const rrfScore = 1 / (60 + i); // k=60 for RRF\n merged.set(r.chunk.id, { chunk: r.chunk, score: rrfScore, matchType: 'bm25' });\n }\n\n // Merge vector results — boost if also found in BM25\n for (let i = 0; i < vectorResults.length; i++) {\n const r = vectorResults[i]!;\n const rrfScore = 1 / (60 + i);\n const existing = merged.get(r.chunk.id);\n if (existing) {\n // Found in both — hybrid result with combined score\n existing.score += rrfScore;\n existing.matchType = 'hybrid';\n } else {\n merged.set(r.chunk.id, { chunk: r.chunk, score: rrfScore, matchType: 'vector' });\n }\n }\n\n // Sort by fused score descending\n const sorted = Array.from(merged.values()).sort((a, b) => b.score - a.score);\n\n return {\n chunks: sorted.slice(0, limit),\n query,\n totalChunks: this.chunks.size,\n bm25Hits: bm25Results.length,\n vectorHits: vectorResults.length,\n duration: Date.now() - start,\n };\n }\n\n /**\n * Get context for a subject using RAG + knowledge indexer.\n * This is the primary retrieval method — combines structured\n * context from the indexer with chunk-level RAG results.\n */\n async getContext(\n query: string,\n indexer?: KnowledgeIndexer,\n options?: {\n collections?: CollectionName[];\n repo?: string;\n maxTokens?: number;\n }\n ): Promise<{ content: string; sources: string[]; tokenEstimate: number }> {\n const maxTokens = options?.maxTokens ?? 4000;\n\n // RAG search for chunk-level results\n const ragResult = await this.search(query, {\n collections: options?.collections,\n repo: options?.repo,\n limit: 15,\n });\n\n // Build context from top chunks within token budget\n const parts: string[] = [];\n const sources: string[] = [];\n let tokenCount = 0;\n\n for (const { chunk, score } of ragResult.chunks) {\n if (tokenCount + chunk.tokenEstimate > maxTokens) break;\n\n parts.push(`<!-- source: ${chunk.source} (${chunk.collection}, score: ${score.toFixed(2)}) -->`);\n parts.push(chunk.content);\n parts.push('');\n\n if (!sources.includes(chunk.source)) {\n sources.push(chunk.source);\n }\n tokenCount += chunk.tokenEstimate;\n }\n\n return {\n content: parts.join('\\n'),\n sources,\n tokenEstimate: tokenCount,\n };\n }\n\n // ─── ChromaDB Initialization ─────────────────────────────────────────────\n\n private async initChroma(errors: string[]): Promise<void> {\n if (!this.config.openaiApiKey) return;\n\n let chromadb: Record<string, unknown>;\n try {\n chromadb = (await import('chromadb' as string)) as Record<string, unknown>;\n } catch {\n // chromadb not installed — BM25-only mode\n return;\n }\n\n try {\n const ChromaClientCtor = chromadb.ChromaClient as new (config: { path: string }) => ChromaClient;\n const client = new ChromaClientCtor({\n path: `http://${this.config.chromaHost}:${this.config.chromaPort}`,\n });\n\n // Create one collection per RAG collection type\n const collectionNames: CollectionName[] = [\n 'codebase', 'protocols', 'decisions', 'errors', 'compliance', 'domain',\n ];\n\n for (const name of collectionNames) {\n const collection = await client.getOrCreateCollection({\n name: `baseline-${name}`,\n metadata: { source: 'baselineos-rag', collection: name },\n });\n this.chromaCollections.set(name, collection);\n }\n\n // Batch-add chunks to their respective collections\n for (const name of collectionNames) {\n const collection = this.chromaCollections.get(name);\n if (!collection) continue;\n\n const collectionChunks = Array.from(this.chunks.values()).filter(\n (c) => c.collection === name\n );\n\n if (collectionChunks.length === 0) continue;\n\n // Check if collection already has data (skip re-ingestion)\n const existingCount = await collection.count();\n if (existingCount >= collectionChunks.length) continue;\n\n // Batch in groups of 100 to avoid oversized requests\n const batchSize = 100;\n for (let i = 0; i < collectionChunks.length; i += batchSize) {\n const batch = collectionChunks.slice(i, i + batchSize);\n await collection.add({\n ids: batch.map((c) => c.id),\n documents: batch.map((c) => c.content),\n metadatas: batch.map((c) => ({\n source: c.source,\n repo: c.repo,\n section: c.section ?? '',\n hash: c.hash,\n collection: c.collection,\n })),\n });\n }\n }\n\n this.chromaAvailable = true;\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n errors.push(`ChromaDB init failed (BM25-only mode): ${msg}`);\n }\n }\n\n // ─── Vector Search ──────────────────────────────────────────────────────\n\n private async vectorSearch(\n query: string,\n options?: {\n collections?: CollectionName[];\n repo?: string;\n limit?: number;\n }\n ): Promise<Array<{ chunk: RAGChunk; score: number; matchType: 'vector' }>> {\n if (!this.chromaAvailable || !this.config.openaiApiKey) return [];\n\n const limit = options?.limit ?? this.config.vectorLimit;\n const targetCollections = options?.collections ?? [\n 'codebase', 'protocols', 'decisions', 'errors', 'compliance', 'domain',\n ] as CollectionName[];\n\n const results: Array<{ chunk: RAGChunk; score: number; matchType: 'vector' }> = [];\n\n for (const collName of targetCollections) {\n const collection = this.chromaCollections.get(collName);\n if (!collection) continue;\n\n try {\n const where = options?.repo ? { repo: options.repo } : undefined;\n\n const queryResult = await collection.query({\n queryTexts: [query],\n nResults: limit,\n where: where as Record<string, unknown> | undefined,\n include: ['documents', 'metadatas', 'distances'],\n });\n\n const ids = queryResult.ids[0] ?? [];\n const distances = queryResult.distances[0] ?? [];\n\n for (let i = 0; i < ids.length; i++) {\n const chunkId = ids[i];\n const chunk = this.chunks.get(chunkId);\n if (!chunk) continue;\n\n // ChromaDB distances are L2 — convert to 0-1 similarity\n // Lower distance = more similar. score = 1 / (1 + distance)\n const distance = distances[i] ?? 1;\n const score = 1 / (1 + distance);\n\n results.push({ chunk, score, matchType: 'vector' });\n }\n } catch (err) {\n process.stderr.write(`[baseline:rag] Vector query failed: ${(err as Error).message}\\n`);\n }\n }\n\n // Sort by score descending, limit\n return results.sort((a, b) => b.score - a.score).slice(0, limit);\n }\n\n // ─── BM25 Search ─────────────────────────────────────────────────────────\n\n private bm25Search(\n query: string,\n options?: {\n collections?: CollectionName[];\n repo?: string;\n limit?: number;\n }\n ): Array<{ chunk: RAGChunk; score: number; matchType: 'bm25' }> {\n if (!this.initialized || this.chunks.size === 0) return [];\n\n const limit = options?.limit ?? this.config.bm25Limit;\n\n let results = this.searchIndex.search(query).slice(0, limit * 3);\n\n // Filter by collection\n if (options?.collections?.length) {\n results = results.filter((r) =>\n options.collections!.includes(r.collection as CollectionName)\n );\n }\n\n // Filter by repo\n if (options?.repo) {\n results = results.filter((r) => r.repo === options.repo);\n }\n\n return results\n .slice(0, limit)\n .map((r) => ({\n chunk: this.chunks.get(r.id as string)!,\n score: r.score / (results[0]?.score ?? 1), // Normalize to 0-1\n matchType: 'bm25' as const,\n }))\n .filter((r) => r.chunk != null);\n }\n\n // ─── Index Management ─────────────────────────────────────────────────────\n\n private rebuildSearchIndex(): void {\n // MiniSearch doesn't support clearing, so create a new instance\n this.searchIndex = new MiniSearch({\n fields: ['content', 'source', 'section', 'repo'],\n storeFields: ['id', 'collection', 'source', 'repo', 'section'],\n searchOptions: {\n boost: { content: 2, section: 1.5, source: 1 },\n fuzzy: 0.2,\n prefix: true,\n },\n });\n\n const docs = Array.from(this.chunks.values()).map((chunk) => ({\n id: chunk.id,\n content: chunk.content,\n source: chunk.source,\n section: chunk.section ?? '',\n repo: chunk.repo,\n collection: chunk.collection,\n }));\n\n this.searchIndex.addAll(docs);\n }\n\n // ─── File Resolution ──────────────────────────────────────────────────────\n\n private resolveFiles(patterns: string[]): string[] {\n const files: Set<string> = new Set();\n\n for (const pattern of patterns) {\n // Simple glob resolution (no external dep needed for basic patterns)\n if (pattern.includes('**')) {\n this.walkGlob(this.config.projectRoot, pattern, files);\n } else {\n const fullPath = join(this.config.projectRoot, pattern);\n if (existsSync(fullPath)) {\n files.add(fullPath);\n }\n }\n }\n\n return Array.from(files);\n }\n\n private walkGlob(root: string, pattern: string, results: Set<string>): void {\n // Split pattern into directory prefix and file pattern\n const parts = pattern.split('**/');\n const prefix = parts[0] ?? '';\n const suffix = parts.slice(1).join('**/');\n const startDir = prefix ? join(root, prefix) : root;\n\n if (!existsSync(startDir)) return;\n\n this.walkDir(startDir, suffix, results);\n }\n\n private walkDir(dir: string, filePattern: string, results: Set<string>): void {\n try {\n const entries = readdirSync(dir);\n for (const entry of entries) {\n // Skip hidden dirs and node_modules\n if (entry.startsWith('.') || entry === 'node_modules' || entry === 'dist' || entry === 'coverage') {\n continue;\n }\n\n const fullPath = join(dir, entry);\n try {\n const stat = statSync(fullPath);\n if (stat.isDirectory()) {\n this.walkDir(fullPath, filePattern, results);\n } else if (stat.isFile() && this.matchesFilePattern(entry, fullPath, filePattern)) {\n results.add(fullPath);\n }\n } catch {\n // Skip unreadable files\n }\n }\n } catch {\n // Skip unreadable directories\n }\n }\n\n private matchesFilePattern(filename: string, fullPath: string, pattern: string): boolean {\n if (!pattern) return true;\n\n // Handle patterns like \"*.md\", \"SPEC.md\", \"*.ts\"\n if (pattern.startsWith('*')) {\n const ext = pattern.slice(1);\n return filename.endsWith(ext);\n }\n\n // Exact filename match\n if (!pattern.includes('/') && !pattern.includes('*')) {\n return filename === pattern;\n }\n\n // Path suffix match\n return fullPath.endsWith(pattern.replace(/\\*/g, ''));\n }\n\n // ─── Helpers ──────────────────────────────────────────────────────────────\n\n private extractRepo(relPath: string): string {\n const parts = relPath.split('/');\n return parts[0] ?? 'unknown';\n }\n\n private extractSection(content: string): string | undefined {\n // Extract first heading from content\n const match = content.match(/^#+\\s+(.+)/m);\n return match?.[1]?.trim();\n }\n\n // ─── Stats ────────────────────────────────────────────────────────────────\n\n getStats(): {\n totalChunks: number;\n collections: Record<CollectionName, number>;\n initialized: boolean;\n vectorStoreAvailable: boolean;\n } {\n const collections: Record<CollectionName, number> = {\n codebase: 0,\n protocols: 0,\n decisions: 0,\n errors: 0,\n compliance: 0,\n domain: 0,\n };\n\n for (const chunk of this.chunks.values()) {\n collections[chunk.collection]++;\n }\n\n return {\n totalChunks: this.chunks.size,\n collections,\n initialized: this.initialized,\n vectorStoreAvailable: this.chromaAvailable,\n };\n }\n}\n"]}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { TaskPriority } from './types.js';
|
|
2
|
+
import 'zod';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Task Queue — SIGNAL-018
|
|
6
|
+
*
|
|
7
|
+
* BullMQ-backed durable task queue. Provides:
|
|
8
|
+
* - Priority-ordered execution (critical → high → medium → low)
|
|
9
|
+
* - Automatic retry with exponential backoff on failure
|
|
10
|
+
* - Concurrency limiting (configurable, default 5)
|
|
11
|
+
* - Job deduplication by task ID
|
|
12
|
+
* - Queue observability via Bull Dashboard / BullMQ Board
|
|
13
|
+
*
|
|
14
|
+
* The queue is optional. When not configured, Orchestrator falls back
|
|
15
|
+
* to direct in-process execution (existing behaviour).
|
|
16
|
+
*
|
|
17
|
+
* Configuration:
|
|
18
|
+
* BASELINE_REDIS_URL Redis connection URL (default: redis://localhost:6379)
|
|
19
|
+
*
|
|
20
|
+
* Self-hosted Redis:
|
|
21
|
+
* docker compose -f docker/docker-compose.monitoring.yml up -d
|
|
22
|
+
* → redis://localhost:6379
|
|
23
|
+
*
|
|
24
|
+
* @license Apache-2.0
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
interface TaskQueueConfig {
|
|
28
|
+
/** Redis URL. Default: BASELINE_REDIS_URL env or redis://localhost:6379 */
|
|
29
|
+
redisUrl?: string;
|
|
30
|
+
/** Queue name (default: 'baseline-tasks') */
|
|
31
|
+
queueName?: string;
|
|
32
|
+
/** Max concurrent jobs processed in parallel (default: 5) */
|
|
33
|
+
concurrency?: number;
|
|
34
|
+
/** Max retry attempts on failure (default: 3) */
|
|
35
|
+
attempts?: number;
|
|
36
|
+
/** Initial backoff delay in ms for exponential retry (default: 2000) */
|
|
37
|
+
backoffDelay?: number;
|
|
38
|
+
}
|
|
39
|
+
declare class TaskQueue {
|
|
40
|
+
private readonly queue;
|
|
41
|
+
private readonly worker;
|
|
42
|
+
private readonly queueName;
|
|
43
|
+
/**
|
|
44
|
+
* @param executor Called by the worker when a job is dequeued.
|
|
45
|
+
* Receives the taskId. Should call Orchestrator.dispatchExecution().
|
|
46
|
+
* @param config Queue configuration.
|
|
47
|
+
*/
|
|
48
|
+
constructor(executor: (taskId: string) => Promise<void>, config?: TaskQueueConfig);
|
|
49
|
+
/**
|
|
50
|
+
* Enqueue a task for execution.
|
|
51
|
+
* Uses taskId as jobId — duplicate enqueues are silently ignored.
|
|
52
|
+
*/
|
|
53
|
+
enqueue(taskId: string, priority: TaskPriority): Promise<void>;
|
|
54
|
+
/**
|
|
55
|
+
* Queue statistics — useful for health checks and dashboards.
|
|
56
|
+
*/
|
|
57
|
+
getStats(): Promise<{
|
|
58
|
+
waiting: number;
|
|
59
|
+
active: number;
|
|
60
|
+
completed: number;
|
|
61
|
+
failed: number;
|
|
62
|
+
delayed: number;
|
|
63
|
+
}>;
|
|
64
|
+
/** Gracefully close queue and worker connections. */
|
|
65
|
+
close(): Promise<void>;
|
|
66
|
+
private parseRedisUrl;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export { TaskQueue, type TaskQueueConfig };
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { Queue, Worker } from 'bullmq';
|
|
2
|
+
|
|
3
|
+
// src/core/task-queue.ts
|
|
4
|
+
var PRIORITY_MAP = {
|
|
5
|
+
critical: 1,
|
|
6
|
+
high: 2,
|
|
7
|
+
medium: 5,
|
|
8
|
+
low: 10
|
|
9
|
+
};
|
|
10
|
+
var TaskQueue = class {
|
|
11
|
+
queue;
|
|
12
|
+
worker;
|
|
13
|
+
queueName;
|
|
14
|
+
/**
|
|
15
|
+
* @param executor Called by the worker when a job is dequeued.
|
|
16
|
+
* Receives the taskId. Should call Orchestrator.dispatchExecution().
|
|
17
|
+
* @param config Queue configuration.
|
|
18
|
+
*/
|
|
19
|
+
constructor(executor, config = {}) {
|
|
20
|
+
this.queueName = config.queueName ?? "baseline-tasks";
|
|
21
|
+
const redisUrl = config.redisUrl ?? process.env["BASELINE_REDIS_URL"] ?? "redis://localhost:6379";
|
|
22
|
+
const connection = this.parseRedisUrl(redisUrl);
|
|
23
|
+
const attempts = config.attempts ?? 3;
|
|
24
|
+
const backoffDelay = config.backoffDelay ?? 2e3;
|
|
25
|
+
this.queue = new Queue(this.queueName, {
|
|
26
|
+
connection,
|
|
27
|
+
defaultJobOptions: {
|
|
28
|
+
attempts,
|
|
29
|
+
backoff: { type: "exponential", delay: backoffDelay },
|
|
30
|
+
removeOnComplete: { count: 500 },
|
|
31
|
+
removeOnFail: { count: 200 }
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
this.worker = new Worker(
|
|
35
|
+
this.queueName,
|
|
36
|
+
async (job) => {
|
|
37
|
+
await executor(job.data.taskId);
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
connection,
|
|
41
|
+
concurrency: config.concurrency ?? 5
|
|
42
|
+
}
|
|
43
|
+
);
|
|
44
|
+
this.worker.on("failed", (job, err) => {
|
|
45
|
+
console.warn(`[TaskQueue] Job ${job?.id ?? "?"} (task ${job?.data.taskId ?? "?"}) failed: ${err.message}`);
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Enqueue a task for execution.
|
|
50
|
+
* Uses taskId as jobId — duplicate enqueues are silently ignored.
|
|
51
|
+
*/
|
|
52
|
+
async enqueue(taskId, priority) {
|
|
53
|
+
await this.queue.add(
|
|
54
|
+
"execute",
|
|
55
|
+
{ taskId },
|
|
56
|
+
{
|
|
57
|
+
jobId: taskId,
|
|
58
|
+
priority: PRIORITY_MAP[priority]
|
|
59
|
+
}
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Queue statistics — useful for health checks and dashboards.
|
|
64
|
+
*/
|
|
65
|
+
async getStats() {
|
|
66
|
+
const [waiting, active, completed, failed, delayed] = await Promise.all([
|
|
67
|
+
this.queue.getWaitingCount(),
|
|
68
|
+
this.queue.getActiveCount(),
|
|
69
|
+
this.queue.getCompletedCount(),
|
|
70
|
+
this.queue.getFailedCount(),
|
|
71
|
+
this.queue.getDelayedCount()
|
|
72
|
+
]);
|
|
73
|
+
return { waiting, active, completed, failed, delayed };
|
|
74
|
+
}
|
|
75
|
+
/** Gracefully close queue and worker connections. */
|
|
76
|
+
async close() {
|
|
77
|
+
await this.worker.close();
|
|
78
|
+
await this.queue.close();
|
|
79
|
+
}
|
|
80
|
+
// ─── Internal ──────────────────────────────────────────────────────────────
|
|
81
|
+
parseRedisUrl(url) {
|
|
82
|
+
try {
|
|
83
|
+
const u = new URL(url);
|
|
84
|
+
const config = {
|
|
85
|
+
host: u.hostname || "localhost",
|
|
86
|
+
port: u.port ? parseInt(u.port, 10) : 6379,
|
|
87
|
+
username: u.username || void 0,
|
|
88
|
+
password: u.password || void 0
|
|
89
|
+
};
|
|
90
|
+
if (u.pathname && u.pathname !== "/") {
|
|
91
|
+
config["db"] = parseInt(u.pathname.slice(1), 10);
|
|
92
|
+
}
|
|
93
|
+
return config;
|
|
94
|
+
} catch {
|
|
95
|
+
return { host: "localhost", port: 6379 };
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
/**
|
|
100
|
+
* Task Queue — SIGNAL-018
|
|
101
|
+
*
|
|
102
|
+
* BullMQ-backed durable task queue. Provides:
|
|
103
|
+
* - Priority-ordered execution (critical → high → medium → low)
|
|
104
|
+
* - Automatic retry with exponential backoff on failure
|
|
105
|
+
* - Concurrency limiting (configurable, default 5)
|
|
106
|
+
* - Job deduplication by task ID
|
|
107
|
+
* - Queue observability via Bull Dashboard / BullMQ Board
|
|
108
|
+
*
|
|
109
|
+
* The queue is optional. When not configured, Orchestrator falls back
|
|
110
|
+
* to direct in-process execution (existing behaviour).
|
|
111
|
+
*
|
|
112
|
+
* Configuration:
|
|
113
|
+
* BASELINE_REDIS_URL Redis connection URL (default: redis://localhost:6379)
|
|
114
|
+
*
|
|
115
|
+
* Self-hosted Redis:
|
|
116
|
+
* docker compose -f docker/docker-compose.monitoring.yml up -d
|
|
117
|
+
* → redis://localhost:6379
|
|
118
|
+
*
|
|
119
|
+
* @license Apache-2.0
|
|
120
|
+
*/
|
|
121
|
+
|
|
122
|
+
export { TaskQueue };
|
|
123
|
+
//# sourceMappingURL=task-queue.js.map
|
|
124
|
+
//# sourceMappingURL=task-queue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/core/task-queue.ts"],"names":[],"mappings":"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","file":"task-queue.js","sourcesContent":["/**\n * Task Queue — SIGNAL-018\n *\n * BullMQ-backed durable task queue. Provides:\n * - Priority-ordered execution (critical → high → medium → low)\n * - Automatic retry with exponential backoff on failure\n * - Concurrency limiting (configurable, default 5)\n * - Job deduplication by task ID\n * - Queue observability via Bull Dashboard / BullMQ Board\n *\n * The queue is optional. When not configured, Orchestrator falls back\n * to direct in-process execution (existing behaviour).\n *\n * Configuration:\n * BASELINE_REDIS_URL Redis connection URL (default: redis://localhost:6379)\n *\n * Self-hosted Redis:\n * docker compose -f docker/docker-compose.monitoring.yml up -d\n * → redis://localhost:6379\n *\n * @license Apache-2.0\n */\n\nimport { Queue, Worker, type ConnectionOptions } from 'bullmq';\nimport type { TaskPriority } from './types.js';\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport interface TaskQueueConfig {\n /** Redis URL. Default: BASELINE_REDIS_URL env or redis://localhost:6379 */\n redisUrl?: string;\n /** Queue name (default: 'baseline-tasks') */\n queueName?: string;\n /** Max concurrent jobs processed in parallel (default: 5) */\n concurrency?: number;\n /** Max retry attempts on failure (default: 3) */\n attempts?: number;\n /** Initial backoff delay in ms for exponential retry (default: 2000) */\n backoffDelay?: number;\n}\n\ninterface TaskJobData {\n taskId: string;\n}\n\n/** Maps task priority to BullMQ integer priority (lower = higher priority) */\nconst PRIORITY_MAP: Record<TaskPriority, number> = {\n critical: 1,\n high: 2,\n medium: 5,\n low: 10,\n};\n\n// ─── TaskQueue ────────────────────────────────────────────────────────────────\n\nexport class TaskQueue {\n private readonly queue: Queue<TaskJobData>;\n private readonly worker: Worker<TaskJobData>;\n private readonly queueName: string;\n\n /**\n * @param executor Called by the worker when a job is dequeued.\n * Receives the taskId. Should call Orchestrator.dispatchExecution().\n * @param config Queue configuration.\n */\n constructor(\n executor: (taskId: string) => Promise<void>,\n config: TaskQueueConfig = {},\n ) {\n this.queueName = config.queueName ?? 'baseline-tasks';\n\n const redisUrl = config.redisUrl ?? process.env['BASELINE_REDIS_URL'] ?? 'redis://localhost:6379';\n const connection: ConnectionOptions = this.parseRedisUrl(redisUrl);\n\n const attempts = config.attempts ?? 3;\n const backoffDelay = config.backoffDelay ?? 2_000;\n\n this.queue = new Queue<TaskJobData>(this.queueName, {\n connection,\n defaultJobOptions: {\n attempts,\n backoff: { type: 'exponential', delay: backoffDelay },\n removeOnComplete: { count: 500 },\n removeOnFail: { count: 200 },\n },\n });\n\n this.worker = new Worker<TaskJobData>(\n this.queueName,\n async (job) => { await executor(job.data.taskId); },\n {\n connection,\n concurrency: config.concurrency ?? 5,\n },\n );\n\n this.worker.on('failed', (job, err) => {\n console.warn(`[TaskQueue] Job ${job?.id ?? '?'} (task ${job?.data.taskId ?? '?'}) failed: ${err.message}`);\n });\n }\n\n /**\n * Enqueue a task for execution.\n * Uses taskId as jobId — duplicate enqueues are silently ignored.\n */\n async enqueue(taskId: string, priority: TaskPriority): Promise<void> {\n await this.queue.add(\n 'execute',\n { taskId },\n {\n jobId: taskId,\n priority: PRIORITY_MAP[priority],\n },\n );\n }\n\n /**\n * Queue statistics — useful for health checks and dashboards.\n */\n async getStats(): Promise<{\n waiting: number;\n active: number;\n completed: number;\n failed: number;\n delayed: number;\n }> {\n const [waiting, active, completed, failed, delayed] = await Promise.all([\n this.queue.getWaitingCount(),\n this.queue.getActiveCount(),\n this.queue.getCompletedCount(),\n this.queue.getFailedCount(),\n this.queue.getDelayedCount(),\n ]);\n return { waiting, active, completed, failed, delayed };\n }\n\n /** Gracefully close queue and worker connections. */\n async close(): Promise<void> {\n await this.worker.close();\n await this.queue.close();\n }\n\n // ─── Internal ──────────────────────────────────────────────────────────────\n\n private parseRedisUrl(url: string): ConnectionOptions {\n try {\n const u = new URL(url);\n const config: ConnectionOptions = {\n host: u.hostname || 'localhost',\n port: u.port ? parseInt(u.port, 10) : 6379,\n username: u.username || undefined,\n password: u.password || undefined,\n };\n if (u.pathname && u.pathname !== '/') {\n (config as Record<string, unknown>)['db'] = parseInt(u.pathname.slice(1), 10);\n }\n return config;\n } catch {\n return { host: 'localhost', port: 6379 };\n }\n }\n}\n"]}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { Context, Tracer, Span } from '@opentelemetry/api';
|
|
2
|
+
export { Context, Span, SpanKind, SpanStatusCode, Tracer } from '@opentelemetry/api';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* BaselineOS OpenTelemetry — SIGNAL-012
|
|
6
|
+
*
|
|
7
|
+
* Distributed tracing for the multi-agent orchestration layer.
|
|
8
|
+
* Initializes the OTel Node SDK and exports a shared tracer.
|
|
9
|
+
*
|
|
10
|
+
* Spans emitted:
|
|
11
|
+
* task.execute root span per executeTask() call
|
|
12
|
+
* task.policy.pre OPA pre-execution gate
|
|
13
|
+
* task.policy.post OPA post-execution gate
|
|
14
|
+
* task.perform performExecution() step loop
|
|
15
|
+
* task.self_verify selfVerify() LLM call
|
|
16
|
+
* task.review conductReview() LLM call
|
|
17
|
+
*
|
|
18
|
+
* Trace context is propagated through AgentBus messages via the
|
|
19
|
+
* optional `traceContext` field (W3C traceparent format).
|
|
20
|
+
*
|
|
21
|
+
* Configuration:
|
|
22
|
+
* OTEL_EXPORTER_OTLP_ENDPOINT OTLP HTTP collector (default: http://localhost:4318)
|
|
23
|
+
* OTEL_SERVICE_NAME service name override (default: baselineos)
|
|
24
|
+
* BASELINE_OTEL_DISABLED set to '1' to disable entirely (e.g. in tests)
|
|
25
|
+
*
|
|
26
|
+
* @license Apache-2.0
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Initialize the OTel SDK. Safe to call multiple times — subsequent calls
|
|
31
|
+
* are no-ops. Should be called once at process startup before any spans.
|
|
32
|
+
*
|
|
33
|
+
* Silently disabled when BASELINE_OTEL_DISABLED=1.
|
|
34
|
+
*/
|
|
35
|
+
declare function initTelemetry(): void;
|
|
36
|
+
/** Gracefully shut down the SDK and flush pending spans. */
|
|
37
|
+
declare function shutdownTelemetry(): Promise<void>;
|
|
38
|
+
declare const tracer: Tracer;
|
|
39
|
+
/**
|
|
40
|
+
* Serialize the active W3C trace context to a string for propagation
|
|
41
|
+
* through AgentBus messages.
|
|
42
|
+
* Returns undefined when there is no active span.
|
|
43
|
+
*/
|
|
44
|
+
declare function extractTraceContext(): string | undefined;
|
|
45
|
+
/**
|
|
46
|
+
* Restore a serialized trace context as the active context.
|
|
47
|
+
* Use as the second argument to `context.with()` before creating child spans.
|
|
48
|
+
*/
|
|
49
|
+
declare function injectTraceContext(traceparent: string): Context;
|
|
50
|
+
/**
|
|
51
|
+
* Run an async function inside a named span.
|
|
52
|
+
* Sets ERROR status and records exception on throw.
|
|
53
|
+
*/
|
|
54
|
+
declare function withSpan<T>(name: string, attributes: Record<string, string | number | boolean>, fn: (span: Span) => Promise<T>): Promise<T>;
|
|
55
|
+
|
|
56
|
+
export { extractTraceContext, initTelemetry, injectTraceContext, shutdownTelemetry, tracer, withSpan };
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { NodeSDK } from '@opentelemetry/sdk-node';
|
|
2
|
+
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
|
|
3
|
+
import { resourceFromAttributes } from '@opentelemetry/resources';
|
|
4
|
+
import { ATTR_SERVICE_VERSION, ATTR_SERVICE_NAME } from '@opentelemetry/semantic-conventions';
|
|
5
|
+
import { trace, propagation, context, SpanKind, SpanStatusCode } from '@opentelemetry/api';
|
|
6
|
+
export { SpanKind, SpanStatusCode } from '@opentelemetry/api';
|
|
7
|
+
|
|
8
|
+
// src/core/telemetry.ts
|
|
9
|
+
var _sdk = null;
|
|
10
|
+
function initTelemetry() {
|
|
11
|
+
if (_sdk || process.env["BASELINE_OTEL_DISABLED"] === "1") return;
|
|
12
|
+
const endpoint = process.env["OTEL_EXPORTER_OTLP_ENDPOINT"] ?? "http://localhost:4318";
|
|
13
|
+
const exporter = new OTLPTraceExporter({
|
|
14
|
+
url: `${endpoint}/v1/traces`
|
|
15
|
+
});
|
|
16
|
+
_sdk = new NodeSDK({
|
|
17
|
+
resource: resourceFromAttributes({
|
|
18
|
+
[ATTR_SERVICE_NAME]: process.env["OTEL_SERVICE_NAME"] ?? "baselineos",
|
|
19
|
+
[ATTR_SERVICE_VERSION]: "0.2.0-beta.1"
|
|
20
|
+
}),
|
|
21
|
+
traceExporter: exporter
|
|
22
|
+
// W3C traceparent propagation is the default — no explicit config needed
|
|
23
|
+
});
|
|
24
|
+
_sdk.start();
|
|
25
|
+
process.on("SIGTERM", () => {
|
|
26
|
+
_sdk?.shutdown().catch(() => {
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
process.on("SIGINT", () => {
|
|
30
|
+
_sdk?.shutdown().catch(() => {
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
async function shutdownTelemetry() {
|
|
35
|
+
await _sdk?.shutdown();
|
|
36
|
+
_sdk = null;
|
|
37
|
+
}
|
|
38
|
+
var tracer = new Proxy({}, {
|
|
39
|
+
get(_target, prop) {
|
|
40
|
+
return trace.getTracer("baselineos", "0.2.0-beta.1")[prop];
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
function extractTraceContext() {
|
|
44
|
+
const carrier = {};
|
|
45
|
+
propagation.inject(context.active(), carrier);
|
|
46
|
+
return carrier["traceparent"];
|
|
47
|
+
}
|
|
48
|
+
function injectTraceContext(traceparent) {
|
|
49
|
+
const carrier = { traceparent };
|
|
50
|
+
return propagation.extract(context.active(), carrier);
|
|
51
|
+
}
|
|
52
|
+
async function withSpan(name, attributes, fn) {
|
|
53
|
+
return tracer.startActiveSpan(name, { kind: SpanKind.INTERNAL, attributes }, async (span) => {
|
|
54
|
+
try {
|
|
55
|
+
const result = await fn(span);
|
|
56
|
+
span.setStatus({ code: SpanStatusCode.OK });
|
|
57
|
+
return result;
|
|
58
|
+
} catch (err) {
|
|
59
|
+
span.recordException(err);
|
|
60
|
+
span.setStatus({ code: SpanStatusCode.ERROR, message: err.message });
|
|
61
|
+
throw err;
|
|
62
|
+
} finally {
|
|
63
|
+
span.end();
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* BaselineOS OpenTelemetry — SIGNAL-012
|
|
69
|
+
*
|
|
70
|
+
* Distributed tracing for the multi-agent orchestration layer.
|
|
71
|
+
* Initializes the OTel Node SDK and exports a shared tracer.
|
|
72
|
+
*
|
|
73
|
+
* Spans emitted:
|
|
74
|
+
* task.execute root span per executeTask() call
|
|
75
|
+
* task.policy.pre OPA pre-execution gate
|
|
76
|
+
* task.policy.post OPA post-execution gate
|
|
77
|
+
* task.perform performExecution() step loop
|
|
78
|
+
* task.self_verify selfVerify() LLM call
|
|
79
|
+
* task.review conductReview() LLM call
|
|
80
|
+
*
|
|
81
|
+
* Trace context is propagated through AgentBus messages via the
|
|
82
|
+
* optional `traceContext` field (W3C traceparent format).
|
|
83
|
+
*
|
|
84
|
+
* Configuration:
|
|
85
|
+
* OTEL_EXPORTER_OTLP_ENDPOINT OTLP HTTP collector (default: http://localhost:4318)
|
|
86
|
+
* OTEL_SERVICE_NAME service name override (default: baselineos)
|
|
87
|
+
* BASELINE_OTEL_DISABLED set to '1' to disable entirely (e.g. in tests)
|
|
88
|
+
*
|
|
89
|
+
* @license Apache-2.0
|
|
90
|
+
*/
|
|
91
|
+
|
|
92
|
+
export { extractTraceContext, initTelemetry, injectTraceContext, shutdownTelemetry, tracer, withSpan };
|
|
93
|
+
//# sourceMappingURL=telemetry.js.map
|
|
94
|
+
//# sourceMappingURL=telemetry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/core/telemetry.ts"],"names":[],"mappings":";;;;;;;;AAqCA,IAAI,IAAA,GAAuB,IAAA;AAQpB,SAAS,aAAA,GAAsB;AACpC,EAAA,IAAI,IAAA,IAAQ,OAAA,CAAQ,GAAA,CAAI,wBAAwB,MAAM,GAAA,EAAK;AAE3D,EAAA,MAAM,QAAA,GACJ,OAAA,CAAQ,GAAA,CAAI,6BAA6B,CAAA,IAAK,uBAAA;AAEhD,EAAA,MAAM,QAAA,GAAW,IAAI,iBAAA,CAAkB;AAAA,IACrC,GAAA,EAAK,GAAG,QAAQ,CAAA,UAAA;AAAA,GACjB,CAAA;AAED,EAAA,IAAA,GAAO,IAAI,OAAA,CAAQ;AAAA,IACjB,UAAU,sBAAA,CAAuB;AAAA,MAC/B,CAAC,iBAAiB,GAAG,OAAA,CAAQ,GAAA,CAAI,mBAAmB,CAAA,IAAK,YAAA;AAAA,MACzD,CAAC,oBAAoB,GAAG;AAAA,KACzB,CAAA;AAAA,IACD,aAAA,EAAe;AAAA;AAAA,GAEhB,CAAA;AAED,EAAA,IAAA,CAAK,KAAA,EAAM;AAEX,EAAA,OAAA,CAAQ,EAAA,CAAG,WAAW,MAAM;AAAE,IAAA,IAAA,EAAM,QAAA,EAAS,CAAE,KAAA,CAAM,MAAM;AAAA,IAAC,CAAC,CAAA;AAAA,EAAG,CAAC,CAAA;AACjE,EAAA,OAAA,CAAQ,EAAA,CAAG,UAAW,MAAM;AAAE,IAAA,IAAA,EAAM,QAAA,EAAS,CAAE,KAAA,CAAM,MAAM;AAAA,IAAC,CAAC,CAAA;AAAA,EAAG,CAAC,CAAA;AACnE;AAGA,eAAsB,iBAAA,GAAmC;AACvD,EAAA,MAAM,MAAM,QAAA,EAAS;AACrB,EAAA,IAAA,GAAO,IAAA;AACT;AAIO,IAAM,MAAA,GAAiB,IAAI,KAAA,CAAM,EAAC,EAAa;AAAA,EACpD,GAAA,CAAI,SAAS,IAAA,EAAM;AACjB,IAAA,OAAO,KAAA,CAAM,SAAA,CAAU,YAAA,EAAc,cAAc,EAAE,IAAoB,CAAA;AAAA,EAC3E;AACF,CAAC;AASM,SAAS,mBAAA,GAA0C;AACxD,EAAA,MAAM,UAAkC,EAAC;AACzC,EAAA,WAAA,CAAY,MAAA,CAAO,OAAA,CAAQ,MAAA,EAAO,EAAG,OAAO,CAAA;AAC5C,EAAA,OAAO,QAAQ,aAAa,CAAA;AAC9B;AAMO,SAAS,mBAAmB,WAAA,EAA8B;AAC/D,EAAA,MAAM,OAAA,GAAkC,EAAE,WAAA,EAAY;AACtD,EAAA,OAAO,WAAA,CAAY,OAAA,CAAQ,OAAA,CAAQ,MAAA,IAAU,OAAO,CAAA;AACtD;AAQA,eAAsB,QAAA,CACpB,IAAA,EACA,UAAA,EACA,EAAA,EACY;AACZ,EAAA,OAAO,MAAA,CAAO,eAAA,CAAgB,IAAA,EAAM,EAAE,IAAA,EAAM,SAAS,QAAA,EAAU,UAAA,EAAW,EAAG,OAAO,IAAA,KAAS;AAC3F,IAAA,IAAI;AACF,MAAA,MAAM,MAAA,GAAS,MAAM,EAAA,CAAG,IAAI,CAAA;AAC5B,MAAA,IAAA,CAAK,SAAA,CAAU,EAAE,IAAA,EAAM,cAAA,CAAe,IAAI,CAAA;AAC1C,MAAA,OAAO,MAAA;AAAA,IACT,SAAS,GAAA,EAAK;AACZ,MAAA,IAAA,CAAK,gBAAgB,GAAY,CAAA;AACjC,MAAA,IAAA,CAAK,SAAA,CAAU,EAAE,IAAA,EAAM,cAAA,CAAe,OAAO,OAAA,EAAU,GAAA,CAAc,SAAS,CAAA;AAC9E,MAAA,MAAM,GAAA;AAAA,IACR,CAAA,SAAE;AACA,MAAA,IAAA,CAAK,GAAA,EAAI;AAAA,IACX;AAAA,EACF,CAAC,CAAA;AACH","file":"telemetry.js","sourcesContent":["/**\n * BaselineOS OpenTelemetry — SIGNAL-012\n *\n * Distributed tracing for the multi-agent orchestration layer.\n * Initializes the OTel Node SDK and exports a shared tracer.\n *\n * Spans emitted:\n * task.execute root span per executeTask() call\n * task.policy.pre OPA pre-execution gate\n * task.policy.post OPA post-execution gate\n * task.perform performExecution() step loop\n * task.self_verify selfVerify() LLM call\n * task.review conductReview() LLM call\n *\n * Trace context is propagated through AgentBus messages via the\n * optional `traceContext` field (W3C traceparent format).\n *\n * Configuration:\n * OTEL_EXPORTER_OTLP_ENDPOINT OTLP HTTP collector (default: http://localhost:4318)\n * OTEL_SERVICE_NAME service name override (default: baselineos)\n * BASELINE_OTEL_DISABLED set to '1' to disable entirely (e.g. in tests)\n *\n * @license Apache-2.0\n */\n\nimport { NodeSDK } from '@opentelemetry/sdk-node';\nimport { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';\nimport { resourceFromAttributes } from '@opentelemetry/resources';\nimport { ATTR_SERVICE_NAME, ATTR_SERVICE_VERSION } from '@opentelemetry/semantic-conventions';\nimport { trace, context, propagation, SpanStatusCode, SpanKind } from '@opentelemetry/api';\nimport type { Span, Tracer, Context } from '@opentelemetry/api';\n\nexport { SpanStatusCode, SpanKind };\nexport type { Span, Tracer, Context };\n\n// ─── SDK init ────────────────────────────────────────────────────────────────\n\nlet _sdk: NodeSDK | null = null;\n\n/**\n * Initialize the OTel SDK. Safe to call multiple times — subsequent calls\n * are no-ops. Should be called once at process startup before any spans.\n *\n * Silently disabled when BASELINE_OTEL_DISABLED=1.\n */\nexport function initTelemetry(): void {\n if (_sdk || process.env['BASELINE_OTEL_DISABLED'] === '1') return;\n\n const endpoint =\n process.env['OTEL_EXPORTER_OTLP_ENDPOINT'] ?? 'http://localhost:4318';\n\n const exporter = new OTLPTraceExporter({\n url: `${endpoint}/v1/traces`,\n });\n\n _sdk = new NodeSDK({\n resource: resourceFromAttributes({\n [ATTR_SERVICE_NAME]: process.env['OTEL_SERVICE_NAME'] ?? 'baselineos',\n [ATTR_SERVICE_VERSION]: '0.2.0-beta.1',\n }),\n traceExporter: exporter,\n // W3C traceparent propagation is the default — no explicit config needed\n });\n\n _sdk.start();\n\n process.on('SIGTERM', () => { _sdk?.shutdown().catch(() => {}); });\n process.on('SIGINT', () => { _sdk?.shutdown().catch(() => {}); });\n}\n\n/** Gracefully shut down the SDK and flush pending spans. */\nexport async function shutdownTelemetry(): Promise<void> {\n await _sdk?.shutdown();\n _sdk = null;\n}\n\n// ─── Tracer ───────────────────────────────────────────────────────────────────\n\nexport const tracer: Tracer = new Proxy({} as Tracer, {\n get(_target, prop) {\n return trace.getTracer('baselineos', '0.2.0-beta.1')[prop as keyof Tracer];\n },\n});\n\n// ─── Trace context helpers ───────────────────────────────────────────────────\n\n/**\n * Serialize the active W3C trace context to a string for propagation\n * through AgentBus messages.\n * Returns undefined when there is no active span.\n */\nexport function extractTraceContext(): string | undefined {\n const carrier: Record<string, string> = {};\n propagation.inject(context.active(), carrier);\n return carrier['traceparent'];\n}\n\n/**\n * Restore a serialized trace context as the active context.\n * Use as the second argument to `context.with()` before creating child spans.\n */\nexport function injectTraceContext(traceparent: string): Context {\n const carrier: Record<string, string> = { traceparent };\n return propagation.extract(context.active(), carrier);\n}\n\n// ─── Span wrapper ─────────────────────────────────────────────────────────────\n\n/**\n * Run an async function inside a named span.\n * Sets ERROR status and records exception on throw.\n */\nexport async function withSpan<T>(\n name: string,\n attributes: Record<string, string | number | boolean>,\n fn: (span: Span) => Promise<T>,\n): Promise<T> {\n return tracer.startActiveSpan(name, { kind: SpanKind.INTERNAL, attributes }, async (span) => {\n try {\n const result = await fn(span);\n span.setStatus({ code: SpanStatusCode.OK });\n return result;\n } catch (err) {\n span.recordException(err as Error);\n span.setStatus({ code: SpanStatusCode.ERROR, message: (err as Error).message });\n throw err;\n } finally {\n span.end();\n }\n });\n}\n"]}
|