anthropics / anthropics/anthropic-sdk-typescript

BetaLocalFilesystemMemoryTool.str_replace: a multi-line old_str never matches, and an omitted new_str writes the word "undefined"

Aberta Para iniciantes
#1,180 0 comentários 0 reações 0 responsáveis Ver no GitHub
Linguagem predominante
TypeScript
Estrelas
2.1k
Forks
403
Merge médio
1d 21h
PRs com merge (30d)
8

Descrição

**SDK version:** `@anthropic-ai/sdk` 0.124.0; the same code is in `src/tools/memory/node.ts` on `main` as of 2026-09-09.
**Runtime:** Node v24.9.0, macOS 15.

`BetaLocalFilesystemMemoryTool.str_replace` has two defects. Both surface with real `str_replace` calls Claude makes when it uses the memory tool.

### 1. A multi-line `old_str` never matches

The uniqueness check walks the file line by line and tests each line with `includes`:

```ts
const lines = content.split('\n');
const matchingLines: number[] = [];
lines.forEach((line, index) => {
if (line.includes(command.old_str)) {
matchingLines.push(index + 1);
}
});
if (matchingLines.length === 0) {
throw new Error(`No replacement was performed, old_str \`${command.old_str}\` did not appear verbatim in ${command.path}.`);
}
```

No single line can contain a string with a newline in it, so any `old_str` that spans lines is reported as absent even when the file contains it verbatim. Claude does send multi-line `old_str` values: in a live session on `claude-opus-5` it issued `old_str: "## Notes\n- (none yet)"` against a file that contained exactly that text, received the "did not appear verbatim" error, and retried with a single-line match. The Python SDK's `BetaLocalFilesystemMemoryTool` counts occurrences over the whole content (`content.count(command.old_str)`), so it does not have this problem.

### 2. An omitted `new_str` writes the word `undefined` into the file

The memory tool documentation says `new_str` is optional for `str_replace`: "when it's omitted, `old_str` is deleted without a replacement." The implementation does `content.replace(command.old_str, command.new_str)`, so a missing `new_str` writes the literal string `undefined` and reports success.

### Repro

```ts
import fs from 'node:fs/promises';
import os from 'node:os';
import path from 'node:path';
import { BetaLocalFilesystemMemoryTool } from '@anthropic-ai/sdk/tools/memory/node';

const base = await fs.mkdtemp(path.join(os.tmpdir(), 'memory-repro-'));
const memory = await BetaLocalFilesystemMemoryTool.init(base);

// 1. multi-line old_str, present verbatim
await memory.create({ command: 'create', path: '/memories/notes.md', file_text: 'line1\nline2\nline3\n' });
await memory.str_replace({ command: 'str_replace', path: '/memories/notes.md', old_str: 'line1\nline2', new_str: 'joined' });
// Error: No replacement was performed, old_str `line1
// line2` did not appear verbatim in /memories/notes.md.

// 2. new_str omitted
await memory.create({ command: 'create', path: '/memories/omit.md', file_text: 'keep DELETEME keep\n' });
await memory.str_replace({ command: 'str_replace', path: '/memories/omit.md', old_str: 'DELETEME' } as any);
console.log(JSON.stringify(await fs.readFile(path.join(base, 'memories/omit.md'), 'utf8')));
// "keep undefined keep\n"
```

### Expected

- `old_str` is matched against the whole file content, as the Python SDK does: `'line1\nline2'` is found once and replaced, leaving `joined\nline3\n`.
- A missing `new_str` behaves as `''`, leaving `keep keep\n`.

### Suggested fix

Search the full content with `indexOf` (collecting the 1-based line number of each hit for the "Multiple occurrences ... in lines:" message) and default `new_str` to `''`. The open PR #1178 fixes the `$&` replacement-pattern expansion in the same `content.replace(...)` statement; building the new content by string concatenation would cover all three at once.

Guia de contribuição

Abrir o guia de contribuição

Direção de pesquisa

Start with src/tools/memory/node.ts and the BetaLocalFilesystemMemoryTool.str_replace entry point. Reproduce the multi-line replacement and omitted new_str cases from the issue, then verify that whole-file matching works and omission deletes old_str without writing undefined; the expected file contents provide the completion check.

Escrita pelo modelo de indexação a partir do texto da issue.

Avaliação

Stack de tecnologia
node.js, typescript
Domínio
tooling
Tipo de issue
Bug
Dificuldade
2/5
Tempo estimado
1-3 horas
Status de atividade
Ativa
Clareza
Claramente especificada
Facilidade para iniciantes
85/100

Receba novas issues na sua caixa de entrada

Um resumo curto de issues do GitHub para quem está começando.