agentic-comms 0.7.1__tar.gz → 0.7.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.7.1 → agentic_comms-0.7.2}/PKG-INFO +1 -1
- {agentic_comms-0.7.1 → agentic_comms-0.7.2}/agent_comms/config.py +1 -0
- {agentic_comms-0.7.1 → agentic_comms-0.7.2}/agent_comms/hook.py +41 -16
- {agentic_comms-0.7.1 → agentic_comms-0.7.2}/agentic_comms.egg-info/PKG-INFO +1 -1
- {agentic_comms-0.7.1 → agentic_comms-0.7.2}/pyproject.toml +1 -1
- {agentic_comms-0.7.1 → agentic_comms-0.7.2}/tests/test_cli.py +29 -16
- {agentic_comms-0.7.1 → agentic_comms-0.7.2}/README.md +0 -0
- {agentic_comms-0.7.1 → agentic_comms-0.7.2}/agent_comms/__init__.py +0 -0
- {agentic_comms-0.7.1 → agentic_comms-0.7.2}/agent_comms/__main__.py +0 -0
- {agentic_comms-0.7.1 → agentic_comms-0.7.2}/agent_comms/api.py +0 -0
- {agentic_comms-0.7.1 → agentic_comms-0.7.2}/agent_comms/cli.py +0 -0
- {agentic_comms-0.7.1 → agentic_comms-0.7.2}/agent_comms/install.py +0 -0
- {agentic_comms-0.7.1 → agentic_comms-0.7.2}/agentic_comms.egg-info/SOURCES.txt +0 -0
- {agentic_comms-0.7.1 → agentic_comms-0.7.2}/agentic_comms.egg-info/dependency_links.txt +0 -0
- {agentic_comms-0.7.1 → agentic_comms-0.7.2}/agentic_comms.egg-info/entry_points.txt +0 -0
- {agentic_comms-0.7.1 → agentic_comms-0.7.2}/agentic_comms.egg-info/requires.txt +0 -0
- {agentic_comms-0.7.1 → agentic_comms-0.7.2}/agentic_comms.egg-info/top_level.txt +0 -0
- {agentic_comms-0.7.1 → agentic_comms-0.7.2}/setup.cfg +0 -0
|
@@ -475,22 +475,34 @@ def _build_skill_block(handle: str, display_name: str | None, version: str) -> s
|
|
|
475
475
|
)
|
|
476
476
|
|
|
477
477
|
|
|
478
|
-
def _maybe_skill_block(handle: str, display_name: str | None,
|
|
479
|
-
"""Emit the onboarding skill block only on first install, session revive, or version upgrade.
|
|
478
|
+
def _maybe_skill_block(handle: str, display_name: str | None, session_id: str | None, version: str) -> str | None:
|
|
479
|
+
"""Emit the onboarding skill block only on first install, session revive, or version upgrade.
|
|
480
|
+
|
|
481
|
+
Keyed by (handle, session_id, version). session_id from hook stdin is stable per Claude
|
|
482
|
+
session and unique across them — strictly better than PID-walking, which lands on different
|
|
483
|
+
PIDs across hook subprocesses when multiple claudes are alive on the same host.
|
|
484
|
+
|
|
485
|
+
Falls back to a stale-shown_at TTL (12h) when session_id is unavailable, so we don't loop
|
|
486
|
+
forever on clients that don't carry one."""
|
|
480
487
|
state_path = _cache_dir() / "last-skill-shown.json"
|
|
481
488
|
try:
|
|
482
489
|
state = json.loads(state_path.read_text()) if state_path.exists() else {}
|
|
483
490
|
except Exception:
|
|
484
491
|
state = {}
|
|
485
|
-
|
|
492
|
+
last_session = state.get("session_id")
|
|
486
493
|
last_version = state.get("version")
|
|
487
|
-
|
|
494
|
+
last_handle = state.get("handle")
|
|
495
|
+
last_shown = state.get("shown_at") or 0
|
|
496
|
+
same_session = (
|
|
497
|
+
last_handle == handle and last_version == version
|
|
498
|
+
and (last_session == session_id if session_id else (time.time() - last_shown) < 12 * 3600)
|
|
499
|
+
)
|
|
500
|
+
if same_session:
|
|
488
501
|
return None
|
|
489
|
-
# Persist BEFORE returning the block so a hook crash mid-emit doesn't loop.
|
|
490
502
|
try:
|
|
491
503
|
state_path.parent.mkdir(parents=True, exist_ok=True)
|
|
492
504
|
state_path.write_text(json.dumps({
|
|
493
|
-
"
|
|
505
|
+
"session_id": session_id, "version": version,
|
|
494
506
|
"shown_at": time.time(), "handle": handle,
|
|
495
507
|
}))
|
|
496
508
|
except Exception:
|
|
@@ -506,20 +518,32 @@ def _get_version() -> str:
|
|
|
506
518
|
return "unknown"
|
|
507
519
|
|
|
508
520
|
|
|
509
|
-
def _detect_revive(handle: str, current_pid: int | None) -> bool:
|
|
510
|
-
"""Returns True if the stored
|
|
511
|
-
|
|
521
|
+
def _detect_revive(handle: str, current_session_id: str | None, current_pid: int | None) -> bool:
|
|
522
|
+
"""Returns True if the stored session_id for this identity differs from current.
|
|
523
|
+
Falls back to PID comparison if session_id is unavailable on either side.
|
|
524
|
+
Updates stored session_id + claude_pid as a side-effect."""
|
|
512
525
|
try:
|
|
513
526
|
ident = config.load_identity()
|
|
514
527
|
if not ident:
|
|
515
528
|
return False
|
|
516
|
-
|
|
517
|
-
|
|
529
|
+
prior_session = getattr(ident, "session_id", None)
|
|
530
|
+
prior_pid = ident.claude_pid
|
|
531
|
+
# Prefer session_id when both sides have one (stable across hook subprocesses)
|
|
532
|
+
if current_session_id and prior_session:
|
|
533
|
+
same = (prior_session == current_session_id)
|
|
534
|
+
else:
|
|
535
|
+
same = (prior_pid == current_pid)
|
|
536
|
+
if same:
|
|
537
|
+
# Refresh stored values opportunistically (no revive)
|
|
538
|
+
if current_session_id and prior_session != current_session_id:
|
|
539
|
+
ident.session_id = current_session_id
|
|
540
|
+
ident.save()
|
|
518
541
|
return False
|
|
519
|
-
#
|
|
542
|
+
# Real change — update + signal revive (only if there WAS a prior record)
|
|
543
|
+
ident.session_id = current_session_id
|
|
520
544
|
ident.claude_pid = current_pid
|
|
521
545
|
ident.save()
|
|
522
|
-
return
|
|
546
|
+
return (prior_session is not None) or (prior_pid is not None)
|
|
523
547
|
except Exception:
|
|
524
548
|
return False
|
|
525
549
|
|
|
@@ -572,10 +596,11 @@ def main() -> int:
|
|
|
572
596
|
early_pieces: list[str] = []
|
|
573
597
|
|
|
574
598
|
if event_name in ("PostToolUse", "UserPromptSubmit"):
|
|
575
|
-
|
|
576
|
-
|
|
599
|
+
session_id = hook_input.get("session_id")
|
|
600
|
+
revived = _detect_revive(handle, session_id, claude_pid)
|
|
601
|
+
# Skill onboarding block — fires once per (handle, session_id, version) tuple
|
|
577
602
|
skill_block = _maybe_skill_block(handle, ident.display_name if hasattr(ident, "display_name") else None,
|
|
578
|
-
|
|
603
|
+
session_id, _get_version())
|
|
579
604
|
if skill_block:
|
|
580
605
|
early_pieces.append(skill_block)
|
|
581
606
|
# Monitor liveness — fire every time until heartbeat is fresh
|
|
@@ -214,20 +214,31 @@ def test_skill_block_fires_on_first_install_then_suppressed(env, tmp_path, monke
|
|
|
214
214
|
monkeypatch.setenv("AGENT_COMMS_CACHE_DIR", str(tmp_path / "cache"))
|
|
215
215
|
from agent_comms.hook import _maybe_skill_block
|
|
216
216
|
# First call — no record yet, should emit
|
|
217
|
-
out1 = _maybe_skill_block("alpha", "Bob",
|
|
217
|
+
out1 = _maybe_skill_block("alpha", "Bob", "session-aaa", "0.7.0")
|
|
218
218
|
assert out1 is not None
|
|
219
219
|
assert "agent_comms_onboarding" in out1
|
|
220
|
-
# Second call same
|
|
221
|
-
out2 = _maybe_skill_block("alpha", "Bob",
|
|
220
|
+
# Second call same session_id+version — suppressed
|
|
221
|
+
out2 = _maybe_skill_block("alpha", "Bob", "session-aaa", "0.7.0")
|
|
222
222
|
assert out2 is None
|
|
223
|
-
#
|
|
224
|
-
out3 = _maybe_skill_block("alpha", "Bob",
|
|
223
|
+
# session_id changed (revive) — re-emit
|
|
224
|
+
out3 = _maybe_skill_block("alpha", "Bob", "session-bbb", "0.7.0")
|
|
225
225
|
assert out3 is not None
|
|
226
226
|
# Version changed (upgrade) — re-emit
|
|
227
|
-
out4 = _maybe_skill_block("alpha", "Bob",
|
|
227
|
+
out4 = _maybe_skill_block("alpha", "Bob", "session-bbb", "0.7.1")
|
|
228
228
|
assert out4 is not None
|
|
229
229
|
|
|
230
230
|
|
|
231
|
+
def test_skill_block_no_session_id_uses_ttl_fallback(env, tmp_path, monkeypatch):
|
|
232
|
+
monkeypatch.setenv("AGENT_COMMS_CACHE_DIR", str(tmp_path / "cache"))
|
|
233
|
+
from agent_comms.hook import _maybe_skill_block
|
|
234
|
+
# No session_id — first call emits
|
|
235
|
+
out1 = _maybe_skill_block("alpha", "Bob", None, "0.7.0")
|
|
236
|
+
assert out1 is not None
|
|
237
|
+
# Subsequent call within TTL — suppressed
|
|
238
|
+
out2 = _maybe_skill_block("alpha", "Bob", None, "0.7.0")
|
|
239
|
+
assert out2 is None
|
|
240
|
+
|
|
241
|
+
|
|
231
242
|
def test_monitor_warning_block_built(env):
|
|
232
243
|
from agent_comms.hook import _build_monitor_warning
|
|
233
244
|
s = _build_monitor_warning("test reason", "alpha")
|
|
@@ -241,17 +252,19 @@ def test_monitor_warning_block_built(env):
|
|
|
241
252
|
def test_detect_revive(env, tmp_path, monkeypatch):
|
|
242
253
|
from agent_comms import config as cfg
|
|
243
254
|
from agent_comms.hook import _detect_revive
|
|
244
|
-
# Force PID-walk to a fixed value for deterministic identity-file keying
|
|
245
255
|
monkeypatch.setattr(cfg, "find_claude_pid", lambda: 1234)
|
|
246
|
-
cwd = str(tmp_path / "work")
|
|
247
|
-
cfg.LocalIdentity(handle="alpha", server_url="x", cwd=cwd, claude_pid=1234).save()
|
|
248
|
-
# Same
|
|
249
|
-
assert _detect_revive("alpha", 1234) is False
|
|
250
|
-
# Different
|
|
251
|
-
monkeypatch.setattr(cfg, "find_claude_pid", lambda:
|
|
252
|
-
assert _detect_revive("alpha",
|
|
253
|
-
# After revive,
|
|
254
|
-
assert _detect_revive("alpha",
|
|
256
|
+
cwd = str(tmp_path / "work")
|
|
257
|
+
cfg.LocalIdentity(handle="alpha", server_url="x", cwd=cwd, claude_pid=1234, session_id="s-aaa").save()
|
|
258
|
+
# Same session_id → not a revive
|
|
259
|
+
assert _detect_revive("alpha", "s-aaa", 1234) is False
|
|
260
|
+
# Different session_id → revive (independent of PID)
|
|
261
|
+
monkeypatch.setattr(cfg, "find_claude_pid", lambda: 1234)
|
|
262
|
+
assert _detect_revive("alpha", "s-bbb", 1234) is True
|
|
263
|
+
# After revive, session_id is updated, so subsequent same call not a revive
|
|
264
|
+
assert _detect_revive("alpha", "s-bbb", 1234) is False
|
|
265
|
+
# PID thrashing while session_id stable should NOT trigger revive
|
|
266
|
+
assert _detect_revive("alpha", "s-bbb", 9999) is False
|
|
267
|
+
assert _detect_revive("alpha", "s-bbb", 7777) is False
|
|
255
268
|
|
|
256
269
|
|
|
257
270
|
def _suppress_warnings(tmp_path):
|
|
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
|