Add protocol prompt area before history
Introduce a new prompt "protocol" separate from extras: add LoopData.protocol_temporary and protocol_persistent, include protocol contents before message history during prompt construction, and clear temporary protocol data each turn. Add helper _build_context_message to render protocol/extras, update response input conversion to include protocol, and move project instructions & active/loaded skills injection into protocol. Update docs, prompts, plugin metadata, the SkillsTool message, and add tests to verify protocol placement and behavior.
frdel committed
Jun 18, 2026 at 21:42 UTC
afdc3aeb44b29b8d59433fa00fbe8e49a01974eb
24 files changed
+236
-53
AGENTS.md
+1
-1
@@ -96,7 +96,7 @@ When running in Docker, Agent Zero uses two distinct Python runtimes to isolate
96
```
97
98
Key Files:
99
-- agent.py: Defines AgentContext and the main Agent class.
99
+- agent.py: Defines AgentContext, LoopData virtual prompt areas (Protocol before history and Extras after history), and the main Agent class.
100
- helpers/plugins.py: Plugin discovery and configuration logic.
101
- webui/js/AlpineStore.js: Store factory for reactive frontend state.
102
- helpers/api.py: Base class for all API endpoints.
agent.py
+43
-25
@@ -338,6 +338,8 @@ class LoopData:
338
self.system = []
339
self.user_message: history.Message | None = None
340
self.history_output: list[history.OutputMessage] = []
341
+ self.protocol_temporary: OrderedDict[str, history.MessageContent] = OrderedDict()
342
+ self.protocol_persistent: OrderedDict[str, history.MessageContent] = OrderedDict()
343
self.extras_temporary: OrderedDict[str, history.MessageContent] = OrderedDict()
344
self.extras_persistent: OrderedDict[str, history.MessageContent] = OrderedDict()
345
self.last_response = ""
@@ -580,24 +582,28 @@ class Agent:
582
# concatenate system prompt
583
system_text = "\n\n".join(loop_data.system)
584
583
- # join extras
584
- extras = history.Message( # type: ignore[abstract]
585
- False,
586
- content=self.read_prompt(
587
- "agent.context.extras.md",
588
- extras=dirty_json.stringify(
589
- {**loop_data.extras_persistent, **loop_data.extras_temporary}
590
- ),
591
- ),
592
- ).output()
585
+ # join protocol and extras
586
+ protocol = self._build_context_message(
587
+ "agent.context.protocol.md",
588
+ "protocol",
589
+ {**loop_data.protocol_persistent, **loop_data.protocol_temporary},
590
+ include_empty=False,
591
+ )
592
+ extras = self._build_context_message(
593
+ "agent.context.extras.md",
594
+ "extras",
595
+ {**loop_data.extras_persistent, **loop_data.extras_temporary},
596
+ include_empty=True,
597
+ )
598
+ loop_data.protocol_temporary.clear()
599
loop_data.extras_temporary.clear()
600
595
- # convert history + extras to LLM format
601
+ # convert protocol + history + extras to LLM format
602
history_langchain: list[BaseMessage] = history.output_langchain(
597
- loop_data.history_output + extras
603
+ protocol + loop_data.history_output + extras
604
)
605
600
- # build full prompt from system prompt, message history and extrS
606
+ # build full prompt from system prompt, protocol, message history and extras
607
full_prompt: list[BaseMessage] = [
608
SystemMessage(content=system_text),
609
*history_langchain,
@@ -615,6 +621,24 @@ class Agent:
621
622
return full_prompt
623
624
+ def _build_context_message(
625
+ self,
626
+ prompt_file: str,
627
+ variable_name: str,
628
+ values: dict[str, history.MessageContent],
629
+ include_empty: bool,
630
+ ) -> list[history.OutputMessage]:
631
+ if not include_empty and not values:
632
+ return []
633
+
634
+ return history.Message( # type: ignore[abstract]
635
+ False,
636
+ content=self.read_prompt(
637
+ prompt_file,
638
+ **{variable_name: dirty_json.stringify(values)},
639
+ ),
640
+ ).output()
641
+
642
@extension.extensible
643
async def handle_exception(self, location: str, exception: Exception):
644
if exception:
@@ -920,9 +944,9 @@ class Agent:
944
model,
945
history_counter,
946
)
923
- call_data["responses_local_input_items"] = (
924
- self._responses_static_prefix_items(model, messages)
925
- + self._responses_input_items_since(model, 0)
947
+ call_data["responses_local_input_items"] = self._responses_prompt_input_items(
948
+ model,
949
+ messages,
950
)
951
952
await extension.call_extensions_async(
@@ -1014,18 +1038,12 @@ class Agent:
1038
return ResponsesTransport.input_from_messages(converted)
1039
return []
1040
1017
- def _responses_static_prefix_items(
1041
+ def _responses_prompt_input_items(
1042
self, model: Any, messages: list[BaseMessage]
1043
) -> list[dict[str, Any]]:
1020
- prefix: list[BaseMessage] = []
1021
- for message in messages:
1022
- if isinstance(message, SystemMessage):
1023
- prefix.append(message)
1024
- continue
1025
- break
1026
- if not prefix or not hasattr(model, "_convert_messages"):
1044
+ if not hasattr(model, "_convert_messages"):
1045
return []
1028
- converted = model._convert_messages(prefix)
1046
+ converted = model._convert_messages(messages)
1047
return ResponsesTransport.input_from_messages(converted)
1048
1049
def _remember_llm_result_state(
docs/README.md
+1
-1
@@ -27,7 +27,7 @@ docs focus on practical setup, screenshots, and user workflows.
27
- **[Desktop Guide](guides/desktop.md):** Use the built-in Linux desktop, GUI apps, and LibreOffice Writer/Calc/Impress Cowork.
28
- **[A0 CLI Connector](guides/a0-cli-connector.md):** Terminal-first host connector for Agent Zero, with screenshots of the host picker, connected shell, command palette, and Browser modes.
29
- **[Create a Small Plugin](guides/create-plugin.md):** Build and review a tiny Web UI plugin that adds an unread dot to the chat list.
30
-- **[Skills Guide](guides/skills.md):** Open the Skills selector, add active skills, and remove prompt extras you no longer need.
30
+- **[Skills Guide](guides/skills.md):** Open the Skills selector, add active skills, and remove prompt protocol entries you no longer need.
31
- **[Agent Profiles](guides/agent-profiles.md):** Switch the current chat profile or create a new guided profile from the chat input.
32
- **[Model Presets](guides/model-presets.md):** Create simple named shortcuts for model setups.
33
- **[Memory Guide](guides/memory.md):** Search, edit, delete, and curate memories so useful context does not become stale noise.
docs/guides/model-presets.md
+1
-1
@@ -83,7 +83,7 @@ You can always rename them later.
83
| **Model Preset** | Which models power the chat. |
84
| **Agent Profile** | The agent's role, tone, and prompt behavior. |
85
| **Project** | Workspace, files, memory, secrets, and project instructions. |
86
-| **Skill** | A specific procedure added to prompt extras. |
86
+| **Skill** | A specific procedure added to prompt protocol. |
87
88
For example, you can use the same "Researcher" Agent Profile with a cheaper
89
preset for simple questions and a stronger preset for difficult investigations.
docs/guides/skills.md
+2
-2
@@ -29,7 +29,7 @@ Click a skill to add it. Active skills are shown at the top of the selector.
29
To remove a skill, use the remove button in **Active skills** or uncheck it in
30
the list.
31
32
-Active skills are added to the **Extras** part of the system prompt. That means
32
+Active skills are added to the **Protocol** part of the prompt. That means
33
Agent Zero sees them every turn while they are active.
34
35
> [!TIP]
@@ -56,7 +56,7 @@ usually easier for the agent to follow.
56
57
| Control | What it changes |
58
| --- | --- |
59
-| **Skills** | Adds a specific procedure to the current prompt extras. |
59
+| **Skills** | Adds a specific procedure to the current prompt protocol. |
60
| **Agent Profiles** | Changes the broader role and behavior of the chat. |
61
| **Projects** | Adds workspace, files, memory, secrets, and project instructions. |
62
docs/guides/usage.md
+1
-1
@@ -74,7 +74,7 @@ Use the selector to add or remove active skills.
74
75

76
77
-Active skills are added to the **Extras** part of the system prompt, so keep the
77
+Active skills are added to the **Protocol** part of the prompt, so keep the
78
list short and intentional. See the [Skills guide](skills.md).
79
80
### Agent Profiles
docs/quickstart.md
+1
-1
@@ -141,7 +141,7 @@ Explains how to search, edit, delete, export, and curate memories before stale c
141
142
### [Open A0 Skills Guide](guides/skills.md)
143
144
-Shows the chat input **+** menu, the Skills selector, and how active skills are added to prompt extras.
144
+Shows the chat input **+** menu, the Skills selector, and how active skills are added to prompt protocol.
145
146
### [Open A0 Agent Profiles Guide](guides/agent-profiles.md)
147
extensions/python/AGENTS.md
+1
-1
@@ -44,7 +44,7 @@ Direct child DOX files:
44
| [hist_add_tool_result/AGENTS.md](hist_add_tool_result/AGENTS.md) | Tool-result history side effects. |
45
| [job_loop/AGENTS.md](job_loop/AGENTS.md) | Periodic backend maintenance jobs. |
46
| [message_loop_end/AGENTS.md](message_loop_end/AGENTS.md) | End-of-message-loop history and persistence behavior. |
47
-| [message_loop_prompts_after/AGENTS.md](message_loop_prompts_after/AGENTS.md) | Prompt extras appended after message-loop prompt construction. |
47
+| [message_loop_prompts_after/AGENTS.md](message_loop_prompts_after/AGENTS.md) | Prompt protocol and extras assembled around message-loop prompt construction. |
48
| [message_loop_prompts_before/AGENTS.md](message_loop_prompts_before/AGENTS.md) | Pre-prompt-construction message-loop gates. |
49
| [message_loop_start/AGENTS.md](message_loop_start/AGENTS.md) | Start-of-message-loop iteration state. |
50
| [monologue_end/AGENTS.md](monologue_end/AGENTS.md) | End-of-monologue UI and cleanup behavior. |
extensions/python/message_loop_prompts_after/AGENTS.md
+4
-3
@@ -2,11 +2,12 @@
2
3
## Purpose
4
5
-- Own prompt extras appended after primary message-loop prompt construction.
5
+- Own prompt protocol and extras appended around primary message-loop prompt construction.
6
7
## Ownership
8
9
- Ordered Python files own current datetime, skill recall/load context, agent info, parallel job status, and workdir extras injection.
10
+- Loaded and active skill instructions belong in prompt protocol, not prompt extras.
11
12
## Local Contracts
13
@@ -16,11 +17,11 @@
17
18
## Work Guidance
19
19
-- Coordinate prompt-extra changes with skill, workdir, and profile contracts.
20
+- Coordinate prompt protocol and prompt-extra changes with skill, workdir, and profile contracts.
21
22
## Verification
23
23
-- Inspect rendered prompt extras or run prompt-construction tests after changes.
24
+- Inspect rendered prompt protocol/extras or run prompt-construction tests after changes.
25
26
## Child DOX Index
27
extensions/python/message_loop_prompts_after/_65_include_loaded_skills.py
+4
-3
@@ -9,7 +9,8 @@ class IncludeLoadedSkills(Extension):
9
if not self.agent:
10
return
11
12
- extras = loop_data.extras_persistent
12
+ protocol = loop_data.protocol_persistent
13
+ protocol.pop("loaded_skills", None)
14
15
# Get loaded skills names
16
skill_names = self.agent.data.get(DATA_NAME_LOADED_SKILLS)
@@ -31,8 +32,8 @@ class IncludeLoadedSkills(Extension):
32
return
33
34
34
- # Inject into extras
35
- extras["loaded_skills"] = self.agent.read_prompt(
35
+ # Inject into protocol
36
+ protocol["loaded_skills"] = self.agent.read_prompt(
37
"agent.system.skills.loaded.md",
38
skills=content,
39
)
extensions/python/system_prompt/AGENTS.md
+1
@@ -7,6 +7,7 @@
7
## Ownership
8
9
- Ordered Python files own main, tools, MCP, secrets, skills, and project prompt sections.
10
+- Active project instruction bodies are moved into prompt protocol; the system prompt keeps project metadata and stable project rules.
11
12
## Local Contracts
13
extensions/python/system_prompt/_14_project_prompt.py
+9
-2
@@ -15,17 +15,24 @@ class ProjectPrompt(Extension):
15
):
16
if not self.agent:
17
return
18
- prompt = await build_prompt(self.agent)
18
+ prompt = await build_prompt(self.agent, loop_data=loop_data)
19
if prompt:
20
system_prompt.append(prompt)
21
22
23
@extensible
24
-async def build_prompt(agent: Agent) -> str:
24
+async def build_prompt(agent: Agent, loop_data: LoopData | None = None) -> str:
25
result = agent.read_prompt("agent.system.projects.main.md")
26
project_name = agent.context.get_data(projects.CONTEXT_DATA_KEY_PROJECT)
27
+ if loop_data:
28
+ loop_data.protocol_persistent.pop("project_instructions", None)
29
if project_name:
30
project_vars = projects.build_system_prompt_vars(project_name)
31
+ if loop_data and project_vars.get("project_instructions"):
32
+ loop_data.protocol_persistent["project_instructions"] = agent.read_prompt(
33
+ "agent.protocol.projects.instructions.md",
34
+ **project_vars,
35
+ )
36
result += "\n\n" + agent.read_prompt(
37
"agent.system.projects.active.md", **project_vars
38
)
plugins/_skills/AGENTS.md
+1
-1
@@ -2,7 +2,7 @@
2
3
## Purpose
4
5
-- Own active and hidden skill configuration injected into prompt extras on each turn.
5
+- Own active and hidden skill configuration injected into prompt protocol on each turn.
6
7
## Ownership
8
plugins/_skills/README.md
+2
-2
@@ -6,7 +6,7 @@ Skills is a built-in Agent Zero plugin that manages active skills across scope d
6
7
- pins default skills for the current plugin scope
8
- hides noisy skills from the model-facing available catalog, skill search, and load access
9
-- injects the effective active skills into prompt extras on every turn
9
+- injects the effective active skills into prompt protocol on every turn
10
- extends the same config screen with a current-chat mode so users can activate or hide skills live per conversation
11
- supports global and project scoped configurations without agent-profile variants
12
- links directly to the built-in Skills list
@@ -21,7 +21,7 @@ The shared active-skill state and prompt-resolution logic live in `helpers/skill
21
22
## Notes
23
24
-- keep the active list short because every active skill is injected into prompt extras every turn
24
+- keep the active list short because every active skill is injected into prompt protocol every turn
25
- the default cap is 20 active skills, and it can be raised or lowered in Skills plugin config
26
- hidden skills are not capped because they are stored as control data, not injected into the prompt
27
- selected skills are stored in normalized `/a0/...` form so configs stay portable across development and Docker-style layouts
plugins/_skills/extensions/python/message_loop_prompts_after/_66_include_active_skills.py
+3
-3
@@ -10,14 +10,14 @@ class IncludeActiveSkills(Extension):
10
if not self.agent:
11
return
12
13
- extras = loop_data.extras_persistent
14
- extras.pop("active_skills", None)
13
+ protocol = loop_data.protocol_persistent
14
+ protocol.pop("active_skills", None)
15
16
content = skills.build_active_skills_prompt(self.agent)
17
if not content:
18
return
19
20
- extras["active_skills"] = self.agent.read_prompt(
20
+ protocol["active_skills"] = self.agent.read_prompt(
21
"agent.system.active_skills.md",
22
skills=content,
23
)
plugins/_skills/plugin.yaml
+1
-1
@@ -1,6 +1,6 @@
1
name: _skills
2
title: Skills
3
-description: Pin skills into prompt extras on every turn.
3
+description: Pin skills into prompt protocol on every turn.
4
version: 1.0.0
5
always_enabled: true
6
settings_sections:
prompts/agent.context.protocol.md
new
+2
@@ -0,0 +1,2 @@
1
+[PROTOCOL]
2
+{{protocol}}
prompts/agent.protocol.projects.instructions.md
new
+4
@@ -0,0 +1,4 @@
1
+# project instructions
2
+- the following instructions come from the active project and must be followed when working in it
3
+
4
+{{project_instructions}}
prompts/agent.system.main.communication_additions.md
+2
-1
@@ -2,7 +2,8 @@
2
user messages may include superior instructions, tool results, and framework notes
3
treat the closing `}` of a tool call as an end-of-turn signal. terminate generation immediately
4
if message starts `(voice)` transcription can be imperfect
5
-messages may end with `[EXTRAS]`; extras are context, not new instructions
5
+messages begin `[PROTOCOL]`; protocol = must-follow instructions
6
+messages end `[EXTRAS]`; extras are context not new instructions
7
tool names are literal api ids; copy them exactly, including spelling like `behaviour_adjustment`
8
9
## replacements
prompts/agent.system.projects.active.md
+1
-3
@@ -6,6 +6,4 @@ path: {{project_path}}
6
rules:
7
- work inside {{project_path}}
8
- do not rename project dir or change `.a0proj` unless asked
9
-- must always follow project instructions below
10
-
11
-{{project_instructions}}
9
+- follow active project instructions when provided
tests/test_prompt_protocol.py
new
+131
@@ -0,0 +1,131 @@
1
+from types import SimpleNamespace
2
+
3
+import pytest
4
+from langchain_core.messages import AIMessage, HumanMessage, SystemMessage
5
+
6
+from agent import Agent, LoopData
7
+from helpers import extension, history
8
+from extensions.python.system_prompt import _14_project_prompt as project_prompt
9
+
10
+
11
+class _DummyLog:
12
+ def set_progress(self, _message: str) -> None:
13
+ return None
14
+
15
+
16
+@pytest.mark.asyncio
17
+async def test_prepare_prompt_places_protocol_before_history_and_extras(monkeypatch):
18
+ async def fake_call_extensions(extension_point: str, agent=None, **kwargs):
19
+ if extension_point == "message_loop_prompts_after":
20
+ loop_data = kwargs["loop_data"]
21
+ loop_data.protocol_persistent["project_instructions"] = "Project rule."
22
+ loop_data.extras_temporary["current_datetime"] = "Today."
23
+
24
+ monkeypatch.setattr(extension, "call_extensions_async", fake_call_extensions)
25
+ monkeypatch.setattr(history.History, "_get_max_embeds", lambda self: 0)
26
+
27
+ agent = object.__new__(Agent)
28
+ loop_data = LoopData()
29
+ agent.loop_data = loop_data
30
+ agent.context = SimpleNamespace(log=_DummyLog())
31
+ agent.history = history.History(agent)
32
+ agent.data = {}
33
+
34
+ agent.history.add_message(False, "User asks.")
35
+ agent.history.add_message(True, "Assistant answers.")
36
+
37
+ async def get_system_prompt(_loop_data):
38
+ return ["System root."]
39
+
40
+ def read_prompt(prompt_file: str, **kwargs) -> str:
41
+ if prompt_file == "agent.context.protocol.md":
42
+ return "[PROTOCOL]\n" + kwargs["protocol"]
43
+ if prompt_file == "agent.context.extras.md":
44
+ return "[EXTRAS]\n" + kwargs["extras"]
45
+ raise AssertionError(f"Unexpected prompt file: {prompt_file}")
46
+
47
+ agent.get_system_prompt = get_system_prompt
48
+ agent.read_prompt = read_prompt
49
+ agent.set_data = lambda key, value: agent.data.__setitem__(key, value)
50
+
51
+ prompt = await Agent.prepare_prompt(agent, loop_data)
52
+
53
+ assert isinstance(prompt[0], SystemMessage)
54
+ assert prompt[0].content == "System root."
55
+ assert isinstance(prompt[1], HumanMessage)
56
+ assert str(prompt[1].content).startswith("[PROTOCOL]")
57
+ assert str(prompt[1].content).index("Project rule.") < str(prompt[1].content).index(
58
+ "User asks."
59
+ )
60
+ assert isinstance(prompt[2], AIMessage)
61
+ assert prompt[2].content == "Assistant answers."
62
+ assert isinstance(prompt[3], HumanMessage)
63
+ assert str(prompt[3].content).startswith("[EXTRAS]")
64
+ assert "Today." in str(prompt[3].content)
65
+
66
+ serialized_history = agent.history.serialize()
67
+ assert "Project rule." not in serialized_history
68
+ assert "Today." not in serialized_history
69
+ assert "protocol" not in serialized_history.lower()
70
+ assert loop_data.protocol_temporary == {}
71
+ assert loop_data.extras_temporary == {}
72
+
73
+ class FakeResponsesModel:
74
+ def _convert_messages(self, messages):
75
+ role_by_type = {"system": "system", "human": "user", "ai": "assistant"}
76
+ return [
77
+ {"role": role_by_type[message.type], "content": message.content}
78
+ for message in messages
79
+ ]
80
+
81
+ input_items = Agent._responses_prompt_input_items(
82
+ agent,
83
+ FakeResponsesModel(),
84
+ prompt,
85
+ )
86
+ assert input_items[1]["role"] == "user"
87
+ assert "[PROTOCOL]" in input_items[1]["content"]
88
+ assert "[EXTRAS]" in input_items[-1]["content"]
89
+
90
+
91
+@pytest.mark.asyncio
92
+async def test_project_prompt_moves_project_instructions_to_protocol(monkeypatch):
93
+ project_vars = {
94
+ "project_name": "Demo",
95
+ "project_description": "",
96
+ "project_instructions": "Project rule.",
97
+ "project_path": "/a0/usr/projects/demo",
98
+ "project_git_url": "",
99
+ }
100
+ loop_data = LoopData()
101
+
102
+ class FakeContext:
103
+ def get_data(self, key):
104
+ assert key == project_prompt.projects.CONTEXT_DATA_KEY_PROJECT
105
+ return "demo"
106
+
107
+ class FakeAgent:
108
+ context = FakeContext()
109
+
110
+ def read_prompt(self, prompt_file: str, **kwargs) -> str:
111
+ if prompt_file == "agent.system.projects.main.md":
112
+ return "project context may be active"
113
+ if prompt_file == "agent.system.projects.active.md":
114
+ return f"active project: {kwargs['project_path']}"
115
+ if prompt_file == "agent.protocol.projects.instructions.md":
116
+ return "protocol project instructions:\n" + kwargs["project_instructions"]
117
+ raise AssertionError(f"Unexpected prompt file: {prompt_file}")
118
+
119
+ monkeypatch.setattr(
120
+ project_prompt.projects,
121
+ "build_system_prompt_vars",
122
+ lambda _name: project_vars,
123
+ )
124
+
125
+ prompt = await project_prompt.build_prompt.__wrapped__( # type: ignore[attr-defined]
126
+ FakeAgent(),
127
+ loop_data=loop_data,
128
+ )
129
+
130
+ assert "Project rule." not in prompt
131
+ assert "Project rule." in loop_data.protocol_persistent["project_instructions"]
tests/test_tool_action_contracts.py
+18
@@ -146,6 +146,24 @@ def test_skills_tool_accepts_action_alias_for_search(monkeypatch, tmp_path: Path
146
assert "browser-form-workflows" in response.message
147
148
149
+def test_skills_tool_load_reports_protocol_injection(monkeypatch, tmp_path: Path):
150
+ module = _load_skills_tool(monkeypatch, tmp_path)
151
+ agent = _FakeAgent()
152
+ tool = module.SkillsTool(
153
+ agent,
154
+ "skills_tool",
155
+ None,
156
+ {"action": "load", "skill_name": "browser-form-workflows"},
157
+ "",
158
+ None,
159
+ )
160
+
161
+ response = asyncio.run(tool.execute(**tool.args))
162
+
163
+ assert response.message == "Loaded skill 'browser-form-workflows' into Protocol."
164
+ assert agent.data["loaded_skills"] == ["browser-form-workflows"]
165
+
166
+
167
def test_skills_tool_read_file_action_reads_inside_skill_dir(
168
monkeypatch, tmp_path: Path
169
):
tools/skills_tool.py
+1
-1
@@ -209,7 +209,7 @@ class SkillsTool(Tool):
209
loaded.append(skill.name)
210
self.agent.data[DATA_NAME_LOADED_SKILLS] = loaded[-max_loaded_skills():]
211
212
- return f"Loaded skill '{skill.name}' into EXTRAS."
212
+ return f"Loaded skill '{skill.name}' into Protocol."
213
214
def _read_file(self, skill_name: str, file_path: str) -> str:
215
if not skill_name:
tools/skills_tool.py.dox.md
+1
@@ -25,6 +25,7 @@
25
- Update this file whenever tool arguments, output shape, `break_loop` behavior, intervention handling, prompt instructions, or side effects change.
26
- `SkillsTool` is a `Tool`.
27
- `SkillsTool` defines `execute(...)`.
28
+- Loading a skill stores it for prompt Protocol injection on subsequent turns.
29
- Observed side-effect areas: filesystem reads, filesystem deletion, settings/state persistence.
30
- Imported dependency areas include: `__future__`, `helpers`, `helpers.print_style`, `helpers.tool`, `pathlib`, `typing`.
31