Memory

Working Memory

A scratchpad memory layer that persists structured or free-form notes across turns within a single execution.

Overview

Working Memory is the simplest built-in memory layer. It acts as a scratchpad that the agent can read and write each turn. The LLM sees the current state as a <working_memory> block in the prompt, and updates it by calling an updateWorkingMemory tool.

  • Slot: 100 (Slot.WORKING_MEMORY)
  • Default scope: execution
  • Default budget: { min: 200, max: 1500 }

Usage

import { workingMemory } from '@noetic/core';

const layer = workingMemory({
  scope: 'thread',
  readOnly: false,
});

Register the layer in your agent config:

const agent: AgentConfig = {
  name: 'assistant',
  description: 'A helpful assistant',
  model: 'gpt-4o',
  instructions: 'You are a helpful assistant.',
  memory: [layer],
  storage: myStorageAdapter,
};

Configuration

interface WorkingMemoryConfig {
  scope?: 'thread' | 'resource';
  schema?: ZodType;
  template?: string;
  readOnly?: boolean;
}
FieldTypeDefaultPurpose
scope'thread' | 'resource''thread'Persistence boundary
schemaZodType--Optional Zod schema for structured state (state becomes Record<string, unknown>)
templatestring--Initial template content
readOnlybooleanfalseWhen true, the store hook skips updates

State Type

type WorkingMemoryState = string | Record<string, unknown>;

When no schema is provided, state is a plain string. When a schema is provided, state is an object and updates are shallow-merged.

How It Works

init

Loads saved state from ScopedStorage. Falls back to an empty string (no schema) or empty object (with schema).

recall

If state is non-empty, wraps it in a <working_memory> XML block and injects it as a developer message item.

store

Watches for updateWorkingMemory function calls in the LLM response. When found, parses the JSON arguments and shallow-merges them into the current state. Prototype keys (__proto__, constructor) are stripped for safety.

If readOnly is true, the store hook is a no-op.

onSpawn

When scope is 'resource', the parent state is deep-cloned to the child. For 'thread' scope, children do not inherit working memory.

Example: Structured Scratchpad

import { z } from 'zod';
import { workingMemory } from '@noetic/core';

const layer = workingMemory({
  schema: z.object({
    currentGoal: z.string(),
    completedSteps: z.array(z.string()),
    blockers: z.array(z.string()),
  }),
});

The LLM can then call updateWorkingMemory({ currentGoal: 'Deploy v2', completedSteps: ['write tests'] }) to update specific fields without overwriting the rest.

On this page