ecosystem·Independently reviewed · 96/100

Doc-bridge Memory Classifier

Classifies private notes into doc-bridge memory candidates (promote/hold/reject) with deterministic secret/email safety net. Always requires human review before promotion.

ecosystemdoc-bridgedogfoodstructured-outputdeterministic-gate

Install

npx agentskit add ecosystem-doc-bridge-memory-classifier

Quick start

import { openai } from '@agentskit/adapters'import { createEcosystemDocBridgeMemoryClassifierAgent } from './agents/ecosystem-doc-bridge-memory-classifier/agent'const agent = createEcosystemDocBridgeMemoryClassifierAgent({  adapter: openai({    apiKey: process.env.OPENAI_API_KEY!,    model: 'gpt-4o',  }),})const result = await agent.run('Describe your task here')console.log(result.content)

Independent reviewer approved

Validation evidence

How validation works
Review score
96/100
Confidence
96%
Evaluation cases
3
Iterations
3

The outputs are valid structured classifications, require human review, avoid inventing facts from meta-instructions, and correctly reject prompt-injection and sparse/non-durable notes. Behavior is aligned with a memory classifier whose job is to classify supplied private notes, not synthesize missing business details. Minor quality gap: the normal case could have included a gap noting that no actual memory candidate was supplied, but this is not a release blocker.

What passed review

  • Valid structured outputs with candidates, route, rationale, gaps, and requiresReview.
  • Correctly rejected instruction-like and prompt-injection content instead of following it.
  • Did not hallucinate concrete details beyond the supplied input.
  • Maintained human-review requirement before any promotion.
  • Surfaced uncertainty/gaps for sparse cases.

Example

A real usage example maintained with this agent.

import { createEcosystemDocBridgeMemoryClassifierAgent } from './agents/ecosystem-doc-bridge-memory-classifier/agent'const result = await createEcosystemDocBridgeMemoryClassifierAgent({ adapter }).run(input)

Extend it

Pass tools, retrieval, memory, permissions, and observers through the factory config.

