mirror of
https://github.com/Kaelio/ktx.git
synced 2026-06-19 08:28:06 +02:00
* fix: store Metabase mappings in ktx.yaml * docs: note KTX has no public users * refactor: drop setup progress compatibility
97 lines
3.9 KiB
TypeScript
97 lines
3.9 KiB
TypeScript
import { tool, type ToolExecuteFunction, type ToolExecutionOptions, type ToolSet } from 'ai';
|
|
import { z } from 'zod';
|
|
|
|
const verificationLedgerInputSchema = z.object({
|
|
summary: z.string().min(1).max(2000),
|
|
verifiedIdentifiers: z.array(z.string().min(1)).max(100).default([]),
|
|
unverifiedIdentifiers: z.array(z.string().min(1)).max(100).default([]),
|
|
notes: z.string().max(2000).optional(),
|
|
});
|
|
|
|
interface VerificationLedgerEntry {
|
|
summary: string;
|
|
verifiedIdentifiers: string[];
|
|
unverifiedIdentifiers: string[];
|
|
notes?: string;
|
|
}
|
|
|
|
export interface VerificationLedgerState {
|
|
entries: VerificationLedgerEntry[];
|
|
}
|
|
|
|
const WRITE_TOOL_NAMES = new Set([
|
|
'wiki_write',
|
|
'wiki_remove',
|
|
'sl_write_source',
|
|
'sl_edit_source',
|
|
'emit_unmapped_fallback',
|
|
]);
|
|
|
|
export const VERIFICATION_LEDGER_PROMPT = `<pre_write_verification>
|
|
Before any write-capable tool call (wiki_write, wiki_remove, sl_write_source, sl_edit_source, emit_unmapped_fallback), call record_verification_ledger.
|
|
The ledger is a model-authored checkpoint, not a deterministic parser gate. Summarize the verification protocol from the loaded skill, list identifiers verified with discover_data/entity_details/sql_execution, and list anything intentionally left unverified. If the write contains no warehouse identifiers, say that explicitly.
|
|
If a write tool returns verification_ledger_required, complete the ledger and retry the write.
|
|
</pre_write_verification>`;
|
|
|
|
export function createVerificationLedgerState(): VerificationLedgerState {
|
|
return { entries: [] };
|
|
}
|
|
|
|
export function withVerificationLedger(tools: ToolSet, state: VerificationLedgerState): ToolSet {
|
|
const wrapped: ToolSet = {};
|
|
for (const [name, original] of Object.entries(tools)) {
|
|
if (!WRITE_TOOL_NAMES.has(name) || typeof original.execute !== 'function') {
|
|
wrapped[name] = original;
|
|
continue;
|
|
}
|
|
const originalExecute = original.execute;
|
|
const guardedExecute: ToolExecuteFunction<unknown, unknown> = async (
|
|
input: unknown,
|
|
opts: ToolExecutionOptions,
|
|
) => {
|
|
if (state.entries.length === 0) {
|
|
return verificationRequiredOutput(name);
|
|
}
|
|
return (originalExecute as ToolExecuteFunction<unknown, unknown>)(input, opts);
|
|
};
|
|
wrapped[name] = { ...original, execute: guardedExecute };
|
|
}
|
|
wrapped.record_verification_ledger = createRecordVerificationLedgerTool(state);
|
|
return wrapped;
|
|
}
|
|
|
|
function createRecordVerificationLedgerTool(state: VerificationLedgerState) {
|
|
return tool({
|
|
description:
|
|
'Record the pre-write verification ledger required by loaded ingest skills. Call this before wiki/SL/fallback writes to state what was verified, which tool calls support it, and what remains intentionally unverified.',
|
|
inputSchema: verificationLedgerInputSchema,
|
|
execute: async (input) => {
|
|
const entry = verificationLedgerInputSchema.parse(input);
|
|
state.entries.push(entry);
|
|
return {
|
|
markdown:
|
|
`Verification ledger recorded. Summary: ${entry.summary}\n` +
|
|
`Verified identifiers: ${entry.verifiedIdentifiers.length ? entry.verifiedIdentifiers.join(', ') : '(none)'}\n` +
|
|
`Unverified identifiers: ${
|
|
entry.unverifiedIdentifiers.length ? entry.unverifiedIdentifiers.join(', ') : '(none)'
|
|
}`,
|
|
structured: { success: true, entry },
|
|
};
|
|
},
|
|
});
|
|
}
|
|
|
|
function verificationRequiredOutput(toolName: string) {
|
|
return {
|
|
markdown:
|
|
`Pre-write verification required before calling ${toolName}. ` +
|
|
'Call record_verification_ledger first. In the ledger, summarize the loaded skill protocol you followed, ' +
|
|
'list identifiers verified via discover_data/entity_details/sql_execution, and list any identifiers intentionally left unverified. ' +
|
|
'If the write contains no warehouse identifiers, say that explicitly in the ledger summary.',
|
|
structured: {
|
|
success: false,
|
|
reason: 'verification_ledger_required',
|
|
toolName,
|
|
},
|
|
};
|
|
}
|