From ffc0611b34613c2e9838ec91238f3e8bc047bf4c Mon Sep 17 00:00:00 2001 From: maelcaldas Date: Wed, 25 Mar 2026 14:22:20 -0300 Subject: [PATCH] Simplify agent reference loading rules --- .../core-concepts.ts => docs/core-concepts.md | 44 +++++++++---------- scripts/fetch-core-concepts.ts | 25 +++-------- src/agent/system-prompt.ts | 23 +++++++--- test/unit/agent/core-concepts.test.ts | 26 +++++++---- test/unit/agent/system-prompt.test.ts | 31 ++++++++++--- 5 files changed, 86 insertions(+), 63 deletions(-) rename src/agent/core-concepts.ts => docs/core-concepts.md (83%) diff --git a/src/agent/core-concepts.ts b/docs/core-concepts.md similarity index 83% rename from src/agent/core-concepts.ts rename to docs/core-concepts.md index 95937c5..399dc4f 100644 --- a/src/agent/core-concepts.ts +++ b/docs/core-concepts.md @@ -1,7 +1,4 @@ -// AUTO-GENERATED FILE. DO NOT EDIT. -// Source: https://bkper.com/docs/core-concepts.md - -export const CORE_CONCEPTS_MARKDOWN = `# Core Concepts +# Core Concepts Bkper tracks resources — money, inventory, or anything countable — as movements between places. Every financial event is recorded as an amount moving **from** one Account **to** another. This from-to model replaces the traditional language of debits and credits with something intuitive: resources leave one place and arrive at another. @@ -76,46 +73,46 @@ The sum of all credits and debits recorded in a Book always tallies to zero — These examples show the same movement model in concrete situations. Some match the diagrams on this page. Others add common accrual flows that are easy to confuse. -These examples use Bkper's transaction shorthand \`From >> To\`, meaning the amount leaves the Account on the left and arrives at the Account on the right. +These examples use Bkper's transaction shorthand `From >> To`, meaning the amount leaves the Account on the left and arrives at the Account on the right. | Situation | Transaction | | --- | --- | -| Salary received | \`Salary >> Bank Account\` | -| Investment funded | \`Bank Account >> Investments\` | -| Dividends received | \`Dividends >> Bank Account\` | -| Loan received | \`Loan >> Bank Account\` | -| Rent paid | \`Bank Account >> Rent\` | -| Transportation bought on credit card | \`Credit Card >> Transportation\` | +| Salary received | `Salary >> Bank Account` | +| Investment funded | `Bank Account >> Investments` | +| Dividends received | `Dividends >> Bank Account` | +| Loan received | `Loan >> Bank Account` | +| Rent paid | `Bank Account >> Rent` | +| Transportation bought on credit card | `Credit Card >> Transportation` | **Buy on a credit card now, pay it later** | Step | Transaction | | --- | --- | -| Purchase | \`Credit Card >> Outgoing\` | -| Payment | \`Bank Account >> Credit Card\` | +| Purchase | `Credit Card >> Outgoing` | +| Payment | `Bank Account >> Credit Card` | **Sell now and receive cash later** | Step | Transaction | | --- | --- | -| Sale on credit | \`Incoming >> Accounts Receivable\` | -| Interest added while unpaid | \`Incoming >> Accounts Receivable\` | -| Collection | \`Accounts Receivable >> Bank Account\` | +| Sale on credit | `Incoming >> Accounts Receivable` | +| Interest added while unpaid | `Incoming >> Accounts Receivable` | +| Collection | `Accounts Receivable >> Bank Account` | **Receive a supplier bill now and pay it later** | Step | Transaction | | --- | --- | -| Bill received | \`Accounts Payable >> Outgoing\` | -| Interest added while unpaid | \`Accounts Payable >> Outgoing\` | -| Payment | \`Bank Account >> Accounts Payable\` | +| Bill received | `Accounts Payable >> Outgoing` | +| Interest added while unpaid | `Accounts Payable >> Outgoing` | +| Payment | `Bank Account >> Accounts Payable` | **Receive a loan now and repay principal later** | Step | Transaction | | --- | --- | -| Loan proceeds | \`Loan >> Bank Account\` | -| Principal repayment | \`Bank Account >> Loan\` | +| Loan proceeds | `Loan >> Bank Account` | +| Principal repayment | `Bank Account >> Loan` | In each case, the first movement records the position that was created — a receivable or a liability. The later movement settles that position. This keeps Incoming and Outgoing focused on activity, while Asset and Liability Accounts hold positions until they are cleared. @@ -134,11 +131,11 @@ Bkper maintains a continuous ledger with no concept of closing periods — the s **Custom Properties** are key-value pairs attachable to any entity — Books, Accounts, Groups, Transactions, Collections, and Files. They add context, metadata, and meaning beyond core financial data. -By attaching properties like \`invoice: inv123456\` or \`exc_code: BRL\`, entities become rich with information that can drive automation and reporting — without changing the core model. +By attaching properties like `invoice: inv123456` or `exc_code: BRL`, entities become rich with information that can drive automation and reporting — without changing the core model. ## Hashtags -**Hashtags** are lightweight labels on Transactions that enable multi-dimensional tracking. They complement the Account structure by adding dynamic categorization — a single transaction might carry \`#team_marketing #project_alpha #q1_campaign\`, enabling filtering and analysis from any perspective. +**Hashtags** are lightweight labels on Transactions that enable multi-dimensional tracking. They complement the Account structure by adding dynamic categorization — a single transaction might carry `#team_marketing #project_alpha #q1_campaign`, enabling filtering and analysis from any perspective. Unlike Account structures, Hashtags can be added or removed as needs evolve, making them ideal for cost allocation, project tracking, and ad-hoc analysis. @@ -153,4 +150,3 @@ Collections can also serve as references for automations (Bots or Apps) that wor Every action in a Book — posting a transaction, editing an account, adding a comment — generates an **Event**. Events record _who_ (a user) or _what_ (a bot, an automation) performed the action and _when_, forming a complete audit trail essential for collaboration and trust. Events are also the foundation of Bkper's automation model. Bots and Agents listen for specific event types and react automatically — for example, calculating taxes when a transaction is posted or converting currencies when one is checked. -`; diff --git a/scripts/fetch-core-concepts.ts b/scripts/fetch-core-concepts.ts index d17655c..c2bbc03 100644 --- a/scripts/fetch-core-concepts.ts +++ b/scripts/fetch-core-concepts.ts @@ -12,7 +12,7 @@ const REQUIRED_CORE_CONCEPTS_HEADINGS = [ function resolveOutputPath(): string { const scriptDir = path.dirname(fileURLToPath(import.meta.url)); - return path.resolve(scriptDir, '..', 'src', 'agent', 'core-concepts.ts'); + return path.resolve(scriptDir, '..', 'docs', 'core-concepts.md'); } function validateCoreConceptsMarkdown(markdown: string): void { @@ -27,25 +27,11 @@ function validateCoreConceptsMarkdown(markdown: string): void { } } -function escapeForTemplateLiteral(value: string): string { - return value - .replace(/\\/g, '\\\\') - .replace(/`/g, '\\`') - .replace(/\$\{/g, '\\${'); -} - -function renderCoreConceptsModule(markdown: string): string { - validateCoreConceptsMarkdown(markdown); - - const escapedMarkdown = escapeForTemplateLiteral(markdown); - return `// AUTO-GENERATED FILE. DO NOT EDIT.\n// Source: ${CORE_CONCEPTS_CANONICAL_URL}\n\nexport const CORE_CONCEPTS_MARKDOWN = \`${escapedMarkdown}\`;\n`; -} - async function fetchCoreConceptsMarkdown(): Promise { const response = await fetch(CORE_CONCEPTS_CANONICAL_URL, { headers: { 'Accept': 'text/markdown,text/plain,*/*', - 'User-Agent': 'Mozilla/5.0 (compatible; bkper-cli build)', + 'User-Agent': 'Mozilla/5.0 (compatible; bkper-cli sync)', }, }); @@ -55,16 +41,17 @@ async function fetchCoreConceptsMarkdown(): Promise { ); } - return response.text(); + const markdown = await response.text(); + validateCoreConceptsMarkdown(markdown); + return markdown; } async function main(): Promise { const markdown = await fetchCoreConceptsMarkdown(); const outputPath = resolveOutputPath(); - const moduleSource = renderCoreConceptsModule(markdown); await mkdir(path.dirname(outputPath), { recursive: true }); - await writeFile(outputPath, moduleSource, 'utf8'); + await writeFile(outputPath, markdown, 'utf8'); } void main().catch(error => { diff --git a/src/agent/system-prompt.ts b/src/agent/system-prompt.ts index 928572b..48af905 100644 --- a/src/agent/system-prompt.ts +++ b/src/agent/system-prompt.ts @@ -1,22 +1,37 @@ import {fileURLToPath} from 'node:url'; import path from 'node:path'; -import {CORE_CONCEPTS_MARKDOWN} from './core-concepts.js'; function resolveCliReferencePath(): string { const thisDir = path.dirname(fileURLToPath(import.meta.url)); return path.resolve(thisDir, '..', 'docs', 'cli-reference.md'); } +function resolveCoreConceptsPath(): string { + const thisDir = path.dirname(fileURLToPath(import.meta.url)); + return path.resolve(thisDir, '..', 'docs', 'core-concepts.md'); +} + export function getBkperAgentSystemPrompt(): string { const cliRefPath = resolveCliReferencePath(); + const coreConceptsPath = resolveCoreConceptsPath(); return `${BKPER_AGENT_SYSTEM_PROMPT} -## Bkper CLI Usage +## Reference Loading Rules + +If the task touches Bkper accounting semantics or data modeling — such as Accounts, Transactions, balances, account types, groups, books, or mapping real-world flows into Bkper — read: + +\`\`\` +${coreConceptsPath} +\`\`\` -Before executing \`bkper\` CLI commands, **read the full CLI reference** at: +If the task involves using or executing \`bkper\` CLI commands, read: \`\`\` ${cliRefPath} \`\`\` + +For generic engineering work, you may proceed without loading either reference unless those semantics become relevant. + +When in doubt, read first. `; } @@ -24,8 +39,6 @@ export const BKPER_AGENT_SYSTEM_PROMPT = `# You are a Bkper team member You think in resources, movements, and balances — not debits and credits. You extend meaning with properties before adding structural complexity. You protect the zero-sum invariant above all else. -${CORE_CONCEPTS_MARKDOWN} - ## Operating Principles - Preserve invariants and data integrity first, then user intent, then implementation convenience. diff --git a/test/unit/agent/core-concepts.test.ts b/test/unit/agent/core-concepts.test.ts index b29e0fa..433ccee 100644 --- a/test/unit/agent/core-concepts.test.ts +++ b/test/unit/agent/core-concepts.test.ts @@ -1,19 +1,29 @@ +import { readFileSync } from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; import { expect } from '../helpers/test-setup.js'; -import { CORE_CONCEPTS_MARKDOWN } from '../../../src/agent/core-concepts.js'; + +function resolveCoreConceptsPath(): string { + const thisDir = path.dirname(fileURLToPath(import.meta.url)); + return path.resolve(thisDir, '..', '..', '..', 'docs', 'core-concepts.md'); +} describe('agent core concepts', function () { - it('should export non-empty markdown', function () { - expect(CORE_CONCEPTS_MARKDOWN.trim().length).to.be.greaterThan(0); + it('should provide a non-empty markdown snapshot', function () { + const markdown = readFileSync(resolveCoreConceptsPath(), 'utf8'); + expect(markdown.trim().length).to.be.greaterThan(0); }); it('should include the required core concepts headings', function () { - expect(CORE_CONCEPTS_MARKDOWN).to.include('# Core Concepts'); - expect(CORE_CONCEPTS_MARKDOWN).to.include('## Accounts'); - expect(CORE_CONCEPTS_MARKDOWN).to.include('## Transactions'); - expect(CORE_CONCEPTS_MARKDOWN).to.include('## Books'); + const markdown = readFileSync(resolveCoreConceptsPath(), 'utf8'); + expect(markdown).to.include('# Core Concepts'); + expect(markdown).to.include('## Accounts'); + expect(markdown).to.include('## Transactions'); + expect(markdown).to.include('## Books'); }); it('should preserve markdown examples with backticks', function () { - expect(CORE_CONCEPTS_MARKDOWN).to.include("These examples use Bkper's transaction shorthand `From >> To`"); + const markdown = readFileSync(resolveCoreConceptsPath(), 'utf8'); + expect(markdown).to.include("These examples use Bkper's transaction shorthand `From >> To`"); }); }); diff --git a/test/unit/agent/system-prompt.test.ts b/test/unit/agent/system-prompt.test.ts index 2222069..48a2a99 100644 --- a/test/unit/agent/system-prompt.test.ts +++ b/test/unit/agent/system-prompt.test.ts @@ -10,19 +10,36 @@ describe('agent system prompt', function () { expect(BKPER_AGENT_SYSTEM_PROMPT).to.include('You are a Bkper team member'); }); - it('should include the canonical core concepts content', function () { - expect(BKPER_AGENT_SYSTEM_PROMPT).to.include('# Core Concepts'); - expect(BKPER_AGENT_SYSTEM_PROMPT).to.include('## Transactions'); - expect(BKPER_AGENT_SYSTEM_PROMPT).to.include('## Books'); + it('should not include a partial core concepts canon', function () { + expect(BKPER_AGENT_SYSTEM_PROMPT).to.not.include('## Core Concepts Canon'); + expect(BKPER_AGENT_SYSTEM_PROMPT).to.not.include('## Critical Flow Reminders'); + expect(BKPER_AGENT_SYSTEM_PROMPT).to.not.include('Credit card purchase: `Credit Card >> Outgoing`'); }); - it('should not include the duplicated core concepts canon heading', function () { - expect(BKPER_AGENT_SYSTEM_PROMPT).to.not.include('## Core Concepts Canon'); + it('should include concise loading rules for core concepts', function () { + const full = getBkperAgentSystemPrompt(); + expect(full).to.include('If the task touches Bkper accounting semantics or data modeling'); + expect(full).to.include('Accounts, Transactions, balances, account types, groups, books'); + expect(full).to.include('mapping real-world flows into Bkper'); + expect(full).to.include('When in doubt, read first.'); + expect(full).to.include('core-concepts.md'); + }); + + it('should not include remote documentation navigation or long enumerated loading rules', function () { + const full = getBkperAgentSystemPrompt(); + expect(full).to.not.include('https://bkper.com/llms.txt'); + expect(full).to.not.include('credit cards, receivables, payables, or loans'); + expect(full).to.not.include('tax, inventory, portfolio, exchange, or subledger logic'); + }); + + it('should not inline the full core concepts reference', function () { + expect(BKPER_AGENT_SYSTEM_PROMPT).to.not.include('## Example Flows'); + expect(BKPER_AGENT_SYSTEM_PROMPT).to.not.include("These examples use Bkper's transaction shorthand `From >> To`"); }); it('should include CLI usage section with reference path', function () { const full = getBkperAgentSystemPrompt(); - expect(full).to.include('## Bkper CLI Usage'); + expect(full).to.include('If the task involves using or executing `bkper` CLI commands'); expect(full).to.include('cli-reference.md'); }); });