Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/fix-approx-count-serialize.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"flint": patch
---

Harden `approxCount` (and the `count` fallback) against non-serializable tool-call arguments. `JSON.stringify` could throw on circular references or `BigInt` values, or return `undefined` for a bare `undefined`, crashing a pure token estimate. Such arguments now contribute 0 tokens instead of throwing.
5 changes: 5 additions & 0 deletions .changeset/fix-memory-append-type.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"flint": patch
---

Fix `ConversationMemory.append` type signature: it was declared as returning `void` but the implementation is `async` and performs summarization. The interface now correctly returns `Promise<void>` so callers don't silently drop the promise.
5 changes: 5 additions & 0 deletions .changeset/fix-redact-private-keys.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"flint": patch
---

Broaden the private-key pattern in the `secretPatterns` redaction preset. The previous regex only matched key types made of uppercase letters and spaces, so it missed the most common modern formats — generic PKCS#8 `-----BEGIN PRIVATE KEY-----` (no type word), `ENCRYPTED PRIVATE KEY`, and types containing digits or hyphens. These are now redacted.
2 changes: 1 addition & 1 deletion packages/flint/src/memory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ export type ConversationMemoryOpts = {
};

export type ConversationMemory = {
append(m: Message): void;
append(m: Message): Promise<void>;
messages(): Message[];
summary(): string | undefined;
clear(): void;
Expand Down
11 changes: 10 additions & 1 deletion packages/flint/src/primitives/approx-count.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,16 @@ export function approxCount(messages: Message[]): number {
if (msg.role === 'assistant' && msg.toolCalls) {
for (const tc of msg.toolCalls) {
total += ROLE_OVERHEAD;
total += textTokens(JSON.stringify(tc.arguments));
// tc.arguments is `unknown` — JSON.stringify can throw (circular refs,
// BigInt) or return undefined (a bare undefined value). Either case must
// not crash a pure token estimate, so fall back to 0 for the arguments.
let serialized: string | undefined;
try {
serialized = JSON.stringify(tc.arguments);
} catch {
serialized = undefined;
}
total += serialized === undefined ? 0 : textTokens(serialized);
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion packages/flint/src/safety/redact.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export const secretPatterns: RegExp[] = [
/gho_[a-zA-Z0-9]{36}/g,
/xox[baprs]-[a-zA-Z0-9-]{10,}/g,
/sk_(?:live|test)_[a-zA-Z0-9]{24,}/g,
/-----BEGIN [A-Z ]+ PRIVATE KEY-----[\s\S]+?-----END [A-Z ]+ PRIVATE KEY-----/g,
/-----BEGIN (?:[A-Z0-9 -]+ )?PRIVATE KEY-----[\s\S]+?-----END (?:[A-Z0-9 -]+ )?PRIVATE KEY-----/g,
/\b\d{3}-\d{2}-\d{4}\b/g,
/\b(?:\d{4}[\s-]?){3}\d{4}\b/g,
];
19 changes: 19 additions & 0 deletions packages/flint/test/count.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,25 @@ describe('approxCount', () => {
const more: Message[] = [...base, { role: 'assistant', content: 'hi back' }];
expect(approxCount(more)).toBeGreaterThanOrEqual(approxCount(base));
});

it('does not throw on non-serializable tool arguments', () => {
const circular: Record<string, unknown> = {};
circular.self = circular;
const msgs: Message[] = [
{
role: 'assistant',
content: '',
toolCalls: [
{ id: 'c1', name: 'circular', arguments: circular },
{ id: 'c2', name: 'bigint', arguments: { n: 1n } },
{ id: 'c3', name: 'undef', arguments: undefined },
],
},
];
// role 4 + 3 tool-call overheads (4 each); unserializable args contribute 0.
expect(() => approxCount(msgs)).not.toThrow();
expect(approxCount(msgs)).toBe(4 + 4 * 3);
});
});

describe('count', () => {
Expand Down
12 changes: 12 additions & 0 deletions packages/flint/test/safety/redact.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,18 @@ describe('secretPatterns preset', () => {
['Stripe live key', 'sk_live_abcdefghijklmnopqrstuvwx'],
['SSN', 'SSN: 123-45-6789'],
['Credit card', 'card 4111-1111-1111-1111'],
[
'RSA private key',
'-----BEGIN RSA PRIVATE KEY-----\nMIIBOgIBAAJBAKj\n-----END RSA PRIVATE KEY-----',
],
[
'generic PKCS#8 private key',
'-----BEGIN PRIVATE KEY-----\nMIGHAgEAMBMGBy\n-----END PRIVATE KEY-----',
],
[
'encrypted private key',
'-----BEGIN ENCRYPTED PRIVATE KEY-----\nMIIFLTBXBgkq\n-----END ENCRYPTED PRIVATE KEY-----',
],
];

for (const [label, text] of cases) {
Expand Down
Loading