Skip to content

v0.11.0 — Memory-as-tool DX: Recall/Remember tools

Choose a tag to compare

@dberry37388 dberry37388 released this 18 Jun 05:30
· 115 commits to main since this release
72bd70c

Memory-as-tool DX. First-class Recall/Remember tools agents can use mid-prompt, streaming-compatible. make:memory-tool generator and recipe book for common patterns. Purely additive — no breaking changes; existing apps upgrade with no required action.

Added

  • Recall and Remember agent memory tools (#128). New BuiltByBerry\LaravelSwarm\Tools\Recall and Tools\Remember implement Laravel\Ai\Contracts\Tool, so an agent can read and write Swarm memory mid-prompt with no Swarm-specific code — drop them into any laravel/ai agent's tools() array. Recall reads by key, prefix, or whole scope; Remember writes a key/value. Both take a scope name (default run, with swarm for shared state) but resolve the scope id from the active run, never from the model, so an agent cannot address another run's or swarm's memory. Recall reads through the active swarm's MemoryPropagationPolicy (it can only surface what the policy permits — no leaking from withheld scopes); Remember writes through SwarmMemory::put() so the MemoryCapturePolicy redacts or drops at the write boundary, and reserved swarm: keys are rejected. Tool-driven writes are tagged with an origin of tool:remember (plus the agent class when the tool is agent-bound) in the entry metadata, so MemoryWritten audit listeners can tell a model-initiated write apart from a framework write. Both work across all four topologies and nested runs, and degrade to a graceful "memory is not available" message outside a swarm run. The new Concerns\HasSwarmMemoryTools trait plus swarm.memory.tools.{enabled,recall,remember} config provide optional, default-off, container-resolvable registration (bind a subclass to customise a tool or bind it to a specific agent for agent-scope addressing). See Swarm Memory.
  • Recall and Remember work inside streamed agent responses (#129). The memory tools now have end-to-end coverage inside $agent->stream(...). Because both implement Laravel\Ai\Contracts\Tool, laravel/ai already drives their invocation during a streamed turn, so the tool call and result surface in the StreamableSwarmResponse as ordinary swarm_tool_call / swarm_tool_result events — no streaming-specific tool code. The sequential stream runner publishes the active run before invoking the final agent's stream(), so a streamed Recall resolves its scope id from the ambient run (and sees what earlier steps wrote) and a streamed Remember write lands in memory before its result event is yielded and is visible to later steps. The snapshot mechanism captures the call's input and output: the runner holds each ToolCall until its matching ToolResult arrives, then appends a single paired entry to the step snapshot (a call left unmatched at stream end is recorded with a null result so replay can detect a partial run). Combined with persisted stream replay, a streamed run that used the memory tools replays byte-identically via SwarmHistory::replay($runId). This release adds the integration and capture tests proving the path; no new runtime logic was required. See docs/memory.md.
  • make:memory-tool generator (#130). New php artisan make:memory-tool {name} Artisan command scaffolds a custom Swarm memory tool under app/Ai/Tools/, following the v0.8 make:swarm:* generator pattern (#101 / #91) and the app/Ai/ starter-example conventions (#89). The generated class extends one of the shipped Recall / Remember tools (#128), so a custom tool inherits the same shape and safety guarantees — scope ids resolve from the active run (never from the model), reads honour the propagation policy, and writes flow through the capture policy. --scope=run|conversation|agent|swarm seeds the tool's default MemoryScope; --base=recall|remember chooses the read- or write-tool shape (default recall); --vector scaffolds a semantic, vector-aware variant but only when the builtbyberry/laravel-swarm-memory-vector companion is installed (detected via Composer's InstalledVersions), erroring with an install hint otherwise; --force overwrites an existing file. Unknown --scope / --base values exit 1 with the valid options. Stubs are publishable via vendor:publish --tag=swarm-stubs (swarm.memory-tool.stub, swarm.memory-tool.vector.stub). See Generators.
  • Octane worker-reset flush for ActiveRunContext (#171). When laravel/octane is installed, SwarmServiceProvider now wires a listener on Octane's OperationTerminated contract that calls ActiveRunContext::flush() on every worker reset (request, task, and tick), so a stale run frame left by an abnormally-terminated run — a hard timeout or fatal that bypasses the enter()/exit() finally — is cleared eagerly instead of relying on the next run to shadow it. Opt-in by presence: the listener is registered only when Octane's contract is loadable, so non-Octane apps see zero behaviour change and the package never hard-depends on Octane. swarm:install:memory now surfaces a confirmation note (and the manual config/octane.php equivalent) when it detects Octane.

Documentation

  • Memory-as-tool recipe book (#131). New docs/memory-recipes.md is the pattern companion to the Recall and Remember tools reference — the "here's how people actually use this" cookbook that anchors the memory contracts to real, copy-paste shipping patterns. Five worked recipes, each built on the tools' real extension hooks (so each is a make:memory-tool-scaffoldable subclass) and stating a problem, code, and when-to-use guidance: per-user scoped recall (override Recall::visibleEntries() to filter to auth()->id()'s keys, so the model can't read across users), tenant-isolated memory (the honest mechanism — partition by swarm class or enforce a tenant-aware MemoryPropagationPolicy — reinforcing the existing warning that swarm scope is shared across every tenant of a class), policy-enforced custom Recall (bake a scope/key allow-list into the tool via resolveScope() + visibleEntries() so the read boundary survives prompt injection), recall + redact for compliance (bind a MemoryCapturePolicy returning CaptureDecision::Redact so PII an agent Remembers never lands unredacted and later Recalls read back [redacted]), and sub-agent with memory continuity (make the agent scope addressable by overriding agent(), giving a reusable sub-agent state that persists across runs). Cross-links the tool reference (docs/memory.md), the generator (docs/generators.md), and the capture-policy and compliance guides; registered in the docs index and cross-linked from docs/memory.md.

Testing

  • Rollback schema tests assert table presence instead of --step counts (#157). MemoriesSchemaTest, MemorySnapshotsSchemaTest, and RunIdForeignKeysTest no longer hard-code migrate:rollback --step N counts — they roll back by locating each migration and asserting the table is actually gone, so adding a new migration no longer silently breaks an unrelated rollback assertion. Test-only change — no src/ behavior change.
  • Coverage and mutation CI memory limit raised to 1G (#179). The tests, nightly, and mutation workflows run their coverage/mutation passes with memory_limit=1G so the growing suite — the v0.11 memory-tool topology matrix and streaming fixtures — no longer risks OOM under coverage instrumentation. CI-only change.