Skip to content
10 changes: 8 additions & 2 deletions app/api/v2/managers/operation_api_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,10 +98,11 @@ async def create_potential_link(self, operation_id: str, data: dict, access: Bas
agent = await self.get_agent(operation, data)
if data['executor']['name'] not in agent.executors:
raise JsonHttpBadRequest(f'Agent {agent.paw} missing specified executor')
encoded_command = self._encode_string(agent.replace(self._encode_string(data['executor']['command']),
file_svc=self.services['file_svc']))
executor = self.build_executor(data=data.pop('executor', {}), agent=agent)
ability = self.build_ability(data=data.pop('ability', {}), executor=executor)
await self._call_ability_plugin_hooks(ability, executor)
encoded_command = self._encode_string(agent.replace(self._encode_string(data['executor']['command']),
file_svc=self.services['file_svc']))
link = Link.load(dict(command=encoded_command, plaintext_command=encoded_command, paw=agent.paw, ability=ability, executor=executor,
status=operation.link_status(), score=data.get('score', 0), jitter=data.get('jitter', 0),
cleanup=data.get('cleanup', 0), pin=data.get('pin', 0),
Expand Down Expand Up @@ -171,6 +172,11 @@ async def _construct_and_dump_source(self, source_id: str):
source = (await self.services['data_svc'].locate('sources', match=dict(name='basic')))
return SourceSchema().dump(source[0])

async def _call_ability_plugin_hooks(self, ability, executor):
"""Calls any plugin hooks (at runtime) that exist for the ability and executor."""
for _hook, fcall in executor.HOOKS.items():
await fcall(ability, executor)
Copy link

Copilot AI Sep 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code will fail with an AttributeError if executor.HOOKS is None or undefined. Add a null check before iterating: if executor.HOOKS: before the for loop.

Suggested change
for _hook, fcall in executor.HOOKS.items():
await fcall(ability, executor)
if executor.HOOKS:
for _hook, fcall in executor.HOOKS.items():
await fcall(ability, executor)

Copilot uses AI. Check for mistakes.

async def validate_operation_state(self, data: dict, existing: Operation = None):
if not existing:
if data.get('state') in Operation.get_finished_states():
Expand Down
9 changes: 6 additions & 3 deletions app/service/planning_svc.py
Original file line number Diff line number Diff line change
Expand Up @@ -351,9 +351,7 @@ async def _generate_new_links(self, operation, agent, abilities, link_status):
executor = await agent.get_preferred_executor(ability)
if not executor:
continue

if executor.HOOKS and executor.language and executor.language in executor.HOOKS:
await executor.HOOKS[executor.language](ability, executor)
await self._call_ability_plugin_hooks(ability, executor)
if executor.command:
link = Link.load(dict(command=self.encode_string(executor.test), paw=agent.paw, score=0,
ability=ability, executor=executor, status=link_status,
Expand Down Expand Up @@ -392,6 +390,11 @@ async def _generate_cleanup_links(self, operation, agent, link_status):
links.append(lnk)
return links

async def _call_ability_plugin_hooks(self, ability, executor):
"""Calls any plugin hooks (at runtime) that exist for the ability and executor."""
for _hook, fcall in executor.HOOKS.items():
await fcall(ability, executor)
Copy link

Copilot AI Sep 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code will fail with an AttributeError if executor.HOOKS is None or undefined. Add a null check before iterating: if executor.HOOKS: before the for loop.

Suggested change
for _hook, fcall in executor.HOOKS.items():
await fcall(ability, executor)
if executor.HOOKS:
for _hook, fcall in executor.HOOKS.items():
await fcall(ability, executor)

Copilot uses AI. Check for mistakes.

Comment on lines 393 to 399
Copy link

Copilot AI Sep 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The duplicated hook execution logic in both files should be extracted to a shared utility or base class to avoid code duplication and ensure consistent behavior.

Suggested change
async def _call_ability_plugin_hooks(self, ability, executor):
"""Calls any plugin hooks (at runtime) that exist for the ability and executor."""
for _hook, fcall in executor.HOOKS.items():
await fcall(ability, executor)
# _call_ability_plugin_hooks is now implemented in BasePlanningService

Copilot uses AI. Check for mistakes.
@staticmethod
async def _apply_adjustments(operation, links):
"""Apply operation source ability adjustments to links
Expand Down
Loading