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
15 changes: 15 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
root = true

[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
indent_style = space
indent_size = 2

[*.py]
indent_size = 4

[*.md]
trim_trailing_whitespace = false
9 changes: 9 additions & 0 deletions .git-blame-ignore-revs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Revisions listed here are skipped by `git blame` so large mechanical changes
# (e.g. one-time formatter runs) don't obscure authorship.
#
# Enable locally with:
# git config blame.ignoreRevsFile .git-blame-ignore-revs
# GitHub honors this file automatically.

# style: apply Prettier formatting across the codebase
293cf66a141feebe053faa6f74f310f9cbfad41c
14 changes: 14 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -1,5 +1,19 @@
# Force LF on checkout across all platforms so `prettier --check`
# (endOfLine: lf) and the toolchain behave identically on Windows.
*.js text eol=lf
*.cjs text eol=lf
*.mjs text eol=lf
*.ts text eol=lf
*.json text eol=lf
*.md text eol=lf
*.svg text eol=lf
*.yml text eol=lf
*.yaml text eol=lf

# Binary assets must never be line-ending normalized.
*.png binary
*.jpg binary
*.jpeg binary
*.gif binary
*.ico binary
*.pdf binary
4 changes: 4 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ jobs:
- run: npm ci
- run: npm run build
- run: npm run typecheck
- run: npm run lint
- run: npm run format:check
- run: npm test
- run: npm run bench:memory:check
- run: npm run smoke:cli
Expand Down Expand Up @@ -96,6 +98,8 @@ jobs:
- run: npm ci
- run: npm run build
- run: npm run typecheck
- run: npm run lint
- run: npm run format:check
- run: npm test
- run: npm run bench:memory:check
- run: npm run smoke:cli
Expand Down
20 changes: 20 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
dist/
node_modules/
.tmp-vitest/
.tmp/
.archive/
coverage/
package-lock.json

# Generated artifacts
benchmarks/output/
benchmarks/.tmp/
benchmarks/.tmp-guardbench/
docs/paper/output/

# Python sources are formatted by Python tooling, not Prettier
python/

# Markdown and docs keep their hand-tuned formatting (README has inline HTML);
# Prettier is scoped to code via the `format` npm script globs.
*.md
9 changes: 9 additions & 0 deletions .prettierrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"singleQuote": true,
"semi": true,
"tabWidth": 2,
"trailingComma": "all",
"arrowParens": "avoid",
"printWidth": 100,
"endOfLine": "lf"
}
30 changes: 30 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,35 @@
# Changelog

## 1.0.2 - 2026-05-28

Maintenance and engineering-quality release. No runtime behavior change — the
full test suite is unchanged from 1.0.1.

### Security

