Files
LLeMbas-CLI/src/tool/memory.ts
T
HomerandClaude Opus 5.5 539896494f Harness spec 1.2.0: the library's tools fit both projects; the CLI implements it
The CLI's facts in memory, notes_search, note_manage, skills_list,
skill_view and skill_manage are `variables` now, filled with the CLI's own
values (src/tool/spec-vars.ts): every description and parameter list the CLI
sends is word for word what 1.1.0 said, except note and document ids, which
are strings on both sides (a number is still taken). Descriptions are
filled by the prompt texts' rules, parameter descriptions too. `narrow`
says what a project offers of a shared tool's parameters. Memory's changes
are one pure function (applyOps). Conformance: fill, narrow, memory.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 11:45:14 +00:00

42 lines
2.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// The memory tool, after Hermes Agent's (tools/memory_tool.py, MIT, © Nous Research): one tool,
// a single change or a batch applied together, checked against the size limit as a whole.
import { z } from "zod"
import { MemoryStore, type Op } from "../memory/store.ts"
import { defineTool, ToolError } from "./tool.ts"
import { described } from "../harness.ts"
import { CLI_VARS } from "./spec-vars.ts"
const OpSchema = z.object({
action: z.enum(["add", "replace", "remove"]),
content: z.string().optional(),
new_text: z.string().optional(),
old_text: z.string().optional(),
})
export const memoryTool = defineTool({
...described("memory", CLI_VARS.memory),
access: "memory",
schema: z.object({
target: z.enum(["memory", "user", "project"]),
action: z.enum(["add", "replace", "remove"]).optional(),
content: z.string().optional(),
new_text: z.string().optional(),
old_text: z.string().optional(),
operations: z.array(OpSchema).optional(),
}),
permission: (a) => ({ permission: "memory", class: "interact", patterns: [a.target] }),
async run(a, ctx) {
if (a.target === "project" && !ctx.projectMemory) throw new ToolError("This project has no project memory: it is not trusted, or has no .agent directory. Use 'memory' for facts that hold everywhere.")
const store = a.target === "project" ? ctx.projectMemory! : (ctx.memory ?? new MemoryStore())
const ops: Op[] = a.operations?.length ? a.operations : a.action ? [{ action: a.action, content: a.content, new_text: a.new_text, old_text: a.old_text }] : []
if (!ops.length) throw new ToolError("Nothing to do: give action (with content and/or old_text), or operations.")
const r = store.apply(a.target === "project" ? "memory" : a.target, ops)
const label = a.target === "user" ? "user profile" : a.target === "project" ? "project memory" : "memory"
if (!r.ok) {
const list = r.entries ? `\n\nCurrent entries (${r.usage}):\n${r.entries.map((e, i) => `${i + 1}. ${e}`).join("\n") || "(none)"}` : `\n\n${label}: ${r.usage}`
return { output: r.message + list, title: `${label}: refused`, isError: true }
}
return { output: `${r.message} ${label}: ${r.usage}. Saved — this update is complete; do not repeat it.`, title: `${label} ${ops.map((o) => (o.action === "add" ? "+" : o.action === "remove" ? "−" : "~")).join("")} · ${r.usage.split(" — ")[0]}` }
},
})