|
@@ -1428,23 +1428,216 @@ def test_static_event_prompt_refresh_uses_unified_tool_mode_state_path():
|
|
|
'toolInvocationMode.addEventListener("change", refreshToolModeState);'
|
|
'toolInvocationMode.addEventListener("change", refreshToolModeState);'
|
|
|
in js
|
|
in js
|
|
|
)
|
|
)
|
|
|
- assert render_tools.count("refreshToolModeState();") == 3
|
|
|
|
|
|
|
+ assert "refreshToolModeState();" in render_tools
|
|
|
assert "updateEventPromptItem(" not in render_tools
|
|
assert "updateEventPromptItem(" not in render_tools
|
|
|
assert "refreshToolModeState();" in set_all_tools
|
|
assert "refreshToolModeState();" in set_all_tools
|
|
|
assert "updateEventPromptItem(" not in set_all_tools
|
|
assert "updateEventPromptItem(" not in set_all_tools
|
|
|
assert "refreshToolModeState();" in restore_snapshot
|
|
assert "refreshToolModeState();" in restore_snapshot
|
|
|
assert "refreshToolModeState();" in apply_selected_tools
|
|
assert "refreshToolModeState();" in apply_selected_tools
|
|
|
assert "refreshToolModeState();" in initialize_prompt_list
|
|
assert "refreshToolModeState();" in initialize_prompt_list
|
|
|
- assert js.count("updateEventPromptItem(") == 2
|
|
|
|
|
assert "removeGeneratedEventPromptItems();" in refresh_tool_mode
|
|
assert "removeGeneratedEventPromptItems();" in refresh_tool_mode
|
|
|
assert "if (!isChatAgentTools)" in refresh_tool_mode
|
|
assert "if (!isChatAgentTools)" in refresh_tool_mode
|
|
|
assert "updateEventPromptItem(availableTools);" in refresh_tool_mode
|
|
assert "updateEventPromptItem(availableTools);" in refresh_tool_mode
|
|
|
assert 'querySelectorAll(".prompt-item")' in remove_generated_prompts
|
|
assert 'querySelectorAll(".prompt-item")' in remove_generated_prompts
|
|
|
- assert 'item.dataset.promptKind === "event_agent_system"' in remove_generated_prompts
|
|
|
|
|
- assert "isGeneratedEventPromptContent(content)" in remove_generated_prompts
|
|
|
|
|
|
|
+ assert "isGeneratedEventPromptItem(item)" in remove_generated_prompts
|
|
|
assert "item.remove();" in remove_generated_prompts
|
|
assert "item.remove();" in remove_generated_prompts
|
|
|
assert 'textarea.readOnly = promptItem.prompt_kind === "event_agent_system"' in js
|
|
assert 'textarea.readOnly = promptItem.prompt_kind === "event_agent_system"' in js
|
|
|
|
|
|
|
|
|
|
+ assert "let eventPromptInsertionIndex" in js
|
|
|
|
|
+ capture_position = js[
|
|
|
|
|
+ js.index("function captureEventPromptInsertionIndex(") :
|
|
|
|
|
+ js.index("function removeGeneratedEventPromptItems(")
|
|
|
|
|
+ ]
|
|
|
|
|
+ ensure_event_prompt = js[
|
|
|
|
|
+ js.index("function ensureEventPromptItem(") :
|
|
|
|
|
+ js.index("function bindPromptItemControls(")
|
|
|
|
|
+ ]
|
|
|
|
|
+ restore_prompts = js[
|
|
|
|
|
+ js.index("function restoreSystemPrompts(") :
|
|
|
|
|
+ js.index("function restorePreMessages(")
|
|
|
|
|
+ ]
|
|
|
|
|
+
|
|
|
|
|
+ assert "return retainedPromptCount;" in capture_position
|
|
|
|
|
+ assert "captureEventPromptInsertionIndex();" in refresh_tool_mode
|
|
|
|
|
+ assert "capturedEventPromptIndex !== null" in refresh_tool_mode
|
|
|
|
|
+ assert "eventPromptInsertionIndex = capturedEventPromptIndex;" in refresh_tool_mode
|
|
|
|
|
+ assert "deriveEventPromptInsertionIndex(prompts);" in restore_prompts
|
|
|
|
|
+ assert "eventPromptInsertionIndex = restoredEventPromptIndex;" in restore_prompts
|
|
|
|
|
+ assert "const insertionTarget" in ensure_event_prompt
|
|
|
|
|
+ assert "systemPrompts.insertBefore(item, insertionTarget);" in ensure_event_prompt
|
|
|
|
|
+ assert "systemPrompts.append(item);" not in ensure_event_prompt
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+def test_static_event_prompt_order_survives_refresh_and_tool_mode_roundtrip():
|
|
|
|
|
+ js = Path("src/agent_lab/presentation/static/app.js").read_text()
|
|
|
|
|
+ generated = "\n".join(
|
|
|
|
|
+ [
|
|
|
|
|
+ "You may request EventAgent work using this text protocol.",
|
|
|
|
|
+ "Available events:",
|
|
|
|
|
+ "- handoff_note: Handoff a note",
|
|
|
|
|
+ "<agent_events>",
|
|
|
|
|
+ "handoff_note",
|
|
|
|
|
+ "</agent_events>",
|
|
|
|
|
+ "Use exact event names only, one per line. Do not include parameters.",
|
|
|
|
|
+ ]
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ assert "function captureEventPromptInsertionIndex(" in js
|
|
|
|
|
+ refresh_tool_mode = js[
|
|
|
|
|
+ js.index("function refreshToolModeState(") :
|
|
|
|
|
+ js.index("function updateToolModeHelp(")
|
|
|
|
|
+ ]
|
|
|
|
|
+ generated_matcher = js[
|
|
|
|
|
+ js.index("function isGeneratedEventPromptContent(") :
|
|
|
|
|
+ js.index("function buildAvailableEventsPrompt(")
|
|
|
|
|
+ ]
|
|
|
|
|
+ event_prompt_functions = js[
|
|
|
|
|
+ js.index("function updateEventPromptItem(") :
|
|
|
|
|
+ js.index("function bindPromptItemControls(")
|
|
|
|
|
+ ]
|
|
|
|
|
+ result = _run_node_json(
|
|
|
|
|
+ "\n".join(
|
|
|
|
|
+ [
|
|
|
|
|
+ refresh_tool_mode,
|
|
|
|
|
+ generated_matcher,
|
|
|
|
|
+ event_prompt_functions,
|
|
|
|
|
+ """
|
|
|
|
|
+const generated = [
|
|
|
|
|
+ "You may request EventAgent work using this text protocol.",
|
|
|
|
|
+ "Available events:",
|
|
|
|
|
+ "- handoff_note: Handoff a note",
|
|
|
|
|
+ "<agent_events>",
|
|
|
|
|
+ "handoff_note",
|
|
|
|
|
+ "</agent_events>",
|
|
|
|
|
+ "Use exact event names only, one per line. Do not include parameters.",
|
|
|
|
|
+].join("\\n");
|
|
|
|
|
+const items = [];
|
|
|
|
|
+function makePromptItem(promptKind, content) {
|
|
|
|
|
+ const item = {
|
|
|
|
|
+ dataset: { promptKind, promptRole: "system" },
|
|
|
|
|
+ content,
|
|
|
|
|
+ querySelector() {
|
|
|
|
|
+ return {
|
|
|
|
|
+ get value() { return item.content; },
|
|
|
|
|
+ set value(value) { item.content = value; },
|
|
|
|
|
+ };
|
|
|
|
|
+ },
|
|
|
|
|
+ remove() {
|
|
|
|
|
+ const index = items.indexOf(item);
|
|
|
|
|
+ if (index >= 0) items.splice(index, 1);
|
|
|
|
|
+ },
|
|
|
|
|
+ };
|
|
|
|
|
+ return item;
|
|
|
|
|
+}
|
|
|
|
|
+items.push(
|
|
|
|
|
+ makePromptItem("custom", "alpha"),
|
|
|
|
|
+ makePromptItem("event_agent_system", "stale generated prompt"),
|
|
|
|
|
+ makePromptItem("custom", "beta"),
|
|
|
|
|
+ makePromptItem("custom", generated),
|
|
|
|
|
+ makePromptItem("custom", "gamma"),
|
|
|
|
|
+);
|
|
|
|
|
+const systemPrompts = {
|
|
|
|
|
+ querySelectorAll() { return [...items]; },
|
|
|
|
|
+ querySelector() {
|
|
|
|
|
+ return items.find((item) => item.dataset.promptKind === "event_agent_system") || null;
|
|
|
|
|
+ },
|
|
|
|
|
+ insertBefore(item, target) {
|
|
|
|
|
+ const currentIndex = items.indexOf(item);
|
|
|
|
|
+ if (currentIndex >= 0) items.splice(currentIndex, 1);
|
|
|
|
|
+ const targetIndex = target ? items.indexOf(target) : items.length;
|
|
|
|
|
+ items.splice(targetIndex < 0 ? items.length : targetIndex, 0, item);
|
|
|
|
|
+ },
|
|
|
|
|
+};
|
|
|
|
|
+const toolInvocationMode = { value: "dual_agent" };
|
|
|
|
|
+const eventAgentModelSettings = { querySelectorAll() { return []; } };
|
|
|
|
|
+const availableTools = [{ name: "handoff_note" }];
|
|
|
|
|
+let eventPromptInsertionIndex = 0;
|
|
|
|
|
+function updateToolModeHelp() {}
|
|
|
|
|
+function selectedTools() { return ["handoff_note"]; }
|
|
|
|
|
+function buildAvailableEventsPrompt() { return generated; }
|
|
|
|
|
+function createSystemPrompt() {
|
|
|
|
|
+ return makePromptItem("event_agent_system", "");
|
|
|
|
|
+}
|
|
|
|
|
+function snapshot() {
|
|
|
|
|
+ return items.map((item) => [item.dataset.promptKind, item.content]);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+refreshToolModeState();
|
|
|
|
|
+const afterRefresh = snapshot();
|
|
|
|
|
+toolInvocationMode.value = "chat_agent_tools";
|
|
|
|
|
+refreshToolModeState();
|
|
|
|
|
+refreshToolModeState();
|
|
|
|
|
+const whileHidden = snapshot();
|
|
|
|
|
+const hiddenIndex = eventPromptInsertionIndex;
|
|
|
|
|
+toolInvocationMode.value = "dual_agent";
|
|
|
|
|
+refreshToolModeState();
|
|
|
|
|
+const afterRoundtrip = snapshot();
|
|
|
|
|
+console.log(JSON.stringify({ afterRefresh, whileHidden, hiddenIndex, afterRoundtrip }));
|
|
|
|
|
+""",
|
|
|
|
|
+ ]
|
|
|
|
|
+ )
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ expected_visible = [
|
|
|
|
|
+ ["custom", "alpha"],
|
|
|
|
|
+ ["event_agent_system", generated],
|
|
|
|
|
+ ["custom", "beta"],
|
|
|
|
|
+ ["custom", "gamma"],
|
|
|
|
|
+ ]
|
|
|
|
|
+ assert result["afterRefresh"] == expected_visible
|
|
|
|
|
+ assert result["whileHidden"] == [
|
|
|
|
|
+ ["custom", "alpha"],
|
|
|
|
|
+ ["custom", "beta"],
|
|
|
|
|
+ ["custom", "gamma"],
|
|
|
|
|
+ ]
|
|
|
|
|
+ assert result["hiddenIndex"] == 1
|
|
|
|
|
+ assert result["afterRoundtrip"] == expected_visible
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+def test_static_prompt_restore_derives_first_generated_event_prompt_position():
|
|
|
|
|
+ js = Path("src/agent_lab/presentation/static/app.js").read_text()
|
|
|
|
|
+
|
|
|
|
|
+ assert "function deriveEventPromptInsertionIndex(" in js
|
|
|
|
|
+ normalization = js[
|
|
|
|
|
+ js.index("function normalizePromptItems(") :
|
|
|
|
|
+ js.index("function buildAvailableEventsPrompt(")
|
|
|
|
|
+ ]
|
|
|
|
|
+ result = _run_node_json(
|
|
|
|
|
+ "\n".join(
|
|
|
|
|
+ [
|
|
|
|
|
+ normalization,
|
|
|
|
|
+ """
|
|
|
|
|
+const generated = [
|
|
|
|
|
+ "You may request EventAgent work using this text protocol.",
|
|
|
|
|
+ "Available events:",
|
|
|
|
|
+ "<agent_events>",
|
|
|
|
|
+ "handoff_note",
|
|
|
|
|
+ "</agent_events>",
|
|
|
|
|
+ "Use exact event names only, one per line. Do not include parameters.",
|
|
|
|
|
+].join("\\n");
|
|
|
|
|
+const prompts = [
|
|
|
|
|
+ { prompt_kind: "custom", content: "alpha" },
|
|
|
|
|
+ generated,
|
|
|
|
|
+ { prompt_kind: "custom", content: "beta" },
|
|
|
|
|
+ { prompt_kind: "event_agent_system", content: generated },
|
|
|
|
|
+ { prompt_kind: "custom", content: "gamma" },
|
|
|
|
|
+];
|
|
|
|
|
+console.log(JSON.stringify({
|
|
|
|
|
+ insertionIndex: deriveEventPromptInsertionIndex(prompts),
|
|
|
|
|
+ normalized: normalizePromptItems(prompts),
|
|
|
|
|
+}));
|
|
|
|
|
+""",
|
|
|
|
|
+ ]
|
|
|
|
|
+ )
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ assert result["insertionIndex"] == 1
|
|
|
|
|
+ assert result["normalized"] == [
|
|
|
|
|
+ {"prompt_kind": "custom", "role": "system", "content": "alpha"},
|
|
|
|
|
+ {"prompt_kind": "custom", "role": "system", "content": "beta"},
|
|
|
|
|
+ {"prompt_kind": "custom", "role": "system", "content": "gamma"},
|
|
|
|
|
+ ]
|
|
|
|
|
+
|
|
|
|
|
|
|
|
def test_static_prompt_restore_discards_generated_event_prompt_derived_state():
|
|
def test_static_prompt_restore_discards_generated_event_prompt_derived_state():
|
|
|
js = Path("src/agent_lab/presentation/static/app.js").read_text()
|
|
js = Path("src/agent_lab/presentation/static/app.js").read_text()
|