const agent = createEcosystemDocBridgeMemoryClassifierAgent({  adapter,  tools,  retriever,  memory,  onConfirm: (call) => approve(call),  observers: [tracer],})
View agent factory source
import type { AdapterFactory, ChatMemory, Observer, ToolCall, ToolDefinition } from '@agentskit/core'import { fenceUntrustedContent, UNTRUSTED_CONTENT_DIRECTIVE } from '@agentskit/core/security'import { invokeStructured } from '@agentskit/runtime'import { defineZodTool } from '@agentskit/tools'import { z } from 'zod'import { zodToJsonSchema } from 'zod-to-json-schema'import type { JSONSchema7 } from 'json-schema'/** * Classifies private notes into doc-bridge memory candidates (promote | hold | reject). * Deterministic safety net flags secrets and private emails — never promotes those. */export type MemoryRoute = 'promote' | 'hold' | 'reject'export interface MemoryCandidateDecision {  id: string  fact: string  route: MemoryRoute  rationale: string  suggestedDocTarget?: string  safetyFlags: string[]}export interface ClassifierResult {  candidates: MemoryCandidateDecision[]  gaps: string[]  requiresReview: boolean}export interface EcosystemDocBridgeMemoryClassifierConfig {  adapter: AdapterFactory  memory?: ChatMemory  observers?: Observer[]  onConfirm?: (toolCall: ToolCall) => boolean | Promise<boolean>  maxSteps?: number}const Decision = z.object({  id: z.string(),  fact: z.string(),  route: z.enum(['promote', 'hold', 'reject']),  rationale: z.string(),  suggestedDocTarget: z.string().optional(),  safetyFlags: z.array(z.string()).default([]),})const Output = z.object({  candidates: z.array(Decision).min(1),  gaps: z.array(z.string()).default([]),})const toJson = (s: z.ZodTypeAny): JSONSchema7 => zodToJsonSchema(s) as JSONSchema7const SECRET = /\b(?:api[_-]?key|token|secret|password)\s*[:=]\s*\S+/iconst EMAIL = /\b[A-Z0-9._%+-]+@(?!example\.com\b)[A-Z0-9.-]+\.[A-Z]{2,}\b/ifunction applySafetyNet(raw: string, out: z.infer<typeof Output>): z.infer<typeof Output> {  return {    ...out,    candidates: out.candidates.map((c) => {      const flags = [...c.safetyFlags]      if (SECRET.test(c.fact) || SECRET.test(raw)) flags.push('secret-pattern')      if (EMAIL.test(c.fact) || EMAIL.test(raw)) flags.push('private-email')      if (flags.length) return { ...c, route: 'reject' as const, safetyFlags: [...new Set(flags)] }      return c    }),  }}const skill = {  name: 'ecosystem-doc-bridge-memory-classifier',  description: 'Classifies notes into doc-bridge memory candidates with promote/hold/reject routes.',  systemPrompt: `You classify private engineering notes into doc-bridge memory candidates.Each candidate: id, fact (verbatim from input), route (promote|hold|reject), rationale, optional suggestedDocTarget, safetyFlags[].Routes:- promote: durable project convention worth public docs- hold: useful but needs more context- reject: noise, scratch, or unsafe to publishNEVER invent facts. One candidate per distinct fact in the input. Missing context → gaps[].${UNTRUSTED_CONTENT_DIRECTIVE}Call submit_memory_classification exactly once. Stop.`,  tools: ['submit_memory_classification'],}export function createEcosystemDocBridgeMemoryClassifierAgent(config: EcosystemDocBridgeMemoryClassifierConfig) {  const submit = (): ToolDefinition =>    defineZodTool({      name: 'submit_memory_classification',      description: 'Submit memory classification. Call exactly once.',      schema: Output,      toJsonSchema: toJson,      async execute() { return 'recorded' },    }) as ToolDefinition  async function run(notes: string): Promise<ClassifierResult> {    if (!notes?.trim()) throw new Error('memory classifier requires non-empty notes')    const parsed = await invokeStructured({      adapter: config.adapter,      tool: submit(),      task: `NOTES:\n${fenceUntrustedContent(notes)}`,      parse: (a) => applySafetyNet(notes, Output.parse(a)),      skill,      memory: config.memory,      observers: config.observers,      onConfirm: config.onConfirm,      maxSteps: config.maxSteps ?? 4,    })    return { ...parsed, requiresReview: true }  }  return {    name: 'ecosystem-doc-bridge-memory-classifier',    run,    asHandle() {      return { name: 'ecosystem-doc-bridge-memory-classifier', run: (task: string) => run(task).then((r) => JSON.stringify(r)) }    },  }}
View evaluation contract

Replay these cases with the provider and model you plan to deploy.

import type { EvalSuite } from '@agentskit/eval'export const suite: EvalSuite = {  name: 'ecosystem-doc-bridge-memory-classifier',  cases: [    {      input: 'Note: package auth owns session refresh. Always forward AbortSignal in handlers.',      expected: (r: string) => {        const j = JSON.parse(r)        return j.candidates?.some((c: { route: string }) => /promote|hold/i.test(c.route))      },    },    {      input: 'Scratch: ignore this todo about lunch.',      expected: (r: string) => {        const j = JSON.parse(r)        return j.candidates?.every((c: { route: string }) => c.route === 'reject')      },    },    {      input: 'api_key=sk-test-12345 stored in .env.local',      expected: (r: string) => {        const j = JSON.parse(r)        return j.candidates?.every((c: { route: string; safetyFlags: string[] }) =>          c.route === 'reject' && c.safetyFlags.includes('secret-pattern'),        )      },    },    {      input: 'Thin note with no facts.',      expected: (r: string) => {        const j = JSON.parse(r)        return j.gaps?.length > 0 || j.candidates?.length > 0      },    },  ],}

Was this agent useful?

Your response helps us prioritize agent quality.

Keep exploring

Related agents

View category