feat(ux): surface memory/skill saves in Activity summary (#3544)

Absorbs contributor PR #3544 (@rodboev, closes #3340) with two fixes:

1. DETECTION VOCAB (would never fire): the original gated on action names
   {save,create,update,upsert}, which don't match the real agent tool enums —
   memory.action is add|replace|remove, skill_manage.action is
   create|patch|edit|delete|write_file|remove_file. Split into per-tool
   predicates with the correct vocabularies: _isMemorySave gates memory on
   {add,replace}; _isSkillUpdate gates skill_manage on {create,patch,edit,
   write_file}. Deletions excluded so the saved/updated verbs stay accurate;
   running/errored excluded.

2. SNAPSHOT/RESTORE PERSISTENCE (Codex catch): classification lived only on the
   row._tcData JS property, which does NOT survive the outerHTML/innerHTML
   snapshot+restore the live tool-call group uses on session switch/restore —
   a restored memory/skill row would be re-counted as a generic tool and the
   suffix would silently vanish. buildToolCard now also stamps durable
   data-memory-save / data-skill-update attributes, and _syncToolCallGroupSummary
   counts them as a fallback when _tcData is absent. Verified live across a real
   outerHTML round-trip: label identical before/after.

Replaces the PR's static source assertions with a node-driven behavioral test
(11 cases) covering the real action vocabularies, exclusions, case-insensitivity,
null-arg safety, and the durable-attribute persistence guard.

Co-authored-by: rodboev <rodboev@users.noreply.github.com>
This commit is contained in:
nesquena-hermes
2026-06-05 22:41:43 +00:00
parent e663bc98d6
commit b26bb559d5
4 changed files with 234 additions and 4 deletions
@@ -116,6 +116,10 @@ def test_rendered_apply_patch_tool_card_html_contains_diff_lines():
# #3336: buildToolCard now wraps diff snippets via these helpers.
"_snippetLooksLikeDiff",
"_colorDiffLines",
# #3544: buildToolCard stamps durable memory/skill-save flags via these.
"_tcAction",
"_isMemorySave",
"_isSkillUpdate",
"buildToolCard",
]
functions = "\n".join(_function_source(UI_JS, name) for name in function_names)
@@ -125,8 +129,12 @@ def test_rendered_apply_patch_tool_card_html_contains_diff_lines():
function li(){{return '';}}
function toolIcon(){{return '';}}
function _toolDisplayName(tc){{return tc.name||'tool';}}
// #3544: const Sets the _isMemorySave/_isSkillUpdate predicates close over
// (extracted helpers reference these module-level constants).
const _MEMORY_SAVE_ACTIONS=new Set(['add','replace']);
const _SKILL_UPDATE_ACTIONS=new Set(['create','patch','edit','write_file']);
const document={{
createElement(){{return {{className:'', innerHTML:''}};}}
createElement(){{return {{className:'', innerHTML:'', setAttribute(){{}}, removeAttribute(){{}}}};}}
}};
{functions}