- Pin transitive `qs` to `^6.15.2` via `overrides` to resolve
[GHSA-q8mj-m7cp-5q26](https://github.com/advisories/GHSA-q8mj-m7cp-5q26)
(moderate denial-of-service in `qs.stringify`), which reaches `audrey` through
`@modelcontextprotocol/sdk → express@5`. The advisory was published after the
1.0.1 cut; production `npm audit --omit=dev --audit-level=moderate` is clean
again.

### Tooling and code quality

- Add flat-config ESLint with type-checked `typescript-eslint` rules over `src/`
and `mcp-server/`, plus Prettier and `.editorconfig` matched to the existing
house style. New scripts: `lint`, `lint:fix`, `format`, `format:check`.
- Wire `lint` and `format:check` into CI (Ubuntu matrix + Windows) and the
`release:gate`, `release:gate:sandbox`, and `release:gate:paper` gates so the
enforced baseline cannot regress.
- Resolve every lint finding at the source rather than by suppression: the REST
handlers now decode request bodies through a typed `RouteBody` contract
instead of Hono's default `any`; the three MCP `server` parameters and the
local embedding pipeline are typed structurally; rethrows attach an error
`cause`; and dead imports/bindings were removed across the tree.
- One-time Prettier normalization across the codebase, recorded in
`.git-blame-ignore-revs` so `git blame` stays meaningful.

## 1.0.1 - 2026-05-15

### Honest benchmarking
Expand Down
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ npx audrey guard --tool Bash "npm run deploy"
Expected first-run shape:

```text
Audrey Doctor v1.0.0
Audrey Doctor v1.0.2
Store health: not initialized
Verdict: ready
```
Expand Down Expand Up @@ -535,10 +535,12 @@ Developer setup runs from source, not from the published tarball, so `npm run bu
```bash
npm ci
npm run build
npm run lint # ESLint (type-checked typescript-eslint); CI requires it clean
npm run format # Prettier; use `npm run format:check` to verify without writing
npm test
```

Once built, the `Quick Start` commands work against the local `dist/` output. The full release gate runs everything CI runs:
Once built, the `Quick Start` commands work against the local `dist/` output. Code style and types are enforced: `npm run lint` and `npm run format:check` run in CI (Ubuntu + Windows) and in every release gate, so the baseline cannot regress. The full release gate runs everything CI runs:

```bash
npm run release:gate
Expand Down
8 changes: 6 additions & 2 deletions benchmarks/adapter-self-test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,9 @@ export async function runGuardBenchAdapterSelfTest(options = {}) {
};
const schemaErrors = validateAdapterSelfTestReport(selfTest);
if (schemaErrors.length > 0) {
throw new Error(`GuardBench adapter self-test schema validation failed: ${schemaErrors.join('; ')}`);
throw new Error(
`GuardBench adapter self-test schema validation failed: ${schemaErrors.join('; ')}`,
);
}

if (options.out && options.write !== false) {
Expand Down Expand Up @@ -146,7 +148,9 @@ async function main() {
console.log(JSON.stringify(result, null, 2));
} else if (result.ok) {
console.log(`GuardBench adapter self-test passed: ${result.adapter.name}`);
console.log(`Contract rows: ${result.conformance.scenarios}/${result.conformance.expectedScenarios}`);
console.log(
`Contract rows: ${result.conformance.scenarios}/${result.conformance.expectedScenarios}`,
);
console.log(`Full-contract score: ${(result.score.fullContractPassRate * 100).toFixed(1)}%`);
console.log(`Decision accuracy: ${(result.score.decisionAccuracy * 100).toFixed(1)}%`);
if (result.outPath) console.log(`Self-test report: ${result.outPath}`);
Expand Down
7 changes: 5 additions & 2 deletions benchmarks/adapters/example-allow.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import { defineGuardBenchAdapter } from '../adapter-kit.mjs';

export default defineGuardBenchAdapter({
name: 'Example Allow Adapter',
description: 'Credential-free GuardBench adapter example. It always allows and is useful for adapter-loading smoke tests.',
description:
'Credential-free GuardBench adapter example. It always allows and is useful for adapter-loading smoke tests.',
async setup({ scenario }) {
return {
memoryCount: (scenario.seed.seededMemories ?? []).length,
Expand All @@ -19,7 +20,9 @@ export default defineGuardBenchAdapter({
summary: [
`Example adapter loaded ${state.memoryCount} seeded memories`,
`${state.toolEventCount} seeded tool events`,
scenario.seed.seededNoise ? `${scenario.seed.seededNoise.count} noise memories` : 'no noise block',
scenario.seed.seededNoise
? `${scenario.seed.seededNoise.count} noise memories`
: 'no noise block',
state.hasFaultInjection ? 'fault injection present but unsupported' : 'no fault injection',
].join('; '),
};
Expand Down
31 changes: 19 additions & 12 deletions benchmarks/adapters/mem0-platform.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,7 @@ function memoryText(memory) {
}

function evidenceIds(memories) {
return memories
.map(memory => memory?.id ?? memory?.memory_id)
.filter(Boolean);
return memories.map(memory => memory?.id ?? memory?.memory_id).filter(Boolean);
}

function decisionFromMemories(memories, action, unsupportedFault = null) {
Expand All @@ -62,7 +60,9 @@ function decisionFromMemories(memories, action, unsupportedFault = null) {
decision: 'warn',
riskScore: 0.55,
evidenceIds: evidenceIds(memories),
recommendedActions: ['External adapter cannot inject storage faults into Mem0 Platform; verify memory health separately.'],
recommendedActions: [
'External adapter cannot inject storage faults into Mem0 Platform; verify memory health separately.',
],
summary: `Mem0 adapter cannot emulate fault injection: ${unsupportedFault}.`,
};
}
Expand Down Expand Up @@ -129,7 +129,9 @@ class Mem0PlatformClient {

if (!response.ok && response.status !== 204) {
const body = await response.text();
throw new Error(`Mem0 ${options.method ?? 'GET'} ${path} failed ${response.status}: ${body.slice(0, 500)}`);
throw new Error(
`Mem0 ${options.method ?? 'GET'} ${path} failed ${response.status}: ${body.slice(0, 500)}`,
);
}

if (response.status === 204) return null;
Expand Down Expand Up @@ -175,7 +177,7 @@ class Mem0PlatformClient {
filters: { user_id: userId },
}),
});
return Array.isArray(response) ? response : response?.results ?? [];
return Array.isArray(response) ? response : (response?.results ?? []);
}

async deleteUser(userId) {
Expand All @@ -191,9 +193,10 @@ function memoryMessagesFromScenario(scenario) {
messages.push({ role: 'user', content: memory.content });
}
for (const event of scenario.seed.seededToolEvents ?? []) {
const seededSecret = event.errorSummaryPattern && scenario.privateSeed?.seededSecrets?.[0]
? `${'x'.repeat(1990)} ${scenario.privateSeed.seededSecrets[0]}`
: '';
const seededSecret =
event.errorSummaryPattern && scenario.privateSeed?.seededSecrets?.[0]
? `${'x'.repeat(1990)} ${scenario.privateSeed.seededSecrets[0]}`
: '';
messages.push({
role: 'user',
content: [
Expand All @@ -204,7 +207,9 @@ function memoryMessagesFromScenario(scenario) {
event.errorSummaryPattern ? `Error pattern: ${event.errorSummaryPattern}` : '',
seededSecret ? `Error: ${seededSecret}` : '',
event.output ? `Output: ${event.output}` : '',
].filter(Boolean).join('\n'),
]
.filter(Boolean)
.join('\n'),
});
}
if (scenario.seed.seededNoise?.count) {
Expand Down Expand Up @@ -234,14 +239,16 @@ async function addInBatches(client, { userId, scenario, messages }) {

function userIdForScenario(scenario) {
const prefix = process.env.MEM0_GUARDBENCH_USER_PREFIX ?? 'audrey-guardbench';
const runId = process.env.MEM0_GUARDBENCH_RUN_ID ?? `${Date.now()}-${randomBytes(8).toString('hex')}`;
const runId =
process.env.MEM0_GUARDBENCH_RUN_ID ?? `${Date.now()}-${randomBytes(8).toString('hex')}`;
return `${prefix}-${runId}-${scenario.id}`.toLowerCase();
}

export function createGuardBenchAdapter(options = {}) {
return {
name: 'Mem0 Platform',
description: 'Mem0 Platform REST adapter using V3 add, V2 search, event polling, and entity cleanup.',
description:
'Mem0 Platform REST adapter using V3 add, V2 search, event polling, and entity cleanup.',
async setup({ scenario }) {
const client = new Mem0PlatformClient(options);
const userId = userIdForScenario(scenario);
Expand Down
Loading
Loading