agcel 1.0.4 → 2.0.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/README.md +40 -47
- package/dist/cli/commands/create.d.ts +4 -0
- package/dist/cli/commands/create.d.ts.map +1 -0
- package/dist/cli/commands/create.js +68 -0
- package/dist/cli/commands/create.js.map +1 -0
- package/dist/cli/commands/init.d.ts +3 -0
- package/dist/cli/commands/init.d.ts.map +1 -0
- package/dist/cli/commands/init.js +98 -0
- package/dist/cli/commands/init.js.map +1 -0
- package/dist/cli/commands/install.d.ts +3 -0
- package/dist/cli/commands/install.d.ts.map +1 -0
- package/dist/cli/commands/install.helpers.d.ts +15 -0
- package/dist/cli/commands/install.helpers.d.ts.map +1 -0
- package/dist/cli/commands/install.helpers.js +47 -0
- package/dist/cli/commands/install.helpers.js.map +1 -0
- package/dist/cli/commands/install.helpers.test.d.ts +2 -0
- package/dist/cli/commands/install.helpers.test.d.ts.map +1 -0
- package/dist/cli/commands/install.helpers.test.js +18 -0
- package/dist/cli/commands/install.helpers.test.js.map +1 -0
- package/dist/cli/commands/install.js +74 -0
- package/dist/cli/commands/install.js.map +1 -0
- package/dist/cli/commands/pulse.d.ts +4 -0
- package/dist/cli/commands/pulse.d.ts.map +1 -0
- package/dist/cli/commands/pulse.js +48 -0
- package/dist/cli/commands/pulse.js.map +1 -0
- package/dist/cli/commands/pulse.test.d.ts +2 -0
- package/dist/cli/commands/pulse.test.d.ts.map +1 -0
- package/dist/cli/commands/pulse.test.js +19 -0
- package/dist/cli/commands/pulse.test.js.map +1 -0
- package/dist/cli/commands/suggest.d.ts +3 -0
- package/dist/cli/commands/suggest.d.ts.map +1 -0
- package/dist/cli/commands/suggest.js +32 -0
- package/dist/cli/commands/suggest.js.map +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +20 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/research-hints.d.ts +5 -0
- package/dist/cli/research-hints.d.ts.map +1 -0
- package/dist/cli/research-hints.js +109 -0
- package/dist/cli/research-hints.js.map +1 -0
- package/dist/cli/research-hints.test.d.ts +2 -0
- package/dist/cli/research-hints.test.d.ts.map +1 -0
- package/dist/cli/research-hints.test.js +50 -0
- package/dist/cli/research-hints.test.js.map +1 -0
- package/dist/core/agcel-config.d.ts +27 -0
- package/dist/core/agcel-config.d.ts.map +1 -0
- package/dist/core/agcel-config.js +4 -0
- package/dist/core/agcel-config.js.map +1 -0
- package/dist/core/build-rag-engine.d.ts +12 -0
- package/dist/core/build-rag-engine.d.ts.map +1 -0
- package/dist/core/build-rag-engine.js +24 -0
- package/dist/core/build-rag-engine.js.map +1 -0
- package/dist/core/build-rag-engine.test.d.ts +2 -0
- package/dist/core/build-rag-engine.test.d.ts.map +1 -0
- package/dist/core/build-rag-engine.test.js +34 -0
- package/dist/core/build-rag-engine.test.js.map +1 -0
- package/dist/core/chroma-rag.d.ts +20 -0
- package/dist/core/chroma-rag.d.ts.map +1 -0
- package/dist/core/chroma-rag.js +108 -0
- package/dist/core/chroma-rag.js.map +1 -0
- package/dist/core/context-hygiene.d.ts +12 -0
- package/dist/core/context-hygiene.d.ts.map +1 -0
- package/dist/core/context-hygiene.js +43 -0
- package/dist/core/context-hygiene.js.map +1 -0
- package/dist/core/context-hygiene.test.d.ts +2 -0
- package/dist/core/context-hygiene.test.d.ts.map +1 -0
- package/dist/core/context-hygiene.test.js +18 -0
- package/dist/core/context-hygiene.test.js.map +1 -0
- package/dist/core/context.d.ts +44 -0
- package/dist/core/context.d.ts.map +1 -0
- package/dist/core/context.js +114 -0
- package/dist/core/context.js.map +1 -0
- package/dist/core/conversation-routing.d.ts +10 -0
- package/dist/core/conversation-routing.d.ts.map +1 -0
- package/dist/core/conversation-routing.js +27 -0
- package/dist/core/conversation-routing.js.map +1 -0
- package/dist/core/conversation-routing.test.d.ts +2 -0
- package/dist/core/conversation-routing.test.d.ts.map +1 -0
- package/dist/core/conversation-routing.test.js +29 -0
- package/dist/core/conversation-routing.test.js.map +1 -0
- package/dist/core/guardrails.d.ts +26 -0
- package/dist/core/guardrails.d.ts.map +1 -0
- package/dist/core/guardrails.js +76 -0
- package/dist/core/guardrails.js.map +1 -0
- package/dist/core/guardrails.test.d.ts +2 -0
- package/dist/core/guardrails.test.d.ts.map +1 -0
- package/dist/core/guardrails.test.js +35 -0
- package/dist/core/guardrails.test.js.map +1 -0
- package/dist/core/intent.d.ts +22 -0
- package/dist/core/intent.d.ts.map +1 -0
- package/dist/core/intent.js +79 -0
- package/dist/core/intent.js.map +1 -0
- package/dist/core/intent.test.d.ts +2 -0
- package/dist/core/intent.test.d.ts.map +1 -0
- package/dist/core/intent.test.js +43 -0
- package/dist/core/intent.test.js.map +1 -0
- package/dist/core/local-rag.d.ts +17 -0
- package/dist/core/local-rag.d.ts.map +1 -0
- package/dist/core/local-rag.js +74 -0
- package/dist/core/local-rag.js.map +1 -0
- package/dist/core/local-rag.test.d.ts +2 -0
- package/dist/core/local-rag.test.d.ts.map +1 -0
- package/dist/core/local-rag.test.js +47 -0
- package/dist/core/local-rag.test.js.map +1 -0
- package/dist/core/orchestrator.d.ts +47 -0
- package/dist/core/orchestrator.d.ts.map +1 -0
- package/dist/core/orchestrator.js +138 -0
- package/dist/core/orchestrator.js.map +1 -0
- package/dist/core/orchestrator.test.d.ts +2 -0
- package/dist/core/orchestrator.test.d.ts.map +1 -0
- package/dist/core/orchestrator.test.js +118 -0
- package/dist/core/orchestrator.test.js.map +1 -0
- package/dist/core/pulse.d.ts +13 -0
- package/dist/core/pulse.d.ts.map +1 -0
- package/dist/core/pulse.js +29 -0
- package/dist/core/pulse.js.map +1 -0
- package/dist/core/pulse.test.d.ts +2 -0
- package/dist/core/pulse.test.d.ts.map +1 -0
- package/dist/core/pulse.test.js +18 -0
- package/dist/core/pulse.test.js.map +1 -0
- package/dist/core/rag-metadata.d.ts +5 -0
- package/dist/core/rag-metadata.d.ts.map +1 -0
- package/dist/core/rag-metadata.js +13 -0
- package/dist/core/rag-metadata.js.map +1 -0
- package/dist/core/rag-metadata.test.d.ts +2 -0
- package/dist/core/rag-metadata.test.d.ts.map +1 -0
- package/dist/core/rag-metadata.test.js +12 -0
- package/dist/core/rag-metadata.test.js.map +1 -0
- package/dist/core/rag-types.d.ts +18 -0
- package/dist/core/rag-types.d.ts.map +1 -0
- package/dist/core/rag-types.js +2 -0
- package/dist/core/rag-types.js.map +1 -0
- package/dist/core/read-agcel-config.d.ts +3 -0
- package/dist/core/read-agcel-config.d.ts.map +1 -0
- package/dist/core/read-agcel-config.js +13 -0
- package/dist/core/read-agcel-config.js.map +1 -0
- package/dist/core/registry.d.ts +9 -0
- package/dist/core/registry.d.ts.map +1 -0
- package/dist/core/registry.js +75 -0
- package/dist/core/registry.js.map +1 -0
- package/dist/core/registry.test.d.ts +2 -0
- package/dist/core/registry.test.d.ts.map +1 -0
- package/dist/core/registry.test.js +128 -0
- package/dist/core/registry.test.js.map +1 -0
- package/dist/core/schema.d.ts +51 -0
- package/dist/core/schema.d.ts.map +1 -0
- package/dist/core/schema.js +31 -0
- package/dist/core/schema.js.map +1 -0
- package/dist/core/summarizer.d.ts +23 -0
- package/dist/core/summarizer.d.ts.map +1 -0
- package/dist/core/summarizer.js +66 -0
- package/dist/core/summarizer.js.map +1 -0
- package/dist/core/text-similarity.d.ts +7 -0
- package/dist/core/text-similarity.d.ts.map +1 -0
- package/dist/core/text-similarity.js +35 -0
- package/dist/core/text-similarity.js.map +1 -0
- package/dist/core/text-similarity.test.d.ts +2 -0
- package/dist/core/text-similarity.test.d.ts.map +1 -0
- package/dist/core/text-similarity.test.js +11 -0
- package/dist/core/text-similarity.test.js.map +1 -0
- package/dist/docs-site.test.d.ts +2 -0
- package/dist/docs-site.test.d.ts.map +1 -0
- package/dist/docs-site.test.js +19 -0
- package/dist/docs-site.test.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -39
- package/dist/index.js.map +1 -0
- package/dist/providers/adapter.d.ts +23 -0
- package/dist/providers/adapter.d.ts.map +1 -0
- package/dist/providers/adapter.js +9 -0
- package/dist/providers/adapter.js.map +1 -0
- package/dist/providers/chatgpt.d.ts +13 -0
- package/dist/providers/chatgpt.d.ts.map +1 -0
- package/dist/providers/chatgpt.js +33 -0
- package/dist/providers/chatgpt.js.map +1 -0
- package/dist/providers/chatgpt.test.d.ts +2 -0
- package/dist/providers/chatgpt.test.d.ts.map +1 -0
- package/dist/providers/chatgpt.test.js +23 -0
- package/dist/providers/chatgpt.test.js.map +1 -0
- package/dist/providers/claude.d.ts +13 -0
- package/dist/providers/claude.d.ts.map +1 -0
- package/dist/providers/claude.js +48 -0
- package/dist/providers/claude.js.map +1 -0
- package/dist/providers/cursor.d.ts +14 -0
- package/dist/providers/cursor.d.ts.map +1 -0
- package/dist/providers/cursor.js +47 -0
- package/dist/providers/cursor.js.map +1 -0
- package/dist/providers/gemini.d.ts +13 -0
- package/dist/providers/gemini.d.ts.map +1 -0
- package/dist/providers/gemini.js +42 -0
- package/dist/providers/gemini.js.map +1 -0
- package/dist/providers/index.d.ts +5 -0
- package/dist/providers/index.d.ts.map +1 -0
- package/dist/providers/index.js +18 -0
- package/dist/providers/index.js.map +1 -0
- package/dist/server/index.d.ts +2 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +288 -154
- package/dist/server/index.js.map +1 -0
- package/package.json +62 -52
- package/.agent/workflows/api-gen.md +0 -63
- package/.agent/workflows/architect.md +0 -44
- package/.agent/workflows/brainstorm.md +0 -227
- package/.agent/workflows/build.md +0 -38
- package/.agent/workflows/changelog.md +0 -55
- package/.agent/workflows/checkpoint.md +0 -142
- package/.agent/workflows/commit.md +0 -227
- package/.agent/workflows/debug.md +0 -61
- package/.agent/workflows/deploy.md +0 -80
- package/.agent/workflows/doc.md +0 -251
- package/.agent/workflows/execute-plan.md +0 -229
- package/.agent/workflows/feature.md +0 -259
- package/.agent/workflows/fix.md +0 -327
- package/.agent/workflows/help.md +0 -67
- package/.agent/workflows/index.md +0 -152
- package/.agent/workflows/load.md +0 -116
- package/.agent/workflows/mode.md +0 -174
- package/.agent/workflows/optimize.md +0 -57
- package/.agent/workflows/plan.md +0 -341
- package/.agent/workflows/pr.md +0 -78
- package/.agent/workflows/product-plan.md +0 -36
- package/.agent/workflows/production-deploy.md +0 -39
- package/.agent/workflows/refactor.md +0 -67
- package/.agent/workflows/research.md +0 -120
- package/.agent/workflows/review.md +0 -348
- package/.agent/workflows/security-scan.md +0 -60
- package/.agent/workflows/ship.md +0 -225
- package/.agent/workflows/spawn.md +0 -181
- package/.agent/workflows/status.md +0 -63
- package/.agent/workflows/tdd.md +0 -143
- package/.agent/workflows/test.md +0 -344
- package/.agent/workflows/verify.md +0 -35
- package/LICENSE +0 -21
- package/dist/commands/init.js +0 -147
- package/dist/commands/list.js +0 -43
- package/dist/commands/uninstall.js +0 -70
- package/dist/utils/index.js +0 -28
- package/skills/api-security-best-practices/SKILL.md +0 -291
- package/skills/api-security-best-practices/references/examples.md +0 -617
- package/skills/architecture/SKILL.md +0 -59
- package/skills/architecture/context-discovery.md +0 -43
- package/skills/architecture/examples.md +0 -94
- package/skills/architecture/pattern-selection.md +0 -68
- package/skills/architecture/patterns-reference.md +0 -50
- package/skills/architecture/trade-off-analysis.md +0 -77
- package/skills/aws-serverless/SKILL.md +0 -327
- package/skills/brainstorming/SKILL.md +0 -234
- package/skills/c4-context/SKILL.md +0 -154
- package/skills/ci-cd-engineer/SKILL.md +0 -50
- package/skills/code-auditing/SKILL.md +0 -55
- package/skills/copywriting/SKILL.md +0 -248
- package/skills/database-engineer/SKILL.md +0 -47
- package/skills/doc-coauthoring/SKILL.md +0 -379
- package/skills/docker-expert/SKILL.md +0 -412
- package/skills/langgraph/SKILL.md +0 -291
- package/skills/postgresql/SKILL.md +0 -73
- package/skills/pricing-strategy/SKILL.md +0 -360
- package/skills/product-manager/SKILL.md +0 -57
- package/skills/prompt-engineer/README.md +0 -659
- package/skills/prompt-engineer/SKILL.md +0 -256
- package/skills/python-patterns/SKILL.md +0 -445
- package/skills/qa-engineer/SKILL.md +0 -42
- package/skills/rag-engineer/SKILL.md +0 -94
- package/skills/react-patterns/SKILL.md +0 -202
- package/skills/secure-refactoring/SKILL.md +0 -54
- package/skills/security-documentation/SKILL.md +0 -53
- package/skills/senior-architect/SKILL.md +0 -213
- package/skills/senior-architect/references/architecture_patterns.md +0 -103
- package/skills/senior-architect/references/system_design_workflows.md +0 -103
- package/skills/senior-architect/references/tech_decision_guide.md +0 -103
- package/skills/senior-architect/scripts/architecture_diagram_generator.py +0 -114
- package/skills/senior-architect/scripts/dependency_analyzer.py +0 -114
- package/skills/senior-architect/scripts/project_architect.py +0 -114
- package/skills/seo-audit/SKILL.md +0 -491
- package/skills/sql-injection-testing/SKILL.md +0 -452
- package/skills/test-driven-development/SKILL.md +0 -375
- package/skills/test-driven-development/testing-anti-patterns.md +0 -299
- package/skills/test-fixing/SKILL.md +0 -123
- package/skills/testing-patterns/SKILL.md +0 -263
- package/skills/typescript-expert/SKILL.md +0 -202
- package/skills/typescript-expert/references/advanced-topics.md +0 -252
- package/skills/typescript-expert/references/tsconfig-strict.json +0 -92
- package/skills/typescript-expert/references/typescript-cheatsheet.md +0 -383
- package/skills/typescript-expert/references/utility-types.ts +0 -335
- package/skills/typescript-expert/scripts/ts_diagnostic.py +0 -203
- package/skills/ui-ux-designer/SKILL.md +0 -46
- package/skills/vercel-deployment/SKILL.md +0 -83
- package/skills/vulnerability-scanner/SKILL.md +0 -280
- package/skills/vulnerability-scanner/checklists.md +0 -121
- package/skills/vulnerability-scanner/scripts/security_scan.py +0 -458
- package/skills/writing-plans/SKILL.md +0 -120
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { deduplicateLines, deduplicateRetrievalBlocks } from './context-hygiene.js';
|
|
3
|
+
describe('deduplicateRetrievalBlocks', () => {
|
|
4
|
+
it('Given duplicate RAG chunks when deduping then only one copy remains', () => {
|
|
5
|
+
const dup = 'Same chunk text here.';
|
|
6
|
+
const text = [dup, dup, 'unique'].join('\n\n---\n\n');
|
|
7
|
+
const out = deduplicateRetrievalBlocks(text);
|
|
8
|
+
expect(out.split('\n\n---\n\n').filter(Boolean).length).toBe(2);
|
|
9
|
+
expect(out).toContain('unique');
|
|
10
|
+
});
|
|
11
|
+
});
|
|
12
|
+
describe('deduplicateLines', () => {
|
|
13
|
+
it('Given repeated paragraphs when deduping then order is preserved', () => {
|
|
14
|
+
const text = 'A\n\nB\n\nA\n\nC';
|
|
15
|
+
expect(deduplicateLines(text)).toBe('A\n\nB\n\nC');
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
//# sourceMappingURL=context-hygiene.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context-hygiene.test.js","sourceRoot":"","sources":["../../src/core/context-hygiene.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EACL,gBAAgB,EAChB,0BAA0B,EAC3B,MAAM,sBAAsB,CAAC;AAE9B,QAAQ,CAAC,4BAA4B,EAAE,GAAG,EAAE;IAC1C,EAAE,CAAC,qEAAqE,EAAE,GAAG,EAAE;QAC7E,MAAM,GAAG,GAAG,uBAAuB,CAAC;QACpC,MAAM,IAAI,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,QAAQ,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QACtD,MAAM,GAAG,GAAG,0BAA0B,CAAC,IAAI,CAAC,CAAC;QAC7C,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAChE,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;IAClC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,kBAAkB,EAAE,GAAG,EAAE;IAChC,EAAE,CAAC,iEAAiE,EAAE,GAAG,EAAE;QACzE,MAAM,IAAI,GAAG,kBAAkB,CAAC;QAChC,MAAM,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;IACrD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { type RagEngineMode } from './build-rag-engine.js';
|
|
2
|
+
import type { IRagEngine } from './rag-types.js';
|
|
3
|
+
import { SummarizationService } from './summarizer.js';
|
|
4
|
+
/**
|
|
5
|
+
* Context Manager (CM).
|
|
6
|
+
* Orchestrates what goes into the prompt by querying the RAG engine
|
|
7
|
+
* and applying summarization when context is too long.
|
|
8
|
+
* Falls back to MEMORY.md when RAG is not available.
|
|
9
|
+
*/
|
|
10
|
+
export declare class ContextManager {
|
|
11
|
+
private rag;
|
|
12
|
+
private readonly summarizer;
|
|
13
|
+
private readonly memoryPath;
|
|
14
|
+
private ragEnabled;
|
|
15
|
+
private ragMode;
|
|
16
|
+
private readonly basePath;
|
|
17
|
+
private ragMetadataFilter;
|
|
18
|
+
constructor(options?: {
|
|
19
|
+
basePath?: string;
|
|
20
|
+
maxTokens?: number;
|
|
21
|
+
});
|
|
22
|
+
initialize(): Promise<void>;
|
|
23
|
+
/**
|
|
24
|
+
* Retrieve relevant context for a given query.
|
|
25
|
+
*/
|
|
26
|
+
getContext(query: string): Promise<string>;
|
|
27
|
+
/**
|
|
28
|
+
* Ingest a document into the knowledge base.
|
|
29
|
+
*/
|
|
30
|
+
ingest(id: string, content: string, metadata?: Record<string, string>): Promise<void>;
|
|
31
|
+
/**
|
|
32
|
+
* Write a summary/memo to MEMORY.md (fallback when RAG is unavailable).
|
|
33
|
+
*/
|
|
34
|
+
appendToMemory(title: string, content: string): Promise<void>;
|
|
35
|
+
/**
|
|
36
|
+
* Read the MEMORY.md fallback file.
|
|
37
|
+
*/
|
|
38
|
+
readMemoryFile(): Promise<string>;
|
|
39
|
+
isRagEnabled(): boolean;
|
|
40
|
+
getRagMode(): RagEngineMode | null;
|
|
41
|
+
getRag(): IRagEngine | null;
|
|
42
|
+
getSummarizer(): SummarizationService;
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/core/context.ts"],"names":[],"mappings":"AAEA,OAAO,EAAkB,KAAK,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAIvD;;;;;GAKG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,GAAG,CAA2B;IACtC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAuB;IAClD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,OAAO,CAA8B;IAC7C,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,iBAAiB,CAAqC;gBAElD,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE;IAMzD,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAqBjC;;OAEG;IACG,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAoBhD;;OAEG;IACG,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAQ/F;;OAEG;IACG,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAUnE;;OAEG;IACG,cAAc,IAAI,OAAO,CAAC,MAAM,CAAC;IAQvC,YAAY,IAAI,OAAO;IAIvB,UAAU,IAAI,aAAa,GAAG,IAAI;IAIlC,MAAM,IAAI,UAAU,GAAG,IAAI;IAI3B,aAAa,IAAI,oBAAoB;CAGtC"}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import * as fs from 'node:fs/promises';
|
|
2
|
+
import * as path from 'node:path';
|
|
3
|
+
import { buildRagEngine } from './build-rag-engine.js';
|
|
4
|
+
import { SummarizationService } from './summarizer.js';
|
|
5
|
+
import { readAgcelConfig } from './read-agcel-config.js';
|
|
6
|
+
import { deduplicateRetrievalBlocks } from './context-hygiene.js';
|
|
7
|
+
/**
|
|
8
|
+
* Context Manager (CM).
|
|
9
|
+
* Orchestrates what goes into the prompt by querying the RAG engine
|
|
10
|
+
* and applying summarization when context is too long.
|
|
11
|
+
* Falls back to MEMORY.md when RAG is not available.
|
|
12
|
+
*/
|
|
13
|
+
export class ContextManager {
|
|
14
|
+
rag = null;
|
|
15
|
+
summarizer;
|
|
16
|
+
memoryPath;
|
|
17
|
+
ragEnabled = false;
|
|
18
|
+
ragMode = null;
|
|
19
|
+
basePath;
|
|
20
|
+
ragMetadataFilter;
|
|
21
|
+
constructor(options) {
|
|
22
|
+
this.basePath = options?.basePath || process.cwd();
|
|
23
|
+
this.summarizer = new SummarizationService(options?.maxTokens);
|
|
24
|
+
this.memoryPath = path.join(this.basePath, 'MEMORY.md');
|
|
25
|
+
}
|
|
26
|
+
async initialize() {
|
|
27
|
+
const config = await readAgcelConfig(this.basePath);
|
|
28
|
+
this.ragMetadataFilter = config.ragQueryMetadataFilter;
|
|
29
|
+
if (config.ragEnabled === false) {
|
|
30
|
+
this.ragEnabled = false;
|
|
31
|
+
this.rag = null;
|
|
32
|
+
this.ragMode = null;
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
try {
|
|
36
|
+
const { engine, mode } = await buildRagEngine(this.basePath, config);
|
|
37
|
+
this.rag = engine;
|
|
38
|
+
this.ragMode = mode;
|
|
39
|
+
this.ragEnabled = true;
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
this.ragEnabled = false;
|
|
43
|
+
this.rag = null;
|
|
44
|
+
this.ragMode = null;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Retrieve relevant context for a given query.
|
|
49
|
+
*/
|
|
50
|
+
async getContext(query) {
|
|
51
|
+
if (this.ragEnabled && this.rag) {
|
|
52
|
+
const filter = this.ragMetadataFilter && Object.keys(this.ragMetadataFilter).length > 0
|
|
53
|
+
? this.ragMetadataFilter
|
|
54
|
+
: undefined;
|
|
55
|
+
const results = await this.rag.query(query, 5, filter);
|
|
56
|
+
let context = results.map(r => r.content).join('\n\n---\n\n');
|
|
57
|
+
context = deduplicateRetrievalBlocks(context);
|
|
58
|
+
if (this.summarizer.exceedsBudget(context)) {
|
|
59
|
+
return deduplicateRetrievalBlocks(this.summarizer.summarize(context));
|
|
60
|
+
}
|
|
61
|
+
return context;
|
|
62
|
+
}
|
|
63
|
+
const mem = await this.readMemoryFile();
|
|
64
|
+
return mem ? deduplicateRetrievalBlocks(mem) : '';
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Ingest a document into the knowledge base.
|
|
68
|
+
*/
|
|
69
|
+
async ingest(id, content, metadata = {}) {
|
|
70
|
+
if (this.ragEnabled && this.rag) {
|
|
71
|
+
await this.rag.addDocument(id, content, metadata);
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
await this.appendToMemory(id, content);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Write a summary/memo to MEMORY.md (fallback when RAG is unavailable).
|
|
79
|
+
*/
|
|
80
|
+
async appendToMemory(title, content) {
|
|
81
|
+
const timestamp = new Date().toISOString();
|
|
82
|
+
const entry = `\n## ${title}\n_${timestamp}_\n\n${content}\n`;
|
|
83
|
+
try {
|
|
84
|
+
await fs.appendFile(this.memoryPath, entry);
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
await fs.writeFile(this.memoryPath, `# AgCEL Memory\n${entry}`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Read the MEMORY.md fallback file.
|
|
92
|
+
*/
|
|
93
|
+
async readMemoryFile() {
|
|
94
|
+
try {
|
|
95
|
+
return await fs.readFile(this.memoryPath, 'utf-8');
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
return '';
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
isRagEnabled() {
|
|
102
|
+
return this.ragEnabled;
|
|
103
|
+
}
|
|
104
|
+
getRagMode() {
|
|
105
|
+
return this.ragMode;
|
|
106
|
+
}
|
|
107
|
+
getRag() {
|
|
108
|
+
return this.rag;
|
|
109
|
+
}
|
|
110
|
+
getSummarizer() {
|
|
111
|
+
return this.summarizer;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/core/context.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Builds the text used for intent detection and RAG when a conversation
|
|
3
|
+
* is in progress, so agent selection can change as the topic evolves.
|
|
4
|
+
*/
|
|
5
|
+
export type ConversationTurn = {
|
|
6
|
+
role: 'user' | 'assistant' | string;
|
|
7
|
+
content: string;
|
|
8
|
+
};
|
|
9
|
+
export declare function buildRoutingQuery(currentQuery: string, history: ConversationTurn[] | undefined, maxChars?: number): string;
|
|
10
|
+
//# sourceMappingURL=conversation-routing.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conversation-routing.d.ts","sourceRoot":"","sources":["../../src/core/conversation-routing.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,MAAM,CAAC;IACpC,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAIF,wBAAgB,iBAAiB,CAC/B,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE,gBAAgB,EAAE,GAAG,SAAS,EACvC,QAAQ,GAAE,MAA0B,GACnC,MAAM,CAuBR"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Builds the text used for intent detection and RAG when a conversation
|
|
3
|
+
* is in progress, so agent selection can change as the topic evolves.
|
|
4
|
+
*/
|
|
5
|
+
const DEFAULT_MAX_CHARS = 8000;
|
|
6
|
+
export function buildRoutingQuery(currentQuery, history, maxChars = DEFAULT_MAX_CHARS) {
|
|
7
|
+
const trimmed = currentQuery.trim();
|
|
8
|
+
if (!history?.length) {
|
|
9
|
+
return trimmed;
|
|
10
|
+
}
|
|
11
|
+
const parts = [];
|
|
12
|
+
for (const turn of history) {
|
|
13
|
+
const label = turn.role === 'user'
|
|
14
|
+
? 'User'
|
|
15
|
+
: turn.role === 'assistant'
|
|
16
|
+
? 'Assistant'
|
|
17
|
+
: String(turn.role);
|
|
18
|
+
parts.push(`${label}: ${turn.content.trim()}`);
|
|
19
|
+
}
|
|
20
|
+
parts.push(`User: ${trimmed}`);
|
|
21
|
+
let combined = parts.join('\n\n');
|
|
22
|
+
if (combined.length > maxChars) {
|
|
23
|
+
combined = combined.slice(-maxChars);
|
|
24
|
+
}
|
|
25
|
+
return combined;
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=conversation-routing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conversation-routing.js","sourceRoot":"","sources":["../../src/core/conversation-routing.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAOH,MAAM,iBAAiB,GAAG,IAAI,CAAC;AAE/B,MAAM,UAAU,iBAAiB,CAC/B,YAAoB,EACpB,OAAuC,EACvC,WAAmB,iBAAiB;IAEpC,MAAM,OAAO,GAAG,YAAY,CAAC,IAAI,EAAE,CAAC;IACpC,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC;QACrB,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,MAAM,IAAI,IAAI,OAAO,EAAE,CAAC;QAC3B,MAAM,KAAK,GACT,IAAI,CAAC,IAAI,KAAK,MAAM;YAClB,CAAC,CAAC,MAAM;YACR,CAAC,CAAC,IAAI,CAAC,IAAI,KAAK,WAAW;gBACzB,CAAC,CAAC,WAAW;gBACb,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC1B,KAAK,CAAC,IAAI,CAAC,GAAG,KAAK,KAAK,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IACjD,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,SAAS,OAAO,EAAE,CAAC,CAAC;IAE/B,IAAI,QAAQ,GAAG,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAClC,IAAI,QAAQ,CAAC,MAAM,GAAG,QAAQ,EAAE,CAAC;QAC/B,QAAQ,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,CAAC;IACvC,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conversation-routing.test.d.ts","sourceRoot":"","sources":["../../src/core/conversation-routing.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { buildRoutingQuery } from './conversation-routing.js';
|
|
3
|
+
describe('buildRoutingQuery', () => {
|
|
4
|
+
it('Given no history when building routing query then returns the current query only', () => {
|
|
5
|
+
const q = ' fix the bug ';
|
|
6
|
+
expect(buildRoutingQuery(q, undefined)).toBe('fix the bug');
|
|
7
|
+
expect(buildRoutingQuery(q, [])).toBe('fix the bug');
|
|
8
|
+
});
|
|
9
|
+
it('Given prior turns when building routing query then concatenates roles and caps length', () => {
|
|
10
|
+
const history = [
|
|
11
|
+
{ role: 'user', content: 'Need React layout help' },
|
|
12
|
+
{ role: 'assistant', content: 'Here is a wireframe idea.' }
|
|
13
|
+
];
|
|
14
|
+
const out = buildRoutingQuery('Now the Python API returns 500', history);
|
|
15
|
+
expect(out).toContain('User: Need React layout help');
|
|
16
|
+
expect(out).toContain('Assistant: Here is a wireframe idea.');
|
|
17
|
+
expect(out).toContain('User: Now the Python API returns 500');
|
|
18
|
+
});
|
|
19
|
+
it('Given combined text exceeds maxChars when building then keeps the suffix', () => {
|
|
20
|
+
const filler = 'x'.repeat(100);
|
|
21
|
+
const history = [{ role: 'user', content: filler }];
|
|
22
|
+
const current = 'END_MARKER_UNIQUE';
|
|
23
|
+
const max = 200;
|
|
24
|
+
const out = buildRoutingQuery(current, history, max);
|
|
25
|
+
expect(out.length).toBeLessThanOrEqual(max);
|
|
26
|
+
expect(out).toContain('END_MARKER_UNIQUE');
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
//# sourceMappingURL=conversation-routing.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conversation-routing.test.js","sourceRoot":"","sources":["../../src/core/conversation-routing.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAE9D,QAAQ,CAAC,mBAAmB,EAAE,GAAG,EAAE;IACjC,EAAE,CAAC,kFAAkF,EAAE,GAAG,EAAE;QAC1F,MAAM,CAAC,GAAG,iBAAiB,CAAC;QAC5B,MAAM,CAAC,iBAAiB,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QAC5D,MAAM,CAAC,iBAAiB,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;IACvD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uFAAuF,EAAE,GAAG,EAAE;QAC/F,MAAM,OAAO,GAAG;YACd,EAAE,IAAI,EAAE,MAAe,EAAE,OAAO,EAAE,wBAAwB,EAAE;YAC5D,EAAE,IAAI,EAAE,WAAoB,EAAE,OAAO,EAAE,2BAA2B,EAAE;SACrE,CAAC;QACF,MAAM,GAAG,GAAG,iBAAiB,CAAC,gCAAgC,EAAE,OAAO,CAAC,CAAC;QACzE,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,8BAA8B,CAAC,CAAC;QACtD,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,sCAAsC,CAAC,CAAC;QAC9D,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,sCAAsC,CAAC,CAAC;IAChE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0EAA0E,EAAE,GAAG,EAAE;QAClF,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAC/B,MAAM,OAAO,GAAG,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;QAC7D,MAAM,OAAO,GAAG,mBAAmB,CAAC;QACpC,MAAM,GAAG,GAAG,GAAG,CAAC;QAChB,MAAM,GAAG,GAAG,iBAAiB,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,CAAC,CAAC;QACrD,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,mBAAmB,CAAC,GAAG,CAAC,CAAC;QAC5C,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,mBAAmB,CAAC,CAAC;IAC7C,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { Guardrail } from './schema.js';
|
|
2
|
+
export interface ValidationResult {
|
|
3
|
+
passed: boolean;
|
|
4
|
+
violations: Violation[];
|
|
5
|
+
}
|
|
6
|
+
export interface Violation {
|
|
7
|
+
guardrailId: string;
|
|
8
|
+
severity: 'error' | 'warning';
|
|
9
|
+
description: string;
|
|
10
|
+
detail: string;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Validates agent output against a set of guardrails.
|
|
14
|
+
* - 'error' violations cause the validation to fail.
|
|
15
|
+
* - 'warning' violations are flagged but don't block.
|
|
16
|
+
*/
|
|
17
|
+
export declare class GuardrailValidator {
|
|
18
|
+
validate(output: string, guardrails: Guardrail[]): ValidationResult;
|
|
19
|
+
/**
|
|
20
|
+
* Format violations into a human-readable report.
|
|
21
|
+
*/
|
|
22
|
+
formatReport(result: ValidationResult): string;
|
|
23
|
+
private checkPattern;
|
|
24
|
+
private checkForbiddenTerms;
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=guardrails.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"guardrails.d.ts","sourceRoot":"","sources":["../../src/core/guardrails.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7C,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,OAAO,CAAC;IAChB,UAAU,EAAE,SAAS,EAAE,CAAC;CACzB;AAED,MAAM,WAAW,SAAS;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,OAAO,GAAG,SAAS,CAAC;IAC9B,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;GAIG;AACH,qBAAa,kBAAkB;IAC7B,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,GAAG,gBAAgB;IA6BnE;;OAEG;IACH,YAAY,CAAC,MAAM,EAAE,gBAAgB,GAAG,MAAM;IAmB9C,OAAO,CAAC,YAAY;IAapB,OAAO,CAAC,mBAAmB;CAa5B"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validates agent output against a set of guardrails.
|
|
3
|
+
* - 'error' violations cause the validation to fail.
|
|
4
|
+
* - 'warning' violations are flagged but don't block.
|
|
5
|
+
*/
|
|
6
|
+
export class GuardrailValidator {
|
|
7
|
+
validate(output, guardrails) {
|
|
8
|
+
const violations = [];
|
|
9
|
+
for (const rule of guardrails) {
|
|
10
|
+
// Pattern check
|
|
11
|
+
if (rule.pattern) {
|
|
12
|
+
this.checkPattern(output, rule, violations);
|
|
13
|
+
}
|
|
14
|
+
// Forbidden terms check
|
|
15
|
+
if (rule.forbiddenTerms) {
|
|
16
|
+
this.checkForbiddenTerms(output, rule, violations);
|
|
17
|
+
}
|
|
18
|
+
// Max length check
|
|
19
|
+
if (rule.maxLength !== undefined && output.length > rule.maxLength) {
|
|
20
|
+
violations.push({
|
|
21
|
+
guardrailId: rule.id,
|
|
22
|
+
severity: rule.severity,
|
|
23
|
+
description: rule.description,
|
|
24
|
+
detail: `Output length (${output.length}) exceeds max (${rule.maxLength}).`
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
const hasErrors = violations.some(v => v.severity === 'error');
|
|
29
|
+
return { passed: !hasErrors, violations };
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Format violations into a human-readable report.
|
|
33
|
+
*/
|
|
34
|
+
formatReport(result) {
|
|
35
|
+
if (result.violations.length === 0) {
|
|
36
|
+
return 'All guardrails passed.';
|
|
37
|
+
}
|
|
38
|
+
const lines = [
|
|
39
|
+
result.passed ? 'Guardrail warnings:' : 'Guardrail violations (output blocked):'
|
|
40
|
+
];
|
|
41
|
+
for (const v of result.violations) {
|
|
42
|
+
lines.push(` [${v.severity}] [${v.guardrailId}] ${v.description}`, ` ${v.detail}`);
|
|
43
|
+
}
|
|
44
|
+
return lines.join('\n');
|
|
45
|
+
}
|
|
46
|
+
checkPattern(output, rule, violations) {
|
|
47
|
+
if (!rule.pattern)
|
|
48
|
+
return;
|
|
49
|
+
const regex = new RegExp(rule.pattern, 'gi');
|
|
50
|
+
const matches = output.match(regex);
|
|
51
|
+
if (!matches)
|
|
52
|
+
return;
|
|
53
|
+
violations.push({
|
|
54
|
+
guardrailId: rule.id,
|
|
55
|
+
severity: rule.severity,
|
|
56
|
+
description: rule.description,
|
|
57
|
+
detail: `Pattern "${rule.pattern}" matched ${matches.length} time(s).`
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
checkForbiddenTerms(output, rule, violations) {
|
|
61
|
+
if (!rule.forbiddenTerms)
|
|
62
|
+
return;
|
|
63
|
+
const lowerOutput = output.toLowerCase();
|
|
64
|
+
for (const term of rule.forbiddenTerms) {
|
|
65
|
+
if (!lowerOutput.includes(term.toLowerCase()))
|
|
66
|
+
continue;
|
|
67
|
+
violations.push({
|
|
68
|
+
guardrailId: rule.id,
|
|
69
|
+
severity: rule.severity,
|
|
70
|
+
description: rule.description,
|
|
71
|
+
detail: `Forbidden term "${term}" detected.`
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=guardrails.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"guardrails.js","sourceRoot":"","sources":["../../src/core/guardrails.ts"],"names":[],"mappings":"AAcA;;;;GAIG;AACH,MAAM,OAAO,kBAAkB;IAC7B,QAAQ,CAAC,MAAc,EAAE,UAAuB;QAC9C,MAAM,UAAU,GAAgB,EAAE,CAAC;QAEnC,KAAK,MAAM,IAAI,IAAI,UAAU,EAAE,CAAC;YAC9B,gBAAgB;YAChB,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;gBACjB,IAAI,CAAC,YAAY,CAAC,MAAM,EAAE,IAAI,EAAE,UAAU,CAAC,CAAC;YAC9C,CAAC;YAED,wBAAwB;YACxB,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;gBACxB,IAAI,CAAC,mBAAmB,CAAC,MAAM,EAAE,IAAI,EAAE,UAAU,CAAC,CAAC;YACrD,CAAC;YAED,mBAAmB;YACnB,IAAI,IAAI,CAAC,SAAS,KAAK,SAAS,IAAI,MAAM,CAAC,MAAM,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC;gBACnE,UAAU,CAAC,IAAI,CAAC;oBACd,WAAW,EAAE,IAAI,CAAC,EAAE;oBACpB,QAAQ,EAAE,IAAI,CAAC,QAAQ;oBACvB,WAAW,EAAE,IAAI,CAAC,WAAW;oBAC7B,MAAM,EAAE,kBAAkB,MAAM,CAAC,MAAM,kBAAkB,IAAI,CAAC,SAAS,IAAI;iBAC5E,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,MAAM,SAAS,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC;QAC/D,OAAO,EAAE,MAAM,EAAE,CAAC,SAAS,EAAE,UAAU,EAAE,CAAC;IAC5C,CAAC;IAED;;OAEG;IACH,YAAY,CAAC,MAAwB;QACnC,IAAI,MAAM,CAAC,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACnC,OAAO,wBAAwB,CAAC;QAClC,CAAC;QAED,MAAM,KAAK,GAAa;YACtB,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,wCAAwC;SACjF,CAAC;QAEF,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC;YAClC,KAAK,CAAC,IAAI,CACR,MAAM,CAAC,CAAC,QAAQ,MAAM,CAAC,CAAC,WAAW,KAAK,CAAC,CAAC,WAAW,EAAE,EACvD,QAAQ,CAAC,CAAC,MAAM,EAAE,CACnB,CAAC;QACJ,CAAC;QAED,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;IAEO,YAAY,CAAC,MAAc,EAAE,IAAe,EAAE,UAAuB;QAC3E,IAAI,CAAC,IAAI,CAAC,OAAO;YAAE,OAAO;QAC1B,MAAM,KAAK,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;QAC7C,MAAM,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QACpC,IAAI,CAAC,OAAO;YAAE,OAAO;QACrB,UAAU,CAAC,IAAI,CAAC;YACd,WAAW,EAAE,IAAI,CAAC,EAAE;YACpB,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,WAAW,EAAE,IAAI,CAAC,WAAW;YAC7B,MAAM,EAAE,YAAY,IAAI,CAAC,OAAO,aAAa,OAAO,CAAC,MAAM,WAAW;SACvE,CAAC,CAAC;IACL,CAAC;IAEO,mBAAmB,CAAC,MAAc,EAAE,IAAe,EAAE,UAAuB;QAClF,IAAI,CAAC,IAAI,CAAC,cAAc;YAAE,OAAO;QACjC,MAAM,WAAW,GAAG,MAAM,CAAC,WAAW,EAAE,CAAC;QACzC,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;YACvC,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;gBAAE,SAAS;YACxD,UAAU,CAAC,IAAI,CAAC;gBACd,WAAW,EAAE,IAAI,CAAC,EAAE;gBACpB,QAAQ,EAAE,IAAI,CAAC,QAAQ;gBACvB,WAAW,EAAE,IAAI,CAAC,WAAW;gBAC7B,MAAM,EAAE,mBAAmB,IAAI,aAAa;aAC7C,CAAC,CAAC;QACL,CAAC;IACH,CAAC;CACF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"guardrails.test.d.ts","sourceRoot":"","sources":["../../src/core/guardrails.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { GuardrailValidator } from './guardrails.js';
|
|
3
|
+
describe('GuardrailValidator', () => {
|
|
4
|
+
it('Given clean output when validating then passes with no violations', () => {
|
|
5
|
+
const validator = new GuardrailValidator();
|
|
6
|
+
const guardrails = [
|
|
7
|
+
{ id: 'G1', description: 'No profanity allowed', severity: 'error', forbiddenTerms: ['badword'] }
|
|
8
|
+
];
|
|
9
|
+
const result = validator.validate('All good here.', guardrails);
|
|
10
|
+
expect(result.passed).toBe(true);
|
|
11
|
+
expect(result.violations).toHaveLength(0);
|
|
12
|
+
});
|
|
13
|
+
it('Given forbidden term when validating then fails with error violation', () => {
|
|
14
|
+
const validator = new GuardrailValidator();
|
|
15
|
+
const guardrails = [
|
|
16
|
+
{ id: 'G1', description: 'No profanity allowed', severity: 'error', forbiddenTerms: ['badword'] }
|
|
17
|
+
];
|
|
18
|
+
const result = validator.validate('This contains a badword.', guardrails);
|
|
19
|
+
expect(result.passed).toBe(false);
|
|
20
|
+
expect(result.violations).toHaveLength(1);
|
|
21
|
+
expect(result.violations[0]?.guardrailId).toBe('G1');
|
|
22
|
+
expect(result.violations[0]?.severity).toBe('error');
|
|
23
|
+
});
|
|
24
|
+
it('Given maxLength warning when validating then passes with warning violation', () => {
|
|
25
|
+
const validator = new GuardrailValidator();
|
|
26
|
+
const guardrails = [
|
|
27
|
+
{ id: 'G2', description: 'Output must be concise', severity: 'warning', maxLength: 10 }
|
|
28
|
+
];
|
|
29
|
+
const result = validator.validate('This is too long.', guardrails);
|
|
30
|
+
expect(result.passed).toBe(true);
|
|
31
|
+
expect(result.violations).toHaveLength(1);
|
|
32
|
+
expect(result.violations[0]?.severity).toBe('warning');
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
//# sourceMappingURL=guardrails.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"guardrails.test.js","sourceRoot":"","sources":["../../src/core/guardrails.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAGrD,QAAQ,CAAC,oBAAoB,EAAE,GAAG,EAAE;IAClC,EAAE,CAAC,mEAAmE,EAAE,GAAG,EAAE;QAC3E,MAAM,SAAS,GAAG,IAAI,kBAAkB,EAAE,CAAC;QAC3C,MAAM,UAAU,GAAgB;YAC9B,EAAE,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,sBAAsB,EAAE,QAAQ,EAAE,OAAO,EAAE,cAAc,EAAE,CAAC,SAAS,CAAC,EAAE;SAClG,CAAC;QAEF,MAAM,MAAM,GAAG,SAAS,CAAC,QAAQ,CAAC,gBAAgB,EAAE,UAAU,CAAC,CAAC;QAEhE,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACjC,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IAC5C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sEAAsE,EAAE,GAAG,EAAE;QAC9E,MAAM,SAAS,GAAG,IAAI,kBAAkB,EAAE,CAAC;QAC3C,MAAM,UAAU,GAAgB;YAC9B,EAAE,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,sBAAsB,EAAE,QAAQ,EAAE,OAAO,EAAE,cAAc,EAAE,CAAC,SAAS,CAAC,EAAE;SAClG,CAAC;QAEF,MAAM,MAAM,GAAG,SAAS,CAAC,QAAQ,CAAC,0BAA0B,EAAE,UAAU,CAAC,CAAC;QAE1E,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAClC,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QAC1C,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,WAAW,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACrD,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACvD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4EAA4E,EAAE,GAAG,EAAE;QACpF,MAAM,SAAS,GAAG,IAAI,kBAAkB,EAAE,CAAC;QAC3C,MAAM,UAAU,GAAgB;YAC9B,EAAE,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,wBAAwB,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,EAAE,EAAE,EAAE;SACxF,CAAC;QAEF,MAAM,MAAM,GAAG,SAAS,CAAC,QAAQ,CAAC,mBAAmB,EAAE,UAAU,CAAC,CAAC;QAEnE,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACjC,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QAC1C,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACzD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { Agent } from './schema.js';
|
|
2
|
+
export interface IntentResult {
|
|
3
|
+
bestAgent: Agent | null;
|
|
4
|
+
confidence: number;
|
|
5
|
+
/** When confidence is below threshold, this contains a suggestion */
|
|
6
|
+
suggestion: string | null;
|
|
7
|
+
}
|
|
8
|
+
/** Score how well `lowerText` matches an agent (lowercased query text). */
|
|
9
|
+
export declare function scoreAgentAgainstText(lowerText: string, agent: Agent): number;
|
|
10
|
+
export declare function agentSemanticProfile(agent: Agent): string;
|
|
11
|
+
export interface DetectIntentOptions {
|
|
12
|
+
/** Latest user message; weighted extra vs `query` so routing can follow topic shifts. */
|
|
13
|
+
latestUserTurn?: string;
|
|
14
|
+
/** Multiplier for `scoreAgentAgainstText(latest, agent)` (default 3). */
|
|
15
|
+
latestTurnWeight?: number;
|
|
16
|
+
}
|
|
17
|
+
export declare class IntentEngine {
|
|
18
|
+
private threshold;
|
|
19
|
+
constructor(threshold?: number);
|
|
20
|
+
detectIntent(query: string, agents: Agent[], options?: DetectIntentOptions): IntentResult;
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=intent.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"intent.d.ts","sourceRoot":"","sources":["../../src/core/intent.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAGzC,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,KAAK,GAAG,IAAI,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,qEAAqE;IACrE,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAID,2EAA2E;AAC3E,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,GAAG,MAAM,CAoB7E;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,KAAK,GAAG,MAAM,CAEzD;AAED,MAAM,WAAW,mBAAmB;IAClC,yFAAyF;IACzF,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,yEAAyE;IACzE,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,qBAAa,YAAY;IACvB,OAAO,CAAC,SAAS,CAAS;gBAEd,SAAS,GAAE,MAA6B;IAIpD,YAAY,CACV,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,KAAK,EAAE,EACf,OAAO,CAAC,EAAE,mBAAmB,GAC5B,YAAY;CAqDhB"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { textCosineSimilarity } from './text-similarity.js';
|
|
2
|
+
const CONFIDENCE_THRESHOLD = 0.5;
|
|
3
|
+
/** Score how well `lowerText` matches an agent (lowercased query text). */
|
|
4
|
+
export function scoreAgentAgainstText(lowerText, agent) {
|
|
5
|
+
let score = 0;
|
|
6
|
+
if (lowerText.includes(agent.name.toLowerCase()))
|
|
7
|
+
score += 10;
|
|
8
|
+
const roleWords = agent.role.toLowerCase().split(/\s+/);
|
|
9
|
+
for (const word of roleWords) {
|
|
10
|
+
if (word.length > 3 && lowerText.includes(word))
|
|
11
|
+
score += 2;
|
|
12
|
+
}
|
|
13
|
+
const descWords = agent.description.toLowerCase().split(/\s+/);
|
|
14
|
+
for (const word of descWords) {
|
|
15
|
+
if (word.length > 3 && lowerText.includes(word))
|
|
16
|
+
score += 1;
|
|
17
|
+
}
|
|
18
|
+
for (const skill of agent.skills) {
|
|
19
|
+
if (lowerText.includes(skill.toLowerCase()))
|
|
20
|
+
score += 5;
|
|
21
|
+
}
|
|
22
|
+
return score;
|
|
23
|
+
}
|
|
24
|
+
export function agentSemanticProfile(agent) {
|
|
25
|
+
return [agent.name, agent.description, agent.role, ...agent.skills].join(' ');
|
|
26
|
+
}
|
|
27
|
+
export class IntentEngine {
|
|
28
|
+
threshold;
|
|
29
|
+
constructor(threshold = CONFIDENCE_THRESHOLD) {
|
|
30
|
+
this.threshold = threshold;
|
|
31
|
+
}
|
|
32
|
+
detectIntent(query, agents, options) {
|
|
33
|
+
if (agents.length === 0) {
|
|
34
|
+
return {
|
|
35
|
+
bestAgent: null,
|
|
36
|
+
confidence: 0,
|
|
37
|
+
suggestion: 'No agents registered. Run `agcel create-agent` to create one, or provide skill keywords to help AgCEL build one for you.'
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
let bestAgent = agents[0];
|
|
41
|
+
let maxScore = 0;
|
|
42
|
+
const lowerQuery = query.toLowerCase();
|
|
43
|
+
const latest = options?.latestUserTurn?.toLowerCase().trim() ?? '';
|
|
44
|
+
const latestWeight = options?.latestTurnWeight ?? 3;
|
|
45
|
+
const useRecencyBoost = Boolean(latest && latest !== lowerQuery);
|
|
46
|
+
for (const agent of agents) {
|
|
47
|
+
const profile = agentSemanticProfile(agent).toLowerCase();
|
|
48
|
+
let score = scoreAgentAgainstText(lowerQuery, agent);
|
|
49
|
+
score += textCosineSimilarity(lowerQuery, profile) * 24;
|
|
50
|
+
if (useRecencyBoost) {
|
|
51
|
+
score += scoreAgentAgainstText(latest, agent) * latestWeight;
|
|
52
|
+
score += textCosineSimilarity(latest, profile) * latestWeight * 8;
|
|
53
|
+
}
|
|
54
|
+
if (score > maxScore) {
|
|
55
|
+
maxScore = score;
|
|
56
|
+
bestAgent = agent;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
const confidenceDivisor = useRecencyBoost ? 95 : 38;
|
|
60
|
+
const confidence = Math.min(maxScore / confidenceDivisor, 1);
|
|
61
|
+
if (confidence < this.threshold) {
|
|
62
|
+
return {
|
|
63
|
+
bestAgent: confidence > 0 ? bestAgent : null,
|
|
64
|
+
confidence,
|
|
65
|
+
suggestion: [
|
|
66
|
+
`Low confidence (${(confidence * 100).toFixed(0)}%) - no agent is a strong match for this query.`,
|
|
67
|
+
`Available agents: ${agents.map(a => a.name).join(', ')}`,
|
|
68
|
+
`Suggestion: Run \`agcel suggest "<your query>"\` for next steps, \`agcel create-agent --research\` to build a persona with best-practice hints, or \`agcel create-skill\` for a new skill bundle.`
|
|
69
|
+
].join('\n')
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
return {
|
|
73
|
+
bestAgent,
|
|
74
|
+
confidence,
|
|
75
|
+
suggestion: null
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=intent.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"intent.js","sourceRoot":"","sources":["../../src/core/intent.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"intent.test.d.ts","sourceRoot":"","sources":["../../src/core/intent.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { IntentEngine, scoreAgentAgainstText } from './intent.js';
|
|
3
|
+
const pythonAgent = {
|
|
4
|
+
name: 'python-expert',
|
|
5
|
+
description: 'Python django asyncio debugging',
|
|
6
|
+
role: 'Python engineer for APIs and servers',
|
|
7
|
+
skills: ['python']
|
|
8
|
+
};
|
|
9
|
+
const uiAgent = {
|
|
10
|
+
name: 'ui-expert',
|
|
11
|
+
description: 'Figma mockups React layout design',
|
|
12
|
+
role: 'Designer for UI and product visuals',
|
|
13
|
+
skills: ['design']
|
|
14
|
+
};
|
|
15
|
+
describe('IntentEngine', () => {
|
|
16
|
+
it('Given only the current query when detecting intent then behavior matches single-shot routing', () => {
|
|
17
|
+
const engine = new IntentEngine(0.25);
|
|
18
|
+
const agents = [pythonAgent, uiAgent];
|
|
19
|
+
const q = 'Need a Figma mockup for onboarding';
|
|
20
|
+
const combined = q;
|
|
21
|
+
const withRecency = engine.detectIntent(combined, agents, {
|
|
22
|
+
latestUserTurn: q
|
|
23
|
+
});
|
|
24
|
+
const without = engine.detectIntent(combined, agents);
|
|
25
|
+
expect(withRecency.bestAgent?.name).toBe(without.bestAgent?.name);
|
|
26
|
+
});
|
|
27
|
+
it('Given long prior UI thread when latest turn is Python then best agent follows the latest turn', () => {
|
|
28
|
+
const engine = new IntentEngine(0.25);
|
|
29
|
+
const agents = [pythonAgent, uiAgent];
|
|
30
|
+
const routing = 'User: Build a Figma dashboard mockup\n\nAssistant: Here are frames.\n\nUser: Fix the Django 500 in our Python API';
|
|
31
|
+
const result = engine.detectIntent(routing, agents, {
|
|
32
|
+
latestUserTurn: 'Fix the Django 500 in our Python API'
|
|
33
|
+
});
|
|
34
|
+
expect(result.bestAgent?.name).toBe('python-expert');
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
describe('scoreAgentAgainstText', () => {
|
|
38
|
+
it('Given django in text when scoring python agent then returns a positive score', () => {
|
|
39
|
+
const s = scoreAgentAgainstText('debug django view', pythonAgent);
|
|
40
|
+
expect(s).toBeGreaterThan(0);
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
//# sourceMappingURL=intent.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"intent.test.js","sourceRoot":"","sources":["../../src/core/intent.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAGlE,MAAM,WAAW,GAAU;IACzB,IAAI,EAAE,eAAe;IACrB,WAAW,EAAE,iCAAiC;IAC9C,IAAI,EAAE,sCAAsC;IAC5C,MAAM,EAAE,CAAC,QAAQ,CAAC;CACnB,CAAC;AAEF,MAAM,OAAO,GAAU;IACrB,IAAI,EAAE,WAAW;IACjB,WAAW,EAAE,mCAAmC;IAChD,IAAI,EAAE,qCAAqC;IAC3C,MAAM,EAAE,CAAC,QAAQ,CAAC;CACnB,CAAC;AAEF,QAAQ,CAAC,cAAc,EAAE,GAAG,EAAE;IAC5B,EAAE,CAAC,8FAA8F,EAAE,GAAG,EAAE;QACtG,MAAM,MAAM,GAAG,IAAI,YAAY,CAAC,IAAI,CAAC,CAAC;QACtC,MAAM,MAAM,GAAG,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;QACtC,MAAM,CAAC,GAAG,oCAAoC,CAAC;QAC/C,MAAM,QAAQ,GAAG,CAAC,CAAC;QACnB,MAAM,WAAW,GAAG,MAAM,CAAC,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE;YACxD,cAAc,EAAE,CAAC;SAClB,CAAC,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,CAAC,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;QACtD,MAAM,CAAC,WAAW,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;IACpE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+FAA+F,EAAE,GAAG,EAAE;QACvG,MAAM,MAAM,GAAG,IAAI,YAAY,CAAC,IAAI,CAAC,CAAC;QACtC,MAAM,MAAM,GAAG,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;QACtC,MAAM,OAAO,GACX,mHAAmH,CAAC;QACtH,MAAM,MAAM,GAAG,MAAM,CAAC,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE;YAClD,cAAc,EAAE,sCAAsC;SACvD,CAAC,CAAC;QACH,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;IACvD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,uBAAuB,EAAE,GAAG,EAAE;IACrC,EAAE,CAAC,8EAA8E,EAAE,GAAG,EAAE;QACtF,MAAM,CAAC,GAAG,qBAAqB,CAAC,mBAAmB,EAAE,WAAW,CAAC,CAAC;QAClE,MAAM,CAAC,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;IAC/B,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|