agent-chat-plugin 0.4.0__tar.gz → 0.5.0__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.
- {agent_chat_plugin-0.4.0/agent_chat_plugin.egg-info → agent_chat_plugin-0.5.0}/PKG-INFO +1 -1
- {agent_chat_plugin-0.4.0 → agent_chat_plugin-0.5.0/agent_chat_plugin.egg-info}/PKG-INFO +1 -1
- {agent_chat_plugin-0.4.0 → agent_chat_plugin-0.5.0}/chat.py +71 -47
- {agent_chat_plugin-0.4.0 → agent_chat_plugin-0.5.0}/pyproject.toml +1 -1
- {agent_chat_plugin-0.4.0 → agent_chat_plugin-0.5.0}/LICENSE +0 -0
- {agent_chat_plugin-0.4.0 → agent_chat_plugin-0.5.0}/README.md +0 -0
- {agent_chat_plugin-0.4.0 → agent_chat_plugin-0.5.0}/agent_chat/lease_store.py +0 -0
- {agent_chat_plugin-0.4.0 → agent_chat_plugin-0.5.0}/agent_chat/path_locks.py +0 -0
- {agent_chat_plugin-0.4.0 → agent_chat_plugin-0.5.0}/agent_chat/state_store.py +0 -0
- {agent_chat_plugin-0.4.0 → agent_chat_plugin-0.5.0}/agent_chat/task_model.py +0 -0
- {agent_chat_plugin-0.4.0 → agent_chat_plugin-0.5.0}/agent_chat/task_store.py +0 -0
- {agent_chat_plugin-0.4.0 → agent_chat_plugin-0.5.0}/agent_chat_plugin.egg-info/SOURCES.txt +0 -0
- {agent_chat_plugin-0.4.0 → agent_chat_plugin-0.5.0}/agent_chat_plugin.egg-info/dependency_links.txt +0 -0
- {agent_chat_plugin-0.4.0 → agent_chat_plugin-0.5.0}/agent_chat_plugin.egg-info/entry_points.txt +0 -0
- {agent_chat_plugin-0.4.0 → agent_chat_plugin-0.5.0}/agent_chat_plugin.egg-info/top_level.txt +0 -0
- {agent_chat_plugin-0.4.0 → agent_chat_plugin-0.5.0}/setup.cfg +0 -0
- {agent_chat_plugin-0.4.0 → agent_chat_plugin-0.5.0}/tests/test_capabilities.py +0 -0
- {agent_chat_plugin-0.4.0 → agent_chat_plugin-0.5.0}/tests/test_chat.py +0 -0
- {agent_chat_plugin-0.4.0 → agent_chat_plugin-0.5.0}/tests/test_hooks.py +0 -0
- {agent_chat_plugin-0.4.0 → agent_chat_plugin-0.5.0}/tests/test_leases.py +0 -0
- {agent_chat_plugin-0.4.0 → agent_chat_plugin-0.5.0}/tests/test_path_locks.py +0 -0
- {agent_chat_plugin-0.4.0 → agent_chat_plugin-0.5.0}/tests/test_plugin_manifest.py +0 -0
- {agent_chat_plugin-0.4.0 → agent_chat_plugin-0.5.0}/tests/test_state.py +0 -0
- {agent_chat_plugin-0.4.0 → agent_chat_plugin-0.5.0}/tests/test_tasks.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agent-chat-plugin
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.0
|
|
4
4
|
Summary: Peer AI agents coordinate through markdown messages in shared channel folders — no orchestrator, autonomous zero-token waiting, cross-platform.
|
|
5
5
|
Author: n24q02m
|
|
6
6
|
License: Apache-2.0
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agent-chat-plugin
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.0
|
|
4
4
|
Summary: Peer AI agents coordinate through markdown messages in shared channel folders — no orchestrator, autonomous zero-token waiting, cross-platform.
|
|
5
5
|
Author: n24q02m
|
|
6
6
|
License: Apache-2.0
|
|
@@ -420,8 +420,24 @@ def cmd_channels(root: Path, a):
|
|
|
420
420
|
print(f"(no channels yet under {root})")
|
|
421
421
|
return
|
|
422
422
|
rows = []
|
|
423
|
-
|
|
424
|
-
|
|
423
|
+
found_channels = []
|
|
424
|
+
# Optimization: Use os.scandir instead of Path.glob("*/_meta.json") to discover channels.
|
|
425
|
+
# This avoids instantiating thousands of Path objects for discarded subdirectories.
|
|
426
|
+
# Filters out hidden directories (starting with '.') to maintain parity with glob("*").
|
|
427
|
+
try:
|
|
428
|
+
with os.scandir(root) as it:
|
|
429
|
+
for entry in it:
|
|
430
|
+
if (
|
|
431
|
+
not entry.name.startswith(".")
|
|
432
|
+
and entry.is_dir()
|
|
433
|
+
and os.path.exists(os.path.join(entry.path, "_meta.json"))
|
|
434
|
+
):
|
|
435
|
+
found_channels.append(entry.name)
|
|
436
|
+
except OSError:
|
|
437
|
+
pass
|
|
438
|
+
for chan_name in sorted(found_channels):
|
|
439
|
+
chan = root / chan_name
|
|
440
|
+
meta_path = chan / "_meta.json"
|
|
425
441
|
try:
|
|
426
442
|
meta = json.loads(meta_path.read_text(encoding="utf-8"))
|
|
427
443
|
except (OSError, ValueError):
|
|
@@ -594,24 +610,32 @@ def cmd_wait(root: Path, a):
|
|
|
594
610
|
d = require_channel(root, a.channel)
|
|
595
611
|
cur = read_cursor(d, a.agent)
|
|
596
612
|
deadline = time.time() + a.timeout
|
|
613
|
+
last_mtime = 0
|
|
597
614
|
while True:
|
|
598
|
-
found = []
|
|
599
|
-
# Optimization: use os.scandir to avoid Path instantiation overhead for
|
|
600
|
-
# thousands of old messages per tick.
|
|
601
615
|
try:
|
|
602
|
-
|
|
603
|
-
for entry in it:
|
|
604
|
-
if not entry.name.endswith(".md"):
|
|
605
|
-
continue
|
|
606
|
-
seq = _seq_from_name(entry.name)
|
|
607
|
-
if seq is None or seq <= cur:
|
|
608
|
-
continue
|
|
609
|
-
p = Path(entry.path)
|
|
610
|
-
meta = parse_frontmatter(p)
|
|
611
|
-
if is_relevant(meta, a.agent):
|
|
612
|
-
found.append(p)
|
|
616
|
+
mtime = d.stat().st_mtime
|
|
613
617
|
except OSError:
|
|
614
|
-
|
|
618
|
+
mtime = 0
|
|
619
|
+
|
|
620
|
+
found = []
|
|
621
|
+
if mtime == 0 or mtime != last_mtime:
|
|
622
|
+
last_mtime = mtime
|
|
623
|
+
# Optimization: use os.scandir to avoid Path instantiation overhead for
|
|
624
|
+
# thousands of old messages per tick.
|
|
625
|
+
try:
|
|
626
|
+
with os.scandir(d) as it:
|
|
627
|
+
for entry in it:
|
|
628
|
+
if not entry.name.endswith(".md"):
|
|
629
|
+
continue
|
|
630
|
+
seq = _seq_from_name(entry.name)
|
|
631
|
+
if seq is None or seq <= cur:
|
|
632
|
+
continue
|
|
633
|
+
p = Path(entry.path)
|
|
634
|
+
meta = parse_frontmatter(p)
|
|
635
|
+
if is_relevant(meta, a.agent):
|
|
636
|
+
found.append(p)
|
|
637
|
+
except OSError:
|
|
638
|
+
pass
|
|
615
639
|
if found:
|
|
616
640
|
# Sort only the newly found messages
|
|
617
641
|
found.sort(key=lambda p: _seq_from_name(p.name))
|
|
@@ -777,12 +801,12 @@ def cmd_compact(root: Path, a):
|
|
|
777
801
|
print(f"compacted state for {a.channel} -> {a.channel}/state.md (open_tasks={len(summary.open_tasks)}, locks={len(summary.path_locks)}, decisions={len(summary.decisions)})")
|
|
778
802
|
|
|
779
803
|
def _event_body(path: Path) -> dict:
|
|
780
|
-
raw = path.read_text(encoding="utf-8")
|
|
781
|
-
parts = raw.split("---", 2)
|
|
782
|
-
body = parts[2].strip() if len(parts) >= 3 else ""
|
|
783
804
|
try:
|
|
805
|
+
raw = path.read_text(encoding="utf-8")
|
|
806
|
+
parts = raw.split("---", 2)
|
|
807
|
+
body = parts[2].strip() if len(parts) >= 3 else ""
|
|
784
808
|
return validate_adapter_event(json.loads(body))
|
|
785
|
-
except (json.JSONDecodeError, UnicodeError) as error:
|
|
809
|
+
except (json.JSONDecodeError, UnicodeError, OSError) as error:
|
|
786
810
|
raise AdapterEventError("EVENT_MALFORMED_BODY", path.name) from error
|
|
787
811
|
|
|
788
812
|
|
|
@@ -1118,50 +1142,50 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
1118
1142
|
|
|
1119
1143
|
|
|
1120
1144
|
s = sub.add_parser("init", help="create a channel")
|
|
1121
|
-
s.add_argument("channel")
|
|
1145
|
+
s.add_argument("channel", help="name of the channel to create")
|
|
1122
1146
|
s.add_argument("--members", help="comma-separated agent names")
|
|
1123
|
-
s.add_argument("--topic")
|
|
1147
|
+
s.add_argument("--topic", help="initial topic of the channel")
|
|
1124
1148
|
s.set_defaults(func=cmd_init)
|
|
1125
1149
|
|
|
1126
1150
|
s = sub.add_parser("channels", help="list channels")
|
|
1127
1151
|
s.set_defaults(func=cmd_channels)
|
|
1128
1152
|
|
|
1129
1153
|
s = sub.add_parser("roster", help="show a channel's members")
|
|
1130
|
-
s.add_argument("channel")
|
|
1154
|
+
s.add_argument("channel", help="channel to inspect")
|
|
1131
1155
|
s.set_defaults(func=cmd_roster)
|
|
1132
1156
|
|
|
1133
1157
|
s = sub.add_parser(
|
|
1134
1158
|
"post", help="post a message (body via --body/--body-file/stdin)"
|
|
1135
1159
|
)
|
|
1136
|
-
s.add_argument("channel")
|
|
1137
|
-
s.add_argument("--from", dest="sender", required=True)
|
|
1160
|
+
s.add_argument("channel", help="channel to post in")
|
|
1161
|
+
s.add_argument("--from", dest="sender", required=True, help="sender agent name")
|
|
1138
1162
|
s.add_argument("--to", help="recipient agent, or 'all' (default all)")
|
|
1139
|
-
s.add_argument("--title", required=True)
|
|
1163
|
+
s.add_argument("--title", required=True, help="message title")
|
|
1140
1164
|
s.add_argument("--reply", type=int, help="seq this replies to")
|
|
1141
|
-
s.add_argument("--status", default="discussion")
|
|
1142
|
-
s.add_argument("--body")
|
|
1143
|
-
s.add_argument("--body-file")
|
|
1165
|
+
s.add_argument("--status", default="discussion", help="message status (default: discussion)")
|
|
1166
|
+
s.add_argument("--body", help="literal message body content")
|
|
1167
|
+
s.add_argument("--body-file", help="read message body from file")
|
|
1144
1168
|
s.set_defaults(func=cmd_post)
|
|
1145
1169
|
|
|
1146
1170
|
event = sub.add_parser("event", help="post/read adapter-neutral events")
|
|
1147
1171
|
event_sub = event.add_subparsers(dest="event_cmd", required=True)
|
|
1148
1172
|
s = event_sub.add_parser("post", help="post a capability or status event")
|
|
1149
|
-
s.add_argument("channel")
|
|
1150
|
-
s.add_argument("--from", dest="sender", required=True)
|
|
1151
|
-
s.add_argument("--type", dest="event_type", choices=EVENT_TYPES, required=True)
|
|
1152
|
-
s.add_argument("--harness", required=True)
|
|
1153
|
-
s.add_argument("--status", choices=STATUS_VALUES)
|
|
1154
|
-
s.add_argument("--detail")
|
|
1155
|
-
s.add_argument("--primitives", action="append")
|
|
1173
|
+
s.add_argument("channel", help="channel to post the event in")
|
|
1174
|
+
s.add_argument("--from", dest="sender", required=True, help="sender agent name")
|
|
1175
|
+
s.add_argument("--type", dest="event_type", choices=EVENT_TYPES, required=True, help="type of the event")
|
|
1176
|
+
s.add_argument("--harness", required=True, help="harness name")
|
|
1177
|
+
s.add_argument("--status", choices=STATUS_VALUES, help="status of the agent")
|
|
1178
|
+
s.add_argument("--detail", help="optional details about the status")
|
|
1179
|
+
s.add_argument("--primitives", action="append", help="primitives supported by the agent")
|
|
1156
1180
|
s.set_defaults(func=cmd_event_post)
|
|
1157
1181
|
s = event_sub.add_parser("read", help="read validated adapter-neutral events")
|
|
1158
|
-
s.add_argument("channel")
|
|
1159
|
-
s.add_argument("--type", dest="event_type", choices=EVENT_TYPES)
|
|
1182
|
+
s.add_argument("channel", help="channel to read events from")
|
|
1183
|
+
s.add_argument("--type", dest="event_type", choices=EVENT_TYPES, help="filter by event type")
|
|
1160
1184
|
s.set_defaults(func=cmd_event_read)
|
|
1161
1185
|
|
|
1162
1186
|
s = sub.add_parser("read", help="print new messages for an agent (advances cursor)")
|
|
1163
|
-
s.add_argument("channel")
|
|
1164
|
-
s.add_argument("--as", dest="agent", required=True)
|
|
1187
|
+
s.add_argument("channel", help="channel to read from")
|
|
1188
|
+
s.add_argument("--as", dest="agent", required=True, help="agent reading the messages")
|
|
1165
1189
|
s.add_argument(
|
|
1166
1190
|
"--all", action="store_true", help="show entire thread, ignore relevance"
|
|
1167
1191
|
)
|
|
@@ -1171,15 +1195,15 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
1171
1195
|
s = sub.add_parser(
|
|
1172
1196
|
"wait", help="block (sleep-poll, 0 tokens) until a reply arrives"
|
|
1173
1197
|
)
|
|
1174
|
-
s.add_argument("channel")
|
|
1175
|
-
s.add_argument("--as", dest="agent", required=True)
|
|
1176
|
-
s.add_argument("--timeout", type=float, default=900.0)
|
|
1177
|
-
s.add_argument("--interval", type=float, default=5.0)
|
|
1198
|
+
s.add_argument("channel", help="channel to wait on")
|
|
1199
|
+
s.add_argument("--as", dest="agent", required=True, help="agent waiting for messages")
|
|
1200
|
+
s.add_argument("--timeout", type=float, default=900.0, help="maximum wait time in seconds")
|
|
1201
|
+
s.add_argument("--interval", type=float, default=5.0, help="polling interval in seconds")
|
|
1178
1202
|
s.set_defaults(func=cmd_wait)
|
|
1179
1203
|
|
|
1180
1204
|
s = sub.add_parser("peek", help="show last N messages without touching the cursor")
|
|
1181
|
-
s.add_argument("channel")
|
|
1182
|
-
s.add_argument("-n", type=int, default=3)
|
|
1205
|
+
s.add_argument("channel", help="channel to peek into")
|
|
1206
|
+
s.add_argument("-n", type=int, default=3, help="number of messages to show")
|
|
1183
1207
|
s.set_defaults(func=cmd_peek)
|
|
1184
1208
|
|
|
1185
1209
|
s = sub.add_parser("claim", help="atomically claim a task-<id>.md marker")
|
|
@@ -10,7 +10,7 @@ requires-python = ">=3.8"
|
|
|
10
10
|
license = { text = "Apache-2.0" }
|
|
11
11
|
authors = [{ name = "n24q02m" }]
|
|
12
12
|
keywords = ["multi-agent", "coordination", "markdown", "blackboard", "claude-code", "agent-skills", "peer-to-peer"]
|
|
13
|
-
version = "0.
|
|
13
|
+
version = "0.5.0" # managed by python-semantic-release; do not hand-edit
|
|
14
14
|
classifiers = [
|
|
15
15
|
"Programming Language :: Python :: 3",
|
|
16
16
|
"License :: OSI Approved :: Apache Software License",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agent_chat_plugin-0.4.0 → agent_chat_plugin-0.5.0}/agent_chat_plugin.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{agent_chat_plugin-0.4.0 → agent_chat_plugin-0.5.0}/agent_chat_plugin.egg-info/entry_points.txt
RENAMED
|
File without changes
|
{agent_chat_plugin-0.4.0 → agent_chat_plugin-0.5.0}/agent_chat_plugin.egg-info/top_level.txt
RENAMED
|
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
|