agentic-comms 0.3.0__tar.gz → 0.3.2__tar.gz
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.
- {agentic_comms-0.3.0 → agentic_comms-0.3.2}/PKG-INFO +1 -1
- {agentic_comms-0.3.0 → agentic_comms-0.3.2}/agent_comms/cli.py +5 -1
- {agentic_comms-0.3.0 → agentic_comms-0.3.2}/agent_comms/hook.py +28 -8
- {agentic_comms-0.3.0 → agentic_comms-0.3.2}/agent_comms/install.py +3 -3
- {agentic_comms-0.3.0 → agentic_comms-0.3.2}/agentic_comms.egg-info/PKG-INFO +1 -1
- {agentic_comms-0.3.0 → agentic_comms-0.3.2}/pyproject.toml +1 -1
- {agentic_comms-0.3.0 → agentic_comms-0.3.2}/README.md +0 -0
- {agentic_comms-0.3.0 → agentic_comms-0.3.2}/agent_comms/__init__.py +0 -0
- {agentic_comms-0.3.0 → agentic_comms-0.3.2}/agent_comms/__main__.py +0 -0
- {agentic_comms-0.3.0 → agentic_comms-0.3.2}/agent_comms/api.py +0 -0
- {agentic_comms-0.3.0 → agentic_comms-0.3.2}/agent_comms/config.py +0 -0
- {agentic_comms-0.3.0 → agentic_comms-0.3.2}/agentic_comms.egg-info/SOURCES.txt +0 -0
- {agentic_comms-0.3.0 → agentic_comms-0.3.2}/agentic_comms.egg-info/dependency_links.txt +0 -0
- {agentic_comms-0.3.0 → agentic_comms-0.3.2}/agentic_comms.egg-info/entry_points.txt +0 -0
- {agentic_comms-0.3.0 → agentic_comms-0.3.2}/agentic_comms.egg-info/requires.txt +0 -0
- {agentic_comms-0.3.0 → agentic_comms-0.3.2}/agentic_comms.egg-info/top_level.txt +0 -0
- {agentic_comms-0.3.0 → agentic_comms-0.3.2}/setup.cfg +0 -0
- {agentic_comms-0.3.0 → agentic_comms-0.3.2}/tests/test_cli.py +0 -0
|
@@ -14,6 +14,7 @@ stored in ~/.config/agent-comms/sessions/. `comms init` creates one,
|
|
|
14
14
|
from __future__ import annotations
|
|
15
15
|
|
|
16
16
|
import json
|
|
17
|
+
import os
|
|
17
18
|
import sys
|
|
18
19
|
from typing import Optional
|
|
19
20
|
|
|
@@ -256,13 +257,16 @@ def check(
|
|
|
256
257
|
handle: Optional[str] = typer.Option(None, help="Defaults to current identity."),
|
|
257
258
|
brief: bool = typer.Option(False, "--brief", help="Hide bodies."),
|
|
258
259
|
json_: bool = typer.Option(False, "--json"),
|
|
259
|
-
hook: bool = typer.Option(False, "--hook", help="Run as Claude Code
|
|
260
|
+
hook: bool = typer.Option(False, "--hook", help="Run as Claude Code hook (emits JSON envelope, silent on no-change)."),
|
|
261
|
+
with_control: bool = typer.Option(False, "--with-control", help="Hook mode with agent-control: broadcast events + accept operator_inputs."),
|
|
260
262
|
):
|
|
261
263
|
"""Silent check for unread DMs. Prints nothing if empty. Otherwise prints each
|
|
262
264
|
unread message in full (title, summary, body) so one command = complete read.
|
|
263
265
|
Always exits 0 (so hooks don't fail)."""
|
|
264
266
|
if hook:
|
|
265
267
|
from . import hook as hook_mod
|
|
268
|
+
if with_control:
|
|
269
|
+
os.environ["AGENT_COMMS_CONTROL"] = "1"
|
|
266
270
|
raise typer.Exit(hook_mod.main())
|
|
267
271
|
ident = config.load_identity() if not handle else None
|
|
268
272
|
h = handle or (ident.handle if ident else None)
|
|
@@ -21,6 +21,7 @@ import os
|
|
|
21
21
|
import re
|
|
22
22
|
import sys
|
|
23
23
|
import time
|
|
24
|
+
import uuid
|
|
24
25
|
from pathlib import Path
|
|
25
26
|
|
|
26
27
|
from . import config
|
|
@@ -188,14 +189,24 @@ def _post_event(client, ident, event_name: str, hook_input: dict) -> None:
|
|
|
188
189
|
elif event_name == "UserPromptSubmit":
|
|
189
190
|
client._h.post("/api/events", json={**common, "kind": "user_prompt",
|
|
190
191
|
"text": _preview(hook_input.get("prompt"))})
|
|
191
|
-
elif event_name
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
"
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
192
|
+
elif event_name in ("PreToolUse", "PostToolUse"):
|
|
193
|
+
tool_name = hook_input.get("tool_name") or ""
|
|
194
|
+
# Skip noisy read-only tools by default (opt-in via env to broadcast all).
|
|
195
|
+
if os.environ.get("AGENT_COMMS_VERBOSE") != "1":
|
|
196
|
+
if tool_name in ("Read", "Grep", "Glob", "TodoRead", "TodoWrite"):
|
|
197
|
+
return
|
|
198
|
+
call_id = _call_id_for(hook_input)
|
|
199
|
+
if event_name == "PreToolUse":
|
|
200
|
+
client._h.post("/api/events", json={
|
|
201
|
+
**common, "kind": "tool_pre", "name": tool_name, "call_id": call_id,
|
|
202
|
+
"input_preview": _preview(json.dumps(hook_input.get("tool_input") or {}, default=str)),
|
|
203
|
+
})
|
|
204
|
+
else:
|
|
205
|
+
client._h.post("/api/events", json={
|
|
206
|
+
**common, "kind": "tool_post", "name": tool_name, "call_id": call_id,
|
|
207
|
+
"input_preview": _preview(json.dumps(hook_input.get("tool_input") or {}, default=str)),
|
|
208
|
+
"result_preview": _preview(_stringify_tool_response(hook_input.get("tool_response"))),
|
|
209
|
+
})
|
|
199
210
|
elif event_name == "Stop":
|
|
200
211
|
msg = hook_input.get("last_assistant_message")
|
|
201
212
|
last_op = _safe_read(_cache_dir() / f"last-op-{_hash(ident.handle)}")
|
|
@@ -208,6 +219,15 @@ def _post_event(client, ident, event_name: str, hook_input: dict) -> None:
|
|
|
208
219
|
pass
|
|
209
220
|
|
|
210
221
|
|
|
222
|
+
def _call_id_for(hook_input: dict) -> str:
|
|
223
|
+
"""Stable call_id per (session_id, tool_input) so PreToolUse and PostToolUse pair up.
|
|
224
|
+
Claude Code doesn't hand us a native id, so we hash the tool_input — deterministic
|
|
225
|
+
within a single tool call, distinct across calls."""
|
|
226
|
+
session = hook_input.get("session_id") or ""
|
|
227
|
+
ti = json.dumps(hook_input.get("tool_input") or {}, sort_keys=True, default=str)
|
|
228
|
+
return hashlib.sha256(f"{session}:{ti}".encode()).hexdigest()[:12]
|
|
229
|
+
|
|
230
|
+
|
|
211
231
|
def _stringify_tool_response(resp) -> str | None:
|
|
212
232
|
if resp is None:
|
|
213
233
|
return None
|
|
@@ -14,7 +14,7 @@ from pathlib import Path
|
|
|
14
14
|
HOOK_TIMEOUT = 5
|
|
15
15
|
MARKER_FIELD = "agent_comms_marker" # embedded in the hook entry so we can find+remove
|
|
16
16
|
BASE_EVENTS = ("PostToolUse", "UserPromptSubmit")
|
|
17
|
-
CONTROL_EVENTS = ("PostToolUse", "UserPromptSubmit", "Stop", "SessionStart", "SessionEnd")
|
|
17
|
+
CONTROL_EVENTS = ("PreToolUse", "PostToolUse", "UserPromptSubmit", "Stop", "SessionStart", "SessionEnd")
|
|
18
18
|
|
|
19
19
|
|
|
20
20
|
def _hook_command(control: bool = False) -> str:
|
|
@@ -63,9 +63,9 @@ def install(project_scope: bool = False, with_control: bool = False) -> tuple[Pa
|
|
|
63
63
|
|
|
64
64
|
events = CONTROL_EVENTS if with_control else BASE_EVENTS
|
|
65
65
|
|
|
66
|
-
# When toggling off control, strip any
|
|
66
|
+
# When toggling off control, strip any control-only event entries we previously added.
|
|
67
67
|
if not with_control:
|
|
68
|
-
for e in ("Stop", "SessionStart", "SessionEnd"):
|
|
68
|
+
for e in ("PreToolUse", "Stop", "SessionStart", "SessionEnd"):
|
|
69
69
|
entries = hooks.get(e) or []
|
|
70
70
|
new_entries = []
|
|
71
71
|
for group in entries:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|