agents-city 0.5.3 → 0.5.5
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/.claude-plugin/marketplace.json +1 -1
- package/README.es.md +11 -0
- package/README.md +10 -0
- package/benchmarks/latency/fake-claude-cli.mjs +3 -2
- package/bin/serve.py +48 -8
- package/bin/test +2 -2
- package/bin/test-alcance.py +470 -0
- package/bin/test-channel.py +261 -7
- package/bin/test-cities.py +57 -0
- package/bin/test-claude-runtime.py +17 -6
- package/bin/test-connect-client.mjs +8 -0
- package/bin/test-connect.py +1 -1
- package/bin/test-contracts.py +145 -4
- package/bin/test-e2e.py +371 -0
- package/bin/test-seat.py +102 -1
- package/bin/test-serve.py +59 -0
- package/city/web/dist-hall/hall.js +384 -3240
- package/docs/managed-connect.md +5 -0
- package/package.json +5 -2
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/channel/hub/lo-que-te-llega.ts +78 -0
- package/plugin/channel/hub/local-roads.ts +36 -0
- package/plugin/channel/hub/road-controller.ts +30 -5
- package/plugin/channel/local-hub.js +177 -105
- package/plugin/channel/local-hub.ts +8 -0
- package/plugin/channel/managed-connect-client.js +53 -3
- package/plugin/channel/managed-connect-client.manifest.json +2 -2
- package/plugin/channel/protocol.ts +24 -0
- package/plugin/commands/notice.md +14 -0
- package/plugin/hooks/ask-the-house.sh +25 -0
- package/plugin/hooks/hooks.json +10 -0
- package/plugin/hooks/notice-on-stop.sh +7 -1
- package/plugin/scripts/alcance.py +318 -0
- package/plugin/scripts/busca.py +13 -0
- package/plugin/scripts/card.py +40 -0
- package/plugin/scripts/cities.py +46 -4
- package/plugin/scripts/city-session.sh +80 -7
- package/plugin/scripts/domains.py +7 -0
- package/plugin/scripts/seat.py +33 -13
- package/plugin/skills/city/SKILL.md +12 -0
package/README.es.md
CHANGED
|
@@ -653,6 +653,7 @@ agents-city seat --no-yolo --no-sync
|
|
|
653
653
|
| `--model ALIAS` | override de modelo para todas las ventanas de este arranque |
|
|
654
654
|
| `--effort LEVEL` | override `low`, `medium`, `high`, `xhigh` o `max` para este arranque |
|
|
655
655
|
| `--seat-yolo on\|off` | si el propio asiento corre sin preguntar permisos; persiste por ciudad (`seat_yolo` en `city.yml`, también la sexta pregunta del asistente). En local el asiento son las manos del dueño; las ventanas de repo conservan su propia historia de yolo/jaula |
|
|
656
|
+
| `--seat-reach open\|closed` | si el propio asiento puede trabajar dentro de los mounts de sus agentes; persiste por ciudad (`seat_reach` en `city.yml`). Cerrado por defecto: el asiento pregunta al agente dueño de ese terreno en vez de leerlo, y el rechazo dice quién es y con qué comando se le pregunta |
|
|
656
657
|
| `--no-yolo` | desactiva autoaprobación en este arranque — asiento incluido, diga lo que diga `seat_yolo` |
|
|
657
658
|
| `--no-sync` | no hace `git fetch/pull` inicial en repos de este arranque |
|
|
658
659
|
|
|
@@ -1818,6 +1819,7 @@ slug: producto
|
|
|
1818
1819
|
owner: alice
|
|
1819
1820
|
domain: software
|
|
1820
1821
|
seat_yolo: 1
|
|
1822
|
+
seat_reach: closed
|
|
1821
1823
|
```
|
|
1822
1824
|
|
|
1823
1825
|
El address público se deriva como `owner/slug`; no se guarda como una identidad
|
|
@@ -1826,6 +1828,15 @@ global del plugin porque varias ciudades pueden ejecutarse a la vez.
|
|
|
1826
1828
|
sexta pregunta del asistente o con `agents-city seat --seat-yolo on|off`;
|
|
1827
1829
|
`--no-yolo` sigue frenando la sesión entera.
|
|
1828
1830
|
|
|
1831
|
+
`seat_reach` dice si la silla tiene manos dentro de los mounts de sus agentes.
|
|
1832
|
+
Está `closed` mientras no escribas `open`, y cerrado significa que una llamada a
|
|
1833
|
+
una herramienta que cae sobre un repo, un worktree o una carpeta de documentos
|
|
1834
|
+
que montó un agente se rechaza, con el nombre de quien es su dueño, su rol y el
|
|
1835
|
+
comando que le pregunta. La silla conserva la carpeta de la ciudad, las
|
|
1836
|
+
carreteras y el registro. Existe porque el fallo que evita no deja rastro: un
|
|
1837
|
+
asiento que lee el código y responde es idéntico a uno que consultó a sus
|
|
1838
|
+
especialistas.
|
|
1839
|
+
|
|
1829
1840
|
### `<owner>.md`: ficha del asiento
|
|
1830
1841
|
|
|
1831
1842
|
Es Markdown con frontmatter. Contiene el rol del asiento, repos, rol de cada repo,
|
package/README.md
CHANGED
|
@@ -644,6 +644,7 @@ agents-city seat --no-yolo --no-sync
|
|
|
644
644
|
| `--model ALIAS` | model override for all windows in this launch |
|
|
645
645
|
| `--effort LEVEL` | `low`, `medium`, `high`, `xhigh`, or `max` override for this launch |
|
|
646
646
|
| `--seat-yolo on\|off` | whether the chair itself runs without permission prompts; persists per city (`city.yml seat_yolo`, also question six of the wizard). Locally the seat is the owner's own hands; repo windows keep their own yolo/cage story either way |
|
|
647
|
+
| `--seat-reach open\|closed` | whether the chair may work inside its agents' mounts; persists per city (`city.yml seat_reach`). Closed by default: the seat asks the agent who owns that ground instead of reading it, and a refusal names them and the command |
|
|
647
648
|
| `--no-yolo` | disable auto-approval for this launch — seat included, whatever `seat_yolo` says |
|
|
648
649
|
| `--no-sync` | skip initial `git fetch/pull` in repos for this launch |
|
|
649
650
|
|
|
@@ -1798,6 +1799,7 @@ slug: product
|
|
|
1798
1799
|
owner: alice
|
|
1799
1800
|
domain: software
|
|
1800
1801
|
seat_yolo: 1
|
|
1802
|
+
seat_reach: closed
|
|
1801
1803
|
```
|
|
1802
1804
|
|
|
1803
1805
|
The public address is derived as `owner/slug`. It is not stored as one global
|
|
@@ -1806,6 +1808,14 @@ launches the chair itself without permission prompts — set at the wizard's
|
|
|
1806
1808
|
sixth question or with `agents-city seat --seat-yolo on|off`; `--no-yolo`
|
|
1807
1809
|
still brakes the whole session.
|
|
1808
1810
|
|
|
1811
|
+
`seat_reach` says whether the chair has hands inside its agents' mounts. It is
|
|
1812
|
+
`closed` unless you write `open`, and closed means a tool call landing on a
|
|
1813
|
+
repo, worktree or document folder an agent mounted is refused, with the owner's
|
|
1814
|
+
name, their role and the one command that asks them. The chair keeps the city
|
|
1815
|
+
folder, the roads and the record. This exists because the failure it prevents is
|
|
1816
|
+
silent: a seat that reads the code and answers looks exactly like a seat that
|
|
1817
|
+
consulted its specialists.
|
|
1818
|
+
|
|
1809
1819
|
### `<owner>.md`: seat card
|
|
1810
1820
|
|
|
1811
1821
|
This is Markdown with frontmatter. It stores the seat role, repos, each repo
|
|
@@ -72,7 +72,8 @@ function runNext() {
|
|
|
72
72
|
working = true;
|
|
73
73
|
const message = queue.shift();
|
|
74
74
|
const text = userText(message);
|
|
75
|
-
const
|
|
75
|
+
const mode = behavior();
|
|
76
|
+
const delay = mode === 'slow-long' ? 1_500 : mode === 'slow' ? 350 : 5;
|
|
76
77
|
setTimeout(() => {
|
|
77
78
|
emit({
|
|
78
79
|
type: 'assistant',
|
|
@@ -103,7 +104,7 @@ function userText(message) {
|
|
|
103
104
|
const content = message?.message?.content;
|
|
104
105
|
if (typeof content === 'string') return content;
|
|
105
106
|
if (!Array.isArray(content)) return '';
|
|
106
|
-
return content.map((block) => block?.type === 'text' ? String(block.text || '') : '').join('');
|
|
107
|
+
return content.map((block) => (block?.type === 'text' ? String(block.text || '') : '')).join('');
|
|
107
108
|
}
|
|
108
109
|
|
|
109
110
|
function emit(value) {
|
package/bin/serve.py
CHANGED
|
@@ -224,6 +224,24 @@ def olvida_crecimiento(datos, slug):
|
|
|
224
224
|
_CRECIDO.pop((os.path.realpath(datos), slug), None)
|
|
225
225
|
|
|
226
226
|
|
|
227
|
+
def refresca_roster(ficha, datos):
|
|
228
|
+
"""Bring the card's `## Agents` list back in line with its frontmatter.
|
|
229
|
+
|
|
230
|
+
The Hall writes the roster as frontmatter keys, which is what the launcher,
|
|
231
|
+
the cage and the map read — and for a while that was the whole of it. But
|
|
232
|
+
the seat reads the BODY: the list under `## Agents` is what the chair is
|
|
233
|
+
told its city contains. So a person who added two houses from the Hall got
|
|
234
|
+
two windows and a card that still said, in prose, that they had one. Every
|
|
235
|
+
door that changes the roster passes through here.
|
|
236
|
+
"""
|
|
237
|
+
texto = card.lee(ficha).get("texto") or ""
|
|
238
|
+
try:
|
|
239
|
+
agentes = workspace.agentes(texto, datos)
|
|
240
|
+
except (OSError, ValueError):
|
|
241
|
+
return
|
|
242
|
+
card.cambia_agentes(ficha, [workspace.como_ficha(a) for a in agentes])
|
|
243
|
+
|
|
244
|
+
|
|
227
245
|
def _crecimiento_cacheado(a, datos, vida=90):
|
|
228
246
|
import time
|
|
229
247
|
|
|
@@ -631,10 +649,14 @@ class Manejador(http.server.BaseHTTPRequestHandler):
|
|
|
631
649
|
"""
|
|
632
650
|
pedida = q.get("city", [""])[0]
|
|
633
651
|
if pedida:
|
|
634
|
-
resuelta = self.resuelve_conocida(pedida)
|
|
652
|
+
resuelta = self.resuelve_conocida(pedida, tocando=False)
|
|
635
653
|
if resuelta:
|
|
636
654
|
return resuelta
|
|
637
|
-
|
|
655
|
+
# And not `donde_viven_las_fichas`, which resolves through `cities.actual`
|
|
656
|
+
# — a resolver that migrates, heals and will CREATE `home` when there is
|
|
657
|
+
# none. Asking it where to write a log line is how a request that removed
|
|
658
|
+
# a city brought it back.
|
|
659
|
+
return cities.actual_sin_tocar()
|
|
638
660
|
|
|
639
661
|
def apunta(self, q, tipo, **campos):
|
|
640
662
|
"""One journal line, in the journal of the city the request acted on.
|
|
@@ -645,7 +667,11 @@ class Manejador(http.server.BaseHTTPRequestHandler):
|
|
|
645
667
|
record, because it is a record that lies about where.
|
|
646
668
|
"""
|
|
647
669
|
try:
|
|
648
|
-
|
|
670
|
+
donde = self._ciudad_para_apuntar(q)
|
|
671
|
+
# No city, no line. A journal with nowhere to go must not be handed a
|
|
672
|
+
# folder invented for it — that is the same bug wearing a default.
|
|
673
|
+
if donde:
|
|
674
|
+
diario.apunta(donde, tipo, **campos)
|
|
649
675
|
except Exception: # noqa: BLE001 logging must not break the request
|
|
650
676
|
pass
|
|
651
677
|
|
|
@@ -687,12 +713,23 @@ class Manejador(http.server.BaseHTTPRequestHandler):
|
|
|
687
713
|
return False
|
|
688
714
|
return True
|
|
689
715
|
|
|
690
|
-
def resuelve_conocida(self, pedida):
|
|
691
|
-
"""Resolve only cities already in this Hall; never arbitrary folders.
|
|
716
|
+
def resuelve_conocida(self, pedida, tocando=True):
|
|
717
|
+
"""Resolve only cities already in this Hall; never arbitrary folders.
|
|
718
|
+
|
|
719
|
+
`tocando=False` makes the whole resolution read-only, for the journal:
|
|
720
|
+
both the listing and the fallback to the selected city are repairers by
|
|
721
|
+
default, and repairing something in order to write a line about it is
|
|
722
|
+
how a deleted city comes back.
|
|
723
|
+
"""
|
|
692
724
|
usuario = seat.quien_soy()
|
|
693
|
-
actual = seat.donde_viven_las_fichas()
|
|
694
|
-
candidatas = cities.lista(usuario)
|
|
695
|
-
|
|
725
|
+
actual = seat.donde_viven_las_fichas() if tocando else cities.actual_sin_tocar()
|
|
726
|
+
candidatas = cities.lista(usuario, tocando=tocando)
|
|
727
|
+
# The selected city, when the listing did not already cover it. With no
|
|
728
|
+
# city selected at all there is nothing to add, and asking anyway would
|
|
729
|
+
# hand `identidad('')` the current working directory.
|
|
730
|
+
if actual and not any(
|
|
731
|
+
os.path.realpath(c["ruta"]) == os.path.realpath(actual) for c in candidatas
|
|
732
|
+
):
|
|
696
733
|
candidatas.append(
|
|
697
734
|
{
|
|
698
735
|
"ruta": actual,
|
|
@@ -1443,6 +1480,7 @@ class Manejador(http.server.BaseHTTPRequestHandler):
|
|
|
1443
1480
|
for clave, valor in workspace.claves_de_roster(roster).items():
|
|
1444
1481
|
card.pon_campo(ficha, clave, valor)
|
|
1445
1482
|
workspace.crea_workspace(datos, slug)
|
|
1483
|
+
refresca_roster(ficha, datos)
|
|
1446
1484
|
return self.responde({"ok": True, "agent": slug, "name": nombre})
|
|
1447
1485
|
|
|
1448
1486
|
def p_montaje(self, q, cuerpo):
|
|
@@ -1495,6 +1533,8 @@ class Manejador(http.server.BaseHTTPRequestHandler):
|
|
|
1495
1533
|
# "this agent declares no mounts" in a longer, staler way.
|
|
1496
1534
|
lista = ("[" + ", ".join(fuentes) + "]") if fuentes else ""
|
|
1497
1535
|
card.pon_campo(ficha, f"mounts.{a.slug}", lista)
|
|
1536
|
+
# The body counts mounts out loud ("2 mounts"), so it moves too.
|
|
1537
|
+
refresca_roster(ficha, datos)
|
|
1498
1538
|
# What this agent works on just changed, which is exactly what growth
|
|
1499
1539
|
# counts: remembering the old number for 90s would read as a failed mount.
|
|
1500
1540
|
olvida_crecimiento(datos, a.slug)
|
package/bin/test
CHANGED
|
@@ -22,7 +22,7 @@ if [ -d .git ] && [ -x .githooks/pre-commit ] \
|
|
|
22
22
|
&& printf ' pre-commit gate armed (core.hooksPath .githooks)\n'
|
|
23
23
|
fi
|
|
24
24
|
|
|
25
|
-
SUITES=(widgets card parcels domains busca arnes i18n diario serve seat cities channel connect committee live-feed runtime claude-runtime runtime-ui runtime-failures stress adapter benchmark demo contracts exit rutas cage broker evidencia admision pairing hall-protocol doctor security workspace avatar crecimiento atajos navegador actualiza desinstala launch)
|
|
25
|
+
SUITES=(widgets card parcels domains busca arnes i18n diario alcance serve seat cities channel connect committee live-feed runtime claude-runtime runtime-ui runtime-failures stress adapter benchmark demo contracts exit rutas cage broker evidencia admision pairing hall-protocol doctor security workspace avatar crecimiento atajos navegador actualiza desinstala launch e2e)
|
|
26
26
|
[ $# -gt 0 ] && SUITES=("$@")
|
|
27
27
|
|
|
28
28
|
fallos=0
|
|
@@ -91,7 +91,7 @@ necesarios = ["bin/agents-city.js", "bin/hall", "bin/seat", "bin/city", "bin/dem
|
|
|
91
91
|
"plugin/scripts/crecimiento.py",
|
|
92
92
|
"plugin/scripts/reset.py", "plugin/scripts/desinstala.py",
|
|
93
93
|
"plugin/scripts/demos.py", "plugin/scripts/arnes.py",
|
|
94
|
-
"plugin/scripts/diario.py",
|
|
94
|
+
"plugin/scripts/diario.py", "plugin/scripts/alcance.py",
|
|
95
95
|
"plugin/channel/runtime/arnes.json", "plugin/scripts/capabilities.py",
|
|
96
96
|
"plugin/scripts/deliberations.py",
|
|
97
97
|
"plugin/scripts/runtime_processes.py",
|