fix(fs): quote a listed name only when it would misreport the listing

The review made every name a JSON string with `<`, `>`, and `&` escaped. The
hazards behind that are real and each is now covered: a control character
splits one entry across lines, `</` closes the envelope, and a regular file
named `x@` reads as a socket named `x` under the non-regular marker.

Quote those, and only those. `list` is the tool an agent reaches for first
and its output sits in every transcript, so `"archive"/` on every ordinary
line is a permanent cost for a case that almost never occurs. A name is now
emitted verbatim unless it matches a control character, a leading quote, a
backslash, `</`, or a trailing `@`, and is otherwise a JSON string with `</`
neutralized — the delimiter treatment `dsh-workspace-context` already applies
to instruction text, extended to an interpolated path as its
`instruction-frame-paths` TODO asks.
This commit is contained in:
NI0317
2026-07-28 13:16:24 +08:00
parent 717852423f
commit 451c21a5b6
10 changed files with 61 additions and 35 deletions

View File

@@ -15,7 +15,7 @@
{"type":"assistant/chunk","seq":47,"time":1785159115922,"data":{"turn":1,"step":1,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}}
{"type":"assistant/message","seq":48,"time":1785159115926,"data":{"turn":1,"step":1,"content":[{"type":"reasoning","text":"The user wants me to call the list tool with no arguments and then reply with the names of the subdirectories it reports, alphabetically, separated by a single space."},{"type":"tool-call","id":"call_00_BSi06gcYvl6oad3I5qqG9244","name":"list","arguments":"{}"}],"provenance":{"provider":"deepseek","model":"deepseek-v4-flash"},"usage":{"inputTokens":5513,"outputTokens":62,"cacheReadTokens":0,"reasoningTokens":35}},"sourceEventSeqs":[5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47],"surfaceOp":"append"}
{"type":"tool/call","seq":49,"time":1785159115927,"data":{"turn":1,"step":1,"callId":"call_00_BSi06gcYvl6oad3I5qqG9244","name":"list","arguments":"{}"}}
{"type":"tool/result","seq":50,"time":1785159115944,"data":{"turn":1,"step":1,"callId":"call_00_BSi06gcYvl6oad3I5qqG9244","content":[{"type":"text","text":"<path>\"/var/folders/8k/kj35k1fd6t90n0czg7k3hv140000gn/T/acp-snap-cwd-QJ2ZC6\"</path>\n<type>directory</type>\n<content>\n\"docs\"/\n\"src\"/\n\"package.json\"\n\"README.txt\"\n\n(4 entries: 2 directories, 2 files)\n</content>"}],"isError":false},"sourceEventSeqs":[49],"surfaceOp":"append"}
{"type":"tool/result","seq":50,"time":1785159115944,"data":{"turn":1,"step":1,"callId":"call_00_BSi06gcYvl6oad3I5qqG9244","content":[{"type":"text","text":"<path>/var/folders/8k/kj35k1fd6t90n0czg7k3hv140000gn/T/acp-snap-cwd-QJ2ZC6</path>\n<type>directory</type>\n<content>\ndocs/\nsrc/\npackage.json\nREADME.txt\n\n(4 entries: 2 directories, 2 files)\n</content>"}],"isError":false},"sourceEventSeqs":[49],"surfaceOp":"append"}
{"type":"step/end","seq":51,"time":1785159115950,"data":{"turn":1,"step":1}}
{"type":"step/start","seq":52,"time":1785159115951,"data":{"turn":1,"step":2}}
{"type":"assistant/chunk","seq":53,"time":1785159116889,"data":{"turn":1,"step":2,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}}}