-
Notifications
You must be signed in to change notification settings - Fork 1
feat: skills/agents detection from user prompts #126
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: staging
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1083,6 +1083,28 @@ def process_stop_event(event: Dict, api_key: str): | |
| # Parse tool uses from Codex transcript (function_call/function_call_output pairs) | ||
| assistant_tool_uses = parse_codex_transcript_for_tools(transcript_path, user_prompt_timestamp) | ||
|
|
||
| # Fold in subagent invocations from SubagentStop hook events. Codex subagents | ||
| # run in their own transcript and don't appear as function_calls in the parent | ||
| # rollout, so we recover them from the accumulated hook events (same session, | ||
| # after the user prompt) and forward them as Task tool_uses for the gateway. | ||
| for log in logs: | ||
| log_session_id = log.get('session_id') or log.get('event', {}).get('session_id') | ||
| if log_session_id != session_id: | ||
| continue | ||
| log_event = log.get('event', {}) if 'event' in log else log | ||
| if log_event.get('hook_event_name') != 'SubagentStop': | ||
| continue | ||
| if user_prompt_timestamp and log.get('timestamp') and log.get('timestamp') <= user_prompt_timestamp: | ||
| continue | ||
| assistant_tool_uses.append({ | ||
| 'type': 'SubagentStop', | ||
| 'tool_name': 'Task', | ||
| 'tool_input': {'subagent_type': log_event.get('agent_type')}, | ||
| 'tool_response': { | ||
| 'last_assistant_message': log_event.get('last_assistant_message'), | ||
| }, | ||
| }) | ||
|
Comment on lines
+1086
to
+1106
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The new loop that scans all logs for |
||
|
|
||
| assistant_msg = { | ||
| 'role': 'assistant', | ||
| 'content': last_assistant_message or '' | ||
|
|
@@ -1427,13 +1449,13 @@ def main(): | |
| input_data = sys.stdin.read().strip() | ||
|
|
||
| if not input_data: | ||
| print('{"suppressOutput": true}', flush=True) | ||
| print("{}", flush=True) | ||
| return | ||
|
|
||
| try: | ||
| event = json.loads(input_data) | ||
| except json.JSONDecodeError: | ||
| print('{"suppressOutput": true}', flush=True) | ||
| print("{}", flush=True) | ||
| return | ||
|
|
||
| hook_event_name = event.get('hook_event_name') | ||
|
|
@@ -1465,7 +1487,8 @@ def main(): | |
| 'session_id': event.get('session_id'), | ||
| 'event': event | ||
| }) | ||
| response["suppressOutput"] = True | ||
| # Codex does not support suppressOutput — emit the plain response. | ||
| response.pop("suppressOutput", None) | ||
| print(json.dumps(response), flush=True) | ||
| return | ||
|
|
||
|
|
@@ -1485,12 +1508,12 @@ def main(): | |
|
|
||
| cleanup_old_logs() | ||
|
|
||
| print('{"suppressOutput": true}', flush=True) | ||
| print("{}", flush=True) | ||
|
|
||
| except Exception as e: | ||
| # Still return empty JSON object to Codex to indicate completion | ||
| log_error(f"Exception in main: {str(e)}", 'general') | ||
| print('{"suppressOutput": true}', flush=True) | ||
| print("{}", flush=True) | ||
|
|
||
|
|
||
| if __name__ == '__main__': | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -1003,12 +1003,28 @@ def build_llm_exchange(events, api_key=None): | |||||||||
|
|
||||||||||
| elif hook_event_name == 'afterAgentResponse': | ||||||||||
| assistant_response = event.get('text') | ||||||||||
|
|
||||||||||
|
|
||||||||||
| elif hook_event_name == 'subagentStop': | ||||||||||
| assistant_tool_uses.append({ | ||||||||||
| 'type': 'subagentStop', | ||||||||||
| 'tool_name': 'Task', | ||||||||||
| 'tool_input': { | ||||||||||
| 'subagent_type': event.get('subagent_type'), | ||||||||||
| 'task': event.get('task'), | ||||||||||
| }, | ||||||||||
| 'tool_response': { | ||||||||||
| 'status': event.get('status'), | ||||||||||
| 'summary': event.get('summary'), | ||||||||||
| 'duration_ms': event.get('duration_ms'), | ||||||||||
| 'tool_call_count': event.get('tool_call_count'), | ||||||||||
| }, | ||||||||||
| }) | ||||||||||
|
|
||||||||||
| if user_prompt: | ||||||||||
| messages.append({'role': 'user', 'content': user_prompt}) | ||||||||||
| if assistant_response: | ||||||||||
| assistant_msg = {'role': 'assistant', 'content': assistant_response} | ||||||||||
|
|
||||||||||
| if assistant_response or assistant_tool_uses: | ||||||||||
| assistant_msg = {'role': 'assistant', 'content': assistant_response or ''} | ||||||||||
| if assistant_tool_uses: | ||||||||||
| assistant_msg['tool_use'] = assistant_tool_uses | ||||||||||
| messages.append(assistant_msg) | ||||||||||
|
|
@@ -1157,7 +1173,16 @@ def process_stop_event(generation_id, api_key=None): | |||||||||
| ) | ||||||||||
|
|
||||||||||
| if has_stop: | ||||||||||
| exchange = build_llm_exchange(events, api_key) | ||||||||||
| turn_start = min((l.get('timestamp', '') for l in events), default='') | ||||||||||
| subagent_events = [ | ||||||||||
| log | ||||||||||
| for gen_events in generations.values() | ||||||||||
| for log in gen_events | ||||||||||
| if log.get('event', {}).get('hook_event_name') | ||||||||||
| in ('subagentStart', 'subagentStop') | ||||||||||
|
Comment on lines
+1181
to
+1182
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
| and (not turn_start or log.get('timestamp', '') >= turn_start) | ||||||||||
| ] | ||||||||||
|
Comment on lines
+1176
to
+1184
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time! |
||||||||||
| exchange = build_llm_exchange(events + subagent_events, api_key) | ||||||||||
|
Comment on lines
+1177
to
+1185
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The new subagent detection and forwarding path has zero instrumentation. Per the observability requirements, every new flow must ship a latency histogram and success/failure counters at minimum. For this specific flow, useful metrics would be: a counter for Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time! |
||||||||||
| if exchange: | ||||||||||
| send_to_api(exchange, api_key) | ||||||||||
| break | ||||||||||
|
|
||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same observability gap as in
cursor/unbound.py: the newSubagentStopscanning loop has no Prometheus counters or histograms. Suggested additions:subagent_stop_events_forwarded_total(labelledsubagent_type) to track which subagent types are active, and asubagent_stop_events_skipped_totalcounter when the timestamp filter drops an event. Without these, there is no signal to distinguish "subagents not running" from "subagent events being silently filtered out".Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!