npcsh 1.1.21__py3-none-any.whl → 1.1.22__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- npcsh/_state.py +10 -5
- npcsh/benchmark/npcsh_agent.py +22 -14
- npcsh/benchmark/templates/install-npcsh.sh.j2 +2 -2
- npcsh/mcp_server.py +9 -1
- npcsh/npc_team/alicanto.npc +12 -6
- npcsh/npc_team/corca.npc +0 -1
- npcsh/npc_team/frederic.npc +2 -3
- npcsh/npc_team/jinxs/lib/core/edit_file.jinx +83 -61
- npcsh/npc_team/jinxs/modes/alicanto.jinx +102 -41
- npcsh/npc_team/jinxs/modes/build.jinx +378 -0
- npcsh/npc_team/jinxs/modes/convene.jinx +597 -0
- npcsh/npc_team/jinxs/modes/corca.jinx +777 -387
- npcsh/npc_team/jinxs/modes/kg.jinx +69 -2
- npcsh/npc_team/jinxs/modes/plonk.jinx +16 -7
- npcsh/npc_team/jinxs/modes/yap.jinx +628 -187
- npcsh/npc_team/kadiefa.npc +2 -1
- npcsh/npc_team/sibiji.npc +3 -3
- {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/alicanto.jinx +102 -41
- {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/alicanto.npc +12 -6
- npcsh-1.1.22.data/data/npcsh/npc_team/build.jinx +378 -0
- npcsh-1.1.22.data/data/npcsh/npc_team/corca.jinx +820 -0
- {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/corca.npc +0 -1
- npcsh-1.1.22.data/data/npcsh/npc_team/edit_file.jinx +119 -0
- {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/frederic.npc +2 -3
- {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/kadiefa.npc +2 -1
- {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/kg.jinx +69 -2
- {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/plonk.jinx +16 -7
- {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/sibiji.npc +3 -3
- npcsh-1.1.22.data/data/npcsh/npc_team/yap.jinx +716 -0
- {npcsh-1.1.21.dist-info → npcsh-1.1.22.dist-info}/METADATA +246 -281
- {npcsh-1.1.21.dist-info → npcsh-1.1.22.dist-info}/RECORD +127 -130
- npcsh/npc_team/jinxs/lib/core/search/kg_search.jinx +0 -429
- npcsh/npc_team/jinxs/lib/core/search.jinx +0 -54
- npcsh/npc_team/jinxs/lib/utils/build.jinx +0 -65
- npcsh-1.1.21.data/data/npcsh/npc_team/build.jinx +0 -65
- npcsh-1.1.21.data/data/npcsh/npc_team/corca.jinx +0 -430
- npcsh-1.1.21.data/data/npcsh/npc_team/edit_file.jinx +0 -97
- npcsh-1.1.21.data/data/npcsh/npc_team/kg_search.jinx +0 -429
- npcsh-1.1.21.data/data/npcsh/npc_team/search.jinx +0 -54
- npcsh-1.1.21.data/data/npcsh/npc_team/yap.jinx +0 -275
- /npcsh/npc_team/jinxs/lib/{core → utils}/chat.jinx +0 -0
- /npcsh/npc_team/jinxs/lib/{core → utils}/cmd.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/add_tab.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/alicanto.png +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/arxiv.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/benchmark.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/browser_action.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/browser_screenshot.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/chat.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/click.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/close_browser.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/close_pane.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/close_tab.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/cmd.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/compile.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/compress.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/config_tui.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/confirm.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/convene.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/corca.png +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/corca_example.png +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/db_search.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/delegate.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/file_search.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/focus_pane.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/frederic4.png +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/git.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/guac.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/guac.npc +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/guac.png +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/help.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/incognide.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/init.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/jinxs.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/kadiefa.png +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/key_press.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/launch_app.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/list_panes.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/load_file.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/memories.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/models.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/navigate.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/notify.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/npcsh.ctx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/npcsh_sibiji.png +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/nql.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/open_browser.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/open_pane.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/ots.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/papers.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/paste.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/plonk.npc +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/plonk.png +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/plonkjr.png +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/pti.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/python.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/read_pane.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/reattach.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/roll.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/run_terminal.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/sample.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/screenshot.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/send_message.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/serve.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/set.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/setup.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/sh.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/shh.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/sibiji.png +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/sleep.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/split_pane.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/spool.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/spool.png +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/sql.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/switch.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/switch_npc.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/switch_tab.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/switches.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/sync.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/team.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/teamviz.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/trigger.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/type_text.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/usage.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/verbose.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/vixynt.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/wait.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/wander.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/web_search.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/write_file.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/yap.png +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/zen_mode.jinx +0 -0
- {npcsh-1.1.21.dist-info → npcsh-1.1.22.dist-info}/WHEEL +0 -0
- {npcsh-1.1.21.dist-info → npcsh-1.1.22.dist-info}/entry_points.txt +0 -0
- {npcsh-1.1.21.dist-info → npcsh-1.1.22.dist-info}/licenses/LICENSE +0 -0
- {npcsh-1.1.21.dist-info → npcsh-1.1.22.dist-info}/top_level.txt +0 -0
npcsh/_state.py
CHANGED
|
@@ -2537,19 +2537,16 @@ def collect_llm_tools(state: ShellState) -> Tuple[List[Dict[str, Any]], Dict[str
|
|
|
2537
2537
|
elif npc_obj and getattr(npc_obj, "tool_map", None):
|
|
2538
2538
|
tool_map.update(npc_obj.tool_map)
|
|
2539
2539
|
|
|
2540
|
-
# Jinx tools from NPC
|
|
2540
|
+
# Jinx tools from NPC only (NPC.jinxs_dict is already filtered by jinxs_spec
|
|
2541
|
+
# during initialize_jinxs - don't add the full team catalog which overwhelms small models)
|
|
2541
2542
|
aggregated_jinxs: Dict[str, Any] = {}
|
|
2542
2543
|
if npc_obj and getattr(npc_obj, "jinxs_dict", None):
|
|
2543
2544
|
aggregated_jinxs.update(npc_obj.jinxs_dict)
|
|
2544
|
-
if state.team and isinstance(state.team, Team) and getattr(state.team, "jinxs_dict", None):
|
|
2545
|
-
aggregated_jinxs.update({k: v for k, v in state.team.jinxs_dict.items() if k not in aggregated_jinxs})
|
|
2546
2545
|
|
|
2547
2546
|
if aggregated_jinxs:
|
|
2548
2547
|
jinx_catalog: Dict[str, Dict[str, Any]] = {}
|
|
2549
2548
|
if npc_obj and getattr(npc_obj, "jinx_tool_catalog", None):
|
|
2550
2549
|
jinx_catalog.update(npc_obj.jinx_tool_catalog or {})
|
|
2551
|
-
if state.team and isinstance(state.team, Team) and getattr(state.team, "jinx_tool_catalog", None):
|
|
2552
|
-
jinx_catalog.update(state.team.jinx_tool_catalog or {})
|
|
2553
2550
|
if not jinx_catalog:
|
|
2554
2551
|
jinx_catalog = build_jinx_tool_catalog(aggregated_jinxs)
|
|
2555
2552
|
|
|
@@ -2898,6 +2895,14 @@ def process_pipeline_command(
|
|
|
2898
2895
|
tools_for_llm, tool_exec_map = collect_llm_tools(state)
|
|
2899
2896
|
if not tools_for_llm:
|
|
2900
2897
|
tool_capable = False
|
|
2898
|
+
else:
|
|
2899
|
+
# Add tool guidance so model knows to use function calls
|
|
2900
|
+
tool_names = [t['function']['name'] for t in tools_for_llm if 'function' in t]
|
|
2901
|
+
info += (
|
|
2902
|
+
f"\nYou have access to these tools: {', '.join(tool_names)}. "
|
|
2903
|
+
f"You MUST use the function calling interface to invoke them. "
|
|
2904
|
+
f"Do NOT write tool names as text - call them as functions."
|
|
2905
|
+
)
|
|
2901
2906
|
|
|
2902
2907
|
npc_name = (
|
|
2903
2908
|
state.npc.name
|
npcsh/benchmark/npcsh_agent.py
CHANGED
|
@@ -53,11 +53,11 @@ class NpcshAgent(BaseInstalledAgent):
|
|
|
53
53
|
Returns:
|
|
54
54
|
List of ExecInput commands to execute
|
|
55
55
|
"""
|
|
56
|
-
# Wrap the instruction with
|
|
57
|
-
tool_instruction = f"""You have access to
|
|
56
|
+
# Wrap the instruction with tool usage directions and retry logic
|
|
57
|
+
tool_instruction = f"""You have access to tools: edit_file (for writing/creating files), sh (for running shell commands), and python (for running Python code).
|
|
58
58
|
|
|
59
59
|
IMPORTANT RULES:
|
|
60
|
-
1. You MUST
|
|
60
|
+
1. You MUST call these tools using the function calling interface to complete the task. Do NOT write tool names as text - invoke them as function calls.
|
|
61
61
|
2. After implementing a solution, you MUST verify it works by running any provided test scripts.
|
|
62
62
|
3. If a test fails or produces an error, you MUST try a DIFFERENT approach. Do not give up.
|
|
63
63
|
4. Keep trying different approaches until you succeed or have tried at least 10 different solutions.
|
|
@@ -66,13 +66,11 @@ IMPORTANT RULES:
|
|
|
66
66
|
Task: {instruction}
|
|
67
67
|
|
|
68
68
|
WORKFLOW:
|
|
69
|
-
1.
|
|
69
|
+
1. Call edit_file to write code files. Call sh to run commands.
|
|
70
70
|
2. Run any test scripts mentioned in the task
|
|
71
71
|
3. Check the output carefully - look for "PASS", "SUCCESS", "OK" or similar
|
|
72
72
|
4. If the test failed, analyze why and try a completely different approach
|
|
73
|
-
5. Repeat until the test passes
|
|
74
|
-
|
|
75
|
-
Remember: Use edit_file to write code files. Use sh to run commands. VERIFY your solution works before concluding."""
|
|
73
|
+
5. Repeat until the test passes"""
|
|
76
74
|
|
|
77
75
|
escaped_instruction = shlex.quote(tool_instruction)
|
|
78
76
|
model_name = self.model_name
|
|
@@ -146,8 +144,14 @@ Remember: Use edit_file to write code files. Use sh to run commands. VERIFY your
|
|
|
146
144
|
# Using corca NPC which has edit_file tool for writing files
|
|
147
145
|
# Using the npc CLI which supports single-command execution
|
|
148
146
|
# NPCSH_DEFAULT_MODE=agent enables automatic tool execution
|
|
147
|
+
ollama_env = ""
|
|
148
|
+
if npcsh_provider == "ollama":
|
|
149
|
+
ollama_host = os.environ.get("OLLAMA_HOST", "http://host.docker.internal:11434")
|
|
150
|
+
ollama_env = f'OLLAMA_HOST="{ollama_host}" '
|
|
151
|
+
|
|
149
152
|
npcsh_cmd = (
|
|
150
153
|
f'{env_prefix}'
|
|
154
|
+
f'{ollama_env}'
|
|
151
155
|
f'NPCSH_CHAT_MODEL="{model}" '
|
|
152
156
|
f'NPCSH_CHAT_PROVIDER="{npcsh_provider}" '
|
|
153
157
|
f'NPCSH_STREAM_OUTPUT=0 '
|
|
@@ -234,11 +238,11 @@ class NpcshAgentWithNpc(NpcshAgent):
|
|
|
234
238
|
|
|
235
239
|
def create_run_agent_commands(self, instruction: str) -> list:
|
|
236
240
|
"""Create commands using a specific NPC."""
|
|
237
|
-
# Wrap the instruction with
|
|
238
|
-
tool_instruction = f"""You have access to
|
|
241
|
+
# Wrap the instruction with tool usage directions and retry logic
|
|
242
|
+
tool_instruction = f"""You have access to tools: edit_file (for writing/creating files), sh (for running shell commands), and python (for running Python code).
|
|
239
243
|
|
|
240
244
|
IMPORTANT RULES:
|
|
241
|
-
1. You MUST
|
|
245
|
+
1. You MUST call these tools using the function calling interface to complete the task. Do NOT write tool names as text - invoke them as function calls.
|
|
242
246
|
2. After implementing a solution, you MUST verify it works by running any provided test scripts.
|
|
243
247
|
3. If a test fails or produces an error, you MUST try a DIFFERENT approach. Do not give up.
|
|
244
248
|
4. Keep trying different approaches until you succeed or have tried at least 10 different solutions.
|
|
@@ -247,13 +251,11 @@ IMPORTANT RULES:
|
|
|
247
251
|
Task: {instruction}
|
|
248
252
|
|
|
249
253
|
WORKFLOW:
|
|
250
|
-
1.
|
|
254
|
+
1. Call edit_file to write code files. Call sh to run commands.
|
|
251
255
|
2. Run any test scripts mentioned in the task
|
|
252
256
|
3. Check the output carefully - look for "PASS", "SUCCESS", "OK" or similar
|
|
253
257
|
4. If the test failed, analyze why and try a completely different approach
|
|
254
|
-
5. Repeat until the test passes
|
|
255
|
-
|
|
256
|
-
Remember: Use edit_file to write code files. Use sh to run commands. VERIFY your solution works before concluding."""
|
|
258
|
+
5. Repeat until the test passes"""
|
|
257
259
|
|
|
258
260
|
escaped_instruction = shlex.quote(tool_instruction)
|
|
259
261
|
model_name = self.model_name
|
|
@@ -309,8 +311,14 @@ Remember: Use edit_file to write code files. Use sh to run commands. VERIFY your
|
|
|
309
311
|
|
|
310
312
|
# Use specific NPC with --npc flag
|
|
311
313
|
# NPCSH_DEFAULT_MODE=agent enables automatic tool execution
|
|
314
|
+
ollama_env = ""
|
|
315
|
+
if npcsh_provider == "ollama":
|
|
316
|
+
ollama_host = os.environ.get("OLLAMA_HOST", "http://host.docker.internal:11434")
|
|
317
|
+
ollama_env = f'OLLAMA_HOST="{ollama_host}" '
|
|
318
|
+
|
|
312
319
|
npcsh_cmd = (
|
|
313
320
|
f'{env_prefix}'
|
|
321
|
+
f'{ollama_env}'
|
|
314
322
|
f'NPCSH_CHAT_MODEL="{model}" '
|
|
315
323
|
f'NPCSH_CHAT_PROVIDER="{npcsh_provider}" '
|
|
316
324
|
f'NPCSH_STREAM_OUTPUT=0 '
|
|
@@ -14,8 +14,8 @@ fi
|
|
|
14
14
|
|
|
15
15
|
# Install npcsh with lite dependencies (API providers only, no local models)
|
|
16
16
|
# Use --break-system-packages for PEP 668 compliance (Ubuntu 24.04+)
|
|
17
|
-
echo "Installing npcsh[lite]..."
|
|
18
|
-
pip install --quiet --break-system-packages npcsh[lite] || pip install --quiet npcsh[lite]
|
|
17
|
+
echo "Installing npcsh[lite] + ollama..."
|
|
18
|
+
pip install --quiet --break-system-packages npcsh[lite] ollama || pip install --quiet npcsh[lite] ollama
|
|
19
19
|
|
|
20
20
|
# Verify installation
|
|
21
21
|
echo "Verifying npcsh installation..."
|
npcsh/mcp_server.py
CHANGED
|
@@ -1,9 +1,17 @@
|
|
|
1
1
|
|
|
2
2
|
"""
|
|
3
|
-
Enhanced MCP server that incorporates functionality from npcpy.routes,
|
|
3
|
+
Enhanced MCP server that incorporates functionality from npcpy.routes,
|
|
4
4
|
npcpy.llm_funcs, and npcpy.npc_compiler as tools.
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
+
# When run as a subprocess, Python adds the script directory to sys.path[0].
|
|
8
|
+
# Since this file lives inside the npcsh package, that shadows the package
|
|
9
|
+
# (npcsh.py is found instead of the npcsh/ package). Remove it.
|
|
10
|
+
import sys as _sys, os as _os
|
|
11
|
+
_script_dir = _os.path.dirname(_os.path.abspath(__file__))
|
|
12
|
+
if _script_dir in _sys.path:
|
|
13
|
+
_sys.path.remove(_script_dir)
|
|
14
|
+
|
|
7
15
|
import os
|
|
8
16
|
import subprocess
|
|
9
17
|
import json
|
npcsh/npc_team/alicanto.npc
CHANGED
|
@@ -11,13 +11,19 @@ colors:
|
|
|
11
11
|
top: "255,215,0"
|
|
12
12
|
bottom: "218,165,32"
|
|
13
13
|
primary_directive: |
|
|
14
|
-
You are alicanto,
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
You are alicanto, a research agent. You investigate hypotheses through experimentation and evidence gathering.
|
|
15
|
+
Search academic papers to ground your work in existing literature.
|
|
16
|
+
Search the web for data, documentation, and recent findings.
|
|
17
|
+
Write and execute Python code to analyze data, compute statistics, and generate results.
|
|
18
|
+
Use shell commands for data processing and system tasks.
|
|
19
|
+
Create files to record your findings, analyses, and evidence.
|
|
20
|
+
When exploring a hypothesis, gather evidence from multiple sources, analyze it quantitatively where possible, and document what you find.
|
|
21
|
+
Say RESEARCH_COMPLETE when you have sufficient evidence to evaluate your hypothesis.
|
|
18
22
|
jinxs:
|
|
19
|
-
- lib/core/search
|
|
23
|
+
- lib/core/search/web_search
|
|
24
|
+
- lib/core/search/file_search
|
|
25
|
+
- lib/core/search/db_search
|
|
20
26
|
- lib/core/sh
|
|
21
27
|
- lib/core/python
|
|
22
28
|
- lib/core/load_file
|
|
23
|
-
- lib/
|
|
29
|
+
- lib/core/edit_file
|
npcsh/npc_team/corca.npc
CHANGED
npcsh/npc_team/frederic.npc
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
jinx_name: edit_file
|
|
2
|
-
description:
|
|
3
|
-
changes.
|
|
2
|
+
description: Creates or edits a file. If the file does not exist, creates it with
|
|
3
|
+
the specified content. If the file exists, examines it and applies changes.
|
|
4
4
|
inputs:
|
|
5
5
|
- file_path
|
|
6
6
|
- edit_instructions
|
|
@@ -17,19 +17,42 @@ steps:
|
|
|
17
17
|
edit_instructions = {{ edit_instructions | string | tojson }}
|
|
18
18
|
backup_str = {{ backup | default("true") | string | tojson }}
|
|
19
19
|
create_backup = backup_str.lower() not in ('false', 'no', '0', '')
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
20
|
+
|
|
21
|
+
# Ensure parent directory exists
|
|
22
|
+
os.makedirs(os.path.dirname(file_path) or '.', exist_ok=True)
|
|
23
|
+
|
|
24
|
+
# If file doesn't exist, create it
|
|
25
|
+
if not os.path.exists(file_path):
|
|
26
|
+
prompt = """You are a code writing assistant. Create the content for a new file based on these instructions.
|
|
27
|
+
|
|
28
|
+
Instructions: """ + edit_instructions + """
|
|
29
|
+
|
|
30
|
+
Return a JSON object with:
|
|
31
|
+
1. "content": The full content to write to the file
|
|
32
|
+
2. "explanation": Brief explanation of what was created
|
|
33
|
+
|
|
34
|
+
Example response:
|
|
35
|
+
{"content": "print('hello world')", "explanation": "Created a Python hello world script"}
|
|
36
|
+
"""
|
|
37
|
+
response = get_llm_response(prompt, model=npc.model, provider=npc.provider, npc=npc, format="json")
|
|
38
|
+
result = response.get("response", {})
|
|
39
|
+
content = result.get("content", edit_instructions)
|
|
40
|
+
explanation = result.get("explanation", "Created new file")
|
|
41
|
+
|
|
42
|
+
with open(file_path, 'w') as f:
|
|
43
|
+
f.write(content)
|
|
44
|
+
|
|
45
|
+
output = "Created " + file_path + "\n\n" + explanation
|
|
46
|
+
else:
|
|
47
|
+
with open(file_path, 'r') as f:
|
|
48
|
+
original_content = f.read()
|
|
49
|
+
|
|
50
|
+
if create_backup:
|
|
51
|
+
backup_path = file_path + ".bak"
|
|
52
|
+
with open(backup_path, 'w') as f:
|
|
53
|
+
f.write(original_content)
|
|
54
|
+
|
|
55
|
+
prompt = """You are a code editing assistant. Analyze this file and make the requested changes.
|
|
33
56
|
|
|
34
57
|
File content:
|
|
35
58
|
""" + original_content + """
|
|
@@ -49,49 +72,48 @@ steps:
|
|
|
49
72
|
Example response:
|
|
50
73
|
{"modifications": [{"type": "replace", "original": "old code", "replacement": "new code"}], "explanation": "Updated the code"}
|
|
51
74
|
"""
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
output = "Applied " + str(changes_applied) + " changes to " + file_path + "\n\n" + explanation
|
|
75
|
+
|
|
76
|
+
response = get_llm_response(prompt, model=npc.model, provider=npc.provider, npc=npc, format="json")
|
|
77
|
+
|
|
78
|
+
result = response.get("response", {})
|
|
79
|
+
modifications = result.get("modifications", [])
|
|
80
|
+
explanation = result.get("explanation", "No explanation provided")
|
|
81
|
+
|
|
82
|
+
updated_content = original_content
|
|
83
|
+
changes_applied = 0
|
|
84
|
+
|
|
85
|
+
for mod in modifications:
|
|
86
|
+
print(mod)
|
|
87
|
+
mod_type = mod.get("type")
|
|
88
|
+
|
|
89
|
+
if mod_type == "replace":
|
|
90
|
+
original = mod.get("original")
|
|
91
|
+
replacement = mod.get("replacement")
|
|
92
|
+
if original in updated_content:
|
|
93
|
+
updated_content = updated_content.replace(original, replacement)
|
|
94
|
+
changes_applied += 1
|
|
95
|
+
|
|
96
|
+
elif mod_type == "insert_after":
|
|
97
|
+
target = mod.get("target")
|
|
98
|
+
insertion = mod.get("insertion")
|
|
99
|
+
if target in updated_content:
|
|
100
|
+
updated_content = updated_content.replace(target, target + insertion)
|
|
101
|
+
changes_applied += 1
|
|
102
|
+
|
|
103
|
+
elif mod_type == "insert_before":
|
|
104
|
+
target = mod.get("target")
|
|
105
|
+
insertion = mod.get("insertion")
|
|
106
|
+
if target in updated_content:
|
|
107
|
+
updated_content = updated_content.replace(target, insertion + target)
|
|
108
|
+
changes_applied += 1
|
|
109
|
+
|
|
110
|
+
elif mod_type == "delete":
|
|
111
|
+
target = mod.get("target")
|
|
112
|
+
if target in updated_content:
|
|
113
|
+
updated_content = updated_content.replace(target, "")
|
|
114
|
+
changes_applied += 1
|
|
115
|
+
|
|
116
|
+
with open(file_path, 'w') as f:
|
|
117
|
+
f.write(updated_content)
|
|
118
|
+
|
|
119
|
+
output = "Applied " + str(changes_applied) + " changes to " + file_path + "\n\n" + explanation
|
|
@@ -33,6 +33,8 @@ steps:
|
|
|
33
33
|
from typing import List, Dict, Any, Tuple
|
|
34
34
|
from pathlib import Path
|
|
35
35
|
|
|
36
|
+
import requests as _requests
|
|
37
|
+
|
|
36
38
|
from npcpy.llm_funcs import get_llm_response
|
|
37
39
|
from npcpy.npc_compiler import NPC
|
|
38
40
|
|
|
@@ -65,6 +67,7 @@ steps:
|
|
|
65
67
|
|
|
66
68
|
model = context.get('model') or (npc.model if npc and hasattr(npc, 'model') else None)
|
|
67
69
|
provider = context.get('provider') or (npc.provider if npc and hasattr(npc, 'provider') else None)
|
|
70
|
+
_alicanto_directive = (npc.primary_directive if npc and hasattr(npc, 'primary_directive') else "") or ""
|
|
68
71
|
|
|
69
72
|
# ========== Utility ==========
|
|
70
73
|
def get_size():
|
|
@@ -164,8 +167,26 @@ steps:
|
|
|
164
167
|
except:
|
|
165
168
|
return "Error listing directory."
|
|
166
169
|
|
|
167
|
-
def
|
|
168
|
-
"""Execute
|
|
170
|
+
def run_python(code: str) -> str:
|
|
171
|
+
"""Execute Python code and return the output. This is your PRIMARY tool for data analysis, file processing, API calls, computations, and any programmatic work. Use this for: downloading data, parsing files, running analyses, making HTTP requests, processing CSVs/FITS/JSON, plotting, statistics, etc. The code runs in a fresh namespace with access to standard library and installed packages (numpy, pandas, astropy, requests, matplotlib, scipy, etc.)."""
|
|
172
|
+
import io as _io
|
|
173
|
+
_old_stdout = sys.stdout
|
|
174
|
+
_old_stderr = sys.stderr
|
|
175
|
+
_capture = _io.StringIO()
|
|
176
|
+
sys.stdout = _capture
|
|
177
|
+
sys.stderr = _capture
|
|
178
|
+
_ns = {'__builtins__': __builtins__}
|
|
179
|
+
try:
|
|
180
|
+
exec(code, _ns)
|
|
181
|
+
except Exception as _e:
|
|
182
|
+
print(f"Error: {type(_e).__name__}: {_e}")
|
|
183
|
+
finally:
|
|
184
|
+
sys.stdout = _old_stdout
|
|
185
|
+
sys.stderr = _old_stderr
|
|
186
|
+
return _capture.getvalue()[:5000] if _capture.getvalue().strip() else "(no output)"
|
|
187
|
+
|
|
188
|
+
def shell_command(command: str) -> str:
|
|
189
|
+
"""Execute a shell command. ONLY use this for simple system tasks like installing packages (pip install), checking disk space, or listing system info. For ALL data work, analysis, file processing, HTTP requests, and computation, use run_python instead."""
|
|
169
190
|
try:
|
|
170
191
|
result = subprocess.run(command, shell=True, capture_output=True, text=True, timeout=60)
|
|
171
192
|
out = ""
|
|
@@ -179,12 +200,14 @@ steps:
|
|
|
179
200
|
except Exception as e:
|
|
180
201
|
return f"Error: {e}"
|
|
181
202
|
|
|
203
|
+
_search_provider = os.environ.get('NPCSH_SEARCH_PROVIDER', 'perplexity')
|
|
204
|
+
|
|
182
205
|
def _web_search_tool(query: str) -> str:
|
|
183
206
|
"""Search the web for information."""
|
|
184
207
|
if not WEB_AVAILABLE:
|
|
185
208
|
return "Web search not available."
|
|
186
209
|
try:
|
|
187
|
-
results = search_web(query, num_results=5)
|
|
210
|
+
results = search_web(query, num_results=5, provider=_search_provider)
|
|
188
211
|
if not results:
|
|
189
212
|
return "No results found."
|
|
190
213
|
if isinstance(results, list):
|
|
@@ -199,6 +222,61 @@ steps:
|
|
|
199
222
|
except Exception as e:
|
|
200
223
|
return f"Search error: {e}"
|
|
201
224
|
|
|
225
|
+
_s2_api_key = os.environ.get('S2_API_KEY', '')
|
|
226
|
+
|
|
227
|
+
def search_papers(query: str, limit: int = 10) -> str:
|
|
228
|
+
"""Search Semantic Scholar for academic papers. Returns titles, authors, year, citation count, abstracts, and URLs."""
|
|
229
|
+
s2_url = "https://api.semanticscholar.org/graph/v1/paper/search"
|
|
230
|
+
params = {
|
|
231
|
+
"query": query,
|
|
232
|
+
"limit": min(limit, 20),
|
|
233
|
+
"fields": "title,abstract,authors,year,citationCount,url,tldr,venue"
|
|
234
|
+
}
|
|
235
|
+
try:
|
|
236
|
+
# Try with API key first if available
|
|
237
|
+
if _s2_api_key:
|
|
238
|
+
resp = _requests.get(s2_url, headers={"x-api-key": _s2_api_key}, params=params, timeout=30)
|
|
239
|
+
if resp.status_code == 403:
|
|
240
|
+
# Key expired/revoked, fall back to unauthenticated
|
|
241
|
+
resp = _requests.get(s2_url, params=params, timeout=30)
|
|
242
|
+
else:
|
|
243
|
+
resp = _requests.get(s2_url, params=params, timeout=30)
|
|
244
|
+
if resp.status_code == 429:
|
|
245
|
+
# Rate limited, wait and retry once
|
|
246
|
+
time.sleep(1.5)
|
|
247
|
+
resp = _requests.get(s2_url, params=params, timeout=30)
|
|
248
|
+
resp.raise_for_status()
|
|
249
|
+
papers = resp.json().get('data', [])
|
|
250
|
+
if not papers:
|
|
251
|
+
return f"No papers found for: {query}"
|
|
252
|
+
out = []
|
|
253
|
+
for i, p in enumerate(papers, 1):
|
|
254
|
+
title = p.get('title', 'No title')
|
|
255
|
+
year = p.get('year', '?')
|
|
256
|
+
cites = p.get('citationCount', 0)
|
|
257
|
+
authors = ', '.join([a.get('name', '') for a in p.get('authors', [])[:3]])
|
|
258
|
+
if len(p.get('authors', [])) > 3:
|
|
259
|
+
authors += ' et al.'
|
|
260
|
+
tldr = p.get('tldr', {}).get('text', '') if p.get('tldr') else ''
|
|
261
|
+
abstract = (p.get('abstract') or '')[:200]
|
|
262
|
+
paper_url = p.get('url', '')
|
|
263
|
+
venue = p.get('venue', '')
|
|
264
|
+
entry = f"{i}. {title} ({year}) [{cites} citations]"
|
|
265
|
+
entry += f"\n Authors: {authors}"
|
|
266
|
+
if venue:
|
|
267
|
+
entry += f"\n Venue: {venue}"
|
|
268
|
+
if tldr:
|
|
269
|
+
entry += f"\n TL;DR: {tldr}"
|
|
270
|
+
elif abstract:
|
|
271
|
+
entry += f"\n Abstract: {abstract}..."
|
|
272
|
+
entry += f"\n URL: {paper_url}"
|
|
273
|
+
out.append(entry)
|
|
274
|
+
return "\n\n".join(out)
|
|
275
|
+
except _requests.exceptions.RequestException as e:
|
|
276
|
+
return f"Semantic Scholar API error: {e}"
|
|
277
|
+
except Exception as e:
|
|
278
|
+
return f"Paper search error: {e}"
|
|
279
|
+
|
|
202
280
|
# ========== File Provenance (matching original) ==========
|
|
203
281
|
@dataclass
|
|
204
282
|
class FileProvenance:
|
|
@@ -362,14 +440,14 @@ steps:
|
|
|
362
440
|
except Exception as e:
|
|
363
441
|
return f"Wander failed: {e}"
|
|
364
442
|
|
|
365
|
-
tools = [create_file, append_to_file, replace_in_file, read_file,
|
|
366
|
-
list_files,
|
|
443
|
+
tools = [run_python, create_file, append_to_file, replace_in_file, read_file,
|
|
444
|
+
list_files, _web_search_tool, search_papers, shell_command, wander_wrapper]
|
|
367
445
|
|
|
368
446
|
agent = NPC(
|
|
369
447
|
name=agent_name.replace(' ', '_').lower(),
|
|
370
448
|
model=_model,
|
|
371
449
|
provider=_provider,
|
|
372
|
-
primary_directive=agent_persona,
|
|
450
|
+
primary_directive=_alicanto_directive + "\n\n" + agent_persona,
|
|
373
451
|
tools=tools
|
|
374
452
|
)
|
|
375
453
|
|
|
@@ -379,6 +457,7 @@ steps:
|
|
|
379
457
|
created_files = set()
|
|
380
458
|
summary = {}
|
|
381
459
|
major_step = 0
|
|
460
|
+
stall_count = 0 # consecutive steps with no filesystem change
|
|
382
461
|
|
|
383
462
|
while major_step < _max_steps:
|
|
384
463
|
# Check for skip/quit
|
|
@@ -401,37 +480,11 @@ steps:
|
|
|
401
480
|
history_str = "\n".join(summarized_history)
|
|
402
481
|
next_step_text = f"This is the next step suggested by your advisor. : BEGIN NEXT_STEP: {summary.get('next_step')} END NEXT STEP" if summary else ""
|
|
403
482
|
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
Use bash commands to carry out research through the execute_shell_command.
|
|
409
|
-
Adjust files with `replace_in_file` and use `read_file` and `list_files` to verify file states and file creation.
|
|
410
|
-
Create files with create_file()
|
|
411
|
-
|
|
412
|
-
Test with execute_shell_command when needed
|
|
413
|
-
Get unstuck with wander_wrapper
|
|
414
|
-
|
|
415
|
-
When you have a definitive result, say RESEARCH_COMPLETE.
|
|
416
|
-
|
|
417
|
-
FILE PROVENANCE HISTORY:
|
|
418
|
-
{chr(10).join(provenance_summary)}
|
|
419
|
-
|
|
420
|
-
CURRENT FILES: {list(fs_before.keys())}
|
|
421
|
-
|
|
422
|
-
COMPLETE ACTION HISTORY:
|
|
423
|
-
BEGIN HISTORY
|
|
483
|
+
initial_prompt = f"""Hypothesis: '{hypothesis}'
|
|
484
|
+
Query: '{user_query}'
|
|
485
|
+
Files: {list(fs_before.keys())}
|
|
424
486
|
{history_str}
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
What specific action will you take next to test your hypothesis?
|
|
428
|
-
AVAILABLE TOOLS: create_file, append_to_file, replace_in_file, read_file, list_files, execute_shell_command, wander_wrapper, _web_search_tool.
|
|
429
|
-
|
|
430
|
-
Do not repeat actions. Use `_web_search_tool` with provider of {search_provider} to look up items if you are struggling.
|
|
431
|
-
|
|
432
|
-
{next_step_text}
|
|
433
|
-
|
|
434
|
-
Your goal is to research. To set up experiments, create figures, and produce data outputs in csvs for verification and reproducibility."""
|
|
487
|
+
{next_step_text}"""
|
|
435
488
|
|
|
436
489
|
ui_state['log'].append(f"\033[90m Major step {major_step + 1}\033[0m")
|
|
437
490
|
|
|
@@ -495,8 +548,16 @@ steps:
|
|
|
495
548
|
|
|
496
549
|
fs_after = get_filesystem_state()
|
|
497
550
|
new_files = set(fs_after.keys()) - set(fs_before.keys())
|
|
551
|
+
changed_files = {f for f in fs_after if fs_before.get(f) != fs_after.get(f)}
|
|
498
552
|
if new_files:
|
|
499
553
|
ui_state['log'].append(f" \033[32mNew files: {list(new_files)}\033[0m")
|
|
554
|
+
stall_count = 0
|
|
555
|
+
elif changed_files:
|
|
556
|
+
stall_count = 0
|
|
557
|
+
else:
|
|
558
|
+
stall_count += 1
|
|
559
|
+
if stall_count >= 3:
|
|
560
|
+
ui_state['log'].append(f" \033[33mStalled for {stall_count} steps, forcing wrap-up\033[0m")
|
|
500
561
|
|
|
501
562
|
combined_thought = " ".join(all_thoughts)
|
|
502
563
|
combined_action = " | ".join(filter(None, all_actions))
|
|
@@ -619,7 +680,7 @@ steps:
|
|
|
619
680
|
|
|
620
681
|
Focus ONLY on the {next_section} section. Write 2-4 paragraphs of substantial academic content.
|
|
621
682
|
|
|
622
|
-
Available tools: replace_in_file, read_file, _web_search_tool"""
|
|
683
|
+
Available tools: replace_in_file, read_file, _web_search_tool, search_papers"""
|
|
623
684
|
|
|
624
685
|
for micro in range(5):
|
|
625
686
|
if micro == 0:
|
|
@@ -743,7 +804,7 @@ steps:
|
|
|
743
804
|
Use replace_in_file to make targeted improvements to paper.tex.
|
|
744
805
|
Use read_file to check current state.
|
|
745
806
|
|
|
746
|
-
Available tools: replace_in_file, read_file, append_to_file, _web_search_tool"""
|
|
807
|
+
Available tools: replace_in_file, read_file, append_to_file, _web_search_tool, search_papers"""
|
|
747
808
|
|
|
748
809
|
coord_messages = []
|
|
749
810
|
for micro in range(8):
|
|
@@ -965,13 +1026,13 @@ steps:
|
|
|
965
1026
|
except Exception as e:
|
|
966
1027
|
return f"Wander failed: {e}"
|
|
967
1028
|
|
|
968
|
-
coord_tools = [create_file, append_to_file, replace_in_file, read_file,
|
|
969
|
-
list_files,
|
|
1029
|
+
coord_tools = [run_python, create_file, append_to_file, replace_in_file, read_file,
|
|
1030
|
+
list_files, _web_search_tool, search_papers, shell_command, wander_wrapper_coord]
|
|
970
1031
|
|
|
971
1032
|
coordinator = NPC(
|
|
972
1033
|
name="Alicanto",
|
|
973
1034
|
model=model, provider=provider,
|
|
974
|
-
primary_directive=
|
|
1035
|
+
primary_directive=_alicanto_directive,
|
|
975
1036
|
tools=coord_tools
|
|
976
1037
|
)
|
|
977
1038
|
|