Two reports from the 08-24 feedback sweep (seq 261545):
- The bulk-book queue title (Samlingsverifikation: 1 transaktioner
2026-07-22) carried no amount, direction or counterparty; the CEO
approving from a phone could not tell what he authorised. Titles now
read: Samlingsverifikation -1 000,00 SEK 2026-05-12: NORDNET UTTAG
(+1 till). Same per-tx text the categorize titles already carry;
preview_data stays aggregate-only.
- gnubok_query_journal called with {query} instead of {text} silently
returned the whole journal. tools/call now rejects unknown top-level
parameters for every tool (all schemas declare additionalProperties:
false) with a VALIDATION_ERROR that lists the valid keys. company_id
stays tolerated everywhere. codedError is exported from
company-routing for the dispatcher.
The copy fixes this branch originally carried (scope-honest
list_pending_operations, BFL 5 kap 6 § on create_voucher, bank-movement
only on categorize/bulk_book) landed independently in #1844 and were
dropped on rebase; no catalog token change remains.
Claude-Session: https://claude.ai/code/session_01ScVhg6XsDtNXkiEQNV7LaZ
Co-authored-by: Jakob Wennberg <311770904+jakobwennberg-oss@users.noreply.github.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
29 lines
1.2 KiB
TypeScript
29 lines
1.2 KiB
TypeScript
/**
|
|
* Top-level argument allow-listing for tools/call.
|
|
*
|
|
* Every tool inputSchema declares `additionalProperties: false` (guarded by
|
|
* strict-schemas.test.ts), but hosts do not reliably enforce it, so a
|
|
* misspelled parameter used to be dropped silently: gnubok_query_journal
|
|
* called with {query} instead of {text} returned the whole journal with
|
|
* applied_filters.text null (feedback seq 261545). Unknown top-level keys are
|
|
* a caller error, never data, and are rejected before execute().
|
|
*
|
|
* company_id is tolerated everywhere: the company-routing layer strips it for
|
|
* company-dependent tools, and a client that always sends it must not break
|
|
* on the few tools that ignore it.
|
|
*/
|
|
export function listArgKeys(inputSchema: Record<string, unknown>): string[] {
|
|
const properties = inputSchema.properties
|
|
if (!properties || typeof properties !== 'object') return []
|
|
return Object.keys(properties as Record<string, unknown>)
|
|
}
|
|
|
|
export function findUnknownArgKeys(
|
|
inputSchema: Record<string, unknown>,
|
|
args: Record<string, unknown>,
|
|
): string[] {
|
|
if (inputSchema.additionalProperties !== false) return []
|
|
const allowed = new Set(listArgKeys(inputSchema))
|
|
return Object.keys(args).filter((key) => key !== 'company_id' && !allowed.has(key))
|
|
}
|