agentainer 0.1.2 → 0.1.3

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.
package/lib/minyaml.py CHANGED
@@ -125,9 +125,50 @@ def _is_balanced(s: str) -> bool:
125
125
  return depth <= 0
126
126
 
127
127
 
128
+ _DQ_ESCAPES = {
129
+ '"': '"', "\\": "\\", "/": "/", "n": "\n", "t": "\t", "r": "\r",
130
+ "b": "\b", "f": "\f", "v": "\x0b", "0": "\x00", "a": "\x07", "e": "\x1b",
131
+ }
132
+
133
+
134
+ def _unescape_double(body: str) -> str:
135
+ """Resolve backslash escapes in a double-quoted scalar.
136
+
137
+ A plain ``bytes.decode("unicode_escape")`` is latin-1 based and mangles any
138
+ non-ASCII character (``"café"`` -> ``"café"``), so escapes are expanded by
139
+ hand and every other character -- including multi-byte UTF-8 -- is kept as-is.
140
+ """
141
+ out: list[str] = []
142
+ i, n = 0, len(body)
143
+ while i < n:
144
+ ch = body[i]
145
+ if ch != "\\" or i + 1 >= n:
146
+ out.append(ch)
147
+ i += 1
148
+ continue
149
+ nxt = body[i + 1]
150
+ if nxt == "u" and i + 6 <= n:
151
+ try:
152
+ out.append(chr(int(body[i + 2 : i + 6], 16)))
153
+ i += 6
154
+ continue
155
+ except ValueError:
156
+ pass
157
+ elif nxt == "x" and i + 4 <= n:
158
+ try:
159
+ out.append(chr(int(body[i + 2 : i + 4], 16)))
160
+ i += 4
161
+ continue
162
+ except ValueError:
163
+ pass
164
+ out.append(_DQ_ESCAPES.get(nxt, nxt))
165
+ i += 2
166
+ return "".join(out)
167
+
168
+
128
169
  def _unquote(s: str) -> str:
129
170
  if len(s) >= 2 and s[0] == s[-1] == '"':
130
- return s[1:-1].encode("utf-8").decode("unicode_escape")
171
+ return _unescape_double(s[1:-1])
131
172
  if len(s) >= 2 and s[0] == s[-1] == "'":
132
173
  return s[1:-1].replace("''", "'")
133
174
  return s
package/lib/swarm.py CHANGED
@@ -2024,11 +2024,15 @@ def cmd_broadcast(args) -> int:
2024
2024
  cfg, sender, peer, text,
2025
2025
  enforce_acl=not args.force,
2026
2026
  allow_busy=args.force or args.ignore_busy,
2027
+ # A broadcast is an announcement, never a question -- so it must not
2028
+ # saddle every recipient with a reply obligation (and a later nag).
2029
+ # Mirrors the tagged <swarm-broadcast> path in parse_outbound().
2030
+ expects_reply=False,
2027
2031
  )
2028
2032
  info(f"{sender} -> {peer}: delivered")
2029
2033
  except BusyError as exc:
2030
2034
  if args.queue:
2031
- _, depth = enqueue(cfg, sender, peer, text, hops=0)
2035
+ _, depth = enqueue(cfg, sender, peer, text, hops=0, expects_reply=False)
2032
2036
  info(f"{sender} -> {peer}: busy, queued at position {depth}")
2033
2037
  else:
2034
2038
  warn(f"{sender} -> {peer}: {exc.args[0].splitlines()[0]}")
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentainer",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "Run a configurable swarm of coding-agent CLIs (Claude Code, Codex, Gemini) in tmux and let them talk to each other.",
5
5
  "keywords": [
6
6
  "agents",