Initial Arka plugin registry: Official plugins mirror + Arka-signed index
11 plugins from github.com/librefang/librefang-registry plugins/. index.json / index.json.sig are signed with Arka's Ed25519 key (not upstream stats.librefang.ai). Private key is not in this repo.
This commit is contained in:
commit
8dec8f6038
62 files changed
+5189
No files matched your search
@@ -0,0 +1,34 @@
|
||||
# conversation-logger
|
||||
|
||||
Logs all conversations to JSONL files for auditing, analytics, and debugging. Each agent gets its own log file at `~/.librefang/logs/conversations/{agent_id}.jsonl`.
|
||||
|
||||
## Log Format
|
||||
|
||||
Each line is a JSON object:
|
||||
|
||||
```json
|
||||
{
|
||||
"timestamp": "2026-03-21T12:34:56Z",
|
||||
"agent_id": "agent-abc123",
|
||||
"turn_number": 5,
|
||||
"message_count": 10,
|
||||
"last_user_message": "truncated to 200 chars...",
|
||||
"last_assistant_message": "truncated to 200 chars..."
|
||||
}
|
||||
```
|
||||
|
||||
## Hooks
|
||||
|
||||
| Hook | Script | Description |
|
||||
|------|--------|-------------|
|
||||
| after_turn | `hooks/after_turn.py` | Appends a log entry after each conversation turn |
|
||||
|
||||
## How It Works
|
||||
|
||||
After each conversation turn the hook extracts summary information from the messages array and appends a single JSON line to the agent's log file. User and assistant messages are truncated to 200 characters to keep log files manageable.
|
||||
|
||||
Errors from the filesystem (permissions, disk full, etc.) are caught silently so the agent conversation is never interrupted by a logging failure.
|
||||
|
||||
## Usage
|
||||
|
||||
Installed automatically when enabled in agent configuration. Log files are created on first write -- no manual setup required.
|
||||
@@ -0,0 +1,68 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Conversation logger after_turn hook.
|
||||
|
||||
Appends a JSON line to a per-agent log file after each conversation turn.
|
||||
Log files are stored under ~/.librefang/logs/conversations/{agent_id}.jsonl.
|
||||
|
||||
Receives via stdin:
|
||||
{"type": "after_turn", "agent_id": "...", "messages": [...]}
|
||||
|
||||
Prints to stdout:
|
||||
{"type": "ok"}
|
||||
"""
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def _log_dir() -> Path:
|
||||
"""Return the conversations log directory, creating it if needed."""
|
||||
home = Path.home()
|
||||
log_path = home / ".librefang" / "logs" / "conversations"
|
||||
log_path.mkdir(parents=True, exist_ok=True)
|
||||
return log_path
|
||||
|
||||
|
||||
def _last_message_by_role(messages: list, role: str) -> str:
|
||||
"""Find the last message with the given role and return its content truncated to 200 chars."""
|
||||
for msg in reversed(messages):
|
||||
if msg.get("role") == role:
|
||||
content = msg.get("content", "")
|
||||
if len(content) > 200:
|
||||
return content[:200] + "..."
|
||||
return content
|
||||
return ""
|
||||
|
||||
|
||||
def main():
|
||||
request = json.loads(sys.stdin.read())
|
||||
agent_id = request.get("agent_id", "unknown")
|
||||
messages = request.get("messages", [])
|
||||
|
||||
entry = {
|
||||
"timestamp": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"),
|
||||
"agent_id": agent_id,
|
||||
"turn_number": max(
|
||||
sum(1 for m in messages if m.get("role") == "assistant"), 1
|
||||
),
|
||||
"message_count": len(messages),
|
||||
"last_user_message": _last_message_by_role(messages, "user"),
|
||||
"last_assistant_message": _last_message_by_role(messages, "assistant"),
|
||||
}
|
||||
|
||||
try:
|
||||
log_file = _log_dir() / f"{agent_id}.jsonl"
|
||||
with open(log_file, "a", encoding="utf-8") as f:
|
||||
f.write(json.dumps(entry, ensure_ascii=False) + "\n")
|
||||
except OSError:
|
||||
# Filesystem issues should not crash the agent. The hook still
|
||||
# responds with "ok" so the conversation continues normally.
|
||||
pass
|
||||
|
||||
print(json.dumps({"type": "ok"}))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,39 @@
|
||||
name = "conversation-logger"
|
||||
version = "0.1.0"
|
||||
description = "Logs all conversations to JSONL files for auditing, analytics, and debugging"
|
||||
author = "librefang"
|
||||
|
||||
[hooks]
|
||||
after_turn = "hooks/after_turn.py"
|
||||
|
||||
|
||||
[i18n.zh]
|
||||
name = "对话日志"
|
||||
description = "将所有会话写入 JSONL 文件,便于审计、分析与调试。"
|
||||
|
||||
[i18n.zh-TW]
|
||||
name = "對話日誌"
|
||||
description = "將所有會話寫入 JSONL 檔案,便於稽核、分析與除錯。"
|
||||
|
||||
[i18n.ja]
|
||||
name = "会話ロガー"
|
||||
description = "全会話を JSONL に記録し、監査・分析・デバッグに利用。"
|
||||
|
||||
[i18n.ko]
|
||||
name = "대화 로거"
|
||||
description = "모든 대화를 JSONL 파일로 기록하여 감사, 분석, 디버깅에 활용."
|
||||
|
||||
[i18n.de]
|
||||
name = "Konversations-Logger"
|
||||
description = "Protokolliert alle Konversationen in JSONL-Dateien für Audit, Analytics und Debugging."
|
||||
|
||||
[i18n.es]
|
||||
name = "Registro de conversaciones"
|
||||
description = "Registra todas las conversaciones en archivos JSONL para auditoría, analítica y depuración."
|
||||
|
||||
[i18n.fr]
|
||||
name = "Journal de conversations"
|
||||
description = "Enregistre toutes les conversations en JSONL pour audit, analytics et débogage."
|
||||
|
||||
[integrity]
|
||||
"hooks/after_turn.py" = "19a008af65e6797186ac0cc5b88e6d3c889fb1e312a099d65b9e10ee749eb65e"
|
||||
Reference in new issue
Block a user